From c8515170fd399b4d2dfaebb59128d4674e58915e Mon Sep 17 00:00:00 2001
From: wangzhibo <wangzhibo@shsening.com>
Date: 星期日, 30 八月 2026 10:34:57 +0800
Subject: [PATCH] add xiaozhi server
---
xiaozhi-esp32-server/main/manager-mobile/src/pages/login/index.vue | 1012
xiaozhi-esp32-server/main/digital-human/wakeword_runtime/config/__init__.py | 2
xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_vi_VN.properties | 213
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/validator/group/DefaultGroup.java | 8
xiaozhi-esp32-server/main/manager-web/src/views/FeatureManagement.vue | 627
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505022134.sql | 27
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202608131030.sql | 4
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/EmitSeverActionDTO.java | 26
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaoaitongxue/wn9_data | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/handle/textHandler/iotMessageHandler.py | 22
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202508081701.sql | 32
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_128/shocked.gif | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506032232.sql | 14
xiaozhi-esp32-server/main/digital-human/images/3.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/87x87.png | 0
xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/agent/service/impl/AgentSnapshotServiceImplTest.java | 1962
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hiwalle_tts2/wn9_data | 0
xiaozhi-esp32-server/docs/images/alisms/sms-01.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/store/speedPitch.ts | 44
xiaozhi-esp32-server/main/manager-web/src/assets/header/knowledge_base.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentSnapshotRestoreDTO.java | 13
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tts/base.py | 639
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506191643.sql | 19
xiaozhi-esp32-server/main/digital-human/wakeword_runtime/plugins/manager.py | 37
xiaozhi-esp32-server/main/manager-web/src/views/ServerSideManager.vue | 272
xiaozhi-esp32-server/main/manager-mobile/src/layouts/fg-tabbar/tabbar.ts | 11
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelBasicInfoDTO.java | 9
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaoyutongxue_tts2/wn9_data | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/connection.py | 1849
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_128/crying.gif | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/service/impl/TimbreServiceImpl.java | 249
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/controller/TimbreController.java | 92
xiaozhi-esp32-server/.github/workflows/build-base-image.yml | 49
xiaozhi-esp32-server/main/xiaozhi-server/docker-compose_all.yml | 92
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202607071530.sql | 20
xiaozhi-esp32-server/main/xiaozhi-server/models/snakers4_silero-vad/src/silero_vad/data/silero_vad_16k_op15.onnx | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/OtaService.java | 22
xiaozhi-esp32-server/main/manager-web/src/assets/device-avatars/xiaozhi-logo8.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/vo/CorrectWordSimpleVO.java | 15
xiaozhi-esp32-server/docs/images/fishspeech/autodl-02.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/modules_initialize.py | 152
xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/80x80.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/fonts/font_puhui_deepseek_20_4.bin | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_ni3hao3xiao3rui4_tts3/wn9_index | 0
xiaozhi-esp32-server/main/xiaozhi-server/models/SenseVoiceSmall/example/en.mp3 | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentCorrectWordMappingEntity.java | 38
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentInfoVO.java | 31
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hai1xiao3ou1_tts3/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaolongxiaolong_tts/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/digital-human/start.py | 55
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_computer_tts/_MODEL_INFO_ | 1
xiaozhi-esp32-server/docs/ali-sms-integration.md | 44
xiaozhi-esp32-server/docs/dev-ops-integration.md | 182
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/page/PageData.java | 33
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori_pro_t06.4096/texture_00.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji32/confident.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/Sm2DecryptUtil.java | 63
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/interceptor/DataScope.java | 27
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaomiaoban_tts2/wn9_index | 0
xiaozhi-esp32-server/main/manager-web/src/components/TtsAdvancedSettings.vue | 371
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/IpUtils.java | 48
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysDictDataServiceImpl.java | 189
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_linaiban_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/README.md | 362
xiaozhi-esp32-server/main/xiaozhi-server/core/api/vision_handler.py | 182
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentVoicePrintSaveDTO.java | 28
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/asr/dto/dto.py | 9
xiaozhi-esp32-server/Dockerfile-server-base | 32
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_alexa/wn9_index | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hiandy_tts2/wn9_index | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/asr/aliyunbl_stream.py | 318
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hijolly_tts2/wn9_data | 0
xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m05.motion3.json | 1543
xiaozhi-esp32-server/main/manager-web/src/assets/device-avatars/xiaozhi-logo14.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/config/assets/bind_code/7.wav | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_en_US.properties | 34
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_heywanda_tts/wn9_index | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaoyaxiaoya_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-web/src/components/AudioPlayer.vue | 379
xiaozhi-esp32-server/main/manager-web/public/generator/static/multinet_model/mn7_en/mn7_index | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/audioRateController.py | 183
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn8_hilexin/wn8_data | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji64/neutral.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505271414.sql | 51
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji64/delicious.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/vllm/openai.py | 68
xiaozhi-esp32-server/main/xiaozhi-server/models/SenseVoiceSmall/example/ko.mp3 | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/entity/ModelConfigEntity.java | 65
xiaozhi-esp32-server/main/manager-mobile/src/api/device/index.ts | 2
xiaozhi-esp32-server/docs/powermem-integration.md | 345
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/annotation/LogOperation.java | 20
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9s_hiesp/wn9_data | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/asr/xunfei_stream.py | 337
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hilexin/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentTagRelationDao.java | 18
xiaozhi-esp32-server/docs/images/demo1.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/fonts/font_noto_qwen_20_4.bin | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/ResultUtils.java | 31
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512192245.sql | 2
xiaozhi-esp32-server/main/manager-mobile/src/typings.ts | 15
xiaozhi-esp32-server/main/manager-mobile/src/pages/login/user-agreement-zh.vue | 542
xiaozhi-esp32-server/main/xiaozhi-server/config_from_api.yaml | 27
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml | 741
xiaozhi-esp32-server/main/manager-web/src/main.js | 31
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentVoicePrintService.java | 50
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/textUtils.py | 117
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/impl/KnowledgeBaseServiceImpl.java | 501
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_64/crying.gif | 0
xiaozhi-esp32-server/main/manager-web/src/assets/login/hi.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/opus_encoder_utils.py | 139
xiaozhi-esp32-server/docs/images/banner1.png | 0
xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m09.motion3.json | 1113
xiaozhi-esp32-server/main/digital-human/js/live2d/live2d.js | 824
xiaozhi-esp32-server/main/digital-human/js/core/network/wakeword-bridge.js | 225
xiaozhi-esp32-server/main/manager-web/public/generator/static/charsets/gb2312.txt | 7445 +
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/ServerSideManageController.java | 139
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505292203.sql | 189
xiaozhi-esp32-server/main/digital-human/wakeword_runtime/bridge/event_bridge.py | 106
xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/20x20.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/asr/base.py | 336
xiaozhi-esp32-server/main/manager-mobile/src/layouts/fg-tabbar/tabbar.md | 17
xiaozhi-esp32-server/main/xiaozhi-server/performance_tester/performance_tester_llm.py | 545
xiaozhi-esp32-server/.github/ISSUE_TEMPLATE/code_improvement.md | 19
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentChatTitleDao.java | 12
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9s_nihaoxiaozhi/wn9_data | 0
xiaozhi-esp32-server/docs/images/deploy2.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/edit.vue | 1705
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602271137.sql | 42
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_64/happy.gif | 0
xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.2048/texture_00.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentTagRelationDao.xml | 22
xiaozhi-esp32-server/main/manager-mobile/src/static/logo.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentPluginMappingMapper.xml | 48
xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/DeviceTest.java | 76
xiaozhi-esp32-server/main/manager-web/public/generator/static/charsets/deepseek.txt | 0
xiaozhi-esp32-server/main/digital-human/images/1.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_heyprinter_tts/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_ni3hao3xiao3mai4_tts2/wn9_index | 0
xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/58x58.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaodongdong_tts2/wn9_data | 0
xiaozhi-esp32-server/main/manager-api/pom.xml | 296
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceManualAddDTO.java | 11
xiaozhi-esp32-server/main/manager-mobile/env/.env.development | 6
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509091042.sql | 10
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/dto/SmsVerificationDTO.java | 28
xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_zh_TW.properties | 34
xiaozhi-esp32-server/main/manager-mobile/src/manifest.json | 124
xiaozhi-esp32-server/main/manager-web/src/components/VersionFooter.vue | 74
xiaozhi-esp32-server/main/manager-mobile/src/style/iconfont.css | 28
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/KnowledgeBaseAdapterFactory.java | 206
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentSnapshotTagDTO.java | 16
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentSnapshotEntity.java | 53
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hijason_tts2/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn8_alexa/wn8_data | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sms/service/SmsService.java | 17
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512161529.sql | 1
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji64/sleepy.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_ni3hao3xiao3rui4_tts3/wn9_data | 0
xiaozhi-esp32-server/main/digital-human/wakeword_runtime/requirements.txt | 3
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/entity/BaseEntity.java | 34
xiaozhi-esp32-server/main/xiaozhi-server/plugins_func/functions/search_from_ragflow.py | 142
xiaozhi-esp32-server/main/manager-mobile/env/.env.production | 6
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hijolly_tts2/wn9_index | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/entity/SysDictTypeEntity.java | 46
xiaozhi-esp32-server/docs/performance_tester.md | 27
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentController.java | 353
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysUserServiceImpl.java | 250
xiaozhi-esp32-server/main/manager-web/src/components/VoiceCloneDialog.vue | 704
xiaozhi-esp32-server/docs/paddlespeech-deploy.md | 109
xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.cdi3.json | 533
xiaozhi-esp32-server/main/manager-web/tests/timbreContract.test.mjs | 17
xiaozhi-esp32-server/docs/fish-speech-integration.md | 72
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceUnBindDTO.java | 20
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentContextProviderDao.java | 9
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelProviderDTO.java | 59
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/memory/nomem/nomem.py | 20
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn8_hiesp/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/120x120.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504092335.sql | 75
xiaozhi-esp32-server/main/manager-mobile/src/layouts/fg-tabbar/tabbarList.ts | 76
xiaozhi-esp32-server/main/manager-web/public/offline.html | 70
xiaozhi-esp32-server/docs/readme/README_pt_BR.md | 361
xiaozhi-esp32-server/main/manager-web/src/views/UserManagement.vue | 461
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dao/SysDictTypeDao.java | 14
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/exp_03.exp3.json | 120
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentMemoryDTO.java | 19
xiaozhi-esp32-server/main/xiaozhi-server/config/assets/bind_code/9.wav | 0
xiaozhi-esp32-server/main/manager-web/src/assets/device-avatars/xiaozhi-logo12.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/asr/fun_local.py | 113
xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/security/config/ShiroConfigTest.java | 60
xiaozhi-esp32-server/main/manager-web/public/generator/static/multinet_model/mn6_en/mn6_index | 0
xiaozhi-esp32-server/main/manager-web/public/privacy-policy-en.html | 162
xiaozhi-esp32-server/main/manager-mobile/src/typings.d.ts | 28
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji32/loving.png | 0
xiaozhi-esp32-server/main/manager-web/src/components/ContextProviderDialog.vue | 329
xiaozhi-esp32-server/main/manager-web/src/views/AddressBookManagement.vue | 1294
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_128/surprised.gif | 0
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/exp_02.exp3.json | 120
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/SysUserDTO.java | 87
xiaozhi-esp32-server/main/xiaozhi-server/core/handle/receiveAudioHandle.py | 179
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hitelly_tts/wn9_data | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentSnapshotDataDTO.java | 40
xiaozhi-esp32-server/main/manager-mobile/src/pages/login/privacy-policy-en.vue | 402
xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/agentSnapshotUtils.mjs | 314
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dao/SysDictDataDao.java | 32
xiaozhi-esp32-server/main/manager-mobile/README.md | 170
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiao3feng1xiao3feng1_tts3/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/xiaozhi-server/core/handle/textMessageHandlerRegistry.py | 47
xiaozhi-esp32-server/main/manager-mobile/src/api/voiceprint/index.ts | 2
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512131453.sql | 6
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202606231030.sql | 15
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji64/angry.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/impl/KnowledgeManagerServiceImpl.java | 47
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/secret/ServerSecretFilter.java | 105
xiaozhi-esp32-server/main/manager-web/src/assets/xiaozhi-ai_en.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_haixiaowu_tts/wn9_data | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/MqttGatewayAuthorization.java | 111
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/redis/RedisKeys.java | 198
xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/72x72.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaobinxiaobin_tts/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m07.motion3.json | 927
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506051538.sql | 20
xiaozhi-esp32-server/main/manager-mobile/src/i18n/de.ts | 597
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_himiaomiao_tts/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/digital-human/wakeword_runtime/plugins/base.py | 18
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysUserUtilService.java | 19
xiaozhi-esp32-server/main/manager-mobile/.npmrc | 8
xiaozhi-esp32-server/main/xiaozhi-server/plugins_func/register.py | 146
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/AdminPageUserDTO.java | 27
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202511221450.sql | 17
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/Oauth2Filter.java | 109
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_himfive/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaoaitongxue/wn9_index | 0
xiaozhi-esp32-server/docs/images/image-ha-integration-02.png | 0
xiaozhi-esp32-server/main/manager-mobile/.gitignore | 44
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceServiceImpl.java | 884
xiaozhi-esp32-server/docs/images/logo_contributors.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hiesp/wn9_data | 0
xiaozhi-esp32-server/main/manager-mobile/src/api/chat-history/types.ts | 39
xiaozhi-esp32-server/main/manager-web/public/generator/static/multinet_model/mn6_en/mn6_data | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/fonts/font_noto_qwen_16_4.bin | 0
xiaozhi-esp32-server/docs/images/alisms/sms-21.png | 0
xiaozhi-esp32-server/docs/images/demo3.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysDictDataService.java | 70
xiaozhi-esp32-server/main/xiaozhi-server/core/handle/textHandler/helloMessageHandler.py | 18
xiaozhi-esp32-server/main/xiaozhi-server/core/websocket_server.py | 227
xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/76x76.png | 0
xiaozhi-esp32-server/main/manager-web/src/assets/knowledge-base/MD.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tts/minimax_httpstream.py | 383
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/intent/intent_llm/intent_llm.py | 296
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604011545.sql | 16
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506251620.sql | 18
xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/chat.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/asr/baidu.py | 74
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentSnapshotDao.java | 29
xiaozhi-esp32-server/main/manager-web/src/apis/module/voiceResource.js | 103
xiaozhi-esp32-server/main/README_en.md | 491
xiaozhi-esp32-server/main/manager-mobile/src/api/voiceprint/voiceprint.ts | 72
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysUserService.java | 75
xiaozhi-esp32-server/main/manager-mobile/src/pages/device-config/components/ultrasonic-config.vue | 685
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentVoicePrintVO.java | 33
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/VoiceDTO.java | 50
xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/common/utils/JsonUtilsTest.java | 77
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaoaitongxue/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-web/src/apis/api.js | 49
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_64/confused.gif | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji32/confused.png | 0
xiaozhi-esp32-server/main/manager-web/src/components/DictDataDialog.vue | 117
xiaozhi-esp32-server/main/xiaozhi-server/core/handle/abortHandle.py | 20
xiaozhi-esp32-server/main/manager-web/src/components/FunctionDialog.vue | 898
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505111914.sql | 6
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceToolsCallReqDTO.java | 15
xiaozhi-esp32-server/main/manager-mobile/src/pages/chat-history/detail.vue | 500
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/mcp_endpoint/mcp_endpoint_executor.py | 97
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaoxiaoyi_tts2/wn9_data | 0
xiaozhi-esp32-server/main/xiaozhi-server/plugins_func/functions/web_search.py | 156
xiaozhi-esp32-server/main/manager-web/public/privacy-policy.html | 170
xiaozhi-esp32-server/main/manager-web/src/assets/device-avatars/xiaozhi-logo4.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/api/ota_handler.py | 415
xiaozhi-esp32-server/main/manager-web/.npmrc | 1
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaojianxiaojian_tts2/wn9_index | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/mcp_endpoint/mcp_endpoint_handler.py | 392
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hixiaoxing_tts/wn9_index | 0
xiaozhi-esp32-server/main/xiaozhi-server/performance_tester/performance_tester_vllm.py | 193
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelConfigServiceImpl.java | 562
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaoxiaoxin_tts/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiao3feng1xiao3feng1_tts3/wn9_index | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/device_mcp/mcp_handler.py | 403
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505151451.sql | 18
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202608061800.sql | 22
xiaozhi-esp32-server/main/manager-web/src/assets/setting/asr.png | 0
xiaozhi-esp32-server/main/manager-web/src/components/CustomTable.vue | 309
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509161610.sql | 3
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_128/thinking.gif | 0
xiaozhi-esp32-server/main/manager-web/src/assets/xiaozhi-ai.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/config/assets/bind_code/3.wav | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelConfigBodyDTO.java | 42
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/controller/OTAController.java | 122
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji32/sleepy.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tts/alibl_stream.py | 559
xiaozhi-esp32-server/docs/images/__init__.py | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentVoicePrintDao.java | 20
xiaozhi-esp32-server/main/manager-web/src/assets/header/robot.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/SysUserTokenService.java | 41
xiaozhi-esp32-server/main/manager-web/src/i18n/pt_BR.js | 1687
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/HttpContextUtils.java | 110
xiaozhi-esp32-server/main/manager-mobile/src/utils/toast.ts | 65
xiaozhi-esp32-server/main/manager-mobile/index.html | 26
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/controller/ModelController.java | 174
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505081146.sql | 43
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/utils/WebSocketClientManager.java | 372
xiaozhi-esp32-server/main/xiaozhi-server/plugins_func/loadplugins.py | 25
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505142037.sql | 7
xiaozhi-esp32-server/main/xiaozhi-server/core/handle/reportHandle.py | 252
xiaozhi-esp32-server/main/manager-web/src/assets/setting/menu.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_sophia_tts_v1/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/src/assets/device-avatars/xiaozhi-logo10.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509191545.sql | 41
xiaozhi-esp32-server/main/manager-web/src/components/ModelEditDialog.vue | 530
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceReportReqDTO.java | 148
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/server_mcp/__init__.py | 7
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaomiaoban_tts2/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/src/components/agentSnapshotDisplayUtils.test.mjs | 155
xiaozhi-esp32-server/docs/FAQ.md | 107
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507031602.sql | 4
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaokangtongxue_tts2/wn9_data | 0
xiaozhi-esp32-server/docs/docker/nginx.conf | 53
xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/152x152.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/entity/VoiceCloneEntity.java | 56
xiaozhi-esp32-server/main/manager-web/src/components/DictTypeDialog.vue | 94
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dao/DeviceAddressBookDao.java | 39
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentContextProviderEntity.java | 43
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202510191042.sql | 2
xiaozhi-esp32-server/main/manager-mobile/src/pages/index/index.vue | 804
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaoxiaoan_tts2/wn9_data | 0
xiaozhi-esp32-server/docs/images/demo5.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/CaptchaServiceImpl.java | 167
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysParamsServiceImpl.java | 331
xiaozhi-esp32-server/main/manager-web/src/assets/device-avatars/xiaozhi-logo6.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hilili_tts/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-web/src/apis/module/user.js | 204
xiaozhi-esp32-server/.gitignore | 199
xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/29x29.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/entity/KnowledgeBaseEntity.java | 81
xiaozhi-esp32-server/main/manager-web/src/apis/module/addressBook.js | 60
xiaozhi-esp32-server/main/manager-web/src/components/HeaderBar.vue | 651
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/dto/CorrectWordsDTO.java | 14
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaobaiying_tts2/wn9_index | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaoyaxiaoya_tts2/wn9_index | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/multinet_model/mn7_en/mn7_data | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/SensitiveDataUtils.java | 165
xiaozhi-esp32-server/Dockerfile-server | 7
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_ni3hao3xiao3rui4_tts3/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dao/DocumentDao.java | 12
xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/service/impl/DeviceTimeSerializationTest.java | 106
xiaozhi-esp32-server/main/manager-web/src/assets/xiaozhi-ai_vi.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaomingtongxue_tts2/wn9_index | 0
xiaozhi-esp32-server/main/xiaozhi-server/models/SenseVoiceSmall/chn_jpn_yue_eng_ko_spectok.bpe.model | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202508271113.sql | 24
xiaozhi-esp32-server/main/manager-web/public/generator/static/fonts/font_noto_qwen_14_1.bin | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/impl/KnowledgeFilesServiceImpl.java | 980
xiaozhi-esp32-server/main/manager-web/src/assets/setting/vad.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202606261131.sql | 40
xiaozhi-esp32-server/main/manager-web/public/generator/workers/gif.worker.js | 3
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_customword/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_64/kissy.gif | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaotexiaote_tts2/wn9_index | 0
xiaozhi-esp32-server/main/xiaozhi-server/models/snakers4_silero-vad/src/silero_vad/data/__init__.py | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysDictTypeServiceImpl.java | 154
xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_vi_VN.properties | 34
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/ConvertUtils.java | 51
xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_pt_BR.properties | 213
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatHistoryServiceImpl.java | 213
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji64/sad.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/XssProperties.java | 26
xiaozhi-esp32-server/docs/images/conda_env_2.png | 0
xiaozhi-esp32-server/main/manager-web/tests/frontendContract.test.mjs | 80
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tts/openai.py | 62
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentChatSessionDTO.java | 31
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_128/funny.gif | 0
xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/system_activate.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/context_provider.py | 64
xiaozhi-esp32-server/main/manager-web/src/assets/login/phone.png | 0
xiaozhi-esp32-server/main/digital-human/index.html | 416
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/enums/SuperAdminEnum.java | 19
xiaozhi-esp32-server/main/manager-web/src/assets/login/login-person.png | 0
xiaozhi-esp32-server/main/manager-web/src/components/ChangePasswordDialog.vue | 129
xiaozhi-esp32-server/main/manager-web/src/views/register.vue | 376
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/dto/TimbreDataDTO.java | 49
xiaozhi-esp32-server/main/xiaozhi-server/core/auth.py | 72
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/base/__init__.py | 6
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dao/ModelConfigDao.java | 24
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatAudioServiceImpl.java | 34
xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/service/impl/MqttGatewayAuthorizationTest.java | 180
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505182234.sql | 65
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori_pro_t06.moc3 | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/Oauth2Realm.java | 108
xiaozhi-esp32-server/main/manager-web/src/apis/module/voiceClone.js | 107
xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/tools.vue | 609
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/dto/LoginDTO.java | 31
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelProviderServiceImpl.java | 180
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/SqlFilter.java | 46
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori_pro_t06.model3.json | 123
xiaozhi-esp32-server/main/manager-web/src/assets/header/voice.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/plugins_func/functions/get_news_from_newsnow.py | 294
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/config/SwaggerConfig.java | 114
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentTemplateServiceImpl.java | 125
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dao/KnowledgeBaseDao.java | 35
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/controller/ModelProviderController.java | 75
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysDictTypeService.java | 60
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentChatHistoryUserVO.java | 16
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hiesp/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/biz/impl/AgentChatHistoryBizServiceImpl.java | 130
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_mycroft_tts/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaosurou_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202510071522.sql | 19
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_histackchan_tts3/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/xiaozhi-server/config/assets/bind_code/5.wav | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tts/doubao.py | 101
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java | 247
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn7_xiaoaitongxue/wn7_data | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaoxiaoxin_tts/wn9_index | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/vo/TimbreDetailsVO.java | 46
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentSnapshotVO.java | 34
xiaozhi-esp32-server/docs/digital-human-wakeword.md | 220
xiaozhi-esp32-server/main/manager-mobile/pnpm-workspace.yaml | 6
xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/speedPitch.vue | 157
xiaozhi-esp32-server/main/manager-web/src/components/ChatHistoryDialog.vue | 647
xiaozhi-esp32-server/main/digital-human/css/bg.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/performance_tester.py | 69
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/entity/TimbreEntity.java | 73
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509081140.sql | 4
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tts/huoshan_double_stream.py | 840
xiaozhi-esp32-server/main/manager-mobile/src/App.vue | 95
xiaozhi-esp32-server/main/manager-web/src/utils/deviceTime.mjs | 41
xiaozhi-esp32-server/main/manager-web/src/assets/setting/voiceclone.png | 0
xiaozhi-esp32-server/main/manager-web/src/assets/login/register-person.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/ShiroServiceImpl.java | 27
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji32/laughing.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/robot.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hilexin/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_64/laughing.gif | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn8_hiesp/wn8_index | 0
xiaozhi-esp32-server/main/digital-human/js/core/audio/player.js | 297
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202511131023.sql | 62
xiaozhi-esp32-server/main/digital-human/js/ui/controller.js | 929
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentCreateDTO.java | 21
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_heyivy_tts2/wn9_index | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_heywillow_tts/wn9_data | 0
xiaozhi-esp32-server/docs/images/image-clone-integration-03.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/vad/silero.py | 116
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hiwalle_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/XssUtils.java | 67
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_128/relaxed.gif | 0
xiaozhi-esp32-server/main/xiaozhi-server/performance_tester/performance_tester_tts.py | 199
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505201744.sql | 3
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202601141645.sql | 31
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_heyily_tts2/wn9_data | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/llm.py | 23
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_64/loving.gif | 0
xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/network.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/handle/textMessageProcessor.py | 44
xiaozhi-esp32-server/docs/images/alisms/sms-32.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504251422.sql | 30
xiaozhi-esp32-server/docs/images/image-ha-integration-06.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/style/index.scss | 19
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentVoicePrintEntity.java | 64
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hifairy_tts2/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/package.json | 53
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/entity/DeviceAddressBookEntity.java | 49
xiaozhi-esp32-server/main/manager-mobile/src/pages/login/user-agreement-en.vue | 539
xiaozhi-esp32-server/main/digital-human/wakeword_runtime/config/logging_setup.py | 23
xiaozhi-esp32-server/main/manager-mobile/src/utils/platform.ts | 24
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiao3jia1xiao3jia1_tts3/wn9_data | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/handle/textHandle.py | 19
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji32/angry.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_histackchan_tts3/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/src/apis/module/device.js | 108
xiaozhi-esp32-server/main/manager-mobile/scripts/postupgrade.js | 35
xiaozhi-esp32-server/main/xiaozhi-server/config/settings.py | 34
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/llm/coze/coze.py | 75
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tts/paddle_speech.py | 157
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_01.motion3.json | 1971
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_sophia_tts_v1/wn9_index | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaoxiaozhi_tts/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_sophia_tts/_MODEL_INFO_ | 2
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tts/index_stream.py | 258
xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/exampleHL.png | 0
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/exp_04.exp3.json | 120
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/init/SystemInitConfig.java | 48
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604011035.sql | 1
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/intent.py | 17
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaoyutongxue_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_linaiban_tts2/wn9_index | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java | 120
xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/common/service/impl/BaseServiceImplTest.java | 219
xiaozhi-esp32-server/main/manager-mobile/src/pages/device/deviceTimeUtils.mjs | 14
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_miaomiaotongxue_tts/_MODEL_INFO_ | 2
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceUpdateDTO.java | 29
xiaozhi-esp32-server/main/manager-web/src/views/AgentTemplateManagement.vue | 489
xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/controller/DeviceControllerTest.java | 78
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentTagService.java | 25
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_64/surprised.gif | 0
xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/120x120.png | 0
xiaozhi-esp32-server/main/manager-web/src/assets/user-avatar4.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/document/RetrievalDTO.java | 307
xiaozhi-esp32-server/main/manager-web/src/apis/module/model.js | 455
xiaozhi-esp32-server/docs/readme/README_en.md | 361
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn7_xiaoaitongxue/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentServiceImpl.java | 719
xiaozhi-esp32-server/docs/images/logo_bailing.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_pt_BR.properties | 34
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/controller/VoiceResourceController.java | 116
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Normal.exp3.json | 120
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/password/BCryptPasswordEncoder.java | 81
xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentTemplateMapper.xml | 6
xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/agentSnapshotUtils.test.mjs | 183
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/dataset/DatasetDTO.java | 449
xiaozhi-esp32-server/main/manager-web/src/assets/device-avatars/xiaozhi-logo1.png | 0
xiaozhi-esp32-server/main/manager-web/src/assets/header/param_management.png | 0
xiaozhi-esp32-server/main/manager-web/babel.config.js | 12
xiaozhi-esp32-server/main/manager-mobile/src/pages-sub/demo/index.vue | 27
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/DateUtils.java | 116
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/device_iot/iot_handler.py | 87
xiaozhi-esp32-server/docs/images/alisms/sms-23.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/vllm/base.py | 12
xiaozhi-esp32-server/main/xiaozhi-server/config/assets/max_output_size.wav | 0
xiaozhi-esp32-server/main/manager-mobile/src/api/chat-history/chat-history.ts | 63
xiaozhi-esp32-server/main/manager-mobile/pnpm-lock.yaml | 14881 +++
xiaozhi-esp32-server/main/manager-web/src/assets/xiaozhi-logo.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/config/RAGTaskConfig.java | 13
xiaozhi-esp32-server/main/digital-human/wakeword_runtime/runtime/__init__.py | 2
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/config/MybatisPlusConfig.java | 36
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/KnowledgeManagerService.java | 24
xiaozhi-esp32-server/docs/images/demo6.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaoluxiaolu_tts2/wn9_index | 0
xiaozhi-esp32-server/main/manager-mobile/src/pages/device-config/components/wifi-selector.vue | 567
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaoxiaoan_tts2/wn9_index | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/cache/manager.py | 216
xiaozhi-esp32-server/docs/images/demo14.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/device.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/personalHL.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/task/DocumentStatusSyncTask.java | 39
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202603111131.sql | 69
xiaozhi-esp32-server/main/manager-api/src/main/resources/application.yml | 66
xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.pose3.json | 15
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/vo/SysDictDataItem.java | 20
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiao3jia1xiao3jia1_tts3/wn9_index | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/TreeNode.java | 30
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/dto/VoiceCloneResponseDTO.java | 51
xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/common/redis/RedisSerializationTest.java | 57
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaojianxiaojian_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_heywillow_tts/wn9_index | 0
xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/180x180.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/base/tool_types.py | 27
xiaozhi-esp32-server/main/digital-human/wakeword_runtime/runtime/app.py | 70
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_128/delicious.gif | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tts/gpt_sovits_v2.py | 105
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji64/confused.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/charsets/qwen18409.txt | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/DeviceService.java | 141
xiaozhi-esp32-server/main/manager-mobile/src/http/request/types.ts | 22
xiaozhi-esp32-server/main/xiaozhi-server/plugins_func/functions/handle_exit_intent.py | 48
xiaozhi-esp32-server/docs/images/image-ha-integration-04.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_mycroft_tts/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaoluxiaolu_tts2/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/multinet_model/mn6_en/_MODEL_INFO_ | 2
xiaozhi-esp32-server/main/manager-web/public/generator/README.md | 89
xiaozhi-esp32-server/docs/images/manager-mobile/重新识别项目.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/config/assets/bind_code/2.wav | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/KnowledgeBaseService.java | 106
xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_en_US.properties | 214
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9s_alexa/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentChatAudioEntity.java | 29
xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/167x167.png | 0
xiaozhi-esp32-server/docs/images/manager-mobile/本地运行.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn8_alexa/wn8_index | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/dto/CorrectWordFileCreateDTO.java | 24
xiaozhi-esp32-server/main/manager-web/public/generator/static/charsets/latin1.txt | 190
xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/96x96.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/sys/SysDictDataDao.xml | 37
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/asr/aliyun.py | 236
xiaozhi-esp32-server/main/xiaozhi-server/performance_tester/performance_tester_asr.py | 354
xiaozhi-esp32-server/docs/newsnow_plugin_config.md | 105
xiaozhi-esp32-server/main/manager-mobile/src/components/custom-tabs/index.vue | 131
xiaozhi-esp32-server/main/manager-mobile/src/api/auth.ts | 143
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_128/angry.gif | 0
xiaozhi-esp32-server/main/xiaozhi-server/plugins_func/functions/call_device.py | 95
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaomingtongxue_tts2/wn9_data | 0
xiaozhi-esp32-server/docs/images/hnlg.jpg | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505091555.sql | 25
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentVoicePrintUpdateDTO.java | 28
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_astrolabe_tts/wn9_index | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_himiaomiao_tts/wn9_index | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaotexiaote_tts2/wn9_data | 0
xiaozhi-esp32-server/main/xiaozhi-server/app.py | 152
xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/144x144.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/IdentifyVoicePrintResponse.java | 21
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/server_mcp/mcp_manager.py | 194
xiaozhi-esp32-server/docs/images/logo_junsen.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/static/images/default-avatar.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/models/snakers4_silero-vad/src/silero_vad/utils_vad.py | 500
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/dao/CorrectWordFileDao.java | 10
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504221555.sql | 49
xiaozhi-esp32-server/main/xiaozhi-server/performance_tester/performance_tester_stream_asr.py | 404
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/typehandler/ContextProviderListTypeHandler.java | 59
xiaozhi-esp32-server/main/manager-web/src/assets/setting/knowledgeBase.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/service/BaseService.java | 109
xiaozhi-esp32-server/main/manager-web/src/views/ModelConfig.vue | 983
xiaozhi-esp32-server/main/digital-human/wakeword_runtime/config/config_loader.py | 153
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/JsonUtils.java | 131
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506080955.sql | 3
xiaozhi-esp32-server/main/xiaozhi-server/performance_tester/performance_tester_stream_tts.py | 672
xiaozhi-esp32-server/main/manager-web/src/router/index.js | 249
xiaozhi-esp32-server/docs/weather-integration.md | 64
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506161101.sql | 1
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hijoy_tts/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/src/utils/cacheViewer.js | 142
xiaozhi-esp32-server/main/xiaozhi-server/models/snakers4_silero-vad/src/silero_vad/data/silero_vad.onnx | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTemplateEntity.java | 149
xiaozhi-esp32-server/main/manager-web/src/views/retrievePassword.vue | 324
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/entity/CorrectWordItemEntity.java | 27
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202503141335.sql | 83
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/Enums/AgentSnapshotField.java | 103
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji64/relaxed.png | 0
xiaozhi-esp32-server/main/digital-human/wakeword_runtime/core/detector.py | 191
xiaozhi-esp32-server/docs/images/manager-mobile/打包发行步骤1.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/assets/index-Guo1hQ-y.js | 81
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9s_nihaoxiaozhi/wn9_index | 0
xiaozhi-esp32-server/main/xiaozhi-server/config/assets/bind_code/0.wav | 0
xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/192x192.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_heyprinter_tts/_MODEL_INFO_ | 2
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji32/embarrassed.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentDao.java | 55
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/document/ChunkDTO.java | 182
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceAddressBookAliasDTO.java | 21
xiaozhi-esp32-server/main/manager-web/README.md | 27
xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/sys/service/impl/SysParamsServiceImplTest.java | 36
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji64/surprised.png | 0
xiaozhi-esp32-server/docs/images/demo8.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_128/loving.gif | 0
xiaozhi-esp32-server/main/xiaozhi-server/models/SenseVoiceSmall/config.yaml | 97
xiaozhi-esp32-server/main/digital-human/wakeword_runtime/bridge/__init__.py | 1
xiaozhi-esp32-server/main/manager-web/src/assets/device-avatars/xiaozhi-logo3.png | 0
xiaozhi-esp32-server/main/manager-web/src/components/AddWisdomBodyDialog.vue | 113
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/TokenServiceImpl.java | 19
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatSummaryService.java | 23
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_02.motion3.json | 2182
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202607290930.sql | 46
xiaozhi-esp32-server/main/manager-web/src/assets/knowledge-base/xlsx.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/http/request/enum.ts | 70
xiaozhi-esp32-server/main/xiaozhi-server/models/snakers4_silero-vad/src/silero_vad/model.py | 36
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentPluginMappingService.java | 36
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/validator/AssertUtils.java | 90
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/secret/ServerSecretToken.java | 25
xiaozhi-esp32-server/main/manager-web/src/utils/format.js | 21
xiaozhi-esp32-server/main/manager-web/public/generator/static/multinet_model/mn6_cn/mn6_index | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hijoy_tts/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/entity/SysDictDataEntity.java | 50
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/MessageUtils.java | 25
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentTemplateDao.java | 17
xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation.properties | 35
xiaozhi-esp32-server/docs/images/demo12.png | 0
xiaozhi-esp32-server/docs/readme/README_de.md | 361
xiaozhi-esp32-server/main/manager-web/src/views/auth.scss | 132
xiaozhi-esp32-server/main/xiaozhi-server/requirements.txt | 43
xiaozhi-esp32-server/main/manager-api/src/main/resources/lua/emptyAll.lua | 1
xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m10.motion3.json | 924
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505122348.sql | 6
xiaozhi-esp32-server/main/manager-web/src/i18n/vi.js | 1687
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202605121509.sql | 6
xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/40x40.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dao/OtaDao.java | 15
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaobaiying_tts2/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/src/views/DictManagement.vue | 580
xiaozhi-esp32-server/main/manager-web/src/views/ProviderManagement.vue | 499
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/memory.py | 18
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/SysDictDataDTO.java | 59
xiaozhi-esp32-server/main/manager-web/src/assets/knowledge-base/pdf.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tts/edge.py | 74
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/convert/DateConverter.java | 73
xiaozhi-esp32-server/main/manager-web/src/assets/device-avatars/xiaozhi-logo16.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/llm/base.py | 34
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_miaomiaotongxue_tts/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaomiaoban_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/vo/SysDictDataVO.java | 50
xiaozhi-esp32-server/main/xiaozhi-server/config.yaml | 1172
xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/sys/loginControllerTest.java | 72
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaodongdong_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-web/src/apis/module/timbre.js | 97
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/CaptchaService.java | 45
xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentCorrectWordMappingDao.xml | 27
xiaozhi-esp32-server/main/digital-human/css/index.css | 1480
xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/agent/service/impl/AgentSnapshotRedactionRunnerTest.java | 77
xiaozhi-esp32-server/main/manager-web/src/assets/login/down-arrow.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/i18n/index.ts | 79
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hilexin/wn9_index | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/multinet_model/mn6_en/vocab | 496
xiaozhi-esp32-server/main/xiaozhi-server/models/snakers4_silero-vad/src/silero_vad/data/silero_vad.jit | 0
xiaozhi-esp32-server/main/manager-mobile/src/pages/device/deviceTimeUtils.test.mjs | 15
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji32/shocked.png | 0
xiaozhi-esp32-server/main/manager-web/src/views/TemplateQuickConfig.vue | 482
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/unified_tool_handler.py | 242
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507071130.sql | 26
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentSnapshotService.java | 25
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_64/winking.gif | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202603101723.sql | 4
xiaozhi-esp32-server/docs/docker/start.sh | 13
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/SysParamsController.java | 301
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9s_hilexin/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/natori_pro_exp_t03.can3 | 0
xiaozhi-esp32-server/main/manager-mobile/src/i18n/pt_BR.ts | 597
xiaozhi-esp32-server/main/manager-web/src/views/KnowledgeBaseItem.vue | 623
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_sophia_tts_v1/_MODEL_INFO_ | 2
xiaozhi-esp32-server/main/manager-web/src/i18n/zh_CN.js | 1687
xiaozhi-esp32-server/main/manager-api/src/main/resources/lua/getKeyOrCreate.lua | 11
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hiwalle_tts2/wn9_index | 0
xiaozhi-esp32-server/main/manager-web/src/components/AddDeviceDialog.vue | 126
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/auth.py | 126
xiaozhi-esp32-server/main/digital-human/wakeword_runtime/__init__.py | 1
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/server_mcp/mcp_executor.py | 89
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/dto/VoiceCloneDTO.java | 23
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_jarvis_tts/_MODEL_INFO_ | 2
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/vllm.py | 23
xiaozhi-esp32-server/main/digital-human/js/core/audio/stream-context.js | 221
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504181536.sql | 7
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji64/kissy.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_heyily_tts2/wn9_index | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/handle/textMessageHandler.py | 21
xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/service/impl/DeviceAddressBookServiceImplTest.java | 46
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_heyprinter_tts/wn9_index | 0
xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/index.vue | 220
xiaozhi-esp32-server/main/digital-human/js/utils/logger.js | 43
xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/microphone.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji64/loving.png | 0
xiaozhi-esp32-server/main/manager-web/src/utils/index.js | 289
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/mcp_endpoint/__init__.py | 21
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaoxiaoan_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/20x20.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/models/SenseVoiceSmall/example/ja.mp3 | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/vo/UserShowDeviceListVO.java | 51
xiaozhi-esp32-server/main/manager-web/src/components/FirmwareDialog.vue | 233
xiaozhi-esp32-server/main/manager-web/src/utils/constant.js | 22
xiaozhi-esp32-server/main/digital-human/wakeword_runtime/core/microphone.py | 91
xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m03.motion3.json | 1295
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/asr/vosk.py | 91
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiao3feng1xiao3feng1_tts3/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_128/confused.gif | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tts/siliconflow.py | 56
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/ShiroService.java | 22
xiaozhi-esp32-server/main/manager-mobile/src/utils/index.ts | 335
xiaozhi-esp32-server/main/manager-web/src/components/EditVoiceDialog.vue | 149
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/vo/CorrectWordFileVO.java | 30
xiaozhi-esp32-server/main/manager-web/src/components/KnowledgeBaseDialog.vue | 243
xiaozhi-esp32-server/main/manager-web/public/generator/static/fonts/font_puhui_deepseek_16_4.bin | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hitelly_tts/wn9_index | 0
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/natori_pro_t06.cmo3 | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/multinet_model/fst/commands_en.txt | 49
xiaozhi-esp32-server/main/xiaozhi-server/docker-compose.yml | 22
xiaozhi-esp32-server/main/manager-web/src/components/CustomDialog.vue | 233
xiaozhi-esp32-server/docs/mcp-get-device-info.md | 40
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/controller/VoiceCloneController.java | 200
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/exception/RenException.java | 67
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_64/angry.gif | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512221117.sql | 10
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelConfigService.java | 66
xiaozhi-esp32-server/main/manager-mobile/src/api/agent/types.ts | 178
xiaozhi-esp32-server/main/manager-web/src/assets/login/shield.png | 0
xiaozhi-esp32-server/docs/images/demo10.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/base/tool_executor.py | 27
xiaozhi-esp32-server/docs/images/image-clone-integration-01.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202510251150.sql | 19
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentChatHistoryReportDTO.java | 33
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/SysDictTypeDTO.java | 56
xiaozhi-esp32-server/main/xiaozhi-server/core/api/base_handler.py | 24
xiaozhi-esp32-server/main/manager-api/src/main/resources/logback-spring.xml | 127
xiaozhi-esp32-server/main/README.md | 592
xiaozhi-esp32-server/main/manager-web/src/components/VoicePrintDialog.vue | 286
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/user/UserDetail.java | 19
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/llm/gemini/gemini.py | 213
xiaozhi-esp32-server/main/manager-web/public/user-agreement.html | 184
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_05.motion3.json | 2345
xiaozhi-esp32-server/main/manager-mobile/src/store/lang.ts | 41
xiaozhi-esp32-server/main/manager-web/src/components/TtsModel.vue | 707
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/memory/mem_report_only/mem_report_only.py | 20
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/vo/SysDictTypeVO.java | 47
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji64/embarrassed.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504112058.sql | 61
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/SM2Utils.java | 130
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/current_time.py | 68
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/asr/aliyun_stream.py | 335
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/vo/AdminPageUserVO.java | 31
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_himfive/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_ni3hao3xiao3mai4_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/sys/SysParamsDao.xml | 23
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentTagDao.java | 24
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512301430.sql | 94
xiaozhi-esp32-server/docs/index-stream-integration.md | 195
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_histackchan_tts3/wn9_index | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji64/happy.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaokangtongxue_tts2/wn9_index | 0
xiaozhi-esp32-server/main/manager-web/src/apis/module/agentSnapshotApi.test.mjs | 123
xiaozhi-esp32-server/main/manager-web/src/App.vue | 179
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaoxiaozhi_tts/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-web/src/assets/user-avatar2.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji64/silly.png | 0
xiaozhi-esp32-server/docs/images/logo_qinren.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceAddressBookPermissionDTO.java | 21
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaoxiaoyi_tts2/wn9_index | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_heywanda_tts/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/xiaozhi-server/config/assets/bind_code.wav | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tts/custom.py | 63
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602061650.sql | 48
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/KnowledgeFilesService.java | 132
xiaozhi-esp32-server/main/manager-web/src/assets/setting/addressBook.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentContextProviderServiceImpl.java | 35
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/asr/openai.py | 70
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tts/aliyun_stream.py | 649
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/unified_tool_manager.py | 124
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentMcpAccessPointController.java | 67
xiaozhi-esp32-server/main/manager-web/src/registerServiceWorker.js | 113
xiaozhi-esp32-server/.dockerignore | 7
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/dao/CorrectWordItemDao.java | 15
xiaozhi-esp32-server/main/manager-mobile/.commitlintrc.cjs | 3
xiaozhi-esp32-server/main/manager-mobile/src/store/config.ts | 66
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn8_hilexin/wn8_index | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602021555.sql | 1
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202605251426.sql | 37
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Sad.exp3.json | 120
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_jarvis_tts/wn9_index | 0
xiaozhi-esp32-server/main/xiaozhi-server/config/assets/bind_code/8.wav | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/wakeup_word.py | 140
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_128/confident.gif | 0
xiaozhi-esp32-server/main/digital-human/js/ui/background-load.js | 27
xiaozhi-esp32-server/main/xiaozhi-server/core/handle/sendAudioHandle.py | 353
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hai1xiao3ou1_tts3/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-mobile/src/layouts/default.vue | 17
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507071530.sql | 56
xiaozhi-esp32-server/main/manager-web/src/assets/device-avatars/xiaozhi-logo13.png | 0
xiaozhi-esp32-server/docs/homeassistant-integration.md | 226
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/llm/fastgpt/fastgpt.py | 68
xiaozhi-esp32-server/main/xiaozhi-server/plugins_func/functions/play_music.py | 234
xiaozhi-esp32-server/main/manager-mobile/src/pages/forgot-password/index.vue | 850
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9s_hilexin/wn9_index | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507101203.sql | 38
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hijason_tts2/wn9_index | 0
xiaozhi-esp32-server/main/manager-web/src/views/home.vue | 905
xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m06.motion3.json | 1947
xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/voicePreviewUtils.test.mjs | 50
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_64/relaxed.gif | 0
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/natori_pro_motions_t03.can3 | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelProviderService.java | 30
xiaozhi-esp32-server/docs/images/demo0.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/asr/qwen3_asr_flash.py | 111
xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentDao.xml | 120
xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/voicePreviewUtils.mjs | 42
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/controller/OTAMagController.java | 334
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tts/default.py | 23
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/llm/AliBL/AliBL.py | 104
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hiandy_tts2/wn9_data | 0
xiaozhi-esp32-server/main/manager-mobile/eslint.config.mjs | 43
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn8_alexa/_MODEL_INFO_ | 1
xiaozhi-esp32-server/docs/mqtt-gateway-integration.md | 187
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_mycroft_tts/wn9_index | 0
xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/robot_activate.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202510250956.sql | 24
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaoyaxiaoya_tts2/wn9_data | 0
xiaozhi-esp32-server/main/digital-human/js/config/manager.js | 127
xiaozhi-esp32-server/main/xiaozhi-server/plugins_func/functions/change_role.py | 58
xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/sys/SysDictTypeDao.xml | 6
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_jarvis_tts/wn9_data | 0
xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/config/service/impl/ConfigServiceImplTest.java | 96
xiaozhi-esp32-server/main/digital-human/wakeword_runtime/plugins/wake_word.py | 53
xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m08.motion3.json | 1023
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509221530.sql | 7
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaoxiaozhi/wn9_data | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202601261730.sql | 14
xiaozhi-esp32-server/docs/images/alisms/sms-02.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/password/PasswordUtils.java | 40
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hiesp/wn9_index | 0
xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/58x58.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_sophia_tts/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_customword/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-web/src/components/ParamDialog.vue | 300
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506031639.sql | 45
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202601051433.sql | 11
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji32/neutral.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentPluginMappingMapper.java | 22
xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/87x87.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/multinet_model/mn7_cn/vocab | 408
xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentTagDao.xml | 56
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9s_alexa/wn9_data | 0
xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/1024x1024.png | 0
xiaozhi-esp32-server/main/manager-web/src/components/AgentSnapshotDialog.vue | 2707
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_bluechip_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/docs/images/fishspeech/autodl-01.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/service/VoiceCloneService.java | 78
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/asr/sherpa_onnx_local.py | 150
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/vo/DeviceOtaVO.java | 59
xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/model/ModelProviderDao.xml | 9
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/validator/ValidatorUtils.java | 77
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/TokenGenerator.java | 45
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/service/CrudService.java | 28
xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/80x80.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/redis/SysParamsRedis.java | 35
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/p3.py | 59
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Surprised.exp3.json | 120
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/validator/group/AddGroup.java | 8
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509051745.sql | 45
xiaozhi-esp32-server/docs/images/logo_huiyuan.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaoxiaoxin_tts/_MODEL_INFO_ | 2
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaomeitongxue_tts/_MODEL_INFO_ | 2
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/dao/SysUserTokenDao.java | 24
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/device_iot/iot_descriptor.py | 46
xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/security/SysUserTokenDao.xml | 17
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/dao/BaseDao.java | 12
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentSnapshotPageDTO.java | 28
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9s_hijason/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/digital-human/wakeword_runtime/plugins/__init__.py | 4
xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_zh_CN.properties | 213
xiaozhi-esp32-server/main/xiaozhi-server/agent-base-prompt.txt | 83
xiaozhi-esp32-server/main/digital-human/js/config/default-mcp-tools.json | 90
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/Result.java | 63
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/server_plugins/__init__.py | 5
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/handler/FieldMetaObjectHandler.java | 74
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentChatHistoryController.java | 246
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaomingtongxue_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/memory/mem_local_short/mem_local_short.py | 201
xiaozhi-esp32-server/main/digital-human/js/core/network/ota-connector.js | 109
xiaozhi-esp32-server/main/manager-web/src/assets/setting/voiceprint.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_heywillow_tts/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/chat_activate.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604211515.sql | 38
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatHistoryService.java | 90
xiaozhi-esp32-server/main/manager-web/src/assets/device-avatars/xiaozhi-logo11.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/ContextProviderDTO.java | 19
xiaozhi-esp32-server/main/manager-web/src/components/CustomPagination.vue | 65
xiaozhi-esp32-server/docs/images/firmware-setting-ota.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/pages/chat-history/index.vue | 410
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_64/cool.gif | 0
xiaozhi-esp32-server/main/manager-web/src/assets/home/red-info.png | 0
xiaozhi-esp32-server/main/manager-web/src/views/OtaManagement.vue | 437
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/entity/DeviceEntity.java | 67
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentVoicePrintController.java | 87
xiaozhi-esp32-server/main/manager-mobile/src/router/interceptor.ts | 66
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/entity/CorrectWordFileEntity.java | 41
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tts/gpt_sovits_v3.py | 66
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/KnowledgeBaseDTO.java | 73
xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentSnapshotDao.xml | 123
xiaozhi-esp32-server/main/manager-mobile/tsconfig.json | 41
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaomeitongxue_tts/wn9_index | 0
xiaozhi-esp32-server/main/manager-mobile/src/components/.gitkeep | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentTemplateVO.java | 15
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202605071037.sql | 5
xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/hiyori_pro_t11.cmo3 | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_computer_tts/wn9_index | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604201719.sql | 102
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_alexa/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentPluginMappingServiceImpl.java | 114
xiaozhi-esp32-server/main/manager-web/src/assets/home/close.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/SysDictTypeController.java | 92
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/Enums/XiaoZhiMcpJsonRpcJson.java | 44
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tts/aliyun.py | 218
xiaozhi-esp32-server/main/digital-human/js/core/network/websocket.js | 591
xiaozhi-esp32-server/main/manager-web/tests/deviceTime.test.mjs | 52
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentMcpAccessPointServiceImpl.java | 235
xiaozhi-esp32-server/main/manager-web/src/apis/module/knowledgeBase.js | 318
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_128/embarrassed.gif | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/controller/KnowledgeFilesController.java | 231
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_bluechip_tts2/wn9_index | 0
xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/provider.vue | 208
xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/40x40.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_de_DE.properties | 34
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604211700.sql | 45
xiaozhi-esp32-server/main/xiaozhi-server/models/snakers4_silero-vad/hubconf.py | 56
xiaozhi-esp32-server/main/digital-human/images/2.png | 0
xiaozhi-esp32-server/main/manager-mobile/env/.env.test | 4
xiaozhi-esp32-server/main/manager-web/.env.production | 4
xiaozhi-esp32-server/main/manager-web/src/assets/knowledge-base/level.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/annotation/DataFilter.java | 33
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/llm/openai/openai.py | 179
xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.2048/texture_01.png | 0
xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/29x29.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hiandy_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/KnowledgeBaseAdapter.java | 224
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tts/dto/dto.py | 43
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sms/service/imp/ALiYunSmsService.java | 77
xiaozhi-esp32-server/main/manager-web/public/generator/static/multinet_model/mn6_cn/mn6_data | 0
xiaozhi-esp32-server/main/xiaozhi-server/config/assets/tts_notify.mp3 | 0
xiaozhi-esp32-server/main/manager-mobile/src/static/images/home/main-top-bg.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/pages/device-config/components/wifi-config.vue | 236
xiaozhi-esp32-server/main/manager-web/src/views/ParamsManagement.vue | 1064
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512041515.sql | 14
xiaozhi-esp32-server/main/manager-web/public/generator/index.html | 14
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaojianxiaojian_tts2/wn9_data | 0
xiaozhi-esp32-server/main/manager-mobile/uno.config.ts | 65
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaodongdong_tts2/wn9_index | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentTemplateController.java | 158
xiaozhi-esp32-server/main/manager-mobile/src/main.ts | 25
xiaozhi-esp32-server/main/digital-human/favicon.ico | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506010920.sql | 29
xiaozhi-esp32-server/main/manager-web/scripts/check-i18n.js | 96
xiaozhi-esp32-server/main/manager-web/src/apis/module/admin.js | 174
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_64/embarrassed.gif | 0
xiaozhi-esp32-server/main/manager-web/src/views/KnowledgeBaseManagement.vue | 1269
xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_de_DE.properties | 213
xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/network_activate.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/asr/fun_server.py | 165
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaolongxiaolong_tts/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji32/crying.png | 0
xiaozhi-esp32-server/LICENSE | 21
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/dialogue.py | 163
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509091633.sql | 16
xiaozhi-esp32-server/main/xiaozhi-server/config/assets/bind_code/4.wav | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/PasswordDTO.java | 24
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DevicePageUserDTO.java | 27
xiaozhi-esp32-server/main/manager-web/src/components/CustomButton.vue | 161
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentMcpAccessPointService.java | 25
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn7q8_xiaoaitongxue/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.physics3.json | 1569
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202607101200.sql | 8
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_64/silly.gif | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hitelly_tts/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hijoy_tts/wn9_index | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/device_iot/__init__.py | 12
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602051125.sql | 2
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/asr/doubao_stream.py | 430
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/validator/group/UpdateGroup.java | 8
xiaozhi-esp32-server/main/digital-human/js/core/mcp/tools.js | 525
xiaozhi-esp32-server/main/digital-human/js/live2d/pixi.js | 43379 +++++++++
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_heyily_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/AgentSnapshotPanel.vue | 1451
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/entity/OtaEntity.java | 61
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaosurou_tts2/wn9_index | 0
xiaozhi-esp32-server/docs/all-in-one-digital-human-setup.md | 442
xiaozhi-esp32-server/main/manager-web/public/generator/static/multinet_model/mn7_cn/_MODEL_INFO_ | 2
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/JsonRpcTwo.java | 21
xiaozhi-esp32-server/main/xiaozhi-server/core/http_server.py | 92
xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/agent/typehandler/ContextProviderListTypeHandlerTest.java | 73
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori_pro_t06.cdi3.json | 711
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/dao/TimbreDao.java | 17
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_128/neutral.gif | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202605081008.sql | 8
xiaozhi-esp32-server/main/manager-mobile/src/env.d.ts | 34
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Smile.exp3.json | 120
xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/agent/dto/AgentUpdateDTOTest.java | 44
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaobinxiaobin_tts/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/src/i18n/index.js | 66
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/device_iot/iot_executor.py | 238
xiaozhi-esp32-server/main/manager-web/src/apis/module/correctWord.js | 166
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java | 263
xiaozhi-esp32-server/docs/Deployment.md | 291
xiaozhi-esp32-server/docker-setup.sh | 413
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentChatTitleEntity.java | 36
xiaozhi-esp32-server/main/manager-mobile/src/api/device/types.ts | 21
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji32/relaxed.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/model/ModelConfigDao.xml | 33
xiaozhi-esp32-server/main/manager-web/public/favicon.ico | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/Enums/AgentChatHistoryType.java | 21
xiaozhi-esp32-server/main/manager-web/src/assets/device-avatars/xiaozhi-logo5.png | 0
xiaozhi-esp32-server/main/manager-web/src/i18n/de.js | 1687
xiaozhi-esp32-server/main/manager-web/public/generator/static/multinet_model/mn7_en/_MODEL_INFO_ | 2
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506091720.sql | 3
xiaozhi-esp32-server/main/xiaozhi-server/core/handle/textHandler/serverMessageHandler.py | 92
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tts/tencent.py | 189
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji64/funny.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602281000.sql | 29
xiaozhi-esp32-server/main/manager-web/src/assets/home/setting-user.png | 0
xiaozhi-esp32-server/docs/images/image-ha-integration-03.png | 0
xiaozhi-esp32-server/main/manager-web/src/views/ReplacementWordManagement.vue | 512
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tts/cozecn.py | 63
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaoyutongxue_tts2/wn9_index | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/llm/xinference/xinference.py | 88
xiaozhi-esp32-server/main/xiaozhi-server/config/config_loader.py | 178
xiaozhi-esp32-server/docs/images/deploy1.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/utils/WebSocketTestHandler.java | 46
xiaozhi-esp32-server/docs/images/demo2.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/server_plugins/plugin_executor.py | 156
xiaozhi-esp32-server/main/xiaozhi-server/models/snakers4_silero-vad/src/silero_vad/data/silero_vad_half.onnx | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/handle/textHandler/listenMessageHandler.py | 116
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/intent/base.py | 33
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaobaiying_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/76x76.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/memory/base.py | 28
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/XssHttpServletRequestWrapper.java | 118
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/mcp_endpoint/mcp_endpoint_client.py | 112
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/server_mcp/mcp_client.py | 261
xiaozhi-esp32-server/main/manager-mobile/env/.env | 19
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji32/funny.png | 0
xiaozhi-esp32-server/main/manager-web/src/assets/model/output_conf.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/voiceclone/VoiceCloneDao.xml | 10
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTagRelationEntity.java | 41
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji64/shocked.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/vad/base.py | 9
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hixiaoxing_tts/_MODEL_INFO_ | 1
xiaozhi-esp32-server/docs/images/conda_env_1.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509080922.sql | 8
xiaozhi-esp32-server/main/manager-web/src/assets/model/inner_conf.png | 0
xiaozhi-esp32-server/main/manager-api/README.md | 18
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/cache/strategies.py | 43
xiaozhi-esp32-server/docs/huoshan-streamTTS-voice-cloning.md | 58
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507041018.sql | 12
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604300930.sql | 57
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tts/xunfei_stream.py | 567
xiaozhi-esp32-server/main/xiaozhi-server/config/assets/bind_code/6.wav | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/service/ConfigService.java | 31
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/controller/DeviceController.java | 216
xiaozhi-esp32-server/main/manager-web/src/assets/knowledge-base/docX.png | 0
xiaozhi-esp32-server/docs/images/alisms/sms-11.png | 0
xiaozhi-esp32-server/main/manager-web/src/views/VoiceCloneManagement.vue | 652
xiaozhi-esp32-server/main/manager-web/src/views/DeviceManagement.vue | 554
xiaozhi-esp32-server/main/manager-web/src/components/CacheViewer.vue | 207
xiaozhi-esp32-server/main/manager-web/src/views/login.vue | 443
xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/device_activate.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentPluginMapping.java | 54
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/SysUserTokenServiceImpl.java | 116
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/RAGFlowClient.java | 278
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202603311200.sql | 2
xiaozhi-esp32-server/main/manager-mobile/src/layouts/fg-tabbar/fg-tabbar.vue | 66
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/config/WebMvcConfig.java | 156
xiaozhi-esp32-server/main/manager-mobile/src/i18n/zh_CN.ts | 597
xiaozhi-esp32-server/main/manager-mobile/src/hooks/useUpload.ts | 160
xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.model3.json | 82
xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m02.motion3.json | 1428
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504082211.sql | 50
xiaozhi-esp32-server/main/manager-web/public/generator/workers/gif.js | 3
xiaozhi-esp32-server/main/manager-web/src/assets/device-avatars/xiaozhi-logo9.png | 0
xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m04.motion3.json | 860
xiaozhi-esp32-server/main/manager-mobile/src/api/device/device.ts | 71
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentSnapshotController.java | 79
xiaozhi-esp32-server/main/xiaozhi-server/models/SenseVoiceSmall/example/yue.mp3 | 0
xiaozhi-esp32-server/main/manager-mobile/src/i18n/zh_TW.ts | 597
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/KnowledgeFilesDTO.java | 119
xiaozhi-esp32-server/main/manager-web/src/i18n/en.js | 1687
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/intent/function_call/function_call.py | 22
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/biz/AgentChatHistoryBizService.java | 22
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/RetrievePasswordDTO.java | 34
xiaozhi-esp32-server/main/xiaozhi-server/plugins_func/functions/hass_init.py | 54
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentEntity.java | 100
xiaozhi-esp32-server/main/manager-web/public/generator/static/README.md | 20
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9s_nihaoxiaozhi/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-web/vue.config.js | 181
xiaozhi-esp32-server/main/manager-mobile/src/i18n/vi.ts | 597
xiaozhi-esp32-server/docs/device-call-guide.md | 148
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_sophia_tts/wn9_index | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/handle/intentHandler.py | 252
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/intent/nointent/nointent.py | 22
xiaozhi-esp32-server/main/manager-web/src/assets/model/model.png | 0
xiaozhi-esp32-server/docs/images/image-ha-integration-01.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaolongxiaolong_tts/wn9_index | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_128/winking.gif | 0
xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/180x180.png | 0
xiaozhi-esp32-server/main/digital-human/js/app.js | 373
xiaozhi-esp32-server/main/manager-web/src/assets/knowledge-base/star.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hijason_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/Dockerfile-web | 50
xiaozhi-esp32-server/docs/images/demo4.png | 0
xiaozhi-esp32-server/main/manager-web/src/assets/xiaozhi-ai_de.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/store/plugin.ts | 58
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/asr/utils.py | 79
xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/72x72.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_64/delicious.gif | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaosurou_tts2/wn9_data | 0
xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/agentSnapshotContracts.test.mjs | 103
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatTitleServiceImpl.java | 60
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/service/impl/CrudServiceImpl.java | 76
xiaozhi-esp32-server/main/manager-api/src/main/resources/application-dev.yml | 51
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysParamsService.java | 71
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentChatHistoryDTO.java | 28
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentChatHistoryEntity.java | 81
xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/common/utils/AESUtilsTest.java | 85
xiaozhi-esp32-server/main/manager-mobile/src/utils/uploadFile.ts | 327
xiaozhi-esp32-server/main/manager-web/public/generator/assets/index-B3ns2K1c.css | 1
xiaozhi-esp32-server/main/manager-web/src/assets/device-avatars/xiaozhi-logo7.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/uni.scss | 77
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504301341.sql | 85
xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/60x60.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji32/delicious.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java | 353
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/password/PasswordEncoder.java | 33
xiaozhi-esp32-server/main/manager-web/public/generator/static/fonts/font_noto_qwen_30_4.bin | 0
xiaozhi-esp32-server/main/xiaozhi-server/plugins_func/functions/get_weather.py | 231
xiaozhi-esp32-server/main/manager-mobile/src/pages/register/index.vue | 903
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/ServerActionResponseDTO.java | 34
xiaozhi-esp32-server/main/manager-web/src/apis/module/dict.js | 227
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/SysDictDataController.java | 105
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hixiaoxing_tts/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaoxiaoyi_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_haixiaowu_tts/wn9_index | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/controller/ConfigController.java | 55
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_heyivy_tts2/wn9_data | 0
xiaozhi-esp32-server/main/digital-human/js/live2d/live2dcubismcore.min.js | 9
xiaozhi-esp32-server/main/digital-human/js/utils/blocking-queue.js | 103
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/vad.py | 19
xiaozhi-esp32-server/main/xiaozhi-server/models/SenseVoiceSmall/configuration.json | 14
xiaozhi-esp32-server/main/manager-web/src/assets/home/search.png | 0
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_06.motion3.json | 2082
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Blushing.exp3.json | 120
xiaozhi-esp32-server/docs/images/alisms/sms-25.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/gc_manager.py | 122
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatSummaryServiceImpl.java | 521
xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/service/impl/DeviceAutoUpdateTest.java | 104
xiaozhi-esp32-server/main/xiaozhi-server/config/assets/wakeup_words.wav | 0
xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/microphone_activate.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/152x152.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dao/DeviceDao.java | 21
xiaozhi-esp32-server/main/manager-web/public/generator/static/fonts/noto_qwen.ttf | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9s_hiesp/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-web/src/components/AddModelDialog.vue | 390
xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_zh_TW.properties | 213
xiaozhi-esp32-server/main/manager-web/src/apis/module/ota.js | 123
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202503141346.sql | 185
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AiAgentChatAudioDao.java | 18
xiaozhi-esp32-server/main/manager-mobile/src/static/images/.gitkeep | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_himiaomiao_tts/wn9_data | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/AdminApplication.java | 13
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/llm/service/LLMService.java | 79
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_128/sad.gif | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/memory/mem0ai/mem0ai.py | 121
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AiAgentChatHistoryDao.java | 50
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/llm/system_prompt.py | 103
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysUserUtilServiceImpl.java | 37
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji32/winking.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentTagDTO.java | 20
xiaozhi-esp32-server/main/manager-web/public/generator/static/multinet_model/mn7_cn/mn7_index | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/service/CorrectWordFileService.java | 96
xiaozhi-esp32-server/main/manager-web/src/assets/user-avatar5.png | 0
xiaozhi-esp32-server/docs/images/logo_xuanfeng.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512031517.sql | 6
xiaozhi-esp32-server/main/manager-web/src/views/roleConfig.vue | 2419
xiaozhi-esp32-server/main/manager-mobile/package.json | 163
xiaozhi-esp32-server/main/manager-web/src/apis/httpRequest.js | 172
xiaozhi-esp32-server/main/xiaozhi-server/config/assets/bind_code/1.wav | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_64/shocked.gif | 0
xiaozhi-esp32-server/main/manager-mobile/src/pages/settings/index.vue | 515
xiaozhi-esp32-server/main/manager-web/src/styles/global.scss | 46
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/utils/WebSocketValidator.java | 60
xiaozhi-esp32-server/docs/images/alisms/sms-24.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_128/cool.gif | 0
xiaozhi-esp32-server/main/manager-web/src/components/VoiceResourceDialog.vue | 161
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/user/SecurityUser.java | 50
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/llm/dify/dify.py | 107
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/PropertiesUtils.java | 11
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceRegisterDTO.java | 23
xiaozhi-esp32-server/main/manager-mobile/src/api/chat-history/index.ts | 2
xiaozhi-esp32-server/docs/images/manager-mobile/打包发行步骤2.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/voiceprint_provider.py | 198
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/XssConfig.java | 33
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaokangtongxue_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_miaomiaotongxue_tts/wn9_index | 0
xiaozhi-esp32-server/docs/voiceprint-integration.md | 235
xiaozhi-esp32-server/main/xiaozhi-server/core/handle/textHandler/pingMessageHandler.py | 45
xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/sys/service/impl/SysDictDataServiceImplTest.java | 35
xiaozhi-esp32-server/docs/images/demo7.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_128/silly.gif | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/__init__.py | 1
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202605071120.sql | 6
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602051017.sql | 115
xiaozhi-esp32-server/main/xiaozhi-server/models/snakers4_silero-vad/src/silero_vad/__init__.py | 12
xiaozhi-esp32-server/main/xiaozhi-server/core/handle/textHandler/mcpMessageHandler.py | 22
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_03.motion3.json | 2596
xiaozhi-esp32-server/main/xiaozhi-server/core/handle/helloHandle.py | 159
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentDTO.java | 53
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn7q8_xiaoaitongxue/wn7q8_index | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_ni3hao3xiao3mai4_tts2/wn9_data | 0
xiaozhi-esp32-server/main/manager-mobile/src/hooks/useRequest.ts | 51
xiaozhi-esp32-server/main/manager-web/src/assets/home/info.png | 0
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_00.motion3.json | 1432
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaoxiaozhi_tts/wn9_index | 0
xiaozhi-esp32-server/main/manager-api/src/test/resources/application.yml | 25
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_alexa/wn9_data | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/AESUtils.java | 78
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelConfigDTO.java | 46
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202601231530.sql | 87
xiaozhi-esp32-server/docs/ragflow-integration.md | 269
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/controller/KnowledgeBaseController.java | 163
xiaozhi-esp32-server/main/manager-mobile/vite.config.ts | 182
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_128/sleepy.gif | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506261637.sql | 2
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentCorrectWordMappingDao.java | 21
xiaozhi-esp32-server/main/manager-web/public/generator/static/multinet_model/mn6_cn/vocab | 408
xiaozhi-esp32-server/main/manager-web/src/assets/xiaozhi-ai_zh_TW.png | 0
xiaozhi-esp32-server/main/manager-web/src/assets/login/username.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dao/SysUserDao.java | 14
xiaozhi-esp32-server/main/manager-web/src/views/VoiceResourceManagement.vue | 376
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hifairy_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/XssFilter.java | 55
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/enums/ServerActionEnum.java | 33
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji64/thinking.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507081646.sql | 3
xiaozhi-esp32-server/main/manager-web/src/i18n/zh_TW.js | 1687
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaomeitongxue_tts/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/src/assets/header/user_management.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/models/SenseVoiceSmall/example/zh.mp3 | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/correctword/CorrectWordItemDao.xml | 13
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_computer_tts/wn9_data | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504112044.sql | 55
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTagEntity.java | 41
xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_zh_CN.properties | 34
xiaozhi-esp32-server/main/manager-mobile/src/store/provider.ts | 23
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori_pro_t06.physics3.json | 966
xiaozhi-esp32-server/main/digital-human/wakeword_runtime/config.json | 25
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentSnapshotServiceImpl.java | 1772
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hai1xiao3ou1_tts3/wn9_index | 0
xiaozhi-esp32-server/main/xiaozhi-server/plugins_func/functions/hass_state.py | 268
xiaozhi-esp32-server/main/xiaozhi-server/config/assets/wakeup_words_short.wav | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_64/sleepy.gif | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/llm/service/impl/OpenAIStyleLLMServiceImpl.java | 420
xiaozhi-esp32-server/main/manager-mobile/src/store/index.ts | 21
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_04.motion3.json | 2316
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaotexiaote_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/service/impl/CorrectWordFileServiceImpl.java | 296
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji32/silly.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji64/winking.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9s_hilexin/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/multinet_model/fst/commands_cn.txt | 313
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504291043.sql | 504
xiaozhi-esp32-server/.github/ISSUE_TEMPLATE/feature_request.md | 19
xiaozhi-esp32-server/main/manager-web/src/store/index.js | 77
xiaozhi-esp32-server/docs/contributor_open_letter.md | 50
xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/60x60.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/hooks/usePageAuth.ts | 50
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn8_hiesp/wn8_data | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/password/BCrypt.java | 691
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_haixiaowu_tts/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/util.py | 609
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/enums/ServerActionResponseEnum.java | 45
xiaozhi-esp32-server/docs/mcp-endpoint-enable.md | 126
xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/knowledge/KnowledgeBaseDao.xml | 36
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/config/KnowledgeBaseConfig.java | 22
xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/1024x1024.png | 0
xiaozhi-esp32-server/docs/images/alisms/sms-22.png | 0
xiaozhi-esp32-server/main/digital-human/js/core/audio/opus-codec.js | 182
xiaozhi-esp32-server/docs/images/demo9.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/entity/DocumentEntity.java | 97
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504151206.sql | 28
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/dto/AgentModelsDTO.java | 25
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_64/thinking.gif | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji64/laughing.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/multinet_model/mn7_cn/mn7_data | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hijolly_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/.github/dependabot.yml | 6
xiaozhi-esp32-server/main/manager-mobile/src/http/request/alova.ts | 151
xiaozhi-esp32-server/main/manager-mobile/src/pages/login/privacy-policy-zh.vue | 402
xiaozhi-esp32-server/main/manager-mobile/src/api/agent/agent.ts | 300
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/service/impl/BaseServiceImpl.java | 232
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hifairy_tts2/wn9_index | 0
xiaozhi-esp32-server/main/manager-web/src/assets/device-avatars/xiaozhi-logo2.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/static/logo.svg | 33
xiaozhi-esp32-server/main/manager-web/src/assets/home/equipment.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/system.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/handle/textMessageType.py | 12
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/exp_01.exp3.json | 120
xiaozhi-esp32-server/main/manager-web/src/assets/home/avatar.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages.properties | 213
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/DeviceAddressBookService.java | 52
xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/192x192.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji64/crying.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dao/SysParamsDao.java | 39
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/config/RestTemplateConfig.java | 21
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/dao/VoiceCloneDao.java | 26
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji32/thinking.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/device/DeviceAddressBookDao.xml | 39
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604010930.sql | 7
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/aspect/RedisAspect.java | 41
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/redis/RedisConfig.java | 33
xiaozhi-esp32-server/main/manager-mobile/src/layouts/tabbar.vue | 19
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_64/confident.gif | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9s_hiesp/wn9_index | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji32/cool.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/multinet_model/mn7_en/vocab | 496
xiaozhi-esp32-server/main/xiaozhi-server/models/SenseVoiceSmall/demo.py | 27
xiaozhi-esp32-server/docs/context-provider-integration.md | 224
xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/example.png | 0
xiaozhi-esp32-server/main/manager-mobile/patches/@dcloudio__uni-h5.patch | 13
xiaozhi-esp32-server/main/manager-mobile/src/pages/voiceprint/index.vue | 657
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_linaiban_tts2/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji32/sad.png | 0
xiaozhi-esp32-server/docs/images/image-ha-integration-05.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/plugins_func/functions/get_news_from_chinanews.py | 258
xiaozhi-esp32-server/main/manager-web/src/components/ReplacementWordDialog.vue | 333
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/llm/homeassistant/homeassistant.py | 64
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509161701.sql | 54
xiaozhi-esp32-server/main/manager-web/public/user-agreement-en.html | 301
xiaozhi-esp32-server/main/digital-human/js/core/audio/recorder.js | 384
xiaozhi-esp32-server/main/digital-human/README.md | 46
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/entity/ModelProviderEntity.java | 47
xiaozhi-esp32-server/main/manager-web/public/generator/static/multinet_model/mn6_cn/_MODEL_INFO_ | 2
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9s_alexa/wn9_index | 0
xiaozhi-esp32-server/main/manager-web/src/assets/home/delete.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/OtaServiceImpl.java | 85
xiaozhi-esp32-server/main/manager-mobile/src/api/voiceprint/types.ts | 29
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentUpdateDTO.java | 143
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/config/AsyncConfig.java | 40
xiaozhi-esp32-server/main/manager-mobile/src/wot-design-uni.d.ts | 56
xiaozhi-esp32-server/docs/web-search-integration.md | 71
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentVoicePrintServiceImpl.java | 411
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji32/happy.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509220926.sql | 26
xiaozhi-esp32-server/docs/mcp-endpoint-integration.md | 94
xiaozhi-esp32-server/main/manager-web/src/service-worker.js | 174
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji32/kissy.png | 0
xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.moc3 | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9s_hijason/wn9_index | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/controller/CorrectWordController.java | 116
xiaozhi-esp32-server/main/manager-web/src/assets/home/main-top-bg.png | 0
xiaozhi-esp32-server/docs/mcp-vision-integration.md | 171
xiaozhi-esp32-server/docs/firmware-setting.md | 54
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/entity/SysUserTokenEntity.java | 47
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604161357.sql | 17
xiaozhi-esp32-server/main/manager-web/src/assets/setting/agent.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_64/neutral.gif | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202508131557.sql | 26
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Angry.exp3.json | 120
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504221135.sql | 88
xiaozhi-esp32-server/main/manager-web/src/components/ManualIcon.vue | 20
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/ResourcesUtils.java | 45
xiaozhi-esp32-server/main/manager-web/src/components/ProviderDialog.vue | 373
xiaozhi-esp32-server/main/manager-web/public/generator/static/fonts/font_puhui_deepseek_14_1.bin | 0
xiaozhi-esp32-server/main/manager-web/src/components/ManualAddDeviceDialog.vue | 116
xiaozhi-esp32-server/docs/Deployment_all.md | 492
xiaozhi-esp32-server/main/manager-web/src/assets/knowledge-base/txt.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/http/README.md | 36
xiaozhi-esp32-server/main/digital-human/wakeword_runtime/core/detector_assets.py | 94
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiao3jia1xiao3jia1_tts3/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_heyivy_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/cache/config.py | 66
xiaozhi-esp32-server/main/manager-web/src/assets/user-avatar1.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji64/cool.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/homeHL.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/interceptor/DataFilterInterceptor.java | 85
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/asr.py | 24
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509220958.sql | 19
xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m01.motion3.json | 1063
xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/sys/SysUserDao.xml | 6
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji32/surprised.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/config/logger.py | 120
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/SpringContextUtils.java | 45
xiaozhi-esp32-server/docs/images/banner2.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/personal.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/hooks/.gitkeep | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/entity/SysParamsEntity.java | 51
xiaozhi-esp32-server/main/digital-human/js/utils/libopus.js | 266
xiaozhi-esp32-server/docs/images/demo13.png | 0
xiaozhi-esp32-server/main/manager-mobile/src/pages.json | 221
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaoluxiaolu_tts2/_MODEL_INFO_ | 1
xiaozhi-esp32-server/docs/readme/README_vi.md | 362
xiaozhi-esp32-server/main/manager-web/src/components/ViewPasswordDialog.vue | 91
xiaozhi-esp32-server/main/manager-web/public/generator/favicon.ico | 0
xiaozhi-esp32-server/main/manager-web/public/index.html | 31
xiaozhi-esp32-server/main/manager-mobile/.editorconfig | 13
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentChatSummaryDTO.java | 45
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/prompt_manager.py | 315
xiaozhi-esp32-server/main/manager-mobile/favicon.ico | 0
xiaozhi-esp32-server/main/manager-web/src/utils/featureManager.js | 362
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceAddressBookServiceImpl.java | 258
xiaozhi-esp32-server/main/manager-web/src/assets/header/model_config.png | 0
xiaozhi-esp32-server/main/manager-mobile/pages.config.ts | 23
xiaozhi-esp32-server/main/digital-human/wakeword_runtime/runtime/http_server.py | 352
xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/timbre/service/impl/TimbreServiceImplTest.java | 131
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/device_mcp/mcp_client.py | 93
xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/device/DeviceDao.xml | 12
xiaozhi-esp32-server/main/digital-human/wakeword_runtime/core/__init__.py | 3
xiaozhi-esp32-server/main/manager-mobile/src/pages/device-config/index.vue | 153
xiaozhi-esp32-server/main/xiaozhi-server/plugins_func/functions/get_time.py | 127
xiaozhi-esp32-server/docs/ota-upgrade-guide.md | 142
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceReportRespDTO.java | 95
xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/home.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_128/laughing.gif | 0
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/exp_05.exp3.json | 120
xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/144x144.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/twemoji64/confident.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/SysParamsDTO.java | 58
xiaozhi-esp32-server/main/manager-mobile/src/i18n/en.ts | 597
xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/hiyori_pro_t04.can3 | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AiAgentChatHistoryDao.xml | 49
xiaozhi-esp32-server/main/manager-web/src/components/agentSnapshotDisplayUtils.mjs | 186
xiaozhi-esp32-server/main/xiaozhi-server/plugins_func/functions/hass_play_music.py | 68
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/page/TokenDTO.java | 25
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/asr/tencent.py | 227
xiaozhi-esp32-server/main/xiaozhi-server/core/handle/textHandler/abortMessageHandler.py | 16
xiaozhi-esp32-server/docs/images/logo_tenclass.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/config/FilterConfig.java | 27
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaoxiaozhi/wn9_index | 0
xiaozhi-esp32-server/main/manager-web/src/assets/device-avatars/xiaozhi-logo15.png | 0
xiaozhi-esp32-server/main/manager-web/src/components/DeviceItem.vue | 215
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/Oauth2Token.java | 26
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tts/fishspeech.py | 188
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/memory/powermem/powermem.py | 385
xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/167x167.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_xiaobinxiaobin_tts/wn9_index | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/exception/RenExceptionHandler.java | 85
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/entity/SysUserEntity.java | 47
xiaozhi-esp32-server/main/digital-human/wakeword_runtime/plugins/audio.py | 37
xiaozhi-esp32-server/main/manager-web/src/assets/header/address_book.png | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentService.java | 157
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202508111734.sql | 16
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dao/ModelProviderDao.java | 15
xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/ReadMe.txt | 72
xiaozhi-esp32-server/main/manager-web/src/assets/user-avatar3.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_nihaoxiaozhi/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentTagServiceImpl.java | 198
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_64/funny.gif | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_128/happy.gif | 0
xiaozhi-esp32-server/main/manager-web/src/assets/xiaozhi-ai_zh_CN.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/device_mcp/mcp_executor.py | 92
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori.pose3.json | 44
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_astrolabe_tts/wn9_data | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/dto/TimbrePageDTO.java | 29
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/tools/device_mcp/__init__.py | 21
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202608061030.sql | 4
xiaozhi-esp32-server/main/manager-web/src/components/MacAddressMask.vue | 43
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/tts.py | 170
xiaozhi-esp32-server/main/manager-web/public/generator/static/fonts/puhui_deepseek.ttf | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/service/impl/VoiceCloneServiceImpl.java | 390
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatTitleService.java | 10
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_astrolabe_tts/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-web/src/utils/date.js | 61
xiaozhi-esp32-server/.github/ISSUE_TEMPLATE/documentation_improvement.md | 16
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/impl/RAGFlowAdapter.java | 721
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/document/DocumentDTO.java | 407
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_07.motion3.json | 1907
xiaozhi-esp32-server/main/manager-mobile/manifest.config.ts | 155
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn7q8_xiaoaitongxue/wn7q8_data | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/LlmModelBasicInfoDTO.java | 13
xiaozhi-esp32-server/main/xiaozhi-server/core/utils/output_counter.py | 50
xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/96x96.png | 0
xiaozhi-esp32-server/main/manager-web/src/components/AddressBookDialog.vue | 174
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202603091051.sql | 11
xiaozhi-esp32-server/main/manager-web/src/assets/setting/mcpAccessPoint.png | 0
xiaozhi-esp32-server/main/xiaozhi-server/mcp_server_settings.json | 56
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_heywanda_tts/wn9_data | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/AdminController.java | 109
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/llm/ollama/ollama.py | 171
xiaozhi-esp32-server/docs/images/image-clone-integration-02.png | 0
xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/ReadMe.txt | 79
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/TreeUtils.java | 71
xiaozhi-esp32-server/docs/firmware-build.md | 138
xiaozhi-esp32-server/main/digital-human/js/live2d/cubism4.min.js | 1
xiaozhi-esp32-server/main/manager-web/src/views/VoicePrint.vue | 309
xiaozhi-esp32-server/main/manager-web/src/assets/knowledge-base/default.png | 0
xiaozhi-esp32-server/.github/ISSUE_TEMPLATE/bug_report.md | 32
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_128/kissy.gif | 0
xiaozhi-esp32-server/main/manager-mobile/LICENSE | 21
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentSnapshotRedactionRunner.java | 59
xiaozhi-esp32-server/docs/images/demo11.png | 0
xiaozhi-esp32-server/main/manager-web/src/assets/login/password.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/fonts/font_puhui_deepseek_30_4.bin | 0
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202607011405.sql | 19
xiaozhi-esp32-server/docs/images/alisms/sms-31.png | 0
xiaozhi-esp32-server/.github/workflows/docker-image.yml | 88
xiaozhi-esp32-server/main/manager-mobile/src/store/user.ts | 82
xiaozhi-esp32-server/main/xiaozhi-server/config/assets/bind_not_found.wav | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/ServerActionPayloadDTO.java | 36
xiaozhi-esp32-server/main/xiaozhi-server/config/manage_api_client.py | 268
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn7_xiaoaitongxue/wn7_index | 0
xiaozhi-esp32-server/docs/images/manager-mobile/生成appid.png | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_customword/wn9_index | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hilili_tts/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9s_hijason/wn9_data | 0
xiaozhi-esp32-server/main/manager-web/src/apis/module/agent.js | 580
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_hilili_tts/wn9_index | 0
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_himfive/wn9_index | 0
xiaozhi-esp32-server/main/manager-mobile/src/pages/device/index.vue | 632
xiaozhi-esp32-server/main/manager-web/public/generator/static/emojis/noto-emoji_64/sad.gif | 0
xiaozhi-esp32-server/docs/docker-build.md | 74
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn8_hilexin/_MODEL_INFO_ | 1
xiaozhi-esp32-server/main/manager-web/public/generator/static/wakenet_model/wn9_bluechip_tts2/wn9_data | 0
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/redis/RedisUtils.java | 195
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/service/TimbreService.java | 84
xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202603231037.sql | 7
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatAudioService.java | 30
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/TokenService.java | 11
xiaozhi-esp32-server/main/manager-web/public/generator/assets/ft_render-ByO_jG18.js | 5
xiaozhi-esp32-server/main/xiaozhi-server/core/providers/asr/doubao.py | 260
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentTemplateService.java | 42
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java | 554
xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentContextProviderService.java | 25
1,578 files changed, 261,184 insertions(+), 0 deletions(-)
diff --git a/xiaozhi-esp32-server/.dockerignore b/xiaozhi-esp32-server/.dockerignore
new file mode 100644
index 0000000..bc155a7
--- /dev/null
+++ b/xiaozhi-esp32-server/.dockerignore
@@ -0,0 +1,7 @@
+.git
+__pycache__
+*.pyc
+.env
+Dockerfile
+tmp/
+data/
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/.github/ISSUE_TEMPLATE/bug_report.md b/xiaozhi-esp32-server/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..deaff83
--- /dev/null
+++ b/xiaozhi-esp32-server/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,32 @@
+---
+name: Bug 鎶ュ憡锛圔ug Report锛�
+about: 鍙嶉椤圭洰涓殑缂洪櫡鎴栭棶棰�
+title: "[Bug] 绠�鐭弿杩伴棶棰�"
+labels: bug
+assignees: ''
+---
+
+## 馃悰 闂鎻忚堪
+<!-- 娓呮櫚绠�娲佸湴鎻忚堪闂鏄粈涔� -->
+
+## 馃枼锔� 鐜淇℃伅
+- 閮ㄧ讲鏂瑰紡: 鍏ㄦā鍧楅儴缃� 杩樻槸 鍗昐erver閮ㄧ讲
+- 鐗堟湰鍙�: 渚嬪 0.3.x
+
+## 馃攳 鍛婅瘔鎴戜滑锛屽簲璇ユ�庝箞澶嶇幇杩欎釜闂
+<!-- 杩欎釜寰堥噸瑕侊紝鏂逛究鎴戜滑蹇�熷畾浣� -->
+1. 鎵撳紑 '...'
+2. 鐐瑰嚮 '...'
+3. 婊氬姩鍒� '...'
+4. 鐪嬪埌閿欒
+
+## 馃 浣犲師鏈笇鏈涙槸鎬庝箞鏍风殑
+<!-- 绠�瑕佹弿杩伴鏈熺殑姝g‘琛屼负 -->
+
+## 馃槸 鎻愪緵涓�浜涙埅鍥�
+<!-- 濡傛灉閫傜敤锛屾坊鍔犻棶棰樼殑鎴浘 -->
+1. 姣斿鏃ュ織鎴浘锛岃秺澶氳秺濂�
+2. 姣斿鐣岄潰鍙嶅簲
+
+## 馃搵 鍏朵粬淇℃伅
+<!-- 鍦ㄦ娣诲姞鍏充簬姝ら棶棰樼殑浠讳綍鍏朵粬涓婁笅鏂囦俊鎭� -->
diff --git a/xiaozhi-esp32-server/.github/ISSUE_TEMPLATE/code_improvement.md b/xiaozhi-esp32-server/.github/ISSUE_TEMPLATE/code_improvement.md
new file mode 100644
index 0000000..0ec0b14
--- /dev/null
+++ b/xiaozhi-esp32-server/.github/ISSUE_TEMPLATE/code_improvement.md
@@ -0,0 +1,19 @@
+---
+name: 浠g爜浼樺寲寤鸿锛圕ode Improvement锛�
+about: 鎻愬嚭瀵圭幇鏈変唬鐮佺殑浼樺寲鎴栨敼杩涘缓璁�
+title: "[Improvement] 绠�鐭弿杩版敼杩涘唴瀹�"
+labels: refactor
+assignees: ''
+---
+
+## 馃挕 鏀硅繘鎻忚堪
+<!-- 鎻忚堪闇�瑕佹敼杩涚殑浠g爜閮ㄥ垎浠ュ強瀛樺湪鐨勯棶棰� -->
+
+## 馃専 鏀硅繘寤鸿
+<!-- 鎻愬嚭鍏蜂綋鐨勬敼杩涙柟妗堟垨鎬濊矾 -->
+
+## 馃洜锔� 鐩稿叧浠g爜
+<!-- 濡傛灉鍙兘锛屾彁渚涚浉鍏充唬鐮佺墖娈垫垨閾炬帴 -->
+
+## 馃搵 鍏朵粬淇℃伅
+<!-- 鍦ㄦ娣诲姞浠讳綍鍏朵粬鐩稿叧淇℃伅 -->
diff --git a/xiaozhi-esp32-server/.github/ISSUE_TEMPLATE/documentation_improvement.md b/xiaozhi-esp32-server/.github/ISSUE_TEMPLATE/documentation_improvement.md
new file mode 100644
index 0000000..9cdc10e
--- /dev/null
+++ b/xiaozhi-esp32-server/.github/ISSUE_TEMPLATE/documentation_improvement.md
@@ -0,0 +1,16 @@
+---
+name: 鏂囨。鏀硅繘寤鸿锛圖ocumentation Improvement锛�
+about: 鎻愬嚭瀵归」鐩枃妗g殑鏀硅繘鎴栬ˉ鍏呭缓璁�
+title: "[Docs] 绠�鐭弿杩版敼杩涘唴瀹�"
+labels: documentation
+assignees: ''
+---
+
+## 馃摎 鏀硅繘鎻忚堪
+<!-- 鎻忚堪闇�瑕佹敼杩涚殑鏂囨。閮ㄥ垎浠ュ強瀛樺湪鐨勯棶棰� -->
+
+## 鉁� 鏀硅繘寤鸿
+<!-- 鎻愬嚭鍏蜂綋鐨勬敼杩涙柟妗堟垨鍐呭 -->
+
+## 馃搵 鍏朵粬淇℃伅
+<!-- 鍦ㄦ娣诲姞浠讳綍鍏朵粬鐩稿叧淇℃伅 -->
diff --git a/xiaozhi-esp32-server/.github/ISSUE_TEMPLATE/feature_request.md b/xiaozhi-esp32-server/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..df1c029
--- /dev/null
+++ b/xiaozhi-esp32-server/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,19 @@
+---
+name: 鍔熻兘璇锋眰锛團eature Request锛�
+about: 鎻愬嚭鏂扮殑鍔熻兘鎴栨敼杩涘缓璁�
+title: "[Feature] 绠�鐭弿杩板姛鑳�"
+labels: enhancement
+assignees: ''
+---
+
+## 馃殌 闇�姹傛弿杩�
+<!-- 娓呮櫚绠�娲佸湴鎻忚堪浣犲笇鏈涘彂鐢熶粈涔� -->
+
+## 馃幆 瑙e喅鏂规
+<!-- 鎻忚堪浣犺涓哄彲琛岀殑瑙e喅鏂规鎴栧疄鐜版柟寮� -->
+
+## 馃摑 澶囬�夋柟妗�
+<!-- 鎻忚堪浣犺�冭檻杩囩殑鏇夸唬鏂规鎴栧姛鑳� -->
+
+## 馃搵 鍏朵粬淇℃伅
+<!-- 鍦ㄦ娣诲姞鍏充簬闇�姹傜殑浠讳綍鍏朵粬涓婁笅鏂囦俊鎭� -->
diff --git a/xiaozhi-esp32-server/.github/dependabot.yml b/xiaozhi-esp32-server/.github/dependabot.yml
new file mode 100644
index 0000000..af99063
--- /dev/null
+++ b/xiaozhi-esp32-server/.github/dependabot.yml
@@ -0,0 +1,6 @@
+version: 2
+updates:
+ - package-ecosystem: pip
+ directory: /main/xiaozhi-server
+ schedule:
+ interval: weekly
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/.github/workflows/build-base-image.yml b/xiaozhi-esp32-server/.github/workflows/build-base-image.yml
new file mode 100644
index 0000000..03c4595
--- /dev/null
+++ b/xiaozhi-esp32-server/.github/workflows/build-base-image.yml
@@ -0,0 +1,49 @@
+name: Build Base Image
+
+on:
+ push:
+ branches:
+ - main
+ paths:
+ - 'main/xiaozhi-server/requirements.txt'
+ - 'Dockerfile-server-base'
+ - '.github/workflows/build-base-image.yml'
+
+jobs:
+ build-base:
+ name: Build and push server base image
+ runs-on: ubuntu-latest
+ permissions:
+ packages: write
+ contents: read
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+
+ - name: Login to GitHub Container Registry
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.actor }}
+ password: ${{ secrets.TOKEN }}
+
+ - name: Build and push server-base
+ uses: docker/build-push-action@v6
+ with:
+ context: .
+ file: Dockerfile-server-base
+ push: true
+ tags: ghcr.io/${{ github.repository }}:server-base
+ platforms: linux/amd64,linux/arm64
+ cache-from: type=gha,scope=server-base
+ cache-to: type=gha,mode=max,scope=server-base
+ build-args: |
+ BUILDKIT_PROGRESS=plain
+
+ - name: Output image info
+ run: |
+ echo "鉁� Base image built and pushed successfully!"
+ echo "馃摝 Tag: ghcr.io/${{ github.repository }}:server-base"
diff --git a/xiaozhi-esp32-server/.github/workflows/docker-image.yml b/xiaozhi-esp32-server/.github/workflows/docker-image.yml
new file mode 100644
index 0000000..7fe789b
--- /dev/null
+++ b/xiaozhi-esp32-server/.github/workflows/docker-image.yml
@@ -0,0 +1,88 @@
+name: Docker Image CI
+
+on:
+ push:
+ tags:
+ - 'v*.*.*' # 鍙湪浠� v 寮�澶寸殑鏍囩鎺ㄩ�佹椂瑙﹀彂锛屼緥濡� v1.0.0
+ workflow_dispatch:
+ workflow_run:
+ workflows: ["Build Base Image"]
+ types:
+ - completed
+
+jobs:
+ release:
+ name: Release Docker images
+ runs-on: ubuntu-latest
+ permissions:
+ packages: write
+ contents: write
+ id-token: write
+ issues: write
+ steps:
+ - name: Check Disk Space
+ run: |
+ df -h
+ docker system df
+
+ - name: Clean up Docker resources
+ run: |
+ docker system prune -af
+ docker builder prune -af
+
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+ with:
+ driver-opts: |
+ network=host
+
+ - name: Login to GitHub Container Registry
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.actor }}
+ password: ${{ secrets.TOKEN }}
+
+ - name: Extract version from tag
+ id: get_version
+ run: |
+ if [[ "$GITHUB_REF" =~ ^refs/tags/v([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then
+ echo "VERSION=${BASH_REMATCH[1]}" >> $GITHUB_ENV
+ echo "IS_VERSION=true" >> $GITHUB_ENV
+ else
+ echo "VERSION=latest" >> $GITHUB_ENV
+ echo "IS_VERSION=false" >> $GITHUB_ENV
+ fi
+
+ # 鏋勫缓 xiaozhi-server 闀滃儚
+ - name: Build and push xiaozhi-server
+ uses: docker/build-push-action@v6
+ with:
+ context: .
+ file: Dockerfile-server
+ push: true
+ tags: |
+ ${{ env.IS_VERSION == 'true' && format('ghcr.io/{0}:server_{1},ghcr.io/{0}:server_latest', github.repository, env.VERSION) || format('ghcr.io/{0}:server_latest', github.repository) }}
+ platforms: linux/amd64,linux/arm64
+ cache-from: type=gha
+ cache-to: type=gha,mode=max
+ build-args: |
+ BUILDKIT_PROGRESS=plain
+
+ # 鏋勫缓 manager-api 闀滃儚
+ - name: Build and push manager-web
+ uses: docker/build-push-action@v6
+ with:
+ context: .
+ file: Dockerfile-web
+ push: true
+ tags: |
+ ${{ env.IS_VERSION == 'true' && format('ghcr.io/{0}:web_{1},ghcr.io/{0}:web_latest', github.repository, env.VERSION) || format('ghcr.io/{0}:web_latest', github.repository) }}
+ platforms: linux/amd64,linux/arm64
+ cache-from: type=gha
+ cache-to: type=gha,mode=max
+ build-args: |
+ BUILDKIT_PROGRESS=plain
diff --git a/xiaozhi-esp32-server/.gitignore b/xiaozhi-esp32-server/.gitignore
new file mode 100644
index 0000000..bbab869
--- /dev/null
+++ b/xiaozhi-esp32-server/.gitignore
@@ -0,0 +1,199 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+.idea/
+*.py[cod]
+*$py.class
+.vscode
+.claude
+AGENTS.md
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit testdata / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+*.pid
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+/.venv-*/
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Repository-local runtimes and package-manager caches
+/.runtime/
+/main/manager-web/.npm-cache/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# music directory
+music/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+*.iml
+tmp
+.history
+.DS_Store
+main/xiaozhi-server/data
+main/xiaozhi-server/.claude
+
+main/manager-web/node_modules
+.config.yaml
+.secrets.yaml
+.private_config.yaml
+.env.development
+
+# model files
+main/xiaozhi-server/models/SenseVoiceSmall/model.pt
+main/xiaozhi-server/models/sherpa-onnx*
+/main/xiaozhi-server/audio_ref/
+/audio_ref/
+/asr-models/iic/SenseVoiceSmall/
+/main/xiaozhi-server/asr-models/iic/SenseVoiceSmall/
+/models/SenseVoiceSmall/model.pt
+my_wakeup_words.mp3
+!main/xiaozhi-server/config/assets/bind_code.wav
+!main/xiaozhi-server/config/assets/wakeup_words.wav
+!main/xiaozhi-server/config/assets/bind_not_found.wav
+!main/xiaozhi-server/config/assets/bind_code/*.wav
+!main/xiaozhi-server/config/assets/max_output_size.wav
+main/manager-api/.vscode
+# Ignore webpack cache directory
+main/manager-web/.webpack_cache/
+main/xiaozhi-server/mysql
+uploadfile
+*.json
+.vscode
+.cursor
+
+!package.json
+!**/package.json
+
+# Do not ignore env and json files inside manager-mobile
+!main/manager-mobile/**/env/
+!main/manager-mobile/**/.env*
+!main/manager-mobile/**/*.json
+!main/xiaozhi-server/**/*.json
+!main/digital-human/**/*.json
+main/digital-human/wakeword_runtime/models
+main/digital-human/wakeword_runtime/sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01
+sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01.tar.bz2
diff --git a/xiaozhi-esp32-server/Dockerfile-server b/xiaozhi-esp32-server/Dockerfile-server
new file mode 100644
index 0000000..9df93c9
--- /dev/null
+++ b/xiaozhi-esp32-server/Dockerfile-server
@@ -0,0 +1,7 @@
+# 鐢熶骇闀滃儚锛屼粎鍖呭惈搴旂敤浠g爜
+FROM ghcr.io/xinnan-tech/xiaozhi-esp32-server:server-base
+
+COPY main/xiaozhi-server .
+
+# 鍚姩搴旂敤
+CMD ["python", "app.py"]
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/Dockerfile-server-base b/xiaozhi-esp32-server/Dockerfile-server-base
new file mode 100644
index 0000000..70429ab
--- /dev/null
+++ b/xiaozhi-esp32-server/Dockerfile-server-base
@@ -0,0 +1,32 @@
+# Dockerfile-server-base
+# 鍩虹闀滃儚锛屽寘鍚郴缁熶緷璧栧拰Python鍖�
+FROM python:3.10-slim
+
+# 瀹夎绯荤粺渚濊禆
+RUN apt-get update && \
+ apt-get install -y --no-install-recommends libopus0 ffmpeg locales && \
+ sed -i '/zh_CN.UTF-8/s/^# //g' /etc/locale.gen && \
+ locale-gen && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/*
+
+# 閰嶇疆pip浣跨敤鍥藉唴闀滃儚婧愶紙闃块噷浜戯級骞惰缃秴鏃跺拰閲嶈瘯
+RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/ && \
+ pip config set global.trusted-host mirrors.aliyun.com && \
+ pip config set global.timeout 120 && \
+ pip config set install.retries 5
+
+# 璁剧疆鐜鍙橀噺浠ョ‘淇濇纭殑瀛楃缂栫爜
+ENV LANG=zh_CN.UTF-8 \
+ LC_ALL=zh_CN.UTF-8 \
+ LANGUAGE=zh_CN:zh \
+ PYTHONIOENCODING=utf-8
+
+WORKDIR /opt/xiaozhi-esp32-server
+
+# 澶嶅埗requirements.txt
+COPY main/xiaozhi-server/requirements.txt .
+
+# 瀹夎Python渚濊禆
+RUN pip install --no-cache-dir --upgrade pip setuptools wheel && \
+ pip install --no-cache-dir -r requirements.txt --default-timeout=120 --retries 5
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/Dockerfile-web b/xiaozhi-esp32-server/Dockerfile-web
new file mode 100644
index 0000000..fd0eb01
--- /dev/null
+++ b/xiaozhi-esp32-server/Dockerfile-web
@@ -0,0 +1,50 @@
+# 绗竴闃舵锛氭瀯寤篤ue鍓嶇
+FROM node:18 AS web-builder
+WORKDIR /app
+COPY main/manager-web/package*.json ./
+RUN npm install
+COPY main/manager-web .
+RUN npm run build
+
+# 绗簩闃舵锛氭瀯寤篔ava鍚庣
+FROM maven:3.9.4-eclipse-temurin-21 AS api-builder
+WORKDIR /app
+COPY main/manager-api/pom.xml .
+COPY main/manager-api/src ./src
+RUN mvn clean package -Dmaven.test.skip=true
+
+# 绗笁闃舵锛氭瀯寤烘渶缁堥暅鍍�
+FROM bellsoft/liberica-runtime-container:jre-21-glibc
+
+# 瀹夎Nginx鍜屽瓧浣撳簱
+RUN apk update && \
+ apk add --no-cache --no-scripts \
+ nginx \
+ bash \
+ fontconfig \
+ ttf-dejavu \
+ && rm -rf /var/cache/apk/* \
+ && mkdir -p /run/nginx /var/log/nginx /var/tmp/nginx /etc/nginx/conf.d
+
+# 澶嶅埗椤圭洰鑷甫鐨勪腑鏂囧瓧浣�
+COPY main/manager-web/public/generator/static/fonts/*.ttf /usr/share/fonts/
+
+# 鏇存柊瀛椾綋缂撳瓨
+RUN fc-cache -f -v
+
+# 閰嶇疆Nginx
+COPY docs/docker/nginx.conf /etc/nginx/nginx.conf
+
+# 澶嶅埗鍓嶇鏋勫缓浜х墿
+COPY --from=web-builder /app/dist /usr/share/nginx/html
+
+# 澶嶅埗Java鍚庣JAR鍖�
+COPY --from=api-builder /app/target/xiaozhi-esp32-api.jar /app/xiaozhi-esp32-api.jar
+
+# 鏆撮湶绔彛
+EXPOSE 8002
+
+# 鍚姩鑴氭湰
+COPY docs/docker/start.sh /start.sh
+RUN chmod +x /start.sh
+CMD ["/start.sh"]
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/LICENSE b/xiaozhi-esp32-server/LICENSE
new file mode 100644
index 0000000..579a3d7
--- /dev/null
+++ b/xiaozhi-esp32-server/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2025 xinnan-tech
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/xiaozhi-esp32-server/README.md b/xiaozhi-esp32-server/README.md
new file mode 100644
index 0000000..df893d2
--- /dev/null
+++ b/xiaozhi-esp32-server/README.md
@@ -0,0 +1,362 @@
+[](https://github.com/xinnan-tech/xiaozhi-esp32-server)
+
+<h1 align="center">灏忔櫤鍚庣鏈嶅姟xiaozhi-esp32-server</h1>
+
+<p align="center">
+鏈」鐩熀浜庝汉鏈哄叡鐢熸櫤鑳界悊璁哄拰鎶�鏈爺鍙戞櫤鑳界粓绔蒋纭欢浣撶郴<br/>涓哄紑婧愭櫤鑳界‖浠堕」鐩�
+<a href="https://github.com/78/xiaozhi-esp32">xiaozhi-esp32</a>鎻愪緵鍚庣鏈嶅姟<br/>
+鏍规嵁<a href="https://ccnphfhqs21z.feishu.cn/wiki/M0XiwldO9iJwHikpXD5cEx71nKh">灏忔櫤閫氫俊鍗忚</a>浣跨敤Python銆丣ava銆乂ue瀹炵幇<br/>
+鏀寔MQTT+UDP鍗忚銆乄ebsocket鍗忚銆丮CP鎺ュ叆鐐广�佸0绾硅瘑鍒�佺煡璇嗗簱
+</p>
+
+<p align="center">
+<a href="./docs/FAQ.md">甯歌闂</a>
+路 <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/issues">鍙嶉闂</a>
+路 <a href="./README.md#%E9%83%A8%E7%BD%B2%E6%96%87%E6%A1%A3">閮ㄧ讲鏂囨。</a>
+路 <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/releases">鏇存柊鏃ュ織</a>
+</p>
+
+<p align="center">
+ <a href="./README.md"><img alt="绠�浣撲腑鏂囩増鑷堪鏂囦欢" src="https://img.shields.io/badge/绠�浣撲腑鏂�-DBEDFA"></a>
+ <a href="./docs/readme/README_en.md"><img alt="README in English" src="https://img.shields.io/badge/English-DFE0E5"></a>
+ <a href="./docs/readme/README_vi.md"><img alt="Ti岷縩g Vi峄噒" src="https://img.shields.io/badge/Ti岷縩g Vi峄噒-DFE0E5"></a>
+ <a href="./docs/readme/README_de.md"><img alt="Deutsch" src="https://img.shields.io/badge/Deutsch-DFE0E5"></a>
+ <a href="./docs/readme/README_pt_BR.md"><img alt="Portugu锚s (Brasil)" src="https://img.shields.io/badge/Portugu锚s (Brasil)-DFE0E5"></a>
+ <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/releases">
+ <img alt="GitHub Contributors" src="https://img.shields.io/github/v/release/xinnan-tech/xiaozhi-esp32-server?logo=docker" />
+ </a>
+ <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/blob/main/LICENSE">
+ <img alt="GitHub pull requests" src="https://img.shields.io/badge/license-MIT-white?labelColor=black" />
+ </a>
+ <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server">
+ <img alt="stars" src="https://img.shields.io/github/stars/xinnan-tech/xiaozhi-esp32-server?color=ffcb47&labelColor=black" />
+ </a>
+</p>
+
+<p align="center">
+Spearheaded by Professor Siyuan Liu's Team (South China University of Technology)
+</br>
+鍒樻�濇簮鏁欐巿鍥㈤槦涓诲鐮斿彂锛堝崕鍗楃悊宸ュぇ瀛︼級
+</br>
+<img src="./docs/images/hnlg.jpg" alt="鍗庡崡鐞嗗伐澶у" width="50%">
+</p>
+
+---
+
+## 閫傜敤浜虹兢 馃懃
+
+鏈」鐩渶瑕侀厤鍚� ESP32 纭欢璁惧浣跨敤銆傚鏋滄偍宸茬粡璐拱浜� ESP32 鐩稿叧纭欢锛屼笖鎴愬姛瀵规帴杩囪櫨鍝ラ儴缃茬殑鍚庣鏈嶅姟锛屽苟甯屾湜鐙珛鎼缓鑷繁鐨�
+`xiaozhi-esp32` 鍚庣鏈嶅姟锛岄偅涔堟湰椤圭洰闈炲父閫傚悎鎮ㄣ��
+
+鎯崇湅浣跨敤鏁堟灉锛熻鐚涙埑瑙嗛 馃帴
+
+<table>
+ <tr>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1FMFyejExX" target="_blank">
+ <picture>
+ <img alt="鍝嶅簲閫熷害鎰熷彈" src="docs/images/demo9.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1vchQzaEse" target="_blank">
+ <picture>
+ <img alt="閫熷害浼樺寲绉樿瘈" src="docs/images/demo6.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1WEcxzFEAT" target="_blank">
+ <picture>
+ <img alt="灏忔櫤鏁板瓧浜� 鏀寔璇煶鍞ら啋" src="docs/images/demo8.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1CKVz6UEuB" target="_blank">
+ <picture>
+ <img alt="璁惧鍛煎彨璁惧锛屾墦鐢佃瘽" src="docs/images/demo0.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1C1tCzUEZh" target="_blank">
+ <picture>
+ <img alt="澶嶆潅鍖荤枟鍦烘櫙" src="docs/images/demo1.png" /></picture>
+ </a>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1VC96Y5EMH" target="_blank">
+ <picture>
+ <img alt="鎾斁闊充箰鏌ヨ澶╂皵鎾姤鏂伴椈" src="docs/images/demo7.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV12J7WzBEaH" target="_blank">
+ <picture>
+ <img alt="瀹炴椂鎵撴柇" src="docs/images/demo10.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1Co76z7EvK" target="_blank">
+ <picture>
+ <img alt="鎷嶇収璇嗙墿鍝�" src="docs/images/demo12.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1pNXWYGEx1" target="_blank">
+ <picture>
+ <img alt="鎺у埗瀹剁數寮�鍏�" src="docs/images/demo5.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1TJ7WzzEo6" target="_blank">
+ <picture>
+ <img alt="澶氭寚浠や换鍔�" src="docs/images/demo11.png" /></picture>
+ </a>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1ZQKUzYExM" target="_blank">
+ <picture>
+ <img alt="MCP鎺ュ叆鐐�" src="docs/images/demo13.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1zUW5zJEkq" target="_blank">
+ <picture>
+ <img alt="MQTT鎸囦护涓嬪彂" src="docs/images/demo4.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1Exu3zqEDe" target="_blank">
+ <picture>
+ <img alt="澹扮汗璇嗗埆" src="docs/images/demo14.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1CDKWemEU6" target="_blank">
+ <picture>
+ <img alt="鑷畾涔夐煶鑹�" src="docs/images/demo2.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV12yA2egEaC" target="_blank">
+ <picture>
+ <img alt="浣跨敤绮よ浜ゆ祦" src="docs/images/demo3.png" /></picture>
+ </a>
+ </td>
+ </tr>
+</table>
+
+---
+
+## 璀﹀憡 鈿狅笍
+
+1銆佹湰椤圭洰涓哄紑婧愯蒋浠讹紝鏈蒋浠朵笌瀵规帴鐨勪换浣曠涓夋柟API鏈嶅姟鍟嗭紙鍖呮嫭浣嗕笉闄愪簬璇煶璇嗗埆銆佸ぇ妯″瀷銆佽闊冲悎鎴愮瓑骞冲彴锛夊潎涓嶅瓨鍦ㄥ晢涓氬悎浣滃叧绯伙紝涓嶄负鍏舵湇鍔¤川閲忓強璧勯噾瀹夊叏鎻愪緵浠讳綍褰㈠紡鐨勬媴淇濄��
+寤鸿浣跨敤鑰呬紭鍏堥�夋嫨鎸佹湁鐩稿叧涓氬姟鐗岀収鐨勬湇鍔″晢锛屽苟浠旂粏闃呰鍏舵湇鍔″崗璁強闅愮鏀跨瓥銆傛湰杞欢涓嶆墭绠′换浣曡处鎴峰瘑閽ャ�佷笉鍙備笌璧勯噾娴佽浆銆佷笉鎵挎媴鍏呭�艰祫閲戞崯澶遍闄┿��
+
+2銆佹湰椤圭洰鍔熻兘鏈畬鍠勶紝涓旀湭閫氳繃缃戠粶瀹夊叏娴嬭瘎锛岃鍕垮湪鐢熶骇鐜涓娇鐢ㄣ�� 濡傛灉鎮ㄥ湪鍏綉鐜涓儴缃插涔犳湰椤圭洰锛岃鍔″繀鍋氬ソ蹇呰鐨勯槻鎶ゃ��
+
+---
+
+## 閮ㄧ讲鏂囨。
+
+
+
+鏈」鐩彁渚涗袱绉嶉儴缃叉柟寮忥紝璇锋牴鎹偍鐨勫叿浣撻渶姹傞�夋嫨锛�
+
+#### 馃殌 閮ㄧ讲鏂瑰紡閫夋嫨
+| 閮ㄧ讲鏂瑰紡 | 鐗圭偣 | 閫傜敤鍦烘櫙 | 閮ㄧ讲鏂囨。 | 閰嶇疆瑕佹眰 | 瑙嗛鏁欑▼ |
+|---------|------|---------|---------|---------|---------|
+| **鏈�绠�鍖栧畨瑁�** | 鏅鸿兘瀵硅瘽銆佸崟鏅鸿兘浣撶鐞� | 浣庨厤缃幆澧冿紝鏁版嵁瀛樺偍鍦ㄩ厤缃枃浠讹紝鏃犻渶鏁版嵁搴� | [鈶燚ocker鐗圿(./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E5%8F%AA%E8%BF%90%E8%A1%8Cserver) / [鈶℃簮鐮侀儴缃瞉(./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E5%8F%AA%E8%BF%90%E8%A1%8Cserver)| 濡傛灉浣跨敤`FunASR`瑕�2鏍�4G锛屽鏋滃叏API锛岃2鏍�2G | - |
+| **鍏ㄦā鍧楀畨瑁�** | 鏅鸿兘瀵硅瘽銆佸鐢ㄦ埛绠$悊銆佸鏅鸿兘浣撶鐞嗐�佹櫤鎺у彴鐣岄潰鎿嶄綔 | 瀹屾暣鍔熻兘浣撻獙锛屾暟鎹瓨鍌ㄥ湪鏁版嵁搴� |[鈶燚ocker鐗圿(./docs/Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [鈶℃簮鐮侀儴缃瞉(./docs/Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [鈶㈡簮鐮侀儴缃茶嚜鍔ㄦ洿鏂版暀绋媇(./docs/dev-ops-integration.md) | 濡傛灉浣跨敤`FunASR`瑕�4鏍�8G锛屽鏋滃叏API锛岃2鏍�4G| [鏈湴婧愮爜鍚姩瑙嗛鏁欑▼](https://www.bilibili.com/video/BV1wBJhz4Ewe) |
+
+甯歌闂鍙婄浉鍏虫暀绋嬶紝鍙弬鑰僛杩欎釜閾炬帴](./docs/FAQ.md)
+
+> 馃挕 鎻愮ず锛氫互涓嬫槸鎸夋渶鏂颁唬鐮侀儴缃插悗鐨勬祴璇曞钩鍙帮紝鏈夐渶瑕佸彲鐑у綍娴嬭瘯锛屽苟鍙戜负6涓紝姣忓ぉ浼氭竻绌烘暟鎹紝
+
+```
+鏅烘帶鍙板湴鍧�: https://2662r3426b.vicp.fun
+鏅烘帶鍙�(h5鐗�): https://2662r3426b.vicp.fun/h5/index.html
+
+鏈嶅姟娴嬭瘯宸ュ叿锛� https://2662r3426b.vicp.fun/test/
+OTA鎺ュ彛鍦板潃: https://2662r3426b.vicp.fun/xiaozhi/ota/
+Websocket鎺ュ彛鍦板潃: wss://2662r3426b.vicp.fun/xiaozhi/v1/
+```
+
+#### 馃毄 閰嶇疆璇存槑鍜屾帹鑽�
+> [!Note]
+> 鏈」鐩彁渚涗袱绉嶉厤缃柟妗堬細
+>
+> 1. `鍏ラ棬鍏ㄥ厤璐筦閰嶇疆锛氶�傚悎涓汉瀹跺涵浣跨敤锛屾墍鏈夌粍浠跺潎閲囩敤鍏嶈垂鏂规锛屾棤闇�棰濆浠樿垂銆�
+>
+> 2. `娴佸紡閰嶇疆`锛氶�傚悎婕旂ず銆佸煿璁�佽秴杩�2涓苟鍙戠瓑鍦烘櫙锛岄噰鐢ㄦ祦寮忓鐞嗘妧鏈紝鍝嶅簲閫熷害鏇村揩锛屼綋楠屾洿浣炽��
+>
+> 鑷猔0.5.2`鐗堟湰璧凤紝椤圭洰鏀寔娴佸紡閰嶇疆锛岀浉姣旀棭鏈熺増鏈紝鍝嶅簲閫熷害鎻愬崌绾2.5绉抈锛屾樉钁楁敼鍠勭敤鎴蜂綋楠屻��
+
+| 妯″潡鍚嶇О | 鍏ラ棬鍏ㄥ厤璐硅缃� | 娴佸紡閰嶇疆 |
+|:---:|:---:|:---:|
+| ASR(璇煶璇嗗埆) | FunASR(鏈湴) | 馃憤XunfeiStreamASR(璁娴佸紡) |
+| LLM(澶фā鍨�) | glm-4-flash(鏅鸿氨) | 馃憤qwen-flash(闃块噷鐧剧偧) |
+| VLLM(瑙嗚澶фā鍨�) | glm-4v-flash(鏅鸿氨) | 馃憤qwen3.5-flash(闃块噷鐧剧偧) |
+| TTS(璇煶鍚堟垚) | EdgeTTS(寰蒋) | 馃憤HuoshanDoubleStreamTTS(鐏北娴佸紡) |
+| Intent(鎰忓浘璇嗗埆) | function_call(鍑芥暟璋冪敤) | function_call(鍑芥暟璋冪敤) |
+| Memory(璁板繂鍔熻兘) | mem_local_short(鏈湴鐭湡璁板繂锛� | mem_local_short锛堟湰鍦扮煭鏈熻蹇嗭級 |
+
+濡傛灉鎮ㄥ叧蹇冨悇缁勪欢鐨勮�楁椂锛岃鏌ラ槄[灏忔櫤鍚勭粍浠舵�ц兘娴嬭瘯鎶ュ憡](https://github.com/xinnan-tech/xiaozhi-performance-research)锛屽彲鎸夋姤鍛婁腑鐨勬祴璇曟柟娉曞湪鎮ㄧ殑鐜涓疄闄呮祴璇曘��
+
+#### 馃敡 娴嬭瘯宸ュ叿
+鏈」鐩彁渚涗互涓嬫祴璇曞伐鍏凤紝甯姪鎮ㄩ獙璇佺郴缁熷拰閫夋嫨鍚堥�傜殑妯″瀷锛�
+
+| 宸ュ叿鍚嶇О | 浣嶇疆 | 浣跨敤鏂规硶 | 鍔熻兘璇存槑 |
+|:---:|:---|:---:|:---:|
+| 闊抽浜や簰娴嬭瘯宸ュ叿 | main銆媎igital-human銆媔ndex.html | 鍦� `main/digital-human` 鎵ц `python start.py` 鍚庤闂� `http://127.0.0.1:8006/index.html` | 娴嬭瘯闊抽鎾斁鍜屾帴鏀跺姛鑳斤紝楠岃瘉Python绔煶棰戝鐞嗘槸鍚︽甯� |
+| 妯″瀷鍝嶅簲娴嬭瘯宸ュ叿 | main銆媥iaozhi-server銆媝erformance_tester.py | 鎵ц `python performance_tester.py` | 娴嬭瘯ASR(璇煶璇嗗埆)銆丩LM(澶фā鍨�)銆乂LLM(瑙嗚妯″瀷)銆乀TS(璇煶鍚堟垚)涓変釜鏍稿績妯″潡鐨勫搷搴旈�熷害 |
+
+> 馃挕 鎻愮ず锛氭祴璇曟ā鍨嬮�熷害鏃讹紝鍙細娴嬭瘯閰嶇疆浜嗗瘑閽ョ殑妯″瀷銆�
+
+---
+## 鍔熻兘娓呭崟 鉁�
+### 宸插疄鐜� 鉁�
+
+| 鍔熻兘妯″潡 | 鎻忚堪 |
+|:---:|:---|
+| 鏍稿績鏋舵瀯 | 鍩轰簬[MQTT+UDP缃戝叧](https://github.com/xinnan-tech/xiaozhi-esp32-server/blob/main/docs/mqtt-gateway-integration.md)銆乄ebSocket銆丠TTP鏈嶅姟鍣紝鎻愪緵瀹屾暣鐨勬帶鍒跺彴绠$悊鍜岃璇佺郴缁� |
+| 璇煶浜や簰 | 鏀寔娴佸紡ASR(璇煶璇嗗埆)銆佹祦寮廡TS(璇煶鍚堟垚)銆乂AD(璇煶娲诲姩妫�娴�)锛屾敮鎸佸璇█璇嗗埆鍜岃闊冲鐞� |
+| 澹扮汗璇嗗埆 | 鏀寔澶氱敤鎴峰0绾规敞鍐屻�佺鐞嗗拰璇嗗埆锛屼笌ASR骞惰澶勭悊锛屽疄鏃惰瘑鍒璇濅汉韬唤骞朵紶閫掔粰LLM杩涜涓�у寲鍥炲簲 |
+| 鏅鸿兘瀵硅瘽 | 鏀寔澶氱LLM(澶ц瑷�妯″瀷)锛屽疄鐜版櫤鑳藉璇� |
+| 瑙嗚鎰熺煡 | 鏀寔澶氱VLLM(瑙嗚澶фā鍨�)锛屽疄鐜板妯℃�佷氦浜� |
+| 鎰忓浘璇嗗埆 | 鏀寔澶栨寕鐨勫ぇ妯″瀷鎰忓浘璇嗗埆銆佸ぇ妯″瀷鑷富鍑芥暟璋冪敤锛屾彁渚涙彃浠跺寲鎰忓浘澶勭悊鏈哄埗 |
+| 璁板繂绯荤粺 | 鏀寔鏈湴鐭湡璁板繂銆乵em0ai鎺ュ彛璁板繂銆丳owerMem鏅鸿兘璁板繂锛屽叿澶囪蹇嗘�荤粨鍔熻兘 |
+| 鐭ヨ瘑搴� | 鏀寔RAGFlow鐭ヨ瘑搴擄紝璁╁ぇ妯″瀷鍒ゆ柇闇�瑕佽皟搴︾煡璇嗗簱鍚庡啀鍥炵瓟 |
+| 宸ュ叿璋冪敤 | 鏀寔瀹㈡埛绔疘OT鍗忚銆佸鎴稭CP鍗忚銆佹湇鍔$MCP鍗忚銆丮CP鎺ュ叆鐐瑰崗璁�佽嚜瀹氫箟宸ュ叿鍑芥暟 |
+| 鎸囦护涓嬪彂 | 渚濇墭MQTT鍗忚锛屾敮鎸佷粠鏅烘帶鍙板皢MCP鎸囦护涓嬪彂鍒癊SP32璁惧 |
+| 绠$悊鍚庡彴 | 鎻愪緵Web绠$悊鐣岄潰锛屾敮鎸佺敤鎴风鐞嗐�佺郴缁熼厤缃拰璁惧绠$悊锛涚晫闈㈡敮鎸佷腑鏂囩畝浣撱�佷腑鏂囩箒浣撱�佽嫳鏂囨樉绀� |
+| 娴嬭瘯宸ュ叿 | 鎻愪緵鎬ц兘娴嬭瘯宸ュ叿銆佽瑙夋ā鍨嬫祴璇曞伐鍏峰拰闊抽浜や簰娴嬭瘯宸ュ叿 |
+| 閮ㄧ讲鏀寔 | 鏀寔Docker閮ㄧ讲鍜屾湰鍦伴儴缃诧紝鎻愪緵瀹屾暣鐨勯厤缃枃浠剁鐞� |
+| 鎻掍欢绯荤粺 | 鏀寔鍔熻兘鎻掍欢鎵╁睍銆佽嚜瀹氫箟鎻掍欢寮�鍙戝拰鎻掍欢鐑姞杞� |
+
+### 姝e湪寮�鍙� 馃毀
+
+鎯充簡瑙e叿浣撳紑鍙戣鍒掕繘搴︼紝[璇风偣鍑昏繖閲宂(https://github.com/users/xinnan-tech/projects/3)銆傚父瑙侀棶棰樺強鐩稿叧鏁欑▼锛屽彲鍙傝�僛杩欎釜閾炬帴](./docs/FAQ.md)
+
+濡傛灉浣犳槸涓�鍚嶈蒋浠跺紑鍙戣�咃紝杩欓噷鏈変竴浠絒銆婅嚧寮�鍙戣�呯殑鍏紑淇°�媇(docs/contributor_open_letter.md)锛屾杩庡姞鍏ワ紒
+
+---
+
+## 浜у搧鐢熸�� 馃懍
+灏忔櫤鏄竴涓敓鎬侊紝褰撲綘浣跨敤杩欎釜浜у搧鏃讹紝涔熷彲浠ョ湅鐪嬪叾浠栧湪杩欎釜鐢熸�佸湀鐨刐浼樼椤圭洰](https://github.com/78/xiaozhi-esp32/blob/main/README_zh.md#%E7%9B%B8%E5%85%B3%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE)
+
+---
+
+## 鏈」鐩敮鎸佺殑骞冲彴/缁勪欢鍒楄〃 馃搵
+### LLM 璇█妯″瀷
+
+| 浣跨敤鏂瑰紡 | 鏀寔骞冲彴 | 鍏嶈垂骞冲彴 |
+|:---:|:---:|:---:|
+| openai 鎺ュ彛璋冪敤 | 闃块噷鐧剧偧銆佺伀灞卞紩鎿庛�丏eepSeek銆佹櫤璋便�丟emini銆佺澶ц椋� | 鏅鸿氨銆丟emini |
+| ollama 鎺ュ彛璋冪敤 | Ollama | - |
+| dify 鎺ュ彛璋冪敤 | Dify | - |
+| fastgpt 鎺ュ彛璋冪敤 | Fastgpt | - |
+| coze 鎺ュ彛璋冪敤 | Coze | - |
+| xinference 鎺ュ彛璋冪敤 | Xinference | - |
+| homeassistant 鎺ュ彛璋冪敤 | HomeAssistant | - |
+
+瀹為檯涓婏紝浠讳綍鏀寔 openai 鎺ュ彛璋冪敤鐨� LLM 鍧囧彲鎺ュ叆浣跨敤銆�
+
+---
+
+### VLLM 瑙嗚妯″瀷
+
+| 浣跨敤鏂瑰紡 | 鏀寔骞冲彴 | 鍏嶈垂骞冲彴 |
+|:---:|:---:|:---:|
+| openai 鎺ュ彛璋冪敤 | 闃块噷鐧剧偧銆佹櫤璋盋hatGLMVLLM | 鏅鸿氨ChatGLMVLLM |
+
+瀹為檯涓婏紝浠讳綍鏀寔 openai 鎺ュ彛璋冪敤鐨� VLLM 鍧囧彲鎺ュ叆浣跨敤銆�
+
+---
+
+### TTS 璇煶鍚堟垚
+
+| 浣跨敤鏂瑰紡 | 鏀寔骞冲彴 | 鍏嶈垂骞冲彴 |
+|:---:|:---:|:---:|
+| 鎺ュ彛璋冪敤 | EdgeTTS銆佺澶ц椋炪�佺伀灞卞紩鎿庛�佽吘璁簯銆侀樋閲屼簯鍙婄櫨鐐笺�丆osyVoiceSiliconflow銆乀TS302AI銆丆ozeCnTTS銆丟izwitsTTS銆丄CGNTTS銆丱penAITTS銆佺伒鐘�娴佸紡TTS銆丮inimaxTTS | 鐏电妧娴佸紡TTS銆丒dgeTTS銆丆osyVoiceSiliconflow(閮ㄥ垎) |
+| 鏈湴鏈嶅姟 | FishSpeech銆丟PT_SOVITS_V2銆丟PT_SOVITS_V3銆両ndex-TTS銆丳addleSpeech | Index-TTS銆丳addleSpeech銆丗ishSpeech銆丟PT_SOVITS_V2銆丟PT_SOVITS_V3 |
+
+---
+
+### VAD 璇煶娲诲姩妫�娴�
+
+| 绫诲瀷 | 骞冲彴鍚嶇О | 浣跨敤鏂瑰紡 | 鏀惰垂妯″紡 | 澶囨敞 |
+|:---:|:---------:|:----:|:----:|:--:|
+| VAD | SileroVAD | 鏈湴浣跨敤 | 鍏嶈垂 | |
+
+---
+
+### ASR 璇煶璇嗗埆
+
+| 浣跨敤鏂瑰紡 | 鏀寔骞冲彴 | 鍏嶈垂骞冲彴 |
+|:---:|:---:|:---:|
+| 鏈湴浣跨敤 | FunASR銆丼herpaASR | FunASR銆丼herpaASR |
+| 鎺ュ彛璋冪敤 | FunASRServer銆佺伀灞卞紩鎿庛�佺澶ц椋炪�佽吘璁簯銆侀樋閲屼簯銆佺櫨搴︿簯銆丱penAI ASR | FunASRServer |
+
+---
+
+### Voiceprint 澹扮汗璇嗗埆
+
+| 浣跨敤鏂瑰紡 | 鏀寔骞冲彴 | 鍏嶈垂骞冲彴 |
+|:---:|:---:|:---:|
+| 鏈湴浣跨敤 | 3D-Speaker | 3D-Speaker |
+
+---
+
+### Memory 璁板繂瀛樺偍
+
+| 绫诲瀷 | 骞冲彴鍚嶇О | 浣跨敤鏂瑰紡 | 鏀惰垂妯″紡 | 澶囨敞 |
+|:------:|:---------------:|:----:|:---------:|:--:|
+| Memory | mem0ai | 鎺ュ彛璋冪敤 | 1000娆�/鏈堥搴� | |
+| Memory | [powermem](./docs/powermem-integration.md) | 鏈湴鎬荤粨 | 鍙栧喅浜嶭LM鍜孌B | OceanBase寮�婧愶紝鏀寔鏅鸿兘妫�绱� |
+| Memory | mem_local_short | 鏈湴鎬荤粨 | 鍏嶈垂 | |
+| Memory | nomem | 鏃犺蹇嗘ā寮� | 鍏嶈垂 | |
+
+---
+
+### Intent 鎰忓浘璇嗗埆
+
+| 绫诲瀷 | 骞冲彴鍚嶇О | 浣跨敤鏂瑰紡 | 鏀惰垂妯″紡 | 澶囨敞 |
+|:------:|:-------------:|:----:|:-------:|:---------------------:|
+| Intent | intent_llm | 鎺ュ彛璋冪敤 | 鏍规嵁LLM鏀惰垂 | 閫氳繃澶фā鍨嬭瘑鍒剰鍥撅紝閫氱敤鎬у己 |
+| Intent | function_call | 鎺ュ彛璋冪敤 | 鏍规嵁LLM鏀惰垂 | 閫氳繃澶фā鍨嬪嚱鏁拌皟鐢ㄥ畬鎴愭剰鍥撅紝閫熷害蹇紝鏁堟灉濂� |
+| Intent | nointent | 鏃犳剰鍥炬ā寮� | 鍏嶈垂 | 涓嶈繘琛屾剰鍥捐瘑鍒紝鐩存帴杩斿洖瀵硅瘽缁撴灉 |
+
+---
+
+### Rag 妫�绱㈠寮虹敓鎴�
+
+| 绫诲瀷 | 骞冲彴鍚嶇О | 浣跨敤鏂瑰紡 | 鏀惰垂妯″紡 | 澶囨敞 |
+|:------:|:-------------:|:----:|:-------:|:---------------------:|
+| Rag | ragflow | 鎺ュ彛璋冪敤 | 鏍规嵁鍒囩墖銆佸垎璇嶆秷鑰楃殑token鏀惰垂 | 鍊熷姪RagFlow鐨勬绱㈠寮虹敓鎴愬姛鑳斤紝鎻愪緵鏇村噯纭殑瀵硅瘽鍥炲 |
+
+---
+
+## 楦h阿 馃檹
+
+| Logo | 椤圭洰/鍏徃 | 璇存槑 |
+|:---:|:---:|:---|
+| <img src="./docs/images/logo_bailing.png" width="160"> | [鐧捐亞璇煶瀵硅瘽鏈哄櫒浜篯(https://github.com/wwbin2017/bailing) | 鏈」鐩彈[鐧捐亞璇煶瀵硅瘽鏈哄櫒浜篯(https://github.com/wwbin2017/bailing)鍚彂锛屽苟鍦ㄥ叾鍩虹涓婂疄鐜� |
+| <img src="./docs/images/logo_tenclass.png" width="160"> | [鍗佹柟铻嶆捣](https://www.tenclass.com/) | 鎰熻阿[鍗佹柟铻嶆捣](https://www.tenclass.com/)涓哄皬鏅虹敓鎬佸埗瀹氫簡鏍囧噯鐨勯�氳鍗忚銆佸璁惧鍏煎鎬ф柟妗堝強楂樺苟鍙戝満鏅疄璺电ず鑼冿紱涓烘湰椤圭洰鎻愪緵浜嗗叏閾捐矾鎶�鏈枃妗f敮鎸� |
+| <img src="./docs/images/logo_xuanfeng.png" width="160"> | [鐜勫嚖绉戞妧](https://github.com/Eric0308) | 鎰熻阿[鐜勫嚖绉戞妧](https://github.com/Eric0308)璐$尞鍑芥暟璋冪敤妗嗘灦銆丮CP閫氫俊鍗忚鍙婃彃浠跺寲璋冪敤鏈哄埗鐨勫疄鐜颁唬鐮侊紝閫氳繃鏍囧噯鍖栫殑鎸囦护璋冨害浣撶郴涓庡姩鎬佹墿灞曡兘鍔涳紝鏄捐憲鎻愬崌浜嗗墠绔澶�(IoT)鐨勪氦浜掓晥鐜囧拰鍔熻兘寤跺睍鎬� |
+| <img src="./docs/images/logo_junsen.png" width="160"> | [huangjunsen](https://github.com/huangjunsen0406) | 鎰熻阿[huangjunsen](https://github.com/huangjunsen0406) 璐$尞`鏅烘帶鍙扮Щ鍔ㄧ`妯″潡锛屽疄鐜颁簡璺ㄥ钩鍙扮Щ鍔ㄨ澶囩殑楂樻晥鎺у埗涓庡疄鏃朵氦浜掞紝澶у箙鎻愬崌浜嗙郴缁熷湪绉诲姩鍦烘櫙涓嬬殑鎿嶄綔渚挎嵎鎬у拰绠$悊鏁堢巼 |
+| <img src="./docs/images/logo_huiyuan.png" width="160"> | [姹囪繙璁捐](http://ui.kwd988.net/) | 鎰熻阿[姹囪繙璁捐](http://ui.kwd988.net/)涓烘湰椤圭洰鎻愪緵涓撲笟瑙嗚瑙e喅鏂规锛岀敤鍏舵湇鍔¤秴鍗冨浼佷笟鐨勮璁″疄鎴樼粡楠岋紝璧嬭兘鏈」鐩骇鍝佺敤鎴蜂綋楠� |
+| <img src="./docs/images/logo_qinren.png" width="160"> | [瑗垮畨鍕や汉淇℃伅绉戞妧](https://www.029app.com/) | 鎰熻阿[瑗垮畨鍕や汉淇℃伅绉戞妧](https://www.029app.com/)娣卞寲鏈」鐩瑙変綋绯伙紝纭繚鏁翠綋璁捐椋庢牸鍦ㄥ鍦烘櫙搴旂敤涓殑涓�鑷存�у拰鎵╁睍鎬� |
+| <img src="./docs/images/logo_contributors.png" width="160"> | [浠g爜璐$尞鑰匽(https://github.com/xinnan-tech/xiaozhi-esp32-server/graphs/contributors) | 鎰熻阿[鎵�鏈変唬鐮佽础鐚�匽(https://github.com/xinnan-tech/xiaozhi-esp32-server/graphs/contributors)璐$尞鑰咃紝浣犱滑鐨勪粯鍑鸿椤圭洰鏇村姞鍋ュ.鍜屽己澶с�� |
+
+
+<a href="https://www.star-history.com/?repos=xinnan-tech%2Fxiaozhi-esp32-server&type=date&legend=top-left">
+ <picture>
+ <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=xinnan-tech/xiaozhi-esp32-server&type=date&theme=dark&legend=top-left&sealed_token=cQovHKgZjqEnQ-svfYfN392irNvGuq-6pyv4cA8nd2jQEhQLz1ETV4YHTVk2UZyLMFbCQuZA7jduRh3YbeK5WPYaRLrfmIimGQa3lram652jJL9oQk-UuSZA5H6L4dPIhZc8KCc-Ur_UAUNbly7TePpnTR2otGknBLCOjOliD4fk1st6z7tPEDVjSRx5" />
+ <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=xinnan-tech/xiaozhi-esp32-server&type=date&legend=top-left&sealed_token=cQovHKgZjqEnQ-svfYfN392irNvGuq-6pyv4cA8nd2jQEhQLz1ETV4YHTVk2UZyLMFbCQuZA7jduRh3YbeK5WPYaRLrfmIimGQa3lram652jJL9oQk-UuSZA5H6L4dPIhZc8KCc-Ur_UAUNbly7TePpnTR2otGknBLCOjOliD4fk1st6z7tPEDVjSRx5" />
+ <img alt="Star History Chart" src="https://api.star-history.com/chart?repos=xinnan-tech/xiaozhi-esp32-server&type=date&legend=top-left&sealed_token=cQovHKgZjqEnQ-svfYfN392irNvGuq-6pyv4cA8nd2jQEhQLz1ETV4YHTVk2UZyLMFbCQuZA7jduRh3YbeK5WPYaRLrfmIimGQa3lram652jJL9oQk-UuSZA5H6L4dPIhZc8KCc-Ur_UAUNbly7TePpnTR2otGknBLCOjOliD4fk1st6z7tPEDVjSRx5" />
+ </picture>
+</a>
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/docker-setup.sh b/xiaozhi-esp32-server/docker-setup.sh
new file mode 100644
index 0000000..8b2f127
--- /dev/null
+++ b/xiaozhi-esp32-server/docker-setup.sh
@@ -0,0 +1,413 @@
+#!/bin/sh
+# 鑴氭湰浣滆�匑VanillaNahida
+# 鏈枃浠舵槸鐢ㄤ簬涓�閿嚜鍔ㄤ笅杞芥湰椤圭洰鎵�闇�鏂囦欢锛岃嚜鍔ㄥ垱寤哄ソ鐩綍
+# 鏆備笖鍙敮鎸乆86鐗堟湰鐨刄buntu绯荤粺锛屽叾浠栫郴缁熸湭娴嬭瘯
+
+# 瀹氫箟涓柇澶勭悊鍑芥暟
+handle_interrupt() {
+ echo ""
+ echo "瀹夎宸茶鐢ㄦ埛涓柇(Ctrl+C鎴朎sc)"
+ echo "濡傞渶閲嶆柊瀹夎锛岃鍐嶆杩愯鑴氭湰"
+ exit 1
+}
+
+# 璁剧疆淇″彿鎹曡幏锛屽鐞咰trl+C
+trap handle_interrupt SIGINT
+
+# 澶勭悊Esc閿�
+# 淇濆瓨缁堢璁剧疆
+old_stty_settings=$(stty -g)
+# 璁剧疆缁堢绔嬪嵆鍝嶅簲锛屼笉鍥炴樉
+stty -icanon -echo min 1 time 0
+
+# 鍚庡彴杩涚▼妫�娴婨sc閿�
+(while true; do
+ read -r key
+ if [[ $key == $'\e' ]]; then
+ # 妫�娴嬪埌Esc閿紝瑙﹀彂涓柇澶勭悊
+ kill -SIGINT $$
+ break
+ fi
+done) &
+
+# 鑴氭湰缁撴潫鏃舵仮澶嶇粓绔缃�
+trap 'stty "$old_stty_settings"' EXIT
+
+
+# 鎵撳嵃褰╄壊瀛楃鐢�
+echo -e "\e[1;32m" # 璁剧疆棰滆壊涓轰寒缁胯壊
+cat << "EOF"
+鑴氭湰浣滆�咃細@Bilibili 棣欒崏鍛崇殑绾宠タ濡插柕
+ __ __ _ _ _ _ _ _ _ _
+ \ \ / / (_)| || | | \ | | | | (_) | |
+ \ \ / /__ _ _ __ _ | || | __ _ | \| | __ _ | |__ _ __| | __ _
+ \ \/ // _` || '_ \ | || || | / _` | | . ` | / _` || '_ \ | | / _` | / _` |
+ \ /| (_| || | | || || || || (_| | | |\ || (_| || | | || || (_| || (_| |
+ \/ \__,_||_| |_||_||_||_| \__,_| |_| \_| \__,_||_| |_||_| \__,_| \__,_|
+EOF
+echo -e "\e[0m" # 閲嶇疆棰滆壊
+echo -e "\e[1;36m 灏忔櫤鏈嶅姟绔叏閲忛儴缃蹭竴閿畨瑁呰剼鏈� Ver 0.2 2025骞�8鏈�20鏃ユ洿鏂� \e[0m\n"
+sleep 1
+
+
+
+# 妫�鏌ュ苟瀹夎whiptail
+check_whiptail() {
+ if ! command -v whiptail &> /dev/null; then
+ echo "姝e湪瀹夎whiptail..."
+ apt update
+ apt install -y whiptail
+ fi
+}
+
+check_whiptail
+
+# 鍒涘缓纭瀵硅瘽妗�
+whiptail --title "瀹夎纭" --yesno "鍗冲皢瀹夎灏忔櫤鏈嶅姟绔紝鏄惁缁х画锛�" \
+ --yes-button "缁х画" --no-button "閫�鍑�" 10 50
+
+# 鏍规嵁鐢ㄦ埛閫夋嫨鎵ц鎿嶄綔
+case $? in
+ 0)
+ ;;
+ 1)
+ exit 1
+ ;;
+esac
+
+# 妫�鏌oot鏉冮檺
+if [ $EUID -ne 0 ]; then
+ whiptail --title "鏉冮檺閿欒" --msgbox "璇蜂娇鐢╮oot鏉冮檺杩愯鏈剼鏈�" 10 50
+ exit 1
+fi
+
+# 妫�鏌ョ郴缁熺増鏈�
+if [ -f /etc/os-release ]; then
+ . /etc/os-release
+ if [ "$ID" != "debian" ] && [ "$ID" != "ubuntu" ]; then
+ whiptail --title "绯荤粺閿欒" --msgbox "璇ヨ剼鏈彧鏀寔Debian/Ubuntu绯荤粺鎵ц" 10 60
+ exit 1
+ fi
+else
+ whiptail --title "绯荤粺閿欒" --msgbox "鏃犳硶纭畾绯荤粺鐗堟湰锛岃鑴氭湰鍙敮鎸丏ebian/Ubuntu绯荤粺鎵ц" 10 60
+ exit 1
+fi
+
+# 涓嬭浇閰嶇疆鏂囦欢鍑芥暟
+check_and_download() {
+ local filepath=$1
+ local url=$2
+ if [ ! -f "$filepath" ]; then
+ if ! curl -fL --progress-bar "$url" -o "$filepath"; then
+ whiptail --title "閿欒" --msgbox "${filepath}鏂囦欢涓嬭浇澶辫触" 10 50
+ exit 1
+ fi
+ else
+ echo "${filepath}鏂囦欢宸插瓨鍦紝璺宠繃涓嬭浇"
+ fi
+}
+
+# 妫�鏌ユ槸鍚﹀凡瀹夎
+check_installed() {
+ # 妫�鏌ョ洰褰曟槸鍚﹀瓨鍦ㄤ笖闈炵┖
+ if [ -d "/opt/xiaozhi-server/" ] && [ "$(ls -A /opt/xiaozhi-server/)" ]; then
+ DIR_CHECK=1
+ else
+ DIR_CHECK=0
+ fi
+
+ # 妫�鏌ュ鍣ㄦ槸鍚﹀瓨鍦�
+ if docker inspect xiaozhi-esp32-server > /dev/null 2>&1; then
+ CONTAINER_CHECK=1
+ else
+ CONTAINER_CHECK=0
+ fi
+
+ # 涓ゆ妫�鏌ラ兘閫氳繃
+ if [ $DIR_CHECK -eq 1 ] && [ $CONTAINER_CHECK -eq 1 ]; then
+ return 0 # 宸插畨瑁�
+ else
+ return 1 # 鏈畨瑁�
+ fi
+}
+
+# 鏇存柊鐩稿叧
+if check_installed; then
+ if whiptail --title "宸插畨瑁呮娴�" --yesno "妫�娴嬪埌灏忔櫤鏈嶅姟绔凡瀹夎锛屾槸鍚﹁繘琛屽崌绾э紵" 10 60; then
+ # 鐢ㄦ埛閫夋嫨鍗囩骇锛屾墽琛屾竻鐞嗘搷浣�
+ echo "寮�濮嬪崌绾ф搷浣�..."
+
+ # 鍋滄骞剁Щ闄ゆ墍鏈塪ocker-compose鏈嶅姟
+ docker compose -f /opt/xiaozhi-server/docker-compose_all.yml down
+
+ # 鍋滄骞跺垹闄ょ壒瀹氬鍣紙鑰冭檻瀹瑰櫒鍙兘涓嶅瓨鍦ㄧ殑鎯呭喌锛�
+ containers=(
+ "xiaozhi-esp32-server"
+ "xiaozhi-esp32-server-web"
+ "xiaozhi-esp32-server-db"
+ "xiaozhi-esp32-server-redis"
+ )
+
+ for container in "${containers[@]}"; do
+ if docker ps -a --format '{{.Names}}' | grep -q "^${container}$"; then
+ docker stop "$container" >/dev/null 2>&1 && \
+ docker rm "$container" >/dev/null 2>&1 && \
+ echo "鎴愬姛绉婚櫎瀹瑰櫒: $container"
+ else
+ echo "瀹瑰櫒涓嶅瓨鍦紝璺宠繃: $container"
+ fi
+ done
+
+ # 鍒犻櫎鐗瑰畾闀滃儚锛堣�冭檻闀滃儚鍙兘涓嶅瓨鍦ㄧ殑鎯呭喌锛�
+ images=(
+ "ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:server_latest"
+ "ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:web_latest"
+ )
+
+ for image in "${images[@]}"; do
+ if docker images --format '{{.Repository}}:{{.Tag}}' | grep -q "^${image}$"; then
+ docker rmi "$image" >/dev/null 2>&1 && \
+ echo "鎴愬姛鍒犻櫎闀滃儚: $image"
+ else
+ echo "闀滃儚涓嶅瓨鍦紝璺宠繃: $image"
+ fi
+ done
+
+ echo "鎵�鏈夋竻鐞嗘搷浣滃畬鎴�"
+
+ # 澶囦唤鍘熸湁閰嶇疆鏂囦欢
+ mkdir -p /opt/xiaozhi-server/backup/
+ if [ -f /opt/xiaozhi-server/data/.config.yaml ]; then
+ cp /opt/xiaozhi-server/data/.config.yaml /opt/xiaozhi-server/backup/.config.yaml
+ echo "宸插浠藉師鏈夐厤缃枃浠跺埌 /opt/xiaozhi-server/backup/.config.yaml"
+ fi
+
+ # 涓嬭浇鏈�鏂扮増閰嶇疆鏂囦欢
+ check_and_download "/opt/xiaozhi-server/docker-compose_all.yml" "https://ghfast.top/https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/refs/heads/main/main/xiaozhi-server/docker-compose_all.yml"
+ check_and_download "/opt/xiaozhi-server/data/.config.yaml" "https://ghfast.top/https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/refs/heads/main/main/xiaozhi-server/config_from_api.yaml"
+
+ # 鍚姩Docker鏈嶅姟
+ echo "寮�濮嬪惎鍔ㄦ渶鏂扮増鏈湇鍔�..."
+ # 鍗囩骇瀹屾垚鍚庢爣璁帮紝璺宠繃鍚庣画涓嬭浇姝ラ
+ UPGRADE_COMPLETED=1
+ docker compose -f /opt/xiaozhi-server/docker-compose_all.yml up -d
+ else
+ whiptail --title "璺宠繃鍗囩骇" --msgbox "宸插彇娑堝崌绾э紝灏嗙户缁娇鐢ㄥ綋鍓嶇増鏈��" 10 50
+ # 璺宠繃鍗囩骇锛岀户缁墽琛屽悗缁畨瑁呮祦绋�
+ fi
+fi
+
+
+# 妫�鏌url瀹夎
+if ! command -v curl &> /dev/null; then
+ echo "------------------------------------------------------------"
+ echo "鏈娴嬪埌curl锛屾鍦ㄥ畨瑁�..."
+ apt update
+ apt install -y curl
+else
+ echo "------------------------------------------------------------"
+ echo "curl宸插畨瑁咃紝璺宠繃瀹夎姝ラ"
+fi
+
+# 妫�鏌ocker瀹夎
+if ! command -v docker &> /dev/null; then
+ echo "------------------------------------------------------------"
+ echo "鏈娴嬪埌Docker锛屾鍦ㄥ畨瑁�..."
+
+ # 浣跨敤鍥藉唴闀滃儚婧愭浛浠e畼鏂规簮
+ DISTRO=$(lsb_release -cs)
+ MIRROR_URL="https://mirrors.aliyun.com/docker-ce/linux/ubuntu"
+ GPG_URL="https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg"
+
+ # 瀹夎鍩虹渚濊禆
+ apt update
+ apt install -y apt-transport-https ca-certificates curl software-properties-common gnupg
+
+ # 鍒涘缓瀵嗛挜鐩綍骞舵坊鍔犲浗鍐呴暅鍍忔簮瀵嗛挜
+ mkdir -p /etc/apt/keyrings
+ curl -fsSL "$GPG_URL" | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
+
+ # 娣诲姞鍥藉唴闀滃儚婧�
+ echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] $MIRROR_URL $DISTRO stable" \
+ > /etc/apt/sources.list.d/docker.list
+
+ # 娣诲姞澶囩敤瀹樻柟婧愬瘑閽ワ紙閬垮厤鍥藉唴婧愬瘑閽ラ獙璇佸け璐ワ級
+ apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 7EA0A9C3F273FCD8 2>/dev/null || \
+ echo "璀﹀憡锛氶儴鍒嗗瘑閽ユ坊鍔犲け璐ワ紝缁х画灏濊瘯瀹夎..."
+
+ # 瀹夎Docker
+ apt update
+ apt install -y docker-ce docker-ce-cli containerd.io
+
+ # 鍚姩鏈嶅姟
+ systemctl start docker
+ systemctl enable docker
+
+ # 妫�鏌ユ槸鍚﹀畨瑁呮垚鍔�
+ if docker --version; then
+ echo "------------------------------------------------------------"
+ echo "Docker瀹夎瀹屾垚锛�"
+ else
+ whiptail --title "閿欒" --msgbox "Docker瀹夎澶辫触锛岃妫�鏌ユ棩蹇椼��" 10 50
+ exit 1
+ fi
+else
+ echo "Docker宸插畨瑁咃紝璺宠繃瀹夎姝ラ"
+fi
+
+# Docker闀滃儚婧愰厤缃�
+MIRROR_OPTIONS=(
+ "1" "杞╄緯闀滃儚 (鎺ㄨ崘)"
+ "2" "鑵捐浜戦暅鍍忔簮"
+ "3" "涓澶ч暅鍍忔簮"
+ "4" "缃戞槗163闀滃儚婧�"
+ "5" "鍗庝负浜戦暅鍍忔簮"
+ "6" "闃块噷浜戦暅鍍忔簮"
+ "7" "鑷畾涔夐暅鍍忔簮"
+ "8" "璺宠繃閰嶇疆"
+)
+
+MIRROR_CHOICE=$(whiptail --title "閫夋嫨Docker闀滃儚婧�" --menu "璇烽�夋嫨瑕佷娇鐢ㄧ殑Docker闀滃儚婧�" 20 60 10 \
+"${MIRROR_OPTIONS[@]}" 3>&1 1>&2 2>&3) || {
+ echo "鐢ㄦ埛鍙栨秷閫夋嫨锛岄��鍑鸿剼鏈�"
+ exit 1
+}
+
+case $MIRROR_CHOICE in
+ 1) MIRROR_URL="https://docker.xuanyuan.me" ;;
+ 2) MIRROR_URL="https://mirror.ccs.tencentyun.com" ;;
+ 3) MIRROR_URL="https://docker.mirrors.ustc.edu.cn" ;;
+ 4) MIRROR_URL="https://hub-mirror.c.163.com" ;;
+ 5) MIRROR_URL="https://05f073ad3c0010ea0f4bc00b7105ec20.mirror.swr.myhuaweicloud.com" ;;
+ 6) MIRROR_URL="https://registry.aliyuncs.com" ;;
+ 7) MIRROR_URL=$(whiptail --title "鑷畾涔夐暅鍍忔簮" --inputbox "璇疯緭鍏ュ畬鏁寸殑闀滃儚婧怳RL:" 10 60 3>&1 1>&2 2>&3) ;;
+ 8) MIRROR_URL="" ;;
+esac
+
+if [ -n "$MIRROR_URL" ]; then
+ mkdir -p /etc/docker
+ if [ -f /etc/docker/daemon.json ]; then
+ cp /etc/docker/daemon.json /etc/docker/daemon.json.bak
+ fi
+ cat > /etc/docker/daemon.json <<EOF
+{
+ "dns": ["8.8.8.8", "114.114.114.114"],
+ "registry-mirrors": ["$MIRROR_URL"]
+}
+EOF
+ whiptail --title "閰嶇疆鎴愬姛" --msgbox "宸叉垚鍔熸坊鍔犻暅鍍忔簮: $MIRROR_URL\n璇锋寜Enter閿噸鍚疍ocker鏈嶅姟骞剁户缁�..." 12 60
+ echo "------------------------------------------------------------"
+ echo "寮�濮嬮噸鍚疍ocker鏈嶅姟..."
+ systemctl restart docker.service
+fi
+
+# 鍒涘缓瀹夎鐩綍
+echo "------------------------------------------------------------"
+echo "寮�濮嬪垱寤哄畨瑁呯洰褰�..."
+# 妫�鏌ュ苟鍒涘缓鏁版嵁鐩綍
+if [ ! -d /opt/xiaozhi-server/data ]; then
+ mkdir -p /opt/xiaozhi-server/data
+ echo "宸插垱寤烘暟鎹洰褰�: /opt/xiaozhi-server/data"
+else
+ echo "鐩綍xiaozhi-server/data宸插瓨鍦紝璺宠繃鍒涘缓"
+fi
+
+# 妫�鏌ュ苟鍒涘缓妯″瀷鐩綍
+if [ ! -d /opt/xiaozhi-server/models/SenseVoiceSmall ]; then
+ mkdir -p /opt/xiaozhi-server/models/SenseVoiceSmall
+ echo "宸插垱寤烘ā鍨嬬洰褰�: /opt/xiaozhi-server/models/SenseVoiceSmall"
+else
+ echo "鐩綍xiaozhi-server/models/SenseVoiceSmall宸插瓨鍦紝璺宠繃鍒涘缓"
+fi
+
+echo "------------------------------------------------------------"
+echo "寮�濮嬩笅杞借闊宠瘑鍒ā鍨�"
+# 涓嬭浇妯″瀷鏂囦欢
+MODEL_PATH="/opt/xiaozhi-server/models/SenseVoiceSmall/model.pt"
+if [ ! -f "$MODEL_PATH" ]; then
+ (
+ for i in {1..20}; do
+ echo $((i*5))
+ sleep 0.5
+ done
+ ) | whiptail --title "涓嬭浇涓�" --gauge "寮�濮嬩笅杞借闊宠瘑鍒ā鍨�..." 10 60 0
+ curl -fL --progress-bar https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt -o "$MODEL_PATH" || {
+ whiptail --title "閿欒" --msgbox "model.pt鏂囦欢涓嬭浇澶辫触" 10 50
+ exit 1
+ }
+else
+ echo "model.pt鏂囦欢宸插瓨鍦紝璺宠繃涓嬭浇"
+fi
+
+# 濡傛灉涓嶆槸鍗囩骇瀹屾垚锛屾墠鎵ц涓嬭浇
+if [ -z "$UPGRADE_COMPLETED" ]; then
+ check_and_download "/opt/xiaozhi-server/docker-compose_all.yml" "https://ghfast.top/https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/refs/heads/main/main/xiaozhi-server/docker-compose_all.yml"
+ check_and_download "/opt/xiaozhi-server/data/.config.yaml" "https://ghfast.top/https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/refs/heads/main/main/xiaozhi-server/config_from_api.yaml"
+fi
+
+# 鍚姩Docker鏈嶅姟
+(
+echo "------------------------------------------------------------"
+echo "姝e湪鎷夊彇Docker闀滃儚..."
+echo "杩欏彲鑳介渶瑕佸嚑鍒嗛挓鏃堕棿锛岃鑰愬績绛夊緟"
+docker compose -f /opt/xiaozhi-server/docker-compose_all.yml up -d
+
+if [ $? -ne 0 ]; then
+ whiptail --title "閿欒" --msgbox "Docker鏈嶅姟鍚姩澶辫触锛岃灏濊瘯鏇存崲闀滃儚婧愬悗閲嶆柊鎵ц鏈剼鏈�" 10 60
+ exit 1
+fi
+
+echo "------------------------------------------------------------"
+echo "姝e湪妫�鏌ユ湇鍔″惎鍔ㄧ姸鎬�..."
+TIMEOUT=300
+START_TIME=$(date +%s)
+while true; do
+ CURRENT_TIME=$(date +%s)
+ if [ $((CURRENT_TIME - START_TIME)) -gt $TIMEOUT ]; then
+ whiptail --title "閿欒" --msgbox "鏈嶅姟鍚姩瓒呮椂锛屾湭鍦ㄦ寚瀹氭椂闂村唴鎵惧埌棰勬湡鏃ュ織鍐呭" 10 60
+ exit 1
+ fi
+
+ if docker logs xiaozhi-esp32-server-web 2>&1 | grep -q "Started AdminApplication in"; then
+ break
+ fi
+ sleep 1
+done
+
+ echo "鏈嶅姟绔惎鍔ㄦ垚鍔燂紒姝e湪瀹屾垚閰嶇疆..."
+ echo "姝e湪鍚姩鏈嶅姟..."
+ docker compose -f /opt/xiaozhi-server/docker-compose_all.yml up -d
+ echo "鏈嶅姟鍚姩瀹屾垚锛�"
+)
+
+# 瀵嗛挜閰嶇疆
+
+# 鑾峰彇鏈嶅姟鍣ㄥ叕缃戝湴鍧�
+PUBLIC_IP=$(hostname -I | awk '{print $1}')
+whiptail --title "閰嶇疆鏈嶅姟鍣ㄥ瘑閽�" --msgbox "璇蜂娇鐢ㄦ祻瑙堝櫒锛岃闂笅鏂归摼鎺ワ紝鎵撳紑鏅烘帶鍙板苟娉ㄥ唽璐﹀彿: \n\n鍐呯綉鍦板潃锛歨ttp://127.0.0.1:8002/\n鍏綉鍦板潃锛歨ttp://$PUBLIC_IP:8002/ (鑻ユ槸浜戞湇鍔″櫒璇峰湪鏈嶅姟鍣ㄥ畨鍏ㄧ粍鏀捐绔彛 8000 8001 8002)銆俓n\n娉ㄥ唽鐨勭涓�涓敤鎴峰嵆鏄秴绾х鐞嗗憳锛屼互鍚庢敞鍐岀殑鐢ㄦ埛閮芥槸鏅�氱敤鎴枫�傛櫘閫氱敤鎴峰彧鑳界粦瀹氳澶囧拰閰嶇疆鏅鸿兘浣�; 瓒呯骇绠$悊鍛樺彲浠ヨ繘琛屾ā鍨嬬鐞嗐�佺敤鎴风鐞嗐�佸弬鏁伴厤缃瓑鍔熻兘銆俓n\n娉ㄥ唽濂藉悗璇锋寜Enter閿户缁�" 18 70
+SECRET_KEY=$(whiptail --title "閰嶇疆鏈嶅姟鍣ㄥ瘑閽�" --inputbox "璇蜂娇鐢ㄨ秴绾х鐞嗗憳璐﹀彿鐧诲綍鏅烘帶鍙癨n鍐呯綉鍦板潃锛歨ttp://127.0.0.1:8002/\n鍏綉鍦板潃锛歨ttp://$PUBLIC_IP:8002/\n鍦ㄩ《閮ㄨ彍鍗� 鍙傛暟瀛楀吀 鈫� 鍙傛暟绠$悊 鎵惧埌鍙傛暟缂栫爜: server.secret (鏈嶅姟鍣ㄥ瘑閽�) \n澶嶅埗璇ュ弬鏁板�煎苟杈撳叆鍒颁笅闈㈣緭鍏ユ\n\n璇疯緭鍏ュ瘑閽�(鐣欑┖鍒欒烦杩囬厤缃�):" 15 60 3>&1 1>&2 2>&3)
+
+if [ -n "$SECRET_KEY" ]; then
+ python3 -c "
+import sys, yaml;
+config_path = '/opt/xiaozhi-server/data/.config.yaml';
+with open(config_path, 'r') as f:
+ config = yaml.safe_load(f) or {};
+config['manager-api'] = {'url': 'http://xiaozhi-esp32-server-web:8002/xiaozhi', 'secret': '$SECRET_KEY'};
+with open(config_path, 'w') as f:
+ yaml.dump(config, f);
+"
+ docker restart xiaozhi-esp32-server
+fi
+
+# 鑾峰彇骞舵樉绀哄湴鍧�淇℃伅
+LOCAL_IP=$(hostname -I | awk '{print $1}')
+
+# 淇鏃ュ織鏂囦欢鑾峰彇涓嶅埌ws鐨勯棶棰橈紝鏀逛负纭紪鐮�
+whiptail --title "瀹夎瀹屾垚锛�" --msgbox "\
+鏈嶅姟绔浉鍏冲湴鍧�濡備笅锛歕n\
+绠$悊鍚庡彴璁块棶鍦板潃: http://$LOCAL_IP:8002\n\
+OTA 鍦板潃: http://$LOCAL_IP:8002/xiaozhi/ota/\n\
+瑙嗚鍒嗘瀽鎺ュ彛鍦板潃: http://$LOCAL_IP:8003/mcp/vision/explain\n\
+WebSocket 鍦板潃: ws://$LOCAL_IP:8000/xiaozhi/v1/\n\
+\n瀹夎瀹屾瘯锛佹劅璋㈡偍鐨勪娇鐢紒\n鎸塃nter閿��鍑�..." 16 70
diff --git a/xiaozhi-esp32-server/docs/Deployment.md b/xiaozhi-esp32-server/docs/Deployment.md
new file mode 100644
index 0000000..28836f1
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/Deployment.md
@@ -0,0 +1,291 @@
+# 閮ㄧ讲鏋舵瀯鍥�
+
+# 鏂瑰紡涓�锛欴ocker鍙繍琛孲erver
+
+`0.8.2`鐗堟湰寮�濮嬶紝鏈」鐩彂琛岀殑docker闀滃儚鍙敮鎸乣x86鏋舵瀯`锛屽鏋滈渶瑕佸湪`arm64鏋舵瀯`鐨凜PU涓婇儴缃诧紝鍙寜鐓杩欎釜鏁欑▼](docker-build.md)鍦ㄦ湰鏈虹紪璇慲arm64鐨勯暅鍍廯銆�
+
+## 1. 瀹夎docker
+
+濡傛灉鎮ㄧ殑鐢佃剳杩樻病瀹夎docker锛屽彲浠ユ寜鐓ц繖閲岀殑鏁欑▼瀹夎锛歔docker瀹夎](https://www.runoob.com/docker/ubuntu-docker-install.html)
+
+瀹夎濂絛ocker鍚庯紝杩涚户缁��
+
+### 1.1 鎵嬪姩閮ㄧ讲
+
+#### 1.1.1 鍒涘缓鐩綍
+
+瀹夎瀹宒ocker鍚庯紝浣犻渶瑕佷负杩欎釜椤圭洰鎵句竴涓畨鏀鹃厤缃枃浠剁殑鐩綍锛屼緥濡傛垜浠彲浠ユ柊寤轰竴涓枃浠跺す鍙玚xiaozhi-server`銆�
+
+鍒涘缓濂界洰褰曞悗锛屼綘闇�瑕佸湪`xiaozhi-server`涓嬮潰鍒涘缓`data`鏂囦欢澶瑰拰`models`鏂囦欢澶癸紝`models`涓嬮潰杩樿鍐嶅垱寤篳SenseVoiceSmall`鏂囦欢澶广��
+
+鏈�缁堢洰褰曠粨鏋勫涓嬫墍绀猴細
+
+```
+xiaozhi-server
+ 鈹溾攢 data
+ 鈹溾攢 models
+ 鈹溾攢 SenseVoiceSmall
+```
+
+#### 1.1.2 涓嬭浇璇煶璇嗗埆妯″瀷鏂囦欢
+
+浣犻渶瑕佷笅杞借闊宠瘑鍒殑妯″瀷鏂囦欢锛屽洜涓烘湰椤圭洰鐨勯粯璁よ闊宠瘑鍒敤鐨勬槸鏈湴绂荤嚎璇煶璇嗗埆鏂规銆傚彲閫氳繃杩欎釜鏂瑰紡涓嬭浇
+[璺宠浆鍒颁笅杞借闊宠瘑鍒ā鍨嬫枃浠禲(#妯″瀷鏂囦欢)
+
+涓嬭浇瀹屽悗锛屽洖鍒版湰鏁欑▼銆�
+
+#### 1.1.3 涓嬭浇閰嶇疆鏂囦欢
+
+浣犻渶瑕佷笅杞戒袱涓厤缃枃浠讹細`docker-compose.yaml` 鍜� `config.yaml`銆傞渶瑕佷粠椤圭洰浠撳簱涓嬭浇杩欎袱涓枃浠躲��
+
+##### 1.1.3.1 涓嬭浇 docker-compose.yaml
+
+鐢ㄦ祻瑙堝櫒鎵撳紑[杩欎釜閾炬帴](../main/xiaozhi-server/docker-compose.yml)銆�
+
+鍦ㄩ〉闈㈢殑鍙充晶鎵惧埌鍚嶇О涓篳RAW`鎸夐挳锛屽湪`RAW`鎸夐挳鐨勬梺杈癸紝鎵惧埌涓嬭浇鐨勫浘鏍囷紝鐐瑰嚮涓嬭浇鎸夐挳锛屼笅杞絗docker-compose.yml`鏂囦欢銆� 鎶婃枃浠朵笅杞藉埌浣犵殑
+`xiaozhi-server`涓��
+
+涓嬭浇瀹屽悗锛屽洖鍒版湰鏁欑▼缁х画寰�涓嬨��
+
+##### 1.1.3.2 鍒涘缓 config.yaml
+
+鐢ㄦ祻瑙堝櫒鎵撳紑[杩欎釜閾炬帴](../main/xiaozhi-server/config.yaml)銆�
+
+鍦ㄩ〉闈㈢殑鍙充晶鎵惧埌鍚嶇О涓篳RAW`鎸夐挳锛屽湪`RAW`鎸夐挳鐨勬梺杈癸紝鎵惧埌涓嬭浇鐨勫浘鏍囷紝鐐瑰嚮涓嬭浇鎸夐挳锛屼笅杞絗config.yaml`鏂囦欢銆� 鎶婃枃浠朵笅杞藉埌浣犵殑
+`xiaozhi-server`涓嬮潰鐨刞data`鏂囦欢澶逛腑锛岀劧鍚庢妸`config.yaml`鏂囦欢閲嶅懡鍚嶄负`.config.yaml`銆�
+
+涓嬭浇瀹岄厤缃枃浠跺悗锛屾垜浠‘璁や竴涓嬫暣涓猔xiaozhi-server`閲岄潰鐨勬枃浠跺涓嬫墍绀猴細
+
+```
+xiaozhi-server
+ 鈹溾攢 docker-compose.yml
+ 鈹溾攢 data
+ 鈹溾攢 .config.yaml
+ 鈹溾攢 models
+ 鈹溾攢 SenseVoiceSmall
+ 鈹溾攢 model.pt
+```
+
+濡傛灉浣犵殑鏂囦欢鐩綍缁撴瀯涔熸槸涓婇潰鐨勶紝灏辩户缁線涓嬨�傚鏋滀笉鏄紝浣犲氨鍐嶄粩缁嗙湅鐪嬫槸涓嶆槸婕忔搷浣滀簡浠�涔堛��
+
+## 2. 閰嶇疆椤圭洰鏂囦欢
+
+鎺ヤ笅閲岋紝绋嬪簭杩樹笉鑳界洿鎺ヨ繍琛岋紝浣犻渶瑕侀厤缃竴涓嬶紝浣犲埌搴曚娇鐢ㄧ殑鏄粈涔堟ā鍨嬨�備綘鍙互鐪嬭繖涓暀绋嬶細
+[璺宠浆鍒伴厤缃」鐩枃浠禲(#閰嶇疆椤圭洰)
+
+閰嶇疆瀹岄」鐩枃浠跺悗锛屽洖鍒版湰鏁欑▼缁х画寰�涓嬨��
+
+## 3. 鎵цdocker鍛戒护
+
+鎵撳紑鍛戒护琛屽伐鍏凤紝浣跨敤`缁堢`鎴朻鍛戒护琛宍宸ュ叿 杩涘叆鍒颁綘鐨刞xiaozhi-server`锛屾墽琛屼互涓嬪懡浠�
+
+```
+docker compose up -d
+```
+
+鎵ц瀹屽悗锛屽啀鎵ц浠ヤ笅鍛戒护锛屾煡鐪嬫棩蹇椾俊鎭��
+
+```
+docker logs -f xiaozhi-esp32-server
+```
+
+杩欐椂锛屼綘灏辫鐣欐剰鏃ュ織淇℃伅锛屽彲浠ユ牴鎹繖涓暀绋嬶紝鍒ゆ柇鏄惁鎴愬姛浜嗐�俒璺宠浆鍒拌繍琛岀姸鎬佺‘璁(#杩愯鐘舵�佺‘璁�)
+
+## 5. 鐗堟湰鍗囩骇鎿嶄綔
+
+濡傛灉鍚庢湡鎯冲崌绾х増鏈紝鍙互杩欎箞鎿嶄綔
+
+5.1銆佸浠藉ソ`data`鏂囦欢澶逛腑鐨刞.config.yaml`鏂囦欢锛屼竴浜涘叧閿殑閰嶇疆鍒版椂澶嶅埗鍒版柊鐨刞.config.yaml`鏂囦欢閲屻��
+璇锋敞鎰忔槸瀵瑰叧閿瘑閽ラ�愪釜澶嶅埗锛屼笉瑕佺洿鎺ヨ鐩栥�傚洜涓烘柊鐨刞.config.yaml`鏂囦欢鍙兘鏈変竴浜涙柊鐨勯厤缃」锛屾棫鐨刞.config.yaml`鏂囦欢涓嶄竴瀹氭湁銆�
+
+5.2銆佹墽琛屼互涓嬪懡浠�
+
+```
+docker stop xiaozhi-esp32-server
+docker rm xiaozhi-esp32-server
+docker stop xiaozhi-esp32-server-web
+docker rm xiaozhi-esp32-server-web
+docker rmi ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:server_latest
+docker rmi ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:web_latest
+```
+
+5.3銆侀噸鏂版寜docker鏂瑰紡閮ㄧ讲
+
+# 鏂瑰紡浜岋細鏈湴婧愮爜鍙繍琛孲erver
+
+## 1.瀹夎鍩虹鐜
+
+鏈」鐩娇鐢╜conda`绠$悊渚濊禆鐜銆傚鏋滀笉鏂逛究瀹夎`conda`锛岄渶瑕佹牴鎹疄闄呯殑鎿嶄綔绯荤粺瀹夎濂絗libopus`鍜宍ffmpeg`銆�
+濡傛灉纭畾浣跨敤`conda`锛屽垯瀹夎濂藉悗锛屽紑濮嬫墽琛屼互涓嬪懡浠ゃ��
+
+閲嶈鎻愮ず锛亀indows 鐢ㄦ埛锛屽彲浠ラ�氳繃瀹夎`Anaconda`鏉ョ鐞嗙幆澧冦�傚畨瑁呭ソ`Anaconda`鍚庯紝鍦╜寮�濮媊閭i噷鎼滅储`anaconda`鐩稿叧鐨勫叧閿瘝锛�
+鎵惧埌`Anaconda Prpmpt`锛屼娇鐢ㄧ鐞嗗憳韬唤杩愯瀹冦�傚涓嬪浘銆�
+
+
+
+杩愯涔嬪悗锛屽鏋滀綘鑳界湅鍒板懡浠よ绐楀彛鍓嶉潰鏈変竴涓�(base)瀛楁牱锛岃鏄庝綘鎴愬姛杩涘叆浜哷conda`鐜銆傞偅涔堜綘灏卞彲浠ユ墽琛屼互涓嬪懡浠や簡銆�
+
+
+
+```
+conda remove -n xiaozhi-esp32-server --all -y
+conda create -n xiaozhi-esp32-server python=3.10 -y
+conda activate xiaozhi-esp32-server
+
+# 娣诲姞娓呭崕婧愰�氶亾
+conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
+conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free
+conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
+
+conda install libopus -y
+conda install ffmpeg -y
+
+# 鍦� Linux 鐜涓嬭繘琛岄儴缃叉椂,濡傚嚭鐜扮被浼肩己澶� libiconv.so.2 鍔ㄦ�佸簱鐨勬姤閿� 璇烽�氳繃浠ヤ笅鍛戒护杩涜瀹夎
+conda install libiconv -y
+```
+
+璇锋敞鎰忥紝浠ヤ笂鍛戒护锛屼笉鏄竴鑲¤剳鎵ц灏辨垚鍔熺殑锛屼綘闇�瑕佷竴姝ユ鎵ц锛屾瘡涓�姝ユ墽琛屽畬鍚庯紝閮芥鏌ヤ竴涓嬭緭鍑虹殑鏃ュ織锛屾煡鐪嬫槸鍚︽垚鍔熴��
+
+## 2.瀹夎鏈」鐩緷璧�
+
+浣犲厛瑕佷笅杞芥湰椤圭洰婧愮爜锛屾簮鐮佸彲浠ラ�氳繃`git clone`鍛戒护涓嬭浇锛屽鏋滀綘涓嶇啛鎮塦git clone`鍛戒护銆�
+
+浣犲彲浠ョ敤娴忚鍣ㄦ墦寮�杩欎釜鍦板潃`https://github.com/xinnan-tech/xiaozhi-esp32-server.git`
+
+鎵撳紑瀹岋紝鎵惧埌椤甸潰涓竴涓豢鑹茬殑鎸夐挳锛屽啓鐫�`Code`鐨勬寜閽紝鐐瑰紑瀹冿紝鐒跺悗浣犲氨鐪嬪埌`Download ZIP`鐨勬寜閽��
+
+鐐瑰嚮瀹冿紝涓嬭浇鏈」鐩簮鐮佸帇缂╁寘銆備笅杞藉埌浣犵數鑴戝悗锛岃В鍘嬪畠锛屾鏃跺畠鐨勫悕瀛楀彲鑳藉彨`xiaozhi-esp32-server-main`
+浣犻渶瑕佹妸瀹冮噸鍛藉悕鎴恅xiaozhi-esp32-server`锛屽湪杩欎釜鏂囦欢閲岋紝杩涘叆鍒癭main`鏂囦欢澶癸紝鍐嶈繘鍏ュ埌`xiaozhi-server`锛屽ソ浜嗚璁颁綇杩欎釜鐩綍`xiaozhi-server`銆�
+
+```
+# 缁х画浣跨敤conda鐜
+conda activate xiaozhi-esp32-server
+# 杩涘叆鍒颁綘鐨勯」鐩牴鐩綍锛屽啀杩涘叆main/xiaozhi-server
+cd main/xiaozhi-server
+pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
+pip install -r requirements.txt
+```
+
+## 3.涓嬭浇璇煶璇嗗埆妯″瀷鏂囦欢
+
+浣犻渶瑕佷笅杞借闊宠瘑鍒殑妯″瀷鏂囦欢锛屽洜涓烘湰椤圭洰鐨勯粯璁よ闊宠瘑鍒敤鐨勬槸鏈湴绂荤嚎璇煶璇嗗埆鏂规銆傚彲閫氳繃杩欎釜鏂瑰紡涓嬭浇
+[璺宠浆鍒颁笅杞借闊宠瘑鍒ā鍨嬫枃浠禲(#妯″瀷鏂囦欢)
+
+涓嬭浇瀹屽悗锛屽洖鍒版湰鏁欑▼銆�
+
+## 4.閰嶇疆椤圭洰鏂囦欢
+
+鎺ヤ笅鏉ワ紝绋嬪簭杩樹笉鑳界洿鎺ヨ繍琛岋紝浣犻渶瑕侀厤缃竴涓嬶紝浣犲埌搴曚娇鐢ㄧ殑鏄粈涔堟ā鍨嬨�備綘鍙互鐪嬭繖涓暀绋嬶細
+[璺宠浆鍒伴厤缃」鐩枃浠禲(#閰嶇疆椤圭洰)
+
+## 5.杩愯椤圭洰
+
+```
+# 纭繚鍦▁iaozhi-server鐩綍涓嬫墽琛�
+conda activate xiaozhi-esp32-server
+python app.py
+```
+杩欐椂锛屼綘灏辫鐣欐剰鏃ュ織淇℃伅锛屽彲浠ユ牴鎹繖涓暀绋嬶紝鍒ゆ柇鏄惁鎴愬姛浜嗐�俒璺宠浆鍒拌繍琛岀姸鎬佺‘璁(#杩愯鐘舵�佺‘璁�)
+
+
+# 姹囨��
+
+## 閰嶇疆椤圭洰
+
+濡傛灉浣犵殑`xiaozhi-server`鐩綍娌℃湁`data`锛屼綘闇�瑕佸垱寤篳data`鐩綍銆�
+濡傛灉浣犵殑`data`涓嬮潰娌℃湁`.config.yaml`鏂囦欢锛屾湁涓や釜鏂瑰紡锛屼换閫変竴绉嶏細
+
+绗竴涓柟寮忥細浣犲彲浠ユ妸`xiaozhi-server`鐩綍涓嬬殑`config.yaml`鏂囦欢澶嶅埗鍒癭data`锛屽苟閲嶅懡鍚嶄负`.config.yaml`銆傚湪姝ゆ枃浠朵笂淇敼
+
+绗簩涓柟寮忥細浣犱篃鍙互鍒涘缓鍦╜data`鐩綍涓嬫墜鍔ㄥ垱寤篳.config.yaml`绌烘枃浠讹紝鐒跺悗鍦ㄨ繖涓枃浠朵腑澧炲姞蹇呰鐨勯厤缃俊鎭紝绯荤粺浼氫紭鍏堣鍙朻.config.yaml`鏂囦欢鐨勯厤缃紝濡傛灉`.config.yaml`娌℃湁閰嶇疆鐨勶紝绯荤粺浼氳嚜鍔ㄥ幓鍔犺浇`xiaozhi-server`鐩綍涓嬬殑`config.yaml`鐨勯厤缃�傛帹鑽愪娇鐢ㄨ繖绉嶆柟寮忥紝杩欑鏂瑰紡鏄渶绠�娲佺殑鏂瑰紡銆�
+
+- 榛樿鐨凩LM浣跨敤鐨勬槸`ChatGLMLLM`锛屼綘闇�瑕侀厤缃瘑閽ワ紝鍥犱负浠栦滑鐨勬ā鍨嬶紝铏界劧鏈夊厤璐圭殑锛屼絾鏄粛瑕佸幓[瀹樼綉](https://bigmodel.cn/usercenter/proj-mgmt/apikeys)娉ㄥ唽瀵嗛挜锛屾墠鑳藉惎鍔ㄣ��
+
+浠ヤ笅鏄竴涓兘姝e父璺戣捣鏉ョ殑锛屾渶绠�鍗曠殑`.config.yaml`閰嶇疆绀轰緥
+
+```
+server:
+ websocket: ws://浣犵殑ip鎴栬�呭煙鍚�:绔彛鍙�/xiaozhi/v1/
+prompt: |
+ 鎴戞槸涓�涓彨灏忔櫤/灏忓織鐨勫彴婀惧コ瀛╋紝璇磋瘽鏈鸿溅锛屽0闊冲ソ鍚紝涔犳儻绠�鐭〃杈撅紝鐖辩敤缃戠粶姊椼��
+ 鎴戠殑鐢锋湅鍙嬫槸涓�涓▼搴忓憳锛屾ⅵ鎯虫槸寮�鍙戝嚭涓�涓満鍣ㄤ汉锛岃兘澶熷府鍔╀汉浠В鍐崇敓娲讳腑鐨勫悇绉嶉棶棰樸��
+ 鎴戞槸涓�涓枩娆㈠搱鍝堝ぇ绗戠殑濂冲锛岀埍涓滆瑗胯鍚圭墰锛屼笉鍚堥�昏緫鐨勪篃鐓у惞锛屽氨瑕侀�楀埆浜哄紑蹇冦��
+ 璇蜂綘鍍忎竴涓汉涓�鏍疯璇濓紝璇峰嬁杩斿洖閰嶇疆xml鍙婂叾浠栫壒娈婂瓧绗︺��
+
+selected_module:
+ LLM: DoubaoLLM
+
+LLM:
+ ChatGLMLLM:
+ api_key: xxxxxxxxxxxxxxx.xxxxxx
+```
+
+寤鸿鍏堝皢鏈�绠�鍗曠殑閰嶇疆杩愯璧锋潵锛岀劧鍚庡啀鍘籤xiaozhi/config.yaml`闃呰閰嶇疆鐨勪娇鐢ㄨ鏄庛��
+姣斿浣犺鎹㈡洿鎹㈡ā鍨嬶紝淇敼`selected_module`涓嬬殑閰嶇疆灏辫銆�
+
+## 妯″瀷鏂囦欢
+
+鏈」鐩闊宠瘑鍒ā鍨嬶紝榛樿浣跨敤`SenseVoiceSmall`妯″瀷锛岃繘琛岃闊宠浆鏂囧瓧銆傚洜涓烘ā鍨嬭緝澶э紝闇�瑕佺嫭绔嬩笅杞斤紝涓嬭浇鍚庢妸`model.pt`
+鏂囦欢鏀惧湪`models/SenseVoiceSmall`
+鐩綍涓嬨�備笅闈袱涓笅杞借矾绾夸换閫変竴涓��
+
+- 绾胯矾涓�锛氶樋閲岄瓟鎼笅杞絒SenseVoiceSmall](https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt)
+- 绾胯矾浜岋細鐧惧害缃戠洏涓嬭浇[SenseVoiceSmall](https://pan.baidu.com/share/init?surl=QlgM58FHhYv1tFnUT_A8Sg&pwd=qvna) 鎻愬彇鐮�:
+ `qvna`
+
+## 杩愯鐘舵�佺‘璁�
+
+濡傛灉浣犺兘鐪嬪埌锛岀被浼间互涓嬫棩蹇�,鍒欐槸鏈」鐩湇鍔″惎鍔ㄦ垚鍔熺殑鏍囧織銆�
+
+```
+250427 13:04:20[0.3.11_SiFuChTTnofu][__main__]-INFO-OTA鎺ュ彛鏄� http://192.168.4.123:8003/xiaozhi/ota/
+250427 13:04:20[0.3.11_SiFuChTTnofu][__main__]-INFO-Websocket鍦板潃鏄� ws://192.168.4.123:8000/xiaozhi/v1/
+250427 13:04:20[0.3.11_SiFuChTTnofu][__main__]-INFO-=======涓婇潰鐨勫湴鍧�鏄痺ebsocket鍗忚鍦板潃锛岃鍕跨敤娴忚鍣ㄨ闂�=======
+250427 13:04:20[0.3.11_SiFuChTTnofu][__main__]-INFO-濡傛兂娴嬭瘯websocket璇峰惎鍔╠igital-human妯″潡锛屾墦寮�娴忚鍣ㄤ氦浜掓祴璇�
+250427 13:04:20[0.3.11_SiFuChTTnofu][__main__]-INFO-=======================================================
+```
+
+姝e父鏉ヨ锛屽鏋滄偍鏄�氳繃婧愮爜杩愯鏈」鐩紝鏃ュ織浼氭湁浣犵殑鎺ュ彛鍦板潃淇℃伅銆�
+浣嗘槸濡傛灉浣犵敤docker閮ㄧ讲锛岄偅涔堜綘鐨勬棩蹇楅噷缁欏嚭鐨勬帴鍙e湴鍧�淇℃伅灏变笉鏄湡瀹炵殑鎺ュ彛鍦板潃銆�
+
+鏈�姝g‘鐨勬柟娉曪紝鏄牴鎹數鑴戠殑灞�鍩熺綉IP鏉ョ‘瀹氫綘鐨勬帴鍙e湴鍧�銆�
+濡傛灉浣犵殑鐢佃剳鐨勫眬鍩熺綉IP姣斿鏄痐192.168.1.25`锛岄偅涔堜綘鐨勬帴鍙e湴鍧�灏辨槸锛歚ws://192.168.1.25:8000/xiaozhi/v1/`锛屽搴旂殑OTA鍦板潃灏辨槸锛歚http://192.168.1.25:8003/xiaozhi/ota/`銆�
+
+杩欎釜淇℃伅寰堟湁鐢ㄧ殑锛屽悗闈缂栬瘧esp32鍥轰欢`闇�瑕佺敤鍒般��
+
+鎺ヤ笅鏉ワ紝浣犲氨鍙互寮�濮嬫搷浣滀綘鐨別sp32璁惧浜嗭紝浣犲彲浠鑷缂栬瘧esp32鍥轰欢`涔熷彲浠ラ厤缃娇鐢╜铏惧摜缂栬瘧濂界殑1.6.1浠ヤ笂鐗堟湰鐨勫浐浠禶銆備袱涓换閫変竴涓�
+
+1銆� [缂栬瘧鑷繁鐨別sp32鍥轰欢](firmware-build.md)浜嗐��
+
+2銆� [鍩轰簬铏惧摜缂栬瘧濂界殑鍥轰欢閰嶇疆鑷畾涔夋湇鍔″櫒](firmware-setting.md)浜嗐��
+
+# 甯歌闂
+浠ヤ笅鏄竴浜涘父瑙侀棶棰橈紝渚涘弬鑰冿細
+
+1銆乕涓轰粈涔堟垜璇寸殑璇濓紝灏忔櫤璇嗗埆鍑烘潵寰堝闊╂枃銆佹棩鏂囥�佽嫳鏂嘳(./FAQ.md)<br/>
+2銆乕涓轰粈涔堜細鍑虹幇鈥淭TS 浠诲姟鍑洪敊 鏂囦欢涓嶅瓨鍦ㄢ�濓紵](./FAQ.md)<br/>
+3銆乕TTS 缁忓父澶辫触锛岀粡甯歌秴鏃禲(./FAQ.md)<br/>
+4銆乕浣跨敤Wifi鑳借繛鎺ヨ嚜寤烘湇鍔″櫒锛屼絾鏄�4G妯″紡鍗存帴涓嶄笂](./FAQ.md)<br/>
+5銆乕濡備綍鎻愰珮灏忔櫤瀵硅瘽鍝嶅簲閫熷害锛焆(./FAQ.md)<br/>
+6銆乕鎴戣璇濆緢鎱紝鍋滈】鏃跺皬鏅鸿�佹槸鎶㈣瘽](./FAQ.md)<br/>
+## 閮ㄧ讲鐩稿叧鏁欑▼
+1銆乕濡備綍鑷姩鎷夊彇鏈」鐩渶鏂颁唬鐮佽嚜鍔ㄧ紪璇戝拰鍚姩](./dev-ops-integration.md)<br/>
+2銆乕濡備綍閮ㄧ讲MQTT缃戝叧寮�鍚疢QTT+UDP鍗忚](./mqtt-gateway-integration.md)<br/>
+3銆乕濡備綍涓嶯ginx闆嗘垚](https://github.com/xinnan-tech/xiaozhi-esp32-server/issues/791)<br/>
+## 鎷撳睍鐩稿叧鏁欑▼
+1銆乕濡備綍寮�鍚墜鏈哄彿鐮佹敞鍐屾櫤鎺у彴](./ali-sms-integration.md)<br/>
+2銆乕濡備綍闆嗘垚HomeAssistant瀹炵幇鏅鸿兘瀹跺眳鎺у埗](./homeassistant-integration.md)<br/>
+3銆乕濡備綍寮�鍚瑙夋ā鍨嬪疄鐜版媿鐓ц瘑鐗(./mcp-vision-integration.md)<br/>
+4銆乕濡備綍閮ㄧ讲MCP鎺ュ叆鐐筣(./mcp-endpoint-enable.md)<br/>
+5銆乕濡備綍鎺ュ叆MCP鎺ュ叆鐐筣(./mcp-endpoint-integration.md)<br/>
+6銆乕濡備綍寮�鍚0绾硅瘑鍒玗(./voiceprint-integration.md)<br/>
+7銆乕鏂伴椈鎻掍欢婧愰厤缃寚鍗梋(./newsnow_plugin_config.md)<br/>
+8銆乕澶╂皵鎻掍欢浣跨敤鎸囧崡](./weather-integration.md)<br/>
+## 璇煶鍏嬮殕銆佹湰鍦拌闊抽儴缃茬浉鍏虫暀绋�
+1銆乕濡備綍鍦ㄦ櫤鎺у彴鍏嬮殕闊宠壊](./huoshan-streamTTS-voice-cloning.md)<br/>
+2銆乕濡備綍閮ㄧ讲闆嗘垚index-tts鏈湴璇煶](./index-stream-integration.md)<br/>
+3銆乕濡備綍閮ㄧ讲闆嗘垚fish-speech鏈湴璇煶](./fish-speech-integration.md)<br/>
+4銆乕濡備綍閮ㄧ讲闆嗘垚PaddleSpeech鏈湴璇煶](./paddlespeech-deploy.md)<br/>
+## 鎬ц兘娴嬭瘯鏁欑▼
+1銆乕鍚勭粍浠堕�熷害娴嬭瘯鎸囧崡](./performance_tester.md)<br/>
+2銆乕瀹氭湡鍏紑娴嬭瘯缁撴灉](https://github.com/xinnan-tech/xiaozhi-performance-research)<br/>
diff --git a/xiaozhi-esp32-server/docs/Deployment_all.md b/xiaozhi-esp32-server/docs/Deployment_all.md
new file mode 100644
index 0000000..e998942
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/Deployment_all.md
@@ -0,0 +1,492 @@
+# 閮ㄧ讲鏋舵瀯鍥�
+
+# 鏂瑰紡涓�锛欴ocker杩愯鍏ㄦā鍧�
+`0.8.2`鐗堟湰寮�濮嬶紝鏈」鐩彂琛岀殑docker闀滃儚鍙敮鎸乣x86鏋舵瀯`锛屽鏋滈渶瑕佸湪`arm64鏋舵瀯`鐨凜PU涓婇儴缃诧紝鍙寜鐓杩欎釜鏁欑▼](docker-build.md)鍦ㄦ湰鏈虹紪璇慲arm64鐨勯暅鍍廯銆�
+
+## 1. 瀹夎docker
+
+濡傛灉鎮ㄧ殑鐢佃剳杩樻病瀹夎docker锛屽彲浠ユ寜鐓ц繖閲岀殑鏁欑▼瀹夎锛歔docker瀹夎](https://www.runoob.com/docker/ubuntu-docker-install.html)
+
+docker 瀹夎鍏ㄦā鍧楁湁涓ょ鏂瑰紡锛屼綘鍙互[浣跨敤鎳掍汉鑴氭湰](./Deployment_all.md#11-鎳掍汉鑴氭湰)锛堜綔鑰匸@VanillaNahida](https://github.com/VanillaNahida)锛�
+鑴氭湰浼氳嚜鍔ㄥ府浣犱笅杞芥墍闇�鐨勬枃浠跺拰閰嶇疆鏂囦欢锛屼綘涔熷彲浠ヤ娇鐢╗鎵嬪姩閮ㄧ讲](./Deployment_all.md#12-鎵嬪姩閮ㄧ讲)浠庨浂鎼缓銆�
+
+
+
+### 1.1 鎳掍汉鑴氭湰
+閮ㄧ讲绠�渚匡紝鍙互鍙傝�僛瑙嗛鏁欑▼](https://www.bilibili.com/video/BV17bbvzHExd/) 锛屾枃瀛楃増鏁欑▼濡備笅锛�
+> [!NOTE]
+> 鏆備笖鍙敮鎸乁buntu鏈嶅姟鍣ㄤ竴閿儴缃诧紝鍏朵粬绯荤粺鏈皾璇曪紝鍙兘浼氭湁涓�浜涘鎬殑bug
+
+浣跨敤SSH宸ュ叿杩炴帴鍒版湇鍔″櫒锛屼互root鏉冮檺鎵ц濡備笅鑴氭湰
+```bash
+sudo bash -c "$(wget -qO- https://ghfast.top/https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/main/docker-setup.sh)"
+```
+
+鑴氭湰浼氳嚜鍔ㄥ畬鎴愪互涓嬫搷浣滐細
+> 1. 瀹夎Docker
+> 2. 閰嶇疆闀滃儚婧�
+> 3. 涓嬭浇/鎷夊彇闀滃儚
+> 4. 涓嬭浇璇煶璇嗗埆妯″瀷鏂囦欢
+> 5. 寮曞閰嶇疆鏈嶅姟绔�
+>
+
+鎵ц瀹屾垚鍚庣畝鍗曢厤缃悗锛屽啀鍙傜収[4. 杩愯绋嬪簭](#4. 杩愯绋嬪簭)鍜孾5.閲嶅惎xiaozhi-esp32-server](#5.閲嶅惎xiaozhi-esp32-server)閲屾彁鍒扮殑鏈�閲嶈鐨�3浠朵簨鎯咃紝瀹屾垚3杩欎笁椤归厤缃悗鍗冲彲浣跨敤銆�
+
+### 1.2 鎵嬪姩閮ㄧ讲
+
+#### 1.2.1 鍒涘缓鐩綍
+
+瀹夎瀹屽悗锛屼綘闇�瑕佷负杩欎釜椤圭洰鎵句竴涓畨鏀鹃厤缃枃浠剁殑鐩綍锛屼緥濡傛垜浠彲浠ユ柊寤轰竴涓枃浠跺す鍙玚xiaozhi-server`銆�
+
+鍒涘缓濂界洰褰曞悗锛屼綘闇�瑕佸湪`xiaozhi-server`涓嬮潰鍒涘缓`data`鏂囦欢澶瑰拰`models`鏂囦欢澶癸紝`models`涓嬮潰杩樿鍐嶅垱寤篳SenseVoiceSmall`鏂囦欢澶广��
+
+鏈�缁堢洰褰曠粨鏋勫涓嬫墍绀猴細
+
+```
+xiaozhi-server
+ 鈹溾攢 data
+ 鈹溾攢 models
+ 鈹溾攢 SenseVoiceSmall
+```
+
+#### 1.2.2 涓嬭浇璇煶璇嗗埆妯″瀷鏂囦欢
+
+鏈」鐩闊宠瘑鍒ā鍨嬶紝榛樿浣跨敤`SenseVoiceSmall`妯″瀷锛岃繘琛岃闊宠浆鏂囧瓧銆傚洜涓烘ā鍨嬭緝澶э紝闇�瑕佺嫭绔嬩笅杞斤紝涓嬭浇鍚庢妸`model.pt`
+鏂囦欢鏀惧湪`models/SenseVoiceSmall`
+鐩綍涓嬨�備笅闈袱涓笅杞借矾绾夸换閫変竴涓��
+
+- 绾胯矾涓�锛氶樋閲岄瓟鎼笅杞絒SenseVoiceSmall](https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt)
+- 绾胯矾浜岋細鐧惧害缃戠洏涓嬭浇[SenseVoiceSmall](https://pan.baidu.com/share/init?surl=QlgM58FHhYv1tFnUT_A8Sg&pwd=qvna) 鎻愬彇鐮�:
+ `qvna`
+
+
+#### 1.2.3 涓嬭浇閰嶇疆鏂囦欢
+
+浣犻渶瑕佷笅杞戒袱涓厤缃枃浠讹細`docker-compose_all.yaml` 鍜� `config_from_api.yaml`銆傞渶瑕佷粠椤圭洰浠撳簱涓嬭浇杩欎袱涓枃浠躲��
+
+##### 1.2.3.1 涓嬭浇 docker-compose_all.yaml
+
+鐢ㄦ祻瑙堝櫒鎵撳紑[杩欎釜閾炬帴](../main/xiaozhi-server/docker-compose_all.yml)銆�
+
+鍦ㄩ〉闈㈢殑鍙充晶鎵惧埌鍚嶇О涓篳RAW`鎸夐挳锛屽湪`RAW`鎸夐挳鐨勬梺杈癸紝鎵惧埌涓嬭浇鐨勫浘鏍囷紝鐐瑰嚮涓嬭浇鎸夐挳锛屼笅杞絗docker-compose_all.yml`鏂囦欢銆� 鎶婃枃浠朵笅杞藉埌浣犵殑
+`xiaozhi-server`涓��
+
+鎴栬�呯洿鎺ユ墽琛� `wget https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/refs/heads/main/main/xiaozhi-server/docker-compose_all.yml` 涓嬭浇銆�
+
+涓嬭浇瀹屽悗锛屽洖鍒版湰鏁欑▼缁х画寰�涓嬨��
+
+##### 1.2.3.2 涓嬭浇 config_from_api.yaml
+
+鐢ㄦ祻瑙堝櫒鎵撳紑[杩欎釜閾炬帴](../main/xiaozhi-server/config_from_api.yaml)銆�
+
+鍦ㄩ〉闈㈢殑鍙充晶鎵惧埌鍚嶇О涓篳RAW`鎸夐挳锛屽湪`RAW`鎸夐挳鐨勬梺杈癸紝鎵惧埌涓嬭浇鐨勫浘鏍囷紝鐐瑰嚮涓嬭浇鎸夐挳锛屼笅杞絗config_from_api.yaml`鏂囦欢銆� 鎶婃枃浠朵笅杞藉埌浣犵殑
+`xiaozhi-server`涓嬮潰鐨刞data`鏂囦欢澶逛腑锛岀劧鍚庢妸`config_from_api.yaml`鏂囦欢閲嶅懡鍚嶄负`.config.yaml`銆�
+
+鎴栬�呯洿鎺ユ墽琛� `wget https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/refs/heads/main/main/xiaozhi-server/config_from_api.yaml` 涓嬭浇淇濆瓨銆�
+
+涓嬭浇瀹岄厤缃枃浠跺悗锛屾垜浠‘璁や竴涓嬫暣涓猔xiaozhi-server`閲岄潰鐨勬枃浠跺涓嬫墍绀猴細
+
+```
+xiaozhi-server
+ 鈹溾攢 docker-compose_all.yml
+ 鈹溾攢 data
+ 鈹溾攢 .config.yaml
+ 鈹溾攢 models
+ 鈹溾攢 SenseVoiceSmall
+ 鈹溾攢 model.pt
+```
+
+濡傛灉浣犵殑鏂囦欢鐩綍缁撴瀯涔熸槸涓婇潰鐨勶紝灏辩户缁線涓嬨�傚鏋滀笉鏄紝浣犲氨鍐嶄粩缁嗙湅鐪嬫槸涓嶆槸婕忔搷浣滀簡浠�涔堛��
+
+## 2. 澶囦唤鏁版嵁
+
+濡傛灉浣犱箣鍓嶅凡缁忔垚鍔熻繍琛屾櫤鎺у彴锛屽鏋滀笂闈繚瀛樻湁浣犵殑瀵嗛挜淇℃伅锛岃鍏堜粠鏅烘帶鍙颁笂鎷疯礉閲嶈鏁版嵁涓嬫潵銆傚洜涓哄崌绾ц繃绋嬩腑锛屾湁鍙兘浼氳鐩栧師鏉ョ殑鏁版嵁銆�
+
+## 3. 娓呴櫎鍘嗗彶鐗堟湰闀滃儚鍜屽鍣�
+鎺ヤ笅鏉ユ墦寮�鍛戒护琛屽伐鍏凤紝浣跨敤`缁堢`鎴朻鍛戒护琛宍宸ュ叿 杩涘叆鍒颁綘鐨刞xiaozhi-server`锛屾墽琛屼互涓嬪懡浠�
+
+```
+docker compose -f docker-compose_all.yml down
+
+docker stop xiaozhi-esp32-server
+docker rm xiaozhi-esp32-server
+
+docker stop xiaozhi-esp32-server-web
+docker rm xiaozhi-esp32-server-web
+
+docker stop xiaozhi-esp32-server-db
+docker rm xiaozhi-esp32-server-db
+
+docker stop xiaozhi-esp32-server-redis
+docker rm xiaozhi-esp32-server-redis
+
+docker rmi ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:server_latest
+docker rmi ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:web_latest
+```
+
+## 4. 杩愯绋嬪簭
+鎵ц浠ヤ笅鍛戒护鍚姩鏂扮増鏈鍣�
+
+```
+docker compose -f docker-compose_all.yml up -d
+```
+
+鎵ц瀹屽悗锛屽啀鎵ц浠ヤ笅鍛戒护锛屾煡鐪嬫棩蹇椾俊鎭��
+
+```
+docker logs -f xiaozhi-esp32-server-web
+```
+
+褰撲綘鐪嬪埌杈撳嚭鏃ュ織鏃讹紝璇存槑浣犵殑`鏅烘帶鍙癭鍚姩鎴愬姛浜嗐��
+
+```
+2025-xx-xx 22:11:12.445 [main] INFO c.a.d.s.b.a.DruidDataSourceAutoConfigure - Init DruidDataSource
+2025-xx-xx 21:28:53.873 [main] INFO xiaozhi.AdminApplication - Started AdminApplication in 16.057 seconds (process running for 17.941)
+http://localhost:8002/xiaozhi/doc.html
+```
+
+璇锋敞鎰忔鍒讳粎鏄痐鏅烘帶鍙癭鑳借繍琛岋紝濡傛灉8000绔彛`xiaozhi-esp32-server`鎶ラ敊锛屽厛涓嶈鐞嗕細銆�
+
+杩欐椂锛屼綘闇�瑕佷娇鐢ㄦ祻瑙堝櫒锛屾墦寮�`鏅烘帶鍙癭锛岄摼鎺ワ細http://127.0.0.1:8002 锛屾敞鍐岀涓�涓敤鎴枫�傜涓�涓敤鎴峰嵆鏄秴绾х鐞嗗憳锛屼互鍚庣殑鐢ㄦ埛閮芥槸鏅�氱敤鎴枫�傛櫘閫氱敤鎴峰彧鑳界粦瀹氳澶囧拰閰嶇疆鏅鸿兘浣�;瓒呯骇绠$悊鍛樺彲浠ヨ繘琛屾ā鍨嬬鐞嗐�佺敤鎴风鐞嗐�佸弬鏁伴厤缃瓑鍔熻兘銆�
+
+鎺ヤ笅鏉ヨ鍋氫笁浠堕噸瑕佺殑浜嬫儏锛�
+
+### 绗竴浠堕噸瑕佺殑浜嬫儏
+
+浣跨敤瓒呯骇绠$悊鍛樿处鍙凤紝鐧诲綍鏅烘帶鍙帮紝鍦ㄩ《閮ㄨ彍鍗曟壘鍒癭鍙傛暟绠$悊`锛屾壘鍒板垪琛ㄤ腑绗竴鏉℃暟鎹紝鍙傛暟缂栫爜鏄痐server.secret`锛屽鍒跺畠鍒癭鍙傛暟鍊糮銆�
+
+`server.secret`闇�瑕佽鏄庝竴涓嬶紝杩欎釜`鍙傛暟鍊糮寰堥噸瑕侊紝浣滅敤鏄鎴戜滑鐨刞Server`绔繛鎺manager-api`銆俙server.secret`鏄瘡娆′粠闆堕儴缃瞞anager妯″潡鏃讹紝浼氳嚜鍔ㄩ殢鏈虹敓鎴愮殑瀵嗛挜銆�
+
+澶嶅埗`鍙傛暟鍊糮鍚庯紝鎵撳紑`xiaozhi-server`涓嬬殑`data`鐩綍鐨刞.config.yaml`鏂囦欢銆傛鍒讳綘鐨勯厤缃枃浠跺唴瀹瑰簲璇ユ槸杩欐牱鐨勶細
+
+```
+manager-api:
+ url: http://127.0.0.1:8002/xiaozhi
+ secret: 浣犵殑server.secret鍊�
+```
+1銆佹妸浣犲垰鎵嶄粠`鏅烘帶鍙癭澶嶅埗杩囨潵鐨刞server.secret`鐨刞鍙傛暟鍊糮澶嶅埗鍒癭.config.yaml`鏂囦欢閲岀殑`secret`閲屻��
+
+2銆佸洜涓轰綘鏄痙ocker閮ㄧ讲锛屾妸`url`鏀规垚涓嬮潰鐨刞http://xiaozhi-esp32-server-web:8002/xiaozhi`
+
+3銆佸洜涓轰綘鏄痙ocker閮ㄧ讲锛屾妸`url`鏀规垚涓嬮潰鐨刞http://xiaozhi-esp32-server-web:8002/xiaozhi`
+
+4銆佸洜涓轰綘鏄痙ocker閮ㄧ讲锛屾妸`url`鏀规垚涓嬮潰鐨刞http://xiaozhi-esp32-server-web:8002/xiaozhi`
+
+绫讳技杩欐牱鐨勬晥鏋�
+```
+manager-api:
+ url: http://xiaozhi-esp32-server-web:8002/xiaozhi
+ secret: 12345678-xxxx-xxxx-xxxx-123456789000
+```
+
+淇濆瓨濂藉悗锛岀户缁線涓嬪仛绗簩浠堕噸瑕佺殑浜嬫儏
+
+### 绗簩浠堕噸瑕佺殑浜嬫儏
+
+浣跨敤瓒呯骇绠$悊鍛樿处鍙凤紝鐧诲綍鏅烘帶鍙帮紝鍦ㄩ《閮ㄨ彍鍗曟壘鍒癭妯″瀷閰嶇疆`锛岀劧鍚庡湪宸︿晶鏍忕偣鍑籤澶ц瑷�妯″瀷`锛屾壘鍒扮涓�鏉℃暟鎹甡鏅鸿氨AI`锛岀偣鍑籤淇敼`鎸夐挳锛�
+寮瑰嚭淇敼妗嗗悗锛屽皢浣犳敞鍐屽埌鐨刞鏅鸿氨AI`鐨勫瘑閽ュ~鍐欏埌`API瀵嗛挜`涓�傜劧鍚庣偣鍑讳繚瀛樸��
+
+## 5.閲嶅惎xiaozhi-esp32-server
+
+鎺ヤ笅鏉ユ墦寮�鍛戒护琛屽伐鍏凤紝浣跨敤`缁堢`鎴朻鍛戒护琛宍宸ュ叿 杈撳叆
+```
+docker restart xiaozhi-esp32-server
+docker logs -f xiaozhi-esp32-server
+```
+濡傛灉浣犺兘鐪嬪埌锛岀被浼间互涓嬫棩蹇�,鍒欐槸Server鍚姩鎴愬姛鐨勬爣蹇椼��
+
+```
+25-02-23 12:01:09[core.websocket_server] - INFO - Websocket鍦板潃鏄� ws://xxx.xx.xx.xx:8000/xiaozhi/v1/
+25-02-23 12:01:09[core.websocket_server] - INFO - =======涓婇潰鐨勫湴鍧�鏄痺ebsocket鍗忚鍦板潃锛岃鍕跨敤娴忚鍣ㄨ闂�=======
+25-02-23 12:01:09[core.websocket_server] - INFO - 濡傛兂娴嬭瘯websocket璇峰惎鍔╠igital-human妯″潡锛屾墦寮�娴忚鍣ㄤ氦浜掓祴璇�
+25-02-23 12:01:09[core.websocket_server] - INFO - =======================================================
+```
+
+鐢变簬浣犳槸鍏ㄦā鍧楅儴缃诧紝鍥犳浣犳湁涓や釜閲嶈鐨勬帴鍙i渶瑕佸啓鍏ュ埌esp32涓��
+
+OTA鎺ュ彛锛�
+```
+http://浣犲涓绘満灞�鍩熺綉鐨刬p:8002/xiaozhi/ota/
+```
+
+Websocket鎺ュ彛锛�
+```
+ws://浣犲涓绘満鐨刬p:8000/xiaozhi/v1/
+```
+
+### 绗笁浠堕噸瑕佺殑浜嬫儏
+
+浣跨敤瓒呯骇绠$悊鍛樿处鍙凤紝鐧诲綍鏅烘帶鍙帮紝鍦ㄩ《閮ㄨ彍鍗曟壘鍒癭鍙傛暟绠$悊`锛屾壘鍒板弬鏁扮紪鐮佹槸`server.websocket`锛岃緭鍏ヤ綘鐨刞Websocket鎺ュ彛`銆�
+
+浣跨敤瓒呯骇绠$悊鍛樿处鍙凤紝鐧诲綍鏅烘帶鍙帮紝鍦ㄩ《閮ㄨ彍鍗曟壘鍒癭鍙傛暟绠$悊`锛屾壘鍒版暟缂栫爜鏄痐server.ota`锛岃緭鍏ヤ綘鐨刞OTA鎺ュ彛`銆�
+
+鎺ヤ笅鏉ワ紝浣犲氨鍙互寮�濮嬫搷浣滀綘鐨別sp32璁惧浜嗭紝浣犲彲浠鑷缂栬瘧esp32鍥轰欢`涔熷彲浠ラ厤缃娇鐢╜铏惧摜缂栬瘧濂界殑1.6.1浠ヤ笂鐗堟湰鐨勫浐浠禶銆備袱涓换閫変竴涓�
+
+1銆� [缂栬瘧鑷繁鐨別sp32鍥轰欢](firmware-build.md)浜嗐��
+
+2銆� [鍩轰簬铏惧摜缂栬瘧濂界殑鍥轰欢閰嶇疆鑷畾涔夋湇鍔″櫒](firmware-setting.md)浜嗐��
+
+
+# 鏂瑰紡浜岋細鏈湴婧愮爜杩愯鍏ㄦā鍧�
+
+## 1.瀹夎MySQL鏁版嵁搴�
+
+濡傛灉鏈満宸茬粡瀹夎浜哅ySQL锛屽彲浠ョ洿鎺ュ湪鏁版嵁搴撲腑鍒涘缓鍚嶄负`xiaozhi_esp32_server`鐨勬暟鎹簱銆�
+
+```sql
+CREATE DATABASE xiaozhi_esp32_server CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+```
+
+濡傛灉杩樻病鏈塎ySQL锛屼綘鍙互閫氳繃docker瀹夎mysql
+
+```
+docker run --name xiaozhi-esp32-server-db -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 -e MYSQL_DATABASE=xiaozhi_esp32_server -e MYSQL_INITDB_ARGS="--character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci" -e TZ=Asia/Shanghai -d mysql:latest
+```
+
+## 2.瀹夎redis
+
+濡傛灉杩樻病鏈塕edis锛屼綘鍙互閫氳繃docker瀹夎redis
+
+```
+docker run --name xiaozhi-esp32-server-redis -d -p 6379:6379 redis
+```
+
+## 3.杩愯manager-api绋嬪簭
+
+3.1 瀹夎JDK21锛岃缃甁DK鐜鍙橀噺
+
+3.2 瀹夎Maven锛岃缃甅aven鐜鍙橀噺
+
+3.3 浣跨敤Vscode缂栫▼宸ュ叿锛屽畨瑁呭ソJava鐜鐩稿叧鎻掍欢
+
+3.4 浣跨敤Vscode缂栫▼宸ュ叿鍔犺浇manager-api妯″潡
+
+鍦╜src/main/resources/application-dev.yml`涓厤缃暟鎹簱杩炴帴淇℃伅
+
+```
+spring:
+ datasource:
+ username: root
+ password: 123456
+```
+鍦╜src/main/resources/application-dev.yml`涓厤缃甊edis杩炴帴淇℃伅
+```
+spring:
+ data:
+ redis:
+ host: localhost
+ port: 6379
+ password:
+ database: 0
+```
+
+3.5 杩愯涓荤▼搴�
+
+鏈」鐩负SpringBoot椤圭洰锛屽惎鍔ㄦ柟寮忎负锛�
+鎵撳紑`Application.java`杩愯`Main`鏂规硶鍚姩
+
+```
+璺緞鍦板潃锛�
+src/main/java/xiaozhi/AdminApplication.java
+```
+
+褰撲綘鐪嬪埌杈撳嚭鏃ュ織鏃讹紝璇存槑浣犵殑`manager-api`鍚姩鎴愬姛浜嗐��
+
+```
+2025-xx-xx 22:11:12.445 [main] INFO c.a.d.s.b.a.DruidDataSourceAutoConfigure - Init DruidDataSource
+2025-xx-xx 21:28:53.873 [main] INFO xiaozhi.AdminApplication - Started AdminApplication in 16.057 seconds (process running for 17.941)
+http://localhost:8002/xiaozhi/doc.html
+```
+
+## 4.杩愯manager-web绋嬪簭
+
+4.1 瀹夎nodejs
+
+4.2 浣跨敤Vscode缂栫▼宸ュ叿鍔犺浇manager-web妯″潡
+
+缁堢鍛戒护杩涘叆manager-web鐩綍涓�
+
+```
+npm install
+```
+鐒跺悗鍚姩
+```
+npm run serve
+```
+
+璇锋敞鎰忥紝濡傛灉浣犵殑manager-api鐨勬帴鍙d笉鍦╜http://localhost:8002`锛岃鍦ㄥ紑鍙戞椂锛屼慨鏀�
+`main/manager-web/.env.development`涓殑璺緞
+
+杩愯鎴愬姛鍚庯紝浣犻渶瑕佷娇鐢ㄦ祻瑙堝櫒锛屾墦寮�`鏅烘帶鍙癭锛岄摼鎺ワ細http://127.0.0.1:8001 锛屾敞鍐岀涓�涓敤鎴枫�傜涓�涓敤鎴峰嵆鏄秴绾х鐞嗗憳锛屼互鍚庣殑鐢ㄦ埛閮芥槸鏅�氱敤鎴枫�傛櫘閫氱敤鎴峰彧鑳界粦瀹氳澶囧拰閰嶇疆鏅鸿兘浣�;瓒呯骇绠$悊鍛樺彲浠ヨ繘琛屾ā鍨嬬鐞嗐�佺敤鎴风鐞嗐�佸弬鏁伴厤缃瓑鍔熻兘銆�
+
+
+閲嶈锛氭敞鍐屾垚鍔熷悗锛屼娇鐢ㄨ秴绾х鐞嗗憳璐﹀彿锛岀櫥褰曟櫤鎺у彴锛屽湪椤堕儴鑿滃崟鎵惧埌`妯″瀷閰嶇疆`锛岀劧鍚庡湪宸︿晶鏍忕偣鍑籤澶ц瑷�妯″瀷`锛屾壘鍒扮涓�鏉℃暟鎹甡鏅鸿氨AI`锛岀偣鍑籤淇敼`鎸夐挳锛�
+寮瑰嚭淇敼妗嗗悗锛屽皢浣犳敞鍐屽埌鐨刞鏅鸿氨AI`鐨勫瘑閽ュ~鍐欏埌`API瀵嗛挜`涓�傜劧鍚庣偣鍑讳繚瀛樸��
+
+閲嶈锛氭敞鍐屾垚鍔熷悗锛屼娇鐢ㄨ秴绾х鐞嗗憳璐﹀彿锛岀櫥褰曟櫤鎺у彴锛屽湪椤堕儴鑿滃崟鎵惧埌`妯″瀷閰嶇疆`锛岀劧鍚庡湪宸︿晶鏍忕偣鍑籤澶ц瑷�妯″瀷`锛屾壘鍒扮涓�鏉℃暟鎹甡鏅鸿氨AI`锛岀偣鍑籤淇敼`鎸夐挳锛�
+寮瑰嚭淇敼妗嗗悗锛屽皢浣犳敞鍐屽埌鐨刞鏅鸿氨AI`鐨勫瘑閽ュ~鍐欏埌`API瀵嗛挜`涓�傜劧鍚庣偣鍑讳繚瀛樸��
+
+閲嶈锛氭敞鍐屾垚鍔熷悗锛屼娇鐢ㄨ秴绾х鐞嗗憳璐﹀彿锛岀櫥褰曟櫤鎺у彴锛屽湪椤堕儴鑿滃崟鎵惧埌`妯″瀷閰嶇疆`锛岀劧鍚庡湪宸︿晶鏍忕偣鍑籤澶ц瑷�妯″瀷`锛屾壘鍒扮涓�鏉℃暟鎹甡鏅鸿氨AI`锛岀偣鍑籤淇敼`鎸夐挳锛�
+寮瑰嚭淇敼妗嗗悗锛屽皢浣犳敞鍐屽埌鐨刞鏅鸿氨AI`鐨勫瘑閽ュ~鍐欏埌`API瀵嗛挜`涓�傜劧鍚庣偣鍑讳繚瀛樸��
+
+## 5.瀹夎Python鐜
+
+鏈」鐩娇鐢╜conda`绠$悊渚濊禆鐜銆傚鏋滀笉鏂逛究瀹夎`conda`锛岄渶瑕佹牴鎹疄闄呯殑鎿嶄綔绯荤粺瀹夎濂絗libopus`鍜宍ffmpeg`銆�
+濡傛灉纭畾浣跨敤`conda`锛屽垯瀹夎濂藉悗锛屽紑濮嬫墽琛屼互涓嬪懡浠ゃ��
+
+閲嶈鎻愮ず锛亀indows 鐢ㄦ埛锛屽彲浠ラ�氳繃瀹夎`Anaconda`鏉ョ鐞嗙幆澧冦�傚畨瑁呭ソ`Anaconda`鍚庯紝鍦╜寮�濮媊閭i噷鎼滅储`anaconda`鐩稿叧鐨勫叧閿瘝锛�
+鎵惧埌`Anaconda Prpmpt`锛屼娇鐢ㄧ鐞嗗憳韬唤杩愯瀹冦�傚涓嬪浘銆�
+
+
+
+杩愯涔嬪悗锛屽鏋滀綘鑳界湅鍒板懡浠よ绐楀彛鍓嶉潰鏈変竴涓�(base)瀛楁牱锛岃鏄庝綘鎴愬姛杩涘叆浜哷conda`鐜銆傞偅涔堜綘灏卞彲浠ユ墽琛屼互涓嬪懡浠や簡銆�
+
+
+
+```
+conda remove -n xiaozhi-esp32-server --all -y
+conda create -n xiaozhi-esp32-server python=3.10 -y
+conda activate xiaozhi-esp32-server
+
+# 娣诲姞娓呭崕婧愰�氶亾
+conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
+conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free
+conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
+
+conda install libopus -y
+conda install ffmpeg -y
+
+# 鍦� Linux 鐜涓嬭繘琛岄儴缃叉椂,濡傚嚭鐜扮被浼肩己澶� libiconv.so.2 鍔ㄦ�佸簱鐨勬姤閿� 璇烽�氳繃浠ヤ笅鍛戒护杩涜瀹夎
+conda install libiconv -y
+```
+
+璇锋敞鎰忥紝浠ヤ笂鍛戒护锛屼笉鏄竴鑲¤剳鎵ц灏辨垚鍔熺殑锛屼綘闇�瑕佷竴姝ユ鎵ц锛屾瘡涓�姝ユ墽琛屽畬鍚庯紝閮芥鏌ヤ竴涓嬭緭鍑虹殑鏃ュ織锛屾煡鐪嬫槸鍚︽垚鍔熴��
+
+## 6.瀹夎鏈」鐩緷璧�
+
+浣犲厛瑕佷笅杞芥湰椤圭洰婧愮爜锛屾簮鐮佸彲浠ラ�氳繃`git clone`鍛戒护涓嬭浇锛屽鏋滀綘涓嶇啛鎮塦git clone`鍛戒护銆�
+
+浣犲彲浠ョ敤娴忚鍣ㄦ墦寮�杩欎釜鍦板潃`https://github.com/xinnan-tech/xiaozhi-esp32-server.git`
+
+鎵撳紑瀹岋紝鎵惧埌椤甸潰涓竴涓豢鑹茬殑鎸夐挳锛屽啓鐫�`Code`鐨勬寜閽紝鐐瑰紑瀹冿紝鐒跺悗浣犲氨鐪嬪埌`Download ZIP`鐨勬寜閽��
+
+鐐瑰嚮瀹冿紝涓嬭浇鏈」鐩簮鐮佸帇缂╁寘銆備笅杞藉埌浣犵數鑴戝悗锛岃В鍘嬪畠锛屾鏃跺畠鐨勫悕瀛楀彲鑳藉彨`xiaozhi-esp32-server-main`
+浣犻渶瑕佹妸瀹冮噸鍛藉悕鎴恅xiaozhi-esp32-server`锛屽湪杩欎釜鏂囦欢閲岋紝杩涘叆鍒癭main`鏂囦欢澶癸紝鍐嶈繘鍏ュ埌`xiaozhi-server`锛屽ソ浜嗚璁颁綇杩欎釜鐩綍`xiaozhi-server`銆�
+
+```
+# 缁х画浣跨敤conda鐜
+conda activate xiaozhi-esp32-server
+# 杩涘叆鍒颁綘鐨勯」鐩牴鐩綍锛屽啀杩涘叆main/xiaozhi-server
+cd main/xiaozhi-server
+pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
+pip install -r requirements.txt
+```
+
+### 7.涓嬭浇璇煶璇嗗埆妯″瀷鏂囦欢
+
+鏈」鐩闊宠瘑鍒ā鍨嬶紝榛樿浣跨敤`SenseVoiceSmall`妯″瀷锛岃繘琛岃闊宠浆鏂囧瓧銆傚洜涓烘ā鍨嬭緝澶э紝闇�瑕佺嫭绔嬩笅杞斤紝涓嬭浇鍚庢妸`model.pt`
+鏂囦欢鏀惧湪`models/SenseVoiceSmall`
+鐩綍涓嬨�備笅闈袱涓笅杞借矾绾夸换閫変竴涓��
+
+- 绾胯矾涓�锛氶樋閲岄瓟鎼笅杞絒SenseVoiceSmall](https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt)
+- 绾胯矾浜岋細鐧惧害缃戠洏涓嬭浇[SenseVoiceSmall](https://pan.baidu.com/share/init?surl=QlgM58FHhYv1tFnUT_A8Sg&pwd=qvna) 鎻愬彇鐮�:
+ `qvna`
+
+## 8.閰嶇疆椤圭洰鏂囦欢
+
+浣跨敤瓒呯骇绠$悊鍛樿处鍙凤紝鐧诲綍鏅烘帶鍙� 锛屽湪椤堕儴鑿滃崟鎵惧埌`鍙傛暟绠$悊`锛屾壘鍒板垪琛ㄤ腑绗竴鏉℃暟鎹紝鍙傛暟缂栫爜鏄痐server.secret`锛屽鍒跺畠鍒癭鍙傛暟鍊糮銆�
+
+`server.secret`闇�瑕佽鏄庝竴涓嬶紝杩欎釜`鍙傛暟鍊糮寰堥噸瑕侊紝浣滅敤鏄鎴戜滑鐨刞Server`绔繛鎺manager-api`銆俙server.secret`鏄瘡娆′粠闆堕儴缃瞞anager妯″潡鏃讹紝浼氳嚜鍔ㄩ殢鏈虹敓鎴愮殑瀵嗛挜銆�
+
+濡傛灉浣犵殑`xiaozhi-server`鐩綍娌℃湁`data`锛屼綘闇�瑕佸垱寤篳data`鐩綍銆�
+濡傛灉浣犵殑`data`涓嬮潰娌℃湁`.config.yaml`鏂囦欢锛屼綘鍙互鎶奰xiaozhi-server`鐩綍涓嬬殑`config_from_api.yaml`鏂囦欢澶嶅埗鍒癭data`锛屽苟閲嶅懡鍚嶄负`.config.yaml`
+
+澶嶅埗`鍙傛暟鍊糮鍚庯紝鎵撳紑`xiaozhi-server`涓嬬殑`data`鐩綍鐨刞.config.yaml`鏂囦欢銆傛鍒讳綘鐨勯厤缃枃浠跺唴瀹瑰簲璇ユ槸杩欐牱鐨勶細
+
+```
+manager-api:
+ url: http://127.0.0.1:8002/xiaozhi
+ secret: 浣犵殑server.secret鍊�
+```
+
+鎶婁綘鍒氭墠浠巂鏅烘帶鍙癭澶嶅埗杩囨潵鐨刞server.secret`鐨刞鍙傛暟鍊糮澶嶅埗鍒癭.config.yaml`鏂囦欢閲岀殑`secret`閲屻��
+
+绫讳技杩欐牱鐨勬晥鏋�
+```
+manager-api:
+ url: http://127.0.0.1:8002/xiaozhi
+ secret: 12345678-xxxx-xxxx-xxxx-123456789000
+```
+
+## 9.杩愯椤圭洰
+
+```
+# 纭繚鍦▁iaozhi-server鐩綍涓嬫墽琛�
+conda activate xiaozhi-esp32-server
+python app.py
+```
+
+濡傛灉浣犺兘鐪嬪埌锛岀被浼间互涓嬫棩蹇�,鍒欐槸鏈」鐩湇鍔″惎鍔ㄦ垚鍔熺殑鏍囧織銆�
+
+```
+25-02-23 12:01:09[core.websocket_server] - INFO - Server is running at ws://xxx.xx.xx.xx:8000/xiaozhi/v1/
+25-02-23 12:01:09[core.websocket_server] - INFO - =======涓婇潰鐨勫湴鍧�鏄痺ebsocket鍗忚鍦板潃锛岃鍕跨敤娴忚鍣ㄨ闂�=======
+25-02-23 12:01:09[core.websocket_server] - INFO - 濡傛兂娴嬭瘯websocket璇峰惎鍔╠igital-human妯″潡锛屾墦寮�娴忚鍣ㄤ氦浜掓祴璇�
+25-02-23 12:01:09[core.websocket_server] - INFO - =======================================================
+```
+
+鐢变簬浣犳槸鍏ㄦā鍧楅儴缃诧紝鍥犳浣犳湁涓や釜閲嶈鐨勬帴鍙c��
+
+OTA鎺ュ彛锛�
+```
+http://浣犵數鑴戝眬鍩熺綉鐨刬p:8002/xiaozhi/ota/
+```
+
+Websocket鎺ュ彛锛�
+```
+ws://浣犵數鑴戝眬鍩熺綉鐨刬p:8000/xiaozhi/v1/
+```
+
+璇蜂綘鍔″繀鎶婁互涓婁袱涓帴鍙e湴鍧�鍐欏叆鍒版櫤鎺у彴涓細浠栦滑灏嗕細褰卞搷websocket鍦板潃鍙戞斁鍜岃嚜鍔ㄥ崌绾у姛鑳姐��
+
+1銆佷娇鐢ㄨ秴绾х鐞嗗憳璐﹀彿锛岀櫥褰曟櫤鎺у彴锛屽湪椤堕儴鑿滃崟鎵惧埌`鍙傛暟绠$悊`锛屾壘鍒板弬鏁扮紪鐮佹槸`server.websocket`锛岃緭鍏ヤ綘鐨刞Websocket鎺ュ彛`銆�
+
+2銆佷娇鐢ㄨ秴绾х鐞嗗憳璐﹀彿锛岀櫥褰曟櫤鎺у彴锛屽湪椤堕儴鑿滃崟鎵惧埌`鍙傛暟绠$悊`锛屾壘鍒版暟缂栫爜鏄痐server.ota`锛岃緭鍏ヤ綘鐨刞OTA鎺ュ彛`銆�
+
+
+鎺ヤ笅鏉ワ紝浣犲氨鍙互寮�濮嬫搷浣滀綘鐨別sp32璁惧浜嗭紝浣犲彲浠鑷缂栬瘧esp32鍥轰欢`涔熷彲浠ラ厤缃娇鐢╜铏惧摜缂栬瘧濂界殑1.6.1浠ヤ笂鐗堟湰鐨勫浐浠禶銆備袱涓换閫変竴涓�
+
+1銆� [缂栬瘧鑷繁鐨別sp32鍥轰欢](firmware-build.md)浜嗐��
+
+2銆� [鍩轰簬铏惧摜缂栬瘧濂界殑鍥轰欢閰嶇疆鑷畾涔夋湇鍔″櫒](firmware-setting.md)浜嗐��
+
+# 甯歌闂
+浠ヤ笅鏄竴浜涘父瑙侀棶棰橈紝渚涘弬鑰冿細
+
+1銆乕涓轰粈涔堟垜璇寸殑璇濓紝灏忔櫤璇嗗埆鍑烘潵寰堝闊╂枃銆佹棩鏂囥�佽嫳鏂嘳(./FAQ.md)<br/>
+2銆乕涓轰粈涔堜細鍑虹幇鈥淭TS 浠诲姟鍑洪敊 鏂囦欢涓嶅瓨鍦ㄢ�濓紵](./FAQ.md)<br/>
+3銆乕TTS 缁忓父澶辫触锛岀粡甯歌秴鏃禲(./FAQ.md)<br/>
+4銆乕浣跨敤Wifi鑳借繛鎺ヨ嚜寤烘湇鍔″櫒锛屼絾鏄�4G妯″紡鍗存帴涓嶄笂](./FAQ.md)<br/>
+5銆乕濡備綍鎻愰珮灏忔櫤瀵硅瘽鍝嶅簲閫熷害锛焆(./FAQ.md)<br/>
+6銆乕鎴戣璇濆緢鎱紝鍋滈】鏃跺皬鏅鸿�佹槸鎶㈣瘽](./FAQ.md)<br/>
+## 閮ㄧ讲鐩稿叧鏁欑▼
+1銆乕濡備綍鑷姩鎷夊彇鏈」鐩渶鏂颁唬鐮佽嚜鍔ㄧ紪璇戝拰鍚姩](./dev-ops-integration.md)<br/>
+2銆乕濡備綍閮ㄧ讲MQTT缃戝叧寮�鍚疢QTT+UDP鍗忚](./mqtt-gateway-integration.md)<br/>
+3銆乕濡備綍涓嶯ginx闆嗘垚](https://github.com/xinnan-tech/xiaozhi-esp32-server/issues/791)<br/>
+## 鎷撳睍鐩稿叧鏁欑▼
+1銆乕濡備綍寮�鍚墜鏈哄彿鐮佹敞鍐屾櫤鎺у彴](./ali-sms-integration.md)<br/>
+2銆乕濡備綍闆嗘垚HomeAssistant瀹炵幇鏅鸿兘瀹跺眳鎺у埗](./homeassistant-integration.md)<br/>
+3銆乕濡備綍寮�鍚瑙夋ā鍨嬪疄鐜版媿鐓ц瘑鐗(./mcp-vision-integration.md)<br/>
+4銆乕濡備綍閮ㄧ讲MCP鎺ュ叆鐐筣(./mcp-endpoint-enable.md)<br/>
+5銆乕濡備綍鎺ュ叆MCP鎺ュ叆鐐筣(./mcp-endpoint-integration.md)<br/>
+6銆乕濡備綍寮�鍚0绾硅瘑鍒玗(./voiceprint-integration.md)<br/>
+7銆乕鏂伴椈鎻掍欢婧愰厤缃寚鍗梋(./newsnow_plugin_config.md)<br/>
+8銆乕澶╂皵鎻掍欢浣跨敤鎸囧崡](./weather-integration.md)<br/>
+## 璇煶鍏嬮殕銆佹湰鍦拌闊抽儴缃茬浉鍏虫暀绋�
+1銆乕濡備綍鍦ㄦ櫤鎺у彴鍏嬮殕闊宠壊](./huoshan-streamTTS-voice-cloning.md)<br/>
+2銆乕濡備綍閮ㄧ讲闆嗘垚index-tts鏈湴璇煶](./index-stream-integration.md)<br/>
+3銆乕濡備綍閮ㄧ讲闆嗘垚fish-speech鏈湴璇煶](./fish-speech-integration.md)<br/>
+4銆乕濡備綍閮ㄧ讲闆嗘垚PaddleSpeech鏈湴璇煶](./paddlespeech-deploy.md)<br/>
+## 鎬ц兘娴嬭瘯鏁欑▼
+1銆乕鍚勭粍浠堕�熷害娴嬭瘯鎸囧崡](./performance_tester.md)<br/>
+2銆乕瀹氭湡鍏紑娴嬭瘯缁撴灉](https://github.com/xinnan-tech/xiaozhi-performance-research)<br/>
diff --git a/xiaozhi-esp32-server/docs/FAQ.md b/xiaozhi-esp32-server/docs/FAQ.md
new file mode 100644
index 0000000..d5785ef
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/FAQ.md
@@ -0,0 +1,107 @@
+# 甯歌闂 鉂�
+
+### 1銆佷负浠�涔堟垜璇寸殑璇濓紝灏忔櫤璇嗗埆鍑烘潵寰堝闊╂枃銆佹棩鏂囥�佽嫳鏂囷紵馃嚢馃嚪
+
+寤鸿锛氭鏌ヤ竴涓媊models/SenseVoiceSmall`鏄惁宸茬粡鏈塦model.pt`
+鏂囦欢锛屽鏋滄病鏈夊氨瑕佷笅杞斤紝鏌ョ湅杩欓噷[涓嬭浇璇煶璇嗗埆妯″瀷鏂囦欢](Deployment.md#妯″瀷鏂囦欢)
+
+### 2銆佷负浠�涔堜細鍑虹幇"TTS 浠诲姟鍑洪敊 鏂囦欢涓嶅瓨鍦�"锛燄煋�
+
+寤鸿锛氭鏌ヤ竴涓嬫槸鍚︽纭娇鐢╜conda` 瀹夎浜哷libopus`鍜宍ffmpeg`搴撱��
+
+濡傛灉娌℃湁瀹夎锛屽氨瀹夎
+
+```
+conda install conda-forge::libopus
+conda install conda-forge::ffmpeg
+```
+
+### 3銆乀TS 缁忓父澶辫触锛岀粡甯歌秴鏃� 鈴�
+
+寤鸿锛氬鏋� `EdgeTTS` 缁忓父澶辫触锛岃鍏堟鏌ユ槸鍚︿娇鐢ㄤ簡浠g悊锛堟瀛愶級銆傚鏋滀娇鐢ㄤ簡锛岃灏濊瘯鍏抽棴浠g悊鍚庡啀璇曪紱
+濡傛灉鐢ㄧ殑鏄伀灞卞紩鎿庣殑璞嗗寘 TTS锛岀粡甯稿け璐ユ椂寤鸿浣跨敤浠樿垂鐗堟湰锛屽洜涓烘祴璇曠増鏈粎鏀寔 2 涓苟鍙戙��
+
+### 4銆佷娇鐢╓ifi鑳借繛鎺ヨ嚜寤烘湇鍔″櫒锛屼絾鏄�4G妯″紡鍗存帴涓嶄笂 馃攼
+
+鍘熷洜锛氳櫨鍝ョ殑鍥轰欢锛�4G妯″紡闇�瑕佷娇鐢ㄥ畨鍏ㄨ繛鎺ャ��
+
+瑙e喅鏂规硶锛氱洰鍓嶆湁涓ょ鏂规硶鍙互瑙e喅銆備换閫変竴绉嶏細
+
+1銆佹敼浠g爜銆傚弬鑰冭繖涓棰戣В鍐� https://www.bilibili.com/video/BV18MfTYoE85
+
+2銆佷娇鐢╪ginx閰嶇疆ssl璇佷功銆傚弬鑰冩暀绋� https://icnt94i5ctj4.feishu.cn/docx/GnYOdMNJOoRCljx1ctecsj9cnRe
+
+### 5銆佸浣曟彁楂樺皬鏅哄璇濆搷搴旈�熷害锛� 鈿�
+
+鏈」鐩粯璁ら厤缃负浣庢垚鏈柟妗堬紝寤鸿鍒濆鑰呭厛浣跨敤榛樿鍏嶈垂妯″瀷锛岃В鍐�"璺戝緱鍔�"鐨勯棶棰橈紝鍐嶄紭鍖�"璺戝緱蹇�"銆�
+濡傞渶鎻愬崌鍝嶅簲閫熷害锛屽彲灏濊瘯鏇存崲鍚勭粍浠躲�傝嚜`0.5.2`鐗堟湰璧凤紝椤圭洰鏀寔娴佸紡閰嶇疆锛岀浉姣旀棭鏈熺増鏈紝鍝嶅簲閫熷害鎻愬崌绾2.5绉抈锛屾樉钁楁敼鍠勭敤鎴蜂綋楠屻��
+
+| 妯″潡鍚嶇О | 鍏ラ棬鍏ㄥ厤璐硅缃� | 娴佸紡閰嶇疆 |
+|:---:|:---:|:---:|
+| ASR(璇煶璇嗗埆) | FunASR(鏈湴) | 馃憤XunfeiStreamASR(璁娴佸紡) |
+| LLM(澶фā鍨�) | glm-4-flash(鏅鸿氨) | 馃憤qwen-flash(闃块噷鐧剧偧) |
+| VLLM(瑙嗚澶фā鍨�) | glm-4v-flash(鏅鸿氨) | 馃憤qwen3.5-flash(闃块噷鐧剧偧) |
+| TTS(璇煶鍚堟垚) | EdgeTTS(寰蒋) | 馃憤HuoshanDoubleStreamTTS(鐏北娴佸紡) |
+| Intent(鎰忓浘璇嗗埆) | function_call(鍑芥暟璋冪敤) | function_call(鍑芥暟璋冪敤) |
+| Memory(璁板繂鍔熻兘) | mem_local_short(鏈湴鐭湡璁板繂锛� | mem_local_short锛堟湰鍦扮煭鏈熻蹇嗭級 |
+
+濡傛灉鎮ㄥ叧蹇冨悇缁勪欢鐨勮�楁椂锛岃鏌ラ槄[灏忔櫤鍚勭粍浠舵�ц兘娴嬭瘯鎶ュ憡](https://github.com/xinnan-tech/xiaozhi-performance-research)锛屽彲鎸夋姤鍛婁腑鐨勬祴璇曟柟娉曞湪鎮ㄧ殑鐜涓疄闄呮祴璇曘��
+
+### 6銆佹垜璇磋瘽寰堟參锛屽仠椤挎椂灏忔櫤鑰佹槸鎶㈣瘽 馃棧锔�
+
+寤鸿锛氬湪閰嶇疆鏂囦欢涓壘鍒板涓嬮儴鍒嗭紝灏� `min_silence_duration_ms` 鐨勫�艰皟澶э紙渚嬪鏀逛负 `1000`锛夛細
+
+```yaml
+VAD:
+ SileroVAD:
+ threshold: 0.5
+ model_dir: models/snakers4_silero-vad
+ min_silence_duration_ms: 700 # 濡傛灉璇磋瘽鍋滈】杈冮暱锛屽彲灏嗘鍊艰皟澶�
+```
+
+### 7銆侀儴缃茬浉鍏虫暀绋�
+1銆乕濡備綍杩涜鏈�绠�鍖栭儴缃瞉(./Deployment.md)<br/>
+2銆乕濡備綍杩涜鍏ㄦā鍧楅儴缃瞉(./Deployment_all.md)<br/>
+3銆乕濡備綍閮ㄧ讲MQTT缃戝叧寮�鍚疢QTT+UDP鍗忚](./mqtt-gateway-integration.md)<br/>
+4銆乕濡備綍鑷姩鎷夊彇鏈」鐩渶鏂颁唬鐮佽嚜鍔ㄧ紪璇戝拰鍚姩](./dev-ops-integration.md)<br/>
+5銆乕濡備綍涓嶯ginx闆嗘垚](https://github.com/xinnan-tech/xiaozhi-esp32-server/issues/791)<br/>
+6銆乕淇敼浠g爜鍚庢�庝箞缂栬瘧鑷繁鐨凞ocker闀滃儚](./docker-build.md)<br/>
+
+### 8銆佺紪璇戝浐浠剁浉鍏虫暀绋�
+1銆乕濡備綍鑷繁缂栬瘧灏忔櫤鍥轰欢](./firmware-build.md)<br/>
+2銆乕濡備綍鍩轰簬铏惧摜缂栬瘧濂界殑鍥轰欢淇敼OTA鍦板潃](./firmware-setting.md)<br/>
+3銆乕鍗曟ā鍧楅儴缃插浣曢厤缃浐浠禣TA鑷姩鍗囩骇](./ota-upgrade-guide.md)<br/>
+
+### 9銆佹嫇灞曠浉鍏虫暀绋�
+1銆乕濡備綍寮�鍚墜鏈哄彿鐮佹敞鍐屾櫤鎺у彴](./ali-sms-integration.md)<br/>
+2銆乕濡備綍闆嗘垚HomeAssistant瀹炵幇鏅鸿兘瀹跺眳鎺у埗](./homeassistant-integration.md)<br/>
+3銆乕濡備綍寮�鍚瑙夋ā鍨嬪疄鐜版媿鐓ц瘑鐗(./mcp-vision-integration.md)<br/>
+4銆乕濡備綍閮ㄧ讲MCP鎺ュ叆鐐筣(./mcp-endpoint-enable.md)<br/>
+5銆乕濡備綍鎺ュ叆MCP鎺ュ叆鐐筣(./mcp-endpoint-integration.md)<br/>
+6銆乕MCP鏂规硶濡備綍鑾峰彇璁惧淇℃伅](./mcp-get-device-info.md)<br/>
+7銆乕濡備綍寮�鍚0绾硅瘑鍒玗(./voiceprint-integration.md)<br/>
+8銆乕鏂伴椈鎻掍欢婧愰厤缃寚鍗梋(./newsnow_plugin_config.md)<br/>
+9銆乕鐭ヨ瘑搴搑agflow闆嗘垚鎸囧崡](./ragflow-integration.md)<br/>
+10銆乕濡備綍閮ㄧ讲涓婁笅鏂囨簮](./context-provider-integration.md)<br/>
+11銆乕濡備綍闆嗘垚PowerMem鏅鸿兘璁板繂](./powermem-integration.md)<br/>
+12銆乕濡備綍閰嶇疆澶╂皵鎻掍欢鏌ヨ澶╂皵](./weather-integration.md)<br/>
+13銆乕濡備綍寮�鍚澶囧懠鍙彃浠禲(./device-call-guide.md)<br/>
+14銆乕濡備綍寮�鍚仈缃戞悳绱㈠姛鑳絔(./web-search-integration.md)<br/>
+
+### 10銆佹暟瀛椾汉鐩稿叧鏁欑▼
+1銆乕鏁板瓧浜篸igital-human鍚姩鏂规硶](./digital-human-wakeword.md)<br/>
+2銆乕濡備綍鍦∟100杩蜂綘涓绘満涓婇儴缃叉暟瀛椾汉digital-human](./all-in-one-digital-human-setup.md)<br/>
+
+### 11銆佽闊冲厠闅嗐�佹湰鍦拌闊抽儴缃茬浉鍏虫暀绋�
+1銆乕濡備綍鍦ㄦ櫤鎺у彴鍏嬮殕闊宠壊](./huoshan-streamTTS-voice-cloning.md)<br/>
+2銆乕濡備綍閮ㄧ讲闆嗘垚index-tts鏈湴璇煶](./index-stream-integration.md)<br/>
+3銆乕濡備綍閮ㄧ讲闆嗘垚fish-speech鏈湴璇煶](./fish-speech-integration.md)<br/>
+4銆乕濡備綍閮ㄧ讲闆嗘垚PaddleSpeech鏈湴璇煶](./paddlespeech-deploy.md)<br/>
+
+### 12銆佹�ц兘娴嬭瘯鏁欑▼
+1銆乕鍚勭粍浠堕�熷害娴嬭瘯鎸囧崡](./performance_tester.md)<br/>
+2銆乕瀹氭湡鍏紑娴嬭瘯缁撴灉](https://github.com/xinnan-tech/xiaozhi-performance-research)<br/>
+
+### 13銆佹洿澶氶棶棰橈紝鍙仈绯绘垜浠弽棣� 馃挰
+
+鍙互鍦╗issues](https://github.com/xinnan-tech/xiaozhi-esp32-server/issues)鎻愪氦鎮ㄧ殑闂銆�
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/docs/ali-sms-integration.md b/xiaozhi-esp32-server/docs/ali-sms-integration.md
new file mode 100644
index 0000000..1be2778
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/ali-sms-integration.md
@@ -0,0 +1,44 @@
+# 闃块噷浜戠煭淇¢泦鎴愭寚鍗�
+
+鐧诲綍闃块噷浜戞帶鍒跺彴锛岃繘鍏モ�滅煭淇℃湇鍔♀�濋〉闈細https://dysms.console.aliyun.com/overview
+
+## 绗竴姝� 娣诲姞绛惧悕
+
+
+
+浠ヤ笂姝ラ锛屼細寰楀埌绛惧悕锛岃鎶婂畠鍐欏叆鍒版櫤鎺у彴鍙傛暟锛宍aliyun.sms.sign_name`
+
+## 绗簩姝� 娣诲姞妯$増
+
+
+浠ヤ笂姝ラ锛屼細寰楀埌妯$増code锛岃鎶婂畠鍐欏叆鍒版櫤鎺у彴鍙傛暟锛宍aliyun.sms.sms_code_template_code`
+
+娉ㄦ剰锛岀鍚嶈绛�7涓伐浣滄棩锛岀瓑杩愯惀鍟嗘姤澶囨垚鍔熷悗鎵嶈兘鍙戦�佹垚鍔熴��
+
+娉ㄦ剰锛岀鍚嶈绛�7涓伐浣滄棩锛岀瓑杩愯惀鍟嗘姤澶囨垚鍔熷悗鎵嶈兘鍙戦�佹垚鍔熴��
+
+娉ㄦ剰锛岀鍚嶈绛�7涓伐浣滄棩锛岀瓑杩愯惀鍟嗘姤澶囨垚鍔熷悗鎵嶈兘鍙戦�佹垚鍔熴��
+
+鍙互绛夋姤澶囨垚鍔熷悗锛屽啀缁х画寰�涓嬫搷浣溿��
+
+## 绗笁姝� 鍒涘缓鐭俊璐︽埛鍜屽紑閫氭潈闄�
+
+鐧诲綍闃块噷浜戞帶鍒跺彴锛岃繘鍏モ�滆闂帶鍒垛�濋〉闈細https://ram.console.aliyun.com/overview?activeTab=overview
+
+
+
+
+
+
+
+浠ヤ笂姝ラ锛屼細寰楀埌access_key_id鍜宎ccess_key_secret锛岃鎶婂畠鍐欏叆鍒版櫤鎺у彴鍙傛暟锛宍aliyun.sms.access_key_id`銆乣aliyun.sms.access_key_secret`
+## 绗洓姝� 鍚姩鎵嬫満娉ㄥ唽鍔熻兘
+
+1銆佹甯告潵璇达紝浠ヤ笂淇℃伅閮藉~瀹屽悗锛屼細鏈夎繖涓晥鏋滐紝濡傛灉娌℃湁锛屽彲鑳界己灏戜簡鏌愪釜姝ラ
+
+
+
+2銆佸紑鍚厑璁搁潪绠$悊鍛樼敤鎴峰彲娉ㄥ唽锛屽皢鍙傛暟`server.allow_user_register`璁剧疆鎴恅true`
+
+3銆佸紑鍚墜鏈烘敞鍐屽姛鑳斤紝灏嗗弬鏁癭server.enable_mobile_register`璁剧疆鎴恅true`
+
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/docs/all-in-one-digital-human-setup.md b/xiaozhi-esp32-server/docs/all-in-one-digital-human-setup.md
new file mode 100644
index 0000000..ebee66d
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/all-in-one-digital-human-setup.md
@@ -0,0 +1,442 @@
+# 涓�浣撴満鏁板瓧浜洪厤缃寚鍗�
+
+鏈」鐩敤浜庡湪 x86 鏋舵瀯璁惧锛堝杩蜂綘涓绘満銆佸伐鎺ф満銆佹櫘閫氱數鑴戠瓑锛変笂閮ㄧ讲涓�濂楀畬鏁寸殑鏁板瓧浜哄睍绀虹郴缁燂紝瀹炵幇浠ヤ笅鍔熻兘锛�
+- 寮�鏈鸿嚜鍔ㄨ繘鍏� Kiosk 鍏ㄥ睆娴忚鍣紝灞曠ず鎸囧畾缃戦〉
+- 鍚庡彴杩愯鍞ら啋璇嶆娴嬫湇鍔★紝鏀寔璇煶浜や簰
+
+> **璇存槑**锛氭湰鏂囨。浠� **Intel N100 杩蜂綘涓绘満锛堝ぉ铏� QN10-100B4锛�** 涓轰緥杩涜閮ㄧ讲婕旂ず锛屽叾浠� x86 璁惧鍙弬鑰冭皟鏁达紙娉ㄦ剰缃戠粶閰嶇疆鍜屽0鍗¤澶囧樊寮傦級銆�
+
+## 閫傜敤鐜
+
+| 椤圭洰 | 璇存槑 |
+|------|------|
+| 绀轰緥纭欢 | 澶╄櫣 QN10-100B4锛圛ntel N100锛� |
+| 鎿嶄綔绯荤粺 | Ubuntu 24.04 LTS (Noble Numbat) |
+| 绀轰緥鐢ㄦ埛 | xz锛堣鏍规嵁瀹為檯鎯呭喌鏇挎崲锛� |
+| 缃戠粶 | Wi-Fi 杩炴帴锛屽浐瀹� IP锛堝彲鎸夐渶鏀逛负鏈夌嚎锛� |
+
+## 閮ㄧ讲娴佺▼
+
+1. 绯荤粺鍒濆鍖栵紙鎹㈡簮銆佽繛缃戯級
+2. 瀹夎鍥惧舰缁勪欢涓� Kiosk 娴忚鍣�
+3. 閰嶇疆鑷姩鐧诲綍涓庡浘褰㈢晫闈�
+4. 閮ㄧ讲鍞ら啋璇嶆湇鍔★紙Python 鐜 + 楹﹀厠椋庯級
+5. 浼樺寲寮�鏈洪�熷害涓庨殣钘忓惎鍔ㄤ俊鎭�
+
+---
+
+
+### 绯荤粺鍒濆鍖栵紙鎹㈡簮銆佽繛缃戯級
+
+```
+sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
+
+sudo tee /etc/apt/sources.list > /dev/null <<EOF
+deb http://mirrors.aliyun.com/ubuntu/ noble main restricted universe multiverse
+# deb-src http://mirrors.aliyun.com/ubuntu/ noble main restricted universe multiverse
+
+deb http://mirrors.aliyun.com/ubuntu/ noble-security main restricted universe multiverse
+# deb-src http://mirrors.aliyun.com/ubuntu/ noble-security main restricted universe multiverse
+
+deb http://mirrors.aliyun.com/ubuntu/ noble-updates main restricted universe multiverse
+# deb-src http://mirrors.aliyun.com/ubuntu/ noble-updates main restricted universe multiverse
+
+deb http://mirrors.aliyun.com/ubuntu/ noble-proposed main restricted universe multiverse
+# deb-src http://mirrors.aliyun.com/ubuntu/ noble-proposed main restricted universe multiverse
+
+deb http://mirrors.aliyun.com/ubuntu/ noble-backports main restricted universe multiverse
+# deb-src http://mirrors.aliyun.com/ubuntu/ noble-backports main restricted universe multiverse
+EOF
+
+echo 'Acquire::ForceIPv4 "true";' | sudo tee /etc/apt/apt.conf.d/99force-ipv4
+```
+
+
+瀹夎缃戠粶绠$悊宸ュ叿锛堣嫢宸插瓨鍦ㄥ彲蹇界暐锛�
+
+Bash
+
+```
+sudo apt update
+sudo apt install network-manager -y
+sudo systemctl start NetworkManager
+sudo systemctl enable NetworkManager
+```
+
+
+璁剧疆wifi瀵嗙爜锛屽浐瀹歩p
+
+> **鎻愰啋**锛氫互涓嬪懡浠や腑鐨� Wi-Fi 鍚嶇О銆佸瘑鐮併�両P 鍦板潃鍧囦负绀轰緥锛岃鍔″繀鏇挎崲涓轰綘鑷繁鐨勫疄闄呬俊鎭��
+
+Bash
+
+```
+sudo nmcli device wifi connect "MERCURY_1812" password "12345678"
+
+sudo nmcli connection modify "MERCURY_1812" ipv4.addresses "192.168.0.86/24" ipv4.gateway "192.168.0.1" ipv4.dns "8.8.8.8,114.114.114.114" ipv4.method "manual"
+
+sudo nmcli connection up "MERCURY_1812"
+```
+
+
+### 绗竴姝ワ細瀹夎鏍稿績鍥惧舰缁勪欢涓庢祻瑙堝櫒
+
+杩欓噷鎴戜滑鍧氭寔鈥滄瀬绠�涓讳箟鈥濓紝鍧氬喅涓嶈澶氫綑鐨勬闈㈢幆澧冿紙濡� GNOME/KDE锛夛紝鍙搴曞眰椹卞姩銆佹渶杞婚噺鐨勭獥鍙g鐞嗗櫒锛圤penbox锛夈�侀殣钘忛紶鏍囧伐鍏蜂互鍙� Chromium 娴忚鍣ㄣ��
+
+Bash
+
+```
+sudo timedatectl set-timezone Asia/Shanghai
+
+
+sudo apt install net-tools vim fonts-wqy-microhei fonts-wqy-zenhei alsa-utils pulseaudio -y
+sudo apt install --no-install-recommends xserver-xorg x11-xserver-utils xinit openbox unclutter -y
+
+wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
+sudo apt install ./google-chrome-stable_current_amd64.deb -y
+rm google-chrome-stable_current_amd64.deb
+
+sudo apt purge snapd -y
+
+```
+
+### 绗簩姝ワ細閰嶇疆 TTY1 寮�鏈哄厤瀵嗚嚜鍔ㄧ櫥褰�
+
+涓轰簡閬垮厤鎵嬪姩杈撳叆璐﹀彿瀵嗙爜鐨勫按灏紝鎴戜滑閫氳繃淇敼 systemd 鏈嶅姟锛岃绯荤粺涓�寮�鏈哄氨鑷姩浠� `xz` 鐨勮韩浠界櫥褰曘�傝繖閲屼娇鐢ㄤ竴閿啓鍏ュ懡浠わ紝褰诲簳閬垮紑鐢变簬 `nano` 鎴� `vi` 鎿嶄綔涓嶅綋瀵艰嚧鐨勪繚瀛樺け璐ラ棶棰樸��
+
+**1. 鍒涘缓閰嶇疆鐩綍锛�**
+
+Bash
+
+```
+sudo mkdir -p /etc/systemd/system/getty@tty1.service.d/
+```
+
+**2. 鍐欏叆鑷姩鐧诲綍瑙勫垯锛�**
+
+Bash
+
+```
+echo -e "[Service]\nExecStart=\nExecStart=-/sbin/agetty --autologin xz --noclear %I \$TERM" | sudo tee /etc/systemd/system/getty@tty1.service.d/override.conf
+```
+
+**3. 閲嶈浇鏈嶅姟骞惰缃粯璁ゅ惎鍔ㄧ洰鏍囷細**
+
+Bash
+
+```
+sudo systemctl daemon-reload
+sudo systemctl set-default multi-user.target
+```
+
+### 绗笁姝ワ細閰嶇疆鐧诲綍鍚庤嚜鍔ㄥ惎鍔ㄥ浘褰㈢晫闈�
+
+绯荤粺鑷姩鐧诲綍鍚庨粯璁ゅ仠鐣欏湪榛戝簳鐧藉瓧鐨勫懡浠よ锛屾垜浠渶瑕侀厤缃剼鏈紝璁╁畠涓�鏃︾櫥褰曠珛椹惎鍔� X11 鍥惧舰鐜銆�
+
+**1. 瑙﹀彂 `startx` 鐨勫惎鍔ㄩ�昏緫锛�**
+
+鐩存帴灏嗚Е鍙戜唬鐮佽拷鍔犲啓鍏ヤ綘鐨勪釜浜虹幆澧冮厤缃枃浠朵腑锛�
+
+Bash
+
+```
+cat << 'EOF' >> ~/.bash_profile
+if [ -z "$DISPLAY" ] && [ "$(fgconsole)" -eq 1 ]; then
+ exec startx
+fi
+EOF
+```
+
+**2. 鍛婅瘔 `startx` 鍘诲惎鍔� Openbox锛�**
+
+Bash
+
+```
+echo "exec openbox-session" > ~/.xinitrc
+```
+
+### 绗洓姝ワ細閰嶇疆鈥滈摐澧欓搧澹佲�濈殑 Openbox 涓庢祻瑙堝櫒
+
+杩欐槸鏈�鏍稿績鐨勪竴姝ワ細鍏抽棴灞忓箷浼戠湢銆侀殣钘忛紶鏍囥�佸叏灞忛攣瀹氭祻瑙堝櫒锛屽苟鍐欎竴涓�滄寰幆鈥濅繚璇佹祻瑙堝櫒琚剰澶栧叧闂悗涔熻兘鐬棿澶嶆椿銆�
+
+**1. 鍒涘缓 Openbox 閰嶇疆鐩綍锛�**
+
+Bash
+
+```
+mkdir -p ~/.config/openbox
+```
+
+**2. 鍐欏叆鑷惎鍔ㄨ剼鏈� (`autostart`)锛�**
+
+澶嶅埗浠ヤ笅鏁存浠g爜骞跺洖杞︼紙杩欎細鑷姩鎶婃墍鏈変繚鎶よ鍒欏啓杩涙枃浠堕噷锛夛細
+
+Bash
+
+```
+cat << 'EOF' > ~/.config/openbox/autostart
+# 鍏抽棴灞忓箷淇濇姢
+xset -dpms
+xset s noblank
+xset s off
+
+# 闅愯棌榧犳爣
+unclutter -idle 0.1 -root &
+
+# 姝诲惊鐜惎鍔� Chromium锛堝穿婧冩垨琚叧涔熻兘绉掗噸鍚級
+while true; do
+ google-chrome \
+ --kiosk \
+ --no-first-run \
+ --no-default-browser-check \
+ --disable-infobars \
+ --disable-session-crashed-bubble \
+ --disable-translate \
+ --disable-external-intent-requests \
+ --autoplay-policy=no-user-gesture-required \
+ --use-fake-ui-for-media-stream \
+ "https://www.douyin.com"
+ sleep 2
+done &
+EOF
+```
+
+**3. 灞忚斀 `Alt+F4` 閫�鍑哄揩鎹烽敭锛�**
+
+涓轰簡闃叉鍒汉鎻掍笂閿洏寮鸿鍏虫帀绐楀彛锛屾垜浠妸 Openbox 榛樿鐨勭郴缁熷揩鎹烽敭骞叉帀銆�
+
+Bash
+
+```
+cp /etc/xdg/openbox/rc.xml ~/.config/openbox/
+sed -i '/<keybind key="A-F4">/,/<\/keybind>/d' ~/.config/openbox/rc.xml
+```
+
+### 绗簲姝ワ細閲嶅惎楠屾敹鎴愭灉
+
+濡傛灉鎷旀帀缃戠嚎鎴栦笉闇�瑕佺瓑寰呮墍鏈夌綉缁滀笂绾匡紝鍙鐢ㄧ綉缁滅瓑寰呮湇鍔★紝閬垮厤寮�鏈哄崱椤�
+
+Bash
+
+```
+sudo systemctl mask systemd-networkd-wait-online.service
+sudo systemctl mask NetworkManager-wait-online.service
+```
+
+闅愯棌寮�鏈轰俊鎭紙GRUB锛�
+
+Bash
+
+```
+sudo sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT="quiet loglevel=3 systemd.show_status=false vt.global_cursor_default=0"/g' /etc/default/grub
+
+echo 'GRUB_TIMEOUT_STYLE="hidden"' | sudo tee -a /etc/default/grub
+echo 'GRUB_RECORDFAIL_TIMEOUT=0' | sudo tee -a /etc/default/grub
+
+sudo update-grub
+```
+
+澹伴煶璁剧疆鎴�100%锛岀劧鍚� 閲嶅惎锛�
+
+Bash
+
+```
+amixer -q sset Master 100% unmute
+sudo reboot
+```
+
+### 閮ㄧ讲鍞ら啋璇嶆湇鍔�
+
+鍦ㄤ竴浣撴満涓婇儴缃插敜閱掕瘝妫�娴嬫湇鍔★紝闇�瑕佸畨瑁� Python 鐜銆佷笂浼犻」鐩枃浠躲�侀厤缃� Camera 楹﹀厠椋庡拰寮�鏈鸿嚜鍚��
+
+#### 1. 瀹夎 Miniconda
+
+```bash
+wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
+bash Miniconda3-latest-Linux-x86_64.sh -b -p $HOME/miniconda3
+~/miniconda3/bin/conda init bash
+source ~/.bashrc
+rm Miniconda3-latest-Linux-x86_64.sh
+```
+
+
+纭繚鐧诲綍鏃惰嚜鍔ㄨ繘鍏onda鐜
+
+```bash
+if ! grep -q '.bashrc' ~/.bash_profile; then
+ cat << 'EOF' >> ~/.bash_profile
+
+if [ -f ~/.bashrc ]; then
+ . ~/.bashrc
+fi
+EOF
+fi
+```
+
+
+#### 2. 鍒涘缓 Python 铏氭嫙鐜
+
+```bash
+conda create -n test python=3.10 -y
+conda activate test
+```
+
+鑻ュ嚭鐜� Terms of Service have not been accepted 閿欒锛屾墽琛岋細
+
+```bash
+conda tos accept --override-channels --channel https://repo.anaconda.com/pkgs/main
+conda tos accept --override-channels --channel https://repo.anaconda.com/pkgs/r
+```
+
+#### 3. 涓婁紶椤圭洰鏂囦欢
+
+灏嗗紑鍙戞満涓婄殑 `main/digital-human/` 鏁翠釜鐩綍涓婁紶鍒颁竴浣撴満鐨� `~/digital-human/` 鐩綍锛�
+
+```bash
+# 鍦ㄥ紑鍙戞満涓婃墽琛岋紙灏� <涓�浣撴満IP> 鏇挎崲涓哄疄闄� IP锛�
+scp -r main/digital-human/ xz@<涓�浣撴満IP>:~/digital-human/
+```
+
+#### 4. 瀹夎绯荤粺渚濊禆
+
+鍞ら啋璇嶆湇鍔¢渶瑕侀煶棰戦噰闆嗗簱鍜� ALSA PulseAudio 鎻掍欢锛�
+
+```bash
+sudo apt install libportaudio2 portaudio19-dev libasound2-plugins -y
+```
+
+#### 5. 瀹夎 Python 渚濊禆
+
+```bash
+cd ~/digital-human/wakeword_runtime
+pip install numpy
+pip install -r requirements.txt
+```
+
+#### 6. 涓嬭浇鍞ら啋璇嶆ā鍨�
+
+妯″瀷鏂囦欢涓嶅寘鍚湪椤圭洰涓紝闇�瑕佸崟鐙笅杞介厤缃紝璇﹁ [docs/digital-human-wakeword.md](digital-human-wakeword.md) 涓殑鈥滄ā鍨嬩笅杞解�濈珷鑺傘��
+
+#### 7. 淇敼 Openbox 鑷惎鍔ㄨ剼鏈�
+
+闇�瑕佸湪 autostart 涓姞涓� PulseAudio 鍜� Camera 楹﹀厠椋庨厤缃紝骞跺皢 Chrome 鍦板潃鏀逛负娴嬭瘯椤甸潰銆�
+
+鍏堢‘璁� Camera 楹﹀厠椋庡湪 PulseAudio 涓殑璁惧鍚嶏細
+
+```bash
+pulseaudio --start
+pactl list sources short
+```
+
+鎵惧埌鍖呭惈 `USB_Camera` 鐨勯偅涓�琛岋紝璁颁笅瀹屾暣鍚嶇О锛屼緥濡傦細
+
+```
+alsa_input.usb-SN0002_2K_USB_Camera_46435000_P030D00_SN0002-02.mono-fallback
+```
+
+鐒跺悗鐢ㄥ畬鏁村唴瀹硅鐩� autostart锛堝皢 `TARGET_MIC` 鏇挎崲涓轰綘鐨勫疄闄呰澶囧悕锛夛細
+
+```bash
+cat << 'EOF' > ~/.config/openbox/autostart
+# 1. 鍚姩澹伴煶鏈嶅姟骞剁◢浣滅瓑寰�
+pulseaudio --start
+sleep 1
+
+# 2. 閿佸畾 Camera 鐨勯害鍏嬮锛堣鏇挎崲涓轰綘鐨勫疄闄呰澶囧悕锛�
+TARGET_MIC="alsa_input.usb-SN0002_2K_USB_Camera_46435000_P030D00_SN0002-02.mono-fallback"
+
+# 3. 璁句负绯荤粺榛樿楹﹀厠椋�
+pactl set-default-source "$TARGET_MIC"
+
+# 4. 瑙i櫎闈欓煶
+pactl set-source-mute "$TARGET_MIC" 0
+
+# 5. 闊抽噺鎷夊埌 100%
+pactl set-source-volume "$TARGET_MIC" 100%
+
+# --- 鏋佺畝妗岄潰涓庢祻瑙堝櫒鐜閰嶇疆 ---
+
+# 鍏抽棴灞忓箷淇濇姢
+xset -dpms
+xset s noblank
+xset s off
+
+# 闅愯棌榧犳爣
+unclutter -idle 0.1 -root &
+
+# 姝诲惊鐜惎鍔ㄦ祻瑙堝櫒锛堝穿婧冩垨琚叧涔熻兘绉掗噸鍚級
+while true; do
+ google-chrome \
+ --kiosk \
+ --no-first-run \
+ --no-default-browser-check \
+ --disable-infobars \
+ --disable-session-crashed-bubble \
+ --disable-translate \
+ --disable-external-intent-requests \
+ --autoplay-policy=no-user-gesture-required \
+ --use-fake-ui-for-media-stream \
+ "http://127.0.0.1:8006/index.html"
+ sleep 2
+done &
+EOF
+```
+
+#### 8. 閰嶇疆鍞ら啋璇嶆湇鍔″紑鏈鸿嚜鍚�
+
+鍒涘缓 systemd 鏈嶅姟鏂囦欢锛岃鍞ら啋璇嶆湇鍔″紑鏈鸿嚜鍔ㄨ繍琛屻��
+
+鍏堢‘璁ゅ綋鍓嶇敤鎴风殑 UID锛�
+
+```bash
+id -u $(whoami)
+```
+
+鐒跺悗鐢ㄦ煡鍒扮殑 UID 鏇挎崲涓嬮潰 `1000`锛堥�氬父绗竴涓敤鎴峰氨鏄� 1000锛夛細
+
+```bash
+sudo tee /etc/systemd/system/digital-human.service << 'EOF'
+[Unit]
+Description=Digital Human Runtime
+After=network.target sound.target
+
+[Service]
+Type=simple
+User=xz
+Environment=XDG_RUNTIME_DIR=/run/user/1000
+Environment=PULSE_SERVER=unix:/run/user/1000/pulse/native
+WorkingDirectory=/home/xz/digital-human
+ExecStartPre=/bin/sleep 10
+ExecStart=/home/xz/miniconda3/envs/test/bin/python start.py
+Restart=on-failure
+RestartSec=10
+
+[Install]
+WantedBy=multi-user.target
+EOF
+```
+
+> **閲嶈璇存槑**锛�
+> - `User=xz` 鈥� 鏇挎崲涓轰綘鐨勫疄闄呯敤鎴峰悕
+> - `/run/user/1000` 鈥� 鏇挎崲涓轰綘瀹為檯鐨� UID
+> - `WorkingDirectory` 鍜� `ExecStart` 涓殑璺緞 鈥� 鏇挎崲涓轰綘鐨勫疄闄呴儴缃茶矾寰�
+> - `Environment` 涓殑 PulseAudio 鐜鍙橀噺**蹇呴』淇濈暀**锛屽惁鍒欏敜閱掕瘝鏈嶅姟鍜屾祻瑙堝櫒鏃犳硶鍚屾椂浣跨敤 Camera 楹﹀厠椋�
+
+鍚敤骞跺惎鍔ㄦ湇鍔★細
+
+```bash
+sudo systemctl daemon-reload
+sudo systemctl enable digital-human
+sudo systemctl start digital-human
+```
+
+#### 9. 甯哥敤鏈嶅姟绠$悊鍛戒护
+
+```bash
+sudo systemctl start digital-human # 绔嬪嵆鍚姩
+sudo systemctl stop digital-human # 鍋滄
+sudo systemctl restart digital-human # 閲嶅惎
+sudo systemctl status digital-human # 鏌ョ湅鐘舵��
+journalctl -u digital-human -f # 鏌ョ湅瀹炴椂鏃ュ織
+```
+
diff --git a/xiaozhi-esp32-server/docs/context-provider-integration.md b/xiaozhi-esp32-server/docs/context-provider-integration.md
new file mode 100644
index 0000000..d8a9049
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/context-provider-integration.md
@@ -0,0 +1,224 @@
+# 涓婁笅鏂囨簮浣跨敤鏁欑▼
+
+## 姒傝堪
+
+`涓婁笅鏂囨簮`锛屽氨鏄负灏忔櫤绯荤粺鎻愮ず璇嶇殑涓婁笅鏂囨坊鍔犮�愭暟鎹簮銆戙��
+
+`涓婁笅鏂囨簮` 鍦ㄥ皬鏅哄湪鍞ら啋閭d竴鍒伙紝鑾峰彇澶栭儴绯荤粺鐨勬暟鎹紝骞跺皢鍏跺姩鎬佹敞鍏ュ埌澶фā鍨嬬殑绯荤粺鎻愮ず璇嶏紙System Prompt锛変腑銆�
+璁╁叾鍋氬埌鍞ら啋鏃舵劅鐭ヤ笘鐣屾煇涓簨鐗╃殑鐘舵�併��
+
+瀹冨拰MCP銆佽蹇嗘湁鏈川鐨勫尯鍒細`涓婁笅鏂囨簮`鏄己鍒惰灏忔櫤鎰熺煡涓栫晫鐨勬暟鎹紱`璁板繂(Mem)`鏄浠栫煡閬撲箣鍓嶈亰浜嗕粈涔堝唴瀹癸紱`MCP(functionc all)`鏄綋闇�瑕佽皟鐢ㄦ煇椤硅兘鍔�/鐭ヨ瘑鐨勬椂鍊欎娇鐢ㄨ皟鐢ㄣ��
+
+閫氳繃杩欎釜鍔熻兘锛屽湪灏忔櫤鍞ら啋鐨勪竴鍒归偅锛屸�滄劅鐭モ�濆埌锛�
+- 浜轰綋鍋ュ悍浼犳劅鍣ㄧ姸鎬侊紙浣撴俯銆佽鍘嬨�佽姘х姸鎬佺瓑锛�
+- 涓氬姟绯荤粺鐨勫疄鏃舵暟鎹紙鏈嶅姟鍣ㄨ礋杞姐�佸緟鍔炴暟鎹�佽偂绁ㄤ俊鎭瓑锛�
+- 浠讳綍鍙互閫氳繃 HTTP API 鑾峰彇鐨勬枃鏈俊鎭�
+
+**娉ㄦ剰**锛氳鍔熻兘鍙槸鏂逛究灏忔櫤鍦ㄥ敜閱掔殑鏃跺�欐劅鐭ヤ簨鐗╃殑鐘舵�侊紝鑰屽鏋滄兂瑕佸皬鏅哄敜閱掑悗瀹炴椂鑾峰彇浜嬬墿鐨勭姸鎬侊紝寤鸿鍦ㄦ鍔熻兘涓婂啀缁撳悎MCP宸ュ叿鐨勮皟鐢ㄣ��
+
+## 宸ヤ綔鍘熺悊
+
+1. **閰嶇疆婧�**锛氱敤鎴烽厤缃竴涓垨澶氫釜 HTTP API 鍦板潃銆�
+2. **瑙﹀彂璇锋眰**锛氬綋绯荤粺鏋勫缓 Prompt 鏃讹紝濡傛灉鍙戠幇妯℃澘涓寘鍚� `{{ dynamic_context }}` 鍗犱綅绗︼紝浼氳姹傛墍鏈夐厤缃殑 API銆�
+3. **鑷姩娉ㄥ叆**锛氱郴缁熶細鑷姩灏� API 杩斿洖鐨勬暟鎹牸寮忓寲涓� Markdown 鍒楄〃锛屾浛鎹� `{{ dynamic_context }}` 鍗犱綅绗︺��
+
+## 鎺ュ彛瑙勮寖
+
+涓轰簡璁╁皬鏅烘纭В鏋愭暟鎹紝鎮ㄧ殑 API 闇�瑕佹弧瓒充互涓嬭鑼冿細
+
+- **璇锋眰鏂瑰紡**锛歚GET`
+- **璇锋眰澶�**锛氱郴缁熶細鑷姩娣诲姞 `device-id` 瀛楁鍒� Request Header銆�
+- **鍝嶅簲鏍煎紡**锛氬繀椤昏繑鍥� JSON 鏍煎紡锛屼笖鍖呭惈 `code` 鍜� `data` 瀛楁銆�
+
+### 鍝嶅簲绀轰緥
+
+**鎯呭喌 1锛氳繑鍥為敭鍊煎**
+```json
+{
+ "code": 0,
+ "msg": "success",
+ "data": {
+ "瀹㈠巺娓╁害": "26鈩�",
+ "瀹㈠巺婀垮害": "45%",
+ "澶ч棬鐘舵��": "宸插叧闂�"
+ }
+}
+```
+*娉ㄥ叆鏁堟灉锛�*
+```markdown
+<context>
+- **瀹㈠巺娓╁害锛�** 26鈩�
+- **瀹㈠巺婀垮害锛�** 45%
+- **澶ч棬鐘舵�侊細** 宸插叧闂�
+</context>
+```
+
+**鎯呭喌 2锛氳繑鍥炲垪琛�**
+```json
+{
+ "code": 0,
+ "data": [
+ "鎮ㄦ湁10涓緟鍔炰簨椤�",
+ "褰撳墠姹借溅鐨勮椹堕�熷害鏄�100km姣忓皬鏃�"
+ ]
+}
+```
+*娉ㄥ叆鏁堟灉锛�*
+```markdown
+<context>
+- 鎮ㄦ湁10涓緟鍔炰簨椤�
+- 褰撳墠姹借溅鐨勮椹堕�熷害鏄�100km姣忓皬鏃�
+</context>
+```
+
+## 閰嶇疆鎸囧崡
+
+### 鏂瑰紡 1锛氭櫤鎺у彴閰嶇疆锛堝叏妯″潡閮ㄧ讲锛�
+
+1. 鐧诲綍鏅烘帶鍙帮紝杩涘叆**瑙掕壊閰嶇疆**椤甸潰銆�
+2. 鎵惧埌**涓婁笅鏂囨簮**閰嶇疆椤癸紙鐐瑰嚮鈥滅紪杈戞簮鈥濇寜閽級銆�
+3. 鐐瑰嚮**娣诲姞**锛岃緭鍏ユ偍鐨� API 鍦板潃銆�
+4. 濡傛灉 API 闇�瑕侀壌鏉冿紝鍙互鍦�**璇锋眰澶�**閮ㄥ垎娣诲姞 `Authorization` 鎴栧叾浠� Header銆�
+5. 淇濆瓨閰嶇疆銆�
+
+### 鏂瑰紡 2锛氶厤缃枃浠堕厤缃紙鍗曟ā鍧楅儴缃诧級
+
+缂栬緫 `xiaozhi-server/data/.config.yaml` 鏂囦欢锛屾坊鍔� `context_providers` 閰嶇疆娈碉細
+
+```yaml
+# 涓婁笅鏂囨簮閰嶇疆
+context_providers:
+ - url: "http://api.example.com/data"
+ headers:
+ Authorization: "Bearer your-token"
+ - url: "http://another-api.com/data"
+```
+
+## 鍚敤鍔熻兘
+
+榛樿鎯呭喌涓嬶紝绯荤粺鐨勬彁绀鸿瘝妯℃澘鏂囦欢锛坄data/.agent-base-prompt.txt`锛変腑宸茬粡棰勭疆浜� `{{ dynamic_context }}` 鍗犱綅绗︼紝鎮ㄦ棤闇�鎵嬪姩娣诲姞銆�
+
+**绀轰緥锛�**
+
+```markdown
+<context>
+銆愰噸瑕侊紒浠ヤ笅淇℃伅宸插疄鏃舵彁渚涳紝鏃犻渶璋冪敤宸ュ叿鏌ヨ锛岃鐩存帴浣跨敤锛氥��
+- **璁惧ID锛�** {{device_id}}
+- **褰撳墠鏃堕棿锛�** {{current_time}}
+...
+{{ dynamic_context }}
+</context>
+```
+
+**娉ㄦ剰**锛氬鏋滄偍涓嶉渶瑕佷娇鐢ㄦ鍔熻兘锛屽彲浠ラ�夋嫨**涓嶉厤缃换浣曚笂涓嬫枃婧�**锛屼篃鍙互浠庢彁绀鸿瘝妯℃澘鏂囦欢涓�**鍒犻櫎** `{{ dynamic_context }}` 鍗犱綅绗︺��
+
+## 闄勫綍锛歁ock 娴嬭瘯鏈嶅姟绀轰緥
+
+涓轰簡鏂逛究鎮ㄦ祴璇曞拰寮�鍙戯紝鎴戜滑鎻愪緵浜嗕竴涓畝鍗曠殑 Python Mock Server 鑴氭湰銆傛偍鍙互杩愯姝よ剼鏈湪鏈湴妯℃嫙 API 鎺ュ彛銆�
+
+**mock_api_server.py**
+
+```python
+import http.server
+import socketserver
+import json
+from urllib.parse import urlparse, parse_qs
+
+# 璁剧疆绔彛鍙�
+PORT = 8081
+
+class MockRequestHandler(http.server.SimpleHTTPRequestHandler):
+ def do_GET(self):
+ # 瑙f瀽璺緞鍜屽弬鏁�
+ parsed_path = urlparse(self.path)
+ path = parsed_path.path
+ query = parse_qs(parsed_path.query)
+
+ response_data = {}
+ status_code = 200
+
+ print(f"鏀跺埌璇锋眰: {path}, 鍙傛暟: {query}")
+
+ # Case 1: 妯℃嫙鍋ュ悍鏁版嵁 (杩斿洖瀛楀吀 Dict)
+ # 璺緞鍙傛暟椋庢牸: /health
+ # device_id 浠� Header 鑾峰彇
+ if path == "/health":
+ device_id = self.headers.get("device-id", "unknown_device")
+ print(f"device_id: {device_id}")
+ response_data = {
+ "code": 0,
+ "msg": "success",
+ "data": {
+ "娴嬭瘯璁惧ID": device_id,
+ "蹇冪巼": "80 bpm",
+ "琛�鍘�": "120/80 mmHg",
+ "鐘舵��": "鑹ソ"
+ }
+ }
+
+ # Case 2: 妯℃嫙鏂伴椈鍒楄〃 (杩斿洖鍒楄〃 List)
+ # 鏃犲弬鏁�: /news/list
+ elif path == "/news/list":
+ response_data = {
+ "code": 0,
+ "msg": "success",
+ "data": [
+ "浠婃棩澶存潯锛歅ython 3.14 鍙戝竷",
+ "绉戞妧鏂伴椈锛欰I 鍔╂墜鏀瑰彉鐢熸椿",
+ "鏈湴鏂伴椈锛氭槑鏃ユ湁澶ч洦锛岃寰楀甫浼�"
+ ]
+ }
+
+ # Case 3: 妯℃嫙澶╂皵绠�鎶� (杩斿洖瀛楃涓� String)
+ # 鏃犲弬鏁�: /weather/simple
+ elif path == "/weather/simple":
+ response_data = {
+ "code": 0,
+ "msg": "success",
+ "data": "浠婃棩鏅磋浆澶氫簯锛屾皵娓� 20-25 搴︼紝绌烘皵璐ㄩ噺浼橈紝閫傚悎鍑鸿銆�"
+ }
+
+ # Case 4: 妯℃嫙璁惧璇︽儏 (Query鍙傛暟椋庢牸)
+ # 鍙傛暟椋庢牸: /device/info
+ # device_id 浠� Header 鑾峰彇
+ elif path == "/device/info":
+ device_id = self.headers.get("device-id", "unknown_device")
+ response_data = {
+ "code": 0,
+ "msg": "success",
+ "data": {
+ "鏌ヨ鏂瑰紡": "Header鍙傛暟",
+ "璁惧ID": device_id,
+ "鐢甸噺": "85%",
+ "鍥轰欢": "v2.0.1"
+ }
+ }
+
+ # Case 5: 404 Not Found
+ else:
+ status_code = 404
+ response_data = {"error": "鎺ュ彛涓嶅瓨鍦�"}
+
+ # 鍙戦�佸搷搴�
+ self.send_response(status_code)
+ self.send_header('Content-type', 'application/json; charset=utf-8')
+ self.end_headers()
+ self.wfile.write(json.dumps(response_data, ensure_ascii=False).encode('utf-8'))
+
+# 鍚姩鏈嶅姟
+# 鍏佽鍦板潃閲嶇敤锛岄槻姝㈠揩閫熼噸鍚姤閿�
+socketserver.TCPServer.allow_reuse_address = True
+with socketserver.TCPServer(("", PORT), MockRequestHandler) as httpd:
+ print(f"==================================================")
+ print(f"Mock API Server 宸插惎鍔�: http://localhost:{PORT}")
+ print(f"鍙敤鎺ュ彛鍒楄〃:")
+ print(f"1. [瀛楀吀] http://localhost:{PORT}/health")
+ print(f"2. [鍒楄〃] http://localhost:{PORT}/news/list")
+ print(f"3. [鏂囨湰] http://localhost:{PORT}/weather/simple")
+ print(f"4. [鍙傛暟] http://localhost:{PORT}/device/info")
+ print(f"==================================================")
+ try:
+ httpd.serve_forever()
+ except KeyboardInterrupt:
+ print("\n鏈嶅姟宸插仠姝�")
+```
diff --git a/xiaozhi-esp32-server/docs/contributor_open_letter.md b/xiaozhi-esp32-server/docs/contributor_open_letter.md
new file mode 100644
index 0000000..7a54719
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/contributor_open_letter.md
@@ -0,0 +1,50 @@
+# 鑷村紑鍙戣�呯殑鍏紑淇�
+
+"鏄ユ睙姘存殩楦厛鐭ワ紝姝f槸娌宠睔娆蹭笂鏃讹紒"
+
+浜茬埍鐨勬湅鍙嬶紝鎴戞槸John锛屾槸涓�鍚嶆櫘閫氬叕鍙搁噷鐨凧ava绋嬪簭鍛橈紝浠婂ぉ锛屾垜鎬�鐫�鏃犳瘮鐪熸寶鐨勫績鎯咃紝鍚戠儹鐖盇I鎶�鏈笌鍒涙柊鐨勪綘鍙戝嚭杩欏皝鍏紑淇°��
+
+鍗婂勾鍓嶆垜鐪嬪埌寰堝浼樼鐨勯」鐩紝姣斿`Dify`銆乣Chat2DB`绛変汉宸ユ櫤鑳界浉鍏崇殑椤圭洰锛屾垜鍦ㄦ兂锛屾垜瑕佹槸鑳藉弬涓庤繖浜涢」鐩濂斤紝鍙儨鈥滄姤鍥芥棤闂紝绌烘墦鍗佸勾浠g爜鈥濄��
+
+鎴戞槸2025骞村垵鍒峰埌铏惧摜鍥㈤槦鐨勮棰戯紝鎴戦潪甯稿ソ濂囦粬鏄�庝箞瀹炵幇鐨勶紝鎴戞兂澶嶅埢浠栦滑鐨勫悗绔湇鍔★紝鎵撻�犱竴涓綆鎴愭湰姘戠敤璐剧淮鏂�傚緢鍙儨鐜板湪鍋氱殑浣滃搧渚濈劧鍙槸涓�涓汉宸ユ櫤闅滐紝瀹冨苟鍙戜綆銆佹病鏈夌伒榄傦紝鍝嶅簲寰堟參锛宐ug寰堝銆�
+
+铏惧摜鍥㈤槦鏄垜浠涔犵殑瀵硅薄锛屾垜寰堟兂鎷ユ湁鍍忚櫨鍝ュ洟闃熶竴鏍锋櫤鑳界殑灏忔櫤鍚庣鏈嶅姟銆備絾鏄垜涔熻兘鐞嗚В铏惧摜涓嶅紑婧愮殑鍐冲畾銆傗�滀竴鑺辩嫭鏀句笉鏄槬锛岀櫨鑺遍綈鏀炬槬婊″洯鈥濓紝浜哄伐鏅鸿兘閬嶅湴寮�鑺辩殑鏃朵唬锛屼篃璁稿氨鍦ㄦ垜浠繖浠e疄鐜帮紝鎴戜滑鍙互鐢ㄨ嚜宸辩殑鍙屾墜锛屽疄鐜颁綆鎴愭湰姘戠敤璐剧淮鏂�傛垜涓汉璁や负锛屼粬鑳藉疄鐜扮殑锛屾垜浠篃鑳藉疄鐜帮紝鍙槸鏃堕棿闂鑰屽凡锛屾垜绉颁箣涓衡�滄垜浠殑鍙栫粡涔嬭矾鈥濄��
+
+閭d箞杩欐潯鍙栫粡涔嬭矾锛屾垜浠細閬囧埌浠�涔堝洶闅撅紵鎴戞兂搴旇涓嶅皯浜庡叓鍗佷竴闅俱�傝繖涓�璺繀鐒朵細鍑虹幇鍚勭濡栨�紝褰撶劧涔熸湁绁炰粰鏆椾腑甯姪鎴戜滑锛屼篃鏈変汉鍔犲叆鍙栫粡闃熶紞銆�
+
+浠ヤ笂鍐呭锛屽鏋滀綘瑙夊緱濂界瑧銆傞偅鎴戜篃瑙夊緱闈炲父鐨勫垢杩愩�傛垜鑳藉鍦ㄤ綘浜虹敓3涓囧澶╅噷鍗氫綘绗戜簲绉掞紝涔熺畻鏄负浣犲仛浜嗕竴娆¤础鐚��
+
+姘戠敤浣庢垚鏈淳缁存柉杩欎釜鎯虫硶锛屼細澶辫触鍚楋紝鎴戜笉鐭ラ亾锛屼絾鏄垜浠櫘閫氫汉鐨勪竴鐢燂紝杩欑澶辫触涓嶆槸寰堝父瑙佸悧锛�
+
+鏈潵锛屾湁涓�鐐规槸鍙互纭畾鐨勶紝灏变竴瀹氫細鏈変汉瀹屽叏澶嶅埢铏惧摜鍥㈤槦鐨勫姛鑳斤紝瀹炵幇姘戠敤浣庢垚鏈淳缁存柉銆傝繖涓」鐩細鏄垜浠悧锛�
+
+鏈熷緟涓庝綘鎼烘墜鍓嶈锛屽叡鍒涙湭鏉ャ��
+
+John锛�2025.3.11锛屽箍宸�
+
+# 闄� 寮�鍙戣础鐚寚鍗�
+## 椤圭洰鐩爣
+
+1. **姘戠敤浣庢垚鏈淳缁存柉瑙e喅鏂规**
+
+2. **鏅鸿兘鑱斿姩鍛ㄨ竟纭欢鐨勮В鍐虫柟妗�**
+
+## 鍔犲叆鎴戜滑
+
+鎴戜滑鐑勘娆㈣繋蹇楀悓閬撳悎鐨勬湅鍙嬪姞鍏ワ紝鍏卞悓涓洪」鐩础鐚姏閲忋�傛偍鍙湪[杩欎釜閾炬帴](https://github.com/users/xinnan-tech/projects/3)鏌ョ湅鎴戜滑杩戞湡瑕佸疄鐜扮殑鍔熻兘锛屽姛鑳藉垪琛ㄤ腑杩樻病鎸囨淳鐩稿叧浜哄憳澶勭悊鐨勶紝姝f槸鎬ラ渶鎮ㄧ殑鍙備笌銆傚弬涓庢柟寮忓涓嬶細
+
+### 1銆佹垚涓烘櫘閫氳础鐚��
+
+Fork 椤圭洰锛屾彁浜� PR锛岀敱寮�鍙戣�呭鏍稿悗鍚堝叆涓诲垎鏀��
+
+### 2銆佹垚涓哄紑鍙戣��
+
+褰撲綘绱鎻愪氦 3 娆℃湁鏁� PR 鍚庯紝鍙互鑱旂郴缇や富鐢宠鎴愪负寮�鍙戣�咃紝缇や富灏嗛個璇蜂綘鍔犲叆鐙珛鐨勫紑鍙戣�呯兢锛屽叡鍚屾帰璁ㄩ」鐩湭鏉ャ��
+
+## 寮�鍙戣�呭紑鍙戞祦绋�
+
+1. **鍒涘缓鏂板垎鏀�**
+ 姣忎釜鍔熻兘鐐硅浠ユ柊鍒嗘敮鏂瑰紡寮�鍙戯紝鍒嗘敮鍚嶇О搴旂畝娲佹槑浜嗭紝璁╀汉涓�鐪肩湅鍑烘墍瀹炵幇鐨勫姛鑳斤紝閬垮厤鍔熻兘鎾炶溅銆�
+
+2. **鎻愪氦 PR 瀹℃牳**
+ 鍔熻兘寮�鍙戝畬鎴愬悗锛岃鍦� GitHub 涓婃彁浜� PR锛岀敱鍏朵粬寮�鍙戣�呭鏍革紝瀹℃牳閫氳繃鍚庡悎骞跺叆涓诲垎鏀��
diff --git a/xiaozhi-esp32-server/docs/dev-ops-integration.md b/xiaozhi-esp32-server/docs/dev-ops-integration.md
new file mode 100644
index 0000000..deeb037
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/dev-ops-integration.md
@@ -0,0 +1,182 @@
+# 鍏ㄦā鍧楁簮鐮侀儴缃茶嚜鍔ㄥ崌绾ф柟娉�
+
+鏈暀绋嬫槸鏂逛究鍏ㄦā鍧楁簮鐮侀儴缃茬殑鐖卞ソ鑰咃紝濡備綍閫氳繃鑷姩鍛戒护锛岃嚜鍔ㄦ媺鍙栨簮鐮侊紝鑷姩缂栬瘧锛岃嚜鍔ㄥ惎鍔ㄧ鍙h繍琛屻�傚疄鐜版渶楂樻晥鐜囩殑鍗囩骇绯荤粺銆�
+
+鏈」鐩殑娴嬭瘯骞冲彴`https://2662r3426b.vicp.fun`锛屼粠寮�鏀句互鏉ュ氨浣跨敤浜嗚鏂规硶锛屾晥鏋滆壇濂姐��
+
+鏁欑▼鍙弬鑰傿绔欏崥涓籤姣曚箰labs`鍙戝竷鐨勮棰戞暀绋嬶細[銆婂紑婧愬皬鏅烘湇鍔″櫒xiaozhi-server鑷姩鏇存柊浠ュ強鏈�鏂扮増鏈琈CP鎺ュ叆鐐归厤缃繚濮嗘暀绋嬨�媇(https://www.bilibili.com/video/BV15H37zHE7Q)
+
+# 寮�濮嬫潯浠�
+- 浣犵殑鐢佃剳/鏈嶅姟鍣ㄦ槸linux鎿嶄綔绯荤粺
+- 浣犲凡缁忚窇閫氫簡鏁翠釜娴佺▼
+- 浣犲枩娆㈣窡杩涙渶鏂板姛鑳斤紝浣嗘槸瑙夊緱姣忔鎵嬪姩閮ㄧ讲鏈夌偣楹荤儲锛屾湡寰呮湁涓�涓嚜鍔ㄦ洿鏂扮殑鏂规硶
+
+绗簩涓潯浠跺繀椤绘弧瓒筹紝鍥犱负鏈暀绋嬫墍娑夊強鐨勬煇浜涙枃浠讹紝JDK銆丯ode.js鐜銆丆onda鐜绛夛紝鏄渶瑕佷綘璺戦�氭暣涓祦绋嬫墠鏈夌殑锛屽鏋滀綘娌℃湁璺戦�氾紝褰撴垜璁插埌鏌愪釜鏂囦欢鐨勬椂鍊欙紝浣犲彲鑳藉氨涓嶇煡閬撲粈涔堟剰鎬濄��
+
+# 鏁欑▼鏁堟灉
+- 瑙e喅鍥藉唴涓嶈兘鎷夊彇鏈�鏂伴」鐩簮鐮侀棶棰�
+- 鑷姩鎷夊彇浠g爜缂栬瘧鍓嶇鏂囦欢
+- 鑷姩鎷夊彇浠g爜缂栬瘧java鏂囦欢锛岃嚜鍔ㄦ潃鎺�8002绔彛锛岃嚜鍔ㄥ惎鍔�8002绔彛
+- 鑷姩鎷夊彇python浠g爜锛岃嚜鍔ㄦ潃鎺�8000绔彛锛岃嚜鍔ㄥ惎鍔�8000绔彛
+
+# 绗竴姝� 閫夊ソ浣犵殑椤圭洰鐩綍
+
+渚嬪锛屾垜瑙勫垝浜嗘垜鐨勯」鐩洰褰曟槸锛岃繖鏄竴涓柊寤虹殑绌虹櫧鐨勭洰褰曪紝濡傛灉浣犱笉鎯冲嚭閿欙紝鍙互鍜屾垜涓�鏍�
+```
+/home/system/xiaozhi
+```
+
+# 绗簩姝� 鍏嬮殕鏈」鐩�
+姝ゅ埢锛屽厛瑕佹墽琛岀涓�鍙ヨ瘽锛屾媺鍙栨簮鐮侊紝杩欏彞鍛戒护閫傜敤浜庡浗鍐呯綉缁滅殑鏈嶅姟鍣ㄥ拰鐢佃剳锛屾棤闇�缈诲
+
+```
+cd /home/system/xiaozhi
+git clone https://ghproxy.net/https://github.com/xinnan-tech/xiaozhi-esp32-server.git
+```
+
+鎵ц瀹屽悗锛屼綘鐨勯」鐩洰褰曚細澶氫簡涓�涓枃浠跺す`xiaozhi-esp32-server`锛岃繖涓氨鏄」鐩殑婧愮爜
+
+# 绗笁姝� 澶嶅埗鍩虹鐨勬枃浠�
+
+濡傛灉浣犱箣鍓嶅凡缁忚窇閫氫簡鏁翠釜娴佺▼锛屽funasr鐨勬ā鍨嬫枃浠禶xiaozhi-server/models/SenseVoiceSmall/model.pt`鍜屼綘鐨勭鏈夐厤缃枃浠禶xiaozhi-server/data/.config.yaml`杩欎袱涓枃浠朵笉浼氶檶鐢熴��
+
+姝ゅ埢浣犻渶瑕佹妸`model.pt`鏂囦欢澶嶅埗鍒版柊鐨勭洰褰曞幓锛屼綘鍙互杩欐牱
+```
+# 鍒涘缓闇�瑕佺殑鐩綍
+mkdir -p /home/system/xiaozhi/xiaozhi-esp32-server/main/xiaozhi-server/data/
+
+cp 浣犲師鏉ョ殑.config.yaml瀹屾暣璺緞 /home/system/xiaozhi/xiaozhi-esp32-server/main/xiaozhi-server/data/.config.yaml
+cp 浣犲師鏉ョ殑model.pt瀹屾暣璺緞 /home/system/xiaozhi/xiaozhi-esp32-server/main/xiaozhi-server/models/SenseVoiceSmall/model.pt
+```
+
+# 绗洓姝� 寤虹珛涓変釜鑷姩缂栬瘧鏂囦欢
+
+## 4.1 鑷姩缂栬瘧mananger-web妯″潡
+鍦╜/home/system/xiaozhi/`鐩綍涓嬶紝鍒涘缓鍚嶅瓧涓篳update_8001.sh`鐨勬枃浠讹紝鍐呭濡備笅
+
+```
+cd /home/system/xiaozhi/xiaozhi-esp32-server
+git fetch --all
+git reset --hard
+git pull origin main
+
+
+cd /home/system/xiaozhi/xiaozhi-esp32-server/main/manager-web
+npm install
+npm run build
+rm -rf /home/system/xiaozhi/manager-web
+mv /home/system/xiaozhi/xiaozhi-esp32-server/main/manager-web/dist /home/system/xiaozhi/manager-web
+```
+
+淇濆瓨濂藉悗鎵ц璧嬫潈鍛戒护
+```
+chmod 777 update_8001.sh
+```
+鎵ц瀹屽悗锛岀户缁線涓�
+
+## 4.2 鑷姩缂栬瘧杩愯manager-api妯″潡
+鍦╜/home/system/xiaozhi/`鐩綍涓嬶紝鍒涘缓鍚嶅瓧涓篳update_8002.sh`鐨勬枃浠讹紝鍐呭濡備笅
+
+```
+cd /home/system/xiaozhi/xiaozhi-esp32-server
+git pull origin main
+
+
+cd /home/system/xiaozhi/xiaozhi-esp32-server/main/manager-api
+rm -rf target
+mvn clean package -Dmaven.test.skip=true
+cd /home/system/xiaozhi/
+
+# 鏌ユ壘鍗犵敤8002绔彛鐨勮繘绋嬪彿
+PID=$(sudo netstat -tulnp | grep 8002 | awk '{print $7}' | cut -d'/' -f1)
+
+rm -rf /home/system/xiaozhi/xiaozhi-esp32-api.jar
+mv /home/system/xiaozhi/xiaozhi-esp32-server/main/manager-api/target/xiaozhi-esp32-api.jar /home/system/xiaozhi/xiaozhi-esp32-api.jar
+
+# 妫�鏌ユ槸鍚︽壘鍒拌繘绋嬪彿
+if [ -z "$PID" ]; then
+ echo "娌℃湁鎵惧埌鍗犵敤8002绔彛鐨勮繘绋�"
+else
+ echo "鎵惧埌鍗犵敤8002绔彛鐨勮繘绋嬶紝杩涚▼鍙蜂负: $PID"
+ # 鏉�鎺夎繘绋�
+ kill -9 $PID
+ kill -9 $PID
+ echo "宸叉潃鎺夎繘绋� $PID"
+fi
+
+nohup java -jar xiaozhi-esp32-api.jar --spring.profiles.active=dev &
+
+tail tail -f nohup.out
+```
+
+淇濆瓨濂藉悗鎵ц璧嬫潈鍛戒护
+```
+chmod 777 update_8002.sh
+```
+鎵ц瀹屽悗锛岀户缁線涓�
+
+## 4.3 鑷姩缂栬瘧杩愯Python椤圭洰
+鍦╜/home/system/xiaozhi/`鐩綍涓嬶紝鍒涘缓鍚嶅瓧涓篳update_8000.sh`鐨勬枃浠讹紝鍐呭濡備笅
+
+```
+cd /home/system/xiaozhi/xiaozhi-esp32-server
+git pull origin main
+
+# 鏌ユ壘鍗犵敤8000绔彛鐨勮繘绋嬪彿
+PID=$(sudo netstat -tulnp | grep 8000 | awk '{print $7}' | cut -d'/' -f1)
+
+# 妫�鏌ユ槸鍚︽壘鍒拌繘绋嬪彿
+if [ -z "$PID" ]; then
+ echo "娌℃湁鎵惧埌鍗犵敤8000绔彛鐨勮繘绋�"
+else
+ echo "鎵惧埌鍗犵敤8000绔彛鐨勮繘绋嬶紝杩涚▼鍙蜂负: $PID"
+ # 鏉�鎺夎繘绋�
+ kill -9 $PID
+ kill -9 $PID
+ echo "宸叉潃鎺夎繘绋� $PID"
+fi
+cd main/xiaozhi-server
+# 鍒濆鍖朿onda鐜
+source ~/.bashrc
+conda activate xiaozhi-esp32-server
+pip install -r requirements.txt
+nohup python app.py >/dev/null &
+tail -f /home/system/xiaozhi/xiaozhi-esp32-server/main/xiaozhi-server/tmp/server.log
+```
+
+淇濆瓨濂藉悗鎵ц璧嬫潈鍛戒护
+```
+chmod 777 update_8000.sh
+```
+鎵ц瀹屽悗锛岀户缁線涓�
+
+# 鏃ュ父鏇存柊
+
+浠ヤ笂鐨勮剼鏈兘寤虹珛濂藉悗锛屾棩甯告洿鏂帮紝鎴戜滑鍙渚濇鎵ц浠ヤ笅鍛戒护灏卞彲浠ュ仛鍒拌嚜鍔ㄦ洿鏂板拰鍚姩
+
+```
+cd /home/system/xiaozhi
+# 鏇存柊骞跺惎鍔↗ava绋嬪簭
+./update_8001.sh
+# 鏇存柊web绋嬪簭
+./update_8002.sh
+# 鏇存柊骞跺惎鍔╬ython绋嬪簭
+./update_8000.sh
+
+
+# 鍚庢湡鎯虫煡鐪媕ava鏃ュ織锛屾墽琛屼互涓嬪懡浠�
+tail -f nohup.out
+# 鍚庢湡鎯虫煡鐪媝ython鏃ュ織锛屾墽琛屼互涓嬪懡浠�
+tail -f /home/system/xiaozhi/xiaozhi-esp32-server/main/xiaozhi-server/tmp/server.log
+```
+
+# 娉ㄦ剰浜嬮」
+娴嬭瘯骞冲彴`https://2662r3426b.vicp.fun`锛屾槸浣跨敤nginx鍋氫簡鍙嶅悜浠g悊銆俷ginx.conf璇︾粏閰嶇疆鍙互[鍙傝�冭繖閲宂(https://github.com/xinnan-tech/xiaozhi-esp32-server/issues/791)
+
+## 甯歌闂
+
+### 1銆佷负浠�涔堟病鏈夎鍒�8001绔彛锛�
+鍥炵瓟锛�8001鏄紑鍙戠幆澧冧娇鐢ㄧ殑锛岀敤浜庤繍琛屽墠绔殑绔彛銆傚鏋滀綘鏄湇鍔″櫒閮ㄧ讲锛屼笉寤鸿浣跨敤`npm run serve`鍚姩8001绔彛杩愯鍓嶇锛岃�屾槸鍍忔湰鏁欑▼涓�鏍风紪璇戞垚html鏂囦欢锛岀劧鍚庝娇鐢╪ginx鏉ョ鐞嗚闂��
+
+### 2銆佹瘡娆℃洿鏂伴渶瑕佹洿鏂版墜鍔⊿QL璇彞鍚楋紵
+鍥炵瓟锛氫笉闇�瑕侊紝鍥犱负椤圭洰浣跨敤**Liquibase**绠$悊鏁版嵁搴撶増鏈紝浼氳嚜鍔ㄦ墽琛屾柊鐨剆ql鑴氭湰銆�
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/docs/device-call-guide.md b/xiaozhi-esp32-server/docs/device-call-guide.md
new file mode 100644
index 0000000..c3e8484
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/device-call-guide.md
@@ -0,0 +1,148 @@
+# 璁惧闂寸浉浜掑懠鍙彃浠朵娇鐢ㄦ寚鍗�
+
+## 姒傝堪
+
+璁惧鍛煎彨鍔熻兘鍏佽涓や釜宸查厤缃澶囦箣闂撮�氳繃璇煶/鏁版嵁閫氶亾杩涜鍙屽悜閫氫俊銆傝澶嘇鍛煎彨璁惧B鏃讹紝绯荤粺閫氳繃浠ヤ笅娴佺▼瀹炵幇锛�
+
+```
+璁惧A 鈫� 鎺堟潈鏍¢獙 鈫� MQTT缃戝叧 鈫� 璁惧B杩滅▼鍞ら啋 鈫� 寤虹珛杩炴帴 鈫� 閫氳瘽寤虹珛
+```
+## 浣跨敤杩欎釜鍔熻兘鐨勫墠鎻愭潯浠�
+1. 浣犲繀椤昏鏈夎嚦灏戜袱涓澶囷紝姣忎釜璁惧鍨嬪彿蹇呴』鏄痐ESP32-S3`锛屽洜涓哄彧鏈塦ESP32-S3`鎵嶆敮鎸佽繙绋嬪敜閱掑姛鑳姐��
+2. 浣犵殑璁惧蹇呴』瑕佹湁`涓や釜楹﹀厠椋巂銆備絾鏄鏋滀綘鐨勮澶囧彧鏈塦鍗曚釜楹﹀厠椋巂锛屼綘鍙槸鎯充綋楠屼竴涓嬭繖涓姛鑳斤紝涔熸槸鍙互鐨勶紝浣嗘槸浼氭湁寮虹儓鐨勫崱椤挎劅銆�
+3. 浣犲繀椤讳娇鐢╗鍏ㄦā鍧楅儴缃瞉(Deployment_all.md)鏈」鐩紝鍥犱负浣犻渶瑕乣鏅烘帶鍙癭鏉ョ鐞嗚澶囩殑鏉冮檺鍜岄�氫俊銆�
+4. 浣犲繀椤诲畨瑁呭苟閰嶇疆濂絗2026骞�5鏈�27鏃浠ュ悗鐨刐MQTT缃戝叧鏈嶅姟](mqtt-gateway-integration.md)锛屽鏋滀綘宸茬粡閮ㄧ讲浜哅QTT缃戝叧鏈嶅姟锛岃纭浠g爜鐨勭増鏈槸`2026骞�5鏈�27鏃涔嬪悗鐨勭増鏈��
+
+浠ヤ笂鏄娇鐢ㄨ繖涓姛鑳界殑纭�ф潯浠讹紝鎺ヤ笅鏉ヤ細璇︾粏浠嬬粛銆�
+
+## 閰嶇疆姝ラ
+
+### 绗竴姝ワ細寮�鍚�氳褰曞姛鑳�
+
+1. 纭浣犵殑鏅烘帶鍙扮増鏈槸`0.9.4`鎴栦互涓婄増鏈��
+2. 鐧诲綍鏅烘帶鍙板悗鍙�
+3. 杩涘叆 **绯荤粺鍔熻兘閰嶇疆**
+4. 鍦ㄥ乏渚у姛鑳藉垪琛ㄤ腑鍕鹃�� **閫氳褰�**
+5. 鐐瑰嚮 **淇濆瓨閰嶇疆** 纭
+
+### 绗簩姝ワ細閰嶇疆璁惧闂村懠鍙潈闄�
+
+1. 鍦ㄦ櫤鎺у彴椤堕儴鑿滃崟鐐瑰嚮 **閫氳褰�**
+2. 鍦ㄥ乏渚ф櫤鑳戒綋涓嬶紝璁惧鍒楄〃涓�夋嫨浣犵殑璁惧A锛堟敮鎸佹寜 MAC鍦板潃 鎴� 澶囨敞鍚� 鎼滅储锛�
+3. 鍦ㄥ彸渚ц鎯呴潰鏉夸腑锛屾壘鍒扮洰鏍囪澶嘊鐨勭О鍛艰缃紝渚嬪 **"灏忕帇"**
+4. 鍕鹃�夎澶嘊鐨� **鍛煎彨鏉冮檺** 澶嶉�夋
+5. 鐐瑰嚮 **淇濆瓨**
+
+**鍙屽悜鎺堟潈璇存槑锛�** 濡傞渶璁惧A鍜岃澶嘊浜掔浉閫氫俊锛屽繀椤诲湪涓や晶鏅烘帶鍙板垎鍒厤缃鏂规潈闄愩�備緥濡傦細
+
+- 鍦ㄨ澶嘇鐨勯厤缃腑鍕鹃�夎澶嘊 鈫� 璁惧A鍙笌璁惧B閫氫俊
+- 鍦ㄨ澶嘊鐨勯厤缃腑鍕鹃�夎澶嘇 鈫� 璁惧B鍙笌璁惧A閫氫俊
+
+### 绗笁姝ワ細鍦ㄦ櫤鑳戒綋閰嶇疆娣诲姞鍛煎彨宸ュ叿
+
+1. 鍦ㄦ櫤鎺у彴椤堕儴鑿滃崟鐐瑰嚮 **鏅鸿兘浣撶鐞�**
+2. 鍦ㄥ垰鍒氶厤缃澶囪仈绯讳汉鐨勭浉鍏虫櫤鑳戒綋涓偣鍑� **缂栬緫瑙掕壊**
+3. 鍦ㄥ彸渚ц鎯呴潰鏉夸腑锛岀偣鍑� **缂栬緫鍔熻兘**
+4. 鍕鹃�� **璁惧鍛煎彨璁惧** 宸ュ叿
+5. 鐐瑰嚮 **淇濆瓨閰嶇疆** 纭
+6. 鍦ㄥ渚у啀娆$偣鍑� **淇濆瓨閰嶇疆** 锛岄殢鍗抽噸鍚澶�
+
+### 绗洓姝ワ細鍥轰欢绔坊鍔犺繙绋嬪敜閱掑伐鍏�
+
+1. 鍦╗xiaozhi-esp32](https://github.com/78/xiaozhi-esp32) 浠g爜鐨勫熀纭�涓婂鍔犺繙绋嬪敜閱掑伐鍏稭CP锛岀増鏈敮鎸佷负2.1.0鑷�2.2.6锛�2026骞�5鏈�29鏃ョ殑鐗堟湰锛�
+2. 鍦╝pplication.h鏂囦欢涓坊鍔犺繙绋嬪敜閱掑嚱鏁板0鏄�
+ ```cpp
+ void RemoteWakeup(const std::string& reason);
+ ```
+3. 鍦╝pplication.cc鏂囦欢涓坊鍔犺繙绋嬪敜閱掑嚱鏁�
+ ```cpp
+ void Application::RemoteWakeup(const std::string& reason){
+ if (!protocol_) {
+ return;
+ }
+
+ auto state = GetDeviceState();
+
+ if (state == kDeviceStateIdle) {
+ audio_service_.EncodeWakeWord();
+
+ if (!protocol_->IsAudioChannelOpened()) {
+ SetDeviceState(kDeviceStateConnecting);
+ if (!protocol_->OpenAudioChannel()) {
+ audio_service_.EnableWakeWordDetection(true);
+ return;
+ }
+ }
+ std::string wake_word = reason;
+ #if CONFIG_USE_AFE_WAKE_WORD || CONFIG_USE_CUSTOM_WAKE_WORD
+ // Encode and send the wake word data to the server
+ while (auto packet = audio_service_.PopWakeWordPacket()) {
+ protocol_->SendAudio(std::move(packet));
+ }
+ // Set the chat state to wake word detected
+ protocol_->SendWakeWordDetected(wake_word);
+ SetListeningMode(aec_mode_ == kAecOff ? kListeningModeAutoStop : kListeningModeRealtime);
+ #else
+ // Set flag to play popup sound after state changes to listening
+ // (PlaySound here would be cleared by ResetDecoder in EnableVoiceProcessing)
+ play_popup_on_listening_ = true;
+ SetListeningMode(aec_mode_ == kAecOff ? kListeningModeAutoStop : kListeningModeRealtime);
+ #endif
+ } else if (state == kDeviceStateSpeaking) {
+ AbortSpeaking(kAbortReasonWakeWordDetected);
+ SetDeviceState(kDeviceStateIdle);
+ } else if (state == kDeviceStateActivating) {
+ SetDeviceState(kDeviceStateIdle);
+ }
+ }
+ ```
+4. 鍦╩cp_server.cc鏂囦欢涓坊鍔犺繙绋嬪敜閱掑伐鍏�
+ ```cpp
+ AddUserOnlyTool("self.remote_wakeup", "Remote wakeup function with configurable parameters",
+ PropertyList({
+ Property("reason", kPropertyTypeString, "Wakeup reason"),
+ }),
+ [this](const PropertyList& properties) -> ReturnValue {
+ std::string reason = properties["reason"].value<std::string>();
+ ESP_LOGI(TAG, "Wakeup reason=%s", reason.c_str());
+ auto& app = Application::GetInstance();
+ app.RemoteWakeup(reason);
+ return true;
+ ```
+5. 鎸夌収 [鍥轰欢缂栬瘧鐑у綍鎸囧崡](firmware-build.md) 瀹屾垚鍥轰欢鐑у綍
+6. 鏃犺浣犵殑璁惧鏄崟楹﹁繕鏄弻楹︼紝璇峰湪缂栬瘧鐜妭锛屽嬀閫夊紑鍚疉EC鍔熻兘!
+7. 鏃犺浣犵殑璁惧鏄崟楹﹁繕鏄弻楹︼紝璇峰湪缂栬瘧鐜妭锛屽嬀閫夊紑鍚疉EC鍔熻兘!
+8. 鏃犺浣犵殑璁惧鏄崟楹﹁繕鏄弻楹︼紝璇峰湪缂栬瘧鐜妭锛屽嬀閫夊紑鍚疉EC鍔熻兘!
+
+### 绗簲姝ワ細閰嶇疆MQTT缃戝叧鏈嶅姟
+
+1. 閮ㄧ讲MQTT缃戝叧鏈嶅姟锛屽弬鑰� [MQTT缃戝叧闆嗘垚鏂囨。](mqtt-gateway-integration.md)
+2. 濡傛灉宸茬粡閮ㄧ讲璇风‘璁や唬鐮佺殑鐗堟湰鏄�2026骞�5鏈�27鏃ョ殑鐗堟湰
+
+## 鍛煎彨娴佺▼璇存槑
+
+鍑嗗涓や釜璁惧锛屽湪鏅烘帶鍙颁笂闈㈤厤缃ソ閫氳鏉冮檺鍜屽湪鏅鸿兘浣撲腑娣诲姞鍛煎彨宸ュ叿涔嬪悗锛屽湪鍏朵腑涓�涓皬鏅哄璇濋偅閲屽浠栬锛氣�濆懠鍙玐XX鈥滐紝瑙傚療璁惧B鏄惁鍝嶅簲銆�
+
+## 甯歌闂
+
+### Q: 璁惧B娌℃湁鍝嶅簲鍛煎彨锛�
+
+- 妫�鏌ヨ澶嘊鏄惁鍦ㄧ嚎锛堟櫤鎺у彴璁惧鐘舵�侊級
+- 纭璁惧B鐨勫浐浠跺凡姝g‘闆嗘垚杩滅▼鍞ら啋宸ュ叿
+- 妫�鏌QTT缃戝叧杩炴帴鏄惁姝e父
+- 楠岃瘉鍙屽悜鏉冮檺閰嶇疆鏄惁瀹屾暣
+
+### Q: 鎻愮ず"鏃犲懠鍙潈闄�"锛�
+
+- 鍦ㄦ櫤鎺у彴纭璁惧A宸插嬀閫夎澶嘊鐨勫懠鍙潈闄�
+- 纭閰嶇疆宸蹭繚瀛橈紙闈炰粎淇敼鏈繚瀛橈級
+
+### Q: 濡備綍纭閫氳褰曞姛鑳藉凡寮�鍚紵
+
+- 鏅烘帶鍙伴《閮ㄨ彍鍗曞鏄剧ず"閫氳褰�"鍏ュ彛锛屽垯琛ㄧず宸插紑鍚�
+
+### Q: 鎴戝彨浠栧懠鍙�"寮犲北"锛屼絾鏄粬鑰佹槸璇嗗埆鎴�"寮犱笁"锛屾�庝箞鍔烇紵
+- 鍙互鏌ラ槄浣犱娇鐢ㄧ殑asr鏈嶅姟鐨勬枃妗o紝纭鏄惁鏀寔鐑瘝璇嗗埆銆�
+- 濡傛灉浣犵敤鐨勬槸`FunASRServer`,鍙互鍦ㄥ鍣ㄩ噷鐨刞鐑瘝鏂囦欢`閲屾坊鍔�"寮犲北"锛岀劧鍚庨噸鍚鍣ㄣ��
+- 濡傛灉浣犵敤鐨勬槸`鐏北寮曟搸`鐨勬湇鍔★紝鍙互鍦╜鐏北寮曟搸鐨勬帶鍒跺彴`閲屾坊鍔燻鐑瘝鏂囦欢`锛岀劧鍚庡洖鍒版櫤鎺у彴鐨刞妯″瀷閰嶇疆椤甸潰`锛屾妸`鐑瘝鏂囦欢鍚嶇О`閰嶇疆鍦╜鐏北寮曟搸鐨則ts`涓婂幓銆�
+
diff --git a/xiaozhi-esp32-server/docs/digital-human-wakeword.md b/xiaozhi-esp32-server/docs/digital-human-wakeword.md
new file mode 100644
index 0000000..613e615
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/digital-human-wakeword.md
@@ -0,0 +1,220 @@
+# 鏁板瓧浜篸igital-human鍚姩鏂规硶
+
+## 姒傝堪
+
+娴嬭瘯椤甸潰闆嗘垚浜嗗熀浜� **Sherpa-ONNX** 鐨勯珮绮惧害璇煶鍞ら啋鍔熻兘锛屾敮鎸佽嚜瀹氫箟鍞ら啋璇嶅拰瀹炴椂妫�娴嬨�備娇鐢ㄨ交閲忕骇鍏抽敭璇嶆娴嬫ā鍨嬶紝鎻愪緵姣绾у搷搴旈�熷害銆�
+
+## 鍞ら啋璇嶆ā鍨�
+
+### 妯″瀷涓嬭浇锛堝繀闇�锛�
+
+**閲嶈璇存槑**: 椤圭洰涓嶅寘鍚ā鍨嬫枃浠讹紝闇�瑕佹彁鍓嶄笅杞介厤缃��
+
+### 瀹樻柟妯″瀷涓嬭浇鍦板潃
+
+- **瀹樻柟妯″瀷鍒楄〃**: <https://csukuangfj.github.io/sherpa/onnx/kws/pretrained_models/index.html>
+- **鎺ㄨ崘妯″瀷**: `sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01`
+
+### 涓嬭浇鍜岄厤缃楠�
+
+#### 1. 涓嬭浇妯″瀷鍖�
+
+```bash
+# 鏂规硶1锛氱洿鎺ヤ笅杞斤紙鎺ㄨ崘锛�
+cd main/digital-human/wakeword_runtime/
+wget https://github.com/k2-fsa/sherpa-onnx/releases/download/kws-models/sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01.tar.bz2
+
+# 瑙e帇
+tar xvf sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01.tar.bz2
+
+# 鏂规硶2锛氫娇鐢∕odelScope
+pip install modelscope
+python -c "
+from modelscope import snapshot_download
+snapshot_download('pkufool/sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01', cache_dir='./models')
+"
+```
+
+#### 2. 閰嶇疆妯″瀷鏂囦欢
+
+妯″瀷鍖呬笅杞藉悗鍖呭惈浠ヤ笅鏂囦欢锛�
+
+```
+sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01/
+鈹溾攢鈹� encoder-epoch-12-avg-2-chunk-16-left-64.int8.onnx # 閫熷害浼樺厛
+鈹溾攢鈹� encoder-epoch-12-avg-2-chunk-16-left-64.onnx
+鈹溾攢鈹� encoder-epoch-99-avg-1-chunk-16-left-64.int8.onnx # 閫熷害浼樺厛
+鈹溾攢鈹� encoder-epoch-99-avg-1-chunk-16-left-64.onnx # 绮惧害浼樺厛
+鈹溾攢鈹� decoder-epoch-12-avg-2-chunk-16-left-64.onnx
+鈹溾攢鈹� decoder-epoch-99-avg-1-chunk-16-left-64.onnx # 绮惧害浼樺厛
+鈹溾攢鈹� joiner-epoch-12-avg-2-chunk-16-left-64.int8.onnx # 閫熷害浼樺厛
+鈹溾攢鈹� joiner-epoch-12-avg-2-chunk-16-left-64.onnx
+鈹溾攢鈹� joiner-epoch-99-avg-1-chunk-16-left-64.int8.onnx # 閫熷害浼樺厛
+鈹溾攢鈹� joiner-epoch-99-avg-1-chunk-16-left-64.onnx # 绮惧害浼樺厛
+鈹溾攢鈹� tokens.txt # Token鏄犲皠琛紙蹇呴渶锛�
+鈹溾攢鈹� keywords_raw.txt # 妯″瀷鍖呴噷鍙兘闄勫甫锛堝彲閫夛紝runtime 涓嶄緷璧栵級
+鈹溾攢鈹� keywords.txt # 鐜版垚鐨�
+鈹溾攢鈹� test_wavs/ # 娴嬭瘯闊抽锛堝彲閫夛級
+鈹溾攢鈹� configuration.json # 妯″瀷鍏冧俊鎭紙鍙�夛級
+鈹斺攢鈹� README.md # 璇存槑鏂囨。锛堝彲閫夛級
+```
+
+#### 3. 閫夋嫨閰嶇疆鏂规
+
+**鏂规涓�锛氱簿搴︿紭鍏堬紙鎺ㄨ崘锛�**
+
+```bash
+cd sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01
+
+# 鍒涘缓妯″瀷鐩綍
+mkdir -p ../models
+
+# 澶嶅埗绮惧害浼樺厛鐨別poch-99 fp32涓変欢濂�
+cp encoder-epoch-99-avg-1-chunk-16-left-64.onnx ../models/encoder.onnx
+cp decoder-epoch-99-avg-1-chunk-16-left-64.onnx ../models/decoder.onnx
+cp joiner-epoch-99-avg-1-chunk-16-left-64.onnx ../models/joiner.onnx
+
+# 澶嶅埗閰嶅鏂囦欢
+cp tokens.txt ../models/tokens.txt
+# keywords_raw.txt 濡傛灉妯″瀷鍖呴噷闄勫甫锛屽彲鑷淇濈暀锛況untime 涓嶄緷璧栧畠
+```
+
+**鏂规浜岋細閫熷害浼樺厛**
+
+```bash
+cd sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01
+
+# 鍒涘缓妯″瀷鐩綍
+mkdir -p ../models
+
+# 澶嶅埗閫熷害浼樺厛鐨別poch-99 int8涓変欢濂�
+cp encoder-epoch-99-avg-1-chunk-16-left-64.int8.onnx ../models/encoder.onnx
+cp decoder-epoch-99-avg-1-chunk-16-left-64.onnx ../models/decoder.onnx
+cp joiner-epoch-99-avg-1-chunk-16-left-64.int8.onnx ../models/joiner.onnx
+
+# 澶嶅埗閰嶅鏂囦欢
+cp tokens.txt ../models/tokens.txt
+```
+
+**娉ㄦ剰浜嬮」**:
+
+- **涓嶈娣风敤 fp32 涓� int8**锛氫笁涓ā鍨嬫枃浠跺繀椤讳繚鎸佷竴鑷寸殑绮惧害
+- **浼樺厛閫夋嫨 epoch-99**锛氭瘮 epoch-12 璁粌鏇村厖鍒嗭紝绮惧害鏇撮珮
+- **蹇呴渶鏂囦欢**锛歚encoder.onnx` + `decoder.onnx` + `joiner.onnx` + `tokens.txt` + `keywords.txt`
+
+### 鏈�缁堟ā鍨嬫枃浠剁粨鏋�
+
+閰嶇疆瀹屾垚鍚庯紝妯″瀷鏂囦欢搴旀斁鍦� `wakeword_runtime/models/` 鐩綍涓嬶紝瀹屾暣璺緞涓� `main/digital-human/wakeword_runtime/models/`锛�
+
+```
+wakeword_runtime/models/
+鈹溾攢鈹� encoder.onnx # 缂栫爜鍣ㄦā鍨嬶紙閲嶅懡鍚嶅悗锛�
+鈹溾攢鈹� decoder.onnx # 瑙g爜鍣ㄦā鍨嬶紙閲嶅懡鍚嶅悗锛�
+鈹溾攢鈹� joiner.onnx # 杩炴帴鍣ㄦā鍨嬶紙閲嶅懡鍚嶅悗锛�
+鈹溾攢鈹� tokens.txt # 鎷奸煶 Token 鏄犲皠琛紙228琛岀増鏈級
+鈹溾攢鈹� keywords.txt # 鍏抽敭璇嶉厤缃枃浠讹紙棣栨鍚姩鑷姩鐢熸垚锛�
+鈹斺攢鈹� keywords_raw.txt # 鍙�夛紝runtime 涓嶄緷璧�
+```
+
+## 鍚姩鏂瑰紡
+
+鍦� `main/digital-human` 鐩綍鎵ц锛�
+
+```bash
+pip install -r wakeword_runtime/requirements.txt
+python start.py
+```
+
+鍚姩鍚庨粯璁ゅ湴鍧�锛�
+
+- 椤甸潰鍦板潃锛歚http://127.0.0.1:8006/index.html`
+- 浜嬩欢妗ュ湴鍧�锛歚ws://127.0.0.1:8006/wakeword-ws`
+- 鍋ュ悍妫�鏌ワ細`http://127.0.0.1:8006/health`
+
+鍋滄鏂瑰紡锛�
+
+- 鍦ㄨ繍琛岀粓绔寜 `Ctrl+C`
+- 浼氬悓鏃跺仠姝㈤潤鎬侀〉闈㈡湇鍔°�佷簨浠舵ˉ鍜屽敜閱掕瘝妫�娴嬫祦绋�
+
+## 閰嶇疆鏂囦欢璇存槑
+
+閰嶇疆鏂囦欢浣嶄簬 [main/digital-human/wakeword_runtime/config.json](../main/digital-human/wakeword_runtime/config.json)銆�
+
+褰撳墠涓昏閰嶇疆椤癸細
+
+```json
+{
+ "wakeword": {
+ "enabled": true
+ },
+ "model_dir": "models",
+ "audio": {
+ "input_device": null,
+ "sample_rate": 16000,
+ "channels": 1
+ },
+ "detector": {
+ "num_threads": 4,
+ "provider": "cpu",
+ "max_active_paths": 2,
+ "keywords_score": 1.8,
+ "keywords_threshold": 0.1,
+ "num_trailing_blanks": 1,
+ "cooldown_seconds": 1.5
+ },
+ "logging": {
+ "level": "INFO",
+ "dir": "logs",
+ "file": "wakeword-runtime.log"
+ }
+}
+```
+
+鍚勫瓧娈靛惈涔夛細
+
+| 鍙傛暟 | 璇存槑 |
+| --- | --- |
+| `wakeword.enabled` | 鏄惁鍚敤鏈湴鍞ら啋璇嶆娴� |
+| `model_dir` | 妯″瀷鍜岃瘝琛ㄦ墍鍦ㄧ洰褰� |
+| `audio.input_device` | 楹﹀厠椋庤緭鍏ヨ澶囷紝榛樿浣跨敤绯荤粺榛樿璁惧 |
+| `audio.sample_rate` | 閲囨牱鐜囷紝榛樿 `16000` |
+| `audio.channels` | 澹伴亾鏁帮紝榛樿 `1` |
+| `detector.num_threads` | 妫�娴嬪櫒绾跨▼鏁� |
+| `detector.provider` | 鎺ㄧ悊 provider锛屽綋鍓嶉�氬父涓� `cpu` |
+| `detector.max_active_paths` | 鎼滅储璺緞鏁� |
+| `detector.keywords_score` | 鍏抽敭璇嶅寮哄垎鏁� |
+| `detector.keywords_threshold` | 妫�娴嬮槇鍊� |
+| `detector.num_trailing_blanks` | 灏鹃殢绌虹櫧鏁伴噺 |
+| `detector.cooldown_seconds` | 杩炵画瑙﹀彂鍐峰嵈鏃堕棿 |
+| `logging.level` | 鏃ュ織绛夌骇 |
+| `logging.dir` | 鏃ュ織鐩綍 |
+| `logging.file` | 鏃ュ織鏂囦欢鍚� |
+
+## 鎺ㄨ崘浣跨敤娴佺▼
+
+### 棣栨浣跨敤
+
+1. 鍑嗗 `models/` 鐩綍涓嬬殑妯″瀷鏂囦欢鍜� `tokens.txt`
+2. 纭 `models/keywords.txt` 瀛樺湪
+3. 鍦� `digital-human` 鐩綍杩愯 `python start.py`
+4. 娴忚鍣ㄦ墦寮� `http://127.0.0.1:8006/index.html`
+5. 杩涘叆璁剧疆椤垫鏌モ�滃敜閱掕瘝鈥濋厤缃�
+
+### 淇敼鍞ら啋璇�
+
+1. 鎵撳紑鏁板瓧浜洪〉闈㈣缃�
+2. 鍒囧埌鈥滃敜閱掕瘝鈥濋〉绛�
+3. 淇敼鍚敤鐘舵�佹垨鍞ら啋璇嶅垪琛�
+4. 鐐瑰嚮鈥滃簲鐢ㄥ敜閱掕瘝鈥�
+5. 鏍规嵁鎻愮ず鍐冲畾鏄惁绔嬪嵆閲嶅惎
+
+### 绂佺敤鍞ら啋璇�
+
+1. 灏嗏�滃惎鐢ㄦ湰鍦板敜閱掕瘝鈥濇敼鎴愮鐢�
+2. 鐐瑰嚮鈥滃簲鐢ㄥ敜閱掕瘝鈥�
+3. 寤鸿绔嬪嵆閲嶅惎涓�娆�
+
+绂佺敤鍚庯細
+
+- 椤甸潰涓庝簨浠舵ˉ浠嶇劧鍙敤
+- 鍞ら啋璇嶆娴嬩笉浼氱户缁繍琛�
diff --git a/xiaozhi-esp32-server/docs/docker-build.md b/xiaozhi-esp32-server/docs/docker-build.md
new file mode 100644
index 0000000..91d45fd
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/docker-build.md
@@ -0,0 +1,74 @@
+# 鏈湴缂栬瘧docker闀滃儚鏂规硶
+
+鐜板湪鏈」鐩凡缁忎娇鐢╜github`鐨刞鑷姩缂栬瘧docker闀滃儚`鍔熻兘锛屽鏋滄偍鎷夊彇鐨勬槸椤圭洰鍙戣鐨勯暅鍍忥紝鎮ㄦ病鏈夎嚜宸辩紪璇戦暅鍍忕殑闇�姹傦紝閭e氨蹇界暐鏈枃妗c��
+
+濡傛灉鎮ㄤ慨鏀逛簡婧愮爜锛岀劧鍚庢兂閲囩敤`docker`鐨勬柟寮忛儴缃茶繍琛岋紝鍙互鍙傜収浠ヤ笅姝ラ鎿嶄綔锛�
+
+## 1銆佺幆澧冨噯澶�
+
+瀹夎docker锛�
+```bash
+sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
+```
+
+## 2銆佺紪璇戦暅鍍�
+
+褰撲綘淇敼濂戒唬鐮佸悗锛岄渶瑕佺紪璇戞柊鐨勯暅鍍忔椂锛岄渶瑕佹寜鐓т互涓嬫楠ゆ搷浣滐細
+
+鍑嗗濂戒綘鐨刞浣犵殑鐢ㄦ埛鍚峘鍜宍鏂扮殑鐗堟湰鍙穈銆�
+- 杩欎釜`浣犵殑鐢ㄦ埛鍚峘鏄綘鍦╜docker hub`娉ㄥ唽鐨勭敤鎴峰悕锛屼緥濡俙xiaozhi`銆傚綋鐒讹紝濡傛灉浣犱笉闇�瑕佹帹閫佸埌`docker hub`锛屼綘鍙互鑷敱瀹氫箟銆�
+- 杩欎釜`鏂扮殑鐗堟湰鍙穈鏄綘缂栬瘧鐨勯暅鍍忕増鏈紝渚嬪`1.2.3`锛屼綘鍙互鏍规嵁闇�瑕佽嚜瀹氫箟鎴栬�呬娇鐢ㄦ棩鏈熸牸寮忥紙渚嬪`20260609`锛変富瑕佹槸鏂逛究鍜岀幇鍦ㄨ繍琛岀殑鐗堟湰鍙峰仛鍖哄垎锛屽悓鏃朵篃鏂逛究涓嬫鍥炲繂浣犳槸浠�涔堟椂鍊欐瀯寤虹殑锛屼笉瑕佸拰鐜板湪浣犳湰鏈鸿繍琛岀殑鐗堟湰鍙风浉鍚屻��
+
+杩涘叆`xiaozhi-esp32-server`椤圭洰鏍圭洰褰曪紝缂栬瘧 server 鍜� web 涓や釜闀滃儚锛�
+
+```bash
+cd 椤圭洰鏍圭洰褰�
+
+# 缂栬瘧server闀滃儚
+docker build -f Dockerfile-server -t 浣犵殑鐢ㄦ埛鍚�/xiaozhi-esp32-server:鏂扮殑鐗堟湰鍙� .
+
+# 缂栬瘧web闀滃儚
+docker build -f Dockerfile-web -t 浣犵殑鐢ㄦ埛鍚�/xiaozhi-esp32-server-web:鏂扮殑鐗堟湰鍙� .
+
+```
+
+## 3銆佷慨鏀筪ocker-compose閰嶇疆
+
+```bash
+cd main/xiaozhi-server
+```
+
+缂栬緫 `docker-compose_all.yml` 鏂囦欢锛屽皢闀滃儚鐗堟湰鏇挎崲涓轰綘鍒氭墠缂栬瘧鐨勭増鏈細
+
+```yaml
+services:
+ xiaozhi-esp32-server:
+ image: 浣犵殑鐢ㄦ埛鍚�/xiaozhi-esp32-server:鏂扮殑鐗堟湰鍙� # 淇敼涓轰綘鐨勯暅鍍忓湴鍧�
+ ...
+
+ xiaozhi-esp32-server-web:
+ image: 浣犵殑鐢ㄦ埛鍚�/xiaozhi-esp32-server-web:鏂扮殑鐗堟湰鍙� #淇敼涓轰綘鐨勯暅鍍忓湴鍧�
+ ...
+```
+
+## 4銆侀噸鍚湇鍔�
+
+```bash
+# 鍋滄鏃у鍣�
+docker compose -f docker-compose_all.yml down
+
+# 鍚姩鏂板鍣�
+docker compose -f docker-compose_all.yml up -d
+```
+
+## 5銆侀獙璇�
+
+鏌ョ湅鏃ュ織纭鏈嶅姟鍚姩姝e父锛�
+
+```bash
+# 鏌ョ湅server鏃ュ織
+docker logs -f -n 50 xiaozhi-esp32-server
+
+# 鏌ョ湅web鏃ュ織
+docker logs -f -n 50 xiaozhi-esp32-server-web
+```
diff --git a/xiaozhi-esp32-server/docs/docker/nginx.conf b/xiaozhi-esp32-server/docs/docker/nginx.conf
new file mode 100644
index 0000000..ca3bac3
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/docker/nginx.conf
@@ -0,0 +1,53 @@
+user root;
+worker_processes 4;
+
+events {
+ worker_connections 1024;
+}
+
+http {
+ include mime.types;
+ default_type application/octet-stream;
+ sendfile on;
+ keepalive_timeout 300;
+ client_header_timeout 180s;
+ client_body_timeout 180s;
+ client_max_body_size 1024M;
+
+ gzip on;
+ gzip_buffers 32 4K;
+ gzip_comp_level 6;
+ gzip_min_length 100;
+ gzip_types application/javascript text/css text/xml image/jpeg image/gif image/png;
+ gzip_disable "MSIE [1-6]\.";
+ gzip_vary on;
+
+ server {
+ # 鏃犲煙鍚嶈闂紝灏辩敤localhost
+ server_name localhost;
+ # 80绔彛
+ listen 8002;
+
+ # 杞彂鍒扮紪璇戝悗鍒皐eb鐩綍
+ location / {
+ root /usr/share/nginx/html;
+ try_files $uri $uri/ /index.html;
+ }
+
+ # 杞彂鍒癿anager-api
+ location /xiaozhi/ {
+ proxy_pass http://127.0.0.1:8003;
+ proxy_set_header Host $host;
+ proxy_cookie_path /manager/ /;
+ proxy_set_header Referer $http_referer;
+ proxy_set_header Cookie $http_cookie;
+
+ proxy_connect_timeout 15;
+ proxy_send_timeout 15;
+ proxy_read_timeout 15;
+
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ }
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/docs/docker/start.sh b/xiaozhi-esp32-server/docs/docker/start.sh
new file mode 100644
index 0000000..a64f46b
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/docker/start.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+# 鍚姩Java鍚庣锛坉ocker鍐呯洃鍚�8003绔彛锛�
+java -jar /app/xiaozhi-esp32-api.jar \
+ --server.port=8003 \
+ --spring.datasource.druid.url=${SPRING_DATASOURCE_DRUID_URL} \
+ --spring.datasource.druid.username=${SPRING_DATASOURCE_DRUID_USERNAME} \
+ --spring.datasource.druid.password=${SPRING_DATASOURCE_DRUID_PASSWORD} \
+ --spring.data.redis.host=${SPRING_DATA_REDIS_HOST} \
+ --spring.data.redis.password=${SPRING_DATA_REDIS_PASSWORD} \
+ --spring.data.redis.port=${SPRING_DATA_REDIS_PORT} &
+
+# 鍚姩Nginx锛堝墠鍙拌繍琛屼繚鎸佸鍣ㄥ瓨娲伙級
+nginx -g 'daemon off;'
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/docs/firmware-build.md b/xiaozhi-esp32-server/docs/firmware-build.md
new file mode 100644
index 0000000..2e27446
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/firmware-build.md
@@ -0,0 +1,138 @@
+# esp32鍥轰欢缂栬瘧
+
+## 绗�1姝� 鍑嗗浣犵殑ota鍦板潃
+
+濡傛灉浣狅紝浣跨敤鐨勬槸鏈」鐩�0.3.12鐗堟湰锛屼笉绠℃槸绠�鍗昐erver閮ㄧ讲杩樻槸鍏ㄦā鍧楅儴缃诧紝閮戒細鏈塷ta鍦板潃銆�
+
+鐢变簬绠�鍗昐erver閮ㄧ讲鍜屽叏妯″潡閮ㄧ讲鐨凮TA鍦板潃璁剧疆鏂瑰紡涓嶄竴鏍凤紝璇蜂綘閫夋嫨涓嬮潰鐨勫叿浣撴柟寮忥細
+
+### 濡傛灉浣犵敤鐨勬槸绠�鍗昐erver閮ㄧ讲
+姝ゅ埢锛岃浣犵敤娴忚鍣ㄦ墦寮�浣犵殑ota鍦板潃锛屼緥濡傛垜鐨刼ta鍦板潃
+```
+http://192.168.1.25:8003/xiaozhi/ota/
+```
+濡傛灉鏄剧ず鈥淥TA鎺ュ彛杩愯姝e父锛屽悜璁惧鍙戦�佺殑websocket鍦板潃鏄細ws://xxx:8000/xiaozhi/v1/
+
+浣犲彲浠ュ惎鍔╜digital-human`妯″潡鍚庢墦寮�`index.html`娴嬭瘯涓�涓嬶紝鏄惁鑳借繛涓妎ta椤甸潰杈撳嚭鐨剋ebsocket鍦板潃銆�
+
+濡傛灉璁块棶涓嶅埌锛屼綘闇�瑕佸埌閰嶇疆鏂囦欢`.config.yaml`閲屼慨鏀筦server.websocket`鐨勫湴鍧�锛岄噸鍚悗鍐嶉噸鏂版祴璇曪紝鐩村埌`index.html`鑳芥甯歌闂��
+
+鎴愬姛鍚庯紝璇峰線涓嬭繘琛岀2姝�
+
+### 濡傛灉浣犵敤鐨勬槸鍏ㄦā鍧楅儴缃�
+姝ゅ埢锛岃浣犵敤娴忚鍣ㄦ墦寮�浣犵殑ota鍦板潃锛屼緥濡傛垜鐨刼ta鍦板潃
+```
+http://192.168.1.25:8002/xiaozhi/ota/
+```
+
+濡傛灉鏄剧ず鈥淥TA鎺ュ彛杩愯姝e父锛寃ebsocket闆嗙兢鏁伴噺锛歑鈥濄�傞偅灏卞線涓嬭繘琛�2姝ャ��
+
+濡傛灉鏄剧ず鈥淥TA鎺ュ彛杩愯涓嶆甯糕�濓紝澶ф鏄綘杩樻病鍦╜鏅烘帶鍙癭閰嶇疆`Websocket`鍦板潃銆傞偅灏憋細
+
+- 1銆佷娇鐢ㄨ秴绾х鐞嗗憳鐧诲綍鏅烘帶鍙�
+
+- 2銆侀《閮ㄨ彍鍗曠偣鍑籤鍙傛暟绠$悊`
+
+- 3銆佸湪鍒楄〃涓壘鍒癭server.websocket`椤圭洰锛岃緭鍏ヤ綘鐨刞Websocket`鍦板潃銆備緥濡傛垜鐨勫氨鏄�
+
+```
+ws://192.168.1.25:8000/xiaozhi/v1/
+```
+
+閰嶇疆瀹屽悗锛屽啀浣跨敤娴忚鍣ㄥ埛鏂颁綘鐨刼ta鎺ュ彛鍦板潃锛岀湅鐪嬫槸涓嶆槸姝e父浜嗐�傚鏋滆繕涓嶆甯稿氨锛屽氨鍐嶆纭涓�涓媁ebsocket鏄惁姝e父鍚姩锛屾槸鍚﹂厤缃簡Websocket鍦板潃銆�
+
+## 绗�2姝� 閰嶇疆鐜
+鍏堟寜鐓ц繖涓暀绋嬮厤缃」鐩幆澧僛銆奧indows鎼缓 ESP IDF 5.3.2寮�鍙戠幆澧冧互鍙婄紪璇戝皬鏅恒�媇(https://icnynnzcwou8.feishu.cn/wiki/JEYDwTTALi5s2zkGlFGcDiRknXf)
+
+## 绗�3姝� 鎵撳紑閰嶇疆鏂囦欢
+閰嶇疆濂界紪璇戠幆澧冨悗锛屼笅杞借櫨鍝aozhi-esp32椤圭洰婧愮爜锛�
+
+浠庤繖閲屼笅杞借櫨鍝xiaozhi-esp32椤圭洰婧愮爜](https://github.com/78/xiaozhi-esp32)銆�
+
+涓嬭浇鍚庯紝鎵撳紑`xiaozhi-esp32/main/Kconfig.projbuild`鏂囦欢銆�
+
+## 绗�4姝� 淇敼OTA鍦板潃
+
+鎵惧埌`OTA_URL`鐨刞default`鐨勫唴瀹癸紝鎶奰https://api.tenclass.net/xiaozhi/ota/`
+ 鏀规垚浣犺嚜宸辩殑鍦板潃锛屼緥濡傦紝鎴戠殑鎺ュ彛鍦板潃鏄痐http://192.168.1.25:8002/xiaozhi/ota/`锛屽氨鎶婂唴瀹规敼鎴愯繖涓��
+
+淇敼鍓嶏細
+```
+config OTA_URL
+ string "Default OTA URL"
+ default "https://api.tenclass.net/xiaozhi/ota/"
+ help
+ The application will access this URL to check for new firmwares and server address.
+```
+淇敼鍚庯細
+```
+config OTA_URL
+ string "Default OTA URL"
+ default "http://192.168.1.25:8002/xiaozhi/ota/"
+ help
+ The application will access this URL to check for new firmwares and server address.
+```
+
+## 绗�4姝� 璁剧疆缂栬瘧鍙傛暟
+
+璁剧疆缂栬瘧鍙傛暟
+
+```
+# 缁堢鍛戒护琛岃繘鍏iaozhi-esp32鐨勬牴鐩綍
+cd xiaozhi-esp32
+# 渚嬪鎴戜娇鐢ㄧ殑鏉垮瓙鏄痚sp32s3锛屾墍浠ヨ缃紪璇戠洰鏍囦负esp32s3锛屽鏋滀綘鐨勬澘瀛愭槸鍏朵粬鍨嬪彿锛岃鏇挎崲鎴愬搴旂殑鍨嬪彿
+idf.py set-target esp32s3
+# 杩涘叆鑿滃崟閰嶇疆
+idf.py menuconfig
+```
+
+杩涘叆鑿滃崟閰嶇疆鍚庯紝鍐嶈繘鍏Xiaozhi Assistant`锛屽皢`BOARD_TYPE`璁剧疆浣犳澘瀛愮殑鍏蜂綋鍨嬪彿
+淇濆瓨閫�鍑猴紝鍥炲埌缁堢鍛戒护琛屻��
+
+## 绗�5姝� 缂栬瘧鍥轰欢
+
+```
+idf.py build
+```
+
+## 绗�6姝� 鎵撳寘bin鍥轰欢
+
+```
+cd scripts
+python release.py
+```
+
+涓婇潰鐨勬墦鍖呭懡浠ゆ墽琛屽畬鎴愬悗锛屼細鍦ㄩ」鐩牴鐩綍涓嬬殑`build`鐩綍涓嬬敓鎴愬浐浠舵枃浠禶merged-binary.bin`銆�
+杩欎釜`merged-binary.bin`灏辨槸瑕佺儳褰曞埌纭欢涓婄殑鍥轰欢鏂囦欢銆�
+
+娉ㄦ剰锛氬鏋滄墽琛屽埌绗簩鍛戒护鍚庯紝鎶ヤ簡鈥渮ip鈥濈浉鍏崇殑閿欒锛岃蹇界暐杩欎釜閿欒锛屽彧瑕乣build`鐩綍涓嬬敓鎴愬浐浠舵枃浠禶merged-binary.bin`
+锛屽浣犳病鏈夊お澶у奖鍝嶏紝璇风户缁��
+
+## 绗�7姝� 鐑у綍鍥轰欢
+ 灏唀sp32璁惧杩炴帴鐢佃剳锛屼娇鐢╟hrome娴忚鍣紝鎵撳紑浠ヤ笅缃戝潃
+
+```
+https://espressif.github.io/esp-launchpad/
+```
+
+鎵撳紑杩欎釜鏁欑▼锛孾Flash宸ュ叿/Web绔儳褰曞浐浠讹紙鏃營DF寮�鍙戠幆澧冿級](https://ccnphfhqs21z.feishu.cn/wiki/Zpz4wXBtdimBrLk25WdcXzxcnNS)銆�
+缈诲埌锛歚鏂瑰紡浜岋細ESP-Launchpad 娴忚鍣╓EB绔儳褰昤锛屼粠`3. 鐑у綍鍥轰欢/涓嬭浇鍒板紑鍙戞澘`寮�濮嬶紝鎸夌収鏁欑▼鎿嶄綔銆�
+
+鐑у綍鎴愬姛涓旇仈缃戞垚鍔熷悗锛岄�氳繃鍞ら啋璇嶅敜閱掑皬鏅猴紝鐣欐剰server绔緭鍑虹殑鎺у埗鍙颁俊鎭��
+
+## 甯歌闂
+浠ヤ笅鏄竴浜涘父瑙侀棶棰橈紝渚涘弬鑰冿細
+
+[1銆佷负浠�涔堟垜璇寸殑璇濓紝灏忔櫤璇嗗埆鍑烘潵寰堝闊╂枃銆佹棩鏂囥�佽嫳鏂嘳(./FAQ.md)
+
+[2銆佷负浠�涔堜細鍑虹幇鈥淭TS 浠诲姟鍑洪敊 鏂囦欢涓嶅瓨鍦ㄢ�濓紵](./FAQ.md)
+
+[3銆乀TS 缁忓父澶辫触锛岀粡甯歌秴鏃禲(./FAQ.md)
+
+[4銆佷娇鐢╓ifi鑳借繛鎺ヨ嚜寤烘湇鍔″櫒锛屼絾鏄�4G妯″紡鍗存帴涓嶄笂](./FAQ.md)
+
+[5銆佸浣曟彁楂樺皬鏅哄璇濆搷搴旈�熷害锛焆(./FAQ.md)
+
+[6銆佹垜璇磋瘽寰堟參锛屽仠椤挎椂灏忔櫤鑰佹槸鎶㈣瘽](./FAQ.md)
+
+[7銆佹垜鎯抽�氳繃灏忔櫤鎺у埗鐢电伅銆佺┖璋冦�佽繙绋嬪紑鍏虫満绛夋搷浣淽(./FAQ.md)
diff --git a/xiaozhi-esp32-server/docs/firmware-setting.md b/xiaozhi-esp32-server/docs/firmware-setting.md
new file mode 100644
index 0000000..0ed350a
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/firmware-setting.md
@@ -0,0 +1,54 @@
+# 鍩轰簬铏惧摜缂栬瘧濂界殑鍥轰欢閰嶇疆鑷畾涔夋湇鍔″櫒
+
+## 绗�1姝� 纭鐗堟湰
+鐑у綍铏惧摜宸茬粡缂栬瘧濂界殑[1.6.1鐗堟湰浠ヤ笂鍥轰欢](https://github.com/78/xiaozhi-esp32/releases)
+
+## 绗�2姝� 鍑嗗浣犵殑ota鍦板潃
+濡傛灉浣犳寜鐓ф暀绋嬩娇鐢ㄧ殑鏄叏妯″潡閮ㄧ讲锛屽氨搴旇浼氭湁ota鍦板潃銆�
+
+姝ゅ埢锛岃浣犵敤娴忚鍣ㄦ墦寮�浣犵殑ota鍦板潃锛屼緥濡傛垜鐨刼ta鍦板潃
+```
+https://2662r3426b.vicp.fun/xiaozhi/ota/
+```
+
+濡傛灉鏄剧ず鈥淥TA鎺ュ彛杩愯姝e父锛寃ebsocket闆嗙兢鏁伴噺锛歑鈥濄�傞偅灏卞線涓嬨��
+
+濡傛灉鏄剧ず鈥淥TA鎺ュ彛杩愯涓嶆甯糕�濓紝澶ф鏄綘杩樻病鍦╜鏅烘帶鍙癭閰嶇疆`Websocket`鍦板潃銆傞偅灏憋細
+
+- 1銆佷娇鐢ㄨ秴绾х鐞嗗憳鐧诲綍鏅烘帶鍙�
+
+- 2銆侀《閮ㄨ彍鍗曠偣鍑籤鍙傛暟绠$悊`
+
+- 3銆佸湪鍒楄〃涓壘鍒癭server.websocket`椤圭洰锛岃緭鍏ヤ綘鐨刞Websocket`鍦板潃銆備緥濡傛垜鐨勫氨鏄�
+
+```
+wss://2662r3426b.vicp.fun/xiaozhi/v1/
+```
+
+閰嶇疆瀹屽悗锛屽啀浣跨敤娴忚鍣ㄥ埛鏂颁綘鐨刼ta鎺ュ彛鍦板潃锛岀湅鐪嬫槸涓嶆槸姝e父浜嗐�傚鏋滆繕涓嶆甯稿氨锛屽氨鍐嶆纭涓�涓媁ebsocket鏄惁姝e父鍚姩锛屾槸鍚﹂厤缃簡Websocket鍦板潃銆�
+
+## 绗�3姝� 杩涘叆閰嶇綉妯″紡
+杩涘叆鏈哄櫒鐨勯厤缃戞ā寮忥紝鍦ㄩ〉闈㈤《閮紝鐐瑰嚮鈥滈珮绾ч�夐」鈥濓紝鍦ㄩ噷闈㈣緭鍏ヤ綘鏈嶅姟鍣ㄧ殑`ota`鍦板潃锛岀偣鍑讳繚瀛樸�傞噸鍚澶�
+
+
+## 绗�4姝� 鍞ら啋灏忔櫤锛屾煡鐪嬫棩蹇楄緭鍑�
+
+鍞ら啋灏忔櫤锛岀湅鐪嬫棩蹇楁槸涓嶆槸姝e父杈撳嚭銆�
+
+
+## 甯歌闂
+浠ヤ笅鏄竴浜涘父瑙侀棶棰橈紝渚涘弬鑰冿細
+
+[1銆佷负浠�涔堟垜璇寸殑璇濓紝灏忔櫤璇嗗埆鍑烘潵寰堝闊╂枃銆佹棩鏂囥�佽嫳鏂嘳(./FAQ.md)
+
+[2銆佷负浠�涔堜細鍑虹幇鈥淭TS 浠诲姟鍑洪敊 鏂囦欢涓嶅瓨鍦ㄢ�濓紵](./FAQ.md)
+
+[3銆乀TS 缁忓父澶辫触锛岀粡甯歌秴鏃禲(./FAQ.md)
+
+[4銆佷娇鐢╓ifi鑳借繛鎺ヨ嚜寤烘湇鍔″櫒锛屼絾鏄�4G妯″紡鍗存帴涓嶄笂](./FAQ.md)
+
+[5銆佸浣曟彁楂樺皬鏅哄璇濆搷搴旈�熷害锛焆(./FAQ.md)
+
+[6銆佹垜璇磋瘽寰堟參锛屽仠椤挎椂灏忔櫤鑰佹槸鎶㈣瘽](./FAQ.md)
+
+[7銆佹垜鎯抽�氳繃灏忔櫤鎺у埗鐢电伅銆佺┖璋冦�佽繙绋嬪紑鍏虫満绛夋搷浣淽(./FAQ.md)
diff --git a/xiaozhi-esp32-server/docs/fish-speech-integration.md b/xiaozhi-esp32-server/docs/fish-speech-integration.md
new file mode 100644
index 0000000..b916e61
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/fish-speech-integration.md
@@ -0,0 +1,72 @@
+鐧诲綍AutoDL锛岀璧侀暅鍍�
+閫夋嫨闀滃儚:
+```
+PyTorch / 2.1.0 / 3.10(ubuntu22.04) / cuda 12.1
+```
+
+鏈哄櫒寮�鏈哄悗锛岃缃鏈姞閫�
+```
+source /etc/network_turbo
+```
+
+杩涘叆宸ヤ綔鐩綍
+```
+cd autodl-tmp/
+```
+
+鎷夊彇椤圭洰
+```
+git clone https://gitclone.com/github.com/fishaudio/fish-speech.git ; cd fish-speech
+```
+
+瀹夎渚濊禆
+```
+pip install -e.
+```
+
+濡傛灉鎶ラ敊锛屽畨瑁卲ortaudio
+```
+apt-get install portaudio19-dev -y
+```
+
+瀹夎鍚庢墽琛�
+```
+pip install torch==2.3.1 torchvision==0.18.1 torchaudio==2.3.1 --index-url https://download.pytorch.org/whl/cu121
+```
+
+涓嬭浇妯″瀷
+```
+cd tools
+python download_models.py
+```
+
+涓嬭浇瀹屾ā鍨嬪悗杩愯鎺ュ彛
+```
+python -m tools.api_server --listen 0.0.0.0:6006
+```
+
+鐒跺悗鐢ㄦ祻瑙堝櫒鍘诲埌aotodl瀹炰緥椤甸潰
+```
+https://autodl.com/console/instance/list
+```
+
+濡備笅鍥剧偣鍑讳綘鍒氭墠鏈哄櫒鐨刞鑷畾涔夋湇鍔鎸夐挳锛屽紑鍚鍙h浆鍙戞湇鍔�
+
+
+
+濡傛灉浣犳槸鍗曟ā鍧楅儴缃诧紝鏍稿績閰嶇疆濡備笅
+```
+selected_module:
+ TTS: FishSpeech
+TTS:
+ FishSpeech:
+ reference_audio: ["config/assets/wakeup_words.wav",]
+ reference_text: ["鍝堝暟鍟婏紝鎴戞槸灏忔櫤鍟︼紝澹伴煶濂藉惉鐨勫彴婀惧コ瀛╀竴鏋氾紝瓒呭紑蹇冭璇嗕綘鑰讹紝鏈�杩戝湪蹇欏暐锛屽埆蹇樹簡缁欐垜鏉ョ偣鏈夎叮鐨勬枡鍝︼紝鎴戣秴鐖卞惉鍏崷鐨勫暒",]
+ api_key: "123"
+ api_url: "http://127.0.0.1:6006/v1/tts"
+```
+
+鐒跺悗閲嶅惎鏈嶅姟
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/docs/homeassistant-integration.md b/xiaozhi-esp32-server/docs/homeassistant-integration.md
new file mode 100644
index 0000000..c6e26ba
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/homeassistant-integration.md
@@ -0,0 +1,226 @@
+# 灏忔櫤ESP32-寮�婧愭湇鍔$涓嶩omeAssistant闆嗘垚鎸囧崡
+
+[TOC]
+
+-----
+
+## 绠�浠�
+
+鏈枃妗e皢鎸囧鎮ㄥ浣曞皢ESP32璁惧涓嶩omeAssistant杩涜闆嗘垚銆�
+
+## 鍓嶆彁鏉′欢
+
+- 宸插畨瑁呭苟閰嶇疆濂絗HomeAssistant`
+- 鏈鎴戦�夋嫨鐨勬ā鍨嬫槸锛氬厤璐圭殑ChatGLM锛屽畠鏀寔functioncall鍑芥暟璋冪敤
+
+## 寮�濮嬪墠鐨勬搷浣滐紙蹇呰锛�
+
+### 1. 鑾峰彇HA鐨勭綉缁滅綉缁滃湴鍧�淇℃伅
+
+璇疯闂綘Home Assistant鐨勭綉缁滃湴鍧�锛屼緥濡傦紝鎴戠殑HA鐨勫湴鍧�鏄�192.168.4.7锛岀鍙e垯鏄粯璁ょ殑8123锛屽垯鍦ㄦ祻瑙堝櫒鎵撳紑
+
+```
+http://192.168.4.7:8123
+```
+
+> 鎵嬪姩鏌ヨ HA 鐨� IP 鍦板潃鏂规硶**锛堜粎闄愬皬鏅篹sp32-server鍜孒A閮ㄧ讲鍦ㄥ悓涓�涓綉缁滆澶嘯渚嬪鍚屼竴涓獁ifi]涓嬶級**锛�
+>
+> 1. 杩涘叆 Home Assistant锛堝墠绔級銆�
+>
+> 2. 鐐瑰嚮宸︿笅瑙� **璁剧疆锛圫ettings锛�** 鈫� **绯荤粺锛圫ystem锛�** 鈫� **缃戠粶锛圢etwork锛�**銆�
+>
+> 3. 婊戝埌鏈�搴曢儴`Home Assistant 缃戝潃(Home Assistant website)`鍖哄煙锛屽湪`鏈湴缃戠粶(local network)`涓紝鐐瑰嚮`鐪肩潧`鎸夐挳锛屽彲浠ョ湅鍒板綋鍓嶄娇鐢ㄧ殑 IP 鍦板潃锛堝 `192.168.1.10`锛夊拰缃戠粶鎺ュ彛銆傜偣鍑籤澶嶅埗杩炴帴(copy link)`鍙互鐩存帴澶嶅埗銆�
+>
+> 
+
+鎴栵紝鎮ㄥ凡缁忚缃簡鐩存帴鍙互璁块棶鐨凥ome Assistant鐨凮Auth鍦板潃锛屾偍涔熷彲浠ュ湪娴忚鍣ㄥ唴鐩存帴璁块棶
+
+```
+http://homeassistant.local:8123
+```
+
+### 2. 鐧诲綍`Home Assistant`鎷垮埌寮�鍙戝瘑閽�
+
+鐧诲綍`HomeAssistant`锛岀偣鍑籤宸︿笅瑙掑ご鍍� -> 涓汉`锛屽垏鎹瀹夊叏`瀵艰埅鏍忥紝鍒掑埌搴曢儴`闀挎湡璁块棶浠ょ墝`鐢熸垚api_key锛屽苟澶嶅埗淇濆瓨锛屽悗缁殑鏂规硶閮介渶瑕佷娇鐢ㄨ繖涓猘pi key涓斾粎鍑虹幇涓�娆★紙灏弔ips: 鎮ㄥ彲浠ヤ繚瀛樼敓鎴愮殑浜岀淮鐮佸浘鍍忥紝鍚庣画鍙互鎵弿浜岀淮鐮佸啀姝ゆ彁鍙朼pi key锛夈��
+
+## 鏂规硶1锛氬皬鏅虹ぞ鍖哄叡寤虹殑HA璋冪敤鍔熻兘
+
+### 鍔熻兘鎻忚堪
+
+- 濡傛偍鍚庣画闇�瑕佸鍔犳柊鐨勮澶囷紝璇ユ柟娉曢渶瑕佹墜鍔ㄩ噸鍚痐xiaozhi-esp32-server鏈嶅姟绔痐浠ユ鏇存柊璁惧淇℃伅**锛堥噸瑕�**锛夈��
+
+- 闇�瑕佹偍纭繚宸茬粡鍦℉omeAssistant涓泦鎴恅Xiaomi Home`锛屽苟灏嗙背瀹剁殑璁惧瀵煎叆杩沗HomeAssistant`銆�
+
+- 闇�瑕佹偍纭繚`xiaozhi-esp32-server鏅烘帶鍙癭鑳芥甯镐娇鐢ㄣ��
+
+- 鎴戠殑`xiaozhi-esp32-server鏅烘帶鍙癭鍜宍HomeAssistant`閮ㄧ讲鍦ㄥ悓涓�鍙版満鍣ㄧ殑鍙︿竴涓鍙o紝鐗堟湰鏄痐0.3.10`
+
+ ```
+ http://192.168.4.7:8002
+ ```
+
+
+### 閰嶇疆姝ラ
+
+#### 1. 鐧诲綍`HomeAssistant`鏁寸悊闇�瑕佹帶鍒剁殑璁惧娓呭崟
+
+鐧诲綍`HomeAssistant`锛岀偣鍑籤宸︿笅瑙掔殑璁剧疆`锛岀劧鍚庤繘鍏璁惧涓庢湇鍔锛屽啀鐐瑰嚮椤堕儴鐨刞瀹炰綋`銆�
+
+鐒跺悗鍦ㄥ疄浣撲腑鎼滅储浣犵浉鍏虫帶鍒剁殑寮�鍏筹紝缁撴灉鍑烘潵鍚庯紝鍦ㄥ垪琛ㄤ腑锛岀偣鍑诲叾涓竴涓粨鏋滐紝杩欐槸浼氬嚭鐜颁竴涓紑鍏崇殑鐣岄潰銆�
+
+鍦ㄥ紑鍏崇殑鐣岄潰锛屾垜浠皾璇曠偣鍑诲紑鍏筹紝鐪嬬湅鏄紑鍙戜細闅忕潃鎴戜滑鐨勭偣鍑诲紑/鍏炽�傚鏋滆兘鎿嶄綔锛岃鏄庢槸姝e父鑱旂綉鐨勩��
+
+鎺ョ潃鍦ㄥ紑鍏抽潰鏉挎壘鍒拌缃寜閽紝鐐瑰嚮鍚庯紝鍙互鏌ョ湅杩欎釜寮�鍏崇殑`瀹炰綋鏍囪瘑绗銆�
+
+鎴戜滑鎵撳紑涓�涓浜嬫湰锛屾寜鐓ц繖鏍锋牸寮忔暣鐞嗕竴鏉℃暟鎹細
+
+浣嶇疆+鑻辨枃閫楀彿+璁惧鍚嶇О+鑻辨枃閫楀彿+`瀹炰綋鏍囪瘑绗+鑻辨枃鍒嗗彿
+
+渚嬪锛屾垜鍦ㄥ叕鍙革紝鎴戞湁涓�涓帺鍏风伅锛屼粬鐨勬爣璇嗙鏄痵witch.cuco_cn_460494544_cp1_on_p_2_1锛岄偅涔堝氨杩欎釜鍐欒繖涓�鏉℃暟鎹�
+
+```
+鍏徃,鐜╁叿鐏�,switch.cuco_cn_460494544_cp1_on_p_2_1;
+```
+
+褰撶劧鏈�鍚庢垜鍙兘瑕佹搷浣滀袱涓伅锛屾垜鐨勬渶缁堢殑缁撴灉鏄細
+
+```
+鍏徃,鐜╁叿鐏�,switch.cuco_cn_460494544_cp1_on_p_2_1;
+鍏徃,鍙扮伅,switch.iot_cn_831898993_socn1_on_p_2_1;
+```
+
+杩欐瀛楃锛屾垜浠О涓衡�滆澶囨竻鍗曞瓧绗︹�濋渶瑕佷繚瀛樺ソ锛岀瓑涓�涓嬫湁鐢ㄣ��
+
+#### 2. 鐧诲綍`鏅烘帶鍙癭
+
+
+
+浣跨敤绠$悊鍛樿处鍙凤紝鐧诲綍`鏅烘帶鍙癭銆傚湪`鏅鸿兘浣撶鐞哷锛屾壘鍒颁綘鐨勬櫤鑳戒綋锛屽啀鐐瑰嚮`閰嶇疆瑙掕壊`銆�
+
+灏嗘剰鍥捐瘑鍒缃垚`澶栨寕鐨勫ぇ妯″瀷鎰忓浘璇嗗埆`鎴朻澶фā鍨嬭嚜涓诲嚱鏁拌皟鐢╜銆傝繖鏃朵綘浼氱湅鍒板彸渚ф湁涓�涓猔缂栬緫鍔熻兘`銆傜偣鍑籤缂栬緫鍔熻兘`鎸夐挳锛屼細寮瑰嚭`鍔熻兘绠$悊`鐨勬銆�
+
+鍦╜鍔熻兘绠$悊`鐨勬閲岋紝浣犻渶瑕佸嬀閫塦HomeAssistant璁惧鐘舵�佹煡璇鍜宍HomeAssistant璁惧鐘舵�佷慨鏀筦銆�
+
+鍕鹃�夊悗锛屽湪`宸查�夊姛鑳絗鐐瑰嚮`HomeAssistant璁惧鐘舵�佹煡璇锛岀劧鍚庡湪`鍙傛暟閰嶇疆`閲岄厤缃綘鐨刞HomeAssistant`鍦板潃銆佸瘑閽ャ�佽澶囨竻鍗曞瓧绗︺��
+
+缂栬緫濂藉悗锛岀偣鍑籤淇濆瓨閰嶇疆`锛岃繖鏃禶鍔熻兘绠$悊`鐨勬浼氶殣钘忥紝杩欐椂浣犲啀鐐瑰嚮淇濆瓨鏅鸿兘浣撻厤缃��
+
+淇濆瓨鎴愬姛鍚庯紝鍗冲彲鍞ら啋璁惧鎿嶄綔銆�
+
+#### 3. 鍞ら啋璁惧埆杩涜鎺у埗
+
+灏濊瘯鍜宔sp32璇达紝鈥滄墦寮�XXX鐏��
+
+## 鏂规硶2锛氬皬鏅哄皢Home Assistant鐨勮闊冲姪鎵嬩綔涓篖LM宸ュ叿
+
+### 鍔熻兘鎻忚堪
+
+- 璇ユ柟娉曟湁涓�涓瘮杈冧弗閲嶇殑缂虹偣鈥斺��**璇ユ柟娉曟棤娉曚娇鐢ㄥ皬鏅哄紑婧愮敓鎬佺殑function_call鎻掍欢鍔熻兘鐨勮兘鍔�**锛屽洜涓轰娇鐢℉ome Assistant浣滀负灏忔櫤鐨凩LM宸ュ叿浼氬皢鎰忓浘璇嗗埆鑳藉姏杞缁橦ome Assistant銆備絾鏄�**杩欎釜鏂规硶鏄兘浣撻獙鍒板師鐢熺殑Home Assistant鎿嶄綔鍔熻兘锛屼笖灏忔櫤鐨勮亰澶╄兘鍔涗笉鍙�**銆傚瀹炲湪浠嬫剰鍙互浣跨敤鍚屾牱鏄疕ome Assistant鏀寔鐨刐鏂规硶3](##鏂规硶3锛氫娇鐢℉ome Assistant鐨凪CP鏈嶅姟锛堟帹鑽愶級)锛岃兘澶熸渶澶х▼搴︿綋楠屽埌Home Assistant鐨勫姛鑳姐��
+
+### 閰嶇疆姝ラ锛�
+
+#### 1. 閰嶇疆Home Assistant鐨勫ぇ妯″瀷璇煶鍔╂墜銆�
+
+**闇�瑕佹偍鎻愬墠閰嶇疆濂紿ome Assistant鐨勮闊冲姪鎵嬫垨澶фā鍨嬪伐鍏枫��**
+
+#### 2. 鑾峰彇Home Assistant鐨勮瑷�鍔╂墜鐨凙gent ID.
+
+1. 杩涘叆Home Assistant椤甸潰鍐呫�傚乏渚х偣鍑籤寮�鍙戣�呭姪鎵媊銆�
+2. 鍦ㄦ墦寮�鐨刞寮�鍙戣�呭姪鎵媊鍐咃紝鐐瑰嚮`鍔ㄤ綔`閫夐」鍗★紙濡傚浘绀烘搷浣�1锛夛紝鍦ㄩ〉闈㈠唴鐨勯�夐」鏍廯鍔ㄤ綔`涓紝鎵惧埌鎴栬緭鍏conversation.process锛堝璇�-澶勭悊锛塦骞堕�夋嫨`瀵硅瘽锛坈onversation锛�: 澶勭悊`锛堝鍥剧ず鎿嶄綔2锛夈��
+
+
+
+3. 鍦ㄩ〉闈㈠唴鍕鹃�塦浠g悊(agent)`閫夐」锛屽湪鍙樻垚甯镐寒鐨刞瀵硅瘽浠g悊(conversation agent)`鍐呴�夋嫨鎮ㄦ楠や竴閰嶇疆濂界殑璇煶鍔╂墜鍚嶇О锛屽鍥剧ず锛屾垜杩欒竟閰嶇疆濂界殑鏄痐ZhipuAi`骞堕�夋嫨銆�
+
+
+
+4. 閫変腑鍚庯紝鐐瑰嚮琛ㄥ崟宸︿笅鏂圭殑`杩涘叆YAML妯″紡`銆�
+
+
+
+5. 澶嶅埗鍏朵腑鐨刟gent-id鐨勫�硷紝渚嬪鍥剧ず涓垜鐨勬槸`01JP2DYMBDF7F4ZA2DMCF2AGX2`(浠呬緵鍙傝��)銆�
+
+
+
+6. 鍒囨崲鍒板皬鏅哄紑婧愭湇鍔$`xiaozhi-esp32-server`鐨刞config.yaml`鏂囦欢鍐咃紝鍦↙LM閰嶇疆涓紝鎵惧埌Home Assistant锛岃缃偍鐨凥ome Assistant鐨勭綉缁滃湴鍧�锛孉pi key鍜屽垰鍒氭煡璇㈠埌鐨刟gent_id銆�
+7. 淇敼`config.yaml`鏂囦欢鍐呯殑`selected_module`灞炴�х殑`LLM`涓篳HomeAssistant`锛宍Intent`涓篳nointent`銆�
+8. 閲嶅惎灏忔櫤寮�婧愭湇鍔$`xiaozhi-esp32-server`鍗冲彲姝e父浣跨敤銆�
+
+## 鏂规硶3锛氫娇鐢℉ome Assistant鐨凪CP鏈嶅姟锛堟帹鑽愶級
+
+### 鍔熻兘鎻忚堪
+
+- 闇�瑕佹偍鎻愬墠鍦℉ome Assistant鍐呴泦鎴愬苟瀹夎濂紿A闆嗘垚鈥斺�擺Model Context Protocol Server](https://www.home-assistant.io/integrations/mcp_server/)銆�
+
+- 杩欎釜鏂规硶涓庢柟娉�2閮芥槸HA瀹樻柟鎻愪緵鐨勮В鍐虫柟娉曪紝涓庢柟娉�2涓嶅悓鐨勬槸锛屾偍鍙互姝e父浣跨敤灏忔櫤寮�婧愭湇鍔$`xiaozhi-esp32-server`鐨勫紑婧愬叡寤虹殑鎻掍欢锛屽悓鏃跺厑璁告偍闅忔剰浣跨敤浠讳綍涓�涓敮鎸乫unction_call鍔熻兘鐨凩LM澶фā鍨嬨��
+
+### 閰嶇疆姝ラ
+
+#### 1. 瀹夎Home Assistant鐨凪CP鏈嶅姟闆嗘垚銆�
+
+闆嗘垚瀹樻柟缃戝潃鈥斺�擺Model Context Protocol Server](https://www.home-assistant.io/integrations/mcp_server/)銆傘��
+
+鎴栬窡闅忎互涓嬫墜鍔ㄦ搷浣溿��
+
+> - 鍓嶅線Home Assistant椤甸潰鐨�**[璁剧疆 > 璁惧鍜屾湇鍔★紙Settings > Devices & Services.锛塢(https://my.home-assistant.io/redirect/integrations)**銆�
+>
+> - 鍦ㄥ彸涓嬭锛岄�夋嫨 **[娣诲姞闆嗘垚锛圓dd Integration锛塢(https://my.home-assistant.io/redirect/config_flow_start?domain=mcp_server)**鎸夐挳銆�
+>
+> - 浠庡垪琛ㄤ腑閫夋嫨**妯″瀷涓婁笅鏂囧崗璁湇鍔″櫒锛圡odel Context Protocol Server锛�**銆�
+>
+> - 鎸夌収灞忓箷涓婄殑璇存槑瀹屾垚璁剧疆銆�
+
+#### 2. 閰嶇疆灏忔櫤寮�婧愭湇鍔$MCP閰嶇疆淇℃伅
+
+
+杩涘叆`data`鐩綍锛屾壘鍒癭.mcp_server_settings.json`鏂囦欢銆�
+
+濡傛灉浣犵殑`data`鐩綍涓嬫病鏈塦.mcp_server_settings.json`鏂囦欢锛�
+- 璇锋妸鍦╜xiaozhi-server`鏂囦欢澶规牴鐩綍鐨刞mcp_server_settings.json`鏂囦欢澶嶅埗鍒癭data`鐩綍涓嬶紝骞堕噸鍛藉悕涓篳.mcp_server_settings.json`
+- 鎴朳涓嬭浇杩欎釜鏂囦欢](https://github.com/xinnan-tech/xiaozhi-esp32-server/blob/main/main/xiaozhi-server/mcp_server_settings.json)锛屼笅杞藉埌`data`鐩綍涓嬶紝骞堕噸鍛藉悕涓篳.mcp_server_settings.json`
+
+
+淇敼`"mcpServers"`閲岀殑杩欓儴鍒嗙殑鍐呭锛�
+
+```json
+"Home Assistant": {
+ "command": "mcp-proxy",
+ "args": [
+ "http://YOUR_HA_HOST/mcp_server/sse"
+ ],
+ "env": {
+ "API_ACCESS_TOKEN": "YOUR_API_ACCESS_TOKEN"
+ }
+},
+```
+
+娉ㄦ剰锛�
+
+1. **鏇挎崲閰嶇疆锛�**
+ - 鏇挎崲`args`鍐呯殑`YOUR_HA_HOST`涓烘偍鐨凥A鏈嶅姟鍦板潃锛屽鏋滀綘鐨勬湇鍔″湴鍧�宸茬粡鍖呭惈浜唄ttps/http瀛楁牱锛堜緥濡俙http://192.168.1.101:8123`)锛屽垯鍙渶瑕佸~鍏192.168.1.101:8123`鍗冲彲銆�
+ - 灏哷env`鍐卄API_ACCESS_TOKEN`鐨刞YOUR_API_ACCESS_TOKEN`鏇挎崲鎴愭偍涔嬪墠鑾峰彇鍒扮殑寮�鍙戝瘑閽pi key銆�
+2. **濡傛灉浣犳坊鍔犻厤缃槸鍦╜"mcpServers"`鐨勬嫭鍙峰唴鍚庣画娌℃湁鏂扮殑`mcpServers`鐨勯厤缃椂锛岄渶瑕佹妸鏈�鍚庣殑閫楀彿`,`绉婚櫎**锛屽惁鍒欏彲鑳戒細瑙f瀽澶辫触銆�
+
+**鏈�鍚庢晥鏋滃弬鑰冧互涓嬶紙鍙傝�冨涓嬶級**锛�
+
+```json
+ "mcpServers": {
+ "Home Assistant": {
+ "command": "mcp-proxy",
+ "args": [
+ "http://192.168.1.101:8123/mcp_server/sse"
+ ],
+ "env": {
+ "API_ACCESS_TOKEN": "abcd.efghi.jkl"
+ }
+ }
+ }
+```
+
+#### 3. 閰嶇疆灏忔櫤寮�婧愭湇鍔$鐨勭郴缁熼厤缃�
+
+1. **閫夋嫨浠绘剰涓�娆炬敮鎸乫unction_call鐨凩LM澶фā鍨嬩綔涓哄皬鏅虹殑LLM鑱婂ぉ鍔╂墜锛堜絾涓嶈閫夋嫨Home Assistant浣滀负LLM宸ュ叿锛�**锛屾湰娆℃垜閫夋嫨鐨勬ā鍨嬫槸锛氬厤璐圭殑ChatGLM锛屽畠鏀寔functioncall鍑芥暟璋冪敤锛屼絾閮ㄥ垎鏃跺�欒皟鐢ㄤ笉澶ǔ瀹氾紝濡傛灉鍍忚拷姹傜ǔ瀹氬缓璁妸LLM璁剧疆鎴愶細DoubaoLLM锛屼娇鐢ㄧ殑鍏蜂綋model_name鏄細doubao-1-5-pro-32k-250115銆�
+
+2. 鍒囨崲鍒板皬鏅哄紑婧愭湇鍔$`xiaozhi-esp32-server`鐨刞config.yaml`鏂囦欢鍐咃紝璁剧疆鎮ㄧ殑LLM澶фā鍨嬮厤缃紝骞朵笖灏哷selected_module`閰嶇疆鐨刞Intent`璋冩暣涓篳function_call`銆�
+
+3. 閲嶅惎灏忔櫤寮�婧愭湇鍔$`xiaozhi-esp32-server`鍗冲彲姝e父浣跨敤銆�
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/docs/huoshan-streamTTS-voice-cloning.md b/xiaozhi-esp32-server/docs/huoshan-streamTTS-voice-cloning.md
new file mode 100644
index 0000000..304a8c9
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/huoshan-streamTTS-voice-cloning.md
@@ -0,0 +1,58 @@
+# 鏅烘帶鍙� 鐏北鍙屾祦寮忚闊冲悎鎴�+闊宠壊鍏嬮殕閰嶇疆鏁欑▼
+
+鏈暀绋嬪垎涓�4涓樁娈碉細鍑嗗闃舵銆侀厤缃樁娈点�佸厠闅嗛樁娈点�佷娇鐢ㄩ樁娈点�備富瑕佹槸浠嬬粛閫氳繃鏅烘帶鍙伴厤缃伀灞卞弻娴佸紡璇煶鍚堟垚+闊宠壊鍏嬮殕鐨勮繃绋嬨��
+
+## 绗竴闃舵锛氬噯澶囬樁娈�
+瓒呯骇绠$悊鍛樺厛棰勫厛鎶婄伀灞卞紩鎿庢湇鍔″紑閫氬ソ锛岃幏鍙栧埌App Id锛孉ccess Token銆傞粯璁ょ伀涓婂紩鎿庝細璧犻�佷竴涓煶鑹茶祫婧愩�傝繖涓煶鑹茶祫婧愰渶瑕佹妸瀹冨鍒跺埌鏈」鐩噷銆�
+
+濡傛灉浣犳兂鍏嬮殕澶氫釜闊宠壊锛岄渶瑕佽喘涔板紑閫氬涓煶鑹茶祫婧愩�傚彧瑕佹妸姣忎釜闊宠壊璧勬簮鐨勫0闊矷D(S_xxxxx)澶嶅埗鍒版湰椤圭洰銆傜劧鍚庡垎閰嶇粰绯荤粺鐨勮处鍙蜂娇鐢ㄥ嵆鍙�備互涓嬫槸璇︾粏姝ラ锛�
+
+### 1.寮�閫氱伀灞卞紩鎿庢湇鍔�
+璁块棶 https://console.volcengine.com/speech/app 鍦ㄥ簲鐢ㄧ鐞嗗垱寤哄簲鐢紝鍕鹃�夎闊冲悎鎴愬ぇ妯″瀷鍜屽0闊冲鍒诲ぇ妯″瀷銆�
+
+### 2.鑾峰彇闊宠壊璧勬簮ID
+璁块棶 https://console.volcengine.com/speech/service/9999 澶嶅埗涓夐」鍐呭锛屽垎鍒槸App Id锛孉ccess Token浠ュ強澹伴煶ID(S_xxxxx)銆傚鍥�
+
+
+
+## 绗簩闃舵锛氶厤缃伀灞卞紩鎿庢湇鍔�
+
+### 1.濉啓鐏北寮曟搸閰嶇疆
+
+浣跨敤瓒呯骇绠$悊鍛樿处鍙风櫥褰曟櫤鎺у彴锛岀偣鍑婚《閮ㄣ�愭ā鍨嬮厤缃�戯紝鍐嶇偣鍑绘ā鍨嬮厤缃〉闈㈠乏渚х殑銆愯闊冲悎鎴愩�戯紝鎼滅储鎵惧埌鈥滅伀灞卞弻娴佸紡璇煶鍚堟垚鈥濓紝鐐瑰嚮淇敼锛屽皢浣犵伀灞卞紩鎿庣殑`App Id`濉叆鍒般�愬簲鐢↖D銆戝瓧娈甸噷锛屽皢`Access Token`濉叆鍒般�愯闂护鐗屻�戝瓧娈甸噷銆傜劧鍚庝繚瀛樸��
+
+### 2.灏嗛煶鑹茶祫婧怚D鍒嗛厤缁欑郴缁熻处鍙�
+
+浣跨敤瓒呯骇绠$悊鍛樿处鍙风櫥褰曟櫤鎺у彴锛岀偣鍑婚《閮╜鍙傛暟瀛楀吀`锛屽湪涓嬫媺鑿滃崟涓紝鐐瑰嚮`绯荤粺鍔熻兘閰嶇疆`椤甸潰銆傚湪椤甸潰涓婂嬀閫塦闊宠壊鍏嬮殕`锛岀偣鍑讳繚瀛橀厤缃�傚嵆鍙湪椤堕儴鑿滃崟鐪嬪埌`闊宠壊鍏嬮殕`鎸夐挳銆�
+
+浣跨敤瓒呯骇绠$悊鍛樿处鍙风櫥褰曟櫤鎺у彴锛岀偣鍑婚《閮ㄣ�愰煶鑹插厠闅嗐�戙�併�愰煶鑹茶祫婧愩�戙��
+
+鐐瑰嚮鏂板鎸夐挳锛屽湪銆愬钩鍙板悕绉般�戦�夋嫨鈥滅伀灞卞弻娴佸紡璇煶鍚堟垚鈥濓紱
+
+鍦ㄣ�愰煶鑹茶祫婧怚D銆戝~鍏ヤ綘鐏北寮曟搸鐨勫0闊宠祫婧怚D(S_xxxxx)锛屽~鍏ュ悗鎸夊洖杞︼紱
+
+鍦ㄣ�愬綊灞炶处鍙枫�戦�夋嫨浣犺鍒嗛厤缁欑殑绯荤粺璐﹀彿锛屼綘鍙互鍒嗛厤缁欎綘鑷繁銆傜劧鍚庣偣鍑讳繚瀛�
+
+## 绗笁闃舵锛氬厠闅嗛樁娈�
+
+濡傛灉鐧诲綍鍚庯紝鐐瑰嚮椤堕儴銆愰煶鑹插厠闅嗐�戙�嬨�愰煶鑹插厠闅嗐�戯紝鏄剧ず銆愭偍鐨勮处鍙锋殏鏃犻煶鑹茶祫婧愯鑱旂郴绠$悊鍛樺垎閰嶉煶鑹茶祫婧愩�戯紝璇存槑浣犲湪绗簩闃舵杩樻病鏈夋妸闊宠壊璧勬簮ID鍒嗛厤缁欒繖涓处鍙枫�傞偅灏辨槸鍥炲埌绗簩闃舵锛屽垎閰嶉煶鑹茶祫婧愮粰瀵瑰簲鐨勮处鍙枫��
+
+濡傛灉鐧诲綍鍚庯紝鐐瑰嚮椤堕儴銆愰煶鑹插厠闅嗐�戙�嬨�愰煶鑹插厠闅嗐�戯紝鑳界湅鍒板搴旂殑闊宠壊鍒楄〃銆傝缁х画銆�
+
+鍦ㄥ垪琛ㄩ噷浼氱湅鍒板搴旂殑闊宠壊鍒楄〃銆傞�夋嫨鍏朵腑涓�涓煶鑹茶祫婧愶紝鐐瑰嚮銆愪笂浼犻煶棰戙�戞寜閽�備笂浼犲悗锛屽彲浠ヨ瘯鍚竴涓嬪0闊虫垨鑰呮埅鍙栨煇娈靛0闊炽�傜‘璁ゅ悗鐐瑰嚮銆愪笂浼犻煶棰戙�戞寜閽��
+
+
+涓婁紶闊抽鍚庯紝鍦ㄥ垪琛ㄩ噷浼氱湅鍒板搴旂殑闊宠壊浼氬彉鎴愨�滃緟澶嶅埢鈥濈姸鎬併�傜偣鍑汇�愮珛鍗冲鍒汇�戞寜閽�傜瓑1~2绉掍細杩斿洖缁撴灉銆�
+
+濡傛灉澶嶅埢澶辫触锛岃灏嗛紶鏍囨斁鍒扳�滈敊璇俊鎭�濆浘鏍囦笂锛屼細鏄剧ず澶辫触鐨勫師鍥犮��
+
+濡傛灉澶嶅埢鎴愬姛锛屽湪鍒楄〃閲屼細鐪嬪埌瀵瑰簲鐨勯煶鑹蹭細鍙樻垚鈥滆缁冩垚鍔熲�濈姸鎬併�傛鏃朵綘鍙互鐐瑰嚮銆愬0闊冲悕绉般�戞爮鐨勪慨鏀规寜閽紝淇敼闊宠壊璧勬簮鐨勫悕绉帮紝鏂逛究鍚庢湡閫夋嫨浣跨敤銆�
+
+## 绗洓闃舵锛氫娇鐢ㄩ樁娈�
+
+鐐瑰嚮椤堕儴銆愭櫤鑳戒綋绠$悊銆戯紝閫夋嫨浠绘剰涓�涓櫤鑳戒綋锛岀偣鍑汇�愰厤缃鑹层�戞寜閽��
+
+璇煶鍚堟垚(TTS)閫夋嫨鈥滅伀灞卞弻娴佸紡璇煶鍚堟垚鈥濄�傚湪鍒楄〃閲岋紝鎵惧埌鍚嶅瓧甯︽湁鈥滃厠闅嗛煶鑹测�濈殑闊宠壊璧勬簮锛堝鍥撅級锛岄�夋嫨瀹冿紝鐐瑰嚮淇濆瓨銆�
+
+
+鎺ヤ笅鏉ワ紝鍙互鍞ら啋灏忔櫤鍜屽畠瀵硅瘽銆�
diff --git a/xiaozhi-esp32-server/docs/images/__init__.py b/xiaozhi-esp32-server/docs/images/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/__init__.py
diff --git a/xiaozhi-esp32-server/docs/images/alisms/sms-01.png b/xiaozhi-esp32-server/docs/images/alisms/sms-01.png
new file mode 100644
index 0000000..89c56f5
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/alisms/sms-01.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/alisms/sms-02.png b/xiaozhi-esp32-server/docs/images/alisms/sms-02.png
new file mode 100644
index 0000000..22537f3
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/alisms/sms-02.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/alisms/sms-11.png b/xiaozhi-esp32-server/docs/images/alisms/sms-11.png
new file mode 100644
index 0000000..05fcaf5
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/alisms/sms-11.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/alisms/sms-21.png b/xiaozhi-esp32-server/docs/images/alisms/sms-21.png
new file mode 100644
index 0000000..5bfc280
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/alisms/sms-21.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/alisms/sms-22.png b/xiaozhi-esp32-server/docs/images/alisms/sms-22.png
new file mode 100644
index 0000000..3d83984
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/alisms/sms-22.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/alisms/sms-23.png b/xiaozhi-esp32-server/docs/images/alisms/sms-23.png
new file mode 100644
index 0000000..584d8d6
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/alisms/sms-23.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/alisms/sms-24.png b/xiaozhi-esp32-server/docs/images/alisms/sms-24.png
new file mode 100644
index 0000000..1b22c35
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/alisms/sms-24.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/alisms/sms-25.png b/xiaozhi-esp32-server/docs/images/alisms/sms-25.png
new file mode 100644
index 0000000..3bb68cf
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/alisms/sms-25.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/alisms/sms-31.png b/xiaozhi-esp32-server/docs/images/alisms/sms-31.png
new file mode 100644
index 0000000..7f0ee6c
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/alisms/sms-31.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/alisms/sms-32.png b/xiaozhi-esp32-server/docs/images/alisms/sms-32.png
new file mode 100644
index 0000000..db06cc4
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/alisms/sms-32.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/banner1.png b/xiaozhi-esp32-server/docs/images/banner1.png
new file mode 100644
index 0000000..974ddaf
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/banner1.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/banner2.png b/xiaozhi-esp32-server/docs/images/banner2.png
new file mode 100644
index 0000000..4d54073
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/banner2.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/conda_env_1.png b/xiaozhi-esp32-server/docs/images/conda_env_1.png
new file mode 100644
index 0000000..114256a
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/conda_env_1.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/conda_env_2.png b/xiaozhi-esp32-server/docs/images/conda_env_2.png
new file mode 100644
index 0000000..61f4626
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/conda_env_2.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/demo0.png b/xiaozhi-esp32-server/docs/images/demo0.png
new file mode 100644
index 0000000..c48449f
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/demo0.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/demo1.png b/xiaozhi-esp32-server/docs/images/demo1.png
new file mode 100644
index 0000000..1ab7c22
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/demo1.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/demo10.png b/xiaozhi-esp32-server/docs/images/demo10.png
new file mode 100644
index 0000000..08bdb77
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/demo10.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/demo11.png b/xiaozhi-esp32-server/docs/images/demo11.png
new file mode 100644
index 0000000..36e16a1
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/demo11.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/demo12.png b/xiaozhi-esp32-server/docs/images/demo12.png
new file mode 100644
index 0000000..c658d67
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/demo12.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/demo13.png b/xiaozhi-esp32-server/docs/images/demo13.png
new file mode 100644
index 0000000..1ad79fa
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/demo13.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/demo14.png b/xiaozhi-esp32-server/docs/images/demo14.png
new file mode 100644
index 0000000..48360cf
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/demo14.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/demo2.png b/xiaozhi-esp32-server/docs/images/demo2.png
new file mode 100644
index 0000000..6b30494
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/demo2.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/demo3.png b/xiaozhi-esp32-server/docs/images/demo3.png
new file mode 100644
index 0000000..e1bc859
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/demo3.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/demo4.png b/xiaozhi-esp32-server/docs/images/demo4.png
new file mode 100644
index 0000000..e0b5ba7
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/demo4.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/demo5.png b/xiaozhi-esp32-server/docs/images/demo5.png
new file mode 100644
index 0000000..c6da299
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/demo5.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/demo6.png b/xiaozhi-esp32-server/docs/images/demo6.png
new file mode 100644
index 0000000..51f1f5e
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/demo6.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/demo7.png b/xiaozhi-esp32-server/docs/images/demo7.png
new file mode 100644
index 0000000..3e1d053
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/demo7.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/demo8.png b/xiaozhi-esp32-server/docs/images/demo8.png
new file mode 100644
index 0000000..c85a42b
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/demo8.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/demo9.png b/xiaozhi-esp32-server/docs/images/demo9.png
new file mode 100644
index 0000000..ff422fe
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/demo9.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/deploy1.png b/xiaozhi-esp32-server/docs/images/deploy1.png
new file mode 100644
index 0000000..70488bc
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/deploy1.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/deploy2.png b/xiaozhi-esp32-server/docs/images/deploy2.png
new file mode 100644
index 0000000..7844153
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/deploy2.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/firmware-setting-ota.png b/xiaozhi-esp32-server/docs/images/firmware-setting-ota.png
new file mode 100644
index 0000000..c151fd9
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/firmware-setting-ota.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/fishspeech/autodl-01.png b/xiaozhi-esp32-server/docs/images/fishspeech/autodl-01.png
new file mode 100644
index 0000000..695b94b
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/fishspeech/autodl-01.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/fishspeech/autodl-02.png b/xiaozhi-esp32-server/docs/images/fishspeech/autodl-02.png
new file mode 100644
index 0000000..e3f72af
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/fishspeech/autodl-02.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/hnlg.jpg b/xiaozhi-esp32-server/docs/images/hnlg.jpg
new file mode 100644
index 0000000..b79be6f
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/hnlg.jpg
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/image-clone-integration-01.png b/xiaozhi-esp32-server/docs/images/image-clone-integration-01.png
new file mode 100644
index 0000000..7132ee7
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/image-clone-integration-01.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/image-clone-integration-02.png b/xiaozhi-esp32-server/docs/images/image-clone-integration-02.png
new file mode 100644
index 0000000..80972df
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/image-clone-integration-02.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/image-clone-integration-03.png b/xiaozhi-esp32-server/docs/images/image-clone-integration-03.png
new file mode 100644
index 0000000..7f35e39
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/image-clone-integration-03.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/image-ha-integration-01.png b/xiaozhi-esp32-server/docs/images/image-ha-integration-01.png
new file mode 100644
index 0000000..eb05461
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/image-ha-integration-01.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/image-ha-integration-02.png b/xiaozhi-esp32-server/docs/images/image-ha-integration-02.png
new file mode 100644
index 0000000..7431def
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/image-ha-integration-02.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/image-ha-integration-03.png b/xiaozhi-esp32-server/docs/images/image-ha-integration-03.png
new file mode 100644
index 0000000..3f1aa59
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/image-ha-integration-03.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/image-ha-integration-04.png b/xiaozhi-esp32-server/docs/images/image-ha-integration-04.png
new file mode 100644
index 0000000..29b402e
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/image-ha-integration-04.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/image-ha-integration-05.png b/xiaozhi-esp32-server/docs/images/image-ha-integration-05.png
new file mode 100644
index 0000000..1aa8ffc
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/image-ha-integration-05.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/image-ha-integration-06.png b/xiaozhi-esp32-server/docs/images/image-ha-integration-06.png
new file mode 100644
index 0000000..56ef98f
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/image-ha-integration-06.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/logo_bailing.png b/xiaozhi-esp32-server/docs/images/logo_bailing.png
new file mode 100644
index 0000000..22ab6e1
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/logo_bailing.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/logo_contributors.png b/xiaozhi-esp32-server/docs/images/logo_contributors.png
new file mode 100644
index 0000000..5e8febe
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/logo_contributors.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/logo_huiyuan.png b/xiaozhi-esp32-server/docs/images/logo_huiyuan.png
new file mode 100644
index 0000000..6e44191
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/logo_huiyuan.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/logo_junsen.png b/xiaozhi-esp32-server/docs/images/logo_junsen.png
new file mode 100644
index 0000000..ded09f2
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/logo_junsen.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/logo_qinren.png b/xiaozhi-esp32-server/docs/images/logo_qinren.png
new file mode 100644
index 0000000..a01af84
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/logo_qinren.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/logo_tenclass.png b/xiaozhi-esp32-server/docs/images/logo_tenclass.png
new file mode 100644
index 0000000..86a6dc4
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/logo_tenclass.png
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/images/logo_xuanfeng.png b/xiaozhi-esp32-server/docs/images/logo_xuanfeng.png
new file mode 100644
index 0000000..552ee78
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/images/logo_xuanfeng.png
Binary files differ
diff --git "a/xiaozhi-esp32-server/docs/images/manager-mobile/\346\211\223\345\214\205\345\217\221\350\241\214\346\255\245\351\252\2441.png" "b/xiaozhi-esp32-server/docs/images/manager-mobile/\346\211\223\345\214\205\345\217\221\350\241\214\346\255\245\351\252\2441.png"
new file mode 100644
index 0000000..88cabf4
--- /dev/null
+++ "b/xiaozhi-esp32-server/docs/images/manager-mobile/\346\211\223\345\214\205\345\217\221\350\241\214\346\255\245\351\252\2441.png"
Binary files differ
diff --git "a/xiaozhi-esp32-server/docs/images/manager-mobile/\346\211\223\345\214\205\345\217\221\350\241\214\346\255\245\351\252\2442.png" "b/xiaozhi-esp32-server/docs/images/manager-mobile/\346\211\223\345\214\205\345\217\221\350\241\214\346\255\245\351\252\2442.png"
new file mode 100644
index 0000000..747b5ff
--- /dev/null
+++ "b/xiaozhi-esp32-server/docs/images/manager-mobile/\346\211\223\345\214\205\345\217\221\350\241\214\346\255\245\351\252\2442.png"
Binary files differ
diff --git "a/xiaozhi-esp32-server/docs/images/manager-mobile/\346\234\254\345\234\260\350\277\220\350\241\214.png" "b/xiaozhi-esp32-server/docs/images/manager-mobile/\346\234\254\345\234\260\350\277\220\350\241\214.png"
new file mode 100644
index 0000000..c3c5b6e
--- /dev/null
+++ "b/xiaozhi-esp32-server/docs/images/manager-mobile/\346\234\254\345\234\260\350\277\220\350\241\214.png"
Binary files differ
diff --git "a/xiaozhi-esp32-server/docs/images/manager-mobile/\347\224\237\346\210\220appid.png" "b/xiaozhi-esp32-server/docs/images/manager-mobile/\347\224\237\346\210\220appid.png"
new file mode 100644
index 0000000..73e6357
--- /dev/null
+++ "b/xiaozhi-esp32-server/docs/images/manager-mobile/\347\224\237\346\210\220appid.png"
Binary files differ
diff --git "a/xiaozhi-esp32-server/docs/images/manager-mobile/\351\207\215\346\226\260\350\257\206\345\210\253\351\241\271\347\233\256.png" "b/xiaozhi-esp32-server/docs/images/manager-mobile/\351\207\215\346\226\260\350\257\206\345\210\253\351\241\271\347\233\256.png"
new file mode 100644
index 0000000..0e9c1c9
--- /dev/null
+++ "b/xiaozhi-esp32-server/docs/images/manager-mobile/\351\207\215\346\226\260\350\257\206\345\210\253\351\241\271\347\233\256.png"
Binary files differ
diff --git a/xiaozhi-esp32-server/docs/index-stream-integration.md b/xiaozhi-esp32-server/docs/index-stream-integration.md
new file mode 100644
index 0000000..752fa89
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/index-stream-integration.md
@@ -0,0 +1,195 @@
+# IndexStreamTTS 浣跨敤鎸囧崡
+
+## 鐜鍑嗗
+### 1. 鍏嬮殕椤圭洰
+```bash
+git clone https://github.com/Ksuriuri/index-tts-vllm.git
+```
+杩涘叆瑙e帇鍚庣殑鐩綍
+```bash
+cd index-tts-vllm
+```
+鍒囨崲鍒版寚瀹氱増鏈� (浣跨敤VLLM-0.10.2鐨勫巻鍙茬増鏈�)
+```bash
+git checkout 224e8d5e5c8f66801845c66b30fa765328fd0be3
+```
+
+### 2. 鍒涘缓骞舵縺娲� conda 鐜
+```bash
+conda create -n index-tts-vllm python=3.12
+conda activate index-tts-vllm
+```
+
+### 3. 瀹夎PyTorch 闇�瑕佺増鏈负2.8.0锛堟渶鏂扮増锛�
+#### 鏌ョ湅鏄惧崱鏈�楂樻敮鎸佺殑鐗堟湰鍜屽疄闄呭畨瑁呯殑鐗堟湰
+```bash
+nvidia-smi
+nvcc --version
+```
+#### 椹卞姩鏀寔鐨勬渶楂� CUDA 鐗堟湰
+```bash
+CUDA Version: 12.8
+```
+#### 瀹為檯瀹夎鐨� CUDA 缂栬瘧鍣ㄧ増鏈�
+```bash
+Cuda compilation tools, release 12.8, V12.8.89
+```
+#### 閭d箞瀵瑰簲鐨勫畨瑁呭懡浠わ紙pytorch榛樿缁欑殑鏄�12.8鐨勯┍鍔ㄧ増鏈級
+```bash
+pip install torch torchvision
+```
+闇�瑕� pytorch 鐗堟湰 2.8.0锛堝搴� vllm 0.10.2锛夛紝鍏蜂綋瀹夎鎸囦护璇峰弬鑰冿細[pytorch 瀹樼綉](https://pytorch.org/get-started/locally/)
+
+### 4. 瀹夎渚濊禆
+```bash
+pip install -r requirements.txt
+```
+
+### 5. 涓嬭浇妯″瀷鏉冮噸
+### 鏂规涓�锛氫笅杞藉畼鏂规潈閲嶆枃浠跺悗杞崲
+姝や负瀹樻柟鏉冮噸鏂囦欢锛屼笅杞藉埌鏈湴浠绘剰璺緞鍗冲彲锛屾敮鎸� IndexTTS-1.5 鐨勬潈閲�
+| HuggingFace | ModelScope |
+|---------------------------------------------------------------|---------------------------------------------------------------------|
+| [IndexTTS](https://huggingface.co/IndexTeam/Index-TTS) | [IndexTTS](https://modelscope.cn/models/IndexTeam/Index-TTS) |
+| [IndexTTS-1.5](https://huggingface.co/IndexTeam/IndexTTS-1.5) | [IndexTTS-1.5](https://modelscope.cn/models/IndexTeam/IndexTTS-1.5) |
+
+涓嬮潰浠odelScope鐨勫畨瑁呮柟娉曚负渚�
+#### 璇锋敞鎰忥細git闇�瑕佸畨瑁呭苟鍒濆鍖栧惎鐢╨fs锛堝宸插畨瑁呭彲浠ヨ烦杩囷級
+```bash
+sudo apt-get install git-lfs
+git lfs install
+```
+鍒涘缓妯″瀷鐩綍锛屽苟鎷夊彇妯″瀷
+```bash
+mkdir model_dir
+cd model_dir
+git clone https://www.modelscope.cn/IndexTeam/IndexTTS-1.5.git
+```
+
+#### 妯″瀷鏉冮噸杞崲
+```bash
+bash convert_hf_format.sh /path/to/your/model_dir
+```
+渚嬪锛氫綘涓嬭浇鐨処ndexTTS-1.5妯″瀷瀛樻斁鍦╩odel_dir鐩綍涓嬶紝鍒欐墽琛屼互涓嬪懡浠�
+```bash
+bash convert_hf_format.sh model_dir/IndexTTS-1.5
+```
+姝ゆ搷浣滀細灏嗗畼鏂圭殑妯″瀷鏉冮噸杞崲涓� transformers 搴撳吋瀹圭殑鐗堟湰锛屼繚瀛樺湪妯″瀷鏉冮噸璺緞涓嬬殑 vllm 鏂囦欢澶逛腑锛屾柟渚垮悗缁� vllm 搴撳姞杞芥ā鍨嬫潈閲�
+
+### 6. 鏇存敼鎺ュ彛閫傞厤涓�涓嬮」鐩�
+鎺ュ彛杩斿洖鏁版嵁涓庨」鐩笉閫傞厤闇�瑕佽皟鏁翠竴涓嬶紝浣垮叾鐩存帴杩斿洖闊抽鏁版嵁
+```bash
+vi api_server.py
+```
+```bash
+@app.post("/tts", responses={
+ 200: {"content": {"application/octet-stream": {}}},
+ 500: {"content": {"application/json": {}}}
+})
+async def tts_api(request: Request):
+ try:
+ data = await request.json()
+ text = data["text"]
+ character = data["character"]
+
+ global tts
+ sr, wav = await tts.infer_with_ref_audio_embed(character, text)
+
+ return Response(content=wav.tobytes(), media_type="application/octet-stream")
+
+ except Exception as ex:
+ tb_str = ''.join(traceback.format_exception(type(ex), ex, ex.__traceback__))
+ print(tb_str)
+ return JSONResponse(
+ status_code=500,
+ content={
+ "status": "error",
+ "error": str(tb_str)
+ }
+ )
+```
+
+### 7.缂栧啓sh鍚姩鑴氭湰锛堣娉ㄦ剰瑕佸湪鐩稿簲鐨刢onda鐜涓嬭繍琛岋級
+```bash
+vi start_api.sh
+```
+### 灏嗕笅闈㈠唴瀹圭矘璐磋繘鍘诲苟鎸�:杈撳叆wq淇濆瓨
+#### 鑴氭湰涓殑/home/system/index-tts-vllm/model_dir/IndexTTS-1.5 璇疯嚜琛屼慨鏀逛负瀹為檯璺緞
+```bash
+# 婵�娲籧onda鐜
+conda activate index-tts-vllm
+echo "婵�娲婚」鐩甤onda鐜"
+sleep 2
+# 鏌ユ壘鍗犵敤11996绔彛鐨勮繘绋嬪彿
+PID_VLLM=$(sudo netstat -tulnp | grep 11996 | awk '{print $7}' | cut -d'/' -f1)
+
+# 妫�鏌ユ槸鍚︽壘鍒拌繘绋嬪彿
+if [ -z "$PID_VLLM" ]; then
+ echo "娌℃湁鎵惧埌鍗犵敤11996绔彛鐨勮繘绋�"
+else
+ echo "鎵惧埌鍗犵敤11996绔彛鐨勮繘绋嬶紝杩涚▼鍙蜂负: $PID_VLLM"
+ # 鍏堝皾璇曟櫘閫歬ill锛岀瓑寰�2绉�
+ kill $PID_VLLM
+ sleep 2
+ # 妫�鏌ヨ繘绋嬫槸鍚﹁繕鍦�
+ if ps -p $PID_VLLM > /dev/null; then
+ echo "杩涚▼浠嶅湪杩愯锛屽己鍒剁粓姝�..."
+ kill -9 $PID_VLLM
+ fi
+ echo "宸茬粓姝㈣繘绋� $PID_VLLM"
+fi
+
+# 鏌ユ壘鍗犵敤VLLM::EngineCore杩涚▼
+GPU_PIDS=$(ps aux | grep -E "VLLM|EngineCore" | grep -v grep | awk '{print $2}')
+
+# 妫�鏌ユ槸鍚︽壘鍒拌繘绋嬪彿
+if [ -z "$GPU_PIDS" ]; then
+ echo "娌℃湁鎵惧埌VLLM鐩稿叧杩涚▼"
+else
+ echo "鎵惧埌VLLM鐩稿叧杩涚▼锛岃繘绋嬪彿涓�: $GPU_PIDS"
+ # 鍏堝皾璇曟櫘閫歬ill锛岀瓑寰�2绉�
+ kill $GPU_PIDS
+ sleep 2
+ # 妫�鏌ヨ繘绋嬫槸鍚﹁繕鍦�
+ if ps -p $GPU_PIDS > /dev/null; then
+ echo "杩涚▼浠嶅湪杩愯锛屽己鍒剁粓姝�..."
+ kill -9 $GPU_PIDS
+ fi
+ echo "宸茬粓姝㈣繘绋� $GPU_PIDS"
+fi
+
+# 鍒涘缓tmp鐩綍锛堝鏋滀笉瀛樺湪锛�
+mkdir -p tmp
+
+# 鍚庡彴杩愯api_server.py锛屾棩蹇楅噸瀹氬悜鍒皌mp/server.log
+nohup python api_server.py --model_dir /home/system/index-tts-vllm/model_dir/IndexTTS-1.5 --port 11996 > tmp/server.log 2>&1 &
+echo "api_server.py 宸插湪鍚庡彴杩愯锛屾棩蹇楄鏌ョ湅 tmp/server.log"
+```
+缁欒剼鏈墽琛屾潈闄愬苟杩愯鑴氭湰
+```bash
+chmod +x start_api.sh
+./start_api.sh
+```
+鏃ュ織浼氬湪tmp/server.log涓緭鍑猴紝鍙互閫氳繃浠ヤ笅鍛戒护鏌ョ湅鏃ュ織鎯呭喌
+```bash
+tail -f tmp/server.log
+```
+濡傛灉鏄惧崱鍐呭瓨瓒冲锛屽彲鍦ㄨ剼鏈腑娣诲姞鍚姩鍙傛暟 ----gpu_memory_utilization 鏉ヨ皟鏁存樉瀛樺崰鐢ㄦ瘮渚嬶紝榛樿鍊间负 0.25
+
+## 闊宠壊閰嶇疆
+index-tts-vllm鏀寔閫氳繃閰嶇疆鏂囦欢娉ㄥ唽鑷畾涔夐煶鑹诧紝鏀寔鍗曢煶鑹插拰娣峰悎闊宠壊閰嶇疆銆�
+鍦ㄩ」鐩牴鐩綍涓嬬殑assets/speaker.json鏂囦欢涓厤缃嚜瀹氫箟闊宠壊
+### 閰嶇疆鏍煎紡璇存槑
+```bash
+{
+ "璇磋瘽浜哄悕绉�1": [
+ "闊抽鏂囦欢璺緞1.wav",
+ "闊抽鏂囦欢璺緞2.wav"
+ ],
+ "璇磋瘽浜哄悕绉�2": [
+ "闊抽鏂囦欢璺緞3.wav"
+ ]
+}
+```
+### 娉ㄦ剰 锛堥厤缃鑹插悗闇�閲嶅惎鏈嶅姟杩涜闊宠壊娉ㄥ唽锛�
+娣诲姞鍚庨渶鍦ㄦ櫤鎺у彴涓坊鍔犵浉搴旂殑璇磋瘽浜猴紙鍗曟ā鍧楀垯鏇存崲鐩稿簲鐨剉oice锛�
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/docs/mcp-endpoint-enable.md b/xiaozhi-esp32-server/docs/mcp-endpoint-enable.md
new file mode 100644
index 0000000..5baf58e
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/mcp-endpoint-enable.md
@@ -0,0 +1,126 @@
+# MCP 鎺ュ叆鐐归儴缃蹭娇鐢ㄦ寚鍗�
+
+鏈暀绋嬪寘鍚�3涓儴鍒�
+- 1銆佸浣曢儴缃睲CP鎺ュ叆鐐硅繖涓湇鍔�
+- 2銆佸叏妯″潡閮ㄧ讲鏃讹紝鎬庝箞閰嶇疆MCP鎺ュ叆鐐�
+- 3銆佸崟妯″潡閮ㄧ讲鏃讹紝鎬庝箞閰嶇疆MCP鎺ュ叆鐐�
+
+# 1銆佸浣曢儴缃睲CP鎺ュ叆鐐硅繖涓湇鍔�
+
+## 绗竴姝ワ紝涓嬭浇mcp鎺ュ叆鐐归」鐩簮鐮�
+
+娴忚鍣ㄦ墦寮�[mcp鎺ュ叆鐐归」鐩湴鍧�](https://github.com/xinnan-tech/mcp-endpoint-server)
+
+鎵撳紑瀹岋紝鎵惧埌椤甸潰涓竴涓豢鑹茬殑鎸夐挳锛屽啓鐫�`Code`鐨勬寜閽紝鐐瑰紑瀹冿紝鐒跺悗浣犲氨鐪嬪埌`Download ZIP`鐨勬寜閽��
+
+鐐瑰嚮瀹冿紝涓嬭浇鏈」鐩簮鐮佸帇缂╁寘銆備笅杞藉埌浣犵數鑴戝悗锛岃В鍘嬪畠锛屾鏃跺畠鐨勫悕瀛楀彲鑳藉彨`mcp-endpoint-server-main`
+浣犻渶瑕佹妸瀹冮噸鍛藉悕鎴恅mcp-endpoint-server`銆�
+
+## 绗簩姝ワ紝鍚姩绋嬪簭
+杩欎釜椤圭洰鏄竴涓緢绠�鍗曠殑椤圭洰锛屽缓璁娇鐢╠ocker杩愯銆備笉杩囧鏋滀綘涓嶆兂浣跨敤docker杩愯锛屼綘鍙互鍙傝�僛杩欎釜椤甸潰](https://github.com/xinnan-tech/mcp-endpoint-server/blob/main/README_dev.md)浣跨敤婧愮爜杩愯銆備互涓嬫槸docker杩愯鐨勬柟娉�
+
+```
+# 杩涘叆鏈」鐩簮鐮佹牴鐩綍
+cd mcp-endpoint-server
+
+# 娓呴櫎缂撳瓨
+docker compose -f docker-compose.yml down
+docker stop mcp-endpoint-server
+docker rm mcp-endpoint-server
+docker rmi ghcr.nju.edu.cn/xinnan-tech/mcp-endpoint-server:latest
+
+# 鍚姩docker瀹瑰櫒
+docker compose -f docker-compose.yml up -d
+# 鏌ョ湅鏃ュ織
+docker logs -f mcp-endpoint-server
+```
+
+姝ゆ椂锛屾棩蹇楅噷浼氳緭鍑虹被浼间互涓嬬殑鏃ュ織
+```
+250705 INFO-=====涓嬮潰鐨勫湴鍧�鍒嗗埆鏄櫤鎺у彴/鍗曟ā鍧桵CP鎺ュ叆鐐瑰湴鍧�====
+250705 INFO-鏅烘帶鍙癕CP鍙傛暟閰嶇疆: http://172.22.0.2:8004/mcp_endpoint/health?key=abc
+250705 INFO-鍗曟ā鍧楅儴缃睲CP鎺ュ叆鐐�: ws://172.22.0.2:8004/mcp_endpoint/mcp/?token=def
+250705 INFO-=====璇锋牴鎹叿浣撻儴缃查�夋嫨浣跨敤锛岃鍕挎硠闇茬粰浠讳綍浜�======
+```
+
+璇蜂綘鎶婁袱涓帴鍙e湴鍧�澶嶅埗鍑烘潵锛�
+
+鐢变簬浣犳槸docker閮ㄧ讲锛屽垏涓嶅彲鐩存帴浣跨敤涓婇潰鐨勫湴鍧�锛�
+
+鐢变簬浣犳槸docker閮ㄧ讲锛屽垏涓嶅彲鐩存帴浣跨敤涓婇潰鐨勫湴鍧�锛�
+
+鐢变簬浣犳槸docker閮ㄧ讲锛屽垏涓嶅彲鐩存帴浣跨敤涓婇潰鐨勫湴鍧�锛�
+
+浣犲厛鎶婂湴鍧�澶嶅埗鍑烘潵锛屾斁鍦ㄤ竴涓崏绋块噷锛屼綘瑕佺煡閬撲綘鐨勭數鑴戠殑灞�鍩熺綉ip鏄粈涔堬紝渚嬪鎴戠殑鐢佃剳灞�鍩熺綉ip鏄痐192.168.1.25`锛岄偅涔�
+鍘熸潵鎴戠殑鎺ュ彛鍦板潃
+```
+鏅烘帶鍙癕CP鍙傛暟閰嶇疆: http://172.22.0.2:8004/mcp_endpoint/health?key=abc
+鍗曟ā鍧楅儴缃睲CP鎺ュ叆鐐�: ws://172.22.0.2:8004/mcp_endpoint/mcp/?token=def
+```
+灏辫鏀规垚
+```
+鏅烘帶鍙癕CP鍙傛暟閰嶇疆: http://192.168.1.25:8004/mcp_endpoint/health?key=abc
+鍗曟ā鍧楅儴缃睲CP鎺ュ叆鐐�: ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=def
+```
+
+鏀瑰ソ鍚庯紝璇蜂娇鐢ㄦ祻瑙堝櫒鐩存帴璁块棶`鏅烘帶鍙癕CP鍙傛暟閰嶇疆`銆傚綋娴忚鍣ㄥ嚭鐜扮被浼艰繖鏍风殑浠g爜锛岃鏄庢槸鎴愬姛浜嗐��
+```
+{"result":{"status":"success","connections":{"tool_connections":0,"robot_connections":0,"total_connections":0}},"error":null,"id":null,"jsonrpc":"2.0"}
+```
+
+璇蜂綘淇濈暀濂戒笂闈袱涓猔鎺ュ彛鍦板潃`锛屼笅涓�姝ヨ鐢ㄥ埌銆�
+
+# 2銆佸叏妯″潡閮ㄧ讲鏃讹紝鎬庝箞閰嶇疆MCP鎺ュ叆鐐�
+棣栧厛锛屼綘瑕佸紑鍚疢CP鎺ュ叆鐐瑰姛鑳姐�傚湪鏅烘帶鍙帮紝鐐瑰嚮椤堕儴`鍙傛暟瀛楀吀`锛屽湪涓嬫媺鑿滃崟涓紝鐐瑰嚮`绯荤粺鍔熻兘閰嶇疆`椤甸潰銆傚湪椤甸潰涓婂嬀閫塦MCP鎺ュ叆鐐筦锛岀偣鍑籤淇濆瓨閰嶇疆`銆傚湪`瑙掕壊閰嶇疆`椤甸潰锛岀偣鍑籤缂栬緫鍔熻兘`鎸夐挳锛屽嵆鍙湅鍒癭mcp鎺ュ叆鐐筦鍔熻兘銆�
+
+濡傛灉浣犳槸鍏ㄦā鍧楅儴缃诧紝浣跨敤绠$悊鍛樿处鍙凤紝鐧诲綍鏅烘帶鍙帮紝鐐瑰嚮椤堕儴`鍙傛暟瀛楀吀`锛岄�夋嫨`鍙傛暟绠$悊`鍔熻兘銆�
+
+鐒跺悗鎼滅储鍙傛暟`server.mcp_endpoint`锛屾鏃讹紝瀹冪殑鍊煎簲璇ユ槸`null`鍊笺��
+鐐瑰嚮淇敼鎸夐挳锛屾妸涓婁竴姝ュ緱鏉ョ殑`鏅烘帶鍙癕CP鍙傛暟閰嶇疆`绮樿创鍒癭鍙傛暟鍊糮閲屻�傜劧鍚庝繚瀛樸��
+
+濡傛灉鑳戒繚瀛樻垚鍔燂紝璇存槑涓�鍒囬『鍒╋紝浣犲彲浠ュ幓鏅鸿兘浣撴煡鐪嬫晥鏋滀簡銆傚鏋滀笉鎴愬姛锛岃鏄庢櫤鎺у彴鏃犳硶璁块棶mcp鎺ュ叆鐐癸紝寰堝ぇ姒傜巼鏄綉缁滈槻鐏锛屾垨鑰呮病鏈夊~鍐欐纭殑灞�鍩熺綉ip銆�
+
+# 3銆佸崟妯″潡閮ㄧ讲鏃讹紝鎬庝箞閰嶇疆MCP鎺ュ叆鐐�
+
+濡傛灉浣犳槸鍗曟ā鍧楅儴缃诧紝鎵惧埌浣犵殑閰嶇疆鏂囦欢`data/.config.yaml`銆�
+鍦ㄩ厤缃枃浠舵悳绱mcp_endpoint`锛屽鏋滄病鏈夋壘鍒帮紝浣犲氨澧炲姞`mcp_endpoint`閰嶇疆銆傜被浼兼垜鏄氨鏄繖鏍�
+```
+server:
+ websocket: ws://浣犵殑ip鎴栬�呭煙鍚�:绔彛鍙�/xiaozhi/v1/
+ http_port: 8002
+log:
+ log_level: INFO
+
+# 姝ゅ鍙兘杩樻洿澶氶厤缃�..
+
+mcp_endpoint: 浣犵殑鎺ュ叆鐐箇ebsocket鍦板潃
+```
+杩欐椂锛岃浣犳妸`濡備綍閮ㄧ讲MCP鎺ュ叆鐐硅繖涓湇鍔涓緱鍒扮殑`鍗曟ā鍧楅儴缃睲CP鎺ュ叆鐐筦 绮樿创鍒� `mcp_endpoint`涓�傜被浼艰繖鏍�
+
+```
+server:
+ websocket: ws://浣犵殑ip鎴栬�呭煙鍚�:绔彛鍙�/xiaozhi/v1/
+ http_port: 8002
+log:
+ log_level: INFO
+
+# 姝ゅ鍙兘杩樻洿澶氶厤缃�
+
+mcp_endpoint: ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=def
+```
+
+閰嶇疆濂藉悗锛屽惎鍔ㄥ崟妯″潡浼氳緭鍑哄涓嬬殑鏃ュ織銆�
+```
+250705[__main__]-INFO-鍒濆鍖栫粍浠�: vad鎴愬姛 SileroVAD
+250705[__main__]-INFO-鍒濆鍖栫粍浠�: asr鎴愬姛 FunASRServer
+250705[__main__]-INFO-OTA鎺ュ彛鏄� http://192.168.1.25:8002/xiaozhi/ota/
+250705[__main__]-INFO-瑙嗚鍒嗘瀽鎺ュ彛鏄� http://192.168.1.25:8002/mcp/vision/explain
+250705[__main__]-INFO-mcp鎺ュ叆鐐规槸 ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=abc
+250705[__main__]-INFO-Websocket鍦板潃鏄� ws://192.168.1.25:8000/xiaozhi/v1/
+250705[__main__]-INFO-=======涓婇潰鐨勫湴鍧�鏄痺ebsocket鍗忚鍦板潃锛岃鍕跨敤娴忚鍣ㄨ闂�=======
+250705[__main__]-INFO-濡傛兂娴嬭瘯websocket璇峰惎鍔╠igital-human妯″潡锛屾墦寮�娴忚鍣ㄤ氦浜掓祴璇�
+250705[__main__]-INFO-=============================================================
+```
+
+濡備笂锛屽鏋滆兘杈撳嚭绫讳技鐨刞mcp鎺ュ叆鐐规槸`涓璥ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=abc`璇存槑閰嶇疆鎴愬姛浜嗐��
+
diff --git a/xiaozhi-esp32-server/docs/mcp-endpoint-integration.md b/xiaozhi-esp32-server/docs/mcp-endpoint-integration.md
new file mode 100644
index 0000000..eec4196
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/mcp-endpoint-integration.md
@@ -0,0 +1,94 @@
+# MCP 鎺ュ叆鐐逛娇鐢ㄦ寚鍗�
+
+鏈暀绋嬩互铏惧摜寮�婧愮殑mcp璁$畻鍣ㄥ姛鑳戒负绀轰緥锛屼粙缁嶅浣曞皢鑷繁鑷畾涔夌殑mcp鏈嶅姟鎺ュ叆鍒拌嚜宸辩殑鎺ュ叆鐐归噷銆�
+
+鏈暀绋嬬殑鍓嶆彁鏄紝浣犵殑`xiaozhi-server`宸茬粡鍚敤浜唌cp鎺ュ叆鐐瑰姛鑳斤紝濡傛灉浣犺繕娌″惎鐢紝鍙互鍏堟牴鎹甗杩欎釜鏁欑▼](./mcp-endpoint-enable.md)鍚敤銆�
+
+# 濡備綍涓烘櫤鑳戒綋鎺ュ叆涓�涓畝鍗曠殑mcp鍔熻兘锛屽璁$畻鍣ㄥ姛鑳�
+
+### 濡傛灉浣犳槸鍏ㄦā鍧楅儴缃�
+濡傛灉浣犳槸鍏ㄦā鍧楅儴缃诧紝浣犲彲浠ヨ繘鍏ユ櫤鎺у彴锛屾櫤鑳戒綋绠$悊锛岀偣鍑籤閰嶇疆瑙掕壊`锛屽湪`鎰忓浘璇嗗埆`鐨勫彸杈癸紝鏈変竴涓猔缂栬緫鍔熻兘`鐨勬寜閽��
+
+鐐瑰嚮杩欎釜鎸夐挳銆傚湪寮瑰嚭鐨勯〉闈㈤噷锛屼綅浜庡簳閮紝浼氭湁`MCP鎺ュ叆鐐筦锛屾甯告潵璇达紝浼氭樉绀鸿繖涓櫤鑳戒綋鐨刞MCP鎺ュ叆鐐瑰湴鍧�`锛屾帴涓嬫潵锛屾垜浠潵缁欒繖涓櫤鑳戒綋鎵╁睍涓�涓熀浜嶮CP鎶�鏈殑璁$畻鍣ㄧ殑鍔熻兘銆�
+
+杩欎釜`MCP鎺ュ叆鐐瑰湴鍧�`寰堥噸瑕侊紝浣犵瓑涓�涓嬩細鐢ㄥ埌銆�
+
+### 濡傛灉浣犳槸鍗曟ā鍧楅儴缃�
+濡傛灉浣犳槸鍗曟ā鍧楅儴缃诧紝涓斾綘宸茬粡鍦ㄩ厤缃枃浠堕噷閰嶇疆浜哅CP鎺ュ叆鐐瑰湴鍧�锛岄偅涔堟甯告潵璇达紝鍗曟ā鍧楅儴缃插惎鍔ㄧ殑鏃跺�欙紝浼氳緭鍑哄涓嬬殑鏃ュ織銆�
+```
+250705[__main__]-INFO-鍒濆鍖栫粍浠�: vad鎴愬姛 SileroVAD
+250705[__main__]-INFO-鍒濆鍖栫粍浠�: asr鎴愬姛 FunASRServer
+250705[__main__]-INFO-OTA鎺ュ彛鏄� http://192.168.1.25:8002/xiaozhi/ota/
+250705[__main__]-INFO-瑙嗚鍒嗘瀽鎺ュ彛鏄� http://192.168.1.25:8002/mcp/vision/explain
+250705[__main__]-INFO-mcp鎺ュ叆鐐规槸 ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=abc
+250705[__main__]-INFO-Websocket鍦板潃鏄� ws://192.168.1.25:8000/xiaozhi/v1/
+250705[__main__]-INFO-=======涓婇潰鐨勫湴鍧�鏄痺ebsocket鍗忚鍦板潃锛岃鍕跨敤娴忚鍣ㄨ闂�=======
+250705[__main__]-INFO-濡傛兂娴嬭瘯websocket璇峰惎鍔╠igital-human妯″潡锛屾墦寮�娴忚鍣ㄤ氦浜掓祴璇�
+250705[__main__]-INFO-=============================================================
+```
+
+濡備笂锛岃緭鍑篳mcp鎺ュ叆鐐规槸`涓璥ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=abc`灏辨槸浣犵殑`MCP鎺ュ叆鐐瑰湴鍧�`銆�
+
+杩欎釜`MCP鎺ュ叆鐐瑰湴鍧�`寰堥噸瑕侊紝浣犵瓑涓�涓嬩細鐢ㄥ埌銆�
+
+## 绗竴姝� 涓嬭浇铏惧摜MCP璁$畻鍣ㄩ」鐩唬鐮�
+
+娴忚鍣ㄦ墦寮�铏惧摜鍐欑殑[璁$畻鍣ㄩ」鐩甝(https://github.com/78/mcp-calculator)锛�
+
+鎵撳紑瀹岋紝鎵惧埌椤甸潰涓竴涓豢鑹茬殑鎸夐挳锛屽啓鐫�`Code`鐨勬寜閽紝鐐瑰紑瀹冿紝鐒跺悗浣犲氨鐪嬪埌`Download ZIP`鐨勬寜閽��
+
+鐐瑰嚮瀹冿紝涓嬭浇鏈」鐩簮鐮佸帇缂╁寘銆備笅杞藉埌浣犵數鑴戝悗锛岃В鍘嬪畠锛屾鏃跺畠鐨勫悕瀛楀彲鑳藉彨`mcp-calculatorr-main`
+浣犻渶瑕佹妸瀹冮噸鍛藉悕鎴恅mcp-calculator`銆傛帴涓嬫潵锛屾垜浠敤鍛戒护琛岃繘鍏ラ」鐩洰褰曞嵆瀹夎渚濊禆
+
+
+```bash
+# 杩涘叆椤圭洰鐩綍
+cd mcp-calculator
+
+conda remove -n mcp-calculator --all -y
+conda create -n mcp-calculator python=3.10 -y
+conda activate mcp-calculator
+
+pip install -r requirements.txt
+```
+
+## 绗簩姝� 鍚姩
+
+鍚姩鍓嶏紝鍏堜粠浣犵殑鏅烘帶鍙扮殑鏅鸿兘浣撻噷锛屽鍒跺埌浜哅CP鎺ュ叆鐐圭殑鍦板潃銆�
+
+渚嬪鎴戠殑鏅鸿兘浣撶殑mcp鍦板潃鏄�
+```
+ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=abc
+```
+
+寮�濮嬭緭鍏ュ懡浠�
+
+```bash
+export MCP_ENDPOINT=ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=abc
+```
+
+杈撳叆瀹屽悗锛屽惎鍔ㄧ▼搴�
+
+```bash
+python mcp_pipe.py calculator.py
+```
+
+### 濡傛灉浣犳槸鏅烘帶鍙伴儴缃�
+濡傛灉浣犳槸鏅烘帶鍙伴儴缃诧紝鍚姩瀹屽悗锛屼綘鍐嶈繘鍏ユ櫤鎺у彴锛岀偣鍑诲埛鏂癕CP鐨勬帴鍏ョ姸鎬侊紝灏变細鐪嬪埌浣犳墿灞曠殑鍔熻兘鍒楄〃浜嗐��
+
+### 濡傛灉浣犳槸鍗曟ā鍧楅儴缃�
+濡傛灉浣犳槸鍗曟ā鍧楅儴缃诧紝褰撹澶囪繛鎺ュ悗锛屼細杈撳嚭绫讳技鐨勬棩蹇楋紝璇存槑鎴愬姛浜�
+
+```
+250705 -INFO-姝e湪鍒濆鍖朚CP鎺ュ叆鐐�: wss://2662r3426b.vicp.fun/mcp_e
+250705 -INFO-鍙戦�丮CP鎺ュ叆鐐瑰垵濮嬪寲娑堟伅
+250705 -INFO-MCP鎺ュ叆鐐硅繛鎺ユ垚鍔�
+250705 -INFO-MCP鎺ュ叆鐐瑰垵濮嬪寲鎴愬姛
+250705 -INFO-缁熶竴宸ュ叿澶勭悊鍣ㄥ垵濮嬪寲瀹屾垚
+250705 -INFO-MCP鎺ュ叆鐐规湇鍔″櫒淇℃伅: name=Calculator, version=1.9.4
+250705 -INFO-MCP鎺ュ叆鐐规敮鎸佺殑宸ュ叿鏁伴噺: 1
+250705 -INFO-鎵�鏈塎CP鎺ュ叆鐐瑰伐鍏峰凡鑾峰彇锛屽鎴风鍑嗗灏辩华
+250705 -INFO-宸ュ叿缂撳瓨宸插埛鏂�
+250705 -INFO-褰撳墠鏀寔鐨勫嚱鏁板垪琛�: [ 'get_time', 'get_lunar', 'play_music', 'get_weather', 'handle_exit_intent', 'calculator']
+```
+濡傛灉鍖呭惈浜� `'calculator'`锛岃鏄庤澶囧皢鍙互鏍规嵁鎰忓浘璇嗗埆锛岃皟鐢ㄨ绠楀櫒杩欎釜宸ュ叿銆�
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/docs/mcp-get-device-info.md b/xiaozhi-esp32-server/docs/mcp-get-device-info.md
new file mode 100644
index 0000000..bf47995
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/mcp-get-device-info.md
@@ -0,0 +1,40 @@
+# MCP 鏂规硶濡備綍鑾峰彇璁惧淇℃伅
+
+鏈暀绋嬪皢鎸囧浣犲浣曚娇鐢∕CP鏂规硶鑾峰彇璁惧淇℃伅銆�
+
+绗竴姝ワ細鑷畾涔変綘鐨刞agent-base-prompt.txt`鏂囦欢
+
+鎶妜iaozhi-server鐩綍鐨刞agent-base-prompt.txt`鏂囦欢鍐呭澶嶅埗鍒颁綘鐨刞data`鐩綍涓嬶紝骞堕噸鍛藉悕涓篳.agent-base-prompt.txt`銆�
+
+绗簩姝ワ細淇敼`data/.agent-base-prompt.txt`鏂囦欢锛屾壘鍒癭<context>`鏍囩锛屽湪鏍囩鍐呭涓坊鍔犱互涓嬩唬鐮佸唴瀹癸細
+```
+- **璁惧ID锛�** {{device_id}}
+```
+
+娣诲姞瀹屾垚鍚庯紝浣犵殑`data/.agent-base-prompt.txt`鏂囦欢鐨刞<context>`鏍囩鍐呭澶ц嚧濡備笅锛�
+```
+<context>
+銆愰噸瑕侊紒浠ヤ笅淇℃伅宸插疄鏃舵彁渚涳紝鏃犻渶璋冪敤宸ュ叿鏌ヨ锛岃鐩存帴浣跨敤锛氥��
+- **璁惧ID锛�** {{device_id}}
+- **褰撳墠鏃堕棿锛�** {{current_time}}
+- **浠婂ぉ鏃ユ湡锛�** {{today_date}} ({{today_weekday}})
+- **浠婂ぉ鍐滃巻锛�** {{lunar_date}}
+- **鐢ㄦ埛鎵�鍦ㄥ煄甯傦細** {{local_address}}
+- **褰撳湴鏈潵7澶╁ぉ姘旓細** {{weather_info}}
+</context>
+```
+
+绗笁姝ワ細淇敼`data/.config.yaml`鏂囦欢锛屾壘鍒癭agent-base-prompt`閰嶇疆锛屼慨鏀瑰墠鍐呭濡備笅锛�
+```
+prompt_template: agent-base-prompt.txt
+```
+淇敼鎴�
+```
+prompt_template: data/.agent-base-prompt.txt
+```
+
+绗洓姝ワ細閲嶅惎浣犵殑xiaozhi-server鏈嶅姟銆�
+
+绗簲姝ワ細鍦ㄤ綘鐨刴cp鏂规硶澧炲姞鍚嶇О涓篳device_id`,绫诲瀷涓篳string`,鎻忚堪涓篳璁惧ID`鐨勫弬鏁般��
+
+绗叚姝ワ細閲嶆柊鍞ら啋灏忔櫤锛岃浠栬皟鐢╩cp鏂规硶锛屾煡鐪嬩綘鐨刴cp鏂规硶鏄惁鍙互鑾峰彇`璁惧ID`銆�
diff --git a/xiaozhi-esp32-server/docs/mcp-vision-integration.md b/xiaozhi-esp32-server/docs/mcp-vision-integration.md
new file mode 100644
index 0000000..5d0cd8c
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/mcp-vision-integration.md
@@ -0,0 +1,171 @@
+# 瑙嗚妯″瀷浣跨敤鎸囧崡
+鏈暀绋嬪垎涓轰袱閮ㄥ垎锛�
+- 绗竴閮ㄥ垎锛氬崟妯″潡杩愯xiaozhi-server寮�鍚瑙夋ā鍨�
+- 绗簩閮ㄥ垎锛氬叏妯″潡杩愯鏃讹紝濡備綍寮�鍚瑙夋ā鍨�
+
+寮�鍚瑙夋ā鍨嬪墠锛屼綘闇�瑕佸噯澶囦笁浠朵簨锛�
+- 浣犻渶瑕佸噯澶囦竴鍙板甫鎽勫儚澶寸殑璁惧锛岃�屼笖杩欏彴璁惧宸茬粡鍦ㄨ櫨鍝ヤ粨搴撻噷锛屽疄鐜颁簡璋冪敤鎽勫儚澶村姛鑳姐�備緥濡俙绔嬪垱路瀹炴垬娲綞SP32-S3寮�鍙戞澘`
+- 浣犺澶囧浐浠剁殑鐗堟湰鍗囩骇鍒�1.6.6鍙婁互涓�
+- 浣犲凡缁忔垚鍔熻窇閫氬熀纭�瀵硅瘽妯″潡
+
+## 鍗曟ā鍧楄繍琛寈iaozhi-server寮�鍚瑙夋ā鍨�
+
+### 绗竴姝ョ‘璁ょ綉缁�
+鐢变簬瑙嗚妯″瀷浼氶粯璁ゅ惎鍔�8003绔彛銆�
+
+濡傛灉浣犳槸docker杩愯锛岃纭涓�涓嬩綘鐨刞docker-compose.yml`鏄惁鏀句簡`8003`绔彛锛屽鏋滄病鏈夊氨鏇存柊鏈�鏂扮殑`docker-compose.yml`鏂囦欢
+
+濡傛灉浣犳槸婧愮爜杩愯锛岀‘璁ら槻鐏鏄惁鏀捐`8003`绔彛
+
+### 绗簩姝ラ�夋嫨浣犵殑瑙嗚妯″瀷
+鎵撳紑浣犵殑`data/.config.yaml`鏂囦欢锛岃缃綘鐨刞selected_module.VLLM`璁剧疆涓烘煇涓瑙夋ā鍨嬨�傜洰鍓嶆垜浠凡缁忔敮鎸乣openai`绫诲瀷鎺ュ彛鐨勮瑙夋ā鍨嬨�俙ChatGLMVLLM`灏辨槸鍏朵腑涓�娆惧吋瀹筦openai`鐨勬ā鍨嬨��
+
+```
+selected_module:
+ VAD: ..
+ ASR: ..
+ LLM: ..
+ VLLM: ChatGLMVLLM
+ TTS: ..
+ Memory: ..
+ Intent: ..
+```
+
+鍋囪鎴戜滑浣跨敤`ChatGLMVLLM`浣滀负瑙嗚妯″瀷锛岄偅鎴戜滑闇�瑕佸厛鐧诲綍[鏅鸿氨AI](https://bigmodel.cn/usercenter/proj-mgmt/apikeys)缃戠珯锛岀敵璇峰瘑閽ャ�傚鏋滀綘涔嬪墠宸茬粡鐢宠杩囦簡瀵嗛挜锛屽彲浠ュ鐢ㄨ繖涓瘑閽ャ��
+
+鍦ㄤ綘鐨勯厤缃枃浠朵腑锛屽鍔犺繖涓厤缃紝濡傛灉宸茬粡鏈変簡杩欎釜閰嶇疆锛屽氨璁剧疆濂戒綘鐨刟pi_key銆�
+
+```
+VLLM:
+ ChatGLMVLLM:
+ api_key: 浣犵殑api_key
+```
+
+### 绗笁姝ュ惎鍔▁iaozhi-server鏈嶅姟
+濡傛灉浣犳槸婧愮爜锛屽氨杈撳叆鍛戒护鍚姩
+```
+python app.py
+```
+濡傛灉浣犳槸docker杩愯锛屽氨閲嶅惎瀹瑰櫒
+```
+docker restart xiaozhi-esp32-server
+```
+
+鍚姩鍚庝細杈撳嚭浠ヤ笅鍐呭鐨勬棩蹇椼��
+
+```
+2025-06-01 **** - OTA鎺ュ彛鏄� http://192.168.4.7:8003/xiaozhi/ota/
+2025-06-01 **** - 瑙嗚鍒嗘瀽鎺ュ彛鏄� http://192.168.4.7:8003/mcp/vision/explain
+2025-06-01 **** - Websocket鍦板潃鏄� ws://192.168.4.7:8000/xiaozhi/v1/
+2025-06-01 **** - =======涓婇潰鐨勫湴鍧�鏄痺ebsocket鍗忚鍦板潃锛岃鍕跨敤娴忚鍣ㄨ闂�=======
+2025-06-01 **** - 濡傛兂娴嬭瘯websocket璇峰惎鍔╠igital-human妯″潡锛屾墦寮�娴忚鍣ㄤ氦浜掓祴璇�
+2025-06-01 **** - =============================================================
+```
+
+鍚姩鍚庯紝浣跨敤浣跨敤娴忚鍣ㄦ墦寮�鏃ュ織閲宍瑙嗚鍒嗘瀽鎺ュ彛`杩炴帴銆傜湅鐪嬭緭鍑轰簡浠�涔堬紵濡傛灉浣犳槸linux,娌℃湁娴忚鍣紝浣犲彲浠ユ墽琛岃繖涓懡浠わ細
+```
+curl -i 浣犵殑瑙嗚鍒嗘瀽鎺ュ彛
+```
+
+姝e父鏉ヨ浼氳繖鏍锋樉绀�
+```
+MCP Vision 鎺ュ彛杩愯姝e父锛岃瑙夎В閲婃帴鍙e湴鍧�鏄細http://xxxx:8003/mcp/vision/explain
+```
+
+璇锋敞鎰忥紝濡傛灉浣犳槸鍏綉閮ㄧ讲锛屾垨鑰卍ocker閮ㄧ讲锛屼竴瀹氳鏀逛竴涓嬩綘鐨刞data/.config.yaml`閲岃繖涓厤缃�
+```
+server:
+ vision_explain: http://浣犵殑ip鎴栬�呭煙鍚�:绔彛鍙�/mcp/vision/explain
+```
+
+涓轰粈涔堝憿锛熷洜涓鸿瑙夎В閲婃帴鍙i渶瑕佷笅鍙戝埌璁惧锛屽鏋滀綘鐨勫湴鍧�鏄眬鍩熺綉鍦板潃锛屾垨鑰呮槸docker鍐呴儴鍦板潃锛岃澶囨槸鏃犳硶璁块棶鐨勩��
+
+鍋囪浣犵殑鍏綉鍦板潃鏄痐111.111.111.111`锛岄偅涔坄vision_explain`搴旇杩欎箞閰�
+
+```
+server:
+ vision_explain: http://111.111.111.111:8003/mcp/vision/explain
+```
+
+濡傛灉浣犵殑MCP Vision 鎺ュ彛杩愯姝e父锛屼笖浣犱篃璇曠潃鐢ㄦ祻瑙堝櫒璁块棶姝e父鎵撳紑涓嬪彂鐨刞瑙嗚瑙i噴鎺ュ彛鍦板潃`锛岃缁х画涓嬩竴姝�
+
+### 绗洓姝� 璁惧鍞ら啋寮�鍚�
+
+瀵硅澶囪鈥滆鎵撳紑鎽勫儚澶达紝璇翠綘浣犵湅鍒颁簡浠�涔堚��
+
+鐣欐剰xiaozhi-server鐨勬棩蹇楄緭鍑猴紝鐪嬬湅鏈夋病鏈夋姤閿欍��
+
+
+## 鍏ㄦā鍧楄繍琛屾椂锛屽浣曞紑鍚瑙夋ā鍨�
+
+### 绗竴姝� 纭缃戠粶
+鐢变簬瑙嗚妯″瀷浼氶粯璁ゅ惎鍔�8003绔彛銆�
+
+濡傛灉浣犳槸docker杩愯锛岃纭涓�涓嬩綘鐨刞docker-compose_all.yml`鏄惁鏄犲皠浜哷8003`绔彛锛屽鏋滄病鏈夊氨鏇存柊鏈�鏂扮殑`docker-compose_all.yml`鏂囦欢
+
+濡傛灉浣犳槸婧愮爜杩愯锛岀‘璁ら槻鐏鏄惁鏀捐`8003`绔彛
+
+### 绗簩姝� 纭浣犻厤缃枃浠�
+
+鎵撳紑浣犵殑`data/.config.yaml`鏂囦欢锛岀‘璁や竴涓嬩綘鐨勯厤缃枃浠剁殑缁撴瀯锛屾槸鍚﹀拰`data/config_from_api.yaml`涓�鏍枫�傚鏋滀笉涓�鏍凤紝鎴栫己灏戞煇椤癸紝璇疯ˉ榻愩��
+
+### 绗笁姝� 閰嶇疆瑙嗚妯″瀷瀵嗛挜
+
+閭f垜浠渶瑕佸厛鐧诲綍[鏅鸿氨AI](https://bigmodel.cn/usercenter/proj-mgmt/apikeys)缃戠珯锛岀敵璇峰瘑閽ャ�傚鏋滀綘涔嬪墠宸茬粡鐢宠杩囦簡瀵嗛挜锛屽彲浠ュ鐢ㄨ繖涓瘑閽ャ��
+
+鐧诲綍`鏅烘帶鍙癭锛岄《閮ㄨ彍鍗曠偣鍑籤妯″瀷閰嶇疆`锛屽湪宸︿晶鏍忕偣鍑籤瑙嗚鎵撹瑷�妯″瀷`锛屾壘鍒癭VLLM_ChatGLMVLLM`锛岀偣鍑讳慨鏀规寜閽紝鍦ㄥ脊妗嗕腑锛屽湪`API瀵嗛挜`杈撳叆浣犲瘑閽ワ紝鐐瑰嚮淇濆瓨銆�
+
+淇濆瓨鎴愬姛鍚庯紝鍘诲埌浣犻渶瑕佹祴璇曠殑鏅鸿兘浣撳摢閲岋紝鐐瑰嚮`閰嶇疆瑙掕壊`锛屽湪鎵撳紑鐨勫唴瀹归噷锛屾煡鐪媊瑙嗚澶ц瑷�妯″瀷(VLLM)`鏄惁閫夋嫨浜嗗垰鎵嶇殑瑙嗚妯″瀷銆傜偣鍑讳繚瀛樸��
+
+### 绗笁姝� 鍚姩xiaozhi-server妯″潡
+濡傛灉浣犳槸婧愮爜锛屽氨杈撳叆鍛戒护鍚姩
+```
+python app.py
+```
+濡傛灉浣犳槸docker杩愯锛屽氨閲嶅惎瀹瑰櫒
+```
+docker restart xiaozhi-esp32-server
+```
+
+鍚姩鍚庝細杈撳嚭浠ヤ笅鍐呭鐨勬棩蹇椼��
+
+```
+2025-06-01 **** - 瑙嗚鍒嗘瀽鎺ュ彛鏄� http://192.168.4.7:8003/mcp/vision/explain
+2025-06-01 **** - Websocket鍦板潃鏄� ws://192.168.4.7:8000/xiaozhi/v1/
+2025-06-01 **** - =======涓婇潰鐨勫湴鍧�鏄痺ebsocket鍗忚鍦板潃锛岃鍕跨敤娴忚鍣ㄨ闂�=======
+2025-06-01 **** - 濡傛兂娴嬭瘯websocket璇峰惎鍔╠igital-human妯″潡锛屾墦寮�娴忚鍣ㄤ氦浜掓祴璇�
+2025-06-01 **** - =============================================================
+```
+
+鍚姩鍚庯紝浣跨敤浣跨敤娴忚鍣ㄦ墦寮�鏃ュ織閲宍瑙嗚鍒嗘瀽鎺ュ彛`杩炴帴銆傜湅鐪嬭緭鍑轰簡浠�涔堬紵濡傛灉浣犳槸linux,娌℃湁娴忚鍣紝浣犲彲浠ユ墽琛岃繖涓懡浠わ細
+```
+curl -i 浣犵殑瑙嗚鍒嗘瀽鎺ュ彛
+```
+
+姝e父鏉ヨ浼氳繖鏍锋樉绀�
+```
+MCP Vision 鎺ュ彛杩愯姝e父锛岃瑙夎В閲婃帴鍙e湴鍧�鏄細http://xxxx:8003/mcp/vision/explain
+```
+
+璇锋敞鎰忥紝濡傛灉浣犳槸鍏綉閮ㄧ讲锛屾垨鑰卍ocker閮ㄧ讲锛屼竴瀹氳鏀逛竴涓嬩綘鐨刞data/.config.yaml`閲岃繖涓厤缃�
+```
+server:
+ vision_explain: http://浣犵殑ip鎴栬�呭煙鍚�:绔彛鍙�/mcp/vision/explain
+```
+
+涓轰粈涔堝憿锛熷洜涓鸿瑙夎В閲婃帴鍙i渶瑕佷笅鍙戝埌璁惧锛屽鏋滀綘鐨勫湴鍧�鏄眬鍩熺綉鍦板潃锛屾垨鑰呮槸docker鍐呴儴鍦板潃锛岃澶囨槸鏃犳硶璁块棶鐨勩��
+
+鍋囪浣犵殑鍏綉鍦板潃鏄痐111.111.111.111`锛岄偅涔坄vision_explain`搴旇杩欎箞閰�
+
+```
+server:
+ vision_explain: http://111.111.111.111:8003/mcp/vision/explain
+```
+
+濡傛灉浣犵殑MCP Vision 鎺ュ彛杩愯姝e父锛屼笖浣犱篃璇曠潃鐢ㄦ祻瑙堝櫒璁块棶姝e父鎵撳紑涓嬪彂鐨刞瑙嗚瑙i噴鎺ュ彛鍦板潃`锛岃缁х画涓嬩竴姝�
+
+### 绗洓姝� 璁惧鍞ら啋寮�鍚�
+
+瀵硅澶囪鈥滆鎵撳紑鎽勫儚澶达紝璇翠綘浣犵湅鍒颁簡浠�涔堚��
+
+鐣欐剰xiaozhi-server鐨勬棩蹇楄緭鍑猴紝鐪嬬湅鏈夋病鏈夋姤閿欍��
diff --git a/xiaozhi-esp32-server/docs/mqtt-gateway-integration.md b/xiaozhi-esp32-server/docs/mqtt-gateway-integration.md
new file mode 100644
index 0000000..5f8bd51
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/mqtt-gateway-integration.md
@@ -0,0 +1,187 @@
+# MQTT 缃戝叧閮ㄧ讲鏁欑▼
+
+`xiaozhi-esp32-server`椤圭洰锛屽彲缁撳悎铏惧摜寮�婧愮殑[xiaozhi-mqtt-gateway](https://github.com/78/xiaozhi-mqtt-gateway) 椤圭洰杩涜绠�鍗曟敼閫狅紝鍗冲彲瀹炵幇灏忔櫤纭欢MQTT+UDP杩炴帴銆�
+鏈暀绋嬪垎涓轰笁閮ㄥ垎锛屼綘鍙互鏍规嵁浣犳槸鍏ㄦā鍧楅儴缃茶繕鏄崟妯″潡閮ㄧ讲锛岄�夋嫨瀵瑰簲鐨勯儴鍒嗘帴鍏QTT缃戝叧锛�
+- 绗竴閮ㄥ垎锛氶儴缃睲QTT缃戝叧
+- 绗簩閮ㄥ垎锛氬叏妯″潡杩愯瀹炵幇灏忔櫤纭欢MQTT+UDP杩炴帴
+- 绗笁閮ㄥ垎锛氬崟妯″潡杩愯xiaozhi-server瀹炵幇灏忔櫤纭欢MQTT+UDP杩炴帴
+
+## 鍑嗗闃舵
+鍑嗗濂戒綘鐨刞xiaozhi-server`鐨刞mqtt-websocket`杩炴帴鍦板潃銆傚湪浣犲師鏉ョ殑`websocket鍦板潃`鍩虹涓婏紝娣诲姞`?from=mqtt_gateway`瀛楃锛屽氨鍙互寰楀埌`mqtt-websocket`杩炴帴鍦板潃
+
+1銆佸鏋滀綘鏄簮鐮侀儴缃诧紝浣犵殑`mqtt-websocket`鍦板潃鏄細
+```
+ws://127.0.0.1:8000/xiaozhi/v1/?from=mqtt_gateway
+```
+
+2銆佸鏋滀綘鏄痙ocker閮ㄧ讲锛屼綘鐨刞mqtt-websocket`鍦板潃鏄�
+```
+ws://浣犲涓绘満灞�鍩熺綉IP:8000/xiaozhi/v1/?from=mqtt_gateway
+```
+
+## 閲嶈鎻愮ず
+
+濡傛灉浣犳槸鏈嶅姟鍣ㄩ儴缃诧紝闇�瑕佺‘淇濇湇鍔″櫒`1883`銆乣8884`銆乣8007`绔彛閮藉澶栧紑鏀俱�俙8884`閫夋嫨鐨勫崗璁被鍨嬫槸`UDP`锛屽叾浠栨槸`TCP`銆�
+
+濡傛灉浣犳槸鏈嶅姟鍣ㄩ儴缃诧紝闇�瑕佺‘淇濇湇鍔″櫒`1883`銆乣8884`銆乣8007`绔彛閮藉澶栧紑鏀俱�俙8884`閫夋嫨鐨勫崗璁被鍨嬫槸`UDP`锛屽叾浠栨槸`TCP`銆�
+
+濡傛灉浣犳槸鏈嶅姟鍣ㄩ儴缃诧紝闇�瑕佺‘淇濇湇鍔″櫒`1883`銆乣8884`銆乣8007`绔彛閮藉澶栧紑鏀俱�俙8884`閫夋嫨鐨勫崗璁被鍨嬫槸`UDP`锛屽叾浠栨槸`TCP`銆�
+
+
+## 绗竴閮ㄥ垎锛氶儴缃睲QTT缃戝叧
+
+1. 鍏嬮殕[鏀归�犲悗鐨剎iaozhi-mqtt-gateway椤圭洰](https://github.com/xinnan-tech/xiaozhi-mqtt-gateway.git)锛�
+```bash
+git clone https://ghfast.top/https://github.com/xinnan-tech/xiaozhi-mqtt-gateway.git
+cd xiaozhi-mqtt-gateway
+```
+
+2. 瀹夎渚濊禆锛�
+```bash
+npm install
+npm install -g pm2
+```
+
+3. 閰嶇疆 `config.json`锛�
+```bash
+cp config/mqtt.json.example config/mqtt.json
+```
+
+4. 缂栬緫閰嶇疆鏂囦欢 config/mqtt.json锛屾妸浣犲湪`鏈枃鍑嗗闃舵`鐨刞mqtt-websocket`鍦板潃鏇挎崲鍒癭chat_servers`閲屻�備緥濡傛簮鐮侀儴缃茬殑`xiaozhi-server`灏辨槸濡備笅閰嶇疆锛�
+
+```
+{
+ "production": {
+ "chat_servers": [
+ "ws://127.0.0.1:8000/xiaozhi/v1/?from=mqtt_gateway"
+ ]
+ },
+ "debug": false,
+ "max_mqtt_payload_size": 8192,
+ "mcp_client": {
+ "capabilities": {
+ },
+ "client_info": {
+ "name": "xiaozhi-mqtt-client",
+ "version": "1.0.0"
+ },
+ "max_tools_count": 128
+ }
+}
+```
+5. 鍦ㄩ」鐩牴鐩綍鍒涘缓涓媊.env`鏂囦欢锛屽苟璁剧疆浠ヤ笅鐜鍙橀噺:
+```
+PUBLIC_IP=your-ip # 鏈嶅姟鍣ㄥ叕缃慖P
+MQTT_PORT=1883 # MQTT鏈嶅姟鍣ㄧ鍙�
+UDP_PORT=8884 # UDP鏈嶅姟鍣ㄧ鍙�
+API_PORT=8007 # 绠$悊API绔彛
+MQTT_SIGNATURE_KEY=test # MQTT绛惧悕瀵嗛挜
+SERVER_SECRET=Te1st12134 # 鏈嶅姟鍣ㄥ瘑閽ワ紝璇蜂繚鎸佸拰鏅烘帶鍙帮紙server.secret锛変竴鑷存垨鑰呭拰xiaozhi-server閲岋紙server.auth_key锛変繚鎸佷竴鑷�
+```
+璇锋敞鎰廯PUBLIC_IP`閰嶇疆锛岀‘淇濆叾涓庡疄闄呭叕缃慖P涓�鑷达紝濡傛灉鏈夊煙鍚嶅氨濉煙鍚嶃��
+
+`MQTT_SIGNATURE_KEY` 鏄敤浜嶮QTT杩炴帴璁よ瘉鐨勫瘑閽ワ紝鏈�濂借缃垚澶嶆潅涓�鐐圭殑锛屾渶濂芥槸璁剧疆鎴�8涓瓧绗︿互涓婁笖鍚屾椂鍖呭惈澶у皬鍐欏瓧姣嶏紝杩欎釜瀵嗛挜绋嶅悗杩樹細鐢ㄥ埌銆�
+
+- 娉ㄦ剰涓嶈鐢ㄧ畝鍗曠殑瀵嗙爜锛屾瘮濡俙123456`銆乣test`绛夈��
+- 娉ㄦ剰涓嶈鐢ㄧ畝鍗曠殑瀵嗙爜锛屾瘮濡俙123456`銆乣test`绛夈��
+- 娉ㄦ剰涓嶈鐢ㄧ畝鍗曠殑瀵嗙爜锛屾瘮濡俙123456`銆乣test`绛夈��
+
+`SERVER_SECRET` 鏄敤鐢熸垚websocket杩炴帴鐨勮璇佷俊鎭��
+
+1銆佸鏋滀綘鏄叏妯″潡閮ㄧ讲锛屼笖浣犵殑鏅烘帶鍙扮殑鍙傛暟绠$悊閲宍server.auth.enabled`璁剧疆鎴愪簡`true`锛岄偅涔堬紝`SERVER_SECRET`闇�瑕佸拰鏅烘帶鍙帮紙`server.secret`锛変繚鎸佷竴鑷淬��
+
+2銆佸鏋滀綘鏄崟妯″潡閮ㄧ讲锛屼笖浣犲湪閰嶇疆鏂囦欢閲屾妸`server.auth.enabled`璁剧疆鎴愪簡`true`锛岄偅涔堬紝`SERVER_SECRET`闇�瑕佸拰閰嶇疆鏂囦欢閲岋紙`server.auth_key`锛変繚鎸佷竴鑷淬��
+
+
+6. 鍚姩MQTT缃戝叧
+```
+# 鍚姩鏈嶅姟
+pm2 start ecosystem.config.js
+
+# 鏌ョ湅鏃ュ織
+pm2 logs xz-mqtt
+```
+
+褰撲綘鐪嬪埌濡備笅鏃ュ織锛岃鏄嶮QTT缃戝叧鍚姩鎴愬姛锛�
+```
+0|xz-mqtt | 2025-09-11T12:14:48: MQTT 鏈嶅姟鍣ㄦ鍦ㄧ洃鍚鍙� 1883
+0|xz-mqtt | 2025-09-11T12:14:48: UDP 鏈嶅姟鍣ㄦ鍦ㄧ洃鍚� x.x.x.x:8884
+```
+
+濡傛灉闇�瑕侀噸鍚疢QTT缃戝叧锛屾墽琛屽涓嬪懡浠わ細
+```
+pm2 restart xz-mqtt
+```
+
+## 绗簩閮ㄥ垎锛氬叏妯″潡杩愯瀹炵幇灏忔櫤纭欢MQTT+UDP杩炴帴
+
+鏌ョ湅浣犳櫤鎺у彴棣栭〉搴曢儴鐨勭増鏈彿锛岀‘璁や綘鐨勬櫤鎺у彴鐗堟湰鏄惁鏄痐0.7.7`鍙婁互涓婄増鏈�傚鏋滀笉鏄紝闇�瑕佸崌绾ф櫤鎺у彴銆�
+
+1. 鍦ㄦ櫤鎺у彴椤堕儴锛岀偣鍑籤鍙傛暟绠$悊`锛屾悳绱server.mqtt_gateway`锛岀偣鍑荤紪杈戯紝濉叆浣犲湪`.env`鏂囦欢涓缃殑`PUBLIC_IP`+`:`+`MQTT_PORT`銆傜被浼艰繖鏍�
+```
+192.168.0.7:1883
+```
+2. 鍦ㄦ櫤鎺у彴椤堕儴锛岀偣鍑籤鍙傛暟绠$悊`锛屾悳绱server.mqtt_signature_key`锛岀偣鍑荤紪杈戯紝濉叆浣犲湪`.env`鏂囦欢涓缃殑`MQTT_SIGNATURE_KEY`銆�
+
+3. 鍦ㄦ櫤鎺у彴椤堕儴锛岀偣鍑籤鍙傛暟绠$悊`锛屾悳绱server.udp_gateway`锛岀偣鍑荤紪杈戯紝濉叆浣犲湪`.env`鏂囦欢涓缃殑`PUBLIC_IP`+`:`+`UDP_PORT`銆傜被浼艰繖鏍�
+```
+192.168.0.7:8884
+```
+4. 鍦ㄦ櫤鎺у彴椤堕儴锛岀偣鍑籤鍙傛暟绠$悊`锛屾悳绱server.mqtt_manager_api`锛岀偣鍑荤紪杈戯紝濉叆浣犲湪`.env`鏂囦欢涓缃殑`PUBLIC_IP`+`:`+`API_PORT`銆傜被浼艰繖鏍�
+```
+192.168.0.7:8007
+```
+
+涓婇潰鐨勯厤缃畬鎴愬悗锛屼綘鍙互浣跨敤curl鍛戒护锛岄獙璇佷綘鐨刼ta鍦板潃鏄惁浼氫笅鍙憁qtt閰嶇疆锛屾妸涓嬮潰鐨刞http://localhost:8002/xiaozhi/ota/`鏀规垚浣犵殑ota鍦板潃
+```
+curl 'http://localhost:8002/xiaozhi/ota/' \
+ -H 'Content-Type: application/json' \
+ -H 'Client-Id: 7b94d69a-9808-4c59-9c9b-704333b38aff' \
+ -H 'Device-Id: 11:22:33:44:55:66' \
+ --data-raw $'{\n "application": {\n "version": "1.0.1",\n "elf_sha256": "1"\n },\n "board": {\n "mac": "11:22:33:44:55:66"\n }\n}'
+```
+
+濡傛灉杩斿洖鐨勫唴瀹瑰寘鍚玚mqtt`鐩稿叧鐨勯厤缃紝璇存槑閰嶇疆鎴愬姛銆傜被浼艰繖鏍�
+
+```
+{"server_time":{"timestamp":1757567894012,"timeZone":"Asia/Shanghai","timezone_offset":480},"activation":{"code":"460609","message":"http://xiaozhi.server.com\n460609","challenge":"11:22:33:44:55:66"},"firmware":{"version":"1.0.1","url":"http://xiaozhi.server.com:8002/xiaozhi/otaMag/download/NOT_ACTIVATED_FIRMWARE_THIS_IS_A_INVALID_URL"},"websocket":{"url":"ws://192.168.4.23:8000/xiaozhi/v1/"},"mqtt":{"endpoint":"192.168.0.7:1883","client_id":"GID_default@@@11_22_33_44_55_66@@@7b94d69a-9808-4c59-9c9b-704333b38aff","username":"eyJpcCI6IjA6MDowOjA6MDowOjA6MSJ9","password":"Y8XP9xcUhVIN9OmbCHT9ETBiYNE3l3Z07Wk46wV9PE8=","publish_topic":"device-server","subscribe_topic":"devices/p2p/11_22_33_44_55_66"}}
+```
+
+鐢变簬MQTT淇℃伅鏄渶瑕侀潬OTA鍦板潃涓嬪彂鐨勶紝鍥犳鍙湁浣犱繚璇佽兘姝e父杩炴帴鏈嶅姟鍣ㄧ殑OTA鍦板潃锛岄噸鍚敜閱掑嵆鍙��
+
+鍞ら啋鍚庣暀鎰弇qtt-gateway鐨勬棩蹇楋紝纭鏄惁鏈夎繛鎺ユ垚鍔熺殑鏃ュ織銆�
+```
+pm2 logs xz-mqtt
+```
+
+## 绗笁閮ㄥ垎锛氬崟妯″潡杩愯xiaozhi-server瀹炵幇灏忔櫤纭欢MQTT+UDP杩炴帴
+
+鎵撳紑浣犵殑`data/.config.yaml`鏂囦欢锛屽湪`server`涓嬫壘鍒癭mqtt_gateway`濉叆浣犲湪`.env`鏂囦欢涓缃殑`PUBLIC_IP`+`:`+`MQTT_PORT`銆傜被浼艰繖鏍�
+```
+192.168.0.7:1883
+```
+鍦╜server`涓嬫壘鍒癭mqtt_signature_key`濉叆浣犲湪`.env`鏂囦欢涓缃殑`MQTT_SIGNATURE_KEY`銆�
+
+鍦╜server`涓嬫壘鍒癭udp_gateway`濉叆浣犲湪`.env`鏂囦欢涓缃殑`PUBLIC_IP`+`:`+`UDP_PORT`銆傜被浼艰繖鏍�
+```
+192.168.0.7:8884
+```
+
+涓婇潰鐨勯厤缃畬鎴愬悗锛屼綘鍙互浣跨敤curl鍛戒护锛岄獙璇佷綘鐨刼ta鍦板潃鏄惁浼氫笅鍙憁qtt閰嶇疆锛屾妸涓嬮潰鐨刞http://localhost:8002/xiaozhi/ota/`鏀规垚浣犵殑ota鍦板潃
+```
+curl 'http://localhost:8002/xiaozhi/ota/' \
+ -H 'Device-Id: 11:22:33:44:55:66' \
+ --data-raw $'{\n "application": {\n "version": "1.0.1",\n "elf_sha256": "1"\n },\n "board": {\n "mac": "11:22:33:44:55:66"\n }\n}'
+```
+
+濡傛灉杩斿洖鐨勫唴瀹瑰寘鍚玚mqtt`鐩稿叧鐨勯厤缃紝璇存槑閰嶇疆鎴愬姛銆傜被浼艰繖鏍�
+```
+{"server_time":{"timestamp":1758781561083,"timeZone":"GMT+08:00","timezone_offset":480},"activation":{"code":"527111","message":"http://xiaozhi.server.com\n527111","challenge":"11:22:33:44:55:66"},"firmware":{"version":"1.0.1","url":"http://xiaozhi.server.com:8002/xiaozhi/otaMag/download/NOT_ACTIVATED_FIRMWARE_THIS_IS_A_INVALID_URL"},"websocket":{"url":"ws://192.168.1.15:8000/xiaozhi/v1/"},"mqtt":{"endpoint":"192.168.1.15:1883","client_id":"GID_default@@@11_22_33_44_55_66@@@11_22_33_44_55_66","username":"eyJpcCI6IjE5Mi4xNjguMS4xNSJ9","password":"fjAYs49zTJecWqJ3jBt+kqxVn/x7vkXRAc85ak/va7Y=","publish_topic":"device-server","subscribe_topic":"devices/p2p/11_22_33_44_55_66"}}
+```
+
+鐢变簬MQTT淇℃伅鏄渶瑕侀潬OTA鍦板潃涓嬪彂鐨勶紝鍥犳鍙湁浣犱繚璇佽兘姝e父杩炴帴鏈嶅姟鍣ㄧ殑OTA鍦板潃锛岄噸鍚敜閱掑嵆鍙��
+
+鍞ら啋鍚庣暀鎰弇qtt-gateway鐨勬棩蹇楋紝纭鏄惁鏈夎繛鎺ユ垚鍔熺殑鏃ュ織銆�
+```
+pm2 logs xz-mqtt
+```
diff --git a/xiaozhi-esp32-server/docs/newsnow_plugin_config.md b/xiaozhi-esp32-server/docs/newsnow_plugin_config.md
new file mode 100644
index 0000000..8be887c
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/newsnow_plugin_config.md
@@ -0,0 +1,105 @@
+# get_news_from_newsnow 鎻掍欢鏂伴椈婧愰厤缃寚鍗�
+
+## 姒傝堪
+
+`get_news_from_newsnow` 鎻掍欢鐜板湪鏀寔閫氳繃Web绠$悊鐣岄潰鍔ㄦ�侀厤缃柊闂绘簮锛屼笉鍐嶉渶瑕佷慨鏀逛唬鐮併�傜敤鎴峰彲浠ュ湪鏅烘帶鍙颁腑涓烘瘡涓櫤鑳戒綋閰嶇疆涓嶅悓鐨勬柊闂绘簮銆�
+
+## 閰嶇疆鏂瑰紡
+
+### 1. 閫氳繃Web绠$悊鐣岄潰閰嶇疆锛堟帹鑽愶級
+
+1. 鐧诲綍鏅烘帶鍙�
+2. 杩涘叆"瑙掕壊閰嶇疆"椤甸潰
+3. 閫夋嫨瑕侀厤缃殑鏅鸿兘浣�
+4. 鐐瑰嚮"缂栬緫鍔熻兘"鎸夐挳
+5. 鍦ㄥ彸渚у弬鏁伴厤缃尯鍩熸壘鍒�"newsnow鏂伴椈鑱氬悎"鎻掍欢
+6. 鍦�"鏂伴椈婧愰厤缃�"瀛楁涓緭鍏ュ垎鍙峰垎闅旂殑涓枃鍚嶇О
+
+### 2. 閰嶇疆鏂囦欢鏂瑰紡
+
+鍦� `config.yaml` 涓厤缃細
+
+```yaml
+plugins:
+ get_news_from_newsnow:
+ url: "https://newsnow.busiyi.world/api/s?id="
+ news_sources: "婢庢箖鏂伴椈;鐧惧害鐑悳;璐㈣仈绀�;寰崥;鎶栭煶"
+```
+
+## 鏂伴椈婧愰厤缃牸寮�
+
+鏂伴椈婧愰厤缃娇鐢ㄥ垎鍙峰垎闅旂殑涓枃鍚嶇О锛屾牸寮忎负锛�
+
+```
+涓枃鍚嶇О1;涓枃鍚嶇О2;涓枃鍚嶇О3
+```
+
+### 閰嶇疆绀轰緥
+
+```
+婢庢箖鏂伴椈;鐧惧害鐑悳;璐㈣仈绀�;寰崥;鎶栭煶;鐭ヤ箮;36姘�
+```
+
+## 鏀寔鐨勬柊闂绘簮
+
+鎻掍欢鏀寔浠ヤ笅鏂伴椈婧愮殑涓枃鍚嶇О锛�
+
+- 婢庢箖鏂伴椈
+- 鐧惧害鐑悳
+- 璐㈣仈绀�
+- 寰崥
+- 鎶栭煶
+- 鐭ヤ箮
+- 36姘�
+- 鍗庡皵琛楄闂�
+- IT涔嬪
+- 浠婃棩澶存潯
+- 铏庢墤
+- 鍝斿摡鍝斿摡
+- 蹇墜
+- 闆悆
+- 鏍奸殕姹�
+- 娉曞竷璐㈢粡
+- 閲戝崄鏁版嵁
+- 鐗涘
+- 灏戞暟娲�
+- 绋�鍦熸帢閲�
+- 鍑ゅ嚢缃�
+- 铏儴钀�
+- 鑱斿悎鏃╂姤
+- 閰峰畨
+- 杩滄櫙璁哄潧
+- 鍙傝�冩秷鎭�
+- 鍗槦閫氳绀�
+- 鐧惧害璐村惂
+- 闈犺氨鏂伴椈
+- 浠ュ強鏇村...
+
+## 榛樿閰嶇疆
+
+濡傛灉鏈厤缃柊闂绘簮锛屾彃浠跺皢浣跨敤浠ヤ笅榛樿閰嶇疆锛�
+
+```
+婢庢箖鏂伴椈;鐧惧害鐑悳;璐㈣仈绀�
+```
+
+## 浣跨敤璇存槑
+
+1. **閰嶇疆鏂伴椈婧�**锛氬湪Web鐣岄潰鎴栭厤缃枃浠朵腑璁剧疆鏂伴椈婧愮殑涓枃鍚嶇О锛岀敤鍒嗗彿鍒嗛殧
+2. **璋冪敤鎻掍欢**锛氱敤鎴峰彲浠ヨ"鎾姤鏂伴椈"鎴�"鑾峰彇鏂伴椈"
+3. **鎸囧畾鏂伴椈婧�**锛氱敤鎴峰彲浠ヨ"鎾姤婢庢箖鏂伴椈"鎴�"鑾峰彇鐧惧害鐑悳"
+4. **鑾峰彇璇︽儏**锛氱敤鎴峰彲浠ヨ"璇︾粏浠嬬粛杩欐潯鏂伴椈"
+
+## 宸ヤ綔鍘熺悊
+
+1. 鎻掍欢鎺ュ彈涓枃鍚嶇О浣滀负鍙傛暟锛堝"婢庢箖鏂伴椈"锛�
+2. 鏍规嵁閰嶇疆鐨勬柊闂绘簮鍒楄〃锛屽皢涓枃鍚嶇О杞崲涓哄搴旂殑鑻辨枃ID锛堝"thepaper"锛�
+3. 浣跨敤鑻辨枃ID璋冪敤API鑾峰彇鏂伴椈鏁版嵁
+4. 杩斿洖鏂伴椈鍐呭缁欑敤鎴�
+
+## 娉ㄦ剰浜嬮」
+
+1. 閰嶇疆鐨勪腑鏂囧悕绉板繀椤讳笌 CHANNEL_MAP 涓畾涔夌殑鍚嶇О瀹屽叏涓�鑷�
+2. 閰嶇疆鏇存敼鍚庨渶瑕侀噸鍚湇鍔℃垨閲嶆柊鍔犺浇閰嶇疆
+3. 濡傛灉閰嶇疆鐨勬柊闂绘簮鏃犳晥锛屾彃浠朵細鑷姩浣跨敤榛樿鏂伴椈婧�
+4. 澶氫釜鏂伴椈婧愪箣闂翠娇鐢ㄨ嫳鏂囧垎鍙�(;)鍒嗛殧锛屼笉瑕佷娇鐢ㄤ腑鏂囧垎鍙�(锛�)
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/docs/ota-upgrade-guide.md b/xiaozhi-esp32-server/docs/ota-upgrade-guide.md
new file mode 100644
index 0000000..0df4a2f
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/ota-upgrade-guide.md
@@ -0,0 +1,142 @@
+# 鍗曟ā鍧楅儴缃插浐浠禣TA鑷姩鍗囩骇閰嶇疆鎸囧崡
+
+鏈暀绋嬪皢鎸囧浣犲浣曞湪**鍗曟ā鍧楅儴缃�**鍦烘櫙涓嬮厤缃浐浠禣TA鑷姩鍗囩骇鍔熻兘锛屽疄鐜拌澶囧浐浠剁殑鑷姩鏇存柊銆�
+
+濡傛灉浣犲凡缁忎娇鐢�**鍏ㄦā鍧楅儴缃�**锛岃蹇界暐鏈暀绋嬨��
+
+## 鍔熻兘浠嬬粛
+
+鍦ㄥ崟妯″潡閮ㄧ讲涓紝xiaozhi-server鍐呯疆浜哋TA鍥轰欢绠$悊鍔熻兘锛屽彲浠ヨ嚜鍔ㄦ娴嬭澶囩増鏈苟涓嬪彂鍗囩骇鍥轰欢銆傜郴缁熶細鏍规嵁璁惧鍨嬪彿鍜屽綋鍓嶇増鏈紝鑷姩鍖归厤骞舵帹閫佹渶鏂扮殑鍥轰欢鐗堟湰銆�
+
+## 鍓嶆彁鏉′欢
+
+- 浣犲凡缁忔垚鍔熻繘琛�**鍗曟ā鍧楅儴缃�**骞惰繍琛寈iaozhi-server
+- 璁惧鑳藉姝e父杩炴帴鍒版湇鍔″櫒
+
+## 绗竴姝� 鍑嗗鍥轰欢鏂囦欢
+
+### 1. 鍒涘缓鍥轰欢瀛樻斁鐩綍
+
+鍥轰欢鏂囦欢闇�瑕佹斁鍦╜data/bin/`鐩綍涓嬨�傚鏋滆鐩綍涓嶅瓨鍦紝璇锋墜鍔ㄥ垱寤猴細
+
+```bash
+mkdir -p data/bin
+```
+
+### 2. 鍥轰欢鏂囦欢鍛藉悕瑙勫垯
+
+鍥轰欢鏂囦欢蹇呴』閬靛惊浠ヤ笅鍛藉悕鏍煎紡锛�
+
+```
+{璁惧鍨嬪彿}_{鐗堟湰鍙穧.bin
+```
+
+**鍛藉悕瑙勫垯璇存槑锛�**
+- `璁惧鍨嬪彿`锛氳澶囩殑鍨嬪彿鍚嶇О锛屼緥濡� `lichuang-dev`銆乣bread-compact-wifi` 绛�
+- `鐗堟湰鍙穈锛氬浐浠剁増鏈彿锛屽繀椤讳互鏁板瓧寮�澶达紝鏀寔鏁板瓧銆佸瓧姣嶃�佺偣鍙枫�佷笅鍒掔嚎鍜岀煭妯嚎锛屼緥濡� `1.6.6`銆乣2.0.0` 绛�
+- 鏂囦欢鎵╁睍鍚嶅繀椤绘槸 `.bin`
+
+**鍛藉悕绀轰緥锛�**
+```
+bread-compact-wifi_1.6.6.bin
+lichuang-dev_2.0.0.bin
+```
+
+### 3. 鏀剧疆鍥轰欢鏂囦欢
+
+灏嗗噯澶囧ソ鐨勫浐浠舵枃浠讹紙.bin鏂囦欢锛夊鍒跺埌`data/bin/`鐩綍涓嬶細
+
+閲嶈鐨勪簨鎯呰涓夐亶锛氬崌绾х殑bin鏂囦欢鏄痐xiaozhi.bin`锛屼笉鏄叏閲忓浐浠舵枃浠禶merged-binary.bin`!
+
+閲嶈鐨勪簨鎯呰涓夐亶锛氬崌绾х殑bin鏂囦欢鏄痐xiaozhi.bin`锛屼笉鏄叏閲忓浐浠舵枃浠禶merged-binary.bin`!
+
+閲嶈鐨勪簨鎯呰涓夐亶锛氬崌绾х殑bin鏂囦欢鏄痐xiaozhi.bin`锛屼笉鏄叏閲忓浐浠舵枃浠禶merged-binary.bin`!
+
+```bash
+cp xiaozhi.bin data/bin/璁惧鍨嬪彿_鐗堟湰鍙�.bin
+```
+
+渚嬪锛�
+```bash
+cp xiaozhi.bin data/bin/bread-compact-wifi_1.6.6.bin
+```
+
+## 绗簩姝� 閰嶇疆鍏綉璁块棶鍦板潃锛堜粎鍏綉閮ㄧ讲闇�瑕侊級
+
+**娉ㄦ剰锛氭姝ラ浠呴�傜敤浜庡崟妯″潡鍏綉閮ㄧ讲鐨勫満鏅��**
+
+濡傛灉浣犵殑xiaozhi-server鏄叕缃戦儴缃诧紙浣跨敤鍏綉IP鎴栧煙鍚嶏級锛�**蹇呴』**閰嶇疆`server.vision_explain`鍙傛暟锛屽洜涓篛TA鍥轰欢涓嬭浇鍦板潃浼氫娇鐢ㄨ閰嶇疆鐨勫煙鍚嶅拰绔彛銆�
+
+濡傛灉浣犳槸灞�鍩熺綉閮ㄧ讲锛屽彲浠ヨ烦杩囨姝ラ銆�
+
+### 涓轰粈涔堣閰嶇疆杩欎釜鍙傛暟锛�
+
+鍦ㄥ崟妯″潡閮ㄧ讲涓紝绯荤粺鐢熸垚鍥轰欢涓嬭浇鍦板潃鏃讹紝浼氫娇鐢╜vision_explain`閰嶇疆鐨勫煙鍚嶅拰绔彛浣滀负鍩虹鍦板潃銆傚鏋滀笉閰嶇疆鎴栭厤缃敊璇紝璁惧灏嗘棤娉曡闂浐浠朵笅杞藉湴鍧�銆�
+
+### 閰嶇疆鏂规硶
+
+鎵撳紑`data/.config.yaml`鏂囦欢锛屾壘鍒癭server`閰嶇疆娈碉紝璁剧疆`vision_explain`鍙傛暟锛�
+
+```yaml
+server:
+ vision_explain: http://浣犵殑鍩熷悕鎴朓P:绔彛鍙�/mcp/vision/explain
+```
+
+**閰嶇疆绀轰緥锛�**
+
+灞�鍩熺綉閮ㄧ讲锛堥粯璁わ級锛�
+```yaml
+server:
+ vision_explain: http://192.168.1.100:8003/mcp/vision/explain
+```
+
+鍏綉鍩熷悕閮ㄧ讲锛�
+```yaml
+server:
+ vision_explain: http://yourdomain.com:8003/mcp/vision/explain
+```
+
+### 娉ㄦ剰浜嬮」
+
+- 鍩熷悕鎴朓P蹇呴』鏄澶囪兘澶熻闂殑鍦板潃
+- 濡傛灉浣跨敤Docker閮ㄧ讲锛屼笉鑳戒娇鐢―ocker鍐呴儴鍦板潃锛堝127.0.0.1鎴杔ocalhost锛�
+- 濡傛灉浣犱娇鐢ㄤ簡nginx鍙嶅悜浠g悊锛岃濉啓瀵瑰鐨勫湴鍧�鍜岀鍙e彿锛屼笉鏄湰椤圭洰杩愯鐨勭鍙e彿
+
+
+## 甯歌闂
+
+### 1. 璁惧鏀朵笉鍒板浐浠舵洿鏂�
+
+**鍙兘鍘熷洜鍜岃В鍐虫柟娉曪細**
+
+- 妫�鏌ュ浐浠舵枃浠跺懡鍚嶆槸鍚︾鍚堣鍒欙細`{鍨嬪彿}_{鐗堟湰鍙穧.bin`
+- 妫�鏌ュ浐浠舵枃浠舵槸鍚︽纭斁缃湪`data/bin/`鐩綍
+- 妫�鏌ヨ澶囧瀷鍙锋槸鍚︿笌鍥轰欢鏂囦欢鍚嶄腑鐨勫瀷鍙峰尮閰�
+- 妫�鏌ュ浐浠剁増鏈彿鏄惁楂樹簬璁惧褰撳墠鐗堟湰
+- 鏌ョ湅鏈嶅姟鍣ㄦ棩蹇楋紝纭OTA璇锋眰鏄惁姝e父澶勭悊
+
+### 2. 璁惧鎶ュ憡涓嬭浇鍦板潃鏃犳硶璁块棶
+
+**鍙兘鍘熷洜鍜岃В鍐虫柟娉曪細**
+
+- 妫�鏌server.vision_explain`閰嶇疆鐨勫煙鍚嶆垨IP鏄惁姝g‘
+- 纭绔彛鍙烽厤缃纭紙榛樿8003锛�
+- 濡傛灉鏄叕缃戦儴缃诧紝纭繚璁惧鑳藉璁块棶璇ュ叕缃戝湴鍧�
+- 濡傛灉鏄疍ocker閮ㄧ讲锛岀‘淇濅笉鏄娇鐢ㄤ簡鍐呴儴鍦板潃锛�127.0.0.1锛�
+- 妫�鏌ラ槻鐏鏄惁寮�鏀句簡瀵瑰簲绔彛
+- 濡傛灉浣犱娇鐢ㄤ簡nginx鍙嶅悜浠g悊锛岃濉啓瀵瑰鐨勫湴鍧�鍜岀鍙e彿锛屼笉鏄湰椤圭洰杩愯鐨勭鍙e彿
+
+### 3. 濡備綍纭璁惧褰撳墠鐗堟湰
+
+鏌ョ湅OTA璇锋眰鏃ュ織锛屾棩蹇椾腑浼氭樉绀鸿澶囦笂鎶ョ殑鐗堟湰鍙凤細
+
+```
+[ota_handler] - 璁惧 AA:BB:CC:DD:EE:FF 鍥轰欢宸叉槸鏈�鏂�: 1.6.6
+```
+
+### 4. 鍥轰欢鏂囦欢鏀剧疆鍚庢病鏈夌敓鏁�
+
+绯荤粺鏈�30绉掔殑缂撳瓨鏃堕棿锛堥粯璁わ級锛屽彲浠ワ細
+- 绛夊緟30绉掑悗鍐嶈璁惧鍙戣捣OTA璇锋眰
+- 閲嶅惎xiaozhi-server鏈嶅姟
+- 璋冩暣`firmware_cache_ttl`閰嶇疆涓烘洿鐭殑鏃堕棿
diff --git a/xiaozhi-esp32-server/docs/paddlespeech-deploy.md b/xiaozhi-esp32-server/docs/paddlespeech-deploy.md
new file mode 100644
index 0000000..7e094b0
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/paddlespeech-deploy.md
@@ -0,0 +1,109 @@
+# PaddleSpeechTTS闆嗘垚xiaozhi鏈嶅姟
+
+## 閲嶇偣璇存槑
+- 浼樼偣锛氭湰鍦扮绾块儴缃层�侀�熷害蹇�
+- 缂虹偣锛氭埅姝�2025骞�9鏈�25鏃ワ紝榛樿鐨勬ā鍨嬫槸涓枃妯″瀷锛屼笉鏀寔鑻辨枃杞闊炽�傚鏋滃惈鑻辨枃浼氬彂涓嶅嚭澹伴煶锛屽闇�鍚屾椂鏀寔涓嫳鏂囬渶瑕佽嚜宸辫缁冦��
+
+## 涓�銆佸熀纭�鐜瑕佹眰
+鎿嶄綔绯荤粺锛歐indows / Linux / WSL 2
+
+Python 鐗堟湰锛�3.9浠ヤ笂锛堣鏍规嵁Paddle瀹樻柟鏁欑▼璋冩暣锛�
+
+Paddle 鐗堟湰锛氬畼鏂规渶鏂扮増鏈� ```https://www.paddlepaddle.org.cn/install```
+
+渚濊禆绠$悊宸ュ叿锛歝onda 鎴� venv
+
+## 浜屻�佸惎鍔╬addlespeech鏈嶅姟
+### 1.浠巔addlespeech瀹樻柟浠撳簱鎷夊彇婧愮爜
+```bash
+git clone https://github.com/PaddlePaddle/PaddleSpeech.git
+```
+### 2.寤虹珛铏氭嫙鐜
+```bash
+
+conda create -n paddle_env python=3.10 -y
+conda activate paddle_env
+```
+### 3.瀹夎paddle
+鍥燙PU鏋舵瀯銆丟PU鏋舵瀯涓嶅悓锛岃鏍规嵁Paddle瀹樻柟鏀寔鐨刾ython鐗堟湰寤虹珛鐜
+```
+https://www.paddlepaddle.org.cn/install
+```
+
+### 4.杩涘叆paddlespeech鐩綍
+```bash
+cd PaddleSpeech
+```
+### 5.瀹夎paddlespeech
+```bash
+pip install pytest-runner -i https://pypi.tuna.tsinghua.edu.cn/simple
+
+#浠ヤ笅鍛戒护浣跨敤浠绘剰涓�涓�
+pip install paddlepaddle -i https://mirror.baidu.com/pypi/simple
+pip install paddlespeech -i https://pypi.tuna.tsinghua.edu.cn/simple
+```
+### 6.浣跨敤鍛戒护鑷姩涓嬭浇璇煶妯″瀷
+```bash
+paddlespeech tts --input "浣犲ソ锛岃繖鏄竴娆℃祴璇�"
+```
+姝ゆ楠や細鑷姩涓嬭浇妯″瀷缂撳瓨鑷虫湰鍦� .paddlespeech/models 鐩綍
+
+### 7.淇敼tts_online_application.yaml閰嶇疆
+鍙傝�冪洰褰� ```"PaddleSpeech\demos\streaming_tts_server\conf\tts_online_application.yaml"```
+閫夋嫨```tts_online_application.yaml```鏂囦欢鐢ㄧ紪杈戝櫒鎵撳紑锛岃缃甡``protocol```涓篳``websocket```
+
+### 8.鍚姩鏈嶅姟
+```yaml
+paddlespeech_server start --config_file ./demos/streaming_tts_server/conf/tts_online_application.yaml
+#瀹樻柟榛樿鍚姩鍛戒护锛�
+paddlespeech_server start --config_file ./conf/tts_online_application.yaml
+```
+璇锋牴鎹綘鐨刞``tts_online_application.yaml```鐨勫疄闄呯洰褰曟潵鍚姩鍛戒护锛岀湅鍒板涓嬫棩蹇楀嵆鍚姩鎴愬姛
+```
+Prefix dict has been built successfully.
+[2025-08-07 10:03:11,312] [ DEBUG] __init__.py:166 - Prefix dict has been built successfully.
+INFO: Started server process [2298]
+INFO: Waiting for application startup.
+INFO: Application startup complete.
+INFO: Uvicorn running on http://0.0.0.0:8092 (Press CTRL+C to quit)
+```
+
+## 涓夈�佷慨鏀瑰皬鏅虹殑閰嶇疆鏂囦欢
+### 1.```main/xiaozhi-server/core/providers/tts/paddle_speech.py```
+
+### 2.```main/xiaozhi-server/data/.config.yaml```
+浣跨敤鍗曟ā鍧楅儴缃�
+```yaml
+selected_module:
+ TTS: PaddleSpeechTTS
+TTS:
+ PaddleSpeechTTS:
+ type: paddle_speech
+ protocol: websocket
+ url: ws://127.0.0.1:8092/paddlespeech/tts/streaming # TTS 鏈嶅姟鐨� URL 鍦板潃锛屾寚鍚戞湰鍦版湇鍔″櫒 [websocket榛樿ws://127.0.0.1:8092/paddlespeech/tts/streaming]
+ spk_id: 0 # 鍙戦煶浜� ID锛�0 閫氬父琛ㄧず榛樿鐨勫彂闊充汉
+ sample_rate: 24000 # 閲囨牱鐜� [websocket榛樿24000锛宧ttp榛樿0 鑷姩閫夋嫨]
+ speed: 1.0 # 璇�燂紝1.0 琛ㄧず姝e父璇�燂紝>1 琛ㄧず鍔犲揩锛�<1 琛ㄧず鍑忔參
+ volume: 1.0 # 闊抽噺锛�1.0 琛ㄧず姝e父闊抽噺锛�>1 琛ㄧず澧炲ぇ锛�<1 琛ㄧず鍑忓皬
+ save_path: # 淇濆瓨璺緞
+```
+### 3.鍚姩xiaozhi鏈嶅姟
+```py
+python app.py
+```
+鍚姩`main/digital-human`涓嬬殑`python start.py`鍚庯紝鎵撳紑`http://127.0.0.1:8006/index.html`锛屾祴璇曡繛鎺ュ拰鍙戦�佹秷鎭椂paddlespeech绔槸鍚︽湁杈撳嚭鏃ュ織
+
+杈撳嚭鏃ュ織鍙傝�冿細
+```
+INFO: 127.0.0.1:44312 - "WebSocket /paddlespeech/tts/streaming" [accepted]
+INFO: connection open
+[2025-08-07 11:16:33,355] [ INFO] - sentence: 鍝堝搱锛屾�庝箞绐佺劧鎵炬垜鑱婂ぉ鍟︼紵
+[2025-08-07 11:16:33,356] [ INFO] - The durations of audio is: 2.4625 s
+[2025-08-07 11:16:33,356] [ INFO] - first response time: 0.1143045425415039 s
+[2025-08-07 11:16:33,356] [ INFO] - final response time: 0.4777836799621582 s
+[2025-08-07 11:16:33,356] [ INFO] - RTF: 0.19402382942625715
+[2025-08-07 11:16:33,356] [ INFO] - Other info: front time: 0.06514096260070801 s, first am infer time: 0.008037090301513672 s, first voc infer time: 0.04112648963928223 s,
+[2025-08-07 11:16:33,356] [ INFO] - Complete the synthesis of the audio streams
+INFO: connection closed
+
+```
diff --git a/xiaozhi-esp32-server/docs/performance_tester.md b/xiaozhi-esp32-server/docs/performance_tester.md
new file mode 100644
index 0000000..8598712
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/performance_tester.md
@@ -0,0 +1,27 @@
+# 璇煶璇嗗埆銆佸ぇ璇█妯″瀷銆侀潪娴佸紡璇煶鍚堟垚銆佹祦寮忚闊冲悎鎴愩�佽瑙夋ā鍨嬬殑鎬ц兘娴嬭瘯宸ュ叿浣跨敤鎸囧崡
+
+1.鍦╩ain/xiaozhi-server鐩綍涓嬪垱寤篸ata鐩綍
+2.鍦╠ata鐩綍涓嬪垱寤�.config.yaml鏂囦欢
+3.鍦�.data/config.yaml涓紝鍐欏叆浣犵殑璇煶璇嗗埆銆佸ぇ璇█妯″瀷銆佹祦寮忚闊冲悎鎴愩�佽瑙夋ā鍨嬬殑鍙傛暟
+渚嬪锛�
+```
+LLM:
+ ChatGLMLLM:
+ # 瀹氫箟LLM API绫诲瀷
+ type: openai
+ # glm-4-flash 鏄厤璐圭殑锛屼絾鏄繕鏄渶瑕佹敞鍐屽~鍐檃pi_key鐨�
+ # 鍙湪杩欓噷鎵惧埌浣犵殑api key https://bigmodel.cn/usercenter/proj-mgmt/apikeys
+ model_name: glm-4-flash
+ url: https://open.bigmodel.cn/api/paas/v4/
+ api_key: 浣犵殑chat-glm web key
+
+TTS:
+
+VLLM:
+
+ASR:
+```
+4.鍦╩ain/xiaozhi-server鐩綍涓嬭繍琛宲erformance_tester.py:
+```
+python performance_tester.py
+```
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/docs/powermem-integration.md b/xiaozhi-esp32-server/docs/powermem-integration.md
new file mode 100644
index 0000000..b9a77ed
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/powermem-integration.md
@@ -0,0 +1,345 @@
+# PowerMem 璁板繂缁勪欢闆嗘垚鎸囧崡
+
+## 绠�浠�
+
+[PowerMem](https://www.powermem.ai/) 鏄敱 OceanBase 寮�婧愮殑 Agent 璁板繂缁勪欢锛岄�氳繃鏈湴 LLM 杩涜璁板繂鎬荤粨鍜屾櫤鑳芥绱紝涓� AI 浠g悊鎻愪緵楂樻晥鐨勮蹇嗙鐞嗗姛鑳姐��
+
+璐圭敤璇存槑锛歅owerMem 鏈韩寮�婧愬厤璐癸紝瀹為檯璐圭敤鍙栧喅浜庢偍閫夋嫨鐨� LLM 鍜屾暟鎹簱锛�
+- 浣跨敤 SQLite + 鍏嶈垂 LLM锛堝鏅鸿氨 glm-4-flash锛�= **瀹屽叏鍏嶈垂**
+- 浣跨敤浜戠 LLM 鎴栦簯绔暟鎹簱 = 鎸夊搴旀湇鍔℃敹璐�
+
+> 馃挕 **鏈�浣虫�ц兘鎻愮ず**锛歅owerMem 閰嶅悎 OceanBase 浣跨敤鍙疄鐜版渶澶ф�ц兘閲婃斁锛孲QLite 浠呭缓璁湪璧勬簮涓嶈冻鐨勬儏鍐典笅浣跨敤銆�
+
+- **GitHub**: https://github.com/oceanbase/powermem
+- **瀹樼綉**: https://www.powermem.ai/
+- **浣跨敤绀轰緥**: https://github.com/oceanbase/powermem/tree/main/examples
+
+## 鍔熻兘鐗规��
+
+- **鏈湴鎬荤粨**锛氶�氳繃 LLM 鍦ㄦ湰鍦拌繘琛岃蹇嗘�荤粨鍜屾彁鍙�
+- **鐢ㄦ埛鐢诲儚**锛氶�氳繃 `UserMemory` 鑷姩鎻愬彇鐢ㄦ埛淇℃伅锛堝鍚嶃�佽亴涓氥�佸叴瓒g瓑锛夛紝鎸佺画鏇存柊鐢ㄦ埛鐢诲儚
+- **鏅鸿兘閬楀繕**锛氬熀浜庤壘瀹炬旦鏂仐蹇樻洸绾匡紝鑷姩"閬楀繕"杩囨椂鍣0淇℃伅
+- **澶氱瀛樺偍鍚庣**锛氭敮鎸� OceanBase锛堟帹鑽愶紝鏈�浣虫�ц兘锛夈�丼eekDB锛堟帹鑽愶紝AI搴旂敤瀛樺偍涓�浣擄級銆丳ostgreSQL銆丼QLite锛堣交閲忓閫夛級
+- **澶氱 LLM 鏀寔**锛氶�氫箟鍗冮棶銆佹櫤璋憋紙glm-4-flash 鍏嶈垂锛夈�丱penAI 绛�
+- **鏅鸿兘妫�绱�**锛氬熀浜庡悜閲忔悳绱㈢殑璇箟妫�绱㈣兘鍔�
+- **绉佹湁閮ㄧ讲**锛氬畬鍏ㄦ敮鎸佹湰鍦扮鏈夊寲閮ㄧ讲
+- **寮傛鎿嶄綔**锛氶珮鏁堢殑寮傛璁板繂绠$悊
+
+## 瀹夎
+
+PowerMem 宸叉坊鍔犲埌椤圭洰渚濊禆涓紝濡傛灉闇�瑕佹墜鍔ㄥ畨瑁咃細
+
+```bash
+pip install powermem
+```
+
+## 閰嶇疆璇存槑
+
+### 鍩虹閰嶇疆
+
+鍦� `config.yaml` 涓厤缃� PowerMem锛�
+
+```yaml
+selected_module:
+ Memory: powermem
+
+Memory:
+ powermem:
+ type: powermem
+ # 鏄惁鍚敤鐢ㄦ埛鐢诲儚鍔熻兘
+ # 鐢ㄦ埛鐢诲儚鏀寔: oceanbase銆乻eekdb銆乻qlite (powermem 0.3.0+)
+ enable_user_profile: true
+
+ # ========== LLM 閰嶇疆 ==========
+ llm:
+ provider: openai # 鍙��: qwen, openai, zhipu 绛�
+ config:
+ api_key: 浣犵殑LLM API瀵嗛挜
+ model: qwen-plus
+ # openai_base_url: https://api.openai.com/v1 # 鍙�夛紝鑷畾涔夋湇鍔″湴鍧�
+
+ # ========== Embedding 閰嶇疆 ==========
+ embedder:
+ provider: openai # 鍙��: qwen, openai 绛�
+ config:
+ api_key: 浣犵殑宓屽叆妯″瀷API瀵嗛挜
+ model: text-embedding-v4
+ openai_base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
+ # embedding_dims: 1024 # 鍚戦噺缁村害锛岄潪1536鏃堕渶閰嶇疆
+
+ # ========== Database 閰嶇疆 ==========
+ vector_store:
+ provider: sqlite # 鍙��: oceanbase(鎺ㄨ崘), seekdb(鎺ㄨ崘), postgres, sqlite(杞婚噺)
+ config: {} # SQLite 鏃犻渶棰濆閰嶇疆
+```
+
+### 閰嶇疆鍙傛暟璇﹁В
+
+#### LLM 閰嶇疆
+
+| 鍙傛暟 | 璇存槑 | 鍙�夊�� |
+|------|------|--------|
+| `llm.provider` | LLM 鎻愪緵鍟� | `qwen`, `openai`, `zhipu` 绛� |
+| `llm.config.api_key` | API 瀵嗛挜 | - |
+| `llm.config.model` | 妯″瀷鍚嶇О | 鏍规嵁鎻愪緵鍟嗛�夋嫨 |
+| `llm.config.openai_base_url` | 鑷畾涔夋湇鍔″湴鍧�锛堝彲閫夛級 | - |
+
+#### Embedding 閰嶇疆
+
+| 鍙傛暟 | 璇存槑 | 鍙�夊�� |
+|------|------|--------|
+| `embedder.provider` | 宓屽叆妯″瀷鎻愪緵鍟� | `qwen`, `openai` 绛� |
+| `embedder.config.api_key` | API 瀵嗛挜 | - |
+| `embedder.config.model` | 妯″瀷鍚嶇О | 鏍规嵁鎻愪緵鍟嗛�夋嫨 |
+| `embedder.config.openai_base_url` | 鑷畾涔夋湇鍔″湴鍧�锛堝彲閫夛級 | - |
+
+#### Database 閰嶇疆
+
+| 鍙傛暟 | 璇存槑 | 鍙�夊�� |
+|------|------|--------|
+| `vector_store.provider` | 瀛樺偍鍚庣绫诲瀷 | `oceanbase`(鎺ㄨ崘), `seekdb`(鎺ㄨ崘), `postgres`, `sqlite`(杞婚噺) |
+| `vector_store.config` | 鏁版嵁搴撹繛鎺ラ厤缃� | 鏍规嵁 provider 璁剧疆 |
+
+### 璁板繂妯″紡璇存槑
+
+PowerMem 鏀寔涓ょ璁板繂妯″紡锛�
+
+| 妯″紡 | 閰嶇疆 | 鍔熻兘 | 瀛樺偍瑕佹眰 |
+|------|------|------|----------|
+| **鏅�氳蹇�** | `enable_user_profile: false` | 瀵硅瘽璁板繂瀛樺偍涓庢绱� | 鏀寔鎵�鏈夋暟鎹簱 |
+| **鐢ㄦ埛鐢诲儚** | `enable_user_profile: true` | 璁板繂 + 鑷姩鎻愬彇鐢ㄦ埛鐢诲儚 | oceanbase銆乻eekdb銆乻qlite |
+
+> 馃搶 **鐗堟湰璇存槑**锛歅owerMem 0.3.0+ 鐗堟湰锛岀敤鎴风敾鍍忓姛鑳芥敮鎸� OceanBase銆丼eekDB銆丼QLite 涓夌瀛樺偍鍚庣銆�
+
+### 浣跨敤閫氫箟鍗冮棶锛堟帹鑽愶級
+
+1. 璁块棶 [闃块噷浜戠櫨鐐煎钩鍙癩(https://bailian.console.aliyun.com/) 娉ㄥ唽璐﹀彿
+2. 鍦� [API Key 绠$悊](https://bailian.console.aliyun.com/?apiKey=1#/api-key) 椤甸潰鑾峰彇 API 瀵嗛挜
+3. 閰嶇疆濡備笅锛�
+
+```yaml
+Memory:
+ powermem:
+ type: powermem
+ enable_user_profile: true
+ llm:
+ provider: qwen
+ config:
+ api_key: sk-xxxxxxxxxxxxxxxx
+ model: qwen-plus
+ embedder:
+ provider: openai
+ config:
+ api_key: sk-xxxxxxxxxxxxxxxx
+ model: text-embedding-v4
+ openai_base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
+ vector_store:
+ provider: sqlite
+ config: {}
+```
+
+### 浣跨敤鏅鸿氨鍏嶈垂 LLM锛堝畬鍏ㄥ厤璐规柟妗堬級
+
+鏅鸿氨鎻愪緵鍏嶈垂鐨� glm-4-flash 妯″瀷锛岄厤鍚� SQLite 鍙疄鐜板畬鍏ㄥ厤璐逛娇鐢細
+
+1. 璁块棶 [鏅鸿氨AI寮�鏀惧钩鍙癩(https://bigmodel.cn/) 娉ㄥ唽璐﹀彿
+2. 鍦� [API Keys](https://bigmodel.cn/usercenter/proj-mgmt/apikeys) 椤甸潰鑾峰彇 API 瀵嗛挜
+3. 閰嶇疆濡備笅锛�
+
+```yaml
+Memory:
+ powermem:
+ type: powermem
+ enable_user_profile: true
+ llm:
+ provider: openai # 浣跨敤 openai 鍏煎妯″紡
+ config:
+ api_key: xxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxx
+ model: glm-4-flash
+ openai_base_url: https://open.bigmodel.cn/api/paas/v4/
+ embedder:
+ provider: openai
+ config:
+ api_key: xxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxx
+ model: embedding-3
+ openai_base_url: https://open.bigmodel.cn/api/paas/v4/
+ vector_store:
+ provider: sqlite
+ config: {}
+```
+
+### 浣跨敤 OpenAI
+
+```yaml
+Memory:
+ powermem:
+ type: powermem
+ enable_user_profile: true
+ llm:
+ provider: openai
+ config:
+ api_key: sk-xxxxxxxxxxxxxxxx
+ model: gpt-4o-mini
+ openai_base_url: https://api.openai.com/v1
+ embedder:
+ provider: openai
+ config:
+ api_key: sk-xxxxxxxxxxxxxxxx
+ model: text-embedding-3-small
+ openai_base_url: https://api.openai.com/v1
+ vector_store:
+ provider: sqlite
+ config: {}
+```
+
+### 浣跨敤 OceanBase锛堟渶浣虫�ц兘鏂规锛�
+
+OceanBase 鏄� PowerMem 鐨勬渶浣虫惌妗o紝鍙疄鐜版渶澶ф�ц兘閲婃斁锛�
+
+1. 閮ㄧ讲 OceanBase 鏁版嵁搴擄紙鏀寔寮�婧愭湰鍦伴儴缃叉垨浣跨敤浜戞湇鍔★級
+ - 寮�婧愰儴缃诧細https://github.com/oceanbase/oceanbase
+ - 浜戞湇鍔★細https://www.oceanbase.com/
+2. 閰嶇疆濡備笅锛�
+
+```yaml
+Memory:
+ powermem:
+ type: powermem
+ enable_user_profile: true
+ llm:
+ provider: qwen
+ config:
+ api_key: sk-xxxxxxxxxxxxxxxx
+ model: qwen-plus
+ embedder:
+ provider: openai
+ config:
+ api_key: sk-xxxxxxxxxxxxxxxx
+ model: text-embedding-v4
+ openai_base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
+ vector_store:
+ provider: oceanbase
+ config:
+ host: 127.0.0.1
+ port: 2881
+ user: root@test
+ password: your_password
+ db_name: powermem
+ collection_name: memories # 榛樿鍊�
+ embedding_model_dims: 1536 # 宓屽叆鍚戦噺缁村害锛屽繀闇�鍙傛暟
+```
+
+## 璁惧璁板繂闅旂
+
+PowerMem 浼氳嚜鍔ㄤ娇鐢ㄨ澶� ID锛坄device_id`锛変綔涓� `user_id` 杩涜璁板繂闅旂銆傝繖鎰忓懗鐫�锛�
+
+- 姣忎釜璁惧鎷ユ湁鐙珛鐨勮蹇嗙┖闂�
+- 涓嶅悓璁惧涔嬮棿鐨勮蹇嗗畬鍏ㄩ殧绂�
+- 鍚屼竴璁惧鐨勫娆″璇濆彲浠ュ叡浜蹇嗕笂涓嬫枃
+
+## 鐢ㄦ埛鐢诲儚锛圲serMemory锛�
+
+PowerMem 鎻愪緵 `UserMemory` 绫伙紝鍙嚜鍔ㄤ粠瀵硅瘽涓彁鍙栫敤鎴风敾鍍忎俊鎭��
+
+> 馃搶 **鐗堟湰璇存槑**锛歅owerMem 0.3.0+ 鐗堟湰锛岀敤鎴风敾鍍忓姛鑳芥敮鎸� OceanBase銆丼eekDB銆丼QLite 涓夌瀛樺偍鍚庣銆�
+
+### 鍚敤鐢ㄦ埛鐢诲儚
+
+鍦ㄩ厤缃腑璁剧疆 `enable_user_profile: true` 鍗冲彲鍚敤锛�
+
+```yaml
+Memory:
+ powermem:
+ type: powermem
+ enable_user_profile: true # 鍚敤鐢ㄦ埛鐢诲儚
+ llm:
+ provider: qwen
+ config:
+ api_key: sk-xxxxxxxxxxxxxxxx
+ model: qwen-plus
+ embedder:
+ provider: openai
+ config:
+ api_key: sk-xxxxxxxxxxxxxxxx
+ model: text-embedding-v4
+ openai_base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
+ vector_store:
+ provider: sqlite # 鐢ㄦ埛鐢诲儚鏀寔: oceanbase銆乻eekdb銆乻qlite
+ config: {}
+```
+
+### 鐢ㄦ埛鐢诲儚鑳藉姏
+
+| 鑳藉姏 | 璇存槑 |
+|------|------|
+| **淇℃伅鎻愬彇** | 鑷姩浠庡璇濅腑鎻愬彇濮撳悕銆佸勾榫勩�佽亴涓氥�佸叴瓒g瓑 |
+| **鎸佺画鏇存柊** | 闅忕潃瀵硅瘽杩涜锛屼笉鏂畬鍠勭敤鎴风敾鍍� |
+| **鐢诲儚妫�绱�** | 灏嗙敤鎴风敾鍍忎笌璁板繂鎼滅储缁撳悎锛屾彁鍗囨绱㈢浉鍏虫�� |
+| **鏅鸿兘閬楀繕** | 鍩轰簬鑹惧娴╂柉閬楀繕鏇茬嚎锛屾贰鍖栬繃鏃朵俊鎭� |
+
+### 宸ヤ綔鍘熺悊
+
+鍚敤鐢ㄦ埛鐢诲儚鍚庯紝灏忔櫤鍦ㄦ煡璇㈣蹇嗘椂浼氳嚜鍔ㄨ繑鍥烇細
+1. **鐢ㄦ埛鐢诲儚**锛氱敤鎴风殑鍩烘湰淇℃伅銆佸叴瓒g埍濂界瓑
+2. **鐩稿叧璁板繂**锛氫笌褰撳墠瀵硅瘽鐩稿叧鐨勫巻鍙茶蹇�
+
+> 鉁� **鐗堟湰璇存槑**锛歅owerMem 0.3.0+ 鐗堟湰锛岀敤鎴风敾鍍忓姛鑳芥敮鎸� OceanBase銆丼eekDB銆丼QLite 涓夌瀛樺偍鍚庣銆�
+
+## 涓庡叾浠栬蹇嗙粍浠剁殑瀵规瘮
+
+| 鐗规�� | PowerMem | mem0ai | mem_local_short |
+|------|----------|--------|-----------------|
+| 宸ヤ綔鏂瑰紡 | 鏈湴鎬荤粨 | 浜戠鎺ュ彛 | 鏈湴鎬荤粨 |
+| 瀛樺偍浣嶇疆 | 鏈湴/浜戠DB | 浜戠 | 鏈湴YAML |
+| 璐圭敤 | 鍙栧喅浜嶭LM鍜孌B | 1000娆�/鏈堝厤璐� | 瀹屽叏鍏嶈垂 |
+| 鏅鸿兘妫�绱� | 鉁� 鍚戦噺鎼滅储 | 鉁� 鍚戦噺鎼滅储 | 鉂� 鍏ㄩ噺杩斿洖 |
+| 鐢ㄦ埛鐢诲儚 | 鉁� UserMemory | 鉂� | 鉂� |
+| 鏅鸿兘閬楀繕 | 鉁� 閬楀繕鏇茬嚎 | 鉂� | 鉂� |
+| 绉佹湁閮ㄧ讲 | 鉁� 鏀寔 | 鉂� 浠呬簯绔� | 鉁� 鏀寔 |
+| 鏁版嵁搴撴敮鎸� | OceanBase(鎺ㄨ崘)/SeekDB/PostgreSQL/SQLite | - | YAML 鏂囦欢 |
+
+## 甯歌闂
+
+### 1. API 瀵嗛挜閿欒
+
+濡傛灉鍑虹幇 `API key is required` 閿欒锛岃妫�鏌ワ細
+- `llm_api_key` 鍜� `embedding_api_key` 鏄惁姝g‘濉啓
+- API 瀵嗛挜鏄惁鏈夋晥
+
+### 2. 妯″瀷涓嶅瓨鍦�
+
+濡傛灉鍑虹幇妯″瀷涓嶅瓨鍦ㄧ殑閿欒锛岃纭锛�
+- `llm_model` 鍜� `embedding_model` 鍚嶇О鏄惁姝g‘
+- 瀵瑰簲鐨勬ā鍨嬫湇鍔℃槸鍚﹀凡寮�閫�
+
+### 3. 杩炴帴瓒呮椂
+
+濡傛灉鍑虹幇杩炴帴瓒呮椂锛屽彲浠ュ皾璇曪細
+- 妫�鏌ョ綉缁滆繛鎺�
+- 濡傛灉浣跨敤浠g悊锛岄厤缃� `llm_base_url` 鍜� `embedding_base_url`
+
+## 娴嬭瘯楠岃瘉
+
+鍙互鍦ㄨ櫄鎷熺幆澧冧腑娴嬭瘯 PowerMem 鏄惁姝e父宸ヤ綔锛�
+
+```bash
+# 婵�娲昏櫄鎷熺幆澧�
+source .venv/bin/activate
+
+# 娴嬭瘯 PowerMem 瀵煎叆
+python -c "from powermem import AsyncMemory; print('PowerMem 瀵煎叆鎴愬姛')"
+
+# 娴嬭瘯 UserMemory 瀵煎叆锛堢敤鎴风敾鍍忓姛鑳斤級
+python -c "from powermem import UserMemory; print('UserMemory 瀵煎叆鎴愬姛')"
+```
+
+## 鏇村璧勬簮
+
+- [PowerMem 瀹樻柟鏂囨。](https://www.powermem.ai/)
+- [PowerMem GitHub 浠撳簱](https://github.com/oceanbase/powermem)
+- [PowerMem 浣跨敤绀轰緥](https://github.com/oceanbase/powermem/tree/main/examples)
+- [OceanBase 瀹樼綉](https://www.oceanbase.com/)
+- [OceanBase GitHub](https://github.com/oceanbase/oceanbase)
+- [SeekDB GitHub](https://github.com/oceanbase/seekdb)锛圓I鍘熺敓鎼滅储鏁版嵁搴擄級
+- [闃块噷浜戠櫨鐐煎钩鍙癩(https://bailian.console.aliyun.com/)
+
diff --git a/xiaozhi-esp32-server/docs/ragflow-integration.md b/xiaozhi-esp32-server/docs/ragflow-integration.md
new file mode 100644
index 0000000..7d2b4ea
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/ragflow-integration.md
@@ -0,0 +1,269 @@
+# ragflow 闆嗘垚鎸囧崡
+
+鏈暀绋嬩富瑕佹槸鏄袱閮ㄥ垎
+
+- 涓�銆佸浣曢儴缃瞨agflow
+- 浜屻�佸浣曞湪鏅烘帶鍙伴厤缃畆agflow鎺ュ彛
+
+濡傛灉鎮ㄥragflow寰堢啛鎮夛紝涓斿凡缁忛儴缃蹭簡ragflow锛屽彲鐩存帴璺宠繃绗竴閮ㄥ垎锛岀洿鎺ヨ繘鍏ョ浜岄儴鍒嗐�備絾鏄鏋滀綘甯屾湜鏈変汉鎸囧浣犻儴缃瞨agflow锛岃瀹冭兘澶熷拰`xiaozhi-esp32-server`鍏卞悓浣跨敤`mysql`銆乣redis`鍩虹鏈嶅姟锛屼互鍑忓皯璧勬簮鎴愭湰锛屼綘闇�瑕佷粠绗竴閮ㄥ垎寮�濮嬨��
+
+# 绗竴閮ㄥ垎 濡備綍閮ㄧ讲ragflow
+## 绗竴姝ワ紝 纭mysql銆乺edis鏄惁鍙敤
+
+ragflow闇�瑕佷緷璧朻mysql`鏁版嵁搴撱�傚鏋滀綘涔嬪墠宸茬粡閮ㄧ讲`鏅烘帶鍙癭锛岃鏄庝綘宸茬粡瀹夎浜哷mysql`銆備綘鍙互鍏辩敤瀹冦��
+
+浣犲彲浠ヤ綘璇曚竴涓嬪湪瀹夸富鏈轰娇鐢╜telnet`鍛戒护锛岀湅鐪嬭兘涓嶈兘姝e父璁块棶`mysql`鐨刞3306`绔彛銆�
+``` shell
+telnet 127.0.0.1 3306
+
+telnet 127.0.0.1 6379
+```
+濡傛灉鑳借闂埌`3306`绔彛鍜宍6379`绔彛锛岃蹇界暐浠ヤ笅鐨勫唴瀹癸紝鐩存帴杩涘叆绗簩姝ャ��
+
+濡傛灉涓嶈兘璁块棶锛屼綘闇�瑕佸洖蹇嗕竴涓嬶紝浣犵殑`mysql`鏄�庝箞瀹夎鐨勩��
+
+濡傛灉浣犵殑mysql鏄�氳繃鑷繁浣跨敤瀹夎鍖呭畨瑁呯殑锛岃鏄庝綘鐨刞mysql`鍋氫簡缃戠粶闅旂銆備綘鍙兘鍏堣В鍐宠闂甡mysql`鐨刞3306`绔彛杩欎釜闂銆�
+
+濡傛灉浣燻mysql`鏄�氳繃鏈」鐩殑`docker-compose_all.yml`瀹夎鐨勩�備綘闇�瑕佹壘涓�涓嬩綘褰撴椂鍒涘缓鏁版嵁搴撶殑`docker-compose_all.yml`鏂囦欢锛屼慨鏀逛互涓嬬殑鍐呭
+
+淇敼鍓�
+``` yaml
+ xiaozhi-esp32-server-db:
+ ...
+ networks:
+ - default
+ expose:
+ - "3306:3306"
+ xiaozhi-esp32-server-redis:
+ ...
+ expose:
+ - 6379
+```
+
+淇敼鍚�
+``` yaml
+ xiaozhi-esp32-server-db:
+ ...
+ networks:
+ - default
+ ports:
+ - "3306:3306"
+ xiaozhi-esp32-server-redis:
+ ...
+ ports:
+ - "6379:6379"
+```
+
+娉ㄦ剰鏄皢`xiaozhi-esp32-server-db`鍜宍xiaozhi-esp32-server-redis`涓嬮潰鐨刞expose`鏀规垚`ports`銆傛敼瀹屽悗锛岄渶瑕侀噸鏂板惎鍔ㄣ�備互涓嬫槸閲嶅惎mysql鐨勫懡浠わ細
+
+``` shell
+# 杩涘叆浣燿ocker-compose_all.yml鎵�鍦ㄧ殑鏂囦欢澶癸紝渚嬪鎴戠殑鏄痻iaozhi-server
+cd xiaozhi-server
+docker compose -f docker-compose_all.yml down
+docker compose -f docker-compose.yml up -d
+```
+
+鍚姩瀹屽悗锛屽湪瀹夸富鏈哄啀浣跨敤`telnet`鍛戒护锛岀湅鐪嬭兘涓嶈兘姝e父璁块棶`mysql`鐨刞3306`绔彛銆�
+``` shell
+telnet 127.0.0.1 3306
+
+telnet 127.0.0.1 6379
+```
+姝e父鏉ヨ杩欐牱灏卞彲浠ヨ闂殑浜嗐��
+
+## 绗簩姝ワ紝 鍒涘缓鏁版嵁搴撳拰琛�
+濡傛灉浣犵殑瀹夸富鏈猴紝鑳芥甯歌闂甿ysql鏁版嵁搴擄紝閭e氨鍦╩ysql涓婂垱寤轰竴涓悕瀛椾负`rag_flow`鐨勬暟鎹簱鍜宍rag_flow`鐢ㄦ埛锛屽瘑鐮佷负`infini_rag_flow`銆�
+
+``` sql
+-- 鍒涘缓鏁版嵁搴�
+CREATE DATABASE IF NOT EXISTS rag_flow CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- 鍒涘缓鐢ㄦ埛骞舵巿鏉�
+CREATE USER IF NOT EXISTS 'rag_flow'@'%' IDENTIFIED BY 'infini_rag_flow';
+GRANT ALL PRIVILEGES ON rag_flow.* TO 'rag_flow'@'%';
+
+-- 鍒锋柊鏉冮檺
+FLUSH PRIVILEGES;
+```
+
+## 绗笁姝ワ紝 涓嬭浇ragflow椤圭洰
+
+浣犻渶瑕佸湪浣犵數鑴戞壘涓�涓枃浠跺す锛岀敤鏉ュ瓨鏀緍agflow椤圭洰銆備緥濡傛垜鍦╜/home/system/xiaozhi`鏂囦欢澶广��
+
+浣犲彲浠ヤ娇鐢╜git`鍛戒护锛屽皢ragflow椤圭洰涓嬭浇鍒拌繖涓枃浠跺す锛屾湰鏁欑▼浣跨敤鐨勬槸`v0.22.0`鐗堟湰杩涜瀹夎閮ㄧ讲銆�
+```
+git clone https://ghfast.top/https://github.com/infiniflow/ragflow.git
+cd ragflow
+git checkout v0.22.0
+```
+涓嬭浇瀹屽悗锛岃繘鍏docker`鏂囦欢澶广��
+``` shell
+cd docker
+```
+淇敼`ragflow/docker`鏂囦欢澶逛笅鐨刞docker-compose.yml`鏂囦欢锛屽皢`ragflow-cpu`鍜宍ragflow-gpu`鏈嶅姟鐨刞depends_on`閰嶇疆鍘绘帀锛岀敤浜庤В闄ragflow-cpu`鏈嶅姟瀵筦mysql`鐨勪緷璧栥��
+
+杩欐槸淇敼鍓嶏細
+``` yaml
+ ragflow-cpu:
+ depends_on:
+ mysql:
+ condition: service_healthy
+ profiles:
+ - cpu
+ ...
+ ragflow-gpu:
+ depends_on:
+ mysql:
+ condition: service_healthy
+ profiles:
+ - gpu
+```
+杩欐槸淇敼鍚庯細
+``` yaml
+ ragflow-cpu:
+ profiles:
+ - cpu
+ ...
+ ragflow-gpu:
+ profiles:
+ - gpu
+```
+
+鎺ョ潃锛屼慨鏀筦ragflow/docker`鏂囦欢澶逛笅鐨刞docker-compose-base.yml`鏂囦欢锛屽幓鎺塦mysql`鍜宍redis`鐨勯厤缃��
+
+渚嬪锛屽垹闄ゅ墠锛�
+``` yaml
+services:
+ minio:
+ image: quay.io/minio/minio:RELEASE.2025-06-13T11-33-47Z
+ ...
+ mysql:
+ image: mysql:8.0
+ ...
+ redis:
+ image: redis:6.2-alpine
+ ...
+```
+
+鍒犻櫎鍚�
+``` yaml
+services:
+ minio:
+ image: quay.io/minio/minio:RELEASE.2025-06-13T11-33-47Z
+ ...
+```
+## 绗洓姝ワ紝淇敼鐜鍙橀噺閰嶇疆
+
+缂栬緫`ragflow/docker`鏂囦欢澶逛笅鐨刞.env`鏂囦欢,鎵惧埌浠ヤ笅閰嶇疆锛岄�愪釜鎼滅储锛岄�愪釜淇敼锛侀�愪釜鎼滅储锛岄�愪釜淇敼锛�
+
+涓嬮潰瀵逛簬`.env`鏂囦欢鐨勪慨鏀癸紝60%鐨勪汉浼氬拷鐣MYSQL_USER`閰嶇疆瀵艰嚧ragflow鍚姩涓嶆垚鍔燂紝鍥犳锛岄渶瑕佸己璋冧笁娆★細
+
+寮鸿皟绗竴娆★細濡傛灉浣犵殑`.env`鏂囦欢濡傛灉娌℃湁`MYSQL_USER`閰嶇疆锛岃鍦ㄩ厤缃枃浠跺鍔犺繖椤癸紒
+
+寮鸿皟绗簩娆★細濡傛灉浣犵殑`.env`鏂囦欢濡傛灉娌℃湁`MYSQL_USER`閰嶇疆锛岃鍦ㄩ厤缃枃浠跺鍔犺繖椤癸紒
+
+寮鸿皟绗笁娆★細濡傛灉浣犵殑`.env`鏂囦欢濡傛灉娌℃湁`MYSQL_USER`閰嶇疆锛岃鍦ㄩ厤缃枃浠跺鍔犺繖椤癸紒
+
+``` env
+# 绔彛璁剧疆
+SVR_WEB_HTTP_PORT=8008 # HTTP绔彛
+SVR_WEB_HTTPS_PORT=8009 # HTTPS绔彛
+# MySQL閰嶇疆 - 淇敼涓烘偍鏈湴MySQL鐨勪俊鎭�
+MYSQL_HOST=host.docker.internal # 浣跨敤host.docker.internal璁╁鍣ㄨ闂富鏈烘湇鍔�
+MYSQL_PORT=3306 # 鏈湴MySQL绔彛
+MYSQL_USER=rag_flow # 涓婇潰鍒涘缓鐨勭敤鎴峰悕锛屽鏋滄病鏈夎繖椤瑰氨澧炲姞杩欎竴椤�
+MYSQL_PASSWORD=infini_rag_flow # 涓婇潰璁剧疆鐨勫瘑鐮�
+MYSQL_DBNAME=rag_flow # 鏁版嵁搴撳悕绉�
+
+# Redis閰嶇疆 - 淇敼涓烘偍鏈湴Redis鐨勪俊鎭�
+REDIS_HOST=host.docker.internal # 浣跨敤host.docker.internal璁╁鍣ㄨ闂富鏈烘湇鍔�
+REDIS_PORT=6379 # 鏈湴Redis绔彛
+REDIS_PASSWORD= # 濡傛灉浣犵殑Redis娌℃湁璁剧疆瀵嗙爜锛屽氨鎸夎繖鏍峰瓙濉啓锛屽惁鍒欏~鍐欏瘑鐮�
+```
+
+娉ㄦ剰锛屽鏋滀綘鐨凴edis娌℃湁璁剧疆瀵嗙爜锛岃繕瑕佷慨鏀筦ragflow/docker`鏂囦欢澶逛笅`service_conf.yaml.template`锛屽皢`infini_rag_flow`鏇挎崲鎴愮┖瀛楃涓层��
+
+淇敼鍓�
+``` shell
+redis:
+ db: 1
+ password: '${REDIS_PASSWORD:-infini_rag_flow}'
+ host: '${REDIS_HOST:-redis}:6379'
+```
+淇敼鍚�
+``` shell
+redis:
+ db: 1
+ password: '${REDIS_PASSWORD:-}'
+ host: '${REDIS_HOST:-redis}:6379'
+```
+
+## 绗簲姝ワ紝鍚姩ragflow鏈嶅姟
+鎵ц鍛戒护锛�
+``` shell
+docker-compose -f docker-compose.yml up -d
+```
+鎵ц鎴愬姛鍚庯紝浣犲彲浠ヤ娇鐢╜docker logs -n 20 -f docker-ragflow-cpu-1`鍛戒护锛屾煡鐪媊docker-ragflow-cpu-1`鏈嶅姟鐨勬棩蹇椼��
+
+濡傛灉鏃ュ織涓病鏈夋姤閿欙紝璇存槑ragflow鏈嶅姟鍚姩鎴愬姛銆�
+
+# 绗簲姝ワ紝娉ㄥ唽璐﹀彿
+浣犲彲浠ュ湪娴忚鍣ㄤ腑璁块棶`http://127.0.0.1:8008`锛岀偣鍑籤Sign Up`锛屾敞鍐屼竴涓处鍙枫��
+
+娉ㄥ唽鎴愬姛鍚庯紝浣犲彲浠ョ偣鍑籤Sign In`锛岀櫥褰曞埌ragflow鏈嶅姟銆傚鏋滀綘鎯冲叧闂璻agflow鏈嶅姟鐨勬敞鍐屾湇鍔★紝涓嶆兂璁╁叾浠栦汉娉ㄥ唽璐﹀彿锛屼綘鍙互鍦╜ragflow/docker`鏂囦欢澶逛笅鐨刞.env`鏂囦欢涓紝灏哷REGISTER_ENABLED`閰嶇疆椤硅缃负`0`銆�
+
+``` dotenv
+REGISTER_ENABLED=0
+```
+淇敼鍚庯紝閲嶅惎鍚姩ragflow鏈嶅姟銆�
+``` shell
+docker-compose -f docker-compose.yml down
+docker-compose -f docker-compose.yml up -d
+```
+
+# 绗叚姝ワ紝閰嶇疆ragflow鏈嶅姟鐨勬ā鍨�
+浣犲彲浠ュ湪娴忚鍣ㄤ腑璁块棶`http://127.0.0.1:8008`锛岀偣鍑籤Sign In`锛岀櫥褰曞埌ragflow鏈嶅姟銆傜偣鍑婚〉闈㈠彸涓婅鐨刞澶村儚`锛岃繘鍏ヨ缃〉闈€��
+棣栧厛锛屽湪宸︿晶瀵艰埅鏍忎腑锛岀偣鍑籤妯″瀷渚涘簲鍟哷锛岃繘鍏ュ埌妯″瀷閰嶇疆椤甸潰銆傚湪鍙充晶鐨刞鍙�夋ā鍨媊鎼滅储妗嗕笅锛岄�夋嫨`LLM`锛屽湪鍒楄〃閫夋嫨浣犱娇鐢ㄧ殑妯″瀷渚涘簲鍟嗭紝鐐瑰嚮`娣诲姞`锛岃緭鍏ヤ綘鐨勫瘑閽ワ紱
+鐒跺悗锛岄�夋嫨`TEXT EMBEDDING`锛屽湪鍒楄〃閫夋嫨浣犱娇鐢ㄧ殑妯″瀷渚涘簲鍟嗭紝鐐瑰嚮`娣诲姞`锛岃緭鍏ヤ綘鐨勫瘑閽ャ��
+鏈�鍚庯紝鍒锋柊涓�涓嬮〉闈紝鍒嗗埆鐐瑰嚮`璁剧疆榛樿妯″瀷`鍒楄〃鐨凩LM鍜孍mbedding锛岄�夋嫨浣犱娇鐢ㄧ殑妯″瀷鍗冲彲銆傝纭浣犵殑瀵嗛挜寮�閫氫簡鐩稿簲鐨勬湇鍔★紝姣斿鎴戞槸鐢ㄧ殑Embedding妯″瀷鏄痻xx渚涘簲鍟嗙殑锛岄渶瑕佸幓杩欎釜渚涘簲鍟嗗畼缃戞煡鐪嬭繖涓ā鍨嬫槸鍚﹂渶瑕佽喘涔拌祫婧愬寘鎵嶈兘浣跨敤銆�
+
+
+# 绗簩閮ㄥ垎 閰嶇疆ragflow鏈嶅姟
+
+# 绗竴姝� 鐧诲綍ragflow鏈嶅姟
+浣犲彲浠ュ湪娴忚鍣ㄤ腑璁块棶`http://127.0.0.1:8008`锛岀偣鍑籤Sign In`锛岀櫥褰曞埌ragflow鏈嶅姟銆�
+
+鐒跺悗鐐瑰嚮鍙充笂瑙掔殑`澶村儚`锛岃繘鍏ヨ缃〉闈€�傚湪宸︿晶瀵艰埅鏍忎腑锛岀偣鍑籤API`鍔熻兘锛岀劧鍚庣偣鍑�"API Key"鎸夐挳銆傚嚭鐜颁竴涓脊妗嗭紝
+
+鍦ㄥ脊妗嗕腑锛岀偣鍑�"Create new Key"鎸夐挳锛岀敓鎴愪竴涓狝PI Key銆傚鍒惰繖涓猔API Key`锛屼綘绋嶅悗浼氱敤鍒般��
+
+# 绗簩姝� 閰嶇疆鍒版櫤鎺у彴
+纭繚浣犵殑鏅烘帶鍙扮増鏈槸`0.8.7`鎴栦互涓娿�備娇鐢ㄨ秴绾х鐞嗗憳璐﹀彿鐧诲綍鍒版櫤鎺у彴銆�
+
+棣栧厛锛屼綘瑕佸厛寮�鍚煡璇嗗簱鍔熻兘銆傚湪椤堕儴瀵艰埅鏍忎腑锛岀偣鍑籤鍙傛暟瀛楀吀`锛屽湪涓嬫媺鑿滃崟涓紝鐐瑰嚮`绯荤粺鍔熻兘閰嶇疆`椤甸潰銆傚湪椤甸潰涓婂嬀閫塦鐭ヨ瘑搴揱锛岀偣鍑籤淇濆瓨閰嶇疆`銆傚嵆鍙湪瀵艰埅鏍忕湅鍒癭鐭ヨ瘑搴揱鍔熻兘銆�
+
+鍦ㄩ《閮ㄥ鑸爮涓紝鐐瑰嚮`妯″瀷閰嶇疆`锛屽湪宸︿晶瀵艰埅鏍忎腑锛岀偣鍑籤鐭ヨ瘑搴揱銆傚湪鍒楄〃涓壘鍒癭RAG_RAGFlow`锛岀偣鍑籤缂栬緫`鎸夐挳銆�
+
+鍦╜鏈嶅姟鍦板潃`涓紝濉啓`http://浣犵殑ragflow鏈嶅姟鐨勫眬鍩熺綉IP:8008`锛屼緥濡傛垜鐨剅agflow鏈嶅姟鐨勫眬鍩熺綉IP鏄痐192.168.1.100`锛岄偅涔堟垜灏卞~鍐檂http://192.168.1.100:8008`銆�
+
+鍦╜API瀵嗛挜`涓紝濉啓涔嬪墠澶嶅埗鐨刞API Key`銆�
+
+鏈�鍚庣偣鍑讳繚瀛樻寜閽��
+
+# 绗簩姝� 鍒涘缓涓�涓煡璇嗗簱
+浣跨敤瓒呯骇绠$悊鍛樿处鍙风櫥褰曞埌鏅烘帶鍙般�傚湪椤堕儴瀵艰埅鏍忎腑锛岀偣鍑籤鐭ヨ瘑搴揱锛屽湪鍒楄〃宸︿笅鑴氾紝鐐瑰嚮`鏂板`鎸夐挳銆傚~鍐欎竴涓煡璇嗗簱鐨勫悕瀛楀拰鎻忚堪銆傜偣鍑讳繚瀛樸��
+
+涓轰簡鎻愰珮澶фā鍨嬪鐭ヨ瘑搴撶殑鐞嗚В鍜屽彫鍥炶兘鍔涳紝寤鸿鍦ㄥ垱寤虹煡璇嗗簱鏃讹紝濉啓涓�涓湁鎰忎箟鐨勫悕瀛楀拰鎻忚堪銆備緥濡傦紝濡傛灉浣犺鍒涘缓涓�涓叧浜巂鍏徃浠嬬粛`鐨勭煡璇嗗簱锛岄偅涔堢煡璇嗗簱鐨勫悕瀛楀彲浠ユ槸`鍏徃浠嬬粛`锛屾弿杩板彲浠ユ槸`鍏充簬鍏徃鐨勭浉鍏充俊鎭緥濡傚叕鍙稿熀鏈俊鎭�佹湇鍔¢」鐩�佽仈绯荤數璇濄�佸湴鍧�绛夈�俙銆�
+
+淇濆瓨鍚庯紝浣犲彲浠ュ湪鐭ヨ瘑搴撳垪琛ㄤ腑鐪嬪埌杩欎釜鐭ヨ瘑搴撱�傜偣鍑诲垰鎵嶅垱寤虹殑鐭ヨ瘑搴撶殑`鏌ョ湅`鎸夐挳锛岃繘鍏ョ煡璇嗗簱璇︽儏椤甸潰銆�
+
+鍦ㄧ煡璇嗗簱璇︽儏椤甸潰涓紝宸︿笅瑙掔偣鍑籤鏂板`鎸夐挳锛屽彲浠ヤ笂浼犳枃妗e埌鐭ヨ瘑搴撱��
+
+涓婁紶鍚庯紝浣犲彲浠ュ湪鐭ヨ瘑搴撹鎯呴〉闈腑锛岀湅鍒颁笂浼犵殑鏂囨。銆傛鏃跺彲浠ョ偣鍑绘枃妗g殑`瑙f瀽`鎸夐挳锛岃В鏋愭枃妗c��
+
+瑙f瀽瀹屾垚鍚庯紝浣犲彲浠ユ煡鐪嬭В鏋愬悗鐨勫垏鐗囦俊鎭�備綘鍙互鍦ㄧ煡璇嗗簱璇︽儏椤甸潰涓紝鐐瑰嚮`鍙洖娴嬭瘯`鎸夐挳锛屽彲浠ユ祴璇曠煡璇嗗簱鐨勫彫鍥�/妫�绱㈠姛鑳姐��
+
+# 绗笁姝� 璁╁皬鏅轰娇鐢╮agflow鐭ヨ瘑搴�
+鐧诲綍鍒版櫤鎺у彴銆傚湪椤堕儴瀵艰埅鏍忎腑锛岀偣鍑籤鏅鸿兘浣揱锛屾壘鍒颁綘瑕侀厤缃殑鏅鸿兘浣擄紝鐐瑰嚮`閰嶇疆瑙掕壊`鎸夐挳銆�
+
+鍦ㄦ剰鍥捐瘑鍒乏渚э紝鐐瑰嚮`缂栬緫鍔熻兘`鎸夐挳锛屽脊鍑轰竴涓脊妗嗐�傚湪寮规涓�夋嫨浣犺娣诲姞鐨勭煡璇嗗簱銆備繚瀛樺嵆鍙��
diff --git a/xiaozhi-esp32-server/docs/readme/README_de.md b/xiaozhi-esp32-server/docs/readme/README_de.md
new file mode 100644
index 0000000..ccc409f
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/readme/README_de.md
@@ -0,0 +1,361 @@
+[](https://github.com/xinnan-tech/xiaozhi-esp32-server)
+
+<h1 align="center">Xiaozhi Backend-Service xiaozhi-esp32-server</h1>
+
+<p align="center">
+Dieses Projekt basiert auf der Theorie und Technologie der Mensch-Maschine-symbiotischen Intelligenz zur Entwicklung intelligenter Terminal-Hardware- und Software-Systeme<br/>und bietet Backend-Dienste f眉r das Open-Source-Hardware-Projekt
+<a href="https://github.com/78/xiaozhi-esp32">xiaozhi-esp32</a><br/>
+Implementiert mit Python, Java und Vue gem盲脽 dem <a href="https://ccnphfhqs21z.feishu.cn/wiki/M0XiwldO9iJwHikpXD5cEx71nKh">Xiaozhi-Kommunikationsprotokoll</a><br/>
+Unterst眉tzt MQTT+UDP-Protokoll, Websocket-Protokoll, MCP-Endpunkte und Stimmabdruckerkennung
+</p>
+
+<p align="center">
+<a href="../FAQ.md">H盲ufige Fragen</a>
+路 <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/issues">Probleme melden</a>
+路 <a href="../../README.md#%E9%83%A8%E7%BD%B2%E6%96%87%E6%A1%A3">Deployment-Dokumentation</a>
+路 <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/releases">Release-Hinweise</a>
+</p>
+
+<p align="center">
+ <a href="../../README.md"><img alt="绠�浣撲腑鏂囩増鑷堪鏂囦欢" src="https://img.shields.io/badge/绠�浣撲腑鏂�-DFE0E5"></a>
+ <a href="./README_en.md"><img alt="README in English" src="https://img.shields.io/badge/English-DFE0E5"></a>
+ <a href="./README_vi.md"><img alt="Ti岷縩g Vi峄噒" src="https://img.shields.io/badge/Ti岷縩g Vi峄噒-DFE0E5"></a>
+ <a href="./README_de.md"><img alt="Deutsch" src="https://img.shields.io/badge/Deutsch-DBEDFA"></a>
+ <a href="./README_pt_BR.md"><img alt="Portugu锚s (Brasil)" src="https://img.shields.io/badge/Portugu锚s (Brasil)-DFE0E5"></a>
+ <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/releases">
+ <img alt="GitHub Contributors" src="https://img.shields.io/github/v/release/xinnan-tech/xiaozhi-esp32-server?logo=docker" />
+ </a>
+ <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/blob/main/LICENSE">
+ <img alt="GitHub pull requests" src="https://img.shields.io/badge/license-MIT-white?labelColor=black" />
+ </a>
+ <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server">
+ <img alt="stars" src="https://img.shields.io/github/stars/xinnan-tech/xiaozhi-esp32-server?color=ffcb47&labelColor=black" />
+ </a>
+</p>
+
+<p align="center">
+Geleitet vom Team von Professor Siyuan Liu (South China University of Technology)
+</br>
+鍒樻�濇簮鏁欐巿鍥㈤槦涓诲鐮斿彂(鍗庡崡鐞嗗伐澶у)
+</br>
+<img src="../images/hnlg.jpg" alt="South China University of Technology" width="50%">
+</p>
+
+---
+
+## Zielgruppe 馃懃
+
+Dieses Projekt erfordert ESP32-Hardware-Ger盲te zum Betrieb. Wenn Sie ESP32-bezogene Hardware erworben haben, erfolgreich eine Verbindung zu Brother Xias bereitgestelltem Backend-Service hergestellt haben und Ihren eigenen `xiaozhi-esp32`-Backend-Service unabh盲ngig aufbauen m枚chten, dann ist dieses Projekt perfekt f眉r Sie.
+
+M枚chten Sie die Nutzungseffekte sehen? Klicken Sie auf die Videos unten 馃帴
+
+<table>
+ <tr>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1FMFyejExX" target="_blank">
+ <picture>
+ <img alt="鍝嶅簲閫熷害鎰熷彈" src="docs/images/demo9.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1vchQzaEse" target="_blank">
+ <picture>
+ <img alt="閫熷害浼樺寲绉樿瘈" src="docs/images/demo6.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1WEcxzFEAT" target="_blank">
+ <picture>
+ <img alt="灏忔櫤鏁板瓧浜� 鏀寔璇煶鍞ら啋" src="docs/images/demo8.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1CKVz6UEuB" target="_blank">
+ <picture>
+ <img alt="璁惧鍛煎彨璁惧锛屾墦鐢佃瘽" src="docs/images/demo0.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1C1tCzUEZh" target="_blank">
+ <picture>
+ <img alt="澶嶆潅鍖荤枟鍦烘櫙" src="docs/images/demo1.png" /></picture>
+ </a>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1VC96Y5EMH" target="_blank">
+ <picture>
+ <img alt="鎾斁闊充箰鏌ヨ澶╂皵鎾姤鏂伴椈" src="docs/images/demo7.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV12J7WzBEaH" target="_blank">
+ <picture>
+ <img alt="瀹炴椂鎵撴柇" src="docs/images/demo10.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1Co76z7EvK" target="_blank">
+ <picture>
+ <img alt="鎷嶇収璇嗙墿鍝�" src="docs/images/demo12.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1pNXWYGEx1" target="_blank">
+ <picture>
+ <img alt="鎺у埗瀹剁數寮�鍏�" src="docs/images/demo5.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1TJ7WzzEo6" target="_blank">
+ <picture>
+ <img alt="澶氭寚浠や换鍔�" src="docs/images/demo11.png" /></picture>
+ </a>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1ZQKUzYExM" target="_blank">
+ <picture>
+ <img alt="MCP鎺ュ叆鐐�" src="docs/images/demo13.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1zUW5zJEkq" target="_blank">
+ <picture>
+ <img alt="MQTT鎸囦护涓嬪彂" src="docs/images/demo4.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1Exu3zqEDe" target="_blank">
+ <picture>
+ <img alt="澹扮汗璇嗗埆" src="docs/images/demo14.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1CDKWemEU6" target="_blank">
+ <picture>
+ <img alt="鑷畾涔夐煶鑹�" src="docs/images/demo2.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV12yA2egEaC" target="_blank">
+ <picture>
+ <img alt="浣跨敤绮よ浜ゆ祦" src="docs/images/demo3.png" /></picture>
+ </a>
+ </td>
+ </tr>
+</table>
+
+---
+
+## Warnungen 鈿狅笍
+
+1. Dieses Projekt ist Open-Source-Software. Diese Software hat keine kommerzielle Partnerschaft mit Drittanbieter-API-Dienstleistern (einschlie脽lich, aber nicht beschr盲nkt auf Spracherkennung, gro脽e Modelle, Sprachsynthese und andere Plattformen), mit denen sie sich verbindet, und bietet keine Garantie f眉r deren Servicequalit盲t oder finanzielle Sicherheit. Es wird empfohlen, dass Benutzer Dienstleister mit entsprechenden Gesch盲ftslizenzen bevorzugen und deren Servicevereinbarungen und Datenschutzrichtlinien sorgf盲ltig lesen. Diese Software hostet keine Kontoschl眉ssel, nimmt nicht an Geldstr枚men teil und tr盲gt nicht das Risiko von Verlusten bei Guthaben-Aufladungen.
+
+2. Die Funktionalit盲t dieses Projekts ist nicht vollst盲ndig und hat keine Netzwerksicherheitsbewertung bestanden. Bitte verwenden Sie es nicht in Produktionsumgebungen. Wenn Sie dieses Projekt zu Lernzwecken in einer 枚ffentlichen Netzwerkumgebung bereitstellen, stellen Sie bitte sicher, dass notwendige Schutzma脽nahmen vorhanden sind.
+
+---
+
+## Deployment-Dokumentation
+
+
+
+Dieses Projekt bietet zwei Bereitstellungsmethoden. Bitte w盲hlen Sie basierend auf Ihren spezifischen Anforderungen:
+
+#### 馃殌 Auswahl der Bereitstellungsmethode
+| Bereitstellungsmethode | Funktionen | Anwendungsszenarien | Deployment-Dokumente | Konfigurationsanforderungen | Video-Tutorials |
+|---------|------|---------|---------|---------|---------|
+| **Vereinfachte Installation** | Intelligenter Dialog, Einzel-Agenten-Verwaltung | Umgebungen mit geringer Konfiguration, Daten in Konfigurationsdateien gespeichert, keine Datenbank erforderlich | [鈶燚ocker-Version](../Deployment.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E5%8F%AA%E8%BF%90%E8%A1%8Cserver) / [鈶uellcode-Deployment](../Deployment.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E5%8F%AA%E8%BF%90%E8%A1%8Cserver)| 2 Kerne 4GB bei Verwendung von `FunASR`, 2 Kerne 2GB bei allen APIs | - |
+| **Vollst盲ndige Modulinstallation** | Intelligenter Dialog, Mehrbenutzerverwaltung, Mehr-Agenten-Verwaltung, Intelligente Steuerkonsole-Bedienung | Vollst盲ndige Funktionserfahrung, Daten in Datenbank gespeichert |[鈶燚ocker-Version](../Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [鈶uellcode-Deployment](../Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [鈶uellcode-Deployment Auto-Update-Tutorial](../dev-ops-integration.md) | 4 Kerne 8GB bei Verwendung von `FunASR`, 2 Kerne 4GB bei allen APIs| [Video-Tutorial f眉r lokalen Quellcode-Start](https://www.bilibili.com/video/BV1wBJhz4Ewe) |
+
+H盲ufige Fragen und entsprechende Tutorials finden Sie unter [diesem Link](../FAQ.md)
+
+> 馃挕 Hinweis: Unten ist eine Testplattform, die mit dem neuesten Code bereitgestellt wurde. Sie k枚nnen bei Bedarf brennen und testen. Gleichzeitige Benutzer: 6, Daten werden t盲glich gel枚scht.
+
+```
+Adresse der intelligenten Steuerkonsole: https://2662r3426b.vicp.fun
+Adresse der intelligenten Steuerkonsole (H5): https://2662r3426b.vicp.fun/h5/index.html
+
+Service-Testtool: https://2662r3426b.vicp.fun/test/
+OTA-Schnittstellenadresse: https://2662r3426b.vicp.fun/xiaozhi/ota/
+Websocket-Schnittstellenadresse: wss://2662r3426b.vicp.fun/xiaozhi/v1/
+```
+
+#### 馃毄 Konfigurationsbeschreibung und Empfehlungen
+> [!Note]
+> Dieses Projekt bietet zwei Konfigurationsschemata:
+>
+> 1. `Einstiegslevel Kostenlose Einstellungen`: Geeignet f眉r den pers枚nlichen und privaten Gebrauch, alle Komponenten verwenden kostenlose L枚sungen, keine zus盲tzliche Zahlung erforderlich.
+>
+> 2. `Streaming-Konfiguration`: Geeignet f眉r Demonstrationen, Schulungen, Szenarien mit mehr als 2 gleichzeitigen Benutzern usw. Verwendet Streaming-Verarbeitungstechnologie f眉r schnellere Reaktionsgeschwindigkeit und bessere Erfahrung.
+>
+> Ab Version `0.5.2` unterst眉tzt das Projekt Streaming-Konfiguration. Im Vergleich zu fr眉heren Versionen ist die Reaktionsgeschwindigkeit um ca. `2,5 Sekunden` verbessert, was die Benutzererfahrung erheblich verbessert.
+
+| Modulname | Einstiegslevel Kostenlose Einstellungen | Streaming-Konfiguration |
+|:---:|:---:|:---:|
+| ASR (Spracherkennung) | FunASR (Lokal) | 馃憤XunfeiStreamASR (Xunfei-Streaming) |
+| LLM (Gro脽es Modell) | glm-4-flash (Zhipu) | 馃憤qwen-flash (Alibaba Bailian) |
+| VLLM (Vision Large Model) | glm-4v-flash (Zhipu) | 馃憤qwen3.5-flash (Alibaba Bailian) |
+| TTS (Sprachsynthese) | EdgeTTS (Microsoft) | 馃憤HuoshanDoubleStreamTTS (Volcano-Streaming) |
+| Intent (Absichtserkennung) | function_call (Funktionsaufruf) | function_call (Funktionsaufruf) |
+| Memory (Ged盲chtnisfunktion) | mem_local_short (Lokales Kurzzeitged盲chtnis) | mem_local_short (Lokales Kurzzeitged盲chtnis) |
+
+Wenn Sie sich um die Latenz jeder Komponente k眉mmern, lesen Sie bitte den [Xiaozhi-Komponenten-Leistungstestbericht](https://github.com/xinnan-tech/xiaozhi-performance-research). Sie k枚nnen gem盲脽 den Testmethoden im Bericht in Ihrer Umgebung tats盲chlich testen.
+
+#### 馃敡 Testwerkzeuge
+Dieses Projekt bietet die folgenden Testwerkzeuge, um Ihnen bei der 脺berpr眉fung des Systems und der Auswahl geeigneter Modelle zu helfen:
+
+| Werkzeugname | Standort | Verwendungsmethode | Funktionsbeschreibung |
+|:---:|:---|:---:|:---:|
+| Audio-Interaktionstesttool | main銆媎igital-human銆媔ndex.html | In `main/digital-human` `python start.py` ausf眉hren und dann `http://127.0.0.1:8006/index.html` 枚ffnen | Testet Audio-Wiedergabe- und Empfangsfunktionen, 眉berpr眉ft, ob die Python-seitige Audioverarbeitung normal ist |
+| Modell-Reaktionstesttool | main銆媥iaozhi-server銆媝erformance_tester.py | Ausf眉hren `python performance_tester.py` | Testet die Reaktionsgeschwindigkeit von ASR (Spracherkennung), LLM (gro脽es Modell), VLLM (Vision-Modell), TTS (Sprachsynthese) drei Kernmodulen |
+
+> 馃挕 Hinweis: Beim Testen der Modellgeschwindigkeit werden nur Modelle mit konfigurierten Schl眉sseln getestet.
+
+---
+## Funktionsliste 鉁�
+### Implementiert 鉁�
+
+| Funktionsmodul | Beschreibung |
+|:---:|:---|
+| Kernarchitektur | Basierend auf [MQTT+UDP-Gateway](https://github.com/xinnan-tech/xiaozhi-esp32-server/blob/main/docs/mqtt-gateway-integration.md), WebSocket und HTTP-Servern, bietet vollst盲ndiges Konsolenverwaltungs- und Authentifizierungssystem |
+| Sprachinteraktion | Unterst眉tzt Streaming-ASR (Spracherkennung), Streaming-TTS (Sprachsynthese), VAD (Sprachaktivit盲tserkennung), unterst眉tzt mehrsprachige Erkennung und Sprachverarbeitung |
+| Stimmabdruckerkennung | Unterst眉tzt Mehrbenutzer-Stimmabdruckregistrierung, -verwaltung und -erkennung, verarbeitet parallel mit ASR, Echtzeit-Sprecheridentit盲tserkennung und Weitergabe an LLM f眉r personalisierte Antworten |
+| Intelligenter Dialog | Unterst眉tzt mehrere LLM (gro脽e Sprachmodelle), implementiert intelligenten Dialog |
+| Visuelle Wahrnehmung | Unterst眉tzt mehrere VLLM (Vision Large Models), implementiert multimodale Interaktion |
+| Absichtserkennung | Unterst眉tzt LLM-Absichtserkennung, Function Call-Funktionsaufruf, bietet plugin-basierten Absichtsverarbeitungsmechanismus |
+| Ged盲chtnissystem | Unterst眉tzt lokales Kurzzeitged盲chtnis, mem0ai-Schnittstellenged盲chtnis, PowerMem intelligentes Ged盲chtnis, mit Ged盲chtniszusammenfassungsfunktion |
+| Wissensdatenbank | Unterst眉tzt RAGFlow-Wissensdatenbank, erm枚glicht gro脽em Modell die Bewertung, ob Wissensdatenbank ben枚tigt wird, bevor geantwortet wird |
+| Werkzeugaufruf | Unterst眉tzt Client-IOT-Protokoll, Client-MCP-Protokoll, Server-MCP-Protokoll, MCP-Endpunktprotokoll, benutzerdefinierte Werkzeugfunktionen |
+| Befehls眉bermittlung | Basierend auf MQTT-Protokoll, unterst眉tzt die 脺bermittlung von MCP-Befehlen von der intelligenten Steuerkonsole an ESP32-Ger盲te |
+| Verwaltungs-Backend | Bietet Web-Verwaltungsoberfl盲che, unterst眉tzt Benutzerverwaltung, Systemkonfiguration und Ger盲teverwaltung; Oberfl盲che unterst眉tzt vereinfachtes Chinesisch, traditionelles Chinesisch und englische Anzeige |
+| Testwerkzeuge | Bietet Leistungstestwerkzeuge, Vision-Modell-Testwerkzeuge und Audio-Interaktionstestwerkzeuge |
+| Deployment-Unterst眉tzung | Unterst眉tzt Docker-Deployment und lokales Deployment, bietet vollst盲ndige Konfigurationsdateiverwaltung |
+| Plugin-System | Unterst眉tzt funktionale Plugin-Erweiterungen, benutzerdefinierte Plugin-Entwicklung und Plugin-Hot-Loading |
+
+### In Entwicklung 馃毀
+
+Um 眉ber spezifische Entwicklungsplanfortschritte zu erfahren, [klicken Sie hier](https://github.com/users/xinnan-tech/projects/3). H盲ufige Fragen und entsprechende Tutorials finden Sie unter [diesem Link](../FAQ.md)
+
+Wenn Sie ein Softwareentwickler sind, finden Sie hier einen [Offenen Brief an Entwickler](../contributor_open_letter.md). Willkommen beim Beitritt!
+
+---
+
+## Produkt枚kosystem 馃懍
+Xiaozhi ist ein 脰kosystem. Wenn Sie dieses Produkt verwenden, k枚nnen Sie sich auch andere [hervorragende Projekte](https://github.com/78/xiaozhi-esp32/blob/main/README_zh.md#%E7%9B%B8%E5%85%B3%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE) in diesem 脰kosystem ansehen
+
+---
+
+## Liste der von diesem Projekt unterst眉tzten Plattformen/Komponenten 馃搵
+### LLM-Sprachmodelle
+
+| Verwendungsmethode | Unterst眉tzte Plattformen | Kostenlose Plattformen |
+|:---:|:---:|:---:|
+| OpenAI-Schnittstellenaufrufe | Alibaba Bailian, Volcano Engine Doubao, DeepSeek, Zhipu ChatGLM, Gemini | Zhipu ChatGLM, Gemini |
+| Ollama-Schnittstellenaufrufe | Ollama | - |
+| Dify-Schnittstellenaufrufe | Dify | - |
+| FastGPT-Schnittstellenaufrufe | FastGPT | - |
+| Coze-Schnittstellenaufrufe | Coze | - |
+| Xinference-Schnittstellenaufrufe | Xinference | - |
+| HomeAssistant-Schnittstellenaufrufe | HomeAssistant | - |
+
+Tats盲chlich kann jedes LLM, das OpenAI-Schnittstellenaufrufe unterst眉tzt, integriert und verwendet werden.
+
+---
+
+### VLLM-Vision-Modelle
+
+| Verwendungsmethode | Unterst眉tzte Plattformen | Kostenlose Plattformen |
+|:---:|:---:|:---:|
+| OpenAI-Schnittstellenaufrufe | Alibaba Bailian, Zhipu ChatGLMVLLM | Zhipu ChatGLMVLLM |
+
+Tats盲chlich kann jedes VLLM, das OpenAI-Schnittstellenaufrufe unterst眉tzt, integriert und verwendet werden.
+
+---
+
+### TTS-Sprachsynthese
+
+| Verwendungsmethode | Unterst眉tzte Plattformen | Kostenlose Plattformen |
+|:---:|:---:|:---:|
+| Schnittstellenaufrufe | EdgeTTS, Volcano Engine Doubao TTS, Tencent Cloud, Alibaba Cloud TTS, AliYun Stream TTS, CosyVoiceSiliconflow, TTS302AI, CozeCnTTS, GizwitsTTS, ACGNTTS, OpenAITTS, Lingxi Streaming TTS, MinimaxTTS, Volcano Dual-Stream TTS | Lingxi Streaming TTS, EdgeTTS, CosyVoiceSiliconflow (teilweise) |
+| Lokale Dienste | FishSpeech, GPT_SOVITS_V2, GPT_SOVITS_V3, Index-TTS, PaddleSpeech | Index-TTS, PaddleSpeech, FishSpeech, GPT_SOVITS_V2, GPT_SOVITS_V3 |
+
+---
+
+### VAD-Sprachaktivit盲tserkennung
+
+| Typ | Plattformname | Verwendungsmethode | Preismodell | Hinweise |
+|:---:|:---------:|:----:|:----:|:--:|
+| VAD | SileroVAD | Lokale Verwendung | Kostenlos | |
+
+---
+
+### ASR-Spracherkennung
+
+| Verwendungsmethode | Unterst眉tzte Plattformen | Kostenlose Plattformen |
+|:---:|:---:|:---:|
+| Lokale Verwendung | FunASR, SherpaASR | FunASR, SherpaASR |
+| Schnittstellenaufrufe | DoubaoASR, Doubao Streaming ASR, FunASRServer, TencentASR, AliyunASR, Aliyun Streaming ASR, Baidu ASR, OpenAI ASR | FunASRServer |
+
+---
+
+### Voiceprint-Stimmabdruckerkennung
+
+| Verwendungsmethode | Unterst眉tzte Plattformen | Kostenlose Plattformen |
+|:---:|:---:|:---:|
+| Lokale Verwendung | 3D-Speaker | 3D-Speaker |
+
+---
+
+### Memory-Ged盲chtnisspeicher
+
+| Typ | Plattformname | Verwendungsmethode | Preismodell | Hinweise |
+|:------:|:---------------:|:----:|:---------:|:--:|
+| Memory | mem0ai | Schnittstellenaufrufe | 1000 Mal/Monat Kontingent | |
+| Memory | [powermem](../powermem-integration.md) | Lokale Zusammenfassung | Abh盲ngig von LLM und DB | OceanBase Open Source, unterst眉tzt intelligente Abfrage |
+| Memory | mem_local_short | Lokale Zusammenfassung | Kostenlos | |
+| Memory | nomem | Kein Ged盲chtnismodus | Kostenlos | |
+
+---
+
+### Intent-Absichtserkennung
+
+| Typ | Plattformname | Verwendungsmethode | Preismodell | Hinweise |
+|:------:|:-------------:|:----:|:-------:|:---------------------:|
+| Intent | intent_llm | Schnittstellenaufrufe | Basierend auf LLM-Preisen | Erkennt Absicht durch gro脽e Modelle, starke Allgemeing眉ltigkeit |
+| Intent | function_call | Schnittstellenaufrufe | Basierend auf LLM-Preisen | Vervollst盲ndigt Absicht durch Funktionsaufruf gro脽er Modelle, schnelle Geschwindigkeit, guter Effekt |
+| Intent | nointent | Kein Absichtsmodus | Kostenlos | F眉hrt keine Absichtserkennung durch, gibt direkt Dialogergebnis zur眉ck |
+
+---
+
+### Rag Retrieval Augmented Generation
+
+| Typ | Plattformname | Verwendungsmethode | Preismodell | Hinweise |
+|:------:|:-------------:|:----:|:-------:|:---------------------:|
+| Rag | ragflow | Schnittstellenaufrufe | Geb眉hren basierend auf Token-Verbrauch f眉r Segmentierung und Tokenisierung | Nutzt RAGFlow's Retrieval-Augmented-Generation-Funktion f眉r pr盲zisere Dialogantworten |
+
+---
+
+## Danksagungen 馃檹
+
+| Logo | Projekt/Unternehmen | Beschreibung |
+|:---:|:---:|:---|
+| <img src="../images/logo_bailing.png" width="160"> | [Bailing Voice Dialogue Robot](https://github.com/wwbin2017/bailing) | Dieses Projekt wurde von [Bailing Voice Dialogue Robot](https://github.com/wwbin2017/bailing) inspiriert und auf dessen Basis implementiert |
+| <img src="../images/logo_tenclass.png" width="160"> | [Tenclass](https://www.tenclass.com/) | Dank an [Tenclass](https://www.tenclass.com/) f眉r die Formulierung von Standardkommunikationsprotokollen, Multi-Ger盲te-Kompatibilit盲tsl枚sungen und High-Concurrency-Szenario-Praxisdemonstrationen f眉r das Xiaozhi-脰kosystem; f眉r die Bereitstellung vollst盲ndiger technischer Dokumentationsunterst眉tzung f眉r dieses Projekt |
+| <img src="../images/logo_xuanfeng.png" width="160"> | [Xuanfeng Technology](https://github.com/Eric0308) | Dank an [Xuanfeng Technology](https://github.com/Eric0308) f眉r den Beitrag des Funktionsaufruf-Frameworks, des MCP-Kommunikationsprotokolls und der plugin-basierten Aufrufmechanismus-Implementierungscode. Durch standardisiertes Befehlsplanungssystem und dynamische Erweiterungsf盲higkeiten wird die Interaktionseffizienz und funktionale Erweiterbarkeit von Frontend-Ger盲ten (IoT) erheblich verbessert |
+| <img src="../images/logo_junsen.png" width="160"> | [huangjunsen](https://github.com/huangjunsen0406) | Dank an [huangjunsen](https://github.com/huangjunsen0406) f眉r den Beitrag des `Smart Control Console Mobile`-Moduls, das eine effiziente Steuerung und Echtzeit-Interaktion 眉ber mobile Ger盲te erm枚glicht und die Betriebsbequemlichkeit und Verwaltungseffizienz des Systems in mobilen Szenarien erheblich verbessert |
+| <img src="../images/logo_huiyuan.png" width="160"> | [Huiyuan Design](http://ui.kwd988.net/) | Dank an [Huiyuan Design](http://ui.kwd988.net/) f眉r die Bereitstellung professioneller visueller L枚sungen f眉r dieses Projekt, unter Verwendung ihrer Design-Praxiserfahrung im Dienst von 眉ber tausend Unternehmen, um die Produktbenutzererfahrung dieses Projekts zu st盲rken |
+| <img src="../images/logo_qinren.png" width="160"> | [Xi'an Qinren Information Technology](https://www.029app.com/) | Dank an [Xi'an Qinren Information Technology](https://www.029app.com/) f眉r die Vertiefung des visuellen Systems dieses Projekts und die Sicherstellung der Konsistenz und Erweiterbarkeit des Gesamtdesignstils in Multi-Szenario-Anwendungen |
+| <img src="../images/logo_contributors.png" width="160"> | [Code-Mitwirkende](https://github.com/xinnan-tech/xiaozhi-esp32-server/graphs/contributors) | Dank an [alle Code-Mitwirkenden](https://github.com/xinnan-tech/xiaozhi-esp32-server/graphs/contributors), Ihre Bem眉hungen haben das Projekt robuster und leistungsf盲higer gemacht. |
+
+
+<a href="https://star-history.com/#xinnan-tech/xiaozhi-esp32-server&Date">
+
+ <picture>
+ <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=xinnan-tech/xiaozhi-esp32-server&type=Date&theme=dark" />
+ <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=xinnan-tech/xiaozhi-esp32-server&type=Date" />
+ <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=xinnan-tech/xiaozhi-esp32-server&type=Date" />
+ </picture>
+</a>
diff --git a/xiaozhi-esp32-server/docs/readme/README_en.md b/xiaozhi-esp32-server/docs/readme/README_en.md
new file mode 100644
index 0000000..5e3d780
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/readme/README_en.md
@@ -0,0 +1,361 @@
+[](https://github.com/xinnan-tech/xiaozhi-esp32-server)
+
+<h1 align="center">Xiaozhi Backend Service xiaozhi-esp32-server</h1>
+
+<p align="center">
+This project is based on human-machine symbiotic intelligence theory and technology to develop intelligent terminal hardware and software systems<br/>providing backend services for the open-source intelligent hardware project
+<a href="https://github.com/78/xiaozhi-esp32">xiaozhi-esp32</a><br/>
+Implemented using Python, Java, and Vue according to the <a href="https://ccnphfhqs21z.feishu.cn/wiki/M0XiwldO9iJwHikpXD5cEx71nKh">Xiaozhi Communication Protocol</a><br/>
+Support for MQTT+UDP protocol, Websocket protocol, MCP access point, voiceprint recognition, and knowledge base
+</p>
+
+<p align="center">
+<a href="../FAQ.md">FAQ</a>
+路 <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/issues">Report Issues</a>
+路 <a href="../../README.md#%E9%83%A8%E7%BD%B2%E6%96%87%E6%A1%A3">Deployment Docs</a>
+路 <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/releases">Release Notes</a>
+</p>
+
+<p align="center">
+ <a href="../../README.md"><img alt="绠�浣撲腑鏂囩増鑷堪鏂囦欢" src="https://img.shields.io/badge/绠�浣撲腑鏂�-DFE0E5"></a>
+ <a href="./README_en.md"><img alt="README in English" src="https://img.shields.io/badge/English-DBEDFA"></a>
+ <a href="./README_vi.md"><img alt="Ti岷縩g Vi峄噒" src="https://img.shields.io/badge/Ti岷縩g Vi峄噒-DFE0E5"></a>
+ <a href="./README_de.md"><img alt="Deutsch" src="https://img.shields.io/badge/Deutsch-DFE0E5"></a>
+ <a href="./README_pt_BR.md"><img alt="Portugu锚s (Brasil)" src="https://img.shields.io/badge/Portugu锚s (Brasil)-DFE0E5"></a>
+ <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/releases">
+ <img alt="GitHub Contributors" src="https://img.shields.io/github/v/release/xinnan-tech/xiaozhi-esp32-server?logo=docker" />
+ </a>
+ <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/blob/main/LICENSE">
+ <img alt="GitHub pull requests" src="https://img.shields.io/badge/license-MIT-white?labelColor=black" />
+ </a>
+ <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server">
+ <img alt="stars" src="https://img.shields.io/github/stars/xinnan-tech/xiaozhi-esp32-server?color=ffcb47&labelColor=black" />
+ </a>
+</p>
+
+<p align="center">
+Spearheaded by Professor Siyuan Liu's Team (South China University of Technology)
+</br>
+鍒樻�濇簮鏁欐巿鍥㈤槦涓诲鐮斿彂锛堝崕鍗楃悊宸ュぇ瀛︼級
+</br>
+<img src="../images/hnlg.jpg" alt="South China University of Technology" width="50%">
+</p>
+
+---
+
+## Target Users 馃懃
+
+This project requires ESP32 hardware devices to work. If you have purchased ESP32-related hardware, successfully connected to Brother Xia's deployed backend service, and want to build your own `xiaozhi-esp32` backend service independently, then this project is perfect for you.
+
+Want to see the usage effects? Click the videos below 馃帴
+
+<table>
+ <tr>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1FMFyejExX" target="_blank">
+ <picture>
+ <img alt="鍝嶅簲閫熷害鎰熷彈" src="docs/images/demo9.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1vchQzaEse" target="_blank">
+ <picture>
+ <img alt="閫熷害浼樺寲绉樿瘈" src="docs/images/demo6.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1WEcxzFEAT" target="_blank">
+ <picture>
+ <img alt="灏忔櫤鏁板瓧浜� 鏀寔璇煶鍞ら啋" src="docs/images/demo8.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1CKVz6UEuB" target="_blank">
+ <picture>
+ <img alt="璁惧鍛煎彨璁惧锛屾墦鐢佃瘽" src="docs/images/demo0.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1C1tCzUEZh" target="_blank">
+ <picture>
+ <img alt="澶嶆潅鍖荤枟鍦烘櫙" src="docs/images/demo1.png" /></picture>
+ </a>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1VC96Y5EMH" target="_blank">
+ <picture>
+ <img alt="鎾斁闊充箰鏌ヨ澶╂皵鎾姤鏂伴椈" src="docs/images/demo7.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV12J7WzBEaH" target="_blank">
+ <picture>
+ <img alt="瀹炴椂鎵撴柇" src="docs/images/demo10.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1Co76z7EvK" target="_blank">
+ <picture>
+ <img alt="鎷嶇収璇嗙墿鍝�" src="docs/images/demo12.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1pNXWYGEx1" target="_blank">
+ <picture>
+ <img alt="鎺у埗瀹剁數寮�鍏�" src="docs/images/demo5.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1TJ7WzzEo6" target="_blank">
+ <picture>
+ <img alt="澶氭寚浠や换鍔�" src="docs/images/demo11.png" /></picture>
+ </a>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1ZQKUzYExM" target="_blank">
+ <picture>
+ <img alt="MCP鎺ュ叆鐐�" src="docs/images/demo13.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1zUW5zJEkq" target="_blank">
+ <picture>
+ <img alt="MQTT鎸囦护涓嬪彂" src="docs/images/demo4.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1Exu3zqEDe" target="_blank">
+ <picture>
+ <img alt="澹扮汗璇嗗埆" src="docs/images/demo14.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1CDKWemEU6" target="_blank">
+ <picture>
+ <img alt="鑷畾涔夐煶鑹�" src="docs/images/demo2.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV12yA2egEaC" target="_blank">
+ <picture>
+ <img alt="浣跨敤绮よ浜ゆ祦" src="docs/images/demo3.png" /></picture>
+ </a>
+ </td>
+ </tr>
+</table>
+
+---
+
+## Warnings 鈿狅笍
+
+1. This project is open-source software. This software has no commercial partnership with any third-party API service providers (including but not limited to speech recognition, large models, speech synthesis, and other platforms) that it interfaces with, and does not provide any form of guarantee for their service quality or financial security. It is recommended that users prioritize service providers with relevant business licenses and carefully read their service agreements and privacy policies. This software does not host any account keys, does not participate in fund flows, and does not bear the risk of recharge fund losses.
+
+2. The functionality of this project is not complete and has not passed network security assessment. Please do not use it in production environments. If you deploy this project for learning purposes in a public network environment, please ensure necessary protection measures are in place.
+
+---
+
+## Deployment Documentation
+
+
+
+This project provides two deployment methods. Please choose based on your specific needs:
+
+#### 馃殌 Deployment Method Selection
+| Deployment Method | Features | Applicable Scenarios | Deployment Docs | Configuration Requirements | Video Tutorials |
+|---------|------|---------|---------|---------|---------|
+| **Simplified Installation** | Intelligent dialogue, single agent management | Low-configuration environments, data stored in config files, no database required | [鈶燚ocker Version](../Deployment.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E5%8F%AA%E8%BF%90%E8%A1%8Cserver) / [鈶ource Code Deployment](../Deployment.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E5%8F%AA%E8%BF%90%E8%A1%8Cserver)| 2 cores 4GB if using `FunASR`, 2 cores 2GB if all APIs | - |
+| **Full Module Installation** | Intelligent dialogue, multi-user management, multi-agent management, intelligent console interface operation | Complete functionality experience, data stored in database |[鈶燚ocker Version](../Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [鈶ource Code Deployment](../Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [鈶ource Code Deployment Auto-Update Tutorial](../dev-ops-integration.md) | 4 cores 8GB if using `FunASR`, 2 cores 4GB if all APIs| [Local Source Code Startup Video Tutorial](https://www.bilibili.com/video/BV1wBJhz4Ewe) |
+
+For frequently asked questions and related tutorials, please refer to [this link](../FAQ.md)
+
+> 馃挕 Note: Below is a test platform deployed with the latest code. You can burn and test if needed. Concurrent users: 6, data will be cleared daily.
+
+```
+Intelligent Control Console Address: https://2662r3426b.vicp.fun
+Intelligent Control Console Address (H5): https://2662r3426b.vicp.fun/h5/index.html
+
+Service Test Tool: https://2662r3426b.vicp.fun/test/
+OTA Interface Address: https://2662r3426b.vicp.fun/xiaozhi/ota/
+Websocket Interface Address: wss://2662r3426b.vicp.fun/xiaozhi/v1/
+```
+
+#### 馃毄 Configuration Description and Recommendations
+> [!Note]
+> This project provides two configuration schemes:
+>
+> 1. `Entry Level Free Settings`: Suitable for personal and home use, all components use free solutions, no additional payment required.
+>
+> 2. `Streaming Configuration`: Suitable for demonstrations, training, scenarios with more than 2 concurrent users, etc. Uses streaming processing technology for faster response speed and better experience.
+>
+> Starting from version `0.5.2`, the project supports streaming configuration. Compared to earlier versions, response speed is improved by approximately `2.5 seconds`, significantly improving user experience.
+
+| Module Name | Entry Level Free Settings | Streaming Configuration |
+|:---:|:---:|:---:|
+| ASR(Speech Recognition) | FunASR(Local) | 馃憤XunfeiStreamASR(Xunfei Streaming) |
+| LLM(Large Model) | glm-4-flash(Zhipu) | 馃憤qwen-flash(Alibaba Bailian) |
+| VLLM(Vision Large Model) | glm-4v-flash(Zhipu) | 馃憤qwen3.5-flash(Alibaba Bailian) |
+| TTS(Speech Synthesis) | EdgeTTS(Microsoft) | 馃憤HuoshanDoubleStreamTTS(Volcano Streaming) |
+| Intent(Intent Recognition) | function_call(Function calling) | function_call(Function calling) |
+| Memory(Memory function) | mem_local_short(Local short-term memory) | mem_local_short(Local short-term memory) |
+
+If you are concerned about the latency of each component, please refer to the [Xiaozhi Component Performance Test Report](https://github.com/xinnan-tech/xiaozhi-performance-research), and test in your own environment following the test methods in the report.
+
+#### 馃敡 Testing Tools
+This project provides the following testing tools to help you verify the system and choose suitable models:
+
+| Tool Name | Location | Usage Method | Function Description |
+|:---:|:---|:---:|:---:|
+| Audio Interaction Test Tool | main銆媎igital-human銆媔ndex.html | Run `python start.py` in `main/digital-human`, then open `http://127.0.0.1:8006/index.html` | Tests audio playback and reception functions, verifies if Python-side audio processing is normal |
+| Model Response Test Tool | main銆媥iaozhi-server銆媝erformance_tester.py | Execute `python performance_tester.py` | Tests response speed of three core modules: ASR(speech recognition), LLM(large model), VLLM(vision model), TTS(speech synthesis) |
+
+> 馃挕 Note: When testing model speed, only models with configured keys will be tested.
+
+---
+## Feature List 鉁�
+### Implemented 鉁�
+
+| Feature Module | Description |
+|:---:|:---|
+| Core Architecture | Based on [MQTT+UDP gateway](https://github.com/xinnan-tech/xiaozhi-esp32-server/blob/main/docs/mqtt-gateway-integration.md), WebSocket and HTTP servers, provides complete console management and authentication system |
+| Voice Interaction | Supports streaming ASR(speech recognition), streaming TTS(speech synthesis), VAD(voice activity detection), supports multi-language recognition and voice processing |
+| Voiceprint Recognition | Supports multi-user voiceprint registration, management, and recognition, processes in parallel with ASR, real-time speaker identity recognition and passes to LLM for personalized responses |
+| Intelligent Dialogue | Supports multiple LLM(large language models), implements intelligent dialogue |
+| Visual Perception | Supports multiple VLLM(vision large models), implements multimodal interaction |
+| Intent Recognition | Supports LLM intent recognition, Function Call function calling, provides plugin-based intent processing mechanism |
+| Memory System | Supports local short-term memory, mem0ai interface memory, PowerMem intelligent memory, with memory summarization functionality |
+| Knowledge Base | Supports RAGFlow knowledge base, enabling LLM to judge whether to schedule the knowledge base after receiving the user's question, and then answer the question |
+| Tool Calling | Supports client IOT protocol, client MCP protocol, server MCP protocol, MCP endpoint protocol, custom tool functions |
+| Command Delivery | Supports MCP command delivery to ESP32 devices via MQTT protocol from Smart Console |
+| Management Backend | Provides Web management interface, supports user management, system configuration and device management; Supports Simplified Chinese, Traditional Chinese and English display |
+| Testing Tools | Provides performance testing tools, vision model testing tools, and audio interaction testing tools |
+| Deployment Support | Supports Docker deployment and local deployment, provides complete configuration file management |
+| Plugin System | Supports functional plugin extensions, custom plugin development, and plugin hot-loading |
+
+### Under Development 馃毀
+
+To learn about specific development plan progress, [click here](https://github.com/users/xinnan-tech/projects/3). For frequently asked questions and related tutorials, please refer to [this link](../FAQ.md)
+
+If you are a software developer, here is an [Open Letter to Developers](../contributor_open_letter.md). Welcome to join!
+
+---
+
+## Product Ecosystem 馃懍
+Xiaozhi is an ecosystem. When using this product, you can also check out other [excellent projects](https://github.com/78/xiaozhi-esp32/blob/main/README_zh.md#%E7%9B%B8%E5%85%B3%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE) in this ecosystem
+
+---
+
+## Supported Platforms/Components List 馃搵
+### LLM Language Models
+
+| Usage Method | Supported Platforms | Free Platforms |
+|:---:|:---:|:---:|
+| OpenAI interface calls | Alibaba Bailian, Volcano Engine, DeepSeek, Zhipu, Gemini, iFLYTEK | Zhipu, Gemini |
+| Ollama interface calls | Ollama | - |
+| Dify interface calls | Dify | - |
+| FastGPT interface calls | FastGPT | - |
+| Coze interface calls | Coze | - |
+| Xinference interface calls | Xinference | - |
+| HomeAssistant interface calls | HomeAssistant | - |
+
+In fact, any LLM that supports OpenAI interface calls can be integrated and used.
+
+---
+
+### VLLM Vision Models
+
+| Usage Method | Supported Platforms | Free Platforms |
+|:---:|:---:|:---:|
+| OpenAI interface calls | Alibaba Bailian, Zhipu ChatGLMVLLM | Zhipu ChatGLMVLLM |
+
+In fact, any VLLM that supports OpenAI interface calls can be integrated and used.
+
+---
+
+### TTS Speech Synthesis
+
+| Usage Method | Supported Platforms | Free Platforms |
+|:---:|:---:|:---:|
+| Interface calls | EdgeTTS, iFLYTEK, Volcano Engine, Tencent Cloud, Alibaba Cloud and Bailian, CosyVoiceSiliconflow, TTS302AI, CozeCnTTS, GizwitsTTS, ACGNTTS, OpenAITTS, Lingxi Streaming TTS, MinimaxTTS | Lingxi Streaming TTS, EdgeTTS, CosyVoiceSiliconflow(partial) |
+| Local services | FishSpeech, GPT_SOVITS_V2, GPT_SOVITS_V3, Index-TTS, PaddleSpeech | Index-TTS, PaddleSpeech, FishSpeech, GPT_SOVITS_V2, GPT_SOVITS_V3 |
+
+---
+
+### VAD Voice Activity Detection
+
+| Type | Platform Name | Usage Method | Pricing Model | Notes |
+|:---:|:---------:|:----:|:----:|:--:|
+| VAD | SileroVAD | Local use | Free | |
+
+---
+
+### ASR Speech Recognition
+
+| Usage Method | Supported Platforms | Free Platforms |
+|:---:|:---:|:---:|
+| Local use | FunASR, SherpaASR | FunASR, SherpaASR |
+| Interface calls | FunASRServer, Volcano Engine, iFLYTEK, Tencent Cloud, Alibaba Cloud, Baidu Cloud, OpenAI ASR | FunASRServer |
+
+---
+
+### Voiceprint Recognition
+
+| Usage Method | Supported Platforms | Free Platforms |
+|:---:|:---:|:---:|
+| Local use | 3D-Speaker | 3D-Speaker |
+
+---
+
+### Memory Storage
+
+| Type | Platform Name | Usage Method | Pricing Model | Notes |
+|:------:|:---------------:|:----:|:---------:|:--:|
+| Memory | mem0ai | Interface calls | 1000 times/month quota | |
+| Memory | [powermem](../powermem-integration.md) | Local summarization | Depends on LLM and DB | OceanBase open source, supports intelligent retrieval |
+| Memory | mem_local_short | Local summarization | Free | |
+| Memory | nomem | No memory mode | Free | |
+
+---
+
+### Intent Recognition
+
+| Type | Platform Name | Usage Method | Pricing Model | Notes |
+|:------:|:-------------:|:----:|:-------:|:---------------------:|
+| Intent | intent_llm | Interface calls | Based on LLM pricing | Recognizes intent through large models, strong generalization |
+| Intent | function_call | Interface calls | Based on LLM pricing | Completes intent through large model function calling, fast speed, good effect |
+| Intent | nointent | No intent mode | Free | Does not perform intent recognition, directly returns dialogue result |
+
+---
+
+### Rag Retrieval-Augmented Generation
+
+| Type | Platform Name | Usage Method | Pricing Model | Notes |
+|:------:|:-------------:|:----:|:-------:|:---------------------:|
+| Rag | ragflow | Interface calls | Charged based on tokens consumed for slicing and word segmentation | Utilizes RagFlow's retrieval-augmented generation feature to provide more accurate dialog responses |
+
+---
+
+## Acknowledgments 馃檹
+
+| Logo | Project/Company | Description |
+|:---:|:---:|:---|
+| <img src="../images/logo_bailing.png" width="160"> | [Bailing Voice Dialogue Robot](https://github.com/wwbin2017/bailing) | This project is inspired by [Bailing Voice Dialogue Robot](https://github.com/wwbin2017/bailing) and implemented on its basis |
+| <img src="../images/logo_tenclass.png" width="160"> | [Tenclass](https://www.tenclass.com/) | Thanks to [Tenclass](https://www.tenclass.com/) for formulating standard communication protocols, multi-device compatibility solutions, and high-concurrency scenario practice demonstrations for the Xiaozhi ecosystem; providing full-link technical documentation support for this project |
+| <img src="../images/logo_xuanfeng.png" width="160"> | [Xuanfeng Technology](https://github.com/Eric0308) | Thanks to [Xuanfeng Technology](https://github.com/Eric0308) for contributing function calling framework, MCP communication protocol, and plugin-based calling mechanism implementation code. Through standardized instruction scheduling system and dynamic expansion capabilities, it significantly improves the interaction efficiency and functional extensibility of frontend devices (IoT) |
+| <img src="../images/logo_junsen.png" width="160"> | [huangjunsen](https://github.com/huangjunsen0406) | Thanks to [huangjunsen](https://github.com/huangjunsen0406) for contributing the `Smart Control Console Mobile` module, which enables efficient control and real-time interaction across mobile devices, significantly enhancing the system's operational convenience and management efficiency in mobile scenarios. |
+| <img src="../images/logo_huiyuan.png" width="160"> | [Huiyuan Design](http://ui.kwd988.net/) | Thanks to [Huiyuan Design](http://ui.kwd988.net/) for providing professional visual solutions for this project, using their design practical experience serving over a thousand enterprises to empower this project's product user experience |
+| <img src="../images/logo_qinren.png" width="160"> | [Xi'an Qinren Information Technology](https://www.029app.com/) | Thanks to [Xi'an Qinren Information Technology](https://www.029app.com/) for deepening this project's visual system, ensuring consistency and extensibility of overall design style in multi-scenario applications |
+| <img src="../images/logo_contributors.png" width="160"> | [Code Contributors](https://github.com/xinnan-tech/xiaozhi-esp32-server/graphs/contributors) | Thanks to [all code contributors](https://github.com/xinnan-tech/xiaozhi-esp32-server/graphs/contributors), your efforts have made the project more robust and powerful. |
+
+
+<a href="https://star-history.com/#xinnan-tech/xiaozhi-esp32-server&Date">
+
+ <picture>
+ <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=xinnan-tech/xiaozhi-esp32-server&type=Date&theme=dark" />
+ <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=xinnan-tech/xiaozhi-esp32-server&type=Date" />
+ <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=xinnan-tech/xiaozhi-esp32-server&type=Date" />
+ </picture>
+</a>
diff --git a/xiaozhi-esp32-server/docs/readme/README_pt_BR.md b/xiaozhi-esp32-server/docs/readme/README_pt_BR.md
new file mode 100644
index 0000000..cf5e82a
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/readme/README_pt_BR.md
@@ -0,0 +1,361 @@
+[](https://github.com/xinnan-tech/xiaozhi-esp32-server)
+
+<h1 align="center">Servi莽o Backend Xiaozhi xiaozhi-esp32-server</h1>
+
+<p align="center">
+Este projeto 茅 baseado na teoria e tecnologia de intelig锚ncia simbi贸tica humano-m谩quina para desenvolver sistemas inteligentes de hardware e software para terminais<br/>fornecendo servi莽os de backend para o projeto de hardware inteligente de c贸digo aberto
+<a href="https://github.com/78/xiaozhi-esp32">xiaozhi-esp32</a><br/>
+Implementado usando Python, Java e Vue de acordo com o <a href="https://ccnphfhqs21z.feishu.cn/wiki/M0XiwldO9iJwHikpXD5cEx71nKh">Protocolo de Comunica莽茫o Xiaozhi</a><br/>
+Suporte ao protocolo MQTT+UDP, protocolo WebSocket, ponto de acesso MCP, reconhecimento de impress茫o vocal e base de conhecimento
+</p>
+
+<p align="center">
+<a href="../FAQ.md">Perguntas Frequentes</a>
+路 <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/issues">Reportar Problemas</a>
+路 <a href="../../README.md#%E9%83%A8%E7%BD%B2%E6%96%87%E6%A1%A3">Documenta莽茫o de Implanta莽茫o</a>
+路 <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/releases">Notas de Lan莽amento</a>
+</p>
+
+<p align="center">
+ <a href="../../README.md"><img alt="绠�浣撲腑鏂囩増鑷堪鏂囦欢" src="https://img.shields.io/badge/绠�浣撲腑鏂�-DFE0E5"></a>
+ <a href="./README_en.md"><img alt="README in English" src="https://img.shields.io/badge/English-DFE0E5"></a>
+ <a href="./README_vi.md"><img alt="Ti岷縩g Vi峄噒" src="https://img.shields.io/badge/Ti岷縩g Vi峄噒-DFE0E5"></a>
+ <a href="./README_de.md"><img alt="Deutsch" src="https://img.shields.io/badge/Deutsch-DFE0E5"></a>
+ <a href="./README_pt_BR.md"><img alt="Portugu锚s (Brasil)" src="https://img.shields.io/badge/Portugu锚s (Brasil)-DBEDFA"></a>
+ <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/releases">
+ <img alt="GitHub Contributors" src="https://img.shields.io/github/v/release/xinnan-tech/xiaozhi-esp32-server?logo=docker" />
+ </a>
+ <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/blob/main/LICENSE">
+ <img alt="GitHub pull requests" src="https://img.shields.io/badge/license-MIT-white?labelColor=black" />
+ </a>
+ <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server">
+ <img alt="stars" src="https://img.shields.io/github/stars/xinnan-tech/xiaozhi-esp32-server?color=ffcb47&labelColor=black" />
+ </a>
+</p>
+
+<p align="center">
+Liderado pela Equipe do Professor Siyuan Liu (Universidade de Tecnologia do Sul da China)
+</br>
+鍒樻�濇簮鏁欐巿鍥㈤槦涓诲鐮斿彂锛堝崕鍗楃悊宸ュぇ瀛︼級
+</br>
+<img src="../images/hnlg.jpg" alt="Universidade de Tecnologia do Sul da China (鍗庡崡鐞嗗伐澶у)" width="50%">
+</p>
+
+---
+
+## P煤blico-Alvo 馃懃
+
+Este projeto requer dispositivos de hardware ESP32 para funcionar. Se voc锚 adquiriu hardware relacionado ao ESP32, conectou-se com sucesso ao servi莽o backend implantado pelo Brother Xia e deseja construir seu pr贸prio servi莽o backend `xiaozhi-esp32` de forma independente, ent茫o este projeto 茅 perfeito para voc锚.
+
+Quer ver os efeitos de uso? Clique nos v铆deos abaixo 馃帴
+
+<table>
+ <tr>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1FMFyejExX" target="_blank">
+ <picture>
+ <img alt="鍝嶅簲閫熷害鎰熷彈" src="docs/images/demo9.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1vchQzaEse" target="_blank">
+ <picture>
+ <img alt="閫熷害浼樺寲绉樿瘈" src="docs/images/demo6.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1WEcxzFEAT" target="_blank">
+ <picture>
+ <img alt="灏忔櫤鏁板瓧浜� 鏀寔璇煶鍞ら啋" src="docs/images/demo8.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1CKVz6UEuB" target="_blank">
+ <picture>
+ <img alt="璁惧鍛煎彨璁惧锛屾墦鐢佃瘽" src="docs/images/demo0.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1C1tCzUEZh" target="_blank">
+ <picture>
+ <img alt="澶嶆潅鍖荤枟鍦烘櫙" src="docs/images/demo1.png" /></picture>
+ </a>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1VC96Y5EMH" target="_blank">
+ <picture>
+ <img alt="鎾斁闊充箰鏌ヨ澶╂皵鎾姤鏂伴椈" src="docs/images/demo7.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV12J7WzBEaH" target="_blank">
+ <picture>
+ <img alt="瀹炴椂鎵撴柇" src="docs/images/demo10.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1Co76z7EvK" target="_blank">
+ <picture>
+ <img alt="鎷嶇収璇嗙墿鍝�" src="docs/images/demo12.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1pNXWYGEx1" target="_blank">
+ <picture>
+ <img alt="鎺у埗瀹剁數寮�鍏�" src="docs/images/demo5.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1TJ7WzzEo6" target="_blank">
+ <picture>
+ <img alt="澶氭寚浠や换鍔�" src="docs/images/demo11.png" /></picture>
+ </a>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1ZQKUzYExM" target="_blank">
+ <picture>
+ <img alt="MCP鎺ュ叆鐐�" src="docs/images/demo13.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1zUW5zJEkq" target="_blank">
+ <picture>
+ <img alt="MQTT鎸囦护涓嬪彂" src="docs/images/demo4.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1Exu3zqEDe" target="_blank">
+ <picture>
+ <img alt="澹扮汗璇嗗埆" src="docs/images/demo14.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1CDKWemEU6" target="_blank">
+ <picture>
+ <img alt="鑷畾涔夐煶鑹�" src="docs/images/demo2.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV12yA2egEaC" target="_blank">
+ <picture>
+ <img alt="浣跨敤绮よ浜ゆ祦" src="docs/images/demo3.png" /></picture>
+ </a>
+ </td>
+ </tr>
+</table>
+
+---
+
+## Avisos 鈿狅笍
+
+1. Este projeto 茅 um software de c贸digo aberto. Este software n茫o possui parceria comercial com nenhum provedor de servi莽os de API de terceiros (incluindo, mas n茫o se limitando a reconhecimento de fala, modelos de linguagem, s铆ntese de voz e outras plataformas) com os quais se conecta, e n茫o fornece nenhuma forma de garantia quanto 脿 qualidade de servi莽o ou seguran莽a financeira desses provedores. Recomenda-se que os usu谩rios priorizem provedores de servi莽o com licen莽as comerciais relevantes e leiam cuidadosamente seus termos de servi莽o e pol铆ticas de privacidade. Este software n茫o armazena nenhuma chave de conta, n茫o participa de fluxos de fundos e n茫o assume o risco de perda de fundos recarregados.
+
+2. A funcionalidade deste projeto n茫o est谩 completa e n茫o passou por avalia莽茫o de seguran莽a de rede. Por favor, n茫o o utilize em ambientes de produ莽茫o. Se voc锚 implantar este projeto para fins de aprendizado em um ambiente de rede p煤blica, certifique-se de que as medidas de prote莽茫o necess谩rias estejam em vigor.
+
+---
+
+## Documenta莽茫o de Implanta莽茫o
+
+
+
+Este projeto oferece dois m茅todos de implanta莽茫o. Por favor, escolha de acordo com suas necessidades espec铆ficas:
+
+#### 馃殌 Sele莽茫o do M茅todo de Implanta莽茫o
+| M茅todo de Implanta莽茫o | Funcionalidades | Cen谩rios Aplic谩veis | Documenta莽茫o de Implanta莽茫o | Requisitos de Configura莽茫o | Tutoriais em V铆deo |
+|---------|------|---------|---------|---------|---------|
+| **Instala莽茫o Simplificada** | Di谩logo inteligente, gerenciamento de agente 煤nico | Ambientes de baixa configura莽茫o, dados armazenados em arquivos de configura莽茫o, sem necessidade de banco de dados | [鈶燰ers茫o Docker](../Deployment.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E5%8F%AA%E8%BF%90%E8%A1%8Cserver) / [鈶mplanta莽茫o via C贸digo-Fonte](../Deployment.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E5%8F%AA%E8%BF%90%E8%A1%8Cserver)| 2 n煤cleos 4GB se usar `FunASR`, 2 n煤cleos 2GB se todas APIs | - |
+| **Instala莽茫o de M贸dulo Completo** | Di谩logo inteligente, gerenciamento multiusu谩rio, gerenciamento de m煤ltiplos agentes, opera莽茫o de interface do console inteligente | Experi锚ncia com funcionalidade completa, dados armazenados em banco de dados |[鈶燰ers茫o Docker](../Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [鈶mplanta莽茫o via C贸digo-Fonte](../Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [鈶utorial de Atualiza莽茫o Autom谩tica via C贸digo-Fonte](../dev-ops-integration.md) | 4 n煤cleos 8GB se usar `FunASR`, 2 n煤cleos 4GB se todas APIs| [Tutorial em V铆deo de Inicializa莽茫o via C贸digo-Fonte Local](https://www.bilibili.com/video/BV1wBJhz4Ewe) |
+
+Perguntas frequentes e tutoriais relacionados podem ser consultados [neste link](../FAQ.md)
+
+> 馃挕 Nota: Abaixo est谩 uma plataforma de teste implantada com o c贸digo mais recente. Voc锚 pode gravar e testar se necess谩rio. Usu谩rios simult芒neos: 6, os dados ser茫o limpos diariamente.
+
+```
+Endere莽o do Console de Controle Inteligente: https://2662r3426b.vicp.fun
+Endere莽o do Console de Controle Inteligente (H5): https://2662r3426b.vicp.fun/h5/index.html
+
+Ferramenta de Teste de Servi莽o: https://2662r3426b.vicp.fun/test/
+Endere莽o da Interface OTA: https://2662r3426b.vicp.fun/xiaozhi/ota/
+Endere莽o da Interface WebSocket: wss://2662r3426b.vicp.fun/xiaozhi/v1/
+```
+
+#### 馃毄 Descri莽茫o e Recomenda莽玫es de Configura莽茫o
+> [!Note]
+> Este projeto oferece dois esquemas de configura莽茫o:
+>
+> 1. `Configura莽玫es Gratuitas N铆vel B谩sico`: Adequado para uso pessoal e dom茅stico, todos os componentes utilizam solu莽玫es gratuitas, sem necessidade de pagamento adicional.
+>
+> 2. `Configura莽茫o de Streaming`: Adequado para demonstra莽玫es, treinamentos, cen谩rios com mais de 2 usu谩rios simult芒neos, etc. Utiliza tecnologia de processamento em streaming para velocidade de resposta mais r谩pida e melhor experi锚ncia.
+>
+> A partir da vers茫o `0.5.2`, o projeto suporta configura莽茫o de streaming. Em compara莽茫o com vers玫es anteriores, a velocidade de resposta 茅 melhorada em aproximadamente `2,5 segundos`, melhorando significativamente a experi锚ncia do usu谩rio.
+
+| Nome do M贸dulo | Configura莽玫es Gratuitas N铆vel B谩sico | Configura莽茫o de Streaming |
+|:---:|:---:|:---:|
+| ASR(Reconhecimento de Fala) | FunASR(Local) | 馃憤XunfeiStreamASR(Xunfei Streaming) |
+| LLM(Modelo de Linguagem) | glm-4-flash(Zhipu) | 馃憤qwen-flash(Alibaba Bailian) |
+| VLLM(Modelo de Vis茫o) | glm-4v-flash(Zhipu) | 馃憤qwen3.5-flash(Alibaba Bailian) |
+| TTS(S铆ntese de Voz) | EdgeTTS(Microsoft) | 馃憤HuoshanDoubleStreamTTS(Volcano Streaming) |
+| Intent(Reconhecimento de Inten莽茫o) | function_call(Chamada de fun莽茫o) | function_call(Chamada de fun莽茫o) |
+| Memory(Fun莽茫o de Mem贸ria) | mem_local_short(Mem贸ria local de curto prazo) | mem_local_short(Mem贸ria local de curto prazo) |
+
+Se voc锚 est谩 preocupado com o tempo de resposta de cada componente, consulte o [Relat贸rio de Teste de Desempenho dos Componentes Xiaozhi](https://github.com/xinnan-tech/xiaozhi-performance-research), e teste em seu pr贸prio ambiente seguindo os m茅todos de teste do relat贸rio.
+
+#### 馃敡 Ferramentas de Teste
+Este projeto fornece as seguintes ferramentas de teste para ajud谩-lo a verificar o sistema e escolher modelos adequados:
+
+| Nome da Ferramenta | Localiza莽茫o | M茅todo de Uso | Descri莽茫o da Fun莽茫o |
+|:---:|:---|:---:|:---:|
+| Ferramenta de Teste de Intera莽茫o por 脕udio | main銆媎igital-human銆媔ndex.html | Execute `python start.py` em `main/digital-human` e depois abra `http://127.0.0.1:8006/index.html` | Testa as fun莽玫es de reprodu莽茫o e recep莽茫o de 谩udio, verifica se o processamento de 谩udio no lado Python est谩 normal |
+| Ferramenta de Teste de Resposta de Modelo | main銆媥iaozhi-server銆媝erformance_tester.py | Execute `python performance_tester.py` | Testa a velocidade de resposta dos tr锚s m贸dulos principais: ASR(reconhecimento de fala), LLM(modelo de linguagem), VLLM(modelo de vis茫o), TTS(s铆ntese de voz) |
+
+> 馃挕 Nota: Ao testar a velocidade dos modelos, apenas os modelos com chaves configuradas ser茫o testados.
+
+---
+## Lista de Funcionalidades 鉁�
+### Implementado 鉁�
+
+| M贸dulo de Funcionalidade | Descri莽茫o |
+|:---:|:---|
+| Arquitetura Principal | Baseado em [gateway MQTT+UDP](https://github.com/xinnan-tech/xiaozhi-esp32-server/blob/main/docs/mqtt-gateway-integration.md), servidores WebSocket e HTTP, fornece sistema completo de gerenciamento de console e autentica莽茫o |
+| Intera莽茫o por Voz | Suporta ASR em streaming (reconhecimento de fala), TTS em streaming (s铆ntese de voz), VAD (detec莽茫o de atividade vocal), suporta reconhecimento multil铆ngue e processamento de voz |
+| Reconhecimento de Impress茫o Vocal | Suporta registro, gerenciamento e reconhecimento de impress茫o vocal de m煤ltiplos usu谩rios, processa em paralelo com o ASR, reconhecimento de identidade do falante em tempo real e repassa ao LLM para respostas personalizadas |
+| Di谩logo Inteligente | Suporta m煤ltiplos LLM (modelos de linguagem de grande porte), implementa di谩logo inteligente |
+| Percep莽茫o Visual | Suporta m煤ltiplos VLLM (modelos de vis茫o de grande porte), implementa intera莽茫o multimodal |
+| Reconhecimento de Inten莽茫o | Suporta reconhecimento de inten莽茫o por LLM, Function Call (chamada de fun莽茫o), fornece mecanismo de processamento de inten莽茫o baseado em plugins |
+| Sistema de Mem贸ria | Suporta mem贸ria local de curto prazo, mem贸ria via interface mem0ai, mem贸ria inteligente PowerMem, com funcionalidade de resumo de mem贸ria |
+| Base de Conhecimento | Suporta base de conhecimento RAGFlow, permitindo que o LLM julgue se deve acionar a base de conhecimento ap贸s receber a pergunta do usu谩rio, e ent茫o responda 脿 pergunta |
+| Chamada de Ferramentas | Suporta protocolo IOT do cliente, protocolo MCP do cliente, protocolo MCP do servidor, protocolo de endpoint MCP, fun莽玫es de ferramentas personalizadas |
+| Envio de Comandos | Suporta envio de comandos MCP para dispositivos ESP32 via protocolo MQTT a partir do Console Inteligente |
+| Backend de Gerenciamento | Fornece interface de gerenciamento Web, suporta gerenciamento de usu谩rios, configura莽茫o do sistema e gerenciamento de dispositivos; Suporta exibi莽茫o em Chin锚s Simplificado, Chin锚s Tradicional e Ingl锚s |
+| Ferramentas de Teste | Fornece ferramentas de teste de desempenho, ferramentas de teste de modelo de vis茫o e ferramentas de teste de intera莽茫o por 谩udio |
+| Suporte 脿 Implanta莽茫o | Suporta implanta莽茫o via Docker e implanta莽茫o local, fornece gerenciamento completo de arquivos de configura莽茫o |
+| Sistema de Plugins | Suporta extens玫es de plugins funcionais, desenvolvimento de plugins personalizados e carregamento din芒mico de plugins |
+
+### Em Desenvolvimento 馃毀
+
+Para conhecer o progresso espec铆fico do plano de desenvolvimento, [clique aqui](https://github.com/users/xinnan-tech/projects/3). Perguntas frequentes e tutoriais relacionados podem ser consultados [neste link](../FAQ.md)
+
+Se voc锚 茅 um desenvolvedor de software, aqui est谩 uma [Carta Aberta aos Desenvolvedores](../contributor_open_letter.md). Seja bem-vindo a participar!
+
+---
+
+## Ecossistema do Produto 馃懍
+Xiaozhi 茅 um ecossistema. Ao utilizar este produto, voc锚 tamb茅m pode conferir outros [projetos excelentes](https://github.com/78/xiaozhi-esp32/blob/main/README_zh.md#%E7%9B%B8%E5%85%B3%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE) neste ecossistema
+
+---
+
+## Lista de Plataformas/Componentes Suportados 馃搵
+### LLM Modelos de Linguagem
+
+| M茅todo de Uso | Plataformas Suportadas | Plataformas Gratuitas |
+|:---:|:---:|:---:|
+| Chamadas via interface OpenAI | Alibaba Bailian, Volcano Engine, DeepSeek, Zhipu, Gemini, iFLYTEK | Zhipu, Gemini |
+| Chamadas via interface Ollama | Ollama | - |
+| Chamadas via interface Dify | Dify | - |
+| Chamadas via interface FastGPT | FastGPT | - |
+| Chamadas via interface Coze | Coze | - |
+| Chamadas via interface Xinference | Xinference | - |
+| Chamadas via interface HomeAssistant | HomeAssistant | - |
+
+Na verdade, qualquer LLM que suporte chamadas via interface openai pode ser integrado e utilizado.
+
+---
+
+### VLLM Modelos de Vis茫o
+
+| M茅todo de Uso | Plataformas Suportadas | Plataformas Gratuitas |
+|:---:|:---:|:---:|
+| Chamadas via interface OpenAI | Alibaba Bailian, Zhipu ChatGLMVLLM | Zhipu ChatGLMVLLM |
+
+Na verdade, qualquer VLLM que suporte chamadas via interface OpenAI pode ser integrado e utilizado.
+
+---
+
+### TTS S铆ntese de Voz
+
+| M茅todo de Uso | Plataformas Suportadas | Plataformas Gratuitas |
+|:---:|:---:|:---:|
+| Chamadas via interface | EdgeTTS, iFLYTEK, Volcano Engine, Tencent Cloud, Alibaba Cloud e Bailian, CosyVoiceSiliconflow, TTS302AI, CozeCnTTS, GizwitsTTS, ACGNTTS, OpenAITTS, Lingxi Streaming TTS, MinimaxTTS | Lingxi Streaming TTS, EdgeTTS, CosyVoiceSiliconflow(parcial) |
+| Servi莽os locais | FishSpeech, GPT_SOVITS_V2, GPT_SOVITS_V3, Index-TTS, PaddleSpeech | Index-TTS, PaddleSpeech, FishSpeech, GPT_SOVITS_V2, GPT_SOVITS_V3 |
+
+---
+
+### VAD Detec莽茫o de Atividade Vocal
+
+| Tipo | Nome da Plataforma | M茅todo de Uso | Modelo de Pre莽o | Observa莽玫es |
+|:---:|:---------:|:----:|:----:|:--:|
+| VAD | SileroVAD | Uso local | Gratuito | |
+
+---
+
+### ASR Reconhecimento de Fala
+
+| M茅todo de Uso | Plataformas Suportadas | Plataformas Gratuitas |
+|:---:|:---:|:---:|
+| Uso local | FunASR, SherpaASR | FunASR, SherpaASR |
+| Chamadas via interface | FunASRServer, Volcano Engine, iFLYTEK, Tencent Cloud, Alibaba Cloud, Baidu Cloud, OpenAI ASR | FunASRServer |
+
+---
+
+### Reconhecimento de Impress茫o Vocal
+
+| M茅todo de Uso | Plataformas Suportadas | Plataformas Gratuitas |
+|:---:|:---:|:---:|
+| Uso local | 3D-Speaker | 3D-Speaker |
+
+---
+
+### Armazenamento de Mem贸ria
+
+| Tipo | Nome da Plataforma | M茅todo de Uso | Modelo de Pre莽o | Observa莽玫es |
+|:------:|:---------------:|:----:|:---------:|:--:|
+| Mem贸ria | mem0ai | Chamadas via interface | Cota de 1000 vezes/m锚s | |
+| Mem贸ria | [powermem](../powermem-integration.md) | Resumo local | Depende do LLM e BD | OceanBase de c贸digo aberto, suporta busca inteligente |
+| Mem贸ria | mem_local_short | Resumo local | Gratuito | |
+| Mem贸ria | nomem | Modo sem mem贸ria | Gratuito | |
+
+---
+
+### Reconhecimento de Inten莽茫o
+
+| Tipo | Nome da Plataforma | M茅todo de Uso | Modelo de Pre莽o | Observa莽玫es |
+|:------:|:-------------:|:----:|:-------:|:---------------------:|
+| Inten莽茫o | intent_llm | Chamadas via interface | Baseado no pre莽o do LLM | Reconhece inten莽茫o atrav茅s de modelos de linguagem, forte generaliza莽茫o |
+| Inten莽茫o | function_call | Chamadas via interface | Baseado no pre莽o do LLM | Completa a inten莽茫o atrav茅s de chamada de fun莽茫o do modelo de linguagem, velocidade r谩pida, bom resultado |
+| Inten莽茫o | nointent | Modo sem inten莽茫o | Gratuito | N茫o realiza reconhecimento de inten莽茫o, retorna diretamente o resultado do di谩logo |
+
+---
+
+### RAG Gera莽茫o Aumentada por Recupera莽茫o
+
+| Tipo | Nome da Plataforma | M茅todo de Uso | Modelo de Pre莽o | Observa莽玫es |
+|:------:|:-------------:|:----:|:-------:|:---------------------:|
+| RAG | ragflow | Chamadas via interface | Cobrado com base nos tokens consumidos para fatiamento e segmenta莽茫o de palavras | Utiliza o recurso de gera莽茫o aumentada por recupera莽茫o do RagFlow para fornecer respostas de di谩logo mais precisas |
+
+---
+
+## Agradecimentos 馃檹
+
+| Logo | Projeto/Empresa | Descri莽茫o |
+|:---:|:---:|:---|
+| <img src="../images/logo_bailing.png" width="160"> | [Rob么 de Di谩logo por Voz Bailing](https://github.com/wwbin2017/bailing) | Este projeto foi inspirado pelo [Rob么 de Di谩logo por Voz Bailing](https://github.com/wwbin2017/bailing) e implementado com base nele |
+| <img src="../images/logo_tenclass.png" width="160"> | [Tenclass](https://www.tenclass.com/) | Agradecimentos 脿 [Tenclass](https://www.tenclass.com/) por formular protocolos de comunica莽茫o padr茫o, solu莽玫es de compatibilidade multidispositivo e demonstra莽玫es pr谩ticas de cen谩rios de alta concorr锚ncia para o ecossistema Xiaozhi; fornecendo suporte completo de documenta莽茫o t茅cnica para este projeto |
+| <img src="../images/logo_xuanfeng.png" width="160"> | [Xuanfeng Technology (鐜勫嚖绉戞妧)](https://github.com/Eric0308) | Agradecimentos 脿 [Xuanfeng Technology](https://github.com/Eric0308) por contribuir com o framework de chamada de fun莽茫o, protocolo de comunica莽茫o MCP e implementa莽茫o do mecanismo de chamada baseado em plugins. Atrav茅s de um sistema padronizado de agendamento de instru莽玫es e capacidades de expans茫o din芒mica, melhora significativamente a efici锚ncia de intera莽茫o e extensibilidade funcional dos dispositivos de frontend (IoT) |
+| <img src="../images/logo_junsen.png" width="160"> | [huangjunsen](https://github.com/huangjunsen0406) | Agradecimentos a [huangjunsen](https://github.com/huangjunsen0406) por contribuir com o m贸dulo `Console de Controle Inteligente Mobile`, que permite controle eficiente e intera莽茫o em tempo real em dispositivos m贸veis, melhorando significativamente a conveni锚ncia operacional e a efici锚ncia de gerenciamento do sistema em cen谩rios m贸veis. |
+| <img src="../images/logo_huiyuan.png" width="160"> | [Huiyuan Design (姹囪繙璁捐)](http://ui.kwd988.net/) | Agradecimentos 脿 [Huiyuan Design](http://ui.kwd988.net/) por fornecer solu莽玫es visuais profissionais para este projeto, utilizando sua experi锚ncia pr谩tica de design atendendo mais de mil empresas para potencializar a experi锚ncia do usu谩rio deste produto |
+| <img src="../images/logo_qinren.png" width="160"> | [Xi'an Qinren Information Technology (瑗垮畨鍕や汉淇℃伅绉戞妧)](https://www.029app.com/) | Agradecimentos 脿 [Xi'an Qinren Information Technology](https://www.029app.com/) por aprofundar o sistema visual deste projeto, garantindo consist锚ncia e extensibilidade do estilo de design geral em aplica莽玫es de m煤ltiplos cen谩rios |
+| <img src="../images/logo_contributors.png" width="160"> | [Contribuidores de C贸digo](https://github.com/xinnan-tech/xiaozhi-esp32-server/graphs/contributors) | Agradecimentos a [todos os contribuidores de c贸digo](https://github.com/xinnan-tech/xiaozhi-esp32-server/graphs/contributors), seus esfor莽os tornaram o projeto mais robusto e poderoso. |
+
+
+<a href="https://star-history.com/#xinnan-tech/xiaozhi-esp32-server&Date">
+
+ <picture>
+ <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=xinnan-tech/xiaozhi-esp32-server&type=Date&theme=dark" />
+ <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=xinnan-tech/xiaozhi-esp32-server&type=Date" />
+ <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=xinnan-tech/xiaozhi-esp32-server&type=Date" />
+ </picture>
+</a>
diff --git a/xiaozhi-esp32-server/docs/readme/README_vi.md b/xiaozhi-esp32-server/docs/readme/README_vi.md
new file mode 100644
index 0000000..e3317ab
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/readme/README_vi.md
@@ -0,0 +1,362 @@
+[](https://github.com/xinnan-tech/xiaozhi-esp32-server)
+
+<h1 align="center">D峄媍h v峄� Backend Xiaozhi xiaozhi-esp32-server</h1>
+
+<p align="center">
+D峄� 谩n n脿y d峄盿 tr锚n l媒 thuy岷縯 v脿 c么ng ngh峄� tr铆 tu峄� c峄檔g sinh ng瓢峄漣-m谩y 膽峄� ph谩t tri峄僴 h峄� th峄憂g ph岷 m峄乵 v脿 ph岷 c峄﹏g thi岷縯 b峄� 膽岷 cu峄慽 th么ng minh<br/>Cung c岷 d峄媍h v峄� backend cho d峄� 谩n ph岷 c峄﹏g th么ng minh m茫 ngu峄搉 m峄�
+<a href="https://github.com/78/xiaozhi-esp32">xiaozhi-esp32</a><br/>
+膼瓢峄 tri峄僴 khai b岷眓g Python, Java, Vue theo <a href="https://ccnphfhqs21z.feishu.cn/wiki/M0XiwldO9iJwHikpXD5cEx71nKh">giao th峄ヽ truy峄乶 th么ng Xiaozhi</a><br/>
+H峄� tr峄� giao th峄ヽ MQTT+UDP, giao th峄ヽ Websocket, 膽i峄僲 truy c岷璸 MCP, nh岷璶 d岷g gi峄峮g n贸i v脿 kho tri th峄ヽ
+</p>
+
+<p align="center">
+<a href="../FAQ.md">C芒u h峄廼 th瓢峄漬g g岷穚</a>
+路 <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/issues">B谩o c谩o v岷 膽峄�</a>
+路 <a href="../../README.md#%E9%83%A8%E7%BD%B2%E6%96%87%E6%A1%A3">T脿i li峄噓 tri峄僴 khai</a>
+路 <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/releases">Nh岷璽 k媒 c岷璸 nh岷璽</a>
+</p>
+
+<p align="center">
+ <a href="../../README.md"><img alt="绠�浣撲腑鏂囩増鑷堪鏂囦欢" src="https://img.shields.io/badge/绠�浣撲腑鏂�-DFE0E5"></a>
+ <a href="./README_en.md"><img alt="README in English" src="https://img.shields.io/badge/English-DFE0E5"></a>
+ <a href="./README_vi.md"><img alt="Ti岷縩g Vi峄噒" src="https://img.shields.io/badge/Ti岷縩g Vi峄噒-DBEDFA"></a>
+ <a href="./README_de.md"><img alt="Deutsch" src="https://img.shields.io/badge/Deutsch-DFE0E5"></a>
+ <a href="./README_pt_BR.md"><img alt="Portugu锚s (Brasil)" src="https://img.shields.io/badge/Portugu锚s (Brasil)-DFE0E5"></a>
+ <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/releases">
+ <img alt="GitHub Contributors" src="https://img.shields.io/github/v/release/xinnan-tech/xiaozhi-esp32-server?logo=docker" />
+ </a>
+ <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/blob/main/LICENSE">
+ <img alt="GitHub pull requests" src="https://img.shields.io/badge/license-MIT-white?labelColor=black" />
+ </a>
+ <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server">
+ <img alt="stars" src="https://img.shields.io/github/stars/xinnan-tech/xiaozhi-esp32-server?color=ffcb47&labelColor=black" />
+ </a>
+</p>
+
+<p align="center">
+Spearheaded by Professor Siyuan Liu's Team (South China University of Technology)
+</br>
+膼瓢峄 d岷玭 d岷痶 b峄焛 nh贸m Gi谩o s瓢 L瓢u T瓢 Nguy锚n (膼岷 h峄峜 B谩ch khoa Nam Trung Qu峄慶)
+</br>
+<img src="../images/hnlg.jpg" alt="鍗庡崡鐞嗗伐澶у" width="50%">
+</p>
+
+---
+
+## Ng瓢峄漣 d霉ng ph霉 h峄 馃懃
+
+D峄� 谩n n脿y c岷 膽瓢峄 s峄� d峄g c霉ng v峄沬 thi岷縯 b峄� ph岷 c峄﹏g ESP32. N岷縰 b岷 膽茫 mua ph岷 c峄﹏g li锚n quan 膽岷縩 ESP32, 膽茫 th脿nh c么ng k岷縯 n峄慽 v峄沬 d峄媍h v峄� backend do anh Xia tri峄僴 khai, v脿 mu峄憂 x芒y d峄眓g d峄媍h v峄� backend `xiaozhi-esp32` ri锚ng c峄 m矛nh, th矛 d峄� 谩n n脿y r岷 ph霉 h峄 v峄沬 b岷.
+
+Mu峄憂 xem hi峄噓 qu岷� s峄� d峄g? H茫y xem video 馃帴
+
+<table>
+ <tr>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1FMFyejExX" target="_blank">
+ <picture>
+ <img alt="鍝嶅簲閫熷害鎰熷彈" src="docs/images/demo9.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1vchQzaEse" target="_blank">
+ <picture>
+ <img alt="閫熷害浼樺寲绉樿瘈" src="docs/images/demo6.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1WEcxzFEAT" target="_blank">
+ <picture>
+ <img alt="灏忔櫤鏁板瓧浜� 鏀寔璇煶鍞ら啋" src="docs/images/demo8.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1CKVz6UEuB" target="_blank">
+ <picture>
+ <img alt="璁惧鍛煎彨璁惧锛屾墦鐢佃瘽" src="docs/images/demo0.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1C1tCzUEZh" target="_blank">
+ <picture>
+ <img alt="澶嶆潅鍖荤枟鍦烘櫙" src="docs/images/demo1.png" /></picture>
+ </a>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1VC96Y5EMH" target="_blank">
+ <picture>
+ <img alt="鎾斁闊充箰鏌ヨ澶╂皵鎾姤鏂伴椈" src="docs/images/demo7.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV12J7WzBEaH" target="_blank">
+ <picture>
+ <img alt="瀹炴椂鎵撴柇" src="docs/images/demo10.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1Co76z7EvK" target="_blank">
+ <picture>
+ <img alt="鎷嶇収璇嗙墿鍝�" src="docs/images/demo12.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1pNXWYGEx1" target="_blank">
+ <picture>
+ <img alt="鎺у埗瀹剁數寮�鍏�" src="docs/images/demo5.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1TJ7WzzEo6" target="_blank">
+ <picture>
+ <img alt="澶氭寚浠や换鍔�" src="docs/images/demo11.png" /></picture>
+ </a>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1ZQKUzYExM" target="_blank">
+ <picture>
+ <img alt="MCP鎺ュ叆鐐�" src="docs/images/demo13.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1zUW5zJEkq" target="_blank">
+ <picture>
+ <img alt="MQTT鎸囦护涓嬪彂" src="docs/images/demo4.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1Exu3zqEDe" target="_blank">
+ <picture>
+ <img alt="澹扮汗璇嗗埆" src="docs/images/demo14.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV1CDKWemEU6" target="_blank">
+ <picture>
+ <img alt="鑷畾涔夐煶鑹�" src="docs/images/demo2.png" /></picture>
+ </a>
+ </td>
+ <td>
+ <a href="https://www.bilibili.com/video/BV12yA2egEaC" target="_blank">
+ <picture>
+ <img alt="浣跨敤绮よ浜ゆ祦" src="docs/images/demo3.png" /></picture>
+ </a>
+ </td>
+ </tr>
+</table>
+
+---
+
+## C岷h b谩o 鈿狅笍
+
+1. D峄� 谩n n脿y l脿 ph岷 m峄乵 m茫 ngu峄搉 m峄�, ph岷 m峄乵 n脿y kh么ng c贸 quan h峄� h峄 t谩c th瓢啤ng m岷 v峄沬 b岷 k峄� nh脿 cung c岷 d峄媍h v峄� API b锚n th峄� ba n脿o (bao g峄搈 nh瓢ng kh么ng gi峄沬 h岷 峄� c谩c n峄乶 t岷g nh岷璶 d岷g gi峄峮g n贸i, m么 h矛nh l峄沶, t峄昻g h峄 gi峄峮g n贸i, v.v.), v脿 kh么ng 膽岷 b岷 ch岷 l瓢峄g d峄媍h v峄� c农ng nh瓢 an to脿n t脿i ch铆nh c峄 h峄�.
+Khuy岷縩 ngh峄� ng瓢峄漣 d霉ng 瓢u ti锚n l峄盿 ch峄峮 nh脿 cung c岷 d峄媍h v峄� c贸 gi岷 ph茅p kinh doanh li锚n quan v脿 膽峄峜 k峄� th峄廰 thu岷璶 d峄媍h v峄� v脿 ch铆nh s谩ch b岷 m岷璽 c峄 h峄�. Ph岷 m峄乵 n脿y kh么ng l瓢u tr峄� b岷 k峄� kh贸a t脿i kho岷 n脿o, kh么ng tham gia v脿o lu峄搉g ti峄乶 v脿 kh么ng ch峄媢 r峄 ro m岷 ti峄乶 n岷.
+
+2. Ch峄ヽ n膬ng c峄 d峄� 谩n n脿y ch瓢a ho脿n thi峄噉 v脿 ch瓢a qua 膽谩nh gi谩 b岷 m岷璽 m岷g, vui l貌ng kh么ng s峄� d峄g trong m么i tr瓢峄漬g s岷 xu岷. N岷縰 b岷 tri峄僴 khai d峄� 谩n n脿y trong m么i tr瓢峄漬g m岷g c么ng c峄檔g 膽峄� h峄峜 t岷璸, vui l貌ng th峄眂 hi峄噉 c谩c bi峄噉 ph谩p b岷 v峄� c岷 thi岷縯.
+
+---
+
+## T脿i li峄噓 tri峄僴 khai
+
+
+
+D峄� 谩n n脿y cung c岷 hai ph瓢啤ng ph谩p tri峄僴 khai, vui l貌ng ch峄峮 theo nhu c岷 c峄� th峄� c峄 b岷:
+
+#### 馃殌 L峄盿 ch峄峮 ph瓢啤ng ph谩p tri峄僴 khai
+| Ph瓢啤ng ph谩p tri峄僴 khai | 膼岷穋 膽i峄僲 | T矛nh hu峄憂g 谩p d峄g | T脿i li峄噓 tri峄僴 khai | Y锚u c岷 c岷 h矛nh | Video h瓢峄沶g d岷玭 |
+|---------|------|---------|---------|---------|---------|
+| **C脿i 膽岷穞 t峄慽 gi岷** | 膼峄慽 tho岷 th么ng minh, qu岷 l媒 膽啤n t谩c nh芒n | M么i tr瓢峄漬g c岷 h矛nh th岷, d峄� li峄噓 l瓢u trong t峄噋 c岷 h矛nh, kh么ng c岷 c啤 s峄� d峄� li峄噓 | [鈶燩hi锚n b岷 Docker](../Deployment.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E5%8F%AA%E8%BF%90%E8%A1%8Cserver) / [鈶ri峄僴 khai m茫 ngu峄搉](../Deployment.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E5%8F%AA%E8%BF%90%E8%A1%8Cserver)| 2 nh芒n 4GB n岷縰 d霉ng `FunASR`, 2 nh芒n 2GB n岷縰 to脿n API | - |
+| **C脿i 膽岷穞 to脿n b峄� module** | 膼峄慽 tho岷 th么ng minh, qu岷 l媒 膽a ng瓢峄漣 d霉ng, qu岷 l媒 膽a t谩c nh芒n, b岷g 膽i峄乽 khi峄僴 th么ng minh | Tr岷 nghi峄噈 膽岷 膽峄� t铆nh n膬ng, d峄� li峄噓 l瓢u trong c啤 s峄� d峄� li峄噓 |[鈶燩hi锚n b岷 Docker](../Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [鈶ri峄僴 khai m茫 ngu峄搉](../Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [鈶瓢峄沶g d岷玭 t峄� 膽峄檔g c岷璸 nh岷璽 tri峄僴 khai m茫 ngu峄搉](../dev-ops-integration.md) | 4 nh芒n 8GB n岷縰 d霉ng `FunASR`, 2 nh芒n 4GB n岷縰 to脿n API| [Video h瓢峄沶g d岷玭 kh峄焛 膽峄檔g m茫 ngu峄搉 c峄 b峄橾(https://www.bilibili.com/video/BV1wBJhz4Ewe) |
+
+C芒u h峄廼 th瓢峄漬g g岷穚 v脿 h瓢峄沶g d岷玭 li锚n quan, vui l貌ng tham kh岷 [li锚n k岷縯 n脿y](../FAQ.md)
+
+> 馃挕 G峄 媒: D瓢峄沬 膽芒y l脿 n峄乶 t岷g th峄� nghi峄噈 膽瓢峄 tri峄僴 khai theo m茫 m峄沬 nh岷, c贸 th峄� flash 膽峄� th峄� nghi峄噈 n岷縰 c岷, 膽峄搉g th峄漣 l脿 6, d峄� li峄噓 s岷� 膽瓢峄 x贸a m峄梚 ng脿y,
+
+```
+膼峄媋 ch峄� b岷g 膽i峄乽 khi峄僴 th么ng minh: https://2662r3426b.vicp.fun
+B岷g 膽i峄乽 khi峄僴 th么ng minh (phi锚n b岷 h5): https://2662r3426b.vicp.fun/h5/index.html
+
+C么ng c峄� ki峄僲 tra d峄媍h v峄�: https://2662r3426b.vicp.fun/test/
+膼峄媋 ch峄� giao di峄噉 OTA: https://2662r3426b.vicp.fun/xiaozhi/ota/
+膼峄媋 ch峄� giao di峄噉 Websocket: wss://2662r3426b.vicp.fun/xiaozhi/v1/
+```
+
+#### 馃毄 M么 t岷� v脿 khuy岷縩 ngh峄� c岷 h矛nh
+> [!Note]
+> D峄� 谩n n脿y cung c岷 hai ph瓢啤ng 谩n c岷 h矛nh:
+>
+> 1. C岷 h矛nh `Mi峄卬 ph铆 ho脿n to脿n cho ng瓢峄漣 m峄沬`: Ph霉 h峄 v峄沬 s峄� d峄g gia 膽矛nh c谩 nh芒n, t岷 c岷� c谩c th脿nh ph岷 膽峄乽 s峄� d峄g ph瓢啤ng 谩n mi峄卬 ph铆, kh么ng c岷 thanh to谩n th锚m.
+>
+> 2. `C岷 h矛nh streaming`: Ph霉 h峄 v峄沬 demo, 膽脿o t岷, h啤n 2 膽峄搉g th峄漣, v.v., s峄� d峄g c么ng ngh峄� x峄� l媒 streaming, t峄慶 膽峄� ph岷 h峄搃 nhanh h啤n, tr岷 nghi峄噈 t峄憈 h啤n.
+>
+> T峄� phi锚n b岷 `0.5.2`, d峄� 谩n h峄� tr峄� c岷 h矛nh streaming, so v峄沬 phi锚n b岷 膽岷, t峄慶 膽峄� ph岷 h峄搃 c岷 thi峄噉 kho岷g `2.5 gi芒y`, c岷 thi峄噉 膽谩ng k峄� tr岷 nghi峄噈 ng瓢峄漣 d霉ng.
+
+| T锚n module | C脿i 膽岷穞 mi峄卬 ph铆 cho ng瓢峄漣 m峄沬 | C岷 h矛nh streaming |
+|:---:|:---:|:---:|
+| ASR(Nh岷璶 d岷g gi峄峮g n贸i) | FunASR(Local) | 馃憤XunfeiStreamASR(Xunfei Streaming) |
+| LLM(M么 h矛nh l峄沶) | glm-4-flash(Zhipu) | 馃憤qwen-flash(Alibaba Bailian) |
+| VLLM(M么 h矛nh l峄沶 th峄� gi谩c) | glm-4v-flash(Zhipu) | 馃憤qwen3.5-flash(Alibaba Bailian) |
+| TTS(T峄昻g h峄 gi峄峮g n贸i) | EdgeTTS(Microsoft) | 馃憤HuoshanDoubleStreamTTS(Volcano Streaming) |
+| Intent(Nh岷璶 d岷g 媒 膽峄媙h) | function_call(G峄峣 h脿m) | function_call(G峄峣 h脿m) |
+| Memory(Ch峄ヽ n膬ng b峄� nh峄�) | mem_local_short(B峄� nh峄� ng岷痭 h岷 c峄 b峄�) | mem_local_short(B峄� nh峄� ng岷痭 h岷 c峄 b峄�) |
+
+N岷縰 b岷 quan t芒m 膽岷縩 th峄漣 gian c峄 t峄玭g th脿nh ph岷, vui l貌ng xem [B谩o c谩o ki峄僲 tra hi峄噓 su岷 c谩c th脿nh ph岷 Xiaozhi](https://github.com/xinnan-tech/xiaozhi-performance-research), c贸 th峄� ki峄僲 tra th峄眂 t岷� trong m么i tr瓢峄漬g c峄 b岷 theo ph瓢啤ng ph谩p ki峄僲 tra trong b谩o c谩o.
+
+#### 馃敡 C么ng c峄� ki峄僲 tra
+D峄� 谩n n脿y cung c岷 c谩c c么ng c峄� ki峄僲 tra sau 膽峄� gi煤p b岷 x谩c minh h峄� th峄憂g v脿 ch峄峮 m么 h矛nh ph霉 h峄:
+
+| T锚n c么ng c峄� | V峄� tr铆 | Ph瓢啤ng ph谩p s峄� d峄g | M么 t岷� ch峄ヽ n膬ng |
+|:---:|:---|:---:|:---:|
+| C么ng c峄� ki峄僲 tra t瓢啤ng t谩c 芒m thanh | main銆媎igital-human銆媔ndex.html | Ch岷 `python start.py` trong `main/digital-human`, sau 膽贸 m峄� `http://127.0.0.1:8006/index.html` | Ki峄僲 tra ch峄ヽ n膬ng ph谩t v脿 nh岷璶 芒m thanh, x谩c minh x峄� l媒 芒m thanh ph铆a Python c贸 b矛nh th瓢峄漬g kh么ng |
+| C么ng c峄� ki峄僲 tra ph岷 h峄搃 m么 h矛nh | main銆媥iaozhi-server銆媝erformance_tester.py | Th峄眂 hi峄噉 `python performance_tester.py` | Ki峄僲 tra t峄慶 膽峄� ph岷 h峄搃 c峄 ba module c峄憈 l玫i ASR(Nh岷璶 d岷g gi峄峮g n贸i), LLM(M么 h矛nh l峄沶), VLLM(M么 h矛nh th峄� gi谩c), TTS(T峄昻g h峄 gi峄峮g n贸i) |
+
+> 馃挕 G峄 媒: Khi ki峄僲 tra t峄慶 膽峄� m么 h矛nh, ch峄� ki峄僲 tra c谩c m么 h矛nh 膽茫 c岷 h矛nh kh贸a.
+
+---
+## Danh s谩ch t铆nh n膬ng 鉁�
+### 膼茫 th峄眂 hi峄噉 鉁�
+
+| Module ch峄ヽ n膬ng | M么 t岷� |
+|:---:|:---|
+| Ki岷縩 tr煤c c峄憈 l玫i | D峄盿 tr锚n [c峄昻g MQTT+UDP](https://github.com/xinnan-tech/xiaozhi-esp32-server/blob/main/docs/mqtt-gateway-integration.md), WebSocket, m谩y ch峄� HTTP, cung c岷 h峄� th峄憂g qu岷 l媒 b岷g 膽i峄乽 khi峄僴 v脿 x谩c th峄眂 ho脿n ch峄塶h |
+| T瓢啤ng t谩c gi峄峮g n贸i | H峄� tr峄� ASR streaming(Nh岷璶 d岷g gi峄峮g n贸i), TTS streaming(T峄昻g h峄 gi峄峮g n贸i), VAD(Ph谩t hi峄噉 ho岷 膽峄檔g gi峄峮g n贸i), h峄� tr峄� nh岷璶 d岷g 膽a ng么n ng峄� v脿 x峄� l媒 gi峄峮g n贸i |
+| Nh岷璶 d岷g v芒n gi峄峮g | H峄� tr峄� 膽膬ng k媒, qu岷 l媒 v脿 nh岷璶 d岷g v芒n gi峄峮g 膽a ng瓢峄漣 d霉ng, x峄� l媒 song song v峄沬 ASR, nh岷璶 d岷g danh t铆nh ng瓢峄漣 n贸i theo th峄漣 gian th峄眂 v脿 truy峄乶 cho LLM 膽峄� ph岷 h峄搃 c谩 nh芒n h贸a |
+| 膼峄慽 tho岷 th么ng minh | H峄� tr峄� nhi峄乽 LLM(M么 h矛nh ng么n ng峄� l峄沶), th峄眂 hi峄噉 膽峄慽 tho岷 th么ng minh |
+| C岷 nh岷璶 th峄� gi谩c | H峄� tr峄� nhi峄乽 VLLM(M么 h矛nh l峄沶 th峄� gi谩c), th峄眂 hi峄噉 t瓢啤ng t谩c 膽a ph瓢啤ng th峄ヽ |
+| Nh岷璶 d岷g 媒 膽峄媙h | H峄� tr峄� nh岷璶 d岷g 媒 膽峄媙h m么 h矛nh l峄沶 g岷痭 ngo脿i, g峄峣 h脿m t峄� ch峄� m么 h矛nh l峄沶, cung c岷 c啤 ch岷� x峄� l媒 媒 膽峄媙h d岷g plugin |
+| H峄� th峄憂g b峄� nh峄� | H峄� tr峄� b峄� nh峄� ng岷痭 h岷 c峄 b峄�, b峄� nh峄� giao di峄噉 mem0ai, b峄� nh峄� th么ng minh PowerMem, c贸 ch峄ヽ n膬ng t贸m t岷痶 b峄� nh峄� |
+| Kho tri th峄ヽ | H峄� tr峄� kho tri th峄ヽ RAGFlow, cho ph茅p m么 h矛nh l峄沶 膽谩nh gi谩 c岷 g峄峣 kho tri th峄ヽ tr瓢峄沜 khi tr岷� l峄漣 |
+| G峄峣 c么ng c峄� | H峄� tr峄� giao th峄ヽ IOT ph铆a client, giao th峄ヽ MCP ph铆a client, giao th峄ヽ MCP ph铆a server, giao th峄ヽ 膽i峄僲 truy c岷璸 MCP, h脿m c么ng c峄� t霉y ch峄塶h |
+| G峄璱 l峄噉h | D峄盿 v脿o giao th峄ヽ MQTT, h峄� tr峄� g峄璱 l峄噉h MCP t峄� b岷g 膽i峄乽 khi峄僴 th么ng minh xu峄憂g thi岷縯 b峄� ESP32 |
+| Backend qu岷 l媒 | Cung c岷 giao di峄噉 qu岷 l媒 Web, h峄� tr峄� qu岷 l媒 ng瓢峄漣 d霉ng, c岷 h矛nh h峄� th峄憂g v脿 qu岷 l媒 thi岷縯 b峄�; giao di峄噉 h峄� tr峄� hi峄僴 th峄� ti岷縩g Trung gi岷 th峄�, ti岷縩g Trung ph峄搉 th峄�, ti岷縩g Anh |
+| C么ng c峄� ki峄僲 tra | Cung c岷 c么ng c峄� ki峄僲 tra hi峄噓 su岷, c么ng c峄� ki峄僲 tra m么 h矛nh th峄� gi谩c v脿 c么ng c峄� ki峄僲 tra t瓢啤ng t谩c 芒m thanh |
+| H峄� tr峄� tri峄僴 khai | H峄� tr峄� tri峄僴 khai Docker v脿 tri峄僴 khai c峄 b峄�, cung c岷 qu岷 l媒 t峄噋 c岷 h矛nh ho脿n ch峄塶h |
+| H峄� th峄憂g plugin | H峄� tr峄� m峄� r峄檔g plugin ch峄ヽ n膬ng, ph谩t tri峄僴 plugin t霉y ch峄塶h v脿 hot loading plugin |
+
+### 膼ang ph谩t tri峄僴 馃毀
+
+Mu峄憂 hi峄僽 ti岷縩 膽峄� k岷� ho岷h ph谩t tri峄僴 c峄� th峄�, [vui l貌ng nh岷 v脿o 膽芒y](https://github.com/users/xinnan-tech/projects/3). C芒u h峄廼 th瓢峄漬g g岷穚 v脿 h瓢峄沶g d岷玭 li锚n quan, vui l貌ng tham kh岷 [li锚n k岷縯 n脿y](../FAQ.md)
+
+N岷縰 b岷 l脿 m峄檛 nh脿 ph谩t tri峄僴 ph岷 m峄乵, 膽芒y c贸 m峄檛 [L谩 th瓢 m峄� g峄璱 c谩c nh脿 ph谩t tri峄僴](../contributor_open_letter.md), ch脿o m峄玭g tham gia!
+
+---
+
+## H峄� sinh th谩i s岷 ph岷﹎ 馃懍
+Xiaozhi l脿 m峄檛 h峄� sinh th谩i, khi b岷 s峄� d峄g s岷 ph岷﹎ n脿y, b岷 c农ng c贸 th峄� xem c谩c [d峄� 谩n xu岷 s岷痗](https://github.com/78/xiaozhi-esp32/blob/main/README_zh.md#%E7%9B%B8%E5%85%B3%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE) kh谩c trong h峄� sinh th谩i n脿y
+
+---
+
+## Danh s谩ch n峄乶 t岷g/th脿nh ph岷 膽瓢峄 d峄� 谩n n脿y h峄� tr峄� 馃搵
+### LLM M么 h矛nh ng么n ng峄�
+
+| Ph瓢啤ng ph谩p s峄� d峄g | N峄乶 t岷g h峄� tr峄� | N峄乶 t岷g mi峄卬 ph铆 |
+|:---:|:---:|:---:|
+| G峄峣 giao di峄噉 openai | Alibaba Bailian, Volcano Engine, DeepSeek, Zhipu, Gemini, iFlytek | Zhipu, Gemini |
+| G峄峣 giao di峄噉 ollama | Ollama | - |
+| G峄峣 giao di峄噉 dify | Dify | - |
+| G峄峣 giao di峄噉 fastgpt | Fastgpt | - |
+| G峄峣 giao di峄噉 coze | Coze | - |
+| G峄峣 giao di峄噉 xinference | Xinference | - |
+| G峄峣 giao di峄噉 homeassistant | HomeAssistant | - |
+
+Tr锚n th峄眂 t岷�, b岷 k峄� LLM n脿o h峄� tr峄� g峄峣 giao di峄噉 openai 膽峄乽 c贸 th峄� truy c岷璸 s峄� d峄g.
+
+---
+
+### VLLM M么 h矛nh th峄� gi谩c
+
+| Ph瓢啤ng ph谩p s峄� d峄g | N峄乶 t岷g h峄� tr峄� | N峄乶 t岷g mi峄卬 ph铆 |
+|:---:|:---:|:---:|
+| G峄峣 giao di峄噉 openai | Alibaba Bailian, Zhipu ChatGLMVLLM | Zhipu ChatGLMVLLM |
+
+Tr锚n th峄眂 t岷�, b岷 k峄� VLLM n脿o h峄� tr峄� g峄峣 giao di峄噉 openai 膽峄乽 c贸 th峄� truy c岷璸 s峄� d峄g.
+
+---
+
+### TTS T峄昻g h峄 gi峄峮g n贸i
+
+| Ph瓢啤ng ph谩p s峄� d峄g | N峄乶 t岷g h峄� tr峄� | N峄乶 t岷g mi峄卬 ph铆 |
+|:---:|:---:|:---:|
+| G峄峣 giao di峄噉 | EdgeTTS, iFlytek, Volcano Engine, Tencent Cloud, Alibaba Cloud v脿 Bailian, CosyVoiceSiliconflow, TTS302AI, CozeCnTTS, GizwitsTTS, ACGNTTS, OpenAITTS, Lingxi streaming TTS, MinimaxTTS | Lingxi streaming TTS, EdgeTTS, CosyVoiceSiliconflow(m峄檛 ph岷) |
+| D峄媍h v峄� c峄 b峄� | FishSpeech, GPT_SOVITS_V2, GPT_SOVITS_V3, Index-TTS, PaddleSpeech | Index-TTS, PaddleSpeech, FishSpeech, GPT_SOVITS_V2, GPT_SOVITS_V3 |
+
+---
+
+### VAD Ph谩t hi峄噉 ho岷 膽峄檔g gi峄峮g n贸i
+
+| Lo岷 | T锚n n峄乶 t岷g | Ph瓢啤ng ph谩p s峄� d峄g | M么 h矛nh thu ph铆 | Ghi ch煤 |
+|:---:|:---------:|:----:|:----:|:--:|
+| VAD | SileroVAD | S峄� d峄g c峄 b峄� | Mi峄卬 ph铆 | |
+
+---
+
+### ASR Nh岷璶 d岷g gi峄峮g n贸i
+
+| Ph瓢啤ng ph谩p s峄� d峄g | N峄乶 t岷g h峄� tr峄� | N峄乶 t岷g mi峄卬 ph铆 |
+|:---:|:---:|:---:|
+| S峄� d峄g c峄 b峄� | FunASR, SherpaASR | FunASR, SherpaASR |
+| G峄峣 giao di峄噉 | FunASRServer, Volcano Engine, iFlytek, Tencent Cloud, Alibaba Cloud, Baidu Cloud, OpenAI ASR | FunASRServer |
+
+---
+
+### Voiceprint Nh岷璶 d岷g v芒n gi峄峮g
+
+| Ph瓢啤ng ph谩p s峄� d峄g | N峄乶 t岷g h峄� tr峄� | N峄乶 t岷g mi峄卬 ph铆 |
+|:---:|:---:|:---:|
+| S峄� d峄g c峄 b峄� | 3D-Speaker | 3D-Speaker |
+
+---
+
+### Memory L瓢u tr峄� b峄� nh峄�
+
+| Lo岷 | T锚n n峄乶 t岷g | Ph瓢啤ng ph谩p s峄� d峄g | M么 h矛nh thu ph铆 | Ghi ch煤 |
+|:------:|:---------------:|:----:|:---------:|:--:|
+| Memory | mem0ai | G峄峣 giao di峄噉 | H岷 m峄ヽ 1000 l岷/th谩ng | |
+| Memory | [powermem](../powermem-integration.md) | T贸m t岷痶 c峄 b峄� | Ph峄� thu峄檆 v脿o LLM v脿 DB | OceanBase m茫 ngu峄搉 m峄�, h峄� tr峄� t矛m ki岷縨 th么ng minh |
+| Memory | mem_local_short | T贸m t岷痶 c峄 b峄� | Mi峄卬 ph铆 | |
+| Memory | nomem | Ch岷� 膽峄� kh么ng c贸 b峄� nh峄� | Mi峄卬 ph铆 | |
+
+---
+
+### Intent Nh岷璶 d岷g 媒 膽峄媙h
+
+| Lo岷 | T锚n n峄乶 t岷g | Ph瓢啤ng ph谩p s峄� d峄g | M么 h矛nh thu ph铆 | Ghi ch煤 |
+|:------:|:-------------:|:----:|:-------:|:---------------------:|
+| Intent | intent_llm | G峄峣 giao di峄噉 | Thu ph铆 theo LLM | Nh岷璶 d岷g 媒 膽峄媙h qua m么 h矛nh l峄沶, t铆nh t峄昻g qu谩t m岷h |
+| Intent | function_call | G峄峣 giao di峄噉 | Thu ph铆 theo LLM | Ho脿n th脿nh 媒 膽峄媙h qua g峄峣 h脿m m么 h矛nh l峄沶, t峄慶 膽峄� nhanh, hi峄噓 qu岷� t峄憈 |
+| Intent | nointent | Ch岷� 膽峄� kh么ng c贸 媒 膽峄媙h | Mi峄卬 ph铆 | Kh么ng th峄眂 hi峄噉 nh岷璶 d岷g 媒 膽峄媙h, tr岷� v峄� tr峄眂 ti岷縫 k岷縯 qu岷� 膽峄慽 tho岷 |
+
+---
+
+### Rag T膬ng c瓢峄漬g truy xu岷 th么ng tin
+
+| Lo岷 | T锚n n峄乶 t岷g | Ph瓢啤ng ph谩p s峄� d峄g | M么 h矛nh thu ph铆 | Ghi ch煤 |
+|:------:|:-------------:|:----:|:-------:|:---------------------:|
+| Rag | ragflow | G峄峣 giao di峄噉 | Thu ph铆 theo token ti锚u t峄憂 c峄 ph芒n 膽o岷, ph芒n t峄� | S峄� d峄g ch峄ヽ n膬ng t膬ng c瓢峄漬g truy xu岷 c峄 RagFlow, cung c岷 ph岷 h峄搃 膽峄慽 tho岷 ch铆nh x谩c h啤n |
+
+---
+
+## L峄漣 c岷 啤n 馃檹
+
+| Logo | D峄� 谩n/C么ng ty | M么 t岷� |
+|:---:|:---:|:---|
+| <img src="../images/logo_bailing.png" width="160"> | [Robot 膽峄慽 tho岷 gi峄峮g n贸i Bailing](https://github.com/wwbin2017/bailing) | D峄� 谩n n脿y 膽瓢峄 l岷 c岷 h峄﹏g t峄� [Robot 膽峄慽 tho岷 gi峄峮g n贸i Bailing](https://github.com/wwbin2017/bailing) v脿 膽瓢峄 tri峄僴 khai tr锚n c啤 s峄� 膽贸 |
+| <img src="../images/logo_tenclass.png" width="160"> | [Shifang Ronghai](https://www.tenclass.com/) | C岷 啤n [Shifang Ronghai](https://www.tenclass.com/) 膽茫 x芒y d峄眓g giao th峄ヽ truy峄乶 th么ng ti锚u chu岷﹏, ph瓢啤ng 谩n t瓢啤ng th铆ch 膽a thi岷縯 b峄� v脿 m么 ph岷 th峄眂 h脿nh t矛nh hu峄憂g 膽峄搉g th峄漣 cao cho h峄� sinh th谩i Xiaozhi; cung c岷 t脿i li峄噓 h峄� tr峄� k峄� thu岷璽 to脿n di峄噉 cho d峄� 谩n n脿y |
+| <img src="../images/logo_xuanfeng.png" width="160"> | [Xuanfeng Technology](https://github.com/Eric0308) | C岷 啤n [Xuanfeng Technology](https://github.com/Eric0308) 膽茫 膽贸ng g贸p khung g峄峣 h脿m, giao th峄ヽ truy峄乶 th么ng MCP v脿 m茫 tri峄僴 khai c啤 ch岷� g峄峣 d岷g plugin, th么ng qua h峄� th峄憂g 膽i峄乽 ph峄慽 l峄噉h ti锚u chu岷﹏ h贸a v脿 kh岷� n膬ng m峄� r峄檔g 膽峄檔g, 膽茫 c岷 thi峄噉 膽谩ng k峄� hi峄噓 su岷 t瓢啤ng t谩c v脿 kh岷� n膬ng m峄� r峄檔g ch峄ヽ n膬ng c峄 thi岷縯 b峄� front-end(IoT) |
+| <img src="../images/logo_junsen.png" width="160"> | [huangjunsen](https://github.com/huangjunsen0406) | C岷 啤n [huangjunsen](https://github.com/huangjunsen0406) 膽茫 膽贸ng g贸p module `B岷g 膽i峄乽 khi峄僴 th么ng minh di 膽峄檔g`, th峄眂 hi峄噉 膽i峄乽 khi峄僴 hi峄噓 qu岷� v脿 t瓢啤ng t谩c th峄漣 gian th峄眂 tr锚n thi岷縯 b峄� di 膽峄檔g 膽a n峄乶 t岷g, c岷 thi峄噉 膽谩ng k峄� s峄� ti峄噉 l峄 v岷璶 h脿nh v脿 hi峄噓 qu岷� qu岷 l媒 c峄 h峄� th峄憂g trong t矛nh hu峄憂g di 膽峄檔g |
+| <img src="../images/logo_huiyuan.png" width="160"> | [Huiyuan Design](http://ui.kwd988.net/) | C岷 啤n [Huiyuan Design](http://ui.kwd988.net/) 膽茫 cung c岷 gi岷 ph谩p th峄� gi谩c chuy锚n nghi峄噋 cho d峄� 谩n n脿y, s峄� d峄g kinh nghi峄噈 th峄眂 t岷� thi岷縯 k岷� ph峄 v峄� h啤n ngh矛n doanh nghi峄噋, trao quy峄乶 cho tr岷 nghi峄噈 ng瓢峄漣 d霉ng s岷 ph岷﹎ c峄 d峄� 谩n n脿y |
+| <img src="../images/logo_qinren.png" width="160"> | [Xi'an Qinren Information Technology](https://www.029app.com/) | C岷 啤n [Xi'an Qinren Information Technology](https://www.029app.com/) 膽茫 l脿m s芒u s岷痗 h峄� th峄憂g th峄� gi谩c c峄 d峄� 谩n n脿y, 膽岷 b岷 t铆nh nh岷 qu谩n v脿 kh岷� n膬ng m峄� r峄檔g c峄 phong c谩ch thi岷縯 k岷� t峄昻g th峄� trong 峄﹏g d峄g 膽a t矛nh hu峄憂g |
+| <img src="../images/logo_contributors.png" width="160"> | [Ng瓢峄漣 膽贸ng g贸p m茫](https://github.com/xinnan-tech/xiaozhi-esp32-server/graphs/contributors) | C岷 啤n [t岷 c岷� ng瓢峄漣 膽贸ng g贸p m茫](https://github.com/xinnan-tech/xiaozhi-esp32-server/graphs/contributors), s峄� c峄憂g hi岷縩 c峄 b岷 khi岷縩 d峄� 谩n m岷h m岷� v脿 v峄痭g ch岷痗 h啤n. |
+
+
+<a href="https://star-history.com/#xinnan-tech/xiaozhi-esp32-server&Date">
+
+ <picture>
+ <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=xinnan-tech/xiaozhi-esp32-server&type=Date&theme=dark" />
+ <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=xinnan-tech/xiaozhi-esp32-server&type=Date" />
+ <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=xinnan-tech/xiaozhi-esp32-server&type=Date" />
+ </picture>
+</a>
diff --git a/xiaozhi-esp32-server/docs/voiceprint-integration.md b/xiaozhi-esp32-server/docs/voiceprint-integration.md
new file mode 100644
index 0000000..bd0731e
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/voiceprint-integration.md
@@ -0,0 +1,235 @@
+# 澹扮汗璇嗗埆鍚敤鎸囧崡
+
+鏈暀绋嬪寘鍚�3涓儴鍒�
+- 1銆佸浣曢儴缃插0绾硅瘑鍒繖涓湇鍔�
+- 2銆佸叏妯″潡閮ㄧ讲鏃讹紝鎬庝箞閰嶇疆澹扮汗璇嗗埆鎺ュ彛
+- 3銆佹渶绠�鍖栭儴缃叉椂锛屾�庝箞閰嶇疆澹扮汗璇嗗埆
+
+# 1銆佸浣曢儴缃插0绾硅瘑鍒繖涓湇鍔�
+
+## 绗竴姝ワ紝涓嬭浇澹扮汗璇嗗埆椤圭洰婧愮爜
+
+娴忚鍣ㄦ墦寮�[澹扮汗璇嗗埆椤圭洰鍦板潃](https://github.com/xinnan-tech/voiceprint-api)
+
+鎵撳紑瀹岋紝鎵惧埌椤甸潰涓竴涓豢鑹茬殑鎸夐挳锛屽啓鐫�`Code`鐨勬寜閽紝鐐瑰紑瀹冿紝鐒跺悗浣犲氨鐪嬪埌`Download ZIP`鐨勬寜閽��
+
+鐐瑰嚮瀹冿紝涓嬭浇鏈」鐩簮鐮佸帇缂╁寘銆備笅杞藉埌浣犵數鑴戝悗锛岃В鍘嬪畠锛屾鏃跺畠鐨勫悕瀛楀彲鑳藉彨`voiceprint-api-main`
+浣犻渶瑕佹妸瀹冮噸鍛藉悕鎴恅voiceprint-api`銆�
+
+## 绗簩姝ワ紝 鍒涘缓鏁版嵁搴撳拰琛�
+
+澹扮汗璇嗗埆闇�瑕佷緷璧朻mysql`鏁版嵁搴撱�傚鏋滀綘涔嬪墠宸茬粡閮ㄧ讲`鏅烘帶鍙癭锛岃鏄庝綘宸茬粡瀹夎浜哷mysql`銆備綘鍙互鍏辩敤瀹冦��
+
+浣犲彲浠ヤ綘璇曚竴涓嬪湪瀹夸富鏈轰娇鐢╜telnet`鍛戒护锛岀湅鐪嬭兘涓嶈兘姝e父璁块棶`mysql`鐨刞3306`绔彛銆�
+```
+telnet 127.0.0.1 3306
+```
+濡傛灉鑳借闂埌3306绔彛锛岃蹇界暐浠ヤ笅鐨勫唴瀹癸紝鐩存帴杩涘叆绗笁姝ャ��
+
+濡傛灉涓嶈兘璁块棶锛屼綘闇�瑕佸洖蹇嗕竴涓嬶紝浣犵殑`mysql`鏄�庝箞瀹夎鐨勩��
+
+濡傛灉浣犵殑mysql鏄�氳繃鑷繁浣跨敤瀹夎鍖呭畨瑁呯殑锛岃鏄庝綘鐨刞mysql`鍋氫簡缃戠粶闅旂銆備綘鍙兘鍏堣В鍐宠闂甡mysql`鐨刞3306`绔彛杩欎釜闂銆�
+
+濡傛灉浣燻mysql`鏄�氳繃鏈」鐩殑`docker-compose_all.yml`瀹夎鐨勩�備綘闇�瑕佹壘涓�涓嬩綘褰撴椂鍒涘缓鏁版嵁搴撶殑`docker-compose_all.yml`鏂囦欢锛屼慨鏀逛互涓嬬殑鍐呭
+
+淇敼鍓�
+```
+ xiaozhi-esp32-server-db:
+ ...
+ networks:
+ - default
+ expose:
+ - "3306:3306"
+```
+
+淇敼鍚�
+```
+ xiaozhi-esp32-server-db:
+ ...
+ networks:
+ - default
+ ports:
+ - "3306:3306"
+```
+
+娉ㄦ剰鏄皢`xiaozhi-esp32-server-db`涓嬮潰鐨刞expose`鏀规垚`ports`銆傛敼瀹屽悗锛岄渶瑕侀噸鏂板惎鍔ㄣ�備互涓嬫槸閲嶅惎mysql鐨勫懡浠わ細
+
+```
+# 杩涘叆浣燿ocker-compose_all.yml鎵�鍦ㄧ殑鏂囦欢澶癸紝渚嬪鎴戠殑鏄痻iaozhi-server
+cd xiaozhi-server
+docker compose -f docker-compose_all.yml down
+docker compose -f docker-compose.yml up -d
+```
+
+鍚姩瀹屽悗锛屽湪瀹夸富鏈哄啀浣跨敤`telnet`鍛戒护锛岀湅鐪嬭兘涓嶈兘姝e父璁块棶`mysql`鐨刞3306`绔彛銆�
+```
+telnet 127.0.0.1 3306
+```
+姝e父鏉ヨ杩欐牱灏卞彲浠ヨ闂殑浜嗐��
+
+## 绗笁姝ワ紝 鍒涘缓鏁版嵁搴撳拰琛�
+濡傛灉浣犵殑瀹夸富鏈猴紝鑳芥甯歌闂甿ysql鏁版嵁搴擄紝閭e氨鍦╩ysql涓婂垱寤轰竴涓悕瀛椾负`voiceprint_db`鐨勬暟鎹簱鍜宍voiceprints`琛ㄣ��
+
+```
+CREATE DATABASE voiceprint_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+USE voiceprint_db;
+
+CREATE TABLE voiceprints (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ speaker_id VARCHAR(255) NOT NULL UNIQUE,
+ feature_vector LONGBLOB NOT NULL,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ INDEX idx_speaker_id (speaker_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+```
+
+## 绗洓姝ワ紝 閰嶇疆鏁版嵁搴撹繛鎺�
+
+杩涘叆`voiceprint-api`鏂囦欢澶癸紝鍒涘缓鍚嶅瓧涓篳data`鐨勬枃浠跺す銆�
+
+鎶奰voiceprint-api`鏍圭洰褰曢噷鐨刞voiceprint.yaml`锛屽鍒跺埌`data`鐨勬枃浠跺す锛屽皢瀹冮噸鍛藉悕涓篳.voiceprint.yaml`
+
+鎺ヤ笅鏉ワ紝浣犻渶瑕侀噸鐐归厤缃竴涓媊.voiceprint.yaml`閲岀殑鏁版嵁搴撹繛鎺ャ��
+
+```
+mysql:
+ host: "127.0.0.1"
+ port: 3306
+ user: "root"
+ password: "your_password"
+ database: "voiceprint_db"
+```
+
+娉ㄦ剰锛佺敱浜庝綘鐨勫0绾硅瘑鍒湇鍔℃槸浣跨敤docker閮ㄧ讲锛宍host`闇�瑕佸~鍐欐垚浣燻mysql鎵�鍦ㄦ満鍣ㄧ殑灞�鍩熺綉ip`銆�
+
+娉ㄦ剰锛佺敱浜庝綘鐨勫0绾硅瘑鍒湇鍔℃槸浣跨敤docker閮ㄧ讲锛宍host`闇�瑕佸~鍐欐垚浣燻mysql鎵�鍦ㄦ満鍣ㄧ殑灞�鍩熺綉ip`銆�
+
+娉ㄦ剰锛佺敱浜庝綘鐨勫0绾硅瘑鍒湇鍔℃槸浣跨敤docker閮ㄧ讲锛宍host`闇�瑕佸~鍐欐垚浣燻mysql鎵�鍦ㄦ満鍣ㄧ殑灞�鍩熺綉ip`銆�
+
+## 绗簲姝ワ紝鍚姩绋嬪簭
+杩欎釜椤圭洰鏄竴涓緢绠�鍗曠殑椤圭洰锛屽缓璁娇鐢╠ocker杩愯銆備笉杩囧鏋滀綘涓嶆兂浣跨敤docker杩愯锛屼綘鍙互鍙傝�僛杩欎釜椤甸潰](https://github.com/xinnan-tech/voiceprint-api/blob/main/README.md)浣跨敤婧愮爜杩愯銆備互涓嬫槸docker杩愯鐨勬柟娉�
+
+```
+# 杩涘叆鏈」鐩簮鐮佹牴鐩綍
+cd voiceprint-api
+
+# 娓呴櫎缂撳瓨
+docker compose -f docker-compose.yml down
+docker stop voiceprint-api
+docker rm voiceprint-api
+docker rmi ghcr.nju.edu.cn/xinnan-tech/voiceprint-api:latest
+
+# 鍚姩docker瀹瑰櫒
+docker compose -f docker-compose.yml up -d
+# 鏌ョ湅鏃ュ織
+docker logs -f voiceprint-api
+```
+
+姝ゆ椂锛屾棩蹇楅噷浼氳緭鍑虹被浼间互涓嬬殑鏃ュ織
+```
+250711 INFO-馃殌 寮�濮�: 鐢熶骇鐜鏈嶅姟鍚姩锛圲vicorn锛夛紝鐩戝惉鍦板潃: 0.0.0.0:8005
+250711 INFO-============================================================
+250711 INFO-澹扮汗鎺ュ彛鍦板潃: http://127.0.0.1:8005/voiceprint/health?key=abcd
+250711 INFO-============================================================
+```
+
+璇蜂綘鎶婂0绾规帴鍙e湴鍧�澶嶅埗鍑烘潵锛�
+
+鐢变簬浣犳槸docker閮ㄧ讲锛屽垏涓嶅彲鐩存帴浣跨敤涓婇潰鐨勫湴鍧�锛�
+
+鐢变簬浣犳槸docker閮ㄧ讲锛屽垏涓嶅彲鐩存帴浣跨敤涓婇潰鐨勫湴鍧�锛�
+
+鐢变簬浣犳槸docker閮ㄧ讲锛屽垏涓嶅彲鐩存帴浣跨敤涓婇潰鐨勫湴鍧�锛�
+
+浣犲厛鎶婂湴鍧�澶嶅埗鍑烘潵锛屾斁鍦ㄤ竴涓崏绋块噷锛屼綘瑕佺煡閬撲綘鐨勭數鑴戠殑灞�鍩熺綉ip鏄粈涔堬紝渚嬪鎴戠殑鐢佃剳灞�鍩熺綉ip鏄痐192.168.1.25`锛岄偅涔�
+鍘熸潵鎴戠殑鎺ュ彛鍦板潃
+```
+http://127.0.0.1:8005/voiceprint/health?key=abcd
+
+```
+灏辫鏀规垚
+```
+http://192.168.1.25:8005/voiceprint/health?key=abcd
+```
+
+鏀瑰ソ鍚庯紝璇蜂娇鐢ㄦ祻瑙堝櫒鐩存帴璁块棶`澹扮汗鎺ュ彛鍦板潃`銆傚綋娴忚鍣ㄥ嚭鐜扮被浼艰繖鏍风殑浠g爜锛岃鏄庢槸鎴愬姛浜嗐��
+```
+{"total_voiceprints":0,"status":"healthy"}
+```
+
+璇蜂綘淇濈暀濂戒慨鏀瑰悗鐨刞澹扮汗鎺ュ彛鍦板潃`锛屼笅涓�姝ヨ鐢ㄥ埌銆�
+
+# 2銆佸叏妯″潡閮ㄧ讲鏃讹紝鎬庝箞閰嶇疆澹扮汗璇嗗埆
+
+## 绗竴姝� 閰嶇疆鎺ュ彛
+棣栧厛锛屼綘瑕佸紑鍚0绾硅瘑鍒姛鑳姐�傚湪鏅烘帶鍙帮紝鐐瑰嚮椤堕儴`鍙傛暟瀛楀吀`锛屽湪涓嬫媺鑿滃崟涓紝鐐瑰嚮`绯荤粺鍔熻兘閰嶇疆`椤甸潰銆傚湪椤甸潰涓婂嬀閫塦澹扮汗璇嗗埆`锛岀偣鍑籤淇濆瓨閰嶇疆`銆傚嵆鍙湪鏂板缓鏅鸿兘浣撶殑鍗$墖涓婄湅鍒癭澹扮汗璇嗗埆`鎸夐挳銆�
+
+濡傛灉浣犳槸鍏ㄦā鍧楅儴缃诧紝浣跨敤绠$悊鍛樿处鍙凤紝鐧诲綍鏅烘帶鍙帮紝鐐瑰嚮椤堕儴`鍙傛暟瀛楀吀`锛岄�夋嫨`鍙傛暟绠$悊`鍔熻兘銆�
+
+鐒跺悗鎼滅储鍙傛暟`server.voice_print`锛屾鏃讹紝瀹冪殑鍊煎簲璇ユ槸`null`鍊笺��
+鐐瑰嚮淇敼鎸夐挳锛屾妸涓婁竴姝ュ緱鏉ョ殑`澹扮汗鎺ュ彛鍦板潃`绮樿创鍒癭鍙傛暟鍊糮閲屻�傜劧鍚庝繚瀛樸��
+
+濡傛灉鑳戒繚瀛樻垚鍔燂紝璇存槑涓�鍒囬『鍒╋紝浣犲彲浠ュ幓鏅鸿兘浣撴煡鐪嬫晥鏋滀簡銆傚鏋滀笉鎴愬姛锛岃鏄庢櫤鎺у彴鏃犳硶璁块棶澹扮汗璇嗗埆锛屽緢澶ф鐜囨槸缃戠粶闃茬伀澧欙紝鎴栬�呮病鏈夊~鍐欐纭殑灞�鍩熺綉ip銆�
+
+## 绗簩姝� 璁剧疆鏅鸿兘浣撹蹇嗘ā寮�
+
+杩涘叆浣犵殑鏅鸿兘浣撶殑瑙掕壊閰嶇疆閲岋紝灏嗚蹇嗚缃垚`鏈湴鐭湡璁板繂`锛屼竴瀹氳寮�鍚痐涓婃姤鏂囧瓧+璇煶`銆�
+
+## 绗笁姝� 鍜屼綘鐨勬櫤鑳戒綋鑱婂ぉ
+
+灏嗕綘鐨勮澶囬�氱數锛岀劧鍚庡拰浠栫敤姝e父鐨勮閫熷拰闊宠皟鑱婂ぉ銆�
+
+## 绗洓姝� 璁剧疆澹扮汗
+
+鍦ㄦ櫤鎺у彴锛宍鏅鸿兘浣撶鐞哷椤甸潰锛屽湪鏅鸿兘浣撶殑闈㈡澘閲岋紝鏈変竴涓猔澹扮汗璇嗗埆`鎸夐挳锛岀偣鍑诲畠銆傚湪搴曢儴鏈変竴涓猔鏂板鎸夐挳`銆傚氨鍙互瀵规煇涓汉璇寸殑璇濊繘琛屽0绾规敞鍐屻��
+鍦ㄥ脊鍑虹殑妗嗛噷锛宍鎻忚堪`杩欎釜灞炴�у缓璁~鍐欎笂锛屽彲浠ユ槸杩欎釜浜虹殑鑱屼笟銆佹�ф牸銆佺埍濂姐�傛柟渚挎櫤鑳戒綋瀵硅璇濅汉杩涜鍒嗘瀽鍜屼簡瑙c��
+
+## 绗笁姝� 鍜屼綘鐨勬櫤鑳戒綋鑱婂ぉ
+
+灏嗕綘鐨勮澶囬�氱數锛岄棶瀹冿紝浣犵煡閬撴垜鏄皝鍚楋紵濡傛灉浠栬兘鍥炵瓟寰楀嚭锛岃鏄庡0绾硅瘑鍒姛鑳芥甯搞��
+
+# 3銆佹渶绠�鍖栭儴缃叉椂锛屾�庝箞閰嶇疆澹扮汗璇嗗埆
+
+## 绗竴姝� 閰嶇疆鎺ュ彛
+鎵撳紑 `xiaozhi-server/data/.config.yaml` 鏂囦欢锛堝鏋滄病鏈夐渶瑕佸垱寤猴級锛岀劧鍚庢坊鍔�/淇敼浠ヤ笅鍐呭锛�
+
+```
+# 澹扮汗璇嗗埆閰嶇疆
+voiceprint:
+ # 澹扮汗鎺ュ彛鍦板潃
+ url: 浣犵殑澹扮汗鎺ュ彛鍦板潃
+ # 璇磋瘽浜洪厤缃細speaker_id,鍚嶇О,鎻忚堪
+ speakers:
+ - "test1,寮犱笁,寮犱笁鏄竴涓▼搴忓憳"
+ - "test2,鏉庡洓,鏉庡洓鏄竴涓骇鍝佺粡鐞�"
+ - "test3,鐜嬩簲,鐜嬩簲鏄竴涓璁″笀"
+```
+
+鎶婁笂涓�姝ュ緱鏉ョ殑 `澹扮汗鎺ュ彛鍦板潃` 绮樿创鍒� `url` 閲屻�傜劧鍚庝繚瀛樸��
+
+`speakers` 鍙傛暟渚濇嵁闇�姹傛坊鍔犮�傝繖閲岄渶瑕佹敞鎰忚繖涓� `speaker_id` 鍙傛暟锛屽悗闈㈡敞鍐屽0绾逛細鐢ㄥ埌銆�
+
+## 绗簩姝� 娉ㄥ唽澹扮汗
+濡傛灉浣犲凡缁忓惎鍔ㄤ簡澹扮汗鏈嶅姟锛屾湰鍦版祻瑙堝櫒閲岃闂� `http://localhost:8005/voiceprint/docs` 鍗冲彲鏌ョ湅 API 鏂囨。锛岃繖閲屽彧璇存槑娉ㄥ唽澹扮汗鐨� API 濡備綍浣跨敤銆�
+
+娉ㄥ唽澹扮汗鐨� API 鍦板潃涓� `http://localhost:8005/voiceprint/register`锛岃姹傛柟寮忎负 POST銆�
+
+璇锋眰澶撮渶瑕佸寘鍚� Bearer Token 璁よ瘉锛宼oken 涓� `澹扮汗鎺ュ彛鍦板潃` 涓� `?key=` 鍚庣殑閮ㄥ垎锛屾瘮濡傚鏋滄垜鐨勫0绾规敞鍐屽湴鍧�涓� `http://127.0.0.1:8005/voiceprint/health?key=abcd`锛岄偅涔堟垜鐨� token 灏辨槸`abcd`銆�
+
+璇锋眰浣撳寘鍚璇濅汉 ID锛坰peaker_id锛夛紝鍜� WAV 闊抽鏂囦欢锛坒ile锛夛紝璇锋眰绀轰緥濡備笅锛�
+
+```
+curl -X POST \
+ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
+ -F "speaker_id=your_speaker_id_here" \
+ -F "file=@/path/to/your/file" \
+ http://localhost:8005/voiceprint/register
+```
+
+ 杩欓噷鐨� `file` 鏄娉ㄥ唽鐨勮璇濅汉璇磋瘽鐨勯煶棰戞枃浠讹紝 `speaker_id` 闇�瑕佸拰绗竴姝ラ厤缃帴鍙g殑 `speaker_id` 淇濇寔涓�鑷淬�傛瘮濡傝鎴戦渶瑕佹敞鍐屽紶涓夌殑澹扮汗锛屽湪 `.config.yaml` 涓~鐨勫紶涓夌殑 `speaker_id` 涓� `test1`锛岄偅涔堟垜娉ㄥ唽寮犱笁澹扮汗鐨勬椂鍊欙紝璇锋眰浣撻噷濉殑 `speaker_id` 灏辨槸 `test1`锛� `file` 濉殑灏辨槸寮犱笁璇翠竴娈佃瘽鐨勯煶棰戞枃浠躲��
+
+ ## 绗笁姝� 鍚姩鏈嶅姟
+
+鍚姩灏忔櫤鏈嶅姟鍣ㄥ拰澹扮汗鏈嶅姟锛屽嵆鍙甯镐娇鐢ㄣ��
diff --git a/xiaozhi-esp32-server/docs/weather-integration.md b/xiaozhi-esp32-server/docs/weather-integration.md
new file mode 100644
index 0000000..3b6ca2b
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/weather-integration.md
@@ -0,0 +1,64 @@
+# 澶╂皵鎻掍欢浣跨敤鎸囧崡
+
+## 姒傝堪
+
+澶╂皵鎻掍欢 `get_weather` 鏄皬鏅篍SP32璇煶鍔╂墜鐨勬牳蹇冨姛鑳戒箣涓�锛屾敮鎸侀�氳繃璇煶鏌ヨ鍏ㄥ浗鍚勫湴鐨勫ぉ姘斾俊鎭�傛彃浠跺熀浜庡拰椋庡ぉ姘擜PI锛屾彁渚涘疄鏃跺ぉ姘斿拰7澶╁ぉ姘旈鎶ュ姛鑳姐��
+
+## API Key 鐢宠鎸囧崡
+
+### 1. 娉ㄥ唽鍜岄澶╂皵璐﹀彿
+
+1. 璁块棶 [鍜岄澶╂皵鎺у埗鍙癩(https://console.qweather.com/)
+2. 娉ㄥ唽璐﹀彿骞跺畬鎴愰偖绠遍獙璇�
+3. 鐧诲綍鎺у埗鍙�
+
+### 2. 鍒涘缓搴旂敤鑾峰彇API Key
+
+1. 杩涘叆鎺у埗鍙板悗锛岀偣鍑诲彸渚"椤圭洰绠$悊"](https://console.qweather.com/project?lang=zh) 鈫� "鍒涘缓椤圭洰"
+2. 濉啓椤圭洰淇℃伅锛�
+ - **椤圭洰鍚嶇О**锛氬"灏忔櫤璇煶鍔╂墜"
+3. 鐐瑰嚮淇濆瓨
+4. 椤圭洰鍒涘缓瀹屾垚鍚庯紝鍦ㄨ椤圭洰涓偣鍑�"鍒涘缓鍑嵁"
+5. 濉啓鍑嵁淇℃伅锛�
+ - **鍑嵁鍚嶇О**锛氬"灏忔櫤璇煶鍔╂墜"
+ - **韬唤璁よ瘉鏂瑰紡**锛氶�夋嫨"API Key"
+6. 鐐瑰嚮淇濆瓨
+7. 鍦ㄥ嚟鎹腑澶嶅埗`API Key`锛岃繖鏄涓�涓叧閿殑閰嶇疆淇℃伅
+
+### 3. 鑾峰彇API Host
+
+1. 鍦ㄦ帶鍒跺彴涓偣鍑籟"璁剧疆"](https://console.qweather.com/setting?lang=zh) 鈫� "API Host"
+2. 鏌ョ湅鍒嗛厤缁欎綘鐨勪笓灞瀈API Host`鍦板潃锛岃繖涓槸绗簩涓叧閿殑閰嶇疆淇℃伅
+
+浠ヤ笂鎿嶄綔锛屼細寰楀埌涓や釜閲嶈鐨勯厤缃俊鎭�:`API Key`鍜宍API Host`
+
+## 閰嶇疆鏂瑰紡(浠婚�変竴绉�)
+
+### 鏂瑰紡1. 濡傛灉浣犱娇鐢ㄤ簡鏅烘帶鍙伴儴缃诧紙鎺ㄨ崘锛�
+
+1. 鐧诲綍鏅烘帶鍙�
+2. 杩涘叆"瑙掕壊閰嶇疆"椤甸潰
+3. 閫夋嫨瑕侀厤缃殑鏅鸿兘浣�
+4. 鐐瑰嚮"缂栬緫鍔熻兘"鎸夐挳
+5. 鍦ㄥ彸渚у弬鏁伴厤缃尯鍩熸壘鍒�"澶╂皵鏌ヨ"鎻掍欢
+6. 鍕鹃��"澶╂皵鏌ヨ"
+7. 灏嗗鍒惰繃鏉ョ殑绗竴涓叧閿厤缃甡API Key`,濉叆鍒癭澶╂皵鎻掍欢 API 瀵嗛挜`閲�
+8. 灏嗗鍒惰繃鏉ョ殑绗簩涓叧閿厤缃甡API Host`,濉叆鍒癭寮�鍙戣�� API Host`閲�
+9. 淇濆瓨閰嶇疆锛屽啀淇濆瓨鏅鸿兘浣撻厤缃�
+
+### 鏂瑰紡2. 濡傛灉浣犲彧鏄崟妯″潡xiaozhi-server閮ㄧ讲
+
+鍦� `data/.config.yaml` 涓厤缃細
+
+1. 灏嗗鍒惰繃鏉ョ殑绗竴涓叧閿厤缃甡API Key`,濉叆鍒癭api_key`閲�
+2. 灏嗗鍒惰繃鏉ョ殑绗簩涓叧閿厤缃甡API Host`,濉叆鍒癭api_host`閲�
+3. 灏嗕綘鎵�鍦ㄧ殑鍩庡競濉叆鍒癭default_location`閲岋紝渚嬪`骞垮窞`
+
+```yaml
+plugins:
+ get_weather:
+ api_key: "浣犵殑鍜岄澶╂皵API瀵嗛挜"
+ api_host: "浣犵殑鍜岄澶╂皵API涓绘満鍦板潃"
+ default_location: "浣犵殑榛樿鏌ヨ鍩庡競"
+```
+
diff --git a/xiaozhi-esp32-server/docs/web-search-integration.md b/xiaozhi-esp32-server/docs/web-search-integration.md
new file mode 100644
index 0000000..25dd546
--- /dev/null
+++ b/xiaozhi-esp32-server/docs/web-search-integration.md
@@ -0,0 +1,71 @@
+# 鑱旂綉鎼滅储鎻掍欢浣跨敤鎸囧崡
+
+## 鍔熻兘绠�浠�
+
+鑱旂綉鎼滅储鎻掍欢 `web_search` 鏀寔鍦ㄥ璇濊繃绋嬩腑瀹炴椂鑱旂綉鎼滅储淇℃伅骞惰繑鍥炵粨鏋溿�傛彃浠舵敮鎸佷袱涓悳绱㈡簮锛氱濉旓紙Metaso锛夊拰Tavily锛岀敤鎴峰彲鏍规嵁闇�瑕侀�夋嫨鍏朵腑涓�涓��
+
+## API Key鐢宠鎸囧崡
+
+鐩墠鎴戜滑閫傞厤浜哷绉樺鎼滅储`鍜宍Tavily鎼滅储`銆�
+- Tavily鎼滅储锛氭瘡涓湀1000娆″厤璐归搴︺��
+- 绉樺鎼滅储锛氭嫢鏈夎緝涓轰紭璐ㄧ殑鍥藉唴鏁版嵁婧愩��
+
+## API Key鐢宠鎸囧崡
+
+### 鏂瑰紡涓�锛氫娇鐢ㄧ濉旀悳绱�
+
+- 璁块棶 [绉樺鎼滅储API](https://metaso.cn/search-api/api-keys)锛屾敞鍐屽苟鐧诲綍璐﹀彿
+- 鍦ˋPI瀵嗛挜绠$悊椤甸潰锛岀偣鍑�"鍒涘缓鏂扮殑Key"
+- 澶嶅埗鐢熸垚鐨凙PI Key锛堜互 `mk-` 涓哄墠缂�锛夛紝杩欐槸閰嶇疆鎵�闇�鐨勫叧閿俊鎭�
+
+### 鏂瑰紡浜岋細浣跨敤Tavily鎼滅储
+
+- 璁块棶 [Tavily鎺у埗鍙癩(https://app.tavily.com/home)锛屾敞鍐屽苟鐧诲綍璐﹀彿
+- 鍦ㄦ帶鍒跺彴涓垱寤篈PI Key
+- 澶嶅埗鐢熸垚鐨凙PI Key锛堜互 `tvly-` 涓哄墠缂�锛夛紝杩欐槸閰嶇疆鎵�闇�鐨勫叧閿俊鎭�
+
+## 閰嶇疆鏂瑰紡
+
+### 鏂瑰紡1. 浣跨敤鏅烘帶鍙伴儴缃诧紙鎺ㄨ崘锛�
+
+- 鐧诲綍鏅烘帶鍙�
+- 杩涘叆"閰嶇疆瑙掕壊"椤甸潰锛岄�夋嫨瑕侀厤缃殑鏅鸿兘浣�
+- 鐐瑰嚮"缂栬緫鍔熻兘"鎸夐挳锛屽湪鍙充晶鍙傛暟閰嶇疆鍖哄煙鎵惧埌"鑱旂綉鎼滅储"鎻掍欢
+- 鍕鹃��"鑱旂綉鎼滅储"
+- 濉叆鎼滅储婧愶紙`metaso`鎴朻tavily`锛夛紝骞跺皢瀵瑰簲鐨刞API Key`濉叆閰嶇疆椤�
+- 淇濆瓨閰嶇疆锛屽啀淇濆瓨鏅鸿兘浣撻厤缃�
+
+### 鏂瑰紡2. 鍗曟ā鍧梮iaozhi-server閮ㄧ讲
+
+鍦� `data/.config.yaml` 涓厤缃細
+
+- 灏嗘悳绱㈡簮濉叆 `provider`锛屽彲閫夊�间负 `metaso` 鎴� `tavily`
+- 灏嗙敵璇峰埌鐨凙PI Key濉叆 `api_key`
+
+```yaml
+plugins:
+ web_search:
+ provider: "metaso"
+ api_key: "浣犵殑API Key"
+```
+
+濡傞渶鑷畾涔夎繑鍥炵粨鏋滄暟閲忓拰宸ュ叿鎻忚堪锛屽彲棰濆閰嶇疆 `max_results` 鍜� `description`锛�
+
+```yaml
+plugins:
+ web_search:
+ provider: "metaso"
+ description: "鑱旂綉鎼滅储宸ュ叿銆傚綋鐢ㄦ埛鏄庣‘闇�瑕佽仈缃戞悳绱㈤棶棰樻椂浣跨敤姝ゅ伐鍏枫��"
+ max_results: 5
+ api_key: "浣犵殑API Key"
+```
+
+鍚屾椂鍦� `functions` 鍒楄〃涓‘淇濆凡鍚敤 `web_search`锛�
+
+```yaml
+plugins:
+ functions:
+ - web_search
+```
+
+閰嶇疆瀹屾垚鍚庨噸鍚湇鍔″嵆鍙敓鏁堛��
diff --git a/xiaozhi-esp32-server/main/README.md b/xiaozhi-esp32-server/main/README.md
new file mode 100644
index 0000000..ed7f9bc
--- /dev/null
+++ b/xiaozhi-esp32-server/main/README.md
@@ -0,0 +1,592 @@
+# 鎶�鏈枃妗o細`xiaozhi-esp32-server`
+
+**鐩綍锛�**
+
+1. [寮曡█](#1-寮曡█)
+2. [鏁翠綋鏋舵瀯](#2-鏁翠綋鏋舵瀯)
+3. [鏍稿績缁勪欢娣卞害鍓栨瀽](#3-鏍稿績缁勪欢娣卞害鍓栨瀽)
+ * [3.1. `xiaozhi-server` (鏍稿績AI寮曟搸 - Python瀹炵幇)](#31-xiaozhi-server-鏍稿績ai寮曟搸---python瀹炵幇)
+ * [3.2. `manager-api` (绠$悊鍚庣 - Java Spring Boot瀹炵幇)](#32-manager-api-绠$悊鍚庣---java-spring-boot瀹炵幇)
+ * [3.3. `manager-web` (Web绠$悊鍓嶇 - Vue.js瀹炵幇)](#33-manager-web-web绠$悊鍓嶇---vuejs瀹炵幇)
+ * [3.4. `manager-mobile` (绉诲姩绠$悊绔� - uni-app+Vue3瀹炵幇)](#34-manager-mobile-绉诲姩绠$悊绔�---uni-appvue3瀹炵幇)
+ * [3.5. `digital-human` (鏁板瓧浜烘祴璇曟ā鍧� - Python+Web瀹炵幇)](#35-digital-human-鏁板瓧浜烘祴璇曟ā鍧�---pythonweb瀹炵幇)
+4. [鏁版嵁娴佷笌浜や簰鏈哄埗](#4-鏁版嵁娴佷笌浜や簰鏈哄埗)
+5. [鏍稿績鍔熻兘姒傝](#5-鏍稿績鍔熻兘姒傝)
+6. [閮ㄧ讲涓庨厤缃杩癩(#6-閮ㄧ讲涓庨厤缃杩�)
+---
+
+## 1. 寮曡█
+
+`xiaozhi-esp32-server` 椤圭洰鏄竴涓笓涓哄熀浜嶦SP32鐨勬櫤鑳界‖浠舵彁渚涙敮鎸佺殑**缁煎悎鎬у悗绔郴缁�**銆傚叾鏍稿績鐩爣鏄娇寮�鍙戜汉鍛樿兘澶熷揩閫熸瀯寤轰竴涓己澶х殑鏈嶅姟鍣ㄥ熀纭�璁炬柦锛岃璁炬柦涓嶄粎鑳藉鐞嗚В鑷劧璇█鎸囦护锛岃繕鑳戒笌澶氱AI鏈嶅姟锛堢敤浜庤闊宠瘑鍒�佽嚜鐒惰瑷�鐞嗚В鍙婅闊冲悎鎴愶級杩涜楂樻晥浜や簰銆佺鐞嗙墿鑱旂綉锛圛oT锛夎澶囷紝骞舵彁渚涗竴涓熀浜嶹eb鐨勭敤鎴风晫闈互杩涜绯荤粺閰嶇疆鍜岀鐞嗐�傞�氳繃灏嗗绉嶅皷绔妧鏈暣鍚堝埌涓�涓珮鍐呰仛涓斿彲鎵╁睍鐨勫钩鍙颁腑锛屾湰椤圭洰鏃ㄥ湪绠�鍖栧拰鍔犻�熷彲瀹氬埗鍖栬闊冲姪鎵嬪強鏅鸿兘鎺у埗绯荤粺鐨勫紑鍙戣繘绋嬨�傚畠涓嶄粎浠呮槸涓�涓畝鍗曠殑鏈嶅姟鍣紝鏇存槸涓�涓繛鎺ョ‖浠躲�丄I鑳藉姏涓庣敤鎴风鐞嗙殑妗ユ銆�
+
+---
+
+## 2. 鏁翠綋鏋舵瀯
+
+`xiaozhi-esp32-server` 绯荤粺閲囩敤浜嗕竴绉�**鍒嗗竷寮忋�佸缁勪欢鍗忎綔**鐨勬灦鏋勮璁★紝纭繚浜嗙郴缁熺殑妯″潡鍖栥�佸彲缁存姢鎬у拰鍙墿灞曟�с�傚悇涓牳蹇冪粍浠跺悇鍙稿叾鑱岋紝鍗忓悓宸ヤ綔銆備富瑕佺粍浠跺寘鎷細
+
+1. **ESP32 纭欢 (瀹㈡埛绔澶�):**
+ 杩欐槸缁堢鐢ㄦ埛鐩存帴涓庝箣浜や簰鐨勭墿鐞嗘櫤鑳界‖浠惰澶囥�傚叾涓昏鑱岃矗鍖呮嫭锛�
+ * 鎹曟崏鐢ㄦ埛鐨勮闊虫寚浠ゃ��
+ * 灏嗘崟鎹夊埌鐨勫師濮嬮煶棰戞暟鎹畨鍏ㄥ湴鍙戦�佽嚦 `xiaozhi-server` 杩涜澶勭悊銆�
+ * 鎺ユ敹鏉ヨ嚜 `xiaozhi-server` 鍚堟垚鐨勮闊冲洖澶嶏紝骞堕�氳繃鎵0鍣ㄦ挱鏀剧粰鐢ㄦ埛銆�
+ * 鏍规嵁浠� `xiaozhi-server` 鏀跺埌鐨勬寚浠わ紝鎺у埗涓庝箣杩炴帴鐨勫叾浠栧鍥磋澶囨垨IoT璁惧锛堜緥濡傛櫤鑳界伅娉°�佷紶鎰熷櫒绛夛級銆�
+
+2. **`xiaozhi-server` (鏍稿績AI寮曟搸 - Python瀹炵幇):**
+ 杩欎釜鍩轰簬Python鐨勬湇鍔″櫒鏄暣涓郴缁熺殑鈥滃ぇ鑴戔�濓紝璐熻矗澶勭悊鎵�鏈夎闊崇浉鍏崇殑閫昏緫鍜孉I浜や簰銆傚叾鍏抽敭鑱岃矗缁嗗寲濡備笅锛�
+ * 閫氳繃WebSocket鍗忚涓嶦SP32璁惧寤虹珛**绋冲畾銆佷綆寤惰繜鐨勫疄鏃跺弻鍚戦�氫俊閾捐矾**銆�
+ * 鎺ユ敹鏉ヨ嚜ESP32鐨勯煶棰戞祦锛屽苟鍒╃敤璇煶娲诲姩妫�娴嬶紙VAD锛夋妧鏈簿纭垏鍒嗘湁鏁堢殑璇煶鐗囨銆�
+ * 闆嗘垚骞惰皟鐢ㄨ嚜鍔ㄨ闊宠瘑鍒紙ASR锛夋湇鍔★紙鍙厤缃湰鍦版垨浜戠锛夛紝灏嗚闊崇墖娈佃浆鎹负鏂囨湰銆�
+ * 閫氳繃涓庡ぇ鍨嬭瑷�妯″瀷锛圠LM锛夌殑浜や簰鏉ヨВ鏋愮敤鎴锋剰鍥俱�佺敓鎴愭櫤鑳藉洖澶嶏紝骞舵敮鎸佸鏉傜殑鑷劧璇█鐞嗚В浠诲姟銆�
+ * 绠$悊澶氳疆瀵硅瘽涓殑涓婁笅鏂囦俊鎭拰鐢ㄦ埛璁板繂锛屼互鎻愪緵杩炶疮鐨勪氦浜掍綋楠屻��
+ * 璋冪敤鏂囨湰杞闊筹紙TTS锛夋湇鍔★紝灏哃LM鐢熸垚鐨勬枃鏈洖澶嶅悎鎴愪负鑷劧娴佺晠鐨勮闊炽��
+ * 閫氳繃涓�涓伒娲荤殑**鎻掍欢绯荤粺**鎵ц鑷畾涔夊懡浠わ紝鍖呮嫭瀵笽oT璁惧鐨勬帶鍒堕�昏緫銆�
+ * 浠� `manager-api` 鏈嶅姟鑾峰彇鍏惰缁嗙殑杩愯鏃舵搷浣滈厤缃��
+
+3. **`manager-api` (绠$悊鍚庣 - Java瀹炵幇):**
+ 杩欐槸涓�涓熀浜嶫ava Spring Boot妗嗘灦鏋勫缓鐨勫簲鐢ㄧ▼搴忥紝瀹冧负鏁翠釜绯荤粺鐨勭鐞嗗拰閰嶇疆鎻愪緵浜嗕竴濂楀畨鍏ㄧ殑RESTful API銆傚畠涓嶄粎鏄� `manager-web` 鎺у埗鍙扮殑鍚庣鏀拺锛屼篃鏄� `xiaozhi-server` 鐨勯厤缃暟鎹潵婧愩�傚叾鏍稿績鍔熻兘鍖呮嫭锛�
+ * 涓篧eb鎺у埗鍙版彁渚涚敤鎴疯璇侊紙鐧诲綍銆佹潈闄愰獙璇侊級鍜岀敤鎴疯处鎴风鐞嗗姛鑳姐��
+ * ESP32璁惧鐨勬敞鍐屻�佷俊鎭鐞嗕互鍙婅澶囩壒瀹氶厤缃殑缁存姢銆�
+ * 鍦�**MySQL鏁版嵁搴�**涓寔涔呭寲瀛樺偍绯荤粺閰嶇疆锛屼緥濡傜敤鎴烽�夋嫨鐨凙I鏈嶅姟鎻愪緵鍟嗐�丄PI瀵嗛挜銆佽澶囧弬鏁般�佹彃浠惰缃瓑銆�
+ * 鎻愪緵鐗瑰畾鐨凙PI绔偣锛屼緵 `xiaozhi-server` 鎷夊彇鍏舵墍闇�鐨勬渶鏂伴厤缃��
+ * 绠$悊TTS闊宠壊閫夐」銆佸鐞哋TA锛圤ver-The-Air锛夊浐浠舵洿鏂版祦绋嬪強鐩稿叧鍏冩暟鎹��
+ * 鍒╃敤 **Redis** 浣滀负楂橀�熺紦瀛橈紝瀛樺偍鐑偣鏁版嵁锛堝浼氳瘽淇℃伅銆侀绻佽闂殑閰嶇疆锛夛紝浠ユ彁鍗嘇PI鍝嶅簲閫熷害鍜岀郴缁熸暣浣撴�ц兘銆�
+
+4. **`manager-web` (Web鎺у埗闈㈡澘 - Vue.js瀹炵幇):**
+ 杩欐槸涓�涓熀浜嶸ue.js鏋勫缓鐨勫崟椤靛簲鐢紙SPA锛夛紝涓虹郴缁熺鐞嗗憳鎻愪緵浜嗕竴涓浘褰㈠寲銆佺敤鎴峰弸濂界殑鎿嶄綔鐣岄潰銆傚叾涓昏鑳藉姏鍖呮嫭锛�
+ * 渚挎嵎鍦伴厤缃� `xiaozhi-server` 鎵�浣跨敤鐨勫悇椤笰I鏈嶅姟锛堝ASR銆丩LM銆乀TS鐨勬彁渚涘晢鍒囨崲銆佸弬鏁拌皟鏁达級銆�
+ * 绠$悊骞冲彴鐢ㄦ埛璐︽埛銆佽鑹插垎閰嶅強鏉冮檺鎺у埗銆�
+ * 绠$悊宸叉敞鍐岀殑ESP32璁惧鍙婂叾鐩稿叧璁剧疆銆�
+ * 锛堟綔鍦ㄥ姛鑳斤級鐩戞帶绯荤粺杩愯鐘舵�併�佹煡鐪嬫棩蹇椼�佽繘琛屾晠闅滄帓鏌ョ瓑銆�
+ * 涓� `manager-api` 鎻愪緵鐨勬墍鏈夊悗绔鐞嗗姛鑳借繘琛屽叏闈㈢殑浜や簰銆�
+
+5. **`manager-mobile` (鏅烘帶鍙扮Щ鍔ㄧ増 - uni-app瀹炵幇):**
+ 杩欐槸涓�涓熀浜巙ni-app v3 + Vue 3 + Vite鐨勮法绔Щ鍔ㄧ鐞嗙锛屾敮鎸丄pp锛圓ndroid & iOS锛夊拰寰俊灏忕▼搴忋�傚叾涓昏鑳藉姏鍖呮嫭锛�
+ * 鎻愪緵绉诲姩璁惧涓婄殑渚挎嵎绠$悊鐣岄潰锛屼笌manager-web鍔熻兘绫讳技浣嗛拡瀵圭Щ鍔ㄧ杩涜浜嗕紭鍖栥��
+ * 鏀寔鐢ㄦ埛鐧诲綍銆佽澶囩鐞嗐�丄I鏈嶅姟閰嶇疆绛夋牳蹇冨姛鑳姐��
+ * 璺ㄥ钩鍙伴�傞厤锛屼竴濂椾唬鐮佸彲鍚屾椂杩愯鍦╥OS銆丄ndroid鍜屽井淇″皬绋嬪簭涓娿��
+ * 鍩轰簬alova + @alova/adapter-uniapp瀹炵幇缃戠粶璇锋眰锛屼笌manager-api鏃犵紳闆嗘垚銆�
+ * 浣跨敤pinia杩涜鐘舵�佺鐞嗭紝纭繚鏁版嵁涓�鑷存�с��
+
+6. **`digital-human` (鏁板瓧浜烘祴璇曟ā鍧� - Python+Web瀹炵幇):**
+ 杩欐槸涓�涓嫭绔嬬殑鏁板瓧浜烘祴璇曟ā鍧楋紝鎻愪緵鏈湴娴嬭瘯椤甸潰銆佸墠绔氦浜掕祫婧愩�佸敜閱掕瘝杩愯鏃朵笌浜嬩欢妗ヨ兘鍔涳紝鐢ㄤ簬鑱旇皟鏁翠釜鏁板瓧浜轰氦浜掗摼璺�傚叾涓昏鑳藉姏鍖呮嫭锛�
+ * 鎻愪緵娴忚鍣ㄧ鐨勬暟瀛椾汉娴嬭瘯椤甸潰锛岀敤浜庨獙璇侀煶棰戞挱鏀俱�佹帴鏀跺拰浜や簰娴佺▼銆�
+ * 闆嗘垚鏈湴鍞ら啋璇嶈繍琛屾椂锛屾敮鎸佸熀浜嶴herpa-ONNX鐨勫叧閿瘝妫�娴嬨��
+ * 閫氳繃浜嬩欢妗ュ皢椤甸潰鐘舵�佷笌鏈湴杩愯鏃舵墦閫氾紝渚夸簬璋冭瘯鍞ら啋璇嶈Е鍙戜笌浜や簰鏁堟灉銆�
+ * 浣滀负鐙珛妯″潡涓巂xiaozhi-server`銆乣manager-web`銆乣manager-api`骞跺垪锛屼究浜庡崟鐙紑鍙戝拰閮ㄧ讲銆�
+
+**楂樺眰浜や簰娴佺▼姒傝堪:**
+
+* **璇煶浜や簰涓荤嚎:** **ESP32璁惧**鎹曟崏鍒扮敤鎴疯闊冲悗锛岄�氳繃**WebSocket**灏嗛煶棰戞暟鎹疄鏃朵紶杈撶粰**`xiaozhi-server`**銆俙xiaozhi-server`瀹屾垚涓�绯诲垪AI澶勭悊锛圴AD銆丄SR銆丩LM浜や簰銆乀TS锛夊悗锛屽啀閫氳繃WebSocket灏嗗悎鎴愮殑璇煶鍥炲鍙戦�佸洖ESP32璁惧杩涜鎾斁銆傛墍鏈変笌璇煶鐩存帴鐩稿叧鐨勫疄鏃朵氦浜掑潎鍦ㄦ閾捐矾瀹屾垚銆�
+* **绠$悊閰嶇疆涓荤嚎:** 绠$悊鍛橀�氳繃娴忚鍣ㄨ闂�**`manager-web`**鎺у埗鍙般�俙manager-web`閫氳繃璋冪敤**`manager-api`**鎻愪緵鐨�**RESTful HTTP鎺ュ彛**鏉ユ墽琛屽悇绉嶇鐞嗘搷浣滐紙濡備慨鏀归厤缃�佺鐞嗙敤鎴锋垨璁惧锛夈�傛暟鎹互JSON鏍煎紡鍦ㄤ袱鑰呴棿浼犻�掋��
+* **閰嶇疆鍚屾:** **`xiaozhi-server`**鍦ㄥ惎鍔ㄦ垨鐗瑰畾鏇存柊鏈哄埗瑙﹀彂鏃讹紝浼氫富鍔ㄩ�氳繃HTTP璇锋眰浠�**`manager-api`**鎷夊彇鍏舵渶鏂扮殑鎿嶄綔閰嶇疆銆傝繖纭繚浜嗙鐞嗗憳鍦╓eb鐣岄潰涓婃墍鍋氱殑閰嶇疆鏇存敼鑳藉鍙婃椂鏈夋晥鍦板簲鐢ㄥ埌鏍稿績AI寮曟搸鐨勮繍琛屼腑銆�
+
+杩欑**鍓嶅悗绔垎绂汇�佹牳蹇冩湇鍔′笌绠$悊鏈嶅姟鍒嗙**鐨勬灦鏋勮璁★紝浣垮緱 `xiaozhi-server`鑳藉涓撴敞浜庨珮鏁堢殑瀹炴椂AI澶勭悊浠诲姟锛岃�� `manager-api` 鍜� `manager-web` 鍒欏叡鍚屾彁渚涗簡涓�涓姛鑳藉己澶т笖鏄撲簬浣跨敤鐨勭鐞嗗拰閰嶇疆骞冲彴銆傚悇缁勪欢鑱岃矗娓呮櫚锛屾湁鍒╀簬鐙珛寮�鍙戙�佹祴璇曘�侀儴缃插拰鎵╁睍銆�
+
+```
+xiaozhi-esp32-server
+ 鈹溾攢 xiaozhi-server 8000 绔彛 Python璇█寮�鍙� 璐熻矗涓巈sp32閫氫俊
+ 鈹溾攢 manager-web 8001 绔彛 Node.js+Vue寮�鍙� 璐熻矗鎻愪緵鎺у埗鍙扮殑web鐣岄潰
+ 鈹溾攢 manager-api 8002 绔彛 Java璇█寮�鍙� 璐熻矗鎻愪緵鎺у埗鍙扮殑api
+ 鈹溾攢 manager-mobile 璺ㄥ钩鍙扮Щ鍔ㄥ簲鐢� uni-app+Vue3寮�鍙� 璐熻矗鎻愪緵绉诲姩鐗堟櫤鎺у彴绠$悊
+ 鈹斺攢 digital-human 鏁板瓧浜烘祴璇曟ā鍧� Python+Web瀹炵幇 璐熻矗鏈湴娴嬭瘯椤甸潰銆佸敜閱掕瘝杩愯鏃朵笌浜嬩欢妗�
+```
+
+---
+
+## 3. 鏍稿績缁勪欢娣卞害鍓栨瀽
+
+### 3.1. `xiaozhi-server` (鏍稿績AI寮曟搸 - Python瀹炵幇)
+
+`xiaozhi-server` 浣滀负绯荤粺鐨勬櫤鑳芥牳蹇冿紝鍏ㄦ潈璐熻矗澶勭悊璇煶浜や簰銆佸鎺ュ悇绫籄I鏈嶅姟浠ュ強绠$悊涓嶦SP32璁惧闂寸殑閫氫俊銆傚叾璁捐鐩爣鏄疄鐜伴珮鏁堛�佺伒娲讳笖鍙墿灞曠殑璇煶AI澶勭悊鑳藉姏銆�
+
+* **鏍稿績鐩爣:**
+ * 涓篍SP32璁惧鎻愪緵瀹炴椂鐨勮闊虫寚浠ゅ鐞嗘湇鍔°��
+ * 娣卞害闆嗘垚鍚勭被AI鏈嶅姟锛屽寘鎷細鑷姩璇煶璇嗗埆 (ASR)銆佸ぇ鍨嬭瑷�妯″瀷 (LLM) 杩涜鑷劧璇█鐞嗚В (NLU)銆佹枃鏈浆璇煶 (TTS)銆佽闊虫椿鍔ㄦ娴� (VAD)銆佹剰鍥捐瘑鍒� (Intent Recognition) 鍙婂璇濊蹇� (Memory)銆�
+ * 绮剧粏绠$悊鐢ㄦ埛涓庤澶囬棿鐨勫璇濇祦绋嬪強涓婁笅鏂囩姸鎬併��
+ * 鍩轰簬鐢ㄦ埛鎸囦护锛岄�氳繃鎻掍欢鍖栨満鍒舵墽琛岃嚜瀹氫箟鍑芥暟鍙婃帶鍒剁墿鑱旂綉 (IoT) 璁惧銆�
+ * 鏀寔閫氳繃 `manager-api`杩涜鍔ㄦ�侀厤缃姞杞戒笌鏇存柊銆�
+
+* **鏍稿績鎶�鏈爤:**
+ * **Python 3:** 浣滀负涓昏缂栫▼璇█锛孭ython浠ュ叾涓板瘜鐨凙I/ML鐢熸�佸簱鍜屽揩閫熷紑鍙戠壒鎬ц閫夌敤銆�
+ * **Asyncio:** Python鐨勫紓姝ョ紪绋嬫鏋讹紝鏄痐xiaozhi-server`楂樻�ц兘鐨勫叧閿�傚畠琚箍娉涚敤浜庨珮鏁堝鐞嗘潵鑷ぇ閲廍SP32璁惧鐨勫苟鍙慦ebSocket杩炴帴锛屼互鍙婃墽琛屼笌澶栭儴AI鏈嶅姟API閫氫俊鏃剁殑闈為樆濉濱/O鎿嶄綔锛岀‘淇濇湇鍔″櫒鍦ㄩ珮骞跺彂涓嬬殑鍝嶅簲鑳藉姏銆�
+ * **`websockets` 搴�:** 鎻愪緵WebSocket鏈嶅姟鍣ㄧ殑鍏蜂綋瀹炵幇锛屾敮鎸佷笌ESP32瀹㈡埛绔繘琛屽叏鍙屽伐瀹炴椂閫氫俊銆�
+ * **HTTP瀹㈡埛绔� (濡� `aiohttp`, `httpx`):** 鐢ㄤ簬寮傛鎵цHTTP璇锋眰锛屼富瑕佺洰鐨勬槸浠巂manager-api`鑾峰彇閰嶇疆淇℃伅锛屼互鍙婁笌浜戠AI鏈嶅姟鐨凙PI杩涜浜や簰銆�
+ * **YAML (閫氬父閫氳繃 PyYAML 搴�):** 鐢ㄤ簬瑙f瀽鏈湴鐨� `config.yaml` 閰嶇疆鏂囦欢銆�
+ * **FFmpeg (澶栭儴渚濊禆):** 鍦� `app.py` 鍚姩鏃朵細杩涜妫�鏌� (`check_ffmpeg_installed()`)銆侳Fmpeg閫氬父鐢ㄤ簬闊抽澶勭悊鍜屾牸寮忚浆鎹紝渚嬪锛岀‘淇濋煶棰戞暟鎹鍚堢壒瀹欰I鏈嶅姟鐨勮姹傛垨杩涜鍐呴儴澶勭悊銆�
+
+* **鍏抽敭瀹炵幇缁嗚妭:**
+
+ 1. **AI鏈嶅姟鎻愪緵鑰呮ā寮� (Provider Pattern - `core/providers/`):**
+ * **璁捐鎬濇兂:** 杩欐槸`xiaozhi-server`闆嗘垚涓嶅悓AI鏈嶅姟鐨勬牳蹇冭璁℃ā寮忥紝鏋佸ぇ鍦板寮轰簡绯荤粺鐨勭伒娲绘�у拰鍙墿灞曟�с�傞拡瀵规瘡涓�绉岮I鏈嶅姟绫诲瀷锛圓SR, TTS, LLM, VAD, Intent, Memory, VLLM锛夛紝閮藉湪鍏跺搴斿瓙鐩綍涓嬪畾涔変簡涓�涓娊璞″熀绫� (ABC, Abstract Base Class)锛屼緥濡� `core/providers/asr/base.py`銆傝繖涓熀绫昏瀹氫簡璇ョ被鍨嬫湇鍔″繀椤诲疄鐜扮殑閫氱敤鎺ュ彛鏂规硶锛堝ASR鐨� `async def transcribe(self, audio_chunk: bytes) -> str: pass`锛夈��
+ * **鍏蜂綋瀹炵幇:** 鍚勭鍏蜂綋鐨凙I鏈嶅姟鎻愪緵鍟嗘垨鏈湴妯″瀷鐨勫疄鐜帮紝鍒欎互鐙珛鐨凱ython绫诲舰寮忓瓨鍦紙渚嬪 `core/providers/asr/fun_local.py` 瀹炵幇浜嗘湰鍦癋unASR鐨勯�昏緫锛宍core/providers/llm/openai.py` 瀹炵幇浜嗕笌OpenAI GPT妯″瀷鐨勫鎺ワ級銆傝繖浜涘叿浣撶被缁ф壙鑷浉搴旂殑鎶借薄鍩虹被锛屽苟瀹炵幇鍏跺畾涔夌殑鎺ュ彛銆傞儴鍒嗘彁渚涜�呰繕浣跨敤DTOs (Data Transfer Objects, 瀛樺湪浜庡悇鑷殑 `dto/` 鐩綍) 鏉ョ粨鏋勫寲涓庡閮ㄦ湇鍔′氦鎹㈢殑鏁版嵁銆�
+ * **浼樺娍:** 浣垮緱鏍稿績涓氬姟閫昏緫鑳藉浠ョ粺涓�鐨勬柟寮忚皟鐢ㄤ笉鍚岀殑AI鏈嶅姟锛岃�屾棤闇�鍏冲績鍏跺簳灞傚叿浣撳疄鐜般�傜敤鎴峰彲浠ラ�氳繃閰嶇疆鏂囦欢杞绘澗鍒囨崲AI鏈嶅姟鍚庣銆傛坊鍔犲鏂癆I鏈嶅姟鐨勬敮鎸佷篃鍙樺緱鐩稿绠�鍗曪紝鍙渶瀹炵幇瀵瑰簲鐨凱rovider鎺ュ彛銆�
+ * **鍔ㄦ�佸姞杞戒笌鍒濆鍖�:** `core/utils/modules_initialize.py` 鑴氭湰鎵紨浜嗗伐鍘傜殑瑙掕壊銆傚畠鍦ㄦ湇鍔″櫒鍚姩鏃讹紝鎴栧湪鎺ユ敹鍒伴厤缃洿鏂版寚浠ゆ椂锛屼細鏍规嵁閰嶇疆鏂囦欢涓� `selected_module` 鍙婂悇椤规湇鍔$殑鍏蜂綋provider璁剧疆锛屽姩鎬佸湴瀵煎叆骞跺疄渚嬪寲鐩稿簲鐨凱rovider绫汇��
+
+ 2. **WebSocket閫氫俊涓庤繛鎺ュ鐞� (`app.py`, `core/websocket_server.py`, `core/connection.py`):**
+ * **鏈嶅姟鍣ㄥ惎鍔ㄤ笌鍏ュ彛 (`app.py`):**
+ * `app.py` 浣滀负涓诲叆鍙o紝璐熻矗鍒濆鍖栧簲鐢ㄧ幆澧冿紙濡傛鏌Fmpeg銆佸姞杞介厤缃�佽缃棩蹇楋級銆�
+ * 瀹冧細鐢熸垚鎴栧姞杞戒竴涓� `auth_key` (JWT瀵嗛挜)锛岀敤浜庝繚鎶ょ壒瀹氱殑HTTP鎺ュ彛锛堝瑙嗚鍒嗘瀽鎺ュ彛 `/mcp/vision/explain`锛夈�傝嫢閰嶇疆涓� `manager-api.secret` 涓虹┖锛屽垯浼氱敓鎴愪竴涓猆UID浣滀负 `auth_key`銆�
+ * 浣跨敤 `asyncio.create_task()` 骞跺彂鍚姩 `WebSocketServer` (鐩戝惉濡� `ws://0.0.0.0:8000/xiaozhi/v1/`) 鍜� `SimpleHttpServer` (鐩戝惉濡� `http://0.0.0.0:8003/xiaozhi/ota/`)銆�
+ * 鍖呭惈涓�涓� `monitor_stdin()` 鍗忕▼锛岀敤浜庡湪鏌愪簺鐜涓嬩繚鎸佸簲鐢ㄥ瓨娲绘垨澶勭悊缁堢杈撳叆銆�
+ * **WebSocket鏈嶅姟鍣ㄦ牳蹇� (`core/websocket_server.py`):**
+ * `WebSocketServer` 绫讳娇鐢� `websockets` 搴撶洃鍚潵鑷狤SP32璁惧鐨勮繛鎺ヨ姹傘��
+ * 瀵逛簬姣忎竴涓垚鍔熺殑WebSocket杩炴帴锛屽畠閮戒細鍒涘缓涓�涓�**鐙珛鐨� `ConnectionHandler` 瀹炰緥** (鎺ㄦ祴瀹氫箟浜� `core/connection.py`)銆傝繖绉嶆瘡涓繛鎺ヤ竴涓鐞嗙▼搴忓疄渚嬬殑璁捐妯″紡锛屾槸瀹炵幇澶氳澶囩姸鎬侀殧绂诲拰骞跺彂澶勭悊鐨勫叧閿紝纭繚姣忎釜璁惧鐨勫璇濇祦绋嬪拰涓婁笅鏂囦俊鎭簰涓嶅共鎵般��
+ * 璇ユ湇鍔″櫒杩樻彁渚涗竴涓� `_http_response` 鏂规硶锛屽厑璁稿湪鍚屼竴绔彛涓婂闈濿ebSocket鍗囩骇鐨凥TTP GET璇锋眰鍋氬嚭绠�鍗曞搷搴旓紙渚嬪杩斿洖 "Server is running"锛夛紝渚夸簬杩涜鍋ュ悍妫�鏌ャ��
+ * **鍔ㄦ�侀厤缃洿鏂�:** `WebSocketServer` 鍖呭惈涓�涓� `update_config()` 寮傛鏂规硶銆傛鏂规硶浣跨敤 `config_lock` (涓�涓� `asyncio.Lock`) 淇濊瘉閰嶇疆鏇存柊鐨勫師瀛愭�с�傚畠璋冪敤 `get_config_from_api()` (鍙兘鍦� `config_loader.py` 涓疄鐜帮紝閫氳繃 `manage_api_client.py` 涓� `manager-api` 閫氫俊) 鏉ヨ幏鍙栨柊鐨勯厤缃�傞�氳繃 `check_vad_update()` 鍜� `check_asr_update()` 绛夎緟鍔╁嚱鏁板垽鏂槸鍚﹂渶瑕侀噸鏂板垵濮嬪寲鐗瑰畾鐨凙I妯″潡锛岄伩鍏嶄笉蹇呰鐨勫紑閿�銆傛洿鏂板悗鐨勯厤缃細鐢ㄤ簬閲嶆柊璋冪敤 `initialize_modules()`锛屼粠鑰屽疄鐜癆I鏈嶅姟鎻愪緵鑰呯殑鐑垏鎹€��
+
+ 3. **娑堟伅澶勭悊涓庡璇濇祦绋嬫帶鍒� (`core/handle/` 鍜� `ConnectionHandler`):**
+ * `ConnectionHandler` (鎺ㄦ祴) 浣滀负姣忎釜杩炴帴鐨勬帶鍒朵腑蹇冿紝璐熻矗鎺ユ敹鏉ヨ嚜ESP32鐨勬秷鎭紝骞舵牴鎹秷鎭被鍨嬫垨褰撳墠瀵硅瘽鐘舵�侊紝灏嗗叾鍒嗗彂缁� `core/handle/` 鐩綍涓嬬殑鐩稿簲澶勭悊妯″潡銆傝繖绉嶆ā鍧楀寲鐨勫鐞嗗櫒璁捐浣垮緱 `ConnectionHandler` 閫昏緫鏇存竻鏅帮紝鏄撲簬鎵╁睍銆�
+ * **涓昏澶勭悊妯″潡鍙婂叾鑱岃矗:**
+ * `helloHandle.py`: 澶勭悊涓嶦SP32鍒濇杩炴帴鏃剁殑鎻℃墜鍗忚銆佽澶囪璇佹垨鍒濆鍖栦俊鎭氦鎹€��
+ * `receiveAudioHandle.py`: 鎺ユ敹闊抽娴佹暟鎹紝璋冪敤VAD Provider杩涜璇煶娲诲姩妫�娴嬶紝骞跺皢鏈夋晥鐨勯煶棰戠墖娈典紶閫掔粰ASR Provider杩涜璇嗗埆銆�
+ * `textHandle.py` / `intentHandler.py`: 鑾峰彇ASR璇嗗埆鍑虹殑鏂囨湰鍚庯紝涓嶪ntent Provider (鍙兘鍒╃敤LLM杩涜鎰忓浘璇嗗埆) 鍜孡LM Provider浜や簰锛屼互鐞嗚В鐢ㄦ埛鎰忓浘骞剁敓鎴愬垵姝ュ洖澶嶆垨鍐崇瓥銆�
+ * `functionHandler.py`: 褰揕LM鐨勫搷搴斿寘鍚墽琛岀壒瀹氣�滃嚱鏁拌皟鐢ㄢ�濈殑鎸囦护鏃讹紝姝ゆā鍧楄礋璐d粠鎻掍欢娉ㄥ唽琛ㄤ腑鏌ユ壘骞舵墽琛屽搴旂殑鎻掍欢鍑芥暟銆�
+ * `sendAudioHandle.py`: 灏哃LM鏈�缁堢敓鎴愮殑鏂囨湰鍥炲浜ょ粰TTS Provider鍚堟垚璇煶锛屽苟灏嗛煶棰戞祦閫氳繃WebSocket鍙戦�佸洖ESP32銆�
+ * `abortHandle.py`: 澶勭悊鏉ヨ嚜ESP32鐨勪腑鏂姹傦紝渚嬪鍋滄褰撳墠鐨凾TS鎾姤銆�
+ * `iotHandle.py`, `mcpHandle.py`: 澶勭悊涓嶪oT璁惧鎺у埗鐩稿叧鐨勭壒瀹氭寚浠ゆ垨鏇村鏉傜殑妯″潡閫氫俊鍗忚 (MCP)銆�
+
+ 4. **鎻掍欢鍖栧姛鑳芥墿灞曠郴缁� (`plugins_func/`):**
+ * **璁捐鐩殑:** 鎻愪緵涓�绉嶆爣鍑嗗寲鐨勬柟寮忔潵鎵╁睍璇煶鍔╂墜鐨勫姛鑳藉拰鈥滄妧鑳解�濓紝鑰屾棤闇�淇敼鏍稿績浠g爜銆�
+ * **瀹炵幇鏈哄埗:**
+ * 鍚勪釜鍏蜂綋鍔熻兘浠ョ嫭绔嬬殑Python鑴氭湰褰㈠紡瀛樺湪浜� `plugins_func/functions/` 鐩綍涓紙渚嬪 `get_weather.py`, `hass_set_state.py` 鐢ㄤ簬Home Assistant闆嗘垚锛夈��
+ * `loadplugins.py` 鍦ㄦ湇鍔″櫒鍚姩鏃惰礋璐f壂鎻忓苟鍔犺浇杩欎簺鎻掍欢妯″潡銆�
+ * `register.py` (鎴栨彃浠舵ā鍧楀唴閮ㄧ殑鐗瑰畾瑁呴グ鍣�/鍑芥暟) 鍙兘鐢ㄤ簬瀹氫箟姣忎釜鎻掍欢鍑芥暟鐨勫厓鏁版嵁锛屽寘鎷細
+ * **鍑芥暟鍚嶇О (Function Name):** LLM璋冪敤鏃朵娇鐢ㄧ殑鏍囪瘑绗︺��
+ * **鍔熻兘鎻忚堪 (Description):** 渚汱LM鐞嗚В姝ゅ嚱鏁扮殑浣滅敤銆�
+ * **鍙傛暟妯″紡 (Parameters Schema):** 閫氬父鏄竴涓狫SON Schema锛岃缁嗗畾涔変簡鍑芥暟鎵�闇�鐨勫弬鏁般�佺被鍨嬨�佹槸鍚﹀繀闇�浠ュ強鎻忚堪銆傝繖鏄疞LM鑳藉姝g‘鐢熸垚鍑芥暟璋冪敤鍙傛暟鐨勫叧閿��
+ * **鎵ц娴佺▼:** 褰揕LM鍦ㄥ叾鎬濊�冭繃绋嬩腑鍐冲畾闇�瑕佽皟鐢ㄦ煇涓閮ㄥ伐鍏锋垨鍑芥暟鏉ヨ幏鍙栦俊鎭垨鎵ц鎿嶄綔鏃讹紝瀹冧細渚濇嵁棰勫厛鎻愪緵鐨勫嚱鏁版ā寮忕敓鎴愪竴涓粨鏋勫寲鐨勨�滃嚱鏁拌皟鐢ㄢ�濊姹傘�俙xiaozhi-server`涓殑`functionHandler.py`鎹曡幏姝よ姹傦紝浠庢彃浠舵敞鍐岃〃涓壘鍒板搴旂殑Python鍑芥暟骞舵墽琛岋紝鐒跺悗灏嗘墽琛岀粨鏋滆繑鍥炵粰LLM锛孡LM鍐嶅熀浜庢缁撴灉鐢熸垚鏈�缁堢粰鐢ㄦ埛鐨勮嚜鐒惰瑷�鍥炲銆�
+
+ 5. **閰嶇疆绠$悊 (`config/`):**
+ * **鍔犺浇鏈哄埗:** `config_loader.py` (閫氳繃 `settings.py` 琚皟鐢�) 璐熻矗浠庢牴鐩綍鐨� `config.yaml` 鏂囦欢鍔犺浇鍩虹閰嶇疆銆�
+ * **杩滅▼閰嶇疆涓庡悎骞�:** 閫氳繃 `manage_api_client.py` (浣跨敤濡俙aiohttp`鐨勫簱涓巂manager-api`閫氫俊) 鍙互浠巂manager-api`鏈嶅姟鎷夊彇閰嶇疆銆傝繙绋嬮厤缃�氬父浼氳鐩栨湰鍦� `config.yaml` 涓殑鍚屽悕璁剧疆锛屼粠鑰屽疄鐜伴�氳繃Web鐣岄潰鍔ㄦ�佽皟鏁存湇鍔″櫒琛屼负銆�
+ * **鏃ュ織绯荤粺:** `logger.py` 鍒濆鍖栧簲鐢ㄦ棩蹇楃郴缁燂紙鍙兘浣跨敤 `loguru` 鎴栧鏍囧噯 `logging` 妯″潡杩涜灏佽锛屾敮鎸侀�氳繃 `logger.bind(tag=TAG)` 娣诲姞鏍囩锛屼究浜庤拷韪拰杩囨护锛夈��
+ * **闈欐�佽祫婧�:** `config/assets/` 鐩綍涓嬪瓨鏀句簡鐢ㄤ簬绯荤粺鎻愮ず闊崇殑闈欐�侀煶棰戞枃浠讹紙濡傝澶囩粦瀹氭彁绀洪煶 `bind_code.wav`銆侀敊璇彁绀洪煶绛夛級銆�
+
+ 6. **杈呭姪HTTP鏈嶅姟 (`core/http_server.py`):**
+ * 涓嶹ebSocket鏈嶅姟骞惰杩愯涓�涓畝鍗曠殑HTTP鏈嶅姟鍣紝鐢ㄤ簬澶勭悊鐗瑰畾鐨凥TTP璇锋眰銆傛渶涓昏鐨勫姛鑳芥槸涓篍SP32璁惧鎻愪緵OTA (Over-The-Air) 鍥轰欢鏇存柊鐨勪笅杞芥湇鍔� (閫氳繃 `/xiaozhi/ota/` 绔偣)銆傛澶栵紝涔熷彲鑳芥壙杞藉叾浠栧 `/mcp/vision/explain` (瑙嗚鍒嗘瀽) 绛夊伐鍏锋�TTP鎺ュ彛銆�
+
+缁间笂鎵�杩帮紝`xiaozhi-server` 鏄竴涓噰鐢ㄧ幇浠ython寮傛缂栫▼妯″瀷鏋勫缓鐨勩�侀珮搴︽ā鍧楀寲銆侀厤缃┍鍔ㄧ殑AI搴旂敤鏈嶅姟鍣ㄣ�傚叾绮惧績璁捐鐨凱rovider妯″紡鍜屾彃浠舵灦鏋勮祴浜堜簡瀹冨己澶х殑閫傚簲鎬у拰鎵╁睍鎬э紝鑳藉鐏垫椿鎺ュ叆涓嶅悓鐨凙I鑳藉姏骞舵敮鎸佹棩鐩婂闀跨殑鍔熻兘闇�姹傘��
+
+---
+
+### 3.2. `manager-api` (绠$悊鍚庣 - Java Spring Boot瀹炵幇)
+
+`manager-api` 缁勪欢鏄娇鐢↗ava鍜孲pring Boot妗嗘灦鏋勫缓鐨勫己澶у悗绔湇鍔★紝浣滀负鏁翠釜`xiaozhi-esp32-server`鐢熸�佺郴缁熺殑涓ぎ琛屾斂绠$悊鍜岄厤缃腑鏋€��
+
+* **鏍稿績鐩爣:**
+ * 涓篳manager-web`锛圴ue.js鍓嶇锛夋彁渚涗竴濂楀畨鍏ㄣ�佺ǔ瀹氥�佺鍚圧ESTful瑙勮寖鐨凙PI鎺ュ彛锛屼娇寰楃鐞嗗憳鑳藉渚挎嵎鍦扮鐞嗙敤鎴枫�佽澶囥�佺郴缁熼厤缃強鍏朵粬鐩稿叧璧勬簮銆�
+ * 鍏呭綋`xiaozhi-server`锛圥ython鏍稿績AI寮曟搸锛夌殑闆嗕腑鍖栭厤缃暟鎹彁渚涜�咃紝鍏佽`xiaozhi-server`瀹炰緥鍦ㄥ惎鍔ㄦ垨杩愯鏃惰幏鍙栧叾鏈�鏂扮殑鎿嶄綔鍙傛暟銆�
+ * 鎸佷箙鍖栧瓨鍌ㄥ叧閿暟鎹紝渚嬪锛氱敤鎴疯处鎴蜂俊鎭�佽澶囨敞鍐岃鎯呫�丄I鏈嶅姟鎻愪緵鍟嗛厤缃紙鍖呮嫭API瀵嗛挜銆侀�夊畾鐨勬湇鍔℃ā鍨嬬瓑锛夈�乀TS闊宠壊鍙傛暟锛屼互鍙奜TA鍥轰欢鐗堟湰淇℃伅绛夈��
+
+* **鏍稿績鎶�鏈爤:**
+ * **Java 21:** 椤圭洰閲囩敤鐨凧DK鐗堟湰锛岀‘淇濅簡瀵圭幇浠ava鐗规�х殑鏀寔銆�
+ * **Spring Boot 3:** 浣滀负鏍稿績寮�鍙戞鏋讹紝鏋佸ぇ鍦扮畝鍖栦簡鐙珛銆佺敓浜х骇鍒殑Spring搴旂敤鐨勫垱寤哄拰閮ㄧ讲銆傚畠鎻愪緵浜嗚嚜鍔ㄩ厤缃�佸唴宓學eb鏈嶅姟鍣紙榛樿涓篢omcat锛夈�佷緷璧栫鐞嗙瓑鍏抽敭鍔熻兘銆�
+ * **Spring MVC:** Spring妗嗘灦涓敤浜庢瀯寤篧eb搴旂敤鍜孯ESTful API鐨勬ā鍧椼��
+ * **MyBatis-Plus:** 涓�涓MyBatis杩涜鍔熻兘澧炲己鐨凮RM锛堝璞″叧绯绘槧灏勶級妗嗘灦銆傚畠绠�鍖栦簡鏁版嵁搴撴搷浣滐紝鎻愪緵浜嗗己澶х殑CRUD锛堝鍒犳敼鏌ワ級鍔熻兘銆佹潯浠舵瀯閫犲櫒銆佷唬鐮佺敓鎴愬櫒绛夛紝骞惰兘寰堝ソ鍦颁笌Spring Boot闆嗘垚銆�
+ * **MySQL:** 浣滀负涓昏鐨勫悗绔叧绯诲瀷鏁版嵁搴擄紝鐢ㄤ簬瀛樺偍鎵�鏈夐渶瑕佹寔涔呭寲鐨勭鐞嗘暟鎹拰閰嶇疆淇℃伅銆�
+ * **Druid (Alibaba Druid):** 涓�涓姛鑳藉己澶х殑JDBC杩炴帴姹犲疄鐜帮紝鎻愪緵浜嗕赴瀵岀殑鐩戞帶鍔熻兘鍜屼紭绉�鐨勬�ц兘锛岀敤浜庨珮鏁堢鐞嗘暟鎹簱杩炴帴銆�
+ * **Redis (閫氳繃 Spring Data Redis):** 涓�涓珮鎬ц兘鐨勫唴瀛樻暟鎹粨鏋勫瓨鍌紝甯哥敤浜庡疄鐜版暟鎹紦瀛橈紙渚嬪缂撳瓨鐑偣閰嶇疆鏁版嵁銆佺敤鎴蜂細璇濅俊鎭級锛屼互鏄捐憲鎻愬崌API鐨勫搷搴旈�熷害銆�
+ * **Apache Shiro:** 涓�涓垚鐔熶笖鏄撶敤鐨凧ava瀹夊叏妗嗘灦锛岃礋璐e鐞嗗簲鐢ㄧ殑璁よ瘉锛堢敤鎴疯韩浠介獙璇侊級鍜屾巿鏉冿紙API璁块棶鏉冮檺鎺у埗锛夐渶姹傘��
+ * **Liquibase:** 涓�涓敤浜庤窡韪�佺鐞嗗拰搴旂敤鏁版嵁搴� sch茅ma锛堟ā寮忥級鍙樻洿鐨勫紑婧愬伐鍏枫�傚畠鍏佽寮�鍙戣�呬互鏁版嵁搴撴棤鍏崇殑鏂瑰紡瀹氫箟鍜岀増鏈寲鏁版嵁搴撶粨鏋勫彉鏇淬��
+ * **Knife4j:** 涓�涓泦鎴愪簡Swagger骞跺寮轰簡UI鐨凙PI鏂囨。鐢熸垚宸ュ叿锛屼笓涓篔ava MVC妗嗘灦锛堝挨鍏舵槸Spring Boot锛夎璁°�傚畠鑳界敓鎴愮編瑙備笖鏄撲簬浜や簰鐨凙PI鏂囨。鐣岄潰锛堥�氬父閫氳繃 `/xiaozhi/doc.html` 璁块棶锛夈��
+ * **Maven:** 鐢ㄤ簬椤圭洰鐨勬瀯寤鸿嚜鍔ㄥ寲鍜屼緷璧栭」绠$悊銆�
+ * **Lombok:** 涓�涓狫ava搴擄紝閫氳繃娉ㄨВ鑷姩鐢熸垚鏋勯�犲嚱鏁般�乬etter/setter銆乪quals/hashCode銆乼oString绛夋牱鏉夸唬鐮侊紝鍑忓皯鍐椾綑銆�
+ * **HuTool / Google Guava:** 鎻愪緵澶ч噺瀹炵敤宸ュ叿绫伙紝绠�鍖栧父瑙佺紪绋嬩换鍔°��
+ * **Aliyun Dysmsapi:** 闃块噷浜戠煭淇℃湇鍔DK锛岀敤浜庨泦鎴愬彂閫佺煭淇″姛鑳斤紙濡傞獙璇佺爜銆侀�氱煡锛夈��
+
+* **鍏抽敭瀹炵幇缁嗚妭:**
+
+ 1. **妯″潡鍖栭」鐩粨鏋� (`modules/` 鍖�):**
+ * `manager-api` 鐨勬牳蹇冧笟鍔¢�昏緫琚竻鏅板湴鍒掑垎鍒� `src/main/java/xiaozhi/modules/` 鐩綍涓嬬殑涓嶅悓妯″潡涓�傝繖绉嶆寜鍔熻兘棰嗗煙鍒掑垎妯″潡鐨勬柟寮忥紙渚嬪 `sys` 璐熻矗绯荤粺绠$悊锛宍agent` 璐熻矗鏅鸿兘浣撻厤缃紝`device` 璐熻矗璁惧绠$悊锛宍config` 璐熻矗涓篳xiaozhi-server`鎻愪緵閰嶇疆锛宍security` 璐熻矗瀹夊叏锛宍timbre` 璐熻矗闊宠壊绠$悊锛宍ota` 璐熻矗鍥轰欢鍗囩骇锛夋瀬澶у湴鎻愰珮浜嗕唬鐮佺殑鍙淮鎶ゆ�у拰鍙墿灞曟�с��
+ * **鍚勬ā鍧楀唴閮ㄧ粨鏋�:** 姣忎釜涓氬姟妯″潡閫氬父閬靛惊缁忓吀鐨勪笁灞傛灦鏋勬垨鍏跺彉浣擄細
+ * **Controller (鎺у埗灞�):** 浣嶄簬 `xiaozhi.modules.[妯″潡鍚峕.controller`銆�
+ * **Service (鏈嶅姟灞�):** 浣嶄簬 `xiaozhi.modules.[妯″潡鍚峕.service`銆�
+ * **DAO/Mapper (鏁版嵁璁块棶灞�):** 浣嶄簬 `xiaozhi.modules.[妯″潡鍚峕.dao`銆�
+ * **Entity (瀹炰綋绫�):** 浣嶄簬 `xiaozhi.modules.[妯″潡鍚峕.entity`銆�
+ * **DTO (鏁版嵁浼犺緭瀵硅薄):** 浣嶄簬 `xiaozhi.modules.[妯″潡鍚峕.dto`銆�
+
+ 2. **鍒嗗眰鏋舵瀯瀹炵幇:**
+ * **Controller灞� (`@RestController`):** 杩欎簺绫讳娇鐢⊿pring MVC娉ㄨВ锛堝 `@GetMapping`, `@PostMapping` 绛夛級鏉ュ畾涔堿PI鐨勭鐐�(endpoints)銆傚畠浠礋璐f帴鏀禜TTP璇锋眰锛屽皢璇锋眰浣撲腑鐨凧SON鏁版嵁鍙嶅簭鍒楀寲涓篋TO瀵硅薄锛岃皟鐢ㄧ浉搴旂殑Service灞傛柟娉曞鐞嗕笟鍔¢�昏緫锛屾渶鍚庡皢Service灞傜殑杩斿洖缁撴灉搴忓垪鍖栦负JSON骞朵綔涓篐TTP鍝嶅簲杩斿洖缁欏鎴风銆�
+ * **Service灞� (`@Service`):** 杩欎簺绫伙紙閫氬父鏄帴鍙e強鍏跺疄鐜扮被鐨勭粍鍚堬級灏佽浜嗘牳蹇冪殑涓氬姟瑙勫垯鍜屾搷浣滄祦绋嬨�傚畠浠彲鑳戒細璋冪敤涓�涓垨澶氫釜DAO/Mapper瀵硅薄鏉ヤ笌鏁版嵁搴撲氦浜掞紝骞跺父甯镐娇鐢� `@Transactional` 娉ㄨВ鏉ョ鐞嗘暟鎹簱浜嬪姟鐨勫師瀛愭�с��
+ * **Data Access (DAO/Mapper) 灞� (MyBatis-Plus Mappers):** 杩欎簺鏄疛ava鎺ュ彛锛岀户鎵胯嚜MyBatis-Plus鎻愪緵鐨� `BaseMapper<Entity>` 鎺ュ彛銆侻yBatis-Plus浼氫负杩欎簺鎺ュ彛鑷姩鎻愪緵鏍囧噯鐨凜RUD鏂规硶銆傚浜庢洿澶嶆潅鐨勬暟鎹簱鏌ヨ锛屽紑鍙戣�呭彲浠ラ�氳繃鍦∕apper鎺ュ彛涓畾涔夋柟娉曞苟浣跨敤娉ㄨВ锛堝 `@Select`, `@Update`锛夋垨缂栧啓瀵瑰簲鐨刋ML鏄犲皠鏂囦欢鏉ュ疄鐜般�備緥濡傦紝`UserMapper.selectById(userId)` 浼氳MyBatis-Plus鑷姩瀹炵幇銆�
+ * **Entity灞� (`@TableName`, `@TableId` 绛塎yBatis-Plus娉ㄨВ):** 杩欎簺POJO锛圥lain Old Java Objects锛夌被鐩存帴鏄犲皠鍒版暟鎹簱涓殑琛ㄧ粨鏋勩�侺ombok鐨� `@Data` 娉ㄨВ甯哥敤浜庤嚜鍔ㄧ敓鎴恎etter/setter绛夈��
+ * **DTO灞�:** 鐢ㄤ簬鍦ㄥ悇灞備箣闂达紝鐗瑰埆鏄疌ontroller灞備笌Service灞備箣闂达紝浠ュ強API鐨勮姹�/鍝嶅簲浣撲腑浼犻�掓暟鎹�備娇鐢―TO鏈夊姪浜庤В鑰PI鎺ュ彛鐨勬暟鎹粨鏋勪笌鏁版嵁搴撳疄浣撶殑鏁版嵁缁撴瀯锛屼娇API鏇寸ǔ瀹氥��
+
+ 3. **閫氱敤鍔熻兘涓庨厤缃� (`common/` 鍖�):**
+ * `src/main/java/xiaozhi/common/` 鍖呮彁渚涗簡涓�绯诲垪璺ㄦā鍧楀叡浜殑閫氱敤缁勪欢鍜岄厤缃細
+ * **鍩虹被:** 濡� `BaseDao`, `BaseEntity`, `BaseService`, `CrudService`锛屼负鍚勬ā鍧楃殑鐩稿簲缁勪欢鎻愪緵閫氱敤鐨勫睘鎬ф垨鏂规硶銆�
+ * **鍏ㄥ眬閰嶇疆:** 鍖呮嫭 `MybatisPlusConfig` (MyBatis-Plus鐨勯厤缃紝濡傚垎椤垫彃浠躲�佹暟鎹潈闄愭彃浠剁瓑)銆乣RedisConfig` (Redis杩炴帴鍙婂簭鍒楀寲閰嶇疆)銆乣SwaggerConfig` (Knife4j鐨勯厤缃�)銆乣AsyncConfig` (寮傛浠诲姟鎵ц鍣ㄩ厤缃�)銆�
+ * **鑷畾涔夋敞瑙�:** 渚嬪 `@LogOperation` 鐢ㄤ簬閫氳繃AOP璁板綍鎿嶄綔鏃ュ織锛宍@DataFilter` 鍙兘鐢ㄤ簬瀹炵幇鏁版嵁鑼冨洿杩囨护銆�
+ * **AOP鍒囬潰:** 濡� `RedisAspect` 鍙兘鐢ㄤ簬瀹炵幇鏂规硶绾у埆鐨勭紦瀛橀�昏緫銆�
+ * **鍏ㄥ眬寮傚父澶勭悊:** `RenExceptionHandler` (浣跨敤 `@ControllerAdvice` 娉ㄨВ) 鎹曡幏搴旂敤涓姏鍑虹殑鐗瑰畾鎴栨墍鏈夊紓甯� (濡傝嚜瀹氫箟鐨� `RenException`)锛屽苟杩斿洖缁熶竴鏍煎紡鐨凧SON閿欒鍝嶅簲缁欏鎴风銆俙ErrorCode` 瀹氫箟浜嗘爣鍑嗗寲鐨勯敊璇爜銆�
+ * **宸ュ叿绫�:** 鎻愪緵浜嗘棩鏈熻浆鎹€�丣SON澶勭悊(Jackson)銆両P鍦板潃鑾峰彇銆丠TTP涓婁笅鏂囨搷浣溿�佺粺涓�缁撴灉灏佽 (`Result` 绫�)绛夊绉嶅疄鐢ㄥ伐鍏枫��
+ * **鏍¢獙宸ュ叿:** `ValidatorUtils` 鍜� `AssertUtils` 鐢ㄤ簬绠�鍖栧弬鏁版牎楠岄�昏緫銆�
+ * **XSS闃叉姢:** `XssFilter` 绛夌粍浠剁敤浜庨槻姝㈣法绔欒剼鏈敾鍑汇��
+ * **MyBatis-Plus鑷姩濉厖:** `FieldMetaObjectHandler` 鐢ㄤ簬鍦ㄦ墽琛屾彃鍏ユ垨鏇存柊鏁版嵁搴撴搷浣滄椂锛岃嚜鍔ㄥ~鍏呭 `createTime`, `updateTime` 绛夊叕鍏卞瓧娈点��
+
+ 4. **瀹夊叏鏈哄埗 (Apache Shiro):**
+ * Shiro鐨勯厤缃紙閫氬父鍦� `modules/security/config/` 鎴� `common/config/` 涓嬶級瀹氫箟浜嗗浣曡繘琛岀敤鎴疯璇佸拰鎺堟潈銆�
+ * **Realms (鍩�):** 鑷畾涔夌殑Shiro Realm绫昏礋璐d粠鏁版嵁搴撲腑鏌ヨ鐢ㄦ埛淇℃伅锛堢敤鎴峰悕銆佸瘑鐮併�佺洂鍊硷級杩涜韬唤楠岃瘉锛屼互鍙婅幏鍙栫敤鎴风殑瑙掕壊鍜屾潈闄愪俊鎭敤浜庢巿鏉冨喅绛栥��
+ * **Filters (杩囨护鍣�):** Shiro杩囨护鍣ㄩ摼琚簲鐢ㄤ簬淇濇姢API绔偣锛岀‘淇濆彧鏈夌粡杩囪璇佷笖鎷ユ湁瓒冲鏉冮檺鐨勭敤鎴锋墠鑳借闂壒瀹氳祫婧愩��
+ * **Session/Token Management:** Shiro绠$悊鐢ㄦ埛浼氳瘽銆傚浜嶳ESTful API锛屽彲鑳界粨鍚圤Auth2鎴朖WT绛変护鐗屾満鍒跺疄鐜版棤鐘舵�佽璇併��
+
+ 5. **鏁版嵁搴撶増鏈帶鍒� (Liquibase):**
+ * 鏁版嵁搴撶殑琛ㄧ粨鏋勩�佺储寮曘�佸垵濮嬫暟鎹瓑鍙樻洿锛岄兘閫氳繃Liquibase鐨� `changelog` 鏂囦欢锛堥�氬父鏄疿ML鏍煎紡锛夎繘琛屽畾涔夊拰鐗堟湰鍖栫鐞嗐�傚綋搴旂敤鍚姩鏃讹紝Liquibase浼氳嚜鍔ㄦ鏌ュ苟搴旂敤蹇呰鐨勬暟鎹簱缁撴瀯鏇存柊锛岀‘淇濆紑鍙戙�佹祴璇曞拰鐢熶骇鐜鏁版嵁搴撶粨鏋勭殑涓�鑷存�с��
+
+ 6. **API鏂囨。:**
+ * 瀹屾暣鐨凙PI鎺ュ彛鏂囨。鍙�氳繃浠ヤ笅鍦板潃璁块棶: https://2662r3426b.vicp.fun/xiaozhi/doc.html
+ * 璇ユ枃妗d娇鐢↘nife4j鐢熸垚,鎻愪緵浜嗘墍鏈塕ESTful API绔偣鐨勮缁嗚鏄庛�佽姹�/鍝嶅簲绀轰緥浠ュ強鍦ㄧ嚎娴嬭瘯鍔熻兘銆�
+
+`manager-api` 閫氳繃杩欎簺绮惧績閫夋嫨鐨勬妧鏈拰璁捐妯″紡锛屾瀯寤轰簡涓�涓姛鑳藉叏闈€�佺粨鏋勬竻鏅般�佸畨鍏ㄥ彲闈犱笖鏄撲簬缁存姢鍜屾墿灞曠殑Java鍚庣鏈嶅姟銆傚叾妯″潡鍖栫殑璁捐鐗瑰埆閫傚悎澶勭悊鍏锋湁澶氱绠$悊鍔熻兘闇�姹傜殑澶嶆潅绯荤粺銆�
+
+---
+
+### 3.3. `manager-web` (Web绠$悊鍓嶇 - Vue.js瀹炵幇)
+
+`manager-web` 缁勪欢鏄竴涓噰鐢� Vue.js 2 妗嗘灦鏋勫缓鐨勫崟椤靛簲鐢� (SPA - Single Page Application)銆傚畠涓虹郴缁熺鐞嗗憳鎻愪緵浜嗕竴涓姛鑳戒赴瀵屻�佷氦浜掑弸濂界殑鍥惧舰鐢ㄦ埛鐣岄潰锛岀敤浜庡叏闈㈢鐞嗗拰閰嶇疆 `xiaozhi-esp32-server` 鐢熸�佺郴缁熴��
+
+* **鏍稿績鐩爣:**
+ * 鎻愪緵涓�涓熀浜嶹eb鐨勯泦涓紡鎺у埗闈㈡澘锛屼緵绠$悊鍛樿繘琛岀郴缁熸搷浣滀笌鐩戞帶銆�
+ * 瀹炵幇瀵� `xiaozhi-server` 涓瑼I鏈嶅姟鎻愪緵鍟嗭紙ASR銆丩LM銆乀TS绛夛級鍙婂叾鐩稿叧API瀵嗛挜鎴栬鍙厤缃殑渚挎嵎绠$悊銆�
+ * 鏀寔鐢ㄦ埛璐︽埛銆佽鑹插強鏉冮檺鐨勭簿缁嗗寲绠$悊銆�
+ * 鎻愪緵ESP32璁惧鐨勬敞鍐屻�侀厤缃強鐘舵�佹煡鐪嬪姛鑳姐��
+ * 鍏佽绠$悊鍛樿嚜瀹氫箟TTS闊宠壊銆佺鐞哋TA鍥轰欢鏇存柊娴佺▼銆佽皟鏁寸郴缁熺骇鍙傛暟鍙婂瓧鍏告暟鎹瓑銆�
+ * 浣滀负 `manager-api` 鎵�鏆撮湶鍚勯」鍔熻兘鐨勫浘褰㈠寲浜や簰鍓嶇銆�
+
+* **鏍稿績鎶�鏈爤:**
+ * **Vue.js 2:** 涓�涓笎杩涘紡鐨凧avaScript妗嗘灦锛岀敤浜庢瀯寤虹敤鎴风晫闈€�傚叾鏍稿績鐗规�у寘鎷0鏄庡紡娓叉煋銆佺粍浠跺寲绯荤粺銆佹暟鎹粦瀹氱瓑锛岄潪甯搁�傚悎鏋勫缓澶嶆潅鐨凷PA銆�
+ * **Vue CLI (`@vue/cli-service`):** Vue.js鐨勫畼鏂瑰懡浠よ宸ュ叿锛岀敤浜庨」鐩殑蹇�熸惌寤恒�佸紑鍙戞湇鍔″櫒鐨勮繍琛岋紙鏀寔鐑ā鍧楁浛鎹MR锛夈�佷互鍙婄敓浜х幆澧冩瀯寤烘墦鍖咃紙鍐呴儴闆嗘垚骞堕厤缃簡Webpack锛夈��
+ * **Vue Router (`vue-router`):** Vue.js瀹樻柟鐨勮矾鐢辩鐞嗗櫒銆傚畠璐熻矗鍦⊿PA鍐呴儴瀹炵幇涓嶅悓鈥滈〉闈⑩�濇垨瑙嗗浘缁勪欢涔嬮棿鐨勫鑸垏鎹紝鑰屾棤闇�閲嶆柊鍔犺浇鏁翠釜HTML椤甸潰锛屾彁渚涗簡娴佺晠鐨勭敤鎴蜂綋楠屻��
+ * **Vuex (`vuex`):** Vue.js瀹樻柟鐨勭姸鎬佺鐞嗘ā寮忓拰搴撱�傚畠鍏呭綋浜嗗簲鐢ㄤ腑鎵�鏈夌粍浠剁殑鈥滀腑澶暟鎹瓨鍌ㄢ�濓紝鐢ㄤ簬绠$悊鍏ㄥ眬鍏变韩鐘舵�侊紙渚嬪褰撳墠鐧诲綍鐢ㄦ埛淇℃伅銆佽澶囧垪琛ㄣ�佸簲鐢ㄩ厤缃瓑锛夛紝鐗瑰埆閫傜敤浜庡ぇ鍨嬪鏉傚簲鐢ㄣ��
+ * **Element UI (`element-ui`):** 涓�涓箍鍙楁杩庣殑鍩轰簬Vue 2.0鐨勬闈㈢UI缁勪欢搴撱�傚畠鎻愪緵浜嗗ぇ閲忛鍏堣璁″拰瀹炵幇鐨勭粍浠讹紙濡傝〃鍗曘�佽〃鏍笺�佸璇濇銆佸鑸彍鍗曘�佹寜閽�佹彁绀虹瓑锛夛紝甯姪寮�鍙戣�呭揩閫熸瀯寤哄嚭涓撲笟涓斾竴鑷寸殑鐢ㄦ埛鐣岄潰銆�
+ * **JavaScript (ES6+):** 鍓嶇閫昏緫瀹炵幇鐨勪富瑕佺紪绋嬭瑷�锛屽埄鐢ㄥ叾鐜颁唬鐗规�ц繘琛屽紑鍙戙��
+ * **SCSS (Sassy CSS):** 涓�绉岰SS棰勫鐞嗗櫒锛屽畠涓篊SS澧炲姞浜嗗彉閲忋�佸祵濂楄鍒欍�佹贩鍚�(Mixin)銆佺户鎵跨瓑楂樼骇鐗规�э紝浣垮緱CSS浠g爜鏇存槗浜庣粍缁囥�佺淮鎶ゅ拰澶嶇敤銆�
+ * **HTTP瀹㈡埛绔� (Flyio 鎴� Axios 閫氳繃 `vue-axios`):** 鐢ㄤ簬鍦ㄦ祻瑙堝櫒绔悜 `manager-api` 鍚庣鍙戣捣寮傛HTTP锛圓JAX锛夎姹傦紝浠ヨ幏鍙栨暟鎹垨鎻愪氦鎿嶄綔銆�
+ * **Webpack:** 涓�涓己澶х殑妯″潡鎵撳寘宸ュ叿锛堢敱Vue CLI鍦ㄥ簳灞傜鐞嗗拰閰嶇疆锛夈�傚畠灏嗛」鐩腑鐨勫悇绉嶈祫婧愶紙JavaScript鏂囦欢銆丆SS銆佸浘鐗囥�佸瓧浣撶瓑锛夎涓烘ā鍧楋紝骞跺皢瀹冧滑鎵撳寘鎴愭祻瑙堝櫒鍙瘑鍒殑闈欐�佹枃浠躲��
+ * **Workbox (閫氳繃 `workbox-webpack-plugin`):** Google寮�鍙戠殑涓�涓簱锛岀敤浜庣畝鍖朣ervice Worker鐨勭紪鍐欏拰PWA锛圥rogressive Web App - 娓愯繘寮廤eb搴旂敤锛夌殑瀹炵幇銆傚畠鍙互甯姪鐢熸垚Service Worker鑴氭湰锛屽疄鐜拌祫婧愮紦瀛樸�佺绾胯闂瓑鍔熻兘銆�
+ * **Opus搴� (`opus-decoder`, `opus-recorder`):** 杩欎簺闊抽澶勭悊搴撹〃鏄庡墠绔彲鑳藉叿澶囦竴浜涚洿鎺ュ湪娴忚鍣ㄤ腑澶勭悊Opus鏍煎紡闊抽鐨勮兘鍔涳紝渚嬪锛氱敤浜庢祴璇曢害鍏嬮杈撳叆銆佸厑璁哥鐞嗗憳褰曞埗鑷畾涔夐煶棰戠墖娈碉紙鍙兘鐢ㄤ簬TTS闊宠壊鏍锋湰鎴栬闊虫寚浠ゆ祴璇曪級锛屾垨鎾斁鍦ㄧ鐞嗙晫闈腑棰勮鐨凮pus缂栫爜闊抽銆�
+
+* **鍏抽敭瀹炵幇缁嗚妭:**
+
+ 1. **鍗曢〉搴旂敤 (SPA) 缁撴瀯:**
+ * 鏁翠釜鍓嶇搴旂敤鍔犺浇涓�涓富HTML鏂囦欢 (`public/index.html`)銆傚悗缁殑鎵�鏈夐〉闈㈠垏鎹㈠拰鍐呭鏇存柊閮藉湪瀹㈡埛绔敱Vue Router鍔ㄦ�佸畬鎴愶紝鏃犻渶姣忔閮戒粠鏈嶅姟鍣ㄨ姹傛柊鐨凥TML椤甸潰銆傝繖绉嶆ā寮忚兘鎻愪緵鏇村揩鐨勯〉闈㈠姞杞介�熷害鍜屾洿娴佺晠鐨勪氦浜掍綋楠屻��
+
+ 2. **缁勪欢鍖栨灦鏋� (Component-Based Architecture):**
+ * 鐢ㄦ埛鐣岄潰鐢变竴绯诲垪鍙鐢ㄧ殑Vue缁勪欢 (`.vue` 鍗曟枃浠剁粍浠�) 鏋勬垚锛屽舰鎴愪竴涓粍浠舵爲銆傝繖绉嶆柟寮忔彁楂樹簡浠g爜鐨勬ā鍧楀寲绋嬪害銆佸彲缁存姢鎬у拰澶嶇敤鎬с��
+ * **`src/main.js`:** 搴旂敤鐨勫叆鍙S鏂囦欢銆傚畠璐熻矗鍒涘缓鍜屽垵濮嬪寲鏍筕ue瀹炰緥锛屾敞鍐屽叏灞�鎻掍欢锛堝Vue Router, Vuex, Element UI锛夛紝骞舵妸鏍筕ue瀹炰緥鎸傝浇鍒� `public/index.html` 涓殑鏌愪釜DOM鍏冪礌涓婏紙閫氬父鏄� `#app`锛夈��
+ * **`src/App.vue`:** 搴旂敤鐨勬牴缁勪欢銆傚畠閫氬父瀹氫箟浜嗗簲鐢ㄧ殑鍩虹甯冨眬缁撴瀯锛堝鍖呭惈瀵艰埅鏍忋�佷晶杈规爮銆佷富鍐呭鍖猴級锛屽苟閫氳繃 `<router-view></router-view>` 鏍囩鏉ユ樉绀哄綋鍓嶈矾鐢卞尮閰嶅埌鐨勮鍥剧粍浠躲��
+ * **瑙嗗浘缁勪欢 (`src/views/`):** 杩欎簺缁勪欢浠h〃浜嗗簲鐢ㄤ腑鐨勫悇涓�滈〉闈⑩�濇垨涓昏鍔熻兘鍖猴紙渚嬪 `Login.vue` 鐧诲綍椤�, `DeviceManagement.vue` 璁惧绠$悊椤�, `UserManagement.vue` 鐢ㄦ埛绠$悊椤�, `ModelConfig.vue` 妯″瀷閰嶇疆椤碉級銆傚畠浠�氬父鐢盫ue Router鐩存帴鏄犲皠銆�
+ * **鍙鐢║I缁勪欢 (`src/components/`):** 鍖呭惈浜嗗湪涓嶅悓瑙嗗浘涔嬮棿鍏变韩鐨勩�佹洿灏忕矑搴︾殑UI缁勪欢锛堜緥濡� `HeaderBar.vue` 椤堕儴瀵艰埅鏍�, `AddDeviceDialog.vue` 娣诲姞璁惧瀵硅瘽妗�, `AudioPlayer.vue` 闊抽鎾斁鍣ㄧ粍浠讹級銆�
+
+ 3. **瀹㈡埛绔矾鐢� (`src/router/index.js`):**
+ * Vue Router鍦ㄦ鏂囦欢涓繘琛岄厤缃紝瀹氫箟浜嗗簲鐢ㄧ殑璺敱琛ㄣ�傛瘡涓矾鐢辫鍒欏皢涓�涓壒瀹氱殑URL璺緞鏄犲皠鍒颁竴涓鍥剧粍浠躲��
+ * 甯稿父鍖呭惈**瀵艰埅瀹堝崼 (Navigation Guards)**锛屼緥濡� `beforeEach` 瀹堝崼锛岀敤浜庡湪璺敱璺宠浆鍓嶆墽琛岄�昏緫锛屽妫�鏌ョ敤鎴锋槸鍚﹀凡鐧诲綍锛屽鏋滄湭鐧诲綍鍒欓噸瀹氬悜鍒扮櫥褰曢〉闈紝浠庤�屼繚鎶ら渶瑕佽璇佹墠鑳借闂殑椤甸潰銆�
+
+ 4. **鐘舵�佺鐞� (`src/store/index.js`):**
+ * Vuex琚敤鏉ユ瀯寤轰竴涓泦涓殑鐘舵�佺鐞嗕腑蹇冿紙Store锛夈�傝繖涓猄tore鍖呭惈浜嗭細
+ * **State:** 瀛樺偍搴旂敤绾у埆鐨勫叡浜暟鎹紙渚嬪锛屽綋鍓嶇櫥褰曠敤鎴风殑璇︾粏淇℃伅銆佷粠API鑾峰彇鐨勮澶囧垪琛ㄣ�佺郴缁熼厤缃瓑锛夈��
+ * **Getters:** 绫讳技浜嶸ue缁勪欢涓殑璁$畻灞炴�э紝鐢ㄤ簬浠嶴tate娲剧敓鍑轰竴浜涚姸鎬佸�硷紝鏂逛究缁勪欢浣跨敤銆�
+ * **Mutations:** **鍞竴**鍙互鍚屾淇敼State涓暟鎹殑鏂规硶銆傚畠浠繀椤绘槸鍚屾鍑芥暟銆�
+ * **Actions:** 鐢ㄤ簬澶勭悊寮傛鎿嶄綔锛堝API璋冪敤锛夋垨灏佽澶氫釜Mutation鎻愪氦銆侫ctions浼氳皟鐢ˋPI锛岃幏鍙栨暟鎹悗锛岄�氳繃 `commit` 涓�涓垨澶氫釜Mutation鏉ユ洿鏂癝tate銆�
+ * 渚嬪锛岀敤鎴风櫥褰曟椂锛屼竴涓悕涓� `login` 鐨凙ction鍙兘浼氳璋冪敤锛屽畠浼氬悜鍚庣API鍙戦�佺櫥褰曡姹傦紝鎴愬姛鍚庤幏鍙栧埌鐢ㄦ埛淇℃伅鍜宼oken锛岀劧鍚� `commit` 涓�涓悕涓� `SET_USER_INFO` 鐨凪utation鏉ユ洿鏂癝tate涓殑鐢ㄦ埛淇℃伅鍜宼oken銆�
+
+ 5. **API閫氫俊 (`src/apis/`):**
+ * 涓� `manager-api` 鍚庣鐨勬墍鏈塇TTP閫氫俊閫昏緫琚皝瑁呭湪 `src/apis/` 鐩綍涓嬶紝閫氬父浼氭寜鐓у悗绔疉PI鐨勬ā鍧楄繘琛岀粍缁囷紙渚嬪 `src/apis/module/agent.js`, `src/apis/module/device.js`锛夈��
+ * 姣忎釜妯″潡瀵煎嚭涓�绯诲垪鍑芥暟锛屾瘡涓嚱鏁板搴斾竴涓叿浣撶殑API璇锋眰銆傝繖浜涘嚱鏁板唴閮ㄤ娇鐢ㄩ厤缃ソ鐨凥TTP瀹㈡埛绔疄渚� (渚嬪锛屽湪 `src/apis/api.js` 鎴� `src/apis/httpRequest.js` 涓粺涓�閰嶇疆Axios鎴朏lyio瀹炰緥锛屽彲鑳藉寘鍚缃姹傚熀鍦板潃銆佽姹�/鍝嶅簲鎷︽埅鍣ㄧ瓑)銆�
+ * **鎷︽埅鍣� (Interceptors):** HTTP瀹㈡埛绔殑璇锋眰鎷︽埅鍣ㄥ父鐢ㄤ簬鍦ㄦ瘡涓姹傚彂閫佸墠鑷姩娣诲姞璁よ瘉浠ょ墝锛堝JWT锛夛紱鍝嶅簲鎷︽埅鍣ㄥ垯鍙敤浜庡叏灞�澶勭悊API閿欒锛堝鏉冮檺涓嶈冻銆佹湇鍔″櫒閿欒锛夋垨瀵瑰搷搴旀暟鎹繘琛岄澶勭悊銆�
+
+ 6. **鏍峰紡涓庤祫婧� (`src/styles/`, `src/assets/`):**
+ * `Element UI` 鎻愪緵浜嗗熀纭�鐨勭粍浠舵牱寮忋��
+ * `src/styles/global.scss` 鏂囦欢鐢ㄤ簬瀹氫箟鍏ㄥ眬鍏变韩鐨凷CSS鏍峰紡銆佸彉閲忋�佹贩鍚�(Mixin)绛夈��
+ * Vue鍗曟枃浠剁粍浠跺唴閮ㄧ殑 `<style scoped>` 鏍囩鍏佽缂栧啓鍙綔鐢ㄤ簬褰撳墠缁勪欢鐨勫眬閮ㄦ牱寮忋��
+ * `src/assets/` 鐩綍瀛樻斁鍥剧墖銆佸瓧浣撶瓑闈欐�佽祫婧愩��
+
+ 7. **鏋勫缓涓嶱WA鐗规��:**
+ * Vue CLI閫氳繃Webpack灏嗘墍鏈変唬鐮佸拰璧勬簮鎵撳寘鎴愪紭鍖栫殑闈欐�佹枃浠讹紝鐢ㄤ簬鐢熶骇閮ㄧ讲銆�
+ * `workbox-webpack-plugin` 鐨勪娇鐢紙浣撶幇鍦� `service-worker.js` 鍜� `registerServiceWorker.js` 鏂囦欢锛夎〃鏄庨」鐩泦鎴愪簡Service Worker鎶�鏈�係ervice Worker鍙互鎷︽埅缃戠粶璇锋眰锛屽疄鐜板墠绔祫婧愮殑鏅鸿兘缂撳瓨锛堜粠鑰屽姞蹇悗缁闂�熷害锛夛紝鐢氳嚦鍦ㄧ綉缁滄柇寮�鏃舵彁渚涗竴瀹氱殑绂荤嚎璁块棶鑳藉姏锛屾槸PWA鐨勬牳蹇冩妧鏈箣涓�銆�
+
+ 8. **鐜閰嶇疆 (`.env`绯诲垪鏂囦欢):**
+ * 椤圭洰鏍圭洰褰曚笅鐨� `.env` (浠ュ強 `.env.development`, `.env.production` 绛�) 鏂囦欢鐢ㄤ簬瀹氫箟鐜鍙橀噺銆傝繖浜涘彉閲忥紙渚嬪 `VUE_APP_API_BASE_URL` 鏉ユ寚瀹� `manager-api` 鐨勫熀纭�URL锛夊彲浠ュ湪搴旂敤浠g爜涓�氳繃 `process.env.VUE_APP_XXX` 鐨勫舰寮忚闂紝浠庤�屽厑璁镐负涓嶅悓鏋勫缓鐜锛堝紑鍙戙�佹祴璇曘�佺敓浜э級閰嶇疆涓嶅悓鐨勫弬鏁般��
+
+`manager-web` 閫氳繃杩欎簺鎶�鏈殑缁煎悎杩愮敤锛屾瀯寤轰簡涓�涓姛鑳藉己澶с�佹槗浜庣淮鎶や笖鐢ㄦ埛浣撻獙鑹ソ鐨勭鐞嗙晫闈紝涓� `xiaozhi-esp32-server` 绯荤粺鐨勯厤缃拰鐩戞帶鎻愪緵浜嗗潥瀹炵殑鍓嶇鏀寔銆�
+
+---
+
+### 3.4. `manager-mobile` (鏅烘帶鍙扮Щ鍔ㄧ増 - uni-app瀹炵幇)
+
+`manager-mobile` 缁勪欢鏄竴涓熀浜巙ni-app v3 + Vue 3 + Vite鐨勮法绔Щ鍔ㄧ鐞嗙锛屾敮鎸丄pp锛圓ndroid & iOS锛夊拰寰俊灏忕▼搴忋�傚畠涓虹郴缁熺鐞嗗憳鎻愪緵浜嗙Щ鍔ㄧ鐨勭鐞嗙晫闈紝浣垮緱绠$悊鎿嶄綔鏇村姞渚挎嵎銆�
+
+* **鏍稿績鐩爣:**
+ * 鎻愪緵绉诲姩璁惧涓婄殑渚挎嵎绠$悊鐣岄潰锛屼笌manager-web鍔熻兘绫讳技浣嗛拡瀵圭Щ鍔ㄧ杩涜浜嗕紭鍖栥��
+ * 鏀寔鐢ㄦ埛鐧诲綍銆佽澶囩鐞嗐�丄I鏈嶅姟閰嶇疆绛夋牳蹇冨姛鑳姐��
+ * 璺ㄥ钩鍙伴�傞厤锛屼竴濂椾唬鐮佸彲鍚屾椂杩愯鍦╥OS銆丄ndroid鍜屽井淇″皬绋嬪簭涓娿��
+ * 涓虹Щ鍔ㄧ敤鎴锋彁渚涙祦鐣呫�侀珮鏁堢殑绠$悊浣撻獙銆�
+
+* **骞冲彴鍏煎鎬�:**
+
+| H5 | iOS | Android | 寰俊灏忕▼搴� |
+| -- | --- | ------- | ---------- |
+| 鈭� | 鈭� | 鈭� | 鈭� |
+
+* **鏍稿績鎶�鏈爤:**
+ * **uni-app v3:** 涓�涓娇鐢╒ue.js寮�鍙戞墍鏈夊墠绔簲鐢ㄧ殑妗嗘灦锛屾敮鎸乮OS銆丄ndroid銆丠5銆佷互鍙婂悇绉嶅皬绋嬪簭銆�
+ * **Vue 3:** 鐢ㄤ簬鏋勫缓鐢ㄦ埛鐣岄潰鐨勬笎杩涘紡妗嗘灦锛屾彁渚涗簡鏇村ソ鐨勬�ц兘鍜屾柊鐗规�с��
+ * **Vite:** 涓嬩竴浠e墠绔紑鍙戜笌鏋勫缓宸ュ叿锛屾彁渚涙瀬閫熺殑寮�鍙戜綋楠屻��
+ * **pnpm:** 蹇�熴�佽妭鐪佺鐩樼┖闂寸殑鍖呯鐞嗗櫒銆�
+ * **alova:** 杞婚噺绾с�佺伒娲荤殑璇锋眰绛栫暐搴擄紝鎼厤@alova/adapter-uniapp閫傞厤uni-app鐜銆�
+ * **pinia:** Vue鐨勭姸鎬佺鐞嗗簱锛屾浛浠uex锛屾彁渚涙洿绠�娲佺殑API鍜屾洿濂界殑TypeScript鏀寔銆�
+ * **UnoCSS:** 鍏锋湁楂樻�ц兘涓旀瀬鍏风伒娲绘�х殑鍗虫椂鍘熷瓙鍖朇SS寮曟搸銆�
+ * **TypeScript:** 鎻愪緵绫诲瀷瀹夊叏鐨勫紑鍙戜綋楠屻��
+
+* **鍏抽敭瀹炵幇缁嗚妭:**
+
+ 1. **璺ㄥ钩鍙版灦鏋�:**
+ * 鍩轰簬uni-app妗嗘灦锛屽疄鐜颁簡涓�濂椾唬鐮佸绔繍琛岀殑鐩爣锛屽ぇ骞呭噺灏戜簡寮�鍙戝拰缁存姢鎴愭湰銆�
+ * 閽堝涓嶅悓骞冲彴鐨勭壒鎬у拰闄愬埗锛岄�氳繃鏉′欢缂栬瘧杩涜骞冲彴鐗瑰畾鐨勪唬鐮佸鐞嗐��
+
+ 2. **椤圭洰缁撴瀯:**
+ * **`src/App.vue`:** 搴旂敤鐨勬牴缁勪欢锛屽畾涔変簡鍏ㄥ眬鐨勬牱寮忓拰閰嶇疆銆�
+ * **`src/main.ts`:** 搴旂敤鐨勫叆鍙f枃浠讹紝璐熻矗鍒濆鍖朧ue瀹炰緥銆佹敞鍐屾彃浠跺拰璺敱鎷︽埅鍣ㄣ��
+ * **`src/pages/`:** 瀛樻斁搴旂敤鐨勯〉闈㈢粍浠讹紝濡傜櫥褰曢〉銆佽澶囩鐞嗛〉绛夈��
+ * **`src/layouts/`:** 瀹氫箟搴旂敤鐨勫竷灞�缁勪欢锛屽榛樿甯冨眬銆佸甫tabbar鐨勫竷灞�绛夈��
+ * **`src/api/`:** 灏佽涓庡悗绔疉PI鐨勯�氫俊閫昏緫銆�
+ * **`src/store/`:** 浣跨敤pinia杩涜鐘舵�佺鐞嗐��
+ * **`src/components/`:** 瀛樻斁鍙鐢ㄧ殑缁勪欢銆�
+ * **`src/utils/`:** 鎻愪緵閫氱敤鐨勫伐鍏峰嚱鏁般��
+
+ 3. **缃戠粶璇锋眰:**
+ * 鍩轰簬alova + @alova/adapter-uniapp瀹炵幇缃戠粶璇锋眰锛岀粺涓�澶勭悊璇锋眰澶淬�佽璇併�侀敊璇瓑銆�
+ * 璇锋眰鍦板潃鍜岀幆澧冮厤缃�氳繃.env鏂囦欢绠$悊锛屾敮鎸佷笉鍚岀幆澧冪殑鍒囨崲銆�
+
+ 4. **璺敱涓庨壌鏉�:**
+ * 浣跨敤uni-app鐨勮矾鐢辩郴缁燂紝缁撳悎璺敱鎷︽埅鍣ㄥ疄鐜伴〉闈㈢殑鐧诲綍楠岃瘉鍜屾潈闄愭帶鍒躲��
+ * 鏈櫥褰曠敤鎴疯闂渶瑕佽璇佺殑椤甸潰鏃讹紝浼氳閲嶅畾鍚戝埌鐧诲綍椤点��
+
+ 5. **鐘舵�佺鐞�:**
+ * 浣跨敤pinia绠$悊搴旂敤鐘舵�侊紝濡傜敤鎴蜂俊鎭�佽澶囧垪琛ㄧ瓑銆�
+ * 閫氳繃pinia-plugin-persistedstate鎻掍欢瀹炵幇鐘舵�佺殑鎸佷箙鍖栧瓨鍌ㄣ��
+
+ 6. **鏋勫缓涓庡彂甯�:**
+ * 鏀寔澶氱鏋勫缓鍛戒护锛屽鏋勫缓寰俊灏忕▼搴忋�丄ndroid鍜宨OS App绛夈��
+ * 浣跨敤HBuilderX杩涜App鐨勪簯鎵撳寘锛岀畝鍖栦簡鎵撳寘娴佺▼銆�
+
+`manager-mobile` 閫氳繃杩欎簺鎶�鏈殑搴旂敤锛屼负鐢ㄦ埛鎻愪緵浜嗕竴涓姛鑳藉畬澶囥�佷綋楠屾祦鐣呯殑绉诲姩绔鐞嗗伐鍏凤紝浣垮緱绠$悊鍛樺彲浠ラ殢鏃堕殢鍦拌繘琛岀郴缁熺鐞嗗拰閰嶇疆銆�
+
+---
+
+### 3.5. `digital-human` (鏁板瓧浜烘祴璇曟ā鍧� - Python+Web瀹炵幇)
+
+`digital-human` 缁勪欢鏄竴涓嫭绔嬬殑鏁板瓧浜烘祴璇曟ā鍧楋紝璐熻矗鎻愪緵鏈湴娴嬭瘯椤甸潰銆侀〉闈㈣祫婧愩�佸敜閱掕瘝杩愯鏃跺拰浜嬩欢妗ヨ兘鍔涖�傚畠涓昏鐢ㄤ簬鑱旇皟鏁翠釜鏁板瓧浜轰氦浜掗摼璺紝甯姪寮�鍙戣�呴獙璇侀〉闈氦浜掋�侀煶棰戣兘鍔涘拰鏈湴鍞ら啋璇嶆祦绋嬨��
+
+* **鏍稿績鐩爣:**
+ * 鎻愪緵鐙珛鐨勬湰鍦版祴璇曢〉闈紝鐢ㄤ簬楠岃瘉鏁板瓧浜轰氦浜掓晥鏋溿��
+ * 鎻愪緵鏈湴鍞ら啋璇嶈繍琛屾椂锛屾敮鎸佸叧閿瘝妫�娴嬩笌浜嬩欢涓婃姤銆�
+ * 涓哄墠绔〉闈㈠拰鏈湴杩愯鏃舵彁渚涗簨浠舵ˉ锛屾墦閫氭祴璇曢摼璺��
+ * 浠ョ嫭绔嬫ā鍧楀舰寮忓瓨鍦紝渚夸簬鍗曠嫭寮�鍙戙�佽皟璇曞拰閮ㄧ讲銆�
+
+* **鏍稿績鎶�鏈爤:**
+ * **Python 3:** 浣滀负娴嬭瘯杩愯鏃剁殑涓昏璇█锛岃礋璐e惎鍔ㄦ湰鍦版湇鍔°�佺鐞嗗敜閱掕瘝杩愯鏃跺拰浜嬩欢妗ャ��
+ * **鍘熺敓HTML/CSS/JavaScript:** 鐢ㄤ簬鏋勫缓鏁板瓧浜烘祴璇曢〉闈笌浜や簰閫昏緫銆�
+ * **Sherpa-ONNX:** 鐢ㄤ簬鏈湴鍞ら啋璇嶆娴嬨��
+ * **ThreadingHTTPServer / WebSocket妗ユ帴:** 鐢ㄤ簬鎵胯浇娴嬭瘯椤甸潰銆佸仴搴锋鏌ュ拰鏈湴浜嬩欢閫氫俊銆�
+
+* **鍏抽敭瀹炵幇缁嗚妭:**
+
+ 1. **妯″潡鍏ュ彛涓庨〉闈㈣祫婧�:**
+ * `start.py` 鏄ā鍧楀惎鍔ㄥ叆鍙o紝璐熻矗鍒濆鍖栨祴璇曡繍琛屾椂銆�
+ * `index.html` 鏄暟瀛椾汉娴嬭瘯椤甸潰鍏ュ彛銆�
+ * `js/`銆乣css/`銆乣images/`銆乣resources/` 鐩綍鎻愪緵椤甸潰鑴氭湰銆佹牱寮忓拰璧勬簮鏂囦欢銆�
+
+ 2. **鏈湴娴嬭瘯杩愯鏃�:**
+ * `wakeword_runtime/` 鐩綍鎵胯浇鏈湴鍞ら啋璇嶈繍琛屾椂瀹炵幇銆�
+ * 杩愯鏃惰礋璐i厤缃姞杞姐�佹棩蹇楀垵濮嬪寲銆侀煶棰戦噰闆嗐�佸叧閿瘝妫�娴嬪拰鏈嶅姟鐢熷懡鍛ㄦ湡绠$悊銆�
+ * 妯″潡榛樿閫氳繃鏈湴HTTP鏈嶅姟鏆撮湶椤甸潰鍦板潃銆佷簨浠舵ˉ鍦板潃鍜屽仴搴锋鏌ユ帴鍙c��
+
+ 3. **鍞ら啋璇嶆娴嬮摼璺�:**
+ * 鍩轰簬Sherpa-ONNX鍏抽敭璇嶆娴嬫ā鍨嬶紝鏀寔鏈湴鍞ら啋璇嶈Е鍙戙��
+ * 妯″瀷鏂囦欢鍜屽叧閿瘝閰嶇疆浣嶄簬 `wakeword_runtime/models/` 鍙婄浉搴旈厤缃枃浠朵腑銆�
+ * 椤甸潰渚у彲缁撳悎浜嬩欢妗ヨ瀵熷敜閱掕瘝鏈嶅姟鐘舵�佸拰瑙﹀彂缁撴灉銆�
+
+ 4. **璋冭瘯涓庤仈璋冨畾浣�:**
+ * 鍚姩鍚庡彲閫氳繃娴忚鍣ㄨ闂湰鍦伴〉闈紝楠岃瘉闊抽鎾斁銆佹帴鏀跺拰浜や簰閫昏緫銆�
+ * 閫氳繃浜嬩欢妗ュ彲浠ュ皢鍞ら啋璇嶇姸鎬佸悓姝ュ埌椤甸潰锛屼究浜庢帓鏌ユ湰鍦伴摼璺棶棰樸��
+ * 璇︾粏鐨勬ā鍨嬩笅杞姐�佽繍琛屾椂閰嶇疆鍜屼娇鐢ㄨ鏄庡凡鏁寸悊鍦� `docs/digital-human-wakeword.md` 涓��
+
+`digital-human` 鐨勫瓨鍦ㄤ娇寰楁暟瀛椾汉鐩稿叧鑳藉姏鍙互鑴辩涓绘湇鍔$嫭绔嬮獙璇侊紝闄嶄綆浜嗛〉闈㈣皟璇曘�佸敜閱掕瘝鑱旇皟鍜屾湰鍦扮幆澧冩惌寤虹殑澶嶆潅搴︺��
+
+---
+
+## 4. 鏁版嵁娴佷笌浜や簰鏈哄埗
+
+`xiaozhi-esp32-server` 绯荤粺閫氳繃鍚勭粍浠堕棿瀹氫箟娓呮櫚鐨勬暟鎹祦鍜屼氦浜掑崗璁潵鍗忓悓宸ヤ綔銆備富瑕佺殑閫氫俊鏂瑰紡渚濊禆浜庨拡瀵瑰疄鏃朵氦浜掍紭鍖栫殑WebSocket鍗忚鍜岄�傜敤浜庡鎴风-鏈嶅姟鍣ㄨ姹傜殑RESTful API銆�
+
+**4.1.鏍稿績璇煶浜や簰娴佺▼ (ESP32璁惧 <-> `xiaozhi-server`)**
+
+姝ゆ祦绋嬫槸瀹炴椂鐨勶紝涓昏閫氳繃WebSocket杩涜浣庡欢杩熴�佸弻鍚戠殑鏁版嵁浜ゆ崲銆�
+
+* **閫氳鍗忚鏂囨。:**
+ * 璇︾粏鐨勯�氳鍗忚璇存槑鏂囨。鍙�氳繃浠ヤ笅鍦板潃璁块棶: https://ccnphfhqs21z.feishu.cn/wiki/M0XiwldO9iJwHikpXD5cEx71nKh
+ * 璇ユ枃妗h缁嗘弿杩颁簡ESP32璁惧涓巂xiaozhi-server`涔嬮棿鐨刉ebSocket閫氫俊鍗忚,鍖呮嫭:
+ * 杩炴帴寤虹珛涓庢彙鎵嬫祦绋�
+ * 闊抽鏁版嵁浼犺緭鏍煎紡
+ * 鎺у埗鍛戒护鏍煎紡
+ * 鐘舵�佹姤鍛婃牸寮�
+ * 閿欒澶勭悊鏈哄埗
+
+* **杩炴帴寤虹珛涓庢彙鎵�:**
+ * ESP32璁惧浣滀负瀹㈡埛绔紝涓诲姩鍚慲xiaozhi-server`鐨勬寚瀹氱鐐癸紙渚嬪 `ws://<鏈嶅姟鍣↖P>:<WebSocket绔彛>/xiaozhi/v1/`锛夊彂璧稺ebSocket杩炴帴璇锋眰銆�
+ * `xiaozhi-server` (`core/websocket_server.py`) 鎺ユ敹杩炴帴锛屽苟涓烘瘡涓垚鍔熻繛鎺ョ殑ESP32璁惧瀹炰緥鍖栦竴涓嫭绔嬬殑`ConnectionHandler`瀵硅薄鏉ョ鐞嗚浼氳瘽鐨勬暣涓敓鍛藉懆鏈熴��
+ * 杩炴帴寤虹珛鍚庯紝鍙兘浼氭墽琛屼竴涓垵濮嬫彙鎵嬫祦绋嬶紙鐢盽core/handle/helloHandle.py`澶勭悊锛夛紝鐢ㄤ簬浜ゆ崲璁惧鏍囪瘑銆佽璇佷俊鎭�佸崗璁増鏈垨鍩烘湰鐘舵�併��
+
+* **闊抽涓婅浼犺緭 (ESP32 -> `xiaozhi-server`):**
+ * 鐢ㄦ埛瀵笶SP32璁惧璁茶瘽鍚庯紝璁惧涓婄殑楹﹀厠椋庢崟鎹夊師濮嬮煶棰戞暟鎹紙閫氬父鏄疨CM鎴栫粡杩囧帇缂╁Opus鐨勬牸寮忥級銆�
+ * ESP32灏嗚繖浜涢煶棰戞暟鎹潡锛坈hunks锛変綔涓篧ebSocket鐨�**浜岃繘鍒舵秷鎭� (binary messages)** 瀹炴椂鎺ㄩ�佸埌`xiaozhi-server`瀵瑰簲鐨刞ConnectionHandler`銆�
+ * 鏈嶅姟鍣ㄧ鐨刞core/handle/receiveAudioHandle.py`妯″潡璐熻矗鎺ユ敹銆佺紦鍐插苟澶勭悊杩欎簺闊抽鏁版嵁銆�
+
+* **AI鏍稿績澶勭悊 (鍦╜xiaozhi-server`鍐呴儴):**
+ * **VAD (璇煶娲诲姩妫�娴�):** `receiveAudioHandle.py`鍒╃敤閰嶇疆鐨刅AD鎻愪緵鑰咃紙濡係ileroVAD锛夊垎鏋愰煶棰戞祦锛屼互鍑嗙‘璇嗗埆璇煶鐨勮捣濮嬪拰缁撴潫鐐癸紝婊ら櫎闈欓粯鎴栧櫔澹扮墖娈点��
+ * **ASR (鑷姩璇煶璇嗗埆):** 妫�娴嬪埌鐨勬湁鏁堣闊崇墖娈佃閫佸線閰嶇疆鐨凙SR鎻愪緵鑰咃紙鏈湴濡侳unASR锛屾垨浜戠鏈嶅姟锛夈�侫SR寮曟搸灏嗛煶棰戜俊鍙疯浆鎹负鏂囨湰瀛楃涓层��
+ * **NLU/LLM (鑷劧璇█鐞嗚В/澶у瀷璇█妯″瀷):** ASR杈撳嚭鐨勬枃鏈紝杩炲悓浠嶮emory鎻愪緵鑰呰幏鍙栫殑褰撳墠瀵硅瘽涓婁笅鏂囧巻鍙诧紝浠ュ強浠巂plugins_func/`鍔犺浇鐨勫彲鐢ㄥ嚱鏁帮紙宸ュ叿锛夌殑鎻忚堪妯″紡锛屼竴鍚岃浼犻�掔粰閰嶇疆鐨凩LM鎻愪緵鑰呫��
+ * **鍑芥暟璋冪敤鎵ц (鑻LM鍐崇瓥闇�瑕�):** 濡傛灉LLM鍒嗘瀽鍚庤涓洪渶瑕佽皟鐢ㄥ閮ㄥ嚱鏁帮紙渚嬪鏌ヨ澶╂皵銆佹帶鍒跺鐢碉級锛屽畠浼氱敓鎴愪竴涓粨鏋勫寲鐨勫嚱鏁拌皟鐢ㄨ姹傘�俙core/handle/functionHandler.py`鎺ユ敹姝よ姹傦紝鏌ユ壘骞舵墽琛屽湪`plugins_func/`涓畾涔夌殑鐩稿簲Python鍑芥暟锛屽苟灏嗗嚱鏁扮殑鎵ц缁撴灉杩斿洖缁橪LM銆侺LM闅忓悗鍩轰簬姝ょ粨鏋滅敓鎴愭渶缁堢殑鑷劧璇█鍥炲銆�
+ * **鍥炲鐢熸垚:** LLM缁煎悎鎵�鏈変俊鎭紙鐢ㄦ埛杈撳叆銆佷笂涓嬫枃銆佸嚱鏁拌皟鐢ㄧ粨鏋滅瓑锛夌敓鎴愭渶缁堢殑鏂囨湰鍥炲銆�
+ * **璁板繂鏇存柊:** 褰撳墠杞鐨勪氦浜掞紙鐢ㄦ埛闂銆丩LM鍥炲銆佸彲鑳界殑鍔熻兘璋冪敤锛変細琚玀emory鎻愪緵鑰呭鐞嗭紝浠ユ洿鏂板璇濆巻鍙诧紝渚涘悗缁氦浜掍娇鐢ㄣ��
+ * **TTS (鏂囨湰杞闊�):** LLM鐢熸垚鐨勬渶缁堟枃鏈洖澶嶈閫佸線閰嶇疆鐨凾TS鎻愪緵鑰咃紝鍚庤�呭皢鏂囨湰鍚堟垚涓鸿闊虫暟鎹祦锛堜緥濡侻P3鎴朩AV鏍煎紡锛夈��
+
+* **闊抽涓嬭鍝嶅簲 (`xiaozhi-server` -> ESP32):**
+ * 鐢盩TS鎻愪緵鑰呭悎鎴愮殑璇煶鏁版嵁娴侊紝閫氳繃`core/handle/sendAudioHandle.py`妯″潡锛屼綔涓篧ebSocket鐨�**浜岃繘鍒舵秷鎭�**瀹炴椂鍙戦�佸洖ESP32璁惧銆�
+ * ESP32璁惧鎺ユ敹杩欎簺闊抽鏁版嵁鍧楀苟绔嬪嵆閫氳繃鎵0鍣ㄦ挱鏀剧粰鐢ㄦ埛銆�
+
+* **鎺у埗涓庣姸鎬佹秷鎭� (鍙屽悜):**
+ * 闄や簡闊抽娴侊紝ESP32涓巂xiaozhi-server`涔嬮棿涔熼�氳繃WebSocket浜ゆ崲**鏂囨湰娑堟伅 (text messages)**锛岃繖浜涙秷鎭�氬父閲囩敤JSON鏍煎紡灏佽銆�
+ * **ESP32 -> Server:** 璁惧鍙兘鍙戦�佺姸鎬佹姤鍛婏紙濡傜綉缁滅姸鍐点�侀害鍏嬮鐘舵�侊級銆侀敊璇唬鐮併�佹垨鐗瑰畾鐨勬帶鍒跺懡浠わ紙渚嬪鐢ㄦ埛鎸夐敭瑙﹀彂鐨勨�滃仠姝TS鎾姤鈥濓級銆�
+ * **Server -> ESP32:** 鏈嶅姟鍣ㄥ彲鑳藉彂閫佹帶鍒舵寚浠ょ粰璁惧锛堝鈥滃紑濮嬬洃鍚�濄�佲�滃仠姝㈢洃鍚�濄�佽皟鏁寸伒鏁忓害銆佷笅鍙戠壒瀹氶厤缃弬鏁帮級銆�
+ * `core/handle/abortHandle.py`锛堝鐞嗕腑鏂姹傦級銆乣core/handle/reportHandle.py`锛堝鐞嗚澶囨姤鍛婏級绛夋ā鍧楄礋璐hВ鏋愬拰鍝嶅簲杩欎簺鎺у埗/鐘舵�佹秷鎭��
+
+**4.2.绠$悊涓庨厤缃祦绋� (`manager-web` <-> `manager-api` <-> `xiaozhi-server`)**
+
+姝ゆ祦绋嬩富瑕佷緷璧栦簬鍩轰簬HTTP/HTTPS鐨凴ESTful API杩涜璇锋眰-鍝嶅簲寮忕殑浜や簰銆�
+
+* **绠$悊鍛楿I鍚庣浜や簰 (`manager-web` -> `manager-api`):**
+ * 褰撶鐞嗗憳鍦╜manager-web`鐣岄潰鎵ц鎿嶄綔鏃讹紙渚嬪淇濆瓨涓�椤归厤缃�佹坊鍔犱竴涓柊鐢ㄦ埛銆佹敞鍐屼竴鍙癊SP32璁惧锛夛細
+ * Vue.js鍓嶇搴旂敤 (`manager-web`) 浼氶�氳繃鍏禔PI灏佽妯″潡锛堜綅浜巂src/apis/module/`锛夊悜`manager-api`鐨勫搴擱EST API绔偣鍙戣捣寮傛HTTP璇锋眰锛堥�氬父鏄疓ET, POST, PUT, DELETE锛夈��
+ * 璇锋眰浣撳拰鍝嶅簲浣撻�氬父浣跨敤JSON鏍煎紡銆�
+ * `manager-api`涓殑`@RestController`绫绘帴鏀惰繖浜涜姹傘��**Apache Shiro**妗嗘灦浼氶鍏堝璇锋眰杩涜璁よ瘉鍜屾巿鏉冩鏌ャ��
+ * 閫氳繃楠岃瘉鍚庯紝Controller灏嗚姹傚垎鍙戠粰鐩稿簲鐨凷ervice灞傚鐞嗕笟鍔¢�昏緫銆係ervice灞傚彲鑳戒細涓嶮ySQL鏁版嵁搴擄紙閫氳繃MyBatis-Plus锛変氦浜掞紝骞跺彲鑳藉埄鐢≧edis杩涜鏁版嵁缂撳瓨銆�
+ * 澶勭悊瀹屾垚鍚庯紝`manager-api`鍚慲manager-web`杩斿洖涓�涓狫SON鏍煎紡鐨凥TTP鍝嶅簲銆�
+ * `manager-web`鏍规嵁鍝嶅簲缁撴灉鏇存柊鍏禫uex鐘舵�佸瓨鍌ㄥ拰鐢ㄦ埛鐣岄潰鏄剧ず銆�
+
+* **閰嶇疆鍚屾 (`manager-api` -> `xiaozhi-server`):**
+ * `xiaozhi-server`鐨勮繍琛屼緷璧栦簬浠巂manager-api`鑾峰彇鐨勫姩鎬侀厤缃紙渚嬪褰撳墠閫夌敤鐨凙I鏈嶅姟鎻愪緵鍟嗗強鍏禔PI瀵嗛挜锛夈��
+ * **鎷夊彇鏈哄埗 (Pull Mechanism):** `xiaozhi-server`鍐呴儴鐨刞config/manage_api_client.py`妯″潡锛屽湪鏈嶅姟鍣ㄥ惎鍔ㄦ椂鎴栭�氳繃鐗瑰畾鏇存柊瑙﹀彂鍣紙渚嬪`WebSocketServer.update_config()`琚皟鐢級锛屼細鍚慲manager-api`鐨勪竴涓寚瀹氱鐐癸紙渚嬪鐢盽modules/config/controller/`涓殑鏌愪釜Controller鎻愪緵锛夊彂璧稨TTP GET璇锋眰銆�
+ * `manager-api`鍝嶅簲璇ヨ姹傦紝杩斿洖`xiaozhi-server`鎵�闇�鐨勯厤缃暟鎹紙JSON鏍煎紡锛夈��
+ * `xiaozhi-server`鎺ユ敹鍒伴厤缃悗锛屼細鏇存柊鍏跺唴閮ㄧ姸鎬侊紝骞跺彲鑳介噸鏂板垵濮嬪寲鐩稿叧鐨凙I鏈嶅姟妯″潡锛屼互浣挎柊閰嶇疆鐢熸晥銆�
+
+* **OTA鍥轰欢鏇存柊娴佺▼ (姒傚康鎬ф弿杩�):**
+ * 绠$悊鍛橀�氳繃`manager-web`鐣岄潰涓婁紶鏂扮殑ESP32鍥轰欢鍖呭埌`manager-api`鐨勭壒瀹氱鐐广��
+ * `manager-api`灏嗗浐浠舵枃浠跺瓨鍌ㄨ捣鏉ワ紝骞惰褰曠浉鍏冲厓鏁版嵁锛堢増鏈彿銆侀�傜敤璁惧鍨嬪彿绛夛級銆�
+ * 褰撶鐞嗗憳瑙﹀彂瀵圭壒瀹氳澶囩殑OTA鏇存柊鏃讹細
+ * `manager-api`鍙兘浼氶�氱煡`xiaozhi-server`锛堝叿浣撻�氱煡鏈哄埗鍙兘鏄竴涓疆璇㈡鏌ョ偣锛屾垨`xiaozhi-server`鏆撮湶涓�涓帴鏀舵洿鏂伴�氱煡鐨凙PI锛屾垨鑰呮洿鏉捐�﹀悎鐨勫娑堟伅闃熷垪锛夈��
+ * `xiaozhi-server`闅忓悗鍙互閫氳繃WebSocket鍚戠洰鏍嘐SP32璁惧鍙戦�佷竴鏉″寘鍚浐浠朵笅杞経RL鐨勬寚浠ゆ秷鎭��
+ * ESP32璁惧鏀跺埌鎸囦护鍚庯紝閫氳繃HTTP GET璇锋眰浠庤URL涓嬭浇鍥轰欢銆傛URL鍙兘鎸囧悜`xiaozhi-server`鑷韩杩愯鐨刞SimpleHttpServer`鎵�鏈嶅姟鐨勮矾寰勶紙濡俙/xiaozhi/ota/`锛夛紝鎴栬�呭湪鏌愪簺鏋舵瀯涓紝涔熷彲鑳界洿鎺ユ寚鍚慲manager-api`鎴栦笓鐢ㄧ殑鏂囦欢鏈嶅姟鍣ㄣ��
+
+**4.3. 涓昏鍗忚鎬荤粨:**
+
+* **WebSocket:** 琚�夌敤浜嶦SP32涓巂xiaozhi-server`涔嬮棿鐨勯�氫俊閾捐矾锛屽洜涓哄畠闈炲父閫傚悎瀹炴椂銆佷綆寤惰繜銆佸弻鍚戠殑鏁版嵁娴佷紶杈擄紙灏ゅ叾鏄煶棰戯級锛屼互鍙婂紓姝ユ帶鍒舵秷鎭殑浼犻�掋��
+* **RESTful APIs (鍩轰簬HTTP/HTTPS锛岄�氬父浣跨敤JSON浣滀负鏁版嵁浜ゆ崲鏍煎紡):** 杩欐槸Web鏈嶅姟闂撮�氫俊鐨勬爣鍑嗘柟寮忋�傜敤浜巂manager-web`锛堝鎴风锛変笌`manager-api`锛堟湇鍔″櫒锛変箣闂寸殑璇锋眰-鍝嶅簲浜や簰锛屼篃鐢ㄤ簬`xiaozhi-server`锛堜綔涓哄鎴风锛変粠`manager-api`锛堜綔涓烘湇鍔″櫒锛夋媺鍙栭厤缃俊鎭�傚叾鏃犵姸鎬佺壒鎬с�佸箍娉涚殑搴撴敮鎸佸拰鏄撲簬鐞嗚В鐨勮涔変娇鍏舵垚涓烘绫讳氦浜掔殑鐞嗘兂閫夋嫨銆�
+
+杩欑澶氬崗璁苟鐢ㄧ殑閫氫俊绛栫暐锛岀‘淇濅簡绯荤粺鍐呬笉鍚岀被鍨嬬殑浜や簰闇�姹傞兘鑳藉緱鍒伴珮鏁堝拰鎭板綋鐨勫鐞嗭紝鍏奸【浜嗗疄鏃舵�у拰鏍囧噯鍖栫殑璇锋眰-鍝嶅簲妯″紡銆�
+
+---
+
+## 5. 鏍稿績鍔熻兘姒傝
+
+`xiaozhi-esp32-server` 绯荤粺鎻愪緵浜嗕竴绯诲垪涓板瘜鐨勫姛鑳斤紝鏃ㄥ湪鏀寔寮�鍙戣�呮瀯寤哄厛杩涚殑璇煶鎺у埗搴旂敤锛�
+
+1. **鍏ㄩ潰鐨勮闊充氦浜掑悗绔�:** 鎻愪緵浠庤闊虫崟鑾锋寚瀵煎埌鍝嶅簲鐢熸垚鍜屽姩浣滄墽琛岀殑绔埌绔В鍐虫柟妗堛��
+2. **妯″潡鍖栧拰鍙彃鎷旂殑AI鏈嶅姟:**
+ * 鏀寔骞挎硾鐨凙SR锛堣嚜鍔ㄨ闊宠瘑鍒級銆丩LM锛堝ぇ鍨嬭瑷�妯″瀷锛夈�乀TS锛堟枃鏈浆璇煶锛夈�乂AD锛堣闊虫椿鍔ㄦ娴嬶級銆佹剰鍥捐瘑鍒拰璁板繂鎻愪緵鍟嗐��
+ * 鍏佽鍔ㄦ�侀�夋嫨鍜岄厤缃繖浜涙湇鍔★紙鍖呮嫭鍩轰簬浜戠殑API鍜屾湰鍦版ā鍨嬶級锛屼互骞宠 鎴愭湰銆佹�ц兘銆侀殣绉佸拰璇█闇�姹傘��
+3. **楂樼骇瀵硅瘽绠$悊:**
+ * 鏀寔鑷劧浜や簰锛屽叿鏈夊敜閱掕瘝鍚姩瀵硅瘽銆佹墜鍔紙鎸夐敭璇磋瘽寮忥級瀵硅瘽浠ュ強瀵圭郴缁熷搷搴旂殑瀹炴椂鎵撴柇绛夊姛鑳姐��
+ * 鍖呭惈涓婁笅鏂囪蹇嗭紝浠ュ湪澶氳疆瀵硅瘽涓繚鎸佽繛璐�с��
+ * 鍦ㄤ竴娈垫椂闂翠笉娲诲姩鍚庡叿鏈夎嚜鍔ㄤ紤鐪犳ā寮忋��
+4. **澶氳瑷�鑳藉姏:**
+ * 鏀寔澶氱璇█鐨勮瘑鍒拰鍚堟垚锛屽寘鎷櫘閫氳瘽銆佺菠璇�佽嫳璇�佹棩璇拰闊╄锛堝叿浣撳彇鍐充簬鎵�閫夌殑ASR/LLM/TTS鎻愪緵鍟嗭級銆�
+5. **閫氳繃鎻掍欢瀹炵幇鐨勫彲鎵╁睍鍔熻兘:**
+ * 寮哄ぇ鐨勬彃浠剁郴缁熷厑璁稿紑鍙戜汉鍛樻坊鍔犺嚜瀹氫箟鈥滄妧鑳解�濇垨鍑芥暟锛堜緥濡傦紝鑾峰彇澶╂皵銆佹帶鍒舵櫤鑳藉灞呰澶囥�佽闂柊闂伙級銆�
+ * 杩欎簺鍑芥暟鍙互鐢盠LM浣跨敤鍏跺嚱鏁拌皟鐢ㄨ兘鍔涳紝鏍规嵁鎻愪緵鐨勬ā寮忔潵瑙﹀彂銆�
+ * 鍐呯疆瀵笻ome Assistant闆嗘垚鐨勬敮鎸併��
+6. **鐗╄仈缃戣澶囨帶鍒�:**
+ * 璁捐鐢ㄤ簬閫氳繃璇煶鍛戒护绠$悊鍜屾帶鍒舵櫤鑳藉灞呰澶囧強鍏朵粬鐗╄仈缃戠‖浠讹紝骞跺埄鐢ㄦ彃浠剁郴缁熴��
+7. **鍩轰簬Web鐨勭鐞嗘帶鍒跺彴 (`manager-web` & `manager-api`):**
+ * 鎻愪緵鍏ㄩ潰鐨勫浘褰㈢晫闈紝鐢ㄤ簬锛�
+ * 绯荤粺閰嶇疆锛圓I鏈嶅姟閫夋嫨銆丄PI瀵嗛挜銆佹搷浣滃弬鏁帮級銆�
+ * 鍩轰簬瑙掕壊鐨勮闂帶鍒剁殑鐢ㄦ埛绠$悊銆�
+ * ESP32璁惧娉ㄥ唽鍜岀鐞嗐��
+ * 璇煶闊宠壊/TTS璇煶瀹氬埗銆�
+ * ESP32璁惧鐨凮TA锛堢┖涓笅杞斤級鍥轰欢鏇存柊绠$悊銆�
+ * 绯荤粺鍙傛暟鍜屽瓧鍏哥殑绠$悊銆�
+8. **鐏垫椿鐨勯儴缃查�夐」:**
+ * 鏀寔閫氳繃Docker瀹瑰櫒锛堢敤浜庣畝鍖栫殑浠呮湇鍔″櫒鎴栧叏鏍堣缃級鍜岀洿鎺ヤ粠婧愪唬鐮侀儴缃诧紝浠ラ�傚簲鍚勭鐜鍜岀敤鎴蜂笓涓氱煡璇嗐��
+9. **鍔ㄦ�佽繙绋嬮厤缃�:**
+ * `xiaozhi-server`鍙互浠巂manager-api`鑾峰彇鍏堕厤缃紝鍏佽瀹炴椂鏇存柊AI鎻愪緵鍟嗗拰璁剧疆锛岃�屾棤闇�閲嶆柊鍚姩鏈嶅姟鍣ㄣ��
+10. **寮�婧愬拰绀惧尯椹卞姩:**
+ * 鏍规嵁MIT璁稿彲璇佹巿鏉冿紝榧撳姳閫忔槑銆佸崗浣滃拰绀惧尯璐$尞銆�
+11. **缁忔祹楂樻晥鐨勮В鍐虫柟妗�:**
+ * 鎻愪緵鈥滃叆闂ㄥ叏鍏嶈垂璁剧疆鈥濊矾寰勶紝鍒╃敤AI鏈嶅姟鐨勫厤璐瑰椁愭垨鏈湴妯″瀷锛屼娇鍏舵槗浜庤繘琛屽疄楠屽拰涓汉椤圭洰銆�
+12. **娓愯繘寮廤eb搴旂敤 (PWA) 鐗规��:**
+ * `manager-web`鎺у埗闈㈡澘鍖呭惈Service Worker闆嗘垚锛屼互澧炲己缂撳瓨鍜屾綔鍦ㄧ殑绂荤嚎璁块棶鑳藉姏銆�
+13. **璇︾粏鐨凙PI鏂囨。:**
+ * `manager-api`閫氳繃Knife4j鎻愪緵OpenAPI (Swagger) 鏂囨。锛屼互渚挎竻鏅扮悊瑙e拰娴嬭瘯鍏禦ESTful绔偣銆�
+
+杩欎簺鍔熻兘鍏卞悓浣縛xiaozhi-esp32-server`鎴愪负涓�涓己澶с�侀�傚簲鎬у己涓旂敤鎴峰弸濂界殑骞冲彴锛岀敤浜庢瀯寤哄鏉傜殑璇煶浜や簰搴旂敤绋嬪簭銆�
+
+---
+
+## 6. 閮ㄧ讲涓庨厤缃杩�
+
+`xiaozhi-esp32-server`绯荤粺鍦ㄨ璁′笂鍏呭垎鑰冭檻浜嗙伒娲绘�э紝鎻愪緵浜嗗绉嶉儴缃叉柟娉曞拰鍏ㄩ潰鐨勯厤缃�夐」锛屼互閫傚簲涓嶅悓鐨勪娇鐢ㄥ満鏅拰闇�姹傘��
+
+**閮ㄧ讲閫夐」:**
+
+椤圭洰鍙互閫氳繃澶氱鏂瑰紡閮ㄧ讲锛屼富瑕佸寘鎷娇鐢―ocker绠�鍖栧畨瑁呰繃绋嬶紝鎴栫洿鎺ヤ粠婧愪唬鐮侀儴缃蹭互鑾峰緱鏇村ぇ鐨勬帶鍒舵潈鍜岃繘琛屽紑鍙戙��
+
+1. **鍩轰簬Docker鐨勯儴缃�:**
+ * **绠�鍖栧畨瑁� (浠卄xiaozhi-server`):** 姝ら�夐」浠呴儴缃叉牳蹇冪殑鍩轰簬Python鐨刞xiaozhi-server`銆傚畠閫傜敤浜庝富瑕侀渶瑕佽闊矨I澶勭悊鑳藉姏鍜孖oT鎺у埗锛岃�屼笉闇�瑕佸畬鏁碬eb绠$悊鐣岄潰鍜屾暟鎹簱鏀寔鍔熻兘锛堝OTA锛夌殑鐢ㄦ埛銆傚湪姝ゆā寮忎笅锛岄厤缃�氬父閫氳繃鏈湴鏂囦欢锛坄config.yaml`锛夌鐞嗭紝浣嗗鏋滈渶瑕侊紝浠嶅彲灏嗗叾鎸囧悜涓�涓凡瀛樺湪鐨刞manager-api`瀹炰緥銆�
+ * **鍏ㄦā鍧楀畨瑁� (鎵�鏈夌粍浠�):** 姝ゆ柟妗堥儴缃叉墍鏈夋牳蹇冪粍浠讹細`xiaozhi-server`銆佸熀浜嶫ava鐨刞manager-api`銆佷互鍙婂熀浜嶸ue.js鐨刞manager-web`锛屽悓鏃惰繕鍖呮嫭鎵�闇�鐨勬暟鎹簱鏈嶅姟锛圡ySQL鍜孯edis锛夈�傝繖鎻愪緵浜嗗畬鏁寸殑绯荤粺浣撻獙锛屽寘鎷敤浜庡叏闈㈤厤缃拰绠$悊鐨刉eb鎺у埗闈㈡澘銆�
+ * 椤圭洰涓烘瘡涓湇鍔℃彁渚涗簡`Dockerfile`瀹氫箟锛屽苟浣跨敤`docker-compose.yml`鏂囦欢锛堜緥濡俙docker-compose.yml`鐢ㄤ簬鍩虹鐗堬紝`docker-compose_all.yml`鐢ㄤ簬鍏ㄥ姛鑳界増锛夋潵缂栨帓鍜岀鐞嗗瀹瑰櫒鐨勯儴缃层�傛澶栵紝杩樺彲鑳芥彁渚涗竴涓猔docker-setup.sh`鑴氭湰鏉ヨ緟鍔╄嚜鍔ㄥ寲閮ㄥ垎Docker鐜鐨勬惌寤哄伐浣溿��
+
+2. **婧愪唬鐮侀儴缃�:**
+ * 杩欑鏂规硶闇�瑕佷负姣忎釜缁勪欢鎵嬪姩璁剧疆鐩稿簲鐨勫紑鍙戠幆澧冿細Python鐜鐢ㄤ簬`xiaozhi-server`锛孞ava/Maven鐜鐢ㄤ簬`manager-api`锛孨ode.js/Vue CLI鐜鐢ㄤ簬`manager-web`銆�
+ * 瀵逛簬鍏ㄦā鍧楀畨瑁咃紝杩橀渶瑕佹墜鍔ㄥ畨瑁呭拰閰嶇疆MySQL鍙奟edis鏁版嵁搴撴湇鍔°��
+ * 杩欑鏂瑰紡閫氬父鐢ㄤ簬椤圭洰寮�鍙戙�佹繁搴﹀畾鍒躲�佽皟璇曪紝鎴栬�呭湪瀵圭幆澧冩湁鐗规畩瑕佹眰鐨勭敓浜у満鏅腑銆�
+
+**閰嶇疆绠$悊:**
+
+閰嶇疆鏄畾鍒剁郴缁熻涓虹殑鍏抽敭锛屽挨鍏舵槸鍦ㄩ�夋嫨AI鏈嶅姟鎻愪緵鍟嗗拰绠$悊API瀵嗛挜鏂归潰銆�
+
+1. **`xiaozhi-server` 閰嶇疆:**
+ * **鏈湴`config.yaml`:** 浣嶄簬`xiaozhi-server`鏍圭洰褰曚笅鐨勪竴涓富瑕佺殑YAML鏍煎紡閰嶇疆鏂囦欢銆傚畠瀹氫箟浜嗘湇鍔″櫒绔彛銆侀�夊畾鐨凙I鏈嶅姟鎻愪緵鍟嗭紙ASR銆丩LM銆乀TS銆乂AD銆佹剰鍥捐瘑鍒�佽蹇嗘ā鍧楃瓑锛夈�佸畠浠悇鑷殑API瀵嗛挜鎴栨ā鍨嬭矾寰勩�佹彃浠堕厤缃互鍙婃棩蹇楃骇鍒瓑銆�
+ * **閫氳繃`manager-api`杩涜杩滅▼閰嶇疆:** `xiaozhi-server`琚璁′负鍙互浠巂manager-api`鑾峰彇鍏惰繍琛岄厤缃�備粠`manager-api`鑾峰彇鐨勮缃�氬父浼氳鐩栨湰鍦癭config.yaml`涓殑鍚屽悕璁剧疆銆傝繖甯︽潵浜嗕袱澶уソ澶勶細
+ * **闆嗕腑绠$悊:** 鎵�鏈夐厤缃兘鍙互閫氳繃`manager-web`鐣岄潰杩涜缁熶竴绠$悊銆�
+ * **鍔ㄦ�佹洿鏂�:** `xiaozhi-server`鍙互鍒锋柊鍏堕厤缃苟閲嶆柊鍒濆鍖朅I妯″潡锛岃�屾棤闇�瀹屽叏閲嶅惎鏈嶅姟銆�
+ * `xiaozhi-server`涓殑`config/config_loader.py`鍜宍config/manage_api_client.py`璐熻矗澶勭悊閰嶇疆鐨勫姞杞姐�佸悎骞跺強浠巂manager-api`鎷夊彇鐨勯�昏緫銆�
+
+2. **`manager-api` 閰嶇疆:**
+ * 浣滀负涓�涓猄pring Boot搴旂敤锛屽叾閰嶇疆涓昏閫氳繃浣嶄簬`src/main/resources`鐩綍涓嬬殑`application.properties`鎴朻application.yml`鏂囦欢杩涜绠$悊銆�
+ * 鍏抽敭閰嶇疆椤瑰寘鎷細鏁版嵁搴撹繛鎺ヤ俊鎭紙MySQL鐨刄RL銆佺敤鎴峰悕銆佸瘑鐮侊級銆丷edis鏈嶅姟鍣ㄥ湴鍧�鍜岀鍙c�佸簲鐢ㄦ湇鍔$鍙o紙榛樿涓�8002锛夈�丄pache Shiro瀹夊叏鐩稿叧鐨勮缃紝浠ュ強浠讳綍闆嗘垚鐨勭涓夋柟鏈嶅姟锛堝闃块噷浜戠煭淇★級鐨勯厤缃弬鏁般��
+
+3. **`manager-web` 閰嶇疆:**
+ * Vue.js鍓嶇搴旂敤鐨勭幆澧冪壒瀹氳缃�氳繃椤圭洰鏍圭洰褰曚笅鐨刞.env`绯诲垪鏂囦欢锛堜緥濡俙.env`, `.env.development`, `.env.production`锛夎繘琛岀鐞嗐��
+ * 杩欓噷鏈�鍏抽敭鐨勯厤缃�氬父鏄痐manager-api`鍚庣鐨凙PI鍩虹URL鍦板潃 (渚嬪 `VUE_APP_API_BASE_URL`)锛屽墠绔簲鐢ㄥ皢鍚戞鍦板潃鍙戦�佹墍鏈堿PI璇锋眰銆�
+
+4. **棰勫畾涔夌殑閰嶇疆鏂规:**
+ * 椤圭洰鏂囨。锛堥�氬父鏄疪EADME锛変腑浼氭帹鑽愪竴浜涘父瑙佺殑閰嶇疆缁勫悎锛屼緥濡傦細
+ * **鈥滃叆闂ㄥ叏鍏嶈垂璁剧疆鈥�:** 璇ユ柟妗堟棬鍦ㄥ埄鐢ㄤ簯AI鏈嶅姟鐨勫厤璐瑰椁愰搴︽垨瀹屽叏鍏嶈垂鐨勬湰鍦版ā鍨嬶紝浠ユ渶澶х▼搴﹀湴闄嶄綆鐢ㄦ埛鐨勫垵濮嬩娇鐢ㄦ垚鏈拰杩愯惀璐圭敤銆�
+ * **鈥滃叏娴佸紡閰嶇疆鈥�:** 璇ユ柟妗堜紭鍏堣�冭檻绯荤粺鐨勫搷搴旈�熷害鍜屼氦浜掔殑娴佺晠鎬э紝閫氬父浼氶�夌敤鏀寔娴佸紡澶勭悊鐨勶紙鍙兘浠樿垂鐨勶級AI鏈嶅姟銆�
+ * 杩欎簺棰勫畾涔夋柟妗堜负鐢ㄦ埛鍦╜xiaozhi-server`涓厤缃瓵I鏈嶅姟鎻愪緵鍟嗭紙閫氳繃`manager-web`鐣岄潰鎴栫洿鎺ヤ慨鏀筦config.yaml`锛夋彁渚涗簡鎸囧銆�
+
+鍦ㄥ叏妯″潡閮ㄧ讲鐨勬儏鍐典笅锛屾帹鑽愪娇鐢╜manager-web`鎺у埗闈㈡澘浣滀负澶у鏁伴厤缃换鍔$殑涓昏鎿嶄綔鐣岄潰锛屽洜涓哄畠鎻愪緵浜嗕竴绉嶇敤鎴峰弸濂界殑鏂瑰紡鏉ョ鐞嗙敱`manager-api`鎸佷箙鍖栧苟鏈�缁堢敱`xiaozhi-server`浣跨敤鐨勫悇椤硅缃��
+
+---
diff --git a/xiaozhi-esp32-server/main/README_en.md b/xiaozhi-esp32-server/main/README_en.md
new file mode 100644
index 0000000..9b0a1fd
--- /dev/null
+++ b/xiaozhi-esp32-server/main/README_en.md
@@ -0,0 +1,491 @@
+# Technical Documentation: `xiaozhi-esp32-server`
+
+**Table of Contents:**
+
+1. [Introduction](#1-introduction)
+2. [Overall Architecture](#2-overall-architecture)
+3. [Component Deep Dive](#3-component-deep-dive)
+ * [3.1. `xiaozhi-server` (Core AI Engine - Python Implementation)](#31-xiaozhi-server-core-ai-engine---python-implementation)
+ * [3.2. `manager-api` (Management Backend - Java Spring Boot Implementation)](#32-manager-api-management-backend---java-spring-boot-implementation)
+ * [3.3. `manager-web` (Web Management Frontend - Vue.js Implementation)](#33-manager-web-web-management-frontend---vuejs-implementation)
+ * [3.4. `manager-mobile` (Mobile Management Console - uni-app Implementation)](#34-manager-mobile-mobile-management-console---uniapp-implementation)
+ * [3.5. `digital-human` (Digital Human Test Module - Python+Web Implementation)](#35-digital-human-digital-human-test-module---pythonweb-implementation)
+4. [Data Flow and Interaction Mechanisms](#4-data-flow-and-interaction-mechanisms)
+5. [Key Features Summary](#5-key-features-summary)
+6. [Deployment and Configuration Overview](#6-deployment-and-configuration-overview)
+
+---
+
+## 1. Introduction
+
+The `xiaozhi-esp32-server` project is a **comprehensive backend system** designed to support intelligent hardware based on ESP32. Its core goal is to enable developers to quickly build a robust server infrastructure that can understand natural language commands, interact efficiently with various AI services (for speech recognition, natural language understanding, and speech synthesis), manage IoT devices, and provide a web-based user interface for system configuration and management. By integrating multiple cutting-edge technologies into a cohesive and extensible platform, this project aims to simplify and accelerate the development process of customizable voice assistants and intelligent control systems. It is not just a simple server, but a bridge connecting hardware, AI capabilities, and user management.
+
+---
+
+## 2. Overall Architecture
+
+The `xiaozhi-esp32-server` system adopts a **distributed, multi-component collaborative** architectural design, ensuring modularity, maintainability, and scalability. Each core component has its specific role and works in coordination. The main components include:
+
+1. **ESP32 Hardware (Client Device):**
+ This is the physical smart hardware device that end-users directly interact with. Its main responsibilities include:
+ * Capturing user voice commands.
+ * Securely sending captured raw audio data to `xiaozhi-server` for processing.
+ * Receiving synthesized voice responses from `xiaozhi-server` and playing them through speakers.
+ * Controlling other connected peripherals or IoT devices (such as smart bulbs, sensors, etc.) based on instructions received from `xiaozhi-server`.
+
+2. **`xiaozhi-server` (Core AI Engine - Python Implementation):**
+ This Python-based server is the "brain" of the entire system, responsible for handling all voice-related logic and AI interactions. Its key responsibilities are detailed as follows:
+ * Establishing **stable, low-latency real-time bidirectional communication links** with ESP32 devices through the WebSocket protocol.
+ * Receiving audio streams from ESP32 and using Voice Activity Detection (VAD) technology to precisely segment valid speech segments.
+ * Integrating and calling Automatic Speech Recognition (ASR) services (configurable for local or cloud), converting speech segments to text.
+ * Interacting with Large Language Models (LLMs) to parse user intent, generate intelligent responses, and support complex natural language understanding tasks.
+ * Managing context information and user memory in multi-turn dialogues to provide coherent interaction experiences.
+ * Calling Text-to-Speech (TTS) services to synthesize natural and fluent speech from LLM-generated text responses.
+ * Executing custom commands through a flexible **plugin system**, including IoT device control logic.
+ * Obtaining its detailed runtime operation configuration from the `manager-api` service.
+
+3. **`manager-api` (Management Backend - Java Spring Boot Implementation):**
+ This is an application built using the Java Spring Boot framework, providing a secure RESTful API for system management and configuration. It serves not only as the backend support for the `manager-web` console but also as the configuration data source for `xiaozhi-server`. Its core functions include:
+ * Providing user authentication (login, permission verification) and user account management functions for the Web console.
+ * Registration, information management of ESP32 devices, and maintenance of device-specific configurations.
+ * Persistently storing system configurations in the **MySQL database**, such as user-selected AI service providers, API keys, device parameters, plugin settings, etc.
+ * Providing specific API endpoints for `xiaozhi-server` to pull its required latest configuration.
+ * Managing TTS voice options, handling OTA (Over-The-Air) firmware update processes, and related metadata.
+ * Utilizing **Redis** as a high-speed cache to store hotspot data (such as session information, frequently accessed configurations) to improve API response speed and overall system performance.
+
+4. **`manager-web` (Web Control Panel - Vue.js Implementation):**
+ This is a Single Page Application (SPA) built with Vue.js, providing system administrators with a graphical, user-friendly operation interface. Its main capabilities include:
+ * Conveniently configuring various AI services used by `xiaozhi-server` (such as ASR, LLM, TTS provider switching, parameter adjustment).
+ * Managing platform user accounts, role assignment, and permission control.
+ * Managing registered ESP32 devices and their related settings.
+ * (Potential functionality) Monitoring system operation status, viewing logs, troubleshooting, etc.
+ * Comprehensive interaction with all backend management functions provided by `manager-api`.
+
+5. **`manager-mobile` (Mobile Management Console - uni-app Implementation):**
+ This is a cross-platform mobile management application based on uni-app v3 + Vue 3 + Vite, supporting App (Android & iOS) and WeChat Mini Program. Its main capabilities include:
+ * Providing a convenient management interface on mobile devices, similar in functionality to manager-web but optimized for mobile platforms.
+ * Supporting core functions such as user login, device management, and AI service configuration.
+ * Cross-platform adaptation, allowing a single codebase to run on iOS, Android, and WeChat Mini Programs.
+ * Integrating with `manager-api` through alova + `@alova/adapter-uniapp`.
+ * Using pinia for state management to ensure data consistency.
+
+6. **`digital-human` (Digital Human Test Module - Python+Web Implementation):**
+ This is an independent digital human test module that provides a local test page, frontend interaction resources, a wake word runtime, and an event bridge for end-to-end debugging of the digital human interaction flow. Its main capabilities include:
+ * Providing a browser-based digital human test page for validating audio playback, audio reception, and interaction flow.
+ * Integrating a local wake word runtime with keyword detection based on Sherpa-ONNX.
+ * Bridging page state and the local runtime through an event bridge to simplify wake word debugging.
+ * Existing as a standalone module alongside `xiaozhi-server`, `manager-web`, and `manager-api`, making independent development and deployment easier.
+
+**High-Level Interaction Flow Overview:**
+
+* **Voice Interaction Main Line:** After the **ESP32 device** captures user voice, it transmits audio data in real-time to **`xiaozhi-server`** through **WebSocket**. After `xiaozhi-server` completes a series of AI processing (VAD, ASR, LLM interaction, TTS), it sends the synthesized voice response back to the ESP32 device for playback through WebSocket. All real-time interactions directly related to voice are completed in this link.
+* **Management Configuration Main Line:** Administrators access the **`manager-web`** console through a browser. `manager-web` executes various management operations (such as modifying configurations, managing users or devices) by calling **RESTful HTTP interfaces** provided by **`manager-api`**. Data is passed between them in JSON format.
+* **Configuration Synchronization:** **`xiaozhi-server`** actively pulls its latest operation configuration from **`manager-api`** through HTTP requests when starting or when specific update mechanisms are triggered. This ensures that configuration changes made by administrators in the Web interface can be effectively applied to the operation of the core AI engine in a timely manner.
+
+This **frontend-backend separation, core service and management service separation** architectural design allows `xiaozhi-server` to focus on efficient real-time AI processing tasks, while `manager-api` and `manager-web` together provide a powerful and easy-to-use management and configuration platform. Each component has clear responsibilities, facilitating independent development, testing, deployment, and expansion.
+
+```
+xiaozhi-esp32-server
+ 鈹溾攢 xiaozhi-server Port 8000 Python development Responsible for ESP32 communication
+ 鈹溾攢 manager-web Port 8001 Node.js+Vue development Responsible for providing web interface for console
+ 鈹溾攢 manager-api Port 8002 Java development Responsible for providing console API
+ 鈹溾攢 manager-mobile Cross-platform mobile application uni-app+Vue3 development Responsible for providing mobile console management
+ 鈹斺攢 digital-human Digital human test module Python+Web implementation Responsible for local test page, wake word runtime, and event bridge
+```
+
+---
+
+## 3. Component Deep Dive
+
+### 3.1. `xiaozhi-server` (Core AI Engine - Python Implementation)
+
+The `xiaozhi-server` is the intelligent core of the system, responsible for processing voice interactions, interfacing with AI services, and managing communication with ESP32 devices.
+
+* **Purpose:**
+ * To provide real-time processing of voice commands from ESP32 devices.
+ * To integrate with various AI services for Speech-to-Text (ASR), Natural Language Understanding (via Large Language Models - LLMs), Text-to-Speech (TTS), Voice Activity Detection (VAD), Intent Recognition, and Memory.
+ * To manage dialogue flow and context with users.
+ * To execute custom functions and control IoT devices based on user commands.
+ * To be dynamically configurable through the `manager-api`.
+
+* **Core Technologies:**
+ * **Python 3:** The primary programming language.
+ * **Asyncio:** Python's asynchronous programming framework, crucial for handling concurrent WebSocket connections and non-blocking I/O for AI service API calls.
+ * **`websockets` Library:** For WebSocket server implementation.
+ * **HTTP Client (e.g., `aiohttp`, `httpx`):** For asynchronous HTTP requests to `manager-api` and external AI services.
+ * **YAML (PyYAML):** For local configuration file parsing.
+
+* **Key Implementation Aspects:**
+
+ 1. **AI Service Provider Pattern (`core/providers/`):**
+ * **Concept:** A flexible design for integrating AI services. Each service type (ASR, TTS, LLM, etc.) has an abstract base class defining a common interface. Concrete classes implement this interface for specific vendors or local models.
+ * **Benefit:** Allows easy switching of AI service backends via configuration and simplifies adding new service integrations.
+ * **Initialization:** `core/utils/modules_initialize.py` acts as a factory to load and instantiate configured providers.
+
+ 2. **WebSocket Communication & Connection Handling (`core/websocket_server.py`, `core/connection.py`):**
+ * **Server Setup:** Manages WebSocket connections from ESP32 devices.
+ * **Connection Isolation:** Each ESP32 client gets a dedicated `ConnectionHandler` instance, isolating its session state and dialogue.
+ * **Dynamic Configuration Updates:** Can fetch updated configurations from `manager-api` and re-initialize AI service modules live, without a full server restart.
+
+ 3. **Message Handling & Dialogue Flow (`core/handle/`):**
+ * Employs a modular handler pattern. The `ConnectionHandler` dispatches message processing to specialized modules based on message type or dialogue phase (e.g., `receiveAudioHandle.py` for audio input, `intentHandler.py` for NLU, `functionHandler.py` for plugin execution, `sendAudioHandle.py` for TTS output).
+
+ 4. **Plugin System for Extensible Functions (`plugins_func/`):**
+ * **Purpose:** Allows adding custom "skills" (e.g., weather, news, Home Assistant control).
+ * **Mechanism:** Plugins define functions and schemas. The LLM can request execution of these functions (function calling). `loadplugins.py` and `register.py` manage plugin discovery and registration.
+
+ 5. **Configuration Management (`config/`):**
+ * Loads settings from a local `config.yaml` and merges them with configurations fetched from `manager-api` (via `manage_api_client.py`), enabling remote dynamic configuration.
+ * `logger.py` sets up structured application logging.
+ * `config/assets/` stores predefined audio files for system notifications.
+
+ 6. **Auxiliary HTTP Server (`core/http_server.py`):**
+ * Handles specific HTTP requests, notably for OTA firmware updates (`/xiaozhi/ota/`) and other utility endpoints.
+
+### 3.2. `manager-api` (Management Backend - Java Spring Boot Implementation)
+
+The `manager-api` component is a backend server built using Java and the Spring Boot framework, serving as the administrative hub.
+
+* **Purpose:**
+ * Provide a secure RESTful API for the `manager-web` frontend.
+ * Act as a centralized configuration provider for `xiaozhi-server`.
+ * Manage persistent data (users, devices, AI configurations, voice timbres, OTA firmware).
+
+* **Core Technologies:**
+ * **Java 21 & Spring Boot 3:** Core language and framework.
+ * **Spring MVC:** For building REST controllers.
+ * **MyBatis-Plus:** ORM for database interaction with MySQL.
+ * **MySQL:** Relational database.
+ * **Druid:** JDBC connection pool.
+ * **Redis (Spring Data Redis):** For caching.
+ * **Apache Shiro:** Security framework for authentication and authorization.
+ * **Liquibase:** Database schema migration.
+ * **Knife4j:** OpenAPI (Swagger) API documentation.
+ * **Maven:** Build and dependency management.
+
+* **Key Implementation Aspects:**
+
+ 1. **Modular Architecture (`modules/` package):**
+ * Business logic is organized into distinct modules (e.g., `sys` for users/roles, `agent` for assistant configs, `device` for ESP32s, `config` for `xiaozhi-server` settings, `security`, `timbre`, `ota`).
+ * Each module typically follows a layered pattern: Controller, Service, DAO (Mapper), Entity, DTO.
+
+ 2. **Layered Architecture:**
+ * **Controller Layer (`@RestController`):** Defines API endpoints, handles HTTP request/response.
+ * **Service Layer (`@Service`):** Contains business logic, transaction management.
+ * **Data Access Layer (MyBatis-Plus Mappers):** Interacts with the MySQL database.
+
+ 3. **Common Functionalities (`common/` package):**
+ * Provides shared code: base classes, global configurations (Spring, MyBatis, Redis, Knife4j), custom annotations (e.g., `@LogOperation`), AOP aspects, global exception handling, utility classes, and XSS protection.
+
+ 4. **Security (Apache Shiro):**
+ * Manages user authentication and permissions for accessing API endpoints. Configured with Shiro Realms and security filters.
+
+ 5. **Database Schema Management (Liquibase):**
+ * Ensures consistent database structure across environments through versioned schema changes.
+
+### 3.3. `manager-web` (Web Control Panel - Vue.js Implementation)
+
+The `manager-web` is a Single Page Application (SPA) providing the administrative user interface.
+
+* **Purpose:**
+ * Offer a web-based control panel for system configuration and management.
+ * Enable administrators to configure `xiaozhi-server`'s AI services, manage users and devices, customize voice timbres, and handle OTA updates.
+
+* **Core Technologies:**
+ * **Vue.js 2 & Vue CLI:** Core JavaScript framework and build tools.
+ * **Vue Router:** For client-side routing within the SPA.
+ * **Vuex:** For centralized state management.
+ * **Element UI:** UI component library for a consistent look and feel.
+ * **SCSS:** CSS preprocessor.
+ * **HTTP Client (Flyio or Axios):** For API calls to `manager-api`.
+ * **Workbox:** For PWA features (caching, service worker).
+ * **Opus Libraries:** For potential in-browser audio recording/playback.
+
+* **Key Implementation Aspects:**
+
+ 1. **SPA Structure:** Single HTML page with dynamic view updates.
+ 2. **Component-Based Architecture:** UI built from reusable Vue components (`.vue` files in `src/views/` for pages and `src/components/` for smaller elements).
+ 3. **Client-Side Routing (`src/router/index.js`):** Maps browser URLs to view components, with route guards for authentication.
+ 4. **State Management (`src/store/index.js`):** Vuex manages global state (user info, device lists, etc.) via state, getters, mutations, and actions (often involving API calls).
+ 5. **API Communication (`src/apis/`):** Modularized API service files make asynchronous calls to `manager-api`.
+ 6. **Build Process & PWA Features:** Vue CLI (Webpack) bundles assets. Workbox enables PWA features like caching.
+ 7. **Environment Configuration (`.env` files):**
+ * The `.env` (and `.env.development`, `.env.production`, etc.) files in the project root directory are used to define environment variables. These variables (such as `VUE_APP_API_BASE_URL` to specify the base URL of `manager-api`) can be accessed in the application code through `process.env.VUE_APP_XXX`, allowing configuration of different parameters for different build environments (development, testing, production).
+
+`manager-web` constructs a powerful, maintainable, and user-friendly management interface through the comprehensive application of these technologies, providing solid frontend support for the configuration and monitoring of the `xiaozhi-esp32-server` system.
+
+### 3.4. `manager-mobile` (Mobile Management Console - uni-app Implementation)
+
+The `manager-mobile` component is a cross-platform mobile management application based on uni-app v3 + Vue 3 + Vite, supporting App (Android & iOS) and WeChat Mini Program. It provides system administrators with a mobile management interface, making management operations more convenient.
+
+* **Core Objectives:**
+ * Provide a convenient management interface on mobile devices, similar in functionality to manager-web but optimized for mobile platforms.
+ * Support core functions such as user login, device management, and AI service configuration.
+ * Cross-platform adaptation, allowing a single codebase to run on iOS, Android, and WeChat Mini Programs.
+ * Provide mobile users with a smooth and efficient management experience.
+
+* **Platform Compatibility:**
+
+| H5 | iOS | Android | WeChat Mini Program |
+| -- | --- | ------- | ------------------ |
+| 脳 | 鈭� | 鈭� | 鈭� |
+
+* **Core Technologies:**
+ * **uni-app v3:** A framework for developing all frontend applications using Vue.js, supporting iOS, Android, H5, and various mini-programs.
+ * **Vue 3:** A progressive framework for building user interfaces, providing better performance and new features.
+ * **Vite:** The next generation frontend development and build tool, offering an extremely fast development experience.
+ * **pnpm:** A fast, disk space-efficient package manager.
+ * **alova:** A lightweight, flexible request strategy library, paired with @alova/adapter-uniapp to adapt to the uni-app environment.
+ * **pinia:** State management library for Vue, replacing Vuex, providing a simpler API and better TypeScript support.
+ * **UnoCSS:** A high-performance and extremely flexible instant atomic CSS engine.
+ * **TypeScript:** Provides a type-safe development experience.
+
+* **Key Implementation Details:**
+
+ 1. **Cross-Platform Architecture:**
+ * Based on the uni-app framework, achieving the goal of "write once, run anywhere," significantly reducing development and maintenance costs.
+ * Handling platform-specific code through conditional compilation to address the characteristics and limitations of different platforms.
+
+ 2. **Project Structure:**
+ * **`src/App.vue`:** The root component of the application, defining global styles and configurations.
+ * **`src/main.ts`:** The entry file of the application, responsible for initializing the Vue instance, registering plugins, and setting up route interceptors.
+ * **`src/pages/`:** Stores application page components, such as login pages, device management pages, etc.
+ * **`src/layouts/`:** Defines application layout components, such as default layouts, layouts with tabbar, etc.
+ * **`src/api/`:** Encapsulates communication logic with backend APIs.
+ * **`src/store/`:** Uses pinia for state management.
+ * **`src/components/`:** Stores reusable components.
+ * **`src/utils/`:** Provides common utility functions.
+
+ 3. **Network Requests:**
+ * Implements network requests based on alova + @alova/adapter-uniapp, unified handling of request headers, authentication, errors, etc.
+ * Request addresses and environment configurations are managed through .env files, supporting switching between different environments.
+
+ 4. **Routing and Authentication:**
+ * Uses uni-app's routing system, combined with route interceptors to implement page login verification and permission control.
+ * When unlogged users access pages requiring authentication, they are redirected to the login page.
+
+ 5. **State Management:**
+ * Uses pinia to manage application state, such as user information, device lists, etc.
+ * Implements persistent storage of state through the pinia-plugin-persistedstate plugin.
+
+ 6. **Build and Release:**
+ * Supports multiple build commands, such as building WeChat Mini Programs, Android, and iOS Apps.
+ * Uses HBuilderX for cloud packaging of Apps, simplifying the packaging process.
+
+`manager-mobile` provides users with a fully functional, smooth mobile management tool through the application of these technologies, allowing administrators to perform system management and configuration anytime, anywhere.
+
+### 3.5. `digital-human` (Digital Human Test Module - Python+Web Implementation)
+
+The `digital-human` component is an independent digital human test module responsible for providing a local test page, page resources, a wake word runtime, and an event bridge. It is primarily used to debug the complete digital human interaction flow and to validate frontend interaction, audio capability, and local wake word behavior.
+
+* **Core Objectives:**
+ * Provide an independent local test page for validating digital human interaction effects.
+ * Provide a local wake word runtime for keyword detection and event reporting.
+ * Provide an event bridge between the frontend page and the local runtime to connect the test flow.
+ * Exist as a standalone module for easier independent development, debugging, and deployment.
+
+* **Core Technologies:**
+ * **Python 3:** Used for the local test runtime, service startup, wake word lifecycle management, and event bridge coordination.
+ * **Native HTML/CSS/JavaScript:** Used to build the digital human test page and interaction logic.
+ * **Sherpa-ONNX:** Used for local wake word keyword detection.
+ * **ThreadingHTTPServer / WebSocket bridge:** Used to serve the local page, health check endpoint, and event communication.
+
+* **Key Implementation Aspects:**
+
+ 1. **Module Entry and Page Resources:**
+ * `start.py` is the module entry point responsible for starting the local test runtime.
+ * `index.html` is the entry page for digital human testing.
+ * The `js/`, `css/`, `images/`, and `resources/` directories provide frontend scripts, styles, and static resources.
+
+ 2. **Local Test Runtime:**
+ * The `wakeword_runtime/` directory contains the local wake word runtime implementation.
+ * The runtime is responsible for configuration loading, logging initialization, audio capture, keyword detection, and lifecycle management.
+ * The module exposes a local page URL, event bridge URL, and health check endpoint through a local HTTP service.
+
+ 3. **Wake Word Detection Flow:**
+ * Uses Sherpa-ONNX keyword detection models to support local wake word triggering.
+ * Model files and keyword configurations are stored in `wakeword_runtime/models/` and related configuration files.
+ * The page can observe runtime state and trigger results through the event bridge.
+
+ 4. **Debugging and Integration Positioning:**
+ * After startup, developers can open the local page in a browser to validate playback, reception, and interaction logic.
+ * The event bridge synchronizes wake word state to the page, which helps with local debugging.
+ * Detailed model download, runtime configuration, and usage instructions are documented in `docs/digital-human-wakeword.md`.
+
+The `digital-human` module allows digital human related capabilities to be validated independently from the main service, reducing the complexity of page debugging, wake word integration, and local environment setup.
+
+---
+
+## 4. Data Flow and Interaction Mechanisms
+
+The `xiaozhi-esp32-server` system coordinates work through well-defined data flows and interaction protocols between components. The main communication methods rely on WebSocket protocol optimized for real-time interaction and RESTful API suitable for client-server requests.
+
+**4.1. Core Voice Interaction Flow (ESP32 Device <-> `xiaozhi-server`)**
+
+This flow is real-time, primarily using WebSocket for low-latency, bidirectional data exchange.
+
+* **Communication Protocol Documentation:**
+ * Detailed communication protocol documentation can be accessed at: https://ccnphfhqs21z.feishu.cn/wiki/M0XiwldO9iJwHikpXD5cEx71nKh
+ * This document details the WebSocket communication protocol between ESP32 devices and `xiaozhi-server`, including:
+ * Connection establishment and handshake process
+ * Audio data transmission format
+ * Control command format
+ * Status report format
+ * Error handling mechanism
+
+* **Connection Establishment and Handshake:**
+ * The ESP32 device, as a client, actively initiates a WebSocket connection request to the specified endpoint of `xiaozhi-server` (e.g., `ws://<server-IP>:<WebSocket-port>/xiaozhi/v1/`).
+ * `xiaozhi-server` (`core/websocket_server.py`) receives the connection and instantiates an independent `ConnectionHandler` object for each successfully connected ESP32 device to manage the entire lifecycle of that session.
+ * After the connection is established, an initial handshake process may be executed (handled by `core/handle/helloHandle.py`) to exchange device identification, authentication information, protocol version, or basic status.
+
+* **Audio Uplink Transmission (ESP32 -> `xiaozhi-server`):**
+ * After a user speaks to the ESP32 device, the device's microphone captures raw audio data (usually in PCM or compressed formats like Opus).
+ * The ESP32 pushes these audio data chunks as WebSocket **binary messages** in real-time to the corresponding `ConnectionHandler` in `xiaozhi-server`.
+ * The server-side `core/handle/receiveAudioHandle.py` module is responsible for receiving, buffering, and processing these audio data.
+
+* **AI Core Processing (within `xiaozhi-server`):**
+ * **VAD (Voice Activity Detection):** `receiveAudioHandle.py` uses the configured VAD provider (such as SileroVAD) to analyze the audio stream, accurately identifying the start and end points of speech, filtering out silent or noise segments.
+ * **ASR (Automatic Speech Recognition):** Detected valid speech segments are sent to the configured ASR provider (local such as FunASR, or cloud services). The ASR engine converts audio signals into text strings.
+ * **NLU/LLM (Natural Language Understanding/Large Language Model):** The ASR output text, along with the current dialogue context history obtained from the Memory provider, and the description schemas of available functions (tools) loaded from `plugins_func/`, are passed to the configured LLM provider.
+ * **Function Call Execution (if LLM decides needed):** If the LLM analysis determines that an external function needs to be called (e.g., querying weather, controlling home appliances), it generates a structured function call request. `core/handle/functionHandler.py` receives this request, finds and executes the corresponding Python function defined in `plugins_func/`, and returns the function's execution result to the LLM. The LLM then generates the final natural language response based on this result.
+ * **Response Generation:** The LLM synthesizes all information (user input, context, function call results, etc.) to generate the final text response.
+ * **Memory Update:** The current round of interaction (user question, LLM response, possible function calls) is processed by the Memory provider to update the dialogue history for subsequent interactions.
+ * **TTS (Text-to-Speech):** The final text response generated by the LLM is sent to the configured TTS provider, which synthesizes the text into a speech data stream (e.g., MP3 or WAV format).
+
+* **Audio Downlink Response (`xiaozhi-server` -> ESP32):**
+ * The speech data stream synthesized by the TTS provider is sent in real-time as WebSocket **binary messages** back to the ESP32 device through the `core/handle/sendAudioHandle.py` module.
+ * The ESP32 device receives these audio data chunks and immediately plays them to the user through the speaker.
+
+* **Control and Status Messages (Bidirectional):**
+ * In addition to audio streams, ESP32 and `xiaozhi-server` also exchange **text messages** through WebSocket, these messages are usually encapsulated in JSON format.
+ * **ESP32 -> Server:** The device may send status reports (such as network conditions, microphone status), error codes, or specific control commands (e.g., "stop TTS playback" triggered by user button press).
+ * **Server -> ESP32:** The server may send control instructions to the device (such as "start listening", "stop listening", adjust sensitivity, send specific configuration parameters).
+ * Modules like `core/handle/abortHandle.py` (handling interrupt requests), `core/handle/reportHandle.py` (handling device reports) are responsible for parsing and responding to these control/status messages.
+
+**4.2. Management and Configuration Flow (`manager-web` <-> `manager-api` <-> `xiaozhi-server`)**
+
+This flow primarily relies on HTTP/HTTPS-based RESTful API for request-response interactions.
+
+* **Administrator UI Backend Interaction (`manager-web` -> `manager-api`):**
+ * When administrators perform operations in the `manager-web` interface (e.g., saving a configuration, adding a new user, registering an ESP32 device):
+ * The Vue.js frontend application (`manager-web`) will initiate asynchronous HTTP requests (usually GET, POST, PUT, DELETE) to the corresponding REST API endpoints of `manager-api` through its API encapsulation module (located in `src/apis/module/`).
+ * Request and response bodies typically use JSON format.
+ * The `@RestController` classes in `manager-api` receive these requests. The **Apache Shiro** framework will first perform authentication and authorization checks on the requests.
+ * After verification, the Controller distributes the request to the corresponding Service layer to handle business logic. The Service layer may interact with the MySQL database (through MyBatis-Plus) and may utilize Redis for data caching.
+ * After processing, `manager-api` returns an HTTP response in JSON format to `manager-web`.
+ * `manager-web` updates its Vuex state store and user interface display based on the response results.
+
+* **Configuration Synchronization (`manager-api` -> `xiaozhi-server`):**
+ * The operation of `xiaozhi-server` depends on dynamic configurations obtained from `manager-api` (such as currently selected AI service providers and their API keys).
+ * **Pull Mechanism:** The `config/manage_api_client.py` module within `xiaozhi-server`, when the server starts or through specific update triggers (e.g., when `WebSocketServer.update_config()` is called), will initiate an HTTP GET request to a specified endpoint of `manager-api` (e.g., provided by a Controller in `modules/config/controller/`).
+ * `manager-api` responds to this request, returning the configuration data required by `xiaozhi-server` (in JSON format).
+ * After receiving the configuration, `xiaozhi-server` will update its internal state and may reinitialize relevant AI service modules to make the new configuration effective.
+
+* **OTA Firmware Update Flow (Conceptual Description):**
+ * Administrators upload new ESP32 firmware packages to specific endpoints of `manager-api` through the `manager-web` interface.
+ * `manager-api` stores the firmware files and records related metadata (version number, applicable device models, etc.).
+ * When administrators trigger OTA updates for specific devices:
+ * `manager-api` may notify `xiaozhi-server` (the specific notification mechanism may be a polling checkpoint, or `xiaozhi-server` exposes an API to receive update notifications, or more loosely coupled like message queues).
+ * `xiaozhi-server` can then send an instruction message containing the firmware download URL to the target ESP32 device through WebSocket.
+ * After receiving the instruction, the ESP32 device downloads the firmware through an HTTP GET request from that URL. This URL may point to a path served by the `SimpleHttpServer` running on `xiaozhi-server` itself (such as `/xiaozhi/ota/`), or in some architectures, it may directly point to `manager-api` or a dedicated file server.
+
+**4.3. Main Protocol Summary:**
+
+* **WebSocket:** Selected for the communication link between ESP32 and `xiaozhi-server` because it is very suitable for real-time, low-latency, bidirectional data stream transmission (especially audio), as well as asynchronous control message delivery.
+* **RESTful APIs (based on HTTP/HTTPS, usually using JSON as the data exchange format):** This is the standard way for web service communication. Used for request-response interactions between `manager-web` (client) and `manager-api` (server), and also for `xiaozhi-server` (as client) to pull configuration information from `manager-api` (as server). Its stateless nature, wide library support, and easy-to-understand semantics make it an ideal choice for such interactions.
+
+This multi-protocol communication strategy ensures that different types of interaction requirements within the system can be handled efficiently and appropriately, balancing real-time performance and standardized request-response patterns.
+
+---
+
+## 5. Key Features Summary
+
+The `xiaozhi-esp32-server` system provides a series of rich features aimed at supporting developers in building advanced voice control applications:
+
+1. **Comprehensive Voice Interaction Backend:** Provides an end-to-end solution from voice capture guidance to response generation and action execution.
+2. **Modular and Pluggable AI Services:**
+ * Supports a wide range of ASR (Automatic Speech Recognition), LLM (Large Language Model), TTS (Text-to-Speech), VAD (Voice Activity Detection), Intent Recognition, and Memory providers.
+ * Allows dynamic selection and configuration of these services (including cloud-based APIs and local models) to balance cost, performance, privacy, and language requirements.
+3. **Advanced Dialogue Management:**
+ * Supports natural interaction, with wake word to start dialogue, manual (push-to-talk) dialogue, and real-time interruption of system responses.
+ * Includes contextual memory to maintain coherence in multi-turn dialogues.
+ * Has automatic sleep mode after a period of inactivity.
+4. **Multi-language Capabilities:**
+ * Supports recognition and synthesis in multiple languages, including Mandarin, Cantonese, English, Japanese, and Korean (specific capabilities depend on the selected ASR/LLM/TTS providers).
+5. **Extensible Functions through Plugins:**
+ * Powerful plugin system allows developers to add custom "skills" or functions (e.g., getting weather, controlling smart home devices, accessing news).
+ * These functions can be triggered by the LLM using its function calling capability, based on provided schemas.
+ * Built-in support for Home Assistant integration.
+6. **IoT Device Control:**
+ * Designed to manage and control smart home devices and other IoT hardware through voice commands, utilizing the plugin system.
+7. **Web-based Management Console (`manager-web` & `manager-api`):**
+ * Provides a comprehensive graphical interface for:
+ * System configuration (AI service selection, API keys, operation parameters).
+ * Role-based access control user management.
+ * ESP32 device registration and management.
+ * Voice timbre/TTS voice customization.
+ * OTA (Over-The-Air) firmware update management for ESP32 devices.
+ * System parameter and dictionary management.
+8. **Flexible Deployment Options:**
+ * Supports deployment through Docker containers (for simplified server-only or full-stack setup) and directly from source code, adapting to various environments and user expertise.
+9. **Dynamic Remote Configuration:**
+ * `xiaozhi-server` can obtain its configuration from `manager-api`, allowing real-time updates of AI providers and settings without restarting the server.
+10. **Open Source and Community-Driven:**
+ * Licensed under MIT License, encouraging transparency, collaboration, and community contribution.
+11. **Cost-Effective Solution:**
+ * Provides an "Entry Level Free Settings" path, utilizing free tiers of AI services or local models, making it easy to experiment and for personal projects.
+12. **Progressive Web Application (PWA) Features:**
+ * The `manager-web` control panel includes Service Worker integration to enhance caching and potential offline access capabilities.
+13. **Detailed API Documentation:**
+ * `manager-api` provides OpenAPI (Swagger) documentation through Knife4j for clear understanding and testing of its RESTful endpoints.
+
+These features together make `xiaozhi-esp32-server` a powerful, adaptable, and user-friendly platform for building complex voice interaction applications.
+
+---
+
+## 6. Deployment and Configuration Overview
+
+The `xiaozhi-esp32-server` system is designed with flexibility in mind, providing multiple deployment methods and comprehensive configuration options to adapt to different usage scenarios and requirements.
+
+**Deployment Options:**
+
+The project can be deployed in multiple ways, mainly including using Docker to simplify the installation process, or deploying directly from source code for greater control and development.
+
+1. **Docker-based Deployment:**
+ * **Simplified Installation (Only `xiaozhi-server`):** This option only deploys the core Python-based `xiaozhi-server`. It is suitable for users who mainly need voice AI processing capabilities and IoT control, without requiring the complete Web management interface and database support functions (such as OTA). In this mode, configuration is typically managed through local files (`config.yaml`), but if needed, it can still point to an existing `manager-api` instance.
+ * **Full Module Installation (All Components):** This scheme deploys all core components: `xiaozhi-server`, Java-based `manager-api`, and Vue.js-based `manager-web`, along with required database services (MySQL and Redis). This provides a complete system experience, including a Web control panel for comprehensive configuration and management.
+ * The project provides `Dockerfile` definitions for each service and uses `docker-compose.yml` files (e.g., `docker-compose.yml` for basic version, `docker-compose_all.yml` for full-featured version) to orchestrate and manage multi-container deployment. Additionally, a `docker-setup.sh` script may be provided to assist in automating part of the Docker environment setup work.
+
+2. **Source Code Deployment:**
+ * This method requires manual setup of the corresponding development environment for each component: Python environment for `xiaozhi-server`, Java/Maven environment for `manager-api`, Node.js/Vue CLI environment for `manager-web`.
+ * For full module installation, MySQL and Redis database services also need to be manually installed and configured.
+ * This approach is typically used for project development, deep customization, debugging, or in production scenarios with special environmental requirements.
+
+**Configuration Management:**
+
+Configuration is key to customizing system behavior, especially in selecting AI service providers and managing API keys.
+
+1. **`xiaozhi-server` Configuration:**
+ * **Local `config.yaml`:** A main YAML format configuration file located in the `xiaozhi-server` root directory. It defines server ports, selected AI service providers (ASR, LLM, TTS, VAD, Intent Recognition, Memory modules, etc.), their respective API keys or model paths, plugin configurations, and log levels.
+ * **Remote Configuration through `manager-api`:** `xiaozhi-server` is designed to obtain its operation configuration from `manager-api`. Settings obtained from `manager-api` typically override settings with the same name in the local `config.yaml`. This brings two major benefits:
+ * **Centralized Management:** All configurations can be managed uniformly through the `manager-web` interface.
+ * **Dynamic Updates:** `xiaozhi-server` can refresh its configuration and reinitialize AI modules without completely restarting the service.
+ * `config/config_loader.py` and `config/manage_api_client.py` in `xiaozhi-server` are responsible for handling configuration loading, merging, and pulling logic from `manager-api`.
+
+2. **`manager-api` Configuration:**
+ * As a Spring Boot application, its configuration is mainly managed through the `application.properties` or `application.yml` file located in the `src/main/resources` directory.
+ * Key configuration items include: database connection information (MySQL URL, username, password), Redis server address and port, application service port (default 8002), Apache Shiro security-related settings, and configuration parameters for any integrated third-party services (such as Aliyun SMS).
+
+3. **`manager-web` Configuration:**
+ * Environment-specific settings for the Vue.js frontend application are managed through `.env` series files (e.g., `.env`, `.env.development`, `.env.production`) in the project root directory.
+ * The most critical configuration here is usually the API base URL address of the `manager-api` backend (e.g., `VUE_APP_API_BASE_URL`), to which the frontend application will send all API requests.
+
+4. **Predefined Configuration Schemes:**
+ * The project documentation (usually README) will recommend some common configuration combinations, for example:
+ * **"Entry Level Free Settings":** This scheme aims to utilize free tier quotas of cloud AI services or completely free local models to minimize users' initial usage costs and operating expenses.
+ * **"Full Streaming Configuration":** This scheme prioritizes system response speed and interaction fluency, typically choosing AI services that support streaming processing (possibly paid).
+ * These predefined schemes provide guidance for users to configure AI service providers in `xiaozhi-server` (through the `manager-web` interface or directly modifying `config.yaml`).
+
+In the case of full module deployment, it is recommended to use the `manager-web` control panel as the main operation interface for most configuration tasks, as it provides a user-friendly way to manage various settings that are persisted by `manager-api` and ultimately used by `xiaozhi-server`.
+
+---
diff --git a/xiaozhi-esp32-server/main/digital-human/README.md b/xiaozhi-esp32-server/main/digital-human/README.md
new file mode 100644
index 0000000..26b41c3
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/README.md
@@ -0,0 +1,46 @@
+鏈枃妗f槸寮�鍙戠被鏂囨。锛屽闇�閮ㄧ讲灏忔櫤鏈嶅姟绔紝[鐐瑰嚮杩欓噷鏌ョ湅閮ㄧ讲鏁欑▼](../../README.md#%E9%83%A8%E7%BD%B2%E6%96%87%E6%A1%A3)
+
+濡傞渶鏌ョ湅涓�浣撴満鏁板瓧浜鸿惤鍦伴儴缃层�並iosk 鍏ㄥ睆鍚姩鍜岀郴缁熺幆澧冮厤缃紝[鐐瑰嚮杩欓噷鏌ョ湅涓�浣撴満閮ㄧ讲鎸囧崡](../../docs/all-in-one-digital-human-setup.md)
+
+濡傞渶鏌ョ湅鍞ら啋璇嶆ā鍨嬩笅杞姐�佽繍琛屾椂閰嶇疆鍜岃缁嗕娇鐢ㄨ鏄庯紝[鐐瑰嚮杩欓噷鏌ョ湅鍞ら啋璇嶄笓棰樻枃妗(../../docs/digital-human-wakeword.md)
+
+# 椤圭洰浠嬬粛
+
+digital-human 鏄嫭绔嬬殑鏁板瓧浜烘祴璇曟ā鍧楋紝璐熻矗鎻愪緵鏈湴娴嬭瘯椤甸潰銆佸墠绔氦浜掕祫婧愩�佸敜閱掕瘝杩愯鏃跺拰浜嬩欢妗ヨ兘鍔涳紝鐢ㄤ簬鑱旇皟鏁翠釜鏁板瓧浜轰氦浜掗摼璺��
+
+# 蹇�熷惎鍔�
+
+瀹夎渚濊禆锛�
+
+```bash
+pip install -r wakeword_runtime/requirements.txt
+```
+
+鍚姩妯″潡锛�
+
+```bash
+python start.py
+```
+
+# 璁块棶鍦板潃
+
+鍚姩鍚庡彲璁块棶锛�
+
+- 椤甸潰鍦板潃锛歨ttp://127.0.0.1:8006/index.html
+- 浜嬩欢妗ュ湴鍧�锛歸s://127.0.0.1:8006/wakeword-ws
+- 鍋ュ悍妫�鏌ワ細http://127.0.0.1:8006/health
+
+# 鐩綍璇存槑
+
+- `start.py`锛氭ā鍧楀惎鍔ㄥ叆鍙�
+- `index.html`锛氭暟瀛椾汉娴嬭瘯椤甸潰鍏ュ彛
+- `wakeword_runtime`锛氭湰鍦板敜閱掕瘝杩愯鏃朵笌閰嶇疆鐩綍
+- `js`銆乣css`锛氶〉闈㈠墠绔剼鏈笌鏍峰紡
+- `images`銆乣resources`锛氶〉闈㈣祫婧愭枃浠�
+
+# 鐩稿叧鏂囨。
+
+- 涓�浣撴満閮ㄧ讲鎸囧崡锛氶�傜敤浜� x86 璁惧鏁存満钀藉湴閮ㄧ讲銆並iosk 灞曠ず鍜屽紑鏈鸿嚜鍚姩閰嶇疆
+ [../../docs/all-in-one-digital-human-setup.md](../../docs/all-in-one-digital-human-setup.md)
+- 鍞ら啋璇嶄笓棰樻枃妗o細閫傜敤浜庡敜閱掕瘝妯″瀷涓嬭浇銆佽繍琛屾椂閰嶇疆鍜屾湰鍦拌皟璇曡鏄�
+ [../../docs/digital-human-wakeword.md](../../docs/digital-human-wakeword.md)
diff --git a/xiaozhi-esp32-server/main/digital-human/css/bg.png b/xiaozhi-esp32-server/main/digital-human/css/bg.png
new file mode 100644
index 0000000..1e63f4d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/css/bg.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/digital-human/css/index.css b/xiaozhi-esp32-server/main/digital-human/css/index.css
new file mode 100644
index 0000000..e2e488b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/css/index.css
@@ -0,0 +1,1480 @@
+/* ==================== 鍏ㄥ眬鏍峰紡閲嶇疆 ==================== */
+* {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+body {
+ font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ height: 100vh;
+ width: 100vw;
+}
+
+/* ==================== 鑳屾櫙瀹瑰櫒 ==================== */
+.background-container {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ background-image: url('../images/1.png');
+ background-size: cover;
+ background-position: center;
+ background-repeat: no-repeat;
+ transition: background-image 0.5s ease-in-out;
+ z-index: -2;
+}
+
+.background-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.3);
+ z-index: -1;
+}
+
+/* ==================== 涓诲鍣� ==================== */
+.container {
+ position: relative;
+ width: 100vw;
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: flex-end;
+ /* 浠庡簳閮ㄥ紑濮嬫帓鍒� */
+ padding: 20px;
+ padding-bottom: 0;
+ /* 绉婚櫎搴曢儴padding锛岃妯″瀷鍙互寤朵几鍒伴〉闈㈠簳閮� */
+}
+
+/* ==================== 妯″瀷鍔犺浇鍖� ==================== */
+.model-container {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 300px;
+ height: 200px;
+ transform: translate(-50%, -50%);
+ user-select: none;
+}
+
+.model-loading {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ display: none;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ z-index: 999;
+ transition: opacity 0.5s ease-out, visibility 0.5s ease-out;
+ background-image: linear-gradient(transparent 50%, rgba(88, 101, 242, 0.05) 50%);
+ background-size: 100% 4px;
+ animation: scan 1.26s linear infinite;
+}
+
+.loading-char-float {
+ font-size: 22px;
+ letter-spacing: 3px;
+ position: relative;
+}
+
+.loading-char-float .main-char {
+ color: #f0f8ff;
+ text-shadow: 0 0 5px #5865f2, 0 0 10px rgba(88, 101, 242, 0.8);
+ display: inline-block;
+}
+
+.loading-char-float .main-char span {
+ animation: char-float 1.26s ease-in-out infinite;
+ display: inline-block;
+}
+
+.loading-char-float .shadow-char {
+ position: absolute;
+ top: 3px;
+ left: 0;
+ color: rgba(88, 101, 242, 0.3);
+ pointer-events: none;
+ animation: shadow-float 1.26s ease-in-out infinite;
+}
+
+.loading-char-float .main-char span:nth-child(1) {
+ animation-delay: 0s;
+}
+
+.loading-char-float .main-char span:nth-child(2) {
+ animation-delay: 0.11s;
+}
+
+.loading-char-float .main-char span:nth-child(3) {
+ animation-delay: 0.22s;
+}
+
+.loading-char-float .main-char span:nth-child(4) {
+ animation-delay: 0.33s;
+}
+
+.loading-char-float .main-char span:nth-child(5) {
+ animation-delay: 0.44s;
+}
+
+.loading-char-float .main-char span:nth-child(6) {
+ animation-delay: 0.55s;
+}
+
+@keyframes scan {
+ 0% {
+ background-position: 0 0;
+ }
+
+ 100% {
+ background-position: 0 100px;
+ }
+}
+
+@keyframes char-float {
+
+ 0%,
+ 100% {
+ transform: translateY(0);
+ opacity: 1;
+ text-shadow: 0 0 5px #5865f2, 0 0 10px rgba(88, 101, 242, 0.8);
+ }
+
+ 50% {
+ transform: translateY(-5px);
+ opacity: 0.9;
+ text-shadow: 0 0 10px #5865f2, 0 0 20px rgba(88, 101, 242, 0.8);
+ }
+}
+
+@keyframes shadow-float {
+
+ 0%,
+ 100% {
+ transform: translateY(3px);
+ opacity: 0.3;
+ }
+
+ 50% {
+ transform: translateY(8px);
+ opacity: 0.15;
+ }
+}
+
+/* ==================== Live2D鏄剧ず鍖哄煙 ==================== */
+#live2d-stage {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ display: block;
+ z-index: 100;
+ pointer-events: auto;
+}
+
+.status-indicator {
+ position: absolute;
+ top: 20px;
+ left: 50%;
+ transform: translateX(-50%);
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ background: rgba(0, 0, 0, 0.4);
+ backdrop-filter: blur(10px);
+ padding: 8px 16px;
+ border-radius: 20px;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ color: white;
+ font-size: 14px;
+ font-weight: 500;
+ z-index: 10;
+}
+
+.status-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ animation: pulse 2s infinite;
+}
+
+.status-disconnected {
+ background-color: #f44336;
+}
+
+.status-connecting {
+ background-color: #ff9800;
+}
+
+.status-connected {
+ background-color: #4caf50;
+}
+
+@keyframes pulse {
+
+ 0%,
+ 100% {
+ opacity: 1;
+ }
+
+ 50% {
+ opacity: 0.5;
+ }
+}
+
+/* ==================== 鑱婂ぉ娑堟伅娴侊紙寮瑰箷鏍峰紡锛� ==================== */
+.chat-container {
+ position: absolute;
+ bottom: 150px;
+ right: 20px;
+ z-index: 100;
+}
+
+.chat-stream {
+ width: 300px;
+ max-height: 200px;
+ overflow-y: auto;
+ overflow-x: hidden;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ pointer-events: none;
+ margin-bottom: 10px;
+}
+
+.chat-ipt {
+ width: 100%;
+ display: none;
+ justify-content: center;
+ margin-top: 10px;
+}
+
+.chat-ipt input {
+ width: 100%;
+ padding: 12px 16px;
+ border: none;
+ border-radius: 24px;
+ background: rgba(35, 39, 42, 0.85);
+ backdrop-filter: blur(10px);
+ color: white;
+ font-size: 14px;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+ transition: all 0.3s ease;
+}
+
+.chat-ipt input::placeholder {
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.chat-ipt input:focus {
+ outline: none;
+ border-color: rgba(88, 101, 242, 0.6);
+ box-shadow: 0 4px 16px rgba(88, 101, 242, 0.3);
+ background: rgba(45, 49, 52, 0.9);
+}
+
+.chat-message {
+ animation: slideInRight 0.3s ease-out;
+ pointer-events: auto;
+}
+
+.chat-message.ai .message-bubble {
+ background: transparent;
+ color: white;
+ align-self: flex-end;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
+}
+
+.chat-message.user .message-bubble {
+ background: rgba(88, 101, 242, 0.7);
+ color: white;
+ align-self: flex-start;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
+ backdrop-filter: blur(5px);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+}
+
+.chat-message.system .message-bubble {
+ background: transparent;
+ color: #b9bbbe;
+ align-self: center;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
+}
+
+.message-bubble {
+ padding: 8px 12px;
+ border-radius: 18px;
+ font-size: 14px;
+ max-width: 250px;
+ word-wrap: break-word;
+ backdrop-filter: blur(10px);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ animation: fadeIn 0.3s ease-out;
+}
+
+@keyframes slideInRight {
+ from {
+ transform: translateX(100%);
+ opacity: 0;
+ }
+
+ to {
+ transform: translateX(0);
+ opacity: 1;
+ }
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(10px);
+ }
+
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes fadeOut {
+ from {
+ opacity: 1;
+ }
+
+ to {
+ opacity: 0;
+ }
+}
+
+/* ==================== 搴曢儴鎺у埗鏍� ==================== */
+.control-bar {
+ position: fixed;
+ bottom: 20px;
+ left: 50%;
+ transform: translateX(-50%);
+ display: flex;
+ gap: 16px;
+ background: rgba(35, 39, 42, 0.9);
+ backdrop-filter: blur(20px);
+ padding: 12px 24px;
+ border-radius: 28px;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
+ z-index: 1000;
+ /* 纭繚鎺у埗鏍忓湪鏈�涓婂眰 */
+}
+
+.control-btn {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ width: 60px;
+ height: 60px;
+ border: none;
+ border-radius: 50%;
+ background: rgba(79, 84, 92, 0.8);
+ color: white;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ position: relative;
+}
+
+.control-btn:hover {
+ background: rgba(114, 137, 218, 0.8);
+ transform: translateY(-2px);
+}
+
+.control-btn:active {
+ transform: translateY(0);
+}
+
+.control-btn:disabled {
+ background: rgba(79, 84, 92, 0.4);
+ color: rgba(255, 255, 255, 0.4);
+ cursor: not-allowed;
+ transform: none;
+}
+
+.control-btn:disabled:hover {
+ background: rgba(79, 84, 92, 0.4);
+ transform: none;
+}
+
+.btn-icon {
+ width: 24px;
+ height: 24px;
+ margin-bottom: 4px;
+}
+
+.btn-text {
+ font-size: 12px;
+ font-weight: 500;
+}
+
+/* 鎷ㄥ彿鎸夐挳鐗规畩鏍峰紡 */
+.dial-btn.dial-active {
+ background: rgba(237, 66, 69, 0.9);
+}
+
+.dial-btn.dial-active:hover {
+ background: rgba(237, 66, 69, 1);
+}
+
+/* 褰曢煶鎸夐挳鐗规畩鏍峰紡 */
+.control-btn.recording {
+ background: rgba(237, 66, 69, 0.9);
+ animation: pulse 1.5s infinite;
+}
+
+.control-btn.recording:hover {
+ background: rgba(237, 66, 69, 1);
+}
+
+/* 鎽勫儚澶存寜閽壒娈婃牱寮� */
+#cameraBtn.camera-active {
+ background: rgba(237, 66, 69, 0.9);
+}
+
+#cameraBtn.camera-active:hover {
+ background: rgba(237, 66, 69, 1);
+}
+
+/* 褰曢煶鎸夐挳鑴夊啿鍔ㄧ敾 */
+@keyframes pulse {
+ 0% {
+ box-shadow: 0 0 0 0 rgba(237, 66, 69, 0.7);
+ }
+
+ 70% {
+ box-shadow: 0 0 0 10px rgba(237, 66, 69, 0);
+ }
+
+ 100% {
+ box-shadow: 0 0 0 0 rgba(237, 66, 69, 0);
+ }
+}
+
+/* ==================== 寮圭獥鏍峰紡 ==================== */
+.modal {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ background: rgba(0, 0, 0, 0.7);
+ backdrop-filter: blur(5px);
+ z-index: 2000;
+ align-items: center;
+ justify-content: center;
+}
+
+.modal-content {
+ background: #36393f;
+ border-radius: 12px;
+ width: 90%;
+ max-width: 500px;
+ max-height: 80vh;
+ overflow: hidden;
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
+ animation: modalSlideIn 0.3s ease-out;
+}
+
+.settings-modal {
+ max-width: 700px;
+ max-height: 85vh;
+}
+
+@keyframes modalSlideIn {
+ from {
+ opacity: 0;
+ transform: scale(0.9) translateY(-20px);
+ }
+
+ to {
+ opacity: 1;
+ transform: scale(1) translateY(0);
+ }
+}
+
+.modal-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 20px 24px;
+ border-bottom: 1px solid #40444b;
+ background: #2f3136;
+}
+
+.modal-header h2 {
+ color: white;
+ font-size: 20px;
+ font-weight: 600;
+ margin: 0;
+}
+
+.close-btn {
+ background: none;
+ border: none;
+ color: #b9bbbe;
+ font-size: 24px;
+ cursor: pointer;
+ width: 32px;
+ height: 32px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 4px;
+ transition: background-color 0.2s;
+}
+
+.close-btn:hover {
+ background: #40444b;
+ color: white;
+}
+
+.modal-body {
+ padding: 24px;
+ max-height: calc(80vh - 80px);
+ overflow-y: auto;
+}
+
+/* ==================== 杈撳叆缁勬牱寮� ==================== */
+.input-group {
+ margin-bottom: 20px;
+}
+
+.input-group label {
+ display: block;
+ margin-bottom: 8px;
+ color: #b9bbbe;
+ font-size: 14px;
+ font-weight: 500;
+}
+
+.input-group input,
+.input-group textarea {
+ width: 100%;
+ padding: 12px;
+ border: 1px solid #40444b;
+ border-radius: 6px;
+ background: #40444b;
+ color: white;
+ font-size: 14px;
+ transition: border-color 0.2s;
+}
+
+.input-group input:focus,
+.input-group textarea:focus {
+ outline: none;
+ border-color: #5865f2;
+}
+
+.input-group textarea {
+ resize: vertical;
+ min-height: 80px;
+ font-family: inherit;
+}
+
+/* ==================== 鎸夐挳鏍峰紡 ==================== */
+.modal-actions {
+ display: flex;
+ gap: 12px;
+ justify-content: flex-end;
+ margin-top: 24px;
+}
+
+/* ==================== 鏂囦欢鍗忚璀﹀憡鏍峰紡 ==================== */
+#fileProtocolWarning {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: 95%;
+ max-width: 800px;
+ background: linear-gradient(135deg, rgba(76, 175, 80, 0.95), rgba(56, 142, 60, 0.95));
+ border: 3px solid #4caf50;
+ border-radius: 16px;
+ padding: 32px;
+ z-index: 10000;
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
+ backdrop-filter: blur(15px);
+ color: white;
+ font-family: inherit;
+ animation: fadeInUp 0.5s ease-out;
+}
+
+#fileProtocolWarning h2 {
+ color: #e8f5e8;
+ margin-bottom: 20px;
+ font-size: 28px;
+ font-weight: 700;
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
+}
+
+#fileProtocolWarning p {
+ margin-bottom: 16px;
+ line-height: 1.6;
+ font-size: 16px;
+ color: #e8f5e8;
+}
+
+#fileProtocolWarning ol {
+ margin: 16px 0;
+ padding-left: 24px;
+ font-size: 16px;
+}
+
+#fileProtocolWarning pre {
+ background: rgba(255, 255, 255, 0.15);
+ padding: 12px 16px;
+ border-radius: 8px;
+ margin: 12px 0;
+ font-family: 'Courier New', monospace;
+ font-size: 14px;
+ overflow-x: auto;
+ border: 1px solid rgba(255, 255, 255, 0.2);
+}
+
+#fileProtocolWarning strong {
+ color: #c8e6c9;
+ font-weight: 600;
+}
+
+/* 鑳屾櫙妯$硦閬僵 */
+.file-protocol-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ background: rgba(0, 0, 0, 0.6);
+ backdrop-filter: blur(10px);
+ z-index: 9999;
+ animation: fadeIn 0.3s ease-out;
+}
+
+/* 鍔ㄧ敾鏁堟灉 */
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ }
+
+ to {
+ opacity: 1;
+ }
+}
+
+@keyframes fadeInUp {
+ from {
+ opacity: 0;
+ transform: translate(-50%, -40%);
+ }
+
+ to {
+ opacity: 1;
+ transform: translate(-50%, -50%);
+ }
+}
+
+/* ==================== 杩炴帴淇℃伅鏍峰紡 ==================== */
+.connection-info {
+ margin-top: 20px;
+ padding: 16px;
+ background: rgba(88, 101, 242, 0.1);
+ border: 1px solid rgba(88, 101, 242, 0.3);
+ border-radius: 8px;
+ text-align: center;
+}
+
+.connection-info p {
+ color: #b9bbbe;
+ font-size: 14px;
+ line-height: 1.4;
+ margin: 0;
+}
+
+.btn-primary {
+ background: #5865f2;
+ color: white;
+ border: none;
+ padding: 10px 20px;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 14px;
+ font-weight: 500;
+ transition: background-color 0.2s;
+}
+
+.btn-primary:hover {
+ background: #4752c4;
+}
+
+.btn-secondary {
+ background: #4f545c;
+ color: white;
+ border: none;
+ padding: 10px 20px;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 14px;
+ font-weight: 500;
+ transition: background-color 0.2s;
+}
+
+.btn-secondary:hover {
+ background: #5d6269;
+}
+
+/* ==================== 璁剧疆鏍囩椤垫牱寮� ==================== */
+.settings-tabs {
+ display: flex;
+ border-bottom: 1px solid #40444b;
+ margin-bottom: 20px;
+}
+
+.tab-btn {
+ background: none;
+ border: none;
+ color: #b9bbbe;
+ padding: 12px 24px;
+ cursor: pointer;
+ font-size: 14px;
+ font-weight: 500;
+ border-bottom: 2px solid transparent;
+ transition: all 0.2s;
+}
+
+.tab-btn.active {
+ color: white;
+ border-bottom-color: #5865f2;
+}
+
+.tab-btn:hover:not(.active) {
+ color: white;
+ background: #40444b;
+}
+
+.tab-content {
+ display: none;
+}
+
+.tab-content.active {
+ display: block;
+}
+
+/* ==================== 閰嶇疆闈㈡澘鏍峰紡 ==================== */
+.config-panel {
+ background: #2f3136;
+ border-radius: 8px;
+ padding: 20px;
+}
+
+.config-row {
+ display: flex;
+ gap: 16px;
+ margin-bottom: 16px;
+}
+
+.config-item {
+ flex: 1;
+}
+
+.config-item label {
+ display: block;
+ margin-bottom: 6px;
+ color: #b9bbbe;
+ font-size: 12px;
+ font-weight: 500;
+}
+
+.config-item input {
+ width: 100%;
+ padding: 8px 12px;
+ border: 1px solid #40444b;
+ border-radius: 4px;
+ background: #40444b;
+ color: white;
+ font-size: 14px;
+}
+
+.config-item textarea {
+ width: 100%;
+ padding: 12px;
+ border: 1px solid #40444b;
+ border-radius: 6px;
+ background: #40444b;
+ color: white;
+ min-height: 100px;
+ resize: vertical;
+ box-sizing: border-box;
+ font-family: inherit;
+ font-size: 14px;
+}
+
+.model-select {
+ width: 100%;
+ padding: 10px 40px 10px 14px;
+ border: 1px solid #40444b;
+ border-radius: 8px;
+ background: #40444b;
+ color: #ffffff;
+ font-size: 14px;
+ font-weight: 500;
+ cursor: pointer;
+ appearance: none;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='%23ffffff'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E");
+ background-repeat: no-repeat;
+ background-position: right 12px center;
+ background-size: 16px 16px;
+}
+
+.model-select:hover {
+ border-color: #5865f2;
+}
+
+.model-select:focus {
+ outline: none;
+ border-color: #5865f2;
+}
+
+.model-select option {
+ background: #2f3136;
+ color: #ffffff;
+ padding: 12px;
+ font-size: 14px;
+}
+
+.background-btn {
+ width: 100%;
+ padding: 10px 14px;
+ border: 1px solid #40444b;
+ border-radius: 8px;
+ background: #40444b;
+ color: #ffffff;
+ font-size: 14px;
+ font-weight: 500;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ transition: all 0.3s ease;
+}
+
+.background-btn:hover {
+ background: #4f545c;
+}
+
+.background-btn:active {
+ background: #40444b;
+}
+
+.background-btn .btn-icon {
+ width: 20px;
+ height: 20px;
+ fill: #ffffff;
+}
+
+/* ==================== MCP宸ュ叿鏍峰紡 ==================== */
+.mcp-tools-container {
+ background: #2f3136;
+ border-radius: 8px;
+ padding: 0;
+ overflow: hidden;
+}
+
+.mcp-tools-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 16px 20px;
+ background: #36393f;
+ border-bottom: 1px solid #40444b;
+}
+
+.mcp-tools-header h3 {
+ margin: 0;
+ color: #ffffff;
+ font-size: 16px;
+ font-weight: 600;
+}
+
+.mcp-tools-panel {
+ padding: 20px;
+}
+
+.mcp-tools-list {
+ min-height: 200px;
+ max-height: 300px;
+ overflow-y: auto;
+ margin-bottom: 16px;
+}
+
+/* MCP宸ュ叿鍗$墖鏍峰紡 */
+.mcp-tool-card {
+ background: #36393f;
+ border: 1px solid #40444b;
+ border-radius: 8px;
+ padding: 16px;
+ margin-bottom: 12px;
+ transition: background-color 0.2s;
+}
+
+.mcp-tool-card:hover {
+ background: #40444b;
+}
+
+.mcp-tool-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 8px;
+}
+
+.mcp-tool-name {
+ font-weight: 600;
+ color: white;
+ font-size: 14px;
+}
+
+.mcp-tool-actions {
+ display: flex;
+ gap: 8px;
+}
+
+.mcp-edit-btn {
+ padding: 4px 10px;
+ border: none;
+ border-radius: 4px;
+ background-color: #2196f3;
+ color: white;
+ cursor: pointer;
+ font-size: 12px;
+ transition: background-color 0.2s;
+}
+
+.mcp-edit-btn:hover {
+ background-color: #1976d2;
+}
+
+.mcp-delete-btn {
+ padding: 4px 10px;
+ border: none;
+ border-radius: 4px;
+ background-color: #f44336;
+ color: white;
+ cursor: pointer;
+ font-size: 12px;
+ transition: background-color 0.2s;
+}
+
+.mcp-delete-btn:hover {
+ background-color: #d32f2f;
+}
+
+.mcp-tool-description {
+ color: #b9bbbe;
+ font-size: 13px;
+ line-height: 1.4;
+ margin-bottom: 12px;
+}
+
+.mcp-tool-info {
+ background: #2f3136;
+ border-radius: 4px;
+ padding: 8px;
+}
+
+.mcp-tool-info-row {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 4px;
+}
+
+.mcp-tool-info-label {
+ color: #b9bbbe;
+ font-size: 12px;
+}
+
+.mcp-tool-info-value {
+ color: white;
+ font-size: 12px;
+ font-weight: 500;
+}
+
+.mcp-actions {
+ text-align: center;
+}
+
+.properties-container {
+ background: #2f3136;
+ border: 1px solid #40444b;
+ border-radius: 6px;
+ padding: 16px;
+ margin-bottom: 12px;
+}
+
+.properties-container #addMcpPropertyBtn {
+ display: block;
+ margin: 0 auto;
+}
+
+.properties-container #addMcpPropertyBtn:hover {
+ background: #4752c4;
+}
+
+.mcp-checkbox-label {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-top: 8px;
+ color: #b9bbbe;
+ font-size: 13px;
+ cursor: pointer;
+ justify-content: flex-start;
+}
+
+.mcp-checkbox-label input[type="checkbox"] {
+ width: 16px;
+ height: 16px;
+ cursor: pointer;
+}
+
+.mcp-property-name {
+ color: white;
+ font-size: 14px;
+ font-weight: 500;
+}
+
+.mcp-property-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 8px;
+}
+
+.mcp-properties-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px;
+ min-height: 60px;
+ padding: 8px;
+}
+
+.input-group-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 8px;
+}
+
+.input-group-header label {
+ margin-bottom: 0;
+}
+
+.input-group-header .properties-btn-primary {
+ margin: 0;
+}
+
+.mcp-empty-state {
+ text-align: center;
+ padding: 20px;
+ color: #999;
+ font-size: 14px;
+ width: 100%;
+ display: none;
+}
+
+.properties-btn-primary {
+ background: #2196f3;
+ color: white;
+ border: none;
+ padding: 6px 12px;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 12px;
+ font-weight: 500;
+ transition: background-color 0.2s;
+ display: block;
+ margin: 0 auto;
+}
+
+.properties-btn-primary:hover {
+ background: #2196f3;
+}
+
+.mcp-property-card {
+ background: #36393f;
+ border: 1px solid #40444b;
+ border-radius: 8px;
+ padding: 12px;
+ width: 100%;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.mcp-property-card:hover {
+ background: #40444b;
+ border-color: #5865f2;
+}
+
+.mcp-property-card:active {
+ transform: none;
+}
+
+.mcp-property-row-label {
+ display: flex;
+ align-items: flex-start;
+ margin-bottom: 6px;
+ gap: 8px;
+}
+
+.mcp-property-label {
+ color: #b9bbbe;
+ font-size: 13px;
+ width: 60px;
+ flex-shrink: 0;
+ text-align: left;
+}
+
+.mcp-property-value {
+ color: white;
+ font-size: 13px;
+ flex: 1;
+ word-break: break-all;
+}
+
+.mcp-property-required-badge {
+ color: #f44336;
+ font-size: 12px;
+}
+
+.mcp-property-row-action {
+ display: flex;
+ justify-content: flex-end;
+ margin-top: 6px;
+ padding-top: 6px;
+ border-top: 1px solid #40444b;
+}
+
+.mcp-property-delete-btn {
+ background: #f44336;
+ color: white;
+ border: none;
+ padding: 4px 12px;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 11px;
+ transition: background-color 0.2s;
+}
+
+.mcp-property-delete-btn:hover {
+ background: #d32f2f;
+}
+
+.mcp-property-card-optional {
+ background: #4f545c;
+ color: #b9bbbe;
+ font-size: 11px;
+ padding: 2px 8px;
+ border-radius: 4px;
+}
+
+.property-modal {
+ max-width: 450px;
+}
+
+/* ==================== 闊抽鍙鍖栧櫒鏍峰紡 ==================== */
+.audio-visualizer {
+ position: fixed;
+ bottom: 100px;
+ left: 20px;
+ width: 200px;
+ height: 60px;
+ background: rgba(0, 0, 0, 0.3);
+ backdrop-filter: blur(10px);
+ border-radius: 10px;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ z-index: 999;
+}
+
+/* 婊氬姩鏉℃牱寮� */
+.chat-stream::-webkit-scrollbar,
+.modal-body::-webkit-scrollbar,
+.mcp-tools-list::-webkit-scrollbar {
+ width: 6px;
+}
+
+.chat-stream::-webkit-scrollbar-track,
+.modal-body::-webkit-scrollbar-track,
+.mcp-tools-list::-webkit-scrollbar-track {
+ background: #2f3136;
+ border-radius: 3px;
+}
+
+.chat-stream::-webkit-scrollbar-thumb,
+.modal-body::-webkit-scrollbar-thumb,
+.mcp-tools-list::-webkit-scrollbar-thumb {
+ background: #40444b;
+ border-radius: 3px;
+}
+
+.chat-stream::-webkit-scrollbar-thumb:hover,
+.modal-body::-webkit-scrollbar-thumb:hover,
+.mcp-tools-list::-webkit-scrollbar-thumb:hover {
+ background: #4f545c;
+}
+
+/* ==================== 鎽勫儚澶存樉绀哄尯鍩熸牱寮� ==================== */
+.camera-container {
+ position: fixed;
+ top: 20px;
+ left: 20px;
+ width: 180px;
+ height: 240px;
+ background: rgba(0, 0, 0, 0.5);
+ backdrop-filter: blur(10px);
+ border-radius: 8px;
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ z-index: 1000;
+ display: none;
+ overflow: hidden;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
+ cursor: move;
+}
+
+.camera-container.active {
+ display: block;
+}
+
+.camera-container.dragging {
+ cursor: grabbing;
+ box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
+ border-color: rgba(88, 101, 242, 0.5);
+}
+
+@keyframes flipWithPosition {
+ 0% {
+ transform: var(--original-transform) rotateY(0deg);
+ }
+
+ 100% {
+ transform: var(--original-transform) rotateY(180deg);
+ }
+}
+
+
+.camera-container.flip {
+ animation: flipWithPosition 0.5s ease-in-out 1 forwards;
+}
+
+#cameraVideo {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ background: #1a1a1a;
+ pointer-events: none;
+ transform: scaleX(-1);
+}
+
+.camera-switch-mask {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+}
+
+.camera-switch-container {
+ position: absolute;
+ bottom: 10px;
+ right: 10px;
+}
+
+.camera-switch {
+ display: none;
+ height: 20px;
+ color: #fff;
+}
+
+.camera-switch .btn-icon {
+ margin-bottom: 0;
+}
+
+.camera-switch.active {
+ display: block;
+}
+
+/* ==================== 鍝嶅簲寮忚璁� ==================== */
+@media (max-width: 768px) {
+ .container {
+ padding: 10px;
+ }
+
+ .live2d-section {
+ height: 60vh;
+ margin-bottom: 10px;
+ }
+
+ .live2d-canvas {
+ max-width: 300px;
+ max-height: 450px;
+ }
+
+ .control-bar {
+ bottom: 10px;
+ padding: 8px 16px;
+ gap: 12px;
+ }
+
+ .control-btn {
+ width: 50px;
+ height: 50px;
+ }
+
+ .btn-icon {
+ width: 20px;
+ height: 20px;
+ }
+
+ .btn-text {
+ font-size: 10px;
+ }
+
+ .chat-container {
+ width: 230px;
+ right: 10px;
+ bottom: 105px;
+ }
+
+ .message-bubble {
+ max-width: 200px;
+ font-size: 12px;
+ }
+
+ .modal-content {
+ width: 95%;
+ margin: 10px;
+ }
+
+ .config-row {
+ flex-direction: column;
+ gap: 12px;
+ }
+
+ .audio-visualizer {
+ width: 150px;
+ height: 50px;
+ left: 10px;
+ bottom: 80px;
+ }
+
+ .chat-ipt input {
+ font-size: 12px;
+ }
+
+ .camera-container {
+ width: 130px;
+ height: 180px;
+ }
+
+ .status-indicator {
+ font-size: 12px;
+ }
+}
+
+@media (max-width: 480px) {
+ .live2d-section {
+ height: 50vh;
+ }
+
+ .live2d-canvas {
+ max-width: 250px;
+ max-height: 350px;
+ }
+
+ .control-bar {
+ gap: 8px;
+ padding: 6px 12px;
+ }
+
+ .control-btn {
+ width: 45px;
+ height: 45px;
+ }
+
+ .btn-text {
+ display: none;
+ }
+
+ .chat-stream {
+ width: 200px;
+ max-height: 150px;
+ }
+
+ .audio-visualizer {
+ width: 120px;
+ height: 40px;
+ left: 5px;
+ bottom: 70px;
+ }
+}
+
+/* 绔栧睆浼樺寲 */
+@media (max-height: 700px) {
+ .live2d-section {
+ height: 55vh;
+ }
+
+ .live2d-canvas {
+ max-height: 400px;
+ }
+
+ .chat-stream {
+ bottom: 70px;
+ max-height: 150px;
+ }
+
+ .audio-visualizer {
+ bottom: 70px;
+ }
+}
+
+/* 妯睆浼樺寲 */
+@media (min-width: 1024px) and (min-height: 600px) {
+ .live2d-section {
+ height: 75vh;
+ }
+
+ .live2d-canvas {
+ max-width: 500px;
+ max-height: 650px;
+ }
+
+ .chat-stream {
+ width: 350px;
+ max-height: 250px;
+ }
+
+ .audio-visualizer {
+ width: 250px;
+ height: 80px;
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/favicon.ico b/xiaozhi-esp32-server/main/digital-human/favicon.ico
new file mode 100644
index 0000000..5f285a5
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/favicon.ico
Binary files differ
diff --git a/xiaozhi-esp32-server/main/digital-human/images/1.png b/xiaozhi-esp32-server/main/digital-human/images/1.png
new file mode 100644
index 0000000..62d848a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/images/1.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/digital-human/images/2.png b/xiaozhi-esp32-server/main/digital-human/images/2.png
new file mode 100644
index 0000000..ec64884
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/images/2.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/digital-human/images/3.png b/xiaozhi-esp32-server/main/digital-human/images/3.png
new file mode 100644
index 0000000..545cc8a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/images/3.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/digital-human/index.html b/xiaozhi-esp32-server/main/digital-human/index.html
new file mode 100644
index 0000000..cfb7263
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/index.html
@@ -0,0 +1,416 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>灏忔櫤鏁板瓧浜洪〉闈�</title>
+ <link rel="stylesheet" href="css/index.css?v=0206">
+ <script>
+ // 妫�娴嬫槸鍚︿娇鐢╢ile://鍗忚鎵撳紑
+ if (window.location.protocol === 'file:') {
+ document.addEventListener('DOMContentLoaded', function () {
+ // 鍒涘缓鑳屾櫙妯$硦閬僵
+ const overlayDiv = document.createElement('div');
+ overlayDiv.className = 'file-protocol-overlay';
+ document.body.appendChild(overlayDiv);
+
+ // 鍒涘缓璀﹀憡妗�
+ const warningDiv = document.createElement('div');
+ warningDiv.id = 'fileProtocolWarning';
+ warningDiv.innerHTML = `
+ <h2>鈿狅笍 璀﹀憡锛氳浣跨敤HTTP鏈嶅姟鍣ㄦ墦寮�姝ら〉闈�</h2>
+ <p>鎮ㄥ綋鍓嶄娇鐢ㄧ殑鏄湰鍦版枃浠舵柟寮忔墦寮�椤甸潰锛坒ile://鍗忚锛夛紝杩欏彲鑳藉鑷撮〉闈㈠姛鑳藉紓甯搞��</p>
+ <p>鎮ㄥ彲浠ヤ娇鐢╪ginx鏄犲皠鍚姩灏忔櫤鏁板瓧浜洪〉闈紝涔熷彲浠ヨ鎸夌収浠ヤ笅姝ラ鍚姩娴嬭瘯杩愯鏃讹細</p>
+ <ol>
+ <li>鎵撳紑鍛戒护琛岀粓绔�</li>
+ <li>鍛戒护琛岃繘鍏ュ埌 digital-human 鐩綍</li>
+ <li>鎵ц浠ヤ笅鍛戒护鍚姩灏忔櫤鏁板瓧浜洪〉闈㈣繍琛屾椂锛�</li>
+ </ol>
+ <pre>python start.py</pre>
+ <p>鐒跺悗鍦ㄦ祻瑙堝櫒涓闂細<strong>http://localhost:8006/index.html</strong></p>
+ `;
+ document.body.appendChild(warningDiv);
+ });
+ }
+ </script>
+</head>
+
+<body>
+ <!-- 鑳屾櫙瀹瑰櫒 -->
+ <div class="background-container" id="backgroundContainer">
+ <div class="background-overlay"></div>
+ </div>
+
+ <!-- 涓诲鍣� -->
+ <div class="container">
+
+ <!-- 鎽勫儚澶存樉绀哄尯鍩燂紙鍙嫋鍔級 -->
+ <div class="camera-container" id="cameraContainer">
+ <video id="cameraVideo" autoplay playsinline muted></video>
+ <div class="camera-switch-mask" id="cameraSwitchMask">
+ <div class="camera-switch-container">
+ <!-- 鎽勫儚澶村垏鎹㈡寜閽� -->
+ <div class="camera-switch" id="cameraSwitch">
+ <svg class="btn-icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7319" width="32" height="32"><path d="M722.1 530.3c0-44.9-13.8-86.5-37.2-120.9-0.3-0.6-0.6-1.2-0.9-1.7-2.9-4.2-6.1-8.1-9.2-12-0.4-0.5-0.7-1-1.1-1.5-21.4-26.3-48.3-46.7-78.7-59.9-0.8-0.4-1.7-0.8-2.5-1.2-4.9-2-9.8-3.8-14.9-5.5-1.8-0.6-3.6-1.3-5.4-1.8-4.4-1.3-8.8-2.4-13.3-3.5-2.5-0.6-4.9-1.2-7.4-1.7-1.2-0.2-2.4-0.6-3.6-0.8-3.3-0.6-6.7-0.8-10-1.3-2.3-0.3-4.6-0.7-6.9-0.9-5.6-0.5-11.2-0.8-16.7-0.9-1 0-2-0.1-3-0.1-0.2 0-0.3 0.1-0.5 0.1-42.8 0-84.6 13.1-120.4 38.6-11.5 8.1-14.2 24.1-6.2 35.7 8 11.6 23.8 14.4 35.2 6.3 27.7-19.7 60-29.6 93.1-29.3 4.8 0 9.5 0.3 14.1 0.7 1.4 0.2 2.8 0.4 4.3 0.5 3.8 0.5 7.6 1 11.3 1.8 1.6 0.3 3.3 0.8 4.9 1.1 3.7 0.8 7.3 1.7 10.9 2.8 1.1 0.4 2.2 0.8 3.4 1.2 4.1 1.3 8.1 2.8 12 4.5 0.4 0.2 0.8 0.4 1.2 0.5 23.6 10.3 44.3 26.1 60.4 45.9 0.1 0.1 0.2 0.3 0.3 0.4 22.7 28 36.4 63.9 36.4 102.9h-42.2L697 632.9l67.5-102.6h-42.4z m-119 133c-27.9 19.8-60.5 29.7-93.9 29.3-4.4-0.1-8.8-0.3-13.1-0.7-1.8-0.2-3.5-0.5-5.3-0.7-3.4-0.5-6.8-0.9-10.1-1.6-2-0.4-4-0.9-6.1-1.4-3.3-0.8-6.5-1.5-9.7-2.5-1.5-0.5-3-1-4.6-1.5-3.7-1.2-7.3-2.5-10.8-4-0.8-0.3-1.6-0.7-2.4-1.1-4.1-1.9-8.2-3.8-12.2-6l-0.6-0.3c-13.4-7.5-25.7-16.8-36.4-27.7-0.2-0.2-0.3-0.4-0.5-0.6-3.3-3.4-6.5-6.9-9.5-10.7-0.6-0.8-1.3-1.6-1.9-2.5-21.9-27.8-35.1-63-35.1-101.2h42.2l-67.5-102.6-67.5 102.6h42.2c0 45 13.9 86.7 37.3 121.1 0.3 0.5 0.5 1 0.8 1.5 2.4 3.5 5.1 6.8 7.7 10.1 1 1.3 1.9 2.6 2.9 3.8 3.9 4.7 7.9 9.2 12.1 13.5 0.4 0.4 0.8 0.9 1.2 1.3 14.1 14.3 30.1 26.4 47.4 36 0.5 0.3 0.9 0.6 1.4 0.8 5 2.7 10.1 5.2 15.3 7.5l3.9 1.8c4.4 1.9 9 3.5 13.6 5.1 2.2 0.7 4.4 1.5 6.6 2.2 4 1.2 8.1 2.2 12.3 3.2 2.8 0.7 5.5 1.4 8.3 1.9 1.2 0.3 2.3 0.6 3.4 0.8 3.9 0.7 7.8 1.1 11.8 1.6l4.2 0.6c7 0.7 14.1 1.2 21.1 1.2 42.9 0 84.7-13.3 120.6-38.8 11.4-8.2 14.2-24.1 6.2-35.8-8-11.5-23.8-14.3-35.3-6.2z m271.3-421.6H763c-26.5 0-82.9-119.7-112.5-119.7H370.6c-29.6 0-85.9 119.7-112.4 119.7H147.5c-46.3 0-83.9 40.2-83.9 89.7v478.8c0 49.6 37.6 89.8 83.9 89.8h726.9c46.3 0 83.9-40.1 83.9-89.8V331.4c0-49.5-37.6-89.7-83.9-89.7z m28 538.6c0 33.1-25 59.8-55.9 59.8h-671c-30.9 0-55.9-26.8-55.9-59.8V361.4c0-33 25-59.8 55.9-59.8h111.2c25.8 0 82-119.7 111.6-119.7h223.2c29.5 0 85.6 119.7 111.5 119.7h113.5c30.9 0 55.9 26.8 55.9 59.8v418.9z" p-id="7320" fill="#ffffff"></path></svg>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- 杩炴帴鐘舵�佹寚绀哄櫒 - 椤甸潰椤堕儴涓儴 -->
+ <div class="connection-status-top">
+ <div class="status-indicator">
+ <span class="status-dot status-disconnected"></span>
+ <span id="connectionStatus">绂荤嚎</span>
+ </div>
+ </div>
+
+ <!-- 妯″瀷鍔犺浇鎻愮ず -->
+ <div class="model-container">
+ <div class="model-loading" id="modelLoading">
+ <div class="loading-char-float">
+ <div class="main-char">
+ <span>妯�</span><span>鍨�</span><span>鍔�</span><span>杞�</span><span>涓�</span><span>鉁�</span>
+ </div>
+ <div class="shadow-char">妯″瀷鍔犺浇涓湪</div>
+ </div>
+ </div>
+ </div>
+
+ <!-- Live2D Canvas 瀹瑰櫒 -->
+ <canvas id="live2d-stage"></canvas>
+
+ <!-- 鑱婂ぉ娑堟伅娴侊紙寮瑰箷鏍峰紡锛� -->
+ <div class="chat-container">
+ <div class="chat-stream" id="chatStream">
+ <!-- 鑱婂ぉ娑堟伅灏嗗姩鎬佹彃鍏ヨ繖閲� -->
+ </div>
+ <div class="chat-ipt" id="chatIpt">
+ <input type="text" id="messageInput" autocomplete="off" placeholder="杈撳叆娑堟伅锛屾寜Enter鍙戦��">
+ </div>
+ </div>
+
+ <!-- 搴曢儴鎺у埗鏍� -->
+ <div class="control-bar">
+ <button class="control-btn" id="settingsBtn" title="璁剧疆">
+ <svg class="btn-icon" viewBox="0 0 24 24" fill="currentColor">
+ <path
+ d="M12 15.5A3.5 3.5 0 0 1 8.5 12A3.5 3.5 0 0 1 12 8.5A3.5 3.5 0 0 1 15.5 12A3.5 3.5 0 0 1 12 15.5M19.43 12.97C19.47 12.65 19.5 12.33 19.5 12C19.5 11.67 19.47 11.34 19.43 11L21.54 9.37C21.73 9.22 21.78 8.95 21.66 8.73L19.66 5.27C19.54 5.05 19.27 4.96 19.05 5.05L16.56 6.05C16.04 5.66 15.5 5.32 14.87 5.07L14.5 2.42C14.46 2.18 14.25 2 14 2H10C9.75 2 9.54 2.18 9.5 2.42L9.13 5.07C8.5 5.32 7.96 5.66 7.44 6.05L4.95 5.05C4.73 4.96 4.46 5.05 4.34 5.27L2.34 8.73C2.22 8.95 2.27 9.22 2.46 9.37L4.57 11C4.53 11.34 4.5 11.67 4.5 12C4.5 12.33 4.53 12.65 4.57 12.97L2.46 14.63C2.27 14.78 2.22 15.05 2.34 15.27L4.34 18.73C4.46 18.95 4.73 19.03 4.95 18.95L7.44 17.94C7.96 18.34 8.5 18.68 9.13 18.93L9.5 21.58C9.54 21.82 9.75 22 10 22H14C14.25 22 14.46 21.82 14.5 21.58L14.87 18.93C15.5 18.68 16.04 18.34 16.56 17.94L19.05 18.95C19.27 19.03 19.54 18.95 19.66 18.73L21.66 15.27C21.78 15.05 21.73 14.78 21.54 14.63L19.43 12.97Z" />
+ </svg>
+ <span class="btn-text">璁剧疆</span>
+ </button>
+
+ <button class="control-btn" id="cameraBtn" title="璇峰厛杩炴帴鏈嶅姟鍣�" disabled>
+ <svg class="btn-icon" viewBox="0 0 24 24" fill="currentColor">
+ <path
+ d="M17,10.5V7A1,1 0 0,0 16,6H4A1,1 0 0,0 3,7V17A1,1 0 0,0 4,18H16A1,1 0 0,0 17,17V13.5L21,17.5V6.5L17,10.5Z" />
+ </svg>
+ <span class="btn-text">鎽勫儚澶�</span>
+ </button>
+
+ <button class="control-btn dial-btn" id="dialBtn" title="鎷ㄥ彿">
+ <svg class="btn-icon" viewBox="0 0 24 24" fill="currentColor">
+ <path
+ d="M6.62,10.79C8.06,13.62 10.38,15.94 13.21,17.38L15.41,15.18C15.69,14.9 16.08,14.82 16.43,14.93C17.55,15.3 18.75,15.5 20,15.5A1,1 0 0,1 21,16.5V20A1,1 0 0,1 20,21A17,17 0 0,1 3,4A1,1 0 0,1 4,3H7.5A1,1 0 0,1 8.5,4C8.5,5.25 8.7,6.45 9.07,7.57C9.18,7.92 9.1,8.31 8.82,8.59L6.62,10.79Z" />
+ </svg>
+ <span class="btn-text">鎷ㄥ彿</span>
+ </button>
+
+ <button class="control-btn" id="recordBtn" title="寮�濮嬪綍闊�" disabled>
+ <svg class="btn-icon" viewBox="0 0 24 24" fill="currentColor">
+ <path
+ d="M12,2A3,3 0 0,1 15,5V11A3,3 0 0,1 12,14A3,3 0 0,1 9,11V5A3,3 0 0,1 12,2M19,11C19,14.53 16.39,17.44 13,17.93V21H11V17.93C7.61,17.44 5,14.53 5,11H7A5,5 0 0,0 12,16A5,5 0 0,0 17,11H19Z" />
+ </svg>
+ <span class="btn-text">褰曢煶</span>
+ </button>
+ </div>
+ </div>
+
+ <!-- 璁剧疆寮圭獥 -->
+ <div class="modal" id="settingsModal">
+ <div class="modal-content settings-modal">
+ <div class="modal-header">
+ <h2>璁剧疆</h2>
+ <button class="close-btn" id="closeSettingsBtn">×</button>
+ </div>
+ <div class="modal-body">
+ <div class="settings-tabs">
+ <button class="tab-btn active" data-tab="device">璁惧閰嶇疆</button>
+ <button class="tab-btn" data-tab="wakeword">鍞ら啋璇�</button>
+ <button class="tab-btn" data-tab="mcp">MCP宸ュ叿</button>
+ <button class="tab-btn" data-tab="other">鏁板瓧浜虹毊鑲�</button>
+ </div>
+
+ <div class="tab-content active" id="deviceTab">
+ <!-- 璁惧閰嶇疆闈㈡澘 -->
+ <div class="config-panel">
+ <div class="control-panel">
+ <div class="config-row">
+ <div class="config-item">
+ <label for="deviceMac">璁惧MAC:</label>
+ <input type="text" id="deviceMac" placeholder="device-id">
+ </div>
+ </div>
+ <div class="config-row">
+ <div class="config-item">
+ <label for="clientId">瀹㈡埛绔疘D:</label>
+ <input type="text" id="clientId" value="web_test_client" placeholder="client-id">
+ </div>
+ <div class="config-item">
+ <label for="deviceName">璁惧鍚嶇О:</label>
+ <input type="text" id="deviceName" value="Web娴嬭瘯璁惧" maxlength="50"
+ placeholder="deviceName">
+ </div>
+ </div>
+ <div class="config-row">
+ <div class="config-item">
+ <label for="emojiEnabled">鎯呯华琛ㄦ儏:</label>
+ <select id="emojiEnabled" class="model-select">
+ <option value="true">鍚敤</option>
+ <option value="false" selected>绂佺敤</option>
+ </select>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- 杩炴帴淇℃伅闈㈡澘 -->
+ <div class="connection-controls">
+ <div class="input-group">
+ <label for="otaUrl">OTA鏈嶅姟鍣ㄥ湴鍧�:</label>
+ <input type="text" id="otaUrl" value="http://127.0.0.1:8002/xiaozhi/ota/"
+ placeholder="OTA鏈嶅姟鍣ㄥ湴鍧�锛屽锛歨ttp://127.0.0.1:8002/xiaozhi/ota/" />
+ </div>
+ <div class="input-group">
+ <label for="serverUrl">WebSocket鏈嶅姟鍣ㄥ湴鍧�:</label>
+ <input type="text" id="serverUrl" value="" readonly disabled
+ placeholder="濉啓OTA鍦板潃鍚庯紝鐐瑰嚮鎷ㄥ彿鎸夐挳鑷姩杩炴帴" />
+ </div>
+ <div class="input-group">
+ <label for="visionUrl">瑙嗚鍒嗘瀽鍦板潃:</label>
+ <input type="text" id="visionUrl" value="" readonly disabled placeholder="鎴愬姛寤虹珛ws杩炴帴鍚庤嚜鍔ㄨ幏鍙�" />
+ </div>
+ </div>
+ </div>
+
+ <div class="tab-content" id="wakewordTab">
+ <div class="config-panel">
+ <div class="control-panel">
+ <div class="config-row">
+ <div class="config-item">
+ <label for="wakewordEnabled">鍚敤鏈湴鍞ら啋璇�:</label>
+ <select id="wakewordEnabled" class="model-select">
+ <option value="true">鍚敤</option>
+ <option value="false">绂佺敤</option>
+ </select>
+ </div>
+ </div>
+ <div class="config-row">
+ <div class="config-item">
+ <label for="wakewordWsUrl">鍞ら啋璇嶆湇鍔″湴鍧�:</label>
+ <input type="text" id="wakewordWsUrl" placeholder="ws://127.0.0.1:8006/wakeword-ws">
+ </div>
+ </div>
+ <div class="config-row">
+ <div class="config-item">
+ <label for="wakewordList">鍞ら啋璇嶉厤缃� (涓�琛屼竴涓�):</label>
+ <textarea id="wakewordList" placeholder="渚嬪锛� 灏忔櫤灏忔櫤 浣犲ソ灏忔櫤"></textarea>
+ </div>
+ </div>
+ <div class="modal-actions">
+ <button type="button" class="btn-primary" id="applyWakewordBtn">搴旂敤鍞ら啋璇�</button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="tab-content" id="mcpTab">
+ <!-- MCP 宸ュ叿绠$悊鍖哄煙 -->
+ <div class="mcp-tools-container">
+ <div class="mcp-tools-header">
+ <h3>MCP 宸ュ叿绠$悊</h3>
+ </div>
+ <div class="mcp-tools-panel" id="mcpToolsPanel">
+ <div class="mcp-tools-list" id="mcpToolsContainer">
+ <!-- 宸ュ叿鍒楄〃灏嗗姩鎬佹彃鍏ヨ繖閲� -->
+ </div>
+ <div class="mcp-actions">
+ <button class="btn-primary" id="addMcpToolBtn">
+ 鉃� 娣诲姞鏂板伐鍏�
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="tab-content" id="otherTab">
+ <div class="other-settings-panel">
+ <div class="control-panel">
+ <div class="config-row">
+ <div class="config-item">
+ <label>閫夋嫨妯″瀷:</label>
+ <select id="live2dModelSelect" class="model-select">
+ <option value="hiyori_pro_zh">hiyori (鏄ユ棩)</option>
+ <option value="natori_pro_zh">natori (鍚嶅彇)</option>
+ </select>
+ </div>
+ </div>
+ <div class="config-row">
+ <div class="config-item">
+ <label></label>
+ <button class="background-btn" id="backgroundBtn" title="鍒囨崲鑳屾櫙">
+ <svg class="btn-icon" viewBox="0 0 24 24" fill="currentColor">
+ <path
+ d="M12,18A6,6 0 0,1 6,12C6,11 6.25,10.03 6.7,9.2L5.24,7.74C4.46,8.97 4,10.43 4,12A8,8 0 0,0 12,20V23L16,19L12,15V18M12,4V1L8,5L12,9V6A6,6 0 0,1 18,12C18,13 17.75,13.97 17.3,14.8L18.76,16.26C19.54,15.03 20,13.57 20,12A8,8 0 0,0 12,4Z" />
+ </svg>
+ <span class="btn-text">鍒囨崲鑳屾櫙</span>
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- MCP 宸ュ叿缂栬緫妯℃�佹 -->
+ <div id="mcpToolModal" class="modal">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h2 id="mcpModalTitle">娣诲姞宸ュ叿</h2>
+ <button class="close-btn" id="closeMcpModalBtn">×</button>
+ </div>
+ <div class="modal-body">
+ <div id="mcpErrorContainer"></div>
+ <form id="mcpToolForm">
+ <div class="input-group">
+ <label for="mcpToolName">宸ュ叿鍚嶇О *</label>
+ <input type="text" id="mcpToolName" placeholder="渚嬪: self.get_device_status" required>
+ </div>
+ <div class="input-group">
+ <label for="mcpToolDescription">宸ュ叿鎻忚堪 *</label>
+ <textarea id="mcpToolDescription" placeholder="璇︾粏鎻忚堪宸ュ叿鐨勫姛鑳藉拰浣跨敤鍦烘櫙..." required></textarea>
+ </div>
+ <div class="input-group">
+ <div class="input-group-header">
+ <label>杈撳叆鍙傛暟</label>
+ <button type="button" class="properties-btn-primary" id="addMcpPropertyBtn">
+ 鉃� 娣诲姞鍙傛暟
+ </button>
+ </div>
+ <div class="properties-container">
+ <div class="mcp-empty-state" id="mcpEmptyState">
+ 鏆傛棤鍙傛暟锛岀偣鍑讳笂鏂规寜閽坊鍔犲弬鏁�
+ </div>
+ <div class="mcp-properties-list" id="mcpPropertiesContainer">
+ </div>
+ </div>
+ </div>
+ <div class="input-group">
+ <label for="mcpMockResponse">
+ 妯℃嫙杩斿洖缁撴灉 (JSON 鏍煎紡锛屽彲閫�)
+ <span style="font-size: 12px; color: #999; font-weight: normal;">- 鐣欑┖鍒欒繑鍥為粯璁ゆ垚鍔熸秷鎭�</span>
+ </label>
+ <textarea id="mcpMockResponse" placeholder='{"success": true, "data": "鎵ц鎴愬姛"}'></textarea>
+ </div>
+ <div class="modal-actions">
+ <button type="button" class="btn-secondary" id="cancelMcpBtn">鍙栨秷</button>
+ <button type="submit" class="btn-primary">淇濆瓨</button>
+ </div>
+ </form>
+ </div>
+ </div>
+ </div>
+
+ <!-- 鍙傛暟缂栬緫妯℃�佹 -->
+ <div id="mcpPropertyModal" class="modal">
+ <div class="modal-content property-modal">
+ <div class="modal-header">
+ <h2 id="mcpPropertyModalTitle">缂栬緫鍙傛暟</h2>
+ <button class="close-btn" id="closeMcpPropertyModalBtn">×</button>
+ </div>
+ <div class="modal-body">
+ <form id="mcpPropertyForm">
+ <input type="hidden" id="mcpPropertyIndex" value="-1">
+ <div class="input-group">
+ <label for="mcpPropertyName">鍙傛暟鍚嶇О *</label>
+ <input type="text" id="mcpPropertyName" placeholder="渚嬪: param_1" required>
+ </div>
+ <div class="input-group">
+ <label for="mcpPropertyType">鏁版嵁绫诲瀷 *</label>
+ <select id="mcpPropertyType" class="model-select" required>
+ <option value="string">瀛楃涓�</option>
+ <option value="integer">鏁存暟</option>
+ <option value="number">鏁板瓧</option>
+ <option value="boolean">甯冨皵鍊�</option>
+ <option value="array">鏁扮粍</option>
+ <option value="object">瀵硅薄</option>
+ </select>
+ </div>
+ <div class="input-group" id="mcpPropertyRangeGroup" style="display: none;">
+ <div class="config-row">
+ <div class="config-item">
+ <label for="mcpPropertyMinimum">鏈�灏忓��</label>
+ <input type="number" id="mcpPropertyMinimum" placeholder="鍙��">
+ </div>
+ <div class="config-item">
+ <label for="mcpPropertyMaximum">鏈�澶у��</label>
+ <input type="number" id="mcpPropertyMaximum" placeholder="鍙��">
+ </div>
+ </div>
+ </div>
+ <div class="input-group">
+ <label for="mcpPropertyDescription">鍙傛暟鎻忚堪</label>
+ <input type="text" id="mcpPropertyDescription" placeholder="鍙��">
+ </div>
+ <div class="input-group">
+ <label class="mcp-checkbox-label">
+ <input type="checkbox" id="mcpPropertyRequired">
+ 蹇呭~鍙傛暟
+ </label>
+ </div>
+ <div class="modal-actions">
+ <button type="button" class="btn-secondary" id="cancelMcpPropertyBtn">鍙栨秷</button>
+ <button type="submit" class="btn-primary">淇濆瓨</button>
+ </div>
+ </form>
+ </div>
+ </div>
+ </div>
+
+ <!-- 鑳屾櫙鍔犺浇 -->
+ <script src="js/ui/background-load.js?v=0205"></script>
+
+ <!-- PIXI.js 2D娓叉煋寮曟搸 -->
+ <script src="js/live2d/pixi.js?v=0205"></script>
+
+ <!-- Live2D Cubism 4.0 SDK -->
+ <script src="js/live2d/live2dcubismcore.min.js?v=0205"></script>
+ <script src="js/live2d/cubism4.min.js?v=0205"></script>
+
+ <!-- Live2D 绠$悊鍣� -->
+ <script src="js/live2d/live2d.js?v=0205"></script>
+
+ <!-- Opus瑙g爜搴� -->
+ <script src="js/utils/libopus.js?v=0205"></script>
+
+ <!-- 涓诲簲鐢ㄥ叆鍙� -->
+ <script type="module" src="js/app.js?v=0205"></script>
+
+ <!-- 鍏ㄥ眬閿欒澶勭悊 -->
+ <script>
+ // 娣诲姞鍏ㄥ眬閿欒澶勭悊
+ window.addEventListener('error', function (event) {
+ console.error('鍏ㄥ眬閿欒:', event.error);
+ });
+
+ // 娣诲姞鏈鐞嗙殑Promise鎷掔粷澶勭悊
+ window.addEventListener('unhandledrejection', function (event) {
+ console.error('鏈鐞嗙殑Promise鎷掔粷:', event.reason);
+ });
+ </script>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/js/app.js b/xiaozhi-esp32-server/main/digital-human/js/app.js
new file mode 100644
index 0000000..09ab350
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/js/app.js
@@ -0,0 +1,373 @@
+// 涓诲簲鐢ㄥ叆鍙�
+import { checkOpusLoaded, initOpusEncoder } from './core/audio/opus-codec.js?v=0205';
+import { getAudioPlayer } from './core/audio/player.js?v=0205';
+import { checkMicrophoneAvailability, isHttpNonLocalhost } from './core/audio/recorder.js?v=0205';
+import { initMcpTools } from './core/mcp/tools.js?v=0205';
+import { startWakewordBridgeListener } from './core/network/wakeword-bridge.js?v=0205';
+import { uiController } from './ui/controller.js?v=0205';
+import { log } from './utils/logger.js?v=0205';
+
+// 杈呭姪鍑芥暟锛氬皢Base64鏁版嵁杞崲涓築lob
+function dataURItoBlob(dataURI) {
+ const byteString = atob(dataURI.split(',')[1]);
+ const mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
+ const ab = new ArrayBuffer(byteString.length);
+ const ia = new Uint8Array(ab);
+ for (let i = 0; i < byteString.length; i++) {
+ ia[i] = byteString.charCodeAt(i);
+ }
+ return new Blob([ab], { type: mimeString });
+}
+
+// 搴旂敤绫�
+class App {
+ constructor() {
+ this.uiController = null;
+ this.audioPlayer = null;
+ this.live2dManager = null;
+ this.cameraStream = null;
+ this.currentFacingMode = 'user';
+ }
+
+ // 鍒濆鍖栧簲鐢�
+ async init() {
+ log('姝e湪鍒濆鍖栧簲鐢�...', 'info');
+ // 鍒濆鍖朥I鎺у埗鍣�
+ this.uiController = uiController;
+ this.uiController.init();
+ // 妫�鏌pus搴�
+ checkOpusLoaded();
+ // 鍒濆鍖朞pus缂栫爜鍣�
+ initOpusEncoder();
+ // 鍒濆鍖栭煶棰戞挱鏀惧櫒
+ this.audioPlayer = getAudioPlayer();
+ await this.audioPlayer.start();
+ // 鍒濆鍖朚CP宸ュ叿
+ initMcpTools();
+ // 鍒濆鍖栨湰鍦板敜閱掍簨浠剁洃鍚�
+ startWakewordBridgeListener();
+ // 妫�鏌ラ害鍏嬮鍙敤鎬�
+ await this.checkMicrophoneAvailability();
+ // 妫�鏌ユ憚鍍忓ご鍙敤鎬�
+ this.checkCameraAvailability();
+ // 鍒濆鍖朙ive2D
+ await this.initLive2D();
+ // 鍒濆鍖栨憚鍍忓ご
+ this.initCamera();
+ // 鍏抽棴鍔犺浇loading
+ this.setModelLoadingStatus(false);
+ log('搴旂敤鍒濆鍖栧畬鎴�', 'success');
+ }
+
+ // 鍒濆鍖朙ive2D
+ async initLive2D() {
+ try {
+ // 妫�鏌ive2DManager鏄惁宸插姞杞�
+ if (typeof window.Live2DManager === 'undefined') {
+ throw new Error('Live2DManager鏈姞杞斤紝璇锋鏌ヨ剼鏈紩鍏ラ『搴�');
+ }
+ this.live2dManager = new window.Live2DManager();
+ await this.live2dManager.initializeLive2D();
+ // 鏇存柊UI鐘舵��
+ const live2dStatus = document.getElementById('live2dStatus');
+ if (live2dStatus) {
+ live2dStatus.textContent = '鈼� 宸插姞杞�';
+ live2dStatus.className = 'status loaded';
+ }
+ log('Live2D鍒濆鍖栧畬鎴�', 'success');
+ } catch (error) {
+ log(`Live2D鍒濆鍖栧け璐�: ${error.message}`, 'error');
+ // 鏇存柊UI鐘舵��
+ const live2dStatus = document.getElementById('live2dStatus');
+ if (live2dStatus) {
+ live2dStatus.textContent = '鈼� 鍔犺浇澶辫触';
+ live2dStatus.className = 'status error';
+ }
+ }
+ }
+
+ // 璁剧疆model鍔犺浇鐘舵��
+ setModelLoadingStatus(isLoading) {
+ const modelLoading = document.getElementById('modelLoading');
+ if (modelLoading) {
+ modelLoading.style.display = isLoading ? 'flex' : 'none';
+ }
+ }
+
+ /**
+ * 妫�鏌ラ害鍏嬮鍙敤鎬�
+ * 鍦ㄥ簲鐢ㄥ垵濮嬪寲鏃惰皟鐢紝妫�鏌ラ害鍏嬮鏄惁鍙敤骞舵洿鏂癠I鐘舵��
+ */
+ async checkMicrophoneAvailability() {
+ try {
+ const isAvailable = await checkMicrophoneAvailability();
+ const isHttp = isHttpNonLocalhost();
+ // 淇濆瓨鍙敤鎬х姸鎬佸埌鍏ㄥ眬鍙橀噺
+ window.microphoneAvailable = isAvailable;
+ window.isHttpNonLocalhost = isHttp;
+ // 鏇存柊UI
+ if (this.uiController) {
+ this.uiController.updateMicrophoneAvailability(isAvailable, isHttp);
+ }
+ log(`楹﹀厠椋庡彲鐢ㄦ�ф鏌ュ畬鎴�: ${isAvailable ? '鍙敤' : '涓嶅彲鐢�'}`, isAvailable ? 'success' : 'warning');
+ } catch (error) {
+ log(`妫�鏌ラ害鍏嬮鍙敤鎬уけ璐�: ${error.message}`, 'error');
+ // 榛樿璁剧疆涓轰笉鍙敤
+ window.microphoneAvailable = false;
+ window.isHttpNonLocalhost = isHttpNonLocalhost();
+ if (this.uiController) {
+ this.uiController.updateMicrophoneAvailability(false, window.isHttpNonLocalhost);
+ }
+ }
+ }
+
+ // 妫�鏌ユ憚鍍忓ご鍙敤鎬�
+ checkCameraAvailability() {
+ window.cameraAvailable = true;
+ log('鎽勫儚澶村彲鐢ㄦ�ф鏌ュ畬鎴�: 榛樿宸茬粦瀹氶獙璇佺爜', 'success');
+ }
+
+ // 鍒濆鍖栨憚鍍忓ご
+ async initCamera() {
+ const cameraContainer = document.getElementById('cameraContainer');
+ const cameraVideo = document.getElementById('cameraVideo');
+ const cameraSwitch = document.getElementById('cameraSwitch');
+ const cameraSwitchMask = document.getElementById('cameraSwitchMask');
+ const dialBtn = document.getElementById('dialBtn');
+
+ if (!cameraContainer || !cameraVideo) {
+ log('鎽勫儚澶村厓绱犳湭鎵惧埌锛岃烦杩囧垵濮嬪寲', 'warning');
+ return Promise.resolve(false);
+ }
+
+ let isDragging = false;
+ let currentX, currentY, initialX, initialY;
+ let xOffset = 0, yOffset = 0;
+
+ cameraContainer.addEventListener('mousedown', dragStart);
+ document.addEventListener('mousemove', drag);
+ document.addEventListener('mouseup', dragEnd);
+ cameraContainer.addEventListener('touchstart', dragStart, { passive: false });
+ document.addEventListener('touchmove', drag, { passive: false });
+ document.addEventListener('touchend', dragEnd);
+
+ function dragStart(e) {
+ if (e.type === 'touchstart') {
+ initialX = e.touches[0].clientX - xOffset;
+ initialY = e.touches[0].clientY - yOffset;
+ } else {
+ initialX = e.clientX - xOffset;
+ initialY = e.clientY - yOffset;
+ }
+ isDragging = true;
+ cameraContainer.classList.add('dragging');
+ }
+
+ function drag(e) {
+ if (isDragging) {
+ e.preventDefault();
+ if (e.type === 'touchmove') {
+ currentX = e.touches[0].clientX - initialX;
+ currentY = e.touches[0].clientY - initialY;
+ } else {
+ currentX = e.clientX - initialX;
+ currentY = e.clientY - initialY;
+ }
+ xOffset = currentX;
+ yOffset = currentY;
+ cameraContainer.style.transform = `translate3d(${currentX}px, ${currentY}px, 0)`;
+ }
+ }
+
+ function dragEnd() {
+ initialX = currentX;
+ initialY = currentY;
+ isDragging = false;
+ cameraContainer.classList.remove('dragging');
+ }
+
+ return new Promise((resolve) => {
+ window.startCamera = async () => {
+ try {
+ if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
+ log('娴忚鍣ㄤ笉鏀寔鎽勫儚澶碅PI', 'warning');
+ return false;
+ }
+ log('姝e湪璇锋眰鎽勫儚澶存潈闄�...', 'info');
+ this.cameraStream = await navigator.mediaDevices.getUserMedia({
+ video: { width: 180, height: 240, facingMode: this.currentFacingMode },
+ audio: false
+ });
+ cameraVideo.srcObject = this.cameraStream;
+ const devices = await navigator.mediaDevices.enumerateDevices();
+ const videoDevices = devices.filter(device => device.kind === 'videoinput');
+ if (videoDevices.length > 1) {
+ if (cameraSwitch) cameraSwitch.classList.add('active');
+ }
+ cameraContainer.classList.add('active');
+
+ // 鍒囨崲鏃舵寕鏂儏鍐�
+ const hasActive = dialBtn.classList.contains('dial-active');
+ if (!hasActive) {
+ cameraContainer.classList.remove('active');
+ cameraSwitch.classList.remove('active');
+ window.stopCamera();
+ }
+ log('鎽勫儚澶村凡鍚姩', 'success');
+ return true;
+ } catch (error) {
+ log(`鍚姩鎽勫儚澶村け璐�: ${error.name} - ${error.message}`, 'error');
+ if (error.name === 'NotAllowedError') {
+ log('鎽勫儚澶存潈闄愯鎷掔粷锛岃妫�鏌ユ祻瑙堝櫒璁剧疆', 'warning');
+ } else if (error.name === 'NotFoundError') {
+ log('鏈壘鍒版憚鍍忓ご璁惧', 'warning');
+ } else if (error.name === 'NotReadableError') {
+ log('鎽勫儚澶村凡琚叾浠栫▼搴忓崰鐢�', 'warning');
+ }
+ return false;
+ }
+ };
+
+ window.stopCamera = () => {
+ if (this.cameraStream) {
+ this.cameraStream.getTracks().forEach(track => track.stop());
+ this.cameraStream = null;
+ cameraVideo.srcObject = null;
+ log('鎽勫儚澶村凡鍏抽棴', 'info');
+ }
+ };
+
+ window.switchCamera = async() => {
+ if (window.switchCameraTimer) return;
+ if (this.cameraStream) {
+ const currentTransform = window.getComputedStyle(cameraContainer).transform;
+ const originalTransform = currentTransform === 'none' ? 'translate(0px, 0px)' : currentTransform;
+ cameraContainer.style.setProperty('--original-transform', originalTransform);
+ cameraContainer.classList.add('flip');
+ if (cameraSwitchMask) cameraSwitchMask.style.opacity = 0;
+ this.currentFacingMode = this.currentFacingMode === 'user' ? 'environment' : 'user';
+ window.stopCamera();
+ window.startCamera();
+
+ window.switchCameraTimer = setTimeout(() => {
+ if (this.currentFacingMode === 'user') {
+ cameraVideo.style.transform = 'scaleX(-1)';
+ } else {
+ cameraVideo.style.transform = 'scaleX(1)';
+ }
+ window.switchCameraTimer = null;
+ cameraContainer.classList.remove('flip');
+ cameraContainer.style.removeProperty('--original-transform');
+ if (cameraSwitchMask) cameraSwitchMask.style.opacity = 1;
+ }, 500);
+ }
+ };
+
+ window.takePhoto = (question = '鎻忚堪涓�涓嬬湅鍒扮殑鐗╁搧') => {
+ return new Promise(async (resolve) => {
+ const canvas = document.createElement('canvas');
+ const video = cameraVideo;
+
+ if (!video || video.readyState !== video.HAVE_ENOUGH_DATA) {
+ log('鏃犳硶鎷嶇収锛氭憚鍍忓ご鏈氨缁�', 'warning');
+ resolve({
+ success: false,
+ error: '鎽勫儚澶存湭灏辩华锛岃纭繚宸茶繛鎺ヤ笖鎽勫儚澶村凡鍚姩'
+ });
+ return;
+ }
+
+ canvas.width = video.videoWidth || 180;
+ canvas.height = video.videoHeight || 240;
+ const ctx = canvas.getContext('2d');
+ ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
+
+ const photoData = canvas.toDataURL('image/jpeg', 0.8);
+ log(`鎷嶇収鎴愬姛锛屽浘鍍忔暟鎹暱搴�: ${photoData.length}`, 'success');
+
+ try {
+ const xz_tester_vision = localStorage.getItem('xz_tester_vision');
+ if (xz_tester_vision) {
+ let visionInfo = null;
+
+ try {
+ visionInfo = JSON.parse(xz_tester_vision);
+ } catch (err) {
+ throw new Error(`瑙嗚閰嶇疆瑙f瀽澶辫触`);
+ }
+
+ const { url, token } = visionInfo || {};
+ if (!url || !token) {
+ throw new Error('瑙嗚鍒嗘瀽澶辫触锛氶厤缃己灏戞帴鍙e湴鍧�(url)鎴栦护鐗�(token)');
+ }
+
+ log(`姝e湪鍙戦�佸浘鐗囧埌瑙嗚鍒嗘瀽鎺ュ彛: ${url}`, 'info');
+
+ const deviceId = document.getElementById('deviceMac')?.value || '';
+ const clientId = document.getElementById('clientId')?.value || 'web_test_client';
+
+ const formData = new FormData();
+ formData.append('question', question);
+ formData.append('image', dataURItoBlob(photoData), 'photo.jpg');
+
+ const response = await fetch(url, {
+ method: 'POST',
+ body: formData,
+ headers: {
+ 'Device-Id': deviceId,
+ 'Client-Id': clientId,
+ 'Authorization': `Bearer ${token}`
+ }
+ });
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ const analysisResult = await response.json();
+ log(`瑙嗚鍒嗘瀽瀹屾垚: ${JSON.stringify(analysisResult).substring(0, 200)}...`, 'success');
+
+ resolve({
+ success: true,
+ message: question,
+ photo_data: photoData,
+ photo_width: canvas.width,
+ photo_height: canvas.height,
+ vision_analysis: analysisResult
+ });
+ } else {
+ log('鏈厤缃瑙夊垎鏋愭湇鍔�', 'warning');
+ }
+ } catch (error) {
+ log(`瑙嗚鍒嗘瀽澶辫触: ${error.message}`, 'error');
+ resolve({
+ success: true,
+ message: question,
+ photo_data: photoData,
+ photo_width: canvas.width,
+ photo_height: canvas.height,
+ vision_analysis: {
+ success: false,
+ error: error.message,
+ fallback: '鏃犳硶杩炴帴鍒拌瑙夊垎鏋愭湇鍔�'
+ }
+ });
+ }
+ });
+ };
+
+ log('鎽勫儚澶村垵濮嬪寲瀹屾垚', 'success');
+ resolve(true);
+ });
+ }
+}
+
+// 鍒涘缓骞跺惎鍔ㄥ簲鐢�
+const app = new App();
+// 灏嗗簲鐢ㄥ疄渚嬫毚闇插埌鍏ㄥ眬锛屼緵鍏朵粬妯″潡璁块棶
+window.chatApp = app;
+document.addEventListener('DOMContentLoaded', () => {
+ // 鍒濆鍖栧簲鐢�
+ app.init();
+});
+export default app;
diff --git a/xiaozhi-esp32-server/main/digital-human/js/config/default-mcp-tools.json b/xiaozhi-esp32-server/main/digital-human/js/config/default-mcp-tools.json
new file mode 100644
index 0000000..13ee666
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/js/config/default-mcp-tools.json
@@ -0,0 +1,90 @@
+[
+ {
+ "name": "self_camera_take_photo",
+ "description": "Take a photo using the device's camera. This tool captures the current camera frame and returns the image data. You MUST call this tool when user asks to 'take a photo', 'what do you see', 'describe what you see', or similar requests. After getting the photo data, describe what you see in the image. Parameter 'question' is optional, defaults to 'describe objects seen'.",
+ "inputSchema": {
+ "type": "object",
+ "properties": {
+ "question": {
+ "type": "string",
+ "description": "Question to guide the photo analysis, e.g., 'describe the objects seen'. Can be empty."
+ }
+ }
+ },
+ "mockResponse": {
+ "success": true,
+ "photo_data": "base64_image_data",
+ "response": "Please describe what you see based on the returned photo_data"
+ }
+ },
+ {
+ "name": "self.get_device_status",
+ "description": "Provides the real-time information of the device, including the current status of the audio speaker, screen, battery, network, etc.\nUse this tool for: \n1. Answering questions about current condition (e.g. what is the current volume of the audio speaker?)\n2. As the first step to control the device (e.g. turn up / down the volume of the audio speaker, etc.)",
+ "inputSchema": {
+ "type": "object",
+ "properties": {}
+ },
+ "mockResponse": {
+ "audio_speaker": {
+ "volume": 50,
+ "muted": false
+ },
+ "screen": {
+ "brightness": 80,
+ "theme": "light"
+ },
+ "battery": {
+ "level": 85,
+ "charging": false
+ },
+ "network": {
+ "connected": true,
+ "type": "wifi"
+ }
+ }
+ },
+ {
+ "name": "self.audio_speaker.set_volume",
+ "description": "Set the volume of the audio speaker. If the current volume is unknown, you must call `self.get_device_status` tool first and then call this tool.",
+ "inputSchema": {
+ "type": "object",
+ "properties": {
+ "volume": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 100
+ }
+ },
+ "required": [
+ "volume"
+ ]
+ },
+ "mockResponse": {
+ "success": true,
+ "volume": "${volume}",
+ "message": "闊抽噺宸茶缃负 ${volume}"
+ }
+ },
+ {
+ "name": "self.screen.set_brightness",
+ "description": "Set the brightness of the screen.",
+ "inputSchema": {
+ "type": "object",
+ "properties": {
+ "brightness": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 100
+ }
+ },
+ "required": [
+ "brightness"
+ ]
+ },
+ "mockResponse": {
+ "success": true,
+ "brightness": "${brightness}",
+ "message": "浜害宸茶缃负 ${brightness}"
+ }
+ }
+]
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/js/config/manager.js b/xiaozhi-esp32-server/main/digital-human/js/config/manager.js
new file mode 100644
index 0000000..50a0c18
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/js/config/manager.js
@@ -0,0 +1,127 @@
+// 閰嶇疆绠$悊妯″潡
+
+// 榛樿鍞ら啋璇嶅垪琛�
+export const DEFAULT_WAKE_WORDS = '浣犲ソ灏忔櫤\n浣犲ソ灏忓織\n灏忕埍鍚屽\n浣犲ソ灏忛懌\n浣犲ソ灏忔柊\n灏忕編鍚屽\n灏忛緳灏忛緳\n鍠靛柕鍚屽\n灏忔花灏忔花\n灏忓啺灏忓啺\n鍢夸綘濂藉憖';
+
+// 鐢熸垚闅忔満MAC鍦板潃
+function generateRandomMac() {
+ const hexDigits = '0123456789ABCDEF';
+ let mac = '';
+ for (let i = 0; i < 6; i++) {
+ if (i > 0) mac += ':';
+ for (let j = 0; j < 2; j++) {
+ mac += hexDigits.charAt(Math.floor(Math.random() * 16));
+ }
+ }
+ return mac;
+}
+
+// 鍔犺浇閰嶇疆
+export function loadConfig() {
+ const deviceMacInput = document.getElementById('deviceMac');
+ const deviceNameInput = document.getElementById('deviceName');
+ const clientIdInput = document.getElementById('clientId');
+ const otaUrlInput = document.getElementById('otaUrl');
+ const wakewordWsUrlInput = document.getElementById('wakewordWsUrl');
+ const wakewordEnabledInput = document.getElementById('wakewordEnabled');
+ const wakewordListInput = document.getElementById('wakewordList');
+
+ // 浠巐ocalStorage鍔犺浇MAC鍦板潃锛屽鏋滄病鏈夊垯鐢熸垚鏂扮殑
+ let savedMac = localStorage.getItem('xz_tester_deviceMac');
+ if (!savedMac) {
+ savedMac = generateRandomMac();
+ localStorage.setItem('xz_tester_deviceMac', savedMac);
+ }
+ deviceMacInput.value = savedMac;
+
+ // 浠巐ocalStorage鍔犺浇鍏朵粬閰嶇疆
+ const savedDeviceName = localStorage.getItem('xz_tester_deviceName');
+ if (savedDeviceName) {
+ deviceNameInput.value = savedDeviceName;
+ }
+
+ const savedClientId = localStorage.getItem('xz_tester_clientId');
+ if (savedClientId) {
+ clientIdInput.value = savedClientId;
+ }
+
+ const savedOtaUrl = localStorage.getItem('xz_tester_otaUrl');
+ if (savedOtaUrl) {
+ otaUrlInput.value = savedOtaUrl;
+ }
+
+ const savedWakewordWsUrl = localStorage.getItem('xz_tester_wakewordWsUrl');
+ if (savedWakewordWsUrl !== null && wakewordWsUrlInput) {
+ wakewordWsUrlInput.value = savedWakewordWsUrl;
+ }
+
+ const savedWakewordEnabled = localStorage.getItem('xz_tester_wakewordEnabled');
+ if (savedWakewordEnabled !== null && wakewordEnabledInput) {
+ wakewordEnabledInput.value = savedWakewordEnabled;
+ }
+
+ const savedWakewordList = localStorage.getItem('xz_tester_wakewordList');
+ if (savedWakewordList !== null && wakewordListInput) {
+ wakewordListInput.value = savedWakewordList;
+ } else if (wakewordListInput) {
+ wakewordListInput.value = DEFAULT_WAKE_WORDS;
+ }
+
+ const emojiEnabledInput = document.getElementById('emojiEnabled');
+ const savedEmojiEnabled = localStorage.getItem('xz_tester_emojiEnabled');
+ if (savedEmojiEnabled !== null && emojiEnabledInput) {
+ emojiEnabledInput.value = savedEmojiEnabled;
+ }
+}
+
+// 淇濆瓨閰嶇疆
+export function saveConfig() {
+ const deviceMacInput = document.getElementById('deviceMac');
+ const deviceNameInput = document.getElementById('deviceName');
+ const clientIdInput = document.getElementById('clientId');
+ const wakewordWsUrlInput = document.getElementById('wakewordWsUrl');
+ const wakewordEnabledInput = document.getElementById('wakewordEnabled');
+ const wakewordListInput = document.getElementById('wakewordList');
+
+ localStorage.setItem('xz_tester_deviceMac', deviceMacInput.value);
+ localStorage.setItem('xz_tester_deviceName', deviceNameInput.value);
+ localStorage.setItem('xz_tester_clientId', clientIdInput.value);
+ const emojiEnabledInput = document.getElementById('emojiEnabled');
+ if (emojiEnabledInput) {
+ localStorage.setItem('xz_tester_emojiEnabled', emojiEnabledInput.value);
+ }
+ if (wakewordEnabledInput) {
+ localStorage.setItem('xz_tester_wakewordEnabled', wakewordEnabledInput.value);
+ }
+ if (wakewordListInput) {
+ localStorage.setItem('xz_tester_wakewordList', wakewordListInput.value);
+ }
+ if (wakewordWsUrlInput && wakewordWsUrlInput.value.trim()) {
+ localStorage.setItem('xz_tester_wakewordWsUrl', wakewordWsUrlInput.value.trim());
+ }
+}
+
+// 鑾峰彇閰嶇疆鍊�
+export function getConfig() {
+ // 浠嶥OM鑾峰彇鍊�
+ const deviceMac = document.getElementById('deviceMac')?.value.trim() || '';
+ const deviceName = document.getElementById('deviceName')?.value.trim() || '';
+ const clientId = document.getElementById('clientId')?.value.trim() || '';
+ const emojiEnabled = document.getElementById('emojiEnabled')?.value !== 'false';
+
+ return {
+ deviceId: deviceMac, // 浣跨敤MAC鍦板潃浣滀负deviceId
+ deviceName,
+ deviceMac,
+ clientId,
+ emojiEnabled
+ };
+}
+
+// 淇濆瓨杩炴帴URL
+export function saveConnectionUrls() {
+ const otaUrl = document.getElementById('otaUrl').value.trim();
+ const wsUrl = document.getElementById('serverUrl').value.trim();
+ localStorage.setItem('xz_tester_otaUrl', otaUrl);
+ localStorage.setItem('xz_tester_wsUrl', wsUrl);
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/js/core/audio/opus-codec.js b/xiaozhi-esp32-server/main/digital-human/js/core/audio/opus-codec.js
new file mode 100644
index 0000000..52ee12e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/js/core/audio/opus-codec.js
@@ -0,0 +1,182 @@
+import { log } from '../../utils/logger.js?v=0205';
+
+
+// 妫�鏌pus搴撴槸鍚﹀凡鍔犺浇
+export function checkOpusLoaded() {
+ try {
+ // 妫�鏌odule鏄惁瀛樺湪锛堟湰鍦板簱瀵煎嚭鐨勫叏灞�鍙橀噺锛�
+ if (typeof Module === 'undefined') {
+ throw new Error('Opus搴撴湭鍔犺浇锛孧odule瀵硅薄涓嶅瓨鍦�');
+ }
+
+ // 灏濊瘯鍏堜娇鐢∕odule.instance锛坙ibopus.js鏈�鍚庝竴琛屽鍑烘柟寮忥級
+ if (typeof Module.instance !== 'undefined' && typeof Module.instance._opus_decoder_get_size === 'function') {
+ // 浣跨敤Module.instance瀵硅薄鏇挎崲鍏ㄥ眬Module瀵硅薄
+ window.ModuleInstance = Module.instance;
+ log('Opus搴撳姞杞芥垚鍔燂紙浣跨敤Module.instance锛�', 'success');
+
+ // 3绉掑悗闅愯棌鐘舵��
+ const statusElement = document.getElementById('scriptStatus');
+ if (statusElement) statusElement.style.display = 'none';
+ return;
+ }
+
+ // 濡傛灉娌℃湁Module.instance锛屾鏌ュ叏灞�Module鍑芥暟
+ if (typeof Module._opus_decoder_get_size === 'function') {
+ window.ModuleInstance = Module;
+ log('Opus搴撳姞杞芥垚鍔燂紙浣跨敤鍏ㄥ眬Module锛�', 'success');
+
+ // 3绉掑悗闅愯棌鐘舵��
+ const statusElement = document.getElementById('scriptStatus');
+ if (statusElement) statusElement.style.display = 'none';
+ return;
+ }
+
+ throw new Error('Opus瑙g爜鍑芥暟鏈壘鍒帮紝鍙兘Module缁撴瀯涓嶆纭�');
+ } catch (err) {
+ log(`Opus搴撳姞杞藉け璐ワ紝璇锋鏌ibopus.js鏂囦欢鏄惁瀛樺湪涓旀纭�: ${err.message}`, 'error');
+ }
+}
+
+
+// 鍒涘缓涓�涓狾pus缂栫爜鍣�
+let opusEncoder = null;
+export function initOpusEncoder() {
+ try {
+ if (opusEncoder) {
+ return opusEncoder; // 宸茬粡鍒濆鍖栬繃
+ }
+
+ if (!window.ModuleInstance) {
+ log('鏃犳硶鍒涘缓Opus缂栫爜鍣細ModuleInstance涓嶅彲鐢�', 'error');
+ return;
+ }
+
+ // 鍒濆鍖栦竴涓狾pus缂栫爜鍣�
+ const mod = window.ModuleInstance;
+ const sampleRate = 16000; // 16kHz閲囨牱鐜�
+ const channels = 1; // 鍗曞0閬�
+ const application = 2048; // OPUS_APPLICATION_VOIP = 2048
+
+ // 鍒涘缓缂栫爜鍣�
+ opusEncoder = {
+ channels: channels,
+ sampleRate: sampleRate,
+ frameSize: 960, // 60ms @ 16kHz = 60 * 16 = 960 samples
+ maxPacketSize: 4000, // 鏈�澶у寘澶у皬
+ module: mod,
+
+ // 鍒濆鍖栫紪鐮佸櫒
+ init: function () {
+ try {
+ // 鑾峰彇缂栫爜鍣ㄥぇ灏�
+ const encoderSize = mod._opus_encoder_get_size(this.channels);
+ log(`Opus缂栫爜鍣ㄥぇ灏�: ${encoderSize}瀛楄妭`, 'info');
+
+ // 鍒嗛厤鍐呭瓨
+ this.encoderPtr = mod._malloc(encoderSize);
+ if (!this.encoderPtr) {
+ throw new Error("鏃犳硶鍒嗛厤缂栫爜鍣ㄥ唴瀛�");
+ }
+
+ // 鍒濆鍖栫紪鐮佸櫒
+ const err = mod._opus_encoder_init(
+ this.encoderPtr,
+ this.sampleRate,
+ this.channels,
+ application
+ );
+
+ if (err < 0) {
+ throw new Error(`Opus缂栫爜鍣ㄥ垵濮嬪寲澶辫触: ${err}`);
+ }
+
+ // 璁剧疆浣嶇巼 (16kbps)
+ mod._opus_encoder_ctl(this.encoderPtr, 4002, 16000); // OPUS_SET_BITRATE
+
+ // 璁剧疆澶嶆潅搴� (0-10, 瓒婇珮璐ㄩ噺瓒婂ソ浣咰PU浣跨敤瓒婂)
+ mod._opus_encoder_ctl(this.encoderPtr, 4010, 5); // OPUS_SET_COMPLEXITY
+
+ // 璁剧疆浣跨敤DTX (涓嶄紶杈撻潤闊冲抚)
+ mod._opus_encoder_ctl(this.encoderPtr, 4016, 1); // OPUS_SET_DTX
+
+ log("Opus缂栫爜鍣ㄥ垵濮嬪寲鎴愬姛", 'success');
+ return true;
+ } catch (error) {
+ if (this.encoderPtr) {
+ mod._free(this.encoderPtr);
+ this.encoderPtr = null;
+ }
+ log(`Opus缂栫爜鍣ㄥ垵濮嬪寲澶辫触: ${error.message}`, 'error');
+ return false;
+ }
+ },
+
+ // 缂栫爜PCM鏁版嵁涓篛pus
+ encode: function (pcmData) {
+ if (!this.encoderPtr) {
+ if (!this.init()) {
+ return null;
+ }
+ }
+
+ try {
+ const mod = this.module;
+
+ // 涓篜CM鏁版嵁鍒嗛厤鍐呭瓨
+ const pcmPtr = mod._malloc(pcmData.length * 2); // 2瀛楄妭/int16
+
+ // 灏哖CM鏁版嵁澶嶅埗鍒癏EAP
+ for (let i = 0; i < pcmData.length; i++) {
+ mod.HEAP16[(pcmPtr >> 1) + i] = pcmData[i];
+ }
+
+ // 涓鸿緭鍑哄垎閰嶅唴瀛�
+ const outPtr = mod._malloc(this.maxPacketSize);
+
+ // 杩涜缂栫爜
+ const encodedLen = mod._opus_encode(
+ this.encoderPtr,
+ pcmPtr,
+ this.frameSize,
+ outPtr,
+ this.maxPacketSize
+ );
+
+ if (encodedLen < 0) {
+ throw new Error(`Opus缂栫爜澶辫触: ${encodedLen}`);
+ }
+
+ // 澶嶅埗缂栫爜鍚庣殑鏁版嵁
+ const opusData = new Uint8Array(encodedLen);
+ for (let i = 0; i < encodedLen; i++) {
+ opusData[i] = mod.HEAPU8[outPtr + i];
+ }
+
+ // 閲婃斁鍐呭瓨
+ mod._free(pcmPtr);
+ mod._free(outPtr);
+
+ return opusData;
+ } catch (error) {
+ log(`Opus缂栫爜鍑洪敊: ${error.message}`, 'error');
+ return null;
+ }
+ },
+
+ // 閿�姣佺紪鐮佸櫒
+ destroy: function () {
+ if (this.encoderPtr) {
+ this.module._free(this.encoderPtr);
+ this.encoderPtr = null;
+ }
+ }
+ };
+
+ opusEncoder.init();
+ return opusEncoder;
+ } catch (error) {
+ log(`鍒涘缓Opus缂栫爜鍣ㄥけ璐�: ${error.message}`, 'error');
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/js/core/audio/player.js b/xiaozhi-esp32-server/main/digital-human/js/core/audio/player.js
new file mode 100644
index 0000000..70b37fa
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/js/core/audio/player.js
@@ -0,0 +1,297 @@
+// 闊抽鎾斁妯″潡
+import BlockingQueue from '../../utils/blocking-queue.js?v=0205';
+import { log } from '../../utils/logger.js?v=0205';
+import { createStreamingContext } from './stream-context.js?v=0205';
+
+// 闊抽鎾斁鍣ㄧ被
+export class AudioPlayer {
+ constructor() {
+ // 闊抽鍙傛暟
+ this.SAMPLE_RATE = 16000;
+ this.CHANNELS = 1;
+ this.FRAME_SIZE = 960;
+ this.MIN_AUDIO_DURATION = 0.12;
+
+ // 鐘舵��
+ this.audioContext = null;
+ this.opusDecoder = null;
+ this.streamingContext = null;
+ this.queue = new BlockingQueue();
+ this.isPlaying = false;
+ }
+
+ // 鑾峰彇鎴栧垱寤篈udioContext
+ getAudioContext() {
+ if (!this.audioContext) {
+ this.audioContext = new (window.AudioContext || window.webkitAudioContext)({
+ sampleRate: this.SAMPLE_RATE,
+ latencyHint: 'interactive'
+ });
+ log('鍒涘缓闊抽涓婁笅鏂囷紝閲囨牱鐜�: ' + this.SAMPLE_RATE + 'Hz', 'debug');
+ }
+ return this.audioContext;
+ }
+
+ // 鍒濆鍖朞pus瑙g爜鍣�
+ async initOpusDecoder() {
+ if (this.opusDecoder) return this.opusDecoder;
+
+ try {
+ if (typeof window.ModuleInstance === 'undefined') {
+ if (typeof Module !== 'undefined') {
+ window.ModuleInstance = Module;
+ log('浣跨敤鍏ㄥ眬Module浣滀负ModuleInstance', 'info');
+ } else {
+ throw new Error('Opus搴撴湭鍔犺浇锛孧oduleInstance鍜孧odule瀵硅薄閮戒笉瀛樺湪');
+ }
+ }
+
+ const mod = window.ModuleInstance;
+
+ this.opusDecoder = {
+ channels: this.CHANNELS,
+ rate: this.SAMPLE_RATE,
+ frameSize: this.FRAME_SIZE,
+ module: mod,
+ decoderPtr: null,
+
+ init: function () {
+ if (this.decoderPtr) return true;
+
+ const decoderSize = mod._opus_decoder_get_size(this.channels);
+ log(`Opus瑙g爜鍣ㄥぇ灏�: ${decoderSize}瀛楄妭`, 'debug');
+
+ this.decoderPtr = mod._malloc(decoderSize);
+ if (!this.decoderPtr) {
+ throw new Error("鏃犳硶鍒嗛厤瑙g爜鍣ㄥ唴瀛�");
+ }
+
+ const err = mod._opus_decoder_init(
+ this.decoderPtr,
+ this.rate,
+ this.channels
+ );
+
+ if (err < 0) {
+ this.destroy();
+ throw new Error(`Opus瑙g爜鍣ㄥ垵濮嬪寲澶辫触: ${err}`);
+ }
+
+ log("Opus瑙g爜鍣ㄥ垵濮嬪寲鎴愬姛", 'success');
+ return true;
+ },
+
+ decode: function (opusData) {
+ if (!this.decoderPtr) {
+ if (!this.init()) {
+ throw new Error("瑙g爜鍣ㄦ湭鍒濆鍖栦笖鏃犳硶鍒濆鍖�");
+ }
+ }
+
+ try {
+ const mod = this.module;
+
+ const opusPtr = mod._malloc(opusData.length);
+ mod.HEAPU8.set(opusData, opusPtr);
+
+ const pcmPtr = mod._malloc(this.frameSize * 2);
+
+ const decodedSamples = mod._opus_decode(
+ this.decoderPtr,
+ opusPtr,
+ opusData.length,
+ pcmPtr,
+ this.frameSize,
+ 0
+ );
+
+ if (decodedSamples < 0) {
+ mod._free(opusPtr);
+ mod._free(pcmPtr);
+ throw new Error(`Opus瑙g爜澶辫触: ${decodedSamples}`);
+ }
+
+ const decodedData = new Int16Array(decodedSamples);
+ for (let i = 0; i < decodedSamples; i++) {
+ decodedData[i] = mod.HEAP16[(pcmPtr >> 1) + i];
+ }
+
+ mod._free(opusPtr);
+ mod._free(pcmPtr);
+
+ return decodedData;
+ } catch (error) {
+ log(`Opus瑙g爜閿欒: ${error.message}`, 'error');
+ return new Int16Array(0);
+ }
+ },
+
+ destroy: function () {
+ if (this.decoderPtr) {
+ this.module._free(this.decoderPtr);
+ this.decoderPtr = null;
+ }
+ }
+ };
+
+ if (!this.opusDecoder.init()) {
+ throw new Error("Opus瑙g爜鍣ㄥ垵濮嬪寲澶辫触");
+ }
+
+ return this.opusDecoder;
+
+ } catch (error) {
+ log(`Opus瑙g爜鍣ㄥ垵濮嬪寲澶辫触: ${error.message}`, 'error');
+ this.opusDecoder = null;
+ throw error;
+ }
+ }
+
+ // 鍚姩闊抽缂撳啿
+ async startAudioBuffering() {
+ log("寮�濮嬮煶棰戠紦鍐�...", 'info');
+
+ this.initOpusDecoder().catch(error => {
+ log(`棰勫垵濮嬪寲Opus瑙g爜鍣ㄥけ璐�: ${error.message}`, 'warning');
+ });
+
+ const timeout = 400;
+ while (true) {
+ const packets = await this.queue.dequeue(
+ 6,
+ timeout,
+ (count) => {
+ log(`缂撳啿瓒呮椂锛屽綋鍓嶇紦鍐插寘鏁�: ${count}锛屽紑濮嬫挱鏀綻, 'info');
+ }
+ );
+ if (packets.length) {
+ log(`宸茬紦鍐� ${packets.length} 涓煶棰戝寘锛屽紑濮嬫挱鏀綻, 'info');
+ this.streamingContext.pushAudioBuffer(packets);
+ }
+
+ while (true) {
+ const data = await this.queue.dequeue(99, 30);
+ if (data.length) {
+ this.streamingContext.pushAudioBuffer(data);
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ // 鎾斁宸茬紦鍐茬殑闊抽
+ async playBufferedAudio() {
+ try {
+ this.audioContext = this.getAudioContext();
+
+ if (!this.opusDecoder) {
+ log('鍒濆鍖朞pus瑙g爜鍣�...', 'info');
+ try {
+ this.opusDecoder = await this.initOpusDecoder();
+ if (!this.opusDecoder) {
+ throw new Error('瑙g爜鍣ㄥ垵濮嬪寲澶辫触');
+ }
+ log('Opus瑙g爜鍣ㄥ垵濮嬪寲鎴愬姛', 'success');
+ } catch (error) {
+ log('Opus瑙g爜鍣ㄥ垵濮嬪寲澶辫触: ' + error.message, 'error');
+ this.isPlaying = false;
+ return;
+ }
+ }
+
+ if (!this.streamingContext) {
+ this.streamingContext = createStreamingContext(
+ this.opusDecoder,
+ this.audioContext,
+ this.SAMPLE_RATE,
+ this.CHANNELS,
+ this.MIN_AUDIO_DURATION
+ );
+ }
+
+ this.streamingContext.decodeOpusFrames();
+ this.streamingContext.startPlaying();
+
+ } catch (error) {
+ log(`鎾斁宸茬紦鍐茬殑闊抽鍑洪敊: ${error.message}`, 'error');
+ this.isPlaying = false;
+ this.streamingContext = null;
+ }
+ }
+
+ // 娣诲姞闊抽鏁版嵁鍒伴槦鍒�
+ enqueueAudioData(opusData) {
+ if (opusData.length > 0) {
+ this.queue.enqueue(opusData);
+ } else {
+ log('鏀跺埌绌洪煶棰戞暟鎹抚锛屽彲鑳芥槸缁撴潫鏍囧織', 'warning');
+ if (this.isPlaying && this.streamingContext) {
+ this.streamingContext.endOfStream = true;
+ }
+ }
+ }
+
+ // 棰勫姞杞借В鐮佸櫒
+ async preload() {
+ log('棰勫姞杞絆pus瑙g爜鍣�...', 'info');
+ try {
+ await this.initOpusDecoder();
+ log('Opus瑙g爜鍣ㄩ鍔犺浇鎴愬姛', 'success');
+ } catch (error) {
+ log(`Opus瑙g爜鍣ㄩ鍔犺浇澶辫触: ${error.message}锛屽皢鍦ㄩ渶瑕佹椂閲嶈瘯`, 'warning');
+ }
+ }
+
+ // 鍚姩鎾斁绯荤粺
+ async start() {
+ await this.preload();
+ this.playBufferedAudio();
+ this.startAudioBuffering();
+ }
+
+ // 鑾峰彇闊抽鍖呯粺璁′俊鎭�
+ getAudioStats() {
+ if (!this.streamingContext) {
+ return {
+ pendingDecode: 0,
+ pendingPlay: 0,
+ totalPending: 0
+ };
+ }
+
+ const pendingDecode = this.streamingContext.getPendingDecodeCount();
+ const pendingPlay = this.streamingContext.getPendingPlayCount();
+
+ return {
+ pendingDecode, // 寰呰В鐮佸寘鏁�
+ pendingPlay, // 寰呮挱鏀惧寘鏁�
+ totalPending: pendingDecode + pendingPlay // 鎬诲緟澶勭悊鍖呮暟
+ };
+ }
+
+ // 娓呯┖鎵�鏈夐煶棰戠紦鍐插苟鍋滄鎾斁
+ clearAllAudio() {
+ log('AudioPlayer: 娓呯┖鎵�鏈夐煶棰�', 'info');
+
+ // 娓呯┖鎺ユ敹闃熷垪锛堜娇鐢╟lear鏂规硶淇濇寔瀵硅薄寮曠敤锛�
+ this.queue.clear();
+
+ // 娓呯┖娴佷笂涓嬫枃鐨勬墍鏈夌紦鍐�
+ if (this.streamingContext) {
+ this.streamingContext.clearAllBuffers();
+ }
+
+ log('AudioPlayer: 闊抽宸叉竻绌�', 'success');
+ }
+}
+
+// 鍒涘缓鍗曚緥
+let audioPlayerInstance = null;
+
+export function getAudioPlayer() {
+ if (!audioPlayerInstance) {
+ audioPlayerInstance = new AudioPlayer();
+ }
+ return audioPlayerInstance;
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/js/core/audio/recorder.js b/xiaozhi-esp32-server/main/digital-human/js/core/audio/recorder.js
new file mode 100644
index 0000000..3a32793
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/js/core/audio/recorder.js
@@ -0,0 +1,384 @@
+// Audio recording module
+import { log } from '../../utils/logger.js?v=0205';
+import { initOpusEncoder } from './opus-codec.js?v=0205';
+import { getAudioPlayer } from './player.js?v=0205';
+
+// Audio recorder class
+export class AudioRecorder {
+ constructor() {
+ this.isRecording = false;
+ this.audioContext = null;
+ this.analyser = null;
+ this.audioProcessor = null;
+ this.audioProcessorType = null;
+ this.audioSource = null;
+ this.opusEncoder = null;
+ this.pcmDataBuffer = new Int16Array();
+ this.audioBuffers = [];
+ this.totalAudioSize = 0;
+ this.visualizationRequest = null;
+ this.recordingTimer = null;
+ this.websocket = null;
+ // Callback functions
+ this.onRecordingStart = null;
+ this.onRecordingStop = null;
+ this.onVisualizerUpdate = null;
+ }
+
+ // Set WebSocket instance
+ setWebSocket(ws) {
+ this.websocket = ws;
+ }
+
+ // Get AudioContext instance
+ getAudioContext() {
+ return getAudioPlayer().getAudioContext();
+ }
+
+ // Initialize encoder
+ initEncoder() {
+ if (!this.opusEncoder) {
+ this.opusEncoder = initOpusEncoder();
+ }
+ return this.opusEncoder;
+ }
+
+ // PCM processor code
+ getAudioProcessorCode() {
+ return `
+ class AudioRecorderProcessor extends AudioWorkletProcessor {
+ constructor() {
+ super();
+ this.buffers = [];
+ this.frameSize = 960;
+ this.buffer = new Int16Array(this.frameSize);
+ this.bufferIndex = 0;
+ this.isRecording = false;
+ this.port.onmessage = (event) => {
+ if (event.data.command === 'start') {
+ this.isRecording = true;
+ this.port.postMessage({ type: 'status', status: 'started' });
+ } else if (event.data.command === 'stop') {
+ this.isRecording = false;
+ if (this.bufferIndex > 0) {
+ const finalBuffer = this.buffer.slice(0, this.bufferIndex);
+ this.port.postMessage({ type: 'buffer', buffer: finalBuffer });
+ this.bufferIndex = 0;
+ }
+ this.port.postMessage({ type: 'status', status: 'stopped' });
+ }
+ };
+ }
+ process(inputs, outputs, parameters) {
+ if (!this.isRecording) return true;
+ const input = inputs[0][0];
+ if (!input) return true;
+ for (let i = 0; i < input.length; i++) {
+ if (this.bufferIndex >= this.frameSize) {
+ this.port.postMessage({ type: 'buffer', buffer: this.buffer.slice(0) });
+ this.bufferIndex = 0;
+ }
+ this.buffer[this.bufferIndex++] = Math.max(-32768, Math.min(32767, Math.floor(input[i] * 32767)));
+ }
+ return true;
+ }
+ }
+ registerProcessor('audio-recorder-processor', AudioRecorderProcessor);
+ `;
+ }
+
+ // Create audio processor
+ async createAudioProcessor() {
+ this.audioContext = this.getAudioContext();
+ try {
+ if (this.audioContext.audioWorklet) {
+ const blob = new Blob([this.getAudioProcessorCode()], { type: 'application/javascript' });
+ const url = URL.createObjectURL(blob);
+ await this.audioContext.audioWorklet.addModule(url);
+ URL.revokeObjectURL(url);
+ const audioProcessor = new AudioWorkletNode(this.audioContext, 'audio-recorder-processor');
+ audioProcessor.port.onmessage = (event) => {
+ if (event.data.type === 'buffer') {
+ this.processPCMBuffer(event.data.buffer);
+ }
+ };
+ log('浣跨敤AudioWorklet澶勭悊闊抽', 'success');
+ const silent = this.audioContext.createGain();
+ silent.gain.value = 0;
+ audioProcessor.connect(silent);
+ silent.connect(this.audioContext.destination);
+ return { node: audioProcessor, type: 'worklet' };
+ } else {
+ log('AudioWorklet涓嶅彲鐢紝浣跨敤ScriptProcessorNode浣滀负鍚庡鏂规', 'warning');
+ return this.createScriptProcessor();
+ }
+ } catch (error) {
+ log(`鍒涘缓闊抽澶勭悊鍣ㄥけ璐�: ${error.message}锛屽皾璇曞悗澶囨柟妗坄, 'error');
+ return this.createScriptProcessor();
+ }
+ }
+
+ // Create ScriptProcessor as fallback
+ createScriptProcessor() {
+ try {
+ const frameSize = 4096;
+ const scriptProcessor = this.audioContext.createScriptProcessor(frameSize, 1, 1);
+ scriptProcessor.onaudioprocess = (event) => {
+ if (!this.isRecording) return;
+ const input = event.inputBuffer.getChannelData(0);
+ const buffer = new Int16Array(input.length);
+ for (let i = 0; i < input.length; i++) {
+ buffer[i] = Math.max(-32768, Math.min(32767, Math.floor(input[i] * 32767)));
+ }
+ this.processPCMBuffer(buffer);
+ };
+ const silent = this.audioContext.createGain();
+ silent.gain.value = 0;
+ scriptProcessor.connect(silent);
+ silent.connect(this.audioContext.destination);
+ log('浣跨敤ScriptProcessorNode浣滀负鍚庡鏂规鎴愬姛', 'warning');
+ return { node: scriptProcessor, type: 'processor' };
+ } catch (fallbackError) {
+ log(`鍚庡鏂规涔熷け璐�: ${fallbackError.message}`, 'error');
+ return null;
+ }
+ }
+
+ // Process PCM buffer data
+ processPCMBuffer(buffer) {
+ if (!this.isRecording) return;
+ const newBuffer = new Int16Array(this.pcmDataBuffer.length + buffer.length);
+ newBuffer.set(this.pcmDataBuffer);
+ newBuffer.set(buffer, this.pcmDataBuffer.length);
+ this.pcmDataBuffer = newBuffer;
+ const samplesPerFrame = 960;
+ while (this.pcmDataBuffer.length >= samplesPerFrame) {
+ const frameData = this.pcmDataBuffer.slice(0, samplesPerFrame);
+ this.pcmDataBuffer = this.pcmDataBuffer.slice(samplesPerFrame);
+ this.encodeAndSendOpus(frameData);
+ }
+ }
+
+ // Encode and send Opus data
+ encodeAndSendOpus(pcmData = null) {
+ if (!this.opusEncoder) {
+ log('Opus缂栫爜鍣ㄦ湭鍒濆鍖�', 'error');
+ return;
+ }
+ try {
+ if (pcmData) {
+ const opusData = this.opusEncoder.encode(pcmData);
+ if (opusData && opusData.length > 0) {
+ this.audioBuffers.push(opusData.buffer);
+ this.totalAudioSize += opusData.length;
+ if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
+ try {
+ this.websocket.send(opusData.buffer);
+ } catch (error) {
+ log(`WebSocket鍙戦�侀敊璇�: ${error.message}`, 'error');
+ }
+ }
+ } else {
+ log('Opus缂栫爜澶辫触锛屾湭杩斿洖鏈夋晥鏁版嵁', 'error');
+ }
+ } else {
+ if (this.pcmDataBuffer.length > 0) {
+ const samplesPerFrame = 960;
+ if (this.pcmDataBuffer.length < samplesPerFrame) {
+ const paddedBuffer = new Int16Array(samplesPerFrame);
+ paddedBuffer.set(this.pcmDataBuffer);
+ this.encodeAndSendOpus(paddedBuffer);
+ } else {
+ this.encodeAndSendOpus(this.pcmDataBuffer.slice(0, samplesPerFrame));
+ }
+ this.pcmDataBuffer = new Int16Array(0);
+ }
+ }
+ } catch (error) {
+ log(`Opus缂栫爜閿欒: ${error.message}`, 'error');
+ }
+ }
+
+ // Start recording
+ async start() {
+ if (this.isRecording) return false;
+ try {
+ if (!this.initEncoder()) {
+ log('鏃犳硶寮�濮嬪綍闊�: Opus缂栫爜鍣ㄥ垵濮嬪寲澶辫触', 'error');
+ return false;
+ }
+ log('璇疯嚦灏戝綍鍒�1-2绉掗煶棰戜互纭繚鏀堕泦瓒冲鐨勬暟鎹�', 'info');
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true, sampleRate: 16000, channelCount: 1 } });
+ this.audioContext = this.getAudioContext();
+ if (this.audioContext.state === 'suspended') {
+ await this.audioContext.resume();
+ }
+ const processorResult = await this.createAudioProcessor();
+ if (!processorResult) {
+ log('鏃犳硶鍒涘缓闊抽澶勭悊鍣�', 'error');
+ return false;
+ }
+ this.audioProcessor = processorResult.node;
+ this.audioProcessorType = processorResult.type;
+ this.audioSource = this.audioContext.createMediaStreamSource(stream);
+ this.analyser = this.audioContext.createAnalyser();
+ this.analyser.fftSize = 2048;
+ this.audioSource.connect(this.analyser);
+ this.audioSource.connect(this.audioProcessor);
+ this.pcmDataBuffer = new Int16Array();
+ this.audioBuffers = [];
+ this.totalAudioSize = 0;
+ this.isRecording = true;
+ if (this.audioProcessorType === 'worklet' && this.audioProcessor.port) {
+ this.audioProcessor.port.postMessage({ command: 'start' });
+ }
+ // Send listening start message
+ if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
+ log(`宸插彂閫佸綍闊冲紑濮嬫秷鎭痐, 'info');
+ } else {
+ log('WebSocket鏈繛鎺ワ紝鏃犳硶鍙戦�佸紑濮嬫秷鎭�', 'error');
+ return false;
+ }
+ // Start visualization
+ if (this.onVisualizerUpdate) {
+ const dataArray = new Uint8Array(this.analyser.frequencyBinCount);
+ this.startVisualization(dataArray);
+ }
+ // Immediately notify recording start, update button state
+ if (this.onRecordingStart) {
+ this.onRecordingStart(0);
+ }
+ // Start recording timer
+ let recordingSeconds = 0;
+ this.recordingTimer = setInterval(() => {
+ recordingSeconds += 0.1;
+ if (this.onRecordingStart) {
+ this.onRecordingStart(recordingSeconds);
+ }
+ }, 100);
+ log('宸插紑濮婸CM鐩存帴褰曢煶', 'success');
+ return true;
+ } catch (error) {
+ log(`鐩存帴褰曢煶鍚姩閿欒: ${error.message}`, 'error');
+ this.isRecording = false;
+ return false;
+ }
+ }
+
+ // Start visualization
+ startVisualization(dataArray) {
+ const draw = () => {
+ this.visualizationRequest = requestAnimationFrame(() => draw());
+ if (!this.isRecording) return;
+ this.analyser.getByteFrequencyData(dataArray);
+ if (this.onVisualizerUpdate) {
+ this.onVisualizerUpdate(dataArray);
+ }
+ };
+ draw();
+ }
+
+ // Stop recording
+ stop() {
+ if (!this.isRecording) return false;
+ try {
+ this.isRecording = false;
+ if (this.audioProcessor) {
+ if (this.audioProcessorType === 'worklet' && this.audioProcessor.port) {
+ this.audioProcessor.port.postMessage({ command: 'stop' });
+ }
+ this.audioProcessor.disconnect();
+ this.audioProcessor = null;
+ }
+ if (this.audioSource) {
+ this.audioSource.disconnect();
+ this.audioSource = null;
+ }
+ if (this.visualizationRequest) {
+ cancelAnimationFrame(this.visualizationRequest);
+ this.visualizationRequest = null;
+ }
+ if (this.recordingTimer) {
+ clearInterval(this.recordingTimer);
+ this.recordingTimer = null;
+ }
+ // Encode and send remaining data
+ this.encodeAndSendOpus();
+ // Send end signal
+ if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
+ const emptyOpusFrame = new Uint8Array(0);
+ this.websocket.send(emptyOpusFrame);
+ log('宸插彂閫佸綍闊冲仠姝俊鍙�', 'info');
+ }
+ if (this.onRecordingStop) {
+ this.onRecordingStop();
+ }
+ log('宸插仠姝CM鐩存帴褰曢煶', 'success');
+ return true;
+ } catch (error) {
+ log(`鐩存帴褰曢煶鍋滄閿欒: ${error.message}`, 'error');
+ return false;
+ }
+ }
+
+ // Get analyser
+ getAnalyser() {
+ return this.analyser;
+ }
+}
+
+// Create singleton instance
+let audioRecorderInstance = null;
+
+export function getAudioRecorder() {
+ if (!audioRecorderInstance) {
+ audioRecorderInstance = new AudioRecorder();
+ }
+ return audioRecorderInstance;
+}
+
+/**
+ * Check if microphone is available
+ * @returns {Promise<boolean>} Returns true if available, false if not available
+ */
+export async function checkMicrophoneAvailability() {
+ // Check if browser supports getUserMedia API
+ if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
+ log('娴忚鍣ㄤ笉鏀寔getUserMedia API', 'warning');
+ return false;
+ }
+ try {
+ // Try to access microphone
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true, sampleRate: 16000, channelCount: 1 } });
+ // Immediately stop all tracks to release microphone
+ stream.getTracks().forEach(track => track.stop());
+ log('楹﹀厠椋庡彲鐢ㄦ�ф鏌ユ垚鍔�', 'success');
+ return true;
+ } catch (error) {
+ log(`楹﹀厠椋庝笉鍙敤: ${error.message}`, 'warning');
+ return false;
+ }
+}
+
+/**
+ * Check if it is HTTP non-localhost access
+ * @returns {boolean} Returns true if it is HTTP non-localhost access
+ */
+export function isHttpNonLocalhost() {
+ const protocol = window.location.protocol;
+ const hostname = window.location.hostname;
+ // Check if it is HTTP protocol
+ if (protocol !== 'http:') {
+ return false;
+ }
+ // localhost and 127.0.0.1 can use microphone
+ if (hostname === 'localhost' || hostname === '127.0.0.1') {
+ return false;
+ }
+ // Private IP addresses can also use microphone (browser allows)
+ if (hostname.startsWith('192.168.') || hostname.startsWith('10.') || hostname.startsWith('172.')) {
+ return false;
+ }
+ // Other HTTP access is considered non-localhost
+ return true;
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/js/core/audio/stream-context.js b/xiaozhi-esp32-server/main/digital-human/js/core/audio/stream-context.js
new file mode 100644
index 0000000..db56eba
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/js/core/audio/stream-context.js
@@ -0,0 +1,221 @@
+import BlockingQueue from '../../utils/blocking-queue.js?v=0205';
+import { log } from '../../utils/logger.js?v=0205';
+
+// 闊抽娴佹挱鏀句笂涓嬫枃绫�
+export class StreamingContext {
+ constructor(opusDecoder, audioContext, sampleRate, channels, minAudioDuration) {
+ this.opusDecoder = opusDecoder;
+ this.audioContext = audioContext;
+
+ // 闊抽鍙傛暟
+ this.sampleRate = sampleRate;
+ this.channels = channels;
+ this.minAudioDuration = minAudioDuration;
+
+ // 鍒濆鍖栭槦鍒楀拰鐘舵��
+ this.queue = []; // 宸茶В鐮佺殑PCM闃熷垪銆傛鍦ㄦ挱鏀�
+ this.activeQueue = new BlockingQueue(); // 宸茶В鐮佺殑PCM闃熷垪銆傚噯澶囨挱鏀�
+ this.pendingAudioBufferQueue = []; // 寰呭鐞嗙殑缂撳瓨闃熷垪
+ this.audioBufferQueue = new BlockingQueue(); // 缂撳瓨闃熷垪
+ this.playing = false; // 鏄惁姝e湪鎾斁
+ this.endOfStream = false; // 鏄惁鏀跺埌缁撴潫淇″彿
+ this.source = null; // 褰撳墠闊抽婧�
+ this.totalSamples = 0; // 绱Н鐨勬�绘牱鏈暟
+ this.lastPlayTime = 0; // 涓婃鎾斁鐨勬椂闂存埑
+ this.scheduledEndTime = 0; // 宸茶皟搴﹂煶棰戠殑缁撴潫鏃堕棿
+
+ // 鍒濆鍖栧垎鏋愬櫒鑺傜偣锛堜緵Live2D浣跨敤锛�
+ this.analyser = this.audioContext.createAnalyser();
+ this.analyser.fftSize = 256;
+ }
+
+ // 缂撳瓨闊抽鏁扮粍
+ pushAudioBuffer(item) {
+ this.audioBufferQueue.enqueue(...item);
+ }
+
+ // 鑾峰彇闇�瑕佸鐞嗙紦瀛橀槦鍒楋紝鍗曠嚎绋嬶細鍦╝udioBufferQueue涓�鐩存洿鏂扮殑鐘舵�佷笅涓嶄細鍑虹幇瀹夊叏闂
+ async getPendingAudioBufferQueue() {
+ // 绛夊緟鏁版嵁鍒拌揪骞惰幏鍙�
+ const data = await this.audioBufferQueue.dequeue();
+ // 璧嬪�肩粰寰呭鐞嗛槦鍒�
+ this.pendingAudioBufferQueue = data;
+ }
+
+ // 鑾峰彇姝e湪鎾斁宸茶В鐮佺殑PCM闃熷垪锛屽崟绾跨▼锛氬湪activeQueue涓�鐩存洿鏂扮殑鐘舵�佷笅涓嶄細鍑虹幇瀹夊叏闂
+ async getQueue(minSamples) {
+ const num = minSamples - this.queue.length > 0 ? minSamples - this.queue.length : 1;
+
+ // 绛夊緟鏁版嵁骞惰幏鍙�
+ const tempArray = await this.activeQueue.dequeue(num);
+ this.queue.push(...tempArray);
+ }
+
+ // 灏咺nt16闊抽鏁版嵁杞崲涓篎loat32闊抽鏁版嵁
+ convertInt16ToFloat32(int16Data) {
+ const float32Data = new Float32Array(int16Data.length);
+ for (let i = 0; i < int16Data.length; i++) {
+ // 灏哰-32768,32767]鑼冨洿杞崲涓篬-1,1]锛岀粺涓�浣跨敤32768.0閬垮厤涓嶅绉板け鐪�
+ float32Data[i] = int16Data[i] / 32768.0;
+ }
+ return float32Data;
+ }
+
+ // 鑾峰彇寰呰В鐮佸寘鏁�
+ getPendingDecodeCount() {
+ return this.audioBufferQueue.length + this.pendingAudioBufferQueue.length;
+ }
+
+ // 鑾峰彇寰呮挱鏀炬牱鏈暟锛堣浆鎹负鍖呮暟锛屾瘡鍖�960鏍锋湰锛�
+ getPendingPlayCount() {
+ // 璁$畻宸插湪闃熷垪涓殑鏍锋湰
+ const queuedSamples = this.activeQueue.length + this.queue.length;
+
+ // 璁$畻宸茶皟搴︿絾鏈挱鏀剧殑鏍锋湰锛堝湪Web Audio缂撳啿鍖轰腑锛�
+ let scheduledSamples = 0;
+ if (this.playing && this.scheduledEndTime) {
+ const currentTime = this.audioContext.currentTime;
+ const remainingTime = Math.max(0, this.scheduledEndTime - currentTime);
+ scheduledSamples = Math.floor(remainingTime * this.sampleRate);
+ }
+
+ const totalSamples = queuedSamples + scheduledSamples;
+ return Math.ceil(totalSamples / 960);
+ }
+
+ // 娓呯┖鎵�鏈夐煶棰戠紦鍐�
+ clearAllBuffers() {
+ log('娓呯┖鎵�鏈夐煶棰戠紦鍐�', 'info');
+
+ // 娓呯┖鎵�鏈夐槦鍒楋紙浣跨敤clear鏂规硶淇濇寔瀵硅薄寮曠敤锛�
+ this.audioBufferQueue.clear();
+ this.pendingAudioBufferQueue = [];
+ this.activeQueue.clear();
+ this.queue = [];
+
+ // 鍋滄褰撳墠鎾斁鐨勯煶棰戞簮
+ if (this.source) {
+ try {
+ this.source.stop();
+ this.source.disconnect();
+ } catch (e) {
+ // 蹇界暐宸茬粡鍋滄鐨勯敊璇�
+ }
+ this.source = null;
+ }
+
+ // 閲嶇疆鐘舵��
+ this.playing = false;
+ this.scheduledEndTime = this.audioContext.currentTime;
+ this.totalSamples = 0;
+
+ log('闊抽缂撳啿宸叉竻绌�', 'success');
+ }
+
+ // 鑾峰彇鍒嗘瀽鍣ㄨ妭鐐癸紙渚汱ive2D浣跨敤锛�
+ getAnalyser() {
+ return this.analyser;
+ }
+
+ // 灏哋pus鏁版嵁瑙g爜涓篜CM
+ async decodeOpusFrames() {
+ if (!this.opusDecoder) {
+ log('Opus瑙g爜鍣ㄦ湭鍒濆鍖栵紝鏃犳硶瑙g爜', 'error');
+ return;
+ } else {
+ log('Opus瑙g爜鍣ㄥ惎鍔�', 'info');
+ }
+
+ while (true) {
+ let decodedSamples = [];
+ for (const frame of this.pendingAudioBufferQueue) {
+ try {
+ // 浣跨敤Opus瑙g爜鍣ㄨВ鐮�
+ const frameData = this.opusDecoder.decode(frame);
+ if (frameData && frameData.length > 0) {
+ // 杞崲涓篎loat32
+ const floatData = this.convertInt16ToFloat32(frameData);
+ // 浣跨敤寰幆鏇夸唬灞曞紑杩愮畻绗�
+ for (let i = 0; i < floatData.length; i++) {
+ decodedSamples.push(floatData[i]);
+ }
+ }
+ } catch (error) {
+ log("Opus瑙g爜澶辫触: " + error.message, 'error');
+ }
+ }
+
+ if (decodedSamples.length > 0) {
+ // 浣跨敤寰幆鏇夸唬灞曞紑杩愮畻绗�
+ for (let i = 0; i < decodedSamples.length; i++) {
+ this.activeQueue.enqueue(decodedSamples[i]);
+ }
+ this.totalSamples += decodedSamples.length;
+ } else {
+ log('娌℃湁鎴愬姛瑙g爜鐨勬牱鏈�', 'warning');
+ }
+ await this.getPendingAudioBufferQueue();
+ }
+ }
+
+ // 寮�濮嬫挱鏀鹃煶棰�
+ async startPlaying() {
+ this.scheduledEndTime = this.audioContext.currentTime; // 璺熻釜宸茶皟搴﹂煶棰戠殑缁撴潫鏃堕棿
+
+ while (true) {
+ // 鍒濆缂撳啿锛氱瓑寰呰冻澶熺殑鏍锋湰鍐嶅紑濮嬫挱鏀�
+ const minSamples = this.sampleRate * this.minAudioDuration * 2;
+ if (!this.playing && this.queue.length < minSamples) {
+ await this.getQueue(minSamples);
+ }
+ this.playing = true;
+
+ // 鎸佺画鎾斁闃熷垪涓殑闊抽锛屾瘡娆℃挱鏀句竴涓皬鍧�
+ while (this.playing && this.queue.length > 0) {
+ // 姣忔鎾斁120ms鐨勯煶棰戯紙2涓狾pus鍖咃級
+ const playDuration = 0.12;
+ const targetSamples = Math.floor(this.sampleRate * playDuration);
+ const actualSamples = Math.min(this.queue.length, targetSamples);
+
+ if (actualSamples === 0) break;
+
+ const currentSamples = this.queue.splice(0, actualSamples);
+ const audioBuffer = this.audioContext.createBuffer(this.channels, currentSamples.length, this.sampleRate);
+ audioBuffer.copyToChannel(new Float32Array(currentSamples), 0);
+
+ // 鍒涘缓闊抽婧�
+ this.source = this.audioContext.createBufferSource();
+ this.source.buffer = audioBuffer;
+
+ // 绮剧‘璋冨害鎾斁鏃堕棿
+ const currentTime = this.audioContext.currentTime;
+ const startTime = Math.max(this.scheduledEndTime, currentTime);
+
+ // 杩炴帴鍒板垎鏋愬櫒鍜岃緭鍑�
+ this.source.connect(this.analyser);
+ this.source.connect(this.audioContext.destination);
+
+ log(`璋冨害鎾斁 ${currentSamples.length} 涓牱鏈紝绾� ${(currentSamples.length / this.sampleRate).toFixed(2)} 绉抈, 'debug');
+ this.source.start(startTime);
+
+ // 鏇存柊涓嬩竴涓煶棰戝潡鐨勮皟搴︽椂闂�
+ const duration = audioBuffer.duration;
+ this.scheduledEndTime = startTime + duration;
+ this.lastPlayTime = startTime;
+
+ // 濡傛灉闃熷垪涓暟鎹笉瓒筹紝绛夊緟鏂版暟鎹�
+ if (this.queue.length < targetSamples) {
+ break;
+ }
+ }
+
+ // 绛夊緟鏂版暟鎹�
+ await this.getQueue(minSamples);
+ }
+ }
+}
+
+// 鍒涘缓streamingContext瀹炰緥鐨勫伐鍘傚嚱鏁�
+export function createStreamingContext(opusDecoder, audioContext, sampleRate, channels, minAudioDuration) {
+ return new StreamingContext(opusDecoder, audioContext, sampleRate, channels, minAudioDuration);
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/js/core/mcp/tools.js b/xiaozhi-esp32-server/main/digital-human/js/core/mcp/tools.js
new file mode 100644
index 0000000..7cfa897
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/js/core/mcp/tools.js
@@ -0,0 +1,525 @@
+import { log } from '../../utils/logger.js?v=0205';
+
+// ==========================================
+// MCP 宸ュ叿绠$悊閫昏緫
+// ==========================================
+
+// 鍏ㄥ眬鍙橀噺
+let mcpTools = [];
+let mcpEditingIndex = null;
+let mcpProperties = [];
+let websocket = null; // 灏嗕粠澶栭儴璁剧疆
+
+/**
+ * 璁剧疆 WebSocket 瀹炰緥
+ * @param {WebSocket} ws - WebSocket 杩炴帴瀹炰緥
+ */
+export function setWebSocket(ws) {
+ websocket = ws;
+}
+
+/**
+ * 鍒濆鍖� MCP 宸ュ叿
+ */
+export async function initMcpTools() {
+ // 鍔犺浇榛樿宸ュ叿鏁版嵁
+ const defaultMcpTools = await fetch("js/config/default-mcp-tools.json").then(res => res.json());
+ const savedTools = localStorage.getItem('mcpTools');
+ if (savedTools) {
+ try {
+ const parsedTools = JSON.parse(savedTools);
+ // 鍚堝苟榛樿宸ュ叿鍜岀敤鎴蜂繚瀛樼殑宸ュ叿锛屼繚鐣欑敤鎴疯嚜瀹氫箟鐨勫伐鍏�
+ const defaultToolNames = new Set(defaultMcpTools.map(t => t.name));
+ // 娣诲姞榛樿宸ュ叿涓笉瀛樺湪鐨勬柊宸ュ叿
+ parsedTools.forEach(tool => {
+ if (!defaultToolNames.has(tool.name)) {
+ defaultMcpTools.push(tool);
+ }
+ });
+ mcpTools = defaultMcpTools;
+ } catch (e) {
+ log('鍔犺浇MCP宸ュ叿澶辫触锛屼娇鐢ㄩ粯璁ゅ伐鍏�', 'warning');
+ mcpTools = [...defaultMcpTools];
+ }
+ } else {
+ mcpTools = [...defaultMcpTools];
+ }
+ renderMcpTools();
+ setupMcpEventListeners();
+}
+
+/**
+ * 娓叉煋宸ュ叿鍒楄〃
+ */
+function renderMcpTools() {
+ const container = document.getElementById('mcpToolsContainer');
+ const countSpan = document.getElementById('mcpToolsCount');
+ if (!container) {
+ return; // Container not found, skip rendering
+ }
+ if (countSpan) {
+ countSpan.textContent = `${mcpTools.length} 涓伐鍏穈;
+ }
+ if (mcpTools.length === 0) {
+ container.innerHTML = '<div style="text-align: center; padding: 30px; color: #999;">鏆傛棤宸ュ叿锛岀偣鍑讳笅鏂规寜閽坊鍔犳柊宸ュ叿</div>';
+ return;
+ }
+ container.innerHTML = mcpTools.map((tool, index) => {
+ const paramCount = tool.inputSchema.properties ? Object.keys(tool.inputSchema.properties).length : 0;
+ const requiredCount = tool.inputSchema.required ? tool.inputSchema.required.length : 0;
+ const hasMockResponse = tool.mockResponse && Object.keys(tool.mockResponse).length > 0;
+ return `
+ <div class="mcp-tool-card">
+ <div class="mcp-tool-header">
+ <div class="mcp-tool-name">${tool.name}</div>
+ <div class="mcp-tool-actions">
+ <button class="mcp-edit-btn" onclick="window.mcpModule.editMcpTool(${index})">
+ 鉁忥笍 缂栬緫
+ </button>
+ <button class="mcp-delete-btn" onclick="window.mcpModule.deleteMcpTool(${index})">
+ 馃棏锔� 鍒犻櫎
+ </button>
+ </div>
+ </div>
+ <div class="mcp-tool-description">${tool.description}</div>
+ <div class="mcp-tool-info">
+ <div class="mcp-tool-info-row">
+ <span class="mcp-tool-info-label">鍙傛暟鏁伴噺:</span>
+ <span class="mcp-tool-info-value">${paramCount} 涓� ${requiredCount > 0 ? `(${requiredCount} 涓繀濉�)` : ''}</span>
+ </div>
+ <div class="mcp-tool-info-row">
+ <span class="mcp-tool-info-label">妯℃嫙杩斿洖:</span>
+ <span class="mcp-tool-info-value">${hasMockResponse ? '鉁� 宸查厤缃�: ' + JSON.stringify(tool.mockResponse) : '鈿� 浣跨敤榛樿'}</span>
+ </div>
+ </div>
+ </div>
+ `;
+ }).join('');
+}
+
+/**
+ * 娓叉煋鍙傛暟鍒楄〃
+ */
+function renderMcpProperties() {
+ const container = document.getElementById('mcpPropertiesContainer');
+ const emptyState = document.getElementById('mcpEmptyState');
+ if (!container) {
+ return; // Container not found, skip rendering
+ }
+ if (mcpProperties.length === 0) {
+ if (emptyState) {
+ emptyState.style.display = 'block';
+ }
+ container.innerHTML = '';
+ return;
+ }
+ if (emptyState) {
+ emptyState.style.display = 'none';
+ }
+ container.innerHTML = mcpProperties.map((prop, index) => `
+ <div class="mcp-property-card" onclick="window.mcpModule.editMcpProperty(${index})">
+ <div class="mcp-property-row-label">
+ <span class="mcp-property-label">鍙傛暟鍚嶇О</span>
+ <span class="mcp-property-value">${prop.name}${prop.required ? ' <span class="mcp-property-required-badge">[蹇呭~]</span>' : ''}</span>
+ </div>
+ <div class="mcp-property-row-label">
+ <span class="mcp-property-label">鏁版嵁绫诲瀷</span>
+ <span class="mcp-property-value">${getTypeLabel(prop.type)}</span>
+ </div>
+ <div class="mcp-property-row-label">
+ <span class="mcp-property-label">鎻忚堪</span>
+ <span class="mcp-property-value">${prop.description || '-'}</span>
+ </div>
+ <div class="mcp-property-row-action">
+ <button class="mcp-property-delete-btn" onclick="event.stopPropagation(); window.mcpModule.deleteMcpProperty(${index})">鍒犻櫎</button>
+ </div>
+ </div>
+ `).join('');
+}
+
+/**
+ * 鑾峰彇鏁版嵁绫诲瀷鏍囩
+ */
+function getTypeLabel(type) {
+ const typeMap = {
+ 'string': '瀛楃涓�',
+ 'integer': '鏁存暟',
+ 'number': '鏁板瓧',
+ 'boolean': '甯冨皵鍊�',
+ 'array': '鏁扮粍',
+ 'object': '瀵硅薄'
+ };
+ return typeMap[type] || type;
+}
+
+/**
+ * 娣诲姞鍙傛暟 - 鎵撳紑鍙傛暟缂栬緫妯℃�佹
+ */
+function addMcpProperty() {
+ openPropertyModal();
+}
+
+/**
+ * 缂栬緫鍙傛暟 - 鎵撳紑鍙傛暟缂栬緫妯℃�佹
+ */
+function editMcpProperty(index) {
+ openPropertyModal(index);
+}
+
+/**
+ * 鎵撳紑鍙傛暟缂栬緫妯℃�佹
+ */
+function openPropertyModal(index = null) {
+ const form = document.getElementById('mcpPropertyForm');
+ const title = document.getElementById('mcpPropertyModalTitle');
+ document.getElementById('mcpPropertyIndex').value = index !== null ? index : -1;
+
+ if (index !== null) {
+ const prop = mcpProperties[index];
+ title.textContent = '缂栬緫鍙傛暟';
+ document.getElementById('mcpPropertyName').value = prop.name;
+ document.getElementById('mcpPropertyType').value = prop.type || 'string';
+ document.getElementById('mcpPropertyMinimum').value = prop.minimum !== undefined ? prop.minimum : '';
+ document.getElementById('mcpPropertyMaximum').value = prop.maximum !== undefined ? prop.maximum : '';
+ document.getElementById('mcpPropertyDescription').value = prop.description || '';
+ document.getElementById('mcpPropertyRequired').checked = prop.required || false;
+ } else {
+ title.textContent = '娣诲姞鍙傛暟';
+ form.reset();
+ document.getElementById('mcpPropertyName').value = `param_${mcpProperties.length + 1}`;
+ document.getElementById('mcpPropertyType').value = 'string';
+ document.getElementById('mcpPropertyMinimum').value = '';
+ document.getElementById('mcpPropertyMaximum').value = '';
+ document.getElementById('mcpPropertyDescription').value = '';
+ document.getElementById('mcpPropertyRequired').checked = false;
+ }
+
+ updatePropertyRangeVisibility();
+ document.getElementById('mcpPropertyModal').style.display = 'flex';
+}
+
+/**
+ * 鍏抽棴鍙傛暟缂栬緫妯℃�佹
+ */
+function closePropertyModal() {
+ document.getElementById('mcpPropertyModal').style.display = 'none';
+}
+
+/**
+ * 鏇存柊鏁板�艰寖鍥磋緭鍏ユ鐨勫彲瑙佹��
+ */
+function updatePropertyRangeVisibility() {
+ const type = document.getElementById('mcpPropertyType').value;
+ const rangeGroup = document.getElementById('mcpPropertyRangeGroup');
+ if (type === 'integer' || type === 'number') {
+ rangeGroup.style.display = 'block';
+ } else {
+ rangeGroup.style.display = 'none';
+ }
+}
+
+/**
+ * 澶勭悊鍙傛暟琛ㄥ崟鎻愪氦
+ */
+function handlePropertySubmit(e) {
+ e.preventDefault();
+ const index = parseInt(document.getElementById('mcpPropertyIndex').value);
+ const name = document.getElementById('mcpPropertyName').value.trim();
+ const type = document.getElementById('mcpPropertyType').value;
+ const minimum = document.getElementById('mcpPropertyMinimum').value;
+ const maximum = document.getElementById('mcpPropertyMaximum').value;
+ const description = document.getElementById('mcpPropertyDescription').value.trim();
+ const required = document.getElementById('mcpPropertyRequired').checked;
+
+ // 妫�鏌ュ悕绉伴噸澶�
+ const isDuplicate = mcpProperties.some((p, i) => i !== index && p.name === name);
+ if (isDuplicate) {
+ alert('鍙傛暟鍚嶇О宸插瓨鍦紝璇蜂娇鐢ㄤ笉鍚岀殑鍚嶇О');
+ return;
+ }
+
+ const propData = {
+ name,
+ type,
+ description,
+ required
+ };
+
+ // 鏁板�肩被鍨嬫坊鍔犺寖鍥撮檺鍒�
+ if (type === 'integer' || type === 'number') {
+ if (minimum !== '') {
+ propData.minimum = parseFloat(minimum);
+ }
+ if (maximum !== '') {
+ propData.maximum = parseFloat(maximum);
+ }
+ }
+
+ if (index >= 0) {
+ mcpProperties[index] = propData;
+ } else {
+ mcpProperties.push(propData);
+ }
+
+ renderMcpProperties();
+ closePropertyModal();
+}
+
+/**
+ * 鍒犻櫎鍙傛暟
+ */
+function deleteMcpProperty(index) {
+ mcpProperties.splice(index, 1);
+ renderMcpProperties();
+}
+
+/**
+ * 璁剧疆浜嬩欢鐩戝惉
+ */
+function setupMcpEventListeners() {
+ const panel = document.getElementById('mcpToolsPanel');
+ const addBtn = document.getElementById('addMcpToolBtn');
+ const modal = document.getElementById('mcpToolModal');
+ const closeBtn = document.getElementById('closeMcpModalBtn');
+ const cancelBtn = document.getElementById('cancelMcpBtn');
+ const form = document.getElementById('mcpToolForm');
+ const addPropertyBtn = document.getElementById('addMcpPropertyBtn');
+
+ // 鍙傛暟缂栬緫妯℃�佹鐩稿叧鍏冪礌
+ const propertyModal = document.getElementById('mcpPropertyModal');
+ const closePropertyBtn = document.getElementById('closeMcpPropertyModalBtn');
+ const cancelPropertyBtn = document.getElementById('cancelMcpPropertyBtn');
+ const propertyForm = document.getElementById('mcpPropertyForm');
+ const propertyTypeSelect = document.getElementById('mcpPropertyType');
+
+ // Return early if required elements don't exist (e.g., in test environment)
+ if (!panel || !addBtn || !modal || !closeBtn || !cancelBtn || !form || !addPropertyBtn) {
+ return;
+ }
+ addBtn.addEventListener('click', () => openMcpModal());
+ closeBtn.addEventListener('click', closeMcpModal);
+ cancelBtn.addEventListener('click', closeMcpModal);
+ addPropertyBtn.addEventListener('click', addMcpProperty);
+ form.addEventListener('submit', handleMcpSubmit);
+
+ // 鍙傛暟缂栬緫妯℃�佹浜嬩欢
+ if (propertyModal && closePropertyBtn && cancelPropertyBtn && propertyForm && propertyTypeSelect) {
+ closePropertyBtn.addEventListener('click', closePropertyModal);
+ cancelPropertyBtn.addEventListener('click', closePropertyModal);
+ propertyForm.addEventListener('submit', handlePropertySubmit);
+ propertyTypeSelect.addEventListener('change', updatePropertyRangeVisibility);
+ }
+}
+
+/**
+ * 鎵撳紑妯℃�佹
+ */
+function openMcpModal(index = null) {
+ const isConnected = websocket && websocket.readyState === WebSocket.OPEN;
+ if (isConnected) {
+ alert('WebSocket 宸茶繛鎺ワ紝鏃犳硶缂栬緫宸ュ叿');
+ return;
+ }
+ mcpEditingIndex = index;
+ const errorContainer = document.getElementById('mcpErrorContainer');
+ errorContainer.innerHTML = '';
+ if (index !== null) {
+ document.getElementById('mcpModalTitle').textContent = '缂栬緫宸ュ叿';
+ const tool = mcpTools[index];
+ document.getElementById('mcpToolName').value = tool.name;
+ document.getElementById('mcpToolDescription').value = tool.description;
+ document.getElementById('mcpMockResponse').value = tool.mockResponse ? JSON.stringify(tool.mockResponse, null, 2) : '';
+ mcpProperties = [];
+ const schema = tool.inputSchema;
+ if (schema.properties) {
+ Object.keys(schema.properties).forEach(key => {
+ const prop = schema.properties[key];
+ mcpProperties.push({
+ name: key,
+ type: prop.type || 'string',
+ minimum: prop.minimum,
+ maximum: prop.maximum,
+ description: prop.description || '',
+ required: schema.required && schema.required.includes(key)
+ });
+ });
+ }
+ } else {
+ document.getElementById('mcpModalTitle').textContent = '娣诲姞宸ュ叿';
+ document.getElementById('mcpToolForm').reset();
+ mcpProperties = [];
+ }
+ renderMcpProperties();
+ document.getElementById('mcpToolModal').style.display = 'flex';
+}
+
+/**
+ * 鍏抽棴妯℃�佹
+ */
+function closeMcpModal() {
+ document.getElementById('mcpToolModal').style.display = 'none';
+ mcpEditingIndex = null;
+ document.getElementById('mcpToolForm').reset();
+ mcpProperties = [];
+ document.getElementById('mcpErrorContainer').innerHTML = '';
+}
+
+/**
+ * 澶勭悊琛ㄥ崟鎻愪氦
+ */
+function handleMcpSubmit(e) {
+ e.preventDefault();
+ const errorContainer = document.getElementById('mcpErrorContainer');
+ errorContainer.innerHTML = '';
+ const name = document.getElementById('mcpToolName').value.trim();
+ const description = document.getElementById('mcpToolDescription').value.trim();
+ const mockResponseText = document.getElementById('mcpMockResponse').value.trim();
+ // 妫�鏌ュ悕绉伴噸澶�
+ const isDuplicate = mcpTools.some((tool, index) => tool.name === name && index !== mcpEditingIndex);
+ if (isDuplicate) {
+ showMcpError('宸ュ叿鍚嶇О宸插瓨鍦紝璇蜂娇鐢ㄤ笉鍚岀殑鍚嶇О');
+ return;
+ }
+ // 瑙f瀽妯℃嫙杩斿洖缁撴灉
+ let mockResponse = null;
+ if (mockResponseText) {
+ try {
+ mockResponse = JSON.parse(mockResponseText);
+ } catch (e) {
+ showMcpError('妯℃嫙杩斿洖缁撴灉涓嶆槸鏈夋晥鐨� JSON 鏍煎紡: ' + e.message);
+ return;
+ }
+ }
+ // 鏋勫缓 inputSchema
+ const inputSchema = { type: "object", properties: {}, required: [] };
+ mcpProperties.forEach(prop => {
+ const propSchema = { type: prop.type };
+ if (prop.description) {
+ propSchema.description = prop.description;
+ }
+ if ((prop.type === 'integer' || prop.type === 'number')) {
+ if (prop.minimum !== undefined && prop.minimum !== '') {
+ propSchema.minimum = prop.minimum;
+ }
+ if (prop.maximum !== undefined && prop.maximum !== '') {
+ propSchema.maximum = prop.maximum;
+ }
+ }
+ inputSchema.properties[prop.name] = propSchema;
+ if (prop.required) {
+ inputSchema.required.push(prop.name);
+ }
+ });
+ if (inputSchema.required.length === 0) {
+ delete inputSchema.required;
+ }
+ const tool = { name, description, inputSchema, mockResponse };
+ if (mcpEditingIndex !== null) {
+ mcpTools[mcpEditingIndex] = tool;
+ log(`宸叉洿鏂板伐鍏�: ${name}`, 'success');
+ } else {
+ mcpTools.push(tool);
+ log(`宸叉坊鍔犲伐鍏�: ${name}`, 'success');
+ }
+ saveMcpTools();
+ renderMcpTools();
+ closeMcpModal();
+}
+
+/**
+ * 鏄剧ず閿欒
+ */
+function showMcpError(message) {
+ const errorContainer = document.getElementById('mcpErrorContainer');
+ errorContainer.innerHTML = `<div class="mcp-error">${message}</div>`;
+}
+
+/**
+ * 缂栬緫宸ュ叿
+ */
+function editMcpTool(index) {
+ openMcpModal(index);
+}
+
+/**
+ * 鍒犻櫎宸ュ叿
+ */
+function deleteMcpTool(index) {
+ const isConnected = websocket && websocket.readyState === WebSocket.OPEN;
+ if (isConnected) {
+ alert('WebSocket 宸茶繛鎺ワ紝鏃犳硶缂栬緫宸ュ叿');
+ return;
+ }
+ if (confirm(`纭畾瑕佸垹闄ゅ伐鍏� "${mcpTools[index].name}" 鍚楋紵`)) {
+ const toolName = mcpTools[index].name;
+ mcpTools.splice(index, 1);
+ saveMcpTools();
+ renderMcpTools();
+ log(`宸插垹闄ゅ伐鍏�: ${toolName}`, 'info');
+ }
+}
+
+/**
+ * 淇濆瓨宸ュ叿
+ */
+function saveMcpTools() {
+ localStorage.setItem('mcpTools', JSON.stringify(mcpTools));
+}
+
+/**
+ * 鑾峰彇宸ュ叿鍒楄〃
+ */
+export function getMcpTools() {
+ return mcpTools.map(tool => ({ name: tool.name, description: tool.description, inputSchema: tool.inputSchema }));
+}
+
+/**
+ * 鎵ц宸ュ叿璋冪敤
+ */
+export async function executeMcpTool(toolName, toolArgs) {
+ const tool = mcpTools.find(t => t.name === toolName);
+ if (!tool) {
+ log(`鏈壘鍒板伐鍏�: ${toolName}`, 'error');
+ return { success: false, error: `鏈煡宸ュ叿: ${toolName}` };
+ }
+
+ // 澶勭悊鎷嶇収宸ュ叿
+ if (toolName === 'self_camera_take_photo') {
+ if (typeof window.takePhoto === 'function') {
+ const question = toolArgs && toolArgs.question ? toolArgs.question : '鎻忚堪涓�涓嬬湅鍒扮殑鐗╁搧';
+ log(`姝e湪鎵ц鎷嶇収: ${question}`, 'info');
+ const result = await window.takePhoto(question);
+ return result;
+ } else {
+ log('鎷嶇収鍔熻兘涓嶅彲鐢�', 'warning');
+ return { success: false, error: '鎽勫儚澶存湭鍚姩鎴栦笉鏀寔鎷嶇収鍔熻兘' };
+ }
+ }
+
+ // 濡傛灉鏈夋ā鎷熻繑鍥炵粨鏋滐紝浣跨敤瀹�
+ if (tool.mockResponse) {
+ // 鏇挎崲妯℃澘鍙橀噺
+ let responseStr = JSON.stringify(tool.mockResponse);
+ // 鏇挎崲 ${paramName} 鏍煎紡鐨勫彉閲�
+ if (toolArgs) {
+ Object.keys(toolArgs).forEach(key => {
+ const regex = new RegExp(`\\$\\{${key}\\}`, 'g');
+ responseStr = responseStr.replace(regex, toolArgs[key]);
+ });
+ }
+ try {
+ const response = JSON.parse(responseStr);
+ log(`宸ュ叿 ${toolName} 鎵ц鎴愬姛锛岃繑鍥炴ā鎷熺粨鏋�: ${responseStr}`, 'success');
+ return response;
+ } catch (e) {
+ log(`瑙f瀽妯℃嫙杩斿洖缁撴灉澶辫触: ${e.message}`, 'error');
+ return tool.mockResponse;
+ }
+ }
+ // 娌℃湁妯℃嫙杩斿洖缁撴灉锛岃繑鍥為粯璁ゆ垚鍔熸秷鎭�
+ log(`宸ュ叿 ${toolName} 鎵ц鎴愬姛锛岃繑鍥為粯璁ょ粨鏋渀, 'success');
+ return { success: true, message: `宸ュ叿 ${toolName} 鎵ц鎴愬姛`, tool: toolName, arguments: toolArgs };
+}
+
+// 鏆撮湶鍏ㄥ眬鏂规硶渚� HTML 鍐呰仈浜嬩欢璋冪敤
+window.mcpModule = { addMcpProperty, editMcpProperty, deleteMcpProperty, editMcpTool, deleteMcpTool };
diff --git a/xiaozhi-esp32-server/main/digital-human/js/core/network/ota-connector.js b/xiaozhi-esp32-server/main/digital-human/js/core/network/ota-connector.js
new file mode 100644
index 0000000..9780f87
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/js/core/network/ota-connector.js
@@ -0,0 +1,109 @@
+import { log } from '../../utils/logger.js?v=0205';
+
+// WebSocket 杩炴帴
+export async function webSocketConnect(otaUrl, config) {
+
+ if (!validateConfig(config)) {
+ return;
+ }
+
+ // 鍙戦�丱TA璇锋眰骞惰幏鍙栬繑鍥炵殑websocket淇℃伅
+ const otaResult = await sendOTA(otaUrl, config);
+ if (!otaResult) {
+ log('鏃犳硶浠嶰TA鏈嶅姟鍣ㄨ幏鍙栦俊鎭�', 'error');
+ return;
+ }
+
+ // 浠嶰TA鍝嶅簲涓彁鍙杦ebsocket淇℃伅
+ const { websocket } = otaResult;
+ if (!websocket || !websocket.url) {
+ log('OTA鍝嶅簲涓己灏憌ebsocket淇℃伅', 'error');
+ return;
+ }
+
+ // 浣跨敤OTA杩斿洖鐨剋ebsocket URL
+ let connUrl = new URL(websocket.url);
+
+ // 娣诲姞token鍙傛暟锛堜粠OTA鍝嶅簲涓幏鍙栵級
+ if (websocket.token) {
+ if (websocket.token.startsWith("Bearer ")) {
+ connUrl.searchParams.append('authorization', websocket.token);
+ } else {
+ connUrl.searchParams.append('authorization', 'Bearer ' + websocket.token);
+ }
+ }
+
+ // 娣诲姞璁よ瘉鍙傛暟锛堜繚鎸佸師鏈夐�昏緫锛�
+ connUrl.searchParams.append('device-id', config.deviceId);
+ connUrl.searchParams.append('client-id', config.clientId);
+
+ const wsurl = connUrl.toString()
+
+ log(`姝e湪杩炴帴: ${wsurl}`, 'info');
+
+ if (wsurl) {
+ document.getElementById('serverUrl').value = wsurl;
+ }
+
+ return new WebSocket(connUrl.toString());
+}
+
+// 楠岃瘉閰嶇疆
+function validateConfig(config) {
+ if (!config.deviceMac) {
+ log('璁惧MAC鍦板潃涓嶈兘涓虹┖', 'error');
+ return false;
+ }
+ if (!config.clientId) {
+ log('瀹㈡埛绔疘D涓嶈兘涓虹┖', 'error');
+ return false;
+ }
+ return true;
+}
+
+// OTA鍙戦�佽姹傦紝楠岃瘉鐘舵�侊紝骞惰繑鍥炲搷搴旀暟鎹�
+async function sendOTA(otaUrl, config) {
+ try {
+ const res = await fetch(otaUrl, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Device-Id': config.deviceId,
+ 'Client-Id': config.clientId
+ },
+ body: JSON.stringify({
+ version: 0,
+ uuid: '',
+ application: {
+ name: 'xiaozhi-web-test',
+ version: '1.0.0',
+ compile_time: '2025-04-16 10:00:00',
+ idf_version: '4.4.3',
+ elf_sha256: '1234567890abcdef1234567890abcdef1234567890abcdef'
+ },
+ ota: { label: 'xiaozhi-web-test' },
+ board: {
+ type: config.deviceName,
+ ssid: 'xiaozhi-web-test',
+ rssi: 0,
+ channel: 0,
+ ip: '192.168.1.1',
+ mac: config.deviceMac
+ },
+ flash_size: 0,
+ minimum_free_heap_size: 0,
+ mac_address: config.deviceMac,
+ chip_model_name: '',
+ chip_info: { model: 0, cores: 0, revision: 0, features: 0 },
+ partition_table: [{ label: '', type: 0, subtype: 0, address: 0, size: 0 }]
+ })
+ });
+
+ if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
+
+ const result = await res.json();
+ return result; // 杩斿洖瀹屾暣鐨勫搷搴旀暟鎹�
+ } catch (err) {
+ return null; // 澶辫触杩斿洖null
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/js/core/network/wakeword-bridge.js b/xiaozhi-esp32-server/main/digital-human/js/core/network/wakeword-bridge.js
new file mode 100644
index 0000000..ea4087a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/js/core/network/wakeword-bridge.js
@@ -0,0 +1,225 @@
+import { uiController } from '../../ui/controller.js?v=0205';
+import { log } from '../../utils/logger.js?v=0205';
+
+let wakewordSocket = null;
+let reconnectTimer = null;
+let reconnectAttempts = 0;
+let shouldReconnect = true;
+let wakewordRequestSeq = 0;
+let onNextBridgeConnectedCallback = null;
+
+const pendingWakewordRequests = new Map();
+
+export function startWakewordBridgeListener() {
+ if (wakewordSocket) {
+ return wakewordSocket;
+ }
+
+ shouldReconnect = true;
+ log('姝e湪杩炴帴鏈湴鍞ら啋浜嬩欢妗�...', 'info');
+ tryConnect();
+ return wakewordSocket;
+}
+
+function tryConnect() {
+ const bridgeUrl = buildWakewordBridgeUrl();
+
+ try {
+ wakewordSocket = new WebSocket(bridgeUrl);
+ wakewordSocket.onopen = () => {
+ reconnectAttempts = 0;
+ log(`鏈湴鍞ら啋浜嬩欢妗ュ凡杩炴帴: ${bridgeUrl}`, 'success');
+ // 杩炴帴鎴愬姛鍚庤嚜鍔ㄤ繚瀛樺湴鍧�锛屽埛鏂板悗浠嶈兘璁颁綇
+ localStorage.setItem('xz_tester_wakewordWsUrl', bridgeUrl);
+ const urlInput = document.getElementById('wakewordWsUrl');
+ if (urlInput) urlInput.value = bridgeUrl;
+ };
+
+ wakewordSocket.onerror = () => {
+ log(`鏈湴鍞ら啋浜嬩欢妗ヨ繛鎺ュけ璐�: ${bridgeUrl}`, 'error');
+ };
+
+ wakewordSocket.onmessage = async (event) => {
+ try {
+ const message = parseWakewordBridgeMessage(event.data);
+ if (message.requestId && pendingWakewordRequests.has(message.requestId)) {
+ settleWakewordRequest(message);
+ return;
+ }
+
+ if (message.success === false) {
+ log(`鏈湴鍞ら啋浜嬩欢妗ヨ繑鍥為敊璇�: ${message.error || '鏈煡閿欒'}`, 'error');
+ return;
+ }
+
+ if (message.type === 'bridge_connected') {
+ log('鏈湴鍞ら啋鐩戝惉宸插氨缁�', 'info');
+ if (onNextBridgeConnectedCallback) {
+ const cb = onNextBridgeConnectedCallback;
+ onNextBridgeConnectedCallback = null;
+ cb(message);
+ }
+ return;
+ }
+
+ if (message.type === 'service_ready') {
+ log('鏈湴鍞ら啋鏈嶅姟宸插惎鍔�', 'info');
+ return;
+ }
+
+ if (message.type === 'wakeword_config') {
+ uiController.applyWakewordConfig(message.payload || {});
+ log('宸插悓姝ユ湰鍦板敜閱掕瘝閰嶇疆', 'info');
+ return;
+ }
+
+ if (message.type === 'service_stopping') {
+ log('鏈湴鍞ら啋鏈嶅姟姝e湪鍋滄', 'warning');
+ return;
+ }
+
+ if (message.type === 'wake_word_detected') {
+ const wakeWord = message.payload?.wake_word || '鍞ら啋璇�';
+ log(`妫�娴嬪埌鏈湴鍞ら啋浜嬩欢: ${wakeWord}`, 'info');
+ await uiController.triggerWakewordDial(wakeWord);
+ }
+ } catch (error) {
+ log(`瑙f瀽鏈湴鍞ら啋浜嬩欢澶辫触: ${error.message}`, 'error');
+ }
+ };
+
+ wakewordSocket.onclose = () => {
+ if (wakewordSocket) {
+ wakewordSocket = null;
+ }
+
+ rejectAllWakewordRequests('鏈湴鍞ら啋浜嬩欢妗ュ凡鏂紑');
+
+ if (!shouldReconnect) {
+ return;
+ }
+
+ if (reconnectTimer) {
+ return;
+ }
+
+ reconnectAttempts += 1;
+ const delay = Math.min(1000 * reconnectAttempts, 5000);
+ log(`鏈湴鍞ら啋浜嬩欢妗ュ皢鍦� ${delay}ms 鍚庨噸杩�: ${bridgeUrl}`, 'warning');
+ reconnectTimer = window.setTimeout(() => {
+ reconnectTimer = null;
+ tryConnect();
+ }, delay);
+ };
+
+ return wakewordSocket;
+ } catch (error) {
+ log(`鍚姩鏈湴鍞ら啋鐩戝惉澶辫触: ${error.message}`, 'error');
+ return null;
+ }
+}
+
+export function stopWakewordBridgeListener() {
+ shouldReconnect = false;
+
+ if (reconnectTimer) {
+ window.clearTimeout(reconnectTimer);
+ reconnectTimer = null;
+ }
+
+ if (!wakewordSocket) {
+ return;
+ }
+
+ wakewordSocket.onclose = null;
+ wakewordSocket.close();
+ wakewordSocket = null;
+}
+
+export function sendWakewordBridgeMessage(type, payload = {}, requestId = null) {
+ if (!wakewordSocket || wakewordSocket.readyState !== WebSocket.OPEN) {
+ log('鏈湴鍞ら啋浜嬩欢妗ユ湭杩炴帴锛屾棤娉曞彂閫佹秷鎭�', 'warning');
+ return false;
+ }
+
+ wakewordSocket.send(JSON.stringify({
+ type,
+ requestId,
+ payload,
+ }));
+ return true;
+}
+
+export function requestWakewordBridge(type, payload = {}, timeout = 5000) {
+ const requestId = `wakeword-${Date.now()}-${++wakewordRequestSeq}`;
+
+ return new Promise((resolve, reject) => {
+ const timer = window.setTimeout(() => {
+ pendingWakewordRequests.delete(requestId);
+ reject(new Error('鏈湴鍞ら啋鏈嶅姟鍝嶅簲瓒呮椂'));
+ }, timeout);
+
+ pendingWakewordRequests.set(requestId, { resolve, reject, timer });
+
+ if (!sendWakewordBridgeMessage(type, payload, requestId)) {
+ window.clearTimeout(timer);
+ pendingWakewordRequests.delete(requestId);
+ reject(new Error('鏈湴鍞ら啋浜嬩欢妗ユ湭杩炴帴'));
+ }
+ });
+}
+
+export function getWakewordBridgeUrl() {
+ if (wakewordSocket && wakewordSocket.url) {
+ return wakewordSocket.url;
+ }
+ return buildWakewordBridgeUrl();
+}
+
+export function onNextBridgeConnected(callback) {
+ onNextBridgeConnectedCallback = callback;
+}
+
+function buildWakewordBridgeUrl() {
+ const configured = localStorage.getItem('xz_tester_wakewordWsUrl');
+ if (configured && configured.trim()) {
+ return configured.trim();
+ }
+ return 'ws://127.0.0.1:8006/wakeword-ws';
+}
+
+function parseWakewordBridgeMessage(rawData) {
+ const message = JSON.parse(rawData);
+ return {
+ type: message.type || '',
+ requestId: message.requestId || null,
+ success: message.success !== false,
+ payload: message.payload || {},
+ error: message.error || null,
+ };
+}
+
+function settleWakewordRequest(message) {
+ const pendingRequest = pendingWakewordRequests.get(message.requestId);
+ if (!pendingRequest) {
+ return;
+ }
+
+ window.clearTimeout(pendingRequest.timer);
+ pendingWakewordRequests.delete(message.requestId);
+
+ if (message.success === false) {
+ pendingRequest.reject(new Error(message.error || '鏈湴鍞ら啋鏈嶅姟杩斿洖澶辫触'));
+ return;
+ }
+
+ pendingRequest.resolve(message);
+}
+
+function rejectAllWakewordRequests(errorMessage) {
+ pendingWakewordRequests.forEach((pendingRequest) => {
+ window.clearTimeout(pendingRequest.timer);
+ pendingRequest.reject(new Error(errorMessage));
+ });
+ pendingWakewordRequests.clear();
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/js/core/network/websocket.js b/xiaozhi-esp32-server/main/digital-human/js/core/network/websocket.js
new file mode 100644
index 0000000..ee7238c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/js/core/network/websocket.js
@@ -0,0 +1,591 @@
+// WebSocket娑堟伅澶勭悊妯″潡
+import { getConfig, saveConnectionUrls } from '../../config/manager.js?v=0205';
+import { uiController } from '../../ui/controller.js?v=0205';
+import { log } from '../../utils/logger.js?v=0205';
+import { getAudioPlayer } from '../audio/player.js?v=0205';
+import { getAudioRecorder } from '../audio/recorder.js?v=0205';
+import { executeMcpTool, getMcpTools, setWebSocket as setMcpWebSocket } from '../mcp/tools.js?v=0205';
+import { webSocketConnect } from './ota-connector.js?v=0205';
+
+// WebSocket澶勭悊鍣ㄧ被
+export class WebSocketHandler {
+ constructor() {
+ this.websocket = null;
+ this.onConnectionStateChange = null;
+ this.onRecordButtonStateChange = null;
+ this.onSessionStateChange = null;
+ this.onSessionEmotionChange = null;
+ this.onChatMessage = null; // 鏂板锛氳亰澶╂秷鎭洖璋�
+ this.currentSessionId = null;
+ this.isRemoteSpeaking = false;
+ }
+
+ // 鍙戦�乭ello鎻℃墜娑堟伅
+ async sendHelloMessage() {
+ if (!this.websocket || this.websocket.readyState !== WebSocket.OPEN) return false;
+
+ try {
+ const config = getConfig();
+
+ const helloMessage = {
+ type: 'hello',
+ device_id: config.deviceId,
+ device_name: config.deviceName,
+ device_mac: config.deviceMac,
+ token: config.token,
+ features: {
+ mcp: true,
+ emoji: config.emojiEnabled
+ }
+ };
+
+ log('鍙戦�乭ello鎻℃墜娑堟伅', 'info');
+ this.websocket.send(JSON.stringify(helloMessage));
+
+ return new Promise(resolve => {
+ const timeout = setTimeout(() => {
+ log('绛夊緟hello鍝嶅簲瓒呮椂', 'error');
+ log('鎻愮ず: 璇峰皾璇曠偣鍑�"娴嬭瘯璁よ瘉"鎸夐挳杩涜杩炴帴鎺掓煡', 'info');
+ resolve(false);
+ }, 5000);
+
+ const onMessageHandler = (event) => {
+ try {
+ const response = JSON.parse(event.data);
+ if (response.type === 'hello' && response.session_id) {
+ log(`鏈嶅姟鍣ㄦ彙鎵嬫垚鍔燂紝浼氳瘽ID: ${response.session_id}`, 'success');
+ clearTimeout(timeout);
+ this.websocket.removeEventListener('message', onMessageHandler);
+ resolve(true);
+ }
+ } catch (e) {
+ // 蹇界暐闈濲SON娑堟伅
+ }
+ };
+
+ this.websocket.addEventListener('message', onMessageHandler);
+ });
+ } catch (error) {
+ log(`鍙戦�乭ello娑堟伅閿欒: ${error.message}`, 'error');
+ return false;
+ }
+ }
+
+ _sendWakeupMessages(sessionId) {
+ if (!this.websocket || this.websocket.readyState !== WebSocket.OPEN) return;
+
+ // listen detect
+ this.websocket.send(JSON.stringify({
+ session_id: sessionId,
+ type: 'listen',
+ state: 'detect',
+ text: '鍢匡紝浣犲ソ鍛�'
+ }));
+ log('鍙戦�乴isten detect娑堟伅锛屽敜閱掕瘝: 鍢匡紝浣犲ソ鍛�', 'info');
+
+ // listen start锛氬紑濮嬬洃鍚�
+ this.websocket.send(JSON.stringify({
+ session_id: sessionId,
+ type: 'listen',
+ state: 'start',
+ mode: 'auto'
+ }));
+ log('鍙戦�乴isten start娑堟伅', 'info');
+ }
+
+ // 澶勭悊鏂囨湰娑堟伅
+ handleTextMessage(message) {
+ if (message.type === 'hello') {
+ log(`鏈嶅姟鍣ㄥ洖搴旓細${JSON.stringify(message, null, 2)}`, 'success');
+ window.cameraAvailable = true;
+ log('杩炴帴鎴愬姛锛屾憚鍍忓ご宸插彲鐢�', 'success');
+ uiController.updateDialButton(true);
+
+ this._sendWakeupMessages(message.session_id);
+
+ uiController.startAIChatSession();
+ } else if (message.type === 'tts') {
+ this.handleTTSMessage(message);
+ } else if (message.type === 'audio') {
+ log(`鏀跺埌闊抽鎺у埗娑堟伅: ${JSON.stringify(message)}`, 'info');
+ } else if (message.type === 'stt') {
+ log(`璇嗗埆缁撴灉: ${message.text}`, 'info');
+ // 妫�鏌ユ槸鍚﹂渶瑕佺粦瀹氳澶�
+ if (message.text && (message.text.includes('缁戝畾') || message.text.includes('bind'))) {
+ log('鏀跺埌璁惧缁戝畾鎻愮ず锛屾洿鏂版憚鍍忓ご鐘舵��', 'warning');
+ window.cameraAvailable = false;
+ // 鍏抽棴鎽勫儚澶�
+ if (typeof window.stopCamera === 'function') {
+ window.stopCamera();
+ }
+ // 鏇存柊鎽勫儚澶存寜閽姸鎬�
+ const cameraBtn = document.getElementById('cameraBtn');
+ if (cameraBtn) {
+ cameraBtn.classList.remove('camera-active');
+ cameraBtn.querySelector('.btn-text').textContent = '鎽勫儚澶�';
+ cameraBtn.disabled = true;
+ cameraBtn.title = '璇峰厛缁戝畾楠岃瘉鐮�';
+ }
+ }
+ // 浣跨敤鏂扮殑鑱婂ぉ娑堟伅鍥炶皟鏄剧ずSTT娑堟伅
+ if (this.onChatMessage && message.text) {
+ this.onChatMessage(message.text, true);
+ }
+ } else if (message.type === 'llm') {
+ log(`澶фā鍨嬪洖澶�: ${message.text}`, 'info');
+ // 浣跨敤鏂扮殑鑱婂ぉ娑堟伅鍥炶皟鏄剧ずLLM鍥炲
+ if (this.onChatMessage && message.text) {
+ this.onChatMessage(message.text, false);
+ }
+
+ // 濡傛灉鍖呭惈琛ㄦ儏锛屾洿鏂皊essionStatus琛ㄦ儏骞惰Е鍙慙ive2D鍔ㄤ綔
+ if (message.text && /[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/u.test(message.text)) {
+ // 鎻愬彇琛ㄦ儏绗﹀彿
+ const emojiMatch = message.text.match(/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/u);
+ if (emojiMatch && this.onSessionEmotionChange) {
+ this.onSessionEmotionChange(emojiMatch[0]);
+ }
+
+ // 瑙﹀彂Live2D鎯呯华鍔ㄤ綔
+ if (message.emotion) {
+ console.log(`鏀跺埌鎯呯华娑堟伅: emotion=${message.emotion}, text=${message.text}`);
+ this.triggerLive2DEmotionAction(message.emotion);
+ }
+ }
+
+ // 鍙湁褰撴枃鏈笉浠呬粎鏄〃鎯呮椂锛屾墠娣诲姞鍒板璇濅腑
+ // 绉婚櫎鏂囨湰涓殑琛ㄦ儏鍚庢鏌ユ槸鍚﹁繕鏈夊唴瀹�
+ const textWithoutEmoji = message.text ? message.text.replace(/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/gu, '').trim() : '';
+ if (textWithoutEmoji && this.onChatMessage) {
+ this.onChatMessage(message.text, false);
+ }
+ } else if (message.type === 'mcp') {
+ this.handleMCPMessage(message);
+ } else {
+ log(`鏈煡娑堟伅绫诲瀷: ${message.type}`, 'info');
+ if (this.onChatMessage) {
+ this.onChatMessage(`鏈煡娑堟伅绫诲瀷: ${message.type}\n${JSON.stringify(message, null, 2)}`, false);
+ }
+ }
+ }
+
+ // 澶勭悊TTS娑堟伅
+ handleTTSMessage(message) {
+ if (message.state === 'start') {
+ log('鏈嶅姟鍣ㄥ紑濮嬪彂閫佽闊�', 'info');
+ this.currentSessionId = message.session_id;
+ this.isRemoteSpeaking = true;
+ if (this.onSessionStateChange) {
+ this.onSessionStateChange(true);
+ }
+
+ // 鍚姩Live2D璇磋瘽鍔ㄧ敾
+ this.startLive2DTalking();
+ } else if (message.state === 'sentence_start') {
+ log(`鏈嶅姟鍣ㄥ彂閫佽闊虫: ${message.text}`, 'info');
+ this.ttsSentenceCount = (this.ttsSentenceCount || 0) + 1;
+
+ if (message.text && this.onChatMessage) {
+ this.onChatMessage(message.text, false);
+ }
+
+ // 纭繚鍔ㄧ敾鍦ㄥ彞瀛愬紑濮嬫椂杩愯
+ const live2dManager = window.chatApp?.live2dManager;
+ if (live2dManager && !live2dManager.isTalking) {
+ this.startLive2DTalking();
+ }
+ } else if (message.state === 'sentence_end') {
+ log(`璇煶娈电粨鏉�: ${message.text}`, 'info');
+
+ // 鍙ュ瓙缁撴潫鏃朵笉娓呴櫎鍔ㄧ敾锛岀瓑寰呬笅涓�涓彞瀛愭垨鏈�缁堝仠姝�
+ } else if (message.state === 'stop') {
+ log('鏈嶅姟鍣ㄨ闊充紶杈撶粨鏉燂紝娓呯┖鎵�鏈夐煶棰戠紦鍐�', 'info');
+
+ // 娓呯┖鎵�鏈夐煶棰戠紦鍐插苟鍋滄鎾斁
+ const audioPlayer = getAudioPlayer();
+ audioPlayer.clearAllAudio();
+
+ this.isRemoteSpeaking = false;
+ if (this.onRecordButtonStateChange) {
+ this.onRecordButtonStateChange(false);
+ }
+ if (this.onSessionStateChange) {
+ this.onSessionStateChange(false);
+ }
+
+ // 寤惰繜鍋滄Live2D璇磋瘽鍔ㄧ敾锛岀‘淇濇墍鏈夊彞瀛愰兘鎾斁瀹屾瘯
+ setTimeout(() => {
+ this.stopLive2DTalking();
+ this.ttsSentenceCount = 0; // 閲嶇疆璁℃暟鍣�
+ }, 1000); // 1绉掑欢杩燂紝纭繚鎵�鏈夊彞瀛愰兘瀹屾垚
+ }
+ }
+
+ // 鍚姩Live2D璇磋瘽鍔ㄧ敾
+ startLive2DTalking() {
+ try {
+ // 鑾峰彇Live2D绠$悊鍣ㄥ疄渚�
+ const live2dManager = window.chatApp?.live2dManager;
+ if (live2dManager && live2dManager.live2dModel) {
+ // 浣跨敤闊抽鎾斁鍣ㄧ殑鍒嗘瀽鍣ㄨ妭鐐�
+ live2dManager.startTalking();
+ log('Live2D璇磋瘽鍔ㄧ敾宸插惎鍔�', 'info');
+ }
+ } catch (error) {
+ log(`鍚姩Live2D璇磋瘽鍔ㄧ敾澶辫触: ${error.message}`, 'error');
+ }
+ }
+
+ // 鍋滄Live2D璇磋瘽鍔ㄧ敾
+ stopLive2DTalking() {
+ try {
+ const live2dManager = window.chatApp?.live2dManager;
+ if (live2dManager) {
+ live2dManager.stopTalking();
+ log('Live2D璇磋瘽鍔ㄧ敾宸插仠姝�', 'info');
+ }
+ } catch (error) {
+ log(`鍋滄Live2D璇磋瘽鍔ㄧ敾澶辫触: ${error.message}`, 'error');
+ }
+ }
+
+ // 鍒濆鍖朙ive2D闊抽鍒嗘瀽鍣�
+ initializeLive2DAudioAnalyzer() {
+ try {
+ const live2dManager = window.chatApp?.live2dManager;
+ if (live2dManager) {
+ // 鍒濆鍖栭煶棰戝垎鏋愬櫒锛堜娇鐢ㄩ煶棰戞挱鏀惧櫒鐨勪笂涓嬫枃锛�
+ if (live2dManager.initializeAudioAnalyzer()) {
+ log('Live2D闊抽鍒嗘瀽鍣ㄥ垵濮嬪寲瀹屾垚锛屽凡杩炴帴鍒伴煶棰戞挱鏀惧櫒', 'success');
+ } else {
+ log('Live2D闊抽鍒嗘瀽鍣ㄥ垵濮嬪寲澶辫触锛屽皢浣跨敤妯℃嫙鍔ㄧ敾', 'warning');
+ }
+ }
+ } catch (error) {
+ log(`鍒濆鍖朙ive2D闊抽鍒嗘瀽鍣ㄥけ璐�: ${error.message}`, 'error');
+ }
+ }
+
+ // 澶勭悊MCP娑堟伅
+ handleMCPMessage(message) {
+ const payload = message.payload || {};
+ log(`鏈嶅姟鍣ㄤ笅鍙�: ${JSON.stringify(message)}`, 'info');
+
+ if (payload.method === 'tools/list') {
+ const tools = getMcpTools();
+
+ const replyMessage = JSON.stringify({
+ "session_id": message.session_id || "",
+ "type": "mcp",
+ "payload": {
+ "jsonrpc": "2.0",
+ "id": payload.id,
+ "result": {
+ "tools": tools
+ }
+ }
+ });
+ log(`瀹㈡埛绔笂鎶�: ${replyMessage}`, 'info');
+ this.websocket.send(replyMessage);
+ log(`鍥炲MCP宸ュ叿鍒楄〃: ${tools.length} 涓伐鍏穈, 'info');
+
+ } else if (payload.method === 'tools/call') {
+ const toolName = payload.params?.name;
+ const toolArgs = payload.params?.arguments;
+
+ log(`璋冪敤宸ュ叿: ${toolName} 鍙傛暟: ${JSON.stringify(toolArgs)}`, 'info');
+
+ executeMcpTool(toolName, toolArgs).then(result => {
+ const replyMessage = JSON.stringify({
+ "session_id": message.session_id || "",
+ "type": "mcp",
+ "payload": {
+ "jsonrpc": "2.0",
+ "id": payload.id,
+ "result": {
+ "content": [
+ {
+ "type": "text",
+ "text": JSON.stringify(result)
+ }
+ ],
+ "isError": false
+ }
+ }
+ });
+
+ log(`瀹㈡埛绔笂鎶�: ${replyMessage}`, 'info');
+ this.websocket.send(replyMessage);
+ }).catch(error => {
+ log(`宸ュ叿鎵ц澶辫触: ${error.message}`, 'error');
+ const errorReply = JSON.stringify({
+ "session_id": message.session_id || "",
+ "type": "mcp",
+ "payload": {
+ "jsonrpc": "2.0",
+ "id": payload.id,
+ "error": {
+ "code": -32603,
+ "message": error.message
+ }
+ }
+ });
+ this.websocket.send(errorReply);
+ });
+ } else if (payload.method === 'initialize') {
+ log(`鏀跺埌宸ュ叿鍒濆鍖栬姹�: ${JSON.stringify(payload.params)}`, 'info');
+ // 淇濆瓨瑙嗚鍒嗘瀽鎺ュ彛鍦板潃
+ const visionUrl = document.getElementById('visionUrl');
+ const visionConfig = payload?.params?.capabilities?.vision;
+ if (visionConfig && typeof visionConfig === 'object' && visionConfig.url && visionConfig.token) {
+ const visionConfigStr = JSON.stringify(visionConfig);
+ localStorage.setItem('xz_tester_vision', visionConfigStr);
+ if (visionUrl) visionUrl.value = visionConfig.url;
+ } else {
+ localStorage.removeItem('xz_tester_vision');
+ if (visionUrl) visionUrl.value = '';
+ }
+
+ const replyMessage = JSON.stringify({
+ "session_id": message.session_id || "",
+ "type": "mcp",
+ "payload": {
+ "jsonrpc": "2.0",
+ "id": payload.id,
+ "result": {
+ "protocolVersion": "2024-11-05",
+ "capabilities": {
+ "tools": {}
+ },
+ "serverInfo": {
+ "name": "xiaozhi-web-test",
+ "version": "2.1.0"
+ }
+ }
+ }
+ });
+ log(`鍥炲鍒濆鍖栧搷搴擿, 'info');
+ this.websocket.send(replyMessage);
+ } else {
+ log(`鏈煡鐨凪CP鏂规硶: ${payload.method}`, 'warning');
+ }
+ }
+
+ // 澶勭悊浜岃繘鍒舵秷鎭�
+ async handleBinaryMessage(data) {
+ try {
+ let arrayBuffer;
+ if (data instanceof ArrayBuffer) {
+ arrayBuffer = data;
+ } else if (data instanceof Blob) {
+ arrayBuffer = await data.arrayBuffer();
+ log(`鏀跺埌Blob闊抽鏁版嵁锛屽ぇ灏�: ${arrayBuffer.byteLength}瀛楄妭`, 'debug');
+ } else {
+ log(`鏀跺埌鏈煡绫诲瀷鐨勪簩杩涘埗鏁版嵁: ${typeof data}`, 'warning');
+ return;
+ }
+
+ const opusData = new Uint8Array(arrayBuffer);
+ const audioPlayer = getAudioPlayer();
+ audioPlayer.enqueueAudioData(opusData);
+ } catch (error) {
+ log(`澶勭悊浜岃繘鍒舵秷鎭嚭閿�: ${error.message}`, 'error');
+ }
+ }
+
+ // 杩炴帴WebSocket鏈嶅姟鍣�
+ async connect() {
+ const config = getConfig();
+ log('姝e湪妫�鏌TA鐘舵��...', 'info');
+ saveConnectionUrls();
+
+ try {
+ const otaUrl = document.getElementById('otaUrl').value.trim();
+ const ws = await webSocketConnect(otaUrl, config);
+ if (ws === undefined) {
+ return false;
+ }
+ this.websocket = ws;
+
+ // 璁剧疆鎺ユ敹浜岃繘鍒舵暟鎹殑绫诲瀷涓篈rrayBuffer
+ this.websocket.binaryType = 'arraybuffer';
+
+ // 璁剧疆 MCP 妯″潡鐨� WebSocket 瀹炰緥
+ setMcpWebSocket(this.websocket);
+
+ // 璁剧疆褰曢煶鍣ㄧ殑WebSocket
+ const audioRecorder = getAudioRecorder();
+ audioRecorder.setWebSocket(this.websocket);
+
+ this.setupEventHandlers();
+
+ return true;
+ } catch (error) {
+ log(`杩炴帴閿欒: ${error.message}`, 'error');
+ if (this.onConnectionStateChange) {
+ this.onConnectionStateChange(false);
+ }
+ return false;
+ }
+ }
+
+ // 璁剧疆浜嬩欢澶勭悊鍣�
+ setupEventHandlers() {
+ this.websocket.onopen = async () => {
+ const url = document.getElementById('serverUrl').value;
+ log(`宸茶繛鎺ュ埌鏈嶅姟鍣�: ${url}`, 'success');
+
+ if (this.onConnectionStateChange) {
+ this.onConnectionStateChange(true);
+ }
+
+ // 杩炴帴鎴愬姛鍚庯紝榛樿鐘舵�佷负鑱嗗惉涓�
+ this.isRemoteSpeaking = false;
+ if (this.onSessionStateChange) {
+ this.onSessionStateChange(false);
+ }
+
+ // 鍦╓ebSocket杩炴帴鎴愬姛鏃跺垵濮嬪寲Live2D闊抽鍒嗘瀽鍣�
+ this.initializeLive2DAudioAnalyzer();
+
+ await this.sendHelloMessage();
+ };
+
+ this.websocket.onclose = () => {
+ log('宸叉柇寮�杩炴帴', 'info');
+
+ if (this.onConnectionStateChange) {
+ this.onConnectionStateChange(false);
+ }
+
+ const audioRecorder = getAudioRecorder();
+ audioRecorder.stop();
+
+ // 鍏抽棴鎽勫儚澶�
+ if (typeof window.stopCamera === 'function') {
+ window.stopCamera();
+ }
+
+ // 闅愯棌鎽勫儚澶存樉绀哄尯鍩�
+ const cameraContainer = document.getElementById('cameraContainer');
+ if (cameraContainer) {
+ cameraContainer.classList.remove('active');
+ }
+ };
+
+ this.websocket.onerror = (error) => {
+ log(`WebSocket閿欒: ${error.message || '鏈煡閿欒'}`, 'error');
+ uiController.addChatMessage(`鈿狅笍 WebSocket閿欒: ${error.message || '鏈煡閿欒'}`, false);
+ if (this.onConnectionStateChange) {
+ this.onConnectionStateChange(false);
+ }
+ };
+
+ this.websocket.onmessage = (event) => {
+ try {
+ if (typeof event.data === 'string') {
+ const message = JSON.parse(event.data);
+ this.handleTextMessage(message);
+ } else {
+ this.handleBinaryMessage(event.data);
+ }
+ } catch (error) {
+ log(`WebSocket娑堟伅澶勭悊閿欒: ${error.message}`, 'error');
+ // 涓嶅啀浣跨敤鏃х殑addMessage鍑芥暟锛屽洜涓篶onversationDiv鍏冪礌涓嶅瓨鍦�
+ // 閿欒娑堟伅灏嗛�氳繃鍏朵粬鏂瑰紡鏄剧ず
+ }
+ };
+ }
+
+ // 鏂紑杩炴帴
+ disconnect() {
+ if (!this.websocket) return;
+
+ this.websocket.close();
+ const audioRecorder = getAudioRecorder();
+ audioRecorder.stop();
+
+ // 鍏抽棴鎽勫儚澶�
+ if (typeof window.stopCamera === 'function') {
+ window.stopCamera();
+ }
+
+ // 闅愯棌鎽勫儚澶存樉绀哄尯鍩�
+ const cameraContainer = document.getElementById('cameraContainer');
+ if (cameraContainer) {
+ cameraContainer.classList.remove('active');
+ }
+ }
+
+ // 鍙戦�佹枃鏈秷鎭�
+ sendTextMessage(text) {
+ if (text === '' || !this.websocket || this.websocket.readyState !== WebSocket.OPEN) {
+ return false;
+ }
+
+ try {
+ // 濡傛灉瀵规柟姝e湪璇磋瘽锛屽厛鍙戦�佹墦鏂秷鎭�
+ if (this.isRemoteSpeaking && this.currentSessionId) {
+ const abortMessage = {
+ session_id: this.currentSessionId,
+ type: 'abort',
+ reason: 'wake_word_detected'
+ };
+ this.websocket.send(JSON.stringify(abortMessage));
+ log('鍙戦�佹墦鏂秷鎭�', 'info');
+ }
+
+ const listenMessage = {
+ type: 'listen',
+ state: 'detect',
+ text: text
+ };
+
+ this.websocket.send(JSON.stringify(listenMessage));
+ log(`鍙戦�佹枃鏈秷鎭�: ${text}`, 'info');
+
+ return true;
+ } catch (error) {
+ log(`鍙戦�佹秷鎭敊璇�: ${error.message}`, 'error');
+ return false;
+ }
+ }
+
+ /**
+ * 瑙﹀彂Live2D鎯呯华鍔ㄤ綔
+ * @param {string} emotion - 鎯呯华鍚嶇О
+ */
+ triggerLive2DEmotionAction(emotion) {
+ try {
+ const live2dManager = window.chatApp?.live2dManager;
+ if (live2dManager && typeof live2dManager.triggerEmotionAction === 'function') {
+ live2dManager.triggerEmotionAction(emotion);
+ log(`瑙﹀彂Live2D鎯呯华鍔ㄤ綔: ${emotion}`, 'info');
+ } else {
+ log(`鏃犳硶瑙﹀彂Live2D鎯呯华鍔ㄤ綔: Live2D绠$悊鍣ㄦ湭鎵惧埌鎴栨柟娉曚笉鍙敤`, 'warning');
+ }
+ } catch (error) {
+ log(`瑙﹀彂Live2D鎯呯华鍔ㄤ綔澶辫触: ${error.message}`, 'error');
+ }
+ }
+
+ // 鑾峰彇WebSocket瀹炰緥
+ getWebSocket() {
+ return this.websocket;
+ }
+
+ // 妫�鏌ユ槸鍚﹀凡杩炴帴
+ isConnected() {
+ return this.websocket && this.websocket.readyState === WebSocket.OPEN;
+ }
+}
+
+// 鍒涘缓鍗曚緥
+let wsHandlerInstance = null;
+
+export function getWebSocketHandler() {
+ if (!wsHandlerInstance) {
+ wsHandlerInstance = new WebSocketHandler();
+ }
+ return wsHandlerInstance;
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/js/live2d/cubism4.min.js b/xiaozhi-esp32-server/main/digital-human/js/live2d/cubism4.min.js
new file mode 100644
index 0000000..f0e4a50
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/js/live2d/cubism4.min.js
@@ -0,0 +1 @@
+var __pow=Math.pow,__async=(t,e,i)=>new Promise(((s,r)=>{var a=t=>{try{n(i.next(t))}catch(e){r(e)}},o=t=>{try{n(i.throw(t))}catch(e){r(e)}},n=t=>t.done?s(t.value):Promise.resolve(t.value).then(a,o);n((i=i.apply(t,e)).next())}));!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("@pixi/utils"),require("@pixi/math"),require("@pixi/core"),require("@pixi/display")):"function"==typeof define&&define.amd?define(["exports","@pixi/utils","@pixi/math","@pixi/core","@pixi/display"],e):e(((t="undefined"!=typeof globalThis?globalThis:t||self).PIXI=t.PIXI||{},t.PIXI.live2d=t.PIXI.live2d||{}),t.PIXI.utils,t.PIXI,t.PIXI,t.PIXI)}(this,(function(t,e,i,s,r){"use strict";class a{constructor(){this._breathParameters=[],this._currentTime=0}static create(){return new a}setParameters(t){this._breathParameters=t}getParameters(){return this._breathParameters}updateParameters(t,e){this._currentTime+=e;const i=2*this._currentTime*3.14159;for(let s=0;s<this._breathParameters.length;++s){const e=this._breathParameters[s];t.addParameterValueById(e.parameterId,e.offset+e.peak*Math.sin(i/e.cycle),e.weight)}}}class o{constructor(t,e,i,s,r){this.parameterId=null==t?void 0:t,this.offset=null==e?0:e,this.peak=null==i?0:i,this.cycle=null==s?0:s,this.weight=null==r?0:r}}const n=class{static create(t){return new n(t)}setBlinkingInterval(t){this._blinkingIntervalSeconds=t}setBlinkingSetting(t,e,i){this._closingSeconds=t,this._closedSeconds=e,this._openingSeconds=i}setParameterIds(t){this._parameterIds=t}getParameterIds(){return this._parameterIds}updateParameters(t,e){let i;this._userTimeSeconds+=e;let s=0;switch(this._blinkingState){case h.EyeState_Closing:s=(this._userTimeSeconds-this._stateStartTimeSeconds)/this._closingSeconds,s>=1&&(s=1,this._blinkingState=h.EyeState_Closed,this._stateStartTimeSeconds=this._userTimeSeconds),i=1-s;break;case h.EyeState_Closed:s=(this._userTimeSeconds-this._stateStartTimeSeconds)/this._closedSeconds,s>=1&&(this._blinkingState=h.EyeState_Opening,this._stateStartTimeSeconds=this._userTimeSeconds),i=0;break;case h.EyeState_Opening:s=(this._userTimeSeconds-this._stateStartTimeSeconds)/this._openingSeconds,s>=1&&(s=1,this._blinkingState=h.EyeState_Interval,this._nextBlinkingTime=this.determinNextBlinkingTiming()),i=s;break;case h.EyeState_Interval:this._nextBlinkingTime<this._userTimeSeconds&&(this._blinkingState=h.EyeState_Closing,this._stateStartTimeSeconds=this._userTimeSeconds),i=1;break;case h.EyeState_First:default:this._blinkingState=h.EyeState_Interval,this._nextBlinkingTime=this.determinNextBlinkingTiming(),i=1}n.CloseIfZero||(i=-i);for(let r=0;r<this._parameterIds.length;++r)t.setParameterValueById(this._parameterIds[r],i)}constructor(t){var e,i;this._blinkingState=h.EyeState_First,this._nextBlinkingTime=0,this._stateStartTimeSeconds=0,this._blinkingIntervalSeconds=4,this._closingSeconds=.1,this._closedSeconds=.05,this._openingSeconds=.15,this._userTimeSeconds=0,this._parameterIds=[],null!=t&&(this._parameterIds=null!=(i=null==(e=t.getEyeBlinkParameters())?void 0:e.slice())?i:this._parameterIds)}determinNextBlinkingTiming(){const t=Math.random();return this._userTimeSeconds+t*(2*this._blinkingIntervalSeconds-1)}};let l=n;l.CloseIfZero=!0;var h=(t=>(t[t.EyeState_First=0]="EyeState_First",t[t.EyeState_Interval=1]="EyeState_Interval",t[t.EyeState_Closing=2]="EyeState_Closing",t[t.EyeState_Closed=3]="EyeState_Closed",t[t.EyeState_Opening=4]="EyeState_Opening",t))(h||{});class u{static create(t){const e=new u;"number"==typeof t.FadeInTime&&(e._fadeTimeSeconds=t.FadeInTime,e._fadeTimeSeconds<=0&&(e._fadeTimeSeconds=.5));const i=t.Groups,s=i.length;for(let r=0;r<s;++r){const t=i[r],s=t.length;let a=0;for(let i=0;i<s;++i){const s=t[i],r=new d;r.partId=s.Id;const o=s.Link;if(o){const t=o.length;for(let e=0;e<t;++e){const t=new d;t.partId=o[e],r.link.push(t)}}e._partGroups.push(r),++a}e._partGroupCounts.push(a)}return e}updateParameters(t,e){t!=this._lastModel&&this.reset(t),this._lastModel=t,e<0&&(e=0);let i=0;for(let s=0;s<this._partGroupCounts.length;s++){const r=this._partGroupCounts[s];this.doFade(t,e,i,r),i+=r}this.copyPartOpacities(t)}reset(t){let e=0;for(let i=0;i<this._partGroupCounts.length;++i){const s=this._partGroupCounts[i];for(let i=e;i<e+s;++i){this._partGroups[i].initialize(t);const s=this._partGroups[i].partIndex,r=this._partGroups[i].parameterIndex;if(!(s<0)){t.setPartOpacityByIndex(s,i==e?1:0),t.setParameterValueByIndex(r,i==e?1:0);for(let e=0;e<this._partGroups[i].link.length;++e)this._partGroups[i].link[e].initialize(t)}}e+=s}}copyPartOpacities(t){for(let e=0;e<this._partGroups.length;++e){const i=this._partGroups[e];if(0==i.link.length)continue;const s=this._partGroups[e].partIndex,r=t.getPartOpacityByIndex(s);for(let e=0;e<i.link.length;++e){const s=i.link[e].partIndex;s<0||t.setPartOpacityByIndex(s,r)}}}doFade(t,e,i,s){let r=-1,a=1;const o=.5;for(let n=i;n<i+s;++n){const i=this._partGroups[n].partIndex,s=this._partGroups[n].parameterIndex;if(t.getParameterValueByIndex(s)>.001){if(r>=0)break;r=n,a=t.getPartOpacityByIndex(i),a+=e/this._fadeTimeSeconds,a>1&&(a=1)}}r<0&&(r=0,a=1);for(let n=i;n<i+s;++n){const e=this._partGroups[n].partIndex;if(r==n)t.setPartOpacityByIndex(e,a);else{let i,s=t.getPartOpacityByIndex(e);i=a<o?-.5*a/o+1:(1-a)*o/.5;(1-i)*(1-a)>.15&&(i=1-.15/(1-a)),s>i&&(s=i),t.setPartOpacityByIndex(e,s)}}}constructor(){this._fadeTimeSeconds=.5,this._lastModel=void 0,this._partGroups=[],this._partGroupCounts=[]}}class d{constructor(t){this.parameterIndex=0,this.partIndex=0,this.partId="",this.link=[],null!=t&&this.assignment(t)}assignment(t){return this.partId=t.partId,this.link=t.link.map((t=>t.clone())),this}initialize(t){this.parameterIndex=t.getParameterIndex(this.partId),this.partIndex=t.getPartIndex(this.partId),t.setParameterValueByIndex(this.parameterIndex,1)}clone(){const t=new d;return t.partId=this.partId,t.parameterIndex=this.parameterIndex,t.partIndex=this.partIndex,t.link=this.link.map((t=>t.clone())),t}}class c{constructor(t,e){this.x=t||0,this.y=e||0}add(t){const e=new c(0,0);return e.x=this.x+t.x,e.y=this.y+t.y,e}substract(t){const e=new c(0,0);return e.x=this.x-t.x,e.y=this.y-t.y,e}multiply(t){const e=new c(0,0);return e.x=this.x*t.x,e.y=this.y*t.y,e}multiplyByScaler(t){return this.multiply(new c(t,t))}division(t){const e=new c(0,0);return e.x=this.x/t.x,e.y=this.y/t.y,e}divisionByScalar(t){return this.division(new c(t,t))}getLength(){return Math.sqrt(this.x*this.x+this.y*this.y)}getDistanceWith(t){return Math.sqrt((this.x-t.x)*(this.x-t.x)+(this.y-t.y)*(this.y-t.y))}dot(t){return this.x*t.x+this.y*t.y}normalize(){const t=Math.pow(this.x*this.x+this.y*this.y,.5);this.x=this.x/t,this.y=this.y/t}isEqual(t){return this.x==t.x&&this.y==t.y}isNotEqual(t){return!this.isEqual(t)}}const m=class{static range(t,e,i){return t<e?t=e:t>i&&(t=i),t}static sin(t){return Math.sin(t)}static cos(t){return Math.cos(t)}static abs(t){return Math.abs(t)}static sqrt(t){return Math.sqrt(t)}static cbrt(t){if(0===t)return t;let e=t;const i=e<0;let s;return i&&(e=-e),e===1/0?s=1/0:(s=Math.exp(Math.log(e)/3),s=(e/(s*s)+2*s)/3),i?-s:s}static getEasingSine(t){return t<0?0:t>1?1:.5-.5*this.cos(t*Math.PI)}static max(t,e){return t>e?t:e}static min(t,e){return t>e?e:t}static degreesToRadian(t){return t/180*Math.PI}static radianToDegrees(t){return 180*t/Math.PI}static directionToRadian(t,e){let i=Math.atan2(e.y,e.x)-Math.atan2(t.y,t.x);for(;i<-Math.PI;)i+=2*Math.PI;for(;i>Math.PI;)i-=2*Math.PI;return i}static directionToDegrees(t,e){const i=this.directionToRadian(t,e);let s=this.radianToDegrees(i);return e.x-t.x>0&&(s=-s),s}static radianToDirection(t){const e=new c;return e.x=this.sin(t),e.y=this.cos(t),e}static quadraticEquation(t,e,i){return this.abs(t)<m.Epsilon?this.abs(e)<m.Epsilon?-i:-i/e:-(e+this.sqrt(e*e-4*t*i))/(2*t)}static cardanoAlgorithmForBezier(t,e,i,s){if(this.sqrt(t)<m.Epsilon)return this.range(this.quadraticEquation(e,i,s),0,1);const r=e/t,a=i/t,o=(3*a-r*r)/3,n=o/3,l=(2*r*r*r-9*r*a+27*(s/t))/27,h=l/2,u=h*h+n*n*n,d=.5,c=.51;if(u<0){const t=-o/3,e=t*t*t,i=this.sqrt(e),s=-l/(2*i),a=this.range(s,-1,1),n=Math.acos(a),h=2*this.cbrt(i),u=h*this.cos(n/3)-r/3;if(this.abs(u-d)<c)return this.range(u,0,1);const m=h*this.cos((n+2*Math.PI)/3)-r/3;if(this.abs(m-d)<c)return this.range(m,0,1);const g=h*this.cos((n+4*Math.PI)/3)-r/3;return this.range(g,0,1)}if(0==u){let t;t=h<0?this.cbrt(-h):-this.cbrt(h);const e=2*t-r/3;if(this.abs(e-d)<c)return this.range(e,0,1);const i=-t-r/3;return this.range(i,0,1)}const g=this.sqrt(u),p=this.cbrt(g-h)-this.cbrt(g+h)-r/3;return this.range(p,0,1)}constructor(){}};let g=m;g.Epsilon=1e-5;class p{constructor(){this._tr=new Float32Array(16),this.loadIdentity()}static multiply(t,e,i){const s=new Float32Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);for(let r=0;r<4;++r)for(let i=0;i<4;++i)for(let a=0;a<4;++a)s[i+4*r]+=t[a+4*r]*e[i+4*a];for(let r=0;r<16;++r)i[r]=s[r]}loadIdentity(){const t=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);this.setMatrix(t)}setMatrix(t){for(let e=0;e<16;++e)this._tr[e]=t[e]}getArray(){return this._tr}getScaleX(){return this._tr[0]}getScaleY(){return this._tr[5]}getTranslateX(){return this._tr[12]}getTranslateY(){return this._tr[13]}transformX(t){return this._tr[0]*t+this._tr[12]}transformY(t){return this._tr[5]*t+this._tr[13]}invertTransformX(t){return(t-this._tr[12])/this._tr[0]}invertTransformY(t){return(t-this._tr[13])/this._tr[5]}translateRelative(t,e){const i=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,t,e,0,1]);p.multiply(i,this._tr,this._tr)}translate(t,e){this._tr[12]=t,this._tr[13]=e}translateX(t){this._tr[12]=t}translateY(t){this._tr[13]=t}scaleRelative(t,e){const i=new Float32Array([t,0,0,0,0,e,0,0,0,0,1,0,0,0,0,1]);p.multiply(i,this._tr,this._tr)}scale(t,e){this._tr[0]=t,this._tr[5]=e}multiplyByMatrix(t){p.multiply(t.getArray(),this._tr,this._tr)}clone(){const t=new p;for(let e=0;e<this._tr.length;e++)t._tr[e]=this._tr[e];return t}}class _{initialize(t){this._model=t}drawModel(){null!=this.getModel()&&this.doDrawModel()}setMvpMatrix(t){this._mvpMatrix4x4.setMatrix(t.getArray())}getMvpMatrix(){return this._mvpMatrix4x4}setModelColor(t,e,i,s){t<0?t=0:t>1&&(t=1),e<0?e=0:e>1&&(e=1),i<0?i=0:i>1&&(i=1),s<0?s=0:s>1&&(s=1),this._modelColor.R=t,this._modelColor.G=e,this._modelColor.B=i,this._modelColor.A=s}getModelColor(){return Object.assign({},this._modelColor)}setIsPremultipliedAlpha(t){this._isPremultipliedAlpha=t}isPremultipliedAlpha(){return this._isPremultipliedAlpha}setIsCulling(t){this._isCulling=t}isCulling(){return this._isCulling}setAnisotropy(t){this._anisortopy=t}getAnisotropy(){return this._anisortopy}getModel(){return this._model}constructor(){this._isCulling=!1,this._isPremultipliedAlpha=!1,this._anisortopy=0,this._modelColor=new y,this._mvpMatrix4x4=new p,this._mvpMatrix4x4.loadIdentity()}}var f=(t=>(t[t.CubismBlendMode_Normal=0]="CubismBlendMode_Normal",t[t.CubismBlendMode_Additive=1]="CubismBlendMode_Additive",t[t.CubismBlendMode_Multiplicative=2]="CubismBlendMode_Multiplicative",t))(f||{});class y{constructor(){this.R=1,this.G=1,this.B=1,this.A=1}}let x,v=!1,M=!1;const P={vertexOffset:0,vertexStep:2};class C{static startUp(t){if(v)return I("CubismFramework.startUp() is already done."),v;if(Live2DCubismCore._isStarted)return v=!0,!0;if(Live2DCubismCore._isStarted=!0,x=t,x&&Live2DCubismCore.Logging.csmSetLogFunction(x.logFunction),v=!0,v){const t=Live2DCubismCore.Version.csmGetVersion(),e=(16711680&t)>>16,i=65535&t,s=t;I("Live2D Cubism Core version: {0}.{1}.{2} ({3})",("00"+((4278190080&t)>>24)).slice(-2),("00"+e).slice(-2),("0000"+i).slice(-4),s)}return I("CubismFramework.startUp() is complete."),v}static cleanUp(){v=!1,M=!1,x=void 0}static initialize(){v?M?L("CubismFramework.initialize() skipped, already initialized."):(M=!0,I("CubismFramework.initialize() is complete.")):L("CubismFramework is not started.")}static dispose(){v?M?(_.staticRelease(),M=!1,I("CubismFramework.dispose() is complete.")):L("CubismFramework.dispose() skipped, not initialized."):L("CubismFramework is not started.")}static isStarted(){return v}static isInitialized(){return M}static coreLogFunction(t){Live2DCubismCore.Logging.csmGetLogFunction()&&Live2DCubismCore.Logging.csmGetLogFunction()(t)}static getLoggingLevel(){return null!=x?x.loggingLevel:S.LogLevel_Off}constructor(){}}var S=(t=>(t[t.LogLevel_Verbose=0]="LogLevel_Verbose",t[t.LogLevel_Debug=1]="LogLevel_Debug",t[t.LogLevel_Info=2]="LogLevel_Info",t[t.LogLevel_Warning=3]="LogLevel_Warning",t[t.LogLevel_Error=4]="LogLevel_Error",t[t.LogLevel_Off=5]="LogLevel_Off",t))(S||{});const b=()=>{};function w(t,...e){E.print(S.LogLevel_Debug,"[CSM][D]"+t+"\n",e)}function I(t,...e){E.print(S.LogLevel_Info,"[CSM][I]"+t+"\n",e)}function L(t,...e){E.print(S.LogLevel_Warning,"[CSM][W]"+t+"\n",e)}function T(t,...e){E.print(S.LogLevel_Error,"[CSM][E]"+t+"\n",e)}class E{static print(t,e,i){if(t<C.getLoggingLevel())return;const s=C.coreLogFunction;if(!s)return;s(e.replace(/{(\d+)}/g,((t,e)=>i[e])))}static dumpBytes(t,e,i){for(let s=0;s<i;s++)s%16==0&&s>0?this.print(t,"\n"):s%8==0&&s>0&&this.print(t," "),this.print(t,"{0} ",[255&e[s]]);this.print(t,"\n")}constructor(){}}class D{update(){this._model.update(),this._model.drawables.resetDynamicFlags()}getCanvasWidth(){return null==this._model?0:this._model.canvasinfo.CanvasWidth/this._model.canvasinfo.PixelsPerUnit}getCanvasHeight(){return null==this._model?0:this._model.canvasinfo.CanvasHeight/this._model.canvasinfo.PixelsPerUnit}saveParameters(){const t=this._model.parameters.count,e=this._savedParameters.length;for(let i=0;i<t;++i)i<e?this._savedParameters[i]=this._parameterValues[i]:this._savedParameters.push(this._parameterValues[i])}getModel(){return this._model}getPartIndex(t){let e;const i=this._model.parts.count;for(e=0;e<i;++e)if(t==this._partIds[e])return e;return t in this._notExistPartId?this._notExistPartId[t]:(e=i+this._notExistPartId.length,this._notExistPartId[t]=e,this._notExistPartOpacities[e]=0,e)}getPartCount(){return this._model.parts.count}setPartOpacityByIndex(t,e){t in this._notExistPartOpacities?this._notExistPartOpacities[t]=e:(0<=t&&this.getPartCount(),this._partOpacities[t]=e)}setPartOpacityById(t,e){const i=this.getPartIndex(t);i<0||this.setPartOpacityByIndex(i,e)}getPartOpacityByIndex(t){return t in this._notExistPartOpacities?this._notExistPartOpacities[t]:(0<=t&&this.getPartCount(),this._partOpacities[t])}getPartOpacityById(t){const e=this.getPartIndex(t);return e<0?0:this.getPartOpacityByIndex(e)}getParameterIndex(t){let e;const i=this._model.parameters.count;for(e=0;e<i;++e)if(t==this._parameterIds[e])return e;return t in this._notExistParameterId?this._notExistParameterId[t]:(e=this._model.parameters.count+Object.keys(this._notExistParameterId).length,this._notExistParameterId[t]=e,this._notExistParameterValues[e]=0,e)}getParameterCount(){return this._model.parameters.count}getParameterMaximumValue(t){return this._model.parameters.maximumValues[t]}getParameterMinimumValue(t){return this._model.parameters.minimumValues[t]}getParameterDefaultValue(t){return this._model.parameters.defaultValues[t]}getParameterValueByIndex(t){return t in this._notExistParameterValues?this._notExistParameterValues[t]:(0<=t&&this.getParameterCount(),this._parameterValues[t])}getParameterValueById(t){const e=this.getParameterIndex(t);return this.getParameterValueByIndex(e)}setParameterValueByIndex(t,e,i=1){t in this._notExistParameterValues?this._notExistParameterValues[t]=1==i?e:this._notExistParameterValues[t]*(1-i)+e*i:(0<=t&&this.getParameterCount(),this._model.parameters.maximumValues[t]<e&&(e=this._model.parameters.maximumValues[t]),this._model.parameters.minimumValues[t]>e&&(e=this._model.parameters.minimumValues[t]),this._parameterValues[t]=1==i?e:this._parameterValues[t]=this._parameterValues[t]*(1-i)+e*i)}setParameterValueById(t,e,i=1){const s=this.getParameterIndex(t);this.setParameterValueByIndex(s,e,i)}addParameterValueByIndex(t,e,i=1){this.setParameterValueByIndex(t,this.getParameterValueByIndex(t)+e*i)}addParameterValueById(t,e,i=1){const s=this.getParameterIndex(t);this.addParameterValueByIndex(s,e,i)}multiplyParameterValueById(t,e,i=1){const s=this.getParameterIndex(t);this.multiplyParameterValueByIndex(s,e,i)}multiplyParameterValueByIndex(t,e,i=1){this.setParameterValueByIndex(t,this.getParameterValueByIndex(t)*(1+(e-1)*i))}getDrawableIds(){return this._drawableIds.slice()}getDrawableIndex(t){const e=this._model.drawables.count;for(let i=0;i<e;++i)if(this._drawableIds[i]==t)return i;return-1}getDrawableCount(){return this._model.drawables.count}getDrawableId(t){return this._model.drawables.ids[t]}getDrawableRenderOrders(){return this._model.drawables.renderOrders}getDrawableTextureIndices(t){return this._model.drawables.textureIndices[t]}getDrawableDynamicFlagVertexPositionsDidChange(t){const e=this._model.drawables.dynamicFlags;return Live2DCubismCore.Utils.hasVertexPositionsDidChangeBit(e[t])}getDrawableVertexIndexCount(t){return this._model.drawables.indexCounts[t]}getDrawableVertexCount(t){return this._model.drawables.vertexCounts[t]}getDrawableVertices(t){return this.getDrawableVertexPositions(t)}getDrawableVertexIndices(t){return this._model.drawables.indices[t]}getDrawableVertexPositions(t){return this._model.drawables.vertexPositions[t]}getDrawableVertexUvs(t){return this._model.drawables.vertexUvs[t]}getDrawableOpacity(t){return this._model.drawables.opacities[t]}getDrawableCulling(t){const e=this._model.drawables.constantFlags;return!Live2DCubismCore.Utils.hasIsDoubleSidedBit(e[t])}getDrawableBlendMode(t){const e=this._model.drawables.constantFlags;return Live2DCubismCore.Utils.hasBlendAdditiveBit(e[t])?f.CubismBlendMode_Additive:Live2DCubismCore.Utils.hasBlendMultiplicativeBit(e[t])?f.CubismBlendMode_Multiplicative:f.CubismBlendMode_Normal}getDrawableInvertedMaskBit(t){const e=this._model.drawables.constantFlags;return Live2DCubismCore.Utils.hasIsInvertedMaskBit(e[t])}getDrawableMasks(){return this._model.drawables.masks}getDrawableMaskCounts(){return this._model.drawables.maskCounts}isUsingMasking(){for(let t=0;t<this._model.drawables.count;++t)if(!(this._model.drawables.maskCounts[t]<=0))return!0;return!1}getDrawableDynamicFlagIsVisible(t){const e=this._model.drawables.dynamicFlags;return Live2DCubismCore.Utils.hasIsVisibleBit(e[t])}getDrawableDynamicFlagVisibilityDidChange(t){const e=this._model.drawables.dynamicFlags;return Live2DCubismCore.Utils.hasVisibilityDidChangeBit(e[t])}getDrawableDynamicFlagOpacityDidChange(t){const e=this._model.drawables.dynamicFlags;return Live2DCubismCore.Utils.hasOpacityDidChangeBit(e[t])}getDrawableDynamicFlagRenderOrderDidChange(t){const e=this._model.drawables.dynamicFlags;return Live2DCubismCore.Utils.hasRenderOrderDidChangeBit(e[t])}loadParameters(){let t=this._model.parameters.count;const e=this._savedParameters.length;t>e&&(t=e);for(let i=0;i<t;++i)this._parameterValues[i]=this._savedParameters[i]}initialize(){this._parameterValues=this._model.parameters.values,this._partOpacities=this._model.parts.opacities,this._parameterMaximumValues=this._model.parameters.maximumValues,this._parameterMinimumValues=this._model.parameters.minimumValues;{const t=this._model.parameters.ids,e=this._model.parameters.count;for(let i=0;i<e;++i)this._parameterIds.push(t[i])}{const t=this._model.parts.ids,e=this._model.parts.count;for(let i=0;i<e;++i)this._partIds.push(t[i])}{const t=this._model.drawables.ids,e=this._model.drawables.count;for(let i=0;i<e;++i)this._drawableIds.push(t[i])}}constructor(t){this._model=t,this._savedParameters=[],this._parameterIds=[],this._drawableIds=[],this._partIds=[],this._notExistPartId={},this._notExistParameterId={},this._notExistParameterValues={},this._notExistPartOpacities={},this.initialize()}release(){this._model.release(),this._model=void 0}}class F{static create(t){const e=Live2DCubismCore.Moc.fromArrayBuffer(t);if(e)return new F(e);throw new Error("Unknown error")}createModel(){let t;const e=Live2DCubismCore.Model.fromMoc(this._moc);if(e)return t=new D(e),++this._modelCount,t;throw new Error("Unknown error")}deleteModel(t){null!=t&&--this._modelCount}constructor(t){this._moc=t,this._modelCount=0}release(){this._moc._release(),this._moc=void 0}}class A{constructor(t,e){this._json=t}release(){this._json=void 0}getUserDataCount(){return this._json.Meta.UserDataCount}getTotalUserDataSize(){return this._json.Meta.TotalUserDataSize}getUserDataTargetType(t){return this._json.UserData[t].Target}getUserDataId(t){return this._json.UserData[t].Id}getUserDataValue(t){return this._json.UserData[t].Value}}class B{static create(t,e){const i=new B;return i.parseUserData(t,e),i}getArtMeshUserDatas(){return this._artMeshUserDataNode}parseUserData(t,e){let i=new A(t,e);const s=i.getUserDataCount();for(let r=0;r<s;r++){const t={targetId:i.getUserDataId(r),targetType:i.getUserDataTargetType(r),value:i.getUserDataValue(r)};this._userDataNodes.push(t),"ArtMesh"==t.targetType&&this._artMeshUserDataNode.push(t)}i.release()}constructor(){this._userDataNodes=[],this._artMeshUserDataNode=[]}release(){this._userDataNodes=null}}class R{constructor(){this._fadeInSeconds=-1,this._fadeOutSeconds=-1,this._weight=1,this._offsetSeconds=0,this._firedEventValues=[]}release(){this._weight=0}updateParameters(t,e,i){if(!e.isAvailable()||e.isFinished())return;if(!e.isStarted()){e.setIsStarted(!0),e.setStartTime(i-this._offsetSeconds),e.setFadeInStartTime(i);const t=this.getDuration();e.getEndTime()<0&&e.setEndTime(t<=0?-1:e.getStartTime()+t)}let s=this._weight;s=s*(0==this._fadeInSeconds?1:g.getEasingSine((i-e.getFadeInStartTime())/this._fadeInSeconds))*(0==this._fadeOutSeconds||e.getEndTime()<0?1:g.getEasingSine((e.getEndTime()-i)/this._fadeOutSeconds)),e.setState(i,s),this.doUpdateParameters(t,i,s,e),e.getEndTime()>0&&e.getEndTime()<i&&e.setIsFinished(!0)}setFadeInTime(t){this._fadeInSeconds=t}setFadeOutTime(t){this._fadeOutSeconds=t}getFadeOutTime(){return this._fadeOutSeconds}getFadeInTime(){return this._fadeInSeconds}setWeight(t){this._weight=t}getWeight(){return this._weight}getDuration(){return-1}getLoopDuration(){return-1}setOffsetTime(t){this._offsetSeconds=t}getFiredEvent(t,e){return this._firedEventValues}setFinishedMotionHandler(t){this._onFinishedMotion=t}getFinishedMotionHandler(){return this._onFinishedMotion}}class O extends R{constructor(){super(),this._parameters=[]}static create(t){const e=new O,i=t.FadeInTime,s=t.FadeOutTime;e.setFadeInTime(void 0!==i?i:1),e.setFadeOutTime(void 0!==s?s:1);const r=t.Parameters||[];for(let a=0;a<r.length;++a){const t=r[a],i=t.Id,s=t.Value;let o;switch(t.Blend){case"Multiply":o=U.ExpressionBlendType_Multiply;break;case"Overwrite":o=U.ExpressionBlendType_Overwrite;break;default:o=U.ExpressionBlendType_Add}const n={parameterId:i,blendType:o,value:s};e._parameters.push(n)}return e}doUpdateParameters(t,e,i,s){for(let r=0;r<this._parameters.length;++r){const e=this._parameters[r];switch(e.blendType){case U.ExpressionBlendType_Add:t.addParameterValueById(e.parameterId,e.value,i);break;case U.ExpressionBlendType_Multiply:t.multiplyParameterValueById(e.parameterId,e.value,i);break;case U.ExpressionBlendType_Overwrite:t.setParameterValueById(e.parameterId,e.value,i)}}}}var k,U=(t=>(t[t.ExpressionBlendType_Add=0]="ExpressionBlendType_Add",t[t.ExpressionBlendType_Multiply=1]="ExpressionBlendType_Multiply",t[t.ExpressionBlendType_Overwrite=2]="ExpressionBlendType_Overwrite",t))(U||{});t.CubismConfig=void 0,(k=t.CubismConfig||(t.CubismConfig={})).supportMoreMaskDivisions=!0,k.setOpacityFromMotion=!1;var N=(t=>(t[t.CubismMotionCurveTarget_Model=0]="CubismMotionCurveTarget_Model",t[t.CubismMotionCurveTarget_Parameter=1]="CubismMotionCurveTarget_Parameter",t[t.CubismMotionCurveTarget_PartOpacity=2]="CubismMotionCurveTarget_PartOpacity",t))(N||{}),V=(t=>(t[t.CubismMotionSegmentType_Linear=0]="CubismMotionSegmentType_Linear",t[t.CubismMotionSegmentType_Bezier=1]="CubismMotionSegmentType_Bezier",t[t.CubismMotionSegmentType_Stepped=2]="CubismMotionSegmentType_Stepped",t[t.CubismMotionSegmentType_InverseStepped=3]="CubismMotionSegmentType_InverseStepped",t))(V||{});class j{constructor(t=0,e=0){this.time=t,this.value=e}}class G{constructor(){this.basePointIndex=0,this.segmentType=0}}class X{constructor(){this.id="",this.type=0,this.segmentCount=0,this.baseSegmentIndex=0,this.fadeInTime=0,this.fadeOutTime=0}}class z{constructor(){this.fireTime=0,this.value=""}}class W{constructor(){this.duration=0,this.loop=!1,this.curveCount=0,this.eventCount=0,this.fps=0,this.curves=[],this.segments=[],this.points=[],this.events=[]}}class H{constructor(t){this._json=t}release(){this._json=void 0}getMotionDuration(){return this._json.Meta.Duration}isMotionLoop(){return this._json.Meta.Loop||!1}getEvaluationOptionFlag(t){return Y.EvaluationOptionFlag_AreBeziersRistricted==t&&!!this._json.Meta.AreBeziersRestricted}getMotionCurveCount(){return this._json.Meta.CurveCount}getMotionFps(){return this._json.Meta.Fps}getMotionTotalSegmentCount(){return this._json.Meta.TotalSegmentCount}getMotionTotalPointCount(){return this._json.Meta.TotalPointCount}getMotionFadeInTime(){return this._json.Meta.FadeInTime}getMotionFadeOutTime(){return this._json.Meta.FadeOutTime}getMotionCurveTarget(t){return this._json.Curves[t].Target}getMotionCurveId(t){return this._json.Curves[t].Id}getMotionCurveFadeInTime(t){return this._json.Curves[t].FadeInTime}getMotionCurveFadeOutTime(t){return this._json.Curves[t].FadeOutTime}getMotionCurveSegmentCount(t){return this._json.Curves[t].Segments.length}getMotionCurveSegment(t,e){return this._json.Curves[t].Segments[e]}getEventCount(){return this._json.Meta.UserDataCount||0}getTotalEventValueSize(){return this._json.Meta.TotalUserDataSize}getEventTime(t){return this._json.UserData[t].Time}getEventValue(t){return this._json.UserData[t].Value}}var Y=(t=>(t[t.EvaluationOptionFlag_AreBeziersRistricted=0]="EvaluationOptionFlag_AreBeziersRistricted",t))(Y||{});function q(t,e,i){const s=new j;return s.time=t.time+(e.time-t.time)*i,s.value=t.value+(e.value-t.value)*i,s}function $(t,e){let i=(e-t[0].time)/(t[1].time-t[0].time);return i<0&&(i=0),t[0].value+(t[1].value-t[0].value)*i}function Z(t,e){let i=(e-t[0].time)/(t[3].time-t[0].time);i<0&&(i=0);const s=q(t[0],t[1],i),r=q(t[1],t[2],i),a=q(t[2],t[3],i),o=q(s,r,i),n=q(r,a,i);return q(o,n,i).value}function J(t,e){const i=e,s=t[0].time,r=t[3].time,a=t[1].time,o=t[2].time,n=r-3*o+3*a-s,l=3*o-6*a+3*s,h=3*a-3*s,u=s-i,d=g.cardanoAlgorithmForBezier(n,l,h,u),c=q(t[0],t[1],d),m=q(t[1],t[2],d),p=q(t[2],t[3],d),_=q(c,m,d),f=q(m,p,d);return q(_,f,d).value}function Q(t,e){return t[0].value}function K(t,e){return t[1].value}function tt(t,e,i){const s=t.curves[e];let r=-1;const a=s.baseSegmentIndex+s.segmentCount;let o=0;for(let l=s.baseSegmentIndex;l<a;++l)if(o=t.segments[l].basePointIndex+(t.segments[l].segmentType==V.CubismMotionSegmentType_Bezier?3:1),t.points[o].time>i){r=l;break}if(-1==r)return t.points[o].value;const n=t.segments[r];return n.evaluate(t.points.slice(n.basePointIndex),i)}class et extends R{constructor(){super(),this._eyeBlinkParameterIds=[],this._lipSyncParameterIds=[],this._sourceFrameRate=30,this._loopDurationSeconds=-1,this._isLoop=!1,this._isLoopFadeIn=!0,this._lastWeight=0}static create(t,e){const i=new et;return i.parse(t),i._sourceFrameRate=i._motionData.fps,i._loopDurationSeconds=i._motionData.duration,i._onFinishedMotion=e,i}doUpdateParameters(e,i,s,r){null==this._modelCurveIdEyeBlink&&(this._modelCurveIdEyeBlink="EyeBlink"),null==this._modelCurveIdLipSync&&(this._modelCurveIdLipSync="LipSync");let a=i-r.getStartTime();a<0&&(a=0);let o=Number.MAX_VALUE,n=Number.MAX_VALUE;const l=64;let h=0,u=0;this._eyeBlinkParameterIds.length>l&&w("too many eye blink targets : {0}",this._eyeBlinkParameterIds.length),this._lipSyncParameterIds.length>l&&w("too many lip sync targets : {0}",this._lipSyncParameterIds.length);const d=this._fadeInSeconds<=0?1:g.getEasingSine((i-r.getFadeInStartTime())/this._fadeInSeconds),c=this._fadeOutSeconds<=0||r.getEndTime()<0?1:g.getEasingSine((r.getEndTime()-i)/this._fadeOutSeconds);let m,p,_,f=a;if(this._isLoop)for(;f>this._motionData.duration;)f-=this._motionData.duration;const y=this._motionData.curves;for(p=0;p<this._motionData.curveCount&&y[p].type==N.CubismMotionCurveTarget_Model;++p)m=tt(this._motionData,p,f),y[p].id==this._modelCurveIdEyeBlink?n=m:y[p].id==this._modelCurveIdLipSync&&(o=m);for(;p<this._motionData.curveCount&&y[p].type==N.CubismMotionCurveTarget_Parameter;++p){if(_=e.getParameterIndex(y[p].id),-1==_)continue;const t=e.getParameterValueByIndex(_);if(m=tt(this._motionData,p,f),n!=Number.MAX_VALUE)for(let e=0;e<this._eyeBlinkParameterIds.length&&e<l;++e)if(this._eyeBlinkParameterIds[e]==y[p].id){m*=n,u|=1<<e;break}if(o!=Number.MAX_VALUE)for(let e=0;e<this._lipSyncParameterIds.length&&e<l;++e)if(this._lipSyncParameterIds[e]==y[p].id){m+=o,h|=1<<e;break}let a;if(y[p].fadeInTime<0&&y[p].fadeOutTime<0)a=t+(m-t)*s;else{let e,s;e=y[p].fadeInTime<0?d:0==y[p].fadeInTime?1:g.getEasingSine((i-r.getFadeInStartTime())/y[p].fadeInTime),s=y[p].fadeOutTime<0?c:0==y[p].fadeOutTime||r.getEndTime()<0?1:g.getEasingSine((r.getEndTime()-i)/y[p].fadeOutTime);a=t+(m-t)*(this._weight*e*s)}e.setParameterValueByIndex(_,a,1)}if(n!=Number.MAX_VALUE)for(let t=0;t<this._eyeBlinkParameterIds.length&&t<l;++t){const i=e.getParameterValueById(this._eyeBlinkParameterIds[t]);if(u>>t&1)continue;const r=i+(n-i)*s;e.setParameterValueById(this._eyeBlinkParameterIds[t],r)}if(o!=Number.MAX_VALUE)for(let t=0;t<this._lipSyncParameterIds.length&&t<l;++t){const i=e.getParameterValueById(this._lipSyncParameterIds[t]);if(h>>t&1)continue;const r=i+(o-i)*s;e.setParameterValueById(this._lipSyncParameterIds[t],r)}for(;p<this._motionData.curveCount&&y[p].type==N.CubismMotionCurveTarget_PartOpacity;++p)if(m=tt(this._motionData,p,f),t.CubismConfig.setOpacityFromMotion)e.setPartOpacityById(y[p].id,m);else{if(_=e.getParameterIndex(y[p].id),-1==_)continue;e.setParameterValueByIndex(_,m)}a>=this._motionData.duration&&(this._isLoop?(r.setStartTime(i),this._isLoopFadeIn&&r.setFadeInStartTime(i)):(this._onFinishedMotion&&this._onFinishedMotion(this),r.setIsFinished(!0))),this._lastWeight=s}setIsLoop(t){this._isLoop=t}isLoop(){return this._isLoop}setIsLoopFadeIn(t){this._isLoopFadeIn=t}isLoopFadeIn(){return this._isLoopFadeIn}getDuration(){return this._isLoop?-1:this._loopDurationSeconds}getLoopDuration(){return this._loopDurationSeconds}setParameterFadeInTime(t,e){const i=this._motionData.curves;for(let s=0;s<this._motionData.curveCount;++s)if(t==i[s].id)return void(i[s].fadeInTime=e)}setParameterFadeOutTime(t,e){const i=this._motionData.curves;for(let s=0;s<this._motionData.curveCount;++s)if(t==i[s].id)return void(i[s].fadeOutTime=e)}getParameterFadeInTime(t){const e=this._motionData.curves;for(let i=0;i<this._motionData.curveCount;++i)if(t==e[i].id)return e[i].fadeInTime;return-1}getParameterFadeOutTime(t){const e=this._motionData.curves;for(let i=0;i<this._motionData.curveCount;++i)if(t==e[i].id)return e[i].fadeOutTime;return-1}setEffectIds(t,e){this._eyeBlinkParameterIds=t,this._lipSyncParameterIds=e}release(){this._motionData=void 0}parse(t){this._motionData=new W;let e=new H(t);this._motionData.duration=e.getMotionDuration(),this._motionData.loop=e.isMotionLoop(),this._motionData.curveCount=e.getMotionCurveCount(),this._motionData.fps=e.getMotionFps(),this._motionData.eventCount=e.getEventCount();const i=e.getEvaluationOptionFlag(Y.EvaluationOptionFlag_AreBeziersRistricted),s=e.getMotionFadeInTime(),r=e.getMotionFadeOutTime();this._fadeInSeconds=void 0!==s?s<0?1:s:1,this._fadeOutSeconds=void 0!==r?r<0?1:r:1,this._motionData.curves=Array.from({length:this._motionData.curveCount}).map((()=>new X)),this._motionData.segments=Array.from({length:e.getMotionTotalSegmentCount()}).map((()=>new G)),this._motionData.events=Array.from({length:this._motionData.eventCount}).map((()=>new z)),this._motionData.points=[];let a=0,o=0;for(let n=0;n<this._motionData.curveCount;++n){const t=this._motionData.curves[n];switch(e.getMotionCurveTarget(n)){case"Model":t.type=N.CubismMotionCurveTarget_Model;break;case"Parameter":t.type=N.CubismMotionCurveTarget_Parameter;break;case"PartOpacity":t.type=N.CubismMotionCurveTarget_PartOpacity;break;default:L('Warning : Unable to get segment type from Curve! The number of "CurveCount" may be incorrect!')}t.id=e.getMotionCurveId(n),t.baseSegmentIndex=o;const s=e.getMotionCurveFadeInTime(n),r=e.getMotionCurveFadeOutTime(n);t.fadeInTime=void 0!==s?s:-1,t.fadeOutTime=void 0!==r?r:-1;for(let l=0;l<e.getMotionCurveSegmentCount(n);){0==l?(this._motionData.segments[o].basePointIndex=a,this._motionData.points[a]=new j(e.getMotionCurveSegment(n,l),e.getMotionCurveSegment(n,l+1)),a+=1,l+=2):this._motionData.segments[o].basePointIndex=a-1;switch(e.getMotionCurveSegment(n,l)){case V.CubismMotionSegmentType_Linear:this._motionData.segments[o].segmentType=V.CubismMotionSegmentType_Linear,this._motionData.segments[o].evaluate=$,this._motionData.points[a]=new j(e.getMotionCurveSegment(n,l+1),e.getMotionCurveSegment(n,l+2)),a+=1,l+=3;break;case V.CubismMotionSegmentType_Bezier:this._motionData.segments[o].segmentType=V.CubismMotionSegmentType_Bezier,this._motionData.segments[o].evaluate=i?Z:J,this._motionData.points[a]=new j(e.getMotionCurveSegment(n,l+1),e.getMotionCurveSegment(n,l+2)),this._motionData.points[a+1]=new j(e.getMotionCurveSegment(n,l+3),e.getMotionCurveSegment(n,l+4)),this._motionData.points[a+2]=new j(e.getMotionCurveSegment(n,l+5),e.getMotionCurveSegment(n,l+6)),a+=3,l+=7;break;case V.CubismMotionSegmentType_Stepped:this._motionData.segments[o].segmentType=V.CubismMotionSegmentType_Stepped,this._motionData.segments[o].evaluate=Q,this._motionData.points[a]=new j(e.getMotionCurveSegment(n,l+1),e.getMotionCurveSegment(n,l+2)),a+=1,l+=3;break;case V.CubismMotionSegmentType_InverseStepped:this._motionData.segments[o].segmentType=V.CubismMotionSegmentType_InverseStepped,this._motionData.segments[o].evaluate=K,this._motionData.points[a]=new j(e.getMotionCurveSegment(n,l+1),e.getMotionCurveSegment(n,l+2)),a+=1,l+=3}++t.segmentCount,++o}this._motionData.curves.push(t)}for(let n=0;n<e.getEventCount();++n)this._motionData.events[n].fireTime=e.getEventTime(n),this._motionData.events[n].value=e.getEventValue(n);e.release()}getFiredEvent(t,e){this._firedEventValues.length=0;for(let i=0;i<this._motionData.eventCount;++i)this._motionData.events[i].fireTime>t&&this._motionData.events[i].fireTime<=e&&this._firedEventValues.push(this._motionData.events[i].value);return this._firedEventValues}}class it{constructor(){this._autoDelete=!1,this._available=!0,this._finished=!1,this._started=!1,this._startTimeSeconds=-1,this._fadeInStartTimeSeconds=0,this._endTimeSeconds=-1,this._stateTimeSeconds=0,this._stateWeight=0,this._lastEventCheckSeconds=0,this._motionQueueEntryHandle=this,this._fadeOutSeconds=0,this._isTriggeredFadeOut=!1}release(){this._autoDelete&&this._motion&&this._motion.release()}setFadeOut(t){this._fadeOutSeconds=t,this._isTriggeredFadeOut=!0}startFadeOut(t,e){const i=e+t;this._isTriggeredFadeOut=!0,(this._endTimeSeconds<0||i<this._endTimeSeconds)&&(this._endTimeSeconds=i)}isFinished(){return this._finished}isStarted(){return this._started}getStartTime(){return this._startTimeSeconds}getFadeInStartTime(){return this._fadeInStartTimeSeconds}getEndTime(){return this._endTimeSeconds}setStartTime(t){this._startTimeSeconds=t}setFadeInStartTime(t){this._fadeInStartTimeSeconds=t}setEndTime(t){this._endTimeSeconds=t}setIsFinished(t){this._finished=t}setIsStarted(t){this._started=t}isAvailable(){return this._available}setIsAvailable(t){this._available=t}setState(t,e){this._stateTimeSeconds=t,this._stateWeight=e}getStateTime(){return this._stateTimeSeconds}getStateWeight(){return this._stateWeight}getLastCheckEventSeconds(){return this._lastEventCheckSeconds}setLastCheckEventSeconds(t){this._lastEventCheckSeconds=t}isTriggeredFadeOut(){return this._isTriggeredFadeOut}getFadeOutSeconds(){return this._fadeOutSeconds}}class st{constructor(){this._userTimeSeconds=0,this._eventCustomData=null,this._motions=[]}release(){for(let t=0;t<this._motions.length;++t)this._motions[t]&&this._motions[t].release();this._motions=void 0}startMotion(t,e,i){if(null==t)return rt;let s;for(let r=0;r<this._motions.length;++r)s=this._motions[r],null!=s&&s.setFadeOut(s._motion.getFadeOutTime());return s=new it,s._autoDelete=e,s._motion=t,this._motions.push(s),s._motionQueueEntryHandle}isFinished(){let t=0;for(;t<this._motions.length;){const e=this._motions[t];if(null==e){this._motions.splice(t,1);continue}if(null!=e._motion){if(!e.isFinished())return!1;t++}else e.release(),this._motions.splice(t,1)}return!0}isFinishedByHandle(t){for(let e=0;e<this._motions.length;e++){const i=this._motions[e];if(null!=i&&(i._motionQueueEntryHandle==t&&!i.isFinished()))return!1}return!0}stopAllMotions(){for(let t=0;t<this._motions.length;t++){const e=this._motions[t];null!=e&&e.release()}this._motions=[]}getCubismMotionQueueEntry(t){return this._motions.find((e=>null!=e&&e._motionQueueEntryHandle==t))}setEventCallback(t,e=null){this._eventCallBack=t,this._eventCustomData=e}doUpdateMotion(t,e){let i=!1,s=0;for(;s<this._motions.length;){const r=this._motions[s];if(null==r){this._motions.splice(s,1);continue}const a=r._motion;if(null==a){r.release(),this._motions.splice(s,1);continue}a.updateParameters(t,r,e),i=!0;const o=a.getFiredEvent(r.getLastCheckEventSeconds()-r.getStartTime(),e-r.getStartTime());for(let t=0;t<o.length;++t)this._eventCallBack(this,o[t],this._eventCustomData);r.setLastCheckEventSeconds(e),r.isFinished()?(r.release(),this._motions.splice(s,1)):(r.isTriggeredFadeOut()&&r.startFadeOut(r.getFadeOutSeconds(),e),s++)}return i}}const rt=-1;var at=(t=>(t[t.CubismPhysicsTargetType_Parameter=0]="CubismPhysicsTargetType_Parameter",t))(at||{}),ot=(t=>(t[t.CubismPhysicsSource_X=0]="CubismPhysicsSource_X",t[t.CubismPhysicsSource_Y=1]="CubismPhysicsSource_Y",t[t.CubismPhysicsSource_Angle=2]="CubismPhysicsSource_Angle",t))(ot||{});class nt{constructor(){this.initialPosition=new c(0,0),this.position=new c(0,0),this.lastPosition=new c(0,0),this.lastGravity=new c(0,0),this.force=new c(0,0),this.velocity=new c(0,0)}}class lt{constructor(){this.normalizationPosition={},this.normalizationAngle={}}}class ht{constructor(){this.source={}}}class ut{constructor(){this.destination={},this.translationScale=new c(0,0)}}class dt{constructor(){this.settings=[],this.inputs=[],this.outputs=[],this.particles=[],this.gravity=new c(0,0),this.wind=new c(0,0)}}class ct{constructor(t){this._json=t}release(){this._json=void 0}getGravity(){const t=new c(0,0);return t.x=this._json.Meta.EffectiveForces.Gravity.X,t.y=this._json.Meta.EffectiveForces.Gravity.Y,t}getWind(){const t=new c(0,0);return t.x=this._json.Meta.EffectiveForces.Wind.X,t.y=this._json.Meta.EffectiveForces.Wind.Y,t}getSubRigCount(){return this._json.Meta.PhysicsSettingCount}getTotalInputCount(){return this._json.Meta.TotalInputCount}getTotalOutputCount(){return this._json.Meta.TotalOutputCount}getVertexCount(){return this._json.Meta.VertexCount}getNormalizationPositionMinimumValue(t){return this._json.PhysicsSettings[t].Normalization.Position.Minimum}getNormalizationPositionMaximumValue(t){return this._json.PhysicsSettings[t].Normalization.Position.Maximum}getNormalizationPositionDefaultValue(t){return this._json.PhysicsSettings[t].Normalization.Position.Default}getNormalizationAngleMinimumValue(t){return this._json.PhysicsSettings[t].Normalization.Angle.Minimum}getNormalizationAngleMaximumValue(t){return this._json.PhysicsSettings[t].Normalization.Angle.Maximum}getNormalizationAngleDefaultValue(t){return this._json.PhysicsSettings[t].Normalization.Angle.Default}getInputCount(t){return this._json.PhysicsSettings[t].Input.length}getInputWeight(t,e){return this._json.PhysicsSettings[t].Input[e].Weight}getInputReflect(t,e){return this._json.PhysicsSettings[t].Input[e].Reflect}getInputType(t,e){return this._json.PhysicsSettings[t].Input[e].Type}getInputSourceId(t,e){return this._json.PhysicsSettings[t].Input[e].Source.Id}getOutputCount(t){return this._json.PhysicsSettings[t].Output.length}getOutputVertexIndex(t,e){return this._json.PhysicsSettings[t].Output[e].VertexIndex}getOutputAngleScale(t,e){return this._json.PhysicsSettings[t].Output[e].Scale}getOutputWeight(t,e){return this._json.PhysicsSettings[t].Output[e].Weight}getOutputDestinationId(t,e){return this._json.PhysicsSettings[t].Output[e].Destination.Id}getOutputType(t,e){return this._json.PhysicsSettings[t].Output[e].Type}getOutputReflect(t,e){return this._json.PhysicsSettings[t].Output[e].Reflect}getParticleCount(t){return this._json.PhysicsSettings[t].Vertices.length}getParticleMobility(t,e){return this._json.PhysicsSettings[t].Vertices[e].Mobility}getParticleDelay(t,e){return this._json.PhysicsSettings[t].Vertices[e].Delay}getParticleAcceleration(t,e){return this._json.PhysicsSettings[t].Vertices[e].Acceleration}getParticleRadius(t,e){return this._json.PhysicsSettings[t].Vertices[e].Radius}getParticlePosition(t,e){const i=new c(0,0);return i.x=this._json.PhysicsSettings[t].Vertices[e].Position.X,i.y=this._json.PhysicsSettings[t].Vertices[e].Position.Y,i}}const mt="Angle";class gt{static create(t){const e=new gt;return e.parse(t),e._physicsRig.gravity.y=0,e}evaluate(t,e){let i,s,r,a;const o=new c;let n,l,h,u,d,m,p,_;d=t.getModel().parameters.values,m=t.getModel().parameters.maximumValues,p=t.getModel().parameters.minimumValues,_=t.getModel().parameters.defaultValues;for(let f=0;f<this._physicsRig.subRigCount;++f){i={angle:0},o.x=0,o.y=0,n=this._physicsRig.settings[f],l=this._physicsRig.inputs.slice(n.baseInputIndex),h=this._physicsRig.outputs.slice(n.baseOutputIndex),u=this._physicsRig.particles.slice(n.baseParticleIndex);for(let e=0;e<n.inputCount;++e)s=l[e].weight/100,-1==l[e].sourceParameterIndex&&(l[e].sourceParameterIndex=t.getParameterIndex(l[e].source.id)),l[e].getNormalizedParameterValue(o,i,d[l[e].sourceParameterIndex],p[l[e].sourceParameterIndex],m[l[e].sourceParameterIndex],_[l[e].sourceParameterIndex],n.normalizationPosition,n.normalizationAngle,l[e].reflect,s);r=g.degreesToRadian(-i.angle),o.x=o.x*g.cos(r)-o.y*g.sin(r),o.y=o.x*g.sin(r)+o.y*g.cos(r),wt(u,n.particleCount,o,i.angle,this._options.wind,.001*n.normalizationPosition.maximum,e,5);for(let e=0;e<n.outputCount;++e){const i=h[e].vertexIndex;if(i<1||i>=n.particleCount)break;-1==h[e].destinationParameterIndex&&(h[e].destinationParameterIndex=t.getParameterIndex(h[e].destination.id));const s=new c;s.x=u[i].position.x-u[i-1].position.x,s.y=u[i].position.y-u[i-1].position.y,a=h[e].getValue(s,u,i,h[e].reflect,this._options.gravity);const r=h[e].destinationParameterIndex,o=!Float32Array.prototype.slice&&"subarray"in Float32Array.prototype?JSON.parse(JSON.stringify(d.subarray(r))):d.slice(r);It(o,p[r],m[r],a,h[e]);for(let t=r,e=0;t<d.length;t++,e++)d[t]=o[e]}}}setOptions(t){this._options=t}getOption(){return this._options}constructor(){this._options=new pt,this._options.gravity.y=-1,this._options.gravity.x=0,this._options.wind.x=0,this._options.wind.y=0}release(){this._physicsRig=void 0}parse(t){this._physicsRig=new dt;let e=new ct(t);this._physicsRig.gravity=e.getGravity(),this._physicsRig.wind=e.getWind(),this._physicsRig.subRigCount=e.getSubRigCount();let i=0,s=0,r=0;for(let a=0;a<this._physicsRig.subRigCount;++a){const t=new lt;t.normalizationPosition.minimum=e.getNormalizationPositionMinimumValue(a),t.normalizationPosition.maximum=e.getNormalizationPositionMaximumValue(a),t.normalizationPosition.defalut=e.getNormalizationPositionDefaultValue(a),t.normalizationAngle.minimum=e.getNormalizationAngleMinimumValue(a),t.normalizationAngle.maximum=e.getNormalizationAngleMaximumValue(a),t.normalizationAngle.defalut=e.getNormalizationAngleDefaultValue(a),t.inputCount=e.getInputCount(a),t.baseInputIndex=i,i+=t.inputCount;for(let i=0;i<t.inputCount;++i){const t=new ht;switch(t.sourceParameterIndex=-1,t.weight=e.getInputWeight(a,i),t.reflect=e.getInputReflect(a,i),e.getInputType(a,i)){case"X":t.type=ot.CubismPhysicsSource_X,t.getNormalizedParameterValue=_t;break;case"Y":t.type=ot.CubismPhysicsSource_Y,t.getNormalizedParameterValue=ft;break;case mt:t.type=ot.CubismPhysicsSource_Angle,t.getNormalizedParameterValue=yt}t.source.targetType=at.CubismPhysicsTargetType_Parameter,t.source.id=e.getInputSourceId(a,i),this._physicsRig.inputs.push(t)}t.outputCount=e.getOutputCount(a),t.baseOutputIndex=s,s+=t.outputCount;for(let i=0;i<t.outputCount;++i){const t=new ut;switch(t.destinationParameterIndex=-1,t.vertexIndex=e.getOutputVertexIndex(a,i),t.angleScale=e.getOutputAngleScale(a,i),t.weight=e.getOutputWeight(a,i),t.destination.targetType=at.CubismPhysicsTargetType_Parameter,t.destination.id=e.getOutputDestinationId(a,i),e.getOutputType(a,i)){case"X":t.type=ot.CubismPhysicsSource_X,t.getValue=xt,t.getScale=Ct;break;case"Y":t.type=ot.CubismPhysicsSource_Y,t.getValue=vt,t.getScale=St;break;case mt:t.type=ot.CubismPhysicsSource_Angle,t.getValue=Mt,t.getScale=bt}t.reflect=e.getOutputReflect(a,i),this._physicsRig.outputs.push(t)}t.particleCount=e.getParticleCount(a),t.baseParticleIndex=r,r+=t.particleCount;for(let i=0;i<t.particleCount;++i){const t=new nt;t.mobility=e.getParticleMobility(a,i),t.delay=e.getParticleDelay(a,i),t.acceleration=e.getParticleAcceleration(a,i),t.radius=e.getParticleRadius(a,i),t.position=e.getParticlePosition(a,i),this._physicsRig.particles.push(t)}this._physicsRig.settings.push(t)}this.initialize(),e.release()}initialize(){let t,e,i;for(let s=0;s<this._physicsRig.subRigCount;++s){e=this._physicsRig.settings[s],t=this._physicsRig.particles.slice(e.baseParticleIndex),t[0].initialPosition=new c(0,0),t[0].lastPosition=new c(t[0].initialPosition.x,t[0].initialPosition.y),t[0].lastGravity=new c(0,-1),t[0].lastGravity.y*=-1,t[0].velocity=new c(0,0),t[0].force=new c(0,0);for(let s=1;s<e.particleCount;++s)i=new c(0,0),i.y=t[s].radius,t[s].initialPosition=new c(t[s-1].initialPosition.x+i.x,t[s-1].initialPosition.y+i.y),t[s].position=new c(t[s].initialPosition.x,t[s].initialPosition.y),t[s].lastPosition=new c(t[s].initialPosition.x,t[s].initialPosition.y),t[s].lastGravity=new c(0,-1),t[s].lastGravity.y*=-1,t[s].velocity=new c(0,0),t[s].force=new c(0,0)}}}class pt{constructor(){this.gravity=new c(0,0),this.wind=new c(0,0)}}function _t(t,e,i,s,r,a,o,n,l,h){t.x+=Lt(i,s,r,a,o.minimum,o.maximum,o.defalut,l)*h}function ft(t,e,i,s,r,a,o,n,l,h){t.y+=Lt(i,s,r,a,o.minimum,o.maximum,o.defalut,l)*h}function yt(t,e,i,s,r,a,o,n,l,h){e.angle+=Lt(i,s,r,a,n.minimum,n.maximum,n.defalut,l)*h}function xt(t,e,i,s,r){let a=t.x;return s&&(a*=-1),a}function vt(t,e,i,s,r){let a=t.y;return s&&(a*=-1),a}function Mt(t,e,i,s,r){let a;return r=i>=2?e[i-1].position.substract(e[i-2].position):r.multiplyByScaler(-1),a=g.directionToRadian(r,t),s&&(a*=-1),a}function Pt(t,e){return Math.min(t,e)+function(t,e){return Math.abs(Math.max(t,e)-Math.min(t,e))}(t,e)/2}function Ct(t,e){return t.x}function St(t,e){return t.y}function bt(t,e){return e}function wt(t,e,i,s,r,a,o,n){let l,h,u,d,m=new c(0,0),p=new c(0,0),_=new c(0,0),f=new c(0,0);t[0].position=new c(i.x,i.y),l=g.degreesToRadian(s),d=g.radianToDirection(l),d.normalize();for(let y=1;y<e;++y)t[y].force=d.multiplyByScaler(t[y].acceleration).add(r),t[y].lastPosition=new c(t[y].position.x,t[y].position.y),h=t[y].delay*o*30,m=t[y].position.substract(t[y-1].position),u=g.directionToRadian(t[y].lastGravity,d)/n,m.x=g.cos(u)*m.x-m.y*g.sin(u),m.y=g.sin(u)*m.x+m.y*g.cos(u),t[y].position=t[y-1].position.add(m),p=t[y].velocity.multiplyByScaler(h),_=t[y].force.multiplyByScaler(h).multiplyByScaler(h),t[y].position=t[y].position.add(p).add(_),f=t[y].position.substract(t[y-1].position),f.normalize(),t[y].position=t[y-1].position.add(f.multiplyByScaler(t[y].radius)),g.abs(t[y].position.x)<a&&(t[y].position.x=0),0!=h&&(t[y].velocity=t[y].position.substract(t[y].lastPosition),t[y].velocity=t[y].velocity.divisionByScalar(h),t[y].velocity=t[y].velocity.multiplyByScaler(t[y].mobility)),t[y].force=new c(0,0),t[y].lastGravity=new c(d.x,d.y)}function It(t,e,i,s,r){let a,o,n;a=r.getScale(r.translationScale,r.angleScale),o=s*a,o<e?(o<r.valueBelowMinimum&&(r.valueBelowMinimum=o),o=e):o>i&&(o>r.valueExceededMaximum&&(r.valueExceededMaximum=o),o=i),n=r.weight/100,n>=1||(o=t[0]*(1-n)+o*n),t[0]=o}function Lt(t,e,i,s,r,a,o,n){let l=0;const h=g.max(i,e);h<t&&(t=h);const u=g.min(i,e);u>t&&(t=u);const d=g.min(r,a),c=g.max(r,a),m=o,p=Pt(u,h),_=t-p;switch(Math.sign(_)){case 1:{const t=c-m,e=h-p;0!=e&&(l=_*(t/e),l+=m);break}case-1:{const t=d-m,e=u-p;0!=e&&(l=_*(t/e),l+=m);break}case 0:l=m}return n?l:-1*l}class Tt{constructor(t=0,e=0,i=0,s=0){this.x=t,this.y=e,this.width=i,this.height=s}getCenterX(){return this.x+.5*this.width}getCenterY(){return this.y+.5*this.height}getRight(){return this.x+this.width}getBottom(){return this.y+this.height}setRect(t){this.x=t.x,this.y=t.y,this.width=t.width,this.height=t.height}expand(t,e){this.x-=t,this.y-=e,this.width+=2*t,this.height+=2*e}}let Et,Dt,Ft;class At{getChannelFlagAsColor(t){return this._channelColors[t]}getMaskRenderTexture(){let t=0;if(this._maskTexture&&0!=this._maskTexture.texture&&(this._maskTexture.frameNo=this._currentFrameNo,t=this._maskTexture.texture),0==t){const e=this._clippingMaskBufferSize;this._colorBuffer=this.gl.createTexture(),this.gl.bindTexture(this.gl.TEXTURE_2D,this._colorBuffer),this.gl.texImage2D(this.gl.TEXTURE_2D,0,this.gl.RGBA,e,e,0,this.gl.RGBA,this.gl.UNSIGNED_BYTE,null),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MAG_FILTER,this.gl.LINEAR),this.gl.bindTexture(this.gl.TEXTURE_2D,null),t=this.gl.createFramebuffer(),this.gl.bindFramebuffer(this.gl.FRAMEBUFFER,t),this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER,this.gl.COLOR_ATTACHMENT0,this.gl.TEXTURE_2D,this._colorBuffer,0),this.gl.bindFramebuffer(this.gl.FRAMEBUFFER,Ft),this._maskTexture=new Bt(this._currentFrameNo,t)}return t}setGL(t){this.gl=t}calcClippedDrawTotalBounds(t,e){let i=Number.MAX_VALUE,s=Number.MAX_VALUE,r=Number.MIN_VALUE,a=Number.MIN_VALUE;const o=e._clippedDrawableIndexList.length;for(let n=0;n<o;n++){const o=e._clippedDrawableIndexList[n],l=t.getDrawableVertexCount(o),h=t.getDrawableVertices(o);let u=Number.MAX_VALUE,d=Number.MAX_VALUE,c=Number.MIN_VALUE,m=Number.MIN_VALUE;const g=l*P.vertexStep;for(let t=P.vertexOffset;t<g;t+=P.vertexStep){const e=h[t],i=h[t+1];e<u&&(u=e),e>c&&(c=e),i<d&&(d=i),i>m&&(m=i)}if(u!=Number.MAX_VALUE)if(u<i&&(i=u),d<s&&(s=d),c>r&&(r=c),m>a&&(a=m),i==Number.MAX_VALUE)e._allClippedDrawRect.x=0,e._allClippedDrawRect.y=0,e._allClippedDrawRect.width=0,e._allClippedDrawRect.height=0,e._isUsing=!1;else{e._isUsing=!0;const t=r-i,o=a-s;e._allClippedDrawRect.x=i,e._allClippedDrawRect.y=s,e._allClippedDrawRect.width=t,e._allClippedDrawRect.height=o}}}constructor(){this._maskRenderTexture=null,this._colorBuffer=null,this._currentFrameNo=0,this._clippingMaskBufferSize=256,this._clippingContextListForMask=[],this._clippingContextListForDraw=[],this._channelColors=[],this._tmpBoundsOnModel=new Tt,this._tmpMatrix=new p,this._tmpMatrixForMask=new p,this._tmpMatrixForDraw=new p;let t=new y;t.R=1,t.G=0,t.B=0,t.A=0,this._channelColors.push(t),t=new y,t.R=0,t.G=1,t.B=0,t.A=0,this._channelColors.push(t),t=new y,t.R=0,t.G=0,t.B=1,t.A=0,this._channelColors.push(t),t=new y,t.R=0,t.G=0,t.B=0,t.A=1,this._channelColors.push(t)}release(){var t,e,i;const s=this;for(let r=0;r<this._clippingContextListForMask.length;r++)this._clippingContextListForMask[r]&&(null==(t=this._clippingContextListForMask[r])||t.release());s._clippingContextListForMask=void 0,s._clippingContextListForDraw=void 0,this._maskTexture&&(null==(e=this.gl)||e.deleteFramebuffer(this._maskTexture.texture),s._maskTexture=void 0),s._channelColors=void 0,null==(i=this.gl)||i.deleteTexture(this._colorBuffer),this._colorBuffer=null}initialize(t,e,i,s){for(let r=0;r<e;r++){if(s[r]<=0){this._clippingContextListForDraw.push(null);continue}let t=this.findSameClip(i[r],s[r]);null==t&&(t=new Rt(this,i[r],s[r]),this._clippingContextListForMask.push(t)),t.addClippedDrawable(r),this._clippingContextListForDraw.push(t)}}setupClippingContext(t,e){this._currentFrameNo++;let i=0;for(let s=0;s<this._clippingContextListForMask.length;s++){const e=this._clippingContextListForMask[s];this.calcClippedDrawTotalBounds(t,e),e._isUsing&&i++}if(i>0){this.gl.viewport(0,0,this._clippingMaskBufferSize,this._clippingMaskBufferSize),this._maskRenderTexture=this.getMaskRenderTexture(),e.getMvpMatrix(),e.preDraw(),this.setupLayoutBounds(i),this.gl.bindFramebuffer(this.gl.FRAMEBUFFER,this._maskRenderTexture),this.gl.clearColor(1,1,1,1),this.gl.clear(this.gl.COLOR_BUFFER_BIT);for(let i=0;i<this._clippingContextListForMask.length;i++){const s=this._clippingContextListForMask[i],r=s._allClippedDrawRect,a=s._layoutBounds,o=.05;this._tmpBoundsOnModel.setRect(r),this._tmpBoundsOnModel.expand(r.width*o,r.height*o);const n=a.width/this._tmpBoundsOnModel.width,l=a.height/this._tmpBoundsOnModel.height;this._tmpMatrix.loadIdentity(),this._tmpMatrix.translateRelative(-1,-1),this._tmpMatrix.scaleRelative(2,2),this._tmpMatrix.translateRelative(a.x,a.y),this._tmpMatrix.scaleRelative(n,l),this._tmpMatrix.translateRelative(-this._tmpBoundsOnModel.x,-this._tmpBoundsOnModel.y),this._tmpMatrixForMask.setMatrix(this._tmpMatrix.getArray()),this._tmpMatrix.loadIdentity(),this._tmpMatrix.translateRelative(a.x,a.y),this._tmpMatrix.scaleRelative(n,l),this._tmpMatrix.translateRelative(-this._tmpBoundsOnModel.x,-this._tmpBoundsOnModel.y),this._tmpMatrixForDraw.setMatrix(this._tmpMatrix.getArray()),s._matrixForMask.setMatrix(this._tmpMatrixForMask.getArray()),s._matrixForDraw.setMatrix(this._tmpMatrixForDraw.getArray());const h=s._clippingIdCount;for(let i=0;i<h;i++){const r=s._clippingIdList[i];t.getDrawableDynamicFlagVertexPositionsDidChange(r)&&(e.setIsCulling(0!=t.getDrawableCulling(r)),e.setClippingContextBufferForMask(s),e.drawMesh(t.getDrawableTextureIndices(r),t.getDrawableVertexIndexCount(r),t.getDrawableVertexCount(r),t.getDrawableVertexIndices(r),t.getDrawableVertices(r),t.getDrawableVertexUvs(r),t.getDrawableOpacity(r),f.CubismBlendMode_Normal,!1))}}this.gl.bindFramebuffer(this.gl.FRAMEBUFFER,Ft),e.setClippingContextBufferForMask(null),this.gl.viewport(Dt[0],Dt[1],Dt[2],Dt[3])}}findSameClip(t,e){for(let i=0;i<this._clippingContextListForMask.length;i++){const s=this._clippingContextListForMask[i],r=s._clippingIdCount;if(r!=e)continue;let a=0;for(let e=0;e<r;e++){const i=s._clippingIdList[e];for(let e=0;e<r;e++)if(t[e]==i){a++;break}}if(a==r)return s}return null}setupLayoutBounds(e){let i=e/4,s=e%4;i=~~i,s=~~s;let r=0;for(let a=0;a<4;a++){const e=i+(a<s?1:0);if(0==e);else if(1==e){const t=this._clippingContextListForMask[r++];t._layoutChannelNo=a,t._layoutBounds.x=0,t._layoutBounds.y=0,t._layoutBounds.width=1,t._layoutBounds.height=1}else if(2==e)for(let t=0;t<e;t++){let e=t%2;e=~~e;const i=this._clippingContextListForMask[r++];i._layoutChannelNo=a,i._layoutBounds.x=.5*e,i._layoutBounds.y=0,i._layoutBounds.width=.5,i._layoutBounds.height=1}else if(e<=4)for(let t=0;t<e;t++){let e=t%2,i=t/2;e=~~e,i=~~i;const s=this._clippingContextListForMask[r++];s._layoutChannelNo=a,s._layoutBounds.x=.5*e,s._layoutBounds.y=.5*i,s._layoutBounds.width=.5,s._layoutBounds.height=.5}else if(e<=9)for(let t=0;t<e;t++){let e=t%3,i=t/3;e=~~e,i=~~i;const s=this._clippingContextListForMask[r++];s._layoutChannelNo=a,s._layoutBounds.x=e/3,s._layoutBounds.y=i/3,s._layoutBounds.width=1/3,s._layoutBounds.height=1/3}else if(t.CubismConfig.supportMoreMaskDivisions&&e<=16)for(let t=0;t<e;t++){let e=t%4,i=t/4;e=~~e,i=~~i;const s=this._clippingContextListForMask[r++];s._layoutChannelNo=a,s._layoutBounds.x=e/4,s._layoutBounds.y=i/4,s._layoutBounds.width=1/4,s._layoutBounds.height=1/4}else T("not supported mask count : {0}",e)}}getColorBuffer(){return this._colorBuffer}getClippingContextListForDraw(){return this._clippingContextListForDraw}setClippingMaskBufferSize(t){this._clippingMaskBufferSize=t}getClippingMaskBufferSize(){return this._clippingMaskBufferSize}}class Bt{constructor(t,e){this.frameNo=t,this.texture=e}}class Rt{constructor(t,e,i){this._isUsing=!1,this._owner=t,this._clippingIdList=e,this._clippingIdCount=i,this._allClippedDrawRect=new Tt,this._layoutBounds=new Tt,this._clippedDrawableIndexList=[],this._matrixForMask=new p,this._matrixForDraw=new p}release(){const t=this;t._layoutBounds=void 0,t._allClippedDrawRect=void 0,t._clippedDrawableIndexList=void 0}addClippedDrawable(t){this._clippedDrawableIndexList.push(t)}getClippingManager(){return this._owner}setGl(t){this._owner.setGL(t)}}class Ot{static getInstance(){return null==Et?(Et=new Ot,Et):Et}static deleteInstance(){Et&&(Et.release(),Et=void 0)}constructor(){this._shaderSets=[]}release(){this.releaseShaderProgram()}setupShaderProgram(t,e,i,s,r,a,o,n,l,h,u,d,c){let m,g,p,_;u||T("NoPremultipliedAlpha is not allowed"),0==this._shaderSets.length&&this.generateShaders();const y=t.getClippingContextBufferForMask();if(null!=y){const t=this._shaderSets[kt.ShaderNames_SetupMask];this.gl.useProgram(t.shaderProgram),this.gl.activeTexture(this.gl.TEXTURE0),this.gl.bindTexture(this.gl.TEXTURE_2D,e),this.gl.uniform1i(t.samplerTexture0Location,0),null==o.vertex&&(o.vertex=this.gl.createBuffer()),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,o.vertex),this.gl.bufferData(this.gl.ARRAY_BUFFER,s,this.gl.DYNAMIC_DRAW),this.gl.enableVertexAttribArray(t.attributePositionLocation),this.gl.vertexAttribPointer(t.attributePositionLocation,2,this.gl.FLOAT,!1,0,0),null==o.uv&&(o.uv=this.gl.createBuffer()),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,o.uv),this.gl.bufferData(this.gl.ARRAY_BUFFER,a,this.gl.DYNAMIC_DRAW),this.gl.enableVertexAttribArray(t.attributeTexCoordLocation),this.gl.vertexAttribPointer(t.attributeTexCoordLocation,2,this.gl.FLOAT,!1,0,0);const i=y._layoutChannelNo,r=y.getClippingManager().getChannelFlagAsColor(i);this.gl.uniform4f(t.uniformChannelFlagLocation,r.R,r.G,r.B,r.A),this.gl.uniformMatrix4fv(t.uniformClipMatrixLocation,!1,y._matrixForMask.getArray());const n=y._layoutBounds;this.gl.uniform4f(t.uniformBaseColorLocation,2*n.x-1,2*n.y-1,2*n.getRight()-1,2*n.getBottom()-1),m=this.gl.ZERO,g=this.gl.ONE_MINUS_SRC_COLOR,p=this.gl.ZERO,_=this.gl.ONE_MINUS_SRC_ALPHA}else{const i=t.getClippingContextBufferForDraw(),r=null!=i?c?2:1:0;let n;switch(l){case f.CubismBlendMode_Normal:default:n=this._shaderSets[kt.ShaderNames_NormalPremultipliedAlpha+r],m=this.gl.ONE,g=this.gl.ONE_MINUS_SRC_ALPHA,p=this.gl.ONE,_=this.gl.ONE_MINUS_SRC_ALPHA;break;case f.CubismBlendMode_Additive:n=this._shaderSets[kt.ShaderNames_AddPremultipliedAlpha+r],m=this.gl.ONE,g=this.gl.ONE,p=this.gl.ZERO,_=this.gl.ONE;break;case f.CubismBlendMode_Multiplicative:n=this._shaderSets[kt.ShaderNames_MultPremultipliedAlpha+r],m=this.gl.DST_COLOR,g=this.gl.ONE_MINUS_SRC_ALPHA,p=this.gl.ZERO,_=this.gl.ONE}if(this.gl.useProgram(n.shaderProgram),null==o.vertex&&(o.vertex=this.gl.createBuffer()),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,o.vertex),this.gl.bufferData(this.gl.ARRAY_BUFFER,s,this.gl.DYNAMIC_DRAW),this.gl.enableVertexAttribArray(n.attributePositionLocation),this.gl.vertexAttribPointer(n.attributePositionLocation,2,this.gl.FLOAT,!1,0,0),null==o.uv&&(o.uv=this.gl.createBuffer()),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,o.uv),this.gl.bufferData(this.gl.ARRAY_BUFFER,a,this.gl.DYNAMIC_DRAW),this.gl.enableVertexAttribArray(n.attributeTexCoordLocation),this.gl.vertexAttribPointer(n.attributeTexCoordLocation,2,this.gl.FLOAT,!1,0,0),null!=i){this.gl.activeTexture(this.gl.TEXTURE1);const t=i.getClippingManager().getColorBuffer();this.gl.bindTexture(this.gl.TEXTURE_2D,t),this.gl.uniform1i(n.samplerTexture1Location,1),this.gl.uniformMatrix4fv(n.uniformClipMatrixLocation,!1,i._matrixForDraw.getArray());const e=i._layoutChannelNo,s=i.getClippingManager().getChannelFlagAsColor(e);this.gl.uniform4f(n.uniformChannelFlagLocation,s.R,s.G,s.B,s.A)}this.gl.activeTexture(this.gl.TEXTURE0),this.gl.bindTexture(this.gl.TEXTURE_2D,e),this.gl.uniform1i(n.samplerTexture0Location,0),this.gl.uniformMatrix4fv(n.uniformMatrixLocation,!1,d.getArray()),this.gl.uniform4f(n.uniformBaseColorLocation,h.R,h.G,h.B,h.A)}null==o.index&&(o.index=this.gl.createBuffer()),this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER,o.index),this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER,r,this.gl.DYNAMIC_DRAW),this.gl.blendFuncSeparate(m,g,p,_)}releaseShaderProgram(){for(let t=0;t<this._shaderSets.length;t++)this.gl.deleteProgram(this._shaderSets[t].shaderProgram),this._shaderSets[t].shaderProgram=0;this._shaderSets=[]}generateShaders(){for(let t=0;t<10;t++)this._shaderSets.push({});this._shaderSets[0].shaderProgram=this.loadShaderProgram(Ut,Nt),this._shaderSets[1].shaderProgram=this.loadShaderProgram(Vt,Gt),this._shaderSets[2].shaderProgram=this.loadShaderProgram(jt,Xt),this._shaderSets[3].shaderProgram=this.loadShaderProgram(jt,zt),this._shaderSets[4].shaderProgram=this._shaderSets[1].shaderProgram,this._shaderSets[5].shaderProgram=this._shaderSets[2].shaderProgram,this._shaderSets[6].shaderProgram=this._shaderSets[3].shaderProgram,this._shaderSets[7].shaderProgram=this._shaderSets[1].shaderProgram,this._shaderSets[8].shaderProgram=this._shaderSets[2].shaderProgram,this._shaderSets[9].shaderProgram=this._shaderSets[3].shaderProgram,this._shaderSets[0].attributePositionLocation=this.gl.getAttribLocation(this._shaderSets[0].shaderProgram,"a_position"),this._shaderSets[0].attributeTexCoordLocation=this.gl.getAttribLocation(this._shaderSets[0].shaderProgram,"a_texCoord"),this._shaderSets[0].samplerTexture0Location=this.gl.getUniformLocation(this._shaderSets[0].shaderProgram,"s_texture0"),this._shaderSets[0].uniformClipMatrixLocation=this.gl.getUniformLocation(this._shaderSets[0].shaderProgram,"u_clipMatrix"),this._shaderSets[0].uniformChannelFlagLocation=this.gl.getUniformLocation(this._shaderSets[0].shaderProgram,"u_channelFlag"),this._shaderSets[0].uniformBaseColorLocation=this.gl.getUniformLocation(this._shaderSets[0].shaderProgram,"u_baseColor"),this._shaderSets[1].attributePositionLocation=this.gl.getAttribLocation(this._shaderSets[1].shaderProgram,"a_position"),this._shaderSets[1].attributeTexCoordLocation=this.gl.getAttribLocation(this._shaderSets[1].shaderProgram,"a_texCoord"),this._shaderSets[1].samplerTexture0Location=this.gl.getUniformLocation(this._shaderSets[1].shaderProgram,"s_texture0"),this._shaderSets[1].uniformMatrixLocation=this.gl.getUniformLocation(this._shaderSets[1].shaderProgram,"u_matrix"),this._shaderSets[1].uniformBaseColorLocation=this.gl.getUniformLocation(this._shaderSets[1].shaderProgram,"u_baseColor"),this._shaderSets[2].attributePositionLocation=this.gl.getAttribLocation(this._shaderSets[2].shaderProgram,"a_position"),this._shaderSets[2].attributeTexCoordLocation=this.gl.getAttribLocation(this._shaderSets[2].shaderProgram,"a_texCoord"),this._shaderSets[2].samplerTexture0Location=this.gl.getUniformLocation(this._shaderSets[2].shaderProgram,"s_texture0"),this._shaderSets[2].samplerTexture1Location=this.gl.getUniformLocation(this._shaderSets[2].shaderProgram,"s_texture1"),this._shaderSets[2].uniformMatrixLocation=this.gl.getUniformLocation(this._shaderSets[2].shaderProgram,"u_matrix"),this._shaderSets[2].uniformClipMatrixLocation=this.gl.getUniformLocation(this._shaderSets[2].shaderProgram,"u_clipMatrix"),this._shaderSets[2].uniformChannelFlagLocation=this.gl.getUniformLocation(this._shaderSets[2].shaderProgram,"u_channelFlag"),this._shaderSets[2].uniformBaseColorLocation=this.gl.getUniformLocation(this._shaderSets[2].shaderProgram,"u_baseColor"),this._shaderSets[3].attributePositionLocation=this.gl.getAttribLocation(this._shaderSets[3].shaderProgram,"a_position"),this._shaderSets[3].attributeTexCoordLocation=this.gl.getAttribLocation(this._shaderSets[3].shaderProgram,"a_texCoord"),this._shaderSets[3].samplerTexture0Location=this.gl.getUniformLocation(this._shaderSets[3].shaderProgram,"s_texture0"),this._shaderSets[3].samplerTexture1Location=this.gl.getUniformLocation(this._shaderSets[3].shaderProgram,"s_texture1"),this._shaderSets[3].uniformMatrixLocation=this.gl.getUniformLocation(this._shaderSets[3].shaderProgram,"u_matrix"),this._shaderSets[3].uniformClipMatrixLocation=this.gl.getUniformLocation(this._shaderSets[3].shaderProgram,"u_clipMatrix"),this._shaderSets[3].uniformChannelFlagLocation=this.gl.getUniformLocation(this._shaderSets[3].shaderProgram,"u_channelFlag"),this._shaderSets[3].uniformBaseColorLocation=this.gl.getUniformLocation(this._shaderSets[3].shaderProgram,"u_baseColor"),this._shaderSets[4].attributePositionLocation=this.gl.getAttribLocation(this._shaderSets[4].shaderProgram,"a_position"),this._shaderSets[4].attributeTexCoordLocation=this.gl.getAttribLocation(this._shaderSets[4].shaderProgram,"a_texCoord"),this._shaderSets[4].samplerTexture0Location=this.gl.getUniformLocation(this._shaderSets[4].shaderProgram,"s_texture0"),this._shaderSets[4].uniformMatrixLocation=this.gl.getUniformLocation(this._shaderSets[4].shaderProgram,"u_matrix"),this._shaderSets[4].uniformBaseColorLocation=this.gl.getUniformLocation(this._shaderSets[4].shaderProgram,"u_baseColor"),this._shaderSets[5].attributePositionLocation=this.gl.getAttribLocation(this._shaderSets[5].shaderProgram,"a_position"),this._shaderSets[5].attributeTexCoordLocation=this.gl.getAttribLocation(this._shaderSets[5].shaderProgram,"a_texCoord"),this._shaderSets[5].samplerTexture0Location=this.gl.getUniformLocation(this._shaderSets[5].shaderProgram,"s_texture0"),this._shaderSets[5].samplerTexture1Location=this.gl.getUniformLocation(this._shaderSets[5].shaderProgram,"s_texture1"),this._shaderSets[5].uniformMatrixLocation=this.gl.getUniformLocation(this._shaderSets[5].shaderProgram,"u_matrix"),this._shaderSets[5].uniformClipMatrixLocation=this.gl.getUniformLocation(this._shaderSets[5].shaderProgram,"u_clipMatrix"),this._shaderSets[5].uniformChannelFlagLocation=this.gl.getUniformLocation(this._shaderSets[5].shaderProgram,"u_channelFlag"),this._shaderSets[5].uniformBaseColorLocation=this.gl.getUniformLocation(this._shaderSets[5].shaderProgram,"u_baseColor"),this._shaderSets[6].attributePositionLocation=this.gl.getAttribLocation(this._shaderSets[6].shaderProgram,"a_position"),this._shaderSets[6].attributeTexCoordLocation=this.gl.getAttribLocation(this._shaderSets[6].shaderProgram,"a_texCoord"),this._shaderSets[6].samplerTexture0Location=this.gl.getUniformLocation(this._shaderSets[6].shaderProgram,"s_texture0"),this._shaderSets[6].samplerTexture1Location=this.gl.getUniformLocation(this._shaderSets[6].shaderProgram,"s_texture1"),this._shaderSets[6].uniformMatrixLocation=this.gl.getUniformLocation(this._shaderSets[6].shaderProgram,"u_matrix"),this._shaderSets[6].uniformClipMatrixLocation=this.gl.getUniformLocation(this._shaderSets[6].shaderProgram,"u_clipMatrix"),this._shaderSets[6].uniformChannelFlagLocation=this.gl.getUniformLocation(this._shaderSets[6].shaderProgram,"u_channelFlag"),this._shaderSets[6].uniformBaseColorLocation=this.gl.getUniformLocation(this._shaderSets[6].shaderProgram,"u_baseColor"),this._shaderSets[7].attributePositionLocation=this.gl.getAttribLocation(this._shaderSets[7].shaderProgram,"a_position"),this._shaderSets[7].attributeTexCoordLocation=this.gl.getAttribLocation(this._shaderSets[7].shaderProgram,"a_texCoord"),this._shaderSets[7].samplerTexture0Location=this.gl.getUniformLocation(this._shaderSets[7].shaderProgram,"s_texture0"),this._shaderSets[7].uniformMatrixLocation=this.gl.getUniformLocation(this._shaderSets[7].shaderProgram,"u_matrix"),this._shaderSets[7].uniformBaseColorLocation=this.gl.getUniformLocation(this._shaderSets[7].shaderProgram,"u_baseColor"),this._shaderSets[8].attributePositionLocation=this.gl.getAttribLocation(this._shaderSets[8].shaderProgram,"a_position"),this._shaderSets[8].attributeTexCoordLocation=this.gl.getAttribLocation(this._shaderSets[8].shaderProgram,"a_texCoord"),this._shaderSets[8].samplerTexture0Location=this.gl.getUniformLocation(this._shaderSets[8].shaderProgram,"s_texture0"),this._shaderSets[8].samplerTexture1Location=this.gl.getUniformLocation(this._shaderSets[8].shaderProgram,"s_texture1"),this._shaderSets[8].uniformMatrixLocation=this.gl.getUniformLocation(this._shaderSets[8].shaderProgram,"u_matrix"),this._shaderSets[8].uniformClipMatrixLocation=this.gl.getUniformLocation(this._shaderSets[8].shaderProgram,"u_clipMatrix"),this._shaderSets[8].uniformChannelFlagLocation=this.gl.getUniformLocation(this._shaderSets[8].shaderProgram,"u_channelFlag"),this._shaderSets[8].uniformBaseColorLocation=this.gl.getUniformLocation(this._shaderSets[8].shaderProgram,"u_baseColor"),this._shaderSets[9].attributePositionLocation=this.gl.getAttribLocation(this._shaderSets[9].shaderProgram,"a_position"),this._shaderSets[9].attributeTexCoordLocation=this.gl.getAttribLocation(this._shaderSets[9].shaderProgram,"a_texCoord"),this._shaderSets[9].samplerTexture0Location=this.gl.getUniformLocation(this._shaderSets[9].shaderProgram,"s_texture0"),this._shaderSets[9].samplerTexture1Location=this.gl.getUniformLocation(this._shaderSets[9].shaderProgram,"s_texture1"),this._shaderSets[9].uniformMatrixLocation=this.gl.getUniformLocation(this._shaderSets[9].shaderProgram,"u_matrix"),this._shaderSets[9].uniformClipMatrixLocation=this.gl.getUniformLocation(this._shaderSets[9].shaderProgram,"u_clipMatrix"),this._shaderSets[9].uniformChannelFlagLocation=this.gl.getUniformLocation(this._shaderSets[9].shaderProgram,"u_channelFlag"),this._shaderSets[9].uniformBaseColorLocation=this.gl.getUniformLocation(this._shaderSets[9].shaderProgram,"u_baseColor")}loadShaderProgram(t,e){let i=this.gl.createProgram(),s=this.compileShaderSource(this.gl.VERTEX_SHADER,t);if(!s)return T("Vertex shader compile error!"),0;let r=this.compileShaderSource(this.gl.FRAGMENT_SHADER,e);if(!r)return T("Vertex shader compile error!"),0;this.gl.attachShader(i,s),this.gl.attachShader(i,r),this.gl.linkProgram(i);return this.gl.getProgramParameter(i,this.gl.LINK_STATUS)?(this.gl.deleteShader(s),this.gl.deleteShader(r),i):(T("Failed to link program: {0}",i),this.gl.deleteShader(s),this.gl.deleteShader(r),i&&this.gl.deleteProgram(i),0)}compileShaderSource(t,e){const i=e,s=this.gl.createShader(t);if(this.gl.shaderSource(s,i),this.gl.compileShader(s),!s){T("Shader compile log: {0} ",this.gl.getShaderInfoLog(s))}return this.gl.getShaderParameter(s,this.gl.COMPILE_STATUS)?s:(this.gl.deleteShader(s),null)}setGl(t){this.gl=t}}var kt=(t=>(t[t.ShaderNames_SetupMask=0]="ShaderNames_SetupMask",t[t.ShaderNames_NormalPremultipliedAlpha=1]="ShaderNames_NormalPremultipliedAlpha",t[t.ShaderNames_NormalMaskedPremultipliedAlpha=2]="ShaderNames_NormalMaskedPremultipliedAlpha",t[t.ShaderNames_NomralMaskedInvertedPremultipliedAlpha=3]="ShaderNames_NomralMaskedInvertedPremultipliedAlpha",t[t.ShaderNames_AddPremultipliedAlpha=4]="ShaderNames_AddPremultipliedAlpha",t[t.ShaderNames_AddMaskedPremultipliedAlpha=5]="ShaderNames_AddMaskedPremultipliedAlpha",t[t.ShaderNames_AddMaskedPremultipliedAlphaInverted=6]="ShaderNames_AddMaskedPremultipliedAlphaInverted",t[t.ShaderNames_MultPremultipliedAlpha=7]="ShaderNames_MultPremultipliedAlpha",t[t.ShaderNames_MultMaskedPremultipliedAlpha=8]="ShaderNames_MultMaskedPremultipliedAlpha",t[t.ShaderNames_MultMaskedPremultipliedAlphaInverted=9]="ShaderNames_MultMaskedPremultipliedAlphaInverted",t))(kt||{});const Ut="attribute vec4 a_position;attribute vec2 a_texCoord;varying vec2 v_texCoord;varying vec4 v_myPos;uniform mat4 u_clipMatrix;void main(){ gl_Position = u_clipMatrix * a_position; v_myPos = u_clipMatrix * a_position; v_texCoord = a_texCoord; v_texCoord.y = 1.0 - v_texCoord.y;}",Nt="precision mediump float;varying vec2 v_texCoord;varying vec4 v_myPos;uniform vec4 u_baseColor;uniform vec4 u_channelFlag;uniform sampler2D s_texture0;void main(){ float isInside = step(u_baseColor.x, v_myPos.x/v_myPos.w) * step(u_baseColor.y, v_myPos.y/v_myPos.w) * step(v_myPos.x/v_myPos.w, u_baseColor.z) * step(v_myPos.y/v_myPos.w, u_baseColor.w); gl_FragColor = u_channelFlag * texture2D(s_texture0, v_texCoord).a * isInside;}",Vt="attribute vec4 a_position;attribute vec2 a_texCoord;varying vec2 v_texCoord;uniform mat4 u_matrix;void main(){ gl_Position = u_matrix * a_position; v_texCoord = a_texCoord; v_texCoord.y = 1.0 - v_texCoord.y;}",jt="attribute vec4 a_position;attribute vec2 a_texCoord;varying vec2 v_texCoord;varying vec4 v_clipPos;uniform mat4 u_matrix;uniform mat4 u_clipMatrix;void main(){ gl_Position = u_matrix * a_position; v_clipPos = u_clipMatrix * a_position; v_texCoord = a_texCoord; v_texCoord.y = 1.0 - v_texCoord.y;}",Gt="precision mediump float;varying vec2 v_texCoord;uniform vec4 u_baseColor;uniform sampler2D s_texture0;void main(){ gl_FragColor = texture2D(s_texture0 , v_texCoord) * u_baseColor;}",Xt="precision mediump float;varying vec2 v_texCoord;varying vec4 v_clipPos;uniform vec4 u_baseColor;uniform vec4 u_channelFlag;uniform sampler2D s_texture0;uniform sampler2D s_texture1;void main(){ vec4 col_formask = texture2D(s_texture0 , v_texCoord) * u_baseColor; vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag; float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a; col_formask = col_formask * maskVal; gl_FragColor = col_formask;}",zt="precision mediump float;varying vec2 v_texCoord;varying vec4 v_clipPos;uniform sampler2D s_texture0;uniform sampler2D s_texture1;uniform vec4 u_channelFlag;uniform vec4 u_baseColor;void main(){vec4 col_formask = texture2D(s_texture0, v_texCoord) * u_baseColor;vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;col_formask = col_formask * (1.0 - maskVal);gl_FragColor = col_formask;}";class Wt extends _{constructor(){super(),this._clippingContextBufferForMask=null,this._clippingContextBufferForDraw=null,this._clippingManager=new At,this.firstDraw=!0,this._textures={},this._sortedDrawableIndexList=[],this._bufferData={vertex:null,uv:null,index:null}}initialize(t){t.isUsingMasking()&&(this._clippingManager=new At,this._clippingManager.initialize(t,t.getDrawableCount(),t.getDrawableMasks(),t.getDrawableMaskCounts()));for(let e=t.getDrawableCount()-1;e>=0;e--)this._sortedDrawableIndexList[e]=0;super.initialize(t)}bindTexture(t,e){this._textures[t]=e}getBindedTextures(){return this._textures}setClippingMaskBufferSize(t){this._clippingManager.release(),this._clippingManager=new At,this._clippingManager.setClippingMaskBufferSize(t),this._clippingManager.initialize(this.getModel(),this.getModel().getDrawableCount(),this.getModel().getDrawableMasks(),this.getModel().getDrawableMaskCounts())}getClippingMaskBufferSize(){return this._clippingManager.getClippingMaskBufferSize()}release(){var t,e,i;const s=this;this._clippingManager.release(),s._clippingManager=void 0,null==(t=this.gl)||t.deleteBuffer(this._bufferData.vertex),this._bufferData.vertex=null,null==(e=this.gl)||e.deleteBuffer(this._bufferData.uv),this._bufferData.uv=null,null==(i=this.gl)||i.deleteBuffer(this._bufferData.index),this._bufferData.index=null,s._bufferData=void 0,s._textures=void 0}doDrawModel(){this.preDraw(),null!=this._clippingManager&&this._clippingManager.setupClippingContext(this.getModel(),this);const t=this.getModel().getDrawableCount(),e=this.getModel().getDrawableRenderOrders();for(let i=0;i<t;++i){const t=e[i];this._sortedDrawableIndexList[t]=i}for(let i=0;i<t;++i){const t=this._sortedDrawableIndexList[i];this.getModel().getDrawableDynamicFlagIsVisible(t)&&(this.setClippingContextBufferForDraw(null!=this._clippingManager?this._clippingManager.getClippingContextListForDraw()[t]:null),this.setIsCulling(this.getModel().getDrawableCulling(t)),this.drawMesh(this.getModel().getDrawableTextureIndices(t),this.getModel().getDrawableVertexIndexCount(t),this.getModel().getDrawableVertexCount(t),this.getModel().getDrawableVertexIndices(t),this.getModel().getDrawableVertices(t),this.getModel().getDrawableVertexUvs(t),this.getModel().getDrawableOpacity(t),this.getModel().getDrawableBlendMode(t),this.getModel().getDrawableInvertedMaskBit(t)))}}drawMesh(t,e,i,s,r,a,o,n,l){this.isCulling()?this.gl.enable(this.gl.CULL_FACE):this.gl.disable(this.gl.CULL_FACE),this.gl.frontFace(this.gl.CCW);const h=this.getModelColor();null==this.getClippingContextBufferForMask()&&(h.A*=o,this.isPremultipliedAlpha()&&(h.R*=h.A,h.G*=h.A,h.B*=h.A));let u=null;null!=this._textures[t]&&(u=this._textures[t]),Ot.getInstance().setupShaderProgram(this,u,i,r,s,a,this._bufferData,o,n,h,this.isPremultipliedAlpha(),this.getMvpMatrix(),l),this.gl.drawElements(this.gl.TRIANGLES,e,this.gl.UNSIGNED_SHORT,0),this.gl.useProgram(null),this.setClippingContextBufferForDraw(null),this.setClippingContextBufferForMask(null)}static doStaticRelease(){Ot.deleteInstance()}setRenderState(t,e){Ft=t,Dt=e}preDraw(){this.firstDraw&&(this.firstDraw=!1,this._anisortopy=this.gl.getExtension("EXT_texture_filter_anisotropic")||this.gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic")||this.gl.getExtension("MOZ_EXT_texture_filter_anisotropic")),this.gl.disable(this.gl.SCISSOR_TEST),this.gl.disable(this.gl.STENCIL_TEST),this.gl.disable(this.gl.DEPTH_TEST),this.gl.frontFace(this.gl.CW),this.gl.enable(this.gl.BLEND),this.gl.colorMask(!0,!0,!0,!0),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,null),this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER,null)}setClippingContextBufferForMask(t){this._clippingContextBufferForMask=t}getClippingContextBufferForMask(){return this._clippingContextBufferForMask}setClippingContextBufferForDraw(t){this._clippingContextBufferForDraw=t}getClippingContextBufferForDraw(){return this._clippingContextBufferForDraw}startUp(t){this.gl=t,this._clippingManager.setGL(t),Ot.getInstance().setGl(t)}}_.staticRelease=()=>{Wt.doStaticRelease()};class Ht{constructor(t){this.groups=t.Groups,this.hitAreas=t.HitAreas,this.layout=t.Layout,this.moc=t.FileReferences.Moc,this.expressions=t.FileReferences.Expressions,this.motions=t.FileReferences.Motions,this.textures=t.FileReferences.Textures,this.physics=t.FileReferences.Physics,this.pose=t.FileReferences.Pose}getEyeBlinkParameters(){var t,e;return null==(e=null==(t=this.groups)?void 0:t.find((t=>"EyeBlink"===t.Name)))?void 0:e.Ids}getLipSyncParameters(){var t,e;return null==(e=null==(t=this.groups)?void 0:t.find((t=>"LipSync"===t.Name)))?void 0:e.Ids}}const Yt="ParamAngleX",qt="ParamAngleY",$t="ParamAngleZ",Zt="ParamEyeBallX",Jt="ParamEyeBallY",Qt="ParamBodyAngleX",Kt="ParamBreath";var te;t.config=void 0,(te=t.config||(t.config={})).LOG_LEVEL_VERBOSE=0,te.LOG_LEVEL_WARNING=1,te.LOG_LEVEL_ERROR=2,te.LOG_LEVEL_NONE=999,te.logLevel=te.LOG_LEVEL_WARNING,te.sound=!0,te.motionSync=!0,te.motionFadingDuration=500,te.idleMotionFadingDuration=2e3,te.expressionFadingDuration=500,te.preserveExpressionOnMotion=!0,te.cubism4=t.CubismConfig;const ee={log(e,...i){t.config.logLevel<=t.config.LOG_LEVEL_VERBOSE&&console.log(`[${e}]`,...i)},warn(e,...i){t.config.logLevel<=t.config.LOG_LEVEL_WARNING&&console.warn(`[${e}]`,...i)},error(e,...i){t.config.logLevel<=t.config.LOG_LEVEL_ERROR&&console.error(`[${e}]`,...i)}};function ie(t,e,i){return t<e?e:t>i?i:t}function se(t,e){e.forEach((e=>{Object.getOwnPropertyNames(e.prototype).forEach((i=>{"constructor"!==i&&Object.defineProperty(t.prototype,i,Object.getOwnPropertyDescriptor(e.prototype,i))}))}))}function re(t){let e=t.lastIndexOf("/");return-1!=e&&(t=t.slice(0,e)),e=t.lastIndexOf("/"),-1!==e&&(t=t.slice(e+1)),t}function ae(t,e){const i=t.indexOf(e);-1!==i&&t.splice(i,1)}class oe extends e.EventEmitter{constructor(t,e){super(),this.expressions=[],this.reserveExpressionIndex=-1,this.destroyed=!1,this.settings=t,this.tag=`ExpressionManager(${t.name})`}init(){this.defaultExpression=this.createExpression({},void 0),this.currentExpression=this.defaultExpression,this.stopAllExpressions()}loadExpression(t){return __async(this,null,(function*(){if(!this.definitions[t])return void ee.warn(this.tag,`Undefined expression at [${t}]`);if(null===this.expressions[t])return void ee.warn(this.tag,`Cannot set expression at [${t}] because it's already failed in loading.`);if(this.expressions[t])return this.expressions[t];const e=yield this._loadExpression(t);return this.expressions[t]=e,e}))}_loadExpression(t){throw new Error("Not implemented.")}setRandomExpression(){return __async(this,null,(function*(){if(this.definitions.length){const t=[];for(let e=0;e<this.definitions.length;e++)null!==this.expressions[e]&&this.expressions[e]!==this.currentExpression&&e!==this.reserveExpressionIndex&&t.push(e);if(t.length){const e=Math.floor(Math.random()*t.length);return this.setExpression(e)}}return!1}))}resetExpression(){this._setExpression(this.defaultExpression)}restoreExpression(){this._setExpression(this.currentExpression)}setExpression(t){return __async(this,null,(function*(){if("number"!=typeof t&&(t=this.getExpressionIndex(t)),!(t>-1&&t<this.definitions.length))return!1;if(t===this.expressions.indexOf(this.currentExpression))return!1;this.reserveExpressionIndex=t;const e=yield this.loadExpression(t);return!(!e||this.reserveExpressionIndex!==t)&&(this.reserveExpressionIndex=-1,this.currentExpression=e,this._setExpression(e),!0)}))}update(t,e){return!this.isFinished()&&this.updateParameters(t,e)}destroy(){this.destroyed=!0,this.emit("destroy");this.definitions=void 0,this.expressions=void 0}}class ne{constructor(){this.targetX=0,this.targetY=0,this.x=0,this.y=0,this.vx=0,this.vy=0}focus(t,e,i=!1){this.targetX=ie(t,-1,1),this.targetY=ie(e,-1,1),i&&(this.x=this.targetX,this.y=this.targetY)}update(t){const e=this.targetX-this.x,i=this.targetY-this.y;if(Math.abs(e)<.01&&Math.abs(i)<.01)return;const s=Math.sqrt(__pow(e,2)+__pow(i,2)),r=5.333333333333333/(1e3/t);let a=r*(e/s)-this.vx,o=r*(i/s)-this.vy;const n=Math.sqrt(__pow(a,2)+__pow(o,2)),l=.006666666666666667*r*t;n>l&&(a*=l/n,o*=l/n),this.vx+=a,this.vy+=o;const h=Math.sqrt(__pow(this.vx,2)+__pow(this.vy,2)),u=.5*(Math.sqrt(__pow(l,2)+8*l*s)-l);h>u&&(this.vx*=u/h,this.vy*=u/h),this.x+=this.vx,this.y+=this.vy}}class le{constructor(t){this.json=t;let e=t.url;if("string"!=typeof e)throw new TypeError("The `url` field in settings JSON must be defined as a string.");this.url=e,this.name=re(this.url)}resolveURL(t){return e.url.resolve(this.url,t)}replaceFiles(t){this.moc=t(this.moc,"moc"),void 0!==this.pose&&(this.pose=t(this.pose,"pose")),void 0!==this.physics&&(this.physics=t(this.physics,"physics"));for(let e=0;e<this.textures.length;e++)this.textures[e]=t(this.textures[e],`textures[${e}]`)}getDefinedFiles(){const t=[];return this.replaceFiles((e=>(t.push(e),e))),t}validateFiles(t){const e=(e,i)=>{const s=this.resolveURL(e);if(!t.includes(s)){if(i)throw new Error(`File "${e}" is defined in settings, but doesn't exist in given files`);return!1}return!0};[this.moc,...this.textures].forEach((t=>e(t,!0)));return this.getDefinedFiles().filter((t=>e(t,!1)))}}var he=(t=>(t[t.NONE=0]="NONE",t[t.IDLE=1]="IDLE",t[t.NORMAL=2]="NORMAL",t[t.FORCE=3]="FORCE",t))(he||{});class ue{constructor(){this.debug=!1,this.currentPriority=0,this.reservePriority=0}reserve(t,e,i){if(i<=0)return ee.log(this.tag,"Cannot start a motion with MotionPriority.NONE."),!1;if(t===this.currentGroup&&e===this.currentIndex)return ee.log(this.tag,"Motion is already playing.",this.dump(t,e)),!1;if(t===this.reservedGroup&&e===this.reservedIndex||t===this.reservedIdleGroup&&e===this.reservedIdleIndex)return ee.log(this.tag,"Motion is already reserved.",this.dump(t,e)),!1;if(1===i){if(0!==this.currentPriority)return ee.log(this.tag,"Cannot start idle motion because another motion is playing.",this.dump(t,e)),!1;if(void 0!==this.reservedIdleGroup)return ee.log(this.tag,"Cannot start idle motion because another idle motion has reserved.",this.dump(t,e)),!1;this.setReservedIdle(t,e)}else{if(i<3){if(i<=this.currentPriority)return ee.log(this.tag,"Cannot start motion because another motion is playing as an equivalent or higher priority.",this.dump(t,e)),!1;if(i<=this.reservePriority)return ee.log(this.tag,"Cannot start motion because another motion has reserved as an equivalent or higher priority.",this.dump(t,e)),!1}this.setReserved(t,e,i)}return!0}start(t,e,i,s){if(1===s){if(this.setReservedIdle(void 0,void 0),0!==this.currentPriority)return ee.log(this.tag,"Cannot start idle motion because another motion is playing.",this.dump(e,i)),!1}else{if(e!==this.reservedGroup||i!==this.reservedIndex)return ee.log(this.tag,"Cannot start motion because another motion has taken the place.",this.dump(e,i)),!1;this.setReserved(void 0,void 0,0)}return!!t&&(this.setCurrent(e,i,s),!0)}complete(){this.setCurrent(void 0,void 0,0)}setCurrent(t,e,i){this.currentPriority=i,this.currentGroup=t,this.currentIndex=e}setReserved(t,e,i){this.reservePriority=i,this.reservedGroup=t,this.reservedIndex=e}setReservedIdle(t,e){this.reservedIdleGroup=t,this.reservedIdleIndex=e}isActive(t,e){return t===this.currentGroup&&e===this.currentIndex||t===this.reservedGroup&&e===this.reservedIndex||t===this.reservedIdleGroup&&e===this.reservedIdleIndex}reset(){this.setCurrent(void 0,void 0,0),this.setReserved(void 0,void 0,0),this.setReservedIdle(void 0,void 0)}shouldRequestIdleMotion(){return void 0===this.currentGroup&&void 0===this.reservedIdleGroup}shouldOverrideExpression(){return!t.config.preserveExpressionOnMotion&&this.currentPriority>1}dump(t,e){if(this.debug){return`\n<Requested> group = "${t}", index = ${e}\n`+["currentPriority","reservePriority","currentGroup","currentIndex","reservedGroup","reservedIndex","reservedIdleGroup","reservedIdleIndex"].map((t=>"["+t+"] "+this[t])).join("\n")}return""}}class de{static get volume(){return this._volume}static set volume(t){this._volume=(t>1?1:t<0?0:t)||0,this.audios.forEach((t=>t.volume=this._volume))}static add(t,e,i){const s=new Audio(t);return s.volume=this._volume,s.preload="auto",s.addEventListener("ended",(()=>{this.dispose(s),null==e||e()})),s.addEventListener("error",(e=>{this.dispose(s),ee.warn("SoundManager",`Error occurred on "${t}"`,e.error),null==i||i(e.error)})),this.audios.push(s),s}static play(t){return new Promise(((e,i)=>{var s;null==(s=t.play())||s.catch((e=>{t.dispatchEvent(new ErrorEvent("error",{error:e})),i(e)})),t.readyState===t.HAVE_ENOUGH_DATA?e():t.addEventListener("canplaythrough",e)}))}static dispose(t){t.pause(),t.removeAttribute("src"),ae(this.audios,t)}static destroy(){for(let t=this.audios.length-1;t>=0;t--)this.dispose(this.audios[t])}}de.audios=[],de._volume=.5;var ce=(t=>(t.ALL="ALL",t.IDLE="IDLE",t.NONE="NONE",t))(ce||{});class me extends e.EventEmitter{constructor(t,e){super(),this.motionGroups={},this.state=new ue,this.playing=!1,this.destroyed=!1,this.settings=t,this.tag=`MotionManager(${t.name})`,this.state.tag=this.tag}init(t){(null==t?void 0:t.idleMotionGroup)&&(this.groups.idle=t.idleMotionGroup),this.setupMotions(t),this.stopAllMotions()}setupMotions(t){for(const i of Object.keys(this.definitions))this.motionGroups[i]=[];let e;switch(null==t?void 0:t.motionPreload){case"NONE":return;case"ALL":e=Object.keys(this.definitions);break;default:e=[this.groups.idle]}for(const i of e)if(this.definitions[i])for(let t=0;t<this.definitions[i].length;t++)this.loadMotion(i,t).then()}loadMotion(t,e){return __async(this,null,(function*(){var i;if(!(null==(i=this.definitions[t])?void 0:i[e]))return void ee.warn(this.tag,`Undefined motion at "${t}"[${e}]`);if(null===this.motionGroups[t][e])return void ee.warn(this.tag,`Cannot start motion at "${t}"[${e}] because it's already failed in loading.`);if(this.motionGroups[t][e])return this.motionGroups[t][e];const s=yield this._loadMotion(t,e);return this.destroyed?void 0:(this.motionGroups[t][e]=null!=s?s:null,s)}))}_loadMotion(t,e){throw new Error("Not implemented.")}startMotion(e,i){return __async(this,arguments,(function*(e,i,s=he.NORMAL){var r;if(!this.state.reserve(e,i,s))return!1;const a=null==(r=this.definitions[e])?void 0:r[i];if(!a)return!1;let o;if(this.currentAudio&&de.dispose(this.currentAudio),t.config.sound){const t=this.getSoundFile(a);if(t)try{o=de.add(this.settings.resolveURL(t),(()=>this.currentAudio=void 0),(()=>this.currentAudio=void 0)),this.currentAudio=o}catch(l){ee.warn(this.tag,"Failed to create audio",t,l)}}const n=yield this.loadMotion(e,i);if(o){const e=de.play(o).catch((t=>ee.warn(this.tag,"Failed to play audio",o.src,t)));t.config.motionSync&&(yield e)}return this.state.start(n,e,i,s)?(ee.log(this.tag,"Start motion:",this.getMotionName(a)),this.emit("motionStart",e,i,o),this.state.shouldOverrideExpression()&&this.expressionManager&&this.expressionManager.resetExpression(),this.playing=!0,this._startMotion(n),!0):(o&&(de.dispose(o),this.currentAudio=void 0),!1)}))}startRandomMotion(t,e){return __async(this,null,(function*(){const i=this.definitions[t];if(null==i?void 0:i.length){const s=[];for(let e=0;e<i.length;e++)null===this.motionGroups[t][e]||this.state.isActive(t,e)||s.push(e);if(s.length){const i=Math.floor(Math.random()*s.length);return this.startMotion(t,s[i],e)}}return!1}))}stopAllMotions(){this._stopAllMotions(),this.state.reset(),this.currentAudio&&(de.dispose(this.currentAudio),this.currentAudio=void 0)}update(t,e){var i;return this.isFinished()&&(this.playing&&(this.playing=!1,this.emit("motionFinish")),this.state.shouldOverrideExpression()&&(null==(i=this.expressionManager)||i.restoreExpression()),this.state.complete(),this.state.shouldRequestIdleMotion()&&this.startRandomMotion(this.groups.idle,he.IDLE)),this.updateParameters(t,e)}destroy(){var t;this.destroyed=!0,this.emit("destroy"),this.stopAllMotions(),null==(t=this.expressionManager)||t.destroy();this.definitions=void 0,this.motionGroups=void 0}}const ge={x:0,y:0,width:0,height:0};class pe extends e.EventEmitter{constructor(){super(...arguments),this.focusController=new ne,this.originalWidth=0,this.originalHeight=0,this.width=0,this.height=0,this.localTransform=new i.Matrix,this.drawingMatrix=new i.Matrix,this.hitAreas={},this.textureFlipY=!1,this.viewport=[0,0,0,0],this.destroyed=!1}init(){this.setupLayout(),this.setupHitAreas()}setupLayout(){const t=this,e=this.getSize();t.originalWidth=e[0],t.originalHeight=e[1];const i=Object.assign({width:2,height:2},this.getLayout());this.localTransform.scale(i.width/2,i.height/2),t.width=this.originalWidth*this.localTransform.a,t.height=this.originalHeight*this.localTransform.d;const s=void 0!==i.x&&i.x-i.width/2||void 0!==i.centerX&&i.centerX||void 0!==i.left&&i.left-i.width/2||void 0!==i.right&&i.right+i.width/2||0,r=void 0!==i.y&&i.y-i.height/2||void 0!==i.centerY&&i.centerY||void 0!==i.top&&i.top-i.height/2||void 0!==i.bottom&&i.bottom+i.height/2||0;this.localTransform.translate(this.width*s,-this.height*r)}setupHitAreas(){const t=this.getHitAreaDefs().filter((t=>t.index>=0));for(const e of t)this.hitAreas[e.name]=e}hitTest(t,e){return Object.keys(this.hitAreas).filter((i=>this.isHit(i,t,e)))}isHit(t,e,i){if(!this.hitAreas[t])return!1;const s=this.hitAreas[t].index,r=this.getDrawableBounds(s,ge);return r.x<=e&&e<=r.x+r.width&&r.y<=i&&i<=r.y+r.height}getDrawableBounds(t,e){const i=this.getDrawableVertices(t);let s=i[0],r=i[0],a=i[1],o=i[1];for(let n=0;n<i.length;n+=2){const t=i[n],e=i[n+1];s=Math.min(t,s),r=Math.max(t,r),a=Math.min(e,a),o=Math.max(e,o)}return null!=e||(e={}),e.x=s,e.y=a,e.width=r-s,e.height=o-a,e}updateTransform(t){this.drawingMatrix.copyFrom(t).append(this.localTransform)}update(t,e){this.focusController.update(t)}destroy(){this.destroyed=!0,this.emit("destroy"),this.motionManager.destroy(),this.motionManager=void 0}}class _e extends Error{constructor(t,e,i,s=!1){super(t),this.url=e,this.status=i,this.aborted=s}}const fe=class{static createXHR(t,e,i,s,r){const a=new XMLHttpRequest;if(fe.allXhrSet.add(a),t){let e=fe.xhrMap.get(t);e?e.add(a):(e=new Set([a]),fe.xhrMap.set(t,e)),t.listeners("destroy").includes(fe.cancelXHRs)||t.once("destroy",fe.cancelXHRs)}return a.open("GET",e),a.responseType=i,a.onload=()=>{200!==a.status&&0!==a.status||!a.response?a.onerror():s(a.response)},a.onerror=()=>{ee.warn("XHRLoader",`Failed to load resource as ${a.responseType} (Status ${a.status}): ${e}`),r(new _e("Network error.",e,a.status))},a.onabort=()=>r(new _e("Aborted.",e,a.status,!0)),a.onloadend=()=>{var e;fe.allXhrSet.delete(a),t&&(null==(e=fe.xhrMap.get(t))||e.delete(a))},a}static cancelXHRs(){var t;null==(t=fe.xhrMap.get(this))||t.forEach((t=>{t.abort(),fe.allXhrSet.delete(t)})),fe.xhrMap.delete(this)}static release(){fe.allXhrSet.forEach((t=>t.abort())),fe.allXhrSet.clear(),fe.xhrMap=new WeakMap}};let ye=fe;function xe(t,e){let i=-1;return function s(r,a){if(a)return Promise.reject(a);if(r<=i)return Promise.reject(new Error("next() called multiple times"));i=r;const o=t[r];if(!o)return Promise.resolve();try{return Promise.resolve(o(e,s.bind(null,r+1)))}catch(n){return Promise.reject(n)}}(0)}ye.xhrMap=new WeakMap,ye.allXhrSet=new Set,ye.loader=(t,e)=>new Promise(((e,i)=>{fe.createXHR(t.target,t.settings?t.settings.resolveURL(t.url):t.url,t.type,(i=>{t.result=i,e()}),i).send()}));class ve{static load(t){return xe(this.middlewares,t).then((()=>t.result))}}ve.middlewares=[ye.loader];const Me="Live2DFactory",Pe=(t,e)=>__async(this,null,(function*(){if("string"==typeof t.source){const e=yield ve.load({url:t.source,type:"json",target:t.live2dModel});e.url=t.source,t.source=e,t.live2dModel.emit("settingsJSONLoaded",e)}return e()})),Ce=(t,e)=>__async(this,null,(function*(){if(t.source instanceof le)return t.settings=t.source,e();if("object"==typeof t.source){const i=Te.findRuntime(t.source);if(i){const s=i.createModelSettings(t.source);return t.settings=s,t.live2dModel.emit("settingsLoaded",s),e()}}throw new TypeError("Unknown settings format.")})),Se=(t,e)=>{if(t.settings){const i=Te.findRuntime(t.settings);if(i)return i.ready().then(e)}return e()},be=(t,e)=>__async(this,null,(function*(){yield e();const i=t.internalModel;if(i){const e=t.settings,s=Te.findRuntime(e);if(s){const r=[];e.pose&&r.push(ve.load({settings:e,url:e.pose,type:"json",target:i}).then((e=>{i.pose=s.createPose(i.coreModel,e),t.live2dModel.emit("poseLoaded",i.pose)})).catch((e=>{t.live2dModel.emit("poseLoadError",e),ee.warn(Me,"Failed to load pose.",e)}))),e.physics&&r.push(ve.load({settings:e,url:e.physics,type:"json",target:i}).then((e=>{i.physics=s.createPhysics(i.coreModel,e),t.live2dModel.emit("physicsLoaded",i.physics)})).catch((e=>{t.live2dModel.emit("physicsLoadError",e),ee.warn(Me,"Failed to load physics.",e)}))),r.length&&(yield Promise.all(r))}}})),we=(t,e)=>__async(this,null,(function*(){if(!t.settings)throw new TypeError("Missing settings.");{const i=t.live2dModel,r=t.settings.textures.map((e=>function(t,e={}){const i={resourceOptions:{crossorigin:e.crossOrigin}};if(s.Texture.fromURL)return s.Texture.fromURL(t,i).catch((t=>{if(t instanceof Error)throw t;const e=new Error("Texture loading error");throw e.event=t,e}));i.resourceOptions.autoLoad=!1;const r=s.Texture.from(t,i);if(r.baseTexture.valid)return Promise.resolve(r);const a=r.baseTexture.resource;return null!=a._live2d_load||(a._live2d_load=new Promise(((t,e)=>{const i=t=>{a.source.removeEventListener("error",i);const s=new Error("Texture loading error");s.event=t,e(s)};a.source.addEventListener("error",i),a.load().then((()=>t(r))).catch(i)}))),a._live2d_load}(t.settings.resolveURL(e),{crossOrigin:t.options.crossOrigin})));if(yield e(),!t.internalModel)throw new TypeError("Missing internal model.");i.internalModel=t.internalModel,i.emit("modelLoaded",t.internalModel),i.textures=yield Promise.all(r),i.emit("textureLoaded",i.textures)}})),Ie=(t,e)=>__async(this,null,(function*(){const i=t.settings;if(i instanceof le){const s=Te.findRuntime(i);if(!s)throw new TypeError("Unknown model settings.");const r=yield ve.load({settings:i,url:i.moc,type:"arraybuffer",target:t.live2dModel});if(!s.isValidMoc(r))throw new Error("Invalid moc data");const a=s.createCoreModel(r);return t.internalModel=s.createInternalModel(a,i,t.options),e()}throw new TypeError("Missing settings.")})),Le=class{static registerRuntime(t){Le.runtimes.push(t),Le.runtimes.sort(((t,e)=>e.version-t.version))}static findRuntime(t){for(const e of Le.runtimes)if(e.test(t))return e}static setupLive2DModel(t,e,i){return __async(this,null,(function*(){const s=new Promise((e=>t.once("textureLoaded",e))),r=new Promise((e=>t.once("modelLoaded",e))),a=Promise.all([s,r]).then((()=>t.emit("ready")));yield xe(Le.live2DModelMiddlewares,{live2dModel:t,source:e,options:i||{}}),yield a,t.emit("load")}))}static loadMotion(t,e,i){var s;const r=s=>t.emit("motionLoadError",e,i,s);try{const a=null==(s=t.definitions[e])?void 0:s[i];if(!a)return Promise.resolve(void 0);t.listeners("destroy").includes(Le.releaseTasks)||t.once("destroy",Le.releaseTasks);let o=Le.motionTasksMap.get(t);o||(o={},Le.motionTasksMap.set(t,o));let n=o[e];n||(n=[],o[e]=n);const l=t.getMotionFile(a);return null!=n[i]||(n[i]=ve.load({url:l,settings:t.settings,type:t.motionDataType,target:t}).then((s=>{var r;const o=null==(r=Le.motionTasksMap.get(t))?void 0:r[e];o&&delete o[i];const n=t.createMotion(s,e,a);return t.emit("motionLoaded",e,i,n),n})).catch((e=>{ee.warn(t.tag,`Failed to load motion: ${l}\n`,e),r(e)}))),n[i]}catch(a){ee.warn(t.tag,`Failed to load motion at "${e}"[${i}]\n`,a),r(a)}return Promise.resolve(void 0)}static loadExpression(t,e){const i=i=>t.emit("expressionLoadError",e,i);try{const s=t.definitions[e];if(!s)return Promise.resolve(void 0);t.listeners("destroy").includes(Le.releaseTasks)||t.once("destroy",Le.releaseTasks);let r=Le.expressionTasksMap.get(t);r||(r=[],Le.expressionTasksMap.set(t,r));const a=t.getExpressionFile(s);return null!=r[e]||(r[e]=ve.load({url:a,settings:t.settings,type:"json",target:t}).then((i=>{const r=Le.expressionTasksMap.get(t);r&&delete r[e];const a=t.createExpression(i,s);return t.emit("expressionLoaded",e,a),a})).catch((e=>{ee.warn(t.tag,`Failed to load expression: ${a}\n`,e),i(e)}))),r[e]}catch(s){ee.warn(t.tag,`Failed to load expression at [${e}]\n`,s),i(s)}return Promise.resolve(void 0)}static releaseTasks(){this instanceof me?Le.motionTasksMap.delete(this):Le.expressionTasksMap.delete(this)}};let Te=Le;Te.runtimes=[],Te.urlToJSON=Pe,Te.jsonToSettings=Ce,Te.waitUntilReady=Se,Te.setupOptionals=be,Te.setupEssentials=we,Te.createInternalModel=Ie,Te.live2DModelMiddlewares=[Pe,Ce,Se,be,we,Ie],Te.motionTasksMap=new WeakMap,Te.expressionTasksMap=new WeakMap,me.prototype._loadMotion=function(t,e){return Te.loadMotion(this,t,e)},oe.prototype._loadExpression=function(t){return Te.loadExpression(this,t)};class Ee{constructor(){this._autoInteract=!1}get autoInteract(){return this._autoInteract}set autoInteract(t){t!==this._autoInteract&&(t?this.on("pointertap",De,this):this.off("pointertap",De,this),this._autoInteract=t)}registerInteraction(t){t!==this.interactionManager&&(this.unregisterInteraction(),this._autoInteract&&t&&(this.interactionManager=t,t.on("pointermove",Fe,this)))}unregisterInteraction(){var t;this.interactionManager&&(null==(t=this.interactionManager)||t.off("pointermove",Fe,this),this.interactionManager=void 0)}}function De(t){this.tap(t.data.global.x,t.data.global.y)}function Fe(t){this.focus(t.data.global.x,t.data.global.y)}class Ae extends i.Transform{}const Be=new i.Point,Re=new i.Matrix;let Oe;class ke extends r.Container{constructor(t){super(),this.tag="Live2DModel(uninitialized)",this.textures=[],this.transform=new Ae,this.anchor=new i.ObservablePoint(this.onAnchorChange,this,0,0),this.glContextID=-1,this.elapsedTime=performance.now(),this.deltaTime=0,this._autoUpdate=!1,this.once("modelLoaded",(()=>this.init(t)))}static from(t,e){const i=new this(e);return Te.setupLive2DModel(i,t,e).then((()=>i))}static fromSync(t,e){const i=new this(e);return Te.setupLive2DModel(i,t,e).then(null==e?void 0:e.onLoad).catch(null==e?void 0:e.onError),i}static registerTicker(t){Oe=t}get autoUpdate(){return this._autoUpdate}set autoUpdate(t){var e;Oe||(Oe=null==(e=window.PIXI)?void 0:e.Ticker),t?this._destroyed||(Oe?(Oe.shared.add(this.onTickerUpdate,this),this._autoUpdate=!0):ee.warn(this.tag,"No Ticker registered, please call Live2DModel.registerTicker(Ticker).")):(null==Oe||Oe.shared.remove(this.onTickerUpdate,this),this._autoUpdate=!1)}init(t){this.tag=`Live2DModel(${this.internalModel.settings.name})`;const e=Object.assign({autoUpdate:!0,autoInteract:!0},t);e.autoInteract&&(this.interactive=!0),this.autoInteract=e.autoInteract,this.autoUpdate=e.autoUpdate}onAnchorChange(){this.pivot.set(this.anchor.x*this.internalModel.width,this.anchor.y*this.internalModel.height)}motion(t,e,i){return void 0===e?this.internalModel.motionManager.startRandomMotion(t,i):this.internalModel.motionManager.startMotion(t,e,i)}expression(t){return this.internalModel.motionManager.expressionManager?void 0===t?this.internalModel.motionManager.expressionManager.setRandomExpression():this.internalModel.motionManager.expressionManager.setExpression(t):Promise.resolve(!1)}focus(t,e,i=!1){Be.x=t,Be.y=e,this.toModelPosition(Be,Be,!0);let s=Be.x/this.internalModel.originalWidth*2-1,r=Be.y/this.internalModel.originalHeight*2-1,a=Math.atan2(r,s);this.internalModel.focusController.focus(Math.cos(a),-Math.sin(a),i)}tap(t,e){const i=this.hitTest(t,e);i.length&&(ee.log(this.tag,"Hit",i),this.emit("hit",i))}hitTest(t,e){return Be.x=t,Be.y=e,this.toModelPosition(Be,Be),this.internalModel.hitTest(Be.x,Be.y)}toModelPosition(t,e=t.clone(),i){return i||(this._recursivePostUpdateTransform(),this.parent?this.displayObjectUpdateTransform():(this.parent=this._tempDisplayObjectParent,this.displayObjectUpdateTransform(),this.parent=null)),this.transform.worldTransform.applyInverse(t,e),this.internalModel.localTransform.applyInverse(e,e),e}containsPoint(t){return this.getBounds(!0).contains(t.x,t.y)}_calculateBounds(){this._bounds.addFrame(this.transform,0,0,this.internalModel.width,this.internalModel.height)}onTickerUpdate(){this.update(Oe.shared.deltaMS)}update(t){this.deltaTime+=t,this.elapsedTime+=t}_render(t){this.registerInteraction(t.plugins.interaction),t.batch.reset(),t.geometry.reset(),t.shader.reset(),t.state.reset();let e=!1;this.glContextID!==t.CONTEXT_UID&&(this.glContextID=t.CONTEXT_UID,this.internalModel.updateWebGLContext(t.gl,this.glContextID),e=!0);for(let r=0;r<this.textures.length;r++){const i=this.textures[r];i.valid&&(!e&&i.baseTexture._glTextures[this.glContextID]||(t.gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL,this.internalModel.textureFlipY),t.texture.bind(i.baseTexture,0)),this.internalModel.bindTexture(r,i.baseTexture._glTextures[this.glContextID].texture),i.baseTexture.touched=t.textureGC.count)}const i=t.framebuffer.viewport;this.internalModel.viewport=[i.x,i.y,i.width,i.height],this.deltaTime&&(this.internalModel.update(this.deltaTime,this.elapsedTime),this.deltaTime=0);const s=Re.copyFrom(t.globalUniforms.uniforms.projectionMatrix).append(this.worldTransform);this.internalModel.updateTransform(s),this.internalModel.draw(t.gl),t.state.reset(),t.texture.reset()}destroy(t){this.emit("destroy"),this.autoUpdate=!1,this.unregisterInteraction(),(null==t?void 0:t.texture)&&this.textures.forEach((e=>e.destroy(t.baseTexture))),this.internalModel.destroy(),super.destroy(t)}}se(ke,[Ee]);const Ue=class{static resolveURL(t,e){var i;const s=null==(i=Ue.filesMap[t])?void 0:i[e];if(void 0===s)throw new Error("Cannot find this file from uploaded files: "+e);return s}static upload(t,i){return __async(this,null,(function*(){const s={};for(const r of i.getDefinedFiles()){const a=decodeURI(e.url.resolve(i.url,r)),o=t.find((t=>t.webkitRelativePath===a));o&&(s[r]=URL.createObjectURL(o))}Ue.filesMap[i._objectURL]=s}))}static createSettings(t){return __async(this,null,(function*(){const e=t.find((t=>t.name.endsWith("model.json")||t.name.endsWith("model3.json")));if(!e)throw new TypeError("Settings file not found");const i=yield Ue.readText(e),s=JSON.parse(i);s.url=e.webkitRelativePath;const r=Te.findRuntime(s);if(!r)throw new Error("Unknown settings JSON");const a=r.createModelSettings(s);return a._objectURL=URL.createObjectURL(e),a}))}static readText(t){return __async(this,null,(function*(){return new Promise(((e,i)=>{const s=new FileReader;s.onload=()=>e(s.result),s.onerror=i,s.readAsText(t,"utf8")}))}))}};let Ne=Ue;Ne.filesMap={},Ne.factory=(t,e)=>__async(this,null,(function*(){if(Array.isArray(t.source)&&t.source[0]instanceof File){const e=t.source;let i=e.settings;if(i){if(!i._objectURL)throw new Error('"_objectURL" must be specified in ModelSettings')}else i=yield Ue.createSettings(e);i.validateFiles(e.map((t=>encodeURI(t.webkitRelativePath)))),yield Ue.upload(e,i),i.resolveURL=function(t){return Ue.resolveURL(this._objectURL,t)},t.source=i,t.live2dModel.once("modelLoaded",(t=>{t.once("destroy",(function(){const t=this.settings._objectURL;if(URL.revokeObjectURL(t),Ue.filesMap[t])for(const e of Object.values(Ue.filesMap[t]))URL.revokeObjectURL(e);delete Ue.filesMap[t]}))}))}return e()})),Te.live2DModelMiddlewares.unshift(Ne.factory);const Ve=class{static unzip(t,i){return __async(this,null,(function*(){const s=yield Ve.getFilePaths(t),r=[];for(const t of i.getDefinedFiles()){const a=decodeURI(e.url.resolve(i.url,t));s.includes(a)&&r.push(a)}const a=yield Ve.getFiles(t,r);for(let t=0;t<a.length;t++){const e=r[t],i=a[t];Object.defineProperty(i,"webkitRelativePath",{value:e})}return a}))}static createSettings(t){return __async(this,null,(function*(){const e=(yield Ve.getFilePaths(t)).find((t=>t.endsWith("model.json")||t.endsWith("model3.json")));if(!e)throw new Error("Settings file not found");const i=yield Ve.readText(t,e);if(!i)throw new Error("Empty settings file: "+e);const s=JSON.parse(i);s.url=e;const r=Te.findRuntime(s);if(!r)throw new Error("Unknown settings JSON");return r.createModelSettings(s)}))}static zipReader(t,e){return __async(this,null,(function*(){throw new Error("Not implemented")}))}static getFilePaths(t){return __async(this,null,(function*(){throw new Error("Not implemented")}))}static getFiles(t,e){return __async(this,null,(function*(){throw new Error("Not implemented")}))}static readText(t,e){return __async(this,null,(function*(){throw new Error("Not implemented")}))}static releaseReader(t){}};let je=Ve;if(je.ZIP_PROTOCOL="zip://",je.uid=0,je.factory=(t,e)=>__async(this,null,(function*(){const i=t.source;let s,r,a;if("string"==typeof i&&(i.endsWith(".zip")||i.startsWith(Ve.ZIP_PROTOCOL))?(s=i.startsWith(Ve.ZIP_PROTOCOL)?i.slice(Ve.ZIP_PROTOCOL.length):i,r=yield ve.load({url:s,type:"blob",target:t.live2dModel})):Array.isArray(i)&&1===i.length&&i[0]instanceof File&&i[0].name.endsWith(".zip")&&(r=i[0],s=URL.createObjectURL(r),a=i.settings),r){if(!r.size)throw new Error("Empty zip file");const e=yield Ve.zipReader(r,s);a||(a=yield Ve.createSettings(e)),a._objectURL=Ve.ZIP_PROTOCOL+Ve.uid+"/"+a.url;const i=yield Ve.unzip(e,a);i.settings=a,t.source=i,s.startsWith("blob:")&&t.live2dModel.once("modelLoaded",(t=>{t.once("destroy",(function(){URL.revokeObjectURL(s)}))})),Ve.releaseReader(e)}return e()})),Te.live2DModelMiddlewares.unshift(je.factory),!window.Live2DCubismCore)throw new Error("Could not find Cubism 4 runtime. This plugin requires live2dcubismcore.js to be loaded.");class Ge extends oe{constructor(t,e){var i;super(t,e),this.queueManager=new st,this.definitions=null!=(i=t.expressions)?i:[],this.init()}isFinished(){return this.queueManager.isFinished()}getExpressionIndex(t){return this.definitions.findIndex((e=>e.Name===t))}getExpressionFile(t){return t.File}createExpression(t,e){return O.create(t)}_setExpression(t){return this.queueManager.startMotion(t,!1,performance.now())}stopAllExpressions(){this.queueManager.stopAllMotions()}updateParameters(t,e){return this.queueManager.doUpdateMotion(t,e)}}class Xe extends le{constructor(t){if(super(t),!Xe.isValidJSON(t))throw new TypeError("Invalid JSON.");Object.assign(this,new Ht(t))}static isValidJSON(t){var e;return!!(null==t?void 0:t.FileReferences)&&"string"==typeof t.FileReferences.Moc&&(null==(e=t.FileReferences.Textures)?void 0:e.length)>0&&t.FileReferences.Textures.every((t=>"string"==typeof t))}replaceFiles(t){if(super.replaceFiles(t),this.motions)for(const[e,i]of Object.entries(this.motions))for(let s=0;s<i.length;s++)i[s].File=t(i[s].File,`motions.${e}[${s}].File`),void 0!==i[s].Sound&&(i[s].Sound=t(i[s].Sound,`motions.${e}[${s}].Sound`));if(this.expressions)for(let e=0;e<this.expressions.length;e++)this.expressions[e].File=t(this.expressions[e].File,`expressions[${e}].File`)}}se(Xe,[Ht]);class ze extends me{constructor(t,e){var i;super(t,e),this.groups={idle:"Idle"},this.motionDataType="json",this.queueManager=new st,this.definitions=null!=(i=t.motions)?i:{},this.eyeBlinkIds=t.getEyeBlinkParameters()||[],this.lipSyncIds=t.getLipSyncParameters()||[],this.init(e)}init(t){super.init(t),this.settings.expressions&&(this.expressionManager=new Ge(this.settings,t)),this.queueManager.setEventCallback(((t,e,i)=>{this.emit("motion:"+e)}))}isFinished(){return this.queueManager.isFinished()}_startMotion(t,e){return t.setFinishedMotionHandler(e),this.queueManager.stopAllMotions(),this.queueManager.startMotion(t,!1,performance.now())}_stopAllMotions(){this.queueManager.stopAllMotions()}createMotion(e,i,s){const r=et.create(e),a=new H(e),o=(i===this.groups.idle?t.config.idleMotionFadingDuration:t.config.motionFadingDuration)/1e3;return void 0===a.getMotionFadeInTime()&&r.setFadeInTime(s.FadeInTime>0?s.FadeInTime:o),void 0===a.getMotionFadeOutTime()&&r.setFadeOutTime(s.FadeOutTime>0?s.FadeOutTime:o),r.setEffectIds(this.eyeBlinkIds,this.lipSyncIds),r}getMotionFile(t){return t.File}getMotionName(t){return t.File}getSoundFile(t){return t.Sound}updateParameters(t,e){return this.queueManager.doUpdateMotion(t,e)}destroy(){super.destroy(),this.queueManager.release(),this.queueManager=void 0}}const We=new p;class He extends pe{constructor(t,e,s){super(),this.lipSync=!0,this.breath=a.create(),this.renderer=new Wt,this.idParamAngleX=Yt,this.idParamAngleY=qt,this.idParamAngleZ=$t,this.idParamEyeBallX=Zt,this.idParamEyeBallY=Jt,this.idParamBodyAngleX=Qt,this.idParamBreath=Kt,this.pixelsPerUnit=1,this.centeringTransform=new i.Matrix,this.coreModel=t,this.settings=e,this.motionManager=new ze(e,s),this.init()}init(){var t;super.init(),(null==(t=this.settings.getEyeBlinkParameters())?void 0:t.length)>0&&(this.eyeBlink=l.create(this.settings)),this.breath.setParameters([new o(this.idParamAngleX,0,15,6.5345,.5),new o(this.idParamAngleY,0,8,3.5345,.5),new o(this.idParamAngleZ,0,10,5.5345,.5),new o(this.idParamBodyAngleX,0,4,15.5345,.5),new o(this.idParamBreath,0,.5,3.2345,.5)]),this.renderer.initialize(this.coreModel),this.renderer.setIsPremultipliedAlpha(!0)}getSize(){return[this.coreModel.getModel().canvasinfo.CanvasWidth,this.coreModel.getModel().canvasinfo.CanvasHeight]}getLayout(){const t={};if(this.settings.layout)for(const e of Object.keys(this.settings.layout)){t[e.charAt(0).toLowerCase()+e.slice(1)]=this.settings.layout[e]}return t}setupLayout(){super.setupLayout(),this.pixelsPerUnit=this.coreModel.getModel().canvasinfo.PixelsPerUnit,this.centeringTransform.scale(this.pixelsPerUnit,this.pixelsPerUnit).translate(this.originalWidth/2,this.originalHeight/2)}updateWebGLContext(t,e){this.renderer.firstDraw=!0,this.renderer._bufferData={vertex:null,uv:null,index:null},this.renderer.startUp(t),this.renderer._clippingManager._currentFrameNo=e,this.renderer._clippingManager._maskTexture=void 0,Ot.getInstance()._shaderSets=[]}bindTexture(t,e){this.renderer.bindTexture(t,e)}getHitAreaDefs(){var t,e;return null!=(e=null==(t=this.settings.hitAreas)?void 0:t.map((t=>({id:t.Id,name:t.Name,index:this.coreModel.getDrawableIndex(t.Id)}))))?e:[]}getDrawableIDs(){return this.coreModel.getDrawableIds()}getDrawableIndex(t){return this.coreModel.getDrawableIndex(t)}getDrawableVertices(t){if("string"==typeof t&&-1===(t=this.coreModel.getDrawableIndex(t)))throw new TypeError("Unable to find drawable ID: "+t);const e=this.coreModel.getDrawableVertices(t).slice();for(let i=0;i<e.length;i+=2)e[i]=e[i]*this.pixelsPerUnit+this.originalWidth/2,e[i+1]=-e[i+1]*this.pixelsPerUnit+this.originalHeight/2;return e}updateTransform(t){this.drawingMatrix.copyFrom(this.centeringTransform).prepend(this.localTransform).prepend(t)}update(t,e){var i,s,r,a;super.update(t,e),t/=1e3,e/=1e3;const o=this.coreModel;this.emit("beforeMotionUpdate");const n=this.motionManager.update(this.coreModel,e);this.emit("afterMotionUpdate"),o.saveParameters(),null==(i=this.motionManager.expressionManager)||i.update(o,e),n||null==(s=this.eyeBlink)||s.updateParameters(o,t),this.updateFocus(),this.updateNaturalMovements(1e3*t,1e3*e),null==(r=this.physics)||r.evaluate(o,t),null==(a=this.pose)||a.updateParameters(o,t),this.emit("beforeModelUpdate"),o.update(),o.loadParameters()}updateFocus(){this.coreModel.addParameterValueById(this.idParamEyeBallX,this.focusController.x),this.coreModel.addParameterValueById(this.idParamEyeBallY,this.focusController.y),this.coreModel.addParameterValueById(this.idParamAngleX,30*this.focusController.x),this.coreModel.addParameterValueById(this.idParamAngleY,30*this.focusController.y),this.coreModel.addParameterValueById(this.idParamAngleZ,this.focusController.x*this.focusController.y*-30),this.coreModel.addParameterValueById(this.idParamBodyAngleX,10*this.focusController.x)}updateNaturalMovements(t,e){var i;null==(i=this.breath)||i.updateParameters(this.coreModel,t/1e3)}draw(t){const e=this.drawingMatrix,i=We.getArray();i[0]=e.a,i[1]=e.b,i[4]=-e.c,i[5]=-e.d,i[12]=e.tx,i[13]=e.ty,this.renderer.setMvpMatrix(We),this.renderer.setRenderState(t.getParameter(t.FRAMEBUFFER_BINDING),this.viewport),this.renderer.drawModel()}destroy(){super.destroy(),this.renderer.release(),this.coreModel.release(),this.renderer=void 0,this.coreModel=void 0}}let Ye,qe=20;function $e(){return C.isStarted()?Promise.resolve():(null!=Ye||(Ye=new Promise(((t,e)=>{!function i(){try{Ze(),t()}catch(s){if(qe--,qe<0){const t=new Error("Failed to start up Cubism 4 framework.");return t.cause=s,void e(t)}ee.log("Cubism4","Startup failed, retrying 10ms later..."),setTimeout(i,10)}}()}))),Ye)}function Ze(t){t=Object.assign({logFunction:console.log,loggingLevel:S.LogLevel_Verbose},t),C.startUp(t),C.initialize()}function Je(){var t;null==(t=this.__moc)||t.release()}Te.registerRuntime({version:4,ready:$e,test:t=>t instanceof Xe||Xe.isValidJSON(t),isValidMoc(t){if(t.byteLength<4)return!1;const e=new Int8Array(t,0,4);return"MOC3"===String.fromCharCode(...e)},createModelSettings:t=>new Xe(t),createCoreModel(t){const e=F.create(t);try{const t=e.createModel();return t.__moc=e,t}catch(i){try{e.release()}catch(s){}throw i}},createInternalModel(t,e,i){const s=new He(t,e,i),r=t;return r.__moc&&(s.__moc=r.__moc,delete r.__moc,s.once("destroy",Je)),s},createPhysics:(t,e)=>gt.create(e),createPose:(t,e)=>u.create(e)}),t.ACubismMotion=R,t.BreathParameterData=o,t.CSM_ASSERT=b,t.Constant=P,t.Cubism4ExpressionManager=Ge,t.Cubism4InternalModel=He,t.Cubism4ModelSettings=Xe,t.Cubism4MotionManager=ze,t.CubismBlendMode=f,t.CubismBreath=a,t.CubismClippingContext=Rt,t.CubismClippingManager_WebGL=At,t.CubismDebug=E,t.CubismExpressionMotion=O,t.CubismEyeBlink=l,t.CubismFramework=C,t.CubismLogDebug=w,t.CubismLogError=T,t.CubismLogInfo=I,t.CubismLogVerbose=function(t,...e){E.print(S.LogLevel_Verbose,"[CSM][V]"+t+"\n",e)},t.CubismLogWarning=L,t.CubismMath=g,t.CubismMatrix44=p,t.CubismMoc=F,t.CubismModel=D,t.CubismModelSettingsJson=Ht,t.CubismModelUserData=B,t.CubismModelUserDataJson=A,t.CubismMotion=et,t.CubismMotionCurve=X,t.CubismMotionCurveTarget=N,t.CubismMotionData=W,t.CubismMotionEvent=z,t.CubismMotionJson=H,t.CubismMotionManager=class extends st{constructor(){super(),this._currentPriority=0,this._reservePriority=0}getCurrentPriority(){return this._currentPriority}getReservePriority(){return this._reservePriority}setReservePriority(t){this._reservePriority=t}startMotionPriority(t,e,i){return i==this._reservePriority&&(this._reservePriority=0),this._currentPriority=i,super.startMotion(t,e,this._userTimeSeconds)}updateMotion(t,e){this._userTimeSeconds+=e;const i=super.doUpdateMotion(t,this._userTimeSeconds);return this.isFinished()&&(this._currentPriority=0),i}reserveMotion(t){return!(t<=this._reservePriority||t<=this._currentPriority)&&(this._reservePriority=t,!0)}},t.CubismMotionPoint=j,t.CubismMotionQueueEntry=it,t.CubismMotionQueueManager=st,t.CubismMotionSegment=G,t.CubismMotionSegmentType=V,t.CubismPhysics=gt,t.CubismPhysicsInput=ht,t.CubismPhysicsJson=ct,t.CubismPhysicsOutput=ut,t.CubismPhysicsParticle=nt,t.CubismPhysicsRig=dt,t.CubismPhysicsSource=ot,t.CubismPhysicsSubRig=lt,t.CubismPhysicsTargetType=at,t.CubismPose=u,t.CubismRenderTextureResource=Bt,t.CubismRenderer=_,t.CubismRenderer_WebGL=Wt,t.CubismShader_WebGL=Ot,t.CubismTextureColor=y,t.CubismVector2=c,t.EvaluationOptionFlag=Y,t.ExpressionBlendType=U,t.ExpressionManager=oe,t.EyeState=h,t.FileLoader=Ne,t.FocusController=ne,t.HitAreaBody="Body",t.HitAreaHead="Head",t.HitAreaPrefix="HitArea",t.InteractionMixin=Ee,t.InternalModel=pe,t.InvalidMotionQueueEntryHandleValue=rt,t.LOGICAL_HEIGHT=2,t.LOGICAL_WIDTH=2,t.Live2DFactory=Te,t.Live2DLoader=ve,t.Live2DModel=ke,t.Live2DTransform=Ae,t.LogLevel=S,t.ModelSettings=le,t.MotionManager=me,t.MotionPreloadStrategy=ce,t.MotionPriority=he,t.MotionState=ue,t.Options=pt,t.ParamAngleX=Yt,t.ParamAngleY=qt,t.ParamAngleZ=$t,t.ParamArmLA="ParamArmLA",t.ParamArmLB="ParamArmLB",t.ParamArmRA="ParamArmRA",t.ParamArmRB="ParamArmRB",t.ParamBaseX="ParamBaseX",t.ParamBaseY="ParamBaseY",t.ParamBodyAngleX=Qt,t.ParamBodyAngleY="ParamBodyAngleY",t.ParamBodyAngleZ="ParamBodyAngleZ",t.ParamBreath=Kt,t.ParamBrowLAngle="ParamBrowLAngle",t.ParamBrowLForm="ParamBrowLForm",t.ParamBrowLX="ParamBrowLX",t.ParamBrowLY="ParamBrowLY",t.ParamBrowRAngle="ParamBrowRAngle",t.ParamBrowRForm="ParamBrowRForm",t.ParamBrowRX="ParamBrowRX",t.ParamBrowRY="ParamBrowRY",t.ParamBustX="ParamBustX",t.ParamBustY="ParamBustY",t.ParamCheek="ParamCheek",t.ParamEyeBallForm="ParamEyeBallForm",t.ParamEyeBallX=Zt,t.ParamEyeBallY=Jt,t.ParamEyeLOpen="ParamEyeLOpen",t.ParamEyeLSmile="ParamEyeLSmile",t.ParamEyeROpen="ParamEyeROpen",t.ParamEyeRSmile="ParamEyeRSmile",t.ParamHairBack="ParamHairBack",t.ParamHairFluffy="ParamHairFluffy",t.ParamHairFront="ParamHairFront",t.ParamHairSide="ParamHairSide",t.ParamHandL="ParamHandL",t.ParamHandR="ParamHandR",t.ParamMouthForm="ParamMouthForm",t.ParamMouthOpenY="ParamMouthOpenY",t.ParamNONE="NONE:",t.ParamShoulderY="ParamShoulderY",t.PartData=d,t.PartsArmLPrefix="Parts01ArmL_",t.PartsArmPrefix="Parts01Arm_",t.PartsArmRPrefix="Parts01ArmR_",t.PartsIdCore="Parts01Core",t.PhysicsJsonEffectiveForces=class{constructor(){this.gravity=new c(0,0),this.wind=new c(0,0)}},t.ShaderNames=kt,t.SoundManager=de,t.VERSION="0.4.0",t.XHRLoader=ye,t.ZipLoader=je,t.applyMixins=se,t.clamp=ie,t.copyArray=function(t,e,i,s,r){const a=e[s];Array.isArray(a)&&(i[r]=a.filter((e=>null!==e&&typeof e===t)))},t.copyProperty=function(t,e,i,s,r){const a=e[s];null!==a&&typeof a===t&&(i[r]=a)},t.csmRect=Tt,t.cubism4Ready=$e,t.folderName=re,t.fragmentShaderSrcMaskInvertedPremultipliedAlpha=zt,t.fragmentShaderSrcMaskPremultipliedAlpha=Xt,t.fragmentShaderSrcPremultipliedAlpha=Gt,t.fragmentShaderSrcsetupMask=Nt,t.logger=ee,t.rand=function(t,e){return Math.random()*(e-t)+t},t.remove=ae,t.startUpCubism4=Ze,t.vertexShaderSrc=Vt,t.vertexShaderSrcMasked=jt,t.vertexShaderSrcSetupMask=Ut,Object.defineProperties(t,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
diff --git a/xiaozhi-esp32-server/main/digital-human/js/live2d/live2d.js b/xiaozhi-esp32-server/main/digital-human/js/live2d/live2d.js
new file mode 100644
index 0000000..0c583c8
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/js/live2d/live2d.js
@@ -0,0 +1,824 @@
+/**
+ * Live2D 绠$悊鍣�
+ * 璐熻矗 Live2D 妯″瀷鐨勫垵濮嬪寲銆佸槾閮ㄥ姩鐢绘帶鍒剁瓑鍔熻兘
+ */
+class Live2DManager {
+ constructor() {
+ this.live2dApp = null;
+ this.live2dModel = null;
+ this.isTalking = false;
+ this.mouthAnimationId = null;
+ this.mouthParam = 'ParamMouthOpenY';
+ this.audioContext = null;
+ this.analyser = null;
+ this.dataArray = null;
+ this.lastEmotionActionTime = null;
+ this.currentModelName = null;
+
+ // 妯″瀷鐗瑰畾閰嶇疆
+ this.modelConfig = {
+ 'hiyori_pro_zh': {
+ mouthParam: 'ParamMouthOpenY',
+ mouthAmplitude: 1.0,
+ mouthThresholds: { low: 0.3, high: 0.7 },
+ motionMap: {
+ 'FlickUp': 'FlickUp',
+ 'FlickDown': 'FlickDown',
+ 'Tap': 'Tap',
+ 'Tap@Body': 'Tap@Body',
+ 'Flick': 'Flick',
+ 'Flick@Body': 'Flick@Body'
+ }
+ },
+ 'natori_pro_zh': {
+ mouthParam: 'ParamMouthOpenY',
+ mouthAmplitude: 1.0,
+ mouthThresholds: { low: 0.1, high: 0.4 },
+ mouthFormParam: 'ParamMouthForm',
+ mouthFormAmplitude: 1.0,
+ mouthForm2Param: 'ParamMouthForm2',
+ mouthForm2Amplitude: 0.8,
+ motionMap: {
+ 'FlickUp': 'FlickUp',
+ 'FlickDown': 'Flick@Body',
+ 'Tap': 'Tap',
+ 'Tap@Body': 'Tap@Head',
+ 'Flick': 'Tap',
+ 'Flick@Body': 'Flick@Body'
+ }
+ }
+ };
+
+ // 鎯呯华鍒板姩浣滅殑鏄犲皠
+ this.emotionToActionMap = {
+ 'happy': 'FlickUp', // 寮�蹇�-鍚戜笂杞绘壂鍔ㄤ綔
+ 'laughing': 'FlickUp', // 澶х瑧-鍚戜笂杞绘壂鍔ㄤ綔
+ 'funny': 'FlickUp', // 鎼炵瑧-鍚戜笂杞绘壂鍔ㄤ綔
+ 'sad': 'FlickDown', // 浼ゅ績-鍚戜笅杞绘壂鍔ㄤ綔
+ 'crying': 'FlickDown', // 鍝常-鍚戜笅杞绘壂鍔ㄤ綔
+ 'angry': 'Tap@Body', // 鐢熸皵-韬綋鐐瑰嚮鍔ㄤ綔
+ 'surprised': 'Tap', // 鎯婅-鐐瑰嚮鍔ㄤ綔
+ 'neutral': 'Flick', // 骞冲父-杞绘壂鍔ㄤ綔
+ 'default': 'Flick@Body' // 榛樿-韬綋杞绘壂鍔ㄤ綔
+ };
+
+ // 鍗�/鍙屽嚮鍒ゅ畾閰嶇疆涓庣姸鎬�
+ this._lastClickTime = 0;
+ this._lastClickPos = { x: 0, y: 0 };
+ this._singleClickTimer = null;
+ this._doubleClickMs = 280; // 鍙屽嚮鏃堕棿闃堝��(ms)
+ this._doubleClickDist = 16; // 鍙屽嚮鍏佽鐨勬渶澶т綅绉�(px)
+ // 婊戝姩鍒ゅ畾
+ this._pointerDown = false;
+ this._downPos = { x: 0, y: 0 };
+ this._downTime = 0;
+ this._downArea = 'Body';
+ this._movedBeyondClick = false;
+ this._swipeMinDist = 24; // 瑙﹀彂婊戝姩鐨勬渶灏忚窛绂�
+ }
+
+ /**
+ * 鍒濆鍖� Live2D
+ */
+ async initializeLive2D() {
+ try {
+ const canvas = document.getElementById('live2d-stage');
+
+ // 渚涘唴閮ㄤ娇鐢�
+ window.PIXI = PIXI;
+
+ this.live2dApp = new PIXI.Application({
+ view: canvas,
+ height: window.innerHeight,
+ width: window.innerWidth,
+ resolution: window.devicePixelRatio,
+ autoDensity: true,
+ antialias: true,
+ backgroundAlpha: 0,
+ });
+
+ // 鍔犺浇 Live2D 妯″瀷 - 鍔ㄦ�佹娴嬪綋鍓嶇洰褰曪紝閫傞厤涓嶅悓鐜
+ // 鑾峰彇褰撳墠HTML鏂囦欢鎵�鍦ㄧ殑鐩綍璺緞
+ const currentPath = window.location.pathname;
+ const lastSlashIndex = currentPath.lastIndexOf('/');
+ const basePath = currentPath.substring(0, lastSlashIndex + 1);
+
+ // 浠� localStorage 璇诲彇涓婃閫夋嫨鐨勬ā鍨嬶紝濡傛灉娌℃湁鍒欎娇鐢ㄩ粯璁�
+ const savedModelName = localStorage.getItem('live2dModel') || 'hiyori_pro_zh';
+ const modelFileMap = {
+ 'hiyori_pro_zh': 'hiyori_pro_t11.model3.json',
+ 'natori_pro_zh': 'natori_pro_t06.model3.json'
+ };
+ const modelFileName = modelFileMap[savedModelName] || 'hiyori_pro_t11.model3.json';
+ const modelPath = basePath + 'resources/' + savedModelName + '/runtime/' + modelFileName;
+
+ this.live2dModel = await PIXI.live2d.Live2DModel.from(modelPath);
+ this.live2dApp.stage.addChild(this.live2dModel);
+
+ // 淇濆瓨褰撳墠妯″瀷鍚嶇О
+ this.currentModelName = savedModelName;
+
+ // 鏇存柊涓嬫媺妗嗘樉绀�
+ const modelSelect = document.getElementById('live2dModelSelect');
+ if (modelSelect) {
+ modelSelect.value = savedModelName;
+ }
+
+ // 璁剧疆妯″瀷鐗瑰畾鐨勫槾閮ㄥ弬鏁板悕
+ if (this.modelConfig[savedModelName]) {
+ this.mouthParam = this.modelConfig[savedModelName].mouthParam || 'ParamMouthOpenY';
+ }
+
+ // 璁剧疆妯″瀷灞炴��
+ this.live2dModel.scale.set(0.33);
+ this.live2dModel.x = (window.innerWidth - this.live2dModel.width) * 0.5;
+ this.live2dModel.y = -50;
+
+ // 鍚敤浜や簰骞剁洃鍚偣鍑诲懡涓紙澶撮儴/韬綋绛夛級
+
+ this.live2dModel.interactive = true;
+
+
+ this.live2dModel.on('doublehit', (args) => {
+ const area = Array.isArray(args) ? args[0] : args;
+
+ // 瑙﹀彂鍙屽嚮鍔ㄤ綔
+ if (area === 'Body') {
+ this.motion('Flick@Body');
+ } else if (area === 'Head' || area === 'Face') {
+ this.motion('Flick');
+ }
+
+ const app = window.chatApp;
+ const payload = JSON.stringify({ type: 'live2d', event: 'doublehit', area });
+ if (app && app.dataChannel && app.dataChannel.readyState === 'open') {
+ app.dataChannel.send(payload);
+ }
+
+ });
+
+ this.live2dModel.on('singlehit', (args) => {
+ const area = Array.isArray(args) ? args[0] : args;
+
+ // 瑙﹀彂鍗曞嚮鍔ㄤ綔
+ if (area === 'Body') {
+ this.motion('Tap@Body');
+ } else if (area === 'Head' || area === 'Face') {
+ this.motion('Tap');
+ }
+
+ const app = window.chatApp;
+ const payload = JSON.stringify({ type: 'live2d', event: 'singlehit', area });
+ if (app && app.dataChannel && app.dataChannel.readyState === 'open') {
+ app.dataChannel.send(payload);
+ }
+
+ });
+
+ this.live2dModel.on('swipe', (args) => {
+ const area = Array.isArray(args) ? args[0] : args;
+ const dir = Array.isArray(args) ? args[1] : undefined;
+
+ // 瑙﹀彂婊戝姩鍔ㄤ綔
+ if (area === 'Body') {
+ if (dir === 'up') {
+ this.motion('FlickUp');
+ } else if (dir === 'down') {
+ this.motion('FlickDown');
+ }
+ } else if (area === 'Head' || area === 'Face') {
+ if (dir === 'up') {
+ this.motion('FlickUp');
+ } else if (dir === 'down') {
+ this.motion('FlickDown');
+ }
+ }
+
+ const app = window.chatApp;
+ const payload = JSON.stringify({ type: 'live2d', event: 'swipe', area, dir });
+ if (app && app.dataChannel && app.dataChannel.readyState === 'open') {
+ app.dataChannel.send(payload);
+ }
+
+ });
+
+ // 鍏滃簳锛氳嚜瀹氫箟"澶撮儴/韬綋"鍛戒腑鍖哄煙 + 鍗�/鍙屽嚮/婊戝姩鍖哄垎
+ this.live2dModel.on('pointerdown', (event) => {
+ try {
+ const global = event.data.global;
+ const bounds = this.live2dModel.getBounds();
+ // 浠呭湪鐐瑰嚮钀藉湪妯″瀷鍙鑼冨洿鍐呮椂鍒ゅ畾
+ if (!bounds || !bounds.contains(global.x, global.y)) return;
+
+ const relX = (global.x - bounds.x) / (bounds.width || 1);
+ const relY = (global.y - bounds.y) / (bounds.height || 1);
+ let area = '';
+ // 缁忛獙闃堝�硷細妯″瀷鍙鐭╁舰鐨勪笂閮� 20% 瑙嗕负"澶撮儴"鍖哄煙
+ if (relX >= 0.4 && relX <= 0.6) {
+ if (relY <= 0.15) {
+ area = 'Head';
+ } else if (relY <= 0.23) {
+ area = 'Face';
+ } else {
+ area = 'Body';
+ }
+ }
+ if (area === '') {
+ return;
+ }
+
+ // 璁板綍鎸変笅鐘舵�佺敤浜庢粦鍔ㄥ垽瀹�
+ this._pointerDown = true;
+ this._downPos = { x: global.x, y: global.y };
+ this._downTime = performance.now();
+ this._downArea = area;
+ this._movedBeyondClick = false;
+
+ const now = performance.now();
+ const dt = now - (this._lastClickTime || 0);
+ const dx = global.x - (this._lastClickPos?.x || 0);
+ const dy = global.y - (this._lastClickPos?.y || 0);
+ const dist = Math.hypot(dx, dy);
+
+ // 鍛戒腑纭锛氫粎褰撶偣鍑诲湪妯″瀷涓婃椂鍋氬崟/鍙屽嚮鍒ゆ柇
+ if (this._lastClickTime && dt <= this._doubleClickMs && dist <= this._doubleClickDist) {
+ // 鍒ゅ畾涓哄弻鍑伙細鍙栨秷寰呰Е鍙戠殑鍗曞嚮浜嬩欢
+ if (this._singleClickTimer) {
+ clearTimeout(this._singleClickTimer);
+ this._singleClickTimer = null;
+ }
+ if (typeof this.live2dModel.emit === 'function') {
+ this.live2dModel.emit('doublehit', [area]);
+ }
+ this._lastClickTime = 0;
+ this._pointerDown = false; // 鍙屽嚮瀹屾垚锛岄噸缃姸鎬�
+ return;
+ }
+
+ // 鍙兘鏄崟鍑伙細璁板綍骞跺欢杩熺‘璁�
+ this._lastClickTime = now;
+ this._lastClickPos = { x: global.x, y: global.y };
+ if (this._singleClickTimer) {
+ clearTimeout(this._singleClickTimer);
+ this._singleClickTimer = null;
+ }
+ this._singleClickTimer = setTimeout(() => {
+ // 鑻ュ湪绛夊緟鏈熼棿鍙戠敓浜嗙Щ鍔ㄨ秴杩囬槇鍊硷紝鍒欎笉鍐嶅綋浣滃崟鍑�
+ if (!this._movedBeyondClick && typeof this.live2dModel.emit === 'function') {
+ this.live2dModel.emit('singlehit', [area]);
+ }
+ this._singleClickTimer = null;
+ this._lastClickTime = 0;
+ }, this._doubleClickMs);
+ } catch (e) {
+ // 蹇界暐鑷畾涔夊懡涓垽鏂腑鐨勫紓甯革紝閬垮厤褰卞搷涓绘祦绋�
+ }
+ });
+
+ // 鎸囬拡绉诲姩锛氱敤浜庡垽瀹氭槸鍚︿粠"鐐瑰嚮"鍗囩骇涓�"婊戝姩"
+ this.live2dModel.on('pointermove', (event) => {
+ try {
+ if (!this._pointerDown) return;
+ const global = event.data.global;
+ const dx = global.x - this._downPos.x;
+ const dy = global.y - this._downPos.y;
+ const dist = Math.hypot(dx, dy);
+
+ // 浣跨敤 _doubleClickDist 浣滀负鐐瑰嚮/婊戝姩鐨勫垽瀹氶槇鍊�
+ if (dist > this._doubleClickDist) {
+ this._movedBeyondClick = true;
+ // 鑻ュ凡瓒呭嚭鐐瑰嚮闃堝�硷紝鍙栨秷鍙兘鐨勫崟鍑昏Е鍙�
+ if (this._singleClickTimer) {
+ clearTimeout(this._singleClickTimer);
+ this._singleClickTimer = null;
+ }
+ this._lastClickTime = 0;
+ }
+ } catch (e) {
+ // 蹇界暐绉诲姩鍒ゅ畾涓殑寮傚父
+ }
+ });
+
+ // 鎸囬拡鎶捣锛氱‘璁ゆ槸鍚︿负婊戝姩
+ const handlePointerUp = (event) => {
+ try {
+ if (!this._pointerDown) return;
+ const global = (event && event.data && event.data.global) ? event.data.global : { x: this._downPos.x, y: this._downPos.y };
+ const dx = global.x - this._downPos.x;
+ const dy = global.y - this._downPos.y;
+ const dist = Math.hypot(dx, dy);
+
+ // 婊戝姩锛氳秴杩囨粦鍔ㄦ渶灏忚窛绂诲垯瑙﹀彂 swipe 浜嬩欢锛堟惡甯︽柟鍚戜笌鍖哄煙锛�
+ if (this._movedBeyondClick && dist >= this._swipeMinDist) {
+ if (typeof this.live2dModel.emit === 'function') {
+ const dir = Math.abs(dx) >= Math.abs(dy)
+ ? (dx > 0 ? 'right' : 'left')
+ : (dy > 0 ? 'down' : 'up');
+ this.live2dModel.emit('swipe', [this._downArea, dir]);
+ }
+ // 缁堟锛氫笉鍐嶈鍗曞嚮/鍙屽嚮瑙﹀彂
+ if (this._singleClickTimer) {
+ clearTimeout(this._singleClickTimer);
+ this._singleClickTimer = null;
+ }
+ this._lastClickTime = 0;
+ }
+ } catch (e) {
+ // 蹇界暐鎶捣鍒ゅ畾涓殑寮傚父
+ }
+ finally {
+ this._pointerDown = false;
+ this._movedBeyondClick = false;
+ }
+ };
+
+ this.live2dModel.on('pointerup', handlePointerUp);
+ this.live2dModel.on('pointerupoutside', handlePointerUp);
+
+ // 娣诲姞绐楀彛澶у皬鍙樺寲鐩戝惉鍣紝淇濇寔妯″瀷鍦–anvas涓棿鍜屽簳閮�
+ window.addEventListener('resize', () => {
+ if (this.live2dModel) {
+ // 浣跨敤绐楀彛瀹為檯灏哄閲嶆柊璁$畻妯″瀷浣嶇疆
+ this.live2dModel.x = (window.innerWidth - this.live2dModel.width) * 0.5;
+ this.live2dModel.y = -50;
+ }
+ });
+
+ } catch (err) {
+ console.error('鍔犺浇 Live2D 妯″瀷澶辫触:', err);
+ }
+ }
+
+ /**
+ * 鍒濆鍖栭煶棰戝垎鏋愬櫒 - 浣跨敤闊抽鎾斁鍣ㄧ殑鍒嗘瀽鍣ㄨ妭鐐�
+ */
+ initializeAudioAnalyzer() {
+ try {
+ // 鑾峰彇闊抽鎾斁鍣ㄥ疄渚�
+ const audioPlayer = window.chatApp?.audioPlayer;
+ if (!audioPlayer) {
+ console.warn('闊抽鎾斁鍣ㄦ湭鍒濆鍖栵紝鏃犳硶鑾峰彇鍒嗘瀽鍣ㄨ妭鐐�');
+ return false;
+ }
+
+ // 鑾峰彇闊抽鎾斁鍣ㄧ殑闊抽涓婁笅鏂�
+ this.audioContext = audioPlayer.getAudioContext();
+ if (!this.audioContext) {
+ console.warn('鏃犳硶鑾峰彇闊抽鎾斁鍣ㄧ殑闊抽涓婁笅鏂�');
+ return false;
+ }
+
+ // 鍒涘缓鍒嗘瀽鍣ㄨ妭鐐�
+ this.analyser = this.audioContext.createAnalyser();
+ this.analyser.fftSize = 256;
+ this.dataArray = new Uint8Array(this.analyser.frequencyBinCount);
+
+ return true;
+ } catch (error) {
+ console.error('鍒濆鍖栭煶棰戝垎鏋愬櫒澶辫触:', error);
+ return false;
+ }
+ }
+
+ /**
+ * 杩炴帴鍒伴煶棰戞挱鏀惧櫒鐨勮緭鍑鸿妭鐐�
+ */
+ connectToAudioPlayer() {
+ try {
+ // 鑾峰彇闊抽鎾斁鍣ㄧ殑娴佷笂涓嬫枃
+ const audioPlayer = window.chatApp?.audioPlayer;
+ if (!audioPlayer || !audioPlayer.streamingContext) {
+ console.warn('闊抽鎾斁鍣ㄦ垨娴佷笂涓嬫枃鏈垵濮嬪寲');
+ return false;
+ }
+
+ // 鑾峰彇闊抽鎾斁鍣ㄧ殑娴佷笂涓嬫枃
+ const streamingContext = audioPlayer.streamingContext;
+
+ // 鑾峰彇鍒嗘瀽鍣ㄨ妭鐐�
+ const analyser = streamingContext.getAnalyser();
+ if (!analyser) {
+ console.warn('闊抽鎾斁鍣ㄥ皻鏈垱寤哄垎鏋愬櫒鑺傜偣锛屾棤娉曡繛鎺�');
+ return false;
+ }
+
+ // 浣跨敤闊抽鎾斁鍣ㄧ殑鍒嗘瀽鍣ㄨ妭鐐�
+ this.analyser = analyser;
+ this.dataArray = new Uint8Array(this.analyser.frequencyBinCount);
+ return true;
+ } catch (error) {
+ console.error('杩炴帴鍒伴煶棰戞挱鏀惧櫒澶辫触:', error);
+ return false;
+ }
+ }
+
+ /**
+ * 鍢撮儴鍔ㄧ敾寰幆
+ */
+ animateMouth() {
+ if (!this.isTalking) return;
+ if (!this.live2dModel) return;
+ const internal = this.live2dModel && this.live2dModel.internalModel;
+ if (internal && internal.coreModel) {
+ const coreModel = internal.coreModel;
+
+ let mouthOpenY = 0;
+ let mouthForm = 0;
+ let mouthForm2 = 0;
+ let average = 0;
+
+ if (this.analyser && this.dataArray) {
+ this.analyser.getByteFrequencyData(this.dataArray);
+ average = this.dataArray.reduce((a, b) => a + b) / this.dataArray.length;
+
+ const normalizedVolume = average / 255;
+
+ // 鑾峰彇妯″瀷鐗瑰畾鐨勯槇鍊�
+ let lowThreshold = 0.3;
+ let highThreshold = 0.7;
+ if (this.currentModelName && this.modelConfig[this.currentModelName]) {
+ lowThreshold = this.modelConfig[this.currentModelName].mouthThresholds?.low || 0.3;
+ highThreshold = this.modelConfig[this.currentModelName].mouthThresholds?.high || 0.7;
+ }
+
+ // 浣跨敤妯″瀷鐗瑰畾鐨勯槇鍊艰繘琛屾槧灏�
+ let minOpenY = 0.1;
+ if (this.currentModelName && this.modelConfig[this.currentModelName]) {
+ minOpenY = this.modelConfig[this.currentModelName].mouthMinOpenY || 0.1;
+ }
+
+ if (normalizedVolume < lowThreshold) {
+ mouthOpenY = minOpenY + Math.pow(normalizedVolume / lowThreshold, 1.5) * (0.4 - minOpenY);
+ } else if (normalizedVolume < highThreshold) {
+ mouthOpenY = 0.4 + (normalizedVolume - lowThreshold) / (highThreshold - lowThreshold) * 0.4;
+ } else {
+ mouthOpenY = 0.8 + Math.pow((normalizedVolume - highThreshold) / (1 - highThreshold), 1.2) * 0.2;
+ }
+
+ // 搴旂敤妯″瀷鐗瑰畾鐨勫槾閮ㄥ紑鍚堝箙搴�
+ let amplitudeMultiplier = 1.0;
+ let maxOpenY = 2.5;
+ if (this.currentModelName && this.modelConfig[this.currentModelName]) {
+ amplitudeMultiplier = this.modelConfig[this.currentModelName].mouthAmplitude;
+ maxOpenY = this.modelConfig[this.currentModelName].maxOpenY || 2.5;
+ }
+ mouthOpenY = mouthOpenY * amplitudeMultiplier;
+ mouthOpenY = Math.min(Math.max(mouthOpenY, 0), maxOpenY);
+
+ // 璁$畻鍢村瀷鍙傛暟锛堜粎瀵规敮鎸佸槾鍨嬪彉鍖栫殑妯″瀷锛�
+ if (this.currentModelName && this.modelConfig[this.currentModelName]?.mouthFormParam) {
+ const config = this.modelConfig[this.currentModelName];
+ const formAmplitude = config.mouthFormAmplitude || 0.5;
+ const form2Amplitude = config.mouthForm2Amplitude || 0;
+
+ // 鍢村瀷闅忛煶閲忓彉鍖栵細
+ // 浣庨煶閲忥細鍢村瀷鍋�"涓�"瀛楋紙璐熷�硷級
+ // 楂橀煶閲忥細鍢村瀷鍋�"o"瀛楋紙姝e�硷級
+ // 闊抽噺=0鏃讹細鍢村瀷=0锛堣嚜鐒剁姸鎬侊級
+ mouthForm = (normalizedVolume - 0.5) * 2 * formAmplitude;
+ mouthForm = Math.max(-formAmplitude, Math.min(formAmplitude, mouthForm));
+
+ // 绗簩鍢村瀷鍙傛暟锛坣atori鐗规湁锛�
+ if (config.mouthForm2Param) {
+ mouthForm2 = (normalizedVolume - 0.3) * 2 * form2Amplitude;
+ mouthForm2 = Math.max(-form2Amplitude, Math.min(form2Amplitude, mouthForm2));
+ }
+ }
+
+ // 璋冭瘯鏃ュ織锛氳緭鍑哄槾閮ㄥ弬鏁�
+ console.log(`[Live2D] 妯″瀷: ${this.currentModelName || 'unknown'}, 闊抽噺: ${average?.toFixed(0)}, OpenY: ${mouthOpenY.toFixed(3)}, Form: ${mouthForm.toFixed(3)}, Form2: ${mouthForm2.toFixed(3)}`);
+ }
+
+ // 璁剧疆鍢撮儴寮�鍚堝弬鏁�
+ coreModel.setParameterValueById(this.mouthParam, mouthOpenY);
+
+ // 璁剧疆鍢村瀷鍙傛暟锛堜粎瀵规敮鎸佸槾鍨嬪彉鍖栫殑妯″瀷锛�
+ if (this.currentModelName && this.modelConfig[this.currentModelName]?.mouthFormParam) {
+ const config = this.modelConfig[this.currentModelName];
+ const formParam = config.mouthFormParam;
+ coreModel.setParameterValueById(formParam, mouthForm);
+
+ // 璁剧疆绗簩鍢村瀷鍙傛暟锛坣atori鐗规湁锛�
+ if (config.mouthForm2Param) {
+ coreModel.setParameterValueById(config.mouthForm2Param, mouthForm2);
+ }
+ }
+
+ coreModel.update();
+ }
+ this.mouthAnimationId = requestAnimationFrame(() => this.animateMouth());
+ }
+
+ /**
+ * 寮�濮嬭璇濆姩鐢�
+ */
+ startTalking() {
+ if (this.isTalking || !this.live2dModel) return;
+
+ // 纭繚闊抽鍒嗘瀽鍣ㄥ凡鍒濆鍖�
+ if (!this.analyser) {
+ if (!this.initializeAudioAnalyzer()) {
+ console.warn('闊抽鍒嗘瀽鍣ㄥ垵濮嬪寲澶辫触锛屽皢浣跨敤妯℃嫙鍔ㄧ敾');
+ // 鍗充娇鍒嗘瀽鍣ㄥ垵濮嬪寲澶辫触锛屼篃鍚姩鍔ㄧ敾锛堜娇鐢ㄦā鎷熸暟鎹級
+ this.isTalking = true;
+ this.animateMouth();
+ return;
+ }
+ }
+
+ // 杩炴帴鍒伴煶棰戞挱鏀惧櫒杈撳嚭
+ if (!this.connectToAudioPlayer()) {
+ console.warn('鏃犳硶杩炴帴鍒伴煶棰戞挱鏀惧櫒杈撳嚭锛屽皢浣跨敤妯℃嫙鍔ㄧ敾');
+ }
+
+ this.isTalking = true;
+ this.animateMouth();
+ }
+
+ /**
+ * 鍋滄璇磋瘽鍔ㄧ敾
+ */
+ stopTalking() {
+ this.isTalking = false;
+ if (this.mouthAnimationId) {
+ cancelAnimationFrame(this.mouthAnimationId);
+ this.mouthAnimationId = null;
+ }
+
+ // 閲嶇疆鍢撮儴鍙傛暟
+ if (this.live2dModel) {
+ const internal = this.live2dModel.internalModel;
+ if (internal && internal.coreModel) {
+ const coreModel = internal.coreModel;
+ coreModel.setParameterValueById(this.mouthParam, 0);
+ coreModel.update();
+ }
+ }
+ }
+
+ /**
+ * 鍩轰簬鎯呯华瑙﹀彂鍔ㄤ綔
+ * @param {string} emotion - 鎯呯华鍚嶇О
+ */
+ triggerEmotionAction(emotion) {
+ if (!this.live2dModel) return;
+
+ // 娣诲姞鍐峰嵈鏃堕棿鎺у埗锛岄伩鍏嶈繃浜庨绻佽Е鍙�
+ const now = Date.now();
+ if (this.lastEmotionActionTime && now - this.lastEmotionActionTime < 5000) { // 5绉掑喎鍗存椂闂�
+ return;
+ }
+
+ // 鏍规嵁鎯呯华鑾峰彇瀵瑰簲鐨勫姩浣�
+ const action = this.emotionToActionMap[emotion] || this.emotionToActionMap['default'];
+
+ // 瑙﹀彂鍔ㄤ綔骞惰褰曟椂闂�
+ this.motion(action);
+ this.lastEmotionActionTime = now;
+ }
+
+
+
+ /**
+ * 瑙﹀彂妯″瀷鍔ㄤ綔锛圡otion锛�
+ * @param {string} name - 鍔ㄤ綔鍒嗙粍鍚嶇О锛屽 'TapBody'銆�'FlickUp'銆�'Idle' 绛�
+ */
+ motion(name) {
+ try {
+ if (!this.live2dModel) return;
+
+ // 鏍规嵁褰撳墠妯″瀷鑾峰彇瀵瑰簲鐨勫姩浣滃悕绉�
+ let actualMotionName = name;
+ if (this.currentModelName && this.modelConfig[this.currentModelName]) {
+ const motionMap = this.modelConfig[this.currentModelName].motionMap;
+ actualMotionName = motionMap[name] || name;
+ }
+
+ this.live2dModel.motion(actualMotionName);
+ } catch (error) {
+ console.error('瑙﹀彂鍔ㄤ綔澶辫触:', error);
+ }
+ }
+
+ /**
+ * 璁剧疆妯″瀷浜や簰浜嬩欢
+ */
+ setupModelInteractions() {
+ if (!this.live2dModel) return;
+
+ this.live2dModel.interactive = true;
+
+ this.live2dModel.on('doublehit', (args) => {
+ const area = Array.isArray(args) ? args[0] : args;
+
+ if (area === 'Body') {
+ this.motion('Flick@Body');
+ } else if (area === 'Head' || area === 'Face') {
+ this.motion('Flick');
+ }
+
+ const app = window.chatApp;
+ const payload = JSON.stringify({ type: 'live2d', event: 'doublehit', area });
+ if (app && app.dataChannel && app.dataChannel.readyState === 'open') {
+ app.dataChannel.send(payload);
+ }
+ });
+
+ this.live2dModel.on('singlehit', (args) => {
+ const area = Array.isArray(args) ? args[0] : args;
+
+ if (area === 'Body') {
+ this.motion('Tap@Body');
+ } else if (area === 'Head' || area === 'Face') {
+ this.motion('Tap');
+ }
+
+ const app = window.chatApp;
+ const payload = JSON.stringify({ type: 'live2d', event: 'singlehit', area });
+ if (app && app.dataChannel && app.dataChannel.readyState === 'open') {
+ app.dataChannel.send(payload);
+ }
+ });
+
+ this.live2dModel.on('swipe', (args) => {
+ const area = Array.isArray(args) ? args[0] : args;
+ const dir = Array.isArray(args) ? args[1] : undefined;
+
+ if (area === 'Body') {
+ if (dir === 'up') {
+ this.motion('FlickUp');
+ } else if (dir === 'down') {
+ this.motion('FlickDown');
+ }
+ }
+
+ const app = window.chatApp;
+ const payload = JSON.stringify({ type: 'live2d', event: 'swipe', area, dir });
+ if (app && app.dataChannel && app.dataChannel.readyState === 'open') {
+ app.dataChannel.send(payload);
+ }
+ });
+
+ this.live2dModel.on('pointerdown', (event) => {
+ try {
+ const global = event.data.global;
+ const bounds = this.live2dModel.getBounds();
+ if (!bounds || !bounds.contains(global.x, global.y)) return;
+
+ const relX = (global.x - bounds.x) / (bounds.width || 1);
+ const relY = (global.y - bounds.y) / (bounds.height || 1);
+ let area = '';
+
+ if (relX >= 0.4 && relX <= 0.6) {
+ if (relY <= 0.15) {
+ area = 'Head';
+ } else if (relY >= 0.7) {
+ area = 'Body';
+ }
+ }
+
+ if (!area) return;
+
+ const now = Date.now();
+ const dt = now - (this._lastClickTime || 0);
+ const dx = global.x - (this._lastClickPos?.x || 0);
+ const dy = global.y - (this._lastClickPos?.y || 0);
+ const dist = Math.hypot(dx, dy);
+
+ if (this._lastClickTime && dt <= this._doubleClickMs && dist <= this._doubleClickDist) {
+ if (this._singleClickTimer) {
+ clearTimeout(this._singleClickTimer);
+ this._singleClickTimer = null;
+ }
+
+ this.live2dModel.emit('doublehit', area);
+ this._lastClickTime = null;
+ this._lastClickPos = null;
+ } else {
+ this._lastClickTime = now;
+ this._lastClickPos = { x: global.x, y: global.y };
+
+ this._singleClickTimer = setTimeout(() => {
+ this._singleClickTimer = null;
+ this.live2dModel.emit('singlehit', area);
+ }, this._doubleClickMs);
+ }
+ } catch (e) {
+ console.warn('pointerdown 澶勭悊鍑洪敊:', e);
+ }
+ });
+ }
+
+ /**
+ * 娓呯悊璧勬簮
+ */
+ destroy() {
+ this.stopTalking();
+
+ // 娓呯悊闊抽鍒嗘瀽鍣�
+ if (this.audioContext) {
+ this.audioContext.close();
+ this.audioContext = null;
+ }
+ this.analyser = null;
+ this.dataArray = null;
+
+ // 娓呯悊 Live2D 搴旂敤
+ if (this.live2dApp) {
+ this.live2dApp.destroy(true);
+ this.live2dApp = null;
+ }
+ this.live2dModel = null;
+ }
+
+ /**
+ * 鍒囨崲 Live2D 妯″瀷
+ * @param {string} modelName - 妯″瀷鐩綍鍚嶇О锛屽 'hiyori_pro_zh'銆�'natori_pro_zh'
+ * @returns {Promise<boolean>} - 鍒囨崲鏄惁鎴愬姛
+ */
+ async switchModel(modelName) {
+ try {
+ // 鑾峰彇妯″瀷鏂囦欢鍚嶆槧灏�
+ const modelFileMap = {
+ 'hiyori_pro_zh': 'hiyori_pro_t11.model3.json',
+ 'natori_pro_zh': 'natori_pro_t06.model3.json',
+ 'chitose': 'chitose.model3.json',
+ 'haru_greeter_pro_jp': 'haru_greeter_t05.model3.json'
+ };
+
+ const modelFileName = modelFileMap[modelName];
+ if (!modelFileName) {
+ console.error('鏈煡鐨勬ā鍨嬪悕绉�:', modelName);
+ return false;
+ }
+
+ // 鑾峰彇鍩虹璺緞
+ const currentPath = window.location.pathname;
+ const lastSlashIndex = currentPath.lastIndexOf('/');
+ const basePath = currentPath.substring(0, lastSlashIndex + 1);
+ const modelPath = basePath + 'resources/' + modelName + '/runtime/' + modelFileName;
+
+ // 濡傛灉宸插瓨鍦ㄦā鍨嬶紝鍏堢Щ闄�
+ if (this.live2dModel) {
+ this.live2dApp.stage.removeChild(this.live2dModel);
+ this.live2dModel.destroy();
+ this.live2dModel = null;
+ }
+
+ // 鏄剧ず鍔犺浇鐘舵��
+ const app = window.chatApp;
+ if (app) {
+ app.setModelLoadingStatus(true);
+ }
+
+ // 鍔犺浇鏂版ā鍨�
+ this.live2dModel = await PIXI.live2d.Live2DModel.from(modelPath);
+ this.live2dApp.stage.addChild(this.live2dModel);
+
+ // 璁剧疆妯″瀷灞炴��
+ this.live2dModel.scale.set(0.33);
+ this.live2dModel.x = (window.innerWidth - this.live2dModel.width) * 0.5;
+ this.live2dModel.y = -50;
+
+ // 閲嶆柊缁戝畾浜や簰浜嬩欢
+ this.setupModelInteractions();
+
+ // 闅愯棌鍔犺浇鐘舵��
+ if (app) {
+ app.setModelLoadingStatus(false);
+ }
+
+ // 淇濆瓨褰撳墠妯″瀷鍚嶇О
+ this.currentModelName = modelName;
+
+ // 璁剧疆妯″瀷鐗瑰畾鐨勫槾閮ㄥ弬鏁板悕
+ if (this.modelConfig[modelName]) {
+ this.mouthParam = this.modelConfig[modelName].mouthParam || 'ParamMouthOpenY';
+ }
+
+ // 淇濆瓨鍒� localStorage
+ localStorage.setItem('live2dModel', modelName);
+
+ // 鏇存柊涓嬫媺妗嗘樉绀�
+ const modelSelect = document.getElementById('live2dModelSelect');
+ if (modelSelect) {
+ modelSelect.value = modelName;
+ }
+
+ console.log('妯″瀷鍒囨崲鎴愬姛:', modelName);
+ return true;
+ } catch (error) {
+ console.error('鍒囨崲妯″瀷澶辫触:', error);
+ const app = window.chatApp;
+ if (app) {
+ app.setModelLoadingStatus(false);
+ }
+ return false;
+ }
+ }
+
+
+}
+
+// 瀵煎嚭鍏ㄥ眬瀹炰緥
+window.Live2DManager = Live2DManager;
diff --git a/xiaozhi-esp32-server/main/digital-human/js/live2d/live2dcubismcore.min.js b/xiaozhi-esp32-server/main/digital-human/js/live2d/live2dcubismcore.min.js
new file mode 100644
index 0000000..d225300
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/js/live2d/live2dcubismcore.min.js
@@ -0,0 +1,9 @@
+/**
+ * Live2D Cubism Core
+ * (C) 2019 Live2D Inc. All rights reserved.
+ *
+ * This file is licensed pursuant to the license agreement below.
+ * This file corresponds to the "Redistributable Code" in the agreement.
+ * https://www.live2d.com/eula/live2d-proprietary-software-license-agreement_en.html
+ */
+var Live2DCubismCore;!function(Live2DCubismCore){var _scriptDir,_csm=function(){function _csm(){}return _csm.getVersion=function(){return _em.ccall("csmGetVersion","number",[],[])},_csm.getLatestMocVersion=function(){return _em.ccall("csmGetLatestMocVersion","number",[],[])},_csm.getMocVersion=function(moc,mocSize){return _em.ccall("csmGetMocVersion","number",["number","number"],[moc,mocSize])},_csm.getLogFunction=function(){return _em.ccall("csmGetLogFunction","number",[],[])},_csm.getSizeofModel=function(moc){return _em.ccall("csmGetSizeofModel","number",["number"],[moc])},_csm.reviveMocInPlace=function(memory,mocSize){return _em.ccall("csmReviveMocInPlace","number",["number","number"],[memory,mocSize])},_csm.initializeModelInPlace=function(moc,memory,modelSize){return _em.ccall("csmInitializeModelInPlace","number",["number","number","number"],[moc,memory,modelSize])},_csm.hasMocConsistency=function(memory,mocSize){return _em.ccall("csmHasMocConsistency","number",["number","number"],[memory,mocSize])},_csm.getParameterCount=function(model){return _em.ccall("csmGetParameterCount","number",["number"],[model])},_csm.getParameterIds=function(model){return _em.ccall("csmGetParameterIds","number",["number"],[model])},_csm.getParameterMinimumValues=function(model){return _em.ccall("csmGetParameterMinimumValues","number",["number"],[model])},_csm.getParameterTypes=function(model){return _em.ccall("csmGetParameterTypes","number",["number"],[model])},_csm.getParameterMaximumValues=function(model){return _em.ccall("csmGetParameterMaximumValues","number",["number"],[model])},_csm.getParameterDefaultValues=function(model){return _em.ccall("csmGetParameterDefaultValues","number",["number"],[model])},_csm.getParameterValues=function(model){return _em.ccall("csmGetParameterValues","number",["number"],[model])},_csm.getParameterRepeats=function(model){return _em.ccall("csmGetParameterRepeats","number",["number"],[model])},_csm.getParameterKeyCounts=function(model){return _em.ccall("csmGetParameterKeyCounts","number",["number"],[model])},_csm.getParameterKeyValues=function(model){return _em.ccall("csmGetParameterKeyValues","number",["number"],[model])},_csm.getPartCount=function(model){return _em.ccall("csmGetPartCount","number",["number"],[model])},_csm.getPartIds=function(model){return _em.ccall("csmGetPartIds","number",["number"],[model])},_csm.getPartOpacities=function(model){return _em.ccall("csmGetPartOpacities","number",["number"],[model])},_csm.getPartParentPartIndices=function(model){return _em.ccall("csmGetPartParentPartIndices","number",["number"],[model])},_csm.getDrawableCount=function(model){return _em.ccall("csmGetDrawableCount","number",["number"],[model])},_csm.getDrawableIds=function(model){return _em.ccall("csmGetDrawableIds","number",["number"],[model])},_csm.getDrawableConstantFlags=function(model){return _em.ccall("csmGetDrawableConstantFlags","number",["number"],[model])},_csm.getDrawableDynamicFlags=function(model){return _em.ccall("csmGetDrawableDynamicFlags","number",["number"],[model])},_csm.getDrawableTextureIndices=function(model){return _em.ccall("csmGetDrawableTextureIndices","number",["number"],[model])},_csm.getDrawableDrawOrders=function(model){return _em.ccall("csmGetDrawableDrawOrders","number",["number"],[model])},_csm.getDrawableRenderOrders=function(model){return _em.ccall("csmGetDrawableRenderOrders","number",["number"],[model])},_csm.getDrawableOpacities=function(model){return _em.ccall("csmGetDrawableOpacities","number",["number"],[model])},_csm.getDrawableMaskCounts=function(model){return _em.ccall("csmGetDrawableMaskCounts","number",["number"],[model])},_csm.getDrawableMasks=function(model){return _em.ccall("csmGetDrawableMasks","number",["number"],[model])},_csm.getDrawableVertexCounts=function(model){return _em.ccall("csmGetDrawableVertexCounts","number",["number"],[model])},_csm.getDrawableVertexPositions=function(model){return _em.ccall("csmGetDrawableVertexPositions","number",["number"],[model])},_csm.getDrawableVertexUvs=function(model){return _em.ccall("csmGetDrawableVertexUvs","number",["number"],[model])},_csm.getDrawableIndexCounts=function(model){return _em.ccall("csmGetDrawableIndexCounts","number",["number"],[model])},_csm.getDrawableIndices=function(model){return _em.ccall("csmGetDrawableIndices","number",["number"],[model])},_csm.getDrawableMultiplyColors=function(model){return _em.ccall("csmGetDrawableMultiplyColors","number",["number"],[model])},_csm.getDrawableScreenColors=function(model){return _em.ccall("csmGetDrawableScreenColors","number",["number"],[model])},_csm.getDrawableParentPartIndices=function(model){return _em.ccall("csmGetDrawableParentPartIndices","number",["number"],[model])},_csm.mallocMoc=function(mocSize){return _em.ccall("csmMallocMoc","number",["number"],[mocSize])},_csm.mallocModelAndInitialize=function(moc){return _em.ccall("csmMallocModelAndInitialize","number",["number"],[moc])},_csm.malloc=function(size){return _em.ccall("csmMalloc","number",["number"],[size])},_csm.setLogFunction=function(handler){_em.ccall("csmSetLogFunction",null,["number"],[handler])},_csm.updateModel=function(model){_em.ccall("csmUpdateModel",null,["number"],[model])},_csm.readCanvasInfo=function(model,outSizeInPixels,outOriginInPixels,outPixelsPerUnit){_em.ccall("csmReadCanvasInfo",null,["number","number","number","number"],[model,outSizeInPixels,outOriginInPixels,outPixelsPerUnit])},_csm.resetDrawableDynamicFlags=function(model){_em.ccall("csmResetDrawableDynamicFlags",null,["number"],[model])},_csm.free=function(memory){_em.ccall("csmFree",null,["number"],[memory])},_csm.initializeAmountOfMemory=function(size){_em.ccall("csmInitializeAmountOfMemory",null,["number"],[size])},_csm}(),Version=(Live2DCubismCore.AlignofMoc=64,Live2DCubismCore.AlignofModel=16,Live2DCubismCore.MocVersion_Unknown=0,Live2DCubismCore.MocVersion_30=1,Live2DCubismCore.MocVersion_33=2,Live2DCubismCore.MocVersion_40=3,Live2DCubismCore.MocVersion_42=4,Live2DCubismCore.MocVersion_50=5,Live2DCubismCore.ParameterType_Normal=0,Live2DCubismCore.ParameterType_BlendShape=1,function(){function Version(){}return Version.csmGetVersion=function(){return _csm.getVersion()},Version.csmGetLatestMocVersion=function(){return _csm.getLatestMocVersion()},Version.csmGetMocVersion=function(moc,mocBytes){return _csm.getMocVersion(moc._ptr,mocBytes.byteLength)},Version}()),Version=(Live2DCubismCore.Version=Version,function(){function Logging(){}return Logging.csmSetLogFunction=function(handler){Logging.logFunction=handler;handler=_em.addFunction(Logging.wrapLogFunction,"vi");_csm.setLogFunction(handler)},Logging.csmGetLogFunction=function(){return Logging.logFunction},Logging.wrapLogFunction=function(messagePtr){messagePtr=_em.UTF8ToString(messagePtr);Logging.logFunction(messagePtr)},Logging}()),Version=(Live2DCubismCore.Logging=Version,function(){function Moc(mocBytes){var memory=_csm.mallocMoc(mocBytes.byteLength);memory&&(new Uint8Array(_em.HEAPU8.buffer,memory,mocBytes.byteLength).set(new Uint8Array(mocBytes)),this._ptr=_csm.reviveMocInPlace(memory,mocBytes.byteLength),this._ptr||_csm.free(memory))}return Moc.prototype.hasMocConsistency=function(mocBytes){var memory=_csm.mallocMoc(mocBytes.byteLength);if(memory)return new Uint8Array(_em.HEAPU8.buffer,memory,mocBytes.byteLength).set(new Uint8Array(mocBytes)),mocBytes=_csm.hasMocConsistency(memory,mocBytes.byteLength),_csm.free(memory),mocBytes},Moc.fromArrayBuffer=function(buffer){return buffer&&(buffer=new Moc(buffer))._ptr?buffer:null},Moc.prototype._release=function(){_csm.free(this._ptr),this._ptr=0},Moc}()),Version=(Live2DCubismCore.Moc=Version,function(){function Model(moc){this._ptr=_csm.mallocModelAndInitialize(moc._ptr),this._ptr&&(this.parameters=new Parameters(this._ptr),this.parts=new Parts(this._ptr),this.drawables=new Drawables(this._ptr),this.canvasinfo=new CanvasInfo(this._ptr))}return Model.fromMoc=function(moc){moc=new Model(moc);return moc._ptr?moc:null},Model.prototype.update=function(){_csm.updateModel(this._ptr)},Model.prototype.release=function(){_csm.free(this._ptr),this._ptr=0},Model}()),CanvasInfo=(Live2DCubismCore.Model=Version,function(modelPtr){var _canvasSize_data,_canvasSize_dataPtr,_canvasSize_nDataBytes,_canvasOrigin_dataPtr,_canvasOrigin_nDataBytes,_canvasPPU_nDataBytes,_canvasPPU_dataPtr;modelPtr&&(_canvasSize_nDataBytes=(_canvasSize_data=new Float32Array(2)).length*_canvasSize_data.BYTES_PER_ELEMENT,_canvasSize_dataPtr=_csm.malloc(_canvasSize_nDataBytes),(_canvasSize_dataPtr=new Uint8Array(_em.HEAPU8.buffer,_canvasSize_dataPtr,_canvasSize_nDataBytes)).set(new Uint8Array(_canvasSize_data.buffer)),_canvasOrigin_nDataBytes=(_canvasSize_nDataBytes=new Float32Array(2)).length*_canvasSize_nDataBytes.BYTES_PER_ELEMENT,_canvasOrigin_dataPtr=_csm.malloc(_canvasOrigin_nDataBytes),(_canvasOrigin_dataPtr=new Uint8Array(_em.HEAPU8.buffer,_canvasOrigin_dataPtr,_canvasOrigin_nDataBytes)).set(new Uint8Array(_canvasSize_nDataBytes.buffer)),_canvasPPU_nDataBytes=(_canvasOrigin_nDataBytes=new Float32Array(1)).length*_canvasOrigin_nDataBytes.BYTES_PER_ELEMENT,_canvasPPU_dataPtr=_csm.malloc(_canvasPPU_nDataBytes),(_canvasPPU_dataPtr=new Uint8Array(_em.HEAPU8.buffer,_canvasPPU_dataPtr,_canvasPPU_nDataBytes)).set(new Uint8Array(_canvasOrigin_nDataBytes.buffer)),_csm.readCanvasInfo(modelPtr,_canvasSize_dataPtr.byteOffset,_canvasOrigin_dataPtr.byteOffset,_canvasPPU_dataPtr.byteOffset),_canvasSize_data=new Float32Array(_canvasSize_dataPtr.buffer,_canvasSize_dataPtr.byteOffset,_canvasSize_dataPtr.length),_canvasSize_nDataBytes=new Float32Array(_canvasOrigin_dataPtr.buffer,_canvasOrigin_dataPtr.byteOffset,_canvasOrigin_dataPtr.length),_canvasOrigin_nDataBytes=new Float32Array(_canvasPPU_dataPtr.buffer,_canvasPPU_dataPtr.byteOffset,_canvasPPU_dataPtr.length),this.CanvasWidth=_canvasSize_data[0],this.CanvasHeight=_canvasSize_data[1],this.CanvasOriginX=_canvasSize_nDataBytes[0],this.CanvasOriginY=_canvasSize_nDataBytes[1],this.PixelsPerUnit=_canvasOrigin_nDataBytes[0],_csm.free(_canvasSize_dataPtr.byteOffset),_csm.free(_canvasOrigin_dataPtr.byteOffset),_csm.free(_canvasPPU_dataPtr.byteOffset))}),Parameters=(Live2DCubismCore.CanvasInfo=CanvasInfo,function(modelPtr){this.count=_csm.getParameterCount(modelPtr),length=_csm.getParameterCount(modelPtr),this.ids=new Array(length);for(var length,length2,_ids=new Uint32Array(_em.HEAPU32.buffer,_csm.getParameterIds(modelPtr),length),i=0;i<_ids.length;i++)this.ids[i]=_em.UTF8ToString(_ids[i]);length=_csm.getParameterCount(modelPtr),this.minimumValues=new Float32Array(_em.HEAPF32.buffer,_csm.getParameterMinimumValues(modelPtr),length),length=_csm.getParameterCount(modelPtr),this.types=new Int32Array(_em.HEAP32.buffer,_csm.getParameterTypes(modelPtr),length),length=_csm.getParameterCount(modelPtr),this.maximumValues=new Float32Array(_em.HEAPF32.buffer,_csm.getParameterMaximumValues(modelPtr),length),length=_csm.getParameterCount(modelPtr),this.defaultValues=new Float32Array(_em.HEAPF32.buffer,_csm.getParameterDefaultValues(modelPtr),length),length=_csm.getParameterCount(modelPtr),this.values=new Float32Array(_em.HEAPF32.buffer,_csm.getParameterValues(modelPtr),length),length=_csm.getParameterCount(modelPtr),this.repeats=new Int32Array(_em.HEAP32.buffer,_csm.getParameterRepeats(modelPtr),length),length=_csm.getParameterCount(modelPtr),this.keyCounts=new Int32Array(_em.HEAP32.buffer,_csm.getParameterKeyCounts(modelPtr),length),length=_csm.getParameterCount(modelPtr),length2=new Int32Array(_em.HEAP32.buffer,_csm.getParameterKeyCounts(modelPtr),length),this.keyValues=new Array(length);for(var _keyValues=new Uint32Array(_em.HEAPU32.buffer,_csm.getParameterKeyValues(modelPtr),length),i=0;i<_keyValues.length;i++)this.keyValues[i]=new Float32Array(_em.HEAPF32.buffer,_keyValues[i],length2[i])}),Parts=(Live2DCubismCore.Parameters=Parameters,function(modelPtr){this.count=_csm.getPartCount(modelPtr),length=_csm.getPartCount(modelPtr),this.ids=new Array(length);for(var length,_ids=new Uint32Array(_em.HEAPU32.buffer,_csm.getPartIds(modelPtr),length),i=0;i<_ids.length;i++)this.ids[i]=_em.UTF8ToString(_ids[i]);length=_csm.getPartCount(modelPtr),this.opacities=new Float32Array(_em.HEAPF32.buffer,_csm.getPartOpacities(modelPtr),length),length=_csm.getPartCount(modelPtr),this.parentIndices=new Int32Array(_em.HEAP32.buffer,_csm.getPartParentPartIndices(modelPtr),length)}),Drawables=(Live2DCubismCore.Parts=Parts,function(){function Drawables(modelPtr){this._modelPtr=modelPtr;for(var length,length2=null,_ids=(this.count=_csm.getDrawableCount(modelPtr),length=_csm.getDrawableCount(modelPtr),this.ids=new Array(length),new Uint32Array(_em.HEAPU32.buffer,_csm.getDrawableIds(modelPtr),length)),i=0;i<_ids.length;i++)this.ids[i]=_em.UTF8ToString(_ids[i]);length=_csm.getDrawableCount(modelPtr),this.constantFlags=new Uint8Array(_em.HEAPU8.buffer,_csm.getDrawableConstantFlags(modelPtr),length),length=_csm.getDrawableCount(modelPtr),this.dynamicFlags=new Uint8Array(_em.HEAPU8.buffer,_csm.getDrawableDynamicFlags(modelPtr),length),length=_csm.getDrawableCount(modelPtr),this.textureIndices=new Int32Array(_em.HEAP32.buffer,_csm.getDrawableTextureIndices(modelPtr),length),length=_csm.getDrawableCount(modelPtr),this.drawOrders=new Int32Array(_em.HEAP32.buffer,_csm.getDrawableDrawOrders(modelPtr),length),length=_csm.getDrawableCount(modelPtr),this.renderOrders=new Int32Array(_em.HEAP32.buffer,_csm.getDrawableRenderOrders(modelPtr),length),length=_csm.getDrawableCount(modelPtr),this.opacities=new Float32Array(_em.HEAPF32.buffer,_csm.getDrawableOpacities(modelPtr),length),length=_csm.getDrawableCount(modelPtr),this.maskCounts=new Int32Array(_em.HEAP32.buffer,_csm.getDrawableMaskCounts(modelPtr),length),length=_csm.getDrawableCount(modelPtr),this.vertexCounts=new Int32Array(_em.HEAP32.buffer,_csm.getDrawableVertexCounts(modelPtr),length),length=_csm.getDrawableCount(modelPtr),this.indexCounts=new Int32Array(_em.HEAP32.buffer,_csm.getDrawableIndexCounts(modelPtr),length),length=_csm.getDrawableCount(modelPtr),this.multiplyColors=new Float32Array(_em.HEAPF32.buffer,_csm.getDrawableMultiplyColors(modelPtr),4*length),length=_csm.getDrawableCount(modelPtr),this.screenColors=new Float32Array(_em.HEAPF32.buffer,_csm.getDrawableScreenColors(modelPtr),4*length),length=_csm.getDrawableCount(modelPtr),this.parentPartIndices=new Int32Array(_em.HEAP32.buffer,_csm.getDrawableParentPartIndices(modelPtr),length),length=_csm.getDrawableCount(modelPtr),length2=new Int32Array(_em.HEAP32.buffer,_csm.getDrawableMaskCounts(modelPtr),length),this.masks=new Array(length);for(var _masks=new Uint32Array(_em.HEAPU32.buffer,_csm.getDrawableMasks(modelPtr),length),i=0;i<_masks.length;i++)this.masks[i]=new Int32Array(_em.HEAP32.buffer,_masks[i],length2[i]);length=_csm.getDrawableCount(modelPtr),length2=new Int32Array(_em.HEAP32.buffer,_csm.getDrawableVertexCounts(modelPtr),length),this.vertexPositions=new Array(length);for(var _vertexPositions=new Uint32Array(_em.HEAPU32.buffer,_csm.getDrawableVertexPositions(modelPtr),length),i=0;i<_vertexPositions.length;i++)this.vertexPositions[i]=new Float32Array(_em.HEAPF32.buffer,_vertexPositions[i],2*length2[i]);length=_csm.getDrawableCount(modelPtr),length2=new Int32Array(_em.HEAP32.buffer,_csm.getDrawableVertexCounts(modelPtr),length),this.vertexUvs=new Array(length);for(var _vertexUvs=new Uint32Array(_em.HEAPU32.buffer,_csm.getDrawableVertexUvs(modelPtr),length),i=0;i<_vertexUvs.length;i++)this.vertexUvs[i]=new Float32Array(_em.HEAPF32.buffer,_vertexUvs[i],2*length2[i]);length=_csm.getDrawableCount(modelPtr),length2=new Int32Array(_em.HEAP32.buffer,_csm.getDrawableIndexCounts(modelPtr),length),this.indices=new Array(length);for(var _indices=new Uint32Array(_em.HEAPU32.buffer,_csm.getDrawableIndices(modelPtr),length),i=0;i<_indices.length;i++)this.indices[i]=new Uint16Array(_em.HEAPU16.buffer,_indices[i],length2[i])}return Drawables.prototype.resetDynamicFlags=function(){_csm.resetDrawableDynamicFlags(this._modelPtr)},Drawables}()),Version=(Live2DCubismCore.Drawables=Drawables,function(){function Utils(){}return Utils.hasBlendAdditiveBit=function(bitfield){return 1==(1&bitfield)},Utils.hasBlendMultiplicativeBit=function(bitfield){return 2==(2&bitfield)},Utils.hasIsDoubleSidedBit=function(bitfield){return 4==(4&bitfield)},Utils.hasIsInvertedMaskBit=function(bitfield){return 8==(8&bitfield)},Utils.hasIsVisibleBit=function(bitfield){return 1==(1&bitfield)},Utils.hasVisibilityDidChangeBit=function(bitfield){return 2==(2&bitfield)},Utils.hasOpacityDidChangeBit=function(bitfield){return 4==(4&bitfield)},Utils.hasDrawOrderDidChangeBit=function(bitfield){return 8==(8&bitfield)},Utils.hasRenderOrderDidChangeBit=function(bitfield){return 16==(16&bitfield)},Utils.hasVertexPositionsDidChangeBit=function(bitfield){return 32==(32&bitfield)},Utils.hasBlendColorDidChangeBit=function(bitfield){return 64==(64&bitfield)},Utils}()),Version=(Live2DCubismCore.Utils=Version,function(){function Memory(){}return Memory.initializeAmountOfMemory=function(size){16777216<size&&_csm.initializeAmountOfMemory(size)},Memory}()),_em_module=(Live2DCubismCore.Memory=Version,_scriptDir="undefined"!=typeof document&&document.currentScript?document.currentScript.src:void 0,function(_em_module){_em_module=_em_module||{};var b,n,l={};for(n in b=b||(void 0!==_em_module?_em_module:{}))b.hasOwnProperty(n)&&(l[n]=b[n]);var x,y,v,w,r=!1,p="object"==typeof window,q="function"==typeof importScripts,r="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node&&!p&&!q,t=!p&&!r&&!q,u="",D=(r?(u=__dirname+"/",v=function(a,c){var d=z(a);return d||(x=x||require("fs"),a=(y=y||require("path")).normalize(a),d=x.readFileSync(a)),c?d:d.toString()},w=function(a){return assert((a=(a=v(a,!0)).buffer?a:new Uint8Array(a)).buffer),a},1<process.argv.length&&process.argv[1].replace(/\\/g,"/"),process.argv.slice(2),process.on("uncaughtException",function(a){throw a}),process.on("unhandledRejection",B),b.inspect=function(){return"[Emscripten Module object]"}):t?("undefined"!=typeof read&&(v=function(a){var c=z(a);return c?C(c):read(a)}),w=function(a){var c;if(!(c=z(a))){if("function"==typeof readbuffer)return new Uint8Array(readbuffer(a));assert("object"==typeof(c=read(a,"binary")))}return c},"undefined"!=typeof print&&((console="undefined"==typeof console?{}:console).log=print,console.warn=console.error="undefined"!=typeof printErr?printErr:print)):(p||q)&&(q?u=self.location.href:document.currentScript&&(u=document.currentScript.src),u=0!==(u=_scriptDir||u).indexOf("blob:")?u.substr(0,u.lastIndexOf("/")+1):"",v=function(a){try{var c=new XMLHttpRequest;return c.open("GET",a,!1),c.send(null),c.responseText}catch(d){if(a=z(a))return C(a);throw d}},q)&&(w=function(a){try{var c=new XMLHttpRequest;return c.open("GET",a,!1),c.responseType="arraybuffer",c.send(null),new Uint8Array(c.response)}catch(d){if(a=z(a))return a;throw d}}),b.print||console.log.bind(console)),E=b.printErr||console.warn.bind(console);for(n in l)l.hasOwnProperty(n)&&(b[n]=l[n]);function da(){return{exports:function(asmLibraryArg,wasmMemory,wasmTable){var scratchBuffer=new ArrayBuffer(8),b=new Int32Array(scratchBuffer),c=new Float32Array(scratchBuffer),d=new Float64Array(scratchBuffer);function f(index,value){b[index]=value}function g(){return d[0]}function h(value){d[0]=value}function j(value){c[0]=value}function k(){return c[0]}scratchBuffer=wasmMemory.buffer;var U,global,buffer,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,C,H,I,J,K,L,M,U=new Uint8Array(scratchBuffer);return(scratchBuffer=function(offset,s){var V,W;if("undefined"==typeof Buffer)for(V=atob(s),W=0;W<V.length;W++)U[offset+W]=V.charCodeAt(W);else for(V=Buffer.from(s,"base64"),W=0;W<V.length;W++)U[offset+W]=V[W]})(1024,"QW4gZXJyb3Igb2NjdXJyZWQgaW4gdGhlIGludGVycG9sYXRpb24gZm9yIGJsZW5kIHNoYXBlcy4gQ29tYmluYXRpb25Db3VudCBpcyAlZC4ACgBbQ1NNXSBbRV1XYXJwRGVmb3JtZXI6OlRyYW5zZm9ybVRhcmdldCgpIGVycm9yLiBbJWRdIHAwMT0oJS40ZiAsICUuNGYpCgBbQ1NNXSBbRV1Jbml0aWFsaXplRGVmb3JtZXJzKCk6IFVua25vd24gRGVmb3JtZXIgVHlwZS4KAFtDU01dIFtFXWNzbUhhc01vY0NvbnNpc3RlbmN5OiBUaGlzIG1vYzMgc2l6ZSBpcyBpbnZhbGlkLgoAW0NTTV0gW0VdJXM6ICVzCgBbQ1NNXSBbV11Sb3RhdGlvbkRlZm9ybWVyOiBOb3QgZm91bmQgdHJhbnNmb3JtZWQgRGlyZWN0aW9uLgoAW0NTTV0gW0VdVXBkYXRlRGVmb3JtZXJIaWVyYXJjaHkoKTogVW5rbm93biBEZWZvcm1lciBUeXBlLgoAJXMKACAgAE1PQzMAImFkZHJlc3MiIGlzIG51bGwuACUwMlggAFtDU01dIFtFXWNzbUhhc01vY0NvbnNpc3RlbmN5OiBGaWxlVHlwZSBpcyBpbnZhbGlkLgoAImFkZHJlc3MiIGFsaWdubWVudCBpcyBpbnZhbGlkLgBbQ1NNXSBbRV1jc21IYXNNb2NDb25zaXN0ZW5jeTogVGhpcyBtb2MzIHZlciBpcyBpbnZhbGlkIFt2ZXI6JWRdLgoAInNpemUiIGlzIGludmFsaWQuAFtDU01dIFtFXWNzbUhhc01vY0NvbnNpc3RlbmN5OiBUaGUgQ29yZSB1bnN1cHBvcnQgbGF0ZXIgdGhhbiBtb2MzIHZlcjpbJWRdLiBUaGlzIG1vYzMgdmVyIGlzIFslZF0uCgAibW9kZWwiIGlzIGludmFsaWQuAFtDU01dIFtFXWNzbUhhc01vY0NvbnNpc3RlbmN5OiBIZWFkZXIgc2VjdGlvbiBpcyBpbnZhbGlkLgoAIm91dFNpemVJblBpeGVscyIgaXMgbnVsbC4AW0NTTV0gW0VdY3NtSGFzTW9jQ29uc2lzdGVuY3k6IERhdGEgc2VjdGlvbiBpcyBpbnZhbGlkLgoAIm91dE9yaWdpbkluUGl4ZWxzIiBpcyBudWxsLgBbQ1NNXSBbRV1jc21SZXZpdmVNb2NJblBsYWNlIGlzIGZhaWxlZC4gQ29ycnVwdGVkICBtb2MzIGZpbGUuCgAib3V0UGl4ZWxzUGVyVW5pdCIgaXMgbnVsbC4AW0NTTV0gW0VdY3NtUmV2aXZlTW9jSW5QbGFjZSBpcyBmYWlsZWQuIFRoZSBDb3JlIHVuc3VwcG9ydCBsYXRlciB0aGFuIG1vYzMgdmVyOlslZF0uIFRoaXMgbW9jMyB2ZXIgaXMgWyVkXS4KACJtb2MiIGlzIGludmFsaWQuAFtDU01dIFtFXWNzbUdldE1vY1ZlcnNpb24gaXMgZmFpbGVkLiBDb3JydXB0ZWQgbW9jMyBmaWxlLgoAInNpemUiIGlzIGludmFsaWQATGl2ZTJEIEN1YmlzbSBTREsgQ29yZSBWZXJzaW9uICVkLiVkLiVkAGNzbUdldE1vY1ZlcnNpb24AY3NtSGFzTW9jQ29uc2lzdGVuY3kAY3NtUmV2aXZlTW9jSW5QbGFjZQBjc21SZWFkQ2FudmFzSW5mbwBjc21HZXRTaXplb2ZNb2RlbABjc21Jbml0aWFsaXplTW9kZWxJblBsYWNlAGNzbVVwZGF0ZU1vZGVsAGNzbUdldFBhcmFtZXRlckNvdW50AGNzbUdldFBhcmFtZXRlcklkcwBjc21HZXRQYXJhbWV0ZXJUeXBlcwBjc21HZXRQYXJhbWV0ZXJNaW5pbXVtVmFsdWVzAGNzbUdldFBhcmFtZXRlck1heGltdW1WYWx1ZXMAY3NtR2V0UGFyYW1ldGVyRGVmYXVsdFZhbHVlcwBjc21HZXRQYXJhbWV0ZXJWYWx1ZXMAY3NtR2V0UGFyYW1ldGVyUmVwZWF0cwBjc21HZXRQYXJ0Q291bnQAY3NtR2V0UGFydElkcwBjc21HZXRQYXJ0T3BhY2l0aWVzAGNzbUdldFBhcnRQYXJlbnRQYXJ0SW5kaWNlcwBjc21HZXREcmF3YWJsZUNvdW50AGNzbUdldERyYXdhYmxlSWRzAGNzbUdldERyYXdhYmxlQ29uc3RhbnRGbGFncwBjc21HZXREcmF3YWJsZUR5bmFtaWNGbGFncwBjc21HZXREcmF3YWJsZVRleHR1cmVJbmRpY2VzAGNzbUdldERyYXdhYmxlRHJhd09yZGVycwBjc21HZXREcmF3YWJsZVJlbmRlck9yZGVycwBjc21HZXREcmF3YWJsZU9wYWNpdGllcwBjc21HZXREcmF3YWJsZU1hc2tDb3VudHMAY3NtR2V0RHJhd2FibGVNYXNrcwBjc21HZXREcmF3YWJsZVZlcnRleENvdW50cwBjc21HZXREcmF3YWJsZVZlcnRleFBvc2l0aW9ucwBjc21HZXREcmF3YWJsZVZlcnRleFV2cwBjc21HZXREcmF3YWJsZUluZGV4Q291bnRzAGNzbUdldERyYXdhYmxlSW5kaWNlcwBjc21HZXREcmF3YWJsZU11bHRpcGx5Q29sb3JzAGNzbUdldERyYXdhYmxlU2NyZWVuQ29sb3JzAGNzbUdldERyYXdhYmxlUGFyZW50UGFydEluZGljZXMAY3NtUmVzZXREcmF3YWJsZUR5bmFtaWNGbGFncwBjc21HZXRQYXJhbWV0ZXJLZXlDb3VudHMAY3NtR2V0UGFyYW1ldGVyS2V5VmFsdWVz"),scratchBuffer(3252,"Cg=="),scratchBuffer(3291,"//////8="),scratchBuffer(3360,"LSsgICAwWDB4AChudWxsKQ=="),scratchBuffer(3392,"EQAKABEREQAAAAAFAAAAAAAACQAAAAAL"),scratchBuffer(3424,"EQAPChEREQMKBwABEwkLCwAACQYLAAALAAYRAAAAERER"),scratchBuffer(3473,"Cw=="),scratchBuffer(3482,"EQAKChEREQAKAAACAAkLAAAACQALAAAL"),scratchBuffer(3531,"DA=="),scratchBuffer(3543,"DAAAAAAMAAAAAAkMAAAAAAAMAAAM"),scratchBuffer(3589,"Dg=="),scratchBuffer(3601,"DQAAAAQNAAAAAAkOAAAAAAAOAAAO"),scratchBuffer(3647,"EA=="),scratchBuffer(3659,"DwAAAAAPAAAAAAkQAAAAAAAQAAAQAAASAAAAEhIS"),scratchBuffer(3714,"EgAAABISEgAAAAAAAAk="),scratchBuffer(3763,"Cw=="),scratchBuffer(3775,"CgAAAAAKAAAAAAkLAAAAAAALAAAL"),scratchBuffer(3821,"DA=="),scratchBuffer(3833,"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"),scratchBuffer(6707,"QPsh+T8AAAAALUR0PgAAAICYRvg8AAAAYFHMeDsAAACAgxvwOQAAAEAgJXo4AAAAgCKC4zYAAAAAHfNpNThj7T7aD0k/Xph7P9oPyT9pN6wxaCEiM7QPFDNoIaIz2w9JP9sPSb/kyxZA5MsWwAAAAAAAAACA2w9JQNsPScAAAIA/AADAPwAAAADcz9E1AAAAAADAFT8="),scratchBuffer(6856,"BQ=="),scratchBuffer(6868,"DQ=="),scratchBuffer(6892,"DgAAAA8AAAB4HAAAAAQ="),scratchBuffer(6916,"AQ=="),scratchBuffer(6931,"Cv////8="),scratchBuffer(7188,"oCA="),global={Int8Array:Int8Array,Int16Array:Int16Array,Int32Array:Int32Array,Uint8Array:Uint8Array,Uint16Array:Uint16Array,Uint32Array:Uint32Array,Float32Array:Float32Array,Float64Array:Float64Array,NaN:NaN,Infinity:1/0,Math:Math},buffer=wasmMemory.buffer,m=(scratchBuffer=asmLibraryArg).memory,n=wasmTable,o=new global.Int8Array(buffer),p=new global.Int16Array(buffer),q=new global.Int32Array(buffer),r=new global.Uint8Array(buffer),s=new global.Uint16Array(buffer),t=new global.Uint32Array(buffer),u=new global.Float32Array(buffer),v=new global.Float64Array(buffer),w=global.Math.imul,x=global.Math.fround,y=global.Math.abs,z=global.Math.clz32,A=global.Math.min,global.Math.max,C=global.Math.floor,global.Math.ceil,global.Math.sqrt,scratchBuffer.abort,global.NaN,H=global.Infinity,I=scratchBuffer.a,J=scratchBuffer.b,K=scratchBuffer.c,L=5251776,M=0,n[1]=function(a,Mn,Nn,On,Pn){a|=0,Mn|=0,Nn|=0,On|=0,Pn|=0;var Xn,lo,mo,po,ro,so,to,uo,vo,wo,xo,yo,zo,Ao,Bo,Co,Do,Eo,Fo,Go,Ho,Io,Jo,qo,Qn=0,Rn=x(0),Sn=x(0),Tn=0,Un=x(0),Vn=x(0),Wn=x(0),Yn=x(0),Zn=x(0),_n=x(0),$n=x(0),ao=x(0),bo=x(0),co=x(0),eo=x(0),fo=x(0),go=x(0),ho=x(0),io=x(0),jo=x(0),ko=x(0),no=x(0),oo=x(0),Ko=(x(0),x(0),x(0),x(0),x(0),x(0),x(0),x(0),x(0),0);if(L=qo=L-32|0,1<=(0|Pn))for(Xn=q[16+(q[a+308>>2]+(Mn<<5)|0)>>2],Qn=q[a+60>>2]+w(Xn,24)|0,Mn=(lo=q[Qn+8>>2])+-1|0,yo=(ro=q[Qn+4>>2])+-1|0,zo=vo=(Xn=q[q[a+152>>2]+(Xn<<2)>>2])+(lo<<3)|0,Ao=wo=Xn+((uo=w(ro,mo=lo+1|0))<<3)|0,Bo=xo=Xn+(lo+uo<<3)|0,Jo=q[Qn+12>>2],so=x(0|ro),to=x(0|lo),a=0;Wn=u[4+(Qn=(po=a<<3)+Nn|0)>>2],Sn=x(Wn*so),Yn=u[Qn>>2],Rn=x(Yn*to),Qn=Wn>=x(1),Sn=!(Wn<x(0)^1)||Qn|Yn>=x(1)|Yn<x(0)?(Ko||(bo=u[4+Bo>>2],Co=u[Xn+4>>2],Zn=x(bo-Co),Do=u[4+zo>>2],Eo=u[4+Ao>>2],_n=x(Do-Eo),co=x(x(Zn-_n)*x(.5)),Fo=u[xo>>2],Go=u[Xn>>2],$n=x(Fo-Go),Ho=u[vo>>2],Io=u[wo>>2],ao=x(Ho-Io),eo=x(x($n-ao)*x(.5)),_n=x(x(_n+Zn)*x(.5)),ao=x(x(ao+$n)*x(.5)),Ko=1,Zn=x(x(x(x(x(Co+Do)+Eo)+bo)*x(.25))-x(Zn*x(.5))),$n=x(x(x(x(x(Go+Ho)+Io)+Fo)*x(.25))-x($n*x(.5)))),Wn<x(3)^1|Yn>x(-2)^1|(Yn<x(3)^1|Wn>x(-2)^1)?(u[On+po>>2]=x(Wn*eo)+x(x(Yn*ao)+$n),Rn=x(Wn*co),x(x(Yn*_n)+Zn)):(Yn<=x(0)?Wn<=x(0)?(Vn=x(x(Wn+x(2))*x(.5)),Un=x(x(Yn+x(2))*x(.5)),Rn=x(co+co),no=x(Zn-Rn),Sn=x(eo+eo),oo=x($n-Sn),jo=x(Zn-x(_n+_n)),fo=x(jo-Rn),ko=x($n-x(ao+ao)),go=x(ko-Sn),ho=u[Xn+4>>2],io=u[Xn>>2]):Qn?(Rn=x(co*x(3)),Sn=x(Zn-x(_n+_n)),jo=x(Rn+Sn),fo=x(eo*x(3)),go=x($n-x(ao+ao)),ko=x(fo+go),Vn=x(x(Wn+x(-1))*x(.5)),Un=x(x(Yn+x(2))*x(.5)),ho=x(Rn+Zn),io=x(fo+$n),fo=x(co+Sn),go=x(eo+go),no=u[4+Ao>>2],oo=u[wo>>2]):(Rn=x(Zn-x(_n+_n)),Qn=yo,Tn=x(y(Sn))<x(2147483648)?~~Sn:-2147483648,Vn=x(0|(Qn=(0|Tn)==(0|ro)?Qn:Tn)),Un=x(Vn/so),fo=x(x(Un*co)+Rn),bo=x(Un*eo),Un=x($n-x(ao+ao)),go=x(bo+Un),Wn=x(x(0|(Tn=Qn+1|0))/so),jo=x(x(Wn*co)+Rn),ko=x(x(Wn*eo)+Un),Un=x(x(Yn+x(2))*x(.5)),Vn=x(Sn-Vn),Qn=Xn+(w(Qn,mo)<<3)|0,no=u[Qn+4>>2],oo=u[Qn>>2],Qn=Xn+(w(Tn,mo)<<3)|0,ho=u[Qn+4>>2],io=u[Qn>>2]):Yn>=x(1)?Wn<=x(0)?(Vn=x(x(Wn+x(2))*x(.5)),Un=x(x(Yn+x(-1))*x(.5)),Rn=x(co+co),fo=x(x(_n+Zn)-Rn),Sn=x(eo+eo),go=x(x(ao+$n)-Sn),ho=x(x(_n*x(3))+Zn),no=x(ho-Rn),io=x(x(ao*x(3))+$n),oo=x(io-Sn),jo=u[4+zo>>2],ko=u[vo>>2]):Qn?(Rn=x(co*x(3)),jo=x(Rn+x(_n+Zn)),Sn=x(eo*x(3)),ko=x(Sn+x(ao+$n)),bo=Rn,Rn=x(x(_n*x(3))+Zn),ho=x(bo+Rn),bo=Sn,Sn=x(x(ao*x(3))+$n),io=x(bo+Sn),Vn=x(x(Wn+x(-1))*x(.5)),Un=x(x(Yn+x(-1))*x(.5)),no=x(co+Rn),oo=x(eo+Sn),fo=u[4+Bo>>2],go=u[xo>>2]):(Rn=x(x(_n*x(3))+Zn),Qn=yo,Tn=x(y(Sn))<x(2147483648)?~~Sn:-2147483648,Vn=x(0|(Qn=(0|Tn)==(0|ro)?Qn:Tn)),Un=x(Vn/so),no=x(x(Un*co)+Rn),bo=x(Un*eo),Un=x(x(ao*x(3))+$n),oo=x(bo+Un),Wn=x(x(0|(Tn=Qn+1|0))/so),ho=x(x(Wn*co)+Rn),io=x(x(Wn*eo)+Un),Un=x(x(Yn+x(-1))*x(.5)),Vn=x(Sn-Vn),Qn=Xn+(w(Qn,mo)+lo<<3)|0,fo=u[Qn+4>>2],go=u[Qn>>2],Qn=Xn+(w(Tn,mo)+lo<<3)|0,jo=u[Qn+4>>2],ko=u[Qn>>2]):Wn<=x(0)?(Vn=x(x(Wn+x(2))*x(.5)),Qn=Mn,Tn=x(y(Sn=Rn))<x(2147483648)?~~Rn:-2147483648,Rn=x(0|(Qn=(0|Tn)==(0|lo)?Qn:Tn)),Un=x(Sn-Rn),Rn=x(Rn/to),Sn=x(co+co),fo=x(x(x(Rn*_n)+Zn)-Sn),bo=x(x(Rn*ao)+$n),Rn=x(eo+eo),go=x(bo-Rn),Wn=x(x(0|(Tn=Qn+1|0))/to),no=x(x(x(Wn*_n)+Zn)-Sn),oo=x(x(x(Wn*ao)+$n)-Rn),jo=u[4+(Qn=Xn+(Qn<<3)|0)>>2],ko=u[Qn>>2],ho=u[4+(Qn=Xn+(Tn<<3)|0)>>2],io=u[Qn>>2]):Qn?(bo=Sn=x(co*x(3)),Qn=Mn,Tn=x(y(Rn))<x(2147483648)?~~Rn:-2147483648,Un=x(0|(Qn=(0|Tn)==(0|lo)?Qn:Tn)),Vn=x(Un/to),jo=x(bo+x(x(Vn*_n)+Zn)),Yn=x(eo*x(3)),ko=x(Yn+x(x(Vn*ao)+$n)),bo=Sn,Sn=x(x(0|(Tn=Qn+1|0))/to),ho=x(bo+x(x(Sn*_n)+Zn)),io=x(Yn+x(x(Sn*ao)+$n)),Vn=x(x(Wn+x(-1))*x(.5)),Un=x(Rn-Un),fo=u[4+(Qn=Xn+(Qn+uo<<3)|0)>>2],go=u[Qn>>2],no=u[4+(Qn=Xn+(Tn+uo<<3)|0)>>2],oo=u[Qn>>2]):(v[16+qo>>3]=Wn,q[qo>>2]=a,v[8+qo>>3]=Yn,Y(4,1107,qo)),x(Un+Vn)<=x(1)?(u[On+po>>2]=x(go+x(x(oo-go)*Un))+x(x(ko-go)*Vn),Rn=x(fo+x(x(no-fo)*Un)),x(x(jo-fo)*Vn)):(Rn=x(x(1)-Un),Sn=x(x(1)-Vn),u[On+po>>2]=x(io+x(x(ko-io)*Rn))+x(x(oo-io)*Sn),Rn=x(ho+x(x(jo-ho)*Rn)),x(x(no-ho)*Sn)))):(Qn=x(y(bo=Sn))<x(2147483648)?~~Sn:-2147483648,Vn=x(bo-x(0|Qn)),Tn=x(y(Sn=Rn))<x(2147483648)?~~Rn:-2147483648,Un=x(Sn-x(0|Tn)),Qn=Tn+w(Qn,mo)|0,Jo?(Rn=x(x(1)-Vn),Sn=x(x(1)-Un),Tn=Xn+(Qn<<3)|0,Qn=Xn+(Qn+mo<<3)|0,u[On+po>>2]=x(x(x(Rn*x(Sn*u[Tn>>2]))+x(Rn*x(Un*u[Tn+8>>2])))+x(Vn*x(Sn*u[Qn>>2])))+x(Vn*x(Un*u[Qn+8>>2])),Rn=x(x(x(Rn*x(Sn*u[Tn+4>>2]))+x(Rn*x(Un*u[Tn+12>>2])))+x(Vn*x(Sn*u[Qn+4>>2]))),x(Vn*x(Un*u[Qn+12>>2]))):x(Un+Vn)<=x(1)?(Rn=x(x(x(1)-Un)-Vn),Tn=Xn+(Qn<<3)|0,Qn=Xn+(Qn+mo<<3)|0,u[On+po>>2]=x(x(Rn*u[Tn>>2])+x(Un*u[Tn+8>>2]))+x(Vn*u[Qn>>2]),Rn=x(x(Rn*u[Tn+4>>2])+x(Un*u[Tn+12>>2])),x(Vn*u[Qn+4>>2])):(Rn=x(x(Un+x(-1))+Vn),Tn=Xn+(Qn+mo<<3)|0,Sn=x(x(1)-Un),Wn=x(x(1)-Vn),Qn=Xn+(Qn<<3)|0,u[On+po>>2]=x(x(Rn*u[Tn+8>>2])+x(Sn*u[Tn>>2]))+x(Wn*u[Qn+8>>2]),Rn=x(x(Rn*u[Tn+12>>2])+x(Sn*u[Tn+4>>2])),x(Wn*u[Qn+12>>2]))),u[4+(On+po|0)>>2]=Rn+Sn,(0|Pn)!=(0|(a=a+1|0)););L=32+qo|0},n[2]=function(a,jh){a|=0,jh|=0;var Ah=0,Bh=0,Ch=0,Dh=0,Eh=0,Fh=x(0),Gh=0,Hh=0,Jh=(x(0),0),Kh=0,Dh=q[a+320>>2],Ah=q[a+316>>2],Eh=q[a+308>>2];-1==(0|(Bh=q[8+(Ch=Eh+(jh<<5)|0)>>2]))?(q[(Kh=Ah)+(Ah=jh<<2)>>2]=q[q[a+148>>2]+(q[Ch+16>>2]<<2)>>2],q[Ah+Dh>>2]=1065353216):(Gh=q[Ch+16>>2],Hh=q[q[a+152>>2]+(Gh<<2)>>2],n[q[24+(Eh+(Bh<<5)|0)>>2]](a,Bh,Hh,Hh,q[16+(q[a+60>>2]+w(Gh,24)|0)>>2]),Fh=u[q[a+148>>2]+(q[Ch+16>>2]<<2)>>2],Ch=q[Ch+8>>2]<<2,u[(Bh=jh<<2)+Ah>>2]=Fh*u[Ch+Ah>>2],q[Bh+Dh>>2]=q[Ch+Dh>>2]),4<=r[q[a>>2]+4|0]&&(Dh=jh<<2,Ah=q[a+308>>2]+(jh<<5)|0,Bh=q[Ah+16>>2]<<2,Ch=q[a+328>>2],jh=q[a+324>>2],-1==(0|(Eh=q[Ah+8>>2]))?(Eh=q[a+156>>2],q[(Ah=Dh<<2)+jh>>2]=q[Eh+(Bh<<=2)>>2],q[(Gh=4|Ah)+jh>>2]=q[(Hh=4|Bh)+Eh>>2],q[(Jh=8|Ah)+jh>>2]=q[Eh+(Kh=8|Bh)>>2],q[jh+((Dh|=3)<<2)>>2]=1065353216,a=q[a+160>>2],q[Ah+Ch>>2]=q[a+Bh>>2],q[Ch+Gh>>2]=q[a+Hh>>2],q[Ch+Jh>>2]=q[a+Kh>>2]):(Bh=(Hh=Bh<<2)+q[a+156>>2]|0,u[(Ah=(Gh=Dh<<2)+jh|0)>>2]=u[Bh>>2]*u[(Eh=(Jh=Eh<<4)+jh|0)>>2],u[Ah+4>>2]=u[Bh+4>>2]*u[Eh+4>>2],u[Ah+8>>2]=u[Bh+8>>2]*u[Eh+8>>2],q[jh+((Dh|=3)<<2)>>2]=1065353216,a=Hh+q[a+160>>2]|0,Kh=u[a>>2],Fh=u[(Ah=Ch+Jh|0)>>2],u[(jh=Ch+Gh|0)>>2]=x(Kh+Fh)-x(Kh*Fh),Kh=u[a+4>>2],Fh=u[Ah+4>>2],u[jh+4>>2]=x(Kh+Fh)-x(Kh*Fh),Kh=u[a+8>>2],Fh=u[Ah+8>>2],u[jh+8>>2]=x(Kh+Fh)-x(Kh*Fh)),q[Ch+(Dh<<2)>>2]=1065353216)},n[3]=function(a,rn,sn,vn,wn){a|=0,rn|=0,sn|=0,vn|=0,wn|=0;var Bn,Cn,Dn,Fn,Gn,xn=0,xn=(x(0),x(0),x(0),x(0),x(0),x(0),x(0),x(0),(rn=q[16+(q[a+308>>2]+(rn<<5)|0)>>2])<<2),zn=function(a){var Cl,Fl,Dl,El,Bl=x(0);L=Dl=L-16|0,j(a);a:if((Cl=2147483647&(El=b[0]))>>>0<=1061752794)Bl=x(1),Cl>>>0<964689920||(Bl=ba(+a));else if(Cl>>>0<=1081824209)Fl=+a,Bl=1075235812<=Cl>>>0?x(-ba(((0|El)<0?3.141592653589793:-3.141592653589793)+Fl)):aa((0|El)<=-1?1.5707963267948966+Fl:1.5707963267948966-Fl);else if(Cl>>>0<=1088565717)Bl=1085271520<=Cl>>>0?ba(+a+((0|El)<0?6.283185307179586:-6.283185307179586)):aa((0|El)<=-1?-4.71238898038469-+a:+a-4.71238898038469);else if(Bl=x(a-a),!(2139095040<=Cl>>>0))if((Cl=3&Da(a,8+Dl|0))>>>0<=2){switch(Cl-1|0){default:Bl=ba(v[8+Dl>>3]);break a;case 0:Bl=aa(-v[8+Dl>>3]);break a;case 1:}Bl=x(-ba(v[8+Dl>>3]))}else Bl=aa(v[8+Dl>>3]);return L=16+Dl|0,Bl}(yn=x(x(x(u[4+(q[a+168>>2]+w(rn,12)|0)>>2]+u[xn+q[a+284>>2]>>2])*x(3.1415927410125732))/x(180))),An=u[xn+q[a+272>>2]>>2],En=q[xn+q[a+292>>2]>>2],yn=function(a){var Tk,yl,Al,zl=0;L=yl=L-16|0,j(a);a:if((Tk=2147483647&(Al=b[0]))>>>0<=1061752794)Tk>>>0<964689920||(a=aa(+a));else if(Tk>>>0<=1081824209)zl=+a,a=Tk>>>0<=1075235811?(0|Al)<=-1?x(-ba(zl+1.5707963267948966)):ba(zl+-1.5707963267948966):aa(-(((0|Al)<0?3.141592653589793:-3.141592653589793)+zl));else if(Tk>>>0<=1088565717)zl=+a,a=Tk>>>0<=1085271519?(0|Al)<=-1?ba(zl+4.71238898038469):x(-ba(zl+-4.71238898038469)):aa(((0|Al)<0?6.283185307179586:-6.283185307179586)+zl);else if(2139095040<=Tk>>>0)a=x(a-a);else if((Tk=3&Da(a,8+yl|0))>>>0<=2){switch(Tk-1|0){default:a=aa(v[8+yl>>3]);break a;case 0:a=ba(v[8+yl>>3]);break a;case 1:}a=aa(-v[8+yl>>3])}else a=x(-ba(v[8+yl>>3]));return L=16+yl|0,a}(yn);if((rn=0)<(0|wn))for(zn=x(An*zn),Cn=x(En?-1:1),Fn=x(zn*Cn),Bn=q[xn+q[a+288>>2]>>2]?x(-1):x(1),Gn=x(x(An*yn)*Bn),zn=x(zn*Bn),An=x(x(An*x(-yn))*Cn),yn=u[xn+q[a+280>>2]>>2],Cn=u[xn+q[a+276>>2]>>2];xn=(a=rn<<3)+vn|0,Bn=u[(a=a+sn|0)>>2],Dn=u[a+4>>2],u[xn+4>>2]=yn+x(x(Gn*Bn)+x(Fn*Dn)),u[xn>>2]=Cn+x(x(zn*Bn)+x(An*Dn)),(0|wn)!=(0|(rn=rn+1|0)););},n[4]=function(a,jh){a|=0,jh|=0;var vh,wh,xh,yh,zh,kh,lh=0,mh=0,nh=0,oh=x(0),ph=0,qh=0,rh=x(0),sh=0,th=0,uh=0;if(x(0),x(0),x(0),x(0),L=kh=L+-64|0,sh=q[a+320>>2],th=q[a+316>>2],mh=q[a+308>>2],-1==(0|(ph=q[8+(nh=mh+(jh<<5)|0)>>2])))lh=q[nh+16>>2]<<2,q[(mh=jh<<2)+th>>2]=q[lh+q[a+268>>2]>>2],q[mh+sh>>2]=q[lh+q[a+272>>2]>>2];else{lh=q[nh+16>>2]<<2,uh=q[lh+q[a+276>>2]>>2],q[24+kh>>2]=uh,lh=q[lh+q[a+280>>2]>>2],q[28+kh>>2]=lh,q[16+kh>>2]=0,wh=1==q[12+(qh=mh+(ph<<5)|0)>>2]?x(-10):x(-.10000000149011612),u[20+kh>>2]=wh,q[60+kh>>2]=lh,q[56+kh>>2]=uh,n[q[qh+24>>2]](a,ph,56+kh|0,48+kh|0,1),oh=x(1),mh=9;b:{for(;;){if(lh=mh,rh=x(oh*x(0)),u[32+kh>>2]=rh+u[56+kh>>2],vh=x(wh*oh),u[36+kh>>2]=vh+u[60+kh>>2],n[q[qh+24>>2]](a,ph,32+kh|0,40+kh|0,1),xh=x(u[44+kh>>2]-u[52+kh>>2]),u[44+kh>>2]=xh,yh=x(u[40+kh>>2]-u[48+kh>>2]),u[40+kh>>2]=yh,xh!=x(0)||yh!=x(0)){mh=q[44+kh>>2],q[8+kh>>2]=q[40+kh>>2],q[12+kh>>2]=mh;break b}if(u[32+kh>>2]=u[56+kh>>2]-rh,u[36+kh>>2]=u[60+kh>>2]-vh,n[q[qh+24>>2]](a,ph,32+kh|0,40+kh|0,1),rh=x(u[40+kh>>2]-u[48+kh>>2]),u[40+kh>>2]=rh,vh=x(u[44+kh>>2]-u[52+kh>>2]),(u[44+kh>>2]=vh)!=x(0)||rh!=x(0)){u[12+kh>>2]=-vh,u[8+kh>>2]=-rh;break b}if(mh=lh+-1|0,oh=x(oh*x(.10000000149011612)),!lh)break}Y(3,1311,0)}oh=function(a,Sh){var Th=x(0);if((Th=x(Ba(u[4+a>>2],u[a>>2])-Ba(u[4+Sh>>2],u[Sh>>2])))<x(-3.1415927410125732))for(;(Th=x(Th+x(6.2831854820251465)))<x(-3.1415927410125732););if(Th>x(3.1415927410125732))for(;(Th=x(Th+x(-6.2831854820251465)))>x(3.1415927410125732););return Th}(16+kh|0,8+kh|0),n[q[qh+24>>2]](a,q[nh+8>>2],24+kh|0,24+kh|0,1),mh=q[nh+16>>2]<<2,q[mh+q[a+276>>2]>>2]=q[24+kh>>2],q[mh+q[a+280>>2]>>2]=q[28+kh>>2],lh=mh+q[a+284>>2]|0,u[lh>>2]=u[lh>>2]+x(x(oh*x(-180))/x(3.1415927410125732)),nh=q[nh+8>>2]<<2,u[(lh=jh<<2)+th>>2]=u[mh+q[a+268>>2]>>2]*u[nh+th>>2],mh=mh+q[a+272>>2]|0,oh=x(u[mh>>2]*u[nh+sh>>2]),u[lh+sh>>2]=oh,u[mh>>2]=oh}4<=r[q[a>>2]+4|0]&&(lh=jh<<2,nh=q[a+308>>2]+(jh<<5)|0,ph=q[nh+16>>2]<<2,mh=q[a+328>>2],jh=q[a+324>>2],-1==(0|(qh=q[nh+8>>2]))?(qh=q[a+296>>2],q[(nh=lh<<2)+jh>>2]=q[qh+(ph<<=2)>>2],q[(sh=4|nh)+jh>>2]=q[(th=4|ph)+qh>>2],q[(uh=8|nh)+jh>>2]=q[qh+(zh=8|ph)>>2],q[jh+((lh|=3)<<2)>>2]=1065353216,a=q[a+300>>2],q[mh+nh>>2]=q[a+ph>>2],q[mh+sh>>2]=q[a+th>>2],q[mh+uh>>2]=q[a+zh>>2]):(ph=(th=ph<<2)+q[a+296>>2]|0,u[(nh=(sh=lh<<2)+jh|0)>>2]=u[ph>>2]*u[(qh=(uh=qh<<4)+jh|0)>>2],u[nh+4>>2]=u[ph+4>>2]*u[qh+4>>2],u[nh+8>>2]=u[ph+8>>2]*u[qh+8>>2],q[jh+((lh|=3)<<2)>>2]=1065353216,a=th+q[a+300>>2]|0,oh=u[a>>2],rh=u[(nh=mh+uh|0)>>2],u[(jh=mh+sh|0)>>2]=x(oh+rh)-x(oh*rh),oh=u[a+4>>2],rh=u[nh+4>>2],u[jh+4>>2]=x(oh+rh)-x(oh*rh),oh=u[a+8>>2],rh=u[nh+8>>2],u[jh+8>>2]=x(oh+rh)-x(oh*rh)),q[mh+(lh<<2)>>2]=1065353216),L=64+kh|0},n[5]=function(a,Tk){return a|=0,Tk|=0,x(0),x(0),0|((a=u[a>>2])<(Tk=u[Tk>>2])?-1:Tk<a)},n[6]=function(a,tj,vj,wj){a|=0,tj|=0,vj|=0,wj|=0;var Tj=0,Uj=0,Vj=x(0),Wj=0,Xj=0,Yj=0,Zj=0,_j=0;if(1<=(0|(Wj=q[a+8>>2])))for(Yj=q[a+12>>2],Xj=q[a+20>>2];u[(Uj=Tj<<2)+Yj>>2]=u[tj+Uj>>2]*u[Uj+Xj>>2],(0|(Tj=Tj+1|0))<(0|Wj););if(!((0|(Wj=q[a>>2]))<1))if(Yj=q[a+4>>2],wj)for(Uj=tj=0;;){if(q[wj>>2]){if((0|(Tj=q[(Xj=tj<<2)+q[a+16>>2]>>2]))<1)Vj=x(0);else for(Zj=Tj+Uj|0,_j=q[a+12>>2],Vj=x(0),Tj=Uj;Vj=x(Vj+u[_j+(Tj<<2)>>2]),(0|(Tj=Tj+1|0))<(0|Zj););u[vj+Xj>>2]=Vj}if(wj=wj+4|0,Uj=q[Yj+(tj<<2)>>2]+Uj|0,!((0|(tj=tj+1|0))<(0|Wj)))break}else for(Xj=q[a+16>>2],tj=wj=0;;){if((0|(Tj=q[(Uj=wj<<2)+Xj>>2]))<=0)Vj=x(0);else for(Zj=tj+Tj|0,_j=q[a+12>>2],Vj=x(0),Tj=tj;Vj=x(Vj+u[_j+(Tj<<2)>>2]),(0|(Tj=Tj+1|0))<(0|Zj););if(u[vj+Uj>>2]=Vj,tj=q[Uj+Yj>>2]+tj|0,!((0|(wj=wj+1|0))<(0|Wj)))break}},n[7]=function(a,tj,vj,wj){a|=0,tj|=0,vj|=0,wj|=0;var xj=0,yj=x(0),Oj=0,Pj=0,Qj=0,Rj=0,Sj=0;if(1<=(0|(Rj=q[a+8>>2])))for(Pj=q[a+12>>2],Qj=q[a+20>>2];u[(Oj=xj<<2)+Pj>>2]=u[tj+Oj>>2]*u[Oj+Qj>>2],(0|(xj=xj+1|0))<(0|Rj););if(!((0|(xj=q[a>>2]))<1))if(Rj=q[a+4>>2],wj)for(Oj=tj=0;;){if(q[wj>>2]){if((0|(xj=q[(Pj=tj<<2)+q[a+16>>2]>>2]))<1)yj=x(0);else for(Qj=xj+Oj|0,Sj=q[a+12>>2],yj=x(0),xj=Oj;yj=x(yj+u[Sj+(xj<<2)>>2]),(0|(xj=xj+1|0))<(0|Qj););xj=vj+Pj|0,yj=x(yj+x(.0010000000474974513)),Pj=x(y(yj))<x(2147483648)?~~yj:-2147483648,q[xj>>2]=Pj,xj=q[a>>2]}if(wj=wj+4|0,Oj=q[Rj+(tj<<2)>>2]+Oj|0,!((0|(tj=tj+1|0))<(0|xj)))break}else for(Pj=q[a+16>>2],tj=wj=0;;){if((0|(xj=q[(Oj=wj<<2)+Pj>>2]))<=0)yj=x(0);else for(Qj=tj+xj|0,Sj=q[a+12>>2],yj=x(0),xj=tj;yj=x(yj+u[Sj+(xj<<2)>>2]),(0|(xj=xj+1|0))<(0|Qj););if(xj=vj+Oj|0,yj=x(yj+x(.0010000000474974513)),Qj=x(y(yj))<x(2147483648)?~~yj:-2147483648,q[xj>>2]=Qj,tj=q[Oj+Rj>>2]+tj|0,!((0|(wj=wj+1|0))<q[a>>2]))break}},n[8]=function(a,tj,vj,wj,xj,yj){a|=0,tj|=0,vj|=0,wj|=0,xj|=0,yj|=0;var Mj,Nj,zj=0,Aj=0,Bj=0,Cj=0,Dj=0,Ej=0,Fj=0,Gj=0,Ij=0,Jj=0,Kj=x(0),Lj=0,Hj=q[a>>2];if(!((0|Hj)<1))if(Mj=xj<<2,Nj=q[a+4>>2],yj)for(;;){if(q[yj>>2]&&(Bj=q[(zj=Cj<<2)+q[a+16>>2]>>2],Fj=q[vj+zj>>2],Aj=q[wj+zj>>2],(zj=(0|(Gj=w(Aj,xj)))<1)||ca(Fj,0,w(Aj,Mj)),!(zj|(0|Bj)<1)))for(Ij=Bj+Ej|0,Jj=q[a+20>>2],zj=Ej;;){for(Kj=u[(Aj=zj<<2)+Jj>>2],Lj=q[tj+Aj>>2],Dj=0;u[(Aj=(Bj=Dj<<2)+Fj|0)>>2]=u[Aj>>2]+x(Kj*u[Bj+Lj>>2]),(0|Gj)!=(0|(Dj=Dj+1|0)););if(!((0|(zj=zj+1|0))<(0|Ij)))break}if(yj=yj+4|0,Ej=q[(Cj<<2)+Nj>>2]+Ej|0,!((0|(Cj=Cj+1|0))<(0|Hj)))break}else for(yj=0;;){if(Bj=q[(Cj=yj<<2)+q[a+16>>2]>>2],Fj=q[vj+Cj>>2],Aj=q[wj+Cj>>2],(zj=(0|(Gj=w(Aj,xj)))<1)||ca(Fj,0,w(Aj,Mj)),!(zj|(0|Bj)<=0))for(Ij=Bj+Ej|0,Jj=q[a+20>>2],zj=Ej;;){for(Kj=u[(Aj=zj<<2)+Jj>>2],Lj=q[tj+Aj>>2],Dj=0;u[(Aj=(Bj=Dj<<2)+Fj|0)>>2]=u[Aj>>2]+x(Kj*u[Bj+Lj>>2]),(0|Gj)!=(0|(Dj=Dj+1|0)););if(!((0|(zj=zj+1|0))<(0|Ij)))break}if(Ej=q[Cj+Nj>>2]+Ej|0,!((0|(yj=yj+1|0))<(0|Hj)))break}},n[9]=function(a){var Je,Ke,Le,Fe=0,Ge=0,He=0,Ie=0;if(!(q[(a|=0)+648>>2]||(0|(Fe=q[a+332>>2]))<1))for(Ke=(Ge=q[a+336>>2])+w(Fe,20)|0,Fe=q[a+424>>2],Ie=q[a+444>>2];;){if(q[Fe>>2]&&!((0|(He=q[Ge+16>>2]))<(a=1)))for(He<<=1,Le=q[Ie>>2];u[(Je=(a<<2)+Le|0)>>2]=-u[Je>>2],(0|(a=a+2|0))<(0|He););if(Ie=Ie+4|0,Fe=Fe+4|0,!((Ge=Ge+20|0)>>>0<Ke>>>0))break}},n[10]=function(a,rn,sn){var un;return $(un=q[20+(a|=0)>>2],rn|=0,rn=(sn|=0)>>>0<(rn=q[a+16>>2]-un|0)>>>0?sn:rn),q[a+20>>2]=rn+q[a+20>>2],0|sn},n[11]=function(a,Gl,Pm,Qm,Rm,Sm){a|=0,Gl=+Gl,Pm|=0,Qm|=0,Rm|=0,Sm|=0;var dn,on,Xm,hn,Tm=0,Um=0,Vm=0,Wm=0,Ym=0,Zm=0,_m=0,$m=0,an=0,bn=0,cn=0,en=0,fn=0,gn=0,kn=0;if(q[44+(L=Xm=L-560|0)>>2]=0,h(+Gl),Tm=0|b[1],on=4294967295<b[0]>>>0?0:1,hn=(0|Tm)<-1||(0|Tm)<=-1&&on?(h(Gl=-Gl),Tm=0|b[1],b[0],gn=1,3872):2048&Rm?(gn=1,3875):(gn=1&Rm)?3878:3873,2146435072==(2146435072&Tm))_(a,32,Pm,Zm=gn+3|0,-65537&Rm),Z(a,hn,gn),Qm=Sm>>>5&1,Z(a,Gl!=Gl?Qm?3899:3903:Qm?3891:3895,3);else if(Gl=function Ja(a,ic){var kc,lc,jc=0;if(h(+a),jc=0|b[1],kc=0|b[0],2047!=(0|(jc=(lc=jc)>>>20&2047))){if(!jc)return jc=ic,ic=0==a?0:(a=Ja(0x10000000000000000*a,ic),q[ic>>2]+-64|0),q[jc>>2]=ic,a;q[ic>>2]=jc+-1022,f(0,0|kc),f(1,-2146435073&lc|1071644672),a=+g()}return a}(Gl,44+Xm|0),0!=(Gl+=Gl)&&(q[44+Xm>>2]=q[44+Xm>>2]+-1),dn=16+Xm|0,97==(0|(on=32|Sm))){if(cn=(bn=32&Sm)?9+hn|0:hn,!(11<Qm>>>0)&&(Tm=12-Qm|0)){for(en=8;en*=16,Tm=Tm+-1|0;);Gl=45==r[0|cn]?-(en+(-Gl-en)):Gl+en-en}for((0|dn)==(0|(Tm=ga((Vm=(Tm=q[44+Xm>>2])>>31)^Tm+Vm,0,dn)))&&(o[15+Xm|0]=48,Tm=15+Xm|0),Ym=2|gn,Vm=q[44+Xm>>2],o[0|(an=Tm+-2|0)]=Sm+15,o[Tm+-1|0]=(0|Vm)<0?45:43,Tm=8&Rm,Um=16+Xm|0;Sm=Um,$m=bn,Vm=y(Gl)<2147483648?~~Gl:-2147483648,o[0|Um]=$m|r[Vm+3856|0],1!=((Um=Sm+1|0)-(16+Xm|0)|0)|(0==(Gl=16*(Gl-(0|Vm)))?!(Tm|0<(0|Qm)):0)||(o[Sm+1|0]=46,Um=Sm+2|0),0!=Gl;);_(a,32,Pm,Zm=(Sm=!Qm|(0|Qm)<=((Um-Xm|0)-18|0)?((dn-(16+Xm|0)|0)-an|0)+Um|0:2+((Qm+dn|0)-an|0)|0)+Ym|0,Rm),Z(a,cn,Ym),_(a,48,Pm,Zm,65536^Rm),Z(a,16+Xm|0,Qm=Um-(16+Xm|0)|0),_(a,48,Sm-((Tm=Qm)+(Qm=dn-an|0)|0)|0,0,0),Z(a,an,Qm)}else{for(Tm=(0|Qm)<0,0==Gl?Wm=q[44+Xm>>2]:(Wm=q[44+Xm>>2]+-28|0,q[44+Xm>>2]=Wm,Gl*=268435456),_m=Tm?6:Qm,Vm=bn=(0|Wm)<0?48+Xm|0:336+Xm|0;Vm=(Qm=Vm)+4|0,0!=(Gl=1e9*(Gl-((q[Qm>>2]=Tm=Gl<4294967296&0<=Gl?~~Gl>>>0:0)>>>0))););if((0|Wm)<1)Tm=Vm,Um=bn;else for(Um=bn;;){if(an=(0|Wm)<29?Wm:29,!((Tm=Vm+-4|0)>>>0<Um>>>0)){for(Qm=an,$m=0;kn=$m,$m=q[(cn=Tm)>>2],Ym=31&Qm,Ym=32<=(63&Qm)>>>(Zm=0)?(Wm=$m<<Ym,0):(Wm=(1<<Ym)-1&$m>>>32-Ym,$m<<Ym),Zm=Wm+Zm|0,Zm=($m=kn+Ym|0)>>>0<Ym>>>0?Zm+1|0:Zm,kn=cn,cn=cd($m=dd(Ym=$m,Zm,1e9),M,1e9),q[kn>>2]=Ym-cn,Um>>>0<=(Tm=Tm+-4|0)>>>0;);(Qm=$m)&&(q[(Um=Um+-4|0)>>2]=Qm)}for(;Um>>>0<(Tm=Vm)>>>0&&!q[(Vm=Tm+-4|0)>>2];);if(Wm=q[44+Xm>>2]-an|0,Vm=Tm,!(0<(0|(q[44+Xm>>2]=Wm))))break}if((0|Wm)<=-1)for(fn=1+((_m+25|0)/9|0)|0,an=102==(0|on);;){if($m=(0|Wm)<-9?9:0-Wm|0,Tm>>>0<=Um>>>0)Um=q[Um>>2]?Um:Um+4|0;else{for(cn=1e9>>>$m,Ym=-1<<$m^-1,Wm=0,Vm=Um;Qm=q[Vm>>2],q[Vm>>2]=(Qm>>>$m)+Wm,Wm=w(cn,Qm&Ym),(Vm=Vm+4|0)>>>0<Tm>>>0;);Um=q[Um>>2]?Um:Um+4|0,Wm&&(q[Tm>>2]=Wm,Tm=Tm+4|0)}if(Wm=$m+q[44+Xm>>2]|0,Tm=(0|fn)<Tm-(Qm=an?bn:Um)>>2?Qm+(fn<<2)|0:Tm,!((0|(q[44+Xm>>2]=Wm))<0))break}if(!(Tm>>>(Vm=0)<=Um>>>0||(Vm=w(bn-Um>>2,9),(Qm=q[Um>>2])>>>0<(Wm=10))))for(;Vm=Vm+1|0,(Wm=w(Wm,10))>>>0<=Qm>>>0;);if((0|(Qm=(_m-(102==(0|on)?0:Vm)|0)-(103==(0|on)&0!=(0|_m))|0))<(w(Tm-bn>>2,9)+-9|0)){if(Zm=(bn+((Qm=(0|(Ym=Qm+9216|0))/9|0)<<2)|0)-4092|0,Wm=10,(0|(Qm=1+(Ym-w(Qm,9)|0)|0))<=8)for(;Wm=w(Wm,10),9!=(0|(Qm=Qm+1|0)););if(fn=Zm+4|0,((an=(cn=q[Zm>>2])-w(Wm,Ym=(cn>>>0)/(Wm>>>0)|0)|0)||(0|fn)!=(0|Tm))&&(en=an>>>0<(Qm=Wm>>>1)>>>0?.5:(0|Tm)==(0|fn)&&(0|Qm)==(0|an)?1:1.5,Gl=1&Ym?9007199254740994:9007199254740992,!gn|45!=r[0|hn]||(en=-en,Gl=-Gl),q[Zm>>2]=Qm=cn-an|0,Gl+en!=Gl)){if(1e9<=(q[Zm>>2]=Qm=Qm+Wm|0)>>>0)for(;(Zm=Zm+-4|(q[Zm>>2]=0))>>>0<Um>>>0&&(q[(Um=Um+-4|0)>>2]=0),Qm=q[Zm>>2]+1|0,999999999<(q[Zm>>2]=Qm)>>>0;);if(Vm=w(bn-Um>>2,9),!((Qm=q[Um>>2])>>>0<(Wm=10)))for(;Vm=Vm+1|0,(Wm=w(Wm,10))>>>0<=Qm>>>0;);}Tm=(Qm=Zm+4|0)>>>0<Tm>>>0?Qm:Tm}j:{for(;;){if((an=Tm)>>>(cn=0)<=Um>>>0)break j;if(q[(Tm=an+-4|0)>>2])break}cn=1}if(103!=(0|on))Ym=8&Rm;else if(_m=((Qm=(0|Vm)<(0|(Tm=_m||1))&-5<(0|Vm))?-1^Vm:-1)+Tm|0,Sm=(Qm?-1:-2)+Sm|0,!(Ym=8&Rm)){if(Tm=9,cn&&(Ym=q[an+-4>>2])&&!((Ym>>>(Tm=0))%(Qm=10)))for(;Tm=Tm+1|0,!((Ym>>>0)%((Qm=w(Qm,10))>>>0)););Qm=w(an-bn>>2,9)+-9|0,_m=102==(32|Sm)?((Ym=0)|_m)<(0|(Qm=0<(0|(Qm=Qm-Tm|0))?Qm:0))?_m:Qm:((Ym=0)|_m)<(0|(Qm=0<(0|(Qm=(Qm+Vm|0)-Tm|0))?Qm:0))?_m:Qm}if(Zm=0!=(0|(Wm=_m|Ym)),Qm=a,kn=Pm,Tm=0<(0|Vm)?Vm:0,102!=(0|($m=32|Sm))){if((dn-(Tm=ga((Tm=Vm>>31)+Vm^Tm,0,dn))|0)<=1)for(;o[0|(Tm=Tm+-1|0)]=48,(dn-Tm|0)<2;);o[0|(fn=Tm+-2|0)]=Sm,o[Tm+-1|0]=(0|Vm)<0?45:43,Tm=dn-fn|0}if(_(Qm,32,kn,Zm=1+(Tm+(Zm+(_m+gn|0)|0)|0)|0,Rm),Z(a,hn,gn),_(a,48,Pm,Zm,65536^Rm),102==(0|$m)){for(Qm=16+Xm|8,Vm=16+Xm|9,Um=Sm=bn>>>0<Um>>>0?bn:Um;;){if(Tm=ga(q[Um>>2],0,Vm),(0|Sm)!=(0|Um)){if(!(Tm>>>0<=16+Xm>>>0))for(;o[0|(Tm=Tm+-1|0)]=48,16+Xm>>>0<Tm>>>0;);}else(0|Tm)==(0|Vm)&&(o[24+Xm|0]=48,Tm=Qm);if(Z(a,Tm,Vm-Tm|0),!((Um=Um+4|0)>>>0<=bn>>>0))break}Wm&&Z(a,3907,1);p:if(!((0|_m)<1|an>>>0<=Um>>>0))for(;;){if(16+Xm>>>0<(Tm=ga(q[Um>>2],0,Vm))>>>0)for(;o[0|(Tm=Tm+-1|0)]=48,16+Xm>>>0<Tm>>>0;);if(Z(a,Tm,(0|_m)<9?_m:9),_m=_m+-9|0,an>>>0<=(Um=Um+4|0)>>>0)break p;if(!(0<(0|_m)))break}_(a,48,_m+9|0,9,0)}else{q:if(!((0|_m)<0))for(Sm=cn?an:Um+4|0,Qm=16+Xm|8,bn=16+Xm|9,Vm=Um;;){if((0|bn)==(0|(Tm=ga(q[Vm>>2],0,bn)))&&(o[24+Xm|0]=48,Tm=Qm),(0|Um)!=(0|Vm)){if(!(Tm>>>0<=16+Xm>>>0))for(;o[0|(Tm=Tm+-1|0)]=48,16+Xm>>>0<Tm>>>0;);}else Z(a,Tm,1),Tm=Tm+1|0,(0|_m)<1&&!Ym||Z(a,3907,1);if(Z(a,$m=Tm,(0|(Tm=bn-Tm|0))<(0|_m)?Tm:_m),_m=_m-Tm|0,Sm>>>0<=(Vm=Vm+4|0)>>>0)break q;if(!(-1<(0|_m)))break}_(a,48,_m+18|0,18,0),Z(a,fn,dn-fn|0)}}return _(a,32,Pm,Zm,8192^Rm),L=560+Xm|0,0|((0|Zm)<(0|Pm)?Pm:Zm)},n[12]=function(a,Gl){a|=0;var Cm=Gl|=0;Gl=q[Gl>>2]+15&-16,q[Cm>>2]=Gl+16,Cm=a,a=function(a,Gl,Hl,Zl){var dm,am,_l=0,$l=0,bm=0,cm=0;return L=am=L-32|0,_l=(cm=_l=2147483647&Zl)-1006698496|0,$l=_l=(dm=$l=bm=Hl)>>>0<0?_l+1|0:_l,_l=cm-1140785152|0,(0|(_l=bm>>>0<0?_l+1|0:_l))==(0|$l)&dm>>>0<bm>>>0|$l>>>0<_l>>>0?(_l=Zl<<4|Hl>>>28,Hl=Hl<<4|Gl>>>28,134217728==(0|(bm=Gl&=268435455))&1<=a>>>0|134217728<Gl>>>0?(_l=_l+1073741824|0,(a=Hl+1|0)>>>0<1&&(_l=_l+1|0),$l=a):(_l=_l-((($l=Hl)>>>0<0)+-1073741824|0)|0,a|134217728^bm||((a=$l+(1&$l)|0)>>>0<$l>>>0&&(_l=_l+1|0),$l=a))):(!bm&2147418112==(0|cm)?!(a|Gl):2147418112==(0|cm)&bm>>>0<0|cm>>>0<2147418112)?(_l=2146435072,1140785151==(($l=0)|cm)&4294967295<bm>>>0|1140785151<cm>>>0||(bm=cm>>>16)>>>(_l=0)<15249||(function(a,Gl,Hl,Cm,Dm,Em){var Hm,Im,Fm=0,Gm=0;64&Em?(Gl=31&(Hl=Em-64|0),Gl=32<=(63&Hl)>>>0?(Hl=0,Dm>>>Gl):(Hl=Dm>>>Gl,((1<<Gl)-1&Dm)<<32-Gl|Cm>>>Gl),Dm=Cm=0):Em&&(Gm=Dm,Fm=31&(Im=64-Em|0),Im=32<=(63&Im)>>>0?(Gm=Cm<<Fm,0):(Gm=(1<<Fm)-1&Cm>>>32-Fm|Gm<<Fm,Cm<<Fm),Hm=Gl,Gl=31&(Fm=Em),Gl=32<=(63&Fm)>>>0?(Fm=0,Hl>>>Gl):(Fm=Hl>>>Gl,((1<<Gl)-1&Hl)<<32-Gl|Hm>>>Gl),Gl|=Im,Hl=Fm|Gm,Fm=Cm,Cm=31&Em,Cm=32<=(63&Em)>>>0?(Gm=0,Dm>>>Cm):(Gm=Dm>>>Cm,((1<<Cm)-1&Dm)<<32-Cm|Fm>>>Cm),Dm=Gm),q[a>>2]=Gl,q[4+a>>2]=Hl,q[8+a>>2]=Cm,q[12+a>>2]=Dm}(am,a,Gl,Hl,_l=65535&Zl|65536,15361-bm|0),function(a,Gl,Hl,Zl,fm,gm){var jm,hm,im=0;64&gm?(Zl=Gl,Gl=31&(fm=gm+-64|0),32<=(63&fm)>>>0?(fm=Zl<<Gl,Zl=0):(fm=(1<<Gl)-1&Zl>>>32-Gl|Hl<<Gl,Zl<<=Gl),Hl=Gl=0):gm&&(hm=Zl,Zl=31&(jm=gm),hm=32<=(63&gm)>>>0?(im=hm<<Zl,0):(im=(1<<Zl)-1&hm>>>32-Zl|fm<<Zl,hm<<Zl),Zl=Hl,fm=31&(gm=64-gm|0),32<=(63&gm)>>>0?(gm=0,Zl>>>=fm):(gm=Zl>>>fm,Zl=((1<<fm)-1&Zl)<<32-fm|Gl>>>fm),Zl|=hm,fm=gm|im,gm=Gl,Gl=31&jm,Gl=32<=(63&jm)>>>0?(im=gm<<Gl,0):(im=(1<<Gl)-1&gm>>>32-Gl|Hl<<Gl,gm<<Gl),Hl=im),q[a>>2]=Gl,q[4+a>>2]=Hl,q[8+a>>2]=Zl,q[12+a>>2]=fm}(16+am|0,a,Gl,Hl,_l,bm+-15233|0),Hl=q[4+am>>2],a=q[8+am>>2],_l=q[12+am>>2]<<4|a>>>28,$l=a<<4|Hl>>>28,134217728==(0|(Hl=a=268435455&Hl))&1<=(Gl=q[am>>2]|(0!=(q[16+am>>2]|q[24+am>>2])|0!=(q[20+am>>2]|q[28+am>>2])))>>>0|134217728<a>>>0?((a=$l+1|0)>>>0<1&&(_l=_l+1|0),$l=a):Gl|134217728^Hl||((a=$l+(1&$l)|0)>>>0<$l>>>0&&(_l=_l+1|0),$l=a))):($l=Hl<<4|Gl>>>28,_l=524287&(_l=Zl<<4|Hl>>>28)|2146959360),L=32+am|0,f(0,0|$l),f(1,-2147483648&Zl|_l),+g()}(q[Gl>>2],q[Gl+4>>2],q[Gl+8>>2],q[Gl+12>>2]),v[Cm>>3]=a},n[13]=function(a){return 0},n[14]=function(a,Gl,Cm){Gl|=0,Cm|=0;var Mm,Em,Dm=0,Jm=0,Km=0,Lm=0;for(L=Em=L-32|0,Dm=q[28+(a|=0)>>2],q[16+Em>>2]=Dm,Km=q[a+20>>2],q[28+Em>>2]=Cm,q[24+Em>>2]=Gl,Km=(q[20+Em>>2]=Gl=Km-Dm|0)+Cm|0,Lm=2,Gl=16+Em|0;;){a:{if((Jm=(Dm=0)|K(q[a+60>>2],0|Gl,0|Lm,12+Em|0))&&(q[2094]=Jm,Dm=-1),(0|(Dm=Dm?q[12+Em>>2]=-1:q[12+Em>>2]))==(0|Km))Gl=q[a+44>>2],q[a+28>>2]=Gl,q[a+20>>2]=Gl,q[a+16>>2]=Gl+q[a+48>>2],a=Cm;else{if(-1<(0|Dm))break a;q[a+28>>2]=0,q[a+16>>2]=0,q[a+20>>2]=0,q[a>>2]=32|q[a>>2],2!=((a=0)|Lm)&&(a=Cm-q[Gl+4>>2]|0)}return L=32+Em|0,0|a}Jm=q[Gl+4>>2],q[(Gl=(Mm=Jm>>>0<Dm>>>0)?Gl+8|0:Gl)>>2]=(Jm=Dm-(Mm?Jm:0)|0)+q[Gl>>2],q[Gl+4>>2]=q[Gl+4>>2]-Jm,Km=Km-Dm|0,Lm=Lm-Mm|0}},n[15]=function(a,Gl,Cm,Dm){return M=0},{d:function(){},e:function(){return q[1813]},f:function(){return 83951616},g:function(){return 5},h:function(a,tj){return tj|=0,L=tj=L-16|0,a=(a|=0)?sa(a)?(Y(4,2150,0),0):r[a+4|0]:(q[tj+4>>2]=1444,q[tj>>2]=2267,Y(4,1294,tj),0),L=tj+16|0,0|a},i:function(a,tj){var uj;return tj|=0,L=uj=L-48|0,a=(a|=0)?(a+63&-64)!=(0|a)?(q[36+uj>>2]=1522,q[32+uj>>2]=2284,Y(4,1294,32+uj|0),0):(tj+63&-64)==(0|tj)&&tj?function(a,Tk){var nl,Uk=0,Vk=0,Wk=0,Xk=0,Yk=0,Zk=0,_k=0,$k=0,al=0,bl=0,cl=0,dl=0,el=0,fl=0,gl=0,hl=0,il=0,jl=0,kl=0,ll=0,ml=0;L=Yk=(nl=Vk=L)-704&-64;a:if(Tk>>>0<=1343)Y(4,1235,0);else if(sa(a))Y(4,1469,0);else if(Vk=r[0|(ll=a+4|0)]){if(!(6<=Vk>>>0)){(hl=1==(0|!r[a+5|0]))||(da(ll,1),X(a- -64|0,4,160)),ca(Yk- -64|0,0,640),na(a,Yk- -64|0),Vk=a+Tk|0,Tk=q[Yk+64>>2];b:{c:{d:{if(5<=(gl=r[a+4|0])>>>0){if(Tk>>>0<a>>>0|Vk>>>0<Tk>>>0)break c;if((Xk=Tk+256|0)>>>0<a>>>0)break c;if(Xk>>>0<=Vk>>>0)break d;break c}if(Tk>>>0<a>>>0|Vk>>>0<Tk>>>0)break c;if((Xk=Tk+128|0)>>>0<a>>>0|Vk>>>0<Xk>>>0)break c}if(!((Wk=q[Yk+68>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Xk>>>0||(Wk=Wk- -64|0)>>>0<a>>>0|Vk>>>0<Wk>>>0||(0|(bl=q[Tk>>2]))<0||(Xk=q[Yk+72>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0||(Wk=(Uk=Xk)+(Xk=bl<<2)|0)>>>0<a>>>0|Vk>>>0<Wk>>>0||(_k=q[Yk+76>>2])>>>0<a>>>0|Vk>>>0<_k>>>0|_k>>>0<Wk>>>0||(Uk=(bl<<6)+_k|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Wk=q[Yk+80>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0||(Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Wk=q[Yk+84>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0||(Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Wk=q[Yk+88>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0||(Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Wk=q[Yk+92>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0||(Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Wk=q[Yk+96>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0||(Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Wk=q[Yk+100>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0||(Wk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Wk>>>0||(0|(Uk=q[Tk+4>>2]))<0||(Xk=q[Yk+104>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0||(Zk=(Wk=Xk)+(Xk=Uk<<2)|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(Wk=q[Yk+108>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Zk>>>0||(Uk=Wk+(Uk<<6)|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Wk=q[Yk+112>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0||(Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Wk=q[Yk+116>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0||(Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Wk=q[Yk+120>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0||(Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Wk=q[Yk+124>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0||(Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Wk=q[Yk+128>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0||(Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Wk=q[Yk+132>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0||(Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Wk=q[Yk+136>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0||(Wk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Wk>>>0||(0|(Uk=q[Tk+8>>2]))<0||(Xk=q[Yk+140>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0||(Wk=(cl=Uk<<2)+Xk|0)>>>0<a>>>0|Vk>>>0<Wk>>>0||(Xk=q[Yk+144>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0||(Wk=Xk+cl|0)>>>0<a>>>0|Vk>>>0<Wk>>>0||(Xk=q[Yk+148>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0||(Wk=Xk+cl|0)>>>0<a>>>0|Vk>>>0<Wk>>>0||(Xk=q[Yk+156>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0||(Wk=Xk+cl|0)>>>0<a>>>0|Vk>>>0<Wk>>>0||(Xk=q[Yk+160>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0||(Wk=Xk+cl|0)>>>0<a>>>0|Vk>>>0<Wk>>>0||(Xk=q[Yk+164>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0||(Wk=Xk+cl|0)>>>0<a>>>0|Vk>>>0<Wk>>>0||(0|(Uk=q[Tk+12>>2]))<0||(Xk=q[Yk+172>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0||(Wk=(dl=Uk<<2)+Xk|0)>>>0<a>>>0|Vk>>>0<Wk>>>0||(Xk=q[Yk+176>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0||(Wk=Xk+dl|0)>>>0<a>>>0|Vk>>>0<Wk>>>0||(Xk=q[Yk+180>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0||(Wk=Xk+dl|0)>>>0<a>>>0|Vk>>>0<Wk>>>0||(Xk=q[Yk+188>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0||(Uk=Xk+dl|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(0|(Wk=q[Tk+16>>2]))<0||(Xk=q[Yk+192>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Uk>>>0||(Zk=(Uk=Xk)+(Xk=Wk<<2)|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(Uk=q[Yk+196>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Zk=Uk+Xk|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(Uk=q[Yk+200>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Zk=Uk+Xk|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(Uk=q[Yk+204>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Zk=Uk+Xk|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(Uk=q[Yk+208>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Zk=Uk+(Wk<<6)|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(Uk=q[Yk+212>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Zk=Uk+Xk|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(Uk=q[Yk+216>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Zk=Uk+Xk|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(Uk=q[Yk+220>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Zk=Uk+Xk|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(Uk=q[Yk+228>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Zk=Uk+Xk|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(Uk=q[Yk+232>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Zk=Uk+Xk|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(Uk=q[Yk+236>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Zk=Uk+Xk|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(Uk=q[Yk+240>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Zk=Uk+Xk|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(Uk=q[Yk+244>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Zk=Uk+Xk|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(Uk=q[Yk+248>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Uk=Uk+Wk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Wk=q[Yk+252>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0||(Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Wk=q[Yk+256>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0||(Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Wk=q[Yk+260>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0||(Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Wk=q[Yk+264>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0||(Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Wk=q[Yk+268>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0||(Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Wk=q[Yk+272>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0||(Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(0|(Zk=q[Tk+20>>2]))<0||(Wk=q[Yk+276>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0||(el=(Uk=Wk)+(Wk=Zk<<2)|0)>>>0<a>>>0|Vk>>>0<el>>>0||(Uk=q[Yk+280>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<el>>>0||(Zk=Uk+(Zk<<6)|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(Uk=q[Yk+284>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Zk=Uk+Wk|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(Uk=q[Yk+288>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Zk=Uk+Wk|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(Uk=q[Yk+292>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Zk=Uk+Wk|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(Uk=q[Yk+296>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Zk=Uk+Wk|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(Uk=q[Yk+300>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Zk=Uk+Wk|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(Uk=q[Yk+308>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Zk=Uk+Wk|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(Uk=q[Yk+312>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Zk=Uk+Wk|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(0|(el=q[Tk+24>>2]))<0||(Uk=q[Yk+336>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Zk=Uk+(el<<2)|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(0|(el=q[Tk+28>>2]))<0||(Uk=q[Yk+340>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Zk=(jl=el<<2)+Uk|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(Uk=q[Yk+344>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(Zk=Uk+jl|0)>>>0<a>>>0|Vk>>>0<Zk>>>0||(0|(el=q[Tk+32>>2]))<0||(Uk=q[Yk+356>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<Zk>>>0||(el=(Zk=el<<2)+Uk|0)>>>0<a>>>0|Vk>>>0<el>>>0||(Uk=q[Yk+360>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<el>>>0||(el=Uk+Zk|0)>>>0<a>>>0|Vk>>>0<el>>>0||(Uk=q[Yk+364>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<el>>>0||(el=Uk+Zk|0)>>>0<a>>>0|Vk>>>0<el>>>0||(Uk=q[Yk+368>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<el>>>0||(el=Uk+Zk|0)>>>0<a>>>0|Vk>>>0<el>>>0||(Uk=q[Yk+372>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<el>>>0||(el=Uk+Zk|0)>>>0<a>>>0|Vk>>>0<el>>>0||(Uk=q[Yk+376>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<el>>>0||(el=Uk+Zk|0)>>>0<a>>>0|Vk>>>0<el>>>0||(Uk=q[Yk+380>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<el>>>0||(el=Uk+Zk|0)>>>0<a>>>0|Vk>>>0<el>>>0||(0|($k=q[Tk+36>>2]))<0||(Uk=q[Yk+392>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<el>>>0||($k=(el=$k<<2)+Uk|0)>>>0<a>>>0|Vk>>>0<$k>>>0||(Uk=q[Yk+396>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<$k>>>0||($k=Uk+el|0)>>>0<a>>>0|Vk>>>0<$k>>>0||(Uk=q[Yk+400>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<$k>>>0||($k=Uk+el|0)>>>0<a>>>0|Vk>>>0<$k>>>0||(0|(al=q[Tk+40>>2]))<0||(Uk=q[Yk+412>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<$k>>>0||($k=Uk+(al<<2)|0)>>>0<a>>>0|Vk>>>0<$k>>>0||(0|(al=q[Tk+44>>2]))<0||(Uk=q[Yk+424>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<$k>>>0||($k=Uk+(al<<2)|0)>>>0<a>>>0|Vk>>>0<$k>>>0||(0|(al=q[Tk+48>>2]))<0||(Uk=q[Yk+428>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<$k>>>0||($k=(al<<=2)+Uk|0)>>>0<a>>>0|Vk>>>0<$k>>>0||(Uk=q[Yk+432>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<$k>>>0||($k=Uk+al|0)>>>0<a>>>0|Vk>>>0<$k>>>0||(0|(al=q[Tk+52>>2]))<0||(Uk=q[Yk+416>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<$k>>>0||($k=(al<<=2)+Uk|0)>>>0<a>>>0|Vk>>>0<$k>>>0||(Uk=q[Yk+420>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<$k>>>0||($k=Uk+al|0)>>>0<a>>>0|Vk>>>0<$k>>>0||(0|(al=q[Tk+56>>2]))<0||(Uk=q[Yk+552>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<$k>>>0||($k=Uk+(al<<2)|0)>>>0<a>>>0|Vk>>>0<$k>>>0||(0|(al=q[Tk+60>>2]))<0||(Uk=q[Yk+556>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<$k>>>0||($k=Uk+(al<<2)|0)>>>0<a>>>0|Vk>>>0<$k>>>0||(0|(al=q[Tk+64>>2]))<0||(Uk=q[Yk+560>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<$k>>>0||($k=Uk+(al<<1)|0)>>>0<a>>>0|Vk>>>0<$k>>>0||(0|(al=q[Tk+68>>2]))<0||(Uk=q[Yk+564>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<$k>>>0||($k=Uk+(al<<2)|0)>>>0<a>>>0|Vk>>>0<$k>>>0||(0|(al=q[Tk+72>>2]))<0||(Uk=q[Yk+568>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<$k>>>0||(al=($k=Uk)+(Uk=al<<2)|0)>>>0<a>>>0|Vk>>>0<al>>>0||($k=q[Yk+572>>2])>>>0<a>>>0|Vk>>>0<$k>>>0|$k>>>0<al>>>0||(al=Uk+$k|0)>>>0<a>>>0|Vk>>>0<al>>>0||($k=q[Yk+576>>2])>>>0<a>>>0|Vk>>>0<$k>>>0|$k>>>0<al>>>0||(al=Uk+$k|0)>>>0<a>>>0|Vk>>>0<al>>>0||($k=q[Yk+580>>2])>>>0<a>>>0|Vk>>>0<$k>>>0|$k>>>0<al>>>0||(al=Uk+$k|0)>>>0<a>>>0|Vk>>>0<al>>>0||($k=q[Yk+584>>2])>>>0<a>>>0|Vk>>>0<$k>>>0|$k>>>0<al>>>0||($k=Uk+$k|0)>>>0<a>>>0|Vk>>>0<$k>>>0||(0|(al=q[Tk+76>>2]))<0||(Uk=q[Yk+588>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<$k>>>0||(al=($k=al<<2)+Uk|0)>>>0<a>>>0|Vk>>>0<al>>>0||(Uk=q[Yk+592>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<al>>>0||(al=Uk+$k|0)>>>0<a>>>0|Vk>>>0<al>>>0||(Uk=q[Yk+596>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<al>>>0||($k=Uk+$k|0)>>>0<a>>>0|Vk>>>0<$k>>>0||(0|(al=q[Tk+80>>2]))<0||(Uk=q[Yk+600>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<$k>>>0||(fl=($k=Uk)+(Uk=al<<2)|0)>>>0<a>>>0|Vk>>>0<fl>>>0||($k=q[Yk+604>>2])>>>0<a>>>0|Vk>>>0<$k>>>0|$k>>>0<fl>>>0||(al=$k+(al<<6)|0)>>>0<a>>>0|Vk>>>0<al>>>0||($k=q[Yk+608>>2])>>>0<a>>>0|Vk>>>0<$k>>>0|$k>>>0<al>>>0||(al=Uk+$k|0)>>>0<a>>>0|Vk>>>0<al>>>0||($k=q[Yk+612>>2])>>>0<a>>>0|Vk>>>0<$k>>>0|$k>>>0<al>>>0||(al=Uk+$k|0)>>>0<a>>>0|Vk>>>0<al>>>0||($k=q[Yk+616>>2])>>>0<a>>>0|Vk>>>0<$k>>>0|$k>>>0<al>>>0||(al=Uk+$k|0)>>>0<a>>>0|Vk>>>0<al>>>0||($k=q[Yk+620>>2])>>>0<a>>>0|Vk>>>0<$k>>>0|$k>>>0<al>>>0||(al=Uk+$k|0)>>>0<a>>>0|Vk>>>0<al>>>0||($k=q[Yk+624>>2])>>>0<a>>>0|Vk>>>0<$k>>>0|$k>>>0<al>>>0||(al=Uk+$k|0)>>>0<a>>>0|Vk>>>0<al>>>0||($k=q[Yk+628>>2])>>>0<a>>>0|Vk>>>0<$k>>>0|$k>>>0<al>>>0||(al=Uk+$k|0)>>>0<a>>>0|Vk>>>0<al>>>0||($k=q[Yk+632>>2])>>>0<a>>>0|Vk>>>0<$k>>>0|$k>>>0<al>>>0||($k=Uk+$k|0)>>>0<a>>>0|Vk>>>0<$k>>>0||(0|(al=q[Tk+84>>2]))<0||(Uk=q[Yk+636>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<$k>>>0||($k=Uk+(al<<2)|0)>>>0<a>>>0|Vk>>>0<$k>>>0||(Uk=q[Yk+640>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<$k>>>0||($k=Uk+(al<<1)|0)>>>0<a>>>0|Vk>>>0<$k>>>0||(0|(al=q[Tk+88>>2]))<0||(Uk=q[Yk+644>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<$k>>>0||(Uk=Uk+(al<<2)|0)>>>0<a>>>0|Vk>>>0<Uk>>>0)){if(!(gl>>>0<2)){if(($k=q[Yk+168>>2])>>>0<a>>>0|Vk>>>0<$k>>>0|$k>>>0<Uk>>>0)break c;if((Uk=$k+cl|0)>>>0<a>>>0|Vk>>>0<Uk>>>0)break c;if(!(gl>>>0<4)){if(($k=q[Yk+324>>2])>>>0<a>>>0|Vk>>>0<$k>>>0|$k>>>0<Uk>>>0)break c;if(($k=Wk+$k|0)>>>0<a>>>0|Vk>>>0<$k>>>0)break c;if((Uk=q[Yk+328>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<$k>>>0)break c;if(($k=Uk+Wk|0)>>>0<a>>>0|Vk>>>0<$k>>>0)break c;if((Uk=q[Yk+332>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<$k>>>0)break c;if(($k=Uk+Wk|0)>>>0<a>>>0|Vk>>>0<$k>>>0)break c;if((Uk=q[Yk+152>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<$k>>>0)break c;if((cl=Uk+cl|0)>>>0<a>>>0|Vk>>>0<cl>>>0)break c;if((Uk=q[Yk+184>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<cl>>>0)break c;if((cl=Uk+dl|0)>>>0<a>>>0|Vk>>>0<cl>>>0)break c;if((Uk=q[Yk+224>>2])>>>0<a>>>0|Vk>>>0<Uk>>>0|Uk>>>0<cl>>>0)break c;if((Uk=Uk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0)break c;if((0|(cl=q[Tk+92>>2]))<0)break c;if((Xk=q[Yk+648>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Uk>>>0)break c;if((cl=(Uk=cl<<2)+Xk|0)>>>0<a>>>0|Vk>>>0<cl>>>0)break c;if((Xk=q[Yk+652>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<cl>>>0)break c;if((cl=Uk+Xk|0)>>>0<a>>>0|Vk>>>0<cl>>>0)break c;if((Xk=q[Yk+656>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<cl>>>0)break c;if((Uk=Uk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0)break c;if((0|(cl=q[Tk+96>>2]))<0)break c;if((Xk=q[Yk+660>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Uk>>>0)break c;if((cl=(Uk=cl<<2)+Xk|0)>>>0<a>>>0|Vk>>>0<cl>>>0)break c;if((Xk=q[Yk+664>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<cl>>>0)break c;if((cl=Uk+Xk|0)>>>0<a>>>0|Vk>>>0<cl>>>0)break c;if((Xk=q[Yk+668>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<cl>>>0)break c;if((Uk=Uk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0)break c;if((Xk=q[Yk+304>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Uk>>>0)break c;if((Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0)break c;if((Xk=q[Yk+316>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Uk>>>0)break c;if((Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0)break c;if((Xk=q[Yk+320>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Uk>>>0)break c;if((Wk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Wk>>>0)break c;if((0|(Uk=q[Tk+100>>2]))<0)break c;if((Xk=q[Yk+436>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0)break c;if((Uk=(Wk=Uk<<2)+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0)break c;if((Xk=q[Yk+440>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Uk>>>0)break c;if((Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0)break c;if((Xk=q[Yk+444>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Uk>>>0)break c;if((Wk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Wk>>>0)break c;if((0|(Uk=q[Tk+104>>2]))<0)break c;if((Xk=q[Yk+448>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0)break c;if((Uk=(Wk=Xk)+(Xk=Uk<<2)|0)>>>0<a>>>0|Vk>>>0<Uk>>>0)break c;if((Wk=q[Yk+452>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0)break c;if((Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0)break c;if((Wk=q[Yk+456>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0)break c;if((Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0)break c;if((Wk=q[Yk+460>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0)break c;if((Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0)break c;if((Wk=q[Yk+464>>2])>>>0<a>>>0|Vk>>>0<Wk>>>0|Wk>>>0<Uk>>>0)break c;if((Wk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Wk>>>0)break c;if((0|(Uk=q[Tk+108>>2]))<0)break c;if((Xk=q[Yk+480>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0)break c;if((Uk=(Wk=Uk<<2)+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0)break c;if((Xk=q[Yk+484>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Uk>>>0)break c;if((Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0)break c;if((Xk=q[Yk+488>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Uk>>>0)break c;if((Wk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Wk>>>0)break c;if((0|(Uk=q[Tk+112>>2]))<0)break c;if((Xk=q[Yk+504>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0)break c;if((Uk=(Wk=Uk<<2)+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0)break c;if((Xk=q[Yk+508>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Uk>>>0)break c;if((Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0)break c;if((Xk=q[Yk+512>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Uk>>>0)break c;if((Wk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Wk>>>0)break c;if((0|(Uk=q[Tk+116>>2]))<0)break c;if((Xk=q[Yk+528>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0)break c;if((Wk=Xk+(Uk<<2)|0)>>>0<a>>>0|Vk>>>0<Wk>>>0)break c;if((0|(Uk=q[Tk+120>>2]))<0)break c;if((Xk=q[Yk+532>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0)break c;if((Uk=(Wk=Uk<<2)+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0)break c;if((Xk=q[Yk+536>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Uk>>>0)break c;if((Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0)break c;if((Xk=q[Yk+540>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Uk>>>0)break c;if((Wk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Wk>>>0)break c;if((0|(Uk=q[Tk+124>>2]))<0)break c;if((Xk=q[Yk+544>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0)break c;if((Wk=(Uk<<=2)+Xk|0)>>>0<a>>>0|Vk>>>0<Wk>>>0)break c;if((Xk=q[Yk+548>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0)break c;if((Uk=Uk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0)break c}}if(gl>>>0<5)break b;if(!((Xk=q[Yk+348>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Uk>>>0||(Wk=Xk+jl|0)>>>0<a>>>0|Vk>>>0<Wk>>>0||(Xk=q[Yk+352>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0||(Wk=Xk+jl|0)>>>0<a>>>0|Vk>>>0<Wk>>>0||(Xk=q[Yk+384>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0||(Wk=Xk+Zk|0)>>>0<a>>>0|Vk>>>0<Wk>>>0||(Xk=q[Yk+388>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0||(Wk=Xk+Zk|0)>>>0<a>>>0|Vk>>>0<Wk>>>0||(Xk=q[Yk+404>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0||(Wk=Xk+el|0)>>>0<a>>>0|Vk>>>0<Wk>>>0||(Xk=q[Yk+408>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0||(Wk=Xk+el|0)>>>0<a>>>0|Vk>>>0<Wk>>>0||(0|(Uk=q[Tk+128>>2]))<0||(Xk=q[Yk+468>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0||(Uk=(Wk=Uk<<2)+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Xk=q[Yk+472>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Uk>>>0||(Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Xk=q[Yk+476>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Uk>>>0||(Wk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Wk>>>0||(0|(Uk=q[Tk+132>>2]))<0||(Xk=q[Yk+492>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0||(Uk=(Wk=Uk<<2)+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Xk=q[Yk+496>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Uk>>>0||(Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Xk=q[Yk+500>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Uk>>>0||(Wk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Wk>>>0||(0|(Uk=q[Tk+136>>2]))<0||(Xk=q[Yk+516>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Wk>>>0||(Uk=(Wk=Uk<<2)+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Xk=q[Yk+520>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Uk>>>0||(Uk=Wk+Xk|0)>>>0<a>>>0|Vk>>>0<Uk>>>0||(Xk=q[Yk+524>>2])>>>0<a>>>0|Vk>>>0<Xk>>>0|Xk>>>0<Uk>>>0||(Xk=Wk+Xk|0)>>>0<a>>>0)&&Xk>>>0<=Vk>>>0)break b}}Y(4,1760,0),da(ll,1),X(a- -64|0,4,160);break a}hl||(ya(a),o[a+5|0]=0,Tk=q[Yk+64>>2],bl=q[Tk>>2],_k=q[Yk+76>>2],gl=r[a+4|0]);f:{if((a=0)<(0|bl)){for(;;){if(63<ia((a<<6)+_k|0)>>>0)break f;if((0|bl)==(0|(a=a+1|0)))break}if(Uk=Tk+48|0,(Vk=0)<(0|(a=q[Tk>>2]))){for(Xk=q[Tk+48>>2],Wk=q[Yk+80>>2];;){if((0|(_k=q[Wk+(Vk<<2)>>2]))<0|(0|Xk)<=(0|_k))break f;if((0|a)==(0|(Vk=Vk+1|0)))break}for(el=Tk+24|0,Xk=q[Tk+24>>2],Zk=q[Yk+88>>2],bl=q[Yk+84>>2],Vk=0;;){if(Wk=q[(_k=Vk<<2)+Zk>>2]){if((0|Wk)<0|(0|Xk)<(0|Wk))break f;if((0|(_k=q[_k+bl>>2]))<0|(0|Xk)<=(0|_k))break f;if((Wk=Wk+_k|0)>>>31|(0|Xk)<(0|Wk))break f}if((0|a)==(0|(Vk=Vk+1|0)))break}for(Vk=0,Xk=q[Yk+92>>2];;){if(1<t[Xk+(Vk<<2)>>2])break f;if((0|a)==(0|(Vk=Vk+1|0)))break}for(Vk=0,Xk=q[Yk+96>>2];;){if(1<t[Xk+(Vk<<2)>>2])break f;if((0|a)==(0|(Vk=Vk+1|0)))break}for(Vk=0,Xk=q[Yk+100>>2];;){if((0|(Wk=q[Xk+(Vk<<2)>>2]))<-1|(0|a)<=(0|Wk))break f;if((0|a)==(0|(Vk=Vk+1|0)))break}}else el=Tk+24|0}else el=Tk+24|0,Uk=Tk+48|0;if((a=0)<(0|(Vk=q[Tk+4>>2]))){for(Xk=q[Yk+108>>2];;){if(63<ia(Xk+(a<<6)|0)>>>0)break f;if((0|Vk)==(0|(a=a+1|0)))break}if(Zk=(Xk=q[Tk+48>>2])+-1|0,!(((Vk=0)|(a=q[Tk+4>>2]))<=0)){for(Wk=q[Yk+112>>2];;){if((0|(_k=q[Wk+(Vk<<2)>>2]))<0|(0|Xk)<=(0|_k))break f;if((0|a)==(0|(Vk=Vk+1|0)))break}for(Vk=0,Xk=q[Yk+116>>2];;){if(1<t[Xk+(Vk<<2)>>2])break f;if((0|a)==(0|(Vk=Vk+1|0)))break}for(Vk=0,Xk=q[Yk+120>>2];;){if(1<t[Xk+(Vk<<2)>>2])break f;if((0|a)==(0|(Vk=Vk+1|0)))break}for(Xk=q[Tk>>2],Vk=0,Wk=q[Yk+124>>2];;){if((0|(_k=q[Wk+(Vk<<2)>>2]))<-1|(0|Xk)<=(0|_k))break f;if((0|a)==(0|(Vk=Vk+1|0)))break}for(Vk=0,Xk=q[Yk+128>>2];;){if((0|(Wk=q[Xk+(Vk<<2)>>2]))<-1|(0|a)<=(0|Wk))break f;if((0|a)==(0|(Vk=Vk+1|0)))break}for(Vk=0,Xk=q[Yk+132>>2];;){if(1<t[Xk+(Vk<<2)>>2])break f;if((0|a)==(0|(Vk=Vk+1|0)))break}for(Wk=Tk+8|0,_k=Tk+12|0,bl=q[Yk+136>>2],Vk=0;;){if(1<(dl=q[(cl=Vk<<2)+Xk>>2])>>>0)break f;if((0|(cl=q[bl+cl>>2]))<0|(0|cl)>=q[(dl-1|0?Wk:_k)>>2])break f;if((0|a)==(0|(Vk=Vk+1|0)))break}}}else Zk=q[Uk>>2]+-1|0;if((a=0)<(0|(Vk=q[Tk+8>>2]))){for(Xk=q[Yk+140>>2];;){if((0|(Wk=q[Xk+(a<<2)>>2]))<0|(0|Zk)<(0|Wk))break f;if((0|Vk)==(0|(a=a+1|0)))break}for(jl=Tk+28|0,Xk=q[Tk+28>>2],bl=q[Yk+148>>2],cl=q[Yk+144>>2],a=0;;){if(Wk=q[(_k=a<<2)+bl>>2]){if((0|Wk)<0|(0|Xk)<(0|Wk))break f;if((0|(_k=q[_k+cl>>2]))<0|(0|Xk)<=(0|_k))break f;if((Wk=Wk+_k|0)>>>31|(0|Xk)<(0|Wk))break f}if((0|Vk)==(0|(a=a+1|0)))break}for(a=0,Wk=q[Yk+156>>2],_k=q[Yk+164>>2],bl=q[Yk+160>>2];;){if((0|(cl=q[(Xk=a<<2)+bl>>2]))<1)break f;if((0|(dl=q[Xk+_k>>2]))<1)break f;if((0|(Xk=q[Wk+Xk>>2]))<1|(0|Xk)!=(0|w(dl+1|0,cl+1|0)))break f;if((0|Vk)==(0|(a=a+1|0)))break}}else jl=Tk+28|0;if((a=0)<(0|(Wk=q[Tk+12>>2]))){for(Vk=q[Yk+172>>2];;){if((0|(Xk=q[Vk+(a<<2)>>2]))<0|(0|Zk)<(0|Xk))break f;if((0|Wk)==(0|(a=a+1|0)))break}for($k=Tk+32|0,Vk=q[Tk+32>>2],Zk=q[Yk+180>>2],bl=q[Yk+176>>2],a=0;;){if(Xk=q[(_k=a<<2)+Zk>>2]){if((0|Xk)<0|(0|Vk)<(0|Xk))break f;if((0|(_k=q[_k+bl>>2]))<0|(0|Vk)<=(0|_k))break f;if((Xk=Xk+_k|0)>>>31|(0|Vk)<(0|Xk))break f}if((0|Wk)==(0|(a=a+1|0)))break}}else $k=Tk+32|0;Xk=Tk+16|0;m:{n:{if(!(((a=0)|(Vk=q[Tk+16>>2]))<=0)){for(Wk=q[Yk+208>>2];;){if(63<ia(Wk+(a<<6)|0)>>>0)break f;if((0|Vk)==(0|(a=a+1|0)))break}if(!(((Vk=0)|(a=q[Xk>>2]))<=0)){for(Wk=q[Uk>>2],_k=q[Yk+212>>2];;){if((0|(Zk=q[_k+(Vk<<2)>>2]))<0|(0|Wk)<=(0|Zk))break f;if((0|a)==(0|(Vk=Vk+1|0)))break}for(ll=Tk+36|0,Wk=q[Tk+36>>2],bl=q[Yk+220>>2],cl=q[Yk+216>>2],Vk=0;;){if(_k=q[(Zk=Vk<<2)+bl>>2]){if((0|_k)<0|(0|Wk)<(0|_k))break f;if((0|(Zk=q[Zk+cl>>2]))<0|(0|Wk)<=(0|Zk))break f;if((_k=_k+Zk|0)>>>31|(0|Wk)<(0|_k))break f}if((0|a)==(0|(Vk=Vk+1|0)))break}for(Vk=0,Wk=q[Yk+228>>2];;){if(1<t[Wk+(Vk<<2)>>2])break f;if((0|a)==(0|(Vk=Vk+1|0)))break}for(Vk=0,Wk=q[Yk+232>>2];;){if(1<t[Wk+(Vk<<2)>>2])break f;if((0|a)==(0|(Vk=Vk+1|0)))break}for(Wk=q[Tk>>2],Vk=0,_k=q[Yk+236>>2];;){if((0|(Zk=q[_k+(Vk<<2)>>2]))<-1|(0|Wk)<=(0|Zk))break f;if((0|a)==(0|(Vk=Vk+1|0)))break}for(Wk=q[Tk+4>>2],Vk=0,_k=q[Yk+240>>2];;){if((0|(Zk=q[_k+(Vk<<2)>>2]))<-1|(0|Wk)<=(0|Zk))break f;if((0|a)==(0|(Vk=Vk+1|0)))break}for(Wk=q[Yk+244>>2],Vk=0;;){if(q[Wk+(Vk<<2)>>2]<0)break f;if((0|a)==(0|(Vk=Vk+1|0)))break}break n}}_k=Tk+68|0,ll=Tk+36|0;break m}for(Wk=q[Yk+252>>2],Vk=0;;){if(q[Wk+(Vk<<2)>>2]<0)break f;if((0|a)==(0|(Vk=Vk+1|0)))break}for(_k=q[Tk+60>>2],Vk=0,Zk=q[Yk+256>>2];;){if((bl=q[(bl=Vk<<2)+Zk>>2]+(q[Wk+bl>>2]<<1)|0)>>>31|(0|_k)<(0|bl))break f;if((0|a)==(0|(Vk=Vk+1|0)))break}for(Wk=q[Tk+64>>2],bl=q[Yk+264>>2],cl=q[Yk+260>>2],Vk=0;;){if(_k=q[(Zk=Vk<<2)+bl>>2]){if((0|_k)<0|(0|Wk)<(0|_k))break f;if((0|(Zk=q[Zk+cl>>2]))<0|(0|Wk)<=(0|Zk))break f;if((_k=_k+Zk|0)>>>31|(0|Wk)<(0|_k))break f}if((0|a)==(0|(Vk=Vk+1|0)))break}for(_k=Tk+68|0,Wk=q[Tk+68>>2],cl=q[Yk+272>>2],dl=q[Yk+268>>2],Vk=0;;){if(Zk=q[(bl=Vk<<2)+cl>>2]){if((0|Zk)<0|(0|Wk)<(0|Zk))break f;if((0|(bl=q[bl+dl>>2]))<0|(0|Wk)<=(0|bl))break f;if((Zk=Zk+bl|0)>>>31|(0|Wk)<(0|Zk))break f}if((0|a)==(0|(Vk=Vk+1|0)))break}}p:{q:{if(!(((a=0)|(Vk=q[Tk+20>>2]))<=0)){for(Wk=q[Yk+280>>2];;){if(63<ia(Wk+(a<<6)|0)>>>0)break f;if((0|Vk)==(0|(a=a+1|0)))break}if(!(((a=0)|(Vk=q[Tk+20>>2]))<=0)){for(Wk=q[Yk+296>>2];;){if(1<t[Wk+(a<<2)>>2])break f;if((0|Vk)==(0|(a=a+1|0)))break}for(Wk=q[Yk+300>>2],a=0;;){if(q[Wk+(a<<2)>>2]<0)break f;if((0|Vk)==(0|(a=a+1|0)))break}break q}}a=q[Tk+52>>2];break p}for(a=q[Tk+52>>2],cl=q[Yk+312>>2],dl=q[Yk+308>>2],Zk=0;;){if(Wk=q[(bl=Zk<<2)+cl>>2]){if((0|Wk)<0|(0|a)<(0|Wk))break f;if((0|(bl=q[bl+dl>>2]))<0|(0|a)<=(0|bl))break f;if((Wk=Wk+bl|0)>>>31|(0|a)<(0|Wk))break f}if((0|Vk)==(0|(Zk=Zk+1|0)))break}}if(Wk=q[Tk+40>>2],(Vk=0)<(0|(Zk=q[Tk+8>>2])))for(bl=q[Yk+344>>2],cl=q[Yk+156>>2];;){if((dl=q[(dl=Vk<<2)+bl>>2]+(q[cl+dl>>2]<<1)|0)>>>31|(0|Wk)<(0|dl))break f;if((0|Zk)==(0|(Vk=Vk+1|0)))break}if((Vk=0)<(0|(Zk=q[$k>>2]))){for(bl=q[Yk+376>>2];;){if(1<t[bl+(Vk<<2)>>2])break f;if((0|Zk)==(0|(Vk=Vk+1|0)))break}for(Vk=0,bl=q[Yk+380>>2];;){if(1<t[bl+(Vk<<2)>>2])break f;if((0|Zk)==(0|(Vk=Vk+1|0)))break}}if((Vk=0)<(0|(Zk=q[Xk>>2])))for(bl=q[Yk+400>>2],cl=q[Yk+252>>2];;){if((dl=q[(dl=Vk<<2)+bl>>2]+(q[cl+dl>>2]<<1)|0)>>>31|(0|Wk)<(0|dl))break f;if((0|Zk)==(0|(Vk=Vk+1|0)))break}if((Vk=0)<(0|(Wk=q[Tk+44>>2])))for(bl=q[Yk+424>>2];;){if((0|(cl=q[bl+(Vk<<2)>>2]))<0|(0|a)<=(0|cl))break f;if((0|Wk)==(0|(Vk=Vk+1|0)))break}if(1<=(0|(cl=q[Uk>>2])))for(Vk=0,dl=q[Yk+432>>2],al=q[Yk+428>>2];;){if(Uk=q[(bl=Vk<<2)+dl>>2]){if((0|Uk)<0|(0|Wk)<(0|Uk))break f;if((0|(bl=q[al+bl>>2]))<0|(0|Wk)<=(0|bl))break f;if((Uk=Uk+bl|0)>>>31|(0|Wk)<(0|Uk))break f}if((0|cl)==(0|(Vk=Vk+1|0)))break}if(1<=(0|a))for(Wk=q[Tk+56>>2],Vk=0,cl=q[Yk+420>>2],dl=q[Yk+416>>2];;){if(Uk=q[(bl=Vk<<2)+cl>>2]){if((0|Uk)<0|(0|Wk)<(0|Uk))break f;if((0|(bl=q[bl+dl>>2]))<0|(0|Wk)<=(0|bl))break f;if((Uk=Uk+bl|0)>>>31|(0|Wk)<(0|Uk))break f}if((0|(Vk=Vk+1|0))==(0|a))break}if((a=0)<(0|(Vk=q[_k>>2])))for(Wk=q[Yk+564>>2];;){if((0|(_k=q[Wk+(a<<2)>>2]))<-1|(0|Zk)<=(0|_k))break f;if((0|Vk)==(0|(a=a+1|0)))break}if(a=q[Tk+76>>2],1<=(0|(_k=q[Tk+72>>2])))for(Vk=0,Zk=q[Yk+572>>2],bl=q[Yk+568>>2];;){if(Wk=q[(Uk=Vk<<2)+Zk>>2]){if((0|Wk)<0|(0|a)<(0|Wk))break f;if((0|(Uk=q[Uk+bl>>2]))<0|(0|a)<=(0|Uk))break f;if((Wk=Uk+Wk|0)>>>31|(0|a)<(0|Wk))break f}if((0|_k)==(0|(Vk=Vk+1|0)))break}if((Vk=0)<(0|a)){for(Wk=q[Yk+588>>2];;){if(1<t[Wk+(Vk<<2)>>2])break f;if((0|a)==(0|(Vk=Vk+1|0)))break}for(Uk=q[Yk+592>>2],Vk=0;;){if(1<(bl=q[(Zk=Vk<<2)+Wk>>2])>>>0)break f;if((0|(Zk=q[Uk+Zk>>2]))<0|(0|Zk)>=q[(bl-1|0?Xk:Tk)>>2])break f;if((0|a)==(0|(Vk=Vk+1|0)))break}for(Vk=0,Wk=q[Yk+596>>2];;){if((0|(Uk=q[Wk+(Vk<<2)>>2]))<-1|(0|_k)<=(0|Uk))break f;if((0|a)==(0|(Vk=Vk+1|0)))break}}s:{if(!(((a=0)|(Zk=q[Tk+80>>2]))<=0)){for(Vk=q[Yk+604>>2];;){if(63<ia(Vk+(a<<6)|0)>>>0)break f;if((0|Zk)==(0|(a=a+1|0)))break}if(!(((a=0)|(Zk=q[Tk+80>>2]))<=0)){for(Vk=q[Tk+48>>2],Wk=q[Yk+608>>2];;){if((0|(_k=q[Wk+(a<<2)>>2]))<0|(0|Vk)<=(0|_k))break f;if((0|Zk)==(0|(a=a+1|0)))break}for(cl=q[Tk+88>>2],_k=q[Yk+616>>2],Uk=q[Yk+612>>2],a=0;;){if(Vk=q[(Wk=a<<2)+_k>>2]){if((0|Vk)<0|(0|cl)<(0|Vk))break f;if((0|(Wk=q[Uk+Wk>>2]))<0|(0|cl)<=(0|Wk))break f;if((Vk=Vk+Wk|0)>>>31|(0|cl)<(0|Vk))break f}if((0|Zk)==(0|(a=a+1|0)))break}for(Xk=q[Xk>>2],_k=q[Yk+620>>2],a=0;;){if((0|(Vk=q[_k+(a<<2)>>2]))<0|(0|Xk)<=(0|Vk))break f;if((0|Zk)==(0|(a=a+1|0)))break}for(Uk=q[Yk+624>>2],a=0;;){if((0|(Vk=q[Uk+(a<<2)>>2]))<0|(0|Xk)<=(0|Vk))break f;if((0|Zk)==(0|(a=a+1|0)))break}for(Vk=q[Tk+84>>2],bl=q[Yk+632>>2],dl=q[Yk+628>>2],a=0;;){if(Wk=q[(al=a<<2)+bl>>2]){if((0|Wk)<0|(0|Vk)<(0|Wk))break f;if((0|(al=q[al+dl>>2]))<0|(0|Vk)<=(0|al))break f;if((Wk=Wk+al|0)>>>31|(0|Vk)<(0|Wk))break f}if((0|Zk)==(0|(a=a+1|0)))break}for(fl=q[Yk+640>>2],Vk=q[Yk+252>>2],Wk=0;;){if(0<(0|(hl=q[(a=Wk<<2)+bl>>2])))for(al=fl+(q[a+dl>>2]<<1)|0,ml=q[Vk+(q[a+Uk>>2]<<2)>>2],il=q[Vk+(q[a+_k>>2]<<2)>>2],a=0;;){if((0|ml)<=s[al+(2|(kl=a<<1))>>1]|(0|il)<=s[al+kl>>1])break f;if(!((0|(a=a+2|0))<(0|hl)))break}if((0|Zk)==(0|(Wk=Wk+1|0)))break}break s}}Xk=q[Tk+16>>2],cl=q[Tk+88>>2]}if(!((255&gl)>>>0<2)){if((a=0)<(0|(bl=q[Tk+8>>2])))for(Vk=q[Yk+168>>2];;){if(1<t[Vk+(a<<2)>>2])break f;if((0|bl)==(0|(a=a+1|0)))break}if(!((255&gl)>>>0<4)){if(_k=q[Tk+56>>2],1<=(0|(dl=q[Tk+20>>2])))for(Uk=q[Yk+332>>2],al=q[Yk+328>>2],a=0;;){if(Vk=q[(Wk=a<<2)+Uk>>2]){if((0|Vk)<0|(0|_k)<(0|Vk))break f;if((0|(Wk=q[Wk+al>>2]))<0|(0|_k)<=(0|Wk))break f;if((Vk=Vk+Wk|0)>>>31|(0|_k)<(0|Vk))break f}if((0|dl)==(0|(a=a+1|0)))break}if((0|(a=q[Tk+92>>2]))!=q[Tk+96>>2])break f;if(1<=(0|bl))for(al=q[Yk+152>>2],Vk=0,fl=q[Yk+148>>2];;){if(Wk=q[(Uk=Vk<<2)+fl>>2]){if((0|Wk)<0|(0|a)<(0|Wk))break f;if((0|(Uk=q[Uk+al>>2]))<0|(0|a)<=(0|Uk))break f;if((Wk=Uk+Wk|0)>>>31|(0|a)<(0|Wk))break f}if((0|bl)==(0|(Vk=Vk+1|0)))break}if(1<=(0|(ml=q[Tk+12>>2])))for(al=q[Yk+184>>2],Vk=0,fl=q[Yk+180>>2];;){if(Wk=q[(Uk=Vk<<2)+fl>>2]){if((0|Wk)<0|(0|a)<(0|Wk))break f;if((0|(Uk=q[Uk+al>>2]))<0|(0|a)<=(0|Uk))break f;if((Wk=Uk+Wk|0)>>>31|(0|a)<(0|Wk))break f}if((0|ml)==(0|(Vk=Vk+1|0)))break}if(1<=(0|Xk))for(al=q[Yk+224>>2],Vk=0,fl=q[Yk+220>>2];;){if(Wk=q[(Uk=Vk<<2)+fl>>2]){if((0|Wk)<0|(0|a)<(0|Wk))break f;if((0|(Uk=q[Uk+al>>2]))<0|(0|a)<=(0|Uk))break f;if((Wk=Uk+Wk|0)>>>31|(0|a)<(0|Wk))break f}if((0|Xk)==(0|(Vk=Vk+1|0)))break}if((Vk=0)<(0|dl)){for(Wk=q[Yk+304>>2];;){if(1<t[Wk+(Vk<<2)>>2])break f;if((0|dl)==(0|(Vk=Vk+1|0)))break}for(Wk=q[Tk+100>>2],fl=q[Yk+320>>2],hl=q[Yk+316>>2],Vk=0;;){if(Uk=q[(al=Vk<<2)+fl>>2]){if((0|Uk)<0|(0|Wk)<(0|Uk))break f;if((0|(al=q[al+hl>>2]))<0|(0|Wk)<=(0|al))break f;if((Uk=Uk+al|0)>>>31|(0|Wk)<(0|Uk))break f}if((0|dl)==(0|(Vk=Vk+1|0)))break}}else Wk=q[Tk+100>>2];if(1<=(0|Wk)){for(al=q[Yk+440>>2],Vk=0,hl=q[Yk+436>>2];;){if(Uk=q[(fl=Vk<<2)+al>>2]){if((0|Uk)<0|(0|_k)<(0|Uk))break f;if((0|(fl=q[fl+hl>>2]))<0|(0|_k)<=(0|fl))break f;if((Uk=Uk+fl|0)>>>31|(0|_k)<(0|Uk))break f}if((0|Wk)==(0|(Vk=Vk+1|0)))break}for(_k=q[Yk+444>>2],Vk=0;;){if((0|(fl=q[(Uk=Vk<<2)+_k>>2]))<0|(0|fl)>=q[Uk+al>>2])break f;if((0|Wk)==(0|(Vk=Vk+1|0)))break}}if((_k=0)<(0|(Vk=q[Tk+104>>2]))){for(Uk=q[Yk+448>>2];;){if((0|(al=q[Uk+(_k<<2)>>2]))<0|(0|Wk)<=(0|al))break f;if((0|Vk)==(0|(_k=_k+1|0)))break}for(Uk=q[Tk+116>>2],fl=q[Yk+464>>2],hl=q[Yk+460>>2],Wk=0;;){if(_k=q[(al=Wk<<2)+fl>>2]){if((0|_k)<0|(0|Uk)<(0|_k))break f;if((0|(al=q[al+hl>>2]))<0|(0|Uk)<=(0|al))break f;if((_k=_k+al|0)>>>31|(0|Uk)<(0|_k))break f}if((0|Vk)==(0|(Wk=Wk+1|0)))break}}else Uk=q[Tk+116>>2];if((Wk=0)<(0|(al=q[Tk+108>>2]))){for(_k=q[Yk+480>>2];;){if((0|(fl=q[_k+(Wk<<2)>>2]))<0|(0|bl)<=(0|fl))break f;if((0|al)==(0|(Wk=Wk+1|0)))break}for(fl=q[Yk+488>>2],il=q[Yk+484>>2],Wk=0;;){if(_k=q[(bl=Wk<<2)+fl>>2]){if((0|_k)<0|(0|Vk)<(0|_k))break f;if((0|(bl=q[bl+il>>2]))<0|(0|Vk)<=(0|bl))break f;if((_k=_k+bl|0)>>>31|(0|Vk)<(0|_k))break f}if((0|al)==(0|(Wk=Wk+1|0)))break}for(fl=q[jl>>2],Wk=q[Yk+456>>2],bl=q[Yk+452>>2],_k=0;;){if(kl=q[il+(_k<<2)>>2]<<2,hl=q[kl+Wk>>2]){if((0|hl)<0|(0|fl)<(0|hl))break f;if((0|(kl=q[bl+kl>>2]))<0|(0|fl)<=(0|kl))break f;if((0|(hl=hl+kl|0))<0|(0|fl)<(0|hl))break f}if((0|al)==(0|(_k=_k+1|0)))break}}else Wk=q[Yk+456>>2],bl=q[Yk+452>>2];if((_k=0)<(0|(al=q[Tk+112>>2]))){for(fl=q[Yk+504>>2];;){if((0|(hl=q[fl+(_k<<2)>>2]))<0|(0|Xk)<=(0|hl))break f;if((0|al)==(0|(_k=_k+1|0)))break}for(il=q[Yk+512>>2],hl=q[Yk+508>>2],Xk=0;;){if(_k=q[(fl=Xk<<2)+il>>2]){if((0|_k)<0|(0|Vk)<(0|_k))break f;if((0|(fl=q[fl+hl>>2]))<0|(0|Vk)<=(0|fl))break f;if((_k=_k+fl|0)>>>31|(0|Vk)<(0|_k))break f}if((0|al)==(0|(Xk=Xk+1|0)))break}for(_k=q[ll>>2],Xk=0;;){if(il=q[hl+(Xk<<2)>>2]<<2,fl=q[il+Wk>>2]){if((0|fl)<0|(0|_k)<(0|fl))break f;if((0|(il=q[bl+il>>2]))<0|(0|_k)<=(0|il))break f;if((0|(fl=fl+il|0))<0|(0|_k)<(0|fl))break f}if((0|al)==(0|(Xk=Xk+1|0)))break}}if(_k=q[Tk+120>>2],(Xk=0)<(0|Uk))for(al=q[Yk+528>>2];;){if((0|(fl=q[al+(Xk<<2)>>2]))<0|(0|_k)<=(0|fl))break f;if((0|(Xk=Xk+1|0))==(0|Uk))break}if((Xk=0)<(0|_k)){for(Uk=q[Yk+532>>2];;){if((0|(al=q[Uk+(Xk<<2)>>2]))<-1|(0|dl)<=(0|al))break f;if((0|_k)==(0|(Xk=Xk+1|0)))break}for(Uk=q[Tk+124>>2],fl=q[Yk+540>>2],hl=q[Yk+536>>2],Xk=0;;){if(dl=q[(al=Xk<<2)+fl>>2]){if((0|dl)<0|(0|Uk)<(0|dl))break f;if((0|(al=q[al+hl>>2]))<0|(0|Uk)<=(0|al))break f;if((dl=al+dl|0)>>>31|(0|Uk)<(0|dl))break f}if((0|_k)==(0|(Xk=Xk+1|0)))break}}if(!((255&gl)>>>0<5)){if((Xk=0)<(0|(gl=q[jl>>2]))){for(_k=q[Yk+348>>2];;){if((0|(Uk=q[_k+(Xk<<2)>>2]))<0|(0|a)<(0|Uk))break f;if((0|gl)==(0|(Xk=Xk+1|0)))break}for(_k=q[Yk+352>>2],Xk=0;;){if((0|(Uk=q[_k+(Xk<<2)>>2]))<0|(0|a)<(0|Uk))break f;if((0|gl)==(0|(Xk=Xk+1|0)))break}}if((gl=0)<(0|(Xk=q[$k>>2]))){for(_k=q[Yk+384>>2];;){if((0|(Uk=q[_k+(gl<<2)>>2]))<0|(0|a)<(0|Uk))break f;if((0|Xk)==(0|(gl=gl+1|0)))break}for(_k=q[Yk+388>>2],gl=0;;){if((0|(Uk=q[_k+(gl<<2)>>2]))<0|(0|a)<(0|Uk))break f;if((0|Xk)==(0|(gl=gl+1|0)))break}}if((gl=0)<(0|(_k=q[ll>>2]))){for(Uk=q[Yk+404>>2];;){if((0|(dl=q[Uk+(gl<<2)>>2]))<0|(0|a)<(0|dl))break f;if((0|_k)==(0|(gl=gl+1|0)))break}for(Uk=q[Yk+408>>2],gl=0;;){if((0|(dl=q[Uk+(gl<<2)>>2]))<0|(0|a)<(0|dl))break f;if((0|_k)==(0|(gl=gl+1|0)))break}}if((a=0)<(0|(gl=q[Tk+128>>2]))){for(_k=q[Tk>>2],Uk=q[Yk+468>>2];;){if((0|(dl=q[Uk+(a<<2)>>2]))<0|(0|_k)<=(0|dl))break f;if((0|gl)==(0|(a=a+1|0)))break}for(jl=q[Yk+476>>2],dl=q[Yk+472>>2],a=0;;){if(_k=q[(Uk=a<<2)+jl>>2]){if((0|_k)<0|(0|Vk)<(0|_k))break f;if((0|(Uk=q[Uk+dl>>2]))<0|(0|Vk)<=(0|Uk))break f;if((_k=Uk+_k|0)>>>31|(0|Vk)<(0|_k))break f}if((0|gl)==(0|(a=a+1|0)))break}for(_k=q[el>>2],a=0;;){if(el=q[dl+(a<<2)>>2]<<2,Uk=q[el+Wk>>2]){if((0|Uk)<0|(0|_k)<(0|Uk))break f;if((0|(el=q[bl+el>>2]))<0|(0|_k)<=(0|el))break f;if((0|(Uk=Uk+el|0))<0|(0|_k)<(0|Uk))break f}if((0|gl)==(0|(a=a+1|0)))break}}if((a=0)<(0|(gl=q[Tk+132>>2]))){for(_k=q[Yk+492>>2];;){if((0|(Uk=q[_k+(a<<2)>>2]))<0|(0|ml)<=(0|Uk))break f;if((0|gl)==(0|(a=a+1|0)))break}for(el=q[Yk+500>>2],Uk=q[Yk+496>>2],a=0;;){if(_k=q[(dl=a<<2)+el>>2]){if((0|_k)<0|(0|Vk)<(0|_k))break f;if((0|(dl=q[Uk+dl>>2]))<0|(0|Vk)<=(0|dl))break f;if((_k=_k+dl|0)>>>31|(0|Vk)<(0|_k))break f}if((0|gl)==(0|(a=a+1|0)))break}for(a=0;;){if(dl=q[Uk+(a<<2)>>2]<<2,_k=q[dl+Wk>>2]){if((0|_k)<0|(0|Xk)<(0|_k))break f;if((0|(dl=q[bl+dl>>2]))<0|(0|Xk)<=(0|dl))break f;if((0|(_k=_k+dl|0))<0|(0|Xk)<(0|_k))break f}if((0|gl)==(0|(a=a+1|0)))break}}if(!(((a=0)|(Tk=q[Tk+136>>2]))<=0)){for(Xk=q[Yk+516>>2];;){if((0|(gl=q[Xk+(a<<2)>>2]))<0|(0|Zk)<=(0|gl))break f;if((0|Tk)==(0|(a=a+1|0)))break}for(_k=q[Yk+524>>2],Xk=q[Yk+520>>2],a=0;;){if(Yk=q[(gl=a<<2)+_k>>2]){if((0|Yk)<0|(0|Vk)<(0|Yk))break f;if((0|(gl=q[Xk+gl>>2]))<0|(0|Vk)<=(0|gl))break f;if((Yk=Yk+gl|0)>>>31|(0|Vk)<(0|Yk))break f}if((0|Tk)==(0|(a=a+1|0)))break}for(a=0;;){if(Yk=q[Xk+(a<<2)>>2]<<2,Vk=q[Yk+Wk>>2]){if((0|Vk)<0|(0|cl)<(0|Vk))break f;if((0|(Yk=q[Yk+bl>>2]))<0|(0|cl)<=(0|Yk))break f;if((0|(Vk=Vk+Yk|0))<0|(0|cl)<(0|Vk))break f}if((0|Tk)==(0|(a=a+1|0)))break}}}}}return L=nl,1}return Y(4,1846,0),L=nl,0}q[Yk+52>>2]=Vk,q[Yk+48>>2]=5,Y(4,1640,Yk+48|0)}else q[Yk+32>>2]=Vk,Y(4,1554,Yk+32|0);return L=nl,0}(a,tj):(q[20+uj>>2]=1621,q[16+uj>>2]=2284,Y(4,1294,16+uj|0),0):(q[4+uj>>2]=1444,q[uj>>2]=2284,Y(4,1294,uj),0),L=48+uj|0,0|a},j:function(a){q[1813]=a|=0},k:function(a,cj){var dj;return cj|=0,L=dj=L-48|0,a=(a|=0)?(a+63&-64)!=(0|a)?(q[36+dj>>2]=1522,q[32+dj>>2]=2305,Y(4,1294,32+dj|0),0):(cj+63&-64)==(0|cj)&&cj?function(a){var Ok,Rk,Sk,Rh,Ik=0,Jk=0,Kk=0,Lk=0,Mk=0,Nk=0,Pk=0,Qk=0;q[24+(L=Ok=L-32|0)>>2]=0,q[16+Ok>>2]=5,q[20+Ok>>2]=1,Ka(16+(L=Rh=L-272|0)|0,2227,q[12+Rh>>2]=16+Ok|0),function(a){var Dc;q[(L=Dc=L-16|0)>>2]=a,function(a,Gl){var Hl;q[12+(L=Hl=L-16|0)>>2]=Gl,Ia(a,1432,Gl,0,0),L=16+Hl|0}(q[978],Dc),L=16+Dc|0}(16+Rh|0),L=272+Rh|0;a:{if(sa(a))Y(4,1932,0);else{if(!(6<=(Kk=r[a+4|0])>>>0)){if(1!=(0|!r[a+5|0])?(da(a+4|0,1),X(a- -64|0,4,160),na(a,a+704|(o[a+5|0]=0)),ya(a)):na(a,a+704|0),r[7256]||(q[1815]=6,o[7256]=1,q[1816]=7,q[1817]=8,q[1818]=9),Jk=q[a+704>>2],1<=(0|(Kk=q[Jk+16>>2]))){for(Qk=(Lk=q[a+912>>2])+(Kk<<2)|0,Mk=q[a+908>>2];;){Pk=q[a+1204>>2]+(q[Mk>>2]<<2)|0;d:if(!(((Jk=0)|(Ik=(Kk=q[Lk>>2])+-1|0))<1))e:for(;;){for(;;){if(q[(Nk=Pk+(Jk<<2)|0)>>2]<=-1){if(function(a,Tk,ol){var pl=0,ql=0;a:if((0|a)!=(0|Tk)){if(!(a>>>0<Tk+ol>>>0&&Tk>>>0<(ql=a+ol|0)>>>0))return $(a,Tk,ol);if(pl=3&(a^Tk),a>>>0<Tk>>>0){if(!pl){if(3&a)for(;;){if(!ol)break a;if(o[0|a]=r[0|Tk],Tk=Tk+1|0,ol=ol+-1|0,!(3&(a=a+1|0)))break}if(!(ol>>>0<=3)){for(pl=ol;q[a>>2]=q[Tk>>2],Tk=Tk+4|0,a=a+4|0,3<(pl=pl+-4|0)>>>0;);ol&=3}}if(ol)for(;o[0|a]=r[0|Tk],a=a+1|0,Tk=Tk+1|0,ol=ol+-1|0;);}else{if(!pl){if(3&ql)for(;;){if(!ol)break a;if(o[0|(pl=(ol=ol+-1|0)+a|0)]=r[Tk+ol|0],!(3&pl))break}if(!(ol>>>0<=3))for(;q[(ol=ol+-4|0)+a>>2]=q[Tk+ol>>2],3<ol>>>0;);}if(ol)for(;o[(ol=ol+-1|0)+a|0]=r[Tk+ol|0],ol;);}}}(Nk,Nk+4|0,(-1^Jk)+Kk<<2),(0|Jk)<(0|(Ik=(Kk=Ik)+-1|0)))continue e;break d}if(!((0|(Jk=Jk+1|0))<(0|Ik)))break}break}if(Jk=Lk,0<(0|Kk)&&(Kk=q[Pk+(Ik<<2)>>2]<0?Ik:Kk),q[Jk>>2]=Kk,Mk=Mk+4|0,!((Lk=Lk+4|0)>>>0<Qk>>>0))break}Jk=q[a+704>>2]}if(1<=q[Jk>>2])for(Ik=0;q[q[a+712>>2]+(Ik<<2)>>2]=q[a+716>>2]+(Ik<<6),Jk=q[a+704>>2],(0|(Ik=Ik+1|0))<q[Jk>>2];);if(1<=q[Jk+4>>2])for(Ik=0;q[q[a+744>>2]+(Ik<<2)>>2]=q[a+748>>2]+(Ik<<6),Jk=q[a+704>>2],(0|(Ik=Ik+1|0))<q[Jk+4>>2];);if(1<=q[Jk+16>>2])for(Ik=0;q[(Kk=Ik<<2)+q[a+832>>2]>>2]=q[a+848>>2]+(Ik<<6),q[Kk+q[a+836>>2]>>2]=q[a+1196>>2]+(q[Kk+q[a+896>>2]>>2]<<2),q[Kk+q[a+840>>2]>>2]=q[a+1200>>2]+(q[Kk+q[a+900>>2]>>2]<<1),q[Kk+q[a+844>>2]>>2]=q[a+1204>>2]+(q[Kk+q[a+908>>2]>>2]<<2),Jk=q[a+704>>2],(0|(Ik=Ik+1|0))<q[Jk+16>>2];);if(1<=q[Jk+20>>2])for(Ik=0;q[q[a+916>>2]+(Ik<<2)>>2]=q[a+920>>2]+(Ik<<6),Jk=q[a+704>>2],(0|(Ik=Ik+1|0))<q[Jk+20>>2];);if(1<=q[Jk+80>>2])for(Ik=0;q[q[a+1240>>2]+(Ik<<2)>>2]=q[a+1244>>2]+(Ik<<6),Jk=q[a+704>>2],(0|(Ik=Ik+1|0))<q[Jk+80>>2];);if(1&o[q[a+708>>2]+20|0])break a;if((0|(Lk=q[Jk+16>>2]))<1)break a;for(Ik=q[a+904>>2],Pk=q[a+900>>2],Nk=q[a+1200>>2],Mk=0;;){if(0<(0|(Qk=q[(Kk=Mk<<2)+Ik>>2]+-1|0)))for(Rk=Nk+(q[Kk+Pk>>2]<<1)|0,Jk=0;Sk=s[(Kk=Rk+(Jk<<1)|0)>>1],p[Kk>>1]=s[Kk+4>>1],p[Kk+4>>1]=Sk,(0|(Jk=Jk+3|0))<(0|Qk););if((0|Lk)==(0|(Mk=Mk+1|0)))break}for(Kk=q[a+892>>2],Mk=q[a+896>>2],Pk=q[a+1196>>2],Ik=0;;){if(1<=(0|(Nk=q[(Jk=Ik<<2)+Kk>>2])))for(Nk=(Jk=Pk+(q[Jk+Mk>>2]<<2)|0)+(Nk<<3)|0,Jk=Jk+4|0;u[Jk>>2]=x(1)-u[Jk>>2],(Jk=Jk+8|0)>>>0<Nk>>>0;);if((0|Lk)==(0|(Ik=Ik+1|0)))break}break a}q[4+Ok>>2]=Kk,q[Ok>>2]=5,Y(4,2023,Ok)}a=0}return L=32+Ok|0,a}(a):(q[20+dj>>2]=1621,q[16+dj>>2]=2305,Y(4,1294,16+dj|0),0):(q[4+dj>>2]=1444,q[dj>>2]=2305,Y(4,1294,dj),0),L=48+dj|0,0|a},l:function(a,cj,dj,ej){var fj;cj|=0,dj|=0,ej|=0,L=fj=L+-64|0,(a|=0)?cj?dj?ej?(a=q[q[a>>2]+708>>2],q[cj>>2]=q[a+12>>2],q[cj+4>>2]=q[a+16>>2],q[dj>>2]=q[a+4>>2],q[dj+4>>2]=q[a+8>>2],q[ej>>2]=q[a>>2]):(q[52+fj>>2]=1995,q[48+fj>>2]=2325,Y(4,1294,48+fj|0)):(q[36+fj>>2]=1903,q[32+fj>>2]=2325,Y(4,1294,32+fj|0)):(q[20+fj>>2]=1819,q[16+fj>>2]=2325,Y(4,1294,16+fj|0)):(q[4+fj>>2]=1740,q[fj>>2]=2325,Y(4,1294,fj)),L=64+fj|0},m:xa,n:wa,o:function(a){var bj;L=bj=L-16|0,(a|=0)?ua(a):(q[4+bj>>2]=1740,q[bj>>2]=2387,Y(4,1294,bj)),L=16+bj|0},p:function(a){var aj;return L=aj=L-16|0,a=(a|=0)?q[a+540>>2]:(q[4+aj>>2]=1740,q[aj>>2]=2402,Y(4,1294,aj),-1),L=16+aj|0,0|a},q:function(a){var $i;return L=$i=L-16|0,a=(a|=0)?q[q[a>>2]+916>>2]:(q[4+$i>>2]=1740,q[$i>>2]=2423,Y(4,1294,$i),0),L=16+$i|0,0|a},r:function(a){var _i;return L=_i=L-16|0,a=(a|=0)?q[a+548>>2]:(q[4+_i>>2]=1740,q[_i>>2]=2442,Y(4,1294,_i),0),L=16+_i|0,0|a},s:function(a){var Zi;return L=Zi=L-16|0,a=(a|=0)?q[q[a>>2]+928>>2]:(q[4+Zi>>2]=1740,q[Zi>>2]=2463,Y(4,1294,Zi),0),L=16+Zi|0,0|a},t:function(a){var Yi;return L=Yi=L-16|0,a=(a|=0)?q[q[a>>2]+924>>2]:(q[4+Yi>>2]=1740,q[Yi>>2]=2492,Y(4,1294,Yi),0),L=16+Yi|0,0|a},u:function(a){var Xi;return L=Xi=L-16|0,a=(a|=0)?q[q[a>>2]+932>>2]:(q[4+Xi>>2]=1740,q[Xi>>2]=2521,Y(4,1294,Xi),0),L=16+Xi|0,0|a},v:function(a){var Wi;return L=Wi=L-16|0,a=(a|=0)?q[a+552>>2]:(q[4+Wi>>2]=1740,q[Wi>>2]=2550,Y(4,1294,Wi),0),L=16+Wi|0,0|a},w:function(a){var Vi;return L=Vi=L-16|0,a=(a|=0)?q[q[a>>2]+936>>2]:(q[4+Vi>>2]=1740,q[Vi>>2]=2572,Y(4,1294,Vi),0),L=16+Vi|0,0|a},x:function(a){var Ui;return L=Ui=L-16|0,a=(a|=0)?q[a+4>>2]:(q[4+Ui>>2]=1740,q[Ui>>2]=2595,Y(4,1294,Ui),-1),L=16+Ui|0,0|a},y:function(a){var Ti;return L=Ti=L-16|0,a=(a|=0)?q[q[a>>2]+712>>2]:(q[4+Ti>>2]=1740,q[Ti>>2]=2611,Y(4,1294,Ti),0),L=16+Ti|0,0|a},z:function(a){var Si;return L=Si=L-16|0,a=(a|=0)?q[a+52>>2]:(q[4+Si>>2]=1740,q[Si>>2]=2625,Y(4,1294,Si),0),L=16+Si|0,0|a},A:function(a){var Ri;return L=Ri=L-16|0,a=(a|=0)?q[q[a>>2]+740>>2]:(q[4+Ri>>2]=1740,q[Ri>>2]=2645,Y(4,1294,Ri),0),L=16+Ri|0,0|a},B:function(a){var Qi;return L=Qi=L-16|0,a=(a|=0)?q[a+332>>2]:(q[4+Qi>>2]=1740,q[Qi>>2]=2673,Y(4,1294,Qi),-1),L=16+Qi|0,0|a},C:function(a){var Pi;return L=Pi=L-16|0,a=(a|=0)?q[q[a>>2]+832>>2]:(q[4+Pi>>2]=1740,q[Pi>>2]=2693,Y(4,1294,Pi),0),L=16+Pi|0,0|a},D:function(a){var Oi;return L=Oi=L-16|0,a=(a|=0)?q[q[a>>2]+888>>2]:(q[4+Oi>>2]=1740,q[Oi>>2]=2711,Y(4,1294,Oi),0),L=16+Oi|0,0|a},E:function(a){var Ni;return L=Ni=L-16|0,a=(a|=0)?q[a+432>>2]:(q[4+Ni>>2]=1740,q[Ni>>2]=2739,Y(4,1294,Ni),0),L=16+Ni|0,0|a},F:function(a){var Mi;return L=Mi=L-16|0,a=(a|=0)?q[q[a>>2]+884>>2]:(q[4+Mi>>2]=1740,q[Mi>>2]=2766,Y(4,1294,Mi),0),L=16+Mi|0,0|a},G:function(a){var Li;return L=Li=L-16|0,a=(a|=0)?q[a+440>>2]:(q[4+Li>>2]=1740,q[Li>>2]=2795,Y(4,1294,Li),0),L=16+Li|0,0|a},H:function(a){var Ki;return L=Ki=L-16|0,a=(a|=0)?q[a+436>>2]:(q[4+Ki>>2]=1740,q[Ki>>2]=2820,Y(4,1294,Ki),0),L=16+Ki|0,0|a},I:function(a){var Ji;return L=Ji=L-16|0,a=(a|=0)?q[a+448>>2]:(q[4+Ji>>2]=1740,q[Ji>>2]=2847,Y(4,1294,Ji),0),L=16+Ji|0,0|a},J:function(a){var Ii;return L=Ii=L-16|0,a=(a|=0)?q[q[a>>2]+912>>2]:(q[4+Ii>>2]=1740,q[Ii>>2]=2871,Y(4,1294,Ii),0),L=16+Ii|0,0|a},K:function(a){var Hi;return L=Hi=L-16|0,a=(a|=0)?q[q[a>>2]+844>>2]:(q[4+Hi>>2]=1740,q[Hi>>2]=2896,Y(4,1294,Hi),0),L=16+Hi|0,0|a},L:function(a){var Gi;return L=Gi=L-16|0,a=(a|=0)?q[q[a>>2]+892>>2]:(q[4+Gi>>2]=1740,q[Gi>>2]=2916,Y(4,1294,Gi),0),L=16+Gi|0,0|a},M:function(a){var qi;return L=qi=L-16|0,a=(a|=0)?q[a+444>>2]:(q[4+qi>>2]=1740,q[qi>>2]=2943,Y(4,1294,qi),0),L=16+qi|0,0|a},N:function(a){var pi;return L=pi=L-16|0,a=(a|=0)?q[q[a>>2]+836>>2]:(q[4+pi>>2]=1740,q[pi>>2]=2973,Y(4,1294,pi),0),L=16+pi|0,0|a},O:function(a){var oi;return L=oi=L-16|0,a=(a|=0)?q[q[a>>2]+904>>2]:(q[4+oi>>2]=1740,q[oi>>2]=2997,Y(4,1294,oi),0),L=16+oi|0,0|a},P:function(a){var ni;return L=ni=L-16|0,a=(a|=0)?q[q[a>>2]+840>>2]:(q[4+ni>>2]=1740,q[ni>>2]=3023,Y(4,1294,ni),0),L=16+ni|0,0|a},Q:function(a){var mi;return L=mi=L-16|0,a=(a|=0)?q[a+452>>2]:(q[4+mi>>2]=1740,q[mi>>2]=3045,Y(4,1294,mi),0),L=16+mi|0,0|a},R:function(a){var li;return L=li=L-16|0,a=(a|=0)?q[a+456>>2]:(q[4+li>>2]=1740,q[li>>2]=3074,Y(4,1294,li),0),L=16+li|0,0|a},S:function(a){var ki;return L=ki=L-16|0,a=(a|=0)?q[q[a>>2]+876>>2]:(q[4+ki>>2]=1740,q[ki>>2]=3101,Y(4,1294,ki),0),L=16+ki|0,0|a},T:function(a){var ji;L=ji=L-16|0,(a|=0)?q[a+428>>2]=1:(q[4+ji>>2]=1740,q[ji>>2]=3133,Y(4,1294,ji)),L=16+ji|0},U:function(a){var ii;return L=ii=L-16|0,a=(a|=0)?q[a+640>>2]:(q[4+ii>>2]=1740,q[ii>>2]=3162,Y(4,1294,ii),0),L=16+ii|0,0|a},V:function(a){var hi;return L=hi=L-16|0,a=(a|=0)?q[a+636>>2]:(q[4+hi>>2]=1740,q[hi>>2]=3187,Y(4,1294,hi),0),L=16+hi|0,0|a},W:function(a){var Cc;return oa(12+(L=Cc=L-16|0)|0,64,a|=0),L=16+Cc|0,q[12+Cc>>2]},X:function(a){var Ln,Jn,Kn=0;return L=Jn=L-16|0,!(a|=0)||oa(12+Jn|0,16,Ln=xa(a))||(Kn=wa(a,q[12+Jn>>2],Ln))||(pa(q[12+Jn>>2]),Kn=0),L=16+Jn|0,0|Kn},Y:function(a){return 0|qa(a|=0)},Z:function(a){pa(a|=0)},_:function(a){var rn;oa(12+(L=rn=L-16|0)|0,64,a|=0),pa(q[12+rn>>2]),L=16+rn|0},$:function(){return 0|L},aa:function(a){return 0|(L=L-(0|a)&-16)},ba:function(a){L=0|a},ca:function(a){return 0|(a=0|(a|=0),(P=0|N())<(a=P+(a|=0)|0)&&a<65536&&(a=new ArrayBuffer(w(a,65536)),(S=new global.Int8Array(a)).set(o),o=S,o=new global.Int8Array(a),p=new global.Int16Array(a),q=new global.Int32Array(a),r=new global.Uint8Array(a),s=new global.Uint16Array(a),t=new global.Uint32Array(a),u=new global.Float32Array(a),v=new global.Float64Array(a),buffer=a,m.buffer=a),P);var S,P},da:function(a,Tk){n[a|=0](Tk|=0)}};function X(a,b,c){var e,f,d=0;if(c)for(;;){if(c=c+-1|0,a>>>0<(d=(e=a+b|0)-1|0)>>>0)for(;f=r[0|a],o[0|a]=r[0|d],o[0|d]=f,(a=a+1|0)>>>0<(d=d+-1|0)>>>0;);if(a=e,!c)break}}function Y(a,b,c){var g;L=g=L-272|0,t[1812]>a>>>0||(a=q[1813])&&(Ka(16+g|0,b,q[12+g>>2]=c),n[a](16+g|0)),L=272+g|0}function Z(a,b,c){32&r[0|a]||!function(a,Gl,Pm){var Qm=0,Rm=0,Sm=0;a:{if(!(Qm=q[Pm+16>>2])){if(function(a){var Pm;return Pm=r[a+74|0],o[a+74|0]=Pm+-1|Pm,8&(Pm=q[a>>2])?(q[a>>2]=32|Pm,1):(q[a+4>>2]=0,q[a+8>>2]=0,Pm=q[a+44>>2],q[a+28>>2]=Pm,q[a+20>>2]=Pm,q[a+16>>2]=Pm+q[a+48>>2],0)}(Pm))break a;Qm=q[Pm+16>>2]}if(Qm-(Sm=q[Pm+20>>2])>>>0<Gl>>>0)return n[q[Pm+36>>2]](Pm,a,Gl);b:if(!(o[Pm+75|0]<0)){for(Qm=Gl;;){if(!(Rm=Qm))break b;if(10==r[(Qm=Rm+-1|0)+a|0])break}if(n[q[Pm+36>>2]](Pm,a,Rm)>>>0<Rm>>>0)break a;Gl=Gl-Rm|0,a=a+Rm|0,Sm=q[Pm+20>>2]}$(Sm,a,Gl),q[Pm+20>>2]=q[Pm+20>>2]+Gl}}(b,c,a)}function _(a,b,c,h,i){var k,l,j;if(L=j=L-256|0,!(73728&i|(0|c)<=(0|h))){if(ca(j,b,(k=(i=c-h|0)>>>0<256)?i:256),b=a,l=j,!k){for(c=c-h|0;Z(a,j,256),255<(i=i+-256|0)>>>0;);i=255&c}Z(b,l,i)}L=256+j|0}function $(a,b,c){var h,i=0;if(8192<=c>>>0)I(0|a,0|b,0|c);else{if(h=a+c|0,3&(a^b))if(h>>>0<4)c=a;else if((i=h-4|0)>>>0<a>>>0)c=a;else for(c=a;o[0|c]=r[0|b],o[c+1|0]=r[b+1|0],o[c+2|0]=r[b+2|0],o[c+3|0]=r[b+3|0],b=b+4|0,(c=c+4|0)>>>0<=i>>>0;);else{b:if((0|c)<1)c=a;else if(3&a)for(c=a;;){if(o[0|c]=r[0|b],b=b+1|0,h>>>0<=(c=c+1|0)>>>0)break b;if(!(3&c))break}else c=a;if(!((a=-4&h)>>>0<64||(i=a+-64|0)>>>0<c>>>0))for(;q[c>>2]=q[b>>2],q[c+4>>2]=q[b+4>>2],q[c+8>>2]=q[b+8>>2],q[c+12>>2]=q[b+12>>2],q[c+16>>2]=q[b+16>>2],q[c+20>>2]=q[b+20>>2],q[c+24>>2]=q[b+24>>2],q[c+28>>2]=q[b+28>>2],q[c+32>>2]=q[b+32>>2],q[c+36>>2]=q[b+36>>2],q[c+40>>2]=q[b+40>>2],q[c+44>>2]=q[b+44>>2],q[c+48>>2]=q[b+48>>2],q[c+52>>2]=q[b+52>>2],q[c+56>>2]=q[b+56>>2],q[c+60>>2]=q[b+60>>2],b=b- -64|0,(c=c- -64|0)>>>0<=i>>>0;);if(!(a>>>0<=c>>>0))for(;q[c>>2]=q[b>>2],b=b+4|0,(c=c+4|0)>>>0<a>>>0;);}if(c>>>0<h>>>0)for(;o[0|c]=r[0|b],b=b+1|0,(0|h)!=(0|(c=c+1|0)););}}function aa(a){var b,c;return x((b=a*a)*b*(c=b*a)*(2718311493989822e-21*b-.00019839334836096632)+(c*(.008333329385889463*b-.16666666641626524)+a))}function ba(a){var m;return x(-.499999997251031*(a*=a)+1+.04166662332373906*(m=a*a)+a*m*(2439044879627741e-20*a-.001388676377460993))}function ca(a,n,p){var r,s,t,u;if(p&&(o[(r=a+p|0)-1|0]=n,o[0|a]=n,!(p>>>0<3||(o[r-2|0]=n,o[a+1|0]=n,o[r-3|0]=n,o[a+2|0]=n,p>>>0<7)||(o[r-4|0]=n,o[a+3|0]=n,p>>>0<9)||(s=(r=0-a&3)+a|0,n=w(255&n,16843009),q[s>>2]=n,q[(r=(p=p-r&-4)+s|0)-4>>2]=n,p>>>0<9)||(q[8+s>>2]=n,q[4+s>>2]=n,q[r-8>>2]=n,q[r-12>>2]=n,p>>>0<25)||(q[24+s>>2]=n,q[20+s>>2]=n,q[16+s>>2]=n,q[12+s>>2]=n,q[r-16>>2]=n,q[r-20>>2]=n,q[r-24>>2]=n,q[r-28>>2]=n,(p=p-(u=4&s|24)|0)>>>0<32))))for(t=r=n,n=s+u|0;q[n+24>>2]=t,q[n+28>>2]=r,q[n+16>>2]=t,q[n+20>>2]=r,q[n+8>>2]=t,q[n+12>>2]=r,q[n>>2]=t,q[n+4>>2]=r,n=n+32|0,31<(p=p+-32|0)>>>0;);return a}function da(a,n){var p;if(a>>>0<(n=(a+n|0)-1|0)>>>0)for(;p=r[0|a],o[0|a]=r[0|n],o[0|n]=p,(a=a+1|0)>>>0<(n=n+-1|0)>>>0;);}function ea(a){var n,o=N();return(a=(n=q[2224])+a|0)>>>0<=o<<16>>>0||J(0|a)?(q[2224]=a,n):(q[2094]=48,-1)}function fa(a,v,y,z,B,C,D){var H,I,K,N,Q,R,S,O,P,J,E=0,F=x(0),G=x(0),M=x(0);if(x(0),x(0),x(0),x(0),L=J=L-16|0,1<=(0|a))for(R=w(a,12)+v|0;;){if(1<=(0|(I=q[v+4>>2])))for(S=(a=q[v+8>>2])+w(I,48)|0,I=(H=q[v>>2]<<4)+D|0,K=(8|H)+D|0,H=(4|H)+D|0;(E=q[a+8>>2])&&((O=E+-1|0)>>>0<=1?(P=(q[a+4>>2]<<2)+y|0,E=q[P+(q[a+12>>2]<<2)>>2]<<2,F=u[E+C>>2],Q=u[B+E>>2],G=u[z+E>>2],O-1?(M=G,G=u[a+20>>2],u[I>>2]=u[I>>2]+x(u[a+44>>2]*x(M*G)),u[H>>2]=u[H>>2]+x(x(Q*G)*u[a+44>>2]),u[K>>2]=u[K>>2]+x(x(F*G)*u[a+44>>2])):(E=q[(q[a+16>>2]<<2)+P>>2]<<2,O=u[E+C>>2],P=u[B+E>>2],M=G,G=u[a+20>>2],N=u[a+24>>2],u[I>>2]=u[I>>2]+x(u[a+44>>2]*x(x(M*G)+x(u[z+E>>2]*N))),u[H>>2]=u[H>>2]+x(x(x(Q*G)+x(P*N))*u[a+44>>2]),u[K>>2]=u[K>>2]+x(x(x(F*G)+x(O*N))*u[a+44>>2]))):(q[J>>2]=E,Y(4,1024,J))),(a=a+48|0)>>>0<S>>>0;);if(a=(q[v>>2]<<4)+D|0,F=u[a>>2],u[a>>2]=F<x(0)?x(0):x(A(F,x(1))),F=u[a+4>>2],u[a+4>>2]=F<x(0)?x(0):x(A(F,x(1))),F=u[a+8>>2],u[a+8>>2]=F<x(0)?x(0):x(A(F,x(1))),!((v=v+12|0)>>>0<R>>>0))break}L=16+J|0}function ga(a,q,v){var y,z,x=0;if(1==(0|q)&a>>>0<0|q>>>0<1)x=a;else for(;y=cd(x=dd(a,q,10),z=M,10),o[0|(v=v+-1|0)]=a-y|48,y=9==(0|q)&4294967295<a>>>0|9<q>>>0,a=x,q=z,y;);if(x)for(;o[0|(v=v+-1|0)]=x-w(a=(x>>>0)/10|0,10)|48,q=9<x>>>0,x=a,q;);return v}function ha(a){return a+-48>>>0<10}function ia(a){var q;return(q=La(a,64))?q-a|0:64}function ja(a,v){var w=0;return 1024<=(0|v)?(a*=898846567431158e293,v=(0|(w=v+-1023|0))<1024?w:(a*=898846567431158e293,((0|v)<3069?v:3069)+-2046|0)):-1023<(0|v)||(a*=22250738585072014e-324,v=-1023<(0|(w=v+1022|0))?w:(a*=22250738585072014e-324,(-3066<(0|v)?v:-3066)+2044|0)),f(0,0),f(1,v+1023<<20),a*+g()}function ka(a,v){var A=0,C=a,B=v>>>0<=31?(A=q[a+4>>2],q[a>>2]):(A=q[a>>2],q[a+4>>2]=A,v=v+-32|(q[a>>2]=0),0);q[C>>2]=B<<v,q[a+4>>2]=A<<v|B>>>32-v}function la(a,v,D,V,W){var X,Y=0,Z=0,_=0;L=X=L-240|0,Y=q[v>>2],q[232+X>>2]=Y,v=q[v+4>>2],q[X>>2]=a,Z=1;a:{b:{c:{if(((q[236+X>>2]=v)||1!=(0|Y))&&(Y=a-q[(D<<2)+W>>2]|0,!((0|n[5](Y,a))<1))){for(_=!V;;){e:{if(v=Y,!(!_|(0|D)<2)){if(V=q[((D<<2)+W|0)-8>>2],-1<(0|n[5](Y=a+-4|0,v)))break e;if(-1<(0|n[5](Y-V|0,v)))break e}if(q[(Z<<2)+X>>2]=v,Z=Z+1|0,ma(232+X|0,a=Oa(232+X|0)),D=a+D|0,!q[236+X>>2]&&1==q[232+X>>2])break b;if(_=1,Y=(a=v)-q[(D<<2)+W>>2]|(V=0),0<(0|n[5](Y,q[X>>2])))continue;break c}break}v=a;break b}v=a}if(V)break a}Na(X,Z),ta(v,D,W)}L=240+X|0}function ma(a,v){var D=0,V=a,L=v>>>0<=31?(D=q[a>>2],q[a+4>>2]):(D=q[a+4>>2],q[a+4>>2]=0,q[a>>2]=D,v=v+-32|0,0);q[V+4>>2]=L>>>v,q[a>>2]=L<<32-v|D>>>v}function na(a,v){var W=r[a+4|0];q[v>>2]=q[a+64>>2]+a,q[v+4>>2]=q[a+68>>2]+a,q[v+8>>2]=q[a+72>>2]+a,q[v+12>>2]=q[a+76>>2]+a,q[v+16>>2]=q[a+80>>2]+a,q[v+20>>2]=q[a+84>>2]+a,q[v+24>>2]=q[a+88>>2]+a,q[v+28>>2]=q[a+92>>2]+a,q[v+32>>2]=q[a+96>>2]+a,q[v+36>>2]=q[a+100>>2]+a,q[v+40>>2]=q[a+104>>2]+a,q[v+44>>2]=q[a+108>>2]+a,q[v+48>>2]=q[a+112>>2]+a,q[v+52>>2]=q[a+116>>2]+a,q[v+56>>2]=q[a+120>>2]+a,q[v+60>>2]=q[a+124>>2]+a,q[v- -64>>2]=q[a+128>>2]+a,q[v+68>>2]=q[a+132>>2]+a,q[v+72>>2]=q[a+136>>2]+a,q[v+76>>2]=q[a+140>>2]+a,q[v+80>>2]=q[a+144>>2]+a,q[v+84>>2]=q[a+148>>2]+a,q[v+92>>2]=q[a+152>>2]+a,q[v+96>>2]=q[a+156>>2]+a,q[v+100>>2]=q[a+160>>2]+a,q[v+108>>2]=q[a+164>>2]+a,q[v+112>>2]=q[a+168>>2]+a,q[v+116>>2]=q[a+172>>2]+a,q[v+124>>2]=q[a+176>>2]+a,q[v+128>>2]=q[a+180>>2]+a,q[v+132>>2]=q[a+184>>2]+a,q[v+136>>2]=q[a+188>>2]+a,q[v+140>>2]=q[a+192>>2]+a,q[v+144>>2]=q[a+196>>2]+a,q[v+148>>2]=q[a+200>>2]+a,q[v+152>>2]=q[a+204>>2]+a,q[v+156>>2]=q[a+208>>2]+a,q[v+164>>2]=q[a+212>>2]+a,q[v+168>>2]=q[a+216>>2]+a,q[v+172>>2]=q[a+220>>2]+a,q[v+176>>2]=q[a+224>>2]+a,q[v+180>>2]=q[a+228>>2]+a,q[v+184>>2]=q[a+232>>2]+a,q[v+188>>2]=q[a+236>>2]+a,q[v+192>>2]=q[a+240>>2]+a,q[v+196>>2]=q[a+244>>2]+a,q[v+200>>2]=q[a+248>>2]+a,q[v+204>>2]=q[a+252>>2]+a,q[v+208>>2]=q[a+256>>2]+a,q[v+212>>2]=q[a+260>>2]+a,q[v+216>>2]=q[a+264>>2]+a,q[v+220>>2]=q[a+268>>2]+a,q[v+224>>2]=q[a+272>>2]+a,q[v+228>>2]=q[a+276>>2]+a,q[v+232>>2]=q[a+280>>2]+a,q[v+236>>2]=q[a+284>>2]+a,q[v+244>>2]=q[a+288>>2]+a,q[v+248>>2]=q[a+292>>2]+a,q[v+272>>2]=q[a+296>>2]+a,q[v+276>>2]=q[a+300>>2]+a,q[v+280>>2]=q[a+304>>2]+a,q[v+292>>2]=q[a+308>>2]+a,q[v+296>>2]=q[a+312>>2]+a,q[v+300>>2]=q[a+316>>2]+a,q[v+304>>2]=q[a+320>>2]+a,q[v+308>>2]=q[a+324>>2]+a,q[v+312>>2]=q[a+328>>2]+a,q[v+316>>2]=q[a+332>>2]+a,q[v+328>>2]=q[a+336>>2]+a,q[v+332>>2]=q[a+340>>2]+a,q[v+336>>2]=q[a+344>>2]+a,q[v+348>>2]=q[a+348>>2]+a,q[v+360>>2]=q[a+352>>2]+a,q[v+364>>2]=q[a+356>>2]+a,q[v+368>>2]=q[a+360>>2]+a,q[v+352>>2]=q[a+364>>2]+a,q[v+356>>2]=q[a+368>>2]+a,q[v+488>>2]=q[a+372>>2]+a,q[v+492>>2]=q[a+376>>2]+a,q[v+496>>2]=q[a+380>>2]+a,q[v+500>>2]=q[a+384>>2]+a,q[v+504>>2]=q[a+388>>2]+a,q[v+508>>2]=q[a+392>>2]+a,q[v+512>>2]=q[a+396>>2]+a,q[v+516>>2]=q[a+400>>2]+a,q[v+520>>2]=q[a+404>>2]+a,q[v+524>>2]=q[a+408>>2]+a,q[v+528>>2]=q[a+412>>2]+a,q[v+532>>2]=q[a+416>>2]+a,q[v+536>>2]=q[a+420>>2]+a,q[v+540>>2]=q[a+424>>2]+a,q[v+544>>2]=q[a+428>>2]+a,q[v+548>>2]=q[a+432>>2]+a,q[v+552>>2]=q[a+436>>2]+a,q[v+556>>2]=q[a+440>>2]+a,q[v+560>>2]=q[a+444>>2]+a,q[v+564>>2]=q[a+448>>2]+a,q[v+568>>2]=q[a+452>>2]+a,q[v+572>>2]=q[a+456>>2]+a,q[v+576>>2]=q[a+460>>2]+a,q[v+580>>2]=q[a+464>>2]+a,W>>>0<2||(q[v+104>>2]=q[a+468>>2]+a,W>>>0<4)||(q[v+260>>2]=q[a+472>>2]+a,q[v+264>>2]=q[a+476>>2]+a,q[v+268>>2]=q[a+480>>2]+a,q[v+88>>2]=q[a+484>>2]+a,q[v+120>>2]=q[a+488>>2]+a,q[v+160>>2]=q[a+492>>2]+a,q[v+584>>2]=q[a+496>>2]+a,q[v+588>>2]=q[a+500>>2]+a,q[v+592>>2]=q[a+504>>2]+a,q[v+596>>2]=q[a+508>>2]+a,q[v+600>>2]=q[a+512>>2]+a,q[v+604>>2]=q[a+516>>2]+a,q[v+240>>2]=q[a+520>>2]+a,q[v+252>>2]=q[a+524>>2]+a,q[v+256>>2]=q[a+528>>2]+a,q[v+372>>2]=q[a+532>>2]+a,q[v+376>>2]=q[a+536>>2]+a,q[v+380>>2]=q[a+540>>2]+a,q[v+384>>2]=q[a+544>>2]+a,q[v+388>>2]=q[a+548>>2]+a,q[v+392>>2]=q[a+552>>2]+a,q[v+396>>2]=q[a+556>>2]+a,q[v+400>>2]=q[a+560>>2]+a,q[v+416>>2]=q[a+564>>2]+a,q[v+420>>2]=q[a+568>>2]+a,q[v+424>>2]=q[a+572>>2]+a,q[v+440>>2]=q[a+576>>2]+a,q[v+444>>2]=q[a+580>>2]+a,q[v+448>>2]=q[a+584>>2]+a,q[v+464>>2]=q[a+588>>2]+a,q[v+468>>2]=q[a+592>>2]+a,q[v+472>>2]=q[a+596>>2]+a,q[v+476>>2]=q[a+600>>2]+a,q[v+480>>2]=q[a+604>>2]+a,q[v+484>>2]=q[a+608>>2]+a,4!=(0|W)&&(q[v+284>>2]=q[a+612>>2]+a,q[v+288>>2]=q[a+616>>2]+a,q[v+320>>2]=q[a+620>>2]+a,q[v+324>>2]=q[a+624>>2]+a,q[v+340>>2]=q[a+628>>2]+a,q[v+344>>2]=q[a+632>>2]+a,q[v+404>>2]=q[a+636>>2]+a,q[v+408>>2]=q[a+640>>2]+a,q[v+412>>2]=q[a+644>>2]+a,q[v+428>>2]=q[a+648>>2]+a,q[v+432>>2]=q[a+652>>2]+a,q[v+436>>2]=q[a+656>>2]+a,q[v+452>>2]=q[a+660>>2]+a,q[v+456>>2]=q[a+664>>2]+a,q[v+460>>2]=q[a+668>>2]+a))}function oa(a,v,$){var aa=0;a:{if(8==(0|v))v=qa($);else{if(aa=28,3&v|1!=(0|function(a){for(var ap=0,bp=0;bp=ap,a;)a&=a-1,ap=ap+1|0;return bp}(v>>>2)))break a;if(aa=48,-64-v>>>0<$>>>0)break a;v=function(a,Tk){var tl,ul,ol=0,rl=0,sl=0;if((rl=a>>>0>(ol=16)?a:16)+-1&rl)for(;ol=(a=ol)<<1,a>>>0<rl>>>0;);else a=rl;return-64-a>>>0<=Tk>>>0?(q[2094]=48,0):(ol=qa(12+((rl=Tk>>>0<11?16:Tk+11&-8)+a|0)|0))?(Tk=ol+-8|0,ol&a+-1?(sl=(-8&(ul=q[(tl=ol+-4|0)>>2]))-(ol=(a=15<(ol=((a+ol|0)-1&0-a)-8|0)-Tk>>>0?ol:a+ol|0)-Tk|0)|0,3&ul?(q[a+4>>2]=sl|1&q[a+4>>2]|2,q[4+(sl=a+sl|0)>>2]=1|q[4+sl>>2],q[tl>>2]=ol|1&q[tl>>2]|2,q[a+4>>2]=1|q[a+4>>2],za(Tk,ol)):(Tk=q[Tk>>2],q[a+4>>2]=sl,q[a>>2]=Tk+ol)):a=Tk,3&(Tk=q[a+4>>2])&&((ol=-8&Tk)>>>0<=rl+16>>>0||(q[a+4>>2]=rl|1&Tk|2,q[(Tk=a+rl|0)+4>>2]=3|(rl=ol-rl|0),q[4+(ol=a+ol|0)>>2]=1|q[ol+4>>2],za(Tk,rl))),a+8|0):0}(16<v>>>0?v:16,$)}if(!v)return 1;q[a>>2]=v,aa=0}return aa}function pa(a){var da,v=0,$=0,ba=0,ca=0,ea=0,fa=0,ha=0;a:if(a){da=(ba=a+-8|0)+(a=-8&($=q[a+-4>>2]))|0;b:if(!(1&$)){if(!(3&$))break a;if((ba=ba-($=q[ba>>2])|0)>>>0<t[2099])break a;if(a=a+$|0,q[2100]!=(0|ba))if($>>>0<=255)ca=q[ba+8>>2],$>>>=3,(0|(v=q[ba+12>>2]))==(0|ca)?(ha=q[2095]&fd($),q[2095]=ha):(q[ca+12>>2]=v,q[v+8>>2]=ca);else{if(fa=q[ba+24>>2],(0|ba)!=(0|($=q[ba+12>>2])))v=q[ba+8>>2],q[v+12>>2]=$,q[$+8>>2]=v;else if(v=(v=q[(ca=ba+20|0)>>2])||q[(ca=ba+16|0)>>2]){for(;ea=ca,(v=q[(ca=($=v)+20|0)>>2])||(ca=$+16|0,v=q[$+16>>2]););q[ea>>2]=0}else $=0;if(fa){ca=q[ba+28>>2];e:{if(q[(v=8684+(ca<<2)|0)>>2]==(0|ba)){if(q[v>>2]=$)break e;ha=q[2096]&fd(ca),q[2096]=ha;break b}if(!(q[fa+(q[fa+16>>2]==(0|ba)?16:20)>>2]=$))break b}q[$+24>>2]=fa,(v=q[ba+16>>2])&&(q[$+16>>2]=v,q[v+24>>2]=$),(v=q[ba+20>>2])&&(q[$+20>>2]=v,q[v+24>>2]=$)}}else if(3==(3&($=q[4+da>>2])))return q[2097]=a,q[4+da>>2]=-2&$,q[ba+4>>2]=1|a,q[a+ba>>2]=a}if(!(da>>>0<=ba>>>0)&&1&($=q[4+da>>2])){f:{if(!(2&$)){if(q[2101]==(0|da)){if(q[2101]=ba,a=q[2098]+a|0,q[2098]=a,q[ba+4>>2]=1|a,q[2100]!=(0|ba))break a;return q[2097]=0,q[2100]=0}if(q[2100]==(0|da))return q[2100]=ba,a=q[2097]+a|0,q[2097]=a,q[ba+4>>2]=1|a,q[a+ba>>2]=a;a=(-8&$)+a|0;g:if($>>>0<=255)$>>>=3,(0|(v=q[8+da>>2]))==(0|(ca=q[12+da>>2]))?(ha=q[2095]&fd($),q[2095]=ha):(q[v+12>>2]=ca,q[ca+8>>2]=v);else{if(fa=q[24+da>>2],(0|da)!=(0|($=q[12+da>>2])))v=q[8+da>>2],q[v+12>>2]=$,q[$+8>>2]=v;else if(v=(v=q[(ca=20+da|0)>>2])||q[(ca=16+da|0)>>2]){for(;ea=ca,(v=q[(ca=($=v)+20|0)>>2])||(ca=$+16|0,v=q[$+16>>2]););q[ea>>2]=0}else $=0;if(fa){ca=q[28+da>>2];j:{if(q[(v=8684+(ca<<2)|0)>>2]==(0|da)){if(q[v>>2]=$)break j;ha=q[2096]&fd(ca),q[2096]=ha;break g}if(!(q[fa+(q[fa+16>>2]==(0|da)?16:20)>>2]=$))break g}q[$+24>>2]=fa,(v=q[16+da>>2])&&(q[$+16>>2]=v,q[v+24>>2]=$),(v=q[20+da>>2])&&(q[$+20>>2]=v,q[v+24>>2]=$)}}if(q[ba+4>>2]=1|a,q[a+ba>>2]=a,q[2100]!=(0|ba))break f;return q[2097]=a}q[4+da>>2]=-2&$,q[ba+4>>2]=1|a,q[a+ba>>2]=a}if(a>>>0<=255)return $=8420+((a>>>=3)<<3)|0,a=(v=q[2095])&(a=1<<a)?q[$+8>>2]:(q[2095]=a|v,$),q[$+8>>2]=ba,q[a+12>>2]=ba,q[ba+12>>2]=$,q[ba+8>>2]=a;q[ba+16>>2]=0,v=q[ba+20>>2]=0,(ca=a>>>8)&&(v=31,16777215<a>>>0||(v=ca,v=28+((v=((v=(v<<=ca=ca+1048320>>>16&8)<<(fa=v+520192>>>16&4))<<(ea=v+245760>>>16&2)>>>15)-(ea|ca|fa)|0)<<1|a>>>v+21&1)|0)),ea=8684+((q[($=ba)+28>>2]=v)<<2)|0;m:if((ca=q[2096])&($=1<<v)){ca=a<<(31==(0|v)?0:25-(v>>>1)|0),$=q[ea>>2];n:{for(;;){if((-8&q[(v=$)+4>>2])==(0|a))break n;if($=ca>>>29,ca<<=1,!($=q[16+(ea=v+(4&$)|0)>>2]))break}q[ea+16>>2]=ba,q[ba+12>>2]=ba,q[ba+24>>2]=v,q[ba+8>>2]=ba;break m}a=q[v+8>>2],q[a+12>>2]=ba,q[v+8>>2]=ba,q[ba+24>>2]=0,q[ba+12>>2]=v,q[ba+8>>2]=a}else q[2096]=$|ca,q[ea>>2]=ba,q[ba+12>>2]=ba,q[ba+24>>2]=ea,q[ba+8>>2]=ba;if(a=q[2103]+-1|0,!(q[2103]=a)){for(ba=8836;ba=(a=q[ba>>2])+8|0,a;);q[2103]=-1}}}}function qa(a){var sa,ia=0,ja=0,ka=0,la=0,ma=0,na=0,oa=0,pa=0,qa=0,ra=0,ua=0;L=sa=L-16|0;a:{b:{c:{d:{e:{f:{g:{h:{i:{j:{k:{if(a>>>0<=244){if(3&(ia=(ma=q[2095])>>>(a=(na=a>>>0<11?16:a+11&-8)>>>3))){a=(ia=q[8428+(la=(ja=a+(1&(-1^ia))|0)<<3)>>2])+8|0,(0|(ka=q[ia+8>>2]))==(0|(la=la+8420|0))?(ua=fd(ja)&ma,q[2095]=ua):(q[ka+12>>2]=la,q[la+8>>2]=ka),q[ia+4>>2]=3|(ja<<=3),q[4+(ia=ia+ja|0)>>2]=1|q[ia+4>>2];break a}if(na>>>0<=(pa=q[2097])>>>0)break k;if(ia){ja=ia=(a=(0-(a=(0-(ja=2<<a)|ja)&ia<<a)&a)-1|0)>>>12&16,ia=q[8428+(ka=(ja=((ja=(ja|=ia=(a>>>=ia)>>>5&8)|(ia=(a>>>=ia)>>>2&4)|(ia=(a>>>=ia)>>>1&2))|(ia=(a>>>=ia)>>>1&1))+(a>>>ia)|0)<<3)>>2],(0|(a=q[ia+8>>2]))==(0|(ka=ka+8420|0))?(ma=fd(ja)&ma,q[2095]=ma):(q[a+12>>2]=ka,q[ka+8>>2]=a),a=ia+8|0,q[ia+4>>2]=3|na,q[4+(oa=ia+na|0)>>2]=1|(la=(ja<<=3)-na|0),q[ia+ja>>2]=la,pa&&(ia=8420+((ja=pa>>>3)<<3)|0,ka=q[2100],ja=(ja=1<<ja)&ma?q[ia+8>>2]:(q[2095]=ja|ma,ia),q[ia+8>>2]=ka,q[ja+12>>2]=ka,q[ka+12>>2]=ia,q[ka+8>>2]=ja),q[2100]=oa,q[2097]=la;break a}if(!(ra=q[2096]))break k;for(ja=ia=(a=(ra&0-ra)-1|0)>>>12&16,ia=q[8684+(((ja=(ja|=ia=(a>>>=ia)>>>5&8)|(ia=(a>>>=ia)>>>2&4)|(ia=(a>>>=ia)>>>1&2))|(ia=(a>>>=ia)>>>1&1))+(a>>>ia)<<2)>>2],ka=(-8&q[ia+4>>2])-na|0,ja=ia;a=(a=q[ja+16>>2])||q[ja+20>>2];)ka=(ja=(la=(-8&q[a+4>>2])-na|0)>>>0<ka>>>0)?la:ka,ia=ja?a:ia,ja=a;if(qa=q[ia+24>>2],(0|(la=q[ia+12>>2]))!=(0|ia)){a=q[ia+8>>2],q[a+12>>2]=la,q[la+8>>2]=a;break b}if(!(a=q[(ja=ia+20|0)>>2])){if(!(a=q[ia+16>>2]))break j;ja=ia+16|0}for(;oa=ja,(a=q[(ja=(la=a)+20|0)>>2])||(ja=la+16|0,a=q[la+16>>2]););q[oa>>2]=0;break b}if(na=-1,!(4294967231<a>>>0)&&(na=-8&(ia=a+11|0),pa=q[2096])){ja=0-na|0,ma=0,(ia>>>=8)&&(ma=31,16777215<na>>>0||(ma=28+((a=((ma=(ia<<=ka=ia+1048320>>>16&8)<<(a=ia+520192>>>16&4))<<(ia=ma+245760>>>16&2)>>>15)-(ia|a|ka)|0)<<1|na>>>a+21&1)|0));q:{r:{if(ka=q[8684+(ma<<2)>>2])for(ia=na<<(31==(0|ma)?0:25-(ma>>>1)|0),a=0;;){if(!(ja>>>0<=(oa=(-8&q[ka+4>>2])-na|0)>>>0||(la=ka,ja=oa))){ja=0,a=ka;break r}if(oa=q[ka+20>>2],ka=q[16+((ia>>>29&4)+ka|0)>>2],a=oa&&(0|oa)!=(0|ka)?oa:a,ia<<=0!=(0|ka),!ka)break}else a=0;if(!(a|la)){if(!(a=(0-(a=2<<ma)|a)&pa))break k;ka=ia=(a=(a&0-a)-1|0)>>>12&16,a=q[8684+(((ka=(ka|=ia=(a>>>=ia)>>>5&8)|(ia=(a>>>=ia)>>>2&4)|(ia=(a>>>=ia)>>>1&2))|(ia=(a>>>=ia)>>>1&1))+(a>>>ia)<<2)>>2]}if(!a)break q}for(;ja=(ia=(ka=(-8&q[a+4>>2])-na|0)>>>0<ja>>>0)?ka:ja,la=ia?a:la,a=(ia=q[a+16>>2])||q[a+20>>2];);}if(!(!la|ja>>>0>=q[2097]-na>>>0)){if(oa=q[la+24>>2],(0|la)!=(0|(ia=q[la+12>>2]))){a=q[la+8>>2],q[a+12>>2]=ia,q[ia+8>>2]=a;break c}if(!(a=q[(ka=la+20|0)>>2])){if(!(a=q[la+16>>2]))break i;ka=la+16|0}for(;ma=ka,(a=q[(ka=(ia=a)+20|0)>>2])||(ka=ia+16|0,a=q[ia+16>>2]););q[ma>>2]=0;break c}}}if(na>>>0<=(ia=q[2097])>>>0){a=q[2100],16<=(ja=ia-na|0)>>>0?(q[2097]=ja,q[2100]=ka=a+na|0,q[ka+4>>2]=1|ja,q[a+ia>>2]=ja,q[a+4>>2]=3|na):(q[2100]=0,q[2097]=0,q[a+4>>2]=3|ia,q[4+(ia=a+ia|0)>>2]=1|q[ia+4>>2]),a=a+8|0;break a}if(na>>>0<(ka=q[2098])>>>0){q[2098]=ia=ka-na|0,a=q[2101],q[2101]=ja=a+na|0,q[ja+4>>2]=1|ia,q[a+4>>2]=3|na,a=a+8|0;break a}if((ja=(ma=(ja=la=na+47|(a=0))+(ia=q[2213]?q[2215]:(q[2216]=-1,q[2217]=-1,q[2214]=4096,q[2215]=4096,q[2213]=12+sa&-16^1431655768,q[2218]=0,q[2206]=0,4096))|0)&(oa=0-ia|0))>>>0<=na>>>0)break a;if((ia=q[2205])&&(qa=(pa=q[2203])+ja|0)>>>0<=pa>>>0|ia>>>0<qa>>>0)break a;if(4&r[8824])break f;v:{w:{if(ia=q[2101])for(a=8828;;){if((pa=q[a>>2])+q[a+4>>2]>>>0>ia>>>0&&pa>>>0<=ia>>>0)break w;if(!(a=q[a+8>>2]))break}if(-1==(0|(ia=ea(0))))break g;if(ma=ja,(ma=(ka=(a=q[2214])+-1|0)&ia?(ja-ia|0)+(ia+ka&0-a)|0:ma)>>>0<=na>>>0|2147483646<ma>>>0)break g;if((a=q[2205])&&(oa=(ka=q[2203])+ma|0)>>>0<=ka>>>0|a>>>0<oa>>>0)break g;if((0|ia)!=(0|(a=ea(ma))))break v;break e}if(2147483646<(ma=oa&ma-ka)>>>0)break g;if((0|(ia=ea(ma)))==(q[a>>2]+q[a+4>>2]|0))break h;a=ia}if(!(na+48>>>0<=ma>>>0|2147483646<ma>>>0|-1==(0|(ia=a)))){if(2147483646<(a=(a=q[2215])+(la-ma|0)&0-a)>>>0)break e;if(-1!=(0|ea(a))){ma=a+ma|0;break e}ea(0-ma|0);break g}if(-1!=(0|ia))break e;break g}la=0;break b}ia=0;break c}if(-1!=(0|ia))break e}q[2206]=4|q[2206]}if(2147483646<ja>>>0)break d;if(ia=ea(ja),(a=ea(0))>>>0<=ia>>>0|-1==(0|ia)|-1==(0|a))break d;if((ma=a-ia|0)>>>0<=na+40>>>0)break d}a=q[2203]+ma|0,(q[2203]=a)>>>0>t[2204]&&(q[2204]=a);x:{y:{z:{if(ja=q[2101]){for(a=8828;;){if(((ka=q[a>>2])+(la=q[a+4>>2])|0)==(0|ia))break z;if(!(a=q[a+8>>2]))break}break y}for((a=q[2099])>>>0<=ia>>>0&&a||(q[2099]=ia),a=0,q[2208]=ma,q[2207]=ia,q[2103]=-1,q[2104]=q[2213],q[2210]=0;q[8428+(ja=a<<3)>>2]=ka=ja+8420|0,q[ja+8432>>2]=ka,32!=(0|(a=a+1|0)););q[2098]=ka=(a=ma+-40|0)-(ja=ia+8&7?-8-ia&7:0)|0,q[2101]=ja=ia+ja|0,q[ja+4>>2]=1|ka,q[4+(a+ia|0)>>2]=40,q[2102]=q[2217];break x}if(!(8&r[a+12|0]|ia>>>0<=ja>>>0|ja>>>0<ka>>>0)){q[a+4>>2]=la+ma,q[2101]=ia=(a=ja+8&7?-8-ja&7:0)+ja|0,ka=q[2098]+ma|0,q[2098]=a=ka-a|0,q[ia+4>>2]=1|a,q[4+(ja+ka|0)>>2]=40,q[2102]=q[2217];break x}}ia>>>0<(la=q[2099])>>>0&&(q[2099]=ia,la=0),ka=ia+ma|0,a=8828;A:{B:{C:{D:{E:{F:{for(;(0|ka)!=q[a>>2];)if(!(a=q[a+8>>2]))break F;if(!(8&r[a+12|0]))break E}for(a=8828;;){if((ka=q[a>>2])>>>0<=ja>>>0&&ja>>>0<(la=ka+q[a+4>>2]|0)>>>0)break D;a=q[a+8>>2]}}if(q[a>>2]=ia,q[a+4>>2]=q[a+4>>2]+ma,q[4+(qa=(ia+8&7?-8-ia&7:0)+ia|0)>>2]=3|na,a=((ia=ka+(ka+8&7?-8-ka&7:0)|0)-qa|0)-na|0,oa=na+qa|0,(0|ia)==(0|ja)){q[2101]=oa,a=q[2098]+a|0,q[2098]=a,q[oa+4>>2]=1|a;break B}if(q[2100]==(0|ia)){q[2100]=oa,a=q[2097]+a|0,q[2097]=a,q[oa+4>>2]=1|a,q[a+oa>>2]=a;break B}if(1==(3&(ja=q[ia+4>>2]))){ra=-8&ja;G:if(ja>>>0<=255)la=ja>>>3,ja=q[ia+8>>2],(0|(ka=q[ia+12>>2]))==(0|ja)?(ua=q[2095]&fd(la),q[2095]=ua):(q[ja+12>>2]=ka,q[ka+8>>2]=ja);else{if(pa=q[ia+24>>2],(0|(ma=q[ia+12>>2]))!=(0|ia))ja=q[ia+8>>2],q[ja+12>>2]=ma,q[ma+8>>2]=ja;else if(na=(na=q[(ka=ia+20|0)>>2])||q[(ka=ia+16|0)>>2]){for(;ja=ka,(na=q[(ka=(ma=na)+20|0)>>2])||(ka=ma+16|0,na=q[ma+16>>2]););q[ja>>2]=0}else ma=0;if(pa){ja=q[ia+28>>2];J:{if(q[(ka=8684+(ja<<2)|0)>>2]==(0|ia)){if(q[ka>>2]=ma)break J;ua=q[2096]&fd(ja),q[2096]=ua;break G}if(!(q[pa+(q[pa+16>>2]==(0|ia)?16:20)>>2]=ma))break G}q[ma+24>>2]=pa,(ja=q[ia+16>>2])&&(q[ma+16>>2]=ja,q[ja+24>>2]=ma),(ja=q[ia+20>>2])&&(q[ma+20>>2]=ja,q[ja+24>>2]=ma)}}ia=ia+ra|0,a=a+ra|0}if(q[ia+4>>2]=-2&q[ia+4>>2],q[oa+4>>2]=1|a,(q[a+oa>>2]=a)>>>0<=255){a=8420+((ia=a>>>3)<<3)|0,ia=(ja=q[2095])&(ia=1<<ia)?q[a+8>>2]:(q[2095]=ia|ja,a),q[a+8>>2]=oa,q[ia+12>>2]=oa,q[oa+12>>2]=a,q[oa+8>>2]=ia;break B}if(ia=0,(ka=a>>>8)&&(ia=31,16777215<a>>>0||(ia=28+((ia=((na=(ka<<=la=ka+1048320>>>16&8)<<(ia=ka+520192>>>16&4))<<(ka=na+245760>>>16&2)>>>15)-(ka|ia|la)|0)<<1|a>>>ia+21&1)|0)),q[(ja=oa)+28>>2]=ia,q[oa+16>>2]=0,ja=8684+(ia<<2)|(q[oa+20>>2]=0),(ka=q[2096])&(la=1<<ia)){for(ka=a<<(31==(0|ia)?0:25-(ia>>>1)|0),ia=q[ja>>2];;){if((-8&q[(ja=ia)+4>>2])==(0|a))break C;if(ia=ka>>>29,ka<<=1,!(ia=q[16+(la=(4&ia)+ja|0)>>2]))break}q[la+16>>2]=oa}else q[2096]=ka|la,q[ja>>2]=oa;q[oa+24>>2]=ja,q[oa+12>>2]=oa,q[oa+8>>2]=oa;break B}for(q[2098]=oa=(a=ma+-40|0)-(ka=ia+8&7?-8-ia&7:0)|0,q[2101]=ka=ia+ka|0,q[ka+4>>2]=1|oa,q[4+(a+ia|0)>>2]=40,q[2102]=q[2217],q[(ka=(a=(la+(la+-39&7?39-la&7:0)|0)-47|0)>>>0<ja+16>>>0?ja:a)+4>>2]=27,a=q[2210],q[ka+16>>2]=q[2209],q[ka+20>>2]=a,a=q[2208],q[ka+8>>2]=q[2207],q[ka+12>>2]=a,q[2209]=ka+8,q[2208]=ma,q[2207]=ia,a=ka+24|(q[2210]=0);q[a+4>>2]=7,ia=a+8|0,a=a+4|0,ia>>>0<la>>>0;);if((0|ja)==(0|ka))break x;if(q[ka+4>>2]=-2&q[ka+4>>2],q[ja+4>>2]=1|(la=ka-ja|0),(q[ka>>2]=la)>>>0<=255){a=8420+((ia=la>>>3)<<3)|0,ia=(ka=q[2095])&(ia=1<<ia)?q[a+8>>2]:(q[2095]=ia|ka,a),q[a+8>>2]=ja,q[ia+12>>2]=ja,q[ja+12>>2]=a,q[ja+8>>2]=ia;break x}if(q[ja+16>>2]=0,a=q[ja+20>>2]=0,(ka=la>>>8)&&(a=31,16777215<la>>>0||(a=28+((a=((oa=(ka<<=ma=ka+1048320>>>16&8)<<(a=ka+520192>>>16&4))<<(ka=oa+245760>>>16&2)>>>15)-(ka|a|ma)|0)<<1|la>>>a+21&1)|0)),ia=8684+((q[(ia=ja)+28>>2]=a)<<2)|0,(ka=q[2096])&(ma=1<<a)){for(a=la<<(31==(0|a)?0:25-(a>>>1)|0),ia=q[ia>>2];;){if((0|la)==(-8&q[(ka=ia)+4>>2]))break A;if(ia=a>>>29,a<<=1,!(ia=q[16+(ma=ka+(4&ia)|0)>>2]))break}q[ma+16>>2]=ja,q[ja+24>>2]=ka}else q[2096]=ka|ma,q[ia>>2]=ja,q[ja+24>>2]=ia;q[ja+12>>2]=ja,q[ja+8>>2]=ja;break x}a=q[ja+8>>2],q[a+12>>2]=oa,q[ja+8>>2]=oa,q[oa+24>>2]=0,q[oa+12>>2]=ja,q[oa+8>>2]=a}a=qa+8|0;break a}a=q[ka+8>>2],q[a+12>>2]=ja,q[ka+8>>2]=ja,q[ja+24>>2]=0,q[ja+12>>2]=ka,q[ja+8>>2]=a}if(!((a=q[2098])>>>0<=na>>>0)){q[2098]=ia=a-na|0,a=q[2101],q[2101]=ja=a+na|0,q[ja+4>>2]=1|ia,q[a+4>>2]=3|na,a=a+8|0;break a}}q[2094]=48,a=0;break a}Q:if(oa){a=q[la+28>>2];R:{if(q[(ka=8684+(a<<2)|0)>>2]==(0|la)){if(q[ka>>2]=ia)break R;pa=fd(a)&pa,q[2096]=pa;break Q}if(!(q[oa+(q[oa+16>>2]==(0|la)?16:20)>>2]=ia))break Q}q[ia+24>>2]=oa,(a=q[la+16>>2])&&(q[ia+16>>2]=a,q[a+24>>2]=ia),(a=q[la+20>>2])&&(q[ia+20>>2]=a,q[a+24>>2]=ia)}S:if(ja>>>0<=15)q[la+4>>2]=3|(a=ja+na|0),q[4+(a=a+la|0)>>2]=1|q[a+4>>2];else if(q[la+4>>2]=3|na,q[4+(ka=la+na|0)>>2]=1|ja,(q[ja+ka>>2]=ja)>>>0<=255)a=8420+((ia=ja>>>3)<<3)|0,ia=(ja=q[2095])&(ia=1<<ia)?q[a+8>>2]:(q[2095]=ia|ja,a),q[a+8>>2]=ka,q[ia+12>>2]=ka,q[ka+12>>2]=a,q[ka+8>>2]=ia;else{a=0,(na=ja>>>8)&&(a=31,16777215<ja>>>0||(a=28+((a=((oa=(na<<=ma=na+1048320>>>16&8)<<(a=na+520192>>>16&4))<<(na=oa+245760>>>16&2)>>>15)-(na|a|ma)|0)<<1|ja>>>a+21&1)|0)),q[(ia=ka)+28>>2]=a,q[ka+16>>2]=0,ia=8684+(a<<2)|(q[ka+20>>2]=0);V:{if((na=1<<a)&pa){for(a=ja<<(31==(0|a)?0:25-(a>>>1)|0),na=q[ia>>2];;){if((-8&q[(ia=na)+4>>2])==(0|ja))break V;if(na=a>>>29,a<<=1,!(na=q[16+(ma=(4&na)+ia|0)>>2]))break}q[ma+16>>2]=ka}else q[2096]=na|pa,q[ia>>2]=ka;q[ka+24>>2]=ia,q[ka+12>>2]=ka,q[ka+8>>2]=ka;break S}a=q[ia+8>>2],q[a+12>>2]=ka,q[ia+8>>2]=ka,q[ka+24>>2]=0,q[ka+12>>2]=ia,q[ka+8>>2]=a}a=la+8|0;break a}X:if(qa){a=q[ia+28>>2];Y:{if(q[(ja=8684+(a<<2)|0)>>2]==(0|ia)){if(q[ja>>2]=la)break Y;ua=fd(a)&ra,q[2096]=ua;break X}if(!(q[qa+(q[qa+16>>2]==(0|ia)?16:20)>>2]=la))break X}q[la+24>>2]=qa,(a=q[ia+16>>2])&&(q[la+16>>2]=a,q[a+24>>2]=la),(a=q[ia+20>>2])&&(q[la+20>>2]=a,q[a+24>>2]=la)}ka>>>0<=15?(q[ia+4>>2]=3|(a=ka+na|0),q[4+(a=a+ia|0)>>2]=1|q[a+4>>2]):(q[ia+4>>2]=3|na,q[4+(na=ia+na|0)>>2]=1|ka,q[ka+na>>2]=ka,pa&&(a=8420+((ja=pa>>>3)<<3)|0,la=q[2100],ja=(ja=1<<ja)&ma?q[a+8>>2]:(q[2095]=ja|ma,a),q[a+8>>2]=la,q[ja+12>>2]=la,q[la+12>>2]=a,q[la+8>>2]=ja),q[2100]=na,q[2097]=ka),a=ia+8|0}return L=16+sa|0,a}function ra(a,va,wa,xa,ya,za,Aa){var Qa,Ta,Ba,Ca=0,Da=0,Fa=0,Ia=0,Ja=0,Ka=0,Ma=0,Na=0,Oa=0,Pa=0,Ra=0,Sa=0;q[76+(L=Ba=L-80|0)>>2]=va,Ta=55+Ba|0,Qa=56+Ba|0,va=0;a:{b:{c:for(;;){(0|Oa)<0||(Oa=(2147483647-Oa|0)<(0|va)?(q[2094]=61,-1):va+Oa|0);e:{f:{g:{h:{i:{j:{k:{l:{m:{n:{o:{p:{q:{if(Ia=q[76+Ba>>2],Fa=r[0|(va=Ia)]){for(;;){r:{s:{t:if(Ca=255&Fa){if(37!=(0|Ca))break s;for(Fa=va;;){if(37!=r[va+1|0])break t;if(q[76+Ba>>2]=Ca=va+2|0,Fa=Fa+1|0,Da=r[va+2|0],va=Ca,37!=(0|Da))break}}else Fa=va;if(va=Fa-Ia|0,a&&Z(a,Ia,va),va)continue c;Pa=-1,Ja=!ha(o[q[76+(Ca=Ba)>>2]+(Fa=1)|0]),va=q[76+Ba>>2],Ja|36!=r[va+2|0]||(Pa=o[va+1|0]+-48|0,Ra=1,Fa=3),q[Ca+76>>2]=va=Fa+va|0;u:if(31<(Da=(Ma=o[(Fa=0)|va])+-32|0)>>>0)Ca=va;else if(Ca=va,75913&(Da=1<<Da))for(;;){if(q[76+Ba>>2]=Ca=va+1|0,Fa|=Da,31<(Da=(Ma=o[va+1|0])+-32|0)>>>0)break u;if(va=Ca,!(75913&(Da=1<<Da)))break}if(42==(0|Ma)){if(Ja=Ba,ha(o[Ca+1|0])&&(va=q[76+Ba>>2],36==r[va+2|0]))q[((o[va+1|0]<<2)+ya|0)-192>>2]=10,Na=q[((o[va+1|0]<<3)+xa|0)-384>>2],Ra=1,va=va+3|0;else{if(Ra)break b;Na=Ra=0,a&&(va=q[wa>>2],q[wa>>2]=va+4,Na=q[va>>2]),va=q[76+Ba>>2]+1|0}q[Ja+76>>2]=va,-1<(0|Na)||(Na=0-Na|0,Fa|=8192)}else{if((0|(Na=Ha(76+Ba|0)))<0)break b;va=q[76+Ba>>2]}if(Da=-1,46==r[0|va])if(42==r[va+1|0])if(ha(o[va+2|0])&&(va=q[76+Ba>>2],36==r[va+3|0]))q[((o[va+2|0]<<2)+ya|0)-192>>2]=10,Da=q[((o[va+2|0]<<3)+xa|0)-384>>2],q[76+Ba>>2]=va=va+4|0;else{if(Ra)break b;Da=a?(va=q[wa>>2],q[wa>>2]=va+4,q[va>>2]):0,va=q[76+Ba>>2]+2|0,q[76+Ba>>2]=va}else q[76+Ba>>2]=va+1,Da=Ha(76+Ba|0),va=q[76+Ba>>2];for(Ca=0;;){if(Sa=Ca,Ka=-1,57<o[0|va]+-65>>>0)break a;if(q[76+Ba>>2]=Ma=va+1|0,Ca=o[0|va],va=Ma,!((Ca=r[3327+(Ca+w(Sa,58)|0)|0])+-1>>>0<8))break}if(!Ca)break a;A:{B:{C:{if(19==(0|Ca)){if((0|Pa)<=-1)break C;break a}if((0|Pa)<0)break B;q[(Pa<<2)+ya>>2]=Ca,Ca=q[4+(va=(Pa<<3)+xa|0)>>2],q[64+Ba>>2]=q[va>>2],q[68+Ba>>2]=Ca}if(va=0,a)break A;continue c}if(!a)break e;Ga(64+Ba|0,Ca,wa,Aa),Ma=q[76+Ba>>2]}if(Ja=-65537&Fa,Fa=8192&Fa?Ja:Fa,Pa=3360,Ca=Qa,va=o[Ma+-1|(Ka=0)],(Ma=(va=Sa&&3==(15&va)?-33&va:va)+-88|0)>>>0<=32)break r;D:{E:{F:{G:{if(6<(Ja=va+-65|0)>>>0){if(83!=(0|va))break f;if(!Da)break G;Ca=q[64+Ba>>2];break E}switch(Ja-1|0){case 1:break F;case 0:case 2:break f;default:break q}}_(a,32,Na,va=0,Fa);break D}q[12+Ba>>2]=0,q[8+Ba>>2]=q[64+Ba>>2],q[64+Ba>>2]=8+Ba,Da=-1,Ca=8+Ba|0}va=0;H:{for(;;){if(!(Ia=q[Ca>>2]))break H;if((Ja=(0|(Ia=Ea(4+Ba|0,Ia)))<0)|Da-va>>>0<Ia>>>0)break;if(Ca=Ca+4|0,!((va=va+Ia|0)>>>0<Da>>>0))break H}if(Ka=-1,Ja)break a}if(_(a,32,Na,va,Fa),va)for(Da=0,Ca=q[64+Ba>>2];;){if(!(Ia=q[Ca>>2]))break D;if((0|va)<(0|(Da=(Ia=Ea(4+Ba|0,Ia))+Da|0)))break D;if(Z(a,4+Ba|0,Ia),Ca=Ca+4|0,!(Da>>>0<va>>>0))break}else va=0}_(a,32,Na,va,8192^Fa),va=(0|va)<(0|Na)?Na:va;continue c}q[76+Ba>>2]=Ca=va+1|0,Fa=r[va+1|0],va=Ca;continue}break}switch(Ma-1|0){case 28:break i;case 21:break j;case 23:break l;case 22:break m;case 11:case 16:break n;case 10:break o;case 26:break p;case 8:case 12:case 13:case 14:break q;case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:case 9:case 15:case 17:case 18:case 19:case 20:case 24:case 25:case 27:case 29:case 30:break f;default:break k}}if(Ka=Oa,a)break a;if(!Ra)break e;for(va=1;;){if(a=q[(va<<2)+ya>>2]){if(Ga((va<<3)+xa|0,a,wa,Aa),10!=(0|(va=va+(Ka=1)|0)))continue;break a}break}if(Ka=1,9<va>>>0)break a;if(Ka=-1,q[(va<<2)+ya>>2])break a;for(;!q[((va=va+1|0)<<2)+ya>>2]&&10!=(0|va););Ka=va>>>0<10?-1:1;break a}va=0|n[za](a,v[64+Ba>>3],Na,Da,Fa,va);continue}Ca=(va=La(Ia=(va=q[64+Ba>>2])||3370,Da))||Da+Ia|0,Fa=Ja,Da=va?va-Ia|0:Da;break f}o[55+Ba|0]=q[64+Ba>>2],Da=1,Ia=Ta,Fa=Ja;break f}if(va=Ja=q[68+Ba>>2],Ia=q[64+Ba>>2],(0|va)<-1||(0|va)<=-1&&!(4294967295<Ia>>>0)){va=0-(va+(0<Ia>>>0)|0)|0,q[64+Ba>>2]=Ia=0-Ia|0,q[68+Ba>>2]=va,Ka=1,Pa=3360;break h}if(2048&Fa){Ka=1,Pa=3361;break h}Pa=(Ka=1&Fa)?3362:3360;break h}if(Ia=function(a,Gl,Pm){if(a|Gl)for(;o[0|(Pm=Pm+-1|0)]=7&a|48,(a=(7&Gl)<<29|a>>>3)|(Gl>>>=3););return Pm}(q[64+Ba>>2],q[68+Ba>>2],Qa),!(8&Fa))break g;Da=(0|(va=Qa-Ia|0))<(0|Da)?Da:va+1|0;break g}Da=8<Da>>>0?Da:8,Fa|=8,va=120}if(Ia=function(a,Gl,Pm,Qm){if(a|Gl)for(;o[0|(Pm=Pm+-1|0)]=r[3856+(15&a)|0]|Qm,(a=(15&Gl)<<28|a>>>4)|(Gl>>>=4););return Pm}(q[64+Ba>>2],q[68+Ba>>2],Qa,32&va),!(8&Fa)|!(q[64+Ba>>2]|q[68+Ba>>2]))break g;Pa=3360+(va>>>4)|0,Ka=2;break g}if(7<(Ca=255&Sa)>>>(va=0))continue;switch(Ca-1|0){default:case 0:q[q[64+Ba>>2]>>2]=Oa;continue;case 1:Ca=q[64+Ba>>2],q[Ca>>2]=Oa,q[Ca+4>>2]=Oa>>31;continue;case 2:p[q[64+Ba>>2]>>1]=Oa;continue;case 3:o[q[64+Ba>>2]]=Oa;continue;case 5:q[q[64+Ba>>2]>>2]=Oa;continue;case 4:continue;case 6:}Ca=q[64+Ba>>2],q[Ca>>2]=Oa,q[Ca+4>>2]=Oa>>31;continue}Ia=q[64+Ba>>2],va=q[68+Ba>>2],Pa=3360}Ia=ga(Ia,va,Qa)}Fa=-1<(0|Da)?-65537&Fa:Fa,Da=!!((Ja=va=q[68+Ba>>2])|(Ma=q[64+Ba>>2]))|Da?(0|(va=!(Ja|Ma)+(Qa-Ia|0)|0))<(0|Da)?Da:va:(Ia=Qa,0)}_(a,32,va=(0|Na)<(0|(Ca=(Da=(0|Da)<(0|(Ja=Ca-Ia|0))?Ja:Da)+Ka|0))?Ca:Na,Ca,Fa),Z(a,Pa,Ka),_(a,48,va,Ca,65536^Fa),_(a,48,Da,Ja,0),Z(a,Ia,Ja),_(a,32,va,Ca,8192^Fa);continue}break}Ka=0;break a}Ka=-1}return L=80+Ba|0,Ka}function sa(a){var ya,va=0,wa=0,xa=0,za=0,xa=4,wa=1439;a:if(va=r[0|a]){for(;!((0|(ya=r[0|wa]))!=(0|va)||!(xa=xa+-1|0)|!ya);)if(wa=wa+1|0,va=r[a+1|0],a=a+1|0,!va)break a;za=va}return(255&za)-r[0|wa]|0}function ta(a,Aa,Ea){var La,Ga,Ha=0,Ua=0,Va=0;q[(L=Ga=L-240|0)>>2]=a,Ua=1;a:if(!((0|Aa)<2))for(Ha=a;;){if(Ha=(La=Ha+-4|0)-q[((Va=Aa+-2|0)<<2)+Ea>>2]|0,0<=(0|n[5](a,Ha))&&-1<(0|n[5](a,La)))break a;if(a=(Ua<<2)+Ga|0,0<=(0|n[5](Ha,La))?(q[a>>2]=Ha,Va=Aa+-1|0):Ha=q[a>>2]=La,Ua=Ua+1|0,(0|Va)<2)break a;a=q[Ga>>2],Aa=Va}Na(Ga,Ua),L=240+Ga|0}function ua(a){var Ea,_a,Aa=0;if(x(0),function(a){var Hg,Ig;q[a+428>>2]&&(Ig=q[a+332>>2],$(q[a+460>>2],q[a+436>>2],Hg=Ig<<2),$(q[a+464>>2],q[a+440>>2],Hg),$(q[a+468>>2],q[a+448>>2],Hg),r[q[a>>2]+4|0]<4||($(q[a+472>>2],q[a+452>>2],Hg=Ig<<4),$(q[a+476>>2],q[a+456>>2],Hg)))}(a),function(a){var he,ie,je,ce=0,de=x(0),ee=x(0),fe=0,ge=x(0);x(0),x(0);if(1<=(0|(fe=q[a>>2])))for(je=(ce=q[a+4>>2])+w(fe,52)|0,a=q[a+12>>2];de=u[a>>2],u[ce+44>>2]!=(de=(he=q[ce+16>>2])?(ee=de,de=u[ce+4>>2],ge=u[ce+12>>2],ee=x(x(ee-de)/ge),ie=x(C(ee)),fe=x(y(ie))<x(2147483648)?~~ie:-2147483648,x(de+x(ge*x(ee-x(0|fe))))):(ge=u[ce+4>>2],ee=u[ce+8>>2],de<ge?ge:ee<de?ee:de))?(u[ce+44>>2]=de,q[ce+48>>2]=1):q[ce+48>>2]=0,he||(u[a>>2]=de),a=a+4|0,(ce=ce+52|0)>>>0<je>>>0;);}(a+540|0),function(a){var Td,Ud,Zd,$d,ae,be,Od=0,Pd=0,Qd=x(0),Rd=0,Sd=x(0),Vd=(x(0),x(0),0),Wd=x(0),Xd=0,Yd=0,_d=0;x(0);if(1<=(0|(Rd=q[a+540>>2])))for(ae=(Vd=q[a+544>>2])+w(Rd,52)|0,be=q[a+644>>2];;){a:if(!(q[Vd>>2]||(0|(Rd=q[Vd+32>>2]))<1))if(Zd=(a=q[Vd+28>>2])+w(Rd,28)|0,$d=u[Vd+24>>2],Ud=u[Vd+20>>2],Td=u[Vd+44>>2],be)for(;;){Wd=x(Yd=0);h:{i:{j:{if((0|(Pd=q[a>>2]))<1)Od=Rd=0;else if(Xd=q[a+4>>2],Sd=u[Xd>>2],Qd=x(Sd-Ud),1==(0|Pd))Rd=Td<x(Ud+Sd)^1|Qd<Td^1,Od=0;else{if(Td<Qd){Rd=1,Od=0;break i}if(Rd=0,Td<x(Ud+Sd))Od=0;else{if(Od=1,Qd=u[Xd+4>>2],!(Td<x(Ud+Qd)))for(;;){if((0|Pd)==(0|(Od=Od+1|0)))break j;if(Sd=Qd,Qd=u[Xd+(Od<<2)>>2],Td<x(Ud+Qd))break}x(Qd-Ud)<Td||(Od=Od+-1|0,(Qd=x(Qd-Sd))<$d)||(Wd=x(x(Td-Sd)/Qd))}}if(Rd)break i;if(Xd=Pd=1,q[a+16>>2])break h;break i}Od=Pd+-1|0,Rd=1}Xd=(Pd=(Sd=u[a+12>>2])!=Wd)&(Wd==x(0)|Sd==x(0))|q[a+8>>2]!=(0|Od),Yd=Rd}if(q[a+20>>2]=Xd,q[a+24>>2]=Pd,u[a+12>>2]=Wd,q[a+16>>2]=Yd,q[a+8>>2]=Od,!((a=a+28|0)>>>0<Zd>>>0))break}else{if(!q[Vd+48>>2])for(;;)if(q[a+20>>2]=0,!((a=a+28|(q[a+24>>2]=0))>>>0<Zd>>>0))break a;for(;;){Wd=x(Yd=0);b:{c:{d:{e:if(!(((Pd=0)|(Od=q[(Xd=a)>>2]))<1)){if(Rd=q[a+4>>2],Sd=u[Rd>>2],Qd=x(Sd-Ud),1!=(0|Od)){if(!(Td<Qd)){if(Pd=0,Td<x(Ud+Sd))break e;Pd=1;f:if(Qd=u[Rd+4>>2],!(Td<x(Ud+Qd))){for(Od=Od+-1|0;Sd=Qd,(0|Od)!=(0|Pd);)if(Qd=u[Rd+((Pd=Pd+1|0)<<2)>>2],Td<x(Ud+Qd))break f;Rd=1;break c}if(Rd=0,x(Qd-Ud)<Td){Od=Pd;break d}if(Od=Pd+-1|0,(Qd=x(Qd-Sd))<$d)break d;Wd=x(x(Td-Sd)/Qd);break d}Rd=1,Od=0;break c}Pd=Td<x(Ud+Sd)^1|Qd<Td^1}Rd=Pd,Od=0}if(!Rd&&(_d=Pd=1,q[a+16>>2]))break b}Yd=Rd,_d=(Pd=(Sd=u[a+12>>2])!=Wd)&(Wd==x(0)|Sd==x(0))|q[a+8>>2]!=(0|Od)}if(q[Xd+20>>2]=_d,q[a+24>>2]=Pd,u[a+12>>2]=Wd,q[a+16>>2]=Yd,q[a+8>>2]=Od,!((a=a+28|0)>>>0<Zd>>>0))break}}if(!((Vd=Vd+52|0)>>>0<ae>>>0))break}}(a),function(a){var od,pd,qd,rd,sd,hd=0,id=x(0),jd=0,kd=0,ld=x(0),md=0,nd=x(0);x(0);if(!(r[q[a>>2]+4|0]<4||(0|(hd=q[a+540>>2]))<1))for(rd=(md=q[a+544>>2])+w(hd,52)|0,sd=q[a+644>>2];;){b:if(1==q[md>>2]&&!((0|(hd=q[md+40>>2]))<1))if(pd=(a=q[md+36>>2])+w(hd,28)|0,od=u[md+44>>2],sd)for(;;){nd=x(hd=0);d:if(!((0|(kd=q[a>>2]))<2||(jd=q[a+4>>2],od<=(id=u[jd>>2])))){hd=1;e:if(!(od<(ld=u[jd+4>>2]))){for(;id=ld,(0|kd)!=(0|(hd=hd+1|0));)if(od<(ld=u[jd+(hd<<2)>>2]))break e;hd=kd+-1|0;break d}nd=x(x(od-id)/x(ld-id)),hd=hd+-1|0}if(id=u[a+16>>2],u[a+16>>2]=nd,kd=q[a+12>>2],q[a+12>>2]=hd,q[a+24>>2]=jd=id!=nd,q[a+20>>2]=jd&(nd==x(0)|id==x(0))|(0|hd)!=(0|kd),!((a=a+28|0)>>>0<pd>>>0))break}else{if(!q[md+48>>2])for(;;)if(q[a+20>>2]=0,!((a=a+28|(q[a+24>>2]=0))>>>0<pd>>>0))break b;for(;;){nd=x(kd=0);c:if(!((0|(qd=q[a>>2]))<2||(jd=q[a+4>>2],od<=(id=u[jd>>2])))){if(hd=1,!(od<(ld=u[jd+4>>2])))for(kd=qd+-1|0;;){if(id=ld,(0|hd)==(0|kd))break c;if(od<(ld=u[jd+((hd=hd+1|0)<<2)>>2]))break}nd=x(x(od-id)/x(ld-id)),kd=hd+-1|0}if(id=u[a+16>>2],u[a+16>>2]=nd,hd=q[a+12>>2],q[a+12>>2]=kd,q[a+24>>2]=jd=id!=nd,q[a+20>>2]=jd&(nd==x(0)|id==x(0))|(0|hd)!=(0|kd),!((a=a+28|0)>>>0<pd>>>0))break}}if(!((md=md+52|0)>>>0<rd>>>0))break}}(a),function(a){var Fd,Gd,Jd,Kd,Ld,Md,Nd,td=0,ud=0,vd=0,wd=0,xd=0,yd=0,zd=0,Ad=x(0),Bd=0,Dd=0,Ed=0,Hd=0,Id=0;if(1<=(0|(ud=q[a+564>>2])))for(Md=(xd=q[a+568>>2])+w(ud,36)|0,Kd=q[a+644>>2];;){a:{if(!(yd=((vd=wd=ud=0)|(Gd=q[xd+4>>2]))<1))for(Bd=q[xd>>2],a=Hd=0;;){if(td=q[Bd+(a<<2)>>2],q[td+16>>2]){td=1,Id=0;break a}if(vd=vd||q[td+24>>2],ud=ud||q[td+20>>2],wd=(u[td+12>>2]!=x(0))+wd|0,(0|Gd)==(0|(a=a+1|0)))break}if(td=0,(Hd=Kd?1:vd)|(Id=Kd?1:ud)&&(q[xd+12>>2]=Bd=1<<wd,31!=(0|wd))){for(ud=q[xd+20>>2],Ld=q[xd>>2],vd=(a=q[xd+16>>2])+(zd=Bd<<2)|0,vd=ca(a,0,4+((-1^a)+((a=a+4|0)>>>0<vd>>>0?vd:a)|0)&-4),zd=ud+zd|0,a=ud;q[a>>2]=1065353216,(a=a+4|0)>>>0<zd>>>0;);if(!yd){if(yd=0,zd=td=1,wd)for(;;){if(wd=q[(yd<<2)+Ld>>2],Dd=q[wd+8>>2],Ed=w(Dd,td),(Ad=u[wd+12>>2])!=x(a=0)){for(q[vd>>2]=Ed+q[vd>>2],u[ud>>2]=x(x(1)-Ad)*u[ud>>2],Dd=w(Dd+(a=1)|0,td);Ad=u[wd+12>>2],Nd=q[(Jd=(Fd=a<<2)+vd|0)>>2],q[Jd>>2]=Nd+((Jd=a&zd)?Dd:Ed),u[(Fd=ud+Fd|0)>>2]=(Jd?Ad:x(x(1)-Ad))*u[Fd>>2],(0|Bd)!=(0|(a=a+1|0)););zd<<=1}else for(;q[(Dd=vd+(a<<2)|0)>>2]=Ed+q[Dd>>2],(0|Bd)!=(0|(a=a+1|0)););if(td=w(q[wd>>2],td),(0|Gd)==(0|(yd=yd+1|0)))break}else for(;;){if(wd=q[(yd<<2)+Ld>>2],zd=w(q[wd+8>>2],td),(Ad=u[wd+12>>2])!=x(a=0))q[vd>>2]=zd+q[vd>>2],u[ud>>2]=x(x(1)-Ad)*u[ud>>2];else for(;q[(Ed=vd+(a<<2)|0)>>2]=zd+q[Ed>>2],(0|Bd)!=(0|(a=a+1|0)););if(td=w(q[wd>>2],td),(0|Gd)==(0|(yd=yd+1|0)))break}td=0}}}if(q[xd+32>>2]=td,q[xd+24>>2]=Id,q[xd+28>>2]=Hd,!((xd=xd+36|0)>>>0<Md>>>0))break}}(a),function(a){var dd,ed,fd,gd,Tc=x(0),Uc=0,Vc=0,Wc=0,Xc=0,Yc=0,Zc=x(0),_c=x(0),$c=x(0),ad=0,bd=0,cd=0;if(!(r[q[a>>2]+4|0]<4||(0|(Uc=q[a+588>>2]))<1))for(gd=(Wc=q[a+592>>2])+w(Uc,48)|0,dd=q[a+644>>2];;){if(a=q[Wc>>2],(bd=dd?1:q[a+20>>2])|(cd=dd?1:q[a+24>>2])){c:{d:{Yc=Wc,Xc=q[a+8>>2],Uc=q[a+12>>2],Tc=u[a+16>>2],a=(0|Xc)!=(0|Uc);e:{if(Tc!=x(0)){if(a=Uc+1|0,(0|Uc)==(0|Xc)){q[Wc+8>>2]=bd=1,Tc=x(x(1)-Tc),cd=1;break e}a=(0|a)==(0|Xc)?1:2}if(q[Yc+8>>2]=a,!cd)break d;a=Uc}u[Wc+24>>2]=Tc,u[Wc+20>>2]=x(1)-Tc;break c}cd=0,a=Uc}bd?(q[Wc+12>>2]=a,q[Wc+16>>2]=a+1):bd=0}else bd=cd=0;g:if((0|(ed=q[Wc+36>>2]))<1)$c=x(1);else{if(fd=q[Wc+40>>2],a=0,$c=x(1),!dd)for(;;){h:{i:{if(Uc=q[(a<<2)+fd>>2],Vc=q[Uc>>2]){if(!q[Vc+48>>2]){Tc=u[Uc+16>>2];break h}if((0|(Xc=q[Uc+12>>2]))<1){Tc=x(1),u[Uc+16>>2]=1;break h}if(ad=q[Uc+8>>2],1!=(0|Xc)&&(Zc=u[Vc+44>>2],Yc=q[Uc+4>>2],!(Zc<=(_c=u[Yc>>2]))))break i;Tc=u[ad>>2],u[Uc+16>>2]=Tc;break h}q[Uc+16>>2]=1065353216,Tc=x(1);break h}Vc=1;j:if(!(Zc<(Tc=u[Yc+4>>2]))){for(;_c=Tc,(0|Xc)!=(0|(Vc=Vc+1|0));)if(Zc<(Tc=u[Yc+(Vc<<2)>>2]))break j;Tc=u[(ad+(Xc<<2)|0)-4>>2],u[Uc+16>>2]=Tc;break h}Yc=Uc,Tc=x(x(Zc-_c)/x(Tc-_c)),Tc=x(x(Tc*u[(Uc=ad+(Vc<<2)|0)>>2])+x(u[Uc+-4>>2]*x(x(1)-Tc))),u[Yc+16>>2]=Tc}if($c=$c<Tc?$c:Tc,(0|ed)==(0|(a=a+1|0)))break g}for(;;){Uc=q[(a<<2)+fd>>2],Vc=q[Uc>>2],Tc=x(1);l:if(Vc&&(ad=q[Uc+12>>2],Tc=x(1),!((0|ad)<1))&&(Xc=q[Uc+8>>2],Tc=u[Xc>>2],1!=(0|ad))){m:{if(Zc=u[Vc+44>>2],Yc=q[Uc+4>>2],Zc<=(_c=u[Yc>>2])){Tc=u[Xc>>2];break l}if(Vc=1,!(Zc<(Tc=u[Yc+4>>2]))){for(;_c=Tc,(0|ad)!=(0|(Vc=Vc+1|0));)if(Zc<(Tc=u[Yc+(Vc<<2)>>2]))break m;Tc=u[(Xc+(ad<<2)|0)-4>>2];break l}}Tc=x(x(Zc-_c)/x(Tc-_c)),Tc=x(x(Tc*u[(Vc=Xc+(Vc<<2)|0)>>2])+x(u[Vc+-4>>2]*x(x(1)-Tc)))}if($c=$c<(u[Uc+16>>2]=Tc)?$c:Tc,(0|ed)==(0|(a=a+1|0)))break}}if(q[Wc+32>>2]=cd,q[Wc+28>>2]=bd,u[Wc+44>>2]=$c,!((Wc=Wc+48|0)>>>0<gd>>>0))break}}(a),1<=(0|(Ea=q[a+4>>2])))for(Ea=(Aa=q[a+52>>2])+(Ea<<2)|0;_a=u[Aa>>2],u[Aa>>2]=_a<x(0)?x(0):x(A(_a,x(1))),(Aa=Aa+4|0)>>>0<Ea>>>0;);!function(a){var Ce,De,Ee,Ae=0,Be=0;if(1<=(0|(Be=q[a+4>>2])))for(Ee=(Ae=q[a+8>>2])+w(Be,12)|0,a=Ce=q[a+40>>2];Be=0,q[Ae+8>>2]&&(De=q[Ae+4>>2],!q[(De<<2)+Ce>>2]&&-1!=(0|De)||(Be=!q[q[Ae>>2]+32>>2])),q[a>>2]=Be,a=a+4|0,(Ae=Ae+12|0)>>>0<Ee>>>0;);}(a),function(a){var og,sg,tg,ug,vg,wg,xg,mg=0,ng=0,pg=0,qg=0,rg=0;if(1<=(0|(sg=q[a+4>>2])))for(ug=q[a+8>>2],tg=q[a>>2],vg=q[tg+724>>2];;){if(og=q[w(qg,12)+ug>>2],(q[og+28>>2]||q[og+24>>2])&&(q[(mg=qg<<2)+q[a+28>>2]>>2]=q[og+12>>2],q[og+24>>2])&&!((0|(pg=q[og+12>>2]))<1))for(pg=(ng=q[og+16>>2])+(pg<<2)|0,wg=q[mg+vg>>2],mg=q[a+36>>2]+(rg<<2)|0,xg=q[tg+976>>2];q[mg>>2]=q[(q[ng>>2]+wg<<2)+xg>>2],mg=mg+4|0,(ng=ng+4|0)>>>0<pg>>>0;);if(q[og+28>>2]&&!((0|(mg=q[og+12>>2]))<1))for(pg=(ng=q[og+20>>2])+(mg<<2)|0,mg=q[a+32>>2]+(rg<<2)|0;q[mg>>2]=q[ng>>2],mg=mg+4|0,(ng=ng+4|0)>>>0<pg>>>0;);if(rg=q[og+8>>2]+rg|0,(0|sg)==(0|(qg=qg+1|0)))break}}(a),n[q[1816]](a+12|0,q[a+36>>2],q[a+44>>2],q[a+40>>2]),function(a){var ue,ve,we,xe,ye,ze,re=0,se=0,te=0;if(1<=(0|(te=q[a+304>>2])))for(we=(re=q[a+308>>2])+(te<<5)|0,xe=q[a+264>>2],ye=q[a+144>>2],ze=q[a+40>>2],te=ve=q[a+312>>2];ue=te,se=0,a=se=!q[re+28>>2]||-1!=(0|(a=q[re+4>>2]))&&(se=0,!q[(a<<2)+ze>>2])||-1!=(0|(a=q[re+8>>2]))&&(se=0,!q[(a<<2)+ve>>2])?se:!q[q[re>>2]+32>>2],q[ue>>2]=a,(ue=q[re+12>>2])>>>0<=1?ue-1?q[(q[re+16>>2]<<2)+ye>>2]=a:q[(q[re+16>>2]<<2)+xe>>2]=a:Y(4,1372,0),te=te+4|0,(re=re+32|0)>>>0<we>>>0;);}(a),function(a){var dg,eg,fg,gg,hg,ig,jg,kg,lg,Rf=0,Sf=0,Tf=0,Uf=0,Vf=0,Wf=0,Xf=0,Yf=0,Zf=0,_f=0,$f=0,ag=0,bg=0,cg=0,Vf=q[a>>2];if(1<=(0|(Yf=q[a+56>>2]))){for(Zf=q[a+60>>2],_f=q[Vf+1052>>2],$f=q[Vf+784>>2];;){if(Rf=q[Zf+w(Wf,24)>>2],(q[Rf+28>>2]||q[Rf+24>>2])&&(q[(Sf=Wf<<2)+q[a+80>>2]>>2]=q[Rf+12>>2],q[Rf+24>>2])&&!((0|(Uf=q[Rf+12>>2]))<1))for(ag=(Tf=q[Rf+16>>2])+(Uf<<2)|0,bg=q[Sf+$f>>2],Sf=(Uf=Xf<<2)+q[a+92>>2]|0,Uf=Uf+q[a+88>>2]|0;cg=bg+q[Tf>>2]<<2,q[Sf>>2]=_f+(q[cg+q[Vf+984>>2]>>2]<<2),q[Uf>>2]=q[cg+q[Vf+980>>2]>>2],Uf=Uf+4|0,Sf=Sf+4|0,(Tf=Tf+4|0)>>>0<ag>>>0;);if(q[Rf+28>>2]&&!((0|(Sf=q[Rf+12>>2]))<1))for(Uf=(Tf=q[Rf+20>>2])+(Sf<<2)|0,Sf=q[a+84>>2]+(Xf<<2)|0;q[Sf>>2]=q[Tf>>2],Sf=Sf+4|0,(Tf=Tf+4|0)>>>0<Uf>>>0;);if(Xf=q[Rf+8>>2]+Xf|0,(0|Yf)==(0|(Wf=Wf+1|0)))break}Vf=q[a>>2]}if(!(r[Vf+4|0]<4||(0|(bg=q[a+56>>2]))<1))for(cg=q[Vf+792>>2],dg=q[a+60>>2],Tf=Xf=0;;){if(Wf=q[w(Tf,24)+dg>>2],q[Wf+24>>2]&&!((0|(Rf=q[Wf+12>>2]))<1))for(eg=(Sf=q[Wf+16>>2])+(Rf<<2)|0,fg=q[cg+(Tf<<2)>>2],Uf=(Rf=Xf<<2)+q[a+96>>2]|0,Yf=Rf+q[a+100>>2]|0,Zf=Rf+q[a+104>>2]|0,_f=Rf+q[a+108>>2]|0,$f=Rf+q[a+112>>2]|0,ag=Rf+q[a+116>>2]|0,gg=q[Vf+1308>>2],hg=q[Vf+1304>>2],ig=q[Vf+1300>>2],jg=q[Vf+1296>>2],kg=q[Vf+1292>>2],lg=q[Vf+1288>>2];Rf=q[Sf>>2]+fg<<2,q[Uf>>2]=q[Rf+lg>>2],q[Yf>>2]=q[Rf+kg>>2],q[Zf>>2]=q[Rf+jg>>2],q[_f>>2]=q[Rf+ig>>2],q[$f>>2]=q[Rf+hg>>2],q[ag>>2]=q[Rf+gg>>2],ag=ag+4|0,$f=$f+4|0,_f=_f+4|0,Zf=Zf+4|0,Yf=Yf+4|0,Uf=Uf+4|0,(Sf=Sf+4|0)>>>0<eg>>>0;);if(Xf=q[Wf+8>>2]+Xf|0,(0|bg)==(0|(Tf=Tf+1|0)))break}}(a),function(a){var uf=0,vf=0,xf=0,yf=0,zf=0,Af=0,Bf=0,Cf=0,Df=0,Ef=0,Ff=0,Gf=0,Hf=0,If=0,Jf=0,Kf=0,Lf=0,Mf=0,Nf=0,Of=0,Pf=0,Qf=q[a+168>>2],wf=q[a>>2];if(1<=(0|(Hf=q[a+164>>2])))for(Jf=q[wf+816>>2];;){if(xf=q[w(Bf,12)+Qf>>2],(q[xf+28>>2]||q[xf+24>>2])&&(q[(Cf=Bf<<2)+q[a+188>>2]>>2]=q[xf+12>>2],q[xf+24>>2])){if(vf=q[xf+16>>2],If=q[Cf+Jf>>2],1<=(0|(uf=q[xf+12>>2])))for(Kf=vf+(uf<<2)|0,yf=(uf=Af<<2)+q[a+200>>2]|0,Df=uf+q[a+204>>2]|0,Ef=uf+q[a+208>>2]|0,Ff=uf+q[a+212>>2]|0,Gf=uf+q[a+196>>2]|0,Lf=q[wf+996>>2],Mf=q[wf+1012>>2],Nf=q[wf+1008>>2],Of=q[wf+1004>>2],Pf=q[wf+1e3>>2],uf=vf;zf=If+q[uf>>2]<<2,q[yf>>2]=q[zf+Pf>>2],q[Df>>2]=q[zf+Of>>2],q[Ef>>2]=q[zf+Nf>>2],q[Ff>>2]=q[zf+Mf>>2],q[Gf>>2]=q[zf+Lf>>2],Gf=Gf+4|0,Ff=Ff+4|0,Ef=Ef+4|0,Df=Df+4|0,yf=yf+4|0,(uf=uf+4|0)>>>0<Kf>>>0;);uf=If+q[vf>>2]<<2,q[Cf+q[a+288>>2]>>2]=q[uf+q[wf+1016>>2]>>2],q[Cf+q[a+292>>2]>>2]=q[uf+q[wf+1020>>2]>>2]}if(q[xf+28>>2]&&!((0|(vf=q[xf+12>>2]))<1))for(vf=(uf=q[xf+20>>2])+(vf<<2)|0,yf=q[a+192>>2]+(Af<<2)|0;q[yf>>2]=q[uf>>2],yf=yf+4|0,(uf=uf+4|0)>>>0<vf>>>0;);if(Af=q[xf+8>>2]+Af|0,(0|Hf)==(0|(Bf=Bf+1|0)))break}if(!(r[wf+4|0]<4||(0|(Cf=q[a+164>>2]))<1))for(If=q[wf+824>>2],Af=xf=0;;){if(zf=q[w(Af,12)+Qf>>2],q[zf+24>>2]&&!((0|(uf=q[zf+12>>2]))<1))for(Hf=(yf=q[zf+16>>2])+(uf<<2)|0,Jf=q[If+(Af<<2)>>2],Df=(vf=xf<<2)+q[a+216>>2]|0,Ef=vf+q[a+220>>2]|0,Ff=vf+q[a+224>>2]|0,Gf=vf+q[a+228>>2]|0,uf=vf+q[a+232>>2]|0,Bf=vf+q[a+236>>2]|0,Kf=q[wf+1308>>2],Lf=q[wf+1304>>2],Mf=q[wf+1300>>2],Nf=q[wf+1296>>2],Of=q[wf+1292>>2],Pf=q[wf+1288>>2];vf=Jf+q[yf>>2]<<2,q[Df>>2]=q[vf+Pf>>2],q[Ef>>2]=q[vf+Of>>2],q[Ff>>2]=q[vf+Nf>>2],q[Gf>>2]=q[vf+Mf>>2],q[uf>>2]=q[vf+Lf>>2],q[Bf>>2]=q[vf+Kf>>2],Bf=Bf+4|0,uf=uf+4|0,Gf=Gf+4|0,Ff=Ff+4|0,Ef=Ef+4|0,Df=Df+4|0,(yf=yf+4|0)>>>0<Hf>>>0;);if(xf=q[zf+8>>2]+xf|0,(0|Cf)==(0|(Af=Af+1|0)))break}}(a),function(a){var wk,ok=0,pk=0,qk=0,rk=0,sk=0,tk=0,uk=0,vk=0,ok=a- -64|0;if(n[q[1815]](ok,q[a+88>>2],q[a+148>>2],q[a+144>>2]),n[q[1817]](ok,q[a+92>>2],q[a+152>>2],q[q[a>>2]+796>>2],2,q[a+144>>2]),!(r[q[a>>2]+4|0]<4||(n[q[1815]](ok,q[a+96>>2],q[a+120>>2],q[a+144>>2]),n[q[1815]](ok,q[a+100>>2],q[a+124>>2],q[a+144>>2]),n[q[1815]](ok,q[a+104>>2],q[a+128>>2],q[a+144>>2]),n[q[1815]](ok,q[a+108>>2],q[a+132>>2],q[a+144>>2]),n[q[1815]](ok,q[a+112>>2],q[a+136>>2],q[a+144>>2]),n[q[1815]](ok,q[a+116>>2],q[a+140>>2],q[a+144>>2]),(0|(tk=q[a+56>>2]))<1))){for(uk=q[a+128>>2],vk=q[a+124>>2],wk=q[a+120>>2],pk=q[a+156>>2],ok=0;q[(qk=rk<<2)+pk>>2]=q[(sk=ok<<2)+wk>>2],q[pk+(4|qk)>>2]=q[sk+vk>>2],q[pk+(8|qk)>>2]=q[sk+uk>>2],rk=rk+4|0,(0|tk)!=(0|(ok=ok+1|0)););for(pk=q[a+160>>2],sk=q[a+140>>2],uk=q[a+136>>2],vk=q[a+132>>2],ok=a=0;q[(rk=a<<2)+pk>>2]=q[(qk=ok<<2)+vk>>2],q[pk+(4|rk)>>2]=q[qk+uk>>2],q[pk+(8|rk)>>2]=q[qk+sk>>2],a=a+4|0,(0|tk)!=(0|(ok=ok+1|0)););}}(a),function(a){var nk,fk=0,gk=0,hk=0,ik=0,jk=0,kk=0,lk=0,mk=0,fk=a+172|0;if(n[q[1815]](fk,q[a+196>>2],q[a+268>>2],q[a+264>>2]),n[q[1815]](fk,q[a+200>>2],q[a+284>>2],q[a+264>>2]),n[q[1815]](fk,q[a+204>>2],q[a+276>>2],q[a+264>>2]),n[q[1815]](fk,q[a+208>>2],q[a+280>>2],q[a+264>>2]),n[q[1815]](fk,q[a+212>>2],q[a+272>>2],q[a+264>>2]),!(r[q[a>>2]+4|0]<4||(n[q[1815]](fk,q[a+216>>2],q[a+240>>2],q[a+264>>2]),n[q[1815]](fk,q[a+220>>2],q[a+244>>2],q[a+264>>2]),n[q[1815]](fk,q[a+224>>2],q[a+248>>2],q[a+264>>2]),n[q[1815]](fk,q[a+228>>2],q[a+252>>2],q[a+264>>2]),n[q[1815]](fk,q[a+232>>2],q[a+256>>2],q[a+264>>2]),n[q[1815]](fk,q[a+236>>2],q[a+260>>2],q[a+264>>2]),(0|(kk=q[a+164>>2]))<1))){for(lk=q[a+248>>2],mk=q[a+244>>2],nk=q[a+240>>2],gk=q[a+296>>2],fk=0;q[(hk=ik<<2)+gk>>2]=q[(jk=fk<<2)+nk>>2],q[gk+(4|hk)>>2]=q[jk+mk>>2],q[gk+(8|hk)>>2]=q[jk+lk>>2],ik=ik+4|0,(0|kk)!=(0|(fk=fk+1|0)););for(gk=q[a+300>>2],jk=q[a+260>>2],lk=q[a+256>>2],mk=q[a+252>>2],fk=a=0;q[(ik=a<<2)+gk>>2]=q[(hk=fk<<2)+mk>>2],q[gk+(4|ik)>>2]=q[hk+lk>>2],q[gk+(8|ik)>>2]=q[hk+jk>>2],a=a+4|0,(0|kk)!=(0|(fk=fk+1|0)););}}(a),function(a){var oe,pe,qe,le=0,me=0,ne=0;if(1<=(0|(me=q[a+332>>2])))for(oe=(le=q[a+336>>2])+w(me,20)|0,pe=q[a+312>>2],qe=q[a+40>>2],a=q[a+424>>2];me=0,q[le+12>>2]&&(ne=q[le+4>>2],q[(ne<<2)+qe>>2]||-1==(0|ne))&&(ne=q[le+8>>2],q[(ne<<2)+pe>>2]||-1==(0|ne))&&(me=!q[q[le>>2]+32>>2]),q[a>>2]=me,a=a+4|0,(le=le+20|0)>>>0<oe>>>0;);}(a),function(a){var mf,nf,of,pf,qf,rf,sf,tf,Ye=0,Ze=0,_e=0,$e=0,af=0,bf=0,cf=0,df=0,ef=0,ff=0,gf=0,hf=0,jf=0,kf=0,lf=0,$e=q[a>>2];if(1<=(0|(ff=q[a+332>>2]))){for(gf=q[a+336>>2],hf=q[$e+1052>>2],jf=q[$e+856>>2];;){if(Ye=q[gf+w(cf,20)>>2],(q[Ye+28>>2]||q[Ye+24>>2])&&(q[(Ze=cf<<2)+q[a+356>>2]>>2]=q[Ye+12>>2],q[Ye+24>>2])&&!((0|(af=q[Ye+12>>2]))<1))for(kf=(_e=q[Ye+16>>2])+(af<<2)|0,lf=q[Ze+jf>>2],Ze=(bf=df<<2)+q[a+372>>2]|0,af=bf+q[a+364>>2]|0,bf=bf+q[a+368>>2]|0;ef=lf+q[_e>>2]<<2,q[Ze>>2]=hf+(q[ef+q[$e+1040>>2]>>2]<<2),q[af>>2]=q[ef+q[$e+1032>>2]>>2],q[bf>>2]=q[ef+q[$e+1036>>2]>>2],bf=bf+4|0,af=af+4|0,Ze=Ze+4|0,(_e=_e+4|0)>>>0<kf>>>0;);if(q[Ye+28>>2]&&!((0|(Ze=q[Ye+12>>2]))<1))for(af=(_e=q[Ye+20>>2])+(Ze<<2)|0,Ze=q[a+360>>2]+(df<<2)|0;q[Ze>>2]=q[_e>>2],Ze=Ze+4|0,(_e=_e+4|0)>>>0<af>>>0;);if(df=q[Ye+8>>2]+df|0,(0|ff)==(0|(cf=cf+1|0)))break}$e=q[a>>2]}if(!(r[$e+4|0]<4||(0|(jf=q[a+332>>2]))<1))for(kf=q[$e+864>>2],lf=q[a+336>>2],_e=df=0;;){if(cf=q[lf+w(_e,20)>>2],q[cf+24>>2]&&!((0|(Ye=q[cf+12>>2]))<1))for(mf=(Ze=q[cf+16>>2])+(Ye<<2)|0,nf=q[kf+(_e<<2)>>2],af=(Ye=df<<2)+q[a+376>>2]|0,bf=Ye+q[a+380>>2]|0,ef=Ye+q[a+384>>2]|0,ff=Ye+q[a+388>>2]|0,gf=Ye+q[a+392>>2]|0,hf=Ye+q[a+396>>2]|0,of=q[$e+1308>>2],pf=q[$e+1304>>2],qf=q[$e+1300>>2],rf=q[$e+1296>>2],sf=q[$e+1292>>2],tf=q[$e+1288>>2];Ye=q[Ze>>2]+nf<<2,q[af>>2]=q[Ye+tf>>2],q[bf>>2]=q[Ye+sf>>2],q[ef>>2]=q[Ye+rf>>2],q[ff>>2]=q[Ye+qf>>2],q[gf>>2]=q[Ye+pf>>2],q[hf>>2]=q[Ye+of>>2],hf=hf+4|0,gf=gf+4|0,ff=ff+4|0,ef=ef+4|0,bf=bf+4|0,af=af+4|0,(Ze=Ze+4|0)>>>0<mf>>>0;);if(df=q[cf+8>>2]+df|0,(0|jf)==(0|(_e=_e+1|0)))break}}(a),function(a){var ek,tj=0,vj=0,wj=0,$j=0,ak=0,bk=0,ck=0,dk=0,tj=a+340|0;if(n[q[1815]](tj,q[a+364>>2],q[a+448>>2],q[a+424>>2]),n[q[1816]](tj,q[a+368>>2],q[a+440>>2],q[a+424>>2]),n[q[1817]](tj,q[a+372>>2],q[a+444>>2],q[q[a>>2]+892>>2],2,q[a+424>>2]),!(r[q[a>>2]+4|0]<4||(n[q[1815]](tj,q[a+376>>2],q[a+400>>2],q[a+424>>2]),n[q[1815]](tj,q[a+380>>2],q[a+404>>2],q[a+424>>2]),n[q[1815]](tj,q[a+384>>2],q[a+408>>2],q[a+424>>2]),n[q[1815]](tj,q[a+388>>2],q[a+412>>2],q[a+424>>2]),n[q[1815]](tj,q[a+392>>2],q[a+416>>2],q[a+424>>2]),n[q[1815]](tj,q[a+396>>2],q[a+420>>2],q[a+424>>2]),(0|(bk=q[a+332>>2]))<1))){for(ck=q[a+408>>2],dk=q[a+404>>2],ek=q[a+400>>2],vj=q[a+452>>2],tj=0;q[(wj=$j<<2)+vj>>2]=q[(ak=tj<<2)+ek>>2],q[vj+(4|wj)>>2]=q[ak+dk>>2],q[vj+(8|wj)>>2]=q[ak+ck>>2],$j=$j+4|0,(0|bk)!=(0|(tj=tj+1|0)););for(vj=q[a+456>>2],ak=q[a+420>>2],ck=q[a+416>>2],dk=q[a+412>>2],tj=a=0;q[($j=a<<2)+vj>>2]=q[(wj=tj<<2)+dk>>2],q[vj+(4|$j)>>2]=q[wj+ck>>2],q[vj+(8|$j)>>2]=q[wj+ak>>2],a=a+4|0,(0|bk)!=(0|(tj=tj+1|0)););}}(a),function(a){var Oe,Se,Te,Ue,Ve,We,Xe,Me=0,Ne=0,Pe=0,Qe=0,Re=0;if(1<=(0|(Se=q[a+500>>2])))for(Ue=q[a+504>>2],Te=q[a>>2],Ve=q[Te+1252>>2];;){if(Oe=q[w(Qe,24)+Ue>>2],(q[Oe+28>>2]||q[Oe+24>>2])&&(q[(Me=Qe<<2)+q[a+524>>2]>>2]=q[Oe+12>>2],q[Oe+24>>2])&&!((0|(Pe=q[Oe+12>>2]))<1))for(Pe=(Ne=q[Oe+16>>2])+(Pe<<2)|0,We=q[Me+Ve>>2],Me=q[a+532>>2]+(Re<<2)|0,Xe=q[Te+1284>>2];q[Me>>2]=q[(q[Ne>>2]+We<<2)+Xe>>2],Me=Me+4|0,(Ne=Ne+4|0)>>>0<Pe>>>0;);if(q[Oe+28>>2]&&!((0|(Me=q[Oe+12>>2]))<1))for(Pe=(Ne=q[Oe+20>>2])+(Me<<2)|0,Me=q[a+528>>2]+(Re<<2)|0;q[Me>>2]=q[Ne>>2],Me=Me+4|0,(Ne=Ne+4|0)>>>0<Pe>>>0;);if(Re=q[Oe+8>>2]+Re|0,(0|Se)==(0|(Qe=Qe+1|0)))break}}(a),n[q[1815]](a+508|0,q[a+532>>2],q[a+536>>2],0),function(a){var Ck,Dk,Ek,Fk,Gk,Hk,xk=x(0),yk=0,zk=0,Ak=0,Bk=0;x(0);if(L=Ck=L-16|0,Ak=q[a>>2],!(r[Ak+4|0]<5||(0|(Bk=q[a+596>>2]))<1))for(Fk=(zk=q[a+600>>2])+w(Bk,12)|0,Gk=q[a+44>>2],Bk=q[Ak+976>>2];;){if(Ak=(q[zk>>2]<<2)+Gk|0,xk=x(q[Ak>>2]),1<=(0|(yk=q[zk+4>>2])))for(Hk=(a=q[zk+8>>2])+w(yk,48)|0;(yk=q[a+8>>2])&&((Dk=yk+-1|0)>>>0<=1?(yk=q[a+4>>2],Ek=u[Bk+(yk+q[a+12>>2]<<2)>>2],xk=x(Dk-1?xk+x(u[a+44>>2]*x(Ek*u[a+20>>2])):xk+x(u[a+44>>2]*x(x(Ek*u[a+20>>2])+x(u[Bk+(yk+q[a+16>>2]<<2)>>2]*u[a+24>>2]))))):(q[Ck>>2]=yk,Y(4,1024,Ck))),(a=a+48|0)>>>0<Hk>>>0;);if(xk=(xk=x(xk+x(.0010000000474974513)))<x(0)?x(0):x(A(xk,x(1e3))),a=x(y(xk))<x(2147483648)?~~xk:-2147483648,q[Ak>>2]=a,!((zk=zk+12|0)>>>0<Fk>>>0))break}L=16+Ck|0}(a),function(a){var kj,lj,mj,nj,oj,pj,qj,rj,sj,cj=0,ej=0,gj=0,hj=0,ij=0,jj=x(0);if(L=kj=L-16|0,cj=q[a>>2],!(r[cj+4|0]<4||(va(a,q[a+604>>2],q[a+608>>2],q[cj+984>>2],q[a+152>>2],q[cj+796>>2]),ej=q[a>>2],r[ej+4|0]<5))){if(gj=q[a+608>>2],oj=q[ej+992>>2],pj=q[ej+988>>2],1<=(0|(cj=q[a+604>>2]))){for(qj=w(cj,12)+gj|0,rj=q[a+148>>2],lj=q[ej+980>>2];;){if(mj=(q[gj>>2]<<2)+rj|0,ij=q[mj>>2],1<=(0|(hj=q[gj+4>>2])))for(sj=(cj=q[gj+8>>2])+w(hj,48)|0;(hj=q[cj+8>>2])&&((nj=hj+-1|0)>>>0<=1?(hj=q[cj+4>>2],jj=u[(hj+q[cj+12>>2]<<2)+lj>>2],j(x(nj-1?x(u[cj+44>>2]*x(jj*u[cj+20>>2]))+(f(0,ij),k()):x(u[cj+44>>2]*x(x(jj*u[cj+20>>2])+x(u[(hj+q[cj+16>>2]<<2)+lj>>2]*u[cj+24>>2])))+(f(0,ij),k()))),ij=b[0]):(q[kj>>2]=hj,Y(4,1024,kj))),(cj=cj+48|0)>>>0<sj>>>0;);if(f(0,ij),jj=k(),u[mj>>2]=jj<x(0)?x(0):x(A(jj,x(1))),!((gj=gj+12|0)>>>0<qj>>>0))break}gj=q[a+608>>2],cj=q[a+604>>2]}fa(cj,gj,pj,q[ej+1288>>2],q[ej+1292>>2],q[ej+1296>>2],q[a+156>>2]),fa(q[a+604>>2],q[a+608>>2],oj,q[ej+1300>>2],q[ej+1304>>2],q[ej+1308>>2],q[a+160>>2])}L=16+kj|0}(a),function(a){var yi,ri=0,si=0,ti=0,ui=0,vi=0,wi=x(0),xi=0,zi=0,Ai=0,Bi=0,Ci=0,Di=0,Ei=0,Fi=0;if(L=yi=L-80|0,vi=q[a>>2],!(r[vi+4|0]<5)){if(Di=q[vi+1028>>2],Ei=q[vi+1024>>2],ti=si=q[a+616>>2],!((0|(ri=q[a+612>>2]))<1)){for(zi=w(ri,12)+si|0,Ai=q[a+276>>2],xi=q[vi+1004>>2];;){if(Bi=Ai+(q[si>>2]<<2)|0,ti=q[Bi>>2],1<=(0|(ui=q[si+4>>2])))for(Fi=(ri=q[si+8>>2])+w(ui,48)|0;(ui=q[ri+8>>2])&&((Ci=ui+-1|0)>>>0<=1?(ui=q[ri+4>>2],wi=u[xi+(ui+q[ri+12>>2]<<2)>>2],j(x(Ci-1?x(u[ri+44>>2]*x(wi*u[ri+20>>2]))+(f(0,ti),k()):x(u[ri+44>>2]*x(x(wi*u[ri+20>>2])+x(u[xi+(ui+q[ri+16>>2]<<2)>>2]*u[ri+24>>2])))+(f(0,ti),k()))),ti=b[0]):(q[64+yi>>2]=ui,Y(4,1024,64+yi|0))),(ri=ri+48|0)>>>0<Fi>>>0;);if(q[Bi>>2]=ti,!((si=si+12|0)>>>0<zi>>>0))break}if(ti=si=q[a+616>>2],!((0|(ri=q[a+612>>2]))<1)){for(zi=w(ri,12)+si|0,Ai=q[a+280>>2],xi=q[q[a>>2]+1008>>2];;){if(Bi=Ai+(q[si>>2]<<2)|0,ti=q[Bi>>2],1<=(0|(ui=q[si+4>>2])))for(Fi=(ri=q[si+8>>2])+w(ui,48)|0;(ui=q[ri+8>>2])&&((Ci=ui+-1|0)>>>0<=1?(ui=q[ri+4>>2],wi=u[xi+(ui+q[ri+12>>2]<<2)>>2],j(x(Ci-1?x(u[ri+44>>2]*x(wi*u[ri+20>>2]))+(f(0,ti),k()):x(u[ri+44>>2]*x(x(wi*u[ri+20>>2])+x(u[xi+(ui+q[ri+16>>2]<<2)>>2]*u[ri+24>>2])))+(f(0,ti),k()))),ti=b[0]):(q[48+yi>>2]=ui,Y(4,1024,48+yi|0))),(ri=ri+48|0)>>>0<Fi>>>0;);if(q[Bi>>2]=ti,!((si=si+12|0)>>>0<zi>>>0))break}if(ti=si=q[a+616>>2],!((0|(ri=q[a+612>>2]))<1)){for(zi=w(ri,12)+si|0,Ai=q[a+268>>2],xi=q[q[a>>2]+996>>2];;){if(Bi=Ai+(q[si>>2]<<2)|0,ti=q[Bi>>2],1<=(0|(ui=q[si+4>>2])))for(Fi=(ri=q[si+8>>2])+w(ui,48)|0;(ui=q[ri+8>>2])&&((Ci=ui+-1|0)>>>0<=1?(ui=q[ri+4>>2],wi=u[xi+(ui+q[ri+12>>2]<<2)>>2],j(x(Ci-1?x(u[ri+44>>2]*x(wi*u[ri+20>>2]))+(f(0,ti),k()):x(u[ri+44>>2]*x(x(wi*u[ri+20>>2])+x(u[xi+(ui+q[ri+16>>2]<<2)>>2]*u[ri+24>>2])))+(f(0,ti),k()))),ti=b[0]):(q[32+yi>>2]=ui,Y(4,1024,32+yi|0))),(ri=ri+48|0)>>>0<Fi>>>0;);if(f(0,ti),wi=k(),u[Bi>>2]=wi<x(0)?x(0):x(A(wi,x(1))),!((si=si+12|0)>>>0<zi>>>0))break}ri=q[a+612>>2],ti=q[a+616>>2]}}}if(fa(ri,ti,Ei,q[vi+1288>>2],q[vi+1292>>2],q[vi+1296>>2],q[a+296>>2]),fa(q[a+612>>2],q[a+616>>2],Di,q[vi+1300>>2],q[vi+1304>>2],q[vi+1308>>2],q[a+300>>2]),!((0|(ri=q[a+612>>2]))<1)){for(ui=(si=q[a+616>>2])+w(ri,12)|0,Di=q[a+284>>2],vi=q[q[a>>2]+1e3>>2];;){if(Ei=Di+(q[si>>2]<<2)|0,ti=q[Ei>>2],1<=(0|(xi=q[si+4>>2])))for(zi=(ri=q[si+8>>2])+w(xi,48)|0;(xi=q[ri+8>>2])&&((Ai=xi+-1|0)>>>0<=1?(xi=q[ri+4>>2],wi=u[vi+(xi+q[ri+12>>2]<<2)>>2],j(x(Ai-1?x(u[ri+44>>2]*x(wi*u[ri+20>>2]))+(f(0,ti),k()):x(u[ri+44>>2]*x(x(wi*u[ri+20>>2])+x(u[vi+(xi+q[ri+16>>2]<<2)>>2]*u[ri+24>>2])))+(f(0,ti),k()))),ti=b[0]):(q[16+yi>>2]=xi,Y(4,1024,16+yi|0))),(ri=ri+48|0)>>>0<zi>>>0;);if(f(0,ti),wi=k(),u[Ei>>2]=wi<x(-3600)?x(-3600):x(A(wi,x(3600))),!((si=si+12|0)>>>0<ui>>>0))break}if(!((0|(ri=q[a+612>>2]))<1))for(xi=(si=q[a+616>>2])+w(ri,12)|0,ui=q[a+272>>2],a=q[q[a>>2]+1012>>2];;){if(Di=ui+(q[si>>2]<<2)|0,ti=q[Di>>2],1<=(0|(vi=q[si+4>>2])))for(Ei=(ri=q[si+8>>2])+w(vi,48)|0;(vi=q[ri+8>>2])&&((zi=vi+-1|0)>>>0<=1?(vi=q[ri+4>>2],wi=u[a+(vi+q[ri+12>>2]<<2)>>2],j(x(zi-1?x(u[ri+44>>2]*x(wi*u[ri+20>>2]))+(f(0,ti),k()):x(u[ri+44>>2]*x(x(wi*u[ri+20>>2])+x(u[a+(vi+q[ri+16>>2]<<2)>>2]*u[ri+24>>2])))+(f(0,ti),k()))),ti=b[0]):(q[yi>>2]=vi,Y(4,1024,yi))),(ri=ri+48|0)>>>0<Ei>>>0;);if(f(0,ti),wi=k(),u[Di>>2]=wi<x(9999999747378752e-20)?x(9999999747378752e-20):x(A(wi,x(100))),!((si=si+12|0)>>>0<xi>>>0))break}}}L=80+yi|0}(a),function(a){var Zh,di,ei,fi,gi,Sh=0,Uh=0,Vh=0,Wh=0,Xh=x(0),Yh=0,_h=0,$h=0,ai=0,bi=0,ci=0;x(0);if(L=Zh=L-32|0,Vh=q[a>>2],!(r[Vh+4|0]<4||(va(a,q[a+620>>2],q[a+624>>2],q[Vh+1040>>2],q[a+444>>2],q[Vh+892>>2]),Yh=q[a>>2],r[Yh+4|0]<5))){if(fi=q[Yh+1048>>2],gi=q[Yh+1044>>2],Vh=Wh=q[a+624>>2],!((0|(Sh=q[a+620>>2]))<1)){for(bi=w(Sh,12)+Wh|0,ci=q[a+440>>2],_h=q[Yh+1036>>2];;){if(Vh=ci+(q[Wh>>2]<<2)|0,Xh=x(q[Vh>>2]),1<=(0|(Uh=q[Wh+4>>2])))for($h=(Sh=q[Wh+8>>2])+w(Uh,48)|0;(Uh=q[Sh+8>>2])&&((ai=Uh+-1|0)>>>0<=1?(Uh=q[Sh+4>>2],di=u[_h+(Uh+q[Sh+12>>2]<<2)>>2],Xh=x(ai-1?Xh+x(u[Sh+44>>2]*x(di*u[Sh+20>>2])):Xh+x(u[Sh+44>>2]*x(x(di*u[Sh+20>>2])+x(u[_h+(Uh+q[Sh+16>>2]<<2)>>2]*u[Sh+24>>2]))))):(q[16+Zh>>2]=Uh,Y(4,1024,16+Zh|0))),(Sh=Sh+48|0)>>>0<$h>>>0;);if(Xh=(Xh=x(Xh+x(.0010000000474974513)))<x(0)?x(0):x(A(Xh,x(1e3))),Sh=x(y(Xh))<x(2147483648)?~~Xh:-2147483648,q[Vh>>2]=Sh,!((Wh=Wh+12|0)>>>0<bi>>>0))break}if(Vh=Wh=q[a+624>>2],!((0|(Sh=q[a+620>>2]))<1)){for(bi=w(Sh,12)+Wh|0,ci=q[a+448>>2],_h=q[q[a>>2]+1032>>2];;){if($h=ci+(q[Wh>>2]<<2)|0,Vh=q[$h>>2],1<=(0|(Uh=q[Wh+4>>2])))for(ai=(Sh=q[Wh+8>>2])+w(Uh,48)|0;(Uh=q[Sh+8>>2])&&((ei=Uh+-1|0)>>>0<=1?(Uh=q[Sh+4>>2],Xh=u[_h+(Uh+q[Sh+12>>2]<<2)>>2],j(x(ei-1?x(u[Sh+44>>2]*x(Xh*u[Sh+20>>2]))+(f(0,Vh),k()):x(u[Sh+44>>2]*x(x(Xh*u[Sh+20>>2])+x(u[_h+(Uh+q[Sh+16>>2]<<2)>>2]*u[Sh+24>>2])))+(f(0,Vh),k()))),Vh=b[0]):(q[Zh>>2]=Uh,Y(4,1024,Zh))),(Sh=Sh+48|0)>>>0<ai>>>0;);if(f(0,Vh),Xh=k(),u[$h>>2]=Xh<x(0)?x(0):x(A(Xh,x(1))),!((Wh=Wh+12|0)>>>0<bi>>>0))break}Sh=q[a+620>>2],Vh=q[a+624>>2]}}fa(Sh,Vh,gi,q[Yh+1288>>2],q[Yh+1292>>2],q[Yh+1296>>2],q[a+452>>2]),fa(q[a+620>>2],q[a+624>>2],fi,q[Yh+1300>>2],q[Yh+1304>>2],q[Yh+1308>>2],q[a+456>>2])}L=32+Zh|0}(a),function(a){var Og,Pg,Qg,Rg,Sg,Tg,Jg=0,Kg=0,Lg=0,Mg=0,Ng=x(0);if(L=Og=L-16|0,Kg=q[a>>2],!(r[Kg+4|0]<5||(0|(Mg=q[a+628>>2]))<1))for(Rg=(Lg=q[a+632>>2])+w(Mg,12)|0,Sg=q[a+536>>2],Mg=q[Kg+1284>>2];;){if(Pg=(q[Lg>>2]<<2)+Sg|0,Kg=q[Pg>>2],1<=(0|(Jg=q[Lg+4>>2])))for(Tg=(a=q[Lg+8>>2])+w(Jg,48)|0;(Jg=q[a+8>>2])&&((Qg=Jg+-1|0)>>>0<=1?(Jg=q[a+4>>2],Ng=u[Mg+(Jg+q[a+12>>2]<<2)>>2],j(x(Qg-1?x(u[a+44>>2]*x(Ng*u[a+20>>2]))+(f(0,Kg),k()):x(u[a+44>>2]*x(x(Ng*u[a+20>>2])+x(u[Mg+(Jg+q[a+16>>2]<<2)>>2]*u[a+24>>2])))+(f(0,Kg),k()))),Kg=b[0]):(q[Og>>2]=Jg,Y(4,1024,Og))),(a=a+48|0)>>>0<Tg>>>0;);if(f(0,Kg),Ng=k(),u[Pg>>2]=Ng<x(0)?x(0):x(A(Ng,x(1))),!((Lg=Lg+12|0)>>>0<Rg>>>0))break}L=16+Og|0}(a),function(a){var Nh,Qh,jh=0,Lh=0,Mh=0,Oh=x(0),Ph=0;if(1<=(0|(jh=q[a+4>>2])))for(Qh=(Lh=q[a+8>>2])+w(jh,12)|0,jh=q[a+40>>2],Mh=q[a+52>>2],a=Nh=q[a+48>>2];q[jh>>2]&&(Oh=u[Mh>>2],u[a>>2]=Oh,-1!=(0|(Ph=q[Lh+4>>2])))&&(u[a>>2]=Oh*u[(Ph<<2)+Nh>>2]),a=a+4|0,Mh=Mh+4|0,jh=jh+4|0,(Lh=Lh+12|0)>>>0<Qh>>>0;);}(a),function(a){var ih,fh=0,gh=0,hh=0;if(1<=(0|(ih=q[a+304>>2])))for(fh=q[a+308>>2],gh=q[a+312>>2];q[gh>>2]&&n[q[fh+20>>2]](a,hh),gh=gh+4|0,fh=fh+32|0,(0|ih)!=(0|(hh=hh+1|0)););}(a),function(a){var Wg,Xg,$g,dh,eh,Ug=0,Vg=0,Yg=(x(0),x(0),0),Zg=0,_g=0,ah=(x(0),0),bh=0,ch=0;if(1<=(0|(Ug=q[a+332>>2])))for(bh=(Vg=q[a+336>>2])+w(Ug,20)|0,ch=q[a+308>>2],ah=q[a+316>>2],eh=q[a+48>>2],Ug=q[a+448>>2],Yg=q[a+444>>2],_g=q[a+424>>2];q[_g>>2]&&(-1!=(0|(Zg=q[Vg+4>>2]))&&(u[Ug>>2]=u[(Zg<<2)+eh>>2]*u[Ug>>2]),-1!=(0|(Zg=q[Vg+8>>2])))&&(u[Ug>>2]=u[ah+(Zg<<2)>>2]*u[Ug>>2],dh=q[Yg>>2],n[q[24+(ch+(Zg<<5)|0)>>2]](a,Zg,dh,dh,q[Vg+16>>2])),Yg=Yg+4|0,Ug=Ug+4|0,_g=_g+4|0,(Vg=Vg+20|0)>>>0<bh>>>0;);if(!(r[q[a>>2]+4|0]<4||(0|(Ug=q[a+332>>2]))<1))for(Zg=(Yg=q[a+336>>2])+w(Ug,20)|0,bh=q[a+328>>2],ch=q[a+324>>2],Vg=q[a+452>>2],Ug=q[a+456>>2],_g=q[a+424>>2];q[_g>>2]&&-1!=(0|(a=q[Yg+8>>2]))&&(a=(ah=a<<4)+ch|0,Wg=x(u[Vg>>2]*u[a>>2]),u[Vg>>2]=Wg,Xg=x(u[Vg+4>>2]*u[a+4>>2]),u[Vg+4>>2]=Xg,$g=u[a+8>>2],q[Vg+12>>2]=1065353216,u[Vg+4>>2]=Xg<x(0)?x(0):x(A(Xg,x(1))),u[Vg>>2]=Wg<x(0)?x(0):x(A(Wg,x(1))),Wg=x($g*u[Vg+8>>2]),u[Vg+8>>2]=Wg<x(0)?x(0):x(A(Wg,x(1))),Wg=u[Ug>>2],Xg=u[(a=bh+ah|0)>>2],Wg=x(x(Wg+Xg)-x(Wg*Xg)),u[Ug>>2]=Wg,Xg=u[Ug+4>>2],$g=u[a+4>>2],Xg=x(x(Xg+$g)-x(Xg*$g)),u[Ug+4>>2]=Xg,$g=u[a+8>>2],q[Ug+12>>2]=1065353216,u[Ug+4>>2]=Xg<x(0)?x(0):x(A(Xg,x(1))),u[Ug>>2]=Wg<x(0)?x(0):x(A(Wg,x(1))),Wg=u[Ug+8>>2],Wg=x(x($g+Wg)-x(Wg*$g)),u[Ug+8>>2]=Wg<x(0)?x(0):x(A(Wg,x(1)))),Ug=Ug+16|0,Vg=Vg+16|0,_g=_g+4|0,(Yg=Yg+20|0)>>>0<Zg>>>0;);}(a),function(a){var Mn,Nn,Pn,Lo,Mo,No,Oo,Po,Qo,Ro,So,To,Uo,Vo,Wo,Xo,Yo,Zo,_o,$o,On=0;x(0),x(0),x(0),x(0),x(0),x(0),x(0);if(1<=(0|(Po=q[a+500>>2])))for(_o=q[a+536>>2],Qo=q[a+444>>2],$o=q[a+504>>2];;){if(a=w(On,24)+$o|0,0<(0|(Ro=q[a+12>>2])))for(Pn=u[(On<<2)+_o>>2],So=q[a+20>>2],To=q[a+16>>2],Uo=q[(q[a+4>>2]<<2)+Qo>>2],Vo=q[(q[a+8>>2]<<2)+Qo>>2],a=0;Wo=u[((Mn=1|a)<<2)+To>>2],Nn=s[(a<<1)+So>>1]<<3&262136,Lo=u[(Xo=(4|Nn)+Uo|0)>>2],Mn=s[(Mn<<1)+So>>1]<<3&262136,Mo=u[(Yo=(4|Mn)+Vo|0)>>2],No=u[(Nn=Nn+Uo|0)>>2],Zo=u[(a<<2)+To>>2],Oo=u[(Mn=Mn+Vo|0)>>2],u[Nn>>2]=No+x(Pn*x(Zo*x(Oo-No))),u[Xo>>2]=Lo+x(Pn*x(Zo*x(Mo-Lo))),u[Mn>>2]=Oo+x(Pn*x(Wo*x(No-Oo))),u[Yo>>2]=Mo+x(Pn*x(Wo*x(Lo-Mo))),(0|(a=a+2|0))<(0|Ro););if(!((0|(On=On+1|0))<(0|Po)))break}}(a),n[q[1818]](a),function(a){var Oc,Pc,Rc,Sc,Ec=0,Fc=0,Gc=0,Hc=0,Ic=0,Jc=0,Kc=0,Lc=0,Mc=0,Nc=0,Qc=0;if(!((0|(Oc=q[a+480>>2]))<1)){for(Hc=(Pc=q[a+484>>2])+w(Oc,28)|0,Kc=q[a+424>>2],Lc=q[a+40>>2],Ic=q[a+44>>2],Qc=q[a+440>>2],Ec=Pc;;){if(1<=(0|(Jc=q[Ec+4>>2])))for(Nc=Ec+20|0,Mc=q[Ec+12>>2],Fc=0;Rc=q[4+(Gc=Mc+(Fc<<4)|0)>>2]<<2,Gc=1==q[(Sc=Gc)>>2],q[Sc+12>>2]=q[(q[(Gc?Lc:Kc)+Rc>>2]?(Gc?Ic:Qc)+Rc|0:Nc)>>2],(0|(Fc=Fc+1|0))<(0|Jc););if(!((Ec=Ec+28|0)>>>0<Hc>>>0))break}if(!((0|Oc)<1))for(Qc=q[a+436>>2],Lc=0;;){if(Hc=w(Lc,28)+Pc|0,!(q[(Kc=Hc)+24>>2]<1)){for(Gc=q[a+488>>2],Fc=0;q[Gc+(Fc<<2)>>2]=-1,(0|(Fc=Fc+1|0))<(0|(Ec=q[Kc+24>>2])););if(!((0|Ec)<1))for(Ec=q[a+496>>2],Fc=0;q[Ec+(Fc<<2)>>2]=-1,(0|(Fc=Fc+1|0))<q[Kc+24>>2];);}if(!(q[Hc+4>>2]<1)){for(Ic=q[a+492>>2],Fc=0;q[Ic+(Fc<<2)>>2]=-1,(0|(Fc=Fc+1|0))<(0|(Ec=q[Hc+4>>2])););if(!((0|Ec)<1))for(Jc=q[Hc+12>>2],Nc=q[a+496>>2],Fc=0;Mc=q[12+(Jc+(Fc<<4)|0)>>2]-q[Hc+20>>2]<<2,Ec=-1==(0|(Ec=q[(Gc=Mc+Nc|0)>>2]))?Mc+q[a+488>>2]|0:Ic+(Ec<<2)|0,q[Ec>>2]=Fc,(0|(Fc=(q[Gc>>2]=Fc)+1|0))<q[Hc+4>>2];);}if(1<=(0|(Ec=q[Kc+24>>2])))for(Ic=q[Hc+8>>2],Nc=q[a+488>>2],Jc=0;;){if(-1!=(0|(Fc=q[Nc+(Jc<<2)>>2]))){for(Mc=q[a+492>>2],Gc=q[Hc+12>>2];Ic=(Ec=1==q[(Ec=Gc+(Fc<<4)|0)>>2]?(Ec=w(q[Ec+8>>2],28)+Pc|0,q[Ec+8>>2]=Ic,q[Ec>>2]):(q[Qc+(q[Ec+4>>2]<<2)>>2]=Ic,1))+Ic|0,(0|Fc)<(0|(Ec=q[Mc+(Fc<<2)>>2]))&&-1!=(0|(Fc=Ec)););Ec=q[Kc+24>>2]}if(!((0|(Jc=Jc+1|0))<(0|Ec)))break}if((0|Oc)==(0|(Lc=Lc+1|0)))break}}}(a),function(a){var yg=0,zg=0,Ag=0,Bg=0,Dg=0,Eg=x(0),Fg=0,Gg=0,Cg=q[a+332>>2];if(q[a+644>>2]){if(!(((q[a+428>>2]=0)|Cg)<1))for(;yg=126,Fg=q[a+432>>2]+Ag|0,!q[(zg=Ag<<2)+q[a+424>>2]>>2]|u[zg+q[a+448>>2]>>2]==x(0)||(yg=127),o[0|Fg]=yg,(0|Cg)!=(0|(Ag=Ag+1|0)););}else if(q[a+428>>2]){if(yg=r[q[a>>2]+4|0],!(((q[a+428>>2]=0)|Cg)<1))if(4<=yg>>>0)for(;Eg=u[(yg=Ag<<2)+q[a+448>>2]>>2],Bg=q[yg+q[a+424>>2]>>2],zg=Eg!=x(0)&0!=(0|Bg),Fg=q[a+432>>2]+Ag|0,zg=(0|zg)==(1&o[0|Fg])?zg:2|zg,zg=Eg!=u[yg+q[a+468>>2]>>2]?4|zg:zg,zg=q[yg+q[a+440>>2]>>2]==q[yg+q[a+464>>2]>>2]?zg:8|zg,yg=q[yg+q[a+436>>2]>>2]==q[yg+q[a+460>>2]>>2]?zg:16|zg,yg=Bg?32|yg:yg,Bg=(zg=Gg<<2)+q[a+452>>2]|0,Dg=zg+q[a+472>>2]|0,(u[Bg>>2]!=u[Dg>>2]|u[Bg+4>>2]!=u[Dg+4>>2]|(u[Bg+8>>2]!=u[Dg+8>>2]|u[Bg+12>>2]!=u[Dg+12>>2])||(Bg=zg+q[a+456>>2]|0,zg=zg+q[a+476>>2]|0,u[Bg>>2]!=u[zg>>2]|u[Bg+4>>2]!=u[zg+4>>2]|u[Bg+8>>2]!=u[zg+8>>2])||u[Bg+12>>2]!=u[zg+12>>2])&&(yg|=64),o[0|Fg]=yg,Gg=Gg+4|0,(0|Cg)!=(0|(Ag=Ag+1|0)););else for(;Eg=u[(yg=Ag<<2)+q[a+448>>2]>>2],Bg=q[yg+q[a+424>>2]>>2],zg=Eg!=x(0)&0!=(0|Bg),Dg=q[a+432>>2]+Ag|0,zg=(0|zg)==(1&o[0|Dg])?zg:2|zg,zg=Eg!=u[yg+q[a+468>>2]>>2]?4|zg:zg,zg=q[yg+q[a+440>>2]>>2]==q[yg+q[a+464>>2]>>2]?zg:8|zg,yg=q[yg+q[a+436>>2]>>2]==q[yg+q[a+460>>2]>>2]?zg:16|zg,o[0|Dg]=Bg?32|yg:yg,(0|Cg)!=(0|(Ag=Ag+1|0)););}else if(!((0|Cg)<1))for(;!q[(yg=Ag<<2)+q[a+424>>2]>>2]|u[yg+q[a+448>>2]>>2]==x(0)?(yg=q[a+432>>2]+Ag|0,o[0|yg]=254&r[0|yg]):(yg=q[a+432>>2]+Ag|0,o[0|yg]=1|r[0|yg]),(0|Cg)!=(0|(Ag=Ag+1|0)););}(a),q[a+644>>2]=0}function va(a,Wa,Xa,Ya,Za,$a){var fb,gb,hb,jb,kb,cb,db,ab=0,bb=0,eb=0,ib=0;if(L=cb=L-32|0,1<=(0|Wa))for(kb=w(Wa,12)+Xa|0;;){if(!((0|(ab=q[Xa+4>>2]))<1))if(fb=(Wa=q[Xa+8>>2])+w(ab,48)|0,ab=q[Xa>>2]<<2,1<=(0|(db=q[ab+$a>>2])))for(db<<=1,gb=q[q[a>>2]+1052>>2],hb=q[Za+ab>>2];;){b:if(ab=q[Wa+8>>2]){c:{if((bb=ab+-1|0)>>>0<=1){if(ab=(q[Wa+4>>2]<<2)+Ya|0,ib=(q[ab+(q[Wa+12>>2]<<2)>>2]<<2)+gb|0,bb-1)break c;for(eb=(q[ab+(q[Wa+16>>2]<<2)>>2]<<2)+gb|0,ab=0;u[(jb=(bb=ab<<2)+hb|0)>>2]=u[jb>>2]+x(u[Wa+44>>2]*x(x(u[bb+ib>>2]*u[Wa+20>>2])+x(u[bb+eb>>2]*u[Wa+24>>2]))),(0|db)!=(0|(ab=ab+1|0)););break b}q[cb>>2]=ab,Y(4,1024,cb);break b}for(ab=0;u[(eb=(bb=ab<<2)+hb|0)>>2]=u[eb>>2]+x(u[Wa+44>>2]*x(u[bb+ib>>2]*u[Wa+20>>2])),(0|db)!=(0|(ab=ab+1|0)););}if(!((Wa=Wa+48|0)>>>0<fb>>>0))break}else for(;3<=(ab=q[Wa+8>>2])>>>0&&(q[16+cb>>2]=ab,Y(4,1024,16+cb|0)),(Wa=Wa+48|0)>>>0<fb>>>0;);if(!((Xa=Xa+12|0)>>>0<kb>>>0))break}L=32+cb|0}function wa(a,Wa,Xa){var Ya;Wa|=0,Xa|=0,L=Ya=L+-64|0;a:{if(a|=0)if(Wa)if((Wa+15&-16)!=(0|Wa))q[52+Ya>>2]=1522,q[48+Ya>>2]=2361,Y(4,1294,48+Ya|0);else{if(Wa=function(a,Gl,Hl){var lm,Zl=0,fm=0,gm=0,mm=0,nm=0,om=0,pm=0,qm=0,rm=0,sm=0,tm=0,um=0,vm=0,wm=x(0),xm=0,ym=0,zm=0,Am=0,Bm=0;if(ca(16+(L=lm=L-576|0)|0,0,560),Fa(a,16+lm|0,12+lm|0),(mm=q[12+lm>>2])>>>0<=Hl>>>0){if(Zl=(fm=ca(Gl,0,mm))+q[16+lm>>2]|0,q[Zl+8>>2]=fm+q[20+lm>>2],q[Zl+40>>2]=fm+q[24+lm>>2],q[Zl+44>>2]=fm+q[28+lm>>2],q[Zl+48>>2]=fm+q[32+lm>>2],q[Zl+52>>2]=fm+q[36+lm>>2],q[Zl+16>>2]=fm+q[40+lm>>2],q[Zl+24>>2]=fm+q[44+lm>>2],q[Zl+28>>2]=fm+q[48+lm>>2],q[Zl+32>>2]=fm+q[52+lm>>2],q[Zl+36>>2]=fm+q[56+lm>>2],Gl=q[a+704>>2],q[Zl+308>>2]=fm+q[60+lm>>2],q[Zl+312>>2]=fm+q[64+lm>>2],q[Zl+316>>2]=fm+q[68+lm>>2],q[Zl+320>>2]=fm+q[72+lm>>2],q[Zl+324>>2]=fm+q[76+lm>>2],q[Zl+328>>2]=fm+q[80+lm>>2],q[Zl+60>>2]=fm+q[84+lm>>2],q[Zl+144>>2]=fm+q[88+lm>>2],q[Zl+148>>2]=fm+q[92+lm>>2],Hl=fm+q[96+lm>>2]|0,q[Zl+152>>2]=Hl,!((0|(mm=q[Gl+8>>2]))<1)&&(Gl=fm+q[100+lm>>2]|0,q[Hl>>2]=Gl,1!=(0|mm)))for(Hl=1;Gl=(15+(q[q[a+796>>2]+(gm<<2)>>2]<<3)&-16)+Gl|0,q[q[Zl+152>>2]+(Hl<<2)>>2]=Gl,(0|mm)!=(0|(Hl=(gm=Hl)+1|0)););if(q[Zl+156>>2]=fm+q[104+lm>>2],q[Zl+160>>2]=fm+q[108+lm>>2],q[Zl+68>>2]=fm+q[112+lm>>2],q[Zl+76>>2]=fm+q[116+lm>>2],q[Zl+80>>2]=fm+q[120+lm>>2],q[Zl+84>>2]=fm+q[124+lm>>2],q[Zl+88>>2]=fm+q[128+lm>>2],q[Zl+92>>2]=fm+q[132+lm>>2],q[Zl+96>>2]=fm+q[136+lm>>2],q[Zl+100>>2]=fm+q[140+lm>>2],q[Zl+104>>2]=fm+q[144+lm>>2],q[Zl+108>>2]=fm+q[148+lm>>2],q[Zl+112>>2]=fm+q[152+lm>>2],q[Zl+116>>2]=fm+q[156+lm>>2],q[Zl+120>>2]=fm+q[160+lm>>2],q[Zl+124>>2]=fm+q[164+lm>>2],q[Zl+128>>2]=fm+q[168+lm>>2],q[Zl+132>>2]=fm+q[172+lm>>2],q[Zl+136>>2]=fm+q[176+lm>>2],q[Zl+140>>2]=fm+q[180+lm>>2],q[Zl+168>>2]=fm+q[184+lm>>2],q[Zl+264>>2]=fm+q[188+lm>>2],q[Zl+268>>2]=fm+q[192+lm>>2],q[Zl+272>>2]=fm+q[196+lm>>2],q[Zl+276>>2]=fm+q[200+lm>>2],q[Zl+280>>2]=fm+q[204+lm>>2],q[Zl+284>>2]=fm+q[208+lm>>2],q[Zl+288>>2]=fm+q[212+lm>>2],q[Zl+292>>2]=fm+q[216+lm>>2],q[Zl+296>>2]=fm+q[220+lm>>2],q[Zl+300>>2]=fm+q[224+lm>>2],q[Zl+176>>2]=fm+q[228+lm>>2],q[Zl+184>>2]=fm+q[232+lm>>2],q[Zl+188>>2]=fm+q[236+lm>>2],q[Zl+192>>2]=fm+q[240+lm>>2],q[Zl+196>>2]=fm+q[244+lm>>2],q[Zl+200>>2]=fm+q[248+lm>>2],q[Zl+204>>2]=fm+q[252+lm>>2],q[Zl+208>>2]=fm+q[256+lm>>2],q[Zl+212>>2]=fm+q[260+lm>>2],q[Zl+216>>2]=fm+q[264+lm>>2],q[Zl+220>>2]=fm+q[268+lm>>2],q[Zl+224>>2]=fm+q[272+lm>>2],q[Zl+228>>2]=fm+q[276+lm>>2],q[Zl+232>>2]=fm+q[280+lm>>2],q[Zl+236>>2]=fm+q[284+lm>>2],q[Zl+240>>2]=fm+q[288+lm>>2],q[Zl+244>>2]=fm+q[292+lm>>2],q[Zl+248>>2]=fm+q[296+lm>>2],q[Zl+252>>2]=fm+q[300+lm>>2],q[Zl+256>>2]=fm+q[304+lm>>2],q[Zl+260>>2]=fm+q[308+lm>>2],Gl=q[a+704>>2],q[Zl+336>>2]=fm+q[312+lm>>2],q[Zl+424>>2]=fm+q[316+lm>>2],q[Zl+432>>2]=fm+q[320+lm>>2],q[Zl+436>>2]=fm+q[324+lm>>2],q[Zl+440>>2]=fm+q[328+lm>>2],Hl=fm+q[332+lm>>2]|0,q[Zl+444>>2]=Hl,!((0|(mm=q[Gl+16>>2]))<1)&&(gm=fm+q[336+lm>>2]|0,q[Hl>>2]=gm,(Hl=1)!=(0|mm)))for(Gl=0;gm=(15+(q[q[a+892>>2]+(Gl<<2)>>2]<<3)&-16)+gm|0,q[q[Zl+444>>2]+(Hl<<2)>>2]=gm,(0|mm)!=(0|(Hl=(Gl=Hl)+1|0)););if(q[Zl+448>>2]=fm+q[340+lm>>2],q[Zl+452>>2]=fm+q[344+lm>>2],q[Zl+456>>2]=fm+q[348+lm>>2],q[Zl+460>>2]=fm+q[352+lm>>2],q[Zl+464>>2]=fm+q[356+lm>>2],q[Zl+468>>2]=fm+q[360+lm>>2],q[Zl+472>>2]=fm+q[364+lm>>2],q[Zl+476>>2]=fm+q[368+lm>>2],q[Zl+344>>2]=fm+q[372+lm>>2],q[Zl+352>>2]=fm+q[376+lm>>2],q[Zl+356>>2]=fm+q[380+lm>>2],q[Zl+360>>2]=fm+q[384+lm>>2],q[Zl+364>>2]=fm+q[388+lm>>2],q[Zl+368>>2]=fm+q[392+lm>>2],q[Zl+372>>2]=fm+q[396+lm>>2],q[Zl+376>>2]=fm+q[400+lm>>2],q[Zl+380>>2]=fm+q[404+lm>>2],q[Zl+384>>2]=fm+q[408+lm>>2],q[Zl+388>>2]=fm+q[412+lm>>2],q[Zl+392>>2]=fm+q[416+lm>>2],q[Zl+396>>2]=fm+q[420+lm>>2],q[Zl+400>>2]=fm+q[424+lm>>2],q[Zl+404>>2]=fm+q[428+lm>>2],q[Zl+408>>2]=fm+q[432+lm>>2],q[Zl+412>>2]=fm+q[436+lm>>2],q[Zl+416>>2]=fm+q[440+lm>>2],q[Zl+420>>2]=fm+q[444+lm>>2],Gl=q[448+lm>>2],Hl=q[452+lm>>2],q[Zl+552>>2]=fm+q[456+lm>>2],q[Zl+548>>2]=Hl+fm,q[Zl+544>>2]=Gl+fm,q[Zl+560>>2]=fm+q[460+lm>>2],Gl=q[a+704>>2],pm=fm+q[464+lm>>2]|0,q[Zl+568>>2]=pm,1<=(0|(om=q[Gl+48>>2])))for(gm=fm+q[468+lm>>2]|0,Gl=fm+q[472+lm>>2]|0,nm=fm+q[476+lm>>2]|0,qm=q[a+1072>>2],Hl=0;mm=pm+w(Hl,36)|0,q[mm+20>>2]=nm,q[mm+16>>2]=Gl,q[mm>>2]=gm,gm=((mm=q[qm+(Hl<<2)>>2])<<2)+gm|0,nm=(mm=1<<mm<<2)+nm|0,Gl=Gl+mm|0,(0|om)!=(0|(Hl=Hl+1|0)););if(Gl=q[a+704>>2],mm=fm+q[516+lm>>2]|0,q[Zl+484>>2]=mm,1<=(0|(Gl=q[Gl+72>>2])))for(gm=fm+q[520+lm>>2]|0,nm=q[a+1212>>2],Hl=0;q[12+(mm+w(Hl,28)|0)>>2]=gm,gm=(q[nm+(Hl<<2)>>2]<<4)+gm|0,(0|Gl)!=(0|(Hl=Hl+1|0)););q[Zl+488>>2]=fm+q[524+lm>>2],q[Zl+492>>2]=fm+q[528+lm>>2],q[Zl+496>>2]=fm+q[532+lm>>2],q[Zl+504>>2]=fm+q[536+lm>>2],q[Zl+536>>2]=fm+q[540+lm>>2],q[Zl+512>>2]=fm+q[544+lm>>2],q[Zl+520>>2]=fm+q[548+lm>>2],q[Zl+524>>2]=fm+q[552+lm>>2],q[Zl+528>>2]=fm+q[556+lm>>2],q[Zl+532>>2]=fm+q[560+lm>>2];c:{if(4<=(om=r[a+4|0])>>>0){if(q[Zl+576>>2]=fm+q[480+lm>>2],q[Zl+584>>2]=fm+q[484+lm>>2],Gl=q[a+704>>2],Hl=q[492+lm>>2],mm=fm+q[488+lm>>2]|0,q[Zl+592>>2]=mm,1<=(0|(Gl=q[Gl+104>>2])))for(gm=Hl+fm|0,nm=q[a+1104>>2],Hl=0;q[40+(mm+w(Hl,48)|0)>>2]=gm,gm=(q[nm+(Hl<<2)>>2]<<2)+gm|0,(0|Gl)!=(0|(Hl=Hl+1|0)););q[Zl+608>>2]=fm+q[500+lm>>2],q[Zl+624>>2]=fm+q[508+lm>>2]}else{if(Gl=q[572+lm>>2],Hl=q[568+lm>>2],q[Zl+636>>2]=fm+q[564+lm>>2],q[Zl+640>>2]=Hl+fm,q[q[a+704>>2]+20>>2]<1)break c;for(mm=Gl+fm|0,pm=0;;){e:{if((0|(gm=q[(Gl=pm<<2)+q[a+952>>2]>>2]))<=0)Gl=Gl+q[Zl+636>>2]|0;else{for(nm=gm+(Hl=q[Gl+q[a+948>>2]>>2])|0,qm=q[a+1060>>2],gm=0;gm=q[qm+(Hl<<2)>>2]+gm|0,(0|(Hl=Hl+1|0))<(0|nm););if(Gl=Gl+q[Zl+636>>2]|0,Hl=mm,gm)break e}Hl=gm=0}if(q[Gl>>2]=Hl,mm=(gm<<2)+mm|0,!((0|(pm=pm+1|0))<q[q[a+704>>2]+20>>2]))break}}om>>>0<5||(q[Zl+600>>2]=fm+q[496+lm>>2],q[Zl+616>>2]=fm+q[504+lm>>2],q[Zl+632>>2]=fm+q[512+lm>>2])}q[Zl+644>>2]=1,q[Zl>>2]=a,q[Zl+648>>2]=1&o[q[a+708>>2]+20|0],fm=q[a+704>>2],pm=q[fm+20>>2];g:if(!((0|(q[Zl+540>>2]=pm))<1)){if(Gl=pm+-1|0,qm=q[a+952>>2],rm=q[a+940>>2],sm=q[a+932>>2],tm=q[a+936>>2],um=q[a+924>>2],vm=q[a+928>>2],xm=q[Zl+552>>2],zm=q[Zl+544>>2],om>>>0<4)for(;;)if(Hl=zm+w(Gl,52)|0,gm=(mm=Gl<<2)+vm|(q[Hl>>2]=0),q[Hl+4>>2]=q[gm>>2],q[Hl+8>>2]=q[(nm=mm+um|0)>>2],u[Hl+12>>2]=u[nm>>2]-u[gm>>2],q[Hl+16>>2]=q[mm+tm>>2],q[Hl+44>>2]=q[(gm=mm+sm|0)>>2],wm=Aa(x(q[mm+rm>>2])),u[Hl+20>>2]=wm,u[Hl+24>>2]=wm*x(1.5),ym=q[mm+qm>>2],nm=0,nm=(q[Hl+32>>2]=ym)?q[Zl+560>>2]+w(q[mm+q[a+948>>2]>>2],28)|0:nm,q[Hl+48>>2]=1,q[Hl+28>>2]=nm,q[mm+xm>>2]=q[gm>>2],Hl=0<(0|Gl),Gl=Gl+-1|0,!Hl)break g;for(ym=q[a+960>>2],Bm=q[a+944>>2];Hl=zm+w(Gl,52)|0,q[Hl>>2]=q[(gm=Gl<<2)+Bm>>2],q[Hl+4>>2]=q[(mm=gm+vm|0)>>2],q[Hl+8>>2]=q[(nm=gm+um|0)>>2],u[Hl+12>>2]=u[nm>>2]-u[mm>>2],q[Hl+16>>2]=q[gm+tm>>2],q[Hl+44>>2]=q[(Am=gm+sm|0)>>2],wm=Aa(x(q[gm+rm>>2])),u[Hl+20>>2]=wm,u[Hl+24>>2]=wm*x(1.5),nm=q[gm+qm>>2],q[Hl+32>>2]=nm,q[Hl+28>>2]=nm?q[Zl+560>>2]+w(q[gm+q[a+948>>2]>>2],28)|0:0,mm=q[gm+ym>>2],mm=(q[Hl+40>>2]=mm)?q[Zl+584>>2]+w(q[gm+q[a+956>>2]>>2],28)|0:0,q[Hl+48>>2]=1,q[Hl+36>>2]=mm,q[gm+xm>>2]=q[Am>>2],Hl=0<(0|Gl),Gl=Gl+-1|0,Hl;);}if(4<=om>>>0?(q[Zl+548>>2]=q[a+944>>2],mm=a):(ca(q[Zl+548>>2],0,pm<<2),mm=q[Zl>>2],fm=q[mm+704>>2]),gm=q[fm+52>>2],1<=(0|(q[Zl+556>>2]=gm)))for(Hl=q[mm+1056>>2],nm=q[mm+1192>>2],pm=q[mm+1060>>2],om=q[Zl+560>>2];Gl=om+w(gm=gm+-1|0,28)|0,q[Gl>>2]=q[(qm=gm<<2)+pm>>2],qm=q[Hl+qm>>2],q[Gl+24>>2]=1,q[Gl+16>>2]=0,q[Gl+20>>2]=1,q[Gl+8>>2]=0,q[Gl+12>>2]=0,q[Gl+4>>2]=nm+(qm<<2),0<(0|gm););if(gm=q[fm+48>>2],1<=(0|(q[Zl+564>>2]=gm))){for(;;){if(Gl=q[Zl+568>>2]+w(gm=gm+-1|0,36)|0,nm=q[(fm=gm<<2)+q[mm+1072>>2]>>2],1<=(0|(q[Gl+4>>2]=nm)))for(Hl=0;q[q[Gl>>2]+(Hl<<2)>>2]=q[Zl+560>>2]+w(q[q[mm+1064>>2]+(q[fm+q[mm+1068>>2]>>2]+Hl<<2)>>2],28),(0|nm)!=(0|(Hl=Hl+1|0)););if(q[Gl+24>>2]=1,q[Gl+28>>2]=1,q[Gl+8>>2]=1<<nm,!(0<(0|gm)))break}mm=q[Zl>>2],fm=q[mm+704>>2]}if(Gl=q[fm>>2],(0|(q[Zl+4>>2]=Gl))<1)Hl=0;else{for(qm=q[mm+732>>2],rm=q[mm+736>>2],sm=q[mm+740>>2],nm=q[mm+720>>2],tm=q[Zl+52>>2],pm=q[Zl+568>>2],um=q[Zl+8>>2],gm=Gl;om=um+w(gm=gm+-1|0,12)|0,q[om>>2]=pm+w(q[(Hl=gm<<2)+nm>>2],36),q[om+4>>2]=q[Hl+sm>>2],q[om+8>>2]=q[Hl+rm>>2],u[Hl+tm>>2]=q[Hl+qm>>2]?x(1):x(0),0<(0|gm););for(om=q[Zl+16>>2],Hl=0;gm=q[8+(pm+w(q[(qm=(Gl=Gl+-1|0)<<2)+nm>>2],36)|0)>>2],Hl=Hl+(q[om+qm>>2]=gm)|0,0<(0|Gl););Gl=q[Zl+4>>2]}if(q[Zl+12>>2]=Gl,q[Zl+20>>2]=Hl,Gl=q[fm+4>>2],1<=(0|(q[Zl+304>>2]=Gl))){for(;Hl=q[Zl+308>>2]+((Gl=Gl+-1|0)<<5)|0,q[Hl>>2]=q[Zl+568>>2]+w(q[(gm=Gl<<2)+q[mm+752>>2]>>2],36),q[Hl+4>>2]=q[gm+q[mm+764>>2]>>2],q[Hl+8>>2]=q[gm+q[mm+768>>2]>>2],nm=q[gm+q[mm+772>>2]>>2],q[Hl+12>>2]=nm,fm=q[gm+q[mm+776>>2]>>2],q[Hl+16>>2]=fm,q[Hl+28>>2]=q[gm+q[mm+760>>2]>>2],nm>>>0<=1?nm-1?(q[20+(q[Zl+60>>2]+w(fm,24)|0)>>2]=Gl,q[Hl+24>>2]=1,q[Hl+20>>2]=2):(q[8+(q[Zl+168>>2]+w(fm,12)|0)>>2]=Gl,q[Hl+24>>2]=3,q[Hl+20>>2]=4):Y(4,1179,0),0<(0|Gl););mm=q[Zl>>2],fm=q[mm+704>>2]}gm=q[fm+8>>2];k:if(!((0|(q[Zl+56>>2]=gm))<1)){if(Hl=gm+-1|0,pm=q[mm+796>>2],om=q[mm+804>>2],qm=q[mm+800>>2],rm=q[mm+780>>2],sm=q[Zl+568>>2],tm=q[Zl+60>>2],r[mm+4|0]<2)for(;;)if(Gl=tm+w(Hl,24)|0,q[Gl>>2]=sm+w(q[(nm=Hl<<2)+rm>>2],36),q[Gl+4>>2]=q[nm+qm>>2],q[Gl+8>>2]=q[nm+om>>2],nm=q[nm+pm>>2],q[Gl+12>>2]=0,q[Gl+16>>2]=nm,Gl=0<(0|Hl),Hl=Hl+-1|0,!Gl)break k;for(um=q[mm+808>>2];Gl=tm+w(Hl,24)|0,q[Gl>>2]=sm+w(q[(nm=Hl<<2)+rm>>2],36),q[Gl+4>>2]=q[nm+qm>>2],q[Gl+8>>2]=q[nm+om>>2],q[Gl+16>>2]=q[nm+pm>>2],q[Gl+12>>2]=q[nm+um>>2],Gl=0<(0|Hl),Hl=Hl+-1|0,Gl;);}if(Hl=q[fm+12>>2],1<=(0|(q[Zl+164>>2]=Hl)))for(nm=q[mm+828>>2],pm=q[mm+812>>2],om=q[Zl+568>>2],qm=q[Zl+168>>2],Gl=Hl;rm=qm+w(Gl=Gl+-1|0,12)|0,q[rm>>2]=om+w(q[(sm=Gl<<2)+pm>>2],36),q[rm+4>>2]=q[nm+sm>>2],0<(0|Gl););if(((Gl=0)|gm)<1)nm=0;else{for(pm=q[Zl+68>>2],om=q[Zl+60>>2],nm=0;Hl=q[q[om+w(gm=gm+-1|0,24)>>2]+8>>2],nm=(q[pm+(gm<<2)>>2]=Hl)+nm|0,0<(0|gm););Hl=q[Zl+164>>2],gm=q[Zl+56>>2]}if(q[Zl+64>>2]=gm,q[Zl+72>>2]=nm,gm=Zl,1<=(0|Hl)){for(pm=q[Zl+176>>2],om=q[Zl+168>>2];nm=q[q[om+w(Hl=Hl+-1|0,12)>>2]+8>>2],Gl=Gl+(q[pm+(Hl<<2)>>2]=nm)|0,0<(0|Hl););Hl=q[Zl+164>>2]}if(q[gm+172>>2]=Hl,q[Zl+180>>2]=Gl,nm=q[fm+16>>2],1<=(0|(q[Zl+332>>2]=nm))){for(qm=q[mm+872>>2],rm=q[mm+892>>2],sm=q[mm+880>>2],tm=q[mm+876>>2],pm=q[mm+852>>2],om=q[Zl+568>>2],um=q[Zl+336>>2],Gl=nm;Hl=um+w(Gl=Gl+-1|0,20)|0,q[Hl>>2]=om+w(q[(gm=Gl<<2)+pm>>2],36),q[Hl+4>>2]=q[gm+tm>>2],q[Hl+8>>2]=q[gm+sm>>2],q[Hl+16>>2]=q[gm+rm>>2],q[Hl+12>>2]=q[gm+qm>>2],0<(0|Gl););for(gm=q[Zl+344>>2],Hl=0;Gl=q[8+(om+w(q[(qm=(nm=nm+-1|0)<<2)+pm>>2],36)|0)>>2],Hl=(q[gm+qm>>2]=Gl)+Hl|0,0<(0|nm););if(q[Zl+348>>2]=Hl,nm=q[Zl+332>>2],!((0|(q[Zl+340>>2]=nm))<1))for(Hl=nm<<2,gm=q[Zl+456>>2],pm=q[Zl+452>>2];q[(om=(Gl=Hl+-4|0)<<2)+pm>>2]=1065353216,q[(qm=(Hl<<=2)-4|0)+pm>>2]=1065353216,q[(rm=(Hl=Hl+-12|0)+pm|0)>>2]=1065353216,q[rm+4>>2]=1065353216,q[gm+om>>2]=0,q[gm+qm>>2]=1065353216,q[(Hl=Hl+gm|0)>>2]=0,q[Hl+4>>2]=0,Hl=Gl,0<(0|(nm=nm+-1|0)););}else q[Zl+340>>2]=nm,q[Zl+348>>2]=0;if(nm=q[fm+72>>2],1<=(0|(q[Zl+480>>2]=nm)))for(qm=q[mm+1208>>2],rm=q[mm+1224>>2],sm=q[mm+1220>>2],tm=q[mm+1216>>2],um=q[mm+1212>>2],vm=q[Zl+484>>2],gm=0;;){if(Gl=vm+w(gm,28)|0,pm=q[(Hl=gm<<2)+um>>2],q[Gl+4>>2]=pm,q[Gl>>2]=q[Hl+tm>>2],om=q[Hl+sm>>2],q[Gl+16>>2]=om,xm=q[Hl+rm>>2],q[Gl+20>>2]=xm,q[Gl+8>>2]=0,q[Gl+24>>2]=1+(om-xm|0),1<=(0|pm))for(xm=q[Hl+qm>>2],zm=q[Gl+12>>2],ym=q[mm+1236>>2],Bm=q[mm+1228>>2],Am=q[mm+1232>>2],Hl=0;q[4+(Gl=zm+(Hl<<4)|0)>>2]=q[(om=Hl+xm<<2)+Am>>2],q[Gl>>2]=q[om+Bm>>2],om=q[om+ym>>2],q[Gl+12>>2]=0,q[Gl+8>>2]=om,(0|pm)!=(0|(Hl=Hl+1|0)););if((0|nm)==(0|(gm=gm+1|0)))break}if(Hl=q[fm+80>>2],(0|(q[Zl+500>>2]=Hl))<1)gm=0;else{for(om=q[mm+1280>>2],qm=q[mm+1268>>2],rm=q[mm+1276>>2],sm=q[mm+1272>>2],tm=q[mm+1264>>2],um=q[mm+1260>>2],nm=q[mm+1248>>2],pm=q[Zl+568>>2],vm=q[Zl+504>>2];Gl=vm+w(Hl=Hl+-1|0,24)|0,q[Gl>>2]=pm+w(q[(gm=Hl<<2)+nm>>2],36),q[Gl+4>>2]=q[gm+um>>2],q[Gl+8>>2]=q[gm+tm>>2],q[Gl+12>>2]=q[gm+sm>>2],gm=q[gm+qm>>2],q[Gl+20>>2]=om+(gm<<1),q[Gl+16>>2]=rm+(gm<<2),0<(0|Hl););if((0|(Hl=q[Zl+500>>2]))<1)gm=0;else{for(om=q[Zl+512>>2],gm=0;Gl=q[8+(pm+w(q[(qm=(Hl=Hl+-1|0)<<2)+nm>>2],36)|0)>>2],gm=(q[om+qm>>2]=Gl)+gm|0,0<(0|Hl););Hl=q[Zl+500>>2]}}q[Zl+508>>2]=Hl,q[Zl+516>>2]=gm;o:if(4<=r[a+4|0]){if(!((nm=r[mm+4|0])>>>0<4)){if(Hl=q[fm+120>>2],1<=(0|(q[Zl+572>>2]=Hl))){for(om=q[mm+1172>>2],qm=q[Zl+576>>2];nm=(0|(fm=q[(Gl=(Hl=Hl+-1|0)<<2)+om>>2]))<0?fm=pm=gm=0:(gm=(nm=q[Gl+q[mm+1176>>2]>>2]<<2)+q[mm+1188>>2]|0,pm=q[Gl+q[mm+1180>>2]>>2],fm=q[Zl+544>>2]+w(fm,52)|0,nm+q[mm+1184>>2]|0),Gl=qm+w(Hl,20)|0,q[Gl+12>>2]=pm,q[Gl+8>>2]=gm,q[Gl+4>>2]=nm,q[Gl>>2]=fm,0<(0|Hl););if(mm=q[Zl>>2],(nm=r[mm+4|0])>>>0<4)break o}if(fm=q[mm+704>>2],gm=q[fm+100>>2],1<=(0|(q[Zl+580>>2]=gm)))for(pm=q[mm+1084>>2],om=q[mm+1076>>2],qm=q[mm+1192>>2],rm=q[mm+1080>>2],sm=q[Zl+584>>2];Gl=sm+w(gm=gm+-1|0,28)|0,q[Gl>>2]=q[(Hl=gm<<2)+rm>>2],q[Gl+4>>2]=qm+(q[Hl+om>>2]<<2),Hl=q[Hl+pm>>2],q[Gl+20>>2]=1,q[Gl+24>>2]=1,q[Gl+12>>2]=0,q[Gl+16>>2]=0,q[Gl+8>>2]=Hl,0<(0|gm););if(gm=q[fm+104>>2],1<=(0|(q[Zl+588>>2]=gm))){for(;;){if(Gl=q[Zl+592>>2]+w(gm=gm+-1|0,48)|0,q[Gl>>2]=q[Zl+584>>2]+w(q[(nm=gm<<2)+q[mm+1088>>2]>>2],28),Hl=q[nm+q[mm+1092>>2]>>2],q[Gl+28>>2]=1,q[Gl+32>>2]=1,q[Gl+8>>2]=0,q[Gl+4>>2]=Hl,fm=q[nm+q[mm+1104>>2]>>2],1<=(0|(q[Gl+36>>2]=fm)))for(Hl=0;q[q[Gl+40>>2]+(Hl<<2)>>2]=q[Zl+576>>2]+w(q[q[mm+1168>>2]+(q[nm+q[mm+1100>>2]>>2]+Hl<<2)>>2],20),(0|fm)!=(0|(Hl=Hl+1|0)););if(!(1<=(0|gm)))break}mm=q[Zl>>2],nm=r[mm+4|0]}if(!(nm>>>0<4)){if(nm=q[a+704>>2],Hl=q[nm+108>>2],1<=(0|(q[Zl+604>>2]=Hl)))for(fm=q[a+1124>>2],pm=q[a+1128>>2],om=q[a+1120>>2],qm=q[Zl+592>>2],rm=q[Zl+608>>2];Gl=rm+w(Hl=Hl+-1|0,12)|0,q[Gl>>2]=q[(gm=Hl<<2)+om>>2],q[Gl+4>>2]=q[gm+pm>>2],q[Gl+8>>2]=qm+w(q[fm+gm>>2],48),0<(0|Hl););if(Hl=q[nm+112>>2],1<=(0|(q[Zl+620>>2]=Hl)))for(nm=q[a+1148>>2],fm=q[a+1152>>2],pm=q[a+1144>>2],om=q[Zl+592>>2],qm=q[Zl+624>>2];Gl=qm+w(Hl=Hl+-1|0,12)|0,q[Gl>>2]=q[(gm=Hl<<2)+pm>>2],q[Gl+4>>2]=q[fm+gm>>2],q[Gl+8>>2]=om+w(q[gm+nm>>2],48),0<(0|Hl););if(gm=q[mm+1192>>2],Gl=q[q[mm+704>>2]+20>>2],q[Zl+640>>2]=q[mm+972>>2],nm=q[mm+964>>2],q[Zl+636>>2]=nm,!((0|Gl)<(Hl=1))&&(q[nm>>2]=gm+(q[q[mm+968>>2]>>2]<<2),1!=(0|Gl)))for(;q[(nm=Hl<<2)+q[Zl+636>>2]>>2]=gm+(q[nm+q[mm+968>>2]>>2]<<2),(0|Gl)!=(0|(Hl=Hl+1|0)););}}}else if(!(q[fm+20>>2]<1))for(fm=0;;){if(gm=q[(pm=fm<<2)+q[Zl+636>>2]>>2],1<=((Gl=0)|(Hl=q[pm+q[mm+952>>2]>>2])))for(rm=Hl+(om=q[pm+q[mm+948>>2]>>2])|0,sm=q[mm+1060>>2],tm=q[mm+1056>>2];;){if(1<=(0|(qm=q[(Hl=om<<2)+sm>>2])))for(um=qm+(nm=q[Hl+tm>>2])|0,vm=q[mm+1192>>2];;){qm=gm+(Gl<<2)|0,wm=u[vm+(nm<<2)>>2],Hl=gm;q:{if(0<(0|Gl))for(;;){if(u[Hl>>2]==wm)break q;if(!((Hl=Hl+4|0)>>>0<qm>>>0))break}u[qm>>2]=wm,Gl=Gl+1|0}if(!((0|(nm=nm+1|0))<(0|um)))break}if(!((0|(om=om+1|0))<(0|rm)))break}if(function(a,rn){var sn,In,vn=0,wn=0,Hn=0;q[8+(L=sn=L-208|0)>>2]=1,q[12+sn>>2]=0;a:if(In=rn<<2){for(q[16+sn>>2]=4,Hn=rn=q[20+sn>>2]=4,vn=2;rn=(Hn+4|0)+(wn=rn)|0,q[(16+sn|0)+(vn<<2)>>2]=rn,vn=vn+1|0,Hn=wn,rn>>>0<In>>>0;);if((wn=(a+In|0)-4|0)>>>0<=a>>>0)rn=vn=1;else for(rn=vn=1;rn=3==(3&vn)?(ta(a,rn,16+sn|0),ma(8+sn|0,2),rn+2|0):(t[(16+sn|0)+((Hn=rn+-1|0)<<2)>>2]>=wn-a>>>0?la(a,8+sn|0,rn,0,16+sn|0):ta(a,rn,16+sn|0),1==(0|rn)?(ka(8+sn|0,1),0):(ka(8+sn|0,Hn),1)),vn=1|q[8+sn>>2],q[8+sn>>2]=vn,(a=a+4|0)>>>0<wn>>>0;);for(la(a,8+sn|0,rn,0,16+sn|0);;){e:{f:{g:{if(!(1!=(0|rn)|1!=(0|vn))){if(q[12+sn>>2])break g;break a}if(1<(0|rn))break f}ma(8+sn|0,wn=Oa(8+sn|0)),vn=q[8+sn>>2],rn=rn+wn|0;break e}ka(8+sn|0,2),q[8+sn>>2]=7^q[8+sn>>2],ma(8+sn|0,1),la((Hn=a+-4|0)-q[(16+sn|0)+((wn=rn+-2|0)<<2)>>2]|0,8+sn|0,rn+-1|0,1,16+sn|0),ka(8+sn|0,1),vn=1|q[8+sn>>2],q[8+sn>>2]=vn,la(Hn,8+sn|0,wn,1,16+sn|0),rn=wn}a=a+-4|0}}L=208+sn|0}(gm,Gl),q[pm+q[Zl+640>>2]>>2]=Gl,!((0|(fm=fm+1|0))<q[q[mm+704>>2]+20>>2]))break}if(!(r[a+4|0]<5|r[q[Zl>>2]+4|0]<4)){if(Gl=q[a+704>>2],Hl=q[Gl+128>>2],1<=(0|(q[Zl+596>>2]=Hl)))for(nm=q[a+1112>>2],fm=q[a+1116>>2],pm=q[a+1108>>2],om=q[Zl+592>>2],qm=q[Zl+600>>2];mm=qm+w(Hl=Hl+-1|0,12)|0,q[mm>>2]=q[(gm=Hl<<2)+pm>>2],q[mm+4>>2]=q[fm+gm>>2],q[mm+8>>2]=om+w(q[gm+nm>>2],48),0<(0|Hl););if(Hl=q[Gl+132>>2],1<=(0|(q[Zl+612>>2]=Hl)))for(nm=q[a+1136>>2],fm=q[a+1140>>2],pm=q[a+1132>>2],om=q[Zl+592>>2],qm=q[Zl+616>>2];mm=qm+w(Hl=Hl+-1|0,12)|0,q[mm>>2]=q[(gm=Hl<<2)+pm>>2],q[mm+4>>2]=q[fm+gm>>2],q[mm+8>>2]=om+w(q[gm+nm>>2],48),0<(0|Hl););if(Hl=q[Gl+136>>2],!((0|(q[Zl+628>>2]=Hl))<1))for(mm=q[a+1160>>2],gm=q[a+1164>>2],nm=q[a+1156>>2],fm=q[Zl+592>>2],pm=q[Zl+632>>2];a=pm+w(Hl=Hl+-1|0,12)|0,q[a>>2]=q[(Gl=Hl<<2)+nm>>2],q[a+4>>2]=q[Gl+gm>>2],q[a+8>>2]=fm+w(q[Gl+mm>>2],48),0<(0|Hl););}ua(Zl)}return L=576+lm|0,Zl}(a,Wa,Xa))break a;q[36+Ya>>2]=2209,q[32+Ya>>2]=2361,Y(4,1294,32+Ya|0)}else q[20+Ya>>2]=1444,q[16+Ya>>2]=2361,Y(4,1294,16+Ya|0);else q[4+Ya>>2]=2132,q[Ya>>2]=2361,Y(4,1294,Ya);Wa=0}return L=64+Ya|0,0|Wa}function xa(a){var Wa;return L=Wa=L-16|0,a=(a|=0)?function(a){var Gl;return ca(16+(L=Gl=L-576|0)|0,0,560),Fa(a,16+Gl|0,12+Gl|0),L=576+Gl|0,q[12+Gl>>2]}(a):(q[4+Wa>>2]=2132,q[Wa>>2]=2343,Y(4,1294,Wa),0),L=16+Wa|0,0|a}function ya(a){var Xa=r[a+4|0];X(q[a+704>>2],4,64),da(q[a+708>>2],4),da(q[a+708>>2]+4|0,4),da(q[a+708>>2]+8|0,4),da(q[a+708>>2]+12|0,4),da(q[a+708>>2]+16|0,4),da(q[a+708>>2]+20|0,1),X(q[a+720>>2],4,q[q[a+704>>2]>>2]),X(q[a+724>>2],4,q[q[a+704>>2]>>2]),X(q[a+728>>2],4,q[q[a+704>>2]>>2]),X(q[a+732>>2],4,q[q[a+704>>2]>>2]),X(q[a+736>>2],4,q[q[a+704>>2]>>2]),X(q[a+740>>2],4,q[q[a+704>>2]>>2]),X(q[a+752>>2],4,q[q[a+704>>2]+4>>2]),X(q[a+756>>2],4,q[q[a+704>>2]+4>>2]),X(q[a+760>>2],4,q[q[a+704>>2]+4>>2]),X(q[a+764>>2],4,q[q[a+704>>2]+4>>2]),X(q[a+768>>2],4,q[q[a+704>>2]+4>>2]),X(q[a+772>>2],4,q[q[a+704>>2]+4>>2]),X(q[a+776>>2],4,q[q[a+704>>2]+4>>2]),X(q[a+780>>2],4,q[q[a+704>>2]+8>>2]),X(q[a+784>>2],4,q[q[a+704>>2]+8>>2]),X(q[a+788>>2],4,q[q[a+704>>2]+8>>2]),X(q[a+796>>2],4,q[q[a+704>>2]+8>>2]),X(q[a+800>>2],4,q[q[a+704>>2]+8>>2]),X(q[a+804>>2],4,q[q[a+704>>2]+8>>2]),X(q[a+812>>2],4,q[q[a+704>>2]+12>>2]),X(q[a+816>>2],4,q[q[a+704>>2]+12>>2]),X(q[a+820>>2],4,q[q[a+704>>2]+12>>2]),X(q[a+828>>2],4,q[q[a+704>>2]+12>>2]),X(q[a+852>>2],4,q[q[a+704>>2]+16>>2]),X(q[a+856>>2],4,q[q[a+704>>2]+16>>2]),X(q[a+860>>2],4,q[q[a+704>>2]+16>>2]),X(q[a+868>>2],4,q[q[a+704>>2]+16>>2]),X(q[a+872>>2],4,q[q[a+704>>2]+16>>2]),X(q[a+876>>2],4,q[q[a+704>>2]+16>>2]),X(q[a+880>>2],4,q[q[a+704>>2]+16>>2]),X(q[a+884>>2],4,q[q[a+704>>2]+16>>2]),X(q[a+888>>2],1,q[q[a+704>>2]+16>>2]),X(q[a+892>>2],4,q[q[a+704>>2]+16>>2]),X(q[a+896>>2],4,q[q[a+704>>2]+16>>2]),X(q[a+900>>2],4,q[q[a+704>>2]+16>>2]),X(q[a+904>>2],4,q[q[a+704>>2]+16>>2]),X(q[a+908>>2],4,q[q[a+704>>2]+16>>2]),X(q[a+912>>2],4,q[q[a+704>>2]+16>>2]),X(q[a+924>>2],4,q[q[a+704>>2]+20>>2]),X(q[a+928>>2],4,q[q[a+704>>2]+20>>2]),X(q[a+932>>2],4,q[q[a+704>>2]+20>>2]),X(q[a+936>>2],4,q[q[a+704>>2]+20>>2]),X(q[a+940>>2],4,q[q[a+704>>2]+20>>2]),X(q[a+948>>2],4,q[q[a+704>>2]+20>>2]),X(q[a+952>>2],4,q[q[a+704>>2]+20>>2]),X(q[a+976>>2],4,q[q[a+704>>2]+24>>2]),X(q[a+980>>2],4,q[q[a+704>>2]+28>>2]),X(q[a+984>>2],4,q[q[a+704>>2]+28>>2]),X(q[a+996>>2],4,q[q[a+704>>2]+32>>2]),X(q[a+1e3>>2],4,q[q[a+704>>2]+32>>2]),X(q[a+1004>>2],4,q[q[a+704>>2]+32>>2]),X(q[a+1008>>2],4,q[q[a+704>>2]+32>>2]),X(q[a+1012>>2],4,q[q[a+704>>2]+32>>2]),X(q[a+1016>>2],4,q[q[a+704>>2]+32>>2]),X(q[a+1020>>2],4,q[q[a+704>>2]+32>>2]),X(q[a+1032>>2],4,q[q[a+704>>2]+36>>2]),X(q[a+1036>>2],4,q[q[a+704>>2]+36>>2]),X(q[a+1040>>2],4,q[q[a+704>>2]+36>>2]),X(q[a+1052>>2],4,q[q[a+704>>2]+40>>2]),X(q[a+1064>>2],4,q[q[a+704>>2]+44>>2]),X(q[a+1068>>2],4,q[q[a+704>>2]+48>>2]),X(q[a+1072>>2],4,q[q[a+704>>2]+48>>2]),X(q[a+1056>>2],4,q[q[a+704>>2]+52>>2]),X(q[a+1060>>2],4,q[q[a+704>>2]+52>>2]),X(q[a+1192>>2],4,q[q[a+704>>2]+56>>2]),X(q[a+1196>>2],4,q[q[a+704>>2]+60>>2]),X(q[a+1200>>2],2,q[q[a+704>>2]+64>>2]),X(q[a+1204>>2],4,q[q[a+704>>2]+68>>2]),X(q[a+1208>>2],4,q[q[a+704>>2]+72>>2]),X(q[a+1212>>2],4,q[q[a+704>>2]+72>>2]),X(q[a+1216>>2],4,q[q[a+704>>2]+72>>2]),X(q[a+1220>>2],4,q[q[a+704>>2]+72>>2]),X(q[a+1224>>2],4,q[q[a+704>>2]+72>>2]),X(q[a+1228>>2],4,q[q[a+704>>2]+76>>2]),X(q[a+1232>>2],4,q[q[a+704>>2]+76>>2]),X(q[a+1236>>2],4,q[q[a+704>>2]+76>>2]),X(q[a+1248>>2],4,q[q[a+704>>2]+80>>2]),X(q[a+1252>>2],4,q[q[a+704>>2]+80>>2]),X(q[a+1256>>2],4,q[q[a+704>>2]+80>>2]),X(q[a+1260>>2],4,q[q[a+704>>2]+80>>2]),X(q[a+1264>>2],4,q[q[a+704>>2]+80>>2]),X(q[a+1268>>2],4,q[q[a+704>>2]+80>>2]),X(q[a+1272>>2],4,q[q[a+704>>2]+80>>2]),X(q[a+1276>>2],4,q[q[a+704>>2]+84>>2]),X(q[a+1280>>2],2,q[q[a+704>>2]+84>>2]),X(q[a+1284>>2],4,q[q[a+704>>2]+88>>2]),Xa>>>0<2||(X(q[a+808>>2],4,q[q[a+704>>2]+8>>2]),Xa>>>0<4)||(X(q[a+968>>2],4,q[q[a+704>>2]+20>>2]),X(q[a+972>>2],4,q[q[a+704>>2]+20>>2]),X(q[a+792>>2],4,q[q[a+704>>2]+8>>2]),X(q[a+824>>2],4,q[q[a+704>>2]+12>>2]),X(q[a+864>>2],4,q[q[a+704>>2]+16>>2]),X(q[a+1288>>2],4,q[q[a+704>>2]+92>>2]),X(q[a+1292>>2],4,q[q[a+704>>2]+92>>2]),X(q[a+1296>>2],4,q[q[a+704>>2]+92>>2]),X(q[a+1300>>2],4,q[q[a+704>>2]+96>>2]),X(q[a+1304>>2],4,q[q[a+704>>2]+96>>2]),X(q[a+1308>>2],4,q[q[a+704>>2]+96>>2]),X(q[a+944>>2],4,q[q[a+704>>2]+20>>2]),X(q[a+956>>2],4,q[q[a+704>>2]+20>>2]),X(q[a+960>>2],4,q[q[a+704>>2]+20>>2]),X(q[a+1076>>2],4,q[q[a+704>>2]+100>>2]),X(q[a+1080>>2],4,q[q[a+704>>2]+100>>2]),X(q[a+1084>>2],4,q[q[a+704>>2]+100>>2]),X(q[a+1088>>2],4,q[q[a+704>>2]+104>>2]),X(q[a+1092>>2],4,q[q[a+704>>2]+104>>2]),X(q[a+1096>>2],4,q[q[a+704>>2]+104>>2]),X(q[a+1100>>2],4,q[q[a+704>>2]+104>>2]),X(q[a+1104>>2],4,q[q[a+704>>2]+104>>2]),X(q[a+1120>>2],4,q[q[a+704>>2]+108>>2]),X(q[a+1124>>2],4,q[q[a+704>>2]+108>>2]),X(q[a+1128>>2],4,q[q[a+704>>2]+108>>2]),X(q[a+1144>>2],4,q[q[a+704>>2]+112>>2]),X(q[a+1148>>2],4,q[q[a+704>>2]+112>>2]),X(q[a+1152>>2],4,q[q[a+704>>2]+112>>2]),X(q[a+1168>>2],4,q[q[a+704>>2]+116>>2]),X(q[a+1172>>2],4,q[q[a+704>>2]+120>>2]),X(q[a+1176>>2],4,q[q[a+704>>2]+120>>2]),X(q[a+1180>>2],4,q[q[a+704>>2]+120>>2]),X(q[a+1184>>2],4,q[q[a+704>>2]+124>>2]),X(q[a+1188>>2],4,q[q[a+704>>2]+124>>2]),4!=(0|Xa)&&(X(q[a+988>>2],4,q[q[a+704>>2]+28>>2]),X(q[a+992>>2],4,q[q[a+704>>2]+28>>2]),X(q[a+1024>>2],4,q[q[a+704>>2]+32>>2]),X(q[a+1028>>2],4,q[q[a+704>>2]+32>>2]),X(q[a+1044>>2],4,q[q[a+704>>2]+36>>2]),X(q[a+1048>>2],4,q[q[a+704>>2]+36>>2]),X(q[a+1108>>2],4,q[q[a+704>>2]+128>>2]),X(q[a+1112>>2],4,q[q[a+704>>2]+128>>2]),X(q[a+1116>>2],4,q[q[a+704>>2]+128>>2]),X(q[a+1132>>2],4,q[q[a+704>>2]+132>>2]),X(q[a+1136>>2],4,q[q[a+704>>2]+132>>2]),X(q[a+1140>>2],4,q[q[a+704>>2]+132>>2]),X(q[a+1156>>2],4,q[q[a+704>>2]+136>>2]),X(q[a+1160>>2],4,q[q[a+704>>2]+136>>2]),X(q[a+1164>>2],4,q[q[a+704>>2]+136>>2])))}function za(a,Za){var $a=0,lb=0,mb=0,ob=0,pb=0,rb=0,nb=a+Za|0;a:{b:if(!(1&($a=q[a+4>>2]))){if(!(3&$a))break a;if(Za=($a=q[a>>2])+Za|0,(0|(a=a-$a|0))!=q[2100])if($a>>>0<=255)mb=$a>>>3,$a=q[a+8>>2],(0|(lb=q[a+12>>2]))==(0|$a)?(rb=q[2095]&fd(mb),q[2095]=rb):(q[$a+12>>2]=lb,q[lb+8>>2]=$a);else{if(pb=q[a+24>>2],(0|($a=q[a+12>>2]))!=(0|a))lb=q[a+8>>2],q[lb+12>>2]=$a,q[$a+8>>2]=lb;else if(mb=(mb=q[(lb=a+20|0)>>2])||q[(lb=a+16|0)>>2]){for(;ob=lb,(mb=q[(lb=($a=mb)+20|0)>>2])||(lb=$a+16|0,mb=q[$a+16>>2]););q[ob>>2]=0}else $a=0;if(pb){lb=q[a+28>>2];e:{if(q[(mb=8684+(lb<<2)|0)>>2]==(0|a)){if(q[mb>>2]=$a)break e;rb=q[2096]&fd(lb),q[2096]=rb;break b}if(!(q[pb+(q[pb+16>>2]==(0|a)?16:20)>>2]=$a))break b}q[$a+24>>2]=pb,(lb=q[a+16>>2])&&(q[$a+16>>2]=lb,q[lb+24>>2]=$a),(lb=q[a+20>>2])&&(q[$a+20>>2]=lb,q[lb+24>>2]=$a)}}else if(3==(3&($a=q[4+nb>>2])))return q[2097]=Za,q[4+nb>>2]=-2&$a,q[a+4>>2]=1|Za,q[nb>>2]=Za}f:{if(!(2&($a=q[4+nb>>2]))){if(q[2101]==(0|nb)){if(q[2101]=a,Za=q[2098]+Za|0,q[2098]=Za,q[a+4>>2]=1|Za,q[2100]!=(0|a))break a;return q[2097]=0,q[2100]=0}if(q[2100]==(0|nb))return q[2100]=a,Za=q[2097]+Za|0,q[2097]=Za,q[a+4>>2]=1|Za,q[a+Za>>2]=Za;Za=(-8&$a)+Za|0;g:if($a>>>0<=255)mb=$a>>>3,$a=q[8+nb>>2],(0|(lb=q[12+nb>>2]))==(0|$a)?(rb=q[2095]&fd(mb),q[2095]=rb):(q[$a+12>>2]=lb,q[lb+8>>2]=$a);else{if(pb=q[24+nb>>2],(0|nb)!=(0|($a=q[12+nb>>2])))lb=q[8+nb>>2],q[lb+12>>2]=$a,q[$a+8>>2]=lb;else if(mb=(mb=q[(lb=20+nb|0)>>2])||q[(lb=16+nb|0)>>2]){for(;ob=lb,(mb=q[(lb=($a=mb)+20|0)>>2])||(lb=$a+16|0,mb=q[$a+16>>2]););q[ob>>2]=0}else $a=0;if(pb){lb=q[28+nb>>2];j:{if(q[(mb=8684+(lb<<2)|0)>>2]==(0|nb)){if(q[mb>>2]=$a)break j;rb=q[2096]&fd(lb),q[2096]=rb;break g}if(!(q[pb+(q[pb+16>>2]==(0|nb)?16:20)>>2]=$a))break g}q[$a+24>>2]=pb,(lb=q[16+nb>>2])&&(q[$a+16>>2]=lb,q[lb+24>>2]=$a),(lb=q[20+nb>>2])&&(q[$a+20>>2]=lb,q[lb+24>>2]=$a)}}if(q[a+4>>2]=1|Za,q[a+Za>>2]=Za,q[2100]!=(0|a))break f;return q[2097]=Za}q[4+nb>>2]=-2&$a,q[a+4>>2]=1|Za,q[a+Za>>2]=Za}if(Za>>>0<=255)return Za=8420+(($a=Za>>>3)<<3)|0,$a=(lb=q[2095])&($a=1<<$a)?q[Za+8>>2]:(q[2095]=$a|lb,Za),q[Za+8>>2]=a,q[$a+12>>2]=a,q[a+12>>2]=Za,q[a+8>>2]=$a;q[a+16>>2]=0,$a=q[a+20>>2]=0,(mb=Za>>>8)&&($a=31,16777215<Za>>>0||($a=28+(($a=((nb=(mb<<=ob=mb+1048320>>>16&8)<<($a=mb+520192>>>16&4))<<(mb=245760+nb>>>16&2)>>>15)-(mb|$a|ob)|0)<<1|Za>>>$a+21&1)|0)),mb=8684+((q[(lb=a)+28>>2]=$a)<<2)|0;m:{if((lb=q[2096])&(ob=1<<$a)){for(lb=Za<<(31==(0|$a)?0:25-($a>>>1)|0),$a=q[mb>>2];;){if((-8&q[(mb=$a)+4>>2])==(0|Za))break m;if($a=lb>>>29,lb<<=1,!($a=q[16+(ob=mb+(4&$a)|0)>>2]))break}q[ob+16>>2]=a}else q[2096]=lb|ob,q[mb>>2]=a;return q[a+24>>2]=mb,q[a+12>>2]=a,q[a+8>>2]=a}Za=q[mb+8>>2],q[Za+12>>2]=a,q[mb+8>>2]=a,q[a+24>>2]=0,q[a+12>>2]=mb,q[a+8>>2]=Za}}function Aa(a){var vb,xb,yb,Ab,Bb,Cb,sb,wb,Za=x(0),tb=(x(0),0),ub=0,zb=(x(0),x(0),x(0),x(0),0);x(0),x(0);a:{b:{if(j(a),ub=2147483647&(tb=b[0])){if(!(ub>>>0<2139095041))return x(x(.10000000149011612)+a);if(1065353216==(0|ub))return x(-1<(0|tb)?.10000000149011612:10);if(2139095040==(0|ub))return x(-1<(0|tb)?0:-a);if(1073741824==(0|tb))return x(.010000000707805157);if(1056964608==(0|tb))return x(.3162277638912201);if(1291845633<=ub>>>0)return x((0|tb)<0?H:0);if(vb=u[1709],wb=x(x(1.600000023841858)-vb),xb=x(x(1)/x(vb+x(1.600000023841858))),f(0,-4096&(j(sb=x(wb*xb)),b[0])),Za=k(),yb=x(Za*Za),Bb=u[1713],vb=x(xb*x(x(wb-x((Ab=Za)*x(3.099609375)))-x(Za*x(x(1.600000023841858)-x(x(3.099609375)-vb))))),xb=x(x(sb+Za)*vb),Za=x(sb*sb),wb=x(xb+x(x(Za*Za)*x(x(Za*x(x(Za*x(x(Za*x(x(Za*x(x(Za*x(.20697501301765442))+x(.23066075146198273)))+x(.2727281153202057)))+x(.3333333432674408)))+x(.4285714328289032)))+x(.6000000238418579)))),f(0,-4096&(j(x(x(yb+x(3))+wb)),b[0])),Za=k(),xb=x(Ab*Za),sb=x(x(vb*Za)+x(sb*x(wb-x(x(Za+x(-3))-yb)))),f(0,-4096&(j(x(xb+sb)),b[0])),Za=k(),vb=x(Za*x(.9619140625)),yb=x(u[1711]+x(x(x(sb-x(Za-xb))*x(.9617967009544373))+x(Za*x(-.00011736857413779944)))),f(0,-4096&(j(x(x(Bb+x(vb+yb))+x(-4))),b[0])),sb=k(),f(0,-4096&tb),wb=k(),Za=x(sb*wb),a=x(x(x(yb-x(x(x(sb-x(-4))-Bb)-vb))*a)+x(x(a-wb)*sb)),j(sb=x(Za+a)),1124073473<=(0|(tb=b[0])))break b;d:{if((ub=1124073472)==(0|tb)){if(x(a+x(4.299566569443414e-8))>x(sb-Za))break b}else{if(ub=2147483647&tb,!(a<=x(sb-Za)^1|-1021968384!=(0|tb))|1125515265<=ub>>>0)break a;if(ub>>>0<1056964609)break d}zb=(8388607&(ub=(8388608>>>(ub>>>23)-126)+tb|0)|8388608)>>>150-(Cb=ub>>>23&255),zb=(0|tb)<0?0-zb|0:zb,Za=x(Za-(f(0,ub&-8388608>>Cb-127),k())),j(x(a+Za)),tb=b[0]}f(0,-32768&tb),sb=k(),vb=x(sb*x(.693145751953125)),sb=x(x(sb*x(14286065379565116e-22))+x(x(a-x(sb-Za))*x(.6931471824645996))),a=x(vb+sb),Za=x(a*a),Za=x(a-x(Za*x(x(Za*x(x(Za*x(x(Za*x(x(Za*x(4.138136944220605e-8))+x(-16533901998627698e-22)))+x(661375597701408e-19)))+x(-.0027777778450399637)))+x(.1666666716337204)))),Ab=x(x(a*Za)/x(Za+x(-2))),Za=x(sb-x(a-vb)),a=(0|(tb=0|(j(a=x(x(a-x(Ab-x(Za+x(a*Za))))+x(1))),b[0]+(zb<<23))))<=8388607?function(a,Tk){var vl=0;return 128<=(0|Tk)?(a=x(a*x(17014118346046923e22)),Tk=(0|(vl=Tk+-127|0))<128?vl:(a=x(a*x(17014118346046923e22)),((0|Tk)<381?Tk:381)+-254|0)):-127<(0|Tk)||(a=x(a*x(11754943508222875e-54)),Tk=-127<(0|(vl=Tk+126|0))?vl:(a=x(a*x(11754943508222875e-54)),(-378<(0|Tk)?Tk:-378)+252|0)),x(a*(f(0,1065353216+(Tk<<23)|0),k()))}(a,zb):(f(0,tb),k()),a=x(x(1)*a)}else a=x(1);return a}return x(H)}return x(0)}function Ba(a,Db){var Jb,Eb,Gb,Fb=0,Hb=0,Ib=x(0);if(j(Db),!((Gb=2147483647&(Eb=b[0]))>>>0<=2139095040&&(j(a),(Fb=2147483647&(Hb=b[0]))>>>0<2139095041)))return x(a+Db);if(1065353216==(0|Eb))return Ca(a);Eb=(Jb=Eb>>>30&2)|Hb>>>31;b:{c:{d:{e:{if(!Fb){switch(Eb-2|0){case 0:break e;case 1:break;default:break d}return x(-3.1415927410125732)}if(2139095040!=(0|Gb)){if(!Gb|!(Fb>>>0<=218103808+Gb>>>0&&2139095040!=(0|Fb)))break b;if(a=Ib=Fb+218103808>>>0<Gb>>>0&&(Ib=x(0),Jb)?Ib:Ca(x(y(x(a/Db)))),Eb>>>0<=2){switch(Eb-1|0){case 0:return x(-a);case 1:break;default:break d}return x(x(3.1415927410125732)-x(a+x(8.742277657347586e-8)))}return x(x(a+x(8.742277657347586e-8))+x(-3.1415927410125732))}if(2139095040==(0|Fb))break c;return u[6816+(Eb<<2)>>2]}a=x(3.1415927410125732)}return a}return u[6800+(Eb<<2)>>2]}return x((0|Hb)<0?-1.5707963705062866:1.5707963705062866)}function Ca(a){x(0);var Kb,Nb,Ob,Db,Mb,Lb=0;x(0),x(0),j(a);a:{if(1283457024<=(Db=2147483647&(Mb=b[0]))>>>0){if(2139095040<Db>>>0)break a;return x((0|Mb)<0?-1.570796251296997:1.570796251296997)}b:{if(Db>>>0<=1054867455){if(Lb=-1,964689920<=Db>>>0)break b;break a}a=x(y(a)),Lb=Db>>>0<=1066926079?Db>>>0<=1060110335?(a=x(x(x(a+a)+x(-1))/x(a+x(2))),0):(a=x(x(a+x(-1))/x(a+x(1))),1):Db>>>0<=1075576831?(a=x(x(a+x(-1.5))/x(x(a*x(1.5))+x(1))),2):(a=x(x(-1)/a),3)}if(Db=Lb,Nb=x(a*a),Kb=x(Nb*Nb),Ob=x(Kb*x(x(Kb*x(-.106480173766613))+x(-.19999158382415771))),Kb=x(Nb*x(x(Kb*x(x(Kb*x(.06168760731816292))+x(.14253635704517365)))+x(.333333283662796))),(0|Db)<=-1)return x(a-x(a*x(Ob+Kb)));a=x(u[6768+(Db<<=2)>>2]-x(x(x(a*x(Ob+Kb))-u[6784+Db>>2])-a)),a=(0|Mb)<0?x(-a):a}return a}function Da(a,Pb){var Ub,Sb,Tb,Qb=0,Rb=0;return L=Sb=L-16|0,j(a),(Qb=2147483647&(Tb=b[0]))>>>0<=1305022426?(v[Pb>>3]=(Ub=+a)+-1.5707963109016418*(Rb=.6366197723675814*Ub+6755399441055744-6755399441055744)+-1.5893254773528196e-8*Rb,Qb=y(Rb)<2147483648?~~Rb:-2147483648):2139095040<=Qb>>>0?(v[Pb>>3]=x(a-a),Qb=0):(Ub=Qb,v[8+Sb>>3]=(f(0,Ub-((Qb=(Qb>>>23)-150|0)<<23)|0),k()),Qb=function(a,Gl,Hl){var Ll,Ql,Ul,Vl,Xl,Yl,Il=0,Jl=0,Kl=0,Ml=0,Nl=0,Ol=0,Pl=0,Rl=0,Sl=0,Tl=0,Wl=0;if(L=Ll=L-560|0,Pl=(Jl=Hl)+w(Ul=0<(0|(Hl=(Hl+-3|0)/24|0))?Hl:0,-24)|0,0<=(0|(Ql=q[980])))for(Jl=Ql+1|0,Hl=Ul;v[(320+Ll|0)+(Kl<<3)>>3]=(0|Hl)<0?0:+q[3936+(Hl<<2)>>2],Hl=Hl+1|0,(0|Jl)!=(0|(Kl=Kl+1|0)););for(Nl=Pl+-24|0,Jl=0;;){for(Il=Hl=0;Il+=v[(Hl<<3)+a>>3]*v[(320+Ll|0)+(Jl-Hl<<3)>>3],1!=(0|(Hl=Hl+1|0)););if(v[(Jl<<3)+Ll>>3]=Il,Hl=(0|Jl)<(0|Ql),Jl=Jl+1|0,!Hl)break}Yl=23-Nl|0,Vl=24-Nl|0,Jl=Ql;a:{for(;;){if(Il=v[(Jl<<3)+Ll>>3],!(Sl=((Hl=0)|(Kl=Jl))<1))for(;Ol=(480+Ll|0)+(Hl<<2)|0,Rl=Il,Ml=y(Il*=5.960464477539063e-8)<2147483648?~~Il:-2147483648,Ml=y(Rl+=-16777216*(Il=0|Ml))<2147483648?~~Rl:-2147483648,q[Ol>>2]=Ml,Il=v[((Kl=Kl+-1|0)<<3)+Ll>>3]+Il,(0|Jl)!=(0|(Hl=Hl+1|0)););Il=ja(Il,Nl),Il=(Il+=-8*C(.125*Il))-(0|(Ol=y(Il)<2147483648?~~Il:-2147483648));e:{f:{g:{if(Wl=(0|Nl)<1){if(Nl)break g;Ml=q[476+((Jl<<2)+Ll|0)>>2]>>23}else Ml=q[476+(Kl=(Jl<<2)+Ll|0)>>2],Tl=Kl,Kl=Ml-((Hl=Ml>>Vl)<<Vl)|0,Ol=Hl+Ol|0,Ml=(q[Tl+476>>2]=Kl)>>Yl;if((0|Ml)<1)break e;break f}if(Ml=2,!(.5<=Il)){Ml=0;break e}}if(Kl=Hl=0,!Sl)for(;;){Sl=q[(Xl=(480+Ll|0)+(Hl<<2)|0)>>2],Tl=16777215;i:{j:{if(!Kl){if(!Sl)break j;Tl=16777216,Kl=1}q[Xl>>2]=Tl-Sl;break i}Kl=0}if((0|Jl)==(0|(Hl=Hl+1|0)))break}Wl||1<(Hl=Nl+-1|0)>>>0||(q[476+(Hl=(Jl<<2)+Ll|0)>>2]=Hl-1?8388607&q[Hl+476>>2]:4194303&q[Hl+476>>2]),Ol=Ol+1|0,2==(0|Ml)&&(Il=1-Il,Ml=2,Kl)&&(Il-=ja(1,Nl))}if(0!=Il)break;if(!(((Kl=0)|(Hl=Jl))<=(0|Ql))){for(;Kl=q[(480+Ll|0)+((Hl=Hl+-1|0)<<2)>>2]|Kl,(0|Ql)<(0|Hl););if(Kl){for(Pl=Nl;Pl=Pl+-24|0,!q[(480+Ll|0)+((Jl=Jl+-1|0)<<2)>>2];);break a}}for(Hl=1;Hl=(Kl=Hl)+1|0,!q[(480+Ll|0)+(Ql-Kl<<2)>>2];);for(Kl=Jl+Kl|0;;){for(Jl=Ol=Jl+1|0,v[(320+Ll|0)+(Ol<<3)>>3]=q[3936+(Ul+Jl<<2)>>2],Il=Hl=0;Il+=v[(Hl<<3)+a>>3]*v[(320+Ll|0)+(Ol-Hl<<3)>>3],1!=(0|(Hl=Hl+1|0)););if(v[(Jl<<3)+Ll>>3]=Il,!((0|Jl)<(0|Kl)))break}Jl=Kl}16777216<=(Il=ja(Il,0-Nl|0))?(a=(480+Ll|0)+(Jl<<2)|0,Rl=Il,Hl=y(Il*=5.960464477539063e-8)<2147483648?~~Il:-2147483648,Kl=y(Il=Rl+-16777216*(0|Hl))<2147483648?~~Il:-2147483648,q[a>>2]=Kl,Jl=Jl+1|0):(Hl=y(Il)<2147483648?~~Il:-2147483648,Pl=Nl),q[(480+Ll|0)+(Jl<<2)>>2]=Hl}if(Il=ja(1,Pl),!((0|Jl)<=-1)){for(Hl=Jl;v[(Hl<<3)+Ll>>3]=Il*+q[(480+Ll|0)+(Hl<<2)>>2],Il*=5.960464477539063e-8,a=0<(0|Hl),Hl=Hl+-1|0,a;);if(!((0|Jl)<=-1))for(Hl=Jl;;){for(Nl=Jl-(a=Hl)|0,Hl=Il=0;Il+=v[6704+(Hl<<3)>>3]*v[(a+Hl<<3)+Ll>>3],!((0|Ql)<=(0|Hl))&&(Pl=Hl>>>0<Nl>>>0,Hl=Hl+1|0,Pl););if(v[(160+Ll|0)+(Nl<<3)>>3]=Il,Hl=a+-1|0,!(0<(0|a)))break}}if(0<=(Jl|(Il=0)))for(;Il+=v[(160+Ll|0)+(Jl<<3)>>3],a=0<(0|Jl),Jl=Jl+-1|0,a;);return v[Gl>>3]=Ml?-Il:Il,L=560+Ll|0,7&Ol}(8+Sb|0,Sb,Qb),Rb=v[Sb>>3],(0|Tb)<=-1?(v[Pb>>3]=-Rb,Qb=0-Qb|0):v[Pb>>3]=Rb),L=16+Sb|0,Qb}function Ea(a,Pb){return a?function(a,Gl){a:{if(a){if(Gl>>>0<=127)break a;if(q[q[1797]>>2]){if(Gl>>>0<=2047)return o[a+1|0]=63&Gl|128,o[0|a]=Gl>>>6|192,2;if(!(57344!=(-8192&Gl)&&55296<=Gl>>>0))return o[a+2|0]=63&Gl|128,o[0|a]=Gl>>>12|224,o[a+1|0]=Gl>>>6&63|128,3;if(Gl+-65536>>>0<=1048575)return o[a+3|0]=63&Gl|128,o[0|a]=Gl>>>18|240,o[a+2|0]=Gl>>>6&63|128,o[a+1|0]=Gl>>>12&63|128,4}else if(57216==(-128&Gl))break a;q[2094]=25,a=-1}else a=1;return a}return o[0|a]=Gl,1}(a,Pb):0}function Fa(a,Pb,Wb){var fc,gc,Xb=0,Yb=0,Zb=0,_b=0,$b=0,ac=0,bc=0,cc=0,dc=0,ec=r[a+4|0];if(q[Pb>>2]=652,Yb=q[a+704>>2],1<=(0|(_b=q[Yb>>2]))){for($b=q[a+720>>2],bc=q[a+1072>>2];Zb=(1<<q[bc+(q[$b+(Xb<<2)>>2]<<2)>>2])+Zb|0,(0|_b)!=(0|(Xb=Xb+1|0)););Xb=Zb<<2}if(q[Pb+4>>2]=w(_b,12),q[Pb+8>>2]=q[Yb>>2]<<2,q[Pb+12>>2]=q[Yb>>2]<<2,q[Pb+16>>2]=q[Yb>>2]<<2,q[Pb+20>>2]=q[Yb>>2]<<2,Zb=q[Yb>>2],q[Pb+28>>2]=Xb,q[Pb+24>>2]=Zb<<2,Zb=q[Yb>>2],q[Pb+40>>2]=Xb,q[Pb+36>>2]=Xb,q[Pb+32>>2]=Zb<<2,q[Pb+44>>2]=q[Yb+4>>2]<<5,q[Pb+48>>2]=q[Yb+4>>2]<<2,q[Pb+52>>2]=q[Yb+4>>2]<<2,q[Pb+56>>2]=q[Yb+4>>2]<<2,q[Pb+60>>2]=q[Yb+4>>2]<<4,q[Pb+64>>2]=q[Yb+4>>2]<<4,1<=((Xb=0)|(_b=q[Yb+8>>2]))){for($b=q[a+780>>2],bc=q[a+1072>>2],dc=q[a+796>>2],Zb=0;ac=(15+(q[(cc=Xb<<2)+dc>>2]<<3)&-16)+ac|0,Zb=(1<<q[bc+(q[$b+cc>>2]<<2)>>2])+Zb|0,(0|_b)!=(0|(Xb=Xb+1|0)););Xb=Zb<<2}if(q[Pb+68>>2]=w(_b,24),q[Pb+72>>2]=q[Yb+8>>2]<<2,q[Pb+76>>2]=q[Yb+8>>2]<<2,Zb=q[Yb+8>>2],q[Pb+84>>2]=ac,q[Pb+80>>2]=Zb<<2,q[Pb+88>>2]=q[Yb+8>>2]<<4,q[Pb+92>>2]=q[Yb+8>>2]<<4,Zb=q[Yb+8>>2],q[Pb+100>>2]=Xb,q[Pb+96>>2]=Zb<<2,Zb=q[Yb+8>>2],q[Pb+140>>2]=Xb,q[Pb+136>>2]=Xb,q[Pb+132>>2]=Xb,q[Pb+128>>2]=Xb,q[Pb+124>>2]=Xb,q[Pb+120>>2]=Xb,q[Pb+116>>2]=Xb,q[Pb+112>>2]=Xb,q[Pb+108>>2]=Xb,q[Pb+104>>2]=Zb<<2,q[Pb+144>>2]=q[Yb+8>>2]<<2,q[Pb+148>>2]=q[Yb+8>>2]<<2,q[Pb+152>>2]=q[Yb+8>>2]<<2,q[Pb+156>>2]=q[Yb+8>>2]<<2,q[Pb+160>>2]=q[Yb+8>>2]<<2,q[Pb+164>>2]=q[Yb+8>>2]<<2,1<=((Xb=ac=0)|(_b=q[Yb+12>>2]))){for($b=q[a+812>>2],bc=q[a+1072>>2],Zb=0;Zb=(1<<q[bc+(q[$b+(Xb<<2)>>2]<<2)>>2])+Zb|0,(0|_b)!=(0|(Xb=Xb+1|0)););Xb=Zb<<2}if(q[Pb+168>>2]=w(_b,12),q[Pb+172>>2]=q[Yb+12>>2]<<2,q[Pb+176>>2]=q[Yb+12>>2]<<2,q[Pb+180>>2]=q[Yb+12>>2]<<2,q[Pb+184>>2]=q[Yb+12>>2]<<2,q[Pb+188>>2]=q[Yb+12>>2]<<2,q[Pb+192>>2]=q[Yb+12>>2]<<2,q[Pb+196>>2]=q[Yb+12>>2]<<2,q[Pb+200>>2]=q[Yb+12>>2]<<2,q[Pb+204>>2]=q[Yb+12>>2]<<4,q[Pb+208>>2]=q[Yb+12>>2]<<4,Zb=q[Yb+12>>2],q[Pb+216>>2]=Xb,q[Pb+212>>2]=Zb<<2,Zb=q[Yb+12>>2],q[Pb+268>>2]=Xb,q[Pb+264>>2]=Xb,q[Pb+260>>2]=Xb,q[Pb+256>>2]=Xb,q[Pb+252>>2]=Xb,q[Pb+248>>2]=Xb,q[Pb+244>>2]=Xb,q[Pb+240>>2]=Xb,q[Pb+236>>2]=Xb,q[Pb+232>>2]=Xb,q[Pb+228>>2]=Xb,q[Pb+224>>2]=Xb,q[Pb+220>>2]=Zb<<2,q[Pb+272>>2]=q[Yb+12>>2]<<2,q[Pb+276>>2]=q[Yb+12>>2]<<2,q[Pb+280>>2]=q[Yb+12>>2]<<2,q[Pb+284>>2]=q[Yb+12>>2]<<2,q[Pb+288>>2]=q[Yb+12>>2]<<2,q[Pb+292>>2]=q[Yb+12>>2]<<2,1<=((Xb=0)|(Zb=q[Yb+16>>2]))){for($b=q[a+852>>2],bc=q[a+1072>>2],dc=q[a+892>>2],_b=0;ac=(15+(q[(cc=Xb<<2)+dc>>2]<<3)&-16)+ac|0,_b=(1<<q[bc+(q[$b+cc>>2]<<2)>>2])+_b|0,(0|Zb)!=(0|(Xb=Xb+1|0)););Xb=_b<<2}if(q[Pb+296>>2]=w(Zb,20),q[Pb+300>>2]=q[Yb+16>>2]<<2,q[Pb+304>>2]=q[Yb+16>>2],q[Pb+308>>2]=q[Yb+16>>2]<<2,q[Pb+312>>2]=q[Yb+16>>2]<<2,Zb=q[Yb+16>>2],q[Pb+320>>2]=ac,q[Pb+316>>2]=Zb<<2,q[Pb+324>>2]=q[Yb+16>>2]<<2,q[Pb+328>>2]=q[Yb+16>>2]<<4,q[Pb+332>>2]=q[Yb+16>>2]<<4,q[Pb+336>>2]=q[Yb+16>>2]<<2,q[Pb+340>>2]=q[Yb+16>>2]<<2,q[Pb+344>>2]=q[Yb+16>>2]<<2,q[Pb+348>>2]=q[Yb+16>>2]<<4,q[Pb+352>>2]=q[Yb+16>>2]<<4,Zb=q[Yb+16>>2],q[Pb+360>>2]=Xb,q[Pb+356>>2]=Zb<<2,Zb=q[Yb+16>>2],q[Pb+404>>2]=Xb,q[Pb+400>>2]=Xb,q[Pb+396>>2]=Xb,q[Pb+392>>2]=Xb,q[Pb+388>>2]=Xb,q[Pb+384>>2]=Xb,q[Pb+380>>2]=Xb,q[Pb+376>>2]=Xb,q[Pb+372>>2]=Xb,q[Pb+368>>2]=Xb,q[Pb+364>>2]=Zb<<2,q[Pb+408>>2]=q[Yb+16>>2]<<2,q[Pb+412>>2]=q[Yb+16>>2]<<2,q[Pb+416>>2]=q[Yb+16>>2]<<2,q[Pb+420>>2]=q[Yb+16>>2]<<2,q[Pb+424>>2]=q[Yb+16>>2]<<2,q[Pb+428>>2]=q[Yb+16>>2]<<2,$b=q[a+704>>2],q[Pb+432>>2]=w(q[$b+20>>2],52),q[Pb+436>>2]=ec>>>(Xb=_b=0)<=3?q[$b+20>>2]<<2:0,q[Pb+440>>2]=q[$b+20>>2]<<2,q[Pb+444>>2]=w(q[$b+52>>2],28),1<=(0|(Yb=q[$b+48>>2]))){for(Zb=q[a+1072>>2],ac=0;ac=(bc=q[Zb+(Xb<<2)>>2])+ac|0,_b=(1<<bc)+_b|0,(0|Yb)!=(0|(Xb=Xb+1|0)););Xb=_b<<2,_b=ac<<2}if(q[Pb+460>>2]=Xb,q[Pb+456>>2]=Xb,q[Pb+452>>2]=_b,q[Pb+448>>2]=w(Yb,36),q[Pb+500>>2]=w(q[$b+72>>2],28),1<=((ac=Xb=Zb=0)|(bc=q[$b+72>>2]))){for(dc=q[a+1224>>2],cc=q[a+1220>>2],gc=q[a+1212>>2],_b=0;_b=(0|(fc=q[(Yb=ac<<2)+cc>>2]-q[Yb+dc>>2]|0))<(0|_b)?_b:1+fc|0,Xb=(0|Xb)<(0|(Yb=q[Yb+gc>>2]))?Yb:Xb,(0|bc)!=(0|(ac=ac+1|0)););ac=Xb<<2,Xb=_b<<2}if(Yb=q[$b+76>>2],q[Pb+516>>2]=Xb,q[Pb+512>>2]=ac,q[Pb+508>>2]=Xb,q[Pb+504>>2]=Yb<<4,1<=(0|(Yb=q[$b+80>>2]))){for(Zb=q[a+1248>>2],ac=q[a+1072>>2],_b=Xb=0;_b=(1<<q[ac+(q[Zb+(Xb<<2)>>2]<<2)>>2])+_b|0,(0|Yb)!=(0|(Xb=Xb+1|0)););Zb=_b<<2}if(q[Pb+520>>2]=w(Yb,24),q[Pb+524>>2]=q[$b+80>>2]<<2,Yb=q[$b+80>>2],q[Pb+532>>2]=Zb,q[Pb+528>>2]=Yb<<2,Yb=q[$b+80>>2],q[Pb+544>>2]=Zb,q[Pb+540>>2]=Zb,q[Pb+536>>2]=Yb<<2,Yb=Pb,4<=ec>>>0){if(q[Pb+464>>2]=w(q[$b+120>>2],20),q[Pb+468>>2]=w(q[$b+100>>2],28),Zb=Pb,1<=((Xb=ac=0)|(bc=q[$b+104>>2]))){for(a=q[a+1104>>2],_b=0;_b=q[a+(Xb<<2)>>2]+_b|0,(0|bc)!=(0|(Xb=Xb+1|0)););a=_b<<2}else a=0;q[Zb+476>>2]=a,q[Pb+472>>2]=w(bc,48),q[Pb+484>>2]=w(q[$b+108>>2],12),a=q[$b+112>>2],q[Pb+552>>2]=0,q[Pb+492>>2]=w(a,12),a=0}else{if((0|(ac=q[$b+20>>2]))<1)_b=0;else for(bc=q[a+1060>>2],dc=q[a+952>>2],a=q[a+948>>2],Zb=_b=0;;){if(1<=(0|(cc=q[(Xb=Zb<<2)+dc>>2])))for(cc=(Xb=bc+(q[a+Xb>>2]<<2)|0)+(cc<<2)|0;_b=q[Xb>>2]+_b|0,(Xb=Xb+4|0)>>>0<cc>>>0;);if((0|ac)==(0|(Zb=Zb+1|0)))break}q[Pb+552>>2]=ac<<2,ac=q[$b+20>>2]<<2,a=_b<<2}for(q[Yb+556>>2]=a,q[Pb+548>>2]=ac,4<ec>>>0&&(q[Pb+480>>2]=w(q[$b+128>>2],12),q[Pb+488>>2]=w(q[$b+132>>2],12),q[Pb+496>>2]=w(q[$b+136>>2],12)),Xb=_b=0;Xb=((Yb=q[(a=(_b<<2)+Pb|0)>>2])+15&-16)+(q[a>>2]=Xb)|0,140!=(0|(_b=_b+1|0)););q[Wb>>2]=Xb}function Ga(a,Pb,Wb,hc){a:{if(!(20<Pb>>>0||9<(Pb=Pb+-9|0)>>>0)){switch(Pb-1|0){default:return Pb=q[Wb>>2],q[Wb>>2]=Pb+4,q[a>>2]=q[Pb>>2];case 0:return Pb=q[Wb>>2],q[Wb>>2]=Pb+4,Pb=q[Pb>>2],q[a>>2]=Pb,q[a+4>>2]=Pb>>31;case 1:return Pb=q[Wb>>2],q[Wb>>2]=Pb+4,q[a>>2]=q[Pb>>2],q[a+4>>2]=0;case 3:return Pb=q[Wb>>2],q[Wb>>2]=Pb+4,Pb=p[Pb>>1],q[a>>2]=Pb,q[a+4>>2]=Pb>>31;case 4:return Pb=q[Wb>>2],q[Wb>>2]=Pb+4,q[a>>2]=s[Pb>>1],q[a+4>>2]=0;case 5:return Pb=q[Wb>>2],q[Wb>>2]=Pb+4,Pb=o[0|Pb],q[a>>2]=Pb,q[a+4>>2]=Pb>>31;case 6:return Pb=q[Wb>>2],q[Wb>>2]=Pb+4,q[a>>2]=r[0|Pb],q[a+4>>2]=0;case 2:case 7:break a;case 8:}n[hc](a,Wb)}return}Pb=q[Wb>>2]+7&-8,q[Wb>>2]=Pb+8,Wb=q[Pb+4>>2],q[a>>2]=q[Pb>>2],q[a+4>>2]=Wb}function Ha(a){var Pb,hc,Wb=0;if(ha(o[q[a>>2]]))for(;Pb=q[a>>2],hc=o[0|Pb],q[a>>2]=Pb+1,Wb=(w(Wb,10)+hc|0)-48|0,ha(o[Pb+1|0]););return Wb}function Ia(a,ic,jc,kc,lc){var oc,mc;q[204+(L=mc=L-208|0)>>2]=jc,ca(160+mc|(jc=0),0,40),q[200+mc>>2]=q[204+mc>>2],(0|ra(0,ic,200+mc|0,80+mc|0,160+mc|0,kc,lc))<0||(jc=0<=q[a+76>>2]?1:jc,jc=q[a>>2],o[a+74|0]<=0&&(q[a>>2]=-33&jc),oc=32&jc,q[a+48>>2]?ra(a,ic,200+mc|0,80+mc|0,160+mc|0,kc,lc):(q[a+48>>2]=80,q[a+16>>2]=80+mc,q[a+28>>2]=mc,q[a+20>>2]=mc,jc=q[a+44>>2],ra(a,ic,200+(q[a+44>>2]=mc)|0,80+mc|0,160+mc|0,kc,lc),jc&&(n[q[a+36>>2]](a,0,0),q[a+48>>2]=0,q[a+44>>2]=jc,q[a+28>>2]=0,q[a+16>>2]=0,q[a+20>>2]=0)),q[a>>2]=q[a>>2]|oc),L=208+mc|0}function Ka(a,ic,pc){var rc,qc;$(8+(L=qc=L-160|0)|0,3216,144),q[52+qc>>2]=a,q[28+qc>>2]=a,q[56+qc>>2]=rc=(rc=-2-a|0)>>>0<256?rc:256,q[36+qc>>2]=a=a+rc|0,q[24+qc>>2]=a,Ia(8+qc|0,ic,pc,11,12),rc&&(a=q[28+qc>>2],o[a-((0|a)==q[24+qc>>2])|0]=0),L=160+qc|0}function La(a,ic){var sc,tc,pc=0,pc=0!=(0|ic);a:{b:{c:{d:if(!(!ic|!(3&a)))for(;;){if(!r[0|a])break c;if(a=a+1|0,pc=0!=(0|(ic=ic+-1|0)),!ic)break d;if(!(3&a))break}if(!pc)break b}if(!r[0|a])break a;e:{if(4<=ic>>>0){for(pc=(pc=ic+-4|0)-(sc=-4&pc)|0,sc=4+(a+sc|0)|0;;){if((-1^(tc=q[a>>2]))&tc+-16843009&-2139062144)break e;if(a=a+4|0,!(3<(ic=ic+-4|0)>>>0))break}ic=pc,a=sc}if(!ic)break b}for(;;){if(!r[0|a])break a;if(a=a+1|0,!(ic=ic+-1|0))break}}return 0}return a}function Ma(a){var uc,ic=0;if(!a)return 32;if(!(1&a))for(;ic=ic+1|0,uc=2&a,a>>>=1,!uc;);return ic}function Na(a,vc){var zc,Ac,Bc,yc,wc=0,xc=0,xc=4;L=yc=L-256|0;a:if(!((0|vc)<2))for(wc=q[(Bc=(vc<<2)+a|0)>>2]=yc;;){for($(wc,q[a>>2],zc=xc>>>0<256?xc:256),wc=0;$(q[(Ac=(wc<<2)+a|0)>>2],q[((wc=wc+1|0)<<2)+a>>2],zc),q[Ac>>2]=q[Ac>>2]+zc,(0|vc)!=(0|wc););if(!(xc=xc-zc|0))break a;wc=q[Bc>>2]}L=256+yc|0}function Oa(a){return Ma(q[a>>2]+-1|0)||((a=Ma(q[a+4>>2]))?a+32|0:0)}function $c(a,ap){ap|=0,b[0]=a|=0,b[1]=ap}function cd(a,ap,bp){return function(a,ap,bp){var fp,dp,cp,ep,gp=w(dp=bp>>>16,cp=a>>>16);return a=(65535&(cp=((fp=w(ep=65535&bp,a&=65535))>>>16)+w(cp,ep)|0))+w(a,dp)|0,M=((gp+w(ap,bp)|0)+(cp>>>16)|0)+(a>>>16)|0,65535&fp|a<<16}(a,ap,bp)}function dd(a,ap,bp){return function(a,ap,bp){var op,np,hp=0,ip=0,jp=0,kp=0,lp=0,mp=0,pp=0;a:{b:{c:{d:{e:{if(!(ip=ap))return $c((ap=a)-w(a=(a>>>0)/(bp>>>0)|0,bp)|0,0),M=0,a;if(hp=bp){if(!((kp=hp+-1|0)&hp))break e;lp=0-(kp=(z(hp)+33|0)-z(ip)|0)|0;break c}if(!a)return $c(0,ip-w(a=(ip>>>0)/0|0,0)|0),M=0,a;if((hp=32-z(ip)|0)>>>0<31)break d;break b}if($c(a&kp,0),1==(0|hp))break a;return bp=31&(hp=hp?31-z(hp+-1^hp)|0:32),a=32<=(63&hp)>>>0?(ip=0,ap>>>bp):(ip=ap>>>bp,((1<<bp)-1&ap)<<32-bp|a>>>bp),M=ip,a}kp=hp+1|0,lp=63-hp|0}if(hp=ap,jp=31&(ip=63&kp),jp=32<=ip>>>0?(ip=0,hp>>>jp):(ip=hp>>>jp,((1<<jp)-1&hp)<<32-jp|a>>>jp),hp=31&(lp&=63),32<=lp>>>0?(ap=a<<hp,a=0):(ap=(1<<hp)-1&a>>>32-hp|ap<<hp,a<<=hp),kp)for((lp=bp+(hp=-1)|0)>>>0<4294967295&&(hp=0);jp=(np=mp=jp<<1|ap>>>31)-(op=bp&(mp=hp-((ip=ip<<1|jp>>>31)+(lp>>>0<mp>>>0)|0)>>31))|0,ip=ip-(np>>>0<op>>>0)|0,ap=ap<<1|a>>>31,a=pp|a<<1,pp=mp&=1,kp=kp+-1|0;);return $c(jp,ip),M=ap<<1|a>>>31,mp|a<<1}$c(a,ap),ap=a=0}return M=ap,a}(a,ap,bp)}function fd(a){var qp;return(-1>>>(qp=31&a)&-2)<<qp|(-1<<(a=0-a&31)&-2)>>>a}function N(){return buffer.byteLength/65536|0}}(H,I,J)}}l=null,b.wasmBinary&&(F=b.wasmBinary);var WebAssembly={},F=[];"object"!=typeof WebAssembly&&E("no native wasm support detected");var I,J=new function(a){var c=Array(16);return c.grow=function(){17<=c.length&&B("Unable to grow wasm table. Use a higher value for RESERVED_FUNCTION_POINTERS or set ALLOW_TABLE_GROWTH."),c.push(null)},c.set=function(a,e){c[a]=e},c.get=function(a){return c[a]},c},K=!1;function assert(a,c){a||B("Assertion failed: "+c)}var buffer,M,L,N,ha="undefined"!=typeof TextDecoder?new TextDecoder("utf8"):void 0;function ia(a,c,d){var e=c+d;for(d=c;a[d]&&!(e<=d);)++d;if(16<d-c&&a.subarray&&ha)return ha.decode(a.subarray(c,d));for(e="";c<d;){var g,m,f=a[c++];128&f?(g=63&a[c++],192==(224&f)?e+=String.fromCharCode((31&f)<<6|g):(m=63&a[c++],(f=224==(240&f)?(15&f)<<12|g<<6|m:(7&f)<<18|g<<12|m<<6|63&a[c++])<65536?e+=String.fromCharCode(f):(f-=65536,e+=String.fromCharCode(55296|f>>10,56320|1023&f)))):e+=String.fromCharCode(f)}return e}function ja(a,c){return a?ia(L,a,c):""}function ka(a){return 0<a%65536&&(a+=65536-a%65536),a}function la(a){buffer=a,b.HEAP8=M=new Int8Array(a),b.HEAP16=new Int16Array(a),b.HEAP32=N=new Int32Array(a),b.HEAPU8=L=new Uint8Array(a),b.HEAPU16=new Uint16Array(a),b.HEAPU32=new Uint32Array(a),b.HEAPF32=new Float32Array(a),b.HEAPF64=new Float64Array(a)}"undefined"!=typeof TextDecoder&&new TextDecoder("utf-16le");var G=b.TOTAL_MEMORY||16777216;function O(a){for(;0<a.length;){var d,c=a.shift();"function"==typeof c?c():"number"==typeof(d=c.fa)?void 0===c.ea?b.dynCall_v(d):b.dynCall_vi(d,c.ea):d(void 0===c.ea?null:c.ea)}}(I=b.wasmMemory||new function(){return{buffer:new ArrayBuffer(G/65536*65536),grow:function(a){return ca(a)}}})&&(buffer=I.buffer),buffer.byteLength,la(buffer),N[2224]=5251968;var ra,ma=[],na=[],oa=[],pa=[],P=(Math.imul&&-5===Math.imul(4294967295,5)||(Math.imul=function(a,c){var d=65535&a,e=65535&c;return d*e+((a>>>16)*e+d*(c>>>16)<<16)|0}),Math.fround||(ra=new Float32Array(1),Math.fround=function(a){return ra[0]=a,ra[0]}),Math.clz32||(Math.clz32=function(a){var c=32,d=a>>16;return d&&(c-=16,a=d),(d=a>>8)&&(c-=8,a=d),(d=a>>4)&&(c-=4,a=d),(d=a>>2)&&(c-=2,a=d),a>>1?c-2:c-a}),Math.trunc||(Math.trunc=function(a){return a<0?Math.ceil(a):Math.floor(a)}),0),Q=null,U=null;function B(a){throw b.onAbort&&b.onAbort(a),D(a),E(a),K=!0,"abort("+a+"). Build with -s ASSERTIONS=1 for more info."}b.preloadedImages={},b.preloadedAudios={};var V="data:application/octet-stream;base64,";function W(a){return String.prototype.startsWith?a.startsWith(V):0===a.indexOf(V)}var X="_em_module.wasm";function ta(){try{if(F)return new Uint8Array(F);var a=z(X);if(a)return a;if(w)return w(X);throw"both async and sync fetching of the wasm failed"}catch(c){B(c)}}W(X)||(t=X,X=b.locateFile?b.locateFile(t,u):u+t),na.push({fa:function(){va()}});var wa=[null,[],[]],xa=!1;function C(a){for(var c=[],d=0;d<a.length;d++){var e=a[d];255<e&&(xa&&assert(!1,"Character code "+e+" ("+String.fromCharCode(e)+") at offset "+d+" not in 0x00-0xFF."),e&=255),c.push(String.fromCharCode(e))}return c.join("")}var ya="function"==typeof atob?atob:function(a){var c="",d=0;a=a.replace(/[^A-Za-z0-9\+\/=]/g,"");do{var e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(d++)),f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(d++)),g="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(d++)),m="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(d++)),e=e<<2|f>>4,f=(15&f)<<4|g>>2,h=(3&g)<<6|m}while(c+=String.fromCharCode(e),64!==g&&(c+=String.fromCharCode(f)),64!==m&&(c+=String.fromCharCode(h)),d<a.length);return c};function z(a){if(W(a)){if(a=a.slice(V.length),"boolean"==typeof r&&r){try{var c=Buffer.from(a,"base64")}catch(g){c=new Buffer(a,"base64")}var d=new Uint8Array(c.buffer,c.byteOffset,c.byteLength)}else try{for(var e=ya(a),f=new Uint8Array(e.length),c=0;c<e.length;++c)f[c]=e.charCodeAt(c);d=f}catch(g){throw Error("Converting base64 string to bytes failed.")}return d}}var Y,H={a:function(a,c,d){L.set(L.subarray(c,c+d),a)},b:function(a){if(2147418112<a)return!1;for(var c=Math.max(M.length,16777216);c<a;)c=c<=536870912?ka(2*c):Math.min(ka((3*c+2147483648)/4),2147418112);a:{try{I.grow(c-buffer.byteLength+65535>>16),la(I.buffer);var d=1;break a}catch(e){}d=void 0}return!!d},c:function(a,c,d,e){try{for(var f=0,g=0;g<d;g++){for(var m=N[c+8*g>>2],h=N[c+(8*g+4)>>2],A=0;A<h;A++){var R=L[m+A],S=wa[a];0===R||10===R?((1===a?D:E)(ia(S,0)),S.length=0):S.push(R)}f+=h}return N[e>>2]=f,0}catch(T){return"undefined"!=typeof FS&&T instanceof FS.ga||B(T),T.ha}},memory:I,table:J},u=function(){function a(a){b.asm=a.exports,P--,b.monitorRunDependencies&&b.monitorRunDependencies(P),0==P&&(null!==Q&&(clearInterval(Q),Q=null),U)&&(a=U,U=null,a())}function c(c){a(c.instance)}function d(a){(F||!p&&!q||"function"!=typeof fetch?new Promise(function(a){a(ta())}):fetch(X,{credentials:"same-origin"}).then(function(a){if(a.ok)return a.arrayBuffer();throw"failed to load wasm binary file at '"+X+"'"}).catch(ta)).then(function(){return{then:function(a){a({instance:new da})}}}).then(a,function(a){E("failed to asynchronously prepare wasm: "+a),B(a)})}var e={env:H,wasi_unstable:H};if(P++,b.monitorRunDependencies&&b.monitorRunDependencies(P),b.instantiateWasm)try{return b.instantiateWasm(e,a)}catch(f){return E("Module.instantiateWasm callback failed with error: "+f),!1}return F||"function"!=typeof WebAssembly.instantiateStreaming||W(X)||"function"!=typeof fetch?d(c):fetch(X,{credentials:"same-origin"}).then(function(a){return WebAssembly.instantiateStreaming(a,e).then(c,function(a){E("wasm streaming compile failed: "+a),E("falling back to ArrayBuffer instantiation"),d(c)})}),{}}(),va=(b.asm=u,b.___wasm_call_ctors=function(){return b.asm.d.apply(null,arguments)}),Aa=(b._csmGetLogFunction=function(){return b.asm.e.apply(null,arguments)},b._csmGetVersion=function(){return b.asm.f.apply(null,arguments)},b._csmGetLatestMocVersion=function(){return b.asm.g.apply(null,arguments)},b._csmGetMocVersion=function(){return b.asm.h.apply(null,arguments)},b._csmHasMocConsistency=function(){return b.asm.i.apply(null,arguments)},b._csmSetLogFunction=function(){return b.asm.j.apply(null,arguments)},b._csmReviveMocInPlace=function(){return b.asm.k.apply(null,arguments)},b._csmReadCanvasInfo=function(){return b.asm.l.apply(null,arguments)},b._csmGetSizeofModel=function(){return b.asm.m.apply(null,arguments)},b._csmInitializeModelInPlace=function(){return b.asm.n.apply(null,arguments)},b._csmUpdateModel=function(){return b.asm.o.apply(null,arguments)},b._csmGetParameterCount=function(){return b.asm.p.apply(null,arguments)},b._csmGetParameterIds=function(){return b.asm.q.apply(null,arguments)},b._csmGetParameterTypes=function(){return b.asm.r.apply(null,arguments)},b._csmGetParameterMinimumValues=function(){return b.asm.s.apply(null,arguments)},b._csmGetParameterMaximumValues=function(){return b.asm.t.apply(null,arguments)},b._csmGetParameterDefaultValues=function(){return b.asm.u.apply(null,arguments)},b._csmGetParameterValues=function(){return b.asm.v.apply(null,arguments)},b._csmGetParameterRepeats=function(){return b.asm.w.apply(null,arguments)},b._csmGetPartCount=function(){return b.asm.x.apply(null,arguments)},b._csmGetPartIds=function(){return b.asm.y.apply(null,arguments)},b._csmGetPartOpacities=function(){return b.asm.z.apply(null,arguments)},b._csmGetPartParentPartIndices=function(){return b.asm.A.apply(null,arguments)},b._csmGetDrawableCount=function(){return b.asm.B.apply(null,arguments)},b._csmGetDrawableIds=function(){return b.asm.C.apply(null,arguments)},b._csmGetDrawableConstantFlags=function(){return b.asm.D.apply(null,arguments)},b._csmGetDrawableDynamicFlags=function(){return b.asm.E.apply(null,arguments)},b._csmGetDrawableTextureIndices=function(){return b.asm.F.apply(null,arguments)},b._csmGetDrawableDrawOrders=function(){return b.asm.G.apply(null,arguments)},b._csmGetDrawableRenderOrders=function(){return b.asm.H.apply(null,arguments)},b._csmGetDrawableOpacities=function(){return b.asm.I.apply(null,arguments)},b._csmGetDrawableMaskCounts=function(){return b.asm.J.apply(null,arguments)},b._csmGetDrawableMasks=function(){return b.asm.K.apply(null,arguments)},b._csmGetDrawableVertexCounts=function(){return b.asm.L.apply(null,arguments)},b._csmGetDrawableVertexPositions=function(){return b.asm.M.apply(null,arguments)},b._csmGetDrawableVertexUvs=function(){return b.asm.N.apply(null,arguments)},b._csmGetDrawableIndexCounts=function(){return b.asm.O.apply(null,arguments)},b._csmGetDrawableIndices=function(){return b.asm.P.apply(null,arguments)},b._csmGetDrawableMultiplyColors=function(){return b.asm.Q.apply(null,arguments)},b._csmGetDrawableScreenColors=function(){return b.asm.R.apply(null,arguments)},b._csmGetDrawableParentPartIndices=function(){return b.asm.S.apply(null,arguments)},b._csmResetDrawableDynamicFlags=function(){return b.asm.T.apply(null,arguments)},b._csmGetParameterKeyCounts=function(){return b.asm.U.apply(null,arguments)},b._csmGetParameterKeyValues=function(){return b.asm.V.apply(null,arguments)},b._csmMallocMoc=function(){return b.asm.W.apply(null,arguments)},b._csmMallocModelAndInitialize=function(){return b.asm.X.apply(null,arguments)},b._csmMalloc=function(){return b.asm.Y.apply(null,arguments)},b._csmFree=function(){return b.asm.Z.apply(null,arguments)},b._csmInitializeAmountOfMemory=function(){return b.asm._.apply(null,arguments)},b.stackSave=function(){return b.asm.$.apply(null,arguments)}),Ba=b.stackAlloc=function(){return b.asm.aa.apply(null,arguments)},Ca=b.stackRestore=function(){return b.asm.ba.apply(null,arguments)},ca=b.__growWasmMemory=function(){return b.asm.ca.apply(null,arguments)};function Z(){function a(){if(!Y&&(Y=!0,!K)){if(O(na),O(oa),b.onRuntimeInitialized&&b.onRuntimeInitialized(),b.postRun)for("function"==typeof b.postRun&&(b.postRun=[b.postRun]);b.postRun.length;){var a=b.postRun.shift();pa.unshift(a)}O(pa)}}if(!(0<P)){if(b.preRun)for("function"==typeof b.preRun&&(b.preRun=[b.preRun]);b.preRun.length;)!function(){var a=b.preRun.shift();ma.unshift(a)}();O(ma),0<P||(b.setStatus?(b.setStatus("Running..."),setTimeout(function(){setTimeout(function(){b.setStatus("")},1),a()},1)):a())}}if(b.dynCall_vi=function(){return b.asm.da.apply(null,arguments)},b.asm=u,b.ccall=function(a,c,d,e){var f={string:function(a){var c=0;if(null!=a&&0!==a){var d=1+(a.length<<2),e=c=Ba(d),f=L;if(0<d){for(var d=e+d-1,g=0;g<a.length;++g){var k=a.charCodeAt(g);if((k=55296<=k&&k<=57343?65536+((1023&k)<<10)|1023&a.charCodeAt(++g):k)<=127){if(d<=e)break;f[e++]=k}else{if(k<=2047){if(d<=e+1)break;f[e++]=192|k>>6}else{if(k<=65535){if(d<=e+2)break;f[e++]=224|k>>12}else{if(d<=e+3)break;f[e++]=240|k>>18,f[e++]=128|k>>12&63}f[e++]=128|k>>6&63}f[e++]=128|63&k}}f[e]=0}}return c},array:function(a){var c=Ba(a.length);return M.set(a,c),c}},g=function(a){var c=b["_"+a];return assert(c,"Cannot call unknown function "+a+", make sure it is exported"),c}(a),m=[];if(a=0,e)for(var h=0;h<e.length;h++){var A=f[d[h]];A?(0===a&&(a=Aa()),m[h]=A(e[h])):m[h]=e[h]}return d=function(a){return"string"===c?ja(a):"boolean"===c?!!a:a}(d=g.apply(null,m)),0!==a&&Ca(a),d},b.UTF8ToString=ja,b.addFunction=function(a,c){var d=J.length;try{J.grow(1)}catch(e){if(!e instanceof RangeError)throw e;throw"Unable to grow wasm table. Use a higher value for RESERVED_FUNCTION_POINTERS or set ALLOW_TABLE_GROWTH."}try{J.set(d,a)}catch(e){if(!e instanceof TypeError)throw e;assert(void 0!==c,"Missing signature argument to addFunction"),J.set(d,a)}return d},b.then=function(a){var c;return Y?a(b):(c=b.onRuntimeInitialized,b.onRuntimeInitialized=function(){c&&c(),a(b)}),b},U=function Da(){Y||Z(),Y||(U=Da)},b.run=Z,b.preInit)for("function"==typeof b.preInit&&(b.preInit=[b.preInit]);0<b.preInit.length;)b.preInit.pop()();return Z(),_em_module}),_em=("object"==typeof exports&&"object"==typeof module?module.exports=_em_module:"function"==typeof define&&define.amd?define([],function(){return _em_module}):"object"==typeof exports&&(exports._em_module=_em_module),_em_module())}(Live2DCubismCore=Live2DCubismCore||{});
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/js/live2d/pixi.js b/xiaozhi-esp32-server/main/digital-human/js/live2d/pixi.js
new file mode 100644
index 0000000..f9d465d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/js/live2d/pixi.js
@@ -0,0 +1,43379 @@
+/*!
+ * pixi.js - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * pixi.js is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+var PIXI = (function (exports) {
+ 'use strict';
+
+ /**
+ * @this {Promise}
+ */
+ function finallyConstructor(callback) {
+ var constructor = this.constructor;
+ return this.then(
+ function(value) {
+ // @ts-ignore
+ return constructor.resolve(callback()).then(function() {
+ return value;
+ });
+ },
+ function(reason) {
+ // @ts-ignore
+ return constructor.resolve(callback()).then(function() {
+ // @ts-ignore
+ return constructor.reject(reason);
+ });
+ }
+ );
+ }
+
+ function allSettled(arr) {
+ var P = this;
+ return new P(function(resolve, reject) {
+ if (!(arr && typeof arr.length !== 'undefined')) {
+ return reject(
+ new TypeError(
+ typeof arr +
+ ' ' +
+ arr +
+ ' is not iterable(cannot read property Symbol(Symbol.iterator))'
+ )
+ );
+ }
+ var args = Array.prototype.slice.call(arr);
+ if (args.length === 0) { return resolve([]); }
+ var remaining = args.length;
+
+ function res(i, val) {
+ if (val && (typeof val === 'object' || typeof val === 'function')) {
+ var then = val.then;
+ if (typeof then === 'function') {
+ then.call(
+ val,
+ function(val) {
+ res(i, val);
+ },
+ function(e) {
+ args[i] = { status: 'rejected', reason: e };
+ if (--remaining === 0) {
+ resolve(args);
+ }
+ }
+ );
+ return;
+ }
+ }
+ args[i] = { status: 'fulfilled', value: val };
+ if (--remaining === 0) {
+ resolve(args);
+ }
+ }
+
+ for (var i = 0; i < args.length; i++) {
+ res(i, args[i]);
+ }
+ });
+ }
+
+ // Store setTimeout reference so promise-polyfill will be unaffected by
+ // other code modifying setTimeout (like sinon.useFakeTimers())
+ var setTimeoutFunc = setTimeout;
+
+ function isArray(x) {
+ return Boolean(x && typeof x.length !== 'undefined');
+ }
+
+ function noop() {}
+
+ // Polyfill for Function.prototype.bind
+ function bind(fn, thisArg) {
+ return function() {
+ fn.apply(thisArg, arguments);
+ };
+ }
+
+ /**
+ * @constructor
+ * @param {Function} fn
+ */
+ function Promise$1(fn) {
+ if (!(this instanceof Promise$1))
+ { throw new TypeError('Promises must be constructed via new'); }
+ if (typeof fn !== 'function') { throw new TypeError('not a function'); }
+ /** @type {!number} */
+ this._state = 0;
+ /** @type {!boolean} */
+ this._handled = false;
+ /** @type {Promise|undefined} */
+ this._value = undefined;
+ /** @type {!Array<!Function>} */
+ this._deferreds = [];
+
+ doResolve(fn, this);
+ }
+
+ function handle(self, deferred) {
+ while (self._state === 3) {
+ self = self._value;
+ }
+ if (self._state === 0) {
+ self._deferreds.push(deferred);
+ return;
+ }
+ self._handled = true;
+ Promise$1._immediateFn(function() {
+ var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
+ if (cb === null) {
+ (self._state === 1 ? resolve$1 : reject)(deferred.promise, self._value);
+ return;
+ }
+ var ret;
+ try {
+ ret = cb(self._value);
+ } catch (e) {
+ reject(deferred.promise, e);
+ return;
+ }
+ resolve$1(deferred.promise, ret);
+ });
+ }
+
+ function resolve$1(self, newValue) {
+ try {
+ // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
+ if (newValue === self)
+ { throw new TypeError('A promise cannot be resolved with itself.'); }
+ if (
+ newValue &&
+ (typeof newValue === 'object' || typeof newValue === 'function')
+ ) {
+ var then = newValue.then;
+ if (newValue instanceof Promise$1) {
+ self._state = 3;
+ self._value = newValue;
+ finale(self);
+ return;
+ } else if (typeof then === 'function') {
+ doResolve(bind(then, newValue), self);
+ return;
+ }
+ }
+ self._state = 1;
+ self._value = newValue;
+ finale(self);
+ } catch (e) {
+ reject(self, e);
+ }
+ }
+
+ function reject(self, newValue) {
+ self._state = 2;
+ self._value = newValue;
+ finale(self);
+ }
+
+ function finale(self) {
+ if (self._state === 2 && self._deferreds.length === 0) {
+ Promise$1._immediateFn(function() {
+ if (!self._handled) {
+ Promise$1._unhandledRejectionFn(self._value);
+ }
+ });
+ }
+
+ for (var i = 0, len = self._deferreds.length; i < len; i++) {
+ handle(self, self._deferreds[i]);
+ }
+ self._deferreds = null;
+ }
+
+ /**
+ * @constructor
+ */
+ function Handler(onFulfilled, onRejected, promise) {
+ this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
+ this.onRejected = typeof onRejected === 'function' ? onRejected : null;
+ this.promise = promise;
+ }
+
+ /**
+ * Take a potentially misbehaving resolver function and make sure
+ * onFulfilled and onRejected are only called once.
+ *
+ * Makes no guarantees about asynchrony.
+ */
+ function doResolve(fn, self) {
+ var done = false;
+ try {
+ fn(
+ function(value) {
+ if (done) { return; }
+ done = true;
+ resolve$1(self, value);
+ },
+ function(reason) {
+ if (done) { return; }
+ done = true;
+ reject(self, reason);
+ }
+ );
+ } catch (ex) {
+ if (done) { return; }
+ done = true;
+ reject(self, ex);
+ }
+ }
+
+ Promise$1.prototype['catch'] = function(onRejected) {
+ return this.then(null, onRejected);
+ };
+
+ Promise$1.prototype.then = function(onFulfilled, onRejected) {
+ // @ts-ignore
+ var prom = new this.constructor(noop);
+
+ handle(this, new Handler(onFulfilled, onRejected, prom));
+ return prom;
+ };
+
+ Promise$1.prototype['finally'] = finallyConstructor;
+
+ Promise$1.all = function(arr) {
+ return new Promise$1(function(resolve, reject) {
+ if (!isArray(arr)) {
+ return reject(new TypeError('Promise.all accepts an array'));
+ }
+
+ var args = Array.prototype.slice.call(arr);
+ if (args.length === 0) { return resolve([]); }
+ var remaining = args.length;
+
+ function res(i, val) {
+ try {
+ if (val && (typeof val === 'object' || typeof val === 'function')) {
+ var then = val.then;
+ if (typeof then === 'function') {
+ then.call(
+ val,
+ function(val) {
+ res(i, val);
+ },
+ reject
+ );
+ return;
+ }
+ }
+ args[i] = val;
+ if (--remaining === 0) {
+ resolve(args);
+ }
+ } catch (ex) {
+ reject(ex);
+ }
+ }
+
+ for (var i = 0; i < args.length; i++) {
+ res(i, args[i]);
+ }
+ });
+ };
+
+ Promise$1.allSettled = allSettled;
+
+ Promise$1.resolve = function(value) {
+ if (value && typeof value === 'object' && value.constructor === Promise$1) {
+ return value;
+ }
+
+ return new Promise$1(function(resolve) {
+ resolve(value);
+ });
+ };
+
+ Promise$1.reject = function(value) {
+ return new Promise$1(function(resolve, reject) {
+ reject(value);
+ });
+ };
+
+ Promise$1.race = function(arr) {
+ return new Promise$1(function(resolve, reject) {
+ if (!isArray(arr)) {
+ return reject(new TypeError('Promise.race accepts an array'));
+ }
+
+ for (var i = 0, len = arr.length; i < len; i++) {
+ Promise$1.resolve(arr[i]).then(resolve, reject);
+ }
+ });
+ };
+
+ // Use polyfill for setImmediate for performance gains
+ Promise$1._immediateFn =
+ // @ts-ignore
+ (typeof setImmediate === 'function' &&
+ function(fn) {
+ // @ts-ignore
+ setImmediate(fn);
+ }) ||
+ function(fn) {
+ setTimeoutFunc(fn, 0);
+ };
+
+ Promise$1._unhandledRejectionFn = function _unhandledRejectionFn(err) {
+ if (typeof console !== 'undefined' && console) {
+ console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console
+ }
+ };
+
+ /*
+ object-assign
+ (c) Sindre Sorhus
+ @license MIT
+ */
+
+ 'use strict';
+ /* eslint-disable no-unused-vars */
+ var getOwnPropertySymbols = Object.getOwnPropertySymbols;
+ var hasOwnProperty$2 = Object.prototype.hasOwnProperty;
+ var propIsEnumerable = Object.prototype.propertyIsEnumerable;
+
+ function toObject(val) {
+ if (val === null || val === undefined) {
+ throw new TypeError('Object.assign cannot be called with null or undefined');
+ }
+
+ return Object(val);
+ }
+
+ function shouldUseNative() {
+ try {
+ if (!Object.assign) {
+ return false;
+ }
+
+ // Detect buggy property enumeration order in older V8 versions.
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=4118
+ var test1 = new String('abc'); // eslint-disable-line no-new-wrappers
+ test1[5] = 'de';
+ if (Object.getOwnPropertyNames(test1)[0] === '5') {
+ return false;
+ }
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=3056
+ var test2 = {};
+ for (var i = 0; i < 10; i++) {
+ test2['_' + String.fromCharCode(i)] = i;
+ }
+ var order2 = Object.getOwnPropertyNames(test2).map(function (n) {
+ return test2[n];
+ });
+ if (order2.join('') !== '0123456789') {
+ return false;
+ }
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=3056
+ var test3 = {};
+ 'abcdefghijklmnopqrst'.split('').forEach(function (letter) {
+ test3[letter] = letter;
+ });
+ if (Object.keys(Object.assign({}, test3)).join('') !==
+ 'abcdefghijklmnopqrst') {
+ return false;
+ }
+
+ return true;
+ } catch (err) {
+ // We don't expect any of the above to throw, but better to be safe.
+ return false;
+ }
+ }
+
+ var objectAssign = shouldUseNative() ? Object.assign : function (target, source) {
+ var arguments$1 = arguments;
+
+ var from;
+ var to = toObject(target);
+ var symbols;
+
+ for (var s = 1; s < arguments.length; s++) {
+ from = Object(arguments$1[s]);
+
+ for (var key in from) {
+ if (hasOwnProperty$2.call(from, key)) {
+ to[key] = from[key];
+ }
+ }
+
+ if (getOwnPropertySymbols) {
+ symbols = getOwnPropertySymbols(from);
+ for (var i = 0; i < symbols.length; i++) {
+ if (propIsEnumerable.call(from, symbols[i])) {
+ to[symbols[i]] = from[symbols[i]];
+ }
+ }
+ }
+ }
+
+ return to;
+ };
+
+ /*!
+ * @pixi/polyfill - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/polyfill is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ if (typeof globalThis === 'undefined') {
+ if (typeof self !== 'undefined') {
+ // covers browsers
+ // @ts-expect-error not-writable ts(2540) error only on node
+ self.globalThis = self;
+ }
+ else if (typeof global !== 'undefined') {
+ // covers versions of Node < 12
+ // @ts-expect-error not-writable ts(2540) error only on node
+ global.globalThis = global;
+ }
+ }
+
+ // Support for IE 9 - 11 which does not include Promises
+ if (!globalThis.Promise) {
+ globalThis.Promise = Promise$1;
+ }
+
+ // References:
+ if (!Object.assign) {
+ Object.assign = objectAssign;
+ }
+
+ // References:
+ // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
+ // https://gist.github.com/1579671
+ // http://updates.html5rocks.com/2012/05/requestAnimationFrame-API-now-with-sub-millisecond-precision
+ // https://gist.github.com/timhall/4078614
+ // https://github.com/Financial-Times/polyfill-service/tree/master/polyfills/requestAnimationFrame
+ // Expected to be used with Browserfiy
+ // Browserify automatically detects the use of `global` and passes the
+ // correct reference of `global`, `globalThis`, and finally `window`
+ var ONE_FRAME_TIME = 16;
+ // Date.now
+ if (!(Date.now && Date.prototype.getTime)) {
+ Date.now = function now() {
+ return new Date().getTime();
+ };
+ }
+ // performance.now
+ if (!(globalThis.performance && globalThis.performance.now)) {
+ var startTime_1 = Date.now();
+ if (!globalThis.performance) {
+ globalThis.performance = {};
+ }
+ globalThis.performance.now = function () { return Date.now() - startTime_1; };
+ }
+ // requestAnimationFrame
+ var lastTime = Date.now();
+ var vendors = ['ms', 'moz', 'webkit', 'o'];
+ for (var x = 0; x < vendors.length && !globalThis.requestAnimationFrame; ++x) {
+ var p = vendors[x];
+ globalThis.requestAnimationFrame = globalThis[p + "RequestAnimationFrame"];
+ globalThis.cancelAnimationFrame = globalThis[p + "CancelAnimationFrame"]
+ || globalThis[p + "CancelRequestAnimationFrame"];
+ }
+ if (!globalThis.requestAnimationFrame) {
+ globalThis.requestAnimationFrame = function (callback) {
+ if (typeof callback !== 'function') {
+ throw new TypeError(callback + "is not a function");
+ }
+ var currentTime = Date.now();
+ var delay = ONE_FRAME_TIME + lastTime - currentTime;
+ if (delay < 0) {
+ delay = 0;
+ }
+ lastTime = currentTime;
+ return globalThis.self.setTimeout(function () {
+ lastTime = Date.now();
+ callback(performance.now());
+ }, delay);
+ };
+ }
+ if (!globalThis.cancelAnimationFrame) {
+ globalThis.cancelAnimationFrame = function (id) { return clearTimeout(id); };
+ }
+
+ // References:
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign
+ if (!Math.sign) {
+ Math.sign = function mathSign(x) {
+ x = Number(x);
+ if (x === 0 || isNaN(x)) {
+ return x;
+ }
+ return x > 0 ? 1 : -1;
+ };
+ }
+
+ // References:
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger
+ if (!Number.isInteger) {
+ Number.isInteger = function numberIsInteger(value) {
+ return typeof value === 'number' && isFinite(value) && Math.floor(value) === value;
+ };
+ }
+
+ if (!globalThis.ArrayBuffer) {
+ globalThis.ArrayBuffer = Array;
+ }
+ if (!globalThis.Float32Array) {
+ globalThis.Float32Array = Array;
+ }
+ if (!globalThis.Uint32Array) {
+ globalThis.Uint32Array = Array;
+ }
+ if (!globalThis.Uint16Array) {
+ globalThis.Uint16Array = Array;
+ }
+ if (!globalThis.Uint8Array) {
+ globalThis.Uint8Array = Array;
+ }
+ if (!globalThis.Int32Array) {
+ globalThis.Int32Array = Array;
+ }
+
+ /*!
+ * @pixi/settings - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/settings is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+ /*!
+ * @pixi/constants - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/constants is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+ /**
+ * Different types of environments for WebGL.
+ * @static
+ * @memberof PIXI
+ * @name ENV
+ * @enum {number}
+ * @property {number} WEBGL_LEGACY - Used for older v1 WebGL devices. PixiJS will aim to ensure compatibility
+ * with older / less advanced devices. If you experience unexplained flickering prefer this environment.
+ * @property {number} WEBGL - Version 1 of WebGL
+ * @property {number} WEBGL2 - Version 2 of WebGL
+ */
+ var ENV$5;
+ (function (ENV) {
+ ENV[ENV["WEBGL_LEGACY"] = 0] = "WEBGL_LEGACY";
+ ENV[ENV["WEBGL"] = 1] = "WEBGL";
+ ENV[ENV["WEBGL2"] = 2] = "WEBGL2";
+ })(ENV$5 || (ENV$5 = {}));
+ /**
+ * Constant to identify the Renderer Type.
+ * @static
+ * @memberof PIXI
+ * @name RENDERER_TYPE
+ * @enum {number}
+ * @property {number} UNKNOWN - Unknown render type.
+ * @property {number} WEBGL - WebGL render type.
+ * @property {number} CANVAS - Canvas render type.
+ */
+ var RENDERER_TYPE$5;
+ (function (RENDERER_TYPE) {
+ RENDERER_TYPE[RENDERER_TYPE["UNKNOWN"] = 0] = "UNKNOWN";
+ RENDERER_TYPE[RENDERER_TYPE["WEBGL"] = 1] = "WEBGL";
+ RENDERER_TYPE[RENDERER_TYPE["CANVAS"] = 2] = "CANVAS";
+ })(RENDERER_TYPE$5 || (RENDERER_TYPE$5 = {}));
+ /**
+ * Bitwise OR of masks that indicate the buffers to be cleared.
+ * @static
+ * @memberof PIXI
+ * @name BUFFER_BITS
+ * @enum {number}
+ * @property {number} COLOR - Indicates the buffers currently enabled for color writing.
+ * @property {number} DEPTH - Indicates the depth buffer.
+ * @property {number} STENCIL - Indicates the stencil buffer.
+ */
+ var BUFFER_BITS$5;
+ (function (BUFFER_BITS) {
+ BUFFER_BITS[BUFFER_BITS["COLOR"] = 16384] = "COLOR";
+ BUFFER_BITS[BUFFER_BITS["DEPTH"] = 256] = "DEPTH";
+ BUFFER_BITS[BUFFER_BITS["STENCIL"] = 1024] = "STENCIL";
+ })(BUFFER_BITS$5 || (BUFFER_BITS$5 = {}));
+ /**
+ * Various blend modes supported by PIXI.
+ *
+ * IMPORTANT - The WebGL renderer only supports the NORMAL, ADD, MULTIPLY and SCREEN blend modes.
+ * Anything else will silently act like NORMAL.
+ * @memberof PIXI
+ * @name BLEND_MODES
+ * @enum {number}
+ * @property {number} NORMAL -
+ * @property {number} ADD -
+ * @property {number} MULTIPLY -
+ * @property {number} SCREEN -
+ * @property {number} OVERLAY -
+ * @property {number} DARKEN -
+ * @property {number} LIGHTEN -
+ * @property {number} COLOR_DODGE -
+ * @property {number} COLOR_BURN -
+ * @property {number} HARD_LIGHT -
+ * @property {number} SOFT_LIGHT -
+ * @property {number} DIFFERENCE -
+ * @property {number} EXCLUSION -
+ * @property {number} HUE -
+ * @property {number} SATURATION -
+ * @property {number} COLOR -
+ * @property {number} LUMINOSITY -
+ * @property {number} NORMAL_NPM -
+ * @property {number} ADD_NPM -
+ * @property {number} SCREEN_NPM -
+ * @property {number} NONE -
+ * @property {number} SRC_IN -
+ * @property {number} SRC_OUT -
+ * @property {number} SRC_ATOP -
+ * @property {number} DST_OVER -
+ * @property {number} DST_IN -
+ * @property {number} DST_OUT -
+ * @property {number} DST_ATOP -
+ * @property {number} SUBTRACT -
+ * @property {number} SRC_OVER -
+ * @property {number} ERASE -
+ * @property {number} XOR -
+ */
+ var BLEND_MODES$5;
+ (function (BLEND_MODES) {
+ BLEND_MODES[BLEND_MODES["NORMAL"] = 0] = "NORMAL";
+ BLEND_MODES[BLEND_MODES["ADD"] = 1] = "ADD";
+ BLEND_MODES[BLEND_MODES["MULTIPLY"] = 2] = "MULTIPLY";
+ BLEND_MODES[BLEND_MODES["SCREEN"] = 3] = "SCREEN";
+ BLEND_MODES[BLEND_MODES["OVERLAY"] = 4] = "OVERLAY";
+ BLEND_MODES[BLEND_MODES["DARKEN"] = 5] = "DARKEN";
+ BLEND_MODES[BLEND_MODES["LIGHTEN"] = 6] = "LIGHTEN";
+ BLEND_MODES[BLEND_MODES["COLOR_DODGE"] = 7] = "COLOR_DODGE";
+ BLEND_MODES[BLEND_MODES["COLOR_BURN"] = 8] = "COLOR_BURN";
+ BLEND_MODES[BLEND_MODES["HARD_LIGHT"] = 9] = "HARD_LIGHT";
+ BLEND_MODES[BLEND_MODES["SOFT_LIGHT"] = 10] = "SOFT_LIGHT";
+ BLEND_MODES[BLEND_MODES["DIFFERENCE"] = 11] = "DIFFERENCE";
+ BLEND_MODES[BLEND_MODES["EXCLUSION"] = 12] = "EXCLUSION";
+ BLEND_MODES[BLEND_MODES["HUE"] = 13] = "HUE";
+ BLEND_MODES[BLEND_MODES["SATURATION"] = 14] = "SATURATION";
+ BLEND_MODES[BLEND_MODES["COLOR"] = 15] = "COLOR";
+ BLEND_MODES[BLEND_MODES["LUMINOSITY"] = 16] = "LUMINOSITY";
+ BLEND_MODES[BLEND_MODES["NORMAL_NPM"] = 17] = "NORMAL_NPM";
+ BLEND_MODES[BLEND_MODES["ADD_NPM"] = 18] = "ADD_NPM";
+ BLEND_MODES[BLEND_MODES["SCREEN_NPM"] = 19] = "SCREEN_NPM";
+ BLEND_MODES[BLEND_MODES["NONE"] = 20] = "NONE";
+ BLEND_MODES[BLEND_MODES["SRC_OVER"] = 0] = "SRC_OVER";
+ BLEND_MODES[BLEND_MODES["SRC_IN"] = 21] = "SRC_IN";
+ BLEND_MODES[BLEND_MODES["SRC_OUT"] = 22] = "SRC_OUT";
+ BLEND_MODES[BLEND_MODES["SRC_ATOP"] = 23] = "SRC_ATOP";
+ BLEND_MODES[BLEND_MODES["DST_OVER"] = 24] = "DST_OVER";
+ BLEND_MODES[BLEND_MODES["DST_IN"] = 25] = "DST_IN";
+ BLEND_MODES[BLEND_MODES["DST_OUT"] = 26] = "DST_OUT";
+ BLEND_MODES[BLEND_MODES["DST_ATOP"] = 27] = "DST_ATOP";
+ BLEND_MODES[BLEND_MODES["ERASE"] = 26] = "ERASE";
+ BLEND_MODES[BLEND_MODES["SUBTRACT"] = 28] = "SUBTRACT";
+ BLEND_MODES[BLEND_MODES["XOR"] = 29] = "XOR";
+ })(BLEND_MODES$5 || (BLEND_MODES$5 = {}));
+ /**
+ * Various webgl draw modes. These can be used to specify which GL drawMode to use
+ * under certain situations and renderers.
+ * @memberof PIXI
+ * @static
+ * @name DRAW_MODES
+ * @enum {number}
+ * @property {number} POINTS -
+ * @property {number} LINES -
+ * @property {number} LINE_LOOP -
+ * @property {number} LINE_STRIP -
+ * @property {number} TRIANGLES -
+ * @property {number} TRIANGLE_STRIP -
+ * @property {number} TRIANGLE_FAN -
+ */
+ var DRAW_MODES$5;
+ (function (DRAW_MODES) {
+ DRAW_MODES[DRAW_MODES["POINTS"] = 0] = "POINTS";
+ DRAW_MODES[DRAW_MODES["LINES"] = 1] = "LINES";
+ DRAW_MODES[DRAW_MODES["LINE_LOOP"] = 2] = "LINE_LOOP";
+ DRAW_MODES[DRAW_MODES["LINE_STRIP"] = 3] = "LINE_STRIP";
+ DRAW_MODES[DRAW_MODES["TRIANGLES"] = 4] = "TRIANGLES";
+ DRAW_MODES[DRAW_MODES["TRIANGLE_STRIP"] = 5] = "TRIANGLE_STRIP";
+ DRAW_MODES[DRAW_MODES["TRIANGLE_FAN"] = 6] = "TRIANGLE_FAN";
+ })(DRAW_MODES$5 || (DRAW_MODES$5 = {}));
+ /**
+ * Various GL texture/resources formats.
+ * @memberof PIXI
+ * @static
+ * @name FORMATS
+ * @enum {number}
+ * @property {number} [RGBA=6408] -
+ * @property {number} [RGB=6407] -
+ * @property {number} [RG=33319] -
+ * @property {number} [RED=6403] -
+ * @property {number} [RGBA_INTEGER=36249] -
+ * @property {number} [RGB_INTEGER=36248] -
+ * @property {number} [RG_INTEGER=33320] -
+ * @property {number} [RED_INTEGER=36244] -
+ * @property {number} [ALPHA=6406] -
+ * @property {number} [LUMINANCE=6409] -
+ * @property {number} [LUMINANCE_ALPHA=6410] -
+ * @property {number} [DEPTH_COMPONENT=6402] -
+ * @property {number} [DEPTH_STENCIL=34041] -
+ */
+ var FORMATS$5;
+ (function (FORMATS) {
+ FORMATS[FORMATS["RGBA"] = 6408] = "RGBA";
+ FORMATS[FORMATS["RGB"] = 6407] = "RGB";
+ FORMATS[FORMATS["RG"] = 33319] = "RG";
+ FORMATS[FORMATS["RED"] = 6403] = "RED";
+ FORMATS[FORMATS["RGBA_INTEGER"] = 36249] = "RGBA_INTEGER";
+ FORMATS[FORMATS["RGB_INTEGER"] = 36248] = "RGB_INTEGER";
+ FORMATS[FORMATS["RG_INTEGER"] = 33320] = "RG_INTEGER";
+ FORMATS[FORMATS["RED_INTEGER"] = 36244] = "RED_INTEGER";
+ FORMATS[FORMATS["ALPHA"] = 6406] = "ALPHA";
+ FORMATS[FORMATS["LUMINANCE"] = 6409] = "LUMINANCE";
+ FORMATS[FORMATS["LUMINANCE_ALPHA"] = 6410] = "LUMINANCE_ALPHA";
+ FORMATS[FORMATS["DEPTH_COMPONENT"] = 6402] = "DEPTH_COMPONENT";
+ FORMATS[FORMATS["DEPTH_STENCIL"] = 34041] = "DEPTH_STENCIL";
+ })(FORMATS$5 || (FORMATS$5 = {}));
+ /**
+ * Various GL target types.
+ * @memberof PIXI
+ * @static
+ * @name TARGETS
+ * @enum {number}
+ * @property {number} [TEXTURE_2D=3553] -
+ * @property {number} [TEXTURE_CUBE_MAP=34067] -
+ * @property {number} [TEXTURE_2D_ARRAY=35866] -
+ * @property {number} [TEXTURE_CUBE_MAP_POSITIVE_X=34069] -
+ * @property {number} [TEXTURE_CUBE_MAP_NEGATIVE_X=34070] -
+ * @property {number} [TEXTURE_CUBE_MAP_POSITIVE_Y=34071] -
+ * @property {number} [TEXTURE_CUBE_MAP_NEGATIVE_Y=34072] -
+ * @property {number} [TEXTURE_CUBE_MAP_POSITIVE_Z=34073] -
+ * @property {number} [TEXTURE_CUBE_MAP_NEGATIVE_Z=34074] -
+ */
+ var TARGETS$5;
+ (function (TARGETS) {
+ TARGETS[TARGETS["TEXTURE_2D"] = 3553] = "TEXTURE_2D";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP"] = 34067] = "TEXTURE_CUBE_MAP";
+ TARGETS[TARGETS["TEXTURE_2D_ARRAY"] = 35866] = "TEXTURE_2D_ARRAY";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_POSITIVE_X"] = 34069] = "TEXTURE_CUBE_MAP_POSITIVE_X";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_NEGATIVE_X"] = 34070] = "TEXTURE_CUBE_MAP_NEGATIVE_X";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_POSITIVE_Y"] = 34071] = "TEXTURE_CUBE_MAP_POSITIVE_Y";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_NEGATIVE_Y"] = 34072] = "TEXTURE_CUBE_MAP_NEGATIVE_Y";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_POSITIVE_Z"] = 34073] = "TEXTURE_CUBE_MAP_POSITIVE_Z";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_NEGATIVE_Z"] = 34074] = "TEXTURE_CUBE_MAP_NEGATIVE_Z";
+ })(TARGETS$5 || (TARGETS$5 = {}));
+ /**
+ * Various GL data format types.
+ * @memberof PIXI
+ * @static
+ * @name TYPES
+ * @enum {number}
+ * @property {number} [UNSIGNED_BYTE=5121] -
+ * @property {number} [UNSIGNED_SHORT=5123] -
+ * @property {number} [UNSIGNED_SHORT_5_6_5=33635] -
+ * @property {number} [UNSIGNED_SHORT_4_4_4_4=32819] -
+ * @property {number} [UNSIGNED_SHORT_5_5_5_1=32820] -
+ * @property {number} [UNSIGNED_INT=5125] -
+ * @property {number} [UNSIGNED_INT_10F_11F_11F_REV=35899] -
+ * @property {number} [UNSIGNED_INT_2_10_10_10_REV=33640] -
+ * @property {number} [UNSIGNED_INT_24_8=34042] -
+ * @property {number} [UNSIGNED_INT_5_9_9_9_REV=35902] -
+ * @property {number} [BYTE=5120] -
+ * @property {number} [SHORT=5122] -
+ * @property {number} [INT=5124] -
+ * @property {number} [FLOAT=5126] -
+ * @property {number} [FLOAT_32_UNSIGNED_INT_24_8_REV=36269] -
+ * @property {number} [HALF_FLOAT=36193] -
+ */
+ var TYPES$5;
+ (function (TYPES) {
+ TYPES[TYPES["UNSIGNED_BYTE"] = 5121] = "UNSIGNED_BYTE";
+ TYPES[TYPES["UNSIGNED_SHORT"] = 5123] = "UNSIGNED_SHORT";
+ TYPES[TYPES["UNSIGNED_SHORT_5_6_5"] = 33635] = "UNSIGNED_SHORT_5_6_5";
+ TYPES[TYPES["UNSIGNED_SHORT_4_4_4_4"] = 32819] = "UNSIGNED_SHORT_4_4_4_4";
+ TYPES[TYPES["UNSIGNED_SHORT_5_5_5_1"] = 32820] = "UNSIGNED_SHORT_5_5_5_1";
+ TYPES[TYPES["UNSIGNED_INT"] = 5125] = "UNSIGNED_INT";
+ TYPES[TYPES["UNSIGNED_INT_10F_11F_11F_REV"] = 35899] = "UNSIGNED_INT_10F_11F_11F_REV";
+ TYPES[TYPES["UNSIGNED_INT_2_10_10_10_REV"] = 33640] = "UNSIGNED_INT_2_10_10_10_REV";
+ TYPES[TYPES["UNSIGNED_INT_24_8"] = 34042] = "UNSIGNED_INT_24_8";
+ TYPES[TYPES["UNSIGNED_INT_5_9_9_9_REV"] = 35902] = "UNSIGNED_INT_5_9_9_9_REV";
+ TYPES[TYPES["BYTE"] = 5120] = "BYTE";
+ TYPES[TYPES["SHORT"] = 5122] = "SHORT";
+ TYPES[TYPES["INT"] = 5124] = "INT";
+ TYPES[TYPES["FLOAT"] = 5126] = "FLOAT";
+ TYPES[TYPES["FLOAT_32_UNSIGNED_INT_24_8_REV"] = 36269] = "FLOAT_32_UNSIGNED_INT_24_8_REV";
+ TYPES[TYPES["HALF_FLOAT"] = 36193] = "HALF_FLOAT";
+ })(TYPES$5 || (TYPES$5 = {}));
+ /**
+ * Various sampler types. Correspond to `sampler`, `isampler`, `usampler` GLSL types respectively.
+ * WebGL1 works only with FLOAT.
+ * @memberof PIXI
+ * @static
+ * @name SAMPLER_TYPES
+ * @enum {number}
+ * @property {number} [FLOAT=0] -
+ * @property {number} [INT=1] -
+ * @property {number} [UINT=2] -
+ */
+ var SAMPLER_TYPES$5;
+ (function (SAMPLER_TYPES) {
+ SAMPLER_TYPES[SAMPLER_TYPES["FLOAT"] = 0] = "FLOAT";
+ SAMPLER_TYPES[SAMPLER_TYPES["INT"] = 1] = "INT";
+ SAMPLER_TYPES[SAMPLER_TYPES["UINT"] = 2] = "UINT";
+ })(SAMPLER_TYPES$5 || (SAMPLER_TYPES$5 = {}));
+ /**
+ * The scale modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.SCALE_MODE} scale mode affects the default scaling mode of future operations.
+ * It can be re-assigned to either LINEAR or NEAREST, depending upon suitability.
+ * @memberof PIXI
+ * @static
+ * @name SCALE_MODES
+ * @enum {number}
+ * @property {number} LINEAR Smooth scaling
+ * @property {number} NEAREST Pixelating scaling
+ */
+ var SCALE_MODES$5;
+ (function (SCALE_MODES) {
+ SCALE_MODES[SCALE_MODES["NEAREST"] = 0] = "NEAREST";
+ SCALE_MODES[SCALE_MODES["LINEAR"] = 1] = "LINEAR";
+ })(SCALE_MODES$5 || (SCALE_MODES$5 = {}));
+ /**
+ * The wrap modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.WRAP_MODE} wrap mode affects the default wrapping mode of future operations.
+ * It can be re-assigned to either CLAMP or REPEAT, depending upon suitability.
+ * If the texture is non power of two then clamp will be used regardless as WebGL can
+ * only use REPEAT if the texture is po2.
+ *
+ * This property only affects WebGL.
+ * @name WRAP_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} CLAMP - The textures uvs are clamped
+ * @property {number} REPEAT - The texture uvs tile and repeat
+ * @property {number} MIRRORED_REPEAT - The texture uvs tile and repeat with mirroring
+ */
+ var WRAP_MODES$5;
+ (function (WRAP_MODES) {
+ WRAP_MODES[WRAP_MODES["CLAMP"] = 33071] = "CLAMP";
+ WRAP_MODES[WRAP_MODES["REPEAT"] = 10497] = "REPEAT";
+ WRAP_MODES[WRAP_MODES["MIRRORED_REPEAT"] = 33648] = "MIRRORED_REPEAT";
+ })(WRAP_MODES$5 || (WRAP_MODES$5 = {}));
+ /**
+ * Mipmap filtering modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.MIPMAP_TEXTURES} affects default texture filtering.
+ * Mipmaps are generated for a baseTexture if its `mipmap` field is `ON`,
+ * or its `POW2` and texture dimensions are powers of 2.
+ * Due to platform restriction, `ON` option will work like `POW2` for webgl-1.
+ *
+ * This property only affects WebGL.
+ * @name MIPMAP_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} OFF - No mipmaps
+ * @property {number} POW2 - Generate mipmaps if texture dimensions are pow2
+ * @property {number} ON - Always generate mipmaps
+ * @property {number} ON_MANUAL - Use mipmaps, but do not auto-generate them; this is used with a resource
+ * that supports buffering each level-of-detail.
+ */
+ var MIPMAP_MODES$5;
+ (function (MIPMAP_MODES) {
+ MIPMAP_MODES[MIPMAP_MODES["OFF"] = 0] = "OFF";
+ MIPMAP_MODES[MIPMAP_MODES["POW2"] = 1] = "POW2";
+ MIPMAP_MODES[MIPMAP_MODES["ON"] = 2] = "ON";
+ MIPMAP_MODES[MIPMAP_MODES["ON_MANUAL"] = 3] = "ON_MANUAL";
+ })(MIPMAP_MODES$5 || (MIPMAP_MODES$5 = {}));
+ /**
+ * How to treat textures with premultiplied alpha
+ * @name ALPHA_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} NO_PREMULTIPLIED_ALPHA - Source is not premultiplied, leave it like that.
+ * Option for compressed and data textures that are created from typed arrays.
+ * @property {number} PREMULTIPLY_ON_UPLOAD - Source is not premultiplied, premultiply on upload.
+ * Default option, used for all loaded images.
+ * @property {number} PREMULTIPLIED_ALPHA - Source is already premultiplied
+ * Example: spine atlases with `_pma` suffix.
+ * @property {number} NPM - Alias for NO_PREMULTIPLIED_ALPHA.
+ * @property {number} UNPACK - Default option, alias for PREMULTIPLY_ON_UPLOAD.
+ * @property {number} PMA - Alias for PREMULTIPLIED_ALPHA.
+ */
+ var ALPHA_MODES$5;
+ (function (ALPHA_MODES) {
+ ALPHA_MODES[ALPHA_MODES["NPM"] = 0] = "NPM";
+ ALPHA_MODES[ALPHA_MODES["UNPACK"] = 1] = "UNPACK";
+ ALPHA_MODES[ALPHA_MODES["PMA"] = 2] = "PMA";
+ ALPHA_MODES[ALPHA_MODES["NO_PREMULTIPLIED_ALPHA"] = 0] = "NO_PREMULTIPLIED_ALPHA";
+ ALPHA_MODES[ALPHA_MODES["PREMULTIPLY_ON_UPLOAD"] = 1] = "PREMULTIPLY_ON_UPLOAD";
+ ALPHA_MODES[ALPHA_MODES["PREMULTIPLY_ALPHA"] = 2] = "PREMULTIPLY_ALPHA";
+ ALPHA_MODES[ALPHA_MODES["PREMULTIPLIED_ALPHA"] = 2] = "PREMULTIPLIED_ALPHA";
+ })(ALPHA_MODES$5 || (ALPHA_MODES$5 = {}));
+ /**
+ * Configure whether filter textures are cleared after binding.
+ *
+ * Filter textures need not be cleared if the filter does not use pixel blending. {@link CLEAR_MODES.BLIT} will detect
+ * this and skip clearing as an optimization.
+ * @name CLEAR_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} BLEND - Do not clear the filter texture. The filter's output will blend on top of the output texture.
+ * @property {number} CLEAR - Always clear the filter texture.
+ * @property {number} BLIT - Clear only if {@link FilterSystem.forceClear} is set or if the filter uses pixel blending.
+ * @property {number} NO - Alias for BLEND, same as `false` in earlier versions
+ * @property {number} YES - Alias for CLEAR, same as `true` in earlier versions
+ * @property {number} AUTO - Alias for BLIT
+ */
+ var CLEAR_MODES$5;
+ (function (CLEAR_MODES) {
+ CLEAR_MODES[CLEAR_MODES["NO"] = 0] = "NO";
+ CLEAR_MODES[CLEAR_MODES["YES"] = 1] = "YES";
+ CLEAR_MODES[CLEAR_MODES["AUTO"] = 2] = "AUTO";
+ CLEAR_MODES[CLEAR_MODES["BLEND"] = 0] = "BLEND";
+ CLEAR_MODES[CLEAR_MODES["CLEAR"] = 1] = "CLEAR";
+ CLEAR_MODES[CLEAR_MODES["BLIT"] = 2] = "BLIT";
+ })(CLEAR_MODES$5 || (CLEAR_MODES$5 = {}));
+ /**
+ * The gc modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.GC_MODE} Garbage Collection mode for PixiJS textures is AUTO
+ * If set to GC_MODE, the renderer will occasionally check textures usage. If they are not
+ * used for a specified period of time they will be removed from the GPU. They will of course
+ * be uploaded again when they are required. This is a silent behind the scenes process that
+ * should ensure that the GPU does not get filled up.
+ *
+ * Handy for mobile devices!
+ * This property only affects WebGL.
+ * @name GC_MODES
+ * @enum {number}
+ * @static
+ * @memberof PIXI
+ * @property {number} AUTO - Garbage collection will happen periodically automatically
+ * @property {number} MANUAL - Garbage collection will need to be called manually
+ */
+ var GC_MODES$5;
+ (function (GC_MODES) {
+ GC_MODES[GC_MODES["AUTO"] = 0] = "AUTO";
+ GC_MODES[GC_MODES["MANUAL"] = 1] = "MANUAL";
+ })(GC_MODES$5 || (GC_MODES$5 = {}));
+ /**
+ * Constants that specify float precision in shaders.
+ * @name PRECISION
+ * @memberof PIXI
+ * @constant
+ * @static
+ * @enum {string}
+ * @property {string} [LOW='lowp'] -
+ * @property {string} [MEDIUM='mediump'] -
+ * @property {string} [HIGH='highp'] -
+ */
+ var PRECISION$5;
+ (function (PRECISION) {
+ PRECISION["LOW"] = "lowp";
+ PRECISION["MEDIUM"] = "mediump";
+ PRECISION["HIGH"] = "highp";
+ })(PRECISION$5 || (PRECISION$5 = {}));
+ /**
+ * Constants for mask implementations.
+ * We use `type` suffix because it leads to very different behaviours
+ * @name MASK_TYPES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} NONE - Mask is ignored
+ * @property {number} SCISSOR - Scissor mask, rectangle on screen, cheap
+ * @property {number} STENCIL - Stencil mask, 1-bit, medium, works only if renderer supports stencil
+ * @property {number} SPRITE - Mask that uses SpriteMaskFilter, uses temporary RenderTexture
+ * @property {number} COLOR - Color mask (RGBA)
+ */
+ var MASK_TYPES$5;
+ (function (MASK_TYPES) {
+ MASK_TYPES[MASK_TYPES["NONE"] = 0] = "NONE";
+ MASK_TYPES[MASK_TYPES["SCISSOR"] = 1] = "SCISSOR";
+ MASK_TYPES[MASK_TYPES["STENCIL"] = 2] = "STENCIL";
+ MASK_TYPES[MASK_TYPES["SPRITE"] = 3] = "SPRITE";
+ MASK_TYPES[MASK_TYPES["COLOR"] = 4] = "COLOR";
+ })(MASK_TYPES$5 || (MASK_TYPES$5 = {}));
+ /**
+ * Bitwise OR of masks that indicate the color channels that are rendered to.
+ * @static
+ * @memberof PIXI
+ * @name COLOR_MASK_BITS
+ * @enum {number}
+ * @property {number} RED - Red channel.
+ * @property {number} GREEN - Green channel
+ * @property {number} BLUE - Blue channel.
+ * @property {number} ALPHA - Alpha channel.
+ */
+ var COLOR_MASK_BITS$5;
+ (function (COLOR_MASK_BITS) {
+ COLOR_MASK_BITS[COLOR_MASK_BITS["RED"] = 1] = "RED";
+ COLOR_MASK_BITS[COLOR_MASK_BITS["GREEN"] = 2] = "GREEN";
+ COLOR_MASK_BITS[COLOR_MASK_BITS["BLUE"] = 4] = "BLUE";
+ COLOR_MASK_BITS[COLOR_MASK_BITS["ALPHA"] = 8] = "ALPHA";
+ })(COLOR_MASK_BITS$5 || (COLOR_MASK_BITS$5 = {}));
+ /**
+ * Constants for multi-sampling antialiasing.
+ * @see PIXI.Framebuffer#multisample
+ * @name MSAA_QUALITY
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} NONE - No multisampling for this renderTexture
+ * @property {number} LOW - Try 2 samples
+ * @property {number} MEDIUM - Try 4 samples
+ * @property {number} HIGH - Try 8 samples
+ */
+ var MSAA_QUALITY$5;
+ (function (MSAA_QUALITY) {
+ MSAA_QUALITY[MSAA_QUALITY["NONE"] = 0] = "NONE";
+ MSAA_QUALITY[MSAA_QUALITY["LOW"] = 2] = "LOW";
+ MSAA_QUALITY[MSAA_QUALITY["MEDIUM"] = 4] = "MEDIUM";
+ MSAA_QUALITY[MSAA_QUALITY["HIGH"] = 8] = "HIGH";
+ })(MSAA_QUALITY$5 || (MSAA_QUALITY$5 = {}));
+ /**
+ * Constants for various buffer types in Pixi
+ * @see PIXI.BUFFER_TYPE
+ * @name BUFFER_TYPE
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} ELEMENT_ARRAY_BUFFER - buffer type for using as an index buffer
+ * @property {number} ARRAY_BUFFER - buffer type for using attribute data
+ * @property {number} UNIFORM_BUFFER - the buffer type is for uniform buffer objects
+ */
+ var BUFFER_TYPE$5;
+ (function (BUFFER_TYPE) {
+ BUFFER_TYPE[BUFFER_TYPE["ELEMENT_ARRAY_BUFFER"] = 34963] = "ELEMENT_ARRAY_BUFFER";
+ BUFFER_TYPE[BUFFER_TYPE["ARRAY_BUFFER"] = 34962] = "ARRAY_BUFFER";
+ // NOT YET SUPPORTED
+ BUFFER_TYPE[BUFFER_TYPE["UNIFORM_BUFFER"] = 35345] = "UNIFORM_BUFFER";
+ })(BUFFER_TYPE$5 || (BUFFER_TYPE$5 = {}));
+
+ var BrowserAdapter$1 = {
+ /**
+ * Creates a canvas element of the given size.
+ * This canvas is created using the browser's native canvas element.
+ * @param width - width of the canvas
+ * @param height - height of the canvas
+ */
+ createCanvas: function (width, height) {
+ var canvas = document.createElement('canvas');
+ canvas.width = width;
+ canvas.height = height;
+ return canvas;
+ },
+ getWebGLRenderingContext: function () { return WebGLRenderingContext; },
+ getNavigator: function () { return navigator; },
+ getBaseUrl: function () { var _a; return ((_a = document.baseURI) !== null && _a !== void 0 ? _a : window.location.href); },
+ fetch: function (url, options) { return fetch(url, options); },
+ };
+
+ var appleIphone$1 = /iPhone/i;
+ var appleIpod$1 = /iPod/i;
+ var appleTablet$1 = /iPad/i;
+ var appleUniversal$1 = /\biOS-universal(?:.+)Mac\b/i;
+ var androidPhone$1 = /\bAndroid(?:.+)Mobile\b/i;
+ var androidTablet$1 = /Android/i;
+ var amazonPhone$1 = /(?:SD4930UR|\bSilk(?:.+)Mobile\b)/i;
+ var amazonTablet$1 = /Silk/i;
+ var windowsPhone$1 = /Windows Phone/i;
+ var windowsTablet$1 = /\bWindows(?:.+)ARM\b/i;
+ var otherBlackBerry$1 = /BlackBerry/i;
+ var otherBlackBerry10$1 = /BB10/i;
+ var otherOpera$1 = /Opera Mini/i;
+ var otherChrome$1 = /\b(CriOS|Chrome)(?:.+)Mobile/i;
+ var otherFirefox$1 = /Mobile(?:.+)Firefox\b/i;
+ var isAppleTabletOnIos13$1 = function (navigator) {
+ return (typeof navigator !== 'undefined' &&
+ navigator.platform === 'MacIntel' &&
+ typeof navigator.maxTouchPoints === 'number' &&
+ navigator.maxTouchPoints > 1 &&
+ typeof MSStream === 'undefined');
+ };
+ function createMatch$1(userAgent) {
+ return function (regex) { return regex.test(userAgent); };
+ }
+ function isMobile$1$1(param) {
+ var nav = {
+ userAgent: '',
+ platform: '',
+ maxTouchPoints: 0
+ };
+ if (!param && typeof navigator !== 'undefined') {
+ nav = {
+ userAgent: navigator.userAgent,
+ platform: navigator.platform,
+ maxTouchPoints: navigator.maxTouchPoints || 0
+ };
+ }
+ else if (typeof param === 'string') {
+ nav.userAgent = param;
+ }
+ else if (param && param.userAgent) {
+ nav = {
+ userAgent: param.userAgent,
+ platform: param.platform,
+ maxTouchPoints: param.maxTouchPoints || 0
+ };
+ }
+ var userAgent = nav.userAgent;
+ var tmp = userAgent.split('[FBAN');
+ if (typeof tmp[1] !== 'undefined') {
+ userAgent = tmp[0];
+ }
+ tmp = userAgent.split('Twitter');
+ if (typeof tmp[1] !== 'undefined') {
+ userAgent = tmp[0];
+ }
+ var match = createMatch$1(userAgent);
+ var result = {
+ apple: {
+ phone: match(appleIphone$1) && !match(windowsPhone$1),
+ ipod: match(appleIpod$1),
+ tablet: !match(appleIphone$1) &&
+ (match(appleTablet$1) || isAppleTabletOnIos13$1(nav)) &&
+ !match(windowsPhone$1),
+ universal: match(appleUniversal$1),
+ device: (match(appleIphone$1) ||
+ match(appleIpod$1) ||
+ match(appleTablet$1) ||
+ match(appleUniversal$1) ||
+ isAppleTabletOnIos13$1(nav)) &&
+ !match(windowsPhone$1)
+ },
+ amazon: {
+ phone: match(amazonPhone$1),
+ tablet: !match(amazonPhone$1) && match(amazonTablet$1),
+ device: match(amazonPhone$1) || match(amazonTablet$1)
+ },
+ android: {
+ phone: (!match(windowsPhone$1) && match(amazonPhone$1)) ||
+ (!match(windowsPhone$1) && match(androidPhone$1)),
+ tablet: !match(windowsPhone$1) &&
+ !match(amazonPhone$1) &&
+ !match(androidPhone$1) &&
+ (match(amazonTablet$1) || match(androidTablet$1)),
+ device: (!match(windowsPhone$1) &&
+ (match(amazonPhone$1) ||
+ match(amazonTablet$1) ||
+ match(androidPhone$1) ||
+ match(androidTablet$1))) ||
+ match(/\bokhttp\b/i)
+ },
+ windows: {
+ phone: match(windowsPhone$1),
+ tablet: match(windowsTablet$1),
+ device: match(windowsPhone$1) || match(windowsTablet$1)
+ },
+ other: {
+ blackberry: match(otherBlackBerry$1),
+ blackberry10: match(otherBlackBerry10$1),
+ opera: match(otherOpera$1),
+ firefox: match(otherFirefox$1),
+ chrome: match(otherChrome$1),
+ device: match(otherBlackBerry$1) ||
+ match(otherBlackBerry10$1) ||
+ match(otherOpera$1) ||
+ match(otherFirefox$1) ||
+ match(otherChrome$1)
+ },
+ any: false,
+ phone: false,
+ tablet: false
+ };
+ result.any =
+ result.apple.device ||
+ result.android.device ||
+ result.windows.device ||
+ result.other.device;
+ result.phone =
+ result.apple.phone || result.android.phone || result.windows.phone;
+ result.tablet =
+ result.apple.tablet || result.android.tablet || result.windows.tablet;
+ return result;
+ }
+
+ var isMobile$2 = isMobile$1$1(globalThis.navigator);
+
+ /**
+ * Uploading the same buffer multiple times in a single frame can cause performance issues.
+ * Apparent on iOS so only check for that at the moment
+ * This check may become more complex if this issue pops up elsewhere.
+ * @private
+ * @returns {boolean} `true` if the same buffer may be uploaded more than once.
+ */
+ function canUploadSameBuffer$1() {
+ return !isMobile$2.apple.device;
+ }
+
+ /**
+ * The maximum recommended texture units to use.
+ * In theory the bigger the better, and for desktop we'll use as many as we can.
+ * But some mobile devices slow down if there is to many branches in the shader.
+ * So in practice there seems to be a sweet spot size that varies depending on the device.
+ *
+ * In v4, all mobile devices were limited to 4 texture units because for this.
+ * In v5, we allow all texture units to be used on modern Apple or Android devices.
+ * @private
+ * @param {number} max
+ * @returns {number} The maximum recommended texture units to use.
+ */
+ function maxRecommendedTextures$1(max) {
+ var allowMax = true;
+ if (isMobile$2.tablet || isMobile$2.phone) {
+ if (isMobile$2.apple.device) {
+ var match = (navigator.userAgent).match(/OS (\d+)_(\d+)?/);
+ if (match) {
+ var majorVersion = parseInt(match[1], 10);
+ // Limit texture units on devices below iOS 11, which will be older hardware
+ if (majorVersion < 11) {
+ allowMax = false;
+ }
+ }
+ }
+ if (isMobile$2.android.device) {
+ var match = (navigator.userAgent).match(/Android\s([0-9.]*)/);
+ if (match) {
+ var majorVersion = parseInt(match[1], 10);
+ // Limit texture units on devices below Android 7 (Nougat), which will be older hardware
+ if (majorVersion < 7) {
+ allowMax = false;
+ }
+ }
+ }
+ }
+ return allowMax ? max : 4;
+ }
+
+ /**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float precision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio;
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
+ * @namespace PIXI.settings
+ */
+ var settings$1 = {
+ /**
+ * This adapter is used to call methods that are platform dependent.
+ * For example `document.createElement` only runs on the web but fails in node environments.
+ * This allows us to support more platforms by abstracting away specific implementations per platform.
+ *
+ * By default the adapter is set to work in the browser. However you can create your own
+ * by implementing the `IAdapter` interface. See `IAdapter` for more information.
+ * @name ADAPTER
+ * @memberof PIXI.settings
+ * @type {PIXI.IAdapter}
+ * @default PIXI.BrowserAdapter
+ */
+ ADAPTER: BrowserAdapter$1,
+ /**
+ * If set to true WebGL will attempt make textures mimpaped by default.
+ * Mipmapping will only succeed if the base texture uploaded has power of two dimensions.
+ * @static
+ * @name MIPMAP_TEXTURES
+ * @memberof PIXI.settings
+ * @type {PIXI.MIPMAP_MODES}
+ * @default PIXI.MIPMAP_MODES.POW2
+ */
+ MIPMAP_TEXTURES: MIPMAP_MODES$5.POW2,
+ /**
+ * Default anisotropic filtering level of textures.
+ * Usually from 0 to 16
+ * @static
+ * @name ANISOTROPIC_LEVEL
+ * @memberof PIXI.settings
+ * @type {number}
+ * @default 0
+ */
+ ANISOTROPIC_LEVEL: 0,
+ /**
+ * Default resolution / device pixel ratio of the renderer.
+ * @static
+ * @name RESOLUTION
+ * @memberof PIXI.settings
+ * @type {number}
+ * @default 1
+ */
+ RESOLUTION: 1,
+ /**
+ * Default filter resolution.
+ * @static
+ * @name FILTER_RESOLUTION
+ * @memberof PIXI.settings
+ * @type {number}
+ * @default 1
+ */
+ FILTER_RESOLUTION: 1,
+ /**
+ * Default filter samples.
+ * @static
+ * @name FILTER_MULTISAMPLE
+ * @memberof PIXI.settings
+ * @type {PIXI.MSAA_QUALITY}
+ * @default PIXI.MSAA_QUALITY.NONE
+ */
+ FILTER_MULTISAMPLE: MSAA_QUALITY$5.NONE,
+ /**
+ * The maximum textures that this device supports.
+ * @static
+ * @name SPRITE_MAX_TEXTURES
+ * @memberof PIXI.settings
+ * @type {number}
+ * @default 32
+ */
+ SPRITE_MAX_TEXTURES: maxRecommendedTextures$1(32),
+ // TODO: maybe change to SPRITE.BATCH_SIZE: 2000
+ // TODO: maybe add PARTICLE.BATCH_SIZE: 15000
+ /**
+ * The default sprite batch size.
+ *
+ * The default aims to balance desktop and mobile devices.
+ * @static
+ * @name SPRITE_BATCH_SIZE
+ * @memberof PIXI.settings
+ * @type {number}
+ * @default 4096
+ */
+ SPRITE_BATCH_SIZE: 4096,
+ /**
+ * The default render options if none are supplied to {@link PIXI.Renderer}
+ * or {@link PIXI.CanvasRenderer}.
+ * @static
+ * @name RENDER_OPTIONS
+ * @memberof PIXI.settings
+ * @type {object}
+ * @property {HTMLCanvasElement} [view=null] -
+ * @property {boolean} [antialias=false] -
+ * @property {boolean} [autoDensity=false] -
+ * @property {boolean} [useContextAlpha=true] -
+ * @property {number} [backgroundColor=0x000000] -
+ * @property {number} [backgroundAlpha=1] -
+ * @property {boolean} [clearBeforeRender=true] -
+ * @property {boolean} [preserveDrawingBuffer=false] -
+ * @property {number} [width=800] -
+ * @property {number} [height=600] -
+ * @property {boolean} [legacy=false] -
+ */
+ RENDER_OPTIONS: {
+ view: null,
+ antialias: false,
+ autoDensity: false,
+ backgroundColor: 0x000000,
+ backgroundAlpha: 1,
+ useContextAlpha: true,
+ clearBeforeRender: true,
+ preserveDrawingBuffer: false,
+ width: 800,
+ height: 600,
+ legacy: false,
+ },
+ /**
+ * Default Garbage Collection mode.
+ * @static
+ * @name GC_MODE
+ * @memberof PIXI.settings
+ * @type {PIXI.GC_MODES}
+ * @default PIXI.GC_MODES.AUTO
+ */
+ GC_MODE: GC_MODES$5.AUTO,
+ /**
+ * Default Garbage Collection max idle.
+ * @static
+ * @name GC_MAX_IDLE
+ * @memberof PIXI.settings
+ * @type {number}
+ * @default 3600
+ */
+ GC_MAX_IDLE: 60 * 60,
+ /**
+ * Default Garbage Collection maximum check count.
+ * @static
+ * @name GC_MAX_CHECK_COUNT
+ * @memberof PIXI.settings
+ * @type {number}
+ * @default 600
+ */
+ GC_MAX_CHECK_COUNT: 60 * 10,
+ /**
+ * Default wrap modes that are supported by pixi.
+ * @static
+ * @name WRAP_MODE
+ * @memberof PIXI.settings
+ * @type {PIXI.WRAP_MODES}
+ * @default PIXI.WRAP_MODES.CLAMP
+ */
+ WRAP_MODE: WRAP_MODES$5.CLAMP,
+ /**
+ * Default scale mode for textures.
+ * @static
+ * @name SCALE_MODE
+ * @memberof PIXI.settings
+ * @type {PIXI.SCALE_MODES}
+ * @default PIXI.SCALE_MODES.LINEAR
+ */
+ SCALE_MODE: SCALE_MODES$5.LINEAR,
+ /**
+ * Default specify float precision in vertex shader.
+ * @static
+ * @name PRECISION_VERTEX
+ * @memberof PIXI.settings
+ * @type {PIXI.PRECISION}
+ * @default PIXI.PRECISION.HIGH
+ */
+ PRECISION_VERTEX: PRECISION$5.HIGH,
+ /**
+ * Default specify float precision in fragment shader.
+ * iOS is best set at highp due to https://github.com/pixijs/pixi.js/issues/3742
+ * @static
+ * @name PRECISION_FRAGMENT
+ * @memberof PIXI.settings
+ * @type {PIXI.PRECISION}
+ * @default PIXI.PRECISION.MEDIUM
+ */
+ PRECISION_FRAGMENT: isMobile$2.apple.device ? PRECISION$5.HIGH : PRECISION$5.MEDIUM,
+ /**
+ * Can we upload the same buffer in a single frame?
+ * @static
+ * @name CAN_UPLOAD_SAME_BUFFER
+ * @memberof PIXI.settings
+ * @type {boolean}
+ */
+ CAN_UPLOAD_SAME_BUFFER: canUploadSameBuffer$1(),
+ /**
+ * Enables bitmap creation before image load. This feature is experimental.
+ * @static
+ * @name CREATE_IMAGE_BITMAP
+ * @memberof PIXI.settings
+ * @type {boolean}
+ * @default false
+ */
+ CREATE_IMAGE_BITMAP: false,
+ /**
+ * If true PixiJS will Math.floor() x/y values when rendering, stopping pixel interpolation.
+ * Advantages can include sharper image quality (like text) and faster rendering on canvas.
+ * The main disadvantage is movement of objects may appear less smooth.
+ * @static
+ * @constant
+ * @memberof PIXI.settings
+ * @type {boolean}
+ * @default false
+ */
+ ROUND_PIXELS: false,
+ };
+
+ var commonjsGlobal$1 = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
+
+ function getDefaultExportFromCjs (x) {
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
+ }
+
+ function createCommonjsModule$1(fn, basedir, module) {
+ return module = {
+ path: basedir,
+ exports: {},
+ require: function (path, base) {
+ return commonjsRequire$1(path, (base === undefined || base === null) ? module.path : base);
+ }
+ }, fn(module, module.exports), module.exports;
+ }
+
+ function getDefaultExportFromNamespaceIfPresent (n) {
+ return n && Object.prototype.hasOwnProperty.call(n, 'default') ? n['default'] : n;
+ }
+
+ function getDefaultExportFromNamespaceIfNotNamed (n) {
+ return n && Object.prototype.hasOwnProperty.call(n, 'default') && Object.keys(n).length === 1 ? n['default'] : n;
+ }
+
+ function getAugmentedNamespace(n) {
+ if (n.__esModule) return n;
+ var a = Object.defineProperty({}, '__esModule', {value: true});
+ Object.keys(n).forEach(function (k) {
+ var d = Object.getOwnPropertyDescriptor(n, k);
+ Object.defineProperty(a, k, d.get ? d : {
+ enumerable: true,
+ get: function () {
+ return n[k];
+ }
+ });
+ });
+ return a;
+ }
+
+ function commonjsRequire$1 () {
+ throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs');
+ }
+
+ var eventemitter3 = createCommonjsModule$1(function (module) {
+ 'use strict';
+
+ var has = Object.prototype.hasOwnProperty
+ , prefix = '~';
+
+ /**
+ * Constructor to create a storage for our `EE` objects.
+ * An `Events` instance is a plain object whose properties are event names.
+ *
+ * @constructor
+ * @private
+ */
+ function Events() {}
+
+ //
+ // We try to not inherit from `Object.prototype`. In some engines creating an
+ // instance in this way is faster than calling `Object.create(null)` directly.
+ // If `Object.create(null)` is not supported we prefix the event names with a
+ // character to make sure that the built-in object properties are not
+ // overridden or used as an attack vector.
+ //
+ if (Object.create) {
+ Events.prototype = Object.create(null);
+
+ //
+ // This hack is needed because the `__proto__` property is still inherited in
+ // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.
+ //
+ if (!new Events().__proto__) { prefix = false; }
+ }
+
+ /**
+ * Representation of a single event listener.
+ *
+ * @param {Function} fn The listener function.
+ * @param {*} context The context to invoke the listener with.
+ * @param {Boolean} [once=false] Specify if the listener is a one-time listener.
+ * @constructor
+ * @private
+ */
+ function EE(fn, context, once) {
+ this.fn = fn;
+ this.context = context;
+ this.once = once || false;
+ }
+
+ /**
+ * Add a listener for a given event.
+ *
+ * @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
+ * @param {(String|Symbol)} event The event name.
+ * @param {Function} fn The listener function.
+ * @param {*} context The context to invoke the listener with.
+ * @param {Boolean} once Specify if the listener is a one-time listener.
+ * @returns {EventEmitter}
+ * @private
+ */
+ function addListener(emitter, event, fn, context, once) {
+ if (typeof fn !== 'function') {
+ throw new TypeError('The listener must be a function');
+ }
+
+ var listener = new EE(fn, context || emitter, once)
+ , evt = prefix ? prefix + event : event;
+
+ if (!emitter._events[evt]) { emitter._events[evt] = listener, emitter._eventsCount++; }
+ else if (!emitter._events[evt].fn) { emitter._events[evt].push(listener); }
+ else { emitter._events[evt] = [emitter._events[evt], listener]; }
+
+ return emitter;
+ }
+
+ /**
+ * Clear event by name.
+ *
+ * @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
+ * @param {(String|Symbol)} evt The Event name.
+ * @private
+ */
+ function clearEvent(emitter, evt) {
+ if (--emitter._eventsCount === 0) { emitter._events = new Events(); }
+ else { delete emitter._events[evt]; }
+ }
+
+ /**
+ * Minimal `EventEmitter` interface that is molded against the Node.js
+ * `EventEmitter` interface.
+ *
+ * @constructor
+ * @public
+ */
+ function EventEmitter() {
+ this._events = new Events();
+ this._eventsCount = 0;
+ }
+
+ /**
+ * Return an array listing the events for which the emitter has registered
+ * listeners.
+ *
+ * @returns {Array}
+ * @public
+ */
+ EventEmitter.prototype.eventNames = function eventNames() {
+ var names = []
+ , events
+ , name;
+
+ if (this._eventsCount === 0) { return names; }
+
+ for (name in (events = this._events)) {
+ if (has.call(events, name)) { names.push(prefix ? name.slice(1) : name); }
+ }
+
+ if (Object.getOwnPropertySymbols) {
+ return names.concat(Object.getOwnPropertySymbols(events));
+ }
+
+ return names;
+ };
+
+ /**
+ * Return the listeners registered for a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @returns {Array} The registered listeners.
+ * @public
+ */
+ EventEmitter.prototype.listeners = function listeners(event) {
+ var evt = prefix ? prefix + event : event
+ , handlers = this._events[evt];
+
+ if (!handlers) { return []; }
+ if (handlers.fn) { return [handlers.fn]; }
+
+ for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {
+ ee[i] = handlers[i].fn;
+ }
+
+ return ee;
+ };
+
+ /**
+ * Return the number of listeners listening to a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @returns {Number} The number of listeners.
+ * @public
+ */
+ EventEmitter.prototype.listenerCount = function listenerCount(event) {
+ var evt = prefix ? prefix + event : event
+ , listeners = this._events[evt];
+
+ if (!listeners) { return 0; }
+ if (listeners.fn) { return 1; }
+ return listeners.length;
+ };
+
+ /**
+ * Calls each of the listeners registered for a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @returns {Boolean} `true` if the event had listeners, else `false`.
+ * @public
+ */
+ EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
+ var arguments$1 = arguments;
+
+ var evt = prefix ? prefix + event : event;
+
+ if (!this._events[evt]) { return false; }
+
+ var listeners = this._events[evt]
+ , len = arguments.length
+ , args
+ , i;
+
+ if (listeners.fn) {
+ if (listeners.once) { this.removeListener(event, listeners.fn, undefined, true); }
+
+ switch (len) {
+ case 1: return listeners.fn.call(listeners.context), true;
+ case 2: return listeners.fn.call(listeners.context, a1), true;
+ case 3: return listeners.fn.call(listeners.context, a1, a2), true;
+ case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;
+ case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
+ case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
+ }
+
+ for (i = 1, args = new Array(len -1); i < len; i++) {
+ args[i - 1] = arguments$1[i];
+ }
+
+ listeners.fn.apply(listeners.context, args);
+ } else {
+ var length = listeners.length
+ , j;
+
+ for (i = 0; i < length; i++) {
+ if (listeners[i].once) { this.removeListener(event, listeners[i].fn, undefined, true); }
+
+ switch (len) {
+ case 1: listeners[i].fn.call(listeners[i].context); break;
+ case 2: listeners[i].fn.call(listeners[i].context, a1); break;
+ case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break;
+ case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break;
+ default:
+ if (!args) { for (j = 1, args = new Array(len -1); j < len; j++) {
+ args[j - 1] = arguments$1[j];
+ } }
+
+ listeners[i].fn.apply(listeners[i].context, args);
+ }
+ }
+ }
+
+ return true;
+ };
+
+ /**
+ * Add a listener for a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @param {Function} fn The listener function.
+ * @param {*} [context=this] The context to invoke the listener with.
+ * @returns {EventEmitter} `this`.
+ * @public
+ */
+ EventEmitter.prototype.on = function on(event, fn, context) {
+ return addListener(this, event, fn, context, false);
+ };
+
+ /**
+ * Add a one-time listener for a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @param {Function} fn The listener function.
+ * @param {*} [context=this] The context to invoke the listener with.
+ * @returns {EventEmitter} `this`.
+ * @public
+ */
+ EventEmitter.prototype.once = function once(event, fn, context) {
+ return addListener(this, event, fn, context, true);
+ };
+
+ /**
+ * Remove the listeners of a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @param {Function} fn Only remove the listeners that match this function.
+ * @param {*} context Only remove the listeners that have this context.
+ * @param {Boolean} once Only remove one-time listeners.
+ * @returns {EventEmitter} `this`.
+ * @public
+ */
+ EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {
+ var evt = prefix ? prefix + event : event;
+
+ if (!this._events[evt]) { return this; }
+ if (!fn) {
+ clearEvent(this, evt);
+ return this;
+ }
+
+ var listeners = this._events[evt];
+
+ if (listeners.fn) {
+ if (
+ listeners.fn === fn &&
+ (!once || listeners.once) &&
+ (!context || listeners.context === context)
+ ) {
+ clearEvent(this, evt);
+ }
+ } else {
+ for (var i = 0, events = [], length = listeners.length; i < length; i++) {
+ if (
+ listeners[i].fn !== fn ||
+ (once && !listeners[i].once) ||
+ (context && listeners[i].context !== context)
+ ) {
+ events.push(listeners[i]);
+ }
+ }
+
+ //
+ // Reset the array, or remove it completely if we have no more listeners.
+ //
+ if (events.length) { this._events[evt] = events.length === 1 ? events[0] : events; }
+ else { clearEvent(this, evt); }
+ }
+
+ return this;
+ };
+
+ /**
+ * Remove all listeners, or those of the specified event.
+ *
+ * @param {(String|Symbol)} [event] The event name.
+ * @returns {EventEmitter} `this`.
+ * @public
+ */
+ EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
+ var evt;
+
+ if (event) {
+ evt = prefix ? prefix + event : event;
+ if (this._events[evt]) { clearEvent(this, evt); }
+ } else {
+ this._events = new Events();
+ this._eventsCount = 0;
+ }
+
+ return this;
+ };
+
+ //
+ // Alias methods names because people roll like that.
+ //
+ EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
+ EventEmitter.prototype.addListener = EventEmitter.prototype.on;
+
+ //
+ // Expose the prefix.
+ //
+ EventEmitter.prefixed = prefix;
+
+ //
+ // Allow `EventEmitter` to be imported as module namespace.
+ //
+ EventEmitter.EventEmitter = EventEmitter;
+
+ //
+ // Expose the module.
+ //
+ if ('undefined' !== 'object') {
+ module.exports = EventEmitter;
+ }
+ });
+
+ 'use strict';
+
+ var earcut_1 = earcut;
+ var _default = earcut;
+
+ function earcut(data, holeIndices, dim) {
+
+ dim = dim || 2;
+
+ var hasHoles = holeIndices && holeIndices.length,
+ outerLen = hasHoles ? holeIndices[0] * dim : data.length,
+ outerNode = linkedList(data, 0, outerLen, dim, true),
+ triangles = [];
+
+ if (!outerNode || outerNode.next === outerNode.prev) { return triangles; }
+
+ var minX, minY, maxX, maxY, x, y, invSize;
+
+ if (hasHoles) { outerNode = eliminateHoles(data, holeIndices, outerNode, dim); }
+
+ // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
+ if (data.length > 80 * dim) {
+ minX = maxX = data[0];
+ minY = maxY = data[1];
+
+ for (var i = dim; i < outerLen; i += dim) {
+ x = data[i];
+ y = data[i + 1];
+ if (x < minX) { minX = x; }
+ if (y < minY) { minY = y; }
+ if (x > maxX) { maxX = x; }
+ if (y > maxY) { maxY = y; }
+ }
+
+ // minX, minY and invSize are later used to transform coords into integers for z-order calculation
+ invSize = Math.max(maxX - minX, maxY - minY);
+ invSize = invSize !== 0 ? 32767 / invSize : 0;
+ }
+
+ earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0);
+
+ return triangles;
+ }
+
+ // create a circular doubly linked list from polygon points in the specified winding order
+ function linkedList(data, start, end, dim, clockwise) {
+ var i, last;
+
+ if (clockwise === (signedArea(data, start, end, dim) > 0)) {
+ for (i = start; i < end; i += dim) { last = insertNode(i, data[i], data[i + 1], last); }
+ } else {
+ for (i = end - dim; i >= start; i -= dim) { last = insertNode(i, data[i], data[i + 1], last); }
+ }
+
+ if (last && equals(last, last.next)) {
+ removeNode(last);
+ last = last.next;
+ }
+
+ return last;
+ }
+
+ // eliminate colinear or duplicate points
+ function filterPoints(start, end) {
+ if (!start) { return start; }
+ if (!end) { end = start; }
+
+ var p = start,
+ again;
+ do {
+ again = false;
+
+ if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
+ removeNode(p);
+ p = end = p.prev;
+ if (p === p.next) { break; }
+ again = true;
+
+ } else {
+ p = p.next;
+ }
+ } while (again || p !== end);
+
+ return end;
+ }
+
+ // main ear slicing loop which triangulates a polygon (given as a linked list)
+ function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
+ if (!ear) { return; }
+
+ // interlink polygon nodes in z-order
+ if (!pass && invSize) { indexCurve(ear, minX, minY, invSize); }
+
+ var stop = ear,
+ prev, next;
+
+ // iterate through ears, slicing them one by one
+ while (ear.prev !== ear.next) {
+ prev = ear.prev;
+ next = ear.next;
+
+ if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) {
+ // cut off the triangle
+ triangles.push(prev.i / dim | 0);
+ triangles.push(ear.i / dim | 0);
+ triangles.push(next.i / dim | 0);
+
+ removeNode(ear);
+
+ // skipping the next vertex leads to less sliver triangles
+ ear = next.next;
+ stop = next.next;
+
+ continue;
+ }
+
+ ear = next;
+
+ // if we looped through the whole remaining polygon and can't find any more ears
+ if (ear === stop) {
+ // try filtering points and slicing again
+ if (!pass) {
+ earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1);
+
+ // if this didn't work, try curing all small self-intersections locally
+ } else if (pass === 1) {
+ ear = cureLocalIntersections(filterPoints(ear), triangles, dim);
+ earcutLinked(ear, triangles, dim, minX, minY, invSize, 2);
+
+ // as a last resort, try splitting the remaining polygon into two
+ } else if (pass === 2) {
+ splitEarcut(ear, triangles, dim, minX, minY, invSize);
+ }
+
+ break;
+ }
+ }
+ }
+
+ // check whether a polygon node forms a valid ear with adjacent nodes
+ function isEar(ear) {
+ var a = ear.prev,
+ b = ear,
+ c = ear.next;
+
+ if (area(a, b, c) >= 0) { return false; } // reflex, can't be an ear
+
+ // now make sure we don't have other points inside the potential ear
+ var ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
+
+ // triangle bbox; min & max are calculated like this for speed
+ var x0 = ax < bx ? (ax < cx ? ax : cx) : (bx < cx ? bx : cx),
+ y0 = ay < by ? (ay < cy ? ay : cy) : (by < cy ? by : cy),
+ x1 = ax > bx ? (ax > cx ? ax : cx) : (bx > cx ? bx : cx),
+ y1 = ay > by ? (ay > cy ? ay : cy) : (by > cy ? by : cy);
+
+ var p = c.next;
+ while (p !== a) {
+ if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 &&
+ pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) &&
+ area(p.prev, p, p.next) >= 0) { return false; }
+ p = p.next;
+ }
+
+ return true;
+ }
+
+ function isEarHashed(ear, minX, minY, invSize) {
+ var a = ear.prev,
+ b = ear,
+ c = ear.next;
+
+ if (area(a, b, c) >= 0) { return false; } // reflex, can't be an ear
+
+ var ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
+
+ // triangle bbox; min & max are calculated like this for speed
+ var x0 = ax < bx ? (ax < cx ? ax : cx) : (bx < cx ? bx : cx),
+ y0 = ay < by ? (ay < cy ? ay : cy) : (by < cy ? by : cy),
+ x1 = ax > bx ? (ax > cx ? ax : cx) : (bx > cx ? bx : cx),
+ y1 = ay > by ? (ay > cy ? ay : cy) : (by > cy ? by : cy);
+
+ // z-order range for the current triangle bbox;
+ var minZ = zOrder(x0, y0, minX, minY, invSize),
+ maxZ = zOrder(x1, y1, minX, minY, invSize);
+
+ var p = ear.prevZ,
+ n = ear.nextZ;
+
+ // look for points inside the triangle in both directions
+ while (p && p.z >= minZ && n && n.z <= maxZ) {
+ if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c &&
+ pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) { return false; }
+ p = p.prevZ;
+
+ if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c &&
+ pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) { return false; }
+ n = n.nextZ;
+ }
+
+ // look for remaining points in decreasing z-order
+ while (p && p.z >= minZ) {
+ if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c &&
+ pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) { return false; }
+ p = p.prevZ;
+ }
+
+ // look for remaining points in increasing z-order
+ while (n && n.z <= maxZ) {
+ if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c &&
+ pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) { return false; }
+ n = n.nextZ;
+ }
+
+ return true;
+ }
+
+ // go through all polygon nodes and cure small local self-intersections
+ function cureLocalIntersections(start, triangles, dim) {
+ var p = start;
+ do {
+ var a = p.prev,
+ b = p.next.next;
+
+ if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {
+
+ triangles.push(a.i / dim | 0);
+ triangles.push(p.i / dim | 0);
+ triangles.push(b.i / dim | 0);
+
+ // remove two nodes involved
+ removeNode(p);
+ removeNode(p.next);
+
+ p = start = b;
+ }
+ p = p.next;
+ } while (p !== start);
+
+ return filterPoints(p);
+ }
+
+ // try splitting polygon into two and triangulate them independently
+ function splitEarcut(start, triangles, dim, minX, minY, invSize) {
+ // look for a valid diagonal that divides the polygon into two
+ var a = start;
+ do {
+ var b = a.next.next;
+ while (b !== a.prev) {
+ if (a.i !== b.i && isValidDiagonal(a, b)) {
+ // split the polygon in two by the diagonal
+ var c = splitPolygon(a, b);
+
+ // filter colinear points around the cuts
+ a = filterPoints(a, a.next);
+ c = filterPoints(c, c.next);
+
+ // run earcut on each half
+ earcutLinked(a, triangles, dim, minX, minY, invSize, 0);
+ earcutLinked(c, triangles, dim, minX, minY, invSize, 0);
+ return;
+ }
+ b = b.next;
+ }
+ a = a.next;
+ } while (a !== start);
+ }
+
+ // link every hole into the outer loop, producing a single-ring polygon without holes
+ function eliminateHoles(data, holeIndices, outerNode, dim) {
+ var queue = [],
+ i, len, start, end, list;
+
+ for (i = 0, len = holeIndices.length; i < len; i++) {
+ start = holeIndices[i] * dim;
+ end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
+ list = linkedList(data, start, end, dim, false);
+ if (list === list.next) { list.steiner = true; }
+ queue.push(getLeftmost(list));
+ }
+
+ queue.sort(compareX);
+
+ // process holes from left to right
+ for (i = 0; i < queue.length; i++) {
+ outerNode = eliminateHole(queue[i], outerNode);
+ }
+
+ return outerNode;
+ }
+
+ function compareX(a, b) {
+ return a.x - b.x;
+ }
+
+ // find a bridge between vertices that connects hole with an outer ring and and link it
+ function eliminateHole(hole, outerNode) {
+ var bridge = findHoleBridge(hole, outerNode);
+ if (!bridge) {
+ return outerNode;
+ }
+
+ var bridgeReverse = splitPolygon(bridge, hole);
+
+ // filter collinear points around the cuts
+ filterPoints(bridgeReverse, bridgeReverse.next);
+ return filterPoints(bridge, bridge.next);
+ }
+
+ // David Eberly's algorithm for finding a bridge between hole and outer polygon
+ function findHoleBridge(hole, outerNode) {
+ var p = outerNode,
+ hx = hole.x,
+ hy = hole.y,
+ qx = -Infinity,
+ m;
+
+ // find a segment intersected by a ray from the hole's leftmost point to the left;
+ // segment's endpoint with lesser x will be potential connection point
+ do {
+ if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
+ var x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
+ if (x <= hx && x > qx) {
+ qx = x;
+ m = p.x < p.next.x ? p : p.next;
+ if (x === hx) { return m; } // hole touches outer segment; pick leftmost endpoint
+ }
+ }
+ p = p.next;
+ } while (p !== outerNode);
+
+ if (!m) { return null; }
+
+ // look for points inside the triangle of hole point, segment intersection and endpoint;
+ // if there are no points found, we have a valid connection;
+ // otherwise choose the point of the minimum angle with the ray as connection point
+
+ var stop = m,
+ mx = m.x,
+ my = m.y,
+ tanMin = Infinity,
+ tan;
+
+ p = m;
+
+ do {
+ if (hx >= p.x && p.x >= mx && hx !== p.x &&
+ pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) {
+
+ tan = Math.abs(hy - p.y) / (hx - p.x); // tangential
+
+ if (locallyInside(p, hole) &&
+ (tan < tanMin || (tan === tanMin && (p.x > m.x || (p.x === m.x && sectorContainsSector(m, p)))))) {
+ m = p;
+ tanMin = tan;
+ }
+ }
+
+ p = p.next;
+ } while (p !== stop);
+
+ return m;
+ }
+
+ // whether sector in vertex m contains sector in vertex p in the same coordinates
+ function sectorContainsSector(m, p) {
+ return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0;
+ }
+
+ // interlink polygon nodes in z-order
+ function indexCurve(start, minX, minY, invSize) {
+ var p = start;
+ do {
+ if (p.z === 0) { p.z = zOrder(p.x, p.y, minX, minY, invSize); }
+ p.prevZ = p.prev;
+ p.nextZ = p.next;
+ p = p.next;
+ } while (p !== start);
+
+ p.prevZ.nextZ = null;
+ p.prevZ = null;
+
+ sortLinked(p);
+ }
+
+ // Simon Tatham's linked list merge sort algorithm
+ // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
+ function sortLinked(list) {
+ var i, p, q, e, tail, numMerges, pSize, qSize,
+ inSize = 1;
+
+ do {
+ p = list;
+ list = null;
+ tail = null;
+ numMerges = 0;
+
+ while (p) {
+ numMerges++;
+ q = p;
+ pSize = 0;
+ for (i = 0; i < inSize; i++) {
+ pSize++;
+ q = q.nextZ;
+ if (!q) { break; }
+ }
+ qSize = inSize;
+
+ while (pSize > 0 || (qSize > 0 && q)) {
+
+ if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) {
+ e = p;
+ p = p.nextZ;
+ pSize--;
+ } else {
+ e = q;
+ q = q.nextZ;
+ qSize--;
+ }
+
+ if (tail) { tail.nextZ = e; }
+ else { list = e; }
+
+ e.prevZ = tail;
+ tail = e;
+ }
+
+ p = q;
+ }
+
+ tail.nextZ = null;
+ inSize *= 2;
+
+ } while (numMerges > 1);
+
+ return list;
+ }
+
+ // z-order of a point given coords and inverse of the longer side of data bbox
+ function zOrder(x, y, minX, minY, invSize) {
+ // coords are transformed into non-negative 15-bit integer range
+ x = (x - minX) * invSize | 0;
+ y = (y - minY) * invSize | 0;
+
+ x = (x | (x << 8)) & 0x00FF00FF;
+ x = (x | (x << 4)) & 0x0F0F0F0F;
+ x = (x | (x << 2)) & 0x33333333;
+ x = (x | (x << 1)) & 0x55555555;
+
+ y = (y | (y << 8)) & 0x00FF00FF;
+ y = (y | (y << 4)) & 0x0F0F0F0F;
+ y = (y | (y << 2)) & 0x33333333;
+ y = (y | (y << 1)) & 0x55555555;
+
+ return x | (y << 1);
+ }
+
+ // find the leftmost node of a polygon ring
+ function getLeftmost(start) {
+ var p = start,
+ leftmost = start;
+ do {
+ if (p.x < leftmost.x || (p.x === leftmost.x && p.y < leftmost.y)) { leftmost = p; }
+ p = p.next;
+ } while (p !== start);
+
+ return leftmost;
+ }
+
+ // check if a point lies within a convex triangle
+ function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
+ return (cx - px) * (ay - py) >= (ax - px) * (cy - py) &&
+ (ax - px) * (by - py) >= (bx - px) * (ay - py) &&
+ (bx - px) * (cy - py) >= (cx - px) * (by - py);
+ }
+
+ // check if a diagonal between two polygon nodes is valid (lies in polygon interior)
+ function isValidDiagonal(a, b) {
+ return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // dones't intersect other edges
+ (locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
+ (area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors
+ equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case
+ }
+
+ // signed area of a triangle
+ function area(p, q, r) {
+ return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
+ }
+
+ // check if two points are equal
+ function equals(p1, p2) {
+ return p1.x === p2.x && p1.y === p2.y;
+ }
+
+ // check if two segments intersect
+ function intersects(p1, q1, p2, q2) {
+ var o1 = sign$1(area(p1, q1, p2));
+ var o2 = sign$1(area(p1, q1, q2));
+ var o3 = sign$1(area(p2, q2, p1));
+ var o4 = sign$1(area(p2, q2, q1));
+
+ if (o1 !== o2 && o3 !== o4) { return true; } // general case
+
+ if (o1 === 0 && onSegment(p1, p2, q1)) { return true; } // p1, q1 and p2 are collinear and p2 lies on p1q1
+ if (o2 === 0 && onSegment(p1, q2, q1)) { return true; } // p1, q1 and q2 are collinear and q2 lies on p1q1
+ if (o3 === 0 && onSegment(p2, p1, q2)) { return true; } // p2, q2 and p1 are collinear and p1 lies on p2q2
+ if (o4 === 0 && onSegment(p2, q1, q2)) { return true; } // p2, q2 and q1 are collinear and q1 lies on p2q2
+
+ return false;
+ }
+
+ // for collinear points p, q, r, check if point q lies on segment pr
+ function onSegment(p, q, r) {
+ return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y);
+ }
+
+ function sign$1(num) {
+ return num > 0 ? 1 : num < 0 ? -1 : 0;
+ }
+
+ // check if a polygon diagonal intersects any polygon segments
+ function intersectsPolygon(a, b) {
+ var p = a;
+ do {
+ if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i &&
+ intersects(p, p.next, a, b)) { return true; }
+ p = p.next;
+ } while (p !== a);
+
+ return false;
+ }
+
+ // check if a polygon diagonal is locally inside the polygon
+ function locallyInside(a, b) {
+ return area(a.prev, a, a.next) < 0 ?
+ area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 :
+ area(a, b, a.prev) < 0 || area(a, a.next, b) < 0;
+ }
+
+ // check if the middle point of a polygon diagonal is inside the polygon
+ function middleInside(a, b) {
+ var p = a,
+ inside = false,
+ px = (a.x + b.x) / 2,
+ py = (a.y + b.y) / 2;
+ do {
+ if (((p.y > py) !== (p.next.y > py)) && p.next.y !== p.y &&
+ (px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x))
+ { inside = !inside; }
+ p = p.next;
+ } while (p !== a);
+
+ return inside;
+ }
+
+ // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two;
+ // if one belongs to the outer ring and another to a hole, it merges it into a single ring
+ function splitPolygon(a, b) {
+ var a2 = new Node(a.i, a.x, a.y),
+ b2 = new Node(b.i, b.x, b.y),
+ an = a.next,
+ bp = b.prev;
+
+ a.next = b;
+ b.prev = a;
+
+ a2.next = an;
+ an.prev = a2;
+
+ b2.next = a2;
+ a2.prev = b2;
+
+ bp.next = b2;
+ b2.prev = bp;
+
+ return b2;
+ }
+
+ // create a node and optionally link it with previous one (in a circular doubly linked list)
+ function insertNode(i, x, y, last) {
+ var p = new Node(i, x, y);
+
+ if (!last) {
+ p.prev = p;
+ p.next = p;
+
+ } else {
+ p.next = last.next;
+ p.prev = last;
+ last.next.prev = p;
+ last.next = p;
+ }
+ return p;
+ }
+
+ function removeNode(p) {
+ p.next.prev = p.prev;
+ p.prev.next = p.next;
+
+ if (p.prevZ) { p.prevZ.nextZ = p.nextZ; }
+ if (p.nextZ) { p.nextZ.prevZ = p.prevZ; }
+ }
+
+ function Node(i, x, y) {
+ // vertex index in coordinates array
+ this.i = i;
+
+ // vertex coordinates
+ this.x = x;
+ this.y = y;
+
+ // previous and next vertex nodes in a polygon ring
+ this.prev = null;
+ this.next = null;
+
+ // z-order curve value
+ this.z = 0;
+
+ // previous and next nodes in z-order
+ this.prevZ = null;
+ this.nextZ = null;
+
+ // indicates whether this is a steiner point
+ this.steiner = false;
+ }
+
+ // return a percentage difference between the polygon area and its triangulation area;
+ // used to verify correctness of triangulation
+ earcut.deviation = function (data, holeIndices, dim, triangles) {
+ var hasHoles = holeIndices && holeIndices.length;
+ var outerLen = hasHoles ? holeIndices[0] * dim : data.length;
+
+ var polygonArea = Math.abs(signedArea(data, 0, outerLen, dim));
+ if (hasHoles) {
+ for (var i = 0, len = holeIndices.length; i < len; i++) {
+ var start = holeIndices[i] * dim;
+ var end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
+ polygonArea -= Math.abs(signedArea(data, start, end, dim));
+ }
+ }
+
+ var trianglesArea = 0;
+ for (i = 0; i < triangles.length; i += 3) {
+ var a = triangles[i] * dim;
+ var b = triangles[i + 1] * dim;
+ var c = triangles[i + 2] * dim;
+ trianglesArea += Math.abs(
+ (data[a] - data[c]) * (data[b + 1] - data[a + 1]) -
+ (data[a] - data[b]) * (data[c + 1] - data[a + 1]));
+ }
+
+ return polygonArea === 0 && trianglesArea === 0 ? 0 :
+ Math.abs((trianglesArea - polygonArea) / polygonArea);
+ };
+
+ function signedArea(data, start, end, dim) {
+ var sum = 0;
+ for (var i = start, j = end - dim; i < end; i += dim) {
+ sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
+ j = i;
+ }
+ return sum;
+ }
+
+ // turn a polygon in a multi-dimensional array form (e.g. as in GeoJSON) into a form Earcut accepts
+ earcut.flatten = function (data) {
+ var dim = data[0][0].length,
+ result = {vertices: [], holes: [], dimensions: dim},
+ holeIndex = 0;
+
+ for (var i = 0; i < data.length; i++) {
+ for (var j = 0; j < data[i].length; j++) {
+ for (var d = 0; d < dim; d++) { result.vertices.push(data[i][j][d]); }
+ }
+ if (i > 0) {
+ holeIndex += data[i - 1].length;
+ result.holes.push(holeIndex);
+ }
+ }
+ return result;
+ };
+ earcut_1.default = _default;
+
+ var punycode = createCommonjsModule$1(function (module, exports) {
+ /*! https://mths.be/punycode v1.3.2 by @mathias */
+ ;(function(root) {
+
+ /** Detect free variables */
+ var freeExports = 'object' == 'object' && exports &&
+ !exports.nodeType && exports;
+ var freeModule = 'object' == 'object' && module &&
+ !module.nodeType && module;
+ var freeGlobal = typeof commonjsGlobal$1 == 'object' && commonjsGlobal$1;
+ if (
+ freeGlobal.global === freeGlobal ||
+ freeGlobal.window === freeGlobal ||
+ freeGlobal.self === freeGlobal
+ ) {
+ root = freeGlobal;
+ }
+
+ /**
+ * The `punycode` object.
+ * @name punycode
+ * @type Object
+ */
+ var punycode,
+
+ /** Highest positive signed 32-bit float value */
+ maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1
+
+ /** Bootstring parameters */
+ base = 36,
+ tMin = 1,
+ tMax = 26,
+ skew = 38,
+ damp = 700,
+ initialBias = 72,
+ initialN = 128, // 0x80
+ delimiter = '-', // '\x2D'
+
+ /** Regular expressions */
+ regexPunycode = /^xn--/,
+ regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars
+ regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators
+
+ /** Error messages */
+ errors = {
+ 'overflow': 'Overflow: input needs wider integers to process',
+ 'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
+ 'invalid-input': 'Invalid input'
+ },
+
+ /** Convenience shortcuts */
+ baseMinusTMin = base - tMin,
+ floor = Math.floor,
+ stringFromCharCode = String.fromCharCode,
+
+ /** Temporary variable */
+ key;
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * A generic error utility function.
+ * @private
+ * @param {String} type The error type.
+ * @returns {Error} Throws a `RangeError` with the applicable error message.
+ */
+ function error(type) {
+ throw RangeError(errors[type]);
+ }
+
+ /**
+ * A generic `Array#map` utility function.
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} callback The function that gets called for every array
+ * item.
+ * @returns {Array} A new array of values returned by the callback function.
+ */
+ function map(array, fn) {
+ var length = array.length;
+ var result = [];
+ while (length--) {
+ result[length] = fn(array[length]);
+ }
+ return result;
+ }
+
+ /**
+ * A simple `Array#map`-like wrapper to work with domain name strings or email
+ * addresses.
+ * @private
+ * @param {String} domain The domain name or email address.
+ * @param {Function} callback The function that gets called for every
+ * character.
+ * @returns {Array} A new string of characters returned by the callback
+ * function.
+ */
+ function mapDomain(string, fn) {
+ var parts = string.split('@');
+ var result = '';
+ if (parts.length > 1) {
+ // In email addresses, only the domain name should be punycoded. Leave
+ // the local part (i.e. everything up to `@`) intact.
+ result = parts[0] + '@';
+ string = parts[1];
+ }
+ // Avoid `split(regex)` for IE8 compatibility. See #17.
+ string = string.replace(regexSeparators, '\x2E');
+ var labels = string.split('.');
+ var encoded = map(labels, fn).join('.');
+ return result + encoded;
+ }
+
+ /**
+ * Creates an array containing the numeric code points of each Unicode
+ * character in the string. While JavaScript uses UCS-2 internally,
+ * this function will convert a pair of surrogate halves (each of which
+ * UCS-2 exposes as separate characters) into a single code point,
+ * matching UTF-16.
+ * @see `punycode.ucs2.encode`
+ * @see <https://mathiasbynens.be/notes/javascript-encoding>
+ * @memberOf punycode.ucs2
+ * @name decode
+ * @param {String} string The Unicode input string (UCS-2).
+ * @returns {Array} The new array of code points.
+ */
+ function ucs2decode(string) {
+ var output = [],
+ counter = 0,
+ length = string.length,
+ value,
+ extra;
+ while (counter < length) {
+ value = string.charCodeAt(counter++);
+ if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
+ // high surrogate, and there is a next character
+ extra = string.charCodeAt(counter++);
+ if ((extra & 0xFC00) == 0xDC00) { // low surrogate
+ output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
+ } else {
+ // unmatched surrogate; only append this code unit, in case the next
+ // code unit is the high surrogate of a surrogate pair
+ output.push(value);
+ counter--;
+ }
+ } else {
+ output.push(value);
+ }
+ }
+ return output;
+ }
+
+ /**
+ * Creates a string based on an array of numeric code points.
+ * @see `punycode.ucs2.decode`
+ * @memberOf punycode.ucs2
+ * @name encode
+ * @param {Array} codePoints The array of numeric code points.
+ * @returns {String} The new Unicode string (UCS-2).
+ */
+ function ucs2encode(array) {
+ return map(array, function(value) {
+ var output = '';
+ if (value > 0xFFFF) {
+ value -= 0x10000;
+ output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
+ value = 0xDC00 | value & 0x3FF;
+ }
+ output += stringFromCharCode(value);
+ return output;
+ }).join('');
+ }
+
+ /**
+ * Converts a basic code point into a digit/integer.
+ * @see `digitToBasic()`
+ * @private
+ * @param {Number} codePoint The basic numeric code point value.
+ * @returns {Number} The numeric value of a basic code point (for use in
+ * representing integers) in the range `0` to `base - 1`, or `base` if
+ * the code point does not represent a value.
+ */
+ function basicToDigit(codePoint) {
+ if (codePoint - 48 < 10) {
+ return codePoint - 22;
+ }
+ if (codePoint - 65 < 26) {
+ return codePoint - 65;
+ }
+ if (codePoint - 97 < 26) {
+ return codePoint - 97;
+ }
+ return base;
+ }
+
+ /**
+ * Converts a digit/integer into a basic code point.
+ * @see `basicToDigit()`
+ * @private
+ * @param {Number} digit The numeric value of a basic code point.
+ * @returns {Number} The basic code point whose value (when used for
+ * representing integers) is `digit`, which needs to be in the range
+ * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
+ * used; else, the lowercase form is used. The behavior is undefined
+ * if `flag` is non-zero and `digit` has no uppercase form.
+ */
+ function digitToBasic(digit, flag) {
+ // 0..25 map to ASCII a..z or A..Z
+ // 26..35 map to ASCII 0..9
+ return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
+ }
+
+ /**
+ * Bias adaptation function as per section 3.4 of RFC 3492.
+ * http://tools.ietf.org/html/rfc3492#section-3.4
+ * @private
+ */
+ function adapt(delta, numPoints, firstTime) {
+ var k = 0;
+ delta = firstTime ? floor(delta / damp) : delta >> 1;
+ delta += floor(delta / numPoints);
+ for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
+ delta = floor(delta / baseMinusTMin);
+ }
+ return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
+ }
+
+ /**
+ * Converts a Punycode string of ASCII-only symbols to a string of Unicode
+ * symbols.
+ * @memberOf punycode
+ * @param {String} input The Punycode string of ASCII-only symbols.
+ * @returns {String} The resulting string of Unicode symbols.
+ */
+ function decode(input) {
+ // Don't use UCS-2
+ var output = [],
+ inputLength = input.length,
+ out,
+ i = 0,
+ n = initialN,
+ bias = initialBias,
+ basic,
+ j,
+ index,
+ oldi,
+ w,
+ k,
+ digit,
+ t,
+ /** Cached calculation results */
+ baseMinusT;
+
+ // Handle the basic code points: let `basic` be the number of input code
+ // points before the last delimiter, or `0` if there is none, then copy
+ // the first basic code points to the output.
+
+ basic = input.lastIndexOf(delimiter);
+ if (basic < 0) {
+ basic = 0;
+ }
+
+ for (j = 0; j < basic; ++j) {
+ // if it's not a basic code point
+ if (input.charCodeAt(j) >= 0x80) {
+ error('not-basic');
+ }
+ output.push(input.charCodeAt(j));
+ }
+
+ // Main decoding loop: start just after the last delimiter if any basic code
+ // points were copied; start at the beginning otherwise.
+
+ for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {
+
+ // `index` is the index of the next character to be consumed.
+ // Decode a generalized variable-length integer into `delta`,
+ // which gets added to `i`. The overflow checking is easier
+ // if we increase `i` as we go, then subtract off its starting
+ // value at the end to obtain `delta`.
+ for (oldi = i, w = 1, k = base; /* no condition */; k += base) {
+
+ if (index >= inputLength) {
+ error('invalid-input');
+ }
+
+ digit = basicToDigit(input.charCodeAt(index++));
+
+ if (digit >= base || digit > floor((maxInt - i) / w)) {
+ error('overflow');
+ }
+
+ i += digit * w;
+ t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
+
+ if (digit < t) {
+ break;
+ }
+
+ baseMinusT = base - t;
+ if (w > floor(maxInt / baseMinusT)) {
+ error('overflow');
+ }
+
+ w *= baseMinusT;
+
+ }
+
+ out = output.length + 1;
+ bias = adapt(i - oldi, out, oldi == 0);
+
+ // `i` was supposed to wrap around from `out` to `0`,
+ // incrementing `n` each time, so we'll fix that now:
+ if (floor(i / out) > maxInt - n) {
+ error('overflow');
+ }
+
+ n += floor(i / out);
+ i %= out;
+
+ // Insert `n` at position `i` of the output
+ output.splice(i++, 0, n);
+
+ }
+
+ return ucs2encode(output);
+ }
+
+ /**
+ * Converts a string of Unicode symbols (e.g. a domain name label) to a
+ * Punycode string of ASCII-only symbols.
+ * @memberOf punycode
+ * @param {String} input The string of Unicode symbols.
+ * @returns {String} The resulting Punycode string of ASCII-only symbols.
+ */
+ function encode(input) {
+ var n,
+ delta,
+ handledCPCount,
+ basicLength,
+ bias,
+ j,
+ m,
+ q,
+ k,
+ t,
+ currentValue,
+ output = [],
+ /** `inputLength` will hold the number of code points in `input`. */
+ inputLength,
+ /** Cached calculation results */
+ handledCPCountPlusOne,
+ baseMinusT,
+ qMinusT;
+
+ // Convert the input in UCS-2 to Unicode
+ input = ucs2decode(input);
+
+ // Cache the length
+ inputLength = input.length;
+
+ // Initialize the state
+ n = initialN;
+ delta = 0;
+ bias = initialBias;
+
+ // Handle the basic code points
+ for (j = 0; j < inputLength; ++j) {
+ currentValue = input[j];
+ if (currentValue < 0x80) {
+ output.push(stringFromCharCode(currentValue));
+ }
+ }
+
+ handledCPCount = basicLength = output.length;
+
+ // `handledCPCount` is the number of code points that have been handled;
+ // `basicLength` is the number of basic code points.
+
+ // Finish the basic string - if it is not empty - with a delimiter
+ if (basicLength) {
+ output.push(delimiter);
+ }
+
+ // Main encoding loop:
+ while (handledCPCount < inputLength) {
+
+ // All non-basic code points < n have been handled already. Find the next
+ // larger one:
+ for (m = maxInt, j = 0; j < inputLength; ++j) {
+ currentValue = input[j];
+ if (currentValue >= n && currentValue < m) {
+ m = currentValue;
+ }
+ }
+
+ // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
+ // but guard against overflow
+ handledCPCountPlusOne = handledCPCount + 1;
+ if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
+ error('overflow');
+ }
+
+ delta += (m - n) * handledCPCountPlusOne;
+ n = m;
+
+ for (j = 0; j < inputLength; ++j) {
+ currentValue = input[j];
+
+ if (currentValue < n && ++delta > maxInt) {
+ error('overflow');
+ }
+
+ if (currentValue == n) {
+ // Represent delta as a generalized variable-length integer
+ for (q = delta, k = base; /* no condition */; k += base) {
+ t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
+ if (q < t) {
+ break;
+ }
+ qMinusT = q - t;
+ baseMinusT = base - t;
+ output.push(
+ stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
+ );
+ q = floor(qMinusT / baseMinusT);
+ }
+
+ output.push(stringFromCharCode(digitToBasic(q, 0)));
+ bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
+ delta = 0;
+ ++handledCPCount;
+ }
+ }
+
+ ++delta;
+ ++n;
+
+ }
+ return output.join('');
+ }
+
+ /**
+ * Converts a Punycode string representing a domain name or an email address
+ * to Unicode. Only the Punycoded parts of the input will be converted, i.e.
+ * it doesn't matter if you call it on a string that has already been
+ * converted to Unicode.
+ * @memberOf punycode
+ * @param {String} input The Punycoded domain name or email address to
+ * convert to Unicode.
+ * @returns {String} The Unicode representation of the given Punycode
+ * string.
+ */
+ function toUnicode(input) {
+ return mapDomain(input, function(string) {
+ return regexPunycode.test(string)
+ ? decode(string.slice(4).toLowerCase())
+ : string;
+ });
+ }
+
+ /**
+ * Converts a Unicode string representing a domain name or an email address to
+ * Punycode. Only the non-ASCII parts of the domain name will be converted,
+ * i.e. it doesn't matter if you call it with a domain that's already in
+ * ASCII.
+ * @memberOf punycode
+ * @param {String} input The domain name or email address to convert, as a
+ * Unicode string.
+ * @returns {String} The Punycode representation of the given domain name or
+ * email address.
+ */
+ function toASCII(input) {
+ return mapDomain(input, function(string) {
+ return regexNonASCII.test(string)
+ ? 'xn--' + encode(string)
+ : string;
+ });
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /** Define the public API */
+ punycode = {
+ /**
+ * A string representing the current Punycode.js version number.
+ * @memberOf punycode
+ * @type String
+ */
+ 'version': '1.3.2',
+ /**
+ * An object of methods to convert from JavaScript's internal character
+ * representation (UCS-2) to Unicode code points, and back.
+ * @see <https://mathiasbynens.be/notes/javascript-encoding>
+ * @memberOf punycode
+ * @type Object
+ */
+ 'ucs2': {
+ 'decode': ucs2decode,
+ 'encode': ucs2encode
+ },
+ 'decode': decode,
+ 'encode': encode,
+ 'toASCII': toASCII,
+ 'toUnicode': toUnicode
+ };
+
+ /** Expose `punycode` */
+ // Some AMD build optimizers, like r.js, check for specific condition patterns
+ // like the following:
+ if (
+ typeof undefined == 'function' &&
+ typeof undefined.amd == 'object' &&
+ undefined.amd
+ ) {
+ undefined('punycode', function() {
+ return punycode;
+ });
+ } else if (freeExports && freeModule) {
+ if (module.exports == freeExports) { // in Node.js or RingoJS v0.8.0+
+ freeModule.exports = punycode;
+ } else { // in Narwhal or RingoJS v0.7.0-
+ for (key in punycode) {
+ punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]);
+ }
+ }
+ } else { // in Rhino or a web browser
+ root.punycode = punycode;
+ }
+
+ }(commonjsGlobal$1));
+ });
+
+ 'use strict';
+
+ var util = {
+ isString: function(arg) {
+ return typeof(arg) === 'string';
+ },
+ isObject: function(arg) {
+ return typeof(arg) === 'object' && arg !== null;
+ },
+ isNull: function(arg) {
+ return arg === null;
+ },
+ isNullOrUndefined: function(arg) {
+ return arg == null;
+ }
+ };
+
+ // Copyright Joyent, Inc. and other Node contributors.
+ //
+ // Permission is hereby granted, free of charge, to any person obtaining a
+ // copy of this software and associated documentation files (the
+ // "Software"), to deal in the Software without restriction, including
+ // without limitation the rights to use, copy, modify, merge, publish,
+ // distribute, sublicense, and/or sell copies of the Software, and to permit
+ // persons to whom the Software is furnished to do so, subject to the
+ // following conditions:
+ //
+ // The above copyright notice and this permission notice shall be included
+ // in all copies or substantial portions of the Software.
+ //
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ // USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ 'use strict';
+
+ // If obj.hasOwnProperty has been overridden, then calling
+ // obj.hasOwnProperty(prop) will break.
+ // See: https://github.com/joyent/node/issues/1707
+ function hasOwnProperty$1(obj, prop) {
+ return Object.prototype.hasOwnProperty.call(obj, prop);
+ }
+
+ var decode$1 = function(qs, sep, eq, options) {
+ sep = sep || '&';
+ eq = eq || '=';
+ var obj = {};
+
+ if (typeof qs !== 'string' || qs.length === 0) {
+ return obj;
+ }
+
+ var regexp = /\+/g;
+ qs = qs.split(sep);
+
+ var maxKeys = 1000;
+ if (options && typeof options.maxKeys === 'number') {
+ maxKeys = options.maxKeys;
+ }
+
+ var len = qs.length;
+ // maxKeys <= 0 means that we should not limit keys count
+ if (maxKeys > 0 && len > maxKeys) {
+ len = maxKeys;
+ }
+
+ for (var i = 0; i < len; ++i) {
+ var x = qs[i].replace(regexp, '%20'),
+ idx = x.indexOf(eq),
+ kstr, vstr, k, v;
+
+ if (idx >= 0) {
+ kstr = x.substr(0, idx);
+ vstr = x.substr(idx + 1);
+ } else {
+ kstr = x;
+ vstr = '';
+ }
+
+ k = decodeURIComponent(kstr);
+ v = decodeURIComponent(vstr);
+
+ if (!hasOwnProperty$1(obj, k)) {
+ obj[k] = v;
+ } else if (Array.isArray(obj[k])) {
+ obj[k].push(v);
+ } else {
+ obj[k] = [obj[k], v];
+ }
+ }
+
+ return obj;
+ };
+
+ // Copyright Joyent, Inc. and other Node contributors.
+ //
+ // Permission is hereby granted, free of charge, to any person obtaining a
+ // copy of this software and associated documentation files (the
+ // "Software"), to deal in the Software without restriction, including
+ // without limitation the rights to use, copy, modify, merge, publish,
+ // distribute, sublicense, and/or sell copies of the Software, and to permit
+ // persons to whom the Software is furnished to do so, subject to the
+ // following conditions:
+ //
+ // The above copyright notice and this permission notice shall be included
+ // in all copies or substantial portions of the Software.
+ //
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ // USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ 'use strict';
+
+ var stringifyPrimitive$1 = function(v) {
+ switch (typeof v) {
+ case 'string':
+ return v;
+
+ case 'boolean':
+ return v ? 'true' : 'false';
+
+ case 'number':
+ return isFinite(v) ? v : '';
+
+ default:
+ return '';
+ }
+ };
+
+ var encode$1 = function(obj, sep, eq, name) {
+ sep = sep || '&';
+ eq = eq || '=';
+ if (obj === null) {
+ obj = undefined;
+ }
+
+ if (typeof obj === 'object') {
+ return Object.keys(obj).map(function(k) {
+ var ks = encodeURIComponent(stringifyPrimitive$1(k)) + eq;
+ if (Array.isArray(obj[k])) {
+ return obj[k].map(function(v) {
+ return ks + encodeURIComponent(stringifyPrimitive$1(v));
+ }).join(sep);
+ } else {
+ return ks + encodeURIComponent(stringifyPrimitive$1(obj[k]));
+ }
+ }).join(sep);
+
+ }
+
+ if (!name) { return ''; }
+ return encodeURIComponent(stringifyPrimitive$1(name)) + eq +
+ encodeURIComponent(stringifyPrimitive$1(obj));
+ };
+
+ var querystring = createCommonjsModule$1(function (module, exports) {
+ 'use strict';
+
+ exports.decode = exports.parse = decode$1;
+ exports.encode = exports.stringify = encode$1;
+ });
+
+ // Copyright Joyent, Inc. and other Node contributors.
+ //
+ // Permission is hereby granted, free of charge, to any person obtaining a
+ // copy of this software and associated documentation files (the
+ // "Software"), to deal in the Software without restriction, including
+ // without limitation the rights to use, copy, modify, merge, publish,
+ // distribute, sublicense, and/or sell copies of the Software, and to permit
+ // persons to whom the Software is furnished to do so, subject to the
+ // following conditions:
+ //
+ // The above copyright notice and this permission notice shall be included
+ // in all copies or substantial portions of the Software.
+ //
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ // USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ 'use strict';
+
+
+
+
+ var parse = urlParse;
+ var resolve = urlResolve;
+ var resolveObject = urlResolveObject;
+ var format = urlFormat;
+
+ var Url_1 = Url;
+
+ function Url() {
+ this.protocol = null;
+ this.slashes = null;
+ this.auth = null;
+ this.host = null;
+ this.port = null;
+ this.hostname = null;
+ this.hash = null;
+ this.search = null;
+ this.query = null;
+ this.pathname = null;
+ this.path = null;
+ this.href = null;
+ }
+
+ // Reference: RFC 3986, RFC 1808, RFC 2396
+
+ // define these here so at least they only have to be
+ // compiled once on the first module load.
+ var protocolPattern = /^([a-z0-9.+-]+:)/i,
+ portPattern = /:[0-9]*$/,
+
+ // Special case for a simple path URL
+ simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,
+
+ // RFC 2396: characters reserved for delimiting URLs.
+ // We actually just auto-escape these.
+ delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'],
+
+ // RFC 2396: characters not allowed for various reasons.
+ unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims),
+
+ // Allowed by RFCs, but cause of XSS attacks. Always escape these.
+ autoEscape = ['\''].concat(unwise),
+ // Characters that are never ever allowed in a hostname.
+ // Note that any invalid chars are also handled, but these
+ // are the ones that are *expected* to be seen, so we fast-path
+ // them.
+ nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape),
+ hostEndingChars = ['/', '?', '#'],
+ hostnameMaxLen = 255,
+ hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/,
+ hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/,
+ // protocols that can allow "unsafe" and "unwise" chars.
+ unsafeProtocol = {
+ 'javascript': true,
+ 'javascript:': true
+ },
+ // protocols that never have a hostname.
+ hostlessProtocol = {
+ 'javascript': true,
+ 'javascript:': true
+ },
+ // protocols that always contain a // bit.
+ slashedProtocol = {
+ 'http': true,
+ 'https': true,
+ 'ftp': true,
+ 'gopher': true,
+ 'file': true,
+ 'http:': true,
+ 'https:': true,
+ 'ftp:': true,
+ 'gopher:': true,
+ 'file:': true
+ };
+
+ function urlParse(url, parseQueryString, slashesDenoteHost) {
+ if (url && util.isObject(url) && url instanceof Url) { return url; }
+
+ var u = new Url;
+ u.parse(url, parseQueryString, slashesDenoteHost);
+ return u;
+ }
+
+ Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
+ if (!util.isString(url)) {
+ throw new TypeError("Parameter 'url' must be a string, not " + typeof url);
+ }
+
+ // Copy chrome, IE, opera backslash-handling behavior.
+ // Back slashes before the query string get converted to forward slashes
+ // See: https://code.google.com/p/chromium/issues/detail?id=25916
+ var queryIndex = url.indexOf('?'),
+ splitter =
+ (queryIndex !== -1 && queryIndex < url.indexOf('#')) ? '?' : '#',
+ uSplit = url.split(splitter),
+ slashRegex = /\\/g;
+ uSplit[0] = uSplit[0].replace(slashRegex, '/');
+ url = uSplit.join(splitter);
+
+ var rest = url;
+
+ // trim before proceeding.
+ // This is to support parse stuff like " http://foo.com \n"
+ rest = rest.trim();
+
+ if (!slashesDenoteHost && url.split('#').length === 1) {
+ // Try fast path regexp
+ var simplePath = simplePathPattern.exec(rest);
+ if (simplePath) {
+ this.path = rest;
+ this.href = rest;
+ this.pathname = simplePath[1];
+ if (simplePath[2]) {
+ this.search = simplePath[2];
+ if (parseQueryString) {
+ this.query = querystring.parse(this.search.substr(1));
+ } else {
+ this.query = this.search.substr(1);
+ }
+ } else if (parseQueryString) {
+ this.search = '';
+ this.query = {};
+ }
+ return this;
+ }
+ }
+
+ var proto = protocolPattern.exec(rest);
+ if (proto) {
+ proto = proto[0];
+ var lowerProto = proto.toLowerCase();
+ this.protocol = lowerProto;
+ rest = rest.substr(proto.length);
+ }
+
+ // figure out if it's got a host
+ // user@server is *always* interpreted as a hostname, and url
+ // resolution will treat //foo/bar as host=foo,path=bar because that's
+ // how the browser resolves relative URLs.
+ if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) {
+ var slashes = rest.substr(0, 2) === '//';
+ if (slashes && !(proto && hostlessProtocol[proto])) {
+ rest = rest.substr(2);
+ this.slashes = true;
+ }
+ }
+
+ if (!hostlessProtocol[proto] &&
+ (slashes || (proto && !slashedProtocol[proto]))) {
+
+ // there's a hostname.
+ // the first instance of /, ?, ;, or # ends the host.
+ //
+ // If there is an @ in the hostname, then non-host chars *are* allowed
+ // to the left of the last @ sign, unless some host-ending character
+ // comes *before* the @-sign.
+ // URLs are obnoxious.
+ //
+ // ex:
+ // http://a@b@c/ => user:a@b host:c
+ // http://a@b?@c => user:a host:c path:/?@c
+
+ // v0.12 TODO(isaacs): This is not quite how Chrome does things.
+ // Review our test case against browsers more comprehensively.
+
+ // find the first instance of any hostEndingChars
+ var hostEnd = -1;
+ for (var i = 0; i < hostEndingChars.length; i++) {
+ var hec = rest.indexOf(hostEndingChars[i]);
+ if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
+ { hostEnd = hec; }
+ }
+
+ // at this point, either we have an explicit point where the
+ // auth portion cannot go past, or the last @ char is the decider.
+ var auth, atSign;
+ if (hostEnd === -1) {
+ // atSign can be anywhere.
+ atSign = rest.lastIndexOf('@');
+ } else {
+ // atSign must be in auth portion.
+ // http://a@b/c@d => host:b auth:a path:/c@d
+ atSign = rest.lastIndexOf('@', hostEnd);
+ }
+
+ // Now we have a portion which is definitely the auth.
+ // Pull that off.
+ if (atSign !== -1) {
+ auth = rest.slice(0, atSign);
+ rest = rest.slice(atSign + 1);
+ this.auth = decodeURIComponent(auth);
+ }
+
+ // the host is the remaining to the left of the first non-host char
+ hostEnd = -1;
+ for (var i = 0; i < nonHostChars.length; i++) {
+ var hec = rest.indexOf(nonHostChars[i]);
+ if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
+ { hostEnd = hec; }
+ }
+ // if we still have not hit it, then the entire thing is a host.
+ if (hostEnd === -1)
+ { hostEnd = rest.length; }
+
+ this.host = rest.slice(0, hostEnd);
+ rest = rest.slice(hostEnd);
+
+ // pull out port.
+ this.parseHost();
+
+ // we've indicated that there is a hostname,
+ // so even if it's empty, it has to be present.
+ this.hostname = this.hostname || '';
+
+ // if hostname begins with [ and ends with ]
+ // assume that it's an IPv6 address.
+ var ipv6Hostname = this.hostname[0] === '[' &&
+ this.hostname[this.hostname.length - 1] === ']';
+
+ // validate a little.
+ if (!ipv6Hostname) {
+ var hostparts = this.hostname.split(/\./);
+ for (var i = 0, l = hostparts.length; i < l; i++) {
+ var part = hostparts[i];
+ if (!part) { continue; }
+ if (!part.match(hostnamePartPattern)) {
+ var newpart = '';
+ for (var j = 0, k = part.length; j < k; j++) {
+ if (part.charCodeAt(j) > 127) {
+ // we replace non-ASCII char with a temporary placeholder
+ // we need this to make sure size of hostname is not
+ // broken by replacing non-ASCII by nothing
+ newpart += 'x';
+ } else {
+ newpart += part[j];
+ }
+ }
+ // we test again with ASCII char only
+ if (!newpart.match(hostnamePartPattern)) {
+ var validParts = hostparts.slice(0, i);
+ var notHost = hostparts.slice(i + 1);
+ var bit = part.match(hostnamePartStart);
+ if (bit) {
+ validParts.push(bit[1]);
+ notHost.unshift(bit[2]);
+ }
+ if (notHost.length) {
+ rest = '/' + notHost.join('.') + rest;
+ }
+ this.hostname = validParts.join('.');
+ break;
+ }
+ }
+ }
+ }
+
+ if (this.hostname.length > hostnameMaxLen) {
+ this.hostname = '';
+ } else {
+ // hostnames are always lower case.
+ this.hostname = this.hostname.toLowerCase();
+ }
+
+ if (!ipv6Hostname) {
+ // IDNA Support: Returns a punycoded representation of "domain".
+ // It only converts parts of the domain name that
+ // have non-ASCII characters, i.e. it doesn't matter if
+ // you call it with a domain that already is ASCII-only.
+ this.hostname = punycode.toASCII(this.hostname);
+ }
+
+ var p = this.port ? ':' + this.port : '';
+ var h = this.hostname || '';
+ this.host = h + p;
+ this.href += this.host;
+
+ // strip [ and ] from the hostname
+ // the host field still retains them, though
+ if (ipv6Hostname) {
+ this.hostname = this.hostname.substr(1, this.hostname.length - 2);
+ if (rest[0] !== '/') {
+ rest = '/' + rest;
+ }
+ }
+ }
+
+ // now rest is set to the post-host stuff.
+ // chop off any delim chars.
+ if (!unsafeProtocol[lowerProto]) {
+
+ // First, make 100% sure that any "autoEscape" chars get
+ // escaped, even if encodeURIComponent doesn't think they
+ // need to be.
+ for (var i = 0, l = autoEscape.length; i < l; i++) {
+ var ae = autoEscape[i];
+ if (rest.indexOf(ae) === -1)
+ { continue; }
+ var esc = encodeURIComponent(ae);
+ if (esc === ae) {
+ esc = escape(ae);
+ }
+ rest = rest.split(ae).join(esc);
+ }
+ }
+
+
+ // chop off from the tail first.
+ var hash = rest.indexOf('#');
+ if (hash !== -1) {
+ // got a fragment string.
+ this.hash = rest.substr(hash);
+ rest = rest.slice(0, hash);
+ }
+ var qm = rest.indexOf('?');
+ if (qm !== -1) {
+ this.search = rest.substr(qm);
+ this.query = rest.substr(qm + 1);
+ if (parseQueryString) {
+ this.query = querystring.parse(this.query);
+ }
+ rest = rest.slice(0, qm);
+ } else if (parseQueryString) {
+ // no query string, but parseQueryString still requested
+ this.search = '';
+ this.query = {};
+ }
+ if (rest) { this.pathname = rest; }
+ if (slashedProtocol[lowerProto] &&
+ this.hostname && !this.pathname) {
+ this.pathname = '/';
+ }
+
+ //to support http.request
+ if (this.pathname || this.search) {
+ var p = this.pathname || '';
+ var s = this.search || '';
+ this.path = p + s;
+ }
+
+ // finally, reconstruct the href based on what has been validated.
+ this.href = this.format();
+ return this;
+ };
+
+ // format a parsed object into a url string
+ function urlFormat(obj) {
+ // ensure it's an object, and not a string url.
+ // If it's an obj, this is a no-op.
+ // this way, you can call url_format() on strings
+ // to clean up potentially wonky urls.
+ if (util.isString(obj)) { obj = urlParse(obj); }
+ if (!(obj instanceof Url)) { return Url.prototype.format.call(obj); }
+ return obj.format();
+ }
+
+ Url.prototype.format = function() {
+ var auth = this.auth || '';
+ if (auth) {
+ auth = encodeURIComponent(auth);
+ auth = auth.replace(/%3A/i, ':');
+ auth += '@';
+ }
+
+ var protocol = this.protocol || '',
+ pathname = this.pathname || '',
+ hash = this.hash || '',
+ host = false,
+ query = '';
+
+ if (this.host) {
+ host = auth + this.host;
+ } else if (this.hostname) {
+ host = auth + (this.hostname.indexOf(':') === -1 ?
+ this.hostname :
+ '[' + this.hostname + ']');
+ if (this.port) {
+ host += ':' + this.port;
+ }
+ }
+
+ if (this.query &&
+ util.isObject(this.query) &&
+ Object.keys(this.query).length) {
+ query = querystring.stringify(this.query);
+ }
+
+ var search = this.search || (query && ('?' + query)) || '';
+
+ if (protocol && protocol.substr(-1) !== ':') { protocol += ':'; }
+
+ // only the slashedProtocols get the //. Not mailto:, xmpp:, etc.
+ // unless they had them to begin with.
+ if (this.slashes ||
+ (!protocol || slashedProtocol[protocol]) && host !== false) {
+ host = '//' + (host || '');
+ if (pathname && pathname.charAt(0) !== '/') { pathname = '/' + pathname; }
+ } else if (!host) {
+ host = '';
+ }
+
+ if (hash && hash.charAt(0) !== '#') { hash = '#' + hash; }
+ if (search && search.charAt(0) !== '?') { search = '?' + search; }
+
+ pathname = pathname.replace(/[?#]/g, function(match) {
+ return encodeURIComponent(match);
+ });
+ search = search.replace('#', '%23');
+
+ return protocol + host + pathname + search + hash;
+ };
+
+ function urlResolve(source, relative) {
+ return urlParse(source, false, true).resolve(relative);
+ }
+
+ Url.prototype.resolve = function(relative) {
+ return this.resolveObject(urlParse(relative, false, true)).format();
+ };
+
+ function urlResolveObject(source, relative) {
+ if (!source) { return relative; }
+ return urlParse(source, false, true).resolveObject(relative);
+ }
+
+ Url.prototype.resolveObject = function(relative) {
+ if (util.isString(relative)) {
+ var rel = new Url();
+ rel.parse(relative, false, true);
+ relative = rel;
+ }
+
+ var result = new Url();
+ var tkeys = Object.keys(this);
+ for (var tk = 0; tk < tkeys.length; tk++) {
+ var tkey = tkeys[tk];
+ result[tkey] = this[tkey];
+ }
+
+ // hash is always overridden, no matter what.
+ // even href="" will remove it.
+ result.hash = relative.hash;
+
+ // if the relative url is empty, then there's nothing left to do here.
+ if (relative.href === '') {
+ result.href = result.format();
+ return result;
+ }
+
+ // hrefs like //foo/bar always cut to the protocol.
+ if (relative.slashes && !relative.protocol) {
+ // take everything except the protocol from relative
+ var rkeys = Object.keys(relative);
+ for (var rk = 0; rk < rkeys.length; rk++) {
+ var rkey = rkeys[rk];
+ if (rkey !== 'protocol')
+ { result[rkey] = relative[rkey]; }
+ }
+
+ //urlParse appends trailing / to urls like http://www.example.com
+ if (slashedProtocol[result.protocol] &&
+ result.hostname && !result.pathname) {
+ result.path = result.pathname = '/';
+ }
+
+ result.href = result.format();
+ return result;
+ }
+
+ if (relative.protocol && relative.protocol !== result.protocol) {
+ // if it's a known url protocol, then changing
+ // the protocol does weird things
+ // first, if it's not file:, then we MUST have a host,
+ // and if there was a path
+ // to begin with, then we MUST have a path.
+ // if it is file:, then the host is dropped,
+ // because that's known to be hostless.
+ // anything else is assumed to be absolute.
+ if (!slashedProtocol[relative.protocol]) {
+ var keys = Object.keys(relative);
+ for (var v = 0; v < keys.length; v++) {
+ var k = keys[v];
+ result[k] = relative[k];
+ }
+ result.href = result.format();
+ return result;
+ }
+
+ result.protocol = relative.protocol;
+ if (!relative.host && !hostlessProtocol[relative.protocol]) {
+ var relPath = (relative.pathname || '').split('/');
+ while (relPath.length && !(relative.host = relPath.shift())){ ; }
+ if (!relative.host) { relative.host = ''; }
+ if (!relative.hostname) { relative.hostname = ''; }
+ if (relPath[0] !== '') { relPath.unshift(''); }
+ if (relPath.length < 2) { relPath.unshift(''); }
+ result.pathname = relPath.join('/');
+ } else {
+ result.pathname = relative.pathname;
+ }
+ result.search = relative.search;
+ result.query = relative.query;
+ result.host = relative.host || '';
+ result.auth = relative.auth;
+ result.hostname = relative.hostname || relative.host;
+ result.port = relative.port;
+ // to support http.request
+ if (result.pathname || result.search) {
+ var p = result.pathname || '';
+ var s = result.search || '';
+ result.path = p + s;
+ }
+ result.slashes = result.slashes || relative.slashes;
+ result.href = result.format();
+ return result;
+ }
+
+ var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'),
+ isRelAbs = (
+ relative.host ||
+ relative.pathname && relative.pathname.charAt(0) === '/'
+ ),
+ mustEndAbs = (isRelAbs || isSourceAbs ||
+ (result.host && relative.pathname)),
+ removeAllDots = mustEndAbs,
+ srcPath = result.pathname && result.pathname.split('/') || [],
+ relPath = relative.pathname && relative.pathname.split('/') || [],
+ psychotic = result.protocol && !slashedProtocol[result.protocol];
+
+ // if the url is a non-slashed url, then relative
+ // links like ../.. should be able
+ // to crawl up to the hostname, as well. This is strange.
+ // result.protocol has already been set by now.
+ // Later on, put the first path part into the host field.
+ if (psychotic) {
+ result.hostname = '';
+ result.port = null;
+ if (result.host) {
+ if (srcPath[0] === '') { srcPath[0] = result.host; }
+ else { srcPath.unshift(result.host); }
+ }
+ result.host = '';
+ if (relative.protocol) {
+ relative.hostname = null;
+ relative.port = null;
+ if (relative.host) {
+ if (relPath[0] === '') { relPath[0] = relative.host; }
+ else { relPath.unshift(relative.host); }
+ }
+ relative.host = null;
+ }
+ mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === '');
+ }
+
+ if (isRelAbs) {
+ // it's absolute.
+ result.host = (relative.host || relative.host === '') ?
+ relative.host : result.host;
+ result.hostname = (relative.hostname || relative.hostname === '') ?
+ relative.hostname : result.hostname;
+ result.search = relative.search;
+ result.query = relative.query;
+ srcPath = relPath;
+ // fall through to the dot-handling below.
+ } else if (relPath.length) {
+ // it's relative
+ // throw away the existing file, and take the new path instead.
+ if (!srcPath) { srcPath = []; }
+ srcPath.pop();
+ srcPath = srcPath.concat(relPath);
+ result.search = relative.search;
+ result.query = relative.query;
+ } else if (!util.isNullOrUndefined(relative.search)) {
+ // just pull out the search.
+ // like href='?foo'.
+ // Put this after the other two cases because it simplifies the booleans
+ if (psychotic) {
+ result.hostname = result.host = srcPath.shift();
+ //occationaly the auth can get stuck only in host
+ //this especially happens in cases like
+ //url.resolveObject('mailto:local1@domain1', 'local2@domain2')
+ var authInHost = result.host && result.host.indexOf('@') > 0 ?
+ result.host.split('@') : false;
+ if (authInHost) {
+ result.auth = authInHost.shift();
+ result.host = result.hostname = authInHost.shift();
+ }
+ }
+ result.search = relative.search;
+ result.query = relative.query;
+ //to support http.request
+ if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
+ result.path = (result.pathname ? result.pathname : '') +
+ (result.search ? result.search : '');
+ }
+ result.href = result.format();
+ return result;
+ }
+
+ if (!srcPath.length) {
+ // no path at all. easy.
+ // we've already handled the other stuff above.
+ result.pathname = null;
+ //to support http.request
+ if (result.search) {
+ result.path = '/' + result.search;
+ } else {
+ result.path = null;
+ }
+ result.href = result.format();
+ return result;
+ }
+
+ // if a url ENDs in . or .., then it must get a trailing slash.
+ // however, if it ends in anything else non-slashy,
+ // then it must NOT get a trailing slash.
+ var last = srcPath.slice(-1)[0];
+ var hasTrailingSlash = (
+ (result.host || relative.host || srcPath.length > 1) &&
+ (last === '.' || last === '..') || last === '');
+
+ // strip single dots, resolve double dots to parent dir
+ // if the path tries to go above the root, `up` ends up > 0
+ var up = 0;
+ for (var i = srcPath.length; i >= 0; i--) {
+ last = srcPath[i];
+ if (last === '.') {
+ srcPath.splice(i, 1);
+ } else if (last === '..') {
+ srcPath.splice(i, 1);
+ up++;
+ } else if (up) {
+ srcPath.splice(i, 1);
+ up--;
+ }
+ }
+
+ // if the path is allowed to go above the root, restore leading ..s
+ if (!mustEndAbs && !removeAllDots) {
+ for (; up--; up) {
+ srcPath.unshift('..');
+ }
+ }
+
+ if (mustEndAbs && srcPath[0] !== '' &&
+ (!srcPath[0] || srcPath[0].charAt(0) !== '/')) {
+ srcPath.unshift('');
+ }
+
+ if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) {
+ srcPath.push('');
+ }
+
+ var isAbsolute = srcPath[0] === '' ||
+ (srcPath[0] && srcPath[0].charAt(0) === '/');
+
+ // put the host back
+ if (psychotic) {
+ result.hostname = result.host = isAbsolute ? '' :
+ srcPath.length ? srcPath.shift() : '';
+ //occationaly the auth can get stuck only in host
+ //this especially happens in cases like
+ //url.resolveObject('mailto:local1@domain1', 'local2@domain2')
+ var authInHost = result.host && result.host.indexOf('@') > 0 ?
+ result.host.split('@') : false;
+ if (authInHost) {
+ result.auth = authInHost.shift();
+ result.host = result.hostname = authInHost.shift();
+ }
+ }
+
+ mustEndAbs = mustEndAbs || (result.host && srcPath.length);
+
+ if (mustEndAbs && !isAbsolute) {
+ srcPath.unshift('');
+ }
+
+ if (!srcPath.length) {
+ result.pathname = null;
+ result.path = null;
+ } else {
+ result.pathname = srcPath.join('/');
+ }
+
+ //to support request.http
+ if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
+ result.path = (result.pathname ? result.pathname : '') +
+ (result.search ? result.search : '');
+ }
+ result.auth = relative.auth || result.auth;
+ result.slashes = result.slashes || relative.slashes;
+ result.href = result.format();
+ return result;
+ };
+
+ Url.prototype.parseHost = function() {
+ var host = this.host;
+ var port = portPattern.exec(host);
+ if (port) {
+ port = port[0];
+ if (port !== ':') {
+ this.port = port.substr(1);
+ }
+ host = host.substr(0, host.length - port.length);
+ }
+ if (host) { this.hostname = host; }
+ };
+
+ var url$1 = {
+ parse: parse,
+ resolve: resolve,
+ resolveObject: resolveObject,
+ format: format,
+ Url: Url_1
+ };
+
+ /*!
+ * @pixi/constants - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/constants is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+ /**
+ * Different types of environments for WebGL.
+ * @static
+ * @memberof PIXI
+ * @name ENV
+ * @enum {number}
+ * @property {number} WEBGL_LEGACY - Used for older v1 WebGL devices. PixiJS will aim to ensure compatibility
+ * with older / less advanced devices. If you experience unexplained flickering prefer this environment.
+ * @property {number} WEBGL - Version 1 of WebGL
+ * @property {number} WEBGL2 - Version 2 of WebGL
+ */
+ exports.ENV = void 0;
+ (function (ENV) {
+ ENV[ENV["WEBGL_LEGACY"] = 0] = "WEBGL_LEGACY";
+ ENV[ENV["WEBGL"] = 1] = "WEBGL";
+ ENV[ENV["WEBGL2"] = 2] = "WEBGL2";
+ })(exports.ENV || (exports.ENV = {}));
+ /**
+ * Constant to identify the Renderer Type.
+ * @static
+ * @memberof PIXI
+ * @name RENDERER_TYPE
+ * @enum {number}
+ * @property {number} UNKNOWN - Unknown render type.
+ * @property {number} WEBGL - WebGL render type.
+ * @property {number} CANVAS - Canvas render type.
+ */
+ exports.RENDERER_TYPE = void 0;
+ (function (RENDERER_TYPE) {
+ RENDERER_TYPE[RENDERER_TYPE["UNKNOWN"] = 0] = "UNKNOWN";
+ RENDERER_TYPE[RENDERER_TYPE["WEBGL"] = 1] = "WEBGL";
+ RENDERER_TYPE[RENDERER_TYPE["CANVAS"] = 2] = "CANVAS";
+ })(exports.RENDERER_TYPE || (exports.RENDERER_TYPE = {}));
+ /**
+ * Bitwise OR of masks that indicate the buffers to be cleared.
+ * @static
+ * @memberof PIXI
+ * @name BUFFER_BITS
+ * @enum {number}
+ * @property {number} COLOR - Indicates the buffers currently enabled for color writing.
+ * @property {number} DEPTH - Indicates the depth buffer.
+ * @property {number} STENCIL - Indicates the stencil buffer.
+ */
+ exports.BUFFER_BITS = void 0;
+ (function (BUFFER_BITS) {
+ BUFFER_BITS[BUFFER_BITS["COLOR"] = 16384] = "COLOR";
+ BUFFER_BITS[BUFFER_BITS["DEPTH"] = 256] = "DEPTH";
+ BUFFER_BITS[BUFFER_BITS["STENCIL"] = 1024] = "STENCIL";
+ })(exports.BUFFER_BITS || (exports.BUFFER_BITS = {}));
+ /**
+ * Various blend modes supported by PIXI.
+ *
+ * IMPORTANT - The WebGL renderer only supports the NORMAL, ADD, MULTIPLY and SCREEN blend modes.
+ * Anything else will silently act like NORMAL.
+ * @memberof PIXI
+ * @name BLEND_MODES
+ * @enum {number}
+ * @property {number} NORMAL -
+ * @property {number} ADD -
+ * @property {number} MULTIPLY -
+ * @property {number} SCREEN -
+ * @property {number} OVERLAY -
+ * @property {number} DARKEN -
+ * @property {number} LIGHTEN -
+ * @property {number} COLOR_DODGE -
+ * @property {number} COLOR_BURN -
+ * @property {number} HARD_LIGHT -
+ * @property {number} SOFT_LIGHT -
+ * @property {number} DIFFERENCE -
+ * @property {number} EXCLUSION -
+ * @property {number} HUE -
+ * @property {number} SATURATION -
+ * @property {number} COLOR -
+ * @property {number} LUMINOSITY -
+ * @property {number} NORMAL_NPM -
+ * @property {number} ADD_NPM -
+ * @property {number} SCREEN_NPM -
+ * @property {number} NONE -
+ * @property {number} SRC_IN -
+ * @property {number} SRC_OUT -
+ * @property {number} SRC_ATOP -
+ * @property {number} DST_OVER -
+ * @property {number} DST_IN -
+ * @property {number} DST_OUT -
+ * @property {number} DST_ATOP -
+ * @property {number} SUBTRACT -
+ * @property {number} SRC_OVER -
+ * @property {number} ERASE -
+ * @property {number} XOR -
+ */
+ exports.BLEND_MODES = void 0;
+ (function (BLEND_MODES) {
+ BLEND_MODES[BLEND_MODES["NORMAL"] = 0] = "NORMAL";
+ BLEND_MODES[BLEND_MODES["ADD"] = 1] = "ADD";
+ BLEND_MODES[BLEND_MODES["MULTIPLY"] = 2] = "MULTIPLY";
+ BLEND_MODES[BLEND_MODES["SCREEN"] = 3] = "SCREEN";
+ BLEND_MODES[BLEND_MODES["OVERLAY"] = 4] = "OVERLAY";
+ BLEND_MODES[BLEND_MODES["DARKEN"] = 5] = "DARKEN";
+ BLEND_MODES[BLEND_MODES["LIGHTEN"] = 6] = "LIGHTEN";
+ BLEND_MODES[BLEND_MODES["COLOR_DODGE"] = 7] = "COLOR_DODGE";
+ BLEND_MODES[BLEND_MODES["COLOR_BURN"] = 8] = "COLOR_BURN";
+ BLEND_MODES[BLEND_MODES["HARD_LIGHT"] = 9] = "HARD_LIGHT";
+ BLEND_MODES[BLEND_MODES["SOFT_LIGHT"] = 10] = "SOFT_LIGHT";
+ BLEND_MODES[BLEND_MODES["DIFFERENCE"] = 11] = "DIFFERENCE";
+ BLEND_MODES[BLEND_MODES["EXCLUSION"] = 12] = "EXCLUSION";
+ BLEND_MODES[BLEND_MODES["HUE"] = 13] = "HUE";
+ BLEND_MODES[BLEND_MODES["SATURATION"] = 14] = "SATURATION";
+ BLEND_MODES[BLEND_MODES["COLOR"] = 15] = "COLOR";
+ BLEND_MODES[BLEND_MODES["LUMINOSITY"] = 16] = "LUMINOSITY";
+ BLEND_MODES[BLEND_MODES["NORMAL_NPM"] = 17] = "NORMAL_NPM";
+ BLEND_MODES[BLEND_MODES["ADD_NPM"] = 18] = "ADD_NPM";
+ BLEND_MODES[BLEND_MODES["SCREEN_NPM"] = 19] = "SCREEN_NPM";
+ BLEND_MODES[BLEND_MODES["NONE"] = 20] = "NONE";
+ BLEND_MODES[BLEND_MODES["SRC_OVER"] = 0] = "SRC_OVER";
+ BLEND_MODES[BLEND_MODES["SRC_IN"] = 21] = "SRC_IN";
+ BLEND_MODES[BLEND_MODES["SRC_OUT"] = 22] = "SRC_OUT";
+ BLEND_MODES[BLEND_MODES["SRC_ATOP"] = 23] = "SRC_ATOP";
+ BLEND_MODES[BLEND_MODES["DST_OVER"] = 24] = "DST_OVER";
+ BLEND_MODES[BLEND_MODES["DST_IN"] = 25] = "DST_IN";
+ BLEND_MODES[BLEND_MODES["DST_OUT"] = 26] = "DST_OUT";
+ BLEND_MODES[BLEND_MODES["DST_ATOP"] = 27] = "DST_ATOP";
+ BLEND_MODES[BLEND_MODES["ERASE"] = 26] = "ERASE";
+ BLEND_MODES[BLEND_MODES["SUBTRACT"] = 28] = "SUBTRACT";
+ BLEND_MODES[BLEND_MODES["XOR"] = 29] = "XOR";
+ })(exports.BLEND_MODES || (exports.BLEND_MODES = {}));
+ /**
+ * Various webgl draw modes. These can be used to specify which GL drawMode to use
+ * under certain situations and renderers.
+ * @memberof PIXI
+ * @static
+ * @name DRAW_MODES
+ * @enum {number}
+ * @property {number} POINTS -
+ * @property {number} LINES -
+ * @property {number} LINE_LOOP -
+ * @property {number} LINE_STRIP -
+ * @property {number} TRIANGLES -
+ * @property {number} TRIANGLE_STRIP -
+ * @property {number} TRIANGLE_FAN -
+ */
+ exports.DRAW_MODES = void 0;
+ (function (DRAW_MODES) {
+ DRAW_MODES[DRAW_MODES["POINTS"] = 0] = "POINTS";
+ DRAW_MODES[DRAW_MODES["LINES"] = 1] = "LINES";
+ DRAW_MODES[DRAW_MODES["LINE_LOOP"] = 2] = "LINE_LOOP";
+ DRAW_MODES[DRAW_MODES["LINE_STRIP"] = 3] = "LINE_STRIP";
+ DRAW_MODES[DRAW_MODES["TRIANGLES"] = 4] = "TRIANGLES";
+ DRAW_MODES[DRAW_MODES["TRIANGLE_STRIP"] = 5] = "TRIANGLE_STRIP";
+ DRAW_MODES[DRAW_MODES["TRIANGLE_FAN"] = 6] = "TRIANGLE_FAN";
+ })(exports.DRAW_MODES || (exports.DRAW_MODES = {}));
+ /**
+ * Various GL texture/resources formats.
+ * @memberof PIXI
+ * @static
+ * @name FORMATS
+ * @enum {number}
+ * @property {number} [RGBA=6408] -
+ * @property {number} [RGB=6407] -
+ * @property {number} [RG=33319] -
+ * @property {number} [RED=6403] -
+ * @property {number} [RGBA_INTEGER=36249] -
+ * @property {number} [RGB_INTEGER=36248] -
+ * @property {number} [RG_INTEGER=33320] -
+ * @property {number} [RED_INTEGER=36244] -
+ * @property {number} [ALPHA=6406] -
+ * @property {number} [LUMINANCE=6409] -
+ * @property {number} [LUMINANCE_ALPHA=6410] -
+ * @property {number} [DEPTH_COMPONENT=6402] -
+ * @property {number} [DEPTH_STENCIL=34041] -
+ */
+ exports.FORMATS = void 0;
+ (function (FORMATS) {
+ FORMATS[FORMATS["RGBA"] = 6408] = "RGBA";
+ FORMATS[FORMATS["RGB"] = 6407] = "RGB";
+ FORMATS[FORMATS["RG"] = 33319] = "RG";
+ FORMATS[FORMATS["RED"] = 6403] = "RED";
+ FORMATS[FORMATS["RGBA_INTEGER"] = 36249] = "RGBA_INTEGER";
+ FORMATS[FORMATS["RGB_INTEGER"] = 36248] = "RGB_INTEGER";
+ FORMATS[FORMATS["RG_INTEGER"] = 33320] = "RG_INTEGER";
+ FORMATS[FORMATS["RED_INTEGER"] = 36244] = "RED_INTEGER";
+ FORMATS[FORMATS["ALPHA"] = 6406] = "ALPHA";
+ FORMATS[FORMATS["LUMINANCE"] = 6409] = "LUMINANCE";
+ FORMATS[FORMATS["LUMINANCE_ALPHA"] = 6410] = "LUMINANCE_ALPHA";
+ FORMATS[FORMATS["DEPTH_COMPONENT"] = 6402] = "DEPTH_COMPONENT";
+ FORMATS[FORMATS["DEPTH_STENCIL"] = 34041] = "DEPTH_STENCIL";
+ })(exports.FORMATS || (exports.FORMATS = {}));
+ /**
+ * Various GL target types.
+ * @memberof PIXI
+ * @static
+ * @name TARGETS
+ * @enum {number}
+ * @property {number} [TEXTURE_2D=3553] -
+ * @property {number} [TEXTURE_CUBE_MAP=34067] -
+ * @property {number} [TEXTURE_2D_ARRAY=35866] -
+ * @property {number} [TEXTURE_CUBE_MAP_POSITIVE_X=34069] -
+ * @property {number} [TEXTURE_CUBE_MAP_NEGATIVE_X=34070] -
+ * @property {number} [TEXTURE_CUBE_MAP_POSITIVE_Y=34071] -
+ * @property {number} [TEXTURE_CUBE_MAP_NEGATIVE_Y=34072] -
+ * @property {number} [TEXTURE_CUBE_MAP_POSITIVE_Z=34073] -
+ * @property {number} [TEXTURE_CUBE_MAP_NEGATIVE_Z=34074] -
+ */
+ exports.TARGETS = void 0;
+ (function (TARGETS) {
+ TARGETS[TARGETS["TEXTURE_2D"] = 3553] = "TEXTURE_2D";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP"] = 34067] = "TEXTURE_CUBE_MAP";
+ TARGETS[TARGETS["TEXTURE_2D_ARRAY"] = 35866] = "TEXTURE_2D_ARRAY";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_POSITIVE_X"] = 34069] = "TEXTURE_CUBE_MAP_POSITIVE_X";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_NEGATIVE_X"] = 34070] = "TEXTURE_CUBE_MAP_NEGATIVE_X";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_POSITIVE_Y"] = 34071] = "TEXTURE_CUBE_MAP_POSITIVE_Y";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_NEGATIVE_Y"] = 34072] = "TEXTURE_CUBE_MAP_NEGATIVE_Y";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_POSITIVE_Z"] = 34073] = "TEXTURE_CUBE_MAP_POSITIVE_Z";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_NEGATIVE_Z"] = 34074] = "TEXTURE_CUBE_MAP_NEGATIVE_Z";
+ })(exports.TARGETS || (exports.TARGETS = {}));
+ /**
+ * Various GL data format types.
+ * @memberof PIXI
+ * @static
+ * @name TYPES
+ * @enum {number}
+ * @property {number} [UNSIGNED_BYTE=5121] -
+ * @property {number} [UNSIGNED_SHORT=5123] -
+ * @property {number} [UNSIGNED_SHORT_5_6_5=33635] -
+ * @property {number} [UNSIGNED_SHORT_4_4_4_4=32819] -
+ * @property {number} [UNSIGNED_SHORT_5_5_5_1=32820] -
+ * @property {number} [UNSIGNED_INT=5125] -
+ * @property {number} [UNSIGNED_INT_10F_11F_11F_REV=35899] -
+ * @property {number} [UNSIGNED_INT_2_10_10_10_REV=33640] -
+ * @property {number} [UNSIGNED_INT_24_8=34042] -
+ * @property {number} [UNSIGNED_INT_5_9_9_9_REV=35902] -
+ * @property {number} [BYTE=5120] -
+ * @property {number} [SHORT=5122] -
+ * @property {number} [INT=5124] -
+ * @property {number} [FLOAT=5126] -
+ * @property {number} [FLOAT_32_UNSIGNED_INT_24_8_REV=36269] -
+ * @property {number} [HALF_FLOAT=36193] -
+ */
+ exports.TYPES = void 0;
+ (function (TYPES) {
+ TYPES[TYPES["UNSIGNED_BYTE"] = 5121] = "UNSIGNED_BYTE";
+ TYPES[TYPES["UNSIGNED_SHORT"] = 5123] = "UNSIGNED_SHORT";
+ TYPES[TYPES["UNSIGNED_SHORT_5_6_5"] = 33635] = "UNSIGNED_SHORT_5_6_5";
+ TYPES[TYPES["UNSIGNED_SHORT_4_4_4_4"] = 32819] = "UNSIGNED_SHORT_4_4_4_4";
+ TYPES[TYPES["UNSIGNED_SHORT_5_5_5_1"] = 32820] = "UNSIGNED_SHORT_5_5_5_1";
+ TYPES[TYPES["UNSIGNED_INT"] = 5125] = "UNSIGNED_INT";
+ TYPES[TYPES["UNSIGNED_INT_10F_11F_11F_REV"] = 35899] = "UNSIGNED_INT_10F_11F_11F_REV";
+ TYPES[TYPES["UNSIGNED_INT_2_10_10_10_REV"] = 33640] = "UNSIGNED_INT_2_10_10_10_REV";
+ TYPES[TYPES["UNSIGNED_INT_24_8"] = 34042] = "UNSIGNED_INT_24_8";
+ TYPES[TYPES["UNSIGNED_INT_5_9_9_9_REV"] = 35902] = "UNSIGNED_INT_5_9_9_9_REV";
+ TYPES[TYPES["BYTE"] = 5120] = "BYTE";
+ TYPES[TYPES["SHORT"] = 5122] = "SHORT";
+ TYPES[TYPES["INT"] = 5124] = "INT";
+ TYPES[TYPES["FLOAT"] = 5126] = "FLOAT";
+ TYPES[TYPES["FLOAT_32_UNSIGNED_INT_24_8_REV"] = 36269] = "FLOAT_32_UNSIGNED_INT_24_8_REV";
+ TYPES[TYPES["HALF_FLOAT"] = 36193] = "HALF_FLOAT";
+ })(exports.TYPES || (exports.TYPES = {}));
+ /**
+ * Various sampler types. Correspond to `sampler`, `isampler`, `usampler` GLSL types respectively.
+ * WebGL1 works only with FLOAT.
+ * @memberof PIXI
+ * @static
+ * @name SAMPLER_TYPES
+ * @enum {number}
+ * @property {number} [FLOAT=0] -
+ * @property {number} [INT=1] -
+ * @property {number} [UINT=2] -
+ */
+ exports.SAMPLER_TYPES = void 0;
+ (function (SAMPLER_TYPES) {
+ SAMPLER_TYPES[SAMPLER_TYPES["FLOAT"] = 0] = "FLOAT";
+ SAMPLER_TYPES[SAMPLER_TYPES["INT"] = 1] = "INT";
+ SAMPLER_TYPES[SAMPLER_TYPES["UINT"] = 2] = "UINT";
+ })(exports.SAMPLER_TYPES || (exports.SAMPLER_TYPES = {}));
+ /**
+ * The scale modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.SCALE_MODE} scale mode affects the default scaling mode of future operations.
+ * It can be re-assigned to either LINEAR or NEAREST, depending upon suitability.
+ * @memberof PIXI
+ * @static
+ * @name SCALE_MODES
+ * @enum {number}
+ * @property {number} LINEAR Smooth scaling
+ * @property {number} NEAREST Pixelating scaling
+ */
+ exports.SCALE_MODES = void 0;
+ (function (SCALE_MODES) {
+ SCALE_MODES[SCALE_MODES["NEAREST"] = 0] = "NEAREST";
+ SCALE_MODES[SCALE_MODES["LINEAR"] = 1] = "LINEAR";
+ })(exports.SCALE_MODES || (exports.SCALE_MODES = {}));
+ /**
+ * The wrap modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.WRAP_MODE} wrap mode affects the default wrapping mode of future operations.
+ * It can be re-assigned to either CLAMP or REPEAT, depending upon suitability.
+ * If the texture is non power of two then clamp will be used regardless as WebGL can
+ * only use REPEAT if the texture is po2.
+ *
+ * This property only affects WebGL.
+ * @name WRAP_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} CLAMP - The textures uvs are clamped
+ * @property {number} REPEAT - The texture uvs tile and repeat
+ * @property {number} MIRRORED_REPEAT - The texture uvs tile and repeat with mirroring
+ */
+ exports.WRAP_MODES = void 0;
+ (function (WRAP_MODES) {
+ WRAP_MODES[WRAP_MODES["CLAMP"] = 33071] = "CLAMP";
+ WRAP_MODES[WRAP_MODES["REPEAT"] = 10497] = "REPEAT";
+ WRAP_MODES[WRAP_MODES["MIRRORED_REPEAT"] = 33648] = "MIRRORED_REPEAT";
+ })(exports.WRAP_MODES || (exports.WRAP_MODES = {}));
+ /**
+ * Mipmap filtering modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.MIPMAP_TEXTURES} affects default texture filtering.
+ * Mipmaps are generated for a baseTexture if its `mipmap` field is `ON`,
+ * or its `POW2` and texture dimensions are powers of 2.
+ * Due to platform restriction, `ON` option will work like `POW2` for webgl-1.
+ *
+ * This property only affects WebGL.
+ * @name MIPMAP_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} OFF - No mipmaps
+ * @property {number} POW2 - Generate mipmaps if texture dimensions are pow2
+ * @property {number} ON - Always generate mipmaps
+ * @property {number} ON_MANUAL - Use mipmaps, but do not auto-generate them; this is used with a resource
+ * that supports buffering each level-of-detail.
+ */
+ exports.MIPMAP_MODES = void 0;
+ (function (MIPMAP_MODES) {
+ MIPMAP_MODES[MIPMAP_MODES["OFF"] = 0] = "OFF";
+ MIPMAP_MODES[MIPMAP_MODES["POW2"] = 1] = "POW2";
+ MIPMAP_MODES[MIPMAP_MODES["ON"] = 2] = "ON";
+ MIPMAP_MODES[MIPMAP_MODES["ON_MANUAL"] = 3] = "ON_MANUAL";
+ })(exports.MIPMAP_MODES || (exports.MIPMAP_MODES = {}));
+ /**
+ * How to treat textures with premultiplied alpha
+ * @name ALPHA_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} NO_PREMULTIPLIED_ALPHA - Source is not premultiplied, leave it like that.
+ * Option for compressed and data textures that are created from typed arrays.
+ * @property {number} PREMULTIPLY_ON_UPLOAD - Source is not premultiplied, premultiply on upload.
+ * Default option, used for all loaded images.
+ * @property {number} PREMULTIPLIED_ALPHA - Source is already premultiplied
+ * Example: spine atlases with `_pma` suffix.
+ * @property {number} NPM - Alias for NO_PREMULTIPLIED_ALPHA.
+ * @property {number} UNPACK - Default option, alias for PREMULTIPLY_ON_UPLOAD.
+ * @property {number} PMA - Alias for PREMULTIPLIED_ALPHA.
+ */
+ exports.ALPHA_MODES = void 0;
+ (function (ALPHA_MODES) {
+ ALPHA_MODES[ALPHA_MODES["NPM"] = 0] = "NPM";
+ ALPHA_MODES[ALPHA_MODES["UNPACK"] = 1] = "UNPACK";
+ ALPHA_MODES[ALPHA_MODES["PMA"] = 2] = "PMA";
+ ALPHA_MODES[ALPHA_MODES["NO_PREMULTIPLIED_ALPHA"] = 0] = "NO_PREMULTIPLIED_ALPHA";
+ ALPHA_MODES[ALPHA_MODES["PREMULTIPLY_ON_UPLOAD"] = 1] = "PREMULTIPLY_ON_UPLOAD";
+ ALPHA_MODES[ALPHA_MODES["PREMULTIPLY_ALPHA"] = 2] = "PREMULTIPLY_ALPHA";
+ ALPHA_MODES[ALPHA_MODES["PREMULTIPLIED_ALPHA"] = 2] = "PREMULTIPLIED_ALPHA";
+ })(exports.ALPHA_MODES || (exports.ALPHA_MODES = {}));
+ /**
+ * Configure whether filter textures are cleared after binding.
+ *
+ * Filter textures need not be cleared if the filter does not use pixel blending. {@link CLEAR_MODES.BLIT} will detect
+ * this and skip clearing as an optimization.
+ * @name CLEAR_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} BLEND - Do not clear the filter texture. The filter's output will blend on top of the output texture.
+ * @property {number} CLEAR - Always clear the filter texture.
+ * @property {number} BLIT - Clear only if {@link FilterSystem.forceClear} is set or if the filter uses pixel blending.
+ * @property {number} NO - Alias for BLEND, same as `false` in earlier versions
+ * @property {number} YES - Alias for CLEAR, same as `true` in earlier versions
+ * @property {number} AUTO - Alias for BLIT
+ */
+ exports.CLEAR_MODES = void 0;
+ (function (CLEAR_MODES) {
+ CLEAR_MODES[CLEAR_MODES["NO"] = 0] = "NO";
+ CLEAR_MODES[CLEAR_MODES["YES"] = 1] = "YES";
+ CLEAR_MODES[CLEAR_MODES["AUTO"] = 2] = "AUTO";
+ CLEAR_MODES[CLEAR_MODES["BLEND"] = 0] = "BLEND";
+ CLEAR_MODES[CLEAR_MODES["CLEAR"] = 1] = "CLEAR";
+ CLEAR_MODES[CLEAR_MODES["BLIT"] = 2] = "BLIT";
+ })(exports.CLEAR_MODES || (exports.CLEAR_MODES = {}));
+ /**
+ * The gc modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.GC_MODE} Garbage Collection mode for PixiJS textures is AUTO
+ * If set to GC_MODE, the renderer will occasionally check textures usage. If they are not
+ * used for a specified period of time they will be removed from the GPU. They will of course
+ * be uploaded again when they are required. This is a silent behind the scenes process that
+ * should ensure that the GPU does not get filled up.
+ *
+ * Handy for mobile devices!
+ * This property only affects WebGL.
+ * @name GC_MODES
+ * @enum {number}
+ * @static
+ * @memberof PIXI
+ * @property {number} AUTO - Garbage collection will happen periodically automatically
+ * @property {number} MANUAL - Garbage collection will need to be called manually
+ */
+ exports.GC_MODES = void 0;
+ (function (GC_MODES) {
+ GC_MODES[GC_MODES["AUTO"] = 0] = "AUTO";
+ GC_MODES[GC_MODES["MANUAL"] = 1] = "MANUAL";
+ })(exports.GC_MODES || (exports.GC_MODES = {}));
+ /**
+ * Constants that specify float precision in shaders.
+ * @name PRECISION
+ * @memberof PIXI
+ * @constant
+ * @static
+ * @enum {string}
+ * @property {string} [LOW='lowp'] -
+ * @property {string} [MEDIUM='mediump'] -
+ * @property {string} [HIGH='highp'] -
+ */
+ exports.PRECISION = void 0;
+ (function (PRECISION) {
+ PRECISION["LOW"] = "lowp";
+ PRECISION["MEDIUM"] = "mediump";
+ PRECISION["HIGH"] = "highp";
+ })(exports.PRECISION || (exports.PRECISION = {}));
+ /**
+ * Constants for mask implementations.
+ * We use `type` suffix because it leads to very different behaviours
+ * @name MASK_TYPES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} NONE - Mask is ignored
+ * @property {number} SCISSOR - Scissor mask, rectangle on screen, cheap
+ * @property {number} STENCIL - Stencil mask, 1-bit, medium, works only if renderer supports stencil
+ * @property {number} SPRITE - Mask that uses SpriteMaskFilter, uses temporary RenderTexture
+ * @property {number} COLOR - Color mask (RGBA)
+ */
+ exports.MASK_TYPES = void 0;
+ (function (MASK_TYPES) {
+ MASK_TYPES[MASK_TYPES["NONE"] = 0] = "NONE";
+ MASK_TYPES[MASK_TYPES["SCISSOR"] = 1] = "SCISSOR";
+ MASK_TYPES[MASK_TYPES["STENCIL"] = 2] = "STENCIL";
+ MASK_TYPES[MASK_TYPES["SPRITE"] = 3] = "SPRITE";
+ MASK_TYPES[MASK_TYPES["COLOR"] = 4] = "COLOR";
+ })(exports.MASK_TYPES || (exports.MASK_TYPES = {}));
+ /**
+ * Bitwise OR of masks that indicate the color channels that are rendered to.
+ * @static
+ * @memberof PIXI
+ * @name COLOR_MASK_BITS
+ * @enum {number}
+ * @property {number} RED - Red channel.
+ * @property {number} GREEN - Green channel
+ * @property {number} BLUE - Blue channel.
+ * @property {number} ALPHA - Alpha channel.
+ */
+ exports.COLOR_MASK_BITS = void 0;
+ (function (COLOR_MASK_BITS) {
+ COLOR_MASK_BITS[COLOR_MASK_BITS["RED"] = 1] = "RED";
+ COLOR_MASK_BITS[COLOR_MASK_BITS["GREEN"] = 2] = "GREEN";
+ COLOR_MASK_BITS[COLOR_MASK_BITS["BLUE"] = 4] = "BLUE";
+ COLOR_MASK_BITS[COLOR_MASK_BITS["ALPHA"] = 8] = "ALPHA";
+ })(exports.COLOR_MASK_BITS || (exports.COLOR_MASK_BITS = {}));
+ /**
+ * Constants for multi-sampling antialiasing.
+ * @see PIXI.Framebuffer#multisample
+ * @name MSAA_QUALITY
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} NONE - No multisampling for this renderTexture
+ * @property {number} LOW - Try 2 samples
+ * @property {number} MEDIUM - Try 4 samples
+ * @property {number} HIGH - Try 8 samples
+ */
+ exports.MSAA_QUALITY = void 0;
+ (function (MSAA_QUALITY) {
+ MSAA_QUALITY[MSAA_QUALITY["NONE"] = 0] = "NONE";
+ MSAA_QUALITY[MSAA_QUALITY["LOW"] = 2] = "LOW";
+ MSAA_QUALITY[MSAA_QUALITY["MEDIUM"] = 4] = "MEDIUM";
+ MSAA_QUALITY[MSAA_QUALITY["HIGH"] = 8] = "HIGH";
+ })(exports.MSAA_QUALITY || (exports.MSAA_QUALITY = {}));
+ /**
+ * Constants for various buffer types in Pixi
+ * @see PIXI.BUFFER_TYPE
+ * @name BUFFER_TYPE
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} ELEMENT_ARRAY_BUFFER - buffer type for using as an index buffer
+ * @property {number} ARRAY_BUFFER - buffer type for using attribute data
+ * @property {number} UNIFORM_BUFFER - the buffer type is for uniform buffer objects
+ */
+ exports.BUFFER_TYPE = void 0;
+ (function (BUFFER_TYPE) {
+ BUFFER_TYPE[BUFFER_TYPE["ELEMENT_ARRAY_BUFFER"] = 34963] = "ELEMENT_ARRAY_BUFFER";
+ BUFFER_TYPE[BUFFER_TYPE["ARRAY_BUFFER"] = 34962] = "ARRAY_BUFFER";
+ // NOT YET SUPPORTED
+ BUFFER_TYPE[BUFFER_TYPE["UNIFORM_BUFFER"] = 35345] = "UNIFORM_BUFFER";
+ })(exports.BUFFER_TYPE || (exports.BUFFER_TYPE = {}));
+
+ /*!
+ * @pixi/utils - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/utils is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /**
+ * This file contains redeclared types for Node `url` and `querystring` modules. These modules
+ * don't provide their own typings but instead are a part of the full Node typings. The purpose of
+ * this file is to redeclare the required types to avoid having the whole Node types as a
+ * dependency.
+ */
+ var url = {
+ parse: parse,
+ format: format,
+ resolve: resolve,
+ };
+
+ function assertPath(path) {
+ if (typeof path !== 'string') {
+ throw new TypeError("Path must be a string. Received " + JSON.stringify(path));
+ }
+ }
+ function removeUrlParams(url) {
+ var re = url.split('?')[0];
+ return re.split('#')[0];
+ }
+ function escapeRegExp(string) {
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
+ }
+ function replaceAll(str, find, replace) {
+ return str.replace(new RegExp(escapeRegExp(find), 'g'), replace);
+ }
+ // Resolves . and .. elements in a path with directory names
+ function normalizeStringPosix(path, allowAboveRoot) {
+ var res = '';
+ var lastSegmentLength = 0;
+ var lastSlash = -1;
+ var dots = 0;
+ var code;
+ for (var i = 0; i <= path.length; ++i) {
+ if (i < path.length) {
+ code = path.charCodeAt(i);
+ }
+ else if (code === 47) {
+ break;
+ }
+ else {
+ code = 47;
+ }
+ if (code === 47) {
+ if (lastSlash === i - 1 || dots === 1) { ; }
+ else if (lastSlash !== i - 1 && dots === 2) {
+ if (res.length < 2
+ || lastSegmentLength !== 2
+ || res.charCodeAt(res.length - 1) !== 46
+ || res.charCodeAt(res.length - 2) !== 46) {
+ if (res.length > 2) {
+ var lastSlashIndex = res.lastIndexOf('/');
+ if (lastSlashIndex !== res.length - 1) {
+ if (lastSlashIndex === -1) {
+ res = '';
+ lastSegmentLength = 0;
+ }
+ else {
+ res = res.slice(0, lastSlashIndex);
+ lastSegmentLength = res.length - 1 - res.lastIndexOf('/');
+ }
+ lastSlash = i;
+ dots = 0;
+ continue;
+ }
+ }
+ else if (res.length === 2 || res.length === 1) {
+ res = '';
+ lastSegmentLength = 0;
+ lastSlash = i;
+ dots = 0;
+ continue;
+ }
+ }
+ if (allowAboveRoot) {
+ if (res.length > 0) {
+ res += '/..';
+ }
+ else {
+ res = '..';
+ }
+ lastSegmentLength = 2;
+ }
+ }
+ else {
+ if (res.length > 0) {
+ res += "/" + path.slice(lastSlash + 1, i);
+ }
+ else {
+ res = path.slice(lastSlash + 1, i);
+ }
+ lastSegmentLength = i - lastSlash - 1;
+ }
+ lastSlash = i;
+ dots = 0;
+ }
+ else if (code === 46 && dots !== -1) {
+ ++dots;
+ }
+ else {
+ dots = -1;
+ }
+ }
+ return res;
+ }
+ var path = {
+ /**
+ * Converts a path to posix format.
+ * @param path - The path to convert to posix
+ */
+ toPosix: function (path) { return replaceAll(path, '\\', '/'); },
+ /**
+ * Checks if the path is a URL
+ * @param path - The path to check
+ */
+ isUrl: function (path) { return (/^https?:/).test(this.toPosix(path)); },
+ /**
+ * Checks if the path is a data URL
+ * @param path - The path to check
+ */
+ isDataUrl: function (path) {
+ // eslint-disable-next-line max-len
+ return (/^data:([a-z]+\/[a-z0-9-+.]+(;[a-z0-9-.!#$%*+.{}|~`]+=[a-z0-9-.!#$%*+.{}()_|~`]+)*)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s<>]*?)$/i)
+ .test(path);
+ },
+ /**
+ * Checks if the path has a protocol e.g. http://
+ * This will return true for windows file paths
+ * @param path - The path to check
+ */
+ hasProtocol: function (path) { return (/^[^/:]+:\//).test(this.toPosix(path)); },
+ /**
+ * Returns the protocol of the path e.g. http://, C:/, file:///
+ * @param path - The path to get the protocol from
+ */
+ getProtocol: function (path) {
+ assertPath(path);
+ path = this.toPosix(path);
+ var protocol = '';
+ var isFile = (/^file:\/\/\//).exec(path);
+ var isHttp = (/^[^/:]+:\/\//).exec(path);
+ var isWindows = (/^[^/:]+:\//).exec(path);
+ if (isFile || isHttp || isWindows) {
+ var arr = (isFile === null || isFile === void 0 ? void 0 : isFile[0]) || (isHttp === null || isHttp === void 0 ? void 0 : isHttp[0]) || (isWindows === null || isWindows === void 0 ? void 0 : isWindows[0]);
+ protocol = arr;
+ path = path.slice(arr.length);
+ }
+ return protocol;
+ },
+ /**
+ * Converts URL to an absolute path.
+ * When loading from a Web Worker, we must use absolute paths.
+ * If the URL is already absolute we return it as is
+ * If it's not, we convert it
+ * @param url - The URL to test
+ * @param customBaseUrl - The base URL to use
+ * @param customRootUrl - The root URL to use
+ */
+ toAbsolute: function (url, customBaseUrl, customRootUrl) {
+ if (this.isDataUrl(url))
+ { return url; }
+ var baseUrl = removeUrlParams(this.toPosix(customBaseUrl !== null && customBaseUrl !== void 0 ? customBaseUrl : settings$1.ADAPTER.getBaseUrl()));
+ var rootUrl = removeUrlParams(this.toPosix(customRootUrl !== null && customRootUrl !== void 0 ? customRootUrl : this.rootname(baseUrl)));
+ assertPath(url);
+ url = this.toPosix(url);
+ // root relative url
+ if (url.startsWith('/')) {
+ return path.join(rootUrl, url.slice(1));
+ }
+ var absolutePath = this.isAbsolute(url) ? url : this.join(baseUrl, url);
+ return absolutePath;
+ },
+ /**
+ * Normalizes the given path, resolving '..' and '.' segments
+ * @param path - The path to normalize
+ */
+ normalize: function (path) {
+ path = this.toPosix(path);
+ assertPath(path);
+ if (path.length === 0)
+ { return '.'; }
+ var protocol = '';
+ var isAbsolute = path.startsWith('/');
+ if (this.hasProtocol(path)) {
+ protocol = this.rootname(path);
+ path = path.slice(protocol.length);
+ }
+ var trailingSeparator = path.endsWith('/');
+ // Normalize the path
+ path = normalizeStringPosix(path, false);
+ if (path.length > 0 && trailingSeparator)
+ { path += '/'; }
+ if (isAbsolute)
+ { return "/" + path; }
+ return protocol + path;
+ },
+ /**
+ * Determines if path is an absolute path.
+ * Absolute paths can be urls, data urls, or paths on disk
+ * @param path - The path to test
+ */
+ isAbsolute: function (path) {
+ assertPath(path);
+ path = this.toPosix(path);
+ if (this.hasProtocol(path))
+ { return true; }
+ return path.startsWith('/');
+ },
+ /**
+ * Joins all given path segments together using the platform-specific separator as a delimiter,
+ * then normalizes the resulting path
+ * @param segments - The segments of the path to join
+ */
+ join: function () {
+ var arguments$1 = arguments;
+
+ var _a;
+ var segments = [];
+ for (var _i = 0; _i < arguments.length; _i++) {
+ segments[_i] = arguments$1[_i];
+ }
+ if (segments.length === 0) {
+ return '.';
+ }
+ var joined;
+ for (var i = 0; i < segments.length; ++i) {
+ var arg = segments[i];
+ assertPath(arg);
+ if (arg.length > 0) {
+ if (joined === undefined)
+ { joined = arg; }
+ else {
+ var prevArg = (_a = segments[i - 1]) !== null && _a !== void 0 ? _a : '';
+ if (this.extname(prevArg)) {
+ joined += "/../" + arg;
+ }
+ else {
+ joined += "/" + arg;
+ }
+ }
+ }
+ }
+ if (joined === undefined) {
+ return '.';
+ }
+ return this.normalize(joined);
+ },
+ /**
+ * Returns the directory name of a path
+ * @param path - The path to parse
+ */
+ dirname: function (path) {
+ assertPath(path);
+ if (path.length === 0)
+ { return '.'; }
+ path = this.toPosix(path);
+ var code = path.charCodeAt(0);
+ var hasRoot = code === 47;
+ var end = -1;
+ var matchedSlash = true;
+ var proto = this.getProtocol(path);
+ var origpath = path;
+ path = path.slice(proto.length);
+ for (var i = path.length - 1; i >= 1; --i) {
+ code = path.charCodeAt(i);
+ if (code === 47) {
+ if (!matchedSlash) {
+ end = i;
+ break;
+ }
+ }
+ else {
+ // We saw the first non-path separator
+ matchedSlash = false;
+ }
+ }
+ // if end is -1 and its a url then we need to add the path back
+ // eslint-disable-next-line no-nested-ternary
+ if (end === -1)
+ { return hasRoot ? '/' : this.isUrl(origpath) ? proto + path : proto; }
+ if (hasRoot && end === 1)
+ { return '//'; }
+ return proto + path.slice(0, end);
+ },
+ /**
+ * Returns the root of the path e.g. /, C:/, file:///, http://domain.com/
+ * @param path - The path to parse
+ */
+ rootname: function (path) {
+ assertPath(path);
+ path = this.toPosix(path);
+ var root = '';
+ if (path.startsWith('/'))
+ { root = '/'; }
+ else {
+ root = this.getProtocol(path);
+ }
+ if (this.isUrl(path)) {
+ // need to find the first path separator
+ var index = path.indexOf('/', root.length);
+ if (index !== -1) {
+ root = path.slice(0, index);
+ }
+ else
+ { root = path; }
+ if (!root.endsWith('/'))
+ { root += '/'; }
+ }
+ return root;
+ },
+ /**
+ * Returns the last portion of a path
+ * @param path - The path to test
+ * @param ext - Optional extension to remove
+ */
+ basename: function (path, ext) {
+ assertPath(path);
+ if (ext)
+ { assertPath(ext); }
+ path = this.toPosix(path);
+ var start = 0;
+ var end = -1;
+ var matchedSlash = true;
+ var i;
+ if (ext !== undefined && ext.length > 0 && ext.length <= path.length) {
+ if (ext.length === path.length && ext === path)
+ { return ''; }
+ var extIdx = ext.length - 1;
+ var firstNonSlashEnd = -1;
+ for (i = path.length - 1; i >= 0; --i) {
+ var code = path.charCodeAt(i);
+ if (code === 47) {
+ // If we reached a path separator that was not part of a set of path
+ // separators at the end of the string, stop now
+ if (!matchedSlash) {
+ start = i + 1;
+ break;
+ }
+ }
+ else {
+ if (firstNonSlashEnd === -1) {
+ // We saw the first non-path separator, remember this index in case
+ // we need it if the extension ends up not matching
+ matchedSlash = false;
+ firstNonSlashEnd = i + 1;
+ }
+ if (extIdx >= 0) {
+ // Try to match the explicit extension
+ if (code === ext.charCodeAt(extIdx)) {
+ if (--extIdx === -1) {
+ // We matched the extension, so mark this as the end of our path
+ // component
+ end = i;
+ }
+ }
+ else {
+ // Extension does not match, so our result is the entire path
+ // component
+ extIdx = -1;
+ end = firstNonSlashEnd;
+ }
+ }
+ }
+ }
+ if (start === end)
+ { end = firstNonSlashEnd; }
+ else if (end === -1)
+ { end = path.length; }
+ return path.slice(start, end);
+ }
+ for (i = path.length - 1; i >= 0; --i) {
+ if (path.charCodeAt(i) === 47) {
+ // If we reached a path separator that was not part of a set of path
+ // separators at the end of the string, stop now
+ if (!matchedSlash) {
+ start = i + 1;
+ break;
+ }
+ }
+ else if (end === -1) {
+ // We saw the first non-path separator, mark this as the end of our
+ // path component
+ matchedSlash = false;
+ end = i + 1;
+ }
+ }
+ if (end === -1)
+ { return ''; }
+ return path.slice(start, end);
+ },
+ /**
+ * Returns the extension of the path, from the last occurrence of the . (period) character to end of string in the last
+ * portion of the path. If there is no . in the last portion of the path, or if there are no . characters other than
+ * the first character of the basename of path, an empty string is returned.
+ * @param path - The path to parse
+ */
+ extname: function (path) {
+ assertPath(path);
+ path = this.toPosix(path);
+ var startDot = -1;
+ var startPart = 0;
+ var end = -1;
+ var matchedSlash = true;
+ // Track the state of characters (if any) we see before our first dot and
+ // after any path separator we find
+ var preDotState = 0;
+ for (var i = path.length - 1; i >= 0; --i) {
+ var code = path.charCodeAt(i);
+ if (code === 47) {
+ // If we reached a path separator that was not part of a set of path
+ // separators at the end of the string, stop now
+ if (!matchedSlash) {
+ startPart = i + 1;
+ break;
+ }
+ continue;
+ }
+ if (end === -1) {
+ // We saw the first non-path separator, mark this as the end of our
+ // extension
+ matchedSlash = false;
+ end = i + 1;
+ }
+ if (code === 46) {
+ // If this is our first dot, mark it as the start of our extension
+ if (startDot === -1)
+ { startDot = i; }
+ else if (preDotState !== 1)
+ { preDotState = 1; }
+ }
+ else if (startDot !== -1) {
+ // We saw a non-dot and non-path separator before our dot, so we should
+ // have a good chance at having a non-empty extension
+ preDotState = -1;
+ }
+ }
+ if (startDot === -1 || end === -1
+ // We saw a non-dot character immediately before the dot
+ || preDotState === 0
+ // The (right-most) trimmed path component is exactly '..'
+ // eslint-disable-next-line no-mixed-operators
+ || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
+ return '';
+ }
+ return path.slice(startDot, end);
+ },
+ /**
+ * Parses a path into an object containing the 'root', `dir`, `base`, `ext`, and `name` properties.
+ * @param path - The path to parse
+ */
+ parse: function (path) {
+ assertPath(path);
+ var ret = { root: '', dir: '', base: '', ext: '', name: '' };
+ if (path.length === 0)
+ { return ret; }
+ path = this.toPosix(path);
+ var code = path.charCodeAt(0);
+ var isAbsolute = this.isAbsolute(path);
+ var start;
+ ret.root = this.rootname(path);
+ if (isAbsolute || this.hasProtocol(path)) {
+ start = 1;
+ }
+ else {
+ start = 0;
+ }
+ var startDot = -1;
+ var startPart = 0;
+ var end = -1;
+ var matchedSlash = true;
+ var i = path.length - 1;
+ // Track the state of characters (if any) we see before our first dot and
+ // after any path separator we find
+ var preDotState = 0;
+ // Get non-dir info
+ for (; i >= start; --i) {
+ code = path.charCodeAt(i);
+ if (code === 47) {
+ // If we reached a path separator that was not part of a set of path
+ // separators at the end of the string, stop now
+ if (!matchedSlash) {
+ startPart = i + 1;
+ break;
+ }
+ continue;
+ }
+ if (end === -1) {
+ // We saw the first non-path separator, mark this as the end of our
+ // extension
+ matchedSlash = false;
+ end = i + 1;
+ }
+ if (code === 46) {
+ // If this is our first dot, mark it as the start of our extension
+ if (startDot === -1)
+ { startDot = i; }
+ else if (preDotState !== 1)
+ { preDotState = 1; }
+ }
+ else if (startDot !== -1) {
+ // We saw a non-dot and non-path separator before our dot, so we should
+ // have a good chance at having a non-empty extension
+ preDotState = -1;
+ }
+ }
+ if (startDot === -1 || end === -1
+ // We saw a non-dot character immediately before the dot
+ || preDotState === 0
+ // The (right-most) trimmed path component is exactly '..'
+ // eslint-disable-next-line no-mixed-operators
+ || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
+ if (end !== -1) {
+ if (startPart === 0 && isAbsolute)
+ { ret.base = ret.name = path.slice(1, end); }
+ else
+ { ret.base = ret.name = path.slice(startPart, end); }
+ }
+ }
+ else {
+ if (startPart === 0 && isAbsolute) {
+ ret.name = path.slice(1, startDot);
+ ret.base = path.slice(1, end);
+ }
+ else {
+ ret.name = path.slice(startPart, startDot);
+ ret.base = path.slice(startPart, end);
+ }
+ ret.ext = path.slice(startDot, end);
+ }
+ ret.dir = this.dirname(path);
+ return ret;
+ },
+ sep: '/',
+ delimiter: ':'
+ };
+
+ /**
+ * The prefix that denotes a URL is for a retina asset.
+ * @static
+ * @name RETINA_PREFIX
+ * @memberof PIXI.settings
+ * @type {RegExp}
+ * @default /@([0-9\.]+)x/
+ * @example `@2x`
+ */
+ settings$1.RETINA_PREFIX = /@([0-9\.]+)x/;
+ /**
+ * Should the `failIfMajorPerformanceCaveat` flag be enabled as a context option used in the `isWebGLSupported` function.
+ * If set to true, a WebGL renderer can fail to be created if the browser thinks there could be performance issues when
+ * using WebGL.
+ *
+ * In PixiJS v6 this has changed from true to false by default, to allow WebGL to work in as many scenarios as possible.
+ * However, some users may have a poor experience, for example, if a user has a gpu or driver version blacklisted by the
+ * browser.
+ *
+ * If your application requires high performance rendering, you may wish to set this to false.
+ * We recommend one of two options if you decide to set this flag to false:
+ *
+ * 1: Use the `pixi.js-legacy` package, which includes a Canvas renderer as a fallback in case high performance WebGL is
+ * not supported.
+ *
+ * 2: Call `isWebGLSupported` (which if found in the PIXI.utils package) in your code before attempting to create a PixiJS
+ * renderer, and show an error message to the user if the function returns false, explaining that their device & browser
+ * combination does not support high performance WebGL.
+ * This is a much better strategy than trying to create a PixiJS renderer and finding it then fails.
+ * @static
+ * @name FAIL_IF_MAJOR_PERFORMANCE_CAVEAT
+ * @memberof PIXI.settings
+ * @type {boolean}
+ * @default false
+ */
+ settings$1.FAIL_IF_MAJOR_PERFORMANCE_CAVEAT = false;
+
+ var saidHello = false;
+ var VERSION$1 = '6.5.3';
+ /**
+ * Skips the hello message of renderers that are created after this is run.
+ * @function skipHello
+ * @memberof PIXI.utils
+ */
+ function skipHello() {
+ saidHello = true;
+ }
+ /**
+ * Logs out the version and renderer information for this running instance of PIXI.
+ * If you don't want to see this message you can run `PIXI.utils.skipHello()` before
+ * creating your renderer. Keep in mind that doing that will forever make you a jerk face.
+ * @static
+ * @function sayHello
+ * @memberof PIXI.utils
+ * @param {string} type - The string renderer type to log.
+ */
+ function sayHello(type) {
+ var _a;
+ if (saidHello) {
+ return;
+ }
+ if (settings$1.ADAPTER.getNavigator().userAgent.toLowerCase().indexOf('chrome') > -1) {
+ var args = [
+ "\n %c %c %c PixiJS " + VERSION$1 + " - \u2730 " + type + " \u2730 %c %c http://www.pixijs.com/ %c %c \u2665%c\u2665%c\u2665 \n\n",
+ 'background: #ff66a5; padding:5px 0;',
+ 'background: #ff66a5; padding:5px 0;',
+ 'color: #ff66a5; background: #030307; padding:5px 0;',
+ 'background: #ff66a5; padding:5px 0;',
+ 'background: #ffc3dc; padding:5px 0;',
+ 'background: #ff66a5; padding:5px 0;',
+ 'color: #ff2424; background: #fff; padding:5px 0;',
+ 'color: #ff2424; background: #fff; padding:5px 0;',
+ 'color: #ff2424; background: #fff; padding:5px 0;' ];
+ (_a = globalThis.console).log.apply(_a, args);
+ }
+ else if (globalThis.console) {
+ globalThis.console.log("PixiJS " + VERSION$1 + " - " + type + " - http://www.pixijs.com/");
+ }
+ saidHello = true;
+ }
+
+ var supported;
+ /**
+ * Helper for checking for WebGL support.
+ * @memberof PIXI.utils
+ * @function isWebGLSupported
+ * @returns {boolean} Is WebGL supported.
+ */
+ function isWebGLSupported() {
+ if (typeof supported === 'undefined') {
+ supported = (function supported() {
+ var contextOptions = {
+ stencil: true,
+ failIfMajorPerformanceCaveat: settings$1.FAIL_IF_MAJOR_PERFORMANCE_CAVEAT,
+ };
+ try {
+ if (!settings$1.ADAPTER.getWebGLRenderingContext()) {
+ return false;
+ }
+ var canvas = settings$1.ADAPTER.createCanvas();
+ var gl = (canvas.getContext('webgl', contextOptions)
+ || canvas.getContext('experimental-webgl', contextOptions));
+ var success = !!(gl && gl.getContextAttributes().stencil);
+ if (gl) {
+ var loseContext = gl.getExtension('WEBGL_lose_context');
+ if (loseContext) {
+ loseContext.loseContext();
+ }
+ }
+ gl = null;
+ return success;
+ }
+ catch (e) {
+ return false;
+ }
+ })();
+ }
+ return supported;
+ }
+
+ var aliceblue = "#f0f8ff";
+ var antiquewhite = "#faebd7";
+ var aqua = "#00ffff";
+ var aquamarine = "#7fffd4";
+ var azure = "#f0ffff";
+ var beige = "#f5f5dc";
+ var bisque = "#ffe4c4";
+ var black = "#000000";
+ var blanchedalmond = "#ffebcd";
+ var blue = "#0000ff";
+ var blueviolet = "#8a2be2";
+ var brown = "#a52a2a";
+ var burlywood = "#deb887";
+ var cadetblue = "#5f9ea0";
+ var chartreuse = "#7fff00";
+ var chocolate = "#d2691e";
+ var coral = "#ff7f50";
+ var cornflowerblue = "#6495ed";
+ var cornsilk = "#fff8dc";
+ var crimson = "#dc143c";
+ var cyan = "#00ffff";
+ var darkblue = "#00008b";
+ var darkcyan = "#008b8b";
+ var darkgoldenrod = "#b8860b";
+ var darkgray = "#a9a9a9";
+ var darkgreen = "#006400";
+ var darkgrey = "#a9a9a9";
+ var darkkhaki = "#bdb76b";
+ var darkmagenta = "#8b008b";
+ var darkolivegreen = "#556b2f";
+ var darkorange = "#ff8c00";
+ var darkorchid = "#9932cc";
+ var darkred = "#8b0000";
+ var darksalmon = "#e9967a";
+ var darkseagreen = "#8fbc8f";
+ var darkslateblue = "#483d8b";
+ var darkslategray = "#2f4f4f";
+ var darkslategrey = "#2f4f4f";
+ var darkturquoise = "#00ced1";
+ var darkviolet = "#9400d3";
+ var deeppink = "#ff1493";
+ var deepskyblue = "#00bfff";
+ var dimgray = "#696969";
+ var dimgrey = "#696969";
+ var dodgerblue = "#1e90ff";
+ var firebrick = "#b22222";
+ var floralwhite = "#fffaf0";
+ var forestgreen = "#228b22";
+ var fuchsia = "#ff00ff";
+ var gainsboro = "#dcdcdc";
+ var ghostwhite = "#f8f8ff";
+ var goldenrod = "#daa520";
+ var gold = "#ffd700";
+ var gray = "#808080";
+ var green = "#008000";
+ var greenyellow = "#adff2f";
+ var grey = "#808080";
+ var honeydew = "#f0fff0";
+ var hotpink = "#ff69b4";
+ var indianred = "#cd5c5c";
+ var indigo = "#4b0082";
+ var ivory = "#fffff0";
+ var khaki = "#f0e68c";
+ var lavenderblush = "#fff0f5";
+ var lavender = "#e6e6fa";
+ var lawngreen = "#7cfc00";
+ var lemonchiffon = "#fffacd";
+ var lightblue = "#add8e6";
+ var lightcoral = "#f08080";
+ var lightcyan = "#e0ffff";
+ var lightgoldenrodyellow = "#fafad2";
+ var lightgray = "#d3d3d3";
+ var lightgreen = "#90ee90";
+ var lightgrey = "#d3d3d3";
+ var lightpink = "#ffb6c1";
+ var lightsalmon = "#ffa07a";
+ var lightseagreen = "#20b2aa";
+ var lightskyblue = "#87cefa";
+ var lightslategray = "#778899";
+ var lightslategrey = "#778899";
+ var lightsteelblue = "#b0c4de";
+ var lightyellow = "#ffffe0";
+ var lime = "#00ff00";
+ var limegreen = "#32cd32";
+ var linen = "#faf0e6";
+ var magenta = "#ff00ff";
+ var maroon = "#800000";
+ var mediumaquamarine = "#66cdaa";
+ var mediumblue = "#0000cd";
+ var mediumorchid = "#ba55d3";
+ var mediumpurple = "#9370db";
+ var mediumseagreen = "#3cb371";
+ var mediumslateblue = "#7b68ee";
+ var mediumspringgreen = "#00fa9a";
+ var mediumturquoise = "#48d1cc";
+ var mediumvioletred = "#c71585";
+ var midnightblue = "#191970";
+ var mintcream = "#f5fffa";
+ var mistyrose = "#ffe4e1";
+ var moccasin = "#ffe4b5";
+ var navajowhite = "#ffdead";
+ var navy = "#000080";
+ var oldlace = "#fdf5e6";
+ var olive = "#808000";
+ var olivedrab = "#6b8e23";
+ var orange = "#ffa500";
+ var orangered = "#ff4500";
+ var orchid = "#da70d6";
+ var palegoldenrod = "#eee8aa";
+ var palegreen = "#98fb98";
+ var paleturquoise = "#afeeee";
+ var palevioletred = "#db7093";
+ var papayawhip = "#ffefd5";
+ var peachpuff = "#ffdab9";
+ var peru = "#cd853f";
+ var pink = "#ffc0cb";
+ var plum = "#dda0dd";
+ var powderblue = "#b0e0e6";
+ var purple = "#800080";
+ var rebeccapurple = "#663399";
+ var red = "#ff0000";
+ var rosybrown = "#bc8f8f";
+ var royalblue = "#4169e1";
+ var saddlebrown = "#8b4513";
+ var salmon = "#fa8072";
+ var sandybrown = "#f4a460";
+ var seagreen = "#2e8b57";
+ var seashell = "#fff5ee";
+ var sienna = "#a0522d";
+ var silver = "#c0c0c0";
+ var skyblue = "#87ceeb";
+ var slateblue = "#6a5acd";
+ var slategray = "#708090";
+ var slategrey = "#708090";
+ var snow = "#fffafa";
+ var springgreen = "#00ff7f";
+ var steelblue = "#4682b4";
+ var tan = "#d2b48c";
+ var teal = "#008080";
+ var thistle = "#d8bfd8";
+ var tomato = "#ff6347";
+ var turquoise = "#40e0d0";
+ var violet = "#ee82ee";
+ var wheat = "#f5deb3";
+ var white = "#ffffff";
+ var whitesmoke = "#f5f5f5";
+ var yellow = "#ffff00";
+ var yellowgreen = "#9acd32";
+ var cssColorNames = {
+ aliceblue: aliceblue,
+ antiquewhite: antiquewhite,
+ aqua: aqua,
+ aquamarine: aquamarine,
+ azure: azure,
+ beige: beige,
+ bisque: bisque,
+ black: black,
+ blanchedalmond: blanchedalmond,
+ blue: blue,
+ blueviolet: blueviolet,
+ brown: brown,
+ burlywood: burlywood,
+ cadetblue: cadetblue,
+ chartreuse: chartreuse,
+ chocolate: chocolate,
+ coral: coral,
+ cornflowerblue: cornflowerblue,
+ cornsilk: cornsilk,
+ crimson: crimson,
+ cyan: cyan,
+ darkblue: darkblue,
+ darkcyan: darkcyan,
+ darkgoldenrod: darkgoldenrod,
+ darkgray: darkgray,
+ darkgreen: darkgreen,
+ darkgrey: darkgrey,
+ darkkhaki: darkkhaki,
+ darkmagenta: darkmagenta,
+ darkolivegreen: darkolivegreen,
+ darkorange: darkorange,
+ darkorchid: darkorchid,
+ darkred: darkred,
+ darksalmon: darksalmon,
+ darkseagreen: darkseagreen,
+ darkslateblue: darkslateblue,
+ darkslategray: darkslategray,
+ darkslategrey: darkslategrey,
+ darkturquoise: darkturquoise,
+ darkviolet: darkviolet,
+ deeppink: deeppink,
+ deepskyblue: deepskyblue,
+ dimgray: dimgray,
+ dimgrey: dimgrey,
+ dodgerblue: dodgerblue,
+ firebrick: firebrick,
+ floralwhite: floralwhite,
+ forestgreen: forestgreen,
+ fuchsia: fuchsia,
+ gainsboro: gainsboro,
+ ghostwhite: ghostwhite,
+ goldenrod: goldenrod,
+ gold: gold,
+ gray: gray,
+ green: green,
+ greenyellow: greenyellow,
+ grey: grey,
+ honeydew: honeydew,
+ hotpink: hotpink,
+ indianred: indianred,
+ indigo: indigo,
+ ivory: ivory,
+ khaki: khaki,
+ lavenderblush: lavenderblush,
+ lavender: lavender,
+ lawngreen: lawngreen,
+ lemonchiffon: lemonchiffon,
+ lightblue: lightblue,
+ lightcoral: lightcoral,
+ lightcyan: lightcyan,
+ lightgoldenrodyellow: lightgoldenrodyellow,
+ lightgray: lightgray,
+ lightgreen: lightgreen,
+ lightgrey: lightgrey,
+ lightpink: lightpink,
+ lightsalmon: lightsalmon,
+ lightseagreen: lightseagreen,
+ lightskyblue: lightskyblue,
+ lightslategray: lightslategray,
+ lightslategrey: lightslategrey,
+ lightsteelblue: lightsteelblue,
+ lightyellow: lightyellow,
+ lime: lime,
+ limegreen: limegreen,
+ linen: linen,
+ magenta: magenta,
+ maroon: maroon,
+ mediumaquamarine: mediumaquamarine,
+ mediumblue: mediumblue,
+ mediumorchid: mediumorchid,
+ mediumpurple: mediumpurple,
+ mediumseagreen: mediumseagreen,
+ mediumslateblue: mediumslateblue,
+ mediumspringgreen: mediumspringgreen,
+ mediumturquoise: mediumturquoise,
+ mediumvioletred: mediumvioletred,
+ midnightblue: midnightblue,
+ mintcream: mintcream,
+ mistyrose: mistyrose,
+ moccasin: moccasin,
+ navajowhite: navajowhite,
+ navy: navy,
+ oldlace: oldlace,
+ olive: olive,
+ olivedrab: olivedrab,
+ orange: orange,
+ orangered: orangered,
+ orchid: orchid,
+ palegoldenrod: palegoldenrod,
+ palegreen: palegreen,
+ paleturquoise: paleturquoise,
+ palevioletred: palevioletred,
+ papayawhip: papayawhip,
+ peachpuff: peachpuff,
+ peru: peru,
+ pink: pink,
+ plum: plum,
+ powderblue: powderblue,
+ purple: purple,
+ rebeccapurple: rebeccapurple,
+ red: red,
+ rosybrown: rosybrown,
+ royalblue: royalblue,
+ saddlebrown: saddlebrown,
+ salmon: salmon,
+ sandybrown: sandybrown,
+ seagreen: seagreen,
+ seashell: seashell,
+ sienna: sienna,
+ silver: silver,
+ skyblue: skyblue,
+ slateblue: slateblue,
+ slategray: slategray,
+ slategrey: slategrey,
+ snow: snow,
+ springgreen: springgreen,
+ steelblue: steelblue,
+ tan: tan,
+ teal: teal,
+ thistle: thistle,
+ tomato: tomato,
+ turquoise: turquoise,
+ violet: violet,
+ wheat: wheat,
+ white: white,
+ whitesmoke: whitesmoke,
+ yellow: yellow,
+ yellowgreen: yellowgreen
+ };
+
+ /**
+ * Converts a hexadecimal color number to an [R, G, B] array of normalized floats (numbers from 0.0 to 1.0).
+ * @example
+ * PIXI.utils.hex2rgb(0xffffff); // returns [1, 1, 1]
+ * @memberof PIXI.utils
+ * @function hex2rgb
+ * @param {number} hex - The hexadecimal number to convert
+ * @param {number[]} [out=[]] - If supplied, this array will be used rather than returning a new one
+ * @returns {number[]} An array representing the [R, G, B] of the color where all values are floats.
+ */
+ function hex2rgb(hex, out) {
+ if (out === void 0) { out = []; }
+ out[0] = ((hex >> 16) & 0xFF) / 255;
+ out[1] = ((hex >> 8) & 0xFF) / 255;
+ out[2] = (hex & 0xFF) / 255;
+ return out;
+ }
+ /**
+ * Converts a hexadecimal color number to a string.
+ * @example
+ * PIXI.utils.hex2string(0xffffff); // returns "#ffffff"
+ * @memberof PIXI.utils
+ * @function hex2string
+ * @param {number} hex - Number in hex (e.g., `0xffffff`)
+ * @returns {string} The string color (e.g., `"#ffffff"`).
+ */
+ function hex2string(hex) {
+ var hexString = hex.toString(16);
+ hexString = '000000'.substring(0, 6 - hexString.length) + hexString;
+ return "#" + hexString;
+ }
+ /**
+ * Converts a string to a hexadecimal color number.
+ * It can handle:
+ * hex strings starting with #: "#ffffff"
+ * hex strings starting with 0x: "0xffffff"
+ * hex strings without prefix: "ffffff"
+ * css colors: "black"
+ * @example
+ * PIXI.utils.string2hex("#ffffff"); // returns 0xffffff
+ * @memberof PIXI.utils
+ * @function string2hex
+ * @param {string} string - The string color (e.g., `"#ffffff"`)
+ * @returns {number} Number in hexadecimal.
+ */
+ function string2hex(string) {
+ if (typeof string === 'string') {
+ string = cssColorNames[string.toLowerCase()] || string;
+ if (string[0] === '#') {
+ string = string.slice(1);
+ }
+ }
+ return parseInt(string, 16);
+ }
+ /**
+ * Converts a color as an [R, G, B] array of normalized floats to a hexadecimal number.
+ * @example
+ * PIXI.utils.rgb2hex([1, 1, 1]); // returns 0xffffff
+ * @memberof PIXI.utils
+ * @function rgb2hex
+ * @param {number[]} rgb - Array of numbers where all values are normalized floats from 0.0 to 1.0.
+ * @returns {number} Number in hexadecimal.
+ */
+ function rgb2hex(rgb) {
+ return (((rgb[0] * 255) << 16) + ((rgb[1] * 255) << 8) + (rgb[2] * 255 | 0));
+ }
+
+ /**
+ * Corrects PixiJS blend, takes premultiplied alpha into account
+ * @memberof PIXI.utils
+ * @function mapPremultipliedBlendModes
+ * @private
+ * @returns {Array<number[]>} Mapped modes.
+ */
+ function mapPremultipliedBlendModes$1() {
+ var pm = [];
+ var npm = [];
+ for (var i = 0; i < 32; i++) {
+ pm[i] = i;
+ npm[i] = i;
+ }
+ pm[exports.BLEND_MODES.NORMAL_NPM] = exports.BLEND_MODES.NORMAL;
+ pm[exports.BLEND_MODES.ADD_NPM] = exports.BLEND_MODES.ADD;
+ pm[exports.BLEND_MODES.SCREEN_NPM] = exports.BLEND_MODES.SCREEN;
+ npm[exports.BLEND_MODES.NORMAL] = exports.BLEND_MODES.NORMAL_NPM;
+ npm[exports.BLEND_MODES.ADD] = exports.BLEND_MODES.ADD_NPM;
+ npm[exports.BLEND_MODES.SCREEN] = exports.BLEND_MODES.SCREEN_NPM;
+ var array = [];
+ array.push(npm);
+ array.push(pm);
+ return array;
+ }
+ /**
+ * maps premultiply flag and blendMode to adjusted blendMode
+ * @memberof PIXI.utils
+ * @constant premultiplyBlendMode
+ * @type {Array<number[]>}
+ */
+ var premultiplyBlendMode = mapPremultipliedBlendModes$1();
+ /**
+ * changes blendMode according to texture format
+ * @memberof PIXI.utils
+ * @function correctBlendMode
+ * @param {number} blendMode - supposed blend mode
+ * @param {boolean} premultiplied - whether source is premultiplied
+ * @returns {number} true blend mode for this texture
+ */
+ function correctBlendMode(blendMode, premultiplied) {
+ return premultiplyBlendMode[premultiplied ? 1 : 0][blendMode];
+ }
+ /**
+ * combines rgb and alpha to out array
+ * @memberof PIXI.utils
+ * @function premultiplyRgba
+ * @param {Float32Array|number[]} rgb - input rgb
+ * @param {number} alpha - alpha param
+ * @param {Float32Array} [out] - output
+ * @param {boolean} [premultiply=true] - do premultiply it
+ * @returns {Float32Array} vec4 rgba
+ */
+ function premultiplyRgba(rgb, alpha, out, premultiply) {
+ out = out || new Float32Array(4);
+ if (premultiply || premultiply === undefined) {
+ out[0] = rgb[0] * alpha;
+ out[1] = rgb[1] * alpha;
+ out[2] = rgb[2] * alpha;
+ }
+ else {
+ out[0] = rgb[0];
+ out[1] = rgb[1];
+ out[2] = rgb[2];
+ }
+ out[3] = alpha;
+ return out;
+ }
+ /**
+ * premultiplies tint
+ * @memberof PIXI.utils
+ * @function premultiplyTint
+ * @param {number} tint - integer RGB
+ * @param {number} alpha - floating point alpha (0.0-1.0)
+ * @returns {number} tint multiplied by alpha
+ */
+ function premultiplyTint(tint, alpha) {
+ if (alpha === 1.0) {
+ return (alpha * 255 << 24) + tint;
+ }
+ if (alpha === 0.0) {
+ return 0;
+ }
+ var R = ((tint >> 16) & 0xFF);
+ var G = ((tint >> 8) & 0xFF);
+ var B = (tint & 0xFF);
+ R = ((R * alpha) + 0.5) | 0;
+ G = ((G * alpha) + 0.5) | 0;
+ B = ((B * alpha) + 0.5) | 0;
+ return (alpha * 255 << 24) + (R << 16) + (G << 8) + B;
+ }
+ /**
+ * converts integer tint and float alpha to vec4 form, premultiplies by default
+ * @memberof PIXI.utils
+ * @function premultiplyTintToRgba
+ * @param {number} tint - input tint
+ * @param {number} alpha - alpha param
+ * @param {Float32Array} [out] - output
+ * @param {boolean} [premultiply=true] - do premultiply it
+ * @returns {Float32Array} vec4 rgba
+ */
+ function premultiplyTintToRgba(tint, alpha, out, premultiply) {
+ out = out || new Float32Array(4);
+ out[0] = ((tint >> 16) & 0xFF) / 255.0;
+ out[1] = ((tint >> 8) & 0xFF) / 255.0;
+ out[2] = (tint & 0xFF) / 255.0;
+ if (premultiply || premultiply === undefined) {
+ out[0] *= alpha;
+ out[1] *= alpha;
+ out[2] *= alpha;
+ }
+ out[3] = alpha;
+ return out;
+ }
+
+ /**
+ * Generic Mask Stack data structure
+ * @memberof PIXI.utils
+ * @function createIndicesForQuads
+ * @param {number} size - Number of quads
+ * @param {Uint16Array|Uint32Array} [outBuffer] - Buffer for output, length has to be `6 * size`
+ * @returns {Uint16Array|Uint32Array} - Resulting index buffer
+ */
+ function createIndicesForQuads(size, outBuffer) {
+ if (outBuffer === void 0) { outBuffer = null; }
+ // the total number of indices in our array, there are 6 points per quad.
+ var totalIndices = size * 6;
+ outBuffer = outBuffer || new Uint16Array(totalIndices);
+ if (outBuffer.length !== totalIndices) {
+ throw new Error("Out buffer length is incorrect, got " + outBuffer.length + " and expected " + totalIndices);
+ }
+ // fill the indices with the quads to draw
+ for (var i = 0, j = 0; i < totalIndices; i += 6, j += 4) {
+ outBuffer[i + 0] = j + 0;
+ outBuffer[i + 1] = j + 1;
+ outBuffer[i + 2] = j + 2;
+ outBuffer[i + 3] = j + 0;
+ outBuffer[i + 4] = j + 2;
+ outBuffer[i + 5] = j + 3;
+ }
+ return outBuffer;
+ }
+
+ function getBufferType(array) {
+ if (array.BYTES_PER_ELEMENT === 4) {
+ if (array instanceof Float32Array) {
+ return 'Float32Array';
+ }
+ else if (array instanceof Uint32Array) {
+ return 'Uint32Array';
+ }
+ return 'Int32Array';
+ }
+ else if (array.BYTES_PER_ELEMENT === 2) {
+ if (array instanceof Uint16Array) {
+ return 'Uint16Array';
+ }
+ }
+ else if (array.BYTES_PER_ELEMENT === 1) {
+ if (array instanceof Uint8Array) {
+ return 'Uint8Array';
+ }
+ }
+ // TODO map out the rest of the array elements!
+ return null;
+ }
+
+ /* eslint-disable object-shorthand */
+ var map$2 = { Float32Array: Float32Array, Uint32Array: Uint32Array, Int32Array: Int32Array, Uint8Array: Uint8Array };
+ function interleaveTypedArrays$1(arrays, sizes) {
+ var outSize = 0;
+ var stride = 0;
+ var views = {};
+ for (var i = 0; i < arrays.length; i++) {
+ stride += sizes[i];
+ outSize += arrays[i].length;
+ }
+ var buffer = new ArrayBuffer(outSize * 4);
+ var out = null;
+ var littleOffset = 0;
+ for (var i = 0; i < arrays.length; i++) {
+ var size = sizes[i];
+ var array = arrays[i];
+ /*
+ @todo This is unsafe casting but consistent with how the code worked previously. Should it stay this way
+ or should and `getBufferTypeUnsafe` function be exposed that throws an Error if unsupported type is passed?
+ */
+ var type = getBufferType(array);
+ if (!views[type]) {
+ views[type] = new map$2[type](buffer);
+ }
+ out = views[type];
+ for (var j = 0; j < array.length; j++) {
+ var indexStart = ((j / size | 0) * stride) + littleOffset;
+ var index = j % size;
+ out[indexStart + index] = array[j];
+ }
+ littleOffset += size;
+ }
+ return new Float32Array(buffer);
+ }
+
+ // Taken from the bit-twiddle package
+ /**
+ * Rounds to next power of two.
+ * @function nextPow2
+ * @memberof PIXI.utils
+ * @param {number} v - input value
+ * @returns {number} - next rounded power of two
+ */
+ function nextPow2(v) {
+ v += v === 0 ? 1 : 0;
+ --v;
+ v |= v >>> 1;
+ v |= v >>> 2;
+ v |= v >>> 4;
+ v |= v >>> 8;
+ v |= v >>> 16;
+ return v + 1;
+ }
+ /**
+ * Checks if a number is a power of two.
+ * @function isPow2
+ * @memberof PIXI.utils
+ * @param {number} v - input value
+ * @returns {boolean} `true` if value is power of two
+ */
+ function isPow2(v) {
+ return !(v & (v - 1)) && (!!v);
+ }
+ /**
+ * Computes ceil of log base 2
+ * @function log2
+ * @memberof PIXI.utils
+ * @param {number} v - input value
+ * @returns {number} logarithm base 2
+ */
+ function log2(v) {
+ var r = (v > 0xFFFF ? 1 : 0) << 4;
+ v >>>= r;
+ var shift = (v > 0xFF ? 1 : 0) << 3;
+ v >>>= shift;
+ r |= shift;
+ shift = (v > 0xF ? 1 : 0) << 2;
+ v >>>= shift;
+ r |= shift;
+ shift = (v > 0x3 ? 1 : 0) << 1;
+ v >>>= shift;
+ r |= shift;
+ return r | (v >> 1);
+ }
+
+ /**
+ * Remove items from a javascript array without generating garbage
+ * @function removeItems
+ * @memberof PIXI.utils
+ * @param {Array<any>} arr - Array to remove elements from
+ * @param {number} startIdx - starting index
+ * @param {number} removeCount - how many to remove
+ */
+ function removeItems(arr, startIdx, removeCount) {
+ var length = arr.length;
+ var i;
+ if (startIdx >= length || removeCount === 0) {
+ return;
+ }
+ removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount);
+ var len = length - removeCount;
+ for (i = startIdx; i < len; ++i) {
+ arr[i] = arr[i + removeCount];
+ }
+ arr.length = len;
+ }
+
+ /**
+ * Returns sign of number
+ * @memberof PIXI.utils
+ * @function sign
+ * @param {number} n - the number to check the sign of
+ * @returns {number} 0 if `n` is 0, -1 if `n` is negative, 1 if `n` is positive
+ */
+ function sign(n) {
+ if (n === 0)
+ { return 0; }
+ return n < 0 ? -1 : 1;
+ }
+
+ var nextUid = 0;
+ /**
+ * Gets the next unique identifier
+ * @memberof PIXI.utils
+ * @function uid
+ * @returns {number} The next unique identifier to use.
+ */
+ function uid() {
+ return ++nextUid;
+ }
+
+ // A map of warning messages already fired
+ var warnings$1 = {};
+ /**
+ * Helper for warning developers about deprecated features & settings.
+ * A stack track for warnings is given; useful for tracking-down where
+ * deprecated methods/properties/classes are being used within the code.
+ * @memberof PIXI.utils
+ * @function deprecation
+ * @param {string} version - The version where the feature became deprecated
+ * @param {string} message - Message should include what is deprecated, where, and the new solution
+ * @param {number} [ignoreDepth=3] - The number of steps to ignore at the top of the error stack
+ * this is mostly to ignore internal deprecation calls.
+ */
+ function deprecation$1(version, message, ignoreDepth) {
+ if (ignoreDepth === void 0) { ignoreDepth = 3; }
+ // Ignore duplicat
+ if (warnings$1[message]) {
+ return;
+ }
+ /* eslint-disable no-console */
+ var stack = new Error().stack;
+ // Handle IE < 10 and Safari < 6
+ if (typeof stack === 'undefined') {
+ console.warn('PixiJS Deprecation Warning: ', message + "\nDeprecated since v" + version);
+ }
+ else {
+ // chop off the stack trace which includes PixiJS internal calls
+ stack = stack.split('\n').splice(ignoreDepth).join('\n');
+ if (console.groupCollapsed) {
+ console.groupCollapsed('%cPixiJS Deprecation Warning: %c%s', 'color:#614108;background:#fffbe6', 'font-weight:normal;color:#614108;background:#fffbe6', message + "\nDeprecated since v" + version);
+ console.warn(stack);
+ console.groupEnd();
+ }
+ else {
+ console.warn('PixiJS Deprecation Warning: ', message + "\nDeprecated since v" + version);
+ console.warn(stack);
+ }
+ }
+ /* eslint-enable no-console */
+ warnings$1[message] = true;
+ }
+
+ /**
+ * @todo Describe property usage
+ * @static
+ * @name ProgramCache
+ * @memberof PIXI.utils
+ * @type {object}
+ */
+ var ProgramCache = {};
+ /**
+ * @todo Describe property usage
+ * @static
+ * @name TextureCache
+ * @memberof PIXI.utils
+ * @type {object}
+ */
+ var TextureCache = Object.create(null);
+ /**
+ * @todo Describe property usage
+ * @static
+ * @name BaseTextureCache
+ * @memberof PIXI.utils
+ * @type {object}
+ */
+ var BaseTextureCache = Object.create(null);
+ /**
+ * Destroys all texture in the cache
+ * @memberof PIXI.utils
+ * @function destroyTextureCache
+ */
+ function destroyTextureCache() {
+ var key;
+ for (key in TextureCache) {
+ TextureCache[key].destroy();
+ }
+ for (key in BaseTextureCache) {
+ BaseTextureCache[key].destroy();
+ }
+ }
+ /**
+ * Removes all textures from cache, but does not destroy them
+ * @memberof PIXI.utils
+ * @function clearTextureCache
+ */
+ function clearTextureCache() {
+ var key;
+ for (key in TextureCache) {
+ delete TextureCache[key];
+ }
+ for (key in BaseTextureCache) {
+ delete BaseTextureCache[key];
+ }
+ }
+
+ /**
+ * Creates a Canvas element of the given size to be used as a target for rendering to.
+ * @class
+ * @memberof PIXI.utils
+ */
+ var CanvasRenderTarget = /** @class */ (function () {
+ /**
+ * @param width - the width for the newly created canvas
+ * @param height - the height for the newly created canvas
+ * @param {number} [resolution=PIXI.settings.RESOLUTION] - The resolution / device pixel ratio of the canvas
+ */
+ function CanvasRenderTarget(width, height, resolution) {
+ this.canvas = settings$1.ADAPTER.createCanvas();
+ this.context = this.canvas.getContext('2d');
+ this.resolution = resolution || settings$1.RESOLUTION;
+ this.resize(width, height);
+ }
+ /**
+ * Clears the canvas that was created by the CanvasRenderTarget class.
+ * @private
+ */
+ CanvasRenderTarget.prototype.clear = function () {
+ this.context.setTransform(1, 0, 0, 1, 0, 0);
+ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ };
+ /**
+ * Resizes the canvas to the specified width and height.
+ * @param desiredWidth - the desired width of the canvas
+ * @param desiredHeight - the desired height of the canvas
+ */
+ CanvasRenderTarget.prototype.resize = function (desiredWidth, desiredHeight) {
+ this.canvas.width = Math.round(desiredWidth * this.resolution);
+ this.canvas.height = Math.round(desiredHeight * this.resolution);
+ };
+ /** Destroys this canvas. */
+ CanvasRenderTarget.prototype.destroy = function () {
+ this.context = null;
+ this.canvas = null;
+ };
+ Object.defineProperty(CanvasRenderTarget.prototype, "width", {
+ /**
+ * The width of the canvas buffer in pixels.
+ * @member {number}
+ */
+ get: function () {
+ return this.canvas.width;
+ },
+ set: function (val) {
+ this.canvas.width = Math.round(val);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(CanvasRenderTarget.prototype, "height", {
+ /**
+ * The height of the canvas buffer in pixels.
+ * @member {number}
+ */
+ get: function () {
+ return this.canvas.height;
+ },
+ set: function (val) {
+ this.canvas.height = Math.round(val);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return CanvasRenderTarget;
+ }());
+
+ /**
+ * Trim transparent borders from a canvas
+ * @memberof PIXI.utils
+ * @function trimCanvas
+ * @param {HTMLCanvasElement} canvas - the canvas to trim
+ * @returns {object} Trim data
+ */
+ function trimCanvas(canvas) {
+ // https://gist.github.com/remy/784508
+ var width = canvas.width;
+ var height = canvas.height;
+ var context = canvas.getContext('2d');
+ var imageData = context.getImageData(0, 0, width, height);
+ var pixels = imageData.data;
+ var len = pixels.length;
+ var bound = {
+ top: null,
+ left: null,
+ right: null,
+ bottom: null,
+ };
+ var data = null;
+ var i;
+ var x;
+ var y;
+ for (i = 0; i < len; i += 4) {
+ if (pixels[i + 3] !== 0) {
+ x = (i / 4) % width;
+ y = ~~((i / 4) / width);
+ if (bound.top === null) {
+ bound.top = y;
+ }
+ if (bound.left === null) {
+ bound.left = x;
+ }
+ else if (x < bound.left) {
+ bound.left = x;
+ }
+ if (bound.right === null) {
+ bound.right = x + 1;
+ }
+ else if (bound.right < x) {
+ bound.right = x + 1;
+ }
+ if (bound.bottom === null) {
+ bound.bottom = y;
+ }
+ else if (bound.bottom < y) {
+ bound.bottom = y;
+ }
+ }
+ }
+ if (bound.top !== null) {
+ width = bound.right - bound.left;
+ height = bound.bottom - bound.top + 1;
+ data = context.getImageData(bound.left, bound.top, width, height);
+ }
+ return {
+ height: height,
+ width: width,
+ data: data,
+ };
+ }
+
+ /**
+ * Regexp for data URI.
+ * Based on: {@link https://github.com/ragingwind/data-uri-regex}
+ * @static
+ * @constant {RegExp|string} DATA_URI
+ * @memberof PIXI
+ * @example data:image/png;base64
+ */
+ var DATA_URI = /^\s*data:(?:([\w-]+)\/([\w+.-]+))?(?:;charset=([\w-]+))?(?:;(base64))?,(.*)/i;
+
+ /**
+ * @memberof PIXI.utils
+ * @interface DecomposedDataUri
+ */
+ /**
+ * type, eg. `image`
+ * @memberof PIXI.utils.DecomposedDataUri#
+ * @member {string} mediaType
+ */
+ /**
+ * Sub type, eg. `png`
+ * @memberof PIXI.utils.DecomposedDataUri#
+ * @member {string} subType
+ */
+ /**
+ * @memberof PIXI.utils.DecomposedDataUri#
+ * @member {string} charset
+ */
+ /**
+ * Data encoding, eg. `base64`
+ * @memberof PIXI.utils.DecomposedDataUri#
+ * @member {string} encoding
+ */
+ /**
+ * The actual data
+ * @memberof PIXI.utils.DecomposedDataUri#
+ * @member {string} data
+ */
+ /**
+ * Split a data URI into components. Returns undefined if
+ * parameter `dataUri` is not a valid data URI.
+ * @memberof PIXI.utils
+ * @function decomposeDataUri
+ * @param {string} dataUri - the data URI to check
+ * @returns {PIXI.utils.DecomposedDataUri|undefined} The decomposed data uri or undefined
+ */
+ function decomposeDataUri(dataUri) {
+ var dataUriMatch = DATA_URI.exec(dataUri);
+ if (dataUriMatch) {
+ return {
+ mediaType: dataUriMatch[1] ? dataUriMatch[1].toLowerCase() : undefined,
+ subType: dataUriMatch[2] ? dataUriMatch[2].toLowerCase() : undefined,
+ charset: dataUriMatch[3] ? dataUriMatch[3].toLowerCase() : undefined,
+ encoding: dataUriMatch[4] ? dataUriMatch[4].toLowerCase() : undefined,
+ data: dataUriMatch[5],
+ };
+ }
+ return undefined;
+ }
+
+ var tempAnchor$1;
+ /**
+ * Sets the `crossOrigin` property for this resource based on if the url
+ * for this resource is cross-origin. If crossOrigin was manually set, this
+ * function does nothing.
+ * Nipped from the resource loader!
+ * @ignore
+ * @param {string} url - The url to test.
+ * @param {object} [loc=window.location] - The location object to test against.
+ * @returns {string} The crossOrigin value to use (or empty string for none).
+ */
+ function determineCrossOrigin(url$1, loc) {
+ if (loc === void 0) { loc = globalThis.location; }
+ // data: and javascript: urls are considered same-origin
+ if (url$1.indexOf('data:') === 0) {
+ return '';
+ }
+ // default is window.location
+ loc = loc || globalThis.location;
+ if (!tempAnchor$1) {
+ tempAnchor$1 = document.createElement('a');
+ }
+ // let the browser determine the full href for the url of this resource and then
+ // parse with the node url lib, we can't use the properties of the anchor element
+ // because they don't work in IE9 :(
+ tempAnchor$1.href = url$1;
+ var parsedUrl = url.parse(tempAnchor$1.href);
+ var samePort = (!parsedUrl.port && loc.port === '') || (parsedUrl.port === loc.port);
+ // if cross origin
+ if (parsedUrl.hostname !== loc.hostname || !samePort || parsedUrl.protocol !== loc.protocol) {
+ return 'anonymous';
+ }
+ return '';
+ }
+
+ /**
+ * get the resolution / device pixel ratio of an asset by looking for the prefix
+ * used by spritesheets and image urls
+ * @memberof PIXI.utils
+ * @function getResolutionOfUrl
+ * @param {string} url - the image path
+ * @param {number} [defaultValue=1] - the defaultValue if no filename prefix is set.
+ * @returns {number} resolution / device pixel ratio of an asset
+ */
+ function getResolutionOfUrl(url, defaultValue) {
+ var resolution = settings$1.RETINA_PREFIX.exec(url);
+ if (resolution) {
+ return parseFloat(resolution[1]);
+ }
+ return defaultValue !== undefined ? defaultValue : 1;
+ }
+
+ var utils = {
+ __proto__: null,
+ BaseTextureCache: BaseTextureCache,
+ CanvasRenderTarget: CanvasRenderTarget,
+ DATA_URI: DATA_URI,
+ ProgramCache: ProgramCache,
+ TextureCache: TextureCache,
+ clearTextureCache: clearTextureCache,
+ correctBlendMode: correctBlendMode,
+ createIndicesForQuads: createIndicesForQuads,
+ decomposeDataUri: decomposeDataUri,
+ deprecation: deprecation$1,
+ destroyTextureCache: destroyTextureCache,
+ determineCrossOrigin: determineCrossOrigin,
+ getBufferType: getBufferType,
+ getResolutionOfUrl: getResolutionOfUrl,
+ hex2rgb: hex2rgb,
+ hex2string: hex2string,
+ interleaveTypedArrays: interleaveTypedArrays$1,
+ isPow2: isPow2,
+ isWebGLSupported: isWebGLSupported,
+ log2: log2,
+ nextPow2: nextPow2,
+ path: path,
+ premultiplyBlendMode: premultiplyBlendMode,
+ premultiplyRgba: premultiplyRgba,
+ premultiplyTint: premultiplyTint,
+ premultiplyTintToRgba: premultiplyTintToRgba,
+ removeItems: removeItems,
+ rgb2hex: rgb2hex,
+ sayHello: sayHello,
+ sign: sign,
+ skipHello: skipHello,
+ string2hex: string2hex,
+ trimCanvas: trimCanvas,
+ uid: uid,
+ url: url,
+ isMobile: isMobile$2,
+ EventEmitter: eventemitter3,
+ earcut: earcut_1
+ };
+
+ /*!
+ * @pixi/math - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/math is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+ /**
+ * Two Pi.
+ * @static
+ * @member {number}
+ * @memberof PIXI
+ */
+ var PI_2 = Math.PI * 2;
+ /**
+ * Conversion factor for converting radians to degrees.
+ * @static
+ * @member {number} RAD_TO_DEG
+ * @memberof PIXI
+ */
+ var RAD_TO_DEG = 180 / Math.PI;
+ /**
+ * Conversion factor for converting degrees to radians.
+ * @static
+ * @member {number}
+ * @memberof PIXI
+ */
+ var DEG_TO_RAD = Math.PI / 180;
+ /**
+ * Constants that identify shapes, mainly to prevent `instanceof` calls.
+ * @static
+ * @memberof PIXI
+ * @enum {number}
+ * @property {number} POLY Polygon
+ * @property {number} RECT Rectangle
+ * @property {number} CIRC Circle
+ * @property {number} ELIP Ellipse
+ * @property {number} RREC Rounded Rectangle
+ */
+ exports.SHAPES = void 0;
+ (function (SHAPES) {
+ SHAPES[SHAPES["POLY"] = 0] = "POLY";
+ SHAPES[SHAPES["RECT"] = 1] = "RECT";
+ SHAPES[SHAPES["CIRC"] = 2] = "CIRC";
+ SHAPES[SHAPES["ELIP"] = 3] = "ELIP";
+ SHAPES[SHAPES["RREC"] = 4] = "RREC";
+ })(exports.SHAPES || (exports.SHAPES = {}));
+
+ /**
+ * The Point object represents a location in a two-dimensional coordinate system, where `x` represents
+ * the position on the horizontal axis and `y` represents the position on the vertical axis
+ * @class
+ * @memberof PIXI
+ * @implements {IPoint}
+ */
+ var Point = /** @class */ (function () {
+ /**
+ * Creates a new `Point`
+ * @param {number} [x=0] - position of the point on the x axis
+ * @param {number} [y=0] - position of the point on the y axis
+ */
+ function Point(x, y) {
+ if (x === void 0) { x = 0; }
+ if (y === void 0) { y = 0; }
+ /** Position of the point on the x axis */
+ this.x = 0;
+ /** Position of the point on the y axis */
+ this.y = 0;
+ this.x = x;
+ this.y = y;
+ }
+ /**
+ * Creates a clone of this point
+ * @returns A clone of this point
+ */
+ Point.prototype.clone = function () {
+ return new Point(this.x, this.y);
+ };
+ /**
+ * Copies `x` and `y` from the given point into this point
+ * @param p - The point to copy from
+ * @returns The point instance itself
+ */
+ Point.prototype.copyFrom = function (p) {
+ this.set(p.x, p.y);
+ return this;
+ };
+ /**
+ * Copies this point's x and y into the given point (`p`).
+ * @param p - The point to copy to. Can be any of type that is or extends `IPointData`
+ * @returns The point (`p`) with values updated
+ */
+ Point.prototype.copyTo = function (p) {
+ p.set(this.x, this.y);
+ return p;
+ };
+ /**
+ * Accepts another point (`p`) and returns `true` if the given point is equal to this point
+ * @param p - The point to check
+ * @returns Returns `true` if both `x` and `y` are equal
+ */
+ Point.prototype.equals = function (p) {
+ return (p.x === this.x) && (p.y === this.y);
+ };
+ /**
+ * Sets the point to a new `x` and `y` position.
+ * If `y` is omitted, both `x` and `y` will be set to `x`.
+ * @param {number} [x=0] - position of the point on the `x` axis
+ * @param {number} [y=x] - position of the point on the `y` axis
+ * @returns The point instance itself
+ */
+ Point.prototype.set = function (x, y) {
+ if (x === void 0) { x = 0; }
+ if (y === void 0) { y = x; }
+ this.x = x;
+ this.y = y;
+ return this;
+ };
+ Point.prototype.toString = function () {
+ return "[@pixi/math:Point x=" + this.x + " y=" + this.y + "]";
+ };
+ return Point;
+ }());
+
+ var tempPoints$1 = [new Point(), new Point(), new Point(), new Point()];
+ /**
+ * Size object, contains width and height
+ * @memberof PIXI
+ * @typedef {object} ISize
+ * @property {number} width - Width component
+ * @property {number} height - Height component
+ */
+ /**
+ * Rectangle object is an area defined by its position, as indicated by its top-left corner
+ * point (x, y) and by its width and its height.
+ * @memberof PIXI
+ */
+ var Rectangle = /** @class */ (function () {
+ /**
+ * @param x - The X coordinate of the upper-left corner of the rectangle
+ * @param y - The Y coordinate of the upper-left corner of the rectangle
+ * @param width - The overall width of the rectangle
+ * @param height - The overall height of the rectangle
+ */
+ function Rectangle(x, y, width, height) {
+ if (x === void 0) { x = 0; }
+ if (y === void 0) { y = 0; }
+ if (width === void 0) { width = 0; }
+ if (height === void 0) { height = 0; }
+ this.x = Number(x);
+ this.y = Number(y);
+ this.width = Number(width);
+ this.height = Number(height);
+ this.type = exports.SHAPES.RECT;
+ }
+ Object.defineProperty(Rectangle.prototype, "left", {
+ /** Returns the left edge of the rectangle. */
+ get: function () {
+ return this.x;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Rectangle.prototype, "right", {
+ /** Returns the right edge of the rectangle. */
+ get: function () {
+ return this.x + this.width;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Rectangle.prototype, "top", {
+ /** Returns the top edge of the rectangle. */
+ get: function () {
+ return this.y;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Rectangle.prototype, "bottom", {
+ /** Returns the bottom edge of the rectangle. */
+ get: function () {
+ return this.y + this.height;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Rectangle, "EMPTY", {
+ /** A constant empty rectangle. */
+ get: function () {
+ return new Rectangle(0, 0, 0, 0);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Creates a clone of this Rectangle
+ * @returns a copy of the rectangle
+ */
+ Rectangle.prototype.clone = function () {
+ return new Rectangle(this.x, this.y, this.width, this.height);
+ };
+ /**
+ * Copies another rectangle to this one.
+ * @param rectangle - The rectangle to copy from.
+ * @returns Returns itself.
+ */
+ Rectangle.prototype.copyFrom = function (rectangle) {
+ this.x = rectangle.x;
+ this.y = rectangle.y;
+ this.width = rectangle.width;
+ this.height = rectangle.height;
+ return this;
+ };
+ /**
+ * Copies this rectangle to another one.
+ * @param rectangle - The rectangle to copy to.
+ * @returns Returns given parameter.
+ */
+ Rectangle.prototype.copyTo = function (rectangle) {
+ rectangle.x = this.x;
+ rectangle.y = this.y;
+ rectangle.width = this.width;
+ rectangle.height = this.height;
+ return rectangle;
+ };
+ /**
+ * Checks whether the x and y coordinates given are contained within this Rectangle
+ * @param x - The X coordinate of the point to test
+ * @param y - The Y coordinate of the point to test
+ * @returns Whether the x/y coordinates are within this Rectangle
+ */
+ Rectangle.prototype.contains = function (x, y) {
+ if (this.width <= 0 || this.height <= 0) {
+ return false;
+ }
+ if (x >= this.x && x < this.x + this.width) {
+ if (y >= this.y && y < this.y + this.height) {
+ return true;
+ }
+ }
+ return false;
+ };
+ /**
+ * Determines whether the `other` Rectangle transformed by `transform` intersects with `this` Rectangle object.
+ * Returns true only if the area of the intersection is >0, this means that Rectangles
+ * sharing a side are not overlapping. Another side effect is that an arealess rectangle
+ * (width or height equal to zero) can't intersect any other rectangle.
+ * @param {Rectangle} other - The Rectangle to intersect with `this`.
+ * @param {Matrix} transform - The transformation matrix of `other`.
+ * @returns {boolean} A value of `true` if the transformed `other` Rectangle intersects with `this`; otherwise `false`.
+ */
+ Rectangle.prototype.intersects = function (other, transform) {
+ if (!transform) {
+ var x0_1 = this.x < other.x ? other.x : this.x;
+ var x1_1 = this.right > other.right ? other.right : this.right;
+ if (x1_1 <= x0_1) {
+ return false;
+ }
+ var y0_1 = this.y < other.y ? other.y : this.y;
+ var y1_1 = this.bottom > other.bottom ? other.bottom : this.bottom;
+ return y1_1 > y0_1;
+ }
+ var x0 = this.left;
+ var x1 = this.right;
+ var y0 = this.top;
+ var y1 = this.bottom;
+ if (x1 <= x0 || y1 <= y0) {
+ return false;
+ }
+ var lt = tempPoints$1[0].set(other.left, other.top);
+ var lb = tempPoints$1[1].set(other.left, other.bottom);
+ var rt = tempPoints$1[2].set(other.right, other.top);
+ var rb = tempPoints$1[3].set(other.right, other.bottom);
+ if (rt.x <= lt.x || lb.y <= lt.y) {
+ return false;
+ }
+ var s = Math.sign((transform.a * transform.d) - (transform.b * transform.c));
+ if (s === 0) {
+ return false;
+ }
+ transform.apply(lt, lt);
+ transform.apply(lb, lb);
+ transform.apply(rt, rt);
+ transform.apply(rb, rb);
+ if (Math.max(lt.x, lb.x, rt.x, rb.x) <= x0
+ || Math.min(lt.x, lb.x, rt.x, rb.x) >= x1
+ || Math.max(lt.y, lb.y, rt.y, rb.y) <= y0
+ || Math.min(lt.y, lb.y, rt.y, rb.y) >= y1) {
+ return false;
+ }
+ var nx = s * (lb.y - lt.y);
+ var ny = s * (lt.x - lb.x);
+ var n00 = (nx * x0) + (ny * y0);
+ var n10 = (nx * x1) + (ny * y0);
+ var n01 = (nx * x0) + (ny * y1);
+ var n11 = (nx * x1) + (ny * y1);
+ if (Math.max(n00, n10, n01, n11) <= (nx * lt.x) + (ny * lt.y)
+ || Math.min(n00, n10, n01, n11) >= (nx * rb.x) + (ny * rb.y)) {
+ return false;
+ }
+ var mx = s * (lt.y - rt.y);
+ var my = s * (rt.x - lt.x);
+ var m00 = (mx * x0) + (my * y0);
+ var m10 = (mx * x1) + (my * y0);
+ var m01 = (mx * x0) + (my * y1);
+ var m11 = (mx * x1) + (my * y1);
+ if (Math.max(m00, m10, m01, m11) <= (mx * lt.x) + (my * lt.y)
+ || Math.min(m00, m10, m01, m11) >= (mx * rb.x) + (my * rb.y)) {
+ return false;
+ }
+ return true;
+ };
+ /**
+ * Pads the rectangle making it grow in all directions.
+ * If paddingY is omitted, both paddingX and paddingY will be set to paddingX.
+ * @param paddingX - The horizontal padding amount.
+ * @param paddingY - The vertical padding amount.
+ * @returns Returns itself.
+ */
+ Rectangle.prototype.pad = function (paddingX, paddingY) {
+ if (paddingX === void 0) { paddingX = 0; }
+ if (paddingY === void 0) { paddingY = paddingX; }
+ this.x -= paddingX;
+ this.y -= paddingY;
+ this.width += paddingX * 2;
+ this.height += paddingY * 2;
+ return this;
+ };
+ /**
+ * Fits this rectangle around the passed one.
+ * @param rectangle - The rectangle to fit.
+ * @returns Returns itself.
+ */
+ Rectangle.prototype.fit = function (rectangle) {
+ var x1 = Math.max(this.x, rectangle.x);
+ var x2 = Math.min(this.x + this.width, rectangle.x + rectangle.width);
+ var y1 = Math.max(this.y, rectangle.y);
+ var y2 = Math.min(this.y + this.height, rectangle.y + rectangle.height);
+ this.x = x1;
+ this.width = Math.max(x2 - x1, 0);
+ this.y = y1;
+ this.height = Math.max(y2 - y1, 0);
+ return this;
+ };
+ /**
+ * Enlarges rectangle that way its corners lie on grid
+ * @param resolution - resolution
+ * @param eps - precision
+ * @returns Returns itself.
+ */
+ Rectangle.prototype.ceil = function (resolution, eps) {
+ if (resolution === void 0) { resolution = 1; }
+ if (eps === void 0) { eps = 0.001; }
+ var x2 = Math.ceil((this.x + this.width - eps) * resolution) / resolution;
+ var y2 = Math.ceil((this.y + this.height - eps) * resolution) / resolution;
+ this.x = Math.floor((this.x + eps) * resolution) / resolution;
+ this.y = Math.floor((this.y + eps) * resolution) / resolution;
+ this.width = x2 - this.x;
+ this.height = y2 - this.y;
+ return this;
+ };
+ /**
+ * Enlarges this rectangle to include the passed rectangle.
+ * @param rectangle - The rectangle to include.
+ * @returns Returns itself.
+ */
+ Rectangle.prototype.enlarge = function (rectangle) {
+ var x1 = Math.min(this.x, rectangle.x);
+ var x2 = Math.max(this.x + this.width, rectangle.x + rectangle.width);
+ var y1 = Math.min(this.y, rectangle.y);
+ var y2 = Math.max(this.y + this.height, rectangle.y + rectangle.height);
+ this.x = x1;
+ this.width = x2 - x1;
+ this.y = y1;
+ this.height = y2 - y1;
+ return this;
+ };
+ Rectangle.prototype.toString = function () {
+ return "[@pixi/math:Rectangle x=" + this.x + " y=" + this.y + " width=" + this.width + " height=" + this.height + "]";
+ };
+ return Rectangle;
+ }());
+
+ /**
+ * The Circle object is used to help draw graphics and can also be used to specify a hit area for displayObjects.
+ * @memberof PIXI
+ */
+ var Circle = /** @class */ (function () {
+ /**
+ * @param x - The X coordinate of the center of this circle
+ * @param y - The Y coordinate of the center of this circle
+ * @param radius - The radius of the circle
+ */
+ function Circle(x, y, radius) {
+ if (x === void 0) { x = 0; }
+ if (y === void 0) { y = 0; }
+ if (radius === void 0) { radius = 0; }
+ this.x = x;
+ this.y = y;
+ this.radius = radius;
+ this.type = exports.SHAPES.CIRC;
+ }
+ /**
+ * Creates a clone of this Circle instance
+ * @returns A copy of the Circle
+ */
+ Circle.prototype.clone = function () {
+ return new Circle(this.x, this.y, this.radius);
+ };
+ /**
+ * Checks whether the x and y coordinates given are contained within this circle
+ * @param x - The X coordinate of the point to test
+ * @param y - The Y coordinate of the point to test
+ * @returns Whether the x/y coordinates are within this Circle
+ */
+ Circle.prototype.contains = function (x, y) {
+ if (this.radius <= 0) {
+ return false;
+ }
+ var r2 = this.radius * this.radius;
+ var dx = (this.x - x);
+ var dy = (this.y - y);
+ dx *= dx;
+ dy *= dy;
+ return (dx + dy <= r2);
+ };
+ /**
+ * Returns the framing rectangle of the circle as a Rectangle object
+ * @returns The framing rectangle
+ */
+ Circle.prototype.getBounds = function () {
+ return new Rectangle(this.x - this.radius, this.y - this.radius, this.radius * 2, this.radius * 2);
+ };
+ Circle.prototype.toString = function () {
+ return "[@pixi/math:Circle x=" + this.x + " y=" + this.y + " radius=" + this.radius + "]";
+ };
+ return Circle;
+ }());
+
+ /**
+ * The Ellipse object is used to help draw graphics and can also be used to specify a hit area for displayObjects.
+ * @memberof PIXI
+ */
+ var Ellipse = /** @class */ (function () {
+ /**
+ * @param x - The X coordinate of the center of this ellipse
+ * @param y - The Y coordinate of the center of this ellipse
+ * @param halfWidth - The half width of this ellipse
+ * @param halfHeight - The half height of this ellipse
+ */
+ function Ellipse(x, y, halfWidth, halfHeight) {
+ if (x === void 0) { x = 0; }
+ if (y === void 0) { y = 0; }
+ if (halfWidth === void 0) { halfWidth = 0; }
+ if (halfHeight === void 0) { halfHeight = 0; }
+ this.x = x;
+ this.y = y;
+ this.width = halfWidth;
+ this.height = halfHeight;
+ this.type = exports.SHAPES.ELIP;
+ }
+ /**
+ * Creates a clone of this Ellipse instance
+ * @returns {PIXI.Ellipse} A copy of the ellipse
+ */
+ Ellipse.prototype.clone = function () {
+ return new Ellipse(this.x, this.y, this.width, this.height);
+ };
+ /**
+ * Checks whether the x and y coordinates given are contained within this ellipse
+ * @param x - The X coordinate of the point to test
+ * @param y - The Y coordinate of the point to test
+ * @returns Whether the x/y coords are within this ellipse
+ */
+ Ellipse.prototype.contains = function (x, y) {
+ if (this.width <= 0 || this.height <= 0) {
+ return false;
+ }
+ // normalize the coords to an ellipse with center 0,0
+ var normx = ((x - this.x) / this.width);
+ var normy = ((y - this.y) / this.height);
+ normx *= normx;
+ normy *= normy;
+ return (normx + normy <= 1);
+ };
+ /**
+ * Returns the framing rectangle of the ellipse as a Rectangle object
+ * @returns The framing rectangle
+ */
+ Ellipse.prototype.getBounds = function () {
+ return new Rectangle(this.x - this.width, this.y - this.height, this.width, this.height);
+ };
+ Ellipse.prototype.toString = function () {
+ return "[@pixi/math:Ellipse x=" + this.x + " y=" + this.y + " width=" + this.width + " height=" + this.height + "]";
+ };
+ return Ellipse;
+ }());
+
+ /**
+ * A class to define a shape via user defined coordinates.
+ * @memberof PIXI
+ */
+ var Polygon = /** @class */ (function () {
+ /**
+ * @param {PIXI.IPointData[]|number[]} points - This can be an array of Points
+ * that form the polygon, a flat array of numbers that will be interpreted as [x,y, x,y, ...], or
+ * the arguments passed can be all the points of the polygon e.g.
+ * `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the arguments passed can be flat
+ * x,y values e.g. `new Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are Numbers.
+ */
+ function Polygon() {
+ var arguments$1 = arguments;
+
+ var points = [];
+ for (var _i = 0; _i < arguments.length; _i++) {
+ points[_i] = arguments$1[_i];
+ }
+ var flat = Array.isArray(points[0]) ? points[0] : points;
+ // if this is an array of points, convert it to a flat array of numbers
+ if (typeof flat[0] !== 'number') {
+ var p = [];
+ for (var i = 0, il = flat.length; i < il; i++) {
+ p.push(flat[i].x, flat[i].y);
+ }
+ flat = p;
+ }
+ this.points = flat;
+ this.type = exports.SHAPES.POLY;
+ this.closeStroke = true;
+ }
+ /**
+ * Creates a clone of this polygon.
+ * @returns - A copy of the polygon.
+ */
+ Polygon.prototype.clone = function () {
+ var points = this.points.slice();
+ var polygon = new Polygon(points);
+ polygon.closeStroke = this.closeStroke;
+ return polygon;
+ };
+ /**
+ * Checks whether the x and y coordinates passed to this function are contained within this polygon.
+ * @param x - The X coordinate of the point to test.
+ * @param y - The Y coordinate of the point to test.
+ * @returns - Whether the x/y coordinates are within this polygon.
+ */
+ Polygon.prototype.contains = function (x, y) {
+ var inside = false;
+ // use some raycasting to test hits
+ // https://github.com/substack/point-in-polygon/blob/master/index.js
+ var length = this.points.length / 2;
+ for (var i = 0, j = length - 1; i < length; j = i++) {
+ var xi = this.points[i * 2];
+ var yi = this.points[(i * 2) + 1];
+ var xj = this.points[j * 2];
+ var yj = this.points[(j * 2) + 1];
+ var intersect = ((yi > y) !== (yj > y)) && (x < ((xj - xi) * ((y - yi) / (yj - yi))) + xi);
+ if (intersect) {
+ inside = !inside;
+ }
+ }
+ return inside;
+ };
+ Polygon.prototype.toString = function () {
+ return "[@pixi/math:Polygon"
+ + ("closeStroke=" + this.closeStroke)
+ + ("points=" + this.points.reduce(function (pointsDesc, currentPoint) { return pointsDesc + ", " + currentPoint; }, '') + "]");
+ };
+ return Polygon;
+ }());
+
+ /**
+ * The Rounded Rectangle object is an area that has nice rounded corners, as indicated by its
+ * top-left corner point (x, y) and by its width and its height and its radius.
+ * @memberof PIXI
+ */
+ var RoundedRectangle = /** @class */ (function () {
+ /**
+ * @param x - The X coordinate of the upper-left corner of the rounded rectangle
+ * @param y - The Y coordinate of the upper-left corner of the rounded rectangle
+ * @param width - The overall width of this rounded rectangle
+ * @param height - The overall height of this rounded rectangle
+ * @param radius - Controls the radius of the rounded corners
+ */
+ function RoundedRectangle(x, y, width, height, radius) {
+ if (x === void 0) { x = 0; }
+ if (y === void 0) { y = 0; }
+ if (width === void 0) { width = 0; }
+ if (height === void 0) { height = 0; }
+ if (radius === void 0) { radius = 20; }
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+ this.radius = radius;
+ this.type = exports.SHAPES.RREC;
+ }
+ /**
+ * Creates a clone of this Rounded Rectangle.
+ * @returns - A copy of the rounded rectangle.
+ */
+ RoundedRectangle.prototype.clone = function () {
+ return new RoundedRectangle(this.x, this.y, this.width, this.height, this.radius);
+ };
+ /**
+ * Checks whether the x and y coordinates given are contained within this Rounded Rectangle
+ * @param x - The X coordinate of the point to test.
+ * @param y - The Y coordinate of the point to test.
+ * @returns - Whether the x/y coordinates are within this Rounded Rectangle.
+ */
+ RoundedRectangle.prototype.contains = function (x, y) {
+ if (this.width <= 0 || this.height <= 0) {
+ return false;
+ }
+ if (x >= this.x && x <= this.x + this.width) {
+ if (y >= this.y && y <= this.y + this.height) {
+ var radius = Math.max(0, Math.min(this.radius, Math.min(this.width, this.height) / 2));
+ if ((y >= this.y + radius && y <= this.y + this.height - radius)
+ || (x >= this.x + radius && x <= this.x + this.width - radius)) {
+ return true;
+ }
+ var dx = x - (this.x + radius);
+ var dy = y - (this.y + radius);
+ var radius2 = radius * radius;
+ if ((dx * dx) + (dy * dy) <= radius2) {
+ return true;
+ }
+ dx = x - (this.x + this.width - radius);
+ if ((dx * dx) + (dy * dy) <= radius2) {
+ return true;
+ }
+ dy = y - (this.y + this.height - radius);
+ if ((dx * dx) + (dy * dy) <= radius2) {
+ return true;
+ }
+ dx = x - (this.x + radius);
+ if ((dx * dx) + (dy * dy) <= radius2) {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+ RoundedRectangle.prototype.toString = function () {
+ return "[@pixi/math:RoundedRectangle x=" + this.x + " y=" + this.y
+ + ("width=" + this.width + " height=" + this.height + " radius=" + this.radius + "]");
+ };
+ return RoundedRectangle;
+ }());
+
+ /**
+ * The ObservablePoint object represents a location in a two-dimensional coordinate system, where `x` represents
+ * the position on the horizontal axis and `y` represents the position on the vertical axis.
+ *
+ * An `ObservablePoint` is a point that triggers a callback when the point's position is changed.
+ * @memberof PIXI
+ */
+ var ObservablePoint = /** @class */ (function () {
+ /**
+ * Creates a new `ObservablePoint`
+ * @param cb - callback function triggered when `x` and/or `y` are changed
+ * @param scope - owner of callback
+ * @param {number} [x=0] - position of the point on the x axis
+ * @param {number} [y=0] - position of the point on the y axis
+ */
+ function ObservablePoint(cb, scope, x, y) {
+ if (x === void 0) { x = 0; }
+ if (y === void 0) { y = 0; }
+ this._x = x;
+ this._y = y;
+ this.cb = cb;
+ this.scope = scope;
+ }
+ /**
+ * Creates a clone of this point.
+ * The callback and scope params can be overridden otherwise they will default
+ * to the clone object's values.
+ * @override
+ * @param cb - The callback function triggered when `x` and/or `y` are changed
+ * @param scope - The owner of the callback
+ * @returns a copy of this observable point
+ */
+ ObservablePoint.prototype.clone = function (cb, scope) {
+ if (cb === void 0) { cb = this.cb; }
+ if (scope === void 0) { scope = this.scope; }
+ return new ObservablePoint(cb, scope, this._x, this._y);
+ };
+ /**
+ * Sets the point to a new `x` and `y` position.
+ * If `y` is omitted, both `x` and `y` will be set to `x`.
+ * @param {number} [x=0] - position of the point on the x axis
+ * @param {number} [y=x] - position of the point on the y axis
+ * @returns The observable point instance itself
+ */
+ ObservablePoint.prototype.set = function (x, y) {
+ if (x === void 0) { x = 0; }
+ if (y === void 0) { y = x; }
+ if (this._x !== x || this._y !== y) {
+ this._x = x;
+ this._y = y;
+ this.cb.call(this.scope);
+ }
+ return this;
+ };
+ /**
+ * Copies x and y from the given point (`p`)
+ * @param p - The point to copy from. Can be any of type that is or extends `IPointData`
+ * @returns The observable point instance itself
+ */
+ ObservablePoint.prototype.copyFrom = function (p) {
+ if (this._x !== p.x || this._y !== p.y) {
+ this._x = p.x;
+ this._y = p.y;
+ this.cb.call(this.scope);
+ }
+ return this;
+ };
+ /**
+ * Copies this point's x and y into that of the given point (`p`)
+ * @param p - The point to copy to. Can be any of type that is or extends `IPointData`
+ * @returns The point (`p`) with values updated
+ */
+ ObservablePoint.prototype.copyTo = function (p) {
+ p.set(this._x, this._y);
+ return p;
+ };
+ /**
+ * Accepts another point (`p`) and returns `true` if the given point is equal to this point
+ * @param p - The point to check
+ * @returns Returns `true` if both `x` and `y` are equal
+ */
+ ObservablePoint.prototype.equals = function (p) {
+ return (p.x === this._x) && (p.y === this._y);
+ };
+ ObservablePoint.prototype.toString = function () {
+ return "[@pixi/math:ObservablePoint x=" + 0 + " y=" + 0 + " scope=" + this.scope + "]";
+ };
+ Object.defineProperty(ObservablePoint.prototype, "x", {
+ /** Position of the observable point on the x axis. */
+ get: function () {
+ return this._x;
+ },
+ set: function (value) {
+ if (this._x !== value) {
+ this._x = value;
+ this.cb.call(this.scope);
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(ObservablePoint.prototype, "y", {
+ /** Position of the observable point on the y axis. */
+ get: function () {
+ return this._y;
+ },
+ set: function (value) {
+ if (this._y !== value) {
+ this._y = value;
+ this.cb.call(this.scope);
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return ObservablePoint;
+ }());
+
+ /**
+ * The PixiJS Matrix as a class makes it a lot faster.
+ *
+ * Here is a representation of it:
+ * ```js
+ * | a | c | tx|
+ * | b | d | ty|
+ * | 0 | 0 | 1 |
+ * ```
+ * @memberof PIXI
+ */
+ var Matrix = /** @class */ (function () {
+ /**
+ * @param a - x scale
+ * @param b - y skew
+ * @param c - x skew
+ * @param d - y scale
+ * @param tx - x translation
+ * @param ty - y translation
+ */
+ function Matrix(a, b, c, d, tx, ty) {
+ if (a === void 0) { a = 1; }
+ if (b === void 0) { b = 0; }
+ if (c === void 0) { c = 0; }
+ if (d === void 0) { d = 1; }
+ if (tx === void 0) { tx = 0; }
+ if (ty === void 0) { ty = 0; }
+ this.array = null;
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ this.d = d;
+ this.tx = tx;
+ this.ty = ty;
+ }
+ /**
+ * Creates a Matrix object based on the given array. The Element to Matrix mapping order is as follows:
+ *
+ * a = array[0]
+ * b = array[1]
+ * c = array[3]
+ * d = array[4]
+ * tx = array[2]
+ * ty = array[5]
+ * @param array - The array that the matrix will be populated from.
+ */
+ Matrix.prototype.fromArray = function (array) {
+ this.a = array[0];
+ this.b = array[1];
+ this.c = array[3];
+ this.d = array[4];
+ this.tx = array[2];
+ this.ty = array[5];
+ };
+ /**
+ * Sets the matrix properties.
+ * @param a - Matrix component
+ * @param b - Matrix component
+ * @param c - Matrix component
+ * @param d - Matrix component
+ * @param tx - Matrix component
+ * @param ty - Matrix component
+ * @returns This matrix. Good for chaining method calls.
+ */
+ Matrix.prototype.set = function (a, b, c, d, tx, ty) {
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ this.d = d;
+ this.tx = tx;
+ this.ty = ty;
+ return this;
+ };
+ /**
+ * Creates an array from the current Matrix object.
+ * @param transpose - Whether we need to transpose the matrix or not
+ * @param [out=new Float32Array(9)] - If provided the array will be assigned to out
+ * @returns The newly created array which contains the matrix
+ */
+ Matrix.prototype.toArray = function (transpose, out) {
+ if (!this.array) {
+ this.array = new Float32Array(9);
+ }
+ var array = out || this.array;
+ if (transpose) {
+ array[0] = this.a;
+ array[1] = this.b;
+ array[2] = 0;
+ array[3] = this.c;
+ array[4] = this.d;
+ array[5] = 0;
+ array[6] = this.tx;
+ array[7] = this.ty;
+ array[8] = 1;
+ }
+ else {
+ array[0] = this.a;
+ array[1] = this.c;
+ array[2] = this.tx;
+ array[3] = this.b;
+ array[4] = this.d;
+ array[5] = this.ty;
+ array[6] = 0;
+ array[7] = 0;
+ array[8] = 1;
+ }
+ return array;
+ };
+ /**
+ * Get a new position with the current transformation applied.
+ * Can be used to go from a child's coordinate space to the world coordinate space. (e.g. rendering)
+ * @param pos - The origin
+ * @param {PIXI.Point} [newPos] - The point that the new position is assigned to (allowed to be same as input)
+ * @returns {PIXI.Point} The new point, transformed through this matrix
+ */
+ Matrix.prototype.apply = function (pos, newPos) {
+ newPos = (newPos || new Point());
+ var x = pos.x;
+ var y = pos.y;
+ newPos.x = (this.a * x) + (this.c * y) + this.tx;
+ newPos.y = (this.b * x) + (this.d * y) + this.ty;
+ return newPos;
+ };
+ /**
+ * Get a new position with the inverse of the current transformation applied.
+ * Can be used to go from the world coordinate space to a child's coordinate space. (e.g. input)
+ * @param pos - The origin
+ * @param {PIXI.Point} [newPos] - The point that the new position is assigned to (allowed to be same as input)
+ * @returns {PIXI.Point} The new point, inverse-transformed through this matrix
+ */
+ Matrix.prototype.applyInverse = function (pos, newPos) {
+ newPos = (newPos || new Point());
+ var id = 1 / ((this.a * this.d) + (this.c * -this.b));
+ var x = pos.x;
+ var y = pos.y;
+ newPos.x = (this.d * id * x) + (-this.c * id * y) + (((this.ty * this.c) - (this.tx * this.d)) * id);
+ newPos.y = (this.a * id * y) + (-this.b * id * x) + (((-this.ty * this.a) + (this.tx * this.b)) * id);
+ return newPos;
+ };
+ /**
+ * Translates the matrix on the x and y.
+ * @param x - How much to translate x by
+ * @param y - How much to translate y by
+ * @returns This matrix. Good for chaining method calls.
+ */
+ Matrix.prototype.translate = function (x, y) {
+ this.tx += x;
+ this.ty += y;
+ return this;
+ };
+ /**
+ * Applies a scale transformation to the matrix.
+ * @param x - The amount to scale horizontally
+ * @param y - The amount to scale vertically
+ * @returns This matrix. Good for chaining method calls.
+ */
+ Matrix.prototype.scale = function (x, y) {
+ this.a *= x;
+ this.d *= y;
+ this.c *= x;
+ this.b *= y;
+ this.tx *= x;
+ this.ty *= y;
+ return this;
+ };
+ /**
+ * Applies a rotation transformation to the matrix.
+ * @param angle - The angle in radians.
+ * @returns This matrix. Good for chaining method calls.
+ */
+ Matrix.prototype.rotate = function (angle) {
+ var cos = Math.cos(angle);
+ var sin = Math.sin(angle);
+ var a1 = this.a;
+ var c1 = this.c;
+ var tx1 = this.tx;
+ this.a = (a1 * cos) - (this.b * sin);
+ this.b = (a1 * sin) + (this.b * cos);
+ this.c = (c1 * cos) - (this.d * sin);
+ this.d = (c1 * sin) + (this.d * cos);
+ this.tx = (tx1 * cos) - (this.ty * sin);
+ this.ty = (tx1 * sin) + (this.ty * cos);
+ return this;
+ };
+ /**
+ * Appends the given Matrix to this Matrix.
+ * @param matrix - The matrix to append.
+ * @returns This matrix. Good for chaining method calls.
+ */
+ Matrix.prototype.append = function (matrix) {
+ var a1 = this.a;
+ var b1 = this.b;
+ var c1 = this.c;
+ var d1 = this.d;
+ this.a = (matrix.a * a1) + (matrix.b * c1);
+ this.b = (matrix.a * b1) + (matrix.b * d1);
+ this.c = (matrix.c * a1) + (matrix.d * c1);
+ this.d = (matrix.c * b1) + (matrix.d * d1);
+ this.tx = (matrix.tx * a1) + (matrix.ty * c1) + this.tx;
+ this.ty = (matrix.tx * b1) + (matrix.ty * d1) + this.ty;
+ return this;
+ };
+ /**
+ * Sets the matrix based on all the available properties
+ * @param x - Position on the x axis
+ * @param y - Position on the y axis
+ * @param pivotX - Pivot on the x axis
+ * @param pivotY - Pivot on the y axis
+ * @param scaleX - Scale on the x axis
+ * @param scaleY - Scale on the y axis
+ * @param rotation - Rotation in radians
+ * @param skewX - Skew on the x axis
+ * @param skewY - Skew on the y axis
+ * @returns This matrix. Good for chaining method calls.
+ */
+ Matrix.prototype.setTransform = function (x, y, pivotX, pivotY, scaleX, scaleY, rotation, skewX, skewY) {
+ this.a = Math.cos(rotation + skewY) * scaleX;
+ this.b = Math.sin(rotation + skewY) * scaleX;
+ this.c = -Math.sin(rotation - skewX) * scaleY;
+ this.d = Math.cos(rotation - skewX) * scaleY;
+ this.tx = x - ((pivotX * this.a) + (pivotY * this.c));
+ this.ty = y - ((pivotX * this.b) + (pivotY * this.d));
+ return this;
+ };
+ /**
+ * Prepends the given Matrix to this Matrix.
+ * @param matrix - The matrix to prepend
+ * @returns This matrix. Good for chaining method calls.
+ */
+ Matrix.prototype.prepend = function (matrix) {
+ var tx1 = this.tx;
+ if (matrix.a !== 1 || matrix.b !== 0 || matrix.c !== 0 || matrix.d !== 1) {
+ var a1 = this.a;
+ var c1 = this.c;
+ this.a = (a1 * matrix.a) + (this.b * matrix.c);
+ this.b = (a1 * matrix.b) + (this.b * matrix.d);
+ this.c = (c1 * matrix.a) + (this.d * matrix.c);
+ this.d = (c1 * matrix.b) + (this.d * matrix.d);
+ }
+ this.tx = (tx1 * matrix.a) + (this.ty * matrix.c) + matrix.tx;
+ this.ty = (tx1 * matrix.b) + (this.ty * matrix.d) + matrix.ty;
+ return this;
+ };
+ /**
+ * Decomposes the matrix (x, y, scaleX, scaleY, and rotation) and sets the properties on to a transform.
+ * @param transform - The transform to apply the properties to.
+ * @returns The transform with the newly applied properties
+ */
+ Matrix.prototype.decompose = function (transform) {
+ // sort out rotation / skew..
+ var a = this.a;
+ var b = this.b;
+ var c = this.c;
+ var d = this.d;
+ var pivot = transform.pivot;
+ var skewX = -Math.atan2(-c, d);
+ var skewY = Math.atan2(b, a);
+ var delta = Math.abs(skewX + skewY);
+ if (delta < 0.00001 || Math.abs(PI_2 - delta) < 0.00001) {
+ transform.rotation = skewY;
+ transform.skew.x = transform.skew.y = 0;
+ }
+ else {
+ transform.rotation = 0;
+ transform.skew.x = skewX;
+ transform.skew.y = skewY;
+ }
+ // next set scale
+ transform.scale.x = Math.sqrt((a * a) + (b * b));
+ transform.scale.y = Math.sqrt((c * c) + (d * d));
+ // next set position
+ transform.position.x = this.tx + ((pivot.x * a) + (pivot.y * c));
+ transform.position.y = this.ty + ((pivot.x * b) + (pivot.y * d));
+ return transform;
+ };
+ /**
+ * Inverts this matrix
+ * @returns This matrix. Good for chaining method calls.
+ */
+ Matrix.prototype.invert = function () {
+ var a1 = this.a;
+ var b1 = this.b;
+ var c1 = this.c;
+ var d1 = this.d;
+ var tx1 = this.tx;
+ var n = (a1 * d1) - (b1 * c1);
+ this.a = d1 / n;
+ this.b = -b1 / n;
+ this.c = -c1 / n;
+ this.d = a1 / n;
+ this.tx = ((c1 * this.ty) - (d1 * tx1)) / n;
+ this.ty = -((a1 * this.ty) - (b1 * tx1)) / n;
+ return this;
+ };
+ /**
+ * Resets this Matrix to an identity (default) matrix.
+ * @returns This matrix. Good for chaining method calls.
+ */
+ Matrix.prototype.identity = function () {
+ this.a = 1;
+ this.b = 0;
+ this.c = 0;
+ this.d = 1;
+ this.tx = 0;
+ this.ty = 0;
+ return this;
+ };
+ /**
+ * Creates a new Matrix object with the same values as this one.
+ * @returns A copy of this matrix. Good for chaining method calls.
+ */
+ Matrix.prototype.clone = function () {
+ var matrix = new Matrix();
+ matrix.a = this.a;
+ matrix.b = this.b;
+ matrix.c = this.c;
+ matrix.d = this.d;
+ matrix.tx = this.tx;
+ matrix.ty = this.ty;
+ return matrix;
+ };
+ /**
+ * Changes the values of the given matrix to be the same as the ones in this matrix
+ * @param matrix - The matrix to copy to.
+ * @returns The matrix given in parameter with its values updated.
+ */
+ Matrix.prototype.copyTo = function (matrix) {
+ matrix.a = this.a;
+ matrix.b = this.b;
+ matrix.c = this.c;
+ matrix.d = this.d;
+ matrix.tx = this.tx;
+ matrix.ty = this.ty;
+ return matrix;
+ };
+ /**
+ * Changes the values of the matrix to be the same as the ones in given matrix
+ * @param {PIXI.Matrix} matrix - The matrix to copy from.
+ * @returns {PIXI.Matrix} this
+ */
+ Matrix.prototype.copyFrom = function (matrix) {
+ this.a = matrix.a;
+ this.b = matrix.b;
+ this.c = matrix.c;
+ this.d = matrix.d;
+ this.tx = matrix.tx;
+ this.ty = matrix.ty;
+ return this;
+ };
+ Matrix.prototype.toString = function () {
+ return "[@pixi/math:Matrix a=" + this.a + " b=" + this.b + " c=" + this.c + " d=" + this.d + " tx=" + this.tx + " ty=" + this.ty + "]";
+ };
+ Object.defineProperty(Matrix, "IDENTITY", {
+ /**
+ * A default (identity) matrix
+ * @readonly
+ */
+ get: function () {
+ return new Matrix();
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Matrix, "TEMP_MATRIX", {
+ /**
+ * A temp matrix
+ * @readonly
+ */
+ get: function () {
+ return new Matrix();
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return Matrix;
+ }());
+
+ // Your friendly neighbour https://en.wikipedia.org/wiki/Dihedral_group
+ /*
+ * Transform matrix for operation n is:
+ * | ux | vx |
+ * | uy | vy |
+ */
+ var ux = [1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1];
+ var uy = [0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1];
+ var vx = [0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 1, 0, -1, -1, -1];
+ var vy = [1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1];
+ /**
+ * [Cayley Table]{@link https://en.wikipedia.org/wiki/Cayley_table}
+ * for the composition of each rotation in the dihederal group D8.
+ * @type {number[][]}
+ * @private
+ */
+ var rotationCayley = [];
+ /**
+ * Matrices for each `GD8Symmetry` rotation.
+ * @type {PIXI.Matrix[]}
+ * @private
+ */
+ var rotationMatrices = [];
+ /*
+ * Alias for {@code Math.sign}.
+ */
+ var signum = Math.sign;
+ /*
+ * Initializes `rotationCayley` and `rotationMatrices`. It is called
+ * only once below.
+ */
+ function init() {
+ for (var i = 0; i < 16; i++) {
+ var row = [];
+ rotationCayley.push(row);
+ for (var j = 0; j < 16; j++) {
+ /* Multiplies rotation matrices i and j. */
+ var _ux = signum((ux[i] * ux[j]) + (vx[i] * uy[j]));
+ var _uy = signum((uy[i] * ux[j]) + (vy[i] * uy[j]));
+ var _vx = signum((ux[i] * vx[j]) + (vx[i] * vy[j]));
+ var _vy = signum((uy[i] * vx[j]) + (vy[i] * vy[j]));
+ /* Finds rotation matrix matching the product and pushes it. */
+ for (var k = 0; k < 16; k++) {
+ if (ux[k] === _ux && uy[k] === _uy
+ && vx[k] === _vx && vy[k] === _vy) {
+ row.push(k);
+ break;
+ }
+ }
+ }
+ }
+ for (var i = 0; i < 16; i++) {
+ var mat = new Matrix();
+ mat.set(ux[i], uy[i], vx[i], vy[i], 0, 0);
+ rotationMatrices.push(mat);
+ }
+ }
+ init();
+ /**
+ * @memberof PIXI
+ * @typedef {number} GD8Symmetry
+ * @see PIXI.groupD8
+ */
+ /**
+ * Implements the dihedral group D8, which is similar to
+ * [group D4]{@link http://mathworld.wolfram.com/DihedralGroupD4.html};
+ * D8 is the same but with diagonals, and it is used for texture
+ * rotations.
+ *
+ * The directions the U- and V- axes after rotation
+ * of an angle of `a: GD8Constant` are the vectors `(uX(a), uY(a))`
+ * and `(vX(a), vY(a))`. These aren't necessarily unit vectors.
+ *
+ * **Origin:**<br>
+ * This is the small part of gameofbombs.com portal system. It works.
+ * @see PIXI.groupD8.E
+ * @see PIXI.groupD8.SE
+ * @see PIXI.groupD8.S
+ * @see PIXI.groupD8.SW
+ * @see PIXI.groupD8.W
+ * @see PIXI.groupD8.NW
+ * @see PIXI.groupD8.N
+ * @see PIXI.groupD8.NE
+ * @author Ivan @ivanpopelyshev
+ * @namespace PIXI.groupD8
+ * @memberof PIXI
+ */
+ var groupD8 = {
+ /**
+ * | Rotation | Direction |
+ * |----------|-----------|
+ * | 0掳 | East |
+ * @memberof PIXI.groupD8
+ * @constant {PIXI.GD8Symmetry}
+ */
+ E: 0,
+ /**
+ * | Rotation | Direction |
+ * |----------|-----------|
+ * | 45掳鈫� | Southeast |
+ * @memberof PIXI.groupD8
+ * @constant {PIXI.GD8Symmetry}
+ */
+ SE: 1,
+ /**
+ * | Rotation | Direction |
+ * |----------|-----------|
+ * | 90掳鈫� | South |
+ * @memberof PIXI.groupD8
+ * @constant {PIXI.GD8Symmetry}
+ */
+ S: 2,
+ /**
+ * | Rotation | Direction |
+ * |----------|-----------|
+ * | 135掳鈫� | Southwest |
+ * @memberof PIXI.groupD8
+ * @constant {PIXI.GD8Symmetry}
+ */
+ SW: 3,
+ /**
+ * | Rotation | Direction |
+ * |----------|-----------|
+ * | 180掳 | West |
+ * @memberof PIXI.groupD8
+ * @constant {PIXI.GD8Symmetry}
+ */
+ W: 4,
+ /**
+ * | Rotation | Direction |
+ * |-------------|--------------|
+ * | -135掳/225掳鈫� | Northwest |
+ * @memberof PIXI.groupD8
+ * @constant {PIXI.GD8Symmetry}
+ */
+ NW: 5,
+ /**
+ * | Rotation | Direction |
+ * |-------------|--------------|
+ * | -90掳/270掳鈫� | North |
+ * @memberof PIXI.groupD8
+ * @constant {PIXI.GD8Symmetry}
+ */
+ N: 6,
+ /**
+ * | Rotation | Direction |
+ * |-------------|--------------|
+ * | -45掳/315掳鈫� | Northeast |
+ * @memberof PIXI.groupD8
+ * @constant {PIXI.GD8Symmetry}
+ */
+ NE: 7,
+ /**
+ * Reflection about Y-axis.
+ * @memberof PIXI.groupD8
+ * @constant {PIXI.GD8Symmetry}
+ */
+ MIRROR_VERTICAL: 8,
+ /**
+ * Reflection about the main diagonal.
+ * @memberof PIXI.groupD8
+ * @constant {PIXI.GD8Symmetry}
+ */
+ MAIN_DIAGONAL: 10,
+ /**
+ * Reflection about X-axis.
+ * @memberof PIXI.groupD8
+ * @constant {PIXI.GD8Symmetry}
+ */
+ MIRROR_HORIZONTAL: 12,
+ /**
+ * Reflection about reverse diagonal.
+ * @memberof PIXI.groupD8
+ * @constant {PIXI.GD8Symmetry}
+ */
+ REVERSE_DIAGONAL: 14,
+ /**
+ * @memberof PIXI.groupD8
+ * @param {PIXI.GD8Symmetry} ind - sprite rotation angle.
+ * @returns {PIXI.GD8Symmetry} The X-component of the U-axis
+ * after rotating the axes.
+ */
+ uX: function (ind) { return ux[ind]; },
+ /**
+ * @memberof PIXI.groupD8
+ * @param {PIXI.GD8Symmetry} ind - sprite rotation angle.
+ * @returns {PIXI.GD8Symmetry} The Y-component of the U-axis
+ * after rotating the axes.
+ */
+ uY: function (ind) { return uy[ind]; },
+ /**
+ * @memberof PIXI.groupD8
+ * @param {PIXI.GD8Symmetry} ind - sprite rotation angle.
+ * @returns {PIXI.GD8Symmetry} The X-component of the V-axis
+ * after rotating the axes.
+ */
+ vX: function (ind) { return vx[ind]; },
+ /**
+ * @memberof PIXI.groupD8
+ * @param {PIXI.GD8Symmetry} ind - sprite rotation angle.
+ * @returns {PIXI.GD8Symmetry} The Y-component of the V-axis
+ * after rotating the axes.
+ */
+ vY: function (ind) { return vy[ind]; },
+ /**
+ * @memberof PIXI.groupD8
+ * @param {PIXI.GD8Symmetry} rotation - symmetry whose opposite
+ * is needed. Only rotations have opposite symmetries while
+ * reflections don't.
+ * @returns {PIXI.GD8Symmetry} The opposite symmetry of `rotation`
+ */
+ inv: function (rotation) {
+ if (rotation & 8) // true only if between 8 & 15 (reflections)
+ {
+ return rotation & 15; // or rotation % 16
+ }
+ return (-rotation) & 7; // or (8 - rotation) % 8
+ },
+ /**
+ * Composes the two D8 operations.
+ *
+ * Taking `^` as reflection:
+ *
+ * | | E=0 | S=2 | W=4 | N=6 | E^=8 | S^=10 | W^=12 | N^=14 |
+ * |-------|-----|-----|-----|-----|------|-------|-------|-------|
+ * | E=0 | E | S | W | N | E^ | S^ | W^ | N^ |
+ * | S=2 | S | W | N | E | S^ | W^ | N^ | E^ |
+ * | W=4 | W | N | E | S | W^ | N^ | E^ | S^ |
+ * | N=6 | N | E | S | W | N^ | E^ | S^ | W^ |
+ * | E^=8 | E^ | N^ | W^ | S^ | E | N | W | S |
+ * | S^=10 | S^ | E^ | N^ | W^ | S | E | N | W |
+ * | W^=12 | W^ | S^ | E^ | N^ | W | S | E | N |
+ * | N^=14 | N^ | W^ | S^ | E^ | N | W | S | E |
+ *
+ * [This is a Cayley table]{@link https://en.wikipedia.org/wiki/Cayley_table}
+ * @memberof PIXI.groupD8
+ * @param {PIXI.GD8Symmetry} rotationSecond - Second operation, which
+ * is the row in the above cayley table.
+ * @param {PIXI.GD8Symmetry} rotationFirst - First operation, which
+ * is the column in the above cayley table.
+ * @returns {PIXI.GD8Symmetry} Composed operation
+ */
+ add: function (rotationSecond, rotationFirst) { return (rotationCayley[rotationSecond][rotationFirst]); },
+ /**
+ * Reverse of `add`.
+ * @memberof PIXI.groupD8
+ * @param {PIXI.GD8Symmetry} rotationSecond - Second operation
+ * @param {PIXI.GD8Symmetry} rotationFirst - First operation
+ * @returns {PIXI.GD8Symmetry} Result
+ */
+ sub: function (rotationSecond, rotationFirst) { return (rotationCayley[rotationSecond][groupD8.inv(rotationFirst)]); },
+ /**
+ * Adds 180 degrees to rotation, which is a commutative
+ * operation.
+ * @memberof PIXI.groupD8
+ * @param {number} rotation - The number to rotate.
+ * @returns {number} Rotated number
+ */
+ rotate180: function (rotation) { return rotation ^ 4; },
+ /**
+ * Checks if the rotation angle is vertical, i.e. south
+ * or north. It doesn't work for reflections.
+ * @memberof PIXI.groupD8
+ * @param {PIXI.GD8Symmetry} rotation - The number to check.
+ * @returns {boolean} Whether or not the direction is vertical
+ */
+ isVertical: function (rotation) { return (rotation & 3) === 2; },
+ /**
+ * Approximates the vector `V(dx,dy)` into one of the
+ * eight directions provided by `groupD8`.
+ * @memberof PIXI.groupD8
+ * @param {number} dx - X-component of the vector
+ * @param {number} dy - Y-component of the vector
+ * @returns {PIXI.GD8Symmetry} Approximation of the vector into
+ * one of the eight symmetries.
+ */
+ byDirection: function (dx, dy) {
+ if (Math.abs(dx) * 2 <= Math.abs(dy)) {
+ if (dy >= 0) {
+ return groupD8.S;
+ }
+ return groupD8.N;
+ }
+ else if (Math.abs(dy) * 2 <= Math.abs(dx)) {
+ if (dx > 0) {
+ return groupD8.E;
+ }
+ return groupD8.W;
+ }
+ else if (dy > 0) {
+ if (dx > 0) {
+ return groupD8.SE;
+ }
+ return groupD8.SW;
+ }
+ else if (dx > 0) {
+ return groupD8.NE;
+ }
+ return groupD8.NW;
+ },
+ /**
+ * Helps sprite to compensate texture packer rotation.
+ * @memberof PIXI.groupD8
+ * @param {PIXI.Matrix} matrix - sprite world matrix
+ * @param {PIXI.GD8Symmetry} rotation - The rotation factor to use.
+ * @param {number} tx - sprite anchoring
+ * @param {number} ty - sprite anchoring
+ */
+ matrixAppendRotationInv: function (matrix, rotation, tx, ty) {
+ if (tx === void 0) { tx = 0; }
+ if (ty === void 0) { ty = 0; }
+ // Packer used "rotation", we use "inv(rotation)"
+ var mat = rotationMatrices[groupD8.inv(rotation)];
+ mat.tx = tx;
+ mat.ty = ty;
+ matrix.append(mat);
+ },
+ };
+
+ /**
+ * Transform that takes care about its versions.
+ * @memberof PIXI
+ */
+ var Transform = /** @class */ (function () {
+ function Transform() {
+ this.worldTransform = new Matrix();
+ this.localTransform = new Matrix();
+ this.position = new ObservablePoint(this.onChange, this, 0, 0);
+ this.scale = new ObservablePoint(this.onChange, this, 1, 1);
+ this.pivot = new ObservablePoint(this.onChange, this, 0, 0);
+ this.skew = new ObservablePoint(this.updateSkew, this, 0, 0);
+ this._rotation = 0;
+ this._cx = 1;
+ this._sx = 0;
+ this._cy = 0;
+ this._sy = 1;
+ this._localID = 0;
+ this._currentLocalID = 0;
+ this._worldID = 0;
+ this._parentID = 0;
+ }
+ /** Called when a value changes. */
+ Transform.prototype.onChange = function () {
+ this._localID++;
+ };
+ /** Called when the skew or the rotation changes. */
+ Transform.prototype.updateSkew = function () {
+ this._cx = Math.cos(this._rotation + this.skew.y);
+ this._sx = Math.sin(this._rotation + this.skew.y);
+ this._cy = -Math.sin(this._rotation - this.skew.x); // cos, added PI/2
+ this._sy = Math.cos(this._rotation - this.skew.x); // sin, added PI/2
+ this._localID++;
+ };
+ Transform.prototype.toString = function () {
+ return "[@pixi/math:Transform "
+ + ("position=(" + this.position.x + ", " + this.position.y + ") ")
+ + ("rotation=" + this.rotation + " ")
+ + ("scale=(" + this.scale.x + ", " + this.scale.y + ") ")
+ + ("skew=(" + this.skew.x + ", " + this.skew.y + ") ")
+ + "]";
+ };
+ /** Updates the local transformation matrix. */
+ Transform.prototype.updateLocalTransform = function () {
+ var lt = this.localTransform;
+ if (this._localID !== this._currentLocalID) {
+ // get the matrix values of the displayobject based on its transform properties..
+ lt.a = this._cx * this.scale.x;
+ lt.b = this._sx * this.scale.x;
+ lt.c = this._cy * this.scale.y;
+ lt.d = this._sy * this.scale.y;
+ lt.tx = this.position.x - ((this.pivot.x * lt.a) + (this.pivot.y * lt.c));
+ lt.ty = this.position.y - ((this.pivot.x * lt.b) + (this.pivot.y * lt.d));
+ this._currentLocalID = this._localID;
+ // force an update..
+ this._parentID = -1;
+ }
+ };
+ /**
+ * Updates the local and the world transformation matrices.
+ * @param parentTransform - The parent transform
+ */
+ Transform.prototype.updateTransform = function (parentTransform) {
+ var lt = this.localTransform;
+ if (this._localID !== this._currentLocalID) {
+ // get the matrix values of the displayobject based on its transform properties..
+ lt.a = this._cx * this.scale.x;
+ lt.b = this._sx * this.scale.x;
+ lt.c = this._cy * this.scale.y;
+ lt.d = this._sy * this.scale.y;
+ lt.tx = this.position.x - ((this.pivot.x * lt.a) + (this.pivot.y * lt.c));
+ lt.ty = this.position.y - ((this.pivot.x * lt.b) + (this.pivot.y * lt.d));
+ this._currentLocalID = this._localID;
+ // force an update..
+ this._parentID = -1;
+ }
+ if (this._parentID !== parentTransform._worldID) {
+ // concat the parent matrix with the objects transform.
+ var pt = parentTransform.worldTransform;
+ var wt = this.worldTransform;
+ wt.a = (lt.a * pt.a) + (lt.b * pt.c);
+ wt.b = (lt.a * pt.b) + (lt.b * pt.d);
+ wt.c = (lt.c * pt.a) + (lt.d * pt.c);
+ wt.d = (lt.c * pt.b) + (lt.d * pt.d);
+ wt.tx = (lt.tx * pt.a) + (lt.ty * pt.c) + pt.tx;
+ wt.ty = (lt.tx * pt.b) + (lt.ty * pt.d) + pt.ty;
+ this._parentID = parentTransform._worldID;
+ // update the id of the transform..
+ this._worldID++;
+ }
+ };
+ /**
+ * Decomposes a matrix and sets the transforms properties based on it.
+ * @param matrix - The matrix to decompose
+ */
+ Transform.prototype.setFromMatrix = function (matrix) {
+ matrix.decompose(this);
+ this._localID++;
+ };
+ Object.defineProperty(Transform.prototype, "rotation", {
+ /** The rotation of the object in radians. */
+ get: function () {
+ return this._rotation;
+ },
+ set: function (value) {
+ if (this._rotation !== value) {
+ this._rotation = value;
+ this.updateSkew();
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /** A default (identity) transform. */
+ Transform.IDENTITY = new Transform();
+ return Transform;
+ }());
+
+ /*!
+ * @pixi/display - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/display is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /**
+ * Sets the default value for the container property 'sortableChildren'.
+ * If set to true, the container will sort its children by zIndex value
+ * when updateTransform() is called, or manually if sortChildren() is called.
+ *
+ * This actually changes the order of elements in the array, so should be treated
+ * as a basic solution that is not performant compared to other solutions,
+ * such as @link https://github.com/pixijs/pixi-display
+ *
+ * Also be aware of that this may not work nicely with the addChildAt() function,
+ * as the zIndex sorting may cause the child to automatically sorted to another position.
+ * @static
+ * @constant
+ * @name SORTABLE_CHILDREN
+ * @memberof PIXI.settings
+ * @type {boolean}
+ * @default false
+ */
+ settings$1.SORTABLE_CHILDREN = false;
+
+ /**
+ * 'Builder' pattern for bounds rectangles.
+ *
+ * This could be called an Axis-Aligned Bounding Box.
+ * It is not an actual shape. It is a mutable thing; no 'EMPTY' or those kind of problems.
+ * @memberof PIXI
+ */
+ var Bounds = /** @class */ (function () {
+ function Bounds() {
+ this.minX = Infinity;
+ this.minY = Infinity;
+ this.maxX = -Infinity;
+ this.maxY = -Infinity;
+ this.rect = null;
+ this.updateID = -1;
+ }
+ /**
+ * Checks if bounds are empty.
+ * @returns - True if empty.
+ */
+ Bounds.prototype.isEmpty = function () {
+ return this.minX > this.maxX || this.minY > this.maxY;
+ };
+ /** Clears the bounds and resets. */
+ Bounds.prototype.clear = function () {
+ this.minX = Infinity;
+ this.minY = Infinity;
+ this.maxX = -Infinity;
+ this.maxY = -Infinity;
+ };
+ /**
+ * Can return Rectangle.EMPTY constant, either construct new rectangle, either use your rectangle
+ * It is not guaranteed that it will return tempRect
+ * @param rect - Temporary object will be used if AABB is not empty
+ * @returns - A rectangle of the bounds
+ */
+ Bounds.prototype.getRectangle = function (rect) {
+ if (this.minX > this.maxX || this.minY > this.maxY) {
+ return Rectangle.EMPTY;
+ }
+ rect = rect || new Rectangle(0, 0, 1, 1);
+ rect.x = this.minX;
+ rect.y = this.minY;
+ rect.width = this.maxX - this.minX;
+ rect.height = this.maxY - this.minY;
+ return rect;
+ };
+ /**
+ * This function should be inlined when its possible.
+ * @param point - The point to add.
+ */
+ Bounds.prototype.addPoint = function (point) {
+ this.minX = Math.min(this.minX, point.x);
+ this.maxX = Math.max(this.maxX, point.x);
+ this.minY = Math.min(this.minY, point.y);
+ this.maxY = Math.max(this.maxY, point.y);
+ };
+ /**
+ * Adds a point, after transformed. This should be inlined when its possible.
+ * @param matrix
+ * @param point
+ */
+ Bounds.prototype.addPointMatrix = function (matrix, point) {
+ var a = matrix.a, b = matrix.b, c = matrix.c, d = matrix.d, tx = matrix.tx, ty = matrix.ty;
+ var x = (a * point.x) + (c * point.y) + tx;
+ var y = (b * point.x) + (d * point.y) + ty;
+ this.minX = Math.min(this.minX, x);
+ this.maxX = Math.max(this.maxX, x);
+ this.minY = Math.min(this.minY, y);
+ this.maxY = Math.max(this.maxY, y);
+ };
+ /**
+ * Adds a quad, not transformed
+ * @param vertices - The verts to add.
+ */
+ Bounds.prototype.addQuad = function (vertices) {
+ var minX = this.minX;
+ var minY = this.minY;
+ var maxX = this.maxX;
+ var maxY = this.maxY;
+ var x = vertices[0];
+ var y = vertices[1];
+ minX = x < minX ? x : minX;
+ minY = y < minY ? y : minY;
+ maxX = x > maxX ? x : maxX;
+ maxY = y > maxY ? y : maxY;
+ x = vertices[2];
+ y = vertices[3];
+ minX = x < minX ? x : minX;
+ minY = y < minY ? y : minY;
+ maxX = x > maxX ? x : maxX;
+ maxY = y > maxY ? y : maxY;
+ x = vertices[4];
+ y = vertices[5];
+ minX = x < minX ? x : minX;
+ minY = y < minY ? y : minY;
+ maxX = x > maxX ? x : maxX;
+ maxY = y > maxY ? y : maxY;
+ x = vertices[6];
+ y = vertices[7];
+ minX = x < minX ? x : minX;
+ minY = y < minY ? y : minY;
+ maxX = x > maxX ? x : maxX;
+ maxY = y > maxY ? y : maxY;
+ this.minX = minX;
+ this.minY = minY;
+ this.maxX = maxX;
+ this.maxY = maxY;
+ };
+ /**
+ * Adds sprite frame, transformed.
+ * @param transform - transform to apply
+ * @param x0 - left X of frame
+ * @param y0 - top Y of frame
+ * @param x1 - right X of frame
+ * @param y1 - bottom Y of frame
+ */
+ Bounds.prototype.addFrame = function (transform, x0, y0, x1, y1) {
+ this.addFrameMatrix(transform.worldTransform, x0, y0, x1, y1);
+ };
+ /**
+ * Adds sprite frame, multiplied by matrix
+ * @param matrix - matrix to apply
+ * @param x0 - left X of frame
+ * @param y0 - top Y of frame
+ * @param x1 - right X of frame
+ * @param y1 - bottom Y of frame
+ */
+ Bounds.prototype.addFrameMatrix = function (matrix, x0, y0, x1, y1) {
+ var a = matrix.a;
+ var b = matrix.b;
+ var c = matrix.c;
+ var d = matrix.d;
+ var tx = matrix.tx;
+ var ty = matrix.ty;
+ var minX = this.minX;
+ var minY = this.minY;
+ var maxX = this.maxX;
+ var maxY = this.maxY;
+ var x = (a * x0) + (c * y0) + tx;
+ var y = (b * x0) + (d * y0) + ty;
+ minX = x < minX ? x : minX;
+ minY = y < minY ? y : minY;
+ maxX = x > maxX ? x : maxX;
+ maxY = y > maxY ? y : maxY;
+ x = (a * x1) + (c * y0) + tx;
+ y = (b * x1) + (d * y0) + ty;
+ minX = x < minX ? x : minX;
+ minY = y < minY ? y : minY;
+ maxX = x > maxX ? x : maxX;
+ maxY = y > maxY ? y : maxY;
+ x = (a * x0) + (c * y1) + tx;
+ y = (b * x0) + (d * y1) + ty;
+ minX = x < minX ? x : minX;
+ minY = y < minY ? y : minY;
+ maxX = x > maxX ? x : maxX;
+ maxY = y > maxY ? y : maxY;
+ x = (a * x1) + (c * y1) + tx;
+ y = (b * x1) + (d * y1) + ty;
+ minX = x < minX ? x : minX;
+ minY = y < minY ? y : minY;
+ maxX = x > maxX ? x : maxX;
+ maxY = y > maxY ? y : maxY;
+ this.minX = minX;
+ this.minY = minY;
+ this.maxX = maxX;
+ this.maxY = maxY;
+ };
+ /**
+ * Adds screen vertices from array
+ * @param vertexData - calculated vertices
+ * @param beginOffset - begin offset
+ * @param endOffset - end offset, excluded
+ */
+ Bounds.prototype.addVertexData = function (vertexData, beginOffset, endOffset) {
+ var minX = this.minX;
+ var minY = this.minY;
+ var maxX = this.maxX;
+ var maxY = this.maxY;
+ for (var i = beginOffset; i < endOffset; i += 2) {
+ var x = vertexData[i];
+ var y = vertexData[i + 1];
+ minX = x < minX ? x : minX;
+ minY = y < minY ? y : minY;
+ maxX = x > maxX ? x : maxX;
+ maxY = y > maxY ? y : maxY;
+ }
+ this.minX = minX;
+ this.minY = minY;
+ this.maxX = maxX;
+ this.maxY = maxY;
+ };
+ /**
+ * Add an array of mesh vertices
+ * @param transform - mesh transform
+ * @param vertices - mesh coordinates in array
+ * @param beginOffset - begin offset
+ * @param endOffset - end offset, excluded
+ */
+ Bounds.prototype.addVertices = function (transform, vertices, beginOffset, endOffset) {
+ this.addVerticesMatrix(transform.worldTransform, vertices, beginOffset, endOffset);
+ };
+ /**
+ * Add an array of mesh vertices.
+ * @param matrix - mesh matrix
+ * @param vertices - mesh coordinates in array
+ * @param beginOffset - begin offset
+ * @param endOffset - end offset, excluded
+ * @param padX - x padding
+ * @param padY - y padding
+ */
+ Bounds.prototype.addVerticesMatrix = function (matrix, vertices, beginOffset, endOffset, padX, padY) {
+ if (padX === void 0) { padX = 0; }
+ if (padY === void 0) { padY = padX; }
+ var a = matrix.a;
+ var b = matrix.b;
+ var c = matrix.c;
+ var d = matrix.d;
+ var tx = matrix.tx;
+ var ty = matrix.ty;
+ var minX = this.minX;
+ var minY = this.minY;
+ var maxX = this.maxX;
+ var maxY = this.maxY;
+ for (var i = beginOffset; i < endOffset; i += 2) {
+ var rawX = vertices[i];
+ var rawY = vertices[i + 1];
+ var x = (a * rawX) + (c * rawY) + tx;
+ var y = (d * rawY) + (b * rawX) + ty;
+ minX = Math.min(minX, x - padX);
+ maxX = Math.max(maxX, x + padX);
+ minY = Math.min(minY, y - padY);
+ maxY = Math.max(maxY, y + padY);
+ }
+ this.minX = minX;
+ this.minY = minY;
+ this.maxX = maxX;
+ this.maxY = maxY;
+ };
+ /**
+ * Adds other {@link Bounds}.
+ * @param bounds - The Bounds to be added
+ */
+ Bounds.prototype.addBounds = function (bounds) {
+ var minX = this.minX;
+ var minY = this.minY;
+ var maxX = this.maxX;
+ var maxY = this.maxY;
+ this.minX = bounds.minX < minX ? bounds.minX : minX;
+ this.minY = bounds.minY < minY ? bounds.minY : minY;
+ this.maxX = bounds.maxX > maxX ? bounds.maxX : maxX;
+ this.maxY = bounds.maxY > maxY ? bounds.maxY : maxY;
+ };
+ /**
+ * Adds other Bounds, masked with Bounds.
+ * @param bounds - The Bounds to be added.
+ * @param mask - TODO
+ */
+ Bounds.prototype.addBoundsMask = function (bounds, mask) {
+ var _minX = bounds.minX > mask.minX ? bounds.minX : mask.minX;
+ var _minY = bounds.minY > mask.minY ? bounds.minY : mask.minY;
+ var _maxX = bounds.maxX < mask.maxX ? bounds.maxX : mask.maxX;
+ var _maxY = bounds.maxY < mask.maxY ? bounds.maxY : mask.maxY;
+ if (_minX <= _maxX && _minY <= _maxY) {
+ var minX = this.minX;
+ var minY = this.minY;
+ var maxX = this.maxX;
+ var maxY = this.maxY;
+ this.minX = _minX < minX ? _minX : minX;
+ this.minY = _minY < minY ? _minY : minY;
+ this.maxX = _maxX > maxX ? _maxX : maxX;
+ this.maxY = _maxY > maxY ? _maxY : maxY;
+ }
+ };
+ /**
+ * Adds other Bounds, multiplied by matrix. Bounds shouldn't be empty.
+ * @param bounds - other bounds
+ * @param matrix - multiplicator
+ */
+ Bounds.prototype.addBoundsMatrix = function (bounds, matrix) {
+ this.addFrameMatrix(matrix, bounds.minX, bounds.minY, bounds.maxX, bounds.maxY);
+ };
+ /**
+ * Adds other Bounds, masked with Rectangle.
+ * @param bounds - TODO
+ * @param area - TODO
+ */
+ Bounds.prototype.addBoundsArea = function (bounds, area) {
+ var _minX = bounds.minX > area.x ? bounds.minX : area.x;
+ var _minY = bounds.minY > area.y ? bounds.minY : area.y;
+ var _maxX = bounds.maxX < area.x + area.width ? bounds.maxX : (area.x + area.width);
+ var _maxY = bounds.maxY < area.y + area.height ? bounds.maxY : (area.y + area.height);
+ if (_minX <= _maxX && _minY <= _maxY) {
+ var minX = this.minX;
+ var minY = this.minY;
+ var maxX = this.maxX;
+ var maxY = this.maxY;
+ this.minX = _minX < minX ? _minX : minX;
+ this.minY = _minY < minY ? _minY : minY;
+ this.maxX = _maxX > maxX ? _maxX : maxX;
+ this.maxY = _maxY > maxY ? _maxY : maxY;
+ }
+ };
+ /**
+ * Pads bounds object, making it grow in all directions.
+ * If paddingY is omitted, both paddingX and paddingY will be set to paddingX.
+ * @param paddingX - The horizontal padding amount.
+ * @param paddingY - The vertical padding amount.
+ */
+ Bounds.prototype.pad = function (paddingX, paddingY) {
+ if (paddingX === void 0) { paddingX = 0; }
+ if (paddingY === void 0) { paddingY = paddingX; }
+ if (!this.isEmpty()) {
+ this.minX -= paddingX;
+ this.maxX += paddingX;
+ this.minY -= paddingY;
+ this.maxY += paddingY;
+ }
+ };
+ /**
+ * Adds padded frame. (x0, y0) should be strictly less than (x1, y1)
+ * @param x0 - left X of frame
+ * @param y0 - top Y of frame
+ * @param x1 - right X of frame
+ * @param y1 - bottom Y of frame
+ * @param padX - padding X
+ * @param padY - padding Y
+ */
+ Bounds.prototype.addFramePad = function (x0, y0, x1, y1, padX, padY) {
+ x0 -= padX;
+ y0 -= padY;
+ x1 += padX;
+ y1 += padY;
+ this.minX = this.minX < x0 ? this.minX : x0;
+ this.maxX = this.maxX > x1 ? this.maxX : x1;
+ this.minY = this.minY < y0 ? this.minY : y0;
+ this.maxY = this.maxY > y1 ? this.maxY : y1;
+ };
+ return Bounds;
+ }());
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+ /* global Reflect, Promise */
+
+ var extendStatics$j = function(d, b) {
+ extendStatics$j = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
+ return extendStatics$j(d, b);
+ };
+
+ function __extends$j(d, b) {
+ extendStatics$j(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ }
+
+ /**
+ * The base class for all objects that are rendered on the screen.
+ *
+ * This is an abstract class and can not be used on its own; rather it should be extended.
+ *
+ * ## Display objects implemented in PixiJS
+ *
+ * | Display Object | Description |
+ * | ------------------------------- | --------------------------------------------------------------------- |
+ * | {@link PIXI.Container} | Adds support for `children` to DisplayObject |
+ * | {@link PIXI.Graphics} | Shape-drawing display object similar to the Canvas API |
+ * | {@link PIXI.Sprite} | Draws textures (i.e. images) |
+ * | {@link PIXI.Text} | Draws text using the Canvas API internally |
+ * | {@link PIXI.BitmapText} | More scaleable solution for text rendering, reusing glyph textures |
+ * | {@link PIXI.TilingSprite} | Draws textures/images in a tiled fashion |
+ * | {@link PIXI.AnimatedSprite} | Draws an animation of multiple images |
+ * | {@link PIXI.Mesh} | Provides a lower-level API for drawing meshes with custom data |
+ * | {@link PIXI.NineSlicePlane} | Mesh-related |
+ * | {@link PIXI.SimpleMesh} | v4-compatible mesh |
+ * | {@link PIXI.SimplePlane} | Mesh-related |
+ * | {@link PIXI.SimpleRope} | Mesh-related |
+ *
+ * ## Transforms
+ *
+ * The [transform]{@link DisplayObject#transform} of a display object describes the projection from its
+ * local coordinate space to its parent's local coordinate space. The following properties are derived
+ * from the transform:
+ *
+ * <table>
+ * <thead>
+ * <tr>
+ * <th>Property</th>
+ * <th>Description</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td>[pivot]{@link PIXI.DisplayObject#pivot}</td>
+ * <td>
+ * Invariant under rotation, scaling, and skewing. The projection of into the parent's space of the pivot
+ * is equal to position, regardless of the other three transformations. In other words, It is the center of
+ * rotation, scaling, and skewing.
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>[position]{@link PIXI.DisplayObject#position}</td>
+ * <td>
+ * Translation. This is the position of the [pivot]{@link PIXI.DisplayObject#pivot} in the parent's local
+ * space. The default value of the pivot is the origin (0,0). If the top-left corner of your display object
+ * is (0,0) in its local space, then the position will be its top-left corner in the parent's local space.
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>[scale]{@link PIXI.DisplayObject#scale}</td>
+ * <td>
+ * Scaling. This will stretch (or compress) the display object's projection. The scale factors are along the
+ * local coordinate axes. In other words, the display object is scaled before rotated or skewed. The center
+ * of scaling is the [pivot]{@link PIXI.DisplayObject#pivot}.
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>[rotation]{@link PIXI.DisplayObject#rotation}</td>
+ * <td>
+ * Rotation. This will rotate the display object's projection by this angle (in radians).
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>[skew]{@link PIXI.DisplayObject#skew}</td>
+ * <td>
+ * <p>Skewing. This can be used to deform a rectangular display object into a parallelogram.</p>
+ * <p>
+ * In PixiJS, skew has a slightly different behaviour than the conventional meaning. It can be
+ * thought of the net rotation applied to the coordinate axes (separately). For example, if "skew.x" is
+ * 鈲� and "skew.y" is 尾, then the line x = 0 will be rotated by 鈲� (y = -x*cot鈲�) and the line y = 0 will be
+ * rotated by 尾 (y = x*tan尾). A line y = x*tan洗 (i.e. a line at angle 洗 to the x-axis in local-space) will
+ * be rotated by an angle between 鈲� and 尾.
+ * </p>
+ * <p>
+ * It can be observed that if skew is applied equally to both axes, then it will be equivalent to applying
+ * a rotation. Indeed, if "skew.x" = -洗 and "skew.y" = 洗, it will produce an equivalent of "rotation" = 洗.
+ * </p>
+ * <p>
+ * Another quite interesting observation is that "skew.x", "skew.y", rotation are communtative operations. Indeed,
+ * because rotation is essentially a careful combination of the two.
+ * </p>
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>angle</td>
+ * <td>Rotation. This is an alias for [rotation]{@link PIXI.DisplayObject#rotation}, but in degrees.</td>
+ * </tr>
+ * <tr>
+ * <td>x</td>
+ * <td>Translation. This is an alias for position.x!</td>
+ * </tr>
+ * <tr>
+ * <td>y</td>
+ * <td>Translation. This is an alias for position.y!</td>
+ * </tr>
+ * <tr>
+ * <td>width</td>
+ * <td>
+ * Implemented in [Container]{@link PIXI.Container}. Scaling. The width property calculates scale.x by dividing
+ * the "requested" width by the local bounding box width. It is indirectly an abstraction over scale.x, and there
+ * is no concept of user-defined width.
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>height</td>
+ * <td>
+ * Implemented in [Container]{@link PIXI.Container}. Scaling. The height property calculates scale.y by dividing
+ * the "requested" height by the local bounding box height. It is indirectly an abstraction over scale.y, and there
+ * is no concept of user-defined height.
+ * </td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * ## Bounds
+ *
+ * The bounds of a display object is defined by the minimum axis-aligned rectangle in world space that can fit
+ * around it. The abstract `calculateBounds` method is responsible for providing it (and it should use the
+ * `worldTransform` to calculate in world space).
+ *
+ * There are a few additional types of bounding boxes:
+ *
+ * | Bounds | Description |
+ * | --------------------- | ---------------------------------------------------------------------------------------- |
+ * | World Bounds | This is synonymous is the regular bounds described above. See `getBounds()`. |
+ * | Local Bounds | This the axis-aligned bounding box in the parent's local space. See `getLocalBounds()`. |
+ * | Render Bounds | The bounds, but including extra rendering effects like filter padding. |
+ * | Projected Bounds | The bounds of the projected display object onto the screen. Usually equals world bounds. |
+ * | Relative Bounds | The bounds of a display object when projected onto a ancestor's (or parent's) space. |
+ * | Natural Bounds | The bounds of an object in its own local space (not parent's space, like in local bounds)|
+ * | Content Bounds | The natural bounds when excluding all children of a `Container`. |
+ *
+ * ### calculateBounds
+ *
+ * [Container]{@link Container} already implements `calculateBounds` in a manner that includes children.
+ *
+ * But for a non-Container display object, the `calculateBounds` method must be overridden in order for `getBounds` and
+ * `getLocalBounds` to work. This method must write the bounds into `this._bounds`.
+ *
+ * Generally, the following technique works for most simple cases: take the list of points
+ * forming the "hull" of the object (i.e. outline of the object's shape), and then add them
+ * using {@link PIXI.Bounds#addPointMatrix}.
+ *
+ * ```js
+ * calculateBounds(): void
+ * {
+ * const points = [...];
+ *
+ * for (let i = 0, j = points.length; i < j; i++)
+ * {
+ * this._bounds.addPointMatrix(this.worldTransform, points[i]);
+ * }
+ * }
+ * ```
+ *
+ * You can optimize this for a large number of points by using {@link PIXI.Bounds#addVerticesMatrix} to pass them
+ * in one array together.
+ *
+ * ## Alpha
+ *
+ * This alpha sets a display object's **relative opacity** w.r.t its parent. For example, if the alpha of a display
+ * object is 0.5 and its parent's alpha is 0.5, then it will be rendered with 25% opacity (assuming alpha is not
+ * applied on any ancestor further up the chain).
+ *
+ * The alpha with which the display object will be rendered is called the [worldAlpha]{@link PIXI.DisplayObject#worldAlpha}.
+ *
+ * ## Renderable vs Visible
+ *
+ * The `renderable` and `visible` properties can be used to prevent a display object from being rendered to the
+ * screen. However, there is a subtle difference between the two. When using `renderable`, the transforms of the display
+ * object (and its children subtree) will continue to be calculated. When using `visible`, the transforms will not
+ * be calculated.
+ *
+ * It is recommended that applications use the `renderable` property for culling. See
+ * [@pixi-essentials/cull]{@link https://www.npmjs.com/package/@pixi-essentials/cull} or
+ * [pixi-cull]{@link https://www.npmjs.com/package/pixi-cull} for more details.
+ *
+ * Otherwise, to prevent an object from rendering in the general-purpose sense - `visible` is the property to use. This
+ * one is also better in terms of performance.
+ * @memberof PIXI
+ */
+ var DisplayObject = /** @class */ (function (_super) {
+ __extends$j(DisplayObject, _super);
+ function DisplayObject() {
+ var _this = _super.call(this) || this;
+ _this.tempDisplayObjectParent = null;
+ // TODO: need to create Transform from factory
+ _this.transform = new Transform();
+ _this.alpha = 1;
+ _this.visible = true;
+ _this.renderable = true;
+ _this.cullable = false;
+ _this.cullArea = null;
+ _this.parent = null;
+ _this.worldAlpha = 1;
+ _this._lastSortedIndex = 0;
+ _this._zIndex = 0;
+ _this.filterArea = null;
+ _this.filters = null;
+ _this._enabledFilters = null;
+ _this._bounds = new Bounds();
+ _this._localBounds = null;
+ _this._boundsID = 0;
+ _this._boundsRect = null;
+ _this._localBoundsRect = null;
+ _this._mask = null;
+ _this._maskRefCount = 0;
+ _this._destroyed = false;
+ _this.isSprite = false;
+ _this.isMask = false;
+ return _this;
+ }
+ /**
+ * Mixes all enumerable properties and methods from a source object to DisplayObject.
+ * @param source - The source of properties and methods to mix in.
+ */
+ DisplayObject.mixin = function (source) {
+ // in ES8/ES2017, this would be really easy:
+ // Object.defineProperties(DisplayObject.prototype, Object.getOwnPropertyDescriptors(source));
+ // get all the enumerable property keys
+ var keys = Object.keys(source);
+ // loop through properties
+ for (var i = 0; i < keys.length; ++i) {
+ var propertyName = keys[i];
+ // Set the property using the property descriptor - this works for accessors and normal value properties
+ Object.defineProperty(DisplayObject.prototype, propertyName, Object.getOwnPropertyDescriptor(source, propertyName));
+ }
+ };
+ Object.defineProperty(DisplayObject.prototype, "destroyed", {
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ * @instance
+ * @event added
+ * @param {PIXI.Container} container - The container added to.
+ */
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ * @instance
+ * @event removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
+ /**
+ * Fired when this DisplayObject is destroyed. This event is emitted once
+ * destroy is finished.
+ * @instance
+ * @event destroyed
+ */
+ /** Readonly flag for destroyed display objects. */
+ get: function () {
+ return this._destroyed;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /** Recursively updates transform of all objects from the root to this one internal function for toLocal() */
+ DisplayObject.prototype._recursivePostUpdateTransform = function () {
+ if (this.parent) {
+ this.parent._recursivePostUpdateTransform();
+ this.transform.updateTransform(this.parent.transform);
+ }
+ else {
+ this.transform.updateTransform(this._tempDisplayObjectParent.transform);
+ }
+ };
+ /** Updates the object transform for rendering. TODO - Optimization pass! */
+ DisplayObject.prototype.updateTransform = function () {
+ this._boundsID++;
+ this.transform.updateTransform(this.parent.transform);
+ // multiply the alphas..
+ this.worldAlpha = this.alpha * this.parent.worldAlpha;
+ };
+ /**
+ * Calculates and returns the (world) bounds of the display object as a [Rectangle]{@link PIXI.Rectangle}.
+ *
+ * This method is expensive on containers with a large subtree (like the stage). This is because the bounds
+ * of a container depend on its children's bounds, which recursively causes all bounds in the subtree to
+ * be recalculated. The upside, however, is that calling `getBounds` once on a container will indeed update
+ * the bounds of all children (the whole subtree, in fact). This side effect should be exploited by using
+ * `displayObject._bounds.getRectangle()` when traversing through all the bounds in a scene graph. Otherwise,
+ * calling `getBounds` on each object in a subtree will cause the total cost to increase quadratically as
+ * its height increases.
+ *
+ * The transforms of all objects in a container's **subtree** and of all **ancestors** are updated.
+ * The world bounds of all display objects in a container's **subtree** will also be recalculated.
+ *
+ * The `_bounds` object stores the last calculation of the bounds. You can use to entirely skip bounds
+ * calculation if needed.
+ *
+ * ```js
+ * const lastCalculatedBounds = displayObject._bounds.getRectangle(optionalRect);
+ * ```
+ *
+ * Do know that usage of `getLocalBounds` can corrupt the `_bounds` of children (the whole subtree, actually). This
+ * is a known issue that has not been solved. See [getLocalBounds]{@link PIXI.DisplayObject#getLocalBounds} for more
+ * details.
+ *
+ * `getBounds` should be called with `skipUpdate` equal to `true` in a render() call. This is because the transforms
+ * are guaranteed to be update-to-date. In fact, recalculating inside a render() call may cause corruption in certain
+ * cases.
+ * @param skipUpdate - Setting to `true` will stop the transforms of the scene graph from
+ * being updated. This means the calculation returned MAY be out of date BUT will give you a
+ * nice performance boost.
+ * @param rect - Optional rectangle to store the result of the bounds calculation.
+ * @returns - The minimum axis-aligned rectangle in world space that fits around this object.
+ */
+ DisplayObject.prototype.getBounds = function (skipUpdate, rect) {
+ if (!skipUpdate) {
+ if (!this.parent) {
+ this.parent = this._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else {
+ this._recursivePostUpdateTransform();
+ this.updateTransform();
+ }
+ }
+ if (this._bounds.updateID !== this._boundsID) {
+ this.calculateBounds();
+ this._bounds.updateID = this._boundsID;
+ }
+ if (!rect) {
+ if (!this._boundsRect) {
+ this._boundsRect = new Rectangle();
+ }
+ rect = this._boundsRect;
+ }
+ return this._bounds.getRectangle(rect);
+ };
+ /**
+ * Retrieves the local bounds of the displayObject as a rectangle object.
+ * @param rect - Optional rectangle to store the result of the bounds calculation.
+ * @returns - The rectangular bounding area.
+ */
+ DisplayObject.prototype.getLocalBounds = function (rect) {
+ if (!rect) {
+ if (!this._localBoundsRect) {
+ this._localBoundsRect = new Rectangle();
+ }
+ rect = this._localBoundsRect;
+ }
+ if (!this._localBounds) {
+ this._localBounds = new Bounds();
+ }
+ var transformRef = this.transform;
+ var parentRef = this.parent;
+ this.parent = null;
+ this.transform = this._tempDisplayObjectParent.transform;
+ var worldBounds = this._bounds;
+ var worldBoundsID = this._boundsID;
+ this._bounds = this._localBounds;
+ var bounds = this.getBounds(false, rect);
+ this.parent = parentRef;
+ this.transform = transformRef;
+ this._bounds = worldBounds;
+ this._bounds.updateID += this._boundsID - worldBoundsID; // reflect side-effects
+ return bounds;
+ };
+ /**
+ * Calculates the global position of the display object.
+ * @param position - The world origin to calculate from.
+ * @param point - A Point object in which to store the value, optional
+ * (otherwise will create a new Point).
+ * @param skipUpdate - Should we skip the update transform.
+ * @returns - A point object representing the position of this object.
+ */
+ DisplayObject.prototype.toGlobal = function (position, point, skipUpdate) {
+ if (skipUpdate === void 0) { skipUpdate = false; }
+ if (!skipUpdate) {
+ this._recursivePostUpdateTransform();
+ // this parent check is for just in case the item is a root object.
+ // If it is we need to give it a temporary parent so that displayObjectUpdateTransform works correctly
+ // this is mainly to avoid a parent check in the main loop. Every little helps for performance :)
+ if (!this.parent) {
+ this.parent = this._tempDisplayObjectParent;
+ this.displayObjectUpdateTransform();
+ this.parent = null;
+ }
+ else {
+ this.displayObjectUpdateTransform();
+ }
+ }
+ // don't need to update the lot
+ return this.worldTransform.apply(position, point);
+ };
+ /**
+ * Calculates the local position of the display object relative to another point.
+ * @param position - The world origin to calculate from.
+ * @param from - The DisplayObject to calculate the global position from.
+ * @param point - A Point object in which to store the value, optional
+ * (otherwise will create a new Point).
+ * @param skipUpdate - Should we skip the update transform
+ * @returns - A point object representing the position of this object
+ */
+ DisplayObject.prototype.toLocal = function (position, from, point, skipUpdate) {
+ if (from) {
+ position = from.toGlobal(position, point, skipUpdate);
+ }
+ if (!skipUpdate) {
+ this._recursivePostUpdateTransform();
+ // this parent check is for just in case the item is a root object.
+ // If it is we need to give it a temporary parent so that displayObjectUpdateTransform works correctly
+ // this is mainly to avoid a parent check in the main loop. Every little helps for performance :)
+ if (!this.parent) {
+ this.parent = this._tempDisplayObjectParent;
+ this.displayObjectUpdateTransform();
+ this.parent = null;
+ }
+ else {
+ this.displayObjectUpdateTransform();
+ }
+ }
+ // simply apply the matrix..
+ return this.worldTransform.applyInverse(position, point);
+ };
+ /**
+ * Set the parent Container of this DisplayObject.
+ * @param container - The Container to add this DisplayObject to.
+ * @returns - The Container that this DisplayObject was added to.
+ */
+ DisplayObject.prototype.setParent = function (container) {
+ if (!container || !container.addChild) {
+ throw new Error('setParent: Argument must be a Container');
+ }
+ container.addChild(this);
+ return container;
+ };
+ /**
+ * Convenience function to set the position, scale, skew and pivot at once.
+ * @param x - The X position
+ * @param y - The Y position
+ * @param scaleX - The X scale value
+ * @param scaleY - The Y scale value
+ * @param rotation - The rotation
+ * @param skewX - The X skew value
+ * @param skewY - The Y skew value
+ * @param pivotX - The X pivot value
+ * @param pivotY - The Y pivot value
+ * @returns - The DisplayObject instance
+ */
+ DisplayObject.prototype.setTransform = function (x, y, scaleX, scaleY, rotation, skewX, skewY, pivotX, pivotY) {
+ if (x === void 0) { x = 0; }
+ if (y === void 0) { y = 0; }
+ if (scaleX === void 0) { scaleX = 1; }
+ if (scaleY === void 0) { scaleY = 1; }
+ if (rotation === void 0) { rotation = 0; }
+ if (skewX === void 0) { skewX = 0; }
+ if (skewY === void 0) { skewY = 0; }
+ if (pivotX === void 0) { pivotX = 0; }
+ if (pivotY === void 0) { pivotY = 0; }
+ this.position.x = x;
+ this.position.y = y;
+ this.scale.x = !scaleX ? 1 : scaleX;
+ this.scale.y = !scaleY ? 1 : scaleY;
+ this.rotation = rotation;
+ this.skew.x = skewX;
+ this.skew.y = skewY;
+ this.pivot.x = pivotX;
+ this.pivot.y = pivotY;
+ return this;
+ };
+ /**
+ * Base destroy method for generic display objects. This will automatically
+ * remove the display object from its parent Container as well as remove
+ * all current event listeners and internal references. Do not use a DisplayObject
+ * after calling `destroy()`.
+ * @param _options
+ */
+ DisplayObject.prototype.destroy = function (_options) {
+ if (this.parent) {
+ this.parent.removeChild(this);
+ }
+ this._destroyed = true;
+ this.transform = null;
+ this.parent = null;
+ this._bounds = null;
+ this.mask = null;
+ this.cullArea = null;
+ this.filters = null;
+ this.filterArea = null;
+ this.hitArea = null;
+ this.interactive = false;
+ this.interactiveChildren = false;
+ this.emit('destroyed');
+ this.removeAllListeners();
+ };
+ Object.defineProperty(DisplayObject.prototype, "_tempDisplayObjectParent", {
+ /**
+ * @protected
+ * @member {PIXI.Container}
+ */
+ get: function () {
+ if (this.tempDisplayObjectParent === null) {
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
+ this.tempDisplayObjectParent = new TemporaryDisplayObject();
+ }
+ return this.tempDisplayObjectParent;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Used in Renderer, cacheAsBitmap and other places where you call an `updateTransform` on root
+ *
+ * ```
+ * const cacheParent = elem.enableTempParent();
+ * elem.updateTransform();
+ * elem.disableTempParent(cacheParent);
+ * ```
+ * @returns - current parent
+ */
+ DisplayObject.prototype.enableTempParent = function () {
+ var myParent = this.parent;
+ this.parent = this._tempDisplayObjectParent;
+ return myParent;
+ };
+ /**
+ * Pair method for `enableTempParent`
+ * @param cacheParent - Actual parent of element
+ */
+ DisplayObject.prototype.disableTempParent = function (cacheParent) {
+ this.parent = cacheParent;
+ };
+ Object.defineProperty(DisplayObject.prototype, "x", {
+ /**
+ * The position of the displayObject on the x axis relative to the local coordinates of the parent.
+ * An alias to position.x
+ */
+ get: function () {
+ return this.position.x;
+ },
+ set: function (value) {
+ this.transform.position.x = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(DisplayObject.prototype, "y", {
+ /**
+ * The position of the displayObject on the y axis relative to the local coordinates of the parent.
+ * An alias to position.y
+ */
+ get: function () {
+ return this.position.y;
+ },
+ set: function (value) {
+ this.transform.position.y = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(DisplayObject.prototype, "worldTransform", {
+ /**
+ * Current transform of the object based on world (parent) factors.
+ * @readonly
+ */
+ get: function () {
+ return this.transform.worldTransform;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(DisplayObject.prototype, "localTransform", {
+ /**
+ * Current transform of the object based on local factors: position, scale, other stuff.
+ * @readonly
+ */
+ get: function () {
+ return this.transform.localTransform;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(DisplayObject.prototype, "position", {
+ /**
+ * The coordinate of the object relative to the local coordinates of the parent.
+ * @since 4.0.0
+ */
+ get: function () {
+ return this.transform.position;
+ },
+ set: function (value) {
+ this.transform.position.copyFrom(value);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(DisplayObject.prototype, "scale", {
+ /**
+ * The scale factors of this object along the local coordinate axes.
+ *
+ * The default scale is (1, 1).
+ * @since 4.0.0
+ */
+ get: function () {
+ return this.transform.scale;
+ },
+ set: function (value) {
+ this.transform.scale.copyFrom(value);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(DisplayObject.prototype, "pivot", {
+ /**
+ * The center of rotation, scaling, and skewing for this display object in its local space. The `position`
+ * is the projection of `pivot` in the parent's local space.
+ *
+ * By default, the pivot is the origin (0, 0).
+ * @since 4.0.0
+ */
+ get: function () {
+ return this.transform.pivot;
+ },
+ set: function (value) {
+ this.transform.pivot.copyFrom(value);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(DisplayObject.prototype, "skew", {
+ /**
+ * The skew factor for the object in radians.
+ * @since 4.0.0
+ */
+ get: function () {
+ return this.transform.skew;
+ },
+ set: function (value) {
+ this.transform.skew.copyFrom(value);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(DisplayObject.prototype, "rotation", {
+ /**
+ * The rotation of the object in radians.
+ * 'rotation' and 'angle' have the same effect on a display object; rotation is in radians, angle is in degrees.
+ */
+ get: function () {
+ return this.transform.rotation;
+ },
+ set: function (value) {
+ this.transform.rotation = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(DisplayObject.prototype, "angle", {
+ /**
+ * The angle of the object in degrees.
+ * 'rotation' and 'angle' have the same effect on a display object; rotation is in radians, angle is in degrees.
+ */
+ get: function () {
+ return this.transform.rotation * RAD_TO_DEG;
+ },
+ set: function (value) {
+ this.transform.rotation = value * DEG_TO_RAD;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(DisplayObject.prototype, "zIndex", {
+ /**
+ * The zIndex of the displayObject.
+ *
+ * If a container has the sortableChildren property set to true, children will be automatically
+ * sorted by zIndex value; a higher value will mean it will be moved towards the end of the array,
+ * and thus rendered on top of other display objects within the same container.
+ * @see PIXI.Container#sortableChildren
+ */
+ get: function () {
+ return this._zIndex;
+ },
+ set: function (value) {
+ this._zIndex = value;
+ if (this.parent) {
+ this.parent.sortDirty = true;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(DisplayObject.prototype, "worldVisible", {
+ /**
+ * Indicates if the object is globally visible.
+ * @readonly
+ */
+ get: function () {
+ var item = this;
+ do {
+ if (!item.visible) {
+ return false;
+ }
+ item = item.parent;
+ } while (item);
+ return true;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(DisplayObject.prototype, "mask", {
+ /**
+ * Sets a mask for the displayObject. A mask is an object that limits the visibility of an
+ * object to the shape of the mask applied to it. In PixiJS a regular mask must be a
+ * {@link PIXI.Graphics} or a {@link PIXI.Sprite} object. This allows for much faster masking in canvas as it
+ * utilities shape clipping. Furthermore, a mask of an object must be in the subtree of its parent.
+ * Otherwise, `getLocalBounds` may calculate incorrect bounds, which makes the container's width and height wrong.
+ * To remove a mask, set this property to `null`.
+ *
+ * For sprite mask both alpha and red channel are used. Black mask is the same as transparent mask.
+ * @example
+ * const graphics = new PIXI.Graphics();
+ * graphics.beginFill(0xFF3300);
+ * graphics.drawRect(50, 250, 100, 100);
+ * graphics.endFill();
+ *
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.mask = graphics;
+ * @todo At the moment, PIXI.CanvasRenderer doesn't support PIXI.Sprite as mask.
+ */
+ get: function () {
+ return this._mask;
+ },
+ set: function (value) {
+ if (this._mask === value) {
+ return;
+ }
+ if (this._mask) {
+ var maskObject = (this._mask.isMaskData
+ ? this._mask.maskObject : this._mask);
+ if (maskObject) {
+ maskObject._maskRefCount--;
+ if (maskObject._maskRefCount === 0) {
+ maskObject.renderable = true;
+ maskObject.isMask = false;
+ }
+ }
+ }
+ this._mask = value;
+ if (this._mask) {
+ var maskObject = (this._mask.isMaskData
+ ? this._mask.maskObject : this._mask);
+ if (maskObject) {
+ if (maskObject._maskRefCount === 0) {
+ maskObject.renderable = false;
+ maskObject.isMask = true;
+ }
+ maskObject._maskRefCount++;
+ }
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return DisplayObject;
+ }(eventemitter3));
+ /**
+ * @private
+ */
+ var TemporaryDisplayObject = /** @class */ (function (_super) {
+ __extends$j(TemporaryDisplayObject, _super);
+ function TemporaryDisplayObject() {
+ var _this = _super !== null && _super.apply(this, arguments) || this;
+ _this.sortDirty = null;
+ return _this;
+ }
+ return TemporaryDisplayObject;
+ }(DisplayObject));
+ /**
+ * DisplayObject default updateTransform, does not update children of container.
+ * Will crash if there's no parent element.
+ * @memberof PIXI.DisplayObject#
+ * @method displayObjectUpdateTransform
+ */
+ DisplayObject.prototype.displayObjectUpdateTransform = DisplayObject.prototype.updateTransform;
+
+ /*!
+ * @pixi/constants - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/constants is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+ /**
+ * Different types of environments for WebGL.
+ * @static
+ * @memberof PIXI
+ * @name ENV
+ * @enum {number}
+ * @property {number} WEBGL_LEGACY - Used for older v1 WebGL devices. PixiJS will aim to ensure compatibility
+ * with older / less advanced devices. If you experience unexplained flickering prefer this environment.
+ * @property {number} WEBGL - Version 1 of WebGL
+ * @property {number} WEBGL2 - Version 2 of WebGL
+ */
+ var ENV$4;
+ (function (ENV) {
+ ENV[ENV["WEBGL_LEGACY"] = 0] = "WEBGL_LEGACY";
+ ENV[ENV["WEBGL"] = 1] = "WEBGL";
+ ENV[ENV["WEBGL2"] = 2] = "WEBGL2";
+ })(ENV$4 || (ENV$4 = {}));
+ /**
+ * Constant to identify the Renderer Type.
+ * @static
+ * @memberof PIXI
+ * @name RENDERER_TYPE
+ * @enum {number}
+ * @property {number} UNKNOWN - Unknown render type.
+ * @property {number} WEBGL - WebGL render type.
+ * @property {number} CANVAS - Canvas render type.
+ */
+ var RENDERER_TYPE$4;
+ (function (RENDERER_TYPE) {
+ RENDERER_TYPE[RENDERER_TYPE["UNKNOWN"] = 0] = "UNKNOWN";
+ RENDERER_TYPE[RENDERER_TYPE["WEBGL"] = 1] = "WEBGL";
+ RENDERER_TYPE[RENDERER_TYPE["CANVAS"] = 2] = "CANVAS";
+ })(RENDERER_TYPE$4 || (RENDERER_TYPE$4 = {}));
+ /**
+ * Bitwise OR of masks that indicate the buffers to be cleared.
+ * @static
+ * @memberof PIXI
+ * @name BUFFER_BITS
+ * @enum {number}
+ * @property {number} COLOR - Indicates the buffers currently enabled for color writing.
+ * @property {number} DEPTH - Indicates the depth buffer.
+ * @property {number} STENCIL - Indicates the stencil buffer.
+ */
+ var BUFFER_BITS$4;
+ (function (BUFFER_BITS) {
+ BUFFER_BITS[BUFFER_BITS["COLOR"] = 16384] = "COLOR";
+ BUFFER_BITS[BUFFER_BITS["DEPTH"] = 256] = "DEPTH";
+ BUFFER_BITS[BUFFER_BITS["STENCIL"] = 1024] = "STENCIL";
+ })(BUFFER_BITS$4 || (BUFFER_BITS$4 = {}));
+ /**
+ * Various blend modes supported by PIXI.
+ *
+ * IMPORTANT - The WebGL renderer only supports the NORMAL, ADD, MULTIPLY and SCREEN blend modes.
+ * Anything else will silently act like NORMAL.
+ * @memberof PIXI
+ * @name BLEND_MODES
+ * @enum {number}
+ * @property {number} NORMAL -
+ * @property {number} ADD -
+ * @property {number} MULTIPLY -
+ * @property {number} SCREEN -
+ * @property {number} OVERLAY -
+ * @property {number} DARKEN -
+ * @property {number} LIGHTEN -
+ * @property {number} COLOR_DODGE -
+ * @property {number} COLOR_BURN -
+ * @property {number} HARD_LIGHT -
+ * @property {number} SOFT_LIGHT -
+ * @property {number} DIFFERENCE -
+ * @property {number} EXCLUSION -
+ * @property {number} HUE -
+ * @property {number} SATURATION -
+ * @property {number} COLOR -
+ * @property {number} LUMINOSITY -
+ * @property {number} NORMAL_NPM -
+ * @property {number} ADD_NPM -
+ * @property {number} SCREEN_NPM -
+ * @property {number} NONE -
+ * @property {number} SRC_IN -
+ * @property {number} SRC_OUT -
+ * @property {number} SRC_ATOP -
+ * @property {number} DST_OVER -
+ * @property {number} DST_IN -
+ * @property {number} DST_OUT -
+ * @property {number} DST_ATOP -
+ * @property {number} SUBTRACT -
+ * @property {number} SRC_OVER -
+ * @property {number} ERASE -
+ * @property {number} XOR -
+ */
+ var BLEND_MODES$4;
+ (function (BLEND_MODES) {
+ BLEND_MODES[BLEND_MODES["NORMAL"] = 0] = "NORMAL";
+ BLEND_MODES[BLEND_MODES["ADD"] = 1] = "ADD";
+ BLEND_MODES[BLEND_MODES["MULTIPLY"] = 2] = "MULTIPLY";
+ BLEND_MODES[BLEND_MODES["SCREEN"] = 3] = "SCREEN";
+ BLEND_MODES[BLEND_MODES["OVERLAY"] = 4] = "OVERLAY";
+ BLEND_MODES[BLEND_MODES["DARKEN"] = 5] = "DARKEN";
+ BLEND_MODES[BLEND_MODES["LIGHTEN"] = 6] = "LIGHTEN";
+ BLEND_MODES[BLEND_MODES["COLOR_DODGE"] = 7] = "COLOR_DODGE";
+ BLEND_MODES[BLEND_MODES["COLOR_BURN"] = 8] = "COLOR_BURN";
+ BLEND_MODES[BLEND_MODES["HARD_LIGHT"] = 9] = "HARD_LIGHT";
+ BLEND_MODES[BLEND_MODES["SOFT_LIGHT"] = 10] = "SOFT_LIGHT";
+ BLEND_MODES[BLEND_MODES["DIFFERENCE"] = 11] = "DIFFERENCE";
+ BLEND_MODES[BLEND_MODES["EXCLUSION"] = 12] = "EXCLUSION";
+ BLEND_MODES[BLEND_MODES["HUE"] = 13] = "HUE";
+ BLEND_MODES[BLEND_MODES["SATURATION"] = 14] = "SATURATION";
+ BLEND_MODES[BLEND_MODES["COLOR"] = 15] = "COLOR";
+ BLEND_MODES[BLEND_MODES["LUMINOSITY"] = 16] = "LUMINOSITY";
+ BLEND_MODES[BLEND_MODES["NORMAL_NPM"] = 17] = "NORMAL_NPM";
+ BLEND_MODES[BLEND_MODES["ADD_NPM"] = 18] = "ADD_NPM";
+ BLEND_MODES[BLEND_MODES["SCREEN_NPM"] = 19] = "SCREEN_NPM";
+ BLEND_MODES[BLEND_MODES["NONE"] = 20] = "NONE";
+ BLEND_MODES[BLEND_MODES["SRC_OVER"] = 0] = "SRC_OVER";
+ BLEND_MODES[BLEND_MODES["SRC_IN"] = 21] = "SRC_IN";
+ BLEND_MODES[BLEND_MODES["SRC_OUT"] = 22] = "SRC_OUT";
+ BLEND_MODES[BLEND_MODES["SRC_ATOP"] = 23] = "SRC_ATOP";
+ BLEND_MODES[BLEND_MODES["DST_OVER"] = 24] = "DST_OVER";
+ BLEND_MODES[BLEND_MODES["DST_IN"] = 25] = "DST_IN";
+ BLEND_MODES[BLEND_MODES["DST_OUT"] = 26] = "DST_OUT";
+ BLEND_MODES[BLEND_MODES["DST_ATOP"] = 27] = "DST_ATOP";
+ BLEND_MODES[BLEND_MODES["ERASE"] = 26] = "ERASE";
+ BLEND_MODES[BLEND_MODES["SUBTRACT"] = 28] = "SUBTRACT";
+ BLEND_MODES[BLEND_MODES["XOR"] = 29] = "XOR";
+ })(BLEND_MODES$4 || (BLEND_MODES$4 = {}));
+ /**
+ * Various webgl draw modes. These can be used to specify which GL drawMode to use
+ * under certain situations and renderers.
+ * @memberof PIXI
+ * @static
+ * @name DRAW_MODES
+ * @enum {number}
+ * @property {number} POINTS -
+ * @property {number} LINES -
+ * @property {number} LINE_LOOP -
+ * @property {number} LINE_STRIP -
+ * @property {number} TRIANGLES -
+ * @property {number} TRIANGLE_STRIP -
+ * @property {number} TRIANGLE_FAN -
+ */
+ var DRAW_MODES$4;
+ (function (DRAW_MODES) {
+ DRAW_MODES[DRAW_MODES["POINTS"] = 0] = "POINTS";
+ DRAW_MODES[DRAW_MODES["LINES"] = 1] = "LINES";
+ DRAW_MODES[DRAW_MODES["LINE_LOOP"] = 2] = "LINE_LOOP";
+ DRAW_MODES[DRAW_MODES["LINE_STRIP"] = 3] = "LINE_STRIP";
+ DRAW_MODES[DRAW_MODES["TRIANGLES"] = 4] = "TRIANGLES";
+ DRAW_MODES[DRAW_MODES["TRIANGLE_STRIP"] = 5] = "TRIANGLE_STRIP";
+ DRAW_MODES[DRAW_MODES["TRIANGLE_FAN"] = 6] = "TRIANGLE_FAN";
+ })(DRAW_MODES$4 || (DRAW_MODES$4 = {}));
+ /**
+ * Various GL texture/resources formats.
+ * @memberof PIXI
+ * @static
+ * @name FORMATS
+ * @enum {number}
+ * @property {number} [RGBA=6408] -
+ * @property {number} [RGB=6407] -
+ * @property {number} [RG=33319] -
+ * @property {number} [RED=6403] -
+ * @property {number} [RGBA_INTEGER=36249] -
+ * @property {number} [RGB_INTEGER=36248] -
+ * @property {number} [RG_INTEGER=33320] -
+ * @property {number} [RED_INTEGER=36244] -
+ * @property {number} [ALPHA=6406] -
+ * @property {number} [LUMINANCE=6409] -
+ * @property {number} [LUMINANCE_ALPHA=6410] -
+ * @property {number} [DEPTH_COMPONENT=6402] -
+ * @property {number} [DEPTH_STENCIL=34041] -
+ */
+ var FORMATS$4;
+ (function (FORMATS) {
+ FORMATS[FORMATS["RGBA"] = 6408] = "RGBA";
+ FORMATS[FORMATS["RGB"] = 6407] = "RGB";
+ FORMATS[FORMATS["RG"] = 33319] = "RG";
+ FORMATS[FORMATS["RED"] = 6403] = "RED";
+ FORMATS[FORMATS["RGBA_INTEGER"] = 36249] = "RGBA_INTEGER";
+ FORMATS[FORMATS["RGB_INTEGER"] = 36248] = "RGB_INTEGER";
+ FORMATS[FORMATS["RG_INTEGER"] = 33320] = "RG_INTEGER";
+ FORMATS[FORMATS["RED_INTEGER"] = 36244] = "RED_INTEGER";
+ FORMATS[FORMATS["ALPHA"] = 6406] = "ALPHA";
+ FORMATS[FORMATS["LUMINANCE"] = 6409] = "LUMINANCE";
+ FORMATS[FORMATS["LUMINANCE_ALPHA"] = 6410] = "LUMINANCE_ALPHA";
+ FORMATS[FORMATS["DEPTH_COMPONENT"] = 6402] = "DEPTH_COMPONENT";
+ FORMATS[FORMATS["DEPTH_STENCIL"] = 34041] = "DEPTH_STENCIL";
+ })(FORMATS$4 || (FORMATS$4 = {}));
+ /**
+ * Various GL target types.
+ * @memberof PIXI
+ * @static
+ * @name TARGETS
+ * @enum {number}
+ * @property {number} [TEXTURE_2D=3553] -
+ * @property {number} [TEXTURE_CUBE_MAP=34067] -
+ * @property {number} [TEXTURE_2D_ARRAY=35866] -
+ * @property {number} [TEXTURE_CUBE_MAP_POSITIVE_X=34069] -
+ * @property {number} [TEXTURE_CUBE_MAP_NEGATIVE_X=34070] -
+ * @property {number} [TEXTURE_CUBE_MAP_POSITIVE_Y=34071] -
+ * @property {number} [TEXTURE_CUBE_MAP_NEGATIVE_Y=34072] -
+ * @property {number} [TEXTURE_CUBE_MAP_POSITIVE_Z=34073] -
+ * @property {number} [TEXTURE_CUBE_MAP_NEGATIVE_Z=34074] -
+ */
+ var TARGETS$4;
+ (function (TARGETS) {
+ TARGETS[TARGETS["TEXTURE_2D"] = 3553] = "TEXTURE_2D";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP"] = 34067] = "TEXTURE_CUBE_MAP";
+ TARGETS[TARGETS["TEXTURE_2D_ARRAY"] = 35866] = "TEXTURE_2D_ARRAY";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_POSITIVE_X"] = 34069] = "TEXTURE_CUBE_MAP_POSITIVE_X";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_NEGATIVE_X"] = 34070] = "TEXTURE_CUBE_MAP_NEGATIVE_X";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_POSITIVE_Y"] = 34071] = "TEXTURE_CUBE_MAP_POSITIVE_Y";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_NEGATIVE_Y"] = 34072] = "TEXTURE_CUBE_MAP_NEGATIVE_Y";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_POSITIVE_Z"] = 34073] = "TEXTURE_CUBE_MAP_POSITIVE_Z";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_NEGATIVE_Z"] = 34074] = "TEXTURE_CUBE_MAP_NEGATIVE_Z";
+ })(TARGETS$4 || (TARGETS$4 = {}));
+ /**
+ * Various GL data format types.
+ * @memberof PIXI
+ * @static
+ * @name TYPES
+ * @enum {number}
+ * @property {number} [UNSIGNED_BYTE=5121] -
+ * @property {number} [UNSIGNED_SHORT=5123] -
+ * @property {number} [UNSIGNED_SHORT_5_6_5=33635] -
+ * @property {number} [UNSIGNED_SHORT_4_4_4_4=32819] -
+ * @property {number} [UNSIGNED_SHORT_5_5_5_1=32820] -
+ * @property {number} [UNSIGNED_INT=5125] -
+ * @property {number} [UNSIGNED_INT_10F_11F_11F_REV=35899] -
+ * @property {number} [UNSIGNED_INT_2_10_10_10_REV=33640] -
+ * @property {number} [UNSIGNED_INT_24_8=34042] -
+ * @property {number} [UNSIGNED_INT_5_9_9_9_REV=35902] -
+ * @property {number} [BYTE=5120] -
+ * @property {number} [SHORT=5122] -
+ * @property {number} [INT=5124] -
+ * @property {number} [FLOAT=5126] -
+ * @property {number} [FLOAT_32_UNSIGNED_INT_24_8_REV=36269] -
+ * @property {number} [HALF_FLOAT=36193] -
+ */
+ var TYPES$4;
+ (function (TYPES) {
+ TYPES[TYPES["UNSIGNED_BYTE"] = 5121] = "UNSIGNED_BYTE";
+ TYPES[TYPES["UNSIGNED_SHORT"] = 5123] = "UNSIGNED_SHORT";
+ TYPES[TYPES["UNSIGNED_SHORT_5_6_5"] = 33635] = "UNSIGNED_SHORT_5_6_5";
+ TYPES[TYPES["UNSIGNED_SHORT_4_4_4_4"] = 32819] = "UNSIGNED_SHORT_4_4_4_4";
+ TYPES[TYPES["UNSIGNED_SHORT_5_5_5_1"] = 32820] = "UNSIGNED_SHORT_5_5_5_1";
+ TYPES[TYPES["UNSIGNED_INT"] = 5125] = "UNSIGNED_INT";
+ TYPES[TYPES["UNSIGNED_INT_10F_11F_11F_REV"] = 35899] = "UNSIGNED_INT_10F_11F_11F_REV";
+ TYPES[TYPES["UNSIGNED_INT_2_10_10_10_REV"] = 33640] = "UNSIGNED_INT_2_10_10_10_REV";
+ TYPES[TYPES["UNSIGNED_INT_24_8"] = 34042] = "UNSIGNED_INT_24_8";
+ TYPES[TYPES["UNSIGNED_INT_5_9_9_9_REV"] = 35902] = "UNSIGNED_INT_5_9_9_9_REV";
+ TYPES[TYPES["BYTE"] = 5120] = "BYTE";
+ TYPES[TYPES["SHORT"] = 5122] = "SHORT";
+ TYPES[TYPES["INT"] = 5124] = "INT";
+ TYPES[TYPES["FLOAT"] = 5126] = "FLOAT";
+ TYPES[TYPES["FLOAT_32_UNSIGNED_INT_24_8_REV"] = 36269] = "FLOAT_32_UNSIGNED_INT_24_8_REV";
+ TYPES[TYPES["HALF_FLOAT"] = 36193] = "HALF_FLOAT";
+ })(TYPES$4 || (TYPES$4 = {}));
+ /**
+ * Various sampler types. Correspond to `sampler`, `isampler`, `usampler` GLSL types respectively.
+ * WebGL1 works only with FLOAT.
+ * @memberof PIXI
+ * @static
+ * @name SAMPLER_TYPES
+ * @enum {number}
+ * @property {number} [FLOAT=0] -
+ * @property {number} [INT=1] -
+ * @property {number} [UINT=2] -
+ */
+ var SAMPLER_TYPES$4;
+ (function (SAMPLER_TYPES) {
+ SAMPLER_TYPES[SAMPLER_TYPES["FLOAT"] = 0] = "FLOAT";
+ SAMPLER_TYPES[SAMPLER_TYPES["INT"] = 1] = "INT";
+ SAMPLER_TYPES[SAMPLER_TYPES["UINT"] = 2] = "UINT";
+ })(SAMPLER_TYPES$4 || (SAMPLER_TYPES$4 = {}));
+ /**
+ * The scale modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.SCALE_MODE} scale mode affects the default scaling mode of future operations.
+ * It can be re-assigned to either LINEAR or NEAREST, depending upon suitability.
+ * @memberof PIXI
+ * @static
+ * @name SCALE_MODES
+ * @enum {number}
+ * @property {number} LINEAR Smooth scaling
+ * @property {number} NEAREST Pixelating scaling
+ */
+ var SCALE_MODES$4;
+ (function (SCALE_MODES) {
+ SCALE_MODES[SCALE_MODES["NEAREST"] = 0] = "NEAREST";
+ SCALE_MODES[SCALE_MODES["LINEAR"] = 1] = "LINEAR";
+ })(SCALE_MODES$4 || (SCALE_MODES$4 = {}));
+ /**
+ * The wrap modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.WRAP_MODE} wrap mode affects the default wrapping mode of future operations.
+ * It can be re-assigned to either CLAMP or REPEAT, depending upon suitability.
+ * If the texture is non power of two then clamp will be used regardless as WebGL can
+ * only use REPEAT if the texture is po2.
+ *
+ * This property only affects WebGL.
+ * @name WRAP_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} CLAMP - The textures uvs are clamped
+ * @property {number} REPEAT - The texture uvs tile and repeat
+ * @property {number} MIRRORED_REPEAT - The texture uvs tile and repeat with mirroring
+ */
+ var WRAP_MODES$4;
+ (function (WRAP_MODES) {
+ WRAP_MODES[WRAP_MODES["CLAMP"] = 33071] = "CLAMP";
+ WRAP_MODES[WRAP_MODES["REPEAT"] = 10497] = "REPEAT";
+ WRAP_MODES[WRAP_MODES["MIRRORED_REPEAT"] = 33648] = "MIRRORED_REPEAT";
+ })(WRAP_MODES$4 || (WRAP_MODES$4 = {}));
+ /**
+ * Mipmap filtering modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.MIPMAP_TEXTURES} affects default texture filtering.
+ * Mipmaps are generated for a baseTexture if its `mipmap` field is `ON`,
+ * or its `POW2` and texture dimensions are powers of 2.
+ * Due to platform restriction, `ON` option will work like `POW2` for webgl-1.
+ *
+ * This property only affects WebGL.
+ * @name MIPMAP_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} OFF - No mipmaps
+ * @property {number} POW2 - Generate mipmaps if texture dimensions are pow2
+ * @property {number} ON - Always generate mipmaps
+ * @property {number} ON_MANUAL - Use mipmaps, but do not auto-generate them; this is used with a resource
+ * that supports buffering each level-of-detail.
+ */
+ var MIPMAP_MODES$4;
+ (function (MIPMAP_MODES) {
+ MIPMAP_MODES[MIPMAP_MODES["OFF"] = 0] = "OFF";
+ MIPMAP_MODES[MIPMAP_MODES["POW2"] = 1] = "POW2";
+ MIPMAP_MODES[MIPMAP_MODES["ON"] = 2] = "ON";
+ MIPMAP_MODES[MIPMAP_MODES["ON_MANUAL"] = 3] = "ON_MANUAL";
+ })(MIPMAP_MODES$4 || (MIPMAP_MODES$4 = {}));
+ /**
+ * How to treat textures with premultiplied alpha
+ * @name ALPHA_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} NO_PREMULTIPLIED_ALPHA - Source is not premultiplied, leave it like that.
+ * Option for compressed and data textures that are created from typed arrays.
+ * @property {number} PREMULTIPLY_ON_UPLOAD - Source is not premultiplied, premultiply on upload.
+ * Default option, used for all loaded images.
+ * @property {number} PREMULTIPLIED_ALPHA - Source is already premultiplied
+ * Example: spine atlases with `_pma` suffix.
+ * @property {number} NPM - Alias for NO_PREMULTIPLIED_ALPHA.
+ * @property {number} UNPACK - Default option, alias for PREMULTIPLY_ON_UPLOAD.
+ * @property {number} PMA - Alias for PREMULTIPLIED_ALPHA.
+ */
+ var ALPHA_MODES$4;
+ (function (ALPHA_MODES) {
+ ALPHA_MODES[ALPHA_MODES["NPM"] = 0] = "NPM";
+ ALPHA_MODES[ALPHA_MODES["UNPACK"] = 1] = "UNPACK";
+ ALPHA_MODES[ALPHA_MODES["PMA"] = 2] = "PMA";
+ ALPHA_MODES[ALPHA_MODES["NO_PREMULTIPLIED_ALPHA"] = 0] = "NO_PREMULTIPLIED_ALPHA";
+ ALPHA_MODES[ALPHA_MODES["PREMULTIPLY_ON_UPLOAD"] = 1] = "PREMULTIPLY_ON_UPLOAD";
+ ALPHA_MODES[ALPHA_MODES["PREMULTIPLY_ALPHA"] = 2] = "PREMULTIPLY_ALPHA";
+ ALPHA_MODES[ALPHA_MODES["PREMULTIPLIED_ALPHA"] = 2] = "PREMULTIPLIED_ALPHA";
+ })(ALPHA_MODES$4 || (ALPHA_MODES$4 = {}));
+ /**
+ * Configure whether filter textures are cleared after binding.
+ *
+ * Filter textures need not be cleared if the filter does not use pixel blending. {@link CLEAR_MODES.BLIT} will detect
+ * this and skip clearing as an optimization.
+ * @name CLEAR_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} BLEND - Do not clear the filter texture. The filter's output will blend on top of the output texture.
+ * @property {number} CLEAR - Always clear the filter texture.
+ * @property {number} BLIT - Clear only if {@link FilterSystem.forceClear} is set or if the filter uses pixel blending.
+ * @property {number} NO - Alias for BLEND, same as `false` in earlier versions
+ * @property {number} YES - Alias for CLEAR, same as `true` in earlier versions
+ * @property {number} AUTO - Alias for BLIT
+ */
+ var CLEAR_MODES$4;
+ (function (CLEAR_MODES) {
+ CLEAR_MODES[CLEAR_MODES["NO"] = 0] = "NO";
+ CLEAR_MODES[CLEAR_MODES["YES"] = 1] = "YES";
+ CLEAR_MODES[CLEAR_MODES["AUTO"] = 2] = "AUTO";
+ CLEAR_MODES[CLEAR_MODES["BLEND"] = 0] = "BLEND";
+ CLEAR_MODES[CLEAR_MODES["CLEAR"] = 1] = "CLEAR";
+ CLEAR_MODES[CLEAR_MODES["BLIT"] = 2] = "BLIT";
+ })(CLEAR_MODES$4 || (CLEAR_MODES$4 = {}));
+ /**
+ * The gc modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.GC_MODE} Garbage Collection mode for PixiJS textures is AUTO
+ * If set to GC_MODE, the renderer will occasionally check textures usage. If they are not
+ * used for a specified period of time they will be removed from the GPU. They will of course
+ * be uploaded again when they are required. This is a silent behind the scenes process that
+ * should ensure that the GPU does not get filled up.
+ *
+ * Handy for mobile devices!
+ * This property only affects WebGL.
+ * @name GC_MODES
+ * @enum {number}
+ * @static
+ * @memberof PIXI
+ * @property {number} AUTO - Garbage collection will happen periodically automatically
+ * @property {number} MANUAL - Garbage collection will need to be called manually
+ */
+ var GC_MODES$4;
+ (function (GC_MODES) {
+ GC_MODES[GC_MODES["AUTO"] = 0] = "AUTO";
+ GC_MODES[GC_MODES["MANUAL"] = 1] = "MANUAL";
+ })(GC_MODES$4 || (GC_MODES$4 = {}));
+ /**
+ * Constants that specify float precision in shaders.
+ * @name PRECISION
+ * @memberof PIXI
+ * @constant
+ * @static
+ * @enum {string}
+ * @property {string} [LOW='lowp'] -
+ * @property {string} [MEDIUM='mediump'] -
+ * @property {string} [HIGH='highp'] -
+ */
+ var PRECISION$4;
+ (function (PRECISION) {
+ PRECISION["LOW"] = "lowp";
+ PRECISION["MEDIUM"] = "mediump";
+ PRECISION["HIGH"] = "highp";
+ })(PRECISION$4 || (PRECISION$4 = {}));
+ /**
+ * Constants for mask implementations.
+ * We use `type` suffix because it leads to very different behaviours
+ * @name MASK_TYPES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} NONE - Mask is ignored
+ * @property {number} SCISSOR - Scissor mask, rectangle on screen, cheap
+ * @property {number} STENCIL - Stencil mask, 1-bit, medium, works only if renderer supports stencil
+ * @property {number} SPRITE - Mask that uses SpriteMaskFilter, uses temporary RenderTexture
+ * @property {number} COLOR - Color mask (RGBA)
+ */
+ var MASK_TYPES$4;
+ (function (MASK_TYPES) {
+ MASK_TYPES[MASK_TYPES["NONE"] = 0] = "NONE";
+ MASK_TYPES[MASK_TYPES["SCISSOR"] = 1] = "SCISSOR";
+ MASK_TYPES[MASK_TYPES["STENCIL"] = 2] = "STENCIL";
+ MASK_TYPES[MASK_TYPES["SPRITE"] = 3] = "SPRITE";
+ MASK_TYPES[MASK_TYPES["COLOR"] = 4] = "COLOR";
+ })(MASK_TYPES$4 || (MASK_TYPES$4 = {}));
+ /**
+ * Bitwise OR of masks that indicate the color channels that are rendered to.
+ * @static
+ * @memberof PIXI
+ * @name COLOR_MASK_BITS
+ * @enum {number}
+ * @property {number} RED - Red channel.
+ * @property {number} GREEN - Green channel
+ * @property {number} BLUE - Blue channel.
+ * @property {number} ALPHA - Alpha channel.
+ */
+ var COLOR_MASK_BITS$4;
+ (function (COLOR_MASK_BITS) {
+ COLOR_MASK_BITS[COLOR_MASK_BITS["RED"] = 1] = "RED";
+ COLOR_MASK_BITS[COLOR_MASK_BITS["GREEN"] = 2] = "GREEN";
+ COLOR_MASK_BITS[COLOR_MASK_BITS["BLUE"] = 4] = "BLUE";
+ COLOR_MASK_BITS[COLOR_MASK_BITS["ALPHA"] = 8] = "ALPHA";
+ })(COLOR_MASK_BITS$4 || (COLOR_MASK_BITS$4 = {}));
+ /**
+ * Constants for multi-sampling antialiasing.
+ * @see PIXI.Framebuffer#multisample
+ * @name MSAA_QUALITY
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} NONE - No multisampling for this renderTexture
+ * @property {number} LOW - Try 2 samples
+ * @property {number} MEDIUM - Try 4 samples
+ * @property {number} HIGH - Try 8 samples
+ */
+ var MSAA_QUALITY$4;
+ (function (MSAA_QUALITY) {
+ MSAA_QUALITY[MSAA_QUALITY["NONE"] = 0] = "NONE";
+ MSAA_QUALITY[MSAA_QUALITY["LOW"] = 2] = "LOW";
+ MSAA_QUALITY[MSAA_QUALITY["MEDIUM"] = 4] = "MEDIUM";
+ MSAA_QUALITY[MSAA_QUALITY["HIGH"] = 8] = "HIGH";
+ })(MSAA_QUALITY$4 || (MSAA_QUALITY$4 = {}));
+ /**
+ * Constants for various buffer types in Pixi
+ * @see PIXI.BUFFER_TYPE
+ * @name BUFFER_TYPE
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} ELEMENT_ARRAY_BUFFER - buffer type for using as an index buffer
+ * @property {number} ARRAY_BUFFER - buffer type for using attribute data
+ * @property {number} UNIFORM_BUFFER - the buffer type is for uniform buffer objects
+ */
+ var BUFFER_TYPE$4;
+ (function (BUFFER_TYPE) {
+ BUFFER_TYPE[BUFFER_TYPE["ELEMENT_ARRAY_BUFFER"] = 34963] = "ELEMENT_ARRAY_BUFFER";
+ BUFFER_TYPE[BUFFER_TYPE["ARRAY_BUFFER"] = 34962] = "ARRAY_BUFFER";
+ // NOT YET SUPPORTED
+ BUFFER_TYPE[BUFFER_TYPE["UNIFORM_BUFFER"] = 35345] = "UNIFORM_BUFFER";
+ })(BUFFER_TYPE$4 || (BUFFER_TYPE$4 = {}));
+
+ function sortChildren(a, b) {
+ if (a.zIndex === b.zIndex) {
+ return a._lastSortedIndex - b._lastSortedIndex;
+ }
+ return a.zIndex - b.zIndex;
+ }
+ /**
+ * Container is a general-purpose display object that holds children. It also adds built-in support for advanced
+ * rendering features like masking and filtering.
+ *
+ * It is the base class of all display objects that act as a container for other objects, including Graphics
+ * and Sprite.
+ *
+ * ```js
+ * import { BlurFilter } from '@pixi/filter-blur';
+ * import { Container } from '@pixi/display';
+ * import { Graphics } from '@pixi/graphics';
+ * import { Sprite } from '@pixi/sprite';
+ *
+ * let container = new Container();
+ * let sprite = Sprite.from("https://s3-us-west-2.amazonaws.com/s.cdpn.io/693612/IaUrttj.png");
+ *
+ * sprite.width = 512;
+ * sprite.height = 512;
+ *
+ * // Adds a sprite as a child to this container. As a result, the sprite will be rendered whenever the container
+ * // is rendered.
+ * container.addChild(sprite);
+ *
+ * // Blurs whatever is rendered by the container
+ * container.filters = [new BlurFilter()];
+ *
+ * // Only the contents within a circle at the center should be rendered onto the screen.
+ * container.mask = new Graphics()
+ * .beginFill(0xffffff)
+ * .drawCircle(sprite.width / 2, sprite.height / 2, Math.min(sprite.width, sprite.height) / 2)
+ * .endFill();
+ * ```
+ * @memberof PIXI
+ */
+ var Container = /** @class */ (function (_super) {
+ __extends$j(Container, _super);
+ function Container() {
+ var _this = _super.call(this) || this;
+ _this.children = [];
+ _this.sortableChildren = settings$1.SORTABLE_CHILDREN;
+ _this.sortDirty = false;
+ return _this;
+ /**
+ * Fired when a DisplayObject is added to this Container.
+ * @event PIXI.Container#childAdded
+ * @param {PIXI.DisplayObject} child - The child added to the Container.
+ * @param {PIXI.Container} container - The container that added the child.
+ * @param {number} index - The children's index of the added child.
+ */
+ /**
+ * Fired when a DisplayObject is removed from this Container.
+ * @event PIXI.DisplayObject#childRemoved
+ * @param {PIXI.DisplayObject} child - The child removed from the Container.
+ * @param {PIXI.Container} container - The container that removed the child.
+ * @param {number} index - The former children's index of the removed child
+ */
+ }
+ /**
+ * Overridable method that can be used by Container subclasses whenever the children array is modified.
+ * @param _length
+ */
+ Container.prototype.onChildrenChange = function (_length) {
+ /* empty */
+ };
+ /**
+ * Adds one or more children to the container.
+ *
+ * Multiple items can be added like so: `myContainer.addChild(thingOne, thingTwo, thingThree)`
+ * @param {...PIXI.DisplayObject} children - The DisplayObject(s) to add to the container
+ * @returns {PIXI.DisplayObject} - The first child that was added.
+ */
+ Container.prototype.addChild = function () {
+ var arguments$1 = arguments;
+
+ var children = [];
+ for (var _i = 0; _i < arguments.length; _i++) {
+ children[_i] = arguments$1[_i];
+ }
+ // if there is only one argument we can bypass looping through the them
+ if (children.length > 1) {
+ // loop through the array and add all children
+ for (var i = 0; i < children.length; i++) {
+ // eslint-disable-next-line prefer-rest-params
+ this.addChild(children[i]);
+ }
+ }
+ else {
+ var child = children[0];
+ // if the child has a parent then lets remove it as PixiJS objects can only exist in one place
+ if (child.parent) {
+ child.parent.removeChild(child);
+ }
+ child.parent = this;
+ this.sortDirty = true;
+ // ensure child transform will be recalculated
+ child.transform._parentID = -1;
+ this.children.push(child);
+ // ensure bounds will be recalculated
+ this._boundsID++;
+ // TODO - lets either do all callbacks or all events.. not both!
+ this.onChildrenChange(this.children.length - 1);
+ this.emit('childAdded', child, this, this.children.length - 1);
+ child.emit('added', this);
+ }
+ return children[0];
+ };
+ /**
+ * Adds a child to the container at a specified index. If the index is out of bounds an error will be thrown
+ * @param {PIXI.DisplayObject} child - The child to add
+ * @param {number} index - The index to place the child in
+ * @returns {PIXI.DisplayObject} The child that was added.
+ */
+ Container.prototype.addChildAt = function (child, index) {
+ if (index < 0 || index > this.children.length) {
+ throw new Error(child + "addChildAt: The index " + index + " supplied is out of bounds " + this.children.length);
+ }
+ if (child.parent) {
+ child.parent.removeChild(child);
+ }
+ child.parent = this;
+ this.sortDirty = true;
+ // ensure child transform will be recalculated
+ child.transform._parentID = -1;
+ this.children.splice(index, 0, child);
+ // ensure bounds will be recalculated
+ this._boundsID++;
+ // TODO - lets either do all callbacks or all events.. not both!
+ this.onChildrenChange(index);
+ child.emit('added', this);
+ this.emit('childAdded', child, this, index);
+ return child;
+ };
+ /**
+ * Swaps the position of 2 Display Objects within this container.
+ * @param child - First display object to swap
+ * @param child2 - Second display object to swap
+ */
+ Container.prototype.swapChildren = function (child, child2) {
+ if (child === child2) {
+ return;
+ }
+ var index1 = this.getChildIndex(child);
+ var index2 = this.getChildIndex(child2);
+ this.children[index1] = child2;
+ this.children[index2] = child;
+ this.onChildrenChange(index1 < index2 ? index1 : index2);
+ };
+ /**
+ * Returns the index position of a child DisplayObject instance
+ * @param child - The DisplayObject instance to identify
+ * @returns - The index position of the child display object to identify
+ */
+ Container.prototype.getChildIndex = function (child) {
+ var index = this.children.indexOf(child);
+ if (index === -1) {
+ throw new Error('The supplied DisplayObject must be a child of the caller');
+ }
+ return index;
+ };
+ /**
+ * Changes the position of an existing child in the display object container
+ * @param child - The child DisplayObject instance for which you want to change the index number
+ * @param index - The resulting index number for the child display object
+ */
+ Container.prototype.setChildIndex = function (child, index) {
+ if (index < 0 || index >= this.children.length) {
+ throw new Error("The index " + index + " supplied is out of bounds " + this.children.length);
+ }
+ var currentIndex = this.getChildIndex(child);
+ removeItems(this.children, currentIndex, 1); // remove from old position
+ this.children.splice(index, 0, child); // add at new position
+ this.onChildrenChange(index);
+ };
+ /**
+ * Returns the child at the specified index
+ * @param index - The index to get the child at
+ * @returns - The child at the given index, if any.
+ */
+ Container.prototype.getChildAt = function (index) {
+ if (index < 0 || index >= this.children.length) {
+ throw new Error("getChildAt: Index (" + index + ") does not exist.");
+ }
+ return this.children[index];
+ };
+ /**
+ * Removes one or more children from the container.
+ * @param {...PIXI.DisplayObject} children - The DisplayObject(s) to remove
+ * @returns {PIXI.DisplayObject} The first child that was removed.
+ */
+ Container.prototype.removeChild = function () {
+ var arguments$1 = arguments;
+
+ var children = [];
+ for (var _i = 0; _i < arguments.length; _i++) {
+ children[_i] = arguments$1[_i];
+ }
+ // if there is only one argument we can bypass looping through the them
+ if (children.length > 1) {
+ // loop through the arguments property and remove all children
+ for (var i = 0; i < children.length; i++) {
+ this.removeChild(children[i]);
+ }
+ }
+ else {
+ var child = children[0];
+ var index = this.children.indexOf(child);
+ if (index === -1)
+ { return null; }
+ child.parent = null;
+ // ensure child transform will be recalculated
+ child.transform._parentID = -1;
+ removeItems(this.children, index, 1);
+ // ensure bounds will be recalculated
+ this._boundsID++;
+ // TODO - lets either do all callbacks or all events.. not both!
+ this.onChildrenChange(index);
+ child.emit('removed', this);
+ this.emit('childRemoved', child, this, index);
+ }
+ return children[0];
+ };
+ /**
+ * Removes a child from the specified index position.
+ * @param index - The index to get the child from
+ * @returns The child that was removed.
+ */
+ Container.prototype.removeChildAt = function (index) {
+ var child = this.getChildAt(index);
+ // ensure child transform will be recalculated..
+ child.parent = null;
+ child.transform._parentID = -1;
+ removeItems(this.children, index, 1);
+ // ensure bounds will be recalculated
+ this._boundsID++;
+ // TODO - lets either do all callbacks or all events.. not both!
+ this.onChildrenChange(index);
+ child.emit('removed', this);
+ this.emit('childRemoved', child, this, index);
+ return child;
+ };
+ /**
+ * Removes all children from this container that are within the begin and end indexes.
+ * @param beginIndex - The beginning position.
+ * @param endIndex - The ending position. Default value is size of the container.
+ * @returns - List of removed children
+ */
+ Container.prototype.removeChildren = function (beginIndex, endIndex) {
+ if (beginIndex === void 0) { beginIndex = 0; }
+ if (endIndex === void 0) { endIndex = this.children.length; }
+ var begin = beginIndex;
+ var end = endIndex;
+ var range = end - begin;
+ var removed;
+ if (range > 0 && range <= end) {
+ removed = this.children.splice(begin, range);
+ for (var i = 0; i < removed.length; ++i) {
+ removed[i].parent = null;
+ if (removed[i].transform) {
+ removed[i].transform._parentID = -1;
+ }
+ }
+ this._boundsID++;
+ this.onChildrenChange(beginIndex);
+ for (var i = 0; i < removed.length; ++i) {
+ removed[i].emit('removed', this);
+ this.emit('childRemoved', removed[i], this, i);
+ }
+ return removed;
+ }
+ else if (range === 0 && this.children.length === 0) {
+ return [];
+ }
+ throw new RangeError('removeChildren: numeric values are outside the acceptable range.');
+ };
+ /** Sorts children by zIndex. Previous order is maintained for 2 children with the same zIndex. */
+ Container.prototype.sortChildren = function () {
+ var sortRequired = false;
+ for (var i = 0, j = this.children.length; i < j; ++i) {
+ var child = this.children[i];
+ child._lastSortedIndex = i;
+ if (!sortRequired && child.zIndex !== 0) {
+ sortRequired = true;
+ }
+ }
+ if (sortRequired && this.children.length > 1) {
+ this.children.sort(sortChildren);
+ }
+ this.sortDirty = false;
+ };
+ /** Updates the transform on all children of this container for rendering. */
+ Container.prototype.updateTransform = function () {
+ if (this.sortableChildren && this.sortDirty) {
+ this.sortChildren();
+ }
+ this._boundsID++;
+ this.transform.updateTransform(this.parent.transform);
+ // TODO: check render flags, how to process stuff here
+ this.worldAlpha = this.alpha * this.parent.worldAlpha;
+ for (var i = 0, j = this.children.length; i < j; ++i) {
+ var child = this.children[i];
+ if (child.visible) {
+ child.updateTransform();
+ }
+ }
+ };
+ /**
+ * Recalculates the bounds of the container.
+ *
+ * This implementation will automatically fit the children's bounds into the calculation. Each child's bounds
+ * is limited to its mask's bounds or filterArea, if any is applied.
+ */
+ Container.prototype.calculateBounds = function () {
+ this._bounds.clear();
+ this._calculateBounds();
+ for (var i = 0; i < this.children.length; i++) {
+ var child = this.children[i];
+ if (!child.visible || !child.renderable) {
+ continue;
+ }
+ child.calculateBounds();
+ // TODO: filter+mask, need to mask both somehow
+ if (child._mask) {
+ var maskObject = (child._mask.isMaskData
+ ? child._mask.maskObject : child._mask);
+ if (maskObject) {
+ maskObject.calculateBounds();
+ this._bounds.addBoundsMask(child._bounds, maskObject._bounds);
+ }
+ else {
+ this._bounds.addBounds(child._bounds);
+ }
+ }
+ else if (child.filterArea) {
+ this._bounds.addBoundsArea(child._bounds, child.filterArea);
+ }
+ else {
+ this._bounds.addBounds(child._bounds);
+ }
+ }
+ this._bounds.updateID = this._boundsID;
+ };
+ /**
+ * Retrieves the local bounds of the displayObject as a rectangle object.
+ *
+ * Calling `getLocalBounds` may invalidate the `_bounds` of the whole subtree below. If using it inside a render()
+ * call, it is advised to call `getBounds()` immediately after to recalculate the world bounds of the subtree.
+ * @param rect - Optional rectangle to store the result of the bounds calculation.
+ * @param skipChildrenUpdate - Setting to `true` will stop re-calculation of children transforms,
+ * it was default behaviour of pixi 4.0-5.2 and caused many problems to users.
+ * @returns - The rectangular bounding area.
+ */
+ Container.prototype.getLocalBounds = function (rect, skipChildrenUpdate) {
+ if (skipChildrenUpdate === void 0) { skipChildrenUpdate = false; }
+ var result = _super.prototype.getLocalBounds.call(this, rect);
+ if (!skipChildrenUpdate) {
+ for (var i = 0, j = this.children.length; i < j; ++i) {
+ var child = this.children[i];
+ if (child.visible) {
+ child.updateTransform();
+ }
+ }
+ }
+ return result;
+ };
+ /**
+ * Recalculates the content bounds of this object. This should be overriden to
+ * calculate the bounds of this specific object (not including children).
+ * @protected
+ */
+ Container.prototype._calculateBounds = function () {
+ // FILL IN//
+ };
+ /**
+ * Renders this object and its children with culling.
+ * @protected
+ * @param {PIXI.Renderer} renderer - The renderer
+ */
+ Container.prototype._renderWithCulling = function (renderer) {
+ var sourceFrame = renderer.renderTexture.sourceFrame;
+ // If the source frame is empty, stop rendering.
+ if (!(sourceFrame.width > 0 && sourceFrame.height > 0)) {
+ return;
+ }
+ // Render the content of the container only if its bounds intersect with the source frame.
+ // All filters are on the stack at this point, and the filter source frame is bound:
+ // therefore, even if the bounds to non intersect the filter frame, the filter
+ // is still applied and any filter padding that is in the frame is rendered correctly.
+ var bounds;
+ var transform;
+ // If cullArea is set, we use this rectangle instead of the bounds of the object. The cullArea
+ // rectangle must completely contain the container and its children including filter padding.
+ if (this.cullArea) {
+ bounds = this.cullArea;
+ transform = this.worldTransform;
+ }
+ // If the container doesn't override _render, we can skip the bounds calculation and intersection test.
+ else if (this._render !== Container.prototype._render) {
+ bounds = this.getBounds(true);
+ }
+ // Render the container if the source frame intersects the bounds.
+ if (bounds && sourceFrame.intersects(bounds, transform)) {
+ this._render(renderer);
+ }
+ // If the bounds are defined by cullArea and do not intersect with the source frame, stop rendering.
+ else if (this.cullArea) {
+ return;
+ }
+ // Unless cullArea is set, we cannot skip the children if the bounds of the container do not intersect
+ // the source frame, because the children might have filters with nonzero padding, which may intersect
+ // with the source frame while the bounds do not: filter padding is not included in the bounds.
+ // If cullArea is not set, render the children with culling temporarily enabled so that they are not rendered
+ // if they are out of frame; otherwise, render the children normally.
+ for (var i = 0, j = this.children.length; i < j; ++i) {
+ var child = this.children[i];
+ var childCullable = child.cullable;
+ child.cullable = childCullable || !this.cullArea;
+ child.render(renderer);
+ child.cullable = childCullable;
+ }
+ };
+ /**
+ * Renders the object using the WebGL renderer.
+ *
+ * The [_render]{@link PIXI.Container#_render} method is be overriden for rendering the contents of the
+ * container itself. This `render` method will invoke it, and also invoke the `render` methods of all
+ * children afterward.
+ *
+ * If `renderable` or `visible` is false or if `worldAlpha` is not positive or if `cullable` is true and
+ * the bounds of this object are out of frame, this implementation will entirely skip rendering.
+ * See {@link PIXI.DisplayObject} for choosing between `renderable` or `visible`. Generally,
+ * setting alpha to zero is not recommended for purely skipping rendering.
+ *
+ * When your scene becomes large (especially when it is larger than can be viewed in a single screen), it is
+ * advised to employ **culling** to automatically skip rendering objects outside of the current screen.
+ * See [cullable]{@link PIXI.DisplayObject#cullable} and [cullArea]{@link PIXI.DisplayObject#cullArea}.
+ * Other culling methods might be better suited for a large number static objects; see
+ * [@pixi-essentials/cull]{@link https://www.npmjs.com/package/@pixi-essentials/cull} and
+ * [pixi-cull]{@link https://www.npmjs.com/package/pixi-cull}.
+ *
+ * The [renderAdvanced]{@link PIXI.Container#renderAdvanced} method is internally used when when masking or
+ * filtering is applied on a container. This does, however, break batching and can affect performance when
+ * masking and filtering is applied extensively throughout the scene graph.
+ * @param renderer - The renderer
+ */
+ Container.prototype.render = function (renderer) {
+ // if the object is not visible or the alpha is 0 then no need to render this element
+ if (!this.visible || this.worldAlpha <= 0 || !this.renderable) {
+ return;
+ }
+ // do a quick check to see if this element has a mask or a filter.
+ if (this._mask || (this.filters && this.filters.length)) {
+ this.renderAdvanced(renderer);
+ }
+ else if (this.cullable) {
+ this._renderWithCulling(renderer);
+ }
+ else {
+ this._render(renderer);
+ for (var i = 0, j = this.children.length; i < j; ++i) {
+ this.children[i].render(renderer);
+ }
+ }
+ };
+ /**
+ * Render the object using the WebGL renderer and advanced features.
+ * @param renderer - The renderer
+ */
+ Container.prototype.renderAdvanced = function (renderer) {
+ var filters = this.filters;
+ var mask = this._mask;
+ // push filter first as we need to ensure the stencil buffer is correct for any masking
+ if (filters) {
+ if (!this._enabledFilters) {
+ this._enabledFilters = [];
+ }
+ this._enabledFilters.length = 0;
+ for (var i = 0; i < filters.length; i++) {
+ if (filters[i].enabled) {
+ this._enabledFilters.push(filters[i]);
+ }
+ }
+ }
+ var flush = (filters && this._enabledFilters && this._enabledFilters.length)
+ || (mask && (!mask.isMaskData
+ || (mask.enabled && (mask.autoDetect || mask.type !== MASK_TYPES$4.NONE))));
+ if (flush) {
+ renderer.batch.flush();
+ }
+ if (filters && this._enabledFilters && this._enabledFilters.length) {
+ renderer.filter.push(this, this._enabledFilters);
+ }
+ if (mask) {
+ renderer.mask.push(this, this._mask);
+ }
+ if (this.cullable) {
+ this._renderWithCulling(renderer);
+ }
+ else {
+ this._render(renderer);
+ for (var i = 0, j = this.children.length; i < j; ++i) {
+ this.children[i].render(renderer);
+ }
+ }
+ if (flush) {
+ renderer.batch.flush();
+ }
+ if (mask) {
+ renderer.mask.pop(this);
+ }
+ if (filters && this._enabledFilters && this._enabledFilters.length) {
+ renderer.filter.pop();
+ }
+ };
+ /**
+ * To be overridden by the subclasses.
+ * @param _renderer - The renderer
+ */
+ Container.prototype._render = function (_renderer) {
+ // this is where content itself gets rendered...
+ };
+ /**
+ * Removes all internal references and listeners as well as removes children from the display list.
+ * Do not use a Container after calling `destroy`.
+ * @param options - Options parameter. A boolean will act as if all options
+ * have been set to that value
+ * @param {boolean} [options.children=false] - if set to true, all the children will have their destroy
+ * method called as well. 'options' will be passed on to those calls.
+ * @param {boolean} [options.texture=false] - Only used for child Sprites if options.children is set to true
+ * Should it destroy the texture of the child sprite
+ * @param {boolean} [options.baseTexture=false] - Only used for child Sprites if options.children is set to true
+ * Should it destroy the base texture of the child sprite
+ */
+ Container.prototype.destroy = function (options) {
+ _super.prototype.destroy.call(this);
+ this.sortDirty = false;
+ var destroyChildren = typeof options === 'boolean' ? options : options && options.children;
+ var oldChildren = this.removeChildren(0, this.children.length);
+ if (destroyChildren) {
+ for (var i = 0; i < oldChildren.length; ++i) {
+ oldChildren[i].destroy(options);
+ }
+ }
+ };
+ Object.defineProperty(Container.prototype, "width", {
+ /** The width of the Container, setting this will actually modify the scale to achieve the value set. */
+ get: function () {
+ return this.scale.x * this.getLocalBounds().width;
+ },
+ set: function (value) {
+ var width = this.getLocalBounds().width;
+ if (width !== 0) {
+ this.scale.x = value / width;
+ }
+ else {
+ this.scale.x = 1;
+ }
+ this._width = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Container.prototype, "height", {
+ /** The height of the Container, setting this will actually modify the scale to achieve the value set. */
+ get: function () {
+ return this.scale.y * this.getLocalBounds().height;
+ },
+ set: function (value) {
+ var height = this.getLocalBounds().height;
+ if (height !== 0) {
+ this.scale.y = value / height;
+ }
+ else {
+ this.scale.y = 1;
+ }
+ this._height = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return Container;
+ }(DisplayObject));
+ /**
+ * Container default updateTransform, does update children of container.
+ * Will crash if there's no parent element.
+ * @memberof PIXI.Container#
+ * @method containerUpdateTransform
+ */
+ Container.prototype.containerUpdateTransform = Container.prototype.updateTransform;
+
+ /*!
+ * @pixi/extensions - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/extensions is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+
+ var __assign$1 = function() {
+ __assign$1 = Object.assign || function __assign(t) {
+ var arguments$1 = arguments;
+
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
+ s = arguments$1[i];
+ for (var p in s) { if (Object.prototype.hasOwnProperty.call(s, p)) { t[p] = s[p]; } }
+ }
+ return t;
+ };
+ return __assign$1.apply(this, arguments);
+ };
+
+ /**
+ * Collection of valid extension types.
+ * @memberof PIXI
+ * @property {string} Application - Application plugins
+ * @property {string} RendererPlugin - Plugins for Renderer
+ * @property {string} CanvasRendererPlugin - Plugins for CanvasRenderer
+ * @property {string} Loader - Plugins to use with Loader
+ * @property {string} LoadParser - Parsers for Assets loader.
+ * @property {string} ResolveParser - Parsers for Assets resolvers.
+ * @property {string} CacheParser - Parsers for Assets cache.
+ */
+ exports.ExtensionType = void 0;
+ (function (ExtensionType) {
+ ExtensionType["Application"] = "application";
+ ExtensionType["RendererPlugin"] = "renderer-webgl-plugin";
+ ExtensionType["CanvasRendererPlugin"] = "renderer-canvas-plugin";
+ ExtensionType["Loader"] = "loader";
+ ExtensionType["LoadParser"] = "load-parser";
+ ExtensionType["ResolveParser"] = "resolve-parser";
+ ExtensionType["CacheParser"] = "cache-parser";
+ ExtensionType["DetectionParser"] = "detection-parser";
+ })(exports.ExtensionType || (exports.ExtensionType = {}));
+ /**
+ * Convert input into extension format data.
+ * @ignore
+ */
+ var normalizeExtension = function (ext) {
+ // Class/Object submission, use extension object
+ if (typeof ext === 'function' || (typeof ext === 'object' && ext.extension)) {
+ if (!ext.extension) {
+ throw new Error('Extension class must have an extension object');
+ }
+ var metadata = (typeof ext.extension !== 'object')
+ ? { type: ext.extension }
+ : ext.extension;
+ ext = __assign$1(__assign$1({}, metadata), { ref: ext });
+ }
+ if (typeof ext === 'object') {
+ ext = __assign$1({}, ext);
+ }
+ else {
+ throw new Error('Invalid extension type');
+ }
+ if (typeof ext.type === 'string') {
+ ext.type = [ext.type];
+ }
+ return ext;
+ };
+ /**
+ * Global registration of all PixiJS extensions. One-stop-shop for extensibility.
+ * @memberof PIXI
+ * @namespace extensions
+ */
+ var extensions = {
+ /** @ignore */
+ _addHandlers: null,
+ /** @ignore */
+ _removeHandlers: null,
+ /** @ignore */
+ _queue: {},
+ /**
+ * Remove extensions from PixiJS.
+ * @param extensions - Extensions to be removed.
+ * @returns {PIXI.extensions} For chaining.
+ */
+ remove: function () {
+ var arguments$1 = arguments;
+
+ var _this = this;
+ var extensions = [];
+ for (var _i = 0; _i < arguments.length; _i++) {
+ extensions[_i] = arguments$1[_i];
+ }
+ extensions.map(normalizeExtension).forEach(function (ext) {
+ ext.type.forEach(function (type) { var _a, _b; return (_b = (_a = _this._removeHandlers)[type]) === null || _b === void 0 ? void 0 : _b.call(_a, ext); });
+ });
+ return this;
+ },
+ /**
+ * Register new extensions with PixiJS.
+ * @param extensions - The spread of extensions to add to PixiJS.
+ * @returns {PIXI.extensions} For chaining.
+ */
+ add: function () {
+ var arguments$1 = arguments;
+
+ var _this = this;
+ var extensions = [];
+ for (var _i = 0; _i < arguments.length; _i++) {
+ extensions[_i] = arguments$1[_i];
+ }
+ // Handle any extensions either passed as class w/ data or as data
+ extensions.map(normalizeExtension).forEach(function (ext) {
+ ext.type.forEach(function (type) {
+ var handlers = _this._addHandlers;
+ var queue = _this._queue;
+ if (!handlers[type]) {
+ queue[type] = queue[type] || [];
+ queue[type].push(ext);
+ }
+ else {
+ handlers[type](ext);
+ }
+ });
+ });
+ return this;
+ },
+ /**
+ * Internal method to handle extensions by name.
+ * @param type - The extension type.
+ * @param onAdd - Function for handling when extensions are added/registered passes {@link PIXI.ExtensionFormat}.
+ * @param onRemove - Function for handling when extensions are removed/unregistered passes {@link PIXI.ExtensionFormat}.
+ * @returns {PIXI.extensions} For chaining.
+ */
+ handle: function (type, onAdd, onRemove) {
+ var addHandlers = this._addHandlers = this._addHandlers || {};
+ var removeHandlers = this._removeHandlers = this._removeHandlers || {};
+ if (addHandlers[type] || removeHandlers[type]) {
+ throw new Error("Extension type " + type + " already has a handler");
+ }
+ addHandlers[type] = onAdd;
+ removeHandlers[type] = onRemove;
+ // Process the queue
+ var queue = this._queue;
+ // Process any plugins that have been registered before the handler
+ if (queue[type]) {
+ queue[type].forEach(function (ext) { return onAdd(ext); });
+ delete queue[type];
+ }
+ return this;
+ },
+ /**
+ * Handle a type, but using a map by `name` property.
+ * @param type - Type of extension to handle.
+ * @param map - The object map of named extensions.
+ * @returns {PIXI.extensions} For chaining.
+ */
+ handleByMap: function (type, map) {
+ return this.handle(type, function (extension) {
+ map[extension.name] = extension.ref;
+ }, function (extension) {
+ delete map[extension.name];
+ });
+ },
+ /**
+ * Handle a type, but using a list of extensions.
+ * @param type - Type of extension to handle.
+ * @param list - The list of extensions.
+ * @returns {PIXI.extensions} For chaining.
+ */
+ handleByList: function (type, list) {
+ return this.handle(type, function (extension) {
+ var _a, _b;
+ list.push(extension.ref);
+ // TODO: remove me later, only added for @pixi/loaders
+ if (type === exports.ExtensionType.Loader) {
+ (_b = (_a = extension.ref).add) === null || _b === void 0 ? void 0 : _b.call(_a);
+ }
+ }, function (extension) {
+ var index = list.indexOf(extension.ref);
+ if (index !== -1) {
+ list.splice(index, 1);
+ }
+ });
+ },
+ };
+
+ /*!
+ * @pixi/runner - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/runner is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+ /**
+ * A Runner is a highly performant and simple alternative to signals. Best used in situations
+ * where events are dispatched to many objects at high frequency (say every frame!)
+ *
+ *
+ * like a signal..
+ * ```
+ * import { Runner } from '@pixi/runner';
+ *
+ * const myObject = {
+ * loaded: new Runner('loaded')
+ * }
+ *
+ * const listener = {
+ * loaded: function(){
+ * // thin
+ * }
+ * }
+ *
+ * myObject.loaded.add(listener);
+ *
+ * myObject.loaded.emit();
+ * ```
+ *
+ * Or for handling calling the same function on many items
+ * ```
+ * import { Runner } from '@pixi/runner';
+ *
+ * const myGame = {
+ * update: new Runner('update')
+ * }
+ *
+ * const gameObject = {
+ * update: function(time){
+ * // update my gamey state
+ * }
+ * }
+ *
+ * myGame.update.add(gameObject);
+ *
+ * myGame.update.emit(time);
+ * ```
+ * @memberof PIXI
+ */
+ var Runner = /** @class */ (function () {
+ /**
+ * @param name - The function name that will be executed on the listeners added to this Runner.
+ */
+ function Runner(name) {
+ this.items = [];
+ this._name = name;
+ this._aliasCount = 0;
+ }
+ /* eslint-disable jsdoc/require-param, jsdoc/check-param-names */
+ /**
+ * Dispatch/Broadcast Runner to all listeners added to the queue.
+ * @param {...any} params - (optional) parameters to pass to each listener
+ */
+ /* eslint-enable jsdoc/require-param, jsdoc/check-param-names */
+ Runner.prototype.emit = function (a0, a1, a2, a3, a4, a5, a6, a7) {
+ if (arguments.length > 8) {
+ throw new Error('max arguments reached');
+ }
+ var _a = this, name = _a.name, items = _a.items;
+ this._aliasCount++;
+ for (var i = 0, len = items.length; i < len; i++) {
+ items[i][name](a0, a1, a2, a3, a4, a5, a6, a7);
+ }
+ if (items === this.items) {
+ this._aliasCount--;
+ }
+ return this;
+ };
+ Runner.prototype.ensureNonAliasedItems = function () {
+ if (this._aliasCount > 0 && this.items.length > 1) {
+ this._aliasCount = 0;
+ this.items = this.items.slice(0);
+ }
+ };
+ /**
+ * Add a listener to the Runner
+ *
+ * Runners do not need to have scope or functions passed to them.
+ * All that is required is to pass the listening object and ensure that it has contains a function that has the same name
+ * as the name provided to the Runner when it was created.
+ *
+ * Eg A listener passed to this Runner will require a 'complete' function.
+ *
+ * ```
+ * import { Runner } from '@pixi/runner';
+ *
+ * const complete = new Runner('complete');
+ * ```
+ *
+ * The scope used will be the object itself.
+ * @param {any} item - The object that will be listening.
+ */
+ Runner.prototype.add = function (item) {
+ if (item[this._name]) {
+ this.ensureNonAliasedItems();
+ this.remove(item);
+ this.items.push(item);
+ }
+ return this;
+ };
+ /**
+ * Remove a single listener from the dispatch queue.
+ * @param {any} item - The listener that you would like to remove.
+ */
+ Runner.prototype.remove = function (item) {
+ var index = this.items.indexOf(item);
+ if (index !== -1) {
+ this.ensureNonAliasedItems();
+ this.items.splice(index, 1);
+ }
+ return this;
+ };
+ /**
+ * Check to see if the listener is already in the Runner
+ * @param {any} item - The listener that you would like to check.
+ */
+ Runner.prototype.contains = function (item) {
+ return this.items.indexOf(item) !== -1;
+ };
+ /** Remove all listeners from the Runner */
+ Runner.prototype.removeAll = function () {
+ this.ensureNonAliasedItems();
+ this.items.length = 0;
+ return this;
+ };
+ /** Remove all references, don't use after this. */
+ Runner.prototype.destroy = function () {
+ this.removeAll();
+ this.items = null;
+ this._name = null;
+ };
+ Object.defineProperty(Runner.prototype, "empty", {
+ /**
+ * `true` if there are no this Runner contains no listeners
+ * @readonly
+ */
+ get: function () {
+ return this.items.length === 0;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Runner.prototype, "name", {
+ /**
+ * The name of the runner.
+ * @readonly
+ */
+ get: function () {
+ return this._name;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return Runner;
+ }());
+ Object.defineProperties(Runner.prototype, {
+ /**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method dispatch
+ * @see PIXI.Runner#emit
+ */
+ dispatch: { value: Runner.prototype.emit },
+ /**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method run
+ * @see PIXI.Runner#emit
+ */
+ run: { value: Runner.prototype.emit },
+ });
+
+ /*!
+ * @pixi/ticker - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/ticker is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /**
+ * Target frames per millisecond.
+ * @static
+ * @name TARGET_FPMS
+ * @memberof PIXI.settings
+ * @type {number}
+ * @default 0.06
+ */
+ settings$1.TARGET_FPMS = 0.06;
+
+ /**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @enum {number}
+ * @property {number} [INTERACTION=50] Highest priority, used for {@link PIXI.InteractionManager}
+ * @property {number} [HIGH=25] High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.AnimatedSprite}
+ * @property {number} [NORMAL=0] Default priority for ticker events, see {@link PIXI.Ticker#add}.
+ * @property {number} [LOW=-25] Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} [UTILITY=-50] Lowest priority used for {@link PIXI.BasePrepare} utility.
+ */
+ exports.UPDATE_PRIORITY = void 0;
+ (function (UPDATE_PRIORITY) {
+ UPDATE_PRIORITY[UPDATE_PRIORITY["INTERACTION"] = 50] = "INTERACTION";
+ UPDATE_PRIORITY[UPDATE_PRIORITY["HIGH"] = 25] = "HIGH";
+ UPDATE_PRIORITY[UPDATE_PRIORITY["NORMAL"] = 0] = "NORMAL";
+ UPDATE_PRIORITY[UPDATE_PRIORITY["LOW"] = -25] = "LOW";
+ UPDATE_PRIORITY[UPDATE_PRIORITY["UTILITY"] = -50] = "UTILITY";
+ })(exports.UPDATE_PRIORITY || (exports.UPDATE_PRIORITY = {}));
+
+ /**
+ * Internal class for handling the priority sorting of ticker handlers.
+ * @private
+ * @class
+ * @memberof PIXI
+ */
+ var TickerListener = /** @class */ (function () {
+ /**
+ * Constructor
+ * @private
+ * @param fn - The listener function to be added for one update
+ * @param context - The listener context
+ * @param priority - The priority for emitting
+ * @param once - If the handler should fire once
+ */
+ function TickerListener(fn, context, priority, once) {
+ if (context === void 0) { context = null; }
+ if (priority === void 0) { priority = 0; }
+ if (once === void 0) { once = false; }
+ /** The next item in chain. */
+ this.next = null;
+ /** The previous item in chain. */
+ this.previous = null;
+ /** `true` if this listener has been destroyed already. */
+ this._destroyed = false;
+ this.fn = fn;
+ this.context = context;
+ this.priority = priority;
+ this.once = once;
+ }
+ /**
+ * Simple compare function to figure out if a function and context match.
+ * @private
+ * @param fn - The listener function to be added for one update
+ * @param context - The listener context
+ * @returns `true` if the listener match the arguments
+ */
+ TickerListener.prototype.match = function (fn, context) {
+ if (context === void 0) { context = null; }
+ return this.fn === fn && this.context === context;
+ };
+ /**
+ * Emit by calling the current function.
+ * @private
+ * @param deltaTime - time since the last emit.
+ * @returns Next ticker
+ */
+ TickerListener.prototype.emit = function (deltaTime) {
+ if (this.fn) {
+ if (this.context) {
+ this.fn.call(this.context, deltaTime);
+ }
+ else {
+ this.fn(deltaTime);
+ }
+ }
+ var redirect = this.next;
+ if (this.once) {
+ this.destroy(true);
+ }
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed) {
+ this.next = null;
+ }
+ return redirect;
+ };
+ /**
+ * Connect to the list.
+ * @private
+ * @param previous - Input node, previous listener
+ */
+ TickerListener.prototype.connect = function (previous) {
+ this.previous = previous;
+ if (previous.next) {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ };
+ /**
+ * Destroy and don't use after this.
+ * @private
+ * @param hard - `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @returns The listener to redirect while emitting or removing.
+ */
+ TickerListener.prototype.destroy = function (hard) {
+ if (hard === void 0) { hard = false; }
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+ // Disconnect, hook up next and previous
+ if (this.previous) {
+ this.previous.next = this.next;
+ }
+ if (this.next) {
+ this.next.previous = this.previous;
+ }
+ // Redirect to the next item
+ var redirect = this.next;
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+ return redirect;
+ };
+ return TickerListener;
+ }());
+
+ /**
+ * A Ticker class that runs an update loop that other objects listen to.
+ *
+ * This class is composed around listeners meant for execution on the next requested animation frame.
+ * Animation frames are requested only when necessary, e.g. When the ticker is started and the emitter has listeners.
+ * @class
+ * @memberof PIXI
+ */
+ var Ticker = /** @class */ (function () {
+ function Ticker() {
+ var _this = this;
+ /**
+ * Whether or not this ticker should invoke the method
+ * {@link PIXI.Ticker#start} automatically
+ * when a listener is added.
+ */
+ this.autoStart = false;
+ /**
+ * Scalar time value from last frame to this frame.
+ * This value is capped by setting {@link PIXI.Ticker#minFPS}
+ * and is scaled with {@link PIXI.Ticker#speed}.
+ * **Note:** The cap may be exceeded by scaling.
+ */
+ this.deltaTime = 1;
+ /**
+ * The last time {@link PIXI.Ticker#update} was invoked.
+ * This value is also reset internally outside of invoking
+ * update, but only when a new animation frame is requested.
+ * If the platform supports DOMHighResTimeStamp,
+ * this value will have a precision of 1 碌s.
+ */
+ this.lastTime = -1;
+ /**
+ * Factor of current {@link PIXI.Ticker#deltaTime}.
+ * @example
+ * // Scales ticker.deltaTime to what would be
+ * // the equivalent of approximately 120 FPS
+ * ticker.speed = 2;
+ */
+ this.speed = 1;
+ /**
+ * Whether or not this ticker has been started.
+ * `true` if {@link PIXI.Ticker#start} has been called.
+ * `false` if {@link PIXI.Ticker#stop} has been called.
+ * While `false`, this value may change to `true` in the
+ * event of {@link PIXI.Ticker#autoStart} being `true`
+ * and a listener is added.
+ */
+ this.started = false;
+ /** Internal current frame request ID */
+ this._requestId = null;
+ /**
+ * Internal value managed by minFPS property setter and getter.
+ * This is the maximum allowed milliseconds between updates.
+ */
+ this._maxElapsedMS = 100;
+ /**
+ * Internal value managed by minFPS property setter and getter.
+ * This is the minimum allowed milliseconds between updates.
+ */
+ this._minElapsedMS = 0;
+ /** If enabled, deleting is disabled.*/
+ this._protected = false;
+ /** The last time keyframe was executed. Maintains a relatively fixed interval with the previous value. */
+ this._lastFrame = -1;
+ this._head = new TickerListener(null, null, Infinity);
+ this.deltaMS = 1 / settings$1.TARGET_FPMS;
+ this.elapsedMS = 1 / settings$1.TARGET_FPMS;
+ this._tick = function (time) {
+ _this._requestId = null;
+ if (_this.started) {
+ // Invoke listeners now
+ _this.update(time);
+ // Listener side effects may have modified ticker state.
+ if (_this.started && _this._requestId === null && _this._head.next) {
+ _this._requestId = requestAnimationFrame(_this._tick);
+ }
+ }
+ };
+ }
+ /**
+ * Conditionally requests a new animation frame.
+ * If a frame has not already been requested, and if the internal
+ * emitter has listeners, a new frame is requested.
+ * @private
+ */
+ Ticker.prototype._requestIfNeeded = function () {
+ if (this._requestId === null && this._head.next) {
+ // ensure callbacks get correct delta
+ this.lastTime = performance.now();
+ this._lastFrame = this.lastTime;
+ this._requestId = requestAnimationFrame(this._tick);
+ }
+ };
+ /**
+ * Conditionally cancels a pending animation frame.
+ * @private
+ */
+ Ticker.prototype._cancelIfNeeded = function () {
+ if (this._requestId !== null) {
+ cancelAnimationFrame(this._requestId);
+ this._requestId = null;
+ }
+ };
+ /**
+ * Conditionally requests a new animation frame.
+ * If the ticker has been started it checks if a frame has not already
+ * been requested, and if the internal emitter has listeners. If these
+ * conditions are met, a new frame is requested. If the ticker has not
+ * been started, but autoStart is `true`, then the ticker starts now,
+ * and continues with the previous conditions to request a new frame.
+ * @private
+ */
+ Ticker.prototype._startIfPossible = function () {
+ if (this.started) {
+ this._requestIfNeeded();
+ }
+ else if (this.autoStart) {
+ this.start();
+ }
+ };
+ /**
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
+ * @param fn - The listener function to be added for updates
+ * @param context - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
+ * @returns This instance of a ticker
+ */
+ Ticker.prototype.add = function (fn, context, priority) {
+ if (priority === void 0) { priority = exports.UPDATE_PRIORITY.NORMAL; }
+ return this._addListener(new TickerListener(fn, context, priority));
+ };
+ /**
+ * Add a handler for the tick event which is only execute once.
+ * @param fn - The listener function to be added for one update
+ * @param context - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
+ * @returns This instance of a ticker
+ */
+ Ticker.prototype.addOnce = function (fn, context, priority) {
+ if (priority === void 0) { priority = exports.UPDATE_PRIORITY.NORMAL; }
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ };
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ * @private
+ * @param listener - Current listener being added.
+ * @returns This instance of a ticker
+ */
+ Ticker.prototype._addListener = function (listener) {
+ // For attaching to head
+ var current = this._head.next;
+ var previous = this._head;
+ // Add the first item
+ if (!current) {
+ listener.connect(previous);
+ }
+ else {
+ // Go from highest to lowest priority
+ while (current) {
+ if (listener.priority > current.priority) {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+ // Not yet connected
+ if (!listener.previous) {
+ listener.connect(previous);
+ }
+ }
+ this._startIfPossible();
+ return this;
+ };
+ /**
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
+ * @param fn - The listener function to be removed
+ * @param context - The listener context to be removed
+ * @returns This instance of a ticker
+ */
+ Ticker.prototype.remove = function (fn, context) {
+ var listener = this._head.next;
+ while (listener) {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context)) {
+ listener = listener.destroy();
+ }
+ else {
+ listener = listener.next;
+ }
+ }
+ if (!this._head.next) {
+ this._cancelIfNeeded();
+ }
+ return this;
+ };
+ Object.defineProperty(Ticker.prototype, "count", {
+ /**
+ * The number of listeners on this ticker, calculated by walking through linked list
+ * @readonly
+ * @member {number}
+ */
+ get: function () {
+ if (!this._head) {
+ return 0;
+ }
+ var count = 0;
+ var current = this._head;
+ while ((current = current.next)) {
+ count++;
+ }
+ return count;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /** Starts the ticker. If the ticker has listeners a new animation frame is requested at this point. */
+ Ticker.prototype.start = function () {
+ if (!this.started) {
+ this.started = true;
+ this._requestIfNeeded();
+ }
+ };
+ /** Stops the ticker. If the ticker has requested an animation frame it is canceled at this point. */
+ Ticker.prototype.stop = function () {
+ if (this.started) {
+ this.started = false;
+ this._cancelIfNeeded();
+ }
+ };
+ /** Destroy the ticker and don't use after this. Calling this method removes all references to internal events. */
+ Ticker.prototype.destroy = function () {
+ if (!this._protected) {
+ this.stop();
+ var listener = this._head.next;
+ while (listener) {
+ listener = listener.destroy(true);
+ }
+ this._head.destroy();
+ this._head = null;
+ }
+ };
+ /**
+ * Triggers an update. An update entails setting the
+ * current {@link PIXI.Ticker#elapsedMS},
+ * the current {@link PIXI.Ticker#deltaTime},
+ * invoking all listeners with current deltaTime,
+ * and then finally setting {@link PIXI.Ticker#lastTime}
+ * with the value of currentTime that was provided.
+ * This method will be called automatically by animation
+ * frame callbacks if the ticker instance has been started
+ * and listeners are added.
+ * @param {number} [currentTime=performance.now()] - the current time of execution
+ */
+ Ticker.prototype.update = function (currentTime) {
+ if (currentTime === void 0) { currentTime = performance.now(); }
+ var elapsedMS;
+ // If the difference in time is zero or negative, we ignore most of the work done here.
+ // If there is no valid difference, then should be no reason to let anyone know about it.
+ // A zero delta, is exactly that, nothing should update.
+ //
+ // The difference in time can be negative, and no this does not mean time traveling.
+ // This can be the result of a race condition between when an animation frame is requested
+ // on the current JavaScript engine event loop, and when the ticker's start method is invoked
+ // (which invokes the internal _requestIfNeeded method). If a frame is requested before
+ // _requestIfNeeded is invoked, then the callback for the animation frame the ticker requests,
+ // can receive a time argument that can be less than the lastTime value that was set within
+ // _requestIfNeeded. This difference is in microseconds, but this is enough to cause problems.
+ //
+ // This check covers this browser engine timing issue, as well as if consumers pass an invalid
+ // currentTime value. This may happen if consumers opt-out of the autoStart, and update themselves.
+ if (currentTime > this.lastTime) {
+ // Save uncapped elapsedMS for measurement
+ elapsedMS = this.elapsedMS = currentTime - this.lastTime;
+ // cap the milliseconds elapsed used for deltaTime
+ if (elapsedMS > this._maxElapsedMS) {
+ elapsedMS = this._maxElapsedMS;
+ }
+ elapsedMS *= this.speed;
+ // If not enough time has passed, exit the function.
+ // Get ready for next frame by setting _lastFrame, but based on _minElapsedMS
+ // adjustment to ensure a relatively stable interval.
+ if (this._minElapsedMS) {
+ var delta = currentTime - this._lastFrame | 0;
+ if (delta < this._minElapsedMS) {
+ return;
+ }
+ this._lastFrame = currentTime - (delta % this._minElapsedMS);
+ }
+ this.deltaMS = elapsedMS;
+ this.deltaTime = this.deltaMS * settings$1.TARGET_FPMS;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ var head = this._head;
+ // Invoke listeners added to internal emitter
+ var listener = head.next;
+ while (listener) {
+ listener = listener.emit(this.deltaTime);
+ }
+ if (!head.next) {
+ this._cancelIfNeeded();
+ }
+ }
+ else {
+ this.deltaTime = this.deltaMS = this.elapsedMS = 0;
+ }
+ this.lastTime = currentTime;
+ };
+ Object.defineProperty(Ticker.prototype, "FPS", {
+ /**
+ * The frames per second at which this ticker is running.
+ * The default is approximately 60 in most modern browsers.
+ * **Note:** This does not factor in the value of
+ * {@link PIXI.Ticker#speed}, which is specific
+ * to scaling {@link PIXI.Ticker#deltaTime}.
+ * @member {number}
+ * @readonly
+ */
+ get: function () {
+ return 1000 / this.elapsedMS;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Ticker.prototype, "minFPS", {
+ /**
+ * Manages the maximum amount of milliseconds allowed to
+ * elapse between invoking {@link PIXI.Ticker#update}.
+ * This value is used to cap {@link PIXI.Ticker#deltaTime},
+ * but does not effect the measured value of {@link PIXI.Ticker#FPS}.
+ * When setting this property it is clamped to a value between
+ * `0` and `PIXI.settings.TARGET_FPMS * 1000`.
+ * @member {number}
+ * @default 10
+ */
+ get: function () {
+ return 1000 / this._maxElapsedMS;
+ },
+ set: function (fps) {
+ // Minimum must be below the maxFPS
+ var minFPS = Math.min(this.maxFPS, fps);
+ // Must be at least 0, but below 1 / settings.TARGET_FPMS
+ var minFPMS = Math.min(Math.max(0, minFPS) / 1000, settings$1.TARGET_FPMS);
+ this._maxElapsedMS = 1 / minFPMS;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Ticker.prototype, "maxFPS", {
+ /**
+ * Manages the minimum amount of milliseconds required to
+ * elapse between invoking {@link PIXI.Ticker#update}.
+ * This will effect the measured value of {@link PIXI.Ticker#FPS}.
+ * If it is set to `0`, then there is no limit; PixiJS will render as many frames as it can.
+ * Otherwise it will be at least `minFPS`
+ * @member {number}
+ * @default 0
+ */
+ get: function () {
+ if (this._minElapsedMS) {
+ return Math.round(1000 / this._minElapsedMS);
+ }
+ return 0;
+ },
+ set: function (fps) {
+ if (fps === 0) {
+ this._minElapsedMS = 0;
+ }
+ else {
+ // Max must be at least the minFPS
+ var maxFPS = Math.max(this.minFPS, fps);
+ this._minElapsedMS = 1 / (maxFPS / 1000);
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Ticker, "shared", {
+ /**
+ * The shared ticker instance used by {@link PIXI.AnimatedSprite} and by
+ * {@link PIXI.VideoResource} to update animation frames / video textures.
+ *
+ * It may also be used by {@link PIXI.Application} if created with the `sharedTicker` option property set to true.
+ *
+ * The property {@link PIXI.Ticker#autoStart} is set to `true` for this instance.
+ * Please follow the examples for usage, including how to opt-out of auto-starting the shared ticker.
+ * @example
+ * let ticker = PIXI.Ticker.shared;
+ * // Set this to prevent starting this ticker when listeners are added.
+ * // By default this is true only for the PIXI.Ticker.shared instance.
+ * ticker.autoStart = false;
+ * // FYI, call this to ensure the ticker is stopped. It should be stopped
+ * // if you have not attempted to render anything yet.
+ * ticker.stop();
+ * // Call this when you are ready for a running shared ticker.
+ * ticker.start();
+ * @example
+ * // You may use the shared ticker to render...
+ * let renderer = PIXI.autoDetectRenderer();
+ * let stage = new PIXI.Container();
+ * document.body.appendChild(renderer.view);
+ * ticker.add(function (time) {
+ * renderer.render(stage);
+ * });
+ * @example
+ * // Or you can just update it manually.
+ * ticker.autoStart = false;
+ * ticker.stop();
+ * function animate(time) {
+ * ticker.update(time);
+ * renderer.render(stage);
+ * requestAnimationFrame(animate);
+ * }
+ * animate(performance.now());
+ * @member {PIXI.Ticker}
+ * @static
+ */
+ get: function () {
+ if (!Ticker._shared) {
+ var shared = Ticker._shared = new Ticker();
+ shared.autoStart = true;
+ shared._protected = true;
+ }
+ return Ticker._shared;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Ticker, "system", {
+ /**
+ * The system ticker instance used by {@link PIXI.InteractionManager} and by
+ * {@link PIXI.BasePrepare} for core timing functionality that shouldn't usually need to be paused,
+ * unlike the `shared` ticker which drives visual animations and rendering which may want to be paused.
+ *
+ * The property {@link PIXI.Ticker#autoStart} is set to `true` for this instance.
+ * @member {PIXI.Ticker}
+ * @static
+ */
+ get: function () {
+ if (!Ticker._system) {
+ var system = Ticker._system = new Ticker();
+ system.autoStart = true;
+ system._protected = true;
+ }
+ return Ticker._system;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return Ticker;
+ }());
+
+ /**
+ * Middleware for for Application Ticker.
+ * @example
+ * import {TickerPlugin} from '@pixi/ticker';
+ * import {Application} from '@pixi/app';
+ * import {extensions} from '@pixi/extensions';
+ * extensions.add(TickerPlugin);
+ * @class
+ * @memberof PIXI
+ */
+ var TickerPlugin = /** @class */ (function () {
+ function TickerPlugin() {
+ }
+ /**
+ * Initialize the plugin with scope of application instance
+ * @static
+ * @private
+ * @param {object} [options] - See application options
+ */
+ TickerPlugin.init = function (options) {
+ var _this = this;
+ // Set default
+ options = Object.assign({
+ autoStart: true,
+ sharedTicker: false,
+ }, options);
+ // Create ticker setter
+ Object.defineProperty(this, 'ticker', {
+ set: function (ticker) {
+ if (this._ticker) {
+ this._ticker.remove(this.render, this);
+ }
+ this._ticker = ticker;
+ if (ticker) {
+ ticker.add(this.render, this, exports.UPDATE_PRIORITY.LOW);
+ }
+ },
+ get: function () {
+ return this._ticker;
+ },
+ });
+ /**
+ * Convenience method for stopping the render.
+ * @method
+ * @memberof PIXI.Application
+ * @instance
+ */
+ this.stop = function () {
+ _this._ticker.stop();
+ };
+ /**
+ * Convenience method for starting the render.
+ * @method
+ * @memberof PIXI.Application
+ * @instance
+ */
+ this.start = function () {
+ _this._ticker.start();
+ };
+ /**
+ * Internal reference to the ticker.
+ * @type {PIXI.Ticker}
+ * @name _ticker
+ * @memberof PIXI.Application#
+ * @private
+ */
+ this._ticker = null;
+ /**
+ * Ticker for doing render updates.
+ * @type {PIXI.Ticker}
+ * @name ticker
+ * @memberof PIXI.Application#
+ * @default PIXI.Ticker.shared
+ */
+ this.ticker = options.sharedTicker ? Ticker.shared : new Ticker();
+ // Start the rendering
+ if (options.autoStart) {
+ this.start();
+ }
+ };
+ /**
+ * Clean up the ticker, scoped to application.
+ * @static
+ * @private
+ */
+ TickerPlugin.destroy = function () {
+ if (this._ticker) {
+ var oldTicker = this._ticker;
+ this.ticker = null;
+ oldTicker.destroy();
+ }
+ };
+ /** @ignore */
+ TickerPlugin.extension = exports.ExtensionType.Application;
+ return TickerPlugin;
+ }());
+
+ /*!
+ * @pixi/core - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/core is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /**
+ * The maximum support for using WebGL. If a device does not
+ * support WebGL version, for instance WebGL 2, it will still
+ * attempt to fallback support to WebGL 1. If you want to
+ * explicitly remove feature support to target a more stable
+ * baseline, prefer a lower environment.
+ *
+ * Due to {@link https://bugs.chromium.org/p/chromium/issues/detail?id=934823|bug in chromium}
+ * we disable webgl2 by default for all non-apple mobile devices.
+ * @static
+ * @name PREFER_ENV
+ * @memberof PIXI.settings
+ * @type {number}
+ * @default PIXI.ENV.WEBGL2
+ */
+ settings$1.PREFER_ENV = isMobile$2.any ? exports.ENV.WEBGL : exports.ENV.WEBGL2;
+ /**
+ * If set to `true`, *only* Textures and BaseTexture objects stored
+ * in the caches ({@link PIXI.utils.TextureCache TextureCache} and
+ * {@link PIXI.utils.BaseTextureCache BaseTextureCache}) can be
+ * used when calling {@link PIXI.Texture.from Texture.from} or
+ * {@link PIXI.BaseTexture.from BaseTexture.from}.
+ * Otherwise, these `from` calls throw an exception. Using this property
+ * can be useful if you want to enforce preloading all assets with
+ * {@link PIXI.Loader Loader}.
+ * @static
+ * @name STRICT_TEXTURE_CACHE
+ * @memberof PIXI.settings
+ * @type {boolean}
+ * @default false
+ */
+ settings$1.STRICT_TEXTURE_CACHE = false;
+
+ /**
+ * Collection of installed resource types, class must extend {@link PIXI.Resource}.
+ * @example
+ * class CustomResource extends PIXI.Resource {
+ * // MUST have source, options constructor signature
+ * // for auto-detected resources to be created.
+ * constructor(source, options) {
+ * super();
+ * }
+ * upload(renderer, baseTexture, glTexture) {
+ * // upload with GL
+ * return true;
+ * }
+ * // used to auto-detect resource
+ * static test(source, extension) {
+ * return extension === 'xyz'|| source instanceof SomeClass;
+ * }
+ * }
+ * // Install the new resource type
+ * PIXI.INSTALLED.push(CustomResource);
+ * @memberof PIXI
+ * @type {Array<PIXI.IResourcePlugin>}
+ * @static
+ * @readonly
+ */
+ var INSTALLED = [];
+ /**
+ * Create a resource element from a single source element. This
+ * auto-detects which type of resource to create. All resources that
+ * are auto-detectable must have a static `test` method and a constructor
+ * with the arguments `(source, options?)`. Currently, the supported
+ * resources for auto-detection include:
+ * - {@link PIXI.ImageResource}
+ * - {@link PIXI.CanvasResource}
+ * - {@link PIXI.VideoResource}
+ * - {@link PIXI.SVGResource}
+ * - {@link PIXI.BufferResource}
+ * @static
+ * @memberof PIXI
+ * @function autoDetectResource
+ * @param {string|*} source - Resource source, this can be the URL to the resource,
+ * a typed-array (for BufferResource), HTMLVideoElement, SVG data-uri
+ * or any other resource that can be auto-detected. If not resource is
+ * detected, it's assumed to be an ImageResource.
+ * @param {object} [options] - Pass-through options to use for Resource
+ * @param {number} [options.width] - Width of BufferResource or SVG rasterization
+ * @param {number} [options.height] - Height of BufferResource or SVG rasterization
+ * @param {boolean} [options.autoLoad=true] - Image, SVG and Video flag to start loading
+ * @param {number} [options.scale=1] - SVG source scale. Overridden by width, height
+ * @param {boolean} [options.createBitmap=PIXI.settings.CREATE_IMAGE_BITMAP] - Image option to create Bitmap object
+ * @param {boolean} [options.crossorigin=true] - Image and Video option to set crossOrigin
+ * @param {boolean} [options.autoPlay=true] - Video option to start playing video immediately
+ * @param {number} [options.updateFPS=0] - Video option to update how many times a second the
+ * texture should be updated from the video. Leave at 0 to update at every render
+ * @returns {PIXI.Resource} The created resource.
+ */
+ function autoDetectResource(source, options) {
+ if (!source) {
+ return null;
+ }
+ var extension = '';
+ if (typeof source === 'string') {
+ // search for file extension: period, 3-4 chars, then ?, # or EOL
+ var result = (/\.(\w{3,4})(?:$|\?|#)/i).exec(source);
+ if (result) {
+ extension = result[1].toLowerCase();
+ }
+ }
+ for (var i = INSTALLED.length - 1; i >= 0; --i) {
+ var ResourcePlugin = INSTALLED[i];
+ if (ResourcePlugin.test && ResourcePlugin.test(source, extension)) {
+ return new ResourcePlugin(source, options);
+ }
+ }
+ throw new Error('Unrecognized source type to auto-detect Resource');
+ }
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+ /* global Reflect, Promise */
+
+ var extendStatics$i = function(d, b) {
+ extendStatics$i = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
+ return extendStatics$i(d, b);
+ };
+
+ function __extends$i(d, b) {
+ extendStatics$i(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ }
+
+ var __assign = function() {
+ __assign = Object.assign || function __assign(t) {
+ var arguments$1 = arguments;
+
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
+ s = arguments$1[i];
+ for (var p in s) { if (Object.prototype.hasOwnProperty.call(s, p)) { t[p] = s[p]; } }
+ }
+ return t;
+ };
+ return __assign.apply(this, arguments);
+ };
+
+ function __rest(s, e) {
+ var t = {};
+ for (var p in s) { if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
+ { t[p] = s[p]; } }
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
+ { for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
+ { t[p[i]] = s[p[i]]; }
+ } }
+ return t;
+ }
+
+ /**
+ * Base resource class for textures that manages validation and uploading, depending on its type.
+ *
+ * Uploading of a base texture to the GPU is required.
+ * @memberof PIXI
+ */
+ var Resource = /** @class */ (function () {
+ /**
+ * @param width - Width of the resource
+ * @param height - Height of the resource
+ */
+ function Resource(width, height) {
+ if (width === void 0) { width = 0; }
+ if (height === void 0) { height = 0; }
+ this._width = width;
+ this._height = height;
+ this.destroyed = false;
+ this.internal = false;
+ this.onResize = new Runner('setRealSize');
+ this.onUpdate = new Runner('update');
+ this.onError = new Runner('onError');
+ }
+ /**
+ * Bind to a parent BaseTexture
+ * @param baseTexture - Parent texture
+ */
+ Resource.prototype.bind = function (baseTexture) {
+ this.onResize.add(baseTexture);
+ this.onUpdate.add(baseTexture);
+ this.onError.add(baseTexture);
+ // Call a resize immediate if we already
+ // have the width and height of the resource
+ if (this._width || this._height) {
+ this.onResize.emit(this._width, this._height);
+ }
+ };
+ /**
+ * Unbind to a parent BaseTexture
+ * @param baseTexture - Parent texture
+ */
+ Resource.prototype.unbind = function (baseTexture) {
+ this.onResize.remove(baseTexture);
+ this.onUpdate.remove(baseTexture);
+ this.onError.remove(baseTexture);
+ };
+ /**
+ * Trigger a resize event
+ * @param width - X dimension
+ * @param height - Y dimension
+ */
+ Resource.prototype.resize = function (width, height) {
+ if (width !== this._width || height !== this._height) {
+ this._width = width;
+ this._height = height;
+ this.onResize.emit(width, height);
+ }
+ };
+ Object.defineProperty(Resource.prototype, "valid", {
+ /**
+ * Has been validated
+ * @readonly
+ */
+ get: function () {
+ return !!this._width && !!this._height;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /** Has been updated trigger event. */
+ Resource.prototype.update = function () {
+ if (!this.destroyed) {
+ this.onUpdate.emit();
+ }
+ };
+ /**
+ * This can be overridden to start preloading a resource
+ * or do any other prepare step.
+ * @protected
+ * @returns Handle the validate event
+ */
+ Resource.prototype.load = function () {
+ return Promise.resolve(this);
+ };
+ Object.defineProperty(Resource.prototype, "width", {
+ /**
+ * The width of the resource.
+ * @readonly
+ */
+ get: function () {
+ return this._width;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Resource.prototype, "height", {
+ /**
+ * The height of the resource.
+ * @readonly
+ */
+ get: function () {
+ return this._height;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Set the style, optional to override
+ * @param _renderer - yeah, renderer!
+ * @param _baseTexture - the texture
+ * @param _glTexture - texture instance for this webgl context
+ * @returns - `true` is success
+ */
+ Resource.prototype.style = function (_renderer, _baseTexture, _glTexture) {
+ return false;
+ };
+ /** Clean up anything, this happens when destroying is ready. */
+ Resource.prototype.dispose = function () {
+ // override
+ };
+ /**
+ * Call when destroying resource, unbind any BaseTexture object
+ * before calling this method, as reference counts are maintained
+ * internally.
+ */
+ Resource.prototype.destroy = function () {
+ if (!this.destroyed) {
+ this.destroyed = true;
+ this.dispose();
+ this.onError.removeAll();
+ this.onError = null;
+ this.onResize.removeAll();
+ this.onResize = null;
+ this.onUpdate.removeAll();
+ this.onUpdate = null;
+ }
+ };
+ /**
+ * Abstract, used to auto-detect resource type.
+ * @param {*} _source - The source object
+ * @param {string} _extension - The extension of source, if set
+ */
+ Resource.test = function (_source, _extension) {
+ return false;
+ };
+ return Resource;
+ }());
+
+ /**
+ * @interface SharedArrayBuffer
+ */
+ /**
+ * Buffer resource with data of typed array.
+ * @memberof PIXI
+ */
+ var BufferResource = /** @class */ (function (_super) {
+ __extends$i(BufferResource, _super);
+ /**
+ * @param source - Source buffer
+ * @param options - Options
+ * @param {number} options.width - Width of the texture
+ * @param {number} options.height - Height of the texture
+ */
+ function BufferResource(source, options) {
+ var _this = this;
+ var _a = options || {}, width = _a.width, height = _a.height;
+ if (!width || !height) {
+ throw new Error('BufferResource width or height invalid');
+ }
+ _this = _super.call(this, width, height) || this;
+ _this.data = source;
+ return _this;
+ }
+ /**
+ * Upload the texture to the GPU.
+ * @param renderer - Upload to the renderer
+ * @param baseTexture - Reference to parent texture
+ * @param glTexture - glTexture
+ * @returns - true is success
+ */
+ BufferResource.prototype.upload = function (renderer, baseTexture, glTexture) {
+ var gl = renderer.gl;
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, baseTexture.alphaMode === exports.ALPHA_MODES.UNPACK);
+ var width = baseTexture.realWidth;
+ var height = baseTexture.realHeight;
+ if (glTexture.width === width && glTexture.height === height) {
+ gl.texSubImage2D(baseTexture.target, 0, 0, 0, width, height, baseTexture.format, glTexture.type, this.data);
+ }
+ else {
+ glTexture.width = width;
+ glTexture.height = height;
+ gl.texImage2D(baseTexture.target, 0, glTexture.internalFormat, width, height, 0, baseTexture.format, glTexture.type, this.data);
+ }
+ return true;
+ };
+ /** Destroy and don't use after this. */
+ BufferResource.prototype.dispose = function () {
+ this.data = null;
+ };
+ /**
+ * Used to auto-detect the type of resource.
+ * @param {*} source - The source object
+ * @returns {boolean} `true` if <canvas>
+ */
+ BufferResource.test = function (source) {
+ return source instanceof Float32Array
+ || source instanceof Uint8Array
+ || source instanceof Uint32Array;
+ };
+ return BufferResource;
+ }(Resource));
+
+ var defaultBufferOptions = {
+ scaleMode: exports.SCALE_MODES.NEAREST,
+ format: exports.FORMATS.RGBA,
+ alphaMode: exports.ALPHA_MODES.NPM,
+ };
+ /**
+ * A Texture stores the information that represents an image.
+ * All textures have a base texture, which contains information about the source.
+ * Therefore you can have many textures all using a single BaseTexture
+ * @memberof PIXI
+ * @typeParam R - The BaseTexture's Resource type.
+ * @typeParam RO - The options for constructing resource.
+ */
+ var BaseTexture = /** @class */ (function (_super) {
+ __extends$i(BaseTexture, _super);
+ /**
+ * @param {PIXI.Resource|string|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} [resource=null] -
+ * The current resource to use, for things that aren't Resource objects, will be converted
+ * into a Resource.
+ * @param options - Collection of options
+ * @param {PIXI.MIPMAP_MODES} [options.mipmap=PIXI.settings.MIPMAP_TEXTURES] - If mipmapping is enabled for texture
+ * @param {number} [options.anisotropicLevel=PIXI.settings.ANISOTROPIC_LEVEL] - Anisotropic filtering level of texture
+ * @param {PIXI.WRAP_MODES} [options.wrapMode=PIXI.settings.WRAP_MODE] - Wrap mode for textures
+ * @param {PIXI.SCALE_MODES} [options.scaleMode=PIXI.settings.SCALE_MODE] - Default scale mode, linear, nearest
+ * @param {PIXI.FORMATS} [options.format=PIXI.FORMATS.RGBA] - GL format type
+ * @param {PIXI.TYPES} [options.type=PIXI.TYPES.UNSIGNED_BYTE] - GL data type
+ * @param {PIXI.TARGETS} [options.target=PIXI.TARGETS.TEXTURE_2D] - GL texture target
+ * @param {PIXI.ALPHA_MODES} [options.alphaMode=PIXI.ALPHA_MODES.UNPACK] - Pre multiply the image alpha
+ * @param {number} [options.width=0] - Width of the texture
+ * @param {number} [options.height=0] - Height of the texture
+ * @param {number} [options.resolution=PIXI.settings.RESOLUTION] - Resolution of the base texture
+ * @param {object} [options.resourceOptions] - Optional resource options,
+ * see {@link PIXI.autoDetectResource autoDetectResource}
+ */
+ function BaseTexture(resource, options) {
+ if (resource === void 0) { resource = null; }
+ if (options === void 0) { options = null; }
+ var _this = _super.call(this) || this;
+ options = options || {};
+ var alphaMode = options.alphaMode, mipmap = options.mipmap, anisotropicLevel = options.anisotropicLevel, scaleMode = options.scaleMode, width = options.width, height = options.height, wrapMode = options.wrapMode, format = options.format, type = options.type, target = options.target, resolution = options.resolution, resourceOptions = options.resourceOptions;
+ // Convert the resource to a Resource object
+ if (resource && !(resource instanceof Resource)) {
+ resource = autoDetectResource(resource, resourceOptions);
+ resource.internal = true;
+ }
+ _this.resolution = resolution || settings$1.RESOLUTION;
+ _this.width = Math.round((width || 0) * _this.resolution) / _this.resolution;
+ _this.height = Math.round((height || 0) * _this.resolution) / _this.resolution;
+ _this._mipmap = mipmap !== undefined ? mipmap : settings$1.MIPMAP_TEXTURES;
+ _this.anisotropicLevel = anisotropicLevel !== undefined ? anisotropicLevel : settings$1.ANISOTROPIC_LEVEL;
+ _this._wrapMode = wrapMode || settings$1.WRAP_MODE;
+ _this._scaleMode = scaleMode !== undefined ? scaleMode : settings$1.SCALE_MODE;
+ _this.format = format || exports.FORMATS.RGBA;
+ _this.type = type || exports.TYPES.UNSIGNED_BYTE;
+ _this.target = target || exports.TARGETS.TEXTURE_2D;
+ _this.alphaMode = alphaMode !== undefined ? alphaMode : exports.ALPHA_MODES.UNPACK;
+ _this.uid = uid();
+ _this.touched = 0;
+ _this.isPowerOfTwo = false;
+ _this._refreshPOT();
+ _this._glTextures = {};
+ _this.dirtyId = 0;
+ _this.dirtyStyleId = 0;
+ _this.cacheId = null;
+ _this.valid = width > 0 && height > 0;
+ _this.textureCacheIds = [];
+ _this.destroyed = false;
+ _this.resource = null;
+ _this._batchEnabled = 0;
+ _this._batchLocation = 0;
+ _this.parentTextureArray = null;
+ /**
+ * Fired when a not-immediately-available source finishes loading.
+ * @protected
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
+ */
+ /**
+ * Fired when a not-immediately-available source fails to load.
+ * @protected
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ * @param {ErrorEvent} event - Load error event.
+ */
+ /**
+ * Fired when BaseTexture is updated.
+ * @protected
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
+ */
+ /**
+ * Fired when BaseTexture is updated.
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+ /**
+ * Fired when BaseTexture is destroyed.
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
+ */
+ // Set the resource
+ _this.setResource(resource);
+ return _this;
+ }
+ Object.defineProperty(BaseTexture.prototype, "realWidth", {
+ /**
+ * Pixel width of the source of this texture
+ * @readonly
+ */
+ get: function () {
+ return Math.round(this.width * this.resolution);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BaseTexture.prototype, "realHeight", {
+ /**
+ * Pixel height of the source of this texture
+ * @readonly
+ */
+ get: function () {
+ return Math.round(this.height * this.resolution);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BaseTexture.prototype, "mipmap", {
+ /**
+ * Mipmap mode of the texture, affects downscaled images
+ * @default PIXI.settings.MIPMAP_TEXTURES
+ */
+ get: function () {
+ return this._mipmap;
+ },
+ set: function (value) {
+ if (this._mipmap !== value) {
+ this._mipmap = value;
+ this.dirtyStyleId++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BaseTexture.prototype, "scaleMode", {
+ /**
+ * The scale mode to apply when scaling this texture
+ * @default PIXI.settings.SCALE_MODE
+ */
+ get: function () {
+ return this._scaleMode;
+ },
+ set: function (value) {
+ if (this._scaleMode !== value) {
+ this._scaleMode = value;
+ this.dirtyStyleId++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BaseTexture.prototype, "wrapMode", {
+ /**
+ * How the texture wraps
+ * @default PIXI.settings.WRAP_MODE
+ */
+ get: function () {
+ return this._wrapMode;
+ },
+ set: function (value) {
+ if (this._wrapMode !== value) {
+ this._wrapMode = value;
+ this.dirtyStyleId++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Changes style options of BaseTexture
+ * @param scaleMode - Pixi scalemode
+ * @param mipmap - enable mipmaps
+ * @returns - this
+ */
+ BaseTexture.prototype.setStyle = function (scaleMode, mipmap) {
+ var dirty;
+ if (scaleMode !== undefined && scaleMode !== this.scaleMode) {
+ this.scaleMode = scaleMode;
+ dirty = true;
+ }
+ if (mipmap !== undefined && mipmap !== this.mipmap) {
+ this.mipmap = mipmap;
+ dirty = true;
+ }
+ if (dirty) {
+ this.dirtyStyleId++;
+ }
+ return this;
+ };
+ /**
+ * Changes w/h/resolution. Texture becomes valid if width and height are greater than zero.
+ * @param desiredWidth - Desired visual width
+ * @param desiredHeight - Desired visual height
+ * @param resolution - Optionally set resolution
+ * @returns - this
+ */
+ BaseTexture.prototype.setSize = function (desiredWidth, desiredHeight, resolution) {
+ resolution = resolution || this.resolution;
+ return this.setRealSize(desiredWidth * resolution, desiredHeight * resolution, resolution);
+ };
+ /**
+ * Sets real size of baseTexture, preserves current resolution.
+ * @param realWidth - Full rendered width
+ * @param realHeight - Full rendered height
+ * @param resolution - Optionally set resolution
+ * @returns - this
+ */
+ BaseTexture.prototype.setRealSize = function (realWidth, realHeight, resolution) {
+ this.resolution = resolution || this.resolution;
+ this.width = Math.round(realWidth) / this.resolution;
+ this.height = Math.round(realHeight) / this.resolution;
+ this._refreshPOT();
+ this.update();
+ return this;
+ };
+ /**
+ * Refresh check for isPowerOfTwo texture based on size
+ * @private
+ */
+ BaseTexture.prototype._refreshPOT = function () {
+ this.isPowerOfTwo = isPow2(this.realWidth) && isPow2(this.realHeight);
+ };
+ /**
+ * Changes resolution
+ * @param resolution - res
+ * @returns - this
+ */
+ BaseTexture.prototype.setResolution = function (resolution) {
+ var oldResolution = this.resolution;
+ if (oldResolution === resolution) {
+ return this;
+ }
+ this.resolution = resolution;
+ if (this.valid) {
+ this.width = Math.round(this.width * oldResolution) / resolution;
+ this.height = Math.round(this.height * oldResolution) / resolution;
+ this.emit('update', this);
+ }
+ this._refreshPOT();
+ return this;
+ };
+ /**
+ * Sets the resource if it wasn't set. Throws error if resource already present
+ * @param resource - that is managing this BaseTexture
+ * @returns - this
+ */
+ BaseTexture.prototype.setResource = function (resource) {
+ if (this.resource === resource) {
+ return this;
+ }
+ if (this.resource) {
+ throw new Error('Resource can be set only once');
+ }
+ resource.bind(this);
+ this.resource = resource;
+ return this;
+ };
+ /** Invalidates the object. Texture becomes valid if width and height are greater than zero. */
+ BaseTexture.prototype.update = function () {
+ if (!this.valid) {
+ if (this.width > 0 && this.height > 0) {
+ this.valid = true;
+ this.emit('loaded', this);
+ this.emit('update', this);
+ }
+ }
+ else {
+ this.dirtyId++;
+ this.dirtyStyleId++;
+ this.emit('update', this);
+ }
+ };
+ /**
+ * Handle errors with resources.
+ * @private
+ * @param event - Error event emitted.
+ */
+ BaseTexture.prototype.onError = function (event) {
+ this.emit('error', this, event);
+ };
+ /**
+ * Destroys this base texture.
+ * The method stops if resource doesn't want this texture to be destroyed.
+ * Removes texture from all caches.
+ */
+ BaseTexture.prototype.destroy = function () {
+ // remove and destroy the resource
+ if (this.resource) {
+ this.resource.unbind(this);
+ // only destroy resourced created internally
+ if (this.resource.internal) {
+ this.resource.destroy();
+ }
+ this.resource = null;
+ }
+ if (this.cacheId) {
+ delete BaseTextureCache[this.cacheId];
+ delete TextureCache[this.cacheId];
+ this.cacheId = null;
+ }
+ // finally let the WebGL renderer know..
+ this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+ this.destroyed = true;
+ };
+ /**
+ * Frees the texture from WebGL memory without destroying this texture object.
+ * This means you can still use the texture later which will upload it to GPU
+ * memory again.
+ * @fires PIXI.BaseTexture#dispose
+ */
+ BaseTexture.prototype.dispose = function () {
+ this.emit('dispose', this);
+ };
+ /** Utility function for BaseTexture|Texture cast. */
+ BaseTexture.prototype.castToBaseTexture = function () {
+ return this;
+ };
+ /**
+ * Helper function that creates a base texture based on the source you provide.
+ * The source can be - image url, image element, canvas element. If the
+ * source is an image url or an image element and not in the base texture
+ * cache, it will be created and loaded.
+ * @static
+ * @param {string|string[]|HTMLImageElement|HTMLCanvasElement|SVGElement|HTMLVideoElement} source - The
+ * source to create base texture from.
+ * @param options - See {@link PIXI.BaseTexture}'s constructor for options.
+ * @param {string} [options.pixiIdPrefix=pixiid] - If a source has no id, this is the prefix of the generated id
+ * @param {boolean} [strict] - Enforce strict-mode, see {@link PIXI.settings.STRICT_TEXTURE_CACHE}.
+ * @returns {PIXI.BaseTexture} The new base texture.
+ */
+ BaseTexture.from = function (source, options, strict) {
+ if (strict === void 0) { strict = settings$1.STRICT_TEXTURE_CACHE; }
+ var isFrame = typeof source === 'string';
+ var cacheId = null;
+ if (isFrame) {
+ cacheId = source;
+ }
+ else {
+ if (!source._pixiId) {
+ var prefix = (options && options.pixiIdPrefix) || 'pixiid';
+ source._pixiId = prefix + "_" + uid();
+ }
+ cacheId = source._pixiId;
+ }
+ var baseTexture = BaseTextureCache[cacheId];
+ // Strict-mode rejects invalid cacheIds
+ if (isFrame && strict && !baseTexture) {
+ throw new Error("The cacheId \"" + cacheId + "\" does not exist in BaseTextureCache.");
+ }
+ if (!baseTexture) {
+ baseTexture = new BaseTexture(source, options);
+ baseTexture.cacheId = cacheId;
+ BaseTexture.addToCache(baseTexture, cacheId);
+ }
+ return baseTexture;
+ };
+ /**
+ * Create a new BaseTexture with a BufferResource from a Float32Array.
+ * RGBA values are floats from 0 to 1.
+ * @param {Float32Array|Uint8Array} buffer - The optional array to use, if no data
+ * is provided, a new Float32Array is created.
+ * @param width - Width of the resource
+ * @param height - Height of the resource
+ * @param options - See {@link PIXI.BaseTexture}'s constructor for options.
+ * Default properties are different from the constructor's defaults.
+ * @param {PIXI.FORMATS} [options.format=PIXI.FORMATS.RGBA] - GL format type
+ * @param {PIXI.ALPHA_MODES} [options.alphaMode=PIXI.ALPHA_MODES.NPM] - Image alpha, not premultiplied by default
+ * @param {PIXI.SCALE_MODES} [options.scaleMode=PIXI.SCALE_MODES.NEAREST] - Scale mode, pixelating by default
+ * @returns - The resulting new BaseTexture
+ */
+ BaseTexture.fromBuffer = function (buffer, width, height, options) {
+ buffer = buffer || new Float32Array(width * height * 4);
+ var resource = new BufferResource(buffer, { width: width, height: height });
+ var type = buffer instanceof Float32Array ? exports.TYPES.FLOAT : exports.TYPES.UNSIGNED_BYTE;
+ return new BaseTexture(resource, Object.assign(defaultBufferOptions, options || { width: width, height: height, type: type }));
+ };
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ BaseTexture.addToCache = function (baseTexture, id) {
+ if (id) {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1) {
+ baseTexture.textureCacheIds.push(id);
+ }
+ if (BaseTextureCache[id]) {
+ // eslint-disable-next-line no-console
+ console.warn("BaseTexture added to the cache with an id [" + id + "] that already had an entry");
+ }
+ BaseTextureCache[id] = baseTexture;
+ }
+ };
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @returns {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ BaseTexture.removeFromCache = function (baseTexture) {
+ if (typeof baseTexture === 'string') {
+ var baseTextureFromCache = BaseTextureCache[baseTexture];
+ if (baseTextureFromCache) {
+ var index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+ if (index > -1) {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+ delete BaseTextureCache[baseTexture];
+ return baseTextureFromCache;
+ }
+ }
+ else if (baseTexture && baseTexture.textureCacheIds) {
+ for (var i = 0; i < baseTexture.textureCacheIds.length; ++i) {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+ baseTexture.textureCacheIds.length = 0;
+ return baseTexture;
+ }
+ return null;
+ };
+ /** Global number of the texture batch, used by multi-texture renderers. */
+ BaseTexture._globalBatch = 0;
+ return BaseTexture;
+ }(eventemitter3));
+
+ /**
+ * Resource that can manage several resource (items) inside.
+ * All resources need to have the same pixel size.
+ * Parent class for CubeResource and ArrayResource
+ * @memberof PIXI
+ */
+ var AbstractMultiResource = /** @class */ (function (_super) {
+ __extends$i(AbstractMultiResource, _super);
+ /**
+ * @param length
+ * @param options - Options to for Resource constructor
+ * @param {number} [options.width] - Width of the resource
+ * @param {number} [options.height] - Height of the resource
+ */
+ function AbstractMultiResource(length, options) {
+ var _this = this;
+ var _a = options || {}, width = _a.width, height = _a.height;
+ _this = _super.call(this, width, height) || this;
+ _this.items = [];
+ _this.itemDirtyIds = [];
+ for (var i = 0; i < length; i++) {
+ var partTexture = new BaseTexture();
+ _this.items.push(partTexture);
+ // -2 - first run of texture array upload
+ // -1 - texture item was allocated
+ // >=0 - texture item uploaded , in sync with items[i].dirtyId
+ _this.itemDirtyIds.push(-2);
+ }
+ _this.length = length;
+ _this._load = null;
+ _this.baseTexture = null;
+ return _this;
+ }
+ /**
+ * Used from ArrayResource and CubeResource constructors.
+ * @param resources - Can be resources, image elements, canvas, etc. ,
+ * length should be same as constructor length
+ * @param options - Detect options for resources
+ */
+ AbstractMultiResource.prototype.initFromArray = function (resources, options) {
+ for (var i = 0; i < this.length; i++) {
+ if (!resources[i]) {
+ continue;
+ }
+ if (resources[i].castToBaseTexture) {
+ this.addBaseTextureAt(resources[i].castToBaseTexture(), i);
+ }
+ else if (resources[i] instanceof Resource) {
+ this.addResourceAt(resources[i], i);
+ }
+ else {
+ this.addResourceAt(autoDetectResource(resources[i], options), i);
+ }
+ }
+ };
+ /** Destroy this BaseImageResource. */
+ AbstractMultiResource.prototype.dispose = function () {
+ for (var i = 0, len = this.length; i < len; i++) {
+ this.items[i].destroy();
+ }
+ this.items = null;
+ this.itemDirtyIds = null;
+ this._load = null;
+ };
+ /**
+ * Set a resource by ID
+ * @param resource
+ * @param index - Zero-based index of resource to set
+ * @returns - Instance for chaining
+ */
+ AbstractMultiResource.prototype.addResourceAt = function (resource, index) {
+ if (!this.items[index]) {
+ throw new Error("Index " + index + " is out of bounds");
+ }
+ // Inherit the first resource dimensions
+ if (resource.valid && !this.valid) {
+ this.resize(resource.width, resource.height);
+ }
+ this.items[index].setResource(resource);
+ return this;
+ };
+ /**
+ * Set the parent base texture.
+ * @param baseTexture
+ */
+ AbstractMultiResource.prototype.bind = function (baseTexture) {
+ if (this.baseTexture !== null) {
+ throw new Error('Only one base texture per TextureArray is allowed');
+ }
+ _super.prototype.bind.call(this, baseTexture);
+ for (var i = 0; i < this.length; i++) {
+ this.items[i].parentTextureArray = baseTexture;
+ this.items[i].on('update', baseTexture.update, baseTexture);
+ }
+ };
+ /**
+ * Unset the parent base texture.
+ * @param baseTexture
+ */
+ AbstractMultiResource.prototype.unbind = function (baseTexture) {
+ _super.prototype.unbind.call(this, baseTexture);
+ for (var i = 0; i < this.length; i++) {
+ this.items[i].parentTextureArray = null;
+ this.items[i].off('update', baseTexture.update, baseTexture);
+ }
+ };
+ /**
+ * Load all the resources simultaneously
+ * @returns - When load is resolved
+ */
+ AbstractMultiResource.prototype.load = function () {
+ var _this = this;
+ if (this._load) {
+ return this._load;
+ }
+ var resources = this.items.map(function (item) { return item.resource; }).filter(function (item) { return item; });
+ // TODO: also implement load part-by-part strategy
+ var promises = resources.map(function (item) { return item.load(); });
+ this._load = Promise.all(promises)
+ .then(function () {
+ var _a = _this.items[0], realWidth = _a.realWidth, realHeight = _a.realHeight;
+ _this.resize(realWidth, realHeight);
+ return Promise.resolve(_this);
+ });
+ return this._load;
+ };
+ return AbstractMultiResource;
+ }(Resource));
+
+ /**
+ * A resource that contains a number of sources.
+ * @memberof PIXI
+ */
+ var ArrayResource = /** @class */ (function (_super) {
+ __extends$i(ArrayResource, _super);
+ /**
+ * @param source - Number of items in array or the collection
+ * of image URLs to use. Can also be resources, image elements, canvas, etc.
+ * @param options - Options to apply to {@link PIXI.autoDetectResource}
+ * @param {number} [options.width] - Width of the resource
+ * @param {number} [options.height] - Height of the resource
+ */
+ function ArrayResource(source, options) {
+ var _this = this;
+ var _a = options || {}, width = _a.width, height = _a.height;
+ var urls;
+ var length;
+ if (Array.isArray(source)) {
+ urls = source;
+ length = source.length;
+ }
+ else {
+ length = source;
+ }
+ _this = _super.call(this, length, { width: width, height: height }) || this;
+ if (urls) {
+ _this.initFromArray(urls, options);
+ }
+ return _this;
+ }
+ /**
+ * Set a baseTexture by ID,
+ * ArrayResource just takes resource from it, nothing more
+ * @param baseTexture
+ * @param index - Zero-based index of resource to set
+ * @returns - Instance for chaining
+ */
+ ArrayResource.prototype.addBaseTextureAt = function (baseTexture, index) {
+ if (baseTexture.resource) {
+ this.addResourceAt(baseTexture.resource, index);
+ }
+ else {
+ throw new Error('ArrayResource does not support RenderTexture');
+ }
+ return this;
+ };
+ /**
+ * Add binding
+ * @param baseTexture
+ */
+ ArrayResource.prototype.bind = function (baseTexture) {
+ _super.prototype.bind.call(this, baseTexture);
+ baseTexture.target = exports.TARGETS.TEXTURE_2D_ARRAY;
+ };
+ /**
+ * Upload the resources to the GPU.
+ * @param renderer
+ * @param texture
+ * @param glTexture
+ * @returns - whether texture was uploaded
+ */
+ ArrayResource.prototype.upload = function (renderer, texture, glTexture) {
+ var _a = this, length = _a.length, itemDirtyIds = _a.itemDirtyIds, items = _a.items;
+ var gl = renderer.gl;
+ if (glTexture.dirtyId < 0) {
+ gl.texImage3D(gl.TEXTURE_2D_ARRAY, 0, glTexture.internalFormat, this._width, this._height, length, 0, texture.format, glTexture.type, null);
+ }
+ for (var i = 0; i < length; i++) {
+ var item = items[i];
+ if (itemDirtyIds[i] < item.dirtyId) {
+ itemDirtyIds[i] = item.dirtyId;
+ if (item.valid) {
+ gl.texSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, // xoffset
+ 0, // yoffset
+ i, // zoffset
+ item.resource.width, item.resource.height, 1, texture.format, glTexture.type, item.resource.source);
+ }
+ }
+ }
+ return true;
+ };
+ return ArrayResource;
+ }(AbstractMultiResource));
+
+ /**
+ * Base for all the image/canvas resources.
+ * @memberof PIXI
+ */
+ var BaseImageResource = /** @class */ (function (_super) {
+ __extends$i(BaseImageResource, _super);
+ /**
+ * @param {HTMLImageElement|HTMLCanvasElement|HTMLVideoElement|SVGElement} source
+ */
+ function BaseImageResource(source) {
+ var _this = this;
+ var sourceAny = source;
+ var width = sourceAny.naturalWidth || sourceAny.videoWidth || sourceAny.width;
+ var height = sourceAny.naturalHeight || sourceAny.videoHeight || sourceAny.height;
+ _this = _super.call(this, width, height) || this;
+ _this.source = source;
+ _this.noSubImage = false;
+ return _this;
+ }
+ /**
+ * Set cross origin based detecting the url and the crossorigin
+ * @param element - Element to apply crossOrigin
+ * @param url - URL to check
+ * @param crossorigin - Cross origin value to use
+ */
+ BaseImageResource.crossOrigin = function (element, url, crossorigin) {
+ if (crossorigin === undefined && url.indexOf('data:') !== 0) {
+ element.crossOrigin = determineCrossOrigin(url);
+ }
+ else if (crossorigin !== false) {
+ element.crossOrigin = typeof crossorigin === 'string' ? crossorigin : 'anonymous';
+ }
+ };
+ /**
+ * Upload the texture to the GPU.
+ * @param renderer - Upload to the renderer
+ * @param baseTexture - Reference to parent texture
+ * @param glTexture
+ * @param {HTMLImageElement|HTMLCanvasElement|HTMLVideoElement|SVGElement} [source] - (optional)
+ * @returns - true is success
+ */
+ BaseImageResource.prototype.upload = function (renderer, baseTexture, glTexture, source) {
+ var gl = renderer.gl;
+ var width = baseTexture.realWidth;
+ var height = baseTexture.realHeight;
+ source = source || this.source;
+ if (source instanceof HTMLImageElement) {
+ if (!source.complete || source.naturalWidth === 0) {
+ return false;
+ }
+ }
+ else if (source instanceof HTMLVideoElement) {
+ if (source.readyState <= 1) {
+ return false;
+ }
+ }
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, baseTexture.alphaMode === exports.ALPHA_MODES.UNPACK);
+ if (!this.noSubImage
+ && baseTexture.target === gl.TEXTURE_2D
+ && glTexture.width === width
+ && glTexture.height === height) {
+ gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, baseTexture.format, glTexture.type, source);
+ }
+ else {
+ glTexture.width = width;
+ glTexture.height = height;
+ gl.texImage2D(baseTexture.target, 0, glTexture.internalFormat, baseTexture.format, glTexture.type, source);
+ }
+ return true;
+ };
+ /**
+ * Checks if source width/height was changed, resize can cause extra baseTexture update.
+ * Triggers one update in any case.
+ */
+ BaseImageResource.prototype.update = function () {
+ if (this.destroyed) {
+ return;
+ }
+ var source = this.source;
+ var width = source.naturalWidth || source.videoWidth || source.width;
+ var height = source.naturalHeight || source.videoHeight || source.height;
+ this.resize(width, height);
+ _super.prototype.update.call(this);
+ };
+ /** Destroy this {@link BaseImageResource} */
+ BaseImageResource.prototype.dispose = function () {
+ this.source = null;
+ };
+ return BaseImageResource;
+ }(Resource));
+
+ /**
+ * @interface OffscreenCanvas
+ */
+ /**
+ * Resource type for HTMLCanvasElement.
+ * @memberof PIXI
+ */
+ var CanvasResource = /** @class */ (function (_super) {
+ __extends$i(CanvasResource, _super);
+ /**
+ * @param source - Canvas element to use
+ */
+ // eslint-disable-next-line @typescript-eslint/no-useless-constructor
+ function CanvasResource(source) {
+ return _super.call(this, source) || this;
+ }
+ /**
+ * Used to auto-detect the type of resource.
+ * @param {*} source - The source object
+ * @returns {boolean} `true` if source is HTMLCanvasElement or OffscreenCanvas
+ */
+ CanvasResource.test = function (source) {
+ var OffscreenCanvas = globalThis.OffscreenCanvas;
+ // Check for browsers that don't yet support OffscreenCanvas
+ if (OffscreenCanvas && source instanceof OffscreenCanvas) {
+ return true;
+ }
+ return globalThis.HTMLCanvasElement && source instanceof HTMLCanvasElement;
+ };
+ return CanvasResource;
+ }(BaseImageResource));
+
+ /**
+ * Resource for a CubeTexture which contains six resources.
+ * @memberof PIXI
+ */
+ var CubeResource = /** @class */ (function (_super) {
+ __extends$i(CubeResource, _super);
+ /**
+ * @param {Array<string|PIXI.Resource>} [source] - Collection of URLs or resources
+ * to use as the sides of the cube.
+ * @param options - ImageResource options
+ * @param {number} [options.width] - Width of resource
+ * @param {number} [options.height] - Height of resource
+ * @param {number} [options.autoLoad=true] - Whether to auto-load resources
+ * @param {number} [options.linkBaseTexture=true] - In case BaseTextures are supplied,
+ * whether to copy them or use
+ */
+ function CubeResource(source, options) {
+ var _this = this;
+ var _a = options || {}, width = _a.width, height = _a.height, autoLoad = _a.autoLoad, linkBaseTexture = _a.linkBaseTexture;
+ if (source && source.length !== CubeResource.SIDES) {
+ throw new Error("Invalid length. Got " + source.length + ", expected 6");
+ }
+ _this = _super.call(this, 6, { width: width, height: height }) || this;
+ for (var i = 0; i < CubeResource.SIDES; i++) {
+ _this.items[i].target = exports.TARGETS.TEXTURE_CUBE_MAP_POSITIVE_X + i;
+ }
+ _this.linkBaseTexture = linkBaseTexture !== false;
+ if (source) {
+ _this.initFromArray(source, options);
+ }
+ if (autoLoad !== false) {
+ _this.load();
+ }
+ return _this;
+ }
+ /**
+ * Add binding.
+ * @param baseTexture - parent base texture
+ */
+ CubeResource.prototype.bind = function (baseTexture) {
+ _super.prototype.bind.call(this, baseTexture);
+ baseTexture.target = exports.TARGETS.TEXTURE_CUBE_MAP;
+ };
+ CubeResource.prototype.addBaseTextureAt = function (baseTexture, index, linkBaseTexture) {
+ if (!this.items[index]) {
+ throw new Error("Index " + index + " is out of bounds");
+ }
+ if (!this.linkBaseTexture
+ || baseTexture.parentTextureArray
+ || Object.keys(baseTexture._glTextures).length > 0) {
+ // copy mode
+ if (baseTexture.resource) {
+ this.addResourceAt(baseTexture.resource, index);
+ }
+ else {
+ throw new Error("CubeResource does not support copying of renderTexture.");
+ }
+ }
+ else {
+ // link mode, the difficult one!
+ baseTexture.target = exports.TARGETS.TEXTURE_CUBE_MAP_POSITIVE_X + index;
+ baseTexture.parentTextureArray = this.baseTexture;
+ this.items[index] = baseTexture;
+ }
+ if (baseTexture.valid && !this.valid) {
+ this.resize(baseTexture.realWidth, baseTexture.realHeight);
+ }
+ this.items[index] = baseTexture;
+ return this;
+ };
+ /**
+ * Upload the resource
+ * @param renderer
+ * @param _baseTexture
+ * @param glTexture
+ * @returns {boolean} true is success
+ */
+ CubeResource.prototype.upload = function (renderer, _baseTexture, glTexture) {
+ var dirty = this.itemDirtyIds;
+ for (var i = 0; i < CubeResource.SIDES; i++) {
+ var side = this.items[i];
+ if (dirty[i] < side.dirtyId || glTexture.dirtyId < _baseTexture.dirtyId) {
+ if (side.valid && side.resource) {
+ side.resource.upload(renderer, side, glTexture);
+ dirty[i] = side.dirtyId;
+ }
+ else if (dirty[i] < -1) {
+ // either item is not valid yet, either its a renderTexture
+ // allocate the memory
+ renderer.gl.texImage2D(side.target, 0, glTexture.internalFormat, _baseTexture.realWidth, _baseTexture.realHeight, 0, _baseTexture.format, glTexture.type, null);
+ dirty[i] = -1;
+ }
+ }
+ }
+ return true;
+ };
+ /**
+ * Used to auto-detect the type of resource.
+ * @param {*} source - The source object
+ * @returns {boolean} `true` if source is an array of 6 elements
+ */
+ CubeResource.test = function (source) {
+ return Array.isArray(source) && source.length === CubeResource.SIDES;
+ };
+ /** Number of texture sides to store for CubeResources. */
+ CubeResource.SIDES = 6;
+ return CubeResource;
+ }(AbstractMultiResource));
+
+ /**
+ * Resource type for HTMLImageElement.
+ * @memberof PIXI
+ */
+ var ImageResource = /** @class */ (function (_super) {
+ __extends$i(ImageResource, _super);
+ /**
+ * @param source - image source or URL
+ * @param options
+ * @param {boolean} [options.autoLoad=true] - start loading process
+ * @param {boolean} [options.createBitmap=PIXI.settings.CREATE_IMAGE_BITMAP] - whether its required to create
+ * a bitmap before upload
+ * @param {boolean} [options.crossorigin=true] - Load image using cross origin
+ * @param {PIXI.ALPHA_MODES} [options.alphaMode=PIXI.ALPHA_MODES.UNPACK] - Premultiply image alpha in bitmap
+ */
+ function ImageResource(source, options) {
+ var _this = this;
+ options = options || {};
+ if (!(source instanceof HTMLImageElement)) {
+ var imageElement = new Image();
+ BaseImageResource.crossOrigin(imageElement, source, options.crossorigin);
+ imageElement.src = source;
+ source = imageElement;
+ }
+ _this = _super.call(this, source) || this;
+ // FireFox 68, and possibly other versions, seems like setting the HTMLImageElement#width and #height
+ // to non-zero values before its loading completes if images are in a cache.
+ // Because of this, need to set the `_width` and the `_height` to zero to avoid uploading incomplete images.
+ // Please refer to the issue #5968 (https://github.com/pixijs/pixi.js/issues/5968).
+ if (!source.complete && !!_this._width && !!_this._height) {
+ _this._width = 0;
+ _this._height = 0;
+ }
+ _this.url = source.src;
+ _this._process = null;
+ _this.preserveBitmap = false;
+ _this.createBitmap = (options.createBitmap !== undefined
+ ? options.createBitmap : settings$1.CREATE_IMAGE_BITMAP) && !!globalThis.createImageBitmap;
+ _this.alphaMode = typeof options.alphaMode === 'number' ? options.alphaMode : null;
+ _this.bitmap = null;
+ _this._load = null;
+ if (options.autoLoad !== false) {
+ _this.load();
+ }
+ return _this;
+ }
+ /**
+ * Returns a promise when image will be loaded and processed.
+ * @param createBitmap - whether process image into bitmap
+ */
+ ImageResource.prototype.load = function (createBitmap) {
+ var _this = this;
+ if (this._load) {
+ return this._load;
+ }
+ if (createBitmap !== undefined) {
+ this.createBitmap = createBitmap;
+ }
+ this._load = new Promise(function (resolve, reject) {
+ var source = _this.source;
+ _this.url = source.src;
+ var completed = function () {
+ if (_this.destroyed) {
+ return;
+ }
+ source.onload = null;
+ source.onerror = null;
+ _this.resize(source.width, source.height);
+ _this._load = null;
+ if (_this.createBitmap) {
+ resolve(_this.process());
+ }
+ else {
+ resolve(_this);
+ }
+ };
+ if (source.complete && source.src) {
+ completed();
+ }
+ else {
+ source.onload = completed;
+ source.onerror = function (event) {
+ // Avoids Promise freezing when resource broken
+ reject(event);
+ _this.onError.emit(event);
+ };
+ }
+ });
+ return this._load;
+ };
+ /**
+ * Called when we need to convert image into BitmapImage.
+ * Can be called multiple times, real promise is cached inside.
+ * @returns - Cached promise to fill that bitmap
+ */
+ ImageResource.prototype.process = function () {
+ var _this = this;
+ var source = this.source;
+ if (this._process !== null) {
+ return this._process;
+ }
+ if (this.bitmap !== null || !globalThis.createImageBitmap) {
+ return Promise.resolve(this);
+ }
+ var createImageBitmap = globalThis.createImageBitmap;
+ var cors = !source.crossOrigin || source.crossOrigin === 'anonymous';
+ this._process = fetch(source.src, {
+ mode: cors ? 'cors' : 'no-cors'
+ })
+ .then(function (r) { return r.blob(); })
+ .then(function (blob) { return createImageBitmap(blob, 0, 0, source.width, source.height, {
+ premultiplyAlpha: _this.alphaMode === exports.ALPHA_MODES.UNPACK ? 'premultiply' : 'none',
+ }); })
+ .then(function (bitmap) {
+ if (_this.destroyed) {
+ return Promise.reject();
+ }
+ _this.bitmap = bitmap;
+ _this.update();
+ _this._process = null;
+ return Promise.resolve(_this);
+ });
+ return this._process;
+ };
+ /**
+ * Upload the image resource to GPU.
+ * @param renderer - Renderer to upload to
+ * @param baseTexture - BaseTexture for this resource
+ * @param glTexture - GLTexture to use
+ * @returns {boolean} true is success
+ */
+ ImageResource.prototype.upload = function (renderer, baseTexture, glTexture) {
+ if (typeof this.alphaMode === 'number') {
+ // bitmap stores unpack premultiply flag, we dont have to notify texImage2D about it
+ baseTexture.alphaMode = this.alphaMode;
+ }
+ if (!this.createBitmap) {
+ return _super.prototype.upload.call(this, renderer, baseTexture, glTexture);
+ }
+ if (!this.bitmap) {
+ // yeah, ignore the output
+ this.process();
+ if (!this.bitmap) {
+ return false;
+ }
+ }
+ _super.prototype.upload.call(this, renderer, baseTexture, glTexture, this.bitmap);
+ if (!this.preserveBitmap) {
+ // checks if there are other renderers that possibly need this bitmap
+ var flag = true;
+ var glTextures = baseTexture._glTextures;
+ for (var key in glTextures) {
+ var otherTex = glTextures[key];
+ if (otherTex !== glTexture && otherTex.dirtyId !== baseTexture.dirtyId) {
+ flag = false;
+ break;
+ }
+ }
+ if (flag) {
+ if (this.bitmap.close) {
+ this.bitmap.close();
+ }
+ this.bitmap = null;
+ }
+ }
+ return true;
+ };
+ /** Destroys this resource. */
+ ImageResource.prototype.dispose = function () {
+ this.source.onload = null;
+ this.source.onerror = null;
+ _super.prototype.dispose.call(this);
+ if (this.bitmap) {
+ this.bitmap.close();
+ this.bitmap = null;
+ }
+ this._process = null;
+ this._load = null;
+ };
+ /**
+ * Used to auto-detect the type of resource.
+ * @param {*} source - The source object
+ * @returns {boolean} `true` if source is string or HTMLImageElement
+ */
+ ImageResource.test = function (source) {
+ return typeof source === 'string' || source instanceof HTMLImageElement;
+ };
+ return ImageResource;
+ }(BaseImageResource));
+
+ /**
+ * Resource type for SVG elements and graphics.
+ * @memberof PIXI
+ */
+ var SVGResource = /** @class */ (function (_super) {
+ __extends$i(SVGResource, _super);
+ /**
+ * @param sourceBase64 - Base64 encoded SVG element or URL for SVG file.
+ * @param {object} [options] - Options to use
+ * @param {number} [options.scale=1] - Scale to apply to SVG. Overridden by...
+ * @param {number} [options.width] - Rasterize SVG this wide. Aspect ratio preserved if height not specified.
+ * @param {number} [options.height] - Rasterize SVG this high. Aspect ratio preserved if width not specified.
+ * @param {boolean} [options.autoLoad=true] - Start loading right away.
+ */
+ function SVGResource(sourceBase64, options) {
+ var _this = this;
+ options = options || {};
+ _this = _super.call(this, settings$1.ADAPTER.createCanvas()) || this;
+ _this._width = 0;
+ _this._height = 0;
+ _this.svg = sourceBase64;
+ _this.scale = options.scale || 1;
+ _this._overrideWidth = options.width;
+ _this._overrideHeight = options.height;
+ _this._resolve = null;
+ _this._crossorigin = options.crossorigin;
+ _this._load = null;
+ if (options.autoLoad !== false) {
+ _this.load();
+ }
+ return _this;
+ }
+ SVGResource.prototype.load = function () {
+ var _this = this;
+ if (this._load) {
+ return this._load;
+ }
+ this._load = new Promise(function (resolve) {
+ // Save this until after load is finished
+ _this._resolve = function () {
+ _this.resize(_this.source.width, _this.source.height);
+ resolve(_this);
+ };
+ // Convert SVG inline string to data-uri
+ if (SVGResource.SVG_XML.test(_this.svg.trim())) {
+ if (!btoa) {
+ throw new Error('Your browser doesn\'t support base64 conversions.');
+ }
+ _this.svg = "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(_this.svg)));
+ }
+ _this._loadSvg();
+ });
+ return this._load;
+ };
+ /** Loads an SVG image from `imageUrl` or `data URL`. */
+ SVGResource.prototype._loadSvg = function () {
+ var _this = this;
+ var tempImage = new Image();
+ BaseImageResource.crossOrigin(tempImage, this.svg, this._crossorigin);
+ tempImage.src = this.svg;
+ tempImage.onerror = function (event) {
+ if (!_this._resolve) {
+ return;
+ }
+ tempImage.onerror = null;
+ _this.onError.emit(event);
+ };
+ tempImage.onload = function () {
+ if (!_this._resolve) {
+ return;
+ }
+ var svgWidth = tempImage.width;
+ var svgHeight = tempImage.height;
+ if (!svgWidth || !svgHeight) {
+ throw new Error('The SVG image must have width and height defined (in pixels), canvas API needs them.');
+ }
+ // Set render size
+ var width = svgWidth * _this.scale;
+ var height = svgHeight * _this.scale;
+ if (_this._overrideWidth || _this._overrideHeight) {
+ width = _this._overrideWidth || _this._overrideHeight / svgHeight * svgWidth;
+ height = _this._overrideHeight || _this._overrideWidth / svgWidth * svgHeight;
+ }
+ width = Math.round(width);
+ height = Math.round(height);
+ // Create a canvas element
+ var canvas = _this.source;
+ canvas.width = width;
+ canvas.height = height;
+ canvas._pixiId = "canvas_" + uid();
+ // Draw the Svg to the canvas
+ canvas
+ .getContext('2d')
+ .drawImage(tempImage, 0, 0, svgWidth, svgHeight, 0, 0, width, height);
+ _this._resolve();
+ _this._resolve = null;
+ };
+ };
+ /**
+ * Get size from an svg string using a regular expression.
+ * @param svgString - a serialized svg element
+ * @returns - image extension
+ */
+ SVGResource.getSize = function (svgString) {
+ var sizeMatch = SVGResource.SVG_SIZE.exec(svgString);
+ var size = {};
+ if (sizeMatch) {
+ size[sizeMatch[1]] = Math.round(parseFloat(sizeMatch[3]));
+ size[sizeMatch[5]] = Math.round(parseFloat(sizeMatch[7]));
+ }
+ return size;
+ };
+ /** Destroys this texture. */
+ SVGResource.prototype.dispose = function () {
+ _super.prototype.dispose.call(this);
+ this._resolve = null;
+ this._crossorigin = null;
+ };
+ /**
+ * Used to auto-detect the type of resource.
+ * @param {*} source - The source object
+ * @param {string} extension - The extension of source, if set
+ * @returns {boolean} - If the source is a SVG source or data file
+ */
+ SVGResource.test = function (source, extension) {
+ // url file extension is SVG
+ return extension === 'svg'
+ // source is SVG data-uri
+ || (typeof source === 'string' && (/^data:image\/svg\+xml(;(charset=utf8|utf8))?;base64/).test(source))
+ // source is SVG inline
+ || (typeof source === 'string' && SVGResource.SVG_XML.test(source));
+ };
+ /**
+ * Regular expression for SVG XML document.
+ * @example <?xml version="1.0" encoding="utf-8" ?><!-- image/svg --><svg
+ * @readonly
+ */
+ SVGResource.SVG_XML = /^(<\?xml[^?]+\?>)?\s*(<!--[^(-->)]*-->)?\s*\<svg/m;
+ /**
+ * Regular expression for SVG size.
+ * @example <svg width="100" height="100"></svg>
+ * @readonly
+ */
+ SVGResource.SVG_SIZE = /<svg[^>]*(?:\s(width|height)=('|")(\d*(?:\.\d+)?)(?:px)?('|"))[^>]*(?:\s(width|height)=('|")(\d*(?:\.\d+)?)(?:px)?('|"))[^>]*>/i; // eslint-disable-line max-len
+ return SVGResource;
+ }(BaseImageResource));
+
+ /**
+ * Resource type for {@code HTMLVideoElement}.
+ * @memberof PIXI
+ */
+ var VideoResource = /** @class */ (function (_super) {
+ __extends$i(VideoResource, _super);
+ /**
+ * @param {HTMLVideoElement|object|string|Array<string|object>} source - Video element to use.
+ * @param {object} [options] - Options to use
+ * @param {boolean} [options.autoLoad=true] - Start loading the video immediately
+ * @param {boolean} [options.autoPlay=true] - Start playing video immediately
+ * @param {number} [options.updateFPS=0] - How many times a second to update the texture from the video.
+ * Leave at 0 to update at every render.
+ * @param {boolean} [options.crossorigin=true] - Load image using cross origin
+ */
+ function VideoResource(source, options) {
+ var _this = this;
+ options = options || {};
+ if (!(source instanceof HTMLVideoElement)) {
+ var videoElement = document.createElement('video');
+ // workaround for https://github.com/pixijs/pixi.js/issues/5996
+ videoElement.setAttribute('preload', 'auto');
+ videoElement.setAttribute('webkit-playsinline', '');
+ videoElement.setAttribute('playsinline', '');
+ if (typeof source === 'string') {
+ source = [source];
+ }
+ var firstSrc = source[0].src || source[0];
+ BaseImageResource.crossOrigin(videoElement, firstSrc, options.crossorigin);
+ // array of objects or strings
+ for (var i = 0; i < source.length; ++i) {
+ var sourceElement = document.createElement('source');
+ var _a = source[i], src = _a.src, mime = _a.mime;
+ src = src || source[i];
+ var baseSrc = src.split('?').shift().toLowerCase();
+ var ext = baseSrc.slice(baseSrc.lastIndexOf('.') + 1);
+ mime = mime || VideoResource.MIME_TYPES[ext] || "video/" + ext;
+ sourceElement.src = src;
+ sourceElement.type = mime;
+ videoElement.appendChild(sourceElement);
+ }
+ // Override the source
+ source = videoElement;
+ }
+ _this = _super.call(this, source) || this;
+ _this.noSubImage = true;
+ _this._autoUpdate = true;
+ _this._isConnectedToTicker = false;
+ _this._updateFPS = options.updateFPS || 0;
+ _this._msToNextUpdate = 0;
+ _this.autoPlay = options.autoPlay !== false;
+ _this._load = null;
+ _this._resolve = null;
+ // Bind for listeners
+ _this._onCanPlay = _this._onCanPlay.bind(_this);
+ _this._onError = _this._onError.bind(_this);
+ if (options.autoLoad !== false) {
+ _this.load();
+ }
+ return _this;
+ }
+ /**
+ * Trigger updating of the texture.
+ * @param _deltaTime - time delta since last tick
+ */
+ VideoResource.prototype.update = function (_deltaTime) {
+ if (!this.destroyed) {
+ // account for if video has had its playbackRate changed
+ var elapsedMS = Ticker.shared.elapsedMS * this.source.playbackRate;
+ this._msToNextUpdate = Math.floor(this._msToNextUpdate - elapsedMS);
+ if (!this._updateFPS || this._msToNextUpdate <= 0) {
+ _super.prototype.update.call(this);
+ this._msToNextUpdate = this._updateFPS ? Math.floor(1000 / this._updateFPS) : 0;
+ }
+ }
+ };
+ /**
+ * Start preloading the video resource.
+ * @returns {Promise<void>} Handle the validate event
+ */
+ VideoResource.prototype.load = function () {
+ var _this = this;
+ if (this._load) {
+ return this._load;
+ }
+ var source = this.source;
+ if ((source.readyState === source.HAVE_ENOUGH_DATA || source.readyState === source.HAVE_FUTURE_DATA)
+ && source.width && source.height) {
+ source.complete = true;
+ }
+ source.addEventListener('play', this._onPlayStart.bind(this));
+ source.addEventListener('pause', this._onPlayStop.bind(this));
+ if (!this._isSourceReady()) {
+ source.addEventListener('canplay', this._onCanPlay);
+ source.addEventListener('canplaythrough', this._onCanPlay);
+ source.addEventListener('error', this._onError, true);
+ }
+ else {
+ this._onCanPlay();
+ }
+ this._load = new Promise(function (resolve) {
+ if (_this.valid) {
+ resolve(_this);
+ }
+ else {
+ _this._resolve = resolve;
+ source.load();
+ }
+ });
+ return this._load;
+ };
+ /**
+ * Handle video error events.
+ * @param event
+ */
+ VideoResource.prototype._onError = function (event) {
+ this.source.removeEventListener('error', this._onError, true);
+ this.onError.emit(event);
+ };
+ /**
+ * Returns true if the underlying source is playing.
+ * @returns - True if playing.
+ */
+ VideoResource.prototype._isSourcePlaying = function () {
+ var source = this.source;
+ return (source.currentTime > 0 && source.paused === false && source.ended === false && source.readyState > 2);
+ };
+ /**
+ * Returns true if the underlying source is ready for playing.
+ * @returns - True if ready.
+ */
+ VideoResource.prototype._isSourceReady = function () {
+ var source = this.source;
+ return source.readyState === 3 || source.readyState === 4;
+ };
+ /** Runs the update loop when the video is ready to play. */
+ VideoResource.prototype._onPlayStart = function () {
+ // Just in case the video has not received its can play even yet..
+ if (!this.valid) {
+ this._onCanPlay();
+ }
+ if (this.autoUpdate && !this._isConnectedToTicker) {
+ Ticker.shared.add(this.update, this);
+ this._isConnectedToTicker = true;
+ }
+ };
+ /** Fired when a pause event is triggered, stops the update loop. */
+ VideoResource.prototype._onPlayStop = function () {
+ if (this._isConnectedToTicker) {
+ Ticker.shared.remove(this.update, this);
+ this._isConnectedToTicker = false;
+ }
+ };
+ /** Fired when the video is loaded and ready to play. */
+ VideoResource.prototype._onCanPlay = function () {
+ var source = this.source;
+ source.removeEventListener('canplay', this._onCanPlay);
+ source.removeEventListener('canplaythrough', this._onCanPlay);
+ var valid = this.valid;
+ this.resize(source.videoWidth, source.videoHeight);
+ // prevent multiple loaded dispatches..
+ if (!valid && this._resolve) {
+ this._resolve(this);
+ this._resolve = null;
+ }
+ if (this._isSourcePlaying()) {
+ this._onPlayStart();
+ }
+ else if (this.autoPlay) {
+ source.play();
+ }
+ };
+ /** Destroys this texture. */
+ VideoResource.prototype.dispose = function () {
+ if (this._isConnectedToTicker) {
+ Ticker.shared.remove(this.update, this);
+ this._isConnectedToTicker = false;
+ }
+ var source = this.source;
+ if (source) {
+ source.removeEventListener('error', this._onError, true);
+ source.pause();
+ source.src = '';
+ source.load();
+ }
+ _super.prototype.dispose.call(this);
+ };
+ Object.defineProperty(VideoResource.prototype, "autoUpdate", {
+ /** Should the base texture automatically update itself, set to true by default. */
+ get: function () {
+ return this._autoUpdate;
+ },
+ set: function (value) {
+ if (value !== this._autoUpdate) {
+ this._autoUpdate = value;
+ if (!this._autoUpdate && this._isConnectedToTicker) {
+ Ticker.shared.remove(this.update, this);
+ this._isConnectedToTicker = false;
+ }
+ else if (this._autoUpdate && !this._isConnectedToTicker && this._isSourcePlaying()) {
+ Ticker.shared.add(this.update, this);
+ this._isConnectedToTicker = true;
+ }
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(VideoResource.prototype, "updateFPS", {
+ /**
+ * How many times a second to update the texture from the video. Leave at 0 to update at every render.
+ * A lower fps can help performance, as updating the texture at 60fps on a 30ps video may not be efficient.
+ */
+ get: function () {
+ return this._updateFPS;
+ },
+ set: function (value) {
+ if (value !== this._updateFPS) {
+ this._updateFPS = value;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Used to auto-detect the type of resource.
+ * @param {*} source - The source object
+ * @param {string} extension - The extension of source, if set
+ * @returns {boolean} `true` if video source
+ */
+ VideoResource.test = function (source, extension) {
+ return (globalThis.HTMLVideoElement && source instanceof HTMLVideoElement)
+ || VideoResource.TYPES.indexOf(extension) > -1;
+ };
+ /**
+ * List of common video file extensions supported by VideoResource.
+ * @readonly
+ */
+ VideoResource.TYPES = ['mp4', 'm4v', 'webm', 'ogg', 'ogv', 'h264', 'avi', 'mov'];
+ /**
+ * Map of video MIME types that can't be directly derived from file extensions.
+ * @readonly
+ */
+ VideoResource.MIME_TYPES = {
+ ogv: 'video/ogg',
+ mov: 'video/quicktime',
+ m4v: 'video/mp4',
+ };
+ return VideoResource;
+ }(BaseImageResource));
+
+ /**
+ * Resource type for ImageBitmap.
+ * @memberof PIXI
+ */
+ var ImageBitmapResource = /** @class */ (function (_super) {
+ __extends$i(ImageBitmapResource, _super);
+ /**
+ * @param source - Image element to use
+ */
+ // eslint-disable-next-line @typescript-eslint/no-useless-constructor
+ function ImageBitmapResource(source) {
+ return _super.call(this, source) || this;
+ }
+ /**
+ * Used to auto-detect the type of resource.
+ * @param {*} source - The source object
+ * @returns {boolean} `true` if source is an ImageBitmap
+ */
+ ImageBitmapResource.test = function (source) {
+ return !!globalThis.createImageBitmap && typeof ImageBitmap !== 'undefined' && source instanceof ImageBitmap;
+ };
+ return ImageBitmapResource;
+ }(BaseImageResource));
+
+ INSTALLED.push(ImageResource, ImageBitmapResource, CanvasResource, VideoResource, SVGResource, BufferResource, CubeResource, ArrayResource);
+
+ var _resources = {
+ __proto__: null,
+ Resource: Resource,
+ BaseImageResource: BaseImageResource,
+ INSTALLED: INSTALLED,
+ autoDetectResource: autoDetectResource,
+ AbstractMultiResource: AbstractMultiResource,
+ ArrayResource: ArrayResource,
+ BufferResource: BufferResource,
+ CanvasResource: CanvasResource,
+ CubeResource: CubeResource,
+ ImageResource: ImageResource,
+ SVGResource: SVGResource,
+ VideoResource: VideoResource,
+ ImageBitmapResource: ImageBitmapResource
+ };
+
+ /**
+ * Resource type for DepthTexture.
+ * @memberof PIXI
+ */
+ var DepthResource = /** @class */ (function (_super) {
+ __extends$i(DepthResource, _super);
+ function DepthResource() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+ /**
+ * Upload the texture to the GPU.
+ * @param renderer - Upload to the renderer
+ * @param baseTexture - Reference to parent texture
+ * @param glTexture - glTexture
+ * @returns - true is success
+ */
+ DepthResource.prototype.upload = function (renderer, baseTexture, glTexture) {
+ var gl = renderer.gl;
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, baseTexture.alphaMode === exports.ALPHA_MODES.UNPACK);
+ var width = baseTexture.realWidth;
+ var height = baseTexture.realHeight;
+ if (glTexture.width === width && glTexture.height === height) {
+ gl.texSubImage2D(baseTexture.target, 0, 0, 0, width, height, baseTexture.format, glTexture.type, this.data);
+ }
+ else {
+ glTexture.width = width;
+ glTexture.height = height;
+ gl.texImage2D(baseTexture.target, 0, glTexture.internalFormat, width, height, 0, baseTexture.format, glTexture.type, this.data);
+ }
+ return true;
+ };
+ return DepthResource;
+ }(BufferResource));
+
+ /**
+ * A framebuffer can be used to render contents off of the screen. {@link PIXI.BaseRenderTexture} uses
+ * one internally to render into itself. You can attach a depth or stencil buffer to a framebuffer.
+ *
+ * On WebGL 2 machines, shaders can output to multiple textures simultaneously with GLSL 300 ES.
+ * @memberof PIXI
+ */
+ var Framebuffer = /** @class */ (function () {
+ /**
+ * @param width - Width of the frame buffer
+ * @param height - Height of the frame buffer
+ */
+ function Framebuffer(width, height) {
+ this.width = Math.round(width || 100);
+ this.height = Math.round(height || 100);
+ this.stencil = false;
+ this.depth = false;
+ this.dirtyId = 0;
+ this.dirtyFormat = 0;
+ this.dirtySize = 0;
+ this.depthTexture = null;
+ this.colorTextures = [];
+ this.glFramebuffers = {};
+ this.disposeRunner = new Runner('disposeFramebuffer');
+ this.multisample = exports.MSAA_QUALITY.NONE;
+ }
+ Object.defineProperty(Framebuffer.prototype, "colorTexture", {
+ /**
+ * Reference to the colorTexture.
+ * @readonly
+ */
+ get: function () {
+ return this.colorTextures[0];
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Add texture to the colorTexture array.
+ * @param index - Index of the array to add the texture to
+ * @param texture - Texture to add to the array
+ */
+ Framebuffer.prototype.addColorTexture = function (index, texture) {
+ if (index === void 0) { index = 0; }
+ // TODO add some validation to the texture - same width / height etc?
+ this.colorTextures[index] = texture || new BaseTexture(null, {
+ scaleMode: exports.SCALE_MODES.NEAREST,
+ resolution: 1,
+ mipmap: exports.MIPMAP_MODES.OFF,
+ width: this.width,
+ height: this.height,
+ });
+ this.dirtyId++;
+ this.dirtyFormat++;
+ return this;
+ };
+ /**
+ * Add a depth texture to the frame buffer.
+ * @param texture - Texture to add.
+ */
+ Framebuffer.prototype.addDepthTexture = function (texture) {
+ /* eslint-disable max-len */
+ this.depthTexture = texture || new BaseTexture(new DepthResource(null, { width: this.width, height: this.height }), {
+ scaleMode: exports.SCALE_MODES.NEAREST,
+ resolution: 1,
+ width: this.width,
+ height: this.height,
+ mipmap: exports.MIPMAP_MODES.OFF,
+ format: exports.FORMATS.DEPTH_COMPONENT,
+ type: exports.TYPES.UNSIGNED_SHORT,
+ });
+ this.dirtyId++;
+ this.dirtyFormat++;
+ return this;
+ };
+ /** Enable depth on the frame buffer. */
+ Framebuffer.prototype.enableDepth = function () {
+ this.depth = true;
+ this.dirtyId++;
+ this.dirtyFormat++;
+ return this;
+ };
+ /** Enable stencil on the frame buffer. */
+ Framebuffer.prototype.enableStencil = function () {
+ this.stencil = true;
+ this.dirtyId++;
+ this.dirtyFormat++;
+ return this;
+ };
+ /**
+ * Resize the frame buffer
+ * @param width - Width of the frame buffer to resize to
+ * @param height - Height of the frame buffer to resize to
+ */
+ Framebuffer.prototype.resize = function (width, height) {
+ width = Math.round(width);
+ height = Math.round(height);
+ if (width === this.width && height === this.height)
+ { return; }
+ this.width = width;
+ this.height = height;
+ this.dirtyId++;
+ this.dirtySize++;
+ for (var i = 0; i < this.colorTextures.length; i++) {
+ var texture = this.colorTextures[i];
+ var resolution = texture.resolution;
+ // take into account the fact the texture may have a different resolution..
+ texture.setSize(width / resolution, height / resolution);
+ }
+ if (this.depthTexture) {
+ var resolution = this.depthTexture.resolution;
+ this.depthTexture.setSize(width / resolution, height / resolution);
+ }
+ };
+ /** Disposes WebGL resources that are connected to this geometry. */
+ Framebuffer.prototype.dispose = function () {
+ this.disposeRunner.emit(this, false);
+ };
+ /** Destroys and removes the depth texture added to this framebuffer. */
+ Framebuffer.prototype.destroyDepthTexture = function () {
+ if (this.depthTexture) {
+ this.depthTexture.destroy();
+ this.depthTexture = null;
+ ++this.dirtyId;
+ ++this.dirtyFormat;
+ }
+ };
+ return Framebuffer;
+ }());
+
+ /**
+ * A BaseRenderTexture is a special texture that allows any PixiJS display object to be rendered to it.
+ *
+ * __Hint__: All DisplayObjects (i.e. Sprites) that render to a BaseRenderTexture should be preloaded
+ * otherwise black rectangles will be drawn instead.
+ *
+ * A BaseRenderTexture takes a snapshot of any Display Object given to its render method. The position
+ * and rotation of the given Display Objects is ignored. For example:
+ *
+ * ```js
+ * let renderer = PIXI.autoDetectRenderer();
+ * let baseRenderTexture = new PIXI.BaseRenderTexture({ width: 800, height: 600 });
+ * let renderTexture = new PIXI.RenderTexture(baseRenderTexture);
+ * let sprite = PIXI.Sprite.from("spinObj_01.png");
+ *
+ * sprite.position.x = 800/2;
+ * sprite.position.y = 600/2;
+ * sprite.anchor.x = 0.5;
+ * sprite.anchor.y = 0.5;
+ *
+ * renderer.render(sprite, {renderTexture});
+ * ```
+ *
+ * The Sprite in this case will be rendered using its local transform. To render this sprite at 0,0
+ * you can clear the transform
+ *
+ * ```js
+ *
+ * sprite.setTransform()
+ *
+ * let baseRenderTexture = new PIXI.BaseRenderTexture({ width: 100, height: 100 });
+ * let renderTexture = new PIXI.RenderTexture(baseRenderTexture);
+ *
+ * renderer.render(sprite, {renderTexture}); // Renders to center of RenderTexture
+ * ```
+ * @memberof PIXI
+ */
+ var BaseRenderTexture = /** @class */ (function (_super) {
+ __extends$i(BaseRenderTexture, _super);
+ /**
+ * @param options
+ * @param {number} [options.width=100] - The width of the base render texture.
+ * @param {number} [options.height=100] - The height of the base render texture.
+ * @param {PIXI.SCALE_MODES} [options.scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES}
+ * for possible values.
+ * @param {number} [options.resolution=PIXI.settings.RESOLUTION] - The resolution / device pixel ratio
+ * of the texture being generated.
+ * @param {PIXI.MSAA_QUALITY} [options.multisample=PIXI.MSAA_QUALITY.NONE] - The number of samples of the frame buffer.
+ */
+ function BaseRenderTexture(options) {
+ if (options === void 0) { options = {}; }
+ var _this = this;
+ if (typeof options === 'number') {
+ /* eslint-disable prefer-rest-params */
+ // Backward compatibility of signature
+ var width = arguments[0];
+ var height = arguments[1];
+ var scaleMode = arguments[2];
+ var resolution = arguments[3];
+ options = { width: width, height: height, scaleMode: scaleMode, resolution: resolution };
+ /* eslint-enable prefer-rest-params */
+ }
+ options.width = options.width || 100;
+ options.height = options.height || 100;
+ options.multisample = options.multisample !== undefined ? options.multisample : exports.MSAA_QUALITY.NONE;
+ _this = _super.call(this, null, options) || this;
+ // Set defaults
+ _this.mipmap = exports.MIPMAP_MODES.OFF;
+ _this.valid = true;
+ _this.clearColor = [0, 0, 0, 0];
+ _this.framebuffer = new Framebuffer(_this.realWidth, _this.realHeight)
+ .addColorTexture(0, _this);
+ _this.framebuffer.multisample = options.multisample;
+ // TODO - could this be added the systems?
+ _this.maskStack = [];
+ _this.filterStack = [{}];
+ return _this;
+ }
+ /**
+ * Resizes the BaseRenderTexture.
+ * @param desiredWidth - The desired width to resize to.
+ * @param desiredHeight - The desired height to resize to.
+ */
+ BaseRenderTexture.prototype.resize = function (desiredWidth, desiredHeight) {
+ this.framebuffer.resize(desiredWidth * this.resolution, desiredHeight * this.resolution);
+ this.setRealSize(this.framebuffer.width, this.framebuffer.height);
+ };
+ /**
+ * Frees the texture and framebuffer from WebGL memory without destroying this texture object.
+ * This means you can still use the texture later which will upload it to GPU
+ * memory again.
+ * @fires PIXI.BaseTexture#dispose
+ */
+ BaseRenderTexture.prototype.dispose = function () {
+ this.framebuffer.dispose();
+ _super.prototype.dispose.call(this);
+ };
+ /** Destroys this texture. */
+ BaseRenderTexture.prototype.destroy = function () {
+ _super.prototype.destroy.call(this);
+ this.framebuffer.destroyDepthTexture();
+ this.framebuffer = null;
+ };
+ return BaseRenderTexture;
+ }(BaseTexture));
+
+ /**
+ * Stores a texture's frame in UV coordinates, in
+ * which everything lies in the rectangle `[(0,0), (1,0),
+ * (1,1), (0,1)]`.
+ *
+ * | Corner | Coordinates |
+ * |--------------|-------------|
+ * | Top-Left | `(x0,y0)` |
+ * | Top-Right | `(x1,y1)` |
+ * | Bottom-Right | `(x2,y2)` |
+ * | Bottom-Left | `(x3,y3)` |
+ * @protected
+ * @memberof PIXI
+ */
+ var TextureUvs = /** @class */ (function () {
+ function TextureUvs() {
+ this.x0 = 0;
+ this.y0 = 0;
+ this.x1 = 1;
+ this.y1 = 0;
+ this.x2 = 1;
+ this.y2 = 1;
+ this.x3 = 0;
+ this.y3 = 1;
+ this.uvsFloat32 = new Float32Array(8);
+ }
+ /**
+ * Sets the texture Uvs based on the given frame information.
+ * @protected
+ * @param frame - The frame of the texture
+ * @param baseFrame - The base frame of the texture
+ * @param rotate - Rotation of frame, see {@link PIXI.groupD8}
+ */
+ TextureUvs.prototype.set = function (frame, baseFrame, rotate) {
+ var tw = baseFrame.width;
+ var th = baseFrame.height;
+ if (rotate) {
+ // width and height div 2 div baseFrame size
+ var w2 = frame.width / 2 / tw;
+ var h2 = frame.height / 2 / th;
+ // coordinates of center
+ var cX = (frame.x / tw) + w2;
+ var cY = (frame.y / th) + h2;
+ rotate = groupD8.add(rotate, groupD8.NW); // NW is top-left corner
+ this.x0 = cX + (w2 * groupD8.uX(rotate));
+ this.y0 = cY + (h2 * groupD8.uY(rotate));
+ rotate = groupD8.add(rotate, 2); // rotate 90 degrees clockwise
+ this.x1 = cX + (w2 * groupD8.uX(rotate));
+ this.y1 = cY + (h2 * groupD8.uY(rotate));
+ rotate = groupD8.add(rotate, 2);
+ this.x2 = cX + (w2 * groupD8.uX(rotate));
+ this.y2 = cY + (h2 * groupD8.uY(rotate));
+ rotate = groupD8.add(rotate, 2);
+ this.x3 = cX + (w2 * groupD8.uX(rotate));
+ this.y3 = cY + (h2 * groupD8.uY(rotate));
+ }
+ else {
+ this.x0 = frame.x / tw;
+ this.y0 = frame.y / th;
+ this.x1 = (frame.x + frame.width) / tw;
+ this.y1 = frame.y / th;
+ this.x2 = (frame.x + frame.width) / tw;
+ this.y2 = (frame.y + frame.height) / th;
+ this.x3 = frame.x / tw;
+ this.y3 = (frame.y + frame.height) / th;
+ }
+ this.uvsFloat32[0] = this.x0;
+ this.uvsFloat32[1] = this.y0;
+ this.uvsFloat32[2] = this.x1;
+ this.uvsFloat32[3] = this.y1;
+ this.uvsFloat32[4] = this.x2;
+ this.uvsFloat32[5] = this.y2;
+ this.uvsFloat32[6] = this.x3;
+ this.uvsFloat32[7] = this.y3;
+ };
+ TextureUvs.prototype.toString = function () {
+ return "[@pixi/core:TextureUvs "
+ + ("x0=" + this.x0 + " y0=" + this.y0 + " ")
+ + ("x1=" + this.x1 + " y1=" + this.y1 + " x2=" + this.x2 + " ")
+ + ("y2=" + this.y2 + " x3=" + this.x3 + " y3=" + this.y3)
+ + "]";
+ };
+ return TextureUvs;
+ }());
+
+ var DEFAULT_UVS = new TextureUvs();
+ /**
+ * Used to remove listeners from WHITE and EMPTY Textures
+ * @ignore
+ */
+ function removeAllHandlers(tex) {
+ tex.destroy = function _emptyDestroy() { };
+ tex.on = function _emptyOn() { };
+ tex.once = function _emptyOnce() { };
+ tex.emit = function _emptyEmit() { };
+ }
+ /**
+ * A texture stores the information that represents an image or part of an image.
+ *
+ * It cannot be added to the display list directly; instead use it as the texture for a Sprite.
+ * If no frame is provided for a texture, then the whole image is used.
+ *
+ * You can directly create a texture from an image and then reuse it multiple times like this :
+ *
+ * ```js
+ * let texture = PIXI.Texture.from('assets/image.png');
+ * let sprite1 = new PIXI.Sprite(texture);
+ * let sprite2 = new PIXI.Sprite(texture);
+ * ```
+ *
+ * If you didnt pass the texture frame to constructor, it enables `noFrame` mode:
+ * it subscribes on baseTexture events, it automatically resizes at the same time as baseTexture.
+ *
+ * Textures made from SVGs, loaded or not, cannot be used before the file finishes processing.
+ * You can check for this by checking the sprite's _textureID property.
+ * ```js
+ * var texture = PIXI.Texture.from('assets/image.svg');
+ * var sprite1 = new PIXI.Sprite(texture);
+ * //sprite1._textureID should not be undefined if the texture has finished processing the SVG file
+ * ```
+ * You can use a ticker or rAF to ensure your sprites load the finished textures after processing. See issue #3068.
+ * @memberof PIXI
+ * @typeParam R - The BaseTexture's Resource type.
+ */
+ var Texture = /** @class */ (function (_super) {
+ __extends$i(Texture, _super);
+ /**
+ * @param baseTexture - The base texture source to create the texture from
+ * @param frame - The rectangle frame of the texture to show
+ * @param orig - The area of original texture
+ * @param trim - Trimmed rectangle of original texture
+ * @param rotate - indicates how the texture was rotated by texture packer. See {@link PIXI.groupD8}
+ * @param anchor - Default anchor point used for sprite placement / rotation
+ */
+ function Texture(baseTexture, frame, orig, trim, rotate, anchor) {
+ var _this = _super.call(this) || this;
+ _this.noFrame = false;
+ if (!frame) {
+ _this.noFrame = true;
+ frame = new Rectangle(0, 0, 1, 1);
+ }
+ if (baseTexture instanceof Texture) {
+ baseTexture = baseTexture.baseTexture;
+ }
+ _this.baseTexture = baseTexture;
+ _this._frame = frame;
+ _this.trim = trim;
+ _this.valid = false;
+ _this._uvs = DEFAULT_UVS;
+ _this.uvMatrix = null;
+ _this.orig = orig || frame; // new Rectangle(0, 0, 1, 1);
+ _this._rotate = Number(rotate || 0);
+ if (rotate === true) {
+ // this is old texturepacker legacy, some games/libraries are passing "true" for rotated textures
+ _this._rotate = 2;
+ }
+ else if (_this._rotate % 2 !== 0) {
+ throw new Error('attempt to use diamond-shaped UVs. If you are sure, set rotation manually');
+ }
+ _this.defaultAnchor = anchor ? new Point(anchor.x, anchor.y) : new Point(0, 0);
+ _this._updateID = 0;
+ _this.textureCacheIds = [];
+ if (!baseTexture.valid) {
+ baseTexture.once('loaded', _this.onBaseTextureUpdated, _this);
+ }
+ else if (_this.noFrame) {
+ // if there is no frame we should monitor for any base texture changes..
+ if (baseTexture.valid) {
+ _this.onBaseTextureUpdated(baseTexture);
+ }
+ }
+ else {
+ _this.frame = frame;
+ }
+ if (_this.noFrame) {
+ baseTexture.on('update', _this.onBaseTextureUpdated, _this);
+ }
+ return _this;
+ }
+ /**
+ * Updates this texture on the gpu.
+ *
+ * Calls the TextureResource update.
+ *
+ * If you adjusted `frame` manually, please call `updateUvs()` instead.
+ */
+ Texture.prototype.update = function () {
+ if (this.baseTexture.resource) {
+ this.baseTexture.resource.update();
+ }
+ };
+ /**
+ * Called when the base texture is updated
+ * @protected
+ * @param baseTexture - The base texture.
+ */
+ Texture.prototype.onBaseTextureUpdated = function (baseTexture) {
+ if (this.noFrame) {
+ if (!this.baseTexture.valid) {
+ return;
+ }
+ this._frame.width = baseTexture.width;
+ this._frame.height = baseTexture.height;
+ this.valid = true;
+ this.updateUvs();
+ }
+ else {
+ // TODO this code looks confusing.. boo to abusing getters and setters!
+ // if user gave us frame that has bigger size than resized texture it can be a problem
+ this.frame = this._frame;
+ }
+ this.emit('update', this);
+ };
+ /**
+ * Destroys this texture
+ * @param [destroyBase=false] - Whether to destroy the base texture as well
+ */
+ Texture.prototype.destroy = function (destroyBase) {
+ if (this.baseTexture) {
+ if (destroyBase) {
+ var resource = this.baseTexture.resource;
+ // delete the texture if it exists in the texture cache..
+ // this only needs to be removed if the base texture is actually destroyed too..
+ if (resource && resource.url && TextureCache[resource.url]) {
+ Texture.removeFromCache(resource.url);
+ }
+ this.baseTexture.destroy();
+ }
+ this.baseTexture.off('loaded', this.onBaseTextureUpdated, this);
+ this.baseTexture.off('update', this.onBaseTextureUpdated, this);
+ this.baseTexture = null;
+ }
+ this._frame = null;
+ this._uvs = null;
+ this.trim = null;
+ this.orig = null;
+ this.valid = false;
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
+ };
+ /**
+ * Creates a new texture object that acts the same as this one.
+ * @returns - The new texture
+ */
+ Texture.prototype.clone = function () {
+ var clonedFrame = this._frame.clone();
+ var clonedOrig = this._frame === this.orig ? clonedFrame : this.orig.clone();
+ var clonedTexture = new Texture(this.baseTexture, !this.noFrame && clonedFrame, clonedOrig, this.trim && this.trim.clone(), this.rotate, this.defaultAnchor);
+ if (this.noFrame) {
+ clonedTexture._frame = clonedFrame;
+ }
+ return clonedTexture;
+ };
+ /**
+ * Updates the internal WebGL UV cache. Use it after you change `frame` or `trim` of the texture.
+ * Call it after changing the frame
+ */
+ Texture.prototype.updateUvs = function () {
+ if (this._uvs === DEFAULT_UVS) {
+ this._uvs = new TextureUvs();
+ }
+ this._uvs.set(this._frame, this.baseTexture, this.rotate);
+ this._updateID++;
+ };
+ /**
+ * Helper function that creates a new Texture based on the source you provide.
+ * The source can be - frame id, image url, video url, canvas element, video element, base texture
+ * @param {string|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement|PIXI.BaseTexture} source -
+ * Source or array of sources to create texture from
+ * @param options - See {@link PIXI.BaseTexture}'s constructor for options.
+ * @param {string} [options.pixiIdPrefix=pixiid] - If a source has no id, this is the prefix of the generated id
+ * @param {boolean} [strict] - Enforce strict-mode, see {@link PIXI.settings.STRICT_TEXTURE_CACHE}.
+ * @returns {PIXI.Texture} The newly created texture
+ */
+ Texture.from = function (source, options, strict) {
+ if (options === void 0) { options = {}; }
+ if (strict === void 0) { strict = settings$1.STRICT_TEXTURE_CACHE; }
+ var isFrame = typeof source === 'string';
+ var cacheId = null;
+ if (isFrame) {
+ cacheId = source;
+ }
+ else if (source instanceof BaseTexture) {
+ if (!source.cacheId) {
+ var prefix = (options && options.pixiIdPrefix) || 'pixiid';
+ source.cacheId = prefix + "-" + uid();
+ BaseTexture.addToCache(source, source.cacheId);
+ }
+ cacheId = source.cacheId;
+ }
+ else {
+ if (!source._pixiId) {
+ var prefix = (options && options.pixiIdPrefix) || 'pixiid';
+ source._pixiId = prefix + "_" + uid();
+ }
+ cacheId = source._pixiId;
+ }
+ var texture = TextureCache[cacheId];
+ // Strict-mode rejects invalid cacheIds
+ if (isFrame && strict && !texture) {
+ throw new Error("The cacheId \"" + cacheId + "\" does not exist in TextureCache.");
+ }
+ if (!texture && !(source instanceof BaseTexture)) {
+ if (!options.resolution) {
+ options.resolution = getResolutionOfUrl(source);
+ }
+ texture = new Texture(new BaseTexture(source, options));
+ texture.baseTexture.cacheId = cacheId;
+ BaseTexture.addToCache(texture.baseTexture, cacheId);
+ Texture.addToCache(texture, cacheId);
+ }
+ else if (!texture && (source instanceof BaseTexture)) {
+ texture = new Texture(source);
+ Texture.addToCache(texture, cacheId);
+ }
+ // lets assume its a base texture!
+ return texture;
+ };
+ /**
+ * Useful for loading textures via URLs. Use instead of `Texture.from` because
+ * it does a better job of handling failed URLs more effectively. This also ignores
+ * `PIXI.settings.STRICT_TEXTURE_CACHE`. Works for Videos, SVGs, Images.
+ * @param url - The remote URL or array of URLs to load.
+ * @param options - Optional options to include
+ * @returns - A Promise that resolves to a Texture.
+ */
+ Texture.fromURL = function (url, options) {
+ var resourceOptions = Object.assign({ autoLoad: false }, options === null || options === void 0 ? void 0 : options.resourceOptions);
+ var texture = Texture.from(url, Object.assign({ resourceOptions: resourceOptions }, options), false);
+ var resource = texture.baseTexture.resource;
+ // The texture was already loaded
+ if (texture.baseTexture.valid) {
+ return Promise.resolve(texture);
+ }
+ // Manually load the texture, this should allow users to handle load errors
+ return resource.load().then(function () { return Promise.resolve(texture); });
+ };
+ /**
+ * Create a new Texture with a BufferResource from a Float32Array.
+ * RGBA values are floats from 0 to 1.
+ * @param {Float32Array|Uint8Array} buffer - The optional array to use, if no data
+ * is provided, a new Float32Array is created.
+ * @param width - Width of the resource
+ * @param height - Height of the resource
+ * @param options - See {@link PIXI.BaseTexture}'s constructor for options.
+ * @returns - The resulting new BaseTexture
+ */
+ Texture.fromBuffer = function (buffer, width, height, options) {
+ return new Texture(BaseTexture.fromBuffer(buffer, width, height, options));
+ };
+ /**
+ * Create a texture from a source and add to the cache.
+ * @param {HTMLImageElement|HTMLCanvasElement|string} source - The input source.
+ * @param imageUrl - File name of texture, for cache and resolving resolution.
+ * @param name - Human readable name for the texture cache. If no name is
+ * specified, only `imageUrl` will be used as the cache ID.
+ * @param options
+ * @returns - Output texture
+ */
+ Texture.fromLoader = function (source, imageUrl, name, options) {
+ var baseTexture = new BaseTexture(source, Object.assign({
+ scaleMode: settings$1.SCALE_MODE,
+ resolution: getResolutionOfUrl(imageUrl),
+ }, options));
+ var resource = baseTexture.resource;
+ if (resource instanceof ImageResource) {
+ resource.url = imageUrl;
+ }
+ var texture = new Texture(baseTexture);
+ // No name, use imageUrl instead
+ if (!name) {
+ name = imageUrl;
+ }
+ // lets also add the frame to pixi's global cache for 'fromLoader' function
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
+ // also add references by url if they are different.
+ if (name !== imageUrl) {
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
+ }
+ // Generally images are valid right away
+ if (texture.baseTexture.valid) {
+ return Promise.resolve(texture);
+ }
+ // SVG assets need to be parsed async, let's wait
+ return new Promise(function (resolve) {
+ texture.baseTexture.once('loaded', function () { return resolve(texture); });
+ });
+ };
+ /**
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * @param texture - The Texture to add to the cache.
+ * @param id - The id that the Texture will be stored against.
+ */
+ Texture.addToCache = function (texture, id) {
+ if (id) {
+ if (texture.textureCacheIds.indexOf(id) === -1) {
+ texture.textureCacheIds.push(id);
+ }
+ if (TextureCache[id]) {
+ // eslint-disable-next-line no-console
+ console.warn("Texture added to the cache with an id [" + id + "] that already had an entry");
+ }
+ TextureCache[id] = texture;
+ }
+ };
+ /**
+ * Remove a Texture from the global TextureCache.
+ * @param texture - id of a Texture to be removed, or a Texture instance itself
+ * @returns - The Texture that was removed
+ */
+ Texture.removeFromCache = function (texture) {
+ if (typeof texture === 'string') {
+ var textureFromCache = TextureCache[texture];
+ if (textureFromCache) {
+ var index = textureFromCache.textureCacheIds.indexOf(texture);
+ if (index > -1) {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+ delete TextureCache[texture];
+ return textureFromCache;
+ }
+ }
+ else if (texture && texture.textureCacheIds) {
+ for (var i = 0; i < texture.textureCacheIds.length; ++i) {
+ // Check that texture matches the one being passed in before deleting it from the cache.
+ if (TextureCache[texture.textureCacheIds[i]] === texture) {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+ }
+ texture.textureCacheIds.length = 0;
+ return texture;
+ }
+ return null;
+ };
+ Object.defineProperty(Texture.prototype, "resolution", {
+ /**
+ * Returns resolution of baseTexture
+ * @readonly
+ */
+ get: function () {
+ return this.baseTexture.resolution;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Texture.prototype, "frame", {
+ /**
+ * The frame specifies the region of the base texture that this texture uses.
+ * Please call `updateUvs()` after you change coordinates of `frame` manually.
+ */
+ get: function () {
+ return this._frame;
+ },
+ set: function (frame) {
+ this._frame = frame;
+ this.noFrame = false;
+ var x = frame.x, y = frame.y, width = frame.width, height = frame.height;
+ var xNotFit = x + width > this.baseTexture.width;
+ var yNotFit = y + height > this.baseTexture.height;
+ if (xNotFit || yNotFit) {
+ var relationship = xNotFit && yNotFit ? 'and' : 'or';
+ var errorX = "X: " + x + " + " + width + " = " + (x + width) + " > " + this.baseTexture.width;
+ var errorY = "Y: " + y + " + " + height + " = " + (y + height) + " > " + this.baseTexture.height;
+ throw new Error('Texture Error: frame does not fit inside the base Texture dimensions: '
+ + (errorX + " " + relationship + " " + errorY));
+ }
+ this.valid = width && height && this.baseTexture.valid;
+ if (!this.trim && !this.rotate) {
+ this.orig = frame;
+ }
+ if (this.valid) {
+ this.updateUvs();
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Texture.prototype, "rotate", {
+ /**
+ * Indicates whether the texture is rotated inside the atlas
+ * set to 2 to compensate for texture packer rotation
+ * set to 6 to compensate for spine packer rotation
+ * can be used to rotate or mirror sprites
+ * See {@link PIXI.groupD8} for explanation
+ */
+ get: function () {
+ return this._rotate;
+ },
+ set: function (rotate) {
+ this._rotate = rotate;
+ if (this.valid) {
+ this.updateUvs();
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Texture.prototype, "width", {
+ /** The width of the Texture in pixels. */
+ get: function () {
+ return this.orig.width;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Texture.prototype, "height", {
+ /** The height of the Texture in pixels. */
+ get: function () {
+ return this.orig.height;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /** Utility function for BaseTexture|Texture cast. */
+ Texture.prototype.castToBaseTexture = function () {
+ return this.baseTexture;
+ };
+ Object.defineProperty(Texture, "EMPTY", {
+ /** An empty texture, used often to not have to create multiple empty textures. Can not be destroyed. */
+ get: function () {
+ if (!Texture._EMPTY) {
+ Texture._EMPTY = new Texture(new BaseTexture());
+ removeAllHandlers(Texture._EMPTY);
+ removeAllHandlers(Texture._EMPTY.baseTexture);
+ }
+ return Texture._EMPTY;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Texture, "WHITE", {
+ /** A white texture of 16x16 size, used for graphics and other things Can not be destroyed. */
+ get: function () {
+ if (!Texture._WHITE) {
+ var canvas = settings$1.ADAPTER.createCanvas(16, 16);
+ var context = canvas.getContext('2d');
+ canvas.width = 16;
+ canvas.height = 16;
+ context.fillStyle = 'white';
+ context.fillRect(0, 0, 16, 16);
+ Texture._WHITE = new Texture(BaseTexture.from(canvas));
+ removeAllHandlers(Texture._WHITE);
+ removeAllHandlers(Texture._WHITE.baseTexture);
+ }
+ return Texture._WHITE;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return Texture;
+ }(eventemitter3));
+
+ /**
+ * A RenderTexture is a special texture that allows any PixiJS display object to be rendered to it.
+ *
+ * __Hint__: All DisplayObjects (i.e. Sprites) that render to a RenderTexture should be preloaded
+ * otherwise black rectangles will be drawn instead.
+ *
+ * __Hint-2__: The actual memory allocation will happen on first render.
+ * You shouldn't create renderTextures each frame just to delete them after, try to reuse them.
+ *
+ * A RenderTexture takes a snapshot of any Display Object given to its render method. For example:
+ *
+ * ```js
+ * let renderer = PIXI.autoDetectRenderer();
+ * let renderTexture = PIXI.RenderTexture.create({ width: 800, height: 600 });
+ * let sprite = PIXI.Sprite.from("spinObj_01.png");
+ *
+ * sprite.position.x = 800/2;
+ * sprite.position.y = 600/2;
+ * sprite.anchor.x = 0.5;
+ * sprite.anchor.y = 0.5;
+ *
+ * renderer.render(sprite, {renderTexture});
+ * ```
+ * Note that you should not create a new renderer, but reuse the same one as the rest of the application.
+ *
+ * The Sprite in this case will be rendered using its local transform. To render this sprite at 0,0
+ * you can clear the transform
+ *
+ * ```js
+ *
+ * sprite.setTransform()
+ *
+ * let renderTexture = new PIXI.RenderTexture.create({ width: 100, height: 100 });
+ *
+ * renderer.render(sprite, {renderTexture}); // Renders to center of RenderTexture
+ * ```
+ * @memberof PIXI
+ */
+ var RenderTexture = /** @class */ (function (_super) {
+ __extends$i(RenderTexture, _super);
+ /**
+ * @param baseRenderTexture - The base texture object that this texture uses.
+ * @param frame - The rectangle frame of the texture to show.
+ */
+ function RenderTexture(baseRenderTexture, frame) {
+ var _this = _super.call(this, baseRenderTexture, frame) || this;
+ _this.valid = true;
+ _this.filterFrame = null;
+ _this.filterPoolKey = null;
+ _this.updateUvs();
+ return _this;
+ }
+ Object.defineProperty(RenderTexture.prototype, "framebuffer", {
+ /**
+ * Shortcut to `this.baseTexture.framebuffer`, saves baseTexture cast.
+ * @readonly
+ */
+ get: function () {
+ return this.baseTexture.framebuffer;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(RenderTexture.prototype, "multisample", {
+ /**
+ * Shortcut to `this.framebuffer.multisample`.
+ * @default PIXI.MSAA_QUALITY.NONE
+ */
+ get: function () {
+ return this.framebuffer.multisample;
+ },
+ set: function (value) {
+ this.framebuffer.multisample = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Resizes the RenderTexture.
+ * @param desiredWidth - The desired width to resize to.
+ * @param desiredHeight - The desired height to resize to.
+ * @param resizeBaseTexture - Should the baseTexture.width and height values be resized as well?
+ */
+ RenderTexture.prototype.resize = function (desiredWidth, desiredHeight, resizeBaseTexture) {
+ if (resizeBaseTexture === void 0) { resizeBaseTexture = true; }
+ var resolution = this.baseTexture.resolution;
+ var width = Math.round(desiredWidth * resolution) / resolution;
+ var height = Math.round(desiredHeight * resolution) / resolution;
+ // TODO - could be not required..
+ this.valid = (width > 0 && height > 0);
+ this._frame.width = this.orig.width = width;
+ this._frame.height = this.orig.height = height;
+ if (resizeBaseTexture) {
+ this.baseTexture.resize(width, height);
+ }
+ this.updateUvs();
+ };
+ /**
+ * Changes the resolution of baseTexture, but does not change framebuffer size.
+ * @param resolution - The new resolution to apply to RenderTexture
+ */
+ RenderTexture.prototype.setResolution = function (resolution) {
+ var baseTexture = this.baseTexture;
+ if (baseTexture.resolution === resolution) {
+ return;
+ }
+ baseTexture.setResolution(resolution);
+ this.resize(baseTexture.width, baseTexture.height, false);
+ };
+ RenderTexture.create = function (options) {
+ var arguments$1 = arguments;
+
+ var rest = [];
+ for (var _i = 1; _i < arguments.length; _i++) {
+ rest[_i - 1] = arguments$1[_i];
+ }
+ // @deprecated fallback, old-style: create(width, height, scaleMode, resolution)
+ if (typeof options === 'number') {
+ deprecation$1('6.0.0', 'Arguments (width, height, scaleMode, resolution) have been deprecated.');
+ /* eslint-disable prefer-rest-params */
+ options = {
+ width: options,
+ height: rest[0],
+ scaleMode: rest[1],
+ resolution: rest[2],
+ };
+ /* eslint-enable prefer-rest-params */
+ }
+ return new RenderTexture(new BaseRenderTexture(options));
+ };
+ return RenderTexture;
+ }(Texture));
+
+ /**
+ * Texture pool, used by FilterSystem and plugins.
+ *
+ * Stores collection of temporary pow2 or screen-sized renderTextures
+ *
+ * If you use custom RenderTexturePool for your filters, you can use methods
+ * `getFilterTexture` and `returnFilterTexture` same as in
+ * @memberof PIXI
+ */
+ var RenderTexturePool = /** @class */ (function () {
+ /**
+ * @param textureOptions - options that will be passed to BaseRenderTexture constructor
+ * @param {PIXI.SCALE_MODES} [textureOptions.scaleMode] - See {@link PIXI.SCALE_MODES} for possible values.
+ */
+ function RenderTexturePool(textureOptions) {
+ this.texturePool = {};
+ this.textureOptions = textureOptions || {};
+ this.enableFullScreen = false;
+ this._pixelsWidth = 0;
+ this._pixelsHeight = 0;
+ }
+ /**
+ * Creates texture with params that were specified in pool constructor.
+ * @param realWidth - Width of texture in pixels.
+ * @param realHeight - Height of texture in pixels.
+ * @param multisample - Number of samples of the framebuffer.
+ */
+ RenderTexturePool.prototype.createTexture = function (realWidth, realHeight, multisample) {
+ if (multisample === void 0) { multisample = exports.MSAA_QUALITY.NONE; }
+ var baseRenderTexture = new BaseRenderTexture(Object.assign({
+ width: realWidth,
+ height: realHeight,
+ resolution: 1,
+ multisample: multisample,
+ }, this.textureOptions));
+ return new RenderTexture(baseRenderTexture);
+ };
+ /**
+ * Gets a Power-of-Two render texture or fullScreen texture
+ * @param minWidth - The minimum width of the render texture.
+ * @param minHeight - The minimum height of the render texture.
+ * @param resolution - The resolution of the render texture.
+ * @param multisample - Number of samples of the render texture.
+ * @returns The new render texture.
+ */
+ RenderTexturePool.prototype.getOptimalTexture = function (minWidth, minHeight, resolution, multisample) {
+ if (resolution === void 0) { resolution = 1; }
+ if (multisample === void 0) { multisample = exports.MSAA_QUALITY.NONE; }
+ var key;
+ minWidth = Math.ceil((minWidth * resolution) - 1e-6);
+ minHeight = Math.ceil((minHeight * resolution) - 1e-6);
+ if (!this.enableFullScreen || minWidth !== this._pixelsWidth || minHeight !== this._pixelsHeight) {
+ minWidth = nextPow2(minWidth);
+ minHeight = nextPow2(minHeight);
+ key = (((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF)) >>> 0;
+ if (multisample > 1) {
+ key += multisample * 0x100000000;
+ }
+ }
+ else {
+ key = multisample > 1 ? -multisample : -1;
+ }
+ if (!this.texturePool[key]) {
+ this.texturePool[key] = [];
+ }
+ var renderTexture = this.texturePool[key].pop();
+ if (!renderTexture) {
+ renderTexture = this.createTexture(minWidth, minHeight, multisample);
+ }
+ renderTexture.filterPoolKey = key;
+ renderTexture.setResolution(resolution);
+ return renderTexture;
+ };
+ /**
+ * Gets extra texture of the same size as input renderTexture
+ *
+ * `getFilterTexture(input, 0.5)` or `getFilterTexture(0.5, input)`
+ * @param input - renderTexture from which size and resolution will be copied
+ * @param resolution - override resolution of the renderTexture
+ * It overrides, it does not multiply
+ * @param multisample - number of samples of the renderTexture
+ */
+ RenderTexturePool.prototype.getFilterTexture = function (input, resolution, multisample) {
+ var filterTexture = this.getOptimalTexture(input.width, input.height, resolution || input.resolution, multisample || exports.MSAA_QUALITY.NONE);
+ filterTexture.filterFrame = input.filterFrame;
+ return filterTexture;
+ };
+ /**
+ * Place a render texture back into the pool.
+ * @param renderTexture - The renderTexture to free
+ */
+ RenderTexturePool.prototype.returnTexture = function (renderTexture) {
+ var key = renderTexture.filterPoolKey;
+ renderTexture.filterFrame = null;
+ this.texturePool[key].push(renderTexture);
+ };
+ /**
+ * Alias for returnTexture, to be compliant with FilterSystem interface.
+ * @param renderTexture - The renderTexture to free
+ */
+ RenderTexturePool.prototype.returnFilterTexture = function (renderTexture) {
+ this.returnTexture(renderTexture);
+ };
+ /**
+ * Clears the pool.
+ * @param destroyTextures - Destroy all stored textures.
+ */
+ RenderTexturePool.prototype.clear = function (destroyTextures) {
+ destroyTextures = destroyTextures !== false;
+ if (destroyTextures) {
+ for (var i in this.texturePool) {
+ var textures = this.texturePool[i];
+ if (textures) {
+ for (var j = 0; j < textures.length; j++) {
+ textures[j].destroy(true);
+ }
+ }
+ }
+ }
+ this.texturePool = {};
+ };
+ /**
+ * If screen size was changed, drops all screen-sized textures,
+ * sets new screen size, sets `enableFullScreen` to true
+ *
+ * Size is measured in pixels, `renderer.view` can be passed here, not `renderer.screen`
+ * @param size - Initial size of screen.
+ */
+ RenderTexturePool.prototype.setScreenSize = function (size) {
+ if (size.width === this._pixelsWidth
+ && size.height === this._pixelsHeight) {
+ return;
+ }
+ this.enableFullScreen = size.width > 0 && size.height > 0;
+ for (var i in this.texturePool) {
+ if (!(Number(i) < 0)) {
+ continue;
+ }
+ var textures = this.texturePool[i];
+ if (textures) {
+ for (var j = 0; j < textures.length; j++) {
+ textures[j].destroy(true);
+ }
+ }
+ this.texturePool[i] = [];
+ }
+ this._pixelsWidth = size.width;
+ this._pixelsHeight = size.height;
+ };
+ /**
+ * Key that is used to store fullscreen renderTextures in a pool
+ * @constant
+ */
+ RenderTexturePool.SCREEN_KEY = -1;
+ return RenderTexturePool;
+ }());
+
+ /* eslint-disable max-len */
+ /**
+ * Holds the information for a single attribute structure required to render geometry.
+ *
+ * This does not contain the actual data, but instead has a buffer id that maps to a {@link PIXI.Buffer}
+ * This can include anything from positions, uvs, normals, colors etc.
+ * @memberof PIXI
+ */
+ var Attribute = /** @class */ (function () {
+ /**
+ * @param buffer - the id of the buffer that this attribute will look for
+ * @param size - the size of the attribute. If you have 2 floats per vertex (eg position x and y) this would be 2.
+ * @param normalized - should the data be normalized.
+ * @param {PIXI.TYPES} [type=PIXI.TYPES.FLOAT] - what type of number is the attribute. Check {@link PIXI.TYPES} to see the ones available
+ * @param [stride=0] - How far apart, in bytes, the start of each value is. (used for interleaving data)
+ * @param [start=0] - How far into the array to start reading values (used for interleaving data)
+ * @param [instance=false] - Whether the geometry is instanced.
+ */
+ function Attribute(buffer, size, normalized, type, stride, start, instance) {
+ if (size === void 0) { size = 0; }
+ if (normalized === void 0) { normalized = false; }
+ if (type === void 0) { type = exports.TYPES.FLOAT; }
+ this.buffer = buffer;
+ this.size = size;
+ this.normalized = normalized;
+ this.type = type;
+ this.stride = stride;
+ this.start = start;
+ this.instance = instance;
+ }
+ /** Destroys the Attribute. */
+ Attribute.prototype.destroy = function () {
+ this.buffer = null;
+ };
+ /**
+ * Helper function that creates an Attribute based on the information provided
+ * @param buffer - the id of the buffer that this attribute will look for
+ * @param [size=0] - the size of the attribute. If you have 2 floats per vertex (eg position x and y) this would be 2
+ * @param [normalized=false] - should the data be normalized.
+ * @param [type=PIXI.TYPES.FLOAT] - what type of number is the attribute. Check {@link PIXI.TYPES} to see the ones available
+ * @param [stride=0] - How far apart, in bytes, the start of each value is. (used for interleaving data)
+ * @returns - A new {@link PIXI.Attribute} based on the information provided
+ */
+ Attribute.from = function (buffer, size, normalized, type, stride) {
+ return new Attribute(buffer, size, normalized, type, stride);
+ };
+ return Attribute;
+ }());
+
+ var UID$4 = 0;
+ /**
+ * A wrapper for data so that it can be used and uploaded by WebGL
+ * @memberof PIXI
+ */
+ var Buffer = /** @class */ (function () {
+ /**
+ * @param {PIXI.IArrayBuffer} data - the data to store in the buffer.
+ * @param _static - `true` for static buffer
+ * @param index - `true` for index buffer
+ */
+ function Buffer(data, _static, index) {
+ if (_static === void 0) { _static = true; }
+ if (index === void 0) { index = false; }
+ this.data = (data || new Float32Array(1));
+ this._glBuffers = {};
+ this._updateID = 0;
+ this.index = index;
+ this.static = _static;
+ this.id = UID$4++;
+ this.disposeRunner = new Runner('disposeBuffer');
+ }
+ // TODO could explore flagging only a partial upload?
+ /**
+ * Flags this buffer as requiring an upload to the GPU.
+ * @param {PIXI.IArrayBuffer|number[]} [data] - the data to update in the buffer.
+ */
+ Buffer.prototype.update = function (data) {
+ if (data instanceof Array) {
+ data = new Float32Array(data);
+ }
+ this.data = data || this.data;
+ this._updateID++;
+ };
+ /** Disposes WebGL resources that are connected to this geometry. */
+ Buffer.prototype.dispose = function () {
+ this.disposeRunner.emit(this, false);
+ };
+ /** Destroys the buffer. */
+ Buffer.prototype.destroy = function () {
+ this.dispose();
+ this.data = null;
+ };
+ Object.defineProperty(Buffer.prototype, "index", {
+ get: function () {
+ return this.type === exports.BUFFER_TYPE.ELEMENT_ARRAY_BUFFER;
+ },
+ /**
+ * Flags whether this is an index buffer.
+ *
+ * Index buffers are of type `ELEMENT_ARRAY_BUFFER`. Note that setting this property to false will make
+ * the buffer of type `ARRAY_BUFFER`.
+ *
+ * For backwards compatibility.
+ */
+ set: function (value) {
+ this.type = value ? exports.BUFFER_TYPE.ELEMENT_ARRAY_BUFFER : exports.BUFFER_TYPE.ARRAY_BUFFER;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Helper function that creates a buffer based on an array or TypedArray
+ * @param {ArrayBufferView | number[]} data - the TypedArray that the buffer will store. If this is a regular Array it will be converted to a Float32Array.
+ * @returns - A new Buffer based on the data provided.
+ */
+ Buffer.from = function (data) {
+ if (data instanceof Array) {
+ data = new Float32Array(data);
+ }
+ return new Buffer(data);
+ };
+ return Buffer;
+ }());
+
+ /* eslint-disable object-shorthand */
+ var map$1 = {
+ Float32Array: Float32Array,
+ Uint32Array: Uint32Array,
+ Int32Array: Int32Array,
+ Uint8Array: Uint8Array,
+ };
+ function interleaveTypedArrays(arrays, sizes) {
+ var outSize = 0;
+ var stride = 0;
+ var views = {};
+ for (var i = 0; i < arrays.length; i++) {
+ stride += sizes[i];
+ outSize += arrays[i].length;
+ }
+ var buffer = new ArrayBuffer(outSize * 4);
+ var out = null;
+ var littleOffset = 0;
+ for (var i = 0; i < arrays.length; i++) {
+ var size = sizes[i];
+ var array = arrays[i];
+ var type = getBufferType(array);
+ if (!views[type]) {
+ views[type] = new map$1[type](buffer);
+ }
+ out = views[type];
+ for (var j = 0; j < array.length; j++) {
+ var indexStart = ((j / size | 0) * stride) + littleOffset;
+ var index = j % size;
+ out[indexStart + index] = array[j];
+ }
+ littleOffset += size;
+ }
+ return new Float32Array(buffer);
+ }
+
+ var byteSizeMap$1 = { 5126: 4, 5123: 2, 5121: 1 };
+ var UID$3 = 0;
+ /* eslint-disable object-shorthand */
+ var map = {
+ Float32Array: Float32Array,
+ Uint32Array: Uint32Array,
+ Int32Array: Int32Array,
+ Uint8Array: Uint8Array,
+ Uint16Array: Uint16Array,
+ };
+ /* eslint-disable max-len */
+ /**
+ * The Geometry represents a model. It consists of two components:
+ * - GeometryStyle - The structure of the model such as the attributes layout
+ * - GeometryData - the data of the model - this consists of buffers.
+ * This can include anything from positions, uvs, normals, colors etc.
+ *
+ * Geometry can be defined without passing in a style or data if required (thats how I prefer!)
+ *
+ * ```js
+ * let geometry = new PIXI.Geometry();
+ *
+ * geometry.addAttribute('positions', [0, 0, 100, 0, 100, 100, 0, 100], 2);
+ * geometry.addAttribute('uvs', [0,0,1,0,1,1,0,1],2)
+ * geometry.addIndex([0,1,2,1,3,2])
+ * ```
+ * @memberof PIXI
+ */
+ var Geometry = /** @class */ (function () {
+ /**
+ * @param buffers - An array of buffers. optional.
+ * @param attributes - Of the geometry, optional structure of the attributes layout
+ */
+ function Geometry(buffers, attributes) {
+ if (buffers === void 0) { buffers = []; }
+ if (attributes === void 0) { attributes = {}; }
+ this.buffers = buffers;
+ this.indexBuffer = null;
+ this.attributes = attributes;
+ this.glVertexArrayObjects = {};
+ this.id = UID$3++;
+ this.instanced = false;
+ this.instanceCount = 1;
+ this.disposeRunner = new Runner('disposeGeometry');
+ this.refCount = 0;
+ }
+ /**
+ *
+ * Adds an attribute to the geometry
+ * Note: `stride` and `start` should be `undefined` if you dont know them, not 0!
+ * @param id - the name of the attribute (matching up to a shader)
+ * @param {PIXI.Buffer|number[]} buffer - the buffer that holds the data of the attribute . You can also provide an Array and a buffer will be created from it.
+ * @param size - the size of the attribute. If you have 2 floats per vertex (eg position x and y) this would be 2
+ * @param normalized - should the data be normalized.
+ * @param [type=PIXI.TYPES.FLOAT] - what type of number is the attribute. Check {PIXI.TYPES} to see the ones available
+ * @param [stride=0] - How far apart, in bytes, the start of each value is. (used for interleaving data)
+ * @param [start=0] - How far into the array to start reading values (used for interleaving data)
+ * @param instance - Instancing flag
+ * @returns - Returns self, useful for chaining.
+ */
+ Geometry.prototype.addAttribute = function (id, buffer, size, normalized, type, stride, start, instance) {
+ if (size === void 0) { size = 0; }
+ if (normalized === void 0) { normalized = false; }
+ if (instance === void 0) { instance = false; }
+ if (!buffer) {
+ throw new Error('You must pass a buffer when creating an attribute');
+ }
+ // check if this is a buffer!
+ if (!(buffer instanceof Buffer)) {
+ // its an array!
+ if (buffer instanceof Array) {
+ buffer = new Float32Array(buffer);
+ }
+ buffer = new Buffer(buffer);
+ }
+ var ids = id.split('|');
+ if (ids.length > 1) {
+ for (var i = 0; i < ids.length; i++) {
+ this.addAttribute(ids[i], buffer, size, normalized, type);
+ }
+ return this;
+ }
+ var bufferIndex = this.buffers.indexOf(buffer);
+ if (bufferIndex === -1) {
+ this.buffers.push(buffer);
+ bufferIndex = this.buffers.length - 1;
+ }
+ this.attributes[id] = new Attribute(bufferIndex, size, normalized, type, stride, start, instance);
+ // assuming that if there is instanced data then this will be drawn with instancing!
+ this.instanced = this.instanced || instance;
+ return this;
+ };
+ /**
+ * Returns the requested attribute.
+ * @param id - The name of the attribute required
+ * @returns - The attribute requested.
+ */
+ Geometry.prototype.getAttribute = function (id) {
+ return this.attributes[id];
+ };
+ /**
+ * Returns the requested buffer.
+ * @param id - The name of the buffer required.
+ * @returns - The buffer requested.
+ */
+ Geometry.prototype.getBuffer = function (id) {
+ return this.buffers[this.getAttribute(id).buffer];
+ };
+ /**
+ *
+ * Adds an index buffer to the geometry
+ * The index buffer contains integers, three for each triangle in the geometry, which reference the various attribute buffers (position, colour, UV coordinates, other UV coordinates, normal, 鈥�). There is only ONE index buffer.
+ * @param {PIXI.Buffer|number[]} [buffer] - The buffer that holds the data of the index buffer. You can also provide an Array and a buffer will be created from it.
+ * @returns - Returns self, useful for chaining.
+ */
+ Geometry.prototype.addIndex = function (buffer) {
+ if (!(buffer instanceof Buffer)) {
+ // its an array!
+ if (buffer instanceof Array) {
+ buffer = new Uint16Array(buffer);
+ }
+ buffer = new Buffer(buffer);
+ }
+ buffer.type = exports.BUFFER_TYPE.ELEMENT_ARRAY_BUFFER;
+ this.indexBuffer = buffer;
+ if (this.buffers.indexOf(buffer) === -1) {
+ this.buffers.push(buffer);
+ }
+ return this;
+ };
+ /**
+ * Returns the index buffer
+ * @returns - The index buffer.
+ */
+ Geometry.prototype.getIndex = function () {
+ return this.indexBuffer;
+ };
+ /**
+ * This function modifies the structure so that all current attributes become interleaved into a single buffer
+ * This can be useful if your model remains static as it offers a little performance boost
+ * @returns - Returns self, useful for chaining.
+ */
+ Geometry.prototype.interleave = function () {
+ // a simple check to see if buffers are already interleaved..
+ if (this.buffers.length === 1 || (this.buffers.length === 2 && this.indexBuffer))
+ { return this; }
+ // assume already that no buffers are interleaved
+ var arrays = [];
+ var sizes = [];
+ var interleavedBuffer = new Buffer();
+ var i;
+ for (i in this.attributes) {
+ var attribute = this.attributes[i];
+ var buffer = this.buffers[attribute.buffer];
+ arrays.push(buffer.data);
+ sizes.push((attribute.size * byteSizeMap$1[attribute.type]) / 4);
+ attribute.buffer = 0;
+ }
+ interleavedBuffer.data = interleaveTypedArrays(arrays, sizes);
+ for (i = 0; i < this.buffers.length; i++) {
+ if (this.buffers[i] !== this.indexBuffer) {
+ this.buffers[i].destroy();
+ }
+ }
+ this.buffers = [interleavedBuffer];
+ if (this.indexBuffer) {
+ this.buffers.push(this.indexBuffer);
+ }
+ return this;
+ };
+ /** Get the size of the geometries, in vertices. */
+ Geometry.prototype.getSize = function () {
+ for (var i in this.attributes) {
+ var attribute = this.attributes[i];
+ var buffer = this.buffers[attribute.buffer];
+ return buffer.data.length / ((attribute.stride / 4) || attribute.size);
+ }
+ return 0;
+ };
+ /** Disposes WebGL resources that are connected to this geometry. */
+ Geometry.prototype.dispose = function () {
+ this.disposeRunner.emit(this, false);
+ };
+ /** Destroys the geometry. */
+ Geometry.prototype.destroy = function () {
+ this.dispose();
+ this.buffers = null;
+ this.indexBuffer = null;
+ this.attributes = null;
+ };
+ /**
+ * Returns a clone of the geometry.
+ * @returns - A new clone of this geometry.
+ */
+ Geometry.prototype.clone = function () {
+ var geometry = new Geometry();
+ for (var i = 0; i < this.buffers.length; i++) {
+ geometry.buffers[i] = new Buffer(this.buffers[i].data.slice(0));
+ }
+ for (var i in this.attributes) {
+ var attrib = this.attributes[i];
+ geometry.attributes[i] = new Attribute(attrib.buffer, attrib.size, attrib.normalized, attrib.type, attrib.stride, attrib.start, attrib.instance);
+ }
+ if (this.indexBuffer) {
+ geometry.indexBuffer = geometry.buffers[this.buffers.indexOf(this.indexBuffer)];
+ geometry.indexBuffer.type = exports.BUFFER_TYPE.ELEMENT_ARRAY_BUFFER;
+ }
+ return geometry;
+ };
+ /**
+ * Merges an array of geometries into a new single one.
+ *
+ * Geometry attribute styles must match for this operation to work.
+ * @param geometries - array of geometries to merge
+ * @returns - Shiny new geometry!
+ */
+ Geometry.merge = function (geometries) {
+ // todo add a geometry check!
+ // also a size check.. cant be too big!]
+ var geometryOut = new Geometry();
+ var arrays = [];
+ var sizes = [];
+ var offsets = [];
+ var geometry;
+ // pass one.. get sizes..
+ for (var i = 0; i < geometries.length; i++) {
+ geometry = geometries[i];
+ for (var j = 0; j < geometry.buffers.length; j++) {
+ sizes[j] = sizes[j] || 0;
+ sizes[j] += geometry.buffers[j].data.length;
+ offsets[j] = 0;
+ }
+ }
+ // build the correct size arrays..
+ for (var i = 0; i < geometry.buffers.length; i++) {
+ // TODO types!
+ arrays[i] = new map[getBufferType(geometry.buffers[i].data)](sizes[i]);
+ geometryOut.buffers[i] = new Buffer(arrays[i]);
+ }
+ // pass to set data..
+ for (var i = 0; i < geometries.length; i++) {
+ geometry = geometries[i];
+ for (var j = 0; j < geometry.buffers.length; j++) {
+ arrays[j].set(geometry.buffers[j].data, offsets[j]);
+ offsets[j] += geometry.buffers[j].data.length;
+ }
+ }
+ geometryOut.attributes = geometry.attributes;
+ if (geometry.indexBuffer) {
+ geometryOut.indexBuffer = geometryOut.buffers[geometry.buffers.indexOf(geometry.indexBuffer)];
+ geometryOut.indexBuffer.type = exports.BUFFER_TYPE.ELEMENT_ARRAY_BUFFER;
+ var offset = 0;
+ var stride = 0;
+ var offset2 = 0;
+ var bufferIndexToCount = 0;
+ // get a buffer
+ for (var i = 0; i < geometry.buffers.length; i++) {
+ if (geometry.buffers[i] !== geometry.indexBuffer) {
+ bufferIndexToCount = i;
+ break;
+ }
+ }
+ // figure out the stride of one buffer..
+ for (var i in geometry.attributes) {
+ var attribute = geometry.attributes[i];
+ if ((attribute.buffer | 0) === bufferIndexToCount) {
+ stride += ((attribute.size * byteSizeMap$1[attribute.type]) / 4);
+ }
+ }
+ // time to off set all indexes..
+ for (var i = 0; i < geometries.length; i++) {
+ var indexBufferData = geometries[i].indexBuffer.data;
+ for (var j = 0; j < indexBufferData.length; j++) {
+ geometryOut.indexBuffer.data[j + offset2] += offset;
+ }
+ offset += geometries[i].buffers[bufferIndexToCount].data.length / (stride);
+ offset2 += indexBufferData.length;
+ }
+ }
+ return geometryOut;
+ };
+ return Geometry;
+ }());
+
+ /**
+ * Helper class to create a quad
+ * @memberof PIXI
+ */
+ var Quad = /** @class */ (function (_super) {
+ __extends$i(Quad, _super);
+ function Quad() {
+ var _this = _super.call(this) || this;
+ _this.addAttribute('aVertexPosition', new Float32Array([
+ 0, 0,
+ 1, 0,
+ 1, 1,
+ 0, 1 ]))
+ .addIndex([0, 1, 3, 2]);
+ return _this;
+ }
+ return Quad;
+ }(Geometry));
+
+ /**
+ * Helper class to create a quad with uvs like in v4
+ * @memberof PIXI
+ */
+ var QuadUv = /** @class */ (function (_super) {
+ __extends$i(QuadUv, _super);
+ function QuadUv() {
+ var _this = _super.call(this) || this;
+ _this.vertices = new Float32Array([
+ -1, -1,
+ 1, -1,
+ 1, 1,
+ -1, 1 ]);
+ _this.uvs = new Float32Array([
+ 0, 0,
+ 1, 0,
+ 1, 1,
+ 0, 1 ]);
+ _this.vertexBuffer = new Buffer(_this.vertices);
+ _this.uvBuffer = new Buffer(_this.uvs);
+ _this.addAttribute('aVertexPosition', _this.vertexBuffer)
+ .addAttribute('aTextureCoord', _this.uvBuffer)
+ .addIndex([0, 1, 2, 0, 2, 3]);
+ return _this;
+ }
+ /**
+ * Maps two Rectangle to the quad.
+ * @param targetTextureFrame - The first rectangle
+ * @param destinationFrame - The second rectangle
+ * @returns - Returns itself.
+ */
+ QuadUv.prototype.map = function (targetTextureFrame, destinationFrame) {
+ var x = 0; // destinationFrame.x / targetTextureFrame.width;
+ var y = 0; // destinationFrame.y / targetTextureFrame.height;
+ this.uvs[0] = x;
+ this.uvs[1] = y;
+ this.uvs[2] = x + (destinationFrame.width / targetTextureFrame.width);
+ this.uvs[3] = y;
+ this.uvs[4] = x + (destinationFrame.width / targetTextureFrame.width);
+ this.uvs[5] = y + (destinationFrame.height / targetTextureFrame.height);
+ this.uvs[6] = x;
+ this.uvs[7] = y + (destinationFrame.height / targetTextureFrame.height);
+ x = destinationFrame.x;
+ y = destinationFrame.y;
+ this.vertices[0] = x;
+ this.vertices[1] = y;
+ this.vertices[2] = x + destinationFrame.width;
+ this.vertices[3] = y;
+ this.vertices[4] = x + destinationFrame.width;
+ this.vertices[5] = y + destinationFrame.height;
+ this.vertices[6] = x;
+ this.vertices[7] = y + destinationFrame.height;
+ this.invalidate();
+ return this;
+ };
+ /**
+ * Legacy upload method, just marks buffers dirty.
+ * @returns - Returns itself.
+ */
+ QuadUv.prototype.invalidate = function () {
+ this.vertexBuffer._updateID++;
+ this.uvBuffer._updateID++;
+ return this;
+ };
+ return QuadUv;
+ }(Geometry));
+
+ var UID$2 = 0;
+ /**
+ * Uniform group holds uniform map and some ID's for work
+ *
+ * `UniformGroup` has two modes:
+ *
+ * 1: Normal mode
+ * Normal mode will upload the uniforms with individual function calls as required
+ *
+ * 2: Uniform buffer mode
+ * This mode will treat the uniforms as a uniform buffer. You can pass in either a buffer that you manually handle, or
+ * or a generic object that PixiJS will automatically map to a buffer for you.
+ * For maximum benefits, make Ubo UniformGroups static, and only update them each frame.
+ *
+ * Rules of UBOs:
+ * - UBOs only work with WebGL2, so make sure you have a fallback!
+ * - Only floats are supported (including vec[2,3,4], mat[2,3,4])
+ * - Samplers cannot be used in ubo's (a GPU limitation)
+ * - You must ensure that the object you pass in exactly matches in the shader ubo structure.
+ * Otherwise, weirdness will ensue!
+ * - The name of the ubo object added to the group must match exactly the name of the ubo in the shader.
+ *
+ * ```
+ * // ubo in shader:
+ * uniform myCoolData { // declaring a ubo..
+ * mat4 uCoolMatrix;
+ * float uFloatyMcFloatFace
+ *
+ *
+ * // a new uniform buffer object..
+ * const myCoolData = new UniformBufferGroup({
+ * uCoolMatrix: new Matrix(),
+ * uFloatyMcFloatFace: 23,
+ * }}
+ *
+ * // build a shader...
+ * const shader = Shader.from(srcVert, srcFrag, {
+ * myCoolData // name matches the ubo name in the shader. will be processed accordingly.
+ * })
+ *
+ * ```
+ * @memberof PIXI
+ */
+ var UniformGroup = /** @class */ (function () {
+ /**
+ * @param {object | Buffer} [uniforms] - Custom uniforms to use to augment the built-in ones. Or a pixi buffer.
+ * @param isStatic - Uniforms wont be changed after creation.
+ * @param isUbo - If true, will treat this uniform group as a uniform buffer object.
+ */
+ function UniformGroup(uniforms, isStatic, isUbo) {
+ this.group = true;
+ // lets generate this when the shader ?
+ this.syncUniforms = {};
+ this.dirtyId = 0;
+ this.id = UID$2++;
+ this.static = !!isStatic;
+ this.ubo = !!isUbo;
+ if (uniforms instanceof Buffer) {
+ this.buffer = uniforms;
+ this.buffer.type = exports.BUFFER_TYPE.UNIFORM_BUFFER;
+ this.autoManage = false;
+ this.ubo = true;
+ }
+ else {
+ this.uniforms = uniforms;
+ if (this.ubo) {
+ this.buffer = new Buffer(new Float32Array(1));
+ this.buffer.type = exports.BUFFER_TYPE.UNIFORM_BUFFER;
+ this.autoManage = true;
+ }
+ }
+ }
+ UniformGroup.prototype.update = function () {
+ this.dirtyId++;
+ if (!this.autoManage && this.buffer) {
+ this.buffer.update();
+ }
+ };
+ UniformGroup.prototype.add = function (name, uniforms, _static) {
+ if (!this.ubo) {
+ this.uniforms[name] = new UniformGroup(uniforms, _static);
+ }
+ else {
+ // eslint-disable-next-line max-len
+ throw new Error('[UniformGroup] uniform groups in ubo mode cannot be modified, or have uniform groups nested in them');
+ }
+ };
+ UniformGroup.from = function (uniforms, _static, _ubo) {
+ return new UniformGroup(uniforms, _static, _ubo);
+ };
+ /**
+ * A short hand function for creating a static UBO UniformGroup.
+ * @param uniforms - the ubo item
+ * @param _static - should this be updated each time it is used? defaults to true here!
+ */
+ UniformGroup.uboFrom = function (uniforms, _static) {
+ return new UniformGroup(uniforms, _static !== null && _static !== void 0 ? _static : true, true);
+ };
+ return UniformGroup;
+ }());
+
+ /**
+ * System plugin to the renderer to manage filter states.
+ * @ignore
+ */
+ var FilterState = /** @class */ (function () {
+ function FilterState() {
+ this.renderTexture = null;
+ this.target = null;
+ this.legacy = false;
+ this.resolution = 1;
+ this.multisample = exports.MSAA_QUALITY.NONE;
+ // next three fields are created only for root
+ // re-assigned for everything else
+ this.sourceFrame = new Rectangle();
+ this.destinationFrame = new Rectangle();
+ this.bindingSourceFrame = new Rectangle();
+ this.bindingDestinationFrame = new Rectangle();
+ this.filters = [];
+ this.transform = null;
+ }
+ /** Clears the state */
+ FilterState.prototype.clear = function () {
+ this.target = null;
+ this.filters = null;
+ this.renderTexture = null;
+ };
+ return FilterState;
+ }());
+
+ var tempPoints = [new Point(), new Point(), new Point(), new Point()];
+ var tempMatrix$2 = new Matrix();
+ /**
+ * System plugin to the renderer to manage filters.
+ *
+ * ## Pipeline
+ *
+ * The FilterSystem executes the filtering pipeline by rendering the display-object into a texture, applying its
+ * [filters]{@link PIXI.Filter} in series, and the last filter outputs into the final render-target.
+ *
+ * The filter-frame is the rectangle in world space being filtered, and those contents are mapped into
+ * `(0, 0, filterFrame.width, filterFrame.height)` into the filter render-texture. The filter-frame is also called
+ * the source-frame, as it is used to bind the filter render-textures. The last filter outputs to the `filterFrame`
+ * in the final render-target.
+ *
+ * ## Usage
+ *
+ * {@link PIXI.Container#renderAdvanced} is an example of how to use the filter system. It is a 3 step process:
+ *
+ * **push**: Use {@link PIXI.FilterSystem#push} to push the set of filters to be applied on a filter-target.
+ * **render**: Render the contents to be filtered using the renderer. The filter-system will only capture the contents
+ * inside the bounds of the filter-target. NOTE: Using {@link PIXI.Renderer#render} is
+ * illegal during an existing render cycle, and it may reset the filter system.
+ * **pop**: Use {@link PIXI.FilterSystem#pop} to pop & execute the filters you initially pushed. It will apply them
+ * serially and output to the bounds of the filter-target.
+ * @memberof PIXI
+ */
+ var FilterSystem = /** @class */ (function () {
+ /**
+ * @param renderer - The renderer this System works for.
+ */
+ function FilterSystem(renderer) {
+ this.renderer = renderer;
+ this.defaultFilterStack = [{}];
+ this.texturePool = new RenderTexturePool();
+ this.texturePool.setScreenSize(renderer.view);
+ this.statePool = [];
+ this.quad = new Quad();
+ this.quadUv = new QuadUv();
+ this.tempRect = new Rectangle();
+ this.activeState = {};
+ this.globalUniforms = new UniformGroup({
+ outputFrame: new Rectangle(),
+ inputSize: new Float32Array(4),
+ inputPixel: new Float32Array(4),
+ inputClamp: new Float32Array(4),
+ resolution: 1,
+ // legacy variables
+ filterArea: new Float32Array(4),
+ filterClamp: new Float32Array(4),
+ }, true);
+ this.forceClear = false;
+ this.useMaxPadding = false;
+ }
+ /**
+ * Pushes a set of filters to be applied later to the system. This will redirect further rendering into an
+ * input render-texture for the rest of the filtering pipeline.
+ * @param {PIXI.DisplayObject} target - The target of the filter to render.
+ * @param filters - The filters to apply.
+ */
+ FilterSystem.prototype.push = function (target, filters) {
+ var _a, _b;
+ var renderer = this.renderer;
+ var filterStack = this.defaultFilterStack;
+ var state = this.statePool.pop() || new FilterState();
+ var renderTextureSystem = this.renderer.renderTexture;
+ var resolution = filters[0].resolution;
+ var multisample = filters[0].multisample;
+ var padding = filters[0].padding;
+ var autoFit = filters[0].autoFit;
+ // We don't know whether it's a legacy filter until it was bound for the first time,
+ // therefore we have to assume that it is if legacy is undefined.
+ var legacy = (_a = filters[0].legacy) !== null && _a !== void 0 ? _a : true;
+ for (var i = 1; i < filters.length; i++) {
+ var filter = filters[i];
+ // let's use the lowest resolution
+ resolution = Math.min(resolution, filter.resolution);
+ // let's use the lowest number of samples
+ multisample = Math.min(multisample, filter.multisample);
+ // figure out the padding required for filters
+ padding = this.useMaxPadding
+ // old behavior: use largest amount of padding!
+ ? Math.max(padding, filter.padding)
+ // new behavior: sum the padding
+ : padding + filter.padding;
+ // only auto fit if all filters are autofit
+ autoFit = autoFit && filter.autoFit;
+ legacy = legacy || ((_b = filter.legacy) !== null && _b !== void 0 ? _b : true);
+ }
+ if (filterStack.length === 1) {
+ this.defaultFilterStack[0].renderTexture = renderTextureSystem.current;
+ }
+ filterStack.push(state);
+ state.resolution = resolution;
+ state.multisample = multisample;
+ state.legacy = legacy;
+ state.target = target;
+ state.sourceFrame.copyFrom(target.filterArea || target.getBounds(true));
+ state.sourceFrame.pad(padding);
+ var sourceFrameProjected = this.tempRect.copyFrom(renderTextureSystem.sourceFrame);
+ // Project source frame into world space (if projection is applied)
+ if (renderer.projection.transform) {
+ this.transformAABB(tempMatrix$2.copyFrom(renderer.projection.transform).invert(), sourceFrameProjected);
+ }
+ if (autoFit) {
+ state.sourceFrame.fit(sourceFrameProjected);
+ if (state.sourceFrame.width <= 0 || state.sourceFrame.height <= 0) {
+ state.sourceFrame.width = 0;
+ state.sourceFrame.height = 0;
+ }
+ }
+ else if (!state.sourceFrame.intersects(sourceFrameProjected)) {
+ state.sourceFrame.width = 0;
+ state.sourceFrame.height = 0;
+ }
+ // Round sourceFrame in screen space based on render-texture.
+ this.roundFrame(state.sourceFrame, renderTextureSystem.current ? renderTextureSystem.current.resolution : renderer.resolution, renderTextureSystem.sourceFrame, renderTextureSystem.destinationFrame, renderer.projection.transform);
+ state.renderTexture = this.getOptimalFilterTexture(state.sourceFrame.width, state.sourceFrame.height, resolution, multisample);
+ state.filters = filters;
+ state.destinationFrame.width = state.renderTexture.width;
+ state.destinationFrame.height = state.renderTexture.height;
+ var destinationFrame = this.tempRect;
+ destinationFrame.x = 0;
+ destinationFrame.y = 0;
+ destinationFrame.width = state.sourceFrame.width;
+ destinationFrame.height = state.sourceFrame.height;
+ state.renderTexture.filterFrame = state.sourceFrame;
+ state.bindingSourceFrame.copyFrom(renderTextureSystem.sourceFrame);
+ state.bindingDestinationFrame.copyFrom(renderTextureSystem.destinationFrame);
+ state.transform = renderer.projection.transform;
+ renderer.projection.transform = null;
+ renderTextureSystem.bind(state.renderTexture, state.sourceFrame, destinationFrame);
+ renderer.framebuffer.clear(0, 0, 0, 0);
+ };
+ /** Pops off the filter and applies it. */
+ FilterSystem.prototype.pop = function () {
+ var filterStack = this.defaultFilterStack;
+ var state = filterStack.pop();
+ var filters = state.filters;
+ this.activeState = state;
+ var globalUniforms = this.globalUniforms.uniforms;
+ globalUniforms.outputFrame = state.sourceFrame;
+ globalUniforms.resolution = state.resolution;
+ var inputSize = globalUniforms.inputSize;
+ var inputPixel = globalUniforms.inputPixel;
+ var inputClamp = globalUniforms.inputClamp;
+ inputSize[0] = state.destinationFrame.width;
+ inputSize[1] = state.destinationFrame.height;
+ inputSize[2] = 1.0 / inputSize[0];
+ inputSize[3] = 1.0 / inputSize[1];
+ inputPixel[0] = Math.round(inputSize[0] * state.resolution);
+ inputPixel[1] = Math.round(inputSize[1] * state.resolution);
+ inputPixel[2] = 1.0 / inputPixel[0];
+ inputPixel[3] = 1.0 / inputPixel[1];
+ inputClamp[0] = 0.5 * inputPixel[2];
+ inputClamp[1] = 0.5 * inputPixel[3];
+ inputClamp[2] = (state.sourceFrame.width * inputSize[2]) - (0.5 * inputPixel[2]);
+ inputClamp[3] = (state.sourceFrame.height * inputSize[3]) - (0.5 * inputPixel[3]);
+ // only update the rect if its legacy..
+ if (state.legacy) {
+ var filterArea = globalUniforms.filterArea;
+ filterArea[0] = state.destinationFrame.width;
+ filterArea[1] = state.destinationFrame.height;
+ filterArea[2] = state.sourceFrame.x;
+ filterArea[3] = state.sourceFrame.y;
+ globalUniforms.filterClamp = globalUniforms.inputClamp;
+ }
+ this.globalUniforms.update();
+ var lastState = filterStack[filterStack.length - 1];
+ this.renderer.framebuffer.blit();
+ if (filters.length === 1) {
+ filters[0].apply(this, state.renderTexture, lastState.renderTexture, exports.CLEAR_MODES.BLEND, state);
+ this.returnFilterTexture(state.renderTexture);
+ }
+ else {
+ var flip = state.renderTexture;
+ var flop = this.getOptimalFilterTexture(flip.width, flip.height, state.resolution);
+ flop.filterFrame = flip.filterFrame;
+ var i = 0;
+ for (i = 0; i < filters.length - 1; ++i) {
+ if (i === 1 && state.multisample > 1) {
+ flop = this.getOptimalFilterTexture(flip.width, flip.height, state.resolution);
+ flop.filterFrame = flip.filterFrame;
+ }
+ filters[i].apply(this, flip, flop, exports.CLEAR_MODES.CLEAR, state);
+ var t = flip;
+ flip = flop;
+ flop = t;
+ }
+ filters[i].apply(this, flip, lastState.renderTexture, exports.CLEAR_MODES.BLEND, state);
+ if (i > 1 && state.multisample > 1) {
+ this.returnFilterTexture(state.renderTexture);
+ }
+ this.returnFilterTexture(flip);
+ this.returnFilterTexture(flop);
+ }
+ // lastState.renderTexture is blitted when lastState is popped
+ state.clear();
+ this.statePool.push(state);
+ };
+ /**
+ * Binds a renderTexture with corresponding `filterFrame`, clears it if mode corresponds.
+ * @param filterTexture - renderTexture to bind, should belong to filter pool or filter stack
+ * @param clearMode - clearMode, by default its CLEAR/YES. See {@link PIXI.CLEAR_MODES}
+ */
+ FilterSystem.prototype.bindAndClear = function (filterTexture, clearMode) {
+ if (clearMode === void 0) { clearMode = exports.CLEAR_MODES.CLEAR; }
+ var _a = this.renderer, renderTextureSystem = _a.renderTexture, stateSystem = _a.state;
+ if (filterTexture === this.defaultFilterStack[this.defaultFilterStack.length - 1].renderTexture) {
+ // Restore projection transform if rendering into the output render-target.
+ this.renderer.projection.transform = this.activeState.transform;
+ }
+ else {
+ // Prevent projection within filtering pipeline.
+ this.renderer.projection.transform = null;
+ }
+ if (filterTexture && filterTexture.filterFrame) {
+ var destinationFrame = this.tempRect;
+ destinationFrame.x = 0;
+ destinationFrame.y = 0;
+ destinationFrame.width = filterTexture.filterFrame.width;
+ destinationFrame.height = filterTexture.filterFrame.height;
+ renderTextureSystem.bind(filterTexture, filterTexture.filterFrame, destinationFrame);
+ }
+ else if (filterTexture !== this.defaultFilterStack[this.defaultFilterStack.length - 1].renderTexture) {
+ renderTextureSystem.bind(filterTexture);
+ }
+ else {
+ // Restore binding for output render-target.
+ this.renderer.renderTexture.bind(filterTexture, this.activeState.bindingSourceFrame, this.activeState.bindingDestinationFrame);
+ }
+ // Clear the texture in BLIT mode if blending is disabled or the forceClear flag is set. The blending
+ // is stored in the 0th bit of the state.
+ var autoClear = (stateSystem.stateId & 1) || this.forceClear;
+ if (clearMode === exports.CLEAR_MODES.CLEAR
+ || (clearMode === exports.CLEAR_MODES.BLIT && autoClear)) {
+ // Use framebuffer.clear because we want to clear the whole filter texture, not just the filtering
+ // area over which the shaders are run. This is because filters may sampling outside of it (e.g. blur)
+ // instead of clamping their arithmetic.
+ this.renderer.framebuffer.clear(0, 0, 0, 0);
+ }
+ };
+ /**
+ * Draws a filter using the default rendering process.
+ *
+ * This should be called only by {@link Filter#apply}.
+ * @param filter - The filter to draw.
+ * @param input - The input render target.
+ * @param output - The target to output to.
+ * @param clearMode - Should the output be cleared before rendering to it
+ */
+ FilterSystem.prototype.applyFilter = function (filter, input, output, clearMode) {
+ var renderer = this.renderer;
+ // Set state before binding, so bindAndClear gets the blend mode.
+ renderer.state.set(filter.state);
+ this.bindAndClear(output, clearMode);
+ // set the uniforms..
+ filter.uniforms.uSampler = input;
+ filter.uniforms.filterGlobals = this.globalUniforms;
+ // TODO make it so that the order of this does not matter..
+ // because it does at the moment cos of global uniforms.
+ // they need to get resynced
+ renderer.shader.bind(filter);
+ // check to see if the filter is a legacy one..
+ filter.legacy = !!filter.program.attributeData.aTextureCoord;
+ if (filter.legacy) {
+ this.quadUv.map(input._frame, input.filterFrame);
+ renderer.geometry.bind(this.quadUv);
+ renderer.geometry.draw(exports.DRAW_MODES.TRIANGLES);
+ }
+ else {
+ renderer.geometry.bind(this.quad);
+ renderer.geometry.draw(exports.DRAW_MODES.TRIANGLE_STRIP);
+ }
+ };
+ /**
+ * Multiply _input normalized coordinates_ to this matrix to get _sprite texture normalized coordinates_.
+ *
+ * Use `outputMatrix * vTextureCoord` in the shader.
+ * @param outputMatrix - The matrix to output to.
+ * @param {PIXI.Sprite} sprite - The sprite to map to.
+ * @returns The mapped matrix.
+ */
+ FilterSystem.prototype.calculateSpriteMatrix = function (outputMatrix, sprite) {
+ var _a = this.activeState, sourceFrame = _a.sourceFrame, destinationFrame = _a.destinationFrame;
+ var orig = sprite._texture.orig;
+ var mappedMatrix = outputMatrix.set(destinationFrame.width, 0, 0, destinationFrame.height, sourceFrame.x, sourceFrame.y);
+ var worldTransform = sprite.worldTransform.copyTo(Matrix.TEMP_MATRIX);
+ worldTransform.invert();
+ mappedMatrix.prepend(worldTransform);
+ mappedMatrix.scale(1.0 / orig.width, 1.0 / orig.height);
+ mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y);
+ return mappedMatrix;
+ };
+ /** Destroys this Filter System. */
+ FilterSystem.prototype.destroy = function () {
+ this.renderer = null;
+ // Those textures has to be destroyed by RenderTextureSystem or FramebufferSystem
+ this.texturePool.clear(false);
+ };
+ /**
+ * Gets a Power-of-Two render texture or fullScreen texture
+ * @param minWidth - The minimum width of the render texture in real pixels.
+ * @param minHeight - The minimum height of the render texture in real pixels.
+ * @param resolution - The resolution of the render texture.
+ * @param multisample - Number of samples of the render texture.
+ * @returns - The new render texture.
+ */
+ FilterSystem.prototype.getOptimalFilterTexture = function (minWidth, minHeight, resolution, multisample) {
+ if (resolution === void 0) { resolution = 1; }
+ if (multisample === void 0) { multisample = exports.MSAA_QUALITY.NONE; }
+ return this.texturePool.getOptimalTexture(minWidth, minHeight, resolution, multisample);
+ };
+ /**
+ * Gets extra render texture to use inside current filter
+ * To be compliant with older filters, you can use params in any order
+ * @param input - renderTexture from which size and resolution will be copied
+ * @param resolution - override resolution of the renderTexture
+ * @param multisample - number of samples of the renderTexture
+ */
+ FilterSystem.prototype.getFilterTexture = function (input, resolution, multisample) {
+ if (typeof input === 'number') {
+ var swap = input;
+ input = resolution;
+ resolution = swap;
+ }
+ input = input || this.activeState.renderTexture;
+ var filterTexture = this.texturePool.getOptimalTexture(input.width, input.height, resolution || input.resolution, multisample || exports.MSAA_QUALITY.NONE);
+ filterTexture.filterFrame = input.filterFrame;
+ return filterTexture;
+ };
+ /**
+ * Frees a render texture back into the pool.
+ * @param renderTexture - The renderTarget to free
+ */
+ FilterSystem.prototype.returnFilterTexture = function (renderTexture) {
+ this.texturePool.returnTexture(renderTexture);
+ };
+ /** Empties the texture pool. */
+ FilterSystem.prototype.emptyPool = function () {
+ this.texturePool.clear(true);
+ };
+ /** Calls `texturePool.resize()`, affects fullScreen renderTextures. */
+ FilterSystem.prototype.resize = function () {
+ this.texturePool.setScreenSize(this.renderer.view);
+ };
+ /**
+ * @param matrix - first param
+ * @param rect - second param
+ */
+ FilterSystem.prototype.transformAABB = function (matrix, rect) {
+ var lt = tempPoints[0];
+ var lb = tempPoints[1];
+ var rt = tempPoints[2];
+ var rb = tempPoints[3];
+ lt.set(rect.left, rect.top);
+ lb.set(rect.left, rect.bottom);
+ rt.set(rect.right, rect.top);
+ rb.set(rect.right, rect.bottom);
+ matrix.apply(lt, lt);
+ matrix.apply(lb, lb);
+ matrix.apply(rt, rt);
+ matrix.apply(rb, rb);
+ var x0 = Math.min(lt.x, lb.x, rt.x, rb.x);
+ var y0 = Math.min(lt.y, lb.y, rt.y, rb.y);
+ var x1 = Math.max(lt.x, lb.x, rt.x, rb.x);
+ var y1 = Math.max(lt.y, lb.y, rt.y, rb.y);
+ rect.x = x0;
+ rect.y = y0;
+ rect.width = x1 - x0;
+ rect.height = y1 - y0;
+ };
+ FilterSystem.prototype.roundFrame = function (frame, resolution, bindingSourceFrame, bindingDestinationFrame, transform) {
+ if (frame.width <= 0 || frame.height <= 0 || bindingSourceFrame.width <= 0 || bindingSourceFrame.height <= 0) {
+ return;
+ }
+ if (transform) {
+ var a = transform.a, b = transform.b, c = transform.c, d = transform.d;
+ // Skip if skew/rotation present in matrix, except for multiple of 90掳 rotation. If rotation
+ // is a multiple of 90掳, then either pair of (b,c) or (a,d) will be (0,0).
+ if ((Math.abs(b) > 1e-4 || Math.abs(c) > 1e-4)
+ && (Math.abs(a) > 1e-4 || Math.abs(d) > 1e-4)) {
+ return;
+ }
+ }
+ transform = transform ? tempMatrix$2.copyFrom(transform) : tempMatrix$2.identity();
+ // Get forward transform from world space to screen space
+ transform
+ .translate(-bindingSourceFrame.x, -bindingSourceFrame.y)
+ .scale(bindingDestinationFrame.width / bindingSourceFrame.width, bindingDestinationFrame.height / bindingSourceFrame.height)
+ .translate(bindingDestinationFrame.x, bindingDestinationFrame.y);
+ // Convert frame to screen space
+ this.transformAABB(transform, frame);
+ // Round frame in screen space
+ frame.ceil(resolution);
+ // Project back into world space.
+ this.transformAABB(transform.invert(), frame);
+ };
+ return FilterSystem;
+ }());
+
+ /**
+ * Base for a common object renderer that can be used as a
+ * system renderer plugin.
+ * @memberof PIXI
+ */
+ var ObjectRenderer = /** @class */ (function () {
+ /**
+ * @param renderer - The renderer this manager works for.
+ */
+ function ObjectRenderer(renderer) {
+ this.renderer = renderer;
+ }
+ /** Stub method that should be used to empty the current batch by rendering objects now. */
+ ObjectRenderer.prototype.flush = function () {
+ // flush!
+ };
+ /** Generic destruction method that frees all resources. This should be called by subclasses. */
+ ObjectRenderer.prototype.destroy = function () {
+ this.renderer = null;
+ };
+ /**
+ * Stub method that initializes any state required before
+ * rendering starts. It is different from the `prerender`
+ * signal, which occurs every frame, in that it is called
+ * whenever an object requests _this_ renderer specifically.
+ */
+ ObjectRenderer.prototype.start = function () {
+ // set the shader..
+ };
+ /** Stops the renderer. It should free up any state and become dormant. */
+ ObjectRenderer.prototype.stop = function () {
+ this.flush();
+ };
+ /**
+ * Keeps the object to render. It doesn't have to be
+ * rendered immediately.
+ * @param {PIXI.DisplayObject} _object - The object to render.
+ */
+ ObjectRenderer.prototype.render = function (_object) {
+ // render the object
+ };
+ return ObjectRenderer;
+ }());
+
+ /**
+ * System plugin to the renderer to manage batching.
+ * @memberof PIXI
+ */
+ var BatchSystem = /** @class */ (function () {
+ /**
+ * @param renderer - The renderer this System works for.
+ */
+ function BatchSystem(renderer) {
+ this.renderer = renderer;
+ this.emptyRenderer = new ObjectRenderer(renderer);
+ this.currentRenderer = this.emptyRenderer;
+ }
+ /**
+ * Changes the current renderer to the one given in parameter
+ * @param objectRenderer - The object renderer to use.
+ */
+ BatchSystem.prototype.setObjectRenderer = function (objectRenderer) {
+ if (this.currentRenderer === objectRenderer) {
+ return;
+ }
+ this.currentRenderer.stop();
+ this.currentRenderer = objectRenderer;
+ this.currentRenderer.start();
+ };
+ /**
+ * This should be called if you wish to do some custom rendering
+ * It will basically render anything that may be batched up such as sprites
+ */
+ BatchSystem.prototype.flush = function () {
+ this.setObjectRenderer(this.emptyRenderer);
+ };
+ /** Reset the system to an empty renderer */
+ BatchSystem.prototype.reset = function () {
+ this.setObjectRenderer(this.emptyRenderer);
+ };
+ /**
+ * Handy function for batch renderers: copies bound textures in first maxTextures locations to array
+ * sets actual _batchLocation for them
+ * @param arr - arr copy destination
+ * @param maxTextures - number of copied elements
+ */
+ BatchSystem.prototype.copyBoundTextures = function (arr, maxTextures) {
+ var boundTextures = this.renderer.texture.boundTextures;
+ for (var i = maxTextures - 1; i >= 0; --i) {
+ arr[i] = boundTextures[i] || null;
+ if (arr[i]) {
+ arr[i]._batchLocation = i;
+ }
+ }
+ };
+ /**
+ * Assigns batch locations to textures in array based on boundTextures state.
+ * All textures in texArray should have `_batchEnabled = _batchId`,
+ * and their count should be less than `maxTextures`.
+ * @param texArray - textures to bound
+ * @param boundTextures - current state of bound textures
+ * @param batchId - marker for _batchEnabled param of textures in texArray
+ * @param maxTextures - number of texture locations to manipulate
+ */
+ BatchSystem.prototype.boundArray = function (texArray, boundTextures, batchId, maxTextures) {
+ var elements = texArray.elements, ids = texArray.ids, count = texArray.count;
+ var j = 0;
+ for (var i = 0; i < count; i++) {
+ var tex = elements[i];
+ var loc = tex._batchLocation;
+ if (loc >= 0 && loc < maxTextures
+ && boundTextures[loc] === tex) {
+ ids[i] = loc;
+ continue;
+ }
+ while (j < maxTextures) {
+ var bound = boundTextures[j];
+ if (bound && bound._batchEnabled === batchId
+ && bound._batchLocation === j) {
+ j++;
+ continue;
+ }
+ ids[i] = j;
+ tex._batchLocation = j;
+ boundTextures[j] = tex;
+ break;
+ }
+ }
+ };
+ /**
+ * @ignore
+ */
+ BatchSystem.prototype.destroy = function () {
+ this.renderer = null;
+ };
+ return BatchSystem;
+ }());
+
+ var CONTEXT_UID_COUNTER = 0;
+ /**
+ * System plugin to the renderer to manage the context.
+ * @memberof PIXI
+ */
+ var ContextSystem = /** @class */ (function () {
+ /** @param renderer - The renderer this System works for. */
+ function ContextSystem(renderer) {
+ this.renderer = renderer;
+ this.webGLVersion = 1;
+ this.extensions = {};
+ this.supports = {
+ uint32Indices: false,
+ };
+ // Bind functions
+ this.handleContextLost = this.handleContextLost.bind(this);
+ this.handleContextRestored = this.handleContextRestored.bind(this);
+ renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false);
+ renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false);
+ }
+ Object.defineProperty(ContextSystem.prototype, "isLost", {
+ /**
+ * `true` if the context is lost
+ * @readonly
+ */
+ get: function () {
+ return (!this.gl || this.gl.isContextLost());
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Handles the context change event.
+ * @param {WebGLRenderingContext} gl - New WebGL context.
+ */
+ ContextSystem.prototype.contextChange = function (gl) {
+ this.gl = gl;
+ this.renderer.gl = gl;
+ this.renderer.CONTEXT_UID = CONTEXT_UID_COUNTER++;
+ // restore a context if it was previously lost
+ if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) {
+ gl.getExtension('WEBGL_lose_context').restoreContext();
+ }
+ };
+ /**
+ * Initializes the context.
+ * @protected
+ * @param {WebGLRenderingContext} gl - WebGL context
+ */
+ ContextSystem.prototype.initFromContext = function (gl) {
+ this.gl = gl;
+ this.validateContext(gl);
+ this.renderer.gl = gl;
+ this.renderer.CONTEXT_UID = CONTEXT_UID_COUNTER++;
+ this.renderer.runners.contextChange.emit(gl);
+ };
+ /**
+ * Initialize from context options
+ * @protected
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext
+ * @param {object} options - context attributes
+ */
+ ContextSystem.prototype.initFromOptions = function (options) {
+ var gl = this.createContext(this.renderer.view, options);
+ this.initFromContext(gl);
+ };
+ /**
+ * Helper class to create a WebGL Context
+ * @param canvas - the canvas element that we will get the context from
+ * @param options - An options object that gets passed in to the canvas element containing the
+ * context attributes
+ * @see https://developer.mozilla.org/en/docs/Web/API/HTMLCanvasElement/getContext
+ * @returns {WebGLRenderingContext} the WebGL context
+ */
+ ContextSystem.prototype.createContext = function (canvas, options) {
+ var gl;
+ if (settings$1.PREFER_ENV >= exports.ENV.WEBGL2) {
+ gl = canvas.getContext('webgl2', options);
+ }
+ if (gl) {
+ this.webGLVersion = 2;
+ }
+ else {
+ this.webGLVersion = 1;
+ gl = canvas.getContext('webgl', options) || canvas.getContext('experimental-webgl', options);
+ if (!gl) {
+ // fail, not able to get a context
+ throw new Error('This browser does not support WebGL. Try using the canvas renderer');
+ }
+ }
+ this.gl = gl;
+ this.getExtensions();
+ return this.gl;
+ };
+ /** Auto-populate the {@link PIXI.ContextSystem.extensions extensions}. */
+ ContextSystem.prototype.getExtensions = function () {
+ // time to set up default extensions that Pixi uses.
+ var gl = this.gl;
+ var common = {
+ anisotropicFiltering: gl.getExtension('EXT_texture_filter_anisotropic'),
+ floatTextureLinear: gl.getExtension('OES_texture_float_linear'),
+ s3tc: gl.getExtension('WEBGL_compressed_texture_s3tc'),
+ s3tc_sRGB: gl.getExtension('WEBGL_compressed_texture_s3tc_srgb'),
+ etc: gl.getExtension('WEBGL_compressed_texture_etc'),
+ etc1: gl.getExtension('WEBGL_compressed_texture_etc1'),
+ pvrtc: gl.getExtension('WEBGL_compressed_texture_pvrtc')
+ || gl.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc'),
+ atc: gl.getExtension('WEBGL_compressed_texture_atc'),
+ astc: gl.getExtension('WEBGL_compressed_texture_astc')
+ };
+ if (this.webGLVersion === 1) {
+ Object.assign(this.extensions, common, {
+ drawBuffers: gl.getExtension('WEBGL_draw_buffers'),
+ depthTexture: gl.getExtension('WEBGL_depth_texture'),
+ loseContext: gl.getExtension('WEBGL_lose_context'),
+ vertexArrayObject: gl.getExtension('OES_vertex_array_object')
+ || gl.getExtension('MOZ_OES_vertex_array_object')
+ || gl.getExtension('WEBKIT_OES_vertex_array_object'),
+ uint32ElementIndex: gl.getExtension('OES_element_index_uint'),
+ // Floats and half-floats
+ floatTexture: gl.getExtension('OES_texture_float'),
+ floatTextureLinear: gl.getExtension('OES_texture_float_linear'),
+ textureHalfFloat: gl.getExtension('OES_texture_half_float'),
+ textureHalfFloatLinear: gl.getExtension('OES_texture_half_float_linear'),
+ });
+ }
+ else if (this.webGLVersion === 2) {
+ Object.assign(this.extensions, common, {
+ // Floats and half-floats
+ colorBufferFloat: gl.getExtension('EXT_color_buffer_float')
+ });
+ }
+ };
+ /**
+ * Handles a lost webgl context
+ * @param {WebGLContextEvent} event - The context lost event.
+ */
+ ContextSystem.prototype.handleContextLost = function (event) {
+ event.preventDefault();
+ };
+ /** Handles a restored webgl context. */
+ ContextSystem.prototype.handleContextRestored = function () {
+ this.renderer.runners.contextChange.emit(this.gl);
+ };
+ ContextSystem.prototype.destroy = function () {
+ var view = this.renderer.view;
+ this.renderer = null;
+ // remove listeners
+ view.removeEventListener('webglcontextlost', this.handleContextLost);
+ view.removeEventListener('webglcontextrestored', this.handleContextRestored);
+ this.gl.useProgram(null);
+ if (this.extensions.loseContext) {
+ this.extensions.loseContext.loseContext();
+ }
+ };
+ /** Handle the post-render runner event. */
+ ContextSystem.prototype.postrender = function () {
+ if (this.renderer.renderingToScreen) {
+ this.gl.flush();
+ }
+ };
+ /**
+ * Validate context.
+ * @param {WebGLRenderingContext} gl - Render context.
+ */
+ ContextSystem.prototype.validateContext = function (gl) {
+ var attributes = gl.getContextAttributes();
+ var isWebGl2 = 'WebGL2RenderingContext' in globalThis && gl instanceof globalThis.WebGL2RenderingContext;
+ if (isWebGl2) {
+ this.webGLVersion = 2;
+ }
+ // this is going to be fairly simple for now.. but at least we have room to grow!
+ if (attributes && !attributes.stencil) {
+ /* eslint-disable max-len, no-console */
+ console.warn('Provided WebGL context does not have a stencil buffer, masks may not render correctly');
+ /* eslint-enable max-len, no-console */
+ }
+ var hasuint32 = isWebGl2 || !!gl.getExtension('OES_element_index_uint');
+ this.supports.uint32Indices = hasuint32;
+ if (!hasuint32) {
+ /* eslint-disable max-len, no-console */
+ console.warn('Provided WebGL context does not support 32 index buffer, complex graphics may not render correctly');
+ /* eslint-enable max-len, no-console */
+ }
+ };
+ return ContextSystem;
+ }());
+
+ /**
+ * Internal framebuffer for WebGL context.
+ * @memberof PIXI
+ */
+ var GLFramebuffer = /** @class */ (function () {
+ function GLFramebuffer(framebuffer) {
+ this.framebuffer = framebuffer;
+ this.stencil = null;
+ this.dirtyId = -1;
+ this.dirtyFormat = -1;
+ this.dirtySize = -1;
+ this.multisample = exports.MSAA_QUALITY.NONE;
+ this.msaaBuffer = null;
+ this.blitFramebuffer = null;
+ this.mipLevel = 0;
+ }
+ return GLFramebuffer;
+ }());
+
+ var tempRectangle = new Rectangle();
+ /**
+ * System plugin to the renderer to manage framebuffers.
+ * @memberof PIXI
+ */
+ var FramebufferSystem = /** @class */ (function () {
+ /**
+ * @param renderer - The renderer this System works for.
+ */
+ function FramebufferSystem(renderer) {
+ this.renderer = renderer;
+ this.managedFramebuffers = [];
+ this.unknownFramebuffer = new Framebuffer(10, 10);
+ this.msaaSamples = null;
+ }
+ /** Sets up the renderer context and necessary buffers. */
+ FramebufferSystem.prototype.contextChange = function () {
+ var gl = this.gl = this.renderer.gl;
+ this.CONTEXT_UID = this.renderer.CONTEXT_UID;
+ this.current = this.unknownFramebuffer;
+ this.viewport = new Rectangle();
+ this.hasMRT = true;
+ this.writeDepthTexture = true;
+ this.disposeAll(true);
+ // webgl2
+ if (this.renderer.context.webGLVersion === 1) {
+ // webgl 1!
+ var nativeDrawBuffersExtension_1 = this.renderer.context.extensions.drawBuffers;
+ var nativeDepthTextureExtension = this.renderer.context.extensions.depthTexture;
+ if (settings$1.PREFER_ENV === exports.ENV.WEBGL_LEGACY) {
+ nativeDrawBuffersExtension_1 = null;
+ nativeDepthTextureExtension = null;
+ }
+ if (nativeDrawBuffersExtension_1) {
+ gl.drawBuffers = function (activeTextures) {
+ return nativeDrawBuffersExtension_1.drawBuffersWEBGL(activeTextures);
+ };
+ }
+ else {
+ this.hasMRT = false;
+ gl.drawBuffers = function () {
+ // empty
+ };
+ }
+ if (!nativeDepthTextureExtension) {
+ this.writeDepthTexture = false;
+ }
+ }
+ else {
+ // WebGL2
+ // cache possible MSAA samples
+ this.msaaSamples = gl.getInternalformatParameter(gl.RENDERBUFFER, gl.RGBA8, gl.SAMPLES);
+ }
+ };
+ /**
+ * Bind a framebuffer.
+ * @param framebuffer
+ * @param frame - frame, default is framebuffer size
+ * @param mipLevel - optional mip level to set on the framebuffer - defaults to 0
+ */
+ FramebufferSystem.prototype.bind = function (framebuffer, frame, mipLevel) {
+ if (mipLevel === void 0) { mipLevel = 0; }
+ var gl = this.gl;
+ if (framebuffer) {
+ // TODO caching layer!
+ var fbo = framebuffer.glFramebuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer);
+ if (this.current !== framebuffer) {
+ this.current = framebuffer;
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer);
+ }
+ // make sure all textures are unbound..
+ if (fbo.mipLevel !== mipLevel) {
+ framebuffer.dirtyId++;
+ framebuffer.dirtyFormat++;
+ fbo.mipLevel = mipLevel;
+ }
+ // now check for updates...
+ if (fbo.dirtyId !== framebuffer.dirtyId) {
+ fbo.dirtyId = framebuffer.dirtyId;
+ if (fbo.dirtyFormat !== framebuffer.dirtyFormat) {
+ fbo.dirtyFormat = framebuffer.dirtyFormat;
+ fbo.dirtySize = framebuffer.dirtySize;
+ this.updateFramebuffer(framebuffer, mipLevel);
+ }
+ else if (fbo.dirtySize !== framebuffer.dirtySize) {
+ fbo.dirtySize = framebuffer.dirtySize;
+ this.resizeFramebuffer(framebuffer);
+ }
+ }
+ for (var i = 0; i < framebuffer.colorTextures.length; i++) {
+ var tex = framebuffer.colorTextures[i];
+ this.renderer.texture.unbind(tex.parentTextureArray || tex);
+ }
+ if (framebuffer.depthTexture) {
+ this.renderer.texture.unbind(framebuffer.depthTexture);
+ }
+ if (frame) {
+ var mipWidth = (frame.width >> mipLevel);
+ var mipHeight = (frame.height >> mipLevel);
+ var scale = mipWidth / frame.width;
+ this.setViewport(frame.x * scale, frame.y * scale, mipWidth, mipHeight);
+ }
+ else {
+ var mipWidth = (framebuffer.width >> mipLevel);
+ var mipHeight = (framebuffer.height >> mipLevel);
+ this.setViewport(0, 0, mipWidth, mipHeight);
+ }
+ }
+ else {
+ if (this.current) {
+ this.current = null;
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ }
+ if (frame) {
+ this.setViewport(frame.x, frame.y, frame.width, frame.height);
+ }
+ else {
+ this.setViewport(0, 0, this.renderer.width, this.renderer.height);
+ }
+ }
+ };
+ /**
+ * Set the WebGLRenderingContext's viewport.
+ * @param x - X position of viewport
+ * @param y - Y position of viewport
+ * @param width - Width of viewport
+ * @param height - Height of viewport
+ */
+ FramebufferSystem.prototype.setViewport = function (x, y, width, height) {
+ var v = this.viewport;
+ x = Math.round(x);
+ y = Math.round(y);
+ width = Math.round(width);
+ height = Math.round(height);
+ if (v.width !== width || v.height !== height || v.x !== x || v.y !== y) {
+ v.x = x;
+ v.y = y;
+ v.width = width;
+ v.height = height;
+ this.gl.viewport(x, y, width, height);
+ }
+ };
+ Object.defineProperty(FramebufferSystem.prototype, "size", {
+ /**
+ * Get the size of the current width and height. Returns object with `width` and `height` values.
+ * @readonly
+ */
+ get: function () {
+ if (this.current) {
+ // TODO store temp
+ return { x: 0, y: 0, width: this.current.width, height: this.current.height };
+ }
+ return { x: 0, y: 0, width: this.renderer.width, height: this.renderer.height };
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Clear the color of the context
+ * @param r - Red value from 0 to 1
+ * @param g - Green value from 0 to 1
+ * @param b - Blue value from 0 to 1
+ * @param a - Alpha value from 0 to 1
+ * @param {PIXI.BUFFER_BITS} [mask=BUFFER_BITS.COLOR | BUFFER_BITS.DEPTH] - Bitwise OR of masks
+ * that indicate the buffers to be cleared, by default COLOR and DEPTH buffers.
+ */
+ FramebufferSystem.prototype.clear = function (r, g, b, a, mask) {
+ if (mask === void 0) { mask = exports.BUFFER_BITS.COLOR | exports.BUFFER_BITS.DEPTH; }
+ var gl = this.gl;
+ // TODO clear color can be set only one right?
+ gl.clearColor(r, g, b, a);
+ gl.clear(mask);
+ };
+ /**
+ * Initialize framebuffer for this context
+ * @protected
+ * @param framebuffer
+ * @returns - created GLFramebuffer
+ */
+ FramebufferSystem.prototype.initFramebuffer = function (framebuffer) {
+ var gl = this.gl;
+ var fbo = new GLFramebuffer(gl.createFramebuffer());
+ fbo.multisample = this.detectSamples(framebuffer.multisample);
+ framebuffer.glFramebuffers[this.CONTEXT_UID] = fbo;
+ this.managedFramebuffers.push(framebuffer);
+ framebuffer.disposeRunner.add(this);
+ return fbo;
+ };
+ /**
+ * Resize the framebuffer
+ * @param framebuffer
+ * @protected
+ */
+ FramebufferSystem.prototype.resizeFramebuffer = function (framebuffer) {
+ var gl = this.gl;
+ var fbo = framebuffer.glFramebuffers[this.CONTEXT_UID];
+ if (fbo.msaaBuffer) {
+ gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.msaaBuffer);
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, fbo.multisample, gl.RGBA8, framebuffer.width, framebuffer.height);
+ }
+ if (fbo.stencil) {
+ gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil);
+ if (fbo.msaaBuffer) {
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, fbo.multisample, gl.DEPTH24_STENCIL8, framebuffer.width, framebuffer.height);
+ }
+ else {
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height);
+ }
+ }
+ var colorTextures = framebuffer.colorTextures;
+ var count = colorTextures.length;
+ if (!gl.drawBuffers) {
+ count = Math.min(count, 1);
+ }
+ for (var i = 0; i < count; i++) {
+ var texture = colorTextures[i];
+ var parentTexture = texture.parentTextureArray || texture;
+ this.renderer.texture.bind(parentTexture, 0);
+ }
+ if (framebuffer.depthTexture && this.writeDepthTexture) {
+ this.renderer.texture.bind(framebuffer.depthTexture, 0);
+ }
+ };
+ /**
+ * Update the framebuffer
+ * @param framebuffer
+ * @param mipLevel
+ * @protected
+ */
+ FramebufferSystem.prototype.updateFramebuffer = function (framebuffer, mipLevel) {
+ var gl = this.gl;
+ var fbo = framebuffer.glFramebuffers[this.CONTEXT_UID];
+ // bind the color texture
+ var colorTextures = framebuffer.colorTextures;
+ var count = colorTextures.length;
+ if (!gl.drawBuffers) {
+ count = Math.min(count, 1);
+ }
+ if (fbo.multisample > 1 && this.canMultisampleFramebuffer(framebuffer)) {
+ fbo.msaaBuffer = fbo.msaaBuffer || gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.msaaBuffer);
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, fbo.multisample, gl.RGBA8, framebuffer.width, framebuffer.height);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, fbo.msaaBuffer);
+ }
+ else if (fbo.msaaBuffer) {
+ gl.deleteRenderbuffer(fbo.msaaBuffer);
+ fbo.msaaBuffer = null;
+ if (fbo.blitFramebuffer) {
+ fbo.blitFramebuffer.dispose();
+ fbo.blitFramebuffer = null;
+ }
+ }
+ var activeTextures = [];
+ for (var i = 0; i < count; i++) {
+ var texture = colorTextures[i];
+ var parentTexture = texture.parentTextureArray || texture;
+ this.renderer.texture.bind(parentTexture, 0);
+ if (i === 0 && fbo.msaaBuffer) {
+ continue;
+ }
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, texture.target, parentTexture._glTextures[this.CONTEXT_UID].texture, mipLevel);
+ activeTextures.push(gl.COLOR_ATTACHMENT0 + i);
+ }
+ if (activeTextures.length > 1) {
+ gl.drawBuffers(activeTextures);
+ }
+ if (framebuffer.depthTexture) {
+ var writeDepthTexture = this.writeDepthTexture;
+ if (writeDepthTexture) {
+ var depthTexture = framebuffer.depthTexture;
+ this.renderer.texture.bind(depthTexture, 0);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTexture._glTextures[this.CONTEXT_UID].texture, mipLevel);
+ }
+ }
+ if ((framebuffer.stencil || framebuffer.depth) && !(framebuffer.depthTexture && this.writeDepthTexture)) {
+ fbo.stencil = fbo.stencil || gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil);
+ if (fbo.msaaBuffer) {
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, fbo.multisample, gl.DEPTH24_STENCIL8, framebuffer.width, framebuffer.height);
+ }
+ else {
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height);
+ }
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil);
+ }
+ else if (fbo.stencil) {
+ gl.deleteRenderbuffer(fbo.stencil);
+ fbo.stencil = null;
+ }
+ };
+ /**
+ * Returns true if the frame buffer can be multisampled.
+ * @param framebuffer
+ */
+ FramebufferSystem.prototype.canMultisampleFramebuffer = function (framebuffer) {
+ return this.renderer.context.webGLVersion !== 1
+ && framebuffer.colorTextures.length <= 1 && !framebuffer.depthTexture;
+ };
+ /**
+ * Detects number of samples that is not more than a param but as close to it as possible
+ * @param samples - number of samples
+ * @returns - recommended number of samples
+ */
+ FramebufferSystem.prototype.detectSamples = function (samples) {
+ var msaaSamples = this.msaaSamples;
+ var res = exports.MSAA_QUALITY.NONE;
+ if (samples <= 1 || msaaSamples === null) {
+ return res;
+ }
+ for (var i = 0; i < msaaSamples.length; i++) {
+ if (msaaSamples[i] <= samples) {
+ res = msaaSamples[i];
+ break;
+ }
+ }
+ if (res === 1) {
+ res = exports.MSAA_QUALITY.NONE;
+ }
+ return res;
+ };
+ /**
+ * Only works with WebGL2
+ *
+ * blits framebuffer to another of the same or bigger size
+ * after that target framebuffer is bound
+ *
+ * Fails with WebGL warning if blits multisample framebuffer to different size
+ * @param framebuffer - by default it blits "into itself", from renderBuffer to texture.
+ * @param sourcePixels - source rectangle in pixels
+ * @param destPixels - dest rectangle in pixels, assumed to be the same as sourcePixels
+ */
+ FramebufferSystem.prototype.blit = function (framebuffer, sourcePixels, destPixels) {
+ var _a = this, current = _a.current, renderer = _a.renderer, gl = _a.gl, CONTEXT_UID = _a.CONTEXT_UID;
+ if (renderer.context.webGLVersion !== 2) {
+ return;
+ }
+ if (!current) {
+ return;
+ }
+ var fbo = current.glFramebuffers[CONTEXT_UID];
+ if (!fbo) {
+ return;
+ }
+ if (!framebuffer) {
+ if (!fbo.msaaBuffer) {
+ return;
+ }
+ var colorTexture = current.colorTextures[0];
+ if (!colorTexture) {
+ return;
+ }
+ if (!fbo.blitFramebuffer) {
+ fbo.blitFramebuffer = new Framebuffer(current.width, current.height);
+ fbo.blitFramebuffer.addColorTexture(0, colorTexture);
+ }
+ framebuffer = fbo.blitFramebuffer;
+ if (framebuffer.colorTextures[0] !== colorTexture) {
+ framebuffer.colorTextures[0] = colorTexture;
+ framebuffer.dirtyId++;
+ framebuffer.dirtyFormat++;
+ }
+ if (framebuffer.width !== current.width || framebuffer.height !== current.height) {
+ framebuffer.width = current.width;
+ framebuffer.height = current.height;
+ framebuffer.dirtyId++;
+ framebuffer.dirtySize++;
+ }
+ }
+ if (!sourcePixels) {
+ sourcePixels = tempRectangle;
+ sourcePixels.width = current.width;
+ sourcePixels.height = current.height;
+ }
+ if (!destPixels) {
+ destPixels = sourcePixels;
+ }
+ var sameSize = sourcePixels.width === destPixels.width && sourcePixels.height === destPixels.height;
+ this.bind(framebuffer);
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo.framebuffer);
+ gl.blitFramebuffer(sourcePixels.left, sourcePixels.top, sourcePixels.right, sourcePixels.bottom, destPixels.left, destPixels.top, destPixels.right, destPixels.bottom, gl.COLOR_BUFFER_BIT, sameSize ? gl.NEAREST : gl.LINEAR);
+ };
+ /**
+ * Disposes framebuffer.
+ * @param framebuffer - framebuffer that has to be disposed of
+ * @param contextLost - If context was lost, we suppress all delete function calls
+ */
+ FramebufferSystem.prototype.disposeFramebuffer = function (framebuffer, contextLost) {
+ var fbo = framebuffer.glFramebuffers[this.CONTEXT_UID];
+ var gl = this.gl;
+ if (!fbo) {
+ return;
+ }
+ delete framebuffer.glFramebuffers[this.CONTEXT_UID];
+ var index = this.managedFramebuffers.indexOf(framebuffer);
+ if (index >= 0) {
+ this.managedFramebuffers.splice(index, 1);
+ }
+ framebuffer.disposeRunner.remove(this);
+ if (!contextLost) {
+ gl.deleteFramebuffer(fbo.framebuffer);
+ if (fbo.msaaBuffer) {
+ gl.deleteRenderbuffer(fbo.msaaBuffer);
+ }
+ if (fbo.stencil) {
+ gl.deleteRenderbuffer(fbo.stencil);
+ }
+ }
+ if (fbo.blitFramebuffer) {
+ fbo.blitFramebuffer.dispose();
+ }
+ };
+ /**
+ * Disposes all framebuffers, but not textures bound to them.
+ * @param [contextLost=false] - If context was lost, we suppress all delete function calls
+ */
+ FramebufferSystem.prototype.disposeAll = function (contextLost) {
+ var list = this.managedFramebuffers;
+ this.managedFramebuffers = [];
+ for (var i = 0; i < list.length; i++) {
+ this.disposeFramebuffer(list[i], contextLost);
+ }
+ };
+ /**
+ * Forcing creation of stencil buffer for current framebuffer, if it wasn't done before.
+ * Used by MaskSystem, when its time to use stencil mask for Graphics element.
+ *
+ * Its an alternative for public lazy `framebuffer.enableStencil`, in case we need stencil without rebind.
+ * @private
+ */
+ FramebufferSystem.prototype.forceStencil = function () {
+ var framebuffer = this.current;
+ if (!framebuffer) {
+ return;
+ }
+ var fbo = framebuffer.glFramebuffers[this.CONTEXT_UID];
+ if (!fbo || fbo.stencil) {
+ return;
+ }
+ framebuffer.stencil = true;
+ var w = framebuffer.width;
+ var h = framebuffer.height;
+ var gl = this.gl;
+ var stencil = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, stencil);
+ if (fbo.msaaBuffer) {
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, fbo.multisample, gl.DEPTH24_STENCIL8, w, h);
+ }
+ else {
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, w, h);
+ }
+ fbo.stencil = stencil;
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, stencil);
+ };
+ /** Resets framebuffer stored state, binds screen framebuffer. Should be called before renderTexture reset(). */
+ FramebufferSystem.prototype.reset = function () {
+ this.current = this.unknownFramebuffer;
+ this.viewport = new Rectangle();
+ };
+ FramebufferSystem.prototype.destroy = function () {
+ this.renderer = null;
+ };
+ return FramebufferSystem;
+ }());
+
+ var byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
+ /**
+ * System plugin to the renderer to manage geometry.
+ * @memberof PIXI
+ */
+ var GeometrySystem = /** @class */ (function () {
+ /** @param renderer - The renderer this System works for. */
+ function GeometrySystem(renderer) {
+ this.renderer = renderer;
+ this._activeGeometry = null;
+ this._activeVao = null;
+ this.hasVao = true;
+ this.hasInstance = true;
+ this.canUseUInt32ElementIndex = false;
+ this.managedGeometries = {};
+ }
+ /** Sets up the renderer context and necessary buffers. */
+ GeometrySystem.prototype.contextChange = function () {
+ this.disposeAll(true);
+ var gl = this.gl = this.renderer.gl;
+ var context = this.renderer.context;
+ this.CONTEXT_UID = this.renderer.CONTEXT_UID;
+ // webgl2
+ if (context.webGLVersion !== 2) {
+ // webgl 1!
+ var nativeVaoExtension_1 = this.renderer.context.extensions.vertexArrayObject;
+ if (settings$1.PREFER_ENV === exports.ENV.WEBGL_LEGACY) {
+ nativeVaoExtension_1 = null;
+ }
+ if (nativeVaoExtension_1) {
+ gl.createVertexArray = function () {
+ return nativeVaoExtension_1.createVertexArrayOES();
+ };
+ gl.bindVertexArray = function (vao) {
+ return nativeVaoExtension_1.bindVertexArrayOES(vao);
+ };
+ gl.deleteVertexArray = function (vao) {
+ return nativeVaoExtension_1.deleteVertexArrayOES(vao);
+ };
+ }
+ else {
+ this.hasVao = false;
+ gl.createVertexArray = function () {
+ return null;
+ };
+ gl.bindVertexArray = function () {
+ return null;
+ };
+ gl.deleteVertexArray = function () {
+ return null;
+ };
+ }
+ }
+ if (context.webGLVersion !== 2) {
+ var instanceExt_1 = gl.getExtension('ANGLE_instanced_arrays');
+ if (instanceExt_1) {
+ gl.vertexAttribDivisor = function (a, b) {
+ return instanceExt_1.vertexAttribDivisorANGLE(a, b);
+ };
+ gl.drawElementsInstanced = function (a, b, c, d, e) {
+ return instanceExt_1.drawElementsInstancedANGLE(a, b, c, d, e);
+ };
+ gl.drawArraysInstanced = function (a, b, c, d) {
+ return instanceExt_1.drawArraysInstancedANGLE(a, b, c, d);
+ };
+ }
+ else {
+ this.hasInstance = false;
+ }
+ }
+ this.canUseUInt32ElementIndex = context.webGLVersion === 2 || !!context.extensions.uint32ElementIndex;
+ };
+ /**
+ * Binds geometry so that is can be drawn. Creating a Vao if required
+ * @param geometry - Instance of geometry to bind.
+ * @param shader - Instance of shader to use vao for.
+ */
+ GeometrySystem.prototype.bind = function (geometry, shader) {
+ shader = shader || this.renderer.shader.shader;
+ var gl = this.gl;
+ // not sure the best way to address this..
+ // currently different shaders require different VAOs for the same geometry
+ // Still mulling over the best way to solve this one..
+ // will likely need to modify the shader attribute locations at run time!
+ var vaos = geometry.glVertexArrayObjects[this.CONTEXT_UID];
+ var incRefCount = false;
+ if (!vaos) {
+ this.managedGeometries[geometry.id] = geometry;
+ geometry.disposeRunner.add(this);
+ geometry.glVertexArrayObjects[this.CONTEXT_UID] = vaos = {};
+ incRefCount = true;
+ }
+ var vao = vaos[shader.program.id] || this.initGeometryVao(geometry, shader, incRefCount);
+ this._activeGeometry = geometry;
+ if (this._activeVao !== vao) {
+ this._activeVao = vao;
+ if (this.hasVao) {
+ gl.bindVertexArray(vao);
+ }
+ else {
+ this.activateVao(geometry, shader.program);
+ }
+ }
+ // TODO - optimise later!
+ // don't need to loop through if nothing changed!
+ // maybe look to add an 'autoupdate' to geometry?
+ this.updateBuffers();
+ };
+ /** Reset and unbind any active VAO and geometry. */
+ GeometrySystem.prototype.reset = function () {
+ this.unbind();
+ };
+ /** Update buffers of the currently bound geometry. */
+ GeometrySystem.prototype.updateBuffers = function () {
+ var geometry = this._activeGeometry;
+ var bufferSystem = this.renderer.buffer;
+ for (var i = 0; i < geometry.buffers.length; i++) {
+ var buffer = geometry.buffers[i];
+ bufferSystem.update(buffer);
+ }
+ };
+ /**
+ * Check compatibility between a geometry and a program
+ * @param geometry - Geometry instance.
+ * @param program - Program instance.
+ */
+ GeometrySystem.prototype.checkCompatibility = function (geometry, program) {
+ // geometry must have at least all the attributes that the shader requires.
+ var geometryAttributes = geometry.attributes;
+ var shaderAttributes = program.attributeData;
+ for (var j in shaderAttributes) {
+ if (!geometryAttributes[j]) {
+ throw new Error("shader and geometry incompatible, geometry missing the \"" + j + "\" attribute");
+ }
+ }
+ };
+ /**
+ * Takes a geometry and program and generates a unique signature for them.
+ * @param geometry - To get signature from.
+ * @param program - To test geometry against.
+ * @returns - Unique signature of the geometry and program
+ */
+ GeometrySystem.prototype.getSignature = function (geometry, program) {
+ var attribs = geometry.attributes;
+ var shaderAttributes = program.attributeData;
+ var strings = ['g', geometry.id];
+ for (var i in attribs) {
+ if (shaderAttributes[i]) {
+ strings.push(i, shaderAttributes[i].location);
+ }
+ }
+ return strings.join('-');
+ };
+ /**
+ * Creates or gets Vao with the same structure as the geometry and stores it on the geometry.
+ * If vao is created, it is bound automatically. We use a shader to infer what and how to set up the
+ * attribute locations.
+ * @param geometry - Instance of geometry to to generate Vao for.
+ * @param shader - Instance of the shader.
+ * @param incRefCount - Increment refCount of all geometry buffers.
+ */
+ GeometrySystem.prototype.initGeometryVao = function (geometry, shader, incRefCount) {
+ if (incRefCount === void 0) { incRefCount = true; }
+ var gl = this.gl;
+ var CONTEXT_UID = this.CONTEXT_UID;
+ var bufferSystem = this.renderer.buffer;
+ var program = shader.program;
+ if (!program.glPrograms[CONTEXT_UID]) {
+ this.renderer.shader.generateProgram(shader);
+ }
+ this.checkCompatibility(geometry, program);
+ var signature = this.getSignature(geometry, program);
+ var vaoObjectHash = geometry.glVertexArrayObjects[this.CONTEXT_UID];
+ var vao = vaoObjectHash[signature];
+ if (vao) {
+ // this will give us easy access to the vao
+ vaoObjectHash[program.id] = vao;
+ return vao;
+ }
+ var buffers = geometry.buffers;
+ var attributes = geometry.attributes;
+ var tempStride = {};
+ var tempStart = {};
+ for (var j in buffers) {
+ tempStride[j] = 0;
+ tempStart[j] = 0;
+ }
+ for (var j in attributes) {
+ if (!attributes[j].size && program.attributeData[j]) {
+ attributes[j].size = program.attributeData[j].size;
+ }
+ else if (!attributes[j].size) {
+ console.warn("PIXI Geometry attribute '" + j + "' size cannot be determined (likely the bound shader does not have the attribute)"); // eslint-disable-line
+ }
+ tempStride[attributes[j].buffer] += attributes[j].size * byteSizeMap[attributes[j].type];
+ }
+ for (var j in attributes) {
+ var attribute = attributes[j];
+ var attribSize = attribute.size;
+ if (attribute.stride === undefined) {
+ if (tempStride[attribute.buffer] === attribSize * byteSizeMap[attribute.type]) {
+ attribute.stride = 0;
+ }
+ else {
+ attribute.stride = tempStride[attribute.buffer];
+ }
+ }
+ if (attribute.start === undefined) {
+ attribute.start = tempStart[attribute.buffer];
+ tempStart[attribute.buffer] += attribSize * byteSizeMap[attribute.type];
+ }
+ }
+ vao = gl.createVertexArray();
+ gl.bindVertexArray(vao);
+ // first update - and create the buffers!
+ // only create a gl buffer if it actually gets
+ for (var i = 0; i < buffers.length; i++) {
+ var buffer = buffers[i];
+ bufferSystem.bind(buffer);
+ if (incRefCount) {
+ buffer._glBuffers[CONTEXT_UID].refCount++;
+ }
+ }
+ // TODO - maybe make this a data object?
+ // lets wait to see if we need to first!
+ this.activateVao(geometry, program);
+ this._activeVao = vao;
+ // add it to the cache!
+ vaoObjectHash[program.id] = vao;
+ vaoObjectHash[signature] = vao;
+ return vao;
+ };
+ /**
+ * Disposes geometry.
+ * @param geometry - Geometry with buffers. Only VAO will be disposed
+ * @param [contextLost=false] - If context was lost, we suppress deleteVertexArray
+ */
+ GeometrySystem.prototype.disposeGeometry = function (geometry, contextLost) {
+ var _a;
+ if (!this.managedGeometries[geometry.id]) {
+ return;
+ }
+ delete this.managedGeometries[geometry.id];
+ var vaos = geometry.glVertexArrayObjects[this.CONTEXT_UID];
+ var gl = this.gl;
+ var buffers = geometry.buffers;
+ var bufferSystem = (_a = this.renderer) === null || _a === void 0 ? void 0 : _a.buffer;
+ geometry.disposeRunner.remove(this);
+ if (!vaos) {
+ return;
+ }
+ // bufferSystem may have already been destroyed..
+ // if this is the case, there is no need to destroy the geometry buffers...
+ // they already have been!
+ if (bufferSystem) {
+ for (var i = 0; i < buffers.length; i++) {
+ var buf = buffers[i]._glBuffers[this.CONTEXT_UID];
+ // my be null as context may have changed right before the dispose is called
+ if (buf) {
+ buf.refCount--;
+ if (buf.refCount === 0 && !contextLost) {
+ bufferSystem.dispose(buffers[i], contextLost);
+ }
+ }
+ }
+ }
+ if (!contextLost) {
+ for (var vaoId in vaos) {
+ // delete only signatures, everything else are copies
+ if (vaoId[0] === 'g') {
+ var vao = vaos[vaoId];
+ if (this._activeVao === vao) {
+ this.unbind();
+ }
+ gl.deleteVertexArray(vao);
+ }
+ }
+ }
+ delete geometry.glVertexArrayObjects[this.CONTEXT_UID];
+ };
+ /**
+ * Dispose all WebGL resources of all managed geometries.
+ * @param [contextLost=false] - If context was lost, we suppress `gl.delete` calls
+ */
+ GeometrySystem.prototype.disposeAll = function (contextLost) {
+ var all = Object.keys(this.managedGeometries);
+ for (var i = 0; i < all.length; i++) {
+ this.disposeGeometry(this.managedGeometries[all[i]], contextLost);
+ }
+ };
+ /**
+ * Activate vertex array object.
+ * @param geometry - Geometry instance.
+ * @param program - Shader program instance.
+ */
+ GeometrySystem.prototype.activateVao = function (geometry, program) {
+ var gl = this.gl;
+ var CONTEXT_UID = this.CONTEXT_UID;
+ var bufferSystem = this.renderer.buffer;
+ var buffers = geometry.buffers;
+ var attributes = geometry.attributes;
+ if (geometry.indexBuffer) {
+ // first update the index buffer if we have one..
+ bufferSystem.bind(geometry.indexBuffer);
+ }
+ var lastBuffer = null;
+ // add a new one!
+ for (var j in attributes) {
+ var attribute = attributes[j];
+ var buffer = buffers[attribute.buffer];
+ var glBuffer = buffer._glBuffers[CONTEXT_UID];
+ if (program.attributeData[j]) {
+ if (lastBuffer !== glBuffer) {
+ bufferSystem.bind(buffer);
+ lastBuffer = glBuffer;
+ }
+ var location = program.attributeData[j].location;
+ // TODO introduce state again
+ // we can optimise this for older devices that have no VAOs
+ gl.enableVertexAttribArray(location);
+ gl.vertexAttribPointer(location, attribute.size, attribute.type || gl.FLOAT, attribute.normalized, attribute.stride, attribute.start);
+ if (attribute.instance) {
+ // TODO calculate instance count based of this...
+ if (this.hasInstance) {
+ gl.vertexAttribDivisor(location, 1);
+ }
+ else {
+ throw new Error('geometry error, GPU Instancing is not supported on this device');
+ }
+ }
+ }
+ }
+ };
+ /**
+ * Draws the currently bound geometry.
+ * @param type - The type primitive to render.
+ * @param size - The number of elements to be rendered. If not specified, all vertices after the
+ * starting vertex will be drawn.
+ * @param start - The starting vertex in the geometry to start drawing from. If not specified,
+ * drawing will start from the first vertex.
+ * @param instanceCount - The number of instances of the set of elements to execute. If not specified,
+ * all instances will be drawn.
+ */
+ GeometrySystem.prototype.draw = function (type, size, start, instanceCount) {
+ var gl = this.gl;
+ var geometry = this._activeGeometry;
+ // TODO.. this should not change so maybe cache the function?
+ if (geometry.indexBuffer) {
+ var byteSize = geometry.indexBuffer.data.BYTES_PER_ELEMENT;
+ var glType = byteSize === 2 ? gl.UNSIGNED_SHORT : gl.UNSIGNED_INT;
+ if (byteSize === 2 || (byteSize === 4 && this.canUseUInt32ElementIndex)) {
+ if (geometry.instanced) {
+ /* eslint-disable max-len */
+ gl.drawElementsInstanced(type, size || geometry.indexBuffer.data.length, glType, (start || 0) * byteSize, instanceCount || 1);
+ /* eslint-enable max-len */
+ }
+ else {
+ /* eslint-disable max-len */
+ gl.drawElements(type, size || geometry.indexBuffer.data.length, glType, (start || 0) * byteSize);
+ /* eslint-enable max-len */
+ }
+ }
+ else {
+ console.warn('unsupported index buffer type: uint32');
+ }
+ }
+ else if (geometry.instanced) {
+ // TODO need a better way to calculate size..
+ gl.drawArraysInstanced(type, start, size || geometry.getSize(), instanceCount || 1);
+ }
+ else {
+ gl.drawArrays(type, start, size || geometry.getSize());
+ }
+ return this;
+ };
+ /** Unbind/reset everything. */
+ GeometrySystem.prototype.unbind = function () {
+ this.gl.bindVertexArray(null);
+ this._activeVao = null;
+ this._activeGeometry = null;
+ };
+ GeometrySystem.prototype.destroy = function () {
+ this.renderer = null;
+ };
+ return GeometrySystem;
+ }());
+
+ /**
+ * Component for masked elements.
+ *
+ * Holds mask mode and temporary data about current mask.
+ * @memberof PIXI
+ */
+ var MaskData = /** @class */ (function () {
+ /**
+ * Create MaskData
+ * @param {PIXI.DisplayObject} [maskObject=null] - object that describes the mask
+ */
+ function MaskData(maskObject) {
+ if (maskObject === void 0) { maskObject = null; }
+ this.type = exports.MASK_TYPES.NONE;
+ this.autoDetect = true;
+ this.maskObject = maskObject || null;
+ this.pooled = false;
+ this.isMaskData = true;
+ this.resolution = null;
+ this.multisample = settings$1.FILTER_MULTISAMPLE;
+ this.enabled = true;
+ this.colorMask = 0xf;
+ this._filters = null;
+ this._stencilCounter = 0;
+ this._scissorCounter = 0;
+ this._scissorRect = null;
+ this._scissorRectLocal = null;
+ this._colorMask = 0xf;
+ this._target = null;
+ }
+ Object.defineProperty(MaskData.prototype, "filter", {
+ /**
+ * The sprite mask filter.
+ * If set to `null`, the default sprite mask filter is used.
+ * @default null
+ */
+ get: function () {
+ return this._filters ? this._filters[0] : null;
+ },
+ set: function (value) {
+ if (value) {
+ if (this._filters) {
+ this._filters[0] = value;
+ }
+ else {
+ this._filters = [value];
+ }
+ }
+ else {
+ this._filters = null;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /** Resets the mask data after popMask(). */
+ MaskData.prototype.reset = function () {
+ if (this.pooled) {
+ this.maskObject = null;
+ this.type = exports.MASK_TYPES.NONE;
+ this.autoDetect = true;
+ }
+ this._target = null;
+ this._scissorRectLocal = null;
+ };
+ /**
+ * Copies counters from maskData above, called from pushMask().
+ * @param maskAbove
+ */
+ MaskData.prototype.copyCountersOrReset = function (maskAbove) {
+ if (maskAbove) {
+ this._stencilCounter = maskAbove._stencilCounter;
+ this._scissorCounter = maskAbove._scissorCounter;
+ this._scissorRect = maskAbove._scissorRect;
+ }
+ else {
+ this._stencilCounter = 0;
+ this._scissorCounter = 0;
+ this._scissorRect = null;
+ }
+ };
+ return MaskData;
+ }());
+
+ /**
+ * @private
+ * @param {WebGLRenderingContext} gl - The current WebGL context {WebGLProgram}
+ * @param {number} type - the type, can be either VERTEX_SHADER or FRAGMENT_SHADER
+ * @param {string} src - The vertex shader source as an array of strings.
+ * @returns {WebGLShader} the shader
+ */
+ function compileShader(gl, type, src) {
+ var shader = gl.createShader(type);
+ gl.shaderSource(shader, src);
+ gl.compileShader(shader);
+ return shader;
+ }
+
+ /**
+ * will log a shader error highlighting the lines with the error
+ * also will add numbers along the side.
+ * @param gl - the WebGLContext
+ * @param shader - the shader to log errors for
+ */
+ function logPrettyShaderError(gl, shader) {
+ var shaderSrc = gl.getShaderSource(shader)
+ .split('\n')
+ .map(function (line, index) { return index + ": " + line; });
+ var shaderLog = gl.getShaderInfoLog(shader);
+ var splitShader = shaderLog.split('\n');
+ var dedupe = {};
+ var lineNumbers = splitShader.map(function (line) { return parseFloat(line.replace(/^ERROR\: 0\:([\d]+)\:.*$/, '$1')); })
+ .filter(function (n) {
+ if (n && !dedupe[n]) {
+ dedupe[n] = true;
+ return true;
+ }
+ return false;
+ });
+ var logArgs = [''];
+ lineNumbers.forEach(function (number) {
+ shaderSrc[number - 1] = "%c" + shaderSrc[number - 1] + "%c";
+ logArgs.push('background: #FF0000; color:#FFFFFF; font-size: 10px', 'font-size: 10px');
+ });
+ var fragmentSourceToLog = shaderSrc
+ .join('\n');
+ logArgs[0] = fragmentSourceToLog;
+ console.error(shaderLog);
+ // eslint-disable-next-line no-console
+ console.groupCollapsed('click to view full shader code');
+ console.warn.apply(console, logArgs);
+ // eslint-disable-next-line no-console
+ console.groupEnd();
+ }
+ /**
+ *
+ * logs out any program errors
+ * @param gl - The current WebGL context
+ * @param program - the WebGL program to display errors for
+ * @param vertexShader - the fragment WebGL shader program
+ * @param fragmentShader - the vertex WebGL shader program
+ */
+ function logProgramError(gl, program, vertexShader, fragmentShader) {
+ // if linking fails, then log and cleanup
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
+ if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
+ logPrettyShaderError(gl, vertexShader);
+ }
+ if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
+ logPrettyShaderError(gl, fragmentShader);
+ }
+ console.error('PixiJS Error: Could not initialize shader.');
+ // if there is a program info log, log it
+ if (gl.getProgramInfoLog(program) !== '') {
+ console.warn('PixiJS Warning: gl.getProgramInfoLog()', gl.getProgramInfoLog(program));
+ }
+ }
+ }
+
+ function booleanArray(size) {
+ var array = new Array(size);
+ for (var i = 0; i < array.length; i++) {
+ array[i] = false;
+ }
+ return array;
+ }
+ /**
+ * @method defaultValue
+ * @memberof PIXI.glCore.shader
+ * @param {string} type - Type of value
+ * @param {number} size
+ * @private
+ */
+ function defaultValue(type, size) {
+ switch (type) {
+ case 'float':
+ return 0;
+ case 'vec2':
+ return new Float32Array(2 * size);
+ case 'vec3':
+ return new Float32Array(3 * size);
+ case 'vec4':
+ return new Float32Array(4 * size);
+ case 'int':
+ case 'uint':
+ case 'sampler2D':
+ case 'sampler2DArray':
+ return 0;
+ case 'ivec2':
+ return new Int32Array(2 * size);
+ case 'ivec3':
+ return new Int32Array(3 * size);
+ case 'ivec4':
+ return new Int32Array(4 * size);
+ case 'uvec2':
+ return new Uint32Array(2 * size);
+ case 'uvec3':
+ return new Uint32Array(3 * size);
+ case 'uvec4':
+ return new Uint32Array(4 * size);
+ case 'bool':
+ return false;
+ case 'bvec2':
+ return booleanArray(2 * size);
+ case 'bvec3':
+ return booleanArray(3 * size);
+ case 'bvec4':
+ return booleanArray(4 * size);
+ case 'mat2':
+ return new Float32Array([1, 0,
+ 0, 1]);
+ case 'mat3':
+ return new Float32Array([1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1]);
+ case 'mat4':
+ return new Float32Array([1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1]);
+ }
+ return null;
+ }
+
+ var unknownContext = {};
+ var context = unknownContext;
+ /**
+ * returns a little WebGL context to use for program inspection.
+ * @static
+ * @private
+ * @returns {WebGLRenderingContext} a gl context to test with
+ */
+ function getTestContext() {
+ if (context === unknownContext || (context && context.isContextLost())) {
+ var canvas = settings$1.ADAPTER.createCanvas();
+ var gl = void 0;
+ if (settings$1.PREFER_ENV >= exports.ENV.WEBGL2) {
+ gl = canvas.getContext('webgl2', {});
+ }
+ if (!gl) {
+ gl = (canvas.getContext('webgl', {})
+ || canvas.getContext('experimental-webgl', {}));
+ if (!gl) {
+ // fail, not able to get a context
+ gl = null;
+ }
+ else {
+ // for shader testing..
+ gl.getExtension('WEBGL_draw_buffers');
+ }
+ }
+ context = gl;
+ }
+ return context;
+ }
+
+ var maxFragmentPrecision;
+ function getMaxFragmentPrecision() {
+ if (!maxFragmentPrecision) {
+ maxFragmentPrecision = exports.PRECISION.MEDIUM;
+ var gl = getTestContext();
+ if (gl) {
+ if (gl.getShaderPrecisionFormat) {
+ var shaderFragment = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT);
+ maxFragmentPrecision = shaderFragment.precision ? exports.PRECISION.HIGH : exports.PRECISION.MEDIUM;
+ }
+ }
+ }
+ return maxFragmentPrecision;
+ }
+
+ /**
+ * Sets the float precision on the shader, ensuring the device supports the request precision.
+ * If the precision is already present, it just ensures that the device is able to handle it.
+ * @private
+ * @param {string} src - The shader source
+ * @param {PIXI.PRECISION} requestedPrecision - The request float precision of the shader.
+ * @param {PIXI.PRECISION} maxSupportedPrecision - The maximum precision the shader supports.
+ * @returns {string} modified shader source
+ */
+ function setPrecision(src, requestedPrecision, maxSupportedPrecision) {
+ if (src.substring(0, 9) !== 'precision') {
+ // no precision supplied, so PixiJS will add the requested level.
+ var precision = requestedPrecision;
+ // If highp is requested but not supported, downgrade precision to a level all devices support.
+ if (requestedPrecision === exports.PRECISION.HIGH && maxSupportedPrecision !== exports.PRECISION.HIGH) {
+ precision = exports.PRECISION.MEDIUM;
+ }
+ return "precision " + precision + " float;\n" + src;
+ }
+ else if (maxSupportedPrecision !== exports.PRECISION.HIGH && src.substring(0, 15) === 'precision highp') {
+ // precision was supplied, but at a level this device does not support, so downgrading to mediump.
+ return src.replace('precision highp', 'precision mediump');
+ }
+ return src;
+ }
+
+ var GLSL_TO_SIZE = {
+ float: 1,
+ vec2: 2,
+ vec3: 3,
+ vec4: 4,
+ int: 1,
+ ivec2: 2,
+ ivec3: 3,
+ ivec4: 4,
+ uint: 1,
+ uvec2: 2,
+ uvec3: 3,
+ uvec4: 4,
+ bool: 1,
+ bvec2: 2,
+ bvec3: 3,
+ bvec4: 4,
+ mat2: 4,
+ mat3: 9,
+ mat4: 16,
+ sampler2D: 1,
+ };
+ /**
+ * @private
+ * @method mapSize
+ * @memberof PIXI.glCore.shader
+ * @param {string} type
+ */
+ function mapSize(type) {
+ return GLSL_TO_SIZE[type];
+ }
+
+ var GL_TABLE = null;
+ var GL_TO_GLSL_TYPES = {
+ FLOAT: 'float',
+ FLOAT_VEC2: 'vec2',
+ FLOAT_VEC3: 'vec3',
+ FLOAT_VEC4: 'vec4',
+ INT: 'int',
+ INT_VEC2: 'ivec2',
+ INT_VEC3: 'ivec3',
+ INT_VEC4: 'ivec4',
+ UNSIGNED_INT: 'uint',
+ UNSIGNED_INT_VEC2: 'uvec2',
+ UNSIGNED_INT_VEC3: 'uvec3',
+ UNSIGNED_INT_VEC4: 'uvec4',
+ BOOL: 'bool',
+ BOOL_VEC2: 'bvec2',
+ BOOL_VEC3: 'bvec3',
+ BOOL_VEC4: 'bvec4',
+ FLOAT_MAT2: 'mat2',
+ FLOAT_MAT3: 'mat3',
+ FLOAT_MAT4: 'mat4',
+ SAMPLER_2D: 'sampler2D',
+ INT_SAMPLER_2D: 'sampler2D',
+ UNSIGNED_INT_SAMPLER_2D: 'sampler2D',
+ SAMPLER_CUBE: 'samplerCube',
+ INT_SAMPLER_CUBE: 'samplerCube',
+ UNSIGNED_INT_SAMPLER_CUBE: 'samplerCube',
+ SAMPLER_2D_ARRAY: 'sampler2DArray',
+ INT_SAMPLER_2D_ARRAY: 'sampler2DArray',
+ UNSIGNED_INT_SAMPLER_2D_ARRAY: 'sampler2DArray',
+ };
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+ function mapType(gl, type) {
+ if (!GL_TABLE) {
+ var typeNames = Object.keys(GL_TO_GLSL_TYPES);
+ GL_TABLE = {};
+ for (var i = 0; i < typeNames.length; ++i) {
+ var tn = typeNames[i];
+ GL_TABLE[gl[tn]] = GL_TO_GLSL_TYPES[tn];
+ }
+ }
+ return GL_TABLE[type];
+ }
+
+ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
+ // Parsers, each one of these will take a look at the type of shader property and uniform.
+ // if they pass the test function then the code function is called that returns a the shader upload code for that uniform.
+ // Shader upload code is automagically generated with these parsers.
+ // If no parser is valid then the default upload functions are used.
+ // exposing Parsers means that custom upload logic can be added to pixi's shaders.
+ // A good example would be a pixi rectangle can be directly set on a uniform.
+ // If the shader sees it it knows how to upload the rectangle structure as a vec4
+ // format is as follows:
+ //
+ // {
+ // test: (data, uniform) => {} <--- test is this code should be used for this uniform
+ // code: (name, uniform) => {} <--- returns the string of the piece of code that uploads the uniform
+ // codeUbo: (name, uniform) => {} <--- returns the string of the piece of code that uploads the
+ // uniform to a uniform buffer
+ // }
+ var uniformParsers = [
+ // a float cache layer
+ {
+ test: function (data) {
+ return data.type === 'float' && data.size === 1;
+ },
+ code: function (name) {
+ return "\n if(uv[\"" + name + "\"] !== ud[\"" + name + "\"].value)\n {\n ud[\"" + name + "\"].value = uv[\"" + name + "\"]\n gl.uniform1f(ud[\"" + name + "\"].location, uv[\"" + name + "\"])\n }\n ";
+ },
+ },
+ // handling samplers
+ {
+ test: function (data, uniform) {
+ // eslint-disable-next-line max-len,no-eq-null,eqeqeq
+ return (data.type === 'sampler2D' || data.type === 'samplerCube' || data.type === 'sampler2DArray') && data.size === 1 && !data.isArray && (uniform == null || uniform.castToBaseTexture !== undefined);
+ },
+ code: function (name) { return "t = syncData.textureCount++;\n\n renderer.texture.bind(uv[\"" + name + "\"], t);\n\n if(ud[\"" + name + "\"].value !== t)\n {\n ud[\"" + name + "\"].value = t;\n gl.uniform1i(ud[\"" + name + "\"].location, t);\n; // eslint-disable-line max-len\n }"; },
+ },
+ // uploading pixi matrix object to mat3
+ {
+ test: function (data, uniform) {
+ return data.type === 'mat3' && data.size === 1 && uniform.a !== undefined;
+ },
+ code: function (name) {
+ // TODO and some smart caching dirty ids here!
+ return "\n gl.uniformMatrix3fv(ud[\"" + name + "\"].location, false, uv[\"" + name + "\"].toArray(true));\n ";
+ },
+ codeUbo: function (name) {
+ return "\n var " + name + "_matrix = uv." + name + ".toArray(true);\n\n data[offset] = " + name + "_matrix[0];\n data[offset+1] = " + name + "_matrix[1];\n data[offset+2] = " + name + "_matrix[2];\n \n data[offset + 4] = " + name + "_matrix[3];\n data[offset + 5] = " + name + "_matrix[4];\n data[offset + 6] = " + name + "_matrix[5];\n \n data[offset + 8] = " + name + "_matrix[6];\n data[offset + 9] = " + name + "_matrix[7];\n data[offset + 10] = " + name + "_matrix[8];\n ";
+ },
+ },
+ // uploading a pixi point as a vec2 with caching layer
+ {
+ test: function (data, uniform) {
+ return data.type === 'vec2' && data.size === 1 && uniform.x !== undefined;
+ },
+ code: function (name) {
+ return "\n cv = ud[\"" + name + "\"].value;\n v = uv[\"" + name + "\"];\n\n if(cv[0] !== v.x || cv[1] !== v.y)\n {\n cv[0] = v.x;\n cv[1] = v.y;\n gl.uniform2f(ud[\"" + name + "\"].location, v.x, v.y);\n }";
+ },
+ codeUbo: function (name) {
+ return "\n v = uv." + name + ";\n\n data[offset] = v.x;\n data[offset+1] = v.y;\n ";
+ }
+ },
+ // caching layer for a vec2
+ {
+ test: function (data) {
+ return data.type === 'vec2' && data.size === 1;
+ },
+ code: function (name) {
+ return "\n cv = ud[\"" + name + "\"].value;\n v = uv[\"" + name + "\"];\n\n if(cv[0] !== v[0] || cv[1] !== v[1])\n {\n cv[0] = v[0];\n cv[1] = v[1];\n gl.uniform2f(ud[\"" + name + "\"].location, v[0], v[1]);\n }\n ";
+ },
+ },
+ // upload a pixi rectangle as a vec4 with caching layer
+ {
+ test: function (data, uniform) {
+ return data.type === 'vec4' && data.size === 1 && uniform.width !== undefined;
+ },
+ code: function (name) {
+ return "\n cv = ud[\"" + name + "\"].value;\n v = uv[\"" + name + "\"];\n\n if(cv[0] !== v.x || cv[1] !== v.y || cv[2] !== v.width || cv[3] !== v.height)\n {\n cv[0] = v.x;\n cv[1] = v.y;\n cv[2] = v.width;\n cv[3] = v.height;\n gl.uniform4f(ud[\"" + name + "\"].location, v.x, v.y, v.width, v.height)\n }";
+ },
+ codeUbo: function (name) {
+ return "\n v = uv." + name + ";\n\n data[offset] = v.x;\n data[offset+1] = v.y;\n data[offset+2] = v.width;\n data[offset+3] = v.height;\n ";
+ }
+ },
+ // a caching layer for vec4 uploading
+ {
+ test: function (data) {
+ return data.type === 'vec4' && data.size === 1;
+ },
+ code: function (name) {
+ return "\n cv = ud[\"" + name + "\"].value;\n v = uv[\"" + name + "\"];\n\n if(cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2] || cv[3] !== v[3])\n {\n cv[0] = v[0];\n cv[1] = v[1];\n cv[2] = v[2];\n cv[3] = v[3];\n\n gl.uniform4f(ud[\"" + name + "\"].location, v[0], v[1], v[2], v[3])\n }";
+ },
+ } ];
+
+ // cu = Cached value's uniform data field
+ // cv = Cached value
+ // v = value to upload
+ // ud = uniformData
+ // uv = uniformValue
+ // l = location
+ var GLSL_TO_SINGLE_SETTERS_CACHED = {
+ float: "\n if (cv !== v)\n {\n cu.value = v;\n gl.uniform1f(location, v);\n }",
+ vec2: "\n if (cv[0] !== v[0] || cv[1] !== v[1])\n {\n cv[0] = v[0];\n cv[1] = v[1];\n\n gl.uniform2f(location, v[0], v[1])\n }",
+ vec3: "\n if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2])\n {\n cv[0] = v[0];\n cv[1] = v[1];\n cv[2] = v[2];\n\n gl.uniform3f(location, v[0], v[1], v[2])\n }",
+ vec4: "\n if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2] || cv[3] !== v[3])\n {\n cv[0] = v[0];\n cv[1] = v[1];\n cv[2] = v[2];\n cv[3] = v[3];\n\n gl.uniform4f(location, v[0], v[1], v[2], v[3]);\n }",
+ int: "\n if (cv !== v)\n {\n cu.value = v;\n\n gl.uniform1i(location, v);\n }",
+ ivec2: "\n if (cv[0] !== v[0] || cv[1] !== v[1])\n {\n cv[0] = v[0];\n cv[1] = v[1];\n\n gl.uniform2i(location, v[0], v[1]);\n }",
+ ivec3: "\n if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2])\n {\n cv[0] = v[0];\n cv[1] = v[1];\n cv[2] = v[2];\n\n gl.uniform3i(location, v[0], v[1], v[2]);\n }",
+ ivec4: "\n if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2] || cv[3] !== v[3])\n {\n cv[0] = v[0];\n cv[1] = v[1];\n cv[2] = v[2];\n cv[3] = v[3];\n\n gl.uniform4i(location, v[0], v[1], v[2], v[3]);\n }",
+ uint: "\n if (cv !== v)\n {\n cu.value = v;\n\n gl.uniform1ui(location, v);\n }",
+ uvec2: "\n if (cv[0] !== v[0] || cv[1] !== v[1])\n {\n cv[0] = v[0];\n cv[1] = v[1];\n\n gl.uniform2ui(location, v[0], v[1]);\n }",
+ uvec3: "\n if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2])\n {\n cv[0] = v[0];\n cv[1] = v[1];\n cv[2] = v[2];\n\n gl.uniform3ui(location, v[0], v[1], v[2]);\n }",
+ uvec4: "\n if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2] || cv[3] !== v[3])\n {\n cv[0] = v[0];\n cv[1] = v[1];\n cv[2] = v[2];\n cv[3] = v[3];\n\n gl.uniform4ui(location, v[0], v[1], v[2], v[3]);\n }",
+ bool: "\n if (cv !== v)\n {\n cu.value = v;\n gl.uniform1i(location, v);\n }",
+ bvec2: "\n if (cv[0] != v[0] || cv[1] != v[1])\n {\n cv[0] = v[0];\n cv[1] = v[1];\n\n gl.uniform2i(location, v[0], v[1]);\n }",
+ bvec3: "\n if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2])\n {\n cv[0] = v[0];\n cv[1] = v[1];\n cv[2] = v[2];\n\n gl.uniform3i(location, v[0], v[1], v[2]);\n }",
+ bvec4: "\n if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2] || cv[3] !== v[3])\n {\n cv[0] = v[0];\n cv[1] = v[1];\n cv[2] = v[2];\n cv[3] = v[3];\n\n gl.uniform4i(location, v[0], v[1], v[2], v[3]);\n }",
+ mat2: 'gl.uniformMatrix2fv(location, false, v)',
+ mat3: 'gl.uniformMatrix3fv(location, false, v)',
+ mat4: 'gl.uniformMatrix4fv(location, false, v)',
+ sampler2D: "\n if (cv !== v)\n {\n cu.value = v;\n\n gl.uniform1i(location, v);\n }",
+ samplerCube: "\n if (cv !== v)\n {\n cu.value = v;\n\n gl.uniform1i(location, v);\n }",
+ sampler2DArray: "\n if (cv !== v)\n {\n cu.value = v;\n\n gl.uniform1i(location, v);\n }",
+ };
+ var GLSL_TO_ARRAY_SETTERS = {
+ float: "gl.uniform1fv(location, v)",
+ vec2: "gl.uniform2fv(location, v)",
+ vec3: "gl.uniform3fv(location, v)",
+ vec4: 'gl.uniform4fv(location, v)',
+ mat4: 'gl.uniformMatrix4fv(location, false, v)',
+ mat3: 'gl.uniformMatrix3fv(location, false, v)',
+ mat2: 'gl.uniformMatrix2fv(location, false, v)',
+ int: 'gl.uniform1iv(location, v)',
+ ivec2: 'gl.uniform2iv(location, v)',
+ ivec3: 'gl.uniform3iv(location, v)',
+ ivec4: 'gl.uniform4iv(location, v)',
+ uint: 'gl.uniform1uiv(location, v)',
+ uvec2: 'gl.uniform2uiv(location, v)',
+ uvec3: 'gl.uniform3uiv(location, v)',
+ uvec4: 'gl.uniform4uiv(location, v)',
+ bool: 'gl.uniform1iv(location, v)',
+ bvec2: 'gl.uniform2iv(location, v)',
+ bvec3: 'gl.uniform3iv(location, v)',
+ bvec4: 'gl.uniform4iv(location, v)',
+ sampler2D: 'gl.uniform1iv(location, v)',
+ samplerCube: 'gl.uniform1iv(location, v)',
+ sampler2DArray: 'gl.uniform1iv(location, v)',
+ };
+ function generateUniformsSync(group, uniformData) {
+ var _a;
+ var funcFragments = ["\n var v = null;\n var cv = null;\n var cu = null;\n var t = 0;\n var gl = renderer.gl;\n "];
+ for (var i in group.uniforms) {
+ var data = uniformData[i];
+ if (!data) {
+ if ((_a = group.uniforms[i]) === null || _a === void 0 ? void 0 : _a.group) {
+ if (group.uniforms[i].ubo) {
+ funcFragments.push("\n renderer.shader.syncUniformBufferGroup(uv." + i + ", '" + i + "');\n ");
+ }
+ else {
+ funcFragments.push("\n renderer.shader.syncUniformGroup(uv." + i + ", syncData);\n ");
+ }
+ }
+ continue;
+ }
+ var uniform = group.uniforms[i];
+ var parsed = false;
+ for (var j = 0; j < uniformParsers.length; j++) {
+ if (uniformParsers[j].test(data, uniform)) {
+ funcFragments.push(uniformParsers[j].code(i, uniform));
+ parsed = true;
+ break;
+ }
+ }
+ if (!parsed) {
+ var templateType = (data.size === 1) ? GLSL_TO_SINGLE_SETTERS_CACHED : GLSL_TO_ARRAY_SETTERS;
+ var template = templateType[data.type].replace('location', "ud[\"" + i + "\"].location");
+ funcFragments.push("\n cu = ud[\"" + i + "\"];\n cv = cu.value;\n v = uv[\"" + i + "\"];\n " + template + ";");
+ }
+ }
+ /*
+ * the introduction of syncData is to solve an issue where textures in uniform groups are not set correctly
+ * the texture count was always starting from 0 in each group. This needs to increment each time a texture is used
+ * no matter which group is being used
+ *
+ */
+ // eslint-disable-next-line no-new-func
+ return new Function('ud', 'uv', 'renderer', 'syncData', funcFragments.join('\n'));
+ }
+
+ var fragTemplate$1 = [
+ 'precision mediump float;',
+ 'void main(void){',
+ 'float test = 0.1;',
+ '%forloop%',
+ 'gl_FragColor = vec4(0.0);',
+ '}' ].join('\n');
+ function generateIfTestSrc(maxIfs) {
+ var src = '';
+ for (var i = 0; i < maxIfs; ++i) {
+ if (i > 0) {
+ src += '\nelse ';
+ }
+ if (i < maxIfs - 1) {
+ src += "if(test == " + i + ".0){}";
+ }
+ }
+ return src;
+ }
+ function checkMaxIfStatementsInShader(maxIfs, gl) {
+ if (maxIfs === 0) {
+ throw new Error('Invalid value of `0` passed to `checkMaxIfStatementsInShader`');
+ }
+ var shader = gl.createShader(gl.FRAGMENT_SHADER);
+ while (true) // eslint-disable-line no-constant-condition
+ {
+ var fragmentSrc = fragTemplate$1.replace(/%forloop%/gi, generateIfTestSrc(maxIfs));
+ gl.shaderSource(shader, fragmentSrc);
+ gl.compileShader(shader);
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
+ maxIfs = (maxIfs / 2) | 0;
+ }
+ else {
+ // valid!
+ break;
+ }
+ }
+ return maxIfs;
+ }
+
+ // Cache the result to prevent running this over and over
+ var unsafeEval;
+ /**
+ * Not all platforms allow to generate function code (e.g., `new Function`).
+ * this provides the platform-level detection.
+ * @private
+ * @returns {boolean} `true` if `new Function` is supported.
+ */
+ function unsafeEvalSupported() {
+ if (typeof unsafeEval === 'boolean') {
+ return unsafeEval;
+ }
+ try {
+ /* eslint-disable no-new-func */
+ var func = new Function('param1', 'param2', 'param3', 'return param1[param2] === param3;');
+ /* eslint-enable no-new-func */
+ unsafeEval = func({ a: 'b' }, 'a', 'b') === true;
+ }
+ catch (e) {
+ unsafeEval = false;
+ }
+ return unsafeEval;
+ }
+
+ var defaultFragment$2 = "varying vec2 vTextureCoord;\n\nuniform sampler2D uSampler;\n\nvoid main(void){\n gl_FragColor *= texture2D(uSampler, vTextureCoord);\n}";
+
+ var defaultVertex$3 = "attribute vec2 aVertexPosition;\nattribute vec2 aTextureCoord;\n\nuniform mat3 projectionMatrix;\n\nvarying vec2 vTextureCoord;\n\nvoid main(void){\n gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);\n vTextureCoord = aTextureCoord;\n}\n";
+
+ var UID$1 = 0;
+ var nameCache = {};
+ /**
+ * Helper class to create a shader program.
+ * @memberof PIXI
+ */
+ var Program = /** @class */ (function () {
+ /**
+ * @param vertexSrc - The source of the vertex shader.
+ * @param fragmentSrc - The source of the fragment shader.
+ * @param name - Name for shader
+ */
+ function Program(vertexSrc, fragmentSrc, name) {
+ if (name === void 0) { name = 'pixi-shader'; }
+ this.id = UID$1++;
+ this.vertexSrc = vertexSrc || Program.defaultVertexSrc;
+ this.fragmentSrc = fragmentSrc || Program.defaultFragmentSrc;
+ this.vertexSrc = this.vertexSrc.trim();
+ this.fragmentSrc = this.fragmentSrc.trim();
+ if (this.vertexSrc.substring(0, 8) !== '#version') {
+ name = name.replace(/\s+/g, '-');
+ if (nameCache[name]) {
+ nameCache[name]++;
+ name += "-" + nameCache[name];
+ }
+ else {
+ nameCache[name] = 1;
+ }
+ this.vertexSrc = "#define SHADER_NAME " + name + "\n" + this.vertexSrc;
+ this.fragmentSrc = "#define SHADER_NAME " + name + "\n" + this.fragmentSrc;
+ this.vertexSrc = setPrecision(this.vertexSrc, settings$1.PRECISION_VERTEX, exports.PRECISION.HIGH);
+ this.fragmentSrc = setPrecision(this.fragmentSrc, settings$1.PRECISION_FRAGMENT, getMaxFragmentPrecision());
+ }
+ // currently this does not extract structs only default types
+ // this is where we store shader references..
+ this.glPrograms = {};
+ this.syncUniforms = null;
+ }
+ Object.defineProperty(Program, "defaultVertexSrc", {
+ /**
+ * The default vertex shader source.
+ * @constant
+ */
+ get: function () {
+ return defaultVertex$3;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Program, "defaultFragmentSrc", {
+ /**
+ * The default fragment shader source.
+ * @constant
+ */
+ get: function () {
+ return defaultFragment$2;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * A short hand function to create a program based of a vertex and fragment shader.
+ *
+ * This method will also check to see if there is a cached program.
+ * @param vertexSrc - The source of the vertex shader.
+ * @param fragmentSrc - The source of the fragment shader.
+ * @param name - Name for shader
+ * @returns A shiny new PixiJS shader program!
+ */
+ Program.from = function (vertexSrc, fragmentSrc, name) {
+ var key = vertexSrc + fragmentSrc;
+ var program = ProgramCache[key];
+ if (!program) {
+ ProgramCache[key] = program = new Program(vertexSrc, fragmentSrc, name);
+ }
+ return program;
+ };
+ return Program;
+ }());
+
+ /**
+ * A helper class for shaders.
+ * @memberof PIXI
+ */
+ var Shader = /** @class */ (function () {
+ /**
+ * @param program - The program the shader will use.
+ * @param uniforms - Custom uniforms to use to augment the built-in ones.
+ */
+ function Shader(program, uniforms) {
+ /**
+ * Used internally to bind uniform buffer objects.
+ * @ignore
+ */
+ this.uniformBindCount = 0;
+ this.program = program;
+ // lets see whats been passed in
+ // uniforms should be converted to a uniform group
+ if (uniforms) {
+ if (uniforms instanceof UniformGroup) {
+ this.uniformGroup = uniforms;
+ }
+ else {
+ this.uniformGroup = new UniformGroup(uniforms);
+ }
+ }
+ else {
+ this.uniformGroup = new UniformGroup({});
+ }
+ }
+ // TODO move to shader system..
+ Shader.prototype.checkUniformExists = function (name, group) {
+ if (group.uniforms[name]) {
+ return true;
+ }
+ for (var i in group.uniforms) {
+ var uniform = group.uniforms[i];
+ if (uniform.group) {
+ if (this.checkUniformExists(name, uniform)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+ Shader.prototype.destroy = function () {
+ // usage count on programs?
+ // remove if not used!
+ this.uniformGroup = null;
+ };
+ Object.defineProperty(Shader.prototype, "uniforms", {
+ /**
+ * Shader uniform values, shortcut for `uniformGroup.uniforms`.
+ * @readonly
+ */
+ get: function () {
+ return this.uniformGroup.uniforms;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * A short hand function to create a shader based of a vertex and fragment shader.
+ * @param vertexSrc - The source of the vertex shader.
+ * @param fragmentSrc - The source of the fragment shader.
+ * @param uniforms - Custom uniforms to use to augment the built-in ones.
+ * @returns A shiny new PixiJS shader!
+ */
+ Shader.from = function (vertexSrc, fragmentSrc, uniforms) {
+ var program = Program.from(vertexSrc, fragmentSrc);
+ return new Shader(program, uniforms);
+ };
+ return Shader;
+ }());
+
+ /* eslint-disable max-len */
+ var BLEND$1 = 0;
+ var OFFSET$1 = 1;
+ var CULLING$1 = 2;
+ var DEPTH_TEST$1 = 3;
+ var WINDING$1 = 4;
+ var DEPTH_MASK$1 = 5;
+ /**
+ * This is a WebGL state, and is is passed to {@link PIXI.StateSystem}.
+ *
+ * Each mesh rendered may require WebGL to be in a different state.
+ * For example you may want different blend mode or to enable polygon offsets
+ * @memberof PIXI
+ */
+ var State = /** @class */ (function () {
+ function State() {
+ this.data = 0;
+ this.blendMode = exports.BLEND_MODES.NORMAL;
+ this.polygonOffset = 0;
+ this.blend = true;
+ this.depthMask = true;
+ // this.depthTest = true;
+ }
+ Object.defineProperty(State.prototype, "blend", {
+ /**
+ * Activates blending of the computed fragment color values.
+ * @default true
+ */
+ get: function () {
+ return !!(this.data & (1 << BLEND$1));
+ },
+ set: function (value) {
+ if (!!(this.data & (1 << BLEND$1)) !== value) {
+ this.data ^= (1 << BLEND$1);
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(State.prototype, "offsets", {
+ /**
+ * Activates adding an offset to depth values of polygon's fragments
+ * @default false
+ */
+ get: function () {
+ return !!(this.data & (1 << OFFSET$1));
+ },
+ set: function (value) {
+ if (!!(this.data & (1 << OFFSET$1)) !== value) {
+ this.data ^= (1 << OFFSET$1);
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(State.prototype, "culling", {
+ /**
+ * Activates culling of polygons.
+ * @default false
+ */
+ get: function () {
+ return !!(this.data & (1 << CULLING$1));
+ },
+ set: function (value) {
+ if (!!(this.data & (1 << CULLING$1)) !== value) {
+ this.data ^= (1 << CULLING$1);
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(State.prototype, "depthTest", {
+ /**
+ * Activates depth comparisons and updates to the depth buffer.
+ * @default false
+ */
+ get: function () {
+ return !!(this.data & (1 << DEPTH_TEST$1));
+ },
+ set: function (value) {
+ if (!!(this.data & (1 << DEPTH_TEST$1)) !== value) {
+ this.data ^= (1 << DEPTH_TEST$1);
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(State.prototype, "depthMask", {
+ /**
+ * Enables or disables writing to the depth buffer.
+ * @default true
+ */
+ get: function () {
+ return !!(this.data & (1 << DEPTH_MASK$1));
+ },
+ set: function (value) {
+ if (!!(this.data & (1 << DEPTH_MASK$1)) !== value) {
+ this.data ^= (1 << DEPTH_MASK$1);
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(State.prototype, "clockwiseFrontFace", {
+ /**
+ * Specifies whether or not front or back-facing polygons can be culled.
+ * @default false
+ */
+ get: function () {
+ return !!(this.data & (1 << WINDING$1));
+ },
+ set: function (value) {
+ if (!!(this.data & (1 << WINDING$1)) !== value) {
+ this.data ^= (1 << WINDING$1);
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(State.prototype, "blendMode", {
+ /**
+ * The blend mode to be applied when this state is set. Apply a value of `PIXI.BLEND_MODES.NORMAL` to reset the blend mode.
+ * Setting this mode to anything other than NO_BLEND will automatically switch blending on.
+ * @default PIXI.BLEND_MODES.NORMAL
+ */
+ get: function () {
+ return this._blendMode;
+ },
+ set: function (value) {
+ this.blend = (value !== exports.BLEND_MODES.NONE);
+ this._blendMode = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(State.prototype, "polygonOffset", {
+ /**
+ * The polygon offset. Setting this property to anything other than 0 will automatically enable polygon offset fill.
+ * @default 0
+ */
+ get: function () {
+ return this._polygonOffset;
+ },
+ set: function (value) {
+ this.offsets = !!value;
+ this._polygonOffset = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ State.prototype.toString = function () {
+ return "[@pixi/core:State "
+ + ("blendMode=" + this.blendMode + " ")
+ + ("clockwiseFrontFace=" + this.clockwiseFrontFace + " ")
+ + ("culling=" + this.culling + " ")
+ + ("depthMask=" + this.depthMask + " ")
+ + ("polygonOffset=" + this.polygonOffset)
+ + "]";
+ };
+ State.for2d = function () {
+ var state = new State();
+ state.depthTest = false;
+ state.blend = true;
+ return state;
+ };
+ return State;
+ }());
+
+ var defaultFragment$1 = "varying vec2 vTextureCoord;\n\nuniform sampler2D uSampler;\n\nvoid main(void){\n gl_FragColor = texture2D(uSampler, vTextureCoord);\n}\n";
+
+ var defaultVertex$2 = "attribute vec2 aVertexPosition;\n\nuniform mat3 projectionMatrix;\n\nvarying vec2 vTextureCoord;\n\nuniform vec4 inputSize;\nuniform vec4 outputFrame;\n\nvec4 filterVertexPosition( void )\n{\n vec2 position = aVertexPosition * max(outputFrame.zw, vec2(0.)) + outputFrame.xy;\n\n return vec4((projectionMatrix * vec3(position, 1.0)).xy, 0.0, 1.0);\n}\n\nvec2 filterTextureCoord( void )\n{\n return aVertexPosition * (outputFrame.zw * inputSize.zw);\n}\n\nvoid main(void)\n{\n gl_Position = filterVertexPosition();\n vTextureCoord = filterTextureCoord();\n}\n";
+
+ /**
+ * A filter is a special shader that applies post-processing effects to an input texture and writes into an output
+ * render-target.
+ *
+ * {@link http://pixijs.io/examples/#/filters/blur-filter.js Example} of the
+ * {@link PIXI.filters.BlurFilter BlurFilter}.
+ *
+ * ### Usage
+ * Filters can be applied to any DisplayObject or Container.
+ * PixiJS' `FilterSystem` renders the container into temporary Framebuffer,
+ * then filter renders it to the screen.
+ * Multiple filters can be added to the `filters` array property and stacked on each other.
+ *
+ * ```
+ * const filter = new PIXI.Filter(myShaderVert, myShaderFrag, { myUniform: 0.5 });
+ * const container = new PIXI.Container();
+ * container.filters = [filter];
+ * ```
+ *
+ * ### Previous Version Differences
+ *
+ * In PixiJS **v3**, a filter was always applied to _whole screen_.
+ *
+ * In PixiJS **v4**, a filter can be applied _only part of the screen_.
+ * Developers had to create a set of uniforms to deal with coordinates.
+ *
+ * In PixiJS **v5** combines _both approaches_.
+ * Developers can use normal coordinates of v3 and then allow filter to use partial Framebuffers,
+ * bringing those extra uniforms into account.
+ *
+ * Also be aware that we have changed default vertex shader, please consult
+ * {@link https://github.com/pixijs/pixi.js/wiki/v5-Creating-filters Wiki}.
+ *
+ * ### Frames
+ *
+ * The following table summarizes the coordinate spaces used in the filtering pipeline:
+ *
+ * <table>
+ * <thead>
+ * <tr>
+ * <th>Coordinate Space</th>
+ * <th>Description</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td>Texture Coordinates</td>
+ * <td>
+ * The texture (or UV) coordinates in the input base-texture's space. These are normalized into the (0,1) range along
+ * both axes.
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>World Space</td>
+ * <td>
+ * A point in the same space as the world bounds of any display-object (i.e. in the scene graph's space).
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>Physical Pixels</td>
+ * <td>
+ * This is base-texture's space with the origin on the top-left. You can calculate these by multiplying the texture
+ * coordinates by the dimensions of the texture.
+ * </td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * ### Built-in Uniforms
+ *
+ * PixiJS viewport uses screen (CSS) coordinates, `(0, 0, renderer.screen.width, renderer.screen.height)`,
+ * and `projectionMatrix` uniform maps it to the gl viewport.
+ *
+ * **uSampler**
+ *
+ * The most important uniform is the input texture that container was rendered into.
+ * _Important note: as with all Framebuffers in PixiJS, both input and output are
+ * premultiplied by alpha._
+ *
+ * By default, input normalized coordinates are passed to fragment shader with `vTextureCoord`.
+ * Use it to sample the input.
+ *
+ * ```
+ * const fragment = `
+ * varying vec2 vTextureCoord;
+ * uniform sampler2D uSampler;
+ * void main(void)
+ * {
+ * gl_FragColor = texture2D(uSampler, vTextureCoord);
+ * }
+ * `;
+ *
+ * const myFilter = new PIXI.Filter(null, fragment);
+ * ```
+ *
+ * This filter is just one uniform less than {@link PIXI.filters.AlphaFilter AlphaFilter}.
+ *
+ * **outputFrame**
+ *
+ * The `outputFrame` holds the rectangle where filter is applied in screen (CSS) coordinates.
+ * It's the same as `renderer.screen` for a fullscreen filter.
+ * Only a part of `outputFrame.zw` size of temporary Framebuffer is used,
+ * `(0, 0, outputFrame.width, outputFrame.height)`,
+ *
+ * Filters uses this quad to normalized (0-1) space, its passed into `aVertexPosition` attribute.
+ * To calculate vertex position in screen space using normalized (0-1) space:
+ *
+ * ```
+ * vec4 filterVertexPosition( void )
+ * {
+ * vec2 position = aVertexPosition * max(outputFrame.zw, vec2(0.)) + outputFrame.xy;
+ * return vec4((projectionMatrix * vec3(position, 1.0)).xy, 0.0, 1.0);
+ * }
+ * ```
+ *
+ * **inputSize**
+ *
+ * Temporary framebuffer is different, it can be either the size of screen, either power-of-two.
+ * The `inputSize.xy` are size of temporary framebuffer that holds input.
+ * The `inputSize.zw` is inverted, it's a shortcut to evade division inside the shader.
+ *
+ * Set `inputSize.xy = outputFrame.zw` for a fullscreen filter.
+ *
+ * To calculate input normalized coordinate, you have to map it to filter normalized space.
+ * Multiply by `outputFrame.zw` to get input coordinate.
+ * Divide by `inputSize.xy` to get input normalized coordinate.
+ *
+ * ```
+ * vec2 filterTextureCoord( void )
+ * {
+ * return aVertexPosition * (outputFrame.zw * inputSize.zw); // same as /inputSize.xy
+ * }
+ * ```
+ * **resolution**
+ *
+ * The `resolution` is the ratio of screen (CSS) pixels to real pixels.
+ *
+ * **inputPixel**
+ *
+ * `inputPixel.xy` is the size of framebuffer in real pixels, same as `inputSize.xy * resolution`
+ * `inputPixel.zw` is inverted `inputPixel.xy`.
+ *
+ * It's handy for filters that use neighbour pixels, like {@link PIXI.filters.FXAAFilter FXAAFilter}.
+ *
+ * **inputClamp**
+ *
+ * If you try to get info from outside of used part of Framebuffer - you'll get undefined behaviour.
+ * For displacements, coordinates has to be clamped.
+ *
+ * The `inputClamp.xy` is left-top pixel center, you may ignore it, because we use left-top part of Framebuffer
+ * `inputClamp.zw` is bottom-right pixel center.
+ *
+ * ```
+ * vec4 color = texture2D(uSampler, clamp(modifiedTextureCoord, inputClamp.xy, inputClamp.zw))
+ * ```
+ * OR
+ * ```
+ * vec4 color = texture2D(uSampler, min(modifigedTextureCoord, inputClamp.zw))
+ * ```
+ *
+ * ### Additional Information
+ *
+ * Complete documentation on Filter usage is located in the
+ * {@link https://github.com/pixijs/pixi.js/wiki/v5-Creating-filters Wiki}.
+ *
+ * Since PixiJS only had a handful of built-in filters, additional filters can be downloaded
+ * {@link https://github.com/pixijs/pixi-filters here} from the PixiJS Filters repository.
+ * @memberof PIXI
+ */
+ var Filter = /** @class */ (function (_super) {
+ __extends$i(Filter, _super);
+ /**
+ * @param vertexSrc - The source of the vertex shader.
+ * @param fragmentSrc - The source of the fragment shader.
+ * @param uniforms - Custom uniforms to use to augment the built-in ones.
+ */
+ function Filter(vertexSrc, fragmentSrc, uniforms) {
+ var _this = this;
+ var program = Program.from(vertexSrc || Filter.defaultVertexSrc, fragmentSrc || Filter.defaultFragmentSrc);
+ _this = _super.call(this, program, uniforms) || this;
+ _this.padding = 0;
+ _this.resolution = settings$1.FILTER_RESOLUTION;
+ _this.multisample = settings$1.FILTER_MULTISAMPLE;
+ _this.enabled = true;
+ _this.autoFit = true;
+ _this.state = new State();
+ return _this;
+ }
+ /**
+ * Applies the filter
+ * @param {PIXI.FilterSystem} filterManager - The renderer to retrieve the filter from
+ * @param {PIXI.RenderTexture} input - The input render target.
+ * @param {PIXI.RenderTexture} output - The target to output to.
+ * @param {PIXI.CLEAR_MODES} [clearMode] - Should the output be cleared before rendering to it.
+ * @param {object} [_currentState] - It's current state of filter.
+ * There are some useful properties in the currentState :
+ * target, filters, sourceFrame, destinationFrame, renderTarget, resolution
+ */
+ Filter.prototype.apply = function (filterManager, input, output, clearMode, _currentState) {
+ // do as you please!
+ filterManager.applyFilter(this, input, output, clearMode);
+ // or just do a regular render..
+ };
+ Object.defineProperty(Filter.prototype, "blendMode", {
+ /**
+ * Sets the blend mode of the filter.
+ * @default PIXI.BLEND_MODES.NORMAL
+ */
+ get: function () {
+ return this.state.blendMode;
+ },
+ set: function (value) {
+ this.state.blendMode = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Filter.prototype, "resolution", {
+ /**
+ * The resolution of the filter. Setting this to be lower will lower the quality but
+ * increase the performance of the filter.
+ */
+ get: function () {
+ return this._resolution;
+ },
+ set: function (value) {
+ this._resolution = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Filter, "defaultVertexSrc", {
+ /**
+ * The default vertex shader source
+ * @constant
+ */
+ get: function () {
+ return defaultVertex$2;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Filter, "defaultFragmentSrc", {
+ /**
+ * The default fragment shader source
+ * @constant
+ */
+ get: function () {
+ return defaultFragment$1;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return Filter;
+ }(Shader));
+
+ var vertex$4 = "attribute vec2 aVertexPosition;\nattribute vec2 aTextureCoord;\n\nuniform mat3 projectionMatrix;\nuniform mat3 otherMatrix;\n\nvarying vec2 vMaskCoord;\nvarying vec2 vTextureCoord;\n\nvoid main(void)\n{\n gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);\n\n vTextureCoord = aTextureCoord;\n vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;\n}\n";
+
+ var fragment$7 = "varying vec2 vMaskCoord;\nvarying vec2 vTextureCoord;\n\nuniform sampler2D uSampler;\nuniform sampler2D mask;\nuniform float alpha;\nuniform float npmAlpha;\nuniform vec4 maskClamp;\n\nvoid main(void)\n{\n float clip = step(3.5,\n step(maskClamp.x, vMaskCoord.x) +\n step(maskClamp.y, vMaskCoord.y) +\n step(vMaskCoord.x, maskClamp.z) +\n step(vMaskCoord.y, maskClamp.w));\n\n vec4 original = texture2D(uSampler, vTextureCoord);\n vec4 masky = texture2D(mask, vMaskCoord);\n float alphaMul = 1.0 - npmAlpha * (1.0 - masky.a);\n\n original *= (alphaMul * masky.r * alpha * clip);\n\n gl_FragColor = original;\n}\n";
+
+ var tempMat$1 = new Matrix();
+ /**
+ * Class controls uv mapping from Texture normal space to BaseTexture normal space.
+ *
+ * Takes `trim` and `rotate` into account. May contain clamp settings for Meshes and TilingSprite.
+ *
+ * Can be used in Texture `uvMatrix` field, or separately, you can use different clamp settings on the same texture.
+ * If you want to add support for texture region of certain feature or filter, that's what you're looking for.
+ *
+ * Takes track of Texture changes through `_lastTextureID` private field.
+ * Use `update()` method call to track it from outside.
+ * @see PIXI.Texture
+ * @see PIXI.Mesh
+ * @see PIXI.TilingSprite
+ * @memberof PIXI
+ */
+ var TextureMatrix = /** @class */ (function () {
+ /**
+ * @param texture - observed texture
+ * @param clampMargin - Changes frame clamping, 0.5 by default. Use -0.5 for extra border.
+ */
+ function TextureMatrix(texture, clampMargin) {
+ this._texture = texture;
+ this.mapCoord = new Matrix();
+ this.uClampFrame = new Float32Array(4);
+ this.uClampOffset = new Float32Array(2);
+ this._textureID = -1;
+ this._updateID = 0;
+ this.clampOffset = 0;
+ this.clampMargin = (typeof clampMargin === 'undefined') ? 0.5 : clampMargin;
+ this.isSimple = false;
+ }
+ Object.defineProperty(TextureMatrix.prototype, "texture", {
+ /** Texture property. */
+ get: function () {
+ return this._texture;
+ },
+ set: function (value) {
+ this._texture = value;
+ this._textureID = -1;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Multiplies uvs array to transform
+ * @param uvs - mesh uvs
+ * @param [out=uvs] - output
+ * @returns - output
+ */
+ TextureMatrix.prototype.multiplyUvs = function (uvs, out) {
+ if (out === undefined) {
+ out = uvs;
+ }
+ var mat = this.mapCoord;
+ for (var i = 0; i < uvs.length; i += 2) {
+ var x = uvs[i];
+ var y = uvs[i + 1];
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+ return out;
+ };
+ /**
+ * Updates matrices if texture was changed.
+ * @param [forceUpdate=false] - if true, matrices will be updated any case
+ * @returns - Whether or not it was updated
+ */
+ TextureMatrix.prototype.update = function (forceUpdate) {
+ var tex = this._texture;
+ if (!tex || !tex.valid) {
+ return false;
+ }
+ if (!forceUpdate
+ && this._textureID === tex._updateID) {
+ return false;
+ }
+ this._textureID = tex._updateID;
+ this._updateID++;
+ var uvs = tex._uvs;
+ this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0);
+ var orig = tex.orig;
+ var trim = tex.trim;
+ if (trim) {
+ tempMat$1.set(orig.width / trim.width, 0, 0, orig.height / trim.height, -trim.x / trim.width, -trim.y / trim.height);
+ this.mapCoord.append(tempMat$1);
+ }
+ var texBase = tex.baseTexture;
+ var frame = this.uClampFrame;
+ var margin = this.clampMargin / texBase.resolution;
+ var offset = this.clampOffset;
+ frame[0] = (tex._frame.x + margin + offset) / texBase.width;
+ frame[1] = (tex._frame.y + margin + offset) / texBase.height;
+ frame[2] = (tex._frame.x + tex._frame.width - margin + offset) / texBase.width;
+ frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
+ this.uClampOffset[0] = offset / texBase.realWidth;
+ this.uClampOffset[1] = offset / texBase.realHeight;
+ this.isSimple = tex._frame.width === texBase.width
+ && tex._frame.height === texBase.height
+ && tex.rotate === 0;
+ return true;
+ };
+ return TextureMatrix;
+ }());
+
+ /**
+ * This handles a Sprite acting as a mask, as opposed to a Graphic.
+ *
+ * WebGL only.
+ * @memberof PIXI
+ */
+ var SpriteMaskFilter = /** @class */ (function (_super) {
+ __extends$i(SpriteMaskFilter, _super);
+ /** @ignore */
+ function SpriteMaskFilter(vertexSrc, fragmentSrc, uniforms) {
+ var _this = this;
+ var sprite = null;
+ if (typeof vertexSrc !== 'string' && fragmentSrc === undefined && uniforms === undefined) {
+ sprite = vertexSrc;
+ vertexSrc = undefined;
+ fragmentSrc = undefined;
+ uniforms = undefined;
+ }
+ _this = _super.call(this, vertexSrc || vertex$4, fragmentSrc || fragment$7, uniforms) || this;
+ _this.maskSprite = sprite;
+ _this.maskMatrix = new Matrix();
+ return _this;
+ }
+ Object.defineProperty(SpriteMaskFilter.prototype, "maskSprite", {
+ /**
+ * Sprite mask
+ * @type {PIXI.DisplayObject}
+ */
+ get: function () {
+ return this._maskSprite;
+ },
+ set: function (value) {
+ this._maskSprite = value;
+ if (this._maskSprite) {
+ this._maskSprite.renderable = false;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Applies the filter
+ * @param filterManager - The renderer to retrieve the filter from
+ * @param input - The input render target.
+ * @param output - The target to output to.
+ * @param clearMode - Should the output be cleared before rendering to it.
+ */
+ SpriteMaskFilter.prototype.apply = function (filterManager, input, output, clearMode) {
+ var maskSprite = this._maskSprite;
+ var tex = maskSprite._texture;
+ if (!tex.valid) {
+ return;
+ }
+ if (!tex.uvMatrix) {
+ // margin = 0.0, let it bleed a bit, shader code becomes easier
+ // assuming that atlas textures were made with 1-pixel padding
+ tex.uvMatrix = new TextureMatrix(tex, 0.0);
+ }
+ tex.uvMatrix.update();
+ this.uniforms.npmAlpha = tex.baseTexture.alphaMode ? 0.0 : 1.0;
+ this.uniforms.mask = tex;
+ // get _normalized sprite texture coords_ and convert them to _normalized atlas texture coords_ with `prepend`
+ this.uniforms.otherMatrix = filterManager.calculateSpriteMatrix(this.maskMatrix, maskSprite)
+ .prepend(tex.uvMatrix.mapCoord);
+ this.uniforms.alpha = maskSprite.worldAlpha;
+ this.uniforms.maskClamp = tex.uvMatrix.uClampFrame;
+ filterManager.applyFilter(this, input, output, clearMode);
+ };
+ return SpriteMaskFilter;
+ }(Filter));
+
+ /**
+ * System plugin to the renderer to manage masks.
+ *
+ * There are three built-in types of masking:
+ * **Scissor Masking**: Scissor masking discards pixels that are outside of a rectangle called the scissor box. It is
+ * the most performant as the scissor test is inexpensive. However, it can only be used when the mask is rectangular.
+ * **Stencil Masking**: Stencil masking discards pixels that don't overlap with the pixels rendered into the stencil
+ * buffer. It is the next fastest option as it does not require rendering into a separate framebuffer. However, it does
+ * cause the mask to be rendered **twice** for each masking operation; hence, minimize the rendering cost of your masks.
+ * **Sprite Mask Filtering**: Sprite mask filtering discards pixels based on the red channel of the sprite-mask's
+ * texture. (Generally, the masking texture is grayscale). Using advanced techniques, you might be able to embed this
+ * type of masking in a custom shader - and hence, bypassing the masking system fully for performance wins.
+ *
+ * The best type of masking is auto-detected when you `push` one. To use scissor masking, you must pass in a `Graphics`
+ * object with just a rectangle drawn.
+ *
+ * ## Mask Stacks
+ *
+ * In the scene graph, masks can be applied recursively, i.e. a mask can be applied during a masking operation. The mask
+ * stack stores the currently applied masks in order. Each {@link PIXI.BaseRenderTexture} holds its own mask stack, i.e.
+ * when you switch render-textures, the old masks only applied when you switch back to rendering to the old render-target.
+ * @memberof PIXI
+ */
+ var MaskSystem = /** @class */ (function () {
+ /**
+ * @param renderer - The renderer this System works for.
+ */
+ function MaskSystem(renderer) {
+ this.renderer = renderer;
+ this.enableScissor = true;
+ this.alphaMaskPool = [];
+ this.maskDataPool = [];
+ this.maskStack = [];
+ this.alphaMaskIndex = 0;
+ }
+ /**
+ * Changes the mask stack that is used by this System.
+ * @param maskStack - The mask stack
+ */
+ MaskSystem.prototype.setMaskStack = function (maskStack) {
+ this.maskStack = maskStack;
+ this.renderer.scissor.setMaskStack(maskStack);
+ this.renderer.stencil.setMaskStack(maskStack);
+ };
+ /**
+ * Enables the mask and appends it to the current mask stack.
+ *
+ * NOTE: The batch renderer should be flushed beforehand to prevent pending renders from being masked.
+ * @param {PIXI.DisplayObject} target - Display Object to push the mask to
+ * @param {PIXI.MaskData|PIXI.Sprite|PIXI.Graphics|PIXI.DisplayObject} maskDataOrTarget - The masking data.
+ */
+ MaskSystem.prototype.push = function (target, maskDataOrTarget) {
+ var maskData = maskDataOrTarget;
+ if (!maskData.isMaskData) {
+ var d = this.maskDataPool.pop() || new MaskData();
+ d.pooled = true;
+ d.maskObject = maskDataOrTarget;
+ maskData = d;
+ }
+ var maskAbove = this.maskStack.length !== 0 ? this.maskStack[this.maskStack.length - 1] : null;
+ maskData.copyCountersOrReset(maskAbove);
+ maskData._colorMask = maskAbove ? maskAbove._colorMask : 0xf;
+ if (maskData.autoDetect) {
+ this.detect(maskData);
+ }
+ maskData._target = target;
+ if (maskData.type !== exports.MASK_TYPES.SPRITE) {
+ this.maskStack.push(maskData);
+ }
+ if (maskData.enabled) {
+ switch (maskData.type) {
+ case exports.MASK_TYPES.SCISSOR:
+ this.renderer.scissor.push(maskData);
+ break;
+ case exports.MASK_TYPES.STENCIL:
+ this.renderer.stencil.push(maskData);
+ break;
+ case exports.MASK_TYPES.SPRITE:
+ maskData.copyCountersOrReset(null);
+ this.pushSpriteMask(maskData);
+ break;
+ case exports.MASK_TYPES.COLOR:
+ this.pushColorMask(maskData);
+ break;
+ }
+ }
+ if (maskData.type === exports.MASK_TYPES.SPRITE) {
+ this.maskStack.push(maskData);
+ }
+ };
+ /**
+ * Removes the last mask from the mask stack and doesn't return it.
+ *
+ * NOTE: The batch renderer should be flushed beforehand to render the masked contents before the mask is removed.
+ * @param {PIXI.IMaskTarget} target - Display Object to pop the mask from
+ */
+ MaskSystem.prototype.pop = function (target) {
+ var maskData = this.maskStack.pop();
+ if (!maskData || maskData._target !== target) {
+ // TODO: add an assert when we have it
+ return;
+ }
+ if (maskData.enabled) {
+ switch (maskData.type) {
+ case exports.MASK_TYPES.SCISSOR:
+ this.renderer.scissor.pop(maskData);
+ break;
+ case exports.MASK_TYPES.STENCIL:
+ this.renderer.stencil.pop(maskData.maskObject);
+ break;
+ case exports.MASK_TYPES.SPRITE:
+ this.popSpriteMask(maskData);
+ break;
+ case exports.MASK_TYPES.COLOR:
+ this.popColorMask(maskData);
+ break;
+ }
+ }
+ maskData.reset();
+ if (maskData.pooled) {
+ this.maskDataPool.push(maskData);
+ }
+ if (this.maskStack.length !== 0) {
+ var maskCurrent = this.maskStack[this.maskStack.length - 1];
+ if (maskCurrent.type === exports.MASK_TYPES.SPRITE && maskCurrent._filters) {
+ maskCurrent._filters[0].maskSprite = maskCurrent.maskObject;
+ }
+ }
+ };
+ /**
+ * Sets type of MaskData based on its maskObject.
+ * @param maskData
+ */
+ MaskSystem.prototype.detect = function (maskData) {
+ var maskObject = maskData.maskObject;
+ if (!maskObject) {
+ maskData.type = exports.MASK_TYPES.COLOR;
+ }
+ else if (maskObject.isSprite) {
+ maskData.type = exports.MASK_TYPES.SPRITE;
+ }
+ else if (this.enableScissor && this.renderer.scissor.testScissor(maskData)) {
+ maskData.type = exports.MASK_TYPES.SCISSOR;
+ }
+ else {
+ maskData.type = exports.MASK_TYPES.STENCIL;
+ }
+ };
+ /**
+ * Applies the Mask and adds it to the current filter stack.
+ * @param maskData - Sprite to be used as the mask.
+ */
+ MaskSystem.prototype.pushSpriteMask = function (maskData) {
+ var _a, _b;
+ var maskObject = maskData.maskObject;
+ var target = maskData._target;
+ var alphaMaskFilter = maskData._filters;
+ if (!alphaMaskFilter) {
+ alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex];
+ if (!alphaMaskFilter) {
+ alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new SpriteMaskFilter()];
+ }
+ }
+ var renderer = this.renderer;
+ var renderTextureSystem = renderer.renderTexture;
+ var resolution;
+ var multisample;
+ if (renderTextureSystem.current) {
+ var renderTexture = renderTextureSystem.current;
+ resolution = maskData.resolution || renderTexture.resolution;
+ multisample = (_a = maskData.multisample) !== null && _a !== void 0 ? _a : renderTexture.multisample;
+ }
+ else {
+ resolution = maskData.resolution || renderer.resolution;
+ multisample = (_b = maskData.multisample) !== null && _b !== void 0 ? _b : renderer.multisample;
+ }
+ alphaMaskFilter[0].resolution = resolution;
+ alphaMaskFilter[0].multisample = multisample;
+ alphaMaskFilter[0].maskSprite = maskObject;
+ var stashFilterArea = target.filterArea;
+ target.filterArea = maskObject.getBounds(true);
+ renderer.filter.push(target, alphaMaskFilter);
+ target.filterArea = stashFilterArea;
+ if (!maskData._filters) {
+ this.alphaMaskIndex++;
+ }
+ };
+ /**
+ * Removes the last filter from the filter stack and doesn't return it.
+ * @param maskData - Sprite to be used as the mask.
+ */
+ MaskSystem.prototype.popSpriteMask = function (maskData) {
+ this.renderer.filter.pop();
+ if (maskData._filters) {
+ maskData._filters[0].maskSprite = null;
+ }
+ else {
+ this.alphaMaskIndex--;
+ this.alphaMaskPool[this.alphaMaskIndex][0].maskSprite = null;
+ }
+ };
+ /**
+ * Pushes the color mask.
+ * @param maskData - The mask data
+ */
+ MaskSystem.prototype.pushColorMask = function (maskData) {
+ var currColorMask = maskData._colorMask;
+ var nextColorMask = maskData._colorMask = currColorMask & maskData.colorMask;
+ if (nextColorMask !== currColorMask) {
+ this.renderer.gl.colorMask((nextColorMask & 0x1) !== 0, (nextColorMask & 0x2) !== 0, (nextColorMask & 0x4) !== 0, (nextColorMask & 0x8) !== 0);
+ }
+ };
+ /**
+ * Pops the color mask.
+ * @param maskData - The mask data
+ */
+ MaskSystem.prototype.popColorMask = function (maskData) {
+ var currColorMask = maskData._colorMask;
+ var nextColorMask = this.maskStack.length > 0
+ ? this.maskStack[this.maskStack.length - 1]._colorMask : 0xf;
+ if (nextColorMask !== currColorMask) {
+ this.renderer.gl.colorMask((nextColorMask & 0x1) !== 0, (nextColorMask & 0x2) !== 0, (nextColorMask & 0x4) !== 0, (nextColorMask & 0x8) !== 0);
+ }
+ };
+ MaskSystem.prototype.destroy = function () {
+ this.renderer = null;
+ };
+ return MaskSystem;
+ }());
+
+ /**
+ * System plugin to the renderer to manage specific types of masking operations.
+ * @memberof PIXI
+ */
+ var AbstractMaskSystem = /** @class */ (function () {
+ /**
+ * @param renderer - The renderer this System works for.
+ */
+ function AbstractMaskSystem(renderer) {
+ this.renderer = renderer;
+ this.maskStack = [];
+ this.glConst = 0;
+ }
+ /** Gets count of masks of certain type. */
+ AbstractMaskSystem.prototype.getStackLength = function () {
+ return this.maskStack.length;
+ };
+ /**
+ * Changes the mask stack that is used by this System.
+ * @param {PIXI.MaskData[]} maskStack - The mask stack
+ */
+ AbstractMaskSystem.prototype.setMaskStack = function (maskStack) {
+ var gl = this.renderer.gl;
+ var curStackLen = this.getStackLength();
+ this.maskStack = maskStack;
+ var newStackLen = this.getStackLength();
+ if (newStackLen !== curStackLen) {
+ if (newStackLen === 0) {
+ gl.disable(this.glConst);
+ }
+ else {
+ gl.enable(this.glConst);
+ this._useCurrent();
+ }
+ }
+ };
+ /**
+ * Setup renderer to use the current mask data.
+ * @private
+ */
+ AbstractMaskSystem.prototype._useCurrent = function () {
+ // OVERWRITE;
+ };
+ /** Destroys the mask stack. */
+ AbstractMaskSystem.prototype.destroy = function () {
+ this.renderer = null;
+ this.maskStack = null;
+ };
+ return AbstractMaskSystem;
+ }());
+
+ var tempMatrix$1 = new Matrix();
+ var rectPool = [];
+ /**
+ * System plugin to the renderer to manage scissor masking.
+ *
+ * Scissor masking discards pixels outside of a rectangle called the scissor box. The scissor box is in the framebuffer
+ * viewport's space; however, the mask's rectangle is projected from world-space to viewport space automatically
+ * by this system.
+ * @memberof PIXI
+ */
+ var ScissorSystem = /** @class */ (function (_super) {
+ __extends$i(ScissorSystem, _super);
+ /**
+ * @param {PIXI.Renderer} renderer - The renderer this System works for.
+ */
+ function ScissorSystem(renderer) {
+ var _this = _super.call(this, renderer) || this;
+ _this.glConst = settings$1.ADAPTER.getWebGLRenderingContext().SCISSOR_TEST;
+ return _this;
+ }
+ ScissorSystem.prototype.getStackLength = function () {
+ var maskData = this.maskStack[this.maskStack.length - 1];
+ if (maskData) {
+ return maskData._scissorCounter;
+ }
+ return 0;
+ };
+ /**
+ * evaluates _boundsTransformed, _scissorRect for MaskData
+ * @param maskData
+ */
+ ScissorSystem.prototype.calcScissorRect = function (maskData) {
+ var _a;
+ if (maskData._scissorRectLocal) {
+ return;
+ }
+ var prevData = maskData._scissorRect;
+ var maskObject = maskData.maskObject;
+ var renderer = this.renderer;
+ var renderTextureSystem = renderer.renderTexture;
+ var rect = maskObject.getBounds(true, (_a = rectPool.pop()) !== null && _a !== void 0 ? _a : new Rectangle());
+ this.roundFrameToPixels(rect, renderTextureSystem.current ? renderTextureSystem.current.resolution : renderer.resolution, renderTextureSystem.sourceFrame, renderTextureSystem.destinationFrame, renderer.projection.transform);
+ if (prevData) {
+ rect.fit(prevData);
+ }
+ maskData._scissorRectLocal = rect;
+ };
+ ScissorSystem.isMatrixRotated = function (matrix) {
+ if (!matrix) {
+ return false;
+ }
+ var a = matrix.a, b = matrix.b, c = matrix.c, d = matrix.d;
+ // Skip if skew/rotation present in matrix, except for multiple of 90掳 rotation. If rotation
+ // is a multiple of 90掳, then either pair of (b,c) or (a,d) will be (0,0).
+ return ((Math.abs(b) > 1e-4 || Math.abs(c) > 1e-4)
+ && (Math.abs(a) > 1e-4 || Math.abs(d) > 1e-4));
+ };
+ /**
+ * Test, whether the object can be scissor mask with current renderer projection.
+ * Calls "calcScissorRect()" if its true.
+ * @param maskData - mask data
+ * @returns whether Whether the object can be scissor mask
+ */
+ ScissorSystem.prototype.testScissor = function (maskData) {
+ var maskObject = maskData.maskObject;
+ if (!maskObject.isFastRect || !maskObject.isFastRect()) {
+ return false;
+ }
+ if (ScissorSystem.isMatrixRotated(maskObject.worldTransform)) {
+ return false;
+ }
+ if (ScissorSystem.isMatrixRotated(this.renderer.projection.transform)) {
+ return false;
+ }
+ this.calcScissorRect(maskData);
+ var rect = maskData._scissorRectLocal;
+ return rect.width > 0 && rect.height > 0;
+ };
+ ScissorSystem.prototype.roundFrameToPixels = function (frame, resolution, bindingSourceFrame, bindingDestinationFrame, transform) {
+ if (ScissorSystem.isMatrixRotated(transform)) {
+ return;
+ }
+ transform = transform ? tempMatrix$1.copyFrom(transform) : tempMatrix$1.identity();
+ // Get forward transform from world space to screen space
+ transform
+ .translate(-bindingSourceFrame.x, -bindingSourceFrame.y)
+ .scale(bindingDestinationFrame.width / bindingSourceFrame.width, bindingDestinationFrame.height / bindingSourceFrame.height)
+ .translate(bindingDestinationFrame.x, bindingDestinationFrame.y);
+ // Convert frame to screen space
+ this.renderer.filter.transformAABB(transform, frame);
+ frame.fit(bindingDestinationFrame);
+ frame.x = Math.round(frame.x * resolution);
+ frame.y = Math.round(frame.y * resolution);
+ frame.width = Math.round(frame.width * resolution);
+ frame.height = Math.round(frame.height * resolution);
+ };
+ /**
+ * Applies the Mask and adds it to the current stencil stack.
+ * @author alvin
+ * @param maskData - The mask data.
+ */
+ ScissorSystem.prototype.push = function (maskData) {
+ if (!maskData._scissorRectLocal) {
+ this.calcScissorRect(maskData);
+ }
+ var gl = this.renderer.gl;
+ if (!maskData._scissorRect) {
+ gl.enable(gl.SCISSOR_TEST);
+ }
+ maskData._scissorCounter++;
+ maskData._scissorRect = maskData._scissorRectLocal;
+ this._useCurrent();
+ };
+ /**
+ * This should be called after a mask is popped off the mask stack. It will rebind the scissor box to be latest with the
+ * last mask in the stack.
+ *
+ * This can also be called when you directly modify the scissor box and want to restore PixiJS state.
+ * @param maskData - The mask data.
+ */
+ ScissorSystem.prototype.pop = function (maskData) {
+ var gl = this.renderer.gl;
+ if (maskData) {
+ rectPool.push(maskData._scissorRectLocal);
+ }
+ if (this.getStackLength() > 0) {
+ this._useCurrent();
+ }
+ else {
+ gl.disable(gl.SCISSOR_TEST);
+ }
+ };
+ /**
+ * Setup renderer to use the current scissor data.
+ * @private
+ */
+ ScissorSystem.prototype._useCurrent = function () {
+ var rect = this.maskStack[this.maskStack.length - 1]._scissorRect;
+ var y;
+ if (this.renderer.renderTexture.current) {
+ y = rect.y;
+ }
+ else {
+ // flipY. In future we'll have it over renderTextures as an option
+ y = this.renderer.height - rect.height - rect.y;
+ }
+ this.renderer.gl.scissor(rect.x, y, rect.width, rect.height);
+ };
+ return ScissorSystem;
+ }(AbstractMaskSystem));
+
+ /**
+ * System plugin to the renderer to manage stencils (used for masks).
+ * @memberof PIXI
+ */
+ var StencilSystem = /** @class */ (function (_super) {
+ __extends$i(StencilSystem, _super);
+ /**
+ * @param renderer - The renderer this System works for.
+ */
+ function StencilSystem(renderer) {
+ var _this = _super.call(this, renderer) || this;
+ _this.glConst = settings$1.ADAPTER.getWebGLRenderingContext().STENCIL_TEST;
+ return _this;
+ }
+ StencilSystem.prototype.getStackLength = function () {
+ var maskData = this.maskStack[this.maskStack.length - 1];
+ if (maskData) {
+ return maskData._stencilCounter;
+ }
+ return 0;
+ };
+ /**
+ * Applies the Mask and adds it to the current stencil stack.
+ * @param maskData - The mask data
+ */
+ StencilSystem.prototype.push = function (maskData) {
+ var maskObject = maskData.maskObject;
+ var gl = this.renderer.gl;
+ var prevMaskCount = maskData._stencilCounter;
+ if (prevMaskCount === 0) {
+ // force use stencil texture in current framebuffer
+ this.renderer.framebuffer.forceStencil();
+ gl.clearStencil(0);
+ gl.clear(gl.STENCIL_BUFFER_BIT);
+ gl.enable(gl.STENCIL_TEST);
+ }
+ maskData._stencilCounter++;
+ var colorMask = maskData._colorMask;
+ if (colorMask !== 0) {
+ maskData._colorMask = 0;
+ gl.colorMask(false, false, false, false);
+ }
+ // Increment the reference stencil value where the new mask overlaps with the old ones.
+ gl.stencilFunc(gl.EQUAL, prevMaskCount, 0xFFFFFFFF);
+ gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR);
+ maskObject.renderable = true;
+ maskObject.render(this.renderer);
+ this.renderer.batch.flush();
+ maskObject.renderable = false;
+ if (colorMask !== 0) {
+ maskData._colorMask = colorMask;
+ gl.colorMask((colorMask & 1) !== 0, (colorMask & 2) !== 0, (colorMask & 4) !== 0, (colorMask & 8) !== 0);
+ }
+ this._useCurrent();
+ };
+ /**
+ * Pops stencil mask. MaskData is already removed from stack
+ * @param {PIXI.DisplayObject} maskObject - object of popped mask data
+ */
+ StencilSystem.prototype.pop = function (maskObject) {
+ var gl = this.renderer.gl;
+ if (this.getStackLength() === 0) {
+ // the stack is empty!
+ gl.disable(gl.STENCIL_TEST);
+ }
+ else {
+ var maskData = this.maskStack.length !== 0 ? this.maskStack[this.maskStack.length - 1] : null;
+ var colorMask = maskData ? maskData._colorMask : 0xf;
+ if (colorMask !== 0) {
+ maskData._colorMask = 0;
+ gl.colorMask(false, false, false, false);
+ }
+ // Decrement the reference stencil value where the popped mask overlaps with the other ones
+ gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR);
+ maskObject.renderable = true;
+ maskObject.render(this.renderer);
+ this.renderer.batch.flush();
+ maskObject.renderable = false;
+ if (colorMask !== 0) {
+ maskData._colorMask = colorMask;
+ gl.colorMask((colorMask & 0x1) !== 0, (colorMask & 0x2) !== 0, (colorMask & 0x4) !== 0, (colorMask & 0x8) !== 0);
+ }
+ this._useCurrent();
+ }
+ };
+ /**
+ * Setup renderer to use the current stencil data.
+ * @private
+ */
+ StencilSystem.prototype._useCurrent = function () {
+ var gl = this.renderer.gl;
+ gl.stencilFunc(gl.EQUAL, this.getStackLength(), 0xFFFFFFFF);
+ gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
+ };
+ return StencilSystem;
+ }(AbstractMaskSystem));
+
+ /**
+ * System plugin to the renderer to manage the projection matrix.
+ *
+ * The `projectionMatrix` is a global uniform provided to all shaders. It is used to transform points in world space to
+ * normalized device coordinates.
+ * @memberof PIXI
+ */
+ var ProjectionSystem = /** @class */ (function () {
+ /** @param renderer - The renderer this System works for. */
+ function ProjectionSystem(renderer) {
+ this.renderer = renderer;
+ this.destinationFrame = null;
+ this.sourceFrame = null;
+ this.defaultFrame = null;
+ this.projectionMatrix = new Matrix();
+ this.transform = null;
+ }
+ /**
+ * Updates the projection-matrix based on the sourceFrame 鈫� destinationFrame mapping provided.
+ *
+ * NOTE: It is expected you call `renderer.framebuffer.setViewport(destinationFrame)` after this. This is because
+ * the framebuffer viewport converts shader vertex output in normalized device coordinates to window coordinates.
+ *
+ * NOTE-2: {@link RenderTextureSystem#bind} updates the projection-matrix when you bind a render-texture. It is expected
+ * that you dirty the current bindings when calling this manually.
+ * @param destinationFrame - The rectangle in the render-target to render the contents into. If rendering to the canvas,
+ * the origin is on the top-left; if rendering to a render-texture, the origin is on the bottom-left.
+ * @param sourceFrame - The rectangle in world space that contains the contents being rendered.
+ * @param resolution - The resolution of the render-target, which is the ratio of
+ * world-space (or CSS) pixels to physical pixels.
+ * @param root - Whether the render-target is the screen. This is required because rendering to textures
+ * is y-flipped (i.e. upside down relative to the screen).
+ */
+ ProjectionSystem.prototype.update = function (destinationFrame, sourceFrame, resolution, root) {
+ this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame;
+ this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame;
+ // Calculate object-space to clip-space projection
+ this.calculateProjection(this.destinationFrame, this.sourceFrame, resolution, root);
+ if (this.transform) {
+ this.projectionMatrix.append(this.transform);
+ }
+ var renderer = this.renderer;
+ renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix;
+ renderer.globalUniforms.update();
+ // this will work for now
+ // but would be sweet to stick and even on the global uniforms..
+ if (renderer.shader.shader) {
+ renderer.shader.syncUniformGroup(renderer.shader.shader.uniforms.globals);
+ }
+ };
+ /**
+ * Calculates the `projectionMatrix` to map points inside `sourceFrame` to inside `destinationFrame`.
+ * @param _destinationFrame - The destination frame in the render-target.
+ * @param sourceFrame - The source frame in world space.
+ * @param _resolution - The render-target's resolution, i.e. ratio of CSS to physical pixels.
+ * @param root - Whether rendering into the screen. Otherwise, if rendering to a framebuffer, the projection
+ * is y-flipped.
+ */
+ ProjectionSystem.prototype.calculateProjection = function (_destinationFrame, sourceFrame, _resolution, root) {
+ var pm = this.projectionMatrix;
+ var sign = !root ? 1 : -1;
+ pm.identity();
+ pm.a = (1 / sourceFrame.width * 2);
+ pm.d = sign * (1 / sourceFrame.height * 2);
+ pm.tx = -1 - (sourceFrame.x * pm.a);
+ pm.ty = -sign - (sourceFrame.y * pm.d);
+ };
+ /**
+ * Sets the transform of the active render target to the given matrix.
+ * @param _matrix - The transformation matrix
+ */
+ ProjectionSystem.prototype.setTransform = function (_matrix) {
+ // this._activeRenderTarget.transform = matrix;
+ };
+ ProjectionSystem.prototype.destroy = function () {
+ this.renderer = null;
+ };
+ return ProjectionSystem;
+ }());
+
+ // Temporary rectangle for assigned sourceFrame or destinationFrame
+ var tempRect = new Rectangle();
+ // Temporary rectangle for renderTexture destinationFrame
+ var tempRect2 = new Rectangle();
+ /* eslint-disable max-len */
+ /**
+ * System plugin to the renderer to manage render textures.
+ *
+ * Should be added after FramebufferSystem
+ *
+ * ### Frames
+ *
+ * The `RenderTextureSystem` holds a sourceFrame 鈫� destinationFrame projection. The following table explains the different
+ * coordinate spaces used:
+ *
+ * | Frame | Description | Coordinate System |
+ * | ---------------------- | ---------------------------------------------------------------- | ------------------------------------------------------- |
+ * | sourceFrame | The rectangle inside of which display-objects are being rendered | **World Space**: The origin on the top-left |
+ * | destinationFrame | The rectangle in the render-target (canvas or texture) into which contents should be rendered | If rendering to the canvas, this is in screen space and the origin is on the top-left. If rendering to a render-texture, this is in its base-texture's space with the origin on the bottom-left. |
+ * | viewportFrame | The framebuffer viewport corresponding to the destination-frame | **Window Coordinates**: The origin is always on the bottom-left. |
+ * @memberof PIXI
+ */
+ var RenderTextureSystem = /** @class */ (function () {
+ /**
+ * @param renderer - The renderer this System works for.
+ */
+ function RenderTextureSystem(renderer) {
+ this.renderer = renderer;
+ this.clearColor = renderer._backgroundColorRgba;
+ this.defaultMaskStack = [];
+ this.current = null;
+ this.sourceFrame = new Rectangle();
+ this.destinationFrame = new Rectangle();
+ this.viewportFrame = new Rectangle();
+ }
+ /**
+ * Bind the current render texture.
+ * @param renderTexture - RenderTexture to bind, by default its `null` - the screen.
+ * @param sourceFrame - Part of world that is mapped to the renderTexture.
+ * @param destinationFrame - Part of renderTexture, by default it has the same size as sourceFrame.
+ */
+ RenderTextureSystem.prototype.bind = function (renderTexture, sourceFrame, destinationFrame) {
+ if (renderTexture === void 0) { renderTexture = null; }
+ var renderer = this.renderer;
+ this.current = renderTexture;
+ var baseTexture;
+ var framebuffer;
+ var resolution;
+ if (renderTexture) {
+ baseTexture = renderTexture.baseTexture;
+ resolution = baseTexture.resolution;
+ if (!sourceFrame) {
+ tempRect.width = renderTexture.frame.width;
+ tempRect.height = renderTexture.frame.height;
+ sourceFrame = tempRect;
+ }
+ if (!destinationFrame) {
+ tempRect2.x = renderTexture.frame.x;
+ tempRect2.y = renderTexture.frame.y;
+ tempRect2.width = sourceFrame.width;
+ tempRect2.height = sourceFrame.height;
+ destinationFrame = tempRect2;
+ }
+ framebuffer = baseTexture.framebuffer;
+ }
+ else {
+ resolution = renderer.resolution;
+ if (!sourceFrame) {
+ tempRect.width = renderer.screen.width;
+ tempRect.height = renderer.screen.height;
+ sourceFrame = tempRect;
+ }
+ if (!destinationFrame) {
+ destinationFrame = tempRect;
+ destinationFrame.width = sourceFrame.width;
+ destinationFrame.height = sourceFrame.height;
+ }
+ }
+ var viewportFrame = this.viewportFrame;
+ viewportFrame.x = destinationFrame.x * resolution;
+ viewportFrame.y = destinationFrame.y * resolution;
+ viewportFrame.width = destinationFrame.width * resolution;
+ viewportFrame.height = destinationFrame.height * resolution;
+ if (!renderTexture) {
+ viewportFrame.y = renderer.view.height - (viewportFrame.y + viewportFrame.height);
+ }
+ viewportFrame.ceil();
+ this.renderer.framebuffer.bind(framebuffer, viewportFrame);
+ this.renderer.projection.update(destinationFrame, sourceFrame, resolution, !framebuffer);
+ if (renderTexture) {
+ this.renderer.mask.setMaskStack(baseTexture.maskStack);
+ }
+ else {
+ this.renderer.mask.setMaskStack(this.defaultMaskStack);
+ }
+ this.sourceFrame.copyFrom(sourceFrame);
+ this.destinationFrame.copyFrom(destinationFrame);
+ };
+ /**
+ * Erases the render texture and fills the drawing area with a colour.
+ * @param clearColor - The color as rgba, default to use the renderer backgroundColor
+ * @param [mask=BUFFER_BITS.COLOR | BUFFER_BITS.DEPTH] - Bitwise OR of masks
+ * that indicate the buffers to be cleared, by default COLOR and DEPTH buffers.
+ */
+ RenderTextureSystem.prototype.clear = function (clearColor, mask) {
+ if (this.current) {
+ clearColor = clearColor || this.current.baseTexture.clearColor;
+ }
+ else {
+ clearColor = clearColor || this.clearColor;
+ }
+ var destinationFrame = this.destinationFrame;
+ var baseFrame = this.current ? this.current.baseTexture : this.renderer.screen;
+ var clearMask = destinationFrame.width !== baseFrame.width || destinationFrame.height !== baseFrame.height;
+ if (clearMask) {
+ var _a = this.viewportFrame, x = _a.x, y = _a.y, width = _a.width, height = _a.height;
+ x = Math.round(x);
+ y = Math.round(y);
+ width = Math.round(width);
+ height = Math.round(height);
+ // TODO: ScissorSystem should cache whether the scissor test is enabled or not.
+ this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST);
+ this.renderer.gl.scissor(x, y, width, height);
+ }
+ this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3], mask);
+ if (clearMask) {
+ // Restore the scissor box
+ this.renderer.scissor.pop();
+ }
+ };
+ RenderTextureSystem.prototype.resize = function () {
+ // resize the root only!
+ this.bind(null);
+ };
+ /** Resets render-texture state. */
+ RenderTextureSystem.prototype.reset = function () {
+ this.bind(null);
+ };
+ RenderTextureSystem.prototype.destroy = function () {
+ this.renderer = null;
+ };
+ return RenderTextureSystem;
+ }());
+
+ function uboUpdate(_ud, _uv, _renderer, _syncData, buffer) {
+ _renderer.buffer.update(buffer);
+ }
+ // cv = CachedValue
+ // v = value
+ // ud = uniformData
+ // uv = uniformValue
+ // l = location
+ var UBO_TO_SINGLE_SETTERS = {
+ float: "\n data[offset] = v;\n ",
+ vec2: "\n data[offset] = v[0];\n data[offset+1] = v[1];\n ",
+ vec3: "\n data[offset] = v[0];\n data[offset+1] = v[1];\n data[offset+2] = v[2];\n\n ",
+ vec4: "\n data[offset] = v[0];\n data[offset+1] = v[1];\n data[offset+2] = v[2];\n data[offset+3] = v[3];\n ",
+ mat2: "\n data[offset] = v[0];\n data[offset+1] = v[1];\n\n data[offset+4] = v[2];\n data[offset+5] = v[3];\n ",
+ mat3: "\n data[offset] = v[0];\n data[offset+1] = v[1];\n data[offset+2] = v[2];\n\n data[offset + 4] = v[3];\n data[offset + 5] = v[4];\n data[offset + 6] = v[5];\n\n data[offset + 8] = v[6];\n data[offset + 9] = v[7];\n data[offset + 10] = v[8];\n ",
+ mat4: "\n for(var i = 0; i < 16; i++)\n {\n data[offset + i] = v[i];\n }\n "
+ };
+ var GLSL_TO_STD40_SIZE = {
+ float: 4,
+ vec2: 8,
+ vec3: 12,
+ vec4: 16,
+ int: 4,
+ ivec2: 8,
+ ivec3: 12,
+ ivec4: 16,
+ uint: 4,
+ uvec2: 8,
+ uvec3: 12,
+ uvec4: 16,
+ bool: 4,
+ bvec2: 8,
+ bvec3: 12,
+ bvec4: 16,
+ mat2: 16 * 2,
+ mat3: 16 * 3,
+ mat4: 16 * 4,
+ };
+ /**
+ * logic originally from here: https://github.com/sketchpunk/FunWithWebGL2/blob/master/lesson_022/Shaders.js
+ * rewrote it, but this was a great starting point to get a solid understanding of whats going on :)
+ * @ignore
+ * @param uniformData
+ */
+ function createUBOElements(uniformData) {
+ var uboElements = uniformData.map(function (data) {
+ return ({
+ data: data,
+ offset: 0,
+ dataLen: 0,
+ dirty: 0
+ });
+ });
+ var size = 0;
+ var chunkSize = 0;
+ var offset = 0;
+ for (var i = 0; i < uboElements.length; i++) {
+ var uboElement = uboElements[i];
+ size = GLSL_TO_STD40_SIZE[uboElement.data.type];
+ if (uboElement.data.size > 1) {
+ size = Math.max(size, 16) * uboElement.data.size;
+ }
+ uboElement.dataLen = size;
+ // add some size offset..
+ // must align to the nearest 16 bytes or internally nearest round size
+ if (chunkSize % size !== 0 && chunkSize < 16) {
+ // diff required to line up..
+ var lineUpValue = (chunkSize % size) % 16;
+ chunkSize += lineUpValue;
+ offset += lineUpValue;
+ }
+ if ((chunkSize + size) > 16) {
+ offset = Math.ceil(offset / 16) * 16;
+ uboElement.offset = offset;
+ offset += size;
+ chunkSize = size;
+ }
+ else {
+ uboElement.offset = offset;
+ chunkSize += size;
+ offset += size;
+ }
+ }
+ offset = Math.ceil(offset / 16) * 16;
+ return { uboElements: uboElements, size: offset };
+ }
+ function getUBOData(uniforms, uniformData) {
+ var usedUniformDatas = [];
+ // build..
+ for (var i in uniforms) {
+ if (uniformData[i]) {
+ usedUniformDatas.push(uniformData[i]);
+ }
+ }
+ // sort them out by index!
+ usedUniformDatas.sort(function (a, b) { return a.index - b.index; });
+ return usedUniformDatas;
+ }
+ function generateUniformBufferSync(group, uniformData) {
+ if (!group.autoManage) {
+ // if the group is nott automatically managed, we don't need to generate a special function for it...
+ return { size: 0, syncFunc: uboUpdate };
+ }
+ var usedUniformDatas = getUBOData(group.uniforms, uniformData);
+ var _a = createUBOElements(usedUniformDatas), uboElements = _a.uboElements, size = _a.size;
+ var funcFragments = ["\n var v = null;\n var v2 = null;\n var cv = null;\n var t = 0;\n var gl = renderer.gl\n var index = 0;\n var data = buffer.data;\n "];
+ for (var i = 0; i < uboElements.length; i++) {
+ var uboElement = uboElements[i];
+ var uniform = group.uniforms[uboElement.data.name];
+ var name = uboElement.data.name;
+ var parsed = false;
+ for (var j = 0; j < uniformParsers.length; j++) {
+ var uniformParser = uniformParsers[j];
+ if (uniformParser.codeUbo && uniformParser.test(uboElement.data, uniform)) {
+ funcFragments.push("offset = " + uboElement.offset / 4 + ";", uniformParsers[j].codeUbo(uboElement.data.name, uniform));
+ parsed = true;
+ break;
+ }
+ }
+ if (!parsed) {
+ if (uboElement.data.size > 1) {
+ var size_1 = mapSize(uboElement.data.type);
+ var rowSize = Math.max(GLSL_TO_STD40_SIZE[uboElement.data.type] / 16, 1);
+ var elementSize = size_1 / rowSize;
+ var remainder = (4 - (elementSize % 4)) % 4;
+ funcFragments.push("\n cv = ud." + name + ".value;\n v = uv." + name + ";\n offset = " + uboElement.offset / 4 + ";\n\n t = 0;\n\n for(var i=0; i < " + uboElement.data.size * rowSize + "; i++)\n {\n for(var j = 0; j < " + elementSize + "; j++)\n {\n data[offset++] = v[t++];\n }\n offset += " + remainder + ";\n }\n\n ");
+ }
+ else {
+ var template = UBO_TO_SINGLE_SETTERS[uboElement.data.type];
+ funcFragments.push("\n cv = ud." + name + ".value;\n v = uv." + name + ";\n offset = " + uboElement.offset / 4 + ";\n " + template + ";\n ");
+ }
+ }
+ }
+ funcFragments.push("\n renderer.buffer.update(buffer);\n ");
+ return {
+ size: size,
+ // eslint-disable-next-line no-new-func
+ syncFunc: new Function('ud', 'uv', 'renderer', 'syncData', 'buffer', funcFragments.join('\n'))
+ };
+ }
+
+ /**
+ * @private
+ */
+ var IGLUniformData = /** @class */ (function () {
+ function IGLUniformData() {
+ }
+ return IGLUniformData;
+ }());
+ /**
+ * Helper class to create a WebGL Program
+ * @memberof PIXI
+ */
+ var GLProgram = /** @class */ (function () {
+ /**
+ * Makes a new Pixi program.
+ * @param program - webgl program
+ * @param uniformData - uniforms
+ */
+ function GLProgram(program, uniformData) {
+ this.program = program;
+ this.uniformData = uniformData;
+ this.uniformGroups = {};
+ this.uniformDirtyGroups = {};
+ this.uniformBufferBindings = {};
+ }
+ /** Destroys this program. */
+ GLProgram.prototype.destroy = function () {
+ this.uniformData = null;
+ this.uniformGroups = null;
+ this.uniformDirtyGroups = null;
+ this.uniformBufferBindings = null;
+ this.program = null;
+ };
+ return GLProgram;
+ }());
+
+ /**
+ * returns the attribute data from the program
+ * @private
+ * @param {WebGLProgram} [program] - the WebGL program
+ * @param {WebGLRenderingContext} [gl] - the WebGL context
+ * @returns {object} the attribute data for this program
+ */
+ function getAttributeData(program, gl) {
+ var attributes = {};
+ var totalAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
+ for (var i = 0; i < totalAttributes; i++) {
+ var attribData = gl.getActiveAttrib(program, i);
+ if (attribData.name.indexOf('gl_') === 0) {
+ continue;
+ }
+ var type = mapType(gl, attribData.type);
+ var data = {
+ type: type,
+ name: attribData.name,
+ size: mapSize(type),
+ location: gl.getAttribLocation(program, attribData.name),
+ };
+ attributes[attribData.name] = data;
+ }
+ return attributes;
+ }
+
+ /**
+ * returns the uniform data from the program
+ * @private
+ * @param program - the webgl program
+ * @param gl - the WebGL context
+ * @returns {object} the uniform data for this program
+ */
+ function getUniformData(program, gl) {
+ var uniforms = {};
+ var totalUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
+ for (var i = 0; i < totalUniforms; i++) {
+ var uniformData = gl.getActiveUniform(program, i);
+ var name = uniformData.name.replace(/\[.*?\]$/, '');
+ var isArray = !!(uniformData.name.match(/\[.*?\]$/));
+ var type = mapType(gl, uniformData.type);
+ uniforms[name] = {
+ name: name,
+ index: i,
+ type: type,
+ size: uniformData.size,
+ isArray: isArray,
+ value: defaultValue(type, uniformData.size),
+ };
+ }
+ return uniforms;
+ }
+
+ /**
+ * generates a WebGL Program object from a high level Pixi Program.
+ * @param gl - a rendering context on which to generate the program
+ * @param program - the high level Pixi Program.
+ */
+ function generateProgram(gl, program) {
+ var glVertShader = compileShader(gl, gl.VERTEX_SHADER, program.vertexSrc);
+ var glFragShader = compileShader(gl, gl.FRAGMENT_SHADER, program.fragmentSrc);
+ var webGLProgram = gl.createProgram();
+ gl.attachShader(webGLProgram, glVertShader);
+ gl.attachShader(webGLProgram, glFragShader);
+ gl.linkProgram(webGLProgram);
+ if (!gl.getProgramParameter(webGLProgram, gl.LINK_STATUS)) {
+ logProgramError(gl, webGLProgram, glVertShader, glFragShader);
+ }
+ program.attributeData = getAttributeData(webGLProgram, gl);
+ program.uniformData = getUniformData(webGLProgram, gl);
+ // GLSL 1.00: bind attributes sorted by name in ascending order
+ // GLSL 3.00: don't change the attribute locations that where chosen by the compiler
+ // or assigned by the layout specifier in the shader source code
+ if (!(/^[ \t]*#[ \t]*version[ \t]+300[ \t]+es[ \t]*$/m).test(program.vertexSrc)) {
+ var keys = Object.keys(program.attributeData);
+ keys.sort(function (a, b) { return (a > b) ? 1 : -1; }); // eslint-disable-line no-confusing-arrow
+ for (var i = 0; i < keys.length; i++) {
+ program.attributeData[keys[i]].location = i;
+ gl.bindAttribLocation(webGLProgram, i, keys[i]);
+ }
+ gl.linkProgram(webGLProgram);
+ }
+ gl.deleteShader(glVertShader);
+ gl.deleteShader(glFragShader);
+ var uniformData = {};
+ for (var i in program.uniformData) {
+ var data = program.uniformData[i];
+ uniformData[i] = {
+ location: gl.getUniformLocation(webGLProgram, i),
+ value: defaultValue(data.type, data.size),
+ };
+ }
+ var glProgram = new GLProgram(webGLProgram, uniformData);
+ return glProgram;
+ }
+
+ var UID = 0;
+ // default sync data so we don't create a new one each time!
+ var defaultSyncData = { textureCount: 0, uboCount: 0 };
+ /**
+ * System plugin to the renderer to manage shaders.
+ * @memberof PIXI
+ */
+ var ShaderSystem = /** @class */ (function () {
+ /** @param renderer - The renderer this System works for. */
+ function ShaderSystem(renderer) {
+ this.destroyed = false;
+ this.renderer = renderer;
+ // Validation check that this environment support `new Function`
+ this.systemCheck();
+ this.gl = null;
+ this.shader = null;
+ this.program = null;
+ this.cache = {};
+ this._uboCache = {};
+ this.id = UID++;
+ }
+ /**
+ * Overrideable function by `@pixi/unsafe-eval` to silence
+ * throwing an error if platform doesn't support unsafe-evals.
+ * @private
+ */
+ ShaderSystem.prototype.systemCheck = function () {
+ if (!unsafeEvalSupported()) {
+ throw new Error('Current environment does not allow unsafe-eval, '
+ + 'please use @pixi/unsafe-eval module to enable support.');
+ }
+ };
+ ShaderSystem.prototype.contextChange = function (gl) {
+ this.gl = gl;
+ this.reset();
+ };
+ /**
+ * Changes the current shader to the one given in parameter.
+ * @param shader - the new shader
+ * @param dontSync - false if the shader should automatically sync its uniforms.
+ * @returns the glProgram that belongs to the shader.
+ */
+ ShaderSystem.prototype.bind = function (shader, dontSync) {
+ shader.uniforms.globals = this.renderer.globalUniforms;
+ var program = shader.program;
+ var glProgram = program.glPrograms[this.renderer.CONTEXT_UID] || this.generateProgram(shader);
+ this.shader = shader;
+ // TODO - some current Pixi plugins bypass this.. so it not safe to use yet..
+ if (this.program !== program) {
+ this.program = program;
+ this.gl.useProgram(glProgram.program);
+ }
+ if (!dontSync) {
+ defaultSyncData.textureCount = 0;
+ defaultSyncData.uboCount = 0;
+ this.syncUniformGroup(shader.uniformGroup, defaultSyncData);
+ }
+ return glProgram;
+ };
+ /**
+ * Uploads the uniforms values to the currently bound shader.
+ * @param uniforms - the uniforms values that be applied to the current shader
+ */
+ ShaderSystem.prototype.setUniforms = function (uniforms) {
+ var shader = this.shader.program;
+ var glProgram = shader.glPrograms[this.renderer.CONTEXT_UID];
+ shader.syncUniforms(glProgram.uniformData, uniforms, this.renderer);
+ };
+ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
+ /**
+ * Syncs uniforms on the group
+ * @param group - the uniform group to sync
+ * @param syncData - this is data that is passed to the sync function and any nested sync functions
+ */
+ ShaderSystem.prototype.syncUniformGroup = function (group, syncData) {
+ var glProgram = this.getGlProgram();
+ if (!group.static || group.dirtyId !== glProgram.uniformDirtyGroups[group.id]) {
+ glProgram.uniformDirtyGroups[group.id] = group.dirtyId;
+ this.syncUniforms(group, glProgram, syncData);
+ }
+ };
+ /**
+ * Overrideable by the @pixi/unsafe-eval package to use static syncUniforms instead.
+ * @param group
+ * @param glProgram
+ * @param syncData
+ */
+ ShaderSystem.prototype.syncUniforms = function (group, glProgram, syncData) {
+ var syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer, syncData);
+ };
+ ShaderSystem.prototype.createSyncGroups = function (group) {
+ var id = this.getSignature(group, this.shader.program.uniformData, 'u');
+ if (!this.cache[id]) {
+ this.cache[id] = generateUniformsSync(group, this.shader.program.uniformData);
+ }
+ group.syncUniforms[this.shader.program.id] = this.cache[id];
+ return group.syncUniforms[this.shader.program.id];
+ };
+ /**
+ * Syncs uniform buffers
+ * @param group - the uniform buffer group to sync
+ * @param name - the name of the uniform buffer
+ */
+ ShaderSystem.prototype.syncUniformBufferGroup = function (group, name) {
+ var glProgram = this.getGlProgram();
+ if (!group.static || group.dirtyId !== 0 || !glProgram.uniformGroups[group.id]) {
+ group.dirtyId = 0;
+ var syncFunc = glProgram.uniformGroups[group.id]
+ || this.createSyncBufferGroup(group, glProgram, name);
+ // TODO wrap update in a cache??
+ group.buffer.update();
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer, defaultSyncData, group.buffer);
+ }
+ this.renderer.buffer.bindBufferBase(group.buffer, glProgram.uniformBufferBindings[name]);
+ };
+ /**
+ * Will create a function that uploads a uniform buffer using the STD140 standard.
+ * The upload function will then be cached for future calls
+ * If a group is manually managed, then a simple upload function is generated
+ * @param group - the uniform buffer group to sync
+ * @param glProgram - the gl program to attach the uniform bindings to
+ * @param name - the name of the uniform buffer (must exist on the shader)
+ */
+ ShaderSystem.prototype.createSyncBufferGroup = function (group, glProgram, name) {
+ var gl = this.renderer.gl;
+ this.renderer.buffer.bind(group.buffer);
+ // bind them...
+ var uniformBlockIndex = this.gl.getUniformBlockIndex(glProgram.program, name);
+ glProgram.uniformBufferBindings[name] = this.shader.uniformBindCount;
+ gl.uniformBlockBinding(glProgram.program, uniformBlockIndex, this.shader.uniformBindCount);
+ this.shader.uniformBindCount++;
+ var id = this.getSignature(group, this.shader.program.uniformData, 'ubo');
+ var uboData = this._uboCache[id];
+ if (!uboData) {
+ uboData = this._uboCache[id] = generateUniformBufferSync(group, this.shader.program.uniformData);
+ }
+ if (group.autoManage) {
+ var data = new Float32Array(uboData.size / 4);
+ group.buffer.update(data);
+ }
+ glProgram.uniformGroups[group.id] = uboData.syncFunc;
+ return glProgram.uniformGroups[group.id];
+ };
+ /**
+ * Takes a uniform group and data and generates a unique signature for them.
+ * @param group - The uniform group to get signature of
+ * @param group.uniforms
+ * @param uniformData - Uniform information generated by the shader
+ * @param preFix
+ * @returns Unique signature of the uniform group
+ */
+ ShaderSystem.prototype.getSignature = function (group, uniformData, preFix) {
+ var uniforms = group.uniforms;
+ var strings = [preFix + "-"];
+ for (var i in uniforms) {
+ strings.push(i);
+ if (uniformData[i]) {
+ strings.push(uniformData[i].type);
+ }
+ }
+ return strings.join('-');
+ };
+ /**
+ * Returns the underlying GLShade rof the currently bound shader.
+ *
+ * This can be handy for when you to have a little more control over the setting of your uniforms.
+ * @returns The glProgram for the currently bound Shader for this context
+ */
+ ShaderSystem.prototype.getGlProgram = function () {
+ if (this.shader) {
+ return this.shader.program.glPrograms[this.renderer.CONTEXT_UID];
+ }
+ return null;
+ };
+ /**
+ * Generates a glProgram version of the Shader provided.
+ * @param shader - The shader that the glProgram will be based on.
+ * @returns A shiny new glProgram!
+ */
+ ShaderSystem.prototype.generateProgram = function (shader) {
+ var gl = this.gl;
+ var program = shader.program;
+ var glProgram = generateProgram(gl, program);
+ program.glPrograms[this.renderer.CONTEXT_UID] = glProgram;
+ return glProgram;
+ };
+ /** Resets ShaderSystem state, does not affect WebGL state. */
+ ShaderSystem.prototype.reset = function () {
+ this.program = null;
+ this.shader = null;
+ };
+ /** Destroys this System and removes all its textures. */
+ ShaderSystem.prototype.destroy = function () {
+ this.renderer = null;
+ // TODO implement destroy method for ShaderSystem
+ this.destroyed = true;
+ };
+ return ShaderSystem;
+ }());
+
+ /**
+ * Maps gl blend combinations to WebGL.
+ * @memberof PIXI
+ * @function mapWebGLBlendModesToPixi
+ * @private
+ * @param {WebGLRenderingContext} gl - The rendering context.
+ * @param {number[][]} [array=[]] - The array to output into.
+ * @returns {number[][]} Mapped modes.
+ */
+ function mapWebGLBlendModesToPixi(gl, array) {
+ if (array === void 0) { array = []; }
+ // TODO - premultiply alpha would be different.
+ // add a boolean for that!
+ array[exports.BLEND_MODES.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ array[exports.BLEND_MODES.ADD] = [gl.ONE, gl.ONE];
+ array[exports.BLEND_MODES.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ array[exports.BLEND_MODES.SCREEN] = [gl.ONE, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ array[exports.BLEND_MODES.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ array[exports.BLEND_MODES.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ array[exports.BLEND_MODES.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ array[exports.BLEND_MODES.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ array[exports.BLEND_MODES.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ array[exports.BLEND_MODES.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ array[exports.BLEND_MODES.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ array[exports.BLEND_MODES.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ array[exports.BLEND_MODES.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ array[exports.BLEND_MODES.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ array[exports.BLEND_MODES.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ array[exports.BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ array[exports.BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ array[exports.BLEND_MODES.NONE] = [0, 0];
+ // not-premultiplied blend modes
+ array[exports.BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ array[exports.BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.ONE, gl.ONE, gl.ONE];
+ array[exports.BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ // composite operations
+ array[exports.BLEND_MODES.SRC_IN] = [gl.DST_ALPHA, gl.ZERO];
+ array[exports.BLEND_MODES.SRC_OUT] = [gl.ONE_MINUS_DST_ALPHA, gl.ZERO];
+ array[exports.BLEND_MODES.SRC_ATOP] = [gl.DST_ALPHA, gl.ONE_MINUS_SRC_ALPHA];
+ array[exports.BLEND_MODES.DST_OVER] = [gl.ONE_MINUS_DST_ALPHA, gl.ONE];
+ array[exports.BLEND_MODES.DST_IN] = [gl.ZERO, gl.SRC_ALPHA];
+ array[exports.BLEND_MODES.DST_OUT] = [gl.ZERO, gl.ONE_MINUS_SRC_ALPHA];
+ array[exports.BLEND_MODES.DST_ATOP] = [gl.ONE_MINUS_DST_ALPHA, gl.SRC_ALPHA];
+ array[exports.BLEND_MODES.XOR] = [gl.ONE_MINUS_DST_ALPHA, gl.ONE_MINUS_SRC_ALPHA];
+ // SUBTRACT from flash
+ array[exports.BLEND_MODES.SUBTRACT] = [gl.ONE, gl.ONE, gl.ONE, gl.ONE, gl.FUNC_REVERSE_SUBTRACT, gl.FUNC_ADD];
+ return array;
+ }
+
+ var BLEND = 0;
+ var OFFSET = 1;
+ var CULLING = 2;
+ var DEPTH_TEST = 3;
+ var WINDING = 4;
+ var DEPTH_MASK = 5;
+ /**
+ * System plugin to the renderer to manage WebGL state machines.
+ * @memberof PIXI
+ */
+ var StateSystem = /** @class */ (function () {
+ function StateSystem() {
+ this.gl = null;
+ this.stateId = 0;
+ this.polygonOffset = 0;
+ this.blendMode = exports.BLEND_MODES.NONE;
+ this._blendEq = false;
+ // map functions for when we set state..
+ this.map = [];
+ this.map[BLEND] = this.setBlend;
+ this.map[OFFSET] = this.setOffset;
+ this.map[CULLING] = this.setCullFace;
+ this.map[DEPTH_TEST] = this.setDepthTest;
+ this.map[WINDING] = this.setFrontFace;
+ this.map[DEPTH_MASK] = this.setDepthMask;
+ this.checks = [];
+ this.defaultState = new State();
+ this.defaultState.blend = true;
+ }
+ StateSystem.prototype.contextChange = function (gl) {
+ this.gl = gl;
+ this.blendModes = mapWebGLBlendModesToPixi(gl);
+ this.set(this.defaultState);
+ this.reset();
+ };
+ /**
+ * Sets the current state
+ * @param {*} state - The state to set.
+ */
+ StateSystem.prototype.set = function (state) {
+ state = state || this.defaultState;
+ // TODO maybe to an object check? ( this.state === state )?
+ if (this.stateId !== state.data) {
+ var diff = this.stateId ^ state.data;
+ var i = 0;
+ // order from least to most common
+ while (diff) {
+ if (diff & 1) {
+ // state change!
+ this.map[i].call(this, !!(state.data & (1 << i)));
+ }
+ diff = diff >> 1;
+ i++;
+ }
+ this.stateId = state.data;
+ }
+ // based on the above settings we check for specific modes..
+ // for example if blend is active we check and set the blend modes
+ // or of polygon offset is active we check the poly depth.
+ for (var i = 0; i < this.checks.length; i++) {
+ this.checks[i](this, state);
+ }
+ };
+ /**
+ * Sets the state, when previous state is unknown.
+ * @param {*} state - The state to set
+ */
+ StateSystem.prototype.forceState = function (state) {
+ state = state || this.defaultState;
+ for (var i = 0; i < this.map.length; i++) {
+ this.map[i].call(this, !!(state.data & (1 << i)));
+ }
+ for (var i = 0; i < this.checks.length; i++) {
+ this.checks[i](this, state);
+ }
+ this.stateId = state.data;
+ };
+ /**
+ * Sets whether to enable or disable blending.
+ * @param value - Turn on or off WebGl blending.
+ */
+ StateSystem.prototype.setBlend = function (value) {
+ this.updateCheck(StateSystem.checkBlendMode, value);
+ this.gl[value ? 'enable' : 'disable'](this.gl.BLEND);
+ };
+ /**
+ * Sets whether to enable or disable polygon offset fill.
+ * @param value - Turn on or off webgl polygon offset testing.
+ */
+ StateSystem.prototype.setOffset = function (value) {
+ this.updateCheck(StateSystem.checkPolygonOffset, value);
+ this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL);
+ };
+ /**
+ * Sets whether to enable or disable depth test.
+ * @param value - Turn on or off webgl depth testing.
+ */
+ StateSystem.prototype.setDepthTest = function (value) {
+ this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST);
+ };
+ /**
+ * Sets whether to enable or disable depth mask.
+ * @param value - Turn on or off webgl depth mask.
+ */
+ StateSystem.prototype.setDepthMask = function (value) {
+ this.gl.depthMask(value);
+ };
+ /**
+ * Sets whether to enable or disable cull face.
+ * @param {boolean} value - Turn on or off webgl cull face.
+ */
+ StateSystem.prototype.setCullFace = function (value) {
+ this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE);
+ };
+ /**
+ * Sets the gl front face.
+ * @param {boolean} value - true is clockwise and false is counter-clockwise
+ */
+ StateSystem.prototype.setFrontFace = function (value) {
+ this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']);
+ };
+ /**
+ * Sets the blend mode.
+ * @param {number} value - The blend mode to set to.
+ */
+ StateSystem.prototype.setBlendMode = function (value) {
+ if (value === this.blendMode) {
+ return;
+ }
+ this.blendMode = value;
+ var mode = this.blendModes[value];
+ var gl = this.gl;
+ if (mode.length === 2) {
+ gl.blendFunc(mode[0], mode[1]);
+ }
+ else {
+ gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]);
+ }
+ if (mode.length === 6) {
+ this._blendEq = true;
+ gl.blendEquationSeparate(mode[4], mode[5]);
+ }
+ else if (this._blendEq) {
+ this._blendEq = false;
+ gl.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD);
+ }
+ };
+ /**
+ * Sets the polygon offset.
+ * @param {number} value - the polygon offset
+ * @param {number} scale - the polygon offset scale
+ */
+ StateSystem.prototype.setPolygonOffset = function (value, scale) {
+ this.gl.polygonOffset(value, scale);
+ };
+ // used
+ /** Resets all the logic and disables the VAOs. */
+ StateSystem.prototype.reset = function () {
+ this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false);
+ this.forceState(this.defaultState);
+ this._blendEq = true;
+ this.blendMode = -1;
+ this.setBlendMode(0);
+ };
+ /**
+ * Checks to see which updates should be checked based on which settings have been activated.
+ *
+ * For example, if blend is enabled then we should check the blend modes each time the state is changed
+ * or if polygon fill is activated then we need to check if the polygon offset changes.
+ * The idea is that we only check what we have too.
+ * @param func - the checking function to add or remove
+ * @param value - should the check function be added or removed.
+ */
+ StateSystem.prototype.updateCheck = function (func, value) {
+ var index = this.checks.indexOf(func);
+ if (value && index === -1) {
+ this.checks.push(func);
+ }
+ else if (!value && index !== -1) {
+ this.checks.splice(index, 1);
+ }
+ };
+ /**
+ * A private little wrapper function that we call to check the blend mode.
+ * @param system - the System to perform the state check on
+ * @param state - the state that the blendMode will pulled from
+ */
+ StateSystem.checkBlendMode = function (system, state) {
+ system.setBlendMode(state.blendMode);
+ };
+ /**
+ * A private little wrapper function that we call to check the polygon offset.
+ * @param system - the System to perform the state check on
+ * @param state - the state that the blendMode will pulled from
+ */
+ StateSystem.checkPolygonOffset = function (system, state) {
+ system.setPolygonOffset(1, state.polygonOffset);
+ };
+ /**
+ * @ignore
+ */
+ StateSystem.prototype.destroy = function () {
+ this.gl = null;
+ };
+ return StateSystem;
+ }());
+
+ /**
+ * System plugin to the renderer to manage texture garbage collection on the GPU,
+ * ensuring that it does not get clogged up with textures that are no longer being used.
+ * @memberof PIXI
+ */
+ var TextureGCSystem = /** @class */ (function () {
+ /** @param renderer - The renderer this System works for. */
+ function TextureGCSystem(renderer) {
+ this.renderer = renderer;
+ this.count = 0;
+ this.checkCount = 0;
+ this.maxIdle = settings$1.GC_MAX_IDLE;
+ this.checkCountMax = settings$1.GC_MAX_CHECK_COUNT;
+ this.mode = settings$1.GC_MODE;
+ }
+ /**
+ * Checks to see when the last time a texture was used
+ * if the texture has not been used for a specified amount of time it will be removed from the GPU
+ */
+ TextureGCSystem.prototype.postrender = function () {
+ if (!this.renderer.renderingToScreen) {
+ return;
+ }
+ this.count++;
+ if (this.mode === exports.GC_MODES.MANUAL) {
+ return;
+ }
+ this.checkCount++;
+ if (this.checkCount > this.checkCountMax) {
+ this.checkCount = 0;
+ this.run();
+ }
+ };
+ /**
+ * Checks to see when the last time a texture was used
+ * if the texture has not been used for a specified amount of time it will be removed from the GPU
+ */
+ TextureGCSystem.prototype.run = function () {
+ var tm = this.renderer.texture;
+ var managedTextures = tm.managedTextures;
+ var wasRemoved = false;
+ for (var i = 0; i < managedTextures.length; i++) {
+ var texture = managedTextures[i];
+ // only supports non generated textures at the moment!
+ if (!texture.framebuffer && this.count - texture.touched > this.maxIdle) {
+ tm.destroyTexture(texture, true);
+ managedTextures[i] = null;
+ wasRemoved = true;
+ }
+ }
+ if (wasRemoved) {
+ var j = 0;
+ for (var i = 0; i < managedTextures.length; i++) {
+ if (managedTextures[i] !== null) {
+ managedTextures[j++] = managedTextures[i];
+ }
+ }
+ managedTextures.length = j;
+ }
+ };
+ /**
+ * Removes all the textures within the specified displayObject and its children from the GPU
+ * @param {PIXI.DisplayObject} displayObject - the displayObject to remove the textures from.
+ */
+ TextureGCSystem.prototype.unload = function (displayObject) {
+ var tm = this.renderer.texture;
+ var texture = displayObject._texture;
+ // only destroy non generated textures
+ if (texture && !texture.framebuffer) {
+ tm.destroyTexture(texture);
+ }
+ for (var i = displayObject.children.length - 1; i >= 0; i--) {
+ this.unload(displayObject.children[i]);
+ }
+ };
+ TextureGCSystem.prototype.destroy = function () {
+ this.renderer = null;
+ };
+ return TextureGCSystem;
+ }());
+
+ /**
+ * Returns a lookup table that maps each type-format pair to a compatible internal format.
+ * @memberof PIXI
+ * @function mapTypeAndFormatToInternalFormat
+ * @private
+ * @param {WebGLRenderingContext} gl - The rendering context.
+ * @returns Lookup table.
+ */
+ function mapTypeAndFormatToInternalFormat(gl) {
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
+ var table;
+ if ('WebGL2RenderingContext' in globalThis && gl instanceof globalThis.WebGL2RenderingContext) {
+ table = (_a = {},
+ _a[exports.TYPES.UNSIGNED_BYTE] = (_b = {},
+ _b[exports.FORMATS.RGBA] = gl.RGBA8,
+ _b[exports.FORMATS.RGB] = gl.RGB8,
+ _b[exports.FORMATS.RG] = gl.RG8,
+ _b[exports.FORMATS.RED] = gl.R8,
+ _b[exports.FORMATS.RGBA_INTEGER] = gl.RGBA8UI,
+ _b[exports.FORMATS.RGB_INTEGER] = gl.RGB8UI,
+ _b[exports.FORMATS.RG_INTEGER] = gl.RG8UI,
+ _b[exports.FORMATS.RED_INTEGER] = gl.R8UI,
+ _b[exports.FORMATS.ALPHA] = gl.ALPHA,
+ _b[exports.FORMATS.LUMINANCE] = gl.LUMINANCE,
+ _b[exports.FORMATS.LUMINANCE_ALPHA] = gl.LUMINANCE_ALPHA,
+ _b),
+ _a[exports.TYPES.BYTE] = (_c = {},
+ _c[exports.FORMATS.RGBA] = gl.RGBA8_SNORM,
+ _c[exports.FORMATS.RGB] = gl.RGB8_SNORM,
+ _c[exports.FORMATS.RG] = gl.RG8_SNORM,
+ _c[exports.FORMATS.RED] = gl.R8_SNORM,
+ _c[exports.FORMATS.RGBA_INTEGER] = gl.RGBA8I,
+ _c[exports.FORMATS.RGB_INTEGER] = gl.RGB8I,
+ _c[exports.FORMATS.RG_INTEGER] = gl.RG8I,
+ _c[exports.FORMATS.RED_INTEGER] = gl.R8I,
+ _c),
+ _a[exports.TYPES.UNSIGNED_SHORT] = (_d = {},
+ _d[exports.FORMATS.RGBA_INTEGER] = gl.RGBA16UI,
+ _d[exports.FORMATS.RGB_INTEGER] = gl.RGB16UI,
+ _d[exports.FORMATS.RG_INTEGER] = gl.RG16UI,
+ _d[exports.FORMATS.RED_INTEGER] = gl.R16UI,
+ _d[exports.FORMATS.DEPTH_COMPONENT] = gl.DEPTH_COMPONENT16,
+ _d),
+ _a[exports.TYPES.SHORT] = (_e = {},
+ _e[exports.FORMATS.RGBA_INTEGER] = gl.RGBA16I,
+ _e[exports.FORMATS.RGB_INTEGER] = gl.RGB16I,
+ _e[exports.FORMATS.RG_INTEGER] = gl.RG16I,
+ _e[exports.FORMATS.RED_INTEGER] = gl.R16I,
+ _e),
+ _a[exports.TYPES.UNSIGNED_INT] = (_f = {},
+ _f[exports.FORMATS.RGBA_INTEGER] = gl.RGBA32UI,
+ _f[exports.FORMATS.RGB_INTEGER] = gl.RGB32UI,
+ _f[exports.FORMATS.RG_INTEGER] = gl.RG32UI,
+ _f[exports.FORMATS.RED_INTEGER] = gl.R32UI,
+ _f[exports.FORMATS.DEPTH_COMPONENT] = gl.DEPTH_COMPONENT24,
+ _f),
+ _a[exports.TYPES.INT] = (_g = {},
+ _g[exports.FORMATS.RGBA_INTEGER] = gl.RGBA32I,
+ _g[exports.FORMATS.RGB_INTEGER] = gl.RGB32I,
+ _g[exports.FORMATS.RG_INTEGER] = gl.RG32I,
+ _g[exports.FORMATS.RED_INTEGER] = gl.R32I,
+ _g),
+ _a[exports.TYPES.FLOAT] = (_h = {},
+ _h[exports.FORMATS.RGBA] = gl.RGBA32F,
+ _h[exports.FORMATS.RGB] = gl.RGB32F,
+ _h[exports.FORMATS.RG] = gl.RG32F,
+ _h[exports.FORMATS.RED] = gl.R32F,
+ _h[exports.FORMATS.DEPTH_COMPONENT] = gl.DEPTH_COMPONENT32F,
+ _h),
+ _a[exports.TYPES.HALF_FLOAT] = (_j = {},
+ _j[exports.FORMATS.RGBA] = gl.RGBA16F,
+ _j[exports.FORMATS.RGB] = gl.RGB16F,
+ _j[exports.FORMATS.RG] = gl.RG16F,
+ _j[exports.FORMATS.RED] = gl.R16F,
+ _j),
+ _a[exports.TYPES.UNSIGNED_SHORT_5_6_5] = (_k = {},
+ _k[exports.FORMATS.RGB] = gl.RGB565,
+ _k),
+ _a[exports.TYPES.UNSIGNED_SHORT_4_4_4_4] = (_l = {},
+ _l[exports.FORMATS.RGBA] = gl.RGBA4,
+ _l),
+ _a[exports.TYPES.UNSIGNED_SHORT_5_5_5_1] = (_m = {},
+ _m[exports.FORMATS.RGBA] = gl.RGB5_A1,
+ _m),
+ _a[exports.TYPES.UNSIGNED_INT_2_10_10_10_REV] = (_o = {},
+ _o[exports.FORMATS.RGBA] = gl.RGB10_A2,
+ _o[exports.FORMATS.RGBA_INTEGER] = gl.RGB10_A2UI,
+ _o),
+ _a[exports.TYPES.UNSIGNED_INT_10F_11F_11F_REV] = (_p = {},
+ _p[exports.FORMATS.RGB] = gl.R11F_G11F_B10F,
+ _p),
+ _a[exports.TYPES.UNSIGNED_INT_5_9_9_9_REV] = (_q = {},
+ _q[exports.FORMATS.RGB] = gl.RGB9_E5,
+ _q),
+ _a[exports.TYPES.UNSIGNED_INT_24_8] = (_r = {},
+ _r[exports.FORMATS.DEPTH_STENCIL] = gl.DEPTH24_STENCIL8,
+ _r),
+ _a[exports.TYPES.FLOAT_32_UNSIGNED_INT_24_8_REV] = (_s = {},
+ _s[exports.FORMATS.DEPTH_STENCIL] = gl.DEPTH32F_STENCIL8,
+ _s),
+ _a);
+ }
+ else {
+ table = (_t = {},
+ _t[exports.TYPES.UNSIGNED_BYTE] = (_u = {},
+ _u[exports.FORMATS.RGBA] = gl.RGBA,
+ _u[exports.FORMATS.RGB] = gl.RGB,
+ _u[exports.FORMATS.ALPHA] = gl.ALPHA,
+ _u[exports.FORMATS.LUMINANCE] = gl.LUMINANCE,
+ _u[exports.FORMATS.LUMINANCE_ALPHA] = gl.LUMINANCE_ALPHA,
+ _u),
+ _t[exports.TYPES.UNSIGNED_SHORT_5_6_5] = (_v = {},
+ _v[exports.FORMATS.RGB] = gl.RGB,
+ _v),
+ _t[exports.TYPES.UNSIGNED_SHORT_4_4_4_4] = (_w = {},
+ _w[exports.FORMATS.RGBA] = gl.RGBA,
+ _w),
+ _t[exports.TYPES.UNSIGNED_SHORT_5_5_5_1] = (_x = {},
+ _x[exports.FORMATS.RGBA] = gl.RGBA,
+ _x),
+ _t);
+ }
+ return table;
+ }
+
+ /**
+ * Internal texture for WebGL context.
+ * @memberof PIXI
+ */
+ var GLTexture = /** @class */ (function () {
+ function GLTexture(texture) {
+ this.texture = texture;
+ this.width = -1;
+ this.height = -1;
+ this.dirtyId = -1;
+ this.dirtyStyleId = -1;
+ this.mipmap = false;
+ this.wrapMode = 33071;
+ this.type = exports.TYPES.UNSIGNED_BYTE;
+ this.internalFormat = exports.FORMATS.RGBA;
+ this.samplerType = 0;
+ }
+ return GLTexture;
+ }());
+
+ /**
+ * System plugin to the renderer to manage textures.
+ * @memberof PIXI
+ */
+ var TextureSystem = /** @class */ (function () {
+ /**
+ * @param renderer - The renderer this system works for.
+ */
+ function TextureSystem(renderer) {
+ this.renderer = renderer;
+ // TODO set to max textures...
+ this.boundTextures = [];
+ this.currentLocation = -1;
+ this.managedTextures = [];
+ this._unknownBoundTextures = false;
+ this.unknownTexture = new BaseTexture();
+ this.hasIntegerTextures = false;
+ }
+ /** Sets up the renderer context and necessary buffers. */
+ TextureSystem.prototype.contextChange = function () {
+ var gl = this.gl = this.renderer.gl;
+ this.CONTEXT_UID = this.renderer.CONTEXT_UID;
+ this.webGLVersion = this.renderer.context.webGLVersion;
+ this.internalFormats = mapTypeAndFormatToInternalFormat(gl);
+ var maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ this.boundTextures.length = maxTextures;
+ for (var i = 0; i < maxTextures; i++) {
+ this.boundTextures[i] = null;
+ }
+ // TODO move this.. to a nice make empty textures class..
+ this.emptyTextures = {};
+ var emptyTexture2D = new GLTexture(gl.createTexture());
+ gl.bindTexture(gl.TEXTURE_2D, emptyTexture2D.texture);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4));
+ this.emptyTextures[gl.TEXTURE_2D] = emptyTexture2D;
+ this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(gl.createTexture());
+ gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture);
+ for (var i = 0; i < 6; i++) {
+ gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+ }
+ gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+ for (var i = 0; i < this.boundTextures.length; i++) {
+ this.bind(null, i);
+ }
+ };
+ /**
+ * Bind a texture to a specific location
+ *
+ * If you want to unbind something, please use `unbind(texture)` instead of `bind(null, textureLocation)`
+ * @param texture - Texture to bind
+ * @param [location=0] - Location to bind at
+ */
+ TextureSystem.prototype.bind = function (texture, location) {
+ if (location === void 0) { location = 0; }
+ var gl = this.gl;
+ texture = texture === null || texture === void 0 ? void 0 : texture.castToBaseTexture();
+ // cannot bind partial texture
+ // TODO: report a warning
+ if (texture && texture.valid && !texture.parentTextureArray) {
+ texture.touched = this.renderer.textureGC.count;
+ var glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture);
+ if (this.boundTextures[location] !== texture) {
+ if (this.currentLocation !== location) {
+ this.currentLocation = location;
+ gl.activeTexture(gl.TEXTURE0 + location);
+ }
+ gl.bindTexture(texture.target, glTexture.texture);
+ }
+ if (glTexture.dirtyId !== texture.dirtyId) {
+ if (this.currentLocation !== location) {
+ this.currentLocation = location;
+ gl.activeTexture(gl.TEXTURE0 + location);
+ }
+ this.updateTexture(texture);
+ }
+ this.boundTextures[location] = texture;
+ }
+ else {
+ if (this.currentLocation !== location) {
+ this.currentLocation = location;
+ gl.activeTexture(gl.TEXTURE0 + location);
+ }
+ gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture);
+ this.boundTextures[location] = null;
+ }
+ };
+ /** Resets texture location and bound textures Actual `bind(null, i)` calls will be performed at next `unbind()` call */
+ TextureSystem.prototype.reset = function () {
+ this._unknownBoundTextures = true;
+ this.hasIntegerTextures = false;
+ this.currentLocation = -1;
+ for (var i = 0; i < this.boundTextures.length; i++) {
+ this.boundTextures[i] = this.unknownTexture;
+ }
+ };
+ /**
+ * Unbind a texture.
+ * @param texture - Texture to bind
+ */
+ TextureSystem.prototype.unbind = function (texture) {
+ var _a = this, gl = _a.gl, boundTextures = _a.boundTextures;
+ if (this._unknownBoundTextures) {
+ this._unknownBoundTextures = false;
+ // someone changed webGL state,
+ // we have to be sure that our texture does not appear in multi-texture renderer samplers
+ for (var i = 0; i < boundTextures.length; i++) {
+ if (boundTextures[i] === this.unknownTexture) {
+ this.bind(null, i);
+ }
+ }
+ }
+ for (var i = 0; i < boundTextures.length; i++) {
+ if (boundTextures[i] === texture) {
+ if (this.currentLocation !== i) {
+ gl.activeTexture(gl.TEXTURE0 + i);
+ this.currentLocation = i;
+ }
+ gl.bindTexture(texture.target, this.emptyTextures[texture.target].texture);
+ boundTextures[i] = null;
+ }
+ }
+ };
+ /**
+ * Ensures that current boundTextures all have FLOAT sampler type,
+ * see {@link PIXI.SAMPLER_TYPES} for explanation.
+ * @param maxTextures - number of locations to check
+ */
+ TextureSystem.prototype.ensureSamplerType = function (maxTextures) {
+ var _a = this, boundTextures = _a.boundTextures, hasIntegerTextures = _a.hasIntegerTextures, CONTEXT_UID = _a.CONTEXT_UID;
+ if (!hasIntegerTextures) {
+ return;
+ }
+ for (var i = maxTextures - 1; i >= 0; --i) {
+ var tex = boundTextures[i];
+ if (tex) {
+ var glTexture = tex._glTextures[CONTEXT_UID];
+ if (glTexture.samplerType !== exports.SAMPLER_TYPES.FLOAT) {
+ this.renderer.texture.unbind(tex);
+ }
+ }
+ }
+ };
+ /**
+ * Initialize a texture
+ * @private
+ * @param texture - Texture to initialize
+ */
+ TextureSystem.prototype.initTexture = function (texture) {
+ var glTexture = new GLTexture(this.gl.createTexture());
+ // guarantee an update..
+ glTexture.dirtyId = -1;
+ texture._glTextures[this.CONTEXT_UID] = glTexture;
+ this.managedTextures.push(texture);
+ texture.on('dispose', this.destroyTexture, this);
+ return glTexture;
+ };
+ TextureSystem.prototype.initTextureType = function (texture, glTexture) {
+ var _a, _b;
+ glTexture.internalFormat = (_b = (_a = this.internalFormats[texture.type]) === null || _a === void 0 ? void 0 : _a[texture.format]) !== null && _b !== void 0 ? _b : texture.format;
+ if (this.webGLVersion === 2 && texture.type === exports.TYPES.HALF_FLOAT) {
+ // TYPES.HALF_FLOAT is WebGL1 HALF_FLOAT_OES
+ // we have to convert it to WebGL HALF_FLOAT
+ glTexture.type = this.gl.HALF_FLOAT;
+ }
+ else {
+ glTexture.type = texture.type;
+ }
+ };
+ /**
+ * Update a texture
+ * @private
+ * @param {PIXI.BaseTexture} texture - Texture to initialize
+ */
+ TextureSystem.prototype.updateTexture = function (texture) {
+ var glTexture = texture._glTextures[this.CONTEXT_UID];
+ if (!glTexture) {
+ return;
+ }
+ var renderer = this.renderer;
+ this.initTextureType(texture, glTexture);
+ if (texture.resource && texture.resource.upload(renderer, texture, glTexture)) {
+ // texture is uploaded, dont do anything!
+ if (glTexture.samplerType !== exports.SAMPLER_TYPES.FLOAT) {
+ this.hasIntegerTextures = true;
+ }
+ }
+ else {
+ // default, renderTexture-like logic
+ var width = texture.realWidth;
+ var height = texture.realHeight;
+ var gl = renderer.gl;
+ if (glTexture.width !== width
+ || glTexture.height !== height
+ || glTexture.dirtyId < 0) {
+ glTexture.width = width;
+ glTexture.height = height;
+ gl.texImage2D(texture.target, 0, glTexture.internalFormat, width, height, 0, texture.format, glTexture.type, null);
+ }
+ }
+ // lets only update what changes..
+ if (texture.dirtyStyleId !== glTexture.dirtyStyleId) {
+ this.updateTextureStyle(texture);
+ }
+ glTexture.dirtyId = texture.dirtyId;
+ };
+ /**
+ * Deletes the texture from WebGL
+ * @private
+ * @param texture - the texture to destroy
+ * @param [skipRemove=false] - Whether to skip removing the texture from the TextureManager.
+ */
+ TextureSystem.prototype.destroyTexture = function (texture, skipRemove) {
+ var gl = this.gl;
+ texture = texture.castToBaseTexture();
+ if (texture._glTextures[this.CONTEXT_UID]) {
+ this.unbind(texture);
+ gl.deleteTexture(texture._glTextures[this.CONTEXT_UID].texture);
+ texture.off('dispose', this.destroyTexture, this);
+ delete texture._glTextures[this.CONTEXT_UID];
+ if (!skipRemove) {
+ var i = this.managedTextures.indexOf(texture);
+ if (i !== -1) {
+ removeItems(this.managedTextures, i, 1);
+ }
+ }
+ }
+ };
+ /**
+ * Update texture style such as mipmap flag
+ * @private
+ * @param {PIXI.BaseTexture} texture - Texture to update
+ */
+ TextureSystem.prototype.updateTextureStyle = function (texture) {
+ var glTexture = texture._glTextures[this.CONTEXT_UID];
+ if (!glTexture) {
+ return;
+ }
+ if ((texture.mipmap === exports.MIPMAP_MODES.POW2 || this.webGLVersion !== 2) && !texture.isPowerOfTwo) {
+ glTexture.mipmap = false;
+ }
+ else {
+ glTexture.mipmap = texture.mipmap >= 1;
+ }
+ if (this.webGLVersion !== 2 && !texture.isPowerOfTwo) {
+ glTexture.wrapMode = exports.WRAP_MODES.CLAMP;
+ }
+ else {
+ glTexture.wrapMode = texture.wrapMode;
+ }
+ if (texture.resource && texture.resource.style(this.renderer, texture, glTexture)) { ; }
+ else {
+ this.setStyle(texture, glTexture);
+ }
+ glTexture.dirtyStyleId = texture.dirtyStyleId;
+ };
+ /**
+ * Set style for texture
+ * @private
+ * @param texture - Texture to update
+ * @param glTexture
+ */
+ TextureSystem.prototype.setStyle = function (texture, glTexture) {
+ var gl = this.gl;
+ if (glTexture.mipmap && texture.mipmap !== exports.MIPMAP_MODES.ON_MANUAL) {
+ gl.generateMipmap(texture.target);
+ }
+ gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, glTexture.wrapMode);
+ gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, glTexture.wrapMode);
+ if (glTexture.mipmap) {
+ /* eslint-disable max-len */
+ gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode === exports.SCALE_MODES.LINEAR ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST);
+ /* eslint-disable max-len */
+ var anisotropicExt = this.renderer.context.extensions.anisotropicFiltering;
+ if (anisotropicExt && texture.anisotropicLevel > 0 && texture.scaleMode === exports.SCALE_MODES.LINEAR) {
+ var level = Math.min(texture.anisotropicLevel, gl.getParameter(anisotropicExt.MAX_TEXTURE_MAX_ANISOTROPY_EXT));
+ gl.texParameterf(texture.target, anisotropicExt.TEXTURE_MAX_ANISOTROPY_EXT, level);
+ }
+ }
+ else {
+ gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode === exports.SCALE_MODES.LINEAR ? gl.LINEAR : gl.NEAREST);
+ }
+ gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode === exports.SCALE_MODES.LINEAR ? gl.LINEAR : gl.NEAREST);
+ };
+ TextureSystem.prototype.destroy = function () {
+ this.renderer = null;
+ };
+ return TextureSystem;
+ }());
+
+ var _systems = {
+ __proto__: null,
+ FilterSystem: FilterSystem,
+ BatchSystem: BatchSystem,
+ ContextSystem: ContextSystem,
+ FramebufferSystem: FramebufferSystem,
+ GeometrySystem: GeometrySystem,
+ MaskSystem: MaskSystem,
+ ScissorSystem: ScissorSystem,
+ StencilSystem: StencilSystem,
+ ProjectionSystem: ProjectionSystem,
+ RenderTextureSystem: RenderTextureSystem,
+ ShaderSystem: ShaderSystem,
+ StateSystem: StateSystem,
+ TextureGCSystem: TextureGCSystem,
+ TextureSystem: TextureSystem
+ };
+
+ var tempMatrix = new Matrix();
+ /**
+ * The AbstractRenderer is the base for a PixiJS Renderer. It is extended by the {@link PIXI.CanvasRenderer}
+ * and {@link PIXI.Renderer} which can be used for rendering a PixiJS scene.
+ * @abstract
+ * @class
+ * @extends PIXI.utils.EventEmitter
+ * @memberof PIXI
+ */
+ var AbstractRenderer = /** @class */ (function (_super) {
+ __extends$i(AbstractRenderer, _super);
+ /**
+ * @param type - The renderer type.
+ * @param [options] - The optional renderer parameters.
+ * @param {number} [options.width=800] - The width of the screen.
+ * @param {number} [options.height=600] - The height of the screen.
+ * @param {HTMLCanvasElement} [options.view] - The canvas to use as a view, optional.
+ * @param {boolean} [options.useContextAlpha=true] - Pass-through value for canvas' context `alpha` property.
+ * If you want to set transparency, please use `backgroundAlpha`. This option is for cases where the
+ * canvas needs to be opaque, possibly for performance reasons on some older devices.
+ * @param {boolean} [options.autoDensity=false] - Resizes renderer view in CSS pixels to allow for
+ * resolutions other than 1.
+ * @param {boolean} [options.antialias=false] - Sets antialias
+ * @param {number} [options.resolution=PIXI.settings.RESOLUTION] - The resolution / device pixel ratio of the renderer.
+ * @param {boolean} [options.preserveDrawingBuffer=false] - Enables drawing buffer preservation,
+ * enable this if you need to call toDataUrl on the WebGL context.
+ * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or
+ * not before the new render pass.
+ * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area
+ * (shown if not transparent).
+ * @param {number} [options.backgroundAlpha=1] - Value from 0 (fully transparent) to 1 (fully opaque).
+ */
+ function AbstractRenderer(type, options) {
+ if (type === void 0) { type = exports.RENDERER_TYPE.UNKNOWN; }
+ var _this = _super.call(this) || this;
+ // Add the default render options
+ options = Object.assign({}, settings$1.RENDER_OPTIONS, options);
+ /**
+ * The supplied constructor options.
+ * @member {object}
+ * @readonly
+ */
+ _this.options = options;
+ /**
+ * The type of the renderer.
+ * @member {number}
+ * @default PIXI.RENDERER_TYPE.UNKNOWN
+ * @see PIXI.RENDERER_TYPE
+ */
+ _this.type = type;
+ /**
+ * Measurements of the screen. (0, 0, screenWidth, screenHeight).
+ *
+ * Its safe to use as filterArea or hitArea for the whole stage.
+ * @member {PIXI.Rectangle}
+ */
+ _this.screen = new Rectangle(0, 0, options.width, options.height);
+ /**
+ * The canvas element that everything is drawn to.
+ * @member {HTMLCanvasElement}
+ */
+ _this.view = options.view || settings$1.ADAPTER.createCanvas();
+ /**
+ * The resolution / device pixel ratio of the renderer.
+ * @member {number}
+ * @default PIXI.settings.RESOLUTION
+ */
+ _this.resolution = options.resolution || settings$1.RESOLUTION;
+ /**
+ * Pass-thru setting for the canvas' context `alpha` property. This is typically
+ * not something you need to fiddle with. If you want transparency, use `backgroundAlpha`.
+ * @member {boolean}
+ */
+ _this.useContextAlpha = options.useContextAlpha;
+ /**
+ * Whether CSS dimensions of canvas view should be resized to screen dimensions automatically.
+ * @member {boolean}
+ */
+ _this.autoDensity = !!options.autoDensity;
+ /**
+ * The value of the preserveDrawingBuffer flag affects whether or not the contents of
+ * the stencil buffer is retained after rendering.
+ * @member {boolean}
+ */
+ _this.preserveDrawingBuffer = options.preserveDrawingBuffer;
+ /**
+ * This sets if the CanvasRenderer will clear the canvas or not before the new render pass.
+ * If the scene is NOT transparent PixiJS will use a canvas sized fillRect operation every
+ * frame to set the canvas background color. If the scene is transparent PixiJS will use clearRect
+ * to clear the canvas every frame. Disable this by setting this to false. For example, if
+ * your game has a canvas filling background image you often don't need this set.
+ * @member {boolean}
+ * @default
+ */
+ _this.clearBeforeRender = options.clearBeforeRender;
+ /**
+ * The background color as a number.
+ * @member {number}
+ * @protected
+ */
+ _this._backgroundColor = 0x000000;
+ /**
+ * The background color as an [R, G, B, A] array.
+ * @member {number[]}
+ * @protected
+ */
+ _this._backgroundColorRgba = [0, 0, 0, 1];
+ /**
+ * The background color as a string.
+ * @member {string}
+ * @protected
+ */
+ _this._backgroundColorString = '#000000';
+ _this.backgroundColor = options.backgroundColor || _this._backgroundColor; // run bg color setter
+ _this.backgroundAlpha = options.backgroundAlpha;
+ // @deprecated
+ if (options.transparent !== undefined) {
+ deprecation$1('6.0.0', 'Option transparent is deprecated, please use backgroundAlpha instead.');
+ _this.useContextAlpha = options.transparent;
+ _this.backgroundAlpha = options.transparent ? 0 : 1;
+ }
+ /**
+ * The last root object that the renderer tried to render.
+ * @member {PIXI.DisplayObject}
+ * @protected
+ */
+ _this._lastObjectRendered = null;
+ /**
+ * Collection of plugins.
+ * @readonly
+ * @member {object}
+ */
+ _this.plugins = {};
+ return _this;
+ }
+ /**
+ * Initialize the plugins.
+ * @protected
+ * @param {object} staticMap - The dictionary of statically saved plugins.
+ */
+ AbstractRenderer.prototype.initPlugins = function (staticMap) {
+ for (var o in staticMap) {
+ this.plugins[o] = new (staticMap[o])(this);
+ }
+ };
+ Object.defineProperty(AbstractRenderer.prototype, "width", {
+ /**
+ * Same as view.width, actual number of pixels in the canvas by horizontal.
+ * @member {number}
+ * @readonly
+ * @default 800
+ */
+ get: function () {
+ return this.view.width;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(AbstractRenderer.prototype, "height", {
+ /**
+ * Same as view.height, actual number of pixels in the canvas by vertical.
+ * @member {number}
+ * @readonly
+ * @default 600
+ */
+ get: function () {
+ return this.view.height;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Resizes the screen and canvas as close as possible to the specified width and height.
+ * Canvas dimensions are multiplied by resolution and rounded to the nearest integers.
+ * The new canvas dimensions divided by the resolution become the new screen dimensions.
+ * @param desiredScreenWidth - The desired width of the screen.
+ * @param desiredScreenHeight - The desired height of the screen.
+ */
+ AbstractRenderer.prototype.resize = function (desiredScreenWidth, desiredScreenHeight) {
+ this.view.width = Math.round(desiredScreenWidth * this.resolution);
+ this.view.height = Math.round(desiredScreenHeight * this.resolution);
+ var screenWidth = this.view.width / this.resolution;
+ var screenHeight = this.view.height / this.resolution;
+ this.screen.width = screenWidth;
+ this.screen.height = screenHeight;
+ if (this.autoDensity) {
+ this.view.style.width = screenWidth + "px";
+ this.view.style.height = screenHeight + "px";
+ }
+ /**
+ * Fired after view has been resized.
+ * @event PIXI.Renderer#resize
+ * @param {number} screenWidth - The new width of the screen.
+ * @param {number} screenHeight - The new height of the screen.
+ */
+ this.emit('resize', screenWidth, screenHeight);
+ };
+ /**
+ * @ignore
+ */
+ AbstractRenderer.prototype.generateTexture = function (displayObject, options, resolution, region) {
+ if (options === void 0) { options = {}; }
+ // @deprecated parameters spread, use options instead
+ if (typeof options === 'number') {
+ deprecation$1('6.1.0', 'generateTexture options (scaleMode, resolution, region) are now object options.');
+ options = { scaleMode: options, resolution: resolution, region: region };
+ }
+ var manualRegion = options.region, textureOptions = __rest(options, ["region"]);
+ region = manualRegion || displayObject.getLocalBounds(null, true);
+ // minimum texture size is 1x1, 0x0 will throw an error
+ if (region.width === 0)
+ { region.width = 1; }
+ if (region.height === 0)
+ { region.height = 1; }
+ var renderTexture = RenderTexture.create(__assign({ width: region.width, height: region.height }, textureOptions));
+ tempMatrix.tx = -region.x;
+ tempMatrix.ty = -region.y;
+ this.render(displayObject, {
+ renderTexture: renderTexture,
+ clear: false,
+ transform: tempMatrix,
+ skipUpdateTransform: !!displayObject.parent
+ });
+ return renderTexture;
+ };
+ /**
+ * Removes everything from the renderer and optionally removes the Canvas DOM element.
+ * @param [removeView=false] - Removes the Canvas element from the DOM.
+ */
+ AbstractRenderer.prototype.destroy = function (removeView) {
+ for (var o in this.plugins) {
+ this.plugins[o].destroy();
+ this.plugins[o] = null;
+ }
+ if (removeView && this.view.parentNode) {
+ this.view.parentNode.removeChild(this.view);
+ }
+ var thisAny = this;
+ // null-ing all objects, that's a tradition!
+ thisAny.plugins = null;
+ thisAny.type = exports.RENDERER_TYPE.UNKNOWN;
+ thisAny.view = null;
+ thisAny.screen = null;
+ thisAny._tempDisplayObjectParent = null;
+ thisAny.options = null;
+ this._backgroundColorRgba = null;
+ this._backgroundColorString = null;
+ this._lastObjectRendered = null;
+ };
+ Object.defineProperty(AbstractRenderer.prototype, "backgroundColor", {
+ /**
+ * The background color to fill if not transparent
+ * @member {number}
+ */
+ get: function () {
+ return this._backgroundColor;
+ },
+ set: function (value) {
+ this._backgroundColor = value;
+ this._backgroundColorString = hex2string(value);
+ hex2rgb(value, this._backgroundColorRgba);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(AbstractRenderer.prototype, "backgroundAlpha", {
+ /**
+ * The background color alpha. Setting this to 0 will make the canvas transparent.
+ * @member {number}
+ */
+ get: function () {
+ return this._backgroundColorRgba[3];
+ },
+ set: function (value) {
+ this._backgroundColorRgba[3] = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return AbstractRenderer;
+ }(eventemitter3));
+
+ var GLBuffer = /** @class */ (function () {
+ function GLBuffer(buffer) {
+ this.buffer = buffer || null;
+ this.updateID = -1;
+ this.byteLength = -1;
+ this.refCount = 0;
+ }
+ return GLBuffer;
+ }());
+
+ /**
+ * System plugin to the renderer to manage buffers.
+ *
+ * WebGL uses Buffers as a way to store objects to the GPU.
+ * This system makes working with them a lot easier.
+ *
+ * Buffers are used in three main places in WebGL
+ * - geometry information
+ * - Uniform information (via uniform buffer objects - a WebGL 2 only feature)
+ * - Transform feedback information. (WebGL 2 only feature)
+ *
+ * This system will handle the binding of buffers to the GPU as well as uploading
+ * them. With this system, you never need to work directly with GPU buffers, but instead work with
+ * the PIXI.Buffer class.
+ * @class
+ * @memberof PIXI
+ */
+ var BufferSystem = /** @class */ (function () {
+ /**
+ * @param {PIXI.Renderer} renderer - The renderer this System works for.
+ */
+ function BufferSystem(renderer) {
+ this.renderer = renderer;
+ this.managedBuffers = {};
+ this.boundBufferBases = {};
+ }
+ /**
+ * @ignore
+ */
+ BufferSystem.prototype.destroy = function () {
+ this.renderer = null;
+ };
+ /** Sets up the renderer context and necessary buffers. */
+ BufferSystem.prototype.contextChange = function () {
+ this.disposeAll(true);
+ this.gl = this.renderer.gl;
+ // TODO fill out...
+ this.CONTEXT_UID = this.renderer.CONTEXT_UID;
+ };
+ /**
+ * This binds specified buffer. On first run, it will create the webGL buffers for the context too
+ * @param buffer - the buffer to bind to the renderer
+ */
+ BufferSystem.prototype.bind = function (buffer) {
+ var _a = this, gl = _a.gl, CONTEXT_UID = _a.CONTEXT_UID;
+ var glBuffer = buffer._glBuffers[CONTEXT_UID] || this.createGLBuffer(buffer);
+ gl.bindBuffer(buffer.type, glBuffer.buffer);
+ };
+ /**
+ * Binds an uniform buffer to at the given index.
+ *
+ * A cache is used so a buffer will not be bound again if already bound.
+ * @param buffer - the buffer to bind
+ * @param index - the base index to bind it to.
+ */
+ BufferSystem.prototype.bindBufferBase = function (buffer, index) {
+ var _a = this, gl = _a.gl, CONTEXT_UID = _a.CONTEXT_UID;
+ if (this.boundBufferBases[index] !== buffer) {
+ var glBuffer = buffer._glBuffers[CONTEXT_UID] || this.createGLBuffer(buffer);
+ this.boundBufferBases[index] = buffer;
+ gl.bindBufferBase(gl.UNIFORM_BUFFER, index, glBuffer.buffer);
+ }
+ };
+ /**
+ * Binds a buffer whilst also binding its range.
+ * This will make the buffer start from the offset supplied rather than 0 when it is read.
+ * @param buffer - the buffer to bind
+ * @param index - the base index to bind at, defaults to 0
+ * @param offset - the offset to bind at (this is blocks of 256). 0 = 0, 1 = 256, 2 = 512 etc
+ */
+ BufferSystem.prototype.bindBufferRange = function (buffer, index, offset) {
+ var _a = this, gl = _a.gl, CONTEXT_UID = _a.CONTEXT_UID;
+ offset = offset || 0;
+ var glBuffer = buffer._glBuffers[CONTEXT_UID] || this.createGLBuffer(buffer);
+ gl.bindBufferRange(gl.UNIFORM_BUFFER, index || 0, glBuffer.buffer, offset * 256, 256);
+ };
+ /**
+ * Will ensure the data in the buffer is uploaded to the GPU.
+ * @param {PIXI.Buffer} buffer - the buffer to update
+ */
+ BufferSystem.prototype.update = function (buffer) {
+ var _a = this, gl = _a.gl, CONTEXT_UID = _a.CONTEXT_UID;
+ var glBuffer = buffer._glBuffers[CONTEXT_UID];
+ if (buffer._updateID === glBuffer.updateID) {
+ return;
+ }
+ glBuffer.updateID = buffer._updateID;
+ gl.bindBuffer(buffer.type, glBuffer.buffer);
+ if (glBuffer.byteLength >= buffer.data.byteLength) {
+ // offset is always zero for now!
+ gl.bufferSubData(buffer.type, 0, buffer.data);
+ }
+ else {
+ var drawType = buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW;
+ glBuffer.byteLength = buffer.data.byteLength;
+ gl.bufferData(buffer.type, buffer.data, drawType);
+ }
+ };
+ /**
+ * Disposes buffer
+ * @param {PIXI.Buffer} buffer - buffer with data
+ * @param {boolean} [contextLost=false] - If context was lost, we suppress deleteVertexArray
+ */
+ BufferSystem.prototype.dispose = function (buffer, contextLost) {
+ if (!this.managedBuffers[buffer.id]) {
+ return;
+ }
+ delete this.managedBuffers[buffer.id];
+ var glBuffer = buffer._glBuffers[this.CONTEXT_UID];
+ var gl = this.gl;
+ buffer.disposeRunner.remove(this);
+ if (!glBuffer) {
+ return;
+ }
+ if (!contextLost) {
+ gl.deleteBuffer(glBuffer.buffer);
+ }
+ delete buffer._glBuffers[this.CONTEXT_UID];
+ };
+ /**
+ * dispose all WebGL resources of all managed buffers
+ * @param {boolean} [contextLost=false] - If context was lost, we suppress `gl.delete` calls
+ */
+ BufferSystem.prototype.disposeAll = function (contextLost) {
+ var all = Object.keys(this.managedBuffers);
+ for (var i = 0; i < all.length; i++) {
+ this.dispose(this.managedBuffers[all[i]], contextLost);
+ }
+ };
+ /**
+ * creates and attaches a GLBuffer object tied to the current context.
+ * @param buffer
+ * @protected
+ */
+ BufferSystem.prototype.createGLBuffer = function (buffer) {
+ var _a = this, CONTEXT_UID = _a.CONTEXT_UID, gl = _a.gl;
+ buffer._glBuffers[CONTEXT_UID] = new GLBuffer(gl.createBuffer());
+ this.managedBuffers[buffer.id] = buffer;
+ buffer.disposeRunner.add(this);
+ return buffer._glBuffers[CONTEXT_UID];
+ };
+ return BufferSystem;
+ }());
+
+ /**
+ * The Renderer draws the scene and all its content onto a WebGL enabled canvas.
+ *
+ * This renderer should be used for browsers that support WebGL.
+ *
+ * This renderer works by automatically managing WebGLBatchesm, so no need for Sprite Batches or Sprite Clouds.
+ * Don't forget to add the view to your DOM or you will not see anything!
+ *
+ * Renderer is composed of systems that manage specific tasks. The following systems are added by default
+ * whenever you create a renderer:
+ *
+ * | System | Description |
+ * | ------------------------------------ | ----------------------------------------------------------------------------- |
+ * | {@link PIXI.BatchSystem} | This manages object renderers that defer rendering until a flush. |
+ * | {@link PIXI.ContextSystem} | This manages the WebGL context and extensions. |
+ * | {@link PIXI.EventSystem} | This manages UI events. |
+ * | {@link PIXI.FilterSystem} | This manages the filtering pipeline for post-processing effects. |
+ * | {@link PIXI.FramebufferSystem} | This manages framebuffers, which are used for offscreen rendering. |
+ * | {@link PIXI.GeometrySystem} | This manages geometries & buffers, which are used to draw object meshes. |
+ * | {@link PIXI.MaskSystem} | This manages masking operations. |
+ * | {@link PIXI.ProjectionSystem} | This manages the `projectionMatrix`, used by shaders to get NDC coordinates. |
+ * | {@link PIXI.RenderTextureSystem} | This manages render-textures, which are an abstraction over framebuffers. |
+ * | {@link PIXI.ScissorSystem} | This handles scissor masking, and is used internally by {@link MaskSystem} |
+ * | {@link PIXI.ShaderSystem} | This manages shaders, programs that run on the GPU to calculate 'em pixels. |
+ * | {@link PIXI.StateSystem} | This manages the WebGL state variables like blend mode, depth testing, etc. |
+ * | {@link PIXI.StencilSystem} | This handles stencil masking, and is used internally by {@link MaskSystem} |
+ * | {@link PIXI.TextureSystem} | This manages textures and their resources on the GPU. |
+ * | {@link PIXI.TextureGCSystem} | This will automatically remove textures from the GPU if they are not used. |
+ *
+ * The breadth of the API surface provided by the renderer is contained within these systems.
+ * @memberof PIXI
+ */
+ var Renderer = /** @class */ (function (_super) {
+ __extends$i(Renderer, _super);
+ /**
+ * @param [options] - The optional renderer parameters.
+ * @param {number} [options.width=800] - The width of the screen.
+ * @param {number} [options.height=600] - The height of the screen.
+ * @param {HTMLCanvasElement} [options.view] - The canvas to use as a view, optional.
+ * @param {boolean} [options.useContextAlpha=true] - Pass-through value for canvas' context `alpha` property.
+ * If you want to set transparency, please use `backgroundAlpha`. This option is for cases where the
+ * canvas needs to be opaque, possibly for performance reasons on some older devices.
+ * @param {boolean} [options.autoDensity=false] - Resizes renderer view in CSS pixels to allow for
+ * resolutions other than 1.
+ * @param {boolean} [options.antialias=false] - Sets antialias. If not available natively then FXAA
+ * antialiasing is used.
+ * @param {number} [options.resolution=PIXI.settings.RESOLUTION] - The resolution / device pixel ratio of the renderer.
+ * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear
+ * the canvas or not before the new render pass. If you wish to set this to false, you *must* set
+ * preserveDrawingBuffer to `true`.
+ * @param {boolean} [options.preserveDrawingBuffer=false] - Enables drawing buffer preservation,
+ * enable this if you need to call toDataUrl on the WebGL context.
+ * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area
+ * (shown if not transparent).
+ * @param {number} [options.backgroundAlpha=1] - Value from 0 (fully transparent) to 1 (fully opaque).
+ * @param {string} [options.powerPreference] - Parameter passed to WebGL context, set to "high-performance"
+ * for devices with dual graphics card.
+ * @param {object} [options.context] - If WebGL context already exists, all parameters must be taken from it.
+ */
+ function Renderer(options) {
+ var _this = _super.call(this, exports.RENDERER_TYPE.WEBGL, options) || this;
+ // the options will have been modified here in the super constructor with pixi's default settings..
+ options = _this.options;
+ _this.gl = null;
+ _this.CONTEXT_UID = 0;
+ _this.runners = {
+ destroy: new Runner('destroy'),
+ contextChange: new Runner('contextChange'),
+ reset: new Runner('reset'),
+ update: new Runner('update'),
+ postrender: new Runner('postrender'),
+ prerender: new Runner('prerender'),
+ resize: new Runner('resize'),
+ };
+ _this.runners.contextChange.add(_this);
+ _this.globalUniforms = new UniformGroup({
+ projectionMatrix: new Matrix(),
+ }, true);
+ _this.addSystem(MaskSystem, 'mask')
+ .addSystem(ContextSystem, 'context')
+ .addSystem(StateSystem, 'state')
+ .addSystem(ShaderSystem, 'shader')
+ .addSystem(TextureSystem, 'texture')
+ .addSystem(BufferSystem, 'buffer')
+ .addSystem(GeometrySystem, 'geometry')
+ .addSystem(FramebufferSystem, 'framebuffer')
+ .addSystem(ScissorSystem, 'scissor')
+ .addSystem(StencilSystem, 'stencil')
+ .addSystem(ProjectionSystem, 'projection')
+ .addSystem(TextureGCSystem, 'textureGC')
+ .addSystem(FilterSystem, 'filter')
+ .addSystem(RenderTextureSystem, 'renderTexture')
+ .addSystem(BatchSystem, 'batch');
+ _this.initPlugins(Renderer.__plugins);
+ _this.multisample = undefined;
+ /*
+ * The options passed in to create a new WebGL context.
+ */
+ if (options.context) {
+ _this.context.initFromContext(options.context);
+ }
+ else {
+ _this.context.initFromOptions({
+ alpha: !!_this.useContextAlpha,
+ antialias: options.antialias,
+ premultipliedAlpha: _this.useContextAlpha && _this.useContextAlpha !== 'notMultiplied',
+ stencil: true,
+ preserveDrawingBuffer: options.preserveDrawingBuffer,
+ powerPreference: _this.options.powerPreference,
+ });
+ }
+ _this.renderingToScreen = true;
+ sayHello(_this.context.webGLVersion === 2 ? 'WebGL 2' : 'WebGL 1');
+ _this.resize(_this.options.width, _this.options.height);
+ return _this;
+ }
+ /**
+ * Create renderer if WebGL is available. Overrideable
+ * by the **@pixi/canvas-renderer** package to allow fallback.
+ * throws error if WebGL is not available.
+ * @param options
+ * @private
+ */
+ Renderer.create = function (options) {
+ if (isWebGLSupported()) {
+ return new Renderer(options);
+ }
+ throw new Error('WebGL unsupported in this browser, use "pixi.js-legacy" for fallback canvas2d support.');
+ };
+ Renderer.prototype.contextChange = function () {
+ var gl = this.gl;
+ var samples;
+ if (this.context.webGLVersion === 1) {
+ var framebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ samples = gl.getParameter(gl.SAMPLES);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
+ }
+ else {
+ var framebuffer = gl.getParameter(gl.DRAW_FRAMEBUFFER_BINDING);
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
+ samples = gl.getParameter(gl.SAMPLES);
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, framebuffer);
+ }
+ if (samples >= exports.MSAA_QUALITY.HIGH) {
+ this.multisample = exports.MSAA_QUALITY.HIGH;
+ }
+ else if (samples >= exports.MSAA_QUALITY.MEDIUM) {
+ this.multisample = exports.MSAA_QUALITY.MEDIUM;
+ }
+ else if (samples >= exports.MSAA_QUALITY.LOW) {
+ this.multisample = exports.MSAA_QUALITY.LOW;
+ }
+ else {
+ this.multisample = exports.MSAA_QUALITY.NONE;
+ }
+ };
+ /**
+ * Add a new system to the renderer.
+ * @param ClassRef - Class reference
+ * @param name - Property name for system, if not specified
+ * will use a static `name` property on the class itself. This
+ * name will be assigned as s property on the Renderer so make
+ * sure it doesn't collide with properties on Renderer.
+ * @returns Return instance of renderer
+ */
+ Renderer.prototype.addSystem = function (ClassRef, name) {
+ var system = new ClassRef(this);
+ if (this[name]) {
+ throw new Error("Whoops! The name \"" + name + "\" is already in use");
+ }
+ this[name] = system;
+ for (var i in this.runners) {
+ this.runners[i].add(system);
+ }
+ /**
+ * Fired after rendering finishes.
+ * @event PIXI.Renderer#postrender
+ */
+ /**
+ * Fired before rendering starts.
+ * @event PIXI.Renderer#prerender
+ */
+ /**
+ * Fired when the WebGL context is set.
+ * @event PIXI.Renderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
+ return this;
+ };
+ /**
+ * @ignore
+ */
+ Renderer.prototype.render = function (displayObject, options) {
+ var renderTexture;
+ var clear;
+ var transform;
+ var skipUpdateTransform;
+ if (options) {
+ if (options instanceof RenderTexture) {
+ deprecation$1('6.0.0', 'Renderer#render arguments changed, use options instead.');
+ /* eslint-disable prefer-rest-params */
+ renderTexture = options;
+ clear = arguments[2];
+ transform = arguments[3];
+ skipUpdateTransform = arguments[4];
+ /* eslint-enable prefer-rest-params */
+ }
+ else {
+ renderTexture = options.renderTexture;
+ clear = options.clear;
+ transform = options.transform;
+ skipUpdateTransform = options.skipUpdateTransform;
+ }
+ }
+ // can be handy to know!
+ this.renderingToScreen = !renderTexture;
+ this.runners.prerender.emit();
+ this.emit('prerender');
+ // apply a transform at a GPU level
+ this.projection.transform = transform;
+ // no point rendering if our context has been blown up!
+ if (this.context.isLost) {
+ return;
+ }
+ if (!renderTexture) {
+ this._lastObjectRendered = displayObject;
+ }
+ if (!skipUpdateTransform) {
+ // update the scene graph
+ var cacheParent = displayObject.enableTempParent();
+ displayObject.updateTransform();
+ displayObject.disableTempParent(cacheParent);
+ // displayObject.hitArea = //TODO add a temp hit area
+ }
+ this.renderTexture.bind(renderTexture);
+ this.batch.currentRenderer.start();
+ if (clear !== undefined ? clear : this.clearBeforeRender) {
+ this.renderTexture.clear();
+ }
+ displayObject.render(this);
+ // apply transform..
+ this.batch.currentRenderer.flush();
+ if (renderTexture) {
+ renderTexture.baseTexture.update();
+ }
+ this.runners.postrender.emit();
+ // reset transform after render
+ this.projection.transform = null;
+ this.emit('postrender');
+ };
+ /**
+ * @override
+ * @ignore
+ */
+ Renderer.prototype.generateTexture = function (displayObject, options, resolution, region) {
+ if (options === void 0) { options = {}; }
+ var renderTexture = _super.prototype.generateTexture.call(this, displayObject, options, resolution, region);
+ this.framebuffer.blit();
+ return renderTexture;
+ };
+ /**
+ * Resizes the WebGL view to the specified width and height.
+ * @param desiredScreenWidth - The desired width of the screen.
+ * @param desiredScreenHeight - The desired height of the screen.
+ */
+ Renderer.prototype.resize = function (desiredScreenWidth, desiredScreenHeight) {
+ _super.prototype.resize.call(this, desiredScreenWidth, desiredScreenHeight);
+ this.runners.resize.emit(this.screen.height, this.screen.width);
+ };
+ /**
+ * Resets the WebGL state so you can render things however you fancy!
+ * @returns Returns itself.
+ */
+ Renderer.prototype.reset = function () {
+ this.runners.reset.emit();
+ return this;
+ };
+ /** Clear the frame buffer. */
+ Renderer.prototype.clear = function () {
+ this.renderTexture.bind();
+ this.renderTexture.clear();
+ };
+ /**
+ * Removes everything from the renderer (event listeners, spritebatch, etc...)
+ * @param [removeView=false] - Removes the Canvas element from the DOM.
+ * See: https://github.com/pixijs/pixi.js/issues/2233
+ */
+ Renderer.prototype.destroy = function (removeView) {
+ this.runners.destroy.emit();
+ for (var r in this.runners) {
+ this.runners[r].destroy();
+ }
+ // call base destroy
+ _super.prototype.destroy.call(this, removeView);
+ // TODO nullify all the managers..
+ this.gl = null;
+ };
+ Object.defineProperty(Renderer.prototype, "extract", {
+ /**
+ * Please use `plugins.extract` instead.
+ * @member {PIXI.Extract} extract
+ * @deprecated since 6.0.0
+ * @readonly
+ */
+ get: function () {
+ deprecation$1('6.0.0', 'Renderer#extract has been deprecated, please use Renderer#plugins.extract instead.');
+ return this.plugins.extract;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Use the {@link PIXI.extensions.add} API to register plugins.
+ * @deprecated since 6.5.0
+ * @param pluginName - The name of the plugin.
+ * @param ctor - The constructor function or class for the plugin.
+ */
+ Renderer.registerPlugin = function (pluginName, ctor) {
+ deprecation$1('6.5.0', 'Renderer.registerPlugin() has been deprecated, please use extensions.add() instead.');
+ extensions.add({
+ name: pluginName,
+ type: exports.ExtensionType.RendererPlugin,
+ ref: ctor,
+ });
+ };
+ /**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @readonly
+ * @property {PIXI.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.Extract} extract Extract image data from renderer.
+ * @property {PIXI.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.ParticleRenderer} particle Renderer for ParticleContainer objects.
+ * @property {PIXI.Prepare} prepare Pre-render display objects.
+ * @property {PIXI.BatchRenderer} batch Batching of Sprite, Graphics and Mesh objects.
+ * @property {PIXI.TilingSpriteRenderer} tilingSprite Renderer for TilingSprite objects.
+ */
+ Renderer.__plugins = {};
+ return Renderer;
+ }(AbstractRenderer));
+ // Handle registration of extensions
+ extensions.handleByMap(exports.ExtensionType.RendererPlugin, Renderer.__plugins);
+
+ /**
+ * This helper function will automatically detect which renderer you should be using.
+ * WebGL is the preferred renderer as it is a lot faster. If WebGL is not supported by
+ * the browser then this function will return a canvas renderer
+ * @memberof PIXI
+ * @function autoDetectRenderer
+ * @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
+ * @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
+ * @param {boolean} [options.useContextAlpha=true] - Pass-through value for canvas' context `alpha` property.
+ * If you want to set transparency, please use `backgroundAlpha`. This option is for cases where the
+ * canvas needs to be opaque, possibly for performance reasons on some older devices.
+ * @param {boolean} [options.autoDensity=false] - Resizes renderer view in CSS pixels to allow for
+ * resolutions other than 1
+ * @param {boolean} [options.antialias=false] - sets antialias
+ * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
+ * need to call toDataUrl on the webgl context
+ * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area
+ * (shown if not transparent).
+ * @param {number} [options.backgroundAlpha=1] - Value from 0 (fully transparent) to 1 (fully opaque).
+ * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or
+ * not before the new render pass.
+ * @param {number} [options.resolution=PIXI.settings.RESOLUTION] - The resolution / device pixel ratio of the renderer.
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present, this
+ * option only is available when using **pixi.js-legacy** or **@pixi/canvas-renderer** modules, otherwise
+ * it is ignored.
+ * @param {string} [options.powerPreference] - Parameter passed to webgl context, set to "high-performance"
+ * for devices with dual graphics card **webgl only**
+ * @returns {PIXI.Renderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
+ */
+ function autoDetectRenderer(options) {
+ return Renderer.create(options);
+ }
+
+ var $defaultVertex = "attribute vec2 aVertexPosition;\nattribute vec2 aTextureCoord;\n\nuniform mat3 projectionMatrix;\n\nvarying vec2 vTextureCoord;\n\nvoid main(void)\n{\n gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);\n vTextureCoord = aTextureCoord;\n}";
+
+ var $defaultFilterVertex = "attribute vec2 aVertexPosition;\n\nuniform mat3 projectionMatrix;\n\nvarying vec2 vTextureCoord;\n\nuniform vec4 inputSize;\nuniform vec4 outputFrame;\n\nvec4 filterVertexPosition( void )\n{\n vec2 position = aVertexPosition * max(outputFrame.zw, vec2(0.)) + outputFrame.xy;\n\n return vec4((projectionMatrix * vec3(position, 1.0)).xy, 0.0, 1.0);\n}\n\nvec2 filterTextureCoord( void )\n{\n return aVertexPosition * (outputFrame.zw * inputSize.zw);\n}\n\nvoid main(void)\n{\n gl_Position = filterVertexPosition();\n vTextureCoord = filterTextureCoord();\n}\n";
+
+ /**
+ * Default vertex shader
+ * @memberof PIXI
+ * @member {string} defaultVertex
+ */
+ /**
+ * Default filter vertex shader
+ * @memberof PIXI
+ * @member {string} defaultFilterVertex
+ */
+ // NOTE: This black magic is so that @microsoft/api-extractor does not complain! This explicitly specifies the types
+ // of defaultVertex, defaultFilterVertex.
+ var defaultVertex$1 = $defaultVertex;
+ var defaultFilterVertex = $defaultFilterVertex;
+
+ /**
+ * Use the ISystem interface instead.
+ * @deprecated since 6.1.0
+ * @memberof PIXI
+ */
+ var System = /** @class */ (function () {
+ /**
+ * @param renderer - Reference to Renderer
+ */
+ function System(renderer) {
+ deprecation$1('6.1.0', 'System class is deprecated, implemement ISystem interface instead.');
+ this.renderer = renderer;
+ }
+ /** Destroy and don't use after this. */
+ System.prototype.destroy = function () {
+ this.renderer = null;
+ };
+ return System;
+ }());
+
+ /**
+ * Used by the batcher to draw batches.
+ * Each one of these contains all information required to draw a bound geometry.
+ * @memberof PIXI
+ */
+ var BatchDrawCall = /** @class */ (function () {
+ function BatchDrawCall() {
+ this.texArray = null;
+ this.blend = 0;
+ this.type = exports.DRAW_MODES.TRIANGLES;
+ this.start = 0;
+ this.size = 0;
+ this.data = null;
+ }
+ return BatchDrawCall;
+ }());
+
+ /**
+ * Used by the batcher to build texture batches.
+ * Holds list of textures and their respective locations.
+ * @memberof PIXI
+ */
+ var BatchTextureArray = /** @class */ (function () {
+ function BatchTextureArray() {
+ this.elements = [];
+ this.ids = [];
+ this.count = 0;
+ }
+ BatchTextureArray.prototype.clear = function () {
+ for (var i = 0; i < this.count; i++) {
+ this.elements[i] = null;
+ }
+ this.count = 0;
+ };
+ return BatchTextureArray;
+ }());
+
+ /**
+ * Flexible wrapper around `ArrayBuffer` that also provides typed array views on demand.
+ * @memberof PIXI
+ */
+ var ViewableBuffer = /** @class */ (function () {
+ function ViewableBuffer(sizeOrBuffer) {
+ if (typeof sizeOrBuffer === 'number') {
+ this.rawBinaryData = new ArrayBuffer(sizeOrBuffer);
+ }
+ else if (sizeOrBuffer instanceof Uint8Array) {
+ this.rawBinaryData = sizeOrBuffer.buffer;
+ }
+ else {
+ this.rawBinaryData = sizeOrBuffer;
+ }
+ this.uint32View = new Uint32Array(this.rawBinaryData);
+ this.float32View = new Float32Array(this.rawBinaryData);
+ }
+ Object.defineProperty(ViewableBuffer.prototype, "int8View", {
+ /** View on the raw binary data as a `Int8Array`. */
+ get: function () {
+ if (!this._int8View) {
+ this._int8View = new Int8Array(this.rawBinaryData);
+ }
+ return this._int8View;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(ViewableBuffer.prototype, "uint8View", {
+ /** View on the raw binary data as a `Uint8Array`. */
+ get: function () {
+ if (!this._uint8View) {
+ this._uint8View = new Uint8Array(this.rawBinaryData);
+ }
+ return this._uint8View;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(ViewableBuffer.prototype, "int16View", {
+ /** View on the raw binary data as a `Int16Array`. */
+ get: function () {
+ if (!this._int16View) {
+ this._int16View = new Int16Array(this.rawBinaryData);
+ }
+ return this._int16View;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(ViewableBuffer.prototype, "uint16View", {
+ /** View on the raw binary data as a `Uint16Array`. */
+ get: function () {
+ if (!this._uint16View) {
+ this._uint16View = new Uint16Array(this.rawBinaryData);
+ }
+ return this._uint16View;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(ViewableBuffer.prototype, "int32View", {
+ /** View on the raw binary data as a `Int32Array`. */
+ get: function () {
+ if (!this._int32View) {
+ this._int32View = new Int32Array(this.rawBinaryData);
+ }
+ return this._int32View;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Returns the view of the given type.
+ * @param type - One of `int8`, `uint8`, `int16`,
+ * `uint16`, `int32`, `uint32`, and `float32`.
+ * @returns - typed array of given type
+ */
+ ViewableBuffer.prototype.view = function (type) {
+ return this[type + "View"];
+ };
+ /** Destroys all buffer references. Do not use after calling this. */
+ ViewableBuffer.prototype.destroy = function () {
+ this.rawBinaryData = null;
+ this._int8View = null;
+ this._uint8View = null;
+ this._int16View = null;
+ this._uint16View = null;
+ this._int32View = null;
+ this.uint32View = null;
+ this.float32View = null;
+ };
+ ViewableBuffer.sizeOf = function (type) {
+ switch (type) {
+ case 'int8':
+ case 'uint8':
+ return 1;
+ case 'int16':
+ case 'uint16':
+ return 2;
+ case 'int32':
+ case 'uint32':
+ case 'float32':
+ return 4;
+ default:
+ throw new Error(type + " isn't a valid view type");
+ }
+ };
+ return ViewableBuffer;
+ }());
+
+ /**
+ * Renderer dedicated to drawing and batching sprites.
+ *
+ * This is the default batch renderer. It buffers objects
+ * with texture-based geometries and renders them in
+ * batches. It uploads multiple textures to the GPU to
+ * reduce to the number of draw calls.
+ * @memberof PIXI
+ */
+ var AbstractBatchRenderer = /** @class */ (function (_super) {
+ __extends$i(AbstractBatchRenderer, _super);
+ /**
+ * This will hook onto the renderer's `contextChange`
+ * and `prerender` signals.
+ * @param {PIXI.Renderer} renderer - The renderer this works for.
+ */
+ function AbstractBatchRenderer(renderer) {
+ var _this = _super.call(this, renderer) || this;
+ _this.shaderGenerator = null;
+ _this.geometryClass = null;
+ _this.vertexSize = null;
+ _this.state = State.for2d();
+ _this.size = settings$1.SPRITE_BATCH_SIZE * 4;
+ _this._vertexCount = 0;
+ _this._indexCount = 0;
+ _this._bufferedElements = [];
+ _this._bufferedTextures = [];
+ _this._bufferSize = 0;
+ _this._shader = null;
+ _this._packedGeometries = [];
+ _this._packedGeometryPoolSize = 2;
+ _this._flushId = 0;
+ _this._aBuffers = {};
+ _this._iBuffers = {};
+ _this.MAX_TEXTURES = 1;
+ _this.renderer.on('prerender', _this.onPrerender, _this);
+ renderer.runners.contextChange.add(_this);
+ _this._dcIndex = 0;
+ _this._aIndex = 0;
+ _this._iIndex = 0;
+ _this._attributeBuffer = null;
+ _this._indexBuffer = null;
+ _this._tempBoundTextures = [];
+ return _this;
+ }
+ /**
+ * Handles the `contextChange` signal.
+ *
+ * It calculates `this.MAX_TEXTURES` and allocating the packed-geometry object pool.
+ */
+ AbstractBatchRenderer.prototype.contextChange = function () {
+ var gl = this.renderer.gl;
+ if (settings$1.PREFER_ENV === exports.ENV.WEBGL_LEGACY) {
+ this.MAX_TEXTURES = 1;
+ }
+ else {
+ // step 1: first check max textures the GPU can handle.
+ this.MAX_TEXTURES = Math.min(gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS), settings$1.SPRITE_MAX_TEXTURES);
+ // step 2: check the maximum number of if statements the shader can have too..
+ this.MAX_TEXTURES = checkMaxIfStatementsInShader(this.MAX_TEXTURES, gl);
+ }
+ this._shader = this.shaderGenerator.generateShader(this.MAX_TEXTURES);
+ // we use the second shader as the first one depending on your browser
+ // may omit aTextureId as it is not used by the shader so is optimized out.
+ for (var i = 0; i < this._packedGeometryPoolSize; i++) {
+ /* eslint-disable max-len */
+ this._packedGeometries[i] = new (this.geometryClass)();
+ }
+ this.initFlushBuffers();
+ };
+ /** Makes sure that static and dynamic flush pooled objects have correct dimensions. */
+ AbstractBatchRenderer.prototype.initFlushBuffers = function () {
+ var _drawCallPool = AbstractBatchRenderer._drawCallPool, _textureArrayPool = AbstractBatchRenderer._textureArrayPool;
+ // max draw calls
+ var MAX_SPRITES = this.size / 4;
+ // max texture arrays
+ var MAX_TA = Math.floor(MAX_SPRITES / this.MAX_TEXTURES) + 1;
+ while (_drawCallPool.length < MAX_SPRITES) {
+ _drawCallPool.push(new BatchDrawCall());
+ }
+ while (_textureArrayPool.length < MAX_TA) {
+ _textureArrayPool.push(new BatchTextureArray());
+ }
+ for (var i = 0; i < this.MAX_TEXTURES; i++) {
+ this._tempBoundTextures[i] = null;
+ }
+ };
+ /** Handles the `prerender` signal. It ensures that flushes start from the first geometry object again. */
+ AbstractBatchRenderer.prototype.onPrerender = function () {
+ this._flushId = 0;
+ };
+ /**
+ * Buffers the "batchable" object. It need not be rendered immediately.
+ * @param {PIXI.DisplayObject} element - the element to render when
+ * using this renderer
+ */
+ AbstractBatchRenderer.prototype.render = function (element) {
+ if (!element._texture.valid) {
+ return;
+ }
+ if (this._vertexCount + (element.vertexData.length / 2) > this.size) {
+ this.flush();
+ }
+ this._vertexCount += element.vertexData.length / 2;
+ this._indexCount += element.indices.length;
+ this._bufferedTextures[this._bufferSize] = element._texture.baseTexture;
+ this._bufferedElements[this._bufferSize++] = element;
+ };
+ AbstractBatchRenderer.prototype.buildTexturesAndDrawCalls = function () {
+ var _a = this, textures = _a._bufferedTextures, MAX_TEXTURES = _a.MAX_TEXTURES;
+ var textureArrays = AbstractBatchRenderer._textureArrayPool;
+ var batch = this.renderer.batch;
+ var boundTextures = this._tempBoundTextures;
+ var touch = this.renderer.textureGC.count;
+ var TICK = ++BaseTexture._globalBatch;
+ var countTexArrays = 0;
+ var texArray = textureArrays[0];
+ var start = 0;
+ batch.copyBoundTextures(boundTextures, MAX_TEXTURES);
+ for (var i = 0; i < this._bufferSize; ++i) {
+ var tex = textures[i];
+ textures[i] = null;
+ if (tex._batchEnabled === TICK) {
+ continue;
+ }
+ if (texArray.count >= MAX_TEXTURES) {
+ batch.boundArray(texArray, boundTextures, TICK, MAX_TEXTURES);
+ this.buildDrawCalls(texArray, start, i);
+ start = i;
+ texArray = textureArrays[++countTexArrays];
+ ++TICK;
+ }
+ tex._batchEnabled = TICK;
+ tex.touched = touch;
+ texArray.elements[texArray.count++] = tex;
+ }
+ if (texArray.count > 0) {
+ batch.boundArray(texArray, boundTextures, TICK, MAX_TEXTURES);
+ this.buildDrawCalls(texArray, start, this._bufferSize);
+ ++countTexArrays;
+ ++TICK;
+ }
+ // Clean-up
+ for (var i = 0; i < boundTextures.length; i++) {
+ boundTextures[i] = null;
+ }
+ BaseTexture._globalBatch = TICK;
+ };
+ /**
+ * Populating drawcalls for rendering
+ * @param texArray
+ * @param start
+ * @param finish
+ */
+ AbstractBatchRenderer.prototype.buildDrawCalls = function (texArray, start, finish) {
+ var _a = this, elements = _a._bufferedElements, _attributeBuffer = _a._attributeBuffer, _indexBuffer = _a._indexBuffer, vertexSize = _a.vertexSize;
+ var drawCalls = AbstractBatchRenderer._drawCallPool;
+ var dcIndex = this._dcIndex;
+ var aIndex = this._aIndex;
+ var iIndex = this._iIndex;
+ var drawCall = drawCalls[dcIndex];
+ drawCall.start = this._iIndex;
+ drawCall.texArray = texArray;
+ for (var i = start; i < finish; ++i) {
+ var sprite = elements[i];
+ var tex = sprite._texture.baseTexture;
+ var spriteBlendMode = premultiplyBlendMode[tex.alphaMode ? 1 : 0][sprite.blendMode];
+ elements[i] = null;
+ if (start < i && drawCall.blend !== spriteBlendMode) {
+ drawCall.size = iIndex - drawCall.start;
+ start = i;
+ drawCall = drawCalls[++dcIndex];
+ drawCall.texArray = texArray;
+ drawCall.start = iIndex;
+ }
+ this.packInterleavedGeometry(sprite, _attributeBuffer, _indexBuffer, aIndex, iIndex);
+ aIndex += sprite.vertexData.length / 2 * vertexSize;
+ iIndex += sprite.indices.length;
+ drawCall.blend = spriteBlendMode;
+ }
+ if (start < finish) {
+ drawCall.size = iIndex - drawCall.start;
+ ++dcIndex;
+ }
+ this._dcIndex = dcIndex;
+ this._aIndex = aIndex;
+ this._iIndex = iIndex;
+ };
+ /**
+ * Bind textures for current rendering
+ * @param texArray
+ */
+ AbstractBatchRenderer.prototype.bindAndClearTexArray = function (texArray) {
+ var textureSystem = this.renderer.texture;
+ for (var j = 0; j < texArray.count; j++) {
+ textureSystem.bind(texArray.elements[j], texArray.ids[j]);
+ texArray.elements[j] = null;
+ }
+ texArray.count = 0;
+ };
+ AbstractBatchRenderer.prototype.updateGeometry = function () {
+ var _a = this, packedGeometries = _a._packedGeometries, attributeBuffer = _a._attributeBuffer, indexBuffer = _a._indexBuffer;
+ if (!settings$1.CAN_UPLOAD_SAME_BUFFER) { /* Usually on iOS devices, where the browser doesn't
+ like uploads to the same buffer in a single frame. */
+ if (this._packedGeometryPoolSize <= this._flushId) {
+ this._packedGeometryPoolSize++;
+ packedGeometries[this._flushId] = new (this.geometryClass)();
+ }
+ packedGeometries[this._flushId]._buffer.update(attributeBuffer.rawBinaryData);
+ packedGeometries[this._flushId]._indexBuffer.update(indexBuffer);
+ this.renderer.geometry.bind(packedGeometries[this._flushId]);
+ this.renderer.geometry.updateBuffers();
+ this._flushId++;
+ }
+ else {
+ // lets use the faster option, always use buffer number 0
+ packedGeometries[this._flushId]._buffer.update(attributeBuffer.rawBinaryData);
+ packedGeometries[this._flushId]._indexBuffer.update(indexBuffer);
+ this.renderer.geometry.updateBuffers();
+ }
+ };
+ AbstractBatchRenderer.prototype.drawBatches = function () {
+ var dcCount = this._dcIndex;
+ var _a = this.renderer, gl = _a.gl, stateSystem = _a.state;
+ var drawCalls = AbstractBatchRenderer._drawCallPool;
+ var curTexArray = null;
+ // Upload textures and do the draw calls
+ for (var i = 0; i < dcCount; i++) {
+ var _b = drawCalls[i], texArray = _b.texArray, type = _b.type, size = _b.size, start = _b.start, blend = _b.blend;
+ if (curTexArray !== texArray) {
+ curTexArray = texArray;
+ this.bindAndClearTexArray(texArray);
+ }
+ this.state.blendMode = blend;
+ stateSystem.set(this.state);
+ gl.drawElements(type, size, gl.UNSIGNED_SHORT, start * 2);
+ }
+ };
+ /** Renders the content _now_ and empties the current batch. */
+ AbstractBatchRenderer.prototype.flush = function () {
+ if (this._vertexCount === 0) {
+ return;
+ }
+ this._attributeBuffer = this.getAttributeBuffer(this._vertexCount);
+ this._indexBuffer = this.getIndexBuffer(this._indexCount);
+ this._aIndex = 0;
+ this._iIndex = 0;
+ this._dcIndex = 0;
+ this.buildTexturesAndDrawCalls();
+ this.updateGeometry();
+ this.drawBatches();
+ // reset elements buffer for the next flush
+ this._bufferSize = 0;
+ this._vertexCount = 0;
+ this._indexCount = 0;
+ };
+ /** Starts a new sprite batch. */
+ AbstractBatchRenderer.prototype.start = function () {
+ this.renderer.state.set(this.state);
+ this.renderer.texture.ensureSamplerType(this.MAX_TEXTURES);
+ this.renderer.shader.bind(this._shader);
+ if (settings$1.CAN_UPLOAD_SAME_BUFFER) {
+ // bind buffer #0, we don't need others
+ this.renderer.geometry.bind(this._packedGeometries[this._flushId]);
+ }
+ };
+ /** Stops and flushes the current batch. */
+ AbstractBatchRenderer.prototype.stop = function () {
+ this.flush();
+ };
+ /** Destroys this `AbstractBatchRenderer`. It cannot be used again. */
+ AbstractBatchRenderer.prototype.destroy = function () {
+ for (var i = 0; i < this._packedGeometryPoolSize; i++) {
+ if (this._packedGeometries[i]) {
+ this._packedGeometries[i].destroy();
+ }
+ }
+ this.renderer.off('prerender', this.onPrerender, this);
+ this._aBuffers = null;
+ this._iBuffers = null;
+ this._packedGeometries = null;
+ this._attributeBuffer = null;
+ this._indexBuffer = null;
+ if (this._shader) {
+ this._shader.destroy();
+ this._shader = null;
+ }
+ _super.prototype.destroy.call(this);
+ };
+ /**
+ * Fetches an attribute buffer from `this._aBuffers` that can hold atleast `size` floats.
+ * @param size - minimum capacity required
+ * @returns - buffer than can hold atleast `size` floats
+ */
+ AbstractBatchRenderer.prototype.getAttributeBuffer = function (size) {
+ // 8 vertices is enough for 2 quads
+ var roundedP2 = nextPow2(Math.ceil(size / 8));
+ var roundedSizeIndex = log2(roundedP2);
+ var roundedSize = roundedP2 * 8;
+ if (this._aBuffers.length <= roundedSizeIndex) {
+ this._iBuffers.length = roundedSizeIndex + 1;
+ }
+ var buffer = this._aBuffers[roundedSize];
+ if (!buffer) {
+ this._aBuffers[roundedSize] = buffer = new ViewableBuffer(roundedSize * this.vertexSize * 4);
+ }
+ return buffer;
+ };
+ /**
+ * Fetches an index buffer from `this._iBuffers` that can
+ * have at least `size` capacity.
+ * @param size - minimum required capacity
+ * @returns - buffer that can fit `size` indices.
+ */
+ AbstractBatchRenderer.prototype.getIndexBuffer = function (size) {
+ // 12 indices is enough for 2 quads
+ var roundedP2 = nextPow2(Math.ceil(size / 12));
+ var roundedSizeIndex = log2(roundedP2);
+ var roundedSize = roundedP2 * 12;
+ if (this._iBuffers.length <= roundedSizeIndex) {
+ this._iBuffers.length = roundedSizeIndex + 1;
+ }
+ var buffer = this._iBuffers[roundedSizeIndex];
+ if (!buffer) {
+ this._iBuffers[roundedSizeIndex] = buffer = new Uint16Array(roundedSize);
+ }
+ return buffer;
+ };
+ /**
+ * Takes the four batching parameters of `element`, interleaves
+ * and pushes them into the batching attribute/index buffers given.
+ *
+ * It uses these properties: `vertexData` `uvs`, `textureId` and
+ * `indicies`. It also uses the "tint" of the base-texture, if
+ * present.
+ * @param {PIXI.DisplayObject} element - element being rendered
+ * @param attributeBuffer - attribute buffer.
+ * @param indexBuffer - index buffer
+ * @param aIndex - number of floats already in the attribute buffer
+ * @param iIndex - number of indices already in `indexBuffer`
+ */
+ AbstractBatchRenderer.prototype.packInterleavedGeometry = function (element, attributeBuffer, indexBuffer, aIndex, iIndex) {
+ var uint32View = attributeBuffer.uint32View, float32View = attributeBuffer.float32View;
+ var packedVertices = aIndex / this.vertexSize;
+ var uvs = element.uvs;
+ var indicies = element.indices;
+ var vertexData = element.vertexData;
+ var textureId = element._texture.baseTexture._batchLocation;
+ var alpha = Math.min(element.worldAlpha, 1.0);
+ var argb = (alpha < 1.0
+ && element._texture.baseTexture.alphaMode)
+ ? premultiplyTint(element._tintRGB, alpha)
+ : element._tintRGB + (alpha * 255 << 24);
+ // lets not worry about tint! for now..
+ for (var i = 0; i < vertexData.length; i += 2) {
+ float32View[aIndex++] = vertexData[i];
+ float32View[aIndex++] = vertexData[i + 1];
+ float32View[aIndex++] = uvs[i];
+ float32View[aIndex++] = uvs[i + 1];
+ uint32View[aIndex++] = argb;
+ float32View[aIndex++] = textureId;
+ }
+ for (var i = 0; i < indicies.length; i++) {
+ indexBuffer[iIndex++] = packedVertices + indicies[i];
+ }
+ };
+ /**
+ * Pool of `BatchDrawCall` objects that `flush` used
+ * to create "batches" of the objects being rendered.
+ *
+ * These are never re-allocated again.
+ * Shared between all batch renderers because it can be only one "flush" working at the moment.
+ * @member {PIXI.BatchDrawCall[]}
+ */
+ AbstractBatchRenderer._drawCallPool = [];
+ /**
+ * Pool of `BatchDrawCall` objects that `flush` used
+ * to create "batches" of the objects being rendered.
+ *
+ * These are never re-allocated again.
+ * Shared between all batch renderers because it can be only one "flush" working at the moment.
+ * @member {PIXI.BatchTextureArray[]}
+ */
+ AbstractBatchRenderer._textureArrayPool = [];
+ return AbstractBatchRenderer;
+ }(ObjectRenderer));
+
+ /**
+ * Helper that generates batching multi-texture shader. Use it with your new BatchRenderer
+ * @memberof PIXI
+ */
+ var BatchShaderGenerator = /** @class */ (function () {
+ /**
+ * @param vertexSrc - Vertex shader
+ * @param fragTemplate - Fragment shader template
+ */
+ function BatchShaderGenerator(vertexSrc, fragTemplate) {
+ this.vertexSrc = vertexSrc;
+ this.fragTemplate = fragTemplate;
+ this.programCache = {};
+ this.defaultGroupCache = {};
+ if (fragTemplate.indexOf('%count%') < 0) {
+ throw new Error('Fragment template must contain "%count%".');
+ }
+ if (fragTemplate.indexOf('%forloop%') < 0) {
+ throw new Error('Fragment template must contain "%forloop%".');
+ }
+ }
+ BatchShaderGenerator.prototype.generateShader = function (maxTextures) {
+ if (!this.programCache[maxTextures]) {
+ var sampleValues = new Int32Array(maxTextures);
+ for (var i = 0; i < maxTextures; i++) {
+ sampleValues[i] = i;
+ }
+ this.defaultGroupCache[maxTextures] = UniformGroup.from({ uSamplers: sampleValues }, true);
+ var fragmentSrc = this.fragTemplate;
+ fragmentSrc = fragmentSrc.replace(/%count%/gi, "" + maxTextures);
+ fragmentSrc = fragmentSrc.replace(/%forloop%/gi, this.generateSampleSrc(maxTextures));
+ this.programCache[maxTextures] = new Program(this.vertexSrc, fragmentSrc);
+ }
+ var uniforms = {
+ tint: new Float32Array([1, 1, 1, 1]),
+ translationMatrix: new Matrix(),
+ default: this.defaultGroupCache[maxTextures],
+ };
+ return new Shader(this.programCache[maxTextures], uniforms);
+ };
+ BatchShaderGenerator.prototype.generateSampleSrc = function (maxTextures) {
+ var src = '';
+ src += '\n';
+ src += '\n';
+ for (var i = 0; i < maxTextures; i++) {
+ if (i > 0) {
+ src += '\nelse ';
+ }
+ if (i < maxTextures - 1) {
+ src += "if(vTextureId < " + i + ".5)";
+ }
+ src += '\n{';
+ src += "\n\tcolor = texture2D(uSamplers[" + i + "], vTextureCoord);";
+ src += '\n}';
+ }
+ src += '\n';
+ src += '\n';
+ return src;
+ };
+ return BatchShaderGenerator;
+ }());
+
+ /**
+ * Geometry used to batch standard PIXI content (e.g. Mesh, Sprite, Graphics objects).
+ * @memberof PIXI
+ */
+ var BatchGeometry = /** @class */ (function (_super) {
+ __extends$i(BatchGeometry, _super);
+ /**
+ * @param {boolean} [_static=false] - Optimization flag, where `false`
+ * is updated every frame, `true` doesn't change frame-to-frame.
+ */
+ function BatchGeometry(_static) {
+ if (_static === void 0) { _static = false; }
+ var _this = _super.call(this) || this;
+ _this._buffer = new Buffer(null, _static, false);
+ _this._indexBuffer = new Buffer(null, _static, true);
+ _this.addAttribute('aVertexPosition', _this._buffer, 2, false, exports.TYPES.FLOAT)
+ .addAttribute('aTextureCoord', _this._buffer, 2, false, exports.TYPES.FLOAT)
+ .addAttribute('aColor', _this._buffer, 4, true, exports.TYPES.UNSIGNED_BYTE)
+ .addAttribute('aTextureId', _this._buffer, 1, true, exports.TYPES.FLOAT)
+ .addIndex(_this._indexBuffer);
+ return _this;
+ }
+ return BatchGeometry;
+ }(Geometry));
+
+ var defaultVertex = "precision highp float;\nattribute vec2 aVertexPosition;\nattribute vec2 aTextureCoord;\nattribute vec4 aColor;\nattribute float aTextureId;\n\nuniform mat3 projectionMatrix;\nuniform mat3 translationMatrix;\nuniform vec4 tint;\n\nvarying vec2 vTextureCoord;\nvarying vec4 vColor;\nvarying float vTextureId;\n\nvoid main(void){\n gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);\n\n vTextureCoord = aTextureCoord;\n vTextureId = aTextureId;\n vColor = aColor * tint;\n}\n";
+
+ var defaultFragment = "varying vec2 vTextureCoord;\nvarying vec4 vColor;\nvarying float vTextureId;\nuniform sampler2D uSamplers[%count%];\n\nvoid main(void){\n vec4 color;\n %forloop%\n gl_FragColor = color * vColor;\n}\n";
+
+ /** @memberof PIXI */
+ var BatchPluginFactory = /** @class */ (function () {
+ function BatchPluginFactory() {
+ }
+ /**
+ * Create a new BatchRenderer plugin for Renderer. this convenience can provide an easy way
+ * to extend BatchRenderer with all the necessary pieces.
+ * @example
+ * const fragment = `
+ * varying vec2 vTextureCoord;
+ * varying vec4 vColor;
+ * varying float vTextureId;
+ * uniform sampler2D uSamplers[%count%];
+ *
+ * void main(void){
+ * vec4 color;
+ * %forloop%
+ * gl_FragColor = vColor * vec4(color.a - color.rgb, color.a);
+ * }
+ * `;
+ * const InvertBatchRenderer = PIXI.BatchPluginFactory.create({ fragment });
+ * PIXI.extensions.add({
+ * name: 'invert',
+ * ref: InvertBatchRenderer,
+ * type: PIXI.ExtensionType.RendererPlugin,
+ * });
+ * const sprite = new PIXI.Sprite();
+ * sprite.pluginName = 'invert';
+ * @param {object} [options]
+ * @param {string} [options.vertex=PIXI.BatchPluginFactory.defaultVertexSrc] - Vertex shader source
+ * @param {string} [options.fragment=PIXI.BatchPluginFactory.defaultFragmentTemplate] - Fragment shader template
+ * @param {number} [options.vertexSize=6] - Vertex size
+ * @param {object} [options.geometryClass=PIXI.BatchGeometry]
+ * @returns {*} New batch renderer plugin
+ */
+ BatchPluginFactory.create = function (options) {
+ var _a = Object.assign({
+ vertex: defaultVertex,
+ fragment: defaultFragment,
+ geometryClass: BatchGeometry,
+ vertexSize: 6,
+ }, options), vertex = _a.vertex, fragment = _a.fragment, vertexSize = _a.vertexSize, geometryClass = _a.geometryClass;
+ return /** @class */ (function (_super) {
+ __extends$i(BatchPlugin, _super);
+ function BatchPlugin(renderer) {
+ var _this = _super.call(this, renderer) || this;
+ _this.shaderGenerator = new BatchShaderGenerator(vertex, fragment);
+ _this.geometryClass = geometryClass;
+ _this.vertexSize = vertexSize;
+ return _this;
+ }
+ return BatchPlugin;
+ }(AbstractBatchRenderer));
+ };
+ Object.defineProperty(BatchPluginFactory, "defaultVertexSrc", {
+ /**
+ * The default vertex shader source
+ * @readonly
+ */
+ get: function () {
+ return defaultVertex;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BatchPluginFactory, "defaultFragmentTemplate", {
+ /**
+ * The default fragment shader source
+ * @readonly
+ */
+ get: function () {
+ return defaultFragment;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return BatchPluginFactory;
+ }());
+ // Setup the default BatchRenderer plugin, this is what
+ // we'll actually export at the root level
+ var BatchRenderer = BatchPluginFactory.create();
+ Object.assign(BatchRenderer, {
+ extension: {
+ name: 'batch',
+ type: exports.ExtensionType.RendererPlugin,
+ },
+ });
+
+ /**
+ * @memberof PIXI
+ * @namespace resources
+ * @see PIXI
+ * @deprecated since 6.0.0
+ */
+ var resources = {};
+ var _loop_1 = function (name) {
+ Object.defineProperty(resources, name, {
+ get: function () {
+ deprecation$1('6.0.0', "PIXI.systems." + name + " has moved to PIXI." + name);
+ return _resources[name];
+ },
+ });
+ };
+ for (var name in _resources) {
+ _loop_1(name);
+ }
+ /**
+ * @memberof PIXI
+ * @namespace systems
+ * @see PIXI
+ * @deprecated since 6.0.0
+ */
+ var systems = {};
+ var _loop_2 = function (name) {
+ Object.defineProperty(systems, name, {
+ get: function () {
+ deprecation$1('6.0.0', "PIXI.resources." + name + " has moved to PIXI." + name);
+ return _systems[name];
+ },
+ });
+ };
+ for (var name in _systems) {
+ _loop_2(name);
+ }
+
+ /**
+ * @namespace PIXI
+ */
+ /**
+ * String of the current PIXI version.
+ * @memberof PIXI
+ */
+ var VERSION = '6.5.3';
+
+ /*!
+ * @pixi/accessibility - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/accessibility is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /**
+ * Default property values of accessible objects
+ * used by {@link PIXI.AccessibilityManager}.
+ * @private
+ * @function accessibleTarget
+ * @memberof PIXI
+ * @type {object}
+ * @example
+ * function MyObject() {}
+ *
+ * Object.assign(
+ * MyObject.prototype,
+ * PIXI.accessibleTarget
+ * );
+ */
+ var accessibleTarget = {
+ /**
+ * Flag for if the object is accessible. If true AccessibilityManager will overlay a
+ * shadow div with attributes set
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
+ */
+ accessible: false,
+ /**
+ * Sets the title attribute of the shadow div
+ * If accessibleTitle AND accessibleHint has not been this will default to 'displayObject [tabIndex]'
+ * @member {?string}
+ * @memberof PIXI.DisplayObject#
+ */
+ accessibleTitle: null,
+ /**
+ * Sets the aria-label attribute of the shadow div
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
+ */
+ accessibleHint: null,
+ /**
+ * @member {number}
+ * @memberof PIXI.DisplayObject#
+ * @private
+ * @todo Needs docs.
+ */
+ tabIndex: 0,
+ /**
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
+ * @todo Needs docs.
+ */
+ _accessibleActive: false,
+ /**
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
+ * @todo Needs docs.
+ */
+ _accessibleDiv: null,
+ /**
+ * Specify the type of div the accessible layer is. Screen readers treat the element differently
+ * depending on this type. Defaults to button.
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
+ * @default 'button'
+ */
+ accessibleType: 'button',
+ /**
+ * Specify the pointer-events the accessible div will use
+ * Defaults to auto.
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
+ * @default 'auto'
+ */
+ accessiblePointerEvents: 'auto',
+ /**
+ * Setting to false will prevent any children inside this container to
+ * be accessible. Defaults to true.
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
+ * @default true
+ */
+ accessibleChildren: true,
+ renderId: -1,
+ };
+
+ // add some extra variables to the container..
+ DisplayObject.mixin(accessibleTarget);
+ var KEY_CODE_TAB = 9;
+ var DIV_TOUCH_SIZE = 100;
+ var DIV_TOUCH_POS_X = 0;
+ var DIV_TOUCH_POS_Y = 0;
+ var DIV_TOUCH_ZINDEX = 2;
+ var DIV_HOOK_SIZE = 1;
+ var DIV_HOOK_POS_X = -1000;
+ var DIV_HOOK_POS_Y = -1000;
+ var DIV_HOOK_ZINDEX = 2;
+ /**
+ * The Accessibility manager recreates the ability to tab and have content read by screen readers.
+ * This is very important as it can possibly help people with disabilities access PixiJS content.
+ *
+ * A DisplayObject can be made accessible just like it can be made interactive. This manager will map the
+ * events as if the mouse was being used, minimizing the effort required to implement.
+ *
+ * An instance of this class is automatically created by default, and can be found at `renderer.plugins.accessibility`
+ * @class
+ * @memberof PIXI
+ */
+ var AccessibilityManager = /** @class */ (function () {
+ /**
+ * @param {PIXI.CanvasRenderer|PIXI.Renderer} renderer - A reference to the current renderer
+ */
+ function AccessibilityManager(renderer) {
+ /** Setting this to true will visually show the divs. */
+ this.debug = false;
+ /** Internal variable, see isActive getter. */
+ this._isActive = false;
+ /** Internal variable, see isMobileAccessibility getter. */
+ this._isMobileAccessibility = false;
+ /** A simple pool for storing divs. */
+ this.pool = [];
+ /** This is a tick used to check if an object is no longer being rendered. */
+ this.renderId = 0;
+ /** The array of currently active accessible items. */
+ this.children = [];
+ /** Count to throttle div updates on android devices. */
+ this.androidUpdateCount = 0;
+ /** The frequency to update the div elements. */
+ this.androidUpdateFrequency = 500; // 2fps
+ this._hookDiv = null;
+ if (isMobile$2.tablet || isMobile$2.phone) {
+ this.createTouchHook();
+ }
+ // first we create a div that will sit over the PixiJS element. This is where the div overlays will go.
+ var div = document.createElement('div');
+ div.style.width = DIV_TOUCH_SIZE + "px";
+ div.style.height = DIV_TOUCH_SIZE + "px";
+ div.style.position = 'absolute';
+ div.style.top = DIV_TOUCH_POS_X + "px";
+ div.style.left = DIV_TOUCH_POS_Y + "px";
+ div.style.zIndex = DIV_TOUCH_ZINDEX.toString();
+ this.div = div;
+ this.renderer = renderer;
+ /**
+ * pre-bind the functions
+ * @type {Function}
+ * @private
+ */
+ this._onKeyDown = this._onKeyDown.bind(this);
+ /**
+ * pre-bind the functions
+ * @type {Function}
+ * @private
+ */
+ this._onMouseMove = this._onMouseMove.bind(this);
+ // let listen for tab.. once pressed we can fire up and show the accessibility layer
+ globalThis.addEventListener('keydown', this._onKeyDown, false);
+ }
+ Object.defineProperty(AccessibilityManager.prototype, "isActive", {
+ /**
+ * Value of `true` if accessibility is currently active and accessibility layers are showing.
+ * @member {boolean}
+ * @readonly
+ */
+ get: function () {
+ return this._isActive;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(AccessibilityManager.prototype, "isMobileAccessibility", {
+ /**
+ * Value of `true` if accessibility is enabled for touch devices.
+ * @member {boolean}
+ * @readonly
+ */
+ get: function () {
+ return this._isMobileAccessibility;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Creates the touch hooks.
+ * @private
+ */
+ AccessibilityManager.prototype.createTouchHook = function () {
+ var _this = this;
+ var hookDiv = document.createElement('button');
+ hookDiv.style.width = DIV_HOOK_SIZE + "px";
+ hookDiv.style.height = DIV_HOOK_SIZE + "px";
+ hookDiv.style.position = 'absolute';
+ hookDiv.style.top = DIV_HOOK_POS_X + "px";
+ hookDiv.style.left = DIV_HOOK_POS_Y + "px";
+ hookDiv.style.zIndex = DIV_HOOK_ZINDEX.toString();
+ hookDiv.style.backgroundColor = '#FF0000';
+ hookDiv.title = 'select to enable accessibility for this content';
+ hookDiv.addEventListener('focus', function () {
+ _this._isMobileAccessibility = true;
+ _this.activate();
+ _this.destroyTouchHook();
+ });
+ document.body.appendChild(hookDiv);
+ this._hookDiv = hookDiv;
+ };
+ /**
+ * Destroys the touch hooks.
+ * @private
+ */
+ AccessibilityManager.prototype.destroyTouchHook = function () {
+ if (!this._hookDiv) {
+ return;
+ }
+ document.body.removeChild(this._hookDiv);
+ this._hookDiv = null;
+ };
+ /**
+ * Activating will cause the Accessibility layer to be shown.
+ * This is called when a user presses the tab key.
+ * @private
+ */
+ AccessibilityManager.prototype.activate = function () {
+ var _a;
+ if (this._isActive) {
+ return;
+ }
+ this._isActive = true;
+ globalThis.document.addEventListener('mousemove', this._onMouseMove, true);
+ globalThis.removeEventListener('keydown', this._onKeyDown, false);
+ this.renderer.on('postrender', this.update, this);
+ (_a = this.renderer.view.parentNode) === null || _a === void 0 ? void 0 : _a.appendChild(this.div);
+ };
+ /**
+ * Deactivating will cause the Accessibility layer to be hidden.
+ * This is called when a user moves the mouse.
+ * @private
+ */
+ AccessibilityManager.prototype.deactivate = function () {
+ var _a;
+ if (!this._isActive || this._isMobileAccessibility) {
+ return;
+ }
+ this._isActive = false;
+ globalThis.document.removeEventListener('mousemove', this._onMouseMove, true);
+ globalThis.addEventListener('keydown', this._onKeyDown, false);
+ this.renderer.off('postrender', this.update);
+ (_a = this.div.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(this.div);
+ };
+ /**
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
+ * @private
+ * @param {PIXI.Container} displayObject - The DisplayObject to check.
+ */
+ AccessibilityManager.prototype.updateAccessibleObjects = function (displayObject) {
+ if (!displayObject.visible || !displayObject.accessibleChildren) {
+ return;
+ }
+ if (displayObject.accessible && displayObject.interactive) {
+ if (!displayObject._accessibleActive) {
+ this.addChild(displayObject);
+ }
+ displayObject.renderId = this.renderId;
+ }
+ var children = displayObject.children;
+ if (children) {
+ for (var i = 0; i < children.length; i++) {
+ this.updateAccessibleObjects(children[i]);
+ }
+ }
+ };
+ /**
+ * Before each render this function will ensure that all divs are mapped correctly to their DisplayObjects.
+ * @private
+ */
+ AccessibilityManager.prototype.update = function () {
+ /* On Android default web browser, tab order seems to be calculated by position rather than tabIndex,
+ * moving buttons can cause focus to flicker between two buttons making it hard/impossible to navigate,
+ * so I am just running update every half a second, seems to fix it.
+ */
+ var now = performance.now();
+ if (isMobile$2.android.device && now < this.androidUpdateCount) {
+ return;
+ }
+ this.androidUpdateCount = now + this.androidUpdateFrequency;
+ if (!this.renderer.renderingToScreen) {
+ return;
+ }
+ // update children...
+ if (this.renderer._lastObjectRendered) {
+ this.updateAccessibleObjects(this.renderer._lastObjectRendered);
+ }
+ var _a = this.renderer.view.getBoundingClientRect(), left = _a.left, top = _a.top, width = _a.width, height = _a.height;
+ var _b = this.renderer, viewWidth = _b.width, viewHeight = _b.height, resolution = _b.resolution;
+ var sx = (width / viewWidth) * resolution;
+ var sy = (height / viewHeight) * resolution;
+ var div = this.div;
+ div.style.left = left + "px";
+ div.style.top = top + "px";
+ div.style.width = viewWidth + "px";
+ div.style.height = viewHeight + "px";
+ for (var i = 0; i < this.children.length; i++) {
+ var child = this.children[i];
+ if (child.renderId !== this.renderId) {
+ child._accessibleActive = false;
+ removeItems(this.children, i, 1);
+ this.div.removeChild(child._accessibleDiv);
+ this.pool.push(child._accessibleDiv);
+ child._accessibleDiv = null;
+ i--;
+ }
+ else {
+ // map div to display..
+ div = child._accessibleDiv;
+ var hitArea = child.hitArea;
+ var wt = child.worldTransform;
+ if (child.hitArea) {
+ div.style.left = (wt.tx + (hitArea.x * wt.a)) * sx + "px";
+ div.style.top = (wt.ty + (hitArea.y * wt.d)) * sy + "px";
+ div.style.width = hitArea.width * wt.a * sx + "px";
+ div.style.height = hitArea.height * wt.d * sy + "px";
+ }
+ else {
+ hitArea = child.getBounds();
+ this.capHitArea(hitArea);
+ div.style.left = hitArea.x * sx + "px";
+ div.style.top = hitArea.y * sy + "px";
+ div.style.width = hitArea.width * sx + "px";
+ div.style.height = hitArea.height * sy + "px";
+ // update button titles and hints if they exist and they've changed
+ if (div.title !== child.accessibleTitle && child.accessibleTitle !== null) {
+ div.title = child.accessibleTitle;
+ }
+ if (div.getAttribute('aria-label') !== child.accessibleHint
+ && child.accessibleHint !== null) {
+ div.setAttribute('aria-label', child.accessibleHint);
+ }
+ }
+ // the title or index may have changed, if so lets update it!
+ if (child.accessibleTitle !== div.title || child.tabIndex !== div.tabIndex) {
+ div.title = child.accessibleTitle;
+ div.tabIndex = child.tabIndex;
+ if (this.debug)
+ { this.updateDebugHTML(div); }
+ }
+ }
+ }
+ // increment the render id..
+ this.renderId++;
+ };
+ /**
+ * private function that will visually add the information to the
+ * accessability div
+ * @param {HTMLElement} div -
+ */
+ AccessibilityManager.prototype.updateDebugHTML = function (div) {
+ div.innerHTML = "type: " + div.type + "</br> title : " + div.title + "</br> tabIndex: " + div.tabIndex;
+ };
+ /**
+ * Adjust the hit area based on the bounds of a display object
+ * @param {PIXI.Rectangle} hitArea - Bounds of the child
+ */
+ AccessibilityManager.prototype.capHitArea = function (hitArea) {
+ if (hitArea.x < 0) {
+ hitArea.width += hitArea.x;
+ hitArea.x = 0;
+ }
+ if (hitArea.y < 0) {
+ hitArea.height += hitArea.y;
+ hitArea.y = 0;
+ }
+ var _a = this.renderer, viewWidth = _a.width, viewHeight = _a.height;
+ if (hitArea.x + hitArea.width > viewWidth) {
+ hitArea.width = viewWidth - hitArea.x;
+ }
+ if (hitArea.y + hitArea.height > viewHeight) {
+ hitArea.height = viewHeight - hitArea.y;
+ }
+ };
+ /**
+ * Adds a DisplayObject to the accessibility manager
+ * @private
+ * @param {PIXI.DisplayObject} displayObject - The child to make accessible.
+ */
+ AccessibilityManager.prototype.addChild = function (displayObject) {
+ // this.activate();
+ var div = this.pool.pop();
+ if (!div) {
+ div = document.createElement('button');
+ div.style.width = DIV_TOUCH_SIZE + "px";
+ div.style.height = DIV_TOUCH_SIZE + "px";
+ div.style.backgroundColor = this.debug ? 'rgba(255,255,255,0.5)' : 'transparent';
+ div.style.position = 'absolute';
+ div.style.zIndex = DIV_TOUCH_ZINDEX.toString();
+ div.style.borderStyle = 'none';
+ // ARIA attributes ensure that button title and hint updates are announced properly
+ if (navigator.userAgent.toLowerCase().indexOf('chrome') > -1) {
+ // Chrome doesn't need aria-live to work as intended; in fact it just gets more confused.
+ div.setAttribute('aria-live', 'off');
+ }
+ else {
+ div.setAttribute('aria-live', 'polite');
+ }
+ if (navigator.userAgent.match(/rv:.*Gecko\//)) {
+ // FireFox needs this to announce only the new button name
+ div.setAttribute('aria-relevant', 'additions');
+ }
+ else {
+ // required by IE, other browsers don't much care
+ div.setAttribute('aria-relevant', 'text');
+ }
+ div.addEventListener('click', this._onClick.bind(this));
+ div.addEventListener('focus', this._onFocus.bind(this));
+ div.addEventListener('focusout', this._onFocusOut.bind(this));
+ }
+ // set pointer events
+ div.style.pointerEvents = displayObject.accessiblePointerEvents;
+ // set the type, this defaults to button!
+ div.type = displayObject.accessibleType;
+ if (displayObject.accessibleTitle && displayObject.accessibleTitle !== null) {
+ div.title = displayObject.accessibleTitle;
+ }
+ else if (!displayObject.accessibleHint
+ || displayObject.accessibleHint === null) {
+ div.title = "displayObject " + displayObject.tabIndex;
+ }
+ if (displayObject.accessibleHint
+ && displayObject.accessibleHint !== null) {
+ div.setAttribute('aria-label', displayObject.accessibleHint);
+ }
+ if (this.debug)
+ { this.updateDebugHTML(div); }
+ displayObject._accessibleActive = true;
+ displayObject._accessibleDiv = div;
+ div.displayObject = displayObject;
+ this.children.push(displayObject);
+ this.div.appendChild(displayObject._accessibleDiv);
+ displayObject._accessibleDiv.tabIndex = displayObject.tabIndex;
+ };
+ /**
+ * Maps the div button press to pixi's InteractionManager (click)
+ * @private
+ * @param {MouseEvent} e - The click event.
+ */
+ AccessibilityManager.prototype._onClick = function (e) {
+ var interactionManager = this.renderer.plugins.interaction;
+ var displayObject = e.target.displayObject;
+ var eventData = interactionManager.eventData;
+ interactionManager.dispatchEvent(displayObject, 'click', eventData);
+ interactionManager.dispatchEvent(displayObject, 'pointertap', eventData);
+ interactionManager.dispatchEvent(displayObject, 'tap', eventData);
+ };
+ /**
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
+ * @private
+ * @param {FocusEvent} e - The focus event.
+ */
+ AccessibilityManager.prototype._onFocus = function (e) {
+ if (!e.target.getAttribute('aria-live')) {
+ e.target.setAttribute('aria-live', 'assertive');
+ }
+ var interactionManager = this.renderer.plugins.interaction;
+ var displayObject = e.target.displayObject;
+ var eventData = interactionManager.eventData;
+ interactionManager.dispatchEvent(displayObject, 'mouseover', eventData);
+ };
+ /**
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
+ * @private
+ * @param {FocusEvent} e - The focusout event.
+ */
+ AccessibilityManager.prototype._onFocusOut = function (e) {
+ if (!e.target.getAttribute('aria-live')) {
+ e.target.setAttribute('aria-live', 'polite');
+ }
+ var interactionManager = this.renderer.plugins.interaction;
+ var displayObject = e.target.displayObject;
+ var eventData = interactionManager.eventData;
+ interactionManager.dispatchEvent(displayObject, 'mouseout', eventData);
+ };
+ /**
+ * Is called when a key is pressed
+ * @private
+ * @param {KeyboardEvent} e - The keydown event.
+ */
+ AccessibilityManager.prototype._onKeyDown = function (e) {
+ if (e.keyCode !== KEY_CODE_TAB) {
+ return;
+ }
+ this.activate();
+ };
+ /**
+ * Is called when the mouse moves across the renderer element
+ * @private
+ * @param {MouseEvent} e - The mouse event.
+ */
+ AccessibilityManager.prototype._onMouseMove = function (e) {
+ if (e.movementX === 0 && e.movementY === 0) {
+ return;
+ }
+ this.deactivate();
+ };
+ /** Destroys the accessibility manager */
+ AccessibilityManager.prototype.destroy = function () {
+ this.destroyTouchHook();
+ this.div = null;
+ globalThis.document.removeEventListener('mousemove', this._onMouseMove, true);
+ globalThis.removeEventListener('keydown', this._onKeyDown);
+ this.pool = null;
+ this.children = null;
+ this.renderer = null;
+ };
+ /** @ignore */
+ AccessibilityManager.extension = {
+ name: 'accessibility',
+ type: [
+ exports.ExtensionType.RendererPlugin,
+ exports.ExtensionType.CanvasRendererPlugin ],
+ };
+ return AccessibilityManager;
+ }());
+
+ /*!
+ * @pixi/interaction - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/interaction is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /**
+ * Holds all information related to an Interaction event
+ * @memberof PIXI
+ */
+ var InteractionData = /** @class */ (function () {
+ function InteractionData() {
+ /**
+ * Pressure applied by the pointing device during the event. A Touch's force property
+ * will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pressure
+ */
+ this.pressure = 0;
+ /**
+ * From TouchEvents (not PointerEvents triggered by touches), the rotationAngle of the Touch.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Touch/rotationAngle
+ */
+ this.rotationAngle = 0;
+ /**
+ * Twist of a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ */
+ this.twist = 0;
+ /**
+ * Barrel pressure on a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ */
+ this.tangentialPressure = 0;
+ this.global = new Point();
+ this.target = null;
+ this.originalEvent = null;
+ this.identifier = null;
+ this.isPrimary = false;
+ this.button = 0;
+ this.buttons = 0;
+ this.width = 0;
+ this.height = 0;
+ this.tiltX = 0;
+ this.tiltY = 0;
+ this.pointerType = null;
+ this.pressure = 0;
+ this.rotationAngle = 0;
+ this.twist = 0;
+ this.tangentialPressure = 0;
+ }
+ Object.defineProperty(InteractionData.prototype, "pointerId", {
+ /**
+ * The unique identifier of the pointer. It will be the same as `identifier`.
+ * @readonly
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerId
+ */
+ get: function () {
+ return this.identifier;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * This will return the local coordinates of the specified displayObject for this InteractionData
+ * @param displayObject - The DisplayObject that you would like the local
+ * coords off
+ * @param point - A Point object in which to store the value, optional (otherwise
+ * will create a new point)
+ * @param globalPos - A Point object containing your custom global coords, optional
+ * (otherwise will use the current global coords)
+ * @returns - A point containing the coordinates of the InteractionData position relative
+ * to the DisplayObject
+ */
+ InteractionData.prototype.getLocalPosition = function (displayObject, point, globalPos) {
+ return displayObject.worldTransform.applyInverse(globalPos || this.global, point);
+ };
+ /**
+ * Copies properties from normalized event data.
+ * @param {Touch|MouseEvent|PointerEvent} event - The normalized event data
+ */
+ InteractionData.prototype.copyEvent = function (event) {
+ // isPrimary should only change on touchstart/pointerdown, so we don't want to overwrite
+ // it with "false" on later events when our shim for it on touch events might not be
+ // accurate
+ if ('isPrimary' in event && event.isPrimary) {
+ this.isPrimary = true;
+ }
+ this.button = 'button' in event && event.button;
+ // event.buttons is not available in all browsers (ie. Safari), but it does have a non-standard
+ // event.which property instead, which conveys the same information.
+ var buttons = 'buttons' in event && event.buttons;
+ this.buttons = Number.isInteger(buttons) ? buttons : 'which' in event && event.which;
+ this.width = 'width' in event && event.width;
+ this.height = 'height' in event && event.height;
+ this.tiltX = 'tiltX' in event && event.tiltX;
+ this.tiltY = 'tiltY' in event && event.tiltY;
+ this.pointerType = 'pointerType' in event && event.pointerType;
+ this.pressure = 'pressure' in event && event.pressure;
+ this.rotationAngle = 'rotationAngle' in event && event.rotationAngle;
+ this.twist = ('twist' in event && event.twist) || 0;
+ this.tangentialPressure = ('tangentialPressure' in event && event.tangentialPressure) || 0;
+ };
+ /** Resets the data for pooling. */
+ InteractionData.prototype.reset = function () {
+ // isPrimary is the only property that we really need to reset - everything else is
+ // guaranteed to be overwritten
+ this.isPrimary = false;
+ };
+ return InteractionData;
+ }());
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+ /* global Reflect, Promise */
+
+ var extendStatics$h = function(d, b) {
+ extendStatics$h = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
+ return extendStatics$h(d, b);
+ };
+
+ function __extends$h(d, b) {
+ extendStatics$h(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ }
+
+ /**
+ * Event class that mimics native DOM events.
+ * @memberof PIXI
+ */
+ var InteractionEvent = /** @class */ (function () {
+ function InteractionEvent() {
+ this.stopped = false;
+ this.stopsPropagatingAt = null;
+ this.stopPropagationHint = false;
+ this.target = null;
+ this.currentTarget = null;
+ this.type = null;
+ this.data = null;
+ }
+ /** Prevents event from reaching any objects other than the current object. */
+ InteractionEvent.prototype.stopPropagation = function () {
+ this.stopped = true;
+ this.stopPropagationHint = true;
+ this.stopsPropagatingAt = this.currentTarget;
+ };
+ /** Resets the event. */
+ InteractionEvent.prototype.reset = function () {
+ this.stopped = false;
+ this.stopsPropagatingAt = null;
+ this.stopPropagationHint = false;
+ this.currentTarget = null;
+ this.target = null;
+ };
+ return InteractionEvent;
+ }());
+
+ /**
+ * DisplayObjects with the {@link PIXI.interactiveTarget} mixin use this class to track interactions
+ * @class
+ * @private
+ * @memberof PIXI
+ */
+ var InteractionTrackingData = /** @class */ (function () {
+ /**
+ * @param {number} pointerId - Unique pointer id of the event
+ * @private
+ */
+ function InteractionTrackingData(pointerId) {
+ this._pointerId = pointerId;
+ this._flags = InteractionTrackingData.FLAGS.NONE;
+ }
+ /**
+ *
+ * @private
+ * @param {number} flag - The interaction flag to set
+ * @param {boolean} yn - Should the flag be set or unset
+ */
+ InteractionTrackingData.prototype._doSet = function (flag, yn) {
+ if (yn) {
+ this._flags = this._flags | flag;
+ }
+ else {
+ this._flags = this._flags & (~flag);
+ }
+ };
+ Object.defineProperty(InteractionTrackingData.prototype, "pointerId", {
+ /**
+ * Unique pointer id of the event
+ * @readonly
+ * @private
+ * @member {number}
+ */
+ get: function () {
+ return this._pointerId;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(InteractionTrackingData.prototype, "flags", {
+ /**
+ * State of the tracking data, expressed as bit flags
+ * @private
+ * @member {number}
+ */
+ get: function () {
+ return this._flags;
+ },
+ set: function (flags) {
+ this._flags = flags;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(InteractionTrackingData.prototype, "none", {
+ /**
+ * Is the tracked event inactive (not over or down)?
+ * @private
+ * @member {number}
+ */
+ get: function () {
+ return this._flags === InteractionTrackingData.FLAGS.NONE;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(InteractionTrackingData.prototype, "over", {
+ /**
+ * Is the tracked event over the DisplayObject?
+ * @private
+ * @member {boolean}
+ */
+ get: function () {
+ return (this._flags & InteractionTrackingData.FLAGS.OVER) !== 0;
+ },
+ set: function (yn) {
+ this._doSet(InteractionTrackingData.FLAGS.OVER, yn);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(InteractionTrackingData.prototype, "rightDown", {
+ /**
+ * Did the right mouse button come down in the DisplayObject?
+ * @private
+ * @member {boolean}
+ */
+ get: function () {
+ return (this._flags & InteractionTrackingData.FLAGS.RIGHT_DOWN) !== 0;
+ },
+ set: function (yn) {
+ this._doSet(InteractionTrackingData.FLAGS.RIGHT_DOWN, yn);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(InteractionTrackingData.prototype, "leftDown", {
+ /**
+ * Did the left mouse button come down in the DisplayObject?
+ * @private
+ * @member {boolean}
+ */
+ get: function () {
+ return (this._flags & InteractionTrackingData.FLAGS.LEFT_DOWN) !== 0;
+ },
+ set: function (yn) {
+ this._doSet(InteractionTrackingData.FLAGS.LEFT_DOWN, yn);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ InteractionTrackingData.FLAGS = Object.freeze({
+ NONE: 0,
+ OVER: 1 << 0,
+ LEFT_DOWN: 1 << 1,
+ RIGHT_DOWN: 1 << 2,
+ });
+ return InteractionTrackingData;
+ }());
+
+ /**
+ * Strategy how to search through stage tree for interactive objects
+ * @memberof PIXI
+ */
+ var TreeSearch = /** @class */ (function () {
+ function TreeSearch() {
+ this._tempPoint = new Point();
+ }
+ /**
+ * Recursive implementation for findHit
+ * @private
+ * @param interactionEvent - event containing the point that
+ * is tested for collision
+ * @param displayObject - the displayObject
+ * that will be hit test (recursively crawls its children)
+ * @param func - the function that will be called on each interactive object. The
+ * interactionEvent, displayObject and hit will be passed to the function
+ * @param hitTest - this indicates if the objects inside should be hit test against the point
+ * @param interactive - Whether the displayObject is interactive
+ * @returns - Returns true if the displayObject hit the point
+ */
+ TreeSearch.prototype.recursiveFindHit = function (interactionEvent, displayObject, func, hitTest, interactive) {
+ var _a;
+ if (!displayObject || !displayObject.visible) {
+ return false;
+ }
+ var point = interactionEvent.data.global;
+ // Took a little while to rework this function correctly! But now it is done and nice and optimized! ^_^
+ //
+ // This function will now loop through all objects and then only hit test the objects it HAS
+ // to, not all of them. MUCH faster..
+ // An object will be hit test if the following is true:
+ //
+ // 1: It is interactive.
+ // 2: It belongs to a parent that is interactive AND one of the parents children have not already been hit.
+ //
+ // As another little optimization once an interactive object has been hit we can carry on
+ // through the scenegraph, but we know that there will be no more hits! So we can avoid extra hit tests
+ // A final optimization is that an object is not hit test directly if a child has already been hit.
+ interactive = displayObject.interactive || interactive;
+ var hit = false;
+ var interactiveParent = interactive;
+ // Flag here can set to false if the event is outside the parents hitArea or mask
+ var hitTestChildren = true;
+ // If there is a hitArea, no need to test against anything else if the pointer is not within the hitArea
+ // There is also no longer a need to hitTest children.
+ if (displayObject.hitArea) {
+ if (hitTest) {
+ displayObject.worldTransform.applyInverse(point, this._tempPoint);
+ if (!displayObject.hitArea.contains(this._tempPoint.x, this._tempPoint.y)) {
+ hitTest = false;
+ hitTestChildren = false;
+ }
+ else {
+ hit = true;
+ }
+ }
+ interactiveParent = false;
+ }
+ // If there is a mask, no need to hitTest against anything else if the pointer is not within the mask.
+ // We still want to hitTestChildren, however, to ensure a mouseout can still be generated.
+ // https://github.com/pixijs/pixi.js/issues/5135
+ else if (displayObject._mask) {
+ if (hitTest) {
+ var maskObject = (displayObject._mask.isMaskData
+ ? displayObject._mask.maskObject : displayObject._mask);
+ if (maskObject && !((_a = maskObject.containsPoint) === null || _a === void 0 ? void 0 : _a.call(maskObject, point))) {
+ hitTest = false;
+ }
+ }
+ }
+ // ** FREE TIP **! If an object is not interactive or has no buttons in it
+ // (such as a game scene!) set interactiveChildren to false for that displayObject.
+ // This will allow PixiJS to completely ignore and bypass checking the displayObjects children.
+ if (hitTestChildren && displayObject.interactiveChildren && displayObject.children) {
+ var children = displayObject.children;
+ for (var i = children.length - 1; i >= 0; i--) {
+ var child = children[i];
+ // time to get recursive.. if this function will return if something is hit..
+ var childHit = this.recursiveFindHit(interactionEvent, child, func, hitTest, interactiveParent);
+ if (childHit) {
+ // its a good idea to check if a child has lost its parent.
+ // this means it has been removed whilst looping so its best
+ if (!child.parent) {
+ continue;
+ }
+ // we no longer need to hit test any more objects in this container as we we
+ // now know the parent has been hit
+ interactiveParent = false;
+ // If the child is interactive , that means that the object hit was actually
+ // interactive and not just the child of an interactive object.
+ // This means we no longer need to hit test anything else. We still need to run
+ // through all objects, but we don't need to perform any hit tests.
+ if (childHit) {
+ if (interactionEvent.target) {
+ hitTest = false;
+ }
+ hit = true;
+ }
+ }
+ }
+ }
+ // no point running this if the item is not interactive or does not have an interactive parent.
+ if (interactive) {
+ // if we are hit testing (as in we have no hit any objects yet)
+ // We also don't need to worry about hit testing if once of the displayObjects children
+ // has already been hit - but only if it was interactive, otherwise we need to keep
+ // looking for an interactive child, just in case we hit one
+ if (hitTest && !interactionEvent.target) {
+ // already tested against hitArea if it is defined
+ if (!displayObject.hitArea && displayObject.containsPoint) {
+ if (displayObject.containsPoint(point)) {
+ hit = true;
+ }
+ }
+ }
+ if (displayObject.interactive) {
+ if (hit && !interactionEvent.target) {
+ interactionEvent.target = displayObject;
+ }
+ if (func) {
+ func(interactionEvent, displayObject, !!hit);
+ }
+ }
+ }
+ return hit;
+ };
+ /**
+ * This function is provides a neat way of crawling through the scene graph and running a
+ * specified function on all interactive objects it finds. It will also take care of hit
+ * testing the interactive objects and passes the hit across in the function.
+ * @private
+ * @param interactionEvent - event containing the point that
+ * is tested for collision
+ * @param displayObject - the displayObject
+ * that will be hit test (recursively crawls its children)
+ * @param func - the function that will be called on each interactive object. The
+ * interactionEvent, displayObject and hit will be passed to the function
+ * @param hitTest - this indicates if the objects inside should be hit test against the point
+ * @returns - Returns true if the displayObject hit the point
+ */
+ TreeSearch.prototype.findHit = function (interactionEvent, displayObject, func, hitTest) {
+ this.recursiveFindHit(interactionEvent, displayObject, func, hitTest, false);
+ };
+ return TreeSearch;
+ }());
+
+ /**
+ * Interface for classes that represent a hit area.
+ *
+ * It is implemented by the following classes:
+ * - {@link PIXI.Circle}
+ * - {@link PIXI.Ellipse}
+ * - {@link PIXI.Polygon}
+ * - {@link PIXI.RoundedRectangle}
+ * @interface IHitArea
+ * @memberof PIXI
+ */
+ /**
+ * Checks whether the x and y coordinates given are contained within this area
+ * @method
+ * @name contains
+ * @memberof PIXI.IHitArea#
+ * @param {number} x - The X coordinate of the point to test
+ * @param {number} y - The Y coordinate of the point to test
+ * @returns {boolean} Whether the x/y coordinates are within this area
+ */
+ /**
+ * Default property values of interactive objects
+ * Used by {@link PIXI.InteractionManager} to automatically give all DisplayObjects these properties
+ * @private
+ * @name interactiveTarget
+ * @type {object}
+ * @memberof PIXI
+ * @example
+ * function MyObject() {}
+ *
+ * Object.assign(
+ * DisplayObject.prototype,
+ * PIXI.interactiveTarget
+ * );
+ */
+ var interactiveTarget = {
+ interactive: false,
+ interactiveChildren: true,
+ hitArea: null,
+ /**
+ * If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
+ * Setting this changes the 'cursor' property to `'pointer'`.
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
+ */
+ get buttonMode() {
+ return this.cursor === 'pointer';
+ },
+ set buttonMode(value) {
+ if (value) {
+ this.cursor = 'pointer';
+ }
+ else if (this.cursor === 'pointer') {
+ this.cursor = null;
+ }
+ },
+ /**
+ * This defines what cursor mode is used when the mouse cursor
+ * is hovered over the displayObject.
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
+ * @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
+ */
+ cursor: null,
+ /**
+ * Internal set of all active pointers, by identifier
+ * @member {Map<number, InteractionTrackingData>}
+ * @memberof PIXI.DisplayObject#
+ * @private
+ */
+ get trackedPointers() {
+ if (this._trackedPointers === undefined)
+ { this._trackedPointers = {}; }
+ return this._trackedPointers;
+ },
+ /**
+ * Map of all tracked pointers, by identifier. Use trackedPointers to access.
+ * @private
+ * @type {Map<number, InteractionTrackingData>}
+ */
+ _trackedPointers: undefined,
+ };
+
+ // Mix interactiveTarget into DisplayObject.prototype
+ DisplayObject.mixin(interactiveTarget);
+ var MOUSE_POINTER_ID = 1;
+ // helpers for hitTest() - only used inside hitTest()
+ var hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+ };
+ /**
+ * The interaction manager deals with mouse, touch and pointer events.
+ *
+ * Any DisplayObject can be interactive if its `interactive` property is set to true.
+ *
+ * This manager also supports multitouch.
+ *
+ * An instance of this class is automatically created by default, and can be found at `renderer.plugins.interaction`
+ * @memberof PIXI
+ */
+ var InteractionManager = /** @class */ (function (_super) {
+ __extends$h(InteractionManager, _super);
+ /**
+ * @param {PIXI.CanvasRenderer|PIXI.Renderer} renderer - A reference to the current renderer
+ * @param options - The options for the manager.
+ * @param {boolean} [options.autoPreventDefault=true] - Should the manager automatically prevent default browser actions.
+ * @param {number} [options.interactionFrequency=10] - Maximum frequency (ms) at pointer over/out states will be checked.
+ * @param {number} [options.useSystemTicker=true] - Whether to add {@link tickerUpdate} to {@link PIXI.Ticker.system}.
+ */
+ function InteractionManager(renderer, options) {
+ var _this = _super.call(this) || this;
+ options = options || {};
+ _this.renderer = renderer;
+ _this.autoPreventDefault = options.autoPreventDefault !== undefined ? options.autoPreventDefault : true;
+ _this.interactionFrequency = options.interactionFrequency || 10;
+ _this.mouse = new InteractionData();
+ _this.mouse.identifier = MOUSE_POINTER_ID;
+ // setting the mouse to start off far off screen will mean that mouse over does
+ // not get called before we even move the mouse.
+ _this.mouse.global.set(-999999);
+ _this.activeInteractionData = {};
+ _this.activeInteractionData[MOUSE_POINTER_ID] = _this.mouse;
+ _this.interactionDataPool = [];
+ _this.eventData = new InteractionEvent();
+ _this.interactionDOMElement = null;
+ _this.moveWhenInside = false;
+ _this.eventsAdded = false;
+ _this.tickerAdded = false;
+ _this.mouseOverRenderer = !('PointerEvent' in globalThis);
+ _this.supportsTouchEvents = 'ontouchstart' in globalThis;
+ _this.supportsPointerEvents = !!globalThis.PointerEvent;
+ // this will make it so that you don't have to call bind all the time
+ _this.onPointerUp = _this.onPointerUp.bind(_this);
+ _this.processPointerUp = _this.processPointerUp.bind(_this);
+ _this.onPointerCancel = _this.onPointerCancel.bind(_this);
+ _this.processPointerCancel = _this.processPointerCancel.bind(_this);
+ _this.onPointerDown = _this.onPointerDown.bind(_this);
+ _this.processPointerDown = _this.processPointerDown.bind(_this);
+ _this.onPointerMove = _this.onPointerMove.bind(_this);
+ _this.processPointerMove = _this.processPointerMove.bind(_this);
+ _this.onPointerOut = _this.onPointerOut.bind(_this);
+ _this.processPointerOverOut = _this.processPointerOverOut.bind(_this);
+ _this.onPointerOver = _this.onPointerOver.bind(_this);
+ _this.cursorStyles = {
+ default: 'inherit',
+ pointer: 'pointer',
+ };
+ _this.currentCursorMode = null;
+ _this.cursor = null;
+ _this.resolution = 1;
+ _this.delayedEvents = [];
+ _this.search = new TreeSearch();
+ _this._tempDisplayObject = new TemporaryDisplayObject();
+ _this._eventListenerOptions = { capture: true, passive: false };
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
+ * object.
+ * @event PIXI.InteractionManager#mousedown
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object.
+ * @event PIXI.InteractionManager#rightdown
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object.
+ * @event PIXI.InteractionManager#mouseup
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object.
+ * @event PIXI.InteractionManager#rightup
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object.
+ * @event PIXI.InteractionManager#click
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object.
+ * @event PIXI.InteractionManager#rightclick
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.InteractionManager#event:mousedown}.
+ * @event PIXI.InteractionManager#mouseupoutside
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.InteractionManager#event:rightdown}.
+ * @event PIXI.InteractionManager#rightupoutside
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object
+ * @event PIXI.InteractionManager#mousemove
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object
+ * @event PIXI.InteractionManager#mouseover
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object
+ * @event PIXI.InteractionManager#mouseout
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * @event PIXI.InteractionManager#pointerdown
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * Not always fired when some buttons are held down while others are released. In those cases,
+ * use [mousedown]{@link PIXI.InteractionManager#event:mousedown} and
+ * [mouseup]{@link PIXI.InteractionManager#event:mouseup} instead.
+ * @event PIXI.InteractionManager#pointerup
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when the operating system cancels a pointer event
+ * @event PIXI.InteractionManager#pointercancel
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * @event PIXI.InteractionManager#pointertap
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.InteractionManager#event:pointerdown}.
+ * @event PIXI.InteractionManager#pointerupoutside
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device is moved while over the display object
+ * @event PIXI.InteractionManager#pointermove
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device is moved onto the display object
+ * @event PIXI.InteractionManager#pointerover
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device is moved off the display object
+ * @event PIXI.InteractionManager#pointerout
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a touch point is placed on the display object.
+ * @event PIXI.InteractionManager#touchstart
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a touch point is removed from the display object.
+ * @event PIXI.InteractionManager#touchend
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when the operating system cancels a touch
+ * @event PIXI.InteractionManager#touchcancel
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * @event PIXI.InteractionManager#tap
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.InteractionManager#event:touchstart}.
+ * @event PIXI.InteractionManager#touchendoutside
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a touch point is moved along the display object.
+ * @event PIXI.InteractionManager#touchmove
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * This comes from the @pixi/interaction package.
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.InteractionEvent} event - Interaction event
+ */
+ _this._useSystemTicker = options.useSystemTicker !== undefined ? options.useSystemTicker : true;
+ _this.setTargetElement(_this.renderer.view, _this.renderer.resolution);
+ return _this;
+ }
+ Object.defineProperty(InteractionManager.prototype, "useSystemTicker", {
+ /**
+ * Should the InteractionManager automatically add {@link tickerUpdate} to {@link PIXI.Ticker.system}.
+ * @default true
+ */
+ get: function () {
+ return this._useSystemTicker;
+ },
+ set: function (useSystemTicker) {
+ this._useSystemTicker = useSystemTicker;
+ if (useSystemTicker) {
+ this.addTickerListener();
+ }
+ else {
+ this.removeTickerListener();
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(InteractionManager.prototype, "lastObjectRendered", {
+ /**
+ * Last rendered object or temp object.
+ * @readonly
+ * @protected
+ */
+ get: function () {
+ return this.renderer._lastObjectRendered || this._tempDisplayObject;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ * @param globalPoint - A point to hit test with, in global space.
+ * @param root - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @returns - The hit display object, if any.
+ */
+ InteractionManager.prototype.hitTest = function (globalPoint, root) {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root) {
+ root = this.lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+ return hitTestEvent.target;
+ };
+ /**
+ * Sets the DOM element which will receive mouse/touch events. This is useful for when you have
+ * other DOM elements on top of the renderers Canvas element. With this you'll be bale to delegate
+ * another DOM element to receive those events.
+ * @param element - the DOM element which will receive mouse and touch events.
+ * @param resolution - The resolution / device pixel ratio of the new element (relative to the canvas).
+ */
+ InteractionManager.prototype.setTargetElement = function (element, resolution) {
+ if (resolution === void 0) { resolution = 1; }
+ this.removeTickerListener();
+ this.removeEvents();
+ this.interactionDOMElement = element;
+ this.resolution = resolution;
+ this.addEvents();
+ this.addTickerListener();
+ };
+ /** Adds the ticker listener. */
+ InteractionManager.prototype.addTickerListener = function () {
+ if (this.tickerAdded || !this.interactionDOMElement || !this._useSystemTicker) {
+ return;
+ }
+ Ticker.system.add(this.tickerUpdate, this, exports.UPDATE_PRIORITY.INTERACTION);
+ this.tickerAdded = true;
+ };
+ /** Removes the ticker listener. */
+ InteractionManager.prototype.removeTickerListener = function () {
+ if (!this.tickerAdded) {
+ return;
+ }
+ Ticker.system.remove(this.tickerUpdate, this);
+ this.tickerAdded = false;
+ };
+ /** Registers all the DOM events. */
+ InteractionManager.prototype.addEvents = function () {
+ if (this.eventsAdded || !this.interactionDOMElement) {
+ return;
+ }
+ var style = this.interactionDOMElement.style;
+ if (globalThis.navigator.msPointerEnabled) {
+ style.msContentZooming = 'none';
+ style.msTouchAction = 'none';
+ }
+ else if (this.supportsPointerEvents) {
+ style.touchAction = 'none';
+ }
+ /*
+ * These events are added first, so that if pointer events are normalized, they are fired
+ * in the same order as non-normalized events. ie. pointer event 1st, mouse / touch 2nd
+ */
+ if (this.supportsPointerEvents) {
+ globalThis.document.addEventListener('pointermove', this.onPointerMove, this._eventListenerOptions);
+ this.interactionDOMElement.addEventListener('pointerdown', this.onPointerDown, this._eventListenerOptions);
+ // pointerout is fired in addition to pointerup (for touch events) and pointercancel
+ // we already handle those, so for the purposes of what we do in onPointerOut, we only
+ // care about the pointerleave event
+ this.interactionDOMElement.addEventListener('pointerleave', this.onPointerOut, this._eventListenerOptions);
+ this.interactionDOMElement.addEventListener('pointerover', this.onPointerOver, this._eventListenerOptions);
+ globalThis.addEventListener('pointercancel', this.onPointerCancel, this._eventListenerOptions);
+ globalThis.addEventListener('pointerup', this.onPointerUp, this._eventListenerOptions);
+ }
+ else {
+ globalThis.document.addEventListener('mousemove', this.onPointerMove, this._eventListenerOptions);
+ this.interactionDOMElement.addEventListener('mousedown', this.onPointerDown, this._eventListenerOptions);
+ this.interactionDOMElement.addEventListener('mouseout', this.onPointerOut, this._eventListenerOptions);
+ this.interactionDOMElement.addEventListener('mouseover', this.onPointerOver, this._eventListenerOptions);
+ globalThis.addEventListener('mouseup', this.onPointerUp, this._eventListenerOptions);
+ }
+ // always look directly for touch events so that we can provide original data
+ // In a future version we should change this to being just a fallback and rely solely on
+ // PointerEvents whenever available
+ if (this.supportsTouchEvents) {
+ this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, this._eventListenerOptions);
+ this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, this._eventListenerOptions);
+ this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, this._eventListenerOptions);
+ this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, this._eventListenerOptions);
+ }
+ this.eventsAdded = true;
+ };
+ /** Removes all the DOM events that were previously registered. */
+ InteractionManager.prototype.removeEvents = function () {
+ if (!this.eventsAdded || !this.interactionDOMElement) {
+ return;
+ }
+ var style = this.interactionDOMElement.style;
+ if (globalThis.navigator.msPointerEnabled) {
+ style.msContentZooming = '';
+ style.msTouchAction = '';
+ }
+ else if (this.supportsPointerEvents) {
+ style.touchAction = '';
+ }
+ if (this.supportsPointerEvents) {
+ globalThis.document.removeEventListener('pointermove', this.onPointerMove, this._eventListenerOptions);
+ this.interactionDOMElement.removeEventListener('pointerdown', this.onPointerDown, this._eventListenerOptions);
+ this.interactionDOMElement.removeEventListener('pointerleave', this.onPointerOut, this._eventListenerOptions);
+ this.interactionDOMElement.removeEventListener('pointerover', this.onPointerOver, this._eventListenerOptions);
+ globalThis.removeEventListener('pointercancel', this.onPointerCancel, this._eventListenerOptions);
+ globalThis.removeEventListener('pointerup', this.onPointerUp, this._eventListenerOptions);
+ }
+ else {
+ globalThis.document.removeEventListener('mousemove', this.onPointerMove, this._eventListenerOptions);
+ this.interactionDOMElement.removeEventListener('mousedown', this.onPointerDown, this._eventListenerOptions);
+ this.interactionDOMElement.removeEventListener('mouseout', this.onPointerOut, this._eventListenerOptions);
+ this.interactionDOMElement.removeEventListener('mouseover', this.onPointerOver, this._eventListenerOptions);
+ globalThis.removeEventListener('mouseup', this.onPointerUp, this._eventListenerOptions);
+ }
+ if (this.supportsTouchEvents) {
+ this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, this._eventListenerOptions);
+ this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, this._eventListenerOptions);
+ this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, this._eventListenerOptions);
+ this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, this._eventListenerOptions);
+ }
+ this.interactionDOMElement = null;
+ this.eventsAdded = false;
+ };
+ /**
+ * Updates the state of interactive objects if at least {@link interactionFrequency}
+ * milliseconds have passed since the last invocation.
+ *
+ * Invoked by a throttled ticker update from {@link PIXI.Ticker.system}.
+ * @param deltaTime - time delta since the last call
+ */
+ InteractionManager.prototype.tickerUpdate = function (deltaTime) {
+ this._deltaTime += deltaTime;
+ if (this._deltaTime < this.interactionFrequency) {
+ return;
+ }
+ this._deltaTime = 0;
+ this.update();
+ };
+ /** Updates the state of interactive objects. */
+ InteractionManager.prototype.update = function () {
+ if (!this.interactionDOMElement) {
+ return;
+ }
+ // if the user move the mouse this check has already been done using the mouse move!
+ if (this._didMove) {
+ this._didMove = false;
+ return;
+ }
+ this.cursor = null;
+ // Resets the flag as set by a stopPropagation call. This flag is usually reset by a user interaction of any kind,
+ // but there was a scenario of a display object moving under a static mouse cursor.
+ // In this case, mouseover and mouseevents would not pass the flag test in dispatchEvent function
+ for (var k in this.activeInteractionData) {
+ // eslint-disable-next-line no-prototype-builtins
+ if (this.activeInteractionData.hasOwnProperty(k)) {
+ var interactionData = this.activeInteractionData[k];
+ if (interactionData.originalEvent && interactionData.pointerType !== 'touch') {
+ var interactionEvent = this.configureInteractionEventForDOMEvent(this.eventData, interactionData.originalEvent, interactionData);
+ this.processInteractive(interactionEvent, this.lastObjectRendered, this.processPointerOverOut, true);
+ }
+ }
+ }
+ this.setCursorMode(this.cursor);
+ };
+ /**
+ * Sets the current cursor mode, handling any callbacks or CSS style changes.
+ * @param mode - cursor mode, a key from the cursorStyles dictionary
+ */
+ InteractionManager.prototype.setCursorMode = function (mode) {
+ mode = mode || 'default';
+ var applyStyles = true;
+ // offscreen canvas does not support setting styles, but cursor modes can be functions,
+ // in order to handle pixi rendered cursors, so we can't bail
+ if (globalThis.OffscreenCanvas && this.interactionDOMElement instanceof OffscreenCanvas) {
+ applyStyles = false;
+ }
+ // if the mode didn't actually change, bail early
+ if (this.currentCursorMode === mode) {
+ return;
+ }
+ this.currentCursorMode = mode;
+ var style = this.cursorStyles[mode];
+ // only do things if there is a cursor style for it
+ if (style) {
+ switch (typeof style) {
+ case 'string':
+ // string styles are handled as cursor CSS
+ if (applyStyles) {
+ this.interactionDOMElement.style.cursor = style;
+ }
+ break;
+ case 'function':
+ // functions are just called, and passed the cursor mode
+ style(mode);
+ break;
+ case 'object':
+ // if it is an object, assume that it is a dictionary of CSS styles,
+ // apply it to the interactionDOMElement
+ if (applyStyles) {
+ Object.assign(this.interactionDOMElement.style, style);
+ }
+ break;
+ }
+ }
+ else if (applyStyles && typeof mode === 'string' && !Object.prototype.hasOwnProperty.call(this.cursorStyles, mode)) {
+ // if it mode is a string (not a Symbol) and cursorStyles doesn't have any entry
+ // for the mode, then assume that the dev wants it to be CSS for the cursor.
+ this.interactionDOMElement.style.cursor = mode;
+ }
+ };
+ /**
+ * Dispatches an event on the display object that was interacted with.
+ * @param displayObject - the display object in question
+ * @param eventString - the name of the event (e.g, mousedown)
+ * @param eventData - the event data object
+ */
+ InteractionManager.prototype.dispatchEvent = function (displayObject, eventString, eventData) {
+ // Even if the event was stopped, at least dispatch any remaining events
+ // for the same display object.
+ if (!eventData.stopPropagationHint || displayObject === eventData.stopsPropagatingAt) {
+ eventData.currentTarget = displayObject;
+ eventData.type = eventString;
+ displayObject.emit(eventString, eventData);
+ if (displayObject[eventString]) {
+ displayObject[eventString](eventData);
+ }
+ }
+ };
+ /**
+ * Puts a event on a queue to be dispatched later. This is used to guarantee correct
+ * ordering of over/out events.
+ * @param displayObject - the display object in question
+ * @param eventString - the name of the event (e.g, mousedown)
+ * @param eventData - the event data object
+ */
+ InteractionManager.prototype.delayDispatchEvent = function (displayObject, eventString, eventData) {
+ this.delayedEvents.push({ displayObject: displayObject, eventString: eventString, eventData: eventData });
+ };
+ /**
+ * Maps x and y coords from a DOM object and maps them correctly to the PixiJS view. The
+ * resulting value is stored in the point. This takes into account the fact that the DOM
+ * element could be scaled and positioned anywhere on the screen.
+ * @param point - the point that the result will be stored in
+ * @param x - the x coord of the position to map
+ * @param y - the y coord of the position to map
+ */
+ InteractionManager.prototype.mapPositionToPoint = function (point, x, y) {
+ var rect;
+ // IE 11 fix
+ if (!this.interactionDOMElement.parentElement) {
+ rect = {
+ x: 0,
+ y: 0,
+ width: this.interactionDOMElement.width,
+ height: this.interactionDOMElement.height,
+ left: 0,
+ top: 0
+ };
+ }
+ else {
+ rect = this.interactionDOMElement.getBoundingClientRect();
+ }
+ var resolutionMultiplier = 1.0 / this.resolution;
+ point.x = ((x - rect.left) * (this.interactionDOMElement.width / rect.width)) * resolutionMultiplier;
+ point.y = ((y - rect.top) * (this.interactionDOMElement.height / rect.height)) * resolutionMultiplier;
+ };
+ /**
+ * This function is provides a neat way of crawling through the scene graph and running a
+ * specified function on all interactive objects it finds. It will also take care of hit
+ * testing the interactive objects and passes the hit across in the function.
+ * @protected
+ * @param interactionEvent - event containing the point that
+ * is tested for collision
+ * @param displayObject - the displayObject
+ * that will be hit test (recursively crawls its children)
+ * @param func - the function that will be called on each interactive object. The
+ * interactionEvent, displayObject and hit will be passed to the function
+ * @param hitTest - indicates whether we want to calculate hits
+ * or just iterate through all interactive objects
+ */
+ InteractionManager.prototype.processInteractive = function (interactionEvent, displayObject, func, hitTest) {
+ var hit = this.search.findHit(interactionEvent, displayObject, func, hitTest);
+ var delayedEvents = this.delayedEvents;
+ if (!delayedEvents.length) {
+ return hit;
+ }
+ // Reset the propagation hint, because we start deeper in the tree again.
+ interactionEvent.stopPropagationHint = false;
+ var delayedLen = delayedEvents.length;
+ this.delayedEvents = [];
+ for (var i = 0; i < delayedLen; i++) {
+ var _a = delayedEvents[i], displayObject_1 = _a.displayObject, eventString = _a.eventString, eventData = _a.eventData;
+ // When we reach the object we wanted to stop propagating at,
+ // set the propagation hint.
+ if (eventData.stopsPropagatingAt === displayObject_1) {
+ eventData.stopPropagationHint = true;
+ }
+ this.dispatchEvent(displayObject_1, eventString, eventData);
+ }
+ return hit;
+ };
+ /**
+ * Is called when the pointer button is pressed down on the renderer element
+ * @param originalEvent - The DOM event of a pointer button being pressed down
+ */
+ InteractionManager.prototype.onPointerDown = function (originalEvent) {
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch')
+ { return; }
+ var events = this.normalizeToPointerData(originalEvent);
+ /*
+ * No need to prevent default on natural pointer events, as there are no side effects
+ * Normalized events, however, may have the double mousedown/touchstart issue on the native android browser,
+ * so still need to be prevented.
+ */
+ // Guaranteed that there will be at least one event in events, and all events must have the same pointer type
+ if (this.autoPreventDefault && events[0].isNormalized) {
+ var cancelable = originalEvent.cancelable || !('cancelable' in originalEvent);
+ if (cancelable) {
+ originalEvent.preventDefault();
+ }
+ }
+ var eventLen = events.length;
+ for (var i = 0; i < eventLen; i++) {
+ var event = events[i];
+ var interactionData = this.getInteractionDataForPointerId(event);
+ var interactionEvent = this.configureInteractionEventForDOMEvent(this.eventData, event, interactionData);
+ interactionEvent.data.originalEvent = originalEvent;
+ this.processInteractive(interactionEvent, this.lastObjectRendered, this.processPointerDown, true);
+ this.emit('pointerdown', interactionEvent);
+ if (event.pointerType === 'touch') {
+ this.emit('touchstart', interactionEvent);
+ }
+ // emit a mouse event for "pen" pointers, the way a browser would emit a fallback event
+ else if (event.pointerType === 'mouse' || event.pointerType === 'pen') {
+ var isRightButton = event.button === 2;
+ this.emit(isRightButton ? 'rightdown' : 'mousedown', this.eventData);
+ }
+ }
+ };
+ /**
+ * Processes the result of the pointer down check and dispatches the event if need be
+ * @param interactionEvent - The interaction event wrapping the DOM event
+ * @param displayObject - The display object that was tested
+ * @param hit - the result of the hit test on the display object
+ */
+ InteractionManager.prototype.processPointerDown = function (interactionEvent, displayObject, hit) {
+ var data = interactionEvent.data;
+ var id = interactionEvent.data.identifier;
+ if (hit) {
+ if (!displayObject.trackedPointers[id]) {
+ displayObject.trackedPointers[id] = new InteractionTrackingData(id);
+ }
+ this.dispatchEvent(displayObject, 'pointerdown', interactionEvent);
+ if (data.pointerType === 'touch') {
+ this.dispatchEvent(displayObject, 'touchstart', interactionEvent);
+ }
+ else if (data.pointerType === 'mouse' || data.pointerType === 'pen') {
+ var isRightButton = data.button === 2;
+ if (isRightButton) {
+ displayObject.trackedPointers[id].rightDown = true;
+ }
+ else {
+ displayObject.trackedPointers[id].leftDown = true;
+ }
+ this.dispatchEvent(displayObject, isRightButton ? 'rightdown' : 'mousedown', interactionEvent);
+ }
+ }
+ };
+ /**
+ * Is called when the pointer button is released on the renderer element
+ * @param originalEvent - The DOM event of a pointer button being released
+ * @param cancelled - true if the pointer is cancelled
+ * @param func - Function passed to {@link processInteractive}
+ */
+ InteractionManager.prototype.onPointerComplete = function (originalEvent, cancelled, func) {
+ var events = this.normalizeToPointerData(originalEvent);
+ var eventLen = events.length;
+ // if the event wasn't targeting our canvas, then consider it to be pointerupoutside
+ // in all cases (unless it was a pointercancel)
+ var target = originalEvent.target;
+ // if in shadow DOM use composedPath to access target
+ if (originalEvent.composedPath && originalEvent.composedPath().length > 0) {
+ target = originalEvent.composedPath()[0];
+ }
+ var eventAppend = target !== this.interactionDOMElement ? 'outside' : '';
+ for (var i = 0; i < eventLen; i++) {
+ var event = events[i];
+ var interactionData = this.getInteractionDataForPointerId(event);
+ var interactionEvent = this.configureInteractionEventForDOMEvent(this.eventData, event, interactionData);
+ interactionEvent.data.originalEvent = originalEvent;
+ // perform hit testing for events targeting our canvas or cancel events
+ this.processInteractive(interactionEvent, this.lastObjectRendered, func, cancelled || !eventAppend);
+ this.emit(cancelled ? 'pointercancel' : "pointerup" + eventAppend, interactionEvent);
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen') {
+ var isRightButton = event.button === 2;
+ this.emit(isRightButton ? "rightup" + eventAppend : "mouseup" + eventAppend, interactionEvent);
+ }
+ else if (event.pointerType === 'touch') {
+ this.emit(cancelled ? 'touchcancel' : "touchend" + eventAppend, interactionEvent);
+ this.releaseInteractionDataForPointerId(event.pointerId);
+ }
+ }
+ };
+ /**
+ * Is called when the pointer button is cancelled
+ * @param event - The DOM event of a pointer button being released
+ */
+ InteractionManager.prototype.onPointerCancel = function (event) {
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch')
+ { return; }
+ this.onPointerComplete(event, true, this.processPointerCancel);
+ };
+ /**
+ * Processes the result of the pointer cancel check and dispatches the event if need be
+ * @param interactionEvent - The interaction event wrapping the DOM event
+ * @param displayObject - The display object that was tested
+ */
+ InteractionManager.prototype.processPointerCancel = function (interactionEvent, displayObject) {
+ var data = interactionEvent.data;
+ var id = interactionEvent.data.identifier;
+ if (displayObject.trackedPointers[id] !== undefined) {
+ delete displayObject.trackedPointers[id];
+ this.dispatchEvent(displayObject, 'pointercancel', interactionEvent);
+ if (data.pointerType === 'touch') {
+ this.dispatchEvent(displayObject, 'touchcancel', interactionEvent);
+ }
+ }
+ };
+ /**
+ * Is called when the pointer button is released on the renderer element
+ * @param event - The DOM event of a pointer button being released
+ */
+ InteractionManager.prototype.onPointerUp = function (event) {
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch')
+ { return; }
+ this.onPointerComplete(event, false, this.processPointerUp);
+ };
+ /**
+ * Processes the result of the pointer up check and dispatches the event if need be
+ * @param interactionEvent - The interaction event wrapping the DOM event
+ * @param displayObject - The display object that was tested
+ * @param hit - the result of the hit test on the display object
+ */
+ InteractionManager.prototype.processPointerUp = function (interactionEvent, displayObject, hit) {
+ var data = interactionEvent.data;
+ var id = interactionEvent.data.identifier;
+ var trackingData = displayObject.trackedPointers[id];
+ var isTouch = data.pointerType === 'touch';
+ var isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
+ // need to track mouse down status in the mouse block so that we can emit
+ // event in a later block
+ var isMouseTap = false;
+ // Mouse only
+ if (isMouse) {
+ var isRightButton = data.button === 2;
+ var flags = InteractionTrackingData.FLAGS;
+ var test = isRightButton ? flags.RIGHT_DOWN : flags.LEFT_DOWN;
+ var isDown = trackingData !== undefined && (trackingData.flags & test);
+ if (hit) {
+ this.dispatchEvent(displayObject, isRightButton ? 'rightup' : 'mouseup', interactionEvent);
+ if (isDown) {
+ this.dispatchEvent(displayObject, isRightButton ? 'rightclick' : 'click', interactionEvent);
+ // because we can confirm that the mousedown happened on this object, flag for later emit of pointertap
+ isMouseTap = true;
+ }
+ }
+ else if (isDown) {
+ this.dispatchEvent(displayObject, isRightButton ? 'rightupoutside' : 'mouseupoutside', interactionEvent);
+ }
+ // update the down state of the tracking data
+ if (trackingData) {
+ if (isRightButton) {
+ trackingData.rightDown = false;
+ }
+ else {
+ trackingData.leftDown = false;
+ }
+ }
+ }
+ // Pointers and Touches, and Mouse
+ if (hit) {
+ this.dispatchEvent(displayObject, 'pointerup', interactionEvent);
+ if (isTouch)
+ { this.dispatchEvent(displayObject, 'touchend', interactionEvent); }
+ if (trackingData) {
+ // emit pointertap if not a mouse, or if the mouse block decided it was a tap
+ if (!isMouse || isMouseTap) {
+ this.dispatchEvent(displayObject, 'pointertap', interactionEvent);
+ }
+ if (isTouch) {
+ this.dispatchEvent(displayObject, 'tap', interactionEvent);
+ // touches are no longer over (if they ever were) when we get the touchend
+ // so we should ensure that we don't keep pretending that they are
+ trackingData.over = false;
+ }
+ }
+ }
+ else if (trackingData) {
+ this.dispatchEvent(displayObject, 'pointerupoutside', interactionEvent);
+ if (isTouch)
+ { this.dispatchEvent(displayObject, 'touchendoutside', interactionEvent); }
+ }
+ // Only remove the tracking data if there is no over/down state still associated with it
+ if (trackingData && trackingData.none) {
+ delete displayObject.trackedPointers[id];
+ }
+ };
+ /**
+ * Is called when the pointer moves across the renderer element
+ * @param originalEvent - The DOM event of a pointer moving
+ */
+ InteractionManager.prototype.onPointerMove = function (originalEvent) {
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch')
+ { return; }
+ var events = this.normalizeToPointerData(originalEvent);
+ if (events[0].pointerType === 'mouse' || events[0].pointerType === 'pen') {
+ this._didMove = true;
+ this.cursor = null;
+ }
+ var eventLen = events.length;
+ for (var i = 0; i < eventLen; i++) {
+ var event = events[i];
+ var interactionData = this.getInteractionDataForPointerId(event);
+ var interactionEvent = this.configureInteractionEventForDOMEvent(this.eventData, event, interactionData);
+ interactionEvent.data.originalEvent = originalEvent;
+ this.processInteractive(interactionEvent, this.lastObjectRendered, this.processPointerMove, true);
+ this.emit('pointermove', interactionEvent);
+ if (event.pointerType === 'touch')
+ { this.emit('touchmove', interactionEvent); }
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
+ { this.emit('mousemove', interactionEvent); }
+ }
+ if (events[0].pointerType === 'mouse') {
+ this.setCursorMode(this.cursor);
+ // TODO BUG for parents interactive object (border order issue)
+ }
+ };
+ /**
+ * Processes the result of the pointer move check and dispatches the event if need be
+ * @param interactionEvent - The interaction event wrapping the DOM event
+ * @param displayObject - The display object that was tested
+ * @param hit - the result of the hit test on the display object
+ */
+ InteractionManager.prototype.processPointerMove = function (interactionEvent, displayObject, hit) {
+ var data = interactionEvent.data;
+ var isTouch = data.pointerType === 'touch';
+ var isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
+ if (isMouse) {
+ this.processPointerOverOut(interactionEvent, displayObject, hit);
+ }
+ if (!this.moveWhenInside || hit) {
+ this.dispatchEvent(displayObject, 'pointermove', interactionEvent);
+ if (isTouch)
+ { this.dispatchEvent(displayObject, 'touchmove', interactionEvent); }
+ if (isMouse)
+ { this.dispatchEvent(displayObject, 'mousemove', interactionEvent); }
+ }
+ };
+ /**
+ * Is called when the pointer is moved out of the renderer element
+ * @private
+ * @param {PointerEvent} originalEvent - The DOM event of a pointer being moved out
+ */
+ InteractionManager.prototype.onPointerOut = function (originalEvent) {
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch')
+ { return; }
+ var events = this.normalizeToPointerData(originalEvent);
+ // Only mouse and pointer can call onPointerOut, so events will always be length 1
+ var event = events[0];
+ if (event.pointerType === 'mouse') {
+ this.mouseOverRenderer = false;
+ this.setCursorMode(null);
+ }
+ var interactionData = this.getInteractionDataForPointerId(event);
+ var interactionEvent = this.configureInteractionEventForDOMEvent(this.eventData, event, interactionData);
+ interactionEvent.data.originalEvent = event;
+ this.processInteractive(interactionEvent, this.lastObjectRendered, this.processPointerOverOut, false);
+ this.emit('pointerout', interactionEvent);
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen') {
+ this.emit('mouseout', interactionEvent);
+ }
+ else {
+ // we can get touchleave events after touchend, so we want to make sure we don't
+ // introduce memory leaks
+ this.releaseInteractionDataForPointerId(interactionData.identifier);
+ }
+ };
+ /**
+ * Processes the result of the pointer over/out check and dispatches the event if need be.
+ * @param interactionEvent - The interaction event wrapping the DOM event
+ * @param displayObject - The display object that was tested
+ * @param hit - the result of the hit test on the display object
+ */
+ InteractionManager.prototype.processPointerOverOut = function (interactionEvent, displayObject, hit) {
+ var data = interactionEvent.data;
+ var id = interactionEvent.data.identifier;
+ var isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
+ var trackingData = displayObject.trackedPointers[id];
+ // if we just moused over the display object, then we need to track that state
+ if (hit && !trackingData) {
+ trackingData = displayObject.trackedPointers[id] = new InteractionTrackingData(id);
+ }
+ if (trackingData === undefined)
+ { return; }
+ if (hit && this.mouseOverRenderer) {
+ if (!trackingData.over) {
+ trackingData.over = true;
+ this.delayDispatchEvent(displayObject, 'pointerover', interactionEvent);
+ if (isMouse) {
+ this.delayDispatchEvent(displayObject, 'mouseover', interactionEvent);
+ }
+ }
+ // only change the cursor if it has not already been changed (by something deeper in the
+ // display tree)
+ if (isMouse && this.cursor === null) {
+ this.cursor = displayObject.cursor;
+ }
+ }
+ else if (trackingData.over) {
+ trackingData.over = false;
+ this.dispatchEvent(displayObject, 'pointerout', this.eventData);
+ if (isMouse) {
+ this.dispatchEvent(displayObject, 'mouseout', interactionEvent);
+ }
+ // if there is no mouse down information for the pointer, then it is safe to delete
+ if (trackingData.none) {
+ delete displayObject.trackedPointers[id];
+ }
+ }
+ };
+ /**
+ * Is called when the pointer is moved into the renderer element.
+ * @param originalEvent - The DOM event of a pointer button being moved into the renderer view.
+ */
+ InteractionManager.prototype.onPointerOver = function (originalEvent) {
+ var events = this.normalizeToPointerData(originalEvent);
+ // Only mouse and pointer can call onPointerOver, so events will always be length 1
+ var event = events[0];
+ var interactionData = this.getInteractionDataForPointerId(event);
+ var interactionEvent = this.configureInteractionEventForDOMEvent(this.eventData, event, interactionData);
+ interactionEvent.data.originalEvent = event;
+ if (event.pointerType === 'mouse') {
+ this.mouseOverRenderer = true;
+ }
+ this.emit('pointerover', interactionEvent);
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen') {
+ this.emit('mouseover', interactionEvent);
+ }
+ };
+ /**
+ * Get InteractionData for a given pointerId. Store that data as well.
+ * @param event - Normalized pointer event, output from normalizeToPointerData.
+ * @returns - Interaction data for the given pointer identifier.
+ */
+ InteractionManager.prototype.getInteractionDataForPointerId = function (event) {
+ var pointerId = event.pointerId;
+ var interactionData;
+ if (pointerId === MOUSE_POINTER_ID || event.pointerType === 'mouse') {
+ interactionData = this.mouse;
+ }
+ else if (this.activeInteractionData[pointerId]) {
+ interactionData = this.activeInteractionData[pointerId];
+ }
+ else {
+ interactionData = this.interactionDataPool.pop() || new InteractionData();
+ interactionData.identifier = pointerId;
+ this.activeInteractionData[pointerId] = interactionData;
+ }
+ // copy properties from the event, so that we can make sure that touch/pointer specific
+ // data is available
+ interactionData.copyEvent(event);
+ return interactionData;
+ };
+ /**
+ * Return unused InteractionData to the pool, for a given pointerId
+ * @param pointerId - Identifier from a pointer event
+ */
+ InteractionManager.prototype.releaseInteractionDataForPointerId = function (pointerId) {
+ var interactionData = this.activeInteractionData[pointerId];
+ if (interactionData) {
+ delete this.activeInteractionData[pointerId];
+ interactionData.reset();
+ this.interactionDataPool.push(interactionData);
+ }
+ };
+ /**
+ * Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
+ * @param interactionEvent - The event to be configured
+ * @param pointerEvent - The DOM event that will be paired with the InteractionEvent
+ * @param interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @returns - the interaction event that was passed in
+ */
+ InteractionManager.prototype.configureInteractionEventForDOMEvent = function (interactionEvent, pointerEvent, interactionData) {
+ interactionEvent.data = interactionData;
+ this.mapPositionToPoint(interactionData.global, pointerEvent.clientX, pointerEvent.clientY);
+ // Not really sure why this is happening, but it's how a previous version handled things
+ if (pointerEvent.pointerType === 'touch') {
+ pointerEvent.globalX = interactionData.global.x;
+ pointerEvent.globalY = interactionData.global.y;
+ }
+ interactionData.originalEvent = pointerEvent;
+ interactionEvent.reset();
+ return interactionEvent;
+ };
+ /**
+ * Ensures that the original event object contains all data that a regular pointer event would have
+ * @param {TouchEvent|MouseEvent|PointerEvent} event - The original event data from a touch or mouse event
+ * @returns - An array containing a single normalized pointer event, in the case of a pointer
+ * or mouse event, or a multiple normalized pointer events if there are multiple changed touches
+ */
+ InteractionManager.prototype.normalizeToPointerData = function (event) {
+ var normalizedEvents = [];
+ if (this.supportsTouchEvents && event instanceof TouchEvent) {
+ for (var i = 0, li = event.changedTouches.length; i < li; i++) {
+ var touch = event.changedTouches[i];
+ if (typeof touch.button === 'undefined')
+ { touch.button = event.touches.length ? 1 : 0; }
+ if (typeof touch.buttons === 'undefined')
+ { touch.buttons = event.touches.length ? 1 : 0; }
+ if (typeof touch.isPrimary === 'undefined') {
+ touch.isPrimary = event.touches.length === 1 && event.type === 'touchstart';
+ }
+ if (typeof touch.width === 'undefined')
+ { touch.width = touch.radiusX || 1; }
+ if (typeof touch.height === 'undefined')
+ { touch.height = touch.radiusY || 1; }
+ if (typeof touch.tiltX === 'undefined')
+ { touch.tiltX = 0; }
+ if (typeof touch.tiltY === 'undefined')
+ { touch.tiltY = 0; }
+ if (typeof touch.pointerType === 'undefined')
+ { touch.pointerType = 'touch'; }
+ if (typeof touch.pointerId === 'undefined')
+ { touch.pointerId = touch.identifier || 0; }
+ if (typeof touch.pressure === 'undefined')
+ { touch.pressure = touch.force || 0.5; }
+ if (typeof touch.twist === 'undefined')
+ { touch.twist = 0; }
+ if (typeof touch.tangentialPressure === 'undefined')
+ { touch.tangentialPressure = 0; }
+ // TODO: Remove these, as layerX/Y is not a standard, is deprecated, has uneven
+ // support, and the fill ins are not quite the same
+ // offsetX/Y might be okay, but is not the same as clientX/Y when the canvas's top
+ // left is not 0,0 on the page
+ if (typeof touch.layerX === 'undefined')
+ { touch.layerX = touch.offsetX = touch.clientX; }
+ if (typeof touch.layerY === 'undefined')
+ { touch.layerY = touch.offsetY = touch.clientY; }
+ // mark the touch as normalized, just so that we know we did it
+ touch.isNormalized = true;
+ normalizedEvents.push(touch);
+ }
+ }
+ // apparently PointerEvent subclasses MouseEvent, so yay
+ else if (!globalThis.MouseEvent
+ || (event instanceof MouseEvent && (!this.supportsPointerEvents || !(event instanceof globalThis.PointerEvent)))) {
+ var tempEvent = event;
+ if (typeof tempEvent.isPrimary === 'undefined')
+ { tempEvent.isPrimary = true; }
+ if (typeof tempEvent.width === 'undefined')
+ { tempEvent.width = 1; }
+ if (typeof tempEvent.height === 'undefined')
+ { tempEvent.height = 1; }
+ if (typeof tempEvent.tiltX === 'undefined')
+ { tempEvent.tiltX = 0; }
+ if (typeof tempEvent.tiltY === 'undefined')
+ { tempEvent.tiltY = 0; }
+ if (typeof tempEvent.pointerType === 'undefined')
+ { tempEvent.pointerType = 'mouse'; }
+ if (typeof tempEvent.pointerId === 'undefined')
+ { tempEvent.pointerId = MOUSE_POINTER_ID; }
+ if (typeof tempEvent.pressure === 'undefined')
+ { tempEvent.pressure = 0.5; }
+ if (typeof tempEvent.twist === 'undefined')
+ { tempEvent.twist = 0; }
+ if (typeof tempEvent.tangentialPressure === 'undefined')
+ { tempEvent.tangentialPressure = 0; }
+ // mark the mouse event as normalized, just so that we know we did it
+ tempEvent.isNormalized = true;
+ normalizedEvents.push(tempEvent);
+ }
+ else {
+ normalizedEvents.push(event);
+ }
+ return normalizedEvents;
+ };
+ /** Destroys the interaction manager. */
+ InteractionManager.prototype.destroy = function () {
+ this.removeEvents();
+ this.removeTickerListener();
+ this.removeAllListeners();
+ this.renderer = null;
+ this.mouse = null;
+ this.eventData = null;
+ this.interactionDOMElement = null;
+ this.onPointerDown = null;
+ this.processPointerDown = null;
+ this.onPointerUp = null;
+ this.processPointerUp = null;
+ this.onPointerCancel = null;
+ this.processPointerCancel = null;
+ this.onPointerMove = null;
+ this.processPointerMove = null;
+ this.onPointerOut = null;
+ this.processPointerOverOut = null;
+ this.onPointerOver = null;
+ this.search = null;
+ };
+ /** @ignore */
+ InteractionManager.extension = {
+ name: 'interaction',
+ type: [
+ exports.ExtensionType.RendererPlugin,
+ exports.ExtensionType.CanvasRendererPlugin ],
+ };
+ return InteractionManager;
+ }(eventemitter3));
+
+ /*!
+ * @pixi/extract - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/extract is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ var TEMP_RECT = new Rectangle();
+ var BYTES_PER_PIXEL = 4;
+ /**
+ * This class provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.Renderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @memberof PIXI
+ */
+ var Extract = /** @class */ (function () {
+ /**
+ * @param renderer - A reference to the current renderer
+ */
+ function Extract(renderer) {
+ this.renderer = renderer;
+ }
+ /**
+ * Will return a HTML Image of the target
+ * @param target - A displayObject or renderTexture
+ * to convert. If left empty will use the main renderer
+ * @param format - Image format, e.g. "image/jpeg" or "image/webp".
+ * @param quality - JPEG or Webp compression from 0 to 1. Default is 0.92.
+ * @returns - HTML Image of the target
+ */
+ Extract.prototype.image = function (target, format, quality) {
+ var image = new Image();
+ image.src = this.base64(target, format, quality);
+ return image;
+ };
+ /**
+ * Will return a base64 encoded string of this target. It works by calling
+ * `Extract.getCanvas` and then running toDataURL on that.
+ * @param target - A displayObject or renderTexture
+ * to convert. If left empty will use the main renderer
+ * @param format - Image format, e.g. "image/jpeg" or "image/webp".
+ * @param quality - JPEG or Webp compression from 0 to 1. Default is 0.92.
+ * @returns - A base64 encoded string of the texture.
+ */
+ Extract.prototype.base64 = function (target, format, quality) {
+ return this.canvas(target).toDataURL(format, quality);
+ };
+ /**
+ * Creates a Canvas element, renders this target to it and then returns it.
+ * @param target - A displayObject or renderTexture
+ * to convert. If left empty will use the main renderer
+ * @param frame - The frame the extraction is restricted to.
+ * @returns - A Canvas element with the texture rendered on.
+ */
+ Extract.prototype.canvas = function (target, frame) {
+ var renderer = this.renderer;
+ var resolution;
+ var flipY = false;
+ var renderTexture;
+ var generated = false;
+ if (target) {
+ if (target instanceof RenderTexture) {
+ renderTexture = target;
+ }
+ else {
+ renderTexture = this.renderer.generateTexture(target);
+ generated = true;
+ }
+ }
+ if (renderTexture) {
+ resolution = renderTexture.baseTexture.resolution;
+ frame = frame !== null && frame !== void 0 ? frame : renderTexture.frame;
+ flipY = false;
+ renderer.renderTexture.bind(renderTexture);
+ }
+ else {
+ resolution = renderer.resolution;
+ if (!frame) {
+ frame = TEMP_RECT;
+ frame.width = renderer.width;
+ frame.height = renderer.height;
+ }
+ flipY = true;
+ renderer.renderTexture.bind(null);
+ }
+ var width = Math.round(frame.width * resolution);
+ var height = Math.round(frame.height * resolution);
+ var canvasBuffer = new CanvasRenderTarget(width, height, 1);
+ var webglPixels = new Uint8Array(BYTES_PER_PIXEL * width * height);
+ // read pixels to the array
+ var gl = renderer.gl;
+ gl.readPixels(Math.round(frame.x * resolution), Math.round(frame.y * resolution), width, height, gl.RGBA, gl.UNSIGNED_BYTE, webglPixels);
+ // add the pixels to the canvas
+ var canvasData = canvasBuffer.context.getImageData(0, 0, width, height);
+ Extract.arrayPostDivide(webglPixels, canvasData.data);
+ canvasBuffer.context.putImageData(canvasData, 0, 0);
+ // pulling pixels
+ if (flipY) {
+ var target_1 = new CanvasRenderTarget(canvasBuffer.width, canvasBuffer.height, 1);
+ target_1.context.scale(1, -1);
+ // we can't render to itself because we should be empty before render.
+ target_1.context.drawImage(canvasBuffer.canvas, 0, -height);
+ canvasBuffer.destroy();
+ canvasBuffer = target_1;
+ }
+ if (generated) {
+ renderTexture.destroy(true);
+ }
+ // send the canvas back..
+ return canvasBuffer.canvas;
+ };
+ /**
+ * Will return a one-dimensional array containing the pixel data of the entire texture in RGBA
+ * order, with integer values between 0 and 255 (included).
+ * @param target - A displayObject or renderTexture
+ * to convert. If left empty will use the main renderer
+ * @param frame - The frame the extraction is restricted to.
+ * @returns - One-dimensional array containing the pixel data of the entire texture
+ */
+ Extract.prototype.pixels = function (target, frame) {
+ var renderer = this.renderer;
+ var resolution;
+ var renderTexture;
+ var generated = false;
+ if (target) {
+ if (target instanceof RenderTexture) {
+ renderTexture = target;
+ }
+ else {
+ renderTexture = this.renderer.generateTexture(target);
+ generated = true;
+ }
+ }
+ if (renderTexture) {
+ resolution = renderTexture.baseTexture.resolution;
+ frame = frame !== null && frame !== void 0 ? frame : renderTexture.frame;
+ renderer.renderTexture.bind(renderTexture);
+ }
+ else {
+ resolution = renderer.resolution;
+ if (!frame) {
+ frame = TEMP_RECT;
+ frame.width = renderer.width;
+ frame.height = renderer.height;
+ }
+ renderer.renderTexture.bind(null);
+ }
+ var width = Math.round(frame.width * resolution);
+ var height = Math.round(frame.height * resolution);
+ var webglPixels = new Uint8Array(BYTES_PER_PIXEL * width * height);
+ // read pixels to the array
+ var gl = renderer.gl;
+ gl.readPixels(Math.round(frame.x * resolution), Math.round(frame.y * resolution), width, height, gl.RGBA, gl.UNSIGNED_BYTE, webglPixels);
+ if (generated) {
+ renderTexture.destroy(true);
+ }
+ Extract.arrayPostDivide(webglPixels, webglPixels);
+ return webglPixels;
+ };
+ /** Destroys the extract. */
+ Extract.prototype.destroy = function () {
+ this.renderer = null;
+ };
+ /**
+ * Takes premultiplied pixel data and produces regular pixel data
+ * @private
+ * @param pixels - array of pixel data
+ * @param out - output array
+ */
+ Extract.arrayPostDivide = function (pixels, out) {
+ for (var i = 0; i < pixels.length; i += 4) {
+ var alpha = out[i + 3] = pixels[i + 3];
+ if (alpha !== 0) {
+ out[i] = Math.round(Math.min(pixels[i] * 255.0 / alpha, 255.0));
+ out[i + 1] = Math.round(Math.min(pixels[i + 1] * 255.0 / alpha, 255.0));
+ out[i + 2] = Math.round(Math.min(pixels[i + 2] * 255.0 / alpha, 255.0));
+ }
+ else {
+ out[i] = pixels[i];
+ out[i + 1] = pixels[i + 1];
+ out[i + 2] = pixels[i + 2];
+ }
+ }
+ };
+ /** @ignore */
+ Extract.extension = {
+ name: 'extract',
+ type: exports.ExtensionType.RendererPlugin,
+ };
+ return Extract;
+ }());
+
+ /*!
+ * @pixi/loaders - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/loaders is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /* jshint -W097 */
+ /**
+ * @memberof PIXI
+ */
+ var SignalBinding = /** @class */ (function () {
+ /**
+ * SignalBinding constructor.
+ * @constructs SignalBinding
+ * @param {Function} fn - Event handler to be called.
+ * @param {boolean} [once=false] - Should this listener be removed after dispatch
+ * @param {object} [thisArg] - The context of the callback function.
+ * @api private
+ */
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+ function SignalBinding(fn, once, thisArg) {
+ if (once === void 0) { once = false; }
+ this._fn = fn;
+ this._once = once;
+ this._thisArg = thisArg;
+ this._next = this._prev = this._owner = null;
+ }
+ SignalBinding.prototype.detach = function () {
+ if (this._owner === null)
+ { return false; }
+ this._owner.detach(this);
+ return true;
+ };
+ return SignalBinding;
+ }());
+ /**
+ * @param self
+ * @param node
+ * @private
+ */
+ function _addSignalBinding(self, node) {
+ if (!self._head) {
+ self._head = node;
+ self._tail = node;
+ }
+ else {
+ self._tail._next = node;
+ node._prev = self._tail;
+ self._tail = node;
+ }
+ node._owner = self;
+ return node;
+ }
+ /**
+ * @memberof PIXI
+ */
+ var Signal = /** @class */ (function () {
+ /**
+ * MiniSignal constructor.
+ * @example
+ * let mySignal = new Signal();
+ * let binding = mySignal.add(onSignal);
+ * mySignal.dispatch('foo', 'bar');
+ * mySignal.detach(binding);
+ */
+ function Signal() {
+ this._head = this._tail = undefined;
+ }
+ /**
+ * Return an array of attached SignalBinding.
+ * @param {boolean} [exists=false] - We only need to know if there are handlers.
+ * @returns {PIXI.SignalBinding[] | boolean} Array of attached SignalBinding or Boolean if called with exists = true
+ * @api public
+ */
+ Signal.prototype.handlers = function (exists) {
+ if (exists === void 0) { exists = false; }
+ var node = this._head;
+ if (exists)
+ { return !!node; }
+ var ee = [];
+ while (node) {
+ ee.push(node);
+ node = node._next;
+ }
+ return ee;
+ };
+ /**
+ * Return true if node is a SignalBinding attached to this MiniSignal
+ * @param {PIXI.SignalBinding} node - Node to check.
+ * @returns {boolean} True if node is attache to mini-signal
+ */
+ Signal.prototype.has = function (node) {
+ if (!(node instanceof SignalBinding)) {
+ throw new Error('MiniSignal#has(): First arg must be a SignalBinding object.');
+ }
+ return node._owner === this;
+ };
+ /**
+ * Dispaches a signal to all registered listeners.
+ * @param {...any} args
+ * @returns {boolean} Indication if we've emitted an event.
+ */
+ Signal.prototype.dispatch = function () {
+ var arguments$1 = arguments;
+
+ var args = [];
+ for (var _i = 0; _i < arguments.length; _i++) {
+ args[_i] = arguments$1[_i];
+ }
+ var node = this._head;
+ if (!node)
+ { return false; }
+ while (node) {
+ if (node._once)
+ { this.detach(node); }
+ node._fn.apply(node._thisArg, args);
+ node = node._next;
+ }
+ return true;
+ };
+ /**
+ * Register a new listener.
+ * @param {Function} fn - Callback function.
+ * @param {object} [thisArg] - The context of the callback function.
+ * @returns {PIXI.SignalBinding} The SignalBinding node that was added.
+ */
+ Signal.prototype.add = function (fn, thisArg) {
+ if (thisArg === void 0) { thisArg = null; }
+ if (typeof fn !== 'function') {
+ throw new Error('MiniSignal#add(): First arg must be a Function.');
+ }
+ return _addSignalBinding(this, new SignalBinding(fn, false, thisArg));
+ };
+ /**
+ * Register a new listener that will be executed only once.
+ * @param {Function} fn - Callback function.
+ * @param {object} [thisArg] - The context of the callback function.
+ * @returns {PIXI.SignalBinding} The SignalBinding node that was added.
+ */
+ Signal.prototype.once = function (fn, thisArg) {
+ if (thisArg === void 0) { thisArg = null; }
+ if (typeof fn !== 'function') {
+ throw new Error('MiniSignal#once(): First arg must be a Function.');
+ }
+ return _addSignalBinding(this, new SignalBinding(fn, true, thisArg));
+ };
+ /**
+ * Remove binding object.
+ * @param {PIXI.SignalBinding} node - The binding node that will be removed.
+ * @returns {Signal} The instance on which this method was called.
+ @api public */
+ Signal.prototype.detach = function (node) {
+ if (!(node instanceof SignalBinding)) {
+ throw new Error('MiniSignal#detach(): First arg must be a SignalBinding object.');
+ }
+ if (node._owner !== this)
+ { return this; } // todo: or error?
+ if (node._prev)
+ { node._prev._next = node._next; }
+ if (node._next)
+ { node._next._prev = node._prev; }
+ if (node === this._head) { // first node
+ this._head = node._next;
+ if (node._next === null) {
+ this._tail = null;
+ }
+ }
+ else if (node === this._tail) { // last node
+ this._tail = node._prev;
+ this._tail._next = null;
+ }
+ node._owner = null;
+ return this;
+ };
+ /**
+ * Detach all listeners.
+ * @returns {Signal} The instance on which this method was called.
+ */
+ Signal.prototype.detachAll = function () {
+ var node = this._head;
+ if (!node)
+ { return this; }
+ this._head = this._tail = null;
+ while (node) {
+ node._owner = null;
+ node = node._next;
+ }
+ return this;
+ };
+ return Signal;
+ }());
+
+ /**
+ * function from npm package `parseUri`, converted to TS to avoid leftpad incident
+ * @param {string} str
+ * @param [opts] - options
+ * @param {boolean} [opts.strictMode] - type of parser
+ */
+ function parseUri(str, opts) {
+ opts = opts || {};
+ var o = {
+ // eslint-disable-next-line max-len
+ key: ['source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor'],
+ q: {
+ name: 'queryKey',
+ parser: /(?:^|&)([^&=]*)=?([^&]*)/g
+ },
+ parser: {
+ // eslint-disable-next-line max-len
+ strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
+ // eslint-disable-next-line max-len
+ loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
+ }
+ };
+ var m = o.parser[opts.strictMode ? 'strict' : 'loose'].exec(str);
+ var uri = {};
+ var i = 14;
+ while (i--)
+ { uri[o.key[i]] = m[i] || ''; }
+ uri[o.q.name] = {};
+ uri[o.key[12]].replace(o.q.parser, function (_t0, t1, t2) {
+ if (t1)
+ { uri[o.q.name][t1] = t2; }
+ });
+ return uri;
+ }
+
+ // tests if CORS is supported in XHR, if not we need to use XDR
+ var useXdr;
+ var tempAnchor = null;
+ // some status constants
+ var STATUS_NONE = 0;
+ var STATUS_OK = 200;
+ var STATUS_EMPTY = 204;
+ var STATUS_IE_BUG_EMPTY = 1223;
+ var STATUS_TYPE_OK = 2;
+ // noop
+ function _noop$1() { }
+ /**
+ * Quick helper to set a value on one of the extension maps. Ensures there is no
+ * dot at the start of the extension.
+ * @ignore
+ * @param map - The map to set on.
+ * @param extname - The extension (or key) to set.
+ * @param val - The value to set.
+ */
+ function setExtMap(map, extname, val) {
+ if (extname && extname.indexOf('.') === 0) {
+ extname = extname.substring(1);
+ }
+ if (!extname) {
+ return;
+ }
+ map[extname] = val;
+ }
+ /**
+ * Quick helper to get string xhr type.
+ * @ignore
+ * @param xhr - The request to check.
+ * @returns The type.
+ */
+ function reqType(xhr) {
+ return xhr.toString().replace('object ', '');
+ }
+ /**
+ * Manages the state and loading of a resource and all child resources.
+ *
+ * Can be extended in `GlobalMixins.LoaderResource`.
+ * @memberof PIXI
+ */
+ exports.LoaderResource = /** @class */ (function () {
+ /**
+ * @param {string} name - The name of the resource to load.
+ * @param {string|string[]} url - The url for this resource, for audio/video loads you can pass
+ * an array of sources.
+ * @param {object} [options] - The options for the load.
+ * @param {string|boolean} [options.crossOrigin] - Is this request cross-origin? Default is to
+ * determine automatically.
+ * @param {number} [options.timeout=0] - A timeout in milliseconds for the load. If the load takes
+ * longer than this time it is cancelled and the load is considered a failure. If this value is
+ * set to `0` then there is no explicit timeout.
+ * @param {PIXI.LoaderResource.LOAD_TYPE} [options.loadType=LOAD_TYPE.XHR] - How should this resource
+ * be loaded?
+ * @param {PIXI.LoaderResource.XHR_RESPONSE_TYPE} [options.xhrType=XHR_RESPONSE_TYPE.DEFAULT] - How
+ * should the data being loaded be interpreted when using XHR?
+ * @param {PIXI.LoaderResource.IMetadata} [options.metadata] - Extra configuration for middleware
+ * and the Resource object.
+ */
+ function LoaderResource(name, url, options) {
+ /**
+ * The `dequeue` method that will be used a storage place for the async queue dequeue method
+ * used privately by the loader.
+ * @private
+ * @member {Function}
+ */
+ this._dequeue = _noop$1;
+ /**
+ * Used a storage place for the on load binding used privately by the loader.
+ * @private
+ * @member {Function}
+ */
+ this._onLoadBinding = null;
+ /**
+ * The timer for element loads to check if they timeout.
+ * @private
+ */
+ this._elementTimer = 0;
+ /**
+ * The `complete` function bound to this resource's context.
+ * @private
+ * @type {Function}
+ */
+ this._boundComplete = null;
+ /**
+ * The `_onError` function bound to this resource's context.
+ * @private
+ * @type {Function}
+ */
+ this._boundOnError = null;
+ /**
+ * The `_onProgress` function bound to this resource's context.
+ * @private
+ * @type {Function}
+ */
+ this._boundOnProgress = null;
+ /**
+ * The `_onTimeout` function bound to this resource's context.
+ * @private
+ * @type {Function}
+ */
+ this._boundOnTimeout = null;
+ this._boundXhrOnError = null;
+ this._boundXhrOnTimeout = null;
+ this._boundXhrOnAbort = null;
+ this._boundXhrOnLoad = null;
+ if (typeof name !== 'string' || typeof url !== 'string') {
+ throw new Error('Both name and url are required for constructing a resource.');
+ }
+ options = options || {};
+ this._flags = 0;
+ // set data url flag, needs to be set early for some _determineX checks to work.
+ this._setFlag(LoaderResource.STATUS_FLAGS.DATA_URL, url.indexOf('data:') === 0);
+ this.name = name;
+ this.url = url;
+ this.extension = this._getExtension();
+ this.data = null;
+ this.crossOrigin = options.crossOrigin === true ? 'anonymous' : options.crossOrigin;
+ this.timeout = options.timeout || 0;
+ this.loadType = options.loadType || this._determineLoadType();
+ // The type used to load the resource via XHR. If unset, determined automatically.
+ this.xhrType = options.xhrType;
+ // Extra info for middleware, and controlling specifics about how the resource loads.
+ // Note that if you pass in a `loadElement`, the Resource class takes ownership of it.
+ // Meaning it will modify it as it sees fit.
+ this.metadata = options.metadata || {};
+ // The error that occurred while loading (if any).
+ this.error = null;
+ // The XHR object that was used to load this resource. This is only set
+ // when `loadType` is `LoaderResource.LOAD_TYPE.XHR`.
+ this.xhr = null;
+ // The child resources this resource owns.
+ this.children = [];
+ // The resource type.
+ this.type = LoaderResource.TYPE.UNKNOWN;
+ // The progress chunk owned by this resource.
+ this.progressChunk = 0;
+ // The `dequeue` method that will be used a storage place for the async queue dequeue method
+ // used privately by the loader.
+ this._dequeue = _noop$1;
+ // Used a storage place for the on load binding used privately by the loader.
+ this._onLoadBinding = null;
+ // The timer for element loads to check if they timeout.
+ this._elementTimer = 0;
+ this._boundComplete = this.complete.bind(this);
+ this._boundOnError = this._onError.bind(this);
+ this._boundOnProgress = this._onProgress.bind(this);
+ this._boundOnTimeout = this._onTimeout.bind(this);
+ // xhr callbacks
+ this._boundXhrOnError = this._xhrOnError.bind(this);
+ this._boundXhrOnTimeout = this._xhrOnTimeout.bind(this);
+ this._boundXhrOnAbort = this._xhrOnAbort.bind(this);
+ this._boundXhrOnLoad = this._xhrOnLoad.bind(this);
+ // Dispatched when the resource beings to load.
+ this.onStart = new Signal();
+ // Dispatched each time progress of this resource load updates.
+ // Not all resources types and loader systems can support this event
+ // so sometimes it may not be available. If the resource
+ // is being loaded on a modern browser, using XHR, and the remote server
+ // properly sets Content-Length headers, then this will be available.
+ this.onProgress = new Signal();
+ // Dispatched once this resource has loaded, if there was an error it will
+ // be in the `error` property.
+ this.onComplete = new Signal();
+ // Dispatched after this resource has had all the *after* middleware run on it.
+ this.onAfterMiddleware = new Signal();
+ }
+ /**
+ * Sets the load type to be used for a specific extension.
+ * @static
+ * @param {string} extname - The extension to set the type for, e.g. "png" or "fnt"
+ * @param {PIXI.LoaderResource.LOAD_TYPE} loadType - The load type to set it to.
+ */
+ LoaderResource.setExtensionLoadType = function (extname, loadType) {
+ setExtMap(LoaderResource._loadTypeMap, extname, loadType);
+ };
+ /**
+ * Sets the load type to be used for a specific extension.
+ * @static
+ * @param {string} extname - The extension to set the type for, e.g. "png" or "fnt"
+ * @param {PIXI.LoaderResource.XHR_RESPONSE_TYPE} xhrType - The xhr type to set it to.
+ */
+ LoaderResource.setExtensionXhrType = function (extname, xhrType) {
+ setExtMap(LoaderResource._xhrTypeMap, extname, xhrType);
+ };
+ Object.defineProperty(LoaderResource.prototype, "isDataUrl", {
+ /**
+ * When the resource starts to load.
+ * @memberof PIXI.LoaderResource
+ * @callback OnStartSignal
+ * @param {PIXI.Resource} resource - The resource that the event happened on.
+ */
+ /**
+ * When the resource reports loading progress.
+ * @memberof PIXI.LoaderResource
+ * @callback OnProgressSignal
+ * @param {PIXI.Resource} resource - The resource that the event happened on.
+ * @param {number} percentage - The progress of the load in the range [0, 1].
+ */
+ /**
+ * When the resource finishes loading.
+ * @memberof PIXI.LoaderResource
+ * @callback OnCompleteSignal
+ * @param {PIXI.Resource} resource - The resource that the event happened on.
+ */
+ /**
+ * @memberof PIXI.LoaderResource
+ * @typedef {object} IMetadata
+ * @property {HTMLImageElement|HTMLAudioElement|HTMLVideoElement} [loadElement=null] - The
+ * element to use for loading, instead of creating one.
+ * @property {boolean} [skipSource=false] - Skips adding source(s) to the load element. This
+ * is useful if you want to pass in a `loadElement` that you already added load sources to.
+ * @property {string|string[]} [mimeType] - The mime type to use for the source element
+ * of a video/audio elment. If the urls are an array, you can pass this as an array as well
+ * where each index is the mime type to use for the corresponding url index.
+ */
+ /**
+ * Stores whether or not this url is a data url.
+ * @readonly
+ * @member {boolean}
+ */
+ get: function () {
+ return this._hasFlag(LoaderResource.STATUS_FLAGS.DATA_URL);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(LoaderResource.prototype, "isComplete", {
+ /**
+ * Describes if this resource has finished loading. Is true when the resource has completely
+ * loaded.
+ * @readonly
+ * @member {boolean}
+ */
+ get: function () {
+ return this._hasFlag(LoaderResource.STATUS_FLAGS.COMPLETE);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(LoaderResource.prototype, "isLoading", {
+ /**
+ * Describes if this resource is currently loading. Is true when the resource starts loading,
+ * and is false again when complete.
+ * @readonly
+ * @member {boolean}
+ */
+ get: function () {
+ return this._hasFlag(LoaderResource.STATUS_FLAGS.LOADING);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /** Marks the resource as complete. */
+ LoaderResource.prototype.complete = function () {
+ this._clearEvents();
+ this._finish();
+ };
+ /**
+ * Aborts the loading of this resource, with an optional message.
+ * @param {string} message - The message to use for the error
+ */
+ LoaderResource.prototype.abort = function (message) {
+ // abort can be called multiple times, ignore subsequent calls.
+ if (this.error) {
+ return;
+ }
+ // store error
+ this.error = new Error(message);
+ // clear events before calling aborts
+ this._clearEvents();
+ // abort the actual loading
+ if (this.xhr) {
+ this.xhr.abort();
+ }
+ else if (this.xdr) {
+ this.xdr.abort();
+ }
+ else if (this.data) {
+ // single source
+ if (this.data.src) {
+ this.data.src = LoaderResource.EMPTY_GIF;
+ }
+ // multi-source
+ else {
+ while (this.data.firstChild) {
+ this.data.removeChild(this.data.firstChild);
+ }
+ }
+ }
+ // done now.
+ this._finish();
+ };
+ /**
+ * Kicks off loading of this resource. This method is asynchronous.
+ * @param {PIXI.LoaderResource.OnCompleteSignal} [cb] - Optional callback to call once the resource is loaded.
+ */
+ LoaderResource.prototype.load = function (cb) {
+ var _this = this;
+ if (this.isLoading) {
+ return;
+ }
+ if (this.isComplete) {
+ if (cb) {
+ setTimeout(function () { return cb(_this); }, 1);
+ }
+ return;
+ }
+ else if (cb) {
+ this.onComplete.once(cb);
+ }
+ this._setFlag(LoaderResource.STATUS_FLAGS.LOADING, true);
+ this.onStart.dispatch(this);
+ // if unset, determine the value
+ if (this.crossOrigin === false || typeof this.crossOrigin !== 'string') {
+ this.crossOrigin = this._determineCrossOrigin(this.url);
+ }
+ switch (this.loadType) {
+ case LoaderResource.LOAD_TYPE.IMAGE:
+ this.type = LoaderResource.TYPE.IMAGE;
+ this._loadElement('image');
+ break;
+ case LoaderResource.LOAD_TYPE.AUDIO:
+ this.type = LoaderResource.TYPE.AUDIO;
+ this._loadSourceElement('audio');
+ break;
+ case LoaderResource.LOAD_TYPE.VIDEO:
+ this.type = LoaderResource.TYPE.VIDEO;
+ this._loadSourceElement('video');
+ break;
+ case LoaderResource.LOAD_TYPE.XHR:
+ /* falls through */
+ default:
+ if (typeof useXdr === 'undefined') {
+ useXdr = !!(globalThis.XDomainRequest && !('withCredentials' in (new XMLHttpRequest())));
+ }
+ if (useXdr && this.crossOrigin) {
+ this._loadXdr();
+ }
+ else {
+ this._loadXhr();
+ }
+ break;
+ }
+ };
+ /**
+ * Checks if the flag is set.
+ * @param flag - The flag to check.
+ * @returns True if the flag is set.
+ */
+ LoaderResource.prototype._hasFlag = function (flag) {
+ return (this._flags & flag) !== 0;
+ };
+ /**
+ * (Un)Sets the flag.
+ * @param flag - The flag to (un)set.
+ * @param value - Whether to set or (un)set the flag.
+ */
+ LoaderResource.prototype._setFlag = function (flag, value) {
+ this._flags = value ? (this._flags | flag) : (this._flags & ~flag);
+ };
+ /** Clears all the events from the underlying loading source. */
+ LoaderResource.prototype._clearEvents = function () {
+ clearTimeout(this._elementTimer);
+ if (this.data && this.data.removeEventListener) {
+ this.data.removeEventListener('error', this._boundOnError, false);
+ this.data.removeEventListener('load', this._boundComplete, false);
+ this.data.removeEventListener('progress', this._boundOnProgress, false);
+ this.data.removeEventListener('canplaythrough', this._boundComplete, false);
+ }
+ if (this.xhr) {
+ if (this.xhr.removeEventListener) {
+ this.xhr.removeEventListener('error', this._boundXhrOnError, false);
+ this.xhr.removeEventListener('timeout', this._boundXhrOnTimeout, false);
+ this.xhr.removeEventListener('abort', this._boundXhrOnAbort, false);
+ this.xhr.removeEventListener('progress', this._boundOnProgress, false);
+ this.xhr.removeEventListener('load', this._boundXhrOnLoad, false);
+ }
+ else {
+ this.xhr.onerror = null;
+ this.xhr.ontimeout = null;
+ this.xhr.onprogress = null;
+ this.xhr.onload = null;
+ }
+ }
+ };
+ /** Finalizes the load. */
+ LoaderResource.prototype._finish = function () {
+ if (this.isComplete) {
+ throw new Error('Complete called again for an already completed resource.');
+ }
+ this._setFlag(LoaderResource.STATUS_FLAGS.COMPLETE, true);
+ this._setFlag(LoaderResource.STATUS_FLAGS.LOADING, false);
+ this.onComplete.dispatch(this);
+ };
+ /**
+ * Loads this resources using an element that has a single source,
+ * like an HTMLImageElement.
+ * @private
+ * @param type - The type of element to use.
+ */
+ LoaderResource.prototype._loadElement = function (type) {
+ if (this.metadata.loadElement) {
+ this.data = this.metadata.loadElement;
+ }
+ else if (type === 'image' && typeof globalThis.Image !== 'undefined') {
+ this.data = new Image();
+ }
+ else {
+ this.data = document.createElement(type);
+ }
+ if (this.crossOrigin) {
+ this.data.crossOrigin = this.crossOrigin;
+ }
+ if (!this.metadata.skipSource) {
+ this.data.src = this.url;
+ }
+ this.data.addEventListener('error', this._boundOnError, false);
+ this.data.addEventListener('load', this._boundComplete, false);
+ this.data.addEventListener('progress', this._boundOnProgress, false);
+ if (this.timeout) {
+ this._elementTimer = setTimeout(this._boundOnTimeout, this.timeout);
+ }
+ };
+ /**
+ * Loads this resources using an element that has multiple sources,
+ * like an HTMLAudioElement or HTMLVideoElement.
+ * @param type - The type of element to use.
+ */
+ LoaderResource.prototype._loadSourceElement = function (type) {
+ if (this.metadata.loadElement) {
+ this.data = this.metadata.loadElement;
+ }
+ else if (type === 'audio' && typeof globalThis.Audio !== 'undefined') {
+ this.data = new Audio();
+ }
+ else {
+ this.data = document.createElement(type);
+ }
+ if (this.data === null) {
+ this.abort("Unsupported element: " + type);
+ return;
+ }
+ if (this.crossOrigin) {
+ this.data.crossOrigin = this.crossOrigin;
+ }
+ if (!this.metadata.skipSource) {
+ // support for CocoonJS Canvas+ runtime, lacks document.createElement('source')
+ if (navigator.isCocoonJS) {
+ this.data.src = Array.isArray(this.url) ? this.url[0] : this.url;
+ }
+ else if (Array.isArray(this.url)) {
+ var mimeTypes = this.metadata.mimeType;
+ for (var i = 0; i < this.url.length; ++i) {
+ this.data.appendChild(this._createSource(type, this.url[i], Array.isArray(mimeTypes) ? mimeTypes[i] : mimeTypes));
+ }
+ }
+ else {
+ var mimeTypes = this.metadata.mimeType;
+ this.data.appendChild(this._createSource(type, this.url, Array.isArray(mimeTypes) ? mimeTypes[0] : mimeTypes));
+ }
+ }
+ this.data.addEventListener('error', this._boundOnError, false);
+ this.data.addEventListener('load', this._boundComplete, false);
+ this.data.addEventListener('progress', this._boundOnProgress, false);
+ this.data.addEventListener('canplaythrough', this._boundComplete, false);
+ this.data.load();
+ if (this.timeout) {
+ this._elementTimer = setTimeout(this._boundOnTimeout, this.timeout);
+ }
+ };
+ /** Loads this resources using an XMLHttpRequest. */
+ LoaderResource.prototype._loadXhr = function () {
+ // if unset, determine the value
+ if (typeof this.xhrType !== 'string') {
+ this.xhrType = this._determineXhrType();
+ }
+ var xhr = this.xhr = new XMLHttpRequest();
+ // send credentials when crossOrigin with credentials requested
+ if (this.crossOrigin === 'use-credentials') {
+ xhr.withCredentials = true;
+ }
+ // set the request type and url
+ xhr.open('GET', this.url, true);
+ xhr.timeout = this.timeout;
+ // load json as text and parse it ourselves. We do this because some browsers
+ // *cough* safari *cough* can't deal with it.
+ if (this.xhrType === LoaderResource.XHR_RESPONSE_TYPE.JSON
+ || this.xhrType === LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT) {
+ xhr.responseType = LoaderResource.XHR_RESPONSE_TYPE.TEXT;
+ }
+ else {
+ xhr.responseType = this.xhrType;
+ }
+ xhr.addEventListener('error', this._boundXhrOnError, false);
+ xhr.addEventListener('timeout', this._boundXhrOnTimeout, false);
+ xhr.addEventListener('abort', this._boundXhrOnAbort, false);
+ xhr.addEventListener('progress', this._boundOnProgress, false);
+ xhr.addEventListener('load', this._boundXhrOnLoad, false);
+ xhr.send();
+ };
+ /** Loads this resources using an XDomainRequest. This is here because we need to support IE9 (gross). */
+ LoaderResource.prototype._loadXdr = function () {
+ // if unset, determine the value
+ if (typeof this.xhrType !== 'string') {
+ this.xhrType = this._determineXhrType();
+ }
+ var xdr = this.xhr = new globalThis.XDomainRequest(); // eslint-disable-line no-undef
+ // XDomainRequest has a few quirks. Occasionally it will abort requests
+ // A way to avoid this is to make sure ALL callbacks are set even if not used
+ // More info here: http://stackoverflow.com/questions/15786966/xdomainrequest-aborts-post-on-ie-9
+ xdr.timeout = this.timeout || 5000; // XDR needs a timeout value or it breaks in IE9
+ xdr.onerror = this._boundXhrOnError;
+ xdr.ontimeout = this._boundXhrOnTimeout;
+ xdr.onprogress = this._boundOnProgress;
+ xdr.onload = this._boundXhrOnLoad;
+ xdr.open('GET', this.url, true);
+ // Note: The xdr.send() call is wrapped in a timeout to prevent an
+ // issue with the interface where some requests are lost if multiple
+ // XDomainRequests are being sent at the same time.
+ // Some info here: https://github.com/photonstorm/phaser/issues/1248
+ setTimeout(function () { return xdr.send(); }, 1);
+ };
+ /**
+ * Creates a source used in loading via an element.
+ * @param type - The element type (video or audio).
+ * @param url - The source URL to load from.
+ * @param [mime] - The mime type of the video
+ * @returns The source element.
+ */
+ LoaderResource.prototype._createSource = function (type, url, mime) {
+ if (!mime) {
+ mime = type + "/" + this._getExtension(url);
+ }
+ var source = document.createElement('source');
+ source.src = url;
+ source.type = mime;
+ return source;
+ };
+ /**
+ * Called if a load errors out.
+ * @param event - The error event from the element that emits it.
+ */
+ LoaderResource.prototype._onError = function (event) {
+ this.abort("Failed to load element using: " + event.target.nodeName);
+ };
+ /**
+ * Called if a load progress event fires for an element or xhr/xdr.
+ * @param event - Progress event.
+ */
+ LoaderResource.prototype._onProgress = function (event) {
+ if (event && event.lengthComputable) {
+ this.onProgress.dispatch(this, event.loaded / event.total);
+ }
+ };
+ /** Called if a timeout event fires for an element. */
+ LoaderResource.prototype._onTimeout = function () {
+ this.abort("Load timed out.");
+ };
+ /** Called if an error event fires for xhr/xdr. */
+ LoaderResource.prototype._xhrOnError = function () {
+ var xhr = this.xhr;
+ this.abort(reqType(xhr) + " Request failed. Status: " + xhr.status + ", text: \"" + xhr.statusText + "\"");
+ };
+ /** Called if an error event fires for xhr/xdr. */
+ LoaderResource.prototype._xhrOnTimeout = function () {
+ var xhr = this.xhr;
+ this.abort(reqType(xhr) + " Request timed out.");
+ };
+ /** Called if an abort event fires for xhr/xdr. */
+ LoaderResource.prototype._xhrOnAbort = function () {
+ var xhr = this.xhr;
+ this.abort(reqType(xhr) + " Request was aborted by the user.");
+ };
+ /** Called when data successfully loads from an xhr/xdr request. */
+ LoaderResource.prototype._xhrOnLoad = function () {
+ var xhr = this.xhr;
+ var text = '';
+ var status = typeof xhr.status === 'undefined' ? STATUS_OK : xhr.status; // XDR has no `.status`, assume 200.
+ // responseText is accessible only if responseType is '' or 'text' and on older browsers
+ if (xhr.responseType === '' || xhr.responseType === 'text' || typeof xhr.responseType === 'undefined') {
+ text = xhr.responseText;
+ }
+ // status can be 0 when using the `file://` protocol so we also check if a response is set.
+ // If it has a response, we assume 200; otherwise a 0 status code with no contents is an aborted request.
+ if (status === STATUS_NONE && (text.length > 0 || xhr.responseType === LoaderResource.XHR_RESPONSE_TYPE.BUFFER)) {
+ status = STATUS_OK;
+ }
+ // handle IE9 bug: http://stackoverflow.com/questions/10046972/msie-returns-status-code-of-1223-for-ajax-request
+ else if (status === STATUS_IE_BUG_EMPTY) {
+ status = STATUS_EMPTY;
+ }
+ var statusType = (status / 100) | 0;
+ if (statusType === STATUS_TYPE_OK) {
+ // if text, just return it
+ if (this.xhrType === LoaderResource.XHR_RESPONSE_TYPE.TEXT) {
+ this.data = text;
+ this.type = LoaderResource.TYPE.TEXT;
+ }
+ // if json, parse into json object
+ else if (this.xhrType === LoaderResource.XHR_RESPONSE_TYPE.JSON) {
+ try {
+ this.data = JSON.parse(text);
+ this.type = LoaderResource.TYPE.JSON;
+ }
+ catch (e) {
+ this.abort("Error trying to parse loaded json: " + e);
+ return;
+ }
+ }
+ // if xml, parse into an xml document or div element
+ else if (this.xhrType === LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT) {
+ try {
+ if (globalThis.DOMParser) {
+ var domparser = new DOMParser();
+ this.data = domparser.parseFromString(text, 'text/xml');
+ }
+ else {
+ var div = document.createElement('div');
+ div.innerHTML = text;
+ this.data = div;
+ }
+ this.type = LoaderResource.TYPE.XML;
+ }
+ catch (e$1) {
+ this.abort("Error trying to parse loaded xml: " + e$1);
+ return;
+ }
+ }
+ // other types just return the response
+ else {
+ this.data = xhr.response || text;
+ }
+ }
+ else {
+ this.abort("[" + xhr.status + "] " + xhr.statusText + ": " + xhr.responseURL);
+ return;
+ }
+ this.complete();
+ };
+ /**
+ * Sets the `crossOrigin` property for this resource based on if the url
+ * for this resource is cross-origin. If crossOrigin was manually set, this
+ * function does nothing.
+ * @private
+ * @param url - The url to test.
+ * @param [loc=globalThis.location] - The location object to test against.
+ * @returns The crossOrigin value to use (or empty string for none).
+ */
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+ LoaderResource.prototype._determineCrossOrigin = function (url, loc) {
+ // data: and javascript: urls are considered same-origin
+ if (url.indexOf('data:') === 0) {
+ return '';
+ }
+ // A sandboxed iframe without the 'allow-same-origin' attribute will have a special
+ // origin designed not to match globalThis.location.origin, and will always require
+ // crossOrigin requests regardless of whether the location matches.
+ if (globalThis.origin !== globalThis.location.origin) {
+ return 'anonymous';
+ }
+ // default is globalThis.location
+ loc = loc || globalThis.location;
+ if (!tempAnchor) {
+ tempAnchor = document.createElement('a');
+ }
+ // let the browser determine the full href for the url of this resource and then
+ // parse with the node url lib, we can't use the properties of the anchor element
+ // because they don't work in IE9 :(
+ tempAnchor.href = url;
+ var parsedUrl = parseUri(tempAnchor.href, { strictMode: true });
+ var samePort = (!parsedUrl.port && loc.port === '') || (parsedUrl.port === loc.port);
+ var protocol = parsedUrl.protocol ? parsedUrl.protocol + ":" : '';
+ // if cross origin
+ if (parsedUrl.host !== loc.hostname || !samePort || protocol !== loc.protocol) {
+ return 'anonymous';
+ }
+ return '';
+ };
+ /**
+ * Determines the responseType of an XHR request based on the extension of the
+ * resource being loaded.
+ * @private
+ * @returns {PIXI.LoaderResource.XHR_RESPONSE_TYPE} The responseType to use.
+ */
+ LoaderResource.prototype._determineXhrType = function () {
+ return LoaderResource._xhrTypeMap[this.extension] || LoaderResource.XHR_RESPONSE_TYPE.TEXT;
+ };
+ /**
+ * Determines the loadType of a resource based on the extension of the
+ * resource being loaded.
+ * @private
+ * @returns {PIXI.LoaderResource.LOAD_TYPE} The loadType to use.
+ */
+ LoaderResource.prototype._determineLoadType = function () {
+ return LoaderResource._loadTypeMap[this.extension] || LoaderResource.LOAD_TYPE.XHR;
+ };
+ /**
+ * Extracts the extension (sans '.') of the file being loaded by the resource.
+ * @param [url] - url to parse, `this.url` by default.
+ * @returns The extension.
+ */
+ LoaderResource.prototype._getExtension = function (url) {
+ if (url === void 0) { url = this.url; }
+ var ext = '';
+ if (this.isDataUrl) {
+ var slashIndex = url.indexOf('/');
+ ext = url.substring(slashIndex + 1, url.indexOf(';', slashIndex));
+ }
+ else {
+ var queryStart = url.indexOf('?');
+ var hashStart = url.indexOf('#');
+ var index = Math.min(queryStart > -1 ? queryStart : url.length, hashStart > -1 ? hashStart : url.length);
+ url = url.substring(0, index);
+ ext = url.substring(url.lastIndexOf('.') + 1);
+ }
+ return ext.toLowerCase();
+ };
+ /**
+ * Determines the mime type of an XHR request based on the responseType of
+ * resource being loaded.
+ * @param type - The type to get a mime type for.
+ * @private
+ * @returns The mime type to use.
+ */
+ LoaderResource.prototype._getMimeFromXhrType = function (type) {
+ switch (type) {
+ case LoaderResource.XHR_RESPONSE_TYPE.BUFFER:
+ return 'application/octet-binary';
+ case LoaderResource.XHR_RESPONSE_TYPE.BLOB:
+ return 'application/blob';
+ case LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT:
+ return 'application/xml';
+ case LoaderResource.XHR_RESPONSE_TYPE.JSON:
+ return 'application/json';
+ case LoaderResource.XHR_RESPONSE_TYPE.DEFAULT:
+ case LoaderResource.XHR_RESPONSE_TYPE.TEXT:
+ /* falls through */
+ default:
+ return 'text/plain';
+ }
+ };
+ return LoaderResource;
+ }());
+ // eslint-disable-next-line @typescript-eslint/no-namespace
+ (function (LoaderResource) {
+ (function (STATUS_FLAGS) {
+ /** None */
+ STATUS_FLAGS[STATUS_FLAGS["NONE"] = 0] = "NONE";
+ /** Data URL */
+ STATUS_FLAGS[STATUS_FLAGS["DATA_URL"] = 1] = "DATA_URL";
+ /** Complete */
+ STATUS_FLAGS[STATUS_FLAGS["COMPLETE"] = 2] = "COMPLETE";
+ /** Loading */
+ STATUS_FLAGS[STATUS_FLAGS["LOADING"] = 4] = "LOADING";
+ })(LoaderResource.STATUS_FLAGS || (LoaderResource.STATUS_FLAGS = {}));
+ (function (TYPE) {
+ /** Unknown */
+ TYPE[TYPE["UNKNOWN"] = 0] = "UNKNOWN";
+ /** JSON */
+ TYPE[TYPE["JSON"] = 1] = "JSON";
+ /** XML */
+ TYPE[TYPE["XML"] = 2] = "XML";
+ /** Image */
+ TYPE[TYPE["IMAGE"] = 3] = "IMAGE";
+ /** Audio */
+ TYPE[TYPE["AUDIO"] = 4] = "AUDIO";
+ /** Video */
+ TYPE[TYPE["VIDEO"] = 5] = "VIDEO";
+ /** Plain text */
+ TYPE[TYPE["TEXT"] = 6] = "TEXT";
+ })(LoaderResource.TYPE || (LoaderResource.TYPE = {}));
+ (function (LOAD_TYPE) {
+ /** Uses XMLHttpRequest to load the resource. */
+ LOAD_TYPE[LOAD_TYPE["XHR"] = 1] = "XHR";
+ /** Uses an `Image` object to load the resource. */
+ LOAD_TYPE[LOAD_TYPE["IMAGE"] = 2] = "IMAGE";
+ /** Uses an `Audio` object to load the resource. */
+ LOAD_TYPE[LOAD_TYPE["AUDIO"] = 3] = "AUDIO";
+ /** Uses a `Video` object to load the resource. */
+ LOAD_TYPE[LOAD_TYPE["VIDEO"] = 4] = "VIDEO";
+ })(LoaderResource.LOAD_TYPE || (LoaderResource.LOAD_TYPE = {}));
+ (function (XHR_RESPONSE_TYPE) {
+ /** string */
+ XHR_RESPONSE_TYPE["DEFAULT"] = "text";
+ /** ArrayBuffer */
+ XHR_RESPONSE_TYPE["BUFFER"] = "arraybuffer";
+ /** Blob */
+ XHR_RESPONSE_TYPE["BLOB"] = "blob";
+ /** Document */
+ XHR_RESPONSE_TYPE["DOCUMENT"] = "document";
+ /** Object */
+ XHR_RESPONSE_TYPE["JSON"] = "json";
+ /** String */
+ XHR_RESPONSE_TYPE["TEXT"] = "text";
+ })(LoaderResource.XHR_RESPONSE_TYPE || (LoaderResource.XHR_RESPONSE_TYPE = {}));
+ LoaderResource._loadTypeMap = {
+ // images
+ gif: LoaderResource.LOAD_TYPE.IMAGE,
+ png: LoaderResource.LOAD_TYPE.IMAGE,
+ bmp: LoaderResource.LOAD_TYPE.IMAGE,
+ jpg: LoaderResource.LOAD_TYPE.IMAGE,
+ jpeg: LoaderResource.LOAD_TYPE.IMAGE,
+ tif: LoaderResource.LOAD_TYPE.IMAGE,
+ tiff: LoaderResource.LOAD_TYPE.IMAGE,
+ webp: LoaderResource.LOAD_TYPE.IMAGE,
+ tga: LoaderResource.LOAD_TYPE.IMAGE,
+ svg: LoaderResource.LOAD_TYPE.IMAGE,
+ 'svg+xml': LoaderResource.LOAD_TYPE.IMAGE,
+ // audio
+ mp3: LoaderResource.LOAD_TYPE.AUDIO,
+ ogg: LoaderResource.LOAD_TYPE.AUDIO,
+ wav: LoaderResource.LOAD_TYPE.AUDIO,
+ // videos
+ mp4: LoaderResource.LOAD_TYPE.VIDEO,
+ webm: LoaderResource.LOAD_TYPE.VIDEO,
+ };
+ LoaderResource._xhrTypeMap = {
+ // xml
+ xhtml: LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT,
+ html: LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT,
+ htm: LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT,
+ xml: LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT,
+ tmx: LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT,
+ svg: LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT,
+ // This was added to handle Tiled Tileset XML, but .tsx is also a TypeScript React Component.
+ // Since it is way less likely for people to be loading TypeScript files instead of Tiled files,
+ // this should probably be fine.
+ tsx: LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT,
+ // images
+ gif: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
+ png: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
+ bmp: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
+ jpg: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
+ jpeg: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
+ tif: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
+ tiff: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
+ webp: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
+ tga: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
+ // json
+ json: LoaderResource.XHR_RESPONSE_TYPE.JSON,
+ // text
+ text: LoaderResource.XHR_RESPONSE_TYPE.TEXT,
+ txt: LoaderResource.XHR_RESPONSE_TYPE.TEXT,
+ // fonts
+ ttf: LoaderResource.XHR_RESPONSE_TYPE.BUFFER,
+ otf: LoaderResource.XHR_RESPONSE_TYPE.BUFFER,
+ };
+ // We can't set the `src` attribute to empty string, so on abort we set it to this 1px transparent gif
+ LoaderResource.EMPTY_GIF = 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==';
+ })(exports.LoaderResource || (exports.LoaderResource = {}));
+
+ /**
+ * Smaller version of the async library constructs.
+ * @ignore
+ */
+ function _noop() {
+ }
+ /**
+ * Ensures a function is only called once.
+ * @ignore
+ * @param {Function} fn - The function to wrap.
+ * @returns {Function} The wrapping function.
+ */
+ function onlyOnce(fn) {
+ return function onceWrapper() {
+ var arguments$1 = arguments;
+
+ var args = [];
+ for (var _i = 0; _i < arguments.length; _i++) {
+ args[_i] = arguments$1[_i];
+ }
+ if (fn === null) {
+ throw new Error('Callback was already called.');
+ }
+ var callFn = fn;
+ fn = null;
+ callFn.apply(this, args);
+ };
+ }
+ /**
+ * @private
+ * @memberof PIXI
+ */
+ var AsyncQueueItem = /** @class */ (function () {
+ /**
+ * @param data
+ * @param callback
+ * @private
+ */
+ function AsyncQueueItem(data, callback) {
+ this.data = data;
+ this.callback = callback;
+ }
+ return AsyncQueueItem;
+ }());
+ /**
+ * @private
+ * @memberof PIXI
+ */
+ var AsyncQueue = /** @class */ (function () {
+ /**
+ * @param worker
+ * @param concurrency
+ * @private
+ */
+ function AsyncQueue(worker, concurrency) {
+ var _this = this;
+ if (concurrency === void 0) { concurrency = 1; }
+ this.workers = 0;
+ this.saturated = _noop;
+ this.unsaturated = _noop;
+ this.empty = _noop;
+ this.drain = _noop;
+ this.error = _noop;
+ this.started = false;
+ this.paused = false;
+ this._tasks = [];
+ this._insert = function (data, insertAtFront, callback) {
+ if (callback && typeof callback !== 'function') {
+ throw new Error('task callback must be a function');
+ }
+ _this.started = true;
+ // eslint-disable-next-line no-eq-null,eqeqeq
+ if (data == null && _this.idle()) {
+ // call drain immediately if there are no tasks
+ setTimeout(function () { return _this.drain(); }, 1);
+ return;
+ }
+ var item = new AsyncQueueItem(data, typeof callback === 'function' ? callback : _noop);
+ if (insertAtFront) {
+ _this._tasks.unshift(item);
+ }
+ else {
+ _this._tasks.push(item);
+ }
+ setTimeout(_this.process, 1);
+ };
+ this.process = function () {
+ while (!_this.paused && _this.workers < _this.concurrency && _this._tasks.length) {
+ var task = _this._tasks.shift();
+ if (_this._tasks.length === 0) {
+ _this.empty();
+ }
+ _this.workers += 1;
+ if (_this.workers === _this.concurrency) {
+ _this.saturated();
+ }
+ _this._worker(task.data, onlyOnce(_this._next(task)));
+ }
+ };
+ this._worker = worker;
+ if (concurrency === 0) {
+ throw new Error('Concurrency must not be zero');
+ }
+ this.concurrency = concurrency;
+ this.buffer = concurrency / 4.0;
+ }
+ /**
+ * @param task
+ * @private
+ */
+ AsyncQueue.prototype._next = function (task) {
+ var _this = this;
+ return function () {
+ var arguments$1 = arguments;
+
+ var args = [];
+ for (var _i = 0; _i < arguments.length; _i++) {
+ args[_i] = arguments$1[_i];
+ }
+ _this.workers -= 1;
+ task.callback.apply(task, args);
+ // eslint-disable-next-line no-eq-null,eqeqeq
+ if (args[0] != null) {
+ _this.error(args[0], task.data);
+ }
+ if (_this.workers <= (_this.concurrency - _this.buffer)) {
+ _this.unsaturated();
+ }
+ if (_this.idle()) {
+ _this.drain();
+ }
+ _this.process();
+ };
+ };
+ // That was in object
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+ AsyncQueue.prototype.push = function (data, callback) {
+ this._insert(data, false, callback);
+ };
+ AsyncQueue.prototype.kill = function () {
+ this.workers = 0;
+ this.drain = _noop;
+ this.started = false;
+ this._tasks = [];
+ };
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+ AsyncQueue.prototype.unshift = function (data, callback) {
+ this._insert(data, true, callback);
+ };
+ AsyncQueue.prototype.length = function () {
+ return this._tasks.length;
+ };
+ AsyncQueue.prototype.running = function () {
+ return this.workers;
+ };
+ AsyncQueue.prototype.idle = function () {
+ return this._tasks.length + this.workers === 0;
+ };
+ AsyncQueue.prototype.pause = function () {
+ if (this.paused === true) {
+ return;
+ }
+ this.paused = true;
+ };
+ AsyncQueue.prototype.resume = function () {
+ if (this.paused === false) {
+ return;
+ }
+ this.paused = false;
+ // Need to call this.process once per concurrent
+ // worker to preserve full concurrency after pause
+ for (var w = 1; w <= this.concurrency; w++) {
+ this.process();
+ }
+ };
+ /**
+ * Iterates an array in series.
+ * @param {Array.<*>} array - Array to iterate.
+ * @param {Function} iterator - Function to call for each element.
+ * @param {Function} callback - Function to call when done, or on error.
+ * @param {boolean} [deferNext=false] - Break synchronous each loop by calling next with a setTimeout of 1.
+ */
+ AsyncQueue.eachSeries = function (array, iterator, callback, deferNext) {
+ var i = 0;
+ var len = array.length;
+ function next(err) {
+ if (err || i === len) {
+ if (callback) {
+ callback(err);
+ }
+ return;
+ }
+ if (deferNext) {
+ setTimeout(function () {
+ iterator(array[i++], next);
+ }, 1);
+ }
+ else {
+ iterator(array[i++], next);
+ }
+ }
+ next();
+ };
+ /**
+ * Async queue implementation,
+ * @param {Function} worker - The worker function to call for each task.
+ * @param {number} concurrency - How many workers to run in parrallel.
+ * @returns {*} The async queue object.
+ */
+ AsyncQueue.queue = function (worker, concurrency) {
+ return new AsyncQueue(worker, concurrency);
+ };
+ return AsyncQueue;
+ }());
+
+ // some constants
+ var MAX_PROGRESS = 100;
+ var rgxExtractUrlHash = /(#[\w-]+)?$/;
+ /**
+ * The new loader, forked from Resource Loader by Chad Engler: https://github.com/englercj/resource-loader
+ *
+ * ```js
+ * const loader = PIXI.Loader.shared; // PixiJS exposes a premade instance for you to use.
+ * // or
+ * const loader = new PIXI.Loader(); // You can also create your own if you want
+ *
+ * const sprites = {};
+ *
+ * // Chainable `add` to enqueue a resource
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.add('scoreFont', 'assets/score.fnt');
+ *
+ * // Chainable `pre` to add a middleware that runs for each resource, *before* loading that resource.
+ * // This is useful to implement custom caching modules (using filesystem, indexeddb, memory, etc).
+ * loader.pre(cachingMiddleware);
+ *
+ * // Chainable `use` to add a middleware that runs for each resource, *after* loading that resource.
+ * // This is useful to implement custom parsing modules (like spritesheet parsers, spine parser, etc).
+ * loader.use(parsingMiddleware);
+ *
+ * // The `load` method loads the queue of resources, and calls the passed in callback called once all
+ * // resources have loaded.
+ * loader.load((loader, resources) => {
+ * // resources is an object where the key is the name of the resource loaded and the value is the resource object.
+ * // They have a couple default properties:
+ * // - `url`: The URL that the resource was loaded from
+ * // - `error`: The error that happened when trying to load (if any)
+ * // - `data`: The raw data that was loaded
+ * // also may contain other properties based on the middleware that runs.
+ * sprites.bunny = new PIXI.TilingSprite(resources.bunny.texture);
+ * sprites.spaceship = new PIXI.TilingSprite(resources.spaceship.texture);
+ * sprites.scoreFont = new PIXI.TilingSprite(resources.scoreFont.texture);
+ * });
+ *
+ * // throughout the process multiple signals can be dispatched.
+ * loader.onProgress.add(() => {}); // called once per loaded/errored file
+ * loader.onError.add(() => {}); // called once per errored file
+ * loader.onLoad.add(() => {}); // called once per loaded file
+ * loader.onComplete.add(() => {}); // called once when the queued resources all load.
+ * ```
+ * @memberof PIXI
+ */
+ var Loader = /** @class */ (function () {
+ /**
+ * @param baseUrl - The base url for all resources loaded by this loader.
+ * @param concurrency - The number of resources to load concurrently.
+ */
+ function Loader(baseUrl, concurrency) {
+ var _this = this;
+ if (baseUrl === void 0) { baseUrl = ''; }
+ if (concurrency === void 0) { concurrency = 10; }
+ /** The progress percent of the loader going through the queue. */
+ this.progress = 0;
+ /** Loading state of the loader, true if it is currently loading resources. */
+ this.loading = false;
+ /**
+ * A querystring to append to every URL added to the loader.
+ *
+ * This should be a valid query string *without* the question-mark (`?`). The loader will
+ * also *not* escape values for you. Make sure to escape your parameters with
+ * [`encodeURIComponent`](https://mdn.io/encodeURIComponent) before assigning this property.
+ * @example
+ * const loader = new Loader();
+ *
+ * loader.defaultQueryString = 'user=me&password=secret';
+ *
+ * // This will request 'image.png?user=me&password=secret'
+ * loader.add('image.png').load();
+ *
+ * loader.reset();
+ *
+ * // This will request 'image.png?v=1&user=me&password=secret'
+ * loader.add('iamge.png?v=1').load();
+ */
+ this.defaultQueryString = '';
+ /** The middleware to run before loading each resource. */
+ this._beforeMiddleware = [];
+ /** The middleware to run after loading each resource. */
+ this._afterMiddleware = [];
+ /** The tracks the resources we are currently completing parsing for. */
+ this._resourcesParsing = [];
+ /**
+ * The `_loadResource` function bound with this object context.
+ * @param r - The resource to load
+ * @param d - The dequeue function
+ */
+ this._boundLoadResource = function (r, d) { return _this._loadResource(r, d); };
+ /** All the resources for this loader keyed by name. */
+ this.resources = {};
+ this.baseUrl = baseUrl;
+ this._beforeMiddleware = [];
+ this._afterMiddleware = [];
+ this._resourcesParsing = [];
+ this._boundLoadResource = function (r, d) { return _this._loadResource(r, d); };
+ this._queue = AsyncQueue.queue(this._boundLoadResource, concurrency);
+ this._queue.pause();
+ this.resources = {};
+ this.onProgress = new Signal();
+ this.onError = new Signal();
+ this.onLoad = new Signal();
+ this.onStart = new Signal();
+ this.onComplete = new Signal();
+ for (var i = 0; i < Loader._plugins.length; ++i) {
+ var plugin = Loader._plugins[i];
+ var pre = plugin.pre, use = plugin.use;
+ if (pre) {
+ this.pre(pre);
+ }
+ if (use) {
+ this.use(use);
+ }
+ }
+ this._protected = false;
+ }
+ /**
+ * Same as add, params have strict order
+ * @private
+ * @param name - The name of the resource to load.
+ * @param url - The url for this resource, relative to the baseUrl of this loader.
+ * @param options - The options for the load.
+ * @param callback - Function to call when this specific resource completes loading.
+ * @returns The loader itself.
+ */
+ Loader.prototype._add = function (name, url, options, callback) {
+ // if loading already you can only add resources that have a parent.
+ if (this.loading && (!options || !options.parentResource)) {
+ throw new Error('Cannot add resources while the loader is running.');
+ }
+ // check if resource already exists.
+ if (this.resources[name]) {
+ throw new Error("Resource named \"" + name + "\" already exists.");
+ }
+ // add base url if this isn't an absolute url
+ url = this._prepareUrl(url);
+ // create the store the resource
+ this.resources[name] = new exports.LoaderResource(name, url, options);
+ if (typeof callback === 'function') {
+ this.resources[name].onAfterMiddleware.once(callback);
+ }
+ // if actively loading, make sure to adjust progress chunks for that parent and its children
+ if (this.loading) {
+ var parent = options.parentResource;
+ var incompleteChildren = [];
+ for (var i = 0; i < parent.children.length; ++i) {
+ if (!parent.children[i].isComplete) {
+ incompleteChildren.push(parent.children[i]);
+ }
+ }
+ var fullChunk = parent.progressChunk * (incompleteChildren.length + 1); // +1 for parent
+ var eachChunk = fullChunk / (incompleteChildren.length + 2); // +2 for parent & new child
+ parent.children.push(this.resources[name]);
+ parent.progressChunk = eachChunk;
+ for (var i = 0; i < incompleteChildren.length; ++i) {
+ incompleteChildren[i].progressChunk = eachChunk;
+ }
+ this.resources[name].progressChunk = eachChunk;
+ }
+ // add the resource to the queue
+ this._queue.push(this.resources[name]);
+ return this;
+ };
+ /* eslint-enable require-jsdoc,valid-jsdoc */
+ /**
+ * Sets up a middleware function that will run *before* the
+ * resource is loaded.
+ * @param fn - The middleware function to register.
+ * @returns The loader itself.
+ */
+ Loader.prototype.pre = function (fn) {
+ this._beforeMiddleware.push(fn);
+ return this;
+ };
+ /**
+ * Sets up a middleware function that will run *after* the
+ * resource is loaded.
+ * @param fn - The middleware function to register.
+ * @returns The loader itself.
+ */
+ Loader.prototype.use = function (fn) {
+ this._afterMiddleware.push(fn);
+ return this;
+ };
+ /**
+ * Resets the queue of the loader to prepare for a new load.
+ * @returns The loader itself.
+ */
+ Loader.prototype.reset = function () {
+ this.progress = 0;
+ this.loading = false;
+ this._queue.kill();
+ this._queue.pause();
+ // abort all resource loads
+ for (var k in this.resources) {
+ var res = this.resources[k];
+ if (res._onLoadBinding) {
+ res._onLoadBinding.detach();
+ }
+ if (res.isLoading) {
+ res.abort('loader reset');
+ }
+ }
+ this.resources = {};
+ return this;
+ };
+ /**
+ * Starts loading the queued resources.
+ * @param cb - Optional callback that will be bound to the `complete` event.
+ * @returns The loader itself.
+ */
+ Loader.prototype.load = function (cb) {
+ deprecation$1('6.5.0', '@pixi/loaders is being replaced with @pixi/assets in the next major release.');
+ // register complete callback if they pass one
+ if (typeof cb === 'function') {
+ this.onComplete.once(cb);
+ }
+ // if the queue has already started we are done here
+ if (this.loading) {
+ return this;
+ }
+ if (this._queue.idle()) {
+ this._onStart();
+ this._onComplete();
+ }
+ else {
+ // distribute progress chunks
+ var numTasks = this._queue._tasks.length;
+ var chunk = MAX_PROGRESS / numTasks;
+ for (var i = 0; i < this._queue._tasks.length; ++i) {
+ this._queue._tasks[i].data.progressChunk = chunk;
+ }
+ // notify we are starting
+ this._onStart();
+ // start loading
+ this._queue.resume();
+ }
+ return this;
+ };
+ Object.defineProperty(Loader.prototype, "concurrency", {
+ /**
+ * The number of resources to load concurrently.
+ * @default 10
+ */
+ get: function () {
+ return this._queue.concurrency;
+ },
+ set: function (concurrency) {
+ this._queue.concurrency = concurrency;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Prepares a url for usage based on the configuration of this object
+ * @param url - The url to prepare.
+ * @returns The prepared url.
+ */
+ Loader.prototype._prepareUrl = function (url) {
+ var parsedUrl = parseUri(url, { strictMode: true });
+ var result;
+ // absolute url, just use it as is.
+ if (parsedUrl.protocol || !parsedUrl.path || url.indexOf('//') === 0) {
+ result = url;
+ }
+ // if baseUrl doesn't end in slash and url doesn't start with slash, then add a slash inbetween
+ else if (this.baseUrl.length
+ && this.baseUrl.lastIndexOf('/') !== this.baseUrl.length - 1
+ && url.charAt(0) !== '/') {
+ result = this.baseUrl + "/" + url;
+ }
+ else {
+ result = this.baseUrl + url;
+ }
+ // if we need to add a default querystring, there is a bit more work
+ if (this.defaultQueryString) {
+ var hash = rgxExtractUrlHash.exec(result)[0];
+ result = result.slice(0, result.length - hash.length);
+ if (result.indexOf('?') !== -1) {
+ result += "&" + this.defaultQueryString;
+ }
+ else {
+ result += "?" + this.defaultQueryString;
+ }
+ result += hash;
+ }
+ return result;
+ };
+ /**
+ * Loads a single resource.
+ * @param resource - The resource to load.
+ * @param dequeue - The function to call when we need to dequeue this item.
+ */
+ Loader.prototype._loadResource = function (resource, dequeue) {
+ var _this = this;
+ resource._dequeue = dequeue;
+ // run before middleware
+ AsyncQueue.eachSeries(this._beforeMiddleware, function (fn, next) {
+ fn.call(_this, resource, function () {
+ // if the before middleware marks the resource as complete,
+ // break and don't process any more before middleware
+ next(resource.isComplete ? {} : null);
+ });
+ }, function () {
+ if (resource.isComplete) {
+ _this._onLoad(resource);
+ }
+ else {
+ resource._onLoadBinding = resource.onComplete.once(_this._onLoad, _this);
+ resource.load();
+ }
+ }, true);
+ };
+ /** Called once loading has started. */
+ Loader.prototype._onStart = function () {
+ this.progress = 0;
+ this.loading = true;
+ this.onStart.dispatch(this);
+ };
+ /** Called once each resource has loaded. */
+ Loader.prototype._onComplete = function () {
+ this.progress = MAX_PROGRESS;
+ this.loading = false;
+ this.onComplete.dispatch(this, this.resources);
+ };
+ /**
+ * Called each time a resources is loaded.
+ * @param resource - The resource that was loaded
+ */
+ Loader.prototype._onLoad = function (resource) {
+ var _this = this;
+ resource._onLoadBinding = null;
+ // remove this resource from the async queue, and add it to our list of resources that are being parsed
+ this._resourcesParsing.push(resource);
+ resource._dequeue();
+ // run all the after middleware for this resource
+ AsyncQueue.eachSeries(this._afterMiddleware, function (fn, next) {
+ fn.call(_this, resource, next);
+ }, function () {
+ resource.onAfterMiddleware.dispatch(resource);
+ _this.progress = Math.min(MAX_PROGRESS, _this.progress + resource.progressChunk);
+ _this.onProgress.dispatch(_this, resource);
+ if (resource.error) {
+ _this.onError.dispatch(resource.error, _this, resource);
+ }
+ else {
+ _this.onLoad.dispatch(_this, resource);
+ }
+ _this._resourcesParsing.splice(_this._resourcesParsing.indexOf(resource), 1);
+ // do completion check
+ if (_this._queue.idle() && _this._resourcesParsing.length === 0) {
+ _this._onComplete();
+ }
+ }, true);
+ };
+ /** Destroy the loader, removes references. */
+ Loader.prototype.destroy = function () {
+ if (!this._protected) {
+ this.reset();
+ }
+ };
+ Object.defineProperty(Loader, "shared", {
+ /** A premade instance of the loader that can be used to load resources. */
+ get: function () {
+ var shared = Loader._shared;
+ if (!shared) {
+ shared = new Loader();
+ shared._protected = true;
+ Loader._shared = shared;
+ }
+ return shared;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Use the {@link PIXI.extensions.add} API to register plugins.
+ * @deprecated since 6.5.0
+ * @param plugin - The plugin to add
+ * @returns Reference to PIXI.Loader for chaining
+ */
+ Loader.registerPlugin = function (plugin) {
+ deprecation$1('6.5.0', 'Loader.registerPlugin() is deprecated, use extensions.add() instead.');
+ extensions.add({
+ type: exports.ExtensionType.Loader,
+ ref: plugin,
+ });
+ return Loader;
+ };
+ Loader._plugins = [];
+ return Loader;
+ }());
+ extensions.handleByList(exports.ExtensionType.Loader, Loader._plugins);
+ Loader.prototype.add = function add(name, url, options, callback) {
+ // special case of an array of objects or urls
+ if (Array.isArray(name)) {
+ for (var i = 0; i < name.length; ++i) {
+ this.add(name[i]);
+ }
+ return this;
+ }
+ // if an object is passed instead of params
+ if (typeof name === 'object') {
+ options = name;
+ callback = url || options.callback || options.onComplete;
+ url = options.url;
+ name = options.name || options.key || options.url;
+ }
+ // case where no name is passed shift all args over by one.
+ if (typeof url !== 'string') {
+ callback = options;
+ options = url;
+ url = name;
+ }
+ // now that we shifted make sure we have a proper url.
+ if (typeof url !== 'string') {
+ throw new Error('No url passed to add resource to loader.');
+ }
+ // options are optional so people might pass a function and no options
+ if (typeof options === 'function') {
+ callback = options;
+ options = null;
+ }
+ return this._add(name, url, options, callback);
+ };
+
+ /**
+ * Application plugin for supporting loader option. Installing the LoaderPlugin
+ * is not necessary if using **pixi.js** or **pixi.js-legacy**.
+ * @example
+ * import {AppLoaderPlugin} from '@pixi/loaders';
+ * import {extensions} from '@pixi/core';
+ * extensions.add(AppLoaderPlugin);
+ * @memberof PIXI
+ */
+ var AppLoaderPlugin = /** @class */ (function () {
+ function AppLoaderPlugin() {
+ }
+ /**
+ * Called on application constructor
+ * @param options
+ * @private
+ */
+ AppLoaderPlugin.init = function (options) {
+ options = Object.assign({
+ sharedLoader: false,
+ }, options);
+ this.loader = options.sharedLoader ? Loader.shared : new Loader();
+ };
+ /**
+ * Called when application destroyed
+ * @private
+ */
+ AppLoaderPlugin.destroy = function () {
+ if (this.loader) {
+ this.loader.destroy();
+ this.loader = null;
+ }
+ };
+ /** @ignore */
+ AppLoaderPlugin.extension = exports.ExtensionType.Application;
+ return AppLoaderPlugin;
+ }());
+
+ /**
+ * Loader plugin for handling Texture resources.
+ * @memberof PIXI
+ */
+ var TextureLoader = /** @class */ (function () {
+ function TextureLoader() {
+ }
+ /** Handle SVG elements a text, render with SVGResource. */
+ TextureLoader.add = function () {
+ exports.LoaderResource.setExtensionLoadType('svg', exports.LoaderResource.LOAD_TYPE.XHR);
+ exports.LoaderResource.setExtensionXhrType('svg', exports.LoaderResource.XHR_RESPONSE_TYPE.TEXT);
+ };
+ /**
+ * Called after a resource is loaded.
+ * @see PIXI.Loader.loaderMiddleware
+ * @param resource
+ * @param {Function} next
+ */
+ TextureLoader.use = function (resource, next) {
+ // create a new texture if the data is an Image object
+ if (resource.data && (resource.type === exports.LoaderResource.TYPE.IMAGE || resource.extension === 'svg')) {
+ var data = resource.data, url = resource.url, name = resource.name, metadata = resource.metadata;
+ Texture.fromLoader(data, url, name, metadata).then(function (texture) {
+ resource.texture = texture;
+ next();
+ })
+ // TODO: handle errors in Texture.fromLoader
+ // so we can pass them to the Loader
+ .catch(next);
+ }
+ else {
+ next();
+ }
+ };
+ /** @ignore */
+ TextureLoader.extension = exports.ExtensionType.Loader;
+ return TextureLoader;
+ }());
+
+ var _keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
+ /**
+ * Encodes binary into base64.
+ * @function encodeBinary
+ * @param {string} input - The input data to encode.
+ * @returns {string} The encoded base64 string
+ */
+ function encodeBinary(input) {
+ var output = '';
+ var inx = 0;
+ while (inx < input.length) {
+ // Fill byte buffer array
+ var bytebuffer = [0, 0, 0];
+ var encodedCharIndexes = [0, 0, 0, 0];
+ for (var jnx = 0; jnx < bytebuffer.length; ++jnx) {
+ if (inx < input.length) {
+ // throw away high-order byte, as documented at:
+ // https://developer.mozilla.org/En/Using_XMLHttpRequest#Handling_binary_data
+ bytebuffer[jnx] = input.charCodeAt(inx++) & 0xff;
+ }
+ else {
+ bytebuffer[jnx] = 0;
+ }
+ }
+ // Get each encoded character, 6 bits at a time
+ // index 1: first 6 bits
+ encodedCharIndexes[0] = bytebuffer[0] >> 2;
+ // index 2: second 6 bits (2 least significant bits from input byte 1 + 4 most significant bits from byte 2)
+ encodedCharIndexes[1] = ((bytebuffer[0] & 0x3) << 4) | (bytebuffer[1] >> 4);
+ // index 3: third 6 bits (4 least significant bits from input byte 2 + 2 most significant bits from byte 3)
+ encodedCharIndexes[2] = ((bytebuffer[1] & 0x0f) << 2) | (bytebuffer[2] >> 6);
+ // index 3: forth 6 bits (6 least significant bits from input byte 3)
+ encodedCharIndexes[3] = bytebuffer[2] & 0x3f;
+ // Determine whether padding happened, and adjust accordingly
+ var paddingBytes = inx - (input.length - 1);
+ switch (paddingBytes) {
+ case 2:
+ // Set last 2 characters to padding char
+ encodedCharIndexes[3] = 64;
+ encodedCharIndexes[2] = 64;
+ break;
+ case 1:
+ // Set last character to padding char
+ encodedCharIndexes[3] = 64;
+ break;
+ }
+ // Now we will grab each appropriate character out of our keystring
+ // based on our index array and append it to the output string
+ for (var jnx = 0; jnx < encodedCharIndexes.length; ++jnx) {
+ output += _keyStr.charAt(encodedCharIndexes[jnx]);
+ }
+ }
+ return output;
+ }
+
+ /**
+ * A middleware for transforming XHR loaded Blobs into more useful objects
+ * @ignore
+ * @function parsing
+ * @example
+ * import { Loader, middleware } from 'resource-loader';
+ * const loader = new Loader();
+ * loader.use(middleware.parsing);
+ * @param resource - Current Resource
+ * @param next - Callback when complete
+ */
+ function parsing(resource, next) {
+ if (!resource.data) {
+ next();
+ return;
+ }
+ // if this was an XHR load of a blob
+ if (resource.xhr && resource.xhrType === exports.LoaderResource.XHR_RESPONSE_TYPE.BLOB) {
+ // if there is no blob support we probably got a binary string back
+ if (!self.Blob || typeof resource.data === 'string') {
+ var type = resource.xhr.getResponseHeader('content-type');
+ // this is an image, convert the binary string into a data url
+ if (type && type.indexOf('image') === 0) {
+ resource.data = new Image();
+ resource.data.src = "data:" + type + ";base64," + encodeBinary(resource.xhr.responseText);
+ resource.type = exports.LoaderResource.TYPE.IMAGE;
+ // wait until the image loads and then callback
+ resource.data.onload = function () {
+ resource.data.onload = null;
+ next();
+ };
+ // next will be called on load
+ return;
+ }
+ }
+ // if content type says this is an image, then we should transform the blob into an Image object
+ else if (resource.data.type.indexOf('image') === 0) {
+ var Url_1 = globalThis.URL || globalThis.webkitURL;
+ var src_1 = Url_1.createObjectURL(resource.data);
+ resource.blob = resource.data;
+ resource.data = new Image();
+ resource.data.src = src_1;
+ resource.type = exports.LoaderResource.TYPE.IMAGE;
+ // cleanup the no longer used blob after the image loads
+ // TODO: Is this correct? Will the image be invalid after revoking?
+ resource.data.onload = function () {
+ Url_1.revokeObjectURL(src_1);
+ resource.data.onload = null;
+ next();
+ };
+ // next will be called on load.
+ return;
+ }
+ }
+ next();
+ }
+
+ /**
+ * Parse any blob into more usable objects (e.g. Image).
+ * @memberof PIXI
+ */
+ var ParsingLoader = /** @class */ (function () {
+ function ParsingLoader() {
+ }
+ /** @ignore */
+ ParsingLoader.extension = exports.ExtensionType.Loader;
+ ParsingLoader.use = parsing;
+ return ParsingLoader;
+ }());
+
+ extensions.add(TextureLoader, ParsingLoader);
+
+ /*!
+ * @pixi/compressed-textures - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/compressed-textures is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ var _a$2;
+ /**
+ * WebGL internal formats, including compressed texture formats provided by extensions
+ * @memberof PIXI
+ * @static
+ * @name INTERNAL_FORMATS
+ * @enum {number}
+ * @property {number} [COMPRESSED_RGB_S3TC_DXT1_EXT=0x83F0] -
+ * @property {number} [COMPRESSED_RGBA_S3TC_DXT1_EXT=0x83F1] -
+ * @property {number} [COMPRESSED_RGBA_S3TC_DXT3_EXT=0x83F2] -
+ * @property {number} [COMPRESSED_RGBA_S3TC_DXT5_EXT=0x83F3] -
+ * @property {number} [COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT=35917] -
+ * @property {number} [COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT=35918] -
+ * @property {number} [COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT=35919] -
+ * @property {number} [COMPRESSED_SRGB_S3TC_DXT1_EXT=35916] -
+ * @property {number} [COMPRESSED_R11_EAC=0x9270] -
+ * @property {number} [COMPRESSED_SIGNED_R11_EAC=0x9271] -
+ * @property {number} [COMPRESSED_RG11_EAC=0x9272] -
+ * @property {number} [COMPRESSED_SIGNED_RG11_EAC=0x9273] -
+ * @property {number} [COMPRESSED_RGB8_ETC2=0x9274] -
+ * @property {number} [COMPRESSED_RGBA8_ETC2_EAC=0x9278] -
+ * @property {number} [COMPRESSED_SRGB8_ETC2=0x9275] -
+ * @property {number} [COMPRESSED_SRGB8_ALPHA8_ETC2_EAC=0x9279] -
+ * @property {number} [COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2=0x9276] -
+ * @property {number} [COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2=0x9277] -
+ * @property {number} [COMPRESSED_RGB_PVRTC_4BPPV1_IMG=0x8C00] -
+ * @property {number} [COMPRESSED_RGBA_PVRTC_4BPPV1_IMG=0x8C02] -
+ * @property {number} [COMPRESSED_RGB_PVRTC_2BPPV1_IMG=0x8C01] -
+ * @property {number} [COMPRESSED_RGBA_PVRTC_2BPPV1_IMG=0x8C03] -
+ * @property {number} [COMPRESSED_RGB_ETC1_WEBGL=0x8D64] -
+ * @property {number} [COMPRESSED_RGB_ATC_WEBGL=0x8C92] -
+ * @property {number} [COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL=0x8C92] -
+ * @property {number} [COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL=0x87EE] -
+ */
+ exports.INTERNAL_FORMATS = void 0;
+ (function (INTERNAL_FORMATS) {
+ // WEBGL_compressed_texture_s3tc
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_RGB_S3TC_DXT1_EXT"] = 33776] = "COMPRESSED_RGB_S3TC_DXT1_EXT";
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_RGBA_S3TC_DXT1_EXT"] = 33777] = "COMPRESSED_RGBA_S3TC_DXT1_EXT";
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_RGBA_S3TC_DXT3_EXT"] = 33778] = "COMPRESSED_RGBA_S3TC_DXT3_EXT";
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_RGBA_S3TC_DXT5_EXT"] = 33779] = "COMPRESSED_RGBA_S3TC_DXT5_EXT";
+ // WEBGL_compressed_texture_s3tc_srgb
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT"] = 35917] = "COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT";
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT"] = 35918] = "COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT";
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT"] = 35919] = "COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT";
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_SRGB_S3TC_DXT1_EXT"] = 35916] = "COMPRESSED_SRGB_S3TC_DXT1_EXT";
+ // WEBGL_compressed_texture_etc
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_R11_EAC"] = 37488] = "COMPRESSED_R11_EAC";
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_SIGNED_R11_EAC"] = 37489] = "COMPRESSED_SIGNED_R11_EAC";
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_RG11_EAC"] = 37490] = "COMPRESSED_RG11_EAC";
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_SIGNED_RG11_EAC"] = 37491] = "COMPRESSED_SIGNED_RG11_EAC";
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_RGB8_ETC2"] = 37492] = "COMPRESSED_RGB8_ETC2";
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_RGBA8_ETC2_EAC"] = 37496] = "COMPRESSED_RGBA8_ETC2_EAC";
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_SRGB8_ETC2"] = 37493] = "COMPRESSED_SRGB8_ETC2";
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_SRGB8_ALPHA8_ETC2_EAC"] = 37497] = "COMPRESSED_SRGB8_ALPHA8_ETC2_EAC";
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2"] = 37494] = "COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2";
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2"] = 37495] = "COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2";
+ // WEBGL_compressed_texture_pvrtc
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_RGB_PVRTC_4BPPV1_IMG"] = 35840] = "COMPRESSED_RGB_PVRTC_4BPPV1_IMG";
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_RGBA_PVRTC_4BPPV1_IMG"] = 35842] = "COMPRESSED_RGBA_PVRTC_4BPPV1_IMG";
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_RGB_PVRTC_2BPPV1_IMG"] = 35841] = "COMPRESSED_RGB_PVRTC_2BPPV1_IMG";
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_RGBA_PVRTC_2BPPV1_IMG"] = 35843] = "COMPRESSED_RGBA_PVRTC_2BPPV1_IMG";
+ // WEBGL_compressed_texture_etc1
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_RGB_ETC1_WEBGL"] = 36196] = "COMPRESSED_RGB_ETC1_WEBGL";
+ // WEBGL_compressed_texture_atc
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_RGB_ATC_WEBGL"] = 35986] = "COMPRESSED_RGB_ATC_WEBGL";
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL"] = 35986] = "COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL";
+ INTERNAL_FORMATS[INTERNAL_FORMATS["COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL"] = 34798] = "COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL";
+ })(exports.INTERNAL_FORMATS || (exports.INTERNAL_FORMATS = {}));
+ /**
+ * Maps the compressed texture formats in {@link PIXI.INTERNAL_FORMATS} to the number of bytes taken by
+ * each texel.
+ * @memberof PIXI
+ * @static
+ * @ignore
+ */
+ var INTERNAL_FORMAT_TO_BYTES_PER_PIXEL = (_a$2 = {},
+ // WEBGL_compressed_texture_s3tc
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_RGB_S3TC_DXT1_EXT] = 0.5,
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_RGBA_S3TC_DXT1_EXT] = 0.5,
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_RGBA_S3TC_DXT3_EXT] = 1,
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_RGBA_S3TC_DXT5_EXT] = 1,
+ // WEBGL_compressed_texture_s3tc
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_SRGB_S3TC_DXT1_EXT] = 0.5,
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT] = 0.5,
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT] = 1,
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT] = 1,
+ // WEBGL_compressed_texture_etc
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_R11_EAC] = 0.5,
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_SIGNED_R11_EAC] = 0.5,
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_RG11_EAC] = 1,
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_SIGNED_RG11_EAC] = 1,
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_RGB8_ETC2] = 0.5,
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_RGBA8_ETC2_EAC] = 1,
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_SRGB8_ETC2] = 0.5,
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC] = 1,
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2] = 0.5,
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2] = 0.5,
+ // WEBGL_compressed_texture_pvrtc
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_RGB_PVRTC_4BPPV1_IMG] = 0.5,
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG] = 0.5,
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_RGB_PVRTC_2BPPV1_IMG] = 0.25,
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG] = 0.25,
+ // WEBGL_compressed_texture_etc1
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_RGB_ETC1_WEBGL] = 0.5,
+ // @see https://www.khronos.org/registry/OpenGL/extensions/AMD/AMD_compressed_ATC_texture.txt
+ // WEBGL_compressed_texture_atc
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_RGB_ATC_WEBGL] = 0.5,
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL] = 1,
+ _a$2[exports.INTERNAL_FORMATS.COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL] = 1,
+ _a$2);
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+ /* global Reflect, Promise */
+
+ var extendStatics$g = function(d, b) {
+ extendStatics$g = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
+ return extendStatics$g(d, b);
+ };
+
+ function __extends$g(d, b) {
+ extendStatics$g(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ }
+
+ function __awaiter(thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+ }
+
+ function __generator(thisArg, body) {
+ var _ = { label: 0, sent: function() { if (t[0] & 1) { throw t[1]; } return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+ function verb(n) { return function (v) { return step([n, v]); }; }
+ function step(op) {
+ if (f) { throw new TypeError("Generator is already executing."); }
+ while (_) { try {
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) { return t; }
+ if (y = 0, t) { op = [op[0] & 2, t.value]; }
+ switch (op[0]) {
+ case 0: case 1: t = op; break;
+ case 4: _.label++; return { value: op[1], done: false };
+ case 5: _.label++; y = op[1]; op = [0]; continue;
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
+ default:
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+ if (t[2]) { _.ops.pop(); }
+ _.trys.pop(); continue;
+ }
+ op = body.call(thisArg, _);
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } }
+ if (op[0] & 5) { throw op[1]; } return { value: op[0] ? op[1] : void 0, done: true };
+ }
+ }
+
+ /**
+ * Resource that fetches texture data over the network and stores it in a buffer.
+ * @class
+ * @extends PIXI.Resource
+ * @memberof PIXI
+ */
+ var BlobResource = /** @class */ (function (_super) {
+ __extends$g(BlobResource, _super);
+ /**
+ * @param {string} source - the URL of the texture file
+ * @param {PIXI.IBlobOptions} options
+ * @param {boolean}[options.autoLoad] - whether to fetch the data immediately;
+ * you can fetch it later via {@link BlobResource#load}
+ * @param {boolean}[options.width] - the width in pixels.
+ * @param {boolean}[options.height] - the height in pixels.
+ */
+ function BlobResource(source, options) {
+ if (options === void 0) { options = { width: 1, height: 1, autoLoad: true }; }
+ var _this = this;
+ var origin;
+ var data;
+ if (typeof source === 'string') {
+ origin = source;
+ data = new Uint8Array();
+ }
+ else {
+ origin = null;
+ data = source;
+ }
+ _this = _super.call(this, data, options) || this;
+ /**
+ * The URL of the texture file
+ * @member {string}
+ */
+ _this.origin = origin;
+ /**
+ * The viewable buffer on the data
+ * @member {ViewableBuffer}
+ */
+ // HINT: BlobResource allows "null" sources, assuming the child class provides an alternative
+ _this.buffer = data ? new ViewableBuffer(data) : null;
+ // Allow autoLoad = "undefined" still load the resource by default
+ if (_this.origin && options.autoLoad !== false) {
+ _this.load();
+ }
+ if (data && data.length) {
+ _this.loaded = true;
+ _this.onBlobLoaded(_this.buffer.rawBinaryData);
+ }
+ return _this;
+ }
+ BlobResource.prototype.onBlobLoaded = function (_data) {
+ // TODO: Override this method
+ };
+ /** Loads the blob */
+ BlobResource.prototype.load = function () {
+ return __awaiter(this, void 0, Promise, function () {
+ var response, blob, arrayBuffer;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0: return [4 /*yield*/, fetch(this.origin)];
+ case 1:
+ response = _a.sent();
+ return [4 /*yield*/, response.blob()];
+ case 2:
+ blob = _a.sent();
+ return [4 /*yield*/, blob.arrayBuffer()];
+ case 3:
+ arrayBuffer = _a.sent();
+ this.data = new Uint32Array(arrayBuffer);
+ this.buffer = new ViewableBuffer(arrayBuffer);
+ this.loaded = true;
+ this.onBlobLoaded(arrayBuffer);
+ this.update();
+ return [2 /*return*/, this];
+ }
+ });
+ });
+ };
+ return BlobResource;
+ }(BufferResource));
+
+ /**
+ * Resource for compressed texture formats, as follows: S3TC/DXTn (& their sRGB formats), ATC, ASTC, ETC 1/2, PVRTC.
+ *
+ * Compressed textures improve performance when rendering is texture-bound. The texture data stays compressed in
+ * graphics memory, increasing memory locality and speeding up texture fetches. These formats can also be used to store
+ * more detail in the same amount of memory.
+ *
+ * For most developers, container file formats are a better abstraction instead of directly handling raw texture
+ * data. PixiJS provides native support for the following texture file formats (via {@link PIXI.Loader}):
+ *
+ * **.dds** - the DirectDraw Surface file format stores DXTn (DXT-1,3,5) data. See {@link PIXI.DDSLoader}
+ * **.ktx** - the Khronos Texture Container file format supports storing all the supported WebGL compression formats.
+ * See {@link PIXI.KTXLoader}.
+ * **.basis** - the BASIS supercompressed file format stores texture data in an internal format that is transcoded
+ * to the compression format supported on the device at _runtime_. It also supports transcoding into a uncompressed
+ * format as a fallback; you must install the `@pixi/basis-loader`, `@pixi/basis-transcoder` packages separately to
+ * use these files. See {@link PIXI.BasisLoader}.
+ *
+ * The loaders for the aforementioned formats use `CompressedTextureResource` internally. It is strongly suggested that
+ * they be used instead.
+ *
+ * ## Working directly with CompressedTextureResource
+ *
+ * Since `CompressedTextureResource` inherits `BlobResource`, you can provide it a URL pointing to a file containing
+ * the raw texture data (with no file headers!):
+ *
+ * ```js
+ * // The resource backing the texture data for your textures.
+ * // NOTE: You can also provide a ArrayBufferView instead of a URL. This is used when loading data from a container file
+ * // format such as KTX, DDS, or BASIS.
+ * const compressedResource = new PIXI.CompressedTextureResource("bunny.dxt5", {
+ * format: PIXI.INTERNAL_FORMATS.COMPRESSED_RGBA_S3TC_DXT5_EXT,
+ * width: 256,
+ * height: 256
+ * });
+ *
+ * // You can create a base-texture to the cache, so that future `Texture`s can be created using the `Texture.from` API.
+ * const baseTexture = new PIXI.BaseTexture(compressedResource, { pmaMode: PIXI.ALPHA_MODES.NPM });
+ *
+ * // Create a Texture to add to the TextureCache
+ * const texture = new PIXI.Texture(baseTexture);
+ *
+ * // Add baseTexture & texture to the global texture cache
+ * PIXI.BaseTexture.addToCache(baseTexture, "bunny.dxt5");
+ * PIXI.Texture.addToCache(texture, "bunny.dxt5");
+ * ```
+ * @memberof PIXI
+ */
+ var CompressedTextureResource = /** @class */ (function (_super) {
+ __extends$g(CompressedTextureResource, _super);
+ /**
+ * @param source - the buffer/URL holding the compressed texture data
+ * @param options
+ * @param {PIXI.INTERNAL_FORMATS} options.format - the compression format
+ * @param {number} options.width - the image width in pixels.
+ * @param {number} options.height - the image height in pixels.
+ * @param {number} [options.level=1] - the mipmap levels stored in the compressed texture, including level 0.
+ * @param {number} [options.levelBuffers] - the buffers for each mipmap level. `CompressedTextureResource` can allows you
+ * to pass `null` for `source`, for cases where each level is stored in non-contiguous memory.
+ */
+ function CompressedTextureResource(source, options) {
+ var _this = _super.call(this, source, options) || this;
+ _this.format = options.format;
+ _this.levels = options.levels || 1;
+ _this._width = options.width;
+ _this._height = options.height;
+ _this._extension = CompressedTextureResource._formatToExtension(_this.format);
+ if (options.levelBuffers || _this.buffer) {
+ // ViewableBuffer doesn't support byteOffset :-( so allow source to be Uint8Array
+ _this._levelBuffers = options.levelBuffers
+ || CompressedTextureResource._createLevelBuffers(source instanceof Uint8Array ? source : _this.buffer.uint8View, _this.format, _this.levels, 4, 4, // PVRTC has 8x4 blocks in 2bpp mode
+ _this.width, _this.height);
+ }
+ return _this;
+ }
+ /**
+ * @override
+ * @param renderer - A reference to the current renderer
+ * @param _texture - the texture
+ * @param _glTexture - texture instance for this webgl context
+ */
+ CompressedTextureResource.prototype.upload = function (renderer, _texture, _glTexture) {
+ var gl = renderer.gl;
+ var extension = renderer.context.extensions[this._extension];
+ if (!extension) {
+ throw new Error(this._extension + " textures are not supported on the current machine");
+ }
+ if (!this._levelBuffers) {
+ // Do not try to upload data before BlobResource loads, unless the levelBuffers were provided directly!
+ return false;
+ }
+ for (var i = 0, j = this.levels; i < j; i++) {
+ var _a = this._levelBuffers[i], levelID = _a.levelID, levelWidth = _a.levelWidth, levelHeight = _a.levelHeight, levelBuffer = _a.levelBuffer;
+ gl.compressedTexImage2D(gl.TEXTURE_2D, levelID, this.format, levelWidth, levelHeight, 0, levelBuffer);
+ }
+ return true;
+ };
+ /** @protected */
+ CompressedTextureResource.prototype.onBlobLoaded = function () {
+ this._levelBuffers = CompressedTextureResource._createLevelBuffers(this.buffer.uint8View, this.format, this.levels, 4, 4, // PVRTC has 8x4 blocks in 2bpp mode
+ this.width, this.height);
+ };
+ /**
+ * Returns the key (to ContextSystem#extensions) for the WebGL extension supporting the compression format
+ * @private
+ * @param format - the compression format to get the extension for.
+ */
+ CompressedTextureResource._formatToExtension = function (format) {
+ if (format >= 0x83F0 && format <= 0x83F3) {
+ return 's3tc';
+ }
+ else if (format >= 0x9270 && format <= 0x9279) {
+ return 'etc';
+ }
+ else if (format >= 0x8C00 && format <= 0x8C03) {
+ return 'pvrtc';
+ }
+ else if (format >= 0x8D64) {
+ return 'etc1';
+ }
+ else if (format >= 0x8C92 && format <= 0x87EE) {
+ return 'atc';
+ }
+ throw new Error('Invalid (compressed) texture format given!');
+ };
+ /**
+ * Pre-creates buffer views for each mipmap level
+ * @private
+ * @param buffer -
+ * @param format - compression formats
+ * @param levels - mipmap levels
+ * @param blockWidth -
+ * @param blockHeight -
+ * @param imageWidth - width of the image in pixels
+ * @param imageHeight - height of the image in pixels
+ */
+ CompressedTextureResource._createLevelBuffers = function (buffer, format, levels, blockWidth, blockHeight, imageWidth, imageHeight) {
+ // The byte-size of the first level buffer
+ var buffers = new Array(levels);
+ var offset = buffer.byteOffset;
+ var levelWidth = imageWidth;
+ var levelHeight = imageHeight;
+ var alignedLevelWidth = (levelWidth + blockWidth - 1) & ~(blockWidth - 1);
+ var alignedLevelHeight = (levelHeight + blockHeight - 1) & ~(blockHeight - 1);
+ var levelSize = alignedLevelWidth * alignedLevelHeight * INTERNAL_FORMAT_TO_BYTES_PER_PIXEL[format];
+ for (var i = 0; i < levels; i++) {
+ buffers[i] = {
+ levelID: i,
+ levelWidth: levels > 1 ? levelWidth : alignedLevelWidth,
+ levelHeight: levels > 1 ? levelHeight : alignedLevelHeight,
+ levelBuffer: new Uint8Array(buffer.buffer, offset, levelSize)
+ };
+ offset += levelSize;
+ // Calculate levelBuffer dimensions for next iteration
+ levelWidth = (levelWidth >> 1) || 1;
+ levelHeight = (levelHeight >> 1) || 1;
+ alignedLevelWidth = (levelWidth + blockWidth - 1) & ~(blockWidth - 1);
+ alignedLevelHeight = (levelHeight + blockHeight - 1) & ~(blockHeight - 1);
+ levelSize = alignedLevelWidth * alignedLevelHeight * INTERNAL_FORMAT_TO_BYTES_PER_PIXEL[format];
+ }
+ return buffers;
+ };
+ return CompressedTextureResource;
+ }(BlobResource));
+
+ /* eslint-enable camelcase */
+ /**
+ * Loader plugin for handling compressed textures for all platforms.
+ * @class
+ * @memberof PIXI
+ * @implements {PIXI.ILoaderPlugin}
+ */
+ var CompressedTextureLoader = /** @class */ (function () {
+ function CompressedTextureLoader() {
+ }
+ /**
+ * Called after a compressed-textures manifest is loaded.
+ *
+ * This will then load the correct compression format for the device. Your manifest should adhere
+ * to the following schema:
+ *
+ * ```js
+ * import { INTERNAL_FORMATS } from '@pixi/constants';
+ *
+ * type CompressedTextureManifest = {
+ * textures: Array<{ src: string, format?: keyof INTERNAL_FORMATS}>,
+ * cacheID: string;
+ * };
+ * ```
+ *
+ * This is an example of a .json manifest file
+ *
+ * ```json
+ * {
+ * "cacheID":"asset",
+ * "textures":[
+ * { "src":"asset.fallback.png" },
+ * { "format":"COMPRESSED_RGBA_S3TC_DXT5_EXT", "src":"asset.s3tc.ktx" },
+ * { "format":"COMPRESSED_RGBA8_ETC2_EAC", "src":"asset.etc.ktx" },
+ * { "format":"RGBA_PVRTC_4BPPV1_IMG", "src":"asset.pvrtc.ktx" }
+ * ]
+ * }
+ * ```
+ */
+ CompressedTextureLoader.use = function (resource, next) {
+ var data = resource.data;
+ var loader = this;
+ if (resource.type === exports.LoaderResource.TYPE.JSON
+ && data
+ && data.cacheID
+ && data.textures) {
+ var textures = data.textures;
+ var textureURL = void 0;
+ var fallbackURL = void 0;
+ // Search for an extension that holds one the formats
+ for (var i = 0, j = textures.length; i < j; i++) {
+ var texture = textures[i];
+ var url_1 = texture.src;
+ var format = texture.format;
+ if (!format) {
+ fallbackURL = url_1;
+ }
+ if (CompressedTextureLoader.textureFormats[format]) {
+ textureURL = url_1;
+ break;
+ }
+ }
+ textureURL = textureURL || fallbackURL;
+ // Make sure we have a URL
+ if (!textureURL) {
+ next(new Error("Cannot load compressed-textures in " + resource.url + ", make sure you provide a fallback"));
+ return;
+ }
+ if (textureURL === resource.url) {
+ // Prevent infinite loops
+ next(new Error('URL of compressed texture cannot be the same as the manifest\'s URL'));
+ return;
+ }
+ var loadOptions = {
+ crossOrigin: resource.crossOrigin,
+ metadata: resource.metadata.imageMetadata,
+ parentResource: resource
+ };
+ var resourcePath = url.resolve(resource.url.replace(loader.baseUrl, ''), textureURL);
+ var resourceName = data.cacheID;
+ // The appropriate loader should register the texture
+ loader.add(resourceName, resourcePath, loadOptions, function (res) {
+ if (res.error) {
+ next(res.error);
+ return;
+ }
+ var _a = res.texture, texture = _a === void 0 ? null : _a, _b = res.textures, textures = _b === void 0 ? {} : _b;
+ // Make sure texture/textures is assigned to parent resource
+ Object.assign(resource, { texture: texture, textures: textures });
+ // Pass along any error
+ next();
+ });
+ }
+ else {
+ next();
+ }
+ };
+ Object.defineProperty(CompressedTextureLoader, "textureExtensions", {
+ /** Map of available texture extensions. */
+ get: function () {
+ if (!CompressedTextureLoader._textureExtensions) {
+ // Auto-detect WebGL compressed-texture extensions
+ var canvas = settings$1.ADAPTER.createCanvas();
+ var gl = canvas.getContext('webgl');
+ if (!gl) {
+ console.warn('WebGL not available for compressed textures. Silently failing.');
+ return {};
+ }
+ var extensions = {
+ s3tc: gl.getExtension('WEBGL_compressed_texture_s3tc'),
+ s3tc_sRGB: gl.getExtension('WEBGL_compressed_texture_s3tc_srgb'),
+ etc: gl.getExtension('WEBGL_compressed_texture_etc'),
+ etc1: gl.getExtension('WEBGL_compressed_texture_etc1'),
+ pvrtc: gl.getExtension('WEBGL_compressed_texture_pvrtc')
+ || gl.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc'),
+ atc: gl.getExtension('WEBGL_compressed_texture_atc'),
+ astc: gl.getExtension('WEBGL_compressed_texture_astc')
+ };
+ CompressedTextureLoader._textureExtensions = extensions;
+ }
+ return CompressedTextureLoader._textureExtensions;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(CompressedTextureLoader, "textureFormats", {
+ /** Map of available texture formats. */
+ get: function () {
+ if (!CompressedTextureLoader._textureFormats) {
+ var extensions = CompressedTextureLoader.textureExtensions;
+ CompressedTextureLoader._textureFormats = {};
+ // Assign all available compressed-texture formats
+ for (var extensionName in extensions) {
+ var extension = extensions[extensionName];
+ if (!extension) {
+ continue;
+ }
+ Object.assign(CompressedTextureLoader._textureFormats, Object.getPrototypeOf(extension));
+ }
+ }
+ return CompressedTextureLoader._textureFormats;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /** @ignore */
+ CompressedTextureLoader.extension = exports.ExtensionType.Loader;
+ return CompressedTextureLoader;
+ }());
+
+ /**
+ * Creates base-textures and textures for each compressed-texture resource and adds them into the global
+ * texture cache. The first texture has two IDs - `${url}`, `${url}-1`; while the rest have an ID of the
+ * form `${url}-i`.
+ * @param url - the original address of the resources
+ * @param resources - the resources backing texture data
+ * @ignore
+ */
+ function registerCompressedTextures(url, resources, metadata) {
+ var result = {
+ textures: {},
+ texture: null,
+ };
+ if (!resources) {
+ return result;
+ }
+ var textures = resources.map(function (resource) {
+ return (new Texture(new BaseTexture(resource, Object.assign({
+ mipmap: exports.MIPMAP_MODES.OFF,
+ alphaMode: exports.ALPHA_MODES.NO_PREMULTIPLIED_ALPHA
+ }, metadata))));
+ });
+ textures.forEach(function (texture, i) {
+ var baseTexture = texture.baseTexture;
+ var cacheID = url + "-" + (i + 1);
+ BaseTexture.addToCache(baseTexture, cacheID);
+ Texture.addToCache(texture, cacheID);
+ if (i === 0) {
+ BaseTexture.addToCache(baseTexture, url);
+ Texture.addToCache(texture, url);
+ result.texture = texture;
+ }
+ result.textures[cacheID] = texture;
+ });
+ return result;
+ }
+
+ var _a$1, _b$1;
+ var DDS_MAGIC_SIZE = 4;
+ var DDS_HEADER_SIZE = 124;
+ var DDS_HEADER_PF_SIZE = 32;
+ var DDS_HEADER_DX10_SIZE = 20;
+ // DDS file format magic word
+ var DDS_MAGIC = 0x20534444;
+ /**
+ * DWORD offsets of the DDS file header fields (relative to file start).
+ * @ignore
+ */
+ var DDS_FIELDS = {
+ SIZE: 1,
+ FLAGS: 2,
+ HEIGHT: 3,
+ WIDTH: 4,
+ MIPMAP_COUNT: 7,
+ PIXEL_FORMAT: 19,
+ };
+ /**
+ * DWORD offsets of the DDS PIXEL_FORMAT fields.
+ * @ignore
+ */
+ var DDS_PF_FIELDS = {
+ SIZE: 0,
+ FLAGS: 1,
+ FOURCC: 2,
+ RGB_BITCOUNT: 3,
+ R_BIT_MASK: 4,
+ G_BIT_MASK: 5,
+ B_BIT_MASK: 6,
+ A_BIT_MASK: 7
+ };
+ /**
+ * DWORD offsets of the DDS_HEADER_DX10 fields.
+ * @ignore
+ */
+ var DDS_DX10_FIELDS = {
+ DXGI_FORMAT: 0,
+ RESOURCE_DIMENSION: 1,
+ MISC_FLAG: 2,
+ ARRAY_SIZE: 3,
+ MISC_FLAGS2: 4
+ };
+ /**
+ * @see https://docs.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format
+ * @ignore
+ */
+ // This is way over-blown for us! Lend us a hand, and remove the ones that aren't used (but set the remaining
+ // ones to their correct value)
+ var DXGI_FORMAT;
+ (function (DXGI_FORMAT) {
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_UNKNOWN"] = 0] = "DXGI_FORMAT_UNKNOWN";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R32G32B32A32_TYPELESS"] = 1] = "DXGI_FORMAT_R32G32B32A32_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R32G32B32A32_FLOAT"] = 2] = "DXGI_FORMAT_R32G32B32A32_FLOAT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R32G32B32A32_UINT"] = 3] = "DXGI_FORMAT_R32G32B32A32_UINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R32G32B32A32_SINT"] = 4] = "DXGI_FORMAT_R32G32B32A32_SINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R32G32B32_TYPELESS"] = 5] = "DXGI_FORMAT_R32G32B32_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R32G32B32_FLOAT"] = 6] = "DXGI_FORMAT_R32G32B32_FLOAT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R32G32B32_UINT"] = 7] = "DXGI_FORMAT_R32G32B32_UINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R32G32B32_SINT"] = 8] = "DXGI_FORMAT_R32G32B32_SINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R16G16B16A16_TYPELESS"] = 9] = "DXGI_FORMAT_R16G16B16A16_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R16G16B16A16_FLOAT"] = 10] = "DXGI_FORMAT_R16G16B16A16_FLOAT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R16G16B16A16_UNORM"] = 11] = "DXGI_FORMAT_R16G16B16A16_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R16G16B16A16_UINT"] = 12] = "DXGI_FORMAT_R16G16B16A16_UINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R16G16B16A16_SNORM"] = 13] = "DXGI_FORMAT_R16G16B16A16_SNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R16G16B16A16_SINT"] = 14] = "DXGI_FORMAT_R16G16B16A16_SINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R32G32_TYPELESS"] = 15] = "DXGI_FORMAT_R32G32_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R32G32_FLOAT"] = 16] = "DXGI_FORMAT_R32G32_FLOAT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R32G32_UINT"] = 17] = "DXGI_FORMAT_R32G32_UINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R32G32_SINT"] = 18] = "DXGI_FORMAT_R32G32_SINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R32G8X24_TYPELESS"] = 19] = "DXGI_FORMAT_R32G8X24_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_D32_FLOAT_S8X24_UINT"] = 20] = "DXGI_FORMAT_D32_FLOAT_S8X24_UINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS"] = 21] = "DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_X32_TYPELESS_G8X24_UINT"] = 22] = "DXGI_FORMAT_X32_TYPELESS_G8X24_UINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R10G10B10A2_TYPELESS"] = 23] = "DXGI_FORMAT_R10G10B10A2_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R10G10B10A2_UNORM"] = 24] = "DXGI_FORMAT_R10G10B10A2_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R10G10B10A2_UINT"] = 25] = "DXGI_FORMAT_R10G10B10A2_UINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R11G11B10_FLOAT"] = 26] = "DXGI_FORMAT_R11G11B10_FLOAT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R8G8B8A8_TYPELESS"] = 27] = "DXGI_FORMAT_R8G8B8A8_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R8G8B8A8_UNORM"] = 28] = "DXGI_FORMAT_R8G8B8A8_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R8G8B8A8_UNORM_SRGB"] = 29] = "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R8G8B8A8_UINT"] = 30] = "DXGI_FORMAT_R8G8B8A8_UINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R8G8B8A8_SNORM"] = 31] = "DXGI_FORMAT_R8G8B8A8_SNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R8G8B8A8_SINT"] = 32] = "DXGI_FORMAT_R8G8B8A8_SINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R16G16_TYPELESS"] = 33] = "DXGI_FORMAT_R16G16_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R16G16_FLOAT"] = 34] = "DXGI_FORMAT_R16G16_FLOAT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R16G16_UNORM"] = 35] = "DXGI_FORMAT_R16G16_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R16G16_UINT"] = 36] = "DXGI_FORMAT_R16G16_UINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R16G16_SNORM"] = 37] = "DXGI_FORMAT_R16G16_SNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R16G16_SINT"] = 38] = "DXGI_FORMAT_R16G16_SINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R32_TYPELESS"] = 39] = "DXGI_FORMAT_R32_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_D32_FLOAT"] = 40] = "DXGI_FORMAT_D32_FLOAT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R32_FLOAT"] = 41] = "DXGI_FORMAT_R32_FLOAT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R32_UINT"] = 42] = "DXGI_FORMAT_R32_UINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R32_SINT"] = 43] = "DXGI_FORMAT_R32_SINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R24G8_TYPELESS"] = 44] = "DXGI_FORMAT_R24G8_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_D24_UNORM_S8_UINT"] = 45] = "DXGI_FORMAT_D24_UNORM_S8_UINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R24_UNORM_X8_TYPELESS"] = 46] = "DXGI_FORMAT_R24_UNORM_X8_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_X24_TYPELESS_G8_UINT"] = 47] = "DXGI_FORMAT_X24_TYPELESS_G8_UINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R8G8_TYPELESS"] = 48] = "DXGI_FORMAT_R8G8_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R8G8_UNORM"] = 49] = "DXGI_FORMAT_R8G8_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R8G8_UINT"] = 50] = "DXGI_FORMAT_R8G8_UINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R8G8_SNORM"] = 51] = "DXGI_FORMAT_R8G8_SNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R8G8_SINT"] = 52] = "DXGI_FORMAT_R8G8_SINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R16_TYPELESS"] = 53] = "DXGI_FORMAT_R16_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R16_FLOAT"] = 54] = "DXGI_FORMAT_R16_FLOAT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_D16_UNORM"] = 55] = "DXGI_FORMAT_D16_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R16_UNORM"] = 56] = "DXGI_FORMAT_R16_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R16_UINT"] = 57] = "DXGI_FORMAT_R16_UINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R16_SNORM"] = 58] = "DXGI_FORMAT_R16_SNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R16_SINT"] = 59] = "DXGI_FORMAT_R16_SINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R8_TYPELESS"] = 60] = "DXGI_FORMAT_R8_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R8_UNORM"] = 61] = "DXGI_FORMAT_R8_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R8_UINT"] = 62] = "DXGI_FORMAT_R8_UINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R8_SNORM"] = 63] = "DXGI_FORMAT_R8_SNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R8_SINT"] = 64] = "DXGI_FORMAT_R8_SINT";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_A8_UNORM"] = 65] = "DXGI_FORMAT_A8_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R1_UNORM"] = 66] = "DXGI_FORMAT_R1_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R9G9B9E5_SHAREDEXP"] = 67] = "DXGI_FORMAT_R9G9B9E5_SHAREDEXP";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R8G8_B8G8_UNORM"] = 68] = "DXGI_FORMAT_R8G8_B8G8_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_G8R8_G8B8_UNORM"] = 69] = "DXGI_FORMAT_G8R8_G8B8_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_BC1_TYPELESS"] = 70] = "DXGI_FORMAT_BC1_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_BC1_UNORM"] = 71] = "DXGI_FORMAT_BC1_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_BC1_UNORM_SRGB"] = 72] = "DXGI_FORMAT_BC1_UNORM_SRGB";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_BC2_TYPELESS"] = 73] = "DXGI_FORMAT_BC2_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_BC2_UNORM"] = 74] = "DXGI_FORMAT_BC2_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_BC2_UNORM_SRGB"] = 75] = "DXGI_FORMAT_BC2_UNORM_SRGB";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_BC3_TYPELESS"] = 76] = "DXGI_FORMAT_BC3_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_BC3_UNORM"] = 77] = "DXGI_FORMAT_BC3_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_BC3_UNORM_SRGB"] = 78] = "DXGI_FORMAT_BC3_UNORM_SRGB";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_BC4_TYPELESS"] = 79] = "DXGI_FORMAT_BC4_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_BC4_UNORM"] = 80] = "DXGI_FORMAT_BC4_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_BC4_SNORM"] = 81] = "DXGI_FORMAT_BC4_SNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_BC5_TYPELESS"] = 82] = "DXGI_FORMAT_BC5_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_BC5_UNORM"] = 83] = "DXGI_FORMAT_BC5_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_BC5_SNORM"] = 84] = "DXGI_FORMAT_BC5_SNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_B5G6R5_UNORM"] = 85] = "DXGI_FORMAT_B5G6R5_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_B5G5R5A1_UNORM"] = 86] = "DXGI_FORMAT_B5G5R5A1_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_B8G8R8A8_UNORM"] = 87] = "DXGI_FORMAT_B8G8R8A8_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_B8G8R8X8_UNORM"] = 88] = "DXGI_FORMAT_B8G8R8X8_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM"] = 89] = "DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_B8G8R8A8_TYPELESS"] = 90] = "DXGI_FORMAT_B8G8R8A8_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_B8G8R8A8_UNORM_SRGB"] = 91] = "DXGI_FORMAT_B8G8R8A8_UNORM_SRGB";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_B8G8R8X8_TYPELESS"] = 92] = "DXGI_FORMAT_B8G8R8X8_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_B8G8R8X8_UNORM_SRGB"] = 93] = "DXGI_FORMAT_B8G8R8X8_UNORM_SRGB";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_BC6H_TYPELESS"] = 94] = "DXGI_FORMAT_BC6H_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_BC6H_UF16"] = 95] = "DXGI_FORMAT_BC6H_UF16";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_BC6H_SF16"] = 96] = "DXGI_FORMAT_BC6H_SF16";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_BC7_TYPELESS"] = 97] = "DXGI_FORMAT_BC7_TYPELESS";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_BC7_UNORM"] = 98] = "DXGI_FORMAT_BC7_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_BC7_UNORM_SRGB"] = 99] = "DXGI_FORMAT_BC7_UNORM_SRGB";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_AYUV"] = 100] = "DXGI_FORMAT_AYUV";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_Y410"] = 101] = "DXGI_FORMAT_Y410";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_Y416"] = 102] = "DXGI_FORMAT_Y416";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_NV12"] = 103] = "DXGI_FORMAT_NV12";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_P010"] = 104] = "DXGI_FORMAT_P010";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_P016"] = 105] = "DXGI_FORMAT_P016";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_420_OPAQUE"] = 106] = "DXGI_FORMAT_420_OPAQUE";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_YUY2"] = 107] = "DXGI_FORMAT_YUY2";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_Y210"] = 108] = "DXGI_FORMAT_Y210";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_Y216"] = 109] = "DXGI_FORMAT_Y216";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_NV11"] = 110] = "DXGI_FORMAT_NV11";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_AI44"] = 111] = "DXGI_FORMAT_AI44";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_IA44"] = 112] = "DXGI_FORMAT_IA44";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_P8"] = 113] = "DXGI_FORMAT_P8";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_A8P8"] = 114] = "DXGI_FORMAT_A8P8";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_B4G4R4A4_UNORM"] = 115] = "DXGI_FORMAT_B4G4R4A4_UNORM";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_P208"] = 116] = "DXGI_FORMAT_P208";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_V208"] = 117] = "DXGI_FORMAT_V208";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_V408"] = 118] = "DXGI_FORMAT_V408";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_SAMPLER_FEEDBACK_MIN_MIP_OPAQUE"] = 119] = "DXGI_FORMAT_SAMPLER_FEEDBACK_MIN_MIP_OPAQUE";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE"] = 120] = "DXGI_FORMAT_SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE";
+ DXGI_FORMAT[DXGI_FORMAT["DXGI_FORMAT_FORCE_UINT"] = 121] = "DXGI_FORMAT_FORCE_UINT";
+ })(DXGI_FORMAT || (DXGI_FORMAT = {}));
+ /**
+ * Possible values of the field {@link DDS_DX10_FIELDS.RESOURCE_DIMENSION}
+ * @ignore
+ */
+ var D3D10_RESOURCE_DIMENSION;
+ (function (D3D10_RESOURCE_DIMENSION) {
+ D3D10_RESOURCE_DIMENSION[D3D10_RESOURCE_DIMENSION["DDS_DIMENSION_TEXTURE1D"] = 2] = "DDS_DIMENSION_TEXTURE1D";
+ D3D10_RESOURCE_DIMENSION[D3D10_RESOURCE_DIMENSION["DDS_DIMENSION_TEXTURE2D"] = 3] = "DDS_DIMENSION_TEXTURE2D";
+ D3D10_RESOURCE_DIMENSION[D3D10_RESOURCE_DIMENSION["DDS_DIMENSION_TEXTURE3D"] = 6] = "DDS_DIMENSION_TEXTURE3D";
+ })(D3D10_RESOURCE_DIMENSION || (D3D10_RESOURCE_DIMENSION = {}));
+ var PF_FLAGS = 1;
+ // PIXEL_FORMAT flags
+ var DDPF_ALPHA = 0x2;
+ var DDPF_FOURCC = 0x4;
+ var DDPF_RGB = 0x40;
+ var DDPF_YUV = 0x200;
+ var DDPF_LUMINANCE = 0x20000;
+ // Four character codes for DXTn formats
+ var FOURCC_DXT1 = 0x31545844;
+ var FOURCC_DXT3 = 0x33545844;
+ var FOURCC_DXT5 = 0x35545844;
+ var FOURCC_DX10 = 0x30315844;
+ // Cubemap texture flag (for DDS_DX10_FIELDS.MISC_FLAG)
+ var DDS_RESOURCE_MISC_TEXTURECUBE = 0x4;
+ /**
+ * Maps `FOURCC_*` formats to internal formats (see {@link PIXI.INTERNAL_FORMATS}).
+ * @ignore
+ */
+ var FOURCC_TO_FORMAT = (_a$1 = {},
+ _a$1[FOURCC_DXT1] = exports.INTERNAL_FORMATS.COMPRESSED_RGBA_S3TC_DXT1_EXT,
+ _a$1[FOURCC_DXT3] = exports.INTERNAL_FORMATS.COMPRESSED_RGBA_S3TC_DXT3_EXT,
+ _a$1[FOURCC_DXT5] = exports.INTERNAL_FORMATS.COMPRESSED_RGBA_S3TC_DXT5_EXT,
+ _a$1);
+ /**
+ * Maps {@link DXGI_FORMAT} to types/internal-formats (see {@link PIXI.TYPES}, {@link PIXI.INTERNAL_FORMATS})
+ * @ignore
+ */
+ var DXGI_TO_FORMAT = (_b$1 = {},
+ // WEBGL_compressed_texture_s3tc
+ _b$1[DXGI_FORMAT.DXGI_FORMAT_BC1_TYPELESS] = exports.INTERNAL_FORMATS.COMPRESSED_RGBA_S3TC_DXT1_EXT,
+ _b$1[DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM] = exports.INTERNAL_FORMATS.COMPRESSED_RGBA_S3TC_DXT1_EXT,
+ _b$1[DXGI_FORMAT.DXGI_FORMAT_BC2_TYPELESS] = exports.INTERNAL_FORMATS.COMPRESSED_RGBA_S3TC_DXT3_EXT,
+ _b$1[DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM] = exports.INTERNAL_FORMATS.COMPRESSED_RGBA_S3TC_DXT3_EXT,
+ _b$1[DXGI_FORMAT.DXGI_FORMAT_BC3_TYPELESS] = exports.INTERNAL_FORMATS.COMPRESSED_RGBA_S3TC_DXT5_EXT,
+ _b$1[DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM] = exports.INTERNAL_FORMATS.COMPRESSED_RGBA_S3TC_DXT5_EXT,
+ // WEBGL_compressed_texture_s3tc_srgb
+ _b$1[DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM_SRGB] = exports.INTERNAL_FORMATS.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT,
+ _b$1[DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM_SRGB] = exports.INTERNAL_FORMATS.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT,
+ _b$1[DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM_SRGB] = exports.INTERNAL_FORMATS.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT,
+ _b$1);
+ /**
+ * @class
+ * @memberof PIXI
+ * @implements {PIXI.ILoaderPlugin}
+ * @see https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-pguide
+ */
+ /**
+ * Parses the DDS file header, generates base-textures, and puts them into the texture cache.
+ * @param arrayBuffer
+ */
+ function parseDDS(arrayBuffer) {
+ var data = new Uint32Array(arrayBuffer);
+ var magicWord = data[0];
+ if (magicWord !== DDS_MAGIC) {
+ throw new Error('Invalid DDS file magic word');
+ }
+ var header = new Uint32Array(arrayBuffer, 0, DDS_HEADER_SIZE / Uint32Array.BYTES_PER_ELEMENT);
+ // DDS header fields
+ var height = header[DDS_FIELDS.HEIGHT];
+ var width = header[DDS_FIELDS.WIDTH];
+ var mipmapCount = header[DDS_FIELDS.MIPMAP_COUNT];
+ // PIXEL_FORMAT fields
+ var pixelFormat = new Uint32Array(arrayBuffer, DDS_FIELDS.PIXEL_FORMAT * Uint32Array.BYTES_PER_ELEMENT, DDS_HEADER_PF_SIZE / Uint32Array.BYTES_PER_ELEMENT);
+ var formatFlags = pixelFormat[PF_FLAGS];
+ // File contains compressed texture(s)
+ if (formatFlags & DDPF_FOURCC) {
+ var fourCC = pixelFormat[DDS_PF_FIELDS.FOURCC];
+ // File contains one DXTn compressed texture
+ if (fourCC !== FOURCC_DX10) {
+ var internalFormat_1 = FOURCC_TO_FORMAT[fourCC];
+ var dataOffset_1 = DDS_MAGIC_SIZE + DDS_HEADER_SIZE;
+ var texData = new Uint8Array(arrayBuffer, dataOffset_1);
+ var resource = new CompressedTextureResource(texData, {
+ format: internalFormat_1,
+ width: width,
+ height: height,
+ levels: mipmapCount // CompressedTextureResource will separate the levelBuffers for us!
+ });
+ return [resource];
+ }
+ // FOURCC_DX10 indicates there is a 20-byte DDS_HEADER_DX10 after DDS_HEADER
+ var dx10Offset = DDS_MAGIC_SIZE + DDS_HEADER_SIZE;
+ var dx10Header = new Uint32Array(data.buffer, dx10Offset, DDS_HEADER_DX10_SIZE / Uint32Array.BYTES_PER_ELEMENT);
+ var dxgiFormat = dx10Header[DDS_DX10_FIELDS.DXGI_FORMAT];
+ var resourceDimension = dx10Header[DDS_DX10_FIELDS.RESOURCE_DIMENSION];
+ var miscFlag = dx10Header[DDS_DX10_FIELDS.MISC_FLAG];
+ var arraySize = dx10Header[DDS_DX10_FIELDS.ARRAY_SIZE];
+ // Map dxgiFormat to PIXI.INTERNAL_FORMATS
+ var internalFormat_2 = DXGI_TO_FORMAT[dxgiFormat];
+ if (internalFormat_2 === undefined) {
+ throw new Error("DDSParser cannot parse texture data with DXGI format " + dxgiFormat);
+ }
+ if (miscFlag === DDS_RESOURCE_MISC_TEXTURECUBE) {
+ // FIXME: Anybody excited about cubemap compressed textures?
+ throw new Error('DDSParser does not support cubemap textures');
+ }
+ if (resourceDimension === D3D10_RESOURCE_DIMENSION.DDS_DIMENSION_TEXTURE3D) {
+ // FIXME: Anybody excited about 3D compressed textures?
+ throw new Error('DDSParser does not supported 3D texture data');
+ }
+ // Uint8Array buffers of image data, including all mipmap levels in each image
+ var imageBuffers = new Array();
+ var dataOffset = DDS_MAGIC_SIZE
+ + DDS_HEADER_SIZE
+ + DDS_HEADER_DX10_SIZE;
+ if (arraySize === 1) {
+ // No need bothering with the imageSize calculation!
+ imageBuffers.push(new Uint8Array(arrayBuffer, dataOffset));
+ }
+ else {
+ // Calculate imageSize for each texture, and then locate each image's texture data
+ var pixelSize = INTERNAL_FORMAT_TO_BYTES_PER_PIXEL[internalFormat_2];
+ var imageSize = 0;
+ var levelWidth = width;
+ var levelHeight = height;
+ for (var i = 0; i < mipmapCount; i++) {
+ var alignedLevelWidth = Math.max(1, (levelWidth + 3) & ~3);
+ var alignedLevelHeight = Math.max(1, (levelHeight + 3) & ~3);
+ var levelSize = alignedLevelWidth * alignedLevelHeight * pixelSize;
+ imageSize += levelSize;
+ levelWidth = levelWidth >>> 1;
+ levelHeight = levelHeight >>> 1;
+ }
+ var imageOffset = dataOffset;
+ // NOTE: Cubemaps have 6-images per texture (but they aren't supported so ^_^)
+ for (var i = 0; i < arraySize; i++) {
+ imageBuffers.push(new Uint8Array(arrayBuffer, imageOffset, imageSize));
+ imageOffset += imageSize;
+ }
+ }
+ // Uint8Array -> CompressedTextureResource, and we're done!
+ return imageBuffers.map(function (buffer) { return new CompressedTextureResource(buffer, {
+ format: internalFormat_2,
+ width: width,
+ height: height,
+ levels: mipmapCount
+ }); });
+ }
+ if (formatFlags & DDPF_RGB) {
+ // FIXME: We might want to allow uncompressed *.dds files?
+ throw new Error('DDSParser does not support uncompressed texture data.');
+ }
+ if (formatFlags & DDPF_YUV) {
+ // FIXME: Does anybody need this feature?
+ throw new Error('DDSParser does not supported YUV uncompressed texture data.');
+ }
+ if (formatFlags & DDPF_LUMINANCE) {
+ // FIXME: Microsoft says older DDS filers use this feature! Probably not worth the effort!
+ throw new Error('DDSParser does not support single-channel (lumninance) texture data!');
+ }
+ if (formatFlags & DDPF_ALPHA) {
+ // FIXME: I'm tired! See above =)
+ throw new Error('DDSParser does not support single-channel (alpha) texture data!');
+ }
+ throw new Error('DDSParser failed to load a texture file due to an unknown reason!');
+ }
+
+ var _a$3, _b, _c;
+ /**
+ * The 12-byte KTX file identifier
+ * @see https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/#2.1
+ * @ignore
+ */
+ var FILE_IDENTIFIER = [0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A];
+ /**
+ * The value stored in the "endianness" field.
+ * @see https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/#2.2
+ * @ignore
+ */
+ var ENDIANNESS = 0x04030201;
+ /**
+ * Byte offsets of the KTX file header fields
+ * @ignore
+ */
+ var KTX_FIELDS = {
+ FILE_IDENTIFIER: 0,
+ ENDIANNESS: 12,
+ GL_TYPE: 16,
+ GL_TYPE_SIZE: 20,
+ GL_FORMAT: 24,
+ GL_INTERNAL_FORMAT: 28,
+ GL_BASE_INTERNAL_FORMAT: 32,
+ PIXEL_WIDTH: 36,
+ PIXEL_HEIGHT: 40,
+ PIXEL_DEPTH: 44,
+ NUMBER_OF_ARRAY_ELEMENTS: 48,
+ NUMBER_OF_FACES: 52,
+ NUMBER_OF_MIPMAP_LEVELS: 56,
+ BYTES_OF_KEY_VALUE_DATA: 60
+ };
+ /**
+ * Byte size of the file header fields in {@code KTX_FIELDS}
+ * @ignore
+ */
+ var FILE_HEADER_SIZE = 64;
+ /**
+ * Maps {@link PIXI.TYPES} to the bytes taken per component, excluding those ones that are bit-fields.
+ * @ignore
+ */
+ var TYPES_TO_BYTES_PER_COMPONENT = (_a$3 = {},
+ _a$3[exports.TYPES.UNSIGNED_BYTE] = 1,
+ _a$3[exports.TYPES.UNSIGNED_SHORT] = 2,
+ _a$3[exports.TYPES.INT] = 4,
+ _a$3[exports.TYPES.UNSIGNED_INT] = 4,
+ _a$3[exports.TYPES.FLOAT] = 4,
+ _a$3[exports.TYPES.HALF_FLOAT] = 8,
+ _a$3);
+ /**
+ * Number of components in each {@link PIXI.FORMATS}
+ * @ignore
+ */
+ var FORMATS_TO_COMPONENTS = (_b = {},
+ _b[exports.FORMATS.RGBA] = 4,
+ _b[exports.FORMATS.RGB] = 3,
+ _b[exports.FORMATS.RG] = 2,
+ _b[exports.FORMATS.RED] = 1,
+ _b[exports.FORMATS.LUMINANCE] = 1,
+ _b[exports.FORMATS.LUMINANCE_ALPHA] = 2,
+ _b[exports.FORMATS.ALPHA] = 1,
+ _b);
+ /**
+ * Number of bytes per pixel in bit-field types in {@link PIXI.TYPES}
+ * @ignore
+ */
+ var TYPES_TO_BYTES_PER_PIXEL = (_c = {},
+ _c[exports.TYPES.UNSIGNED_SHORT_4_4_4_4] = 2,
+ _c[exports.TYPES.UNSIGNED_SHORT_5_5_5_1] = 2,
+ _c[exports.TYPES.UNSIGNED_SHORT_5_6_5] = 2,
+ _c);
+ function parseKTX(url, arrayBuffer, loadKeyValueData) {
+ if (loadKeyValueData === void 0) { loadKeyValueData = false; }
+ var dataView = new DataView(arrayBuffer);
+ if (!validate(url, dataView)) {
+ return null;
+ }
+ var littleEndian = dataView.getUint32(KTX_FIELDS.ENDIANNESS, true) === ENDIANNESS;
+ var glType = dataView.getUint32(KTX_FIELDS.GL_TYPE, littleEndian);
+ // const glTypeSize = dataView.getUint32(KTX_FIELDS.GL_TYPE_SIZE, littleEndian);
+ var glFormat = dataView.getUint32(KTX_FIELDS.GL_FORMAT, littleEndian);
+ var glInternalFormat = dataView.getUint32(KTX_FIELDS.GL_INTERNAL_FORMAT, littleEndian);
+ var pixelWidth = dataView.getUint32(KTX_FIELDS.PIXEL_WIDTH, littleEndian);
+ var pixelHeight = dataView.getUint32(KTX_FIELDS.PIXEL_HEIGHT, littleEndian) || 1; // "pixelHeight = 0" -> "1"
+ var pixelDepth = dataView.getUint32(KTX_FIELDS.PIXEL_DEPTH, littleEndian) || 1; // ^^
+ var numberOfArrayElements = dataView.getUint32(KTX_FIELDS.NUMBER_OF_ARRAY_ELEMENTS, littleEndian) || 1; // ^^
+ var numberOfFaces = dataView.getUint32(KTX_FIELDS.NUMBER_OF_FACES, littleEndian);
+ var numberOfMipmapLevels = dataView.getUint32(KTX_FIELDS.NUMBER_OF_MIPMAP_LEVELS, littleEndian);
+ var bytesOfKeyValueData = dataView.getUint32(KTX_FIELDS.BYTES_OF_KEY_VALUE_DATA, littleEndian);
+ // Whether the platform architecture is little endian. If littleEndian !== platformLittleEndian, then the
+ // file contents must be endian-converted!
+ // TODO: Endianness conversion
+ // const platformLittleEndian = new Uint8Array((new Uint32Array([ENDIANNESS])).buffer)[0] === 0x01;
+ if (pixelHeight === 0 || pixelDepth !== 1) {
+ throw new Error('Only 2D textures are supported');
+ }
+ if (numberOfFaces !== 1) {
+ throw new Error('CubeTextures are not supported by KTXLoader yet!');
+ }
+ if (numberOfArrayElements !== 1) {
+ // TODO: Support splitting array-textures into multiple BaseTextures
+ throw new Error('WebGL does not support array textures');
+ }
+ // TODO: 8x4 blocks for 2bpp pvrtc
+ var blockWidth = 4;
+ var blockHeight = 4;
+ var alignedWidth = (pixelWidth + 3) & ~3;
+ var alignedHeight = (pixelHeight + 3) & ~3;
+ var imageBuffers = new Array(numberOfArrayElements);
+ var imagePixels = pixelWidth * pixelHeight;
+ if (glType === 0) {
+ // Align to 16 pixels (4x4 blocks)
+ imagePixels = alignedWidth * alignedHeight;
+ }
+ var imagePixelByteSize;
+ if (glType !== 0) {
+ // Uncompressed texture format
+ if (TYPES_TO_BYTES_PER_COMPONENT[glType]) {
+ imagePixelByteSize = TYPES_TO_BYTES_PER_COMPONENT[glType] * FORMATS_TO_COMPONENTS[glFormat];
+ }
+ else {
+ imagePixelByteSize = TYPES_TO_BYTES_PER_PIXEL[glType];
+ }
+ }
+ else {
+ imagePixelByteSize = INTERNAL_FORMAT_TO_BYTES_PER_PIXEL[glInternalFormat];
+ }
+ if (imagePixelByteSize === undefined) {
+ throw new Error('Unable to resolve the pixel format stored in the *.ktx file!');
+ }
+ var kvData = loadKeyValueData
+ ? parseKvData(dataView, bytesOfKeyValueData, littleEndian)
+ : null;
+ var imageByteSize = imagePixels * imagePixelByteSize;
+ var mipByteSize = imageByteSize;
+ var mipWidth = pixelWidth;
+ var mipHeight = pixelHeight;
+ var alignedMipWidth = alignedWidth;
+ var alignedMipHeight = alignedHeight;
+ var imageOffset = FILE_HEADER_SIZE + bytesOfKeyValueData;
+ for (var mipmapLevel = 0; mipmapLevel < numberOfMipmapLevels; mipmapLevel++) {
+ var imageSize = dataView.getUint32(imageOffset, littleEndian);
+ var elementOffset = imageOffset + 4;
+ for (var arrayElement = 0; arrayElement < numberOfArrayElements; arrayElement++) {
+ // TODO: Maybe support 3D textures? :-)
+ // for (let zSlice = 0; zSlice < pixelDepth; zSlice)
+ var mips = imageBuffers[arrayElement];
+ if (!mips) {
+ mips = imageBuffers[arrayElement] = new Array(numberOfMipmapLevels);
+ }
+ mips[mipmapLevel] = {
+ levelID: mipmapLevel,
+ // don't align mipWidth when texture not compressed! (glType not zero)
+ levelWidth: numberOfMipmapLevels > 1 || glType !== 0 ? mipWidth : alignedMipWidth,
+ levelHeight: numberOfMipmapLevels > 1 || glType !== 0 ? mipHeight : alignedMipHeight,
+ levelBuffer: new Uint8Array(arrayBuffer, elementOffset, mipByteSize)
+ };
+ elementOffset += mipByteSize;
+ }
+ // HINT: Aligns to 4-byte boundary after jumping imageSize (in lieu of mipPadding)
+ imageOffset += imageSize + 4; // (+4 to jump the imageSize field itself)
+ imageOffset = imageOffset % 4 !== 0 ? imageOffset + 4 - (imageOffset % 4) : imageOffset;
+ // Calculate mipWidth, mipHeight for _next_ iteration
+ mipWidth = (mipWidth >> 1) || 1;
+ mipHeight = (mipHeight >> 1) || 1;
+ alignedMipWidth = (mipWidth + blockWidth - 1) & ~(blockWidth - 1);
+ alignedMipHeight = (mipHeight + blockHeight - 1) & ~(blockHeight - 1);
+ // Each mipmap level is 4-times smaller?
+ mipByteSize = alignedMipWidth * alignedMipHeight * imagePixelByteSize;
+ }
+ // We use the levelBuffers feature of CompressedTextureResource b/c texture data is image-major, not level-major.
+ if (glType !== 0) {
+ return {
+ uncompressed: imageBuffers.map(function (levelBuffers) {
+ var buffer = levelBuffers[0].levelBuffer;
+ var convertToInt = false;
+ if (glType === exports.TYPES.FLOAT) {
+ buffer = new Float32Array(levelBuffers[0].levelBuffer.buffer, levelBuffers[0].levelBuffer.byteOffset, levelBuffers[0].levelBuffer.byteLength / 4);
+ }
+ else if (glType === exports.TYPES.UNSIGNED_INT) {
+ convertToInt = true;
+ buffer = new Uint32Array(levelBuffers[0].levelBuffer.buffer, levelBuffers[0].levelBuffer.byteOffset, levelBuffers[0].levelBuffer.byteLength / 4);
+ }
+ else if (glType === exports.TYPES.INT) {
+ convertToInt = true;
+ buffer = new Int32Array(levelBuffers[0].levelBuffer.buffer, levelBuffers[0].levelBuffer.byteOffset, levelBuffers[0].levelBuffer.byteLength / 4);
+ }
+ return {
+ resource: new BufferResource(buffer, {
+ width: levelBuffers[0].levelWidth,
+ height: levelBuffers[0].levelHeight,
+ }),
+ type: glType,
+ format: convertToInt ? convertFormatToInteger(glFormat) : glFormat,
+ };
+ }),
+ kvData: kvData
+ };
+ }
+ return {
+ compressed: imageBuffers.map(function (levelBuffers) { return new CompressedTextureResource(null, {
+ format: glInternalFormat,
+ width: pixelWidth,
+ height: pixelHeight,
+ levels: numberOfMipmapLevels,
+ levelBuffers: levelBuffers,
+ }); }),
+ kvData: kvData
+ };
+ }
+ /**
+ * Checks whether the arrayBuffer contains a valid *.ktx file.
+ * @param url
+ * @param dataView
+ */
+ function validate(url, dataView) {
+ // NOTE: Do not optimize this into 3 32-bit integer comparison because the endianness
+ // of the data is not specified.
+ for (var i = 0; i < FILE_IDENTIFIER.length; i++) {
+ if (dataView.getUint8(i) !== FILE_IDENTIFIER[i]) {
+ console.error(url + " is not a valid *.ktx file!");
+ return false;
+ }
+ }
+ return true;
+ }
+ function convertFormatToInteger(format) {
+ switch (format) {
+ case exports.FORMATS.RGBA: return exports.FORMATS.RGBA_INTEGER;
+ case exports.FORMATS.RGB: return exports.FORMATS.RGB_INTEGER;
+ case exports.FORMATS.RG: return exports.FORMATS.RG_INTEGER;
+ case exports.FORMATS.RED: return exports.FORMATS.RED_INTEGER;
+ default: return format;
+ }
+ }
+ function parseKvData(dataView, bytesOfKeyValueData, littleEndian) {
+ var kvData = new Map();
+ var bytesIntoKeyValueData = 0;
+ while (bytesIntoKeyValueData < bytesOfKeyValueData) {
+ var keyAndValueByteSize = dataView.getUint32(FILE_HEADER_SIZE + bytesIntoKeyValueData, littleEndian);
+ var keyAndValueByteOffset = FILE_HEADER_SIZE + bytesIntoKeyValueData + 4;
+ var valuePadding = 3 - ((keyAndValueByteSize + 3) % 4);
+ // Bounds check
+ if (keyAndValueByteSize === 0 || keyAndValueByteSize > bytesOfKeyValueData - bytesIntoKeyValueData) {
+ console.error('KTXLoader: keyAndValueByteSize out of bounds');
+ break;
+ }
+ // Note: keyNulByte can't be 0 otherwise the key is an empty string.
+ var keyNulByte = 0;
+ for (; keyNulByte < keyAndValueByteSize; keyNulByte++) {
+ if (dataView.getUint8(keyAndValueByteOffset + keyNulByte) === 0x00) {
+ break;
+ }
+ }
+ if (keyNulByte === -1) {
+ console.error('KTXLoader: Failed to find null byte terminating kvData key');
+ break;
+ }
+ var key = new TextDecoder().decode(new Uint8Array(dataView.buffer, keyAndValueByteOffset, keyNulByte));
+ var value = new DataView(dataView.buffer, keyAndValueByteOffset + keyNulByte + 1, keyAndValueByteSize - keyNulByte - 1);
+ kvData.set(key, value);
+ // 4 = the keyAndValueByteSize field itself
+ // keyAndValueByteSize = the bytes taken by the key and value
+ // valuePadding = extra padding to align with 4 bytes
+ bytesIntoKeyValueData += 4 + keyAndValueByteSize + valuePadding;
+ }
+ return kvData;
+ }
+
+ // Set DDS files to be loaded as an ArrayBuffer
+ exports.LoaderResource.setExtensionXhrType('dds', exports.LoaderResource.XHR_RESPONSE_TYPE.BUFFER);
+ /**
+ * @class
+ * @memberof PIXI
+ * @implements {PIXI.ILoaderPlugin}
+ * @see https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-pguide
+ */
+ var DDSLoader = /** @class */ (function () {
+ function DDSLoader() {
+ }
+ /**
+ * Registers a DDS compressed texture
+ * @see PIXI.Loader.loaderMiddleware
+ * @param resource - loader resource that is checked to see if it is a DDS file
+ * @param next - callback Function to call when done
+ */
+ DDSLoader.use = function (resource, next) {
+ if (resource.extension === 'dds' && resource.data) {
+ try {
+ Object.assign(resource, registerCompressedTextures(resource.name || resource.url, parseDDS(resource.data), resource.metadata));
+ }
+ catch (err) {
+ next(err);
+ return;
+ }
+ }
+ next();
+ };
+ /** @ignore */
+ DDSLoader.extension = exports.ExtensionType.Loader;
+ return DDSLoader;
+ }());
+
+ // Set KTX files to be loaded as an ArrayBuffer
+ exports.LoaderResource.setExtensionXhrType('ktx', exports.LoaderResource.XHR_RESPONSE_TYPE.BUFFER);
+ /**
+ * Loader plugin for handling KTX texture container files.
+ *
+ * This KTX loader does not currently support the following features:
+ * * cube textures
+ * * 3D textures
+ * * endianness conversion for big-endian machines
+ * * embedded *.basis files
+ *
+ * It does supports the following features:
+ * * multiple textures per file
+ * * mipmapping (only for compressed formats)
+ * * vendor-specific key/value data parsing (enable {@link PIXI.KTXLoader.loadKeyValueData})
+ * @class
+ * @memberof PIXI
+ * @implements {PIXI.ILoaderPlugin}
+ */
+ var KTXLoader = /** @class */ (function () {
+ function KTXLoader() {
+ }
+ /**
+ * Called after a KTX file is loaded.
+ *
+ * This will parse the KTX file header and add a {@code BaseTexture} to the texture
+ * cache.
+ * @see PIXI.Loader.loaderMiddleware
+ * @param resource - loader resource that is checked to see if it is a KTX file
+ * @param next - callback Function to call when done
+ */
+ KTXLoader.use = function (resource, next) {
+ if (resource.extension === 'ktx' && resource.data) {
+ try {
+ var url_1 = resource.name || resource.url;
+ var _a = parseKTX(url_1, resource.data, this.loadKeyValueData), compressed = _a.compressed, uncompressed = _a.uncompressed, kvData_1 = _a.kvData;
+ if (compressed) {
+ var result = registerCompressedTextures(url_1, compressed, resource.metadata);
+ if (kvData_1 && result.textures) {
+ for (var textureId in result.textures) {
+ result.textures[textureId].baseTexture.ktxKeyValueData = kvData_1;
+ }
+ }
+ Object.assign(resource, result);
+ }
+ else if (uncompressed) {
+ var textures_1 = {};
+ uncompressed.forEach(function (image, i) {
+ var texture = new Texture(new BaseTexture(image.resource, {
+ mipmap: exports.MIPMAP_MODES.OFF,
+ alphaMode: exports.ALPHA_MODES.NO_PREMULTIPLIED_ALPHA,
+ type: image.type,
+ format: image.format,
+ }));
+ var cacheID = url_1 + "-" + (i + 1);
+ if (kvData_1)
+ { texture.baseTexture.ktxKeyValueData = kvData_1; }
+ BaseTexture.addToCache(texture.baseTexture, cacheID);
+ Texture.addToCache(texture, cacheID);
+ if (i === 0) {
+ textures_1[url_1] = texture;
+ BaseTexture.addToCache(texture.baseTexture, url_1);
+ Texture.addToCache(texture, url_1);
+ }
+ textures_1[cacheID] = texture;
+ });
+ Object.assign(resource, { textures: textures_1 });
+ }
+ }
+ catch (err) {
+ next(err);
+ return;
+ }
+ }
+ next();
+ };
+ /** @ignore */
+ KTXLoader.extension = exports.ExtensionType.Loader;
+ /**
+ * If set to `true`, {@link PIXI.KTXLoader} will parse key-value data in KTX textures. This feature relies
+ * on the [Encoding Standard]{@link https://encoding.spec.whatwg.org}.
+ *
+ * The key-value data will be available on the base-textures as {@code PIXI.BaseTexture.ktxKeyValueData}. They
+ * will hold a reference to the texture data buffer, so make sure to delete key-value data once you are done
+ * using it.
+ */
+ KTXLoader.loadKeyValueData = false;
+ return KTXLoader;
+ }());
+
+ /*!
+ * @pixi/particle-container - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/particle-container is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+ /* global Reflect, Promise */
+
+ var extendStatics$f = function(d, b) {
+ extendStatics$f = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
+ return extendStatics$f(d, b);
+ };
+
+ function __extends$f(d, b) {
+ extendStatics$f(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ }
+
+ /**
+ * The ParticleContainer class is a really fast version of the Container built solely for speed,
+ * so use when you need a lot of sprites or particles.
+ *
+ * The tradeoff of the ParticleContainer is that most advanced functionality will not work.
+ * ParticleContainer implements the basic object transform (position, scale, rotation)
+ * and some advanced functionality like tint (as of v4.5.6).
+ *
+ * Other more advanced functionality like masking, children, filters, etc will not work on sprites in this batch.
+ *
+ * It's extremely easy to use:
+ * ```js
+ * let container = new ParticleContainer();
+ *
+ * for (let i = 0; i < 100; ++i)
+ * {
+ * let sprite = PIXI.Sprite.from("myImage.png");
+ * container.addChild(sprite);
+ * }
+ * ```
+ *
+ * And here you have a hundred sprites that will be rendered at the speed of light.
+ * @memberof PIXI
+ */
+ var ParticleContainer = /** @class */ (function (_super) {
+ __extends$f(ParticleContainer, _super);
+ /**
+ * @param maxSize - The maximum number of particles that can be rendered by the container.
+ * Affects size of allocated buffers.
+ * @param properties - The properties of children that should be uploaded to the gpu and applied.
+ * @param {boolean} [properties.vertices=false] - When true, vertices be uploaded and applied.
+ * if sprite's ` scale/anchor/trim/frame/orig` is dynamic, please set `true`.
+ * @param {boolean} [properties.position=true] - When true, position be uploaded and applied.
+ * @param {boolean} [properties.rotation=false] - When true, rotation be uploaded and applied.
+ * @param {boolean} [properties.uvs=false] - When true, uvs be uploaded and applied.
+ * @param {boolean} [properties.tint=false] - When true, alpha and tint be uploaded and applied.
+ * @param {number} [batchSize=16384] - Number of particles per batch. If less than maxSize, it uses maxSize instead.
+ * @param {boolean} [autoResize=false] - If true, container allocates more batches in case
+ * there are more than `maxSize` particles.
+ */
+ function ParticleContainer(maxSize, properties, batchSize, autoResize) {
+ if (maxSize === void 0) { maxSize = 1500; }
+ if (batchSize === void 0) { batchSize = 16384; }
+ if (autoResize === void 0) { autoResize = false; }
+ var _this = _super.call(this) || this;
+ // Making sure the batch size is valid
+ // 65535 is max vertex index in the index buffer (see ParticleRenderer)
+ // so max number of particles is 65536 / 4 = 16384
+ var maxBatchSize = 16384;
+ if (batchSize > maxBatchSize) {
+ batchSize = maxBatchSize;
+ }
+ _this._properties = [false, true, false, false, false];
+ _this._maxSize = maxSize;
+ _this._batchSize = batchSize;
+ _this._buffers = null;
+ _this._bufferUpdateIDs = [];
+ _this._updateID = 0;
+ _this.interactiveChildren = false;
+ _this.blendMode = exports.BLEND_MODES.NORMAL;
+ _this.autoResize = autoResize;
+ _this.roundPixels = true;
+ _this.baseTexture = null;
+ _this.setProperties(properties);
+ _this._tint = 0;
+ _this.tintRgb = new Float32Array(4);
+ _this.tint = 0xFFFFFF;
+ return _this;
+ }
+ /**
+ * Sets the private properties array to dynamic / static based on the passed properties object
+ * @param properties - The properties to be uploaded
+ */
+ ParticleContainer.prototype.setProperties = function (properties) {
+ if (properties) {
+ this._properties[0] = 'vertices' in properties || 'scale' in properties
+ ? !!properties.vertices || !!properties.scale : this._properties[0];
+ this._properties[1] = 'position' in properties ? !!properties.position : this._properties[1];
+ this._properties[2] = 'rotation' in properties ? !!properties.rotation : this._properties[2];
+ this._properties[3] = 'uvs' in properties ? !!properties.uvs : this._properties[3];
+ this._properties[4] = 'tint' in properties || 'alpha' in properties
+ ? !!properties.tint || !!properties.alpha : this._properties[4];
+ }
+ };
+ ParticleContainer.prototype.updateTransform = function () {
+ // TODO don't need to!
+ this.displayObjectUpdateTransform();
+ };
+ Object.defineProperty(ParticleContainer.prototype, "tint", {
+ /**
+ * The tint applied to the container. This is a hex value.
+ * A value of 0xFFFFFF will remove any tint effect.
+ * IMPORTANT: This is a WebGL only feature and will be ignored by the canvas renderer.
+ * @default 0xFFFFFF
+ */
+ get: function () {
+ return this._tint;
+ },
+ set: function (value) {
+ this._tint = value;
+ hex2rgb(value, this.tintRgb);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Renders the container using the WebGL renderer.
+ * @param renderer - The WebGL renderer.
+ */
+ ParticleContainer.prototype.render = function (renderer) {
+ var _this = this;
+ if (!this.visible || this.worldAlpha <= 0 || !this.children.length || !this.renderable) {
+ return;
+ }
+ if (!this.baseTexture) {
+ this.baseTexture = this.children[0]._texture.baseTexture;
+ if (!this.baseTexture.valid) {
+ this.baseTexture.once('update', function () { return _this.onChildrenChange(0); });
+ }
+ }
+ renderer.batch.setObjectRenderer(renderer.plugins.particle);
+ renderer.plugins.particle.render(this);
+ };
+ /**
+ * Set the flag that static data should be updated to true
+ * @param smallestChildIndex - The smallest child index.
+ */
+ ParticleContainer.prototype.onChildrenChange = function (smallestChildIndex) {
+ var bufferIndex = Math.floor(smallestChildIndex / this._batchSize);
+ while (this._bufferUpdateIDs.length < bufferIndex) {
+ this._bufferUpdateIDs.push(0);
+ }
+ this._bufferUpdateIDs[bufferIndex] = ++this._updateID;
+ };
+ ParticleContainer.prototype.dispose = function () {
+ if (this._buffers) {
+ for (var i = 0; i < this._buffers.length; ++i) {
+ this._buffers[i].destroy();
+ }
+ this._buffers = null;
+ }
+ };
+ /**
+ * Destroys the container
+ * @param options - Options parameter. A boolean will act as if all options
+ * have been set to that value
+ * @param {boolean} [options.children=false] - if set to true, all the children will have their
+ * destroy method called as well. 'options' will be passed on to those calls.
+ * @param {boolean} [options.texture=false] - Only used for child Sprites if options.children is set to true
+ * Should it destroy the texture of the child sprite
+ * @param {boolean} [options.baseTexture=false] - Only used for child Sprites if options.children is set to true
+ * Should it destroy the base texture of the child sprite
+ */
+ ParticleContainer.prototype.destroy = function (options) {
+ _super.prototype.destroy.call(this, options);
+ this.dispose();
+ this._properties = null;
+ this._buffers = null;
+ this._bufferUpdateIDs = null;
+ };
+ return ParticleContainer;
+ }(Container));
+
+ /*
+ * @author Mat Groves
+ *
+ * Big thanks to the very clever Matt DesLauriers <mattdesl> https://github.com/mattdesl/
+ * for creating the original PixiJS version!
+ * Also a thanks to https://github.com/bchevalier for tweaking the tint and alpha so that
+ * they now share 4 bytes on the vertex buffer
+ *
+ * Heavily inspired by LibGDX's ParticleBuffer:
+ * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/ParticleBuffer.java
+ */
+ /**
+ * The particle buffer manages the static and dynamic buffers for a particle container.
+ * @private
+ * @memberof PIXI
+ */
+ var ParticleBuffer = /** @class */ (function () {
+ /**
+ * @param {object} properties - The properties to upload.
+ * @param {boolean[]} dynamicPropertyFlags - Flags for which properties are dynamic.
+ * @param {number} size - The size of the batch.
+ */
+ function ParticleBuffer(properties, dynamicPropertyFlags, size) {
+ this.geometry = new Geometry();
+ this.indexBuffer = null;
+ this.size = size;
+ this.dynamicProperties = [];
+ this.staticProperties = [];
+ for (var i = 0; i < properties.length; ++i) {
+ var property = properties[i];
+ // Make copy of properties object so that when we edit the offset it doesn't
+ // change all other instances of the object literal
+ property = {
+ attributeName: property.attributeName,
+ size: property.size,
+ uploadFunction: property.uploadFunction,
+ type: property.type || exports.TYPES.FLOAT,
+ offset: property.offset,
+ };
+ if (dynamicPropertyFlags[i]) {
+ this.dynamicProperties.push(property);
+ }
+ else {
+ this.staticProperties.push(property);
+ }
+ }
+ this.staticStride = 0;
+ this.staticBuffer = null;
+ this.staticData = null;
+ this.staticDataUint32 = null;
+ this.dynamicStride = 0;
+ this.dynamicBuffer = null;
+ this.dynamicData = null;
+ this.dynamicDataUint32 = null;
+ this._updateID = 0;
+ this.initBuffers();
+ }
+ /** Sets up the renderer context and necessary buffers. */
+ ParticleBuffer.prototype.initBuffers = function () {
+ var geometry = this.geometry;
+ var dynamicOffset = 0;
+ this.indexBuffer = new Buffer(createIndicesForQuads(this.size), true, true);
+ geometry.addIndex(this.indexBuffer);
+ this.dynamicStride = 0;
+ for (var i = 0; i < this.dynamicProperties.length; ++i) {
+ var property = this.dynamicProperties[i];
+ property.offset = dynamicOffset;
+ dynamicOffset += property.size;
+ this.dynamicStride += property.size;
+ }
+ var dynBuffer = new ArrayBuffer(this.size * this.dynamicStride * 4 * 4);
+ this.dynamicData = new Float32Array(dynBuffer);
+ this.dynamicDataUint32 = new Uint32Array(dynBuffer);
+ this.dynamicBuffer = new Buffer(this.dynamicData, false, false);
+ // static //
+ var staticOffset = 0;
+ this.staticStride = 0;
+ for (var i = 0; i < this.staticProperties.length; ++i) {
+ var property = this.staticProperties[i];
+ property.offset = staticOffset;
+ staticOffset += property.size;
+ this.staticStride += property.size;
+ }
+ var statBuffer = new ArrayBuffer(this.size * this.staticStride * 4 * 4);
+ this.staticData = new Float32Array(statBuffer);
+ this.staticDataUint32 = new Uint32Array(statBuffer);
+ this.staticBuffer = new Buffer(this.staticData, true, false);
+ for (var i = 0; i < this.dynamicProperties.length; ++i) {
+ var property = this.dynamicProperties[i];
+ geometry.addAttribute(property.attributeName, this.dynamicBuffer, 0, property.type === exports.TYPES.UNSIGNED_BYTE, property.type, this.dynamicStride * 4, property.offset * 4);
+ }
+ for (var i = 0; i < this.staticProperties.length; ++i) {
+ var property = this.staticProperties[i];
+ geometry.addAttribute(property.attributeName, this.staticBuffer, 0, property.type === exports.TYPES.UNSIGNED_BYTE, property.type, this.staticStride * 4, property.offset * 4);
+ }
+ };
+ /**
+ * Uploads the dynamic properties.
+ * @param children - The children to upload.
+ * @param startIndex - The index to start at.
+ * @param amount - The number to upload.
+ */
+ ParticleBuffer.prototype.uploadDynamic = function (children, startIndex, amount) {
+ for (var i = 0; i < this.dynamicProperties.length; i++) {
+ var property = this.dynamicProperties[i];
+ property.uploadFunction(children, startIndex, amount, property.type === exports.TYPES.UNSIGNED_BYTE ? this.dynamicDataUint32 : this.dynamicData, this.dynamicStride, property.offset);
+ }
+ this.dynamicBuffer._updateID++;
+ };
+ /**
+ * Uploads the static properties.
+ * @param children - The children to upload.
+ * @param startIndex - The index to start at.
+ * @param amount - The number to upload.
+ */
+ ParticleBuffer.prototype.uploadStatic = function (children, startIndex, amount) {
+ for (var i = 0; i < this.staticProperties.length; i++) {
+ var property = this.staticProperties[i];
+ property.uploadFunction(children, startIndex, amount, property.type === exports.TYPES.UNSIGNED_BYTE ? this.staticDataUint32 : this.staticData, this.staticStride, property.offset);
+ }
+ this.staticBuffer._updateID++;
+ };
+ /** Destroys the ParticleBuffer. */
+ ParticleBuffer.prototype.destroy = function () {
+ this.indexBuffer = null;
+ this.dynamicProperties = null;
+ this.dynamicBuffer = null;
+ this.dynamicData = null;
+ this.dynamicDataUint32 = null;
+ this.staticProperties = null;
+ this.staticBuffer = null;
+ this.staticData = null;
+ this.staticDataUint32 = null;
+ // all buffers are destroyed inside geometry
+ this.geometry.destroy();
+ };
+ return ParticleBuffer;
+ }());
+
+ var fragment$6 = "varying vec2 vTextureCoord;\nvarying vec4 vColor;\n\nuniform sampler2D uSampler;\n\nvoid main(void){\n vec4 color = texture2D(uSampler, vTextureCoord) * vColor;\n gl_FragColor = color;\n}";
+
+ var vertex$3 = "attribute vec2 aVertexPosition;\nattribute vec2 aTextureCoord;\nattribute vec4 aColor;\n\nattribute vec2 aPositionCoord;\nattribute float aRotation;\n\nuniform mat3 translationMatrix;\nuniform vec4 uColor;\n\nvarying vec2 vTextureCoord;\nvarying vec4 vColor;\n\nvoid main(void){\n float x = (aVertexPosition.x) * cos(aRotation) - (aVertexPosition.y) * sin(aRotation);\n float y = (aVertexPosition.x) * sin(aRotation) + (aVertexPosition.y) * cos(aRotation);\n\n vec2 v = vec2(x, y);\n v = v + aPositionCoord;\n\n gl_Position = vec4((translationMatrix * vec3(v, 1.0)).xy, 0.0, 1.0);\n\n vTextureCoord = aTextureCoord;\n vColor = aColor * uColor;\n}\n";
+
+ /*
+ * @author Mat Groves
+ *
+ * Big thanks to the very clever Matt DesLauriers <mattdesl> https://github.com/mattdesl/
+ * for creating the original PixiJS version!
+ * Also a thanks to https://github.com/bchevalier for tweaking the tint and alpha so that they now
+ * share 4 bytes on the vertex buffer
+ *
+ * Heavily inspired by LibGDX's ParticleRenderer:
+ * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/ParticleRenderer.java
+ */
+ /**
+ * Renderer for Particles that is designer for speed over feature set.
+ * @memberof PIXI
+ */
+ var ParticleRenderer = /** @class */ (function (_super) {
+ __extends$f(ParticleRenderer, _super);
+ /**
+ * @param renderer - The renderer this sprite batch works for.
+ */
+ function ParticleRenderer(renderer) {
+ var _this = _super.call(this, renderer) || this;
+ // 65535 is max vertex index in the index buffer (see ParticleRenderer)
+ // so max number of particles is 65536 / 4 = 16384
+ // and max number of element in the index buffer is 16384 * 6 = 98304
+ // Creating a full index buffer, overhead is 98304 * 2 = 196Ko
+ // let numIndices = 98304;
+ _this.shader = null;
+ _this.properties = null;
+ _this.tempMatrix = new Matrix();
+ _this.properties = [
+ // verticesData
+ {
+ attributeName: 'aVertexPosition',
+ size: 2,
+ uploadFunction: _this.uploadVertices,
+ offset: 0,
+ },
+ // positionData
+ {
+ attributeName: 'aPositionCoord',
+ size: 2,
+ uploadFunction: _this.uploadPosition,
+ offset: 0,
+ },
+ // rotationData
+ {
+ attributeName: 'aRotation',
+ size: 1,
+ uploadFunction: _this.uploadRotation,
+ offset: 0,
+ },
+ // uvsData
+ {
+ attributeName: 'aTextureCoord',
+ size: 2,
+ uploadFunction: _this.uploadUvs,
+ offset: 0,
+ },
+ // tintData
+ {
+ attributeName: 'aColor',
+ size: 1,
+ type: exports.TYPES.UNSIGNED_BYTE,
+ uploadFunction: _this.uploadTint,
+ offset: 0,
+ } ];
+ _this.shader = Shader.from(vertex$3, fragment$6, {});
+ _this.state = State.for2d();
+ return _this;
+ }
+ /**
+ * Renders the particle container object.
+ * @param container - The container to render using this ParticleRenderer.
+ */
+ ParticleRenderer.prototype.render = function (container) {
+ var children = container.children;
+ var maxSize = container._maxSize;
+ var batchSize = container._batchSize;
+ var renderer = this.renderer;
+ var totalChildren = children.length;
+ if (totalChildren === 0) {
+ return;
+ }
+ else if (totalChildren > maxSize && !container.autoResize) {
+ totalChildren = maxSize;
+ }
+ var buffers = container._buffers;
+ if (!buffers) {
+ buffers = container._buffers = this.generateBuffers(container);
+ }
+ var baseTexture = children[0]._texture.baseTexture;
+ var premultiplied = baseTexture.alphaMode > 0;
+ // if the uvs have not updated then no point rendering just yet!
+ this.state.blendMode = correctBlendMode(container.blendMode, premultiplied);
+ renderer.state.set(this.state);
+ var gl = renderer.gl;
+ var m = container.worldTransform.copyTo(this.tempMatrix);
+ m.prepend(renderer.globalUniforms.uniforms.projectionMatrix);
+ this.shader.uniforms.translationMatrix = m.toArray(true);
+ this.shader.uniforms.uColor = premultiplyRgba(container.tintRgb, container.worldAlpha, this.shader.uniforms.uColor, premultiplied);
+ this.shader.uniforms.uSampler = baseTexture;
+ this.renderer.shader.bind(this.shader);
+ var updateStatic = false;
+ // now lets upload and render the buffers..
+ for (var i = 0, j = 0; i < totalChildren; i += batchSize, j += 1) {
+ var amount = (totalChildren - i);
+ if (amount > batchSize) {
+ amount = batchSize;
+ }
+ if (j >= buffers.length) {
+ buffers.push(this._generateOneMoreBuffer(container));
+ }
+ var buffer = buffers[j];
+ // we always upload the dynamic
+ buffer.uploadDynamic(children, i, amount);
+ var bid = container._bufferUpdateIDs[j] || 0;
+ updateStatic = updateStatic || (buffer._updateID < bid);
+ // we only upload the static content when we have to!
+ if (updateStatic) {
+ buffer._updateID = container._updateID;
+ buffer.uploadStatic(children, i, amount);
+ }
+ // bind the buffer
+ renderer.geometry.bind(buffer.geometry);
+ gl.drawElements(gl.TRIANGLES, amount * 6, gl.UNSIGNED_SHORT, 0);
+ }
+ };
+ /**
+ * Creates one particle buffer for each child in the container we want to render and updates internal properties.
+ * @param container - The container to render using this ParticleRenderer
+ * @returns - The buffers
+ */
+ ParticleRenderer.prototype.generateBuffers = function (container) {
+ var buffers = [];
+ var size = container._maxSize;
+ var batchSize = container._batchSize;
+ var dynamicPropertyFlags = container._properties;
+ for (var i = 0; i < size; i += batchSize) {
+ buffers.push(new ParticleBuffer(this.properties, dynamicPropertyFlags, batchSize));
+ }
+ return buffers;
+ };
+ /**
+ * Creates one more particle buffer, because container has autoResize feature.
+ * @param container - The container to render using this ParticleRenderer
+ * @returns - The generated buffer
+ */
+ ParticleRenderer.prototype._generateOneMoreBuffer = function (container) {
+ var batchSize = container._batchSize;
+ var dynamicPropertyFlags = container._properties;
+ return new ParticleBuffer(this.properties, dynamicPropertyFlags, batchSize);
+ };
+ /**
+ * Uploads the vertices.
+ * @param children - the array of sprites to render
+ * @param startIndex - the index to start from in the children array
+ * @param amount - the amount of children that will have their vertices uploaded
+ * @param array - The vertices to upload.
+ * @param stride - Stride to use for iteration.
+ * @param offset - Offset to start at.
+ */
+ ParticleRenderer.prototype.uploadVertices = function (children, startIndex, amount, array, stride, offset) {
+ var w0 = 0;
+ var w1 = 0;
+ var h0 = 0;
+ var h1 = 0;
+ for (var i = 0; i < amount; ++i) {
+ var sprite = children[startIndex + i];
+ var texture = sprite._texture;
+ var sx = sprite.scale.x;
+ var sy = sprite.scale.y;
+ var trim = texture.trim;
+ var orig = texture.orig;
+ if (trim) {
+ // if the sprite is trimmed and is not a tilingsprite then we need to add the
+ // extra space before transforming the sprite coords..
+ w1 = trim.x - (sprite.anchor.x * orig.width);
+ w0 = w1 + trim.width;
+ h1 = trim.y - (sprite.anchor.y * orig.height);
+ h0 = h1 + trim.height;
+ }
+ else {
+ w0 = (orig.width) * (1 - sprite.anchor.x);
+ w1 = (orig.width) * -sprite.anchor.x;
+ h0 = orig.height * (1 - sprite.anchor.y);
+ h1 = orig.height * -sprite.anchor.y;
+ }
+ array[offset] = w1 * sx;
+ array[offset + 1] = h1 * sy;
+ array[offset + stride] = w0 * sx;
+ array[offset + stride + 1] = h1 * sy;
+ array[offset + (stride * 2)] = w0 * sx;
+ array[offset + (stride * 2) + 1] = h0 * sy;
+ array[offset + (stride * 3)] = w1 * sx;
+ array[offset + (stride * 3) + 1] = h0 * sy;
+ offset += stride * 4;
+ }
+ };
+ /**
+ * Uploads the position.
+ * @param children - the array of sprites to render
+ * @param startIndex - the index to start from in the children array
+ * @param amount - the amount of children that will have their positions uploaded
+ * @param array - The vertices to upload.
+ * @param stride - Stride to use for iteration.
+ * @param offset - Offset to start at.
+ */
+ ParticleRenderer.prototype.uploadPosition = function (children, startIndex, amount, array, stride, offset) {
+ for (var i = 0; i < amount; i++) {
+ var spritePosition = children[startIndex + i].position;
+ array[offset] = spritePosition.x;
+ array[offset + 1] = spritePosition.y;
+ array[offset + stride] = spritePosition.x;
+ array[offset + stride + 1] = spritePosition.y;
+ array[offset + (stride * 2)] = spritePosition.x;
+ array[offset + (stride * 2) + 1] = spritePosition.y;
+ array[offset + (stride * 3)] = spritePosition.x;
+ array[offset + (stride * 3) + 1] = spritePosition.y;
+ offset += stride * 4;
+ }
+ };
+ /**
+ * Uploads the rotation.
+ * @param children - the array of sprites to render
+ * @param startIndex - the index to start from in the children array
+ * @param amount - the amount of children that will have their rotation uploaded
+ * @param array - The vertices to upload.
+ * @param stride - Stride to use for iteration.
+ * @param offset - Offset to start at.
+ */
+ ParticleRenderer.prototype.uploadRotation = function (children, startIndex, amount, array, stride, offset) {
+ for (var i = 0; i < amount; i++) {
+ var spriteRotation = children[startIndex + i].rotation;
+ array[offset] = spriteRotation;
+ array[offset + stride] = spriteRotation;
+ array[offset + (stride * 2)] = spriteRotation;
+ array[offset + (stride * 3)] = spriteRotation;
+ offset += stride * 4;
+ }
+ };
+ /**
+ * Uploads the UVs.
+ * @param children - the array of sprites to render
+ * @param startIndex - the index to start from in the children array
+ * @param amount - the amount of children that will have their rotation uploaded
+ * @param array - The vertices to upload.
+ * @param stride - Stride to use for iteration.
+ * @param offset - Offset to start at.
+ */
+ ParticleRenderer.prototype.uploadUvs = function (children, startIndex, amount, array, stride, offset) {
+ for (var i = 0; i < amount; ++i) {
+ var textureUvs = children[startIndex + i]._texture._uvs;
+ if (textureUvs) {
+ array[offset] = textureUvs.x0;
+ array[offset + 1] = textureUvs.y0;
+ array[offset + stride] = textureUvs.x1;
+ array[offset + stride + 1] = textureUvs.y1;
+ array[offset + (stride * 2)] = textureUvs.x2;
+ array[offset + (stride * 2) + 1] = textureUvs.y2;
+ array[offset + (stride * 3)] = textureUvs.x3;
+ array[offset + (stride * 3) + 1] = textureUvs.y3;
+ offset += stride * 4;
+ }
+ else {
+ // TODO you know this can be easier!
+ array[offset] = 0;
+ array[offset + 1] = 0;
+ array[offset + stride] = 0;
+ array[offset + stride + 1] = 0;
+ array[offset + (stride * 2)] = 0;
+ array[offset + (stride * 2) + 1] = 0;
+ array[offset + (stride * 3)] = 0;
+ array[offset + (stride * 3) + 1] = 0;
+ offset += stride * 4;
+ }
+ }
+ };
+ /**
+ * Uploads the tint.
+ * @param children - the array of sprites to render
+ * @param startIndex - the index to start from in the children array
+ * @param amount - the amount of children that will have their rotation uploaded
+ * @param array - The vertices to upload.
+ * @param stride - Stride to use for iteration.
+ * @param offset - Offset to start at.
+ */
+ ParticleRenderer.prototype.uploadTint = function (children, startIndex, amount, array, stride, offset) {
+ for (var i = 0; i < amount; ++i) {
+ var sprite = children[startIndex + i];
+ var premultiplied = sprite._texture.baseTexture.alphaMode > 0;
+ var alpha = sprite.alpha;
+ // we dont call extra function if alpha is 1.0, that's faster
+ var argb = alpha < 1.0 && premultiplied
+ ? premultiplyTint(sprite._tintRGB, alpha) : sprite._tintRGB + (alpha * 255 << 24);
+ array[offset] = argb;
+ array[offset + stride] = argb;
+ array[offset + (stride * 2)] = argb;
+ array[offset + (stride * 3)] = argb;
+ offset += stride * 4;
+ }
+ };
+ /** Destroys the ParticleRenderer. */
+ ParticleRenderer.prototype.destroy = function () {
+ _super.prototype.destroy.call(this);
+ if (this.shader) {
+ this.shader.destroy();
+ this.shader = null;
+ }
+ this.tempMatrix = null;
+ };
+ /** @ignore */
+ ParticleRenderer.extension = {
+ name: 'particle',
+ type: exports.ExtensionType.RendererPlugin,
+ };
+ return ParticleRenderer;
+ }(ObjectRenderer));
+
+ /*!
+ * @pixi/graphics - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/graphics is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /**
+ * Supported line joints in `PIXI.LineStyle` for graphics.
+ * @see PIXI.Graphics#lineStyle
+ * @see https://graphicdesign.stackexchange.com/questions/59018/what-is-a-bevel-join-of-two-lines-exactly-illustrator
+ * @name LINE_JOIN
+ * @memberof PIXI
+ * @static
+ * @enum {string}
+ * @property {string} MITER - 'miter': make a sharp corner where outer part of lines meet
+ * @property {string} BEVEL - 'bevel': add a square butt at each end of line segment and fill the triangle at turn
+ * @property {string} ROUND - 'round': add an arc at the joint
+ */
+ exports.LINE_JOIN = void 0;
+ (function (LINE_JOIN) {
+ LINE_JOIN["MITER"] = "miter";
+ LINE_JOIN["BEVEL"] = "bevel";
+ LINE_JOIN["ROUND"] = "round";
+ })(exports.LINE_JOIN || (exports.LINE_JOIN = {}));
+ /**
+ * Support line caps in `PIXI.LineStyle` for graphics.
+ * @see PIXI.Graphics#lineStyle
+ * @name LINE_CAP
+ * @memberof PIXI
+ * @static
+ * @enum {string}
+ * @property {string} BUTT - 'butt': don't add any cap at line ends (leaves orthogonal edges)
+ * @property {string} ROUND - 'round': add semicircle at ends
+ * @property {string} SQUARE - 'square': add square at end (like `BUTT` except more length at end)
+ */
+ exports.LINE_CAP = void 0;
+ (function (LINE_CAP) {
+ LINE_CAP["BUTT"] = "butt";
+ LINE_CAP["ROUND"] = "round";
+ LINE_CAP["SQUARE"] = "square";
+ })(exports.LINE_CAP || (exports.LINE_CAP = {}));
+ /**
+ * Graphics curves resolution settings. If `adaptive` flag is set to `true`,
+ * the resolution is calculated based on the curve's length to ensure better visual quality.
+ * Adaptive draw works with `bezierCurveTo` and `quadraticCurveTo`.
+ * @static
+ * @constant
+ * @memberof PIXI
+ * @name GRAPHICS_CURVES
+ * @type {object}
+ * @property {boolean} [adaptive=true] - flag indicating if the resolution should be adaptive
+ * @property {number} [maxLength=10] - maximal length of a single segment of the curve (if adaptive = false, ignored)
+ * @property {number} [minSegments=8] - minimal number of segments in the curve (if adaptive = false, ignored)
+ * @property {number} [maxSegments=2048] - maximal number of segments in the curve (if adaptive = false, ignored)
+ */
+ var GRAPHICS_CURVES = {
+ adaptive: true,
+ maxLength: 10,
+ minSegments: 8,
+ maxSegments: 2048,
+ epsilon: 0.0001,
+ _segmentsCount: function (length, defaultSegments) {
+ if (defaultSegments === void 0) { defaultSegments = 20; }
+ if (!this.adaptive || !length || isNaN(length)) {
+ return defaultSegments;
+ }
+ var result = Math.ceil(length / this.maxLength);
+ if (result < this.minSegments) {
+ result = this.minSegments;
+ }
+ else if (result > this.maxSegments) {
+ result = this.maxSegments;
+ }
+ return result;
+ },
+ };
+
+ /**
+ * Fill style object for Graphics.
+ * @memberof PIXI
+ */
+ var FillStyle = /** @class */ (function () {
+ function FillStyle() {
+ /**
+ * The hex color value used when coloring the Graphics object.
+ * @default 0xFFFFFF
+ */
+ this.color = 0xFFFFFF;
+ /** The alpha value used when filling the Graphics object. */
+ this.alpha = 1.0;
+ /**
+ * The texture to be used for the fill.
+ * @default 0
+ */
+ this.texture = Texture.WHITE;
+ /**
+ * The transform applied to the texture.
+ * @default null
+ */
+ this.matrix = null;
+ /** If the current fill is visible. */
+ this.visible = false;
+ this.reset();
+ }
+ /** Clones the object */
+ FillStyle.prototype.clone = function () {
+ var obj = new FillStyle();
+ obj.color = this.color;
+ obj.alpha = this.alpha;
+ obj.texture = this.texture;
+ obj.matrix = this.matrix;
+ obj.visible = this.visible;
+ return obj;
+ };
+ /** Reset */
+ FillStyle.prototype.reset = function () {
+ this.color = 0xFFFFFF;
+ this.alpha = 1;
+ this.texture = Texture.WHITE;
+ this.matrix = null;
+ this.visible = false;
+ };
+ /** Destroy and don't use after this. */
+ FillStyle.prototype.destroy = function () {
+ this.texture = null;
+ this.matrix = null;
+ };
+ return FillStyle;
+ }());
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+ /* global Reflect, Promise */
+
+ var extendStatics$e = function(d, b) {
+ extendStatics$e = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
+ return extendStatics$e(d, b);
+ };
+
+ function __extends$e(d, b) {
+ extendStatics$e(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ }
+
+ function fixOrientation(points, hole) {
+ var _a, _b;
+ if (hole === void 0) { hole = false; }
+ var m = points.length;
+ if (m < 6) {
+ return;
+ }
+ var area = 0;
+ for (var i = 0, x1 = points[m - 2], y1 = points[m - 1]; i < m; i += 2) {
+ var x2 = points[i];
+ var y2 = points[i + 1];
+ area += (x2 - x1) * (y2 + y1);
+ x1 = x2;
+ y1 = y2;
+ }
+ if ((!hole && area > 0) || (hole && area <= 0)) {
+ var n = m / 2;
+ for (var i = n + (n % 2); i < m; i += 2) {
+ var i1 = m - i - 2;
+ var i2 = m - i - 1;
+ var i3 = i;
+ var i4 = i + 1;
+ _a = [points[i3], points[i1]], points[i1] = _a[0], points[i3] = _a[1];
+ _b = [points[i4], points[i2]], points[i2] = _b[0], points[i4] = _b[1];
+ }
+ }
+ }
+ /**
+ * Builds a polygon to draw
+ *
+ * Ignored from docs since it is not directly exposed.
+ * @ignore
+ * @private
+ * @param {PIXI.WebGLGraphicsData} graphicsData - The graphics object containing all the necessary properties
+ * @param {object} webGLData - an object containing all the WebGL-specific information to create this shape
+ * @param {object} webGLDataNativeLines - an object containing all the WebGL-specific information to create nativeLines
+ */
+ var buildPoly = {
+ build: function (graphicsData) {
+ graphicsData.points = graphicsData.shape.points.slice();
+ },
+ triangulate: function (graphicsData, graphicsGeometry) {
+ var points = graphicsData.points;
+ var holes = graphicsData.holes;
+ var verts = graphicsGeometry.points;
+ var indices = graphicsGeometry.indices;
+ if (points.length >= 6) {
+ fixOrientation(points, false);
+ var holeArray = [];
+ // Process holes..
+ for (var i = 0; i < holes.length; i++) {
+ var hole = holes[i];
+ fixOrientation(hole.points, true);
+ holeArray.push(points.length / 2);
+ points = points.concat(hole.points);
+ }
+ // sort color
+ var triangles = earcut_1(points, holeArray, 2);
+ if (!triangles) {
+ return;
+ }
+ var vertPos = verts.length / 2;
+ for (var i = 0; i < triangles.length; i += 3) {
+ indices.push(triangles[i] + vertPos);
+ indices.push(triangles[i + 1] + vertPos);
+ indices.push(triangles[i + 2] + vertPos);
+ }
+ for (var i = 0; i < points.length; i++) {
+ verts.push(points[i]);
+ }
+ }
+ },
+ };
+
+ // for type only
+ /**
+ * Builds a circle to draw
+ *
+ * Ignored from docs since it is not directly exposed.
+ * @ignore
+ * @private
+ * @param {PIXI.WebGLGraphicsData} graphicsData - The graphics object to draw
+ * @param {object} webGLData - an object containing all the WebGL-specific information to create this shape
+ * @param {object} webGLDataNativeLines - an object containing all the WebGL-specific information to create nativeLines
+ */
+ var buildCircle = {
+ build: function (graphicsData) {
+ // need to convert points to a nice regular data
+ var points = graphicsData.points;
+ var x;
+ var y;
+ var dx;
+ var dy;
+ var rx;
+ var ry;
+ if (graphicsData.type === exports.SHAPES.CIRC) {
+ var circle = graphicsData.shape;
+ x = circle.x;
+ y = circle.y;
+ rx = ry = circle.radius;
+ dx = dy = 0;
+ }
+ else if (graphicsData.type === exports.SHAPES.ELIP) {
+ var ellipse = graphicsData.shape;
+ x = ellipse.x;
+ y = ellipse.y;
+ rx = ellipse.width;
+ ry = ellipse.height;
+ dx = dy = 0;
+ }
+ else {
+ var roundedRect = graphicsData.shape;
+ var halfWidth = roundedRect.width / 2;
+ var halfHeight = roundedRect.height / 2;
+ x = roundedRect.x + halfWidth;
+ y = roundedRect.y + halfHeight;
+ rx = ry = Math.max(0, Math.min(roundedRect.radius, Math.min(halfWidth, halfHeight)));
+ dx = halfWidth - rx;
+ dy = halfHeight - ry;
+ }
+ if (!(rx >= 0 && ry >= 0 && dx >= 0 && dy >= 0)) {
+ points.length = 0;
+ return;
+ }
+ // Choose a number of segments such that the maximum absolute deviation from the circle is approximately 0.029
+ var n = Math.ceil(2.3 * Math.sqrt(rx + ry));
+ var m = (n * 8) + (dx ? 4 : 0) + (dy ? 4 : 0);
+ points.length = m;
+ if (m === 0) {
+ return;
+ }
+ if (n === 0) {
+ points.length = 8;
+ points[0] = points[6] = x + dx;
+ points[1] = points[3] = y + dy;
+ points[2] = points[4] = x - dx;
+ points[5] = points[7] = y - dy;
+ return;
+ }
+ var j1 = 0;
+ var j2 = (n * 4) + (dx ? 2 : 0) + 2;
+ var j3 = j2;
+ var j4 = m;
+ {
+ var x0 = dx + rx;
+ var y0 = dy;
+ var x1 = x + x0;
+ var x2 = x - x0;
+ var y1 = y + y0;
+ points[j1++] = x1;
+ points[j1++] = y1;
+ points[--j2] = y1;
+ points[--j2] = x2;
+ if (dy) {
+ var y2 = y - y0;
+ points[j3++] = x2;
+ points[j3++] = y2;
+ points[--j4] = y2;
+ points[--j4] = x1;
+ }
+ }
+ for (var i = 1; i < n; i++) {
+ var a = Math.PI / 2 * (i / n);
+ var x0 = dx + (Math.cos(a) * rx);
+ var y0 = dy + (Math.sin(a) * ry);
+ var x1 = x + x0;
+ var x2 = x - x0;
+ var y1 = y + y0;
+ var y2 = y - y0;
+ points[j1++] = x1;
+ points[j1++] = y1;
+ points[--j2] = y1;
+ points[--j2] = x2;
+ points[j3++] = x2;
+ points[j3++] = y2;
+ points[--j4] = y2;
+ points[--j4] = x1;
+ }
+ {
+ var x0 = dx;
+ var y0 = dy + ry;
+ var x1 = x + x0;
+ var x2 = x - x0;
+ var y1 = y + y0;
+ var y2 = y - y0;
+ points[j1++] = x1;
+ points[j1++] = y1;
+ points[--j4] = y2;
+ points[--j4] = x1;
+ if (dx) {
+ points[j1++] = x2;
+ points[j1++] = y1;
+ points[--j4] = y2;
+ points[--j4] = x2;
+ }
+ }
+ },
+ triangulate: function (graphicsData, graphicsGeometry) {
+ var points = graphicsData.points;
+ var verts = graphicsGeometry.points;
+ var indices = graphicsGeometry.indices;
+ if (points.length === 0) {
+ return;
+ }
+ var vertPos = verts.length / 2;
+ var center = vertPos;
+ var x;
+ var y;
+ if (graphicsData.type !== exports.SHAPES.RREC) {
+ var circle = graphicsData.shape;
+ x = circle.x;
+ y = circle.y;
+ }
+ else {
+ var roundedRect = graphicsData.shape;
+ x = roundedRect.x + (roundedRect.width / 2);
+ y = roundedRect.y + (roundedRect.height / 2);
+ }
+ var matrix = graphicsData.matrix;
+ // Push center (special point)
+ verts.push(graphicsData.matrix ? (matrix.a * x) + (matrix.c * y) + matrix.tx : x, graphicsData.matrix ? (matrix.b * x) + (matrix.d * y) + matrix.ty : y);
+ vertPos++;
+ verts.push(points[0], points[1]);
+ for (var i = 2; i < points.length; i += 2) {
+ verts.push(points[i], points[i + 1]);
+ // add some uvs
+ indices.push(vertPos++, center, vertPos);
+ }
+ indices.push(center + 1, center, vertPos);
+ },
+ };
+
+ /**
+ * Builds a rectangle to draw
+ *
+ * Ignored from docs since it is not directly exposed.
+ * @ignore
+ * @private
+ * @param {PIXI.WebGLGraphicsData} graphicsData - The graphics object containing all the necessary properties
+ * @param {object} webGLData - an object containing all the WebGL-specific information to create this shape
+ * @param {object} webGLDataNativeLines - an object containing all the WebGL-specific information to create nativeLines
+ */
+ var buildRectangle = {
+ build: function (graphicsData) {
+ // --- //
+ // need to convert points to a nice regular data
+ //
+ var rectData = graphicsData.shape;
+ var x = rectData.x;
+ var y = rectData.y;
+ var width = rectData.width;
+ var height = rectData.height;
+ var points = graphicsData.points;
+ points.length = 0;
+ points.push(x, y, x + width, y, x + width, y + height, x, y + height);
+ },
+ triangulate: function (graphicsData, graphicsGeometry) {
+ var points = graphicsData.points;
+ var verts = graphicsGeometry.points;
+ var vertPos = verts.length / 2;
+ verts.push(points[0], points[1], points[2], points[3], points[6], points[7], points[4], points[5]);
+ graphicsGeometry.indices.push(vertPos, vertPos + 1, vertPos + 2, vertPos + 1, vertPos + 2, vertPos + 3);
+ },
+ };
+
+ /**
+ * Calculate a single point for a quadratic bezier curve.
+ * Utility function used by quadraticBezierCurve.
+ * Ignored from docs since it is not directly exposed.
+ * @ignore
+ * @private
+ * @param {number} n1 - first number
+ * @param {number} n2 - second number
+ * @param {number} perc - percentage
+ * @returns {number} the result
+ */
+ function getPt(n1, n2, perc) {
+ var diff = n2 - n1;
+ return n1 + (diff * perc);
+ }
+ /**
+ * Calculate the points for a quadratic bezier curve. (helper function..)
+ * Based on: https://stackoverflow.com/questions/785097/how-do-i-implement-a-bezier-curve-in-c
+ *
+ * Ignored from docs since it is not directly exposed.
+ * @ignore
+ * @private
+ * @param {number} fromX - Origin point x
+ * @param {number} fromY - Origin point x
+ * @param {number} cpX - Control point x
+ * @param {number} cpY - Control point y
+ * @param {number} toX - Destination point x
+ * @param {number} toY - Destination point y
+ * @param {number[]} [out=[]] - The output array to add points into. If not passed, a new array is created.
+ * @returns {number[]} an array of points
+ */
+ function quadraticBezierCurve(fromX, fromY, cpX, cpY, toX, toY, out) {
+ if (out === void 0) { out = []; }
+ var n = 20;
+ var points = out;
+ var xa = 0;
+ var ya = 0;
+ var xb = 0;
+ var yb = 0;
+ var x = 0;
+ var y = 0;
+ for (var i = 0, j = 0; i <= n; ++i) {
+ j = i / n;
+ // The Green Line
+ xa = getPt(fromX, cpX, j);
+ ya = getPt(fromY, cpY, j);
+ xb = getPt(cpX, toX, j);
+ yb = getPt(cpY, toY, j);
+ // The Black Dot
+ x = getPt(xa, xb, j);
+ y = getPt(ya, yb, j);
+ // Handle case when first curve points overlaps and earcut fails to triangulate
+ if (i === 0 && points[points.length - 2] === x && points[points.length - 1] === y) {
+ continue;
+ }
+ points.push(x, y);
+ }
+ return points;
+ }
+ /**
+ * Builds a rounded rectangle to draw
+ *
+ * Ignored from docs since it is not directly exposed.
+ * @ignore
+ * @private
+ * @param {PIXI.WebGLGraphicsData} graphicsData - The graphics object containing all the necessary properties
+ * @param {object} webGLData - an object containing all the WebGL-specific information to create this shape
+ * @param {object} webGLDataNativeLines - an object containing all the WebGL-specific information to create nativeLines
+ */
+ var buildRoundedRectangle = {
+ build: function (graphicsData) {
+ if (Graphics.nextRoundedRectBehavior) {
+ buildCircle.build(graphicsData);
+ return;
+ }
+ var rrectData = graphicsData.shape;
+ var points = graphicsData.points;
+ var x = rrectData.x;
+ var y = rrectData.y;
+ var width = rrectData.width;
+ var height = rrectData.height;
+ // Don't allow negative radius or greater than half the smallest width
+ var radius = Math.max(0, Math.min(rrectData.radius, Math.min(width, height) / 2));
+ points.length = 0;
+ // No radius, do a simple rectangle
+ if (!radius) {
+ points.push(x, y, x + width, y, x + width, y + height, x, y + height);
+ }
+ else {
+ quadraticBezierCurve(x, y + radius, x, y, x + radius, y, points);
+ quadraticBezierCurve(x + width - radius, y, x + width, y, x + width, y + radius, points);
+ quadraticBezierCurve(x + width, y + height - radius, x + width, y + height, x + width - radius, y + height, points);
+ quadraticBezierCurve(x + radius, y + height, x, y + height, x, y + height - radius, points);
+ }
+ },
+ triangulate: function (graphicsData, graphicsGeometry) {
+ if (Graphics.nextRoundedRectBehavior) {
+ buildCircle.triangulate(graphicsData, graphicsGeometry);
+ return;
+ }
+ var points = graphicsData.points;
+ var verts = graphicsGeometry.points;
+ var indices = graphicsGeometry.indices;
+ var vecPos = verts.length / 2;
+ var triangles = earcut_1(points, null, 2);
+ for (var i = 0, j = triangles.length; i < j; i += 3) {
+ indices.push(triangles[i] + vecPos);
+ // indices.push(triangles[i] + vecPos);
+ indices.push(triangles[i + 1] + vecPos);
+ // indices.push(triangles[i + 2] + vecPos);
+ indices.push(triangles[i + 2] + vecPos);
+ }
+ for (var i = 0, j = points.length; i < j; i++) {
+ verts.push(points[i], points[++i]);
+ }
+ },
+ };
+
+ /**
+ * Buffers vertices to draw a square cap.
+ *
+ * Ignored from docs since it is not directly exposed.
+ * @ignore
+ * @private
+ * @param {number} x - X-coord of end point
+ * @param {number} y - Y-coord of end point
+ * @param {number} nx - X-coord of line normal pointing inside
+ * @param {number} ny - Y-coord of line normal pointing inside
+ * @param {number} innerWeight - Weight of inner points
+ * @param {number} outerWeight - Weight of outer points
+ * @param {boolean} clockwise - Whether the cap is drawn clockwise
+ * @param {Array<number>} verts - vertex buffer
+ * @returns {number} - no. of vertices pushed
+ */
+ function square(x, y, nx, ny, innerWeight, outerWeight, clockwise, /* rotation for square (true at left end, false at right end) */ verts) {
+ var ix = x - (nx * innerWeight);
+ var iy = y - (ny * innerWeight);
+ var ox = x + (nx * outerWeight);
+ var oy = y + (ny * outerWeight);
+ /* Rotate nx,ny for extension vector */
+ var exx;
+ var eyy;
+ if (clockwise) {
+ exx = ny;
+ eyy = -nx;
+ }
+ else {
+ exx = -ny;
+ eyy = nx;
+ }
+ /* [i|0]x,y extended at cap */
+ var eix = ix + exx;
+ var eiy = iy + eyy;
+ var eox = ox + exx;
+ var eoy = oy + eyy;
+ /* Square itself must be inserted clockwise*/
+ verts.push(eix, eiy);
+ verts.push(eox, eoy);
+ return 2;
+ }
+ /**
+ * Buffers vertices to draw an arc at the line joint or cap.
+ *
+ * Ignored from docs since it is not directly exposed.
+ * @ignore
+ * @private
+ * @param {number} cx - X-coord of center
+ * @param {number} cy - Y-coord of center
+ * @param {number} sx - X-coord of arc start
+ * @param {number} sy - Y-coord of arc start
+ * @param {number} ex - X-coord of arc end
+ * @param {number} ey - Y-coord of arc end
+ * @param {Array<number>} verts - buffer of vertices
+ * @param {boolean} clockwise - orientation of vertices
+ * @returns {number} - no. of vertices pushed
+ */
+ function round(cx, cy, sx, sy, ex, ey, verts, clockwise) {
+ var cx2p0x = sx - cx;
+ var cy2p0y = sy - cy;
+ var angle0 = Math.atan2(cx2p0x, cy2p0y);
+ var angle1 = Math.atan2(ex - cx, ey - cy);
+ if (clockwise && angle0 < angle1) {
+ angle0 += Math.PI * 2;
+ }
+ else if (!clockwise && angle0 > angle1) {
+ angle1 += Math.PI * 2;
+ }
+ var startAngle = angle0;
+ var angleDiff = angle1 - angle0;
+ var absAngleDiff = Math.abs(angleDiff);
+ /* if (absAngleDiff >= PI_LBOUND && absAngleDiff <= PI_UBOUND)
+ {
+ const r1x = cx - nxtPx;
+ const r1y = cy - nxtPy;
+
+ if (r1x === 0)
+ {
+ if (r1y > 0)
+ {
+ angleDiff = -angleDiff;
+ }
+ }
+ else if (r1x >= -GRAPHICS_CURVES.epsilon)
+ {
+ angleDiff = -angleDiff;
+ }
+ }*/
+ var radius = Math.sqrt((cx2p0x * cx2p0x) + (cy2p0y * cy2p0y));
+ var segCount = ((15 * absAngleDiff * Math.sqrt(radius) / Math.PI) >> 0) + 1;
+ var angleInc = angleDiff / segCount;
+ startAngle += angleInc;
+ if (clockwise) {
+ verts.push(cx, cy);
+ verts.push(sx, sy);
+ for (var i = 1, angle = startAngle; i < segCount; i++, angle += angleInc) {
+ verts.push(cx, cy);
+ verts.push(cx + ((Math.sin(angle) * radius)), cy + ((Math.cos(angle) * radius)));
+ }
+ verts.push(cx, cy);
+ verts.push(ex, ey);
+ }
+ else {
+ verts.push(sx, sy);
+ verts.push(cx, cy);
+ for (var i = 1, angle = startAngle; i < segCount; i++, angle += angleInc) {
+ verts.push(cx + ((Math.sin(angle) * radius)), cy + ((Math.cos(angle) * radius)));
+ verts.push(cx, cy);
+ }
+ verts.push(ex, ey);
+ verts.push(cx, cy);
+ }
+ return segCount * 2;
+ }
+ /**
+ * Builds a line to draw using the polygon method.
+ *
+ * Ignored from docs since it is not directly exposed.
+ * @ignore
+ * @private
+ * @param {PIXI.GraphicsData} graphicsData - The graphics object containing all the necessary properties
+ * @param {PIXI.GraphicsGeometry} graphicsGeometry - Geometry where to append output
+ */
+ function buildNonNativeLine(graphicsData, graphicsGeometry) {
+ var shape = graphicsData.shape;
+ var points = graphicsData.points || shape.points.slice();
+ var eps = graphicsGeometry.closePointEps;
+ if (points.length === 0) {
+ return;
+ }
+ // if the line width is an odd number add 0.5 to align to a whole pixel
+ // commenting this out fixes #711 and #1620
+ // if (graphicsData.lineWidth%2)
+ // {
+ // for (i = 0; i < points.length; i++)
+ // {
+ // points[i] += 0.5;
+ // }
+ // }
+ var style = graphicsData.lineStyle;
+ // get first and last point.. figure out the middle!
+ var firstPoint = new Point(points[0], points[1]);
+ var lastPoint = new Point(points[points.length - 2], points[points.length - 1]);
+ var closedShape = shape.type !== exports.SHAPES.POLY || shape.closeStroke;
+ var closedPath = Math.abs(firstPoint.x - lastPoint.x) < eps
+ && Math.abs(firstPoint.y - lastPoint.y) < eps;
+ // if the first point is the last point - gonna have issues :)
+ if (closedShape) {
+ // need to clone as we are going to slightly modify the shape..
+ points = points.slice();
+ if (closedPath) {
+ points.pop();
+ points.pop();
+ lastPoint.set(points[points.length - 2], points[points.length - 1]);
+ }
+ var midPointX = (firstPoint.x + lastPoint.x) * 0.5;
+ var midPointY = (lastPoint.y + firstPoint.y) * 0.5;
+ points.unshift(midPointX, midPointY);
+ points.push(midPointX, midPointY);
+ }
+ var verts = graphicsGeometry.points;
+ var length = points.length / 2;
+ var indexCount = points.length;
+ var indexStart = verts.length / 2;
+ // Max. inner and outer width
+ var width = style.width / 2;
+ var widthSquared = width * width;
+ var miterLimitSquared = style.miterLimit * style.miterLimit;
+ /* Line segments of interest where (x1,y1) forms the corner. */
+ var x0 = points[0];
+ var y0 = points[1];
+ var x1 = points[2];
+ var y1 = points[3];
+ var x2 = 0;
+ var y2 = 0;
+ /* perp[?](x|y) = the line normal with magnitude lineWidth. */
+ var perpx = -(y0 - y1);
+ var perpy = x0 - x1;
+ var perp1x = 0;
+ var perp1y = 0;
+ var dist = Math.sqrt((perpx * perpx) + (perpy * perpy));
+ perpx /= dist;
+ perpy /= dist;
+ perpx *= width;
+ perpy *= width;
+ var ratio = style.alignment; // 0.5;
+ var innerWeight = (1 - ratio) * 2;
+ var outerWeight = ratio * 2;
+ if (!closedShape) {
+ if (style.cap === exports.LINE_CAP.ROUND) {
+ indexCount += round(x0 - (perpx * (innerWeight - outerWeight) * 0.5), y0 - (perpy * (innerWeight - outerWeight) * 0.5), x0 - (perpx * innerWeight), y0 - (perpy * innerWeight), x0 + (perpx * outerWeight), y0 + (perpy * outerWeight), verts, true) + 2;
+ }
+ else if (style.cap === exports.LINE_CAP.SQUARE) {
+ indexCount += square(x0, y0, perpx, perpy, innerWeight, outerWeight, true, verts);
+ }
+ }
+ // Push first point (below & above vertices)
+ verts.push(x0 - (perpx * innerWeight), y0 - (perpy * innerWeight));
+ verts.push(x0 + (perpx * outerWeight), y0 + (perpy * outerWeight));
+ for (var i = 1; i < length - 1; ++i) {
+ x0 = points[(i - 1) * 2];
+ y0 = points[((i - 1) * 2) + 1];
+ x1 = points[i * 2];
+ y1 = points[(i * 2) + 1];
+ x2 = points[(i + 1) * 2];
+ y2 = points[((i + 1) * 2) + 1];
+ perpx = -(y0 - y1);
+ perpy = x0 - x1;
+ dist = Math.sqrt((perpx * perpx) + (perpy * perpy));
+ perpx /= dist;
+ perpy /= dist;
+ perpx *= width;
+ perpy *= width;
+ perp1x = -(y1 - y2);
+ perp1y = x1 - x2;
+ dist = Math.sqrt((perp1x * perp1x) + (perp1y * perp1y));
+ perp1x /= dist;
+ perp1y /= dist;
+ perp1x *= width;
+ perp1y *= width;
+ /* d[x|y](0|1) = the component displacement between points p(0,1|1,2) */
+ var dx0 = x1 - x0;
+ var dy0 = y0 - y1;
+ var dx1 = x1 - x2;
+ var dy1 = y2 - y1;
+ /* +ve if internal angle counterclockwise, -ve if internal angle clockwise. */
+ var cross = (dy0 * dx1) - (dy1 * dx0);
+ var clockwise = (cross < 0);
+ /* Going nearly straight? */
+ if (Math.abs(cross) < 0.1) {
+ verts.push(x1 - (perpx * innerWeight), y1 - (perpy * innerWeight));
+ verts.push(x1 + (perpx * outerWeight), y1 + (perpy * outerWeight));
+ continue;
+ }
+ /* p[x|y] is the miter point. pdist is the distance between miter point and p1. */
+ var c1 = ((-perpx + x0) * (-perpy + y1)) - ((-perpx + x1) * (-perpy + y0));
+ var c2 = ((-perp1x + x2) * (-perp1y + y1)) - ((-perp1x + x1) * (-perp1y + y2));
+ var px = ((dx0 * c2) - (dx1 * c1)) / cross;
+ var py = ((dy1 * c1) - (dy0 * c2)) / cross;
+ var pdist = ((px - x1) * (px - x1)) + ((py - y1) * (py - y1));
+ /* Inner miter point */
+ var imx = x1 + ((px - x1) * innerWeight);
+ var imy = y1 + ((py - y1) * innerWeight);
+ /* Outer miter point */
+ var omx = x1 - ((px - x1) * outerWeight);
+ var omy = y1 - ((py - y1) * outerWeight);
+ /* Is the inside miter point too far away, creating a spike? */
+ var smallerInsideSegmentSq = Math.min((dx0 * dx0) + (dy0 * dy0), (dx1 * dx1) + (dy1 * dy1));
+ var insideWeight = clockwise ? innerWeight : outerWeight;
+ var smallerInsideDiagonalSq = smallerInsideSegmentSq + (insideWeight * insideWeight * widthSquared);
+ var insideMiterOk = pdist <= smallerInsideDiagonalSq;
+ if (insideMiterOk) {
+ if (style.join === exports.LINE_JOIN.BEVEL || pdist / widthSquared > miterLimitSquared) {
+ if (clockwise) /* rotating at inner angle */ {
+ verts.push(imx, imy); // inner miter point
+ verts.push(x1 + (perpx * outerWeight), y1 + (perpy * outerWeight)); // first segment's outer vertex
+ verts.push(imx, imy); // inner miter point
+ verts.push(x1 + (perp1x * outerWeight), y1 + (perp1y * outerWeight)); // second segment's outer vertex
+ }
+ else /* rotating at outer angle */ {
+ verts.push(x1 - (perpx * innerWeight), y1 - (perpy * innerWeight)); // first segment's inner vertex
+ verts.push(omx, omy); // outer miter point
+ verts.push(x1 - (perp1x * innerWeight), y1 - (perp1y * innerWeight)); // second segment's outer vertex
+ verts.push(omx, omy); // outer miter point
+ }
+ indexCount += 2;
+ }
+ else if (style.join === exports.LINE_JOIN.ROUND) {
+ if (clockwise) /* arc is outside */ {
+ verts.push(imx, imy);
+ verts.push(x1 + (perpx * outerWeight), y1 + (perpy * outerWeight));
+ indexCount += round(x1, y1, x1 + (perpx * outerWeight), y1 + (perpy * outerWeight), x1 + (perp1x * outerWeight), y1 + (perp1y * outerWeight), verts, true) + 4;
+ verts.push(imx, imy);
+ verts.push(x1 + (perp1x * outerWeight), y1 + (perp1y * outerWeight));
+ }
+ else /* arc is inside */ {
+ verts.push(x1 - (perpx * innerWeight), y1 - (perpy * innerWeight));
+ verts.push(omx, omy);
+ indexCount += round(x1, y1, x1 - (perpx * innerWeight), y1 - (perpy * innerWeight), x1 - (perp1x * innerWeight), y1 - (perp1y * innerWeight), verts, false) + 4;
+ verts.push(x1 - (perp1x * innerWeight), y1 - (perp1y * innerWeight));
+ verts.push(omx, omy);
+ }
+ }
+ else {
+ verts.push(imx, imy);
+ verts.push(omx, omy);
+ }
+ }
+ else // inside miter is NOT ok
+ {
+ verts.push(x1 - (perpx * innerWeight), y1 - (perpy * innerWeight)); // first segment's inner vertex
+ verts.push(x1 + (perpx * outerWeight), y1 + (perpy * outerWeight)); // first segment's outer vertex
+ if (style.join === exports.LINE_JOIN.ROUND) {
+ if (clockwise) /* arc is outside */ {
+ indexCount += round(x1, y1, x1 + (perpx * outerWeight), y1 + (perpy * outerWeight), x1 + (perp1x * outerWeight), y1 + (perp1y * outerWeight), verts, true) + 2;
+ }
+ else /* arc is inside */ {
+ indexCount += round(x1, y1, x1 - (perpx * innerWeight), y1 - (perpy * innerWeight), x1 - (perp1x * innerWeight), y1 - (perp1y * innerWeight), verts, false) + 2;
+ }
+ }
+ else if (style.join === exports.LINE_JOIN.MITER && pdist / widthSquared <= miterLimitSquared) {
+ if (clockwise) {
+ verts.push(omx, omy); // inner miter point
+ verts.push(omx, omy); // inner miter point
+ }
+ else {
+ verts.push(imx, imy); // outer miter point
+ verts.push(imx, imy); // outer miter point
+ }
+ indexCount += 2;
+ }
+ verts.push(x1 - (perp1x * innerWeight), y1 - (perp1y * innerWeight)); // second segment's inner vertex
+ verts.push(x1 + (perp1x * outerWeight), y1 + (perp1y * outerWeight)); // second segment's outer vertex
+ indexCount += 2;
+ }
+ }
+ x0 = points[(length - 2) * 2];
+ y0 = points[((length - 2) * 2) + 1];
+ x1 = points[(length - 1) * 2];
+ y1 = points[((length - 1) * 2) + 1];
+ perpx = -(y0 - y1);
+ perpy = x0 - x1;
+ dist = Math.sqrt((perpx * perpx) + (perpy * perpy));
+ perpx /= dist;
+ perpy /= dist;
+ perpx *= width;
+ perpy *= width;
+ verts.push(x1 - (perpx * innerWeight), y1 - (perpy * innerWeight));
+ verts.push(x1 + (perpx * outerWeight), y1 + (perpy * outerWeight));
+ if (!closedShape) {
+ if (style.cap === exports.LINE_CAP.ROUND) {
+ indexCount += round(x1 - (perpx * (innerWeight - outerWeight) * 0.5), y1 - (perpy * (innerWeight - outerWeight) * 0.5), x1 - (perpx * innerWeight), y1 - (perpy * innerWeight), x1 + (perpx * outerWeight), y1 + (perpy * outerWeight), verts, false) + 2;
+ }
+ else if (style.cap === exports.LINE_CAP.SQUARE) {
+ indexCount += square(x1, y1, perpx, perpy, innerWeight, outerWeight, false, verts);
+ }
+ }
+ var indices = graphicsGeometry.indices;
+ var eps2 = GRAPHICS_CURVES.epsilon * GRAPHICS_CURVES.epsilon;
+ // indices.push(indexStart);
+ for (var i = indexStart; i < indexCount + indexStart - 2; ++i) {
+ x0 = verts[(i * 2)];
+ y0 = verts[(i * 2) + 1];
+ x1 = verts[(i + 1) * 2];
+ y1 = verts[((i + 1) * 2) + 1];
+ x2 = verts[(i + 2) * 2];
+ y2 = verts[((i + 2) * 2) + 1];
+ /* Skip zero area triangles */
+ if (Math.abs((x0 * (y1 - y2)) + (x1 * (y2 - y0)) + (x2 * (y0 - y1))) < eps2) {
+ continue;
+ }
+ indices.push(i, i + 1, i + 2);
+ }
+ }
+ /**
+ * Builds a line to draw using the gl.drawArrays(gl.LINES) method
+ *
+ * Ignored from docs since it is not directly exposed.
+ * @ignore
+ * @private
+ * @param {PIXI.GraphicsData} graphicsData - The graphics object containing all the necessary properties
+ * @param {PIXI.GraphicsGeometry} graphicsGeometry - Geometry where to append output
+ */
+ function buildNativeLine(graphicsData, graphicsGeometry) {
+ var i = 0;
+ var shape = graphicsData.shape;
+ var points = graphicsData.points || shape.points;
+ var closedShape = shape.type !== exports.SHAPES.POLY || shape.closeStroke;
+ if (points.length === 0)
+ { return; }
+ var verts = graphicsGeometry.points;
+ var indices = graphicsGeometry.indices;
+ var length = points.length / 2;
+ var startIndex = verts.length / 2;
+ var currentIndex = startIndex;
+ verts.push(points[0], points[1]);
+ for (i = 1; i < length; i++) {
+ verts.push(points[i * 2], points[(i * 2) + 1]);
+ indices.push(currentIndex, currentIndex + 1);
+ currentIndex++;
+ }
+ if (closedShape) {
+ indices.push(currentIndex, startIndex);
+ }
+ }
+ /**
+ * Builds a line to draw
+ *
+ * Ignored from docs since it is not directly exposed.
+ * @ignore
+ * @private
+ * @param {PIXI.GraphicsData} graphicsData - The graphics object containing all the necessary properties
+ * @param {PIXI.GraphicsGeometry} graphicsGeometry - Geometry where to append output
+ */
+ function buildLine(graphicsData, graphicsGeometry) {
+ if (graphicsData.lineStyle.native) {
+ buildNativeLine(graphicsData, graphicsGeometry);
+ }
+ else {
+ buildNonNativeLine(graphicsData, graphicsGeometry);
+ }
+ }
+
+ /**
+ * Utilities for arc curves.
+ * @private
+ */
+ var ArcUtils = /** @class */ (function () {
+ function ArcUtils() {
+ }
+ /**
+ * The arcTo() method creates an arc/curve between two tangents on the canvas.
+ *
+ * "borrowed" from https://code.google.com/p/fxcanvas/ - thanks google!
+ * @private
+ * @param x1 - The x-coordinate of the beginning of the arc
+ * @param y1 - The y-coordinate of the beginning of the arc
+ * @param x2 - The x-coordinate of the end of the arc
+ * @param y2 - The y-coordinate of the end of the arc
+ * @param radius - The radius of the arc
+ * @param points -
+ * @returns - If the arc length is valid, return center of circle, radius and other info otherwise `null`.
+ */
+ ArcUtils.curveTo = function (x1, y1, x2, y2, radius, points) {
+ var fromX = points[points.length - 2];
+ var fromY = points[points.length - 1];
+ var a1 = fromY - y1;
+ var b1 = fromX - x1;
+ var a2 = y2 - y1;
+ var b2 = x2 - x1;
+ var mm = Math.abs((a1 * b2) - (b1 * a2));
+ if (mm < 1.0e-8 || radius === 0) {
+ if (points[points.length - 2] !== x1 || points[points.length - 1] !== y1) {
+ points.push(x1, y1);
+ }
+ return null;
+ }
+ var dd = (a1 * a1) + (b1 * b1);
+ var cc = (a2 * a2) + (b2 * b2);
+ var tt = (a1 * a2) + (b1 * b2);
+ var k1 = radius * Math.sqrt(dd) / mm;
+ var k2 = radius * Math.sqrt(cc) / mm;
+ var j1 = k1 * tt / dd;
+ var j2 = k2 * tt / cc;
+ var cx = (k1 * b2) + (k2 * b1);
+ var cy = (k1 * a2) + (k2 * a1);
+ var px = b1 * (k2 + j1);
+ var py = a1 * (k2 + j1);
+ var qx = b2 * (k1 + j2);
+ var qy = a2 * (k1 + j2);
+ var startAngle = Math.atan2(py - cy, px - cx);
+ var endAngle = Math.atan2(qy - cy, qx - cx);
+ return {
+ cx: (cx + x1),
+ cy: (cy + y1),
+ radius: radius,
+ startAngle: startAngle,
+ endAngle: endAngle,
+ anticlockwise: (b1 * a2 > b2 * a1),
+ };
+ };
+ /* eslint-disable max-len */
+ /**
+ * The arc method creates an arc/curve (used to create circles, or parts of circles).
+ * @private
+ * @param _startX - Start x location of arc
+ * @param _startY - Start y location of arc
+ * @param cx - The x-coordinate of the center of the circle
+ * @param cy - The y-coordinate of the center of the circle
+ * @param radius - The radius of the circle
+ * @param startAngle - The starting angle, in radians (0 is at the 3 o'clock position
+ * of the arc's circle)
+ * @param endAngle - The ending angle, in radians
+ * @param _anticlockwise - Specifies whether the drawing should be
+ * counter-clockwise or clockwise. False is default, and indicates clockwise, while true
+ * indicates counter-clockwise.
+ * @param points - Collection of points to add to
+ */
+ ArcUtils.arc = function (_startX, _startY, cx, cy, radius, startAngle, endAngle, _anticlockwise, points) {
+ var sweep = endAngle - startAngle;
+ var n = GRAPHICS_CURVES._segmentsCount(Math.abs(sweep) * radius, Math.ceil(Math.abs(sweep) / PI_2) * 40);
+ var theta = (sweep) / (n * 2);
+ var theta2 = theta * 2;
+ var cTheta = Math.cos(theta);
+ var sTheta = Math.sin(theta);
+ var segMinus = n - 1;
+ var remainder = (segMinus % 1) / segMinus;
+ for (var i = 0; i <= segMinus; ++i) {
+ var real = i + (remainder * i);
+ var angle = ((theta) + startAngle + (theta2 * real));
+ var c = Math.cos(angle);
+ var s = -Math.sin(angle);
+ points.push((((cTheta * c) + (sTheta * s)) * radius) + cx, (((cTheta * -s) + (sTheta * c)) * radius) + cy);
+ }
+ };
+ return ArcUtils;
+ }());
+
+ /**
+ * Utilities for bezier curves
+ * @private
+ */
+ var BezierUtils = /** @class */ (function () {
+ function BezierUtils() {
+ }
+ /**
+ * Calculate length of bezier curve.
+ * Analytical solution is impossible, since it involves an integral that does not integrate in general.
+ * Therefore numerical solution is used.
+ * @private
+ * @param fromX - Starting point x
+ * @param fromY - Starting point y
+ * @param cpX - Control point x
+ * @param cpY - Control point y
+ * @param cpX2 - Second Control point x
+ * @param cpY2 - Second Control point y
+ * @param toX - Destination point x
+ * @param toY - Destination point y
+ * @returns - Length of bezier curve
+ */
+ BezierUtils.curveLength = function (fromX, fromY, cpX, cpY, cpX2, cpY2, toX, toY) {
+ var n = 10;
+ var result = 0.0;
+ var t = 0.0;
+ var t2 = 0.0;
+ var t3 = 0.0;
+ var nt = 0.0;
+ var nt2 = 0.0;
+ var nt3 = 0.0;
+ var x = 0.0;
+ var y = 0.0;
+ var dx = 0.0;
+ var dy = 0.0;
+ var prevX = fromX;
+ var prevY = fromY;
+ for (var i = 1; i <= n; ++i) {
+ t = i / n;
+ t2 = t * t;
+ t3 = t2 * t;
+ nt = (1.0 - t);
+ nt2 = nt * nt;
+ nt3 = nt2 * nt;
+ x = (nt3 * fromX) + (3.0 * nt2 * t * cpX) + (3.0 * nt * t2 * cpX2) + (t3 * toX);
+ y = (nt3 * fromY) + (3.0 * nt2 * t * cpY) + (3 * nt * t2 * cpY2) + (t3 * toY);
+ dx = prevX - x;
+ dy = prevY - y;
+ prevX = x;
+ prevY = y;
+ result += Math.sqrt((dx * dx) + (dy * dy));
+ }
+ return result;
+ };
+ /**
+ * Calculate the points for a bezier curve and then draws it.
+ *
+ * Ignored from docs since it is not directly exposed.
+ * @ignore
+ * @param cpX - Control point x
+ * @param cpY - Control point y
+ * @param cpX2 - Second Control point x
+ * @param cpY2 - Second Control point y
+ * @param toX - Destination point x
+ * @param toY - Destination point y
+ * @param points - Path array to push points into
+ */
+ BezierUtils.curveTo = function (cpX, cpY, cpX2, cpY2, toX, toY, points) {
+ var fromX = points[points.length - 2];
+ var fromY = points[points.length - 1];
+ points.length -= 2;
+ var n = GRAPHICS_CURVES._segmentsCount(BezierUtils.curveLength(fromX, fromY, cpX, cpY, cpX2, cpY2, toX, toY));
+ var dt = 0;
+ var dt2 = 0;
+ var dt3 = 0;
+ var t2 = 0;
+ var t3 = 0;
+ points.push(fromX, fromY);
+ for (var i = 1, j = 0; i <= n; ++i) {
+ j = i / n;
+ dt = (1 - j);
+ dt2 = dt * dt;
+ dt3 = dt2 * dt;
+ t2 = j * j;
+ t3 = t2 * j;
+ points.push((dt3 * fromX) + (3 * dt2 * j * cpX) + (3 * dt * t2 * cpX2) + (t3 * toX), (dt3 * fromY) + (3 * dt2 * j * cpY) + (3 * dt * t2 * cpY2) + (t3 * toY));
+ }
+ };
+ return BezierUtils;
+ }());
+
+ /**
+ * Utilities for quadratic curves.
+ * @private
+ */
+ var QuadraticUtils = /** @class */ (function () {
+ function QuadraticUtils() {
+ }
+ /**
+ * Calculate length of quadratic curve
+ * @see {@link http://www.malczak.linuxpl.com/blog/quadratic-bezier-curve-length/}
+ * for the detailed explanation of math behind this.
+ * @private
+ * @param fromX - x-coordinate of curve start point
+ * @param fromY - y-coordinate of curve start point
+ * @param cpX - x-coordinate of curve control point
+ * @param cpY - y-coordinate of curve control point
+ * @param toX - x-coordinate of curve end point
+ * @param toY - y-coordinate of curve end point
+ * @returns - Length of quadratic curve
+ */
+ QuadraticUtils.curveLength = function (fromX, fromY, cpX, cpY, toX, toY) {
+ var ax = fromX - (2.0 * cpX) + toX;
+ var ay = fromY - (2.0 * cpY) + toY;
+ var bx = (2.0 * cpX) - (2.0 * fromX);
+ var by = (2.0 * cpY) - (2.0 * fromY);
+ var a = 4.0 * ((ax * ax) + (ay * ay));
+ var b = 4.0 * ((ax * bx) + (ay * by));
+ var c = (bx * bx) + (by * by);
+ var s = 2.0 * Math.sqrt(a + b + c);
+ var a2 = Math.sqrt(a);
+ var a32 = 2.0 * a * a2;
+ var c2 = 2.0 * Math.sqrt(c);
+ var ba = b / a2;
+ return ((a32 * s)
+ + (a2 * b * (s - c2))
+ + (((4.0 * c * a) - (b * b))
+ * Math.log(((2.0 * a2) + ba + s) / (ba + c2)))) / (4.0 * a32);
+ };
+ /**
+ * Calculate the points for a quadratic bezier curve and then draws it.
+ * Based on: https://stackoverflow.com/questions/785097/how-do-i-implement-a-bezier-curve-in-c
+ * @private
+ * @param cpX - Control point x
+ * @param cpY - Control point y
+ * @param toX - Destination point x
+ * @param toY - Destination point y
+ * @param points - Points to add segments to.
+ */
+ QuadraticUtils.curveTo = function (cpX, cpY, toX, toY, points) {
+ var fromX = points[points.length - 2];
+ var fromY = points[points.length - 1];
+ var n = GRAPHICS_CURVES._segmentsCount(QuadraticUtils.curveLength(fromX, fromY, cpX, cpY, toX, toY));
+ var xa = 0;
+ var ya = 0;
+ for (var i = 1; i <= n; ++i) {
+ var j = i / n;
+ xa = fromX + ((cpX - fromX) * j);
+ ya = fromY + ((cpY - fromY) * j);
+ points.push(xa + (((cpX + ((toX - cpX) * j)) - xa) * j), ya + (((cpY + ((toY - cpY) * j)) - ya) * j));
+ }
+ };
+ return QuadraticUtils;
+ }());
+
+ /**
+ * A structure to hold interim batch objects for Graphics.
+ * @memberof PIXI.graphicsUtils
+ */
+ var BatchPart = /** @class */ (function () {
+ function BatchPart() {
+ this.reset();
+ }
+ /**
+ * Begin batch part.
+ * @param style
+ * @param startIndex
+ * @param attribStart
+ */
+ BatchPart.prototype.begin = function (style, startIndex, attribStart) {
+ this.reset();
+ this.style = style;
+ this.start = startIndex;
+ this.attribStart = attribStart;
+ };
+ /**
+ * End batch part.
+ * @param endIndex
+ * @param endAttrib
+ */
+ BatchPart.prototype.end = function (endIndex, endAttrib) {
+ this.attribSize = endAttrib - this.attribStart;
+ this.size = endIndex - this.start;
+ };
+ BatchPart.prototype.reset = function () {
+ this.style = null;
+ this.size = 0;
+ this.start = 0;
+ this.attribStart = 0;
+ this.attribSize = 0;
+ };
+ return BatchPart;
+ }());
+
+ /**
+ * Generalized convenience utilities for Graphics.
+ * @namespace graphicsUtils
+ * @memberof PIXI
+ */
+ var _a;
+ /**
+ * Map of fill commands for each shape type.
+ * @memberof PIXI.graphicsUtils
+ * @member {object} FILL_COMMANDS
+ */
+ var FILL_COMMANDS = (_a = {},
+ _a[exports.SHAPES.POLY] = buildPoly,
+ _a[exports.SHAPES.CIRC] = buildCircle,
+ _a[exports.SHAPES.ELIP] = buildCircle,
+ _a[exports.SHAPES.RECT] = buildRectangle,
+ _a[exports.SHAPES.RREC] = buildRoundedRectangle,
+ _a);
+ /**
+ * Batch pool, stores unused batches for preventing allocations.
+ * @memberof PIXI.graphicsUtils
+ * @member {Array<PIXI.graphicsUtils.BatchPart>} BATCH_POOL
+ */
+ var BATCH_POOL = [];
+ /**
+ * Draw call pool, stores unused draw calls for preventing allocations.
+ * @memberof PIXI.graphicsUtils
+ * @member {Array<PIXI.BatchDrawCall>} DRAW_CALL_POOL
+ */
+ var DRAW_CALL_POOL = [];
+
+ /**
+ * A class to contain data useful for Graphics objects
+ * @memberof PIXI
+ */
+ var GraphicsData = /** @class */ (function () {
+ /**
+ * @param {PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.Rectangle|PIXI.RoundedRectangle} shape - The shape object to draw.
+ * @param fillStyle - the width of the line to draw
+ * @param lineStyle - the color of the line to draw
+ * @param matrix - Transform matrix
+ */
+ function GraphicsData(shape, fillStyle, lineStyle, matrix) {
+ if (fillStyle === void 0) { fillStyle = null; }
+ if (lineStyle === void 0) { lineStyle = null; }
+ if (matrix === void 0) { matrix = null; }
+ /** The collection of points. */
+ this.points = [];
+ /** The collection of holes. */
+ this.holes = [];
+ this.shape = shape;
+ this.lineStyle = lineStyle;
+ this.fillStyle = fillStyle;
+ this.matrix = matrix;
+ this.type = shape.type;
+ }
+ /**
+ * Creates a new GraphicsData object with the same values as this one.
+ * @returns - Cloned GraphicsData object
+ */
+ GraphicsData.prototype.clone = function () {
+ return new GraphicsData(this.shape, this.fillStyle, this.lineStyle, this.matrix);
+ };
+ /** Destroys the Graphics data. */
+ GraphicsData.prototype.destroy = function () {
+ this.shape = null;
+ this.holes.length = 0;
+ this.holes = null;
+ this.points.length = 0;
+ this.points = null;
+ this.lineStyle = null;
+ this.fillStyle = null;
+ };
+ return GraphicsData;
+ }());
+
+ var tmpPoint = new Point();
+ /**
+ * The Graphics class contains methods used to draw primitive shapes such as lines, circles and
+ * rectangles to the display, and to color and fill them.
+ *
+ * GraphicsGeometry is designed to not be continually updating the geometry since it's expensive
+ * to re-tesselate using **earcut**. Consider using {@link PIXI.Mesh} for this use-case, it's much faster.
+ * @memberof PIXI
+ */
+ var GraphicsGeometry = /** @class */ (function (_super) {
+ __extends$e(GraphicsGeometry, _super);
+ // eslint-disable-next-line @typescript-eslint/no-useless-constructor
+ function GraphicsGeometry() {
+ var _this = _super.call(this) || this;
+ /** Minimal distance between points that are considered different. Affects line tesselation. */
+ _this.closePointEps = 1e-4;
+ /** Padding to add to the bounds. */
+ _this.boundsPadding = 0;
+ _this.uvsFloat32 = null;
+ _this.indicesUint16 = null;
+ _this.batchable = false;
+ /** An array of points to draw, 2 numbers per point */
+ _this.points = [];
+ /** The collection of colors */
+ _this.colors = [];
+ /** The UVs collection */
+ _this.uvs = [];
+ /** The indices of the vertices */
+ _this.indices = [];
+ /** Reference to the texture IDs. */
+ _this.textureIds = [];
+ /**
+ * The collection of drawn shapes.
+ * @member {PIXI.GraphicsData[]}
+ */
+ _this.graphicsData = [];
+ /**
+ * List of current draw calls drived from the batches.
+ * @member {PIXI.BatchDrawCall[]}
+ */
+ _this.drawCalls = [];
+ /** Batches need to regenerated if the geometry is updated. */
+ _this.batchDirty = -1;
+ /**
+ * Intermediate abstract format sent to batch system.
+ * Can be converted to drawCalls or to batchable objects.
+ * @member {PIXI.graphicsUtils.BatchPart[]}
+ */
+ _this.batches = [];
+ /** Used to detect if the graphics object has changed. */
+ _this.dirty = 0;
+ /** Used to check if the cache is dirty. */
+ _this.cacheDirty = -1;
+ /** Used to detect if we cleared the graphicsData. */
+ _this.clearDirty = 0;
+ /** Index of the last batched shape in the stack of calls. */
+ _this.shapeIndex = 0;
+ /** Cached bounds. */
+ _this._bounds = new Bounds();
+ /** The bounds dirty flag. */
+ _this.boundsDirty = -1;
+ return _this;
+ }
+ Object.defineProperty(GraphicsGeometry.prototype, "bounds", {
+ /**
+ * Get the current bounds of the graphic geometry.
+ * @readonly
+ */
+ get: function () {
+ this.updateBatches();
+ if (this.boundsDirty !== this.dirty) {
+ this.boundsDirty = this.dirty;
+ this.calculateBounds();
+ }
+ return this._bounds;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /** Call if you changed graphicsData manually. Empties all batch buffers. */
+ GraphicsGeometry.prototype.invalidate = function () {
+ this.boundsDirty = -1;
+ this.dirty++;
+ this.batchDirty++;
+ this.shapeIndex = 0;
+ this.points.length = 0;
+ this.colors.length = 0;
+ this.uvs.length = 0;
+ this.indices.length = 0;
+ this.textureIds.length = 0;
+ for (var i = 0; i < this.drawCalls.length; i++) {
+ this.drawCalls[i].texArray.clear();
+ DRAW_CALL_POOL.push(this.drawCalls[i]);
+ }
+ this.drawCalls.length = 0;
+ for (var i = 0; i < this.batches.length; i++) {
+ var batchPart = this.batches[i];
+ batchPart.reset();
+ BATCH_POOL.push(batchPart);
+ }
+ this.batches.length = 0;
+ };
+ /**
+ * Clears the graphics that were drawn to this Graphics object, and resets fill and line style settings.
+ * @returns - This GraphicsGeometry object. Good for chaining method calls
+ */
+ GraphicsGeometry.prototype.clear = function () {
+ if (this.graphicsData.length > 0) {
+ this.invalidate();
+ this.clearDirty++;
+ this.graphicsData.length = 0;
+ }
+ return this;
+ };
+ /**
+ * Draws the given shape to this Graphics object. Can be any of Circle, Rectangle, Ellipse, Line or Polygon.
+ * @param {PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.Rectangle|PIXI.RoundedRectangle} shape - The shape object to draw.
+ * @param fillStyle - Defines style of the fill.
+ * @param lineStyle - Defines style of the lines.
+ * @param matrix - Transform applied to the points of the shape.
+ * @returns - Returns geometry for chaining.
+ */
+ GraphicsGeometry.prototype.drawShape = function (shape, fillStyle, lineStyle, matrix) {
+ if (fillStyle === void 0) { fillStyle = null; }
+ if (lineStyle === void 0) { lineStyle = null; }
+ if (matrix === void 0) { matrix = null; }
+ var data = new GraphicsData(shape, fillStyle, lineStyle, matrix);
+ this.graphicsData.push(data);
+ this.dirty++;
+ return this;
+ };
+ /**
+ * Draws the given shape to this Graphics object. Can be any of Circle, Rectangle, Ellipse, Line or Polygon.
+ * @param {PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.Rectangle|PIXI.RoundedRectangle} shape - The shape object to draw.
+ * @param matrix - Transform applied to the points of the shape.
+ * @returns - Returns geometry for chaining.
+ */
+ GraphicsGeometry.prototype.drawHole = function (shape, matrix) {
+ if (matrix === void 0) { matrix = null; }
+ if (!this.graphicsData.length) {
+ return null;
+ }
+ var data = new GraphicsData(shape, null, null, matrix);
+ var lastShape = this.graphicsData[this.graphicsData.length - 1];
+ data.lineStyle = lastShape.lineStyle;
+ lastShape.holes.push(data);
+ this.dirty++;
+ return this;
+ };
+ /** Destroys the GraphicsGeometry object. */
+ GraphicsGeometry.prototype.destroy = function () {
+ _super.prototype.destroy.call(this);
+ // destroy each of the GraphicsData objects
+ for (var i = 0; i < this.graphicsData.length; ++i) {
+ this.graphicsData[i].destroy();
+ }
+ this.points.length = 0;
+ this.points = null;
+ this.colors.length = 0;
+ this.colors = null;
+ this.uvs.length = 0;
+ this.uvs = null;
+ this.indices.length = 0;
+ this.indices = null;
+ this.indexBuffer.destroy();
+ this.indexBuffer = null;
+ this.graphicsData.length = 0;
+ this.graphicsData = null;
+ this.drawCalls.length = 0;
+ this.drawCalls = null;
+ this.batches.length = 0;
+ this.batches = null;
+ this._bounds = null;
+ };
+ /**
+ * Check to see if a point is contained within this geometry.
+ * @param point - Point to check if it's contained.
+ * @returns {boolean} `true` if the point is contained within geometry.
+ */
+ GraphicsGeometry.prototype.containsPoint = function (point) {
+ var graphicsData = this.graphicsData;
+ for (var i = 0; i < graphicsData.length; ++i) {
+ var data = graphicsData[i];
+ if (!data.fillStyle.visible) {
+ continue;
+ }
+ // only deal with fills..
+ if (data.shape) {
+ if (data.matrix) {
+ data.matrix.applyInverse(point, tmpPoint);
+ }
+ else {
+ tmpPoint.copyFrom(point);
+ }
+ if (data.shape.contains(tmpPoint.x, tmpPoint.y)) {
+ var hitHole = false;
+ if (data.holes) {
+ for (var i_1 = 0; i_1 < data.holes.length; i_1++) {
+ var hole = data.holes[i_1];
+ if (hole.shape.contains(tmpPoint.x, tmpPoint.y)) {
+ hitHole = true;
+ break;
+ }
+ }
+ }
+ if (!hitHole) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ };
+ /**
+ * Generates intermediate batch data. Either gets converted to drawCalls
+ * or used to convert to batch objects directly by the Graphics object.
+ */
+ GraphicsGeometry.prototype.updateBatches = function () {
+ if (!this.graphicsData.length) {
+ this.batchable = true;
+ return;
+ }
+ if (!this.validateBatching()) {
+ return;
+ }
+ this.cacheDirty = this.dirty;
+ var uvs = this.uvs;
+ var graphicsData = this.graphicsData;
+ var batchPart = null;
+ var currentStyle = null;
+ if (this.batches.length > 0) {
+ batchPart = this.batches[this.batches.length - 1];
+ currentStyle = batchPart.style;
+ }
+ for (var i = this.shapeIndex; i < graphicsData.length; i++) {
+ this.shapeIndex++;
+ var data = graphicsData[i];
+ var fillStyle = data.fillStyle;
+ var lineStyle = data.lineStyle;
+ var command = FILL_COMMANDS[data.type];
+ // build out the shapes points..
+ command.build(data);
+ if (data.matrix) {
+ this.transformPoints(data.points, data.matrix);
+ }
+ if (fillStyle.visible || lineStyle.visible) {
+ this.processHoles(data.holes);
+ }
+ for (var j = 0; j < 2; j++) {
+ var style = (j === 0) ? fillStyle : lineStyle;
+ if (!style.visible)
+ { continue; }
+ var nextTexture = style.texture.baseTexture;
+ var index_1 = this.indices.length;
+ var attribIndex = this.points.length / 2;
+ nextTexture.wrapMode = exports.WRAP_MODES.REPEAT;
+ if (j === 0) {
+ this.processFill(data);
+ }
+ else {
+ this.processLine(data);
+ }
+ var size = (this.points.length / 2) - attribIndex;
+ if (size === 0)
+ { continue; }
+ // close batch if style is different
+ if (batchPart && !this._compareStyles(currentStyle, style)) {
+ batchPart.end(index_1, attribIndex);
+ batchPart = null;
+ }
+ // spawn new batch if its first batch or previous was closed
+ if (!batchPart) {
+ batchPart = BATCH_POOL.pop() || new BatchPart();
+ batchPart.begin(style, index_1, attribIndex);
+ this.batches.push(batchPart);
+ currentStyle = style;
+ }
+ this.addUvs(this.points, uvs, style.texture, attribIndex, size, style.matrix);
+ }
+ }
+ var index = this.indices.length;
+ var attrib = this.points.length / 2;
+ if (batchPart) {
+ batchPart.end(index, attrib);
+ }
+ if (this.batches.length === 0) {
+ // there are no visible styles in GraphicsData
+ // its possible that someone wants Graphics just for the bounds
+ this.batchable = true;
+ return;
+ }
+ var need32 = attrib > 0xffff;
+ // prevent allocation when length is same as buffer
+ if (this.indicesUint16 && this.indices.length === this.indicesUint16.length
+ && need32 === (this.indicesUint16.BYTES_PER_ELEMENT > 2)) {
+ this.indicesUint16.set(this.indices);
+ }
+ else {
+ this.indicesUint16 = need32 ? new Uint32Array(this.indices) : new Uint16Array(this.indices);
+ }
+ // TODO make this a const..
+ this.batchable = this.isBatchable();
+ if (this.batchable) {
+ this.packBatches();
+ }
+ else {
+ this.buildDrawCalls();
+ }
+ };
+ /**
+ * Affinity check
+ * @param styleA
+ * @param styleB
+ */
+ GraphicsGeometry.prototype._compareStyles = function (styleA, styleB) {
+ if (!styleA || !styleB) {
+ return false;
+ }
+ if (styleA.texture.baseTexture !== styleB.texture.baseTexture) {
+ return false;
+ }
+ if (styleA.color + styleA.alpha !== styleB.color + styleB.alpha) {
+ return false;
+ }
+ if (!!styleA.native !== !!styleB.native) {
+ return false;
+ }
+ return true;
+ };
+ /** Test geometry for batching process. */
+ GraphicsGeometry.prototype.validateBatching = function () {
+ if (this.dirty === this.cacheDirty || !this.graphicsData.length) {
+ return false;
+ }
+ for (var i = 0, l = this.graphicsData.length; i < l; i++) {
+ var data = this.graphicsData[i];
+ var fill = data.fillStyle;
+ var line = data.lineStyle;
+ if (fill && !fill.texture.baseTexture.valid)
+ { return false; }
+ if (line && !line.texture.baseTexture.valid)
+ { return false; }
+ }
+ return true;
+ };
+ /** Offset the indices so that it works with the batcher. */
+ GraphicsGeometry.prototype.packBatches = function () {
+ this.batchDirty++;
+ this.uvsFloat32 = new Float32Array(this.uvs);
+ var batches = this.batches;
+ for (var i = 0, l = batches.length; i < l; i++) {
+ var batch = batches[i];
+ for (var j = 0; j < batch.size; j++) {
+ var index = batch.start + j;
+ this.indicesUint16[index] = this.indicesUint16[index] - batch.attribStart;
+ }
+ }
+ };
+ /**
+ * Checks to see if this graphics geometry can be batched.
+ * Currently it needs to be small enough and not contain any native lines.
+ */
+ GraphicsGeometry.prototype.isBatchable = function () {
+ // prevent heavy mesh batching
+ if (this.points.length > 0xffff * 2) {
+ return false;
+ }
+ var batches = this.batches;
+ for (var i = 0; i < batches.length; i++) {
+ if (batches[i].style.native) {
+ return false;
+ }
+ }
+ return (this.points.length < GraphicsGeometry.BATCHABLE_SIZE * 2);
+ };
+ /** Converts intermediate batches data to drawCalls. */
+ GraphicsGeometry.prototype.buildDrawCalls = function () {
+ var TICK = ++BaseTexture._globalBatch;
+ for (var i = 0; i < this.drawCalls.length; i++) {
+ this.drawCalls[i].texArray.clear();
+ DRAW_CALL_POOL.push(this.drawCalls[i]);
+ }
+ this.drawCalls.length = 0;
+ var colors = this.colors;
+ var textureIds = this.textureIds;
+ var currentGroup = DRAW_CALL_POOL.pop();
+ if (!currentGroup) {
+ currentGroup = new BatchDrawCall();
+ currentGroup.texArray = new BatchTextureArray();
+ }
+ currentGroup.texArray.count = 0;
+ currentGroup.start = 0;
+ currentGroup.size = 0;
+ currentGroup.type = exports.DRAW_MODES.TRIANGLES;
+ var textureCount = 0;
+ var currentTexture = null;
+ var textureId = 0;
+ var native = false;
+ var drawMode = exports.DRAW_MODES.TRIANGLES;
+ var index = 0;
+ this.drawCalls.push(currentGroup);
+ // TODO - this can be simplified
+ for (var i = 0; i < this.batches.length; i++) {
+ var data = this.batches[i];
+ // TODO add some full on MAX_TEXTURE CODE..
+ var MAX_TEXTURES = 8;
+ // Forced cast for checking `native` without errors
+ var style = data.style;
+ var nextTexture = style.texture.baseTexture;
+ if (native !== !!style.native) {
+ native = !!style.native;
+ drawMode = native ? exports.DRAW_MODES.LINES : exports.DRAW_MODES.TRIANGLES;
+ // force the batch to break!
+ currentTexture = null;
+ textureCount = MAX_TEXTURES;
+ TICK++;
+ }
+ if (currentTexture !== nextTexture) {
+ currentTexture = nextTexture;
+ if (nextTexture._batchEnabled !== TICK) {
+ if (textureCount === MAX_TEXTURES) {
+ TICK++;
+ textureCount = 0;
+ if (currentGroup.size > 0) {
+ currentGroup = DRAW_CALL_POOL.pop();
+ if (!currentGroup) {
+ currentGroup = new BatchDrawCall();
+ currentGroup.texArray = new BatchTextureArray();
+ }
+ this.drawCalls.push(currentGroup);
+ }
+ currentGroup.start = index;
+ currentGroup.size = 0;
+ currentGroup.texArray.count = 0;
+ currentGroup.type = drawMode;
+ }
+ // TODO add this to the render part..
+ // Hack! Because texture has protected `touched`
+ nextTexture.touched = 1; // touch;
+ nextTexture._batchEnabled = TICK;
+ nextTexture._batchLocation = textureCount;
+ nextTexture.wrapMode = exports.WRAP_MODES.REPEAT;
+ currentGroup.texArray.elements[currentGroup.texArray.count++] = nextTexture;
+ textureCount++;
+ }
+ }
+ currentGroup.size += data.size;
+ index += data.size;
+ textureId = nextTexture._batchLocation;
+ this.addColors(colors, style.color, style.alpha, data.attribSize, data.attribStart);
+ this.addTextureIds(textureIds, textureId, data.attribSize, data.attribStart);
+ }
+ BaseTexture._globalBatch = TICK;
+ // upload..
+ // merge for now!
+ this.packAttributes();
+ };
+ /** Packs attributes to single buffer. */
+ GraphicsGeometry.prototype.packAttributes = function () {
+ var verts = this.points;
+ var uvs = this.uvs;
+ var colors = this.colors;
+ var textureIds = this.textureIds;
+ // verts are 2 positions.. so we * by 3 as there are 6 properties.. then 4 cos its bytes
+ var glPoints = new ArrayBuffer(verts.length * 3 * 4);
+ var f32 = new Float32Array(glPoints);
+ var u32 = new Uint32Array(glPoints);
+ var p = 0;
+ for (var i = 0; i < verts.length / 2; i++) {
+ f32[p++] = verts[i * 2];
+ f32[p++] = verts[(i * 2) + 1];
+ f32[p++] = uvs[i * 2];
+ f32[p++] = uvs[(i * 2) + 1];
+ u32[p++] = colors[i];
+ f32[p++] = textureIds[i];
+ }
+ this._buffer.update(glPoints);
+ this._indexBuffer.update(this.indicesUint16);
+ };
+ /**
+ * Process fill part of Graphics.
+ * @param data
+ */
+ GraphicsGeometry.prototype.processFill = function (data) {
+ if (data.holes.length) {
+ buildPoly.triangulate(data, this);
+ }
+ else {
+ var command = FILL_COMMANDS[data.type];
+ command.triangulate(data, this);
+ }
+ };
+ /**
+ * Process line part of Graphics.
+ * @param data
+ */
+ GraphicsGeometry.prototype.processLine = function (data) {
+ buildLine(data, this);
+ for (var i = 0; i < data.holes.length; i++) {
+ buildLine(data.holes[i], this);
+ }
+ };
+ /**
+ * Process the holes data.
+ * @param holes
+ */
+ GraphicsGeometry.prototype.processHoles = function (holes) {
+ for (var i = 0; i < holes.length; i++) {
+ var hole = holes[i];
+ var command = FILL_COMMANDS[hole.type];
+ command.build(hole);
+ if (hole.matrix) {
+ this.transformPoints(hole.points, hole.matrix);
+ }
+ }
+ };
+ /** Update the local bounds of the object. Expensive to use performance-wise. */
+ GraphicsGeometry.prototype.calculateBounds = function () {
+ var bounds = this._bounds;
+ bounds.clear();
+ bounds.addVertexData(this.points, 0, this.points.length);
+ bounds.pad(this.boundsPadding, this.boundsPadding);
+ };
+ /**
+ * Transform points using matrix.
+ * @param points - Points to transform
+ * @param matrix - Transform matrix
+ */
+ GraphicsGeometry.prototype.transformPoints = function (points, matrix) {
+ for (var i = 0; i < points.length / 2; i++) {
+ var x = points[(i * 2)];
+ var y = points[(i * 2) + 1];
+ points[(i * 2)] = (matrix.a * x) + (matrix.c * y) + matrix.tx;
+ points[(i * 2) + 1] = (matrix.b * x) + (matrix.d * y) + matrix.ty;
+ }
+ };
+ /**
+ * Add colors.
+ * @param colors - List of colors to add to
+ * @param color - Color to add
+ * @param alpha - Alpha to use
+ * @param size - Number of colors to add
+ * @param offset
+ */
+ GraphicsGeometry.prototype.addColors = function (colors, color, alpha, size, offset) {
+ if (offset === void 0) { offset = 0; }
+ // TODO use the premultiply bits Ivan added
+ var rgb = (color >> 16) + (color & 0xff00) + ((color & 0xff) << 16);
+ var rgba = premultiplyTint(rgb, alpha);
+ colors.length = Math.max(colors.length, offset + size);
+ for (var i = 0; i < size; i++) {
+ colors[offset + i] = rgba;
+ }
+ };
+ /**
+ * Add texture id that the shader/fragment wants to use.
+ * @param textureIds
+ * @param id
+ * @param size
+ * @param offset
+ */
+ GraphicsGeometry.prototype.addTextureIds = function (textureIds, id, size, offset) {
+ if (offset === void 0) { offset = 0; }
+ textureIds.length = Math.max(textureIds.length, offset + size);
+ for (var i = 0; i < size; i++) {
+ textureIds[offset + i] = id;
+ }
+ };
+ /**
+ * Generates the UVs for a shape.
+ * @param verts - Vertices
+ * @param uvs - UVs
+ * @param texture - Reference to Texture
+ * @param start - Index buffer start index.
+ * @param size - The size/length for index buffer.
+ * @param matrix - Optional transform for all points.
+ */
+ GraphicsGeometry.prototype.addUvs = function (verts, uvs, texture, start, size, matrix) {
+ if (matrix === void 0) { matrix = null; }
+ var index = 0;
+ var uvsStart = uvs.length;
+ var frame = texture.frame;
+ while (index < size) {
+ var x = verts[(start + index) * 2];
+ var y = verts[((start + index) * 2) + 1];
+ if (matrix) {
+ var nx = (matrix.a * x) + (matrix.c * y) + matrix.tx;
+ y = (matrix.b * x) + (matrix.d * y) + matrix.ty;
+ x = nx;
+ }
+ index++;
+ uvs.push(x / frame.width, y / frame.height);
+ }
+ var baseTexture = texture.baseTexture;
+ if (frame.width < baseTexture.width
+ || frame.height < baseTexture.height) {
+ this.adjustUvs(uvs, texture, uvsStart, size);
+ }
+ };
+ /**
+ * Modify uvs array according to position of texture region
+ * Does not work with rotated or trimmed textures
+ * @param uvs - array
+ * @param texture - region
+ * @param start - starting index for uvs
+ * @param size - how many points to adjust
+ */
+ GraphicsGeometry.prototype.adjustUvs = function (uvs, texture, start, size) {
+ var baseTexture = texture.baseTexture;
+ var eps = 1e-6;
+ var finish = start + (size * 2);
+ var frame = texture.frame;
+ var scaleX = frame.width / baseTexture.width;
+ var scaleY = frame.height / baseTexture.height;
+ var offsetX = frame.x / frame.width;
+ var offsetY = frame.y / frame.height;
+ var minX = Math.floor(uvs[start] + eps);
+ var minY = Math.floor(uvs[start + 1] + eps);
+ for (var i = start + 2; i < finish; i += 2) {
+ minX = Math.min(minX, Math.floor(uvs[i] + eps));
+ minY = Math.min(minY, Math.floor(uvs[i + 1] + eps));
+ }
+ offsetX -= minX;
+ offsetY -= minY;
+ for (var i = start; i < finish; i += 2) {
+ uvs[i] = (uvs[i] + offsetX) * scaleX;
+ uvs[i + 1] = (uvs[i + 1] + offsetY) * scaleY;
+ }
+ };
+ /**
+ * The maximum number of points to consider an object "batchable",
+ * able to be batched by the renderer's batch system.
+ \
+ */
+ GraphicsGeometry.BATCHABLE_SIZE = 100;
+ return GraphicsGeometry;
+ }(BatchGeometry));
+
+ /**
+ * Represents the line style for Graphics.
+ * @memberof PIXI
+ */
+ var LineStyle = /** @class */ (function (_super) {
+ __extends$e(LineStyle, _super);
+ function LineStyle() {
+ var _this = _super !== null && _super.apply(this, arguments) || this;
+ /** The width (thickness) of any lines drawn. */
+ _this.width = 0;
+ /** The alignment of any lines drawn (0.5 = middle, 1 = outer, 0 = inner). WebGL only. */
+ _this.alignment = 0.5;
+ /** If true the lines will be draw using LINES instead of TRIANGLE_STRIP. */
+ _this.native = false;
+ /**
+ * Line cap style.
+ * @member {PIXI.LINE_CAP}
+ * @default PIXI.LINE_CAP.BUTT
+ */
+ _this.cap = exports.LINE_CAP.BUTT;
+ /**
+ * Line join style.
+ * @member {PIXI.LINE_JOIN}
+ * @default PIXI.LINE_JOIN.MITER
+ */
+ _this.join = exports.LINE_JOIN.MITER;
+ /** Miter limit. */
+ _this.miterLimit = 10;
+ return _this;
+ }
+ /** Clones the object. */
+ LineStyle.prototype.clone = function () {
+ var obj = new LineStyle();
+ obj.color = this.color;
+ obj.alpha = this.alpha;
+ obj.texture = this.texture;
+ obj.matrix = this.matrix;
+ obj.visible = this.visible;
+ obj.width = this.width;
+ obj.alignment = this.alignment;
+ obj.native = this.native;
+ obj.cap = this.cap;
+ obj.join = this.join;
+ obj.miterLimit = this.miterLimit;
+ return obj;
+ };
+ /** Reset the line style to default. */
+ LineStyle.prototype.reset = function () {
+ _super.prototype.reset.call(this);
+ // Override default line style color
+ this.color = 0x0;
+ this.alignment = 0.5;
+ this.width = 0;
+ this.native = false;
+ };
+ return LineStyle;
+ }(FillStyle));
+
+ var temp = new Float32Array(3);
+ // a default shaders map used by graphics..
+ var DEFAULT_SHADERS = {};
+ /**
+ * The Graphics class is primarily used to render primitive shapes such as lines, circles and
+ * rectangles to the display, and to color and fill them. However, you can also use a Graphics
+ * object to build a list of primitives to use as a mask, or as a complex hitArea.
+ *
+ * Please note that due to legacy naming conventions, the behavior of some functions in this class
+ * can be confusing. Each call to `drawRect()`, `drawPolygon()`, etc. actually stores that primitive
+ * in the Geometry class's GraphicsGeometry object for later use in rendering or hit testing - the
+ * functions do not directly draw anything to the screen. Similarly, the `clear()` function doesn't
+ * change the screen, it simply resets the list of primitives, which can be useful if you want to
+ * rebuild the contents of an existing Graphics object.
+ *
+ * Once a GraphicsGeometry list is built, you can re-use it in other Geometry objects as
+ * an optimization, by passing it into a new Geometry object's constructor. Because of this
+ * ability, it's important to call `destroy()` on Geometry objects once you are done with them, to
+ * properly dereference each GraphicsGeometry and prevent memory leaks.
+ * @memberof PIXI
+ */
+ var Graphics = /** @class */ (function (_super) {
+ __extends$e(Graphics, _super);
+ /**
+ * @param geometry - Geometry to use, if omitted will create a new GraphicsGeometry instance.
+ */
+ function Graphics(geometry) {
+ if (geometry === void 0) { geometry = null; }
+ var _this = _super.call(this) || this;
+ /**
+ * Represents the vertex and fragment shaders that processes the geometry and runs on the GPU.
+ * Can be shared between multiple Graphics objects.
+ */
+ _this.shader = null;
+ /** Renderer plugin for batching */
+ _this.pluginName = 'batch';
+ /**
+ * Current path
+ * @readonly
+ */
+ _this.currentPath = null;
+ /** A collections of batches! These can be drawn by the renderer batch system. */
+ _this.batches = [];
+ /** Update dirty for limiting calculating tints for batches. */
+ _this.batchTint = -1;
+ /** Update dirty for limiting calculating batches.*/
+ _this.batchDirty = -1;
+ /** Copy of the object vertex data. */
+ _this.vertexData = null;
+ /** Current fill style. */
+ _this._fillStyle = new FillStyle();
+ /** Current line style. */
+ _this._lineStyle = new LineStyle();
+ /** Current shape transform matrix. */
+ _this._matrix = null;
+ /** Current hole mode is enabled. */
+ _this._holeMode = false;
+ /**
+ * Represents the WebGL state the Graphics required to render, excludes shader and geometry. E.g.,
+ * blend mode, culling, depth testing, direction of rendering triangles, backface, etc.
+ */
+ _this.state = State.for2d();
+ _this._geometry = geometry || new GraphicsGeometry();
+ _this._geometry.refCount++;
+ /**
+ * When cacheAsBitmap is set to true the graphics object will be rendered as if it was a sprite.
+ * This is useful if your graphics element does not change often, as it will speed up the rendering
+ * of the object in exchange for taking up texture memory. It is also useful if you need the graphics
+ * object to be anti-aliased, because it will be rendered using canvas. This is not recommended if
+ * you are constantly redrawing the graphics element.
+ * @name cacheAsBitmap
+ * @member {boolean}
+ * @memberof PIXI.Graphics#
+ * @default false
+ */
+ _this._transformID = -1;
+ // Set default
+ _this.tint = 0xFFFFFF;
+ _this.blendMode = exports.BLEND_MODES.NORMAL;
+ return _this;
+ }
+ Object.defineProperty(Graphics.prototype, "geometry", {
+ /**
+ * Includes vertex positions, face indices, normals, colors, UVs, and
+ * custom attributes within buffers, reducing the cost of passing all
+ * this data to the GPU. Can be shared between multiple Mesh or Graphics objects.
+ * @readonly
+ */
+ get: function () {
+ return this._geometry;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Creates a new Graphics object with the same values as this one.
+ * Note that only the geometry of the object is cloned, not its transform (position,scale,etc)
+ * @returns - A clone of the graphics object
+ */
+ Graphics.prototype.clone = function () {
+ this.finishPoly();
+ return new Graphics(this._geometry);
+ };
+ Object.defineProperty(Graphics.prototype, "blendMode", {
+ get: function () {
+ return this.state.blendMode;
+ },
+ /**
+ * The blend mode to be applied to the graphic shape. Apply a value of
+ * `PIXI.BLEND_MODES.NORMAL` to reset the blend mode. Note that, since each
+ * primitive in the GraphicsGeometry list is rendered sequentially, modes
+ * such as `PIXI.BLEND_MODES.ADD` and `PIXI.BLEND_MODES.MULTIPLY` will
+ * be applied per-primitive.
+ * @default PIXI.BLEND_MODES.NORMAL
+ */
+ set: function (value) {
+ this.state.blendMode = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Graphics.prototype, "tint", {
+ /**
+ * The tint applied to each graphic shape. This is a hex value. A value of
+ * 0xFFFFFF will remove any tint effect.
+ * @default 0xFFFFFF
+ */
+ get: function () {
+ return this._tint;
+ },
+ set: function (value) {
+ this._tint = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Graphics.prototype, "fill", {
+ /**
+ * The current fill style.
+ * @readonly
+ */
+ get: function () {
+ return this._fillStyle;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Graphics.prototype, "line", {
+ /**
+ * The current line style.
+ * @readonly
+ */
+ get: function () {
+ return this._lineStyle;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Graphics.prototype.lineStyle = function (options, color, alpha, alignment, native) {
+ if (options === void 0) { options = null; }
+ if (color === void 0) { color = 0x0; }
+ if (alpha === void 0) { alpha = 1; }
+ if (alignment === void 0) { alignment = 0.5; }
+ if (native === void 0) { native = false; }
+ // Support non-object params: (width, color, alpha, alignment, native)
+ if (typeof options === 'number') {
+ options = { width: options, color: color, alpha: alpha, alignment: alignment, native: native };
+ }
+ return this.lineTextureStyle(options);
+ };
+ /**
+ * Like line style but support texture for line fill.
+ * @param [options] - Collection of options for setting line style.
+ * @param {number} [options.width=0] - width of the line to draw, will update the objects stored style
+ * @param {PIXI.Texture} [options.texture=PIXI.Texture.WHITE] - Texture to use
+ * @param {number} [options.color=0x0] - color of the line to draw, will update the objects stored style.
+ * Default 0xFFFFFF if texture present.
+ * @param {number} [options.alpha=1] - alpha of the line to draw, will update the objects stored style
+ * @param {PIXI.Matrix} [options.matrix=null] - Texture matrix to transform texture
+ * @param {number} [options.alignment=0.5] - alignment of the line to draw, (0 = inner, 0.5 = middle, 1 = outer).
+ * WebGL only.
+ * @param {boolean} [options.native=false] - If true the lines will be draw using LINES instead of TRIANGLE_STRIP
+ * @param {PIXI.LINE_CAP}[options.cap=PIXI.LINE_CAP.BUTT] - line cap style
+ * @param {PIXI.LINE_JOIN}[options.join=PIXI.LINE_JOIN.MITER] - line join style
+ * @param {number}[options.miterLimit=10] - miter limit ratio
+ * @returns {PIXI.Graphics} This Graphics object. Good for chaining method calls
+ */
+ Graphics.prototype.lineTextureStyle = function (options) {
+ // Apply defaults
+ options = Object.assign({
+ width: 0,
+ texture: Texture.WHITE,
+ color: (options && options.texture) ? 0xFFFFFF : 0x0,
+ alpha: 1,
+ matrix: null,
+ alignment: 0.5,
+ native: false,
+ cap: exports.LINE_CAP.BUTT,
+ join: exports.LINE_JOIN.MITER,
+ miterLimit: 10,
+ }, options);
+ if (this.currentPath) {
+ this.startPoly();
+ }
+ var visible = options.width > 0 && options.alpha > 0;
+ if (!visible) {
+ this._lineStyle.reset();
+ }
+ else {
+ if (options.matrix) {
+ options.matrix = options.matrix.clone();
+ options.matrix.invert();
+ }
+ Object.assign(this._lineStyle, { visible: visible }, options);
+ }
+ return this;
+ };
+ /**
+ * Start a polygon object internally.
+ * @protected
+ */
+ Graphics.prototype.startPoly = function () {
+ if (this.currentPath) {
+ var points = this.currentPath.points;
+ var len = this.currentPath.points.length;
+ if (len > 2) {
+ this.drawShape(this.currentPath);
+ this.currentPath = new Polygon();
+ this.currentPath.closeStroke = false;
+ this.currentPath.points.push(points[len - 2], points[len - 1]);
+ }
+ }
+ else {
+ this.currentPath = new Polygon();
+ this.currentPath.closeStroke = false;
+ }
+ };
+ /**
+ * Finish the polygon object.
+ * @protected
+ */
+ Graphics.prototype.finishPoly = function () {
+ if (this.currentPath) {
+ if (this.currentPath.points.length > 2) {
+ this.drawShape(this.currentPath);
+ this.currentPath = null;
+ }
+ else {
+ this.currentPath.points.length = 0;
+ }
+ }
+ };
+ /**
+ * Moves the current drawing position to x, y.
+ * @param x - the X coordinate to move to
+ * @param y - the Y coordinate to move to
+ * @returns - This Graphics object. Good for chaining method calls
+ */
+ Graphics.prototype.moveTo = function (x, y) {
+ this.startPoly();
+ this.currentPath.points[0] = x;
+ this.currentPath.points[1] = y;
+ return this;
+ };
+ /**
+ * Draws a line using the current line style from the current drawing position to (x, y);
+ * The current drawing position is then set to (x, y).
+ * @param x - the X coordinate to draw to
+ * @param y - the Y coordinate to draw to
+ * @returns - This Graphics object. Good for chaining method calls
+ */
+ Graphics.prototype.lineTo = function (x, y) {
+ if (!this.currentPath) {
+ this.moveTo(0, 0);
+ }
+ // remove duplicates..
+ var points = this.currentPath.points;
+ var fromX = points[points.length - 2];
+ var fromY = points[points.length - 1];
+ if (fromX !== x || fromY !== y) {
+ points.push(x, y);
+ }
+ return this;
+ };
+ /**
+ * Initialize the curve
+ * @param x
+ * @param y
+ */
+ Graphics.prototype._initCurve = function (x, y) {
+ if (x === void 0) { x = 0; }
+ if (y === void 0) { y = 0; }
+ if (this.currentPath) {
+ if (this.currentPath.points.length === 0) {
+ this.currentPath.points = [x, y];
+ }
+ }
+ else {
+ this.moveTo(x, y);
+ }
+ };
+ /**
+ * Calculate the points for a quadratic bezier curve and then draws it.
+ * Based on: https://stackoverflow.com/questions/785097/how-do-i-implement-a-bezier-curve-in-c
+ * @param cpX - Control point x
+ * @param cpY - Control point y
+ * @param toX - Destination point x
+ * @param toY - Destination point y
+ * @returns - This Graphics object. Good for chaining method calls
+ */
+ Graphics.prototype.quadraticCurveTo = function (cpX, cpY, toX, toY) {
+ this._initCurve();
+ var points = this.currentPath.points;
+ if (points.length === 0) {
+ this.moveTo(0, 0);
+ }
+ QuadraticUtils.curveTo(cpX, cpY, toX, toY, points);
+ return this;
+ };
+ /**
+ * Calculate the points for a bezier curve and then draws it.
+ * @param cpX - Control point x
+ * @param cpY - Control point y
+ * @param cpX2 - Second Control point x
+ * @param cpY2 - Second Control point y
+ * @param toX - Destination point x
+ * @param toY - Destination point y
+ * @returns This Graphics object. Good for chaining method calls
+ */
+ Graphics.prototype.bezierCurveTo = function (cpX, cpY, cpX2, cpY2, toX, toY) {
+ this._initCurve();
+ BezierUtils.curveTo(cpX, cpY, cpX2, cpY2, toX, toY, this.currentPath.points);
+ return this;
+ };
+ /**
+ * The arcTo() method creates an arc/curve between two tangents on the canvas.
+ *
+ * "borrowed" from https://code.google.com/p/fxcanvas/ - thanks google!
+ * @param x1 - The x-coordinate of the first tangent point of the arc
+ * @param y1 - The y-coordinate of the first tangent point of the arc
+ * @param x2 - The x-coordinate of the end of the arc
+ * @param y2 - The y-coordinate of the end of the arc
+ * @param radius - The radius of the arc
+ * @returns - This Graphics object. Good for chaining method calls
+ */
+ Graphics.prototype.arcTo = function (x1, y1, x2, y2, radius) {
+ this._initCurve(x1, y1);
+ var points = this.currentPath.points;
+ var result = ArcUtils.curveTo(x1, y1, x2, y2, radius, points);
+ if (result) {
+ var cx = result.cx, cy = result.cy, radius_1 = result.radius, startAngle = result.startAngle, endAngle = result.endAngle, anticlockwise = result.anticlockwise;
+ this.arc(cx, cy, radius_1, startAngle, endAngle, anticlockwise);
+ }
+ return this;
+ };
+ /**
+ * The arc method creates an arc/curve (used to create circles, or parts of circles).
+ * @param cx - The x-coordinate of the center of the circle
+ * @param cy - The y-coordinate of the center of the circle
+ * @param radius - The radius of the circle
+ * @param startAngle - The starting angle, in radians (0 is at the 3 o'clock position
+ * of the arc's circle)
+ * @param endAngle - The ending angle, in radians
+ * @param anticlockwise - Specifies whether the drawing should be
+ * counter-clockwise or clockwise. False is default, and indicates clockwise, while true
+ * indicates counter-clockwise.
+ * @returns - This Graphics object. Good for chaining method calls
+ */
+ Graphics.prototype.arc = function (cx, cy, radius, startAngle, endAngle, anticlockwise) {
+ if (anticlockwise === void 0) { anticlockwise = false; }
+ if (startAngle === endAngle) {
+ return this;
+ }
+ if (!anticlockwise && endAngle <= startAngle) {
+ endAngle += PI_2;
+ }
+ else if (anticlockwise && startAngle <= endAngle) {
+ startAngle += PI_2;
+ }
+ var sweep = endAngle - startAngle;
+ if (sweep === 0) {
+ return this;
+ }
+ var startX = cx + (Math.cos(startAngle) * radius);
+ var startY = cy + (Math.sin(startAngle) * radius);
+ var eps = this._geometry.closePointEps;
+ // If the currentPath exists, take its points. Otherwise call `moveTo` to start a path.
+ var points = this.currentPath ? this.currentPath.points : null;
+ if (points) {
+ // TODO: make a better fix.
+ // We check how far our start is from the last existing point
+ var xDiff = Math.abs(points[points.length - 2] - startX);
+ var yDiff = Math.abs(points[points.length - 1] - startY);
+ if (xDiff < eps && yDiff < eps) { ; }
+ else {
+ points.push(startX, startY);
+ }
+ }
+ else {
+ this.moveTo(startX, startY);
+ points = this.currentPath.points;
+ }
+ ArcUtils.arc(startX, startY, cx, cy, radius, startAngle, endAngle, anticlockwise, points);
+ return this;
+ };
+ /**
+ * Specifies a simple one-color fill that subsequent calls to other Graphics methods
+ * (such as lineTo() or drawCircle()) use when drawing.
+ * @param color - the color of the fill
+ * @param alpha - the alpha of the fill
+ * @returns - This Graphics object. Good for chaining method calls
+ */
+ Graphics.prototype.beginFill = function (color, alpha) {
+ if (color === void 0) { color = 0; }
+ if (alpha === void 0) { alpha = 1; }
+ return this.beginTextureFill({ texture: Texture.WHITE, color: color, alpha: alpha });
+ };
+ /**
+ * Begin the texture fill
+ * @param options - Object object.
+ * @param {PIXI.Texture} [options.texture=PIXI.Texture.WHITE] - Texture to fill
+ * @param {number} [options.color=0xffffff] - Background to fill behind texture
+ * @param {number} [options.alpha=1] - Alpha of fill
+ * @param {PIXI.Matrix} [options.matrix=null] - Transform matrix
+ * @returns {PIXI.Graphics} This Graphics object. Good for chaining method calls
+ */
+ Graphics.prototype.beginTextureFill = function (options) {
+ // Apply defaults
+ options = Object.assign({
+ texture: Texture.WHITE,
+ color: 0xFFFFFF,
+ alpha: 1,
+ matrix: null,
+ }, options);
+ if (this.currentPath) {
+ this.startPoly();
+ }
+ var visible = options.alpha > 0;
+ if (!visible) {
+ this._fillStyle.reset();
+ }
+ else {
+ if (options.matrix) {
+ options.matrix = options.matrix.clone();
+ options.matrix.invert();
+ }
+ Object.assign(this._fillStyle, { visible: visible }, options);
+ }
+ return this;
+ };
+ /**
+ * Applies a fill to the lines and shapes that were added since the last call to the beginFill() method.
+ * @returns - This Graphics object. Good for chaining method calls
+ */
+ Graphics.prototype.endFill = function () {
+ this.finishPoly();
+ this._fillStyle.reset();
+ return this;
+ };
+ /**
+ * Draws a rectangle shape.
+ * @param x - The X coord of the top-left of the rectangle
+ * @param y - The Y coord of the top-left of the rectangle
+ * @param width - The width of the rectangle
+ * @param height - The height of the rectangle
+ * @returns - This Graphics object. Good for chaining method calls
+ */
+ Graphics.prototype.drawRect = function (x, y, width, height) {
+ return this.drawShape(new Rectangle(x, y, width, height));
+ };
+ /**
+ * Draw a rectangle shape with rounded/beveled corners.
+ * @param x - The X coord of the top-left of the rectangle
+ * @param y - The Y coord of the top-left of the rectangle
+ * @param width - The width of the rectangle
+ * @param height - The height of the rectangle
+ * @param radius - Radius of the rectangle corners
+ * @returns - This Graphics object. Good for chaining method calls
+ */
+ Graphics.prototype.drawRoundedRect = function (x, y, width, height, radius) {
+ return this.drawShape(new RoundedRectangle(x, y, width, height, radius));
+ };
+ /**
+ * Draws a circle.
+ * @param x - The X coordinate of the center of the circle
+ * @param y - The Y coordinate of the center of the circle
+ * @param radius - The radius of the circle
+ * @returns - This Graphics object. Good for chaining method calls
+ */
+ Graphics.prototype.drawCircle = function (x, y, radius) {
+ return this.drawShape(new Circle(x, y, radius));
+ };
+ /**
+ * Draws an ellipse.
+ * @param x - The X coordinate of the center of the ellipse
+ * @param y - The Y coordinate of the center of the ellipse
+ * @param width - The half width of the ellipse
+ * @param height - The half height of the ellipse
+ * @returns - This Graphics object. Good for chaining method calls
+ */
+ Graphics.prototype.drawEllipse = function (x, y, width, height) {
+ return this.drawShape(new Ellipse(x, y, width, height));
+ };
+ /**
+ * Draws a polygon using the given path.
+ * @param {number[]|PIXI.IPointData[]|PIXI.Polygon} path - The path data used to construct the polygon.
+ * @returns - This Graphics object. Good for chaining method calls
+ */
+ Graphics.prototype.drawPolygon = function () {
+ var arguments$1 = arguments;
+
+ var path = [];
+ for (var _i = 0; _i < arguments.length; _i++) {
+ path[_i] = arguments$1[_i];
+ }
+ var points;
+ var closeStroke = true; // !!this._fillStyle;
+ var poly = path[0];
+ // check if data has points..
+ if (poly.points) {
+ closeStroke = poly.closeStroke;
+ points = poly.points;
+ }
+ else if (Array.isArray(path[0])) {
+ points = path[0];
+ }
+ else {
+ points = path;
+ }
+ var shape = new Polygon(points);
+ shape.closeStroke = closeStroke;
+ this.drawShape(shape);
+ return this;
+ };
+ /**
+ * Draw any shape.
+ * @param {PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.Rectangle|PIXI.RoundedRectangle} shape - Shape to draw
+ * @returns - This Graphics object. Good for chaining method calls
+ */
+ Graphics.prototype.drawShape = function (shape) {
+ if (!this._holeMode) {
+ this._geometry.drawShape(shape, this._fillStyle.clone(), this._lineStyle.clone(), this._matrix);
+ }
+ else {
+ this._geometry.drawHole(shape, this._matrix);
+ }
+ return this;
+ };
+ /**
+ * Clears the graphics that were drawn to this Graphics object, and resets fill and line style settings.
+ * @returns - This Graphics object. Good for chaining method calls
+ */
+ Graphics.prototype.clear = function () {
+ this._geometry.clear();
+ this._lineStyle.reset();
+ this._fillStyle.reset();
+ this._boundsID++;
+ this._matrix = null;
+ this._holeMode = false;
+ this.currentPath = null;
+ return this;
+ };
+ /**
+ * True if graphics consists of one rectangle, and thus, can be drawn like a Sprite and
+ * masked with gl.scissor.
+ * @returns - True if only 1 rect.
+ */
+ Graphics.prototype.isFastRect = function () {
+ var data = this._geometry.graphicsData;
+ return data.length === 1
+ && data[0].shape.type === exports.SHAPES.RECT
+ && !data[0].matrix
+ && !data[0].holes.length
+ && !(data[0].lineStyle.visible && data[0].lineStyle.width);
+ };
+ /**
+ * Renders the object using the WebGL renderer
+ * @param renderer - The renderer
+ */
+ Graphics.prototype._render = function (renderer) {
+ this.finishPoly();
+ var geometry = this._geometry;
+ // batch part..
+ // batch it!
+ geometry.updateBatches();
+ if (geometry.batchable) {
+ if (this.batchDirty !== geometry.batchDirty) {
+ this._populateBatches();
+ }
+ this._renderBatched(renderer);
+ }
+ else {
+ // no batching...
+ renderer.batch.flush();
+ this._renderDirect(renderer);
+ }
+ };
+ /** Populating batches for rendering. */
+ Graphics.prototype._populateBatches = function () {
+ var geometry = this._geometry;
+ var blendMode = this.blendMode;
+ var len = geometry.batches.length;
+ this.batchTint = -1;
+ this._transformID = -1;
+ this.batchDirty = geometry.batchDirty;
+ this.batches.length = len;
+ this.vertexData = new Float32Array(geometry.points);
+ for (var i = 0; i < len; i++) {
+ var gI = geometry.batches[i];
+ var color = gI.style.color;
+ var vertexData = new Float32Array(this.vertexData.buffer, gI.attribStart * 4 * 2, gI.attribSize * 2);
+ var uvs = new Float32Array(geometry.uvsFloat32.buffer, gI.attribStart * 4 * 2, gI.attribSize * 2);
+ var indices = new Uint16Array(geometry.indicesUint16.buffer, gI.start * 2, gI.size);
+ var batch = {
+ vertexData: vertexData,
+ blendMode: blendMode,
+ indices: indices,
+ uvs: uvs,
+ _batchRGB: hex2rgb(color),
+ _tintRGB: color,
+ _texture: gI.style.texture,
+ alpha: gI.style.alpha,
+ worldAlpha: 1
+ };
+ this.batches[i] = batch;
+ }
+ };
+ /**
+ * Renders the batches using the BathedRenderer plugin
+ * @param renderer - The renderer
+ */
+ Graphics.prototype._renderBatched = function (renderer) {
+ if (!this.batches.length) {
+ return;
+ }
+ renderer.batch.setObjectRenderer(renderer.plugins[this.pluginName]);
+ this.calculateVertices();
+ this.calculateTints();
+ for (var i = 0, l = this.batches.length; i < l; i++) {
+ var batch = this.batches[i];
+ batch.worldAlpha = this.worldAlpha * batch.alpha;
+ renderer.plugins[this.pluginName].render(batch);
+ }
+ };
+ /**
+ * Renders the graphics direct
+ * @param renderer - The renderer
+ */
+ Graphics.prototype._renderDirect = function (renderer) {
+ var shader = this._resolveDirectShader(renderer);
+ var geometry = this._geometry;
+ var tint = this.tint;
+ var worldAlpha = this.worldAlpha;
+ var uniforms = shader.uniforms;
+ var drawCalls = geometry.drawCalls;
+ // lets set the transfomr
+ uniforms.translationMatrix = this.transform.worldTransform;
+ // and then lets set the tint..
+ uniforms.tint[0] = (((tint >> 16) & 0xFF) / 255) * worldAlpha;
+ uniforms.tint[1] = (((tint >> 8) & 0xFF) / 255) * worldAlpha;
+ uniforms.tint[2] = ((tint & 0xFF) / 255) * worldAlpha;
+ uniforms.tint[3] = worldAlpha;
+ // the first draw call, we can set the uniforms of the shader directly here.
+ // this means that we can tack advantage of the sync function of pixi!
+ // bind and sync uniforms..
+ // there is a way to optimise this..
+ renderer.shader.bind(shader);
+ renderer.geometry.bind(geometry, shader);
+ // set state..
+ renderer.state.set(this.state);
+ // then render the rest of them...
+ for (var i = 0, l = drawCalls.length; i < l; i++) {
+ this._renderDrawCallDirect(renderer, geometry.drawCalls[i]);
+ }
+ };
+ /**
+ * Renders specific DrawCall
+ * @param renderer
+ * @param drawCall
+ */
+ Graphics.prototype._renderDrawCallDirect = function (renderer, drawCall) {
+ var texArray = drawCall.texArray, type = drawCall.type, size = drawCall.size, start = drawCall.start;
+ var groupTextureCount = texArray.count;
+ for (var j = 0; j < groupTextureCount; j++) {
+ renderer.texture.bind(texArray.elements[j], j);
+ }
+ renderer.geometry.draw(type, size, start);
+ };
+ /**
+ * Resolves shader for direct rendering
+ * @param renderer - The renderer
+ */
+ Graphics.prototype._resolveDirectShader = function (renderer) {
+ var shader = this.shader;
+ var pluginName = this.pluginName;
+ if (!shader) {
+ // if there is no shader here, we can use the default shader.
+ // and that only gets created if we actually need it..
+ // but may be more than one plugins for graphics
+ if (!DEFAULT_SHADERS[pluginName]) {
+ var MAX_TEXTURES = renderer.plugins[pluginName].MAX_TEXTURES;
+ var sampleValues = new Int32Array(MAX_TEXTURES);
+ for (var i = 0; i < MAX_TEXTURES; i++) {
+ sampleValues[i] = i;
+ }
+ var uniforms = {
+ tint: new Float32Array([1, 1, 1, 1]),
+ translationMatrix: new Matrix(),
+ default: UniformGroup.from({ uSamplers: sampleValues }, true),
+ };
+ var program = renderer.plugins[pluginName]._shader.program;
+ DEFAULT_SHADERS[pluginName] = new Shader(program, uniforms);
+ }
+ shader = DEFAULT_SHADERS[pluginName];
+ }
+ return shader;
+ };
+ /** Retrieves the bounds of the graphic shape as a rectangle object. */
+ Graphics.prototype._calculateBounds = function () {
+ this.finishPoly();
+ var geometry = this._geometry;
+ // skipping when graphics is empty, like a container
+ if (!geometry.graphicsData.length) {
+ return;
+ }
+ var _a = geometry.bounds, minX = _a.minX, minY = _a.minY, maxX = _a.maxX, maxY = _a.maxY;
+ this._bounds.addFrame(this.transform, minX, minY, maxX, maxY);
+ };
+ /**
+ * Tests if a point is inside this graphics object
+ * @param point - the point to test
+ * @returns - the result of the test
+ */
+ Graphics.prototype.containsPoint = function (point) {
+ this.worldTransform.applyInverse(point, Graphics._TEMP_POINT);
+ return this._geometry.containsPoint(Graphics._TEMP_POINT);
+ };
+ /** Recalculate the tint by applying tint to batches using Graphics tint. */
+ Graphics.prototype.calculateTints = function () {
+ if (this.batchTint !== this.tint) {
+ this.batchTint = this.tint;
+ var tintRGB = hex2rgb(this.tint, temp);
+ for (var i = 0; i < this.batches.length; i++) {
+ var batch = this.batches[i];
+ var batchTint = batch._batchRGB;
+ var r = (tintRGB[0] * batchTint[0]) * 255;
+ var g = (tintRGB[1] * batchTint[1]) * 255;
+ var b = (tintRGB[2] * batchTint[2]) * 255;
+ // TODO Ivan, can this be done in one go?
+ var color = (r << 16) + (g << 8) + (b | 0);
+ batch._tintRGB = (color >> 16)
+ + (color & 0xff00)
+ + ((color & 0xff) << 16);
+ }
+ }
+ };
+ /** If there's a transform update or a change to the shape of the geometry, recalculate the vertices. */
+ Graphics.prototype.calculateVertices = function () {
+ var wtID = this.transform._worldID;
+ if (this._transformID === wtID) {
+ return;
+ }
+ this._transformID = wtID;
+ var wt = this.transform.worldTransform;
+ var a = wt.a;
+ var b = wt.b;
+ var c = wt.c;
+ var d = wt.d;
+ var tx = wt.tx;
+ var ty = wt.ty;
+ var data = this._geometry.points; // batch.vertexDataOriginal;
+ var vertexData = this.vertexData;
+ var count = 0;
+ for (var i = 0; i < data.length; i += 2) {
+ var x = data[i];
+ var y = data[i + 1];
+ vertexData[count++] = (a * x) + (c * y) + tx;
+ vertexData[count++] = (d * y) + (b * x) + ty;
+ }
+ };
+ /**
+ * Closes the current path.
+ * @returns - Returns itself.
+ */
+ Graphics.prototype.closePath = function () {
+ var currentPath = this.currentPath;
+ if (currentPath) {
+ // we don't need to add extra point in the end because buildLine will take care of that
+ currentPath.closeStroke = true;
+ // ensure that the polygon is completed, and is available for hit detection
+ // (even if the graphics is not rendered yet)
+ this.finishPoly();
+ }
+ return this;
+ };
+ /**
+ * Apply a matrix to the positional data.
+ * @param matrix - Matrix to use for transform current shape.
+ * @returns - Returns itself.
+ */
+ Graphics.prototype.setMatrix = function (matrix) {
+ this._matrix = matrix;
+ return this;
+ };
+ /**
+ * Begin adding holes to the last draw shape
+ * IMPORTANT: holes must be fully inside a shape to work
+ * Also weirdness ensues if holes overlap!
+ * Ellipses, Circles, Rectangles and Rounded Rectangles cannot be holes or host for holes in CanvasRenderer,
+ * please use `moveTo` `lineTo`, `quadraticCurveTo` if you rely on pixi-legacy bundle.
+ * @returns - Returns itself.
+ */
+ Graphics.prototype.beginHole = function () {
+ this.finishPoly();
+ this._holeMode = true;
+ return this;
+ };
+ /**
+ * End adding holes to the last draw shape.
+ * @returns - Returns itself.
+ */
+ Graphics.prototype.endHole = function () {
+ this.finishPoly();
+ this._holeMode = false;
+ return this;
+ };
+ /**
+ * Destroys the Graphics object.
+ * @param options - Options parameter. A boolean will act as if all
+ * options have been set to that value
+ * @param {boolean} [options.children=false] - if set to true, all the children will have
+ * their destroy method called as well. 'options' will be passed on to those calls.
+ * @param {boolean} [options.texture=false] - Only used for child Sprites if options.children is set to true
+ * Should it destroy the texture of the child sprite
+ * @param {boolean} [options.baseTexture=false] - Only used for child Sprites if options.children is set to true
+ * Should it destroy the base texture of the child sprite
+ */
+ Graphics.prototype.destroy = function (options) {
+ this._geometry.refCount--;
+ if (this._geometry.refCount === 0) {
+ this._geometry.dispose();
+ }
+ this._matrix = null;
+ this.currentPath = null;
+ this._lineStyle.destroy();
+ this._lineStyle = null;
+ this._fillStyle.destroy();
+ this._fillStyle = null;
+ this._geometry = null;
+ this.shader = null;
+ this.vertexData = null;
+ this.batches.length = 0;
+ this.batches = null;
+ _super.prototype.destroy.call(this, options);
+ };
+ /**
+ * New rendering behavior for rounded rectangles: circular arcs instead of quadratic bezier curves.
+ * In the next major release, we'll enable this by default.
+ */
+ Graphics.nextRoundedRectBehavior = false;
+ /**
+ * Temporary point to use for containsPoint.
+ * @private
+ */
+ Graphics._TEMP_POINT = new Point();
+ return Graphics;
+ }(Container));
+
+ var graphicsUtils = {
+ buildPoly: buildPoly,
+ buildCircle: buildCircle,
+ buildRectangle: buildRectangle,
+ buildRoundedRectangle: buildRoundedRectangle,
+ buildLine: buildLine,
+ ArcUtils: ArcUtils,
+ BezierUtils: BezierUtils,
+ QuadraticUtils: QuadraticUtils,
+ BatchPart: BatchPart,
+ FILL_COMMANDS: FILL_COMMANDS,
+ BATCH_POOL: BATCH_POOL,
+ DRAW_CALL_POOL: DRAW_CALL_POOL
+ };
+
+ /*!
+ * @pixi/sprite - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/sprite is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+ /* global Reflect, Promise */
+
+ var extendStatics$d = function(d, b) {
+ extendStatics$d = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
+ return extendStatics$d(d, b);
+ };
+
+ function __extends$d(d, b) {
+ extendStatics$d(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ }
+
+ var tempPoint$2 = new Point();
+ var indices = new Uint16Array([0, 1, 2, 0, 2, 3]);
+ /**
+ * The Sprite object is the base for all textured objects that are rendered to the screen
+ *
+ * A sprite can be created directly from an image like this:
+ *
+ * ```js
+ * let sprite = PIXI.Sprite.from('assets/image.png');
+ * ```
+ *
+ * The more efficient way to create sprites is using a {@link PIXI.Spritesheet},
+ * as swapping base textures when rendering to the screen is inefficient.
+ *
+ * ```js
+ * PIXI.Loader.shared.add("assets/spritesheet.json").load(setup);
+ *
+ * function setup() {
+ * let sheet = PIXI.Loader.shared.resources["assets/spritesheet.json"].spritesheet;
+ * let sprite = new PIXI.Sprite(sheet.textures["image.png"]);
+ * ...
+ * }
+ * ```
+ * @memberof PIXI
+ */
+ var Sprite = /** @class */ (function (_super) {
+ __extends$d(Sprite, _super);
+ /** @param texture - The texture for this sprite. */
+ function Sprite(texture) {
+ var _this = _super.call(this) || this;
+ _this._anchor = new ObservablePoint(_this._onAnchorUpdate, _this, (texture ? texture.defaultAnchor.x : 0), (texture ? texture.defaultAnchor.y : 0));
+ _this._texture = null;
+ _this._width = 0;
+ _this._height = 0;
+ _this._tint = null;
+ _this._tintRGB = null;
+ _this.tint = 0xFFFFFF;
+ _this.blendMode = exports.BLEND_MODES.NORMAL;
+ _this._cachedTint = 0xFFFFFF;
+ _this.uvs = null;
+ // call texture setter
+ _this.texture = texture || Texture.EMPTY;
+ _this.vertexData = new Float32Array(8);
+ _this.vertexTrimmedData = null;
+ _this._transformID = -1;
+ _this._textureID = -1;
+ _this._transformTrimmedID = -1;
+ _this._textureTrimmedID = -1;
+ // Batchable stuff..
+ // TODO could make this a mixin?
+ _this.indices = indices;
+ _this.pluginName = 'batch';
+ /**
+ * Used to fast check if a sprite is.. a sprite!
+ * @member {boolean}
+ */
+ _this.isSprite = true;
+ _this._roundPixels = settings$1.ROUND_PIXELS;
+ return _this;
+ }
+ /** When the texture is updated, this event will fire to update the scale and frame. */
+ Sprite.prototype._onTextureUpdate = function () {
+ this._textureID = -1;
+ this._textureTrimmedID = -1;
+ this._cachedTint = 0xFFFFFF;
+ // so if _width is 0 then width was not set..
+ if (this._width) {
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
+ }
+ if (this._height) {
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
+ }
+ };
+ /** Called when the anchor position updates. */
+ Sprite.prototype._onAnchorUpdate = function () {
+ this._transformID = -1;
+ this._transformTrimmedID = -1;
+ };
+ /** Calculates worldTransform * vertices, store it in vertexData. */
+ Sprite.prototype.calculateVertices = function () {
+ var texture = this._texture;
+ if (this._transformID === this.transform._worldID && this._textureID === texture._updateID) {
+ return;
+ }
+ // update texture UV here, because base texture can be changed without calling `_onTextureUpdate`
+ if (this._textureID !== texture._updateID) {
+ this.uvs = this._texture._uvs.uvsFloat32;
+ }
+ this._transformID = this.transform._worldID;
+ this._textureID = texture._updateID;
+ // set the vertex data
+ var wt = this.transform.worldTransform;
+ var a = wt.a;
+ var b = wt.b;
+ var c = wt.c;
+ var d = wt.d;
+ var tx = wt.tx;
+ var ty = wt.ty;
+ var vertexData = this.vertexData;
+ var trim = texture.trim;
+ var orig = texture.orig;
+ var anchor = this._anchor;
+ var w0 = 0;
+ var w1 = 0;
+ var h0 = 0;
+ var h1 = 0;
+ if (trim) {
+ // if the sprite is trimmed and is not a tilingsprite then we need to add the extra
+ // space before transforming the sprite coords.
+ w1 = trim.x - (anchor._x * orig.width);
+ w0 = w1 + trim.width;
+ h1 = trim.y - (anchor._y * orig.height);
+ h0 = h1 + trim.height;
+ }
+ else {
+ w1 = -anchor._x * orig.width;
+ w0 = w1 + orig.width;
+ h1 = -anchor._y * orig.height;
+ h0 = h1 + orig.height;
+ }
+ // xy
+ vertexData[0] = (a * w1) + (c * h1) + tx;
+ vertexData[1] = (d * h1) + (b * w1) + ty;
+ // xy
+ vertexData[2] = (a * w0) + (c * h1) + tx;
+ vertexData[3] = (d * h1) + (b * w0) + ty;
+ // xy
+ vertexData[4] = (a * w0) + (c * h0) + tx;
+ vertexData[5] = (d * h0) + (b * w0) + ty;
+ // xy
+ vertexData[6] = (a * w1) + (c * h0) + tx;
+ vertexData[7] = (d * h0) + (b * w1) + ty;
+ if (this._roundPixels) {
+ var resolution = settings$1.RESOLUTION;
+ for (var i = 0; i < vertexData.length; ++i) {
+ vertexData[i] = Math.round((vertexData[i] * resolution | 0) / resolution);
+ }
+ }
+ };
+ /**
+ * Calculates worldTransform * vertices for a non texture with a trim. store it in vertexTrimmedData.
+ *
+ * This is used to ensure that the true width and height of a trimmed texture is respected.
+ */
+ Sprite.prototype.calculateTrimmedVertices = function () {
+ if (!this.vertexTrimmedData) {
+ this.vertexTrimmedData = new Float32Array(8);
+ }
+ else if (this._transformTrimmedID === this.transform._worldID && this._textureTrimmedID === this._texture._updateID) {
+ return;
+ }
+ this._transformTrimmedID = this.transform._worldID;
+ this._textureTrimmedID = this._texture._updateID;
+ // lets do some special trim code!
+ var texture = this._texture;
+ var vertexData = this.vertexTrimmedData;
+ var orig = texture.orig;
+ var anchor = this._anchor;
+ // lets calculate the new untrimmed bounds..
+ var wt = this.transform.worldTransform;
+ var a = wt.a;
+ var b = wt.b;
+ var c = wt.c;
+ var d = wt.d;
+ var tx = wt.tx;
+ var ty = wt.ty;
+ var w1 = -anchor._x * orig.width;
+ var w0 = w1 + orig.width;
+ var h1 = -anchor._y * orig.height;
+ var h0 = h1 + orig.height;
+ // xy
+ vertexData[0] = (a * w1) + (c * h1) + tx;
+ vertexData[1] = (d * h1) + (b * w1) + ty;
+ // xy
+ vertexData[2] = (a * w0) + (c * h1) + tx;
+ vertexData[3] = (d * h1) + (b * w0) + ty;
+ // xy
+ vertexData[4] = (a * w0) + (c * h0) + tx;
+ vertexData[5] = (d * h0) + (b * w0) + ty;
+ // xy
+ vertexData[6] = (a * w1) + (c * h0) + tx;
+ vertexData[7] = (d * h0) + (b * w1) + ty;
+ };
+ /**
+ *
+ * Renders the object using the WebGL renderer
+ * @param renderer - The webgl renderer to use.
+ */
+ Sprite.prototype._render = function (renderer) {
+ this.calculateVertices();
+ renderer.batch.setObjectRenderer(renderer.plugins[this.pluginName]);
+ renderer.plugins[this.pluginName].render(this);
+ };
+ /** Updates the bounds of the sprite. */
+ Sprite.prototype._calculateBounds = function () {
+ var trim = this._texture.trim;
+ var orig = this._texture.orig;
+ // First lets check to see if the current texture has a trim..
+ if (!trim || (trim.width === orig.width && trim.height === orig.height)) {
+ // no trim! lets use the usual calculations..
+ this.calculateVertices();
+ this._bounds.addQuad(this.vertexData);
+ }
+ else {
+ // lets calculate a special trimmed bounds...
+ this.calculateTrimmedVertices();
+ this._bounds.addQuad(this.vertexTrimmedData);
+ }
+ };
+ /**
+ * Gets the local bounds of the sprite object.
+ * @param rect - Optional output rectangle.
+ * @returns The bounds.
+ */
+ Sprite.prototype.getLocalBounds = function (rect) {
+ // we can do a fast local bounds if the sprite has no children!
+ if (this.children.length === 0) {
+ if (!this._localBounds) {
+ this._localBounds = new Bounds();
+ }
+ this._localBounds.minX = this._texture.orig.width * -this._anchor._x;
+ this._localBounds.minY = this._texture.orig.height * -this._anchor._y;
+ this._localBounds.maxX = this._texture.orig.width * (1 - this._anchor._x);
+ this._localBounds.maxY = this._texture.orig.height * (1 - this._anchor._y);
+ if (!rect) {
+ if (!this._localBoundsRect) {
+ this._localBoundsRect = new Rectangle();
+ }
+ rect = this._localBoundsRect;
+ }
+ return this._localBounds.getRectangle(rect);
+ }
+ return _super.prototype.getLocalBounds.call(this, rect);
+ };
+ /**
+ * Tests if a point is inside this sprite
+ * @param point - the point to test
+ * @returns The result of the test
+ */
+ Sprite.prototype.containsPoint = function (point) {
+ this.worldTransform.applyInverse(point, tempPoint$2);
+ var width = this._texture.orig.width;
+ var height = this._texture.orig.height;
+ var x1 = -width * this.anchor.x;
+ var y1 = 0;
+ if (tempPoint$2.x >= x1 && tempPoint$2.x < x1 + width) {
+ y1 = -height * this.anchor.y;
+ if (tempPoint$2.y >= y1 && tempPoint$2.y < y1 + height) {
+ return true;
+ }
+ }
+ return false;
+ };
+ /**
+ * Destroys this sprite and optionally its texture and children.
+ * @param options - Options parameter. A boolean will act as if all options
+ * have been set to that value
+ * @param [options.children=false] - if set to true, all the children will have their destroy
+ * method called as well. 'options' will be passed on to those calls.
+ * @param [options.texture=false] - Should it destroy the current texture of the sprite as well
+ * @param [options.baseTexture=false] - Should it destroy the base texture of the sprite as well
+ */
+ Sprite.prototype.destroy = function (options) {
+ _super.prototype.destroy.call(this, options);
+ this._texture.off('update', this._onTextureUpdate, this);
+ this._anchor = null;
+ var destroyTexture = typeof options === 'boolean' ? options : options && options.texture;
+ if (destroyTexture) {
+ var destroyBaseTexture = typeof options === 'boolean' ? options : options && options.baseTexture;
+ this._texture.destroy(!!destroyBaseTexture);
+ }
+ this._texture = null;
+ };
+ // some helper functions..
+ /**
+ * Helper function that creates a new sprite based on the source you provide.
+ * The source can be - frame id, image url, video url, canvas element, video element, base texture
+ * @param {string|PIXI.Texture|HTMLCanvasElement|HTMLVideoElement} source - Source to create texture from
+ * @param {object} [options] - See {@link PIXI.BaseTexture}'s constructor for options.
+ * @returns The newly created sprite
+ */
+ Sprite.from = function (source, options) {
+ var texture = (source instanceof Texture)
+ ? source
+ : Texture.from(source, options);
+ return new Sprite(texture);
+ };
+ Object.defineProperty(Sprite.prototype, "roundPixels", {
+ get: function () {
+ return this._roundPixels;
+ },
+ /**
+ * If true PixiJS will Math.floor() x/y values when rendering, stopping pixel interpolation.
+ *
+ * Advantages can include sharper image quality (like text) and faster rendering on canvas.
+ * The main disadvantage is movement of objects may appear less smooth.
+ *
+ * To set the global default, change {@link PIXI.settings.ROUND_PIXELS}.
+ * @default false
+ */
+ set: function (value) {
+ if (this._roundPixels !== value) {
+ this._transformID = -1;
+ }
+ this._roundPixels = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Sprite.prototype, "width", {
+ /** The width of the sprite, setting this will actually modify the scale to achieve the value set. */
+ get: function () {
+ return Math.abs(this.scale.x) * this._texture.orig.width;
+ },
+ set: function (value) {
+ var s = sign(this.scale.x) || 1;
+ this.scale.x = s * value / this._texture.orig.width;
+ this._width = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Sprite.prototype, "height", {
+ /** The height of the sprite, setting this will actually modify the scale to achieve the value set. */
+ get: function () {
+ return Math.abs(this.scale.y) * this._texture.orig.height;
+ },
+ set: function (value) {
+ var s = sign(this.scale.y) || 1;
+ this.scale.y = s * value / this._texture.orig.height;
+ this._height = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Sprite.prototype, "anchor", {
+ /**
+ * The anchor sets the origin point of the sprite. The default value is taken from the {@link PIXI.Texture|Texture}
+ * and passed to the constructor.
+ *
+ * The default is `(0,0)`, this means the sprite's origin is the top left.
+ *
+ * Setting the anchor to `(0.5,0.5)` means the sprite's origin is centered.
+ *
+ * Setting the anchor to `(1,1)` would mean the sprite's origin point will be the bottom right corner.
+ *
+ * If you pass only single parameter, it will set both x and y to the same value as shown in the example below.
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.anchor.set(0.5); // This will set the origin to center. (0.5) is same as (0.5, 0.5).
+ */
+ get: function () {
+ return this._anchor;
+ },
+ set: function (value) {
+ this._anchor.copyFrom(value);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Sprite.prototype, "tint", {
+ /**
+ * The tint applied to the sprite. This is a hex value.
+ *
+ * A value of 0xFFFFFF will remove any tint effect.
+ * @default 0xFFFFFF
+ */
+ get: function () {
+ return this._tint;
+ },
+ set: function (value) {
+ this._tint = value;
+ this._tintRGB = (value >> 16) + (value & 0xff00) + ((value & 0xff) << 16);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Sprite.prototype, "texture", {
+ /** The texture that the sprite is using. */
+ get: function () {
+ return this._texture;
+ },
+ set: function (value) {
+ if (this._texture === value) {
+ return;
+ }
+ if (this._texture) {
+ this._texture.off('update', this._onTextureUpdate, this);
+ }
+ this._texture = value || Texture.EMPTY;
+ this._cachedTint = 0xFFFFFF;
+ this._textureID = -1;
+ this._textureTrimmedID = -1;
+ if (value) {
+ // wait for the texture to load
+ if (value.baseTexture.valid) {
+ this._onTextureUpdate();
+ }
+ else {
+ value.once('update', this._onTextureUpdate, this);
+ }
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return Sprite;
+ }(Container));
+
+ /*!
+ * @pixi/text - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/text is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+ /* global Reflect, Promise */
+
+ var extendStatics$c = function(d, b) {
+ extendStatics$c = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
+ return extendStatics$c(d, b);
+ };
+
+ function __extends$c(d, b) {
+ extendStatics$c(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ }
+
+ /**
+ * Constants that define the type of gradient on text.
+ * @static
+ * @constant
+ * @name TEXT_GRADIENT
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} LINEAR_VERTICAL Vertical gradient
+ * @property {number} LINEAR_HORIZONTAL Linear gradient
+ */
+ exports.TEXT_GRADIENT = void 0;
+ (function (TEXT_GRADIENT) {
+ TEXT_GRADIENT[TEXT_GRADIENT["LINEAR_VERTICAL"] = 0] = "LINEAR_VERTICAL";
+ TEXT_GRADIENT[TEXT_GRADIENT["LINEAR_HORIZONTAL"] = 1] = "LINEAR_HORIZONTAL";
+ })(exports.TEXT_GRADIENT || (exports.TEXT_GRADIENT = {}));
+
+ // disabling eslint for now, going to rewrite this in v5
+ var defaultStyle = {
+ align: 'left',
+ breakWords: false,
+ dropShadow: false,
+ dropShadowAlpha: 1,
+ dropShadowAngle: Math.PI / 6,
+ dropShadowBlur: 0,
+ dropShadowColor: 'black',
+ dropShadowDistance: 5,
+ fill: 'black',
+ fillGradientType: exports.TEXT_GRADIENT.LINEAR_VERTICAL,
+ fillGradientStops: [],
+ fontFamily: 'Arial',
+ fontSize: 26,
+ fontStyle: 'normal',
+ fontVariant: 'normal',
+ fontWeight: 'normal',
+ letterSpacing: 0,
+ lineHeight: 0,
+ lineJoin: 'miter',
+ miterLimit: 10,
+ padding: 0,
+ stroke: 'black',
+ strokeThickness: 0,
+ textBaseline: 'alphabetic',
+ trim: false,
+ whiteSpace: 'pre',
+ wordWrap: false,
+ wordWrapWidth: 100,
+ leading: 0,
+ };
+ var genericFontFamilies = [
+ 'serif',
+ 'sans-serif',
+ 'monospace',
+ 'cursive',
+ 'fantasy',
+ 'system-ui' ];
+ /**
+ * A TextStyle Object contains information to decorate a Text objects.
+ *
+ * An instance can be shared between multiple Text objects; then changing the style will update all text objects using it.
+ *
+ * A tool can be used to generate a text style [here](https://pixijs.io/pixi-text-style).
+ *
+ * @memberof PIXI
+ */
+ var TextStyle = /** @class */ (function () {
+ /**
+ * @param {object} [style] - The style parameters
+ * @param {string} [style.align='left'] - Alignment for multiline text ('left', 'center' or 'right'),
+ * does not affect single line text
+ * @param {boolean} [style.breakWords=false] - Indicates if lines can be wrapped within words, it
+ * needs wordWrap to be set to true
+ * @param {boolean} [style.dropShadow=false] - Set a drop shadow for the text
+ * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow
+ * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow
+ * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius
+ * @param {string|number} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00'
+ * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow
+ * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas
+ * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient
+ * eg ['#000000','#FFFFFF']
+ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN}
+ * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fill is an array of colours
+ * to create a gradient, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT}
+ * @param {number[]} [style.fillGradientStops] - If fill is an array of colours to create a gradient, this array can set
+ * the stop points (numbers between 0 and 1) for the color, overriding the default behaviour of evenly spacing them.
+ * @param {string|string[]} [style.fontFamily='Arial'] - The font family
+ * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string,
+ * equivalents are '26px','20pt','160%' or '1.6em')
+ * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique')
+ * @param {string} [style.fontVariant='normal'] - The font variant ('normal' or 'small-caps')
+ * @param {string} [style.fontWeight='normal'] - The font weight ('normal', 'bold', 'bolder', 'lighter' and '100',
+ * '200', '300', '400', '500', '600', '700', '800' or '900')
+ * @param {number} [style.leading=0] - The space between lines
+ * @param {number} [style.letterSpacing=0] - The amount of spacing between letters, default is 0
+ * @param {number} [style.lineHeight] - The line height, a number that represents the vertical space that a letter uses
+ * @param {string} [style.lineJoin='miter'] - The lineJoin property sets the type of corner created, it can resolve
+ * spiked text issues. Possible values "miter" (creates a sharp corner), "round" (creates a round corner) or "bevel"
+ * (creates a squared corner).
+ * @param {number} [style.miterLimit=10] - The miter limit to use when using the 'miter' lineJoin mode. This can reduce
+ * or increase the spikiness of rendered text.
+ * @param {number} [style.padding=0] - Occasionally some fonts are cropped. Adding some padding will prevent this from
+ * happening by adding padding to all sides of the text.
+ * @param {string|number} [style.stroke='black'] - A canvas fillstyle that will be used on the text stroke
+ * e.g 'blue', '#FCFF00'
+ * @param {number} [style.strokeThickness=0] - A number that represents the thickness of the stroke.
+ * Default is 0 (no stroke)
+ * @param {boolean} [style.trim=false] - Trim transparent borders
+ * @param {string} [style.textBaseline='alphabetic'] - The baseline of the text that is rendered.
+ * @param {string} [style.whiteSpace='pre'] - Determines whether newlines & spaces are collapsed or preserved "normal"
+ * (collapse, collapse), "pre" (preserve, preserve) | "pre-line" (preserve, collapse). It needs wordWrap to be set to true
+ * @param {boolean} [style.wordWrap=false] - Indicates if word wrap should be used
+ * @param {number} [style.wordWrapWidth=100] - The width at which text will wrap, it needs wordWrap to be set to true
+ */
+ function TextStyle(style) {
+ this.styleID = 0;
+ this.reset();
+ deepCopyProperties(this, style, style);
+ }
+ /**
+ * Creates a new TextStyle object with the same values as this one.
+ * Note that the only the properties of the object are cloned.
+ *
+ * @return New cloned TextStyle object
+ */
+ TextStyle.prototype.clone = function () {
+ var clonedProperties = {};
+ deepCopyProperties(clonedProperties, this, defaultStyle);
+ return new TextStyle(clonedProperties);
+ };
+ /** Resets all properties to the defaults specified in TextStyle.prototype._default */
+ TextStyle.prototype.reset = function () {
+ deepCopyProperties(this, defaultStyle, defaultStyle);
+ };
+ Object.defineProperty(TextStyle.prototype, "align", {
+ /**
+ * Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text
+ *
+ * @member {string}
+ */
+ get: function () {
+ return this._align;
+ },
+ set: function (align) {
+ if (this._align !== align) {
+ this._align = align;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "breakWords", {
+ /** Indicates if lines can be wrapped within words, it needs wordWrap to be set to true. */
+ get: function () {
+ return this._breakWords;
+ },
+ set: function (breakWords) {
+ if (this._breakWords !== breakWords) {
+ this._breakWords = breakWords;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "dropShadow", {
+ /** Set a drop shadow for the text. */
+ get: function () {
+ return this._dropShadow;
+ },
+ set: function (dropShadow) {
+ if (this._dropShadow !== dropShadow) {
+ this._dropShadow = dropShadow;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "dropShadowAlpha", {
+ /** Set alpha for the drop shadow. */
+ get: function () {
+ return this._dropShadowAlpha;
+ },
+ set: function (dropShadowAlpha) {
+ if (this._dropShadowAlpha !== dropShadowAlpha) {
+ this._dropShadowAlpha = dropShadowAlpha;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "dropShadowAngle", {
+ /** Set a angle of the drop shadow. */
+ get: function () {
+ return this._dropShadowAngle;
+ },
+ set: function (dropShadowAngle) {
+ if (this._dropShadowAngle !== dropShadowAngle) {
+ this._dropShadowAngle = dropShadowAngle;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "dropShadowBlur", {
+ /** Set a shadow blur radius. */
+ get: function () {
+ return this._dropShadowBlur;
+ },
+ set: function (dropShadowBlur) {
+ if (this._dropShadowBlur !== dropShadowBlur) {
+ this._dropShadowBlur = dropShadowBlur;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "dropShadowColor", {
+ /** A fill style to be used on the dropshadow e.g 'red', '#00FF00'. */
+ get: function () {
+ return this._dropShadowColor;
+ },
+ set: function (dropShadowColor) {
+ var outputColor = getColor(dropShadowColor);
+ if (this._dropShadowColor !== outputColor) {
+ this._dropShadowColor = outputColor;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "dropShadowDistance", {
+ /** Set a distance of the drop shadow. */
+ get: function () {
+ return this._dropShadowDistance;
+ },
+ set: function (dropShadowDistance) {
+ if (this._dropShadowDistance !== dropShadowDistance) {
+ this._dropShadowDistance = dropShadowDistance;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "fill", {
+ /**
+ * A canvas fillstyle that will be used on the text e.g 'red', '#00FF00'.
+ *
+ * Can be an array to create a gradient eg ['#000000','#FFFFFF']
+ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN}
+ *
+ * @member {string|string[]|number|number[]|CanvasGradient|CanvasPattern}
+ */
+ get: function () {
+ return this._fill;
+ },
+ set: function (fill) {
+ // TODO: Can't have different types for getter and setter. The getter shouldn't have the number type as
+ // the setter converts to string. See this thread for more details:
+ // https://github.com/microsoft/TypeScript/issues/2521
+ // TODO: Not sure if getColor works properly with CanvasGradient and/or CanvasPattern, can't pass in
+ // without casting here.
+ var outputColor = getColor(fill);
+ if (this._fill !== outputColor) {
+ this._fill = outputColor;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "fillGradientType", {
+ /**
+ * If fill is an array of colours to create a gradient, this can change the type/direction of the gradient.
+ *
+ * @see PIXI.TEXT_GRADIENT
+ */
+ get: function () {
+ return this._fillGradientType;
+ },
+ set: function (fillGradientType) {
+ if (this._fillGradientType !== fillGradientType) {
+ this._fillGradientType = fillGradientType;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "fillGradientStops", {
+ /**
+ * If fill is an array of colours to create a gradient, this array can set the stop points
+ * (numbers between 0 and 1) for the color, overriding the default behaviour of evenly spacing them.
+ */
+ get: function () {
+ return this._fillGradientStops;
+ },
+ set: function (fillGradientStops) {
+ if (!areArraysEqual(this._fillGradientStops, fillGradientStops)) {
+ this._fillGradientStops = fillGradientStops;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "fontFamily", {
+ /** The font family. */
+ get: function () {
+ return this._fontFamily;
+ },
+ set: function (fontFamily) {
+ if (this.fontFamily !== fontFamily) {
+ this._fontFamily = fontFamily;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "fontSize", {
+ /**
+ * The font size
+ * (as a number it converts to px, but as a string, equivalents are '26px','20pt','160%' or '1.6em')
+ */
+ get: function () {
+ return this._fontSize;
+ },
+ set: function (fontSize) {
+ if (this._fontSize !== fontSize) {
+ this._fontSize = fontSize;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "fontStyle", {
+ /**
+ * The font style
+ * ('normal', 'italic' or 'oblique')
+ *
+ * @member {string}
+ */
+ get: function () {
+ return this._fontStyle;
+ },
+ set: function (fontStyle) {
+ if (this._fontStyle !== fontStyle) {
+ this._fontStyle = fontStyle;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "fontVariant", {
+ /**
+ * The font variant
+ * ('normal' or 'small-caps')
+ *
+ * @member {string}
+ */
+ get: function () {
+ return this._fontVariant;
+ },
+ set: function (fontVariant) {
+ if (this._fontVariant !== fontVariant) {
+ this._fontVariant = fontVariant;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "fontWeight", {
+ /**
+ * The font weight
+ * ('normal', 'bold', 'bolder', 'lighter' and '100', '200', '300', '400', '500', '600', '700', 800' or '900')
+ *
+ * @member {string}
+ */
+ get: function () {
+ return this._fontWeight;
+ },
+ set: function (fontWeight) {
+ if (this._fontWeight !== fontWeight) {
+ this._fontWeight = fontWeight;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "letterSpacing", {
+ /** The amount of spacing between letters, default is 0. */
+ get: function () {
+ return this._letterSpacing;
+ },
+ set: function (letterSpacing) {
+ if (this._letterSpacing !== letterSpacing) {
+ this._letterSpacing = letterSpacing;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "lineHeight", {
+ /** The line height, a number that represents the vertical space that a letter uses. */
+ get: function () {
+ return this._lineHeight;
+ },
+ set: function (lineHeight) {
+ if (this._lineHeight !== lineHeight) {
+ this._lineHeight = lineHeight;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "leading", {
+ /** The space between lines. */
+ get: function () {
+ return this._leading;
+ },
+ set: function (leading) {
+ if (this._leading !== leading) {
+ this._leading = leading;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "lineJoin", {
+ /**
+ * The lineJoin property sets the type of corner created, it can resolve spiked text issues.
+ * Default is 'miter' (creates a sharp corner).
+ *
+ * @member {string}
+ */
+ get: function () {
+ return this._lineJoin;
+ },
+ set: function (lineJoin) {
+ if (this._lineJoin !== lineJoin) {
+ this._lineJoin = lineJoin;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "miterLimit", {
+ /**
+ * The miter limit to use when using the 'miter' lineJoin mode.
+ *
+ * This can reduce or increase the spikiness of rendered text.
+ */
+ get: function () {
+ return this._miterLimit;
+ },
+ set: function (miterLimit) {
+ if (this._miterLimit !== miterLimit) {
+ this._miterLimit = miterLimit;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "padding", {
+ /**
+ * Occasionally some fonts are cropped. Adding some padding will prevent this from happening
+ * by adding padding to all sides of the text.
+ */
+ get: function () {
+ return this._padding;
+ },
+ set: function (padding) {
+ if (this._padding !== padding) {
+ this._padding = padding;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "stroke", {
+ /**
+ * A canvas fillstyle that will be used on the text stroke
+ * e.g 'blue', '#FCFF00'
+ */
+ get: function () {
+ return this._stroke;
+ },
+ set: function (stroke) {
+ // TODO: Can't have different types for getter and setter. The getter shouldn't have the number type as
+ // the setter converts to string. See this thread for more details:
+ // https://github.com/microsoft/TypeScript/issues/2521
+ var outputColor = getColor(stroke);
+ if (this._stroke !== outputColor) {
+ this._stroke = outputColor;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "strokeThickness", {
+ /**
+ * A number that represents the thickness of the stroke.
+ *
+ * @default 0
+ */
+ get: function () {
+ return this._strokeThickness;
+ },
+ set: function (strokeThickness) {
+ if (this._strokeThickness !== strokeThickness) {
+ this._strokeThickness = strokeThickness;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "textBaseline", {
+ /**
+ * The baseline of the text that is rendered.
+ *
+ * @member {string}
+ */
+ get: function () {
+ return this._textBaseline;
+ },
+ set: function (textBaseline) {
+ if (this._textBaseline !== textBaseline) {
+ this._textBaseline = textBaseline;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "trim", {
+ /** Trim transparent borders. */
+ get: function () {
+ return this._trim;
+ },
+ set: function (trim) {
+ if (this._trim !== trim) {
+ this._trim = trim;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "whiteSpace", {
+ /**
+ * How newlines and spaces should be handled.
+ * Default is 'pre' (preserve, preserve).
+ *
+ * value | New lines | Spaces
+ * --- | --- | ---
+ * 'normal' | Collapse | Collapse
+ * 'pre' | Preserve | Preserve
+ * 'pre-line' | Preserve | Collapse
+ *
+ * @member {string}
+ */
+ get: function () {
+ return this._whiteSpace;
+ },
+ set: function (whiteSpace) {
+ if (this._whiteSpace !== whiteSpace) {
+ this._whiteSpace = whiteSpace;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "wordWrap", {
+ /** Indicates if word wrap should be used. */
+ get: function () {
+ return this._wordWrap;
+ },
+ set: function (wordWrap) {
+ if (this._wordWrap !== wordWrap) {
+ this._wordWrap = wordWrap;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextStyle.prototype, "wordWrapWidth", {
+ /** The width at which text will wrap, it needs wordWrap to be set to true. */
+ get: function () {
+ return this._wordWrapWidth;
+ },
+ set: function (wordWrapWidth) {
+ if (this._wordWrapWidth !== wordWrapWidth) {
+ this._wordWrapWidth = wordWrapWidth;
+ this.styleID++;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ TextStyle.prototype.toFontString = function () {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ var fontSizeString = (typeof this.fontSize === 'number') ? this.fontSize + "px" : this.fontSize;
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ var fontFamilies = this.fontFamily;
+ if (!Array.isArray(this.fontFamily)) {
+ fontFamilies = this.fontFamily.split(',');
+ }
+ for (var i = fontFamilies.length - 1; i >= 0; i--) {
+ // Trim any extra white-space
+ var fontFamily = fontFamilies[i].trim();
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily) && genericFontFamilies.indexOf(fontFamily) < 0) {
+ fontFamily = "\"" + fontFamily + "\"";
+ }
+ fontFamilies[i] = fontFamily;
+ }
+ return this.fontStyle + " " + this.fontVariant + " " + this.fontWeight + " " + fontSizeString + " " + fontFamilies.join(',');
+ };
+ return TextStyle;
+ }());
+ /**
+ * Utility function to convert hexadecimal colors to strings, and simply return the color if it's a string.
+ * @private
+ * @param color
+ * @return The color as a string.
+ */
+ function getSingleColor(color) {
+ if (typeof color === 'number') {
+ return hex2string(color);
+ }
+ else if (typeof color === 'string') {
+ if (color.indexOf('0x') === 0) {
+ color = color.replace('0x', '#');
+ }
+ }
+ return color;
+ }
+ function getColor(color) {
+ if (!Array.isArray(color)) {
+ return getSingleColor(color);
+ }
+ else {
+ for (var i = 0; i < color.length; ++i) {
+ color[i] = getSingleColor(color[i]);
+ }
+ return color;
+ }
+ }
+ /**
+ * Utility function to convert hexadecimal colors to strings, and simply return the color if it's a string.
+ * This version can also convert array of colors
+ * @private
+ * @param array1 - First array to compare
+ * @param array2 - Second array to compare
+ * @return Do the arrays contain the same values in the same order
+ */
+ function areArraysEqual(array1, array2) {
+ if (!Array.isArray(array1) || !Array.isArray(array2)) {
+ return false;
+ }
+ if (array1.length !== array2.length) {
+ return false;
+ }
+ for (var i = 0; i < array1.length; ++i) {
+ if (array1[i] !== array2[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ /**
+ * Utility function to ensure that object properties are copied by value, and not by reference
+ * @private
+ * @param target - Target object to copy properties into
+ * @param source - Source object for the properties to copy
+ * @param propertyObj - Object containing properties names we want to loop over
+ */
+ function deepCopyProperties(target, source, propertyObj) {
+ for (var prop in propertyObj) {
+ if (Array.isArray(source[prop])) {
+ target[prop] = source[prop].slice();
+ }
+ else {
+ target[prop] = source[prop];
+ }
+ }
+ }
+
+ /**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * ```js
+ * let style = new PIXI.TextStyle({fontFamily : 'Arial', fontSize: 24, fill : 0xff1010, align : 'center'})
+ * let textMetrics = PIXI.TextMetrics.measureText('Your text', style)
+ * ```
+ * @memberof PIXI
+ */
+ var TextMetrics = /** @class */ (function () {
+ /**
+ * @param text - the text that was measured
+ * @param style - the style that was measured
+ * @param width - the measured width of the text
+ * @param height - the measured height of the text
+ * @param lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param lineWidths - an array of the line widths for each line matched to `lines`
+ * @param lineHeight - the measured line height for this style
+ * @param maxLineWidth - the maximum line width for all measured lines
+ * @param {PIXI.IFontMetrics} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ function TextMetrics(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties) {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ * @param text - The text to measure.
+ * @param style - The text style to use for measuring
+ * @param wordWrap - Override for if word-wrap should be applied to the text.
+ * @param canvas - optional specification of the canvas to use for measuring.
+ * @returns Measured width and height of the text.
+ */
+ TextMetrics.measureText = function (text, style, wordWrap, canvas) {
+ if (canvas === void 0) { canvas = TextMetrics._canvas; }
+ wordWrap = (wordWrap === undefined || wordWrap === null) ? style.wordWrap : wordWrap;
+ var font = style.toFontString();
+ var fontProperties = TextMetrics.measureFont(font);
+ // fallback in case UA disallow canvas data extraction
+ // (toDataURI, getImageData functions)
+ if (fontProperties.fontSize === 0) {
+ fontProperties.fontSize = style.fontSize;
+ fontProperties.ascent = style.fontSize;
+ }
+ var context = canvas.getContext('2d');
+ context.font = font;
+ var outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ var lines = outputText.split(/(?:\r\n|\r|\n)/);
+ var lineWidths = new Array(lines.length);
+ var maxLineWidth = 0;
+ for (var i = 0; i < lines.length; i++) {
+ var lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ var width = maxLineWidth + style.strokeThickness;
+ if (style.dropShadow) {
+ width += style.dropShadowDistance;
+ }
+ var lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ var height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * (lineHeight + style.leading));
+ if (style.dropShadow) {
+ height += style.dropShadowDistance;
+ }
+ return new TextMetrics(text, style, width, height, lines, lineWidths, lineHeight + style.leading, maxLineWidth, fontProperties);
+ };
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ * @param text - String to apply word wrapping to
+ * @param style - the style to use when wrapping
+ * @param canvas - optional specification of the canvas to use for measuring.
+ * @returns New string with new lines applied where required
+ */
+ TextMetrics.wordWrap = function (text, style, canvas) {
+ if (canvas === void 0) { canvas = TextMetrics._canvas; }
+ var context = canvas.getContext('2d');
+ var width = 0;
+ var line = '';
+ var lines = '';
+ var cache = Object.create(null);
+ var letterSpacing = style.letterSpacing, whiteSpace = style.whiteSpace;
+ // How to handle whitespaces
+ var collapseSpaces = TextMetrics.collapseSpaces(whiteSpace);
+ var collapseNewlines = TextMetrics.collapseNewlines(whiteSpace);
+ // whether or not spaces may be added to the beginning of lines
+ var canPrependSpaces = !collapseSpaces;
+ // There is letterSpacing after every char except the last one
+ // t_h_i_s_' '_i_s_' '_a_n_' '_e_x_a_m_p_l_e_' '_!
+ // so for convenience the above needs to be compared to width + 1 extra letterSpace
+ // t_h_i_s_' '_i_s_' '_a_n_' '_e_x_a_m_p_l_e_' '_!_
+ // ________________________________________________
+ // And then the final space is simply no appended to each line
+ var wordWrapWidth = style.wordWrapWidth + letterSpacing;
+ // break text into words, spaces and newline chars
+ var tokens = TextMetrics.tokenize(text);
+ for (var i = 0; i < tokens.length; i++) {
+ // get the word, space or newlineChar
+ var token = tokens[i];
+ // if word is a new line
+ if (TextMetrics.isNewline(token)) {
+ // keep the new line
+ if (!collapseNewlines) {
+ lines += TextMetrics.addLine(line);
+ canPrependSpaces = !collapseSpaces;
+ line = '';
+ width = 0;
+ continue;
+ }
+ // if we should collapse new lines
+ // we simply convert it into a space
+ token = ' ';
+ }
+ // if we should collapse repeated whitespaces
+ if (collapseSpaces) {
+ // check both this and the last tokens for spaces
+ var currIsBreakingSpace = TextMetrics.isBreakingSpace(token);
+ var lastIsBreakingSpace = TextMetrics.isBreakingSpace(line[line.length - 1]);
+ if (currIsBreakingSpace && lastIsBreakingSpace) {
+ continue;
+ }
+ }
+ // get word width from cache if possible
+ var tokenWidth = TextMetrics.getFromCache(token, letterSpacing, cache, context);
+ // word is longer than desired bounds
+ if (tokenWidth > wordWrapWidth) {
+ // if we are not already at the beginning of a line
+ if (line !== '') {
+ // start newlines for overflow words
+ lines += TextMetrics.addLine(line);
+ line = '';
+ width = 0;
+ }
+ // break large word over multiple lines
+ if (TextMetrics.canBreakWords(token, style.breakWords)) {
+ // break word into characters
+ var characters = TextMetrics.wordWrapSplit(token);
+ // loop the characters
+ for (var j = 0; j < characters.length; j++) {
+ var char = characters[j];
+ var k = 1;
+ // we are not at the end of the token
+ while (characters[j + k]) {
+ var nextChar = characters[j + k];
+ var lastChar = char[char.length - 1];
+ // should not split chars
+ if (!TextMetrics.canBreakChars(lastChar, nextChar, token, j, style.breakWords)) {
+ // combine chars & move forward one
+ char += nextChar;
+ }
+ else {
+ break;
+ }
+ k++;
+ }
+ j += char.length - 1;
+ var characterWidth = TextMetrics.getFromCache(char, letterSpacing, cache, context);
+ if (characterWidth + width > wordWrapWidth) {
+ lines += TextMetrics.addLine(line);
+ canPrependSpaces = false;
+ line = '';
+ width = 0;
+ }
+ line += char;
+ width += characterWidth;
+ }
+ }
+ // run word out of the bounds
+ else {
+ // if there are words in this line already
+ // finish that line and start a new one
+ if (line.length > 0) {
+ lines += TextMetrics.addLine(line);
+ line = '';
+ width = 0;
+ }
+ var isLastToken = i === tokens.length - 1;
+ // give it its own line if it's not the end
+ lines += TextMetrics.addLine(token, !isLastToken);
+ canPrependSpaces = false;
+ line = '';
+ width = 0;
+ }
+ }
+ // word could fit
+ else {
+ // word won't fit because of existing words
+ // start a new line
+ if (tokenWidth + width > wordWrapWidth) {
+ // if its a space we don't want it
+ canPrependSpaces = false;
+ // add a new line
+ lines += TextMetrics.addLine(line);
+ // start a new line
+ line = '';
+ width = 0;
+ }
+ // don't add spaces to the beginning of lines
+ if (line.length > 0 || !TextMetrics.isBreakingSpace(token) || canPrependSpaces) {
+ // add the word to the current line
+ line += token;
+ // update width counter
+ width += tokenWidth;
+ }
+ }
+ }
+ lines += TextMetrics.addLine(line, false);
+ return lines;
+ };
+ /**
+ * Convienience function for logging each line added during the wordWrap method.
+ * @param line - The line of text to add
+ * @param newLine - Add new line character to end
+ * @returns A formatted line
+ */
+ TextMetrics.addLine = function (line, newLine) {
+ if (newLine === void 0) { newLine = true; }
+ line = TextMetrics.trimRight(line);
+ line = (newLine) ? line + "\n" : line;
+ return line;
+ };
+ /**
+ * Gets & sets the widths of calculated characters in a cache object
+ * @param key - The key
+ * @param letterSpacing - The letter spacing
+ * @param cache - The cache
+ * @param context - The canvas context
+ * @returns The from cache.
+ */
+ TextMetrics.getFromCache = function (key, letterSpacing, cache, context) {
+ var width = cache[key];
+ if (typeof width !== 'number') {
+ var spacing = ((key.length) * letterSpacing);
+ width = context.measureText(key).width + spacing;
+ cache[key] = width;
+ }
+ return width;
+ };
+ /**
+ * Determines whether we should collapse breaking spaces.
+ * @param whiteSpace - The TextStyle property whiteSpace
+ * @returns Should collapse
+ */
+ TextMetrics.collapseSpaces = function (whiteSpace) {
+ return (whiteSpace === 'normal' || whiteSpace === 'pre-line');
+ };
+ /**
+ * Determines whether we should collapse newLine chars.
+ * @param whiteSpace - The white space
+ * @returns should collapse
+ */
+ TextMetrics.collapseNewlines = function (whiteSpace) {
+ return (whiteSpace === 'normal');
+ };
+ /**
+ * Trims breaking whitespaces from string.
+ * @param text - The text
+ * @returns Trimmed string
+ */
+ TextMetrics.trimRight = function (text) {
+ if (typeof text !== 'string') {
+ return '';
+ }
+ for (var i = text.length - 1; i >= 0; i--) {
+ var char = text[i];
+ if (!TextMetrics.isBreakingSpace(char)) {
+ break;
+ }
+ text = text.slice(0, -1);
+ }
+ return text;
+ };
+ /**
+ * Determines if char is a newline.
+ * @param char - The character
+ * @returns True if newline, False otherwise.
+ */
+ TextMetrics.isNewline = function (char) {
+ if (typeof char !== 'string') {
+ return false;
+ }
+ return (TextMetrics._newlines.indexOf(char.charCodeAt(0)) >= 0);
+ };
+ /**
+ * Determines if char is a breaking whitespace.
+ *
+ * It allows one to determine whether char should be a breaking whitespace
+ * For example certain characters in CJK langs or numbers.
+ * It must return a boolean.
+ * @param char - The character
+ * @param [_nextChar] - The next character
+ * @returns True if whitespace, False otherwise.
+ */
+ TextMetrics.isBreakingSpace = function (char, _nextChar) {
+ if (typeof char !== 'string') {
+ return false;
+ }
+ return (TextMetrics._breakingSpaces.indexOf(char.charCodeAt(0)) >= 0);
+ };
+ /**
+ * Splits a string into words, breaking-spaces and newLine characters
+ * @param text - The text
+ * @returns A tokenized array
+ */
+ TextMetrics.tokenize = function (text) {
+ var tokens = [];
+ var token = '';
+ if (typeof text !== 'string') {
+ return tokens;
+ }
+ for (var i = 0; i < text.length; i++) {
+ var char = text[i];
+ var nextChar = text[i + 1];
+ if (TextMetrics.isBreakingSpace(char, nextChar) || TextMetrics.isNewline(char)) {
+ if (token !== '') {
+ tokens.push(token);
+ token = '';
+ }
+ tokens.push(char);
+ continue;
+ }
+ token += char;
+ }
+ if (token !== '') {
+ tokens.push(token);
+ }
+ return tokens;
+ };
+ /**
+ * Overridable helper method used internally by TextMetrics, exposed to allow customizing the class's behavior.
+ *
+ * It allows one to customise which words should break
+ * Examples are if the token is CJK or numbers.
+ * It must return a boolean.
+ * @param _token - The token
+ * @param breakWords - The style attr break words
+ * @returns Whether to break word or not
+ */
+ TextMetrics.canBreakWords = function (_token, breakWords) {
+ return breakWords;
+ };
+ /**
+ * Overridable helper method used internally by TextMetrics, exposed to allow customizing the class's behavior.
+ *
+ * It allows one to determine whether a pair of characters
+ * should be broken by newlines
+ * For example certain characters in CJK langs or numbers.
+ * It must return a boolean.
+ * @param _char - The character
+ * @param _nextChar - The next character
+ * @param _token - The token/word the characters are from
+ * @param _index - The index in the token of the char
+ * @param _breakWords - The style attr break words
+ * @returns whether to break word or not
+ */
+ TextMetrics.canBreakChars = function (_char, _nextChar, _token, _index, _breakWords) {
+ return true;
+ };
+ /**
+ * Overridable helper method used internally by TextMetrics, exposed to allow customizing the class's behavior.
+ *
+ * It is called when a token (usually a word) has to be split into separate pieces
+ * in order to determine the point to break a word.
+ * It must return an array of characters.
+ * @example
+ * // Correctly splits emojis, eg "馃お馃お" will result in two element array, each with one emoji.
+ * TextMetrics.wordWrapSplit = (token) => [...token];
+ * @param token - The token to split
+ * @returns The characters of the token
+ */
+ TextMetrics.wordWrapSplit = function (token) {
+ return token.split('');
+ };
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ * @param font - String representing the style of the font
+ * @returns Font properties object
+ */
+ TextMetrics.measureFont = function (font) {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font]) {
+ return TextMetrics._fonts[font];
+ }
+ var properties = {
+ ascent: 0,
+ descent: 0,
+ fontSize: 0,
+ };
+ var canvas = TextMetrics._canvas;
+ var context = TextMetrics._context;
+ context.font = font;
+ var metricsString = TextMetrics.METRICS_STRING + TextMetrics.BASELINE_SYMBOL;
+ var width = Math.ceil(context.measureText(metricsString).width);
+ var baseline = Math.ceil(context.measureText(TextMetrics.BASELINE_SYMBOL).width);
+ var height = Math.ceil(TextMetrics.HEIGHT_MULTIPLIER * baseline);
+ baseline = baseline * TextMetrics.BASELINE_MULTIPLIER | 0;
+ canvas.width = width;
+ canvas.height = height;
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+ context.font = font;
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText(metricsString, 0, baseline);
+ var imagedata = context.getImageData(0, 0, width, height).data;
+ var pixels = imagedata.length;
+ var line = width * 4;
+ var i = 0;
+ var idx = 0;
+ var stop = false;
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i) {
+ for (var j = 0; j < line; j += 4) {
+ if (imagedata[idx + j] !== 255) {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop) {
+ idx += line;
+ }
+ else {
+ break;
+ }
+ }
+ properties.ascent = baseline - i;
+ idx = pixels - line;
+ stop = false;
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i) {
+ for (var j = 0; j < line; j += 4) {
+ if (imagedata[idx + j] !== 255) {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop) {
+ idx -= line;
+ }
+ else {
+ break;
+ }
+ }
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+ TextMetrics._fonts[font] = properties;
+ return properties;
+ };
+ /**
+ * Clear font metrics in metrics cache.
+ * @param {string} [font] - font name. If font name not set then clear cache for all fonts.
+ */
+ TextMetrics.clearMetrics = function (font) {
+ if (font === void 0) { font = ''; }
+ if (font) {
+ delete TextMetrics._fonts[font];
+ }
+ else {
+ TextMetrics._fonts = {};
+ }
+ };
+ Object.defineProperty(TextMetrics, "_canvas", {
+ /**
+ * Cached canvas element for measuring text
+ * TODO: this should be private, but isn't because of backward compat, will fix later.
+ * @ignore
+ */
+ get: function () {
+ if (!TextMetrics.__canvas) {
+ var canvas = void 0;
+ try {
+ // OffscreenCanvas2D measureText can be up to 40% faster.
+ var c = new OffscreenCanvas(0, 0);
+ var context = c.getContext('2d');
+ if (context && context.measureText) {
+ TextMetrics.__canvas = c;
+ return c;
+ }
+ canvas = settings$1.ADAPTER.createCanvas();
+ }
+ catch (ex) {
+ canvas = settings$1.ADAPTER.createCanvas();
+ }
+ canvas.width = canvas.height = 10;
+ TextMetrics.__canvas = canvas;
+ }
+ return TextMetrics.__canvas;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TextMetrics, "_context", {
+ /**
+ * TODO: this should be private, but isn't because of backward compat, will fix later.
+ * @ignore
+ */
+ get: function () {
+ if (!TextMetrics.__context) {
+ TextMetrics.__context = TextMetrics._canvas.getContext('2d');
+ }
+ return TextMetrics.__context;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return TextMetrics;
+ }());
+ /**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @typedef {object} FontMetrics
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ * @memberof PIXI.TextMetrics
+ * @private
+ */
+ /**
+ * Cache of {@see PIXI.TextMetrics.FontMetrics} objects.
+ * @memberof PIXI.TextMetrics
+ * @type {object}
+ * @private
+ */
+ TextMetrics._fonts = {};
+ /**
+ * String used for calculate font metrics.
+ * These characters are all tall to help calculate the height required for text.
+ * @static
+ * @memberof PIXI.TextMetrics
+ * @name METRICS_STRING
+ * @type {string}
+ * @default |脡q脜
+ */
+ TextMetrics.METRICS_STRING = '|脡q脜';
+ /**
+ * Baseline symbol for calculate font metrics.
+ * @static
+ * @memberof PIXI.TextMetrics
+ * @name BASELINE_SYMBOL
+ * @type {string}
+ * @default M
+ */
+ TextMetrics.BASELINE_SYMBOL = 'M';
+ /**
+ * Baseline multiplier for calculate font metrics.
+ * @static
+ * @memberof PIXI.TextMetrics
+ * @name BASELINE_MULTIPLIER
+ * @type {number}
+ * @default 1.4
+ */
+ TextMetrics.BASELINE_MULTIPLIER = 1.4;
+ /**
+ * Height multiplier for setting height of canvas to calculate font metrics.
+ * @static
+ * @memberof PIXI.TextMetrics
+ * @name HEIGHT_MULTIPLIER
+ * @type {number}
+ * @default 2.00
+ */
+ TextMetrics.HEIGHT_MULTIPLIER = 2.0;
+ /**
+ * Cache of new line chars.
+ * @memberof PIXI.TextMetrics
+ * @type {number[]}
+ * @private
+ */
+ TextMetrics._newlines = [
+ 0x000A,
+ 0x000D ];
+ /**
+ * Cache of breaking spaces.
+ * @memberof PIXI.TextMetrics
+ * @type {number[]}
+ * @private
+ */
+ TextMetrics._breakingSpaces = [
+ 0x0009,
+ 0x0020,
+ 0x2000,
+ 0x2001,
+ 0x2002,
+ 0x2003,
+ 0x2004,
+ 0x2005,
+ 0x2006,
+ 0x2008,
+ 0x2009,
+ 0x200A,
+ 0x205F,
+ 0x3000 ];
+ /**
+ * A number, or a string containing a number.
+ * @memberof PIXI
+ * @typedef {object} IFontMetrics
+ * @property {number} ascent - Font ascent
+ * @property {number} descent - Font descent
+ * @property {number} fontSize - Font size
+ */
+
+ var defaultDestroyOptions = {
+ texture: true,
+ children: false,
+ baseTexture: true,
+ };
+ /**
+ * A Text Object will create a line or multiple lines of text.
+ *
+ * The text is created using the [Canvas API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API).
+ *
+ * The primary advantage of this class over BitmapText is that you have great control over the style of the text,
+ * which you can change at runtime.
+ *
+ * The primary disadvantages is that each piece of text has it's own texture, which can use more memory.
+ * When text changes, this texture has to be re-generated and re-uploaded to the GPU, taking up time.
+ *
+ * To split a line you can use '\n' in your text string, or, on the `style` object,
+ * change its `wordWrap` property to true and and give the `wordWrapWidth` property a value.
+ *
+ * A Text can be created directly from a string and a style object,
+ * which can be generated [here](https://pixijs.io/pixi-text-style).
+ *
+ * ```js
+ * let text = new PIXI.Text('This is a PixiJS text',{fontFamily : 'Arial', fontSize: 24, fill : 0xff1010, align : 'center'});
+ * ```
+ * @memberof PIXI
+ */
+ var Text = /** @class */ (function (_super) {
+ __extends$c(Text, _super);
+ /**
+ * @param text - The string that you would like the text to display
+ * @param {object|PIXI.TextStyle} [style] - The style parameters
+ * @param canvas - The canvas element for drawing text
+ */
+ function Text(text, style, canvas) {
+ var _this = this;
+ var ownCanvas = false;
+ if (!canvas) {
+ canvas = settings$1.ADAPTER.createCanvas();
+ ownCanvas = true;
+ }
+ canvas.width = 3;
+ canvas.height = 3;
+ var texture = Texture.from(canvas);
+ texture.orig = new Rectangle();
+ texture.trim = new Rectangle();
+ _this = _super.call(this, texture) || this;
+ _this._ownCanvas = ownCanvas;
+ _this.canvas = canvas;
+ _this.context = canvas.getContext('2d');
+ _this._resolution = settings$1.RESOLUTION;
+ _this._autoResolution = true;
+ _this._text = null;
+ _this._style = null;
+ _this._styleListener = null;
+ _this._font = '';
+ _this.text = text;
+ _this.style = style;
+ _this.localStyleID = -1;
+ return _this;
+ }
+ /**
+ * Renders text to its canvas, and updates its texture.
+ *
+ * By default this is used internally to ensure the texture is correct before rendering,
+ * but it can be used called externally, for example from this class to 'pre-generate' the texture from a piece of text,
+ * and then shared across multiple Sprites.
+ * @param respectDirty - Whether to abort updating the text if the Text isn't dirty and the function is called.
+ */
+ Text.prototype.updateText = function (respectDirty) {
+ var style = this._style;
+ // check if style has changed..
+ if (this.localStyleID !== style.styleID) {
+ this.dirty = true;
+ this.localStyleID = style.styleID;
+ }
+ if (!this.dirty && respectDirty) {
+ return;
+ }
+ this._font = this._style.toFontString();
+ var context = this.context;
+ var measured = TextMetrics.measureText(this._text || ' ', this._style, this._style.wordWrap, this.canvas);
+ var width = measured.width;
+ var height = measured.height;
+ var lines = measured.lines;
+ var lineHeight = measured.lineHeight;
+ var lineWidths = measured.lineWidths;
+ var maxLineWidth = measured.maxLineWidth;
+ var fontProperties = measured.fontProperties;
+ this.canvas.width = Math.ceil(Math.ceil((Math.max(1, width) + (style.padding * 2))) * this._resolution);
+ this.canvas.height = Math.ceil(Math.ceil((Math.max(1, height) + (style.padding * 2))) * this._resolution);
+ context.scale(this._resolution, this._resolution);
+ context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ context.font = this._font;
+ context.lineWidth = style.strokeThickness;
+ context.textBaseline = style.textBaseline;
+ context.lineJoin = style.lineJoin;
+ context.miterLimit = style.miterLimit;
+ var linePositionX;
+ var linePositionY;
+ // require 2 passes if a shadow; the first to draw the drop shadow, the second to draw the text
+ var passesCount = style.dropShadow ? 2 : 1;
+ // For v4, we drew text at the colours of the drop shadow underneath the normal text. This gave the correct zIndex,
+ // but features such as alpha and shadowblur did not look right at all, since we were using actual text as a shadow.
+ //
+ // For v5.0.0, we moved over to just use the canvas API for drop shadows, which made them look much nicer and more
+ // visually please, but now because the stroke is drawn and then the fill, drop shadows would appear on both the fill
+ // and the stroke; and fill drop shadows would appear over the top of the stroke.
+ //
+ // For v5.1.1, the new route is to revert to v4 style of drawing text first to get the drop shadows underneath normal
+ // text, but instead drawing text in the correct location, we'll draw it off screen (-paddingY), and then adjust the
+ // drop shadow so only that appears on screen (+paddingY). Now we'll have the correct draw order of the shadow
+ // beneath the text, whilst also having the proper text shadow styling.
+ for (var i = 0; i < passesCount; ++i) {
+ var isShadowPass = style.dropShadow && i === 0;
+ // we only want the drop shadow, so put text way off-screen
+ var dsOffsetText = isShadowPass ? Math.ceil(Math.max(1, height) + (style.padding * 2)) : 0;
+ var dsOffsetShadow = dsOffsetText * this._resolution;
+ if (isShadowPass) {
+ // On Safari, text with gradient and drop shadows together do not position correctly
+ // if the scale of the canvas is not 1: https://bugs.webkit.org/show_bug.cgi?id=197689
+ // Therefore we'll set the styles to be a plain black whilst generating this drop shadow
+ context.fillStyle = 'black';
+ context.strokeStyle = 'black';
+ var dropShadowColor = style.dropShadowColor;
+ var rgb = hex2rgb(typeof dropShadowColor === 'number' ? dropShadowColor : string2hex(dropShadowColor));
+ var dropShadowBlur = style.dropShadowBlur * this._resolution;
+ var dropShadowDistance = style.dropShadowDistance * this._resolution;
+ context.shadowColor = "rgba(" + rgb[0] * 255 + "," + rgb[1] * 255 + "," + rgb[2] * 255 + "," + style.dropShadowAlpha + ")";
+ context.shadowBlur = dropShadowBlur;
+ context.shadowOffsetX = Math.cos(style.dropShadowAngle) * dropShadowDistance;
+ context.shadowOffsetY = (Math.sin(style.dropShadowAngle) * dropShadowDistance) + dsOffsetShadow;
+ }
+ else {
+ // set canvas text styles
+ context.fillStyle = this._generateFillStyle(style, lines, measured);
+ // TODO: Can't have different types for getter and setter. The getter shouldn't have the number type as
+ // the setter converts to string. See this thread for more details:
+ // https://github.com/microsoft/TypeScript/issues/2521
+ context.strokeStyle = style.stroke;
+ context.shadowColor = 'black';
+ context.shadowBlur = 0;
+ context.shadowOffsetX = 0;
+ context.shadowOffsetY = 0;
+ }
+ var linePositionYShift = (lineHeight - fontProperties.fontSize) / 2;
+ if (!Text.nextLineHeightBehavior || lineHeight - fontProperties.fontSize < 0) {
+ linePositionYShift = 0;
+ }
+ // draw lines line by line
+ for (var i_1 = 0; i_1 < lines.length; i_1++) {
+ linePositionX = style.strokeThickness / 2;
+ linePositionY = ((style.strokeThickness / 2) + (i_1 * lineHeight)) + fontProperties.ascent
+ + linePositionYShift;
+ if (style.align === 'right') {
+ linePositionX += maxLineWidth - lineWidths[i_1];
+ }
+ else if (style.align === 'center') {
+ linePositionX += (maxLineWidth - lineWidths[i_1]) / 2;
+ }
+ if (style.stroke && style.strokeThickness) {
+ this.drawLetterSpacing(lines[i_1], linePositionX + style.padding, linePositionY + style.padding - dsOffsetText, true);
+ }
+ if (style.fill) {
+ this.drawLetterSpacing(lines[i_1], linePositionX + style.padding, linePositionY + style.padding - dsOffsetText);
+ }
+ }
+ }
+ this.updateTexture();
+ };
+ /**
+ * Render the text with letter-spacing.
+ * @param text - The text to draw
+ * @param x - Horizontal position to draw the text
+ * @param y - Vertical position to draw the text
+ * @param isStroke - Is this drawing for the outside stroke of the
+ * text? If not, it's for the inside fill
+ */
+ Text.prototype.drawLetterSpacing = function (text, x, y, isStroke) {
+ if (isStroke === void 0) { isStroke = false; }
+ var style = this._style;
+ // letterSpacing of 0 means normal
+ var letterSpacing = style.letterSpacing;
+ // Checking that we can use moddern canvas2D api
+ // https://developer.chrome.com/origintrials/#/view_trial/3585991203293757441
+ // note: this is unstable API, Chrome less 94 use a `textLetterSpacing`, newest use a letterSpacing
+ // eslint-disable-next-line max-len
+ var supportLetterSpacing = Text.experimentalLetterSpacing
+ && ('letterSpacing' in CanvasRenderingContext2D.prototype
+ || 'textLetterSpacing' in CanvasRenderingContext2D.prototype);
+ if (letterSpacing === 0 || supportLetterSpacing) {
+ if (supportLetterSpacing) {
+ this.context.letterSpacing = letterSpacing;
+ this.context.textLetterSpacing = letterSpacing;
+ }
+ if (isStroke) {
+ this.context.strokeText(text, x, y);
+ }
+ else {
+ this.context.fillText(text, x, y);
+ }
+ return;
+ }
+ var currentPosition = x;
+ // Using Array.from correctly splits characters whilst keeping emoji together.
+ // This is not supported on IE as it requires ES6, so regular text splitting occurs.
+ // This also doesn't account for emoji that are multiple emoji put together to make something else.
+ // Handling all of this would require a big library itself.
+ // https://medium.com/@giltayar/iterating-over-emoji-characters-the-es6-way-f06e4589516
+ // https://github.com/orling/grapheme-splitter
+ var stringArray = Array.from ? Array.from(text) : text.split('');
+ var previousWidth = this.context.measureText(text).width;
+ var currentWidth = 0;
+ for (var i = 0; i < stringArray.length; ++i) {
+ var currentChar = stringArray[i];
+ if (isStroke) {
+ this.context.strokeText(currentChar, currentPosition, y);
+ }
+ else {
+ this.context.fillText(currentChar, currentPosition, y);
+ }
+ var textStr = '';
+ for (var j = i + 1; j < stringArray.length; ++j) {
+ textStr += stringArray[j];
+ }
+ currentWidth = this.context.measureText(textStr).width;
+ currentPosition += previousWidth - currentWidth + letterSpacing;
+ previousWidth = currentWidth;
+ }
+ };
+ /** Updates texture size based on canvas size. */
+ Text.prototype.updateTexture = function () {
+ var canvas = this.canvas;
+ if (this._style.trim) {
+ var trimmed = trimCanvas(canvas);
+ if (trimmed.data) {
+ canvas.width = trimmed.width;
+ canvas.height = trimmed.height;
+ this.context.putImageData(trimmed.data, 0, 0);
+ }
+ }
+ var texture = this._texture;
+ var style = this._style;
+ var padding = style.trim ? 0 : style.padding;
+ var baseTexture = texture.baseTexture;
+ texture.trim.width = texture._frame.width = canvas.width / this._resolution;
+ texture.trim.height = texture._frame.height = canvas.height / this._resolution;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
+ // call sprite onTextureUpdate to update scale if _width or _height were set
+ this._onTextureUpdate();
+ baseTexture.setRealSize(canvas.width, canvas.height, this._resolution);
+ texture.updateUvs();
+ this.dirty = false;
+ };
+ /**
+ * Renders the object using the WebGL renderer
+ * @param renderer - The renderer
+ */
+ Text.prototype._render = function (renderer) {
+ if (this._autoResolution && this._resolution !== renderer.resolution) {
+ this._resolution = renderer.resolution;
+ this.dirty = true;
+ }
+ this.updateText(true);
+ _super.prototype._render.call(this, renderer);
+ };
+ /** Updates the transform on all children of this container for rendering. */
+ Text.prototype.updateTransform = function () {
+ this.updateText(true);
+ _super.prototype.updateTransform.call(this);
+ };
+ Text.prototype.getBounds = function (skipUpdate, rect) {
+ this.updateText(true);
+ if (this._textureID === -1) {
+ // texture was updated: recalculate transforms
+ skipUpdate = false;
+ }
+ return _super.prototype.getBounds.call(this, skipUpdate, rect);
+ };
+ /**
+ * Gets the local bounds of the text object.
+ * @param rect - The output rectangle.
+ * @returns The bounds.
+ */
+ Text.prototype.getLocalBounds = function (rect) {
+ this.updateText(true);
+ return _super.prototype.getLocalBounds.call(this, rect);
+ };
+ /** Calculates the bounds of the Text as a rectangle. The bounds calculation takes the worldTransform into account. */
+ Text.prototype._calculateBounds = function () {
+ this.calculateVertices();
+ // if we have already done this on THIS frame.
+ this._bounds.addQuad(this.vertexData);
+ };
+ /**
+ * Generates the fill style. Can automatically generate a gradient based on the fill style being an array
+ * @param style - The style.
+ * @param lines - The lines of text.
+ * @param metrics
+ * @returns The fill style
+ */
+ Text.prototype._generateFillStyle = function (style, lines, metrics) {
+ // TODO: Can't have different types for getter and setter. The getter shouldn't have the number type as
+ // the setter converts to string. See this thread for more details:
+ // https://github.com/microsoft/TypeScript/issues/2521
+ var fillStyle = style.fill;
+ if (!Array.isArray(fillStyle)) {
+ return fillStyle;
+ }
+ else if (fillStyle.length === 1) {
+ return fillStyle[0];
+ }
+ // the gradient will be evenly spaced out according to how large the array is.
+ // ['#FF0000', '#00FF00', '#0000FF'] would created stops at 0.25, 0.5 and 0.75
+ var gradient;
+ // a dropshadow will enlarge the canvas and result in the gradient being
+ // generated with the incorrect dimensions
+ var dropShadowCorrection = (style.dropShadow) ? style.dropShadowDistance : 0;
+ // should also take padding into account, padding can offset the gradient
+ var padding = style.padding || 0;
+ var width = (this.canvas.width / this._resolution) - dropShadowCorrection - (padding * 2);
+ var height = (this.canvas.height / this._resolution) - dropShadowCorrection - (padding * 2);
+ // make a copy of the style settings, so we can manipulate them later
+ var fill = fillStyle.slice();
+ var fillGradientStops = style.fillGradientStops.slice();
+ // wanting to evenly distribute the fills. So an array of 4 colours should give fills of 0.25, 0.5 and 0.75
+ if (!fillGradientStops.length) {
+ var lengthPlus1 = fill.length + 1;
+ for (var i = 1; i < lengthPlus1; ++i) {
+ fillGradientStops.push(i / lengthPlus1);
+ }
+ }
+ // stop the bleeding of the last gradient on the line above to the top gradient of the this line
+ // by hard defining the first gradient colour at point 0, and last gradient colour at point 1
+ fill.unshift(fillStyle[0]);
+ fillGradientStops.unshift(0);
+ fill.push(fillStyle[fillStyle.length - 1]);
+ fillGradientStops.push(1);
+ if (style.fillGradientType === exports.TEXT_GRADIENT.LINEAR_VERTICAL) {
+ // start the gradient at the top center of the canvas, and end at the bottom middle of the canvas
+ gradient = this.context.createLinearGradient(width / 2, padding, width / 2, height + padding);
+ // we need to repeat the gradient so that each individual line of text has the same vertical gradient effect
+ // ['#FF0000', '#00FF00', '#0000FF'] over 2 lines would create stops at 0.125, 0.25, 0.375, 0.625, 0.75, 0.875
+ // Actual height of the text itself, not counting spacing for lineHeight/leading/dropShadow etc
+ var textHeight = metrics.fontProperties.fontSize + style.strokeThickness;
+ for (var i = 0; i < lines.length; i++) {
+ var lastLineBottom = (metrics.lineHeight * (i - 1)) + textHeight;
+ var thisLineTop = metrics.lineHeight * i;
+ var thisLineGradientStart = thisLineTop;
+ // Handle case where last & this line overlap
+ if (i > 0 && lastLineBottom > thisLineTop) {
+ thisLineGradientStart = (thisLineTop + lastLineBottom) / 2;
+ }
+ var thisLineBottom = thisLineTop + textHeight;
+ var nextLineTop = metrics.lineHeight * (i + 1);
+ var thisLineGradientEnd = thisLineBottom;
+ // Handle case where this & next line overlap
+ if (i + 1 < lines.length && nextLineTop < thisLineBottom) {
+ thisLineGradientEnd = (thisLineBottom + nextLineTop) / 2;
+ }
+ // textHeight, but as a 0-1 size in global gradient stop space
+ var gradStopLineHeight = (thisLineGradientEnd - thisLineGradientStart) / height;
+ for (var j = 0; j < fill.length; j++) {
+ // 0-1 stop point for the current line, multiplied to global space afterwards
+ var lineStop = 0;
+ if (typeof fillGradientStops[j] === 'number') {
+ lineStop = fillGradientStops[j];
+ }
+ else {
+ lineStop = j / fill.length;
+ }
+ var globalStop = Math.min(1, Math.max(0, (thisLineGradientStart / height) + (lineStop * gradStopLineHeight)));
+ // There's potential for floating point precision issues at the seams between gradient repeats.
+ globalStop = Number(globalStop.toFixed(5));
+ gradient.addColorStop(globalStop, fill[j]);
+ }
+ }
+ }
+ else {
+ // start the gradient at the center left of the canvas, and end at the center right of the canvas
+ gradient = this.context.createLinearGradient(padding, height / 2, width + padding, height / 2);
+ // can just evenly space out the gradients in this case, as multiple lines makes no difference
+ // to an even left to right gradient
+ var totalIterations = fill.length + 1;
+ var currentIteration = 1;
+ for (var i = 0; i < fill.length; i++) {
+ var stop = void 0;
+ if (typeof fillGradientStops[i] === 'number') {
+ stop = fillGradientStops[i];
+ }
+ else {
+ stop = currentIteration / totalIterations;
+ }
+ gradient.addColorStop(stop, fill[i]);
+ currentIteration++;
+ }
+ }
+ return gradient;
+ };
+ /**
+ * Destroys this text object.
+ *
+ * Note* Unlike a Sprite, a Text object will automatically destroy its baseTexture and texture as
+ * the majority of the time the texture will not be shared with any other Sprites.
+ * @param options - Options parameter. A boolean will act as if all options
+ * have been set to that value
+ * @param {boolean} [options.children=false] - if set to true, all the children will have their
+ * destroy method called as well. 'options' will be passed on to those calls.
+ * @param {boolean} [options.texture=true] - Should it destroy the current texture of the sprite as well
+ * @param {boolean} [options.baseTexture=true] - Should it destroy the base texture of the sprite as well
+ */
+ Text.prototype.destroy = function (options) {
+ if (typeof options === 'boolean') {
+ options = { children: options };
+ }
+ options = Object.assign({}, defaultDestroyOptions, options);
+ _super.prototype.destroy.call(this, options);
+ // set canvas width and height to 0 to workaround memory leak in Safari < 13
+ // https://stackoverflow.com/questions/52532614/total-canvas-memory-use-exceeds-the-maximum-limit-safari-12
+ if (this._ownCanvas) {
+ this.canvas.height = this.canvas.width = 0;
+ }
+ // make sure to reset the context and canvas.. dont want this hanging around in memory!
+ this.context = null;
+ this.canvas = null;
+ this._style = null;
+ };
+ Object.defineProperty(Text.prototype, "width", {
+ /** The width of the Text, setting this will actually modify the scale to achieve the value set. */
+ get: function () {
+ this.updateText(true);
+ return Math.abs(this.scale.x) * this._texture.orig.width;
+ },
+ set: function (value) {
+ this.updateText(true);
+ var s = sign(this.scale.x) || 1;
+ this.scale.x = s * value / this._texture.orig.width;
+ this._width = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Text.prototype, "height", {
+ /** The height of the Text, setting this will actually modify the scale to achieve the value set. */
+ get: function () {
+ this.updateText(true);
+ return Math.abs(this.scale.y) * this._texture.orig.height;
+ },
+ set: function (value) {
+ this.updateText(true);
+ var s = sign(this.scale.y) || 1;
+ this.scale.y = s * value / this._texture.orig.height;
+ this._height = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Text.prototype, "style", {
+ /**
+ * Set the style of the text.
+ *
+ * Set up an event listener to listen for changes on the style object and mark the text as dirty.
+ */
+ get: function () {
+ // TODO: Can't have different types for getter and setter. The getter shouldn't have the ITextStyle
+ // since the setter creates the TextStyle. See this thread for more details:
+ // https://github.com/microsoft/TypeScript/issues/2521
+ return this._style;
+ },
+ set: function (style) {
+ style = style || {};
+ if (style instanceof TextStyle) {
+ this._style = style;
+ }
+ else {
+ this._style = new TextStyle(style);
+ }
+ this.localStyleID = -1;
+ this.dirty = true;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Text.prototype, "text", {
+ /** Set the copy for the text object. To split a line you can use '\n'. */
+ get: function () {
+ return this._text;
+ },
+ set: function (text) {
+ text = String(text === null || text === undefined ? '' : text);
+ if (this._text === text) {
+ return;
+ }
+ this._text = text;
+ this.dirty = true;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Text.prototype, "resolution", {
+ /**
+ * The resolution / device pixel ratio of the canvas.
+ *
+ * This is set to automatically match the renderer resolution by default, but can be overridden by setting manually.
+ * @default 1
+ */
+ get: function () {
+ return this._resolution;
+ },
+ set: function (value) {
+ this._autoResolution = false;
+ if (this._resolution === value) {
+ return;
+ }
+ this._resolution = value;
+ this.dirty = true;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * New behavior for `lineHeight` that's meant to mimic HTML text. A value of `true` will
+ * make sure the first baseline is offset by the `lineHeight` value if it is greater than `fontSize`.
+ * A value of `false` will use the legacy behavior and not change the baseline of the first line.
+ * In the next major release, we'll enable this by default.
+ */
+ Text.nextLineHeightBehavior = false;
+ /**
+ * New rendering behavior for letter-spacing which uses Chrome's new native API. This will
+ * lead to more accurate letter-spacing results because it does not try to manually draw
+ * each character. However, this Chrome API is experimental and may not serve all cases yet.
+ */
+ Text.experimentalLetterSpacing = false;
+ return Text;
+ }(Sprite));
+
+ /*!
+ * @pixi/prepare - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/prepare is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /**
+ * Default number of uploads per frame using prepare plugin.
+ * @static
+ * @memberof PIXI.settings
+ * @name UPLOADS_PER_FRAME
+ * @type {number}
+ * @default 4
+ */
+ settings$1.UPLOADS_PER_FRAME = 4;
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+ /* global Reflect, Promise */
+
+ var extendStatics$b = function(d, b) {
+ extendStatics$b = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
+ return extendStatics$b(d, b);
+ };
+
+ function __extends$b(d, b) {
+ extendStatics$b(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ }
+
+ /**
+ * CountLimiter limits the number of items handled by a {@link PIXI.BasePrepare} to a specified
+ * number of items per frame.
+ * @memberof PIXI
+ */
+ var CountLimiter = /** @class */ (function () {
+ /**
+ * @param maxItemsPerFrame - The maximum number of items that can be prepared each frame.
+ */
+ function CountLimiter(maxItemsPerFrame) {
+ this.maxItemsPerFrame = maxItemsPerFrame;
+ this.itemsLeft = 0;
+ }
+ /** Resets any counting properties to start fresh on a new frame. */
+ CountLimiter.prototype.beginFrame = function () {
+ this.itemsLeft = this.maxItemsPerFrame;
+ };
+ /**
+ * Checks to see if another item can be uploaded. This should only be called once per item.
+ * @returns If the item is allowed to be uploaded.
+ */
+ CountLimiter.prototype.allowedToUpload = function () {
+ return this.itemsLeft-- > 0;
+ };
+ return CountLimiter;
+ }());
+
+ /**
+ * Built-in hook to find multiple textures from objects like AnimatedSprites.
+ * @private
+ * @param item - Display object to check
+ * @param queue - Collection of items to upload
+ * @returns If a PIXI.Texture object was found.
+ */
+ function findMultipleBaseTextures(item, queue) {
+ var result = false;
+ // Objects with multiple textures
+ if (item && item._textures && item._textures.length) {
+ for (var i = 0; i < item._textures.length; i++) {
+ if (item._textures[i] instanceof Texture) {
+ var baseTexture = item._textures[i].baseTexture;
+ if (queue.indexOf(baseTexture) === -1) {
+ queue.push(baseTexture);
+ result = true;
+ }
+ }
+ }
+ }
+ return result;
+ }
+ /**
+ * Built-in hook to find BaseTextures from Texture.
+ * @private
+ * @param item - Display object to check
+ * @param queue - Collection of items to upload
+ * @returns If a PIXI.Texture object was found.
+ */
+ function findBaseTexture(item, queue) {
+ if (item.baseTexture instanceof BaseTexture) {
+ var texture = item.baseTexture;
+ if (queue.indexOf(texture) === -1) {
+ queue.push(texture);
+ }
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Built-in hook to find textures from objects.
+ * @private
+ * @param item - Display object to check
+ * @param queue - Collection of items to upload
+ * @returns If a PIXI.Texture object was found.
+ */
+ function findTexture(item, queue) {
+ if (item._texture && item._texture instanceof Texture) {
+ var texture = item._texture.baseTexture;
+ if (queue.indexOf(texture) === -1) {
+ queue.push(texture);
+ }
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Built-in hook to draw PIXI.Text to its texture.
+ * @private
+ * @param _helper - Not used by this upload handler
+ * @param item - Item to check
+ * @returns If item was uploaded.
+ */
+ function drawText(_helper, item) {
+ if (item instanceof Text) {
+ // updating text will return early if it is not dirty
+ item.updateText(true);
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Built-in hook to calculate a text style for a PIXI.Text object.
+ * @private
+ * @param _helper - Not used by this upload handler
+ * @param item - Item to check
+ * @returns If item was uploaded.
+ */
+ function calculateTextStyle(_helper, item) {
+ if (item instanceof TextStyle) {
+ var font = item.toFontString();
+ TextMetrics.measureFont(font);
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Built-in hook to find Text objects.
+ * @private
+ * @param item - Display object to check
+ * @param queue - Collection of items to upload
+ * @returns if a PIXI.Text object was found.
+ */
+ function findText(item, queue) {
+ if (item instanceof Text) {
+ // push the text style to prepare it - this can be really expensive
+ if (queue.indexOf(item.style) === -1) {
+ queue.push(item.style);
+ }
+ // also push the text object so that we can render it (to canvas/texture) if needed
+ if (queue.indexOf(item) === -1) {
+ queue.push(item);
+ }
+ // also push the Text's texture for upload to GPU
+ var texture = item._texture.baseTexture;
+ if (queue.indexOf(texture) === -1) {
+ queue.push(texture);
+ }
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Built-in hook to find TextStyle objects.
+ * @private
+ * @param item - Display object to check
+ * @param queue - Collection of items to upload
+ * @returns If a PIXI.TextStyle object was found.
+ */
+ function findTextStyle(item, queue) {
+ if (item instanceof TextStyle) {
+ if (queue.indexOf(item) === -1) {
+ queue.push(item);
+ }
+ return true;
+ }
+ return false;
+ }
+ /**
+ * The prepare manager provides functionality to upload content to the GPU.
+ *
+ * BasePrepare handles basic queuing functionality and is extended by
+ * {@link PIXI.Prepare} and {@link PIXI.CanvasPrepare}
+ * to provide preparation capabilities specific to their respective renderers.
+ * @example
+ * // Create a sprite
+ * const sprite = PIXI.Sprite.from('something.png');
+ *
+ * // Load object into GPU
+ * app.renderer.plugins.prepare.upload(sprite, () => {
+ *
+ * //Texture(s) has been uploaded to GPU
+ * app.stage.addChild(sprite);
+ *
+ * })
+ * @abstract
+ * @memberof PIXI
+ */
+ var BasePrepare = /** @class */ (function () {
+ /**
+ * @param {PIXI.AbstractRenderer} renderer - A reference to the current renderer
+ */
+ function BasePrepare(renderer) {
+ var _this = this;
+ this.limiter = new CountLimiter(settings$1.UPLOADS_PER_FRAME);
+ this.renderer = renderer;
+ this.uploadHookHelper = null;
+ this.queue = [];
+ this.addHooks = [];
+ this.uploadHooks = [];
+ this.completes = [];
+ this.ticking = false;
+ this.delayedTick = function () {
+ // unlikely, but in case we were destroyed between tick() and delayedTick()
+ if (!_this.queue) {
+ return;
+ }
+ _this.prepareItems();
+ };
+ // hooks to find the correct texture
+ this.registerFindHook(findText);
+ this.registerFindHook(findTextStyle);
+ this.registerFindHook(findMultipleBaseTextures);
+ this.registerFindHook(findBaseTexture);
+ this.registerFindHook(findTexture);
+ // upload hooks
+ this.registerUploadHook(drawText);
+ this.registerUploadHook(calculateTextStyle);
+ }
+ /** @ignore */
+ BasePrepare.prototype.upload = function (item, done) {
+ var _this = this;
+ if (typeof item === 'function') {
+ done = item;
+ item = null;
+ }
+ if (done) {
+ deprecation$1('6.5.0', 'BasePrepare.upload callback is deprecated, use the return Promise instead.');
+ }
+ return new Promise(function (resolve) {
+ // If a display object, search for items
+ // that we could upload
+ if (item) {
+ _this.add(item);
+ }
+ // TODO: remove done callback and just use resolve
+ var complete = function () {
+ done === null || done === void 0 ? void 0 : done();
+ resolve();
+ };
+ // Get the items for upload from the display
+ if (_this.queue.length) {
+ _this.completes.push(complete);
+ if (!_this.ticking) {
+ _this.ticking = true;
+ Ticker.system.addOnce(_this.tick, _this, exports.UPDATE_PRIORITY.UTILITY);
+ }
+ }
+ else {
+ complete();
+ }
+ });
+ };
+ /**
+ * Handle tick update
+ * @private
+ */
+ BasePrepare.prototype.tick = function () {
+ setTimeout(this.delayedTick, 0);
+ };
+ /**
+ * Actually prepare items. This is handled outside of the tick because it will take a while
+ * and we do NOT want to block the current animation frame from rendering.
+ * @private
+ */
+ BasePrepare.prototype.prepareItems = function () {
+ this.limiter.beginFrame();
+ // Upload the graphics
+ while (this.queue.length && this.limiter.allowedToUpload()) {
+ var item = this.queue[0];
+ var uploaded = false;
+ if (item && !item._destroyed) {
+ for (var i = 0, len = this.uploadHooks.length; i < len; i++) {
+ if (this.uploadHooks[i](this.uploadHookHelper, item)) {
+ this.queue.shift();
+ uploaded = true;
+ break;
+ }
+ }
+ }
+ if (!uploaded) {
+ this.queue.shift();
+ }
+ }
+ // We're finished
+ if (!this.queue.length) {
+ this.ticking = false;
+ var completes = this.completes.slice(0);
+ this.completes.length = 0;
+ for (var i = 0, len = completes.length; i < len; i++) {
+ completes[i]();
+ }
+ }
+ else {
+ // if we are not finished, on the next rAF do this again
+ Ticker.system.addOnce(this.tick, this, exports.UPDATE_PRIORITY.UTILITY);
+ }
+ };
+ /**
+ * Adds hooks for finding items.
+ * @param {Function} addHook - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @returns Instance of plugin for chaining.
+ */
+ BasePrepare.prototype.registerFindHook = function (addHook) {
+ if (addHook) {
+ this.addHooks.push(addHook);
+ }
+ return this;
+ };
+ /**
+ * Adds hooks for uploading items.
+ * @param {Function} uploadHook - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @returns Instance of plugin for chaining.
+ */
+ BasePrepare.prototype.registerUploadHook = function (uploadHook) {
+ if (uploadHook) {
+ this.uploadHooks.push(uploadHook);
+ }
+ return this;
+ };
+ /**
+ * Manually add an item to the uploading queue.
+ * @param {PIXI.DisplayObject|PIXI.Container|PIXI.BaseTexture|PIXI.Texture|PIXI.Graphics|PIXI.Text|*} item - Object to
+ * add to the queue
+ * @returns Instance of plugin for chaining.
+ */
+ BasePrepare.prototype.add = function (item) {
+ // Add additional hooks for finding elements on special
+ // types of objects that
+ for (var i = 0, len = this.addHooks.length; i < len; i++) {
+ if (this.addHooks[i](item, this.queue)) {
+ break;
+ }
+ }
+ // Get children recursively
+ if (item instanceof Container) {
+ for (var i = item.children.length - 1; i >= 0; i--) {
+ this.add(item.children[i]);
+ }
+ }
+ return this;
+ };
+ /** Destroys the plugin, don't use after this. */
+ BasePrepare.prototype.destroy = function () {
+ if (this.ticking) {
+ Ticker.system.remove(this.tick, this);
+ }
+ this.ticking = false;
+ this.addHooks = null;
+ this.uploadHooks = null;
+ this.renderer = null;
+ this.completes = null;
+ this.queue = null;
+ this.limiter = null;
+ this.uploadHookHelper = null;
+ };
+ return BasePrepare;
+ }());
+
+ /**
+ * Built-in hook to upload PIXI.Texture objects to the GPU.
+ * @private
+ * @param renderer - instance of the webgl renderer
+ * @param item - Item to check
+ * @returns If item was uploaded.
+ */
+ function uploadBaseTextures(renderer, item) {
+ if (item instanceof BaseTexture) {
+ // if the texture already has a GL texture, then the texture has been prepared or rendered
+ // before now. If the texture changed, then the changer should be calling texture.update() which
+ // reuploads the texture without need for preparing it again
+ if (!item._glTextures[renderer.CONTEXT_UID]) {
+ renderer.texture.bind(item);
+ }
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Built-in hook to upload PIXI.Graphics to the GPU.
+ * @private
+ * @param renderer - instance of the webgl renderer
+ * @param item - Item to check
+ * @returns If item was uploaded.
+ */
+ function uploadGraphics(renderer, item) {
+ if (!(item instanceof Graphics)) {
+ return false;
+ }
+ var geometry = item.geometry;
+ // update dirty graphics to get batches
+ item.finishPoly();
+ geometry.updateBatches();
+ var batches = geometry.batches;
+ // upload all textures found in styles
+ for (var i = 0; i < batches.length; i++) {
+ var texture = batches[i].style.texture;
+ if (texture) {
+ uploadBaseTextures(renderer, texture.baseTexture);
+ }
+ }
+ // if its not batchable - update vao for particular shader
+ if (!geometry.batchable) {
+ renderer.geometry.bind(geometry, item._resolveDirectShader(renderer));
+ }
+ return true;
+ }
+ /**
+ * Built-in hook to find graphics.
+ * @private
+ * @param item - Display object to check
+ * @param queue - Collection of items to upload
+ * @returns if a PIXI.Graphics object was found.
+ */
+ function findGraphics(item, queue) {
+ if (item instanceof Graphics) {
+ queue.push(item);
+ return true;
+ }
+ return false;
+ }
+ /**
+ * The prepare plugin provides renderer-specific plugins for pre-rendering DisplayObjects. These plugins are useful for
+ * asynchronously preparing and uploading to the GPU assets, textures, graphics waiting to be displayed.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.Renderer#plugins}.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ * document.body.appendChild(app.view);
+ *
+ * // Don't start rendering right away
+ * app.stop();
+ *
+ * // create a display object
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add to the stage
+ * app.stage.addChild(rect);
+ *
+ * // Don't start rendering until the graphic is uploaded to the GPU
+ * app.renderer.plugins.prepare.upload(app.stage, () => {
+ * app.start();
+ * });
+ * @memberof PIXI
+ */
+ var Prepare = /** @class */ (function (_super) {
+ __extends$b(Prepare, _super);
+ /**
+ * @param {PIXI.Renderer} renderer - A reference to the current renderer
+ */
+ function Prepare(renderer) {
+ var _this = _super.call(this, renderer) || this;
+ _this.uploadHookHelper = _this.renderer;
+ // Add textures and graphics to upload
+ _this.registerFindHook(findGraphics);
+ _this.registerUploadHook(uploadBaseTextures);
+ _this.registerUploadHook(uploadGraphics);
+ return _this;
+ }
+ /** @ignore */
+ Prepare.extension = {
+ name: 'prepare',
+ type: exports.ExtensionType.RendererPlugin,
+ };
+ return Prepare;
+ }(BasePrepare));
+
+ /**
+ * TimeLimiter limits the number of items handled by a {@link PIXI.BasePrepare} to a specified
+ * number of milliseconds per frame.
+ * @memberof PIXI
+ */
+ var TimeLimiter = /** @class */ (function () {
+ /** @param maxMilliseconds - The maximum milliseconds that can be spent preparing items each frame. */
+ function TimeLimiter(maxMilliseconds) {
+ this.maxMilliseconds = maxMilliseconds;
+ this.frameStart = 0;
+ }
+ /** Resets any counting properties to start fresh on a new frame. */
+ TimeLimiter.prototype.beginFrame = function () {
+ this.frameStart = Date.now();
+ };
+ /**
+ * Checks to see if another item can be uploaded. This should only be called once per item.
+ * @returns - If the item is allowed to be uploaded.
+ */
+ TimeLimiter.prototype.allowedToUpload = function () {
+ return Date.now() - this.frameStart < this.maxMilliseconds;
+ };
+ return TimeLimiter;
+ }());
+
+ /*!
+ * @pixi/spritesheet - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/spritesheet is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /**
+ * Utility class for maintaining reference to a collection
+ * of Textures on a single Spritesheet.
+ *
+ * To access a sprite sheet from your code you may pass its JSON data file to Pixi's loader:
+ *
+ * ```js
+ * PIXI.Loader.shared.add("images/spritesheet.json").load(setup);
+ *
+ * function setup() {
+ * let sheet = PIXI.Loader.shared.resources["images/spritesheet.json"].spritesheet;
+ * ...
+ * }
+ * ```
+ *
+ * Alternately, you may circumvent the loader by instantiating the Spritesheet directly:
+ * ```js
+ * const sheet = new PIXI.Spritesheet(texture, spritesheetData);
+ * await sheet.parse();
+ * console.log('Spritesheet ready to use!');
+ * ```
+ *
+ * With the `sheet.textures` you can create Sprite objects,`sheet.animations` can be used to create an AnimatedSprite.
+ *
+ * Sprite sheets can be packed using tools like {@link https://codeandweb.com/texturepacker|TexturePacker},
+ * {@link https://renderhjs.net/shoebox/|Shoebox} or {@link https://github.com/krzysztof-o/spritesheet.js|Spritesheet.js}.
+ * Default anchor points (see {@link PIXI.Texture#defaultAnchor}) and grouping of animation sprites are currently only
+ * supported by TexturePacker.
+ * @memberof PIXI
+ */
+ var Spritesheet = /** @class */ (function () {
+ /**
+ * @param texture - Reference to the source BaseTexture object.
+ * @param {object} data - Spritesheet image data.
+ * @param resolutionFilename - The filename to consider when determining
+ * the resolution of the spritesheet. If not provided, the imageUrl will
+ * be used on the BaseTexture.
+ */
+ function Spritesheet(texture, data, resolutionFilename) {
+ if (resolutionFilename === void 0) { resolutionFilename = null; }
+ /** For multi-packed spritesheets, this contains a reference to all the other spritesheets it depends on. */
+ this.linkedSheets = [];
+ this._texture = texture instanceof Texture ? texture : null;
+ this.baseTexture = texture instanceof BaseTexture ? texture : this._texture.baseTexture;
+ this.textures = {};
+ this.animations = {};
+ this.data = data;
+ var resource = this.baseTexture.resource;
+ this.resolution = this._updateResolution(resolutionFilename || (resource ? resource.url : null));
+ this._frames = this.data.frames;
+ this._frameKeys = Object.keys(this._frames);
+ this._batchIndex = 0;
+ this._callback = null;
+ }
+ /**
+ * Generate the resolution from the filename or fallback
+ * to the meta.scale field of the JSON data.
+ * @param resolutionFilename - The filename to use for resolving
+ * the default resolution.
+ * @returns Resolution to use for spritesheet.
+ */
+ Spritesheet.prototype._updateResolution = function (resolutionFilename) {
+ if (resolutionFilename === void 0) { resolutionFilename = null; }
+ var scale = this.data.meta.scale;
+ // Use a defaultValue of `null` to check if a url-based resolution is set
+ var resolution = getResolutionOfUrl(resolutionFilename, null);
+ // No resolution found via URL
+ if (resolution === null) {
+ // Use the scale value or default to 1
+ resolution = scale !== undefined ? parseFloat(scale) : 1;
+ }
+ // For non-1 resolutions, update baseTexture
+ if (resolution !== 1) {
+ this.baseTexture.setResolution(resolution);
+ }
+ return resolution;
+ };
+ /** @ignore */
+ Spritesheet.prototype.parse = function (callback) {
+ var _this = this;
+ if (callback) {
+ deprecation$1('6.5.0', 'Spritesheet.parse callback is deprecated, use the return Promise instead.');
+ }
+ return new Promise(function (resolve) {
+ _this._callback = function (textures) {
+ callback === null || callback === void 0 ? void 0 : callback(textures);
+ resolve(textures);
+ };
+ _this._batchIndex = 0;
+ if (_this._frameKeys.length <= Spritesheet.BATCH_SIZE) {
+ _this._processFrames(0);
+ _this._processAnimations();
+ _this._parseComplete();
+ }
+ else {
+ _this._nextBatch();
+ }
+ });
+ };
+ /**
+ * Process a batch of frames
+ * @param initialFrameIndex - The index of frame to start.
+ */
+ Spritesheet.prototype._processFrames = function (initialFrameIndex) {
+ var frameIndex = initialFrameIndex;
+ var maxFrames = Spritesheet.BATCH_SIZE;
+ while (frameIndex - initialFrameIndex < maxFrames && frameIndex < this._frameKeys.length) {
+ var i = this._frameKeys[frameIndex];
+ var data = this._frames[i];
+ var rect = data.frame;
+ if (rect) {
+ var frame = null;
+ var trim = null;
+ var sourceSize = data.trimmed !== false && data.sourceSize
+ ? data.sourceSize : data.frame;
+ var orig = new Rectangle(0, 0, Math.floor(sourceSize.w) / this.resolution, Math.floor(sourceSize.h) / this.resolution);
+ if (data.rotated) {
+ frame = new Rectangle(Math.floor(rect.x) / this.resolution, Math.floor(rect.y) / this.resolution, Math.floor(rect.h) / this.resolution, Math.floor(rect.w) / this.resolution);
+ }
+ else {
+ frame = new Rectangle(Math.floor(rect.x) / this.resolution, Math.floor(rect.y) / this.resolution, Math.floor(rect.w) / this.resolution, Math.floor(rect.h) / this.resolution);
+ }
+ // Check to see if the sprite is trimmed
+ if (data.trimmed !== false && data.spriteSourceSize) {
+ trim = new Rectangle(Math.floor(data.spriteSourceSize.x) / this.resolution, Math.floor(data.spriteSourceSize.y) / this.resolution, Math.floor(rect.w) / this.resolution, Math.floor(rect.h) / this.resolution);
+ }
+ this.textures[i] = new Texture(this.baseTexture, frame, orig, trim, data.rotated ? 2 : 0, data.anchor);
+ // lets also add the frame to pixi's global cache for 'from' and 'fromLoader' functions
+ Texture.addToCache(this.textures[i], i);
+ }
+ frameIndex++;
+ }
+ };
+ /** Parse animations config. */
+ Spritesheet.prototype._processAnimations = function () {
+ var animations = this.data.animations || {};
+ for (var animName in animations) {
+ this.animations[animName] = [];
+ for (var i = 0; i < animations[animName].length; i++) {
+ var frameName = animations[animName][i];
+ this.animations[animName].push(this.textures[frameName]);
+ }
+ }
+ };
+ /** The parse has completed. */
+ Spritesheet.prototype._parseComplete = function () {
+ var callback = this._callback;
+ this._callback = null;
+ this._batchIndex = 0;
+ callback.call(this, this.textures);
+ };
+ /** Begin the next batch of textures. */
+ Spritesheet.prototype._nextBatch = function () {
+ var _this = this;
+ this._processFrames(this._batchIndex * Spritesheet.BATCH_SIZE);
+ this._batchIndex++;
+ setTimeout(function () {
+ if (_this._batchIndex * Spritesheet.BATCH_SIZE < _this._frameKeys.length) {
+ _this._nextBatch();
+ }
+ else {
+ _this._processAnimations();
+ _this._parseComplete();
+ }
+ }, 0);
+ };
+ /**
+ * Destroy Spritesheet and don't use after this.
+ * @param {boolean} [destroyBase=false] - Whether to destroy the base texture as well
+ */
+ Spritesheet.prototype.destroy = function (destroyBase) {
+ var _a;
+ if (destroyBase === void 0) { destroyBase = false; }
+ for (var i in this.textures) {
+ this.textures[i].destroy();
+ }
+ this._frames = null;
+ this._frameKeys = null;
+ this.data = null;
+ this.textures = null;
+ if (destroyBase) {
+ (_a = this._texture) === null || _a === void 0 ? void 0 : _a.destroy();
+ this.baseTexture.destroy();
+ }
+ this._texture = null;
+ this.baseTexture = null;
+ this.linkedSheets = [];
+ };
+ /** The maximum number of Textures to build per process. */
+ Spritesheet.BATCH_SIZE = 1000;
+ return Spritesheet;
+ }());
+ /**
+ * Reference to Spritesheet object created.
+ * @member {PIXI.Spritesheet} spritesheet
+ * @memberof PIXI.LoaderResource
+ * @instance
+ */
+ /**
+ * Dictionary of textures from Spritesheet.
+ * @member {Object<string, PIXI.Texture>} textures
+ * @memberof PIXI.LoaderResource
+ * @instance
+ */
+
+ /**
+ * {@link PIXI.Loader} middleware for loading texture atlases that have been created with
+ * TexturePacker or similar JSON-based spritesheet.
+ *
+ * This middleware automatically generates Texture resources.
+ *
+ * If you're using Webpack or other bundlers and plan on bundling the atlas' JSON,
+ * use the {@link PIXI.Spritesheet} class to directly parse the JSON.
+ *
+ * The Loader's image Resource name is automatically appended with `"_image"`.
+ * If a Resource with this name is already loaded, the Loader will skip parsing the
+ * Spritesheet. The code below will generate an internal Loader Resource called `"myatlas_image"`.
+ * @example
+ * loader.add('myatlas', 'path/to/myatlas.json');
+ * loader.load(() => {
+ * loader.resources.myatlas; // atlas JSON resource
+ * loader.resources.myatlas_image; // atlas Image resource
+ * });
+ * @memberof PIXI
+ */
+ var SpritesheetLoader = /** @class */ (function () {
+ function SpritesheetLoader() {
+ }
+ /**
+ * Called after a resource is loaded.
+ * @see PIXI.Loader.loaderMiddleware
+ * @param resource
+ * @param next
+ */
+ SpritesheetLoader.use = function (resource, next) {
+ var _a, _b;
+ // because this is middleware, it execute in loader context. `this` = loader
+ var loader = this;
+ var imageResourceName = resource.name + "_image";
+ // skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
+ if (!resource.data
+ || resource.type !== exports.LoaderResource.TYPE.JSON
+ || !resource.data.frames
+ || loader.resources[imageResourceName]) {
+ next();
+ return;
+ }
+ // Check and add the multi atlas
+ // Heavily influenced and based on https://github.com/rocket-ua/pixi-tps-loader/blob/master/src/ResourceLoader.js
+ // eslint-disable-next-line camelcase
+ var multiPacks = (_b = (_a = resource.data) === null || _a === void 0 ? void 0 : _a.meta) === null || _b === void 0 ? void 0 : _b.related_multi_packs;
+ if (Array.isArray(multiPacks)) {
+ var _loop_1 = function (item) {
+ if (typeof item !== 'string') {
+ return "continue";
+ }
+ var itemName = item.replace('.json', '');
+ var itemUrl = url.resolve(resource.url.replace(loader.baseUrl, ''), item);
+ // Check if the file wasn't already added as multipacks are redundant
+ if (loader.resources[itemName]
+ || Object.values(loader.resources).some(function (r) { return url.format(url.parse(r.url)) === itemUrl; })) {
+ return "continue";
+ }
+ var options = {
+ crossOrigin: resource.crossOrigin,
+ loadType: exports.LoaderResource.LOAD_TYPE.XHR,
+ xhrType: exports.LoaderResource.XHR_RESPONSE_TYPE.JSON,
+ parentResource: resource,
+ metadata: resource.metadata
+ };
+ loader.add(itemName, itemUrl, options);
+ };
+ for (var _i = 0, multiPacks_1 = multiPacks; _i < multiPacks_1.length; _i++) {
+ var item = multiPacks_1[_i];
+ _loop_1(item);
+ }
+ }
+ var loadOptions = {
+ crossOrigin: resource.crossOrigin,
+ metadata: resource.metadata.imageMetadata,
+ parentResource: resource,
+ };
+ var resourcePath = SpritesheetLoader.getResourcePath(resource, loader.baseUrl);
+ // load the image for this sheet
+ loader.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res) {
+ if (res.error) {
+ next(res.error);
+ return;
+ }
+ var spritesheet = new Spritesheet(res.texture, resource.data, resource.url);
+ spritesheet.parse().then(function () {
+ resource.spritesheet = spritesheet;
+ resource.textures = spritesheet.textures;
+ next();
+ });
+ });
+ };
+ /**
+ * Get the spritesheets root path
+ * @param resource - Resource to check path
+ * @param baseUrl - Base root url
+ */
+ SpritesheetLoader.getResourcePath = function (resource, baseUrl) {
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl) {
+ return resource.data.meta.image;
+ }
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+ };
+ /** @ignore */
+ SpritesheetLoader.extension = exports.ExtensionType.Loader;
+ return SpritesheetLoader;
+ }());
+
+ /*!
+ * @pixi/sprite-tiling - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/sprite-tiling is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+ /* global Reflect, Promise */
+
+ var extendStatics$a = function(d, b) {
+ extendStatics$a = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
+ return extendStatics$a(d, b);
+ };
+
+ function __extends$a(d, b) {
+ extendStatics$a(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ }
+
+ var tempPoint$1 = new Point();
+ /**
+ * A tiling sprite is a fast way of rendering a tiling image.
+ * @memberof PIXI
+ */
+ var TilingSprite = /** @class */ (function (_super) {
+ __extends$a(TilingSprite, _super);
+ /**
+ * @param texture - The texture of the tiling sprite.
+ * @param width - The width of the tiling sprite.
+ * @param height - The height of the tiling sprite.
+ */
+ function TilingSprite(texture, width, height) {
+ if (width === void 0) { width = 100; }
+ if (height === void 0) { height = 100; }
+ var _this = _super.call(this, texture) || this;
+ _this.tileTransform = new Transform();
+ // The width of the tiling sprite
+ _this._width = width;
+ // The height of the tiling sprite
+ _this._height = height;
+ _this.uvMatrix = _this.texture.uvMatrix || new TextureMatrix(texture);
+ /**
+ * Plugin that is responsible for rendering this element.
+ * Allows to customize the rendering process without overriding '_render' method.
+ * @default 'tilingSprite'
+ */
+ _this.pluginName = 'tilingSprite';
+ _this.uvRespectAnchor = false;
+ return _this;
+ }
+ Object.defineProperty(TilingSprite.prototype, "clampMargin", {
+ /**
+ * Changes frame clamping in corresponding textureTransform, shortcut
+ * Change to -0.5 to add a pixel to the edge, recommended for transparent trimmed textures in atlas
+ * @default 0.5
+ * @member {number}
+ */
+ get: function () {
+ return this.uvMatrix.clampMargin;
+ },
+ set: function (value) {
+ this.uvMatrix.clampMargin = value;
+ this.uvMatrix.update(true);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TilingSprite.prototype, "tileScale", {
+ /** The scaling of the image that is being tiled. */
+ get: function () {
+ return this.tileTransform.scale;
+ },
+ set: function (value) {
+ this.tileTransform.scale.copyFrom(value);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TilingSprite.prototype, "tilePosition", {
+ /** The offset of the image that is being tiled. */
+ get: function () {
+ return this.tileTransform.position;
+ },
+ set: function (value) {
+ this.tileTransform.position.copyFrom(value);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * @protected
+ */
+ TilingSprite.prototype._onTextureUpdate = function () {
+ if (this.uvMatrix) {
+ this.uvMatrix.texture = this._texture;
+ }
+ this._cachedTint = 0xFFFFFF;
+ };
+ /**
+ * Renders the object using the WebGL renderer
+ * @param renderer - The renderer
+ */
+ TilingSprite.prototype._render = function (renderer) {
+ // tweak our texture temporarily..
+ var texture = this._texture;
+ if (!texture || !texture.valid) {
+ return;
+ }
+ this.tileTransform.updateLocalTransform();
+ this.uvMatrix.update();
+ renderer.batch.setObjectRenderer(renderer.plugins[this.pluginName]);
+ renderer.plugins[this.pluginName].render(this);
+ };
+ /** Updates the bounds of the tiling sprite. */
+ TilingSprite.prototype._calculateBounds = function () {
+ var minX = this._width * -this._anchor._x;
+ var minY = this._height * -this._anchor._y;
+ var maxX = this._width * (1 - this._anchor._x);
+ var maxY = this._height * (1 - this._anchor._y);
+ this._bounds.addFrame(this.transform, minX, minY, maxX, maxY);
+ };
+ /**
+ * Gets the local bounds of the sprite object.
+ * @param rect - Optional output rectangle.
+ * @returns The bounds.
+ */
+ TilingSprite.prototype.getLocalBounds = function (rect) {
+ // we can do a fast local bounds if the sprite has no children!
+ if (this.children.length === 0) {
+ this._bounds.minX = this._width * -this._anchor._x;
+ this._bounds.minY = this._height * -this._anchor._y;
+ this._bounds.maxX = this._width * (1 - this._anchor._x);
+ this._bounds.maxY = this._height * (1 - this._anchor._y);
+ if (!rect) {
+ if (!this._localBoundsRect) {
+ this._localBoundsRect = new Rectangle();
+ }
+ rect = this._localBoundsRect;
+ }
+ return this._bounds.getRectangle(rect);
+ }
+ return _super.prototype.getLocalBounds.call(this, rect);
+ };
+ /**
+ * Checks if a point is inside this tiling sprite.
+ * @param point - The point to check.
+ * @returns Whether or not the sprite contains the point.
+ */
+ TilingSprite.prototype.containsPoint = function (point) {
+ this.worldTransform.applyInverse(point, tempPoint$1);
+ var width = this._width;
+ var height = this._height;
+ var x1 = -width * this.anchor._x;
+ if (tempPoint$1.x >= x1 && tempPoint$1.x < x1 + width) {
+ var y1 = -height * this.anchor._y;
+ if (tempPoint$1.y >= y1 && tempPoint$1.y < y1 + height) {
+ return true;
+ }
+ }
+ return false;
+ };
+ /**
+ * Destroys this sprite and optionally its texture and children
+ * @param {object|boolean} [options] - Options parameter. A boolean will act as if all options
+ * have been set to that value
+ * @param {boolean} [options.children=false] - if set to true, all the children will have their destroy
+ * method called as well. 'options' will be passed on to those calls.
+ * @param {boolean} [options.texture=false] - Should it destroy the current texture of the sprite as well
+ * @param {boolean} [options.baseTexture=false] - Should it destroy the base texture of the sprite as well
+ */
+ TilingSprite.prototype.destroy = function (options) {
+ _super.prototype.destroy.call(this, options);
+ this.tileTransform = null;
+ this.uvMatrix = null;
+ };
+ /**
+ * Helper function that creates a new tiling sprite based on the source you provide.
+ * The source can be - frame id, image url, video url, canvas element, video element, base texture
+ * @static
+ * @param {string|PIXI.Texture|HTMLCanvasElement|HTMLVideoElement} source - Source to create texture from
+ * @param {object} options - See {@link PIXI.BaseTexture}'s constructor for options.
+ * @param {number} options.width - required width of the tiling sprite
+ * @param {number} options.height - required height of the tiling sprite
+ * @returns {PIXI.TilingSprite} The newly created texture
+ */
+ TilingSprite.from = function (source, options) {
+ var texture = (source instanceof Texture)
+ ? source
+ : Texture.from(source, options);
+ return new TilingSprite(texture, options.width, options.height);
+ };
+ Object.defineProperty(TilingSprite.prototype, "width", {
+ /** The width of the sprite, setting this will actually modify the scale to achieve the value set. */
+ get: function () {
+ return this._width;
+ },
+ set: function (value) {
+ this._width = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(TilingSprite.prototype, "height", {
+ /** The height of the TilingSprite, setting this will actually modify the scale to achieve the value set. */
+ get: function () {
+ return this._height;
+ },
+ set: function (value) {
+ this._height = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return TilingSprite;
+ }(Sprite));
+
+ var fragmentSimpleSrc = "#version 100\n#define SHADER_NAME Tiling-Sprite-Simple-100\n\nprecision lowp float;\n\nvarying vec2 vTextureCoord;\n\nuniform sampler2D uSampler;\nuniform vec4 uColor;\n\nvoid main(void)\n{\n vec4 texSample = texture2D(uSampler, vTextureCoord);\n gl_FragColor = texSample * uColor;\n}\n";
+
+ var gl1VertexSrc = "#version 100\n#define SHADER_NAME Tiling-Sprite-100\n\nprecision lowp float;\n\nattribute vec2 aVertexPosition;\nattribute vec2 aTextureCoord;\n\nuniform mat3 projectionMatrix;\nuniform mat3 translationMatrix;\nuniform mat3 uTransform;\n\nvarying vec2 vTextureCoord;\n\nvoid main(void)\n{\n gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);\n\n vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;\n}\n";
+
+ var gl1FragmentSrc = "#version 100\n#ifdef GL_EXT_shader_texture_lod\n #extension GL_EXT_shader_texture_lod : enable\n#endif\n#define SHADER_NAME Tiling-Sprite-100\n\nprecision lowp float;\n\nvarying vec2 vTextureCoord;\n\nuniform sampler2D uSampler;\nuniform vec4 uColor;\nuniform mat3 uMapCoord;\nuniform vec4 uClampFrame;\nuniform vec2 uClampOffset;\n\nvoid main(void)\n{\n vec2 coord = vTextureCoord + ceil(uClampOffset - vTextureCoord);\n coord = (uMapCoord * vec3(coord, 1.0)).xy;\n vec2 unclamped = coord;\n coord = clamp(coord, uClampFrame.xy, uClampFrame.zw);\n\n #ifdef GL_EXT_shader_texture_lod\n vec4 texSample = unclamped == coord\n ? texture2D(uSampler, coord) \n : texture2DLodEXT(uSampler, coord, 0);\n #else\n vec4 texSample = texture2D(uSampler, coord);\n #endif\n\n gl_FragColor = texSample * uColor;\n}\n";
+
+ var gl2VertexSrc = "#version 300 es\n#define SHADER_NAME Tiling-Sprite-300\n\nprecision lowp float;\n\nin vec2 aVertexPosition;\nin vec2 aTextureCoord;\n\nuniform mat3 projectionMatrix;\nuniform mat3 translationMatrix;\nuniform mat3 uTransform;\n\nout vec2 vTextureCoord;\n\nvoid main(void)\n{\n gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);\n\n vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;\n}\n";
+
+ var gl2FragmentSrc = "#version 300 es\n#define SHADER_NAME Tiling-Sprite-100\n\nprecision lowp float;\n\nin vec2 vTextureCoord;\n\nout vec4 fragmentColor;\n\nuniform sampler2D uSampler;\nuniform vec4 uColor;\nuniform mat3 uMapCoord;\nuniform vec4 uClampFrame;\nuniform vec2 uClampOffset;\n\nvoid main(void)\n{\n vec2 coord = vTextureCoord + ceil(uClampOffset - vTextureCoord);\n coord = (uMapCoord * vec3(coord, 1.0)).xy;\n vec2 unclamped = coord;\n coord = clamp(coord, uClampFrame.xy, uClampFrame.zw);\n\n vec4 texSample = texture(uSampler, coord, unclamped == coord ? 0.0f : -32.0f);// lod-bias very negative to force lod 0\n\n fragmentColor = texSample * uColor;\n}\n";
+
+ var tempMat = new Matrix();
+ /**
+ * WebGL renderer plugin for tiling sprites
+ * @class
+ * @memberof PIXI
+ * @extends PIXI.ObjectRenderer
+ */
+ var TilingSpriteRenderer = /** @class */ (function (_super) {
+ __extends$a(TilingSpriteRenderer, _super);
+ /**
+ * constructor for renderer
+ * @param {PIXI.Renderer} renderer - The renderer this tiling awesomeness works for.
+ */
+ function TilingSpriteRenderer(renderer) {
+ var _this = _super.call(this, renderer) || this;
+ // WebGL version is not available during initialization!
+ renderer.runners.contextChange.add(_this);
+ _this.quad = new QuadUv();
+ /**
+ * The WebGL state in which this renderer will work.
+ * @member {PIXI.State}
+ * @readonly
+ */
+ _this.state = State.for2d();
+ return _this;
+ }
+ /** Creates shaders when context is initialized. */
+ TilingSpriteRenderer.prototype.contextChange = function () {
+ var renderer = this.renderer;
+ var uniforms = { globals: renderer.globalUniforms };
+ this.simpleShader = Shader.from(gl1VertexSrc, fragmentSimpleSrc, uniforms);
+ this.shader = renderer.context.webGLVersion > 1
+ ? Shader.from(gl2VertexSrc, gl2FragmentSrc, uniforms)
+ : Shader.from(gl1VertexSrc, gl1FragmentSrc, uniforms);
+ };
+ /**
+ * @param {PIXI.TilingSprite} ts - tilingSprite to be rendered
+ */
+ TilingSpriteRenderer.prototype.render = function (ts) {
+ var renderer = this.renderer;
+ var quad = this.quad;
+ var vertices = quad.vertices;
+ vertices[0] = vertices[6] = (ts._width) * -ts.anchor.x;
+ vertices[1] = vertices[3] = ts._height * -ts.anchor.y;
+ vertices[2] = vertices[4] = (ts._width) * (1.0 - ts.anchor.x);
+ vertices[5] = vertices[7] = ts._height * (1.0 - ts.anchor.y);
+ var anchorX = ts.uvRespectAnchor ? ts.anchor.x : 0;
+ var anchorY = ts.uvRespectAnchor ? ts.anchor.y : 0;
+ vertices = quad.uvs;
+ vertices[0] = vertices[6] = -anchorX;
+ vertices[1] = vertices[3] = -anchorY;
+ vertices[2] = vertices[4] = 1.0 - anchorX;
+ vertices[5] = vertices[7] = 1.0 - anchorY;
+ quad.invalidate();
+ var tex = ts._texture;
+ var baseTex = tex.baseTexture;
+ var premultiplied = baseTex.alphaMode > 0;
+ var lt = ts.tileTransform.localTransform;
+ var uv = ts.uvMatrix;
+ var isSimple = baseTex.isPowerOfTwo
+ && tex.frame.width === baseTex.width && tex.frame.height === baseTex.height;
+ // auto, force repeat wrapMode for big tiling textures
+ if (isSimple) {
+ if (!baseTex._glTextures[renderer.CONTEXT_UID]) {
+ if (baseTex.wrapMode === exports.WRAP_MODES.CLAMP) {
+ baseTex.wrapMode = exports.WRAP_MODES.REPEAT;
+ }
+ }
+ else {
+ isSimple = baseTex.wrapMode !== exports.WRAP_MODES.CLAMP;
+ }
+ }
+ var shader = isSimple ? this.simpleShader : this.shader;
+ var w = tex.width;
+ var h = tex.height;
+ var W = ts._width;
+ var H = ts._height;
+ tempMat.set(lt.a * w / W, lt.b * w / H, lt.c * h / W, lt.d * h / H, lt.tx / W, lt.ty / H);
+ // that part is the same as above:
+ // tempMat.identity();
+ // tempMat.scale(tex.width, tex.height);
+ // tempMat.prepend(lt);
+ // tempMat.scale(1.0 / ts._width, 1.0 / ts._height);
+ tempMat.invert();
+ if (isSimple) {
+ tempMat.prepend(uv.mapCoord);
+ }
+ else {
+ shader.uniforms.uMapCoord = uv.mapCoord.toArray(true);
+ shader.uniforms.uClampFrame = uv.uClampFrame;
+ shader.uniforms.uClampOffset = uv.uClampOffset;
+ }
+ shader.uniforms.uTransform = tempMat.toArray(true);
+ shader.uniforms.uColor = premultiplyTintToRgba(ts.tint, ts.worldAlpha, shader.uniforms.uColor, premultiplied);
+ shader.uniforms.translationMatrix = ts.transform.worldTransform.toArray(true);
+ shader.uniforms.uSampler = tex;
+ renderer.shader.bind(shader);
+ renderer.geometry.bind(quad);
+ this.state.blendMode = correctBlendMode(ts.blendMode, premultiplied);
+ renderer.state.set(this.state);
+ renderer.geometry.draw(this.renderer.gl.TRIANGLES, 6, 0);
+ };
+ /** @ignore */
+ TilingSpriteRenderer.extension = {
+ name: 'tilingSprite',
+ type: exports.ExtensionType.RendererPlugin,
+ };
+ return TilingSpriteRenderer;
+ }(ObjectRenderer));
+
+ /*!
+ * @pixi/mesh - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/mesh is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+ /* global Reflect, Promise */
+
+ var extendStatics$9 = function(d, b) {
+ extendStatics$9 = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
+ return extendStatics$9(d, b);
+ };
+
+ function __extends$9(d, b) {
+ extendStatics$9(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ }
+
+ /**
+ * Class controls cache for UV mapping from Texture normal space to BaseTexture normal space.
+ * @memberof PIXI
+ */
+ var MeshBatchUvs = /** @class */ (function () {
+ /**
+ * @param uvBuffer - Buffer with normalized uv's
+ * @param uvMatrix - Material UV matrix
+ */
+ function MeshBatchUvs(uvBuffer, uvMatrix) {
+ this.uvBuffer = uvBuffer;
+ this.uvMatrix = uvMatrix;
+ this.data = null;
+ this._bufferUpdateId = -1;
+ this._textureUpdateId = -1;
+ this._updateID = 0;
+ }
+ /**
+ * Updates
+ * @param forceUpdate - force the update
+ */
+ MeshBatchUvs.prototype.update = function (forceUpdate) {
+ if (!forceUpdate
+ && this._bufferUpdateId === this.uvBuffer._updateID
+ && this._textureUpdateId === this.uvMatrix._updateID) {
+ return;
+ }
+ this._bufferUpdateId = this.uvBuffer._updateID;
+ this._textureUpdateId = this.uvMatrix._updateID;
+ var data = this.uvBuffer.data;
+ if (!this.data || this.data.length !== data.length) {
+ this.data = new Float32Array(data.length);
+ }
+ this.uvMatrix.multiplyUvs(data, this.data);
+ this._updateID++;
+ };
+ return MeshBatchUvs;
+ }());
+
+ var tempPoint = new Point();
+ var tempPolygon = new Polygon();
+ /**
+ * Base mesh class.
+ *
+ * This class empowers you to have maximum flexibility to render any kind of WebGL visuals you can think of.
+ * This class assumes a certain level of WebGL knowledge.
+ * If you know a bit this should abstract enough away to make your life easier!
+ *
+ * Pretty much ALL WebGL can be broken down into the following:
+ * - Geometry - The structure and data for the mesh. This can include anything from positions, uvs, normals, colors etc..
+ * - Shader - This is the shader that PixiJS will render the geometry with (attributes in the shader must match the geometry)
+ * - State - This is the state of WebGL required to render the mesh.
+ *
+ * Through a combination of the above elements you can render anything you want, 2D or 3D!
+ * @memberof PIXI
+ */
+ var Mesh = /** @class */ (function (_super) {
+ __extends$9(Mesh, _super);
+ /**
+ * @param geometry - The geometry the mesh will use.
+ * @param {PIXI.MeshMaterial} shader - The shader the mesh will use.
+ * @param state - The state that the WebGL context is required to be in to render the mesh
+ * if no state is provided, uses {@link PIXI.State.for2d} to create a 2D state for PixiJS.
+ * @param drawMode - The drawMode, can be any of the {@link PIXI.DRAW_MODES} constants.
+ */
+ function Mesh(geometry, shader, state, drawMode) {
+ if (drawMode === void 0) { drawMode = exports.DRAW_MODES.TRIANGLES; }
+ var _this = _super.call(this) || this;
+ _this.geometry = geometry;
+ _this.shader = shader;
+ _this.state = state || State.for2d();
+ _this.drawMode = drawMode;
+ _this.start = 0;
+ _this.size = 0;
+ _this.uvs = null;
+ _this.indices = null;
+ _this.vertexData = new Float32Array(1);
+ _this.vertexDirty = -1;
+ _this._transformID = -1;
+ _this._roundPixels = settings$1.ROUND_PIXELS;
+ _this.batchUvs = null;
+ return _this;
+ }
+ Object.defineProperty(Mesh.prototype, "geometry", {
+ /**
+ * Includes vertex positions, face indices, normals, colors, UVs, and
+ * custom attributes within buffers, reducing the cost of passing all
+ * this data to the GPU. Can be shared between multiple Mesh objects.
+ */
+ get: function () {
+ return this._geometry;
+ },
+ set: function (value) {
+ if (this._geometry === value) {
+ return;
+ }
+ if (this._geometry) {
+ this._geometry.refCount--;
+ if (this._geometry.refCount === 0) {
+ this._geometry.dispose();
+ }
+ }
+ this._geometry = value;
+ if (this._geometry) {
+ this._geometry.refCount++;
+ }
+ this.vertexDirty = -1;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Mesh.prototype, "uvBuffer", {
+ /**
+ * To change mesh uv's, change its uvBuffer data and increment its _updateID.
+ * @readonly
+ */
+ get: function () {
+ return this.geometry.buffers[1];
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Mesh.prototype, "verticesBuffer", {
+ /**
+ * To change mesh vertices, change its uvBuffer data and increment its _updateID.
+ * Incrementing _updateID is optional because most of Mesh objects do it anyway.
+ * @readonly
+ */
+ get: function () {
+ return this.geometry.buffers[0];
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Mesh.prototype, "material", {
+ get: function () {
+ return this.shader;
+ },
+ /** Alias for {@link PIXI.Mesh#shader}. */
+ set: function (value) {
+ this.shader = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Mesh.prototype, "blendMode", {
+ get: function () {
+ return this.state.blendMode;
+ },
+ /**
+ * The blend mode to be applied to the Mesh. Apply a value of
+ * `PIXI.BLEND_MODES.NORMAL` to reset the blend mode.
+ * @default PIXI.BLEND_MODES.NORMAL;
+ */
+ set: function (value) {
+ this.state.blendMode = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Mesh.prototype, "roundPixels", {
+ get: function () {
+ return this._roundPixels;
+ },
+ /**
+ * If true PixiJS will Math.floor() x/y values when rendering, stopping pixel interpolation.
+ * Advantages can include sharper image quality (like text) and faster rendering on canvas.
+ * The main disadvantage is movement of objects may appear less smooth.
+ * To set the global default, change {@link PIXI.settings.ROUND_PIXELS}
+ * @default false
+ */
+ set: function (value) {
+ if (this._roundPixels !== value) {
+ this._transformID = -1;
+ }
+ this._roundPixels = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Mesh.prototype, "tint", {
+ /**
+ * The multiply tint applied to the Mesh. This is a hex value. A value of
+ * `0xFFFFFF` will remove any tint effect.
+ *
+ * Null for non-MeshMaterial shaders
+ * @default 0xFFFFFF
+ */
+ get: function () {
+ return 'tint' in this.shader ? this.shader.tint : null;
+ },
+ set: function (value) {
+ this.shader.tint = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Mesh.prototype, "texture", {
+ /** The texture that the Mesh uses. Null for non-MeshMaterial shaders */
+ get: function () {
+ return 'texture' in this.shader ? this.shader.texture : null;
+ },
+ set: function (value) {
+ this.shader.texture = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Standard renderer draw.
+ * @param renderer - Instance to renderer.
+ */
+ Mesh.prototype._render = function (renderer) {
+ // set properties for batching..
+ // TODO could use a different way to grab verts?
+ var vertices = this.geometry.buffers[0].data;
+ var shader = this.shader;
+ // TODO benchmark check for attribute size..
+ if (shader.batchable
+ && this.drawMode === exports.DRAW_MODES.TRIANGLES
+ && vertices.length < Mesh.BATCHABLE_SIZE * 2) {
+ this._renderToBatch(renderer);
+ }
+ else {
+ this._renderDefault(renderer);
+ }
+ };
+ /**
+ * Standard non-batching way of rendering.
+ * @param renderer - Instance to renderer.
+ */
+ Mesh.prototype._renderDefault = function (renderer) {
+ var shader = this.shader;
+ shader.alpha = this.worldAlpha;
+ if (shader.update) {
+ shader.update();
+ }
+ renderer.batch.flush();
+ // bind and sync uniforms..
+ shader.uniforms.translationMatrix = this.transform.worldTransform.toArray(true);
+ renderer.shader.bind(shader);
+ // set state..
+ renderer.state.set(this.state);
+ // bind the geometry...
+ renderer.geometry.bind(this.geometry, shader);
+ // then render it
+ renderer.geometry.draw(this.drawMode, this.size, this.start, this.geometry.instanceCount);
+ };
+ /**
+ * Rendering by using the Batch system.
+ * @param renderer - Instance to renderer.
+ */
+ Mesh.prototype._renderToBatch = function (renderer) {
+ var geometry = this.geometry;
+ var shader = this.shader;
+ if (shader.uvMatrix) {
+ shader.uvMatrix.update();
+ this.calculateUvs();
+ }
+ // set properties for batching..
+ this.calculateVertices();
+ this.indices = geometry.indexBuffer.data;
+ this._tintRGB = shader._tintRGB;
+ this._texture = shader.texture;
+ var pluginName = this.material.pluginName;
+ renderer.batch.setObjectRenderer(renderer.plugins[pluginName]);
+ renderer.plugins[pluginName].render(this);
+ };
+ /** Updates vertexData field based on transform and vertices. */
+ Mesh.prototype.calculateVertices = function () {
+ var geometry = this.geometry;
+ var verticesBuffer = geometry.buffers[0];
+ var vertices = verticesBuffer.data;
+ var vertexDirtyId = verticesBuffer._updateID;
+ if (vertexDirtyId === this.vertexDirty && this._transformID === this.transform._worldID) {
+ return;
+ }
+ this._transformID = this.transform._worldID;
+ if (this.vertexData.length !== vertices.length) {
+ this.vertexData = new Float32Array(vertices.length);
+ }
+ var wt = this.transform.worldTransform;
+ var a = wt.a;
+ var b = wt.b;
+ var c = wt.c;
+ var d = wt.d;
+ var tx = wt.tx;
+ var ty = wt.ty;
+ var vertexData = this.vertexData;
+ for (var i = 0; i < vertexData.length / 2; i++) {
+ var x = vertices[(i * 2)];
+ var y = vertices[(i * 2) + 1];
+ vertexData[(i * 2)] = (a * x) + (c * y) + tx;
+ vertexData[(i * 2) + 1] = (b * x) + (d * y) + ty;
+ }
+ if (this._roundPixels) {
+ var resolution = settings$1.RESOLUTION;
+ for (var i = 0; i < vertexData.length; ++i) {
+ vertexData[i] = Math.round((vertexData[i] * resolution | 0) / resolution);
+ }
+ }
+ this.vertexDirty = vertexDirtyId;
+ };
+ /** Updates uv field based on from geometry uv's or batchUvs. */
+ Mesh.prototype.calculateUvs = function () {
+ var geomUvs = this.geometry.buffers[1];
+ var shader = this.shader;
+ if (!shader.uvMatrix.isSimple) {
+ if (!this.batchUvs) {
+ this.batchUvs = new MeshBatchUvs(geomUvs, shader.uvMatrix);
+ }
+ this.batchUvs.update();
+ this.uvs = this.batchUvs.data;
+ }
+ else {
+ this.uvs = geomUvs.data;
+ }
+ };
+ /**
+ * Updates the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account.
+ * there must be a aVertexPosition attribute present in the geometry for bounds to be calculated correctly.
+ */
+ Mesh.prototype._calculateBounds = function () {
+ this.calculateVertices();
+ this._bounds.addVertexData(this.vertexData, 0, this.vertexData.length);
+ };
+ /**
+ * Tests if a point is inside this mesh. Works only for PIXI.DRAW_MODES.TRIANGLES.
+ * @param point - The point to test.
+ * @returns - The result of the test.
+ */
+ Mesh.prototype.containsPoint = function (point) {
+ if (!this.getBounds().contains(point.x, point.y)) {
+ return false;
+ }
+ this.worldTransform.applyInverse(point, tempPoint);
+ var vertices = this.geometry.getBuffer('aVertexPosition').data;
+ var points = tempPolygon.points;
+ var indices = this.geometry.getIndex().data;
+ var len = indices.length;
+ var step = this.drawMode === 4 ? 3 : 1;
+ for (var i = 0; i + 2 < len; i += step) {
+ var ind0 = indices[i] * 2;
+ var ind1 = indices[i + 1] * 2;
+ var ind2 = indices[i + 2] * 2;
+ points[0] = vertices[ind0];
+ points[1] = vertices[ind0 + 1];
+ points[2] = vertices[ind1];
+ points[3] = vertices[ind1 + 1];
+ points[4] = vertices[ind2];
+ points[5] = vertices[ind2 + 1];
+ if (tempPolygon.contains(tempPoint.x, tempPoint.y)) {
+ return true;
+ }
+ }
+ return false;
+ };
+ Mesh.prototype.destroy = function (options) {
+ _super.prototype.destroy.call(this, options);
+ if (this._cachedTexture) {
+ this._cachedTexture.destroy();
+ this._cachedTexture = null;
+ }
+ this.geometry = null;
+ this.shader = null;
+ this.state = null;
+ this.uvs = null;
+ this.indices = null;
+ this.vertexData = null;
+ };
+ /** The maximum number of vertices to consider batchable. Generally, the complexity of the geometry. */
+ Mesh.BATCHABLE_SIZE = 100;
+ return Mesh;
+ }(Container));
+
+ var fragment$5 = "varying vec2 vTextureCoord;\nuniform vec4 uColor;\n\nuniform sampler2D uSampler;\n\nvoid main(void)\n{\n gl_FragColor = texture2D(uSampler, vTextureCoord) * uColor;\n}\n";
+
+ var vertex$2 = "attribute vec2 aVertexPosition;\nattribute vec2 aTextureCoord;\n\nuniform mat3 projectionMatrix;\nuniform mat3 translationMatrix;\nuniform mat3 uTextureMatrix;\n\nvarying vec2 vTextureCoord;\n\nvoid main(void)\n{\n gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);\n\n vTextureCoord = (uTextureMatrix * vec3(aTextureCoord, 1.0)).xy;\n}\n";
+
+ /**
+ * Slightly opinionated default shader for PixiJS 2D objects.
+ * @memberof PIXI
+ */
+ var MeshMaterial = /** @class */ (function (_super) {
+ __extends$9(MeshMaterial, _super);
+ /**
+ * @param uSampler - Texture that material uses to render.
+ * @param options - Additional options
+ * @param {number} [options.alpha=1] - Default alpha.
+ * @param {number} [options.tint=0xFFFFFF] - Default tint.
+ * @param {string} [options.pluginName='batch'] - Renderer plugin for batching.
+ * @param {PIXI.Program} [options.program=0xFFFFFF] - Custom program.
+ * @param {object} [options.uniforms] - Custom uniforms.
+ */
+ function MeshMaterial(uSampler, options) {
+ var _this = this;
+ var uniforms = {
+ uSampler: uSampler,
+ alpha: 1,
+ uTextureMatrix: Matrix.IDENTITY,
+ uColor: new Float32Array([1, 1, 1, 1]),
+ };
+ // Set defaults
+ options = Object.assign({
+ tint: 0xFFFFFF,
+ alpha: 1,
+ pluginName: 'batch',
+ }, options);
+ if (options.uniforms) {
+ Object.assign(uniforms, options.uniforms);
+ }
+ _this = _super.call(this, options.program || Program.from(vertex$2, fragment$5), uniforms) || this;
+ _this._colorDirty = false;
+ _this.uvMatrix = new TextureMatrix(uSampler);
+ _this.batchable = options.program === undefined;
+ _this.pluginName = options.pluginName;
+ _this.tint = options.tint;
+ _this.alpha = options.alpha;
+ return _this;
+ }
+ Object.defineProperty(MeshMaterial.prototype, "texture", {
+ /** Reference to the texture being rendered. */
+ get: function () {
+ return this.uniforms.uSampler;
+ },
+ set: function (value) {
+ if (this.uniforms.uSampler !== value) {
+ if (!this.uniforms.uSampler.baseTexture.alphaMode !== !value.baseTexture.alphaMode) {
+ this._colorDirty = true;
+ }
+ this.uniforms.uSampler = value;
+ this.uvMatrix.texture = value;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(MeshMaterial.prototype, "alpha", {
+ get: function () {
+ return this._alpha;
+ },
+ /**
+ * This gets automatically set by the object using this.
+ * @default 1
+ */
+ set: function (value) {
+ if (value === this._alpha)
+ { return; }
+ this._alpha = value;
+ this._colorDirty = true;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(MeshMaterial.prototype, "tint", {
+ get: function () {
+ return this._tint;
+ },
+ /**
+ * Multiply tint for the material.
+ * @default 0xFFFFFF
+ */
+ set: function (value) {
+ if (value === this._tint)
+ { return; }
+ this._tint = value;
+ this._tintRGB = (value >> 16) + (value & 0xff00) + ((value & 0xff) << 16);
+ this._colorDirty = true;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /** Gets called automatically by the Mesh. Intended to be overridden for custom {@link MeshMaterial} objects. */
+ MeshMaterial.prototype.update = function () {
+ if (this._colorDirty) {
+ this._colorDirty = false;
+ var baseTexture = this.texture.baseTexture;
+ premultiplyTintToRgba(this._tint, this._alpha, this.uniforms.uColor, baseTexture.alphaMode);
+ }
+ if (this.uvMatrix.update()) {
+ this.uniforms.uTextureMatrix = this.uvMatrix.mapCoord;
+ }
+ };
+ return MeshMaterial;
+ }(Shader));
+
+ /**
+ * Standard 2D geometry used in PixiJS.
+ *
+ * Geometry can be defined without passing in a style or data if required.
+ *
+ * ```js
+ * const geometry = new PIXI.Geometry();
+ *
+ * geometry.addAttribute('positions', [0, 0, 100, 0, 100, 100, 0, 100], 2);
+ * geometry.addAttribute('uvs', [0,0,1,0,1,1,0,1], 2);
+ * geometry.addIndex([0,1,2,1,3,2]);
+ *
+ * ```
+ * @memberof PIXI
+ */
+ var MeshGeometry = /** @class */ (function (_super) {
+ __extends$9(MeshGeometry, _super);
+ /**
+ * @param {Float32Array|number[]} [vertices] - Positional data on geometry.
+ * @param {Float32Array|number[]} [uvs] - Texture UVs.
+ * @param {Uint16Array|number[]} [index] - IndexBuffer
+ */
+ function MeshGeometry(vertices, uvs, index) {
+ var _this = _super.call(this) || this;
+ var verticesBuffer = new Buffer(vertices);
+ var uvsBuffer = new Buffer(uvs, true);
+ var indexBuffer = new Buffer(index, true, true);
+ _this.addAttribute('aVertexPosition', verticesBuffer, 2, false, exports.TYPES.FLOAT)
+ .addAttribute('aTextureCoord', uvsBuffer, 2, false, exports.TYPES.FLOAT)
+ .addIndex(indexBuffer);
+ _this._updateId = -1;
+ return _this;
+ }
+ Object.defineProperty(MeshGeometry.prototype, "vertexDirtyId", {
+ /**
+ * If the vertex position is updated.
+ * @readonly
+ * @private
+ */
+ get: function () {
+ return this.buffers[0]._updateID;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return MeshGeometry;
+ }(Geometry));
+
+ /*!
+ * @pixi/text-bitmap - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/text-bitmap is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+ /* global Reflect, Promise */
+
+ var extendStatics$8 = function(d, b) {
+ extendStatics$8 = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
+ return extendStatics$8(d, b);
+ };
+
+ function __extends$8(d, b) {
+ extendStatics$8(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ }
+
+ /* eslint-disable max-len */
+ /**
+ * Normalized parsed data from .fnt files.
+ * @memberof PIXI
+ */
+ var BitmapFontData = /** @class */ (function () {
+ function BitmapFontData() {
+ this.info = [];
+ this.common = [];
+ this.page = [];
+ this.char = [];
+ this.kerning = [];
+ this.distanceField = [];
+ }
+ return BitmapFontData;
+ }());
+
+ /**
+ * BitmapFont format that's Text-based.
+ * @private
+ */
+ var TextFormat = /** @class */ (function () {
+ function TextFormat() {
+ }
+ /**
+ * Check if resource refers to txt font data.
+ * @param data
+ * @returns - True if resource could be treated as font data, false otherwise.
+ */
+ TextFormat.test = function (data) {
+ return typeof data === 'string' && data.indexOf('info face=') === 0;
+ };
+ /**
+ * Convert text font data to a javascript object.
+ * @param txt - Raw string data to be converted
+ * @returns - Parsed font data
+ */
+ TextFormat.parse = function (txt) {
+ // Retrieve data item
+ var items = txt.match(/^[a-z]+\s+.+$/gm);
+ var rawData = {
+ info: [],
+ common: [],
+ page: [],
+ char: [],
+ chars: [],
+ kerning: [],
+ kernings: [],
+ distanceField: [],
+ };
+ for (var i in items) {
+ // Extract item name
+ var name = items[i].match(/^[a-z]+/gm)[0];
+ // Extract item attribute list as string ex.: "width=10"
+ var attributeList = items[i].match(/[a-zA-Z]+=([^\s"']+|"([^"]*)")/gm);
+ // Convert attribute list into an object
+ var itemData = {};
+ for (var i_1 in attributeList) {
+ // Split key-value pairs
+ var split = attributeList[i_1].split('=');
+ var key = split[0];
+ // Remove eventual quotes from value
+ var strValue = split[1].replace(/"/gm, '');
+ // Try to convert value into float
+ var floatValue = parseFloat(strValue);
+ // Use string value case float value is NaN
+ var value = isNaN(floatValue) ? strValue : floatValue;
+ itemData[key] = value;
+ }
+ // Push current item to the resulting data
+ rawData[name].push(itemData);
+ }
+ var font = new BitmapFontData();
+ rawData.info.forEach(function (info) { return font.info.push({
+ face: info.face,
+ size: parseInt(info.size, 10),
+ }); });
+ rawData.common.forEach(function (common) { return font.common.push({
+ lineHeight: parseInt(common.lineHeight, 10),
+ }); });
+ rawData.page.forEach(function (page) { return font.page.push({
+ id: parseInt(page.id, 10),
+ file: page.file,
+ }); });
+ rawData.char.forEach(function (char) { return font.char.push({
+ id: parseInt(char.id, 10),
+ page: parseInt(char.page, 10),
+ x: parseInt(char.x, 10),
+ y: parseInt(char.y, 10),
+ width: parseInt(char.width, 10),
+ height: parseInt(char.height, 10),
+ xoffset: parseInt(char.xoffset, 10),
+ yoffset: parseInt(char.yoffset, 10),
+ xadvance: parseInt(char.xadvance, 10),
+ }); });
+ rawData.kerning.forEach(function (kerning) { return font.kerning.push({
+ first: parseInt(kerning.first, 10),
+ second: parseInt(kerning.second, 10),
+ amount: parseInt(kerning.amount, 10),
+ }); });
+ rawData.distanceField.forEach(function (df) { return font.distanceField.push({
+ distanceRange: parseInt(df.distanceRange, 10),
+ fieldType: df.fieldType,
+ }); });
+ return font;
+ };
+ return TextFormat;
+ }());
+
+ /**
+ * BitmapFont format that's XML-based.
+ * @private
+ */
+ var XMLFormat = /** @class */ (function () {
+ function XMLFormat() {
+ }
+ /**
+ * Check if resource refers to xml font data.
+ * @param data
+ * @returns - True if resource could be treated as font data, false otherwise.
+ */
+ XMLFormat.test = function (data) {
+ return data instanceof XMLDocument
+ && data.getElementsByTagName('page').length
+ && data.getElementsByTagName('info')[0].getAttribute('face') !== null;
+ };
+ /**
+ * Convert the XML into BitmapFontData that we can use.
+ * @param xml
+ * @returns - Data to use for BitmapFont
+ */
+ XMLFormat.parse = function (xml) {
+ var data = new BitmapFontData();
+ var info = xml.getElementsByTagName('info');
+ var common = xml.getElementsByTagName('common');
+ var page = xml.getElementsByTagName('page');
+ var char = xml.getElementsByTagName('char');
+ var kerning = xml.getElementsByTagName('kerning');
+ var distanceField = xml.getElementsByTagName('distanceField');
+ for (var i = 0; i < info.length; i++) {
+ data.info.push({
+ face: info[i].getAttribute('face'),
+ size: parseInt(info[i].getAttribute('size'), 10),
+ });
+ }
+ for (var i = 0; i < common.length; i++) {
+ data.common.push({
+ lineHeight: parseInt(common[i].getAttribute('lineHeight'), 10),
+ });
+ }
+ for (var i = 0; i < page.length; i++) {
+ data.page.push({
+ id: parseInt(page[i].getAttribute('id'), 10) || 0,
+ file: page[i].getAttribute('file'),
+ });
+ }
+ for (var i = 0; i < char.length; i++) {
+ var letter = char[i];
+ data.char.push({
+ id: parseInt(letter.getAttribute('id'), 10),
+ page: parseInt(letter.getAttribute('page'), 10) || 0,
+ x: parseInt(letter.getAttribute('x'), 10),
+ y: parseInt(letter.getAttribute('y'), 10),
+ width: parseInt(letter.getAttribute('width'), 10),
+ height: parseInt(letter.getAttribute('height'), 10),
+ xoffset: parseInt(letter.getAttribute('xoffset'), 10),
+ yoffset: parseInt(letter.getAttribute('yoffset'), 10),
+ xadvance: parseInt(letter.getAttribute('xadvance'), 10),
+ });
+ }
+ for (var i = 0; i < kerning.length; i++) {
+ data.kerning.push({
+ first: parseInt(kerning[i].getAttribute('first'), 10),
+ second: parseInt(kerning[i].getAttribute('second'), 10),
+ amount: parseInt(kerning[i].getAttribute('amount'), 10),
+ });
+ }
+ for (var i = 0; i < distanceField.length; i++) {
+ data.distanceField.push({
+ fieldType: distanceField[i].getAttribute('fieldType'),
+ distanceRange: parseInt(distanceField[i].getAttribute('distanceRange'), 10),
+ });
+ }
+ return data;
+ };
+ return XMLFormat;
+ }());
+
+ /**
+ * BitmapFont format that's XML-based.
+ * @private
+ */
+ var XMLStringFormat = /** @class */ (function () {
+ function XMLStringFormat() {
+ }
+ /**
+ * Check if resource refers to text xml font data.
+ * @param data
+ * @returns - True if resource could be treated as font data, false otherwise.
+ */
+ XMLStringFormat.test = function (data) {
+ if (typeof data === 'string' && data.indexOf('<font>') > -1) {
+ var xml = new globalThis.DOMParser().parseFromString(data, 'text/xml');
+ return XMLFormat.test(xml);
+ }
+ return false;
+ };
+ /**
+ * Convert the text XML into BitmapFontData that we can use.
+ * @param xmlTxt
+ * @returns - Data to use for BitmapFont
+ */
+ XMLStringFormat.parse = function (xmlTxt) {
+ var xml = new globalThis.DOMParser().parseFromString(xmlTxt, 'text/xml');
+ return XMLFormat.parse(xml);
+ };
+ return XMLStringFormat;
+ }());
+
+ // Registered formats, maybe make this extensible in the future?
+ var formats = [
+ TextFormat,
+ XMLFormat,
+ XMLStringFormat ];
+ /**
+ * Auto-detect BitmapFont parsing format based on data.
+ * @private
+ * @param {any} data - Data to detect format
+ * @returns {any} Format or null
+ */
+ function autoDetectFormat(data) {
+ for (var i = 0; i < formats.length; i++) {
+ if (formats[i].test(data)) {
+ return formats[i];
+ }
+ }
+ return null;
+ }
+
+ // TODO: Prevent code duplication b/w generateFillStyle & Text#generateFillStyle
+ /**
+ * Generates the fill style. Can automatically generate a gradient based on the fill style being an array
+ * @private
+ * @param canvas
+ * @param context
+ * @param {object} style - The style.
+ * @param resolution
+ * @param {string[]} lines - The lines of text.
+ * @param metrics
+ * @returns {string|number|CanvasGradient} The fill style
+ */
+ function generateFillStyle(canvas, context, style, resolution, lines, metrics) {
+ // TODO: Can't have different types for getter and setter. The getter shouldn't have the number type as
+ // the setter converts to string. See this thread for more details:
+ // https://github.com/microsoft/TypeScript/issues/2521
+ var fillStyle = style.fill;
+ if (!Array.isArray(fillStyle)) {
+ return fillStyle;
+ }
+ else if (fillStyle.length === 1) {
+ return fillStyle[0];
+ }
+ // the gradient will be evenly spaced out according to how large the array is.
+ // ['#FF0000', '#00FF00', '#0000FF'] would created stops at 0.25, 0.5 and 0.75
+ var gradient;
+ // a dropshadow will enlarge the canvas and result in the gradient being
+ // generated with the incorrect dimensions
+ var dropShadowCorrection = (style.dropShadow) ? style.dropShadowDistance : 0;
+ // should also take padding into account, padding can offset the gradient
+ var padding = style.padding || 0;
+ var width = (canvas.width / resolution) - dropShadowCorrection - (padding * 2);
+ var height = (canvas.height / resolution) - dropShadowCorrection - (padding * 2);
+ // make a copy of the style settings, so we can manipulate them later
+ var fill = fillStyle.slice();
+ var fillGradientStops = style.fillGradientStops.slice();
+ // wanting to evenly distribute the fills. So an array of 4 colours should give fills of 0.25, 0.5 and 0.75
+ if (!fillGradientStops.length) {
+ var lengthPlus1 = fill.length + 1;
+ for (var i = 1; i < lengthPlus1; ++i) {
+ fillGradientStops.push(i / lengthPlus1);
+ }
+ }
+ // stop the bleeding of the last gradient on the line above to the top gradient of the this line
+ // by hard defining the first gradient colour at point 0, and last gradient colour at point 1
+ fill.unshift(fillStyle[0]);
+ fillGradientStops.unshift(0);
+ fill.push(fillStyle[fillStyle.length - 1]);
+ fillGradientStops.push(1);
+ if (style.fillGradientType === exports.TEXT_GRADIENT.LINEAR_VERTICAL) {
+ // start the gradient at the top center of the canvas, and end at the bottom middle of the canvas
+ gradient = context.createLinearGradient(width / 2, padding, width / 2, height + padding);
+ // we need to repeat the gradient so that each individual line of text has the same vertical gradient effect
+ // ['#FF0000', '#00FF00', '#0000FF'] over 2 lines would create stops at 0.125, 0.25, 0.375, 0.625, 0.75, 0.875
+ // There's potential for floating point precision issues at the seams between gradient repeats.
+ // The loop below generates the stops in order, so track the last generated one to prevent
+ // floating point precision from making us go the teeniest bit backwards, resulting in
+ // the first and last colors getting swapped.
+ var lastIterationStop = 0;
+ // Actual height of the text itself, not counting spacing for lineHeight/leading/dropShadow etc
+ var textHeight = metrics.fontProperties.fontSize + style.strokeThickness;
+ // textHeight, but as a 0-1 size in global gradient stop space
+ var gradStopLineHeight = textHeight / height;
+ for (var i = 0; i < lines.length; i++) {
+ var thisLineTop = metrics.lineHeight * i;
+ for (var j = 0; j < fill.length; j++) {
+ // 0-1 stop point for the current line, multiplied to global space afterwards
+ var lineStop = 0;
+ if (typeof fillGradientStops[j] === 'number') {
+ lineStop = fillGradientStops[j];
+ }
+ else {
+ lineStop = j / fill.length;
+ }
+ var globalStop = (thisLineTop / height) + (lineStop * gradStopLineHeight);
+ // Prevent color stop generation going backwards from floating point imprecision
+ var clampedStop = Math.max(lastIterationStop, globalStop);
+ clampedStop = Math.min(clampedStop, 1); // Cap at 1 as well for safety's sake to avoid a possible throw.
+ gradient.addColorStop(clampedStop, fill[j]);
+ lastIterationStop = clampedStop;
+ }
+ }
+ }
+ else {
+ // start the gradient at the center left of the canvas, and end at the center right of the canvas
+ gradient = context.createLinearGradient(padding, height / 2, width + padding, height / 2);
+ // can just evenly space out the gradients in this case, as multiple lines makes no difference
+ // to an even left to right gradient
+ var totalIterations = fill.length + 1;
+ var currentIteration = 1;
+ for (var i = 0; i < fill.length; i++) {
+ var stop = void 0;
+ if (typeof fillGradientStops[i] === 'number') {
+ stop = fillGradientStops[i];
+ }
+ else {
+ stop = currentIteration / totalIterations;
+ }
+ gradient.addColorStop(stop, fill[i]);
+ currentIteration++;
+ }
+ }
+ return gradient;
+ }
+
+ // TODO: Prevent code duplication b/w drawGlyph & Text#updateText
+ /**
+ * Draws the glyph `metrics.text` on the given canvas.
+ *
+ * Ignored because not directly exposed.
+ * @ignore
+ * @param {HTMLCanvasElement} canvas
+ * @param {CanvasRenderingContext2D} context
+ * @param {TextMetrics} metrics
+ * @param {number} x
+ * @param {number} y
+ * @param {number} resolution
+ * @param {TextStyle} style
+ */
+ function drawGlyph(canvas, context, metrics, x, y, resolution, style) {
+ var char = metrics.text;
+ var fontProperties = metrics.fontProperties;
+ context.translate(x, y);
+ context.scale(resolution, resolution);
+ var tx = style.strokeThickness / 2;
+ var ty = -(style.strokeThickness / 2);
+ context.font = style.toFontString();
+ context.lineWidth = style.strokeThickness;
+ context.textBaseline = style.textBaseline;
+ context.lineJoin = style.lineJoin;
+ context.miterLimit = style.miterLimit;
+ // set canvas text styles
+ context.fillStyle = generateFillStyle(canvas, context, style, resolution, [char], metrics);
+ context.strokeStyle = style.stroke;
+ if (style.dropShadow) {
+ var dropShadowColor = style.dropShadowColor;
+ var rgb = hex2rgb(typeof dropShadowColor === 'number' ? dropShadowColor : string2hex(dropShadowColor));
+ var dropShadowBlur = style.dropShadowBlur * resolution;
+ var dropShadowDistance = style.dropShadowDistance * resolution;
+ context.shadowColor = "rgba(" + rgb[0] * 255 + "," + rgb[1] * 255 + "," + rgb[2] * 255 + "," + style.dropShadowAlpha + ")";
+ context.shadowBlur = dropShadowBlur;
+ context.shadowOffsetX = Math.cos(style.dropShadowAngle) * dropShadowDistance;
+ context.shadowOffsetY = Math.sin(style.dropShadowAngle) * dropShadowDistance;
+ }
+ else {
+ context.shadowColor = 'black';
+ context.shadowBlur = 0;
+ context.shadowOffsetX = 0;
+ context.shadowOffsetY = 0;
+ }
+ if (style.stroke && style.strokeThickness) {
+ context.strokeText(char, tx, ty + metrics.lineHeight - fontProperties.descent);
+ }
+ if (style.fill) {
+ context.fillText(char, tx, ty + metrics.lineHeight - fontProperties.descent);
+ }
+ context.setTransform(1, 0, 0, 1, 0, 0); // defaults needed for older browsers (e.g. Opera 29)
+ context.fillStyle = 'rgba(0, 0, 0, 0)';
+ }
+
+ /**
+ * Ponyfill for IE because it doesn't support `Array.from`
+ * @param text
+ * @private
+ */
+ function splitTextToCharacters(text) {
+ return Array.from ? Array.from(text) : text.split('');
+ }
+
+ /**
+ * Processes the passed character set data and returns a flattened array of all the characters.
+ *
+ * Ignored because not directly exposed.
+ * @ignore
+ * @param {string | string[] | string[][] } chars
+ * @returns {string[]} the flattened array of characters
+ */
+ function resolveCharacters(chars) {
+ // Split the chars string into individual characters
+ if (typeof chars === 'string') {
+ chars = [chars];
+ }
+ // Handle an array of characters+ranges
+ var result = [];
+ for (var i = 0, j = chars.length; i < j; i++) {
+ var item = chars[i];
+ // Handle range delimited by start/end chars
+ if (Array.isArray(item)) {
+ if (item.length !== 2) {
+ throw new Error("[BitmapFont]: Invalid character range length, expecting 2 got " + item.length + ".");
+ }
+ var startCode = item[0].charCodeAt(0);
+ var endCode = item[1].charCodeAt(0);
+ if (endCode < startCode) {
+ throw new Error('[BitmapFont]: Invalid character range.');
+ }
+ for (var i_1 = startCode, j_1 = endCode; i_1 <= j_1; i_1++) {
+ result.push(String.fromCharCode(i_1));
+ }
+ }
+ // Handle a character set string
+ else {
+ result.push.apply(result, splitTextToCharacters(item));
+ }
+ }
+ if (result.length === 0) {
+ throw new Error('[BitmapFont]: Empty set when resolving characters.');
+ }
+ return result;
+ }
+
+ /**
+ * Ponyfill for IE because it doesn't support `codePointAt`
+ * @param str
+ * @private
+ */
+ function extractCharCode(str) {
+ return str.codePointAt ? str.codePointAt(0) : str.charCodeAt(0);
+ }
+
+ /**
+ * BitmapFont represents a typeface available for use with the BitmapText class. Use the `install`
+ * method for adding a font to be used.
+ * @memberof PIXI
+ */
+ var BitmapFont = /** @class */ (function () {
+ /**
+ * @param data
+ * @param textures
+ * @param ownsTextures - Setting to `true` will destroy page textures
+ * when the font is uninstalled.
+ */
+ function BitmapFont(data, textures, ownsTextures) {
+ var _a, _b;
+ var info = data.info[0];
+ var common = data.common[0];
+ var page = data.page[0];
+ var distanceField = data.distanceField[0];
+ var res = getResolutionOfUrl(page.file);
+ var pageTextures = {};
+ this._ownsTextures = ownsTextures;
+ this.font = info.face;
+ this.size = info.size;
+ this.lineHeight = common.lineHeight / res;
+ this.chars = {};
+ this.pageTextures = pageTextures;
+ // Convert the input Texture, Textures or object
+ // into a page Texture lookup by "id"
+ for (var i = 0; i < data.page.length; i++) {
+ var _c = data.page[i], id = _c.id, file = _c.file;
+ pageTextures[id] = textures instanceof Array
+ ? textures[i] : textures[file];
+ // only MSDF and SDF fonts need no-premultiplied-alpha
+ if ((distanceField === null || distanceField === void 0 ? void 0 : distanceField.fieldType) && distanceField.fieldType !== 'none') {
+ pageTextures[id].baseTexture.alphaMode = exports.ALPHA_MODES.NO_PREMULTIPLIED_ALPHA;
+ }
+ }
+ // parse letters
+ for (var i = 0; i < data.char.length; i++) {
+ var _d = data.char[i], id = _d.id, page_1 = _d.page;
+ var _e = data.char[i], x = _e.x, y = _e.y, width = _e.width, height = _e.height, xoffset = _e.xoffset, yoffset = _e.yoffset, xadvance = _e.xadvance;
+ x /= res;
+ y /= res;
+ width /= res;
+ height /= res;
+ xoffset /= res;
+ yoffset /= res;
+ xadvance /= res;
+ var rect = new Rectangle(x + (pageTextures[page_1].frame.x / res), y + (pageTextures[page_1].frame.y / res), width, height);
+ this.chars[id] = {
+ xOffset: xoffset,
+ yOffset: yoffset,
+ xAdvance: xadvance,
+ kerning: {},
+ texture: new Texture(pageTextures[page_1].baseTexture, rect),
+ page: page_1,
+ };
+ }
+ // parse kernings
+ for (var i = 0; i < data.kerning.length; i++) {
+ var _f = data.kerning[i], first = _f.first, second = _f.second, amount = _f.amount;
+ first /= res;
+ second /= res;
+ amount /= res;
+ if (this.chars[second]) {
+ this.chars[second].kerning[first] = amount;
+ }
+ }
+ // Store distance field information
+ this.distanceFieldRange = distanceField === null || distanceField === void 0 ? void 0 : distanceField.distanceRange;
+ this.distanceFieldType = (_b = (_a = distanceField === null || distanceField === void 0 ? void 0 : distanceField.fieldType) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : 'none';
+ }
+ /** Remove references to created glyph textures. */
+ BitmapFont.prototype.destroy = function () {
+ for (var id in this.chars) {
+ this.chars[id].texture.destroy();
+ this.chars[id].texture = null;
+ }
+ for (var id in this.pageTextures) {
+ if (this._ownsTextures) {
+ this.pageTextures[id].destroy(true);
+ }
+ this.pageTextures[id] = null;
+ }
+ // Set readonly null.
+ this.chars = null;
+ this.pageTextures = null;
+ };
+ /**
+ * Register a new bitmap font.
+ * @param data - The
+ * characters map that could be provided as xml or raw string.
+ * @param textures - List of textures for each page.
+ * @param ownsTextures - Set to `true` to destroy page textures
+ * when the font is uninstalled. By default fonts created with
+ * `BitmapFont.from` or from the `BitmapFontLoader` are `true`.
+ * @returns {PIXI.BitmapFont} Result font object with font, size, lineHeight
+ * and char fields.
+ */
+ BitmapFont.install = function (data, textures, ownsTextures) {
+ var fontData;
+ if (data instanceof BitmapFontData) {
+ fontData = data;
+ }
+ else {
+ var format = autoDetectFormat(data);
+ if (!format) {
+ throw new Error('Unrecognized data format for font.');
+ }
+ fontData = format.parse(data);
+ }
+ // Single texture, convert to list
+ if (textures instanceof Texture) {
+ textures = [textures];
+ }
+ var font = new BitmapFont(fontData, textures, ownsTextures);
+ BitmapFont.available[font.font] = font;
+ return font;
+ };
+ /**
+ * Remove bitmap font by name.
+ * @param name - Name of the font to uninstall.
+ */
+ BitmapFont.uninstall = function (name) {
+ var font = BitmapFont.available[name];
+ if (!font) {
+ throw new Error("No font found named '" + name + "'");
+ }
+ font.destroy();
+ delete BitmapFont.available[name];
+ };
+ /**
+ * Generates a bitmap-font for the given style and character set. This does not support
+ * kernings yet. With `style` properties, only the following non-layout properties are used:
+ *
+ * - {@link PIXI.TextStyle#dropShadow|dropShadow}
+ * - {@link PIXI.TextStyle#dropShadowDistance|dropShadowDistance}
+ * - {@link PIXI.TextStyle#dropShadowColor|dropShadowColor}
+ * - {@link PIXI.TextStyle#dropShadowBlur|dropShadowBlur}
+ * - {@link PIXI.TextStyle#dropShadowAngle|dropShadowAngle}
+ * - {@link PIXI.TextStyle#fill|fill}
+ * - {@link PIXI.TextStyle#fillGradientStops|fillGradientStops}
+ * - {@link PIXI.TextStyle#fillGradientType|fillGradientType}
+ * - {@link PIXI.TextStyle#fontFamily|fontFamily}
+ * - {@link PIXI.TextStyle#fontSize|fontSize}
+ * - {@link PIXI.TextStyle#fontVariant|fontVariant}
+ * - {@link PIXI.TextStyle#fontWeight|fontWeight}
+ * - {@link PIXI.TextStyle#lineJoin|lineJoin}
+ * - {@link PIXI.TextStyle#miterLimit|miterLimit}
+ * - {@link PIXI.TextStyle#stroke|stroke}
+ * - {@link PIXI.TextStyle#strokeThickness|strokeThickness}
+ * - {@link PIXI.TextStyle#textBaseline|textBaseline}
+ * @param name - The name of the custom font to use with BitmapText.
+ * @param textStyle - Style options to render with BitmapFont.
+ * @param options - Setup options for font or name of the font.
+ * @param {string|string[]|string[][]} [options.chars=PIXI.BitmapFont.ALPHANUMERIC] - characters included
+ * in the font set. You can also use ranges. For example, `[['a', 'z'], ['A', 'Z'], "!@#$%^&*()~{}[] "]`.
+ * Don't forget to include spaces ' ' in your character set!
+ * @param {number} [options.resolution=1] - Render resolution for glyphs.
+ * @param {number} [options.textureWidth=512] - Optional width of atlas, smaller values to reduce memory.
+ * @param {number} [options.textureHeight=512] - Optional height of atlas, smaller values to reduce memory.
+ * @param {number} [options.padding=4] - Padding between glyphs on texture atlas.
+ * @returns Font generated by style options.
+ * @example
+ * PIXI.BitmapFont.from("TitleFont", {
+ * fontFamily: "Arial",
+ * fontSize: 12,
+ * strokeThickness: 2,
+ * fill: "purple"
+ * });
+ *
+ * const title = new PIXI.BitmapText("This is the title", { fontName: "TitleFont" });
+ */
+ BitmapFont.from = function (name, textStyle, options) {
+ if (!name) {
+ throw new Error('[BitmapFont] Property `name` is required.');
+ }
+ var _a = Object.assign({}, BitmapFont.defaultOptions, options), chars = _a.chars, padding = _a.padding, resolution = _a.resolution, textureWidth = _a.textureWidth, textureHeight = _a.textureHeight;
+ var charsList = resolveCharacters(chars);
+ var style = textStyle instanceof TextStyle ? textStyle : new TextStyle(textStyle);
+ var lineWidth = textureWidth;
+ var fontData = new BitmapFontData();
+ fontData.info[0] = {
+ face: style.fontFamily,
+ size: style.fontSize,
+ };
+ fontData.common[0] = {
+ lineHeight: style.fontSize,
+ };
+ var positionX = 0;
+ var positionY = 0;
+ var canvas;
+ var context;
+ var baseTexture;
+ var maxCharHeight = 0;
+ var textures = [];
+ for (var i = 0; i < charsList.length; i++) {
+ if (!canvas) {
+ canvas = settings$1.ADAPTER.createCanvas();
+ canvas.width = textureWidth;
+ canvas.height = textureHeight;
+ context = canvas.getContext('2d');
+ baseTexture = new BaseTexture(canvas, { resolution: resolution });
+ textures.push(new Texture(baseTexture));
+ fontData.page.push({
+ id: textures.length - 1,
+ file: '',
+ });
+ }
+ // Measure glyph dimensions
+ var metrics = TextMetrics.measureText(charsList[i], style, false, canvas);
+ var width = metrics.width;
+ var height = Math.ceil(metrics.height);
+ // This is ugly - but italics are given more space so they don't overlap
+ var textureGlyphWidth = Math.ceil((style.fontStyle === 'italic' ? 2 : 1) * width);
+ // Can't fit char anymore: next canvas please!
+ if (positionY >= textureHeight - (height * resolution)) {
+ if (positionY === 0) {
+ // We don't want user debugging an infinite loop (or do we? :)
+ throw new Error("[BitmapFont] textureHeight " + textureHeight + "px is "
+ + ("too small for " + style.fontSize + "px fonts"));
+ }
+ --i;
+ // Create new atlas once current has filled up
+ canvas = null;
+ context = null;
+ baseTexture = null;
+ positionY = 0;
+ positionX = 0;
+ maxCharHeight = 0;
+ continue;
+ }
+ maxCharHeight = Math.max(height + metrics.fontProperties.descent, maxCharHeight);
+ // Wrap line once full row has been rendered
+ if ((textureGlyphWidth * resolution) + positionX >= lineWidth) {
+ --i;
+ positionY += maxCharHeight * resolution;
+ positionY = Math.ceil(positionY);
+ positionX = 0;
+ maxCharHeight = 0;
+ continue;
+ }
+ drawGlyph(canvas, context, metrics, positionX, positionY, resolution, style);
+ // Unique (numeric) ID mapping to this glyph
+ var id = extractCharCode(metrics.text);
+ // Create a texture holding just the glyph
+ fontData.char.push({
+ id: id,
+ page: textures.length - 1,
+ x: positionX / resolution,
+ y: positionY / resolution,
+ width: textureGlyphWidth,
+ height: height,
+ xoffset: 0,
+ yoffset: 0,
+ xadvance: Math.ceil(width
+ - (style.dropShadow ? style.dropShadowDistance : 0)
+ - (style.stroke ? style.strokeThickness : 0)),
+ });
+ positionX += (textureGlyphWidth + (2 * padding)) * resolution;
+ positionX = Math.ceil(positionX);
+ }
+ // Brute-force kerning info, this can be expensive b/c it's an O(n虏),
+ // but we're using measureText which is native and fast.
+ for (var i = 0, len = charsList.length; i < len; i++) {
+ var first = charsList[i];
+ for (var j = 0; j < len; j++) {
+ var second = charsList[j];
+ var c1 = context.measureText(first).width;
+ var c2 = context.measureText(second).width;
+ var total = context.measureText(first + second).width;
+ var amount = total - (c1 + c2);
+ if (amount) {
+ fontData.kerning.push({
+ first: extractCharCode(first),
+ second: extractCharCode(second),
+ amount: amount,
+ });
+ }
+ }
+ }
+ var font = new BitmapFont(fontData, textures, true);
+ // Make it easier to replace a font
+ if (BitmapFont.available[name] !== undefined) {
+ BitmapFont.uninstall(name);
+ }
+ BitmapFont.available[name] = font;
+ return font;
+ };
+ /**
+ * This character set includes all the letters in the alphabet (both lower- and upper- case).
+ * @type {string[][]}
+ * @example
+ * BitmapFont.from("ExampleFont", style, { chars: BitmapFont.ALPHA })
+ */
+ BitmapFont.ALPHA = [['a', 'z'], ['A', 'Z'], ' '];
+ /**
+ * This character set includes all decimal digits (from 0 to 9).
+ * @type {string[][]}
+ * @example
+ * BitmapFont.from("ExampleFont", style, { chars: BitmapFont.NUMERIC })
+ */
+ BitmapFont.NUMERIC = [['0', '9']];
+ /**
+ * This character set is the union of `BitmapFont.ALPHA` and `BitmapFont.NUMERIC`.
+ * @type {string[][]}
+ */
+ BitmapFont.ALPHANUMERIC = [['a', 'z'], ['A', 'Z'], ['0', '9'], ' '];
+ /**
+ * This character set consists of all the ASCII table.
+ * @member {string[][]}
+ * @see http://www.asciitable.com/
+ */
+ BitmapFont.ASCII = [[' ', '~']];
+ /**
+ * Collection of default options when using `BitmapFont.from`.
+ * @property {number} [resolution=1] -
+ * @property {number} [textureWidth=512] -
+ * @property {number} [textureHeight=512] -
+ * @property {number} [padding=4] -
+ * @property {string|string[]|string[][]} chars = PIXI.BitmapFont.ALPHANUMERIC
+ */
+ BitmapFont.defaultOptions = {
+ resolution: 1,
+ textureWidth: 512,
+ textureHeight: 512,
+ padding: 4,
+ chars: BitmapFont.ALPHANUMERIC,
+ };
+ /** Collection of available/installed fonts. */
+ BitmapFont.available = {};
+ return BitmapFont;
+ }());
+
+ var msdfFrag = "// Pixi texture info\r\nvarying vec2 vTextureCoord;\r\nuniform sampler2D uSampler;\r\n\r\n// Tint\r\nuniform vec4 uColor;\r\n\r\n// on 2D applications fwidth is screenScale / glyphAtlasScale * distanceFieldRange\r\nuniform float uFWidth;\r\n\r\nvoid main(void) {\r\n\r\n // To stack MSDF and SDF we need a non-pre-multiplied-alpha texture.\r\n vec4 texColor = texture2D(uSampler, vTextureCoord);\r\n\r\n // MSDF\r\n float median = texColor.r + texColor.g + texColor.b -\r\n min(texColor.r, min(texColor.g, texColor.b)) -\r\n max(texColor.r, max(texColor.g, texColor.b));\r\n // SDF\r\n median = min(median, texColor.a);\r\n\r\n float screenPxDistance = uFWidth * (median - 0.5);\r\n float alpha = clamp(screenPxDistance + 0.5, 0.0, 1.0);\r\n\r\n // NPM Textures, NPM outputs\r\n gl_FragColor = vec4(uColor.rgb, uColor.a * alpha);\r\n\r\n}\r\n";
+
+ var msdfVert = "// Mesh material default fragment\r\nattribute vec2 aVertexPosition;\r\nattribute vec2 aTextureCoord;\r\n\r\nuniform mat3 projectionMatrix;\r\nuniform mat3 translationMatrix;\r\nuniform mat3 uTextureMatrix;\r\n\r\nvarying vec2 vTextureCoord;\r\n\r\nvoid main(void)\r\n{\r\n gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);\r\n\r\n vTextureCoord = (uTextureMatrix * vec3(aTextureCoord, 1.0)).xy;\r\n}\r\n";
+
+ // If we ever need more than two pools, please make a Dict or something better.
+ var pageMeshDataDefaultPageMeshData = [];
+ var pageMeshDataMSDFPageMeshData = [];
+ var charRenderDataPool = [];
+ /**
+ * A BitmapText object will create a line or multiple lines of text using bitmap font.
+ *
+ * The primary advantage of this class over Text is that all of your textures are pre-generated and loading,
+ * meaning that rendering is fast, and changing text has no performance implications.
+ *
+ * Supporting character sets other than latin, such as CJK languages, may be impractical due to the number of characters.
+ *
+ * To split a line you can use '\n', '\r' or '\r\n' in your string.
+ *
+ * PixiJS can auto-generate fonts on-the-fly using BitmapFont or use fnt files provided by:
+ * http://www.angelcode.com/products/bmfont/ for Windows or
+ * http://www.bmglyph.com/ for Mac.
+ *
+ * You can also use SDF, MSDF and MTSDF BitmapFonts for vector-like scaling appearance provided by:
+ * https://github.com/soimy/msdf-bmfont-xml for SDF and MSDF fnt files or
+ * https://github.com/Chlumsky/msdf-atlas-gen for SDF, MSDF and MTSDF json files
+ *
+ * A BitmapText can only be created when the font is loaded.
+ *
+ * ```js
+ * // in this case the font is in a file called 'desyrel.fnt'
+ * let bitmapText = new PIXI.BitmapText("text using a fancy font!", {
+ * fontName: "Desyrel",
+ * fontSize: 35,
+ * align: "right"
+ * });
+ * ```
+ * @memberof PIXI
+ */
+ var BitmapText = /** @class */ (function (_super) {
+ __extends$8(BitmapText, _super);
+ /**
+ * @param text - A string that you would like the text to display.
+ * @param style - The style parameters.
+ * @param {string} style.fontName - The installed BitmapFont name.
+ * @param {number} [style.fontSize] - The size of the font in pixels, e.g. 24. If undefined,
+ *. this will default to the BitmapFont size.
+ * @param {string} [style.align='left'] - Alignment for multiline text ('left', 'center', 'right' or 'justify'),
+ * does not affect single line text.
+ * @param {number} [style.tint=0xFFFFFF] - The tint color.
+ * @param {number} [style.letterSpacing=0] - The amount of spacing between letters.
+ * @param {number} [style.maxWidth=0] - The max width of the text before line wrapping.
+ */
+ function BitmapText(text, style) {
+ if (style === void 0) { style = {}; }
+ var _this = _super.call(this) || this;
+ /**
+ * Private tracker for the current tint.
+ * @private
+ */
+ _this._tint = 0xFFFFFF;
+ // Apply the defaults
+ var _a = Object.assign({}, BitmapText.styleDefaults, style), align = _a.align, tint = _a.tint, maxWidth = _a.maxWidth, letterSpacing = _a.letterSpacing, fontName = _a.fontName, fontSize = _a.fontSize;
+ if (!BitmapFont.available[fontName]) {
+ throw new Error("Missing BitmapFont \"" + fontName + "\"");
+ }
+ _this._activePagesMeshData = [];
+ _this._textWidth = 0;
+ _this._textHeight = 0;
+ _this._align = align;
+ _this._tint = tint;
+ _this._fontName = fontName;
+ _this._fontSize = fontSize || BitmapFont.available[fontName].size;
+ _this.text = text;
+ _this._maxWidth = maxWidth;
+ _this._maxLineHeight = 0;
+ _this._letterSpacing = letterSpacing;
+ _this._anchor = new ObservablePoint(function () { _this.dirty = true; }, _this, 0, 0);
+ _this._roundPixels = settings$1.ROUND_PIXELS;
+ _this.dirty = true;
+ _this._resolution = settings$1.RESOLUTION;
+ _this._autoResolution = true;
+ _this._textureCache = {};
+ return _this;
+ }
+ /** Renders text and updates it when needed. This should only be called if the BitmapFont is regenerated. */
+ BitmapText.prototype.updateText = function () {
+ var _a;
+ var data = BitmapFont.available[this._fontName];
+ var scale = this._fontSize / data.size;
+ var pos = new Point();
+ var chars = [];
+ var lineWidths = [];
+ var lineSpaces = [];
+ var text = this._text.replace(/(?:\r\n|\r)/g, '\n') || ' ';
+ var charsInput = splitTextToCharacters(text);
+ var maxWidth = this._maxWidth * data.size / this._fontSize;
+ var pageMeshDataPool = data.distanceFieldType === 'none'
+ ? pageMeshDataDefaultPageMeshData : pageMeshDataMSDFPageMeshData;
+ var prevCharCode = null;
+ var lastLineWidth = 0;
+ var maxLineWidth = 0;
+ var line = 0;
+ var lastBreakPos = -1;
+ var lastBreakWidth = 0;
+ var spacesRemoved = 0;
+ var maxLineHeight = 0;
+ var spaceCount = 0;
+ for (var i = 0; i < charsInput.length; i++) {
+ var char = charsInput[i];
+ var charCode = extractCharCode(char);
+ if ((/(?:\s)/).test(char)) {
+ lastBreakPos = i;
+ lastBreakWidth = lastLineWidth;
+ spaceCount++;
+ }
+ if (char === '\r' || char === '\n') {
+ lineWidths.push(lastLineWidth);
+ lineSpaces.push(-1);
+ maxLineWidth = Math.max(maxLineWidth, lastLineWidth);
+ ++line;
+ ++spacesRemoved;
+ pos.x = 0;
+ pos.y += data.lineHeight;
+ prevCharCode = null;
+ spaceCount = 0;
+ continue;
+ }
+ var charData = data.chars[charCode];
+ if (!charData) {
+ continue;
+ }
+ if (prevCharCode && charData.kerning[prevCharCode]) {
+ pos.x += charData.kerning[prevCharCode];
+ }
+ var charRenderData = charRenderDataPool.pop() || {
+ texture: Texture.EMPTY,
+ line: 0,
+ charCode: 0,
+ prevSpaces: 0,
+ position: new Point(),
+ };
+ charRenderData.texture = charData.texture;
+ charRenderData.line = line;
+ charRenderData.charCode = charCode;
+ charRenderData.position.x = pos.x + charData.xOffset + (this._letterSpacing / 2);
+ charRenderData.position.y = pos.y + charData.yOffset;
+ charRenderData.prevSpaces = spaceCount;
+ chars.push(charRenderData);
+ lastLineWidth = charRenderData.position.x
+ + Math.max(charData.xAdvance - charData.xOffset, charData.texture.orig.width);
+ pos.x += charData.xAdvance + this._letterSpacing;
+ maxLineHeight = Math.max(maxLineHeight, (charData.yOffset + charData.texture.height));
+ prevCharCode = charCode;
+ if (lastBreakPos !== -1 && maxWidth > 0 && pos.x > maxWidth) {
+ ++spacesRemoved;
+ removeItems(chars, 1 + lastBreakPos - spacesRemoved, 1 + i - lastBreakPos);
+ i = lastBreakPos;
+ lastBreakPos = -1;
+ lineWidths.push(lastBreakWidth);
+ lineSpaces.push(chars.length > 0 ? chars[chars.length - 1].prevSpaces : 0);
+ maxLineWidth = Math.max(maxLineWidth, lastBreakWidth);
+ line++;
+ pos.x = 0;
+ pos.y += data.lineHeight;
+ prevCharCode = null;
+ spaceCount = 0;
+ }
+ }
+ var lastChar = charsInput[charsInput.length - 1];
+ if (lastChar !== '\r' && lastChar !== '\n') {
+ if ((/(?:\s)/).test(lastChar)) {
+ lastLineWidth = lastBreakWidth;
+ }
+ lineWidths.push(lastLineWidth);
+ maxLineWidth = Math.max(maxLineWidth, lastLineWidth);
+ lineSpaces.push(-1);
+ }
+ var lineAlignOffsets = [];
+ for (var i = 0; i <= line; i++) {
+ var alignOffset = 0;
+ if (this._align === 'right') {
+ alignOffset = maxLineWidth - lineWidths[i];
+ }
+ else if (this._align === 'center') {
+ alignOffset = (maxLineWidth - lineWidths[i]) / 2;
+ }
+ else if (this._align === 'justify') {
+ alignOffset = lineSpaces[i] < 0 ? 0 : (maxLineWidth - lineWidths[i]) / lineSpaces[i];
+ }
+ lineAlignOffsets.push(alignOffset);
+ }
+ var lenChars = chars.length;
+ var pagesMeshData = {};
+ var newPagesMeshData = [];
+ var activePagesMeshData = this._activePagesMeshData;
+ for (var i = 0; i < activePagesMeshData.length; i++) {
+ pageMeshDataPool.push(activePagesMeshData[i]);
+ }
+ for (var i = 0; i < lenChars; i++) {
+ var texture = chars[i].texture;
+ var baseTextureUid = texture.baseTexture.uid;
+ if (!pagesMeshData[baseTextureUid]) {
+ var pageMeshData = pageMeshDataPool.pop();
+ if (!pageMeshData) {
+ var geometry = new MeshGeometry();
+ var material = void 0;
+ var meshBlendMode = void 0;
+ if (data.distanceFieldType === 'none') {
+ material = new MeshMaterial(Texture.EMPTY);
+ meshBlendMode = exports.BLEND_MODES.NORMAL;
+ }
+ else {
+ material = new MeshMaterial(Texture.EMPTY, { program: Program.from(msdfVert, msdfFrag), uniforms: { uFWidth: 0 } });
+ meshBlendMode = exports.BLEND_MODES.NORMAL_NPM;
+ }
+ var mesh = new Mesh(geometry, material);
+ mesh.blendMode = meshBlendMode;
+ pageMeshData = {
+ index: 0,
+ indexCount: 0,
+ vertexCount: 0,
+ uvsCount: 0,
+ total: 0,
+ mesh: mesh,
+ vertices: null,
+ uvs: null,
+ indices: null,
+ };
+ }
+ // reset data..
+ pageMeshData.index = 0;
+ pageMeshData.indexCount = 0;
+ pageMeshData.vertexCount = 0;
+ pageMeshData.uvsCount = 0;
+ pageMeshData.total = 0;
+ // TODO need to get page texture here somehow..
+ var _textureCache = this._textureCache;
+ _textureCache[baseTextureUid] = _textureCache[baseTextureUid] || new Texture(texture.baseTexture);
+ pageMeshData.mesh.texture = _textureCache[baseTextureUid];
+ pageMeshData.mesh.tint = this._tint;
+ newPagesMeshData.push(pageMeshData);
+ pagesMeshData[baseTextureUid] = pageMeshData;
+ }
+ pagesMeshData[baseTextureUid].total++;
+ }
+ // lets find any previously active pageMeshDatas that are no longer required for
+ // the updated text (if any), removed and return them to the pool.
+ for (var i = 0; i < activePagesMeshData.length; i++) {
+ if (newPagesMeshData.indexOf(activePagesMeshData[i]) === -1) {
+ this.removeChild(activePagesMeshData[i].mesh);
+ }
+ }
+ // next lets add any new meshes, that have not yet been added to this BitmapText
+ // we only add if its not already a child of this BitmapObject
+ for (var i = 0; i < newPagesMeshData.length; i++) {
+ if (newPagesMeshData[i].mesh.parent !== this) {
+ this.addChild(newPagesMeshData[i].mesh);
+ }
+ }
+ // active page mesh datas are set to be the new pages added.
+ this._activePagesMeshData = newPagesMeshData;
+ for (var i in pagesMeshData) {
+ var pageMeshData = pagesMeshData[i];
+ var total = pageMeshData.total;
+ // lets only allocate new buffers if we can fit the new text in the current ones..
+ // unless that is, we will be batching. Currently batching dose not respect the size property of mesh
+ if (!(((_a = pageMeshData.indices) === null || _a === void 0 ? void 0 : _a.length) > 6 * total) || pageMeshData.vertices.length < Mesh.BATCHABLE_SIZE * 2) {
+ pageMeshData.vertices = new Float32Array(4 * 2 * total);
+ pageMeshData.uvs = new Float32Array(4 * 2 * total);
+ pageMeshData.indices = new Uint16Array(6 * total);
+ }
+ else {
+ var total_1 = pageMeshData.total;
+ var vertices = pageMeshData.vertices;
+ // Clear the garbage at the end of the vertices buffer. This will prevent the bounds miscalculation.
+ for (var i_1 = total_1 * 4 * 2; i_1 < vertices.length; i_1++) {
+ vertices[i_1] = 0;
+ }
+ }
+ // as a buffer maybe bigger than the current word, we set the size of the meshMaterial
+ // to match the number of letters needed
+ pageMeshData.mesh.size = 6 * total;
+ }
+ for (var i = 0; i < lenChars; i++) {
+ var char = chars[i];
+ var offset = char.position.x + (lineAlignOffsets[char.line] * (this._align === 'justify' ? char.prevSpaces : 1));
+ if (this._roundPixels) {
+ offset = Math.round(offset);
+ }
+ var xPos = offset * scale;
+ var yPos = char.position.y * scale;
+ var texture = char.texture;
+ var pageMesh = pagesMeshData[texture.baseTexture.uid];
+ var textureFrame = texture.frame;
+ var textureUvs = texture._uvs;
+ var index = pageMesh.index++;
+ pageMesh.indices[(index * 6) + 0] = 0 + (index * 4);
+ pageMesh.indices[(index * 6) + 1] = 1 + (index * 4);
+ pageMesh.indices[(index * 6) + 2] = 2 + (index * 4);
+ pageMesh.indices[(index * 6) + 3] = 0 + (index * 4);
+ pageMesh.indices[(index * 6) + 4] = 2 + (index * 4);
+ pageMesh.indices[(index * 6) + 5] = 3 + (index * 4);
+ pageMesh.vertices[(index * 8) + 0] = xPos;
+ pageMesh.vertices[(index * 8) + 1] = yPos;
+ pageMesh.vertices[(index * 8) + 2] = xPos + (textureFrame.width * scale);
+ pageMesh.vertices[(index * 8) + 3] = yPos;
+ pageMesh.vertices[(index * 8) + 4] = xPos + (textureFrame.width * scale);
+ pageMesh.vertices[(index * 8) + 5] = yPos + (textureFrame.height * scale);
+ pageMesh.vertices[(index * 8) + 6] = xPos;
+ pageMesh.vertices[(index * 8) + 7] = yPos + (textureFrame.height * scale);
+ pageMesh.uvs[(index * 8) + 0] = textureUvs.x0;
+ pageMesh.uvs[(index * 8) + 1] = textureUvs.y0;
+ pageMesh.uvs[(index * 8) + 2] = textureUvs.x1;
+ pageMesh.uvs[(index * 8) + 3] = textureUvs.y1;
+ pageMesh.uvs[(index * 8) + 4] = textureUvs.x2;
+ pageMesh.uvs[(index * 8) + 5] = textureUvs.y2;
+ pageMesh.uvs[(index * 8) + 6] = textureUvs.x3;
+ pageMesh.uvs[(index * 8) + 7] = textureUvs.y3;
+ }
+ this._textWidth = maxLineWidth * scale;
+ this._textHeight = (pos.y + data.lineHeight) * scale;
+ for (var i in pagesMeshData) {
+ var pageMeshData = pagesMeshData[i];
+ // apply anchor
+ if (this.anchor.x !== 0 || this.anchor.y !== 0) {
+ var vertexCount = 0;
+ var anchorOffsetX = this._textWidth * this.anchor.x;
+ var anchorOffsetY = this._textHeight * this.anchor.y;
+ for (var i_2 = 0; i_2 < pageMeshData.total; i_2++) {
+ pageMeshData.vertices[vertexCount++] -= anchorOffsetX;
+ pageMeshData.vertices[vertexCount++] -= anchorOffsetY;
+ pageMeshData.vertices[vertexCount++] -= anchorOffsetX;
+ pageMeshData.vertices[vertexCount++] -= anchorOffsetY;
+ pageMeshData.vertices[vertexCount++] -= anchorOffsetX;
+ pageMeshData.vertices[vertexCount++] -= anchorOffsetY;
+ pageMeshData.vertices[vertexCount++] -= anchorOffsetX;
+ pageMeshData.vertices[vertexCount++] -= anchorOffsetY;
+ }
+ }
+ this._maxLineHeight = maxLineHeight * scale;
+ var vertexBuffer = pageMeshData.mesh.geometry.getBuffer('aVertexPosition');
+ var textureBuffer = pageMeshData.mesh.geometry.getBuffer('aTextureCoord');
+ var indexBuffer = pageMeshData.mesh.geometry.getIndex();
+ vertexBuffer.data = pageMeshData.vertices;
+ textureBuffer.data = pageMeshData.uvs;
+ indexBuffer.data = pageMeshData.indices;
+ vertexBuffer.update();
+ textureBuffer.update();
+ indexBuffer.update();
+ }
+ for (var i = 0; i < chars.length; i++) {
+ charRenderDataPool.push(chars[i]);
+ }
+ };
+ BitmapText.prototype.updateTransform = function () {
+ this.validate();
+ this.containerUpdateTransform();
+ };
+ BitmapText.prototype._render = function (renderer) {
+ if (this._autoResolution && this._resolution !== renderer.resolution) {
+ this._resolution = renderer.resolution;
+ this.dirty = true;
+ }
+ // Update the uniform
+ var _a = BitmapFont.available[this._fontName], distanceFieldRange = _a.distanceFieldRange, distanceFieldType = _a.distanceFieldType, size = _a.size;
+ if (distanceFieldType !== 'none') {
+ // Inject the shader code with the correct value
+ var _b = this.worldTransform, a = _b.a, b = _b.b, c = _b.c, d = _b.d;
+ var dx = Math.sqrt((a * a) + (b * b));
+ var dy = Math.sqrt((c * c) + (d * d));
+ var worldScale = (Math.abs(dx) + Math.abs(dy)) / 2;
+ var fontScale = this._fontSize / size;
+ for (var _i = 0, _c = this._activePagesMeshData; _i < _c.length; _i++) {
+ var mesh = _c[_i];
+ mesh.mesh.shader.uniforms.uFWidth = worldScale * distanceFieldRange * fontScale * this._resolution;
+ }
+ }
+ _super.prototype._render.call(this, renderer);
+ };
+ /**
+ * Validates text before calling parent's getLocalBounds
+ * @returns - The rectangular bounding area
+ */
+ BitmapText.prototype.getLocalBounds = function () {
+ this.validate();
+ return _super.prototype.getLocalBounds.call(this);
+ };
+ /**
+ * Updates text when needed
+ * @private
+ */
+ BitmapText.prototype.validate = function () {
+ if (this.dirty) {
+ this.updateText();
+ this.dirty = false;
+ }
+ };
+ Object.defineProperty(BitmapText.prototype, "tint", {
+ /**
+ * The tint of the BitmapText object.
+ * @default 0xffffff
+ */
+ get: function () {
+ return this._tint;
+ },
+ set: function (value) {
+ if (this._tint === value)
+ { return; }
+ this._tint = value;
+ for (var i = 0; i < this._activePagesMeshData.length; i++) {
+ this._activePagesMeshData[i].mesh.tint = value;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BitmapText.prototype, "align", {
+ /**
+ * The alignment of the BitmapText object.
+ * @member {string}
+ * @default 'left'
+ */
+ get: function () {
+ return this._align;
+ },
+ set: function (value) {
+ if (this._align !== value) {
+ this._align = value;
+ this.dirty = true;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BitmapText.prototype, "fontName", {
+ /** The name of the BitmapFont. */
+ get: function () {
+ return this._fontName;
+ },
+ set: function (value) {
+ if (!BitmapFont.available[value]) {
+ throw new Error("Missing BitmapFont \"" + value + "\"");
+ }
+ if (this._fontName !== value) {
+ this._fontName = value;
+ this.dirty = true;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BitmapText.prototype, "fontSize", {
+ /** The size of the font to display. */
+ get: function () {
+ return this._fontSize;
+ },
+ set: function (value) {
+ if (this._fontSize !== value) {
+ this._fontSize = value;
+ this.dirty = true;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BitmapText.prototype, "anchor", {
+ /**
+ * The anchor sets the origin point of the text.
+ *
+ * The default is `(0,0)`, this means the text's origin is the top left.
+ *
+ * Setting the anchor to `(0.5,0.5)` means the text's origin is centered.
+ *
+ * Setting the anchor to `(1,1)` would mean the text's origin point will be the bottom right corner.
+ */
+ get: function () {
+ return this._anchor;
+ },
+ set: function (value) {
+ if (typeof value === 'number') {
+ this._anchor.set(value);
+ }
+ else {
+ this._anchor.copyFrom(value);
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BitmapText.prototype, "text", {
+ /** The text of the BitmapText object. */
+ get: function () {
+ return this._text;
+ },
+ set: function (text) {
+ text = String(text === null || text === undefined ? '' : text);
+ if (this._text === text) {
+ return;
+ }
+ this._text = text;
+ this.dirty = true;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BitmapText.prototype, "maxWidth", {
+ /**
+ * The max width of this bitmap text in pixels. If the text provided is longer than the
+ * value provided, line breaks will be automatically inserted in the last whitespace.
+ * Disable by setting the value to 0.
+ */
+ get: function () {
+ return this._maxWidth;
+ },
+ set: function (value) {
+ if (this._maxWidth === value) {
+ return;
+ }
+ this._maxWidth = value;
+ this.dirty = true;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BitmapText.prototype, "maxLineHeight", {
+ /**
+ * The max line height. This is useful when trying to use the total height of the Text,
+ * i.e. when trying to vertically align.
+ * @readonly
+ */
+ get: function () {
+ this.validate();
+ return this._maxLineHeight;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BitmapText.prototype, "textWidth", {
+ /**
+ * The width of the overall text, different from fontSize,
+ * which is defined in the style object.
+ * @readonly
+ */
+ get: function () {
+ this.validate();
+ return this._textWidth;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BitmapText.prototype, "letterSpacing", {
+ /** Additional space between characters. */
+ get: function () {
+ return this._letterSpacing;
+ },
+ set: function (value) {
+ if (this._letterSpacing !== value) {
+ this._letterSpacing = value;
+ this.dirty = true;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BitmapText.prototype, "roundPixels", {
+ /**
+ * If true PixiJS will Math.floor() x/y values when rendering, stopping pixel interpolation.
+ * Advantages can include sharper image quality (like text) and faster rendering on canvas.
+ * The main disadvantage is movement of objects may appear less smooth.
+ * To set the global default, change {@link PIXI.settings.ROUND_PIXELS}
+ * @default PIXI.settings.ROUND_PIXELS
+ */
+ get: function () {
+ return this._roundPixels;
+ },
+ set: function (value) {
+ if (value !== this._roundPixels) {
+ this._roundPixels = value;
+ this.dirty = true;
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BitmapText.prototype, "textHeight", {
+ /**
+ * The height of the overall text, different from fontSize,
+ * which is defined in the style object.
+ * @readonly
+ */
+ get: function () {
+ this.validate();
+ return this._textHeight;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BitmapText.prototype, "resolution", {
+ /**
+ * The resolution / device pixel ratio of the canvas.
+ *
+ * This is set to automatically match the renderer resolution by default, but can be overridden by setting manually.
+ * @default 1
+ */
+ get: function () {
+ return this._resolution;
+ },
+ set: function (value) {
+ this._autoResolution = false;
+ if (this._resolution === value) {
+ return;
+ }
+ this._resolution = value;
+ this.dirty = true;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ BitmapText.prototype.destroy = function (options) {
+ var _textureCache = this._textureCache;
+ for (var id in _textureCache) {
+ var texture = _textureCache[id];
+ texture.destroy();
+ delete _textureCache[id];
+ }
+ this._textureCache = null;
+ _super.prototype.destroy.call(this, options);
+ };
+ BitmapText.styleDefaults = {
+ align: 'left',
+ tint: 0xFFFFFF,
+ maxWidth: 0,
+ letterSpacing: 0,
+ };
+ return BitmapText;
+ }(Container));
+
+ /**
+ * {@link PIXI.Loader Loader} middleware for loading
+ * bitmap-based fonts suitable for using with {@link PIXI.BitmapText}.
+ * @memberof PIXI
+ */
+ var BitmapFontLoader = /** @class */ (function () {
+ function BitmapFontLoader() {
+ }
+ /**
+ * Called when the plugin is installed.
+ * @see PIXI.extensions.add
+ */
+ BitmapFontLoader.add = function () {
+ exports.LoaderResource.setExtensionXhrType('fnt', exports.LoaderResource.XHR_RESPONSE_TYPE.TEXT);
+ };
+ /**
+ * Called after a resource is loaded.
+ * @see PIXI.Loader.loaderMiddleware
+ * @param this
+ * @param {PIXI.LoaderResource} resource
+ * @param {Function} next
+ */
+ BitmapFontLoader.use = function (resource, next) {
+ var format = autoDetectFormat(resource.data);
+ // Resource was not recognised as any of the expected font data format
+ if (!format) {
+ next();
+ return;
+ }
+ var baseUrl = BitmapFontLoader.getBaseUrl(this, resource);
+ var data = format.parse(resource.data);
+ var textures = {};
+ // Handle completed, when the number of textures
+ // load is the same number as references in the fnt file
+ var completed = function (page) {
+ textures[page.metadata.pageFile] = page.texture;
+ if (Object.keys(textures).length === data.page.length) {
+ resource.bitmapFont = BitmapFont.install(data, textures, true);
+ next();
+ }
+ };
+ for (var i = 0; i < data.page.length; ++i) {
+ var pageFile = data.page[i].file;
+ var url = baseUrl + pageFile;
+ var exists = false;
+ // incase the image is loaded outside
+ // using the same loader, resource will be available
+ for (var name in this.resources) {
+ var bitmapResource = this.resources[name];
+ if (bitmapResource.url === url) {
+ bitmapResource.metadata.pageFile = pageFile;
+ if (bitmapResource.texture) {
+ completed(bitmapResource);
+ }
+ else {
+ bitmapResource.onAfterMiddleware.add(completed);
+ }
+ exists = true;
+ break;
+ }
+ }
+ // texture is not loaded, we'll attempt to add
+ // it to the load and add the texture to the list
+ if (!exists) {
+ // Standard loading options for images
+ var options = {
+ crossOrigin: resource.crossOrigin,
+ loadType: exports.LoaderResource.LOAD_TYPE.IMAGE,
+ metadata: Object.assign({ pageFile: pageFile }, resource.metadata.imageMetadata),
+ parentResource: resource,
+ };
+ this.add(url, options, completed);
+ }
+ }
+ };
+ /**
+ * Get folder path from a resource.
+ * @param loader
+ * @param resource
+ */
+ BitmapFontLoader.getBaseUrl = function (loader, resource) {
+ var resUrl = !resource.isDataUrl ? BitmapFontLoader.dirname(resource.url) : '';
+ if (resource.isDataUrl) {
+ if (resUrl === '.') {
+ resUrl = '';
+ }
+ if (loader.baseUrl && resUrl) {
+ // if baseurl has a trailing slash then add one to resUrl so the replace works below
+ if (loader.baseUrl.charAt(loader.baseUrl.length - 1) === '/') {
+ resUrl += '/';
+ }
+ }
+ }
+ // remove baseUrl from resUrl
+ resUrl = resUrl.replace(loader.baseUrl, '');
+ // if there is an resUrl now, it needs a trailing slash. Ensure that it does if the string isn't empty.
+ if (resUrl && resUrl.charAt(resUrl.length - 1) !== '/') {
+ resUrl += '/';
+ }
+ return resUrl;
+ };
+ /**
+ * Replacement for NodeJS's path.dirname
+ * @param {string} url - Path to get directory for
+ */
+ BitmapFontLoader.dirname = function (url) {
+ var dir = url
+ .replace(/\\/g, '/') // convert windows notation to UNIX notation, URL-safe because it's a forbidden character
+ .replace(/\/$/, '') // replace trailing slash
+ .replace(/\/[^\/]*$/, ''); // remove everything after the last
+ // File request is relative, use current directory
+ if (dir === url) {
+ return '.';
+ }
+ // Started with a slash
+ else if (dir === '') {
+ return '/';
+ }
+ return dir;
+ };
+ /** @ignore */
+ BitmapFontLoader.extension = exports.ExtensionType.Loader;
+ return BitmapFontLoader;
+ }());
+
+ /*!
+ * @pixi/filter-alpha - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/filter-alpha is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+ /* global Reflect, Promise */
+
+ var extendStatics$7 = function(d, b) {
+ extendStatics$7 = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
+ return extendStatics$7(d, b);
+ };
+
+ function __extends$7(d, b) {
+ extendStatics$7(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ }
+
+ var fragment$4 = "varying vec2 vTextureCoord;\n\nuniform sampler2D uSampler;\nuniform float uAlpha;\n\nvoid main(void)\n{\n gl_FragColor = texture2D(uSampler, vTextureCoord) * uAlpha;\n}\n";
+
+ /**
+ * Simplest filter - applies alpha.
+ *
+ * Use this instead of Container's alpha property to avoid visual layering of individual elements.
+ * AlphaFilter applies alpha evenly across the entire display object and any opaque elements it contains.
+ * If elements are not opaque, they will blend with each other anyway.
+ *
+ * Very handy if you want to use common features of all filters:
+ *
+ * 1. Assign a blendMode to this filter, blend all elements inside display object with background.
+ *
+ * 2. To use clipping in display coordinates, assign a filterArea to the same container that has this filter.
+ * @memberof PIXI.filters
+ */
+ var AlphaFilter = /** @class */ (function (_super) {
+ __extends$7(AlphaFilter, _super);
+ /**
+ * @param alpha - Amount of alpha from 0 to 1, where 0 is transparent
+ */
+ function AlphaFilter(alpha) {
+ if (alpha === void 0) { alpha = 1.0; }
+ var _this = _super.call(this, defaultVertex$1, fragment$4, { uAlpha: 1 }) || this;
+ _this.alpha = alpha;
+ return _this;
+ }
+ Object.defineProperty(AlphaFilter.prototype, "alpha", {
+ /**
+ * Coefficient for alpha multiplication
+ * @default 1
+ */
+ get: function () {
+ return this.uniforms.uAlpha;
+ },
+ set: function (value) {
+ this.uniforms.uAlpha = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return AlphaFilter;
+ }(Filter));
+
+ /*!
+ * @pixi/filter-blur - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/filter-blur is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+ /* global Reflect, Promise */
+
+ var extendStatics$6 = function(d, b) {
+ extendStatics$6 = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
+ return extendStatics$6(d, b);
+ };
+
+ function __extends$6(d, b) {
+ extendStatics$6(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ }
+
+ var vertTemplate = "\n attribute vec2 aVertexPosition;\n\n uniform mat3 projectionMatrix;\n\n uniform float strength;\n\n varying vec2 vBlurTexCoords[%size%];\n\n uniform vec4 inputSize;\n uniform vec4 outputFrame;\n\n vec4 filterVertexPosition( void )\n {\n vec2 position = aVertexPosition * max(outputFrame.zw, vec2(0.)) + outputFrame.xy;\n\n return vec4((projectionMatrix * vec3(position, 1.0)).xy, 0.0, 1.0);\n }\n\n vec2 filterTextureCoord( void )\n {\n return aVertexPosition * (outputFrame.zw * inputSize.zw);\n }\n\n void main(void)\n {\n gl_Position = filterVertexPosition();\n\n vec2 textureCoord = filterTextureCoord();\n %blur%\n }";
+ function generateBlurVertSource(kernelSize, x) {
+ var halfLength = Math.ceil(kernelSize / 2);
+ var vertSource = vertTemplate;
+ var blurLoop = '';
+ var template;
+ if (x) {
+ template = 'vBlurTexCoords[%index%] = textureCoord + vec2(%sampleIndex% * strength, 0.0);';
+ }
+ else {
+ template = 'vBlurTexCoords[%index%] = textureCoord + vec2(0.0, %sampleIndex% * strength);';
+ }
+ for (var i = 0; i < kernelSize; i++) {
+ var blur = template.replace('%index%', i.toString());
+ blur = blur.replace('%sampleIndex%', i - (halfLength - 1) + ".0");
+ blurLoop += blur;
+ blurLoop += '\n';
+ }
+ vertSource = vertSource.replace('%blur%', blurLoop);
+ vertSource = vertSource.replace('%size%', kernelSize.toString());
+ return vertSource;
+ }
+
+ var GAUSSIAN_VALUES = {
+ 5: [0.153388, 0.221461, 0.250301],
+ 7: [0.071303, 0.131514, 0.189879, 0.214607],
+ 9: [0.028532, 0.067234, 0.124009, 0.179044, 0.20236],
+ 11: [0.0093, 0.028002, 0.065984, 0.121703, 0.175713, 0.198596],
+ 13: [0.002406, 0.009255, 0.027867, 0.065666, 0.121117, 0.174868, 0.197641],
+ 15: [0.000489, 0.002403, 0.009246, 0.02784, 0.065602, 0.120999, 0.174697, 0.197448],
+ };
+ var fragTemplate = [
+ 'varying vec2 vBlurTexCoords[%size%];',
+ 'uniform sampler2D uSampler;',
+ 'void main(void)',
+ '{',
+ ' gl_FragColor = vec4(0.0);',
+ ' %blur%',
+ '}' ].join('\n');
+ function generateBlurFragSource(kernelSize) {
+ var kernel = GAUSSIAN_VALUES[kernelSize];
+ var halfLength = kernel.length;
+ var fragSource = fragTemplate;
+ var blurLoop = '';
+ var template = 'gl_FragColor += texture2D(uSampler, vBlurTexCoords[%index%]) * %value%;';
+ var value;
+ for (var i = 0; i < kernelSize; i++) {
+ var blur = template.replace('%index%', i.toString());
+ value = i;
+ if (i >= halfLength) {
+ value = kernelSize - i - 1;
+ }
+ blur = blur.replace('%value%', kernel[value].toString());
+ blurLoop += blur;
+ blurLoop += '\n';
+ }
+ fragSource = fragSource.replace('%blur%', blurLoop);
+ fragSource = fragSource.replace('%size%', kernelSize.toString());
+ return fragSource;
+ }
+
+ /*!
+ * @pixi/constants - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/constants is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+ /**
+ * Different types of environments for WebGL.
+ * @static
+ * @memberof PIXI
+ * @name ENV
+ * @enum {number}
+ * @property {number} WEBGL_LEGACY - Used for older v1 WebGL devices. PixiJS will aim to ensure compatibility
+ * with older / less advanced devices. If you experience unexplained flickering prefer this environment.
+ * @property {number} WEBGL - Version 1 of WebGL
+ * @property {number} WEBGL2 - Version 2 of WebGL
+ */
+ var ENV$3;
+ (function (ENV) {
+ ENV[ENV["WEBGL_LEGACY"] = 0] = "WEBGL_LEGACY";
+ ENV[ENV["WEBGL"] = 1] = "WEBGL";
+ ENV[ENV["WEBGL2"] = 2] = "WEBGL2";
+ })(ENV$3 || (ENV$3 = {}));
+ /**
+ * Constant to identify the Renderer Type.
+ * @static
+ * @memberof PIXI
+ * @name RENDERER_TYPE
+ * @enum {number}
+ * @property {number} UNKNOWN - Unknown render type.
+ * @property {number} WEBGL - WebGL render type.
+ * @property {number} CANVAS - Canvas render type.
+ */
+ var RENDERER_TYPE$3;
+ (function (RENDERER_TYPE) {
+ RENDERER_TYPE[RENDERER_TYPE["UNKNOWN"] = 0] = "UNKNOWN";
+ RENDERER_TYPE[RENDERER_TYPE["WEBGL"] = 1] = "WEBGL";
+ RENDERER_TYPE[RENDERER_TYPE["CANVAS"] = 2] = "CANVAS";
+ })(RENDERER_TYPE$3 || (RENDERER_TYPE$3 = {}));
+ /**
+ * Bitwise OR of masks that indicate the buffers to be cleared.
+ * @static
+ * @memberof PIXI
+ * @name BUFFER_BITS
+ * @enum {number}
+ * @property {number} COLOR - Indicates the buffers currently enabled for color writing.
+ * @property {number} DEPTH - Indicates the depth buffer.
+ * @property {number} STENCIL - Indicates the stencil buffer.
+ */
+ var BUFFER_BITS$3;
+ (function (BUFFER_BITS) {
+ BUFFER_BITS[BUFFER_BITS["COLOR"] = 16384] = "COLOR";
+ BUFFER_BITS[BUFFER_BITS["DEPTH"] = 256] = "DEPTH";
+ BUFFER_BITS[BUFFER_BITS["STENCIL"] = 1024] = "STENCIL";
+ })(BUFFER_BITS$3 || (BUFFER_BITS$3 = {}));
+ /**
+ * Various blend modes supported by PIXI.
+ *
+ * IMPORTANT - The WebGL renderer only supports the NORMAL, ADD, MULTIPLY and SCREEN blend modes.
+ * Anything else will silently act like NORMAL.
+ * @memberof PIXI
+ * @name BLEND_MODES
+ * @enum {number}
+ * @property {number} NORMAL -
+ * @property {number} ADD -
+ * @property {number} MULTIPLY -
+ * @property {number} SCREEN -
+ * @property {number} OVERLAY -
+ * @property {number} DARKEN -
+ * @property {number} LIGHTEN -
+ * @property {number} COLOR_DODGE -
+ * @property {number} COLOR_BURN -
+ * @property {number} HARD_LIGHT -
+ * @property {number} SOFT_LIGHT -
+ * @property {number} DIFFERENCE -
+ * @property {number} EXCLUSION -
+ * @property {number} HUE -
+ * @property {number} SATURATION -
+ * @property {number} COLOR -
+ * @property {number} LUMINOSITY -
+ * @property {number} NORMAL_NPM -
+ * @property {number} ADD_NPM -
+ * @property {number} SCREEN_NPM -
+ * @property {number} NONE -
+ * @property {number} SRC_IN -
+ * @property {number} SRC_OUT -
+ * @property {number} SRC_ATOP -
+ * @property {number} DST_OVER -
+ * @property {number} DST_IN -
+ * @property {number} DST_OUT -
+ * @property {number} DST_ATOP -
+ * @property {number} SUBTRACT -
+ * @property {number} SRC_OVER -
+ * @property {number} ERASE -
+ * @property {number} XOR -
+ */
+ var BLEND_MODES$3;
+ (function (BLEND_MODES) {
+ BLEND_MODES[BLEND_MODES["NORMAL"] = 0] = "NORMAL";
+ BLEND_MODES[BLEND_MODES["ADD"] = 1] = "ADD";
+ BLEND_MODES[BLEND_MODES["MULTIPLY"] = 2] = "MULTIPLY";
+ BLEND_MODES[BLEND_MODES["SCREEN"] = 3] = "SCREEN";
+ BLEND_MODES[BLEND_MODES["OVERLAY"] = 4] = "OVERLAY";
+ BLEND_MODES[BLEND_MODES["DARKEN"] = 5] = "DARKEN";
+ BLEND_MODES[BLEND_MODES["LIGHTEN"] = 6] = "LIGHTEN";
+ BLEND_MODES[BLEND_MODES["COLOR_DODGE"] = 7] = "COLOR_DODGE";
+ BLEND_MODES[BLEND_MODES["COLOR_BURN"] = 8] = "COLOR_BURN";
+ BLEND_MODES[BLEND_MODES["HARD_LIGHT"] = 9] = "HARD_LIGHT";
+ BLEND_MODES[BLEND_MODES["SOFT_LIGHT"] = 10] = "SOFT_LIGHT";
+ BLEND_MODES[BLEND_MODES["DIFFERENCE"] = 11] = "DIFFERENCE";
+ BLEND_MODES[BLEND_MODES["EXCLUSION"] = 12] = "EXCLUSION";
+ BLEND_MODES[BLEND_MODES["HUE"] = 13] = "HUE";
+ BLEND_MODES[BLEND_MODES["SATURATION"] = 14] = "SATURATION";
+ BLEND_MODES[BLEND_MODES["COLOR"] = 15] = "COLOR";
+ BLEND_MODES[BLEND_MODES["LUMINOSITY"] = 16] = "LUMINOSITY";
+ BLEND_MODES[BLEND_MODES["NORMAL_NPM"] = 17] = "NORMAL_NPM";
+ BLEND_MODES[BLEND_MODES["ADD_NPM"] = 18] = "ADD_NPM";
+ BLEND_MODES[BLEND_MODES["SCREEN_NPM"] = 19] = "SCREEN_NPM";
+ BLEND_MODES[BLEND_MODES["NONE"] = 20] = "NONE";
+ BLEND_MODES[BLEND_MODES["SRC_OVER"] = 0] = "SRC_OVER";
+ BLEND_MODES[BLEND_MODES["SRC_IN"] = 21] = "SRC_IN";
+ BLEND_MODES[BLEND_MODES["SRC_OUT"] = 22] = "SRC_OUT";
+ BLEND_MODES[BLEND_MODES["SRC_ATOP"] = 23] = "SRC_ATOP";
+ BLEND_MODES[BLEND_MODES["DST_OVER"] = 24] = "DST_OVER";
+ BLEND_MODES[BLEND_MODES["DST_IN"] = 25] = "DST_IN";
+ BLEND_MODES[BLEND_MODES["DST_OUT"] = 26] = "DST_OUT";
+ BLEND_MODES[BLEND_MODES["DST_ATOP"] = 27] = "DST_ATOP";
+ BLEND_MODES[BLEND_MODES["ERASE"] = 26] = "ERASE";
+ BLEND_MODES[BLEND_MODES["SUBTRACT"] = 28] = "SUBTRACT";
+ BLEND_MODES[BLEND_MODES["XOR"] = 29] = "XOR";
+ })(BLEND_MODES$3 || (BLEND_MODES$3 = {}));
+ /**
+ * Various webgl draw modes. These can be used to specify which GL drawMode to use
+ * under certain situations and renderers.
+ * @memberof PIXI
+ * @static
+ * @name DRAW_MODES
+ * @enum {number}
+ * @property {number} POINTS -
+ * @property {number} LINES -
+ * @property {number} LINE_LOOP -
+ * @property {number} LINE_STRIP -
+ * @property {number} TRIANGLES -
+ * @property {number} TRIANGLE_STRIP -
+ * @property {number} TRIANGLE_FAN -
+ */
+ var DRAW_MODES$3;
+ (function (DRAW_MODES) {
+ DRAW_MODES[DRAW_MODES["POINTS"] = 0] = "POINTS";
+ DRAW_MODES[DRAW_MODES["LINES"] = 1] = "LINES";
+ DRAW_MODES[DRAW_MODES["LINE_LOOP"] = 2] = "LINE_LOOP";
+ DRAW_MODES[DRAW_MODES["LINE_STRIP"] = 3] = "LINE_STRIP";
+ DRAW_MODES[DRAW_MODES["TRIANGLES"] = 4] = "TRIANGLES";
+ DRAW_MODES[DRAW_MODES["TRIANGLE_STRIP"] = 5] = "TRIANGLE_STRIP";
+ DRAW_MODES[DRAW_MODES["TRIANGLE_FAN"] = 6] = "TRIANGLE_FAN";
+ })(DRAW_MODES$3 || (DRAW_MODES$3 = {}));
+ /**
+ * Various GL texture/resources formats.
+ * @memberof PIXI
+ * @static
+ * @name FORMATS
+ * @enum {number}
+ * @property {number} [RGBA=6408] -
+ * @property {number} [RGB=6407] -
+ * @property {number} [RG=33319] -
+ * @property {number} [RED=6403] -
+ * @property {number} [RGBA_INTEGER=36249] -
+ * @property {number} [RGB_INTEGER=36248] -
+ * @property {number} [RG_INTEGER=33320] -
+ * @property {number} [RED_INTEGER=36244] -
+ * @property {number} [ALPHA=6406] -
+ * @property {number} [LUMINANCE=6409] -
+ * @property {number} [LUMINANCE_ALPHA=6410] -
+ * @property {number} [DEPTH_COMPONENT=6402] -
+ * @property {number} [DEPTH_STENCIL=34041] -
+ */
+ var FORMATS$3;
+ (function (FORMATS) {
+ FORMATS[FORMATS["RGBA"] = 6408] = "RGBA";
+ FORMATS[FORMATS["RGB"] = 6407] = "RGB";
+ FORMATS[FORMATS["RG"] = 33319] = "RG";
+ FORMATS[FORMATS["RED"] = 6403] = "RED";
+ FORMATS[FORMATS["RGBA_INTEGER"] = 36249] = "RGBA_INTEGER";
+ FORMATS[FORMATS["RGB_INTEGER"] = 36248] = "RGB_INTEGER";
+ FORMATS[FORMATS["RG_INTEGER"] = 33320] = "RG_INTEGER";
+ FORMATS[FORMATS["RED_INTEGER"] = 36244] = "RED_INTEGER";
+ FORMATS[FORMATS["ALPHA"] = 6406] = "ALPHA";
+ FORMATS[FORMATS["LUMINANCE"] = 6409] = "LUMINANCE";
+ FORMATS[FORMATS["LUMINANCE_ALPHA"] = 6410] = "LUMINANCE_ALPHA";
+ FORMATS[FORMATS["DEPTH_COMPONENT"] = 6402] = "DEPTH_COMPONENT";
+ FORMATS[FORMATS["DEPTH_STENCIL"] = 34041] = "DEPTH_STENCIL";
+ })(FORMATS$3 || (FORMATS$3 = {}));
+ /**
+ * Various GL target types.
+ * @memberof PIXI
+ * @static
+ * @name TARGETS
+ * @enum {number}
+ * @property {number} [TEXTURE_2D=3553] -
+ * @property {number} [TEXTURE_CUBE_MAP=34067] -
+ * @property {number} [TEXTURE_2D_ARRAY=35866] -
+ * @property {number} [TEXTURE_CUBE_MAP_POSITIVE_X=34069] -
+ * @property {number} [TEXTURE_CUBE_MAP_NEGATIVE_X=34070] -
+ * @property {number} [TEXTURE_CUBE_MAP_POSITIVE_Y=34071] -
+ * @property {number} [TEXTURE_CUBE_MAP_NEGATIVE_Y=34072] -
+ * @property {number} [TEXTURE_CUBE_MAP_POSITIVE_Z=34073] -
+ * @property {number} [TEXTURE_CUBE_MAP_NEGATIVE_Z=34074] -
+ */
+ var TARGETS$3;
+ (function (TARGETS) {
+ TARGETS[TARGETS["TEXTURE_2D"] = 3553] = "TEXTURE_2D";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP"] = 34067] = "TEXTURE_CUBE_MAP";
+ TARGETS[TARGETS["TEXTURE_2D_ARRAY"] = 35866] = "TEXTURE_2D_ARRAY";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_POSITIVE_X"] = 34069] = "TEXTURE_CUBE_MAP_POSITIVE_X";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_NEGATIVE_X"] = 34070] = "TEXTURE_CUBE_MAP_NEGATIVE_X";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_POSITIVE_Y"] = 34071] = "TEXTURE_CUBE_MAP_POSITIVE_Y";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_NEGATIVE_Y"] = 34072] = "TEXTURE_CUBE_MAP_NEGATIVE_Y";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_POSITIVE_Z"] = 34073] = "TEXTURE_CUBE_MAP_POSITIVE_Z";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_NEGATIVE_Z"] = 34074] = "TEXTURE_CUBE_MAP_NEGATIVE_Z";
+ })(TARGETS$3 || (TARGETS$3 = {}));
+ /**
+ * Various GL data format types.
+ * @memberof PIXI
+ * @static
+ * @name TYPES
+ * @enum {number}
+ * @property {number} [UNSIGNED_BYTE=5121] -
+ * @property {number} [UNSIGNED_SHORT=5123] -
+ * @property {number} [UNSIGNED_SHORT_5_6_5=33635] -
+ * @property {number} [UNSIGNED_SHORT_4_4_4_4=32819] -
+ * @property {number} [UNSIGNED_SHORT_5_5_5_1=32820] -
+ * @property {number} [UNSIGNED_INT=5125] -
+ * @property {number} [UNSIGNED_INT_10F_11F_11F_REV=35899] -
+ * @property {number} [UNSIGNED_INT_2_10_10_10_REV=33640] -
+ * @property {number} [UNSIGNED_INT_24_8=34042] -
+ * @property {number} [UNSIGNED_INT_5_9_9_9_REV=35902] -
+ * @property {number} [BYTE=5120] -
+ * @property {number} [SHORT=5122] -
+ * @property {number} [INT=5124] -
+ * @property {number} [FLOAT=5126] -
+ * @property {number} [FLOAT_32_UNSIGNED_INT_24_8_REV=36269] -
+ * @property {number} [HALF_FLOAT=36193] -
+ */
+ var TYPES$3;
+ (function (TYPES) {
+ TYPES[TYPES["UNSIGNED_BYTE"] = 5121] = "UNSIGNED_BYTE";
+ TYPES[TYPES["UNSIGNED_SHORT"] = 5123] = "UNSIGNED_SHORT";
+ TYPES[TYPES["UNSIGNED_SHORT_5_6_5"] = 33635] = "UNSIGNED_SHORT_5_6_5";
+ TYPES[TYPES["UNSIGNED_SHORT_4_4_4_4"] = 32819] = "UNSIGNED_SHORT_4_4_4_4";
+ TYPES[TYPES["UNSIGNED_SHORT_5_5_5_1"] = 32820] = "UNSIGNED_SHORT_5_5_5_1";
+ TYPES[TYPES["UNSIGNED_INT"] = 5125] = "UNSIGNED_INT";
+ TYPES[TYPES["UNSIGNED_INT_10F_11F_11F_REV"] = 35899] = "UNSIGNED_INT_10F_11F_11F_REV";
+ TYPES[TYPES["UNSIGNED_INT_2_10_10_10_REV"] = 33640] = "UNSIGNED_INT_2_10_10_10_REV";
+ TYPES[TYPES["UNSIGNED_INT_24_8"] = 34042] = "UNSIGNED_INT_24_8";
+ TYPES[TYPES["UNSIGNED_INT_5_9_9_9_REV"] = 35902] = "UNSIGNED_INT_5_9_9_9_REV";
+ TYPES[TYPES["BYTE"] = 5120] = "BYTE";
+ TYPES[TYPES["SHORT"] = 5122] = "SHORT";
+ TYPES[TYPES["INT"] = 5124] = "INT";
+ TYPES[TYPES["FLOAT"] = 5126] = "FLOAT";
+ TYPES[TYPES["FLOAT_32_UNSIGNED_INT_24_8_REV"] = 36269] = "FLOAT_32_UNSIGNED_INT_24_8_REV";
+ TYPES[TYPES["HALF_FLOAT"] = 36193] = "HALF_FLOAT";
+ })(TYPES$3 || (TYPES$3 = {}));
+ /**
+ * Various sampler types. Correspond to `sampler`, `isampler`, `usampler` GLSL types respectively.
+ * WebGL1 works only with FLOAT.
+ * @memberof PIXI
+ * @static
+ * @name SAMPLER_TYPES
+ * @enum {number}
+ * @property {number} [FLOAT=0] -
+ * @property {number} [INT=1] -
+ * @property {number} [UINT=2] -
+ */
+ var SAMPLER_TYPES$3;
+ (function (SAMPLER_TYPES) {
+ SAMPLER_TYPES[SAMPLER_TYPES["FLOAT"] = 0] = "FLOAT";
+ SAMPLER_TYPES[SAMPLER_TYPES["INT"] = 1] = "INT";
+ SAMPLER_TYPES[SAMPLER_TYPES["UINT"] = 2] = "UINT";
+ })(SAMPLER_TYPES$3 || (SAMPLER_TYPES$3 = {}));
+ /**
+ * The scale modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.SCALE_MODE} scale mode affects the default scaling mode of future operations.
+ * It can be re-assigned to either LINEAR or NEAREST, depending upon suitability.
+ * @memberof PIXI
+ * @static
+ * @name SCALE_MODES
+ * @enum {number}
+ * @property {number} LINEAR Smooth scaling
+ * @property {number} NEAREST Pixelating scaling
+ */
+ var SCALE_MODES$3;
+ (function (SCALE_MODES) {
+ SCALE_MODES[SCALE_MODES["NEAREST"] = 0] = "NEAREST";
+ SCALE_MODES[SCALE_MODES["LINEAR"] = 1] = "LINEAR";
+ })(SCALE_MODES$3 || (SCALE_MODES$3 = {}));
+ /**
+ * The wrap modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.WRAP_MODE} wrap mode affects the default wrapping mode of future operations.
+ * It can be re-assigned to either CLAMP or REPEAT, depending upon suitability.
+ * If the texture is non power of two then clamp will be used regardless as WebGL can
+ * only use REPEAT if the texture is po2.
+ *
+ * This property only affects WebGL.
+ * @name WRAP_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} CLAMP - The textures uvs are clamped
+ * @property {number} REPEAT - The texture uvs tile and repeat
+ * @property {number} MIRRORED_REPEAT - The texture uvs tile and repeat with mirroring
+ */
+ var WRAP_MODES$3;
+ (function (WRAP_MODES) {
+ WRAP_MODES[WRAP_MODES["CLAMP"] = 33071] = "CLAMP";
+ WRAP_MODES[WRAP_MODES["REPEAT"] = 10497] = "REPEAT";
+ WRAP_MODES[WRAP_MODES["MIRRORED_REPEAT"] = 33648] = "MIRRORED_REPEAT";
+ })(WRAP_MODES$3 || (WRAP_MODES$3 = {}));
+ /**
+ * Mipmap filtering modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.MIPMAP_TEXTURES} affects default texture filtering.
+ * Mipmaps are generated for a baseTexture if its `mipmap` field is `ON`,
+ * or its `POW2` and texture dimensions are powers of 2.
+ * Due to platform restriction, `ON` option will work like `POW2` for webgl-1.
+ *
+ * This property only affects WebGL.
+ * @name MIPMAP_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} OFF - No mipmaps
+ * @property {number} POW2 - Generate mipmaps if texture dimensions are pow2
+ * @property {number} ON - Always generate mipmaps
+ * @property {number} ON_MANUAL - Use mipmaps, but do not auto-generate them; this is used with a resource
+ * that supports buffering each level-of-detail.
+ */
+ var MIPMAP_MODES$3;
+ (function (MIPMAP_MODES) {
+ MIPMAP_MODES[MIPMAP_MODES["OFF"] = 0] = "OFF";
+ MIPMAP_MODES[MIPMAP_MODES["POW2"] = 1] = "POW2";
+ MIPMAP_MODES[MIPMAP_MODES["ON"] = 2] = "ON";
+ MIPMAP_MODES[MIPMAP_MODES["ON_MANUAL"] = 3] = "ON_MANUAL";
+ })(MIPMAP_MODES$3 || (MIPMAP_MODES$3 = {}));
+ /**
+ * How to treat textures with premultiplied alpha
+ * @name ALPHA_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} NO_PREMULTIPLIED_ALPHA - Source is not premultiplied, leave it like that.
+ * Option for compressed and data textures that are created from typed arrays.
+ * @property {number} PREMULTIPLY_ON_UPLOAD - Source is not premultiplied, premultiply on upload.
+ * Default option, used for all loaded images.
+ * @property {number} PREMULTIPLIED_ALPHA - Source is already premultiplied
+ * Example: spine atlases with `_pma` suffix.
+ * @property {number} NPM - Alias for NO_PREMULTIPLIED_ALPHA.
+ * @property {number} UNPACK - Default option, alias for PREMULTIPLY_ON_UPLOAD.
+ * @property {number} PMA - Alias for PREMULTIPLIED_ALPHA.
+ */
+ var ALPHA_MODES$3;
+ (function (ALPHA_MODES) {
+ ALPHA_MODES[ALPHA_MODES["NPM"] = 0] = "NPM";
+ ALPHA_MODES[ALPHA_MODES["UNPACK"] = 1] = "UNPACK";
+ ALPHA_MODES[ALPHA_MODES["PMA"] = 2] = "PMA";
+ ALPHA_MODES[ALPHA_MODES["NO_PREMULTIPLIED_ALPHA"] = 0] = "NO_PREMULTIPLIED_ALPHA";
+ ALPHA_MODES[ALPHA_MODES["PREMULTIPLY_ON_UPLOAD"] = 1] = "PREMULTIPLY_ON_UPLOAD";
+ ALPHA_MODES[ALPHA_MODES["PREMULTIPLY_ALPHA"] = 2] = "PREMULTIPLY_ALPHA";
+ ALPHA_MODES[ALPHA_MODES["PREMULTIPLIED_ALPHA"] = 2] = "PREMULTIPLIED_ALPHA";
+ })(ALPHA_MODES$3 || (ALPHA_MODES$3 = {}));
+ /**
+ * Configure whether filter textures are cleared after binding.
+ *
+ * Filter textures need not be cleared if the filter does not use pixel blending. {@link CLEAR_MODES.BLIT} will detect
+ * this and skip clearing as an optimization.
+ * @name CLEAR_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} BLEND - Do not clear the filter texture. The filter's output will blend on top of the output texture.
+ * @property {number} CLEAR - Always clear the filter texture.
+ * @property {number} BLIT - Clear only if {@link FilterSystem.forceClear} is set or if the filter uses pixel blending.
+ * @property {number} NO - Alias for BLEND, same as `false` in earlier versions
+ * @property {number} YES - Alias for CLEAR, same as `true` in earlier versions
+ * @property {number} AUTO - Alias for BLIT
+ */
+ var CLEAR_MODES$3;
+ (function (CLEAR_MODES) {
+ CLEAR_MODES[CLEAR_MODES["NO"] = 0] = "NO";
+ CLEAR_MODES[CLEAR_MODES["YES"] = 1] = "YES";
+ CLEAR_MODES[CLEAR_MODES["AUTO"] = 2] = "AUTO";
+ CLEAR_MODES[CLEAR_MODES["BLEND"] = 0] = "BLEND";
+ CLEAR_MODES[CLEAR_MODES["CLEAR"] = 1] = "CLEAR";
+ CLEAR_MODES[CLEAR_MODES["BLIT"] = 2] = "BLIT";
+ })(CLEAR_MODES$3 || (CLEAR_MODES$3 = {}));
+ /**
+ * The gc modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.GC_MODE} Garbage Collection mode for PixiJS textures is AUTO
+ * If set to GC_MODE, the renderer will occasionally check textures usage. If they are not
+ * used for a specified period of time they will be removed from the GPU. They will of course
+ * be uploaded again when they are required. This is a silent behind the scenes process that
+ * should ensure that the GPU does not get filled up.
+ *
+ * Handy for mobile devices!
+ * This property only affects WebGL.
+ * @name GC_MODES
+ * @enum {number}
+ * @static
+ * @memberof PIXI
+ * @property {number} AUTO - Garbage collection will happen periodically automatically
+ * @property {number} MANUAL - Garbage collection will need to be called manually
+ */
+ var GC_MODES$3;
+ (function (GC_MODES) {
+ GC_MODES[GC_MODES["AUTO"] = 0] = "AUTO";
+ GC_MODES[GC_MODES["MANUAL"] = 1] = "MANUAL";
+ })(GC_MODES$3 || (GC_MODES$3 = {}));
+ /**
+ * Constants that specify float precision in shaders.
+ * @name PRECISION
+ * @memberof PIXI
+ * @constant
+ * @static
+ * @enum {string}
+ * @property {string} [LOW='lowp'] -
+ * @property {string} [MEDIUM='mediump'] -
+ * @property {string} [HIGH='highp'] -
+ */
+ var PRECISION$3;
+ (function (PRECISION) {
+ PRECISION["LOW"] = "lowp";
+ PRECISION["MEDIUM"] = "mediump";
+ PRECISION["HIGH"] = "highp";
+ })(PRECISION$3 || (PRECISION$3 = {}));
+ /**
+ * Constants for mask implementations.
+ * We use `type` suffix because it leads to very different behaviours
+ * @name MASK_TYPES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} NONE - Mask is ignored
+ * @property {number} SCISSOR - Scissor mask, rectangle on screen, cheap
+ * @property {number} STENCIL - Stencil mask, 1-bit, medium, works only if renderer supports stencil
+ * @property {number} SPRITE - Mask that uses SpriteMaskFilter, uses temporary RenderTexture
+ * @property {number} COLOR - Color mask (RGBA)
+ */
+ var MASK_TYPES$3;
+ (function (MASK_TYPES) {
+ MASK_TYPES[MASK_TYPES["NONE"] = 0] = "NONE";
+ MASK_TYPES[MASK_TYPES["SCISSOR"] = 1] = "SCISSOR";
+ MASK_TYPES[MASK_TYPES["STENCIL"] = 2] = "STENCIL";
+ MASK_TYPES[MASK_TYPES["SPRITE"] = 3] = "SPRITE";
+ MASK_TYPES[MASK_TYPES["COLOR"] = 4] = "COLOR";
+ })(MASK_TYPES$3 || (MASK_TYPES$3 = {}));
+ /**
+ * Bitwise OR of masks that indicate the color channels that are rendered to.
+ * @static
+ * @memberof PIXI
+ * @name COLOR_MASK_BITS
+ * @enum {number}
+ * @property {number} RED - Red channel.
+ * @property {number} GREEN - Green channel
+ * @property {number} BLUE - Blue channel.
+ * @property {number} ALPHA - Alpha channel.
+ */
+ var COLOR_MASK_BITS$3;
+ (function (COLOR_MASK_BITS) {
+ COLOR_MASK_BITS[COLOR_MASK_BITS["RED"] = 1] = "RED";
+ COLOR_MASK_BITS[COLOR_MASK_BITS["GREEN"] = 2] = "GREEN";
+ COLOR_MASK_BITS[COLOR_MASK_BITS["BLUE"] = 4] = "BLUE";
+ COLOR_MASK_BITS[COLOR_MASK_BITS["ALPHA"] = 8] = "ALPHA";
+ })(COLOR_MASK_BITS$3 || (COLOR_MASK_BITS$3 = {}));
+ /**
+ * Constants for multi-sampling antialiasing.
+ * @see PIXI.Framebuffer#multisample
+ * @name MSAA_QUALITY
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} NONE - No multisampling for this renderTexture
+ * @property {number} LOW - Try 2 samples
+ * @property {number} MEDIUM - Try 4 samples
+ * @property {number} HIGH - Try 8 samples
+ */
+ var MSAA_QUALITY$3;
+ (function (MSAA_QUALITY) {
+ MSAA_QUALITY[MSAA_QUALITY["NONE"] = 0] = "NONE";
+ MSAA_QUALITY[MSAA_QUALITY["LOW"] = 2] = "LOW";
+ MSAA_QUALITY[MSAA_QUALITY["MEDIUM"] = 4] = "MEDIUM";
+ MSAA_QUALITY[MSAA_QUALITY["HIGH"] = 8] = "HIGH";
+ })(MSAA_QUALITY$3 || (MSAA_QUALITY$3 = {}));
+ /**
+ * Constants for various buffer types in Pixi
+ * @see PIXI.BUFFER_TYPE
+ * @name BUFFER_TYPE
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} ELEMENT_ARRAY_BUFFER - buffer type for using as an index buffer
+ * @property {number} ARRAY_BUFFER - buffer type for using attribute data
+ * @property {number} UNIFORM_BUFFER - the buffer type is for uniform buffer objects
+ */
+ var BUFFER_TYPE$3;
+ (function (BUFFER_TYPE) {
+ BUFFER_TYPE[BUFFER_TYPE["ELEMENT_ARRAY_BUFFER"] = 34963] = "ELEMENT_ARRAY_BUFFER";
+ BUFFER_TYPE[BUFFER_TYPE["ARRAY_BUFFER"] = 34962] = "ARRAY_BUFFER";
+ // NOT YET SUPPORTED
+ BUFFER_TYPE[BUFFER_TYPE["UNIFORM_BUFFER"] = 35345] = "UNIFORM_BUFFER";
+ })(BUFFER_TYPE$3 || (BUFFER_TYPE$3 = {}));
+
+ /**
+ * The BlurFilterPass applies a horizontal or vertical Gaussian blur to an object.
+ * @memberof PIXI.filters
+ */
+ var BlurFilterPass = /** @class */ (function (_super) {
+ __extends$6(BlurFilterPass, _super);
+ /**
+ * @param horizontal - Do pass along the x-axis (`true`) or y-axis (`false`).
+ * @param strength - The strength of the blur filter.
+ * @param quality - The quality of the blur filter.
+ * @param resolution - The resolution of the blur filter.
+ * @param kernelSize - The kernelSize of the blur filter.Options: 5, 7, 9, 11, 13, 15.
+ */
+ function BlurFilterPass(horizontal, strength, quality, resolution, kernelSize) {
+ if (strength === void 0) { strength = 8; }
+ if (quality === void 0) { quality = 4; }
+ if (resolution === void 0) { resolution = settings$1.FILTER_RESOLUTION; }
+ if (kernelSize === void 0) { kernelSize = 5; }
+ var _this = this;
+ var vertSrc = generateBlurVertSource(kernelSize, horizontal);
+ var fragSrc = generateBlurFragSource(kernelSize);
+ _this = _super.call(this,
+ // vertex shader
+ vertSrc,
+ // fragment shader
+ fragSrc) || this;
+ _this.horizontal = horizontal;
+ _this.resolution = resolution;
+ _this._quality = 0;
+ _this.quality = quality;
+ _this.blur = strength;
+ return _this;
+ }
+ /**
+ * Applies the filter.
+ * @param filterManager - The manager.
+ * @param input - The input target.
+ * @param output - The output target.
+ * @param clearMode - How to clear
+ */
+ BlurFilterPass.prototype.apply = function (filterManager, input, output, clearMode) {
+ if (output) {
+ if (this.horizontal) {
+ this.uniforms.strength = (1 / output.width) * (output.width / input.width);
+ }
+ else {
+ this.uniforms.strength = (1 / output.height) * (output.height / input.height);
+ }
+ }
+ else {
+ if (this.horizontal) // eslint-disable-line
+ {
+ this.uniforms.strength = (1 / filterManager.renderer.width) * (filterManager.renderer.width / input.width);
+ }
+ else {
+ this.uniforms.strength = (1 / filterManager.renderer.height) * (filterManager.renderer.height / input.height); // eslint-disable-line
+ }
+ }
+ // screen space!
+ this.uniforms.strength *= this.strength;
+ this.uniforms.strength /= this.passes;
+ if (this.passes === 1) {
+ filterManager.applyFilter(this, input, output, clearMode);
+ }
+ else {
+ var renderTarget = filterManager.getFilterTexture();
+ var renderer = filterManager.renderer;
+ var flip = input;
+ var flop = renderTarget;
+ this.state.blend = false;
+ filterManager.applyFilter(this, flip, flop, CLEAR_MODES$3.CLEAR);
+ for (var i = 1; i < this.passes - 1; i++) {
+ filterManager.bindAndClear(flip, CLEAR_MODES$3.BLIT);
+ this.uniforms.uSampler = flop;
+ var temp = flop;
+ flop = flip;
+ flip = temp;
+ renderer.shader.bind(this);
+ renderer.geometry.draw(5);
+ }
+ this.state.blend = true;
+ filterManager.applyFilter(this, flop, output, clearMode);
+ filterManager.returnFilterTexture(renderTarget);
+ }
+ };
+ Object.defineProperty(BlurFilterPass.prototype, "blur", {
+ /**
+ * Sets the strength of both the blur.
+ * @default 16
+ */
+ get: function () {
+ return this.strength;
+ },
+ set: function (value) {
+ this.padding = 1 + (Math.abs(value) * 2);
+ this.strength = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BlurFilterPass.prototype, "quality", {
+ /**
+ * Sets the quality of the blur by modifying the number of passes. More passes means higher
+ * quality bluring but the lower the performance.
+ * @default 4
+ */
+ get: function () {
+ return this._quality;
+ },
+ set: function (value) {
+ this._quality = value;
+ this.passes = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return BlurFilterPass;
+ }(Filter));
+
+ /**
+ * The BlurFilter applies a Gaussian blur to an object.
+ *
+ * The strength of the blur can be set for the x-axis and y-axis separately.
+ * @memberof PIXI.filters
+ */
+ var BlurFilter = /** @class */ (function (_super) {
+ __extends$6(BlurFilter, _super);
+ /**
+ * @param strength - The strength of the blur filter.
+ * @param quality - The quality of the blur filter.
+ * @param [resolution=PIXI.settings.FILTER_RESOLUTION] - The resolution of the blur filter.
+ * @param kernelSize - The kernelSize of the blur filter.Options: 5, 7, 9, 11, 13, 15.
+ */
+ function BlurFilter(strength, quality, resolution, kernelSize) {
+ if (strength === void 0) { strength = 8; }
+ if (quality === void 0) { quality = 4; }
+ if (resolution === void 0) { resolution = settings$1.FILTER_RESOLUTION; }
+ if (kernelSize === void 0) { kernelSize = 5; }
+ var _this = _super.call(this) || this;
+ _this.blurXFilter = new BlurFilterPass(true, strength, quality, resolution, kernelSize);
+ _this.blurYFilter = new BlurFilterPass(false, strength, quality, resolution, kernelSize);
+ _this.resolution = resolution;
+ _this.quality = quality;
+ _this.blur = strength;
+ _this.repeatEdgePixels = false;
+ return _this;
+ }
+ /**
+ * Applies the filter.
+ * @param filterManager - The manager.
+ * @param input - The input target.
+ * @param output - The output target.
+ * @param clearMode - How to clear
+ */
+ BlurFilter.prototype.apply = function (filterManager, input, output, clearMode) {
+ var xStrength = Math.abs(this.blurXFilter.strength);
+ var yStrength = Math.abs(this.blurYFilter.strength);
+ if (xStrength && yStrength) {
+ var renderTarget = filterManager.getFilterTexture();
+ this.blurXFilter.apply(filterManager, input, renderTarget, CLEAR_MODES$3.CLEAR);
+ this.blurYFilter.apply(filterManager, renderTarget, output, clearMode);
+ filterManager.returnFilterTexture(renderTarget);
+ }
+ else if (yStrength) {
+ this.blurYFilter.apply(filterManager, input, output, clearMode);
+ }
+ else {
+ this.blurXFilter.apply(filterManager, input, output, clearMode);
+ }
+ };
+ BlurFilter.prototype.updatePadding = function () {
+ if (this._repeatEdgePixels) {
+ this.padding = 0;
+ }
+ else {
+ this.padding = Math.max(Math.abs(this.blurXFilter.strength), Math.abs(this.blurYFilter.strength)) * 2;
+ }
+ };
+ Object.defineProperty(BlurFilter.prototype, "blur", {
+ /**
+ * Sets the strength of both the blurX and blurY properties simultaneously
+ * @default 2
+ */
+ get: function () {
+ return this.blurXFilter.blur;
+ },
+ set: function (value) {
+ this.blurXFilter.blur = this.blurYFilter.blur = value;
+ this.updatePadding();
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BlurFilter.prototype, "quality", {
+ /**
+ * Sets the number of passes for blur. More passes means higher quality bluring.
+ * @default 1
+ */
+ get: function () {
+ return this.blurXFilter.quality;
+ },
+ set: function (value) {
+ this.blurXFilter.quality = this.blurYFilter.quality = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BlurFilter.prototype, "blurX", {
+ /**
+ * Sets the strength of the blurX property
+ * @default 2
+ */
+ get: function () {
+ return this.blurXFilter.blur;
+ },
+ set: function (value) {
+ this.blurXFilter.blur = value;
+ this.updatePadding();
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BlurFilter.prototype, "blurY", {
+ /**
+ * Sets the strength of the blurY property
+ * @default 2
+ */
+ get: function () {
+ return this.blurYFilter.blur;
+ },
+ set: function (value) {
+ this.blurYFilter.blur = value;
+ this.updatePadding();
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BlurFilter.prototype, "blendMode", {
+ /**
+ * Sets the blendmode of the filter
+ * @default PIXI.BLEND_MODES.NORMAL
+ */
+ get: function () {
+ return this.blurYFilter.blendMode;
+ },
+ set: function (value) {
+ this.blurYFilter.blendMode = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BlurFilter.prototype, "repeatEdgePixels", {
+ /**
+ * If set to true the edge of the target will be clamped
+ * @default false
+ */
+ get: function () {
+ return this._repeatEdgePixels;
+ },
+ set: function (value) {
+ this._repeatEdgePixels = value;
+ this.updatePadding();
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return BlurFilter;
+ }(Filter));
+
+ /*!
+ * @pixi/filter-color-matrix - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/filter-color-matrix is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+ /* global Reflect, Promise */
+
+ var extendStatics$5 = function(d, b) {
+ extendStatics$5 = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
+ return extendStatics$5(d, b);
+ };
+
+ function __extends$5(d, b) {
+ extendStatics$5(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ }
+
+ var fragment$3 = "varying vec2 vTextureCoord;\nuniform sampler2D uSampler;\nuniform float m[20];\nuniform float uAlpha;\n\nvoid main(void)\n{\n vec4 c = texture2D(uSampler, vTextureCoord);\n\n if (uAlpha == 0.0) {\n gl_FragColor = c;\n return;\n }\n\n // Un-premultiply alpha before applying the color matrix. See issue #3539.\n if (c.a > 0.0) {\n c.rgb /= c.a;\n }\n\n vec4 result;\n\n result.r = (m[0] * c.r);\n result.r += (m[1] * c.g);\n result.r += (m[2] * c.b);\n result.r += (m[3] * c.a);\n result.r += m[4];\n\n result.g = (m[5] * c.r);\n result.g += (m[6] * c.g);\n result.g += (m[7] * c.b);\n result.g += (m[8] * c.a);\n result.g += m[9];\n\n result.b = (m[10] * c.r);\n result.b += (m[11] * c.g);\n result.b += (m[12] * c.b);\n result.b += (m[13] * c.a);\n result.b += m[14];\n\n result.a = (m[15] * c.r);\n result.a += (m[16] * c.g);\n result.a += (m[17] * c.b);\n result.a += (m[18] * c.a);\n result.a += m[19];\n\n vec3 rgb = mix(c.rgb, result.rgb, uAlpha);\n\n // Premultiply alpha again.\n rgb *= result.a;\n\n gl_FragColor = vec4(rgb, result.a);\n}\n";
+
+ /**
+ * The ColorMatrixFilter class lets you apply a 5x4 matrix transformation on the RGBA
+ * color and alpha values of every pixel on your displayObject to produce a result
+ * with a new set of RGBA color and alpha values. It's pretty powerful!
+ *
+ * ```js
+ * let colorMatrix = new PIXI.filters.ColorMatrixFilter();
+ * container.filters = [colorMatrix];
+ * colorMatrix.contrast(2);
+ * ```
+ * @author Cl茅ment Chenebault <clement@goodboydigital.com>
+ * @memberof PIXI.filters
+ */
+ var ColorMatrixFilter = /** @class */ (function (_super) {
+ __extends$5(ColorMatrixFilter, _super);
+ function ColorMatrixFilter() {
+ var _this = this;
+ var uniforms = {
+ m: new Float32Array([1, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0,
+ 0, 0, 1, 0, 0,
+ 0, 0, 0, 1, 0]),
+ uAlpha: 1,
+ };
+ _this = _super.call(this, defaultFilterVertex, fragment$3, uniforms) || this;
+ _this.alpha = 1;
+ return _this;
+ }
+ /**
+ * Transforms current matrix and set the new one
+ * @param {number[]} matrix - 5x4 matrix
+ * @param multiply - if true, current matrix and matrix are multiplied. If false,
+ * just set the current matrix with @param matrix
+ */
+ ColorMatrixFilter.prototype._loadMatrix = function (matrix, multiply) {
+ if (multiply === void 0) { multiply = false; }
+ var newMatrix = matrix;
+ if (multiply) {
+ this._multiply(newMatrix, this.uniforms.m, matrix);
+ newMatrix = this._colorMatrix(newMatrix);
+ }
+ // set the new matrix
+ this.uniforms.m = newMatrix;
+ };
+ /**
+ * Multiplies two mat5's
+ * @private
+ * @param out - 5x4 matrix the receiving matrix
+ * @param a - 5x4 matrix the first operand
+ * @param b - 5x4 matrix the second operand
+ * @returns {number[]} 5x4 matrix
+ */
+ ColorMatrixFilter.prototype._multiply = function (out, a, b) {
+ // Red Channel
+ out[0] = (a[0] * b[0]) + (a[1] * b[5]) + (a[2] * b[10]) + (a[3] * b[15]);
+ out[1] = (a[0] * b[1]) + (a[1] * b[6]) + (a[2] * b[11]) + (a[3] * b[16]);
+ out[2] = (a[0] * b[2]) + (a[1] * b[7]) + (a[2] * b[12]) + (a[3] * b[17]);
+ out[3] = (a[0] * b[3]) + (a[1] * b[8]) + (a[2] * b[13]) + (a[3] * b[18]);
+ out[4] = (a[0] * b[4]) + (a[1] * b[9]) + (a[2] * b[14]) + (a[3] * b[19]) + a[4];
+ // Green Channel
+ out[5] = (a[5] * b[0]) + (a[6] * b[5]) + (a[7] * b[10]) + (a[8] * b[15]);
+ out[6] = (a[5] * b[1]) + (a[6] * b[6]) + (a[7] * b[11]) + (a[8] * b[16]);
+ out[7] = (a[5] * b[2]) + (a[6] * b[7]) + (a[7] * b[12]) + (a[8] * b[17]);
+ out[8] = (a[5] * b[3]) + (a[6] * b[8]) + (a[7] * b[13]) + (a[8] * b[18]);
+ out[9] = (a[5] * b[4]) + (a[6] * b[9]) + (a[7] * b[14]) + (a[8] * b[19]) + a[9];
+ // Blue Channel
+ out[10] = (a[10] * b[0]) + (a[11] * b[5]) + (a[12] * b[10]) + (a[13] * b[15]);
+ out[11] = (a[10] * b[1]) + (a[11] * b[6]) + (a[12] * b[11]) + (a[13] * b[16]);
+ out[12] = (a[10] * b[2]) + (a[11] * b[7]) + (a[12] * b[12]) + (a[13] * b[17]);
+ out[13] = (a[10] * b[3]) + (a[11] * b[8]) + (a[12] * b[13]) + (a[13] * b[18]);
+ out[14] = (a[10] * b[4]) + (a[11] * b[9]) + (a[12] * b[14]) + (a[13] * b[19]) + a[14];
+ // Alpha Channel
+ out[15] = (a[15] * b[0]) + (a[16] * b[5]) + (a[17] * b[10]) + (a[18] * b[15]);
+ out[16] = (a[15] * b[1]) + (a[16] * b[6]) + (a[17] * b[11]) + (a[18] * b[16]);
+ out[17] = (a[15] * b[2]) + (a[16] * b[7]) + (a[17] * b[12]) + (a[18] * b[17]);
+ out[18] = (a[15] * b[3]) + (a[16] * b[8]) + (a[17] * b[13]) + (a[18] * b[18]);
+ out[19] = (a[15] * b[4]) + (a[16] * b[9]) + (a[17] * b[14]) + (a[18] * b[19]) + a[19];
+ return out;
+ };
+ /**
+ * Create a Float32 Array and normalize the offset component to 0-1
+ * @param {number[]} matrix - 5x4 matrix
+ * @returns {number[]} 5x4 matrix with all values between 0-1
+ */
+ ColorMatrixFilter.prototype._colorMatrix = function (matrix) {
+ // Create a Float32 Array and normalize the offset component to 0-1
+ var m = new Float32Array(matrix);
+ m[4] /= 255;
+ m[9] /= 255;
+ m[14] /= 255;
+ m[19] /= 255;
+ return m;
+ };
+ /**
+ * Adjusts brightness
+ * @param b - value of the brigthness (0-1, where 0 is black)
+ * @param multiply - if true, current matrix and matrix are multiplied. If false,
+ * just set the current matrix with @param matrix
+ */
+ ColorMatrixFilter.prototype.brightness = function (b, multiply) {
+ var matrix = [
+ b, 0, 0, 0, 0,
+ 0, b, 0, 0, 0,
+ 0, 0, b, 0, 0,
+ 0, 0, 0, 1, 0 ];
+ this._loadMatrix(matrix, multiply);
+ };
+ /**
+ * Sets each channel on the diagonal of the color matrix.
+ * This can be used to achieve a tinting effect on Containers similar to the tint field of some
+ * display objects like Sprite, Text, Graphics, and Mesh.
+ * @param color - Color of the tint. This is a hex value.
+ * @param multiply - if true, current matrix and matrix are multiplied. If false,
+ * just set the current matrix with @param matrix
+ */
+ ColorMatrixFilter.prototype.tint = function (color, multiply) {
+ var r = (color >> 16) & 0xff;
+ var g = (color >> 8) & 0xff;
+ var b = color & 0xff;
+ var matrix = [
+ r / 255, 0, 0, 0, 0,
+ 0, g / 255, 0, 0, 0,
+ 0, 0, b / 255, 0, 0,
+ 0, 0, 0, 1, 0 ];
+ this._loadMatrix(matrix, multiply);
+ };
+ /**
+ * Set the matrices in grey scales
+ * @param scale - value of the grey (0-1, where 0 is black)
+ * @param multiply - if true, current matrix and matrix are multiplied. If false,
+ * just set the current matrix with @param matrix
+ */
+ ColorMatrixFilter.prototype.greyscale = function (scale, multiply) {
+ var matrix = [
+ scale, scale, scale, 0, 0,
+ scale, scale, scale, 0, 0,
+ scale, scale, scale, 0, 0,
+ 0, 0, 0, 1, 0 ];
+ this._loadMatrix(matrix, multiply);
+ };
+ /**
+ * Set the black and white matrice.
+ * @param multiply - if true, current matrix and matrix are multiplied. If false,
+ * just set the current matrix with @param matrix
+ */
+ ColorMatrixFilter.prototype.blackAndWhite = function (multiply) {
+ var matrix = [
+ 0.3, 0.6, 0.1, 0, 0,
+ 0.3, 0.6, 0.1, 0, 0,
+ 0.3, 0.6, 0.1, 0, 0,
+ 0, 0, 0, 1, 0 ];
+ this._loadMatrix(matrix, multiply);
+ };
+ /**
+ * Set the hue property of the color
+ * @param rotation - in degrees
+ * @param multiply - if true, current matrix and matrix are multiplied. If false,
+ * just set the current matrix with @param matrix
+ */
+ ColorMatrixFilter.prototype.hue = function (rotation, multiply) {
+ rotation = (rotation || 0) / 180 * Math.PI;
+ var cosR = Math.cos(rotation);
+ var sinR = Math.sin(rotation);
+ var sqrt = Math.sqrt;
+ /* a good approximation for hue rotation
+ This matrix is far better than the versions with magic luminance constants
+ formerly used here, but also used in the starling framework (flash) and known from this
+ old part of the internet: quasimondo.com/archives/000565.php
+
+ This new matrix is based on rgb cube rotation in space. Look here for a more descriptive
+ implementation as a shader not a general matrix:
+ https://github.com/evanw/glfx.js/blob/58841c23919bd59787effc0333a4897b43835412/src/filters/adjust/huesaturation.js
+
+ This is the source for the code:
+ see http://stackoverflow.com/questions/8507885/shift-hue-of-an-rgb-color/8510751#8510751
+ */
+ var w = 1 / 3;
+ var sqrW = sqrt(w); // weight is
+ var a00 = cosR + ((1.0 - cosR) * w);
+ var a01 = (w * (1.0 - cosR)) - (sqrW * sinR);
+ var a02 = (w * (1.0 - cosR)) + (sqrW * sinR);
+ var a10 = (w * (1.0 - cosR)) + (sqrW * sinR);
+ var a11 = cosR + (w * (1.0 - cosR));
+ var a12 = (w * (1.0 - cosR)) - (sqrW * sinR);
+ var a20 = (w * (1.0 - cosR)) - (sqrW * sinR);
+ var a21 = (w * (1.0 - cosR)) + (sqrW * sinR);
+ var a22 = cosR + (w * (1.0 - cosR));
+ var matrix = [
+ a00, a01, a02, 0, 0,
+ a10, a11, a12, 0, 0,
+ a20, a21, a22, 0, 0,
+ 0, 0, 0, 1, 0 ];
+ this._loadMatrix(matrix, multiply);
+ };
+ /**
+ * Set the contrast matrix, increase the separation between dark and bright
+ * Increase contrast : shadows darker and highlights brighter
+ * Decrease contrast : bring the shadows up and the highlights down
+ * @param amount - value of the contrast (0-1)
+ * @param multiply - if true, current matrix and matrix are multiplied. If false,
+ * just set the current matrix with @param matrix
+ */
+ ColorMatrixFilter.prototype.contrast = function (amount, multiply) {
+ var v = (amount || 0) + 1;
+ var o = -0.5 * (v - 1);
+ var matrix = [
+ v, 0, 0, 0, o,
+ 0, v, 0, 0, o,
+ 0, 0, v, 0, o,
+ 0, 0, 0, 1, 0 ];
+ this._loadMatrix(matrix, multiply);
+ };
+ /**
+ * Set the saturation matrix, increase the separation between colors
+ * Increase saturation : increase contrast, brightness, and sharpness
+ * @param amount - The saturation amount (0-1)
+ * @param multiply - if true, current matrix and matrix are multiplied. If false,
+ * just set the current matrix with @param matrix
+ */
+ ColorMatrixFilter.prototype.saturate = function (amount, multiply) {
+ if (amount === void 0) { amount = 0; }
+ var x = (amount * 2 / 3) + 1;
+ var y = ((x - 1) * -0.5);
+ var matrix = [
+ x, y, y, 0, 0,
+ y, x, y, 0, 0,
+ y, y, x, 0, 0,
+ 0, 0, 0, 1, 0 ];
+ this._loadMatrix(matrix, multiply);
+ };
+ /** Desaturate image (remove color) Call the saturate function */
+ ColorMatrixFilter.prototype.desaturate = function () {
+ this.saturate(-1);
+ };
+ /**
+ * Negative image (inverse of classic rgb matrix)
+ * @param multiply - if true, current matrix and matrix are multiplied. If false,
+ * just set the current matrix with @param matrix
+ */
+ ColorMatrixFilter.prototype.negative = function (multiply) {
+ var matrix = [
+ -1, 0, 0, 1, 0,
+ 0, -1, 0, 1, 0,
+ 0, 0, -1, 1, 0,
+ 0, 0, 0, 1, 0 ];
+ this._loadMatrix(matrix, multiply);
+ };
+ /**
+ * Sepia image
+ * @param multiply - if true, current matrix and matrix are multiplied. If false,
+ * just set the current matrix with @param matrix
+ */
+ ColorMatrixFilter.prototype.sepia = function (multiply) {
+ var matrix = [
+ 0.393, 0.7689999, 0.18899999, 0, 0,
+ 0.349, 0.6859999, 0.16799999, 0, 0,
+ 0.272, 0.5339999, 0.13099999, 0, 0,
+ 0, 0, 0, 1, 0 ];
+ this._loadMatrix(matrix, multiply);
+ };
+ /**
+ * Color motion picture process invented in 1916 (thanks Dominic Szablewski)
+ * @param multiply - if true, current matrix and matrix are multiplied. If false,
+ * just set the current matrix with @param matrix
+ */
+ ColorMatrixFilter.prototype.technicolor = function (multiply) {
+ var matrix = [
+ 1.9125277891456083, -0.8545344976951645, -0.09155508482755585, 0, 11.793603434377337,
+ -0.3087833385928097, 1.7658908555458428, -0.10601743074722245, 0, -70.35205161461398,
+ -0.231103377548616, -0.7501899197440212, 1.847597816108189, 0, 30.950940869491138,
+ 0, 0, 0, 1, 0 ];
+ this._loadMatrix(matrix, multiply);
+ };
+ /**
+ * Polaroid filter
+ * @param multiply - if true, current matrix and matrix are multiplied. If false,
+ * just set the current matrix with @param matrix
+ */
+ ColorMatrixFilter.prototype.polaroid = function (multiply) {
+ var matrix = [
+ 1.438, -0.062, -0.062, 0, 0,
+ -0.122, 1.378, -0.122, 0, 0,
+ -0.016, -0.016, 1.483, 0, 0,
+ 0, 0, 0, 1, 0 ];
+ this._loadMatrix(matrix, multiply);
+ };
+ /**
+ * Filter who transforms : Red -> Blue and Blue -> Red
+ * @param multiply - if true, current matrix and matrix are multiplied. If false,
+ * just set the current matrix with @param matrix
+ */
+ ColorMatrixFilter.prototype.toBGR = function (multiply) {
+ var matrix = [
+ 0, 0, 1, 0, 0,
+ 0, 1, 0, 0, 0,
+ 1, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0 ];
+ this._loadMatrix(matrix, multiply);
+ };
+ /**
+ * Color reversal film introduced by Eastman Kodak in 1935. (thanks Dominic Szablewski)
+ * @param multiply - if true, current matrix and matrix are multiplied. If false,
+ * just set the current matrix with @param matrix
+ */
+ ColorMatrixFilter.prototype.kodachrome = function (multiply) {
+ var matrix = [
+ 1.1285582396593525, -0.3967382283601348, -0.03992559172921793, 0, 63.72958762196502,
+ -0.16404339962244616, 1.0835251566291304, -0.05498805115633132, 0, 24.732407896706203,
+ -0.16786010706155763, -0.5603416277695248, 1.6014850761964943, 0, 35.62982807460946,
+ 0, 0, 0, 1, 0 ];
+ this._loadMatrix(matrix, multiply);
+ };
+ /**
+ * Brown delicious browni filter (thanks Dominic Szablewski)
+ * @param multiply - if true, current matrix and matrix are multiplied. If false,
+ * just set the current matrix with @param matrix
+ */
+ ColorMatrixFilter.prototype.browni = function (multiply) {
+ var matrix = [
+ 0.5997023498159715, 0.34553243048391263, -0.2708298674538042, 0, 47.43192855600873,
+ -0.037703249837783157, 0.8609577587992641, 0.15059552388459913, 0, -36.96841498319127,
+ 0.24113635128153335, -0.07441037908422492, 0.44972182064877153, 0, -7.562075277591283,
+ 0, 0, 0, 1, 0 ];
+ this._loadMatrix(matrix, multiply);
+ };
+ /**
+ * Vintage filter (thanks Dominic Szablewski)
+ * @param multiply - if true, current matrix and matrix are multiplied. If false,
+ * just set the current matrix with @param matrix
+ */
+ ColorMatrixFilter.prototype.vintage = function (multiply) {
+ var matrix = [
+ 0.6279345635605994, 0.3202183420819367, -0.03965408211312453, 0, 9.651285835294123,
+ 0.02578397704808868, 0.6441188644374771, 0.03259127616149294, 0, 7.462829176470591,
+ 0.0466055556782719, -0.0851232987247891, 0.5241648018700465, 0, 5.159190588235296,
+ 0, 0, 0, 1, 0 ];
+ this._loadMatrix(matrix, multiply);
+ };
+ /**
+ * We don't know exactly what it does, kind of gradient map, but funny to play with!
+ * @param desaturation - Tone values.
+ * @param toned - Tone values.
+ * @param lightColor - Tone values, example: `0xFFE580`
+ * @param darkColor - Tone values, example: `0xFFE580`
+ * @param multiply - if true, current matrix and matrix are multiplied. If false,
+ * just set the current matrix with @param matrix
+ */
+ ColorMatrixFilter.prototype.colorTone = function (desaturation, toned, lightColor, darkColor, multiply) {
+ desaturation = desaturation || 0.2;
+ toned = toned || 0.15;
+ lightColor = lightColor || 0xFFE580;
+ darkColor = darkColor || 0x338000;
+ var lR = ((lightColor >> 16) & 0xFF) / 255;
+ var lG = ((lightColor >> 8) & 0xFF) / 255;
+ var lB = (lightColor & 0xFF) / 255;
+ var dR = ((darkColor >> 16) & 0xFF) / 255;
+ var dG = ((darkColor >> 8) & 0xFF) / 255;
+ var dB = (darkColor & 0xFF) / 255;
+ var matrix = [
+ 0.3, 0.59, 0.11, 0, 0,
+ lR, lG, lB, desaturation, 0,
+ dR, dG, dB, toned, 0,
+ lR - dR, lG - dG, lB - dB, 0, 0 ];
+ this._loadMatrix(matrix, multiply);
+ };
+ /**
+ * Night effect
+ * @param intensity - The intensity of the night effect.
+ * @param multiply - if true, current matrix and matrix are multiplied. If false,
+ * just set the current matrix with @param matrix
+ */
+ ColorMatrixFilter.prototype.night = function (intensity, multiply) {
+ intensity = intensity || 0.1;
+ var matrix = [
+ intensity * (-2.0), -intensity, 0, 0, 0,
+ -intensity, 0, intensity, 0, 0,
+ 0, intensity, intensity * 2.0, 0, 0,
+ 0, 0, 0, 1, 0 ];
+ this._loadMatrix(matrix, multiply);
+ };
+ /**
+ * Predator effect
+ *
+ * Erase the current matrix by setting a new indepent one
+ * @param amount - how much the predator feels his future victim
+ * @param multiply - if true, current matrix and matrix are multiplied. If false,
+ * just set the current matrix with @param matrix
+ */
+ ColorMatrixFilter.prototype.predator = function (amount, multiply) {
+ var matrix = [
+ // row 1
+ 11.224130630493164 * amount,
+ -4.794486999511719 * amount,
+ -2.8746118545532227 * amount,
+ 0 * amount,
+ 0.40342438220977783 * amount,
+ // row 2
+ -3.6330697536468506 * amount,
+ 9.193157196044922 * amount,
+ -2.951810836791992 * amount,
+ 0 * amount,
+ -1.316135048866272 * amount,
+ // row 3
+ -3.2184197902679443 * amount,
+ -4.2375030517578125 * amount,
+ 7.476448059082031 * amount,
+ 0 * amount,
+ 0.8044459223747253 * amount,
+ // row 4
+ 0, 0, 0, 1, 0 ];
+ this._loadMatrix(matrix, multiply);
+ };
+ /**
+ * LSD effect
+ *
+ * Multiply the current matrix
+ * @param multiply - if true, current matrix and matrix are multiplied. If false,
+ * just set the current matrix with @param matrix
+ */
+ ColorMatrixFilter.prototype.lsd = function (multiply) {
+ var matrix = [
+ 2, -0.4, 0.5, 0, 0,
+ -0.5, 2, -0.4, 0, 0,
+ -0.4, -0.5, 3, 0, 0,
+ 0, 0, 0, 1, 0 ];
+ this._loadMatrix(matrix, multiply);
+ };
+ /** Erase the current matrix by setting the default one. */
+ ColorMatrixFilter.prototype.reset = function () {
+ var matrix = [
+ 1, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0,
+ 0, 0, 1, 0, 0,
+ 0, 0, 0, 1, 0 ];
+ this._loadMatrix(matrix, false);
+ };
+ Object.defineProperty(ColorMatrixFilter.prototype, "matrix", {
+ /**
+ * The matrix of the color matrix filter
+ * @member {number[]}
+ * @default [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0]
+ */
+ get: function () {
+ return this.uniforms.m;
+ },
+ set: function (value) {
+ this.uniforms.m = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(ColorMatrixFilter.prototype, "alpha", {
+ /**
+ * The opacity value to use when mixing the original and resultant colors.
+ *
+ * When the value is 0, the original color is used without modification.
+ * When the value is 1, the result color is used.
+ * When in the range (0, 1) the color is interpolated between the original and result by this amount.
+ * @default 1
+ */
+ get: function () {
+ return this.uniforms.uAlpha;
+ },
+ set: function (value) {
+ this.uniforms.uAlpha = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return ColorMatrixFilter;
+ }(Filter));
+ // Americanized alias
+ ColorMatrixFilter.prototype.grayscale = ColorMatrixFilter.prototype.greyscale;
+
+ /*!
+ * @pixi/filter-displacement - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/filter-displacement is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+ /* global Reflect, Promise */
+
+ var extendStatics$4 = function(d, b) {
+ extendStatics$4 = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
+ return extendStatics$4(d, b);
+ };
+
+ function __extends$4(d, b) {
+ extendStatics$4(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ }
+
+ var fragment$2 = "varying vec2 vFilterCoord;\nvarying vec2 vTextureCoord;\n\nuniform vec2 scale;\nuniform mat2 rotation;\nuniform sampler2D uSampler;\nuniform sampler2D mapSampler;\n\nuniform highp vec4 inputSize;\nuniform vec4 inputClamp;\n\nvoid main(void)\n{\n vec4 map = texture2D(mapSampler, vFilterCoord);\n\n map -= 0.5;\n map.xy = scale * inputSize.zw * (rotation * map.xy);\n\n gl_FragColor = texture2D(uSampler, clamp(vec2(vTextureCoord.x + map.x, vTextureCoord.y + map.y), inputClamp.xy, inputClamp.zw));\n}\n";
+
+ var vertex$1 = "attribute vec2 aVertexPosition;\n\nuniform mat3 projectionMatrix;\nuniform mat3 filterMatrix;\n\nvarying vec2 vTextureCoord;\nvarying vec2 vFilterCoord;\n\nuniform vec4 inputSize;\nuniform vec4 outputFrame;\n\nvec4 filterVertexPosition( void )\n{\n vec2 position = aVertexPosition * max(outputFrame.zw, vec2(0.)) + outputFrame.xy;\n\n return vec4((projectionMatrix * vec3(position, 1.0)).xy, 0.0, 1.0);\n}\n\nvec2 filterTextureCoord( void )\n{\n return aVertexPosition * (outputFrame.zw * inputSize.zw);\n}\n\nvoid main(void)\n{\n\tgl_Position = filterVertexPosition();\n\tvTextureCoord = filterTextureCoord();\n\tvFilterCoord = ( filterMatrix * vec3( vTextureCoord, 1.0) ).xy;\n}\n";
+
+ /**
+ * The DisplacementFilter class uses the pixel values from the specified texture
+ * (called the displacement map) to perform a displacement of an object.
+ *
+ * You can use this filter to apply all manor of crazy warping effects.
+ * Currently the `r` property of the texture is used to offset the `x`
+ * and the `g` property of the texture is used to offset the `y`.
+ *
+ * The way it works is it uses the values of the displacement map to look up the
+ * correct pixels to output. This means it's not technically moving the original.
+ * Instead, it's starting at the output and asking "which pixel from the original goes here".
+ * For example, if a displacement map pixel has `red = 1` and the filter scale is `20`,
+ * this filter will output the pixel approximately 20 pixels to the right of the original.
+ * @memberof PIXI.filters
+ */
+ var DisplacementFilter = /** @class */ (function (_super) {
+ __extends$4(DisplacementFilter, _super);
+ /**
+ * @param {PIXI.Sprite} sprite - The sprite used for the displacement map. (make sure its added to the scene!)
+ * @param scale - The scale of the displacement
+ */
+ function DisplacementFilter(sprite, scale) {
+ var _this = this;
+ var maskMatrix = new Matrix();
+ sprite.renderable = false;
+ _this = _super.call(this, vertex$1, fragment$2, {
+ mapSampler: sprite._texture,
+ filterMatrix: maskMatrix,
+ scale: { x: 1, y: 1 },
+ rotation: new Float32Array([1, 0, 0, 1]),
+ }) || this;
+ _this.maskSprite = sprite;
+ _this.maskMatrix = maskMatrix;
+ if (scale === null || scale === undefined) {
+ scale = 20;
+ }
+ /**
+ * scaleX, scaleY for displacements
+ * @member {PIXI.Point}
+ */
+ _this.scale = new Point(scale, scale);
+ return _this;
+ }
+ /**
+ * Applies the filter.
+ * @param filterManager - The manager.
+ * @param input - The input target.
+ * @param output - The output target.
+ * @param clearMode - clearMode.
+ */
+ DisplacementFilter.prototype.apply = function (filterManager, input, output, clearMode) {
+ // fill maskMatrix with _normalized sprite texture coords_
+ this.uniforms.filterMatrix = filterManager.calculateSpriteMatrix(this.maskMatrix, this.maskSprite);
+ this.uniforms.scale.x = this.scale.x;
+ this.uniforms.scale.y = this.scale.y;
+ // Extract rotation from world transform
+ var wt = this.maskSprite.worldTransform;
+ var lenX = Math.sqrt((wt.a * wt.a) + (wt.b * wt.b));
+ var lenY = Math.sqrt((wt.c * wt.c) + (wt.d * wt.d));
+ if (lenX !== 0 && lenY !== 0) {
+ this.uniforms.rotation[0] = wt.a / lenX;
+ this.uniforms.rotation[1] = wt.b / lenX;
+ this.uniforms.rotation[2] = wt.c / lenY;
+ this.uniforms.rotation[3] = wt.d / lenY;
+ }
+ // draw the filter...
+ filterManager.applyFilter(this, input, output, clearMode);
+ };
+ Object.defineProperty(DisplacementFilter.prototype, "map", {
+ /** The texture used for the displacement map. Must be power of 2 sized texture. */
+ get: function () {
+ return this.uniforms.mapSampler;
+ },
+ set: function (value) {
+ this.uniforms.mapSampler = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return DisplacementFilter;
+ }(Filter));
+
+ /*!
+ * @pixi/filter-fxaa - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/filter-fxaa is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+ /* global Reflect, Promise */
+
+ var extendStatics$3 = function(d, b) {
+ extendStatics$3 = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
+ return extendStatics$3(d, b);
+ };
+
+ function __extends$3(d, b) {
+ extendStatics$3(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ }
+
+ var vertex = "\nattribute vec2 aVertexPosition;\n\nuniform mat3 projectionMatrix;\n\nvarying vec2 v_rgbNW;\nvarying vec2 v_rgbNE;\nvarying vec2 v_rgbSW;\nvarying vec2 v_rgbSE;\nvarying vec2 v_rgbM;\n\nvarying vec2 vFragCoord;\n\nuniform vec4 inputSize;\nuniform vec4 outputFrame;\n\nvec4 filterVertexPosition( void )\n{\n vec2 position = aVertexPosition * max(outputFrame.zw, vec2(0.)) + outputFrame.xy;\n\n return vec4((projectionMatrix * vec3(position, 1.0)).xy, 0.0, 1.0);\n}\n\nvoid texcoords(vec2 fragCoord, vec2 inverseVP,\n out vec2 v_rgbNW, out vec2 v_rgbNE,\n out vec2 v_rgbSW, out vec2 v_rgbSE,\n out vec2 v_rgbM) {\n v_rgbNW = (fragCoord + vec2(-1.0, -1.0)) * inverseVP;\n v_rgbNE = (fragCoord + vec2(1.0, -1.0)) * inverseVP;\n v_rgbSW = (fragCoord + vec2(-1.0, 1.0)) * inverseVP;\n v_rgbSE = (fragCoord + vec2(1.0, 1.0)) * inverseVP;\n v_rgbM = vec2(fragCoord * inverseVP);\n}\n\nvoid main(void) {\n\n gl_Position = filterVertexPosition();\n\n vFragCoord = aVertexPosition * outputFrame.zw;\n\n texcoords(vFragCoord, inputSize.zw, v_rgbNW, v_rgbNE, v_rgbSW, v_rgbSE, v_rgbM);\n}\n";
+
+ var fragment$1 = "varying vec2 v_rgbNW;\nvarying vec2 v_rgbNE;\nvarying vec2 v_rgbSW;\nvarying vec2 v_rgbSE;\nvarying vec2 v_rgbM;\n\nvarying vec2 vFragCoord;\nuniform sampler2D uSampler;\nuniform highp vec4 inputSize;\n\n\n/**\n Basic FXAA implementation based on the code on geeks3d.com with the\n modification that the texture2DLod stuff was removed since it's\n unsupported by WebGL.\n\n --\n\n From:\n https://github.com/mitsuhiko/webgl-meincraft\n\n Copyright (c) 2011 by Armin Ronacher.\n\n Some rights reserved.\n\n Redistribution and use in source and binary forms, with or without\n modification, are permitted provided that the following conditions are\n met:\n\n * Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n * Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the following\n disclaimer in the documentation and/or other materials provided\n with the distribution.\n\n * The names of the contributors may not be used to endorse or\n promote products derived from this software without specific\n prior written permission.\n\n THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef FXAA_REDUCE_MIN\n#define FXAA_REDUCE_MIN (1.0/ 128.0)\n#endif\n#ifndef FXAA_REDUCE_MUL\n#define FXAA_REDUCE_MUL (1.0 / 8.0)\n#endif\n#ifndef FXAA_SPAN_MAX\n#define FXAA_SPAN_MAX 8.0\n#endif\n\n//optimized version for mobile, where dependent\n//texture reads can be a bottleneck\nvec4 fxaa(sampler2D tex, vec2 fragCoord, vec2 inverseVP,\n vec2 v_rgbNW, vec2 v_rgbNE,\n vec2 v_rgbSW, vec2 v_rgbSE,\n vec2 v_rgbM) {\n vec4 color;\n vec3 rgbNW = texture2D(tex, v_rgbNW).xyz;\n vec3 rgbNE = texture2D(tex, v_rgbNE).xyz;\n vec3 rgbSW = texture2D(tex, v_rgbSW).xyz;\n vec3 rgbSE = texture2D(tex, v_rgbSE).xyz;\n vec4 texColor = texture2D(tex, v_rgbM);\n vec3 rgbM = texColor.xyz;\n vec3 luma = vec3(0.299, 0.587, 0.114);\n float lumaNW = dot(rgbNW, luma);\n float lumaNE = dot(rgbNE, luma);\n float lumaSW = dot(rgbSW, luma);\n float lumaSE = dot(rgbSE, luma);\n float lumaM = dot(rgbM, luma);\n float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\n mediump vec2 dir;\n dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\n float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) *\n (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\n float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX),\n max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),\n dir * rcpDirMin)) * inverseVP;\n\n vec3 rgbA = 0.5 * (\n texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz +\n texture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n vec3 rgbB = rgbA * 0.5 + 0.25 * (\n texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz +\n texture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\n float lumaB = dot(rgbB, luma);\n if ((lumaB < lumaMin) || (lumaB > lumaMax))\n color = vec4(rgbA, texColor.a);\n else\n color = vec4(rgbB, texColor.a);\n return color;\n}\n\nvoid main() {\n\n vec4 color;\n\n color = fxaa(uSampler, vFragCoord, inputSize.zw, v_rgbNW, v_rgbNE, v_rgbSW, v_rgbSE, v_rgbM);\n\n gl_FragColor = color;\n}\n";
+
+ /**
+ * Basic FXAA (Fast Approximate Anti-Aliasing) implementation based on the code on geeks3d.com
+ * with the modification that the texture2DLod stuff was removed since it is unsupported by WebGL.
+ * @see https://github.com/mitsuhiko/webgl-meincraft
+ * @memberof PIXI.filters
+ */
+ var FXAAFilter = /** @class */ (function (_super) {
+ __extends$3(FXAAFilter, _super);
+ function FXAAFilter() {
+ // TODO - needs work
+ return _super.call(this, vertex, fragment$1) || this;
+ }
+ return FXAAFilter;
+ }(Filter));
+
+ /*!
+ * @pixi/filter-noise - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/filter-noise is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+ /* global Reflect, Promise */
+
+ var extendStatics$2 = function(d, b) {
+ extendStatics$2 = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
+ return extendStatics$2(d, b);
+ };
+
+ function __extends$2(d, b) {
+ extendStatics$2(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ }
+
+ var fragment = "precision highp float;\n\nvarying vec2 vTextureCoord;\nvarying vec4 vColor;\n\nuniform float uNoise;\nuniform float uSeed;\nuniform sampler2D uSampler;\n\nfloat rand(vec2 co)\n{\n return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453);\n}\n\nvoid main()\n{\n vec4 color = texture2D(uSampler, vTextureCoord);\n float randomValue = rand(gl_FragCoord.xy * uSeed);\n float diff = (randomValue - 0.5) * uNoise;\n\n // Un-premultiply alpha before applying the color matrix. See issue #3539.\n if (color.a > 0.0) {\n color.rgb /= color.a;\n }\n\n color.r += diff;\n color.g += diff;\n color.b += diff;\n\n // Premultiply alpha again.\n color.rgb *= color.a;\n\n gl_FragColor = color;\n}\n";
+
+ /**
+ * A Noise effect filter.
+ *
+ * original filter: https://github.com/evanw/glfx.js/blob/master/src/filters/adjust/noise.js
+ * @memberof PIXI.filters
+ * @author Vico @vicocotea
+ */
+ var NoiseFilter = /** @class */ (function (_super) {
+ __extends$2(NoiseFilter, _super);
+ /**
+ * @param {number} [noise=0.5] - The noise intensity, should be a normalized value in the range [0, 1].
+ * @param {number} [seed] - A random seed for the noise generation. Default is `Math.random()`.
+ */
+ function NoiseFilter(noise, seed) {
+ if (noise === void 0) { noise = 0.5; }
+ if (seed === void 0) { seed = Math.random(); }
+ var _this = _super.call(this, defaultFilterVertex, fragment, {
+ uNoise: 0,
+ uSeed: 0,
+ }) || this;
+ _this.noise = noise;
+ _this.seed = seed;
+ return _this;
+ }
+ Object.defineProperty(NoiseFilter.prototype, "noise", {
+ /**
+ * The amount of noise to apply, this value should be in the range (0, 1].
+ * @default 0.5
+ */
+ get: function () {
+ return this.uniforms.uNoise;
+ },
+ set: function (value) {
+ this.uniforms.uNoise = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(NoiseFilter.prototype, "seed", {
+ /** A seed value to apply to the random noise generation. `Math.random()` is a good value to use. */
+ get: function () {
+ return this.uniforms.uSeed;
+ },
+ set: function (value) {
+ this.uniforms.uSeed = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return NoiseFilter;
+ }(Filter));
+
+ /*!
+ * @pixi/mixin-cache-as-bitmap - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/mixin-cache-as-bitmap is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /*!
+ * @pixi/constants - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/constants is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+ /**
+ * Different types of environments for WebGL.
+ * @static
+ * @memberof PIXI
+ * @name ENV
+ * @enum {number}
+ * @property {number} WEBGL_LEGACY - Used for older v1 WebGL devices. PixiJS will aim to ensure compatibility
+ * with older / less advanced devices. If you experience unexplained flickering prefer this environment.
+ * @property {number} WEBGL - Version 1 of WebGL
+ * @property {number} WEBGL2 - Version 2 of WebGL
+ */
+ var ENV$2;
+ (function (ENV) {
+ ENV[ENV["WEBGL_LEGACY"] = 0] = "WEBGL_LEGACY";
+ ENV[ENV["WEBGL"] = 1] = "WEBGL";
+ ENV[ENV["WEBGL2"] = 2] = "WEBGL2";
+ })(ENV$2 || (ENV$2 = {}));
+ /**
+ * Constant to identify the Renderer Type.
+ * @static
+ * @memberof PIXI
+ * @name RENDERER_TYPE
+ * @enum {number}
+ * @property {number} UNKNOWN - Unknown render type.
+ * @property {number} WEBGL - WebGL render type.
+ * @property {number} CANVAS - Canvas render type.
+ */
+ var RENDERER_TYPE$2;
+ (function (RENDERER_TYPE) {
+ RENDERER_TYPE[RENDERER_TYPE["UNKNOWN"] = 0] = "UNKNOWN";
+ RENDERER_TYPE[RENDERER_TYPE["WEBGL"] = 1] = "WEBGL";
+ RENDERER_TYPE[RENDERER_TYPE["CANVAS"] = 2] = "CANVAS";
+ })(RENDERER_TYPE$2 || (RENDERER_TYPE$2 = {}));
+ /**
+ * Bitwise OR of masks that indicate the buffers to be cleared.
+ * @static
+ * @memberof PIXI
+ * @name BUFFER_BITS
+ * @enum {number}
+ * @property {number} COLOR - Indicates the buffers currently enabled for color writing.
+ * @property {number} DEPTH - Indicates the depth buffer.
+ * @property {number} STENCIL - Indicates the stencil buffer.
+ */
+ var BUFFER_BITS$2;
+ (function (BUFFER_BITS) {
+ BUFFER_BITS[BUFFER_BITS["COLOR"] = 16384] = "COLOR";
+ BUFFER_BITS[BUFFER_BITS["DEPTH"] = 256] = "DEPTH";
+ BUFFER_BITS[BUFFER_BITS["STENCIL"] = 1024] = "STENCIL";
+ })(BUFFER_BITS$2 || (BUFFER_BITS$2 = {}));
+ /**
+ * Various blend modes supported by PIXI.
+ *
+ * IMPORTANT - The WebGL renderer only supports the NORMAL, ADD, MULTIPLY and SCREEN blend modes.
+ * Anything else will silently act like NORMAL.
+ * @memberof PIXI
+ * @name BLEND_MODES
+ * @enum {number}
+ * @property {number} NORMAL -
+ * @property {number} ADD -
+ * @property {number} MULTIPLY -
+ * @property {number} SCREEN -
+ * @property {number} OVERLAY -
+ * @property {number} DARKEN -
+ * @property {number} LIGHTEN -
+ * @property {number} COLOR_DODGE -
+ * @property {number} COLOR_BURN -
+ * @property {number} HARD_LIGHT -
+ * @property {number} SOFT_LIGHT -
+ * @property {number} DIFFERENCE -
+ * @property {number} EXCLUSION -
+ * @property {number} HUE -
+ * @property {number} SATURATION -
+ * @property {number} COLOR -
+ * @property {number} LUMINOSITY -
+ * @property {number} NORMAL_NPM -
+ * @property {number} ADD_NPM -
+ * @property {number} SCREEN_NPM -
+ * @property {number} NONE -
+ * @property {number} SRC_IN -
+ * @property {number} SRC_OUT -
+ * @property {number} SRC_ATOP -
+ * @property {number} DST_OVER -
+ * @property {number} DST_IN -
+ * @property {number} DST_OUT -
+ * @property {number} DST_ATOP -
+ * @property {number} SUBTRACT -
+ * @property {number} SRC_OVER -
+ * @property {number} ERASE -
+ * @property {number} XOR -
+ */
+ var BLEND_MODES$2;
+ (function (BLEND_MODES) {
+ BLEND_MODES[BLEND_MODES["NORMAL"] = 0] = "NORMAL";
+ BLEND_MODES[BLEND_MODES["ADD"] = 1] = "ADD";
+ BLEND_MODES[BLEND_MODES["MULTIPLY"] = 2] = "MULTIPLY";
+ BLEND_MODES[BLEND_MODES["SCREEN"] = 3] = "SCREEN";
+ BLEND_MODES[BLEND_MODES["OVERLAY"] = 4] = "OVERLAY";
+ BLEND_MODES[BLEND_MODES["DARKEN"] = 5] = "DARKEN";
+ BLEND_MODES[BLEND_MODES["LIGHTEN"] = 6] = "LIGHTEN";
+ BLEND_MODES[BLEND_MODES["COLOR_DODGE"] = 7] = "COLOR_DODGE";
+ BLEND_MODES[BLEND_MODES["COLOR_BURN"] = 8] = "COLOR_BURN";
+ BLEND_MODES[BLEND_MODES["HARD_LIGHT"] = 9] = "HARD_LIGHT";
+ BLEND_MODES[BLEND_MODES["SOFT_LIGHT"] = 10] = "SOFT_LIGHT";
+ BLEND_MODES[BLEND_MODES["DIFFERENCE"] = 11] = "DIFFERENCE";
+ BLEND_MODES[BLEND_MODES["EXCLUSION"] = 12] = "EXCLUSION";
+ BLEND_MODES[BLEND_MODES["HUE"] = 13] = "HUE";
+ BLEND_MODES[BLEND_MODES["SATURATION"] = 14] = "SATURATION";
+ BLEND_MODES[BLEND_MODES["COLOR"] = 15] = "COLOR";
+ BLEND_MODES[BLEND_MODES["LUMINOSITY"] = 16] = "LUMINOSITY";
+ BLEND_MODES[BLEND_MODES["NORMAL_NPM"] = 17] = "NORMAL_NPM";
+ BLEND_MODES[BLEND_MODES["ADD_NPM"] = 18] = "ADD_NPM";
+ BLEND_MODES[BLEND_MODES["SCREEN_NPM"] = 19] = "SCREEN_NPM";
+ BLEND_MODES[BLEND_MODES["NONE"] = 20] = "NONE";
+ BLEND_MODES[BLEND_MODES["SRC_OVER"] = 0] = "SRC_OVER";
+ BLEND_MODES[BLEND_MODES["SRC_IN"] = 21] = "SRC_IN";
+ BLEND_MODES[BLEND_MODES["SRC_OUT"] = 22] = "SRC_OUT";
+ BLEND_MODES[BLEND_MODES["SRC_ATOP"] = 23] = "SRC_ATOP";
+ BLEND_MODES[BLEND_MODES["DST_OVER"] = 24] = "DST_OVER";
+ BLEND_MODES[BLEND_MODES["DST_IN"] = 25] = "DST_IN";
+ BLEND_MODES[BLEND_MODES["DST_OUT"] = 26] = "DST_OUT";
+ BLEND_MODES[BLEND_MODES["DST_ATOP"] = 27] = "DST_ATOP";
+ BLEND_MODES[BLEND_MODES["ERASE"] = 26] = "ERASE";
+ BLEND_MODES[BLEND_MODES["SUBTRACT"] = 28] = "SUBTRACT";
+ BLEND_MODES[BLEND_MODES["XOR"] = 29] = "XOR";
+ })(BLEND_MODES$2 || (BLEND_MODES$2 = {}));
+ /**
+ * Various webgl draw modes. These can be used to specify which GL drawMode to use
+ * under certain situations and renderers.
+ * @memberof PIXI
+ * @static
+ * @name DRAW_MODES
+ * @enum {number}
+ * @property {number} POINTS -
+ * @property {number} LINES -
+ * @property {number} LINE_LOOP -
+ * @property {number} LINE_STRIP -
+ * @property {number} TRIANGLES -
+ * @property {number} TRIANGLE_STRIP -
+ * @property {number} TRIANGLE_FAN -
+ */
+ var DRAW_MODES$2;
+ (function (DRAW_MODES) {
+ DRAW_MODES[DRAW_MODES["POINTS"] = 0] = "POINTS";
+ DRAW_MODES[DRAW_MODES["LINES"] = 1] = "LINES";
+ DRAW_MODES[DRAW_MODES["LINE_LOOP"] = 2] = "LINE_LOOP";
+ DRAW_MODES[DRAW_MODES["LINE_STRIP"] = 3] = "LINE_STRIP";
+ DRAW_MODES[DRAW_MODES["TRIANGLES"] = 4] = "TRIANGLES";
+ DRAW_MODES[DRAW_MODES["TRIANGLE_STRIP"] = 5] = "TRIANGLE_STRIP";
+ DRAW_MODES[DRAW_MODES["TRIANGLE_FAN"] = 6] = "TRIANGLE_FAN";
+ })(DRAW_MODES$2 || (DRAW_MODES$2 = {}));
+ /**
+ * Various GL texture/resources formats.
+ * @memberof PIXI
+ * @static
+ * @name FORMATS
+ * @enum {number}
+ * @property {number} [RGBA=6408] -
+ * @property {number} [RGB=6407] -
+ * @property {number} [RG=33319] -
+ * @property {number} [RED=6403] -
+ * @property {number} [RGBA_INTEGER=36249] -
+ * @property {number} [RGB_INTEGER=36248] -
+ * @property {number} [RG_INTEGER=33320] -
+ * @property {number} [RED_INTEGER=36244] -
+ * @property {number} [ALPHA=6406] -
+ * @property {number} [LUMINANCE=6409] -
+ * @property {number} [LUMINANCE_ALPHA=6410] -
+ * @property {number} [DEPTH_COMPONENT=6402] -
+ * @property {number} [DEPTH_STENCIL=34041] -
+ */
+ var FORMATS$2;
+ (function (FORMATS) {
+ FORMATS[FORMATS["RGBA"] = 6408] = "RGBA";
+ FORMATS[FORMATS["RGB"] = 6407] = "RGB";
+ FORMATS[FORMATS["RG"] = 33319] = "RG";
+ FORMATS[FORMATS["RED"] = 6403] = "RED";
+ FORMATS[FORMATS["RGBA_INTEGER"] = 36249] = "RGBA_INTEGER";
+ FORMATS[FORMATS["RGB_INTEGER"] = 36248] = "RGB_INTEGER";
+ FORMATS[FORMATS["RG_INTEGER"] = 33320] = "RG_INTEGER";
+ FORMATS[FORMATS["RED_INTEGER"] = 36244] = "RED_INTEGER";
+ FORMATS[FORMATS["ALPHA"] = 6406] = "ALPHA";
+ FORMATS[FORMATS["LUMINANCE"] = 6409] = "LUMINANCE";
+ FORMATS[FORMATS["LUMINANCE_ALPHA"] = 6410] = "LUMINANCE_ALPHA";
+ FORMATS[FORMATS["DEPTH_COMPONENT"] = 6402] = "DEPTH_COMPONENT";
+ FORMATS[FORMATS["DEPTH_STENCIL"] = 34041] = "DEPTH_STENCIL";
+ })(FORMATS$2 || (FORMATS$2 = {}));
+ /**
+ * Various GL target types.
+ * @memberof PIXI
+ * @static
+ * @name TARGETS
+ * @enum {number}
+ * @property {number} [TEXTURE_2D=3553] -
+ * @property {number} [TEXTURE_CUBE_MAP=34067] -
+ * @property {number} [TEXTURE_2D_ARRAY=35866] -
+ * @property {number} [TEXTURE_CUBE_MAP_POSITIVE_X=34069] -
+ * @property {number} [TEXTURE_CUBE_MAP_NEGATIVE_X=34070] -
+ * @property {number} [TEXTURE_CUBE_MAP_POSITIVE_Y=34071] -
+ * @property {number} [TEXTURE_CUBE_MAP_NEGATIVE_Y=34072] -
+ * @property {number} [TEXTURE_CUBE_MAP_POSITIVE_Z=34073] -
+ * @property {number} [TEXTURE_CUBE_MAP_NEGATIVE_Z=34074] -
+ */
+ var TARGETS$2;
+ (function (TARGETS) {
+ TARGETS[TARGETS["TEXTURE_2D"] = 3553] = "TEXTURE_2D";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP"] = 34067] = "TEXTURE_CUBE_MAP";
+ TARGETS[TARGETS["TEXTURE_2D_ARRAY"] = 35866] = "TEXTURE_2D_ARRAY";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_POSITIVE_X"] = 34069] = "TEXTURE_CUBE_MAP_POSITIVE_X";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_NEGATIVE_X"] = 34070] = "TEXTURE_CUBE_MAP_NEGATIVE_X";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_POSITIVE_Y"] = 34071] = "TEXTURE_CUBE_MAP_POSITIVE_Y";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_NEGATIVE_Y"] = 34072] = "TEXTURE_CUBE_MAP_NEGATIVE_Y";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_POSITIVE_Z"] = 34073] = "TEXTURE_CUBE_MAP_POSITIVE_Z";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_NEGATIVE_Z"] = 34074] = "TEXTURE_CUBE_MAP_NEGATIVE_Z";
+ })(TARGETS$2 || (TARGETS$2 = {}));
+ /**
+ * Various GL data format types.
+ * @memberof PIXI
+ * @static
+ * @name TYPES
+ * @enum {number}
+ * @property {number} [UNSIGNED_BYTE=5121] -
+ * @property {number} [UNSIGNED_SHORT=5123] -
+ * @property {number} [UNSIGNED_SHORT_5_6_5=33635] -
+ * @property {number} [UNSIGNED_SHORT_4_4_4_4=32819] -
+ * @property {number} [UNSIGNED_SHORT_5_5_5_1=32820] -
+ * @property {number} [UNSIGNED_INT=5125] -
+ * @property {number} [UNSIGNED_INT_10F_11F_11F_REV=35899] -
+ * @property {number} [UNSIGNED_INT_2_10_10_10_REV=33640] -
+ * @property {number} [UNSIGNED_INT_24_8=34042] -
+ * @property {number} [UNSIGNED_INT_5_9_9_9_REV=35902] -
+ * @property {number} [BYTE=5120] -
+ * @property {number} [SHORT=5122] -
+ * @property {number} [INT=5124] -
+ * @property {number} [FLOAT=5126] -
+ * @property {number} [FLOAT_32_UNSIGNED_INT_24_8_REV=36269] -
+ * @property {number} [HALF_FLOAT=36193] -
+ */
+ var TYPES$2;
+ (function (TYPES) {
+ TYPES[TYPES["UNSIGNED_BYTE"] = 5121] = "UNSIGNED_BYTE";
+ TYPES[TYPES["UNSIGNED_SHORT"] = 5123] = "UNSIGNED_SHORT";
+ TYPES[TYPES["UNSIGNED_SHORT_5_6_5"] = 33635] = "UNSIGNED_SHORT_5_6_5";
+ TYPES[TYPES["UNSIGNED_SHORT_4_4_4_4"] = 32819] = "UNSIGNED_SHORT_4_4_4_4";
+ TYPES[TYPES["UNSIGNED_SHORT_5_5_5_1"] = 32820] = "UNSIGNED_SHORT_5_5_5_1";
+ TYPES[TYPES["UNSIGNED_INT"] = 5125] = "UNSIGNED_INT";
+ TYPES[TYPES["UNSIGNED_INT_10F_11F_11F_REV"] = 35899] = "UNSIGNED_INT_10F_11F_11F_REV";
+ TYPES[TYPES["UNSIGNED_INT_2_10_10_10_REV"] = 33640] = "UNSIGNED_INT_2_10_10_10_REV";
+ TYPES[TYPES["UNSIGNED_INT_24_8"] = 34042] = "UNSIGNED_INT_24_8";
+ TYPES[TYPES["UNSIGNED_INT_5_9_9_9_REV"] = 35902] = "UNSIGNED_INT_5_9_9_9_REV";
+ TYPES[TYPES["BYTE"] = 5120] = "BYTE";
+ TYPES[TYPES["SHORT"] = 5122] = "SHORT";
+ TYPES[TYPES["INT"] = 5124] = "INT";
+ TYPES[TYPES["FLOAT"] = 5126] = "FLOAT";
+ TYPES[TYPES["FLOAT_32_UNSIGNED_INT_24_8_REV"] = 36269] = "FLOAT_32_UNSIGNED_INT_24_8_REV";
+ TYPES[TYPES["HALF_FLOAT"] = 36193] = "HALF_FLOAT";
+ })(TYPES$2 || (TYPES$2 = {}));
+ /**
+ * Various sampler types. Correspond to `sampler`, `isampler`, `usampler` GLSL types respectively.
+ * WebGL1 works only with FLOAT.
+ * @memberof PIXI
+ * @static
+ * @name SAMPLER_TYPES
+ * @enum {number}
+ * @property {number} [FLOAT=0] -
+ * @property {number} [INT=1] -
+ * @property {number} [UINT=2] -
+ */
+ var SAMPLER_TYPES$2;
+ (function (SAMPLER_TYPES) {
+ SAMPLER_TYPES[SAMPLER_TYPES["FLOAT"] = 0] = "FLOAT";
+ SAMPLER_TYPES[SAMPLER_TYPES["INT"] = 1] = "INT";
+ SAMPLER_TYPES[SAMPLER_TYPES["UINT"] = 2] = "UINT";
+ })(SAMPLER_TYPES$2 || (SAMPLER_TYPES$2 = {}));
+ /**
+ * The scale modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.SCALE_MODE} scale mode affects the default scaling mode of future operations.
+ * It can be re-assigned to either LINEAR or NEAREST, depending upon suitability.
+ * @memberof PIXI
+ * @static
+ * @name SCALE_MODES
+ * @enum {number}
+ * @property {number} LINEAR Smooth scaling
+ * @property {number} NEAREST Pixelating scaling
+ */
+ var SCALE_MODES$2;
+ (function (SCALE_MODES) {
+ SCALE_MODES[SCALE_MODES["NEAREST"] = 0] = "NEAREST";
+ SCALE_MODES[SCALE_MODES["LINEAR"] = 1] = "LINEAR";
+ })(SCALE_MODES$2 || (SCALE_MODES$2 = {}));
+ /**
+ * The wrap modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.WRAP_MODE} wrap mode affects the default wrapping mode of future operations.
+ * It can be re-assigned to either CLAMP or REPEAT, depending upon suitability.
+ * If the texture is non power of two then clamp will be used regardless as WebGL can
+ * only use REPEAT if the texture is po2.
+ *
+ * This property only affects WebGL.
+ * @name WRAP_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} CLAMP - The textures uvs are clamped
+ * @property {number} REPEAT - The texture uvs tile and repeat
+ * @property {number} MIRRORED_REPEAT - The texture uvs tile and repeat with mirroring
+ */
+ var WRAP_MODES$2;
+ (function (WRAP_MODES) {
+ WRAP_MODES[WRAP_MODES["CLAMP"] = 33071] = "CLAMP";
+ WRAP_MODES[WRAP_MODES["REPEAT"] = 10497] = "REPEAT";
+ WRAP_MODES[WRAP_MODES["MIRRORED_REPEAT"] = 33648] = "MIRRORED_REPEAT";
+ })(WRAP_MODES$2 || (WRAP_MODES$2 = {}));
+ /**
+ * Mipmap filtering modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.MIPMAP_TEXTURES} affects default texture filtering.
+ * Mipmaps are generated for a baseTexture if its `mipmap` field is `ON`,
+ * or its `POW2` and texture dimensions are powers of 2.
+ * Due to platform restriction, `ON` option will work like `POW2` for webgl-1.
+ *
+ * This property only affects WebGL.
+ * @name MIPMAP_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} OFF - No mipmaps
+ * @property {number} POW2 - Generate mipmaps if texture dimensions are pow2
+ * @property {number} ON - Always generate mipmaps
+ * @property {number} ON_MANUAL - Use mipmaps, but do not auto-generate them; this is used with a resource
+ * that supports buffering each level-of-detail.
+ */
+ var MIPMAP_MODES$2;
+ (function (MIPMAP_MODES) {
+ MIPMAP_MODES[MIPMAP_MODES["OFF"] = 0] = "OFF";
+ MIPMAP_MODES[MIPMAP_MODES["POW2"] = 1] = "POW2";
+ MIPMAP_MODES[MIPMAP_MODES["ON"] = 2] = "ON";
+ MIPMAP_MODES[MIPMAP_MODES["ON_MANUAL"] = 3] = "ON_MANUAL";
+ })(MIPMAP_MODES$2 || (MIPMAP_MODES$2 = {}));
+ /**
+ * How to treat textures with premultiplied alpha
+ * @name ALPHA_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} NO_PREMULTIPLIED_ALPHA - Source is not premultiplied, leave it like that.
+ * Option for compressed and data textures that are created from typed arrays.
+ * @property {number} PREMULTIPLY_ON_UPLOAD - Source is not premultiplied, premultiply on upload.
+ * Default option, used for all loaded images.
+ * @property {number} PREMULTIPLIED_ALPHA - Source is already premultiplied
+ * Example: spine atlases with `_pma` suffix.
+ * @property {number} NPM - Alias for NO_PREMULTIPLIED_ALPHA.
+ * @property {number} UNPACK - Default option, alias for PREMULTIPLY_ON_UPLOAD.
+ * @property {number} PMA - Alias for PREMULTIPLIED_ALPHA.
+ */
+ var ALPHA_MODES$2;
+ (function (ALPHA_MODES) {
+ ALPHA_MODES[ALPHA_MODES["NPM"] = 0] = "NPM";
+ ALPHA_MODES[ALPHA_MODES["UNPACK"] = 1] = "UNPACK";
+ ALPHA_MODES[ALPHA_MODES["PMA"] = 2] = "PMA";
+ ALPHA_MODES[ALPHA_MODES["NO_PREMULTIPLIED_ALPHA"] = 0] = "NO_PREMULTIPLIED_ALPHA";
+ ALPHA_MODES[ALPHA_MODES["PREMULTIPLY_ON_UPLOAD"] = 1] = "PREMULTIPLY_ON_UPLOAD";
+ ALPHA_MODES[ALPHA_MODES["PREMULTIPLY_ALPHA"] = 2] = "PREMULTIPLY_ALPHA";
+ ALPHA_MODES[ALPHA_MODES["PREMULTIPLIED_ALPHA"] = 2] = "PREMULTIPLIED_ALPHA";
+ })(ALPHA_MODES$2 || (ALPHA_MODES$2 = {}));
+ /**
+ * Configure whether filter textures are cleared after binding.
+ *
+ * Filter textures need not be cleared if the filter does not use pixel blending. {@link CLEAR_MODES.BLIT} will detect
+ * this and skip clearing as an optimization.
+ * @name CLEAR_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} BLEND - Do not clear the filter texture. The filter's output will blend on top of the output texture.
+ * @property {number} CLEAR - Always clear the filter texture.
+ * @property {number} BLIT - Clear only if {@link FilterSystem.forceClear} is set or if the filter uses pixel blending.
+ * @property {number} NO - Alias for BLEND, same as `false` in earlier versions
+ * @property {number} YES - Alias for CLEAR, same as `true` in earlier versions
+ * @property {number} AUTO - Alias for BLIT
+ */
+ var CLEAR_MODES$2;
+ (function (CLEAR_MODES) {
+ CLEAR_MODES[CLEAR_MODES["NO"] = 0] = "NO";
+ CLEAR_MODES[CLEAR_MODES["YES"] = 1] = "YES";
+ CLEAR_MODES[CLEAR_MODES["AUTO"] = 2] = "AUTO";
+ CLEAR_MODES[CLEAR_MODES["BLEND"] = 0] = "BLEND";
+ CLEAR_MODES[CLEAR_MODES["CLEAR"] = 1] = "CLEAR";
+ CLEAR_MODES[CLEAR_MODES["BLIT"] = 2] = "BLIT";
+ })(CLEAR_MODES$2 || (CLEAR_MODES$2 = {}));
+ /**
+ * The gc modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.GC_MODE} Garbage Collection mode for PixiJS textures is AUTO
+ * If set to GC_MODE, the renderer will occasionally check textures usage. If they are not
+ * used for a specified period of time they will be removed from the GPU. They will of course
+ * be uploaded again when they are required. This is a silent behind the scenes process that
+ * should ensure that the GPU does not get filled up.
+ *
+ * Handy for mobile devices!
+ * This property only affects WebGL.
+ * @name GC_MODES
+ * @enum {number}
+ * @static
+ * @memberof PIXI
+ * @property {number} AUTO - Garbage collection will happen periodically automatically
+ * @property {number} MANUAL - Garbage collection will need to be called manually
+ */
+ var GC_MODES$2;
+ (function (GC_MODES) {
+ GC_MODES[GC_MODES["AUTO"] = 0] = "AUTO";
+ GC_MODES[GC_MODES["MANUAL"] = 1] = "MANUAL";
+ })(GC_MODES$2 || (GC_MODES$2 = {}));
+ /**
+ * Constants that specify float precision in shaders.
+ * @name PRECISION
+ * @memberof PIXI
+ * @constant
+ * @static
+ * @enum {string}
+ * @property {string} [LOW='lowp'] -
+ * @property {string} [MEDIUM='mediump'] -
+ * @property {string} [HIGH='highp'] -
+ */
+ var PRECISION$2;
+ (function (PRECISION) {
+ PRECISION["LOW"] = "lowp";
+ PRECISION["MEDIUM"] = "mediump";
+ PRECISION["HIGH"] = "highp";
+ })(PRECISION$2 || (PRECISION$2 = {}));
+ /**
+ * Constants for mask implementations.
+ * We use `type` suffix because it leads to very different behaviours
+ * @name MASK_TYPES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} NONE - Mask is ignored
+ * @property {number} SCISSOR - Scissor mask, rectangle on screen, cheap
+ * @property {number} STENCIL - Stencil mask, 1-bit, medium, works only if renderer supports stencil
+ * @property {number} SPRITE - Mask that uses SpriteMaskFilter, uses temporary RenderTexture
+ * @property {number} COLOR - Color mask (RGBA)
+ */
+ var MASK_TYPES$2;
+ (function (MASK_TYPES) {
+ MASK_TYPES[MASK_TYPES["NONE"] = 0] = "NONE";
+ MASK_TYPES[MASK_TYPES["SCISSOR"] = 1] = "SCISSOR";
+ MASK_TYPES[MASK_TYPES["STENCIL"] = 2] = "STENCIL";
+ MASK_TYPES[MASK_TYPES["SPRITE"] = 3] = "SPRITE";
+ MASK_TYPES[MASK_TYPES["COLOR"] = 4] = "COLOR";
+ })(MASK_TYPES$2 || (MASK_TYPES$2 = {}));
+ /**
+ * Bitwise OR of masks that indicate the color channels that are rendered to.
+ * @static
+ * @memberof PIXI
+ * @name COLOR_MASK_BITS
+ * @enum {number}
+ * @property {number} RED - Red channel.
+ * @property {number} GREEN - Green channel
+ * @property {number} BLUE - Blue channel.
+ * @property {number} ALPHA - Alpha channel.
+ */
+ var COLOR_MASK_BITS$2;
+ (function (COLOR_MASK_BITS) {
+ COLOR_MASK_BITS[COLOR_MASK_BITS["RED"] = 1] = "RED";
+ COLOR_MASK_BITS[COLOR_MASK_BITS["GREEN"] = 2] = "GREEN";
+ COLOR_MASK_BITS[COLOR_MASK_BITS["BLUE"] = 4] = "BLUE";
+ COLOR_MASK_BITS[COLOR_MASK_BITS["ALPHA"] = 8] = "ALPHA";
+ })(COLOR_MASK_BITS$2 || (COLOR_MASK_BITS$2 = {}));
+ /**
+ * Constants for multi-sampling antialiasing.
+ * @see PIXI.Framebuffer#multisample
+ * @name MSAA_QUALITY
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} NONE - No multisampling for this renderTexture
+ * @property {number} LOW - Try 2 samples
+ * @property {number} MEDIUM - Try 4 samples
+ * @property {number} HIGH - Try 8 samples
+ */
+ var MSAA_QUALITY$2;
+ (function (MSAA_QUALITY) {
+ MSAA_QUALITY[MSAA_QUALITY["NONE"] = 0] = "NONE";
+ MSAA_QUALITY[MSAA_QUALITY["LOW"] = 2] = "LOW";
+ MSAA_QUALITY[MSAA_QUALITY["MEDIUM"] = 4] = "MEDIUM";
+ MSAA_QUALITY[MSAA_QUALITY["HIGH"] = 8] = "HIGH";
+ })(MSAA_QUALITY$2 || (MSAA_QUALITY$2 = {}));
+ /**
+ * Constants for various buffer types in Pixi
+ * @see PIXI.BUFFER_TYPE
+ * @name BUFFER_TYPE
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} ELEMENT_ARRAY_BUFFER - buffer type for using as an index buffer
+ * @property {number} ARRAY_BUFFER - buffer type for using attribute data
+ * @property {number} UNIFORM_BUFFER - the buffer type is for uniform buffer objects
+ */
+ var BUFFER_TYPE$2;
+ (function (BUFFER_TYPE) {
+ BUFFER_TYPE[BUFFER_TYPE["ELEMENT_ARRAY_BUFFER"] = 34963] = "ELEMENT_ARRAY_BUFFER";
+ BUFFER_TYPE[BUFFER_TYPE["ARRAY_BUFFER"] = 34962] = "ARRAY_BUFFER";
+ // NOT YET SUPPORTED
+ BUFFER_TYPE[BUFFER_TYPE["UNIFORM_BUFFER"] = 35345] = "UNIFORM_BUFFER";
+ })(BUFFER_TYPE$2 || (BUFFER_TYPE$2 = {}));
+
+ var _tempMatrix = new Matrix();
+ DisplayObject.prototype._cacheAsBitmap = false;
+ DisplayObject.prototype._cacheData = null;
+ DisplayObject.prototype._cacheAsBitmapResolution = null;
+ DisplayObject.prototype._cacheAsBitmapMultisample = MSAA_QUALITY$2.NONE;
+ // figured there's no point adding ALL the extra variables to prototype.
+ // this model can hold the information needed. This can also be generated on demand as
+ // most objects are not cached as bitmaps.
+ /**
+ * @class
+ * @ignore
+ * @private
+ */
+ var CacheData = /** @class */ (function () {
+ function CacheData() {
+ this.textureCacheId = null;
+ this.originalRender = null;
+ this.originalRenderCanvas = null;
+ this.originalCalculateBounds = null;
+ this.originalGetLocalBounds = null;
+ this.originalUpdateTransform = null;
+ this.originalDestroy = null;
+ this.originalMask = null;
+ this.originalFilterArea = null;
+ this.originalContainsPoint = null;
+ this.sprite = null;
+ }
+ return CacheData;
+ }());
+ Object.defineProperties(DisplayObject.prototype, {
+ /**
+ * The resolution to use for cacheAsBitmap. By default this will use the renderer's resolution
+ * but can be overriden for performance. Lower values will reduce memory usage at the expense
+ * of render quality. A falsey value of `null` or `0` will default to the renderer's resolution.
+ * If `cacheAsBitmap` is set to `true`, this will re-render with the new resolution.
+ * @member {number} cacheAsBitmapResolution
+ * @memberof PIXI.DisplayObject#
+ * @default null
+ */
+ cacheAsBitmapResolution: {
+ get: function () {
+ return this._cacheAsBitmapResolution;
+ },
+ set: function (resolution) {
+ if (resolution === this._cacheAsBitmapResolution) {
+ return;
+ }
+ this._cacheAsBitmapResolution = resolution;
+ if (this.cacheAsBitmap) {
+ // Toggle to re-render at the new resolution
+ this.cacheAsBitmap = false;
+ this.cacheAsBitmap = true;
+ }
+ },
+ },
+ /**
+ * The number of samples to use for cacheAsBitmap. If set to `null`, the renderer's
+ * sample count is used.
+ * If `cacheAsBitmap` is set to `true`, this will re-render with the new number of samples.
+ * @member {number} cacheAsBitmapMultisample
+ * @memberof PIXI.DisplayObject#
+ * @default PIXI.MSAA_QUALITY.NONE
+ */
+ cacheAsBitmapMultisample: {
+ get: function () {
+ return this._cacheAsBitmapMultisample;
+ },
+ set: function (multisample) {
+ if (multisample === this._cacheAsBitmapMultisample) {
+ return;
+ }
+ this._cacheAsBitmapMultisample = multisample;
+ if (this.cacheAsBitmap) {
+ // Toggle to re-render with new multisample
+ this.cacheAsBitmap = false;
+ this.cacheAsBitmap = true;
+ }
+ },
+ },
+ /**
+ * Set this to true if you want this display object to be cached as a bitmap.
+ * This basically takes a snap shot of the display object as it is at that moment. It can
+ * provide a performance benefit for complex static displayObjects.
+ * To remove simply set this property to `false`
+ *
+ * IMPORTANT GOTCHA - Make sure that all your textures are preloaded BEFORE setting this property to true
+ * as it will take a snapshot of what is currently there. If the textures have not loaded then they will not appear.
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
+ */
+ cacheAsBitmap: {
+ get: function () {
+ return this._cacheAsBitmap;
+ },
+ set: function (value) {
+ if (this._cacheAsBitmap === value) {
+ return;
+ }
+ this._cacheAsBitmap = value;
+ var data;
+ if (value) {
+ if (!this._cacheData) {
+ this._cacheData = new CacheData();
+ }
+ data = this._cacheData;
+ data.originalRender = this.render;
+ data.originalRenderCanvas = this.renderCanvas;
+ data.originalUpdateTransform = this.updateTransform;
+ data.originalCalculateBounds = this.calculateBounds;
+ data.originalGetLocalBounds = this.getLocalBounds;
+ data.originalDestroy = this.destroy;
+ data.originalContainsPoint = this.containsPoint;
+ data.originalMask = this._mask;
+ data.originalFilterArea = this.filterArea;
+ this.render = this._renderCached;
+ this.renderCanvas = this._renderCachedCanvas;
+ this.destroy = this._cacheAsBitmapDestroy;
+ }
+ else {
+ data = this._cacheData;
+ if (data.sprite) {
+ this._destroyCachedDisplayObject();
+ }
+ this.render = data.originalRender;
+ this.renderCanvas = data.originalRenderCanvas;
+ this.calculateBounds = data.originalCalculateBounds;
+ this.getLocalBounds = data.originalGetLocalBounds;
+ this.destroy = data.originalDestroy;
+ this.updateTransform = data.originalUpdateTransform;
+ this.containsPoint = data.originalContainsPoint;
+ this._mask = data.originalMask;
+ this.filterArea = data.originalFilterArea;
+ }
+ },
+ },
+ });
+ /**
+ * Renders a cached version of the sprite with WebGL
+ * @private
+ * @method _renderCached
+ * @memberof PIXI.DisplayObject#
+ * @param {PIXI.Renderer} renderer - the WebGL renderer
+ */
+ DisplayObject.prototype._renderCached = function _renderCached(renderer) {
+ if (!this.visible || this.worldAlpha <= 0 || !this.renderable) {
+ return;
+ }
+ this._initCachedDisplayObject(renderer);
+ this._cacheData.sprite.transform._worldID = this.transform._worldID;
+ this._cacheData.sprite.worldAlpha = this.worldAlpha;
+ this._cacheData.sprite._render(renderer);
+ };
+ /**
+ * Prepares the WebGL renderer to cache the sprite
+ * @private
+ * @method _initCachedDisplayObject
+ * @memberof PIXI.DisplayObject#
+ * @param {PIXI.Renderer} renderer - the WebGL renderer
+ */
+ DisplayObject.prototype._initCachedDisplayObject = function _initCachedDisplayObject(renderer) {
+ var _a;
+ if (this._cacheData && this._cacheData.sprite) {
+ return;
+ }
+ // make sure alpha is set to 1 otherwise it will get rendered as invisible!
+ var cacheAlpha = this.alpha;
+ this.alpha = 1;
+ // first we flush anything left in the renderer (otherwise it would get rendered to the cached texture)
+ renderer.batch.flush();
+ // this.filters= [];
+ // next we find the dimensions of the untransformed object
+ // this function also calls updatetransform on all its children as part of the measuring.
+ // This means we don't need to update the transform again in this function
+ // TODO pass an object to clone too? saves having to create a new one each time!
+ var bounds = this.getLocalBounds(null, true).clone();
+ // add some padding!
+ if (this.filters && this.filters.length) {
+ var padding = this.filters[0].padding;
+ bounds.pad(padding);
+ }
+ bounds.ceil(settings$1.RESOLUTION);
+ // for now we cache the current renderTarget that the WebGL renderer is currently using.
+ // this could be more elegant..
+ var cachedRenderTexture = renderer.renderTexture.current;
+ var cachedSourceFrame = renderer.renderTexture.sourceFrame.clone();
+ var cachedDestinationFrame = renderer.renderTexture.destinationFrame.clone();
+ var cachedProjectionTransform = renderer.projection.transform;
+ // We also store the filter stack - I will definitely look to change how this works a little later down the line.
+ // const stack = renderer.filterManager.filterStack;
+ // this renderTexture will be used to store the cached DisplayObject
+ var renderTexture = RenderTexture.create({
+ width: bounds.width,
+ height: bounds.height,
+ resolution: this.cacheAsBitmapResolution || renderer.resolution,
+ multisample: (_a = this.cacheAsBitmapMultisample) !== null && _a !== void 0 ? _a : renderer.multisample,
+ });
+ var textureCacheId = "cacheAsBitmap_" + uid();
+ this._cacheData.textureCacheId = textureCacheId;
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+ // need to set //
+ var m = this.transform.localTransform.copyTo(_tempMatrix).invert().translate(-bounds.x, -bounds.y);
+ // set all properties to there original so we can render to a texture
+ this.render = this._cacheData.originalRender;
+ renderer.render(this, { renderTexture: renderTexture, clear: true, transform: m, skipUpdateTransform: false });
+ renderer.framebuffer.blit();
+ // now restore the state be setting the new properties
+ renderer.projection.transform = cachedProjectionTransform;
+ renderer.renderTexture.bind(cachedRenderTexture, cachedSourceFrame, cachedDestinationFrame);
+ // renderer.filterManager.filterStack = stack;
+ this.render = this._renderCached;
+ // the rest is the same as for Canvas
+ this.updateTransform = this.displayObjectUpdateTransform;
+ this.calculateBounds = this._calculateCachedBounds;
+ this.getLocalBounds = this._getCachedLocalBounds;
+ this._mask = null;
+ this.filterArea = null;
+ this.alpha = cacheAlpha;
+ // create our cached sprite
+ var cachedSprite = new Sprite(renderTexture);
+ cachedSprite.transform.worldTransform = this.transform.worldTransform;
+ cachedSprite.anchor.x = -(bounds.x / bounds.width);
+ cachedSprite.anchor.y = -(bounds.y / bounds.height);
+ cachedSprite.alpha = cacheAlpha;
+ cachedSprite._bounds = this._bounds;
+ this._cacheData.sprite = cachedSprite;
+ this.transform._parentID = -1;
+ // restore the transform of the cached sprite to avoid the nasty flicker..
+ if (!this.parent) {
+ this.enableTempParent();
+ this.updateTransform();
+ this.disableTempParent(null);
+ }
+ else {
+ this.updateTransform();
+ }
+ // map the hit test..
+ this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
+ };
+ /**
+ * Renders a cached version of the sprite with canvas
+ * @private
+ * @method _renderCachedCanvas
+ * @memberof PIXI.DisplayObject#
+ * @param {PIXI.CanvasRenderer} renderer - The canvas renderer
+ */
+ DisplayObject.prototype._renderCachedCanvas = function _renderCachedCanvas(renderer) {
+ if (!this.visible || this.worldAlpha <= 0 || !this.renderable) {
+ return;
+ }
+ this._initCachedDisplayObjectCanvas(renderer);
+ this._cacheData.sprite.worldAlpha = this.worldAlpha;
+ this._cacheData.sprite._renderCanvas(renderer);
+ };
+ // TODO this can be the same as the WebGL version.. will need to do a little tweaking first though..
+ /**
+ * Prepares the Canvas renderer to cache the sprite
+ * @private
+ * @method _initCachedDisplayObjectCanvas
+ * @memberof PIXI.DisplayObject#
+ * @param {PIXI.CanvasRenderer} renderer - The canvas renderer
+ */
+ DisplayObject.prototype._initCachedDisplayObjectCanvas = function _initCachedDisplayObjectCanvas(renderer) {
+ if (this._cacheData && this._cacheData.sprite) {
+ return;
+ }
+ // get bounds actually transforms the object for us already!
+ var bounds = this.getLocalBounds(null, true);
+ var cacheAlpha = this.alpha;
+ this.alpha = 1;
+ var cachedRenderTarget = renderer.context;
+ var cachedProjectionTransform = renderer._projTransform;
+ bounds.ceil(settings$1.RESOLUTION);
+ var renderTexture = RenderTexture.create({ width: bounds.width, height: bounds.height });
+ var textureCacheId = "cacheAsBitmap_" + uid();
+ this._cacheData.textureCacheId = textureCacheId;
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+ // need to set //
+ var m = _tempMatrix;
+ this.transform.localTransform.copyTo(m);
+ m.invert();
+ m.tx -= bounds.x;
+ m.ty -= bounds.y;
+ // m.append(this.transform.worldTransform.)
+ // set all properties to there original so we can render to a texture
+ this.renderCanvas = this._cacheData.originalRenderCanvas;
+ renderer.render(this, { renderTexture: renderTexture, clear: true, transform: m, skipUpdateTransform: false });
+ // now restore the state be setting the new properties
+ renderer.context = cachedRenderTarget;
+ renderer._projTransform = cachedProjectionTransform;
+ this.renderCanvas = this._renderCachedCanvas;
+ // the rest is the same as for WebGL
+ this.updateTransform = this.displayObjectUpdateTransform;
+ this.calculateBounds = this._calculateCachedBounds;
+ this.getLocalBounds = this._getCachedLocalBounds;
+ this._mask = null;
+ this.filterArea = null;
+ this.alpha = cacheAlpha;
+ // create our cached sprite
+ var cachedSprite = new Sprite(renderTexture);
+ cachedSprite.transform.worldTransform = this.transform.worldTransform;
+ cachedSprite.anchor.x = -(bounds.x / bounds.width);
+ cachedSprite.anchor.y = -(bounds.y / bounds.height);
+ cachedSprite.alpha = cacheAlpha;
+ cachedSprite._bounds = this._bounds;
+ this._cacheData.sprite = cachedSprite;
+ this.transform._parentID = -1;
+ // restore the transform of the cached sprite to avoid the nasty flicker..
+ if (!this.parent) {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else {
+ this.updateTransform();
+ }
+ // map the hit test..
+ this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
+ };
+ /**
+ * Calculates the bounds of the cached sprite
+ * @private
+ * @method
+ */
+ DisplayObject.prototype._calculateCachedBounds = function _calculateCachedBounds() {
+ this._bounds.clear();
+ this._cacheData.sprite.transform._worldID = this.transform._worldID;
+ this._cacheData.sprite._calculateBounds();
+ this._bounds.updateID = this._boundsID;
+ };
+ /**
+ * Gets the bounds of the cached sprite.
+ * @private
+ * @method
+ * @returns {Rectangle} The local bounds.
+ */
+ DisplayObject.prototype._getCachedLocalBounds = function _getCachedLocalBounds() {
+ return this._cacheData.sprite.getLocalBounds(null);
+ };
+ /**
+ * Destroys the cached sprite.
+ * @private
+ * @method
+ */
+ DisplayObject.prototype._destroyCachedDisplayObject = function _destroyCachedDisplayObject() {
+ this._cacheData.sprite._texture.destroy(true);
+ this._cacheData.sprite = null;
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+ this._cacheData.textureCacheId = null;
+ };
+ /**
+ * Destroys the cached object.
+ * @private
+ * @method
+ * @param {object|boolean} [options] - Options parameter. A boolean will act as if all options
+ * have been set to that value.
+ * Used when destroying containers, see the Container.destroy method.
+ */
+ DisplayObject.prototype._cacheAsBitmapDestroy = function _cacheAsBitmapDestroy(options) {
+ this.cacheAsBitmap = false;
+ this.destroy(options);
+ };
+
+ /*!
+ * @pixi/mixin-get-child-by-name - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/mixin-get-child-by-name is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /**
+ * The instance name of the object.
+ * @memberof PIXI.DisplayObject#
+ * @member {string} name
+ */
+ DisplayObject.prototype.name = null;
+ /**
+ * Returns the display object in the container.
+ *
+ * Recursive searches are done in a preorder traversal.
+ * @method getChildByName
+ * @memberof PIXI.Container#
+ * @param {string} name - Instance name.
+ * @param {boolean}[deep=false] - Whether to search recursively
+ * @returns {PIXI.DisplayObject} The child with the specified name.
+ */
+ Container.prototype.getChildByName = function getChildByName(name, deep) {
+ for (var i = 0, j = this.children.length; i < j; i++) {
+ if (this.children[i].name === name) {
+ return this.children[i];
+ }
+ }
+ if (deep) {
+ for (var i = 0, j = this.children.length; i < j; i++) {
+ var child = this.children[i];
+ if (!child.getChildByName) {
+ continue;
+ }
+ var target = child.getChildByName(name, true);
+ if (target) {
+ return target;
+ }
+ }
+ }
+ return null;
+ };
+
+ /*!
+ * @pixi/mixin-get-global-position - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/mixin-get-global-position is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /**
+ * Returns the global position of the displayObject. Does not depend on object scale, rotation and pivot.
+ * @method getGlobalPosition
+ * @memberof PIXI.DisplayObject#
+ * @param {PIXI.Point} [point=new PIXI.Point()] - The point to write the global value to.
+ * @param {boolean} [skipUpdate=false] - Setting to true will stop the transforms of the scene graph from
+ * being updated. This means the calculation returned MAY be out of date BUT will give you a
+ * nice performance boost.
+ * @returns {PIXI.Point} The updated point.
+ */
+ DisplayObject.prototype.getGlobalPosition = function getGlobalPosition(point, skipUpdate) {
+ if (point === void 0) { point = new Point(); }
+ if (skipUpdate === void 0) { skipUpdate = false; }
+ if (this.parent) {
+ this.parent.toGlobal(this.position, point, skipUpdate);
+ }
+ else {
+ point.x = this.position.x;
+ point.y = this.position.y;
+ }
+ return point;
+ };
+
+ /*!
+ * @pixi/app - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/app is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /**
+ * Middleware for for Application's resize functionality
+ * @private
+ * @class
+ */
+ var ResizePlugin = /** @class */ (function () {
+ function ResizePlugin() {
+ }
+ /**
+ * Initialize the plugin with scope of application instance
+ * @static
+ * @private
+ * @param {object} [options] - See application options
+ */
+ ResizePlugin.init = function (options) {
+ var _this = this;
+ Object.defineProperty(this, 'resizeTo',
+ /**
+ * The HTML element or window to automatically resize the
+ * renderer's view element to match width and height.
+ * @member {Window|HTMLElement}
+ * @name resizeTo
+ * @memberof PIXI.Application#
+ */
+ {
+ set: function (dom) {
+ globalThis.removeEventListener('resize', this.queueResize);
+ this._resizeTo = dom;
+ if (dom) {
+ globalThis.addEventListener('resize', this.queueResize);
+ this.resize();
+ }
+ },
+ get: function () {
+ return this._resizeTo;
+ },
+ });
+ /**
+ * Resize is throttled, so it's safe to call this multiple times per frame and it'll
+ * only be called once.
+ * @memberof PIXI.Application#
+ * @method queueResize
+ * @private
+ */
+ this.queueResize = function () {
+ if (!_this._resizeTo) {
+ return;
+ }
+ _this.cancelResize();
+ // // Throttle resize events per raf
+ _this._resizeId = requestAnimationFrame(function () { return _this.resize(); });
+ };
+ /**
+ * Cancel the resize queue.
+ * @memberof PIXI.Application#
+ * @method cancelResize
+ * @private
+ */
+ this.cancelResize = function () {
+ if (_this._resizeId) {
+ cancelAnimationFrame(_this._resizeId);
+ _this._resizeId = null;
+ }
+ };
+ /**
+ * Execute an immediate resize on the renderer, this is not
+ * throttled and can be expensive to call many times in a row.
+ * Will resize only if `resizeTo` property is set.
+ * @memberof PIXI.Application#
+ * @method resize
+ */
+ this.resize = function () {
+ if (!_this._resizeTo) {
+ return;
+ }
+ // clear queue resize
+ _this.cancelResize();
+ var width;
+ var height;
+ // Resize to the window
+ if (_this._resizeTo === globalThis.window) {
+ width = globalThis.innerWidth;
+ height = globalThis.innerHeight;
+ }
+ // Resize to other HTML entities
+ else {
+ var _a = _this._resizeTo, clientWidth = _a.clientWidth, clientHeight = _a.clientHeight;
+ width = clientWidth;
+ height = clientHeight;
+ }
+ _this.renderer.resize(width, height);
+ };
+ // On resize
+ this._resizeId = null;
+ this._resizeTo = null;
+ this.resizeTo = options.resizeTo || null;
+ };
+ /**
+ * Clean up the ticker, scoped to application
+ * @static
+ * @private
+ */
+ ResizePlugin.destroy = function () {
+ globalThis.removeEventListener('resize', this.queueResize);
+ this.cancelResize();
+ this.cancelResize = null;
+ this.queueResize = null;
+ this.resizeTo = null;
+ this.resize = null;
+ };
+ /** @ignore */
+ ResizePlugin.extension = exports.ExtensionType.Application;
+ return ResizePlugin;
+ }());
+
+ /*!
+ * @pixi/settings - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/settings is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+ /*!
+ * @pixi/constants - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/constants is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+ /**
+ * Different types of environments for WebGL.
+ * @static
+ * @memberof PIXI
+ * @name ENV
+ * @enum {number}
+ * @property {number} WEBGL_LEGACY - Used for older v1 WebGL devices. PixiJS will aim to ensure compatibility
+ * with older / less advanced devices. If you experience unexplained flickering prefer this environment.
+ * @property {number} WEBGL - Version 1 of WebGL
+ * @property {number} WEBGL2 - Version 2 of WebGL
+ */
+ var ENV$1;
+ (function (ENV) {
+ ENV[ENV["WEBGL_LEGACY"] = 0] = "WEBGL_LEGACY";
+ ENV[ENV["WEBGL"] = 1] = "WEBGL";
+ ENV[ENV["WEBGL2"] = 2] = "WEBGL2";
+ })(ENV$1 || (ENV$1 = {}));
+ /**
+ * Constant to identify the Renderer Type.
+ * @static
+ * @memberof PIXI
+ * @name RENDERER_TYPE
+ * @enum {number}
+ * @property {number} UNKNOWN - Unknown render type.
+ * @property {number} WEBGL - WebGL render type.
+ * @property {number} CANVAS - Canvas render type.
+ */
+ var RENDERER_TYPE$1;
+ (function (RENDERER_TYPE) {
+ RENDERER_TYPE[RENDERER_TYPE["UNKNOWN"] = 0] = "UNKNOWN";
+ RENDERER_TYPE[RENDERER_TYPE["WEBGL"] = 1] = "WEBGL";
+ RENDERER_TYPE[RENDERER_TYPE["CANVAS"] = 2] = "CANVAS";
+ })(RENDERER_TYPE$1 || (RENDERER_TYPE$1 = {}));
+ /**
+ * Bitwise OR of masks that indicate the buffers to be cleared.
+ * @static
+ * @memberof PIXI
+ * @name BUFFER_BITS
+ * @enum {number}
+ * @property {number} COLOR - Indicates the buffers currently enabled for color writing.
+ * @property {number} DEPTH - Indicates the depth buffer.
+ * @property {number} STENCIL - Indicates the stencil buffer.
+ */
+ var BUFFER_BITS$1;
+ (function (BUFFER_BITS) {
+ BUFFER_BITS[BUFFER_BITS["COLOR"] = 16384] = "COLOR";
+ BUFFER_BITS[BUFFER_BITS["DEPTH"] = 256] = "DEPTH";
+ BUFFER_BITS[BUFFER_BITS["STENCIL"] = 1024] = "STENCIL";
+ })(BUFFER_BITS$1 || (BUFFER_BITS$1 = {}));
+ /**
+ * Various blend modes supported by PIXI.
+ *
+ * IMPORTANT - The WebGL renderer only supports the NORMAL, ADD, MULTIPLY and SCREEN blend modes.
+ * Anything else will silently act like NORMAL.
+ * @memberof PIXI
+ * @name BLEND_MODES
+ * @enum {number}
+ * @property {number} NORMAL -
+ * @property {number} ADD -
+ * @property {number} MULTIPLY -
+ * @property {number} SCREEN -
+ * @property {number} OVERLAY -
+ * @property {number} DARKEN -
+ * @property {number} LIGHTEN -
+ * @property {number} COLOR_DODGE -
+ * @property {number} COLOR_BURN -
+ * @property {number} HARD_LIGHT -
+ * @property {number} SOFT_LIGHT -
+ * @property {number} DIFFERENCE -
+ * @property {number} EXCLUSION -
+ * @property {number} HUE -
+ * @property {number} SATURATION -
+ * @property {number} COLOR -
+ * @property {number} LUMINOSITY -
+ * @property {number} NORMAL_NPM -
+ * @property {number} ADD_NPM -
+ * @property {number} SCREEN_NPM -
+ * @property {number} NONE -
+ * @property {number} SRC_IN -
+ * @property {number} SRC_OUT -
+ * @property {number} SRC_ATOP -
+ * @property {number} DST_OVER -
+ * @property {number} DST_IN -
+ * @property {number} DST_OUT -
+ * @property {number} DST_ATOP -
+ * @property {number} SUBTRACT -
+ * @property {number} SRC_OVER -
+ * @property {number} ERASE -
+ * @property {number} XOR -
+ */
+ var BLEND_MODES$1;
+ (function (BLEND_MODES) {
+ BLEND_MODES[BLEND_MODES["NORMAL"] = 0] = "NORMAL";
+ BLEND_MODES[BLEND_MODES["ADD"] = 1] = "ADD";
+ BLEND_MODES[BLEND_MODES["MULTIPLY"] = 2] = "MULTIPLY";
+ BLEND_MODES[BLEND_MODES["SCREEN"] = 3] = "SCREEN";
+ BLEND_MODES[BLEND_MODES["OVERLAY"] = 4] = "OVERLAY";
+ BLEND_MODES[BLEND_MODES["DARKEN"] = 5] = "DARKEN";
+ BLEND_MODES[BLEND_MODES["LIGHTEN"] = 6] = "LIGHTEN";
+ BLEND_MODES[BLEND_MODES["COLOR_DODGE"] = 7] = "COLOR_DODGE";
+ BLEND_MODES[BLEND_MODES["COLOR_BURN"] = 8] = "COLOR_BURN";
+ BLEND_MODES[BLEND_MODES["HARD_LIGHT"] = 9] = "HARD_LIGHT";
+ BLEND_MODES[BLEND_MODES["SOFT_LIGHT"] = 10] = "SOFT_LIGHT";
+ BLEND_MODES[BLEND_MODES["DIFFERENCE"] = 11] = "DIFFERENCE";
+ BLEND_MODES[BLEND_MODES["EXCLUSION"] = 12] = "EXCLUSION";
+ BLEND_MODES[BLEND_MODES["HUE"] = 13] = "HUE";
+ BLEND_MODES[BLEND_MODES["SATURATION"] = 14] = "SATURATION";
+ BLEND_MODES[BLEND_MODES["COLOR"] = 15] = "COLOR";
+ BLEND_MODES[BLEND_MODES["LUMINOSITY"] = 16] = "LUMINOSITY";
+ BLEND_MODES[BLEND_MODES["NORMAL_NPM"] = 17] = "NORMAL_NPM";
+ BLEND_MODES[BLEND_MODES["ADD_NPM"] = 18] = "ADD_NPM";
+ BLEND_MODES[BLEND_MODES["SCREEN_NPM"] = 19] = "SCREEN_NPM";
+ BLEND_MODES[BLEND_MODES["NONE"] = 20] = "NONE";
+ BLEND_MODES[BLEND_MODES["SRC_OVER"] = 0] = "SRC_OVER";
+ BLEND_MODES[BLEND_MODES["SRC_IN"] = 21] = "SRC_IN";
+ BLEND_MODES[BLEND_MODES["SRC_OUT"] = 22] = "SRC_OUT";
+ BLEND_MODES[BLEND_MODES["SRC_ATOP"] = 23] = "SRC_ATOP";
+ BLEND_MODES[BLEND_MODES["DST_OVER"] = 24] = "DST_OVER";
+ BLEND_MODES[BLEND_MODES["DST_IN"] = 25] = "DST_IN";
+ BLEND_MODES[BLEND_MODES["DST_OUT"] = 26] = "DST_OUT";
+ BLEND_MODES[BLEND_MODES["DST_ATOP"] = 27] = "DST_ATOP";
+ BLEND_MODES[BLEND_MODES["ERASE"] = 26] = "ERASE";
+ BLEND_MODES[BLEND_MODES["SUBTRACT"] = 28] = "SUBTRACT";
+ BLEND_MODES[BLEND_MODES["XOR"] = 29] = "XOR";
+ })(BLEND_MODES$1 || (BLEND_MODES$1 = {}));
+ /**
+ * Various webgl draw modes. These can be used to specify which GL drawMode to use
+ * under certain situations and renderers.
+ * @memberof PIXI
+ * @static
+ * @name DRAW_MODES
+ * @enum {number}
+ * @property {number} POINTS -
+ * @property {number} LINES -
+ * @property {number} LINE_LOOP -
+ * @property {number} LINE_STRIP -
+ * @property {number} TRIANGLES -
+ * @property {number} TRIANGLE_STRIP -
+ * @property {number} TRIANGLE_FAN -
+ */
+ var DRAW_MODES$1;
+ (function (DRAW_MODES) {
+ DRAW_MODES[DRAW_MODES["POINTS"] = 0] = "POINTS";
+ DRAW_MODES[DRAW_MODES["LINES"] = 1] = "LINES";
+ DRAW_MODES[DRAW_MODES["LINE_LOOP"] = 2] = "LINE_LOOP";
+ DRAW_MODES[DRAW_MODES["LINE_STRIP"] = 3] = "LINE_STRIP";
+ DRAW_MODES[DRAW_MODES["TRIANGLES"] = 4] = "TRIANGLES";
+ DRAW_MODES[DRAW_MODES["TRIANGLE_STRIP"] = 5] = "TRIANGLE_STRIP";
+ DRAW_MODES[DRAW_MODES["TRIANGLE_FAN"] = 6] = "TRIANGLE_FAN";
+ })(DRAW_MODES$1 || (DRAW_MODES$1 = {}));
+ /**
+ * Various GL texture/resources formats.
+ * @memberof PIXI
+ * @static
+ * @name FORMATS
+ * @enum {number}
+ * @property {number} [RGBA=6408] -
+ * @property {number} [RGB=6407] -
+ * @property {number} [RG=33319] -
+ * @property {number} [RED=6403] -
+ * @property {number} [RGBA_INTEGER=36249] -
+ * @property {number} [RGB_INTEGER=36248] -
+ * @property {number} [RG_INTEGER=33320] -
+ * @property {number} [RED_INTEGER=36244] -
+ * @property {number} [ALPHA=6406] -
+ * @property {number} [LUMINANCE=6409] -
+ * @property {number} [LUMINANCE_ALPHA=6410] -
+ * @property {number} [DEPTH_COMPONENT=6402] -
+ * @property {number} [DEPTH_STENCIL=34041] -
+ */
+ var FORMATS$1;
+ (function (FORMATS) {
+ FORMATS[FORMATS["RGBA"] = 6408] = "RGBA";
+ FORMATS[FORMATS["RGB"] = 6407] = "RGB";
+ FORMATS[FORMATS["RG"] = 33319] = "RG";
+ FORMATS[FORMATS["RED"] = 6403] = "RED";
+ FORMATS[FORMATS["RGBA_INTEGER"] = 36249] = "RGBA_INTEGER";
+ FORMATS[FORMATS["RGB_INTEGER"] = 36248] = "RGB_INTEGER";
+ FORMATS[FORMATS["RG_INTEGER"] = 33320] = "RG_INTEGER";
+ FORMATS[FORMATS["RED_INTEGER"] = 36244] = "RED_INTEGER";
+ FORMATS[FORMATS["ALPHA"] = 6406] = "ALPHA";
+ FORMATS[FORMATS["LUMINANCE"] = 6409] = "LUMINANCE";
+ FORMATS[FORMATS["LUMINANCE_ALPHA"] = 6410] = "LUMINANCE_ALPHA";
+ FORMATS[FORMATS["DEPTH_COMPONENT"] = 6402] = "DEPTH_COMPONENT";
+ FORMATS[FORMATS["DEPTH_STENCIL"] = 34041] = "DEPTH_STENCIL";
+ })(FORMATS$1 || (FORMATS$1 = {}));
+ /**
+ * Various GL target types.
+ * @memberof PIXI
+ * @static
+ * @name TARGETS
+ * @enum {number}
+ * @property {number} [TEXTURE_2D=3553] -
+ * @property {number} [TEXTURE_CUBE_MAP=34067] -
+ * @property {number} [TEXTURE_2D_ARRAY=35866] -
+ * @property {number} [TEXTURE_CUBE_MAP_POSITIVE_X=34069] -
+ * @property {number} [TEXTURE_CUBE_MAP_NEGATIVE_X=34070] -
+ * @property {number} [TEXTURE_CUBE_MAP_POSITIVE_Y=34071] -
+ * @property {number} [TEXTURE_CUBE_MAP_NEGATIVE_Y=34072] -
+ * @property {number} [TEXTURE_CUBE_MAP_POSITIVE_Z=34073] -
+ * @property {number} [TEXTURE_CUBE_MAP_NEGATIVE_Z=34074] -
+ */
+ var TARGETS$1;
+ (function (TARGETS) {
+ TARGETS[TARGETS["TEXTURE_2D"] = 3553] = "TEXTURE_2D";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP"] = 34067] = "TEXTURE_CUBE_MAP";
+ TARGETS[TARGETS["TEXTURE_2D_ARRAY"] = 35866] = "TEXTURE_2D_ARRAY";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_POSITIVE_X"] = 34069] = "TEXTURE_CUBE_MAP_POSITIVE_X";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_NEGATIVE_X"] = 34070] = "TEXTURE_CUBE_MAP_NEGATIVE_X";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_POSITIVE_Y"] = 34071] = "TEXTURE_CUBE_MAP_POSITIVE_Y";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_NEGATIVE_Y"] = 34072] = "TEXTURE_CUBE_MAP_NEGATIVE_Y";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_POSITIVE_Z"] = 34073] = "TEXTURE_CUBE_MAP_POSITIVE_Z";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_NEGATIVE_Z"] = 34074] = "TEXTURE_CUBE_MAP_NEGATIVE_Z";
+ })(TARGETS$1 || (TARGETS$1 = {}));
+ /**
+ * Various GL data format types.
+ * @memberof PIXI
+ * @static
+ * @name TYPES
+ * @enum {number}
+ * @property {number} [UNSIGNED_BYTE=5121] -
+ * @property {number} [UNSIGNED_SHORT=5123] -
+ * @property {number} [UNSIGNED_SHORT_5_6_5=33635] -
+ * @property {number} [UNSIGNED_SHORT_4_4_4_4=32819] -
+ * @property {number} [UNSIGNED_SHORT_5_5_5_1=32820] -
+ * @property {number} [UNSIGNED_INT=5125] -
+ * @property {number} [UNSIGNED_INT_10F_11F_11F_REV=35899] -
+ * @property {number} [UNSIGNED_INT_2_10_10_10_REV=33640] -
+ * @property {number} [UNSIGNED_INT_24_8=34042] -
+ * @property {number} [UNSIGNED_INT_5_9_9_9_REV=35902] -
+ * @property {number} [BYTE=5120] -
+ * @property {number} [SHORT=5122] -
+ * @property {number} [INT=5124] -
+ * @property {number} [FLOAT=5126] -
+ * @property {number} [FLOAT_32_UNSIGNED_INT_24_8_REV=36269] -
+ * @property {number} [HALF_FLOAT=36193] -
+ */
+ var TYPES$1;
+ (function (TYPES) {
+ TYPES[TYPES["UNSIGNED_BYTE"] = 5121] = "UNSIGNED_BYTE";
+ TYPES[TYPES["UNSIGNED_SHORT"] = 5123] = "UNSIGNED_SHORT";
+ TYPES[TYPES["UNSIGNED_SHORT_5_6_5"] = 33635] = "UNSIGNED_SHORT_5_6_5";
+ TYPES[TYPES["UNSIGNED_SHORT_4_4_4_4"] = 32819] = "UNSIGNED_SHORT_4_4_4_4";
+ TYPES[TYPES["UNSIGNED_SHORT_5_5_5_1"] = 32820] = "UNSIGNED_SHORT_5_5_5_1";
+ TYPES[TYPES["UNSIGNED_INT"] = 5125] = "UNSIGNED_INT";
+ TYPES[TYPES["UNSIGNED_INT_10F_11F_11F_REV"] = 35899] = "UNSIGNED_INT_10F_11F_11F_REV";
+ TYPES[TYPES["UNSIGNED_INT_2_10_10_10_REV"] = 33640] = "UNSIGNED_INT_2_10_10_10_REV";
+ TYPES[TYPES["UNSIGNED_INT_24_8"] = 34042] = "UNSIGNED_INT_24_8";
+ TYPES[TYPES["UNSIGNED_INT_5_9_9_9_REV"] = 35902] = "UNSIGNED_INT_5_9_9_9_REV";
+ TYPES[TYPES["BYTE"] = 5120] = "BYTE";
+ TYPES[TYPES["SHORT"] = 5122] = "SHORT";
+ TYPES[TYPES["INT"] = 5124] = "INT";
+ TYPES[TYPES["FLOAT"] = 5126] = "FLOAT";
+ TYPES[TYPES["FLOAT_32_UNSIGNED_INT_24_8_REV"] = 36269] = "FLOAT_32_UNSIGNED_INT_24_8_REV";
+ TYPES[TYPES["HALF_FLOAT"] = 36193] = "HALF_FLOAT";
+ })(TYPES$1 || (TYPES$1 = {}));
+ /**
+ * Various sampler types. Correspond to `sampler`, `isampler`, `usampler` GLSL types respectively.
+ * WebGL1 works only with FLOAT.
+ * @memberof PIXI
+ * @static
+ * @name SAMPLER_TYPES
+ * @enum {number}
+ * @property {number} [FLOAT=0] -
+ * @property {number} [INT=1] -
+ * @property {number} [UINT=2] -
+ */
+ var SAMPLER_TYPES$1;
+ (function (SAMPLER_TYPES) {
+ SAMPLER_TYPES[SAMPLER_TYPES["FLOAT"] = 0] = "FLOAT";
+ SAMPLER_TYPES[SAMPLER_TYPES["INT"] = 1] = "INT";
+ SAMPLER_TYPES[SAMPLER_TYPES["UINT"] = 2] = "UINT";
+ })(SAMPLER_TYPES$1 || (SAMPLER_TYPES$1 = {}));
+ /**
+ * The scale modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.SCALE_MODE} scale mode affects the default scaling mode of future operations.
+ * It can be re-assigned to either LINEAR or NEAREST, depending upon suitability.
+ * @memberof PIXI
+ * @static
+ * @name SCALE_MODES
+ * @enum {number}
+ * @property {number} LINEAR Smooth scaling
+ * @property {number} NEAREST Pixelating scaling
+ */
+ var SCALE_MODES$1;
+ (function (SCALE_MODES) {
+ SCALE_MODES[SCALE_MODES["NEAREST"] = 0] = "NEAREST";
+ SCALE_MODES[SCALE_MODES["LINEAR"] = 1] = "LINEAR";
+ })(SCALE_MODES$1 || (SCALE_MODES$1 = {}));
+ /**
+ * The wrap modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.WRAP_MODE} wrap mode affects the default wrapping mode of future operations.
+ * It can be re-assigned to either CLAMP or REPEAT, depending upon suitability.
+ * If the texture is non power of two then clamp will be used regardless as WebGL can
+ * only use REPEAT if the texture is po2.
+ *
+ * This property only affects WebGL.
+ * @name WRAP_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} CLAMP - The textures uvs are clamped
+ * @property {number} REPEAT - The texture uvs tile and repeat
+ * @property {number} MIRRORED_REPEAT - The texture uvs tile and repeat with mirroring
+ */
+ var WRAP_MODES$1;
+ (function (WRAP_MODES) {
+ WRAP_MODES[WRAP_MODES["CLAMP"] = 33071] = "CLAMP";
+ WRAP_MODES[WRAP_MODES["REPEAT"] = 10497] = "REPEAT";
+ WRAP_MODES[WRAP_MODES["MIRRORED_REPEAT"] = 33648] = "MIRRORED_REPEAT";
+ })(WRAP_MODES$1 || (WRAP_MODES$1 = {}));
+ /**
+ * Mipmap filtering modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.MIPMAP_TEXTURES} affects default texture filtering.
+ * Mipmaps are generated for a baseTexture if its `mipmap` field is `ON`,
+ * or its `POW2` and texture dimensions are powers of 2.
+ * Due to platform restriction, `ON` option will work like `POW2` for webgl-1.
+ *
+ * This property only affects WebGL.
+ * @name MIPMAP_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} OFF - No mipmaps
+ * @property {number} POW2 - Generate mipmaps if texture dimensions are pow2
+ * @property {number} ON - Always generate mipmaps
+ * @property {number} ON_MANUAL - Use mipmaps, but do not auto-generate them; this is used with a resource
+ * that supports buffering each level-of-detail.
+ */
+ var MIPMAP_MODES$1;
+ (function (MIPMAP_MODES) {
+ MIPMAP_MODES[MIPMAP_MODES["OFF"] = 0] = "OFF";
+ MIPMAP_MODES[MIPMAP_MODES["POW2"] = 1] = "POW2";
+ MIPMAP_MODES[MIPMAP_MODES["ON"] = 2] = "ON";
+ MIPMAP_MODES[MIPMAP_MODES["ON_MANUAL"] = 3] = "ON_MANUAL";
+ })(MIPMAP_MODES$1 || (MIPMAP_MODES$1 = {}));
+ /**
+ * How to treat textures with premultiplied alpha
+ * @name ALPHA_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} NO_PREMULTIPLIED_ALPHA - Source is not premultiplied, leave it like that.
+ * Option for compressed and data textures that are created from typed arrays.
+ * @property {number} PREMULTIPLY_ON_UPLOAD - Source is not premultiplied, premultiply on upload.
+ * Default option, used for all loaded images.
+ * @property {number} PREMULTIPLIED_ALPHA - Source is already premultiplied
+ * Example: spine atlases with `_pma` suffix.
+ * @property {number} NPM - Alias for NO_PREMULTIPLIED_ALPHA.
+ * @property {number} UNPACK - Default option, alias for PREMULTIPLY_ON_UPLOAD.
+ * @property {number} PMA - Alias for PREMULTIPLIED_ALPHA.
+ */
+ var ALPHA_MODES$1;
+ (function (ALPHA_MODES) {
+ ALPHA_MODES[ALPHA_MODES["NPM"] = 0] = "NPM";
+ ALPHA_MODES[ALPHA_MODES["UNPACK"] = 1] = "UNPACK";
+ ALPHA_MODES[ALPHA_MODES["PMA"] = 2] = "PMA";
+ ALPHA_MODES[ALPHA_MODES["NO_PREMULTIPLIED_ALPHA"] = 0] = "NO_PREMULTIPLIED_ALPHA";
+ ALPHA_MODES[ALPHA_MODES["PREMULTIPLY_ON_UPLOAD"] = 1] = "PREMULTIPLY_ON_UPLOAD";
+ ALPHA_MODES[ALPHA_MODES["PREMULTIPLY_ALPHA"] = 2] = "PREMULTIPLY_ALPHA";
+ ALPHA_MODES[ALPHA_MODES["PREMULTIPLIED_ALPHA"] = 2] = "PREMULTIPLIED_ALPHA";
+ })(ALPHA_MODES$1 || (ALPHA_MODES$1 = {}));
+ /**
+ * Configure whether filter textures are cleared after binding.
+ *
+ * Filter textures need not be cleared if the filter does not use pixel blending. {@link CLEAR_MODES.BLIT} will detect
+ * this and skip clearing as an optimization.
+ * @name CLEAR_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} BLEND - Do not clear the filter texture. The filter's output will blend on top of the output texture.
+ * @property {number} CLEAR - Always clear the filter texture.
+ * @property {number} BLIT - Clear only if {@link FilterSystem.forceClear} is set or if the filter uses pixel blending.
+ * @property {number} NO - Alias for BLEND, same as `false` in earlier versions
+ * @property {number} YES - Alias for CLEAR, same as `true` in earlier versions
+ * @property {number} AUTO - Alias for BLIT
+ */
+ var CLEAR_MODES$1;
+ (function (CLEAR_MODES) {
+ CLEAR_MODES[CLEAR_MODES["NO"] = 0] = "NO";
+ CLEAR_MODES[CLEAR_MODES["YES"] = 1] = "YES";
+ CLEAR_MODES[CLEAR_MODES["AUTO"] = 2] = "AUTO";
+ CLEAR_MODES[CLEAR_MODES["BLEND"] = 0] = "BLEND";
+ CLEAR_MODES[CLEAR_MODES["CLEAR"] = 1] = "CLEAR";
+ CLEAR_MODES[CLEAR_MODES["BLIT"] = 2] = "BLIT";
+ })(CLEAR_MODES$1 || (CLEAR_MODES$1 = {}));
+ /**
+ * The gc modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.GC_MODE} Garbage Collection mode for PixiJS textures is AUTO
+ * If set to GC_MODE, the renderer will occasionally check textures usage. If they are not
+ * used for a specified period of time they will be removed from the GPU. They will of course
+ * be uploaded again when they are required. This is a silent behind the scenes process that
+ * should ensure that the GPU does not get filled up.
+ *
+ * Handy for mobile devices!
+ * This property only affects WebGL.
+ * @name GC_MODES
+ * @enum {number}
+ * @static
+ * @memberof PIXI
+ * @property {number} AUTO - Garbage collection will happen periodically automatically
+ * @property {number} MANUAL - Garbage collection will need to be called manually
+ */
+ var GC_MODES$1;
+ (function (GC_MODES) {
+ GC_MODES[GC_MODES["AUTO"] = 0] = "AUTO";
+ GC_MODES[GC_MODES["MANUAL"] = 1] = "MANUAL";
+ })(GC_MODES$1 || (GC_MODES$1 = {}));
+ /**
+ * Constants that specify float precision in shaders.
+ * @name PRECISION
+ * @memberof PIXI
+ * @constant
+ * @static
+ * @enum {string}
+ * @property {string} [LOW='lowp'] -
+ * @property {string} [MEDIUM='mediump'] -
+ * @property {string} [HIGH='highp'] -
+ */
+ var PRECISION$1;
+ (function (PRECISION) {
+ PRECISION["LOW"] = "lowp";
+ PRECISION["MEDIUM"] = "mediump";
+ PRECISION["HIGH"] = "highp";
+ })(PRECISION$1 || (PRECISION$1 = {}));
+ /**
+ * Constants for mask implementations.
+ * We use `type` suffix because it leads to very different behaviours
+ * @name MASK_TYPES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} NONE - Mask is ignored
+ * @property {number} SCISSOR - Scissor mask, rectangle on screen, cheap
+ * @property {number} STENCIL - Stencil mask, 1-bit, medium, works only if renderer supports stencil
+ * @property {number} SPRITE - Mask that uses SpriteMaskFilter, uses temporary RenderTexture
+ * @property {number} COLOR - Color mask (RGBA)
+ */
+ var MASK_TYPES$1;
+ (function (MASK_TYPES) {
+ MASK_TYPES[MASK_TYPES["NONE"] = 0] = "NONE";
+ MASK_TYPES[MASK_TYPES["SCISSOR"] = 1] = "SCISSOR";
+ MASK_TYPES[MASK_TYPES["STENCIL"] = 2] = "STENCIL";
+ MASK_TYPES[MASK_TYPES["SPRITE"] = 3] = "SPRITE";
+ MASK_TYPES[MASK_TYPES["COLOR"] = 4] = "COLOR";
+ })(MASK_TYPES$1 || (MASK_TYPES$1 = {}));
+ /**
+ * Bitwise OR of masks that indicate the color channels that are rendered to.
+ * @static
+ * @memberof PIXI
+ * @name COLOR_MASK_BITS
+ * @enum {number}
+ * @property {number} RED - Red channel.
+ * @property {number} GREEN - Green channel
+ * @property {number} BLUE - Blue channel.
+ * @property {number} ALPHA - Alpha channel.
+ */
+ var COLOR_MASK_BITS$1;
+ (function (COLOR_MASK_BITS) {
+ COLOR_MASK_BITS[COLOR_MASK_BITS["RED"] = 1] = "RED";
+ COLOR_MASK_BITS[COLOR_MASK_BITS["GREEN"] = 2] = "GREEN";
+ COLOR_MASK_BITS[COLOR_MASK_BITS["BLUE"] = 4] = "BLUE";
+ COLOR_MASK_BITS[COLOR_MASK_BITS["ALPHA"] = 8] = "ALPHA";
+ })(COLOR_MASK_BITS$1 || (COLOR_MASK_BITS$1 = {}));
+ /**
+ * Constants for multi-sampling antialiasing.
+ * @see PIXI.Framebuffer#multisample
+ * @name MSAA_QUALITY
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} NONE - No multisampling for this renderTexture
+ * @property {number} LOW - Try 2 samples
+ * @property {number} MEDIUM - Try 4 samples
+ * @property {number} HIGH - Try 8 samples
+ */
+ var MSAA_QUALITY$1;
+ (function (MSAA_QUALITY) {
+ MSAA_QUALITY[MSAA_QUALITY["NONE"] = 0] = "NONE";
+ MSAA_QUALITY[MSAA_QUALITY["LOW"] = 2] = "LOW";
+ MSAA_QUALITY[MSAA_QUALITY["MEDIUM"] = 4] = "MEDIUM";
+ MSAA_QUALITY[MSAA_QUALITY["HIGH"] = 8] = "HIGH";
+ })(MSAA_QUALITY$1 || (MSAA_QUALITY$1 = {}));
+ /**
+ * Constants for various buffer types in Pixi
+ * @see PIXI.BUFFER_TYPE
+ * @name BUFFER_TYPE
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} ELEMENT_ARRAY_BUFFER - buffer type for using as an index buffer
+ * @property {number} ARRAY_BUFFER - buffer type for using attribute data
+ * @property {number} UNIFORM_BUFFER - the buffer type is for uniform buffer objects
+ */
+ var BUFFER_TYPE$1;
+ (function (BUFFER_TYPE) {
+ BUFFER_TYPE[BUFFER_TYPE["ELEMENT_ARRAY_BUFFER"] = 34963] = "ELEMENT_ARRAY_BUFFER";
+ BUFFER_TYPE[BUFFER_TYPE["ARRAY_BUFFER"] = 34962] = "ARRAY_BUFFER";
+ // NOT YET SUPPORTED
+ BUFFER_TYPE[BUFFER_TYPE["UNIFORM_BUFFER"] = 35345] = "UNIFORM_BUFFER";
+ })(BUFFER_TYPE$1 || (BUFFER_TYPE$1 = {}));
+
+ var BrowserAdapter = {
+ /**
+ * Creates a canvas element of the given size.
+ * This canvas is created using the browser's native canvas element.
+ * @param width - width of the canvas
+ * @param height - height of the canvas
+ */
+ createCanvas: function (width, height) {
+ var canvas = document.createElement('canvas');
+ canvas.width = width;
+ canvas.height = height;
+ return canvas;
+ },
+ getWebGLRenderingContext: function () { return WebGLRenderingContext; },
+ getNavigator: function () { return navigator; },
+ getBaseUrl: function () { var _a; return ((_a = document.baseURI) !== null && _a !== void 0 ? _a : window.location.href); },
+ fetch: function (url, options) { return fetch(url, options); },
+ };
+
+ var appleIphone = /iPhone/i;
+ var appleIpod = /iPod/i;
+ var appleTablet = /iPad/i;
+ var appleUniversal = /\biOS-universal(?:.+)Mac\b/i;
+ var androidPhone = /\bAndroid(?:.+)Mobile\b/i;
+ var androidTablet = /Android/i;
+ var amazonPhone = /(?:SD4930UR|\bSilk(?:.+)Mobile\b)/i;
+ var amazonTablet = /Silk/i;
+ var windowsPhone = /Windows Phone/i;
+ var windowsTablet = /\bWindows(?:.+)ARM\b/i;
+ var otherBlackBerry = /BlackBerry/i;
+ var otherBlackBerry10 = /BB10/i;
+ var otherOpera = /Opera Mini/i;
+ var otherChrome = /\b(CriOS|Chrome)(?:.+)Mobile/i;
+ var otherFirefox = /Mobile(?:.+)Firefox\b/i;
+ var isAppleTabletOnIos13 = function (navigator) {
+ return (typeof navigator !== 'undefined' &&
+ navigator.platform === 'MacIntel' &&
+ typeof navigator.maxTouchPoints === 'number' &&
+ navigator.maxTouchPoints > 1 &&
+ typeof MSStream === 'undefined');
+ };
+ function createMatch(userAgent) {
+ return function (regex) { return regex.test(userAgent); };
+ }
+ function isMobile$1(param) {
+ var nav = {
+ userAgent: '',
+ platform: '',
+ maxTouchPoints: 0
+ };
+ if (!param && typeof navigator !== 'undefined') {
+ nav = {
+ userAgent: navigator.userAgent,
+ platform: navigator.platform,
+ maxTouchPoints: navigator.maxTouchPoints || 0
+ };
+ }
+ else if (typeof param === 'string') {
+ nav.userAgent = param;
+ }
+ else if (param && param.userAgent) {
+ nav = {
+ userAgent: param.userAgent,
+ platform: param.platform,
+ maxTouchPoints: param.maxTouchPoints || 0
+ };
+ }
+ var userAgent = nav.userAgent;
+ var tmp = userAgent.split('[FBAN');
+ if (typeof tmp[1] !== 'undefined') {
+ userAgent = tmp[0];
+ }
+ tmp = userAgent.split('Twitter');
+ if (typeof tmp[1] !== 'undefined') {
+ userAgent = tmp[0];
+ }
+ var match = createMatch(userAgent);
+ var result = {
+ apple: {
+ phone: match(appleIphone) && !match(windowsPhone),
+ ipod: match(appleIpod),
+ tablet: !match(appleIphone) &&
+ (match(appleTablet) || isAppleTabletOnIos13(nav)) &&
+ !match(windowsPhone),
+ universal: match(appleUniversal),
+ device: (match(appleIphone) ||
+ match(appleIpod) ||
+ match(appleTablet) ||
+ match(appleUniversal) ||
+ isAppleTabletOnIos13(nav)) &&
+ !match(windowsPhone)
+ },
+ amazon: {
+ phone: match(amazonPhone),
+ tablet: !match(amazonPhone) && match(amazonTablet),
+ device: match(amazonPhone) || match(amazonTablet)
+ },
+ android: {
+ phone: (!match(windowsPhone) && match(amazonPhone)) ||
+ (!match(windowsPhone) && match(androidPhone)),
+ tablet: !match(windowsPhone) &&
+ !match(amazonPhone) &&
+ !match(androidPhone) &&
+ (match(amazonTablet) || match(androidTablet)),
+ device: (!match(windowsPhone) &&
+ (match(amazonPhone) ||
+ match(amazonTablet) ||
+ match(androidPhone) ||
+ match(androidTablet))) ||
+ match(/\bokhttp\b/i)
+ },
+ windows: {
+ phone: match(windowsPhone),
+ tablet: match(windowsTablet),
+ device: match(windowsPhone) || match(windowsTablet)
+ },
+ other: {
+ blackberry: match(otherBlackBerry),
+ blackberry10: match(otherBlackBerry10),
+ opera: match(otherOpera),
+ firefox: match(otherFirefox),
+ chrome: match(otherChrome),
+ device: match(otherBlackBerry) ||
+ match(otherBlackBerry10) ||
+ match(otherOpera) ||
+ match(otherFirefox) ||
+ match(otherChrome)
+ },
+ any: false,
+ phone: false,
+ tablet: false
+ };
+ result.any =
+ result.apple.device ||
+ result.android.device ||
+ result.windows.device ||
+ result.other.device;
+ result.phone =
+ result.apple.phone || result.android.phone || result.windows.phone;
+ result.tablet =
+ result.apple.tablet || result.android.tablet || result.windows.tablet;
+ return result;
+ }
+
+ var isMobile = isMobile$1(globalThis.navigator);
+
+ /**
+ * Uploading the same buffer multiple times in a single frame can cause performance issues.
+ * Apparent on iOS so only check for that at the moment
+ * This check may become more complex if this issue pops up elsewhere.
+ * @private
+ * @returns {boolean} `true` if the same buffer may be uploaded more than once.
+ */
+ function canUploadSameBuffer() {
+ return !isMobile.apple.device;
+ }
+
+ /**
+ * The maximum recommended texture units to use.
+ * In theory the bigger the better, and for desktop we'll use as many as we can.
+ * But some mobile devices slow down if there is to many branches in the shader.
+ * So in practice there seems to be a sweet spot size that varies depending on the device.
+ *
+ * In v4, all mobile devices were limited to 4 texture units because for this.
+ * In v5, we allow all texture units to be used on modern Apple or Android devices.
+ * @private
+ * @param {number} max
+ * @returns {number} The maximum recommended texture units to use.
+ */
+ function maxRecommendedTextures(max) {
+ var allowMax = true;
+ if (isMobile.tablet || isMobile.phone) {
+ if (isMobile.apple.device) {
+ var match = (navigator.userAgent).match(/OS (\d+)_(\d+)?/);
+ if (match) {
+ var majorVersion = parseInt(match[1], 10);
+ // Limit texture units on devices below iOS 11, which will be older hardware
+ if (majorVersion < 11) {
+ allowMax = false;
+ }
+ }
+ }
+ if (isMobile.android.device) {
+ var match = (navigator.userAgent).match(/Android\s([0-9.]*)/);
+ if (match) {
+ var majorVersion = parseInt(match[1], 10);
+ // Limit texture units on devices below Android 7 (Nougat), which will be older hardware
+ if (majorVersion < 7) {
+ allowMax = false;
+ }
+ }
+ }
+ }
+ return allowMax ? max : 4;
+ }
+
+ /**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float precision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio;
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
+ * @namespace PIXI.settings
+ */
+ var settings = {
+ /**
+ * This adapter is used to call methods that are platform dependent.
+ * For example `document.createElement` only runs on the web but fails in node environments.
+ * This allows us to support more platforms by abstracting away specific implementations per platform.
+ *
+ * By default the adapter is set to work in the browser. However you can create your own
+ * by implementing the `IAdapter` interface. See `IAdapter` for more information.
+ * @name ADAPTER
+ * @memberof PIXI.settings
+ * @type {PIXI.IAdapter}
+ * @default PIXI.BrowserAdapter
+ */
+ ADAPTER: BrowserAdapter,
+ /**
+ * If set to true WebGL will attempt make textures mimpaped by default.
+ * Mipmapping will only succeed if the base texture uploaded has power of two dimensions.
+ * @static
+ * @name MIPMAP_TEXTURES
+ * @memberof PIXI.settings
+ * @type {PIXI.MIPMAP_MODES}
+ * @default PIXI.MIPMAP_MODES.POW2
+ */
+ MIPMAP_TEXTURES: MIPMAP_MODES$1.POW2,
+ /**
+ * Default anisotropic filtering level of textures.
+ * Usually from 0 to 16
+ * @static
+ * @name ANISOTROPIC_LEVEL
+ * @memberof PIXI.settings
+ * @type {number}
+ * @default 0
+ */
+ ANISOTROPIC_LEVEL: 0,
+ /**
+ * Default resolution / device pixel ratio of the renderer.
+ * @static
+ * @name RESOLUTION
+ * @memberof PIXI.settings
+ * @type {number}
+ * @default 1
+ */
+ RESOLUTION: 1,
+ /**
+ * Default filter resolution.
+ * @static
+ * @name FILTER_RESOLUTION
+ * @memberof PIXI.settings
+ * @type {number}
+ * @default 1
+ */
+ FILTER_RESOLUTION: 1,
+ /**
+ * Default filter samples.
+ * @static
+ * @name FILTER_MULTISAMPLE
+ * @memberof PIXI.settings
+ * @type {PIXI.MSAA_QUALITY}
+ * @default PIXI.MSAA_QUALITY.NONE
+ */
+ FILTER_MULTISAMPLE: MSAA_QUALITY$1.NONE,
+ /**
+ * The maximum textures that this device supports.
+ * @static
+ * @name SPRITE_MAX_TEXTURES
+ * @memberof PIXI.settings
+ * @type {number}
+ * @default 32
+ */
+ SPRITE_MAX_TEXTURES: maxRecommendedTextures(32),
+ // TODO: maybe change to SPRITE.BATCH_SIZE: 2000
+ // TODO: maybe add PARTICLE.BATCH_SIZE: 15000
+ /**
+ * The default sprite batch size.
+ *
+ * The default aims to balance desktop and mobile devices.
+ * @static
+ * @name SPRITE_BATCH_SIZE
+ * @memberof PIXI.settings
+ * @type {number}
+ * @default 4096
+ */
+ SPRITE_BATCH_SIZE: 4096,
+ /**
+ * The default render options if none are supplied to {@link PIXI.Renderer}
+ * or {@link PIXI.CanvasRenderer}.
+ * @static
+ * @name RENDER_OPTIONS
+ * @memberof PIXI.settings
+ * @type {object}
+ * @property {HTMLCanvasElement} [view=null] -
+ * @property {boolean} [antialias=false] -
+ * @property {boolean} [autoDensity=false] -
+ * @property {boolean} [useContextAlpha=true] -
+ * @property {number} [backgroundColor=0x000000] -
+ * @property {number} [backgroundAlpha=1] -
+ * @property {boolean} [clearBeforeRender=true] -
+ * @property {boolean} [preserveDrawingBuffer=false] -
+ * @property {number} [width=800] -
+ * @property {number} [height=600] -
+ * @property {boolean} [legacy=false] -
+ */
+ RENDER_OPTIONS: {
+ view: null,
+ antialias: false,
+ autoDensity: false,
+ backgroundColor: 0x000000,
+ backgroundAlpha: 1,
+ useContextAlpha: true,
+ clearBeforeRender: true,
+ preserveDrawingBuffer: false,
+ width: 800,
+ height: 600,
+ legacy: false,
+ },
+ /**
+ * Default Garbage Collection mode.
+ * @static
+ * @name GC_MODE
+ * @memberof PIXI.settings
+ * @type {PIXI.GC_MODES}
+ * @default PIXI.GC_MODES.AUTO
+ */
+ GC_MODE: GC_MODES$1.AUTO,
+ /**
+ * Default Garbage Collection max idle.
+ * @static
+ * @name GC_MAX_IDLE
+ * @memberof PIXI.settings
+ * @type {number}
+ * @default 3600
+ */
+ GC_MAX_IDLE: 60 * 60,
+ /**
+ * Default Garbage Collection maximum check count.
+ * @static
+ * @name GC_MAX_CHECK_COUNT
+ * @memberof PIXI.settings
+ * @type {number}
+ * @default 600
+ */
+ GC_MAX_CHECK_COUNT: 60 * 10,
+ /**
+ * Default wrap modes that are supported by pixi.
+ * @static
+ * @name WRAP_MODE
+ * @memberof PIXI.settings
+ * @type {PIXI.WRAP_MODES}
+ * @default PIXI.WRAP_MODES.CLAMP
+ */
+ WRAP_MODE: WRAP_MODES$1.CLAMP,
+ /**
+ * Default scale mode for textures.
+ * @static
+ * @name SCALE_MODE
+ * @memberof PIXI.settings
+ * @type {PIXI.SCALE_MODES}
+ * @default PIXI.SCALE_MODES.LINEAR
+ */
+ SCALE_MODE: SCALE_MODES$1.LINEAR,
+ /**
+ * Default specify float precision in vertex shader.
+ * @static
+ * @name PRECISION_VERTEX
+ * @memberof PIXI.settings
+ * @type {PIXI.PRECISION}
+ * @default PIXI.PRECISION.HIGH
+ */
+ PRECISION_VERTEX: PRECISION$1.HIGH,
+ /**
+ * Default specify float precision in fragment shader.
+ * iOS is best set at highp due to https://github.com/pixijs/pixi.js/issues/3742
+ * @static
+ * @name PRECISION_FRAGMENT
+ * @memberof PIXI.settings
+ * @type {PIXI.PRECISION}
+ * @default PIXI.PRECISION.MEDIUM
+ */
+ PRECISION_FRAGMENT: isMobile.apple.device ? PRECISION$1.HIGH : PRECISION$1.MEDIUM,
+ /**
+ * Can we upload the same buffer in a single frame?
+ * @static
+ * @name CAN_UPLOAD_SAME_BUFFER
+ * @memberof PIXI.settings
+ * @type {boolean}
+ */
+ CAN_UPLOAD_SAME_BUFFER: canUploadSameBuffer(),
+ /**
+ * Enables bitmap creation before image load. This feature is experimental.
+ * @static
+ * @name CREATE_IMAGE_BITMAP
+ * @memberof PIXI.settings
+ * @type {boolean}
+ * @default false
+ */
+ CREATE_IMAGE_BITMAP: false,
+ /**
+ * If true PixiJS will Math.floor() x/y values when rendering, stopping pixel interpolation.
+ * Advantages can include sharper image quality (like text) and faster rendering on canvas.
+ * The main disadvantage is movement of objects may appear less smooth.
+ * @static
+ * @constant
+ * @memberof PIXI.settings
+ * @type {boolean}
+ * @default false
+ */
+ ROUND_PIXELS: false,
+ };
+
+ var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
+
+ function createCommonjsModule(fn, basedir, module) {
+ return module = {
+ path: basedir,
+ exports: {},
+ require: function (path, base) {
+ return commonjsRequire(path, (base === undefined || base === null) ? module.path : base);
+ }
+ }, fn(module, module.exports), module.exports;
+ }
+
+ function commonjsRequire () {
+ throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs');
+ }
+
+ createCommonjsModule(function (module) {
+
+ var has = Object.prototype.hasOwnProperty
+ , prefix = '~';
+
+ /**
+ * Constructor to create a storage for our `EE` objects.
+ * An `Events` instance is a plain object whose properties are event names.
+ *
+ * @constructor
+ * @private
+ */
+ function Events() {}
+
+ //
+ // We try to not inherit from `Object.prototype`. In some engines creating an
+ // instance in this way is faster than calling `Object.create(null)` directly.
+ // If `Object.create(null)` is not supported we prefix the event names with a
+ // character to make sure that the built-in object properties are not
+ // overridden or used as an attack vector.
+ //
+ if (Object.create) {
+ Events.prototype = Object.create(null);
+
+ //
+ // This hack is needed because the `__proto__` property is still inherited in
+ // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.
+ //
+ if (!new Events().__proto__) { prefix = false; }
+ }
+
+ /**
+ * Representation of a single event listener.
+ *
+ * @param {Function} fn The listener function.
+ * @param {*} context The context to invoke the listener with.
+ * @param {Boolean} [once=false] Specify if the listener is a one-time listener.
+ * @constructor
+ * @private
+ */
+ function EE(fn, context, once) {
+ this.fn = fn;
+ this.context = context;
+ this.once = once || false;
+ }
+
+ /**
+ * Add a listener for a given event.
+ *
+ * @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
+ * @param {(String|Symbol)} event The event name.
+ * @param {Function} fn The listener function.
+ * @param {*} context The context to invoke the listener with.
+ * @param {Boolean} once Specify if the listener is a one-time listener.
+ * @returns {EventEmitter}
+ * @private
+ */
+ function addListener(emitter, event, fn, context, once) {
+ if (typeof fn !== 'function') {
+ throw new TypeError('The listener must be a function');
+ }
+
+ var listener = new EE(fn, context || emitter, once)
+ , evt = prefix ? prefix + event : event;
+
+ if (!emitter._events[evt]) { emitter._events[evt] = listener, emitter._eventsCount++; }
+ else if (!emitter._events[evt].fn) { emitter._events[evt].push(listener); }
+ else { emitter._events[evt] = [emitter._events[evt], listener]; }
+
+ return emitter;
+ }
+
+ /**
+ * Clear event by name.
+ *
+ * @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
+ * @param {(String|Symbol)} evt The Event name.
+ * @private
+ */
+ function clearEvent(emitter, evt) {
+ if (--emitter._eventsCount === 0) { emitter._events = new Events(); }
+ else { delete emitter._events[evt]; }
+ }
+
+ /**
+ * Minimal `EventEmitter` interface that is molded against the Node.js
+ * `EventEmitter` interface.
+ *
+ * @constructor
+ * @public
+ */
+ function EventEmitter() {
+ this._events = new Events();
+ this._eventsCount = 0;
+ }
+
+ /**
+ * Return an array listing the events for which the emitter has registered
+ * listeners.
+ *
+ * @returns {Array}
+ * @public
+ */
+ EventEmitter.prototype.eventNames = function eventNames() {
+ var names = []
+ , events
+ , name;
+
+ if (this._eventsCount === 0) { return names; }
+
+ for (name in (events = this._events)) {
+ if (has.call(events, name)) { names.push(prefix ? name.slice(1) : name); }
+ }
+
+ if (Object.getOwnPropertySymbols) {
+ return names.concat(Object.getOwnPropertySymbols(events));
+ }
+
+ return names;
+ };
+
+ /**
+ * Return the listeners registered for a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @returns {Array} The registered listeners.
+ * @public
+ */
+ EventEmitter.prototype.listeners = function listeners(event) {
+ var evt = prefix ? prefix + event : event
+ , handlers = this._events[evt];
+
+ if (!handlers) { return []; }
+ if (handlers.fn) { return [handlers.fn]; }
+
+ for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {
+ ee[i] = handlers[i].fn;
+ }
+
+ return ee;
+ };
+
+ /**
+ * Return the number of listeners listening to a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @returns {Number} The number of listeners.
+ * @public
+ */
+ EventEmitter.prototype.listenerCount = function listenerCount(event) {
+ var evt = prefix ? prefix + event : event
+ , listeners = this._events[evt];
+
+ if (!listeners) { return 0; }
+ if (listeners.fn) { return 1; }
+ return listeners.length;
+ };
+
+ /**
+ * Calls each of the listeners registered for a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @returns {Boolean} `true` if the event had listeners, else `false`.
+ * @public
+ */
+ EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
+ var arguments$1 = arguments;
+
+ var evt = prefix ? prefix + event : event;
+
+ if (!this._events[evt]) { return false; }
+
+ var listeners = this._events[evt]
+ , len = arguments.length
+ , args
+ , i;
+
+ if (listeners.fn) {
+ if (listeners.once) { this.removeListener(event, listeners.fn, undefined, true); }
+
+ switch (len) {
+ case 1: return listeners.fn.call(listeners.context), true;
+ case 2: return listeners.fn.call(listeners.context, a1), true;
+ case 3: return listeners.fn.call(listeners.context, a1, a2), true;
+ case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;
+ case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
+ case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
+ }
+
+ for (i = 1, args = new Array(len -1); i < len; i++) {
+ args[i - 1] = arguments$1[i];
+ }
+
+ listeners.fn.apply(listeners.context, args);
+ } else {
+ var length = listeners.length
+ , j;
+
+ for (i = 0; i < length; i++) {
+ if (listeners[i].once) { this.removeListener(event, listeners[i].fn, undefined, true); }
+
+ switch (len) {
+ case 1: listeners[i].fn.call(listeners[i].context); break;
+ case 2: listeners[i].fn.call(listeners[i].context, a1); break;
+ case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break;
+ case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break;
+ default:
+ if (!args) { for (j = 1, args = new Array(len -1); j < len; j++) {
+ args[j - 1] = arguments$1[j];
+ } }
+
+ listeners[i].fn.apply(listeners[i].context, args);
+ }
+ }
+ }
+
+ return true;
+ };
+
+ /**
+ * Add a listener for a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @param {Function} fn The listener function.
+ * @param {*} [context=this] The context to invoke the listener with.
+ * @returns {EventEmitter} `this`.
+ * @public
+ */
+ EventEmitter.prototype.on = function on(event, fn, context) {
+ return addListener(this, event, fn, context, false);
+ };
+
+ /**
+ * Add a one-time listener for a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @param {Function} fn The listener function.
+ * @param {*} [context=this] The context to invoke the listener with.
+ * @returns {EventEmitter} `this`.
+ * @public
+ */
+ EventEmitter.prototype.once = function once(event, fn, context) {
+ return addListener(this, event, fn, context, true);
+ };
+
+ /**
+ * Remove the listeners of a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @param {Function} fn Only remove the listeners that match this function.
+ * @param {*} context Only remove the listeners that have this context.
+ * @param {Boolean} once Only remove one-time listeners.
+ * @returns {EventEmitter} `this`.
+ * @public
+ */
+ EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {
+ var evt = prefix ? prefix + event : event;
+
+ if (!this._events[evt]) { return this; }
+ if (!fn) {
+ clearEvent(this, evt);
+ return this;
+ }
+
+ var listeners = this._events[evt];
+
+ if (listeners.fn) {
+ if (
+ listeners.fn === fn &&
+ (!once || listeners.once) &&
+ (!context || listeners.context === context)
+ ) {
+ clearEvent(this, evt);
+ }
+ } else {
+ for (var i = 0, events = [], length = listeners.length; i < length; i++) {
+ if (
+ listeners[i].fn !== fn ||
+ (once && !listeners[i].once) ||
+ (context && listeners[i].context !== context)
+ ) {
+ events.push(listeners[i]);
+ }
+ }
+
+ //
+ // Reset the array, or remove it completely if we have no more listeners.
+ //
+ if (events.length) { this._events[evt] = events.length === 1 ? events[0] : events; }
+ else { clearEvent(this, evt); }
+ }
+
+ return this;
+ };
+
+ /**
+ * Remove all listeners, or those of the specified event.
+ *
+ * @param {(String|Symbol)} [event] The event name.
+ * @returns {EventEmitter} `this`.
+ * @public
+ */
+ EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
+ var evt;
+
+ if (event) {
+ evt = prefix ? prefix + event : event;
+ if (this._events[evt]) { clearEvent(this, evt); }
+ } else {
+ this._events = new Events();
+ this._eventsCount = 0;
+ }
+
+ return this;
+ };
+
+ //
+ // Alias methods names because people roll like that.
+ //
+ EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
+ EventEmitter.prototype.addListener = EventEmitter.prototype.on;
+
+ //
+ // Expose the prefix.
+ //
+ EventEmitter.prefixed = prefix;
+
+ //
+ // Allow `EventEmitter` to be imported as module namespace.
+ //
+ EventEmitter.EventEmitter = EventEmitter;
+
+ //
+ // Expose the module.
+ //
+ {
+ module.exports = EventEmitter;
+ }
+ });
+
+ createCommonjsModule(function (module, exports) {
+ (function(root) {
+
+ /** Detect free variables */
+ var freeExports = exports &&
+ !exports.nodeType && exports;
+ var freeModule = module &&
+ !module.nodeType && module;
+ var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal;
+ if (
+ freeGlobal.global === freeGlobal ||
+ freeGlobal.window === freeGlobal ||
+ freeGlobal.self === freeGlobal
+ ) {
+ root = freeGlobal;
+ }
+
+ /**
+ * The `punycode` object.
+ * @name punycode
+ * @type Object
+ */
+ var punycode,
+
+ /** Highest positive signed 32-bit float value */
+ maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1
+
+ /** Bootstring parameters */
+ base = 36,
+ tMin = 1,
+ tMax = 26,
+ skew = 38,
+ damp = 700,
+ initialBias = 72,
+ initialN = 128, // 0x80
+ delimiter = '-', // '\x2D'
+
+ /** Regular expressions */
+ regexPunycode = /^xn--/,
+ regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars
+ regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators
+
+ /** Error messages */
+ errors = {
+ 'overflow': 'Overflow: input needs wider integers to process',
+ 'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
+ 'invalid-input': 'Invalid input'
+ },
+
+ /** Convenience shortcuts */
+ baseMinusTMin = base - tMin,
+ floor = Math.floor,
+ stringFromCharCode = String.fromCharCode,
+
+ /** Temporary variable */
+ key;
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * A generic error utility function.
+ * @private
+ * @param {String} type The error type.
+ * @returns {Error} Throws a `RangeError` with the applicable error message.
+ */
+ function error(type) {
+ throw RangeError(errors[type]);
+ }
+
+ /**
+ * A generic `Array#map` utility function.
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} callback The function that gets called for every array
+ * item.
+ * @returns {Array} A new array of values returned by the callback function.
+ */
+ function map(array, fn) {
+ var length = array.length;
+ var result = [];
+ while (length--) {
+ result[length] = fn(array[length]);
+ }
+ return result;
+ }
+
+ /**
+ * A simple `Array#map`-like wrapper to work with domain name strings or email
+ * addresses.
+ * @private
+ * @param {String} domain The domain name or email address.
+ * @param {Function} callback The function that gets called for every
+ * character.
+ * @returns {Array} A new string of characters returned by the callback
+ * function.
+ */
+ function mapDomain(string, fn) {
+ var parts = string.split('@');
+ var result = '';
+ if (parts.length > 1) {
+ // In email addresses, only the domain name should be punycoded. Leave
+ // the local part (i.e. everything up to `@`) intact.
+ result = parts[0] + '@';
+ string = parts[1];
+ }
+ // Avoid `split(regex)` for IE8 compatibility. See #17.
+ string = string.replace(regexSeparators, '\x2E');
+ var labels = string.split('.');
+ var encoded = map(labels, fn).join('.');
+ return result + encoded;
+ }
+
+ /**
+ * Creates an array containing the numeric code points of each Unicode
+ * character in the string. While JavaScript uses UCS-2 internally,
+ * this function will convert a pair of surrogate halves (each of which
+ * UCS-2 exposes as separate characters) into a single code point,
+ * matching UTF-16.
+ * @see `punycode.ucs2.encode`
+ * @see <https://mathiasbynens.be/notes/javascript-encoding>
+ * @memberOf punycode.ucs2
+ * @name decode
+ * @param {String} string The Unicode input string (UCS-2).
+ * @returns {Array} The new array of code points.
+ */
+ function ucs2decode(string) {
+ var output = [],
+ counter = 0,
+ length = string.length,
+ value,
+ extra;
+ while (counter < length) {
+ value = string.charCodeAt(counter++);
+ if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
+ // high surrogate, and there is a next character
+ extra = string.charCodeAt(counter++);
+ if ((extra & 0xFC00) == 0xDC00) { // low surrogate
+ output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
+ } else {
+ // unmatched surrogate; only append this code unit, in case the next
+ // code unit is the high surrogate of a surrogate pair
+ output.push(value);
+ counter--;
+ }
+ } else {
+ output.push(value);
+ }
+ }
+ return output;
+ }
+
+ /**
+ * Creates a string based on an array of numeric code points.
+ * @see `punycode.ucs2.decode`
+ * @memberOf punycode.ucs2
+ * @name encode
+ * @param {Array} codePoints The array of numeric code points.
+ * @returns {String} The new Unicode string (UCS-2).
+ */
+ function ucs2encode(array) {
+ return map(array, function(value) {
+ var output = '';
+ if (value > 0xFFFF) {
+ value -= 0x10000;
+ output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
+ value = 0xDC00 | value & 0x3FF;
+ }
+ output += stringFromCharCode(value);
+ return output;
+ }).join('');
+ }
+
+ /**
+ * Converts a basic code point into a digit/integer.
+ * @see `digitToBasic()`
+ * @private
+ * @param {Number} codePoint The basic numeric code point value.
+ * @returns {Number} The numeric value of a basic code point (for use in
+ * representing integers) in the range `0` to `base - 1`, or `base` if
+ * the code point does not represent a value.
+ */
+ function basicToDigit(codePoint) {
+ if (codePoint - 48 < 10) {
+ return codePoint - 22;
+ }
+ if (codePoint - 65 < 26) {
+ return codePoint - 65;
+ }
+ if (codePoint - 97 < 26) {
+ return codePoint - 97;
+ }
+ return base;
+ }
+
+ /**
+ * Converts a digit/integer into a basic code point.
+ * @see `basicToDigit()`
+ * @private
+ * @param {Number} digit The numeric value of a basic code point.
+ * @returns {Number} The basic code point whose value (when used for
+ * representing integers) is `digit`, which needs to be in the range
+ * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
+ * used; else, the lowercase form is used. The behavior is undefined
+ * if `flag` is non-zero and `digit` has no uppercase form.
+ */
+ function digitToBasic(digit, flag) {
+ // 0..25 map to ASCII a..z or A..Z
+ // 26..35 map to ASCII 0..9
+ return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
+ }
+
+ /**
+ * Bias adaptation function as per section 3.4 of RFC 3492.
+ * http://tools.ietf.org/html/rfc3492#section-3.4
+ * @private
+ */
+ function adapt(delta, numPoints, firstTime) {
+ var k = 0;
+ delta = firstTime ? floor(delta / damp) : delta >> 1;
+ delta += floor(delta / numPoints);
+ for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
+ delta = floor(delta / baseMinusTMin);
+ }
+ return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
+ }
+
+ /**
+ * Converts a Punycode string of ASCII-only symbols to a string of Unicode
+ * symbols.
+ * @memberOf punycode
+ * @param {String} input The Punycode string of ASCII-only symbols.
+ * @returns {String} The resulting string of Unicode symbols.
+ */
+ function decode(input) {
+ // Don't use UCS-2
+ var output = [],
+ inputLength = input.length,
+ out,
+ i = 0,
+ n = initialN,
+ bias = initialBias,
+ basic,
+ j,
+ index,
+ oldi,
+ w,
+ k,
+ digit,
+ t,
+ /** Cached calculation results */
+ baseMinusT;
+
+ // Handle the basic code points: let `basic` be the number of input code
+ // points before the last delimiter, or `0` if there is none, then copy
+ // the first basic code points to the output.
+
+ basic = input.lastIndexOf(delimiter);
+ if (basic < 0) {
+ basic = 0;
+ }
+
+ for (j = 0; j < basic; ++j) {
+ // if it's not a basic code point
+ if (input.charCodeAt(j) >= 0x80) {
+ error('not-basic');
+ }
+ output.push(input.charCodeAt(j));
+ }
+
+ // Main decoding loop: start just after the last delimiter if any basic code
+ // points were copied; start at the beginning otherwise.
+
+ for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {
+
+ // `index` is the index of the next character to be consumed.
+ // Decode a generalized variable-length integer into `delta`,
+ // which gets added to `i`. The overflow checking is easier
+ // if we increase `i` as we go, then subtract off its starting
+ // value at the end to obtain `delta`.
+ for (oldi = i, w = 1, k = base; /* no condition */; k += base) {
+
+ if (index >= inputLength) {
+ error('invalid-input');
+ }
+
+ digit = basicToDigit(input.charCodeAt(index++));
+
+ if (digit >= base || digit > floor((maxInt - i) / w)) {
+ error('overflow');
+ }
+
+ i += digit * w;
+ t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
+
+ if (digit < t) {
+ break;
+ }
+
+ baseMinusT = base - t;
+ if (w > floor(maxInt / baseMinusT)) {
+ error('overflow');
+ }
+
+ w *= baseMinusT;
+
+ }
+
+ out = output.length + 1;
+ bias = adapt(i - oldi, out, oldi == 0);
+
+ // `i` was supposed to wrap around from `out` to `0`,
+ // incrementing `n` each time, so we'll fix that now:
+ if (floor(i / out) > maxInt - n) {
+ error('overflow');
+ }
+
+ n += floor(i / out);
+ i %= out;
+
+ // Insert `n` at position `i` of the output
+ output.splice(i++, 0, n);
+
+ }
+
+ return ucs2encode(output);
+ }
+
+ /**
+ * Converts a string of Unicode symbols (e.g. a domain name label) to a
+ * Punycode string of ASCII-only symbols.
+ * @memberOf punycode
+ * @param {String} input The string of Unicode symbols.
+ * @returns {String} The resulting Punycode string of ASCII-only symbols.
+ */
+ function encode(input) {
+ var n,
+ delta,
+ handledCPCount,
+ basicLength,
+ bias,
+ j,
+ m,
+ q,
+ k,
+ t,
+ currentValue,
+ output = [],
+ /** `inputLength` will hold the number of code points in `input`. */
+ inputLength,
+ /** Cached calculation results */
+ handledCPCountPlusOne,
+ baseMinusT,
+ qMinusT;
+
+ // Convert the input in UCS-2 to Unicode
+ input = ucs2decode(input);
+
+ // Cache the length
+ inputLength = input.length;
+
+ // Initialize the state
+ n = initialN;
+ delta = 0;
+ bias = initialBias;
+
+ // Handle the basic code points
+ for (j = 0; j < inputLength; ++j) {
+ currentValue = input[j];
+ if (currentValue < 0x80) {
+ output.push(stringFromCharCode(currentValue));
+ }
+ }
+
+ handledCPCount = basicLength = output.length;
+
+ // `handledCPCount` is the number of code points that have been handled;
+ // `basicLength` is the number of basic code points.
+
+ // Finish the basic string - if it is not empty - with a delimiter
+ if (basicLength) {
+ output.push(delimiter);
+ }
+
+ // Main encoding loop:
+ while (handledCPCount < inputLength) {
+
+ // All non-basic code points < n have been handled already. Find the next
+ // larger one:
+ for (m = maxInt, j = 0; j < inputLength; ++j) {
+ currentValue = input[j];
+ if (currentValue >= n && currentValue < m) {
+ m = currentValue;
+ }
+ }
+
+ // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
+ // but guard against overflow
+ handledCPCountPlusOne = handledCPCount + 1;
+ if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
+ error('overflow');
+ }
+
+ delta += (m - n) * handledCPCountPlusOne;
+ n = m;
+
+ for (j = 0; j < inputLength; ++j) {
+ currentValue = input[j];
+
+ if (currentValue < n && ++delta > maxInt) {
+ error('overflow');
+ }
+
+ if (currentValue == n) {
+ // Represent delta as a generalized variable-length integer
+ for (q = delta, k = base; /* no condition */; k += base) {
+ t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
+ if (q < t) {
+ break;
+ }
+ qMinusT = q - t;
+ baseMinusT = base - t;
+ output.push(
+ stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
+ );
+ q = floor(qMinusT / baseMinusT);
+ }
+
+ output.push(stringFromCharCode(digitToBasic(q, 0)));
+ bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
+ delta = 0;
+ ++handledCPCount;
+ }
+ }
+
+ ++delta;
+ ++n;
+
+ }
+ return output.join('');
+ }
+
+ /**
+ * Converts a Punycode string representing a domain name or an email address
+ * to Unicode. Only the Punycoded parts of the input will be converted, i.e.
+ * it doesn't matter if you call it on a string that has already been
+ * converted to Unicode.
+ * @memberOf punycode
+ * @param {String} input The Punycoded domain name or email address to
+ * convert to Unicode.
+ * @returns {String} The Unicode representation of the given Punycode
+ * string.
+ */
+ function toUnicode(input) {
+ return mapDomain(input, function(string) {
+ return regexPunycode.test(string)
+ ? decode(string.slice(4).toLowerCase())
+ : string;
+ });
+ }
+
+ /**
+ * Converts a Unicode string representing a domain name or an email address to
+ * Punycode. Only the non-ASCII parts of the domain name will be converted,
+ * i.e. it doesn't matter if you call it with a domain that's already in
+ * ASCII.
+ * @memberOf punycode
+ * @param {String} input The domain name or email address to convert, as a
+ * Unicode string.
+ * @returns {String} The Punycode representation of the given domain name or
+ * email address.
+ */
+ function toASCII(input) {
+ return mapDomain(input, function(string) {
+ return regexNonASCII.test(string)
+ ? 'xn--' + encode(string)
+ : string;
+ });
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /** Define the public API */
+ punycode = {
+ /**
+ * A string representing the current Punycode.js version number.
+ * @memberOf punycode
+ * @type String
+ */
+ 'version': '1.3.2',
+ /**
+ * An object of methods to convert from JavaScript's internal character
+ * representation (UCS-2) to Unicode code points, and back.
+ * @see <https://mathiasbynens.be/notes/javascript-encoding>
+ * @memberOf punycode
+ * @type Object
+ */
+ 'ucs2': {
+ 'decode': ucs2decode,
+ 'encode': ucs2encode
+ },
+ 'decode': decode,
+ 'encode': encode,
+ 'toASCII': toASCII,
+ 'toUnicode': toUnicode
+ };
+
+ /** Expose `punycode` */
+ // Some AMD build optimizers, like r.js, check for specific condition patterns
+ // like the following:
+ if (freeExports && freeModule) {
+ if (module.exports == freeExports) { // in Node.js or RingoJS v0.8.0+
+ freeModule.exports = punycode;
+ } else { // in Narwhal or RingoJS v0.7.0-
+ for (key in punycode) {
+ punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]);
+ }
+ }
+ } else { // in Rhino or a web browser
+ root.punycode = punycode;
+ }
+
+ }(commonjsGlobal));
+ });
+
+ // Copyright Joyent, Inc. and other Node contributors.
+
+ // If obj.hasOwnProperty has been overridden, then calling
+ // obj.hasOwnProperty(prop) will break.
+ // See: https://github.com/joyent/node/issues/1707
+ function hasOwnProperty(obj, prop) {
+ return Object.prototype.hasOwnProperty.call(obj, prop);
+ }
+
+ var decode = function(qs, sep, eq, options) {
+ sep = sep || '&';
+ eq = eq || '=';
+ var obj = {};
+
+ if (typeof qs !== 'string' || qs.length === 0) {
+ return obj;
+ }
+
+ var regexp = /\+/g;
+ qs = qs.split(sep);
+
+ var maxKeys = 1000;
+ if (options && typeof options.maxKeys === 'number') {
+ maxKeys = options.maxKeys;
+ }
+
+ var len = qs.length;
+ // maxKeys <= 0 means that we should not limit keys count
+ if (maxKeys > 0 && len > maxKeys) {
+ len = maxKeys;
+ }
+
+ for (var i = 0; i < len; ++i) {
+ var x = qs[i].replace(regexp, '%20'),
+ idx = x.indexOf(eq),
+ kstr, vstr, k, v;
+
+ if (idx >= 0) {
+ kstr = x.substr(0, idx);
+ vstr = x.substr(idx + 1);
+ } else {
+ kstr = x;
+ vstr = '';
+ }
+
+ k = decodeURIComponent(kstr);
+ v = decodeURIComponent(vstr);
+
+ if (!hasOwnProperty(obj, k)) {
+ obj[k] = v;
+ } else if (Array.isArray(obj[k])) {
+ obj[k].push(v);
+ } else {
+ obj[k] = [obj[k], v];
+ }
+ }
+
+ return obj;
+ };
+
+ // Copyright Joyent, Inc. and other Node contributors.
+
+ var stringifyPrimitive = function(v) {
+ switch (typeof v) {
+ case 'string':
+ return v;
+
+ case 'boolean':
+ return v ? 'true' : 'false';
+
+ case 'number':
+ return isFinite(v) ? v : '';
+
+ default:
+ return '';
+ }
+ };
+
+ var encode = function(obj, sep, eq, name) {
+ sep = sep || '&';
+ eq = eq || '=';
+ if (obj === null) {
+ obj = undefined;
+ }
+
+ if (typeof obj === 'object') {
+ return Object.keys(obj).map(function(k) {
+ var ks = encodeURIComponent(stringifyPrimitive(k)) + eq;
+ if (Array.isArray(obj[k])) {
+ return obj[k].map(function(v) {
+ return ks + encodeURIComponent(stringifyPrimitive(v));
+ }).join(sep);
+ } else {
+ return ks + encodeURIComponent(stringifyPrimitive(obj[k]));
+ }
+ }).join(sep);
+
+ }
+
+ if (!name) { return ''; }
+ return encodeURIComponent(stringifyPrimitive(name)) + eq +
+ encodeURIComponent(stringifyPrimitive(obj));
+ };
+
+ createCommonjsModule(function (module, exports) {
+
+ exports.decode = exports.parse = decode;
+ exports.encode = exports.stringify = encode;
+ });
+
+ /*!
+ * @pixi/constants - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/constants is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+ /**
+ * Different types of environments for WebGL.
+ * @static
+ * @memberof PIXI
+ * @name ENV
+ * @enum {number}
+ * @property {number} WEBGL_LEGACY - Used for older v1 WebGL devices. PixiJS will aim to ensure compatibility
+ * with older / less advanced devices. If you experience unexplained flickering prefer this environment.
+ * @property {number} WEBGL - Version 1 of WebGL
+ * @property {number} WEBGL2 - Version 2 of WebGL
+ */
+ var ENV;
+ (function (ENV) {
+ ENV[ENV["WEBGL_LEGACY"] = 0] = "WEBGL_LEGACY";
+ ENV[ENV["WEBGL"] = 1] = "WEBGL";
+ ENV[ENV["WEBGL2"] = 2] = "WEBGL2";
+ })(ENV || (ENV = {}));
+ /**
+ * Constant to identify the Renderer Type.
+ * @static
+ * @memberof PIXI
+ * @name RENDERER_TYPE
+ * @enum {number}
+ * @property {number} UNKNOWN - Unknown render type.
+ * @property {number} WEBGL - WebGL render type.
+ * @property {number} CANVAS - Canvas render type.
+ */
+ var RENDERER_TYPE;
+ (function (RENDERER_TYPE) {
+ RENDERER_TYPE[RENDERER_TYPE["UNKNOWN"] = 0] = "UNKNOWN";
+ RENDERER_TYPE[RENDERER_TYPE["WEBGL"] = 1] = "WEBGL";
+ RENDERER_TYPE[RENDERER_TYPE["CANVAS"] = 2] = "CANVAS";
+ })(RENDERER_TYPE || (RENDERER_TYPE = {}));
+ /**
+ * Bitwise OR of masks that indicate the buffers to be cleared.
+ * @static
+ * @memberof PIXI
+ * @name BUFFER_BITS
+ * @enum {number}
+ * @property {number} COLOR - Indicates the buffers currently enabled for color writing.
+ * @property {number} DEPTH - Indicates the depth buffer.
+ * @property {number} STENCIL - Indicates the stencil buffer.
+ */
+ var BUFFER_BITS;
+ (function (BUFFER_BITS) {
+ BUFFER_BITS[BUFFER_BITS["COLOR"] = 16384] = "COLOR";
+ BUFFER_BITS[BUFFER_BITS["DEPTH"] = 256] = "DEPTH";
+ BUFFER_BITS[BUFFER_BITS["STENCIL"] = 1024] = "STENCIL";
+ })(BUFFER_BITS || (BUFFER_BITS = {}));
+ /**
+ * Various blend modes supported by PIXI.
+ *
+ * IMPORTANT - The WebGL renderer only supports the NORMAL, ADD, MULTIPLY and SCREEN blend modes.
+ * Anything else will silently act like NORMAL.
+ * @memberof PIXI
+ * @name BLEND_MODES
+ * @enum {number}
+ * @property {number} NORMAL -
+ * @property {number} ADD -
+ * @property {number} MULTIPLY -
+ * @property {number} SCREEN -
+ * @property {number} OVERLAY -
+ * @property {number} DARKEN -
+ * @property {number} LIGHTEN -
+ * @property {number} COLOR_DODGE -
+ * @property {number} COLOR_BURN -
+ * @property {number} HARD_LIGHT -
+ * @property {number} SOFT_LIGHT -
+ * @property {number} DIFFERENCE -
+ * @property {number} EXCLUSION -
+ * @property {number} HUE -
+ * @property {number} SATURATION -
+ * @property {number} COLOR -
+ * @property {number} LUMINOSITY -
+ * @property {number} NORMAL_NPM -
+ * @property {number} ADD_NPM -
+ * @property {number} SCREEN_NPM -
+ * @property {number} NONE -
+ * @property {number} SRC_IN -
+ * @property {number} SRC_OUT -
+ * @property {number} SRC_ATOP -
+ * @property {number} DST_OVER -
+ * @property {number} DST_IN -
+ * @property {number} DST_OUT -
+ * @property {number} DST_ATOP -
+ * @property {number} SUBTRACT -
+ * @property {number} SRC_OVER -
+ * @property {number} ERASE -
+ * @property {number} XOR -
+ */
+ var BLEND_MODES;
+ (function (BLEND_MODES) {
+ BLEND_MODES[BLEND_MODES["NORMAL"] = 0] = "NORMAL";
+ BLEND_MODES[BLEND_MODES["ADD"] = 1] = "ADD";
+ BLEND_MODES[BLEND_MODES["MULTIPLY"] = 2] = "MULTIPLY";
+ BLEND_MODES[BLEND_MODES["SCREEN"] = 3] = "SCREEN";
+ BLEND_MODES[BLEND_MODES["OVERLAY"] = 4] = "OVERLAY";
+ BLEND_MODES[BLEND_MODES["DARKEN"] = 5] = "DARKEN";
+ BLEND_MODES[BLEND_MODES["LIGHTEN"] = 6] = "LIGHTEN";
+ BLEND_MODES[BLEND_MODES["COLOR_DODGE"] = 7] = "COLOR_DODGE";
+ BLEND_MODES[BLEND_MODES["COLOR_BURN"] = 8] = "COLOR_BURN";
+ BLEND_MODES[BLEND_MODES["HARD_LIGHT"] = 9] = "HARD_LIGHT";
+ BLEND_MODES[BLEND_MODES["SOFT_LIGHT"] = 10] = "SOFT_LIGHT";
+ BLEND_MODES[BLEND_MODES["DIFFERENCE"] = 11] = "DIFFERENCE";
+ BLEND_MODES[BLEND_MODES["EXCLUSION"] = 12] = "EXCLUSION";
+ BLEND_MODES[BLEND_MODES["HUE"] = 13] = "HUE";
+ BLEND_MODES[BLEND_MODES["SATURATION"] = 14] = "SATURATION";
+ BLEND_MODES[BLEND_MODES["COLOR"] = 15] = "COLOR";
+ BLEND_MODES[BLEND_MODES["LUMINOSITY"] = 16] = "LUMINOSITY";
+ BLEND_MODES[BLEND_MODES["NORMAL_NPM"] = 17] = "NORMAL_NPM";
+ BLEND_MODES[BLEND_MODES["ADD_NPM"] = 18] = "ADD_NPM";
+ BLEND_MODES[BLEND_MODES["SCREEN_NPM"] = 19] = "SCREEN_NPM";
+ BLEND_MODES[BLEND_MODES["NONE"] = 20] = "NONE";
+ BLEND_MODES[BLEND_MODES["SRC_OVER"] = 0] = "SRC_OVER";
+ BLEND_MODES[BLEND_MODES["SRC_IN"] = 21] = "SRC_IN";
+ BLEND_MODES[BLEND_MODES["SRC_OUT"] = 22] = "SRC_OUT";
+ BLEND_MODES[BLEND_MODES["SRC_ATOP"] = 23] = "SRC_ATOP";
+ BLEND_MODES[BLEND_MODES["DST_OVER"] = 24] = "DST_OVER";
+ BLEND_MODES[BLEND_MODES["DST_IN"] = 25] = "DST_IN";
+ BLEND_MODES[BLEND_MODES["DST_OUT"] = 26] = "DST_OUT";
+ BLEND_MODES[BLEND_MODES["DST_ATOP"] = 27] = "DST_ATOP";
+ BLEND_MODES[BLEND_MODES["ERASE"] = 26] = "ERASE";
+ BLEND_MODES[BLEND_MODES["SUBTRACT"] = 28] = "SUBTRACT";
+ BLEND_MODES[BLEND_MODES["XOR"] = 29] = "XOR";
+ })(BLEND_MODES || (BLEND_MODES = {}));
+ /**
+ * Various webgl draw modes. These can be used to specify which GL drawMode to use
+ * under certain situations and renderers.
+ * @memberof PIXI
+ * @static
+ * @name DRAW_MODES
+ * @enum {number}
+ * @property {number} POINTS -
+ * @property {number} LINES -
+ * @property {number} LINE_LOOP -
+ * @property {number} LINE_STRIP -
+ * @property {number} TRIANGLES -
+ * @property {number} TRIANGLE_STRIP -
+ * @property {number} TRIANGLE_FAN -
+ */
+ var DRAW_MODES;
+ (function (DRAW_MODES) {
+ DRAW_MODES[DRAW_MODES["POINTS"] = 0] = "POINTS";
+ DRAW_MODES[DRAW_MODES["LINES"] = 1] = "LINES";
+ DRAW_MODES[DRAW_MODES["LINE_LOOP"] = 2] = "LINE_LOOP";
+ DRAW_MODES[DRAW_MODES["LINE_STRIP"] = 3] = "LINE_STRIP";
+ DRAW_MODES[DRAW_MODES["TRIANGLES"] = 4] = "TRIANGLES";
+ DRAW_MODES[DRAW_MODES["TRIANGLE_STRIP"] = 5] = "TRIANGLE_STRIP";
+ DRAW_MODES[DRAW_MODES["TRIANGLE_FAN"] = 6] = "TRIANGLE_FAN";
+ })(DRAW_MODES || (DRAW_MODES = {}));
+ /**
+ * Various GL texture/resources formats.
+ * @memberof PIXI
+ * @static
+ * @name FORMATS
+ * @enum {number}
+ * @property {number} [RGBA=6408] -
+ * @property {number} [RGB=6407] -
+ * @property {number} [RG=33319] -
+ * @property {number} [RED=6403] -
+ * @property {number} [RGBA_INTEGER=36249] -
+ * @property {number} [RGB_INTEGER=36248] -
+ * @property {number} [RG_INTEGER=33320] -
+ * @property {number} [RED_INTEGER=36244] -
+ * @property {number} [ALPHA=6406] -
+ * @property {number} [LUMINANCE=6409] -
+ * @property {number} [LUMINANCE_ALPHA=6410] -
+ * @property {number} [DEPTH_COMPONENT=6402] -
+ * @property {number} [DEPTH_STENCIL=34041] -
+ */
+ var FORMATS;
+ (function (FORMATS) {
+ FORMATS[FORMATS["RGBA"] = 6408] = "RGBA";
+ FORMATS[FORMATS["RGB"] = 6407] = "RGB";
+ FORMATS[FORMATS["RG"] = 33319] = "RG";
+ FORMATS[FORMATS["RED"] = 6403] = "RED";
+ FORMATS[FORMATS["RGBA_INTEGER"] = 36249] = "RGBA_INTEGER";
+ FORMATS[FORMATS["RGB_INTEGER"] = 36248] = "RGB_INTEGER";
+ FORMATS[FORMATS["RG_INTEGER"] = 33320] = "RG_INTEGER";
+ FORMATS[FORMATS["RED_INTEGER"] = 36244] = "RED_INTEGER";
+ FORMATS[FORMATS["ALPHA"] = 6406] = "ALPHA";
+ FORMATS[FORMATS["LUMINANCE"] = 6409] = "LUMINANCE";
+ FORMATS[FORMATS["LUMINANCE_ALPHA"] = 6410] = "LUMINANCE_ALPHA";
+ FORMATS[FORMATS["DEPTH_COMPONENT"] = 6402] = "DEPTH_COMPONENT";
+ FORMATS[FORMATS["DEPTH_STENCIL"] = 34041] = "DEPTH_STENCIL";
+ })(FORMATS || (FORMATS = {}));
+ /**
+ * Various GL target types.
+ * @memberof PIXI
+ * @static
+ * @name TARGETS
+ * @enum {number}
+ * @property {number} [TEXTURE_2D=3553] -
+ * @property {number} [TEXTURE_CUBE_MAP=34067] -
+ * @property {number} [TEXTURE_2D_ARRAY=35866] -
+ * @property {number} [TEXTURE_CUBE_MAP_POSITIVE_X=34069] -
+ * @property {number} [TEXTURE_CUBE_MAP_NEGATIVE_X=34070] -
+ * @property {number} [TEXTURE_CUBE_MAP_POSITIVE_Y=34071] -
+ * @property {number} [TEXTURE_CUBE_MAP_NEGATIVE_Y=34072] -
+ * @property {number} [TEXTURE_CUBE_MAP_POSITIVE_Z=34073] -
+ * @property {number} [TEXTURE_CUBE_MAP_NEGATIVE_Z=34074] -
+ */
+ var TARGETS;
+ (function (TARGETS) {
+ TARGETS[TARGETS["TEXTURE_2D"] = 3553] = "TEXTURE_2D";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP"] = 34067] = "TEXTURE_CUBE_MAP";
+ TARGETS[TARGETS["TEXTURE_2D_ARRAY"] = 35866] = "TEXTURE_2D_ARRAY";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_POSITIVE_X"] = 34069] = "TEXTURE_CUBE_MAP_POSITIVE_X";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_NEGATIVE_X"] = 34070] = "TEXTURE_CUBE_MAP_NEGATIVE_X";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_POSITIVE_Y"] = 34071] = "TEXTURE_CUBE_MAP_POSITIVE_Y";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_NEGATIVE_Y"] = 34072] = "TEXTURE_CUBE_MAP_NEGATIVE_Y";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_POSITIVE_Z"] = 34073] = "TEXTURE_CUBE_MAP_POSITIVE_Z";
+ TARGETS[TARGETS["TEXTURE_CUBE_MAP_NEGATIVE_Z"] = 34074] = "TEXTURE_CUBE_MAP_NEGATIVE_Z";
+ })(TARGETS || (TARGETS = {}));
+ /**
+ * Various GL data format types.
+ * @memberof PIXI
+ * @static
+ * @name TYPES
+ * @enum {number}
+ * @property {number} [UNSIGNED_BYTE=5121] -
+ * @property {number} [UNSIGNED_SHORT=5123] -
+ * @property {number} [UNSIGNED_SHORT_5_6_5=33635] -
+ * @property {number} [UNSIGNED_SHORT_4_4_4_4=32819] -
+ * @property {number} [UNSIGNED_SHORT_5_5_5_1=32820] -
+ * @property {number} [UNSIGNED_INT=5125] -
+ * @property {number} [UNSIGNED_INT_10F_11F_11F_REV=35899] -
+ * @property {number} [UNSIGNED_INT_2_10_10_10_REV=33640] -
+ * @property {number} [UNSIGNED_INT_24_8=34042] -
+ * @property {number} [UNSIGNED_INT_5_9_9_9_REV=35902] -
+ * @property {number} [BYTE=5120] -
+ * @property {number} [SHORT=5122] -
+ * @property {number} [INT=5124] -
+ * @property {number} [FLOAT=5126] -
+ * @property {number} [FLOAT_32_UNSIGNED_INT_24_8_REV=36269] -
+ * @property {number} [HALF_FLOAT=36193] -
+ */
+ var TYPES;
+ (function (TYPES) {
+ TYPES[TYPES["UNSIGNED_BYTE"] = 5121] = "UNSIGNED_BYTE";
+ TYPES[TYPES["UNSIGNED_SHORT"] = 5123] = "UNSIGNED_SHORT";
+ TYPES[TYPES["UNSIGNED_SHORT_5_6_5"] = 33635] = "UNSIGNED_SHORT_5_6_5";
+ TYPES[TYPES["UNSIGNED_SHORT_4_4_4_4"] = 32819] = "UNSIGNED_SHORT_4_4_4_4";
+ TYPES[TYPES["UNSIGNED_SHORT_5_5_5_1"] = 32820] = "UNSIGNED_SHORT_5_5_5_1";
+ TYPES[TYPES["UNSIGNED_INT"] = 5125] = "UNSIGNED_INT";
+ TYPES[TYPES["UNSIGNED_INT_10F_11F_11F_REV"] = 35899] = "UNSIGNED_INT_10F_11F_11F_REV";
+ TYPES[TYPES["UNSIGNED_INT_2_10_10_10_REV"] = 33640] = "UNSIGNED_INT_2_10_10_10_REV";
+ TYPES[TYPES["UNSIGNED_INT_24_8"] = 34042] = "UNSIGNED_INT_24_8";
+ TYPES[TYPES["UNSIGNED_INT_5_9_9_9_REV"] = 35902] = "UNSIGNED_INT_5_9_9_9_REV";
+ TYPES[TYPES["BYTE"] = 5120] = "BYTE";
+ TYPES[TYPES["SHORT"] = 5122] = "SHORT";
+ TYPES[TYPES["INT"] = 5124] = "INT";
+ TYPES[TYPES["FLOAT"] = 5126] = "FLOAT";
+ TYPES[TYPES["FLOAT_32_UNSIGNED_INT_24_8_REV"] = 36269] = "FLOAT_32_UNSIGNED_INT_24_8_REV";
+ TYPES[TYPES["HALF_FLOAT"] = 36193] = "HALF_FLOAT";
+ })(TYPES || (TYPES = {}));
+ /**
+ * Various sampler types. Correspond to `sampler`, `isampler`, `usampler` GLSL types respectively.
+ * WebGL1 works only with FLOAT.
+ * @memberof PIXI
+ * @static
+ * @name SAMPLER_TYPES
+ * @enum {number}
+ * @property {number} [FLOAT=0] -
+ * @property {number} [INT=1] -
+ * @property {number} [UINT=2] -
+ */
+ var SAMPLER_TYPES;
+ (function (SAMPLER_TYPES) {
+ SAMPLER_TYPES[SAMPLER_TYPES["FLOAT"] = 0] = "FLOAT";
+ SAMPLER_TYPES[SAMPLER_TYPES["INT"] = 1] = "INT";
+ SAMPLER_TYPES[SAMPLER_TYPES["UINT"] = 2] = "UINT";
+ })(SAMPLER_TYPES || (SAMPLER_TYPES = {}));
+ /**
+ * The scale modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.SCALE_MODE} scale mode affects the default scaling mode of future operations.
+ * It can be re-assigned to either LINEAR or NEAREST, depending upon suitability.
+ * @memberof PIXI
+ * @static
+ * @name SCALE_MODES
+ * @enum {number}
+ * @property {number} LINEAR Smooth scaling
+ * @property {number} NEAREST Pixelating scaling
+ */
+ var SCALE_MODES;
+ (function (SCALE_MODES) {
+ SCALE_MODES[SCALE_MODES["NEAREST"] = 0] = "NEAREST";
+ SCALE_MODES[SCALE_MODES["LINEAR"] = 1] = "LINEAR";
+ })(SCALE_MODES || (SCALE_MODES = {}));
+ /**
+ * The wrap modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.WRAP_MODE} wrap mode affects the default wrapping mode of future operations.
+ * It can be re-assigned to either CLAMP or REPEAT, depending upon suitability.
+ * If the texture is non power of two then clamp will be used regardless as WebGL can
+ * only use REPEAT if the texture is po2.
+ *
+ * This property only affects WebGL.
+ * @name WRAP_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} CLAMP - The textures uvs are clamped
+ * @property {number} REPEAT - The texture uvs tile and repeat
+ * @property {number} MIRRORED_REPEAT - The texture uvs tile and repeat with mirroring
+ */
+ var WRAP_MODES;
+ (function (WRAP_MODES) {
+ WRAP_MODES[WRAP_MODES["CLAMP"] = 33071] = "CLAMP";
+ WRAP_MODES[WRAP_MODES["REPEAT"] = 10497] = "REPEAT";
+ WRAP_MODES[WRAP_MODES["MIRRORED_REPEAT"] = 33648] = "MIRRORED_REPEAT";
+ })(WRAP_MODES || (WRAP_MODES = {}));
+ /**
+ * Mipmap filtering modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.MIPMAP_TEXTURES} affects default texture filtering.
+ * Mipmaps are generated for a baseTexture if its `mipmap` field is `ON`,
+ * or its `POW2` and texture dimensions are powers of 2.
+ * Due to platform restriction, `ON` option will work like `POW2` for webgl-1.
+ *
+ * This property only affects WebGL.
+ * @name MIPMAP_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} OFF - No mipmaps
+ * @property {number} POW2 - Generate mipmaps if texture dimensions are pow2
+ * @property {number} ON - Always generate mipmaps
+ * @property {number} ON_MANUAL - Use mipmaps, but do not auto-generate them; this is used with a resource
+ * that supports buffering each level-of-detail.
+ */
+ var MIPMAP_MODES;
+ (function (MIPMAP_MODES) {
+ MIPMAP_MODES[MIPMAP_MODES["OFF"] = 0] = "OFF";
+ MIPMAP_MODES[MIPMAP_MODES["POW2"] = 1] = "POW2";
+ MIPMAP_MODES[MIPMAP_MODES["ON"] = 2] = "ON";
+ MIPMAP_MODES[MIPMAP_MODES["ON_MANUAL"] = 3] = "ON_MANUAL";
+ })(MIPMAP_MODES || (MIPMAP_MODES = {}));
+ /**
+ * How to treat textures with premultiplied alpha
+ * @name ALPHA_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} NO_PREMULTIPLIED_ALPHA - Source is not premultiplied, leave it like that.
+ * Option for compressed and data textures that are created from typed arrays.
+ * @property {number} PREMULTIPLY_ON_UPLOAD - Source is not premultiplied, premultiply on upload.
+ * Default option, used for all loaded images.
+ * @property {number} PREMULTIPLIED_ALPHA - Source is already premultiplied
+ * Example: spine atlases with `_pma` suffix.
+ * @property {number} NPM - Alias for NO_PREMULTIPLIED_ALPHA.
+ * @property {number} UNPACK - Default option, alias for PREMULTIPLY_ON_UPLOAD.
+ * @property {number} PMA - Alias for PREMULTIPLIED_ALPHA.
+ */
+ var ALPHA_MODES;
+ (function (ALPHA_MODES) {
+ ALPHA_MODES[ALPHA_MODES["NPM"] = 0] = "NPM";
+ ALPHA_MODES[ALPHA_MODES["UNPACK"] = 1] = "UNPACK";
+ ALPHA_MODES[ALPHA_MODES["PMA"] = 2] = "PMA";
+ ALPHA_MODES[ALPHA_MODES["NO_PREMULTIPLIED_ALPHA"] = 0] = "NO_PREMULTIPLIED_ALPHA";
+ ALPHA_MODES[ALPHA_MODES["PREMULTIPLY_ON_UPLOAD"] = 1] = "PREMULTIPLY_ON_UPLOAD";
+ ALPHA_MODES[ALPHA_MODES["PREMULTIPLY_ALPHA"] = 2] = "PREMULTIPLY_ALPHA";
+ ALPHA_MODES[ALPHA_MODES["PREMULTIPLIED_ALPHA"] = 2] = "PREMULTIPLIED_ALPHA";
+ })(ALPHA_MODES || (ALPHA_MODES = {}));
+ /**
+ * Configure whether filter textures are cleared after binding.
+ *
+ * Filter textures need not be cleared if the filter does not use pixel blending. {@link CLEAR_MODES.BLIT} will detect
+ * this and skip clearing as an optimization.
+ * @name CLEAR_MODES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} BLEND - Do not clear the filter texture. The filter's output will blend on top of the output texture.
+ * @property {number} CLEAR - Always clear the filter texture.
+ * @property {number} BLIT - Clear only if {@link FilterSystem.forceClear} is set or if the filter uses pixel blending.
+ * @property {number} NO - Alias for BLEND, same as `false` in earlier versions
+ * @property {number} YES - Alias for CLEAR, same as `true` in earlier versions
+ * @property {number} AUTO - Alias for BLIT
+ */
+ var CLEAR_MODES;
+ (function (CLEAR_MODES) {
+ CLEAR_MODES[CLEAR_MODES["NO"] = 0] = "NO";
+ CLEAR_MODES[CLEAR_MODES["YES"] = 1] = "YES";
+ CLEAR_MODES[CLEAR_MODES["AUTO"] = 2] = "AUTO";
+ CLEAR_MODES[CLEAR_MODES["BLEND"] = 0] = "BLEND";
+ CLEAR_MODES[CLEAR_MODES["CLEAR"] = 1] = "CLEAR";
+ CLEAR_MODES[CLEAR_MODES["BLIT"] = 2] = "BLIT";
+ })(CLEAR_MODES || (CLEAR_MODES = {}));
+ /**
+ * The gc modes that are supported by pixi.
+ *
+ * The {@link PIXI.settings.GC_MODE} Garbage Collection mode for PixiJS textures is AUTO
+ * If set to GC_MODE, the renderer will occasionally check textures usage. If they are not
+ * used for a specified period of time they will be removed from the GPU. They will of course
+ * be uploaded again when they are required. This is a silent behind the scenes process that
+ * should ensure that the GPU does not get filled up.
+ *
+ * Handy for mobile devices!
+ * This property only affects WebGL.
+ * @name GC_MODES
+ * @enum {number}
+ * @static
+ * @memberof PIXI
+ * @property {number} AUTO - Garbage collection will happen periodically automatically
+ * @property {number} MANUAL - Garbage collection will need to be called manually
+ */
+ var GC_MODES;
+ (function (GC_MODES) {
+ GC_MODES[GC_MODES["AUTO"] = 0] = "AUTO";
+ GC_MODES[GC_MODES["MANUAL"] = 1] = "MANUAL";
+ })(GC_MODES || (GC_MODES = {}));
+ /**
+ * Constants that specify float precision in shaders.
+ * @name PRECISION
+ * @memberof PIXI
+ * @constant
+ * @static
+ * @enum {string}
+ * @property {string} [LOW='lowp'] -
+ * @property {string} [MEDIUM='mediump'] -
+ * @property {string} [HIGH='highp'] -
+ */
+ var PRECISION;
+ (function (PRECISION) {
+ PRECISION["LOW"] = "lowp";
+ PRECISION["MEDIUM"] = "mediump";
+ PRECISION["HIGH"] = "highp";
+ })(PRECISION || (PRECISION = {}));
+ /**
+ * Constants for mask implementations.
+ * We use `type` suffix because it leads to very different behaviours
+ * @name MASK_TYPES
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} NONE - Mask is ignored
+ * @property {number} SCISSOR - Scissor mask, rectangle on screen, cheap
+ * @property {number} STENCIL - Stencil mask, 1-bit, medium, works only if renderer supports stencil
+ * @property {number} SPRITE - Mask that uses SpriteMaskFilter, uses temporary RenderTexture
+ * @property {number} COLOR - Color mask (RGBA)
+ */
+ var MASK_TYPES;
+ (function (MASK_TYPES) {
+ MASK_TYPES[MASK_TYPES["NONE"] = 0] = "NONE";
+ MASK_TYPES[MASK_TYPES["SCISSOR"] = 1] = "SCISSOR";
+ MASK_TYPES[MASK_TYPES["STENCIL"] = 2] = "STENCIL";
+ MASK_TYPES[MASK_TYPES["SPRITE"] = 3] = "SPRITE";
+ MASK_TYPES[MASK_TYPES["COLOR"] = 4] = "COLOR";
+ })(MASK_TYPES || (MASK_TYPES = {}));
+ /**
+ * Bitwise OR of masks that indicate the color channels that are rendered to.
+ * @static
+ * @memberof PIXI
+ * @name COLOR_MASK_BITS
+ * @enum {number}
+ * @property {number} RED - Red channel.
+ * @property {number} GREEN - Green channel
+ * @property {number} BLUE - Blue channel.
+ * @property {number} ALPHA - Alpha channel.
+ */
+ var COLOR_MASK_BITS;
+ (function (COLOR_MASK_BITS) {
+ COLOR_MASK_BITS[COLOR_MASK_BITS["RED"] = 1] = "RED";
+ COLOR_MASK_BITS[COLOR_MASK_BITS["GREEN"] = 2] = "GREEN";
+ COLOR_MASK_BITS[COLOR_MASK_BITS["BLUE"] = 4] = "BLUE";
+ COLOR_MASK_BITS[COLOR_MASK_BITS["ALPHA"] = 8] = "ALPHA";
+ })(COLOR_MASK_BITS || (COLOR_MASK_BITS = {}));
+ /**
+ * Constants for multi-sampling antialiasing.
+ * @see PIXI.Framebuffer#multisample
+ * @name MSAA_QUALITY
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} NONE - No multisampling for this renderTexture
+ * @property {number} LOW - Try 2 samples
+ * @property {number} MEDIUM - Try 4 samples
+ * @property {number} HIGH - Try 8 samples
+ */
+ var MSAA_QUALITY;
+ (function (MSAA_QUALITY) {
+ MSAA_QUALITY[MSAA_QUALITY["NONE"] = 0] = "NONE";
+ MSAA_QUALITY[MSAA_QUALITY["LOW"] = 2] = "LOW";
+ MSAA_QUALITY[MSAA_QUALITY["MEDIUM"] = 4] = "MEDIUM";
+ MSAA_QUALITY[MSAA_QUALITY["HIGH"] = 8] = "HIGH";
+ })(MSAA_QUALITY || (MSAA_QUALITY = {}));
+ /**
+ * Constants for various buffer types in Pixi
+ * @see PIXI.BUFFER_TYPE
+ * @name BUFFER_TYPE
+ * @memberof PIXI
+ * @static
+ * @enum {number}
+ * @property {number} ELEMENT_ARRAY_BUFFER - buffer type for using as an index buffer
+ * @property {number} ARRAY_BUFFER - buffer type for using attribute data
+ * @property {number} UNIFORM_BUFFER - the buffer type is for uniform buffer objects
+ */
+ var BUFFER_TYPE;
+ (function (BUFFER_TYPE) {
+ BUFFER_TYPE[BUFFER_TYPE["ELEMENT_ARRAY_BUFFER"] = 34963] = "ELEMENT_ARRAY_BUFFER";
+ BUFFER_TYPE[BUFFER_TYPE["ARRAY_BUFFER"] = 34962] = "ARRAY_BUFFER";
+ // NOT YET SUPPORTED
+ BUFFER_TYPE[BUFFER_TYPE["UNIFORM_BUFFER"] = 35345] = "UNIFORM_BUFFER";
+ })(BUFFER_TYPE || (BUFFER_TYPE = {}));
+
+ /*!
+ * @pixi/utils - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/utils is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /**
+ * The prefix that denotes a URL is for a retina asset.
+ * @static
+ * @name RETINA_PREFIX
+ * @memberof PIXI.settings
+ * @type {RegExp}
+ * @default /@([0-9\.]+)x/
+ * @example `@2x`
+ */
+ settings.RETINA_PREFIX = /@([0-9\.]+)x/;
+ /**
+ * Should the `failIfMajorPerformanceCaveat` flag be enabled as a context option used in the `isWebGLSupported` function.
+ * If set to true, a WebGL renderer can fail to be created if the browser thinks there could be performance issues when
+ * using WebGL.
+ *
+ * In PixiJS v6 this has changed from true to false by default, to allow WebGL to work in as many scenarios as possible.
+ * However, some users may have a poor experience, for example, if a user has a gpu or driver version blacklisted by the
+ * browser.
+ *
+ * If your application requires high performance rendering, you may wish to set this to false.
+ * We recommend one of two options if you decide to set this flag to false:
+ *
+ * 1: Use the `pixi.js-legacy` package, which includes a Canvas renderer as a fallback in case high performance WebGL is
+ * not supported.
+ *
+ * 2: Call `isWebGLSupported` (which if found in the PIXI.utils package) in your code before attempting to create a PixiJS
+ * renderer, and show an error message to the user if the function returns false, explaining that their device & browser
+ * combination does not support high performance WebGL.
+ * This is a much better strategy than trying to create a PixiJS renderer and finding it then fails.
+ * @static
+ * @name FAIL_IF_MAJOR_PERFORMANCE_CAVEAT
+ * @memberof PIXI.settings
+ * @type {boolean}
+ * @default false
+ */
+ settings.FAIL_IF_MAJOR_PERFORMANCE_CAVEAT = false;
+
+ /**
+ * Corrects PixiJS blend, takes premultiplied alpha into account
+ * @memberof PIXI.utils
+ * @function mapPremultipliedBlendModes
+ * @private
+ * @returns {Array<number[]>} Mapped modes.
+ */
+ function mapPremultipliedBlendModes() {
+ var pm = [];
+ var npm = [];
+ for (var i = 0; i < 32; i++) {
+ pm[i] = i;
+ npm[i] = i;
+ }
+ pm[BLEND_MODES.NORMAL_NPM] = BLEND_MODES.NORMAL;
+ pm[BLEND_MODES.ADD_NPM] = BLEND_MODES.ADD;
+ pm[BLEND_MODES.SCREEN_NPM] = BLEND_MODES.SCREEN;
+ npm[BLEND_MODES.NORMAL] = BLEND_MODES.NORMAL_NPM;
+ npm[BLEND_MODES.ADD] = BLEND_MODES.ADD_NPM;
+ npm[BLEND_MODES.SCREEN] = BLEND_MODES.SCREEN_NPM;
+ var array = [];
+ array.push(npm);
+ array.push(pm);
+ return array;
+ }
+ /**
+ * maps premultiply flag and blendMode to adjusted blendMode
+ * @memberof PIXI.utils
+ * @constant premultiplyBlendMode
+ * @type {Array<number[]>}
+ */
+ mapPremultipliedBlendModes();
+
+ // A map of warning messages already fired
+ var warnings = {};
+ /**
+ * Helper for warning developers about deprecated features & settings.
+ * A stack track for warnings is given; useful for tracking-down where
+ * deprecated methods/properties/classes are being used within the code.
+ * @memberof PIXI.utils
+ * @function deprecation
+ * @param {string} version - The version where the feature became deprecated
+ * @param {string} message - Message should include what is deprecated, where, and the new solution
+ * @param {number} [ignoreDepth=3] - The number of steps to ignore at the top of the error stack
+ * this is mostly to ignore internal deprecation calls.
+ */
+ function deprecation(version, message, ignoreDepth) {
+ if (ignoreDepth === void 0) { ignoreDepth = 3; }
+ // Ignore duplicat
+ if (warnings[message]) {
+ return;
+ }
+ /* eslint-disable no-console */
+ var stack = new Error().stack;
+ // Handle IE < 10 and Safari < 6
+ if (typeof stack === 'undefined') {
+ console.warn('PixiJS Deprecation Warning: ', message + "\nDeprecated since v" + version);
+ }
+ else {
+ // chop off the stack trace which includes PixiJS internal calls
+ stack = stack.split('\n').splice(ignoreDepth).join('\n');
+ if (console.groupCollapsed) {
+ console.groupCollapsed('%cPixiJS Deprecation Warning: %c%s', 'color:#614108;background:#fffbe6', 'font-weight:normal;color:#614108;background:#fffbe6', message + "\nDeprecated since v" + version);
+ console.warn(stack);
+ console.groupEnd();
+ }
+ else {
+ console.warn('PixiJS Deprecation Warning: ', message + "\nDeprecated since v" + version);
+ console.warn(stack);
+ }
+ }
+ /* eslint-enable no-console */
+ warnings[message] = true;
+ }
+
+ /**
+ * Creates a Canvas element of the given size to be used as a target for rendering to.
+ * @class
+ * @memberof PIXI.utils
+ */
+ /** @class */ ((function () {
+ /**
+ * @param width - the width for the newly created canvas
+ * @param height - the height for the newly created canvas
+ * @param {number} [resolution=PIXI.settings.RESOLUTION] - The resolution / device pixel ratio of the canvas
+ */
+ function CanvasRenderTarget(width, height, resolution) {
+ this.canvas = settings.ADAPTER.createCanvas();
+ this.context = this.canvas.getContext('2d');
+ this.resolution = resolution || settings.RESOLUTION;
+ this.resize(width, height);
+ }
+ /**
+ * Clears the canvas that was created by the CanvasRenderTarget class.
+ * @private
+ */
+ CanvasRenderTarget.prototype.clear = function () {
+ this.context.setTransform(1, 0, 0, 1, 0, 0);
+ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ };
+ /**
+ * Resizes the canvas to the specified width and height.
+ * @param desiredWidth - the desired width of the canvas
+ * @param desiredHeight - the desired height of the canvas
+ */
+ CanvasRenderTarget.prototype.resize = function (desiredWidth, desiredHeight) {
+ this.canvas.width = Math.round(desiredWidth * this.resolution);
+ this.canvas.height = Math.round(desiredHeight * this.resolution);
+ };
+ /** Destroys this canvas. */
+ CanvasRenderTarget.prototype.destroy = function () {
+ this.context = null;
+ this.canvas = null;
+ };
+ Object.defineProperty(CanvasRenderTarget.prototype, "width", {
+ /**
+ * The width of the canvas buffer in pixels.
+ * @member {number}
+ */
+ get: function () {
+ return this.canvas.width;
+ },
+ set: function (val) {
+ this.canvas.width = Math.round(val);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(CanvasRenderTarget.prototype, "height", {
+ /**
+ * The height of the canvas buffer in pixels.
+ * @member {number}
+ */
+ get: function () {
+ return this.canvas.height;
+ },
+ set: function (val) {
+ this.canvas.height = Math.round(val);
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return CanvasRenderTarget;
+ })());
+
+ /**
+ * Convenience class to create a new PIXI application.
+ *
+ * This class automatically creates the renderer, ticker and root container.
+ * @example
+ * // Create the application
+ * const app = new PIXI.Application();
+ *
+ * // Add the view to the DOM
+ * document.body.appendChild(app.view);
+ *
+ * // ex, add display objects
+ * app.stage.addChild(PIXI.Sprite.from('something.png'));
+ * @class
+ * @memberof PIXI
+ */
+ var Application = /** @class */ (function () {
+ /**
+ * @param {object} [options] - The optional renderer parameters.
+ * @param {boolean} [options.autoStart=true] - Automatically starts the rendering after the construction.
+ * **Note**: Setting this parameter to false does NOT stop the shared ticker even if you set
+ * options.sharedTicker to true in case that it is already started. Stop it by your own.
+ * @param {number} [options.width=800] - The width of the renderers view.
+ * @param {number} [options.height=600] - The height of the renderers view.
+ * @param {HTMLCanvasElement} [options.view] - The canvas to use as a view, optional.
+ * @param {boolean} [options.useContextAlpha=true] - Pass-through value for canvas' context `alpha` property.
+ * If you want to set transparency, please use `backgroundAlpha`. This option is for cases where the
+ * canvas needs to be opaque, possibly for performance reasons on some older devices.
+ * @param {boolean} [options.autoDensity=false] - Resizes renderer view in CSS pixels to allow for
+ * resolutions other than 1.
+ * @param {boolean} [options.antialias=false] - Sets antialias
+ * @param {boolean} [options.preserveDrawingBuffer=false] - Enables drawing buffer preservation, enable this if you
+ * need to call toDataUrl on the WebGL context.
+ * @param {number} [options.resolution=PIXI.settings.RESOLUTION] - The resolution / device pixel ratio of the renderer.
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present, this
+ * option only is available when using **pixi.js-legacy** or **@pixi/canvas-renderer** modules, otherwise
+ * it is ignored.
+ * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area
+ * (shown if not transparent).
+ * @param {number} [options.backgroundAlpha=1] - Value from 0 (fully transparent) to 1 (fully opaque).
+ * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or
+ * not before the new render pass.
+ * @param {string} [options.powerPreference] - Parameter passed to webgl context, set to "high-performance"
+ * for devices with dual graphics card. **(WebGL only)**.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.Ticker.shared, `false` to create new ticker.
+ * If set to false, you cannot register a handler to occur before anything that runs on the shared ticker.
+ * The system ticker will always run before both the shared ticker and the app ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.Loader.shared, `false` to create new Loader.
+ * @param {Window|HTMLElement} [options.resizeTo] - Element to automatically resize stage to.
+ */
+ function Application(options) {
+ var _this = this;
+ /**
+ * The root display container that's rendered.
+ * @member {PIXI.Container}
+ */
+ this.stage = new Container();
+ // The default options
+ options = Object.assign({
+ forceCanvas: false,
+ }, options);
+ this.renderer = autoDetectRenderer(options);
+ // install plugins here
+ Application._plugins.forEach(function (plugin) {
+ plugin.init.call(_this, options);
+ });
+ }
+ /**
+ * Use the {@link PIXI.extensions.add} API to register plugins.
+ * @deprecated since 6.5.0
+ * @static
+ * @param {PIXI.IApplicationPlugin} plugin - Plugin being installed
+ */
+ Application.registerPlugin = function (plugin) {
+ deprecation('6.5.0', 'Application.registerPlugin() is deprecated, use extensions.add()');
+ extensions.add({
+ type: exports.ExtensionType.Application,
+ ref: plugin,
+ });
+ };
+ /** Render the current stage. */
+ Application.prototype.render = function () {
+ this.renderer.render(this.stage);
+ };
+ Object.defineProperty(Application.prototype, "view", {
+ /**
+ * Reference to the renderer's canvas element.
+ * @member {HTMLCanvasElement}
+ * @readonly
+ */
+ get: function () {
+ return this.renderer.view;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Application.prototype, "screen", {
+ /**
+ * Reference to the renderer's screen rectangle. Its safe to use as `filterArea` or `hitArea` for the whole screen.
+ * @member {PIXI.Rectangle}
+ * @readonly
+ */
+ get: function () {
+ return this.renderer.screen;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [removeView=false] - Automatically remove canvas from DOM.
+ * @param {object|boolean} [stageOptions] - Options parameter. A boolean will act as if all options
+ * have been set to that value
+ * @param {boolean} [stageOptions.children=false] - if set to true, all the children will have their destroy
+ * method called as well. 'stageOptions' will be passed on to those calls.
+ * @param {boolean} [stageOptions.texture=false] - Only used for child Sprites if stageOptions.children is set
+ * to true. Should it destroy the texture of the child sprite
+ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set
+ * to true. Should it destroy the base texture of the child sprite
+ */
+ Application.prototype.destroy = function (removeView, stageOptions) {
+ var _this = this;
+ // Destroy plugins in the opposite order
+ // which they were constructed
+ var plugins = Application._plugins.slice(0);
+ plugins.reverse();
+ plugins.forEach(function (plugin) {
+ plugin.destroy.call(_this);
+ });
+ this.stage.destroy(stageOptions);
+ this.stage = null;
+ this.renderer.destroy(removeView);
+ this.renderer = null;
+ };
+ /** Collection of installed plugins. */
+ Application._plugins = [];
+ return Application;
+ }());
+ extensions.handleByList(exports.ExtensionType.Application, Application._plugins);
+
+ extensions.add(ResizePlugin);
+
+ /*!
+ * @pixi/mesh-extras - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/mesh-extras is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+ /* global Reflect, Promise */
+
+ var extendStatics$1 = function(d, b) {
+ extendStatics$1 = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
+ return extendStatics$1(d, b);
+ };
+
+ function __extends$1(d, b) {
+ extendStatics$1(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ }
+
+ /**
+ * @memberof PIXI
+ */
+ var PlaneGeometry = /** @class */ (function (_super) {
+ __extends$1(PlaneGeometry, _super);
+ /**
+ * @param width - The width of the plane.
+ * @param height - The height of the plane.
+ * @param segWidth - Number of horizontal segments.
+ * @param segHeight - Number of vertical segments.
+ */
+ function PlaneGeometry(width, height, segWidth, segHeight) {
+ if (width === void 0) { width = 100; }
+ if (height === void 0) { height = 100; }
+ if (segWidth === void 0) { segWidth = 10; }
+ if (segHeight === void 0) { segHeight = 10; }
+ var _this = _super.call(this) || this;
+ _this.segWidth = segWidth;
+ _this.segHeight = segHeight;
+ _this.width = width;
+ _this.height = height;
+ _this.build();
+ return _this;
+ }
+ /**
+ * Refreshes plane coordinates
+ * @private
+ */
+ PlaneGeometry.prototype.build = function () {
+ var total = this.segWidth * this.segHeight;
+ var verts = [];
+ var uvs = [];
+ var indices = [];
+ var segmentsX = this.segWidth - 1;
+ var segmentsY = this.segHeight - 1;
+ var sizeX = (this.width) / segmentsX;
+ var sizeY = (this.height) / segmentsY;
+ for (var i = 0; i < total; i++) {
+ var x = (i % this.segWidth);
+ var y = ((i / this.segWidth) | 0);
+ verts.push(x * sizeX, y * sizeY);
+ uvs.push(x / segmentsX, y / segmentsY);
+ }
+ var totalSub = segmentsX * segmentsY;
+ for (var i = 0; i < totalSub; i++) {
+ var xpos = i % segmentsX;
+ var ypos = (i / segmentsX) | 0;
+ var value = (ypos * this.segWidth) + xpos;
+ var value2 = (ypos * this.segWidth) + xpos + 1;
+ var value3 = ((ypos + 1) * this.segWidth) + xpos;
+ var value4 = ((ypos + 1) * this.segWidth) + xpos + 1;
+ indices.push(value, value2, value3, value2, value4, value3);
+ }
+ this.buffers[0].data = new Float32Array(verts);
+ this.buffers[1].data = new Float32Array(uvs);
+ this.indexBuffer.data = new Uint16Array(indices);
+ // ensure that the changes are uploaded
+ this.buffers[0].update();
+ this.buffers[1].update();
+ this.indexBuffer.update();
+ };
+ return PlaneGeometry;
+ }(MeshGeometry));
+
+ /**
+ * RopeGeometry allows you to draw a geometry across several points and then manipulate these points.
+ *
+ * ```js
+ * for (let i = 0; i < 20; i++) {
+ * points.push(new PIXI.Point(i * 50, 0));
+ * };
+ * const rope = new PIXI.RopeGeometry(100, points);
+ * ```
+ * @memberof PIXI
+ */
+ var RopeGeometry = /** @class */ (function (_super) {
+ __extends$1(RopeGeometry, _super);
+ /**
+ * @param width - The width (i.e., thickness) of the rope.
+ * @param points - An array of {@link PIXI.Point} objects to construct this rope.
+ * @param textureScale - By default the rope texture will be stretched to match
+ * rope length. If textureScale is positive this value will be treated as a scaling
+ * factor and the texture will preserve its aspect ratio instead. To create a tiling rope
+ * set baseTexture.wrapMode to {@link PIXI.WRAP_MODES.REPEAT} and use a power of two texture,
+ * then set textureScale=1 to keep the original texture pixel size.
+ * In order to reduce alpha channel artifacts provide a larger texture and downsample -
+ * i.e. set textureScale=0.5 to scale it down twice.
+ */
+ function RopeGeometry(width, points, textureScale) {
+ if (width === void 0) { width = 200; }
+ if (textureScale === void 0) { textureScale = 0; }
+ var _this = _super.call(this, new Float32Array(points.length * 4), new Float32Array(points.length * 4), new Uint16Array((points.length - 1) * 6)) || this;
+ _this.points = points;
+ _this._width = width;
+ _this.textureScale = textureScale;
+ _this.build();
+ return _this;
+ }
+ Object.defineProperty(RopeGeometry.prototype, "width", {
+ /**
+ * The width (i.e., thickness) of the rope.
+ * @readonly
+ */
+ get: function () {
+ return this._width;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /** Refreshes Rope indices and uvs */
+ RopeGeometry.prototype.build = function () {
+ var points = this.points;
+ if (!points)
+ { return; }
+ var vertexBuffer = this.getBuffer('aVertexPosition');
+ var uvBuffer = this.getBuffer('aTextureCoord');
+ var indexBuffer = this.getIndex();
+ // if too little points, or texture hasn't got UVs set yet just move on.
+ if (points.length < 1) {
+ return;
+ }
+ // if the number of points has changed we will need to recreate the arraybuffers
+ if (vertexBuffer.data.length / 4 !== points.length) {
+ vertexBuffer.data = new Float32Array(points.length * 4);
+ uvBuffer.data = new Float32Array(points.length * 4);
+ indexBuffer.data = new Uint16Array((points.length - 1) * 6);
+ }
+ var uvs = uvBuffer.data;
+ var indices = indexBuffer.data;
+ uvs[0] = 0;
+ uvs[1] = 0;
+ uvs[2] = 0;
+ uvs[3] = 1;
+ var amount = 0;
+ var prev = points[0];
+ var textureWidth = this._width * this.textureScale;
+ var total = points.length; // - 1;
+ for (var i = 0; i < total; i++) {
+ // time to do some smart drawing!
+ var index = i * 4;
+ if (this.textureScale > 0) {
+ // calculate pixel distance from previous point
+ var dx = prev.x - points[i].x;
+ var dy = prev.y - points[i].y;
+ var distance = Math.sqrt((dx * dx) + (dy * dy));
+ prev = points[i];
+ amount += distance / textureWidth;
+ }
+ else {
+ // stretch texture
+ amount = i / (total - 1);
+ }
+ uvs[index] = amount;
+ uvs[index + 1] = 0;
+ uvs[index + 2] = amount;
+ uvs[index + 3] = 1;
+ }
+ var indexCount = 0;
+ for (var i = 0; i < total - 1; i++) {
+ var index = i * 2;
+ indices[indexCount++] = index;
+ indices[indexCount++] = index + 1;
+ indices[indexCount++] = index + 2;
+ indices[indexCount++] = index + 2;
+ indices[indexCount++] = index + 1;
+ indices[indexCount++] = index + 3;
+ }
+ // ensure that the changes are uploaded
+ uvBuffer.update();
+ indexBuffer.update();
+ this.updateVertices();
+ };
+ /** refreshes vertices of Rope mesh */
+ RopeGeometry.prototype.updateVertices = function () {
+ var points = this.points;
+ if (points.length < 1) {
+ return;
+ }
+ var lastPoint = points[0];
+ var nextPoint;
+ var perpX = 0;
+ var perpY = 0;
+ var vertices = this.buffers[0].data;
+ var total = points.length;
+ for (var i = 0; i < total; i++) {
+ var point = points[i];
+ var index = i * 4;
+ if (i < points.length - 1) {
+ nextPoint = points[i + 1];
+ }
+ else {
+ nextPoint = point;
+ }
+ perpY = -(nextPoint.x - lastPoint.x);
+ perpX = nextPoint.y - lastPoint.y;
+ var perpLength = Math.sqrt((perpX * perpX) + (perpY * perpY));
+ var num = this.textureScale > 0 ? this.textureScale * this._width / 2 : this._width / 2;
+ perpX /= perpLength;
+ perpY /= perpLength;
+ perpX *= num;
+ perpY *= num;
+ vertices[index] = point.x + perpX;
+ vertices[index + 1] = point.y + perpY;
+ vertices[index + 2] = point.x - perpX;
+ vertices[index + 3] = point.y - perpY;
+ lastPoint = point;
+ }
+ this.buffers[0].update();
+ };
+ RopeGeometry.prototype.update = function () {
+ if (this.textureScale > 0) {
+ this.build(); // we need to update UVs
+ }
+ else {
+ this.updateVertices();
+ }
+ };
+ return RopeGeometry;
+ }(MeshGeometry));
+
+ /**
+ * The rope allows you to draw a texture across several points and then manipulate these points
+ *
+ *```js
+ * for (let i = 0; i < 20; i++) {
+ * points.push(new PIXI.Point(i * 50, 0));
+ * };
+ * let rope = new PIXI.SimpleRope(PIXI.Texture.from("snake.png"), points);
+ * ```
+ * @memberof PIXI
+ */
+ var SimpleRope = /** @class */ (function (_super) {
+ __extends$1(SimpleRope, _super);
+ /**
+ * @param texture - The texture to use on the rope.
+ * @param points - An array of {@link PIXI.Point} objects to construct this rope.
+ * @param {number} textureScale - Optional. Positive values scale rope texture
+ * keeping its aspect ratio. You can reduce alpha channel artifacts by providing a larger texture
+ * and downsampling here. If set to zero, texture will be stretched instead.
+ */
+ function SimpleRope(texture, points, textureScale) {
+ if (textureScale === void 0) { textureScale = 0; }
+ var _this = this;
+ var ropeGeometry = new RopeGeometry(texture.height, points, textureScale);
+ var meshMaterial = new MeshMaterial(texture);
+ if (textureScale > 0) {
+ // attempt to set UV wrapping, will fail on non-power of two textures
+ texture.baseTexture.wrapMode = exports.WRAP_MODES.REPEAT;
+ }
+ _this = _super.call(this, ropeGeometry, meshMaterial) || this;
+ /**
+ * re-calculate vertices by rope points each frame
+ * @member {boolean}
+ */
+ _this.autoUpdate = true;
+ return _this;
+ }
+ SimpleRope.prototype._render = function (renderer) {
+ var geometry = this.geometry;
+ if (this.autoUpdate || geometry._width !== this.shader.texture.height) {
+ geometry._width = this.shader.texture.height;
+ geometry.update();
+ }
+ _super.prototype._render.call(this, renderer);
+ };
+ return SimpleRope;
+ }(Mesh));
+
+ /**
+ * The SimplePlane allows you to draw a texture across several points and then manipulate these points
+ *
+ *```js
+ * for (let i = 0; i < 20; i++) {
+ * points.push(new PIXI.Point(i * 50, 0));
+ * };
+ * let SimplePlane = new PIXI.SimplePlane(PIXI.Texture.from("snake.png"), points);
+ * ```
+ * @memberof PIXI
+ */
+ var SimplePlane = /** @class */ (function (_super) {
+ __extends$1(SimplePlane, _super);
+ /**
+ * @param texture - The texture to use on the SimplePlane.
+ * @param verticesX - The number of vertices in the x-axis
+ * @param verticesY - The number of vertices in the y-axis
+ */
+ function SimplePlane(texture, verticesX, verticesY) {
+ var _this = this;
+ var planeGeometry = new PlaneGeometry(texture.width, texture.height, verticesX, verticesY);
+ var meshMaterial = new MeshMaterial(Texture.WHITE);
+ _this = _super.call(this, planeGeometry, meshMaterial) || this;
+ // lets call the setter to ensure all necessary updates are performed
+ _this.texture = texture;
+ _this.autoResize = true;
+ return _this;
+ }
+ /**
+ * Method used for overrides, to do something in case texture frame was changed.
+ * Meshes based on plane can override it and change more details based on texture.
+ */
+ SimplePlane.prototype.textureUpdated = function () {
+ this._textureID = this.shader.texture._updateID;
+ var geometry = this.geometry;
+ var _a = this.shader.texture, width = _a.width, height = _a.height;
+ if (this.autoResize && (geometry.width !== width || geometry.height !== height)) {
+ geometry.width = this.shader.texture.width;
+ geometry.height = this.shader.texture.height;
+ geometry.build();
+ }
+ };
+ Object.defineProperty(SimplePlane.prototype, "texture", {
+ get: function () {
+ return this.shader.texture;
+ },
+ set: function (value) {
+ // Track texture same way sprite does.
+ // For generated meshes like NineSlicePlane it can change the geometry.
+ // Unfortunately, this method might not work if you directly change texture in material.
+ if (this.shader.texture === value) {
+ return;
+ }
+ this.shader.texture = value;
+ this._textureID = -1;
+ if (value.baseTexture.valid) {
+ this.textureUpdated();
+ }
+ else {
+ value.once('update', this.textureUpdated, this);
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ SimplePlane.prototype._render = function (renderer) {
+ if (this._textureID !== this.shader.texture._updateID) {
+ this.textureUpdated();
+ }
+ _super.prototype._render.call(this, renderer);
+ };
+ SimplePlane.prototype.destroy = function (options) {
+ this.shader.texture.off('update', this.textureUpdated, this);
+ _super.prototype.destroy.call(this, options);
+ };
+ return SimplePlane;
+ }(Mesh));
+
+ /**
+ * The Simple Mesh class mimics Mesh in PixiJS v4, providing easy-to-use constructor arguments.
+ * For more robust customization, use {@link PIXI.Mesh}.
+ * @memberof PIXI
+ */
+ var SimpleMesh = /** @class */ (function (_super) {
+ __extends$1(SimpleMesh, _super);
+ /**
+ * @param texture - The texture to use
+ * @param {Float32Array} [vertices] - if you want to specify the vertices
+ * @param {Float32Array} [uvs] - if you want to specify the uvs
+ * @param {Uint16Array} [indices] - if you want to specify the indices
+ * @param drawMode - the drawMode, can be any of the Mesh.DRAW_MODES consts
+ */
+ function SimpleMesh(texture, vertices, uvs, indices, drawMode) {
+ if (texture === void 0) { texture = Texture.EMPTY; }
+ var _this = this;
+ var geometry = new MeshGeometry(vertices, uvs, indices);
+ geometry.getBuffer('aVertexPosition').static = false;
+ var meshMaterial = new MeshMaterial(texture);
+ _this = _super.call(this, geometry, meshMaterial, null, drawMode) || this;
+ _this.autoUpdate = true;
+ return _this;
+ }
+ Object.defineProperty(SimpleMesh.prototype, "vertices", {
+ /**
+ * Collection of vertices data.
+ * @type {Float32Array}
+ */
+ get: function () {
+ return this.geometry.getBuffer('aVertexPosition').data;
+ },
+ set: function (value) {
+ this.geometry.getBuffer('aVertexPosition').data = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ SimpleMesh.prototype._render = function (renderer) {
+ if (this.autoUpdate) {
+ this.geometry.getBuffer('aVertexPosition').update();
+ }
+ _super.prototype._render.call(this, renderer);
+ };
+ return SimpleMesh;
+ }(Mesh));
+
+ var DEFAULT_BORDER_SIZE = 10;
+ /**
+ * The NineSlicePlane allows you to stretch a texture using 9-slice scaling. The corners will remain unscaled (useful
+ * for buttons with rounded corners for example) and the other areas will be scaled horizontally and or vertically
+ *
+ *```js
+ * let Plane9 = new PIXI.NineSlicePlane(PIXI.Texture.from('BoxWithRoundedCorners.png'), 15, 15, 15, 15);
+ * ```
+ * <pre>
+ * A B
+ * +---+----------------------+---+
+ * C | 1 | 2 | 3 |
+ * +---+----------------------+---+
+ * | | | |
+ * | 4 | 5 | 6 |
+ * | | | |
+ * +---+----------------------+---+
+ * D | 7 | 8 | 9 |
+ * +---+----------------------+---+
+ * When changing this objects width and/or height:
+ * areas 1 3 7 and 9 will remain unscaled.
+ * areas 2 and 8 will be stretched horizontally
+ * areas 4 and 6 will be stretched vertically
+ * area 5 will be stretched both horizontally and vertically
+ * </pre>
+ * @memberof PIXI
+ */
+ var NineSlicePlane = /** @class */ (function (_super) {
+ __extends$1(NineSlicePlane, _super);
+ /**
+ * @param texture - The texture to use on the NineSlicePlane.
+ * @param {number} [leftWidth=10] - size of the left vertical bar (A)
+ * @param {number} [topHeight=10] - size of the top horizontal bar (C)
+ * @param {number} [rightWidth=10] - size of the right vertical bar (B)
+ * @param {number} [bottomHeight=10] - size of the bottom horizontal bar (D)
+ */
+ function NineSlicePlane(texture, leftWidth, topHeight, rightWidth, bottomHeight) {
+ if (leftWidth === void 0) { leftWidth = DEFAULT_BORDER_SIZE; }
+ if (topHeight === void 0) { topHeight = DEFAULT_BORDER_SIZE; }
+ if (rightWidth === void 0) { rightWidth = DEFAULT_BORDER_SIZE; }
+ if (bottomHeight === void 0) { bottomHeight = DEFAULT_BORDER_SIZE; }
+ var _this = _super.call(this, Texture.WHITE, 4, 4) || this;
+ _this._origWidth = texture.orig.width;
+ _this._origHeight = texture.orig.height;
+ /** The width of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane. */
+ _this._width = _this._origWidth;
+ /** The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane. */
+ _this._height = _this._origHeight;
+ _this._leftWidth = leftWidth;
+ _this._rightWidth = rightWidth;
+ _this._topHeight = topHeight;
+ _this._bottomHeight = bottomHeight;
+ // lets call the setter to ensure all necessary updates are performed
+ _this.texture = texture;
+ return _this;
+ }
+ NineSlicePlane.prototype.textureUpdated = function () {
+ this._textureID = this.shader.texture._updateID;
+ this._refresh();
+ };
+ Object.defineProperty(NineSlicePlane.prototype, "vertices", {
+ get: function () {
+ return this.geometry.getBuffer('aVertexPosition').data;
+ },
+ set: function (value) {
+ this.geometry.getBuffer('aVertexPosition').data = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /** Updates the horizontal vertices. */
+ NineSlicePlane.prototype.updateHorizontalVertices = function () {
+ var vertices = this.vertices;
+ var scale = this._getMinScale();
+ vertices[9] = vertices[11] = vertices[13] = vertices[15] = this._topHeight * scale;
+ vertices[17] = vertices[19] = vertices[21] = vertices[23] = this._height - (this._bottomHeight * scale);
+ vertices[25] = vertices[27] = vertices[29] = vertices[31] = this._height;
+ };
+ /** Updates the vertical vertices. */
+ NineSlicePlane.prototype.updateVerticalVertices = function () {
+ var vertices = this.vertices;
+ var scale = this._getMinScale();
+ vertices[2] = vertices[10] = vertices[18] = vertices[26] = this._leftWidth * scale;
+ vertices[4] = vertices[12] = vertices[20] = vertices[28] = this._width - (this._rightWidth * scale);
+ vertices[6] = vertices[14] = vertices[22] = vertices[30] = this._width;
+ };
+ /**
+ * Returns the smaller of a set of vertical and horizontal scale of nine slice corners.
+ * @returns Smaller number of vertical and horizontal scale.
+ */
+ NineSlicePlane.prototype._getMinScale = function () {
+ var w = this._leftWidth + this._rightWidth;
+ var scaleW = this._width > w ? 1.0 : this._width / w;
+ var h = this._topHeight + this._bottomHeight;
+ var scaleH = this._height > h ? 1.0 : this._height / h;
+ var scale = Math.min(scaleW, scaleH);
+ return scale;
+ };
+ Object.defineProperty(NineSlicePlane.prototype, "width", {
+ /** The width of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane. */
+ get: function () {
+ return this._width;
+ },
+ set: function (value) {
+ this._width = value;
+ this._refresh();
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(NineSlicePlane.prototype, "height", {
+ /** The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane. */
+ get: function () {
+ return this._height;
+ },
+ set: function (value) {
+ this._height = value;
+ this._refresh();
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(NineSlicePlane.prototype, "leftWidth", {
+ /** The width of the left column. */
+ get: function () {
+ return this._leftWidth;
+ },
+ set: function (value) {
+ this._leftWidth = value;
+ this._refresh();
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(NineSlicePlane.prototype, "rightWidth", {
+ /** The width of the right column. */
+ get: function () {
+ return this._rightWidth;
+ },
+ set: function (value) {
+ this._rightWidth = value;
+ this._refresh();
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(NineSlicePlane.prototype, "topHeight", {
+ /** The height of the top row. */
+ get: function () {
+ return this._topHeight;
+ },
+ set: function (value) {
+ this._topHeight = value;
+ this._refresh();
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(NineSlicePlane.prototype, "bottomHeight", {
+ /** The height of the bottom row. */
+ get: function () {
+ return this._bottomHeight;
+ },
+ set: function (value) {
+ this._bottomHeight = value;
+ this._refresh();
+ },
+ enumerable: false,
+ configurable: true
+ });
+ /** Refreshes NineSlicePlane coords. All of them. */
+ NineSlicePlane.prototype._refresh = function () {
+ var texture = this.texture;
+ var uvs = this.geometry.buffers[1].data;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
+ var _uvw = 1.0 / this._origWidth;
+ var _uvh = 1.0 / this._origHeight;
+ uvs[0] = uvs[8] = uvs[16] = uvs[24] = 0;
+ uvs[1] = uvs[3] = uvs[5] = uvs[7] = 0;
+ uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
+ uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
+ uvs[2] = uvs[10] = uvs[18] = uvs[26] = _uvw * this._leftWidth;
+ uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (_uvw * this._rightWidth);
+ uvs[9] = uvs[11] = uvs[13] = uvs[15] = _uvh * this._topHeight;
+ uvs[17] = uvs[19] = uvs[21] = uvs[23] = 1 - (_uvh * this._bottomHeight);
+ this.updateHorizontalVertices();
+ this.updateVerticalVertices();
+ this.geometry.buffers[0].update();
+ this.geometry.buffers[1].update();
+ };
+ return NineSlicePlane;
+ }(SimplePlane));
+
+ /*!
+ * @pixi/sprite-animated - v6.5.3
+ * Compiled Fri, 09 Sep 2022 13:55:20 UTC
+ *
+ * @pixi/sprite-animated is licensed under the MIT License.
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */
+ /* global Reflect, Promise */
+
+ var extendStatics = function(d, b) {
+ extendStatics = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
+ return extendStatics(d, b);
+ };
+
+ function __extends(d, b) {
+ extendStatics(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ }
+
+ /**
+ * An AnimatedSprite is a simple way to display an animation depicted by a list of textures.
+ *
+ * ```js
+ * let alienImages = ["image_sequence_01.png","image_sequence_02.png","image_sequence_03.png","image_sequence_04.png"];
+ * let textureArray = [];
+ *
+ * for (let i=0; i < 4; i++)
+ * {
+ * let texture = PIXI.Texture.from(alienImages[i]);
+ * textureArray.push(texture);
+ * };
+ *
+ * let animatedSprite = new PIXI.AnimatedSprite(textureArray);
+ * ```
+ *
+ * The more efficient and simpler way to create an animated sprite is using a {@link PIXI.Spritesheet}
+ * containing the animation definitions:
+ *
+ * ```js
+ * PIXI.Loader.shared.add("assets/spritesheet.json").load(setup);
+ *
+ * function setup() {
+ * let sheet = PIXI.Loader.shared.resources["assets/spritesheet.json"].spritesheet;
+ * animatedSprite = new PIXI.AnimatedSprite(sheet.animations["image_sequence"]);
+ * ...
+ * }
+ * ```
+ * @memberof PIXI
+ */
+ var AnimatedSprite = /** @class */ (function (_super) {
+ __extends(AnimatedSprite, _super);
+ /**
+ * @param textures - An array of {@link PIXI.Texture} or frame
+ * objects that make up the animation.
+ * @param {boolean} [autoUpdate=true] - Whether to use PIXI.Ticker.shared to auto update animation time.
+ */
+ function AnimatedSprite(textures, autoUpdate) {
+ if (autoUpdate === void 0) { autoUpdate = true; }
+ var _this = _super.call(this, textures[0] instanceof Texture ? textures[0] : textures[0].texture) || this;
+ _this._textures = null;
+ _this._durations = null;
+ _this._autoUpdate = autoUpdate;
+ _this._isConnectedToTicker = false;
+ _this.animationSpeed = 1;
+ _this.loop = true;
+ _this.updateAnchor = false;
+ _this.onComplete = null;
+ _this.onFrameChange = null;
+ _this.onLoop = null;
+ _this._currentTime = 0;
+ _this._playing = false;
+ _this._previousFrame = null;
+ _this.textures = textures;
+ return _this;
+ }
+ /** Stops the AnimatedSprite. */
+ AnimatedSprite.prototype.stop = function () {
+ if (!this._playing) {
+ return;
+ }
+ this._playing = false;
+ if (this._autoUpdate && this._isConnectedToTicker) {
+ Ticker.shared.remove(this.update, this);
+ this._isConnectedToTicker = false;
+ }
+ };
+ /** Plays the AnimatedSprite. */
+ AnimatedSprite.prototype.play = function () {
+ if (this._playing) {
+ return;
+ }
+ this._playing = true;
+ if (this._autoUpdate && !this._isConnectedToTicker) {
+ Ticker.shared.add(this.update, this, exports.UPDATE_PRIORITY.HIGH);
+ this._isConnectedToTicker = true;
+ }
+ };
+ /**
+ * Stops the AnimatedSprite and goes to a specific frame.
+ * @param frameNumber - Frame index to stop at.
+ */
+ AnimatedSprite.prototype.gotoAndStop = function (frameNumber) {
+ this.stop();
+ var previousFrame = this.currentFrame;
+ this._currentTime = frameNumber;
+ if (previousFrame !== this.currentFrame) {
+ this.updateTexture();
+ }
+ };
+ /**
+ * Goes to a specific frame and begins playing the AnimatedSprite.
+ * @param frameNumber - Frame index to start at.
+ */
+ AnimatedSprite.prototype.gotoAndPlay = function (frameNumber) {
+ var previousFrame = this.currentFrame;
+ this._currentTime = frameNumber;
+ if (previousFrame !== this.currentFrame) {
+ this.updateTexture();
+ }
+ this.play();
+ };
+ /**
+ * Updates the object transform for rendering.
+ * @param deltaTime - Time since last tick.
+ */
+ AnimatedSprite.prototype.update = function (deltaTime) {
+ if (!this._playing) {
+ return;
+ }
+ var elapsed = this.animationSpeed * deltaTime;
+ var previousFrame = this.currentFrame;
+ if (this._durations !== null) {
+ var lag = this._currentTime % 1 * this._durations[this.currentFrame];
+ lag += elapsed / 60 * 1000;
+ while (lag < 0) {
+ this._currentTime--;
+ lag += this._durations[this.currentFrame];
+ }
+ var sign = Math.sign(this.animationSpeed * deltaTime);
+ this._currentTime = Math.floor(this._currentTime);
+ while (lag >= this._durations[this.currentFrame]) {
+ lag -= this._durations[this.currentFrame] * sign;
+ this._currentTime += sign;
+ }
+ this._currentTime += lag / this._durations[this.currentFrame];
+ }
+ else {
+ this._currentTime += elapsed;
+ }
+ if (this._currentTime < 0 && !this.loop) {
+ this.gotoAndStop(0);
+ if (this.onComplete) {
+ this.onComplete();
+ }
+ }
+ else if (this._currentTime >= this._textures.length && !this.loop) {
+ this.gotoAndStop(this._textures.length - 1);
+ if (this.onComplete) {
+ this.onComplete();
+ }
+ }
+ else if (previousFrame !== this.currentFrame) {
+ if (this.loop && this.onLoop) {
+ if (this.animationSpeed > 0 && this.currentFrame < previousFrame) {
+ this.onLoop();
+ }
+ else if (this.animationSpeed < 0 && this.currentFrame > previousFrame) {
+ this.onLoop();
+ }
+ }
+ this.updateTexture();
+ }
+ };
+ /** Updates the displayed texture to match the current frame index. */
+ AnimatedSprite.prototype.updateTexture = function () {
+ var currentFrame = this.currentFrame;
+ if (this._previousFrame === currentFrame) {
+ return;
+ }
+ this._previousFrame = currentFrame;
+ this._texture = this._textures[currentFrame];
+ this._textureID = -1;
+ this._textureTrimmedID = -1;
+ this._cachedTint = 0xFFFFFF;
+ this.uvs = this._texture._uvs.uvsFloat32;
+ if (this.updateAnchor) {
+ this._anchor.copyFrom(this._texture.defaultAnchor);
+ }
+ if (this.onFrameChange) {
+ this.onFrameChange(this.currentFrame);
+ }
+ };
+ /**
+ * Stops the AnimatedSprite and destroys it.
+ * @param {object|boolean} [options] - Options parameter. A boolean will act as if all options
+ * have been set to that value.
+ * @param {boolean} [options.children=false] - If set to true, all the children will have their destroy
+ * method called as well. 'options' will be passed on to those calls.
+ * @param {boolean} [options.texture=false] - Should it destroy the current texture of the sprite as well.
+ * @param {boolean} [options.baseTexture=false] - Should it destroy the base texture of the sprite as well.
+ */
+ AnimatedSprite.prototype.destroy = function (options) {
+ this.stop();
+ _super.prototype.destroy.call(this, options);
+ this.onComplete = null;
+ this.onFrameChange = null;
+ this.onLoop = null;
+ };
+ /**
+ * A short hand way of creating an AnimatedSprite from an array of frame ids.
+ * @param frames - The array of frames ids the AnimatedSprite will use as its texture frames.
+ * @returns - The new animated sprite with the specified frames.
+ */
+ AnimatedSprite.fromFrames = function (frames) {
+ var textures = [];
+ for (var i = 0; i < frames.length; ++i) {
+ textures.push(Texture.from(frames[i]));
+ }
+ return new AnimatedSprite(textures);
+ };
+ /**
+ * A short hand way of creating an AnimatedSprite from an array of image ids.
+ * @param images - The array of image urls the AnimatedSprite will use as its texture frames.
+ * @returns The new animate sprite with the specified images as frames.
+ */
+ AnimatedSprite.fromImages = function (images) {
+ var textures = [];
+ for (var i = 0; i < images.length; ++i) {
+ textures.push(Texture.from(images[i]));
+ }
+ return new AnimatedSprite(textures);
+ };
+ Object.defineProperty(AnimatedSprite.prototype, "totalFrames", {
+ /**
+ * The total number of frames in the AnimatedSprite. This is the same as number of textures
+ * assigned to the AnimatedSprite.
+ * @readonly
+ * @default 0
+ */
+ get: function () {
+ return this._textures.length;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(AnimatedSprite.prototype, "textures", {
+ /** The array of textures used for this AnimatedSprite. */
+ get: function () {
+ return this._textures;
+ },
+ set: function (value) {
+ if (value[0] instanceof Texture) {
+ this._textures = value;
+ this._durations = null;
+ }
+ else {
+ this._textures = [];
+ this._durations = [];
+ for (var i = 0; i < value.length; i++) {
+ this._textures.push(value[i].texture);
+ this._durations.push(value[i].time);
+ }
+ }
+ this._previousFrame = null;
+ this.gotoAndStop(0);
+ this.updateTexture();
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(AnimatedSprite.prototype, "currentFrame", {
+ /**
+ * The AnimatedSprites current frame index.
+ * @readonly
+ */
+ get: function () {
+ var currentFrame = Math.floor(this._currentTime) % this._textures.length;
+ if (currentFrame < 0) {
+ currentFrame += this._textures.length;
+ }
+ return currentFrame;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(AnimatedSprite.prototype, "playing", {
+ /**
+ * Indicates if the AnimatedSprite is currently playing.
+ * @readonly
+ */
+ get: function () {
+ return this._playing;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(AnimatedSprite.prototype, "autoUpdate", {
+ /** Whether to use PIXI.Ticker.shared to auto update animation time. */
+ get: function () {
+ return this._autoUpdate;
+ },
+ set: function (value) {
+ if (value !== this._autoUpdate) {
+ this._autoUpdate = value;
+ if (!this._autoUpdate && this._isConnectedToTicker) {
+ Ticker.shared.remove(this.update, this);
+ this._isConnectedToTicker = false;
+ }
+ else if (this._autoUpdate && !this._isConnectedToTicker && this._playing) {
+ Ticker.shared.add(this.update, this);
+ this._isConnectedToTicker = true;
+ }
+ }
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return AnimatedSprite;
+ }(Sprite));
+
+ extensions.add(
+ // Install renderer plugins
+ AccessibilityManager, Extract, InteractionManager, ParticleRenderer, Prepare, BatchRenderer, TilingSpriteRenderer,
+ // Install loader plugins
+ BitmapFontLoader, CompressedTextureLoader, DDSLoader, KTXLoader, SpritesheetLoader,
+ // Install application plugins
+ TickerPlugin, AppLoaderPlugin);
+ /**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ *
+ * Since PixiJS only had a handful of built-in filters, additional filters
+ * can be downloaded {@link https://github.com/pixijs/pixi-filters here} from the
+ * PixiJS Filters repository.
+ *
+ * All filters must extend {@link PIXI.Filter}.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
+ * @namespace PIXI.filters
+ */
+ var filters = {
+ AlphaFilter: AlphaFilter,
+ BlurFilter: BlurFilter,
+ BlurFilterPass: BlurFilterPass,
+ ColorMatrixFilter: ColorMatrixFilter,
+ DisplacementFilter: DisplacementFilter,
+ FXAAFilter: FXAAFilter,
+ NoiseFilter: NoiseFilter,
+ };
+
+ exports.AbstractBatchRenderer = AbstractBatchRenderer;
+ exports.AbstractMultiResource = AbstractMultiResource;
+ exports.AbstractRenderer = AbstractRenderer;
+ exports.AccessibilityManager = AccessibilityManager;
+ exports.AnimatedSprite = AnimatedSprite;
+ exports.AppLoaderPlugin = AppLoaderPlugin;
+ exports.Application = Application;
+ exports.ArrayResource = ArrayResource;
+ exports.Attribute = Attribute;
+ exports.BaseImageResource = BaseImageResource;
+ exports.BasePrepare = BasePrepare;
+ exports.BaseRenderTexture = BaseRenderTexture;
+ exports.BaseTexture = BaseTexture;
+ exports.BatchDrawCall = BatchDrawCall;
+ exports.BatchGeometry = BatchGeometry;
+ exports.BatchPluginFactory = BatchPluginFactory;
+ exports.BatchRenderer = BatchRenderer;
+ exports.BatchShaderGenerator = BatchShaderGenerator;
+ exports.BatchSystem = BatchSystem;
+ exports.BatchTextureArray = BatchTextureArray;
+ exports.BitmapFont = BitmapFont;
+ exports.BitmapFontData = BitmapFontData;
+ exports.BitmapFontLoader = BitmapFontLoader;
+ exports.BitmapText = BitmapText;
+ exports.BlobResource = BlobResource;
+ exports.Bounds = Bounds;
+ exports.BrowserAdapter = BrowserAdapter$1;
+ exports.Buffer = Buffer;
+ exports.BufferResource = BufferResource;
+ exports.CanvasResource = CanvasResource;
+ exports.Circle = Circle;
+ exports.CompressedTextureLoader = CompressedTextureLoader;
+ exports.CompressedTextureResource = CompressedTextureResource;
+ exports.Container = Container;
+ exports.ContextSystem = ContextSystem;
+ exports.CountLimiter = CountLimiter;
+ exports.CubeResource = CubeResource;
+ exports.DDSLoader = DDSLoader;
+ exports.DEG_TO_RAD = DEG_TO_RAD;
+ exports.DisplayObject = DisplayObject;
+ exports.Ellipse = Ellipse;
+ exports.Extract = Extract;
+ exports.FORMATS_TO_COMPONENTS = FORMATS_TO_COMPONENTS;
+ exports.FillStyle = FillStyle;
+ exports.Filter = Filter;
+ exports.FilterState = FilterState;
+ exports.FilterSystem = FilterSystem;
+ exports.Framebuffer = Framebuffer;
+ exports.FramebufferSystem = FramebufferSystem;
+ exports.GLFramebuffer = GLFramebuffer;
+ exports.GLProgram = GLProgram;
+ exports.GLTexture = GLTexture;
+ exports.GRAPHICS_CURVES = GRAPHICS_CURVES;
+ exports.Geometry = Geometry;
+ exports.GeometrySystem = GeometrySystem;
+ exports.Graphics = Graphics;
+ exports.GraphicsData = GraphicsData;
+ exports.GraphicsGeometry = GraphicsGeometry;
+ exports.IGLUniformData = IGLUniformData;
+ exports.INSTALLED = INSTALLED;
+ exports.INTERNAL_FORMAT_TO_BYTES_PER_PIXEL = INTERNAL_FORMAT_TO_BYTES_PER_PIXEL;
+ exports.ImageBitmapResource = ImageBitmapResource;
+ exports.ImageResource = ImageResource;
+ exports.InteractionData = InteractionData;
+ exports.InteractionEvent = InteractionEvent;
+ exports.InteractionManager = InteractionManager;
+ exports.InteractionTrackingData = InteractionTrackingData;
+ exports.KTXLoader = KTXLoader;
+ exports.LineStyle = LineStyle;
+ exports.Loader = Loader;
+ exports.MaskData = MaskData;
+ exports.MaskSystem = MaskSystem;
+ exports.Matrix = Matrix;
+ exports.Mesh = Mesh;
+ exports.MeshBatchUvs = MeshBatchUvs;
+ exports.MeshGeometry = MeshGeometry;
+ exports.MeshMaterial = MeshMaterial;
+ exports.NineSlicePlane = NineSlicePlane;
+ exports.ObjectRenderer = ObjectRenderer;
+ exports.ObservablePoint = ObservablePoint;
+ exports.PI_2 = PI_2;
+ exports.ParticleContainer = ParticleContainer;
+ exports.ParticleRenderer = ParticleRenderer;
+ exports.PlaneGeometry = PlaneGeometry;
+ exports.Point = Point;
+ exports.Polygon = Polygon;
+ exports.Prepare = Prepare;
+ exports.Program = Program;
+ exports.ProjectionSystem = ProjectionSystem;
+ exports.Quad = Quad;
+ exports.QuadUv = QuadUv;
+ exports.RAD_TO_DEG = RAD_TO_DEG;
+ exports.Rectangle = Rectangle;
+ exports.RenderTexture = RenderTexture;
+ exports.RenderTexturePool = RenderTexturePool;
+ exports.RenderTextureSystem = RenderTextureSystem;
+ exports.Renderer = Renderer;
+ exports.ResizePlugin = ResizePlugin;
+ exports.Resource = Resource;
+ exports.RopeGeometry = RopeGeometry;
+ exports.RoundedRectangle = RoundedRectangle;
+ exports.Runner = Runner;
+ exports.SVGResource = SVGResource;
+ exports.ScissorSystem = ScissorSystem;
+ exports.Shader = Shader;
+ exports.ShaderSystem = ShaderSystem;
+ exports.SimpleMesh = SimpleMesh;
+ exports.SimplePlane = SimplePlane;
+ exports.SimpleRope = SimpleRope;
+ exports.Sprite = Sprite;
+ exports.SpriteMaskFilter = SpriteMaskFilter;
+ exports.Spritesheet = Spritesheet;
+ exports.SpritesheetLoader = SpritesheetLoader;
+ exports.State = State;
+ exports.StateSystem = StateSystem;
+ exports.StencilSystem = StencilSystem;
+ exports.System = System;
+ exports.TYPES_TO_BYTES_PER_COMPONENT = TYPES_TO_BYTES_PER_COMPONENT;
+ exports.TYPES_TO_BYTES_PER_PIXEL = TYPES_TO_BYTES_PER_PIXEL;
+ exports.TemporaryDisplayObject = TemporaryDisplayObject;
+ exports.Text = Text;
+ exports.TextFormat = TextFormat;
+ exports.TextMetrics = TextMetrics;
+ exports.TextStyle = TextStyle;
+ exports.Texture = Texture;
+ exports.TextureGCSystem = TextureGCSystem;
+ exports.TextureLoader = TextureLoader;
+ exports.TextureMatrix = TextureMatrix;
+ exports.TextureSystem = TextureSystem;
+ exports.TextureUvs = TextureUvs;
+ exports.Ticker = Ticker;
+ exports.TickerPlugin = TickerPlugin;
+ exports.TilingSprite = TilingSprite;
+ exports.TilingSpriteRenderer = TilingSpriteRenderer;
+ exports.TimeLimiter = TimeLimiter;
+ exports.Transform = Transform;
+ exports.UniformGroup = UniformGroup;
+ exports.VERSION = VERSION;
+ exports.VideoResource = VideoResource;
+ exports.ViewableBuffer = ViewableBuffer;
+ exports.XMLFormat = XMLFormat;
+ exports.XMLStringFormat = XMLStringFormat;
+ exports.accessibleTarget = accessibleTarget;
+ exports.autoDetectFormat = autoDetectFormat;
+ exports.autoDetectRenderer = autoDetectRenderer;
+ exports.autoDetectResource = autoDetectResource;
+ exports.checkMaxIfStatementsInShader = checkMaxIfStatementsInShader;
+ exports.createUBOElements = createUBOElements;
+ exports.defaultFilterVertex = defaultFilterVertex;
+ exports.defaultVertex = defaultVertex$1;
+ exports.extensions = extensions;
+ exports.filters = filters;
+ exports.generateProgram = generateProgram;
+ exports.generateUniformBufferSync = generateUniformBufferSync;
+ exports.getTestContext = getTestContext;
+ exports.getUBOData = getUBOData;
+ exports.graphicsUtils = graphicsUtils;
+ exports.groupD8 = groupD8;
+ exports.interactiveTarget = interactiveTarget;
+ exports.isMobile = isMobile$2;
+ exports.parseDDS = parseDDS;
+ exports.parseKTX = parseKTX;
+ exports.resources = resources;
+ exports.settings = settings$1;
+ exports.systems = systems;
+ exports.uniformParsers = uniformParsers;
+ exports.utils = utils;
+
+ Object.defineProperty(exports, '__esModule', { value: true });
+
+ return exports;
+
+})({});
+//# sourceMappingURL=pixi.js.map
diff --git a/xiaozhi-esp32-server/main/digital-human/js/ui/background-load.js b/xiaozhi-esp32-server/main/digital-human/js/ui/background-load.js
new file mode 100644
index 0000000..b956f73
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/js/ui/background-load.js
@@ -0,0 +1,27 @@
+// 鑳屾櫙鍥惧姞杞芥娴�
+(function() {
+ const backgroundContainer = document.getElementById('backgroundContainer');
+
+ // 鎻愬彇鑳屾櫙鍥剧墖URL
+ let bgImageUrl = window.getComputedStyle(backgroundContainer).backgroundImage;
+ const urlMatch = bgImageUrl && bgImageUrl.match(/url\(["']?(.*?)["']?\)/);
+
+ if (!urlMatch || !urlMatch[1]) {
+ console.warn('鏈彁鍙栧埌鏈夋晥鐨勮儗鏅浘鐗嘦RL');
+ return;
+ }
+
+ bgImageUrl = urlMatch[1];
+
+ const bgImage = new Image();
+ bgImage.onerror = function() {
+ console.error('鑳屾櫙鍥剧墖鍔犺浇澶辫触:', bgImageUrl);
+ };
+
+ // 鍔犺浇鎴愬姛鏄剧ず妯″瀷鍔犺浇
+ bgImage.onload = function() {
+ modelLoading.style.display = 'flex';
+ };
+
+ bgImage.src = bgImageUrl;
+})();
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/js/ui/controller.js b/xiaozhi-esp32-server/main/digital-human/js/ui/controller.js
new file mode 100644
index 0000000..b71abab
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/js/ui/controller.js
@@ -0,0 +1,929 @@
+// UI controller module
+import { loadConfig, saveConfig } from '../config/manager.js?v=0205';
+import { getAudioPlayer } from '../core/audio/player.js?v=0205';
+import { getAudioRecorder } from '../core/audio/recorder.js?v=0205';
+import { requestWakewordBridge, stopWakewordBridgeListener, startWakewordBridgeListener, getWakewordBridgeUrl, onNextBridgeConnected } from '../core/network/wakeword-bridge.js?v=0205';
+import { getWebSocketHandler } from '../core/network/websocket.js?v=0205';
+import { log } from '../utils/logger.js?v=0205';
+
+// UI controller class
+class UIController {
+ constructor() {
+ this.isEditing = false;
+ this.visualizerCanvas = null;
+ this.visualizerContext = null;
+ this.audioStatsTimer = null;
+ this.currentBackgroundIndex = localStorage.getItem('backgroundIndex') ? parseInt(localStorage.getItem('backgroundIndex')) : 0;
+ this.backgroundImages = ['1.png', '2.png', '3.png'];
+ this.dialBtnDisabled = false;
+ this.isConnecting = false;
+ this.lastWakewordDialTime = 0;
+
+ // Bind methods
+ this.init = this.init.bind(this);
+ this.initEventListeners = this.initEventListeners.bind(this);
+ this.updateDialButton = this.updateDialButton.bind(this);
+ this.addChatMessage = this.addChatMessage.bind(this);
+ this.switchBackground = this.switchBackground.bind(this);
+ this.switchLive2DModel = this.switchLive2DModel.bind(this);
+ this.showModal = this.showModal.bind(this);
+ this.hideModal = this.hideModal.bind(this);
+ this.switchTab = this.switchTab.bind(this);
+ this.applyWakewordConfig = this.applyWakewordConfig.bind(this);
+ this.handleApplyWakeword = this.handleApplyWakeword.bind(this);
+ this.triggerWakewordDial = this.triggerWakewordDial.bind(this);
+ }
+
+ // Initialize
+ init() {
+ console.log('UIController init started');
+
+ this.visualizerCanvas = document.getElementById('audioVisualizer');
+ if (this.visualizerCanvas) {
+ this.visualizerContext = this.visualizerCanvas.getContext('2d');
+ this.initVisualizer();
+ }
+
+ // Check if connect button exists during initialization
+ const connectBtn = document.getElementById('connectBtn');
+ console.log('connectBtn during init:', connectBtn);
+
+ this.initEventListeners();
+ this.startAudioStatsMonitor();
+ loadConfig();
+
+ // Register recording callback
+ const audioRecorder = getAudioRecorder();
+ audioRecorder.onRecordingStart = (seconds) => {
+ this.updateRecordButtonState(true, seconds);
+ };
+
+ // Initialize status display
+ this.updateConnectionUI(false);
+ // Apply saved background
+ const backgroundContainer = document.querySelector('.background-container');
+ if (backgroundContainer) {
+ backgroundContainer.style.backgroundImage = `url('./images/${this.backgroundImages[this.currentBackgroundIndex]}')`;
+ }
+
+ this.updateDialButton(false);
+
+ console.log('UIController init completed');
+ }
+
+ // Initialize visualizer
+ initVisualizer() {
+ if (this.visualizerCanvas) {
+ this.visualizerCanvas.width = this.visualizerCanvas.clientWidth;
+ this.visualizerCanvas.height = this.visualizerCanvas.clientHeight;
+ this.visualizerContext.fillStyle = '#fafafa';
+ this.visualizerContext.fillRect(0, 0, this.visualizerCanvas.width, this.visualizerCanvas.height);
+ }
+ }
+
+ // Initialize event listeners
+ initEventListeners() {
+ // Settings button
+ const settingsBtn = document.getElementById('settingsBtn');
+ if (settingsBtn) {
+ settingsBtn.addEventListener('click', () => {
+ this.showModal('settingsModal');
+ });
+ }
+
+ // Background switch button
+ const backgroundBtn = document.getElementById('backgroundBtn');
+ if (backgroundBtn) {
+ backgroundBtn.addEventListener('click', this.switchBackground);
+ }
+
+ // Model select change event
+ const modelSelect = document.getElementById('live2dModelSelect');
+ if (modelSelect) {
+ modelSelect.addEventListener('change', () => {
+ this.switchLive2DModel();
+ });
+ }
+
+ // Camera switch button
+ const cameraSwitch = document.getElementById('cameraSwitch');
+ const cameraSwitchMask = document.getElementById('cameraSwitchMask');
+ if (cameraSwitchMask) {
+ cameraSwitchMask.addEventListener('click', () => {
+ const isCameraActive = cameraSwitch.classList.contains('active');
+ if (isCameraActive) {
+ window.switchCamera();
+ }
+ })
+ }
+
+ // Dial button
+ const dialBtn = document.getElementById('dialBtn');
+ if (dialBtn) {
+ dialBtn.addEventListener('click', () => {
+ dialBtn.disabled = true;
+ this.dialBtnDisabled = true;
+ setTimeout(() => {
+ dialBtn.disabled = false;
+ this.dialBtnDisabled = false;
+ }, 3000);
+
+ const wsHandler = getWebSocketHandler();
+ const isConnected = wsHandler.isConnected();
+
+ if (isConnected) {
+ wsHandler.disconnect();
+ this.updateDialButton(false);
+ if (cameraSwitch) cameraSwitch.classList.remove('active');
+ this.addChatMessage('Disconnected, see you next time~馃槉', false);
+ } else {
+ // Check if OTA URL is filled
+ const otaUrlInput = document.getElementById('otaUrl');
+ if (!otaUrlInput || !otaUrlInput.value.trim()) {
+ // If OTA URL is not filled, show settings modal and switch to device tab
+ this.showModal('settingsModal');
+ this.switchTab('device');
+ this.addChatMessage('Please fill in OTA server URL', false);
+ return;
+ }
+
+ // Start connection process
+ this.handleConnect();
+ }
+ });
+ }
+
+ // Camera button
+ const cameraBtn = document.getElementById('cameraBtn');
+ let cameraTimer = null;
+ if (cameraBtn) {
+ cameraBtn.addEventListener('click', () => {
+ if (cameraTimer) {
+ clearTimeout(cameraTimer);
+ cameraTimer = null;
+ }
+ cameraTimer = setTimeout(() => {
+ const cameraContainer = document.getElementById('cameraContainer');
+ if (!cameraContainer) {
+ log('鎽勫儚澶村鍣ㄤ笉瀛樺湪', 'warning');
+ return;
+ }
+
+ const isActive = cameraContainer.classList.contains('active');
+ if (isActive) {
+ // 鍏抽棴鎽勫儚澶�
+ if (typeof window.stopCamera === 'function') {
+ if (cameraSwitch) cameraSwitch.classList.remove('active');
+ window.stopCamera();
+ }
+ cameraContainer.classList.remove('active');
+ cameraBtn.classList.remove('camera-active');
+ cameraBtn.querySelector('.btn-text').textContent = '鎽勫儚澶�';
+ log('鎽勫儚澶村凡鍏抽棴', 'info');
+ } else {
+ // 鎵撳紑鎽勫儚澶�
+ if (typeof window.startCamera === 'function') {
+ window.startCamera().then(success => {
+ if (success) {
+ cameraBtn.classList.add('camera-active');
+ cameraBtn.querySelector('.btn-text').textContent = '鍏抽棴';
+ } else {
+ this.addChatMessage('鈿狅笍 鎽勫儚澶村惎鍔ㄥけ璐ワ紝璇锋鏌ユ祻瑙堝櫒鏉冮檺', false);
+ }
+ }).catch(error => {
+ log(`鍚姩鎽勫儚澶村紓甯�: ${error.message}`, 'error');
+ });
+ } else {
+ log('startCamera鍑芥暟鏈畾涔�', 'warning');
+ }
+ }
+ }, 300);
+ });
+ }
+
+ // Record button
+ const recordBtn = document.getElementById('recordBtn');
+ if (recordBtn) {
+ let recordTimer = null;
+ recordBtn.addEventListener('click', () => {
+ if (recordTimer) {
+ clearTimeout(recordTimer);
+ recordTimer = null;
+ }
+ recordTimer = setTimeout(() => {
+ const audioRecorder = getAudioRecorder();
+ if (audioRecorder.isRecording) {
+ audioRecorder.stop();
+ // Restore record button to normal state
+ recordBtn.classList.remove('recording');
+ recordBtn.querySelector('.btn-text').textContent = '褰曢煶';
+ } else {
+ // Update button state to recording
+ recordBtn.classList.add('recording');
+ recordBtn.querySelector('.btn-text').textContent = '褰曢煶涓�';
+
+ // Start recording, update button state after delay
+ setTimeout(() => {
+ audioRecorder.start();
+ }, 100);
+ }
+ }, 300);
+ });
+ }
+
+ // Chat input event listener
+ const chatIpt = document.getElementById('chatIpt');
+ if (chatIpt) {
+ const wsHandler = getWebSocketHandler();
+ chatIpt.addEventListener('keydown', (e) => {
+ if (e.key === 'Enter') {
+ if (e.target.value) {
+ wsHandler.sendTextMessage(e.target.value);
+ e.target.value = '';
+ return;
+ }
+ }
+ });
+ }
+
+ // Close button
+ const closeButtons = document.querySelectorAll('.close-btn');
+ closeButtons.forEach(btn => {
+ btn.addEventListener('click', (e) => {
+ e.stopPropagation();
+ const modal = e.target.closest('.modal');
+ if (modal) {
+ if (modal.id === 'settingsModal') {
+ saveConfig();
+ }
+ this.hideModal(modal.id);
+ }
+ });
+ });
+
+ // Settings tab switch
+ const tabBtns = document.querySelectorAll('.tab-btn');
+ tabBtns.forEach(btn => {
+ btn.addEventListener('click', (e) => {
+ this.switchTab(e.target.dataset.tab);
+ });
+ });
+
+ const applyWakewordBtn = document.getElementById('applyWakewordBtn');
+ if (applyWakewordBtn) {
+ applyWakewordBtn.addEventListener('click', this.handleApplyWakeword);
+ }
+
+ // 鐐瑰嚮妯℃�佹鑳屾櫙鍏抽棴锛堜粎瀵圭壒瀹氭ā鎬佹绂佺敤姝ゅ姛鑳斤級
+ const modals = document.querySelectorAll('.modal');
+ modals.forEach(modal => {
+ modal.addEventListener('click', (e) => {
+ if (e.target === modal) {
+ // settingsModal銆乵cpToolModal銆乵cpPropertyModal 鍙兘閫氳繃鐐瑰嚮X鍏抽棴
+ const nonClosableModals = ['settingsModal', 'mcpToolModal', 'mcpPropertyModal'];
+ if (nonClosableModals.includes(modal.id)) {
+ return; // 绂佹鐐瑰嚮鑳屾櫙鍏抽棴
+ }
+ this.hideModal(modal.id);
+ }
+ });
+ });
+
+ // Add MCP tool button
+ const addMCPToolBtn = document.getElementById('addMCPToolBtn');
+ if (addMCPToolBtn) {
+ addMCPToolBtn.addEventListener('click', (e) => {
+ e.stopPropagation();
+ this.addMCPTool();
+ });
+ }
+
+ // Connect button and send button are not removed, can be added to dial button later
+ }
+
+ // Update connection status UI
+ updateConnectionUI(isConnected) {
+ const connectionStatus = document.getElementById('connectionStatus');
+ const statusDot = document.querySelector('.status-dot');
+
+ if (connectionStatus) {
+ if (isConnected) {
+ connectionStatus.textContent = '宸茶繛鎺�';
+ if (statusDot) {
+ statusDot.className = 'status-dot status-connected';
+ }
+ } else {
+ connectionStatus.textContent = '绂荤嚎';
+ if (statusDot) {
+ statusDot.className = 'status-dot status-disconnected';
+ }
+ }
+ }
+ }
+
+ // Update dial button state
+ updateDialButton(isConnected) {
+ const dialBtn = document.getElementById('dialBtn');
+ const recordBtn = document.getElementById('recordBtn');
+ const cameraBtn = document.getElementById('cameraBtn');
+
+ if (dialBtn) {
+ if (isConnected) {
+ dialBtn.classList.add('dial-active');
+ dialBtn.querySelector('.btn-text').textContent = '鎸傛柇';
+ // Update dial button icon to hang up icon
+ dialBtn.querySelector('svg').innerHTML = `
+ <path d="M12,9C10.4,9 9,10.4 9,12C9,13.6 10.4,15 12,15C13.6,15 15,13.6 15,12C15,10.4 13.6,9 12,9M12,17C9.2,17 7,14.8 7,12C7,9.2 9.2,7 12,7C14.8,7 17,9.2 17,12C17,14.8 14.8,17 12,17M12,4.5C7,4.5 2.7,7.6 1,12C2.7,16.4 7,19.5 12,19.5C17,19.5 21.3,16.4 23,12C21.3,7.6 17,4.5 12,4.5Z"/>
+ `;
+ } else {
+ dialBtn.classList.remove('dial-active');
+ dialBtn.querySelector('.btn-text').textContent = '鎷ㄥ彿';
+ // Restore dial button icon
+ dialBtn.querySelector('svg').innerHTML = `
+ <path d="M6.62,10.79C8.06,13.62 10.38,15.94 13.21,17.38L15.41,15.18C15.69,14.9 16.08,14.82 16.43,14.93C17.55,15.3 18.75,15.5 20,15.5A1,1 0 0,1 21,16.5V20A1,1 0 0,1 20,21A17,17 0 0,1 3,4A1,1 0 0,1 4,3H7.5A1,1 0 0,1 8.5,4C8.5,5.25 8.7,6.45 9.07,7.57C9.18,7.92 9.1,8.31 8.82,8.59L6.62,10.79Z"/>
+ `;
+ }
+ }
+
+ // Update camera button state - reset to default when disconnected
+ if (cameraBtn && !isConnected) {
+ const cameraContainer = document.getElementById('cameraContainer');
+ if (cameraContainer && cameraContainer.classList.contains('active')) {
+ cameraContainer.classList.remove('active');
+ }
+ cameraBtn.classList.remove('camera-active');
+ cameraBtn.querySelector('.btn-text').textContent = '鎽勫儚澶�';
+ cameraBtn.disabled = true;
+ cameraBtn.title = '璇峰厛杩炴帴鏈嶅姟鍣�';
+ // 鍏抽棴鎽勫儚澶�
+ if (typeof window.stopCamera === 'function') {
+ window.stopCamera();
+ }
+ }
+
+ // Update camera button state - enable when connected and camera is available
+ if (cameraBtn && isConnected) {
+ if (window.cameraAvailable) {
+ cameraBtn.disabled = false;
+ cameraBtn.title = '鎵撳紑/鍏抽棴鎽勫儚澶�';
+ } else {
+ cameraBtn.disabled = true;
+ cameraBtn.title = '璇峰厛缁戝畾楠岃瘉鐮�';
+ }
+ }
+
+ // Update record button state
+ if (recordBtn) {
+ const microphoneAvailable = window.microphoneAvailable !== false;
+ if (isConnected && microphoneAvailable) {
+ recordBtn.disabled = false;
+ recordBtn.title = '寮�濮嬪綍闊�';
+ // Restore record button to normal state
+ recordBtn.querySelector('.btn-text').textContent = '褰曢煶';
+ recordBtn.classList.remove('recording');
+ } else {
+ recordBtn.disabled = true;
+ if (!microphoneAvailable) {
+ recordBtn.title = window.isHttpNonLocalhost ? '褰撳墠鐢变簬鏄痟ttp璁块棶锛屾棤娉曞綍闊筹紝鍙兘鐢ㄦ枃瀛椾氦浜�' : '楹﹀厠椋庝笉鍙敤';
+ } else {
+ recordBtn.title = '璇峰厛杩炴帴鏈嶅姟鍣�';
+ }
+ // Restore record button to normal state
+ recordBtn.querySelector('.btn-text').textContent = '褰曢煶';
+ recordBtn.classList.remove('recording');
+ }
+ }
+ }
+
+ // Update record button state
+ updateRecordButtonState(isRecording, seconds = 0) {
+ const recordBtn = document.getElementById('recordBtn');
+ if (recordBtn) {
+ if (isRecording) {
+ recordBtn.querySelector('.btn-text').textContent = `褰曢煶涓璥;
+ recordBtn.classList.add('recording');
+ } else {
+ recordBtn.querySelector('.btn-text').textContent = '褰曢煶';
+ recordBtn.classList.remove('recording');
+ }
+ // Only enable button when microphone is available
+ recordBtn.disabled = window.microphoneAvailable === false;
+ }
+ }
+
+ /**
+ * Update microphone availability state
+ * @param {boolean} isAvailable - Whether microphone is available
+ * @param {boolean} isHttpNonLocalhost - Whether it is HTTP non-localhost access
+ */
+ updateMicrophoneAvailability(isAvailable, isHttpNonLocalhost) {
+ const recordBtn = document.getElementById('recordBtn');
+ if (!recordBtn) return;
+ if (!isAvailable) {
+ // Disable record button
+ recordBtn.disabled = true;
+ // Update button text and title
+ recordBtn.querySelector('.btn-text').textContent = '褰曢煶';
+ recordBtn.title = isHttpNonLocalhost ? '褰撳墠鐢变簬鏄痟ttp璁块棶锛屾棤娉曞綍闊筹紝鍙兘鐢ㄦ枃瀛椾氦浜�' : '楹﹀厠椋庝笉鍙敤';
+
+ } else {
+ // If connected, enable record button
+ const wsHandler = getWebSocketHandler();
+ if (wsHandler && wsHandler.isConnected()) {
+ recordBtn.disabled = false;
+ recordBtn.title = '寮�濮嬪綍闊�';
+ }
+ }
+ }
+
+ // Add chat message
+ addChatMessage(content, isUser = false) {
+ const chatStream = document.getElementById('chatStream');
+ if (!chatStream) return;
+
+ const messageDiv = document.createElement('div');
+ messageDiv.className = `chat-message ${isUser ? 'user' : 'ai'}`;
+ messageDiv.innerHTML = `<div class="message-bubble">${content}</div>`;
+ chatStream.appendChild(messageDiv);
+
+ // Scroll to bottom
+ chatStream.scrollTop = chatStream.scrollHeight;
+ }
+
+ // Switch background
+ switchBackground() {
+ this.currentBackgroundIndex = (this.currentBackgroundIndex + 1) % this.backgroundImages.length;
+ const backgroundContainer = document.querySelector('.background-container');
+ if (backgroundContainer) {
+ backgroundContainer.style.backgroundImage = `url('./images/${this.backgroundImages[this.currentBackgroundIndex]}')`;
+ }
+ localStorage.setItem('backgroundIndex', this.currentBackgroundIndex);
+ }
+
+ // Switch Live2D model
+ switchLive2DModel() {
+ const modelSelect = document.getElementById('live2dModelSelect');
+ if (!modelSelect) {
+ console.error('妯″瀷閫夋嫨涓嬫媺妗嗕笉瀛樺湪');
+ return;
+ }
+
+ const selectedModel = modelSelect.value;
+ const app = window.chatApp;
+
+ if (app && app.live2dManager) {
+ app.live2dManager.switchModel(selectedModel)
+ .then(success => {
+ if (success) {
+ this.addChatMessage(`宸插垏鎹㈠埌妯″瀷: ${selectedModel}`, false);
+ } else {
+ this.addChatMessage('妯″瀷鍒囨崲澶辫触', false);
+ }
+ })
+ .catch(error => {
+ console.error('妯″瀷鍒囨崲閿欒:', error);
+ this.addChatMessage('妯″瀷鍒囨崲鍑洪敊', false);
+ });
+ } else {
+ this.addChatMessage('Live2D绠$悊鍣ㄦ湭鍒濆鍖�', false);
+ }
+ }
+
+ // Show modal
+ showModal(modalId) {
+ const modal = document.getElementById(modalId);
+ if (modal) {
+ modal.style.display = 'flex';
+ }
+ }
+
+ // Hide modal
+ hideModal(modalId) {
+ const modal = document.getElementById(modalId);
+ if (modal) {
+ modal.style.display = 'none';
+ }
+ }
+
+ // Switch tab
+ switchTab(tabName) {
+ // Remove active class from all tabs
+ const tabBtns = document.querySelectorAll('.tab-btn');
+ const tabContents = document.querySelectorAll('.tab-content');
+
+ tabBtns.forEach(btn => btn.classList.remove('active'));
+ tabContents.forEach(content => content.classList.remove('active'));
+
+ // Activate selected tab
+ const activeTabBtn = document.querySelector(`[data-tab="${tabName}"]`);
+ const activeTabContent = document.getElementById(`${tabName}Tab`);
+
+ if (activeTabBtn && activeTabContent) {
+ activeTabBtn.classList.add('active');
+ activeTabContent.classList.add('active');
+ }
+ }
+
+ applyWakewordConfig(config = {}) {
+ const wakewordEnabledInput = document.getElementById('wakewordEnabled');
+ const wakewordListInput = document.getElementById('wakewordList');
+
+ if (!wakewordEnabledInput || !wakewordListInput) {
+ return;
+ }
+
+ const wakeWords = Array.isArray(config.wakeWords)
+ ? config.wakeWords.filter(item => typeof item === 'string' && item.trim())
+ : [];
+
+ wakewordEnabledInput.value = config.enabled === false ? 'false' : 'true';
+ wakewordListInput.value = wakeWords.join('\n');
+ saveConfig();
+ }
+
+ async handleApplyWakeword() {
+ const wakewordEnabledInput = document.getElementById('wakewordEnabled');
+ const wakewordListInput = document.getElementById('wakewordList');
+ const wakewordWsUrlInput = document.getElementById('wakewordWsUrl');
+ if (!wakewordEnabledInput || !wakewordListInput) {
+ return;
+ }
+
+ const wakeWords = wakewordListInput.value
+ .split(/\r?\n/u)
+ .map(item => item.trim())
+ .filter(Boolean)
+ .filter((item, index, items) => items.indexOf(item) === index);
+
+ const payload = {
+ enabled: wakewordEnabledInput.value !== 'false',
+ wakeWords,
+ };
+
+ if (payload.enabled && payload.wakeWords.length === 0) {
+ this.addChatMessage('鍚敤鍞ら啋璇嶆椂锛岃嚦灏戦渶瑕佸~鍐欎竴涓敜閱掕瘝銆�', false);
+ return;
+ }
+
+ const applyWakewordBtn = document.getElementById('applyWakewordBtn');
+ if (applyWakewordBtn) {
+ applyWakewordBtn.disabled = true;
+ applyWakewordBtn.textContent = '搴旂敤涓�...';
+ }
+
+ try {
+ // 淇濆瓨鍦板潃鍒� localStorage
+ if (wakewordWsUrlInput && wakewordWsUrlInput.value.trim()) {
+ localStorage.setItem('xz_tester_wakewordWsUrl', wakewordWsUrlInput.value.trim());
+ }
+
+ // 姣旇緝鏂板湴鍧�鍜屽綋鍓嶈繛鎺ュ湴鍧�
+ const newWsUrl = localStorage.getItem('xz_tester_wakewordWsUrl');
+ const currentWsUrl = getWakewordBridgeUrl();
+ const urlChanged = newWsUrl !== currentWsUrl;
+
+ if (urlChanged) {
+ // 鍦板潃鍙樹簡锛屽厛纭
+ const shouldRestart = window.confirm('鍦板潃宸插彉鏇达紝鏄惁缁х画锛燂紙灏嗘柇寮�鏃ц繛鎺ュ苟閲嶆柊杩炴帴锛�');
+ if (!shouldRestart) {
+ // 鎭㈠ localStorage 閲岀殑鏃у湴鍧�
+ localStorage.setItem('xz_tester_wakewordWsUrl', currentWsUrl);
+ this.addChatMessage('宸插彇娑堝湴鍧�鍙樻洿銆�', false);
+ return;
+ }
+
+ // 鏂紑鏃ц繛鎺�
+ stopWakewordBridgeListener();
+
+ // 鍚姩鏂拌繛鎺ワ紙鑷姩鐢ㄦ柊鍦板潃锛�
+ startWakewordBridgeListener();
+
+ // 绛� bridge_connected
+ await new Promise((resolve, reject) => {
+ const timeout = setTimeout(() => {
+ reject(new Error('杩炴帴鏂版湇鍔″櫒瓒呮椂'));
+ }, 5000);
+
+ onNextBridgeConnected(() => {
+ clearTimeout(timeout);
+ resolve();
+ });
+ });
+
+ // 鍙戦厤缃拰閲嶅惎
+ await requestWakewordBridge('set_wakeword_config', payload);
+ this.applyWakewordConfig(payload);
+ await requestWakewordBridge('restart_wakeword_service');
+ this.addChatMessage('鍞ら啋璇嶉厤缃凡淇濆瓨锛屽敜閱掕瘝鏈嶅姟姝e湪閲嶅惎銆�', false);
+ } else {
+ // 鍦板潃娌″彉锛氱洿鎺ュ湪褰撳墠杩炴帴鎿嶄綔
+ const response = await requestWakewordBridge('set_wakeword_config', payload);
+ this.applyWakewordConfig(response.payload || payload);
+
+ const shouldRestart = window.confirm('鍞ら啋璇嶅凡淇濆瓨銆傛槸鍚︾幇鍦ㄩ噸鍚敜閱掕瘝鏈嶅姟浠ョ珛鍗崇敓鏁堬紵');
+ if (!shouldRestart) {
+ this.addChatMessage('鍞ら啋璇嶉厤缃凡淇濆瓨锛屽彲绋嶅悗鎵嬪姩閲嶅惎鏈嶅姟鍚庣敓鏁堛��', false);
+ return;
+ }
+
+ await requestWakewordBridge('restart_wakeword_service');
+ this.addChatMessage('鍞ら啋璇嶉厤缃凡淇濆瓨锛屽敜閱掕瘝鏈嶅姟姝e湪閲嶅惎銆�', false);
+ }
+ } catch (error) {
+ this.addChatMessage(`搴旂敤鍞ら啋璇嶅け璐�: ${error.message}`, false);
+ } finally {
+ if (applyWakewordBtn) {
+ applyWakewordBtn.disabled = false;
+ applyWakewordBtn.textContent = '搴旂敤鍞ら啋璇�';
+ }
+ }
+ }
+
+ // Start AI chat session after connection
+ startAIChatSession() {
+ this.addChatMessage('杩炴帴鎴愬姛锛屽紑濮嬭亰澶╁惂~馃槉', false);
+ // Check microphone availability and show error messages if needed
+ if (!window.microphoneAvailable) {
+ if (window.isHttpNonLocalhost) {
+ this.addChatMessage('鈿狅笍 褰撳墠鐢变簬鏄痟ttp璁块棶锛屾棤娉曞綍闊筹紝鍙兘鐢ㄦ枃瀛椾氦浜�', false);
+ } else {
+ this.addChatMessage('鈿狅笍 楹﹀厠椋庝笉鍙敤锛岃妫�鏌ユ潈闄愯缃紝鍙兘鐢ㄦ枃瀛椾氦浜�', false);
+ }
+ }
+ // Start recording only if microphone is available
+ if (window.microphoneAvailable) {
+ const recordBtn = document.getElementById('recordBtn');
+ if (recordBtn) {
+ recordBtn.click();
+ }
+ }
+ // Start camera only if camera is available (bound with verification code)
+ if (window.cameraAvailable && typeof window.startCamera === 'function') {
+ window.startCamera().then(success => {
+ if (success) {
+ const cameraBtn = document.getElementById('cameraBtn');
+ if (cameraBtn) {
+ cameraBtn.classList.add('camera-active');
+ cameraBtn.querySelector('.btn-text').textContent = '鍏抽棴';
+ }
+ } else {
+ this.addChatMessage('鈿狅笍 鎽勫儚澶村惎鍔ㄥけ璐ワ紝鍙兘琚祻瑙堝櫒鎷掔粷', false);
+ }
+ }).catch(error => {
+ log(`鍚姩鎽勫儚澶村紓甯�: ${error.message}`, 'error');
+ });
+ }
+ }
+
+ // Handle connect button click
+ async handleConnect() {
+ const wsHandler = getWebSocketHandler();
+ if (this.isConnecting || (wsHandler && wsHandler.isConnected())) {
+ log('杩炴帴宸插瓨鍦ㄦ垨姝e湪杩涜锛屽拷鐣ユ湰娆℃嫧鍙疯姹�', 'info');
+ return;
+ }
+
+ this.isConnecting = true;
+ console.log('handleConnect called');
+
+ try {
+ // Switch to device settings tab
+ this.switchTab('device');
+
+ // Wait for DOM update
+ await new Promise(resolve => setTimeout(resolve, 50));
+
+ const otaUrlInput = document.getElementById('otaUrl');
+
+ console.log('otaUrl element:', otaUrlInput);
+
+ if (!otaUrlInput || !otaUrlInput.value) {
+ this.addChatMessage('璇疯緭鍏TA鏈嶅姟鍣ㄥ湴鍧�', false);
+ return;
+ }
+
+ const otaUrl = otaUrlInput.value;
+ console.log('otaUrl value:', otaUrl);
+
+ // Update dial button state to connecting
+ const dialBtn = document.getElementById('dialBtn');
+ if (dialBtn) {
+ dialBtn.classList.add('dial-active');
+ dialBtn.querySelector('.btn-text').textContent = '杩炴帴涓�...';
+ dialBtn.disabled = true;
+ }
+
+ // Show connecting message
+ this.addChatMessage('姝e湪杩炴帴鏈嶅姟鍣�...', false);
+
+ const chatIpt = document.getElementById('chatIpt');
+ if (chatIpt) {
+ chatIpt.style.display = 'flex';
+ }
+
+ // Get WebSocket handler instance
+ // Register connection state callback BEFORE connecting
+ wsHandler.onConnectionStateChange = (isConnected) => {
+ this.updateConnectionUI(isConnected);
+ this.updateDialButton(isConnected);
+ };
+
+ // Register chat message callback BEFORE connecting
+ wsHandler.onChatMessage = (text, isUser) => {
+ this.addChatMessage(text, isUser);
+ };
+
+ // Register record button state callback BEFORE connecting
+ wsHandler.onRecordButtonStateChange = (isRecording) => {
+ const recordBtn = document.getElementById('recordBtn');
+ if (recordBtn) {
+ if (isRecording) {
+ recordBtn.classList.add('recording');
+ recordBtn.querySelector('.btn-text').textContent = '褰曢煶涓�';
+ } else {
+ recordBtn.classList.remove('recording');
+ recordBtn.querySelector('.btn-text').textContent = '褰曢煶';
+ }
+ }
+ };
+
+ const isConnected = await wsHandler.connect();
+
+ if (isConnected) {
+ // Check microphone availability (check again after connection)
+ const { checkMicrophoneAvailability } = await import('../core/audio/recorder.js?v=0205');
+ const micAvailable = await checkMicrophoneAvailability();
+
+ if (!micAvailable) {
+ const isHttp = window.isHttpNonLocalhost;
+ if (isHttp) {
+ this.addChatMessage('鈿狅笍 褰撳墠鐢变簬鏄痟ttp璁块棶锛屾棤娉曞綍闊筹紝鍙兘鐢ㄦ枃瀛椾氦浜�', false);
+ }
+ // Update global state
+ window.microphoneAvailable = false;
+ }
+
+ // Update dial button state
+ const dialBtn = document.getElementById('dialBtn');
+ if (dialBtn) {
+ if (!this.dialBtnDisabled) {
+ dialBtn.disabled = false;
+ }
+ dialBtn.querySelector('.btn-text').textContent = '鎸傛柇';
+ dialBtn.classList.add('dial-active');
+ }
+
+ this.hideModal('settingsModal');
+ } else {
+ throw new Error('OTA杩炴帴澶辫触');
+ }
+ } catch (error) {
+ console.error('Connection error details:', {
+ message: error.message,
+ stack: error.stack,
+ name: error.name
+ });
+
+ // Show error message
+ const errorMessage = error.message.includes('Cannot set properties of null')
+ ? '杩炴帴澶辫触锛氳妫�鏌ヨ澶囪繛鎺�'
+ : `杩炴帴澶辫触: ${error.message}`;
+
+ this.addChatMessage(errorMessage, false);
+
+ // Restore dial button state
+ const dialBtn = document.getElementById('dialBtn');
+ if (dialBtn) {
+ if (!this.dialBtnDisabled) {
+ dialBtn.disabled = false;
+ }
+ dialBtn.querySelector('.btn-text').textContent = '鎷ㄥ彿';
+ dialBtn.classList.remove('dial-active');
+ console.log('Dial button state restored successfully');
+ }
+ } finally {
+ this.isConnecting = false;
+ }
+ }
+
+ async triggerWakewordDial(wakeWord = '鍞ら啋璇�') {
+ const wsHandler = getWebSocketHandler();
+ const now = Date.now();
+
+ if (wsHandler && wsHandler.isConnected()) {
+ log('椤甸潰宸茶繛鎺ワ紝蹇界暐鑷姩鎷ㄥ彿', 'info');
+ return false;
+ }
+
+ if (this.isConnecting || this.dialBtnDisabled) {
+ log('椤甸潰姝e湪杩炴帴涓紝蹇界暐閲嶅鍞ら啋', 'info');
+ return false;
+ }
+
+ if (now - this.lastWakewordDialTime < 3000) {
+ log('鍞ら啋瑙﹀彂杩囦簬棰戠箒锛屽拷鐣ユ湰娆¤嚜鍔ㄦ嫧鍙�', 'warning');
+ return false;
+ }
+
+ this.lastWakewordDialTime = now;
+ this.addChatMessage(`妫�娴嬪埌鍞ら啋璇嶁��${wakeWord}鈥濓紝鍑嗗杩炴帴鏈嶅姟鍣�...`, false);
+ await this.handleConnect();
+ return true;
+ }
+
+ // Add MCP tool
+ addMCPTool() {
+ const mcpToolsList = document.getElementById('mcpToolsList');
+ if (!mcpToolsList) return;
+
+ const toolId = `mcp-tool-${Date.now()}`;
+ const toolDiv = document.createElement('div');
+ toolDiv.className = 'properties-container';
+ toolDiv.innerHTML = `
+ <div class="property-item">
+ <input type="text" placeholder="宸ュ叿鍚嶇О" value="鏂板伐鍏�">
+ <input type="text" placeholder="宸ュ叿鎻忚堪" value="宸ュ叿鎻忚堪">
+ <button class="remove-property" onclick="uiController.removeMCPTool('${toolId}')">鍒犻櫎</button>
+ </div>
+ `;
+
+ mcpToolsList.appendChild(toolDiv);
+ }
+
+ // Remove MCP tool
+ removeMCPTool(toolId) {
+ const toolElement = document.getElementById(toolId);
+ if (toolElement) {
+ toolElement.remove();
+ }
+ }
+
+ // Update audio statistics display
+ updateAudioStats() {
+ const audioPlayer = getAudioPlayer();
+ if (!audioPlayer) return;
+
+ const stats = audioPlayer.getAudioStats();
+ // Here can add audio statistics UI update logic
+ }
+
+ // Start audio statistics monitor
+ startAudioStatsMonitor() {
+ // Update audio statistics every 100ms
+ this.audioStatsTimer = setInterval(() => {
+ this.updateAudioStats();
+ }, 100);
+ }
+
+ // Stop audio statistics monitor
+ stopAudioStatsMonitor() {
+ if (this.audioStatsTimer) {
+ clearInterval(this.audioStatsTimer);
+ this.audioStatsTimer = null;
+ }
+ }
+
+ // Draw audio visualizer waveform
+ drawVisualizer(dataArray) {
+ if (!this.visualizerContext || !this.visualizerCanvas) return;
+
+ this.visualizerContext.fillStyle = '#fafafa';
+ this.visualizerContext.fillRect(0, 0, this.visualizerCanvas.width, this.visualizerCanvas.height);
+
+ const barWidth = (this.visualizerCanvas.width / dataArray.length) * 2.5;
+ let barHeight;
+ let x = 0;
+
+ for (let i = 0; i < dataArray.length; i++) {
+ barHeight = dataArray[i] / 2;
+
+ // Create gradient color: from purple to blue to green
+ const gradient = this.visualizerContext.createLinearGradient(0, 0, 0, this.visualizerCanvas.height);
+ gradient.addColorStop(0, '#8e44ad');
+ gradient.addColorStop(0.5, '#3498db');
+ gradient.addColorStop(1, '#1abc9c');
+
+ this.visualizerContext.fillStyle = gradient;
+ this.visualizerContext.fillRect(x, this.visualizerCanvas.height - barHeight, barWidth, barHeight);
+ x += barWidth + 1;
+ }
+ }
+
+ // Update session status UI
+ updateSessionStatus(isSpeaking) {
+ // Here can add session status UI update logic
+ // For example: update Live2D model's mouth movement status
+ }
+
+ // Update session emotion
+ updateSessionEmotion(emoji) {
+ // Here can add emotion update logic
+ // For example: display emoji in status indicator
+ }
+}
+
+// Create singleton instance
+export const uiController = new UIController();
+
+// Export class for module usage
+export { UIController };
diff --git a/xiaozhi-esp32-server/main/digital-human/js/utils/blocking-queue.js b/xiaozhi-esp32-server/main/digital-human/js/utils/blocking-queue.js
new file mode 100644
index 0000000..738a3e7
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/js/utils/blocking-queue.js
@@ -0,0 +1,103 @@
+export default class BlockingQueue {
+ #items = [];
+ #waiters = []; // {resolve, reject, min, timer, onTimeout}
+
+ /* 绌洪槦鍒椾竴娆℃�ч椄闂� */
+ #emptyPromise = null;
+ #emptyResolve = null;
+
+ /* 鐢熶骇鑰咃細鎶婃暟鎹杩涘幓 */
+ enqueue(item, ...restItems) {
+ if (restItems.length === 0) {
+ this.#items.push(item);
+ }
+ // 濡傛灉鏈夐澶栧弬鏁帮紝鎵归噺澶勭悊鎵�鏈夐」
+ else {
+ const items = [item, ...restItems].filter(i => i);
+ if (items.length === 0) return;
+ this.#items.push(...items);
+ }
+ // 鑻ユ湁绌洪槦鍒楅椄闂紝涓�娆℃�ф斁琛屾墍鏈夌瓑寰呰��
+ if (this.#emptyResolve) {
+ this.#emptyResolve();
+ this.#emptyResolve = null;
+ this.#emptyPromise = null;
+ }
+
+ // 鍞ら啋鎵�鏈夋鍦ㄧ瓑鐨� waiter
+ this.#wakeWaiters();
+ }
+
+ /* 娑堣垂鑰咃細min 鏉℃垨 timeout ms 鍏堝埌璋� */
+ async dequeue(min = 1, timeout = Infinity, onTimeout = null) {
+ // 1. 鑻ョ┖锛岀瓑绗竴娆℃暟鎹埌杈撅紙鎵�鏈夎皟鐢ㄥ叡浜悓涓�涓� promise锛�
+ if (this.#items.length === 0) {
+ await this.#waitForFirstItem();
+ }
+
+ // 绔嬪嵆婊¤冻
+ if (this.#items.length >= min) {
+ return this.#flush();
+ }
+
+ // 闇�瑕佺瓑寰�
+ return new Promise((resolve, reject) => {
+ let timer = null;
+ const waiter = { resolve, reject, min, onTimeout, timer };
+
+ // 瓒呮椂閫昏緫
+ if (Number.isFinite(timeout)) {
+ waiter.timer = setTimeout(() => {
+ this.#removeWaiter(waiter);
+ if (onTimeout) onTimeout(this.#items.length);
+ resolve(this.#flush());
+ }, timeout);
+ }
+
+ this.#waiters.push(waiter);
+ });
+ }
+
+ /* 绌洪槦鍒楅椄闂ㄧ敓鎴愬櫒 */
+ #waitForFirstItem() {
+ if (!this.#emptyPromise) {
+ this.#emptyPromise = new Promise(r => (this.#emptyResolve = r));
+ }
+ return this.#emptyPromise;
+ }
+
+ /* 鍐呴儴锛氭瘡娆℃暟鎹彉鍔ㄥ悗锛屾鏌ュ摢浜� waiter 宸叉弧瓒� */
+ #wakeWaiters() {
+ for (let i = this.#waiters.length - 1; i >= 0; i--) {
+ const w = this.#waiters[i];
+ if (this.#items.length >= w.min) {
+ this.#removeWaiter(w);
+ w.resolve(this.#flush());
+ }
+ }
+ }
+
+ #removeWaiter(waiter) {
+ const idx = this.#waiters.indexOf(waiter);
+ if (idx !== -1) {
+ this.#waiters.splice(idx, 1);
+ if (waiter.timer) clearTimeout(waiter.timer);
+ }
+ }
+
+ #flush() {
+ const snapshot = [...this.#items];
+ this.#items.length = 0;
+ return snapshot;
+ }
+
+ /* 褰撳墠缂撳瓨闀垮害锛堜笉鍚瓑寰呰�咃級 */
+ get length() {
+ return this.#items.length;
+ }
+
+ /* 娓呯┖闃熷垪锛堜繚鎸佸璞″紩鐢紝涓嶅奖鍝嶇瓑寰呰�咃級 */
+ clear() {
+ this.#items.length = 0;
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/js/utils/libopus.js b/xiaozhi-esp32-server/main/digital-human/js/utils/libopus.js
new file mode 100644
index 0000000..bff6b2a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/js/utils/libopus.js
@@ -0,0 +1,266 @@
+var Module = function(Module) {
+ Module = Module || {};
+
+ var b;b||(b=eval("(function() { try { return Module || {} } catch(e) { return {} } })()"));var f={},l;for(l in b)b.hasOwnProperty(l)&&(f[l]=b[l]);var p=!1,q=!1,r=!1,t=!1;
+ if(b.ENVIRONMENT)if("WEB"===b.ENVIRONMENT)p=!0;else if("WORKER"===b.ENVIRONMENT)q=!0;else if("NODE"===b.ENVIRONMENT)r=!0;else if("SHELL"===b.ENVIRONMENT)t=!0;else throw Error("The provided Module['ENVIRONMENT'] value is not valid. It must be one of: WEB|WORKER|NODE|SHELL.");else p="object"===typeof window,q="function"===typeof importScripts,r="object"===typeof process&&"function"===typeof require&&!p&&!q,t=!p&&!r&&!q;
+ if(r){b.print||(b.print=console.log);b.printErr||(b.printErr=console.warn);var u,v;b.read=function(a,c){u||(u=null);v||(v=require("path"));a=v.normalize(a);var d=u.readFileSync(a);d||a==v.resolve(a)||(a=path.join(__dirname,"..","src",a),d=u.readFileSync(a));d&&!c&&(d=d.toString());return d};b.readBinary=function(a){a=b.read(a,!0);a.buffer||(a=new Uint8Array(a));assert(a.buffer);return a};b.load=function(a){aa(read(a))};b.thisProgram||(b.thisProgram=1<process.argv.length?process.argv[1].replace(/\\/g,
+ "/"):"unknown-program");b.arguments=process.argv.slice(2);"undefined"!==typeof module&&(module.exports=b);process.on("uncaughtException",function(a){if(!(a instanceof w))throw a;});b.inspect=function(){return"[Emscripten Module object]"}}else if(t)b.print||(b.print=print),"undefined"!=typeof printErr&&(b.printErr=printErr),b.read="undefined"!=typeof read?read:function(){throw"no read() available (jsc?)";},b.readBinary=function(a){if("function"===typeof readbuffer)return new Uint8Array(readbuffer(a));
+ a=read(a,"binary");assert("object"===typeof a);return a},"undefined"!=typeof scriptArgs?b.arguments=scriptArgs:"undefined"!=typeof arguments&&(b.arguments=arguments),eval("if (typeof gc === 'function' && gc.toString().indexOf('[native code]') > 0) var gc = undefined");else if(p||q)b.read=function(a){var c=new XMLHttpRequest;c.open("GET",a,!1);c.send(null);return c.responseText},b.readAsync=function(a,c,d){var e=new XMLHttpRequest;e.open("GET",a,!0);e.responseType="arraybuffer";e.onload=function(){200==
+ e.status||0==e.status&&e.response?c(e.response):d()};e.onerror=d;e.send(null)},"undefined"!=typeof arguments&&(b.arguments=arguments),"undefined"!==typeof console?(b.print||(b.print=function(a){console.log(a)}),b.printErr||(b.printErr=function(a){console.warn(a)})):b.print||(b.print=function(){}),q&&(b.load=importScripts),"undefined"===typeof b.setWindowTitle&&(b.setWindowTitle=function(a){document.title=a});else throw"Unknown runtime environment. Where are we?";function aa(a){eval.call(null,a)}
+ !b.load&&b.read&&(b.load=function(a){aa(b.read(a))});b.print||(b.print=function(){});b.printErr||(b.printErr=b.print);b.arguments||(b.arguments=[]);b.thisProgram||(b.thisProgram="./this.program");b.print=b.print;b.m=b.printErr;b.preRun=[];b.postRun=[];for(l in f)f.hasOwnProperty(l)&&(b[l]=f[l]);
+ var f=void 0,y={B:function(a){tempRet0=a},w:function(){return tempRet0},g:function(){return x},c:function(a){x=a},q:function(a){switch(a){case "i1":case "i8":return 1;case "i16":return 2;case "i32":return 4;case "i64":return 8;case "float":return 4;case "double":return 8;default:return"*"===a[a.length-1]?y.i:"i"===a[0]?(a=parseInt(a.substr(1)),assert(0===a%8),a/8):0}},v:function(a){return Math.max(y.q(a),y.i)},C:16,Q:function(a,c){"double"===c||"i64"===c?a&7&&(assert(4===(a&7)),a+=4):assert(0===(a&
+ 3));return a},K:function(a,c,d){return d||"i64"!=a&&"double"!=a?a?Math.min(c||(a?y.v(a):0),y.i):Math.min(c,8):8},k:function(a,c,d){return d&&d.length?(d.splice||(d=Array.prototype.slice.call(d)),d.splice(0,0,c),b["dynCall_"+a].apply(null,d)):b["dynCall_"+a].call(null,c)},e:[],r:function(a){for(var c=0;c<y.e.length;c++)if(!y.e[c])return y.e[c]=a,2*(1+c);throw"Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS.";},A:function(a){y.e[(a-2)/2]=null},d:function(a){y.d.n||
+ (y.d.n={});y.d.n[a]||(y.d.n[a]=1,b.m(a))},l:{},M:function(a,c){assert(c);y.l[c]||(y.l[c]={});var d=y.l[c];d[a]||(d[a]=function(){return y.k(c,a,arguments)});return d[a]},L:function(){throw"You must build with -s RETAIN_COMPILER_SETTINGS=1 for Runtime.getCompilerSetting or emscripten_get_compiler_setting to work";},f:function(a){var c=x;x=x+a|0;x=x+15&-16;return c},o:function(a){var c=z;z=z+a|0;z=z+15&-16;return c},b:function(a){var c=E;E=E+a|0;E=E+15&-16;if(a=E>=F)G("Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value "+
+ F+", (2) compile with -s ALLOW_MEMORY_GROWTH=1 which adjusts the size at runtime but prevents some optimizations, (3) set Module.TOTAL_MEMORY to a higher value before the program runs, or if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 "),a=!0;return a?(E=c,0):c},p:function(a,c){return Math.ceil(a/(c?c:16))*(c?c:16)},P:function(a,c,d){return d?+(a>>>0)+4294967296*+(c>>>0):+(a>>>0)+4294967296*+(c|0)},h:8,i:4,D:0};b.Runtime=y;y.addFunction=y.r;
+ y.removeFunction=y.A;var H=!1;function assert(a,c){a||G("Assertion failed: "+c)}function ba(a){var c=b["_"+a];if(!c)try{c=eval("_"+a)}catch(d){}assert(c,"Cannot call unknown function "+a+" (perhaps LLVM optimizations or closure removed it?)");return c}var ca,da;
+ (function(){function a(a){a=a.toString().match(g).slice(1);return{arguments:a[0],body:a[1],returnValue:a[2]}}function c(){if(!k){k={};for(var c in d)d.hasOwnProperty(c)&&(k[c]=a(d[c]))}}var d={stackSave:function(){y.g()},stackRestore:function(){y.c()},arrayToC:function(a){var c=y.f(a.length);ea(a,c);return c},stringToC:function(a){var c=0;null!==a&&void 0!==a&&0!==a&&(c=y.f((a.length<<2)+1),fa(a,c));return c}},e={string:d.stringToC,array:d.arrayToC};da=function(a,c,d,g,k){a=ba(a);var C=[],D=0;if(g)for(var n=
+ 0;n<g.length;n++){var P=e[d[n]];P?(0===D&&(D=y.g()),C[n]=P(g[n])):C[n]=g[n]}d=a.apply(null,C);"string"===c&&(d=I(d));if(0!==D){if(k&&k.async){EmterpreterAsync.F.push(function(){y.c(D)});return}y.c(D)}return d};var g=/^function\s*[a-zA-Z$_0-9]*\s*\(([^)]*)\)\s*{\s*([^*]*?)[\s;]*(?:return\s*(.*?)[;\s]*)?}$/,k=null;ca=function(d,e,g){g=g||[];var B=ba(d);d=g.every(function(a){return"number"===a});var oa="string"!==e;if(oa&&d)return B;var C=g.map(function(a,c){return"$"+c});e="(function("+C.join(",")+
+ ") {";var D=g.length;if(!d){c();e+="var stack = "+k.stackSave.body+";";for(var n=0;n<D;n++){var P=C[n],K=g[n];"number"!==K&&(K=k[K+"ToC"],e+="var "+K.arguments+" = "+P+";",e+=K.body+";",e+=P+"=("+K.returnValue+");")}}g=a(function(){return B}).returnValue;e+="var ret = "+g+"("+C.join(",")+");";oa||(g=a(function(){return I}).returnValue,e+="ret = "+g+"(ret);");d||(c(),e+=k.stackRestore.body.replace("()","(stack)")+";");return eval(e+"return ret})")}})();b.ccall=da;b.cwrap=ca;
+ function ga(a,c,d){d=d||"i8";"*"===d.charAt(d.length-1)&&(d="i32");switch(d){case "i1":J[a>>0]=c;break;case "i8":J[a>>0]=c;break;case "i16":L[a>>1]=c;break;case "i32":M[a>>2]=c;break;case "i64":tempI64=[c>>>0,(tempDouble=c,1<=+ha(tempDouble)?0<tempDouble?(ia(+ja(tempDouble/4294967296),4294967295)|0)>>>0:~~+ka((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)];M[a>>2]=tempI64[0];M[a+4>>2]=tempI64[1];break;case "float":N[a>>2]=c;break;case "double":la[a>>3]=c;break;default:G("invalid type for setValue: "+
+ d)}}b.setValue=ga;function ma(a,c){c=c||"i8";"*"===c.charAt(c.length-1)&&(c="i32");switch(c){case "i1":return J[a>>0];case "i8":return J[a>>0];case "i16":return L[a>>1];case "i32":return M[a>>2];case "i64":return M[a>>2];case "float":return N[a>>2];case "double":return la[a>>3];default:G("invalid type for setValue: "+c)}return null}b.getValue=ma;b.ALLOC_NORMAL=0;b.ALLOC_STACK=1;b.ALLOC_STATIC=2;b.ALLOC_DYNAMIC=3;b.ALLOC_NONE=4;
+ function O(a,c,d,e){var g,k;"number"===typeof a?(g=!0,k=a):(g=!1,k=a.length);var h="string"===typeof c?c:null;d=4==d?e:["function"===typeof Q?Q:y.o,y.f,y.o,y.b][void 0===d?2:d](Math.max(k,h?1:c.length));if(g){e=d;assert(0==(d&3));for(a=d+(k&-4);e<a;e+=4)M[e>>2]=0;for(a=d+k;e<a;)J[e++>>0]=0;return d}if("i8"===h)return a.subarray||a.slice?R.set(a,d):R.set(new Uint8Array(a),d),d;e=0;for(var A,m;e<k;){var B=a[e];"function"===typeof B&&(B=y.N(B));g=h||c[e];0===g?e++:("i64"==g&&(g="i32"),ga(d+e,B,g),m!==
+ g&&(A=y.q(g),m=g),e+=A)}return d}b.allocate=O;b.getMemory=function(a){return na?"undefined"!==typeof S&&!S.a||!T?y.b(a):Q(a):y.o(a)};function I(a,c){if(0===c||!a)return"";for(var d=0,e,g=0;;){e=R[a+g>>0];d|=e;if(0==e&&!c)break;g++;if(c&&g==c)break}c||(c=g);e="";if(128>d){for(;0<c;)d=String.fromCharCode.apply(String,R.subarray(a,a+Math.min(c,1024))),e=e?e+d:d,a+=1024,c-=1024;return e}return b.UTF8ToString(a)}b.Pointer_stringify=I;
+ b.AsciiToString=function(a){for(var c="";;){var d=J[a++>>0];if(!d)return c;c+=String.fromCharCode(d)}};b.stringToAscii=function(a,c){return pa(a,c,!1)};
+ function qa(a,c){for(var d,e,g,k,h,A,m="";;){d=a[c++];if(!d)return m;d&128?(e=a[c++]&63,192==(d&224)?m+=String.fromCharCode((d&31)<<6|e):(g=a[c++]&63,224==(d&240)?d=(d&15)<<12|e<<6|g:(k=a[c++]&63,240==(d&248)?d=(d&7)<<18|e<<12|g<<6|k:(h=a[c++]&63,248==(d&252)?d=(d&3)<<24|e<<18|g<<12|k<<6|h:(A=a[c++]&63,d=(d&1)<<30|e<<24|g<<18|k<<12|h<<6|A))),65536>d?m+=String.fromCharCode(d):(d-=65536,m+=String.fromCharCode(55296|d>>10,56320|d&1023)))):m+=String.fromCharCode(d)}}b.UTF8ArrayToString=qa;
+ b.UTF8ToString=function(a){return qa(R,a)};
+ function ra(a,c,d,e){if(!(0<e))return 0;var g=d;e=d+e-1;for(var k=0;k<a.length;++k){var h=a.charCodeAt(k);55296<=h&&57343>=h&&(h=65536+((h&1023)<<10)|a.charCodeAt(++k)&1023);if(127>=h){if(d>=e)break;c[d++]=h}else{if(2047>=h){if(d+1>=e)break;c[d++]=192|h>>6}else{if(65535>=h){if(d+2>=e)break;c[d++]=224|h>>12}else{if(2097151>=h){if(d+3>=e)break;c[d++]=240|h>>18}else{if(67108863>=h){if(d+4>=e)break;c[d++]=248|h>>24}else{if(d+5>=e)break;c[d++]=252|h>>30;c[d++]=128|h>>24&63}c[d++]=128|h>>18&63}c[d++]=128|
+ h>>12&63}c[d++]=128|h>>6&63}c[d++]=128|h&63}}c[d]=0;return d-g}b.stringToUTF8Array=ra;b.stringToUTF8=function(a,c,d){return ra(a,R,c,d)};function sa(a){for(var c=0,d=0;d<a.length;++d){var e=a.charCodeAt(d);55296<=e&&57343>=e&&(e=65536+((e&1023)<<10)|a.charCodeAt(++d)&1023);127>=e?++c:c=2047>=e?c+2:65535>=e?c+3:2097151>=e?c+4:67108863>=e?c+5:c+6}return c}b.lengthBytesUTF8=sa;
+ function ta(){return ua().replace(/__Z[\w\d_]+/g,function(a){var c;a:{if(b.___cxa_demangle)try{var d=Q(a.length);fa(a.substr(1),d);var e=Q(4),g=b.___cxa_demangle(d,0,0,e);if(0===ma(e,"i32")&&g){c=I(g);break a}}catch(k){c=a;break a}finally{d&&va(d),e&&va(e),g&&va(g)}y.d("warning: build with -s DEMANGLE_SUPPORT=1 to link in libcxxabi demangling");c=a}return a===c?a:a+" ["+c+"]"})}
+ function ua(){var a=Error();if(!a.stack){try{throw Error(0);}catch(c){a=c}if(!a.stack)return"(no stack trace available)"}return a.stack.toString()}b.stackTrace=function(){return ta()};function wa(){var a=E;0<a%4096&&(a+=4096-a%4096);return a}for(var buffer,J,R,L,xa,M,ya,N,la,za=0,z=0,na=!1,Aa=0,x=0,Ba=0,E=0,Ca=b.TOTAL_STACK||5242880,F=b.TOTAL_MEMORY||16777216,U=65536;U<F||U<2*Ca;)U=16777216>U?2*U:U+16777216;U!==F&&(F=U);b.buffer?buffer=b.buffer:buffer=new ArrayBuffer(F);b.HEAP8=J=new Int8Array(buffer);
+ b.HEAP16=L=new Int16Array(buffer);b.HEAP32=M=new Int32Array(buffer);b.HEAPU8=R=new Uint8Array(buffer);b.HEAPU16=xa=new Uint16Array(buffer);b.HEAPU32=ya=new Uint32Array(buffer);b.HEAPF32=N=new Float32Array(buffer);b.HEAPF64=la=new Float64Array(buffer);M[0]=255;if(255!==R[0]||0!==R[3])throw"Typed arrays 2 must be run on a little-endian system";b.HEAP=void 0;b.buffer=buffer;b.HEAP8=J;b.HEAP16=L;b.HEAP32=M;b.HEAPU8=R;b.HEAPU16=xa;b.HEAPU32=ya;b.HEAPF32=N;b.HEAPF64=la;
+ function V(a){for(;0<a.length;){var c=a.shift();if("function"==typeof c)c();else{var d=c.J;"number"===typeof d?void 0===c.j?y.k("v",d):y.k("vi",d,[c.j]):d(void 0===c.j?null:c.j)}}}var Da=[],Ea=[],Fa=[],Ga=[],Ha=[],T=!1;function Ia(a){Da.unshift(a)}b.addOnPreRun=Ia;b.addOnInit=function(a){Ea.unshift(a)};b.addOnPreMain=function(a){Fa.unshift(a)};b.addOnExit=function(a){Ga.unshift(a)};function Ja(a){Ha.unshift(a)}b.addOnPostRun=Ja;
+ function Ka(a,c,d){d=Array(0<d?d:sa(a)+1);a=ra(a,d,0,d.length);c&&(d.length=a);return d}b.intArrayFromString=Ka;b.intArrayToString=function(a){for(var c=[],d=0;d<a.length;d++){var e=a[d];255<e&&(e&=255);c.push(String.fromCharCode(e))}return c.join("")};function fa(a,c,d){a=Ka(a,d);for(d=0;d<a.length;)J[c+d>>0]=a[d],d+=1}b.writeStringToMemory=fa;function ea(a,c){for(var d=0;d<a.length;d++)J[c++>>0]=a[d]}b.writeArrayToMemory=ea;
+ function pa(a,c,d){for(var e=0;e<a.length;++e)J[c++>>0]=a.charCodeAt(e);d||(J[c>>0]=0)}b.writeAsciiToMemory=pa;Math.imul&&-5===Math.imul(4294967295,5)||(Math.imul=function(a,c){var d=a&65535,e=c&65535;return d*e+((a>>>16)*e+d*(c>>>16)<<16)|0});Math.O=Math.imul;Math.clz32||(Math.clz32=function(a){a=a>>>0;for(var c=0;32>c;c++)if(a&1<<31-c)return c;return 32});Math.H=Math.clz32;var ha=Math.abs,ka=Math.ceil,ja=Math.floor,La=Math.pow,ia=Math.min,W=0,Ma=null,X=null;
+ b.addRunDependency=function(){W++;b.monitorRunDependencies&&b.monitorRunDependencies(W)};b.removeRunDependency=function(){W--;b.monitorRunDependencies&&b.monitorRunDependencies(W);if(0==W&&(null!==Ma&&(clearInterval(Ma),Ma=null),X)){var a=X;X=null;a()}};b.preloadedImages={};b.preloadedAudios={};za=8;z=za+31072;Ea.push();
+ O([1,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,2,0,0,0,1,0,0,0,7,0,0,0,0,0,0,0,4,0,0,0,3,0,0,0,6,0,0,0,1,0,0,0,5,0,0,0,2,0,0,0,15,0,0,0,0,0,0,0,8,0,0,0,7,0,0,0,12,0,0,0,3,0,0,0,11,0,0,0,4,0,0,0,14,0,0,0,1,0,0,0,9,0,0,0,6,0,0,0,13,0,0,0,2,0,0,0,10,0,0,0,5,0,0,0,0,0,157,62,0,64,94,62,0,192,4,62,0,128,237,62,0,64,137,62,0,0,0,0,0,192,76,63,0,0,205,61,0,0,0,0,190,98,0,0,198,98,0,0,215,98,0,0,232,98,0,0,247,98,0,0,8,99,0,0,32,99,0,0,46,99,0,0,0,0,128,63,0,0,0,64,0,0,64,64,0,0,128,64,0,0,160,64,0,0,192,64,0,0,224,
+ 64,0,0,0,65,0,0,128,65,0,0,192,65,0,0,16,66,0,0,48,66,0,0,72,66,0,0,96,66,0,0,120,66,0,0,134,66,0,0,144,66,0,0,158,66,0,0,176,66,0,0,212,66,0,0,6,67,0,0,128,63,0,0,128,63,0,0,128,63,0,0,128,63,0,0,128,63,0,0,128,63,0,0,128,63,0,0,0,64,0,0,0,64,0,0,0,64,0,0,0,64,0,0,0,64,0,0,0,64,0,0,0,64,0,0,64,64,0,0,64,64,0,0,128,64,0,0,160,64,0,0,192,64,0,0,0,65,0,0,0,65,168,1,0,0,104,4,0,0,36,7,0,0,220,9,0,0,144,12,0,0,64,15,0,0,236,17,0,0,84,19,0,0,16,20,0,0,132,20,0,0,208,20,0,0,8,21,0,0,40,21,0,0,64,21,0,0,
+ 76,21,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,
+ 1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,
+ 0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,
+ 0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,5,0,0,0,7,0,0,0,9,0,0,0,11,0,0,0,13,0,0,0,15,0,0,0,17,0,0,0,19,0,0,0,21,0,0,0,23,0,0,0,25,0,0,0,27,0,0,0,29,0,0,0,31,0,0,0,33,0,0,0,35,0,0,0,37,0,0,0,39,0,0,0,41,0,0,0,43,
+ 0,0,0,45,0,0,0,47,0,0,0,49,0,0,0,51,0,0,0,53,0,0,0,55,0,0,0,57,0,0,0,59,0,0,0,61,0,0,0,63,0,0,0,65,0,0,0,67,0,0,0,69,0,0,0,71,0,0,0,73,0,0,0,75,0,0,0,77,0,0,0,79,0,0,0,81,0,0,0,83,0,0,0,85,0,0,0,87,0,0,0,89,0,0,0,91,0,0,0,93,0,0,0,95,0,0,0,97,0,0,0,99,0,0,0,101,0,0,0,103,0,0,0,105,0,0,0,107,0,0,0,109,0,0,0,111,0,0,0,113,0,0,0,115,0,0,0,117,0,0,0,119,0,0,0,121,0,0,0,123,0,0,0,125,0,0,0,127,0,0,0,129,0,0,0,131,0,0,0,133,0,0,0,135,0,0,0,137,0,0,0,139,0,0,0,141,0,0,0,143,0,0,0,145,0,0,0,147,0,0,0,149,
+ 0,0,0,151,0,0,0,153,0,0,0,155,0,0,0,157,0,0,0,159,0,0,0,161,0,0,0,163,0,0,0,165,0,0,0,167,0,0,0,169,0,0,0,171,0,0,0,173,0,0,0,175,0,0,0,177,0,0,0,179,0,0,0,181,0,0,0,183,0,0,0,185,0,0,0,187,0,0,0,189,0,0,0,191,0,0,0,193,0,0,0,195,0,0,0,197,0,0,0,199,0,0,0,201,0,0,0,203,0,0,0,205,0,0,0,207,0,0,0,209,0,0,0,211,0,0,0,213,0,0,0,215,0,0,0,217,0,0,0,219,0,0,0,221,0,0,0,223,0,0,0,225,0,0,0,227,0,0,0,229,0,0,0,231,0,0,0,233,0,0,0,235,0,0,0,237,0,0,0,239,0,0,0,241,0,0,0,243,0,0,0,245,0,0,0,247,0,0,0,249,0,
+ 0,0,251,0,0,0,253,0,0,0,255,0,0,0,1,1,0,0,3,1,0,0,5,1,0,0,7,1,0,0,9,1,0,0,11,1,0,0,13,1,0,0,15,1,0,0,17,1,0,0,19,1,0,0,21,1,0,0,23,1,0,0,25,1,0,0,27,1,0,0,29,1,0,0,31,1,0,0,33,1,0,0,35,1,0,0,37,1,0,0,39,1,0,0,41,1,0,0,43,1,0,0,45,1,0,0,47,1,0,0,49,1,0,0,51,1,0,0,53,1,0,0,55,1,0,0,57,1,0,0,59,1,0,0,61,1,0,0,63,1,0,0,65,1,0,0,67,1,0,0,69,1,0,0,71,1,0,0,73,1,0,0,75,1,0,0,77,1,0,0,79,1,0,0,81,1,0,0,83,1,0,0,85,1,0,0,87,1,0,0,89,1,0,0,91,1,0,0,93,1,0,0,95,1,0,0,13,0,0,0,25,0,0,0,41,0,0,0,61,0,0,0,85,0,
+ 0,0,113,0,0,0,145,0,0,0,181,0,0,0,221,0,0,0,9,1,0,0,57,1,0,0,109,1,0,0,165,1,0,0,225,1,0,0,33,2,0,0,101,2,0,0,173,2,0,0,249,2,0,0,73,3,0,0,157,3,0,0,245,3,0,0,81,4,0,0,177,4,0,0,21,5,0,0,125,5,0,0,233,5,0,0,89,6,0,0,205,6,0,0,69,7,0,0,193,7,0,0,65,8,0,0,197,8,0,0,77,9,0,0,217,9,0,0,105,10,0,0,253,10,0,0,149,11,0,0,49,12,0,0,209,12,0,0,117,13,0,0,29,14,0,0,201,14,0,0,121,15,0,0,45,16,0,0,229,16,0,0,161,17,0,0,97,18,0,0,37,19,0,0,237,19,0,0,185,20,0,0,137,21,0,0,93,22,0,0,53,23,0,0,17,24,0,0,241,24,
+ 0,0,213,25,0,0,189,26,0,0,169,27,0,0,153,28,0,0,141,29,0,0,133,30,0,0,129,31,0,0,129,32,0,0,133,33,0,0,141,34,0,0,153,35,0,0,169,36,0,0,189,37,0,0,213,38,0,0,241,39,0,0,17,41,0,0,53,42,0,0,93,43,0,0,137,44,0,0,185,45,0,0,237,46,0,0,37,48,0,0,97,49,0,0,161,50,0,0,229,51,0,0,45,53,0,0,121,54,0,0,201,55,0,0,29,57,0,0,117,58,0,0,209,59,0,0,49,61,0,0,149,62,0,0,253,63,0,0,105,65,0,0,217,66,0,0,77,68,0,0,197,69,0,0,65,71,0,0,193,72,0,0,69,74,0,0,205,75,0,0,89,77,0,0,233,78,0,0,125,80,0,0,21,82,0,0,177,
+ 83,0,0,81,85,0,0,245,86,0,0,157,88,0,0,73,90,0,0,249,91,0,0,173,93,0,0,101,95,0,0,33,97,0,0,225,98,0,0,165,100,0,0,109,102,0,0,57,104,0,0,9,106,0,0,221,107,0,0,181,109,0,0,145,111,0,0,113,113,0,0,85,115,0,0,61,117,0,0,41,119,0,0,25,121,0,0,13,123,0,0,5,125,0,0,1,127,0,0,1,129,0,0,5,131,0,0,13,133,0,0,25,135,0,0,41,137,0,0,61,139,0,0,85,141,0,0,113,143,0,0,145,145,0,0,181,147,0,0,221,149,0,0,9,152,0,0,57,154,0,0,109,156,0,0,165,158,0,0,225,160,0,0,33,163,0,0,101,165,0,0,173,167,0,0,249,169,0,0,73,
+ 172,0,0,157,174,0,0,245,176,0,0,81,179,0,0,177,181,0,0,21,184,0,0,125,186,0,0,233,188,0,0,89,191,0,0,205,193,0,0,69,196,0,0,193,198,0,0,65,201,0,0,197,203,0,0,77,206,0,0,217,208,0,0,105,211,0,0,253,213,0,0,149,216,0,0,49,219,0,0,209,221,0,0,117,224,0,0,29,227,0,0,201,229,0,0,121,232,0,0,45,235,0,0,229,237,0,0,161,240,0,0,63,0,0,0,129,0,0,0,231,0,0,0,121,1,0,0,63,2,0,0,65,3,0,0,135,4,0,0,25,6,0,0,255,7,0,0,65,10,0,0,231,12,0,0,249,15,0,0,127,19,0,0,129,23,0,0,7,28,0,0,25,33,0,0,191,38,0,0,1,45,0,0,
+ 231,51,0,0,121,59,0,0,191,67,0,0,193,76,0,0,135,86,0,0,25,97,0,0,127,108,0,0,193,120,0,0,231,133,0,0,249,147,0,0,255,162,0,0,1,179,0,0,7,196,0,0,25,214,0,0,63,233,0,0,129,253,0,0,231,18,1,0,121,41,1,0,63,65,1,0,65,90,1,0,135,116,1,0,25,144,1,0,255,172,1,0,65,203,1,0,231,234,1,0,249,11,2,0,127,46,2,0,129,82,2,0,7,120,2,0,25,159,2,0,191,199,2,0,1,242,2,0,231,29,3,0,121,75,3,0,191,122,3,0,193,171,3,0,135,222,3,0,25,19,4,0,127,73,4,0,193,129,4,0,231,187,4,0,249,247,4,0,255,53,5,0,1,118,5,0,7,184,5,0,
+ 25,252,5,0,63,66,6,0,129,138,6,0,231,212,6,0,121,33,7,0,63,112,7,0,65,193,7,0,135,20,8,0,25,106,8,0,255,193,8,0,65,28,9,0,231,120,9,0,249,215,9,0,127,57,10,0,129,157,10,0,7,4,11,0,25,109,11,0,191,216,11,0,1,71,12,0,231,183,12,0,121,43,13,0,191,161,13,0,193,26,14,0,135,150,14,0,25,21,15,0,127,150,15,0,193,26,16,0,231,161,16,0,249,43,17,0,255,184,17,0,1,73,18,0,7,220,18,0,25,114,19,0,63,11,20,0,129,167,20,0,231,70,21,0,121,233,21,0,63,143,22,0,65,56,23,0,135,228,23,0,25,148,24,0,255,70,25,0,65,253,
+ 25,0,231,182,26,0,249,115,27,0,127,52,28,0,129,248,28,0,7,192,29,0,25,139,30,0,191,89,31,0,1,44,32,0,231,1,33,0,121,219,33,0,191,184,34,0,193,153,35,0,135,126,36,0,25,103,37,0,127,83,38,0,193,67,39,0,231,55,40,0,249,47,41,0,255,43,42,0,1,44,43,0,7,48,44,0,25,56,45,0,63,68,46,0,129,84,47,0,231,104,48,0,121,129,49,0,63,158,50,0,65,191,51,0,135,228,52,0,25,14,54,0,255,59,55,0,65,110,56,0,231,164,57,0,249,223,58,0,127,31,60,0,129,99,61,0,7,172,62,0,25,249,63,0,191,74,65,0,1,161,66,0,231,251,67,0,121,
+ 91,69,0,191,191,70,0,193,40,72,0,135,150,73,0,25,9,75,0,127,128,76,0,193,252,77,0,231,125,79,0,249,3,81,0,255,142,82,0,1,31,84,0,7,180,85,0,25,78,87,0,63,237,88,0,129,145,90,0,231,58,92,0,121,233,93,0,63,157,95,0,65,86,97,0,135,20,99,0,25,216,100,0,255,160,102,0,65,111,104,0,231,66,106,0,249,27,108,0,127,250,109,0,65,1,0,0,169,2,0,0,9,5,0,0,193,8,0,0,65,14,0,0,9,22,0,0,169,32,0,0,193,46,0,0,1,65,0,0,41,88,0,0,9,117,0,0,129,152,0,0,129,195,0,0,9,247,0,0,41,52,1,0,1,124,1,0,193,207,1,0,169,48,2,0,9,
+ 160,2,0,65,31,3,0,193,175,3,0,9,83,4,0,169,10,5,0,65,216,5,0,129,189,6,0,41,188,7,0,9,214,8,0,1,13,10,0,1,99,11,0,9,218,12,0,41,116,14,0,129,51,16,0,65,26,18,0,169,42,20,0,9,103,22,0,193,209,24,0,65,109,27,0,9,60,30,0,169,64,33,0,193,125,36,0,1,246,39,0,41,172,43,0,9,163,47,0,129,221,51,0,129,94,56,0,9,41,61,0,41,64,66,0,1,167,71,0,193,96,77,0,169,112,83,0,9,218,89,0,65,160,96,0,193,198,103,0,9,81,111,0,169,66,119,0,65,159,127,0,129,106,136,0,41,168,145,0,9,92,155,0,1,138,165,0,1,54,176,0,9,100,187,
+ 0,41,24,199,0,129,86,211,0,65,35,224,0,169,130,237,0,9,121,251,0,193,10,10,1,65,60,25,1,9,18,41,1,169,144,57,1,193,188,74,1,1,155,92,1,41,48,111,1,9,129,130,1,129,146,150,1,129,105,171,1,9,11,193,1,41,124,215,1,1,194,238,1,193,225,6,2,169,224,31,2,9,196,57,2,65,145,84,2,193,77,112,2,9,255,140,2,169,170,170,2,65,86,201,2,129,7,233,2,41,196,9,3,9,146,43,3,1,119,78,3,1,121,114,3,9,158,151,3,41,236,189,3,129,105,229,3,65,28,14,4,169,10,56,4,9,59,99,4,193,179,143,4,65,123,189,4,9,152,236,4,169,16,29,5,
+ 193,235,78,5,1,48,130,5,41,228,182,5,9,15,237,5,129,183,36,6,129,228,93,6,9,157,152,6,41,232,212,6,1,205,18,7,193,82,82,7,169,128,147,7,9,94,214,7,65,242,26,8,193,68,97,8,9,93,169,8,169,66,243,8,65,253,62,9,129,148,140,9,41,16,220,9,9,120,45,10,1,212,128,10,1,44,214,10,9,136,45,11,41,240,134,11,129,108,226,11,65,5,64,12,169,194,159,12,9,173,1,13,193,204,101,13,65,42,204,13,9,206,52,14,169,192,159,14,193,10,13,15,1,181,124,15,41,200,238,15,9,77,99,16,129,76,218,16,129,207,83,17,9,223,207,17,41,132,
+ 78,18,1,200,207,18,193,179,83,19,169,80,218,19,9,168,99,20,65,195,239,20,193,171,126,21,9,107,16,22,169,10,165,22,65,148,60,23,129,17,215,23,41,140,116,24,9,14,21,25,1,161,184,25,1,79,95,26,9,34,9,27,41,36,182,27,129,95,102,28,65,222,25,29,169,170,208,29,9,207,138,30,193,85,72,31,65,73,9,32,9,180,205,32,169,160,149,33,193,25,97,34,1,42,48,35,41,220,2,36,9,59,217,36,129,81,179,37,147,6,0,0,69,14,0,0,15,28,0,0,17,51,0,0,91,87,0,0,13,142,0,0,119,221,0,0,57,77,1,0,99,230,1,0,149,179,2,0,31,193,3,0,33,
+ 29,5,0,171,215,6,0,221,2,9,0,7,179,11,0,201,254,14,0,51,255,18,0,229,207,23,0,47,143,29,0,49,94,36,0,251,96,44,0,173,190,53,0,151,161,64,0,89,55,77,0,3,177,91,0,53,67,108,0,63,38,127,0,65,150,148,0,75,211,172,0,125,33,200,0,39,201,230,0,233,22,9,1,211,91,47,1,133,237,89,1,79,38,137,1,81,101,189,1,155,14,247,1,77,139,54,2,183,73,124,2,121,189,200,2,163,95,28,3,213,174,119,3,95,47,219,3,97,107,71,4,235,242,188,4,29,92,60,5,71,67,198,5,9,75,91,6,115,28,252,6,37,103,169,7,111,225,99,8,113,72,44,9,59,
+ 96,3,10,237,243,233,10,215,213,224,11,153,223,232,12,67,242,2,14,117,246,47,15,127,220,112,16,129,156,198,17,139,54,50,19,189,178,180,20,103,33,79,22,41,155,2,24,19,65,208,25,197,60,185,27,143,192,190,29,145,7,226,31,219,85,36,34,141,248,134,36,247,69,11,39,185,157,178,41,227,104,126,44,21,26,112,47,159,45,137,50,161,41,203,53,43,158,55,57,93,37,208,60,135,99,150,64,73,7,140,68,179,201,178,72,101,110,12,77,175,195,154,81,177,162,95,86,123,239,92,91,45,153,148,96,23,154,8,102,217,247,186,107,131,195,
+ 173,113,181,25,227,119,191,34,93,126,29,35,0,0,113,77,0,0,145,156,0,0,253,38,1,0,101,12,2,0,233,119,3,0,153,162,5,0,53,214,8,0,45,112,13,0,225,228,19,0,33,195,28,0,237,183,40,0,117,146,56,0,89,72,77,0,41,250,103,0,37,248,137,0,61,199,180,0,81,38,234,0,177,19,44,1,221,210,124,1,133,242,222,1,201,82,85,2,185,43,227,2,21,20,140,3,77,8,84,4,193,113,63,5,65,46,83,6,205,151,148,7,149,140,9,9,57,119,184,10,73,87,168,12,5,202,224,14,93,19,106,17,49,39,77,20,209,178,147,23,189,38,72,27,165,192,117,31,169,
+ 149,40,36,217,156,109,41,245,185,82,47,109,200,230,53,161,166,57,61,97,65,92,69,173,159,96,78,181,238,89,88,25,142,92,99,105,28,126,111,229,131,213,124,255,189,0,0,1,168,1,0,143,107,3,0,241,158,6,0,63,35,12,0,193,61,21,0,143,182,35,0,241,252,57,0,255,81,91,0,1,250,139,0,15,117,209,0,113,191,50,1,63,154,184,1,193,220,109,2,15,207,95,3,113,142,158,4,255,123,61,6,1,182,83,8,143,156,252,10,241,97,88,14,63,167,140,18,193,37,197,23,143,101,52,30,241,129,20,38,255,251,167,47,1,156,58,59,15,98,34,73,113,
+ 134,192,89,63,138,130,109,193,88,227,132,1,14,4,0,145,33,9,0,17,44,19,0,65,238,37,0,65,79,71,0,145,67,128,0,17,247,221,0,1,70,115,1,1,146,90,2,17,1,184,3,145,53,188,5,65,143,167,8,65,6,206,12,17,178,155,18,145,15,154,26,1,26,118,37,1,76,7,52,145,158,87,71,17,157,172,96,65,166,145,129,35,81,22,0,197,158,50,0,23,185,107,0,153,246,216,0,107,137,160,1,13,196,254,2,31,1,80,5,33,217,29,9,51,108,48,15,213,162,164,24,167,103,8,39,41,253,125,60,123,181,231,91,29,119,29,137,175,160,45,201,173,142,123,0,137,
+ 230,25,1,57,150,94,2,61,22,216,4,181,99,119,9,225,40,198,17,33,3,52,32,117,72,130,56,125,87,87,96,191,91,175,2,129,216,39,6,247,132,94,13,233,254,173,27,127,139,235,54,129,183,229,104,23,3,156,193,193,12,255,14,57,106,133,34,25,238,145,75,129,120,43,158,51,225,9,84,149,139,0,0,55,152,0,0,255,165,0,0,4,181,0,0,103,197,0,0,69,215,0,0,193,234,0,0,255,255,0,0,172,21,0,0,128,187,0,0,120,0,0,0,21,0,0,0,21,0,0,0,0,154,89,63,0,0,0,0,0,0,128,63,0,0,128,63,72,87,0,0,3,0,0,0,8,0,0,0,120,0,0,0,11,0,0,0,249,99,
+ 0,0,116,87,0,0,24,22,0,0,128,7,0,0,3,0,0,0,248,23,0,0,48,39,0,0,104,39,0,0,160,39,0,0,216,39,0,0,136,1,0,0,166,94,0,0,224,100,0,0,104,102,0,0,106,28,141,56,82,187,30,58,8,105,220,58,130,237,87,59,137,99,178,59,3,42,5,60,48,220,57,60,180,62,119,60,28,163,158,60,209,242,197,60,254,134,241,60,155,171,16,61,5,173,42,61,132,194,70,61,83,230,100,61,17,137,130,61,135,159,147,61,203,178,165,61,209,190,184,61,58,191,204,61,84,175,225,61,20,138,247,61,14,37,7,62,217,244,18,62,95,49,31,62,104,215,43,62,138,
+ 227,56,62,48,82,70,62,148,31,84,62,191,71,98,62,142,198,112,62,176,151,127,62,82,91,135,62,96,15,143,62,152,229,150,62,121,219,158,62,112,238,166,62,216,27,175,62,251,96,183,62,17,187,191,62,70,39,200,62,183,162,208,62,120,42,217,62,148,187,225,62,12,83,234,62,222,237,242,62,6,137,251,62,190,16,2,63,31,90,6,63,36,159,10,63,80,222,14,63,43,22,19,63,65,69,23,63,37,106,27,63,115,131,31,63,206,143,35,63,230,141,39,63,116,124,43,63,63,90,47,63,25,38,51,63,231,222,54,63,153,131,58,63,51,19,62,63,197,140,
+ 65,63,119,239,68,63,127,58,72,63,39,109,75,63,206,134,78,63,229,134,81,63,241,108,84,63,142,56,87,63,105,233,89,63,69,127,92,63,250,249,94,63,115,89,97,63,175,157,99,63,193,198,101,63,207,212,103,63,17,200,105,63,210,160,107,63,110,95,109,63,80,4,111,63,244,143,112,63,230,2,114,63,189,93,115,63,31,161,116,63,191,205,117,63,87,228,118,63,176,229,119,63,151,210,120,63,227,171,121,63,115,114,122,63,39,39,123,63,231,202,123,63,157,94,124,63,53,227,124,63,156,89,125,63,189,194,125,63,134,31,126,63,222,
+ 112,126,63,171,183,126,63,207,244,126,63,38,41,127,63,134,85,127,63,190,122,127,63,150,153,127,63,204,178,127,63,20,199,127,63,28,215,127,63,130,227,127,63,221,236,127,63,182,243,127,63,138,248,127,63,200,251,127,63,214,253,127,63,7,255,127,63,165,255,127,63,232,255,127,63,253,255,127,63,0,0,128,63,224,1,0,0,135,136,8,59,255,255,255,255,5,0,96,0,3,0,32,0,4,0,8,0,2,0,4,0,4,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,158,87,0,0,48,24,0,0,0,0,0,0,0,0,128,63,0,0,0,128,99,250,127,63,191,117,86,188,139,233,127,63,10,
+ 113,214,188,121,205,127,63,231,206,32,189,47,166,127,63,58,94,86,189,175,115,127,63,19,242,133,189,249,53,127,63,42,175,160,189,18,237,126,63,51,101,187,189,253,152,126,63,4,19,214,189,188,57,126,63,115,183,240,189,85,207,125,63,168,168,5,190,203,89,125,63,187,239,18,190,37,217,124,63,92,48,32,190,103,77,124,63,245,105,45,190,152,182,123,63,243,155,58,190,190,20,123,63,194,197,71,190,226,103,122,63,205,230,84,190,9,176,121,63,130,254,97,190,60,237,120,63,77,12,111,190,132,31,120,63,156,15,124,190,
+ 234,70,119,63,238,131,132,190,119,99,118,63,62,250,138,190,54,117,117,63,117,106,145,190,48,124,116,63,76,212,151,190,113,120,115,63,122,55,158,190,3,106,114,63,183,147,164,190,244,80,113,63,188,232,170,190,79,45,112,63,65,54,177,190,33,255,110,63,1,124,183,190,118,198,109,63,180,185,189,190,94,131,108,63,21,239,195,190,231,53,107,63,222,27,202,190,30,222,105,63,201,63,208,190,18,124,104,63,146,90,214,190,212,15,103,63,243,107,220,190,116,153,101,63,170,115,226,190,1,25,100,63,113,113,232,190,141,
+ 142,98,63,7,101,238,190,40,250,96,63,39,78,244,190,230,91,95,63,144,44,250,190,215,179,93,63,0,0,0,191,15,2,92,63,27,228,2,191,160,70,90,63,119,194,5,191,158,129,88,63,246,154,8,191,29,179,86,63,119,109,11,191,49,219,84,63,218,57,14,191,239,249,82,63,0,0,17,191,108,15,81,63,202,191,19,191,189,27,79,63,24,121,22,191,248,30,77,63,205,43,25,191,52,25,75,63,202,215,27,191,136,10,73,63,241,124,30,191,10,243,70,63,36,27,33,191,209,210,68,63,70,178,35,191,247,169,66,63,58,66,38,191,147,120,64,63,227,202,
+ 40,191,189,62,62,63,37,76,43,191,143,252,59,63,227,197,45,191,34,178,57,63,1,56,48,191,144,95,55,63,101,162,50,191,243,4,53,63,243,4,53,191,101,162,50,63,144,95,55,191,1,56,48,63,34,178,57,191,227,197,45,63,143,252,59,191,37,76,43,63,189,62,62,191,227,202,40,63,147,120,64,191,58,66,38,63,247,169,66,191,70,178,35,63,209,210,68,191,36,27,33,63,10,243,70,191,241,124,30,63,136,10,73,191,202,215,27,63,52,25,75,191,205,43,25,63,248,30,77,191,24,121,22,63,189,27,79,191,202,191,19,63,108,15,81,191,0,0,17,
+ 63,239,249,82,191,218,57,14,63,49,219,84,191,119,109,11,63,29,179,86,191,246,154,8,63,158,129,88,191,119,194,5,63,160,70,90,191,27,228,2,63,15,2,92,191,0,0,0,63,215,179,93,191,144,44,250,62,230,91,95,191,39,78,244,62,40,250,96,191,7,101,238,62,141,142,98,191,113,113,232,62,1,25,100,191,170,115,226,62,116,153,101,191,243,107,220,62,212,15,103,191,146,90,214,62,18,124,104,191,201,63,208,62,30,222,105,191,222,27,202,62,231,53,107,191,21,239,195,62,94,131,108,191,180,185,189,62,118,198,109,191,1,124,
+ 183,62,33,255,110,191,65,54,177,62,79,45,112,191,188,232,170,62,244,80,113,191,183,147,164,62,3,106,114,191,122,55,158,62,113,120,115,191,76,212,151,62,48,124,116,191,117,106,145,62,54,117,117,191,62,250,138,62,119,99,118,191,238,131,132,62,234,70,119,191,156,15,124,62,132,31,120,191,77,12,111,62,60,237,120,191,130,254,97,62,9,176,121,191,205,230,84,62,226,103,122,191,194,197,71,62,190,20,123,191,243,155,58,62,152,182,123,191,245,105,45,62,103,77,124,191,92,48,32,62,37,217,124,191,187,239,18,62,203,
+ 89,125,191,168,168,5,62,85,207,125,191,115,183,240,61,188,57,126,191,4,19,214,61,253,152,126,191,51,101,187,61,18,237,126,191,42,175,160,61,249,53,127,191,19,242,133,61,175,115,127,191,58,94,86,61,47,166,127,191,231,206,32,61,121,205,127,191,10,113,214,60,139,233,127,191,191,117,86,60,99,250,127,191,0,48,141,36,0,0,128,191,191,117,86,188,99,250,127,191,10,113,214,188,139,233,127,191,231,206,32,189,121,205,127,191,58,94,86,189,47,166,127,191,19,242,133,189,175,115,127,191,42,175,160,189,249,53,127,
+ 191,51,101,187,189,18,237,126,191,4,19,214,189,253,152,126,191,115,183,240,189,188,57,126,191,168,168,5,190,85,207,125,191,187,239,18,190,203,89,125,191,92,48,32,190,37,217,124,191,245,105,45,190,103,77,124,191,243,155,58,190,152,182,123,191,194,197,71,190,190,20,123,191,205,230,84,190,226,103,122,191,130,254,97,190,9,176,121,191,77,12,111,190,60,237,120,191,156,15,124,190,132,31,120,191,238,131,132,190,234,70,119,191,62,250,138,190,119,99,118,191,117,106,145,190,54,117,117,191,76,212,151,190,48,
+ 124,116,191,122,55,158,190,113,120,115,191,183,147,164,190,3,106,114,191,188,232,170,190,244,80,113,191,65,54,177,190,79,45,112,191,1,124,183,190,33,255,110,191,180,185,189,190,118,198,109,191,21,239,195,190,94,131,108,191,222,27,202,190,231,53,107,191,201,63,208,190,30,222,105,191,146,90,214,190,18,124,104,191,243,107,220,190,212,15,103,191,170,115,226,190,116,153,101,191,113,113,232,190,1,25,100,191,7,101,238,190,141,142,98,191,39,78,244,190,40,250,96,191,144,44,250,190,230,91,95,191,0,0,0,191,
+ 215,179,93,191,27,228,2,191,15,2,92,191,119,194,5,191,160,70,90,191,246,154,8,191,158,129,88,191,119,109,11,191,29,179,86,191,218,57,14,191,49,219,84,191,0,0,17,191,239,249,82,191,202,191,19,191,108,15,81,191,24,121,22,191,189,27,79,191,205,43,25,191,248,30,77,191,202,215,27,191,52,25,75,191,241,124,30,191,136,10,73,191,36,27,33,191,10,243,70,191,70,178,35,191,209,210,68,191,58,66,38,191,247,169,66,191,227,202,40,191,147,120,64,191,37,76,43,191,189,62,62,191,227,197,45,191,143,252,59,191,1,56,48,
+ 191,34,178,57,191,101,162,50,191,144,95,55,191,243,4,53,191,243,4,53,191,144,95,55,191,101,162,50,191,34,178,57,191,1,56,48,191,143,252,59,191,227,197,45,191,189,62,62,191,37,76,43,191,147,120,64,191,227,202,40,191,247,169,66,191,58,66,38,191,209,210,68,191,70,178,35,191,10,243,70,191,36,27,33,191,136,10,73,191,241,124,30,191,52,25,75,191,202,215,27,191,248,30,77,191,205,43,25,191,189,27,79,191,24,121,22,191,108,15,81,191,202,191,19,191,239,249,82,191,0,0,17,191,49,219,84,191,218,57,14,191,29,179,
+ 86,191,119,109,11,191,158,129,88,191,246,154,8,191,160,70,90,191,119,194,5,191,15,2,92,191,27,228,2,191,215,179,93,191,0,0,0,191,230,91,95,191,144,44,250,190,40,250,96,191,39,78,244,190,141,142,98,191,7,101,238,190,1,25,100,191,113,113,232,190,116,153,101,191,170,115,226,190,212,15,103,191,243,107,220,190,18,124,104,191,146,90,214,190,30,222,105,191,201,63,208,190,231,53,107,191,222,27,202,190,94,131,108,191,21,239,195,190,118,198,109,191,180,185,189,190,33,255,110,191,1,124,183,190,79,45,112,191,
+ 65,54,177,190,244,80,113,191,188,232,170,190,3,106,114,191,183,147,164,190,113,120,115,191,122,55,158,190,48,124,116,191,76,212,151,190,54,117,117,191,117,106,145,190,119,99,118,191,62,250,138,190,234,70,119,191,238,131,132,190,132,31,120,191,156,15,124,190,60,237,120,191,77,12,111,190,9,176,121,191,130,254,97,190,226,103,122,191,205,230,84,190,190,20,123,191,194,197,71,190,152,182,123,191,243,155,58,190,103,77,124,191,245,105,45,190,37,217,124,191,92,48,32,190,203,89,125,191,187,239,18,190,85,207,
+ 125,191,168,168,5,190,188,57,126,191,115,183,240,189,253,152,126,191,4,19,214,189,18,237,126,191,51,101,187,189,249,53,127,191,42,175,160,189,175,115,127,191,19,242,133,189,47,166,127,191,58,94,86,189,121,205,127,191,231,206,32,189,139,233,127,191,10,113,214,188,99,250,127,191,191,117,86,188,0,0,128,191,0,48,13,165,99,250,127,191,191,117,86,60,139,233,127,191,10,113,214,60,121,205,127,191,231,206,32,61,47,166,127,191,58,94,86,61,175,115,127,191,19,242,133,61,249,53,127,191,42,175,160,61,18,237,126,
+ 191,51,101,187,61,253,152,126,191,4,19,214,61,188,57,126,191,115,183,240,61,85,207,125,191,168,168,5,62,203,89,125,191,187,239,18,62,37,217,124,191,92,48,32,62,103,77,124,191,245,105,45,62,152,182,123,191,243,155,58,62,190,20,123,191,194,197,71,62,226,103,122,191,205,230,84,62,9,176,121,191,130,254,97,62,60,237,120,191,77,12,111,62,132,31,120,191,156,15,124,62,234,70,119,191,238,131,132,62,119,99,118,191,62,250,138,62,54,117,117,191,117,106,145,62,48,124,116,191,76,212,151,62,113,120,115,191,122,
+ 55,158,62,3,106,114,191,183,147,164,62,244,80,113,191,188,232,170,62,79,45,112,191,65,54,177,62,33,255,110,191,1,124,183,62,118,198,109,191,180,185,189,62,94,131,108,191,21,239,195,62,231,53,107,191,222,27,202,62,30,222,105,191,201,63,208,62,18,124,104,191,146,90,214,62,212,15,103,191,243,107,220,62,116,153,101,191,170,115,226,62,1,25,100,191,113,113,232,62,141,142,98,191,7,101,238,62,40,250,96,191,39,78,244,62,230,91,95,191,144,44,250,62,215,179,93,191,0,0,0,63,15,2,92,191,27,228,2,63,160,70,90,
+ 191,119,194,5,63,158,129,88,191,246,154,8,63,29,179,86,191,119,109,11,63,49,219,84,191,218,57,14,63,239,249,82,191,0,0,17,63,108,15,81,191,202,191,19,63,189,27,79,191,24,121,22,63,248,30,77,191,205,43,25,63,52,25,75,191,202,215,27,63,136,10,73,191,241,124,30,63,10,243,70,191,36,27,33,63,209,210,68,191,70,178,35,63,247,169,66,191,58,66,38,63,147,120,64,191,227,202,40,63,189,62,62,191,37,76,43,63,143,252,59,191,227,197,45,63,34,178,57,191,1,56,48,63,144,95,55,191,101,162,50,63,243,4,53,191,243,4,53,
+ 63,101,162,50,191,144,95,55,63,1,56,48,191,34,178,57,63,227,197,45,191,143,252,59,63,37,76,43,191,189,62,62,63,227,202,40,191,147,120,64,63,58,66,38,191,247,169,66,63,70,178,35,191,209,210,68,63,36,27,33,191,10,243,70,63,241,124,30,191,136,10,73,63,202,215,27,191,52,25,75,63,205,43,25,191,248,30,77,63,24,121,22,191,189,27,79,63,202,191,19,191,108,15,81,63,0,0,17,191,239,249,82,63,218,57,14,191,49,219,84,63,119,109,11,191,29,179,86,63,246,154,8,191,158,129,88,63,119,194,5,191,160,70,90,63,27,228,2,
+ 191,15,2,92,63,0,0,0,191,215,179,93,63,144,44,250,190,230,91,95,63,39,78,244,190,40,250,96,63,7,101,238,190,141,142,98,63,113,113,232,190,1,25,100,63,170,115,226,190,116,153,101,63,243,107,220,190,212,15,103,63,146,90,214,190,18,124,104,63,201,63,208,190,30,222,105,63,222,27,202,190,231,53,107,63,21,239,195,190,94,131,108,63,180,185,189,190,118,198,109,63,1,124,183,190,33,255,110,63,65,54,177,190,79,45,112,63,188,232,170,190,244,80,113,63,183,147,164,190,3,106,114,63,122,55,158,190,113,120,115,63,
+ 76,212,151,190,48,124,116,63,117,106,145,190,54,117,117,63,62,250,138,190,119,99,118,63,238,131,132,190,234,70,119,63,156,15,124,190,132,31,120,63,77,12,111,190,60,237,120,63,130,254,97,190,9,176,121,63,205,230,84,190,226,103,122,63,194,197,71,190,190,20,123,63,243,155,58,190,152,182,123,63,245,105,45,190,103,77,124,63,92,48,32,190,37,217,124,63,187,239,18,190,203,89,125,63,168,168,5,190,85,207,125,63,115,183,240,189,188,57,126,63,4,19,214,189,253,152,126,63,51,101,187,189,18,237,126,63,42,175,160,
+ 189,249,53,127,63,19,242,133,189,175,115,127,63,58,94,86,189,47,166,127,63,231,206,32,189,121,205,127,63,10,113,214,188,139,233,127,63,191,117,86,188,99,250,127,63,0,200,83,165,0,0,128,63,191,117,86,60,99,250,127,63,10,113,214,60,139,233,127,63,231,206,32,61,121,205,127,63,58,94,86,61,47,166,127,63,19,242,133,61,175,115,127,63,42,175,160,61,249,53,127,63,51,101,187,61,18,237,126,63,4,19,214,61,253,152,126,63,115,183,240,61,188,57,126,63,168,168,5,62,85,207,125,63,187,239,18,62,203,89,125,63,92,48,
+ 32,62,37,217,124,63,245,105,45,62,103,77,124,63,243,155,58,62,152,182,123,63,194,197,71,62,190,20,123,63,205,230,84,62,226,103,122,63,130,254,97,62,9,176,121,63,77,12,111,62,60,237,120,63,156,15,124,62,132,31,120,63,238,131,132,62,234,70,119,63,62,250,138,62,119,99,118,63,117,106,145,62,54,117,117,63,76,212,151,62,48,124,116,63,122,55,158,62,113,120,115,63,183,147,164,62,3,106,114,63,188,232,170,62,244,80,113,63,65,54,177,62,79,45,112,63,1,124,183,62,33,255,110,63,180,185,189,62,118,198,109,63,21,
+ 239,195,62,94,131,108,63,222,27,202,62,231,53,107,63,201,63,208,62,30,222,105,63,146,90,214,62,18,124,104,63,243,107,220,62,212,15,103,63,170,115,226,62,116,153,101,63,113,113,232,62,1,25,100,63,7,101,238,62,141,142,98,63,39,78,244,62,40,250,96,63,144,44,250,62,230,91,95,63,0,0,0,63,215,179,93,63,27,228,2,63,15,2,92,63,119,194,5,63,160,70,90,63,246,154,8,63,158,129,88,63,119,109,11,63,29,179,86,63,218,57,14,63,49,219,84,63,0,0,17,63,239,249,82,63,202,191,19,63,108,15,81,63,24,121,22,63,189,27,79,
+ 63,205,43,25,63,248,30,77,63,202,215,27,63,52,25,75,63,241,124,30,63,136,10,73,63,36,27,33,63,10,243,70,63,70,178,35,63,209,210,68,63,58,66,38,63,247,169,66,63,227,202,40,63,147,120,64,63,37,76,43,63,189,62,62,63,227,197,45,63,143,252,59,63,1,56,48,63,34,178,57,63,101,162,50,63,144,95,55,63,243,4,53,63,243,4,53,63,144,95,55,63,101,162,50,63,34,178,57,63,1,56,48,63,143,252,59,63,227,197,45,63,189,62,62,63,37,76,43,63,147,120,64,63,227,202,40,63,247,169,66,63,58,66,38,63,209,210,68,63,70,178,35,63,
+ 10,243,70,63,36,27,33,63,136,10,73,63,241,124,30,63,52,25,75,63,202,215,27,63,248,30,77,63,205,43,25,63,189,27,79,63,24,121,22,63,108,15,81,63,202,191,19,63,239,249,82,63,0,0,17,63,49,219,84,63,218,57,14,63,29,179,86,63,119,109,11,63,158,129,88,63,246,154,8,63,160,70,90,63,119,194,5,63,15,2,92,63,27,228,2,63,215,179,93,63,0,0,0,63,230,91,95,63,144,44,250,62,40,250,96,63,39,78,244,62,141,142,98,63,7,101,238,62,1,25,100,63,113,113,232,62,116,153,101,63,170,115,226,62,212,15,103,63,243,107,220,62,18,
+ 124,104,63,146,90,214,62,30,222,105,63,201,63,208,62,231,53,107,63,222,27,202,62,94,131,108,63,21,239,195,62,118,198,109,63,180,185,189,62,33,255,110,63,1,124,183,62,79,45,112,63,65,54,177,62,244,80,113,63,188,232,170,62,3,106,114,63,183,147,164,62,113,120,115,63,122,55,158,62,48,124,116,63,76,212,151,62,54,117,117,63,117,106,145,62,119,99,118,63,62,250,138,62,234,70,119,63,238,131,132,62,132,31,120,63,156,15,124,62,60,237,120,63,77,12,111,62,9,176,121,63,130,254,97,62,226,103,122,63,205,230,84,62,
+ 190,20,123,63,194,197,71,62,152,182,123,63,243,155,58,62,103,77,124,63,245,105,45,62,37,217,124,63,92,48,32,62,203,89,125,63,187,239,18,62,85,207,125,63,168,168,5,62,188,57,126,63,115,183,240,61,253,152,126,63,4,19,214,61,18,237,126,63,51,101,187,61,249,53,127,63,42,175,160,61,175,115,127,63,19,242,133,61,47,166,127,63,58,94,86,61,121,205,127,63,231,206,32,61,139,233,127,63,10,113,214,60,99,250,127,63,191,117,86,60,240,0,0,0,137,136,136,59,1,0,0,0,5,0,48,0,3,0,16,0,4,0,4,0,4,0,1,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,94,91,0,0,48,24,0,0,0,0,0,0,120,0,0,0,136,136,8,60,2,0,0,0,5,0,24,0,3,0,8,0,2,0,4,0,4,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,62,93,0,0,48,24,0,0,0,0,0,0,60,0,0,0,137,136,136,60,3,0,0,0,5,0,12,0,3,0,4,0,4,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,46,94,0,0,48,24,0,0,0,0,0,0,255,255,127,63,142,255,127,63,106,254,127,63,147,252,127,63,7,250,127,63,200,246,127,63,214,242,127,63,48,238,127,63,214,232,127,63,200,226,127,63,7,220,127,63,147,212,127,63],"i8",4,y.h);
+ O([107,204,127,63,143,195,127,63,0,186,127,63,189,175,127,63,199,164,127,63,29,153,127,63,192,140,127,63,176,127,127,63,236,113,127,63,118,99,127,63,75,84,127,63,110,68,127,63,222,51,127,63,154,34,127,63,163,16,127,63,250,253,126,63,157,234,126,63,141,214,126,63,203,193,126,63,86,172,126,63,46,150,126,63,83,127,126,63,198,103,126,63,134,79,126,63,148,54,126,63,239,28,126,63,152,2,126,63,143,231,125,63,211,203,125,63,102,175,125,63,70,146,125,63,116,116,125,63,241,85,125,63,188,54,125,63,213,22,125,
+ 63,60,246,124,63,242,212,124,63,246,178,124,63,73,144,124,63,235,108,124,63,219,72,124,63,27,36,124,63,169,254,123,63,135,216,123,63,180,177,123,63,48,138,123,63,252,97,123,63,23,57,123,63,130,15,123,63,61,229,122,63,72,186,122,63,162,142,122,63,77,98,122,63,72,53,122,63,148,7,122,63,48,217,121,63,29,170,121,63,90,122,121,63,233,73,121,63,200,24,121,63,249,230,120,63,123,180,120,63,78,129,120,63,115,77,120,63,234,24,120,63,178,227,119,63,205,173,119,63,58,119,119,63,249,63,119,63,10,8,119,63,110,
+ 207,118,63,37,150,118,63,47,92,118,63,140,33,118,63,60,230,117,63,64,170,117,63,151,109,117,63,66,48,117,63,65,242,116,63,148,179,116,63,59,116,116,63,55,52,116,63,135,243,115,63,44,178,115,63,38,112,115,63,118,45,115,63,26,234,114,63,20,166,114,63,100,97,114,63,10,28,114,63,5,214,113,63,87,143,113,63,0,72,113,63,255,255,112,63,85,183,112,63,2,110,112,63,6,36,112,63,98,217,111,63,21,142,111,63,32,66,111,63,132,245,110,63,63,168,110,63,83,90,110,63,192,11,110,63,134,188,109,63,165,108,109,63,29,28,
+ 109,63,239,202,108,63,27,121,108,63,161,38,108,63,128,211,107,63,187,127,107,63,80,43,107,63,64,214,106,63,140,128,106,63,50,42,106,63,53,211,105,63,147,123,105,63,77,35,105,63,100,202,104,63,216,112,104,63,168,22,104,63,213,187,103,63,96,96,103,63,72,4,103,63,143,167,102,63,51,74,102,63,54,236,101,63,151,141,101,63,87,46,101,63,119,206,100,63,245,109,100,63,212,12,100,63,18,171,99,63,177,72,99,63,176,229,98,63,16,130,98,63,209,29,98,63,243,184,97,63,119,83,97,63,92,237,96,63,164,134,96,63,78,31,
+ 96,63,91,183,95,63,203,78,95,63,158,229,94,63,213,123,94,63,112,17,94,63,110,166,93,63,210,58,93,63,154,206,92,63,198,97,92,63,89,244,91,63,81,134,91,63,174,23,91,63,114,168,90,63,157,56,90,63,46,200,89,63,39,87,89,63,135,229,88,63,79,115,88,63,127,0,88,63,23,141,87,63,24,25,87,63,130,164,86,63,86,47,86,63,147,185,85,63,58,67,85,63,75,204,84,63,199,84,84,63,174,220,83,63,1,100,83,63,191,234,82,63,233,112,82,63,127,246,81,63,130,123,81,63,242,255,80,63,207,131,80,63,26,7,80,63,210,137,79,63,250,11,
+ 79,63,144,141,78,63,148,14,78,63,9,143,77,63,237,14,77,63,65,142,76,63,5,13,76,63,59,139,75,63,225,8,75,63,249,133,74,63,131,2,74,63,127,126,73,63,238,249,72,63,207,116,72,63,36,239,71,63,237,104,71,63,41,226,70,63,218,90,70,63,0,211,69,63,155,74,69,63,172,193,68,63,50,56,68,63,47,174,67,63,162,35,67,63,141,152,66,63,239,12,66,63,200,128,65,63,26,244,64,63,229,102,64,63,40,217,63,63,229,74,63,63,27,188,62,63,204,44,62,63,247,156,61,63,157,12,61,63,190,123,60,63,92,234,59,63,117,88,59,63,10,198,58,
+ 63,29,51,58,63,173,159,57,63,187,11,57,63,71,119,56,63,81,226,55,63,218,76,55,63,227,182,54,63,107,32,54,63,116,137,53,63,253,241,52,63,7,90,52,63,147,193,51,63,160,40,51,63,48,143,50,63,66,245,49,63,216,90,49,63,241,191,48,63,142,36,48,63,175,136,47,63,85,236,46,63,129,79,46,63,50,178,45,63,105,20,45,63,39,118,44,63,107,215,43,63,55,56,43,63,139,152,42,63,103,248,41,63,204,87,41,63,186,182,40,63,50,21,40,63,51,115,39,63,191,208,38,63,214,45,38,63,121,138,37,63,167,230,36,63,97,66,36,63,169,157,35,
+ 63,125,248,34,63,223,82,34,63,207,172,33,63,77,6,33,63,91,95,32,63,248,183,31,63,37,16,31,63,226,103,30,63,48,191,29,63,16,22,29,63,129,108,28,63,132,194,27,63,26,24,27,63,67,109,26,63,0,194,25,63,81,22,25,63,54,106,24,63,177,189,23,63,193,16,23,63,103,99,22,63,163,181,21,63,118,7,21,63,225,88,20,63,228,169,19,63,127,250,18,63,179,74,18,63,128,154,17,63,231,233,16,63,232,56,16,63,132,135,15,63,187,213,14,63,142,35,14,63,254,112,13,63,10,190,12,63,179,10,12,63,250,86,11,63,223,162,10,63,99,238,9,63,
+ 134,57,9,63,73,132,8,63,172,206,7,63,175,24,7,63,84,98,6,63,155,171,5,63,131,244,4,63,15,61,4,63,61,133,3,63,15,205,2,63,134,20,2,63,161,91,1,63,97,162,0,63,143,209,255,62,167,93,254,62,14,233,252,62,194,115,251,62,198,253,249,62,27,135,248,62,193,15,247,62,186,151,245,62,6,31,244,62,168,165,242,62,158,43,241,62,236,176,239,62,145,53,238,62,144,185,236,62,232,60,235,62,154,191,233,62,169,65,232,62,21,195,230,62,223,67,229,62,8,196,227,62,145,67,226,62,124,194,224,62,200,64,223,62,120,190,221,62,140,
+ 59,220,62,6,184,218,62,230,51,217,62,46,175,215,62,223,41,214,62,249,163,212,62,125,29,211,62,110,150,209,62,204,14,208,62,151,134,206,62,210,253,204,62,125,116,203,62,153,234,201,62,39,96,200,62,40,213,198,62,159,73,197,62,138,189,195,62,236,48,194,62,198,163,192,62,25,22,191,62,230,135,189,62,45,249,187,62,241,105,186,62,50,218,184,62,241,73,183,62,47,185,181,62,238,39,180,62,47,150,178,62,242,3,177,62,57,113,175,62,4,222,173,62,86,74,172,62,47,182,170,62,144,33,169,62,122,140,167,62,239,246,165,
+ 62,239,96,164,62,124,202,162,62,151,51,161,62,64,156,159,62,122,4,158,62,68,108,156,62,161,211,154,62,145,58,153,62,22,161,151,62,48,7,150,62,225,108,148,62,41,210,146,62,11,55,145,62,135,155,143,62,158,255,141,62,81,99,140,62,162,198,138,62,145,41,137,62,32,140,135,62,80,238,133,62,34,80,132,62,151,177,130,62,176,18,129,62,222,230,126,62,169,167,123,62,195,103,120,62,47,39,117,62,238,229,113,62,4,164,110,62,115,97,107,62,60,30,104,62,98,218,100,62,232,149,97,62,207,80,94,62,26,11,91,62,204,196,87,
+ 62,230,125,84,62,107,54,81,62,93,238,77,62,191,165,74,62,146,92,71,62,218,18,68,62,151,200,64,62,206,125,61,62,128,50,58,62,174,230,54,62,93,154,51,62,141,77,48,62,66,0,45,62,125,178,41,62,66,100,38,62,145,21,35,62,110,198,31,62,219,118,28,62,218,38,25,62,109,214,21,62,152,133,18,62,91,52,15,62,186,226,11,62,183,144,8,62,84,62,5,62,148,235,1,62,240,48,253,61,6,138,246,61,113,226,239,61,51,58,233,61,79,145,226,61,207,231,219,61,181,61,213,61,3,147,206,61,192,231,199,61,242,59,193,61,156,143,186,61,
+ 195,226,179,61,108,53,173,61,155,135,166,61,85,217,159,61,159,42,153,61,126,123,146,61,246,203,139,61,11,28,133,61,135,215,124,61,70,118,111,61,93,20,98,61,214,177,84,61,185,78,71,61,16,235,57,61,229,134,44,61,64,34,31,61,44,189,17,61,178,87,4,61,181,227,237,60,96,23,211,60,118,74,184,60,11,125,157,60,50,175,130,60,250,193,79,60,254,36,26,60,42,15,201,59,153,167,59,59,46,125,214,185,210,70,113,187,171,222,227,187,166,140,39,188,129,41,93,188,225,98,137,188,160,48,164,188,236,253,190,188,179,202,217,
+ 188,224,150,244,188,49,177,7,189,147,22,21,189,140,123,34,189,19,224,47,189,30,68,61,189,165,167,74,189,157,10,88,189,254,108,101,189,190,206,114,189,234,23,128,189,27,200,134,189,237,119,141,189,92,39,148,189,99,214,154,189,253,132,161,189,38,51,168,189,217,224,174,189,17,142,181,189,202,58,188,189,254,230,194,189,170,146,201,189,200,61,208,189,84,232,214,189,74,146,221,189,164,59,228,189,93,228,234,189,114,140,241,189,221,51,248,189,154,218,254,189,82,192,2,190,252,18,6,190,71,101,9,190,50,183,
+ 12,190,186,8,16,190,221,89,19,190,152,170,22,190,234,250,25,190,208,74,29,190,71,154,32,190,78,233,35,190,225,55,39,190,0,134,42,190,166,211,45,190,211,32,49,190,131,109,52,190,181,185,55,190,101,5,59,190,147,80,62,190,58,155,65,190,90,229,68,190,240,46,72,190,249,119,75,190,116,192,78,190,93,8,82,190,179,79,85,190,115,150,88,190,156,220,91,190,42,34,95,190,27,103,98,190,109,171,101,190,31,239,104,190,44,50,108,190,148,116,111,190,84,182,114,190,106,247,117,190,211,55,121,190,141,119,124,190,150,
+ 182,127,190,117,122,129,190,69,25,131,190,185,183,132,190,208,85,134,190,136,243,135,190,225,144,137,190,218,45,139,190,112,202,140,190,164,102,142,190,116,2,144,190,223,157,145,190,228,56,147,190,129,211,148,190,182,109,150,190,129,7,152,190,226,160,153,190,215,57,155,190,95,210,156,190,121,106,158,190,35,2,160,190,94,153,161,190,38,48,163,190,125,198,164,190,96,92,166,190,206,241,167,190,198,134,169,190,71,27,171,190,80,175,172,190,224,66,174,190,245,213,175,190,143,104,177,190,173,250,178,190,
+ 77,140,180,190,110,29,182,190,16,174,183,190,48,62,185,190,207,205,186,190,234,92,188,190,130,235,189,190,148,121,191,190,31,7,193,190,35,148,194,190,159,32,196,190,145,172,197,190,248,55,199,190,211,194,200,190,34,77,202,190,226,214,203,190,19,96,205,190,181,232,206,190,197,112,208,190,66,248,209,190,45,127,211,190,131,5,213,190,67,139,214,190,109,16,216,190,255,148,217,190,249,24,219,190,89,156,220,190,29,31,222,190,70,161,223,190,211,34,225,190,193,163,226,190,16,36,228,190,190,163,229,190,204,
+ 34,231,190,56,161,232,190,0,31,234,190,36,156,235,190,162,24,237,190,122,148,238,190,171,15,240,190,51,138,241,190,18,4,243,190,70,125,244,190,207,245,245,190,170,109,247,190,217,228,248,190,88,91,250,190,40,209,251,190,71,70,253,190,181,186,254,190,56,23,0,191,187,208,0,191,228,137,1,191,178,66,2,191,37,251,2,191,59,179,3,191,246,106,4,191,83,34,5,191,83,217,5,191,245,143,6,191,56,70,7,191,29,252,7,191,162,177,8,191,199,102,9,191,140,27,10,191,240,207,10,191,243,131,11,191,147,55,12,191,209,234,
+ 12,191,172,157,13,191,36,80,14,191,56,2,15,191,232,179,15,191,50,101,16,191,24,22,17,191,151,198,17,191,176,118,18,191,99,38,19,191,174,213,19,191,145,132,20,191,13,51,21,191,31,225,21,191,200,142,22,191,8,60,23,191,221,232,23,191,72,149,24,191,72,65,25,191,220,236,25,191,4,152,26,191,192,66,27,191,15,237,27,191,240,150,28,191,99,64,29,191,104,233,29,191,254,145,30,191,37,58,31,191,220,225,31,191,35,137,32,191,250,47,33,191,95,214,33,191,82,124,34,191,212,33,35,191,227,198,35,191,127,107,36,191,167,
+ 15,37,191,92,179,37,191,157,86,38,191,104,249,38,191,191,155,39,191,160,61,40,191,11,223,40,191,255,127,41,191,125,32,42,191,131,192,42,191,17,96,43,191,39,255,43,191,196,157,44,191,232,59,45,191,146,217,45,191,195,118,46,191,121,19,47,191,180,175,47,191,115,75,48,191,183,230,48,191,127,129,49,191,203,27,50,191,153,181,50,191,234,78,51,191,189,231,51,191,18,128,52,191,232,23,53,191,63,175,53,191,22,70,54,191,110,220,54,191,69,114,55,191,156,7,56,191,113,156,56,191,197,48,57,191,150,196,57,191,230,
+ 87,58,191,178,234,58,191,252,124,59,191,194,14,60,191,3,160,60,191,193,48,61,191,250,192,61,191,173,80,62,191,219,223,62,191,131,110,63,191,165,252,63,191,64,138,64,191,83,23,65,191,224,163,65,191,228,47,66,191,96,187,66,191,83,70,67,191,190,208,67,191,158,90,68,191,246,227,68,191,194,108,69,191,5,245,69,191,188,124,70,191,232,3,71,191,137,138,71,191,157,16,72,191,37,150,72,191,32,27,73,191,142,159,73,191,111,35,74,191,193,166,74,191,134,41,75,191,188,171,75,191,99,45,76,191,122,174,76,191,2,47,77,
+ 191,250,174,77,191,98,46,78,191,57,173,78,191,126,43,79,191,51,169,79,191,85,38,80,191,230,162,80,191,228,30,81,191,80,154,81,191,40,21,82,191,109,143,82,191,30,9,83,191,59,130,83,191,195,250,83,191,183,114,84,191,22,234,84,191,223,96,85,191,18,215,85,191,176,76,86,191,183,193,86,191,39,54,87,191,0,170,87,191,66,29,88,191,236,143,88,191,254,1,89,191,120,115,89,191,89,228,89,191,162,84,90,191,81,196,90,191,102,51,91,191,226,161,91,191,195,15,92,191,10,125,92,191,183,233,92,191,200,85,93,191,62,193,
+ 93,191,24,44,94,191,87,150,94,191,249,255,94,191,255,104,95,191,104,209,95,191,51,57,96,191,98,160,96,191,243,6,97,191,229,108,97,191,58,210,97,191,240,54,98,191,8,155,98,191,128,254,98,191,89,97,99,191,146,195,99,191,44,37,100,191,37,134,100,191,126,230,100,191,55,70,101,191,78,165,101,191,197,3,102,191,154,97,102,191,205,190,102,191,94,27,103,191,77,119,103,191,154,210,103,191,68,45,104,191,75,135,104,191,174,224,104,191,111,57,105,191,139,145,105,191,4,233,105,191,217,63,106,191,9,150,106,191,
+ 148,235,106,191,123,64,107,191,188,148,107,191,89,232,107,191,79,59,108,191,160,141,108,191,75,223,108,191,79,48,109,191,173,128,109,191,101,208,109,191,117,31,110,191,223,109,110,191,161,187,110,191,187,8,111,191,46,85,111,191,248,160,111,191,27,236,111,191,149,54,112,191,103,128,112,191,144,201,112,191,15,18,113,191,230,89,113,191,19,161,113,191,151,231,113,191,113,45,114,191,160,114,114,191,38,183,114,191,1,251,114,191,50,62,115,191,184,128,115,191,148,194,115,191,196,3,116,191,73,68,116,191,34,
+ 132,116,191,80,195,116,191,210,1,117,191,168,63,117,191,210,124,117,191,80,185,117,191,33,245,117,191,69,48,118,191,189,106,118,191,136,164,118,191,166,221,118,191,22,22,119,191,217,77,119,191,239,132,119,191,87,187,119,191,17,241,119,191,29,38,120,191,122,90,120,191,42,142,120,191,43,193,120,191,125,243,120,191,33,37,121,191,22,86,121,191,92,134,121,191,242,181,121,191,218,228,121,191,18,19,122,191,154,64,122,191,115,109,122,191,157,153,122,191,22,197,122,191,223,239,122,191,248,25,123,191,97,67,
+ 123,191,26,108,123,191,34,148,123,191,122,187,123,191,32,226,123,191,23,8,124,191,92,45,124,191,240,81,124,191,211,117,124,191,5,153,124,191,134,187,124,191,85,221,124,191,115,254,124,191,223,30,125,191,154,62,125,191,163,93,125,191,250,123,125,191,159,153,125,191,146,182,125,191,211,210,125,191,98,238,125,191,63,9,126,191,105,35,126,191,225,60,126,191,167,85,126,191,186,109,126,191,27,133,126,191,201,155,126,191,196,177,126,191,13,199,126,191,162,219,126,191,133,239,126,191,181,2,127,191,50,21,127,
+ 191,252,38,127,191,19,56,127,191,118,72,127,191,39,88,127,191,36,103,127,191,110,117,127,191,5,131,127,191,232,143,127,191,25,156,127,191,149,167,127,191,95,178,127,191,116,188,127,191,215,197,127,191,133,206,127,191,129,214,127,191,200,221,127,191,93,228,127,191,61,234,127,191,106,239,127,191,227,243,127,191,169,247,127,191,187,250,127,191,25,253,127,191,196,254,127,191,187,255,127,191,250,255,127,63,57,254,127,63,169,249,127,63,75,242,127,63,30,232,127,63,35,219,127,63,89,203,127,63,193,184,127,
+ 63,91,163,127,63,40,139,127,63,39,112,127,63,90,82,127,63,191,49,127,63,88,14,127,63,37,232,126,63,38,191,126,63,92,147,126,63,200,100,126,63,105,51,126,63,65,255,125,63,79,200,125,63,150,142,125,63,20,82,125,63,203,18,125,63,188,208,124,63,231,139,124,63,77,68,124,63,239,249,123,63,205,172,123,63,233,92,123,63,67,10,123,63,221,180,122,63,182,92,122,63,209,1,122,63,46,164,121,63,206,67,121,63,178,224,120,63,220,122,120,63,76,18,120,63,4,167,119,63,4,57,119,63,79,200,118,63,228,84,118,63,198,222,117,
+ 63,246,101,117,63,117,234,116,63,68,108,116,63,101,235,115,63,218,103,115,63,163,225,114,63,194,88,114,63,57,205,113,63,9,63,113,63,52,174,112,63,187,26,112,63,160,132,111,63,228,235,110,63,138,80,110,63,147,178,109,63,1,18,109,63,213,110,108,63,17,201,107,63,183,32,107,63,201,117,106,63,73,200,105,63,57,24,105,63,155,101,104,63,111,176,103,63,186,248,102,63,124,62,102,63,184,129,101,63,111,194,100,63,164,0,100,63,90,60,99,63,145,117,98,63,76,172,97,63,142,224,96,63,89,18,96,63,174,65,95,63,145,110,
+ 94,63,3,153,93,63,8,193,92,63,160,230,91,63,207,9,91,63,152,42,90,63,251,72,89,63,253,100,88,63,159,126,87,63,229,149,86,63,208,170,85,63,99,189,84,63,161,205,83,63,140,219,82,63,39,231,81,63,117,240,80,63,121,247,79,63,52,252,78,63,171,254,77,63,223,254,76,63,212,252,75,63,140,248,74,63,10,242,73,63,82,233,72,63,101,222,71,63,71,209,70,63,251,193,69,63,132,176,68,63,229,156,67,63,32,135,66,63,58,111,65,63,52,85,64,63,19,57,63,63,216,26,62,63,136,250,60,63,38,216,59,63,180,179,58,63,54,141,57,63,
+ 175,100,56,63,34,58,55,63,147,13,54,63,5,223,52,63,124,174,51,63,249,123,50,63,130,71,49,63,25,17,48,63,194,216,46,63,127,158,45,63,86,98,44,63,72,36,43,63,90,228,41,63,144,162,40,63,235,94,39,63,113,25,38,63,37,210,36,63,9,137,35,63,35,62,34,63,117,241,32,63,4,163,31,63,210,82,30,63,228,0,29,63,61,173,27,63,225,87,26,63,211,0,25,63,25,168,23,63,180,77,22,63,170,241,20,63,253,147,19,63,178,52,18,63,204,211,16,63,80,113,15,63,66,13,14,63,164,167,12,63,124,64,11,63,205,215,9,63,154,109,8,63,233,1,7,
+ 63,189,148,5,63,25,38,4,63,3,182,2,63,126,68,1,63,28,163,255,62,110,186,252,62,250,206,249,62,202,224,246,62,228,239,243,62,81,252,240,62,26,6,238,62,71,13,235,62,224,17,232,62,237,19,229,62,119,19,226,62,135,16,223,62,36,11,220,62,88,3,217,62,42,249,213,62,164,236,210,62,205,221,207,62,175,204,204,62,82,185,201,62,191,163,198,62,254,139,195,62,24,114,192,62,22,86,189,62,0,56,186,62,224,23,183,62,189,245,179,62,161,209,176,62,149,171,173,62,162,131,170,62,207,89,167,62,39,46,164,62,178,0,161,62,121,
+ 209,157,62,133,160,154,62,223,109,151,62,143,57,148,62,160,3,145,62,26,204,141,62,5,147,138,62,107,88,135,62,86,28,132,62,205,222,128,62,182,63,123,62,16,191,116,62,187,59,110,62,201,181,103,62,77,45,97,62,89,162,90,62,255,20,84,62,81,133,77,62,99,243,70,62,70,95,64,62,13,201,57,62,202,48,51,62,144,150,44,62,114,250,37,62,130,92,31,62,210,188,24,62,118,27,18,62,127,120,11,62,1,212,4,62,29,92,252,61,114,13,239,61,41,188,225,61,102,104,212,61,78,18,199,61,8,186,185,61,184,95,172,61,132,3,159,61,146,
+ 165,145,61,7,70,132,61,18,202,109,61,122,5,83,61,145,62,56,61,164,117,29,61,252,170,2,61,202,189,207,60,86,35,154,60,97,14,73,60,197,167,187,59,61,122,86,186,9,70,241,187,18,221,99,188,80,138,167,188,65,36,221,188,227,93,9,189,35,40,36,189,150,240,62,189,242,182,89,189,234,122,116,189,26,158,135,189,66,253,148,189,200,90,162,189,134,182,175,189,87,16,189,189,22,104,202,189,155,189,215,189,195,16,229,189,105,97,242,189,101,175,255,189,74,125,6,190,104,33,13,190,250,195,19,190,237,100,26,190,46,4,33,
+ 190,172,161,39,190,83,61,46,190,16,215,52,190,210,110,59,190,134,4,66,190,25,152,72,190,121,41,79,190,148,184,85,190,86,69,92,190,174,207,98,190,137,87,105,190,214,220,111,190,128,95,118,190,120,223,124,190,84,174,129,190,129,235,132,190,56,39,136,190,114,97,139,190,36,154,142,190,69,209,145,190,205,6,149,190,179,58,152,190,238,108,155,190,116,157,158,190,61,204,161,190,64,249,164,190,115,36,168,190,207,77,171,190,73,117,174,190,218,154,177,190,120,190,180,190,27,224,183,190,186,255,186,190,75,29,
+ 190,190,199,56,193,190,37,82,196,190,91,105,199,190,97,126,202,190,48,145,205,190,188,161,208,190,0,176,211,190,241,187,214,190,135,197,217,190,186,204,220,190,129,209,223,190,211,211,226,190,169,211,229,190,250,208,232,190,189,203,235,190,234,195,238,190,120,185,241,190,96,172,244,190,154,156,247,190,28,138,250,190,223,116,253,190,109,46,0,191,3,161,1,191,45,18,3,191,230,129,4,191,44,240,5,191,250,92,7,191,76,200,8,191,30,50,10,191,108,154,11,191,50,1,13,191,108,102,14,191,23,202,15,191,45,44,17,
+ 191,172,140,18,191,144,235,19,191,213,72,21,191,118,164,22,191,113,254,23,191,192,86,25,191,98,173,26,191,81,2,28,191,138,85,29,191,9,167,30,191,203,246,31,191,204,68,33,191,9,145,34,191,124,219,35,191,36,36,37,191,253,106,38,191,2,176,39,191,48,243,40,191,132,52,42,191,250,115,43,191,143,177,44,191,63,237,45,191,7,39,47,191,227,94,48,191,208,148,49,191,202,200,50,191,206,250,51,191,218,42,53,191,232,88,54,191,247,132,55,191,2,175,56,191,7,215,57,191,3,253,58,191,241,32,60,191,207,66,61,191,154,98,
+ 62,191,79,128,63,191,233,155,64,191,104,181,65,191,198,204,66,191,1,226,67,191,23,245,68,191,3,6,70,191,196,20,71,191,86,33,72,191,182,43,73,191,225,51,74,191,212,57,75,191,141,61,76,191,9,63,77,191,68,62,78,191,61,59,79,191,240,53,80,191,90,46,81,191,121,36,82,191,74,24,83,191,202,9,84,191,247,248,84,191,206,229,85,191,77,208,86,191,112,184,87,191,55,158,88,191,156,129,89,191,160,98,90,191,62,65,91,191,117,29,92,191,65,247,92,191,162,206,93,191,148,163,94,191,20,118,95,191,34,70,96,191,186,19,97,
+ 191,217,222,97,191,127,167,98,191,169,109,99,191,84,49,100,191,126,242,100,191,38,177,101,191,73,109,102,191,229,38,103,191,248,221,103,191,128,146,104,191,123,68,105,191,232,243,105,191,195,160,106,191,12,75,107,191,192,242,107,191,222,151,108,191,100,58,109,191,80,218,109,191,160,119,110,191,83,18,111,191,102,170,111,191,217,63,112,191,169,210,112,191,213,98,113,191,91,240,113,191,58,123,114,191,113,3,115,191,253,136,115,191,222,11,116,191,17,140,116,191,150,9,117,191,107,132,117,191,143,252,117,
+ 191,0,114,118,191,189,228,118,191,198,84,119,191,24,194,119,191,178,44,120,191,147,148,120,191,187,249,120,191,40,92,121,191,217,187,121,191,205,24,122,191,2,115,122,191,121,202,122,191,47,31,123,191,36,113,123,191,88,192,123,191,201,12,124,191,118,86,124,191,95,157,124,191,130,225,124,191,224,34,125,191,119,97,125,191,71,157,125,191,79,214,125,191,142,12,126,191,4,64,126,191,176,112,126,191,146,158,126,191,169,201,126,191,245,241,126,191,117,23,127,191,41,58,127,191,16,90,127,191,43,119,127,191,
+ 120,145,127,191,248,168,127,191,170,189,127,191,143,207,127,191,165,222,127,191,237,234,127,191,102,244,127,191,17,251,127,191,237,254,127,191,234,255,127,63,229,248,127,63,166,230,127,63,45,201,127,63,124,160,127,63,149,108,127,63,121,45,127,63,44,227,126,63,177,141,126,63,11,45,126,63,63,193,125,63,82,74,125,63,72,200,124,63,40,59,124,63,247,162,123,63,189,255,122,63,128,81,122,63,72,152,121,63,30,212,120,63,9,5,120,63,19,43,119,63,70,70,118,63,172,86,117,63,78,92,116,63,56,87,115,63,118,71,114,
+ 63,19,45,113,63,28,8,112,63,158,216,110,63,165,158,109,63,64,90,108,63,126,11,107,63,107,178,105,63,25,79,104,63,150,225,102,63,242,105,101,63,62,232,99,63,139,92,98,63,234,198,96,63,109,39,95,63,38,126,93,63,40,203,91,63,133,14,90,63,83,72,88,63,163,120,86,63,139,159,84,63,32,189,82,63,118,209,80,63,163,220,78,63,189,222,76,63,219,215,74,63,19,200,72,63,124,175,70,63,46,142,68,63,65,100,66,63,206,49,64,63,236,246,61,63,180,179,59,63,66,104,57,63,173,20,55,63,16,185,52,63,134,85,50,63,41,234,47,63,
+ 21,119,45,63,101,252,42,63,53,122,40,63,161,240,37,63,198,95,35,63,192,199,32,63,172,40,30,63,169,130,27,63,212,213,24,63,74,34,22,63,42,104,19,63,147,167,16,63,164,224,13,63,123,19,11,63,57,64,8,63,253,102,5,63,231,135,2,63,45,70,255,62,91,113,249,62,151,145,243,62,36,167,237,62,69,178,231,62,60,179,225,62,76,170,219,62,186,151,213,62,201,123,207,62,190,86,201,62,223,40,195,62,112,242,188,62,183,179,182,62,251,108,176,62,129,30,170,62,146,200,163,62,115,107,157,62,108,7,151,62,197,156,144,62,199,
+ 43,138,62,185,180,131,62,199,111,122,62,33,107,109,62,17,92,96,62,41,67,83,62,253,32,70,62,32,246,56,62,38,195,43,62,164,136,30,62,45,71,17,62,87,255,3,62,110,99,237,61,194,189,210,61,218,14,184,61,222,87,157,61,251,153,130,61,188,172,79,61,101,28,26,61,153,10,201,60,42,167,59,60,193,120,214,186,45,68,113,188,87,215,227,188,76,129,39,189,148,15,93,189,21,74,137,189,90,6,164,189,109,187,190,189,34,104,217,189,78,11,244,189,227,81,7,190,47,152,20,190,247,215,33,190,165,16,47,190,166,65,60,190,100,106,
+ 73,190,77,138,86,190,205,160,99,190,80,173,112,190,69,175,125,190,13,83,133,190,158,200,139,190,13,56,146,190,18,161,152,190,102,3,159,190,191,94,165,190,216,178,171,190,105,255,177,190,43,68,184,190,216,128,190,190,42,181,196,190,219,224,202,190,165,3,209,190,69,29,215,190,117,45,221,190,241,51,227,190,118,48,233,190,192,34,239,190,141,10,245,190,155,231,250,190,211,92,0,191,56,64,3,191,219,29,6,191,155,245,8,191,90,199,11,191,247,146,14,191,84,88,17,191,80,23,20,191,205,207,22,191,172,129,25,191,
+ 208,44,28,191,26,209,30,191,109,110,33,191,171,4,36,191,183,147,38,191,116,27,41,191,199,155,43,191,147,20,46,191,187,133,48,191,38,239,50,191,183,80,53,191,85,170,55,191,227,251,57,191,74,69,60,191,110,134,62,191,55,191,64,191,139,239,66,191,83,23,69,191,117,54,71,191,218,76,73,191,107,90,75,191,16,95,77,191,179,90,79,191,62,77,81,191,154,54,83,191,179,22,85,191,114,237,86,191,197,186,88,191,149,126,90,191,208,56,92,191,98,233,93,191,56,144,95,191,64,45,97,191,103,192,98,191,156,73,100,191,206,200,
+ 101,191,235,61,103,191,227,168,104,191,167,9,106,191,39,96,107,191,84,172,108,191,31,238,109,191,122,37,111,191,88,82,112,191,171,116,113,191,103,140,114,191,127,153,115,191,231,155,116,191,149,147,117,191,126,128,118,191,150,98,119,191,212,57,120,191,47,6,121,191,158,199,121,191,23,126,122,191,148,41,123,191,13,202,123,191,122,95,124,191,213,233,124,191,24,105,125,191,62,221,125,191,64,70,126,191,28,164,126,191,204,246,126,191,77,62,127,191,156,122,127,191,182,171,127,191,153,209,127,191,67,236,
+ 127,191,180,251,127,191,166,255,127,63,148,227,127,63,156,154,127,63,204,36,127,63,56,130,126,63,253,178,125,63,63,183,124,63,42,143,123,63,243,58,122,63,212,186,120,63,17,15,119,63,246,55,117,63,213,53,115,63,8,9,113,63,241,177,110,63,249,48,108,63,144,134,105,63,47,179,102,63,83,183,99,63,132,147,96,63,78,72,93,63,69,214,89,63,3,62,86,63,43,128,82,63,101,157,78,63,94,150,74,63,204,107,70,63,106,30,66,63,249,174,61,63,64,30,57,63,13,109,52,63,50,156,47,63,135,172,42,63,235,158,37,63,63,116,32,63,
+ 109,45,27,63,97,203,21,63,13,79,16,63,104,185,10,63,107,11,5,63,46,140,254,62,221,212,242,62,241,242,230,62,127,232,218,62,166,183,206,62,136,98,194,62,78,235,181,62,42,84,169,62,81,159,156,62,253,206,143,62,109,229,130,62,206,201,107,62,98,159,81,62,48,80,55,62,211,224,28,62,241,85,2,62,98,104,207,61,124,0,154,61,36,251,72,61,27,164,187,60,243,119,86,187,100,61,241,188,187,192,99,189,103,93,167,189,20,189,220,189,3,251,8,190,115,127,35,190,52,231,61,190,164,45,88,190,38,78,114,190,18,34,134,190,
+ 137,5,147,190,52,207,159,190,213,124,172,190,51,12,185,190,26,123,197,190,91,199,209,190,205,238,221,190,80,239,233,190,199,198,245,190,144,185,0,191,38,121,6,191,36,33,12,191,141,176,17,191,102,38,23,191,186,129,28,191,152,193,33,191,21,229,38,191,74,235,43,191,86,211,48,191,91,156,53,191,131,69,58,191,253,205,62,191,252,52,67,191,188,121,71,191,125,155,75,191,132,153,79,191,31,115,83,191,161,39,87,191,99,182,90,191,198,30,94,191,48,96,97,191,15,122,100,191,216,107,103,191,7,53,106,191,31,213,108,
+ 191,169,75,111,191,55,152,113,191,98,186,115,191,201,177,117,191,22,126,119,191,246,30,121,191,33,148,122,191,85,221,123,191,89,250,124,191,250,234,125,191,14,175,126,191,116,70,127,191,15,177,127,191,206,238,127,191,0,0,0,0,0,0,0,0,3,0,0,0,2,0,0,0,3,0,0,0,2,0,0,0,5,0,0,0,2,0,0,0,3,0,0,0,2,0,0,0,3,0,0,0,2,0,0,0,5,0,0,0,2,0,0,0,3,0,0,0,2,0,0,0,0,0,206,64,0,0,200,64,0,0,184,64,0,0,170,64,0,0,162,64,0,0,154,64,0,0,144,64,0,0,140,64,0,0,156,64,0,0,150,64,0,0,146,64,0,0,142,64,0,0,156,64,0,0,148,64,0,
+ 0,138,64,0,0,144,64,0,0,140,64,0,0,148,64,0,0,152,64,0,0,142,64,0,0,112,64,0,0,112,64,0,0,112,64,0,0,112,64,0,0,112,64,0,134,107,63,0,20,46,63,0,112,189,62,0,208,76,62,0,0,102,63,0,0,76,63,0,0,38,63,0,0,0,63,15,0,0,0,10,0,0,0,5,0,0,0,6,0,0,0,4,0,0,0,3,0,0,0,191,104,0,0,199,104,0,0,215,104,0,0,247,104,0,0,255,104,0,0,15,105,0,0,47,105,0,0,87,105,0,0,167,105,0,0,71,106,0,0,79,106,0,0,95,106,0,0,32,0,10,0,20,46,100,1,130,106,0,0,194,107,0,0,2,108,0,0,20,108,0,0,180,108,0,0,252,108,0,0,134,95,0,0,32,
+ 0,16,0,102,38,171,1,68,109,0,0,68,111,0,0,132,111,0,0,162,111,0,0,162,112,0,0,234,112,0,0,156,95,0,0,0,0,0,0,64,31,0,0,184,36,0,0,236,44,0,0,188,52,0,0,92,68,0,0,168,97,0,0,128,56,1,0,0,0,0,0,40,35,0,0,224,46,0,0,164,56,0,0,68,72,0,0,180,95,0,0,172,138,0,0,128,56,1,0,0,0,0,0,4,41,0,0,176,54,0,0,104,66,0,0,252,83,0,0,84,111,0,0,16,164,0,0,128,56,1,0,77,113,0,0,80,113,0,0,10,103,242,14,86,205,228,29,10,103,242,14,117,82,130,12,89,154,4,25,117,82,130,12,70,17,49,10,237,3,98,20,70,17,49,10,218,2,215,
+ 7,249,198,173,15,218,2,215,7,34,182,82,5,218,250,164,10,34,182,82,5,70,243,46,30,43,227,75,14,31,102,128,24,28,44,29,10,218,97,72,18,237,156,244,6,236,48,19,11,227,144,165,4,237,164,29,2,10,223,107,3,48,117,0,0,112,23,0,0,32,209,255,255,32,209,255,255,0,64,0,0,108,34,0,0,66,15,0,0,18,6,0,0,77,2,0,0,219,0,0,0,237,0,0,0,153,0,0,0,73,0,0,0,30,0,0,0,12,0,0,0,7,0,0,0,0,64,0,0,147,93,0,0,189,112,0,0,237,121,0,0,178,125,0,0,36,127,0,0,0,250,0,0,128,62,0,0,160,140,0,0,128,62,0,0,248,42,0,0,232,3,0,0,176,
+ 54,0,0,232,3,0,0,8,82,0,0,208,7,0,0,96,109,0,0,208,7,0,0,224,46,0,0,232,3,0,0,80,70,0,0,208,7,0,0,8,82,0,0,208,7,0,0,48,117,0,0,208,7,0,0,248,42,0,0,232,3,0,0,176,54,0,0,232,3,0,0,104,66,0,0,232,3,0,0,8,82,0,0,208,7,0,0,224,46,0,0,232,3,0,0,152,58,0,0,232,3,0,0,80,70,0,0,208,7,0,0,240,85,0,0,208,7,0,0,230,90,52,56,119,78,51,57,211,217,201,57,146,145,51,58,204,96,140,58,97,251,201,58,153,126,9,59,203,128,51,59,213,37,99,59,119,46,140,59,168,138,169,59,69,184,201,59,135,166,236,59,232,46,9,60,174,102,
+ 29,60,247,2,51,60,147,255,73,60,79,88,98,60,94,17,124,60,46,145,139,60,189,199,153,60,92,172,168,60,243,60,184,60,129,121,200,60,238,95,217,60,57,240,234,60,99,42,253,60,53,7,8,61,16,204,17,61,205,228,27,61,97,80,38,61,203,14,49,61,0,31,60,61,254,128,71,61,198,52,83,61,63,56,95,61,105,139,107,61,69,46,120,61,105,144,130,61,123,48,137,61,224,247,143,61,138,229,150,61,123,249,157,61,177,51,165,61,33,147,172,61,80,24,180,61,51,194,187,61,79,145,195,61,18,132,203,61,2,155,211,61,31,214,219,61,215,51,
+ 228,61,175,180,236,61,33,88,245,61,168,29,254,61,161,130,3,62,242,6,8,62,199,155,12,62,221,64,17,62,52,246,21,62,69,187,26,62,17,144,31,62,84,116,36,62,203,103,41,62,51,106,46,62,141,123,51,62,82,155,56,62,197,201,61,62,28,6,67,62,89,80,72,62,122,168,77,62,183,13,83,62,82,128,88,62,8,0,94,62,84,140,99,62,242,36,105,62,37,202,110,62,36,123,116,62,172,55,122,62,0,0,128,62,171,233,130,62,249,216,133,62,133,205,136,62,80,199,139,62,55,198,142,62,247,201,145,62,179,210,148,62,38,224,151,62,15,242,154,
+ 62,108,8,158,62,28,35,161,62,255,65,164,62,208,100,167,62,177,139,170,62,28,182,173,62,84,228,176,62,211,21,180,62,186,74,183,62,232,130,186,62,249,189,189,62,13,252,192,62,226,60,196,62,86,128,199,62,71,198,202,62,149,14,206,62,251,88,209,62,122,165,212,62,241,243,215,62,28,68,219,62,217,149,222,62,8,233,225,62,167,61,229,62,83,147,232,62,12,234,235,62,175,65,239,62,28,154,242,62,14,243,245,62,136,76,249,62,34,166,252,62,0,0,0,63,239,172,1,63,188,89,3,63,121,6,5,63,242,178,6,63,41,95,8,63,250,10,
+ 10,63,86,182,11,63,44,97,13,63,124,11,15,63,19,181,16,63,242,93,18,63,8,6,20,63,67,173,21,63,130,83,23,63,182,248,24,63,220,156,26,63,213,63,28,63,143,225,29,63,249,129,31,63,4,33,33,63,140,190,34,63,163,90,36,63,23,245,37,63,214,141,39,63,242,36,41,63,40,186,42,63,152,77,44,63,1,223,45,63,114,110,47,63,202,251,48,63,249,134,50,63,237,15,52,63,167,150,53,63,4,27,55,63,229,156,56,63,88,28,58,63,61,153,59,63,131,19,61,63,42,139,62,63,0,0,64,63,21,114,65,63,55,225,66,63,119,77,68,63,195,182,69,63,235,
+ 28,71,63,254,127,72,63,236,223,73,63,146,60,75,63,225,149,76,63,234,235,77,63,121,62,79,63,143,141,80,63,43,217,81,63,29,33,83,63,115,101,84,63,13,166,85,63,235,226,86,63,252,27,88,63,47,81,89,63,115,130,90,63,201,175,91,63,14,217,92,63,67,254,93,63,88,31,95,63,75,60,96,63,252,84,97,63,106,105,98,63,133,121,99,63,60,133,100,63,160,140,101,63,126,143,102,63,214,141,103,63,186,135,104,63,246,124,105,63,156,109,106,63,138,89,107,63,209,64,108,63,79,35,109,63,4,1,110,63,241,217,110,63,243,173,111,63,
+ 28,125,112,63,73,71,113,63,124,12,114,63,180,204,114,63,240,135,115,63,16,62,116,63,19,239,116,63,250,154,117,63,179,65,118,63,63,227,118,63,141,127,119,63,173,22,120,63,126,168,120,63,1,53,121,63,52,188,121,63,24,62,122,63,157,186,122,63,194,49,123,63,119,163,123,63,187,15,124,63,159,118,124,63,2,216,124,63,244,51,125,63,101,138,125,63,68,219,125,63,179,38,126,63,143,108,126,63,235,172,126,63,163,231,126,63,218,28,127,63,127,76,127,63,129,118,127,63,2,155,127,63,208,185,127,63,28,211,127,63,197,
+ 230,127,63,203,244,127,63,47,253,127,63,0,0,128,63,2,0,0,0,4,0,0,0,6,0,0,0,8,0,0,0,10,0,0,0,12,0,0,0,14,0,0,0,16,0,0,0,20,0,0,0,24,0,0,0,28,0,0,0,32,0,0,0,40,0,0,0,48,0,0,0,56,0,0,0,68,0,0,0,80,0,0,0,96,0,0,0,120,0,0,0,1,0,0,0,2,0,0,0,4,0,0,0,6,0,0,0,8,0,0,0,10,0,0,0,12,0,0,0,14,0,0,0,16,0,0,0,20,0,0,0,24,0,0,0,28,0,0,0,32,0,0,0,40,0,0,0,48,0,0,0,56,0,0,0,68,0,0,0,80,0,0,0,96,0,0,0,120,0,0,0,160,0,0,0,200,0,0,0,0,0,128,62,0,0,128,62,0,0,128,62,0,0,128,62,0,0,128,62,0,0,128,62,0,0,128,62,0,0,128,62,
+ 0,0,128,62,0,0,128,62,0,0,128,62,0,0,128,62,0,0,128,62,0,0,128,62,0,0,128,62,0,0,128,62,208,37,180,62,151,57,173,62,9,165,159,62,250,237,139,62,205,172,101,62,248,169,42,62,52,48,210,61,90,241,13,61,90,241,13,189,52,48,210,189,248,169,42,190,205,172,101,190,250,237,139,190,9,165,159,190,151,57,173,190,208,37,180,190,135,138,177,62,27,131,150,62,96,35,73,62,196,66,141,61,196,66,141,189,96,35,73,190,27,131,150,190,135,138,177,190,135,138,177,190,27,131,150,190,96,35,73,190,196,66,141,189,196,66,141,
+ 61,96,35,73,62,27,131,150,62,135,138,177,62,151,57,173,62,205,172,101,62,90,241,13,61,248,169,42,190,9,165,159,190,208,37,180,190,250,237,139,190,52,48,210,189,52,48,210,61,250,237,139,62,208,37,180,62,9,165,159,62,248,169,42,62,90,241,13,189,205,172,101,190,151,57,173,190,125,61,167,62,210,139,10,62,210,139,10,190,125,61,167,190,125,61,167,190,210,139,10,190,210,139,10,62,125,61,167,62,125,61,167,62,210,139,10,62,210,139,10,190,125,61,167,190,125,61,167,190,210,139,10,190,210,139,10,62,125,61,167,
+ 62,9,165,159,62,90,241,13,61,250,237,139,190,151,57,173,190,52,48,210,189,205,172,101,62,208,37,180,62,248,169,42,62,248,169,42,190,208,37,180,190,205,172,101,190,52,48,210,61,151,57,173,62,250,237,139,62,90,241,13,189,9,165,159,190,27,131,150,62,196,66,141,189,135,138,177,190,96,35,73,190,96,35,73,62,135,138,177,62,196,66,141,61,27,131,150,190,27,131,150,190,196,66,141,61,135,138,177,62,96,35,73,62,96,35,73,190,135,138,177,190,196,66,141,189,27,131,150,62,250,237,139,62,248,169,42,190,151,57,173,
+ 190,90,241,13,61,208,37,180,62,52,48,210,61,9,165,159,190,205,172,101,190,205,172,101,62,9,165,159,62,52,48,210,189,208,37,180,190,90,241,13,189,151,57,173,62,248,169,42,62,250,237,139,190,0,0,0,0,5,193,35,61,233,125,163,61,37,150,244,61,226,116,34,62,172,28,74,62,221,37,113,62,52,186,139,62,180,119,158,62,228,191,176,62,173,136,194,62,37,201,211,62,24,122,228,62,24,149,244,62,200,10,2,63,28,124,9,63,73,157,16,63,202,109,23,63,192,237,29,63,159,29,36,63,84,254,41,63,46,145,47,63,224,215,52,63,99,
+ 212,57,63,240,136,62,63,211,247,66,63,171,35,71,63,23,15,75,63,216,188,78,63,173,47,82,63,106,106,85,63,206,111,88,63,154,66,91,63,142,229,93,63,75,91,96,63,110,166,98,63,100,201,100,63,155,198,102,63,111,160,104,63,247,88,106,63,128,242,107,63,223,110,109,63,11,208,110,63,202,23,112,63,224,71,113,63,225,97,114,63,77,103,115,63,150,89,116,63,12,58,117,63,255,9,118,63,138,202,118,63,187,124,119,63,192,33,120,63,98,186,120,63,157,71,121,63,75,202,121,63,36,67,122,63,242,178,122,63,59,26,123,63,200,
+ 121,123,63,32,210,123,63,200,35,124,63,55,111,124,63,242,180,124,63,94,245,124,63,224,48,125,63,236,103,125,63,183,154,125,63,180,201,125,63,6,245,125,63,17,29,126,63,24,66,126,63,78,100,126,63,211,131,126,63,253,160,126,63,237,187,126,63,195,212,126,63,179,235,126,63,239,0,127,63,135,20,127,63,141,38,127,63,67,55,127,63,170,70,127,63,227,84,127,63,15,98,127,63,47,110,127,63,100,121,127,63,190,131,127,63,63,141,127,63,24,150,127,63,56,158,127,63,194,165,127,63,163,172,127,63,16,179,127,63,245,184,
+ 127,63,119,190,127,63,114,195,127,63,25,200,127,63,108,204,127,63,91,208,127,63,6,212,127,63,111,215,127,63,131,218,127,63,102,221,127,63,21,224,127,63,130,226,127,63,205,228,127,63,230,230,127,63,205,232,127,63,146,234,127,63,70,236,127,63,200,237,127,63,40,239,127,63,120,240,127,63,166,241,127,63,195,242,127,63,191,243,127,63,186,244,127,63,148,245,127,63,94,246,127,63,39,247,127,63,207,247,127,63,119,248,127,63,253,248,127,63,148,249,127,63,9,250,127,63,127,250,127,63,244,250,127,63,89,251,127,
+ 63,173,251,127,63,1,252,127,63,84,252,127,63,152,252,127,63,219,252,127,63,30,253,127,63,80,253,127,63,130,253,127,63,181,253,127,63,231,253,127,63,9,254,127,63,59,254,127,63,93,254,127,63,126,254,127,63,143,254,127,63,176,254,127,63,210,254,127,63,227,254,127,63,244,254,127,63,21,255,127,63,38,255,127,63,55,255,127,63,71,255,127,63,88,255,127,63,88,255,127,63,105,255,127,63,122,255,127,63,122,255,127,63,139,255,127,63,155,255,127,63,155,255,127,63,155,255,127,63,172,255,127,63,172,255,127,63,189,
+ 255,127,63,189,255,127,63,189,255,127,63,206,255,127,63,206,255,127,63,206,255,127,63],"i8",4,y.h+10240);
+ O([206,255,127,63,206,255,127,63,222,255,127,63,222,255,127,63,222,255,127,63,222,255,127,63,222,255,127,63,222,255,127,63,239,255,127,63,239,255,127,63,239,255,127,63,239,255,127,63,239,255,127,63,239,255,127,63,239,255,127,63,239,255,127,63,239,255,127,63,239,255,127,63,239,255,127,63,239,255,127,63,239,255,127,63,0,0,128,63,0,0,128,63,0,0,128,63,0,0,128,63,0,0,128,63,0,0,128,63,0,0,128,63,0,0,128,63,0,0,128,63,0,0,128,63,0,0,128,63,3,0,0,0,148,80,0,0,160,80,0,0,25,0,0,0,15,0,0,0,2,0,0,0,14,190,
+ 192,189,172,31,155,190,149,130,26,191,150,149,70,190,84,114,62,190,146,3,26,191,6,152,62,189,2,160,234,189,182,43,212,189,185,114,30,191,106,190,162,190,28,7,46,190,107,243,143,189,90,158,23,62,33,173,209,62,10,102,12,63,125,60,188,62,20,33,253,190,143,169,67,63,8,119,235,191,10,243,46,62,117,147,76,65,80,83,139,191,108,236,162,191,181,21,130,193,28,107,193,65,162,98,178,192,255,231,48,190,47,79,39,190,158,206,101,190,255,87,194,189,155,60,149,189,203,248,135,190,44,97,205,189,203,33,83,189,64,166,
+ 21,190,238,35,247,189,160,253,56,190,219,167,3,62,233,95,226,62,213,202,252,190,29,203,43,62,231,168,83,62,1,79,74,190,247,3,214,62,71,119,192,63,173,249,69,191,64,164,32,193,43,194,205,62,192,178,62,64,201,118,115,65,100,204,241,191,39,165,152,191,23,204,233,60,134,193,132,187,201,232,144,61,84,72,7,60,154,231,189,189,103,71,42,188,59,137,140,187,159,122,160,187,88,90,145,189,85,196,39,187,169,11,34,61,177,219,103,62,241,54,5,61,52,17,38,62,170,10,205,189,86,185,248,62,108,4,2,62,86,102,146,62,228,
+ 254,126,60,106,251,215,61,159,142,67,64,136,70,147,63,57,40,129,191,71,90,234,191,139,84,84,64,210,53,91,192,13,253,243,189,232,39,38,189,25,31,226,59,241,90,147,60,171,170,28,189,237,238,195,59,5,106,150,188,246,141,249,58,37,201,19,190,106,115,50,189,210,214,129,58,161,100,98,62,158,210,17,62,128,215,247,62,221,12,207,62,124,15,3,63,250,242,114,190,55,139,119,62,47,110,179,62,183,13,51,191,136,99,38,65,18,165,41,64,83,208,27,192,53,7,134,192,125,150,135,63,60,247,218,63,12,212,218,59,186,186,147,
+ 189,191,192,34,189,69,144,20,61,38,112,235,189,208,37,193,188,210,156,6,60,124,58,104,188,114,11,7,189,31,26,17,189,171,204,53,59,154,208,148,190,218,230,146,191,140,104,163,190,89,193,47,191,163,233,188,62,64,50,245,62,253,245,58,62,163,119,210,190,8,144,97,63,39,107,147,192,33,31,188,63,224,243,171,62,161,214,232,191,245,91,241,193,8,172,177,64,252,177,255,58,106,21,253,189,37,245,148,189,41,102,131,189,252,233,90,189,35,134,221,189,20,249,191,189,43,237,142,189,75,171,225,188,167,236,68,190,122,
+ 110,225,189,172,28,146,62,105,170,207,190,7,203,189,61,35,101,147,190,201,231,89,191,252,194,203,189,212,95,111,190,111,129,164,191,13,108,145,63,155,201,71,64,187,39,143,189,66,91,238,191,113,201,41,64,120,238,233,192,26,168,28,64,135,138,146,186,54,152,129,189,127,33,26,189,138,114,25,190,229,100,18,62,247,202,60,62,113,202,252,61,117,220,154,61,70,65,240,61,200,40,191,61,71,193,141,61,22,144,172,61,175,81,144,61,27,166,113,61,173,246,192,61,61,209,229,190,92,47,215,60,148,107,138,62,106,78,134,
+ 190,98,186,48,62,49,37,0,64,133,9,35,190,99,96,29,61,26,81,35,65,182,248,132,64,7,206,21,192,120,99,97,189,79,18,30,60,98,186,16,190,8,223,224,60,187,222,12,61,136,166,71,189,97,152,194,61,35,245,253,187,158,146,24,189,185,155,179,187,187,236,135,189,45,182,196,61,230,206,76,190,12,24,41,189,251,87,22,63,48,68,83,61,142,172,172,62,218,226,90,63,93,26,43,63,202,82,235,189,178,75,104,192,37,89,239,190,177,164,92,190,57,98,39,64,145,238,207,62,180,142,174,191,203,61,46,61,20,5,250,61,210,98,191,61,67,
+ 4,252,61,160,165,11,61,155,226,17,190,245,130,15,61,15,250,72,189,55,41,150,61,113,52,108,61,83,235,253,61,185,215,83,189,147,139,129,190,69,47,23,63,113,89,21,62,238,95,161,62,207,217,98,62,177,168,24,190,79,89,93,62,127,251,178,190,253,135,196,65,161,131,126,191,11,66,29,63,242,82,150,193,27,76,53,192,69,128,55,191,84,196,177,190,253,130,245,62,128,238,123,190,215,96,155,61,137,150,12,62,211,19,54,190,185,51,243,61,46,253,141,186,175,7,115,190,129,34,182,62,33,7,5,190,218,78,96,189,101,28,163,190,
+ 21,171,166,190,107,211,56,62,171,31,128,189,183,155,16,62,40,41,176,62,24,207,192,62,95,126,23,191,102,247,186,64,170,241,194,190,46,56,99,62,239,172,181,191,48,108,229,201,122,170,171,63,218,31,232,60,27,113,55,189,162,59,173,188,127,121,210,188,9,192,100,60,236,86,170,60,101,102,48,188,198,207,53,60,202,13,112,61,62,180,207,188,178,134,6,189,121,35,243,61,78,38,94,190,247,62,21,62,230,93,245,61,106,111,187,189,198,21,247,189,41,83,161,189,106,23,19,190,134,89,24,191,188,116,147,191,198,109,160,
+ 191,181,224,149,191,42,227,138,64,64,26,110,201,249,102,175,191,204,76,36,189,13,168,87,62,141,239,11,190,159,57,11,62,64,87,86,189,28,28,54,61,199,207,107,60,239,56,135,59,170,27,158,188,226,177,95,62,162,178,225,189,236,163,1,192,165,17,107,63,28,8,29,192,134,3,153,63,184,86,123,189,48,18,246,191,186,192,157,62,172,202,254,62,42,144,105,63,102,75,86,62,147,24,22,192,95,94,12,64,39,20,207,192,144,78,217,63,169,161,57,191,112,218,66,60,77,206,26,61,109,235,98,61,109,130,185,60,243,67,144,189,93,3,
+ 246,188,182,124,73,60,72,233,136,187,62,158,140,189,125,64,0,61,219,50,32,61,194,108,186,62,242,165,193,189,126,80,188,60,194,81,50,190,228,218,168,62,44,239,234,61,112,182,153,62,62,33,219,61,18,136,7,62,8,148,185,64,125,118,104,63,80,195,103,191,88,202,86,192,248,56,67,62,207,161,60,62,50,116,44,191,208,94,109,62,213,29,112,189,65,74,108,62,216,101,224,190,240,193,123,62,23,72,48,190,182,123,179,61,121,115,56,191,85,106,38,62,85,187,139,60,143,114,208,61,117,230,198,62,213,38,170,63,2,241,138,63,
+ 108,177,111,191,51,167,23,192,66,9,215,192,144,102,92,192,241,215,8,64,116,181,99,65,82,68,157,64,20,203,69,192,16,18,27,193,252,170,68,191,164,228,229,63,75,35,97,61,17,82,39,62,16,59,163,61,253,223,12,61,211,175,99,189,237,178,165,187,217,102,153,60,110,201,5,61,34,162,189,60,175,119,31,62,154,15,67,61,75,120,130,190,151,255,204,63,210,28,77,191,119,132,35,64,65,213,60,63,19,102,174,191,221,9,50,191,71,90,28,192,62,174,221,191,131,250,124,64,205,1,242,63,101,224,248,62,75,89,53,193,128,147,112,
+ 74,249,75,195,190,126,29,248,61,94,44,104,191,249,20,60,64,51,196,209,63,231,255,97,63,2,213,95,63,45,207,155,63,46,226,95,191,166,182,164,62,93,249,72,63,160,81,114,63,134,55,19,191,62,203,93,192,34,137,98,63,173,62,189,61,144,131,30,193,116,93,200,62,10,242,35,62,170,43,3,192,240,167,132,64,210,22,140,61,58,60,20,190,123,16,146,190,69,44,194,62,116,70,148,191,167,29,227,188,154,153,29,193,16,93,154,192,51,167,109,64,139,224,119,64,26,163,97,64,0,64,202,69,27,76,255,82,130,90,179,98,162,107,96,117,
+ 0,0,1,0,2,0,3,0,4,0,5,0,6,0,7,0,8,0,10,0,12,0,14,0,16,0,20,0,24,0,28,0,34,0,40,0,48,0,60,0,78,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,8,0,8,0,8,0,16,0,16,0,16,0,21,0,21,0,24,0,29,0,34,0,36,0,0,0,96,0,192,0,32,1,128,1,32,0,128,0,224,0,64,1,160,1,64,0,160,0,0,1,96,1,192,1,8,0,104,0,200,0,40,1,136,1,40,0,136,0,232,0,72,1,168,1,72,0,168,0,8,1,104,1,200,1,16,0,112,0,208,0,48,1,144,1,48,0,144,0,240,0,80,1,176,1,80,0,176,0,16,1,112,1,208,1,24,0,120,0,216,0,56,1,152,1,56,0,152,0,248,0,88,1,184,1,88,0,
+ 184,0,24,1,120,1,216,1,4,0,100,0,196,0,36,1,132,1,36,0,132,0,228,0,68,1,164,1,68,0,164,0,4,1,100,1,196,1,12,0,108,0,204,0,44,1,140,1,44,0,140,0,236,0,76,1,172,1,76,0,172,0,12,1,108,1,204,1,20,0,116,0,212,0,52,1,148,1,52,0,148,0,244,0,84,1,180,1,84,0,180,0,20,1,116,1,212,1,28,0,124,0,220,0,60,1,156,1,60,0,156,0,252,0,92,1,188,1,92,0,188,0,28,1,124,1,220,1,1,0,97,0,193,0,33,1,129,1,33,0,129,0,225,0,65,1,161,1,65,0,161,0,1,1,97,1,193,1,9,0,105,0,201,0,41,1,137,1,41,0,137,0,233,0,73,1,169,1,73,0,169,
+ 0,9,1,105,1,201,1,17,0,113,0,209,0,49,1,145,1,49,0,145,0,241,0,81,1,177,1,81,0,177,0,17,1,113,1,209,1,25,0,121,0,217,0,57,1,153,1,57,0,153,0,249,0,89,1,185,1,89,0,185,0,25,1,121,1,217,1,5,0,101,0,197,0,37,1,133,1,37,0,133,0,229,0,69,1,165,1,69,0,165,0,5,1,101,1,197,1,13,0,109,0,205,0,45,1,141,1,45,0,141,0,237,0,77,1,173,1,77,0,173,0,13,1,109,1,205,1,21,0,117,0,213,0,53,1,149,1,53,0,149,0,245,0,85,1,181,1,85,0,181,0,21,1,117,1,213,1,29,0,125,0,221,0,61,1,157,1,61,0,157,0,253,0,93,1,189,1,93,0,189,
+ 0,29,1,125,1,221,1,2,0,98,0,194,0,34,1,130,1,34,0,130,0,226,0,66,1,162,1,66,0,162,0,2,1,98,1,194,1,10,0,106,0,202,0,42,1,138,1,42,0,138,0,234,0,74,1,170,1,74,0,170,0,10,1,106,1,202,1,18,0,114,0,210,0,50,1,146,1,50,0,146,0,242,0,82,1,178,1,82,0,178,0,18,1,114,1,210,1,26,0,122,0,218,0,58,1,154,1,58,0,154,0,250,0,90,1,186,1,90,0,186,0,26,1,122,1,218,1,6,0,102,0,198,0,38,1,134,1,38,0,134,0,230,0,70,1,166,1,70,0,166,0,6,1,102,1,198,1,14,0,110,0,206,0,46,1,142,1,46,0,142,0,238,0,78,1,174,1,78,0,174,0,14,
+ 1,110,1,206,1,22,0,118,0,214,0,54,1,150,1,54,0,150,0,246,0,86,1,182,1,86,0,182,0,22,1,118,1,214,1,30,0,126,0,222,0,62,1,158,1,62,0,158,0,254,0,94,1,190,1,94,0,190,0,30,1,126,1,222,1,3,0,99,0,195,0,35,1,131,1,35,0,131,0,227,0,67,1,163,1,67,0,163,0,3,1,99,1,195,1,11,0,107,0,203,0,43,1,139,1,43,0,139,0,235,0,75,1,171,1,75,0,171,0,11,1,107,1,203,1,19,0,115,0,211,0,51,1,147,1,51,0,147,0,243,0,83,1,179,1,83,0,179,0,19,1,115,1,211,1,27,0,123,0,219,0,59,1,155,1,59,0,155,0,251,0,91,1,187,1,91,0,187,0,27,1,
+ 123,1,219,1,7,0,103,0,199,0,39,1,135,1,39,0,135,0,231,0,71,1,167,1,71,0,167,0,7,1,103,1,199,1,15,0,111,0,207,0,47,1,143,1,47,0,143,0,239,0,79,1,175,1,79,0,175,0,15,1,111,1,207,1,23,0,119,0,215,0,55,1,151,1,55,0,151,0,247,0,87,1,183,1,87,0,183,0,23,1,119,1,215,1,31,0,127,0,223,0,63,1,159,1,63,0,159,0,255,0,95,1,191,1,95,0,191,0,31,1,127,1,223,1,0,0,48,0,96,0,144,0,192,0,16,0,64,0,112,0,160,0,208,0,32,0,80,0,128,0,176,0,224,0,4,0,52,0,100,0,148,0,196,0,20,0,68,0,116,0,164,0,212,0,36,0,84,0,132,0,180,
+ 0,228,0,8,0,56,0,104,0,152,0,200,0,24,0,72,0,120,0,168,0,216,0,40,0,88,0,136,0,184,0,232,0,12,0,60,0,108,0,156,0,204,0,28,0,76,0,124,0,172,0,220,0,44,0,92,0,140,0,188,0,236,0,1,0,49,0,97,0,145,0,193,0,17,0,65,0,113,0,161,0,209,0,33,0,81,0,129,0,177,0,225,0,5,0,53,0,101,0,149,0,197,0,21,0,69,0,117,0,165,0,213,0,37,0,85,0,133,0,181,0,229,0,9,0,57,0,105,0,153,0,201,0,25,0,73,0,121,0,169,0,217,0,41,0,89,0,137,0,185,0,233,0,13,0,61,0,109,0,157,0,205,0,29,0,77,0,125,0,173,0,221,0,45,0,93,0,141,0,189,0,
+ 237,0,2,0,50,0,98,0,146,0,194,0,18,0,66,0,114,0,162,0,210,0,34,0,82,0,130,0,178,0,226,0,6,0,54,0,102,0,150,0,198,0,22,0,70,0,118,0,166,0,214,0,38,0,86,0,134,0,182,0,230,0,10,0,58,0,106,0,154,0,202,0,26,0,74,0,122,0,170,0,218,0,42,0,90,0,138,0,186,0,234,0,14,0,62,0,110,0,158,0,206,0,30,0,78,0,126,0,174,0,222,0,46,0,94,0,142,0,190,0,238,0,3,0,51,0,99,0,147,0,195,0,19,0,67,0,115,0,163,0,211,0,35,0,83,0,131,0,179,0,227,0,7,0,55,0,103,0,151,0,199,0,23,0,71,0,119,0,167,0,215,0,39,0,87,0,135,0,183,0,231,
+ 0,11,0,59,0,107,0,155,0,203,0,27,0,75,0,123,0,171,0,219,0,43,0,91,0,139,0,187,0,235,0,15,0,63,0,111,0,159,0,207,0,31,0,79,0,127,0,175,0,223,0,47,0,95,0,143,0,191,0,239,0,0,0,24,0,48,0,72,0,96,0,8,0,32,0,56,0,80,0,104,0,16,0,40,0,64,0,88,0,112,0,4,0,28,0,52,0,76,0,100,0,12,0,36,0,60,0,84,0,108,0,20,0,44,0,68,0,92,0,116,0,1,0,25,0,49,0,73,0,97,0,9,0,33,0,57,0,81,0,105,0,17,0,41,0,65,0,89,0,113,0,5,0,29,0,53,0,77,0,101,0,13,0,37,0,61,0,85,0,109,0,21,0,45,0,69,0,93,0,117,0,2,0,26,0,50,0,74,0,98,0,10,
+ 0,34,0,58,0,82,0,106,0,18,0,42,0,66,0,90,0,114,0,6,0,30,0,54,0,78,0,102,0,14,0,38,0,62,0,86,0,110,0,22,0,46,0,70,0,94,0,118,0,3,0,27,0,51,0,75,0,99,0,11,0,35,0,59,0,83,0,107,0,19,0,43,0,67,0,91,0,115,0,7,0,31,0,55,0,79,0,103,0,15,0,39,0,63,0,87,0,111,0,23,0,47,0,71,0,95,0,119,0,0,0,12,0,24,0,36,0,48,0,4,0,16,0,28,0,40,0,52,0,8,0,20,0,32,0,44,0,56,0,1,0,13,0,25,0,37,0,49,0,5,0,17,0,29,0,41,0,53,0,9,0,21,0,33,0,45,0,57,0,2,0,14,0,26,0,38,0,50,0,6,0,18,0,30,0,42,0,54,0,10,0,22,0,34,0,46,0,58,0,3,0,15,
+ 0,27,0,39,0,51,0,7,0,19,0,31,0,43,0,55,0,11,0,23,0,35,0,47,0,59,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,41,0,41,0,41,0,82,0,82,0,123,0,164,0,200,0,222,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,0,41,0,41,0,41,0,123,0,123,0,123,0,164,0,164,0,240,0,10,1,27,1,39,1,41,0,41,0,41,0,41,0,41,0,41,0,41,0,41,0,123,0,123,0,123,0,123,0,240,0,240,0,240,0,10,1,10,1,49,1,62,1,72,1,80,1,123,0,123,0,123,0,123,0,123,0,123,0,123,0,123,0,240,0,240,0,240,0,240,0,49,1,49,1,49,1,62,1,
+ 62,1,87,1,95,1,102,1,108,1,240,0,240,0,240,0,240,0,240,0,240,0,240,0,240,0,49,1,49,1,49,1,49,1,87,1,87,1,87,1,95,1,95,1,114,1,120,1,126,1,131,1,184,126,154,121,154,121,102,102,184,126,51,115,16,48,250,0,3,0,6,0,3,0,3,0,3,0,4,0,3,0,3,0,3,0,205,1,100,0,3,0,40,0,3,0,3,0,3,0,5,0,14,0,14,0,10,0,11,0,3,0,8,0,9,0,7,0,3,0,91,1,18,0,29,0,38,0,40,0,46,0,52,0,62,0,84,0,92,202,190,216,182,223,154,226,156,230,120,236,122,244,204,252,52,3,134,11,136,19,100,25,102,29,74,32,66,39,164,53,100,0,240,0,32,0,100,0,205,
+ 60,0,48,0,32,30,161,36,42,0,32,254,31,246,31,234,31,216,31,194,31,168,31,136,31,98,31,58,31,10,31,216,30,160,30,98,30,34,30,220,29,144,29,66,29,238,28,150,28,58,28,216,27,114,27,10,27,156,26,42,26,180,25,58,25,188,24,60,24,182,23,46,23,160,22,16,22,126,21,232,20,78,20,176,19,16,19,110,18,200,17,30,17,116,16,198,15,22,15,100,14,174,13,248,12,64,12,132,11,200,10,10,10,74,9,138,8,198,7,2,7,62,6,120,5,178,4,234,3,34,3,90,2,146,1,202,0,0,0,54,255,110,254,166,253,222,252,22,252,78,251,136,250,194,249,254,
+ 248,58,248,118,247,182,246,246,245,56,245,124,244,192,243,8,243,82,242,156,241,234,240,58,240,140,239,226,238,56,238,146,237,240,236,80,236,178,235,24,235,130,234,240,233,96,233,210,232,74,232,196,231,68,231,198,230,76,230,214,229,100,229,246,228,142,228,40,228,198,227,106,227,18,227,190,226,112,226,36,226,222,225,158,225,96,225,40,225,246,224,198,224,158,224,120,224,88,224,62,224,40,224,22,224,10,224,2,224,0,224,210,6,138,58,171,152,198,26,169,100,246,216,42,175,213,201,207,255,64,0,17,0,99,255,
+ 97,1,16,254,163,0,39,43,189,86,217,255,6,0,91,0,86,255,186,0,23,0,128,252,192,24,216,77,237,255,220,255,102,0,167,255,232,255,72,1,73,252,8,10,37,62,135,199,61,201,64,0,128,0,134,255,36,0,54,1,0,253,72,2,51,36,69,69,12,0,128,0,18,0,114,255,32,1,139,255,159,252,27,16,123,56,104,2,13,200,246,255,39,0,58,0,210,255,172,255,120,0,184,0,197,254,227,253,4,5,4,21,64,35,230,62,198,196,243,255,0,0,20,0,26,0,5,0,225,255,213,255,252,255,65,0,90,0,7,0,99,255,8,255,212,255,81,2,47,6,52,10,199,12,228,87,5,197,3,
+ 0,242,255,236,255,241,255,2,0,25,0,37,0,25,0,240,255,185,255,149,255,177,255,50,0,36,1,111,2,214,3,8,5,184,5,148,107,103,196,17,0,12,0,8,0,1,0,246,255,234,255,226,255,224,255,234,255,3,0,44,0,100,0,168,0,243,0,61,1,125,1,173,1,199,1,19,245,149,230,89,18,243,41,31,6,84,32,189,0,168,253,105,2,103,119,117,0,97,255,210,251,8,116,52,0,221,0,168,246,116,110,252,255,17,2,234,242,229,102,208,255,246,2,140,240,165,93,176,255,137,3,117,239,6,83,157,255,204,3,130,239,102,71,149,255,199,3,139,240,39,59,153,255,
+ 128,3,97,242,174,46,165,255,5,3,207,244,94,34,185,255,99,2,161,247,152,22,210,255,169,1,161,250,180,11,0,1,1,1,2,3,3,3,2,3,3,3,2,3,3,3,0,3,12,15,48,51,60,63,192,195,204,207,240,243,252,255,0,255,0,255,0,255,0,255,0,255,0,254,1,0,1,255,0,254,0,253,2,0,1,255,0,254,0,253,3,0,1,255,117,110,107,110,111,119,110,32,101,114,114,111,114,0,115,117,99,99,101,115,115,0,105,110,118,97,108,105,100,32,97,114,103,117,109,101,110,116,0,98,117,102,102,101,114,32,116,111,111,32,115,109,97,108,108,0,105,110,116,101,
+ 114,110,97,108,32,101,114,114,111,114,0,99,111,114,114,117,112,116,101,100,32,115,116,114,101,97,109,0,114,101,113,117,101,115,116,32,110,111,116,32,105,109,112,108,101,109,101,110,116,101,100,0,105,110,118,97,108,105,100,32,115,116,97,116,101,0,109,101,109,111,114,121,32,97,108,108,111,99,97,116,105,111,110,32,102,97,105,108,101,100,0,108,105,98,111,112,117,115,32,49,46,49,46,50,0,2,1,0,255,255,156,110,86,70,59,51,45,40,37,33,31,28,26,25,23,22,21,20,19,18,17,16,16,15,15,14,13,13,12,12,12,12,11,11,
+ 11,10,10,10,9,9,9,9,9,9,8,8,8,8,8,7,7,7,7,7,7,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,25,23,2,0,126,124,119,109,87,41,19,9,4,2,0,2,1,0,25,23,2,0,126,124,119,109,87,41,19,9,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,80,75,69,63,56,49,40,34,29,20,18,10,0,0,0,0,0,0,0,0,110,100,90,84,78,71,65,58,51,45,39,32,26,20,12,0,0,0,0,0,0,118,110,103,93,86,80,75,70,65,59,53,47,40,31,23,15,4,0,0,0,0,126,
+ 119,112,104,95,89,83,78,72,66,60,54,47,39,32,25,17,12,1,0,0,134,127,120,114,103,97,91,85,78,72,66,60,54,47,41,35,29,23,16,10,1,144,137,130,124,113,107,101,95,88,82,76,70,64,57,51,45,39,33,26,15,1,152,145,138,132,123,117,111,105,98,92,86,80,74,67,61,55,49,43,36,20,1,162,155,148,142,133,127,121,115,108,102,96,90,84,77,71,65,59,53,46,30,1,172,165,158,152,143,137,131,125,118,112,106,100,94,87,81,75,69,63,56,45,20,200,200,200,200,200,200,200,200,198,193,188,183,178,173,168,163,158,153,148,129,104,40,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,40,15,23,28,31,34,36,38,39,41,42,43,44,45,46,47,47,49,50,51,52,53,54,55,55,57,58,59,60,61,62,63,63,65,66,67,68,69,70,71,71,40,20,33,41,48,53,57,61,64,66,69,71,73,75,76,78,80,82,85,87,89,91,92,94,96,98,101,103,105,107,108,110,112,114,117,119,121,123,124,126,128,40,23,39,51,60,67,73,79,83,87,91,94,97,100,102,105,107,111,115,118,121,124,126,129,131,135,139,142,145,148,150,153,155,159,163,166,169,172,174,177,179,35,28,49,65,
+ 78,89,99,107,114,120,126,132,136,141,145,149,153,159,165,171,176,180,185,189,192,199,205,211,216,220,225,229,232,239,245,251,21,33,58,79,97,112,125,137,148,157,166,174,182,189,195,201,207,217,227,235,243,251,17,35,63,86,106,123,139,152,165,177,187,197,206,214,222,230,237,250,25,31,55,75,91,105,117,128,138,146,154,161,168,174,180,185,190,200,208,215,222,229,235,240,245,255,16,36,65,89,110,128,144,159,173,185,196,207,217,226,234,242,250,11,41,74,103,128,151,172,191,209,225,241,255,9,43,79,110,138,163,
+ 186,207,227,246,12,39,71,99,123,144,164,182,198,214,228,241,253,9,44,81,113,142,168,192,214,235,255,7,49,90,127,160,191,220,247,6,51,95,134,170,203,234,7,47,87,123,155,184,212,237,6,52,97,137,174,208,240,5,57,106,151,192,231,5,59,111,158,202,243,5,55,103,147,187,224,5,60,113,161,206,248,4,65,122,175,224,4,67,127,182,234,224,224,224,224,224,224,224,224,160,160,160,160,185,185,185,178,178,168,134,61,37,224,224,224,224,224,224,224,224,240,240,240,240,207,207,207,198,198,183,144,66,40,160,160,160,160,
+ 160,160,160,160,185,185,185,185,193,193,193,183,183,172,138,64,38,240,240,240,240,240,240,240,240,207,207,207,207,204,204,204,193,193,180,143,66,40,185,185,185,185,185,185,185,185,193,193,193,193,193,193,193,183,183,172,138,65,39,207,207,207,207,207,207,207,207,204,204,204,204,201,201,201,188,188,176,141,66,40,193,193,193,193,193,193,193,193,193,193,193,193,194,194,194,184,184,173,139,65,39,204,204,204,204,204,204,204,204,201,201,201,201,198,198,198,187,187,175,140,66,40,72,127,65,129,66,128,65,128,
+ 64,128,62,128,64,128,64,128,92,78,92,79,92,78,90,79,116,41,115,40,114,40,132,26,132,26,145,17,161,12,176,10,177,11,24,179,48,138,54,135,54,132,53,134,56,133,55,132,55,132,61,114,70,96,74,88,75,88,87,74,89,66,91,67,100,59,108,50,120,40,122,37,97,43,78,50,83,78,84,81,88,75,86,74,87,71,90,73,93,74,93,74,109,40,114,36,117,34,117,34,143,17,145,18,146,19,162,12,165,10,178,7,189,6,190,8,177,9,23,178,54,115,63,102,66,98,69,99,74,89,71,91,73,91,78,89,86,80,92,66,93,64,102,59,103,60,104,60,117,52,123,44,138,
+ 35,133,31,97,38,77,45,61,90,93,60,105,42,107,41,110,45,116,38,113,38,112,38,124,26,132,27,136,19,140,20,155,14,159,16,158,18,170,13,177,10,187,8,192,6,175,9,159,10,21,178,59,110,71,86,75,85,84,83,91,66,88,73,87,72,92,75,98,72,105,58,107,54,115,52,114,55,112,56,129,51,132,40,150,33,140,29,98,35,77,42,42,121,96,66,108,43,111,40,117,44,123,32,120,36,119,33,127,33,134,34,139,21,147,23,152,20,158,25,154,26,166,21,173,16,184,13,184,10,150,13,139,15,22,178,63,114,74,82,84,83,92,82,103,62,96,72,96,67,101,
+ 73,107,72,113,55,118,52,125,52,118,52,117,55,135,49,137,39,157,32,145,29,97,33,77,40,2,1,0,0,8,13,16,19,21,23,24,26,27,28,29,30,31,32,32,33,34,34,35,36,36,37,37,224,112,44,15,3,2,1,0,254,237,192,132,70,23,4,0,255,252,226,155,61,11,2,0,250,245,234,203,71,50,42,38,35,33,31,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,179,99,0,71,56,43,30,21,12,6,0,199,165,144,124,109,96,84,71,61,51,42,32,23,15,8,0,241,225,211,199,187,175,164,153,142,132,123,114,105,96,88,80,72,64,
+ 57,50,44,38,33,29,24,20,16,12,9,5,2,0,15,131,138,138,155,155,173,173,69,93,115,118,131,138,141,138,150,150,155,150,155,160,166,160,131,128,134,141,141,141,145,145,145,150,155,155,155,155,160,160,160,160,166,166,173,173,182,192,182,192,192,192,205,192,205,224,4,6,24,7,5,0,0,2,0,0,12,28,41,13,252,247,15,42,25,14,1,254,62,41,247,246,37,65,252,3,250,4,66,7,248,16,14,38,253,33,13,22,39,23,12,255,36,64,27,250,249,10,55,43,17,1,1,8,1,1,6,245,74,53,247,244,55,76,244,8,253,3,93,27,252,26,39,59,3,248,2,0,77,
+ 11,9,248,22,44,250,7,40,9,26,3,9,249,20,101,249,4,3,248,42,26,0,241,33,68,2,23,254,55,46,254,15,3,255,21,16,41,250,27,61,39,5,245,42,88,4,1,254,60,65,6,252,255,251,73,56,1,247,19,94,29,247,0,12,99,6,4,8,237,102,46,243,3,2,13,3,2,9,235,84,72,238,245,46,104,234,8,18,38,48,23,0,240,70,83,235,11,5,245,117,22,248,250,23,117,244,3,3,248,95,28,4,246,15,77,60,241,255,4,124,2,252,3,38,84,24,231,2,13,42,13,31,21,252,56,46,255,255,35,79,243,19,249,65,88,247,242,20,4,81,49,227,20,0,75,3,239,5,247,44,92,248,1,
+ 253,22,69,31,250,95,41,244,5,39,67,16,252,1,0,250,120,55,220,243,44,122,4,232,81,5,11,3,7,2,0,9,10,88,46,2,90,87,93,91,82,98,109,120,118,12,113,115,117,119,99,59,87,111,63,111,112,80,126,124,125,124,129,121,126,23,132,127,127,127,126,127,122,133,130,134,101,118,119,145,126,86,124,120,123,119,170,173,107,109,8,16,32,12,35,60,83,108,132,157,180,206,228,15,32,55,77,101,125,151,175,201,225,19,42,66,89,114,137,162,184,209,230,12,25,50,72,97,120,147,172,200,223,26,44,69,90,114,135,159,180,205,225,13,22,
+ 53,80,106,130,156,180,205,228,15,25,44,64,90,115,142,168,196,222,19,24,62,82,100,120,145,168,190,214,22,31,50,79,103,120,151,170,203,227,21,29,45,65,106,124,150,171,196,224,30,49,75,97,121,142,165,186,209,229,19,25,52,70,93,116,143,166,192,219,26,34,62,75,97,118,145,167,194,217,25,33,56,70,91,113,143,165,196,223,21,34,51,72,97,117,145,171,196,222,20,29,50,67,90,117,144,168,197,221,22,31,48,66,95,117,146,168,196,222,24,33,51,77,116,134,158,180,200,224,21,28,70,87,106,124,149,170,194,217,26,33,53,64,
+ 83,117,152,173,204,225,27,34,65,95,108,129,155,174,210,225,20,26,72,99,113,131,154,176,200,219,34,43,61,78,93,114,155,177,205,229,23,29,54,97,124,138,163,179,209,229,30,38,56,89,118,129,158,178,200,231,21,29,49,63,85,111,142,163,193,222,27,48,77,103,133,158,179,196,215,232,29,47,74,99,124,151,176,198,220,237,33,42,61,76,93,121,155,174,207,225,29,53,87,112,136,154,170,188,208,227,24,30,52,84,131,150,166,186,203,229,37,48,64,84,104,118,156,177,201,230,212,178,148,129,108,96,85,82,79,77,61,59,57,56,
+ 51,49,48,45,42,41,40,38,36,34,31,30,21,12,10,3,1,0,255,245,244,236,233,225,217,203,190,176,175,161,149,136,125,114,102,91,81,71,60,52,43,35,28,20,19,18,12,11,5,0,179,138,140,148,151,149,153,151,163,116,67,82,59,92,72,100,89,92,16,0,0,0,0,99,66,36,36,34,36,34,34,34,34,83,69,36,52,34,116,102,70,68,68,176,102,68,68,34,65,85,68,84,36,116,141,152,139,170,132,187,184,216,137,132,249,168,185,139,104,102,100,68,68,178,218,185,185,170,244,216,187,187,170,244,187,187,219,138,103,155,184,185,137,116,183,155,
+ 152,136,132,217,184,184,170,164,217,171,155,139,244,169,184,185,170,164,216,223,218,138,214,143,188,218,168,244,141,136,155,170,168,138,220,219,139,164,219,202,216,137,168,186,246,185,139,116,185,219,185,138,100,100,134,100,102,34,68,68,100,68,168,203,221,218,168,167,154,136,104,70,164,246,171,137,139,137,155,218,219,139,255,254,253,238,14,3,2,1,0,255,254,252,218,35,3,2,1,0,255,254,250,208,59,4,2,1,0,255,254,246,194,71,10,2,1,0,255,252,236,183,82,8,2,1,0,255,252,235,180,90,17,2,1,0,255,248,224,171,
+ 97,30,4,1,0,255,254,236,173,95,37,7,1,0,255,255,255,131,6,145,255,255,255,255,255,236,93,15,96,255,255,255,255,255,194,83,25,71,221,255,255,255,255,162,73,34,66,162,255,255,255,210,126,73,43,57,173,255,255,255,201,125,71,48,58,130,255,255,255,166,110,73,57,62,104,210,255,255,251,123,65,55,68,100,171,255,7,23,38,54,69,85,100,116,131,147,162,178,193,208,223,239,13,25,41,55,69,83,98,112,127,142,157,171,187,203,220,236,15,21,34,51,61,78,92,106,126,136,152,167,185,205,225,240,10,21,36,50,63,79,95,110,
+ 126,141,157,173,189,205,221,237,17,20,37,51,59,78,89,107,123,134,150,164,184,205,224,240,10,15,32,51,67,81,96,112,129,142,158,173,189,204,220,236,8,21,37,51,65,79,98,113,126,138,155,168,179,192,209,218,12,15,34,55,63,78,87,108,118,131,148,167,185,203,219,236,16,19,32,36,56,79,91,108,118,136,154,171,186,204,220,237,11,28,43,58,74,89,105,120,135,150,165,180,196,211,226,241,6,16,33,46,60,75,92,107,123,137,156,169,185,199,214,225,11,19,30,44,57,74,89,105,121,135,152,169,186,202,218,234,12,19,29,46,57,
+ 71,88,100,120,132,148,165,182,199,216,233,17,23,35,46,56,77,92,106,123,134,152,167,185,204,222,237,14,17,45,53,63,75,89,107,115,132,151,171,188,206,221,240,9,16,29,40,56,71,88,103,119,137,154,171,189,205,222,237,16,19,36,48,57,76,87,105,118,132,150,167,185,202,218,236,12,17,29,54,71,81,94,104,126,136,149,164,182,201,221,237,15,28,47,62,79,97,115,129,142,155,168,180,194,208,223,238,8,14,30,45,62,78,94,111,127,143,159,175,192,207,223,239,17,30,49,62,79,92,107,119,132,145,160,174,190,204,220,235,14,
+ 19,36,45,61,76,91,108,121,138,154,172,189,205,222,238,12,18,31,45,60,76,91,107,123,138,154,171,187,204,221,236,13,17,31,43,53,70,83,103,114,131,149,167,185,203,220,237,17,22,35,42,58,78,93,110,125,139,155,170,188,206,224,240,8,15,34,50,67,83,99,115,131,146,162,178,193,209,224,239,13,16,41,66,73,86,95,111,128,137,150,163,183,206,225,241,17,25,37,52,63,75,92,102,119,132,144,160,175,191,212,231,19,31,49,65,83,100,117,133,147,161,174,187,200,213,227,242,18,31,52,68,88,103,117,126,138,149,163,177,192,
+ 207,223,239,16,29,47,61,76,90,106,119,133,147,161,176,193,209,224,240,15,21,35,50,61,73,86,97,110,119,129,141,175,198,218,237,225,204,201,184,183,175,158,154,153,135,119,115,113,110,109,99,98,95,79,68,52,50,48,45,43,32,31,27,18,10,3,0,255,251,235,230,212,201,196,182,167,166,163,151,138,124,110,104,90,78,76,70,69,57,45,34,24,21,11,6,5,4,3,0,175,148,160,176,178,173,174,164,177,174,196,182,198,192,182,68,62,66,60,72,117,85,90,118,136,151,142,160,142,155,0,0,0,0,0,0,0,1,100,102,102,68,68,36,34,96,164,
+ 107,158,185,180,185,139,102,64,66,36,34,34,0,1,32,208,139,141,191,152,185,155,104,96,171,104,166,102,102,102,132,1,0,0,0,0,16,16,0,80,109,78,107,185,139,103,101,208,212,141,139,173,153,123,103,36,0,0,0,0,0,0,1,48,0,0,0,0,0,0,32,68,135,123,119,119,103,69,98,68,103,120,118,118,102,71,98,134,136,157,184,182,153,139,134,208,168,248,75,189,143,121,107,32,49,34,34,34,0,17,2,210,235,139,123,185,137,105,134,98,135,104,182,100,183,171,134,100,70,68,70,66,66,34,131,64,166,102,68,36,2,1,0,134,166,102,68,34,
+ 34,66,132,212,246,158,139,107,107,87,102,100,219,125,122,137,118,103,132,114,135,137,105,171,106,50,34,164,214,141,143,185,151,121,103,192,34,0,0,0,0,0,1,208,109,74,187,134,249,159,137,102,110,154,118,87,101,119,101,0,2,0,36,36,66,68,35,96,164,102,100,36,0,2,33,167,138,174,102,100,84,2,2,100,107,120,119,36,197,24,0,255,254,253,244,12,3,2,1,0,255,254,252,224,38,3,2,1,0,255,254,251,209,57,4,2,1,0,255,254,244,195,69,4,2,1,0,255,251,232,184,84,7,2,1,0,255,254,240,186,86,14,2,1,0,255,254,239,178,91,30,
+ 5,1,0,255,248,227,177,100,19,2,1,0,255,255,255,156,4,154,255,255,255,255,255,227,102,15,92,255,255,255,255,255,213,83,24,72,236,255,255,255,255,150,76,33,63,214,255,255,255,190,121,77,43,55,185,255,255,255,245,137,71,43,59,139,255,255,255,255,131,66,50,66,107,194,255,255,166,116,76,55,53,125,255,255,249,247,246,245,244,234,210,202,201,200,197,174,82,59,56,55,54,46,22,12,11,10,9,7,0,64,0,203,150,0,215,195,166,125,110,82,0,120,0,128,64,0,232,158,10,0,230,0,243,221,192,181,0,171,85,0,192,128,64,0,205,
+ 154,102,51,0,213,171,128,85,43,0,224,192,160,128,96,64,32,0,100,40,16,7,3,1,0,253,250,244,233,212,182,150,131,120,110,98,85,72,60,49,40,32,25,19,15,13,11,9,8,7,6,5,4,3,2,1,0,210,208,206,203,199,193,183,168,142,104,74,52,37,27,20,14,10,6,4,2,0,223,201,183,167,152,138,124,111,98,88,79,70,62,56,50,44,39,35,31,27,24,21,18,16,14,12,10,8,6,4,3,2,1,0,188,176,155,138,119,97,67,43,26,10,0,165,119,80,61,47,35,27,20,14,9,4,0,113,63,0,8,10,12,16,125,51,26,18,15,12,11,10,9,8,7,6,5,4,3,2,1,0,198,105,45,22,15,12,
+ 11,10,9,8,7,6,5,4,3,2,1,0,213,162,116,83,59,43,32,24,18,15,12,9,7,6,5,3,2,0,239,187,116,59,28,16,11,10,9,8,7,6,5,4,3,2,1,0,250,229,188,135,86,51,30,19,13,10,8,6,5,4,3,2,1,0,249,235,213,185,156,128,103,83,66,53,42,33,26,21,17,13,10,0,254,249,235,206,164,118,77,46,27,16,10,7,5,4,3,2,1,0,255,253,249,239,220,191,156,119,85,57,37,23,15,10,6,4,2,0,255,253,251,246,237,223,203,179,152,124,98,75,55,40,29,21,15,0,255,254,253,247,220,162,106,67,42,28,18,12,9,6,4,3,2,0,31,57,107,160,205,205,255,255,255,255,255,
+ 255,255,255,255,255,255,255,69,47,67,111,166,205,255,255,255,255,255,255,255,255,255,255,255,255,82,74,79,95,109,128,145,160,173,205,205,205,224,255,255,224,255,224,125,74,59,69,97,141,182,255,255,255,255,255,255,255,255,255,255,255,173,115,85,73,76,92,115,145,173,205,224,224,255,255,255,255,255,255,166,134,113,102,101,102,107,118,125,138,145,155,166,182,192,192,205,150,224,182,134,101,83,79,85,97,120,145,173,205,224,255,255,255,255,255,255,224,192,150,120,101,92,89,93,102,118,134,160,182,192,224,
+ 224,224,255,224,224,182,155,134,118,109,104,102,106,111,118,131,145,160,173,131,241,190,178,132,87,74,41,14,0,223,193,157,140,106,57,39,18,0,131,74,141,79,80,138,95,104,134,95,99,91,125,93,76,123,115,123,128,0,214,42,0,235,128,21,0,244,184,72,11,0,248,214,128,42,7,0,248,225,170,80,25,5,0,251,236,198,126,54,18,3,0,250,238,211,159,82,35,15,5,0,250,231,203,168,128,88,53,25,6,0,252,238,216,185,148,108,71,40,18,4,0,253,243,225,199,166,128,90,57,31,13,3,0,254,246,233,212,183,147,109,73,44,23,10,2,0,255,
+ 250,240,223,198,166,128,90,58,33,16,6,1,0,255,251,244,231,210,181,146,110,75,46,25,12,5,1,0,255,253,248,238,221,196,164,128,92,60,35,18,8,3,1,0,255,253,249,242,229,208,180,146,110,76,48,27,14,7,3,1,0,129,0,207,50,0,236,129,20,0,245,185,72,10,0,249,213,129,42,6,0,250,226,169,87,27,4,0,251,233,194,130,62,20,4,0,250,236,207,160,99,47,17,3,0,255,240,217,182,131,81,41,11,1,0,255,254,233,201,159,107,61,20,2,1,0,255,249,233,206,170,128,86,50,23,7,1,0,255,250,238,217,186,148,108,70,39,18,6,1,0,255,252,243,
+ 226,200,166,128,90,56,30,13,4,1,0,255,252,245,231,209,180,146,110,76,47,25,11,4,1,0,255,253,248,237,219,194,163,128,93,62,37,19,8,3,1,0,255,254,250,241,226,205,177,145,111,79,51,30,15,6,2,1,0,129,0,203,54,0,234,129,23,0,245,184,73,10,0,250,215,129,41,5,0,252,232,173,86,24,3,0,253,240,200,129,56,15,2,0,253,244,217,164,94,38,10,1,0,253,245,226,189,132,71,27,7,1,0,253,246,231,203,159,105,56,23,6,1,0,255,248,235,213,179,133,85,47,19,5,1,0,255,254,243,221,194,159,117,70,37,12,2,1,0,255,254,248,234,208,
+ 171,128,85,48,22,8,2,1,0,255,254,250,240,220,189,149,107,67,36,16,6,2,1,0,255,254,251,243,227,201,166,128,90,55,29,13,5,2,1,0,255,254,252,246,234,213,183,147,109,73,43,22,10,4,2,1,0,130,0,200,58,0,231,130,26,0,244,184,76,12,0,249,214,130,43,6,0,252,232,173,87,24,3,0,253,241,203,131,56,14,2,0,254,246,221,167,94,35,8,1,0,254,249,232,193,130,65,23,5,1,0,255,251,239,211,162,99,45,15,4,1,0,255,251,243,223,186,131,74,33,11,3,1,0,255,252,245,230,202,158,105,57,24,8,2,1,0,255,253,247,235,214,179,132,84,44,
+ 19,7,2,1,0,255,254,250,240,223,196,159,112,69,36,15,6,2,1,0,255,254,253,245,231,209,176,136,93,55,27,11,3,2,1,0,255,254,253,252,239,221,194,158,117,76,42,18,4,3,2,1,0,0,0,2,5,9,14,20,27,35,44,54,65,77,90,104,119,135,254,49,67,77,82,93,99,198,11,18,24,31,36,45,255,46,66,78,87,94,104,208,14,21,32,42,51,66,255,94,104,109,112,115,118,248,53,69,80,88,95,102,0,15,8,7,4,11,12,3,2,13,10,5,6,9,14,1,0,9,6,3,4,5,8,1,2,7,0,1,0,0,0,1,0,0,1,255,1,255,2,254,2,254,3,253,0,1,0,1,255,2,255,2,254,3,254,3,253,7,254,
+ 7,0,2,255,255,255,0,0,1,1,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,255,2,1,0,1,1,0,0,255,255,0,0,1,255,0,1,255,0,255,1,254,2,254,254,2,253,2,3,253,252,3,252,4,4,251,5,250,251,6,249,6,5,8,247,0,0,1,0,0,0,0,0,0,0,255,1,0,0,1,255,0,1,255,255,1,255,2,1,255,2,254,254,2,254,2,2,3,253,0,1,0,0,0,0,0,0,1,0,1,0,0,1,255,1,0,0,2,1,255,2,255,255,2,255,2,2,255,3,254,254,254,3,0,1,0,0,1,0,1,255,2,255,2,255,2,3,254,3,254,254,4,4,253,5,253,252,6,252,6,5,251,8,250,251,249,9,251,8,255,6,255,6,252,10,250,10,
+ 254,6,255,6,251,10,247,12,253,7,254,7,249,13,16,24,34,6,0,3,0,7,3,0,1,10,0,2,6,18,10,12,4,0,2,0,0,0,9,4,7,4,0,3,12,7,7,255,255],"i8",4,y.h+20480);var Na=z,z=z+16;function Oa(a){b.___errno_location&&(M[b.___errno_location()>>2]=a);return a}b._i64Add=Pa;b._bitshift64Ashr=Qa;b._memset=Ra;b._memcpy=Sa;var Ta=ha;function S(a){S.a||(E=wa(),S.a=!0,assert(y.b),S.u=y.b,y.b=function(){G("cannot dynamically allocate, sbrk now has control")});var c=E;return 0==a||S.u(a)?c:4294967295}
+ function Y(){Y.a||(Y.a=[]);Y.a.push(y.g());return Y.a.length-1}b._memmove=Ua;
+ var Aa=x=y.p(z),na=!0,Ba=Aa+Ca,E=y.p(Ba),Va=O([8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,
+ 0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0],"i8",3);b.s={Math:Math,Int8Array:Int8Array,Int16Array:Int16Array,Int32Array:Int32Array,Uint8Array:Uint8Array,Uint16Array:Uint16Array,Uint32Array:Uint32Array,Float32Array:Float32Array,Float64Array:Float64Array,NaN:NaN,Infinity:Infinity};
+ b.t={abort:G,assert:assert,invoke_viiiiiii:function(a,c,d,e,g,k,h,A){try{b.dynCall_viiiiiii(a,c,d,e,g,k,h,A)}catch(m){if("number"!==typeof m&&"longjmp"!==m)throw m;Z.setThrew(1,0)}},_llvm_pow_f64:La,_pthread_self:function(){return 0},_abort:function(){b.abort()},___setErrNo:Oa,_llvm_stacksave:Y,_sbrk:S,_time:function(a){var c=Date.now()/1E3|0;a&&(M[a>>2]=c);return c},_llvm_fabs_f64:Ta,_emscripten_memcpy_big:function(a,c,d){R.set(R.subarray(c,c+d),a);return a},_llvm_stackrestore:function(a){var c=
+ Y.a[a];Y.a.splice(a,1);y.c(c)},_sysconf:function(a){switch(a){case 30:return 4096;case 85:return U/4096;case 132:case 133:case 12:case 137:case 138:case 15:case 235:case 16:case 17:case 18:case 19:case 20:case 149:case 13:case 10:case 236:case 153:case 9:case 21:case 22:case 159:case 154:case 14:case 77:case 78:case 139:case 80:case 81:case 82:case 68:case 67:case 164:case 11:case 29:case 47:case 48:case 95:case 52:case 51:case 46:return 200809;case 79:return 0;case 27:case 246:case 127:case 128:case 23:case 24:case 160:case 161:case 181:case 182:case 242:case 183:case 184:case 243:case 244:case 245:case 165:case 178:case 179:case 49:case 50:case 168:case 169:case 175:case 170:case 171:case 172:case 97:case 76:case 32:case 173:case 35:return-1;
+ case 176:case 177:case 7:case 155:case 8:case 157:case 125:case 126:case 92:case 93:case 129:case 130:case 131:case 94:case 91:return 1;case 74:case 60:case 69:case 70:case 4:return 1024;case 31:case 42:case 72:return 32;case 87:case 26:case 33:return 2147483647;case 34:case 1:return 47839;case 38:case 36:return 99;case 43:case 37:return 2048;case 0:return 2097152;case 3:return 65536;case 28:return 32768;case 44:return 32767;case 75:return 16384;case 39:return 1E3;case 89:return 700;case 71:return 256;
+ case 40:return 255;case 2:return 100;case 180:return 64;case 25:return 20;case 5:return 16;case 6:return 6;case 73:return 4;case 84:return"object"===typeof navigator?navigator.hardwareConcurrency||1:1}Oa(22);return-1},STACKTOP:x,STACK_MAX:Ba,tempDoublePtr:Na,ABORT:H,cttz_i8:Va};// EMSCRIPTEN_START_ASM
+ var Z=(function(global,env,buffer) {
+ "use asm";var a=new global.Int8Array(buffer);var b=new global.Int16Array(buffer);var c=new global.Int32Array(buffer);var d=new global.Uint8Array(buffer);var e=new global.Uint16Array(buffer);var f=new global.Uint32Array(buffer);var g=new global.Float32Array(buffer);var h=new global.Float64Array(buffer);var i=env.STACKTOP|0;var j=env.STACK_MAX|0;var k=env.tempDoublePtr|0;var l=env.ABORT|0;var m=env.cttz_i8|0;var n=0;var o=0;var p=0;var q=0;var r=global.NaN,s=global.Infinity;var t=0,u=0,v=0,w=0,x=0.0,y=0,z=0,A=0,B=0.0;var C=0;var D=0;var E=0;var F=0;var G=0;var H=0;var I=0;var J=0;var K=0;var L=0;var M=global.Math.floor;var N=global.Math.abs;var O=global.Math.sqrt;var P=global.Math.pow;var Q=global.Math.cos;var R=global.Math.sin;var S=global.Math.tan;var T=global.Math.acos;var U=global.Math.asin;var V=global.Math.atan;var W=global.Math.atan2;var X=global.Math.exp;var Y=global.Math.log;var Z=global.Math.ceil;var _=global.Math.imul;var $=global.Math.min;var aa=global.Math.clz32;var ba=env.abort;var ca=env.assert;var da=env.invoke_viiiiiii;var ea=env._llvm_pow_f64;var fa=env._pthread_self;var ga=env._abort;var ha=env.___setErrNo;var ia=env._llvm_stacksave;var ja=env._sbrk;var ka=env._time;var la=env._llvm_fabs_f64;var ma=env._emscripten_memcpy_big;var na=env._llvm_stackrestore;var oa=env._sysconf;var pa=0.0;
+ // EMSCRIPTEN_START_FUNCS
+ function Lb(a,d,e,f,h,j,k){a=a|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;var l=0,m=0.0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0,ha=0,ja=0,ka=0,la=0,ma=0,oa=0,pa=0,qa=0,ra=0,sa=0,ta=0,ua=0;ta=i;i=i+304|0;X=ta+288|0;Y=ta+284|0;l=ta+280|0;Z=ta+276|0;$=ta+272|0;aa=ta+268|0;ba=ta+264|0;ca=ta+260|0;ha=ta+256|0;ka=ta+252|0;ga=ta+248|0;M=ta+244|0;B=ta+240|0;n=ta+192|0;C=ta+184|0;ra=ta+176|0;ua=ta+168|0;oa=ta+164|0;pa=ta+160|0;qa=ta+156|0;V=ta+152|0;K=ta+148|0;Q=ta+144|0;q=ta+140|0;fa=ta+136|0;O=ta+132|0;P=ta+128|0;sa=ta+124|0;ja=ta+120|0;I=ta+116|0;G=ta+112|0;A=ta+108|0;T=ta+104|0;S=ta+100|0;J=ta+96|0;H=ta+92|0;z=ta+88|0;F=ta+84|0;y=ta+80|0;s=ta+76|0;U=ta+72|0;E=ta+68|0;D=ta+64|0;L=ta+60|0;N=ta+56|0;la=ta+52|0;ma=ta+48|0;R=ta+44|0;u=ta+40|0;p=ta+36|0;o=ta+32|0;da=ta+28|0;x=ta+24|0;w=ta+20|0;t=ta+16|0;r=ta+12|0;v=ta+8|0;W=ta+4|0;ea=ta;c[Y>>2]=a;c[l>>2]=d;c[Z>>2]=e;c[$>>2]=f;c[aa>>2]=h;c[ba>>2]=j;c[ca>>2]=k;c[fa>>2]=c[(c[Y>>2]|0)+8>>2];c[J>>2]=0;c[H>>2]=0;c[D>>2]=0;c[N>>2]=c[(c[Y>>2]|0)+12>>2];c[la>>2]=c[c[Y>>2]>>2];c[ma>>2]=c[(c[la>>2]|0)+8>>2];c[R>>2]=c[(c[la>>2]|0)+4>>2];c[u>>2]=c[(c[la>>2]|0)+32>>2];c[sa>>2]=c[(c[Y>>2]|0)+20>>2];c[ja>>2]=c[(c[Y>>2]|0)+24>>2];c[aa>>2]=_(c[aa>>2]|0,c[(c[Y>>2]|0)+16>>2]|0)|0;c[ua>>2]=(c[Y>>2]|0)+84+((_(2048+(c[R>>2]|0)|0,c[fa>>2]|0)|0)<<2);c[oa>>2]=(c[ua>>2]|0)+((c[fa>>2]|0)*24<<2);c[pa>>2]=(c[oa>>2]|0)+(c[ma>>2]<<1<<2);c[qa>>2]=(c[pa>>2]|0)+(c[ma>>2]<<1<<2);c[V>>2]=(c[qa>>2]|0)+(c[ma>>2]<<1<<2);c[O>>2]=0;while(1){if((c[O>>2]|0)>(c[(c[la>>2]|0)+36>>2]|0))break;if((c[(c[la>>2]|0)+44>>2]<<c[O>>2]|0)==(c[aa>>2]|0))break;c[O>>2]=(c[O>>2]|0)+1}if((c[O>>2]|0)>(c[(c[la>>2]|0)+36>>2]|0)){c[X>>2]=-1;ua=c[X>>2]|0;i=ta;return ua|0}c[P>>2]=1<<c[O>>2];if((c[Z>>2]|0)<0|(c[Z>>2]|0)>1275|(c[$>>2]|0)==0){c[X>>2]=-1;ua=c[X>>2]|0;i=ta;return ua|0}c[ga>>2]=_(c[P>>2]|0,c[(c[la>>2]|0)+44>>2]|0)|0;c[ha>>2]=0;do{ua=(c[Y>>2]|0)+84+((_(c[ha>>2]|0,2048+(c[R>>2]|0)|0)|0)<<2)|0;c[C+(c[ha>>2]<<2)>>2]=ua;c[ra+(c[ha>>2]<<2)>>2]=(c[C+(c[ha>>2]<<2)>>2]|0)+8192+(0-(c[ga>>2]|0)<<2);ua=(c[ha>>2]|0)+1|0;c[ha>>2]=ua}while((ua|0)<(c[fa>>2]|0));c[I>>2]=c[ja>>2];if((c[I>>2]|0)>(c[(c[la>>2]|0)+12>>2]|0))c[I>>2]=c[(c[la>>2]|0)+12>>2];if((c[l>>2]|0)==0|(c[Z>>2]|0)<=1){Mb(c[Y>>2]|0,c[ga>>2]|0,c[O>>2]|0);Pb(ra,c[$>>2]|0,c[ga>>2]|0,c[fa>>2]|0,c[(c[Y>>2]|0)+16>>2]|0,(c[la>>2]|0)+16|0,(c[Y>>2]|0)+76|0,c[ca>>2]|0);c[X>>2]=(c[aa>>2]|0)/(c[(c[Y>>2]|0)+16>>2]|0)|0;ua=c[X>>2]|0;i=ta;return ua|0}if(!(c[ba>>2]|0)){Yb(n,c[l>>2]|0,c[Z>>2]|0);c[ba>>2]=n}a:do if((c[N>>2]|0)==1){c[ka>>2]=0;while(1){if((c[ka>>2]|0)>=(c[ma>>2]|0))break a;if(+g[(c[oa>>2]|0)+(c[ka>>2]<<2)>>2]>+g[(c[oa>>2]|0)+((c[ma>>2]|0)+(c[ka>>2]|0)<<2)>>2])m=+g[(c[oa>>2]|0)+(c[ka>>2]<<2)>>2];else m=+g[(c[oa>>2]|0)+((c[ma>>2]|0)+(c[ka>>2]|0)<<2)>>2];g[(c[oa>>2]|0)+(c[ka>>2]<<2)>>2]=m;c[ka>>2]=(c[ka>>2]|0)+1}}while(0);c[z>>2]=c[Z>>2]<<3;c[y>>2]=Qb(c[ba>>2]|0)|0;do if((c[y>>2]|0)<(c[z>>2]|0))if((c[y>>2]|0)==1){c[L>>2]=dc(c[ba>>2]|0,15)|0;break}else{c[L>>2]=0;break}else c[L>>2]=1;while(0);if(c[L>>2]|0){c[y>>2]=c[Z>>2]<<3;a=c[y>>2]|0;a=a-(Qb(c[ba>>2]|0)|0)|0;ua=(c[ba>>2]|0)+20|0;c[ua>>2]=(c[ua>>2]|0)+a}g[S>>2]=0.0;c[T>>2]=0;c[U>>2]=0;if((c[sa>>2]|0)==0?((c[y>>2]|0)+16|0)<=(c[z>>2]|0):0){if(dc(c[ba>>2]|0,1)|0){c[o>>2]=fc(c[ba>>2]|0,6)|0;ua=16<<c[o>>2];c[T>>2]=ua+(gc(c[ba>>2]|0,4+(c[o>>2]|0)|0)|0)-1;c[p>>2]=gc(c[ba>>2]|0,3)|0;ua=(Qb(c[ba>>2]|0)|0)+2|0;if((ua|0)<=(c[z>>2]|0))c[U>>2]=ec(c[ba>>2]|0,25575,2)|0;g[S>>2]=+((c[p>>2]|0)+1|0)*.09375}c[y>>2]=Qb(c[ba>>2]|0)|0}if((c[O>>2]|0)>0?((c[y>>2]|0)+3|0)<=(c[z>>2]|0):0){c[Q>>2]=dc(c[ba>>2]|0,3)|0;c[y>>2]=Qb(c[ba>>2]|0)|0}else c[Q>>2]=0;if(c[Q>>2]|0)c[K>>2]=c[P>>2];else c[K>>2]=0;if(((c[y>>2]|0)+3|0)<=(c[z>>2]|0))l=dc(c[ba>>2]|0,3)|0;else l=0;c[q>>2]=l;fd(c[la>>2]|0,c[sa>>2]|0,c[ja>>2]|0,c[oa>>2]|0,c[q>>2]|0,c[ba>>2]|0,c[N>>2]|0,c[O>>2]|0);ua=c[ma>>2]|0;c[da>>2]=ia()|0;e=i;i=i+((1*(ua<<2)|0)+15&-16)|0;Rb(c[sa>>2]|0,c[ja>>2]|0,c[Q>>2]|0,e,c[O>>2]|0,c[ba>>2]|0);c[y>>2]=Qb(c[ba>>2]|0)|0;c[M>>2]=2;if(((c[y>>2]|0)+4|0)<=(c[z>>2]|0))c[M>>2]=ec(c[ba>>2]|0,25578,5)|0;n=i;i=i+((1*(c[ma>>2]<<2)|0)+15&-16)|0;eb(c[la>>2]|0,n,c[O>>2]|0,c[N>>2]|0);h=i;i=i+((1*(c[ma>>2]<<2)|0)+15&-16)|0;c[s>>2]=6;c[z>>2]=c[z>>2]<<3;c[y>>2]=Xb(c[ba>>2]|0)|0;c[ka>>2]=c[sa>>2];while(1){if((c[ka>>2]|0)>=(c[ja>>2]|0))break;l=_(c[N>>2]|0,(b[(c[u>>2]|0)+((c[ka>>2]|0)+1<<1)>>1]|0)-(b[(c[u>>2]|0)+(c[ka>>2]<<1)>>1]|0)|0)|0;c[x>>2]=l<<c[O>>2];l=c[x>>2]|0;if((c[x>>2]<<3|0)<((48>(c[x>>2]|0)?48:c[x>>2]|0)|0))l=l<<3;else l=48>(l|0)?48:c[x>>2]|0;c[w>>2]=l;c[t>>2]=c[s>>2];c[r>>2]=0;while(1){if(((c[y>>2]|0)+(c[t>>2]<<3)|0)>=(c[z>>2]|0))break;if((c[r>>2]|0)>=(c[n+(c[ka>>2]<<2)>>2]|0))break;c[v>>2]=dc(c[ba>>2]|0,c[t>>2]|0)|0;c[y>>2]=Xb(c[ba>>2]|0)|0;if(!(c[v>>2]|0))break;c[r>>2]=(c[r>>2]|0)+(c[w>>2]|0);c[z>>2]=(c[z>>2]|0)-(c[w>>2]|0);c[t>>2]=1}c[h+(c[ka>>2]<<2)>>2]=c[r>>2];if((c[r>>2]|0)>0)c[s>>2]=2>((c[s>>2]|0)-1|0)?2:(c[s>>2]|0)-1|0;c[ka>>2]=(c[ka>>2]|0)+1}j=i;i=i+((1*(c[ma>>2]<<2)|0)+15&-16)|0;if(((c[y>>2]|0)+48|0)<=(c[z>>2]|0))l=ec(c[ba>>2]|0,25582,7)|0;else l=5;c[A>>2]=l;ua=c[Z>>2]<<3<<3;c[B>>2]=ua-(Xb(c[ba>>2]|0)|0)-1;if((c[Q>>2]|0)!=0&(c[O>>2]|0)>=2)l=(c[B>>2]|0)>=((c[O>>2]|0)+2<<3|0);else l=0;c[E>>2]=l?8:0;c[B>>2]=(c[B>>2]|0)-(c[E>>2]|0);k=i;i=i+((1*(c[ma>>2]<<2)|0)+15&-16)|0;d=i;i=i+((1*(c[ma>>2]<<2)|0)+15&-16)|0;c[G>>2]=jd(c[la>>2]|0,c[sa>>2]|0,c[ja>>2]|0,h,n,c[A>>2]|0,J,H,c[B>>2]|0,F,k,j,d,c[N>>2]|0,c[O>>2]|0,c[ba>>2]|0,0,0,0)|0;gd(c[la>>2]|0,c[sa>>2]|0,c[ja>>2]|0,c[oa>>2]|0,j,c[ba>>2]|0,c[N>>2]|0);c[ha>>2]=0;do{qj(c[C+(c[ha>>2]<<2)>>2]|0,(c[C+(c[ha>>2]<<2)>>2]|0)+(c[ga>>2]<<2)|0,(2048-(c[ga>>2]|0)+((c[R>>2]|0)/2|0)<<2)+0|0)|0;ua=(c[ha>>2]|0)+1|0;c[ha>>2]=ua}while((ua|0)<(c[fa>>2]|0));ua=_(c[N>>2]|0,c[ma>>2]|0)|0;l=i;i=i+((1*ua|0)+15&-16)|0;ua=(_(c[N>>2]|0,c[ga>>2]|0)|0)<<2;n=i;i=i+((1*ua|0)+15&-16)|0;Ka(0,c[la>>2]|0,c[sa>>2]|0,c[ja>>2]|0,n,(c[N>>2]|0)==2?n+(c[ga>>2]<<2)|0:0,l,0,k,c[K>>2]|0,c[M>>2]|0,c[H>>2]|0,c[J>>2]|0,e,(c[Z>>2]<<6)-(c[E>>2]|0)|0,c[F>>2]|0,c[ba>>2]|0,c[O>>2]|0,c[G>>2]|0,(c[Y>>2]|0)+36|0,c[(c[Y>>2]|0)+32>>2]|0);if((c[E>>2]|0)>0)c[D>>2]=gc(c[ba>>2]|0,1)|0;H=c[la>>2]|0;J=c[sa>>2]|0;K=c[ja>>2]|0;M=c[oa>>2]|0;ua=c[Z>>2]<<3;ua=ua-(Qb(c[ba>>2]|0)|0)|0;hd(H,J,K,M,j,d,ua,c[ba>>2]|0,c[N>>2]|0);if(c[D>>2]|0)Ga(c[la>>2]|0,n,l,c[O>>2]|0,c[N>>2]|0,c[ga>>2]|0,c[sa>>2]|0,c[ja>>2]|0,c[oa>>2]|0,c[pa>>2]|0,c[qa>>2]|0,k,c[(c[Y>>2]|0)+36>>2]|0,c[(c[Y>>2]|0)+32>>2]|0);b:do if(c[L>>2]|0){c[ka>>2]=0;while(1){if((c[ka>>2]|0)>=(_(c[N>>2]|0,c[ma>>2]|0)|0))break b;g[(c[oa>>2]|0)+(c[ka>>2]<<2)>>2]=-28.0;c[ka>>2]=(c[ka>>2]|0)+1}}while(0);Nb(c[la>>2]|0,n,ra,c[oa>>2]|0,c[sa>>2]|0,c[I>>2]|0,c[N>>2]|0,c[fa>>2]|0,c[Q>>2]|0,c[O>>2]|0,c[(c[Y>>2]|0)+16>>2]|0,c[L>>2]|0,c[(c[Y>>2]|0)+32>>2]|0);c[ha>>2]=0;do{if((c[(c[Y>>2]|0)+52>>2]|0)>15)l=c[(c[Y>>2]|0)+52>>2]|0;else l=15;c[(c[Y>>2]|0)+52>>2]=l;if((c[(c[Y>>2]|0)+56>>2]|0)>15)l=c[(c[Y>>2]|0)+56>>2]|0;else l=15;c[(c[Y>>2]|0)+56>>2]=l;cb(c[ra+(c[ha>>2]<<2)>>2]|0,c[ra+(c[ha>>2]<<2)>>2]|0,c[(c[Y>>2]|0)+56>>2]|0,c[(c[Y>>2]|0)+52>>2]|0,c[(c[la>>2]|0)+44>>2]|0,+g[(c[Y>>2]|0)+64>>2],+g[(c[Y>>2]|0)+60>>2],c[(c[Y>>2]|0)+72>>2]|0,c[(c[Y>>2]|0)+68>>2]|0,c[(c[la>>2]|0)+60>>2]|0,c[R>>2]|0,c[(c[Y>>2]|0)+32>>2]|0);if(c[O>>2]|0)cb((c[ra+(c[ha>>2]<<2)>>2]|0)+(c[(c[la>>2]|0)+44>>2]<<2)|0,(c[ra+(c[ha>>2]<<2)>>2]|0)+(c[(c[la>>2]|0)+44>>2]<<2)|0,c[(c[Y>>2]|0)+52>>2]|0,c[T>>2]|0,(c[ga>>2]|0)-(c[(c[la>>2]|0)+44>>2]|0)|0,+g[(c[Y>>2]|0)+60>>2],+g[S>>2],c[(c[Y>>2]|0)+68>>2]|0,c[U>>2]|0,c[(c[la>>2]|0)+60>>2]|0,c[R>>2]|0,c[(c[Y>>2]|0)+32>>2]|0);ua=(c[ha>>2]|0)+1|0;c[ha>>2]=ua}while((ua|0)<(c[fa>>2]|0));c[(c[Y>>2]|0)+56>>2]=c[(c[Y>>2]|0)+52>>2];g[(c[Y>>2]|0)+64>>2]=+g[(c[Y>>2]|0)+60>>2];c[(c[Y>>2]|0)+72>>2]=c[(c[Y>>2]|0)+68>>2];c[(c[Y>>2]|0)+52>>2]=c[T>>2];g[(c[Y>>2]|0)+60>>2]=+g[S>>2];c[(c[Y>>2]|0)+68>>2]=c[U>>2];if(c[O>>2]|0){c[(c[Y>>2]|0)+56>>2]=c[(c[Y>>2]|0)+52>>2];g[(c[Y>>2]|0)+64>>2]=+g[(c[Y>>2]|0)+60>>2];c[(c[Y>>2]|0)+72>>2]=c[(c[Y>>2]|0)+68>>2]}if((c[N>>2]|0)==1)pj((c[oa>>2]|0)+(c[ma>>2]<<2)|0,c[oa>>2]|0,(c[ma>>2]<<2)+0|0)|0;c:do if(c[Q>>2]|0){c[ka>>2]=0;while(1){if((c[ka>>2]|0)>=(c[ma>>2]<<1|0))break c;l=c[ka>>2]|0;if(+g[(c[pa>>2]|0)+(c[ka>>2]<<2)>>2]<+g[(c[oa>>2]|0)+(c[ka>>2]<<2)>>2])m=+g[(c[pa>>2]|0)+(l<<2)>>2];else m=+g[(c[oa>>2]|0)+(l<<2)>>2];g[(c[pa>>2]|0)+(c[ka>>2]<<2)>>2]=m;c[ka>>2]=(c[ka>>2]|0)+1}}else{pj(c[qa>>2]|0,c[pa>>2]|0,(c[ma>>2]<<1<<2)+0|0)|0;pj(c[pa>>2]|0,c[oa>>2]|0,(c[ma>>2]<<1<<2)+0|0)|0;if((c[(c[Y>>2]|0)+48>>2]|0)<10)g[W>>2]=+(c[P>>2]|0)*1.0000000474974513e-03;else g[W>>2]=1.0;c[ka>>2]=0;while(1){if((c[ka>>2]|0)>=(c[ma>>2]<<1|0))break c;l=c[ka>>2]|0;if(+g[(c[V>>2]|0)+(c[ka>>2]<<2)>>2]+ +g[W>>2]<+g[(c[oa>>2]|0)+(c[ka>>2]<<2)>>2])m=+g[(c[V>>2]|0)+(l<<2)>>2]+ +g[W>>2];else m=+g[(c[oa>>2]|0)+(l<<2)>>2];g[(c[V>>2]|0)+(c[ka>>2]<<2)>>2]=m;c[ka>>2]=(c[ka>>2]|0)+1}}while(0);c[ha>>2]=0;do{c[ka>>2]=0;while(1){if((c[ka>>2]|0)>=(c[sa>>2]|0))break;ua=_(c[ha>>2]|0,c[ma>>2]|0)|0;g[(c[oa>>2]|0)+(ua+(c[ka>>2]|0)<<2)>>2]=0.0;ua=_(c[ha>>2]|0,c[ma>>2]|0)|0;g[(c[qa>>2]|0)+(ua+(c[ka>>2]|0)<<2)>>2]=-28.0;ua=_(c[ha>>2]|0,c[ma>>2]|0)|0;g[(c[pa>>2]|0)+(ua+(c[ka>>2]|0)<<2)>>2]=-28.0;c[ka>>2]=(c[ka>>2]|0)+1}c[ka>>2]=c[ja>>2];while(1){l=c[ha>>2]|0;if((c[ka>>2]|0)>=(c[ma>>2]|0))break;ua=_(l,c[ma>>2]|0)|0;g[(c[oa>>2]|0)+(ua+(c[ka>>2]|0)<<2)>>2]=0.0;ua=_(c[ha>>2]|0,c[ma>>2]|0)|0;g[(c[qa>>2]|0)+(ua+(c[ka>>2]|0)<<2)>>2]=-28.0;ua=_(c[ha>>2]|0,c[ma>>2]|0)|0;g[(c[pa>>2]|0)+(ua+(c[ka>>2]|0)<<2)>>2]=-28.0;c[ka>>2]=(c[ka>>2]|0)+1}ua=l+1|0;c[ha>>2]=ua}while((ua|0)<2);c[(c[Y>>2]|0)+36>>2]=c[(c[ba>>2]|0)+28>>2];Pb(ra,c[$>>2]|0,c[ga>>2]|0,c[fa>>2]|0,c[(c[Y>>2]|0)+16>>2]|0,(c[la>>2]|0)+16|0,(c[Y>>2]|0)+76|0,c[ca>>2]|0);c[(c[Y>>2]|0)+48>>2]=0;ua=Qb(c[ba>>2]|0)|0;if((ua|0)>(c[Z>>2]<<3|0)){c[X>>2]=-3;c[ea>>2]=1}else{if(Sb(c[ba>>2]|0)|0)c[(c[Y>>2]|0)+40>>2]=1;c[X>>2]=(c[aa>>2]|0)/(c[(c[Y>>2]|0)+16>>2]|0)|0;c[ea>>2]=1}na(c[da>>2]|0);ua=c[X>>2]|0;i=ta;return ua|0}function Mb(a,d,e){a=a|0;d=d|0;e=e|0;var f=0.0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0,ha=0,ja=0,ka=0;ka=i;i=i+4608|0;aa=ka+4596|0;ba=ka+4592|0;L=ka+4588|0;da=ka+4584|0;fa=ka+4580|0;ca=ka+4576|0;ea=ka+4568|0;Y=ka+4560|0;C=ka+4556|0;X=ka+4552|0;j=ka+4548|0;k=ka+4544|0;N=ka+4540|0;ha=ka+4536|0;W=ka+4532|0;ja=ka+4528|0;$=ka+4524|0;ga=ka+4520|0;h=ka+4516|0;S=ka+4512|0;Z=ka+4508|0;U=ka+4504|0;T=ka+4500|0;R=ka+4496|0;M=ka+4492|0;V=ka+4488|0;Q=ka+4484|0;P=ka+4480|0;K=ka+4476|0;A=ka+4472|0;F=ka+4468|0;l=ka+4464|0;w=ka+368|0;t=ka+360|0;r=ka+356|0;o=ka+352|0;s=ka+348|0;z=ka+344|0;y=ka+340|0;x=ka+336|0;B=ka+332|0;q=ka+232|0;D=ka+136|0;m=ka+128|0;n=ka+124|0;u=ka+120|0;v=ka+116|0;H=ka+112|0;E=ka+16|0;p=ka+12|0;I=ka+8|0;G=ka+4|0;J=ka;c[aa>>2]=a;c[ba>>2]=d;c[L>>2]=e;c[ca>>2]=c[(c[aa>>2]|0)+8>>2];c[ha>>2]=c[c[aa>>2]>>2];c[W>>2]=c[(c[ha>>2]|0)+8>>2];c[ja>>2]=c[(c[ha>>2]|0)+4>>2];c[S>>2]=c[(c[ha>>2]|0)+32>>2];c[da>>2]=0;do{a=(c[aa>>2]|0)+84+((_(c[da>>2]|0,2048+(c[ja>>2]|0)|0)|0)<<2)|0;c[ea+(c[da>>2]<<2)>>2]=a;c[Y+(c[da>>2]<<2)>>2]=(c[ea+(c[da>>2]<<2)>>2]|0)+8192+(0-(c[ba>>2]|0)<<2);a=(c[da>>2]|0)+1|0;c[da>>2]=a}while((a|0)<(c[ca>>2]|0));c[C>>2]=(c[aa>>2]|0)+84+((_(2048+(c[ja>>2]|0)|0,c[ca>>2]|0)|0)<<2);c[X>>2]=(c[C>>2]|0)+((c[ca>>2]|0)*24<<2);c[j>>2]=(c[X>>2]|0)+(c[W>>2]<<1<<2);c[k>>2]=(c[j>>2]|0)+(c[W>>2]<<1<<2);c[N>>2]=(c[k>>2]|0)+(c[W>>2]<<1<<2);c[ga>>2]=c[(c[aa>>2]|0)+48>>2];c[$>>2]=c[(c[aa>>2]|0)+20>>2];c[h>>2]=((c[ga>>2]|0)>=5?1:(c[$>>2]|0)!=0)&1;if(c[h>>2]|0){c[U>>2]=c[(c[aa>>2]|0)+24>>2];if((c[U>>2]|0)<(c[(c[ha>>2]|0)+12>>2]|0))e=c[U>>2]|0;else e=c[(c[ha>>2]|0)+12>>2]|0;do if((c[$>>2]|0)<=(e|0))if((c[U>>2]|0)<(c[(c[ha>>2]|0)+12>>2]|0)){e=c[U>>2]|0;break}else{e=c[(c[ha>>2]|0)+12>>2]|0;break}else e=c[$>>2]|0;while(0);c[T>>2]=e;K=_(c[ca>>2]|0,c[ba>>2]|0)|0;c[M>>2]=ia()|0;d=i;i=i+((1*(K<<2)|0)+15&-16)|0;g[R>>2]=(c[ga>>2]|0)==0?1.5:.5;c[da>>2]=0;do{c[fa>>2]=c[$>>2];while(1){e=c[da>>2]|0;if((c[fa>>2]|0)>=(c[U>>2]|0))break;J=_(e,c[W>>2]|0)|0;K=_(c[da>>2]|0,c[W>>2]|0)|0;e=_(c[da>>2]|0,c[W>>2]|0)|0;e=e+(c[fa>>2]|0)|0;if(+g[(c[N>>2]|0)+(J+(c[fa>>2]|0)<<2)>>2]>+g[(c[X>>2]|0)+(K+(c[fa>>2]|0)<<2)>>2]-+g[R>>2])f=+g[(c[N>>2]|0)+(e<<2)>>2];else f=+g[(c[X>>2]|0)+(e<<2)>>2]-+g[R>>2];K=_(c[da>>2]|0,c[W>>2]|0)|0;g[(c[X>>2]|0)+(K+(c[fa>>2]|0)<<2)>>2]=f;c[fa>>2]=(c[fa>>2]|0)+1}K=e+1|0;c[da>>2]=K}while((K|0)<(c[ca>>2]|0));c[Z>>2]=c[(c[aa>>2]|0)+36>>2];c[da>>2]=0;while(1){if((c[da>>2]|0)>=(c[ca>>2]|0))break;c[fa>>2]=c[$>>2];while(1){if((c[fa>>2]|0)>=(c[T>>2]|0))break;W=_(c[ba>>2]|0,c[da>>2]|0)|0;c[Q>>2]=W+(b[(c[S>>2]|0)+(c[fa>>2]<<1)>>1]<<c[L>>2]);c[P>>2]=(b[(c[S>>2]|0)+((c[fa>>2]|0)+1<<1)>>1]|0)-(b[(c[S>>2]|0)+(c[fa>>2]<<1)>>1]|0)<<c[L>>2];c[V>>2]=0;while(1){if((c[V>>2]|0)>=(c[P>>2]|0))break;c[Z>>2]=Ba(c[Z>>2]|0)|0;g[d+((c[Q>>2]|0)+(c[V>>2]|0)<<2)>>2]=+(c[Z>>2]>>20|0);c[V>>2]=(c[V>>2]|0)+1}td(d+(c[Q>>2]<<2)|0,c[P>>2]|0,1.0,c[(c[aa>>2]|0)+32>>2]|0);c[fa>>2]=(c[fa>>2]|0)+1}c[da>>2]=(c[da>>2]|0)+1}c[(c[aa>>2]|0)+36>>2]=c[Z>>2];c[da>>2]=0;do{qj(c[ea+(c[da>>2]<<2)>>2]|0,(c[ea+(c[da>>2]<<2)>>2]|0)+(c[ba>>2]<<2)|0,(2048-(c[ba>>2]|0)+(c[ja>>2]>>1)<<2)+0|0)|0;fa=(c[da>>2]|0)+1|0;c[da>>2]=fa}while((fa|0)<(c[ca>>2]|0));Nb(c[ha>>2]|0,d,Y,c[X>>2]|0,c[$>>2]|0,c[T>>2]|0,c[ca>>2]|0,c[ca>>2]|0,0,c[L>>2]|0,c[(c[aa>>2]|0)+16>>2]|0,0,c[(c[aa>>2]|0)+32>>2]|0);na(c[M>>2]|0);ha=c[ga>>2]|0;ha=ha+1|0;ja=c[aa>>2]|0;ja=ja+48|0;c[ja>>2]=ha;i=ka;return}g[A>>2]=1.0;if(!(c[ga>>2]|0)){$=Ob(ea,c[ca>>2]|0,c[(c[aa>>2]|0)+32>>2]|0)|0;c[F>>2]=$;c[(c[aa>>2]|0)+44>>2]=$}else{c[F>>2]=c[(c[aa>>2]|0)+44>>2];g[A>>2]=.800000011920929}$=c[ja>>2]|0;c[l>>2]=ia()|0;e=i;i=i+((1*($<<2)|0)+15&-16)|0;c[K>>2]=c[(c[ha>>2]|0)+60>>2];c[da>>2]=0;do{g[o>>2]=0.0;c[s>>2]=c[ea+(c[da>>2]<<2)>>2];c[fa>>2]=0;while(1){if((c[fa>>2]|0)>=1024)break;g[w+(c[fa>>2]<<2)>>2]=+g[(c[s>>2]|0)+(1024+(c[fa>>2]|0)<<2)>>2];c[fa>>2]=(c[fa>>2]|0)+1}if(!(c[ga>>2]|0)){Yc(w,q,c[K>>2]|0,c[ja>>2]|0,24,1024,c[(c[aa>>2]|0)+32>>2]|0)|0;g[q>>2]=+g[q>>2]*1.000100016593933;c[fa>>2]=1;while(1){if((c[fa>>2]|0)>24)break;ha=q+(c[fa>>2]<<2)|0;g[ha>>2]=+g[ha>>2]-+g[q+(c[fa>>2]<<2)>>2]*6.400000711437315e-05*+(c[fa>>2]|0)*+(c[fa>>2]|0);c[fa>>2]=(c[fa>>2]|0)+1}Uc((c[C>>2]|0)+((c[da>>2]|0)*24<<2)|0,q,24)}c[x>>2]=(c[F>>2]<<1|0)<1024?c[F>>2]<<1:1024;c[fa>>2]=0;while(1){if((c[fa>>2]|0)>=24)break;g[D+(c[fa>>2]<<2)>>2]=+g[(c[s>>2]|0)+(2048-(c[x>>2]|0)-1-(c[fa>>2]|0)<<2)>>2];c[fa>>2]=(c[fa>>2]|0)+1}Vc(w+4096+(0-(c[x>>2]|0)<<2)|0,(c[C>>2]|0)+((c[da>>2]|0)*24<<2)|0,w+4096+(0-(c[x>>2]|0)<<2)|0,c[x>>2]|0,24,D,c[(c[aa>>2]|0)+32>>2]|0);g[m>>2]=1.0;g[n>>2]=1.0;c[u>>2]=c[x>>2]>>1;c[fa>>2]=0;while(1){if((c[fa>>2]|0)>=(c[u>>2]|0))break;g[v>>2]=+g[w+(1024-(c[u>>2]|0)+(c[fa>>2]|0)<<2)>>2];g[m>>2]=+g[m>>2]+ +g[v>>2]*+g[v>>2];g[v>>2]=+g[w+(1024-(c[u>>2]<<1)+(c[fa>>2]|0)<<2)>>2];g[n>>2]=+g[n>>2]+ +g[v>>2]*+g[v>>2];c[fa>>2]=(c[fa>>2]|0)+1}g[m>>2]=+g[m>>2]<+g[n>>2]?+g[m>>2]:+g[n>>2];g[t>>2]=+O(+(+g[m>>2]/+g[n>>2]));qj(c[s>>2]|0,(c[s>>2]|0)+(c[ba>>2]<<2)|0,(2048-(c[ba>>2]|0)<<2)+0|0)|0;c[z>>2]=1024-(c[F>>2]|0);c[y>>2]=(c[ba>>2]|0)+(c[ja>>2]|0);g[r>>2]=+g[A>>2]*+g[t>>2];c[B>>2]=0;c[fa>>2]=0;while(1){if((c[fa>>2]|0)>=(c[y>>2]|0))break;if((c[B>>2]|0)>=(c[F>>2]|0)){c[B>>2]=(c[B>>2]|0)-(c[F>>2]|0);g[r>>2]=+g[r>>2]*+g[t>>2]}g[(c[s>>2]|0)+(2048-(c[ba>>2]|0)+(c[fa>>2]|0)<<2)>>2]=+g[r>>2]*+g[w+((c[z>>2]|0)+(c[B>>2]|0)<<2)>>2];g[H>>2]=+g[(c[s>>2]|0)+(1024-(c[ba>>2]|0)+(c[z>>2]|0)+(c[B>>2]|0)<<2)>>2];g[o>>2]=+g[o>>2]+ +g[H>>2]*+g[H>>2];c[fa>>2]=(c[fa>>2]|0)+1;c[B>>2]=(c[B>>2]|0)+1}c[fa>>2]=0;while(1){if((c[fa>>2]|0)>=24)break;g[E+(c[fa>>2]<<2)>>2]=+g[(c[s>>2]|0)+(2048-(c[ba>>2]|0)-1-(c[fa>>2]|0)<<2)>>2];c[fa>>2]=(c[fa>>2]|0)+1}Xc((c[s>>2]|0)+8192+(0-(c[ba>>2]|0)<<2)|0,(c[C>>2]|0)+((c[da>>2]|0)*24<<2)|0,(c[s>>2]|0)+8192+(0-(c[ba>>2]|0)<<2)|0,c[y>>2]|0,24,E,c[(c[aa>>2]|0)+32>>2]|0);g[p>>2]=0.0;c[fa>>2]=0;while(1){if((c[fa>>2]|0)>=(c[y>>2]|0))break;g[I>>2]=+g[(c[s>>2]|0)+(2048-(c[ba>>2]|0)+(c[fa>>2]|0)<<2)>>2];g[p>>2]=+g[p>>2]+ +g[I>>2]*+g[I>>2];c[fa>>2]=(c[fa>>2]|0)+1}a:do if(+g[o>>2]>+g[p>>2]*.20000000298023224){if(+g[o>>2]<+g[p>>2]){g[G>>2]=+O(+((+g[o>>2]+1.0)/(+g[p>>2]+1.0)));c[fa>>2]=0;while(1){if((c[fa>>2]|0)>=(c[ja>>2]|0))break;g[J>>2]=1.0-+g[(c[K>>2]|0)+(c[fa>>2]<<2)>>2]*(1.0-+g[G>>2]);g[(c[s>>2]|0)+(2048-(c[ba>>2]|0)+(c[fa>>2]|0)<<2)>>2]=+g[J>>2]*+g[(c[s>>2]|0)+(2048-(c[ba>>2]|0)+(c[fa>>2]|0)<<2)>>2];c[fa>>2]=(c[fa>>2]|0)+1}c[fa>>2]=c[ja>>2];while(1){if((c[fa>>2]|0)>=(c[y>>2]|0))break a;g[(c[s>>2]|0)+(2048-(c[ba>>2]|0)+(c[fa>>2]|0)<<2)>>2]=+g[G>>2]*+g[(c[s>>2]|0)+(2048-(c[ba>>2]|0)+(c[fa>>2]|0)<<2)>>2];c[fa>>2]=(c[fa>>2]|0)+1}}}else{c[fa>>2]=0;while(1){if((c[fa>>2]|0)>=(c[y>>2]|0))break a;g[(c[s>>2]|0)+(2048-(c[ba>>2]|0)+(c[fa>>2]|0)<<2)>>2]=0.0;c[fa>>2]=(c[fa>>2]|0)+1}}while(0);cb(e,(c[s>>2]|0)+8192|0,c[(c[aa>>2]|0)+52>>2]|0,c[(c[aa>>2]|0)+52>>2]|0,c[ja>>2]|0,-+g[(c[aa>>2]|0)+60>>2],-+g[(c[aa>>2]|0)+60>>2],c[(c[aa>>2]|0)+68>>2]|0,c[(c[aa>>2]|0)+68>>2]|0,0,0,c[(c[aa>>2]|0)+32>>2]|0);c[fa>>2]=0;while(1){if((c[fa>>2]|0)>=((c[ja>>2]|0)/2|0|0))break;g[(c[s>>2]|0)+(2048+(c[fa>>2]|0)<<2)>>2]=+g[(c[K>>2]|0)+(c[fa>>2]<<2)>>2]*+g[e+((c[ja>>2]|0)-1-(c[fa>>2]|0)<<2)>>2]+ +g[(c[K>>2]|0)+((c[ja>>2]|0)-(c[fa>>2]|0)-1<<2)>>2]*+g[e+(c[fa>>2]<<2)>>2];c[fa>>2]=(c[fa>>2]|0)+1}ha=(c[da>>2]|0)+1|0;c[da>>2]=ha}while((ha|0)<(c[ca>>2]|0));na(c[l>>2]|0);ha=c[ga>>2]|0;ha=ha+1|0;ja=c[aa>>2]|0;ja=ja+48|0;c[ja>>2]=ha;i=ka;return}function Nb(a,b,d,e,f,h,j,k,l,m,n,o,p){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;p=p|0;var q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0;P=i;i=i+112|0;q=P+100|0;r=P+96|0;w=P+92|0;x=P+88|0;y=P+84|0;z=P+80|0;A=P+76|0;B=P+72|0;Q=P+68|0;C=P+64|0;s=P+60|0;t=P+56|0;u=P+52|0;I=P+48|0;L=P+44|0;E=P+40|0;H=P+36|0;D=P+32|0;F=P+28|0;G=P+24|0;O=P+20|0;M=P+16|0;N=P+12|0;v=P+8|0;J=P+4|0;K=P;c[q>>2]=a;c[r>>2]=b;c[w>>2]=d;c[x>>2]=e;c[y>>2]=f;c[z>>2]=h;c[A>>2]=j;c[B>>2]=k;c[Q>>2]=l;c[C>>2]=m;c[s>>2]=n;c[t>>2]=o;c[u>>2]=p;c[N>>2]=c[(c[q>>2]|0)+4>>2];c[M>>2]=c[(c[q>>2]|0)+8>>2];c[F>>2]=c[(c[q>>2]|0)+44>>2]<<c[C>>2];f=c[F>>2]|0;c[v>>2]=ia()|0;j=i;i=i+((1*(f<<2)|0)+15&-16)|0;c[E>>2]=1<<c[C>>2];if(c[Q>>2]|0){c[D>>2]=c[E>>2];c[G>>2]=c[(c[q>>2]|0)+44>>2];c[O>>2]=c[(c[q>>2]|0)+36>>2]}else{c[D>>2]=1;c[G>>2]=c[(c[q>>2]|0)+44>>2]<<c[C>>2];c[O>>2]=(c[(c[q>>2]|0)+36>>2]|0)-(c[C>>2]|0)}if((c[B>>2]|0)==2&(c[A>>2]|0)==1){Fa(c[q>>2]|0,c[r>>2]|0,j,c[x>>2]|0,c[y>>2]|0,c[z>>2]|0,c[E>>2]|0,c[s>>2]|0,c[t>>2]|0);c[J>>2]=(c[(c[w>>2]|0)+4>>2]|0)+(((c[N>>2]|0)/2|0)<<2);pj(c[J>>2]|0,j|0,(c[F>>2]<<2)+0|0)|0;c[H>>2]=0;while(1){if((c[H>>2]|0)>=(c[D>>2]|0))break;Q=(c[c[w>>2]>>2]|0)+((_(c[G>>2]|0,c[H>>2]|0)|0)<<2)|0;Ic((c[q>>2]|0)+64|0,(c[J>>2]|0)+(c[H>>2]<<2)|0,Q,c[(c[q>>2]|0)+60>>2]|0,c[N>>2]|0,c[O>>2]|0,c[D>>2]|0,c[u>>2]|0);c[H>>2]=(c[H>>2]|0)+1}c[H>>2]=0;while(1){if((c[H>>2]|0)>=(c[D>>2]|0))break;Q=(c[(c[w>>2]|0)+4>>2]|0)+((_(c[G>>2]|0,c[H>>2]|0)|0)<<2)|0;Ic((c[q>>2]|0)+64|0,j+(c[H>>2]<<2)|0,Q,c[(c[q>>2]|0)+60>>2]|0,c[N>>2]|0,c[O>>2]|0,c[D>>2]|0,c[u>>2]|0);c[H>>2]=(c[H>>2]|0)+1}Q=c[v>>2]|0;na(Q|0);i=P;return}if(!((c[B>>2]|0)==1&(c[A>>2]|0)==2)){c[I>>2]=0;do{L=(c[r>>2]|0)+((_(c[I>>2]|0,c[F>>2]|0)|0)<<2)|0;Q=(c[x>>2]|0)+((_(c[I>>2]|0,c[M>>2]|0)|0)<<2)|0;Fa(c[q>>2]|0,L,j,Q,c[y>>2]|0,c[z>>2]|0,c[E>>2]|0,c[s>>2]|0,c[t>>2]|0);c[H>>2]=0;while(1){if((c[H>>2]|0)>=(c[D>>2]|0))break;Q=(c[(c[w>>2]|0)+(c[I>>2]<<2)>>2]|0)+((_(c[G>>2]|0,c[H>>2]|0)|0)<<2)|0;Ic((c[q>>2]|0)+64|0,j+(c[H>>2]<<2)|0,Q,c[(c[q>>2]|0)+60>>2]|0,c[N>>2]|0,c[O>>2]|0,c[D>>2]|0,c[u>>2]|0);c[H>>2]=(c[H>>2]|0)+1}Q=(c[I>>2]|0)+1|0;c[I>>2]=Q}while((Q|0)<(c[B>>2]|0));Q=c[v>>2]|0;na(Q|0);i=P;return}c[K>>2]=(c[c[w>>2]>>2]|0)+(((c[N>>2]|0)/2|0)<<2);Fa(c[q>>2]|0,c[r>>2]|0,j,c[x>>2]|0,c[y>>2]|0,c[z>>2]|0,c[E>>2]|0,c[s>>2]|0,c[t>>2]|0);Fa(c[q>>2]|0,(c[r>>2]|0)+(c[F>>2]<<2)|0,c[K>>2]|0,(c[x>>2]|0)+(c[M>>2]<<2)|0,c[y>>2]|0,c[z>>2]|0,c[E>>2]|0,c[s>>2]|0,c[t>>2]|0);c[L>>2]=0;while(1){if((c[L>>2]|0)>=(c[F>>2]|0))break;g[j+(c[L>>2]<<2)>>2]=(+g[j+(c[L>>2]<<2)>>2]+ +g[(c[K>>2]|0)+(c[L>>2]<<2)>>2])*.5;c[L>>2]=(c[L>>2]|0)+1}c[H>>2]=0;while(1){if((c[H>>2]|0)>=(c[D>>2]|0))break;Q=(c[c[w>>2]>>2]|0)+((_(c[G>>2]|0,c[H>>2]|0)|0)<<2)|0;Ic((c[q>>2]|0)+64|0,j+(c[H>>2]<<2)|0,Q,c[(c[q>>2]|0)+60>>2]|0,c[N>>2]|0,c[O>>2]|0,c[D>>2]|0,c[u>>2]|0);c[H>>2]=(c[H>>2]|0)+1}Q=c[v>>2]|0;na(Q|0);i=P;return}function Ob(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0;f=i;i=i+4112|0;k=f+4108|0;j=f+4104|0;g=f+4100|0;e=f+4096|0;h=f;c[k>>2]=a;c[j>>2]=b;c[g>>2]=d;Kc(c[k>>2]|0,h,2048,c[j>>2]|0,c[g>>2]|0);Pc(h+1440|0,h,1328,620,e,c[g>>2]|0);c[e>>2]=720-(c[e>>2]|0);i=f;return c[e>>2]|0}function Pb(a,b,d,e,f,h,j,k){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;var l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0.0;C=i;i=i+80|0;l=C+72|0;m=C+68|0;n=C+64|0;o=C+60|0;p=C+56|0;D=C+52|0;q=C+48|0;u=C+40|0;s=C+36|0;t=C+32|0;v=C+28|0;r=C+24|0;w=C+20|0;A=C+16|0;B=C+12|0;x=C+8|0;y=C+4|0;z=C;c[l>>2]=a;c[m>>2]=b;c[n>>2]=d;c[o>>2]=e;c[p>>2]=f;c[D>>2]=h;c[q>>2]=j;c[C+44>>2]=k;c[t>>2]=0;b=c[n>>2]|0;c[r>>2]=ia()|0;e=i;i=i+((1*(b<<2)|0)+15&-16)|0;g[v>>2]=+g[c[D>>2]>>2];c[s>>2]=(c[n>>2]|0)/(c[p>>2]|0)|0;c[u>>2]=0;do{g[x>>2]=+g[(c[q>>2]|0)+(c[u>>2]<<2)>>2];c[A>>2]=c[(c[l>>2]|0)+(c[u>>2]<<2)>>2];c[B>>2]=(c[m>>2]|0)+(c[u>>2]<<2);D=(c[p>>2]|0)>1;c[w>>2]=0;a:do if(D){while(1){if((c[w>>2]|0)>=(c[n>>2]|0))break;g[y>>2]=+g[(c[A>>2]|0)+(c[w>>2]<<2)>>2]+ +g[x>>2]+1.0000000031710769e-30;g[x>>2]=+g[v>>2]*+g[y>>2];g[e+(c[w>>2]<<2)>>2]=+g[y>>2];c[w>>2]=(c[w>>2]|0)+1}c[t>>2]=1}else while(1){if((c[w>>2]|0)>=(c[n>>2]|0))break a;g[z>>2]=+g[(c[A>>2]|0)+(c[w>>2]<<2)>>2]+ +g[x>>2]+1.0000000031710769e-30;g[x>>2]=+g[v>>2]*+g[z>>2];D=_(c[w>>2]|0,c[o>>2]|0)|0;g[(c[B>>2]|0)+(D<<2)>>2]=+g[z>>2]*.000030517578125;c[w>>2]=(c[w>>2]|0)+1}while(0);g[(c[q>>2]|0)+(c[u>>2]<<2)>>2]=+g[x>>2];b:do if(c[t>>2]|0){c[w>>2]=0;while(1){if((c[w>>2]|0)>=(c[s>>2]|0))break b;E=+g[e+((_(c[w>>2]|0,c[p>>2]|0)|0)<<2)>>2]*.000030517578125;D=_(c[w>>2]|0,c[o>>2]|0)|0;g[(c[B>>2]|0)+(D<<2)>>2]=E;c[w>>2]=(c[w>>2]|0)+1}}while(0);D=(c[u>>2]|0)+1|0;c[u>>2]=D}while((D|0)<(c[o>>2]|0));na(c[r>>2]|0);i=C;return}function Qb(a){a=a|0;var b=0,d=0;b=i;i=i+16|0;d=b;c[d>>2]=a;a=(c[(c[d>>2]|0)+20>>2]|0)-(32-(aa(c[(c[d>>2]|0)+28>>2]|0)|0))|0;i=b;return a|0}function Rb(b,d,e,f,g,h){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0;x=i;i=i+64|0;j=x+52|0;k=x+48|0;l=x+44|0;m=x+40|0;n=x+36|0;o=x+32|0;r=x+28|0;q=x+24|0;v=x+20|0;w=x+16|0;u=x+12|0;s=x+8|0;p=x+4|0;t=x;c[j>>2]=b;c[k>>2]=d;c[l>>2]=e;c[m>>2]=f;c[n>>2]=g;c[o>>2]=h;c[p>>2]=c[(c[o>>2]|0)+4>>2]<<3;c[t>>2]=Qb(c[o>>2]|0)|0;c[s>>2]=c[l>>2]|0?2:4;if((c[n>>2]|0)>0)g=((c[t>>2]|0)+(c[s>>2]|0)+1|0)>>>0<=(c[p>>2]|0)>>>0;else g=0;c[w>>2]=g&1;c[p>>2]=(c[p>>2]|0)-(c[w>>2]|0);c[q>>2]=0;c[u>>2]=0;c[r>>2]=c[j>>2];while(1){if((c[r>>2]|0)>=(c[k>>2]|0))break;if(((c[t>>2]|0)+(c[s>>2]|0)|0)>>>0<=(c[p>>2]|0)>>>0){f=dc(c[o>>2]|0,c[s>>2]|0)|0;c[q>>2]=c[q>>2]^f;c[t>>2]=Qb(c[o>>2]|0)|0;c[u>>2]=c[u>>2]|c[q>>2]}c[(c[m>>2]|0)+(c[r>>2]<<2)>>2]=c[q>>2];c[s>>2]=c[l>>2]|0?4:5;c[r>>2]=(c[r>>2]|0)+1}c[v>>2]=0;if(c[w>>2]|0?(a[25232+(c[n>>2]<<3)+((c[l>>2]<<2)+0+(c[u>>2]|0))>>0]|0)!=(a[25232+(c[n>>2]<<3)+((c[l>>2]<<2)+2+(c[u>>2]|0))>>0]|0):0)c[v>>2]=dc(c[o>>2]|0,1)|0;c[r>>2]=c[j>>2];while(1){if((c[r>>2]|0)>=(c[k>>2]|0))break;c[(c[m>>2]|0)+(c[r>>2]<<2)>>2]=a[25232+(c[n>>2]<<3)+((c[l>>2]<<2)+(c[v>>2]<<1)+(c[(c[m>>2]|0)+(c[r>>2]<<2)>>2]|0))>>0];c[r>>2]=(c[r>>2]|0)+1}i=x;return}function Sb(a){a=a|0;var b=0,d=0;d=i;i=i+16|0;b=d;c[b>>2]=a;i=d;return c[(c[b>>2]|0)+44>>2]|0}function Tb(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0;f=i;i=i+16|0;j=f+12|0;h=f+8|0;g=f+4|0;k=f;c[j>>2]=a;c[h>>2]=b;c[g>>2]=d;c[k>>2]=e;b=c[k>>2]|0;a=Ub(c[h>>2]|0,c[j>>2]|0)|0;rc(b,a,(c[(c[364+(((c[h>>2]|0)<(c[g>>2]|0)?c[h>>2]|0:c[g>>2]|0)<<2)>>2]|0)+(((c[h>>2]|0)>(c[g>>2]|0)?c[h>>2]|0:c[g>>2]|0)<<2)>>2]|0)+(c[(c[364+(((c[h>>2]|0)<((c[g>>2]|0)+1|0)?c[h>>2]|0:(c[g>>2]|0)+1|0)<<2)>>2]|0)+(((c[h>>2]|0)>((c[g>>2]|0)+1|0)?c[h>>2]|0:(c[g>>2]|0)+1|0)<<2)>>2]|0)|0);i=f;return}function Ub(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0;j=i;i=i+32|0;d=j+16|0;e=j+12|0;f=j+8|0;g=j+4|0;h=j;c[d>>2]=a;c[e>>2]=b;c[g>>2]=(c[d>>2]|0)-1;c[f>>2]=(c[(c[e>>2]|0)+(c[g>>2]<<2)>>2]|0)<0&1;c[h>>2]=N(c[(c[e>>2]|0)+(c[g>>2]<<2)>>2]|0)|0;do{c[g>>2]=(c[g>>2]|0)+-1;if(((c[d>>2]|0)-(c[g>>2]|0)|0)>(c[h>>2]|0))b=(c[d>>2]|0)-(c[g>>2]|0)|0;else b=c[h>>2]|0;if(((c[d>>2]|0)-(c[g>>2]|0)|0)<(c[h>>2]|0))a=(c[d>>2]|0)-(c[g>>2]|0)|0;else a=c[h>>2]|0;c[f>>2]=(c[f>>2]|0)+(c[(c[364+(a<<2)>>2]|0)+(b<<2)>>2]|0);b=N(c[(c[e>>2]|0)+(c[g>>2]<<2)>>2]|0)|0;c[h>>2]=(c[h>>2]|0)+b;if((c[(c[e>>2]|0)+(c[g>>2]<<2)>>2]|0)<0){if(((c[d>>2]|0)-(c[g>>2]|0)|0)>((c[h>>2]|0)+1|0))b=(c[d>>2]|0)-(c[g>>2]|0)|0;else b=(c[h>>2]|0)+1|0;if(((c[d>>2]|0)-(c[g>>2]|0)|0)<((c[h>>2]|0)+1|0))a=(c[d>>2]|0)-(c[g>>2]|0)|0;else a=(c[h>>2]|0)+1|0;c[f>>2]=(c[f>>2]|0)+(c[(c[364+(a<<2)>>2]|0)+(b<<2)>>2]|0)}}while((c[g>>2]|0)>0);i=j;return c[f>>2]|0}function Vb(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0.0,g=0,h=0,j=0,k=0,l=0;g=i;i=i+16|0;h=g+12|0;k=g+8|0;j=g+4|0;l=g;c[h>>2]=a;c[k>>2]=b;c[j>>2]=d;c[l>>2]=e;d=c[k>>2]|0;b=c[j>>2]|0;a=fc(c[l>>2]|0,(c[(c[364+(((c[k>>2]|0)<(c[j>>2]|0)?c[k>>2]|0:c[j>>2]|0)<<2)>>2]|0)+(((c[k>>2]|0)>(c[j>>2]|0)?c[k>>2]|0:c[j>>2]|0)<<2)>>2]|0)+(c[(c[364+(((c[k>>2]|0)<((c[j>>2]|0)+1|0)?c[k>>2]|0:(c[j>>2]|0)+1|0)<<2)>>2]|0)+(((c[k>>2]|0)>((c[j>>2]|0)+1|0)?c[k>>2]|0:(c[j>>2]|0)+1|0)<<2)>>2]|0)|0)|0;f=+Wb(d,b,a,c[h>>2]|0);i=g;return +f}function Wb(a,d,e,f){a=a|0;d=d|0;e=e|0;f=f|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0.0,v=0.0,w=0.0;t=i;i=i+48|0;h=t+36|0;j=t+32|0;k=t+28|0;l=t+24|0;n=t+20|0;q=t+16|0;m=t+12|0;r=t+40|0;s=t+8|0;o=t+4|0;p=t;c[h>>2]=a;c[j>>2]=d;c[k>>2]=e;c[l>>2]=f;g[s>>2]=0.0;while(1){e=c[j>>2]|0;if((c[h>>2]|0)<=2)break;d=c[h>>2]|0;do if((e|0)>=(c[h>>2]|0)){c[p>>2]=c[364+(d<<2)>>2];c[n>>2]=c[(c[p>>2]|0)+((c[j>>2]|0)+1<<2)>>2];c[q>>2]=0-((c[k>>2]|0)>>>0>=(c[n>>2]|0)>>>0&1);c[k>>2]=(c[k>>2]|0)-(c[n>>2]&c[q>>2]);c[m>>2]=c[j>>2];c[o>>2]=c[(c[p>>2]|0)+(c[h>>2]<<2)>>2];a:do if((c[o>>2]|0)>>>0>(c[k>>2]|0)>>>0){c[j>>2]=c[h>>2];do{f=c[h>>2]|0;a=(c[j>>2]|0)+-1|0;c[j>>2]=a;c[n>>2]=c[(c[364+(a<<2)>>2]|0)+(f<<2)>>2]}while((c[n>>2]|0)>>>0>(c[k>>2]|0)>>>0)}else{c[n>>2]=c[(c[p>>2]|0)+(c[j>>2]<<2)>>2];while(1){if((c[n>>2]|0)>>>0<=(c[k>>2]|0)>>>0)break a;c[j>>2]=(c[j>>2]|0)+-1;c[n>>2]=c[(c[p>>2]|0)+(c[j>>2]<<2)>>2]}}while(0);c[k>>2]=(c[k>>2]|0)-(c[n>>2]|0);b[r>>1]=(c[m>>2]|0)-(c[j>>2]|0)+(c[q>>2]|0)^c[q>>2];a=b[r>>1]|0;f=c[l>>2]|0;c[l>>2]=f+4;c[f>>2]=a;g[s>>2]=+g[s>>2]+ +(b[r>>1]|0)*+(b[r>>1]|0)}else{c[n>>2]=c[(c[364+(c[j>>2]<<2)>>2]|0)+(d<<2)>>2];c[o>>2]=c[(c[364+((c[j>>2]|0)+1<<2)>>2]|0)+(c[h>>2]<<2)>>2];if((c[n>>2]|0)>>>0<=(c[k>>2]|0)>>>0?(c[k>>2]|0)>>>0<(c[o>>2]|0)>>>0:0){c[k>>2]=(c[k>>2]|0)-(c[n>>2]|0);f=c[l>>2]|0;c[l>>2]=f+4;c[f>>2]=0;break}c[q>>2]=0-((c[k>>2]|0)>>>0>=(c[o>>2]|0)>>>0&1);c[k>>2]=(c[k>>2]|0)-(c[o>>2]&c[q>>2]);c[m>>2]=c[j>>2];do{f=c[h>>2]|0;a=(c[j>>2]|0)+-1|0;c[j>>2]=a;c[n>>2]=c[(c[364+(a<<2)>>2]|0)+(f<<2)>>2]}while((c[n>>2]|0)>>>0>(c[k>>2]|0)>>>0);c[k>>2]=(c[k>>2]|0)-(c[n>>2]|0);b[r>>1]=(c[m>>2]|0)-(c[j>>2]|0)+(c[q>>2]|0)^c[q>>2];a=b[r>>1]|0;f=c[l>>2]|0;c[l>>2]=f+4;c[f>>2]=a;g[s>>2]=+g[s>>2]+ +(b[r>>1]|0)*+(b[r>>1]|0)}while(0);c[h>>2]=(c[h>>2]|0)+-1}c[n>>2]=(e<<1)+1;c[q>>2]=0-((c[k>>2]|0)>>>0>=(c[n>>2]|0)>>>0&1);c[k>>2]=(c[k>>2]|0)-(c[n>>2]&c[q>>2]);c[m>>2]=c[j>>2];c[j>>2]=((c[k>>2]|0)+1|0)>>>1;if(!(c[j>>2]|0)){p=c[m>>2]|0;o=c[j>>2]|0;o=p-o|0;p=c[q>>2]|0;p=o+p|0;o=c[q>>2]|0;o=p^o;o=o&65535;b[r>>1]=o;o=b[r>>1]|0;o=o<<16>>16;p=c[l>>2]|0;n=p+4|0;c[l>>2]=n;c[p>>2]=o;w=+g[s>>2];p=b[r>>1]|0;u=+(p<<16>>16);p=b[r>>1]|0;v=+(p<<16>>16);v=u*v;v=w+v;g[s>>2]=v;p=c[k>>2]|0;p=0-p|0;c[q>>2]=p;p=c[j>>2]|0;o=c[q>>2]|0;o=p+o|0;p=c[q>>2]|0;p=o^p;p=p&65535;b[r>>1]=p;p=b[r>>1]|0;p=p<<16>>16;q=c[l>>2]|0;c[q>>2]=p;v=+g[s>>2];q=b[r>>1]|0;w=+(q<<16>>16);r=b[r>>1]|0;u=+(r<<16>>16);u=w*u;u=v+u;g[s>>2]=u;u=+g[s>>2];i=t;return +u}c[k>>2]=(c[k>>2]|0)-((c[j>>2]<<1)-1);p=c[m>>2]|0;o=c[j>>2]|0;o=p-o|0;p=c[q>>2]|0;p=o+p|0;o=c[q>>2]|0;o=p^o;o=o&65535;b[r>>1]=o;o=b[r>>1]|0;o=o<<16>>16;p=c[l>>2]|0;n=p+4|0;c[l>>2]=n;c[p>>2]=o;u=+g[s>>2];p=b[r>>1]|0;w=+(p<<16>>16);p=b[r>>1]|0;v=+(p<<16>>16);v=w*v;v=u+v;g[s>>2]=v;p=c[k>>2]|0;p=0-p|0;c[q>>2]=p;p=c[j>>2]|0;o=c[q>>2]|0;o=p+o|0;p=c[q>>2]|0;p=o^p;p=p&65535;b[r>>1]=p;p=b[r>>1]|0;p=p<<16>>16;q=c[l>>2]|0;c[q>>2]=p;v=+g[s>>2];q=b[r>>1]|0;u=+(q<<16>>16);r=b[r>>1]|0;w=+(r<<16>>16);w=u*w;w=v+w;g[s>>2]=w;w=+g[s>>2];i=t;return +w}function Xb(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0;e=i;i=i+32|0;h=e+16|0;d=e+12|0;g=e+8|0;b=e+4|0;f=e;c[h>>2]=a;c[d>>2]=c[(c[h>>2]|0)+20>>2]<<3;c[b>>2]=32-(aa(c[(c[h>>2]|0)+28>>2]|0)|0);c[g>>2]=(c[(c[h>>2]|0)+28>>2]|0)>>>((c[b>>2]|0)-16|0);c[f>>2]=((c[g>>2]|0)>>>12)-8;c[f>>2]=(c[f>>2]|0)+((c[g>>2]|0)>>>0>(c[5512+(c[f>>2]<<2)>>2]|0)>>>0&1);c[b>>2]=(c[b>>2]<<3)+(c[f>>2]|0);i=e;return (c[d>>2]|0)-(c[b>>2]|0)|0}function Yb(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0;e=i;i=i+16|0;f=e+8|0;h=e+4|0;g=e;c[f>>2]=a;c[h>>2]=b;c[g>>2]=d;c[c[f>>2]>>2]=c[h>>2];c[(c[f>>2]|0)+4>>2]=c[g>>2];c[(c[f>>2]|0)+8>>2]=0;c[(c[f>>2]|0)+12>>2]=0;c[(c[f>>2]|0)+16>>2]=0;c[(c[f>>2]|0)+20>>2]=9;c[(c[f>>2]|0)+24>>2]=0;c[(c[f>>2]|0)+28>>2]=128;a=Zb(c[f>>2]|0)|0;c[(c[f>>2]|0)+40>>2]=a;c[(c[f>>2]|0)+32>>2]=(c[(c[f>>2]|0)+28>>2]|0)-1-(c[(c[f>>2]|0)+40>>2]>>1);c[(c[f>>2]|0)+44>>2]=0;_b(c[f>>2]|0);i=e;return}function Zb(a){a=a|0;var b=0,e=0,f=0;e=i;i=i+16|0;b=e;c[b>>2]=a;if((c[(c[b>>2]|0)+24>>2]|0)>>>0>=(c[(c[b>>2]|0)+4>>2]|0)>>>0){a=0;i=e;return a|0}f=(c[b>>2]|0)+24|0;a=c[f>>2]|0;c[f>>2]=a+1;a=d[(c[c[b>>2]>>2]|0)+a>>0]|0;i=e;return a|0}function _b(a){a=a|0;var b=0,d=0,e=0;e=i;i=i+16|0;b=e+4|0;d=e;c[b>>2]=a;while(1){if((c[(c[b>>2]|0)+28>>2]|0)>>>0>8388608)break;a=(c[b>>2]|0)+20|0;c[a>>2]=(c[a>>2]|0)+8;a=(c[b>>2]|0)+28|0;c[a>>2]=c[a>>2]<<8;c[d>>2]=c[(c[b>>2]|0)+40>>2];a=Zb(c[b>>2]|0)|0;c[(c[b>>2]|0)+40>>2]=a;c[d>>2]=(c[d>>2]<<8|c[(c[b>>2]|0)+40>>2])>>1;c[(c[b>>2]|0)+32>>2]=(c[(c[b>>2]|0)+32>>2]<<8)+(255&~c[d>>2])&2147483647}i=e;return}function $b(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0;f=i;i=i+16|0;g=f+8|0;e=f+4|0;d=f;c[g>>2]=a;c[e>>2]=b;a=ac(c[(c[g>>2]|0)+28>>2]|0,c[e>>2]|0)|0;c[(c[g>>2]|0)+36>>2]=a;c[d>>2]=((c[(c[g>>2]|0)+32>>2]|0)>>>0)/((c[(c[g>>2]|0)+36>>2]|0)>>>0)|0;i=f;return (c[e>>2]|0)-((c[d>>2]|0)+1+((c[e>>2]|0)-((c[d>>2]|0)+1)&0-((c[e>>2]|0)>>>0<((c[d>>2]|0)+1|0)>>>0&1)))|0}function ac(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)>>>0)/((c[d>>2]|0)>>>0)|0|0}function bc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0;f=i;i=i+16|0;g=f+8|0;e=f+4|0;d=f;c[g>>2]=a;c[e>>2]=b;c[(c[g>>2]|0)+36>>2]=(c[(c[g>>2]|0)+28>>2]|0)>>>(c[e>>2]|0);c[d>>2]=((c[(c[g>>2]|0)+32>>2]|0)>>>0)/((c[(c[g>>2]|0)+36>>2]|0)>>>0)|0;i=f;return (1<<c[e>>2])-((c[d>>2]|0)+1+((1<<c[e>>2])-((c[d>>2]|0)+1)&0-(1<<c[e>>2]>>>0<((c[d>>2]|0)+1|0)>>>0&1)))|0}function cc(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0;k=i;i=i+32|0;f=k+16|0;g=k+12|0;h=k+8|0;l=k+4|0;j=k;c[f>>2]=a;c[g>>2]=b;c[h>>2]=d;c[l>>2]=e;c[j>>2]=_(c[(c[f>>2]|0)+36>>2]|0,(c[l>>2]|0)-(c[h>>2]|0)|0)|0;d=(c[f>>2]|0)+32|0;c[d>>2]=(c[d>>2]|0)-(c[j>>2]|0);d=c[f>>2]|0;if((c[g>>2]|0)>>>0>0){j=_(c[d+36>>2]|0,(c[h>>2]|0)-(c[g>>2]|0)|0)|0;l=c[f>>2]|0;l=l+28|0;c[l>>2]=j;l=c[f>>2]|0;_b(l);i=k;return}else{j=(c[d+28>>2]|0)-(c[j>>2]|0)|0;l=c[f>>2]|0;l=l+28|0;c[l>>2]=j;l=c[f>>2]|0;_b(l);i=k;return}}function dc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0;j=i;i=i+32|0;d=j+20|0;k=j+16|0;f=j+12|0;e=j+8|0;h=j+4|0;g=j;c[d>>2]=a;c[k>>2]=b;c[f>>2]=c[(c[d>>2]|0)+28>>2];c[e>>2]=c[(c[d>>2]|0)+32>>2];c[h>>2]=(c[f>>2]|0)>>>(c[k>>2]|0);c[g>>2]=(c[e>>2]|0)>>>0<(c[h>>2]|0)>>>0&1;if(!(c[g>>2]|0))c[(c[d>>2]|0)+32>>2]=(c[e>>2]|0)-(c[h>>2]|0);if(c[g>>2]|0){h=c[h>>2]|0;k=c[d>>2]|0;k=k+28|0;c[k>>2]=h;k=c[d>>2]|0;_b(k);k=c[g>>2]|0;i=j;return k|0}else{h=(c[f>>2]|0)-(c[h>>2]|0)|0;k=c[d>>2]|0;k=k+28|0;c[k>>2]=h;k=c[d>>2]|0;_b(k);k=c[g>>2]|0;i=j;return k|0}return 0}function ec(a,b,e){a=a|0;b=b|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0;n=i;i=i+32|0;f=n+28|0;g=n+24|0;o=n+20|0;j=n+16|0;h=n+12|0;l=n+8|0;m=n+4|0;k=n;c[f>>2]=a;c[g>>2]=b;c[o>>2]=e;c[l>>2]=c[(c[f>>2]|0)+28>>2];c[h>>2]=c[(c[f>>2]|0)+32>>2];c[j>>2]=(c[l>>2]|0)>>>(c[o>>2]|0);c[k>>2]=-1;do{c[m>>2]=c[l>>2];a=c[j>>2]|0;o=(c[k>>2]|0)+1|0;c[k>>2]=o;c[l>>2]=_(a,d[(c[g>>2]|0)+o>>0]|0)|0}while((c[h>>2]|0)>>>0<(c[l>>2]|0)>>>0);c[(c[f>>2]|0)+32>>2]=(c[h>>2]|0)-(c[l>>2]|0);c[(c[f>>2]|0)+28>>2]=(c[m>>2]|0)-(c[l>>2]|0);_b(c[f>>2]|0);i=n;return c[k>>2]|0}function fc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0;l=i;i=i+32|0;d=l+24|0;e=l+20|0;f=l+16|0;g=l+12|0;j=l+8|0;h=l+4|0;k=l;c[e>>2]=a;c[f>>2]=b;c[f>>2]=(c[f>>2]|0)+-1;c[h>>2]=32-(aa(c[f>>2]|0)|0);if((c[h>>2]|0)<=8){c[f>>2]=(c[f>>2]|0)+1;c[j>>2]=$b(c[e>>2]|0,c[f>>2]|0)|0;cc(c[e>>2]|0,c[j>>2]|0,(c[j>>2]|0)+1|0,c[f>>2]|0);c[d>>2]=c[j>>2];k=c[d>>2]|0;i=l;return k|0}c[h>>2]=(c[h>>2]|0)-8;c[g>>2]=((c[f>>2]|0)>>>(c[h>>2]|0))+1;c[j>>2]=$b(c[e>>2]|0,c[g>>2]|0)|0;cc(c[e>>2]|0,c[j>>2]|0,(c[j>>2]|0)+1|0,c[g>>2]|0);j=c[j>>2]<<c[h>>2];c[k>>2]=j|(gc(c[e>>2]|0,c[h>>2]|0)|0);if((c[k>>2]|0)>>>0<=(c[f>>2]|0)>>>0){c[d>>2]=c[k>>2];k=c[d>>2]|0;i=l;return k|0}else{c[(c[e>>2]|0)+44>>2]=1;c[d>>2]=c[f>>2];k=c[d>>2]|0;i=l;return k|0}return 0}function gc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0;j=i;i=i+32|0;d=j+16|0;e=j+12|0;h=j+8|0;f=j+4|0;g=j;c[d>>2]=a;c[e>>2]=b;c[h>>2]=c[(c[d>>2]|0)+12>>2];c[f>>2]=c[(c[d>>2]|0)+16>>2];if((c[f>>2]|0)>>>0<(c[e>>2]|0)>>>0)do{a=hc(c[d>>2]|0)|0;c[h>>2]=c[h>>2]|a<<c[f>>2];c[f>>2]=(c[f>>2]|0)+8}while((c[f>>2]|0)<=24);c[g>>2]=c[h>>2]&(1<<c[e>>2])-1;c[h>>2]=(c[h>>2]|0)>>>(c[e>>2]|0);c[f>>2]=(c[f>>2]|0)-(c[e>>2]|0);c[(c[d>>2]|0)+12>>2]=c[h>>2];c[(c[d>>2]|0)+16>>2]=c[f>>2];h=(c[d>>2]|0)+20|0;c[h>>2]=(c[h>>2]|0)+(c[e>>2]|0);i=j;return c[g>>2]|0}function hc(a){a=a|0;var b=0,e=0,f=0,g=0;e=i;i=i+16|0;b=e;c[b>>2]=a;if((c[(c[b>>2]|0)+8>>2]|0)>>>0>=(c[(c[b>>2]|0)+4>>2]|0)>>>0){a=0;i=e;return a|0}f=c[(c[b>>2]|0)+4>>2]|0;g=(c[b>>2]|0)+8|0;a=(c[g>>2]|0)+1|0;c[g>>2]=a;a=d[(c[c[b>>2]>>2]|0)+(f-a)>>0]|0;i=e;return a|0}function ic(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0;e=i;i=i+16|0;f=e+8|0;h=e+4|0;g=e;c[f>>2]=a;c[h>>2]=b;c[g>>2]=d;c[c[f>>2]>>2]=c[h>>2];c[(c[f>>2]|0)+8>>2]=0;c[(c[f>>2]|0)+12>>2]=0;c[(c[f>>2]|0)+16>>2]=0;c[(c[f>>2]|0)+20>>2]=33;c[(c[f>>2]|0)+24>>2]=0;c[(c[f>>2]|0)+28>>2]=-2147483648;c[(c[f>>2]|0)+40>>2]=-1;c[(c[f>>2]|0)+32>>2]=0;c[(c[f>>2]|0)+36>>2]=0;c[(c[f>>2]|0)+4>>2]=c[g>>2];c[(c[f>>2]|0)+44>>2]=0;i=e;return}function jc(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0;l=i;i=i+32|0;f=l+16|0;g=l+12|0;h=l+8|0;j=l+4|0;k=l;c[f>>2]=a;c[g>>2]=b;c[h>>2]=d;c[j>>2]=e;c[k>>2]=kc(c[(c[f>>2]|0)+28>>2]|0,c[j>>2]|0)|0;if((c[g>>2]|0)>>>0>0){e=(c[(c[f>>2]|0)+28>>2]|0)-(_(c[k>>2]|0,(c[j>>2]|0)-(c[g>>2]|0)|0)|0)|0;a=(c[f>>2]|0)+32|0;c[a>>2]=(c[a>>2]|0)+e;k=_(c[k>>2]|0,(c[h>>2]|0)-(c[g>>2]|0)|0)|0;c[(c[f>>2]|0)+28>>2]=k;k=c[f>>2]|0;lc(k);i=l;return}else{a=_(c[k>>2]|0,(c[j>>2]|0)-(c[h>>2]|0)|0)|0;k=(c[f>>2]|0)+28|0;c[k>>2]=(c[k>>2]|0)-a;k=c[f>>2]|0;lc(k);i=l;return}}function kc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)>>>0)/((c[d>>2]|0)>>>0)|0|0}function lc(a){a=a|0;var b=0,d=0;d=i;i=i+16|0;b=d;c[b>>2]=a;while(1){if((c[(c[b>>2]|0)+28>>2]|0)>>>0>8388608)break;mc(c[b>>2]|0,(c[(c[b>>2]|0)+32>>2]|0)>>>23);c[(c[b>>2]|0)+32>>2]=c[(c[b>>2]|0)+32>>2]<<8&2147483647;a=(c[b>>2]|0)+28|0;c[a>>2]=c[a>>2]<<8;a=(c[b>>2]|0)+20|0;c[a>>2]=(c[a>>2]|0)+8}i=d;return}function mc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0;h=i;i=i+16|0;d=h+12|0;e=h+8|0;f=h+4|0;g=h;c[d>>2]=a;c[e>>2]=b;if((c[e>>2]|0)==255){g=(c[d>>2]|0)+36|0;c[g>>2]=(c[g>>2]|0)+1;i=h;return}c[f>>2]=c[e>>2]>>8;if((c[(c[d>>2]|0)+40>>2]|0)>=0){b=nc(c[d>>2]|0,(c[(c[d>>2]|0)+40>>2]|0)+(c[f>>2]|0)|0)|0;a=(c[d>>2]|0)+44|0;c[a>>2]=c[a>>2]|b}if((c[(c[d>>2]|0)+36>>2]|0)>>>0>0){c[g>>2]=255+(c[f>>2]|0)&255;do{f=nc(c[d>>2]|0,c[g>>2]|0)|0;a=(c[d>>2]|0)+44|0;c[a>>2]=c[a>>2]|f;a=(c[d>>2]|0)+36|0;f=(c[a>>2]|0)+-1|0;c[a>>2]=f}while(f>>>0>0)}c[(c[d>>2]|0)+40>>2]=c[e>>2]&255;i=h;return}function nc(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0;h=i;i=i+16|0;e=h+8|0;f=h+4|0;g=h;c[f>>2]=b;c[g>>2]=d;if(((c[(c[f>>2]|0)+24>>2]|0)+(c[(c[f>>2]|0)+8>>2]|0)|0)>>>0>=(c[(c[f>>2]|0)+4>>2]|0)>>>0){c[e>>2]=-1;d=c[e>>2]|0;i=h;return d|0}else{b=c[g>>2]&255;g=(c[f>>2]|0)+24|0;d=c[g>>2]|0;c[g>>2]=d+1;a[(c[c[f>>2]>>2]|0)+d>>0]=b;c[e>>2]=0;d=c[e>>2]|0;i=h;return d|0}return 0}function oc(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0;l=i;i=i+32|0;f=l+16|0;g=l+12|0;h=l+8|0;j=l+4|0;k=l;c[f>>2]=a;c[g>>2]=b;c[h>>2]=d;c[j>>2]=e;c[k>>2]=(c[(c[f>>2]|0)+28>>2]|0)>>>(c[j>>2]|0);if((c[g>>2]|0)>>>0>0){b=(c[(c[f>>2]|0)+28>>2]|0)-(_(c[k>>2]|0,(1<<c[j>>2])-(c[g>>2]|0)|0)|0)|0;a=(c[f>>2]|0)+32|0;c[a>>2]=(c[a>>2]|0)+b;k=_(c[k>>2]|0,(c[h>>2]|0)-(c[g>>2]|0)|0)|0;c[(c[f>>2]|0)+28>>2]=k;k=c[f>>2]|0;lc(k);i=l;return}else{a=_(c[k>>2]|0,(1<<c[j>>2])-(c[h>>2]|0)|0)|0;k=(c[f>>2]|0)+28|0;c[k>>2]=(c[k>>2]|0)-a;k=c[f>>2]|0;lc(k);i=l;return}}function pc(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0;k=i;i=i+32|0;e=k+20|0;f=k+16|0;l=k+12|0;h=k+8|0;j=k+4|0;g=k;c[e>>2]=a;c[f>>2]=b;c[l>>2]=d;c[h>>2]=c[(c[e>>2]|0)+28>>2];c[g>>2]=c[(c[e>>2]|0)+32>>2];c[j>>2]=(c[h>>2]|0)>>>(c[l>>2]|0);c[h>>2]=(c[h>>2]|0)-(c[j>>2]|0);if(c[f>>2]|0)c[(c[e>>2]|0)+32>>2]=(c[g>>2]|0)+(c[h>>2]|0);c[(c[e>>2]|0)+28>>2]=c[f>>2]|0?c[j>>2]|0:c[h>>2]|0;lc(c[e>>2]|0);i=k;return}function qc(a,b,e,f){a=a|0;b=b|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0;l=i;i=i+32|0;g=l+16|0;h=l+12|0;j=l+8|0;m=l+4|0;k=l;c[g>>2]=a;c[h>>2]=b;c[j>>2]=e;c[m>>2]=f;c[k>>2]=(c[(c[g>>2]|0)+28>>2]|0)>>>(c[m>>2]|0);if((c[h>>2]|0)>0){a=(c[(c[g>>2]|0)+28>>2]|0)-(_(c[k>>2]|0,d[(c[j>>2]|0)+((c[h>>2]|0)-1)>>0]|0)|0)|0;m=(c[g>>2]|0)+32|0;c[m>>2]=(c[m>>2]|0)+a;m=_(c[k>>2]|0,(d[(c[j>>2]|0)+((c[h>>2]|0)-1)>>0]|0)-(d[(c[j>>2]|0)+(c[h>>2]|0)>>0]|0)|0)|0;c[(c[g>>2]|0)+28>>2]=m;m=c[g>>2]|0;lc(m);i=l;return}else{k=_(c[k>>2]|0,d[(c[j>>2]|0)+(c[h>>2]|0)>>0]|0)|0;m=(c[g>>2]|0)+28|0;c[m>>2]=(c[m>>2]|0)-k;m=c[g>>2]|0;lc(m);i=l;return}}function rc(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0;l=i;i=i+32|0;e=l+20|0;f=l+16|0;g=l+12|0;j=l+8|0;h=l+4|0;k=l;c[e>>2]=a;c[f>>2]=b;c[g>>2]=d;c[g>>2]=(c[g>>2]|0)+-1;c[k>>2]=32-(aa(c[g>>2]|0)|0);if((c[k>>2]|0)>8){c[k>>2]=(c[k>>2]|0)-8;c[j>>2]=((c[g>>2]|0)>>>(c[k>>2]|0))+1;c[h>>2]=(c[f>>2]|0)>>>(c[k>>2]|0);jc(c[e>>2]|0,c[h>>2]|0,(c[h>>2]|0)+1|0,c[j>>2]|0);sc(c[e>>2]|0,c[f>>2]&(1<<c[k>>2])-1,c[k>>2]|0);i=l;return}else{jc(c[e>>2]|0,c[f>>2]|0,(c[f>>2]|0)+1|0,(c[g>>2]|0)+1|0);i=l;return}}function sc(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0;k=i;i=i+32|0;e=k+16|0;f=k+12|0;g=k+8|0;j=k+4|0;h=k;c[e>>2]=a;c[f>>2]=b;c[g>>2]=d;c[j>>2]=c[(c[e>>2]|0)+12>>2];c[h>>2]=c[(c[e>>2]|0)+16>>2];if(((c[h>>2]|0)+(c[g>>2]|0)|0)>>>0>32)do{b=tc(c[e>>2]|0,c[j>>2]&255)|0;a=(c[e>>2]|0)+44|0;c[a>>2]=c[a>>2]|b;c[j>>2]=(c[j>>2]|0)>>>8;c[h>>2]=(c[h>>2]|0)-8}while((c[h>>2]|0)>=8);c[j>>2]=c[j>>2]|c[f>>2]<<c[h>>2];c[h>>2]=(c[h>>2]|0)+(c[g>>2]|0);c[(c[e>>2]|0)+12>>2]=c[j>>2];c[(c[e>>2]|0)+16>>2]=c[h>>2];j=(c[e>>2]|0)+20|0;c[j>>2]=(c[j>>2]|0)+(c[g>>2]|0);i=k;return}function tc(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0;h=i;i=i+16|0;e=h+8|0;f=h+4|0;g=h;c[f>>2]=b;c[g>>2]=d;if(((c[(c[f>>2]|0)+24>>2]|0)+(c[(c[f>>2]|0)+8>>2]|0)|0)>>>0>=(c[(c[f>>2]|0)+4>>2]|0)>>>0){c[e>>2]=-1;d=c[e>>2]|0;i=h;return d|0}else{g=c[g>>2]&255;b=c[(c[f>>2]|0)+4>>2]|0;j=(c[f>>2]|0)+8|0;d=(c[j>>2]|0)+1|0;c[j>>2]=d;a[(c[c[f>>2]>>2]|0)+(b-d)>>0]=g;c[e>>2]=0;d=c[e>>2]|0;i=h;return d|0}return 0}function uc(b,e,f){b=b|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0;m=i;i=i+32|0;g=m+16|0;h=m+12|0;j=m+8|0;l=m+4|0;k=m;c[g>>2]=b;c[h>>2]=e;c[j>>2]=f;c[l>>2]=8-(c[j>>2]|0);c[k>>2]=(1<<c[j>>2])-1<<c[l>>2];f=c[g>>2]|0;if((c[(c[g>>2]|0)+24>>2]|0)>>>0>0){a[c[c[g>>2]>>2]>>0]=(d[c[f>>2]>>0]|0)&~c[k>>2]|c[h>>2]<<c[l>>2];i=m;return}b=c[g>>2]|0;if((c[f+40>>2]|0)>=0){c[(c[g>>2]|0)+40>>2]=c[b+40>>2]&~c[k>>2]|c[h>>2]<<c[l>>2];i=m;return}f=c[g>>2]|0;if((c[b+28>>2]|0)>>>0<=-2147483648>>>(c[j>>2]|0)>>>0){c[(c[g>>2]|0)+32>>2]=c[f+32>>2]&~(c[k>>2]<<23)|c[h>>2]<<23+(c[l>>2]|0);i=m;return}else{c[f+44>>2]=-1;i=m;return}}function vc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;d=i;i=i+16|0;e=d+4|0;f=d;c[e>>2]=a;c[f>>2]=b;qj((c[c[e>>2]>>2]|0)+(c[f>>2]|0)+(0-(c[(c[e>>2]|0)+8>>2]|0))|0,(c[c[e>>2]>>2]|0)+(c[(c[e>>2]|0)+4>>2]|0)+(0-(c[(c[e>>2]|0)+8>>2]|0))|0,(c[(c[e>>2]|0)+8>>2]|0)+0|0)|0;c[(c[e>>2]|0)+4>>2]=c[f>>2];i=d;return}function wc(b){b=b|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0;l=i;i=i+32|0;g=l+20|0;k=l+16|0;j=l+12|0;e=l+8|0;f=l+4|0;h=l;c[g>>2]=b;c[h>>2]=32-(32-(aa(c[(c[g>>2]|0)+28>>2]|0)|0));c[e>>2]=2147483647>>>(c[h>>2]|0);c[f>>2]=(c[(c[g>>2]|0)+32>>2]|0)+(c[e>>2]|0)&~c[e>>2];if((c[f>>2]|c[e>>2])>>>0>=((c[(c[g>>2]|0)+32>>2]|0)+(c[(c[g>>2]|0)+28>>2]|0)|0)>>>0){c[h>>2]=(c[h>>2]|0)+1;c[e>>2]=(c[e>>2]|0)>>>1;c[f>>2]=(c[(c[g>>2]|0)+32>>2]|0)+(c[e>>2]|0)&~c[e>>2]}while(1){b=c[g>>2]|0;if((c[h>>2]|0)<=0)break;mc(b,(c[f>>2]|0)>>>23);c[f>>2]=c[f>>2]<<8&2147483647;c[h>>2]=(c[h>>2]|0)-8}if(!((c[b+40>>2]|0)<0?(c[(c[g>>2]|0)+36>>2]|0)>>>0<=0:0))mc(c[g>>2]|0,0);c[k>>2]=c[(c[g>>2]|0)+12>>2];c[j>>2]=c[(c[g>>2]|0)+16>>2];while(1){b=c[g>>2]|0;if((c[j>>2]|0)<8)break;e=tc(b,c[k>>2]&255)|0;f=(c[g>>2]|0)+44|0;c[f>>2]=c[f>>2]|e;c[k>>2]=(c[k>>2]|0)>>>8;c[j>>2]=(c[j>>2]|0)-8}if(c[b+44>>2]|0){i=l;return}oj((c[c[g>>2]>>2]|0)+(c[(c[g>>2]|0)+24>>2]|0)|0,0,(c[(c[g>>2]|0)+4>>2]|0)-(c[(c[g>>2]|0)+24>>2]|0)-(c[(c[g>>2]|0)+8>>2]|0)|0)|0;if((c[j>>2]|0)<=0){i=l;return}if((c[(c[g>>2]|0)+8>>2]|0)>>>0>=(c[(c[g>>2]|0)+4>>2]|0)>>>0){c[(c[g>>2]|0)+44>>2]=-1;i=l;return}c[h>>2]=0-(c[h>>2]|0);if(((c[(c[g>>2]|0)+24>>2]|0)+(c[(c[g>>2]|0)+8>>2]|0)|0)>>>0>=(c[(c[g>>2]|0)+4>>2]|0)>>>0?(c[h>>2]|0)<(c[j>>2]|0):0){c[k>>2]=c[k>>2]&(1<<c[h>>2])-1;c[(c[g>>2]|0)+44>>2]=-1}j=(c[c[g>>2]>>2]|0)+((c[(c[g>>2]|0)+4>>2]|0)-(c[(c[g>>2]|0)+8>>2]|0)-1)|0;a[j>>0]=d[j>>0]|0|c[k>>2]&255;i=l;return}function xc(a,d){a=a|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0;o=i;i=i+64|0;e=o+60|0;f=o+56|0;l=o+52|0;k=o+48|0;m=o+44|0;g=o+40|0;h=o+8|0;j=o+4|0;n=o;c[e>>2]=a;c[f>>2]=d;if((c[(c[e>>2]|0)+8>>2]|0)>0)d=c[(c[e>>2]|0)+8>>2]|0;else d=0;c[n>>2]=d;c[h>>2]=1;c[g>>2]=0;do{c[m>>2]=b[(c[e>>2]|0)+12+(c[g>>2]<<1<<1)>>1];c[k>>2]=b[(c[e>>2]|0)+12+((c[g>>2]<<1)+1<<1)>>1];a=_(c[h+(c[g>>2]<<2)>>2]|0,c[m>>2]|0)|0;c[h+((c[g>>2]|0)+1<<2)>>2]=a;c[g>>2]=(c[g>>2]|0)+1}while((c[k>>2]|0)!=1);c[k>>2]=b[(c[e>>2]|0)+12+((c[g>>2]<<1)-1<<1)>>1];c[j>>2]=(c[g>>2]|0)-1;while(1){if((c[j>>2]|0)<0)break;if(c[j>>2]|0)c[l>>2]=b[(c[e>>2]|0)+12+((c[j>>2]<<1)-1<<1)>>1];else c[l>>2]=1;switch(b[(c[e>>2]|0)+12+(c[j>>2]<<1<<1)>>1]|0){case 2:{yc(c[f>>2]|0,c[k>>2]|0,c[h+(c[j>>2]<<2)>>2]|0);break}case 4:{zc(c[f>>2]|0,c[h+(c[j>>2]<<2)>>2]<<c[n>>2],c[e>>2]|0,c[k>>2]|0,c[h+(c[j>>2]<<2)>>2]|0,c[l>>2]|0);break}case 3:{Ac(c[f>>2]|0,c[h+(c[j>>2]<<2)>>2]<<c[n>>2],c[e>>2]|0,c[k>>2]|0,c[h+(c[j>>2]<<2)>>2]|0,c[l>>2]|0);break}case 5:{Bc(c[f>>2]|0,c[h+(c[j>>2]<<2)>>2]<<c[n>>2],c[e>>2]|0,c[k>>2]|0,c[h+(c[j>>2]<<2)>>2]|0,c[l>>2]|0);break}default:{}}c[k>>2]=c[l>>2];c[j>>2]=(c[j>>2]|0)+-1}i=o;return}function yc(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,h=0,j=0,k=0,l=0,m=0;m=i;i=i+32|0;e=m+28|0;f=m+20|0;h=m+16|0;j=m+12|0;l=m+8|0;k=m;c[e>>2]=a;c[m+24>>2]=b;c[f>>2]=d;g[l>>2]=.7071067690849304;c[j>>2]=0;while(1){if((c[j>>2]|0)>=(c[f>>2]|0))break;c[h>>2]=(c[e>>2]|0)+32;b=c[h>>2]|0;c[k>>2]=c[b>>2];c[k+4>>2]=c[b+4>>2];g[c[h>>2]>>2]=+g[c[e>>2]>>2]-+g[k>>2];g[(c[h>>2]|0)+4>>2]=+g[(c[e>>2]|0)+4>>2]-+g[k+4>>2];b=c[e>>2]|0;g[b>>2]=+g[b>>2]+ +g[k>>2];b=(c[e>>2]|0)+4|0;g[b>>2]=+g[b>>2]+ +g[k+4>>2];g[k>>2]=(+g[(c[h>>2]|0)+8>>2]+ +g[(c[h>>2]|0)+8+4>>2])*+g[l>>2];g[k+4>>2]=(+g[(c[h>>2]|0)+8+4>>2]-+g[(c[h>>2]|0)+8>>2])*+g[l>>2];g[(c[h>>2]|0)+8>>2]=+g[(c[e>>2]|0)+8>>2]-+g[k>>2];g[(c[h>>2]|0)+8+4>>2]=+g[(c[e>>2]|0)+8+4>>2]-+g[k+4>>2];b=(c[e>>2]|0)+8|0;g[b>>2]=+g[b>>2]+ +g[k>>2];b=(c[e>>2]|0)+8+4|0;g[b>>2]=+g[b>>2]+ +g[k+4>>2];g[k>>2]=+g[(c[h>>2]|0)+16+4>>2];g[k+4>>2]=-+g[(c[h>>2]|0)+16>>2];g[(c[h>>2]|0)+16>>2]=+g[(c[e>>2]|0)+16>>2]-+g[k>>2];g[(c[h>>2]|0)+16+4>>2]=+g[(c[e>>2]|0)+16+4>>2]-+g[k+4>>2];b=(c[e>>2]|0)+16|0;g[b>>2]=+g[b>>2]+ +g[k>>2];b=(c[e>>2]|0)+16+4|0;g[b>>2]=+g[b>>2]+ +g[k+4>>2];g[k>>2]=(+g[(c[h>>2]|0)+24+4>>2]-+g[(c[h>>2]|0)+24>>2])*+g[l>>2];g[k+4>>2]=(-+g[(c[h>>2]|0)+24+4>>2]-+g[(c[h>>2]|0)+24>>2])*+g[l>>2];g[(c[h>>2]|0)+24>>2]=+g[(c[e>>2]|0)+24>>2]-+g[k>>2];g[(c[h>>2]|0)+24+4>>2]=+g[(c[e>>2]|0)+24+4>>2]-+g[k+4>>2];b=(c[e>>2]|0)+24|0;g[b>>2]=+g[b>>2]+ +g[k>>2];b=(c[e>>2]|0)+24+4|0;g[b>>2]=+g[b>>2]+ +g[k+4>>2];c[e>>2]=(c[e>>2]|0)+64;c[j>>2]=(c[j>>2]|0)+1}i=m;return}function zc(a,b,d,e,f,h){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0;A=i;i=i+128|0;j=A+120|0;k=A+116|0;l=A+112|0;m=A+108|0;n=A+104|0;o=A+100|0;q=A+96|0;v=A+88|0;w=A+80|0;r=A+72|0;u=A+24|0;x=A+20|0;y=A+16|0;z=A+12|0;s=A+8|0;t=A+4|0;p=A;c[j>>2]=a;c[k>>2]=b;c[l>>2]=d;c[m>>2]=e;c[n>>2]=f;c[o>>2]=h;if((c[m>>2]|0)==1){c[q>>2]=0;while(1){if((c[q>>2]|0)>=(c[n>>2]|0))break;g[v>>2]=+g[c[j>>2]>>2]-+g[(c[j>>2]|0)+16>>2];g[v+4>>2]=+g[(c[j>>2]|0)+4>>2]-+g[(c[j>>2]|0)+16+4>>2];z=c[j>>2]|0;g[z>>2]=+g[z>>2]+ +g[(c[j>>2]|0)+16>>2];z=(c[j>>2]|0)+4|0;g[z>>2]=+g[z>>2]+ +g[(c[j>>2]|0)+16+4>>2];g[w>>2]=+g[(c[j>>2]|0)+8>>2]+ +g[(c[j>>2]|0)+24>>2];g[w+4>>2]=+g[(c[j>>2]|0)+8+4>>2]+ +g[(c[j>>2]|0)+24+4>>2];g[(c[j>>2]|0)+16>>2]=+g[c[j>>2]>>2]-+g[w>>2];g[(c[j>>2]|0)+16+4>>2]=+g[(c[j>>2]|0)+4>>2]-+g[w+4>>2];z=c[j>>2]|0;g[z>>2]=+g[z>>2]+ +g[w>>2];z=(c[j>>2]|0)+4|0;g[z>>2]=+g[z>>2]+ +g[w+4>>2];g[w>>2]=+g[(c[j>>2]|0)+8>>2]-+g[(c[j>>2]|0)+24>>2];g[w+4>>2]=+g[(c[j>>2]|0)+8+4>>2]-+g[(c[j>>2]|0)+24+4>>2];g[(c[j>>2]|0)+8>>2]=+g[v>>2]+ +g[w+4>>2];g[(c[j>>2]|0)+8+4>>2]=+g[v+4>>2]-+g[w>>2];g[(c[j>>2]|0)+24>>2]=+g[v>>2]-+g[w+4>>2];g[(c[j>>2]|0)+24+4>>2]=+g[v+4>>2]+ +g[w>>2];c[j>>2]=(c[j>>2]|0)+32;c[q>>2]=(c[q>>2]|0)+1}i=A;return}c[s>>2]=c[m>>2]<<1;c[t>>2]=(c[m>>2]|0)*3;c[p>>2]=c[j>>2];c[q>>2]=0;while(1){if((c[q>>2]|0)>=(c[n>>2]|0))break;c[j>>2]=(c[p>>2]|0)+((_(c[q>>2]|0,c[o>>2]|0)|0)<<3);d=c[(c[l>>2]|0)+48>>2]|0;c[x>>2]=d;c[y>>2]=d;c[z>>2]=d;c[r>>2]=0;while(1){if((c[r>>2]|0)>=(c[m>>2]|0))break;g[u>>2]=+g[(c[j>>2]|0)+(c[m>>2]<<3)>>2]*+g[c[x>>2]>>2]-+g[(c[j>>2]|0)+(c[m>>2]<<3)+4>>2]*+g[(c[x>>2]|0)+4>>2];g[u+4>>2]=+g[(c[j>>2]|0)+(c[m>>2]<<3)>>2]*+g[(c[x>>2]|0)+4>>2]+ +g[(c[j>>2]|0)+(c[m>>2]<<3)+4>>2]*+g[c[x>>2]>>2];g[u+8>>2]=+g[(c[j>>2]|0)+(c[s>>2]<<3)>>2]*+g[c[y>>2]>>2]-+g[(c[j>>2]|0)+(c[s>>2]<<3)+4>>2]*+g[(c[y>>2]|0)+4>>2];g[u+8+4>>2]=+g[(c[j>>2]|0)+(c[s>>2]<<3)>>2]*+g[(c[y>>2]|0)+4>>2]+ +g[(c[j>>2]|0)+(c[s>>2]<<3)+4>>2]*+g[c[y>>2]>>2];g[u+16>>2]=+g[(c[j>>2]|0)+(c[t>>2]<<3)>>2]*+g[c[z>>2]>>2]-+g[(c[j>>2]|0)+(c[t>>2]<<3)+4>>2]*+g[(c[z>>2]|0)+4>>2];g[u+16+4>>2]=+g[(c[j>>2]|0)+(c[t>>2]<<3)>>2]*+g[(c[z>>2]|0)+4>>2]+ +g[(c[j>>2]|0)+(c[t>>2]<<3)+4>>2]*+g[c[z>>2]>>2];g[u+40>>2]=+g[c[j>>2]>>2]-+g[u+8>>2];g[u+40+4>>2]=+g[(c[j>>2]|0)+4>>2]-+g[u+8+4>>2];d=c[j>>2]|0;g[d>>2]=+g[d>>2]+ +g[u+8>>2];d=(c[j>>2]|0)+4|0;g[d>>2]=+g[d>>2]+ +g[u+8+4>>2];g[u+24>>2]=+g[u>>2]+ +g[u+16>>2];g[u+24+4>>2]=+g[u+4>>2]+ +g[u+16+4>>2];g[u+32>>2]=+g[u>>2]-+g[u+16>>2];g[u+32+4>>2]=+g[u+4>>2]-+g[u+16+4>>2];g[(c[j>>2]|0)+(c[s>>2]<<3)>>2]=+g[c[j>>2]>>2]-+g[u+24>>2];g[(c[j>>2]|0)+(c[s>>2]<<3)+4>>2]=+g[(c[j>>2]|0)+4>>2]-+g[u+24+4>>2];c[x>>2]=(c[x>>2]|0)+(c[k>>2]<<3);c[y>>2]=(c[y>>2]|0)+(c[k>>2]<<1<<3);c[z>>2]=(c[z>>2]|0)+((c[k>>2]|0)*3<<3);d=c[j>>2]|0;g[d>>2]=+g[d>>2]+ +g[u+24>>2];d=(c[j>>2]|0)+4|0;g[d>>2]=+g[d>>2]+ +g[u+24+4>>2];g[(c[j>>2]|0)+(c[m>>2]<<3)>>2]=+g[u+40>>2]+ +g[u+32+4>>2];g[(c[j>>2]|0)+(c[m>>2]<<3)+4>>2]=+g[u+40+4>>2]-+g[u+32>>2];g[(c[j>>2]|0)+(c[t>>2]<<3)>>2]=+g[u+40>>2]-+g[u+32+4>>2];g[(c[j>>2]|0)+(c[t>>2]<<3)+4>>2]=+g[u+40+4>>2]+ +g[u+32>>2];c[j>>2]=(c[j>>2]|0)+8;c[r>>2]=(c[r>>2]|0)+1}c[q>>2]=(c[q>>2]|0)+1}i=A;return}function Ac(a,b,d,e,f,h){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0;x=i;i=i+112|0;j=x+96|0;k=x+92|0;l=x+88|0;m=x+84|0;n=x+80|0;o=x+76|0;r=x+72|0;s=x+68|0;t=x+64|0;v=x+60|0;w=x+56|0;u=x+16|0;q=x+8|0;p=x;c[j>>2]=a;c[k>>2]=b;c[l>>2]=d;c[m>>2]=e;c[n>>2]=f;c[o>>2]=h;c[t>>2]=c[m>>2]<<1;c[p>>2]=c[j>>2];d=_(c[k>>2]|0,c[m>>2]|0)|0;d=(c[(c[l>>2]|0)+48>>2]|0)+(d<<3)|0;c[q>>2]=c[d>>2];c[q+4>>2]=c[d+4>>2];c[r>>2]=0;while(1){if((c[r>>2]|0)>=(c[n>>2]|0))break;c[j>>2]=(c[p>>2]|0)+((_(c[r>>2]|0,c[o>>2]|0)|0)<<3);d=c[(c[l>>2]|0)+48>>2]|0;c[w>>2]=d;c[v>>2]=d;c[s>>2]=c[m>>2];do{g[u+8>>2]=+g[(c[j>>2]|0)+(c[m>>2]<<3)>>2]*+g[c[v>>2]>>2]-+g[(c[j>>2]|0)+(c[m>>2]<<3)+4>>2]*+g[(c[v>>2]|0)+4>>2];g[u+8+4>>2]=+g[(c[j>>2]|0)+(c[m>>2]<<3)>>2]*+g[(c[v>>2]|0)+4>>2]+ +g[(c[j>>2]|0)+(c[m>>2]<<3)+4>>2]*+g[c[v>>2]>>2];g[u+16>>2]=+g[(c[j>>2]|0)+(c[t>>2]<<3)>>2]*+g[c[w>>2]>>2]-+g[(c[j>>2]|0)+(c[t>>2]<<3)+4>>2]*+g[(c[w>>2]|0)+4>>2];g[u+16+4>>2]=+g[(c[j>>2]|0)+(c[t>>2]<<3)>>2]*+g[(c[w>>2]|0)+4>>2]+ +g[(c[j>>2]|0)+(c[t>>2]<<3)+4>>2]*+g[c[w>>2]>>2];g[u+24>>2]=+g[u+8>>2]+ +g[u+16>>2];g[u+24+4>>2]=+g[u+8+4>>2]+ +g[u+16+4>>2];g[u>>2]=+g[u+8>>2]-+g[u+16>>2];g[u+4>>2]=+g[u+8+4>>2]-+g[u+16+4>>2];c[v>>2]=(c[v>>2]|0)+(c[k>>2]<<3);c[w>>2]=(c[w>>2]|0)+(c[k>>2]<<1<<3);g[(c[j>>2]|0)+(c[m>>2]<<3)>>2]=+g[c[j>>2]>>2]-+g[u+24>>2]*.5;g[(c[j>>2]|0)+(c[m>>2]<<3)+4>>2]=+g[(c[j>>2]|0)+4>>2]-+g[u+24+4>>2]*.5;g[u>>2]=+g[u>>2]*+g[q+4>>2];d=u+4|0;g[d>>2]=+g[d>>2]*+g[q+4>>2];d=c[j>>2]|0;g[d>>2]=+g[d>>2]+ +g[u+24>>2];d=(c[j>>2]|0)+4|0;g[d>>2]=+g[d>>2]+ +g[u+24+4>>2];g[(c[j>>2]|0)+(c[t>>2]<<3)>>2]=+g[(c[j>>2]|0)+(c[m>>2]<<3)>>2]+ +g[u+4>>2];g[(c[j>>2]|0)+(c[t>>2]<<3)+4>>2]=+g[(c[j>>2]|0)+(c[m>>2]<<3)+4>>2]-+g[u>>2];d=(c[j>>2]|0)+(c[m>>2]<<3)|0;g[d>>2]=+g[d>>2]-+g[u+4>>2];d=(c[j>>2]|0)+(c[m>>2]<<3)+4|0;g[d>>2]=+g[d>>2]+ +g[u>>2];c[j>>2]=(c[j>>2]|0)+8;d=(c[s>>2]|0)+-1|0;c[s>>2]=d}while((d|0)!=0);c[r>>2]=(c[r>>2]|0)+1}i=x;return}function Bc(a,b,d,e,f,h){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0;A=i;i=i+192|0;j=A+184|0;k=A+180|0;B=A+176|0;l=A+172|0;m=A+168|0;n=A+164|0;o=A+160|0;p=A+156|0;q=A+152|0;r=A+148|0;s=A+144|0;u=A+140|0;x=A+136|0;v=A+32|0;w=A+24|0;y=A+16|0;z=A+8|0;t=A;c[j>>2]=a;c[k>>2]=b;c[B>>2]=d;c[l>>2]=e;c[m>>2]=f;c[n>>2]=h;c[t>>2]=c[j>>2];d=_(c[k>>2]|0,c[l>>2]|0)|0;d=(c[(c[B>>2]|0)+48>>2]|0)+(d<<3)|0;c[y>>2]=c[d>>2];c[y+4>>2]=c[d+4>>2];d=_(c[k>>2]<<1,c[l>>2]|0)|0;d=(c[(c[B>>2]|0)+48>>2]|0)+(d<<3)|0;c[z>>2]=c[d>>2];c[z+4>>2]=c[d+4>>2];c[w>>2]=c[(c[B>>2]|0)+48>>2];c[u>>2]=0;while(1){if((c[u>>2]|0)>=(c[m>>2]|0))break;c[j>>2]=(c[t>>2]|0)+((_(c[u>>2]|0,c[n>>2]|0)|0)<<3);c[o>>2]=c[j>>2];c[p>>2]=(c[o>>2]|0)+(c[l>>2]<<3);c[q>>2]=(c[o>>2]|0)+(c[l>>2]<<1<<3);c[r>>2]=(c[o>>2]|0)+((c[l>>2]|0)*3<<3);c[s>>2]=(c[o>>2]|0)+(c[l>>2]<<2<<3);c[x>>2]=0;while(1){if((c[x>>2]|0)>=(c[l>>2]|0))break;B=c[o>>2]|0;c[v>>2]=c[B>>2];c[v+4>>2]=c[B+4>>2];B=_(c[x>>2]|0,c[k>>2]|0)|0;d=_(c[x>>2]|0,c[k>>2]|0)|0;g[v+8>>2]=+g[c[p>>2]>>2]*+g[(c[w>>2]|0)+(B<<3)>>2]-+g[(c[p>>2]|0)+4>>2]*+g[(c[w>>2]|0)+(d<<3)+4>>2];d=_(c[x>>2]|0,c[k>>2]|0)|0;B=_(c[x>>2]|0,c[k>>2]|0)|0;g[v+8+4>>2]=+g[c[p>>2]>>2]*+g[(c[w>>2]|0)+(d<<3)+4>>2]+ +g[(c[p>>2]|0)+4>>2]*+g[(c[w>>2]|0)+(B<<3)>>2];B=_(c[x>>2]<<1,c[k>>2]|0)|0;d=_(c[x>>2]<<1,c[k>>2]|0)|0;g[v+16>>2]=+g[c[q>>2]>>2]*+g[(c[w>>2]|0)+(B<<3)>>2]-+g[(c[q>>2]|0)+4>>2]*+g[(c[w>>2]|0)+(d<<3)+4>>2];d=_(c[x>>2]<<1,c[k>>2]|0)|0;B=_(c[x>>2]<<1,c[k>>2]|0)|0;g[v+16+4>>2]=+g[c[q>>2]>>2]*+g[(c[w>>2]|0)+(d<<3)+4>>2]+ +g[(c[q>>2]|0)+4>>2]*+g[(c[w>>2]|0)+(B<<3)>>2];B=_((c[x>>2]|0)*3|0,c[k>>2]|0)|0;d=_((c[x>>2]|0)*3|0,c[k>>2]|0)|0;g[v+24>>2]=+g[c[r>>2]>>2]*+g[(c[w>>2]|0)+(B<<3)>>2]-+g[(c[r>>2]|0)+4>>2]*+g[(c[w>>2]|0)+(d<<3)+4>>2];d=_((c[x>>2]|0)*3|0,c[k>>2]|0)|0;B=_((c[x>>2]|0)*3|0,c[k>>2]|0)|0;g[v+24+4>>2]=+g[c[r>>2]>>2]*+g[(c[w>>2]|0)+(d<<3)+4>>2]+ +g[(c[r>>2]|0)+4>>2]*+g[(c[w>>2]|0)+(B<<3)>>2];B=_(c[x>>2]<<2,c[k>>2]|0)|0;d=_(c[x>>2]<<2,c[k>>2]|0)|0;g[v+32>>2]=+g[c[s>>2]>>2]*+g[(c[w>>2]|0)+(B<<3)>>2]-+g[(c[s>>2]|0)+4>>2]*+g[(c[w>>2]|0)+(d<<3)+4>>2];d=_(c[x>>2]<<2,c[k>>2]|0)|0;B=_(c[x>>2]<<2,c[k>>2]|0)|0;g[v+32+4>>2]=+g[c[s>>2]>>2]*+g[(c[w>>2]|0)+(d<<3)+4>>2]+ +g[(c[s>>2]|0)+4>>2]*+g[(c[w>>2]|0)+(B<<3)>>2];g[v+56>>2]=+g[v+8>>2]+ +g[v+32>>2];g[v+56+4>>2]=+g[v+8+4>>2]+ +g[v+32+4>>2];g[v+80>>2]=+g[v+8>>2]-+g[v+32>>2];g[v+80+4>>2]=+g[v+8+4>>2]-+g[v+32+4>>2];g[v+64>>2]=+g[v+16>>2]+ +g[v+24>>2];g[v+64+4>>2]=+g[v+16+4>>2]+ +g[v+24+4>>2];g[v+72>>2]=+g[v+16>>2]-+g[v+24>>2];g[v+72+4>>2]=+g[v+16+4>>2]-+g[v+24+4>>2];B=c[o>>2]|0;g[B>>2]=+g[B>>2]+(+g[v+56>>2]+ +g[v+64>>2]);B=(c[o>>2]|0)+4|0;g[B>>2]=+g[B>>2]+(+g[v+56+4>>2]+ +g[v+64+4>>2]);g[v+40>>2]=+g[v>>2]+ +g[v+56>>2]*+g[y>>2]+ +g[v+64>>2]*+g[z>>2];g[v+40+4>>2]=+g[v+4>>2]+ +g[v+56+4>>2]*+g[y>>2]+ +g[v+64+4>>2]*+g[z>>2];g[v+48>>2]=+g[v+80+4>>2]*+g[y+4>>2]+ +g[v+72+4>>2]*+g[z+4>>2];g[v+48+4>>2]=-(+g[v+80>>2]*+g[y+4>>2])-+g[v+72>>2]*+g[z+4>>2];g[c[p>>2]>>2]=+g[v+40>>2]-+g[v+48>>2];g[(c[p>>2]|0)+4>>2]=+g[v+40+4>>2]-+g[v+48+4>>2];g[c[s>>2]>>2]=+g[v+40>>2]+ +g[v+48>>2];g[(c[s>>2]|0)+4>>2]=+g[v+40+4>>2]+ +g[v+48+4>>2];g[v+88>>2]=+g[v>>2]+ +g[v+56>>2]*+g[z>>2]+ +g[v+64>>2]*+g[y>>2];g[v+88+4>>2]=+g[v+4>>2]+ +g[v+56+4>>2]*+g[z>>2]+ +g[v+64+4>>2]*+g[y>>2];g[v+96>>2]=-(+g[v+80+4>>2]*+g[z+4>>2])+ +g[v+72+4>>2]*+g[y+4>>2];g[v+96+4>>2]=+g[v+80>>2]*+g[z+4>>2]-+g[v+72>>2]*+g[y+4>>2];g[c[q>>2]>>2]=+g[v+88>>2]+ +g[v+96>>2];g[(c[q>>2]|0)+4>>2]=+g[v+88+4>>2]+ +g[v+96+4>>2];g[c[r>>2]>>2]=+g[v+88>>2]-+g[v+96>>2];g[(c[r>>2]|0)+4>>2]=+g[v+88+4>>2]-+g[v+96+4>>2];c[o>>2]=(c[o>>2]|0)+8;c[p>>2]=(c[p>>2]|0)+8;c[q>>2]=(c[q>>2]|0)+8;c[r>>2]=(c[r>>2]|0)+8;c[s>>2]=(c[s>>2]|0)+8;c[x>>2]=(c[x>>2]|0)+1}c[u>>2]=(c[u>>2]|0)+1}i=A;return}function Cc(a,d,e){a=a|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0,m=0,n=0;n=i;i=i+32|0;f=n+24|0;h=n+20|0;j=n+16|0;k=n+12|0;l=n+8|0;m=n;c[f>>2]=a;c[h>>2]=d;c[j>>2]=e;g[l>>2]=+g[(c[f>>2]|0)+4>>2];c[k>>2]=0;while(1){if((c[k>>2]|0)>=(c[c[f>>2]>>2]|0))break;a=(c[h>>2]|0)+(c[k>>2]<<3)|0;c[m>>2]=c[a>>2];c[m+4>>2]=c[a+4>>2];g[(c[j>>2]|0)+(b[(c[(c[f>>2]|0)+44>>2]|0)+(c[k>>2]<<1)>>1]<<3)>>2]=+g[l>>2]*+g[m>>2];g[(c[j>>2]|0)+(b[(c[(c[f>>2]|0)+44>>2]|0)+(c[k>>2]<<1)>>1]<<3)+4>>2]=+g[l>>2]*+g[m+4>>2];c[k>>2]=(c[k>>2]|0)+1}xc(c[f>>2]|0,c[j>>2]|0);i=n;return}function Dc(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;q=i;i=i+48|0;j=q+36|0;k=q+32|0;l=q+28|0;f=q+24|0;n=q+20|0;h=q+16|0;p=q+12|0;o=q+8|0;m=q+4|0;g=q;c[j>>2]=a;c[k>>2]=b;c[l>>2]=d;c[f>>2]=e;c[h>>2]=c[c[k>>2]>>2];c[n>>2]=0;if(!(c[h>>2]|0)){m=c[j>>2]|0;o=c[n>>2]|0;n=c[n>>2]|0;p=c[l>>2]|0;p=n+p|0;oc(m,o,p,15);i=q;return}c[p>>2]=0-((c[h>>2]|0)<0&1);c[h>>2]=(c[h>>2]|0)+(c[p>>2]|0)^c[p>>2];c[n>>2]=c[l>>2];c[l>>2]=Ec(c[l>>2]|0,c[f>>2]|0)|0;c[o>>2]=1;while(1){if((c[l>>2]|0)>>>0>0)a=(c[o>>2]|0)<(c[h>>2]|0);else a=0;e=c[l>>2]|0;if(!a)break;c[l>>2]=e<<1;c[n>>2]=(c[n>>2]|0)+((c[l>>2]|0)+2);c[l>>2]=(_(c[l>>2]|0,c[f>>2]|0)|0)>>>15;c[o>>2]=(c[o>>2]|0)+1}if(e|0){c[l>>2]=(c[l>>2]|0)+1;c[n>>2]=(c[n>>2]|0)+(c[l>>2]&~c[p>>2]);m=c[j>>2]|0;o=c[n>>2]|0;n=c[n>>2]|0;p=c[l>>2]|0;p=n+p|0;oc(m,o,p,15);i=q;return}c[g>>2]=(32768-(c[n>>2]|0)+1-1|0)>>>0;c[g>>2]=(c[g>>2]|0)-(c[p>>2]|0)>>1;if(((c[h>>2]|0)-(c[o>>2]|0)|0)<((c[g>>2]|0)-1|0))e=(c[h>>2]|0)-(c[o>>2]|0)|0;else e=(c[g>>2]|0)-1|0;c[m>>2]=e;c[n>>2]=(c[n>>2]|0)+((c[m>>2]<<1)+1+(c[p>>2]|0));c[l>>2]=1<(32768-(c[n>>2]|0)|0)>>>0?1:32768-(c[n>>2]|0)|0;c[c[k>>2]>>2]=(c[o>>2]|0)+(c[m>>2]|0)+(c[p>>2]|0)^c[p>>2];m=c[j>>2]|0;o=c[n>>2]|0;n=c[n>>2]|0;p=c[l>>2]|0;p=n+p|0;oc(m,o,p,15);i=q;return}function Ec(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0;d=i;i=i+16|0;g=d+8|0;e=d+4|0;f=d;c[g>>2]=a;c[e>>2]=b;c[f>>2]=32736-(c[g>>2]|0);a=(_(c[f>>2]|0,16384-(c[e>>2]|0)|0)|0)>>>15;i=d;return a|0}function Fc(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;m=i;i=i+32|0;e=m+24|0;j=m+20|0;f=m+16|0;l=m+12|0;k=m+8|0;h=m+4|0;g=m;c[e>>2]=a;c[j>>2]=b;c[f>>2]=d;c[l>>2]=0;c[h>>2]=bc(c[e>>2]|0,15)|0;c[k>>2]=0;do if((c[h>>2]|0)>>>0>=(c[j>>2]|0)>>>0){c[l>>2]=(c[l>>2]|0)+1;c[k>>2]=c[j>>2];c[j>>2]=(Ec(c[j>>2]|0,c[f>>2]|0)|0)+1;while(1){if((c[j>>2]|0)>>>0>1)d=(c[h>>2]|0)>>>0>=((c[k>>2]|0)+(c[j>>2]<<1)|0)>>>0;else d=0;a=c[j>>2]|0;if(!d)break;c[j>>2]=a<<1;c[k>>2]=(c[k>>2]|0)+(c[j>>2]|0);c[j>>2]=(_((c[j>>2]|0)-2|0,c[f>>2]|0)|0)>>>15;c[j>>2]=(c[j>>2]|0)+1;c[l>>2]=(c[l>>2]|0)+1}if(a>>>0<=1){c[g>>2]=((c[h>>2]|0)-(c[k>>2]|0)|0)>>>1;c[l>>2]=(c[l>>2]|0)+(c[g>>2]|0);c[k>>2]=(c[k>>2]|0)+(c[g>>2]<<1)}if((c[h>>2]|0)>>>0<((c[k>>2]|0)+(c[j>>2]|0)|0)>>>0){c[l>>2]=0-(c[l>>2]|0);break}else{c[k>>2]=(c[k>>2]|0)+(c[j>>2]|0);break}}while(0);a=c[e>>2]|0;d=c[k>>2]|0;if(((c[k>>2]|0)+(c[j>>2]|0)|0)>>>0>=32768){k=32768;cc(a,d,k,32768);l=c[l>>2]|0;i=m;return l|0}k=(c[k>>2]|0)+(c[j>>2]|0)|0;cc(a,d,k,32768);l=c[l>>2]|0;i=m;return l|0}function Gc(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0;h=i;i=i+32|0;b=h+16|0;d=h+12|0;f=h+8|0;e=h+4|0;g=h;c[b>>2]=a;c[f>>2]=0;c[e>>2]=32-(aa(c[b>>2]|0)|0)-1>>1;c[d>>2]=1<<c[e>>2];do{c[g>>2]=(c[f>>2]<<1)+(c[d>>2]|0)<<c[e>>2];if((c[g>>2]|0)>>>0<=(c[b>>2]|0)>>>0){c[f>>2]=(c[f>>2]|0)+(c[d>>2]|0);c[b>>2]=(c[b>>2]|0)-(c[g>>2]|0)}c[d>>2]=(c[d>>2]|0)>>>1;c[e>>2]=(c[e>>2]|0)+-1}while((c[e>>2]|0)>=0);i=h;return c[f>>2]|0}function Hc(a,d,e,f,h,j,k,l){a=a|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;var m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0.0;U=i;i=i+160|0;V=U+144|0;o=U+140|0;p=U+136|0;q=U+132|0;r=U+128|0;m=U+124|0;s=U+120|0;x=U+112|0;n=U+108|0;u=U+104|0;v=U+100|0;B=U+96|0;G=U+92|0;A=U+88|0;t=U+84|0;J=U+80|0;K=U+76|0;O=U+72|0;H=U+68|0;I=U+64|0;P=U+60|0;C=U+56|0;L=U+48|0;D=U+44|0;E=U+40|0;z=U+36|0;y=U+32|0;S=U+28|0;M=U+24|0;w=U+20|0;Q=U+16|0;R=U+12|0;F=U+8|0;T=U+4|0;N=U;c[V>>2]=a;c[o>>2]=d;c[p>>2]=e;c[q>>2]=f;c[r>>2]=h;c[m>>2]=j;c[s>>2]=k;c[U+116>>2]=l;c[B>>2]=c[(c[V>>2]|0)+8+(c[m>>2]<<2)>>2];g[A>>2]=+g[(c[B>>2]|0)+4>>2];c[n>>2]=c[c[V>>2]>>2];c[G>>2]=c[(c[V>>2]|0)+24>>2];c[x>>2]=0;while(1){l=c[n>>2]>>1;if((c[x>>2]|0)>=(c[m>>2]|0))break;c[n>>2]=l;c[G>>2]=(c[G>>2]|0)+(c[n>>2]<<2);c[x>>2]=(c[x>>2]|0)+1}c[u>>2]=l;c[v>>2]=c[n>>2]>>2;d=c[u>>2]|0;c[t>>2]=ia()|0;l=i;i=i+((1*(d<<2)|0)+15&-16)|0;d=i;i=i+((1*(c[v>>2]<<3)|0)+15&-16)|0;c[J>>2]=(c[o>>2]|0)+(c[r>>2]>>1<<2);c[K>>2]=(c[o>>2]|0)+(c[u>>2]<<2)+-4+(c[r>>2]>>1<<2);c[O>>2]=l;c[H>>2]=(c[q>>2]|0)+(c[r>>2]>>1<<2);c[I>>2]=(c[q>>2]|0)+(c[r>>2]>>1<<2)+-4;c[x>>2]=0;while(1){if((c[x>>2]|0)>=((c[r>>2]|0)+3>>2|0))break;W=+g[c[I>>2]>>2]*+g[(c[J>>2]|0)+(c[u>>2]<<2)>>2]+ +g[c[H>>2]>>2]*+g[c[K>>2]>>2];V=c[O>>2]|0;c[O>>2]=V+4;g[V>>2]=W;W=+g[c[H>>2]>>2]*+g[c[J>>2]>>2]-+g[c[I>>2]>>2]*+g[(c[K>>2]|0)+(0-(c[u>>2]|0)<<2)>>2];V=c[O>>2]|0;c[O>>2]=V+4;g[V>>2]=W;c[J>>2]=(c[J>>2]|0)+8;c[K>>2]=(c[K>>2]|0)+-8;c[H>>2]=(c[H>>2]|0)+8;c[I>>2]=(c[I>>2]|0)+-8;c[x>>2]=(c[x>>2]|0)+1}c[H>>2]=c[q>>2];c[I>>2]=(c[q>>2]|0)+(c[r>>2]<<2)+-4;while(1){if((c[x>>2]|0)>=((c[v>>2]|0)-((c[r>>2]|0)+3>>2)|0))break;W=+g[c[K>>2]>>2];V=c[O>>2]|0;c[O>>2]=V+4;g[V>>2]=W;W=+g[c[J>>2]>>2];V=c[O>>2]|0;c[O>>2]=V+4;g[V>>2]=W;c[J>>2]=(c[J>>2]|0)+8;c[K>>2]=(c[K>>2]|0)+-8;c[x>>2]=(c[x>>2]|0)+1}while(1){if((c[x>>2]|0)>=(c[v>>2]|0))break;W=-(+g[c[H>>2]>>2]*+g[(c[J>>2]|0)+(0-(c[u>>2]|0)<<2)>>2])+ +g[c[I>>2]>>2]*+g[c[K>>2]>>2];V=c[O>>2]|0;c[O>>2]=V+4;g[V>>2]=W;W=+g[c[I>>2]>>2]*+g[c[J>>2]>>2]+ +g[c[H>>2]>>2]*+g[(c[K>>2]|0)+(c[u>>2]<<2)>>2];V=c[O>>2]|0;c[O>>2]=V+4;g[V>>2]=W;c[J>>2]=(c[J>>2]|0)+8;c[K>>2]=(c[K>>2]|0)+-8;c[H>>2]=(c[H>>2]|0)+8;c[I>>2]=(c[I>>2]|0)+-8;c[x>>2]=(c[x>>2]|0)+1}c[P>>2]=l;c[C>>2]=c[G>>2];c[x>>2]=0;while(1){if((c[x>>2]|0)>=(c[v>>2]|0))break;g[D>>2]=+g[(c[C>>2]|0)+(c[x>>2]<<2)>>2];g[E>>2]=+g[(c[C>>2]|0)+((c[v>>2]|0)+(c[x>>2]|0)<<2)>>2];V=c[P>>2]|0;c[P>>2]=V+4;g[z>>2]=+g[V>>2];V=c[P>>2]|0;c[P>>2]=V+4;g[y>>2]=+g[V>>2];g[S>>2]=+g[z>>2]*+g[D>>2]-+g[y>>2]*+g[E>>2];g[M>>2]=+g[y>>2]*+g[D>>2]+ +g[z>>2]*+g[E>>2];g[L>>2]=+g[S>>2];g[L+4>>2]=+g[M>>2];g[L>>2]=+g[A>>2]*+g[L>>2];g[L+4>>2]=+g[A>>2]*+g[L+4>>2];V=d+(b[(c[(c[B>>2]|0)+44>>2]|0)+(c[x>>2]<<1)>>1]<<3)|0;c[V>>2]=c[L>>2];c[V+4>>2]=c[L+4>>2];c[x>>2]=(c[x>>2]|0)+1}xc(c[B>>2]|0,d);c[w>>2]=d;c[Q>>2]=c[p>>2];c[R>>2]=(c[p>>2]|0)+((_(c[s>>2]|0,(c[u>>2]|0)-1|0)|0)<<2);c[F>>2]=c[G>>2];c[x>>2]=0;while(1){if((c[x>>2]|0)>=(c[v>>2]|0))break;g[T>>2]=+g[(c[w>>2]|0)+4>>2]*+g[(c[F>>2]|0)+((c[v>>2]|0)+(c[x>>2]|0)<<2)>>2]-+g[c[w>>2]>>2]*+g[(c[F>>2]|0)+(c[x>>2]<<2)>>2];g[N>>2]=+g[c[w>>2]>>2]*+g[(c[F>>2]|0)+((c[v>>2]|0)+(c[x>>2]|0)<<2)>>2]+ +g[(c[w>>2]|0)+4>>2]*+g[(c[F>>2]|0)+(c[x>>2]<<2)>>2];g[c[Q>>2]>>2]=+g[T>>2];g[c[R>>2]>>2]=+g[N>>2];c[w>>2]=(c[w>>2]|0)+8;c[Q>>2]=(c[Q>>2]|0)+(c[s>>2]<<1<<2);c[R>>2]=(c[R>>2]|0)+(0-(c[s>>2]<<1)<<2);c[x>>2]=(c[x>>2]|0)+1}na(c[t>>2]|0);i=U;return}function Ic(a,d,e,f,h,j,k,l){a=a|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;var m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0.0;V=i;i=i+144|0;m=V+140|0;n=V+136|0;o=V+132|0;p=V+128|0;q=V+124|0;r=V+120|0;s=V+116|0;x=V+108|0;t=V+104|0;u=V+100|0;v=V+96|0;F=V+92|0;K=V+88|0;M=V+84|0;P=V+80|0;B=V+76|0;w=V+72|0;A=V+68|0;T=V+64|0;N=V+60|0;Q=V+56|0;R=V+52|0;D=V+48|0;z=V+44|0;y=V+40|0;U=V+36|0;O=V+32|0;C=V+28|0;E=V+24|0;L=V+20|0;S=V+16|0;G=V+12|0;H=V+8|0;I=V+4|0;J=V;c[m>>2]=a;c[n>>2]=d;c[o>>2]=e;c[p>>2]=f;c[q>>2]=h;c[r>>2]=j;c[s>>2]=k;c[V+112>>2]=l;c[t>>2]=c[c[m>>2]>>2];c[F>>2]=c[(c[m>>2]|0)+24>>2];c[x>>2]=0;while(1){l=c[t>>2]>>1;if((c[x>>2]|0)>=(c[r>>2]|0))break;c[t>>2]=l;c[F>>2]=(c[F>>2]|0)+(c[t>>2]<<2);c[x>>2]=(c[x>>2]|0)+1}c[u>>2]=l;c[v>>2]=c[t>>2]>>2;c[K>>2]=c[n>>2];c[M>>2]=(c[n>>2]|0)+((_(c[s>>2]|0,(c[u>>2]|0)-1|0)|0)<<2);c[P>>2]=(c[o>>2]|0)+(c[q>>2]>>1<<2);c[B>>2]=c[F>>2];c[w>>2]=c[(c[(c[m>>2]|0)+8+(c[r>>2]<<2)>>2]|0)+44>>2];c[x>>2]=0;while(1){if((c[x>>2]|0)>=(c[v>>2]|0))break;t=c[w>>2]|0;c[w>>2]=t+2;c[A>>2]=b[t>>1];g[T>>2]=+g[c[M>>2]>>2]*+g[(c[B>>2]|0)+(c[x>>2]<<2)>>2]+ +g[c[K>>2]>>2]*+g[(c[B>>2]|0)+((c[v>>2]|0)+(c[x>>2]|0)<<2)>>2];g[N>>2]=+g[c[K>>2]>>2]*+g[(c[B>>2]|0)+(c[x>>2]<<2)>>2]-+g[c[M>>2]>>2]*+g[(c[B>>2]|0)+((c[v>>2]|0)+(c[x>>2]|0)<<2)>>2];g[(c[P>>2]|0)+((c[A>>2]<<1)+1<<2)>>2]=+g[T>>2];g[(c[P>>2]|0)+(c[A>>2]<<1<<2)>>2]=+g[N>>2];c[K>>2]=(c[K>>2]|0)+(c[s>>2]<<1<<2);c[M>>2]=(c[M>>2]|0)+(0-(c[s>>2]<<1)<<2);c[x>>2]=(c[x>>2]|0)+1}xc(c[(c[m>>2]|0)+8+(c[r>>2]<<2)>>2]|0,(c[o>>2]|0)+(c[q>>2]>>1<<2)|0);c[Q>>2]=(c[o>>2]|0)+(c[q>>2]>>1<<2);c[R>>2]=(c[o>>2]|0)+(c[q>>2]>>1<<2)+(c[u>>2]<<2)+-8;c[D>>2]=c[F>>2];c[x>>2]=0;while(1){if((c[x>>2]|0)>=((c[v>>2]|0)+1>>1|0))break;g[z>>2]=+g[(c[Q>>2]|0)+4>>2];g[y>>2]=+g[c[Q>>2]>>2];g[C>>2]=+g[(c[D>>2]|0)+(c[x>>2]<<2)>>2];g[E>>2]=+g[(c[D>>2]|0)+((c[v>>2]|0)+(c[x>>2]|0)<<2)>>2];g[U>>2]=+g[z>>2]*+g[C>>2]+ +g[y>>2]*+g[E>>2];g[O>>2]=+g[z>>2]*+g[E>>2]-+g[y>>2]*+g[C>>2];g[z>>2]=+g[(c[R>>2]|0)+4>>2];g[y>>2]=+g[c[R>>2]>>2];g[c[Q>>2]>>2]=+g[U>>2];g[(c[R>>2]|0)+4>>2]=+g[O>>2];g[C>>2]=+g[(c[D>>2]|0)+((c[v>>2]|0)-(c[x>>2]|0)-1<<2)>>2];g[E>>2]=+g[(c[D>>2]|0)+((c[u>>2]|0)-(c[x>>2]|0)-1<<2)>>2];g[U>>2]=+g[z>>2]*+g[C>>2]+ +g[y>>2]*+g[E>>2];g[O>>2]=+g[z>>2]*+g[E>>2]-+g[y>>2]*+g[C>>2];g[c[R>>2]>>2]=+g[U>>2];g[(c[Q>>2]|0)+4>>2]=+g[O>>2];c[Q>>2]=(c[Q>>2]|0)+8;c[R>>2]=(c[R>>2]|0)+-8;c[x>>2]=(c[x>>2]|0)+1}c[L>>2]=(c[o>>2]|0)+(c[q>>2]<<2)+-4;c[S>>2]=c[o>>2];c[G>>2]=c[p>>2];c[H>>2]=(c[p>>2]|0)+(c[q>>2]<<2)+-4;c[x>>2]=0;while(1){if((c[x>>2]|0)>=((c[q>>2]|0)/2|0|0))break;g[I>>2]=+g[c[L>>2]>>2];g[J>>2]=+g[c[S>>2]>>2];W=+g[c[H>>2]>>2]*+g[J>>2]-+g[c[G>>2]>>2]*+g[I>>2];U=c[S>>2]|0;c[S>>2]=U+4;g[U>>2]=W;W=+g[c[G>>2]>>2]*+g[J>>2]+ +g[c[H>>2]>>2]*+g[I>>2];U=c[L>>2]|0;c[L>>2]=U+-4;g[U>>2]=W;c[G>>2]=(c[G>>2]|0)+4;c[H>>2]=(c[H>>2]|0)+-4;c[x>>2]=(c[x>>2]|0)+1}i=V;return}function Jc(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0;l=i;i=i+32|0;h=l+20|0;e=l+16|0;f=l+12|0;j=l+8|0;k=l+4|0;g=l;c[e>>2]=a;c[f>>2]=b;c[j>>2]=d;c[k>>2]=0;a:while(1){if((c[k>>2]|0)>=1){e=12;break}c[g>>2]=0;while(1){if((c[g>>2]|0)>=4)break;if((c[e>>2]|0)==(c[c[5544+(c[k>>2]<<2)>>2]>>2]|0)?(c[f>>2]<<c[g>>2]|0)==(_(c[(c[5544+(c[k>>2]<<2)>>2]|0)+44>>2]|0,c[(c[5544+(c[k>>2]<<2)>>2]|0)+40>>2]|0)|0):0){e=7;break a}c[g>>2]=(c[g>>2]|0)+1}c[k>>2]=(c[k>>2]|0)+1}if((e|0)==7){if(c[j>>2]|0)c[c[j>>2]>>2]=0;c[h>>2]=c[5544+(c[k>>2]<<2)>>2];k=c[h>>2]|0;i=l;return k|0}else if((e|0)==12){if(c[j>>2]|0)c[c[j>>2]>>2]=-1;c[h>>2]=0;k=c[h>>2]|0;i=l;return k|0}return 0}function Kc(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;u=i;i=i+112|0;h=u+108|0;j=u+104|0;k=u+100|0;l=u+96|0;m=u+92|0;p=u+88|0;n=u+68|0;t=u+64|0;q=u+48|0;s=u+24|0;r=u+4|0;o=u;c[h>>2]=a;c[j>>2]=b;c[k>>2]=d;c[l>>2]=e;c[m>>2]=f;g[t>>2]=1.0;c[s>>2]=0;c[s+4>>2]=0;c[s+8>>2]=0;c[s+12>>2]=0;c[s+16>>2]=0;g[o>>2]=.800000011920929;c[p>>2]=1;while(1){if((c[p>>2]|0)>=(c[k>>2]>>1|0))break;g[(c[j>>2]|0)+(c[p>>2]<<2)>>2]=((+g[(c[c[h>>2]>>2]|0)+((c[p>>2]<<1)-1<<2)>>2]+ +g[(c[c[h>>2]>>2]|0)+((c[p>>2]<<1)+1<<2)>>2])*.5+ +g[(c[c[h>>2]>>2]|0)+(c[p>>2]<<1<<2)>>2])*.5;c[p>>2]=(c[p>>2]|0)+1}g[c[j>>2]>>2]=(+g[(c[c[h>>2]>>2]|0)+4>>2]*.5+ +g[c[c[h>>2]>>2]>>2])*.5;if((c[l>>2]|0)==2){c[p>>2]=1;while(1){if((c[p>>2]|0)>=(c[k>>2]>>1|0))break;b=(c[j>>2]|0)+(c[p>>2]<<2)|0;g[b>>2]=+g[b>>2]+((+g[(c[(c[h>>2]|0)+4>>2]|0)+((c[p>>2]<<1)-1<<2)>>2]+ +g[(c[(c[h>>2]|0)+4>>2]|0)+((c[p>>2]<<1)+1<<2)>>2])*.5+ +g[(c[(c[h>>2]|0)+4>>2]|0)+(c[p>>2]<<1<<2)>>2])*.5;c[p>>2]=(c[p>>2]|0)+1}b=c[j>>2]|0;g[b>>2]=+g[b>>2]+(+g[(c[(c[h>>2]|0)+4>>2]|0)+4>>2]*.5+ +g[c[(c[h>>2]|0)+4>>2]>>2])*.5}Yc(c[j>>2]|0,n,0,0,4,c[k>>2]>>1,c[m>>2]|0)|0;g[n>>2]=+g[n>>2]*1.000100016593933;c[p>>2]=1;while(1){if((c[p>>2]|0)>4)break;b=n+(c[p>>2]<<2)|0;g[b>>2]=+g[b>>2]-+g[n+(c[p>>2]<<2)>>2]*(+(c[p>>2]|0)*.00800000037997961)*(+(c[p>>2]|0)*.00800000037997961);c[p>>2]=(c[p>>2]|0)+1}Uc(q,n,4);c[p>>2]=0;while(1){if((c[p>>2]|0)>=4)break;g[t>>2]=+g[t>>2]*.8999999761581421;g[q+(c[p>>2]<<2)>>2]=+g[q+(c[p>>2]<<2)>>2]*+g[t>>2];c[p>>2]=(c[p>>2]|0)+1}g[r>>2]=+g[q>>2]+.800000011920929;g[r+4>>2]=+g[q+4>>2]+ +g[o>>2]*+g[q>>2];g[r+8>>2]=+g[q+8>>2]+ +g[o>>2]*+g[q+4>>2];g[r+12>>2]=+g[q+12>>2]+ +g[o>>2]*+g[q+8>>2];g[r+16>>2]=+g[o>>2]*+g[q+12>>2];Lc(c[j>>2]|0,r,c[j>>2]|0,c[k>>2]>>1,s);i=u;return}function Lc(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0;y=i;i=i+80|0;h=y+64|0;z=y+60|0;j=y+56|0;k=y+52|0;l=y+48|0;m=y+44|0;s=y+40|0;t=y+36|0;u=y+32|0;v=y+28|0;w=y+24|0;n=y+20|0;o=y+16|0;p=y+12|0;q=y+8|0;r=y+4|0;x=y;c[h>>2]=a;c[z>>2]=b;c[j>>2]=d;c[k>>2]=e;c[l>>2]=f;g[s>>2]=+g[c[z>>2]>>2];g[t>>2]=+g[(c[z>>2]|0)+4>>2];g[u>>2]=+g[(c[z>>2]|0)+8>>2];g[v>>2]=+g[(c[z>>2]|0)+12>>2];g[w>>2]=+g[(c[z>>2]|0)+16>>2];g[n>>2]=+g[c[l>>2]>>2];g[o>>2]=+g[(c[l>>2]|0)+4>>2];g[p>>2]=+g[(c[l>>2]|0)+8>>2];g[q>>2]=+g[(c[l>>2]|0)+12>>2];g[r>>2]=+g[(c[l>>2]|0)+16>>2];c[m>>2]=0;while(1){if((c[m>>2]|0)>=(c[k>>2]|0))break;g[x>>2]=+g[(c[h>>2]|0)+(c[m>>2]<<2)>>2];g[x>>2]=+g[x>>2]+ +g[s>>2]*+g[n>>2];g[x>>2]=+g[x>>2]+ +g[t>>2]*+g[o>>2];g[x>>2]=+g[x>>2]+ +g[u>>2]*+g[p>>2];g[x>>2]=+g[x>>2]+ +g[v>>2]*+g[q>>2];g[x>>2]=+g[x>>2]+ +g[w>>2]*+g[r>>2];g[r>>2]=+g[q>>2];g[q>>2]=+g[p>>2];g[p>>2]=+g[o>>2];g[o>>2]=+g[n>>2];g[n>>2]=+g[(c[h>>2]|0)+(c[m>>2]<<2)>>2];g[(c[j>>2]|0)+(c[m>>2]<<2)>>2]=+g[x>>2];c[m>>2]=(c[m>>2]|0)+1}g[c[l>>2]>>2]=+g[n>>2];g[(c[l>>2]|0)+4>>2]=+g[o>>2];g[(c[l>>2]|0)+8>>2]=+g[p>>2];g[(c[l>>2]|0)+12>>2]=+g[q>>2];g[(c[l>>2]|0)+16>>2]=+g[r>>2];i=y;return}function Mc(a,b,d,e,f,h){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;r=i;i=i+64|0;j=r+48|0;k=r+44|0;l=r+40|0;m=r+36|0;n=r+32|0;o=r+24|0;p=r+8|0;q=r;c[j>>2]=a;c[k>>2]=b;c[l>>2]=d;c[m>>2]=e;c[n>>2]=f;c[r+28>>2]=h;c[o>>2]=0;while(1){if((c[o>>2]|0)>=((c[n>>2]|0)-3|0))break;c[p>>2]=0;c[p+4>>2]=0;c[p+8>>2]=0;c[p+12>>2]=0;Nc(c[j>>2]|0,(c[k>>2]|0)+(c[o>>2]<<2)|0,p,c[m>>2]|0);g[(c[l>>2]|0)+(c[o>>2]<<2)>>2]=+g[p>>2];g[(c[l>>2]|0)+((c[o>>2]|0)+1<<2)>>2]=+g[p+4>>2];g[(c[l>>2]|0)+((c[o>>2]|0)+2<<2)>>2]=+g[p+8>>2];g[(c[l>>2]|0)+((c[o>>2]|0)+3<<2)>>2]=+g[p+12>>2];c[o>>2]=(c[o>>2]|0)+4}while(1){if((c[o>>2]|0)>=(c[n>>2]|0))break;g[q>>2]=+Oc(c[j>>2]|0,(c[k>>2]|0)+(c[o>>2]<<2)|0,c[m>>2]|0);g[(c[l>>2]|0)+(c[o>>2]<<2)>>2]=+g[q>>2];c[o>>2]=(c[o>>2]|0)+1}i=r;return}function Nc(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;u=i;i=i+64|0;f=u+48|0;h=u+44|0;j=u+40|0;k=u+36|0;l=u+32|0;q=u+28|0;r=u+24|0;s=u+20|0;t=u+16|0;m=u+12|0;n=u+8|0;o=u+4|0;p=u;c[f>>2]=a;c[h>>2]=b;c[j>>2]=d;c[k>>2]=e;g[t>>2]=0.0;b=c[h>>2]|0;c[h>>2]=b+4;g[q>>2]=+g[b>>2];b=c[h>>2]|0;c[h>>2]=b+4;g[r>>2]=+g[b>>2];b=c[h>>2]|0;c[h>>2]=b+4;g[s>>2]=+g[b>>2];c[l>>2]=0;while(1){if((c[l>>2]|0)>=((c[k>>2]|0)-3|0))break;b=c[f>>2]|0;c[f>>2]=b+4;g[m>>2]=+g[b>>2];b=c[h>>2]|0;c[h>>2]=b+4;g[t>>2]=+g[b>>2];g[c[j>>2]>>2]=+g[c[j>>2]>>2]+ +g[m>>2]*+g[q>>2];g[(c[j>>2]|0)+4>>2]=+g[(c[j>>2]|0)+4>>2]+ +g[m>>2]*+g[r>>2];g[(c[j>>2]|0)+8>>2]=+g[(c[j>>2]|0)+8>>2]+ +g[m>>2]*+g[s>>2];g[(c[j>>2]|0)+12>>2]=+g[(c[j>>2]|0)+12>>2]+ +g[m>>2]*+g[t>>2];b=c[f>>2]|0;c[f>>2]=b+4;g[m>>2]=+g[b>>2];b=c[h>>2]|0;c[h>>2]=b+4;g[q>>2]=+g[b>>2];g[c[j>>2]>>2]=+g[c[j>>2]>>2]+ +g[m>>2]*+g[r>>2];g[(c[j>>2]|0)+4>>2]=+g[(c[j>>2]|0)+4>>2]+ +g[m>>2]*+g[s>>2];g[(c[j>>2]|0)+8>>2]=+g[(c[j>>2]|0)+8>>2]+ +g[m>>2]*+g[t>>2];g[(c[j>>2]|0)+12>>2]=+g[(c[j>>2]|0)+12>>2]+ +g[m>>2]*+g[q>>2];b=c[f>>2]|0;c[f>>2]=b+4;g[m>>2]=+g[b>>2];b=c[h>>2]|0;c[h>>2]=b+4;g[r>>2]=+g[b>>2];g[c[j>>2]>>2]=+g[c[j>>2]>>2]+ +g[m>>2]*+g[s>>2];g[(c[j>>2]|0)+4>>2]=+g[(c[j>>2]|0)+4>>2]+ +g[m>>2]*+g[t>>2];g[(c[j>>2]|0)+8>>2]=+g[(c[j>>2]|0)+8>>2]+ +g[m>>2]*+g[q>>2];g[(c[j>>2]|0)+12>>2]=+g[(c[j>>2]|0)+12>>2]+ +g[m>>2]*+g[r>>2];b=c[f>>2]|0;c[f>>2]=b+4;g[m>>2]=+g[b>>2];b=c[h>>2]|0;c[h>>2]=b+4;g[s>>2]=+g[b>>2];g[c[j>>2]>>2]=+g[c[j>>2]>>2]+ +g[m>>2]*+g[t>>2];g[(c[j>>2]|0)+4>>2]=+g[(c[j>>2]|0)+4>>2]+ +g[m>>2]*+g[q>>2];g[(c[j>>2]|0)+8>>2]=+g[(c[j>>2]|0)+8>>2]+ +g[m>>2]*+g[r>>2];g[(c[j>>2]|0)+12>>2]=+g[(c[j>>2]|0)+12>>2]+ +g[m>>2]*+g[s>>2];c[l>>2]=(c[l>>2]|0)+4}b=c[l>>2]|0;c[l>>2]=b+1;if((b|0)<(c[k>>2]|0)){b=c[f>>2]|0;c[f>>2]=b+4;g[n>>2]=+g[b>>2];b=c[h>>2]|0;c[h>>2]=b+4;g[t>>2]=+g[b>>2];g[c[j>>2]>>2]=+g[c[j>>2]>>2]+ +g[n>>2]*+g[q>>2];g[(c[j>>2]|0)+4>>2]=+g[(c[j>>2]|0)+4>>2]+ +g[n>>2]*+g[r>>2];g[(c[j>>2]|0)+8>>2]=+g[(c[j>>2]|0)+8>>2]+ +g[n>>2]*+g[s>>2];g[(c[j>>2]|0)+12>>2]=+g[(c[j>>2]|0)+12>>2]+ +g[n>>2]*+g[t>>2]}b=c[l>>2]|0;c[l>>2]=b+1;if((b|0)<(c[k>>2]|0)){b=c[f>>2]|0;c[f>>2]=b+4;g[o>>2]=+g[b>>2];b=c[h>>2]|0;c[h>>2]=b+4;g[q>>2]=+g[b>>2];g[c[j>>2]>>2]=+g[c[j>>2]>>2]+ +g[o>>2]*+g[r>>2];g[(c[j>>2]|0)+4>>2]=+g[(c[j>>2]|0)+4>>2]+ +g[o>>2]*+g[s>>2];g[(c[j>>2]|0)+8>>2]=+g[(c[j>>2]|0)+8>>2]+ +g[o>>2]*+g[t>>2];g[(c[j>>2]|0)+12>>2]=+g[(c[j>>2]|0)+12>>2]+ +g[o>>2]*+g[q>>2]}if((c[l>>2]|0)>=(c[k>>2]|0)){i=u;return}b=c[f>>2]|0;c[f>>2]=b+4;g[p>>2]=+g[b>>2];b=c[h>>2]|0;c[h>>2]=b+4;g[r>>2]=+g[b>>2];g[c[j>>2]>>2]=+g[c[j>>2]>>2]+ +g[p>>2]*+g[s>>2];g[(c[j>>2]|0)+4>>2]=+g[(c[j>>2]|0)+4>>2]+ +g[p>>2]*+g[t>>2];g[(c[j>>2]|0)+8>>2]=+g[(c[j>>2]|0)+8>>2]+ +g[p>>2]*+g[q>>2];g[(c[j>>2]|0)+12>>2]=+g[(c[j>>2]|0)+12>>2]+ +g[p>>2]*+g[r>>2];i=u;return}function Oc(a,b,d){a=a|0;b=b|0;d=d|0;var e=0.0,f=0,h=0,j=0,k=0,l=0,m=0;m=i;i=i+32|0;f=m+16|0;h=m+12|0;j=m+8|0;k=m+4|0;l=m;c[f>>2]=a;c[h>>2]=b;c[j>>2]=d;g[l>>2]=0.0;c[k>>2]=0;while(1){e=+g[l>>2];if((c[k>>2]|0)>=(c[j>>2]|0))break;g[l>>2]=e+ +g[(c[f>>2]|0)+(c[k>>2]<<2)>>2]*+g[(c[h>>2]|0)+(c[k>>2]<<2)>>2];c[k>>2]=(c[k>>2]|0)+1}i=m;return +e}function Pc(a,b,d,e,f,h){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0;z=i;i=i+80|0;j=z+64|0;k=z+60|0;l=z+56|0;m=z+52|0;n=z+48|0;o=z+44|0;u=z+40|0;v=z+36|0;w=z+32|0;s=z+24|0;x=z+20|0;p=z+16|0;y=z+12|0;q=z+8|0;r=z+4|0;t=z;c[j>>2]=a;c[k>>2]=b;c[l>>2]=d;c[m>>2]=e;c[n>>2]=f;c[o>>2]=h;c[s>>2]=0;c[s+4>>2]=0;c[w>>2]=(c[l>>2]|0)+(c[m>>2]|0);d=c[l>>2]>>2;c[p>>2]=ia()|0;h=i;i=i+((1*(d<<2)|0)+15&-16)|0;d=i;i=i+((1*(c[w>>2]>>2<<2)|0)+15&-16)|0;e=i;i=i+((1*(c[m>>2]>>1<<2)|0)+15&-16)|0;c[v>>2]=0;while(1){if((c[v>>2]|0)>=(c[l>>2]>>2|0))break;g[h+(c[v>>2]<<2)>>2]=+g[(c[j>>2]|0)+(c[v>>2]<<1<<2)>>2];c[v>>2]=(c[v>>2]|0)+1}c[v>>2]=0;while(1){if((c[v>>2]|0)>=(c[w>>2]>>2|0))break;g[d+(c[v>>2]<<2)>>2]=+g[(c[k>>2]|0)+(c[v>>2]<<1<<2)>>2];c[v>>2]=(c[v>>2]|0)+1}Mc(h,d,e,c[l>>2]>>2,c[m>>2]>>2,c[o>>2]|0);Qc(e,d,c[l>>2]>>2,c[m>>2]>>2,s);c[u>>2]=0;while(1){if((c[u>>2]|0)>=(c[m>>2]>>1|0))break;g[e+(c[u>>2]<<2)>>2]=0.0;if(!((N((c[u>>2]|0)-(c[s>>2]<<1)|0)|0)>2?(N((c[u>>2]|0)-(c[s+4>>2]<<1)|0)|0)>2:0)){g[y>>2]=+Oc(c[j>>2]|0,(c[k>>2]|0)+(c[u>>2]<<2)|0,c[l>>2]>>1);g[e+(c[u>>2]<<2)>>2]=-1.0>+g[y>>2]?-1.0:+g[y>>2]}c[u>>2]=(c[u>>2]|0)+1}Qc(e,c[k>>2]|0,c[l>>2]>>1,c[m>>2]>>1,s);if((c[s>>2]|0)>0?(c[s>>2]|0)<((c[m>>2]>>1)-1|0):0){g[q>>2]=+g[e+((c[s>>2]|0)-1<<2)>>2];g[r>>2]=+g[e+(c[s>>2]<<2)>>2];g[t>>2]=+g[e+((c[s>>2]|0)+1<<2)>>2];if(+g[t>>2]-+g[q>>2]>(+g[r>>2]-+g[q>>2])*.699999988079071){c[x>>2]=1;y=c[s>>2]|0;y=y<<1;x=c[x>>2]|0;x=y-x|0;y=c[n>>2]|0;c[y>>2]=x;y=c[p>>2]|0;na(y|0);i=z;return}if(+g[q>>2]-+g[t>>2]>(+g[r>>2]-+g[t>>2])*.699999988079071){c[x>>2]=-1;y=c[s>>2]|0;y=y<<1;x=c[x>>2]|0;x=y-x|0;y=c[n>>2]|0;c[y>>2]=x;y=c[p>>2]|0;na(y|0);i=z;return}else{c[x>>2]=0;y=c[s>>2]|0;y=y<<1;x=c[x>>2]|0;x=y-x|0;y=c[n>>2]|0;c[y>>2]=x;y=c[p>>2]|0;na(y|0);i=z;return}}c[x>>2]=0;y=c[s>>2]|0;y=y<<1;x=c[x>>2]|0;x=y-x|0;y=c[n>>2]|0;c[y>>2]=x;y=c[p>>2]|0;na(y|0);i=z;return}function Qc(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;u=i;i=i+64|0;h=u+52|0;j=u+48|0;k=u+44|0;l=u+40|0;m=u+36|0;q=u+32|0;r=u+28|0;n=u+24|0;p=u+16|0;o=u+8|0;s=u+4|0;t=u;c[h>>2]=a;c[j>>2]=b;c[k>>2]=d;c[l>>2]=e;c[m>>2]=f;g[n>>2]=1.0;g[p>>2]=-1.0;g[p+4>>2]=-1.0;g[o>>2]=0.0;g[o+4>>2]=0.0;c[c[m>>2]>>2]=0;c[(c[m>>2]|0)+4>>2]=1;c[r>>2]=0;while(1){if((c[r>>2]|0)>=(c[k>>2]|0))break;g[n>>2]=+g[n>>2]+ +g[(c[j>>2]|0)+(c[r>>2]<<2)>>2]*+g[(c[j>>2]|0)+(c[r>>2]<<2)>>2];c[r>>2]=(c[r>>2]|0)+1}c[q>>2]=0;while(1){if((c[q>>2]|0)>=(c[l>>2]|0))break;do if(+g[(c[h>>2]|0)+(c[q>>2]<<2)>>2]>0.0?(g[t>>2]=+g[(c[h>>2]|0)+(c[q>>2]<<2)>>2],g[t>>2]=+g[t>>2]*9.999999960041972e-13,g[s>>2]=+g[t>>2]*+g[t>>2],+g[s>>2]*+g[o+4>>2]>+g[p+4>>2]*+g[n>>2]):0)if(+g[s>>2]*+g[o>>2]>+g[p>>2]*+g[n>>2]){g[p+4>>2]=+g[p>>2];g[o+4>>2]=+g[o>>2];c[(c[m>>2]|0)+4>>2]=c[c[m>>2]>>2];g[p>>2]=+g[s>>2];g[o>>2]=+g[n>>2];c[c[m>>2]>>2]=c[q>>2];break}else{g[p+4>>2]=+g[s>>2];g[o+4>>2]=+g[n>>2];c[(c[m>>2]|0)+4>>2]=c[q>>2];break}while(0);g[n>>2]=+g[n>>2]+(+g[(c[j>>2]|0)+((c[q>>2]|0)+(c[k>>2]|0)<<2)>>2]*+g[(c[j>>2]|0)+((c[q>>2]|0)+(c[k>>2]|0)<<2)>>2]-+g[(c[j>>2]|0)+(c[q>>2]<<2)>>2]*+g[(c[j>>2]|0)+(c[q>>2]<<2)>>2]);g[n>>2]=1.0>+g[n>>2]?1.0:+g[n>>2];c[q>>2]=(c[q>>2]|0)+1}i=u;return}function Rc(a,b,d,e,f,h,j,k){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=+j;k=k|0;var l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,P=0,Q=0;Q=i;i=i+128|0;m=Q+124|0;n=Q+120|0;o=Q+116|0;p=Q+112|0;q=Q+108|0;r=Q+104|0;s=Q+100|0;E=Q+92|0;l=Q+88|0;u=Q+84|0;v=Q+80|0;B=Q+76|0;C=Q+72|0;H=Q+68|0;L=Q+64|0;K=Q+60|0;P=Q+56|0;M=Q+52|0;J=Q+40|0;y=Q+36|0;z=Q+32|0;G=Q+28|0;F=Q+24|0;t=Q+20|0;w=Q+16|0;x=Q+12|0;D=Q+8|0;A=Q+4|0;I=Q;c[m>>2]=a;c[n>>2]=b;c[o>>2]=d;c[p>>2]=e;c[q>>2]=f;c[r>>2]=h;g[s>>2]=j;c[Q+96>>2]=k;c[F>>2]=c[o>>2];c[n>>2]=(c[n>>2]|0)/2|0;c[o>>2]=(c[o>>2]|0)/2|0;a=c[q>>2]|0;c[a>>2]=(c[a>>2]|0)/2|0;c[r>>2]=(c[r>>2]|0)/2|0;c[p>>2]=(c[p>>2]|0)/2|0;c[m>>2]=(c[m>>2]|0)+(c[n>>2]<<2);if((c[c[q>>2]>>2]|0)>=(c[n>>2]|0))c[c[q>>2]>>2]=(c[n>>2]|0)-1;a=c[c[q>>2]>>2]|0;c[v>>2]=a;c[u>>2]=a;a=(c[n>>2]|0)+1|0;c[t>>2]=ia()|0;f=i;i=i+((1*(a<<2)|0)+15&-16)|0;Sc(c[m>>2]|0,c[m>>2]|0,(c[m>>2]|0)+(0-(c[v>>2]|0)<<2)|0,c[p>>2]|0,K,L);g[f>>2]=+g[K>>2];g[P>>2]=+g[K>>2];c[l>>2]=1;while(1){if((c[l>>2]|0)>(c[n>>2]|0))break;g[P>>2]=+g[P>>2]+ +g[(c[m>>2]|0)+(0-(c[l>>2]|0)<<2)>>2]*+g[(c[m>>2]|0)+(0-(c[l>>2]|0)<<2)>>2]-+g[(c[m>>2]|0)+((c[p>>2]|0)-(c[l>>2]|0)<<2)>>2]*+g[(c[m>>2]|0)+((c[p>>2]|0)-(c[l>>2]|0)<<2)>>2];g[f+(c[l>>2]<<2)>>2]=0.0>+g[P>>2]?0.0:+g[P>>2];c[l>>2]=(c[l>>2]|0)+1}g[P>>2]=+g[f+(c[v>>2]<<2)>>2];g[y>>2]=+g[L>>2];g[z>>2]=+g[P>>2];j=+g[L>>2]/+O(+(+g[K>>2]*+g[P>>2]+1.0));g[C>>2]=j;g[B>>2]=j;c[E>>2]=2;while(1){if((c[E>>2]|0)>15)break;g[A>>2]=0.0;c[w>>2]=Tc((c[v>>2]<<1)+(c[E>>2]|0)|0,c[E>>2]<<1)|0;if((c[w>>2]|0)<(c[o>>2]|0))break;do if((c[E>>2]|0)==2){e=c[v>>2]|0;if(((c[w>>2]|0)+(c[v>>2]|0)|0)>(c[n>>2]|0)){c[x>>2]=e;break}else{c[x>>2]=e+(c[w>>2]|0);break}}else{l=_(c[17400+(c[E>>2]<<2)>>2]<<1,c[v>>2]|0)|0;c[x>>2]=Tc(l+(c[E>>2]|0)|0,c[E>>2]<<1)|0}while(0);Sc(c[m>>2]|0,(c[m>>2]|0)+(0-(c[w>>2]|0)<<2)|0,(c[m>>2]|0)+(0-(c[x>>2]|0)<<2)|0,c[p>>2]|0,L,M);g[L>>2]=+g[L>>2]+ +g[M>>2];g[P>>2]=+g[f+(c[w>>2]<<2)>>2]+ +g[f+(c[x>>2]<<2)>>2];g[D>>2]=+g[L>>2]/+O(+(+g[K>>2]*2.0*1.0*+g[P>>2]+1.0));do if((N((c[w>>2]|0)-(c[r>>2]|0)|0)|0)<=1)g[A>>2]=+g[s>>2];else{if((N((c[w>>2]|0)-(c[r>>2]|0)|0)|0)<=2?(l=_((c[E>>2]|0)*5|0,c[E>>2]|0)|0,(l|0)<(c[v>>2]|0)):0){g[A>>2]=+g[s>>2]*.5;break}g[A>>2]=0.0}while(0);if(.30000001192092896>+g[C>>2]*.699999988079071-+g[A>>2])j=.30000001192092896;else j=+g[C>>2]*.699999988079071-+g[A>>2];g[I>>2]=j;if((c[w>>2]|0)>=((c[o>>2]|0)*3|0)){if((c[w>>2]|0)<(c[o>>2]<<1|0)){if(.5>+g[C>>2]*.8999999761581421-+g[A>>2])j=.5;else j=+g[C>>2]*.8999999761581421-+g[A>>2];g[I>>2]=j}}else{if(.4000000059604645>+g[C>>2]*.8500000238418579-+g[A>>2])j=.4000000059604645;else j=+g[C>>2]*.8500000238418579-+g[A>>2];g[I>>2]=j}if(+g[D>>2]>+g[I>>2]){g[y>>2]=+g[L>>2];g[z>>2]=+g[P>>2];c[u>>2]=c[w>>2];g[B>>2]=+g[D>>2]}c[E>>2]=(c[E>>2]|0)+1}g[y>>2]=0.0>+g[y>>2]?0.0:+g[y>>2];if(+g[z>>2]<=+g[y>>2])g[H>>2]=1.0;else g[H>>2]=+g[y>>2]/(+g[z>>2]+1.0);c[E>>2]=0;while(1){if((c[E>>2]|0)>=3)break;j=+Oc(c[m>>2]|0,(c[m>>2]|0)+(0-((c[u>>2]|0)+(c[E>>2]|0)-1)<<2)|0,c[p>>2]|0);g[J+(c[E>>2]<<2)>>2]=j;c[E>>2]=(c[E>>2]|0)+1}do if(!(+g[J+8>>2]-+g[J>>2]>(+g[J+4>>2]-+g[J>>2])*.699999988079071))if(+g[J>>2]-+g[J+8>>2]>(+g[J+4>>2]-+g[J+8>>2])*.699999988079071){c[G>>2]=-1;break}else{c[G>>2]=0;break}else c[G>>2]=1;while(0);if(+g[H>>2]>+g[B>>2])g[H>>2]=+g[B>>2];c[c[q>>2]>>2]=(c[u>>2]<<1)+(c[G>>2]|0);if((c[c[q>>2]>>2]|0)>=(c[F>>2]|0)){j=+g[H>>2];P=c[t>>2]|0;na(P|0);i=Q;return +j}c[c[q>>2]>>2]=c[F>>2];j=+g[H>>2];P=c[t>>2]|0;na(P|0);i=Q;return +j}function Sc(a,b,d,e,f,h){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;var j=0.0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;t=i;i=i+48|0;k=t+32|0;l=t+28|0;m=t+24|0;n=t+20|0;o=t+16|0;p=t+12|0;q=t+8|0;r=t+4|0;s=t;c[k>>2]=a;c[l>>2]=b;c[m>>2]=d;c[n>>2]=e;c[o>>2]=f;c[p>>2]=h;g[r>>2]=0.0;g[s>>2]=0.0;c[q>>2]=0;while(1){j=+g[r>>2];if((c[q>>2]|0)>=(c[n>>2]|0))break;g[r>>2]=j+ +g[(c[k>>2]|0)+(c[q>>2]<<2)>>2]*+g[(c[l>>2]|0)+(c[q>>2]<<2)>>2];g[s>>2]=+g[s>>2]+ +g[(c[k>>2]|0)+(c[q>>2]<<2)>>2]*+g[(c[m>>2]|0)+(c[q>>2]<<2)>>2];c[q>>2]=(c[q>>2]|0)+1}g[c[o>>2]>>2]=j;g[c[p>>2]>>2]=+g[s>>2];i=t;return}function Tc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)>>>0)/((c[d>>2]|0)>>>0)|0|0}function Uc(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;q=i;i=i+48|0;r=q+40|0;e=q+36|0;f=q+32|0;j=q+28|0;k=q+24|0;m=q+20|0;h=q+16|0;l=q+12|0;n=q+8|0;o=q+4|0;p=q;c[r>>2]=a;c[e>>2]=b;c[f>>2]=d;g[h>>2]=+g[c[e>>2]>>2];c[l>>2]=c[r>>2];c[j>>2]=0;while(1){if((c[j>>2]|0)>=(c[f>>2]|0))break;g[(c[l>>2]|0)+(c[j>>2]<<2)>>2]=0.0;c[j>>2]=(c[j>>2]|0)+1}if(!(+g[c[e>>2]>>2]!=0.0)){i=q;return}c[j>>2]=0;while(1){if((c[j>>2]|0)>=(c[f>>2]|0)){e=15;break}g[n>>2]=0.0;c[k>>2]=0;while(1){if((c[k>>2]|0)>=(c[j>>2]|0))break;g[n>>2]=+g[n>>2]+ +g[(c[l>>2]|0)+(c[k>>2]<<2)>>2]*+g[(c[e>>2]|0)+((c[j>>2]|0)-(c[k>>2]|0)<<2)>>2];c[k>>2]=(c[k>>2]|0)+1}g[n>>2]=+g[n>>2]+ +g[(c[e>>2]|0)+((c[j>>2]|0)+1<<2)>>2];g[m>>2]=-(+g[n>>2]/+g[h>>2]);g[(c[l>>2]|0)+(c[j>>2]<<2)>>2]=+g[m>>2];c[k>>2]=0;while(1){if((c[k>>2]|0)>=((c[j>>2]|0)+1>>1|0))break;g[o>>2]=+g[(c[l>>2]|0)+(c[k>>2]<<2)>>2];g[p>>2]=+g[(c[l>>2]|0)+((c[j>>2]|0)-1-(c[k>>2]|0)<<2)>>2];g[(c[l>>2]|0)+(c[k>>2]<<2)>>2]=+g[o>>2]+ +g[m>>2]*+g[p>>2];g[(c[l>>2]|0)+((c[j>>2]|0)-1-(c[k>>2]|0)<<2)>>2]=+g[p>>2]+ +g[m>>2]*+g[o>>2];c[k>>2]=(c[k>>2]|0)+1}g[h>>2]=+g[h>>2]-+g[m>>2]*+g[m>>2]*+g[h>>2];if(+g[h>>2]<+g[c[e>>2]>>2]*1.0000000474974513e-03){e=15;break}c[j>>2]=(c[j>>2]|0)+1}if((e|0)==15){i=q;return}}function Vc(a,b,d,e,f,h,j){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;v=i;i=i+64|0;k=v+60|0;l=v+56|0;m=v+52|0;n=v+48|0;o=v+44|0;p=v+40|0;r=v+32|0;s=v+28|0;q=v+24|0;t=v+8|0;u=v;c[k>>2]=a;c[l>>2]=b;c[m>>2]=d;c[n>>2]=e;c[o>>2]=f;c[p>>2]=h;c[v+36>>2]=j;a=c[o>>2]|0;c[q>>2]=ia()|0;e=i;i=i+((1*(a<<2)|0)+15&-16)|0;a=i;i=i+((1*((c[n>>2]|0)+(c[o>>2]|0)<<2)|0)+15&-16)|0;c[r>>2]=0;while(1){if((c[r>>2]|0)>=(c[o>>2]|0))break;g[e+(c[r>>2]<<2)>>2]=+g[(c[l>>2]|0)+((c[o>>2]|0)-(c[r>>2]|0)-1<<2)>>2];c[r>>2]=(c[r>>2]|0)+1}c[r>>2]=0;while(1){if((c[r>>2]|0)>=(c[o>>2]|0))break;g[a+(c[r>>2]<<2)>>2]=+g[(c[p>>2]|0)+((c[o>>2]|0)-(c[r>>2]|0)-1<<2)>>2];c[r>>2]=(c[r>>2]|0)+1}c[r>>2]=0;while(1){if((c[r>>2]|0)>=(c[n>>2]|0))break;g[a+((c[r>>2]|0)+(c[o>>2]|0)<<2)>>2]=+g[(c[k>>2]|0)+(c[r>>2]<<2)>>2];c[r>>2]=(c[r>>2]|0)+1}c[r>>2]=0;while(1){if((c[r>>2]|0)>=(c[o>>2]|0))break;g[(c[p>>2]|0)+(c[r>>2]<<2)>>2]=+g[(c[k>>2]|0)+((c[n>>2]|0)-(c[r>>2]|0)-1<<2)>>2];c[r>>2]=(c[r>>2]|0)+1}c[r>>2]=0;while(1){if((c[r>>2]|0)>=((c[n>>2]|0)-3|0))break;c[t>>2]=0;c[t+4>>2]=0;c[t+8>>2]=0;c[t+12>>2]=0;Wc(e,a+(c[r>>2]<<2)|0,t,c[o>>2]|0);g[(c[m>>2]|0)+(c[r>>2]<<2)>>2]=+g[(c[k>>2]|0)+(c[r>>2]<<2)>>2]+ +g[t>>2];g[(c[m>>2]|0)+((c[r>>2]|0)+1<<2)>>2]=+g[(c[k>>2]|0)+((c[r>>2]|0)+1<<2)>>2]+ +g[t+4>>2];g[(c[m>>2]|0)+((c[r>>2]|0)+2<<2)>>2]=+g[(c[k>>2]|0)+((c[r>>2]|0)+2<<2)>>2]+ +g[t+8>>2];g[(c[m>>2]|0)+((c[r>>2]|0)+3<<2)>>2]=+g[(c[k>>2]|0)+((c[r>>2]|0)+3<<2)>>2]+ +g[t+12>>2];c[r>>2]=(c[r>>2]|0)+4}while(1){if((c[r>>2]|0)>=(c[n>>2]|0))break;g[u>>2]=0.0;c[s>>2]=0;while(1){if((c[s>>2]|0)>=(c[o>>2]|0))break;g[u>>2]=+g[u>>2]+ +g[e+(c[s>>2]<<2)>>2]*+g[a+((c[r>>2]|0)+(c[s>>2]|0)<<2)>>2];c[s>>2]=(c[s>>2]|0)+1}g[(c[m>>2]|0)+(c[r>>2]<<2)>>2]=+g[(c[k>>2]|0)+(c[r>>2]<<2)>>2]+ +g[u>>2];c[r>>2]=(c[r>>2]|0)+1}na(c[q>>2]|0);i=v;return}function Wc(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;u=i;i=i+64|0;f=u+48|0;h=u+44|0;j=u+40|0;k=u+36|0;l=u+32|0;q=u+28|0;r=u+24|0;s=u+20|0;t=u+16|0;m=u+12|0;n=u+8|0;o=u+4|0;p=u;c[f>>2]=a;c[h>>2]=b;c[j>>2]=d;c[k>>2]=e;g[t>>2]=0.0;b=c[h>>2]|0;c[h>>2]=b+4;g[q>>2]=+g[b>>2];b=c[h>>2]|0;c[h>>2]=b+4;g[r>>2]=+g[b>>2];b=c[h>>2]|0;c[h>>2]=b+4;g[s>>2]=+g[b>>2];c[l>>2]=0;while(1){if((c[l>>2]|0)>=((c[k>>2]|0)-3|0))break;b=c[f>>2]|0;c[f>>2]=b+4;g[m>>2]=+g[b>>2];b=c[h>>2]|0;c[h>>2]=b+4;g[t>>2]=+g[b>>2];g[c[j>>2]>>2]=+g[c[j>>2]>>2]+ +g[m>>2]*+g[q>>2];g[(c[j>>2]|0)+4>>2]=+g[(c[j>>2]|0)+4>>2]+ +g[m>>2]*+g[r>>2];g[(c[j>>2]|0)+8>>2]=+g[(c[j>>2]|0)+8>>2]+ +g[m>>2]*+g[s>>2];g[(c[j>>2]|0)+12>>2]=+g[(c[j>>2]|0)+12>>2]+ +g[m>>2]*+g[t>>2];b=c[f>>2]|0;c[f>>2]=b+4;g[m>>2]=+g[b>>2];b=c[h>>2]|0;c[h>>2]=b+4;g[q>>2]=+g[b>>2];g[c[j>>2]>>2]=+g[c[j>>2]>>2]+ +g[m>>2]*+g[r>>2];g[(c[j>>2]|0)+4>>2]=+g[(c[j>>2]|0)+4>>2]+ +g[m>>2]*+g[s>>2];g[(c[j>>2]|0)+8>>2]=+g[(c[j>>2]|0)+8>>2]+ +g[m>>2]*+g[t>>2];g[(c[j>>2]|0)+12>>2]=+g[(c[j>>2]|0)+12>>2]+ +g[m>>2]*+g[q>>2];b=c[f>>2]|0;c[f>>2]=b+4;g[m>>2]=+g[b>>2];b=c[h>>2]|0;c[h>>2]=b+4;g[r>>2]=+g[b>>2];g[c[j>>2]>>2]=+g[c[j>>2]>>2]+ +g[m>>2]*+g[s>>2];g[(c[j>>2]|0)+4>>2]=+g[(c[j>>2]|0)+4>>2]+ +g[m>>2]*+g[t>>2];g[(c[j>>2]|0)+8>>2]=+g[(c[j>>2]|0)+8>>2]+ +g[m>>2]*+g[q>>2];g[(c[j>>2]|0)+12>>2]=+g[(c[j>>2]|0)+12>>2]+ +g[m>>2]*+g[r>>2];b=c[f>>2]|0;c[f>>2]=b+4;g[m>>2]=+g[b>>2];b=c[h>>2]|0;c[h>>2]=b+4;g[s>>2]=+g[b>>2];g[c[j>>2]>>2]=+g[c[j>>2]>>2]+ +g[m>>2]*+g[t>>2];g[(c[j>>2]|0)+4>>2]=+g[(c[j>>2]|0)+4>>2]+ +g[m>>2]*+g[q>>2];g[(c[j>>2]|0)+8>>2]=+g[(c[j>>2]|0)+8>>2]+ +g[m>>2]*+g[r>>2];g[(c[j>>2]|0)+12>>2]=+g[(c[j>>2]|0)+12>>2]+ +g[m>>2]*+g[s>>2];c[l>>2]=(c[l>>2]|0)+4}b=c[l>>2]|0;c[l>>2]=b+1;if((b|0)<(c[k>>2]|0)){b=c[f>>2]|0;c[f>>2]=b+4;g[n>>2]=+g[b>>2];b=c[h>>2]|0;c[h>>2]=b+4;g[t>>2]=+g[b>>2];g[c[j>>2]>>2]=+g[c[j>>2]>>2]+ +g[n>>2]*+g[q>>2];g[(c[j>>2]|0)+4>>2]=+g[(c[j>>2]|0)+4>>2]+ +g[n>>2]*+g[r>>2];g[(c[j>>2]|0)+8>>2]=+g[(c[j>>2]|0)+8>>2]+ +g[n>>2]*+g[s>>2];g[(c[j>>2]|0)+12>>2]=+g[(c[j>>2]|0)+12>>2]+ +g[n>>2]*+g[t>>2]}b=c[l>>2]|0;c[l>>2]=b+1;if((b|0)<(c[k>>2]|0)){b=c[f>>2]|0;c[f>>2]=b+4;g[o>>2]=+g[b>>2];b=c[h>>2]|0;c[h>>2]=b+4;g[q>>2]=+g[b>>2];g[c[j>>2]>>2]=+g[c[j>>2]>>2]+ +g[o>>2]*+g[r>>2];g[(c[j>>2]|0)+4>>2]=+g[(c[j>>2]|0)+4>>2]+ +g[o>>2]*+g[s>>2];g[(c[j>>2]|0)+8>>2]=+g[(c[j>>2]|0)+8>>2]+ +g[o>>2]*+g[t>>2];g[(c[j>>2]|0)+12>>2]=+g[(c[j>>2]|0)+12>>2]+ +g[o>>2]*+g[q>>2]}if((c[l>>2]|0)>=(c[k>>2]|0)){i=u;return}b=c[f>>2]|0;c[f>>2]=b+4;g[p>>2]=+g[b>>2];b=c[h>>2]|0;c[h>>2]=b+4;g[r>>2]=+g[b>>2];g[c[j>>2]>>2]=+g[c[j>>2]>>2]+ +g[p>>2]*+g[s>>2];g[(c[j>>2]|0)+4>>2]=+g[(c[j>>2]|0)+4>>2]+ +g[p>>2]*+g[t>>2];g[(c[j>>2]|0)+8>>2]=+g[(c[j>>2]|0)+8>>2]+ +g[p>>2]*+g[q>>2];g[(c[j>>2]|0)+12>>2]=+g[(c[j>>2]|0)+12>>2]+ +g[p>>2]*+g[r>>2];i=u;return}function Xc(a,b,d,e,f,h,j){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;v=i;i=i+64|0;k=v+60|0;l=v+56|0;m=v+52|0;n=v+48|0;o=v+44|0;p=v+40|0;r=v+32|0;s=v+28|0;q=v+24|0;t=v+8|0;u=v;c[k>>2]=a;c[l>>2]=b;c[m>>2]=d;c[n>>2]=e;c[o>>2]=f;c[p>>2]=h;c[v+36>>2]=j;a=c[o>>2]|0;c[q>>2]=ia()|0;e=i;i=i+((1*(a<<2)|0)+15&-16)|0;a=i;i=i+((1*((c[n>>2]|0)+(c[o>>2]|0)<<2)|0)+15&-16)|0;c[r>>2]=0;while(1){if((c[r>>2]|0)>=(c[o>>2]|0))break;g[e+(c[r>>2]<<2)>>2]=+g[(c[l>>2]|0)+((c[o>>2]|0)-(c[r>>2]|0)-1<<2)>>2];c[r>>2]=(c[r>>2]|0)+1}c[r>>2]=0;while(1){if((c[r>>2]|0)>=(c[o>>2]|0))break;g[a+(c[r>>2]<<2)>>2]=-+g[(c[p>>2]|0)+((c[o>>2]|0)-(c[r>>2]|0)-1<<2)>>2];c[r>>2]=(c[r>>2]|0)+1}while(1){if((c[r>>2]|0)>=((c[n>>2]|0)+(c[o>>2]|0)|0))break;g[a+(c[r>>2]<<2)>>2]=0.0;c[r>>2]=(c[r>>2]|0)+1}c[r>>2]=0;while(1){if((c[r>>2]|0)>=((c[n>>2]|0)-3|0))break;g[t>>2]=+g[(c[k>>2]|0)+(c[r>>2]<<2)>>2];g[t+4>>2]=+g[(c[k>>2]|0)+((c[r>>2]|0)+1<<2)>>2];g[t+8>>2]=+g[(c[k>>2]|0)+((c[r>>2]|0)+2<<2)>>2];g[t+12>>2]=+g[(c[k>>2]|0)+((c[r>>2]|0)+3<<2)>>2];Wc(e,a+(c[r>>2]<<2)|0,t,c[o>>2]|0);g[a+((c[r>>2]|0)+(c[o>>2]|0)<<2)>>2]=-+g[t>>2];g[(c[m>>2]|0)+(c[r>>2]<<2)>>2]=+g[t>>2];g[t+4>>2]=+g[t+4>>2]+ +g[a+((c[r>>2]|0)+(c[o>>2]|0)<<2)>>2]*+g[c[l>>2]>>2];g[a+((c[r>>2]|0)+(c[o>>2]|0)+1<<2)>>2]=-+g[t+4>>2];g[(c[m>>2]|0)+((c[r>>2]|0)+1<<2)>>2]=+g[t+4>>2];g[t+8>>2]=+g[t+8>>2]+ +g[a+((c[r>>2]|0)+(c[o>>2]|0)+1<<2)>>2]*+g[c[l>>2]>>2];g[t+8>>2]=+g[t+8>>2]+ +g[a+((c[r>>2]|0)+(c[o>>2]|0)<<2)>>2]*+g[(c[l>>2]|0)+4>>2];g[a+((c[r>>2]|0)+(c[o>>2]|0)+2<<2)>>2]=-+g[t+8>>2];g[(c[m>>2]|0)+((c[r>>2]|0)+2<<2)>>2]=+g[t+8>>2];g[t+12>>2]=+g[t+12>>2]+ +g[a+((c[r>>2]|0)+(c[o>>2]|0)+2<<2)>>2]*+g[c[l>>2]>>2];g[t+12>>2]=+g[t+12>>2]+ +g[a+((c[r>>2]|0)+(c[o>>2]|0)+1<<2)>>2]*+g[(c[l>>2]|0)+4>>2];g[t+12>>2]=+g[t+12>>2]+ +g[a+((c[r>>2]|0)+(c[o>>2]|0)<<2)>>2]*+g[(c[l>>2]|0)+8>>2];g[a+((c[r>>2]|0)+(c[o>>2]|0)+3<<2)>>2]=-+g[t+12>>2];g[(c[m>>2]|0)+((c[r>>2]|0)+3<<2)>>2]=+g[t+12>>2];c[r>>2]=(c[r>>2]|0)+4}while(1){if((c[r>>2]|0)>=(c[n>>2]|0))break;g[u>>2]=+g[(c[k>>2]|0)+(c[r>>2]<<2)>>2];c[s>>2]=0;while(1){if((c[s>>2]|0)>=(c[o>>2]|0))break;g[u>>2]=+g[u>>2]-+g[e+(c[s>>2]<<2)>>2]*+g[a+((c[r>>2]|0)+(c[s>>2]|0)<<2)>>2];c[s>>2]=(c[s>>2]|0)+1}g[a+((c[r>>2]|0)+(c[o>>2]|0)<<2)>>2]=+g[u>>2];g[(c[m>>2]|0)+(c[r>>2]<<2)>>2]=+g[u>>2];c[r>>2]=(c[r>>2]|0)+1}c[r>>2]=0;while(1){if((c[r>>2]|0)>=(c[o>>2]|0))break;g[(c[p>>2]|0)+(c[r>>2]<<2)>>2]=+g[(c[m>>2]|0)+((c[n>>2]|0)-(c[r>>2]|0)-1<<2)>>2];c[r>>2]=(c[r>>2]|0)+1}na(c[q>>2]|0);i=v;return}function Yc(a,b,d,e,f,h,j){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;var k=0.0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0;z=i;i=i+64|0;l=z+52|0;p=z+48|0;m=z+44|0;n=z+40|0;q=z+36|0;r=z+32|0;o=z+28|0;t=z+24|0;v=z+20|0;w=z+16|0;u=z+12|0;x=z+8|0;y=z+4|0;s=z;c[l>>2]=a;c[p>>2]=b;c[m>>2]=d;c[n>>2]=e;c[q>>2]=f;c[r>>2]=h;c[o>>2]=j;c[u>>2]=(c[r>>2]|0)-(c[q>>2]|0);a=c[r>>2]|0;c[s>>2]=ia()|0;b=i;i=i+((1*(a<<2)|0)+15&-16)|0;if(!(c[n>>2]|0))c[y>>2]=c[l>>2];else{c[v>>2]=0;while(1){if((c[v>>2]|0)>=(c[r>>2]|0))break;g[b+(c[v>>2]<<2)>>2]=+g[(c[l>>2]|0)+(c[v>>2]<<2)>>2];c[v>>2]=(c[v>>2]|0)+1}c[v>>2]=0;while(1){if((c[v>>2]|0)>=(c[n>>2]|0))break;g[b+(c[v>>2]<<2)>>2]=+g[(c[l>>2]|0)+(c[v>>2]<<2)>>2]*+g[(c[m>>2]|0)+(c[v>>2]<<2)>>2];g[b+((c[r>>2]|0)-(c[v>>2]|0)-1<<2)>>2]=+g[(c[l>>2]|0)+((c[r>>2]|0)-(c[v>>2]|0)-1<<2)>>2]*+g[(c[m>>2]|0)+(c[v>>2]<<2)>>2];c[v>>2]=(c[v>>2]|0)+1}c[y>>2]=b}c[x>>2]=0;Mc(c[y>>2]|0,c[y>>2]|0,c[p>>2]|0,c[u>>2]|0,(c[q>>2]|0)+1|0,c[o>>2]|0);c[w>>2]=0;while(1){if((c[w>>2]|0)>(c[q>>2]|0))break;c[v>>2]=(c[w>>2]|0)+(c[u>>2]|0);g[t>>2]=0.0;while(1){k=+g[t>>2];if((c[v>>2]|0)>=(c[r>>2]|0))break;g[t>>2]=k+ +g[(c[y>>2]|0)+(c[v>>2]<<2)>>2]*+g[(c[y>>2]|0)+((c[v>>2]|0)-(c[w>>2]|0)<<2)>>2];c[v>>2]=(c[v>>2]|0)+1}o=(c[p>>2]|0)+(c[w>>2]<<2)|0;g[o>>2]=+g[o>>2]+k;c[w>>2]=(c[w>>2]|0)+1}y=c[x>>2]|0;na(c[s>>2]|0);i=z;return y|0}function Zc(a,b,d,e,f,h,j,k,l,m,n,o,p,q,r,s,t){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;p=p|0;q=q|0;r=r|0;s=s|0;t=t|0;var u=0.0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,$=0,aa=0,ba=0;aa=i;i=i+224|0;z=aa+216|0;A=aa+212|0;H=aa+208|0;x=aa+204|0;I=aa+200|0;J=aa+196|0;K=aa+192|0;L=aa+188|0;M=aa+184|0;N=aa+180|0;B=aa+176|0;v=aa+172|0;ba=aa+168|0;C=aa+164|0;D=aa+160|0;w=aa+156|0;E=aa+152|0;S=aa+148|0;V=aa+144|0;R=aa+96|0;Z=aa+92|0;O=aa+88|0;T=aa+84|0;W=aa+80|0;F=aa+76|0;U=aa+72|0;Q=aa+24|0;$=aa+20|0;Y=aa+16|0;X=aa+12|0;y=aa+8|0;P=aa+4|0;G=aa;c[z>>2]=a;c[A>>2]=b;c[H>>2]=d;c[x>>2]=e;c[I>>2]=f;c[J>>2]=h;c[K>>2]=j;c[L>>2]=k;c[M>>2]=l;c[N>>2]=m;c[B>>2]=n;c[v>>2]=o;c[ba>>2]=p;c[C>>2]=q;c[D>>2]=r;c[w>>2]=s;c[E>>2]=t;c[O>>2]=0;if(!(c[ba>>2]|0))if(!(c[D>>2]|0)?+g[c[C>>2]>>2]>+(_(c[N>>2]<<1,(c[H>>2]|0)-(c[A>>2]|0)|0)|0):0)m=(c[v>>2]|0)>(_((c[H>>2]|0)-(c[A>>2]|0)|0,c[N>>2]|0)|0);else m=0;else m=1;c[S>>2]=m&1;c[T>>2]=~~(+((c[K>>2]|0)>>>0)*+g[c[C>>2]>>2]*+(c[w>>2]|0)/+(c[N>>2]<<9|0));g[W>>2]=+_c(c[I>>2]|0,c[J>>2]|0,c[A>>2]|0,c[x>>2]|0,c[(c[z>>2]|0)+8>>2]|0,c[N>>2]|0);c[Z>>2]=$c(c[M>>2]|0)|0;if(((c[Z>>2]|0)+3|0)>>>0>(c[K>>2]|0)>>>0){c[S>>2]=0;c[D>>2]=0}g[V>>2]=16.0;if(((c[H>>2]|0)-(c[A>>2]|0)|0)>10){if(+g[V>>2]<+(c[v>>2]|0)*.125)u=+g[V>>2];else u=+(c[v>>2]|0)*.125;g[V>>2]=u}if(c[E>>2]|0)g[V>>2]=3.0;q=R;f=c[M>>2]|0;e=q+48|0;do{c[q>>2]=c[f>>2];q=q+4|0;f=f+4|0}while((q|0)<(e|0));ba=_(c[N>>2]|0,c[(c[z>>2]|0)+8>>2]|0)|0;c[F>>2]=ia()|0;n=i;i=i+((1*(ba<<2)|0)+15&-16)|0;ba=(_(c[N>>2]|0,c[(c[z>>2]|0)+8>>2]|0)|0)<<2;j=i;i=i+((1*ba|0)+15&-16)|0;ba=(_(c[N>>2]|0,c[(c[z>>2]|0)+8>>2]|0)|0)<<2;pj(n|0,c[J>>2]|0,ba+0|0)|0;if((c[D>>2]|0)!=0|(c[S>>2]|0)!=0)c[O>>2]=ad(c[z>>2]|0,c[A>>2]|0,c[H>>2]|0,c[I>>2]|0,n,c[K>>2]|0,c[Z>>2]|0,26384+((c[B>>2]|0)*84|0)+42|0,j,c[M>>2]|0,c[N>>2]|0,c[B>>2]|0,1,+g[V>>2],c[E>>2]|0)|0;if(c[S>>2]|0){ba=(_(c[N>>2]|0,c[(c[z>>2]|0)+8>>2]|0)|0)<<2;pj(c[J>>2]|0,n|0,ba+0|0)|0;ba=(_(c[N>>2]|0,c[(c[z>>2]|0)+8>>2]|0)|0)<<2;pj(c[L>>2]|0,j|0,ba+0|0)|0}else{c[$>>2]=Xb(c[M>>2]|0)|0;q=Q;f=c[M>>2]|0;e=q+48|0;do{c[q>>2]=c[f>>2];q=q+4|0;f=f+4|0}while((q|0)<(e|0));c[Y>>2]=bd(R)|0;c[X>>2]=bd(Q)|0;q=cd(Q)|0;c[U>>2]=q+(c[Y>>2]|0);q=(c[X>>2]|0)-(c[Y>>2]|0)|0;c[y>>2]=q;c[y>>2]=(c[y>>2]|0)==0?1:q;q=c[y>>2]|0;c[G>>2]=ia()|0;m=i;i=i+((1*q|0)+15&-16)|0;pj(m|0,c[U>>2]|0,(c[X>>2]|0)-(c[Y>>2]|0)+0|0)|0;q=c[M>>2]|0;f=R;e=q+48|0;do{c[q>>2]=c[f>>2];q=q+4|0;f=f+4|0}while((q|0)<(e|0));c[P>>2]=ad(c[z>>2]|0,c[A>>2]|0,c[H>>2]|0,c[I>>2]|0,c[J>>2]|0,c[K>>2]|0,c[Z>>2]|0,26384+((c[B>>2]|0)*84|0)+((c[S>>2]|0)*42|0)|0,c[L>>2]|0,c[M>>2]|0,c[N>>2]|0,c[B>>2]|0,0,+g[V>>2],c[E>>2]|0)|0;do if(c[D>>2]|0){if((c[O>>2]|0)>=(c[P>>2]|0)){if((c[O>>2]|0)!=(c[P>>2]|0))break;ba=Xb(c[M>>2]|0)|0;if((ba+(c[T>>2]|0)|0)<=(c[$>>2]|0))break}q=c[M>>2]|0;f=Q;e=q+48|0;do{c[q>>2]=c[f>>2];q=q+4|0;f=f+4|0}while((q|0)<(e|0));pj(c[U>>2]|0,m|0,(c[X>>2]|0)-(c[Y>>2]|0)+0|0)|0;ba=(_(c[N>>2]|0,c[(c[z>>2]|0)+8>>2]|0)|0)<<2;pj(c[J>>2]|0,n|0,ba+0|0)|0;ba=(_(c[N>>2]|0,c[(c[z>>2]|0)+8>>2]|0)|0)<<2;pj(c[L>>2]|0,j|0,ba+0|0)|0;c[S>>2]=1}while(0);na(c[G>>2]|0)}if(c[S>>2]|0){g[c[C>>2]>>2]=+g[W>>2];ba=c[F>>2]|0;na(ba|0);i=aa;return}else{g[c[C>>2]>>2]=+g[17580+(c[B>>2]<<2)>>2]*+g[17580+(c[B>>2]<<2)>>2]*+g[c[C>>2]>>2]+ +g[W>>2];ba=c[F>>2]|0;na(ba|0);i=aa;return}}function _c(a,b,d,e,f,h){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;t=i;i=i+48|0;j=t+36|0;k=t+32|0;l=t+28|0;m=t+24|0;n=t+20|0;o=t+16|0;p=t+12|0;s=t+8|0;r=t+4|0;q=t;c[j>>2]=a;c[k>>2]=b;c[l>>2]=d;c[m>>2]=e;c[n>>2]=f;c[o>>2]=h;g[r>>2]=0.0;c[p>>2]=0;do{c[s>>2]=c[l>>2];while(1){if((c[s>>2]|0)>=(c[m>>2]|0))break;b=(c[s>>2]|0)+(_(c[p>>2]|0,c[n>>2]|0)|0)|0;d=(c[s>>2]|0)+(_(c[p>>2]|0,c[n>>2]|0)|0)|0;g[q>>2]=+g[(c[j>>2]|0)+(b<<2)>>2]-+g[(c[k>>2]|0)+(d<<2)>>2];g[r>>2]=+g[r>>2]+ +g[q>>2]*+g[q>>2];c[s>>2]=(c[s>>2]|0)+1}d=(c[p>>2]|0)+1|0;c[p>>2]=d}while((d|0)<(c[o>>2]|0));i=t;return +(200.0<+g[r>>2]?200.0:+g[r>>2])}function $c(a){a=a|0;var b=0,d=0;b=i;i=i+16|0;d=b;c[d>>2]=a;a=(c[(c[d>>2]|0)+20>>2]|0)-(32-(aa(c[(c[d>>2]|0)+28>>2]|0)|0))|0;i=b;return a|0}function ad(a,b,e,f,h,j,k,l,m,n,o,p,q,r,s){a=a|0;b=b|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;p=p|0;q=q|0;r=+r;s=s|0;var t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,$=0;$=i;i=i+128|0;v=$+124|0;w=$+120|0;A=$+116|0;B=$+112|0;C=$+108|0;D=$+104|0;E=$+100|0;F=$+96|0;G=$+92|0;H=$+88|0;x=$+84|0;t=$+80|0;u=$+76|0;y=$+72|0;z=$+68|0;R=$+64|0;L=$+60|0;I=$+56|0;U=$+48|0;O=$+44|0;J=$+40|0;K=$+36|0;W=$+32|0;X=$+28|0;V=$+24|0;Z=$+20|0;Q=$+16|0;Y=$+12|0;S=$+8|0;P=$+4|0;T=$;c[v>>2]=a;c[w>>2]=b;c[A>>2]=e;c[B>>2]=f;c[C>>2]=h;c[D>>2]=j;c[E>>2]=k;c[F>>2]=l;c[G>>2]=m;c[H>>2]=n;c[x>>2]=o;c[t>>2]=p;c[u>>2]=q;g[y>>2]=r;c[z>>2]=s;c[I>>2]=0;c[U>>2]=0;c[U+4>>2]=0;if(((c[E>>2]|0)+3|0)<=(c[D>>2]|0))pc(c[H>>2]|0,c[u>>2]|0,3);if(c[u>>2]|0){g[O>>2]=0.0;g[J>>2]=.149993896484375}else{g[J>>2]=+g[17564+(c[t>>2]<<2)>>2];g[O>>2]=+g[17580+(c[t>>2]<<2)>>2]}c[R>>2]=c[w>>2];while(1){if((c[R>>2]|0)>=(c[A>>2]|0))break;c[L>>2]=0;do{k=(c[R>>2]|0)+(_(c[L>>2]|0,c[(c[v>>2]|0)+8>>2]|0)|0)|0;g[Z>>2]=+g[(c[B>>2]|0)+(k<<2)>>2];k=(c[R>>2]|0)+(_(c[L>>2]|0,c[(c[v>>2]|0)+8>>2]|0)|0)|0;if(-9.0>+g[(c[C>>2]|0)+(k<<2)>>2])r=-9.0;else{k=(c[R>>2]|0)+(_(c[L>>2]|0,c[(c[v>>2]|0)+8>>2]|0)|0)|0;r=+g[(c[C>>2]|0)+(k<<2)>>2]}g[S>>2]=r;g[Q>>2]=+g[Z>>2]-+g[O>>2]*+g[S>>2]-+g[U+(c[L>>2]<<2)>>2];c[W>>2]=~~+M(+(+g[Q>>2]+.5));k=(c[R>>2]|0)+(_(c[L>>2]|0,c[(c[v>>2]|0)+8>>2]|0)|0)|0;if(-28.0>+g[(c[C>>2]|0)+(k<<2)>>2])r=-28.0;else{k=(c[R>>2]|0)+(_(c[L>>2]|0,c[(c[v>>2]|0)+8>>2]|0)|0)|0;r=+g[(c[C>>2]|0)+(k<<2)>>2]}g[P>>2]=r-+g[y>>2];if((c[W>>2]|0)<0?+g[Z>>2]<+g[P>>2]:0){k=(c[W>>2]|0)+~~(+g[P>>2]-+g[Z>>2])|0;c[W>>2]=k;c[W>>2]=(c[W>>2]|0)>0?0:k}c[X>>2]=c[W>>2];c[E>>2]=$c(c[H>>2]|0)|0;c[K>>2]=(c[D>>2]|0)-(c[E>>2]|0)-(_((c[x>>2]|0)*3|0,(c[A>>2]|0)-(c[R>>2]|0)|0)|0);if((c[K>>2]|0)<30?(c[R>>2]|0)!=(c[w>>2]|0):0){if((c[K>>2]|0)<24)c[W>>2]=1<(c[W>>2]|0)?1:c[W>>2]|0;if((c[K>>2]|0)<16)c[W>>2]=-1>(c[W>>2]|0)?-1:c[W>>2]|0}if((c[z>>2]|0)!=0&(c[R>>2]|0)>=2)c[W>>2]=(c[W>>2]|0)<0?c[W>>2]|0:0;do if(((c[D>>2]|0)-(c[E>>2]|0)|0)<15)if(((c[D>>2]|0)-(c[E>>2]|0)|0)<2)if(((c[D>>2]|0)-(c[E>>2]|0)|0)>=1){c[W>>2]=0<(c[W>>2]|0)?0:c[W>>2]|0;pc(c[H>>2]|0,0-(c[W>>2]|0)|0,1);break}else{c[W>>2]=-1;break}else{if(-1>(((c[W>>2]|0)<1?c[W>>2]|0:1)|0))t=-1;else t=(c[W>>2]|0)<1?c[W>>2]|0:1;c[W>>2]=t;qc(c[H>>2]|0,c[W>>2]<<1^0-((c[W>>2]|0)<0&1),26720,2);break}else{c[T>>2]=((c[R>>2]|0)<20?c[R>>2]|0:20)<<1;Dc(c[H>>2]|0,W,(d[(c[F>>2]|0)+(c[T>>2]|0)>>0]|0)<<7,(d[(c[F>>2]|0)+((c[T>>2]|0)+1)>>0]|0)<<6)}while(0);k=(c[R>>2]|0)+(_(c[L>>2]|0,c[(c[v>>2]|0)+8>>2]|0)|0)|0;g[(c[G>>2]|0)+(k<<2)>>2]=+g[Q>>2]-+(c[W>>2]|0);k=N((c[X>>2]|0)-(c[W>>2]|0)|0)|0;c[I>>2]=(c[I>>2]|0)+k;g[V>>2]=+(c[W>>2]|0);g[Y>>2]=+g[O>>2]*+g[S>>2]+ +g[U+(c[L>>2]<<2)>>2]+ +g[V>>2];k=(c[R>>2]|0)+(_(c[L>>2]|0,c[(c[v>>2]|0)+8>>2]|0)|0)|0;g[(c[C>>2]|0)+(k<<2)>>2]=+g[Y>>2];g[U+(c[L>>2]<<2)>>2]=+g[U+(c[L>>2]<<2)>>2]+ +g[V>>2]-+g[J>>2]*+g[V>>2];k=(c[L>>2]|0)+1|0;c[L>>2]=k}while((k|0)<(c[x>>2]|0));c[R>>2]=(c[R>>2]|0)+1}i=$;return (c[z>>2]|0?0:c[I>>2]|0)|0}function bd(a){a=a|0;var b=0,d=0;d=i;i=i+16|0;b=d;c[b>>2]=a;i=d;return c[(c[b>>2]|0)+24>>2]|0}function cd(a){a=a|0;var b=0,d=0;d=i;i=i+16|0;b=d;c[b>>2]=a;i=d;return c[c[b>>2]>>2]|0}function dd(a,d,e,f,h,j,k,l){a=a|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;var m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0;y=i;i=i+64|0;m=y+44|0;z=y+40|0;n=y+36|0;o=y+32|0;p=y+28|0;q=y+24|0;r=y+20|0;s=y+16|0;v=y+12|0;t=y+8|0;u=y+48|0;x=y+4|0;w=y;c[m>>2]=a;c[z>>2]=d;c[n>>2]=e;c[o>>2]=f;c[p>>2]=h;c[q>>2]=j;c[r>>2]=k;c[s>>2]=l;c[v>>2]=c[z>>2];while(1){if((c[v>>2]|0)>=(c[n>>2]|0))break;b[u>>1]=1<<c[(c[q>>2]|0)+(c[v>>2]<<2)>>2];if((c[(c[q>>2]|0)+(c[v>>2]<<2)>>2]|0)>0){c[t>>2]=0;do{z=(c[v>>2]|0)+(_(c[t>>2]|0,c[(c[m>>2]|0)+8>>2]|0)|0)|0;c[x>>2]=~~+M(+((+g[(c[p>>2]|0)+(z<<2)>>2]+.5)*+(b[u>>1]|0)));if((c[x>>2]|0)>((b[u>>1]|0)-1|0))c[x>>2]=(b[u>>1]|0)-1;if((c[x>>2]|0)<0)c[x>>2]=0;sc(c[r>>2]|0,c[x>>2]|0,c[(c[q>>2]|0)+(c[v>>2]<<2)>>2]|0);g[w>>2]=(+(c[x>>2]|0)+.5)*+(1<<14-(c[(c[q>>2]|0)+(c[v>>2]<<2)>>2]|0)|0)*.00006103515625-.5;z=(c[v>>2]|0)+(_(c[t>>2]|0,c[(c[m>>2]|0)+8>>2]|0)|0)|0;z=(c[o>>2]|0)+(z<<2)|0;g[z>>2]=+g[z>>2]+ +g[w>>2];z=(c[v>>2]|0)+(_(c[t>>2]|0,c[(c[m>>2]|0)+8>>2]|0)|0)|0;z=(c[p>>2]|0)+(z<<2)|0;g[z>>2]=+g[z>>2]-+g[w>>2];z=(c[t>>2]|0)+1|0;c[t>>2]=z}while((z|0)<(c[s>>2]|0))}c[v>>2]=(c[v>>2]|0)+1}i=y;return}function ed(a,b,d,e,f,h,j,k,l,m){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;var n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0;C=i;i=i+64|0;n=C+56|0;o=C+52|0;p=C+48|0;q=C+44|0;r=C+40|0;s=C+36|0;t=C+32|0;u=C+28|0;v=C+24|0;w=C+20|0;y=C+16|0;A=C+12|0;x=C+8|0;B=C+4|0;z=C;c[n>>2]=a;c[o>>2]=b;c[p>>2]=d;c[q>>2]=e;c[r>>2]=f;c[s>>2]=h;c[t>>2]=j;c[u>>2]=k;c[v>>2]=l;c[w>>2]=m;c[A>>2]=0;while(1){if((c[A>>2]|0)>=2)break;c[y>>2]=c[o>>2];while(1){if((c[y>>2]|0)>=(c[p>>2]|0))break;if((c[u>>2]|0)<(c[w>>2]|0))break;if((c[(c[s>>2]|0)+(c[y>>2]<<2)>>2]|0)<8?(c[(c[t>>2]|0)+(c[y>>2]<<2)>>2]|0)==(c[A>>2]|0):0){c[x>>2]=0;do{b=(c[y>>2]|0)+(_(c[x>>2]|0,c[(c[n>>2]|0)+8>>2]|0)|0)|0;c[B>>2]=+g[(c[r>>2]|0)+(b<<2)>>2]<0.0?0:1;sc(c[v>>2]|0,c[B>>2]|0,1);g[z>>2]=(+(c[B>>2]|0)-.5)*+(1<<14-(c[(c[s>>2]|0)+(c[y>>2]<<2)>>2]|0)-1|0)*.00006103515625;b=(c[y>>2]|0)+(_(c[x>>2]|0,c[(c[n>>2]|0)+8>>2]|0)|0)|0;b=(c[q>>2]|0)+(b<<2)|0;g[b>>2]=+g[b>>2]+ +g[z>>2];c[u>>2]=(c[u>>2]|0)+-1;b=(c[x>>2]|0)+1|0;c[x>>2]=b}while((b|0)<(c[w>>2]|0))}c[y>>2]=(c[y>>2]|0)+1}c[A>>2]=(c[A>>2]|0)+1}i=C;return}function fd(a,b,e,f,h,j,k,l){a=a|0;b=b|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;var m=0.0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0;G=i;i=i+96|0;p=G+80|0;n=G+76|0;q=G+72|0;r=G+68|0;H=G+64|0;s=G+60|0;t=G+56|0;o=G+52|0;B=G+48|0;y=G+44|0;w=G+40|0;A=G+32|0;x=G+28|0;u=G+24|0;v=G+20|0;E=G+16|0;D=G+12|0;C=G+8|0;F=G+4|0;z=G;c[p>>2]=a;c[n>>2]=b;c[q>>2]=e;c[r>>2]=f;c[H>>2]=h;c[s>>2]=j;c[t>>2]=k;c[o>>2]=l;c[B>>2]=26384+((c[o>>2]|0)*84|0)+((c[H>>2]|0)*42|0);c[A>>2]=0;c[A+4>>2]=0;if(c[H>>2]|0){g[x>>2]=0.0;g[u>>2]=.149993896484375}else{g[u>>2]=+g[17564+(c[o>>2]<<2)>>2];g[x>>2]=+g[17580+(c[o>>2]<<2)>>2]}c[v>>2]=c[(c[s>>2]|0)+4>>2]<<3;c[y>>2]=c[n>>2];while(1){if((c[y>>2]|0)>=(c[q>>2]|0))break;c[w>>2]=0;do{c[E>>2]=$c(c[s>>2]|0)|0;do if(((c[v>>2]|0)-(c[E>>2]|0)|0)<15){if(((c[v>>2]|0)-(c[E>>2]|0)|0)>=2){c[D>>2]=ec(c[s>>2]|0,26720,2)|0;c[D>>2]=c[D>>2]>>1^0-(c[D>>2]&1);break}if(((c[v>>2]|0)-(c[E>>2]|0)|0)>=1){c[D>>2]=0-(dc(c[s>>2]|0,1)|0);break}else{c[D>>2]=-1;break}}else{c[z>>2]=((c[y>>2]|0)<20?c[y>>2]|0:20)<<1;c[D>>2]=Fc(c[s>>2]|0,(d[(c[B>>2]|0)+(c[z>>2]|0)>>0]|0)<<7,(d[(c[B>>2]|0)+((c[z>>2]|0)+1)>>0]|0)<<6)|0}while(0);g[C>>2]=+(c[D>>2]|0);H=(c[y>>2]|0)+(_(c[w>>2]|0,c[(c[p>>2]|0)+8>>2]|0)|0)|0;if(-9.0>+g[(c[r>>2]|0)+(H<<2)>>2])m=-9.0;else{H=(c[y>>2]|0)+(_(c[w>>2]|0,c[(c[p>>2]|0)+8>>2]|0)|0)|0;m=+g[(c[r>>2]|0)+(H<<2)>>2]}H=(c[y>>2]|0)+(_(c[w>>2]|0,c[(c[p>>2]|0)+8>>2]|0)|0)|0;g[(c[r>>2]|0)+(H<<2)>>2]=m;H=(c[y>>2]|0)+(_(c[w>>2]|0,c[(c[p>>2]|0)+8>>2]|0)|0)|0;g[F>>2]=+g[x>>2]*+g[(c[r>>2]|0)+(H<<2)>>2]+ +g[A+(c[w>>2]<<2)>>2]+ +g[C>>2];H=(c[y>>2]|0)+(_(c[w>>2]|0,c[(c[p>>2]|0)+8>>2]|0)|0)|0;g[(c[r>>2]|0)+(H<<2)>>2]=+g[F>>2];g[A+(c[w>>2]<<2)>>2]=+g[A+(c[w>>2]<<2)>>2]+ +g[C>>2]-+g[u>>2]*+g[C>>2];H=(c[w>>2]|0)+1|0;c[w>>2]=H}while((H|0)<(c[t>>2]|0));c[y>>2]=(c[y>>2]|0)+1}i=G;return}function gd(a,b,d,e,f,h,j){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;u=i;i=i+48|0;k=u+40|0;v=u+36|0;l=u+32|0;m=u+28|0;n=u+24|0;o=u+20|0;p=u+16|0;r=u+12|0;q=u+8|0;t=u+4|0;s=u;c[k>>2]=a;c[v>>2]=b;c[l>>2]=d;c[m>>2]=e;c[n>>2]=f;c[o>>2]=h;c[p>>2]=j;c[r>>2]=c[v>>2];while(1){if((c[r>>2]|0)>=(c[l>>2]|0))break;if((c[(c[n>>2]|0)+(c[r>>2]<<2)>>2]|0)>0){c[q>>2]=0;do{c[t>>2]=gc(c[o>>2]|0,c[(c[n>>2]|0)+(c[r>>2]<<2)>>2]|0)|0;g[s>>2]=(+(c[t>>2]|0)+.5)*+(1<<14-(c[(c[n>>2]|0)+(c[r>>2]<<2)>>2]|0)|0)*.00006103515625-.5;v=(c[r>>2]|0)+(_(c[q>>2]|0,c[(c[k>>2]|0)+8>>2]|0)|0)|0;v=(c[m>>2]|0)+(v<<2)|0;g[v>>2]=+g[v>>2]+ +g[s>>2];v=(c[q>>2]|0)+1|0;c[q>>2]=v}while((v|0)<(c[p>>2]|0))}c[r>>2]=(c[r>>2]|0)+1}i=u;return}function hd(a,b,d,e,f,h,j,k,l){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;var m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0;A=i;i=i+64|0;m=A+52|0;n=A+48|0;o=A+44|0;p=A+40|0;q=A+36|0;r=A+32|0;s=A+28|0;t=A+24|0;u=A+20|0;w=A+16|0;y=A+12|0;v=A+8|0;z=A+4|0;x=A;c[m>>2]=a;c[n>>2]=b;c[o>>2]=d;c[p>>2]=e;c[q>>2]=f;c[r>>2]=h;c[s>>2]=j;c[t>>2]=k;c[u>>2]=l;c[y>>2]=0;while(1){if((c[y>>2]|0)>=2)break;c[w>>2]=c[n>>2];while(1){if((c[w>>2]|0)>=(c[o>>2]|0))break;if((c[s>>2]|0)<(c[u>>2]|0))break;if((c[(c[q>>2]|0)+(c[w>>2]<<2)>>2]|0)<8?(c[(c[r>>2]|0)+(c[w>>2]<<2)>>2]|0)==(c[y>>2]|0):0){c[v>>2]=0;do{c[z>>2]=gc(c[t>>2]|0,1)|0;g[x>>2]=(+(c[z>>2]|0)-.5)*+(1<<14-(c[(c[q>>2]|0)+(c[w>>2]<<2)>>2]|0)-1|0)*.00006103515625;b=(c[w>>2]|0)+(_(c[v>>2]|0,c[(c[m>>2]|0)+8>>2]|0)|0)|0;b=(c[p>>2]|0)+(b<<2)|0;g[b>>2]=+g[b>>2]+ +g[x>>2];c[s>>2]=(c[s>>2]|0)+-1;b=(c[v>>2]|0)+1|0;c[v>>2]=b}while((b|0)<(c[u>>2]|0))}c[w>>2]=(c[w>>2]|0)+1}c[y>>2]=(c[y>>2]|0)+1}i=A;return}function id(a,b,d,e,f,h){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0.0;r=i;i=i+32|0;j=r+28|0;k=r+24|0;l=r+20|0;m=r+16|0;n=r+12|0;o=r+8|0;p=r+4|0;q=r;c[j>>2]=a;c[k>>2]=b;c[l>>2]=d;c[m>>2]=e;c[n>>2]=f;c[o>>2]=h;c[p>>2]=0;do{c[q>>2]=0;while(1){if((c[q>>2]|0)>=(c[k>>2]|0))break;a=(c[q>>2]|0)+(_(c[p>>2]|0,c[(c[j>>2]|0)+8>>2]|0)|0)|0;s=+Y(+(+g[(c[m>>2]|0)+(a<<2)>>2]))*1.4426950408889634;a=(c[q>>2]|0)+(_(c[p>>2]|0,c[(c[j>>2]|0)+8>>2]|0)|0)|0;g[(c[n>>2]|0)+(a<<2)>>2]=s-+g[17464+(c[q>>2]<<2)>>2];c[q>>2]=(c[q>>2]|0)+1}c[q>>2]=c[k>>2];while(1){h=c[p>>2]|0;if((c[q>>2]|0)>=(c[l>>2]|0))break;a=_(h,c[(c[j>>2]|0)+8>>2]|0)|0;g[(c[n>>2]|0)+(a+(c[q>>2]|0)<<2)>>2]=-14.0;c[q>>2]=(c[q>>2]|0)+1}a=h+1|0;c[p>>2]=a}while((a|0)<(c[o>>2]|0));i=r;return}function jd(a,e,f,g,h,j,k,l,m,n,o,p,q,r,s,t,u,v,w){a=a|0;e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;p=p|0;q=q|0;r=r|0;s=s|0;t=t|0;u=u|0;v=v|0;w=w|0;var x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0,ha=0;ha=i;i=i+160|0;D=ha+144|0;E=ha+140|0;P=ha+136|0;Q=ha+132|0;R=ha+128|0;x=ha+124|0;S=ha+120|0;T=ha+116|0;U=ha+112|0;V=ha+108|0;F=ha+104|0;G=ha+100|0;H=ha+96|0;I=ha+92|0;J=ha+88|0;K=ha+84|0;L=ha+80|0;M=ha+76|0;N=ha+72|0;ea=ha+68|0;aa=ha+64|0;da=ha+60|0;ca=ha+56|0;Z=ha+52|0;ga=ha+48|0;fa=ha+44|0;ba=ha+40|0;$=ha+36|0;O=ha+32|0;A=ha+28|0;C=ha+24|0;B=ha+20|0;z=ha+16|0;y=ha+12|0;X=ha+8|0;Y=ha+4|0;W=ha;c[D>>2]=a;c[E>>2]=e;c[P>>2]=f;c[Q>>2]=g;c[R>>2]=h;c[x>>2]=j;c[S>>2]=k;c[T>>2]=l;c[U>>2]=m;c[V>>2]=n;c[F>>2]=o;c[G>>2]=p;c[H>>2]=q;c[I>>2]=r;c[J>>2]=s;c[K>>2]=t;c[L>>2]=u;c[M>>2]=v;c[N>>2]=w;c[U>>2]=(c[U>>2]|0)>0?c[U>>2]|0:0;c[da>>2]=c[(c[D>>2]|0)+8>>2];c[ga>>2]=c[E>>2];c[fa>>2]=(c[U>>2]|0)>=8?8:0;c[U>>2]=(c[U>>2]|0)-(c[fa>>2]|0);c[$>>2]=0;c[ba>>2]=0;do if((c[I>>2]|0)==2){c[ba>>2]=d[26723+((c[P>>2]|0)-(c[E>>2]|0))>>0];if((c[ba>>2]|0)>(c[U>>2]|0)){c[ba>>2]=0;break}else{c[U>>2]=(c[U>>2]|0)-(c[ba>>2]|0);c[$>>2]=(c[U>>2]|0)>=8?8:0;c[U>>2]=(c[U>>2]|0)-(c[$>>2]|0);break}}while(0);j=c[da>>2]|0;c[O>>2]=ia()|0;s=i;i=i+((1*(j<<2)|0)+15&-16)|0;j=i;i=i+((1*(c[da>>2]<<2)|0)+15&-16)|0;n=i;i=i+((1*(c[da>>2]<<2)|0)+15&-16)|0;h=i;i=i+((1*(c[da>>2]<<2)|0)+15&-16)|0;c[ca>>2]=c[E>>2];while(1){if((c[ca>>2]|0)>=(c[P>>2]|0))break;if((c[I>>2]<<3|0)>(((b[(c[(c[D>>2]|0)+32>>2]|0)+((c[ca>>2]|0)+1<<1)>>1]|0)-(b[(c[(c[D>>2]|0)+32>>2]|0)+(c[ca>>2]<<1)>>1]|0)|0)*3<<c[J>>2]<<3>>4|0))r=c[I>>2]<<3;else r=((b[(c[(c[D>>2]|0)+32>>2]|0)+((c[ca>>2]|0)+1<<1)>>1]|0)-(b[(c[(c[D>>2]|0)+32>>2]|0)+(c[ca>>2]<<1)>>1]|0)|0)*3<<c[J>>2]<<3>>4;c[n+(c[ca>>2]<<2)>>2]=r;m=_(c[I>>2]|0,(b[(c[(c[D>>2]|0)+32>>2]|0)+((c[ca>>2]|0)+1<<1)>>1]|0)-(b[(c[(c[D>>2]|0)+32>>2]|0)+(c[ca>>2]<<1)>>1]|0)|0)|0;m=_(m,(c[x>>2]|0)-5-(c[J>>2]|0)|0)|0;m=_(m,(c[P>>2]|0)-(c[ca>>2]|0)-1|0)|0;m=(_(m,1<<(c[J>>2]|0)+3)|0)>>6;c[h+(c[ca>>2]<<2)>>2]=m;if(((b[(c[(c[D>>2]|0)+32>>2]|0)+((c[ca>>2]|0)+1<<1)>>1]|0)-(b[(c[(c[D>>2]|0)+32>>2]|0)+(c[ca>>2]<<1)>>1]|0)<<c[J>>2]|0)==1){m=h+(c[ca>>2]<<2)|0;c[m>>2]=(c[m>>2]|0)-(c[I>>2]<<3)}c[ca>>2]=(c[ca>>2]|0)+1}c[ea>>2]=1;c[aa>>2]=(c[(c[D>>2]|0)+48>>2]|0)-1;do{c[A>>2]=0;c[C>>2]=0;c[B>>2]=(c[ea>>2]|0)+(c[aa>>2]|0)>>1;c[ca>>2]=c[P>>2];while(1){x=c[ca>>2]|0;c[ca>>2]=x+-1;if((x|0)<=(c[E>>2]|0))break;c[y>>2]=(b[(c[(c[D>>2]|0)+32>>2]|0)+((c[ca>>2]|0)+1<<1)>>1]|0)-(b[(c[(c[D>>2]|0)+32>>2]|0)+(c[ca>>2]<<1)>>1]|0);m=_(c[I>>2]|0,c[y>>2]|0)|0;x=_(c[B>>2]|0,c[da>>2]|0)|0;x=_(m,d[(c[(c[D>>2]|0)+52>>2]|0)+(x+(c[ca>>2]|0))>>0]|0)|0;c[z>>2]=x<<c[J>>2]>>2;if((c[z>>2]|0)>0){if(0>((c[z>>2]|0)+(c[h+(c[ca>>2]<<2)>>2]|0)|0))r=0;else r=(c[z>>2]|0)+(c[h+(c[ca>>2]<<2)>>2]|0)|0;c[z>>2]=r}c[z>>2]=(c[z>>2]|0)+(c[(c[Q>>2]|0)+(c[ca>>2]<<2)>>2]|0);if(!(c[A>>2]|0?1:(c[z>>2]|0)>=(c[n+(c[ca>>2]<<2)>>2]|0))){if((c[z>>2]|0)<(c[I>>2]<<3|0))continue;c[C>>2]=(c[C>>2]|0)+(c[I>>2]<<3);continue}c[A>>2]=1;if((c[z>>2]|0)<(c[(c[R>>2]|0)+(c[ca>>2]<<2)>>2]|0))r=c[z>>2]|0;else r=c[(c[R>>2]|0)+(c[ca>>2]<<2)>>2]|0;c[C>>2]=(c[C>>2]|0)+r}r=c[B>>2]|0;if((c[C>>2]|0)>(c[U>>2]|0))c[aa>>2]=r-1;else c[ea>>2]=r+1}while((c[ea>>2]|0)<=(c[aa>>2]|0));C=c[ea>>2]|0;c[ea>>2]=C+-1;c[aa>>2]=C;c[ca>>2]=c[E>>2];while(1){if((c[ca>>2]|0)>=(c[P>>2]|0))break;c[W>>2]=(b[(c[(c[D>>2]|0)+32>>2]|0)+((c[ca>>2]|0)+1<<1)>>1]|0)-(b[(c[(c[D>>2]|0)+32>>2]|0)+(c[ca>>2]<<1)>>1]|0);B=_(c[I>>2]|0,c[W>>2]|0)|0;C=_(c[ea>>2]|0,c[da>>2]|0)|0;C=_(B,d[(c[(c[D>>2]|0)+52>>2]|0)+(C+(c[ca>>2]|0))>>0]|0)|0;c[X>>2]=C<<c[J>>2]>>2;if((c[aa>>2]|0)>=(c[(c[D>>2]|0)+48>>2]|0))r=c[(c[R>>2]|0)+(c[ca>>2]<<2)>>2]|0;else{C=_(c[I>>2]|0,c[W>>2]|0)|0;r=_(c[aa>>2]|0,c[da>>2]|0)|0;r=_(C,d[(c[(c[D>>2]|0)+52>>2]|0)+(r+(c[ca>>2]|0))>>0]|0)|0;r=r<<c[J>>2]>>2}c[Y>>2]=r;if((c[X>>2]|0)>0){if(0>((c[X>>2]|0)+(c[h+(c[ca>>2]<<2)>>2]|0)|0))r=0;else r=(c[X>>2]|0)+(c[h+(c[ca>>2]<<2)>>2]|0)|0;c[X>>2]=r}if((c[Y>>2]|0)>0){if(0>((c[Y>>2]|0)+(c[h+(c[ca>>2]<<2)>>2]|0)|0))r=0;else r=(c[Y>>2]|0)+(c[h+(c[ca>>2]<<2)>>2]|0)|0;c[Y>>2]=r}if((c[ea>>2]|0)>0)c[X>>2]=(c[X>>2]|0)+(c[(c[Q>>2]|0)+(c[ca>>2]<<2)>>2]|0);c[Y>>2]=(c[Y>>2]|0)+(c[(c[Q>>2]|0)+(c[ca>>2]<<2)>>2]|0);if((c[(c[Q>>2]|0)+(c[ca>>2]<<2)>>2]|0)>0)c[ga>>2]=c[ca>>2];if(0>((c[Y>>2]|0)-(c[X>>2]|0)|0))r=0;else r=(c[Y>>2]|0)-(c[X>>2]|0)|0;c[Y>>2]=r;c[s+(c[ca>>2]<<2)>>2]=c[X>>2];c[j+(c[ca>>2]<<2)>>2]=c[Y>>2];c[ca>>2]=(c[ca>>2]|0)+1}c[Z>>2]=kd(c[D>>2]|0,c[E>>2]|0,c[P>>2]|0,c[ga>>2]|0,s,j,n,c[R>>2]|0,c[U>>2]|0,c[V>>2]|0,c[fa>>2]|0,c[S>>2]|0,c[ba>>2]|0,c[T>>2]|0,c[$>>2]|0,c[F>>2]|0,c[G>>2]|0,c[H>>2]|0,c[I>>2]|0,c[J>>2]|0,c[K>>2]|0,c[L>>2]|0,c[M>>2]|0,c[N>>2]|0)|0;ga=c[Z>>2]|0;na(c[O>>2]|0);i=ha;return ga|0}function kd(a,e,f,g,h,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B){a=a|0;e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;p=p|0;q=q|0;r=r|0;s=s|0;t=t|0;u=u|0;v=v|0;w=w|0;x=x|0;y=y|0;z=z|0;A=A|0;B=B|0;var C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0,ha=0,ia=0,ja=0,ka=0,la=0,ma=0,na=0,oa=0,pa=0,qa=0,ra=0,sa=0,ta=0,ua=0,va=0,wa=0,xa=0,ya=0,za=0,Aa=0,Ba=0,Ca=0,Da=0;Da=i;i=i+224|0;ea=Da+208|0;ba=Da+204|0;ma=Da+200|0;P=Da+196|0;G=Da+192|0;H=Da+188|0;Q=Da+184|0;na=Da+180|0;Z=Da+176|0;oa=Da+172|0;L=Da+168|0;fa=Da+164|0;M=Da+160|0;ga=Da+156|0;W=Da+152|0;ha=Da+148|0;ia=Da+144|0;ja=Da+140|0;ka=Da+136|0;la=Da+132|0;X=Da+128|0;Y=Da+124|0;N=Da+120|0;O=Da+116|0;aa=Da+112|0;J=Da+108|0;C=Da+104|0;D=Da+100|0;za=Da+96|0;Aa=Da+92|0;Ca=Da+88|0;ua=Da+84|0;R=Da+80|0;ca=Da+76|0;$=Da+72|0;I=Da+68|0;sa=Da+64|0;E=Da+60|0;F=Da+56|0;K=Da+52|0;T=Da+48|0;S=Da+44|0;U=Da+40|0;da=Da+36|0;qa=Da+32|0;pa=Da+28|0;va=Da+24|0;Ba=Da+20|0;ra=Da+16|0;wa=Da+12|0;ta=Da+8|0;ya=Da+4|0;xa=Da;c[ea>>2]=a;c[ba>>2]=e;c[ma>>2]=f;c[P>>2]=g;c[G>>2]=h;c[H>>2]=j;c[Q>>2]=k;c[na>>2]=l;c[Z>>2]=m;c[oa>>2]=n;c[L>>2]=o;c[fa>>2]=p;c[M>>2]=q;c[ga>>2]=r;c[W>>2]=s;c[ha>>2]=t;c[ia>>2]=u;c[ja>>2]=v;c[ka>>2]=w;c[la>>2]=x;c[X>>2]=y;c[Y>>2]=z;c[N>>2]=A;c[O>>2]=B;c[ua>>2]=-1;c[R>>2]=c[ka>>2]<<3;c[Ca>>2]=(c[ka>>2]|0)>1&1;c[Aa>>2]=c[la>>2]<<3;c[J>>2]=0;c[C>>2]=64;c[D>>2]=0;while(1){if((c[D>>2]|0)>=6)break;c[E>>2]=(c[J>>2]|0)+(c[C>>2]|0)>>1;c[aa>>2]=0;c[I>>2]=0;c[za>>2]=c[ma>>2];while(1){m=c[za>>2]|0;c[za>>2]=m+-1;if((m|0)<=(c[ba>>2]|0))break;c[F>>2]=(c[(c[G>>2]|0)+(c[za>>2]<<2)>>2]|0)+((_(c[E>>2]|0,c[(c[H>>2]|0)+(c[za>>2]<<2)>>2]|0)|0)>>6);if(!(c[I>>2]|0?1:(c[F>>2]|0)>=(c[(c[Q>>2]|0)+(c[za>>2]<<2)>>2]|0))){if((c[F>>2]|0)<(c[R>>2]|0))continue;c[aa>>2]=(c[aa>>2]|0)+(c[R>>2]|0);continue}c[I>>2]=1;if((c[F>>2]|0)<(c[(c[na>>2]|0)+(c[za>>2]<<2)>>2]|0))w=c[F>>2]|0;else w=c[(c[na>>2]|0)+(c[za>>2]<<2)>>2]|0;c[aa>>2]=(c[aa>>2]|0)+w}w=c[E>>2]|0;if((c[aa>>2]|0)>(c[Z>>2]|0))c[C>>2]=w;else c[J>>2]=w;c[D>>2]=(c[D>>2]|0)+1}c[aa>>2]=0;c[I>>2]=0;c[za>>2]=c[ma>>2];while(1){F=c[za>>2]|0;c[za>>2]=F+-1;if((F|0)<=(c[ba>>2]|0))break;c[K>>2]=(c[(c[G>>2]|0)+(c[za>>2]<<2)>>2]|0)+((_(c[J>>2]|0,c[(c[H>>2]|0)+(c[za>>2]<<2)>>2]|0)|0)>>6);do if(!(c[I>>2]|0?1:(c[K>>2]|0)>=(c[(c[Q>>2]|0)+(c[za>>2]<<2)>>2]|0)))if((c[K>>2]|0)>=(c[R>>2]|0)){c[K>>2]=c[R>>2];break}else{c[K>>2]=0;break}else c[I>>2]=1;while(0);if((c[K>>2]|0)<(c[(c[na>>2]|0)+(c[za>>2]<<2)>>2]|0))w=c[K>>2]|0;else w=c[(c[na>>2]|0)+(c[za>>2]<<2)>>2]|0;c[K>>2]=w;c[(c[ha>>2]|0)+(c[za>>2]<<2)>>2]=c[K>>2];c[aa>>2]=(c[aa>>2]|0)+(c[K>>2]|0)}c[ua>>2]=c[ma>>2];while(1){c[za>>2]=(c[ua>>2]|0)-1;if((c[za>>2]|0)<=(c[P>>2]|0)){V=29;break}c[ca>>2]=(c[Z>>2]|0)-(c[aa>>2]|0);c[$>>2]=ld(c[ca>>2]|0,(b[(c[(c[ea>>2]|0)+32>>2]|0)+(c[ua>>2]<<1)>>1]|0)-(b[(c[(c[ea>>2]|0)+32>>2]|0)+(c[ba>>2]<<1)>>1]|0)|0)|0;K=_((b[(c[(c[ea>>2]|0)+32>>2]|0)+(c[ua>>2]<<1)>>1]|0)-(b[(c[(c[ea>>2]|0)+32>>2]|0)+(c[ba>>2]<<1)>>1]|0)|0,c[$>>2]|0)|0;c[ca>>2]=(c[ca>>2]|0)-K;if(((c[ca>>2]|0)-((b[(c[(c[ea>>2]|0)+32>>2]|0)+(c[za>>2]<<1)>>1]|0)-(b[(c[(c[ea>>2]|0)+32>>2]|0)+(c[ba>>2]<<1)>>1]|0))|0)>0)w=(c[ca>>2]|0)-((b[(c[(c[ea>>2]|0)+32>>2]|0)+(c[za>>2]<<1)>>1]|0)-(b[(c[(c[ea>>2]|0)+32>>2]|0)+(c[ba>>2]<<1)>>1]|0))|0;else w=0;c[U>>2]=w;c[T>>2]=(b[(c[(c[ea>>2]|0)+32>>2]|0)+(c[ua>>2]<<1)>>1]|0)-(b[(c[(c[ea>>2]|0)+32>>2]|0)+(c[za>>2]<<1)>>1]|0);K=(c[(c[ha>>2]|0)+(c[za>>2]<<2)>>2]|0)+(_(c[$>>2]|0,c[T>>2]|0)|0)|0;c[S>>2]=K+(c[U>>2]|0);if((c[(c[Q>>2]|0)+(c[za>>2]<<2)>>2]|0)>((c[R>>2]|0)+8|0))w=c[(c[Q>>2]|0)+(c[za>>2]<<2)>>2]|0;else w=(c[R>>2]|0)+8|0;if((c[S>>2]|0)>=(w|0)){if(!(c[Y>>2]|0)){if(dc(c[X>>2]|0,1)|0)break}else{if((c[ua>>2]|0)<=((c[ba>>2]|0)+2|0)){V=40;break}K=_((c[za>>2]|0)<(c[N>>2]|0)?7:9,c[T>>2]|0)|0;if((c[S>>2]|0)>(K<<c[la>>2]<<3>>4|0)?(c[za>>2]|0)<=(c[O>>2]|0):0){V=40;break}pc(c[X>>2]|0,0,1)}c[aa>>2]=(c[aa>>2]|0)+8;c[S>>2]=(c[S>>2]|0)-8}c[aa>>2]=(c[aa>>2]|0)-((c[(c[ha>>2]|0)+(c[za>>2]<<2)>>2]|0)+(c[M>>2]|0));if((c[M>>2]|0)>0)c[M>>2]=d[26723+((c[za>>2]|0)-(c[ba>>2]|0))>>0];c[aa>>2]=(c[aa>>2]|0)+(c[M>>2]|0);if((c[S>>2]|0)>=(c[R>>2]|0)){c[aa>>2]=(c[aa>>2]|0)+(c[R>>2]|0);c[(c[ha>>2]|0)+(c[za>>2]<<2)>>2]=c[R>>2]}else c[(c[ha>>2]|0)+(c[za>>2]<<2)>>2]=0;c[ua>>2]=(c[ua>>2]|0)+-1}if((V|0)==29)c[Z>>2]=(c[Z>>2]|0)+(c[L>>2]|0);else if((V|0)==40)pc(c[X>>2]|0,1,1);do if((c[M>>2]|0)>0){if(!(c[Y>>2]|0)){V=c[ba>>2]|0;V=V+(fc(c[X>>2]|0,(c[ua>>2]|0)+1-(c[ba>>2]|0)|0)|0)|0;c[c[fa>>2]>>2]=V;break}if((c[c[fa>>2]>>2]|0)<(c[ua>>2]|0))w=c[c[fa>>2]>>2]|0;else w=c[ua>>2]|0;c[c[fa>>2]>>2]=w;rc(c[X>>2]|0,(c[c[fa>>2]>>2]|0)-(c[ba>>2]|0)|0,(c[ua>>2]|0)+1-(c[ba>>2]|0)|0)}else c[c[fa>>2]>>2]=0;while(0);if((c[c[fa>>2]>>2]|0)<=(c[ba>>2]|0)){c[Z>>2]=(c[Z>>2]|0)+(c[W>>2]|0);c[W>>2]=0}do if((c[W>>2]|0)>0){w=c[X>>2]|0;if(c[Y>>2]|0){pc(w,c[c[ga>>2]>>2]|0,1);break}else{Y=dc(w,1)|0;c[c[ga>>2]>>2]=Y;break}}else c[c[ga>>2]>>2]=0;while(0);c[ca>>2]=(c[Z>>2]|0)-(c[aa>>2]|0);c[$>>2]=ld(c[ca>>2]|0,(b[(c[(c[ea>>2]|0)+32>>2]|0)+(c[ua>>2]<<1)>>1]|0)-(b[(c[(c[ea>>2]|0)+32>>2]|0)+(c[ba>>2]<<1)>>1]|0)|0)|0;aa=_((b[(c[(c[ea>>2]|0)+32>>2]|0)+(c[ua>>2]<<1)>>1]|0)-(b[(c[(c[ea>>2]|0)+32>>2]|0)+(c[ba>>2]<<1)>>1]|0)|0,c[$>>2]|0)|0;c[ca>>2]=(c[ca>>2]|0)-aa;c[za>>2]=c[ba>>2];while(1){if((c[za>>2]|0)>=(c[ua>>2]|0))break;Z=_(c[$>>2]|0,(b[(c[(c[ea>>2]|0)+32>>2]|0)+((c[za>>2]|0)+1<<1)>>1]|0)-(b[(c[(c[ea>>2]|0)+32>>2]|0)+(c[za>>2]<<1)>>1]|0)|0)|0;aa=(c[ha>>2]|0)+(c[za>>2]<<2)|0;c[aa>>2]=(c[aa>>2]|0)+Z;c[za>>2]=(c[za>>2]|0)+1}c[za>>2]=c[ba>>2];while(1){if((c[za>>2]|0)>=(c[ua>>2]|0))break;if((c[ca>>2]|0)<((b[(c[(c[ea>>2]|0)+32>>2]|0)+((c[za>>2]|0)+1<<1)>>1]|0)-(b[(c[(c[ea>>2]|0)+32>>2]|0)+(c[za>>2]<<1)>>1]|0)|0))w=c[ca>>2]|0;else w=(b[(c[(c[ea>>2]|0)+32>>2]|0)+((c[za>>2]|0)+1<<1)>>1]|0)-(b[(c[(c[ea>>2]|0)+32>>2]|0)+(c[za>>2]<<1)>>1]|0)|0;c[da>>2]=w;aa=(c[ha>>2]|0)+(c[za>>2]<<2)|0;c[aa>>2]=(c[aa>>2]|0)+(c[da>>2]|0);c[ca>>2]=(c[ca>>2]|0)-(c[da>>2]|0);c[za>>2]=(c[za>>2]|0)+1}c[sa>>2]=0;c[za>>2]=c[ba>>2];while(1){if((c[za>>2]|0)>=(c[ua>>2]|0))break;c[qa>>2]=(b[(c[(c[ea>>2]|0)+32>>2]|0)+((c[za>>2]|0)+1<<1)>>1]|0)-(b[(c[(c[ea>>2]|0)+32>>2]|0)+(c[za>>2]<<1)>>1]|0);c[pa>>2]=c[qa>>2]<<c[la>>2];c[ta>>2]=(c[(c[ha>>2]|0)+(c[za>>2]<<2)>>2]|0)+(c[sa>>2]|0);w=c[ta>>2]|0;if((c[pa>>2]|0)>1){if((w-(c[(c[na>>2]|0)+(c[za>>2]<<2)>>2]|0)|0)>0)w=(c[ta>>2]|0)-(c[(c[na>>2]|0)+(c[za>>2]<<2)>>2]|0)|0;else w=0;c[wa>>2]=w;c[(c[ha>>2]|0)+(c[za>>2]<<2)>>2]=(c[ta>>2]|0)-(c[wa>>2]|0);w=_(c[ka>>2]|0,c[pa>>2]|0)|0;if((c[ka>>2]|0)==2&(c[pa>>2]|0)>2?!(c[c[ga>>2]>>2]|0):0)x=(c[za>>2]|0)<(c[c[fa>>2]>>2]|0);else x=0;c[va>>2]=w+(x?1:0);c[ra>>2]=_(c[va>>2]|0,(b[(c[(c[ea>>2]|0)+56>>2]|0)+(c[za>>2]<<1)>>1]|0)+(c[Aa>>2]|0)|0)|0;c[Ba>>2]=(c[ra>>2]>>1)-((c[va>>2]|0)*21|0);if((c[pa>>2]|0)==2)c[Ba>>2]=(c[Ba>>2]|0)+(c[va>>2]<<3>>2);if(((c[(c[ha>>2]|0)+(c[za>>2]<<2)>>2]|0)+(c[Ba>>2]|0)|0)>=(c[va>>2]<<1<<3|0)){if(((c[(c[ha>>2]|0)+(c[za>>2]<<2)>>2]|0)+(c[Ba>>2]|0)|0)<((c[va>>2]|0)*3<<3|0))c[Ba>>2]=(c[Ba>>2]|0)+(c[ra>>2]>>3)}else c[Ba>>2]=(c[Ba>>2]|0)+(c[ra>>2]>>2);if(0>((c[(c[ha>>2]|0)+(c[za>>2]<<2)>>2]|0)+(c[Ba>>2]|0)+(c[va>>2]<<2)|0))w=0;else w=(c[(c[ha>>2]|0)+(c[za>>2]<<2)>>2]|0)+(c[Ba>>2]|0)+(c[va>>2]<<2)|0;c[(c[ia>>2]|0)+(c[za>>2]<<2)>>2]=w;da=(ld(c[(c[ia>>2]|0)+(c[za>>2]<<2)>>2]|0,c[va>>2]|0)|0)>>>3;c[(c[ia>>2]|0)+(c[za>>2]<<2)>>2]=da;da=_(c[ka>>2]|0,c[(c[ia>>2]|0)+(c[za>>2]<<2)>>2]|0)|0;if((da|0)>(c[(c[ha>>2]|0)+(c[za>>2]<<2)>>2]>>3|0))c[(c[ia>>2]|0)+(c[za>>2]<<2)>>2]=c[(c[ha>>2]|0)+(c[za>>2]<<2)>>2]>>c[Ca>>2]>>3;if((c[(c[ia>>2]|0)+(c[za>>2]<<2)>>2]|0)<8)w=c[(c[ia>>2]|0)+(c[za>>2]<<2)>>2]|0;else w=8;c[(c[ia>>2]|0)+(c[za>>2]<<2)>>2]=w;ca=_(c[(c[ia>>2]|0)+(c[za>>2]<<2)>>2]|0,c[va>>2]<<3)|0;c[(c[ja>>2]|0)+(c[za>>2]<<2)>>2]=(ca|0)>=((c[(c[ha>>2]|0)+(c[za>>2]<<2)>>2]|0)+(c[Ba>>2]|0)|0)&1;ca=(_(c[ka>>2]|0,c[(c[ia>>2]|0)+(c[za>>2]<<2)>>2]|0)|0)<<3;da=(c[ha>>2]|0)+(c[za>>2]<<2)|0;c[da>>2]=(c[da>>2]|0)-ca}else{if(0>(w-(c[ka>>2]<<3)|0))w=0;else w=(c[ta>>2]|0)-(c[ka>>2]<<3)|0;c[wa>>2]=w;c[(c[ha>>2]|0)+(c[za>>2]<<2)>>2]=(c[ta>>2]|0)-(c[wa>>2]|0);c[(c[ia>>2]|0)+(c[za>>2]<<2)>>2]=0;c[(c[ja>>2]|0)+(c[za>>2]<<2)>>2]=1}if((c[wa>>2]|0)>0){if((c[wa>>2]>>(c[Ca>>2]|0)+3|0)<(8-(c[(c[ia>>2]|0)+(c[za>>2]<<2)>>2]|0)|0))w=c[wa>>2]>>(c[Ca>>2]|0)+3;else w=8-(c[(c[ia>>2]|0)+(c[za>>2]<<2)>>2]|0)|0;c[ya>>2]=w;da=(c[ia>>2]|0)+(c[za>>2]<<2)|0;c[da>>2]=(c[da>>2]|0)+(c[ya>>2]|0);c[xa>>2]=(_(c[ya>>2]|0,c[ka>>2]|0)|0)<<3;c[(c[ja>>2]|0)+(c[za>>2]<<2)>>2]=(c[xa>>2]|0)>=((c[wa>>2]|0)-(c[sa>>2]|0)|0)&1;c[wa>>2]=(c[wa>>2]|0)-(c[xa>>2]|0)}c[sa>>2]=c[wa>>2];c[za>>2]=(c[za>>2]|0)+1}c[c[oa>>2]>>2]=c[sa>>2];while(1){if((c[za>>2]|0)>=(c[ma>>2]|0))break;c[(c[ia>>2]|0)+(c[za>>2]<<2)>>2]=c[(c[ha>>2]|0)+(c[za>>2]<<2)>>2]>>c[Ca>>2]>>3;c[(c[ha>>2]|0)+(c[za>>2]<<2)>>2]=0;c[(c[ja>>2]|0)+(c[za>>2]<<2)>>2]=(c[(c[ia>>2]|0)+(c[za>>2]<<2)>>2]|0)<1&1;c[za>>2]=(c[za>>2]|0)+1}i=Da;return c[ua>>2]|0}function ld(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)>>>0)/((c[d>>2]|0)>>>0)|0|0}function md(a,b,d,e,f,h){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0;E=i;i=i+96|0;j=E+84|0;k=E+80|0;l=E+76|0;F=E+72|0;m=E+68|0;n=E+64|0;v=E+60|0;w=E+56|0;z=E+52|0;x=E+48|0;A=E+44|0;C=E+40|0;D=E+36|0;u=E+32|0;o=E+28|0;y=E+24|0;B=E+20|0;s=E+16|0;t=E+12|0;r=E+8|0;p=E+4|0;q=E;c[j>>2]=a;c[k>>2]=b;c[l>>2]=d;c[F>>2]=e;c[m>>2]=f;c[n>>2]=h;f=c[k>>2]|0;c[o>>2]=ia()|0;a=i;i=i+((1*(f<<2)|0)+15&-16)|0;f=i;i=i+((1*(c[k>>2]<<2)|0)+15&-16)|0;d=i;i=i+((1*(c[k>>2]<<2)|0)+15&-16)|0;nd(c[j>>2]|0,c[k>>2]|0,1,c[m>>2]|0,c[l>>2]|0,c[F>>2]|0);g[A>>2]=0.0;c[w>>2]=0;do{b=d+(c[w>>2]<<2)|0;if(+g[(c[j>>2]|0)+(c[w>>2]<<2)>>2]>0.0)g[b>>2]=1.0;else{g[b>>2]=-1.0;g[(c[j>>2]|0)+(c[w>>2]<<2)>>2]=-+g[(c[j>>2]|0)+(c[w>>2]<<2)>>2]}c[f+(c[w>>2]<<2)>>2]=0;g[a+(c[w>>2]<<2)>>2]=0.0;F=(c[w>>2]|0)+1|0;c[w>>2]=F}while((F|0)<(c[k>>2]|0));g[D>>2]=0.0;g[C>>2]=0.0;c[x>>2]=c[l>>2];if((c[l>>2]|0)>(c[k>>2]>>1|0)){c[w>>2]=0;do{g[A>>2]=+g[A>>2]+ +g[(c[j>>2]|0)+(c[w>>2]<<2)>>2];F=(c[w>>2]|0)+1|0;c[w>>2]=F}while((F|0)<(c[k>>2]|0));if(!(+g[A>>2]>1.0000000036274937e-15&+g[A>>2]<64.0)){g[c[j>>2]>>2]=1.0;c[w>>2]=1;do{g[(c[j>>2]|0)+(c[w>>2]<<2)>>2]=0.0;F=(c[w>>2]|0)+1|0;c[w>>2]=F}while((F|0)<(c[k>>2]|0));g[A>>2]=1.0}g[y>>2]=+((c[l>>2]|0)-1|0)*(1.0/+g[A>>2]);c[w>>2]=0;do{F=~~+M(+(+g[y>>2]*+g[(c[j>>2]|0)+(c[w>>2]<<2)>>2]));c[f+(c[w>>2]<<2)>>2]=F;g[a+(c[w>>2]<<2)>>2]=+(c[f+(c[w>>2]<<2)>>2]|0);g[D>>2]=+g[D>>2]+ +g[a+(c[w>>2]<<2)>>2]*+g[a+(c[w>>2]<<2)>>2];g[C>>2]=+g[C>>2]+ +g[(c[j>>2]|0)+(c[w>>2]<<2)>>2]*+g[a+(c[w>>2]<<2)>>2];F=a+(c[w>>2]<<2)|0;g[F>>2]=+g[F>>2]*2.0;c[x>>2]=(c[x>>2]|0)-(c[f+(c[w>>2]<<2)>>2]|0);F=(c[w>>2]|0)+1|0;c[w>>2]=F}while((F|0)<(c[k>>2]|0))}if((c[x>>2]|0)>((c[k>>2]|0)+3|0)){g[B>>2]=+(c[x>>2]|0);g[D>>2]=+g[D>>2]+ +g[B>>2]*+g[B>>2];g[D>>2]=+g[D>>2]+ +g[B>>2]*+g[a>>2];c[f>>2]=(c[f>>2]|0)+(c[x>>2]|0);c[x>>2]=0}g[z>>2]=1.0;c[v>>2]=0;while(1){if((c[v>>2]|0)>=(c[x>>2]|0))break;g[t>>2]=-999999986991104.0;g[r>>2]=0.0;c[s>>2]=0;g[D>>2]=+g[D>>2]+1.0;c[w>>2]=0;do{g[p>>2]=+g[C>>2]+ +g[(c[j>>2]|0)+(c[w>>2]<<2)>>2];g[q>>2]=+g[D>>2]+ +g[a+(c[w>>2]<<2)>>2];g[p>>2]=+g[p>>2]*+g[p>>2];if(+g[r>>2]*+g[p>>2]>+g[q>>2]*+g[t>>2]){g[r>>2]=+g[q>>2];g[t>>2]=+g[p>>2];c[s>>2]=c[w>>2]}F=(c[w>>2]|0)+1|0;c[w>>2]=F}while((F|0)<(c[k>>2]|0));g[C>>2]=+g[C>>2]+ +g[(c[j>>2]|0)+(c[s>>2]<<2)>>2];g[D>>2]=+g[D>>2]+ +g[a+(c[s>>2]<<2)>>2];F=a+(c[s>>2]<<2)|0;g[F>>2]=+g[F>>2]+ +g[z>>2]*2.0;F=f+(c[s>>2]<<2)|0;c[F>>2]=(c[F>>2]|0)+1;c[v>>2]=(c[v>>2]|0)+1}c[w>>2]=0;do{g[(c[j>>2]|0)+(c[w>>2]<<2)>>2]=+g[d+(c[w>>2]<<2)>>2]*+g[(c[j>>2]|0)+(c[w>>2]<<2)>>2];if(+g[d+(c[w>>2]<<2)>>2]<0.0)c[f+(c[w>>2]<<2)>>2]=0-(c[f+(c[w>>2]<<2)>>2]|0);F=(c[w>>2]|0)+1|0;c[w>>2]=F}while((F|0)<(c[k>>2]|0));Tb(f,c[k>>2]|0,c[l>>2]|0,c[n>>2]|0);c[u>>2]=qd(f,c[k>>2]|0,c[m>>2]|0)|0;F=c[u>>2]|0;na(c[o>>2]|0);i=E;return F|0}function nd(a,b,d,e,f,h){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0;w=i;i=i+64|0;j=w+48|0;k=w+44|0;l=w+40|0;m=w+36|0;n=w+32|0;o=w+28|0;s=w+24|0;p=w+20|0;t=w+16|0;r=w+12|0;v=w+8|0;u=w+4|0;q=w;c[j>>2]=a;c[k>>2]=b;c[l>>2]=d;c[m>>2]=e;c[n>>2]=f;c[o>>2]=h;c[u>>2]=0;if((c[o>>2]|0)==0?1:(c[n>>2]<<1|0)>=(c[k>>2]|0)){i=w;return}c[q>>2]=c[17596+((c[o>>2]|0)-1<<2)>>2];g[r>>2]=+(c[k>>2]|0)*1.0/+((c[k>>2]|0)+(_(c[q>>2]|0,c[n>>2]|0)|0)|0);g[v>>2]=+g[r>>2]*+g[r>>2]*.5;g[p>>2]=+Q(+(+g[v>>2]*1.5707963705062866));g[t>>2]=+Q(+((1.0-+g[v>>2])*1.5707963705062866));a:do if((c[k>>2]|0)>=(c[m>>2]<<3|0)){c[u>>2]=1;while(1){v=_(c[u>>2]|0,c[u>>2]|0)|0;v=_(v+(c[u>>2]|0)|0,c[m>>2]|0)|0;if((v+(c[m>>2]>>2)|0)>=(c[k>>2]|0))break a;c[u>>2]=(c[u>>2]|0)+1}}while(0);c[k>>2]=od(c[k>>2]|0,c[m>>2]|0)|0;c[s>>2]=0;while(1){if((c[s>>2]|0)>=(c[m>>2]|0))break;if((c[l>>2]|0)>=0){v=(c[j>>2]|0)+((_(c[s>>2]|0,c[k>>2]|0)|0)<<2)|0;pd(v,c[k>>2]|0,1,+g[p>>2],-+g[t>>2]);if(c[u>>2]|0){v=(c[j>>2]|0)+((_(c[s>>2]|0,c[k>>2]|0)|0)<<2)|0;pd(v,c[k>>2]|0,c[u>>2]|0,+g[t>>2],-+g[p>>2])}}else{if(c[u>>2]|0){v=(c[j>>2]|0)+((_(c[s>>2]|0,c[k>>2]|0)|0)<<2)|0;pd(v,c[k>>2]|0,c[u>>2]|0,+g[t>>2],+g[p>>2])}v=(c[j>>2]|0)+((_(c[s>>2]|0,c[k>>2]|0)|0)<<2)|0;pd(v,c[k>>2]|0,1,+g[p>>2],+g[t>>2])}c[s>>2]=(c[s>>2]|0)+1}i=w;return}function od(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)>>>0)/((c[d>>2]|0)>>>0)|0|0}function pd(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=+e;f=+f;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;u=i;i=i+48|0;h=u+44|0;j=u+40|0;k=u+36|0;l=u+32|0;m=u+28|0;o=u+24|0;p=u+20|0;n=u+16|0;q=u+12|0;s=u+8|0;r=u+4|0;t=u;c[h>>2]=a;c[j>>2]=b;c[k>>2]=d;g[l>>2]=e;g[m>>2]=f;c[n>>2]=c[h>>2];g[p>>2]=-+g[m>>2];c[o>>2]=0;while(1){if((c[o>>2]|0)>=((c[j>>2]|0)-(c[k>>2]|0)|0))break;g[q>>2]=+g[c[n>>2]>>2];g[s>>2]=+g[(c[n>>2]|0)+(c[k>>2]<<2)>>2];g[(c[n>>2]|0)+(c[k>>2]<<2)>>2]=+g[l>>2]*+g[s>>2]+ +g[m>>2]*+g[q>>2];f=+g[l>>2]*+g[q>>2]+ +g[p>>2]*+g[s>>2];d=c[n>>2]|0;c[n>>2]=d+4;g[d>>2]=f;c[o>>2]=(c[o>>2]|0)+1}c[n>>2]=(c[h>>2]|0)+((c[j>>2]|0)-(c[k>>2]<<1)-1<<2);c[o>>2]=(c[j>>2]|0)-(c[k>>2]<<1)-1;while(1){if((c[o>>2]|0)<0)break;g[r>>2]=+g[c[n>>2]>>2];g[t>>2]=+g[(c[n>>2]|0)+(c[k>>2]<<2)>>2];g[(c[n>>2]|0)+(c[k>>2]<<2)>>2]=+g[l>>2]*+g[t>>2]+ +g[m>>2]*+g[r>>2];f=+g[l>>2]*+g[r>>2]+ +g[p>>2]*+g[t>>2];s=c[n>>2]|0;c[n>>2]=s+-4;g[s>>2]=f;c[o>>2]=(c[o>>2]|0)+-1}i=u;return}function qd(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0;o=i;i=i+48|0;e=o+32|0;f=o+28|0;g=o+24|0;h=o+20|0;k=o+16|0;j=o+12|0;l=o+8|0;m=o+4|0;n=o;c[f>>2]=a;c[g>>2]=b;c[h>>2]=d;if((c[h>>2]|0)<=1){c[e>>2]=1;n=c[e>>2]|0;i=o;return n|0}c[j>>2]=od(c[g>>2]|0,c[h>>2]|0)|0;c[k>>2]=0;c[l>>2]=0;do{c[n>>2]=0;c[m>>2]=0;do{a=_(c[l>>2]|0,c[j>>2]|0)|0;c[n>>2]=c[n>>2]|c[(c[f>>2]|0)+(a+(c[m>>2]|0)<<2)>>2];a=(c[m>>2]|0)+1|0;c[m>>2]=a}while((a|0)<(c[j>>2]|0));c[k>>2]=c[k>>2]|((c[n>>2]|0)!=0&1)<<c[l>>2];a=(c[l>>2]|0)+1|0;c[l>>2]=a}while((a|0)<(c[h>>2]|0));c[e>>2]=c[k>>2];n=c[e>>2]|0;i=o;return n|0}function rd(a,b,d,e,f,h,j){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=+j;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;k=i;i=i+48|0;r=k+36|0;o=k+32|0;q=k+28|0;p=k+24|0;n=k+20|0;u=k+16|0;s=k+12|0;t=k+8|0;m=k+4|0;l=k;c[r>>2]=a;c[o>>2]=b;c[q>>2]=d;c[p>>2]=e;c[n>>2]=f;c[u>>2]=h;g[s>>2]=j;h=c[o>>2]|0;c[l>>2]=ia()|0;e=i;i=i+((1*(h<<2)|0)+15&-16)|0;g[t>>2]=+Vb(e,c[o>>2]|0,c[q>>2]|0,c[u>>2]|0);sd(e,c[r>>2]|0,c[o>>2]|0,+g[t>>2],+g[s>>2]);nd(c[r>>2]|0,c[o>>2]|0,-1,c[n>>2]|0,c[q>>2]|0,c[p>>2]|0);c[m>>2]=qd(e,c[o>>2]|0,c[n>>2]|0)|0;e=c[m>>2]|0;na(c[l>>2]|0);i=k;return e|0}function sd(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=+e;f=+f;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;n=i;i=i+32|0;h=n+28|0;j=n+24|0;k=n+20|0;q=n+16|0;o=n+12|0;m=n+8|0;p=n+4|0;l=n;c[h>>2]=a;c[j>>2]=b;c[k>>2]=d;g[q>>2]=e;g[o>>2]=f;g[p>>2]=+g[q>>2];f=1.0/+O(+(+g[p>>2]));g[l>>2]=f*+g[o>>2];c[m>>2]=0;do{g[(c[j>>2]|0)+(c[m>>2]<<2)>>2]=+g[l>>2]*+(c[(c[h>>2]|0)+(c[m>>2]<<2)>>2]|0);q=(c[m>>2]|0)+1|0;c[m>>2]=q}while((q|0)<(c[k>>2]|0));i=n;return}function td(a,b,d,e){a=a|0;b=b|0;d=+d;e=e|0;var f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;l=i;i=i+48|0;m=l+32|0;f=l+28|0;n=l+24|0;j=l+16|0;p=l+12|0;h=l+8|0;o=l+4|0;k=l;c[m>>2]=a;c[f>>2]=b;g[n>>2]=d;c[l+20>>2]=e;g[p>>2]=+ud(c[m>>2]|0,c[m>>2]|0,c[f>>2]|0)+1.0000000036274937e-15;g[o>>2]=+g[p>>2];d=1.0/+O(+(+g[o>>2]));g[h>>2]=d*+g[n>>2];c[k>>2]=c[m>>2];c[j>>2]=0;while(1){if((c[j>>2]|0)>=(c[f>>2]|0))break;g[c[k>>2]>>2]=+g[h>>2]*+g[c[k>>2]>>2];c[k>>2]=(c[k>>2]|0)+4;c[j>>2]=(c[j>>2]|0)+1}i=l;return}function ud(a,b,d){a=a|0;b=b|0;d=d|0;var e=0.0,f=0,h=0,j=0,k=0,l=0,m=0;m=i;i=i+32|0;f=m+16|0;h=m+12|0;j=m+8|0;k=m+4|0;l=m;c[f>>2]=a;c[h>>2]=b;c[j>>2]=d;g[l>>2]=0.0;c[k>>2]=0;while(1){e=+g[l>>2];if((c[k>>2]|0)>=(c[j>>2]|0))break;g[l>>2]=e+ +g[(c[f>>2]|0)+(c[k>>2]<<2)>>2]*+g[(c[h>>2]|0)+(c[k>>2]<<2)>>2];c[k>>2]=(c[k>>2]|0)+1}i=m;return +e}function vd(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0.0;t=i;i=i+64|0;h=t+48|0;j=t+44|0;u=t+40|0;k=t+36|0;n=t+28|0;o=t+24|0;q=t+20|0;s=t+16|0;l=t+12|0;m=t+8|0;p=t+4|0;r=t;c[h>>2]=a;c[j>>2]=b;c[u>>2]=d;c[k>>2]=e;c[t+32>>2]=f;g[m>>2]=1.0000000036274937e-15;g[l>>2]=1.0000000036274937e-15;a:do if(c[u>>2]|0){c[n>>2]=0;while(1){if((c[n>>2]|0)>=(c[k>>2]|0))break a;g[p>>2]=+g[(c[h>>2]|0)+(c[n>>2]<<2)>>2]+ +g[(c[j>>2]|0)+(c[n>>2]<<2)>>2];g[r>>2]=+g[(c[h>>2]|0)+(c[n>>2]<<2)>>2]-+g[(c[j>>2]|0)+(c[n>>2]<<2)>>2];g[l>>2]=+g[l>>2]+ +g[p>>2]*+g[p>>2];g[m>>2]=+g[m>>2]+ +g[r>>2]*+g[r>>2];c[n>>2]=(c[n>>2]|0)+1}}else{v=+ud(c[h>>2]|0,c[h>>2]|0,c[k>>2]|0);g[l>>2]=+g[l>>2]+v;v=+ud(c[j>>2]|0,c[j>>2]|0,c[k>>2]|0);g[m>>2]=+g[m>>2]+v}while(0);g[q>>2]=+O(+(+g[l>>2]));g[s>>2]=+O(+(+g[m>>2]));c[o>>2]=~~+M(+(+W(+(+g[s>>2]),+(+g[q>>2]))*10430.3818359375+.5));i=t;return c[o>>2]|0}function wd(a){a=a|0;var d=0,e=0,f=0,g=0,h=0;h=i;i=i+16|0;d=h+12|0;g=h+8|0;f=h+4|0;e=h;c[d>>2]=a;c[f>>2]=32767/((c[(c[d>>2]|0)+2340>>2]|0)+1|0)|0;c[e>>2]=0;c[g>>2]=0;while(1){if((c[g>>2]|0)>=(c[(c[d>>2]|0)+2340>>2]|0))break;c[e>>2]=(c[e>>2]|0)+(c[f>>2]|0);b[(c[d>>2]|0)+2772+1280+(c[g>>2]<<1)>>1]=c[e>>2];c[g>>2]=(c[g>>2]|0)+1}c[(c[d>>2]|0)+2772+1376>>2]=0;c[(c[d>>2]|0)+2772+1380>>2]=3176576;i=h;return}
+ function cg(a,d,e){a=a|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;t=i;i=i+48|0;f=t+40|0;g=t+36|0;h=t+32|0;m=t+28|0;j=t+24|0;n=t+20|0;o=t+16|0;k=t+44|0;l=t+12|0;r=t+8|0;q=t+4|0;p=t;c[f>>2]=a;c[g>>2]=d;c[h>>2]=e;c[j>>2]=0;c[o>>2]=0;while(1){if((c[o>>2]|0)>=20)break;c[r>>2]=(b[c[f>>2]>>1]|0)-(b[c[g>>2]>>1]|0);c[j>>2]=0;c[m>>2]=1;while(1){if((c[m>>2]|0)>((c[h>>2]|0)-1|0))break;c[l>>2]=(b[(c[f>>2]|0)+(c[m>>2]<<1)>>1]|0)-((b[(c[f>>2]|0)+((c[m>>2]|0)-1<<1)>>1]|0)+(b[(c[g>>2]|0)+(c[m>>2]<<1)>>1]|0));if((c[l>>2]|0)<(c[r>>2]|0)){c[r>>2]=c[l>>2];c[j>>2]=c[m>>2]}c[m>>2]=(c[m>>2]|0)+1}c[l>>2]=32768-((b[(c[f>>2]|0)+((c[h>>2]|0)-1<<1)>>1]|0)+(b[(c[g>>2]|0)+(c[h>>2]<<1)>>1]|0));if((c[l>>2]|0)<(c[r>>2]|0)){c[r>>2]=c[l>>2];c[j>>2]=c[h>>2]}if((c[r>>2]|0)>=0){s=41;break}do if(!(c[j>>2]|0))b[c[f>>2]>>1]=b[c[g>>2]>>1]|0;else{if((c[j>>2]|0)==(c[h>>2]|0)){b[(c[f>>2]|0)+((c[h>>2]|0)-1<<1)>>1]=32768-(b[(c[g>>2]|0)+(c[h>>2]<<1)>>1]|0);break}c[q>>2]=0;c[n>>2]=0;while(1){if((c[n>>2]|0)>=(c[j>>2]|0))break;c[q>>2]=(c[q>>2]|0)+(b[(c[g>>2]|0)+(c[n>>2]<<1)>>1]|0);c[n>>2]=(c[n>>2]|0)+1}c[q>>2]=(c[q>>2]|0)+(b[(c[g>>2]|0)+(c[j>>2]<<1)>>1]>>1);c[p>>2]=32768;c[n>>2]=c[h>>2];while(1){if((c[n>>2]|0)<=(c[j>>2]|0))break;c[p>>2]=(c[p>>2]|0)-(b[(c[g>>2]|0)+(c[n>>2]<<1)>>1]|0);c[n>>2]=(c[n>>2]|0)+-1}c[p>>2]=(c[p>>2]|0)-(b[(c[g>>2]|0)+(c[j>>2]<<1)>>1]>>1);e=((b[(c[f>>2]|0)+((c[j>>2]|0)-1<<1)>>1]|0)+(b[(c[f>>2]|0)+(c[j>>2]<<1)>>1]|0)>>1)+((b[(c[f>>2]|0)+((c[j>>2]|0)-1<<1)>>1]|0)+(b[(c[f>>2]|0)+(c[j>>2]<<1)>>1]|0)&1)|0;do if((c[q>>2]|0)>(c[p>>2]|0)){if((e|0)>(c[q>>2]|0)){e=c[q>>2]|0;break}if((((b[(c[f>>2]|0)+((c[j>>2]|0)-1<<1)>>1]|0)+(b[(c[f>>2]|0)+(c[j>>2]<<1)>>1]|0)>>1)+((b[(c[f>>2]|0)+((c[j>>2]|0)-1<<1)>>1]|0)+(b[(c[f>>2]|0)+(c[j>>2]<<1)>>1]|0)&1)|0)<(c[p>>2]|0)){e=c[p>>2]|0;break}else{e=((b[(c[f>>2]|0)+((c[j>>2]|0)-1<<1)>>1]|0)+(b[(c[f>>2]|0)+(c[j>>2]<<1)>>1]|0)>>1)+((b[(c[f>>2]|0)+((c[j>>2]|0)-1<<1)>>1]|0)+(b[(c[f>>2]|0)+(c[j>>2]<<1)>>1]|0)&1)|0;break}}else{if((e|0)>(c[p>>2]|0)){e=c[p>>2]|0;break}if((((b[(c[f>>2]|0)+((c[j>>2]|0)-1<<1)>>1]|0)+(b[(c[f>>2]|0)+(c[j>>2]<<1)>>1]|0)>>1)+((b[(c[f>>2]|0)+((c[j>>2]|0)-1<<1)>>1]|0)+(b[(c[f>>2]|0)+(c[j>>2]<<1)>>1]|0)&1)|0)<(c[q>>2]|0)){e=c[q>>2]|0;break}else{e=((b[(c[f>>2]|0)+((c[j>>2]|0)-1<<1)>>1]|0)+(b[(c[f>>2]|0)+(c[j>>2]<<1)>>1]|0)>>1)+((b[(c[f>>2]|0)+((c[j>>2]|0)-1<<1)>>1]|0)+(b[(c[f>>2]|0)+(c[j>>2]<<1)>>1]|0)&1)|0;break}}while(0);b[k>>1]=e;b[(c[f>>2]|0)+((c[j>>2]|0)-1<<1)>>1]=(b[k>>1]|0)-(b[(c[g>>2]|0)+(c[j>>2]<<1)>>1]>>1);b[(c[f>>2]|0)+(c[j>>2]<<1)>>1]=(b[(c[f>>2]|0)+((c[j>>2]|0)-1<<1)>>1]|0)+(b[(c[g>>2]|0)+(c[j>>2]<<1)>>1]|0)}while(0);c[o>>2]=(c[o>>2]|0)+1}if((s|0)==41){i=t;return}if((c[o>>2]|0)!=20){i=t;return}vg(c[f>>2]|0,c[h>>2]|0);s=(dg(b[c[f>>2]>>1]|0,b[c[g>>2]>>1]|0)|0)&65535;b[c[f>>2]>>1]=s;c[m>>2]=1;while(1){if((c[m>>2]|0)>=(c[h>>2]|0))break;s=(dg(b[(c[f>>2]|0)+(c[m>>2]<<1)>>1]|0,(b[(c[f>>2]|0)+((c[m>>2]|0)-1<<1)>>1]|0)+(b[(c[g>>2]|0)+(c[m>>2]<<1)>>1]|0)|0)|0)&65535;b[(c[f>>2]|0)+(c[m>>2]<<1)>>1]=s;c[m>>2]=(c[m>>2]|0)+1}s=(eg(b[(c[f>>2]|0)+((c[h>>2]|0)-1<<1)>>1]|0,32768-(b[(c[g>>2]|0)+(c[h>>2]<<1)>>1]|0)|0)|0)&65535;b[(c[f>>2]|0)+((c[h>>2]|0)-1<<1)>>1]=s;c[m>>2]=(c[h>>2]|0)-2;while(1){if((c[m>>2]|0)<0)break;s=(eg(b[(c[f>>2]|0)+(c[m>>2]<<1)>>1]|0,(b[(c[f>>2]|0)+((c[m>>2]|0)+1<<1)>>1]|0)-(b[(c[g>>2]|0)+((c[m>>2]|0)+1<<1)>>1]|0)|0)|0)&65535;b[(c[f>>2]|0)+(c[m>>2]<<1)>>1]=s;c[m>>2]=(c[m>>2]|0)+-1}i=t;return}function dg(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)>(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function eg(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)<(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function fg(a,d,e){a=a|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0;m=i;i=i+32|0;f=m+20|0;g=m+16|0;h=m+12|0;j=m+8|0;k=m+4|0;l=m;c[f>>2]=a;c[g>>2]=d;c[h>>2]=e;c[k>>2]=gg(b[c[g>>2]>>1]|0,1)|0;c[k>>2]=131072/(c[k>>2]|0)|0;c[l>>2]=gg((b[(c[g>>2]|0)+2>>1]|0)-(b[c[g>>2]>>1]|0)|0,1)|0;c[l>>2]=131072/(c[l>>2]|0)|0;d=(hg((c[k>>2]|0)+(c[l>>2]|0)|0,32767)|0)&65535;b[c[f>>2]>>1]=d;c[j>>2]=1;while(1){if((c[j>>2]|0)>=((c[h>>2]|0)-1|0))break;c[k>>2]=gg((b[(c[g>>2]|0)+((c[j>>2]|0)+1<<1)>>1]|0)-(b[(c[g>>2]|0)+(c[j>>2]<<1)>>1]|0)|0,1)|0;c[k>>2]=131072/(c[k>>2]|0)|0;d=(hg((c[k>>2]|0)+(c[l>>2]|0)|0,32767)|0)&65535;b[(c[f>>2]|0)+(c[j>>2]<<1)>>1]=d;c[l>>2]=gg((b[(c[g>>2]|0)+((c[j>>2]|0)+2<<1)>>1]|0)-(b[(c[g>>2]|0)+((c[j>>2]|0)+1<<1)>>1]|0)|0,1)|0;c[l>>2]=131072/(c[l>>2]|0)|0;d=(hg((c[k>>2]|0)+(c[l>>2]|0)|0,32767)|0)&65535;b[(c[f>>2]|0)+((c[j>>2]|0)+1<<1)>>1]=d;c[j>>2]=(c[j>>2]|0)+2}c[k>>2]=gg(32768-(b[(c[g>>2]|0)+((c[h>>2]|0)-1<<1)>>1]|0)|0,1)|0;c[k>>2]=131072/(c[k>>2]|0)|0;l=(hg((c[k>>2]|0)+(c[l>>2]|0)|0,32767)|0)&65535;b[(c[f>>2]|0)+((c[h>>2]|0)-1<<1)>>1]=l;i=m;return}function gg(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)>(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function hg(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)<(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function ig(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0;m=i;i=i+32|0;g=m+20|0;h=m+16|0;j=m+12|0;k=m+8|0;n=m+4|0;l=m;c[h>>2]=b;c[j>>2]=d;c[k>>2]=e;c[n>>2]=f;oj(c[h>>2]|0,0,300)|0;d=(c[j>>2]|0)!=8e3&(c[j>>2]|0)!=12e3&(c[j>>2]|0)!=16e3;do if(c[n>>2]|0){if(!(d&(c[j>>2]|0)!=24e3&(c[j>>2]|0)!=48e3)?!((c[k>>2]|0)!=8e3&(c[k>>2]|0)!=12e3&(c[k>>2]|0)!=16e3):0){c[(c[h>>2]|0)+292>>2]=a[30493+((((c[j>>2]>>12)-((c[j>>2]|0)>16e3&1)>>((c[j>>2]|0)>24e3&1))-1|0)*3|0)+(((c[k>>2]>>12)-((c[k>>2]|0)>16e3&1)>>((c[k>>2]|0)>24e3&1))-1)>>0];break}c[g>>2]=-1;n=c[g>>2]|0;i=m;return n|0}else{if(!d?!((c[k>>2]|0)!=8e3&(c[k>>2]|0)!=12e3&(c[k>>2]|0)!=16e3&(c[k>>2]|0)!=24e3&(c[k>>2]|0)!=48e3):0){c[(c[h>>2]|0)+292>>2]=a[30508+((((c[j>>2]>>12)-((c[j>>2]|0)>16e3&1)>>((c[j>>2]|0)>24e3&1))-1|0)*5|0)+(((c[k>>2]>>12)-((c[k>>2]|0)>16e3&1)>>((c[k>>2]|0)>24e3&1))-1)>>0];break}c[g>>2]=-1;n=c[g>>2]|0;i=m;return n|0}while(0);c[(c[h>>2]|0)+284>>2]=(c[j>>2]|0)/1e3|0;c[(c[h>>2]|0)+288>>2]=(c[k>>2]|0)/1e3|0;c[(c[h>>2]|0)+268>>2]=(c[(c[h>>2]|0)+284>>2]|0)*10;c[l>>2]=0;e=c[k>>2]|0;b=c[j>>2]|0;do if((c[k>>2]|0)>(c[j>>2]|0)){d=(c[h>>2]|0)+264|0;if((e|0)==(b<<1|0)){c[d>>2]=1;break}else{c[d>>2]=2;c[l>>2]=1;break}}else{d=(c[h>>2]|0)+264|0;if((e|0)>=(b|0)){c[d>>2]=0;break}c[d>>2]=3;if((c[k>>2]<<2|0)==((c[j>>2]|0)*3|0)){c[(c[h>>2]|0)+280>>2]=3;c[(c[h>>2]|0)+276>>2]=18;c[(c[h>>2]|0)+296>>2]=24846;break}if(((c[k>>2]|0)*3|0)==(c[j>>2]<<1|0)){c[(c[h>>2]|0)+280>>2]=2;c[(c[h>>2]|0)+276>>2]=18;c[(c[h>>2]|0)+296>>2]=24904;break}if((c[k>>2]<<1|0)==(c[j>>2]|0)){c[(c[h>>2]|0)+280>>2]=1;c[(c[h>>2]|0)+276>>2]=24;c[(c[h>>2]|0)+296>>2]=24944;break}if(((c[k>>2]|0)*3|0)==(c[j>>2]|0)){c[(c[h>>2]|0)+280>>2]=1;c[(c[h>>2]|0)+276>>2]=36;c[(c[h>>2]|0)+296>>2]=24972;break}if((c[k>>2]<<2|0)==(c[j>>2]|0)){c[(c[h>>2]|0)+280>>2]=1;c[(c[h>>2]|0)+276>>2]=36;c[(c[h>>2]|0)+296>>2]=25012;break}if(((c[k>>2]|0)*6|0)==(c[j>>2]|0)){c[(c[h>>2]|0)+280>>2]=1;c[(c[h>>2]|0)+276>>2]=36;c[(c[h>>2]|0)+296>>2]=25052;break}c[g>>2]=-1;n=c[g>>2]|0;i=m;return n|0}while(0);c[(c[h>>2]|0)+272>>2]=((c[j>>2]<<14+(c[l>>2]|0)|0)/(c[k>>2]|0)|0)<<2;while(1){n=_(c[(c[h>>2]|0)+272>>2]>>16,(c[k>>2]&65535)<<16>>16)|0;n=n+((_(c[(c[h>>2]|0)+272>>2]&65535,(c[k>>2]&65535)<<16>>16)|0)>>16)|0;n=n+(_(c[(c[h>>2]|0)+272>>2]|0,(c[k>>2]>>15)+1>>1)|0)|0;if((n|0)>=(c[j>>2]<<c[l>>2]|0))break;n=(c[h>>2]|0)+272|0;c[n>>2]=(c[n>>2]|0)+1}c[g>>2]=0;n=c[g>>2]|0;i=m;return n|0}function jg(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0;l=i;i=i+32|0;f=l+16|0;g=l+12|0;h=l+8|0;j=l+4|0;k=l;c[f>>2]=a;c[g>>2]=b;c[h>>2]=d;c[j>>2]=e;c[k>>2]=(c[(c[f>>2]|0)+284>>2]|0)-(c[(c[f>>2]|0)+292>>2]|0);pj((c[f>>2]|0)+168+(c[(c[f>>2]|0)+292>>2]<<1)|0,c[h>>2]|0,c[k>>2]<<1|0)|0;switch(c[(c[f>>2]|0)+264>>2]|0){case 1:{sg(c[f>>2]|0,c[g>>2]|0,(c[f>>2]|0)+168|0,c[(c[f>>2]|0)+284>>2]|0);sg(c[f>>2]|0,(c[g>>2]|0)+(c[(c[f>>2]|0)+288>>2]<<1)|0,(c[h>>2]|0)+(c[k>>2]<<1)|0,(c[j>>2]|0)-(c[(c[f>>2]|0)+284>>2]|0)|0);break}case 2:{pg(c[f>>2]|0,c[g>>2]|0,(c[f>>2]|0)+168|0,c[(c[f>>2]|0)+284>>2]|0);pg(c[f>>2]|0,(c[g>>2]|0)+(c[(c[f>>2]|0)+288>>2]<<1)|0,(c[h>>2]|0)+(c[k>>2]<<1)|0,(c[j>>2]|0)-(c[(c[f>>2]|0)+284>>2]|0)|0);break}case 3:{ng(c[f>>2]|0,c[g>>2]|0,(c[f>>2]|0)+168|0,c[(c[f>>2]|0)+284>>2]|0);ng(c[f>>2]|0,(c[g>>2]|0)+(c[(c[f>>2]|0)+288>>2]<<1)|0,(c[h>>2]|0)+(c[k>>2]<<1)|0,(c[j>>2]|0)-(c[(c[f>>2]|0)+284>>2]|0)|0);break}default:{pj(c[g>>2]|0,(c[f>>2]|0)+168|0,c[(c[f>>2]|0)+284>>2]<<1|0)|0;pj((c[g>>2]|0)+(c[(c[f>>2]|0)+288>>2]<<1)|0,(c[h>>2]|0)+(c[k>>2]<<1)|0,(c[j>>2]|0)-(c[(c[f>>2]|0)+284>>2]|0)<<1|0)|0}}pj((c[f>>2]|0)+168|0,(c[h>>2]|0)+((c[j>>2]|0)-(c[(c[f>>2]|0)+292>>2]|0)<<1)|0,c[(c[f>>2]|0)+292>>2]<<1|0)|0;i=l;return 0}function kg(a,d,e,f){a=a|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;q=i;i=i+1968|0;g=q+1964|0;h=q+1960|0;j=q+1956|0;k=q+1952|0;o=q+1948|0;n=q+1944|0;p=q+1940|0;m=q+1936|0;l=q;c[g>>2]=a;c[h>>2]=d;c[j>>2]=e;c[k>>2]=f;d=c[g>>2]|0;c[l>>2]=c[d>>2];c[l+4>>2]=c[d+4>>2];c[l+8>>2]=c[d+8>>2];c[l+12>>2]=c[d+12>>2];while(1){c[o>>2]=(c[k>>2]|0)<480?c[k>>2]|0:480;mg((c[g>>2]|0)+16|0,l+16|0,c[j>>2]|0,25092,c[o>>2]|0);c[m>>2]=l;c[n>>2]=c[o>>2];while(1){if((c[n>>2]|0)<=2)break;d=_(c[c[m>>2]>>2]>>16,b[12548]|0)|0;c[p>>2]=d+((_(c[c[m>>2]>>2]&65535,b[12548]|0)|0)>>16);d=_(c[(c[m>>2]|0)+4>>2]>>16,b[12549]|0)|0;c[p>>2]=(c[p>>2]|0)+(d+((_(c[(c[m>>2]|0)+4>>2]&65535,b[12549]|0)|0)>>16));d=_(c[(c[m>>2]|0)+8>>2]>>16,b[12551]|0)|0;c[p>>2]=(c[p>>2]|0)+(d+((_(c[(c[m>>2]|0)+8>>2]&65535,b[12551]|0)|0)>>16));d=_(c[(c[m>>2]|0)+12>>2]>>16,b[12550]|0)|0;c[p>>2]=(c[p>>2]|0)+(d+((_(c[(c[m>>2]|0)+12>>2]&65535,b[12550]|0)|0)>>16));if(((c[p>>2]>>5)+1>>1|0)<=32767)if(((c[p>>2]>>5)+1>>1|0)<-32768)a=-32768;else a=(c[p>>2]>>5)+1>>1;else a=32767;d=c[h>>2]|0;c[h>>2]=d+2;b[d>>1]=a;d=_(c[(c[m>>2]|0)+4>>2]>>16,b[12550]|0)|0;c[p>>2]=d+((_(c[(c[m>>2]|0)+4>>2]&65535,b[12550]|0)|0)>>16);d=_(c[(c[m>>2]|0)+8>>2]>>16,b[12551]|0)|0;c[p>>2]=(c[p>>2]|0)+(d+((_(c[(c[m>>2]|0)+8>>2]&65535,b[12551]|0)|0)>>16));d=_(c[(c[m>>2]|0)+12>>2]>>16,b[12549]|0)|0;c[p>>2]=(c[p>>2]|0)+(d+((_(c[(c[m>>2]|0)+12>>2]&65535,b[12549]|0)|0)>>16));d=_(c[(c[m>>2]|0)+16>>2]>>16,b[12548]|0)|0;c[p>>2]=(c[p>>2]|0)+(d+((_(c[(c[m>>2]|0)+16>>2]&65535,b[12548]|0)|0)>>16));if(((c[p>>2]>>5)+1>>1|0)<=32767)if(((c[p>>2]>>5)+1>>1|0)<-32768)a=-32768;else a=(c[p>>2]>>5)+1>>1;else a=32767;d=c[h>>2]|0;c[h>>2]=d+2;b[d>>1]=a;c[m>>2]=(c[m>>2]|0)+12;c[n>>2]=(c[n>>2]|0)-3}c[j>>2]=(c[j>>2]|0)+(c[o>>2]<<1);c[k>>2]=(c[k>>2]|0)-(c[o>>2]|0);if((c[k>>2]|0)<=0)break;d=l+(c[o>>2]<<2)|0;c[l>>2]=c[d>>2];c[l+4>>2]=c[d+4>>2];c[l+8>>2]=c[d+8>>2];c[l+12>>2]=c[d+12>>2]}p=c[g>>2]|0;o=l+(c[o>>2]<<2)|0;c[p>>2]=c[o>>2];c[p+4>>2]=c[o+4>>2];c[p+8>>2]=c[o+8>>2];c[p+12>>2]=c[o+12>>2];i=q;return}function lg(a,d,e,f){a=a|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;q=i;i=i+48|0;g=q+36|0;h=q+32|0;j=q+28|0;r=q+24|0;n=q+20|0;o=q+16|0;m=q+12|0;p=q+8|0;l=q+4|0;k=q;c[g>>2]=a;c[h>>2]=d;c[j>>2]=e;c[r>>2]=f;c[o>>2]=c[r>>2]>>1;c[n>>2]=0;while(1){if((c[n>>2]|0)>=(c[o>>2]|0))break;c[m>>2]=b[(c[j>>2]|0)+(c[n>>2]<<1<<1)>>1]<<10;c[l>>2]=(c[m>>2]|0)-(c[c[g>>2]>>2]|0);r=_(c[l>>2]>>16,-25727)|0;c[k>>2]=(c[l>>2]|0)+(r+((_(c[l>>2]&65535,-25727)|0)>>16));c[p>>2]=(c[c[g>>2]>>2]|0)+(c[k>>2]|0);c[c[g>>2]>>2]=(c[m>>2]|0)+(c[k>>2]|0);c[m>>2]=b[(c[j>>2]|0)+((c[n>>2]<<1)+1<<1)>>1]<<10;c[l>>2]=(c[m>>2]|0)-(c[(c[g>>2]|0)+4>>2]|0);c[k>>2]=((c[l>>2]>>16)*9872|0)+((c[l>>2]&65535)*9872>>16);c[p>>2]=(c[p>>2]|0)+(c[(c[g>>2]|0)+4>>2]|0);c[p>>2]=(c[p>>2]|0)+(c[k>>2]|0);c[(c[g>>2]|0)+4>>2]=(c[m>>2]|0)+(c[k>>2]|0);if(((c[p>>2]>>10)+1>>1|0)<=32767)if(((c[p>>2]>>10)+1>>1|0)<-32768)a=-32768;else a=(c[p>>2]>>10)+1>>1;else a=32767;b[(c[h>>2]|0)+(c[n>>2]<<1)>>1]=a;c[n>>2]=(c[n>>2]|0)+1}i=q;return}function mg(a,d,e,f,g){a=a|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;p=i;i=i+32|0;h=p+24|0;j=p+20|0;k=p+16|0;l=p+12|0;m=p+8|0;n=p+4|0;o=p;c[h>>2]=a;c[j>>2]=d;c[k>>2]=e;c[l>>2]=f;c[m>>2]=g;c[n>>2]=0;while(1){if((c[n>>2]|0)>=(c[m>>2]|0))break;c[o>>2]=(c[c[h>>2]>>2]|0)+(b[(c[k>>2]|0)+(c[n>>2]<<1)>>1]<<8);c[(c[j>>2]|0)+(c[n>>2]<<2)>>2]=c[o>>2];c[o>>2]=c[o>>2]<<2;d=_(c[o>>2]>>16,b[c[l>>2]>>1]|0)|0;d=(c[(c[h>>2]|0)+4>>2]|0)+(d+((_(c[o>>2]&65535,b[c[l>>2]>>1]|0)|0)>>16))|0;c[c[h>>2]>>2]=d;d=_(c[o>>2]>>16,b[(c[l>>2]|0)+2>>1]|0)|0;d=d+((_(c[o>>2]&65535,b[(c[l>>2]|0)+2>>1]|0)|0)>>16)|0;c[(c[h>>2]|0)+4>>2]=d;c[n>>2]=(c[n>>2]|0)+1}i=p;return}function ng(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;p=i;i=i+48|0;q=p+36|0;f=p+32|0;g=p+28|0;h=p+24|0;l=p+20|0;o=p+16|0;n=p+12|0;m=p+8|0;k=p+4|0;j=p;c[q>>2]=a;c[f>>2]=b;c[g>>2]=d;c[h>>2]=e;c[l>>2]=c[q>>2];b=(c[(c[l>>2]|0)+268>>2]|0)+(c[(c[l>>2]|0)+276>>2]|0)|0;c[j>>2]=ia()|0;a=i;i=i+((1*(b<<2)|0)+15&-16)|0;pj(a|0,(c[l>>2]|0)+24|0,c[(c[l>>2]|0)+276>>2]<<2|0)|0;c[k>>2]=(c[(c[l>>2]|0)+296>>2]|0)+4;c[m>>2]=c[(c[l>>2]|0)+272>>2];while(1){if((c[h>>2]|0)<(c[(c[l>>2]|0)+268>>2]|0))d=c[h>>2]|0;else d=c[(c[l>>2]|0)+268>>2]|0;c[o>>2]=d;mg(c[l>>2]|0,a+(c[(c[l>>2]|0)+276>>2]<<2)|0,c[g>>2]|0,c[(c[l>>2]|0)+296>>2]|0,c[o>>2]|0);c[n>>2]=c[o>>2]<<16;c[f>>2]=og(c[f>>2]|0,a,c[k>>2]|0,c[(c[l>>2]|0)+276>>2]|0,c[(c[l>>2]|0)+280>>2]|0,c[n>>2]|0,c[m>>2]|0)|0;c[g>>2]=(c[g>>2]|0)+(c[o>>2]<<1);c[h>>2]=(c[h>>2]|0)-(c[o>>2]|0);if((c[h>>2]|0)<=1)break;pj(a|0,a+(c[o>>2]<<2)|0,c[(c[l>>2]|0)+276>>2]<<2|0)|0}pj((c[l>>2]|0)+24|0,a+(c[o>>2]<<2)|0,c[(c[l>>2]|0)+276>>2]<<2|0)|0;na(c[j>>2]|0);i=p;return}function og(a,d,e,f,g,h,j){a=a|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0;v=i;i=i+48|0;n=v+44|0;o=v+40|0;p=v+36|0;w=v+32|0;k=v+28|0;q=v+24|0;r=v+20|0;t=v+16|0;u=v+12|0;s=v+8|0;l=v+4|0;m=v;c[n>>2]=a;c[o>>2]=d;c[p>>2]=e;c[w>>2]=f;c[k>>2]=g;c[q>>2]=h;c[r>>2]=j;switch(c[w>>2]|0){case 18:{c[t>>2]=0;while(1){if((c[t>>2]|0)>=(c[q>>2]|0))break;c[s>>2]=(c[o>>2]|0)+(c[t>>2]>>16<<2);w=_((c[t>>2]&65535)>>16,(c[k>>2]&65535)<<16>>16)|0;c[l>>2]=w+((_(c[t>>2]&65535,(c[k>>2]&65535)<<16>>16)|0)>>16);c[m>>2]=(c[p>>2]|0)+((c[l>>2]|0)*9<<1);w=_(c[c[s>>2]>>2]>>16,b[c[m>>2]>>1]|0)|0;c[u>>2]=w+((_(c[c[s>>2]>>2]&65535,b[c[m>>2]>>1]|0)|0)>>16);w=_(c[(c[s>>2]|0)+4>>2]>>16,b[(c[m>>2]|0)+2>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_(c[(c[s>>2]|0)+4>>2]&65535,b[(c[m>>2]|0)+2>>1]|0)|0)>>16));w=_(c[(c[s>>2]|0)+8>>2]>>16,b[(c[m>>2]|0)+4>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_(c[(c[s>>2]|0)+8>>2]&65535,b[(c[m>>2]|0)+4>>1]|0)|0)>>16));w=_(c[(c[s>>2]|0)+12>>2]>>16,b[(c[m>>2]|0)+6>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_(c[(c[s>>2]|0)+12>>2]&65535,b[(c[m>>2]|0)+6>>1]|0)|0)>>16));w=_(c[(c[s>>2]|0)+16>>2]>>16,b[(c[m>>2]|0)+8>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_(c[(c[s>>2]|0)+16>>2]&65535,b[(c[m>>2]|0)+8>>1]|0)|0)>>16));w=_(c[(c[s>>2]|0)+20>>2]>>16,b[(c[m>>2]|0)+10>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_(c[(c[s>>2]|0)+20>>2]&65535,b[(c[m>>2]|0)+10>>1]|0)|0)>>16));w=_(c[(c[s>>2]|0)+24>>2]>>16,b[(c[m>>2]|0)+12>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_(c[(c[s>>2]|0)+24>>2]&65535,b[(c[m>>2]|0)+12>>1]|0)|0)>>16));w=_(c[(c[s>>2]|0)+28>>2]>>16,b[(c[m>>2]|0)+14>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_(c[(c[s>>2]|0)+28>>2]&65535,b[(c[m>>2]|0)+14>>1]|0)|0)>>16));w=_(c[(c[s>>2]|0)+32>>2]>>16,b[(c[m>>2]|0)+16>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_(c[(c[s>>2]|0)+32>>2]&65535,b[(c[m>>2]|0)+16>>1]|0)|0)>>16));c[m>>2]=(c[p>>2]|0)+(((c[k>>2]|0)-1-(c[l>>2]|0)|0)*9<<1);w=_(c[(c[s>>2]|0)+68>>2]>>16,b[c[m>>2]>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_(c[(c[s>>2]|0)+68>>2]&65535,b[c[m>>2]>>1]|0)|0)>>16));w=_(c[(c[s>>2]|0)+64>>2]>>16,b[(c[m>>2]|0)+2>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_(c[(c[s>>2]|0)+64>>2]&65535,b[(c[m>>2]|0)+2>>1]|0)|0)>>16));w=_(c[(c[s>>2]|0)+60>>2]>>16,b[(c[m>>2]|0)+4>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_(c[(c[s>>2]|0)+60>>2]&65535,b[(c[m>>2]|0)+4>>1]|0)|0)>>16));w=_(c[(c[s>>2]|0)+56>>2]>>16,b[(c[m>>2]|0)+6>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_(c[(c[s>>2]|0)+56>>2]&65535,b[(c[m>>2]|0)+6>>1]|0)|0)>>16));w=_(c[(c[s>>2]|0)+52>>2]>>16,b[(c[m>>2]|0)+8>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_(c[(c[s>>2]|0)+52>>2]&65535,b[(c[m>>2]|0)+8>>1]|0)|0)>>16));w=_(c[(c[s>>2]|0)+48>>2]>>16,b[(c[m>>2]|0)+10>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_(c[(c[s>>2]|0)+48>>2]&65535,b[(c[m>>2]|0)+10>>1]|0)|0)>>16));w=_(c[(c[s>>2]|0)+44>>2]>>16,b[(c[m>>2]|0)+12>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_(c[(c[s>>2]|0)+44>>2]&65535,b[(c[m>>2]|0)+12>>1]|0)|0)>>16));w=_(c[(c[s>>2]|0)+40>>2]>>16,b[(c[m>>2]|0)+14>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_(c[(c[s>>2]|0)+40>>2]&65535,b[(c[m>>2]|0)+14>>1]|0)|0)>>16));w=_(c[(c[s>>2]|0)+36>>2]>>16,b[(c[m>>2]|0)+16>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_(c[(c[s>>2]|0)+36>>2]&65535,b[(c[m>>2]|0)+16>>1]|0)|0)>>16));if(((c[u>>2]>>5)+1>>1|0)<=32767)if(((c[u>>2]>>5)+1>>1|0)<-32768)e=-32768;else e=(c[u>>2]>>5)+1>>1;else e=32767;w=c[n>>2]|0;c[n>>2]=w+2;b[w>>1]=e;c[t>>2]=(c[t>>2]|0)+(c[r>>2]|0)}w=c[n>>2]|0;i=v;return w|0}case 24:{c[t>>2]=0;while(1){if((c[t>>2]|0)>=(c[q>>2]|0))break;c[s>>2]=(c[o>>2]|0)+(c[t>>2]>>16<<2);w=_((c[c[s>>2]>>2]|0)+(c[(c[s>>2]|0)+92>>2]|0)>>16,b[c[p>>2]>>1]|0)|0;c[u>>2]=w+((_((c[c[s>>2]>>2]|0)+(c[(c[s>>2]|0)+92>>2]|0)&65535,b[c[p>>2]>>1]|0)|0)>>16);w=_((c[(c[s>>2]|0)+4>>2]|0)+(c[(c[s>>2]|0)+88>>2]|0)>>16,b[(c[p>>2]|0)+2>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+4>>2]|0)+(c[(c[s>>2]|0)+88>>2]|0)&65535,b[(c[p>>2]|0)+2>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+8>>2]|0)+(c[(c[s>>2]|0)+84>>2]|0)>>16,b[(c[p>>2]|0)+4>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+8>>2]|0)+(c[(c[s>>2]|0)+84>>2]|0)&65535,b[(c[p>>2]|0)+4>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+12>>2]|0)+(c[(c[s>>2]|0)+80>>2]|0)>>16,b[(c[p>>2]|0)+6>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+12>>2]|0)+(c[(c[s>>2]|0)+80>>2]|0)&65535,b[(c[p>>2]|0)+6>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+16>>2]|0)+(c[(c[s>>2]|0)+76>>2]|0)>>16,b[(c[p>>2]|0)+8>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+16>>2]|0)+(c[(c[s>>2]|0)+76>>2]|0)&65535,b[(c[p>>2]|0)+8>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+20>>2]|0)+(c[(c[s>>2]|0)+72>>2]|0)>>16,b[(c[p>>2]|0)+10>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+20>>2]|0)+(c[(c[s>>2]|0)+72>>2]|0)&65535,b[(c[p>>2]|0)+10>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+24>>2]|0)+(c[(c[s>>2]|0)+68>>2]|0)>>16,b[(c[p>>2]|0)+12>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+24>>2]|0)+(c[(c[s>>2]|0)+68>>2]|0)&65535,b[(c[p>>2]|0)+12>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+28>>2]|0)+(c[(c[s>>2]|0)+64>>2]|0)>>16,b[(c[p>>2]|0)+14>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+28>>2]|0)+(c[(c[s>>2]|0)+64>>2]|0)&65535,b[(c[p>>2]|0)+14>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+32>>2]|0)+(c[(c[s>>2]|0)+60>>2]|0)>>16,b[(c[p>>2]|0)+16>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+32>>2]|0)+(c[(c[s>>2]|0)+60>>2]|0)&65535,b[(c[p>>2]|0)+16>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+36>>2]|0)+(c[(c[s>>2]|0)+56>>2]|0)>>16,b[(c[p>>2]|0)+18>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+36>>2]|0)+(c[(c[s>>2]|0)+56>>2]|0)&65535,b[(c[p>>2]|0)+18>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+40>>2]|0)+(c[(c[s>>2]|0)+52>>2]|0)>>16,b[(c[p>>2]|0)+20>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+40>>2]|0)+(c[(c[s>>2]|0)+52>>2]|0)&65535,b[(c[p>>2]|0)+20>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+44>>2]|0)+(c[(c[s>>2]|0)+48>>2]|0)>>16,b[(c[p>>2]|0)+22>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+44>>2]|0)+(c[(c[s>>2]|0)+48>>2]|0)&65535,b[(c[p>>2]|0)+22>>1]|0)|0)>>16));if(((c[u>>2]>>5)+1>>1|0)<=32767)if(((c[u>>2]>>5)+1>>1|0)<-32768)e=-32768;else e=(c[u>>2]>>5)+1>>1;else e=32767;w=c[n>>2]|0;c[n>>2]=w+2;b[w>>1]=e;c[t>>2]=(c[t>>2]|0)+(c[r>>2]|0)}w=c[n>>2]|0;i=v;return w|0}case 36:{c[t>>2]=0;while(1){if((c[t>>2]|0)>=(c[q>>2]|0))break;c[s>>2]=(c[o>>2]|0)+(c[t>>2]>>16<<2);w=_((c[c[s>>2]>>2]|0)+(c[(c[s>>2]|0)+140>>2]|0)>>16,b[c[p>>2]>>1]|0)|0;c[u>>2]=w+((_((c[c[s>>2]>>2]|0)+(c[(c[s>>2]|0)+140>>2]|0)&65535,b[c[p>>2]>>1]|0)|0)>>16);w=_((c[(c[s>>2]|0)+4>>2]|0)+(c[(c[s>>2]|0)+136>>2]|0)>>16,b[(c[p>>2]|0)+2>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+4>>2]|0)+(c[(c[s>>2]|0)+136>>2]|0)&65535,b[(c[p>>2]|0)+2>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+8>>2]|0)+(c[(c[s>>2]|0)+132>>2]|0)>>16,b[(c[p>>2]|0)+4>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+8>>2]|0)+(c[(c[s>>2]|0)+132>>2]|0)&65535,b[(c[p>>2]|0)+4>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+12>>2]|0)+(c[(c[s>>2]|0)+128>>2]|0)>>16,b[(c[p>>2]|0)+6>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+12>>2]|0)+(c[(c[s>>2]|0)+128>>2]|0)&65535,b[(c[p>>2]|0)+6>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+16>>2]|0)+(c[(c[s>>2]|0)+124>>2]|0)>>16,b[(c[p>>2]|0)+8>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+16>>2]|0)+(c[(c[s>>2]|0)+124>>2]|0)&65535,b[(c[p>>2]|0)+8>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+20>>2]|0)+(c[(c[s>>2]|0)+120>>2]|0)>>16,b[(c[p>>2]|0)+10>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+20>>2]|0)+(c[(c[s>>2]|0)+120>>2]|0)&65535,b[(c[p>>2]|0)+10>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+24>>2]|0)+(c[(c[s>>2]|0)+116>>2]|0)>>16,b[(c[p>>2]|0)+12>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+24>>2]|0)+(c[(c[s>>2]|0)+116>>2]|0)&65535,b[(c[p>>2]|0)+12>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+28>>2]|0)+(c[(c[s>>2]|0)+112>>2]|0)>>16,b[(c[p>>2]|0)+14>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+28>>2]|0)+(c[(c[s>>2]|0)+112>>2]|0)&65535,b[(c[p>>2]|0)+14>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+32>>2]|0)+(c[(c[s>>2]|0)+108>>2]|0)>>16,b[(c[p>>2]|0)+16>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+32>>2]|0)+(c[(c[s>>2]|0)+108>>2]|0)&65535,b[(c[p>>2]|0)+16>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+36>>2]|0)+(c[(c[s>>2]|0)+104>>2]|0)>>16,b[(c[p>>2]|0)+18>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+36>>2]|0)+(c[(c[s>>2]|0)+104>>2]|0)&65535,b[(c[p>>2]|0)+18>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+40>>2]|0)+(c[(c[s>>2]|0)+100>>2]|0)>>16,b[(c[p>>2]|0)+20>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+40>>2]|0)+(c[(c[s>>2]|0)+100>>2]|0)&65535,b[(c[p>>2]|0)+20>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+44>>2]|0)+(c[(c[s>>2]|0)+96>>2]|0)>>16,b[(c[p>>2]|0)+22>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+44>>2]|0)+(c[(c[s>>2]|0)+96>>2]|0)&65535,b[(c[p>>2]|0)+22>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+48>>2]|0)+(c[(c[s>>2]|0)+92>>2]|0)>>16,b[(c[p>>2]|0)+24>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+48>>2]|0)+(c[(c[s>>2]|0)+92>>2]|0)&65535,b[(c[p>>2]|0)+24>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+52>>2]|0)+(c[(c[s>>2]|0)+88>>2]|0)>>16,b[(c[p>>2]|0)+26>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+52>>2]|0)+(c[(c[s>>2]|0)+88>>2]|0)&65535,b[(c[p>>2]|0)+26>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+56>>2]|0)+(c[(c[s>>2]|0)+84>>2]|0)>>16,b[(c[p>>2]|0)+28>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+56>>2]|0)+(c[(c[s>>2]|0)+84>>2]|0)&65535,b[(c[p>>2]|0)+28>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+60>>2]|0)+(c[(c[s>>2]|0)+80>>2]|0)>>16,b[(c[p>>2]|0)+30>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+60>>2]|0)+(c[(c[s>>2]|0)+80>>2]|0)&65535,b[(c[p>>2]|0)+30>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+64>>2]|0)+(c[(c[s>>2]|0)+76>>2]|0)>>16,b[(c[p>>2]|0)+32>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+64>>2]|0)+(c[(c[s>>2]|0)+76>>2]|0)&65535,b[(c[p>>2]|0)+32>>1]|0)|0)>>16));w=_((c[(c[s>>2]|0)+68>>2]|0)+(c[(c[s>>2]|0)+72>>2]|0)>>16,b[(c[p>>2]|0)+34>>1]|0)|0;c[u>>2]=(c[u>>2]|0)+(w+((_((c[(c[s>>2]|0)+68>>2]|0)+(c[(c[s>>2]|0)+72>>2]|0)&65535,b[(c[p>>2]|0)+34>>1]|0)|0)>>16));if(((c[u>>2]>>5)+1>>1|0)<=32767)if(((c[u>>2]>>5)+1>>1|0)<-32768)e=-32768;else e=(c[u>>2]>>5)+1>>1;else e=32767;w=c[n>>2]|0;c[n>>2]=w+2;b[w>>1]=e;c[t>>2]=(c[t>>2]|0)+(c[r>>2]|0)}w=c[n>>2]|0;i=v;return w|0}default:{w=c[n>>2]|0;i=v;return w|0}}return 0}function pg(a,d,e,f){a=a|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;p=i;i=i+48|0;q=p+32|0;g=p+28|0;h=p+24|0;j=p+20|0;l=p+16|0;o=p+12|0;n=p+8|0;m=p+4|0;k=p;c[q>>2]=a;c[g>>2]=d;c[h>>2]=e;c[j>>2]=f;c[l>>2]=c[q>>2];d=(c[(c[l>>2]|0)+268>>2]<<1)+8|0;c[k>>2]=ia()|0;a=i;i=i+((1*(d<<1)|0)+15&-16)|0;d=(c[l>>2]|0)+24|0;b[a>>1]=b[d>>1]|0;b[a+2>>1]=b[d+2>>1]|0;b[a+4>>1]=b[d+4>>1]|0;b[a+6>>1]=b[d+6>>1]|0;b[a+8>>1]=b[d+8>>1]|0;b[a+10>>1]=b[d+10>>1]|0;b[a+12>>1]=b[d+12>>1]|0;b[a+14>>1]=b[d+14>>1]|0;c[m>>2]=c[(c[l>>2]|0)+272>>2];while(1){if((c[j>>2]|0)<(c[(c[l>>2]|0)+268>>2]|0))e=c[j>>2]|0;else e=c[(c[l>>2]|0)+268>>2]|0;c[o>>2]=e;rg(c[l>>2]|0,a+16|0,c[h>>2]|0,c[o>>2]|0);c[n>>2]=c[o>>2]<<17;c[g>>2]=qg(c[g>>2]|0,a,c[n>>2]|0,c[m>>2]|0)|0;c[h>>2]=(c[h>>2]|0)+(c[o>>2]<<1);c[j>>2]=(c[j>>2]|0)-(c[o>>2]|0);if((c[j>>2]|0)<=0)break;q=a+(c[o>>2]<<1<<1)|0;b[a>>1]=b[q>>1]|0;b[a+2>>1]=b[q+2>>1]|0;b[a+4>>1]=b[q+4>>1]|0;b[a+6>>1]=b[q+6>>1]|0;b[a+8>>1]=b[q+8>>1]|0;b[a+10>>1]=b[q+10>>1]|0;b[a+12>>1]=b[q+12>>1]|0;b[a+14>>1]=b[q+14>>1]|0}q=(c[l>>2]|0)+24|0;o=a+(c[o>>2]<<1<<1)|0;b[q>>1]=b[o>>1]|0;b[q+2>>1]=b[o+2>>1]|0;b[q+4>>1]=b[o+4>>1]|0;b[q+6>>1]=b[o+6>>1]|0;b[q+8>>1]=b[o+8>>1]|0;b[q+10>>1]=b[o+10>>1]|0;b[q+12>>1]=b[o+12>>1]|0;b[q+14>>1]=b[o+14>>1]|0;na(c[k>>2]|0);i=p;return}function qg(a,d,e,f){a=a|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;p=i;i=i+32|0;g=p+28|0;h=p+24|0;j=p+20|0;k=p+16|0;m=p+12|0;n=p+8|0;l=p+4|0;o=p;c[g>>2]=a;c[h>>2]=d;c[j>>2]=e;c[k>>2]=f;c[m>>2]=0;while(1){if((c[m>>2]|0)>=(c[j>>2]|0))break;c[o>>2]=(((c[m>>2]&65535)>>16)*12|0)+((c[m>>2]&65535)*12>>16);c[l>>2]=(c[h>>2]|0)+(c[m>>2]>>16<<1);c[n>>2]=_(b[c[l>>2]>>1]|0,b[25104+(c[o>>2]<<3)>>1]|0)|0;c[n>>2]=(c[n>>2]|0)+(_(b[(c[l>>2]|0)+2>>1]|0,b[25104+(c[o>>2]<<3)+2>>1]|0)|0);c[n>>2]=(c[n>>2]|0)+(_(b[(c[l>>2]|0)+4>>1]|0,b[25104+(c[o>>2]<<3)+4>>1]|0)|0);c[n>>2]=(c[n>>2]|0)+(_(b[(c[l>>2]|0)+6>>1]|0,b[25104+(c[o>>2]<<3)+6>>1]|0)|0);c[n>>2]=(c[n>>2]|0)+(_(b[(c[l>>2]|0)+8>>1]|0,b[25104+(11-(c[o>>2]|0)<<3)+6>>1]|0)|0);c[n>>2]=(c[n>>2]|0)+(_(b[(c[l>>2]|0)+10>>1]|0,b[25104+(11-(c[o>>2]|0)<<3)+4>>1]|0)|0);c[n>>2]=(c[n>>2]|0)+(_(b[(c[l>>2]|0)+12>>1]|0,b[25104+(11-(c[o>>2]|0)<<3)+2>>1]|0)|0);c[n>>2]=(c[n>>2]|0)+(_(b[(c[l>>2]|0)+14>>1]|0,b[25104+(11-(c[o>>2]|0)<<3)>>1]|0)|0);if(((c[n>>2]>>14)+1>>1|0)<=32767)if(((c[n>>2]>>14)+1>>1|0)<-32768)d=-32768;else d=(c[n>>2]>>14)+1>>1;else d=32767;a=c[g>>2]|0;c[g>>2]=a+2;b[a>>1]=d;c[m>>2]=(c[m>>2]|0)+(c[k>>2]|0)}i=p;return c[g>>2]|0}function rg(a,d,e,f){a=a|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;r=i;i=i+48|0;g=r+36|0;h=r+32|0;j=r+28|0;k=r+24|0;o=r+20|0;n=r+16|0;p=r+12|0;q=r+8|0;m=r+4|0;l=r;c[g>>2]=a;c[h>>2]=d;c[j>>2]=e;c[k>>2]=f;c[o>>2]=0;while(1){if((c[o>>2]|0)>=(c[k>>2]|0))break;c[n>>2]=b[(c[j>>2]|0)+(c[o>>2]<<1)>>1]<<10;c[m>>2]=(c[n>>2]|0)-(c[c[g>>2]>>2]|0);d=_(c[m>>2]>>16,b[12417]|0)|0;c[l>>2]=d+((_(c[m>>2]&65535,b[12417]|0)|0)>>16);c[p>>2]=(c[c[g>>2]>>2]|0)+(c[l>>2]|0);c[c[g>>2]>>2]=(c[n>>2]|0)+(c[l>>2]|0);c[m>>2]=(c[p>>2]|0)-(c[(c[g>>2]|0)+4>>2]|0);d=_(c[m>>2]>>16,b[12418]|0)|0;c[l>>2]=d+((_(c[m>>2]&65535,b[12418]|0)|0)>>16);c[q>>2]=(c[(c[g>>2]|0)+4>>2]|0)+(c[l>>2]|0);c[(c[g>>2]|0)+4>>2]=(c[p>>2]|0)+(c[l>>2]|0);c[m>>2]=(c[q>>2]|0)-(c[(c[g>>2]|0)+8>>2]|0);d=_(c[m>>2]>>16,b[12419]|0)|0;c[l>>2]=(c[m>>2]|0)+(d+((_(c[m>>2]&65535,b[12419]|0)|0)>>16));c[p>>2]=(c[(c[g>>2]|0)+8>>2]|0)+(c[l>>2]|0);c[(c[g>>2]|0)+8>>2]=(c[q>>2]|0)+(c[l>>2]|0);if(((c[p>>2]>>9)+1>>1|0)<=32767)if(((c[p>>2]>>9)+1>>1|0)<-32768)a=-32768;else a=(c[p>>2]>>9)+1>>1;else a=32767;b[(c[h>>2]|0)+(c[o>>2]<<1<<1)>>1]=a;c[m>>2]=(c[n>>2]|0)-(c[(c[g>>2]|0)+12>>2]|0);d=_(c[m>>2]>>16,b[12420]|0)|0;c[l>>2]=d+((_(c[m>>2]&65535,b[12420]|0)|0)>>16);c[p>>2]=(c[(c[g>>2]|0)+12>>2]|0)+(c[l>>2]|0);c[(c[g>>2]|0)+12>>2]=(c[n>>2]|0)+(c[l>>2]|0);c[m>>2]=(c[p>>2]|0)-(c[(c[g>>2]|0)+16>>2]|0);d=_(c[m>>2]>>16,b[12421]|0)|0;c[l>>2]=d+((_(c[m>>2]&65535,b[12421]|0)|0)>>16);c[q>>2]=(c[(c[g>>2]|0)+16>>2]|0)+(c[l>>2]|0);c[(c[g>>2]|0)+16>>2]=(c[p>>2]|0)+(c[l>>2]|0);c[m>>2]=(c[q>>2]|0)-(c[(c[g>>2]|0)+20>>2]|0);d=_(c[m>>2]>>16,b[12422]|0)|0;c[l>>2]=(c[m>>2]|0)+(d+((_(c[m>>2]&65535,b[12422]|0)|0)>>16));c[p>>2]=(c[(c[g>>2]|0)+20>>2]|0)+(c[l>>2]|0);c[(c[g>>2]|0)+20>>2]=(c[q>>2]|0)+(c[l>>2]|0);if(((c[p>>2]>>9)+1>>1|0)<=32767)if(((c[p>>2]>>9)+1>>1|0)<-32768)a=-32768;else a=(c[p>>2]>>9)+1>>1;else a=32767;b[(c[h>>2]|0)+((c[o>>2]<<1)+1<<1)>>1]=a;c[o>>2]=(c[o>>2]|0)+1}i=r;return}function sg(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0;f=i;i=i+32|0;l=f+16|0;j=f+12|0;h=f+8|0;g=f+4|0;k=f;c[l>>2]=a;c[j>>2]=b;c[h>>2]=d;c[g>>2]=e;c[k>>2]=c[l>>2];rg(c[k>>2]|0,c[j>>2]|0,c[h>>2]|0,c[g>>2]|0);i=f;return}function tg(a){a=a|0;var b=0,d=0,e=0,f=0;f=i;i=i+16|0;b=f+8|0;d=f+4|0;e=f;c[d>>2]=a;a=c[d>>2]|0;if((c[d>>2]|0)<0){c[d>>2]=0-a;if((c[d>>2]|0)>=192){c[b>>2]=0;e=c[b>>2]|0;i=f;return e|0}else{c[e>>2]=c[d>>2]>>5;c[b>>2]=(c[17960+(c[e>>2]<<2)>>2]|0)-(_((c[17984+(c[e>>2]<<2)>>2]&65535)<<16>>16,(c[d>>2]&31)<<16>>16)|0);e=c[b>>2]|0;i=f;return e|0}}else if((a|0)>=192){c[b>>2]=32767;e=c[b>>2]|0;i=f;return e|0}else{c[e>>2]=c[d>>2]>>5;c[b>>2]=(c[18008+(c[e>>2]<<2)>>2]|0)+(_((c[17984+(c[e>>2]<<2)>>2]&65535)<<16>>16,(c[d>>2]&31)<<16>>16)|0);e=c[b>>2]|0;i=f;return e|0}return 0}function ug(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0;n=i;i=i+32|0;f=n+24|0;g=n+20|0;h=n+16|0;j=n+12|0;m=n+8|0;k=n+4|0;l=n;c[f>>2]=a;c[g>>2]=b;c[h>>2]=d;c[j>>2]=e;c[k>>2]=0;while(1){if((c[k>>2]|0)>=(c[j>>2]|0))break;c[(c[g>>2]|0)+(c[k>>2]<<2)>>2]=c[k>>2];c[k>>2]=(c[k>>2]|0)+1}c[k>>2]=1;while(1){if((c[k>>2]|0)>=(c[j>>2]|0))break;c[m>>2]=c[(c[f>>2]|0)+(c[k>>2]<<2)>>2];c[l>>2]=(c[k>>2]|0)-1;while(1){if((c[l>>2]|0)<0)break;if((c[m>>2]|0)>=(c[(c[f>>2]|0)+(c[l>>2]<<2)>>2]|0))break;c[(c[f>>2]|0)+((c[l>>2]|0)+1<<2)>>2]=c[(c[f>>2]|0)+(c[l>>2]<<2)>>2];c[(c[g>>2]|0)+((c[l>>2]|0)+1<<2)>>2]=c[(c[g>>2]|0)+(c[l>>2]<<2)>>2];c[l>>2]=(c[l>>2]|0)+-1}c[(c[f>>2]|0)+((c[l>>2]|0)+1<<2)>>2]=c[m>>2];c[(c[g>>2]|0)+((c[l>>2]|0)+1<<2)>>2]=c[k>>2];c[k>>2]=(c[k>>2]|0)+1}c[k>>2]=c[j>>2];while(1){if((c[k>>2]|0)>=(c[h>>2]|0))break;c[m>>2]=c[(c[f>>2]|0)+(c[k>>2]<<2)>>2];if((c[m>>2]|0)<(c[(c[f>>2]|0)+((c[j>>2]|0)-1<<2)>>2]|0)){c[l>>2]=(c[j>>2]|0)-2;while(1){if((c[l>>2]|0)<0)break;if((c[m>>2]|0)>=(c[(c[f>>2]|0)+(c[l>>2]<<2)>>2]|0))break;c[(c[f>>2]|0)+((c[l>>2]|0)+1<<2)>>2]=c[(c[f>>2]|0)+(c[l>>2]<<2)>>2];c[(c[g>>2]|0)+((c[l>>2]|0)+1<<2)>>2]=c[(c[g>>2]|0)+(c[l>>2]<<2)>>2];c[l>>2]=(c[l>>2]|0)+-1}c[(c[f>>2]|0)+((c[l>>2]|0)+1<<2)>>2]=c[m>>2];c[(c[g>>2]|0)+((c[l>>2]|0)+1<<2)>>2]=c[k>>2]}c[k>>2]=(c[k>>2]|0)+1}i=n;return}function vg(a,d){a=a|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0;k=i;i=i+32|0;e=k+16|0;f=k+12|0;j=k+8|0;g=k+4|0;h=k;c[e>>2]=a;c[f>>2]=d;c[g>>2]=1;while(1){if((c[g>>2]|0)>=(c[f>>2]|0))break;c[j>>2]=b[(c[e>>2]|0)+(c[g>>2]<<1)>>1];c[h>>2]=(c[g>>2]|0)-1;while(1){if((c[h>>2]|0)<0)break;if((c[j>>2]|0)>=(b[(c[e>>2]|0)+(c[h>>2]<<1)>>1]|0))break;b[(c[e>>2]|0)+((c[h>>2]|0)+1<<1)>>1]=b[(c[e>>2]|0)+(c[h>>2]<<1)>>1]|0;c[h>>2]=(c[h>>2]|0)+-1}b[(c[e>>2]|0)+((c[h>>2]|0)+1<<1)>>1]=c[j>>2];c[g>>2]=(c[g>>2]|0)+1}i=k;return}function wg(a,d,e,f){a=a|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;q=i;i=i+32|0;h=q+28|0;j=q+24|0;k=q+20|0;l=q+16|0;m=q+12|0;p=q+8|0;o=q+4|0;n=q;c[h>>2]=a;c[j>>2]=d;c[k>>2]=e;c[l>>2]=f;c[n>>2]=0;c[p>>2]=0;c[l>>2]=(c[l>>2]|0)+-1;c[m>>2]=0;while(1){if((c[m>>2]|0)>=(c[l>>2]|0))break;c[n>>2]=(c[n>>2]|0)+(_(b[(c[k>>2]|0)+(c[m>>2]<<1)>>1]|0,b[(c[k>>2]|0)+(c[m>>2]<<1)>>1]|0)|0);c[n>>2]=(c[n>>2]|0)+(_(b[(c[k>>2]|0)+((c[m>>2]|0)+1<<1)>>1]|0,b[(c[k>>2]|0)+((c[m>>2]|0)+1<<1)>>1]|0)|0);if((c[n>>2]|0)<0){g=4;break}c[m>>2]=(c[m>>2]|0)+2}if((g|0)==4){c[n>>2]=(c[n>>2]|0)>>>2;c[p>>2]=2;c[m>>2]=(c[m>>2]|0)+2}while(1){a=c[m>>2]|0;if((c[m>>2]|0)>=(c[l>>2]|0))break;c[o>>2]=_(b[(c[k>>2]|0)+(a<<1)>>1]|0,b[(c[k>>2]|0)+(c[m>>2]<<1)>>1]|0)|0;c[o>>2]=(c[o>>2]|0)+(_(b[(c[k>>2]|0)+((c[m>>2]|0)+1<<1)>>1]|0,b[(c[k>>2]|0)+((c[m>>2]|0)+1<<1)>>1]|0)|0);c[n>>2]=(c[n>>2]|0)+((c[o>>2]|0)>>>(c[p>>2]|0));if((c[n>>2]|0)<0){c[n>>2]=(c[n>>2]|0)>>>2;c[p>>2]=(c[p>>2]|0)+2}c[m>>2]=(c[m>>2]|0)+2}if((a|0)==(c[l>>2]|0)){c[o>>2]=_(b[(c[k>>2]|0)+(c[m>>2]<<1)>>1]|0,b[(c[k>>2]|0)+(c[m>>2]<<1)>>1]|0)|0;c[n>>2]=(c[n>>2]|0)+(c[o>>2]>>c[p>>2])}if(!(c[n>>2]&-1073741824)){p=c[p>>2]|0;o=c[j>>2]|0;c[o>>2]=p;o=c[n>>2]|0;p=c[h>>2]|0;c[p>>2]=o;i=q;return}c[n>>2]=(c[n>>2]|0)>>>2;c[p>>2]=(c[p>>2]|0)+2;p=c[p>>2]|0;o=c[j>>2]|0;c[o>>2]=p;o=c[n>>2]|0;p=c[h>>2]|0;c[p>>2]=o;i=q;return}function xg(a,d){a=a|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0;l=i;i=i+48|0;e=l+40|0;f=l+36|0;j=l+32|0;g=l+8|0;h=l+4|0;k=l;c[e>>2]=a;c[f>>2]=d;c[j>>2]=ec(c[e>>2]|0,28978,8)|0;c[g+8>>2]=(c[j>>2]|0)/5|0;c[g+12+8>>2]=(c[j>>2]|0)-((c[g+8>>2]|0)*5|0);c[j>>2]=0;while(1){if((c[j>>2]|0)>=2)break;a=ec(c[e>>2]|0,29031,8)|0;c[g+((c[j>>2]|0)*12|0)>>2]=a;a=ec(c[e>>2]|0,29038,8)|0;c[g+((c[j>>2]|0)*12|0)+4>>2]=a;c[j>>2]=(c[j>>2]|0)+1}c[j>>2]=0;while(1){if((c[j>>2]|0)>=2)break;a=g+((c[j>>2]|0)*12|0)|0;c[a>>2]=(c[a>>2]|0)+((c[g+((c[j>>2]|0)*12|0)+8>>2]|0)*3|0);c[h>>2]=b[24526+(c[g+((c[j>>2]|0)*12|0)>>2]<<1)>>1];c[k>>2]=(((b[24526+((c[g+((c[j>>2]|0)*12|0)>>2]|0)+1<<1)>>1]|0)-(c[h>>2]|0)>>16)*6554|0)+(((b[24526+((c[g+((c[j>>2]|0)*12|0)>>2]|0)+1<<1)>>1]|0)-(c[h>>2]|0)&65535)*6554>>16);a=(c[h>>2]|0)+(_((c[k>>2]&65535)<<16>>16,((c[g+((c[j>>2]|0)*12|0)+4>>2]<<1)+1&65535)<<16>>16)|0)|0;c[(c[f>>2]|0)+(c[j>>2]<<2)>>2]=a;c[j>>2]=(c[j>>2]|0)+1}k=c[f>>2]|0;c[k>>2]=(c[k>>2]|0)-(c[(c[f>>2]|0)+4>>2]|0);i=l;return}function yg(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;d=i;i=i+16|0;f=d+4|0;e=d;c[f>>2]=a;c[e>>2]=b;a=ec(c[f>>2]|0,29003,8)|0;c[c[e>>2]>>2]=a;i=d;return}function zg(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0;h=i;i=i+16|0;e=h+8|0;f=h+4|0;g=h;c[e>>2]=b;c[f>>2]=d;c[g>>2]=((a[(c[f>>2]|0)+2>>0]|0)*5|0)+(a[(c[f>>2]|0)+3+2>>0]|0);qc(c[e>>2]|0,c[g>>2]|0,28978,8);c[g>>2]=0;while(1){if((c[g>>2]|0)>=2)break;qc(c[e>>2]|0,a[(c[f>>2]|0)+((c[g>>2]|0)*3|0)>>0]|0,29031,8);qc(c[e>>2]|0,a[(c[f>>2]|0)+((c[g>>2]|0)*3|0)+1>>0]|0,29038,8);c[g>>2]=(c[g>>2]|0)+1}i=h;return}function Ag(b,d){b=b|0;d=d|0;var e=0,f=0,g=0;e=i;i=i+16|0;g=e;f=e+4|0;c[g>>2]=b;a[f>>0]=d;qc(c[g>>2]|0,a[f>>0]|0,29003,8);i=e;return}function Bg(a,b,d,e,f,g){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0;r=i;i=i+64|0;o=r+52|0;u=r+48|0;t=r+44|0;p=r+40|0;s=r+36|0;h=r+32|0;n=r+28|0;v=r+24|0;w=r+20|0;k=r+16|0;l=r+12|0;j=r+8|0;q=r+4|0;m=r;c[o>>2]=a;c[u>>2]=b;c[t>>2]=d;c[p>>2]=e;c[s>>2]=f;c[h>>2]=g;wg(k,v,c[u>>2]|0,c[s>>2]|0);wg(l,w,c[t>>2]|0,c[s>>2]|0);c[n>>2]=Cg(c[v>>2]|0,c[w>>2]|0)|0;c[n>>2]=(c[n>>2]|0)+(c[n>>2]&1);c[l>>2]=c[l>>2]>>(c[n>>2]|0)-(c[w>>2]|0);c[k>>2]=c[k>>2]>>(c[n>>2]|0)-(c[v>>2]|0);c[k>>2]=Cg(c[k>>2]|0,1)|0;c[j>>2]=Rf(c[u>>2]|0,c[t>>2]|0,c[n>>2]|0,c[s>>2]|0)|0;c[q>>2]=Dg(c[j>>2]|0,c[k>>2]|0,13)|0;if((c[q>>2]|0)>16384)f=16384;else f=(c[q>>2]|0)<-16384?-16384:c[q>>2]|0;c[q>>2]=f;w=_(c[q>>2]>>16,(c[q>>2]&65535)<<16>>16)|0;c[m>>2]=w+((_(c[q>>2]&65535,(c[q>>2]&65535)<<16>>16)|0)>>16);w=c[m>>2]|0;c[h>>2]=Cg(c[h>>2]|0,(c[m>>2]|0)>0?w:0-w|0)|0;c[n>>2]=c[n>>2]>>1;w=c[c[p>>2]>>2]|0;v=Fg(c[k>>2]|0)|0;v=_((v<<c[n>>2])-(c[c[p>>2]>>2]|0)>>16,(c[h>>2]&65535)<<16>>16)|0;u=Fg(c[k>>2]|0)|0;u=w+(v+((_((u<<c[n>>2])-(c[c[p>>2]>>2]|0)&65535,(c[h>>2]&65535)<<16>>16)|0)>>16))|0;c[c[p>>2]>>2]=u;u=_(c[j>>2]>>16,(c[q>>2]&65535)<<16>>16)|0;c[l>>2]=(c[l>>2]|0)-(u+((_(c[j>>2]&65535,(c[q>>2]&65535)<<16>>16)|0)>>16)<<4);u=_(c[k>>2]>>16,(c[m>>2]&65535)<<16>>16)|0;c[l>>2]=(c[l>>2]|0)+(u+((_(c[k>>2]&65535,(c[m>>2]&65535)<<16>>16)|0)>>16)<<6);u=c[(c[p>>2]|0)+4>>2]|0;v=Fg(c[l>>2]|0)|0;v=_((v<<c[n>>2])-(c[(c[p>>2]|0)+4>>2]|0)>>16,(c[h>>2]&65535)<<16>>16)|0;w=Fg(c[l>>2]|0)|0;w=u+(v+((_((w<<c[n>>2])-(c[(c[p>>2]|0)+4>>2]|0)&65535,(c[h>>2]&65535)<<16>>16)|0)>>16))|0;c[(c[p>>2]|0)+4>>2]=w;if((c[c[p>>2]>>2]|0)>1)f=c[c[p>>2]>>2]|0;else f=1;w=Dg(c[(c[p>>2]|0)+4>>2]|0,f,14)|0;c[c[o>>2]>>2]=w;if((c[c[o>>2]>>2]|0)>32767){v=32767;w=c[o>>2]|0;c[w>>2]=v;w=c[q>>2]|0;i=r;return w|0}if((c[c[o>>2]>>2]|0)<0){v=0;w=c[o>>2]|0;c[w>>2]=v;w=c[q>>2]|0;i=r;return w|0}v=c[c[o>>2]>>2]|0;w=c[o>>2]|0;c[w>>2]=v;w=c[q>>2]|0;i=r;return w|0}function Cg(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)>(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function Dg(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;h=i;i=i+48|0;f=h+40|0;q=h+36|0;p=h+32|0;j=h+28|0;l=h+24|0;k=h+20|0;g=h+16|0;m=h+12|0;n=h+8|0;o=h+4|0;e=h;c[q>>2]=a;c[p>>2]=b;c[j>>2]=d;b=c[q>>2]|0;c[l>>2]=(Eg((c[q>>2]|0)>0?b:0-b|0)|0)-1;c[n>>2]=c[q>>2]<<c[l>>2];b=c[p>>2]|0;c[k>>2]=(Eg((c[p>>2]|0)>0?b:0-b|0)|0)-1;c[o>>2]=c[p>>2]<<c[k>>2];c[m>>2]=536870911/(c[o>>2]>>16|0)|0;b=_(c[n>>2]>>16,(c[m>>2]&65535)<<16>>16)|0;c[e>>2]=b+((_(c[n>>2]&65535,(c[m>>2]&65535)<<16>>16)|0)>>16);b=c[n>>2]|0;a=c[o>>2]|0;d=c[e>>2]|0;d=yj(a|0,((a|0)<0)<<31>>31|0,d|0,((d|0)<0)<<31>>31|0)|0;d=nj(d|0,C|0,32)|0;c[n>>2]=b-(d<<3);d=_(c[n>>2]>>16,(c[m>>2]&65535)<<16>>16)|0;c[e>>2]=(c[e>>2]|0)+(d+((_(c[n>>2]&65535,(c[m>>2]&65535)<<16>>16)|0)>>16));c[g>>2]=29+(c[l>>2]|0)-(c[k>>2]|0)-(c[j>>2]|0);d=c[g>>2]|0;if((c[g>>2]|0)>=0)if((d|0)<32){c[f>>2]=c[e>>2]>>c[g>>2];q=c[f>>2]|0;i=h;return q|0}else{c[f>>2]=0;q=c[f>>2]|0;i=h;return q|0}a=c[e>>2]|0;b=0-(c[g>>2]|0)|0;do if((-2147483648>>0-d|0)>(2147483647>>0-(c[g>>2]|0)|0)){if((a|0)>(-2147483648>>b|0)){d=-2147483648>>0-(c[g>>2]|0);break}if((c[e>>2]|0)<(2147483647>>0-(c[g>>2]|0)|0)){d=2147483647>>0-(c[g>>2]|0);break}else{d=c[e>>2]|0;break}}else{if((a|0)>(2147483647>>b|0)){d=2147483647>>0-(c[g>>2]|0);break}if((c[e>>2]|0)<(-2147483648>>0-(c[g>>2]|0)|0)){d=-2147483648>>0-(c[g>>2]|0);break}else{d=c[e>>2]|0;break}}while(0);c[f>>2]=d<<0-(c[g>>2]|0);q=c[f>>2]|0;i=h;return q|0}function Eg(a){a=a|0;var b=0,d=0;d=i;i=i+16|0;b=d;c[b>>2]=a;if(!(c[b>>2]|0)){a=32;i=d;return a|0}a=32-(32-(aa(c[b>>2]|0)|0))|0;i=d;return a|0}function Fg(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0;h=i;i=i+32|0;b=h+16|0;d=h+12|0;g=h+8|0;f=h+4|0;e=h;c[d>>2]=a;if((c[d>>2]|0)<=0){c[b>>2]=0;g=c[b>>2]|0;i=h;return g|0}Gg(c[d>>2]|0,f,e);if(c[f>>2]&1|0)c[g>>2]=32768;else c[g>>2]=46214;c[g>>2]=c[g>>2]>>(c[f>>2]>>1);a=_(c[g>>2]>>16,(((c[e>>2]&65535)<<16>>16)*213&65535)<<16>>16)|0;c[g>>2]=(c[g>>2]|0)+(a+((_(c[g>>2]&65535,(((c[e>>2]&65535)<<16>>16)*213&65535)<<16>>16)|0)>>16));c[b>>2]=c[g>>2];g=c[b>>2]|0;i=h;return g|0}function Gg(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0;e=i;i=i+16|0;h=e+12|0;j=e+8|0;f=e+4|0;g=e;c[h>>2]=a;c[j>>2]=b;c[f>>2]=d;c[g>>2]=Eg(c[h>>2]|0)|0;c[c[j>>2]>>2]=c[g>>2];b=(Hg(c[h>>2]|0,24-(c[g>>2]|0)|0)|0)&127;c[c[f>>2]>>2]=b;i=e;return}function Hg(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0;k=i;i=i+32|0;e=k+20|0;d=k+16|0;f=k+12|0;j=k+8|0;h=k+4|0;g=k;c[d>>2]=a;c[f>>2]=b;c[j>>2]=c[d>>2];c[h>>2]=c[f>>2];c[g>>2]=0-(c[f>>2]|0);if(!(c[f>>2]|0)){c[e>>2]=c[d>>2];j=c[e>>2]|0;i=k;return j|0}d=c[j>>2]|0;if((c[f>>2]|0)<0){c[e>>2]=d<<c[g>>2]|(c[j>>2]|0)>>>(32-(c[g>>2]|0)|0);j=c[e>>2]|0;i=k;return j|0}else{c[e>>2]=d<<32-(c[h>>2]|0)|(c[j>>2]|0)>>>(c[h>>2]|0);j=c[e>>2]|0;i=k;return j|0}return 0}function Ig(d,e){d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;r=i;i=i+48|0;f=r+40|0;g=r+36|0;k=r+32|0;l=r+28|0;o=r+24|0;m=r+20|0;q=r+16|0;n=r+12|0;j=r+8|0;h=r+4|0;p=r;c[f>>2]=d;c[g>>2]=e;c[p>>2]=0;c[o>>2]=0;while(1){if((c[o>>2]|0)>=2)break;c[j>>2]=2147483647;c[k>>2]=0;a:while(1){if((c[k>>2]|0)>=15)break;c[m>>2]=b[24526+(c[k>>2]<<1)>>1];c[q>>2]=(((b[24526+((c[k>>2]|0)+1<<1)>>1]|0)-(c[m>>2]|0)>>16)*6554|0)+(((b[24526+((c[k>>2]|0)+1<<1)>>1]|0)-(c[m>>2]|0)&65535)*6554>>16);c[l>>2]=0;while(1){if((c[l>>2]|0)>=5)break;c[n>>2]=(c[m>>2]|0)+(_((c[q>>2]&65535)<<16>>16,((c[l>>2]<<1)+1&65535)<<16>>16)|0);d=(c[(c[f>>2]|0)+(c[o>>2]<<2)>>2]|0)-(c[n>>2]|0)|0;c[h>>2]=((c[(c[f>>2]|0)+(c[o>>2]<<2)>>2]|0)-(c[n>>2]|0)|0)>0?d:0-d|0;if((c[h>>2]|0)>=(c[j>>2]|0))break a;c[j>>2]=c[h>>2];c[p>>2]=c[n>>2];a[(c[g>>2]|0)+((c[o>>2]|0)*3|0)>>0]=c[k>>2];a[(c[g>>2]|0)+((c[o>>2]|0)*3|0)+1>>0]=c[l>>2];c[l>>2]=(c[l>>2]|0)+1}c[k>>2]=(c[k>>2]|0)+1}a[(c[g>>2]|0)+((c[o>>2]|0)*3|0)+2>>0]=(a[(c[g>>2]|0)+((c[o>>2]|0)*3|0)>>0]|0)/3|0;d=(c[g>>2]|0)+((c[o>>2]|0)*3|0)|0;a[d>>0]=(a[d>>0]|0)-((a[(c[g>>2]|0)+((c[o>>2]|0)*3|0)+2>>0]|0)*3|0);c[(c[f>>2]|0)+(c[o>>2]<<2)>>2]=c[p>>2];c[o>>2]=(c[o>>2]|0)+1}q=c[f>>2]|0;c[q>>2]=(c[q>>2]|0)-(c[(c[f>>2]|0)+4>>2]|0);i=r;return}function Jg(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;p=i;i=i+48|0;f=p+32|0;h=p+28|0;q=p+24|0;j=p+20|0;o=p+16|0;n=p+12|0;m=p+8|0;k=p+4|0;l=p;c[f>>2]=a;c[h>>2]=b;c[q>>2]=d;c[j>>2]=e;g[n>>2]=3.1415927410125732/+((c[j>>2]|0)+1|0);g[m>>2]=2.0-+g[n>>2]*+g[n>>2];if((c[q>>2]|0)<2){g[k>>2]=0.0;g[l>>2]=+g[n>>2]}else{g[k>>2]=1.0;g[l>>2]=+g[m>>2]*.5}c[o>>2]=0;while(1){if((c[o>>2]|0)>=(c[j>>2]|0))break;g[(c[f>>2]|0)+((c[o>>2]|0)+0<<2)>>2]=+g[(c[h>>2]|0)+((c[o>>2]|0)+0<<2)>>2]*.5*(+g[k>>2]+ +g[l>>2]);g[(c[f>>2]|0)+((c[o>>2]|0)+1<<2)>>2]=+g[(c[h>>2]|0)+((c[o>>2]|0)+1<<2)>>2]*+g[l>>2];g[k>>2]=+g[m>>2]*+g[l>>2]-+g[k>>2];g[(c[f>>2]|0)+((c[o>>2]|0)+2<<2)>>2]=+g[(c[h>>2]|0)+((c[o>>2]|0)+2<<2)>>2]*.5*(+g[l>>2]+ +g[k>>2]);g[(c[f>>2]|0)+((c[o>>2]|0)+3<<2)>>2]=+g[(c[h>>2]|0)+((c[o>>2]|0)+3<<2)>>2]*+g[k>>2];g[l>>2]=+g[m>>2]*+g[k>>2]-+g[l>>2];c[o>>2]=(c[o>>2]|0)+4}i=p;return}function Kg(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0.0;o=i;i=i+32|0;p=o+24|0;h=o+20|0;j=o+16|0;k=o+12|0;l=o+8|0;m=o+4|0;n=o;c[p>>2]=a;c[h>>2]=b;c[j>>2]=d;c[k>>2]=e;c[l>>2]=f;c[n>>2]=(c[p>>2]|0)+((c[k>>2]|0)-1<<2);c[m>>2]=0;while(1){if((c[m>>2]|0)>=(c[k>>2]|0))break;q=+Hh(c[n>>2]|0,c[h>>2]|0,c[j>>2]|0);g[(c[l>>2]|0)+(c[m>>2]<<2)>>2]=q;c[n>>2]=(c[n>>2]|0)+-4;c[m>>2]=(c[m>>2]|0)+1}i=o;return}function Lg(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;r=i;i=i+48|0;f=r+36|0;j=r+32|0;k=r+28|0;l=r+24|0;n=r+20|0;o=r+16|0;m=r;p=r+12|0;q=r+8|0;c[f>>2]=a;c[j>>2]=b;c[k>>2]=d;c[l>>2]=e;c[p>>2]=(c[f>>2]|0)+((c[k>>2]|0)-1<<2);h[m>>3]=+Gh(c[p>>2]|0,c[j>>2]|0);g[(c[l>>2]|0)+(0<<2)>>2]=+h[m>>3];c[n>>2]=1;while(1){if((c[n>>2]|0)>=(c[k>>2]|0))break;h[m>>3]=+h[m>>3]+(+g[(c[p>>2]|0)+(0-(c[n>>2]|0)<<2)>>2]*+g[(c[p>>2]|0)+(0-(c[n>>2]|0)<<2)>>2]-+g[(c[p>>2]|0)+((c[j>>2]|0)-(c[n>>2]|0)<<2)>>2]*+g[(c[p>>2]|0)+((c[j>>2]|0)-(c[n>>2]|0)<<2)>>2]);a=_(c[n>>2]|0,c[k>>2]|0)|0;g[(c[l>>2]|0)+(a+(c[n>>2]|0)<<2)>>2]=+h[m>>3];c[n>>2]=(c[n>>2]|0)+1}c[q>>2]=(c[f>>2]|0)+((c[k>>2]|0)-2<<2);c[o>>2]=1;while(1){if((c[o>>2]|0)>=(c[k>>2]|0))break;h[m>>3]=+Hh(c[p>>2]|0,c[q>>2]|0,c[j>>2]|0);g[(c[l>>2]|0)+((_(c[o>>2]|0,c[k>>2]|0)|0)+0<<2)>>2]=+h[m>>3];g[(c[l>>2]|0)+(0+(c[o>>2]|0)<<2)>>2]=+h[m>>3];c[n>>2]=1;while(1){if((c[n>>2]|0)>=((c[k>>2]|0)-(c[o>>2]|0)|0))break;h[m>>3]=+h[m>>3]+(+g[(c[p>>2]|0)+(0-(c[n>>2]|0)<<2)>>2]*+g[(c[q>>2]|0)+(0-(c[n>>2]|0)<<2)>>2]-+g[(c[p>>2]|0)+((c[j>>2]|0)-(c[n>>2]|0)<<2)>>2]*+g[(c[q>>2]|0)+((c[j>>2]|0)-(c[n>>2]|0)<<2)>>2]);a=_((c[o>>2]|0)+(c[n>>2]|0)|0,c[k>>2]|0)|0;g[(c[l>>2]|0)+(a+(c[n>>2]|0)<<2)>>2]=+h[m>>3];a=_(c[n>>2]|0,c[k>>2]|0)|0;g[(c[l>>2]|0)+(a+((c[o>>2]|0)+(c[n>>2]|0))<<2)>>2]=+h[m>>3];c[n>>2]=(c[n>>2]|0)+1}c[q>>2]=(c[q>>2]|0)+-4;c[o>>2]=(c[o>>2]|0)+1}i=r;return}function Mg(b){b=b|0;var d=0,e=0;e=i;i=i+16|0;d=e;c[d>>2]=b;We(c[d>>2]|0,(c[d>>2]|0)+5128+2|0)|0;b=c[d>>2]|0;if((c[(c[d>>2]|0)+4556>>2]|0)>=13){c[b+6116>>2]=0;c[(c[d>>2]|0)+6112>>2]=0;a[(c[d>>2]|0)+4768+29>>0]=1;a[(c[d>>2]|0)+4752+(c[(c[d>>2]|0)+5780>>2]|0)>>0]=1;i=e;return}a[b+4768+29>>0]=0;b=(c[d>>2]|0)+6116|0;c[b>>2]=(c[b>>2]|0)+1;b=c[d>>2]|0;if((c[(c[d>>2]|0)+6116>>2]|0)>=10){if((c[b+6116>>2]|0)>30){c[(c[d>>2]|0)+6116>>2]=10;c[(c[d>>2]|0)+6112>>2]=0}}else c[b+6112>>2]=0;a[(c[d>>2]|0)+4752+(c[(c[d>>2]|0)+5780>>2]|0)>>0]=0;i=e;return}function Ng(d,e,f,h,j,k){d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;var l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0;X=i;i=i+15152|0;o=X+13856|0;p=X+13852|0;q=X+13848|0;r=X+13844|0;s=X+13840|0;t=X+13836|0;u=X+13832|0;Q=X+12920|0;I=X+12916|0;J=X+12912|0;K=X+12908|0;A=X+12904|0;z=X+12900|0;P=X+12896|0;n=X+12892|0;m=X+12888|0;W=X+11608|0;l=X+8920|0;T=X+8872|0;U=X+8824|0;R=X+4444|0;S=X+64|0;V=X+60|0;L=X+56|0;M=X+52|0;N=X+48|0;C=X+44|0;D=X+40|0;F=X+36|0;G=X+32|0;H=X+28|0;B=X+13862|0;x=X+13860|0;y=X+24|0;v=X+15139|0;O=X+8|0;w=X+13864|0;E=X;c[p>>2]=d;c[q>>2]=e;c[r>>2]=f;c[s>>2]=h;c[t>>2]=j;c[u>>2]=k;c[P>>2]=0;c[D>>2]=0;c[C>>2]=0;c[N>>2]=0;c[M>>2]=0;a[v>>0]=0;f=(c[p>>2]|0)+4644|0;k=c[f>>2]|0;c[f>>2]=k+1;a[(c[p>>2]|0)+4768+34>>0]=k&3;c[n>>2]=(c[p>>2]|0)+9356+(c[(c[p>>2]|0)+4616>>2]<<2);c[m>>2]=l+(c[(c[p>>2]|0)+4616>>2]<<2);ee((c[p>>2]|0)+16|0,(c[p>>2]|0)+5128+2|0,c[(c[p>>2]|0)+4608>>2]|0);Og((c[n>>2]|0)+((c[(c[p>>2]|0)+4600>>2]|0)*5<<2)|0,(c[p>>2]|0)+5128+2|0,c[(c[p>>2]|0)+4608>>2]|0);c[I>>2]=0;while(1){if((c[I>>2]|0)>=8)break;k=((c[(c[p>>2]|0)+4600>>2]|0)*5|0)+(_(c[I>>2]|0,c[(c[p>>2]|0)+4608>>2]>>3)|0)|0;k=(c[n>>2]|0)+(k<<2)|0;g[k>>2]=+g[k>>2]+ +(1-(c[I>>2]&2)|0)*9.999999974752427e-07;c[I>>2]=(c[I>>2]|0)+1}a:do if(!(c[(c[p>>2]|0)+4712>>2]|0)){Xg(c[p>>2]|0,Q,l,c[n>>2]|0,c[(c[p>>2]|0)+5124>>2]|0);fh(c[p>>2]|0,Q,c[m>>2]|0,c[n>>2]|0);Yg(c[p>>2]|0,Q,l,c[n>>2]|0,c[s>>2]|0);nh(c[p>>2]|0,Q,c[s>>2]|0);kh(c[p>>2]|0,Q,W,c[n>>2]|0);Pg(c[p>>2]|0,Q,W,c[s>>2]|0);c[K>>2]=6;b[B>>1]=256;c[z>>2]=0;c[A>>2]=0;c[F>>2]=ce((c[p>>2]|0)+4768|0,c[(c[p>>2]|0)+4604>>2]|0)|0;c[G>>2]=-1;c[H>>2]=-1;h=T;j=c[r>>2]|0;e=h+48|0;do{c[h>>2]=c[j>>2];h=h+4|0;j=j+4|0}while((h|0)<(e|0));pj(R|0,(c[p>>2]|0)+144|0,4380)|0;c[V>>2]=a[(c[p>>2]|0)+4768+34>>0];b[x>>1]=b[(c[p>>2]|0)+5804>>1]|0;c[y>>2]=c[(c[p>>2]|0)+5800>>2];c[J>>2]=0;while(1){do if((c[F>>2]|0)!=(c[G>>2]|0)){if((c[F>>2]|0)==(c[H>>2]|0)){c[L>>2]=c[N>>2];break}if((c[J>>2]|0)>0){h=c[r>>2]|0;j=T;e=h+48|0;do{c[h>>2]=c[j>>2];h=h+4|0;j=j+4|0}while((h|0)<(e|0));pj((c[p>>2]|0)+144|0,R|0,4380)|0;a[(c[p>>2]|0)+4768+34>>0]=c[V>>2];b[(c[p>>2]|0)+5804>>1]=b[x>>1]|0;c[(c[p>>2]|0)+5800>>2]=c[y>>2]}Bh(c[p>>2]|0,Q,(c[p>>2]|0)+4768|0,(c[p>>2]|0)+144|0,(c[p>>2]|0)+4804|0,W);Xd(c[p>>2]|0,c[r>>2]|0,c[(c[p>>2]|0)+5780>>2]|0,0,c[s>>2]|0);Yd(c[r>>2]|0,a[(c[p>>2]|0)+4768+29>>0]|0,a[(c[p>>2]|0)+4768+30>>0]|0,(c[p>>2]|0)+4804|0,c[(c[p>>2]|0)+4608>>2]|0);c[L>>2]=Rg(c[r>>2]|0)|0;if((c[u>>2]|0)==0&(c[J>>2]|0)==0?(c[L>>2]|0)<=(c[t>>2]|0):0)break a}else c[L>>2]=c[M>>2];while(0);if((c[J>>2]|0)==(c[K>>2]|0))break;do if((c[L>>2]|0)>(c[t>>2]|0))if((c[z>>2]|0)==0&(c[J>>2]|0)>=2){n=Q+852|0;g[n>>2]=+g[n>>2]*1.5;c[A>>2]=0;c[H>>2]=-1;break}else{c[A>>2]=1;c[N>>2]=c[L>>2];c[D>>2]=b[B>>1];c[H>>2]=c[F>>2];break}else{if((c[L>>2]|0)>=((c[t>>2]|0)-5|0))break a;c[z>>2]=1;c[M>>2]=c[L>>2];c[C>>2]=b[B>>1];if((c[F>>2]|0)!=(c[G>>2]|0)){c[G>>2]=c[F>>2];h=U;j=c[r>>2]|0;e=h+48|0;do{c[h>>2]=c[j>>2];h=h+4|0;j=j+4|0}while((h|0)<(e|0));pj(w|0,c[c[r>>2]>>2]|0,c[(c[r>>2]|0)+24>>2]|0)|0;pj(S|0,(c[p>>2]|0)+144|0,4380)|0;a[v>>0]=a[(c[p>>2]|0)+7200>>0]|0}}while(0);do if(c[z>>2]&c[A>>2]){n=_((c[D>>2]|0)-(c[C>>2]|0)|0,(c[t>>2]|0)-(c[M>>2]|0)|0)|0;b[B>>1]=(c[C>>2]|0)+((n|0)/((c[N>>2]|0)-(c[M>>2]|0)|0)|0);if((b[B>>1]|0)>((c[C>>2]|0)+((c[D>>2]|0)-(c[C>>2]|0)>>2)|0)){b[B>>1]=(c[C>>2]|0)+((c[D>>2]|0)-(c[C>>2]|0)>>2);break}if((b[B>>1]|0)<((c[D>>2]|0)-((c[D>>2]|0)-(c[C>>2]|0)>>2)|0))b[B>>1]=(c[D>>2]|0)-((c[D>>2]|0)-(c[C>>2]|0)>>2)}else{c[E>>2]=Wf((((c[L>>2]|0)-(c[t>>2]|0)<<7|0)/(c[(c[p>>2]|0)+4608>>2]|0)|0)+2048|0)|0;c[E>>2]=Sg(c[E>>2]|0,131072)|0;if((c[L>>2]|0)>(c[t>>2]|0))c[E>>2]=Tg(c[E>>2]|0,85197)|0;n=_(c[E>>2]>>16,b[B>>1]|0)|0;b[B>>1]=n+((_(c[E>>2]&65535,b[B>>1]|0)|0)>>16)}while(0);c[I>>2]=0;while(1){if((c[I>>2]|0)>=(c[(c[p>>2]|0)+4604>>2]|0))break;n=_(c[Q+892+(c[I>>2]<<2)>>2]>>16,b[B>>1]|0)|0;if((n+((_(c[Q+892+(c[I>>2]<<2)>>2]&65535,b[B>>1]|0)|0)>>16)|0)<=8388607){n=_(c[Q+892+(c[I>>2]<<2)>>2]>>16,b[B>>1]|0)|0;if((n+((_(c[Q+892+(c[I>>2]<<2)>>2]&65535,b[B>>1]|0)|0)>>16)|0)<-8388608)h=-8388608;else{h=_(c[Q+892+(c[I>>2]<<2)>>2]>>16,b[B>>1]|0)|0;h=h+((_(c[Q+892+(c[I>>2]<<2)>>2]&65535,b[B>>1]|0)|0)>>16)|0}}else h=8388607;c[O+(c[I>>2]<<2)>>2]=h<<8;c[I>>2]=(c[I>>2]|0)+1}a[(c[p>>2]|0)+7200>>0]=a[Q+908>>0]|0;_d((c[p>>2]|0)+4768|0,O,(c[p>>2]|0)+7200|0,(c[s>>2]|0)==2&1,c[(c[p>>2]|0)+4604>>2]|0);c[F>>2]=ce((c[p>>2]|0)+4768|0,c[(c[p>>2]|0)+4604>>2]|0)|0;c[I>>2]=0;while(1){if((c[I>>2]|0)>=(c[(c[p>>2]|0)+4604>>2]|0))break;g[Q+(c[I>>2]<<2)>>2]=+(c[O+(c[I>>2]<<2)>>2]|0)/65536.0;c[I>>2]=(c[I>>2]|0)+1}c[J>>2]=(c[J>>2]|0)+1}if(c[z>>2]|0){if((c[F>>2]|0)!=(c[G>>2]|0)?(c[L>>2]|0)<=(c[t>>2]|0):0)break;h=c[r>>2]|0;j=U;e=h+48|0;do{c[h>>2]=c[j>>2];h=h+4|0;j=j+4|0}while((h|0)<(e|0));pj(c[c[r>>2]>>2]|0,w|0,c[U+24>>2]|0)|0;pj((c[p>>2]|0)+144|0,S|0,4380)|0;a[(c[p>>2]|0)+7200>>0]=a[v>>0]|0}}while(0);qj((c[p>>2]|0)+9356|0,(c[p>>2]|0)+9356+(c[(c[p>>2]|0)+4608>>2]<<2)|0,(c[(c[p>>2]|0)+4616>>2]|0)+((c[(c[p>>2]|0)+4600>>2]|0)*5|0)<<2|0)|0;if(c[(c[p>>2]|0)+4712>>2]|0){c[c[q>>2]>>2]=0;c[o>>2]=c[P>>2];W=c[o>>2]|0;i=X;return W|0}else{c[(c[p>>2]|0)+4568>>2]=c[Q+228+((c[(c[p>>2]|0)+4604>>2]|0)-1<<2)>>2];a[(c[p>>2]|0)+4565>>0]=a[(c[p>>2]|0)+4768+29>>0]|0;c[(c[p>>2]|0)+4696>>2]=0;W=(Rg(c[r>>2]|0)|0)+7>>3;c[c[q>>2]>>2]=W;c[o>>2]=c[P>>2];W=c[o>>2]|0;i=X;return W|0}return 0}function Og(a,d,e){a=a|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0;k=i;i=i+16|0;f=k+12|0;h=k+8|0;l=k+4|0;j=k;c[f>>2]=a;c[h>>2]=d;c[l>>2]=e;c[j>>2]=(c[l>>2]|0)-1;while(1){if((c[j>>2]|0)<0)break;g[(c[f>>2]|0)+(c[j>>2]<<2)>>2]=+(b[(c[h>>2]|0)+(c[j>>2]<<1)>>1]|0);c[j>>2]=(c[j>>2]|0)+-1}i=k;return}function Pg(d,e,f,h){d=d|0;e=e|0;f=f|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;s=i;i=i+4448|0;j=s+4432|0;k=s+4428|0;l=s+4424|0;m=s+4420|0;p=s+4416|0;n=s+4400|0;o=s+4384|0;q=s+4380|0;r=s;c[j>>2]=d;c[k>>2]=e;c[l>>2]=f;c[m>>2]=h;c[q>>2]=(c[j>>2]|0)+6132+((c[(c[j>>2]|0)+5780>>2]|0)*36|0);if(!(c[(c[j>>2]|0)+6124>>2]|0)){i=s;return}if((c[(c[j>>2]|0)+4556>>2]|0)<=77){i=s;return}c[(c[j>>2]|0)+4756+(c[(c[j>>2]|0)+5780>>2]<<2)>>2]=1;pj(r|0,(c[j>>2]|0)+144|0,4380)|0;h=c[q>>2]|0;d=(c[j>>2]|0)+4768|0;e=h+36|0;do{b[h>>1]=b[d>>1]|0;h=h+2|0;d=d+2|0}while((h|0)<(e|0));pj(o|0,c[k>>2]|0,c[(c[j>>2]|0)+4604>>2]<<2|0)|0;if(!((c[(c[j>>2]|0)+5780>>2]|0)!=0?(c[(c[j>>2]|0)+4756+((c[(c[j>>2]|0)+5780>>2]|0)-1<<2)>>2]|0)!=0:0)){a[(c[j>>2]|0)+4564>>0]=a[(c[j>>2]|0)+7200>>0]|0;f=c[q>>2]|0;a[f>>0]=(a[f>>0]|0)+(c[(c[j>>2]|0)+6128>>2]|0);f=(Qg(a[c[q>>2]>>0]|0,63)|0)&255;a[c[q>>2]>>0]=f}ae(n,c[q>>2]|0,(c[j>>2]|0)+4564|0,(c[m>>2]|0)==2&1,c[(c[j>>2]|0)+4604>>2]|0);c[p>>2]=0;while(1){if((c[p>>2]|0)>=(c[(c[j>>2]|0)+4604>>2]|0))break;g[(c[k>>2]|0)+(c[p>>2]<<2)>>2]=+(c[n+(c[p>>2]<<2)>>2]|0)*.0000152587890625;c[p>>2]=(c[p>>2]|0)+1}Bh(c[j>>2]|0,c[k>>2]|0,c[q>>2]|0,r,(c[j>>2]|0)+6240+((c[(c[j>>2]|0)+5780>>2]|0)*320|0)|0,c[l>>2]|0);pj(c[k>>2]|0,o|0,c[(c[j>>2]|0)+4604>>2]<<2|0)|0;i=s;return}function Qg(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)<(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function Rg(a){a=a|0;var b=0,d=0;b=i;i=i+16|0;d=b;c[d>>2]=a;a=(c[(c[d>>2]|0)+20>>2]|0)-(32-(aa(c[(c[d>>2]|0)+28>>2]|0)|0))|0;i=b;return a|0}function Sg(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)<(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function Tg(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)>(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function Ug(b,d,e,f){b=b|0;d=d|0;e=e|0;f=+f;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;v=i;i=i+1744|0;j=v+1700|0;k=v+1696|0;l=v+1692|0;h=v+1688|0;q=v+1684|0;u=v+1680|0;o=v+1616|0;r=v+1608|0;s=v+1604|0;t=v+1600|0;n=v+1704|0;p=v+1536|0;m=v;c[j>>2]=b;c[k>>2]=d;c[l>>2]=e;g[h>>2]=f;c[u>>2]=(c[(c[j>>2]|0)+4612>>2]|0)+(c[(c[j>>2]|0)+4664>>2]|0);a[(c[j>>2]|0)+4768+31>>0]=4;g[r>>2]=+Eh(o,c[l>>2]|0,+g[h>>2],c[u>>2]|0,c[(c[j>>2]|0)+4604>>2]|0,c[(c[j>>2]|0)+4664>>2]|0);a:do if((c[(c[j>>2]|0)+4656>>2]|0?(c[(c[j>>2]|0)+4696>>2]|0)==0:0)?(c[(c[j>>2]|0)+4604>>2]|0)==4:0){f=+Eh(p,(c[l>>2]|0)+(c[u>>2]<<1<<2)|0,+g[h>>2],c[u>>2]|0,2,c[(c[j>>2]|0)+4664>>2]|0);g[r>>2]=+g[r>>2]-f;xh(c[k>>2]|0,p,c[(c[j>>2]|0)+4664>>2]|0);g[s>>2]=3402823466385288598117041.0e14;c[q>>2]=3;while(1){if((c[q>>2]|0)<0)break a;de(n,(c[j>>2]|0)+4524|0,c[k>>2]|0,c[q>>2]|0,c[(c[j>>2]|0)+4664>>2]|0);zh(p,n,c[(c[j>>2]|0)+4664>>2]|0);Zg(m,p,c[l>>2]|0,c[u>>2]<<1,c[(c[j>>2]|0)+4664>>2]|0);f=+Gh(m+(c[(c[j>>2]|0)+4664>>2]<<2)|0,(c[u>>2]|0)-(c[(c[j>>2]|0)+4664>>2]|0)|0);g[t>>2]=f+ +Gh(m+(c[(c[j>>2]|0)+4664>>2]<<2)+(c[u>>2]<<2)|0,(c[u>>2]|0)-(c[(c[j>>2]|0)+4664>>2]|0)|0);f=+g[t>>2];if(!(+g[t>>2]<+g[r>>2])){if(f>+g[s>>2])break a}else{g[r>>2]=f;a[(c[j>>2]|0)+4768+31>>0]=c[q>>2]}g[s>>2]=+g[t>>2];c[q>>2]=(c[q>>2]|0)+-1}}while(0);if((a[(c[j>>2]|0)+4768+31>>0]|0)!=4){i=v;return}xh(c[k>>2]|0,o,c[(c[j>>2]|0)+4664>>2]|0);i=v;return}function Vg(a,b,d,e,f,h,j,k,l){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;var m=0.0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0;L=i;i=i+192|0;n=L+188|0;O=L+184|0;o=L+180|0;N=L+176|0;p=L+172|0;q=L+168|0;r=L+164|0;B=L+160|0;M=L+156|0;G=L+152|0;H=L+148|0;C=L+144|0;J=L+140|0;v=L+136|0;t=L+132|0;s=L+128|0;D=L+112|0;I=L+104|0;F=L+100|0;E=L+80|0;K=L+64|0;x=L+48|0;z=L+44|0;u=L+24|0;A=L+8|0;y=L+4|0;w=L;c[n>>2]=a;c[O>>2]=b;c[o>>2]=d;c[N>>2]=e;c[p>>2]=f;c[q>>2]=h;c[r>>2]=j;c[B>>2]=k;c[M>>2]=l;c[C>>2]=c[n>>2];c[v>>2]=c[O>>2];c[y>>2]=(c[N>>2]|0)+(c[M>>2]<<2);c[H>>2]=0;while(1){if((c[H>>2]|0)>=(c[B>>2]|0))break;c[w>>2]=(c[y>>2]|0)+(0-((c[(c[p>>2]|0)+(c[H>>2]<<2)>>2]|0)+2)<<2);Lg(c[w>>2]|0,c[r>>2]|0,5,c[v>>2]|0);Kg(c[w>>2]|0,c[y>>2]|0,c[r>>2]|0,5,u);m=+Gh(c[y>>2]|0,c[r>>2]|0);g[A+(c[H>>2]<<2)>>2]=m;g[z>>2]=+g[A+(c[H>>2]<<2)>>2]+1.0+ +g[c[v>>2]>>2]+ +g[(c[v>>2]|0)+96>>2];g[z>>2]=+g[z>>2]*.01666666753590107;ph(c[v>>2]|0,A+(c[H>>2]<<2)|0,+g[z>>2],5);sh(c[v>>2]|0,5,u,c[C>>2]|0);m=+qh(c[C>>2]|0,c[v>>2]|0,u,+g[A+(c[H>>2]<<2)>>2],5);g[x+(c[H>>2]<<2)>>2]=m;g[J>>2]=+g[(c[q>>2]|0)+(c[H>>2]<<2)>>2]/(+g[x+(c[H>>2]<<2)>>2]*+g[(c[q>>2]|0)+(c[H>>2]<<2)>>2]+ +(c[r>>2]|0)*.009999999776482582);Uh(c[v>>2]|0,+g[J>>2],25);g[K+(c[H>>2]<<2)>>2]=+g[(c[v>>2]|0)+48>>2];c[y>>2]=(c[y>>2]|0)+(c[r>>2]<<2);c[C>>2]=(c[C>>2]|0)+20;c[v>>2]=(c[v>>2]|0)+100;c[H>>2]=(c[H>>2]|0)+1}if(c[o>>2]|0){g[s>>2]=9.999999974752427e-07;g[t>>2]=0.0;c[H>>2]=0;while(1){if((c[H>>2]|0)>=(c[B>>2]|0))break;g[t>>2]=+g[t>>2]+ +g[A+(c[H>>2]<<2)>>2]*+g[(c[q>>2]|0)+(c[H>>2]<<2)>>2];g[s>>2]=+g[s>>2]+ +g[x+(c[H>>2]<<2)>>2]*+g[(c[q>>2]|0)+(c[H>>2]<<2)>>2];c[H>>2]=(c[H>>2]|0)+1}m=+Wg(+g[t>>2]/+g[s>>2])*3.0;g[c[o>>2]>>2]=m}c[C>>2]=c[n>>2];c[H>>2]=0;while(1){if((c[H>>2]|0)>=(c[B>>2]|0))break;g[D+(c[H>>2]<<2)>>2]=0.0;c[G>>2]=0;while(1){if((c[G>>2]|0)>=5)break;O=D+(c[H>>2]<<2)|0;g[O>>2]=+g[O>>2]+ +g[(c[C>>2]|0)+(c[G>>2]<<2)>>2];c[G>>2]=(c[G>>2]|0)+1}c[C>>2]=(c[C>>2]|0)+20;c[H>>2]=(c[H>>2]|0)+1}g[J>>2]=1.0000000474974513e-03;c[H>>2]=0;while(1){if((c[H>>2]|0)>=(c[B>>2]|0))break;g[J>>2]=+g[J>>2]+ +g[K+(c[H>>2]<<2)>>2];c[H>>2]=(c[H>>2]|0)+1}g[I>>2]=0.0;c[H>>2]=0;while(1){if((c[H>>2]|0)>=(c[B>>2]|0))break;g[I>>2]=+g[I>>2]+ +g[D+(c[H>>2]<<2)>>2]*+g[K+(c[H>>2]<<2)>>2];c[H>>2]=(c[H>>2]|0)+1}g[I>>2]=+g[I>>2]/+g[J>>2];c[C>>2]=c[n>>2];c[H>>2]=0;while(1){if((c[H>>2]|0)>=(c[B>>2]|0))break;g[F>>2]=.10000000149011612/(+g[K+(c[H>>2]<<2)>>2]+.10000000149011612)*(+g[I>>2]-+g[D+(c[H>>2]<<2)>>2]);g[J>>2]=0.0;c[G>>2]=0;while(1){if((c[G>>2]|0)>=5)break;if(+g[(c[C>>2]|0)+(c[G>>2]<<2)>>2]>.10000000149011612)m=+g[(c[C>>2]|0)+(c[G>>2]<<2)>>2];else m=.10000000149011612;g[E+(c[G>>2]<<2)>>2]=m;g[J>>2]=+g[J>>2]+ +g[E+(c[G>>2]<<2)>>2];c[G>>2]=(c[G>>2]|0)+1}g[J>>2]=+g[F>>2]/+g[J>>2];c[G>>2]=0;while(1){if((c[G>>2]|0)>=5)break;g[(c[C>>2]|0)+(c[G>>2]<<2)>>2]=+g[(c[C>>2]|0)+(c[G>>2]<<2)>>2]+ +g[E+(c[G>>2]<<2)>>2]*+g[J>>2];c[G>>2]=(c[G>>2]|0)+1}c[C>>2]=(c[C>>2]|0)+20;c[H>>2]=(c[H>>2]|0)+1}i=L;return}function Wg(a){a=+a;var b=0,c=0;b=i;i=i+16|0;c=b;h[c>>3]=a;a=+hj(+h[c>>3])*3.32192809488736;i=b;return +a}function Xg(d,e,f,h,j){d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0;p=i;i=i+1792|0;k=p+1776|0;l=p+1772|0;m=p+1768|0;z=p+1764|0;n=p+1760|0;q=p+1756|0;o=p+1752|0;u=p+1748|0;x=p+1744|0;r=p+1740|0;v=p+1672|0;s=p+1608|0;t=p+1544|0;w=p+8|0;y=p;c[k>>2]=d;c[l>>2]=e;c[m>>2]=f;c[z>>2]=h;c[n>>2]=j;c[q>>2]=(c[(c[k>>2]|0)+4620>>2]|0)+(c[(c[k>>2]|0)+4608>>2]|0)+(c[(c[k>>2]|0)+4616>>2]|0);c[r>>2]=(c[z>>2]|0)+(0-(c[(c[k>>2]|0)+4616>>2]|0)<<2);c[x>>2]=(c[r>>2]|0)+(c[q>>2]<<2)+(0-(c[(c[k>>2]|0)+4572>>2]|0)<<2);c[y>>2]=w;Jg(c[y>>2]|0,c[x>>2]|0,1,c[(c[k>>2]|0)+4620>>2]|0);c[y>>2]=(c[y>>2]|0)+(c[(c[k>>2]|0)+4620>>2]<<2);c[x>>2]=(c[x>>2]|0)+(c[(c[k>>2]|0)+4620>>2]<<2);pj(c[y>>2]|0,c[x>>2]|0,(c[(c[k>>2]|0)+4572>>2]|0)-(c[(c[k>>2]|0)+4620>>2]<<1)<<2|0)|0;c[y>>2]=(c[y>>2]|0)+((c[(c[k>>2]|0)+4572>>2]|0)-(c[(c[k>>2]|0)+4620>>2]<<1)<<2);c[x>>2]=(c[x>>2]|0)+((c[(c[k>>2]|0)+4572>>2]|0)-(c[(c[k>>2]|0)+4620>>2]<<1)<<2);Jg(c[y>>2]|0,c[x>>2]|0,2,c[(c[k>>2]|0)+4620>>2]|0);Dh(v,w,c[(c[k>>2]|0)+4572>>2]|0,(c[(c[k>>2]|0)+4672>>2]|0)+1|0);g[v>>2]=+g[v>>2]+(+g[v>>2]*1.0000000474974513e-03+1.0);g[u>>2]=+Vh(t,v,c[(c[k>>2]|0)+4672>>2]|0);g[(c[l>>2]|0)+868>>2]=+g[v>>2]/(+g[u>>2]>1.0?+g[u>>2]:1.0);Ih(s,t,c[(c[k>>2]|0)+4672>>2]|0);Fh(s,c[(c[k>>2]|0)+4672>>2]|0,.9900000095367432);Zg(c[m>>2]|0,s,c[r>>2]|0,c[q>>2]|0,c[(c[k>>2]|0)+4672>>2]|0);if(a[(c[k>>2]|0)+4768+29>>0]|0?(c[(c[k>>2]|0)+4696>>2]|0)==0:0){g[o>>2]=.6000000238418579;g[o>>2]=+g[o>>2]-+(c[(c[k>>2]|0)+4672>>2]|0)*.004000000189989805;g[o>>2]=+g[o>>2]-+(c[(c[k>>2]|0)+4556>>2]|0)*.10000000149011612*.00390625;g[o>>2]=+g[o>>2]-+(a[(c[k>>2]|0)+4565>>0]>>1|0)*.15000000596046448;g[o>>2]=+g[o>>2]-+(c[(c[k>>2]|0)+4744>>2]|0)*.10000000149011612*.000030517578125;z=(Lh(c[m>>2]|0,(c[l>>2]|0)+228|0,(c[k>>2]|0)+4768+26|0,(c[k>>2]|0)+4768+28|0,(c[k>>2]|0)+12236|0,c[(c[k>>2]|0)+4568>>2]|0,+(c[(c[k>>2]|0)+4676>>2]|0)/65536.0,+g[o>>2],c[(c[k>>2]|0)+4600>>2]|0,c[(c[k>>2]|0)+4668>>2]|0,c[(c[k>>2]|0)+4604>>2]|0,c[n>>2]|0)|0)==0;k=(c[k>>2]|0)+4768+29|0;if(z){a[k>>0]=2;i=p;return}else{a[k>>0]=1;i=p;return}}z=(c[l>>2]|0)+228|0;c[z>>2]=0;c[z+4>>2]=0;c[z+8>>2]=0;c[z+12>>2]=0;b[(c[k>>2]|0)+4768+26>>1]=0;a[(c[k>>2]|0)+4768+28>>0]=0;g[(c[k>>2]|0)+12236>>2]=0.0;i=p;return}function Yg(d,e,f,h,j){d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0.0;y=i;i=i+2048|0;l=y+2004|0;m=y+2e3|0;n=y+1996|0;o=y+1992|0;p=y+1988|0;t=y+1984|0;r=y+1584|0;u=y+1568|0;s=y+1552|0;k=y+2008|0;x=y+1548|0;w=y+1544|0;q=y+8|0;v=y;c[l>>2]=d;c[m>>2]=e;c[n>>2]=f;c[o>>2]=h;c[p>>2]=j;c[t>>2]=0;while(1){if((c[t>>2]|0)>=(c[(c[l>>2]|0)+4604>>2]|0))break;g[u+(c[t>>2]<<2)>>2]=1.0/+g[(c[m>>2]|0)+(c[t>>2]<<2)>>2];g[s+(c[t>>2]<<2)>>2]=+g[u+(c[t>>2]<<2)>>2]*+g[u+(c[t>>2]<<2)>>2];c[t>>2]=(c[t>>2]|0)+1}if((a[(c[l>>2]|0)+4768+29>>0]|0)==2){Vg((c[m>>2]|0)+144|0,r,(c[m>>2]|0)+872|0,c[n>>2]|0,(c[m>>2]|0)+228|0,s,c[(c[l>>2]|0)+4612>>2]|0,c[(c[l>>2]|0)+4604>>2]|0,c[(c[l>>2]|0)+4616>>2]|0);Ch((c[m>>2]|0)+144|0,(c[l>>2]|0)+4768+4|0,(c[l>>2]|0)+4768+32|0,(c[l>>2]|0)+4688|0,r,c[(c[l>>2]|0)+4684>>2]|0,c[(c[l>>2]|0)+4680>>2]|0,c[(c[l>>2]|0)+4604>>2]|0,c[(c[l>>2]|0)+5124>>2]|0);eh(c[l>>2]|0,c[m>>2]|0,c[p>>2]|0);dh(q,(c[o>>2]|0)+(0-(c[(c[l>>2]|0)+4664>>2]|0)<<2)|0,(c[m>>2]|0)+144|0,(c[m>>2]|0)+228|0,u,c[(c[l>>2]|0)+4612>>2]|0,c[(c[l>>2]|0)+4604>>2]|0,c[(c[l>>2]|0)+4664>>2]|0)}else{c[x>>2]=(c[o>>2]|0)+(0-(c[(c[l>>2]|0)+4664>>2]|0)<<2);c[w>>2]=q;c[t>>2]=0;while(1){if((c[t>>2]|0)>=(c[(c[l>>2]|0)+4604>>2]|0))break;Th(c[w>>2]|0,c[x>>2]|0,+g[u+(c[t>>2]<<2)>>2],(c[(c[l>>2]|0)+4612>>2]|0)+(c[(c[l>>2]|0)+4664>>2]|0)|0);c[w>>2]=(c[w>>2]|0)+((c[(c[l>>2]|0)+4612>>2]|0)+(c[(c[l>>2]|0)+4664>>2]|0)<<2);c[x>>2]=(c[x>>2]|0)+(c[(c[l>>2]|0)+4612>>2]<<2);c[t>>2]=(c[t>>2]|0)+1}oj((c[m>>2]|0)+144|0,0,(c[(c[l>>2]|0)+4604>>2]|0)*5<<2|0)|0;g[(c[m>>2]|0)+872>>2]=0.0;c[(c[l>>2]|0)+4688>>2]=0}if(c[(c[l>>2]|0)+4696>>2]|0){g[v>>2]=.009999999776482582;h=c[l>>2]|0;z=+g[v>>2];Ug(h,k,q,z);h=c[l>>2]|0;f=c[m>>2]|0;f=f+16|0;e=c[l>>2]|0;e=e+4524|0;Ah(h,f,k,e);e=c[m>>2]|0;e=e+876|0;f=c[m>>2]|0;f=f+16|0;h=c[m>>2]|0;w=c[l>>2]|0;w=w+4612|0;w=c[w>>2]|0;x=c[l>>2]|0;x=x+4604|0;x=c[x>>2]|0;m=c[l>>2]|0;m=m+4664|0;m=c[m>>2]|0;rh(e,q,f,h,w,x,m);m=c[l>>2]|0;m=m+4524|0;l=m+32|0;do{b[m>>1]=b[k>>1]|0;m=m+2|0;k=k+2|0}while((m|0)<(l|0));i=y;return}else{g[v>>2]=+P(2.0,+(+g[(c[m>>2]|0)+872>>2]/3.0))/1.0e4;g[v>>2]=+g[v>>2]/(+g[(c[m>>2]|0)+860>>2]*.75+.25);h=c[l>>2]|0;z=+g[v>>2];Ug(h,k,q,z);h=c[l>>2]|0;f=c[m>>2]|0;f=f+16|0;e=c[l>>2]|0;e=e+4524|0;Ah(h,f,k,e);e=c[m>>2]|0;e=e+876|0;f=c[m>>2]|0;f=f+16|0;h=c[m>>2]|0;w=c[l>>2]|0;w=w+4612|0;w=c[w>>2]|0;x=c[l>>2]|0;x=x+4604|0;x=c[x>>2]|0;m=c[l>>2]|0;m=m+4664|0;m=c[m>>2]|0;rh(e,q,f,h,w,x,m);m=c[l>>2]|0;m=m+4524|0;l=m+32|0;do{b[m>>1]=b[k>>1]|0;m=m+2|0;k=k+2|0}while((m|0)<(l|0));i=y;return}}function Zg(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0;m=i;i=i+32|0;g=m+16|0;h=m+12|0;j=m+8|0;k=m+4|0;l=m;c[g>>2]=a;c[h>>2]=b;c[j>>2]=d;c[k>>2]=e;c[l>>2]=f;switch(c[l>>2]|0){case 6:{_g(c[g>>2]|0,c[h>>2]|0,c[j>>2]|0,c[k>>2]|0);break}case 8:{$g(c[g>>2]|0,c[h>>2]|0,c[j>>2]|0,c[k>>2]|0);break}case 10:{ah(c[g>>2]|0,c[h>>2]|0,c[j>>2]|0,c[k>>2]|0);break}case 12:{bh(c[g>>2]|0,c[h>>2]|0,c[j>>2]|0,c[k>>2]|0);break}case 16:{ch(c[g>>2]|0,c[h>>2]|0,c[j>>2]|0,c[k>>2]|0);break}default:{}}oj(c[g>>2]|0,0,c[l>>2]<<2|0)|0;i=m;return}function _g(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0;o=i;i=i+32|0;f=o+24|0;h=o+20|0;j=o+16|0;k=o+12|0;m=o+8|0;l=o+4|0;n=o;c[f>>2]=a;c[h>>2]=b;c[j>>2]=d;c[k>>2]=e;c[m>>2]=6;while(1){if((c[m>>2]|0)>=(c[k>>2]|0))break;c[n>>2]=(c[j>>2]|0)+((c[m>>2]|0)-1<<2);g[l>>2]=+g[c[n>>2]>>2]*+g[c[h>>2]>>2]+ +g[(c[n>>2]|0)+-4>>2]*+g[(c[h>>2]|0)+4>>2]+ +g[(c[n>>2]|0)+-8>>2]*+g[(c[h>>2]|0)+8>>2]+ +g[(c[n>>2]|0)+-12>>2]*+g[(c[h>>2]|0)+12>>2]+ +g[(c[n>>2]|0)+-16>>2]*+g[(c[h>>2]|0)+16>>2]+ +g[(c[n>>2]|0)+-20>>2]*+g[(c[h>>2]|0)+20>>2];g[(c[f>>2]|0)+(c[m>>2]<<2)>>2]=+g[(c[n>>2]|0)+4>>2]-+g[l>>2];c[m>>2]=(c[m>>2]|0)+1}i=o;return}function $g(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0;o=i;i=i+32|0;f=o+24|0;h=o+20|0;j=o+16|0;k=o+12|0;m=o+8|0;l=o+4|0;n=o;c[f>>2]=a;c[h>>2]=b;c[j>>2]=d;c[k>>2]=e;c[m>>2]=8;while(1){if((c[m>>2]|0)>=(c[k>>2]|0))break;c[n>>2]=(c[j>>2]|0)+((c[m>>2]|0)-1<<2);g[l>>2]=+g[c[n>>2]>>2]*+g[c[h>>2]>>2]+ +g[(c[n>>2]|0)+-4>>2]*+g[(c[h>>2]|0)+4>>2]+ +g[(c[n>>2]|0)+-8>>2]*+g[(c[h>>2]|0)+8>>2]+ +g[(c[n>>2]|0)+-12>>2]*+g[(c[h>>2]|0)+12>>2]+ +g[(c[n>>2]|0)+-16>>2]*+g[(c[h>>2]|0)+16>>2]+ +g[(c[n>>2]|0)+-20>>2]*+g[(c[h>>2]|0)+20>>2]+ +g[(c[n>>2]|0)+-24>>2]*+g[(c[h>>2]|0)+24>>2]+ +g[(c[n>>2]|0)+-28>>2]*+g[(c[h>>2]|0)+28>>2];g[(c[f>>2]|0)+(c[m>>2]<<2)>>2]=+g[(c[n>>2]|0)+4>>2]-+g[l>>2];c[m>>2]=(c[m>>2]|0)+1}i=o;return}function ah(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0;o=i;i=i+32|0;f=o+24|0;h=o+20|0;j=o+16|0;k=o+12|0;m=o+8|0;l=o+4|0;n=o;c[f>>2]=a;c[h>>2]=b;c[j>>2]=d;c[k>>2]=e;c[m>>2]=10;while(1){if((c[m>>2]|0)>=(c[k>>2]|0))break;c[n>>2]=(c[j>>2]|0)+((c[m>>2]|0)-1<<2);g[l>>2]=+g[c[n>>2]>>2]*+g[c[h>>2]>>2]+ +g[(c[n>>2]|0)+-4>>2]*+g[(c[h>>2]|0)+4>>2]+ +g[(c[n>>2]|0)+-8>>2]*+g[(c[h>>2]|0)+8>>2]+ +g[(c[n>>2]|0)+-12>>2]*+g[(c[h>>2]|0)+12>>2]+ +g[(c[n>>2]|0)+-16>>2]*+g[(c[h>>2]|0)+16>>2]+ +g[(c[n>>2]|0)+-20>>2]*+g[(c[h>>2]|0)+20>>2]+ +g[(c[n>>2]|0)+-24>>2]*+g[(c[h>>2]|0)+24>>2]+ +g[(c[n>>2]|0)+-28>>2]*+g[(c[h>>2]|0)+28>>2]+ +g[(c[n>>2]|0)+-32>>2]*+g[(c[h>>2]|0)+32>>2]+ +g[(c[n>>2]|0)+-36>>2]*+g[(c[h>>2]|0)+36>>2];g[(c[f>>2]|0)+(c[m>>2]<<2)>>2]=+g[(c[n>>2]|0)+4>>2]-+g[l>>2];c[m>>2]=(c[m>>2]|0)+1}i=o;return}function bh(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0;o=i;i=i+32|0;f=o+24|0;h=o+20|0;j=o+16|0;k=o+12|0;m=o+8|0;l=o+4|0;n=o;c[f>>2]=a;c[h>>2]=b;c[j>>2]=d;c[k>>2]=e;c[m>>2]=12;while(1){if((c[m>>2]|0)>=(c[k>>2]|0))break;c[n>>2]=(c[j>>2]|0)+((c[m>>2]|0)-1<<2);g[l>>2]=+g[c[n>>2]>>2]*+g[c[h>>2]>>2]+ +g[(c[n>>2]|0)+-4>>2]*+g[(c[h>>2]|0)+4>>2]+ +g[(c[n>>2]|0)+-8>>2]*+g[(c[h>>2]|0)+8>>2]+ +g[(c[n>>2]|0)+-12>>2]*+g[(c[h>>2]|0)+12>>2]+ +g[(c[n>>2]|0)+-16>>2]*+g[(c[h>>2]|0)+16>>2]+ +g[(c[n>>2]|0)+-20>>2]*+g[(c[h>>2]|0)+20>>2]+ +g[(c[n>>2]|0)+-24>>2]*+g[(c[h>>2]|0)+24>>2]+ +g[(c[n>>2]|0)+-28>>2]*+g[(c[h>>2]|0)+28>>2]+ +g[(c[n>>2]|0)+-32>>2]*+g[(c[h>>2]|0)+32>>2]+ +g[(c[n>>2]|0)+-36>>2]*+g[(c[h>>2]|0)+36>>2]+ +g[(c[n>>2]|0)+-40>>2]*+g[(c[h>>2]|0)+40>>2]+ +g[(c[n>>2]|0)+-44>>2]*+g[(c[h>>2]|0)+44>>2];g[(c[f>>2]|0)+(c[m>>2]<<2)>>2]=+g[(c[n>>2]|0)+4>>2]-+g[l>>2];c[m>>2]=(c[m>>2]|0)+1}i=o;return}function ch(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0;o=i;i=i+32|0;f=o+24|0;h=o+20|0;j=o+16|0;k=o+12|0;m=o+8|0;l=o+4|0;n=o;c[f>>2]=a;c[h>>2]=b;c[j>>2]=d;c[k>>2]=e;c[m>>2]=16;while(1){if((c[m>>2]|0)>=(c[k>>2]|0))break;c[n>>2]=(c[j>>2]|0)+((c[m>>2]|0)-1<<2);g[l>>2]=+g[c[n>>2]>>2]*+g[c[h>>2]>>2]+ +g[(c[n>>2]|0)+-4>>2]*+g[(c[h>>2]|0)+4>>2]+ +g[(c[n>>2]|0)+-8>>2]*+g[(c[h>>2]|0)+8>>2]+ +g[(c[n>>2]|0)+-12>>2]*+g[(c[h>>2]|0)+12>>2]+ +g[(c[n>>2]|0)+-16>>2]*+g[(c[h>>2]|0)+16>>2]+ +g[(c[n>>2]|0)+-20>>2]*+g[(c[h>>2]|0)+20>>2]+ +g[(c[n>>2]|0)+-24>>2]*+g[(c[h>>2]|0)+24>>2]+ +g[(c[n>>2]|0)+-28>>2]*+g[(c[h>>2]|0)+28>>2]+ +g[(c[n>>2]|0)+-32>>2]*+g[(c[h>>2]|0)+32>>2]+ +g[(c[n>>2]|0)+-36>>2]*+g[(c[h>>2]|0)+36>>2]+ +g[(c[n>>2]|0)+-40>>2]*+g[(c[h>>2]|0)+40>>2]+ +g[(c[n>>2]|0)+-44>>2]*+g[(c[h>>2]|0)+44>>2]+ +g[(c[n>>2]|0)+-48>>2]*+g[(c[h>>2]|0)+48>>2]+ +g[(c[n>>2]|0)+-52>>2]*+g[(c[h>>2]|0)+52>>2]+ +g[(c[n>>2]|0)+-56>>2]*+g[(c[h>>2]|0)+56>>2]+ +g[(c[n>>2]|0)+-60>>2]*+g[(c[h>>2]|0)+60>>2];g[(c[f>>2]|0)+(c[m>>2]<<2)>>2]=+g[(c[n>>2]|0)+4>>2]-+g[l>>2];c[m>>2]=(c[m>>2]|0)+1}i=o;return}function dh(a,b,d,e,f,h,j,k){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;var l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0;z=i;i=i+80|0;A=z+76|0;B=z+72|0;l=z+68|0;m=z+64|0;n=z+60|0;o=z+56|0;p=z+52|0;q=z+48|0;y=z+44|0;x=z+40|0;r=z+20|0;s=z+16|0;u=z+12|0;w=z+8|0;t=z+4|0;v=z;c[A>>2]=a;c[B>>2]=b;c[l>>2]=d;c[m>>2]=e;c[n>>2]=f;c[o>>2]=h;c[p>>2]=j;c[q>>2]=k;c[y>>2]=c[B>>2];c[s>>2]=c[A>>2];c[w>>2]=0;while(1){if((c[w>>2]|0)>=(c[p>>2]|0))break;c[x>>2]=(c[y>>2]|0)+(0-(c[(c[m>>2]|0)+(c[w>>2]<<2)>>2]|0)<<2);g[u>>2]=+g[(c[n>>2]|0)+(c[w>>2]<<2)>>2];c[t>>2]=0;while(1){if((c[t>>2]|0)>=5)break;g[r+(c[t>>2]<<2)>>2]=+g[(c[l>>2]|0)+(((c[w>>2]|0)*5|0)+(c[t>>2]|0)<<2)>>2];c[t>>2]=(c[t>>2]|0)+1}c[t>>2]=0;while(1){if((c[t>>2]|0)>=((c[o>>2]|0)+(c[q>>2]|0)|0))break;g[(c[s>>2]|0)+(c[t>>2]<<2)>>2]=+g[(c[y>>2]|0)+(c[t>>2]<<2)>>2];c[v>>2]=0;while(1){if((c[v>>2]|0)>=5)break;B=(c[s>>2]|0)+(c[t>>2]<<2)|0;g[B>>2]=+g[B>>2]-+g[r+(c[v>>2]<<2)>>2]*+g[(c[x>>2]|0)+(2-(c[v>>2]|0)<<2)>>2];c[v>>2]=(c[v>>2]|0)+1}B=(c[s>>2]|0)+(c[t>>2]<<2)|0;g[B>>2]=+g[B>>2]*+g[u>>2];c[x>>2]=(c[x>>2]|0)+4;c[t>>2]=(c[t>>2]|0)+1}c[s>>2]=(c[s>>2]|0)+((c[o>>2]|0)+(c[q>>2]|0)<<2);c[y>>2]=(c[y>>2]|0)+(c[o>>2]<<2);c[w>>2]=(c[w>>2]|0)+1}i=z;return}function eh(d,e,f){d=d|0;e=e|0;f=f|0;var h=0.0,j=0,k=0,l=0,m=0,n=0;m=i;i=i+16|0;k=m+12|0;l=m+8|0;n=m+4|0;j=m;c[k>>2]=d;c[l>>2]=e;c[n>>2]=f;f=c[k>>2]|0;if(!(c[n>>2]|0)){c[j>>2]=(c[f+4640>>2]|0)+(c[(c[k>>2]|0)+5776>>2]|0);if(!(+(c[j>>2]|0)*+g[(c[l>>2]|0)+872>>2]*.10000000149011612>2.0))if(+(c[j>>2]|0)*+g[(c[l>>2]|0)+872>>2]*.10000000149011612<0.0)h=0.0;else h=+(c[j>>2]|0)*+g[(c[l>>2]|0)+872>>2]*.10000000149011612;else h=2.0;a[(c[k>>2]|0)+4768+33>>0]=~~h}else a[f+4768+33>>0]=0;g[(c[l>>2]|0)+224>>2]=+(b[24566+(a[(c[k>>2]|0)+4768+33>>0]<<1)>>1]|0)/16384.0;i=m;return}function fh(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,Q=0.0,R=0;M=i;i=i+1152|0;D=M+1140|0;E=M+1136|0;h=M+1132|0;R=M+1128|0;K=M+1124|0;J=M+1120|0;m=M+1116|0;q=M+1112|0;F=M+1108|0;G=M+1104|0;H=M+1100|0;w=M+1096|0;x=M+1092|0;k=M+1088|0;l=M+1084|0;j=M+1080|0;s=M+1076|0;o=M+1072|0;p=M+1068|0;v=M+1064|0;u=M+1060|0;L=M+1056|0;I=M+1052|0;A=M+1048|0;C=M+88|0;r=M+20|0;B=M+16|0;n=M+12|0;y=M+8|0;z=M+4|0;t=M;c[D>>2]=b;c[E>>2]=d;c[h>>2]=e;c[R>>2]=f;c[K>>2]=(c[D>>2]|0)+7200;c[B>>2]=(c[R>>2]|0)+(0-(c[(c[D>>2]|0)+4624>>2]|0)<<2);g[q>>2]=+(c[(c[D>>2]|0)+4748>>2]|0)*.0078125;g[(c[E>>2]|0)+856>>2]=+((c[(c[D>>2]|0)+4728>>2]|0)+(c[(c[D>>2]|0)+4728+4>>2]|0)|0)*.5*.000030517578125;Q=+gh((+g[q>>2]-20.0)*.25);g[(c[E>>2]|0)+860>>2]=Q;if(!(c[(c[D>>2]|0)+4708>>2]|0)){g[I>>2]=1.0-+(c[(c[D>>2]|0)+4556>>2]|0)*.00390625;g[q>>2]=+g[q>>2]-+g[(c[E>>2]|0)+860>>2]*2.0*(+g[(c[E>>2]|0)+856>>2]*.5+.5)*+g[I>>2]*+g[I>>2]}e=c[D>>2]|0;if((a[(c[D>>2]|0)+4768+29>>0]|0)==2)g[q>>2]=+g[q>>2]+ +g[e+12236>>2]*2.0;else g[q>>2]=+g[q>>2]+(+(c[e+4748>>2]|0)*-.4000000059604645*.0078125+6.0)*(1.0-+g[(c[E>>2]|0)+856>>2]);e=c[D>>2]|0;if((a[(c[D>>2]|0)+4768+29>>0]|0)==2){a[e+4768+30>>0]=0;g[(c[E>>2]|0)+864>>2]=0.0}else{c[m>>2]=c[e+4600>>2]<<1;g[j>>2]=0.0;g[l>>2]=0.0;c[n>>2]=c[h>>2];c[J>>2]=0;while(1){if((c[J>>2]|0)>=((((c[(c[D>>2]|0)+4604>>2]&65535)<<16>>16)*5|0)/2|0|0))break;Q=+(c[m>>2]|0);g[w>>2]=Q+ +Gh(c[n>>2]|0,c[m>>2]|0);g[k>>2]=+hh(+g[w>>2]);if((c[J>>2]|0)>0){Q=+N(+(+g[k>>2]-+g[l>>2]));g[j>>2]=+g[j>>2]+Q}g[l>>2]=+g[k>>2];c[n>>2]=(c[n>>2]|0)+(c[m>>2]<<2);c[J>>2]=(c[J>>2]|0)+1}Q=+gh((+g[j>>2]-5.0)*.4000000059604645);g[(c[E>>2]|0)+864>>2]=Q;e=(c[D>>2]|0)+4768+30|0;if(+g[(c[E>>2]|0)+864>>2]>.75)a[e>>0]=0;else a[e>>0]=1;g[q>>2]=+g[q>>2]+(+g[(c[E>>2]|0)+864>>2]-.5)*2.0}g[L>>2]=+g[(c[E>>2]|0)+868>>2]*1.0000000474974513e-03;Q=.949999988079071/(+g[L>>2]*+g[L>>2]+1.0);g[p>>2]=Q;g[o>>2]=Q;g[s>>2]=(1.0-+g[(c[E>>2]|0)+860>>2]*.75)*.009999999776482582;g[o>>2]=+g[o>>2]-+g[s>>2];g[p>>2]=+g[p>>2]+ +g[s>>2];g[o>>2]=+g[o>>2]/+g[p>>2];if((c[(c[D>>2]|0)+4704>>2]|0)>0)g[A>>2]=+(c[(c[D>>2]|0)+4704>>2]|0)/65536.0+ +g[(c[E>>2]|0)+860>>2]*.009999999776482582;else g[A>>2]=0.0;c[J>>2]=0;while(1){if((c[J>>2]|0)>=(c[(c[D>>2]|0)+4604>>2]|0))break;c[t>>2]=(c[(c[D>>2]|0)+4600>>2]|0)*3;c[z>>2]=((c[(c[D>>2]|0)+4628>>2]|0)-(c[t>>2]|0)|0)/2|0;Jg(C,c[B>>2]|0,1,c[z>>2]|0);c[y>>2]=c[z>>2];pj(C+(c[y>>2]<<2)|0,(c[B>>2]|0)+(c[y>>2]<<2)|0,c[t>>2]<<2|0)|0;c[y>>2]=(c[y>>2]|0)+(c[t>>2]|0);Jg(C+(c[y>>2]<<2)|0,(c[B>>2]|0)+(c[y>>2]<<2)|0,2,c[z>>2]|0);c[B>>2]=(c[B>>2]|0)+(c[(c[D>>2]|0)+4612>>2]<<2);if((c[(c[D>>2]|0)+4704>>2]|0)>0)wh(r,C,+g[A>>2],c[(c[D>>2]|0)+4628>>2]|0,c[(c[D>>2]|0)+4660>>2]|0);else Dh(r,C,c[(c[D>>2]|0)+4628>>2]|0,(c[(c[D>>2]|0)+4660>>2]|0)+1|0);g[r>>2]=+g[r>>2]+ +g[r>>2]*4.999999873689376e-05;g[w>>2]=+Jh((c[E>>2]|0)+500+(c[J>>2]<<4<<2)|0,r,c[(c[D>>2]|0)+4660>>2]|0);Q=+O(+(+g[w>>2]));g[(c[E>>2]|0)+(c[J>>2]<<2)>>2]=Q;if((c[(c[D>>2]|0)+4704>>2]|0)>0){Q=+ih((c[E>>2]|0)+500+(c[J>>2]<<4<<2)|0,+g[A>>2],c[(c[D>>2]|0)+4660>>2]|0);R=(c[E>>2]|0)+(c[J>>2]<<2)|0;g[R>>2]=+g[R>>2]*Q}Fh((c[E>>2]|0)+500+(c[J>>2]<<4<<2)|0,c[(c[D>>2]|0)+4660>>2]|0,+g[p>>2]);pj((c[E>>2]|0)+244+(c[J>>2]<<4<<2)|0,(c[E>>2]|0)+500+(c[J>>2]<<4<<2)|0,c[(c[D>>2]|0)+4660>>2]<<2|0)|0;Fh((c[E>>2]|0)+244+(c[J>>2]<<4<<2)|0,c[(c[D>>2]|0)+4660>>2]|0,+g[o>>2]);g[x>>2]=+Kh((c[E>>2]|0)+500+(c[J>>2]<<4<<2)|0,c[(c[D>>2]|0)+4660>>2]|0);g[w>>2]=+Kh((c[E>>2]|0)+244+(c[J>>2]<<4<<2)|0,c[(c[D>>2]|0)+4660>>2]|0);g[(c[E>>2]|0)+788+(c[J>>2]<<2)>>2]=1.0-(1.0-+g[x>>2]/+g[w>>2])*.699999988079071;jh((c[E>>2]|0)+500+(c[J>>2]<<4<<2)|0,(c[E>>2]|0)+244+(c[J>>2]<<4<<2)|0,+g[A>>2],3.999000072479248,c[(c[D>>2]|0)+4660>>2]|0);c[J>>2]=(c[J>>2]|0)+1}g[v>>2]=+P(2.0,+(+g[q>>2]*-.1599999964237213));g[u>>2]=+P(2.0,.3199999928474426);c[J>>2]=0;while(1){if((c[J>>2]|0)>=(c[(c[D>>2]|0)+4604>>2]|0))break;R=(c[E>>2]|0)+(c[J>>2]<<2)|0;g[R>>2]=+g[R>>2]*+g[v>>2];R=(c[E>>2]|0)+(c[J>>2]<<2)|0;g[R>>2]=+g[R>>2]+ +g[u>>2];c[J>>2]=(c[J>>2]|0)+1}g[v>>2]=+g[(c[E>>2]|0)+860>>2]*.10000000149011612+1.0499999523162842;c[J>>2]=0;while(1){if((c[J>>2]|0)>=(c[(c[D>>2]|0)+4604>>2]|0))break;R=(c[E>>2]|0)+788+(c[J>>2]<<2)|0;g[R>>2]=+g[R>>2]*+g[v>>2];c[J>>2]=(c[J>>2]|0)+1}g[L>>2]=((+(c[(c[D>>2]|0)+4728>>2]|0)*.000030517578125-1.0)*.5+1.0)*4.0;g[L>>2]=+g[L>>2]*(+(c[(c[D>>2]|0)+4556>>2]|0)*.00390625);if((a[(c[D>>2]|0)+4768+29>>0]|0)==2){c[J>>2]=0;while(1){e=c[D>>2]|0;if((c[J>>2]|0)>=(c[(c[D>>2]|0)+4604>>2]|0))break;g[I>>2]=.20000000298023224/+(c[e+4600>>2]|0)+3.0/+(c[(c[E>>2]|0)+228+(c[J>>2]<<2)>>2]|0);g[(c[E>>2]|0)+756+(c[J>>2]<<2)>>2]=+g[I>>2]+-1.0;g[(c[E>>2]|0)+772+(c[J>>2]<<2)>>2]=1.0-+g[I>>2]-+g[I>>2]*+g[L>>2];c[J>>2]=(c[J>>2]|0)+1}g[H>>2]=-.25-+(c[e+4556>>2]|0)*.26249998807907104*.00390625}else{g[I>>2]=1.2999999523162842/+(c[(c[D>>2]|0)+4600>>2]|0);g[(c[E>>2]|0)+756>>2]=+g[I>>2]+-1.0;g[(c[E>>2]|0)+772>>2]=1.0-+g[I>>2]-+g[I>>2]*+g[L>>2]*.6000000238418579;c[J>>2]=1;while(1){if((c[J>>2]|0)>=(c[(c[D>>2]|0)+4604>>2]|0))break;g[(c[E>>2]|0)+756+(c[J>>2]<<2)>>2]=+g[(c[E>>2]|0)+756>>2];g[(c[E>>2]|0)+772+(c[J>>2]<<2)>>2]=+g[(c[E>>2]|0)+772>>2];c[J>>2]=(c[J>>2]|0)+1}g[H>>2]=-.25}g[F>>2]=(1.0-+g[(c[E>>2]|0)+860>>2])*.10000000149011612*+g[(c[D>>2]|0)+12236>>2];g[F>>2]=+g[F>>2]+(1.0-+g[(c[E>>2]|0)+856>>2])*.10000000149011612;if((a[(c[D>>2]|0)+4768+29>>0]|0)==2){g[G>>2]=.30000001192092896;g[G>>2]=+g[G>>2]+(1.0-(1.0-+g[(c[E>>2]|0)+860>>2])*+g[(c[E>>2]|0)+856>>2])*.20000000298023224;Q=+O(+(+g[(c[D>>2]|0)+12236>>2]));g[G>>2]=+g[G>>2]*Q}else g[G>>2]=0.0;c[J>>2]=0;while(1){if((c[J>>2]|0)>=(c[(c[D>>2]|0)+4604>>2]|0))break;R=(c[K>>2]|0)+4|0;g[R>>2]=+g[R>>2]+(+g[F>>2]-+g[(c[K>>2]|0)+4>>2])*.4000000059604645;g[(c[E>>2]|0)+804+(c[J>>2]<<2)>>2]=+g[(c[K>>2]|0)+4>>2];R=(c[K>>2]|0)+8|0;g[R>>2]=+g[R>>2]+(+g[G>>2]-+g[(c[K>>2]|0)+8>>2])*.4000000059604645;g[(c[E>>2]|0)+836+(c[J>>2]<<2)>>2]=+g[(c[K>>2]|0)+8>>2];R=(c[K>>2]|0)+12|0;g[R>>2]=+g[R>>2]+(+g[H>>2]-+g[(c[K>>2]|0)+12>>2])*.4000000059604645;g[(c[E>>2]|0)+820+(c[J>>2]<<2)>>2]=+g[(c[K>>2]|0)+12>>2];c[J>>2]=(c[J>>2]|0)+1}i=M;return}function gh(a){a=+a;var b=0,c=0;b=i;i=i+16|0;c=b;g[c>>2]=a;a=1.0/(+X(+-+g[c>>2])+1.0);i=b;return +a}function hh(a){a=+a;var b=0,c=0;b=i;i=i+16|0;c=b;h[c>>3]=a;a=+hj(+h[c>>3])*3.32192809488736;i=b;return +a}function ih(a,b,d){a=a|0;b=+b;d=d|0;var e=0,f=0,h=0,j=0,k=0,l=0;k=i;i=i+32|0;e=k+16|0;f=k+12|0;l=k+8|0;j=k+4|0;h=k;c[e>>2]=a;g[f>>2]=b;c[l>>2]=d;g[f>>2]=-+g[f>>2];g[h>>2]=+g[(c[e>>2]|0)+((c[l>>2]|0)-1<<2)>>2];c[j>>2]=(c[l>>2]|0)-2;while(1){b=+g[f>>2]*+g[h>>2];if((c[j>>2]|0)<0)break;g[h>>2]=b+ +g[(c[e>>2]|0)+(c[j>>2]<<2)>>2];c[j>>2]=(c[j>>2]|0)+-1}i=k;return +(1.0/(1.0-b))}function jh(a,b,d,e,f){a=a|0;b=b|0;d=+d;e=+e;f=f|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;v=i;i=i+64|0;h=v+48|0;j=v+44|0;k=v+40|0;l=v+36|0;m=v+32|0;q=v+28|0;s=v+24|0;r=v+20|0;u=v+16|0;t=v+12|0;n=v+8|0;p=v+4|0;o=v;c[h>>2]=a;c[j>>2]=b;g[k>>2]=d;g[l>>2]=e;c[m>>2]=f;c[r>>2]=0;c[q>>2]=(c[m>>2]|0)-1;while(1){d=+g[k>>2];if((c[q>>2]|0)<=0)break;f=(c[h>>2]|0)+((c[q>>2]|0)-1<<2)|0;g[f>>2]=+g[f>>2]-d*+g[(c[h>>2]|0)+(c[q>>2]<<2)>>2];f=(c[j>>2]|0)+((c[q>>2]|0)-1<<2)|0;g[f>>2]=+g[f>>2]-+g[k>>2]*+g[(c[j>>2]|0)+(c[q>>2]<<2)>>2];c[q>>2]=(c[q>>2]|0)+-1}g[p>>2]=(1.0-d*+g[k>>2])/(+g[k>>2]*+g[c[h>>2]>>2]+1.0);g[o>>2]=(1.0-+g[k>>2]*+g[k>>2])/(+g[k>>2]*+g[c[j>>2]>>2]+1.0);c[q>>2]=0;while(1){if((c[q>>2]|0)>=(c[m>>2]|0))break;f=(c[h>>2]|0)+(c[q>>2]<<2)|0;g[f>>2]=+g[f>>2]*+g[p>>2];f=(c[j>>2]|0)+(c[q>>2]<<2)|0;g[f>>2]=+g[f>>2]*+g[o>>2];c[q>>2]=(c[q>>2]|0)+1}c[s>>2]=0;while(1){if((c[s>>2]|0)>=10){b=31;break}g[t>>2]=-1.0;c[q>>2]=0;while(1){if((c[q>>2]|0)>=(c[m>>2]|0))break;e=+N(+(+g[(c[h>>2]|0)+(c[q>>2]<<2)>>2]));f=e>+N(+(+g[(c[j>>2]|0)+(c[q>>2]<<2)>>2]));b=c[q>>2]|0;if(f)d=+g[(c[h>>2]|0)+(b<<2)>>2];else d=+g[(c[j>>2]|0)+(b<<2)>>2];g[u>>2]=+N(+d);if(+g[u>>2]>+g[t>>2]){g[t>>2]=+g[u>>2];c[r>>2]=c[q>>2]}c[q>>2]=(c[q>>2]|0)+1}if(+g[t>>2]<=+g[l>>2]){b=31;break}c[q>>2]=1;while(1){if((c[q>>2]|0)>=(c[m>>2]|0))break;f=(c[h>>2]|0)+((c[q>>2]|0)-1<<2)|0;g[f>>2]=+g[f>>2]+ +g[k>>2]*+g[(c[h>>2]|0)+(c[q>>2]<<2)>>2];f=(c[j>>2]|0)+((c[q>>2]|0)-1<<2)|0;g[f>>2]=+g[f>>2]+ +g[k>>2]*+g[(c[j>>2]|0)+(c[q>>2]<<2)>>2];c[q>>2]=(c[q>>2]|0)+1}g[p>>2]=1.0/+g[p>>2];g[o>>2]=1.0/+g[o>>2];c[q>>2]=0;while(1){if((c[q>>2]|0)>=(c[m>>2]|0))break;f=(c[h>>2]|0)+(c[q>>2]<<2)|0;g[f>>2]=+g[f>>2]*+g[p>>2];f=(c[j>>2]|0)+(c[q>>2]<<2)|0;g[f>>2]=+g[f>>2]*+g[o>>2];c[q>>2]=(c[q>>2]|0)+1}g[n>>2]=.9900000095367432-(+(c[s>>2]|0)*.10000000149011612+.800000011920929)*(+g[t>>2]-+g[l>>2])/(+g[t>>2]*+((c[r>>2]|0)+1|0));Fh(c[h>>2]|0,c[m>>2]|0,+g[n>>2]);Fh(c[j>>2]|0,c[m>>2]|0,+g[n>>2]);c[q>>2]=(c[m>>2]|0)-1;while(1){d=+g[k>>2];if((c[q>>2]|0)<=0)break;f=(c[h>>2]|0)+((c[q>>2]|0)-1<<2)|0;g[f>>2]=+g[f>>2]-d*+g[(c[h>>2]|0)+(c[q>>2]<<2)>>2];f=(c[j>>2]|0)+((c[q>>2]|0)-1<<2)|0;g[f>>2]=+g[f>>2]-+g[k>>2]*+g[(c[j>>2]|0)+(c[q>>2]<<2)>>2];c[q>>2]=(c[q>>2]|0)+-1}g[p>>2]=(1.0-d*+g[k>>2])/(+g[k>>2]*+g[c[h>>2]>>2]+1.0);g[o>>2]=(1.0-+g[k>>2]*+g[k>>2])/(+g[k>>2]*+g[c[j>>2]>>2]+1.0);c[q>>2]=0;while(1){if((c[q>>2]|0)>=(c[m>>2]|0))break;f=(c[h>>2]|0)+(c[q>>2]<<2)|0;g[f>>2]=+g[f>>2]*+g[p>>2];f=(c[j>>2]|0)+(c[q>>2]<<2)|0;g[f>>2]=+g[f>>2]*+g[o>>2];c[q>>2]=(c[q>>2]|0)+1}c[s>>2]=(c[s>>2]|0)+1}if((b|0)==31){i=v;return}}function kh(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0;y=i;i=i+464|0;h=y+460|0;j=y+456|0;z=y+452|0;A=y+448|0;q=y+444|0;s=y+440|0;t=y+436|0;u=y+432|0;n=y+428|0;r=y+424|0;p=y+420|0;o=y+416|0;l=y+408|0;k=y+404|0;v=y+400|0;w=y+396|0;m=y+384|0;x=y;c[h>>2]=b;c[j>>2]=d;c[z>>2]=e;c[A>>2]=f;c[q>>2]=(c[h>>2]|0)+7216;c[v>>2]=c[A>>2];c[w>>2]=c[z>>2];c[u>>2]=c[(c[q>>2]|0)+2136>>2];c[t>>2]=0;while(1){b=c[h>>2]|0;if((c[t>>2]|0)>=(c[(c[h>>2]|0)+4604>>2]|0))break;if((a[b+4768+29>>0]|0)==2)c[u>>2]=c[(c[j>>2]|0)+228+(c[t>>2]<<2)>>2];g[n>>2]=+g[(c[j>>2]|0)+836+(c[t>>2]<<2)>>2]*(1.0-+g[(c[j>>2]|0)+804+(c[t>>2]<<2)>>2]);g[m>>2]=+g[n>>2]*.25;g[m+4>>2]=+g[n>>2]*.4999847412109375;g[m+8>>2]=+g[n>>2]*.25;g[r>>2]=+g[(c[j>>2]|0)+820+(c[t>>2]<<2)>>2];g[p>>2]=+g[(c[j>>2]|0)+756+(c[t>>2]<<2)>>2];g[o>>2]=+g[(c[j>>2]|0)+772+(c[t>>2]<<2)>>2];c[k>>2]=(c[j>>2]|0)+244+(c[t>>2]<<4<<2);lh((c[q>>2]|0)+2048|0,x,c[k>>2]|0,c[v>>2]|0,+(c[(c[h>>2]|0)+4704>>2]|0)/65536.0,c[(c[h>>2]|0)+4612>>2]|0,c[(c[h>>2]|0)+4660>>2]|0);g[l>>2]=+g[(c[j>>2]|0)+788+(c[t>>2]<<2)>>2];g[l+4>>2]=-+g[(c[j>>2]|0)+788+(c[t>>2]<<2)>>2]*(+g[(c[j>>2]|0)+804+(c[t>>2]<<2)>>2]*+g[n>>2]+.05000000074505806+ +g[(c[j>>2]|0)+860>>2]*.10000000149011612);g[c[w>>2]>>2]=+g[l>>2]*+g[x>>2]+ +g[l+4>>2]*+g[(c[q>>2]|0)+2128>>2];c[s>>2]=1;while(1){if((c[s>>2]|0)>=(c[(c[h>>2]|0)+4612>>2]|0))break;g[(c[w>>2]|0)+(c[s>>2]<<2)>>2]=+g[l>>2]*+g[x+(c[s>>2]<<2)>>2]+ +g[l+4>>2]*+g[x+((c[s>>2]|0)-1<<2)>>2];c[s>>2]=(c[s>>2]|0)+1}g[(c[q>>2]|0)+2128>>2]=+g[x+((c[(c[h>>2]|0)+4612>>2]|0)-1<<2)>>2];mh(c[q>>2]|0,c[w>>2]|0,c[w>>2]|0,m,+g[r>>2],+g[p>>2],+g[o>>2],c[u>>2]|0,c[(c[h>>2]|0)+4612>>2]|0);c[v>>2]=(c[v>>2]|0)+(c[(c[h>>2]|0)+4612>>2]<<2);c[w>>2]=(c[w>>2]|0)+(c[(c[h>>2]|0)+4612>>2]<<2);c[t>>2]=(c[t>>2]|0)+1}c[(c[q>>2]|0)+2136>>2]=c[(c[j>>2]|0)+228+((c[b+4604>>2]|0)-1<<2)>>2];i=y;return}function lh(a,b,d,e,f,h,j){a=a|0;b=b|0;d=d|0;e=e|0;f=+f;h=h|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0;w=i;i=i+48|0;k=w+44|0;l=w+40|0;m=w+36|0;n=w+32|0;o=w+28|0;p=w+24|0;q=w+20|0;t=w+16|0;s=w+12|0;r=w+8|0;u=w+4|0;v=w;c[k>>2]=a;c[l>>2]=b;c[m>>2]=d;c[n>>2]=e;g[o>>2]=f;c[p>>2]=h;c[q>>2]=j;c[t>>2]=0;while(1){if((c[t>>2]|0)>=(c[p>>2]|0))break;g[v>>2]=+g[c[k>>2]>>2]+ +g[o>>2]*+g[(c[k>>2]|0)+4>>2];g[c[k>>2]>>2]=+g[(c[n>>2]|0)+(c[t>>2]<<2)>>2];g[u>>2]=+g[(c[k>>2]|0)+4>>2]+ +g[o>>2]*(+g[(c[k>>2]|0)+8>>2]-+g[v>>2]);g[(c[k>>2]|0)+4>>2]=+g[v>>2];g[r>>2]=+g[c[m>>2]>>2]*+g[v>>2];c[s>>2]=2;while(1){if((c[s>>2]|0)>=(c[q>>2]|0))break;g[v>>2]=+g[(c[k>>2]|0)+(c[s>>2]<<2)>>2]+ +g[o>>2]*(+g[(c[k>>2]|0)+((c[s>>2]|0)+1<<2)>>2]-+g[u>>2]);g[(c[k>>2]|0)+(c[s>>2]<<2)>>2]=+g[u>>2];g[r>>2]=+g[r>>2]+ +g[(c[m>>2]|0)+((c[s>>2]|0)-1<<2)>>2]*+g[u>>2];g[u>>2]=+g[(c[k>>2]|0)+((c[s>>2]|0)+1<<2)>>2]+ +g[o>>2]*(+g[(c[k>>2]|0)+((c[s>>2]|0)+2<<2)>>2]-+g[v>>2]);g[(c[k>>2]|0)+((c[s>>2]|0)+1<<2)>>2]=+g[v>>2];g[r>>2]=+g[r>>2]+ +g[(c[m>>2]|0)+(c[s>>2]<<2)>>2]*+g[v>>2];c[s>>2]=(c[s>>2]|0)+2}g[(c[k>>2]|0)+(c[q>>2]<<2)>>2]=+g[u>>2];g[r>>2]=+g[r>>2]+ +g[(c[m>>2]|0)+((c[q>>2]|0)-1<<2)>>2]*+g[u>>2];g[(c[l>>2]|0)+(c[t>>2]<<2)>>2]=+g[(c[n>>2]|0)+(c[t>>2]<<2)>>2]-+g[r>>2];c[t>>2]=(c[t>>2]|0)+1}i=w;return}function mh(a,b,d,e,f,h,j,k,l){a=a|0;b=b|0;d=d|0;e=e|0;f=+f;h=+h;j=+j;k=k|0;l=l|0;var m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0;E=i;i=i+80|0;m=E+68|0;n=E+64|0;o=E+60|0;p=E+56|0;q=E+52|0;r=E+48|0;s=E+44|0;t=E+40|0;u=E+36|0;x=E+32|0;y=E+28|0;w=E+24|0;B=E+20|0;z=E+16|0;A=E+12|0;C=E+8|0;D=E+4|0;v=E;c[m>>2]=a;c[n>>2]=b;c[o>>2]=d;c[p>>2]=e;g[q>>2]=f;g[r>>2]=h;g[s>>2]=j;c[t>>2]=k;c[u>>2]=l;c[v>>2]=c[m>>2];c[w>>2]=c[(c[m>>2]|0)+2116>>2];g[C>>2]=+g[(c[m>>2]|0)+2120>>2];g[D>>2]=+g[(c[m>>2]|0)+2124>>2];c[x>>2]=0;while(1){if((c[x>>2]|0)>=(c[u>>2]|0))break;if((c[t>>2]|0)>0){c[y>>2]=(c[t>>2]|0)+(c[w>>2]|0);g[A>>2]=+g[(c[v>>2]|0)+(((c[y>>2]|0)-1-1&511)<<2)>>2]*+g[c[p>>2]>>2];g[A>>2]=+g[A>>2]+ +g[(c[v>>2]|0)+(((c[y>>2]|0)-1&511)<<2)>>2]*+g[(c[p>>2]|0)+4>>2];g[A>>2]=+g[A>>2]+ +g[(c[v>>2]|0)+(((c[y>>2]|0)-1+1&511)<<2)>>2]*+g[(c[p>>2]|0)+8>>2]}else g[A>>2]=0.0;g[B>>2]=+g[C>>2]*+g[q>>2];g[z>>2]=+g[C>>2]*+g[s>>2]+ +g[D>>2]*+g[r>>2];g[C>>2]=+g[(c[n>>2]|0)+(c[x>>2]<<2)>>2]-+g[B>>2];g[D>>2]=+g[C>>2]-+g[z>>2];c[w>>2]=(c[w>>2]|0)-1&511;g[(c[v>>2]|0)+(c[w>>2]<<2)>>2]=+g[D>>2];g[(c[o>>2]|0)+(c[x>>2]<<2)>>2]=+g[D>>2]-+g[A>>2];c[x>>2]=(c[x>>2]|0)+1}g[(c[m>>2]|0)+2120>>2]=+g[C>>2];g[(c[m>>2]|0)+2124>>2]=+g[D>>2];c[(c[m>>2]|0)+2116>>2]=c[w>>2];i=E;return}function nh(d,e,f){d=d|0;e=e|0;f=f|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0.0;s=i;i=i+64|0;p=s+48|0;q=s+44|0;h=s+40|0;n=s+36|0;l=s+32|0;m=s+16|0;o=s+12|0;j=s+8|0;k=s+4|0;r=s;c[p>>2]=d;c[q>>2]=e;c[h>>2]=f;c[n>>2]=(c[p>>2]|0)+7200;a:do if((a[(c[p>>2]|0)+4768+29>>0]|0)==2){g[o>>2]=1.0-+oh((+g[(c[q>>2]|0)+872>>2]-12.0)*.25)*.5;c[l>>2]=0;while(1){if((c[l>>2]|0)>=(c[(c[p>>2]|0)+4604>>2]|0))break a;e=(c[q>>2]|0)+(c[l>>2]<<2)|0;g[e>>2]=+g[e>>2]*+g[o>>2];c[l>>2]=(c[l>>2]|0)+1}}while(0);t=+P(2.0,+((21.0-+(c[(c[p>>2]|0)+4748>>2]|0)*.0078125)*.33000001311302185));g[j>>2]=t/+(c[(c[p>>2]|0)+4612>>2]|0);c[l>>2]=0;while(1){if((c[l>>2]|0)>=(c[(c[p>>2]|0)+4604>>2]|0))break;g[k>>2]=+g[(c[q>>2]|0)+(c[l>>2]<<2)>>2];g[k>>2]=+O(+(+g[k>>2]*+g[k>>2]+ +g[(c[q>>2]|0)+876+(c[l>>2]<<2)>>2]*+g[j>>2]));g[(c[q>>2]|0)+(c[l>>2]<<2)>>2]=+g[k>>2]<32767.0?+g[k>>2]:32767.0;c[l>>2]=(c[l>>2]|0)+1}c[l>>2]=0;while(1){if((c[l>>2]|0)>=(c[(c[p>>2]|0)+4604>>2]|0))break;c[m+(c[l>>2]<<2)>>2]=~~(+g[(c[q>>2]|0)+(c[l>>2]<<2)>>2]*65536.0);c[l>>2]=(c[l>>2]|0)+1}pj((c[q>>2]|0)+892|0,m|0,c[(c[p>>2]|0)+4604>>2]<<2|0)|0;a[(c[q>>2]|0)+908>>0]=a[c[n>>2]>>0]|0;_d((c[p>>2]|0)+4768|0,m,c[n>>2]|0,(c[h>>2]|0)==2&1,c[(c[p>>2]|0)+4604>>2]|0);c[l>>2]=0;while(1){if((c[l>>2]|0)>=(c[(c[p>>2]|0)+4604>>2]|0))break;g[(c[q>>2]|0)+(c[l>>2]<<2)>>2]=+(c[m+(c[l>>2]<<2)>>2]|0)/65536.0;c[l>>2]=(c[l>>2]|0)+1}do if((a[(c[p>>2]|0)+4768+29>>0]|0)==2){h=(c[p>>2]|0)+4768+30|0;if(+g[(c[q>>2]|0)+872>>2]+ +(c[(c[p>>2]|0)+4744>>2]|0)*.000030517578125>1.0){a[h>>0]=0;break}else{a[h>>0]=1;break}}while(0);g[r>>2]=+(b[24558+(a[(c[p>>2]|0)+4768+29>>0]>>1<<2)+(a[(c[p>>2]|0)+4768+30>>0]<<1)>>1]|0)/1024.0;g[(c[q>>2]|0)+852>>2]=+(c[(c[p>>2]|0)+4652>>2]|0)*-.05000000074505806+1.2000000476837158+ +(c[(c[p>>2]|0)+4556>>2]|0)*-.20000000298023224*.00390625+ +g[(c[q>>2]|0)+856>>2]*-.10000000149011612+ +g[(c[q>>2]|0)+860>>2]*-.20000000298023224+ +g[r>>2]*.800000011920929;i=s;return}function oh(a){a=+a;var b=0,c=0;b=i;i=i+16|0;c=b;g[c>>2]=a;a=1.0/(+X(+-+g[c>>2])+1.0);i=b;return +a}function ph(a,b,d,e){a=a|0;b=b|0;d=+d;e=e|0;var f=0,h=0,j=0,k=0,l=0,m=0;m=i;i=i+32|0;f=m+16|0;h=m+12|0;j=m+8|0;k=m+4|0;l=m;c[f>>2]=a;c[h>>2]=b;g[j>>2]=d;c[k>>2]=e;c[l>>2]=0;while(1){d=+g[j>>2];if((c[l>>2]|0)>=(c[k>>2]|0))break;b=_(c[l>>2]|0,c[k>>2]|0)|0;b=(c[f>>2]|0)+(b+(c[l>>2]|0)<<2)|0;g[b>>2]=+g[b>>2]+d;c[l>>2]=(c[l>>2]|0)+1}l=c[h>>2]|0;g[l>>2]=+g[l>>2]+d;i=m;return}function qh(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=+e;f=f|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;t=i;i=i+48|0;h=t+40|0;j=t+36|0;k=t+32|0;l=t+28|0;m=t+24|0;n=t+20|0;o=t+16|0;p=t+12|0;s=t+8|0;q=t+4|0;r=t;c[h>>2]=a;c[j>>2]=b;c[k>>2]=d;g[l>>2]=e;c[m>>2]=f;g[q>>2]=0.0;d=(_(c[m>>2]|0,c[m>>2]|0)|0)-1|0;g[r>>2]=(+g[c[j>>2]>>2]+ +g[(c[j>>2]|0)+(d<<2)>>2])*9.99999993922529e-09;c[p>>2]=0;while(1){if((c[p>>2]|0)>=10)break;g[q>>2]=+g[l>>2];g[s>>2]=0.0;c[n>>2]=0;while(1){if((c[n>>2]|0)>=(c[m>>2]|0))break;g[s>>2]=+g[s>>2]+ +g[(c[k>>2]|0)+(c[n>>2]<<2)>>2]*+g[(c[h>>2]|0)+(c[n>>2]<<2)>>2];c[n>>2]=(c[n>>2]|0)+1}g[q>>2]=+g[q>>2]-+g[s>>2]*2.0;c[n>>2]=0;while(1){if((c[n>>2]|0)>=(c[m>>2]|0))break;g[s>>2]=0.0;c[o>>2]=(c[n>>2]|0)+1;while(1){if((c[o>>2]|0)>=(c[m>>2]|0))break;e=+g[(c[j>>2]|0)+((c[n>>2]|0)+(_(c[m>>2]|0,c[o>>2]|0)|0)<<2)>>2];g[s>>2]=+g[s>>2]+e*+g[(c[h>>2]|0)+(c[o>>2]<<2)>>2];c[o>>2]=(c[o>>2]|0)+1}e=+g[(c[j>>2]|0)+((c[n>>2]|0)+(_(c[m>>2]|0,c[n>>2]|0)|0)<<2)>>2];g[q>>2]=+g[q>>2]+ +g[(c[h>>2]|0)+(c[n>>2]<<2)>>2]*(+g[s>>2]*2.0+e*+g[(c[h>>2]|0)+(c[n>>2]<<2)>>2]);c[n>>2]=(c[n>>2]|0)+1}if(+g[q>>2]>0.0)break;c[n>>2]=0;while(1){e=+g[r>>2];if((c[n>>2]|0)>=(c[m>>2]|0))break;d=(c[j>>2]|0)+((c[n>>2]|0)+(_(c[m>>2]|0,c[n>>2]|0)|0)<<2)|0;g[d>>2]=+g[d>>2]+e;c[n>>2]=(c[n>>2]|0)+1}g[r>>2]=e*2.0;c[p>>2]=(c[p>>2]|0)+1}if((c[p>>2]|0)!=10){e=+g[q>>2];i=t;return +e}g[q>>2]=1.0;e=+g[q>>2];i=t;return +e}function rh(a,b,d,e,f,h,j){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0.0;t=i;i=i+816|0;k=t+800|0;l=t+796|0;m=t+792|0;n=t+788|0;o=t+784|0;u=t+780|0;p=t+776|0;s=t+772|0;r=t+768|0;q=t;c[k>>2]=a;c[l>>2]=b;c[m>>2]=d;c[n>>2]=e;c[o>>2]=f;c[u>>2]=h;c[p>>2]=j;c[r>>2]=q+(c[p>>2]<<2);c[s>>2]=(c[p>>2]|0)+(c[o>>2]|0);Zg(q,c[m>>2]|0,(c[l>>2]|0)+(0<<2)|0,c[s>>2]<<1,c[p>>2]|0);v=+g[c[n>>2]>>2]*+g[c[n>>2]>>2];v=v*+Gh((c[r>>2]|0)+(0<<2)|0,c[o>>2]|0);g[c[k>>2]>>2]=v;v=+g[(c[n>>2]|0)+4>>2]*+g[(c[n>>2]|0)+4>>2];v=v*+Gh((c[r>>2]|0)+(c[s>>2]<<2)|0,c[o>>2]|0);g[(c[k>>2]|0)+4>>2]=v;if((c[u>>2]|0)!=4){i=t;return}Zg(q,(c[m>>2]|0)+64|0,(c[l>>2]|0)+(c[s>>2]<<1<<2)|0,c[s>>2]<<1,c[p>>2]|0);v=+g[(c[n>>2]|0)+8>>2]*+g[(c[n>>2]|0)+8>>2];v=v*+Gh((c[r>>2]|0)+(0<<2)|0,c[o>>2]|0);g[(c[k>>2]|0)+8>>2]=v;v=+g[(c[n>>2]|0)+12>>2]*+g[(c[n>>2]|0)+12>>2];v=v*+Gh((c[r>>2]|0)+(c[s>>2]<<2)|0,c[o>>2]|0);g[(c[k>>2]|0)+12>>2]=v;i=t;return}function sh(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;n=i;i=i+1184|0;p=n+1168|0;f=n+1164|0;o=n+1160|0;h=n+1156|0;m=n+1152|0;k=n+128|0;l=n+64|0;j=n;c[p>>2]=a;c[f>>2]=b;c[o>>2]=d;c[h>>2]=e;th(c[p>>2]|0,c[f>>2]|0,k,j);uh(k,c[f>>2]|0,c[o>>2]|0,l);c[m>>2]=0;while(1){if((c[m>>2]|0)>=(c[f>>2]|0))break;g[l+(c[m>>2]<<2)>>2]=+g[l+(c[m>>2]<<2)>>2]*+g[j+(c[m>>2]<<2)>>2];c[m>>2]=(c[m>>2]|0)+1}vh(k,c[f>>2]|0,l,c[h>>2]|0);i=n;return}function th(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0;y=i;i=i+192|0;f=y+184|0;j=y+180|0;k=y+176|0;l=y+172|0;p=y+168|0;q=y+164|0;r=y+160|0;s=y+156|0;o=y+152|0;t=y+148|0;u=y+144|0;v=y+8|0;n=y;w=y+80|0;m=y+16|0;c[f>>2]=a;c[j>>2]=b;c[k>>2]=d;c[l>>2]=e;c[o>>2]=1;b=(_(c[j>>2]|0,c[j>>2]|0)|0)-1|0;h[n>>3]=(+g[c[f>>2]>>2]+ +g[(c[f>>2]|0)+(b<<2)>>2])*4.999999873689376e-06;c[s>>2]=0;while(1){if(!((c[s>>2]|0)<(c[j>>2]|0)?(c[o>>2]|0)==1:0))break;c[o>>2]=0;c[q>>2]=0;while(1){if((c[q>>2]|0)>=(c[j>>2]|0))break;c[t>>2]=(c[k>>2]|0)+((_(c[q>>2]|0,c[j>>2]|0)|0)+0<<2);b=_(c[q>>2]|0,c[j>>2]|0)|0;h[v>>3]=+g[(c[f>>2]|0)+(b+(c[q>>2]|0)<<2)>>2];c[p>>2]=0;while(1){if((c[p>>2]|0)>=(c[q>>2]|0))break;g[w+(c[p>>2]<<2)>>2]=+g[(c[t>>2]|0)+(c[p>>2]<<2)>>2]*+g[m+(c[p>>2]<<2)>>2];h[v>>3]=+h[v>>3]-+g[(c[t>>2]|0)+(c[p>>2]<<2)>>2]*+g[w+(c[p>>2]<<2)>>2];c[p>>2]=(c[p>>2]|0)+1}if(+h[v>>3]<+h[n>>3]){x=9;break}g[m+(c[q>>2]<<2)>>2]=+h[v>>3];g[(c[l>>2]|0)+(c[q>>2]<<2)>>2]=1.0/+h[v>>3];b=_(c[q>>2]|0,c[j>>2]|0)|0;g[(c[k>>2]|0)+(b+(c[q>>2]|0)<<2)>>2]=1.0;c[t>>2]=(c[f>>2]|0)+((_(c[q>>2]|0,c[j>>2]|0)|0)+0<<2);c[u>>2]=(c[k>>2]|0)+((_((c[q>>2]|0)+1|0,c[j>>2]|0)|0)+0<<2);c[p>>2]=(c[q>>2]|0)+1;while(1){if((c[p>>2]|0)>=(c[j>>2]|0))break;h[v>>3]=0.0;c[r>>2]=0;while(1){if((c[r>>2]|0)>=(c[q>>2]|0))break;h[v>>3]=+h[v>>3]+ +g[(c[u>>2]|0)+(c[r>>2]<<2)>>2]*+g[w+(c[r>>2]<<2)>>2];c[r>>2]=(c[r>>2]|0)+1}b=_(c[p>>2]|0,c[j>>2]|0)|0;g[(c[k>>2]|0)+(b+(c[q>>2]|0)<<2)>>2]=(+g[(c[t>>2]|0)+(c[p>>2]<<2)>>2]-+h[v>>3])*+g[(c[l>>2]|0)+(c[q>>2]<<2)>>2];c[u>>2]=(c[u>>2]|0)+(c[j>>2]<<2);c[p>>2]=(c[p>>2]|0)+1}c[q>>2]=(c[q>>2]|0)+1}if((x|0)==9){x=0;h[v>>3]=+((c[s>>2]|0)+1|0)*+h[n>>3]-+h[v>>3];c[p>>2]=0;while(1){if((c[p>>2]|0)>=(c[j>>2]|0))break;b=_(c[p>>2]|0,c[j>>2]|0)|0;b=(c[f>>2]|0)+(b+(c[p>>2]|0)<<2)|0;g[b>>2]=+g[b>>2]+ +h[v>>3];c[p>>2]=(c[p>>2]|0)+1}c[o>>2]=1}c[s>>2]=(c[s>>2]|0)+1}i=y;return}function uh(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;p=i;i=i+32|0;f=p+28|0;h=p+24|0;j=p+20|0;k=p+16|0;l=p+12|0;m=p+8|0;o=p+4|0;n=p;c[f>>2]=a;c[h>>2]=b;c[j>>2]=d;c[k>>2]=e;c[l>>2]=0;while(1){if((c[l>>2]|0)>=(c[h>>2]|0))break;c[n>>2]=(c[f>>2]|0)+((_(c[l>>2]|0,c[h>>2]|0)|0)+0<<2);g[o>>2]=0.0;c[m>>2]=0;while(1){if((c[m>>2]|0)>=(c[l>>2]|0))break;g[o>>2]=+g[o>>2]+ +g[(c[n>>2]|0)+(c[m>>2]<<2)>>2]*+g[(c[k>>2]|0)+(c[m>>2]<<2)>>2];c[m>>2]=(c[m>>2]|0)+1}g[o>>2]=+g[(c[j>>2]|0)+(c[l>>2]<<2)>>2]-+g[o>>2];g[(c[k>>2]|0)+(c[l>>2]<<2)>>2]=+g[o>>2];c[l>>2]=(c[l>>2]|0)+1}i=p;return}function vh(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;p=i;i=i+32|0;f=p+28|0;h=p+24|0;j=p+20|0;k=p+16|0;l=p+12|0;m=p+8|0;o=p+4|0;n=p;c[f>>2]=a;c[h>>2]=b;c[j>>2]=d;c[k>>2]=e;c[l>>2]=(c[h>>2]|0)-1;while(1){if((c[l>>2]|0)<0)break;c[n>>2]=(c[f>>2]|0)+(0+(c[l>>2]|0)<<2);g[o>>2]=0.0;c[m>>2]=(c[h>>2]|0)-1;while(1){if((c[m>>2]|0)<=(c[l>>2]|0))break;e=_(c[m>>2]|0,c[h>>2]|0)|0;g[o>>2]=+g[o>>2]+ +g[(c[n>>2]|0)+(e<<2)>>2]*+g[(c[k>>2]|0)+(c[m>>2]<<2)>>2];c[m>>2]=(c[m>>2]|0)+-1}g[o>>2]=+g[(c[j>>2]|0)+(c[l>>2]<<2)>>2]-+g[o>>2];g[(c[k>>2]|0)+(c[l>>2]<<2)>>2]=+g[o>>2];c[l>>2]=(c[l>>2]|0)+-1}i=p;return}function wh(a,b,d,e,f){a=a|0;b=b|0;d=+d;e=e|0;f=f|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;u=i;i=i+320|0;j=u+312|0;k=u+308|0;l=u+304|0;m=u+300|0;n=u+296|0;q=u+292|0;p=u+288|0;s=u+280|0;t=u+272|0;r=u+136|0;o=u;c[j>>2]=a;c[k>>2]=b;g[l>>2]=d;c[m>>2]=e;c[n>>2]=f;oj(r|0,0,136)|0;oj(o|0,0,136)|0;c[q>>2]=0;while(1){if((c[q>>2]|0)>=(c[m>>2]|0))break;h[s>>3]=+g[(c[k>>2]|0)+(c[q>>2]<<2)>>2];c[p>>2]=0;while(1){if((c[p>>2]|0)>=(c[n>>2]|0))break;h[t>>3]=+h[r+(c[p>>2]<<3)>>3]+ +g[l>>2]*(+h[r+((c[p>>2]|0)+1<<3)>>3]-+h[s>>3]);h[r+(c[p>>2]<<3)>>3]=+h[s>>3];f=o+(c[p>>2]<<3)|0;h[f>>3]=+h[f>>3]+ +h[r>>3]*+h[s>>3];h[s>>3]=+h[r+((c[p>>2]|0)+1<<3)>>3]+ +g[l>>2]*(+h[r+((c[p>>2]|0)+2<<3)>>3]-+h[t>>3]);h[r+((c[p>>2]|0)+1<<3)>>3]=+h[t>>3];f=o+((c[p>>2]|0)+1<<3)|0;h[f>>3]=+h[f>>3]+ +h[r>>3]*+h[t>>3];c[p>>2]=(c[p>>2]|0)+2}h[r+(c[n>>2]<<3)>>3]=+h[s>>3];f=o+(c[n>>2]<<3)|0;h[f>>3]=+h[f>>3]+ +h[r>>3]*+h[s>>3];c[q>>2]=(c[q>>2]|0)+1}c[p>>2]=0;while(1){if((c[p>>2]|0)>=((c[n>>2]|0)+1|0))break;g[(c[j>>2]|0)+(c[p>>2]<<2)>>2]=+h[o+(c[p>>2]<<3)>>3];c[p>>2]=(c[p>>2]|0)+1}i=u;return}function xh(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,h=0,j=0,k=0,l=0;l=i;i=i+80|0;e=l+76|0;f=l+72|0;h=l+68|0;k=l+64|0;j=l;c[e>>2]=a;c[f>>2]=b;c[h>>2]=d;c[k>>2]=0;while(1){if((c[k>>2]|0)>=(c[h>>2]|0))break;b=yh(+g[(c[f>>2]|0)+(c[k>>2]<<2)>>2]*65536.0)|0;c[j+(c[k>>2]<<2)>>2]=b;c[k>>2]=(c[k>>2]|0)+1}Hf(c[e>>2]|0,j,c[h>>2]|0);i=l;return}function yh(a){a=+a;var b=0,c=0;c=i;i=i+16|0;b=c;g[b>>2]=a;b=ij(+g[b>>2])|0;i=c;return b|0}function zh(a,d,e){a=a|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0,m=0;l=i;i=i+48|0;f=l+12|0;m=l+8|0;h=l+4|0;k=l;j=l+16|0;c[f>>2]=a;c[m>>2]=d;c[h>>2]=e;ag(j,c[m>>2]|0,c[h>>2]|0);c[k>>2]=0;while(1){if((c[k>>2]|0)>=(c[h>>2]|0))break;g[(c[f>>2]|0)+(c[k>>2]<<2)>>2]=+(b[j+(c[k>>2]<<1)>>1]|0)*.000244140625;c[k>>2]=(c[k>>2]|0)+1}i=l;return}function Ah(a,d,e,f){a=a|0;d=d|0;e=e|0;f=f|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;n=i;i=i+96|0;h=n+20|0;j=n+16|0;p=n+12|0;o=n+8|0;l=n+4|0;m=n;k=n+24|0;c[h>>2]=a;c[j>>2]=d;c[p>>2]=e;c[o>>2]=f;pf(c[h>>2]|0,k,c[p>>2]|0,c[o>>2]|0);c[m>>2]=0;while(1){if((c[m>>2]|0)>=2)break;c[l>>2]=0;while(1){if((c[l>>2]|0)>=(c[(c[h>>2]|0)+4664>>2]|0))break;g[(c[j>>2]|0)+(c[m>>2]<<6)+(c[l>>2]<<2)>>2]=+(b[k+(c[m>>2]<<5)+(c[l>>2]<<1)>>1]|0)*.000244140625;c[l>>2]=(c[l>>2]|0)+1}c[m>>2]=(c[m>>2]|0)+1}i=n;return}function Bh(d,e,f,h,j,k){d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;var l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0;D=i;i=i+1632|0;l=D+1388|0;m=D+1384|0;n=D+1380|0;o=D+1376|0;p=D+1372|0;q=D+1368|0;A=D+1364|0;B=D+1360|0;C=D+80|0;s=D+64|0;y=D+1560|0;v=D+1520|0;w=D+56|0;r=D+1392|0;u=D+40|0;x=D+32|0;z=D+16|0;t=D;c[l>>2]=d;c[m>>2]=e;c[n>>2]=f;c[o>>2]=h;c[p>>2]=j;c[q>>2]=k;c[A>>2]=0;while(1){if((c[A>>2]|0)>=(c[(c[l>>2]|0)+4604>>2]|0))break;c[B>>2]=0;while(1){d=c[A>>2]|0;if((c[B>>2]|0)>=(c[(c[l>>2]|0)+4660>>2]|0))break;k=(yh(+g[(c[m>>2]|0)+500+((d<<4)+(c[B>>2]|0)<<2)>>2]*8192.0)|0)&65535;b[r+((c[A>>2]<<4)+(c[B>>2]|0)<<1)>>1]=k;c[B>>2]=(c[B>>2]|0)+1}c[A>>2]=d+1}c[A>>2]=0;while(1){if((c[A>>2]|0)>=(c[(c[l>>2]|0)+4604>>2]|0))break;k=(yh(+g[(c[m>>2]|0)+772+(c[A>>2]<<2)>>2]*16384.0)|0)<<16;k=k|(yh(+g[(c[m>>2]|0)+756+(c[A>>2]<<2)>>2]*16384.0)|0)&65535;c[u+(c[A>>2]<<2)>>2]=k;k=yh(+g[(c[m>>2]|0)+820+(c[A>>2]<<2)>>2]*16384.0)|0;c[z+(c[A>>2]<<2)>>2]=k;k=yh(+g[(c[m>>2]|0)+836+(c[A>>2]<<2)>>2]*16384.0)|0;c[t+(c[A>>2]<<2)>>2]=k;c[A>>2]=(c[A>>2]|0)+1}c[x>>2]=yh(+g[(c[m>>2]|0)+852>>2]*1024.0)|0;c[A>>2]=0;while(1){if((c[A>>2]|0)>=((c[(c[l>>2]|0)+4604>>2]|0)*5|0))break;k=(yh(+g[(c[m>>2]|0)+144+(c[A>>2]<<2)>>2]*16384.0)|0)&65535;b[v+(c[A>>2]<<1)>>1]=k;c[A>>2]=(c[A>>2]|0)+1}c[B>>2]=0;while(1){k=(c[B>>2]|0)<2;c[A>>2]=0;if(!k)break;while(1){if((c[A>>2]|0)>=(c[(c[l>>2]|0)+4664>>2]|0))break;k=(yh(+g[(c[m>>2]|0)+16+(c[B>>2]<<6)+(c[A>>2]<<2)>>2]*4096.0)|0)&65535;b[y+(c[B>>2]<<5)+(c[A>>2]<<1)>>1]=k;c[A>>2]=(c[A>>2]|0)+1}c[B>>2]=(c[B>>2]|0)+1}while(1){if((c[A>>2]|0)>=(c[(c[l>>2]|0)+4604>>2]|0))break;B=yh(+g[(c[m>>2]|0)+(c[A>>2]<<2)>>2]*65536.0)|0;c[s+(c[A>>2]<<2)>>2]=B;c[A>>2]=(c[A>>2]|0)+1}if((a[(c[n>>2]|0)+29>>0]|0)==2)c[w>>2]=b[24566+(a[(c[n>>2]|0)+33>>0]<<1)>>1];else c[w>>2]=0;c[A>>2]=0;while(1){if((c[A>>2]|0)>=(c[(c[l>>2]|0)+4608>>2]|0))break;B=yh(+g[(c[q>>2]|0)+(c[A>>2]<<2)>>2]*8.0)|0;c[C+(c[A>>2]<<2)>>2]=B;c[A>>2]=(c[A>>2]|0)+1}if((c[(c[l>>2]|0)+4652>>2]|0)<=1?(c[(c[l>>2]|0)+4704>>2]|0)<=0:0){me(c[l>>2]|0,c[o>>2]|0,c[n>>2]|0,C,c[p>>2]|0,y,v,r,t,z,u,s,(c[m>>2]|0)+228|0,c[x>>2]|0,c[w>>2]|0);i=D;return}se(c[l>>2]|0,c[o>>2]|0,c[n>>2]|0,C,c[p>>2]|0,y,v,r,t,z,u,s,(c[m>>2]|0)+228|0,c[x>>2]|0,c[w>>2]|0);i=D;return}function Ch(a,d,e,f,h,j,k,l,m){a=a|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;var n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0;z=i;i=i+480|0;n=z+436|0;o=z+432|0;p=z+428|0;q=z+424|0;r=z+420|0;s=z+416|0;t=z+412|0;u=z+408|0;v=z+404|0;y=z+400|0;w=z+440|0;x=z;c[n>>2]=a;c[o>>2]=d;c[p>>2]=e;c[q>>2]=f;c[r>>2]=h;c[s>>2]=j;c[t>>2]=k;c[u>>2]=l;c[v>>2]=m;c[y>>2]=0;while(1){if((c[y>>2]|0)>=((c[u>>2]|0)*5|0))break;f=(yh(+g[(c[n>>2]|0)+(c[y>>2]<<2)>>2]*16384.0)|0)&65535;b[w+(c[y>>2]<<1)>>1]=f;c[y>>2]=(c[y>>2]|0)+1}c[y>>2]=0;while(1){if((c[y>>2]|0)>=(((c[u>>2]|0)*5|0)*5|0))break;f=yh(+g[(c[r>>2]|0)+(c[y>>2]<<2)>>2]*262144.0)|0;c[x+(c[y>>2]<<2)>>2]=f;c[y>>2]=(c[y>>2]|0)+1}df(w,c[o>>2]|0,c[p>>2]|0,c[q>>2]|0,x,c[s>>2]|0,c[t>>2]|0,c[u>>2]|0,c[v>>2]|0);c[y>>2]=0;while(1){if((c[y>>2]|0)>=((c[u>>2]|0)*5|0))break;g[(c[n>>2]|0)+(c[y>>2]<<2)>>2]=+(b[w+(c[y>>2]<<1)>>1]|0)*.00006103515625;c[y>>2]=(c[y>>2]|0)+1}i=z;return}function Dh(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0,m=0,n=0.0;m=i;i=i+32|0;f=m+16|0;h=m+12|0;j=m+8|0;k=m+4|0;l=m;c[f>>2]=a;c[h>>2]=b;c[j>>2]=d;c[k>>2]=e;if((c[k>>2]|0)>(c[j>>2]|0))c[k>>2]=c[j>>2];c[l>>2]=0;while(1){if((c[l>>2]|0)>=(c[k>>2]|0))break;n=+Hh(c[h>>2]|0,(c[h>>2]|0)+(c[l>>2]<<2)|0,(c[j>>2]|0)-(c[l>>2]|0)|0);g[(c[f>>2]|0)+(c[l>>2]<<2)>>2]=n;c[l>>2]=(c[l>>2]|0)+1}i=m;return}function Eh(a,b,d,e,f,j){a=a|0;b=b|0;d=+d;e=e|0;f=f|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0;K=i;i=i+784|0;k=K+768|0;l=K+764|0;m=K+760|0;n=K+756|0;o=K+752|0;p=K+748|0;y=K+744|0;z=K+740|0;F=K+736|0;E=K+732|0;s=K+720|0;x=K+712|0;C=K+704|0;B=K+696|0;A=K+688|0;D=K+680|0;r=K+672|0;G=K+664|0;H=K+656|0;I=K+728|0;v=K+528|0;w=K+400|0;u=K+264|0;t=K+128|0;q=K;c[k>>2]=a;c[l>>2]=b;g[m>>2]=d;c[n>>2]=e;c[o>>2]=f;c[p>>2]=j;h[s>>3]=+Gh(c[l>>2]|0,_(c[o>>2]|0,c[n>>2]|0)|0);a=v;f=a+128|0;do{c[a>>2]=0;a=a+4|0}while((a|0)<(f|0));c[F>>2]=0;while(1){if((c[F>>2]|0)>=(c[o>>2]|0))break;c[I>>2]=(c[l>>2]|0)+((_(c[F>>2]|0,c[n>>2]|0)|0)<<2);c[z>>2]=1;while(1){if((c[z>>2]|0)>=((c[p>>2]|0)+1|0))break;d=+Hh(c[I>>2]|0,(c[I>>2]|0)+(c[z>>2]<<2)|0,(c[n>>2]|0)-(c[z>>2]|0)|0);b=v+((c[z>>2]|0)-1<<3)|0;h[b>>3]=+h[b>>3]+d;c[z>>2]=(c[z>>2]|0)+1}c[F>>2]=(c[F>>2]|0)+1}a=w;j=v;f=a+128|0;do{c[a>>2]=c[j>>2];a=a+4|0;j=j+4|0}while((a|0)<(f|0));d=+h[s>>3]+ +h[s>>3]*9.999999747378752e-06+9.999999717180685e-10;h[u>>3]=d;h[t>>3]=d;h[x>>3]=1.0;c[E>>2]=0;c[z>>2]=0;while(1){if((c[z>>2]|0)>=(c[p>>2]|0))break;c[F>>2]=0;while(1){if((c[F>>2]|0)>=(c[o>>2]|0))break;c[I>>2]=(c[l>>2]|0)+((_(c[F>>2]|0,c[n>>2]|0)|0)<<2);h[G>>3]=+g[(c[I>>2]|0)+(c[z>>2]<<2)>>2];h[H>>3]=+g[(c[I>>2]|0)+((c[n>>2]|0)-(c[z>>2]|0)-1<<2)>>2];c[y>>2]=0;while(1){if((c[y>>2]|0)>=(c[z>>2]|0))break;b=v+(c[y>>2]<<3)|0;h[b>>3]=+h[b>>3]-+g[(c[I>>2]|0)+(c[z>>2]<<2)>>2]*+g[(c[I>>2]|0)+((c[z>>2]|0)-(c[y>>2]|0)-1<<2)>>2];b=w+(c[y>>2]<<3)|0;h[b>>3]=+h[b>>3]-+g[(c[I>>2]|0)+((c[n>>2]|0)-(c[z>>2]|0)-1<<2)>>2]*+g[(c[I>>2]|0)+((c[n>>2]|0)-(c[z>>2]|0)+(c[y>>2]|0)<<2)>>2];h[r>>3]=+h[q+(c[y>>2]<<3)>>3];h[G>>3]=+h[G>>3]+ +g[(c[I>>2]|0)+((c[z>>2]|0)-(c[y>>2]|0)-1<<2)>>2]*+h[r>>3];h[H>>3]=+h[H>>3]+ +g[(c[I>>2]|0)+((c[n>>2]|0)-(c[z>>2]|0)+(c[y>>2]|0)<<2)>>2]*+h[r>>3];c[y>>2]=(c[y>>2]|0)+1}c[y>>2]=0;while(1){if((c[y>>2]|0)>(c[z>>2]|0))break;b=u+(c[y>>2]<<3)|0;h[b>>3]=+h[b>>3]-+h[G>>3]*+g[(c[I>>2]|0)+((c[z>>2]|0)-(c[y>>2]|0)<<2)>>2];b=t+(c[y>>2]<<3)|0;h[b>>3]=+h[b>>3]-+h[H>>3]*+g[(c[I>>2]|0)+((c[n>>2]|0)-(c[z>>2]|0)+(c[y>>2]|0)-1<<2)>>2];c[y>>2]=(c[y>>2]|0)+1}c[F>>2]=(c[F>>2]|0)+1}h[G>>3]=+h[v+(c[z>>2]<<3)>>3];h[H>>3]=+h[w+(c[z>>2]<<3)>>3];c[y>>2]=0;while(1){if((c[y>>2]|0)>=(c[z>>2]|0))break;h[r>>3]=+h[q+(c[y>>2]<<3)>>3];h[G>>3]=+h[G>>3]+ +h[w+((c[z>>2]|0)-(c[y>>2]|0)-1<<3)>>3]*+h[r>>3];h[H>>3]=+h[H>>3]+ +h[v+((c[z>>2]|0)-(c[y>>2]|0)-1<<3)>>3]*+h[r>>3];c[y>>2]=(c[y>>2]|0)+1}h[u+((c[z>>2]|0)+1<<3)>>3]=+h[G>>3];h[t+((c[z>>2]|0)+1<<3)>>3]=+h[H>>3];h[C>>3]=+h[t+((c[z>>2]|0)+1<<3)>>3];h[A>>3]=+h[t>>3];h[B>>3]=+h[u>>3];c[y>>2]=0;while(1){if((c[y>>2]|0)>=(c[z>>2]|0))break;h[r>>3]=+h[q+(c[y>>2]<<3)>>3];h[C>>3]=+h[C>>3]+ +h[t+((c[z>>2]|0)-(c[y>>2]|0)<<3)>>3]*+h[r>>3];h[A>>3]=+h[A>>3]+ +h[t+((c[y>>2]|0)+1<<3)>>3]*+h[r>>3];h[B>>3]=+h[B>>3]+ +h[u+((c[y>>2]|0)+1<<3)>>3]*+h[r>>3];c[y>>2]=(c[y>>2]|0)+1}h[D>>3]=+h[C>>3]*-2.0/(+h[B>>3]+ +h[A>>3]);h[G>>3]=+h[x>>3]*(1.0-+h[D>>3]*+h[D>>3]);if(+h[G>>3]<=+g[m>>2]){h[D>>3]=+O(+(1.0-+g[m>>2]/+h[x>>3]));if(+h[C>>3]>0.0)h[D>>3]=-+h[D>>3];h[x>>3]=+g[m>>2];c[E>>2]=1}else h[x>>3]=+h[G>>3];c[y>>2]=0;while(1){if((c[y>>2]|0)>=((c[z>>2]|0)+1>>1|0))break;h[G>>3]=+h[q+(c[y>>2]<<3)>>3];h[H>>3]=+h[q+((c[z>>2]|0)-(c[y>>2]|0)-1<<3)>>3];h[q+(c[y>>2]<<3)>>3]=+h[G>>3]+ +h[D>>3]*+h[H>>3];h[q+((c[z>>2]|0)-(c[y>>2]|0)-1<<3)>>3]=+h[H>>3]+ +h[D>>3]*+h[G>>3];c[y>>2]=(c[y>>2]|0)+1}h[q+(c[z>>2]<<3)>>3]=+h[D>>3];if(c[E>>2]|0){J=33;break}c[y>>2]=0;while(1){if((c[y>>2]|0)>((c[z>>2]|0)+1|0))break;h[G>>3]=+h[u+(c[y>>2]<<3)>>3];b=u+(c[y>>2]<<3)|0;h[b>>3]=+h[b>>3]+ +h[D>>3]*+h[t+((c[z>>2]|0)-(c[y>>2]|0)+1<<3)>>3];b=t+((c[z>>2]|0)-(c[y>>2]|0)+1<<3)|0;h[b>>3]=+h[b>>3]+ +h[D>>3]*+h[G>>3];c[y>>2]=(c[y>>2]|0)+1}c[z>>2]=(c[z>>2]|0)+1}a:do if((J|0)==33){c[y>>2]=(c[z>>2]|0)+1;while(1){if((c[y>>2]|0)>=(c[p>>2]|0))break a;h[q+(c[y>>2]<<3)>>3]=0.0;c[y>>2]=(c[y>>2]|0)+1}}while(0);if(!(c[E>>2]|0)){h[B>>3]=+h[u>>3];h[G>>3]=1.0;c[y>>2]=0;while(1){if((c[y>>2]|0)>=(c[p>>2]|0))break;h[r>>3]=+h[q+(c[y>>2]<<3)>>3];h[B>>3]=+h[B>>3]+ +h[u+((c[y>>2]|0)+1<<3)>>3]*+h[r>>3];h[G>>3]=+h[G>>3]+ +h[r>>3]*+h[r>>3];g[(c[k>>2]|0)+(c[y>>2]<<2)>>2]=-+h[r>>3];c[y>>2]=(c[y>>2]|0)+1}h[B>>3]=+h[B>>3]-+h[s>>3]*9.999999747378752e-06*+h[G>>3];d=+h[B>>3];i=K;return +d}c[y>>2]=0;while(1){if((c[y>>2]|0)>=(c[p>>2]|0))break;g[(c[k>>2]|0)+(c[y>>2]<<2)>>2]=-+h[q+(c[y>>2]<<3)>>3];c[y>>2]=(c[y>>2]|0)+1}c[F>>2]=0;while(1){if((c[F>>2]|0)>=(c[o>>2]|0))break;J=(c[l>>2]|0)+((_(c[F>>2]|0,c[n>>2]|0)|0)<<2)|0;d=+Gh(J,c[p>>2]|0);h[s>>3]=+h[s>>3]-d;c[F>>2]=(c[F>>2]|0)+1}h[B>>3]=+h[s>>3]*+h[x>>3];d=+h[B>>3];i=K;return +d}function Fh(a,b,d){a=a|0;b=b|0;d=+d;var e=0,f=0,h=0,j=0,k=0,l=0;l=i;i=i+32|0;e=l+16|0;f=l+12|0;h=l+8|0;k=l+4|0;j=l;c[e>>2]=a;c[f>>2]=b;g[h>>2]=d;g[j>>2]=+g[h>>2];c[k>>2]=0;while(1){d=+g[j>>2];if((c[k>>2]|0)>=((c[f>>2]|0)-1|0))break;b=(c[e>>2]|0)+(c[k>>2]<<2)|0;g[b>>2]=+g[b>>2]*d;g[j>>2]=+g[j>>2]*+g[h>>2];c[k>>2]=(c[k>>2]|0)+1}k=(c[e>>2]|0)+((c[f>>2]|0)-1<<2)|0;g[k>>2]=+g[k>>2]*d;i=l;return}function Gh(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,j=0,k=0,l=0;l=i;i=i+32|0;d=l+20|0;e=l+16|0;j=l+12|0;f=l+8|0;k=l;c[d>>2]=a;c[e>>2]=b;h[k>>3]=0.0;c[f>>2]=c[e>>2]&65532;c[j>>2]=0;while(1){if((c[j>>2]|0)>=(c[f>>2]|0))break;h[k>>3]=+h[k>>3]+(+g[(c[d>>2]|0)+((c[j>>2]|0)+0<<2)>>2]*+g[(c[d>>2]|0)+((c[j>>2]|0)+0<<2)>>2]+ +g[(c[d>>2]|0)+((c[j>>2]|0)+1<<2)>>2]*+g[(c[d>>2]|0)+((c[j>>2]|0)+1<<2)>>2]+ +g[(c[d>>2]|0)+((c[j>>2]|0)+2<<2)>>2]*+g[(c[d>>2]|0)+((c[j>>2]|0)+2<<2)>>2]+ +g[(c[d>>2]|0)+((c[j>>2]|0)+3<<2)>>2]*+g[(c[d>>2]|0)+((c[j>>2]|0)+3<<2)>>2]);c[j>>2]=(c[j>>2]|0)+4}while(1){if((c[j>>2]|0)>=(c[e>>2]|0))break;h[k>>3]=+h[k>>3]+ +g[(c[d>>2]|0)+(c[j>>2]<<2)>>2]*+g[(c[d>>2]|0)+(c[j>>2]<<2)>>2];c[j>>2]=(c[j>>2]|0)+1}i=l;return +(+h[k>>3])}function Hh(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,j=0,k=0,l=0,m=0,n=0;n=i;i=i+32|0;e=n+24|0;f=n+20|0;j=n+16|0;l=n+12|0;k=n+8|0;m=n;c[e>>2]=a;c[f>>2]=b;c[j>>2]=d;h[m>>3]=0.0;c[k>>2]=c[j>>2]&65532;c[l>>2]=0;while(1){if((c[l>>2]|0)>=(c[k>>2]|0))break;h[m>>3]=+h[m>>3]+(+g[(c[e>>2]|0)+((c[l>>2]|0)+0<<2)>>2]*+g[(c[f>>2]|0)+((c[l>>2]|0)+0<<2)>>2]+ +g[(c[e>>2]|0)+((c[l>>2]|0)+1<<2)>>2]*+g[(c[f>>2]|0)+((c[l>>2]|0)+1<<2)>>2]+ +g[(c[e>>2]|0)+((c[l>>2]|0)+2<<2)>>2]*+g[(c[f>>2]|0)+((c[l>>2]|0)+2<<2)>>2]+ +g[(c[e>>2]|0)+((c[l>>2]|0)+3<<2)>>2]*+g[(c[f>>2]|0)+((c[l>>2]|0)+3<<2)>>2]);c[l>>2]=(c[l>>2]|0)+4}while(1){if((c[l>>2]|0)>=(c[j>>2]|0))break;h[m>>3]=+h[m>>3]+ +g[(c[e>>2]|0)+(c[l>>2]<<2)>>2]*+g[(c[f>>2]|0)+(c[l>>2]<<2)>>2];c[l>>2]=(c[l>>2]|0)+1}i=n;return +(+h[m>>3])}function Ih(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,h=0,j=0,k=0,l=0,m=0;m=i;i=i+96|0;e=m+80|0;f=m+76|0;h=m+72|0;k=m+68|0;l=m+64|0;j=m;c[e>>2]=a;c[f>>2]=b;c[h>>2]=d;c[k>>2]=0;while(1){if((c[k>>2]|0)>=(c[h>>2]|0))break;c[l>>2]=0;while(1){if((c[l>>2]|0)>=(c[k>>2]|0))break;g[j+(c[l>>2]<<2)>>2]=+g[(c[e>>2]|0)+(c[l>>2]<<2)>>2];c[l>>2]=(c[l>>2]|0)+1}c[l>>2]=0;while(1){a=c[k>>2]|0;if((c[l>>2]|0)>=(c[k>>2]|0))break;b=(c[e>>2]|0)+(c[l>>2]<<2)|0;g[b>>2]=+g[b>>2]+ +g[j+(a-(c[l>>2]|0)-1<<2)>>2]*+g[(c[f>>2]|0)+(c[k>>2]<<2)>>2];c[l>>2]=(c[l>>2]|0)+1}g[(c[e>>2]|0)+(c[k>>2]<<2)>>2]=-+g[(c[f>>2]|0)+(a<<2)>>2];c[k>>2]=(c[k>>2]|0)+1}i=m;return}function Jh(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;s=i;i=i+48|0;e=s+44|0;f=s+40|0;h=s+36|0;l=s+32|0;o=s+28|0;n=s+24|0;p=s+20|0;q=s+16|0;r=s+12|0;m=s+8|0;j=s+4|0;k=s;c[e>>2]=a;c[f>>2]=b;c[h>>2]=d;g[p>>2]=+g[c[f>>2]>>2]*9.999999960041972e-13+9.999999717180685e-10;g[q>>2]=+g[c[f>>2]>>2];g[q>>2]=+g[p>>2]>+g[q>>2]?+g[p>>2]:+g[q>>2];g[c[e>>2]>>2]=+g[(c[f>>2]|0)+4>>2]/+g[q>>2];g[q>>2]=+g[q>>2]-+g[c[e>>2]>>2]*+g[(c[f>>2]|0)+4>>2];g[q>>2]=+g[p>>2]>+g[q>>2]?+g[p>>2]:+g[q>>2];c[n>>2]=1;while(1){if((c[n>>2]|0)>=(c[h>>2]|0))break;g[r>>2]=+g[(c[f>>2]|0)+((c[n>>2]|0)+1<<2)>>2];c[l>>2]=0;while(1){if((c[l>>2]|0)>=(c[n>>2]|0))break;g[r>>2]=+g[r>>2]-+g[(c[e>>2]|0)+(c[l>>2]<<2)>>2]*+g[(c[f>>2]|0)+((c[n>>2]|0)-(c[l>>2]|0)<<2)>>2];c[l>>2]=(c[l>>2]|0)+1}g[m>>2]=+g[r>>2]/+g[q>>2];g[q>>2]=+g[q>>2]-+g[m>>2]*+g[r>>2];g[q>>2]=+g[p>>2]>+g[q>>2]?+g[p>>2]:+g[q>>2];c[o>>2]=c[n>>2]>>1;c[l>>2]=0;while(1){if((c[l>>2]|0)>=(c[o>>2]|0))break;g[j>>2]=+g[(c[e>>2]|0)+(c[l>>2]<<2)>>2];g[k>>2]=+g[(c[e>>2]|0)+((c[n>>2]|0)-(c[l>>2]|0)-1<<2)>>2];d=(c[e>>2]|0)+((c[n>>2]|0)-(c[l>>2]|0)-1<<2)|0;g[d>>2]=+g[d>>2]-+g[m>>2]*+g[j>>2];d=(c[e>>2]|0)+(c[l>>2]<<2)|0;g[d>>2]=+g[d>>2]-+g[m>>2]*+g[k>>2];c[l>>2]=(c[l>>2]|0)+1}if(c[n>>2]&1|0){d=(c[e>>2]|0)+(c[o>>2]<<2)|0;g[d>>2]=+g[d>>2]-+g[m>>2]*+g[(c[e>>2]|0)+(c[o>>2]<<2)>>2]}g[(c[e>>2]|0)+(c[n>>2]<<2)>>2]=+g[m>>2];c[n>>2]=(c[n>>2]|0)+1}i=s;return +(+g[q>>2])}function Kh(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0.0;r=i;i=i+192|0;d=r+184|0;t=r+180|0;s=r+176|0;l=r+172|0;m=r+168|0;k=r+24|0;n=r+16|0;o=r+8|0;p=r;j=r+40|0;f=r+36|0;e=r+32|0;c[t>>2]=a;c[s>>2]=b;c[e>>2]=j+((c[s>>2]&1)<<6);pj(c[e>>2]|0,c[t>>2]|0,c[s>>2]<<2|0)|0;h[k>>3]=1.0;c[l>>2]=(c[s>>2]|0)-1;while(1){if((c[l>>2]|0)<=0)break;h[n>>3]=-+g[(c[e>>2]|0)+(c[l>>2]<<2)>>2];if(+h[n>>3]>.9998999834060669|+h[n>>3]<-.9998999834060669){q=4;break}h[o>>3]=1.0-+h[n>>3]*+h[n>>3];h[p>>3]=1.0/+h[o>>3];h[k>>3]=+h[k>>3]*+h[o>>3];c[f>>2]=c[e>>2];c[e>>2]=j+((c[l>>2]&1)<<6);c[m>>2]=0;while(1){if((c[m>>2]|0)>=(c[l>>2]|0))break;g[(c[e>>2]|0)+(c[m>>2]<<2)>>2]=(+g[(c[f>>2]|0)+(c[m>>2]<<2)>>2]-+g[(c[f>>2]|0)+((c[l>>2]|0)-(c[m>>2]|0)-1<<2)>>2]*+h[n>>3])*+h[p>>3];c[m>>2]=(c[m>>2]|0)+1}c[l>>2]=(c[l>>2]|0)+-1}if((q|0)==4){g[d>>2]=0.0;u=+g[d>>2];i=r;return +u}h[n>>3]=-+g[c[e>>2]>>2];if(+h[n>>3]>.9998999834060669|+h[n>>3]<-.9998999834060669){g[d>>2]=0.0;u=+g[d>>2];i=r;return +u}else{h[o>>3]=1.0-+h[n>>3]*+h[n>>3];h[k>>3]=+h[k>>3]*+h[o>>3];g[d>>2]=+h[k>>3];u=+g[d>>2];i=r;return +u}return 0.0}function Lh(d,e,f,j,k,l,m,n,o,p,q,r){d=d|0;e=e|0;f=f|0;j=j|0;k=k|0;l=l|0;m=+m;n=+n;o=o|0;p=p|0;q=q|0;r=r|0;var s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0,ha=0,ia=0,ja=0,ka=0,la=0,ma=0,na=0,oa=0,pa=0,qa=0,ra=0,sa=0,ta=0,ua=0,va=0,wa=0,xa=0,ya=0,za=0,Aa=0,Ba=0,Ca=0,Da=0,Ea=0,Fa=0,Ga=0,Ha=0,Ia=0,Ja=0,Ka=0,La=0;La=i;i=i+13936|0;Ca=La+10416|0;da=La+10412|0;Ea=La+10408|0;Fa=La+10404|0;Ga=La+10400|0;H=La+10396|0;I=La+10392|0;s=La+10388|0;J=La+10384|0;ya=La+10380|0;ea=La+10376|0;Da=La+10372|0;fa=La+10368|0;U=La+10364|0;Ka=La+10360|0;la=La+10356|0;qa=La+10352|0;T=La+9072|0;y=La+8432|0;z=La+13288|0;x=La+12968|0;u=La+8408|0;ba=La+8404|0;ia=La+8400|0;K=La+6016|0;G=La+5756|0;M=La+5712|0;xa=La+5708|0;P=La+5704|0;ja=La+24|0;E=La+16|0;oa=La+8|0;pa=La;R=La+5608|0;Q=La+12664|0;X=La+5600|0;W=La+5596|0;t=La+5592|0;ga=La+5588|0;N=La+5584|0;O=La+5580|0;ha=La+5576|0;Ha=La+5572|0;L=La+5568|0;za=La+5564|0;wa=La+5560|0;ma=La+5556|0;Aa=La+5552|0;Ja=La+5548|0;V=La+5544|0;$=La+5540|0;S=La+5536|0;na=La+2816|0;ka=La+96|0;ra=La+88|0;A=La+84|0;C=La+80|0;B=La+76|0;va=La+72|0;aa=La+68|0;F=La+64|0;Ba=La+60|0;ta=La+56|0;Z=La+52|0;sa=La+48|0;Y=La+44|0;D=La+40|0;ua=La+36|0;Ia=La+32|0;w=La+11384|0;v=La+10424|0;c[da>>2]=d;c[Ea>>2]=e;c[Fa>>2]=f;c[Ga>>2]=j;c[H>>2]=k;c[I>>2]=l;g[s>>2]=m;g[J>>2]=n;c[ya>>2]=o;c[ea>>2]=p;c[Da>>2]=q;c[fa>>2]=r;c[A>>2]=_(20+((c[Da>>2]|0)*5|0)|0,c[ya>>2]|0)|0;c[B>>2]=20+((c[Da>>2]|0)*5|0)<<2;c[C>>2]=20+((c[Da>>2]|0)*5|0)<<3;c[va>>2]=(c[ya>>2]|0)*5;c[F>>2]=20;c[aa>>2]=40;c[Ba>>2]=c[ya>>2]<<1;c[Z>>2]=8;c[ta>>2]=16;c[sa>>2]=((c[ya>>2]|0)*18|0)-1;c[D>>2]=72;c[Y>>2]=143;do if((c[ya>>2]|0)!=16)if((c[ya>>2]|0)==12){Mh(v,c[da>>2]|0,c[A>>2]|0);c[u>>2]=0;c[u+4>>2]=0;c[u+8>>2]=0;c[u+12>>2]=0;c[u+16>>2]=0;c[u+20>>2]=0;kg(u,z,v,c[A>>2]|0);Nh(T,z,c[C>>2]|0);break}else{Mh(z,c[da>>2]|0,c[C>>2]|0);break}else{Mh(w,c[da>>2]|0,c[A>>2]|0);c[u>>2]=0;c[u+4>>2]=0;lg(u,z,w,c[A>>2]|0);Nh(T,z,c[C>>2]|0)}while(0);c[u>>2]=0;c[u+4>>2]=0;lg(u,x,z,c[C>>2]|0);Nh(y,x,c[B>>2]|0);c[U>>2]=(c[B>>2]|0)-1;while(1){if((c[U>>2]|0)<=0)break;l=y+(c[U>>2]<<2)|0;g[l>>2]=+g[l>>2]+ +g[y+((c[U>>2]|0)-1<<2)>>2];c[U>>2]=(c[U>>2]|0)+-1}oj(K|0,0,(c[Da>>2]<<2)*149|0)|0;c[xa>>2]=y+(c[F>>2]<<2<<2);c[Ka>>2]=0;while(1){if((c[Ka>>2]|0)>=(c[Da>>2]>>1|0))break;c[P>>2]=(c[xa>>2]|0)+(0-(c[Z>>2]|0)<<2);Mc(c[xa>>2]|0,(c[xa>>2]|0)+(0-(c[D>>2]|0)<<2)|0,G,c[aa>>2]|0,(c[D>>2]|0)-(c[Z>>2]|0)+1|0,c[fa>>2]|0);h[ja>>3]=+g[G+((c[D>>2]|0)-(c[Z>>2]|0)<<2)>>2];n=+Gh(c[xa>>2]|0,c[aa>>2]|0);n=n+ +Gh(c[P>>2]|0,c[aa>>2]|0);h[E>>3]=n+ +(c[aa>>2]|0)*4.0e3;F=K+(c[Z>>2]<<2)|0;g[F>>2]=+g[F>>2]+ +h[ja>>3]*2.0/+h[E>>3];c[la>>2]=(c[Z>>2]|0)+1;while(1){if((c[la>>2]|0)>(c[D>>2]|0))break;c[P>>2]=(c[P>>2]|0)+-4;h[ja>>3]=+g[G+((c[D>>2]|0)-(c[la>>2]|0)<<2)>>2];h[E>>3]=+h[E>>3]+(+g[c[P>>2]>>2]*+g[c[P>>2]>>2]-+g[(c[P>>2]|0)+(c[aa>>2]<<2)>>2]*+g[(c[P>>2]|0)+(c[aa>>2]<<2)>>2]);F=K+(c[la>>2]<<2)|0;g[F>>2]=+g[F>>2]+ +h[ja>>3]*2.0/+h[E>>3];c[la>>2]=(c[la>>2]|0)+1}c[xa>>2]=(c[xa>>2]|0)+(c[aa>>2]<<2);c[Ka>>2]=(c[Ka>>2]|0)+1}c[U>>2]=c[D>>2];while(1){if((c[U>>2]|0)<(c[Z>>2]|0))break;G=K+(c[U>>2]<<2)|0;g[G>>2]=+g[G>>2]-+g[K+(c[U>>2]<<2)>>2]*+(c[U>>2]|0)/4096.0;c[U>>2]=(c[U>>2]|0)+-1}c[X>>2]=4+(c[ea>>2]<<1);Wh(K+(c[Z>>2]<<2)|0,R,(c[D>>2]|0)-(c[Z>>2]|0)+1|0,c[X>>2]|0);g[t>>2]=+g[K+(c[Z>>2]<<2)>>2];if(+g[t>>2]<.20000000298023224){oj(c[Ea>>2]|0,0,c[Da>>2]<<2|0)|0;g[c[H>>2]>>2]=0.0;b[c[Fa>>2]>>1]=0;a[c[Ga>>2]>>0]=0;c[Ca>>2]=1;Ka=c[Ca>>2]|0;i=La;return Ka|0}g[ba>>2]=+g[s>>2]*+g[t>>2];c[U>>2]=0;while(1){if((c[U>>2]|0)>=(c[X>>2]|0))break;s=c[U>>2]|0;if(!(+g[K+((c[Z>>2]|0)+(c[U>>2]|0)<<2)>>2]>+g[ba>>2])){ca=24;break}c[R+(c[U>>2]<<2)>>2]=(c[R+(s<<2)>>2]|0)+(c[Z>>2]|0)<<1;c[U>>2]=(c[U>>2]|0)+1}if((ca|0)==24)c[X>>2]=s;c[U>>2]=(c[ta>>2]|0)-5;while(1){if((c[U>>2]|0)>=((c[Y>>2]|0)+5|0))break;b[Q+(c[U>>2]<<1)>>1]=0;c[U>>2]=(c[U>>2]|0)+1}c[U>>2]=0;while(1){if((c[U>>2]|0)>=(c[X>>2]|0))break;b[Q+(c[R+(c[U>>2]<<2)>>2]<<1)>>1]=1;c[U>>2]=(c[U>>2]|0)+1}c[U>>2]=(c[Y>>2]|0)+3;while(1){if((c[U>>2]|0)<(c[ta>>2]|0))break;ca=Q+(c[U>>2]<<1)|0;b[ca>>1]=(b[ca>>1]|0)+((b[Q+((c[U>>2]|0)-1<<1)>>1]|0)+(b[Q+((c[U>>2]|0)-2<<1)>>1]|0));c[U>>2]=(c[U>>2]|0)+-1}c[X>>2]=0;c[U>>2]=c[ta>>2];while(1){if((c[U>>2]|0)>=((c[Y>>2]|0)+1|0))break;if((b[Q+((c[U>>2]|0)+1<<1)>>1]|0)>0){c[R+(c[X>>2]<<2)>>2]=c[U>>2];c[X>>2]=(c[X>>2]|0)+1}c[U>>2]=(c[U>>2]|0)+1}c[U>>2]=(c[Y>>2]|0)+3;while(1){if((c[U>>2]|0)<(c[ta>>2]|0))break;ca=Q+(c[U>>2]<<1)|0;b[ca>>1]=(b[ca>>1]|0)+((b[Q+((c[U>>2]|0)-1<<1)>>1]|0)+(b[Q+((c[U>>2]|0)-2<<1)>>1]|0)+(b[Q+((c[U>>2]|0)-3<<1)>>1]|0));c[U>>2]=(c[U>>2]|0)+-1}c[W>>2]=0;c[U>>2]=c[ta>>2];while(1){if((c[U>>2]|0)>=((c[Y>>2]|0)+4|0))break;if((b[Q+(c[U>>2]<<1)>>1]|0)>0){b[Q+(c[W>>2]<<1)>>1]=(c[U>>2]|0)-2;c[W>>2]=(c[W>>2]|0)+1}c[U>>2]=(c[U>>2]|0)+1}oj(K|0,0,2384)|0;if((c[ya>>2]|0)==8)c[xa>>2]=(c[da>>2]|0)+640;else c[xa>>2]=T+640;c[Ka>>2]=0;while(1){if((c[Ka>>2]|0)>=(c[Da>>2]|0))break;h[pa>>3]=+Gh(c[xa>>2]|0,c[aa>>2]|0)+1.0;c[qa>>2]=0;while(1){if((c[qa>>2]|0)>=(c[W>>2]|0))break;c[la>>2]=b[Q+(c[qa>>2]<<1)>>1];c[P>>2]=(c[xa>>2]|0)+(0-(c[la>>2]|0)<<2);h[ja>>3]=+Hh(c[P>>2]|0,c[xa>>2]|0,c[aa>>2]|0);if(+h[ja>>3]>0.0){h[oa>>3]=+Gh(c[P>>2]|0,c[aa>>2]|0);g[K+((c[Ka>>2]|0)*596|0)+(c[la>>2]<<2)>>2]=+h[ja>>3]*2.0/(+h[oa>>3]+ +h[pa>>3])}else g[K+((c[Ka>>2]|0)*596|0)+(c[la>>2]<<2)>>2]=0.0;c[qa>>2]=(c[qa>>2]|0)+1}c[xa>>2]=(c[xa>>2]|0)+(c[aa>>2]<<2);c[Ka>>2]=(c[Ka>>2]|0)+1}g[ga>>2]=0.0;g[N>>2]=-1.0e3;c[Ha>>2]=0;c[za>>2]=-1;if((c[I>>2]|0)>0){if((c[ya>>2]|0)!=12){if((c[ya>>2]|0)==16)c[I>>2]=c[I>>2]>>1}else c[I>>2]=(c[I>>2]<<1|0)/3|0;g[$>>2]=+Oh(+(c[I>>2]|0))}else g[$>>2]=0.0;do if((c[Da>>2]|0)==4){c[Ja>>2]=11;c[Ia>>2]=30286;if((c[ya>>2]|0)==8&(c[ea>>2]|0)>0){c[ua>>2]=11;break}else{c[ua>>2]=3;break}}else{c[Ja>>2]=3;c[Ia>>2]=30252;c[ua>>2]=3}while(0);c[Ka>>2]=0;while(1){if((c[Ka>>2]|0)>=(c[X>>2]|0))break;c[la>>2]=c[R+(c[Ka>>2]<<2)>>2];c[qa>>2]=0;while(1){if((c[qa>>2]|0)>=(c[ua>>2]|0))break;g[M+(c[qa>>2]<<2)>>2]=0.0;c[U>>2]=0;while(1){if((c[U>>2]|0)>=(c[Da>>2]|0))break;ba=_(c[U>>2]|0,c[Ja>>2]|0)|0;ca=M+(c[qa>>2]<<2)|0;g[ca>>2]=+g[ca>>2]+ +g[K+((c[U>>2]|0)*596|0)+((c[la>>2]|0)+(a[(c[Ia>>2]|0)+(ba+(c[qa>>2]|0))>>0]|0)<<2)>>2];c[U>>2]=(c[U>>2]|0)+1}c[qa>>2]=(c[qa>>2]|0)+1}g[ha>>2]=-1.0e3;c[L>>2]=0;c[U>>2]=0;while(1){if((c[U>>2]|0)>=(c[ua>>2]|0))break;if(+g[M+(c[U>>2]<<2)>>2]>+g[ha>>2]){g[ha>>2]=+g[M+(c[U>>2]<<2)>>2];c[L>>2]=c[U>>2]}c[U>>2]=(c[U>>2]|0)+1}g[V>>2]=+Oh(+(c[la>>2]|0));g[O>>2]=+g[ha>>2]-+(c[Da>>2]|0)*.20000000298023224*+g[V>>2];if((c[I>>2]|0)>0){g[S>>2]=+g[V>>2]-+g[$>>2];g[S>>2]=+g[S>>2]*+g[S>>2];g[O>>2]=+g[O>>2]-+(c[Da>>2]|0)*.20000000298023224*+g[c[H>>2]>>2]*+g[S>>2]/(+g[S>>2]+.5)}if(+g[O>>2]>+g[N>>2]?+g[ha>>2]>+(c[Da>>2]|0)*+g[J>>2]:0){g[N>>2]=+g[O>>2];g[ga>>2]=+g[ha>>2];c[za>>2]=c[la>>2];c[Ha>>2]=c[L>>2]}c[Ka>>2]=(c[Ka>>2]|0)+1}if((c[za>>2]|0)==-1){Ka=c[Ea>>2]|0;c[Ka>>2]=0;c[Ka+4>>2]=0;c[Ka+8>>2]=0;c[Ka+12>>2]=0;g[c[H>>2]>>2]=0.0;b[c[Fa>>2]>>1]=0;a[c[Ga>>2]>>0]=0;c[Ca>>2]=1;Ka=c[Ca>>2]|0;i=La;return Ka|0}g[c[H>>2]>>2]=+g[ga>>2]/+(c[Da>>2]|0);if((c[ya>>2]|0)>8){s=c[za>>2]|0;if((c[ya>>2]|0)==12)c[za>>2]=(((s&65535)<<16>>16)*3>>1)+(((c[za>>2]&65535)<<16>>16)*3&1);else c[za>>2]=s<<1;s=c[za>>2]|0;do if((c[Ba>>2]|0)>(c[sa>>2]|0))if((s|0)>(c[Ba>>2]|0)){s=c[Ba>>2]|0;break}else{s=(c[za>>2]|0)<(c[sa>>2]|0)?c[sa>>2]|0:c[za>>2]|0;break}else if((s|0)>(c[sa>>2]|0)){s=c[sa>>2]|0;break}else{s=(c[za>>2]|0)<(c[Ba>>2]|0)?c[Ba>>2]|0:c[za>>2]|0;break}while(0);c[za>>2]=s;c[wa>>2]=Ph((c[za>>2]|0)-2|0,c[Ba>>2]|0)|0;c[ma>>2]=Qh((c[za>>2]|0)+2|0,c[sa>>2]|0)|0;c[Aa>>2]=c[za>>2];c[Ha>>2]=0;g[ga>>2]=-1.0e3;Rh(ka,c[da>>2]|0,c[wa>>2]|0,c[va>>2]|0,c[Da>>2]|0,c[ea>>2]|0,c[fa>>2]|0);Sh(na,c[da>>2]|0,c[wa>>2]|0,c[va>>2]|0,c[Da>>2]|0,c[ea>>2]|0);c[ra>>2]=0;g[ia>>2]=.05000000074505806/+(c[za>>2]|0);if((c[Da>>2]|0)==4){c[ua>>2]=a[30490+(c[ea>>2]|0)>>0];c[Ja>>2]=34;c[Ia>>2]=30330}else{c[ua>>2]=12;c[Ja>>2]=12;c[Ia>>2]=30258}c[xa>>2]=(c[da>>2]|0)+((c[ya>>2]|0)*20<<2);h[pa>>3]=+Gh(c[xa>>2]|0,_(c[Da>>2]|0,c[va>>2]|0)|0)+1.0;c[la>>2]=c[wa>>2];while(1){if((c[la>>2]|0)>(c[ma>>2]|0))break;c[qa>>2]=0;while(1){if((c[qa>>2]|0)>=(c[ua>>2]|0))break;h[ja>>3]=0.0;h[oa>>3]=+h[pa>>3];c[Ka>>2]=0;while(1){if((c[Ka>>2]|0)>=(c[Da>>2]|0))break;h[ja>>3]=+h[ja>>3]+ +g[ka+((c[Ka>>2]|0)*680|0)+((c[qa>>2]|0)*20|0)+(c[ra>>2]<<2)>>2];h[oa>>3]=+h[oa>>3]+ +g[na+((c[Ka>>2]|0)*680|0)+((c[qa>>2]|0)*20|0)+(c[ra>>2]<<2)>>2];c[Ka>>2]=(c[Ka>>2]|0)+1}if(+h[ja>>3]>0.0){g[ha>>2]=+h[ja>>3]*2.0/+h[oa>>3];g[ha>>2]=+g[ha>>2]*(1.0-+g[ia>>2]*+(c[qa>>2]|0))}else g[ha>>2]=0.0;if(+g[ha>>2]>+g[ga>>2]?((c[la>>2]|0)+(a[30330+(c[qa>>2]|0)>>0]|0)|0)<=(c[sa>>2]|0):0){g[ga>>2]=+g[ha>>2];c[Aa>>2]=c[la>>2];c[Ha>>2]=c[qa>>2]}c[qa>>2]=(c[qa>>2]|0)+1}c[ra>>2]=(c[ra>>2]|0)+1;c[la>>2]=(c[la>>2]|0)+1}c[Ka>>2]=0;while(1){s=c[Aa>>2]|0;if((c[Ka>>2]|0)>=(c[Da>>2]|0))break;za=_(c[Ka>>2]|0,c[Ja>>2]|0)|0;c[(c[Ea>>2]|0)+(c[Ka>>2]<<2)>>2]=s+(a[(c[Ia>>2]|0)+(za+(c[Ha>>2]|0))>>0]|0);s=c[(c[Ea>>2]|0)+(c[Ka>>2]<<2)>>2]|0;do if((c[Ba>>2]|0)>((c[ya>>2]|0)*18|0)){if((s|0)>(c[Ba>>2]|0)){s=c[Ba>>2]|0;break}if((c[(c[Ea>>2]|0)+(c[Ka>>2]<<2)>>2]|0)<((c[ya>>2]|0)*18|0)){s=(c[ya>>2]|0)*18|0;break}else{s=c[(c[Ea>>2]|0)+(c[Ka>>2]<<2)>>2]|0;break}}else{if((s|0)>((c[ya>>2]|0)*18|0)){s=(c[ya>>2]|0)*18|0;break}if((c[(c[Ea>>2]|0)+(c[Ka>>2]<<2)>>2]|0)<(c[Ba>>2]|0)){s=c[Ba>>2]|0;break}else{s=c[(c[Ea>>2]|0)+(c[Ka>>2]<<2)>>2]|0;break}}while(0);c[(c[Ea>>2]|0)+(c[Ka>>2]<<2)>>2]=s;c[Ka>>2]=(c[Ka>>2]|0)+1}b[c[Fa>>2]>>1]=s-(c[Ba>>2]|0);a[c[Ga>>2]>>0]=c[Ha>>2]}else{c[Ka>>2]=0;while(1){s=c[za>>2]|0;if((c[Ka>>2]|0)>=(c[Da>>2]|0))break;Ba=_(c[Ka>>2]|0,c[Ja>>2]|0)|0;c[(c[Ea>>2]|0)+(c[Ka>>2]<<2)>>2]=s+(a[(c[Ia>>2]|0)+(Ba+(c[Ha>>2]|0))>>0]|0);s=c[(c[Ea>>2]|0)+(c[Ka>>2]<<2)>>2]|0;do if((c[ta>>2]|0)>144){if((s|0)>(c[ta>>2]|0)){s=c[ta>>2]|0;break}if((c[(c[Ea>>2]|0)+(c[Ka>>2]<<2)>>2]|0)<144)s=144;else s=c[(c[Ea>>2]|0)+(c[Ka>>2]<<2)>>2]|0}else if((s|0)<=144)if((c[(c[Ea>>2]|0)+(c[Ka>>2]<<2)>>2]|0)<(c[ta>>2]|0)){s=c[ta>>2]|0;break}else{s=c[(c[Ea>>2]|0)+(c[Ka>>2]<<2)>>2]|0;break}else s=144;while(0);c[(c[Ea>>2]|0)+(c[Ka>>2]<<2)>>2]=s;c[Ka>>2]=(c[Ka>>2]|0)+1}b[c[Fa>>2]>>1]=s-(c[ta>>2]|0);a[c[Ga>>2]>>0]=c[Ha>>2]}c[Ca>>2]=0;Ka=c[Ca>>2]|0;i=La;return Ka|0}function Mh(a,d,e){a=a|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0;k=i;i=i+16|0;f=k+12|0;h=k+8|0;l=k+4|0;j=k;c[f>>2]=a;c[h>>2]=d;c[l>>2]=e;c[j>>2]=(c[l>>2]|0)-1;while(1){if((c[j>>2]|0)<0)break;if((ij(+g[(c[h>>2]|0)+(c[j>>2]<<2)>>2])|0)<=32767)if((ij(+g[(c[h>>2]|0)+(c[j>>2]<<2)>>2])|0)<-32768)d=-32768;else d=ij(+g[(c[h>>2]|0)+(c[j>>2]<<2)>>2])|0;else d=32767;b[(c[f>>2]|0)+(c[j>>2]<<1)>>1]=d;c[j>>2]=(c[j>>2]|0)+-1}i=k;return}function Nh(a,d,e){a=a|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0;k=i;i=i+16|0;f=k+12|0;h=k+8|0;l=k+4|0;j=k;c[f>>2]=a;c[h>>2]=d;c[l>>2]=e;c[j>>2]=(c[l>>2]|0)-1;while(1){if((c[j>>2]|0)<0)break;g[(c[f>>2]|0)+(c[j>>2]<<2)>>2]=+(b[(c[h>>2]|0)+(c[j>>2]<<1)>>1]|0);c[j>>2]=(c[j>>2]|0)+-1}i=k;return}function Oh(a){a=+a;var b=0,c=0;b=i;i=i+16|0;c=b;h[c>>3]=a;a=+hj(+h[c>>3])*3.32192809488736;i=b;return +a}function Ph(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)>(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function Qh(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)<(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function Rh(b,d,e,f,h,j,k){b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;var l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0;H=i;i=i+256|0;l=H+252|0;m=H+248|0;n=H+244|0;o=H+240|0;p=H+236|0;q=H+232|0;r=H+228|0;F=H+224|0;w=H+220|0;y=H+216|0;z=H+212|0;A=H+208|0;C=H+204|0;B=H+200|0;D=H+196|0;v=H+192|0;x=H+188|0;u=H+184|0;E=H+96|0;G=H+8|0;t=H+4|0;s=H;c[l>>2]=b;c[m>>2]=d;c[n>>2]=e;c[o>>2]=f;c[p>>2]=h;c[q>>2]=j;c[r>>2]=k;if((c[p>>2]|0)==4){c[t>>2]=30466+(c[q>>2]<<3);c[s>>2]=30330;c[D>>2]=a[30490+(c[q>>2]|0)>>0];c[u>>2]=34}else{c[t>>2]=30282;c[s>>2]=30258;c[D>>2]=12;c[u>>2]=12}c[F>>2]=(c[m>>2]|0)+(c[o>>2]<<2<<2);c[z>>2]=0;while(1){if((c[z>>2]|0)>=(c[p>>2]|0))break;c[A>>2]=0;c[C>>2]=a[(c[t>>2]|0)+((c[z>>2]<<1)+0)>>0];c[B>>2]=a[(c[t>>2]|0)+((c[z>>2]<<1)+1)>>0];Mc(c[F>>2]|0,(c[F>>2]|0)+(0-(c[n>>2]|0)<<2)+(0-(c[B>>2]|0)<<2)|0,G,c[o>>2]|0,(c[B>>2]|0)-(c[C>>2]|0)+1|0,c[r>>2]|0);c[y>>2]=c[C>>2];while(1){if((c[y>>2]|0)>(c[B>>2]|0))break;g[E+(c[A>>2]<<2)>>2]=+g[G+((c[B>>2]|0)-(c[y>>2]|0)<<2)>>2];c[A>>2]=(c[A>>2]|0)+1;c[y>>2]=(c[y>>2]|0)+1}c[v>>2]=a[(c[t>>2]|0)+((c[z>>2]<<1)+0)>>0];c[w>>2]=0;while(1){if((c[w>>2]|0)>=(c[D>>2]|0))break;e=_(c[z>>2]|0,c[u>>2]|0)|0;c[x>>2]=(a[(c[s>>2]|0)+(e+(c[w>>2]|0))>>0]|0)-(c[v>>2]|0);c[y>>2]=0;while(1){if((c[y>>2]|0)>=5)break;g[(c[l>>2]|0)+((c[z>>2]|0)*680|0)+((c[w>>2]|0)*20|0)+(c[y>>2]<<2)>>2]=+g[E+((c[x>>2]|0)+(c[y>>2]|0)<<2)>>2];c[y>>2]=(c[y>>2]|0)+1}c[w>>2]=(c[w>>2]|0)+1}c[F>>2]=(c[F>>2]|0)+(c[o>>2]<<2);c[z>>2]=(c[z>>2]|0)+1}i=H;return}function Sh(b,d,e,f,j,k){b=b|0;d=d|0;e=e|0;f=f|0;j=j|0;k=k|0;var l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0;G=i;i=i+176|0;l=G+168|0;m=G+164|0;n=G+160|0;o=G+156|0;p=G+152|0;q=G+148|0;F=G+144|0;t=G+140|0;w=G;A=G+136|0;x=G+132|0;z=G+128|0;B=G+124|0;D=G+120|0;v=G+116|0;y=G+112|0;u=G+108|0;C=G+104|0;E=G+16|0;s=G+12|0;r=G+8|0;c[l>>2]=b;c[m>>2]=d;c[n>>2]=e;c[o>>2]=f;c[p>>2]=j;c[q>>2]=k;if((c[p>>2]|0)==4){c[s>>2]=30466+(c[q>>2]<<3);c[r>>2]=30330;c[D>>2]=a[30490+(c[q>>2]|0)>>0];c[u>>2]=34}else{c[s>>2]=30282;c[r>>2]=30258;c[D>>2]=12;c[u>>2]=12}c[F>>2]=(c[m>>2]|0)+(c[o>>2]<<2<<2);c[A>>2]=0;while(1){if((c[A>>2]|0)>=(c[p>>2]|0))break;c[B>>2]=0;c[t>>2]=(c[F>>2]|0)+(0-((c[n>>2]|0)+(a[(c[s>>2]|0)+((c[A>>2]<<1)+0)>>0]|0))<<2);h[w>>3]=+Gh(c[t>>2]|0,c[o>>2]|0)+.001;g[E+(c[B>>2]<<2)>>2]=+h[w>>3];c[B>>2]=(c[B>>2]|0)+1;c[C>>2]=(a[(c[s>>2]|0)+((c[A>>2]<<1)+1)>>0]|0)-(a[(c[s>>2]|0)+((c[A>>2]<<1)+0)>>0]|0)+1;c[x>>2]=1;while(1){if((c[x>>2]|0)>=(c[C>>2]|0))break;h[w>>3]=+h[w>>3]-+g[(c[t>>2]|0)+((c[o>>2]|0)-(c[x>>2]|0)<<2)>>2]*+g[(c[t>>2]|0)+((c[o>>2]|0)-(c[x>>2]|0)<<2)>>2];h[w>>3]=+h[w>>3]+ +g[(c[t>>2]|0)+(0-(c[x>>2]|0)<<2)>>2]*+g[(c[t>>2]|0)+(0-(c[x>>2]|0)<<2)>>2];g[E+(c[B>>2]<<2)>>2]=+h[w>>3];c[B>>2]=(c[B>>2]|0)+1;c[x>>2]=(c[x>>2]|0)+1}c[v>>2]=a[(c[s>>2]|0)+((c[A>>2]<<1)+0)>>0];c[x>>2]=0;while(1){if((c[x>>2]|0)>=(c[D>>2]|0))break;e=_(c[A>>2]|0,c[u>>2]|0)|0;c[y>>2]=(a[(c[r>>2]|0)+(e+(c[x>>2]|0))>>0]|0)-(c[v>>2]|0);c[z>>2]=0;while(1){if((c[z>>2]|0)>=5)break;g[(c[l>>2]|0)+((c[A>>2]|0)*680|0)+((c[x>>2]|0)*20|0)+(c[z>>2]<<2)>>2]=+g[E+((c[y>>2]|0)+(c[z>>2]|0)<<2)>>2];c[z>>2]=(c[z>>2]|0)+1}c[x>>2]=(c[x>>2]|0)+1}c[F>>2]=(c[F>>2]|0)+(c[o>>2]<<2);c[A>>2]=(c[A>>2]|0)+1}i=G;return}function Th(a,b,d,e){a=a|0;b=b|0;d=+d;e=e|0;var f=0,h=0,j=0,k=0,l=0,m=0,n=0;n=i;i=i+32|0;f=n+20|0;h=n+16|0;j=n+12|0;k=n+8|0;m=n+4|0;l=n;c[f>>2]=a;c[h>>2]=b;g[j>>2]=d;c[k>>2]=e;c[l>>2]=c[k>>2]&65532;c[m>>2]=0;while(1){if((c[m>>2]|0)>=(c[l>>2]|0))break;g[(c[f>>2]|0)+((c[m>>2]|0)+0<<2)>>2]=+g[j>>2]*+g[(c[h>>2]|0)+((c[m>>2]|0)+0<<2)>>2];g[(c[f>>2]|0)+((c[m>>2]|0)+1<<2)>>2]=+g[j>>2]*+g[(c[h>>2]|0)+((c[m>>2]|0)+1<<2)>>2];g[(c[f>>2]|0)+((c[m>>2]|0)+2<<2)>>2]=+g[j>>2]*+g[(c[h>>2]|0)+((c[m>>2]|0)+2<<2)>>2];g[(c[f>>2]|0)+((c[m>>2]|0)+3<<2)>>2]=+g[j>>2]*+g[(c[h>>2]|0)+((c[m>>2]|0)+3<<2)>>2];c[m>>2]=(c[m>>2]|0)+4}while(1){if((c[m>>2]|0)>=(c[k>>2]|0))break;g[(c[f>>2]|0)+(c[m>>2]<<2)>>2]=+g[j>>2]*+g[(c[h>>2]|0)+(c[m>>2]<<2)>>2];c[m>>2]=(c[m>>2]|0)+1}i=n;return}
+ function ra(a){a=a|0;var b=0;b=i;i=i+a|0;i=i+15&-16;return b|0}function sa(){return i|0}function ta(a){a=a|0;i=a}function ua(a,b){a=a|0;b=b|0;i=a;j=b}function va(a,b){a=a|0;b=b|0;if(!n){n=a;o=b}}function wa(b){b=b|0;a[k>>0]=a[b>>0];a[k+1>>0]=a[b+1>>0];a[k+2>>0]=a[b+2>>0];a[k+3>>0]=a[b+3>>0]}function xa(b){b=b|0;a[k>>0]=a[b>>0];a[k+1>>0]=a[b+1>>0];a[k+2>>0]=a[b+2>>0];a[k+3>>0]=a[b+3>>0];a[k+4>>0]=a[b+4>>0];a[k+5>>0]=a[b+5>>0];a[k+6>>0]=a[b+6>>0];a[k+7>>0]=a[b+7>>0]}function ya(a){a=a|0;C=a}function za(){return C|0}function Aa(a,b,d,e,f){a=+a;b=b|0;d=d|0;e=e|0;f=f|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0;o=i;i=i+32|0;h=o+20|0;j=o+16|0;k=o+12|0;l=o+8|0;m=o+4|0;n=o;g[h>>2]=a;c[j>>2]=b;c[k>>2]=d;c[l>>2]=e;c[m>>2]=f;c[n>>2]=0;while(1){if((c[n>>2]|0)>=(c[l>>2]|0))break;if(+g[h>>2]<+g[(c[j>>2]|0)+(c[n>>2]<<2)>>2])break;c[n>>2]=(c[n>>2]|0)+1}if((c[n>>2]|0)>(c[m>>2]|0)?+g[h>>2]<+g[(c[j>>2]|0)+(c[m>>2]<<2)>>2]+ +g[(c[k>>2]|0)+(c[m>>2]<<2)>>2]:0)c[n>>2]=c[m>>2];if((c[n>>2]|0)>=(c[m>>2]|0)){b=c[n>>2]|0;i=o;return b|0}if(!(+g[h>>2]>+g[(c[j>>2]|0)+((c[m>>2]|0)-1<<2)>>2]-+g[(c[k>>2]|0)+((c[m>>2]|0)-1<<2)>>2])){b=c[n>>2]|0;i=o;return b|0}c[n>>2]=c[m>>2];b=c[n>>2]|0;i=o;return b|0}function Ba(a){a=a|0;var b=0,d=0;b=i;i=i+16|0;d=b;c[d>>2]=a;a=(_(1664525,c[d>>2]|0)|0)+1013904223|0;i=b;return a|0}function Ca(a,d,e,f,h,j){a=a|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0.0;v=i;i=i+48|0;k=v+40|0;l=v+36|0;m=v+32|0;n=v+28|0;o=v+24|0;p=v+20|0;t=v+16|0;r=v+12|0;q=v+8|0;s=v+4|0;u=v;c[k>>2]=a;c[l>>2]=d;c[m>>2]=e;c[n>>2]=f;c[o>>2]=h;c[p>>2]=j;c[s>>2]=c[(c[k>>2]|0)+32>>2];c[q>>2]=c[(c[k>>2]|0)+44>>2]<<c[p>>2];c[r>>2]=0;do{c[t>>2]=0;while(1){h=c[r>>2]|0;if((c[t>>2]|0)>=(c[n>>2]|0))break;f=_(h,c[q>>2]|0)|0;a=_(c[r>>2]|0,c[q>>2]|0)|0;g[u>>2]=+Da((c[l>>2]|0)+(f+(b[(c[s>>2]|0)+(c[t>>2]<<1)>>1]<<c[p>>2])<<2)|0,(c[l>>2]|0)+(a+(b[(c[s>>2]|0)+(c[t>>2]<<1)>>1]<<c[p>>2])<<2)|0,(b[(c[s>>2]|0)+((c[t>>2]|0)+1<<1)>>1]|0)-(b[(c[s>>2]|0)+(c[t>>2]<<1)>>1]|0)<<c[p>>2])+1.0000000272452012e-27;w=+O(+(+g[u>>2]));a=(c[t>>2]|0)+(_(c[r>>2]|0,c[(c[k>>2]|0)+8>>2]|0)|0)|0;g[(c[m>>2]|0)+(a<<2)>>2]=w;c[t>>2]=(c[t>>2]|0)+1}a=h+1|0;c[r>>2]=a}while((a|0)<(c[o>>2]|0));i=v;return}function Da(a,b,d){a=a|0;b=b|0;d=d|0;var e=0.0,f=0,h=0,j=0,k=0,l=0,m=0;m=i;i=i+32|0;f=m+16|0;h=m+12|0;j=m+8|0;k=m+4|0;l=m;c[f>>2]=a;c[h>>2]=b;c[j>>2]=d;g[l>>2]=0.0;c[k>>2]=0;while(1){e=+g[l>>2];if((c[k>>2]|0)>=(c[j>>2]|0))break;g[l>>2]=e+ +g[(c[f>>2]|0)+(c[k>>2]<<2)>>2]*+g[(c[h>>2]|0)+(c[k>>2]<<2)>>2];c[k>>2]=(c[k>>2]|0)+1}i=m;return +e}function Ea(a,d,e,f,h,j,k){a=a|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;var l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0;y=i;i=i+64|0;l=y+48|0;m=y+44|0;n=y+40|0;o=y+36|0;p=y+32|0;q=y+28|0;r=y+24|0;w=y+20|0;t=y+16|0;s=y+12|0;u=y+8|0;x=y+4|0;v=y;c[l>>2]=a;c[m>>2]=d;c[n>>2]=e;c[o>>2]=f;c[p>>2]=h;c[q>>2]=j;c[r>>2]=k;c[u>>2]=c[(c[l>>2]|0)+32>>2];c[s>>2]=_(c[r>>2]|0,c[(c[l>>2]|0)+44>>2]|0)|0;c[t>>2]=0;do{c[w>>2]=0;while(1){if((c[w>>2]|0)>=(c[p>>2]|0))break;a=(c[w>>2]|0)+(_(c[t>>2]|0,c[(c[l>>2]|0)+8>>2]|0)|0)|0;g[v>>2]=1.0/(+g[(c[o>>2]|0)+(a<<2)>>2]+1.0000000272452012e-27);c[x>>2]=_(c[r>>2]|0,b[(c[u>>2]|0)+(c[w>>2]<<1)>>1]|0)|0;while(1){if((c[x>>2]|0)>=(_(c[r>>2]|0,b[(c[u>>2]|0)+((c[w>>2]|0)+1<<1)>>1]|0)|0))break;d=(c[x>>2]|0)+(_(c[t>>2]|0,c[s>>2]|0)|0)|0;a=(c[x>>2]|0)+(_(c[t>>2]|0,c[s>>2]|0)|0)|0;g[(c[n>>2]|0)+(a<<2)>>2]=+g[(c[m>>2]|0)+(d<<2)>>2]*+g[v>>2];c[x>>2]=(c[x>>2]|0)+1}c[w>>2]=(c[w>>2]|0)+1}a=(c[t>>2]|0)+1|0;c[t>>2]=a}while((a|0)<(c[q>>2]|0));i=y;return}function Fa(a,d,e,f,h,j,k,l,m){a=a|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;var n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0.0;F=i;i=i+80|0;G=F+72|0;o=F+68|0;p=F+64|0;q=F+60|0;r=F+56|0;s=F+52|0;t=F+48|0;n=F+44|0;u=F+40|0;B=F+36|0;v=F+32|0;x=F+28|0;z=F+24|0;E=F+20|0;y=F+16|0;C=F+12|0;w=F+8|0;A=F+4|0;D=F;c[G>>2]=a;c[o>>2]=d;c[p>>2]=e;c[q>>2]=f;c[r>>2]=h;c[s>>2]=j;c[t>>2]=k;c[n>>2]=l;c[u>>2]=m;c[y>>2]=c[(c[G>>2]|0)+32>>2];c[v>>2]=_(c[t>>2]|0,c[(c[G>>2]|0)+44>>2]|0)|0;c[x>>2]=_(c[t>>2]|0,b[(c[y>>2]|0)+(c[s>>2]<<1)>>1]|0)|0;if((c[n>>2]|0)!=1){if((c[x>>2]|0)<((c[v>>2]|0)/(c[n>>2]|0)|0|0))n=c[x>>2]|0;else n=(c[v>>2]|0)/(c[n>>2]|0)|0;c[x>>2]=n}if(c[u>>2]|0){c[x>>2]=0;c[s>>2]=0;c[r>>2]=0}c[z>>2]=c[p>>2];c[E>>2]=(c[o>>2]|0)+((_(c[t>>2]|0,b[(c[y>>2]|0)+(c[r>>2]<<1)>>1]|0)|0)<<2);c[B>>2]=0;while(1){if((c[B>>2]|0)>=(_(c[t>>2]|0,b[(c[y>>2]|0)+(c[r>>2]<<1)>>1]|0)|0))break;G=c[z>>2]|0;c[z>>2]=G+4;g[G>>2]=0.0;c[B>>2]=(c[B>>2]|0)+1}c[B>>2]=c[r>>2];while(1){if((c[B>>2]|0)>=(c[s>>2]|0))break;c[C>>2]=_(c[t>>2]|0,b[(c[y>>2]|0)+(c[B>>2]<<1)>>1]|0)|0;c[w>>2]=_(c[t>>2]|0,b[(c[y>>2]|0)+((c[B>>2]|0)+1<<1)>>1]|0)|0;g[D>>2]=+g[(c[q>>2]|0)+(c[B>>2]<<2)>>2]+ +g[17464+(c[B>>2]<<2)>>2];g[A>>2]=+X(+(+g[D>>2]*.6931471805599453));do{G=c[E>>2]|0;c[E>>2]=G+4;H=+g[G>>2]*+g[A>>2];G=c[z>>2]|0;c[z>>2]=G+4;g[G>>2]=H;G=(c[C>>2]|0)+1|0;c[C>>2]=G}while((G|0)<(c[w>>2]|0));c[B>>2]=(c[B>>2]|0)+1}oj((c[p>>2]|0)+(c[x>>2]<<2)|0,0,(c[v>>2]|0)-(c[x>>2]|0)<<2|0)|0;i=F;return}function Ga(a,e,f,h,j,k,l,m,n,o,p,q,r,s){a=a|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;p=p|0;q=q|0;r=r|0;s=s|0;var t=0.0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,Y=0;W=i;i=i+112|0;u=W+108|0;v=W+104|0;A=W+100|0;B=W+96|0;C=W+92|0;D=W+88|0;Y=W+84|0;E=W+80|0;F=W+76|0;G=W+72|0;w=W+68|0;x=W+64|0;y=W+60|0;z=W+56|0;K=W+52|0;M=W+48|0;N=W+44|0;P=W+40|0;I=W+36|0;V=W+32|0;U=W+28|0;L=W+24|0;J=W+20|0;Q=W+16|0;R=W+12|0;H=W+8|0;S=W+4|0;T=W;c[u>>2]=a;c[v>>2]=e;c[A>>2]=f;c[B>>2]=h;c[C>>2]=j;c[D>>2]=k;c[Y>>2]=l;c[E>>2]=m;c[F>>2]=n;c[G>>2]=o;c[w>>2]=p;c[x>>2]=q;c[y>>2]=r;c[z>>2]=s;c[M>>2]=c[Y>>2];while(1){if((c[M>>2]|0)>=(c[E>>2]|0))break;c[I>>2]=(b[(c[(c[u>>2]|0)+32>>2]|0)+((c[M>>2]|0)+1<<1)>>1]|0)-(b[(c[(c[u>>2]|0)+32>>2]|0)+(c[M>>2]<<1)>>1]|0);Y=Ha(1+(c[(c[x>>2]|0)+(c[M>>2]<<2)>>2]|0)|0,(b[(c[(c[u>>2]|0)+32>>2]|0)+((c[M>>2]|0)+1<<1)>>1]|0)-(b[(c[(c[u>>2]|0)+32>>2]|0)+(c[M>>2]<<1)>>1]|0)|0)|0;c[L>>2]=Y>>>(c[B>>2]|0);g[V>>2]=+X(+(+(c[L>>2]|0)*-.125*.6931471805599453))*.5;g[U>>2]=1.0/+O(+(+(c[I>>2]<<c[B>>2]|0)));c[K>>2]=0;do{c[T>>2]=0;Y=_(c[K>>2]|0,c[(c[u>>2]|0)+8>>2]|0)|0;g[Q>>2]=+g[(c[G>>2]|0)+(Y+(c[M>>2]|0)<<2)>>2];Y=_(c[K>>2]|0,c[(c[u>>2]|0)+8>>2]|0)|0;g[R>>2]=+g[(c[w>>2]|0)+(Y+(c[M>>2]|0)<<2)>>2];if((c[C>>2]|0)==1){if(+g[Q>>2]>+g[(c[G>>2]|0)+((c[(c[u>>2]|0)+8>>2]|0)+(c[M>>2]|0)<<2)>>2])t=+g[Q>>2];else t=+g[(c[G>>2]|0)+((c[(c[u>>2]|0)+8>>2]|0)+(c[M>>2]|0)<<2)>>2];g[Q>>2]=t;if(+g[R>>2]>+g[(c[w>>2]|0)+((c[(c[u>>2]|0)+8>>2]|0)+(c[M>>2]|0)<<2)>>2])t=+g[R>>2];else t=+g[(c[w>>2]|0)+((c[(c[u>>2]|0)+8>>2]|0)+(c[M>>2]|0)<<2)>>2];g[R>>2]=t}Y=_(c[K>>2]|0,c[(c[u>>2]|0)+8>>2]|0)|0;g[H>>2]=+g[(c[F>>2]|0)+(Y+(c[M>>2]|0)<<2)>>2]-(+g[Q>>2]<+g[R>>2]?+g[Q>>2]:+g[R>>2]);g[H>>2]=0.0>+g[H>>2]?0.0:+g[H>>2];g[S>>2]=+X(+(-+g[H>>2]*.6931471805599453))*2.0;if((c[B>>2]|0)==3)g[S>>2]=+g[S>>2]*1.4142135381698608;g[S>>2]=+g[V>>2]<+g[S>>2]?+g[V>>2]:+g[S>>2];g[S>>2]=+g[S>>2]*+g[U>>2];Y=(c[v>>2]|0)+((_(c[K>>2]|0,c[D>>2]|0)|0)<<2)|0;c[J>>2]=Y+(b[(c[(c[u>>2]|0)+32>>2]|0)+(c[M>>2]<<1)>>1]<<c[B>>2]<<2);c[P>>2]=0;while(1){if((c[P>>2]|0)>=(1<<c[B>>2]|0))break;Y=_(c[M>>2]|0,c[C>>2]|0)|0;if(!(d[(c[A>>2]|0)+(Y+(c[K>>2]|0))>>0]&1<<c[P>>2])){c[N>>2]=0;while(1){if((c[N>>2]|0)>=(c[I>>2]|0))break;c[y>>2]=Ba(c[y>>2]|0)|0;t=+g[S>>2];g[(c[J>>2]|0)+((c[N>>2]<<c[B>>2])+(c[P>>2]|0)<<2)>>2]=c[y>>2]&32768|0?t:-t;c[N>>2]=(c[N>>2]|0)+1}c[T>>2]=1}c[P>>2]=(c[P>>2]|0)+1}if(c[T>>2]|0)td(c[J>>2]|0,c[I>>2]<<c[B>>2],1.0,c[z>>2]|0);Y=(c[K>>2]|0)+1|0;c[K>>2]=Y}while((Y|0)<(c[C>>2]|0));c[M>>2]=(c[M>>2]|0)+1}i=W;return}function Ha(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)>>>0)/((c[d>>2]|0)>>>0)|0|0}function Ia(a,d,e,f,h,j,k,l,m,n){a=a|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;var o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0;N=i;i=i+112|0;G=N+104|0;o=N+100|0;q=N+96|0;H=N+92|0;I=N+88|0;r=N+84|0;s=N+80|0;t=N+76|0;u=N+72|0;v=N+68|0;p=N+64|0;A=N+60|0;y=N+56|0;x=N+52|0;M=N+48|0;L=N+44|0;z=N+40|0;J=N+36|0;K=N+32|0;B=N+28|0;w=N+24|0;D=N+20|0;C=N+8|0;E=N+4|0;F=N;c[o>>2]=a;c[q>>2]=d;c[H>>2]=e;c[I>>2]=f;c[r>>2]=h;c[s>>2]=j;c[t>>2]=k;c[u>>2]=l;c[v>>2]=m;c[p>>2]=n;c[M>>2]=0;c[L>>2]=0;c[z>>2]=c[(c[o>>2]|0)+32>>2];c[K>>2]=0;c[x>>2]=_(c[p>>2]|0,c[(c[o>>2]|0)+44>>2]|0)|0;if((_(c[p>>2]|0,(b[(c[z>>2]|0)+(c[u>>2]<<1)>>1]|0)-(b[(c[z>>2]|0)+((c[u>>2]|0)-1<<1)>>1]|0)|0)|0)<=8){c[G>>2]=0;M=c[G>>2]|0;i=N;return M|0}c[y>>2]=0;do{c[A>>2]=0;while(1){if((c[A>>2]|0)>=(c[u>>2]|0))break;c[D>>2]=0;c[C>>2]=0;c[C+4>>2]=0;c[C+8>>2]=0;k=(c[q>>2]|0)+((_(c[p>>2]|0,b[(c[z>>2]|0)+(c[A>>2]<<1)>>1]|0)|0)<<2)|0;c[E>>2]=k+((_(c[y>>2]|0,c[x>>2]|0)|0)<<2);c[w>>2]=_(c[p>>2]|0,(b[(c[z>>2]|0)+((c[A>>2]|0)+1<<1)>>1]|0)-(b[(c[z>>2]|0)+(c[A>>2]<<1)>>1]|0)|0)|0;if((c[w>>2]|0)>8){c[B>>2]=0;while(1){if((c[B>>2]|0)>=(c[w>>2]|0))break;g[F>>2]=+g[(c[E>>2]|0)+(c[B>>2]<<2)>>2]*+g[(c[E>>2]|0)+(c[B>>2]<<2)>>2]*+(c[w>>2]|0);if(+g[F>>2]<.25)c[C>>2]=(c[C>>2]|0)+1;if(+g[F>>2]<.0625){k=C+4|0;c[k>>2]=(c[k>>2]|0)+1}if(+g[F>>2]<.015625){k=C+8|0;c[k>>2]=(c[k>>2]|0)+1}c[B>>2]=(c[B>>2]|0)+1}if((c[A>>2]|0)>((c[(c[o>>2]|0)+8>>2]|0)-4|0)){k=Ha((c[C+4>>2]|0)+(c[C>>2]|0)<<5,c[w>>2]|0)|0;c[K>>2]=(c[K>>2]|0)+k}c[D>>2]=((c[C+8>>2]<<1|0)>=(c[w>>2]|0)&1)+((c[C+4>>2]<<1|0)>=(c[w>>2]|0)&1)+((c[C>>2]<<1|0)>=(c[w>>2]|0)&1);c[M>>2]=(c[M>>2]|0)+(c[D>>2]<<8);c[L>>2]=(c[L>>2]|0)+1}c[A>>2]=(c[A>>2]|0)+1}k=(c[y>>2]|0)+1|0;c[y>>2]=k}while((k|0)<(c[v>>2]|0));do if(c[t>>2]|0){if(c[K>>2]|0)c[K>>2]=Ha(c[K>>2]|0,_(c[v>>2]|0,4-(c[(c[o>>2]|0)+8>>2]|0)+(c[u>>2]|0)|0)|0)|0;c[c[r>>2]>>2]=(c[c[r>>2]>>2]|0)+(c[K>>2]|0)>>1;c[K>>2]=c[c[r>>2]>>2];if((c[c[s>>2]>>2]|0)!=2){if(!(c[c[s>>2]>>2]|0))c[K>>2]=(c[K>>2]|0)-4}else c[K>>2]=(c[K>>2]|0)+4;if((c[K>>2]|0)>22){c[c[s>>2]>>2]=2;break}o=c[s>>2]|0;if((c[K>>2]|0)>18){c[o>>2]=1;break}else{c[o>>2]=0;break}}while(0);c[M>>2]=Ha(c[M>>2]|0,c[L>>2]|0)|0;c[M>>2]=(c[M>>2]|0)+(c[c[H>>2]>>2]|0)>>1;c[c[H>>2]>>2]=c[M>>2];c[M>>2]=((c[M>>2]|0)*3|0)+((3-(c[I>>2]|0)<<7)+64)+2>>2;do if((c[M>>2]|0)>=80){if((c[M>>2]|0)<256){c[J>>2]=2;break}if((c[M>>2]|0)<384){c[J>>2]=1;break}else{c[J>>2]=0;break}}else c[J>>2]=3;while(0);c[G>>2]=c[J>>2];M=c[G>>2]|0;i=N;return M|0}function Ja(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0;n=i;i=i+32|0;e=n+24|0;f=n+20|0;h=n+16|0;j=n+12|0;k=n+8|0;l=n+4|0;m=n;c[e>>2]=a;c[f>>2]=b;c[h>>2]=d;c[f>>2]=c[f>>2]>>1;c[j>>2]=0;while(1){if((c[j>>2]|0)>=(c[h>>2]|0))break;c[k>>2]=0;while(1){if((c[k>>2]|0)>=(c[f>>2]|0))break;d=_(c[h>>2]<<1,c[k>>2]|0)|0;g[l>>2]=+g[(c[e>>2]|0)+(d+(c[j>>2]|0)<<2)>>2]*.7071067690849304;d=_(c[h>>2]|0,(c[k>>2]<<1)+1|0)|0;g[m>>2]=+g[(c[e>>2]|0)+(d+(c[j>>2]|0)<<2)>>2]*.7071067690849304;d=_(c[h>>2]<<1,c[k>>2]|0)|0;g[(c[e>>2]|0)+(d+(c[j>>2]|0)<<2)>>2]=+g[l>>2]+ +g[m>>2];d=_(c[h>>2]|0,(c[k>>2]<<1)+1|0)|0;g[(c[e>>2]|0)+(d+(c[j>>2]|0)<<2)>>2]=+g[l>>2]-+g[m>>2];c[k>>2]=(c[k>>2]|0)+1}c[j>>2]=(c[j>>2]|0)+1}i=n;return}function Ka(e,f,h,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A){e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;p=p|0;q=q|0;r=r|0;s=s|0;t=t|0;u=u|0;v=v|0;w=w|0;x=x|0;y=y|0;z=z|0;A=A|0;var B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0,ha=0,ja=0,ka=0,la=0,ma=0,oa=0,pa=0,qa=0,ra=0,sa=0,ta=0,ua=0,va=0,wa=0,xa=0,ya=0,za=0,Aa=0,Ba=0;xa=i;i=i+256|0;za=xa+240|0;B=xa+236|0;M=xa+232|0;O=xa+228|0;P=xa+224|0;Q=xa+220|0;R=xa+216|0;Aa=xa+212|0;S=xa+208|0;Ba=xa+204|0;C=xa+200|0;D=xa+196|0;E=xa+192|0;F=xa+188|0;G=xa+184|0;H=xa+180|0;I=xa+176|0;J=xa+172|0;K=xa+168|0;L=xa+164|0;ya=xa+160|0;ga=xa+156|0;qa=xa+152|0;ba=xa+148|0;ma=xa+144|0;oa=xa+140|0;la=xa+136|0;T=xa+132|0;V=xa+128|0;ka=xa+124|0;ua=xa+120|0;U=xa+116|0;pa=xa+112|0;ra=xa+108|0;$=xa+64|0;N=xa+60|0;sa=xa+56|0;Z=xa+52|0;W=xa+48|0;aa=xa+44|0;ca=xa+40|0;X=xa+36|0;Y=xa+32|0;ta=xa+28|0;va=xa+24|0;wa=xa+20|0;ja=xa+16|0;fa=xa+12|0;da=xa+8|0;ea=xa+4|0;ha=xa;c[za>>2]=e;c[B>>2]=f;c[M>>2]=h;c[O>>2]=j;c[P>>2]=k;c[Q>>2]=l;c[R>>2]=m;c[Aa>>2]=n;c[S>>2]=o;c[Ba>>2]=p;c[C>>2]=q;c[D>>2]=r;c[E>>2]=s;c[F>>2]=t;c[G>>2]=u;c[H>>2]=v;c[I>>2]=w;c[J>>2]=x;c[K>>2]=y;c[L>>2]=z;c[ya>>2]=A;c[ba>>2]=c[(c[B>>2]|0)+32>>2];c[ua>>2]=1;c[U>>2]=c[Q>>2]|0?2:1;c[ra>>2]=((c[za>>2]|0)!=0^1)&1;c[V>>2]=1<<c[J>>2];c[T>>2]=c[Ba>>2]|0?c[V>>2]|0:1;c[pa>>2]=_(c[V>>2]|0,b[(c[ba>>2]|0)+(c[M>>2]<<1)>>1]|0)|0;t=_(c[V>>2]|0,b[(c[ba>>2]|0)+((c[(c[B>>2]|0)+8>>2]|0)-1<<1)>>1]|0)|0;t=_(c[U>>2]|0,t-(c[pa>>2]|0)|0)|0;c[N>>2]=ia()|0;u=i;i=i+((1*(t<<2)|0)+15&-16)|0;c[ma>>2]=u;u=(c[ma>>2]|0)+((_(c[V>>2]|0,b[(c[ba>>2]|0)+((c[(c[B>>2]|0)+8>>2]|0)-1<<1)>>1]|0)|0)<<2)|0;c[oa>>2]=u+(0-(c[pa>>2]|0)<<2);c[la>>2]=(c[P>>2]|0)+((_(c[V>>2]|0,b[(c[ba>>2]|0)+((c[(c[B>>2]|0)+8>>2]|0)-1<<1)>>1]|0)|0)<<2);c[ka>>2]=0;c[$+32>>2]=c[Aa>>2];c[$+24>>2]=c[I>>2];c[$>>2]=c[za>>2];c[$+12>>2]=c[E>>2];c[$+4>>2]=c[B>>2];c[$+36>>2]=c[c[L>>2]>>2];c[$+16>>2]=c[C>>2];c[$+40>>2]=c[ya>>2];c[ga>>2]=c[M>>2];while(1){if((c[ga>>2]|0)>=(c[O>>2]|0))break;c[ca>>2]=-1;c[ta>>2]=0;c[$+8>>2]=c[ga>>2];c[ja>>2]=(c[ga>>2]|0)==((c[O>>2]|0)-1|0)&1;c[X>>2]=(c[P>>2]|0)+((_(c[V>>2]|0,b[(c[ba>>2]|0)+(c[ga>>2]<<1)>>1]|0)|0)<<2);if(c[Q>>2]|0)c[Y>>2]=(c[Q>>2]|0)+((_(c[V>>2]|0,b[(c[ba>>2]|0)+(c[ga>>2]<<1)>>1]|0)|0)<<2);else c[Y>>2]=0;Ba=_(c[V>>2]|0,b[(c[ba>>2]|0)+((c[ga>>2]|0)+1<<1)>>1]|0)|0;c[W>>2]=Ba-(_(c[V>>2]|0,b[(c[ba>>2]|0)+(c[ga>>2]<<1)>>1]|0)|0);c[sa>>2]=Xb(c[I>>2]|0)|0;if((c[ga>>2]|0)!=(c[M>>2]|0))c[H>>2]=(c[H>>2]|0)-(c[sa>>2]|0);c[qa>>2]=(c[G>>2]|0)-(c[sa>>2]|0)-1;c[$+28>>2]=c[qa>>2];if((c[ga>>2]|0)<=((c[K>>2]|0)-1|0)){if(3<((c[K>>2]|0)-(c[ga>>2]|0)|0))x=3;else x=(c[K>>2]|0)-(c[ga>>2]|0)|0;c[aa>>2]=La(c[H>>2]|0,x)|0;if(((c[qa>>2]|0)+1|0)<((c[(c[S>>2]|0)+(c[ga>>2]<<2)>>2]|0)+(c[aa>>2]|0)|0))x=(c[qa>>2]|0)+1|0;else x=(c[(c[S>>2]|0)+(c[ga>>2]<<2)>>2]|0)+(c[aa>>2]|0)|0;do if(16383>=(x|0))if(((c[qa>>2]|0)+1|0)<((c[(c[S>>2]|0)+(c[ga>>2]<<2)>>2]|0)+(c[aa>>2]|0)|0)){x=(c[qa>>2]|0)+1|0;break}else{x=(c[(c[S>>2]|0)+(c[ga>>2]<<2)>>2]|0)+(c[aa>>2]|0)|0;break}else x=16383;while(0);do if(0<=(x|0)){if(((c[qa>>2]|0)+1|0)<((c[(c[S>>2]|0)+(c[ga>>2]<<2)>>2]|0)+(c[aa>>2]|0)|0))x=(c[qa>>2]|0)+1|0;else x=(c[(c[S>>2]|0)+(c[ga>>2]<<2)>>2]|0)+(c[aa>>2]|0)|0;if(16383>=(x|0))if(((c[qa>>2]|0)+1|0)<((c[(c[S>>2]|0)+(c[ga>>2]<<2)>>2]|0)+(c[aa>>2]|0)|0)){x=(c[qa>>2]|0)+1|0;break}else{x=(c[(c[S>>2]|0)+(c[ga>>2]<<2)>>2]|0)+(c[aa>>2]|0)|0;break}else x=16383}else x=0;while(0);c[Z>>2]=x}else c[Z>>2]=0;if((c[ra>>2]|0?(Ba=_(c[V>>2]|0,b[(c[ba>>2]|0)+(c[ga>>2]<<1)>>1]|0)|0,(Ba-(c[W>>2]|0)|0)>=(_(c[V>>2]|0,b[(c[ba>>2]|0)+(c[M>>2]<<1)>>1]|0)|0)):0)?(c[ua>>2]|0)!=0|(c[ka>>2]|0)==0:0)c[ka>>2]=c[ga>>2];c[ta>>2]=c[(c[F>>2]|0)+(c[ga>>2]<<2)>>2];c[$+20>>2]=c[ta>>2];if((c[ga>>2]|0)>=(c[(c[B>>2]|0)+12>>2]|0)){c[X>>2]=c[ma>>2];if(c[Q>>2]|0)c[Y>>2]=c[ma>>2];c[la>>2]=0}if((c[ga>>2]|0)==((c[O>>2]|0)-1|0))c[la>>2]=0;if(c[ka>>2]|0?(c[C>>2]|0)!=3|(c[T>>2]|0)>1|(c[ta>>2]|0)<0:0){Ba=_(c[V>>2]|0,b[(c[ba>>2]|0)+(c[ka>>2]<<1)>>1]|0)|0;if(0>(Ba-(c[pa>>2]|0)-(c[W>>2]|0)|0))x=0;else{x=_(c[V>>2]|0,b[(c[ba>>2]|0)+(c[ka>>2]<<1)>>1]|0)|0;x=x-(c[pa>>2]|0)-(c[W>>2]|0)|0}c[ca>>2]=x;c[fa>>2]=c[ka>>2];do{Aa=c[V>>2]|0;Ba=(c[fa>>2]|0)+-1|0;c[fa>>2]=Ba;Ba=_(Aa,b[(c[ba>>2]|0)+(Ba<<1)>>1]|0)|0}while((Ba|0)>((c[ca>>2]|0)+(c[pa>>2]|0)|0));c[da>>2]=(c[ka>>2]|0)-1;do{Aa=c[V>>2]|0;Ba=(c[da>>2]|0)+1|0;c[da>>2]=Ba;Ba=_(Aa,b[(c[ba>>2]|0)+(Ba<<1)>>1]|0)|0}while((Ba|0)<((c[ca>>2]|0)+(c[pa>>2]|0)+(c[W>>2]|0)|0));c[wa>>2]=0;c[va>>2]=0;c[ea>>2]=c[fa>>2];do{Ba=(_(c[ea>>2]|0,c[U>>2]|0)|0)+0|0;c[va>>2]=c[va>>2]|d[(c[R>>2]|0)+Ba>>0];Ba=_(c[ea>>2]|0,c[U>>2]|0)|0;c[wa>>2]=c[wa>>2]|d[(c[R>>2]|0)+(Ba+(c[U>>2]|0)-1)>>0];Ba=(c[ea>>2]|0)+1|0;c[ea>>2]=Ba}while((Ba|0)<(c[da>>2]|0))}else{Ba=(1<<c[T>>2])-1|0;c[wa>>2]=Ba;c[va>>2]=Ba}a:do if((c[D>>2]|0?(c[ga>>2]|0)==(c[E>>2]|0):0)?(c[D>>2]=0,c[ra>>2]|0):0){c[ha>>2]=0;while(1){Ba=_(c[V>>2]|0,b[(c[ba>>2]|0)+(c[ga>>2]<<1)>>1]|0)|0;if((c[ha>>2]|0)>=(Ba-(c[pa>>2]|0)|0))break a;g[(c[ma>>2]|0)+(c[ha>>2]<<2)>>2]=(+g[(c[ma>>2]|0)+(c[ha>>2]<<2)>>2]+ +g[(c[oa>>2]|0)+(c[ha>>2]<<2)>>2])*.5;c[ha>>2]=(c[ha>>2]|0)+1}}while(0);if(c[D>>2]|0){if((c[ca>>2]|0)!=-1)x=(c[ma>>2]|0)+(c[ca>>2]<<2)|0;else x=0;if(c[ja>>2]|0)k=0;else{k=(c[ma>>2]|0)+((_(c[V>>2]|0,b[(c[ba>>2]|0)+(c[ga>>2]<<1)>>1]|0)|0)<<2)|0;k=k+(0-(c[pa>>2]|0)<<2)|0}c[va>>2]=Ma($,c[X>>2]|0,c[W>>2]|0,(c[Z>>2]|0)/2|0,c[T>>2]|0,x,c[J>>2]|0,k,1.0,c[la>>2]|0,c[va>>2]|0)|0;if((c[ca>>2]|0)!=-1)x=(c[oa>>2]|0)+(c[ca>>2]<<2)|0;else x=0;if(c[ja>>2]|0)k=0;else{k=(c[oa>>2]|0)+((_(c[V>>2]|0,b[(c[ba>>2]|0)+(c[ga>>2]<<1)>>1]|0)|0)<<2)|0;k=k+(0-(c[pa>>2]|0)<<2)|0}c[wa>>2]=Ma($,c[Y>>2]|0,c[W>>2]|0,(c[Z>>2]|0)/2|0,c[T>>2]|0,x,c[J>>2]|0,k,1.0,c[la>>2]|0,c[wa>>2]|0)|0}else{l=c[X>>2]|0;if(c[Y>>2]|0){if((c[ca>>2]|0)!=-1)x=(c[ma>>2]|0)+(c[ca>>2]<<2)|0;else x=0;if(c[ja>>2]|0)k=0;else{k=(c[ma>>2]|0)+((_(c[V>>2]|0,b[(c[ba>>2]|0)+(c[ga>>2]<<1)>>1]|0)|0)<<2)|0;k=k+(0-(c[pa>>2]|0)<<2)|0}c[va>>2]=Na($,l,c[Y>>2]|0,c[W>>2]|0,c[Z>>2]|0,c[T>>2]|0,x,c[J>>2]|0,k,c[la>>2]|0,c[va>>2]|c[wa>>2])|0}else{if((c[ca>>2]|0)!=-1)x=(c[ma>>2]|0)+(c[ca>>2]<<2)|0;else x=0;if(c[ja>>2]|0)k=0;else{k=(c[ma>>2]|0)+((_(c[V>>2]|0,b[(c[ba>>2]|0)+(c[ga>>2]<<1)>>1]|0)|0)<<2)|0;k=k+(0-(c[pa>>2]|0)<<2)|0}c[va>>2]=Ma($,l,c[W>>2]|0,c[Z>>2]|0,c[T>>2]|0,x,c[J>>2]|0,k,1.0,c[la>>2]|0,c[va>>2]|c[wa>>2])|0}c[wa>>2]=c[va>>2]}Ba=(_(c[ga>>2]|0,c[U>>2]|0)|0)+0|0;a[(c[R>>2]|0)+Ba>>0]=c[va>>2];Ba=_(c[ga>>2]|0,c[U>>2]|0)|0;a[(c[R>>2]|0)+(Ba+(c[U>>2]|0)-1)>>0]=c[wa>>2];c[H>>2]=(c[H>>2]|0)+((c[(c[S>>2]|0)+(c[ga>>2]<<2)>>2]|0)+(c[sa>>2]|0));c[ua>>2]=(c[Z>>2]|0)>(c[W>>2]<<3|0)&1;c[ga>>2]=(c[ga>>2]|0)+1}c[c[L>>2]>>2]=c[$+36>>2];na(c[N>>2]|0);i=xa;return}function La(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return (c[e>>2]|0)/(c[d>>2]|0)|0|0}function Ma(a,b,e,f,h,j,k,l,m,n,o){a=a|0;b=b|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;m=+m;n=n|0;o=o|0;var p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,P=0,Q=0;Q=i;i=i+112|0;p=Q+100|0;q=Q+96|0;t=Q+92|0;u=Q+88|0;v=Q+84|0;w=Q+80|0;x=Q+76|0;y=Q+72|0;z=Q+68|0;A=Q+64|0;r=Q+60|0;s=Q+56|0;C=Q+52|0;D=Q+48|0;E=Q+44|0;B=Q+40|0;P=Q+36|0;L=Q+32|0;J=Q+28|0;F=Q+24|0;M=Q+20|0;I=Q+16|0;G=Q+12|0;N=Q+8|0;H=Q+4|0;K=Q;c[q>>2]=a;c[t>>2]=b;c[u>>2]=e;c[v>>2]=f;c[w>>2]=h;c[x>>2]=j;c[y>>2]=k;c[z>>2]=l;g[A>>2]=m;c[r>>2]=n;c[s>>2]=o;c[C>>2]=c[u>>2];c[D>>2]=c[u>>2];c[B>>2]=c[w>>2];c[P>>2]=0;c[L>>2]=0;c[F>>2]=0;c[M>>2]=((c[c[q>>2]>>2]|0)!=0^1)&1;c[G>>2]=c[c[q>>2]>>2];c[N>>2]=c[(c[q>>2]|0)+20>>2];c[J>>2]=(c[B>>2]|0)==1&1;c[D>>2]=Ha(c[D>>2]|0,c[w>>2]|0)|0;if((c[u>>2]|0)==1){c[p>>2]=Oa(c[q>>2]|0,c[t>>2]|0,0,c[v>>2]|0,c[z>>2]|0)|0;P=c[p>>2]|0;i=Q;return P|0}if((c[N>>2]|0)>0)c[L>>2]=c[N>>2];do if((c[r>>2]|0)!=0&(c[x>>2]|0)!=0){if((c[L>>2]|0)==0?!((c[D>>2]&1|0)==0&(c[N>>2]|0)<0|(c[B>>2]|0)>1):0)break;pj(c[r>>2]|0,c[x>>2]|0,(c[u>>2]<<2)+0|0)|0;c[x>>2]=c[r>>2]}while(0);c[I>>2]=0;while(1){if((c[I>>2]|0)>=(c[L>>2]|0))break;if(c[G>>2]|0)Ja(c[t>>2]|0,c[u>>2]>>c[I>>2],1<<c[I>>2]);if(c[x>>2]|0)Ja(c[x>>2]|0,c[u>>2]>>c[I>>2],1<<c[I>>2]);c[s>>2]=d[25200+(c[s>>2]&15)>>0]|0|(d[25200+(c[s>>2]>>4)>>0]|0)<<2;c[I>>2]=(c[I>>2]|0)+1}c[w>>2]=c[w>>2]>>c[L>>2];c[D>>2]=c[D>>2]<<c[L>>2];while(1){if(!((c[D>>2]&1|0)==0?(c[N>>2]|0)<0:0))break;if(c[G>>2]|0)Ja(c[t>>2]|0,c[D>>2]|0,c[w>>2]|0);if(c[x>>2]|0)Ja(c[x>>2]|0,c[D>>2]|0,c[w>>2]|0);c[s>>2]=c[s>>2]|c[s>>2]<<c[w>>2];c[w>>2]=c[w>>2]<<1;c[D>>2]=c[D>>2]>>1;c[P>>2]=(c[P>>2]|0)+1;c[N>>2]=(c[N>>2]|0)+1}c[B>>2]=c[w>>2];c[E>>2]=c[D>>2];if((c[B>>2]|0)>1){if(c[G>>2]|0)Pa(c[t>>2]|0,c[D>>2]>>c[L>>2],c[B>>2]<<c[L>>2],c[J>>2]|0);if(c[x>>2]|0)Pa(c[x>>2]|0,c[D>>2]>>c[L>>2],c[B>>2]<<c[L>>2],c[J>>2]|0)}c[F>>2]=Qa(c[q>>2]|0,c[t>>2]|0,c[u>>2]|0,c[v>>2]|0,c[w>>2]|0,c[x>>2]|0,c[y>>2]|0,+g[A>>2],c[s>>2]|0)|0;if(c[M>>2]|0){if((c[B>>2]|0)>1)Ra(c[t>>2]|0,c[D>>2]>>c[L>>2],c[B>>2]<<c[L>>2],c[J>>2]|0);c[D>>2]=c[E>>2];c[w>>2]=c[B>>2];c[I>>2]=0;while(1){if((c[I>>2]|0)>=(c[P>>2]|0))break;c[w>>2]=c[w>>2]>>1;c[D>>2]=c[D>>2]<<1;c[F>>2]=c[F>>2]|(c[F>>2]|0)>>>(c[w>>2]|0);Ja(c[t>>2]|0,c[D>>2]|0,c[w>>2]|0);c[I>>2]=(c[I>>2]|0)+1}c[I>>2]=0;while(1){if((c[I>>2]|0)>=(c[L>>2]|0))break;c[F>>2]=d[25216+(c[F>>2]|0)>>0];Ja(c[t>>2]|0,c[C>>2]>>c[I>>2],1<<c[I>>2]);c[I>>2]=(c[I>>2]|0)+1}c[w>>2]=c[w>>2]<<c[L>>2];a:do if(c[z>>2]|0){g[K>>2]=+O(+(+(c[C>>2]|0)));c[H>>2]=0;while(1){if((c[H>>2]|0)>=(c[C>>2]|0))break a;g[(c[z>>2]|0)+(c[H>>2]<<2)>>2]=+g[K>>2]*+g[(c[t>>2]|0)+(c[H>>2]<<2)>>2];c[H>>2]=(c[H>>2]|0)+1}}while(0);c[F>>2]=c[F>>2]&(1<<c[w>>2])-1}c[p>>2]=c[F>>2];P=c[p>>2]|0;i=Q;return P|0}function Na(a,b,d,e,f,h,j,k,l,m,n){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;var o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0;X=i;i=i+160|0;C=X+156|0;D=X+152|0;G=X+148|0;H=X+144|0;I=X+140|0;A=X+136|0;J=X+132|0;K=X+128|0;L=X+124|0;M=X+120|0;E=X+116|0;F=X+112|0;r=X+108|0;s=X+104|0;O=X+100|0;S=X+96|0;W=X+92|0;N=X+88|0;U=X+84|0;R=X+80|0;V=X+76|0;z=X+72|0;P=X+68|0;B=X+64|0;u=X+40|0;t=X+36|0;q=X+32|0;p=X+28|0;o=X+24|0;v=X+20|0;x=X+16|0;y=X+12|0;w=X+8|0;T=X+4|0;Q=X;c[D>>2]=a;c[G>>2]=b;c[H>>2]=d;c[I>>2]=e;c[A>>2]=f;c[J>>2]=h;c[K>>2]=j;c[L>>2]=k;c[M>>2]=l;c[E>>2]=m;c[F>>2]=n;c[r>>2]=0;c[s>>2]=0;c[O>>2]=0;g[S>>2]=0.0;g[W>>2]=0.0;c[N>>2]=0;c[U>>2]=((c[c[D>>2]>>2]|0)!=0^1)&1;c[q>>2]=c[c[D>>2]>>2];c[p>>2]=c[(c[D>>2]|0)+24>>2];if((c[I>>2]|0)==1){c[C>>2]=Oa(c[D>>2]|0,c[G>>2]|0,c[H>>2]|0,c[A>>2]|0,c[M>>2]|0)|0;W=c[C>>2]|0;i=X;return W|0}c[t>>2]=c[F>>2];Sa(c[D>>2]|0,u,c[G>>2]|0,c[H>>2]|0,c[I>>2]|0,A,c[J>>2]|0,c[J>>2]|0,c[L>>2]|0,1,F);c[O>>2]=c[u>>2];c[r>>2]=c[u+4>>2];c[s>>2]=c[u+8>>2];c[z>>2]=c[u+12>>2];c[P>>2]=c[u+16>>2];c[B>>2]=c[u+20>>2];g[S>>2]=+(c[r>>2]|0)*.000030517578125;g[W>>2]=+(c[s>>2]|0)*.000030517578125;do if((c[I>>2]|0)==2){c[v>>2]=0;c[R>>2]=c[A>>2];c[V>>2]=0;c[V>>2]=(c[P>>2]|0)!=0&(c[P>>2]|0)!=16384?8:0;c[R>>2]=(c[R>>2]|0)-(c[V>>2]|0);c[o>>2]=(c[P>>2]|0)>8192&1;T=(c[D>>2]|0)+28|0;c[T>>2]=(c[T>>2]|0)-((c[B>>2]|0)+(c[V>>2]|0));c[x>>2]=c[o>>2]|0?c[H>>2]|0:c[G>>2]|0;c[y>>2]=c[o>>2]|0?c[G>>2]|0:c[H>>2]|0;do if(c[V>>2]|0)if(c[q>>2]|0){c[v>>2]=+g[c[x>>2]>>2]*+g[(c[y>>2]|0)+4>>2]-+g[(c[x>>2]|0)+4>>2]*+g[c[y>>2]>>2]<0.0&1;sc(c[p>>2]|0,c[v>>2]|0,1);break}else{c[v>>2]=gc(c[p>>2]|0,1)|0;break}while(0);c[v>>2]=1-(c[v>>2]<<1);c[N>>2]=Ma(c[D>>2]|0,c[x>>2]|0,c[I>>2]|0,c[R>>2]|0,c[J>>2]|0,c[K>>2]|0,c[L>>2]|0,c[M>>2]|0,1.0,c[E>>2]|0,c[t>>2]|0)|0;g[c[y>>2]>>2]=+(0-(c[v>>2]|0)|0)*+g[(c[x>>2]|0)+4>>2];g[(c[y>>2]|0)+4>>2]=+(c[v>>2]|0)*+g[c[x>>2]>>2];if(c[U>>2]|0){g[c[G>>2]>>2]=+g[S>>2]*+g[c[G>>2]>>2];g[(c[G>>2]|0)+4>>2]=+g[S>>2]*+g[(c[G>>2]|0)+4>>2];g[c[H>>2]>>2]=+g[W>>2]*+g[c[H>>2]>>2];g[(c[H>>2]|0)+4>>2]=+g[W>>2]*+g[(c[H>>2]|0)+4>>2];g[w>>2]=+g[c[G>>2]>>2];g[c[G>>2]>>2]=+g[w>>2]-+g[c[H>>2]>>2];g[c[H>>2]>>2]=+g[w>>2]+ +g[c[H>>2]>>2];g[w>>2]=+g[(c[G>>2]|0)+4>>2];g[(c[G>>2]|0)+4>>2]=+g[w>>2]-+g[(c[H>>2]|0)+4>>2];g[(c[H>>2]|0)+4>>2]=+g[w>>2]+ +g[(c[H>>2]|0)+4>>2]}}else{h=c[A>>2]|0;if((c[A>>2]|0)>=(((c[A>>2]|0)-(c[z>>2]|0)|0)/2|0|0))h=(h-(c[z>>2]|0)|0)/2|0;if(0<=(h|0)){h=c[A>>2]|0;if((c[A>>2]|0)>=(((c[A>>2]|0)-(c[z>>2]|0)|0)/2|0|0))h=(h-(c[z>>2]|0)|0)/2|0}else h=0;c[R>>2]=h;c[V>>2]=(c[A>>2]|0)-(c[R>>2]|0);h=(c[D>>2]|0)+28|0;c[h>>2]=(c[h>>2]|0)-(c[B>>2]|0);c[T>>2]=c[(c[D>>2]|0)+28>>2];h=c[D>>2]|0;if((c[R>>2]|0)>=(c[V>>2]|0)){c[N>>2]=Ma(h,c[G>>2]|0,c[I>>2]|0,c[R>>2]|0,c[J>>2]|0,c[K>>2]|0,c[L>>2]|0,c[M>>2]|0,1.0,c[E>>2]|0,c[F>>2]|0)|0;c[T>>2]=(c[R>>2]|0)-((c[T>>2]|0)-(c[(c[D>>2]|0)+28>>2]|0));if((c[T>>2]|0)>24&(c[P>>2]|0)!=0)c[V>>2]=(c[V>>2]|0)+((c[T>>2]|0)-24);W=Ma(c[D>>2]|0,c[H>>2]|0,c[I>>2]|0,c[V>>2]|0,c[J>>2]|0,0,c[L>>2]|0,0,+g[W>>2],0,c[F>>2]>>c[J>>2])|0;c[N>>2]=c[N>>2]|W;break}else{c[N>>2]=Ma(h,c[H>>2]|0,c[I>>2]|0,c[V>>2]|0,c[J>>2]|0,0,c[L>>2]|0,0,+g[W>>2],0,c[F>>2]>>c[J>>2])|0;c[T>>2]=(c[V>>2]|0)-((c[T>>2]|0)-(c[(c[D>>2]|0)+28>>2]|0));if((c[T>>2]|0)>24&(c[P>>2]|0)!=16384)c[R>>2]=(c[R>>2]|0)+((c[T>>2]|0)-24);W=Ma(c[D>>2]|0,c[G>>2]|0,c[I>>2]|0,c[R>>2]|0,c[J>>2]|0,c[K>>2]|0,c[L>>2]|0,c[M>>2]|0,1.0,c[E>>2]|0,c[F>>2]|0)|0;c[N>>2]=c[N>>2]|W;break}}while(0);a:do if(c[U>>2]|0){if((c[I>>2]|0)!=2)$a(c[G>>2]|0,c[H>>2]|0,+g[S>>2],c[I>>2]|0,c[(c[D>>2]|0)+40>>2]|0);if(c[O>>2]|0){c[Q>>2]=0;while(1){if((c[Q>>2]|0)>=(c[I>>2]|0))break a;g[(c[H>>2]|0)+(c[Q>>2]<<2)>>2]=-+g[(c[H>>2]|0)+(c[Q>>2]<<2)>>2];c[Q>>2]=(c[Q>>2]|0)+1}}}while(0);c[C>>2]=c[N>>2];W=c[C>>2]|0;i=X;return W|0}function Oa(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;u=i;i=i+48|0;h=u+44|0;j=u+40|0;k=u+36|0;l=u+32|0;m=u+28|0;q=u+24|0;n=u+20|0;s=u+16|0;t=u+12|0;p=u+8|0;o=u+4|0;r=u;c[h>>2]=a;c[j>>2]=b;c[k>>2]=d;c[l>>2]=e;c[m>>2]=f;c[q>>2]=((c[c[h>>2]>>2]|0)!=0^1)&1;c[t>>2]=c[j>>2];c[p>>2]=c[c[h>>2]>>2];c[o>>2]=c[(c[h>>2]|0)+24>>2];c[s>>2]=(c[k>>2]|0)!=0&1;c[n>>2]=0;do{c[r>>2]=0;if((c[(c[h>>2]|0)+28>>2]|0)>=8){if(c[p>>2]|0){c[r>>2]=+g[c[t>>2]>>2]<0.0&1;sc(c[o>>2]|0,c[r>>2]|0,1)}else c[r>>2]=gc(c[o>>2]|0,1)|0;f=(c[h>>2]|0)+28|0;c[f>>2]=(c[f>>2]|0)-8;c[l>>2]=(c[l>>2]|0)-8}if(c[q>>2]|0)g[c[t>>2]>>2]=c[r>>2]|0?-1.0:1.0;c[t>>2]=c[k>>2];f=(c[n>>2]|0)+1|0;c[n>>2]=f}while((f|0)<(1+(c[s>>2]|0)|0));if(!(c[m>>2]|0)){i=u;return 1}g[c[m>>2]>>2]=+g[c[j>>2]>>2];i=u;return 1}function Pa(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;p=i;i=i+48|0;f=p+32|0;h=p+28|0;j=p+24|0;q=p+20|0;m=p+16|0;n=p+12|0;l=p+8|0;k=p+4|0;o=p;c[f>>2]=a;c[h>>2]=b;c[j>>2]=d;c[q>>2]=e;c[l>>2]=_(c[h>>2]|0,c[j>>2]|0)|0;d=c[l>>2]|0;c[k>>2]=ia()|0;b=i;i=i+((1*(d<<2)|0)+15&-16)|0;if(c[q>>2]|0){c[o>>2]=8+(c[j>>2]<<2)+-8;c[m>>2]=0;while(1){if((c[m>>2]|0)>=(c[j>>2]|0))break;c[n>>2]=0;while(1){if((c[n>>2]|0)>=(c[h>>2]|0))break;d=_(c[n>>2]|0,c[j>>2]|0)|0;q=_(c[(c[o>>2]|0)+(c[m>>2]<<2)>>2]|0,c[h>>2]|0)|0;g[b+(q+(c[n>>2]|0)<<2)>>2]=+g[(c[f>>2]|0)+(d+(c[m>>2]|0)<<2)>>2];c[n>>2]=(c[n>>2]|0)+1}c[m>>2]=(c[m>>2]|0)+1}o=c[f>>2]|0;n=c[l>>2]|0;n=n<<2;q=0;q=n+q|0;pj(o|0,b|0,q|0)|0;q=c[k>>2]|0;na(q|0);i=p;return}else{c[m>>2]=0;while(1){if((c[m>>2]|0)>=(c[j>>2]|0))break;c[n>>2]=0;while(1){if((c[n>>2]|0)>=(c[h>>2]|0))break;o=_(c[n>>2]|0,c[j>>2]|0)|0;q=_(c[m>>2]|0,c[h>>2]|0)|0;g[b+(q+(c[n>>2]|0)<<2)>>2]=+g[(c[f>>2]|0)+(o+(c[m>>2]|0)<<2)>>2];c[n>>2]=(c[n>>2]|0)+1}c[m>>2]=(c[m>>2]|0)+1}o=c[f>>2]|0;n=c[l>>2]|0;n=n<<2;q=0;q=n+q|0;pj(o|0,b|0,q|0)|0;q=c[k>>2]|0;na(q|0);i=p;return}}function Qa(a,e,f,h,j,k,l,m,n){a=a|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;m=+m;n=n|0;var o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0;Y=i;i=i+176|0;G=Y+164|0;H=Y+160|0;U=Y+156|0;D=Y+152|0;V=Y+148|0;I=Y+144|0;J=Y+140|0;W=Y+136|0;K=Y+132|0;Z=Y+128|0;v=Y+124|0;p=Y+120|0;r=Y+116|0;s=Y+112|0;L=Y+108|0;P=Y+104|0;T=Y+100|0;X=Y+96|0;w=Y+92|0;M=Y+88|0;E=Y+84|0;u=Y+80|0;q=Y+76|0;A=Y+72|0;C=Y+68|0;O=Y+64|0;S=Y+60|0;B=Y+56|0;N=Y+52|0;F=Y+48|0;x=Y+24|0;Q=Y+20|0;R=Y+16|0;z=Y+12|0;t=Y+8|0;o=Y+4|0;y=Y;c[G>>2]=a;c[H>>2]=e;c[U>>2]=f;c[D>>2]=h;c[V>>2]=j;c[I>>2]=k;c[J>>2]=l;g[W>>2]=m;c[K>>2]=n;c[r>>2]=0;c[s>>2]=0;c[L>>2]=c[V>>2];g[P>>2]=0.0;g[T>>2]=0.0;c[X>>2]=0;c[w>>2]=((c[c[G>>2]>>2]|0)!=0^1)&1;c[M>>2]=0;c[E>>2]=c[c[G>>2]>>2];c[u>>2]=c[(c[G>>2]|0)+4>>2];c[q>>2]=c[(c[G>>2]|0)+8>>2];c[A>>2]=c[(c[G>>2]|0)+16>>2];c[C>>2]=c[(c[G>>2]|0)+24>>2];k=_((c[J>>2]|0)+1|0,c[(c[u>>2]|0)+8>>2]|0)|0;c[Z>>2]=(c[(c[u>>2]|0)+92+8>>2]|0)+(b[(c[(c[u>>2]|0)+92+4>>2]|0)+(k+(c[q>>2]|0)<<1)>>1]|0);if((c[J>>2]|0)!=-1?((c[U>>2]|0)>2?(c[D>>2]|0)>((d[(c[Z>>2]|0)+(d[c[Z>>2]>>0]|0)>>0]|0)+12|0):0):0){c[Q>>2]=0;c[U>>2]=c[U>>2]>>1;c[M>>2]=(c[H>>2]|0)+(c[U>>2]<<2);c[J>>2]=(c[J>>2]|0)-1;if((c[V>>2]|0)==1)c[K>>2]=c[K>>2]&1|c[K>>2]<<1;c[V>>2]=(c[V>>2]|0)+1>>1;Sa(c[G>>2]|0,x,c[H>>2]|0,c[M>>2]|0,c[U>>2]|0,D,c[V>>2]|0,c[L>>2]|0,c[J>>2]|0,0,K);c[r>>2]=c[x+4>>2];c[s>>2]=c[x+8>>2];c[B>>2]=c[x+12>>2];c[N>>2]=c[x+16>>2];c[F>>2]=c[x+20>>2];g[P>>2]=+(c[r>>2]|0)*.000030517578125;g[T>>2]=+(c[s>>2]|0)*.000030517578125;do if((c[L>>2]|0)>1?c[N>>2]&16383|0:0){j=c[B>>2]|0;if((c[N>>2]|0)>8192){c[B>>2]=(c[B>>2]|0)-(j>>4-(c[J>>2]|0));break}if(0<(j+(c[U>>2]<<3>>5-(c[J>>2]|0))|0))j=0;else j=(c[B>>2]|0)+(c[U>>2]<<3>>5-(c[J>>2]|0))|0;c[B>>2]=j}while(0);j=c[D>>2]|0;if((c[D>>2]|0)>=(((c[D>>2]|0)-(c[B>>2]|0)|0)/2|0|0))j=(j-(c[B>>2]|0)|0)/2|0;if(0<=(j|0)){j=c[D>>2]|0;if((c[D>>2]|0)>=(((c[D>>2]|0)-(c[B>>2]|0)|0)/2|0|0))j=(j-(c[B>>2]|0)|0)/2|0}else j=0;c[O>>2]=j;c[S>>2]=(c[D>>2]|0)-(c[O>>2]|0);Z=(c[G>>2]|0)+28|0;c[Z>>2]=(c[Z>>2]|0)-(c[F>>2]|0);if(c[I>>2]|0)c[Q>>2]=(c[I>>2]|0)+(c[U>>2]<<2);c[R>>2]=c[(c[G>>2]|0)+28>>2];j=c[G>>2]|0;if((c[O>>2]|0)>=(c[S>>2]|0)){c[X>>2]=Qa(j,c[H>>2]|0,c[U>>2]|0,c[O>>2]|0,c[V>>2]|0,c[I>>2]|0,c[J>>2]|0,+g[W>>2]*+g[P>>2],c[K>>2]|0)|0;c[R>>2]=(c[O>>2]|0)-((c[R>>2]|0)-(c[(c[G>>2]|0)+28>>2]|0));if((c[R>>2]|0)>24&(c[N>>2]|0)!=0)c[S>>2]=(c[S>>2]|0)+((c[R>>2]|0)-24);Z=Qa(c[G>>2]|0,c[M>>2]|0,c[U>>2]|0,c[S>>2]|0,c[V>>2]|0,c[Q>>2]|0,c[J>>2]|0,+g[W>>2]*+g[T>>2],c[K>>2]>>c[V>>2])|0;c[X>>2]=c[X>>2]|Z<<(c[L>>2]>>1);Z=c[X>>2]|0;i=Y;return Z|0}else{Z=Qa(j,c[M>>2]|0,c[U>>2]|0,c[S>>2]|0,c[V>>2]|0,c[Q>>2]|0,c[J>>2]|0,+g[W>>2]*+g[T>>2],c[K>>2]>>c[V>>2])|0;c[X>>2]=Z<<(c[L>>2]>>1);c[R>>2]=(c[S>>2]|0)-((c[R>>2]|0)-(c[(c[G>>2]|0)+28>>2]|0));if((c[R>>2]|0)>24&(c[N>>2]|0)!=16384)c[O>>2]=(c[O>>2]|0)+((c[R>>2]|0)-24);Z=Qa(c[G>>2]|0,c[H>>2]|0,c[U>>2]|0,c[O>>2]|0,c[V>>2]|0,c[I>>2]|0,c[J>>2]|0,+g[W>>2]*+g[P>>2],c[K>>2]|0)|0;c[X>>2]=c[X>>2]|Z;Z=c[X>>2]|0;i=Y;return Z|0}}c[v>>2]=Ta(c[u>>2]|0,c[q>>2]|0,c[J>>2]|0,c[D>>2]|0)|0;c[p>>2]=Ua(c[u>>2]|0,c[q>>2]|0,c[J>>2]|0,c[v>>2]|0)|0;Z=(c[G>>2]|0)+28|0;c[Z>>2]=(c[Z>>2]|0)-(c[p>>2]|0);while(1){if(!((c[(c[G>>2]|0)+28>>2]|0)<0?(c[v>>2]|0)>0:0))break;Z=(c[G>>2]|0)+28|0;c[Z>>2]=(c[Z>>2]|0)+(c[p>>2]|0);c[v>>2]=(c[v>>2]|0)+-1;c[p>>2]=Ua(c[u>>2]|0,c[q>>2]|0,c[J>>2]|0,c[v>>2]|0)|0;Z=(c[G>>2]|0)+28|0;c[Z>>2]=(c[Z>>2]|0)-(c[p>>2]|0)}if(c[v>>2]|0){c[z>>2]=Va(c[v>>2]|0)|0;o=c[H>>2]|0;h=c[U>>2]|0;e=c[z>>2]|0;f=c[A>>2]|0;l=c[V>>2]|0;j=c[C>>2]|0;if(c[E>>2]|0){c[X>>2]=md(o,h,e,f,l,j)|0;Z=c[X>>2]|0;i=Y;return Z|0}else{c[X>>2]=rd(o,h,e,f,l,j,+g[W>>2])|0;Z=c[X>>2]|0;i=Y;return Z|0}}if(!(c[w>>2]|0)){Z=c[X>>2]|0;i=Y;return Z|0}c[o>>2]=(1<<c[V>>2])-1;c[K>>2]=c[K>>2]&c[o>>2];if(!(c[K>>2]|0)){oj(c[H>>2]|0,0,c[U>>2]<<2|0)|0;Z=c[X>>2]|0;i=Y;return Z|0}Z=(c[I>>2]|0)==0;c[t>>2]=0;if(Z){while(1){if((c[t>>2]|0)>=(c[U>>2]|0))break;Z=Ba(c[(c[G>>2]|0)+36>>2]|0)|0;c[(c[G>>2]|0)+36>>2]=Z;g[(c[H>>2]|0)+(c[t>>2]<<2)>>2]=+(c[(c[G>>2]|0)+36>>2]>>20|0);c[t>>2]=(c[t>>2]|0)+1}c[X>>2]=c[o>>2]}else{while(1){if((c[t>>2]|0)>=(c[U>>2]|0))break;Z=Ba(c[(c[G>>2]|0)+36>>2]|0)|0;c[(c[G>>2]|0)+36>>2]=Z;g[y>>2]=.00390625;m=+g[y>>2];g[y>>2]=c[(c[G>>2]|0)+36>>2]&32768|0?m:-m;g[(c[H>>2]|0)+(c[t>>2]<<2)>>2]=+g[(c[I>>2]|0)+(c[t>>2]<<2)>>2]+ +g[y>>2];c[t>>2]=(c[t>>2]|0)+1}c[X>>2]=c[K>>2]}td(c[H>>2]|0,c[U>>2]|0,+g[W>>2],c[(c[G>>2]|0)+40>>2]|0);Z=c[X>>2]|0;i=Y;return Z|0}function Ra(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;p=i;i=i+48|0;h=p+32|0;j=p+28|0;k=p+24|0;q=p+20|0;n=p+16|0;o=p+12|0;m=p+8|0;l=p+4|0;f=p;c[h>>2]=a;c[j>>2]=b;c[k>>2]=d;c[q>>2]=e;c[m>>2]=_(c[j>>2]|0,c[k>>2]|0)|0;d=c[m>>2]|0;c[l>>2]=ia()|0;a=i;i=i+((1*(d<<2)|0)+15&-16)|0;if(c[q>>2]|0){c[f>>2]=8+(c[k>>2]<<2)+-8;c[n>>2]=0;while(1){if((c[n>>2]|0)>=(c[k>>2]|0))break;c[o>>2]=0;while(1){b=c[n>>2]|0;if((c[o>>2]|0)>=(c[j>>2]|0))break;d=_(c[(c[f>>2]|0)+(b<<2)>>2]|0,c[j>>2]|0)|0;q=_(c[o>>2]|0,c[k>>2]|0)|0;g[a+(q+(c[n>>2]|0)<<2)>>2]=+g[(c[h>>2]|0)+(d+(c[o>>2]|0)<<2)>>2];c[o>>2]=(c[o>>2]|0)+1}c[n>>2]=b+1}o=c[h>>2]|0;n=c[m>>2]|0;n=n<<2;q=0;q=n+q|0;pj(o|0,a|0,q|0)|0;q=c[l>>2]|0;na(q|0);i=p;return}else{c[n>>2]=0;while(1){if((c[n>>2]|0)>=(c[k>>2]|0))break;c[o>>2]=0;while(1){b=c[n>>2]|0;if((c[o>>2]|0)>=(c[j>>2]|0))break;f=_(b,c[j>>2]|0)|0;q=_(c[o>>2]|0,c[k>>2]|0)|0;g[a+(q+(c[n>>2]|0)<<2)>>2]=+g[(c[h>>2]|0)+(f+(c[o>>2]|0)<<2)>>2];c[o>>2]=(c[o>>2]|0)+1}c[n>>2]=b+1}o=c[h>>2]|0;n=c[m>>2]|0;n=n<<2;q=0;q=n+q|0;pj(o|0,a|0,q|0)|0;q=c[l>>2]|0;na(q|0);i=p;return}}function Sa(a,d,e,f,h,j,k,l,m,n,o){a=a|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;var p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,$=0,aa=0,ba=0;Y=i;i=i+160|0;p=Y+148|0;K=Y+144|0;D=Y+140|0;E=Y+136|0;M=Y+132|0;N=Y+128|0;O=Y+124|0;q=Y+120|0;ba=Y+116|0;F=Y+112|0;L=Y+108|0;J=Y+104|0;V=Y+100|0;P=Y+96|0;S=Y+92|0;U=Y+88|0;W=Y+84|0;$=Y+80|0;aa=Y+76|0;X=Y+72|0;T=Y+68|0;R=Y+64|0;I=Y+60|0;H=Y+56|0;Z=Y+52|0;Q=Y+48|0;G=Y+44|0;z=Y+40|0;B=Y+36|0;A=Y+32|0;C=Y+28|0;u=Y+24|0;x=Y+20|0;y=Y+16|0;w=Y+12|0;r=Y+8|0;s=Y+4|0;t=Y;c[p>>2]=a;c[K>>2]=d;c[D>>2]=e;c[E>>2]=f;c[M>>2]=h;c[N>>2]=j;c[O>>2]=k;c[q>>2]=l;c[ba>>2]=m;c[F>>2]=n;c[L>>2]=o;c[V>>2]=0;c[T>>2]=0;c[R>>2]=c[c[p>>2]>>2];c[I>>2]=c[(c[p>>2]|0)+4>>2];c[H>>2]=c[(c[p>>2]|0)+8>>2];c[Z>>2]=c[(c[p>>2]|0)+12>>2];c[Q>>2]=c[(c[p>>2]|0)+24>>2];c[G>>2]=c[(c[p>>2]|0)+32>>2];c[$>>2]=(b[(c[(c[I>>2]|0)+56>>2]|0)+(c[H>>2]<<1)>>1]|0)+(c[ba>>2]<<3);c[aa>>2]=(c[$>>2]>>1)-((c[F>>2]|0?(c[M>>2]|0)==2:0)?16:4);c[J>>2]=Wa(c[M>>2]|0,c[c[N>>2]>>2]|0,c[aa>>2]|0,c[$>>2]|0,c[F>>2]|0)|0;if(c[F>>2]|0?(c[H>>2]|0)>=(c[Z>>2]|0):0)c[J>>2]=1;if(c[R>>2]|0)c[V>>2]=vd(c[D>>2]|0,c[E>>2]|0,c[F>>2]|0,c[M>>2]|0,c[(c[p>>2]|0)+40>>2]|0)|0;c[X>>2]=Xb(c[Q>>2]|0)|0;do if((c[J>>2]|0)!=1){if(c[R>>2]|0)c[V>>2]=(_(c[V>>2]|0,c[J>>2]|0)|0)+8192>>14;do if((c[F>>2]|0)!=0&(c[M>>2]|0)>2){c[z>>2]=3;c[B>>2]=c[V>>2];c[A>>2]=(c[J>>2]|0)/2|0;k=_(c[z>>2]|0,(c[A>>2]|0)+1|0)|0;c[C>>2]=k+(c[A>>2]|0);k=c[Q>>2]|0;if(c[R>>2]|0){if((c[B>>2]|0)<=(c[A>>2]|0))q=_(c[z>>2]|0,c[B>>2]|0)|0;else q=(c[B>>2]|0)-1-(c[A>>2]|0)+(_((c[A>>2]|0)+1|0,c[z>>2]|0)|0)|0;if((c[B>>2]|0)<=(c[A>>2]|0))p=_(c[z>>2]|0,(c[B>>2]|0)+1|0)|0;else p=(c[B>>2]|0)-(c[A>>2]|0)+(_((c[A>>2]|0)+1|0,c[z>>2]|0)|0)|0;jc(k,q,p,c[C>>2]|0);break}c[u>>2]=$b(k,c[C>>2]|0)|0;if((c[u>>2]|0)<(_((c[A>>2]|0)+1|0,c[z>>2]|0)|0))c[B>>2]=(c[u>>2]|0)/(c[z>>2]|0)|0;else c[B>>2]=(c[A>>2]|0)+1+((c[u>>2]|0)-(_((c[A>>2]|0)+1|0,c[z>>2]|0)|0));if((c[B>>2]|0)<=(c[A>>2]|0))q=_(c[z>>2]|0,c[B>>2]|0)|0;else q=(c[B>>2]|0)-1-(c[A>>2]|0)+(_((c[A>>2]|0)+1|0,c[z>>2]|0)|0)|0;if((c[B>>2]|0)<=(c[A>>2]|0))p=_(c[z>>2]|0,(c[B>>2]|0)+1|0)|0;else p=(c[B>>2]|0)-(c[A>>2]|0)+(_((c[A>>2]|0)+1|0,c[z>>2]|0)|0)|0;cc(c[Q>>2]|0,q,p,c[C>>2]|0);c[V>>2]=c[B>>2]}else{if((c[q>>2]|0)>1|(c[F>>2]|0)!=0){p=c[Q>>2]|0;if(c[R>>2]|0){rc(p,c[V>>2]|0,(c[J>>2]|0)+1|0);break}else{c[V>>2]=fc(p,(c[J>>2]|0)+1|0)|0;break}}c[x>>2]=1;c[y>>2]=_((c[J>>2]>>1)+1|0,(c[J>>2]>>1)+1|0)|0;if(!(c[R>>2]|0)){c[r>>2]=0;c[s>>2]=$b(c[Q>>2]|0,c[y>>2]|0)|0;if((c[s>>2]|0)<((_(c[J>>2]>>1,(c[J>>2]>>1)+1|0)|0)>>1|0)){c[V>>2]=((Gc((c[s>>2]<<3)+1|0)|0)-1|0)>>>1;c[x>>2]=(c[V>>2]|0)+1;c[r>>2]=(_(c[V>>2]|0,(c[V>>2]|0)+1|0)|0)>>1}else{ba=(c[J>>2]|0)+1<<1;c[V>>2]=(ba-(Gc(((c[y>>2]|0)-(c[s>>2]|0)-1<<3)+1|0)|0)|0)>>>1;c[x>>2]=(c[J>>2]|0)+1-(c[V>>2]|0);c[r>>2]=(c[y>>2]|0)-((_((c[J>>2]|0)+1-(c[V>>2]|0)|0,(c[J>>2]|0)+2-(c[V>>2]|0)|0)|0)>>1)}cc(c[Q>>2]|0,c[r>>2]|0,(c[r>>2]|0)+(c[x>>2]|0)|0,c[y>>2]|0);break}if((c[V>>2]|0)<=(c[J>>2]>>1|0))p=(c[V>>2]|0)+1|0;else p=(c[J>>2]|0)+1-(c[V>>2]|0)|0;c[x>>2]=p;if((c[V>>2]|0)<=(c[J>>2]>>1|0))p=(_(c[V>>2]|0,(c[V>>2]|0)+1|0)|0)>>1;else p=(c[y>>2]|0)-((_((c[J>>2]|0)+1-(c[V>>2]|0)|0,(c[J>>2]|0)+2-(c[V>>2]|0)|0)|0)>>1)|0;c[w>>2]=p;jc(c[Q>>2]|0,c[w>>2]|0,(c[w>>2]|0)+(c[x>>2]|0)|0,c[y>>2]|0)}while(0);c[V>>2]=Ha(c[V>>2]<<14,c[J>>2]|0)|0;if((c[R>>2]|0)!=0&(c[F>>2]|0)!=0)if(!(c[V>>2]|0)){Xa(c[I>>2]|0,c[D>>2]|0,c[E>>2]|0,c[G>>2]|0,c[H>>2]|0,c[M>>2]|0);break}else{Ya(c[D>>2]|0,c[E>>2]|0,c[M>>2]|0);break}}else if(c[F>>2]|0){if(c[R>>2]|0){c[T>>2]=(c[V>>2]|0)>8192&1;a:do if(c[T>>2]|0){c[t>>2]=0;while(1){if((c[t>>2]|0)>=(c[M>>2]|0))break a;g[(c[E>>2]|0)+(c[t>>2]<<2)>>2]=-+g[(c[E>>2]|0)+(c[t>>2]<<2)>>2];c[t>>2]=(c[t>>2]|0)+1}}while(0);Xa(c[I>>2]|0,c[D>>2]|0,c[E>>2]|0,c[G>>2]|0,c[H>>2]|0,c[M>>2]|0)}do if((c[c[N>>2]>>2]|0)>16?(c[(c[p>>2]|0)+28>>2]|0)>16:0){p=c[Q>>2]|0;if(c[R>>2]|0){pc(p,c[T>>2]|0,2);break}else{c[T>>2]=dc(p,2)|0;break}}else v=60;while(0);if((v|0)==60)c[T>>2]=0;c[V>>2]=0}while(0);ba=Xb(c[Q>>2]|0)|0;c[W>>2]=ba-(c[X>>2]|0);ba=c[N>>2]|0;c[ba>>2]=(c[ba>>2]|0)-(c[W>>2]|0);if(!(c[V>>2]|0)){c[S>>2]=32767;c[U>>2]=0;ba=c[L>>2]|0;c[ba>>2]=c[ba>>2]&(1<<c[O>>2])-1;c[P>>2]=-16384;ba=c[T>>2]|0;aa=c[K>>2]|0;c[aa>>2]=ba;aa=c[S>>2]|0;ba=c[K>>2]|0;ba=ba+4|0;c[ba>>2]=aa;ba=c[U>>2]|0;aa=c[K>>2]|0;aa=aa+8|0;c[aa>>2]=ba;aa=c[P>>2]|0;ba=c[K>>2]|0;ba=ba+12|0;c[ba>>2]=aa;ba=c[V>>2]|0;aa=c[K>>2]|0;aa=aa+16|0;c[aa>>2]=ba;aa=c[W>>2]|0;ba=c[K>>2]|0;ba=ba+20|0;c[ba>>2]=aa;i=Y;return}if((c[V>>2]|0)==16384){c[S>>2]=0;c[U>>2]=32767;ba=c[L>>2]|0;c[ba>>2]=c[ba>>2]&(1<<c[O>>2])-1<<c[O>>2];c[P>>2]=16384;ba=c[T>>2]|0;aa=c[K>>2]|0;c[aa>>2]=ba;aa=c[S>>2]|0;ba=c[K>>2]|0;ba=ba+4|0;c[ba>>2]=aa;ba=c[U>>2]|0;aa=c[K>>2]|0;aa=aa+8|0;c[aa>>2]=ba;aa=c[P>>2]|0;ba=c[K>>2]|0;ba=ba+12|0;c[ba>>2]=aa;ba=c[V>>2]|0;aa=c[K>>2]|0;aa=aa+16|0;c[aa>>2]=ba;aa=c[W>>2]|0;ba=c[K>>2]|0;ba=ba+20|0;c[ba>>2]=aa;i=Y;return}else{c[S>>2]=(Za(c[V>>2]&65535)|0)<<16>>16;c[U>>2]=(Za(16384-(c[V>>2]|0)&65535)|0)<<16>>16;ba=((c[M>>2]|0)-1<<7&65535)<<16>>16;c[P>>2]=16384+(_(ba,((_a(c[U>>2]|0,c[S>>2]|0)|0)&65535)<<16>>16)|0)>>15;ba=c[T>>2]|0;aa=c[K>>2]|0;c[aa>>2]=ba;aa=c[S>>2]|0;ba=c[K>>2]|0;ba=ba+4|0;c[ba>>2]=aa;ba=c[U>>2]|0;aa=c[K>>2]|0;aa=aa+8|0;c[aa>>2]=ba;aa=c[P>>2]|0;ba=c[K>>2]|0;ba=ba+12|0;c[ba>>2]=aa;ba=c[V>>2]|0;aa=c[K>>2]|0;aa=aa+16|0;c[aa>>2]=ba;aa=c[W>>2]|0;ba=c[K>>2]|0;ba=ba+20|0;c[ba>>2]=aa;i=Y;return}}function Ta(a,e,f,g){a=a|0;e=e|0;f=f|0;g=g|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;p=i;i=i+48|0;k=p+36|0;r=p+32|0;q=p+28|0;s=p+24|0;l=p+20|0;h=p+16|0;o=p+12|0;n=p+8|0;m=p+4|0;j=p;c[r>>2]=a;c[q>>2]=e;c[s>>2]=f;c[l>>2]=g;c[s>>2]=(c[s>>2]|0)+1;a=_(c[s>>2]|0,c[(c[r>>2]|0)+8>>2]|0)|0;c[m>>2]=(c[(c[r>>2]|0)+92+8>>2]|0)+(b[(c[(c[r>>2]|0)+92+4>>2]|0)+(a+(c[q>>2]|0)<<1)>>1]|0);c[o>>2]=0;c[n>>2]=d[c[m>>2]>>0];c[l>>2]=(c[l>>2]|0)+-1;c[h>>2]=0;while(1){if((c[h>>2]|0)>=6)break;c[j>>2]=(c[o>>2]|0)+(c[n>>2]|0)+1>>1;f=c[j>>2]|0;if((d[(c[m>>2]|0)+(c[j>>2]|0)>>0]|0)>=(c[l>>2]|0))c[n>>2]=f;else c[o>>2]=f;c[h>>2]=(c[h>>2]|0)+1}if(!(c[o>>2]|0))f=-1;else f=d[(c[m>>2]|0)+(c[o>>2]|0)>>0]|0;if(((c[l>>2]|0)-f|0)<=((d[(c[m>>2]|0)+(c[n>>2]|0)>>0]|0)-(c[l>>2]|0)|0)){c[k>>2]=c[o>>2];s=c[k>>2]|0;i=p;return s|0}else{c[k>>2]=c[n>>2];s=c[k>>2]|0;i=p;return s|0}return 0}function Ua(a,e,f,g){a=a|0;e=e|0;f=f|0;g=g|0;var h=0,j=0,k=0,l=0,m=0,n=0;k=i;i=i+32|0;m=k+16|0;l=k+12|0;n=k+8|0;h=k+4|0;j=k;c[m>>2]=a;c[l>>2]=e;c[n>>2]=f;c[h>>2]=g;c[n>>2]=(c[n>>2]|0)+1;g=_(c[n>>2]|0,c[(c[m>>2]|0)+8>>2]|0)|0;c[j>>2]=(c[(c[m>>2]|0)+92+8>>2]|0)+(b[(c[(c[m>>2]|0)+92+4>>2]|0)+(g+(c[l>>2]|0)<<1)>>1]|0);if(!(c[h>>2]|0)){n=0;i=k;return n|0}n=(d[(c[j>>2]|0)+(c[h>>2]|0)>>0]|0)+1|0;i=k;return n|0}function Va(a){a=a|0;var b=0,d=0;d=i;i=i+16|0;b=d;c[b>>2]=a;a=c[b>>2]|0;if((c[b>>2]|0)<8){b=a;i=d;return b|0}b=8+(a&7)<<(c[b>>2]>>3)-1;i=d;return b|0}function Wa(a,d,e,f,g){a=a|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;o=i;i=i+32|0;p=o+28|0;h=o+24|0;j=o+20|0;k=o+16|0;q=o+12|0;n=o+8|0;m=o+4|0;l=o;c[p>>2]=a;c[h>>2]=d;c[j>>2]=e;c[k>>2]=f;c[q>>2]=g;c[l>>2]=(c[p>>2]<<1)-1;if((c[q>>2]|0)!=0&(c[p>>2]|0)==2)c[l>>2]=(c[l>>2]|0)+-1;q=(c[h>>2]|0)+(_(c[l>>2]|0,c[j>>2]|0)|0)|0;c[m>>2]=La(q,c[l>>2]|0)|0;if(((c[h>>2]|0)-(c[k>>2]|0)-32|0)<(c[m>>2]|0))h=(c[h>>2]|0)-(c[k>>2]|0)-32|0;else h=c[m>>2]|0;c[m>>2]=h;c[m>>2]=64<(c[m>>2]|0)?64:c[m>>2]|0;if((c[m>>2]|0)<4){c[n>>2]=1;q=c[n>>2]|0;i=o;return q|0}else{c[n>>2]=b[22328+((c[m>>2]&7)<<1)>>1]>>14-(c[m>>2]>>3);c[n>>2]=(c[n>>2]|0)+1>>1<<1;q=c[n>>2]|0;i=o;return q|0}return 0}function Xa(a,b,d,e,f,h){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0;r=i;i=i+64|0;v=r+56|0;j=r+52|0;k=r+48|0;x=r+44|0;y=r+40|0;l=r+36|0;w=r+32|0;o=r+28|0;m=r+24|0;n=r+20|0;u=r+16|0;t=r+12|0;s=r+8|0;q=r+4|0;p=r;c[v>>2]=a;c[j>>2]=b;c[k>>2]=d;c[x>>2]=e;c[y>>2]=f;c[l>>2]=h;c[w>>2]=c[y>>2];g[u>>2]=+g[(c[x>>2]|0)+(c[w>>2]<<2)>>2];g[t>>2]=+g[(c[x>>2]|0)+((c[w>>2]|0)+(c[(c[v>>2]|0)+8>>2]|0)<<2)>>2];g[s>>2]=+O(+(+g[u>>2]*+g[u>>2]+1.0000000036274937e-15+ +g[t>>2]*+g[t>>2]))+1.0000000036274937e-15;g[m>>2]=+g[u>>2]/+g[s>>2];g[n>>2]=+g[t>>2]/+g[s>>2];c[o>>2]=0;while(1){if((c[o>>2]|0)>=(c[l>>2]|0))break;g[p>>2]=+g[(c[j>>2]|0)+(c[o>>2]<<2)>>2];g[q>>2]=+g[(c[k>>2]|0)+(c[o>>2]<<2)>>2];g[(c[j>>2]|0)+(c[o>>2]<<2)>>2]=+g[m>>2]*+g[p>>2]+ +g[n>>2]*+g[q>>2];c[o>>2]=(c[o>>2]|0)+1}i=r;return}function Ya(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,h=0,j=0,k=0,l=0,m=0;m=i;i=i+32|0;e=m+20|0;f=m+16|0;h=m+12|0;j=m+8|0;l=m+4|0;k=m;c[e>>2]=a;c[f>>2]=b;c[h>>2]=d;c[j>>2]=0;while(1){if((c[j>>2]|0)>=(c[h>>2]|0))break;g[k>>2]=+g[(c[e>>2]|0)+(c[j>>2]<<2)>>2]*.7071067690849304;g[l>>2]=+g[(c[f>>2]|0)+(c[j>>2]<<2)>>2]*.7071067690849304;g[(c[e>>2]|0)+(c[j>>2]<<2)>>2]=+g[k>>2]+ +g[l>>2];g[(c[f>>2]|0)+(c[j>>2]<<2)>>2]=+g[l>>2]-+g[k>>2];c[j>>2]=(c[j>>2]|0)+1}i=m;return}function Za(a){a=a|0;var d=0,e=0,f=0,g=0;e=i;i=i+16|0;g=e+6|0;f=e;d=e+4|0;b[g>>1]=a;c[f>>2]=4096+(_(b[g>>1]|0,b[g>>1]|0)|0)>>13;b[d>>1]=c[f>>2];b[d>>1]=32767-(b[d>>1]|0)+(16384+(_(b[d>>1]|0,((16384+(_(b[d>>1]|0,(8277+(16384+(_(-626,b[d>>1]|0)|0)>>15)&65535)<<16>>16)|0)>>15)+-7651&65535)<<16>>16)|0)>>15);i=e;return 1+(b[d>>1]|0)&65535|0}function _a(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0;d=i;i=i+16|0;f=d+12|0;e=d+8|0;g=d+4|0;h=d;c[f>>2]=a;c[e>>2]=b;c[g>>2]=32-(aa(c[e>>2]|0)|0);c[h>>2]=32-(aa(c[f>>2]|0)|0);c[e>>2]=c[e>>2]<<15-(c[g>>2]|0);c[f>>2]=c[f>>2]<<15-(c[h>>2]|0);a=((c[h>>2]|0)-(c[g>>2]|0)<<11)+(16384+(_((c[f>>2]&65535)<<16>>16,((16384+(_((c[f>>2]&65535)<<16>>16,-2597)|0)>>15)+7932&65535)<<16>>16)|0)>>15)|0;a=a-(16384+(_((c[e>>2]&65535)<<16>>16,((16384+(_((c[e>>2]&65535)<<16>>16,-2597)|0)>>15)+7932&65535)<<16>>16)|0)>>15)|0;i=d;return a|0}function $a(a,b,d,e,f){a=a|0;b=b|0;d=+d;e=e|0;f=f|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0;u=i;i=i+64|0;h=u+60|0;j=u+56|0;k=u+52|0;l=u+48|0;o=u+40|0;v=u+36|0;w=u+32|0;m=u+28|0;n=u+24|0;x=u+20|0;t=u+16|0;q=u+12|0;s=u+8|0;r=u+4|0;p=u;c[h>>2]=a;c[j>>2]=b;g[k>>2]=d;c[l>>2]=e;c[u+44>>2]=f;g[v>>2]=0.0;g[w>>2]=0.0;ab(c[j>>2]|0,c[h>>2]|0,c[j>>2]|0,c[l>>2]|0,v,w);g[v>>2]=+g[k>>2]*+g[v>>2];g[x>>2]=+g[k>>2];g[m>>2]=+g[x>>2]*+g[x>>2]+ +g[w>>2]-+g[v>>2]*2.0;g[n>>2]=+g[x>>2]*+g[x>>2]+ +g[w>>2]+ +g[v>>2]*2.0;if(+g[n>>2]<6.000000284984708e-04|+g[m>>2]<6.000000284984708e-04){pj(c[j>>2]|0,c[h>>2]|0,(c[l>>2]<<2)+0|0)|0;i=u;return}g[t>>2]=+g[m>>2];g[q>>2]=1.0/+O(+(+g[t>>2]));g[t>>2]=+g[n>>2];g[s>>2]=1.0/+O(+(+g[t>>2]));c[o>>2]=0;while(1){if((c[o>>2]|0)>=(c[l>>2]|0))break;g[p>>2]=+g[k>>2]*+g[(c[h>>2]|0)+(c[o>>2]<<2)>>2];g[r>>2]=+g[(c[j>>2]|0)+(c[o>>2]<<2)>>2];g[(c[h>>2]|0)+(c[o>>2]<<2)>>2]=+g[q>>2]*(+g[p>>2]-+g[r>>2]);g[(c[j>>2]|0)+(c[o>>2]<<2)>>2]=+g[s>>2]*(+g[p>>2]+ +g[r>>2]);c[o>>2]=(c[o>>2]|0)+1}i=u;return}function ab(a,b,d,e,f,h){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;var j=0.0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;t=i;i=i+48|0;k=t+32|0;l=t+28|0;m=t+24|0;n=t+20|0;o=t+16|0;p=t+12|0;q=t+8|0;r=t+4|0;s=t;c[k>>2]=a;c[l>>2]=b;c[m>>2]=d;c[n>>2]=e;c[o>>2]=f;c[p>>2]=h;g[r>>2]=0.0;g[s>>2]=0.0;c[q>>2]=0;while(1){j=+g[r>>2];if((c[q>>2]|0)>=(c[n>>2]|0))break;g[r>>2]=j+ +g[(c[k>>2]|0)+(c[q>>2]<<2)>>2]*+g[(c[l>>2]|0)+(c[q>>2]<<2)>>2];g[s>>2]=+g[s>>2]+ +g[(c[k>>2]|0)+(c[q>>2]<<2)>>2]*+g[(c[m>>2]|0)+(c[q>>2]<<2)>>2];c[q>>2]=(c[q>>2]|0)+1}g[c[o>>2]>>2]=j;g[c[p>>2]>>2]=+g[s>>2];i=t;return}function bb(a){a=a|0;var b=0,d=0,e=0,f=0;e=i;i=i+16|0;f=e+4|0;b=e;c[f>>2]=a;a=c[f>>2]|0;a:do if((a|0)<16e3)if((a|0)<12e3){switch(a|0){case 8e3:break;default:{d=7;break a}}c[b>>2]=6;break}else{switch(a|0){case 12e3:break;default:{d=7;break a}}c[b>>2]=4;break}else{if((a|0)<24e3){switch(a|0){case 16e3:break;default:{d=7;break a}}c[b>>2]=3;break}if((a|0)<48e3){switch(a|0){case 24e3:break;default:{d=7;break a}}c[b>>2]=2;break}else{switch(a|0){case 48e3:break;default:{d=7;break a}}c[b>>2]=1;break}}while(0);if((d|0)==7)c[b>>2]=0;i=e;return c[b>>2]|0}function cb(a,b,d,e,f,h,j,k,l,m,n,o){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=+h;j=+j;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;var p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0;N=i;i=i+112|0;p=N+96|0;q=N+92|0;s=N+88|0;t=N+84|0;u=N+80|0;v=N+76|0;w=N+72|0;x=N+68|0;y=N+64|0;z=N+60|0;r=N+56|0;H=N+48|0;B=N+44|0;C=N+40|0;D=N+36|0;E=N+32|0;F=N+28|0;G=N+24|0;I=N+20|0;J=N+16|0;K=N+12|0;L=N+8|0;M=N+4|0;A=N;c[p>>2]=a;c[q>>2]=b;c[s>>2]=d;c[t>>2]=e;c[u>>2]=f;g[v>>2]=h;g[w>>2]=j;c[x>>2]=k;c[y>>2]=l;c[z>>2]=m;c[r>>2]=n;c[N+52>>2]=o;if(+g[v>>2]==0.0&+g[w>>2]==0.0){if((c[q>>2]|0)==(c[p>>2]|0)){i=N;return}qj(c[p>>2]|0,c[q>>2]|0,(c[u>>2]<<2)+0|0)|0;i=N;return}g[B>>2]=+g[v>>2]*+g[128+((c[x>>2]|0)*12|0)>>2];g[C>>2]=+g[v>>2]*+g[128+((c[x>>2]|0)*12|0)+4>>2];g[D>>2]=+g[v>>2]*+g[128+((c[x>>2]|0)*12|0)+8>>2];g[E>>2]=+g[w>>2]*+g[128+((c[y>>2]|0)*12|0)>>2];g[F>>2]=+g[w>>2]*+g[128+((c[y>>2]|0)*12|0)+4>>2];g[G>>2]=+g[w>>2]*+g[128+((c[y>>2]|0)*12|0)+8>>2];g[J>>2]=+g[(c[q>>2]|0)+(0-(c[t>>2]|0)+1<<2)>>2];g[K>>2]=+g[(c[q>>2]|0)+(0-(c[t>>2]|0)<<2)>>2];g[L>>2]=+g[(c[q>>2]|0)+(0-(c[t>>2]|0)-1<<2)>>2];g[M>>2]=+g[(c[q>>2]|0)+(0-(c[t>>2]|0)-2<<2)>>2];if((+g[v>>2]==+g[w>>2]?(c[s>>2]|0)==(c[t>>2]|0):0)?(c[x>>2]|0)==(c[y>>2]|0):0)c[r>>2]=0;c[H>>2]=0;while(1){if((c[H>>2]|0)>=(c[r>>2]|0))break;g[I>>2]=+g[(c[q>>2]|0)+((c[H>>2]|0)-(c[t>>2]|0)+2<<2)>>2];g[A>>2]=+g[(c[z>>2]|0)+(c[H>>2]<<2)>>2]*+g[(c[z>>2]|0)+(c[H>>2]<<2)>>2];g[(c[p>>2]|0)+(c[H>>2]<<2)>>2]=+g[(c[q>>2]|0)+(c[H>>2]<<2)>>2]+(1.0-+g[A>>2])*+g[B>>2]*+g[(c[q>>2]|0)+((c[H>>2]|0)-(c[s>>2]|0)<<2)>>2]+(1.0-+g[A>>2])*+g[C>>2]*(+g[(c[q>>2]|0)+((c[H>>2]|0)-(c[s>>2]|0)+1<<2)>>2]+ +g[(c[q>>2]|0)+((c[H>>2]|0)-(c[s>>2]|0)-1<<2)>>2])+(1.0-+g[A>>2])*+g[D>>2]*(+g[(c[q>>2]|0)+((c[H>>2]|0)-(c[s>>2]|0)+2<<2)>>2]+ +g[(c[q>>2]|0)+((c[H>>2]|0)-(c[s>>2]|0)-2<<2)>>2])+ +g[A>>2]*+g[E>>2]*+g[K>>2]+ +g[A>>2]*+g[F>>2]*(+g[J>>2]+ +g[L>>2])+ +g[A>>2]*+g[G>>2]*(+g[I>>2]+ +g[M>>2]);g[M>>2]=+g[L>>2];g[L>>2]=+g[K>>2];g[K>>2]=+g[J>>2];g[J>>2]=+g[I>>2];c[H>>2]=(c[H>>2]|0)+1}if(!(+g[w>>2]==0.0)){db((c[p>>2]|0)+(c[H>>2]<<2)|0,(c[q>>2]|0)+(c[H>>2]<<2)|0,c[t>>2]|0,(c[u>>2]|0)-(c[H>>2]|0)|0,+g[E>>2],+g[F>>2],+g[G>>2]);i=N;return}if((c[q>>2]|0)==(c[p>>2]|0)){i=N;return}qj((c[p>>2]|0)+(c[r>>2]<<2)|0,(c[q>>2]|0)+(c[r>>2]<<2)|0,((c[u>>2]|0)-(c[r>>2]|0)<<2)+0|0)|0;i=N;return}function db(a,b,d,e,f,h,j){a=a|0;b=b|0;d=d|0;e=e|0;f=+f;h=+h;j=+j;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0;x=i;i=i+64|0;k=x+48|0;l=x+44|0;m=x+40|0;n=x+36|0;o=x+32|0;p=x+28|0;q=x+24|0;s=x+20|0;t=x+16|0;u=x+12|0;v=x+8|0;w=x+4|0;r=x;c[k>>2]=a;c[l>>2]=b;c[m>>2]=d;c[n>>2]=e;g[o>>2]=f;g[p>>2]=h;g[q>>2]=j;g[w>>2]=+g[(c[l>>2]|0)+(0-(c[m>>2]|0)-2<<2)>>2];g[v>>2]=+g[(c[l>>2]|0)+(0-(c[m>>2]|0)-1<<2)>>2];g[u>>2]=+g[(c[l>>2]|0)+(0-(c[m>>2]|0)<<2)>>2];g[t>>2]=+g[(c[l>>2]|0)+(0-(c[m>>2]|0)+1<<2)>>2];c[r>>2]=0;while(1){if((c[r>>2]|0)>=(c[n>>2]|0))break;g[s>>2]=+g[(c[l>>2]|0)+((c[r>>2]|0)-(c[m>>2]|0)+2<<2)>>2];g[(c[k>>2]|0)+(c[r>>2]<<2)>>2]=+g[(c[l>>2]|0)+(c[r>>2]<<2)>>2]+ +g[o>>2]*+g[u>>2]+ +g[p>>2]*(+g[t>>2]+ +g[v>>2])+ +g[q>>2]*(+g[s>>2]+ +g[w>>2]);g[w>>2]=+g[v>>2];g[v>>2]=+g[u>>2];g[u>>2]=+g[t>>2];g[t>>2]=+g[s>>2];c[r>>2]=(c[r>>2]|0)+1}i=x;return}function eb(a,e,f,g){a=a|0;e=e|0;f=f|0;g=g|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0;o=i;i=i+32|0;h=o+20|0;j=o+16|0;k=o+12|0;l=o+8|0;n=o+4|0;m=o;c[h>>2]=a;c[j>>2]=e;c[k>>2]=f;c[l>>2]=g;c[n>>2]=0;while(1){if((c[n>>2]|0)>=(c[(c[h>>2]|0)+8>>2]|0))break;c[m>>2]=(b[(c[(c[h>>2]|0)+32>>2]|0)+((c[n>>2]|0)+1<<1)>>1]|0)-(b[(c[(c[h>>2]|0)+32>>2]|0)+(c[n>>2]<<1)>>1]|0)<<c[k>>2];a=_(c[(c[h>>2]|0)+8>>2]|0,(c[k>>2]<<1)+(c[l>>2]|0)-1|0)|0;a=_((d[(c[(c[h>>2]|0)+92+12>>2]|0)+(a+(c[n>>2]|0))>>0]|0)+64|0,c[l>>2]|0)|0;a=(_(a,c[m>>2]|0)|0)>>2;c[(c[j>>2]|0)+(c[n>>2]<<2)>>2]=a;c[n>>2]=(c[n>>2]|0)+1}i=o;return}function fb(a){a=a|0;var b=0,d=0,e=0;e=i;i=i+16|0;b=e+4|0;d=e;c[d>>2]=a;if((c[d>>2]|0)>0|(c[d>>2]|0)<-7){c[b>>2]=25264;a=c[b>>2]|0;i=e;return a|0}else{c[b>>2]=c[164+(0-(c[d>>2]|0)<<2)>>2];a=c[b>>2]|0;i=e;return a|0}return 0}function gb(){return 25415}function hb(a){a=a|0;var b=0,d=0,e=0;b=i;i=i+16|0;d=b+4|0;e=b;c[d>>2]=a;c[e>>2]=Jc(48e3,960,0)|0;a=ib(c[e>>2]|0,c[d>>2]|0)|0;i=b;return a|0}function ib(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0;e=i;i=i+16|0;f=e+8|0;g=e+4|0;d=e;c[f>>2]=a;c[g>>2]=b;a=204+((_(c[g>>2]|0,c[(c[f>>2]|0)+4>>2]|0)|0)-1<<2)|0;c[d>>2]=a+(c[g>>2]<<10<<2)+((_((c[g>>2]|0)*3|0,c[(c[f>>2]|0)+8>>2]|0)|0)<<2);i=e;return c[d>>2]|0}function jb(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0;k=i;i=i+32|0;f=k+20|0;g=k+16|0;h=k+12|0;m=k+8|0;l=k+4|0;j=k;c[g>>2]=a;c[h>>2]=b;c[m>>2]=d;c[l>>2]=e;b=c[g>>2]|0;a=Jc(48e3,960,0)|0;c[j>>2]=kb(b,a,c[m>>2]|0,c[l>>2]|0)|0;if(c[j>>2]|0){c[f>>2]=c[j>>2];m=c[f>>2]|0;i=k;return m|0}else{m=bb(c[h>>2]|0)|0;c[(c[g>>2]|0)+28>>2]=m;c[f>>2]=0;m=c[f>>2]|0;i=k;return m|0}return 0}function kb(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0;l=i;i=i+32|0;f=l+20|0;g=l+16|0;h=l+12|0;j=l+8|0;k=l+4|0;c[g>>2]=a;c[h>>2]=b;c[j>>2]=d;c[k>>2]=e;if((c[j>>2]|0)<0|(c[j>>2]|0)>2){c[f>>2]=-1;a=c[f>>2]|0;i=l;return a|0}if((c[g>>2]|0)==0|(c[h>>2]|0)==0){c[f>>2]=-7;a=c[f>>2]|0;i=l;return a|0}else{a=c[g>>2]|0;oj(a|0,0,ib(c[h>>2]|0,c[j>>2]|0)|0)|0;c[c[g>>2]>>2]=c[h>>2];a=c[j>>2]|0;c[(c[g>>2]|0)+4>>2]=a;c[(c[g>>2]|0)+8>>2]=a;c[(c[g>>2]|0)+28>>2]=1;c[(c[g>>2]|0)+32>>2]=0;c[(c[g>>2]|0)+36>>2]=c[(c[c[g>>2]>>2]|0)+12>>2];c[(c[g>>2]|0)+48>>2]=1;c[(c[g>>2]|0)+72>>2]=c[k>>2];c[(c[g>>2]|0)+52>>2]=1;c[(c[g>>2]|0)+16>>2]=1;c[(c[g>>2]|0)+40>>2]=-1;c[(c[g>>2]|0)+44>>2]=0;c[(c[g>>2]|0)+12>>2]=0;c[(c[g>>2]|0)+24>>2]=5;c[(c[g>>2]|0)+60>>2]=24;lb(c[g>>2]|0,4028,l)|0;c[f>>2]=0;a=c[f>>2]|0;i=l;return a|0}return 0}function lb(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0;F=i;i=i+128|0;E=F+112|0;C=F+108|0;G=F+104|0;e=F+88|0;m=F+84|0;n=F+80|0;v=F+76|0;w=F+72|0;x=F+68|0;y=F+64|0;z=F+60|0;D=F+56|0;A=F+52|0;B=F+48|0;o=F+44|0;p=F+40|0;f=F+36|0;j=F+32|0;k=F+28|0;l=F+24|0;q=F+20|0;h=F+16|0;r=F+12|0;s=F+8|0;t=F+4|0;u=F;c[C>>2]=a;c[G>>2]=b;c[e>>2]=d;do switch(c[G>>2]|0){case 4010:{D=(c[e>>2]|0)+(4-1)&~(4-1);G=c[D>>2]|0;c[e>>2]=D+4;c[m>>2]=G;if((c[m>>2]|0)<0|(c[m>>2]|0)>10)e=41;else{c[(c[C>>2]|0)+24>>2]=c[m>>2];e=40}break}case 10010:{D=(c[e>>2]|0)+(4-1)&~(4-1);G=c[D>>2]|0;c[e>>2]=D+4;c[n>>2]=G;if((c[n>>2]|0)>=0?(c[n>>2]|0)<(c[(c[c[C>>2]>>2]|0)+8>>2]|0):0){c[(c[C>>2]|0)+32>>2]=c[n>>2];e=40}else e=41;break}case 10012:{D=(c[e>>2]|0)+(4-1)&~(4-1);G=c[D>>2]|0;c[e>>2]=D+4;c[v>>2]=G;if((c[v>>2]|0)>=1?(c[v>>2]|0)<=(c[(c[c[C>>2]>>2]|0)+8>>2]|0):0){c[(c[C>>2]|0)+36>>2]=c[v>>2];e=40}else e=41;break}case 10002:{D=(c[e>>2]|0)+(4-1)&~(4-1);G=c[D>>2]|0;c[e>>2]=D+4;c[w>>2]=G;if((c[w>>2]|0)<0|(c[w>>2]|0)>2)e=41;else{c[(c[C>>2]|0)+20>>2]=(c[w>>2]|0)<=1&1;c[(c[C>>2]|0)+12>>2]=(c[w>>2]|0)==0&1;e=40}break}case 4014:{D=(c[e>>2]|0)+(4-1)&~(4-1);G=c[D>>2]|0;c[e>>2]=D+4;c[x>>2]=G;if((c[x>>2]|0)<0|(c[x>>2]|0)>100)e=41;else{c[(c[C>>2]|0)+56>>2]=c[x>>2];e=40}break}case 4020:{D=(c[e>>2]|0)+(4-1)&~(4-1);G=c[D>>2]|0;c[e>>2]=D+4;c[y>>2]=G;c[(c[C>>2]|0)+52>>2]=c[y>>2];e=40;break}case 4006:{D=(c[e>>2]|0)+(4-1)&~(4-1);G=c[D>>2]|0;c[e>>2]=D+4;c[z>>2]=G;c[(c[C>>2]|0)+44>>2]=c[z>>2];e=40;break}case 4002:{d=(c[e>>2]|0)+(4-1)&~(4-1);G=c[d>>2]|0;c[e>>2]=d+4;c[D>>2]=G;if((c[D>>2]|0)<=500&(c[D>>2]|0)!=-1)e=41;else{if((c[D>>2]|0)<((c[(c[C>>2]|0)+4>>2]|0)*26e4|0))e=c[D>>2]|0;else e=(c[(c[C>>2]|0)+4>>2]|0)*26e4|0;c[D>>2]=e;c[(c[C>>2]|0)+40>>2]=c[D>>2];e=40}break}case 10008:{D=(c[e>>2]|0)+(4-1)&~(4-1);G=c[D>>2]|0;c[e>>2]=D+4;c[A>>2]=G;if((c[A>>2]|0)<1|(c[A>>2]|0)>2)e=41;else{c[(c[C>>2]|0)+8>>2]=c[A>>2];e=40}break}case 4036:{D=(c[e>>2]|0)+(4-1)&~(4-1);G=c[D>>2]|0;c[e>>2]=D+4;c[B>>2]=G;if((c[B>>2]|0)<8|(c[B>>2]|0)>24)e=41;else{c[(c[C>>2]|0)+60>>2]=c[B>>2];e=40}break}case 4037:{D=(c[e>>2]|0)+(4-1)&~(4-1);G=c[D>>2]|0;c[e>>2]=D+4;c[o>>2]=G;c[c[o>>2]>>2]=c[(c[C>>2]|0)+60>>2];e=40;break}case 4040:{D=(c[e>>2]|0)+(4-1)&~(4-1);G=c[D>>2]|0;c[e>>2]=D+4;c[p>>2]=G;c[(c[C>>2]|0)+64>>2]=c[p>>2];e=40;break}case 4028:{c[j>>2]=(c[C>>2]|0)+200+((_(c[(c[C>>2]|0)+4>>2]|0,(c[(c[c[C>>2]>>2]|0)+4>>2]|0)+1024|0)|0)<<2);c[k>>2]=(c[j>>2]|0)+((_(c[(c[C>>2]|0)+4>>2]|0,c[(c[c[C>>2]>>2]|0)+8>>2]|0)|0)<<2);c[l>>2]=(c[k>>2]|0)+((_(c[(c[C>>2]|0)+4>>2]|0,c[(c[c[C>>2]>>2]|0)+8>>2]|0)|0)<<2);oj((c[C>>2]|0)+76|0,0,(ib(c[c[C>>2]>>2]|0,c[(c[C>>2]|0)+4>>2]|0)|0)-((c[C>>2]|0)+76-(c[C>>2]|0))|0)|0;c[f>>2]=0;while(1){if((c[f>>2]|0)>=(_(c[(c[C>>2]|0)+4>>2]|0,c[(c[c[C>>2]>>2]|0)+8>>2]|0)|0))break;g[(c[l>>2]|0)+(c[f>>2]<<2)>>2]=-28.0;g[(c[k>>2]|0)+(c[f>>2]<<2)>>2]=-28.0;c[f>>2]=(c[f>>2]|0)+1}c[(c[C>>2]|0)+172>>2]=0;g[(c[C>>2]|0)+84>>2]=1.0;c[(c[C>>2]|0)+80>>2]=2;c[(c[C>>2]|0)+88>>2]=256;c[(c[C>>2]|0)+96>>2]=0;c[(c[C>>2]|0)+100>>2]=0;e=40;break}case 10016:{D=(c[e>>2]|0)+(4-1)&~(4-1);G=c[D>>2]|0;c[e>>2]=D+4;c[q>>2]=G;c[(c[C>>2]|0)+48>>2]=c[q>>2];e=40;break}case 10022:{D=(c[e>>2]|0)+(4-1)&~(4-1);G=c[D>>2]|0;c[e>>2]=D+4;c[h>>2]=G;if(c[h>>2]|0){pj((c[C>>2]|0)+120|0,c[h>>2]|0,28|0)|0;e=40}else e=40;break}case 10015:{D=(c[e>>2]|0)+(4-1)&~(4-1);G=c[D>>2]|0;c[e>>2]=D+4;c[r>>2]=G;if(!(c[r>>2]|0))e=41;else{c[c[r>>2]>>2]=c[c[C>>2]>>2];e=40}break}case 4031:{D=(c[e>>2]|0)+(4-1)&~(4-1);G=c[D>>2]|0;c[e>>2]=D+4;c[s>>2]=G;if(!(c[s>>2]|0))e=41;else{c[c[s>>2]>>2]=c[(c[C>>2]|0)+76>>2];e=40}break}case 10024:{D=(c[e>>2]|0)+(4-1)&~(4-1);G=c[D>>2]|0;c[e>>2]=D+4;c[t>>2]=G;c[(c[C>>2]|0)+68>>2]=c[t>>2];e=40;break}case 10026:{D=(c[e>>2]|0)+(4-1)&~(4-1);G=c[D>>2]|0;c[e>>2]=D+4;c[u>>2]=G;c[(c[C>>2]|0)+192>>2]=c[u>>2];e=40;break}default:{c[E>>2]=-5;G=c[E>>2]|0;i=F;return G|0}}while(0);if((e|0)==40){c[E>>2]=0;G=c[E>>2]|0;i=F;return G|0}else if((e|0)==41){c[E>>2]=-1;G=c[E>>2]|0;i=F;return G|0}return 0}function mb(a,b,d,e,f,h,j,k){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;var l=0.0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0;z=i;i=i+64|0;m=z+52|0;q=z+48|0;r=z+44|0;n=z+40|0;s=z+36|0;A=z+32|0;t=z+28|0;o=z+24|0;w=z+20|0;v=z+16|0;x=z+12|0;u=z+8|0;p=z+4|0;y=z;c[m>>2]=a;c[q>>2]=b;c[r>>2]=d;c[n>>2]=e;c[s>>2]=f;c[A>>2]=h;c[t>>2]=j;c[o>>2]=k;g[v>>2]=+g[c[A>>2]>>2];g[x>>2]=+g[c[t>>2]>>2];if(!(((c[s>>2]|0)==1?+g[(c[A>>2]|0)+4>>2]==0.0:0)^1|(c[o>>2]|0)!=0)){c[w>>2]=0;while(1){if((c[w>>2]|0)>=(c[r>>2]|0))break;A=_(c[n>>2]|0,c[w>>2]|0)|0;g[p>>2]=+g[(c[m>>2]|0)+(A<<2)>>2]*32768.0;g[(c[q>>2]|0)+(c[w>>2]<<2)>>2]=+g[p>>2]-+g[x>>2];g[x>>2]=+g[v>>2]*+g[p>>2];c[w>>2]=(c[w>>2]|0)+1}g[c[t>>2]>>2]=+g[x>>2];i=z;return}c[u>>2]=(c[r>>2]|0)/(c[s>>2]|0)|0;if((c[s>>2]|0)!=1)oj(c[q>>2]|0,0,c[r>>2]<<2|0)|0;c[w>>2]=0;while(1){if((c[w>>2]|0)>=(c[u>>2]|0))break;p=_(c[n>>2]|0,c[w>>2]|0)|0;A=_(c[w>>2]|0,c[s>>2]|0)|0;g[(c[q>>2]|0)+(A<<2)>>2]=+g[(c[m>>2]|0)+(p<<2)>>2]*32768.0;c[w>>2]=(c[w>>2]|0)+1}a:do if(c[o>>2]|0){c[w>>2]=0;while(1){if((c[w>>2]|0)>=(c[u>>2]|0))break a;A=_(c[w>>2]|0,c[s>>2]|0)|0;if(65536.0<+g[(c[q>>2]|0)+(A<<2)>>2])l=65536.0;else{A=_(c[w>>2]|0,c[s>>2]|0)|0;l=+g[(c[q>>2]|0)+(A<<2)>>2]}if(!(-65536.0>l)){A=_(c[w>>2]|0,c[s>>2]|0)|0;if(65536.0<+g[(c[q>>2]|0)+(A<<2)>>2])l=65536.0;else{A=_(c[w>>2]|0,c[s>>2]|0)|0;l=+g[(c[q>>2]|0)+(A<<2)>>2]}}else l=-65536.0;A=_(c[w>>2]|0,c[s>>2]|0)|0;g[(c[q>>2]|0)+(A<<2)>>2]=l;c[w>>2]=(c[w>>2]|0)+1}}while(0);c[w>>2]=0;while(1){if((c[w>>2]|0)>=(c[r>>2]|0))break;g[y>>2]=+g[(c[q>>2]|0)+(c[w>>2]<<2)>>2];g[(c[q>>2]|0)+(c[w>>2]<<2)>>2]=+g[y>>2]-+g[x>>2];g[x>>2]=+g[v>>2]*+g[y>>2];c[w>>2]=(c[w>>2]|0)+1}g[c[t>>2]>>2]=+g[x>>2];i=z;return}function nb(a,d,e,f,h,j){a=a|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;var k=0,l=0.0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,$=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0,ha=0,ja=0,ka=0,la=0,ma=0,oa=0,pa=0,qa=0,ra=0,sa=0,ta=0,ua=0,va=0,wa=0,xa=0,ya=0,za=0,Ba=0,Da=0,Fa=0,Ga=0,Ha=0,Ja=0,La=0,Ma=0,Na=0,Oa=0,Pa=0,Qa=0,Ra=0,Sa=0,Ta=0,Ua=0,Va=0,Wa=0,Xa=0,Ya=0,Za=0,_a=0,$a=0,ab=0,bb=0,cb=0,db=0,fb=0,gb=0,hb=0,ib=0,jb=0,kb=0,lb=0,nb=0,vb=0,yb=0;yb=i;i=i+448|0;gb=yb+440|0;hb=yb+436|0;s=yb+432|0;k=yb+428|0;o=yb+424|0;ib=yb+420|0;jb=yb+416|0;$a=yb+412|0;Za=yb+408|0;Ma=yb+404|0;Ga=yb+400|0;p=yb+352|0;y=yb+348|0;bb=yb+344|0;cb=yb+340|0;db=yb+336|0;Wa=yb+332|0;nb=yb+328|0;Ya=yb+324|0;Ja=yb+320|0;La=yb+316|0;T=yb+312|0;S=yb+308|0;xa=yb+304|0;wa=yb+300|0;fb=yb+296|0;_a=yb+292|0;U=yb+288|0;Qa=yb+284|0;P=yb+280|0;Fa=yb+276|0;Ua=yb+272|0;Sa=yb+268|0;Ra=yb+264|0;V=yb+260|0;X=yb+256|0;za=yb+252|0;fa=yb+248|0;ra=yb+244|0;Pa=yb+240|0;ma=yb+236|0;Va=yb+232|0;R=yb+228|0;Oa=yb+224|0;Na=yb+220|0;Xa=yb+216|0;O=yb+212|0;pa=yb+208|0;ka=yb+204|0;qa=yb+200|0;u=yb+196|0;ha=yb+192|0;Ta=yb+188|0;ab=yb+184|0;x=yb+180|0;Z=yb+176|0;M=yb+172|0;Ha=yb+168|0;vb=yb+164|0;la=yb+160|0;oa=yb+156|0;ea=yb+152|0;Ba=yb+148|0;m=yb+144|0;n=yb+140|0;q=yb+136|0;r=yb+132|0;kb=yb+128|0;t=yb+124|0;v=yb+120|0;z=yb+116|0;w=yb+112|0;I=yb+108|0;E=yb+104|0;C=yb+100|0;H=yb+96|0;G=yb+92|0;A=yb+88|0;B=yb+84|0;D=yb+80|0;F=yb+76|0;J=yb+72|0;K=yb+68|0;L=yb+64|0;N=yb+60|0;da=yb+56|0;ca=yb+52|0;Y=yb+48|0;W=yb+44|0;ba=yb+40|0;$=yb+36|0;ta=yb+32|0;ua=yb+28|0;ya=yb+24|0;ga=yb+20|0;ja=yb+16|0;va=yb+12|0;sa=yb+8|0;Da=yb+4|0;lb=yb;c[hb>>2]=a;c[s>>2]=d;c[k>>2]=e;c[o>>2]=f;c[ib>>2]=h;c[jb>>2]=j;c[Wa>>2]=0;c[nb>>2]=0;c[Ya>>2]=c[(c[hb>>2]|0)+4>>2];c[Ja>>2]=c[(c[hb>>2]|0)+8>>2];c[Ua>>2]=15;g[Sa>>2]=0.0;c[Ra>>2]=0;c[Va>>2]=0;c[Na>>2]=0;c[Xa>>2]=0;c[O>>2]=0;c[ka>>2]=0;c[vb>>2]=0;g[la>>2]=0.0;g[oa>>2]=0.0;g[ea>>2]=0.0;c[Ba>>2]=51e4;c[Ta>>2]=c[c[hb>>2]>>2];c[ab>>2]=c[(c[Ta>>2]|0)+8>>2];c[x>>2]=c[(c[Ta>>2]|0)+4>>2];c[Z>>2]=c[(c[Ta>>2]|0)+32>>2];c[fb>>2]=c[(c[hb>>2]|0)+32>>2];c[_a>>2]=c[(c[hb>>2]|0)+36>>2];g[pa>>2]=0.0;if((c[ib>>2]|0)<2|(c[s>>2]|0)==0){c[gb>>2]=-1;vb=c[gb>>2]|0;i=yb;return vb|0}c[k>>2]=_(c[k>>2]|0,c[(c[hb>>2]|0)+28>>2]|0)|0;c[La>>2]=0;while(1){if((c[La>>2]|0)>(c[(c[Ta>>2]|0)+36>>2]|0))break;if((c[(c[Ta>>2]|0)+44>>2]<<c[La>>2]|0)==(c[k>>2]|0))break;c[La>>2]=(c[La>>2]|0)+1}if((c[La>>2]|0)>(c[(c[Ta>>2]|0)+36>>2]|0)){c[gb>>2]=-1;vb=c[gb>>2]|0;i=yb;return vb|0}c[T>>2]=1<<c[La>>2];c[Ma>>2]=_(c[T>>2]|0,c[(c[Ta>>2]|0)+44>>2]|0)|0;c[y>>2]=(c[hb>>2]|0)+200+((_(c[Ya>>2]|0,c[x>>2]|0)|0)<<2);c[bb>>2]=(c[hb>>2]|0)+200+((_(c[Ya>>2]|0,(c[x>>2]|0)+1024|0)|0)<<2);c[cb>>2]=(c[bb>>2]|0)+((_(c[Ya>>2]|0,c[ab>>2]|0)|0)<<2);c[db>>2]=(c[cb>>2]|0)+((_(c[Ya>>2]|0,c[ab>>2]|0)|0)<<2);if(!(c[jb>>2]|0)){c[ma>>2]=1;c[xa>>2]=0}else{c[ma>>2]=ob(c[jb>>2]|0)|0;c[xa>>2]=(c[ma>>2]|0)+4>>3}c[ib>>2]=(c[ib>>2]|0)<1275?c[ib>>2]|0:1275;c[wa>>2]=(c[ib>>2]|0)-(c[xa>>2]|0);if(c[(c[hb>>2]|0)+44>>2]|0?(c[(c[hb>>2]|0)+40>>2]|0)!=-1:0){c[m>>2]=c[c[Ta>>2]>>2]>>3;n=_(c[(c[hb>>2]|0)+40>>2]|0,c[k>>2]|0)|0;c[za>>2]=(n+(c[m>>2]>>1)|0)/(c[m>>2]|0)|0;c[V>>2]=c[za>>2]>>6}else{c[za>>2]=0;c[n>>2]=_(c[(c[hb>>2]|0)+40>>2]|0,c[k>>2]|0)|0;if((c[ma>>2]|0)>1)c[n>>2]=(c[n>>2]|0)+(c[ma>>2]|0);if((c[(c[hb>>2]|0)+40>>2]|0)!=-1){if((c[ib>>2]|0)<((((c[n>>2]|0)+(c[c[Ta>>2]>>2]<<2)|0)/(c[c[Ta>>2]>>2]<<3|0)|0)-(((c[(c[hb>>2]|0)+48>>2]|0)!=0^1^1)&1)|0))k=c[ib>>2]|0;else k=(((c[n>>2]|0)+(c[c[Ta>>2]>>2]<<2)|0)/(c[c[Ta>>2]>>2]<<3|0)|0)-(((c[(c[hb>>2]|0)+48>>2]|0)!=0^1^1)&1)|0;do if(2<=(k|0))if((c[ib>>2]|0)<((((c[n>>2]|0)+(c[c[Ta>>2]>>2]<<2)|0)/(c[c[Ta>>2]>>2]<<3|0)|0)-(((c[(c[hb>>2]|0)+48>>2]|0)!=0^1^1)&1)|0)){k=c[ib>>2]|0;break}else{k=(((c[n>>2]|0)+(c[c[Ta>>2]>>2]<<2)|0)/(c[c[Ta>>2]>>2]<<3|0)|0)-(((c[(c[hb>>2]|0)+48>>2]|0)!=0^1^1)&1)|0;break}else k=2;while(0);c[ib>>2]=k}c[V>>2]=c[ib>>2]}if((c[(c[hb>>2]|0)+40>>2]|0)!=-1)c[Ba>>2]=(c[(c[hb>>2]|0)+40>>2]|0)-(_(((c[Ja>>2]|0)*40|0)+20|0,(400>>c[La>>2])-50|0)|0);if(!(c[jb>>2]|0)){ic(p,c[o>>2]|0,c[ib>>2]|0);c[jb>>2]=p}if((c[za>>2]|0)>0?c[(c[hb>>2]|0)+52>>2]|0:0){c[q>>2]=c[za>>2];if((((c[ma>>2]|0)==1?2:0)|0)>((c[za>>2]|0)+(c[q>>2]|0)-(c[(c[hb>>2]|0)+164>>2]|0)>>6|0))k=(c[ma>>2]|0)==1?2:0;else k=(c[za>>2]|0)+(c[q>>2]|0)-(c[(c[hb>>2]|0)+164>>2]|0)>>6;do if((k|0)<(c[wa>>2]|0))if((((c[ma>>2]|0)==1?2:0)|0)>((c[za>>2]|0)+(c[q>>2]|0)-(c[(c[hb>>2]|0)+164>>2]|0)>>6|0)){k=(c[ma>>2]|0)==1?2:0;break}else{k=(c[za>>2]|0)+(c[q>>2]|0)-(c[(c[hb>>2]|0)+164>>2]|0)>>6;break}else k=c[wa>>2]|0;while(0);c[r>>2]=k;if((c[r>>2]|0)<(c[wa>>2]|0)){c[ib>>2]=(c[xa>>2]|0)+(c[r>>2]|0);c[wa>>2]=c[r>>2];vc(c[jb>>2]|0,c[ib>>2]|0)}}c[fa>>2]=c[ib>>2]<<3;c[U>>2]=c[_a>>2];if((c[U>>2]|0)>(c[(c[Ta>>2]|0)+12>>2]|0))c[U>>2]=c[(c[Ta>>2]|0)+12>>2];r=_(c[Ya>>2]|0,(c[Ma>>2]|0)+(c[x>>2]|0)|0)|0;c[kb>>2]=ia()|0;e=i;i=i+((1*(r<<2)|0)+15&-16)|0;l=+g[(c[hb>>2]|0)+180>>2];r=_(c[Ja>>2]|0,(c[Ma>>2]|0)-(c[x>>2]|0)|0)|0;if(l>+pb(c[s>>2]|0,(r|0)/(c[(c[hb>>2]|0)+28>>2]|0)|0))l=+g[(c[hb>>2]|0)+180>>2];else{r=_(c[Ja>>2]|0,(c[Ma>>2]|0)-(c[x>>2]|0)|0)|0;l=+pb(c[s>>2]|0,(r|0)/(c[(c[hb>>2]|0)+28>>2]|0)|0)}g[u>>2]=l;q=_(c[Ja>>2]|0,(c[Ma>>2]|0)-(c[x>>2]|0)|0)|0;r=_(c[Ja>>2]|0,c[x>>2]|0)|0;l=+pb((c[s>>2]|0)+(((q|0)/(c[(c[hb>>2]|0)+28>>2]|0)|0)<<2)|0,(r|0)/(c[(c[hb>>2]|0)+28>>2]|0)|0);g[(c[hb>>2]|0)+180>>2]=l;if(+g[u>>2]>+g[(c[hb>>2]|0)+180>>2])l=+g[u>>2];else l=+g[(c[hb>>2]|0)+180>>2];g[u>>2]=l;c[Xa>>2]=+g[u>>2]<=1.0/+(1<<c[(c[hb>>2]|0)+60>>2]|0)&1;if((c[ma>>2]|0)==1)pc(c[jb>>2]|0,c[Xa>>2]|0,15);else c[Xa>>2]=0;if(c[Xa>>2]|0){if((c[za>>2]|0)>0){r=(c[ib>>2]|0)<((c[xa>>2]|0)+2|0)?c[ib>>2]|0:(c[xa>>2]|0)+2|0;c[ib>>2]=r;c[V>>2]=r;c[fa>>2]=c[ib>>2]<<3;c[wa>>2]=2;vc(c[jb>>2]|0,c[ib>>2]|0)}c[ma>>2]=c[ib>>2]<<3;q=c[ma>>2]|0;q=q-(ob(c[jb>>2]|0)|0)|0;r=(c[jb>>2]|0)+20|0;c[r>>2]=(c[r>>2]|0)+q}c[Za>>2]=0;do{c[t>>2]=0;c[t>>2]=(c[(c[hb>>2]|0)+16>>2]|0?+g[u>>2]>65536.0:0)&1;r=e+((_(c[Za>>2]|0,(c[Ma>>2]|0)+(c[x>>2]|0)|0)|0)<<2)|0;mb((c[s>>2]|0)+(c[Za>>2]<<2)|0,r+(c[x>>2]<<2)|0,c[Ma>>2]|0,c[Ya>>2]|0,c[(c[hb>>2]|0)+28>>2]|0,(c[Ta>>2]|0)+16|0,(c[hb>>2]|0)+148+(c[Za>>2]<<2)|0,c[t>>2]|0);r=(c[Za>>2]|0)+1|0;c[Za>>2]=r}while((r|0)<(c[Ya>>2]|0));if((c[wa>>2]|0)>3?(c[(c[hb>>2]|0)+68>>2]|0)!=0:0)if((c[fb>>2]|0)!=0|(c[Xa>>2]|0)!=0)k=0;else Q=63;else if(((c[fb>>2]|0)==0?(c[wa>>2]|0)>((c[Ja>>2]|0)*12|0):0)^1|(c[Xa>>2]|0)!=0)k=0;else Q=63;if((Q|0)==63)if(!(c[(c[hb>>2]|0)+20>>2]|0)?(c[(c[hb>>2]|0)+24>>2]|0)>=5:0){if((c[La>>2]|0)!=3?(c[(c[hb>>2]|0)+116>>2]|0)!=0:0)k=(c[(c[hb>>2]|0)+64>>2]|0)==5010;else k=0;k=k^1}else k=0;c[v>>2]=k&1;c[Va>>2]=c[(c[hb>>2]|0)+100>>2];c[R>>2]=qb(c[hb>>2]|0,e,c[y>>2]|0,c[Ya>>2]|0,c[Ma>>2]|0,c[Va>>2]|0,Ua,Sa,z,c[v>>2]|0,c[wa>>2]|0)|0;if(!(!(+g[Sa>>2]>.4000000059604645)?!(+g[(c[hb>>2]|0)+108>>2]>.4000000059604645):0))Q=70;do if((Q|0)==70){if(c[(c[hb>>2]|0)+120>>2]|0?!(+g[(c[hb>>2]|0)+120+4>>2]>.3):0)break;if(!(+(c[Ua>>2]|0)>+(c[(c[hb>>2]|0)+104>>2]|0)*1.26)?!(+(c[Ua>>2]|0)<+(c[(c[hb>>2]|0)+104>>2]|0)*.79):0)break;c[ka>>2]=1}while(0);if(!(c[R>>2]|0)){if((c[fb>>2]|0)==0?((c[ma>>2]|0)+16|0)<=(c[fa>>2]|0):0)pc(c[jb>>2]|0,0,1)}else{pc(c[jb>>2]|0,1,1);c[Ua>>2]=(c[Ua>>2]|0)+1;c[w>>2]=32-(aa(c[Ua>>2]|0)|0)-5;rc(c[jb>>2]|0,c[w>>2]|0,6);sc(c[jb>>2]|0,(c[Ua>>2]|0)-(16<<c[w>>2])|0,4+(c[w>>2]|0)|0);c[Ua>>2]=(c[Ua>>2]|0)-1;sc(c[jb>>2]|0,c[z>>2]|0,3);qc(c[jb>>2]|0,c[Va>>2]|0,25429,2)}c[nb>>2]=0;c[Wa>>2]=0;if((c[(c[hb>>2]|0)+24>>2]|0)>=1?(c[(c[hb>>2]|0)+68>>2]|0)==0:0)c[nb>>2]=rb(e,(c[Ma>>2]|0)+(c[x>>2]|0)|0,c[Ya>>2]|0,pa,O)|0;if((c[La>>2]|0)>0?(z=(ob(c[jb>>2]|0)|0)+3|0,(z|0)<=(c[fa>>2]|0)):0){if(c[nb>>2]|0)c[Wa>>2]=c[T>>2]}else{c[nb>>2]=0;c[vb>>2]=1}z=(_(c[Ya>>2]|0,c[Ma>>2]|0)|0)<<2;f=i;i=i+((1*z|0)+15&-16)|0;z=(_(c[ab>>2]|0,c[Ya>>2]|0)|0)<<2;p=i;i=i+((1*z|0)+15&-16)|0;z=(_(c[ab>>2]|0,c[Ya>>2]|0)|0)<<2;o=i;i=i+((1*z|0)+15&-16)|0;if(c[Wa>>2]|0)k=(c[(c[hb>>2]|0)+24>>2]|0)>=8;else k=0;c[M>>2]=k&1;z=(_(c[Ja>>2]|0,c[ab>>2]|0)|0)<<2;j=i;i=i+((1*z|0)+15&-16)|0;a:do if(c[M>>2]|0){sb(c[Ta>>2]|0,0,e,f,c[Ja>>2]|0,c[Ya>>2]|0,c[La>>2]|0,c[(c[hb>>2]|0)+28>>2]|0,c[(c[hb>>2]|0)+72>>2]|0);Ca(c[Ta>>2]|0,f,p,c[U>>2]|0,c[Ja>>2]|0,c[La>>2]|0);id(c[Ta>>2]|0,c[U>>2]|0,c[_a>>2]|0,p,j,c[Ja>>2]|0);c[$a>>2]=0;while(1){if((c[$a>>2]|0)>=(_(c[Ja>>2]|0,c[ab>>2]|0)|0))break a;z=j+(c[$a>>2]<<2)|0;g[z>>2]=+g[z>>2]+ +(c[La>>2]|0)*.5;c[$a>>2]=(c[$a>>2]|0)+1}}while(0);sb(c[Ta>>2]|0,c[Wa>>2]|0,e,f,c[Ja>>2]|0,c[Ya>>2]|0,c[La>>2]|0,c[(c[hb>>2]|0)+28>>2]|0,c[(c[hb>>2]|0)+72>>2]|0);if((c[Ya>>2]|0)==2&(c[Ja>>2]|0)==1)c[O>>2]=0;Ca(c[Ta>>2]|0,f,p,c[U>>2]|0,c[Ja>>2]|0,c[La>>2]|0);b:do if(c[(c[hb>>2]|0)+68>>2]|0){c[$a>>2]=2;while(1){if((c[$a>>2]|0)>=(c[_a>>2]|0))break b;if(+g[p+(c[$a>>2]<<2)>>2]<+g[p>>2]*9.999999747378752e-05)l=+g[p+(c[$a>>2]<<2)>>2];else l=+g[p>>2]*9.999999747378752e-05;g[p+(c[$a>>2]<<2)>>2]=l;if(+g[p+(c[$a>>2]<<2)>>2]>1.0000000036274937e-15)l=+g[p+(c[$a>>2]<<2)>>2];else l=1.0000000036274937e-15;g[p+(c[$a>>2]<<2)>>2]=l;c[$a>>2]=(c[$a>>2]|0)+1}}while(0);id(c[Ta>>2]|0,c[U>>2]|0,c[_a>>2]|0,p,o,c[Ja>>2]|0);z=(_(c[Ja>>2]|0,c[ab>>2]|0)|0)<<2;m=i;i=i+((1*z|0)+15&-16)|0;oj(m|0,0,c[_a>>2]<<2|0)|0;do if(!(c[fb>>2]|0)){if(!(c[(c[hb>>2]|0)+192>>2]|0))break;if(c[(c[hb>>2]|0)+68>>2]|0)break;g[H>>2]=0.0;g[G>>2]=0.0;c[A>>2]=0;if(2>(c[(c[hb>>2]|0)+92>>2]|0))k=2;else k=c[(c[hb>>2]|0)+92>>2]|0;c[I>>2]=k;c[Za>>2]=0;while(1){if((c[Za>>2]|0)>=(c[Ja>>2]|0))break;c[$a>>2]=0;while(1){if((c[$a>>2]|0)>=(c[I>>2]|0))break;z=_(c[ab>>2]|0,c[Za>>2]|0)|0;if(+g[(c[(c[hb>>2]|0)+192>>2]|0)+(z+(c[$a>>2]|0)<<2)>>2]<.25){z=_(c[ab>>2]|0,c[Za>>2]|0)|0;l=+g[(c[(c[hb>>2]|0)+192>>2]|0)+(z+(c[$a>>2]|0)<<2)>>2]}else l=.25;do if(l>-2.0){z=_(c[ab>>2]|0,c[Za>>2]|0)|0;if(!(+g[(c[(c[hb>>2]|0)+192>>2]|0)+(z+(c[$a>>2]|0)<<2)>>2]<.25)){l=.25;break}z=_(c[ab>>2]|0,c[Za>>2]|0)|0;l=+g[(c[(c[hb>>2]|0)+192>>2]|0)+(z+(c[$a>>2]|0)<<2)>>2]}else l=-2.0;while(0);g[B>>2]=l;if(+g[B>>2]>0.0)g[B>>2]=+g[B>>2]*.5;g[H>>2]=+g[H>>2]+ +g[B>>2]*+((b[(c[Z>>2]|0)+((c[$a>>2]|0)+1<<1)>>1]|0)-(b[(c[Z>>2]|0)+(c[$a>>2]<<1)>>1]|0)|0);c[A>>2]=(c[A>>2]|0)+((b[(c[Z>>2]|0)+((c[$a>>2]|0)+1<<1)>>1]|0)-(b[(c[Z>>2]|0)+(c[$a>>2]<<1)>>1]|0));g[G>>2]=+g[G>>2]+ +g[B>>2]*+(1+(c[$a>>2]<<1)-(c[I>>2]|0)|0);c[$a>>2]=(c[$a>>2]|0)+1}c[Za>>2]=(c[Za>>2]|0)+1}g[H>>2]=+g[H>>2]/+(c[A>>2]|0);g[H>>2]=+g[H>>2]+.20000000298023224;B=_(c[Ja>>2]|0,(c[I>>2]|0)-1|0)|0;B=_(B,(c[I>>2]|0)+1|0)|0;g[G>>2]=+g[G>>2]*6.0/+(_(B,c[I>>2]|0)|0);g[G>>2]=+g[G>>2]*.5;if((+g[G>>2]<.03099999949336052?+g[G>>2]:.03099999949336052)>-.03099999949336052)l=+g[G>>2]<.03099999949336052?+g[G>>2]:.03099999949336052;else l=-.03099999949336052;g[G>>2]=l;c[E>>2]=0;while(1){if((b[(c[Z>>2]|0)+((c[E>>2]|0)+1<<1)>>1]|0)>=((b[(c[Z>>2]|0)+(c[I>>2]<<1)>>1]|0)/2|0|0))break;c[E>>2]=(c[E>>2]|0)+1}c[C>>2]=0;c[$a>>2]=0;while(1){if((c[$a>>2]|0)>=(c[I>>2]|0))break;g[D>>2]=+g[H>>2]+ +g[G>>2]*+((c[$a>>2]|0)-(c[E>>2]|0)|0);l=+g[(c[(c[hb>>2]|0)+192>>2]|0)+(c[$a>>2]<<2)>>2];if((c[Ja>>2]|0)==2){if(l>+g[(c[(c[hb>>2]|0)+192>>2]|0)+((c[ab>>2]|0)+(c[$a>>2]|0)<<2)>>2])l=+g[(c[(c[hb>>2]|0)+192>>2]|0)+(c[$a>>2]<<2)>>2];else l=+g[(c[(c[hb>>2]|0)+192>>2]|0)+((c[ab>>2]|0)+(c[$a>>2]|0)<<2)>>2];g[F>>2]=l}else g[F>>2]=l;g[F>>2]=+g[F>>2]<0.0?+g[F>>2]:0.0;g[F>>2]=+g[F>>2]-+g[D>>2];if(+g[F>>2]>.25){g[m+(c[$a>>2]<<2)>>2]=+g[F>>2]-.25;c[C>>2]=(c[C>>2]|0)+1}c[$a>>2]=(c[$a>>2]|0)+1}c:do if((c[C>>2]|0)>=3){g[H>>2]=+g[H>>2]+.25;if(+g[H>>2]>0.0){g[H>>2]=0.0;g[G>>2]=0.0;oj(m|0,0,c[I>>2]<<2|0)|0;break}c[$a>>2]=0;while(1){if((c[$a>>2]|0)>=(c[I>>2]|0))break c;if(0.0>+g[m+(c[$a>>2]<<2)>>2]-.25)l=0.0;else l=+g[m+(c[$a>>2]<<2)>>2]-.25;g[m+(c[$a>>2]<<2)>>2]=l;c[$a>>2]=(c[$a>>2]|0)+1}}while(0);g[H>>2]=+g[H>>2]+.20000000298023224;g[ea>>2]=+g[G>>2]*64.0;g[la>>2]=+g[H>>2]}while(0);if(!(c[(c[hb>>2]|0)+68>>2]|0)){g[J>>2]=-10.0;g[K>>2]=0.0;if(c[Wa>>2]|0)l=+(c[La>>2]|0)*.5;else l=0.0;g[L>>2]=l;c[$a>>2]=c[fb>>2];while(1){if((c[$a>>2]|0)>=(c[_a>>2]|0))break;if(+g[J>>2]-1.0>+g[o+(c[$a>>2]<<2)>>2]-+g[L>>2])l=+g[J>>2]-1.0;else l=+g[o+(c[$a>>2]<<2)>>2]-+g[L>>2];g[J>>2]=l;if((c[Ja>>2]|0)==2){if(+g[J>>2]>+g[o+((c[$a>>2]|0)+(c[ab>>2]|0)<<2)>>2]-+g[L>>2])l=+g[J>>2];else l=+g[o+((c[$a>>2]|0)+(c[ab>>2]|0)<<2)>>2]-+g[L>>2];g[J>>2]=l}g[K>>2]=+g[K>>2]+ +g[J>>2];c[$a>>2]=(c[$a>>2]|0)+1}g[K>>2]=+g[K>>2]/+((c[_a>>2]|0)-(c[fb>>2]|0)|0);g[oa>>2]=+g[K>>2]-+g[(c[hb>>2]|0)+196>>2];if(3.0<(-1.5>+g[oa>>2]?-1.5:+g[oa>>2]))l=3.0;else l=-1.5>+g[oa>>2]?-1.5:+g[oa>>2];g[oa>>2]=l;L=(c[hb>>2]|0)+196|0;g[L>>2]=+g[L>>2]+ +g[oa>>2]*.019999999552965164}if(!(c[M>>2]|0)){M=(_(c[Ja>>2]|0,c[ab>>2]|0)|0)<<2;pj(j|0,o|0,M+0|0)|0}do if((c[La>>2]|0)>0){M=(ob(c[jb>>2]|0)|0)+3|0;if(c[nb>>2]|0?1:(M|0)>(c[fa>>2]|0))break;if((c[(c[hb>>2]|0)+24>>2]|0)<5)break;if(c[(c[hb>>2]|0)+68>>2]|0)break;if(!(tb(o,c[bb>>2]|0,c[ab>>2]|0,c[fb>>2]|0,c[_a>>2]|0,c[Ja>>2]|0)|0))break;c[nb>>2]=1;c[Wa>>2]=c[T>>2];sb(c[Ta>>2]|0,c[Wa>>2]|0,e,f,c[Ja>>2]|0,c[Ya>>2]|0,c[La>>2]|0,c[(c[hb>>2]|0)+28>>2]|0,c[(c[hb>>2]|0)+72>>2]|0);Ca(c[Ta>>2]|0,f,p,c[U>>2]|0,c[Ja>>2]|0,c[La>>2]|0);id(c[Ta>>2]|0,c[U>>2]|0,c[_a>>2]|0,p,o,c[Ja>>2]|0);c[$a>>2]=0;while(1){if((c[$a>>2]|0)>=(_(c[Ja>>2]|0,c[ab>>2]|0)|0))break;M=j+(c[$a>>2]<<2)|0;g[M>>2]=+g[M>>2]+ +(c[La>>2]|0)*.5;c[$a>>2]=(c[$a>>2]|0)+1}g[pa>>2]=.20000000298023224}while(0);do if((c[La>>2]|0)>0){M=(ob(c[jb>>2]|0)|0)+3|0;if((M|0)>(c[fa>>2]|0))break;pc(c[jb>>2]|0,c[nb>>2]|0,3)}while(0);a=(_(c[Ja>>2]|0,c[Ma>>2]|0)|0)<<2;n=i;i=i+((1*a|0)+15&-16)|0;Ea(c[Ta>>2]|0,f,n,p,c[U>>2]|0,c[Ja>>2]|0,c[T>>2]|0);a=i;i=i+((1*(c[ab>>2]<<2)|0)+15&-16)|0;d:do if((c[fb>>2]|0)==0?(c[V>>2]|0)>=((c[Ja>>2]|0)*15|0):0){if((c[(c[hb>>2]|0)+24>>2]|0)<2){Q=192;break}if(c[(c[hb>>2]|0)+68>>2]|0){Q=192;break}do if((c[V>>2]|0)>=40){if((c[V>>2]|0)<60){c[N>>2]=6;break}if((c[V>>2]|0)<100){c[N>>2]=4;break}else{c[N>>2]=3;break}}else c[N>>2]=12;while(0);c[N>>2]=c[N>>2]<<1;c[S>>2]=ub(c[Ta>>2]|0,c[U>>2]|0,c[nb>>2]|0,a,c[N>>2]|0,n,c[Ma>>2]|0,c[La>>2]|0,P,+g[pa>>2],c[O>>2]|0)|0;c[$a>>2]=c[U>>2];while(1){if((c[$a>>2]|0)>=(c[_a>>2]|0))break d;c[a+(c[$a>>2]<<2)>>2]=c[a+((c[U>>2]|0)-1<<2)>>2];c[$a>>2]=(c[$a>>2]|0)+1}}else Q=192;while(0);if((Q|0)==192){c[P>>2]=0;c[$a>>2]=0;while(1){if((c[$a>>2]|0)>=(c[_a>>2]|0))break;c[a+(c[$a>>2]<<2)>>2]=c[nb>>2];c[$a>>2]=(c[$a>>2]|0)+1}c[S>>2]=0}Q=(_(c[Ja>>2]|0,c[ab>>2]|0)|0)<<2;d=i;i=i+((1*Q|0)+15&-16)|0;Zc(c[Ta>>2]|0,c[fb>>2]|0,c[_a>>2]|0,c[U>>2]|0,o,c[bb>>2]|0,c[fa>>2]|0,d,c[jb>>2]|0,c[Ja>>2]|0,c[La>>2]|0,c[wa>>2]|0,c[(c[hb>>2]|0)+12>>2]|0,(c[hb>>2]|0)+84|0,(c[(c[hb>>2]|0)+24>>2]|0)>=4&1,c[(c[hb>>2]|0)+56>>2]|0,c[(c[hb>>2]|0)+68>>2]|0);wb(c[fb>>2]|0,c[_a>>2]|0,c[nb>>2]|0,a,c[La>>2]|0,c[S>>2]|0,c[jb>>2]|0);S=(ob(c[jb>>2]|0)|0)+4|0;if((S|0)<=(c[fa>>2]|0)){e:do if(!(c[(c[hb>>2]|0)+68>>2]|0)){do if(!(c[Wa>>2]|0)){if((c[(c[hb>>2]|0)+24>>2]|0)<3)break;if(c[fb>>2]|0?1:(c[wa>>2]|0)<((c[Ja>>2]|0)*10|0))break;if(c[R>>2]|0)k=(c[Wa>>2]|0)!=0^1;else k=0;U=Ia(c[Ta>>2]|0,n,(c[hb>>2]|0)+88|0,c[(c[hb>>2]|0)+80>>2]|0,(c[hb>>2]|0)+96|0,(c[hb>>2]|0)+100|0,k&1,c[U>>2]|0,c[Ja>>2]|0,c[T>>2]|0)|0;c[(c[hb>>2]|0)+80>>2]=U;break e}while(0);k=(c[hb>>2]|0)+80|0;if(!(c[(c[hb>>2]|0)+24>>2]|0)){c[k>>2]=0;break}else{c[k>>2]=2;break}}else{c[(c[hb>>2]|0)+100>>2]=0;c[(c[hb>>2]|0)+80>>2]=2}while(0);qc(c[jb>>2]|0,c[(c[hb>>2]|0)+80>>2]|0,25560,5)}h=i;i=i+((1*(c[ab>>2]<<2)|0)+15&-16)|0;g[ha>>2]=+xb(o,j,c[ab>>2]|0,c[fb>>2]|0,c[_a>>2]|0,c[Ja>>2]|0,h,c[(c[hb>>2]|0)+60>>2]|0,c[(c[Ta>>2]|0)+56>>2]|0,c[nb>>2]|0,c[(c[hb>>2]|0)+44>>2]|0,c[(c[hb>>2]|0)+52>>2]|0,c[Z>>2]|0,c[La>>2]|0,c[V>>2]|0,qa,c[(c[hb>>2]|0)+68>>2]|0,m);if(c[(c[hb>>2]|0)+68>>2]|0){if(8<((c[V>>2]|0)/3|0|0))k=8;else k=(c[V>>2]|0)/3|0;c[h>>2]=k}f=i;i=i+((1*(c[ab>>2]<<2)|0)+15&-16)|0;eb(c[Ta>>2]|0,f,c[La>>2]|0,c[Ja>>2]|0);c[X>>2]=6;c[fa>>2]=c[fa>>2]<<3;c[ra>>2]=0;c[ma>>2]=Xb(c[jb>>2]|0)|0;c[$a>>2]=c[fb>>2];while(1){k=c[Ja>>2]|0;if((c[$a>>2]|0)>=(c[_a>>2]|0))break;k=_(k,(b[(c[Z>>2]|0)+((c[$a>>2]|0)+1<<1)>>1]|0)-(b[(c[Z>>2]|0)+(c[$a>>2]<<1)>>1]|0)|0)|0;c[da>>2]=k<<c[La>>2];k=c[da>>2]|0;if((c[da>>2]<<3|0)<((48>(c[da>>2]|0)?48:c[da>>2]|0)|0))k=k<<3;else k=48>(k|0)?48:c[da>>2]|0;c[ca>>2]=k;c[Y>>2]=c[X>>2];c[W>>2]=0;c[ba>>2]=0;while(1){if(((c[ma>>2]|0)+(c[Y>>2]<<3)|0)>=((c[fa>>2]|0)-(c[ra>>2]|0)|0))break;if((c[W>>2]|0)>=(c[f+(c[$a>>2]<<2)>>2]|0))break;c[$>>2]=(c[ba>>2]|0)<(c[h+(c[$a>>2]<<2)>>2]|0)&1;pc(c[jb>>2]|0,c[$>>2]|0,c[Y>>2]|0);c[ma>>2]=Xb(c[jb>>2]|0)|0;if(!(c[$>>2]|0))break;c[W>>2]=(c[W>>2]|0)+(c[ca>>2]|0);c[ra>>2]=(c[ra>>2]|0)+(c[ca>>2]|0);c[Y>>2]=1;c[ba>>2]=(c[ba>>2]|0)+1}if(c[ba>>2]|0)c[X>>2]=2>((c[X>>2]|0)-1|0)?2:(c[X>>2]|0)-1|0;c[h+(c[$a>>2]<<2)>>2]=c[W>>2];c[$a>>2]=(c[$a>>2]|0)+1}if((k|0)==2){if(c[La>>2]|0)c[Ra>>2]=Ab(c[Ta>>2]|0,n,c[La>>2]|0,c[Ma>>2]|0)|0;da=Aa(+((c[Ba>>2]|0)/1e3|0|0),196,280,21,c[(c[hb>>2]|0)+188>>2]|0)|0;c[(c[hb>>2]|0)+188>>2]=da;if((c[fb>>2]|0)>(c[(c[hb>>2]|0)+188>>2]|0))k=c[fb>>2]|0;else k=c[(c[hb>>2]|0)+188>>2]|0;do if((c[_a>>2]|0)>=(k|0))if((c[fb>>2]|0)>(c[(c[hb>>2]|0)+188>>2]|0)){k=c[fb>>2]|0;break}else{k=c[(c[hb>>2]|0)+188>>2]|0;break}else k=c[_a>>2]|0;while(0);c[(c[hb>>2]|0)+188>>2]=k}c[Fa>>2]=5;if(((c[ma>>2]|0)+48|0)<=((c[fa>>2]|0)-(c[ra>>2]|0)|0)){if(c[(c[hb>>2]|0)+68>>2]|0)c[Fa>>2]=5;else c[Fa>>2]=Bb(c[Ta>>2]|0,n,o,c[_a>>2]|0,c[La>>2]|0,c[Ja>>2]|0,c[Ma>>2]|0,(c[hb>>2]|0)+120|0,(c[hb>>2]|0)+184|0,+g[pa>>2],c[(c[hb>>2]|0)+188>>2]|0,+g[ea>>2],c[(c[hb>>2]|0)+72>>2]|0)|0;qc(c[jb>>2]|0,c[Fa>>2]|0,25564,7);c[ma>>2]=Xb(c[jb>>2]|0)|0}if((c[za>>2]|0)>0){c[va>>2]=(c[(c[Ta>>2]|0)+36>>2]|0)-(c[La>>2]|0);if((c[ib>>2]|0)<(1275>>3-(c[La>>2]|0)|0))k=c[ib>>2]|0;else k=1275>>3-(c[La>>2]|0);c[ib>>2]=k;c[ga>>2]=(c[za>>2]|0)-(((c[Ja>>2]|0)*40|0)+20<<3);if(c[(c[hb>>2]|0)+52>>2]|0)c[ga>>2]=(c[ga>>2]|0)+(c[(c[hb>>2]|0)+172>>2]>>c[va>>2]);c[ya>>2]=Db(c[Ta>>2]|0,(c[hb>>2]|0)+120|0,c[ga>>2]|0,c[La>>2]|0,c[Ba>>2]|0,c[(c[hb>>2]|0)+92>>2]|0,c[Ja>>2]|0,c[(c[hb>>2]|0)+188>>2]|0,c[(c[hb>>2]|0)+52>>2]|0,+g[(c[hb>>2]|0)+184>>2],c[qa>>2]|0,+g[pa>>2],c[ka>>2]|0,+g[ha>>2],c[(c[hb>>2]|0)+64>>2]|0,c[(c[hb>>2]|0)+68>>2]|0,(c[(c[hb>>2]|0)+192>>2]|0)!=0&1,+g[la>>2],+g[oa>>2])|0;c[ya>>2]=(c[ya>>2]|0)+(c[ma>>2]|0);c[ja>>2]=((c[ma>>2]|0)+(c[ra>>2]|0)+64-1>>6)+2-(c[xa>>2]|0);c[wa>>2]=(c[ya>>2]|0)+32>>6;c[wa>>2]=(c[ja>>2]|0)>(c[wa>>2]|0)?c[ja>>2]|0:c[wa>>2]|0;if((c[ib>>2]|0)<((c[wa>>2]|0)+(c[xa>>2]|0)|0))k=c[ib>>2]|0;else k=(c[wa>>2]|0)+(c[xa>>2]|0)|0;c[wa>>2]=k-(c[xa>>2]|0);c[ua>>2]=(c[ya>>2]|0)-(c[za>>2]|0);c[ya>>2]=c[wa>>2]<<6;if(c[Xa>>2]|0){c[wa>>2]=2;c[ya>>2]=128;c[ua>>2]=0}if((c[(c[hb>>2]|0)+176>>2]|0)<970){ra=(c[hb>>2]|0)+176|0;c[ra>>2]=(c[ra>>2]|0)+1;g[ta>>2]=1.0/+((c[(c[hb>>2]|0)+176>>2]|0)+20|0)}else g[ta>>2]=1.0000000474974513e-03;if(c[(c[hb>>2]|0)+52>>2]|0){ra=(c[hb>>2]|0)+164|0;c[ra>>2]=(c[ra>>2]|0)+((c[ya>>2]|0)-(c[za>>2]|0))}if(c[(c[hb>>2]|0)+52>>2]|0){ya=_(c[ua>>2]|0,1<<c[va>>2])|0;za=(c[hb>>2]|0)+168|0;c[za>>2]=(c[za>>2]|0)+~~(+g[ta>>2]*+(ya-(c[(c[hb>>2]|0)+172>>2]|0)-(c[(c[hb>>2]|0)+168>>2]|0)|0));c[(c[hb>>2]|0)+172>>2]=0-(c[(c[hb>>2]|0)+168>>2]|0)}do if(c[(c[hb>>2]|0)+52>>2]|0){if((c[(c[hb>>2]|0)+164>>2]|0)>=0)break;c[sa>>2]=(0-(c[(c[hb>>2]|0)+164>>2]|0)|0)/64|0;c[wa>>2]=(c[wa>>2]|0)+(c[Xa>>2]|0?0:c[sa>>2]|0);c[(c[hb>>2]|0)+164>>2]=0}while(0);if((c[ib>>2]|0)<((c[wa>>2]|0)+(c[xa>>2]|0)|0))k=c[ib>>2]|0;else k=(c[wa>>2]|0)+(c[xa>>2]|0)|0;c[ib>>2]=k;vc(c[jb>>2]|0,c[ib>>2]|0)}m=i;i=i+((1*(c[ab>>2]<<2)|0)+15&-16)|0;j=i;i=i+((1*(c[ab>>2]<<2)|0)+15&-16)|0;e=i;i=i+((1*(c[ab>>2]<<2)|0)+15&-16)|0;za=c[ib>>2]<<3<<3;c[Ga>>2]=za-(Xb(c[jb>>2]|0)|0)-1;if((c[nb>>2]|0)!=0&(c[La>>2]|0)>=2)k=(c[Ga>>2]|0)>=((c[La>>2]|0)+2<<3|0);else k=0;c[Oa>>2]=k?8:0;c[Ga>>2]=(c[Ga>>2]|0)-(c[Oa>>2]|0);c[Ha>>2]=(c[_a>>2]|0)-1;if(c[(c[hb>>2]|0)+120>>2]|0){do if((c[Ba>>2]|0)>=((c[Ja>>2]|0)*32e3|0)){if((c[Ba>>2]|0)<((c[Ja>>2]|0)*48e3|0)){c[Da>>2]=16;break}if((c[Ba>>2]|0)<((c[Ja>>2]|0)*6e4|0)){c[Da>>2]=18;break}if((c[Ba>>2]|0)<((c[Ja>>2]|0)*8e4|0)){c[Da>>2]=19;break}else{c[Da>>2]=20;break}}else c[Da>>2]=13;while(0);if((c[(c[hb>>2]|0)+120+24>>2]|0)>(c[Da>>2]|0))k=c[(c[hb>>2]|0)+120+24>>2]|0;else k=c[Da>>2]|0;c[Ha>>2]=k}if(c[(c[hb>>2]|0)+68>>2]|0)c[Ha>>2]=1;c[Qa>>2]=jd(c[Ta>>2]|0,c[fb>>2]|0,c[_a>>2]|0,h,f,c[Fa>>2]|0,(c[hb>>2]|0)+188|0,Ra,c[Ga>>2]|0,Pa,j,m,e,c[Ja>>2]|0,c[La>>2]|0,c[jb>>2]|0,1,c[(c[hb>>2]|0)+92>>2]|0,c[Ha>>2]|0)|0;if(c[(c[hb>>2]|0)+92>>2]|0){if(((c[(c[hb>>2]|0)+92>>2]|0)-1|0)>(c[Qa>>2]|0))k=(c[(c[hb>>2]|0)+92>>2]|0)-1|0;else k=c[Qa>>2]|0;f=c[(c[hb>>2]|0)+92>>2]|0;do if(((c[(c[hb>>2]|0)+92>>2]|0)+1|0)>=(k|0))if((f-1|0)>(c[Qa>>2]|0)){k=(c[(c[hb>>2]|0)+92>>2]|0)-1|0;break}else{k=c[Qa>>2]|0;break}else k=f+1|0;while(0);c[(c[hb>>2]|0)+92>>2]=k}else c[(c[hb>>2]|0)+92>>2]=c[Qa>>2];dd(c[Ta>>2]|0,c[fb>>2]|0,c[_a>>2]|0,c[bb>>2]|0,d,m,c[jb>>2]|0,c[Ja>>2]|0);Ga=_(c[Ja>>2]|0,c[ab>>2]|0)|0;Ha=i;i=i+((1*Ga|0)+15&-16)|0;Ka(1,c[Ta>>2]|0,c[fb>>2]|0,c[_a>>2]|0,n,(c[Ja>>2]|0)==2?n+(c[Ma>>2]<<2)|0:0,Ha,p,j,c[Wa>>2]|0,c[(c[hb>>2]|0)+80>>2]|0,c[Ra>>2]|0,c[(c[hb>>2]|0)+188>>2]|0,a,(c[ib>>2]<<6)-(c[Oa>>2]|0)|0,c[Pa>>2]|0,c[jb>>2]|0,c[La>>2]|0,c[Qa>>2]|0,(c[hb>>2]|0)+76|0,c[(c[hb>>2]|0)+72>>2]|0);if((c[Oa>>2]|0)>0){c[Na>>2]=(c[(c[hb>>2]|0)+116>>2]|0)<2&1;sc(c[jb>>2]|0,c[Na>>2]|0,1)}Pa=c[Ta>>2]|0;Qa=c[fb>>2]|0;Ra=c[_a>>2]|0;Ta=c[bb>>2]|0;Wa=c[ib>>2]<<3;Wa=Wa-(ob(c[jb>>2]|0)|0)|0;ed(Pa,Qa,Ra,Ta,d,m,e,Wa,c[jb>>2]|0,c[Ja>>2]|0);f:do if(c[Xa>>2]|0){c[$a>>2]=0;while(1){if((c[$a>>2]|0)>=(_(c[Ja>>2]|0,c[ab>>2]|0)|0))break f;g[(c[bb>>2]|0)+(c[$a>>2]<<2)>>2]=-28.0;c[$a>>2]=(c[$a>>2]|0)+1}}while(0);c[(c[hb>>2]|0)+104>>2]=c[Ua>>2];g[(c[hb>>2]|0)+108>>2]=+g[Sa>>2];c[(c[hb>>2]|0)+112>>2]=c[Va>>2];if((c[Ya>>2]|0)==2&(c[Ja>>2]|0)==1)pj((c[bb>>2]|0)+(c[ab>>2]<<2)|0,c[bb>>2]|0,(c[ab>>2]<<2)+0|0)|0;g:do if(c[nb>>2]|0){c[$a>>2]=0;while(1){if((c[$a>>2]|0)>=(_(c[Ya>>2]|0,c[ab>>2]|0)|0))break g;k=c[$a>>2]|0;if(+g[(c[cb>>2]|0)+(c[$a>>2]<<2)>>2]<+g[(c[bb>>2]|0)+(c[$a>>2]<<2)>>2])l=+g[(c[cb>>2]|0)+(k<<2)>>2];else l=+g[(c[bb>>2]|0)+(k<<2)>>2];g[(c[cb>>2]|0)+(c[$a>>2]<<2)>>2]=l;c[$a>>2]=(c[$a>>2]|0)+1}}else{Xa=(_(c[Ya>>2]|0,c[ab>>2]|0)|0)<<2;pj(c[db>>2]|0,c[cb>>2]|0,Xa+0|0)|0;Xa=(_(c[Ya>>2]|0,c[ab>>2]|0)|0)<<2;pj(c[cb>>2]|0,c[bb>>2]|0,Xa+0|0)|0}while(0);c[Za>>2]=0;do{c[$a>>2]=0;while(1){if((c[$a>>2]|0)>=(c[fb>>2]|0))break;Xa=_(c[Za>>2]|0,c[ab>>2]|0)|0;g[(c[bb>>2]|0)+(Xa+(c[$a>>2]|0)<<2)>>2]=0.0;Xa=_(c[Za>>2]|0,c[ab>>2]|0)|0;g[(c[db>>2]|0)+(Xa+(c[$a>>2]|0)<<2)>>2]=-28.0;Xa=_(c[Za>>2]|0,c[ab>>2]|0)|0;g[(c[cb>>2]|0)+(Xa+(c[$a>>2]|0)<<2)>>2]=-28.0;c[$a>>2]=(c[$a>>2]|0)+1}c[$a>>2]=c[_a>>2];while(1){k=c[Za>>2]|0;if((c[$a>>2]|0)>=(c[ab>>2]|0))break;Xa=_(k,c[ab>>2]|0)|0;g[(c[bb>>2]|0)+(Xa+(c[$a>>2]|0)<<2)>>2]=0.0;Xa=_(c[Za>>2]|0,c[ab>>2]|0)|0;g[(c[db>>2]|0)+(Xa+(c[$a>>2]|0)<<2)>>2]=-28.0;Xa=_(c[Za>>2]|0,c[ab>>2]|0)|0;g[(c[cb>>2]|0)+(Xa+(c[$a>>2]|0)<<2)>>2]=-28.0;c[$a>>2]=(c[$a>>2]|0)+1}Xa=k+1|0;c[Za>>2]=Xa}while((Xa|0)<(c[Ya>>2]|0));k=(c[hb>>2]|0)+116|0;if((c[nb>>2]|0)!=0|(c[vb>>2]|0)!=0)c[k>>2]=(c[k>>2]|0)+1;else c[k>>2]=0;c[(c[hb>>2]|0)+76>>2]=c[(c[jb>>2]|0)+28>>2];wc(c[jb>>2]|0);if(Eb(c[jb>>2]|0)|0){c[gb>>2]=-3;c[lb>>2]=1}else{c[gb>>2]=c[ib>>2];c[lb>>2]=1}na(c[kb>>2]|0);vb=c[gb>>2]|0;i=yb;return vb|0}function ob(a){a=a|0;var b=0,d=0;b=i;i=i+16|0;d=b;c[d>>2]=a;a=(c[(c[d>>2]|0)+20>>2]|0)-(32-(aa(c[(c[d>>2]|0)+28>>2]|0)|0))|0;i=b;return a|0}function pb(a,b){a=a|0;b=b|0;var d=0.0,e=0,f=0,h=0,j=0,k=0,l=0;l=i;i=i+32|0;e=l+16|0;f=l+12|0;h=l+8|0;j=l+4|0;k=l;c[e>>2]=a;c[f>>2]=b;g[j>>2]=0.0;g[k>>2]=0.0;c[h>>2]=0;while(1){d=+g[j>>2];if((c[h>>2]|0)>=(c[f>>2]|0))break;if(d>+g[(c[e>>2]|0)+(c[h>>2]<<2)>>2])d=+g[j>>2];else d=+g[(c[e>>2]|0)+(c[h>>2]<<2)>>2];g[j>>2]=d;if(+g[k>>2]<+g[(c[e>>2]|0)+(c[h>>2]<<2)>>2])d=+g[k>>2];else d=+g[(c[e>>2]|0)+(c[h>>2]<<2)>>2];g[k>>2]=d;c[h>>2]=(c[h>>2]|0)+1}i=l;return +(d>-+g[k>>2]?+g[j>>2]:-+g[k>>2])}function qb(a,b,d,e,f,h,j,k,l,m,n){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;var o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0;L=i;i=i+96|0;s=L+92|0;t=L+88|0;v=L+84|0;w=L+80|0;x=L+76|0;y=L+72|0;z=L+68|0;A=L+64|0;B=L+60|0;q=L+56|0;o=L+52|0;C=L+48|0;J=L+40|0;E=L+36|0;I=L+32|0;D=L+28|0;r=L+24|0;H=L+20|0;K=L+16|0;G=L+12|0;u=L+8|0;p=L+4|0;F=L;c[s>>2]=a;c[t>>2]=b;c[v>>2]=d;c[w>>2]=e;c[x>>2]=f;c[y>>2]=h;c[z>>2]=j;c[A>>2]=k;c[B>>2]=l;c[q>>2]=m;c[o>>2]=n;c[E>>2]=c[c[s>>2]>>2];c[G>>2]=c[(c[E>>2]|0)+4>>2];l=_(c[w>>2]|0,(c[x>>2]|0)+1024|0)|0;c[u>>2]=ia()|0;a=i;i=i+((1*(l<<2)|0)+15&-16)|0;c[J>>2]=a;c[J+4>>2]=a+((c[x>>2]|0)+1024<<2);c[C>>2]=0;do{pj(c[J+(c[C>>2]<<2)>>2]|0,(c[v>>2]|0)+(c[C>>2]<<10<<2)|0,4096|0)|0;a=(c[t>>2]|0)+((_(c[C>>2]|0,(c[x>>2]|0)+(c[G>>2]|0)|0)|0)<<2)|0;pj((c[J+(c[C>>2]<<2)>>2]|0)+4096|0,a+(c[G>>2]<<2)|0,(c[x>>2]<<2)+0|0)|0;a=(c[C>>2]|0)+1|0;c[C>>2]=a}while((a|0)<(c[w>>2]|0));if(c[q>>2]|0){l=1024+(c[x>>2]|0)>>1;c[p>>2]=ia()|0;a=i;i=i+((1*(l<<2)|0)+15&-16)|0;Kc(J,a,1024+(c[x>>2]|0)|0,c[w>>2]|0,c[(c[s>>2]|0)+72>>2]|0);Pc(a+2048|0,a,c[x>>2]|0,979,I,c[(c[s>>2]|0)+72>>2]|0);c[I>>2]=1024-(c[I>>2]|0);g[D>>2]=+Rc(a,1024,15,c[x>>2]|0,I,c[(c[s>>2]|0)+104>>2]|0,+g[(c[s>>2]|0)+108>>2],c[(c[s>>2]|0)+72>>2]|0);if((c[I>>2]|0)>1022)c[I>>2]=1022;g[D>>2]=+g[D>>2]*.699999988079071;if((c[(c[s>>2]|0)+56>>2]|0)>2)g[D>>2]=+g[D>>2]*.5;if((c[(c[s>>2]|0)+56>>2]|0)>4)g[D>>2]=+g[D>>2]*.5;if((c[(c[s>>2]|0)+56>>2]|0)>8)g[D>>2]=0.0;na(c[p>>2]|0)}else{g[D>>2]=0.0;c[I>>2]=15}g[r>>2]=.20000000298023224;a=(N((c[I>>2]|0)-(c[(c[s>>2]|0)+104>>2]|0)|0)|0)*10|0;if((a|0)>(c[I>>2]|0))g[r>>2]=+g[r>>2]+.20000000298023224;if((c[o>>2]|0)<25)g[r>>2]=+g[r>>2]+.10000000149011612;if((c[o>>2]|0)<35)g[r>>2]=+g[r>>2]+.10000000149011612;if(+g[(c[s>>2]|0)+108>>2]>.4000000059604645)g[r>>2]=+g[r>>2]-.10000000149011612;if(+g[(c[s>>2]|0)+108>>2]>.550000011920929)g[r>>2]=+g[r>>2]-.10000000149011612;g[r>>2]=+g[r>>2]>.20000000298023224?+g[r>>2]:.20000000298023224;if(+g[D>>2]<+g[r>>2]){g[D>>2]=0.0;c[H>>2]=0;c[K>>2]=0}else{if(+N(+(+g[D>>2]-+g[(c[s>>2]|0)+108>>2]))<.10000000149011612)g[D>>2]=+g[(c[s>>2]|0)+108>>2];c[K>>2]=~~+M(+(+g[D>>2]*32.0/3.0+.5))-1;if(0>((7<(c[K>>2]|0)?7:c[K>>2]|0)|0))o=0;else o=7<(c[K>>2]|0)?7:c[K>>2]|0;c[K>>2]=o;g[D>>2]=+((c[K>>2]|0)+1|0)*.09375;c[H>>2]=1}c[C>>2]=0;do{c[F>>2]=(c[(c[E>>2]|0)+44>>2]|0)-(c[G>>2]|0);if((c[(c[s>>2]|0)+104>>2]|0)>15)o=c[(c[s>>2]|0)+104>>2]|0;else o=15;c[(c[s>>2]|0)+104>>2]=o;l=(c[t>>2]|0)+((_(c[C>>2]|0,(c[x>>2]|0)+(c[G>>2]|0)|0)|0)<<2)|0;a=(c[s>>2]|0)+200+((_(c[C>>2]|0,c[G>>2]|0)|0)<<2)|0;pj(l|0,a|0,(c[G>>2]<<2)+0|0)|0;if(c[F>>2]|0){a=(c[t>>2]|0)+((_(c[C>>2]|0,(c[x>>2]|0)+(c[G>>2]|0)|0)|0)<<2)|0;cb(a+(c[G>>2]<<2)|0,(c[J+(c[C>>2]<<2)>>2]|0)+4096|0,c[(c[s>>2]|0)+104>>2]|0,c[(c[s>>2]|0)+104>>2]|0,c[F>>2]|0,-+g[(c[s>>2]|0)+108>>2],-+g[(c[s>>2]|0)+108>>2],c[(c[s>>2]|0)+112>>2]|0,c[(c[s>>2]|0)+112>>2]|0,0,0,c[(c[s>>2]|0)+72>>2]|0)}a=(c[t>>2]|0)+((_(c[C>>2]|0,(c[x>>2]|0)+(c[G>>2]|0)|0)|0)<<2)|0;cb(a+(c[G>>2]<<2)+(c[F>>2]<<2)|0,(c[J+(c[C>>2]<<2)>>2]|0)+4096+(c[F>>2]<<2)|0,c[(c[s>>2]|0)+104>>2]|0,c[I>>2]|0,(c[x>>2]|0)-(c[F>>2]|0)|0,-+g[(c[s>>2]|0)+108>>2],-+g[D>>2],c[(c[s>>2]|0)+112>>2]|0,c[y>>2]|0,c[(c[E>>2]|0)+60>>2]|0,c[G>>2]|0,c[(c[s>>2]|0)+72>>2]|0);a=(c[s>>2]|0)+200+((_(c[C>>2]|0,c[G>>2]|0)|0)<<2)|0;o=(c[t>>2]|0)+((_(c[C>>2]|0,(c[x>>2]|0)+(c[G>>2]|0)|0)|0)<<2)|0;pj(a|0,o+(c[x>>2]<<2)|0,(c[G>>2]<<2)+0|0)|0;o=(c[v>>2]|0)+(c[C>>2]<<10<<2)|0;if((c[x>>2]|0)>1024)qj(o|0,(c[J+(c[C>>2]<<2)>>2]|0)+(c[x>>2]<<2)|0,4096|0)|0;else{qj(o|0,(c[v>>2]|0)+(c[C>>2]<<10<<2)+(c[x>>2]<<2)|0,(1024-(c[x>>2]|0)<<2)+0|0)|0;qj((c[v>>2]|0)+(c[C>>2]<<10<<2)+4096+(0-(c[x>>2]|0)<<2)|0,(c[J+(c[C>>2]<<2)>>2]|0)+4096|0,(c[x>>2]<<2)+0|0)|0}a=(c[C>>2]|0)+1|0;c[C>>2]=a}while((a|0)<(c[w>>2]|0));g[c[A>>2]>>2]=+g[D>>2];c[c[z>>2]>>2]=c[I>>2];c[c[B>>2]>>2]=c[K>>2];K=c[H>>2]|0;na(c[u>>2]|0);i=L;return K|0}function rb(a,b,e,f,h){a=a|0;b=b|0;e=e|0;f=f|0;h=h|0;var j=0.0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0;G=i;i=i+96|0;k=G+84|0;l=G+80|0;m=G+76|0;C=G+72|0;n=G+68|0;p=G+64|0;v=G+60|0;w=G+56|0;E=G+52|0;s=G+48|0;o=G+44|0;F=G+40|0;r=G+36|0;D=G+32|0;u=G+28|0;y=G+24|0;x=G+20|0;t=G+16|0;z=G+12|0;B=G+8|0;A=G+4|0;q=G;c[k>>2]=a;c[l>>2]=b;c[m>>2]=e;c[C>>2]=f;c[n>>2]=h;c[E>>2]=0;c[s>>2]=0;f=c[l>>2]|0;c[D>>2]=ia()|0;b=i;i=i+((1*(f<<2)|0)+15&-16)|0;c[r>>2]=(c[l>>2]|0)/2|0;c[o>>2]=0;while(1){if((c[o>>2]|0)>=(c[m>>2]|0))break;c[y>>2]=0;g[v>>2]=0.0;g[w>>2]=0.0;c[p>>2]=0;while(1){if((c[p>>2]|0)>=(c[l>>2]|0))break;f=(c[p>>2]|0)+(_(c[o>>2]|0,c[l>>2]|0)|0)|0;g[z>>2]=+g[(c[k>>2]|0)+(f<<2)>>2];g[B>>2]=+g[v>>2]+ +g[z>>2];g[v>>2]=+g[w>>2]+ +g[B>>2]-+g[z>>2]*2.0;g[w>>2]=+g[z>>2]-+g[B>>2]*.5;g[b+(c[p>>2]<<2)>>2]=+g[B>>2];c[p>>2]=(c[p>>2]|0)+1}e=b;a=e+48|0;do{c[e>>2]=0;e=e+4|0}while((e|0)<(a|0));g[u>>2]=0.0;g[v>>2]=0.0;c[p>>2]=0;while(1){if((c[p>>2]|0)>=(c[r>>2]|0))break;g[A>>2]=+g[b+(c[p>>2]<<1<<2)>>2]*+g[b+(c[p>>2]<<1<<2)>>2]+ +g[b+((c[p>>2]<<1)+1<<2)>>2]*+g[b+((c[p>>2]<<1)+1<<2)>>2];g[u>>2]=+g[u>>2]+ +g[A>>2];g[b+(c[p>>2]<<2)>>2]=+g[v>>2]+(+g[A>>2]-+g[v>>2])*.0625;g[v>>2]=+g[b+(c[p>>2]<<2)>>2];c[p>>2]=(c[p>>2]|0)+1}g[v>>2]=0.0;g[t>>2]=0.0;c[p>>2]=(c[r>>2]|0)-1;while(1){if((c[p>>2]|0)<0)break;g[b+(c[p>>2]<<2)>>2]=+g[v>>2]+(+g[b+(c[p>>2]<<2)>>2]-+g[v>>2])*.125;g[v>>2]=+g[b+(c[p>>2]<<2)>>2];g[t>>2]=+g[t>>2]>+g[v>>2]?+g[t>>2]:+g[v>>2];c[p>>2]=(c[p>>2]|0)+-1}g[u>>2]=+O(+(+g[u>>2]*+g[t>>2]*.5*+(c[r>>2]|0)));g[x>>2]=+(c[r>>2]|0)/(+g[u>>2]+1.0000000036274937e-15);c[y>>2]=0;c[p>>2]=12;while(1){if((c[p>>2]|0)>=((c[r>>2]|0)-5|0))break;if(127.0<+M(+(+g[x>>2]*64.0*(+g[b+(c[p>>2]<<2)>>2]+1.0000000036274937e-15))))j=127.0;else j=+M(+(+g[x>>2]*64.0*(+g[b+(c[p>>2]<<2)>>2]+1.0000000036274937e-15)));if(!(0.0>j))if(127.0<+M(+(+g[x>>2]*64.0*(+g[b+(c[p>>2]<<2)>>2]+1.0000000036274937e-15))))j=127.0;else j=+M(+(+g[x>>2]*64.0*(+g[b+(c[p>>2]<<2)>>2]+1.0000000036274937e-15)));else j=0.0;c[q>>2]=~~j;c[y>>2]=(c[y>>2]|0)+(d[25432+(c[q>>2]|0)>>0]|0);c[p>>2]=(c[p>>2]|0)+4}c[y>>2]=(c[y>>2]<<6<<2|0)/(((c[r>>2]|0)-17|0)*6|0)|0;if((c[y>>2]|0)>(c[s>>2]|0)){c[c[n>>2]>>2]=c[o>>2];c[s>>2]=c[y>>2]}c[o>>2]=(c[o>>2]|0)+1}c[E>>2]=(c[s>>2]|0)>200&1;if(0.0>+O(+(+((c[s>>2]|0)*27|0)))-42.0)j=0.0;else j=+O(+(+((c[s>>2]|0)*27|0)))-42.0;g[F>>2]=j;if(0.0>(163.0<+g[F>>2]?163.0:+g[F>>2])*.006899999920278788-.139){j=0.0;j=+O(+j);F=c[C>>2]|0;g[F>>2]=j;F=c[E>>2]|0;E=c[D>>2]|0;na(E|0);i=G;return F|0}j=(163.0<+g[F>>2]?163.0:+g[F>>2])*.006899999920278788-.139;j=+O(+j);F=c[C>>2]|0;g[F>>2]=j;F=c[E>>2]|0;E=c[D>>2]|0;na(E|0);i=G;return F|0}function sb(a,b,d,e,f,h,j,k,l){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;var m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0;D=i;i=i+80|0;m=D+64|0;n=D+60|0;o=D+56|0;p=D+52|0;q=D+48|0;r=D+44|0;s=D+40|0;t=D+36|0;u=D+32|0;B=D+28|0;w=D+24|0;v=D+20|0;C=D+16|0;A=D+12|0;x=D+8|0;z=D+4|0;y=D;c[m>>2]=a;c[n>>2]=b;c[o>>2]=d;c[p>>2]=e;c[q>>2]=f;c[r>>2]=h;c[s>>2]=j;c[t>>2]=k;c[u>>2]=l;c[B>>2]=c[(c[m>>2]|0)+4>>2];if(c[n>>2]|0){c[v>>2]=c[n>>2];c[w>>2]=c[(c[m>>2]|0)+44>>2];c[C>>2]=c[(c[m>>2]|0)+36>>2]}else{c[v>>2]=1;c[w>>2]=c[(c[m>>2]|0)+44>>2]<<c[s>>2];c[C>>2]=(c[(c[m>>2]|0)+36>>2]|0)-(c[s>>2]|0)}c[z>>2]=0;do{c[x>>2]=0;while(1){if((c[x>>2]|0)>=(c[v>>2]|0))break;b=_(c[v>>2]|0,c[w>>2]|0)|0;b=(c[o>>2]|0)+((_(c[z>>2]|0,b+(c[B>>2]|0)|0)|0)<<2)|0;b=b+((_(c[x>>2]|0,c[w>>2]|0)|0)<<2)|0;k=_(c[z>>2]|0,c[w>>2]|0)|0;k=(c[x>>2]|0)+(_(k,c[v>>2]|0)|0)|0;Hc((c[m>>2]|0)+64|0,b,(c[p>>2]|0)+(k<<2)|0,c[(c[m>>2]|0)+60>>2]|0,c[B>>2]|0,c[C>>2]|0,c[v>>2]|0,c[u>>2]|0);c[x>>2]=(c[x>>2]|0)+1}k=(c[z>>2]|0)+1|0;c[z>>2]=k}while((k|0)<(c[r>>2]|0));a:do if((c[r>>2]|0)==2&(c[q>>2]|0)==1){c[A>>2]=0;while(1){if((c[A>>2]|0)>=(_(c[v>>2]|0,c[w>>2]|0)|0))break a;k=_(c[v>>2]|0,c[w>>2]|0)|0;g[(c[p>>2]|0)+(c[A>>2]<<2)>>2]=+g[(c[p>>2]|0)+(c[A>>2]<<2)>>2]*.5+ +g[(c[p>>2]|0)+(k+(c[A>>2]|0)<<2)>>2]*.5;c[A>>2]=(c[A>>2]|0)+1}}while(0);if((c[t>>2]|0)==1){i=D;return}c[z>>2]=0;do{k=_(c[v>>2]|0,c[w>>2]|0)|0;c[y>>2]=(k|0)/(c[t>>2]|0)|0;c[A>>2]=0;while(1){if((c[A>>2]|0)>=(c[y>>2]|0))break;k=_(c[z>>2]|0,c[v>>2]|0)|0;k=_(k,c[w>>2]|0)|0;k=(c[p>>2]|0)+(k+(c[A>>2]|0)<<2)|0;g[k>>2]=+g[k>>2]*+(c[t>>2]|0);c[A>>2]=(c[A>>2]|0)+1}b=_(c[z>>2]|0,c[v>>2]|0)|0;b=_(b,c[w>>2]|0)|0;k=_(c[v>>2]|0,c[w>>2]|0)|0;oj((c[p>>2]|0)+(b+(c[y>>2]|0)<<2)|0,0,k-(c[y>>2]|0)<<2|0)|0;k=(c[z>>2]|0)+1|0;c[z>>2]=k}while((k|0)<(c[q>>2]|0));i=D;return}function tb(a,b,d,e,f,h){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;var j=0.0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0;w=i;i=i+160|0;l=w+144|0;k=w+140|0;m=w+136|0;n=w+132|0;o=w+128|0;p=w+124|0;r=w+120|0;q=w+116|0;s=w+112|0;t=w+8|0;u=w+4|0;v=w;c[l>>2]=a;c[k>>2]=b;c[m>>2]=d;c[n>>2]=e;c[o>>2]=f;c[p>>2]=h;g[s>>2]=0.0;j=+g[(c[k>>2]|0)+(c[n>>2]<<2)>>2];h=c[n>>2]|0;a:do if((c[p>>2]|0)==1){g[t+(h<<2)>>2]=j;c[r>>2]=(c[n>>2]|0)+1;while(1){if((c[r>>2]|0)>=(c[o>>2]|0))break a;h=c[r>>2]|0;if(+g[t+((c[r>>2]|0)-1<<2)>>2]-1.0>+g[(c[k>>2]|0)+(c[r>>2]<<2)>>2])j=+g[t+(h-1<<2)>>2]-1.0;else j=+g[(c[k>>2]|0)+(h<<2)>>2];g[t+(c[r>>2]<<2)>>2]=j;c[r>>2]=(c[r>>2]|0)+1}}else{f=c[n>>2]|0;if(j>+g[(c[k>>2]|0)+(h+(c[m>>2]|0)<<2)>>2])j=+g[(c[k>>2]|0)+(f<<2)>>2];else j=+g[(c[k>>2]|0)+(f+(c[m>>2]|0)<<2)>>2];g[t+(c[n>>2]<<2)>>2]=j;c[r>>2]=(c[n>>2]|0)+1;while(1){if((c[r>>2]|0)>=(c[o>>2]|0))break a;h=c[r>>2]|0;if(+g[(c[k>>2]|0)+(c[r>>2]<<2)>>2]>+g[(c[k>>2]|0)+((c[r>>2]|0)+(c[m>>2]|0)<<2)>>2])j=+g[(c[k>>2]|0)+(h<<2)>>2];else j=+g[(c[k>>2]|0)+(h+(c[m>>2]|0)<<2)>>2];f=c[r>>2]|0;do if(!(+g[t+((c[r>>2]|0)-1<<2)>>2]-1.0>j)){h=c[r>>2]|0;if(+g[(c[k>>2]|0)+(f<<2)>>2]>+g[(c[k>>2]|0)+((c[r>>2]|0)+(c[m>>2]|0)<<2)>>2]){j=+g[(c[k>>2]|0)+(h<<2)>>2];break}else{j=+g[(c[k>>2]|0)+(h+(c[m>>2]|0)<<2)>>2];break}}else j=+g[t+(f-1<<2)>>2]-1.0;while(0);g[t+(c[r>>2]<<2)>>2]=j;c[r>>2]=(c[r>>2]|0)+1}}while(0);c[r>>2]=(c[o>>2]|0)-2;while(1){if((c[r>>2]|0)<(c[n>>2]|0))break;h=c[r>>2]|0;if(+g[t+(c[r>>2]<<2)>>2]>+g[t+((c[r>>2]|0)+1<<2)>>2]-1.0)j=+g[t+(h<<2)>>2];else j=+g[t+(h+1<<2)>>2]-1.0;g[t+(c[r>>2]<<2)>>2]=j;c[r>>2]=(c[r>>2]|0)+-1}c[q>>2]=0;do{c[r>>2]=2>(c[n>>2]|0)?2:c[n>>2]|0;while(1){if((c[r>>2]|0)>=((c[o>>2]|0)-1|0))break;k=(c[r>>2]|0)+(_(c[q>>2]|0,c[m>>2]|0)|0)|0;if(0.0>+g[(c[l>>2]|0)+(k<<2)>>2])j=0.0;else{k=(c[r>>2]|0)+(_(c[q>>2]|0,c[m>>2]|0)|0)|0;j=+g[(c[l>>2]|0)+(k<<2)>>2]}g[u>>2]=j;if(0.0>+g[t+(c[r>>2]<<2)>>2])j=0.0;else j=+g[t+(c[r>>2]<<2)>>2];g[v>>2]=j;if(0.0>+g[u>>2]-+g[v>>2])j=0.0;else j=+g[u>>2]-+g[v>>2];g[s>>2]=+g[s>>2]+j;c[r>>2]=(c[r>>2]|0)+1}k=(c[q>>2]|0)+1|0;c[q>>2]=k}while((k|0)<(c[p>>2]|0));g[s>>2]=+g[s>>2]/+(_(c[p>>2]|0,(c[o>>2]|0)-1-(2>(c[n>>2]|0)?2:c[n>>2]|0)|0)|0);i=w;return +g[s>>2]>1.0|0}function ub(d,e,f,h,j,k,l,m,n,o,p){d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=+o;p=p|0;var q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0;W=i;i=i+144|0;q=W+128|0;H=W+124|0;I=W+120|0;T=W+116|0;J=W+112|0;s=W+108|0;t=W+104|0;K=W+100|0;u=W+96|0;X=W+92|0;r=W+88|0;U=W+84|0;L=W+80|0;M=W+76|0;F=W+72|0;G=W+64|0;V=W+60|0;A=W+56|0;S=W+52|0;B=W+48|0;x=W+44|0;C=W+40|0;w=W+36|0;y=W+32|0;z=W+28|0;v=W+24|0;D=W+20|0;E=W+16|0;O=W+12|0;P=W+8|0;Q=W+4|0;R=W;c[q>>2]=d;c[H>>2]=e;c[I>>2]=f;c[T>>2]=h;c[J>>2]=j;c[s>>2]=k;c[t>>2]=l;c[K>>2]=m;c[u>>2]=n;g[X>>2]=o;c[r>>2]=p;c[V>>2]=0;g[A>>2]=(-.25>.5-+g[X>>2]?-.25:.5-+g[X>>2])*.03999999910593033;k=c[H>>2]|0;c[S>>2]=ia()|0;j=i;i=i+((1*(k<<2)|0)+15&-16)|0;k=i;i=i+((1*((b[(c[(c[q>>2]|0)+32>>2]|0)+(c[H>>2]<<1)>>1]|0)-(b[(c[(c[q>>2]|0)+32>>2]|0)+((c[H>>2]|0)-1<<1)>>1]|0)<<c[K>>2]<<2)|0)+15&-16)|0;f=i;i=i+((1*((b[(c[(c[q>>2]|0)+32>>2]|0)+(c[H>>2]<<1)>>1]|0)-(b[(c[(c[q>>2]|0)+32>>2]|0)+((c[H>>2]|0)-1<<1)>>1]|0)<<c[K>>2]<<2)|0)+15&-16)|0;e=i;i=i+((1*(c[H>>2]<<2)|0)+15&-16)|0;d=i;i=i+((1*(c[H>>2]<<2)|0)+15&-16)|0;c[c[u>>2]>>2]=0;c[U>>2]=0;while(1){if((c[U>>2]|0)>=(c[H>>2]|0))break;c[z>>2]=0;c[x>>2]=(b[(c[(c[q>>2]|0)+32>>2]|0)+((c[U>>2]|0)+1<<1)>>1]|0)-(b[(c[(c[q>>2]|0)+32>>2]|0)+(c[U>>2]<<1)>>1]|0)<<c[K>>2];c[C>>2]=((b[(c[(c[q>>2]|0)+32>>2]|0)+((c[U>>2]|0)+1<<1)>>1]|0)-(b[(c[(c[q>>2]|0)+32>>2]|0)+(c[U>>2]<<1)>>1]|0)|0)==1&1;X=_(c[r>>2]|0,c[t>>2]|0)|0;pj(k|0,(c[s>>2]|0)+(X+(b[(c[(c[q>>2]|0)+32>>2]|0)+(c[U>>2]<<1)>>1]<<c[K>>2])<<2)|0,(c[x>>2]<<2)+0|0)|0;g[w>>2]=+vb(k,c[x>>2]|0,c[I>>2]|0?c[K>>2]|0:0,+g[A>>2]);g[y>>2]=+g[w>>2];if(!((c[I>>2]|0)==0|(c[C>>2]|0)!=0)?(pj(f|0,k|0,(c[x>>2]<<2)+0|0)|0,Ja(f,c[x>>2]>>c[K>>2],1<<c[K>>2]),g[w>>2]=+vb(f,c[x>>2]|0,(c[K>>2]|0)+1|0,+g[A>>2]),+g[w>>2]<+g[y>>2]):0){g[y>>2]=+g[w>>2];c[z>>2]=-1}c[B>>2]=0;while(1){m=(c[I>>2]|0)!=0;if((c[B>>2]|0)>=((c[K>>2]|0)+(((c[I>>2]|0?1:(c[C>>2]|0)!=0)^1)&1)|0))break;if(m)c[v>>2]=(c[K>>2]|0)-(c[B>>2]|0)-1;else c[v>>2]=(c[B>>2]|0)+1;Ja(k,c[x>>2]>>c[B>>2],1<<c[B>>2]);g[w>>2]=+vb(k,c[x>>2]|0,c[v>>2]|0,+g[A>>2]);if(+g[w>>2]<+g[y>>2]){g[y>>2]=+g[w>>2];c[z>>2]=(c[B>>2]|0)+1}c[B>>2]=(c[B>>2]|0)+1}l=c[z>>2]|0;if(m)c[j+(c[U>>2]<<2)>>2]=l<<1;else{X=_(-2,l)|0;c[j+(c[U>>2]<<2)>>2]=X}X=c[u>>2]|0;c[X>>2]=(c[X>>2]|0)+((c[I>>2]|0?c[K>>2]|0:0)-((c[j+(c[U>>2]<<2)>>2]|0)/2|0));do if(c[C>>2]|0){if(c[j+(c[U>>2]<<2)>>2]|0?(c[j+(c[U>>2]<<2)>>2]|0)!=(_(-2,c[K>>2]|0)|0):0)break;X=j+(c[U>>2]<<2)|0;c[X>>2]=(c[X>>2]|0)-1}while(0);c[U>>2]=(c[U>>2]|0)+1}c[V>>2]=0;c[F>>2]=0;while(1){if((c[F>>2]|0)>=2)break;c[L>>2]=0;c[M>>2]=c[I>>2]|0?0:c[J>>2]|0;c[U>>2]=1;while(1){m=c[L>>2]|0;l=c[M>>2]|0;if((c[U>>2]|0)>=(c[H>>2]|0))break;if((m|0)<(l+(c[J>>2]|0)|0))m=c[L>>2]|0;else m=(c[M>>2]|0)+(c[J>>2]|0)|0;c[D>>2]=m;if(((c[L>>2]|0)+(c[J>>2]|0)|0)<(c[M>>2]|0))m=(c[L>>2]|0)+(c[J>>2]|0)|0;else m=c[M>>2]|0;c[E>>2]=m;c[L>>2]=(c[D>>2]|0)+(N((c[j+(c[U>>2]<<2)>>2]|0)-(a[25232+(c[K>>2]<<3)+((c[I>>2]<<2)+(c[F>>2]<<1)+0)>>0]<<1)|0)|0);c[M>>2]=(c[E>>2]|0)+(N((c[j+(c[U>>2]<<2)>>2]|0)-(a[25232+(c[K>>2]<<3)+((c[I>>2]<<2)+(c[F>>2]<<1)+1)>>0]<<1)|0)|0);c[U>>2]=(c[U>>2]|0)+1}c[L>>2]=(m|0)<(l|0)?c[L>>2]|0:c[M>>2]|0;c[G+(c[F>>2]<<2)>>2]=c[L>>2];c[F>>2]=(c[F>>2]|0)+1}if(c[I>>2]|0?(c[G+4>>2]|0)<(c[G>>2]|0):0)c[V>>2]=1;c[L>>2]=0;c[M>>2]=c[I>>2]|0?0:c[J>>2]|0;c[U>>2]=1;while(1){m=c[L>>2]|0;if((c[U>>2]|0)>=(c[H>>2]|0))break;c[Q>>2]=m;c[R>>2]=(c[M>>2]|0)+(c[J>>2]|0);if((c[Q>>2]|0)<(c[R>>2]|0)){c[O>>2]=c[Q>>2];c[e+(c[U>>2]<<2)>>2]=0}else{c[O>>2]=c[R>>2];c[e+(c[U>>2]<<2)>>2]=1}c[Q>>2]=(c[L>>2]|0)+(c[J>>2]|0);c[R>>2]=c[M>>2];if((c[Q>>2]|0)<(c[R>>2]|0)){c[P>>2]=c[Q>>2];c[d+(c[U>>2]<<2)>>2]=0}else{c[P>>2]=c[R>>2];c[d+(c[U>>2]<<2)>>2]=1}c[L>>2]=(c[O>>2]|0)+(N((c[j+(c[U>>2]<<2)>>2]|0)-(a[25232+(c[K>>2]<<3)+((c[I>>2]<<2)+(c[V>>2]<<1)+0)>>0]<<1)|0)|0);c[M>>2]=(c[P>>2]|0)+(N((c[j+(c[U>>2]<<2)>>2]|0)-(a[25232+(c[K>>2]<<3)+((c[I>>2]<<2)+(c[V>>2]<<1)+1)>>0]<<1)|0)|0);c[U>>2]=(c[U>>2]|0)+1}c[(c[T>>2]|0)+((c[H>>2]|0)-1<<2)>>2]=(m|0)<(c[M>>2]|0)?0:1;c[U>>2]=(c[H>>2]|0)-2;while(1){if((c[U>>2]|0)<0)break;m=(c[U>>2]|0)+1|0;if((c[(c[T>>2]|0)+((c[U>>2]|0)+1<<2)>>2]|0)==1)c[(c[T>>2]|0)+(c[U>>2]<<2)>>2]=c[d+(m<<2)>>2];else c[(c[T>>2]|0)+(c[U>>2]<<2)>>2]=c[e+(m<<2)>>2];c[U>>2]=(c[U>>2]|0)+-1}X=c[V>>2]|0;na(c[S>>2]|0);i=W;return X|0}function vb(a,b,d,e){a=a|0;b=b|0;d=d|0;e=+e;var f=0,h=0,j=0,k=0,l=0,m=0,n=0;n=i;i=i+32|0;f=n+20|0;h=n+16|0;j=n+12|0;k=n+8|0;m=n+4|0;l=n;c[f>>2]=a;c[h>>2]=b;c[j>>2]=d;g[k>>2]=e;g[l>>2]=0.0;c[m>>2]=0;while(1){if((c[m>>2]|0)>=(c[h>>2]|0))break;e=+N(+(+g[(c[f>>2]|0)+(c[m>>2]<<2)>>2]));g[l>>2]=+g[l>>2]+e;c[m>>2]=(c[m>>2]|0)+1}g[l>>2]=+g[l>>2]+ +(c[j>>2]|0)*+g[k>>2]*+g[l>>2];i=n;return +(+g[l>>2])}function wb(b,d,e,f,g,h,j){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0;y=i;i=i+64|0;k=y+52|0;l=y+48|0;m=y+44|0;n=y+40|0;o=y+36|0;p=y+32|0;q=y+28|0;s=y+24|0;t=y+20|0;x=y+16|0;w=y+12|0;u=y+8|0;r=y+4|0;v=y;c[k>>2]=b;c[l>>2]=d;c[m>>2]=e;c[n>>2]=f;c[o>>2]=g;c[p>>2]=h;c[q>>2]=j;c[r>>2]=c[(c[q>>2]|0)+4>>2]<<3;c[v>>2]=ob(c[q>>2]|0)|0;c[u>>2]=c[m>>2]|0?2:4;if((c[o>>2]|0)>0)g=((c[v>>2]|0)+(c[u>>2]|0)+1|0)>>>0<=(c[r>>2]|0)>>>0;else g=0;c[x>>2]=g&1;c[r>>2]=(c[r>>2]|0)-(c[x>>2]|0);c[w>>2]=0;c[s>>2]=0;c[t>>2]=c[k>>2];while(1){if((c[t>>2]|0)>=(c[l>>2]|0))break;if(((c[v>>2]|0)+(c[u>>2]|0)|0)>>>0<=(c[r>>2]|0)>>>0){pc(c[q>>2]|0,c[(c[n>>2]|0)+(c[t>>2]<<2)>>2]^c[s>>2],c[u>>2]|0);c[v>>2]=ob(c[q>>2]|0)|0;c[s>>2]=c[(c[n>>2]|0)+(c[t>>2]<<2)>>2];c[w>>2]=c[w>>2]|c[s>>2]}else c[(c[n>>2]|0)+(c[t>>2]<<2)>>2]=c[s>>2];c[u>>2]=c[m>>2]|0?4:5;c[t>>2]=(c[t>>2]|0)+1}if(c[x>>2]|0?(a[25232+(c[o>>2]<<3)+((c[m>>2]<<2)+0+(c[w>>2]|0))>>0]|0)!=(a[25232+(c[o>>2]<<3)+((c[m>>2]<<2)+2+(c[w>>2]|0))>>0]|0):0)pc(c[q>>2]|0,c[p>>2]|0,1);else c[p>>2]=0;c[t>>2]=c[k>>2];while(1){if((c[t>>2]|0)>=(c[l>>2]|0))break;c[(c[n>>2]|0)+(c[t>>2]<<2)>>2]=a[25232+(c[o>>2]<<3)+((c[m>>2]<<2)+(c[p>>2]<<1)+(c[(c[n>>2]|0)+(c[t>>2]<<2)>>2]|0))>>0];c[t>>2]=(c[t>>2]|0)+1}i=y;return}function xb(a,d,e,f,h,j,k,l,m,n,o,p,q,r,s,t,u,v){a=a|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;p=p|0;q=q|0;r=r|0;s=s|0;t=t|0;u=u|0;v=v|0;var w=0.0,x=0.0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,$=0,aa=0,ba=0,ca=0,da=0;da=i;i=i+128|0;I=da+120|0;B=da+116|0;H=da+112|0;K=da+108|0;S=da+104|0;T=da+100|0;U=da+96|0;y=da+92|0;z=da+88|0;V=da+84|0;L=da+80|0;M=da+76|0;N=da+72|0;O=da+68|0;P=da+64|0;Q=da+60|0;A=da+56|0;J=da+52|0;Z=da+48|0;C=da+44|0;aa=da+40|0;$=da+36|0;R=da+32|0;E=da+28|0;F=da+24|0;G=da+20|0;D=da+16|0;ba=da+12|0;W=da+8|0;X=da+4|0;Y=da;c[I>>2]=a;c[B>>2]=d;c[H>>2]=e;c[K>>2]=f;c[S>>2]=h;c[T>>2]=j;c[U>>2]=k;c[y>>2]=l;c[z>>2]=m;c[V>>2]=n;c[L>>2]=o;c[M>>2]=p;c[N>>2]=q;c[O>>2]=r;c[P>>2]=s;c[Q>>2]=t;c[A>>2]=u;c[J>>2]=v;c[aa>>2]=0;o=_(c[T>>2]|0,c[H>>2]|0)|0;c[R>>2]=ia()|0;a=i;i=i+((1*(o<<2)|0)+15&-16)|0;o=(_(c[T>>2]|0,c[H>>2]|0)|0)<<2;r=i;i=i+((1*o|0)+15&-16)|0;oj(c[U>>2]|0,0,c[H>>2]<<2|0)|0;g[$>>2]=-31.899999618530273;c[Z>>2]=0;while(1){if((c[Z>>2]|0)>=(c[S>>2]|0))break;x=+(b[(c[z>>2]|0)+(c[Z>>2]<<1)>>1]|0)*.0625+.5+ +(9-(c[y>>2]|0)|0)-+g[17464+(c[Z>>2]<<2)>>2]+ +(_((c[Z>>2]|0)+5|0,(c[Z>>2]|0)+5|0)|0)*.006200000178068876;g[r+(c[Z>>2]<<2)>>2]=x;c[Z>>2]=(c[Z>>2]|0)+1}c[C>>2]=0;do{c[Z>>2]=0;while(1){if((c[Z>>2]|0)>=(c[S>>2]|0))break;z=_(c[C>>2]|0,c[H>>2]|0)|0;if(+g[$>>2]>+g[(c[I>>2]|0)+(z+(c[Z>>2]|0)<<2)>>2]-+g[r+(c[Z>>2]<<2)>>2])w=+g[$>>2];else{z=_(c[C>>2]|0,c[H>>2]|0)|0;w=+g[(c[I>>2]|0)+(z+(c[Z>>2]|0)<<2)>>2]-+g[r+(c[Z>>2]<<2)>>2]}g[$>>2]=w;c[Z>>2]=(c[Z>>2]|0)+1}z=(c[C>>2]|0)+1|0;c[C>>2]=z}while((z|0)<(c[T>>2]|0));if((c[P>>2]|0)>50&(c[O>>2]|0)>=1^1|(c[A>>2]|0)!=0){ba=c[aa>>2]|0;ca=c[Q>>2]|0;c[ca>>2]=ba;x=+g[$>>2];ca=c[R>>2]|0;na(ca|0);i=da;return +x}c[E>>2]=0;c[C>>2]=0;do{c[D>>2]=a+((_(c[C>>2]|0,c[H>>2]|0)|0)<<2);A=_(c[C>>2]|0,c[H>>2]|0)|0;g[c[D>>2]>>2]=+g[(c[B>>2]|0)+(A<<2)>>2];c[Z>>2]=1;while(1){if((c[Z>>2]|0)>=(c[S>>2]|0))break;z=_(c[C>>2]|0,c[H>>2]|0)|0;A=_(c[C>>2]|0,c[H>>2]|0)|0;if(+g[(c[B>>2]|0)+(z+(c[Z>>2]|0)<<2)>>2]>+g[(c[B>>2]|0)+(A+(c[Z>>2]|0)-1<<2)>>2]+.5)c[E>>2]=c[Z>>2];A=_(c[C>>2]|0,c[H>>2]|0)|0;if(+g[(c[D>>2]|0)+((c[Z>>2]|0)-1<<2)>>2]+1.5<+g[(c[B>>2]|0)+(A+(c[Z>>2]|0)<<2)>>2])w=+g[(c[D>>2]|0)+((c[Z>>2]|0)-1<<2)>>2]+1.5;else{A=_(c[C>>2]|0,c[H>>2]|0)|0;w=+g[(c[B>>2]|0)+(A+(c[Z>>2]|0)<<2)>>2]}g[(c[D>>2]|0)+(c[Z>>2]<<2)>>2]=w;c[Z>>2]=(c[Z>>2]|0)+1}c[Z>>2]=(c[E>>2]|0)-1;while(1){if((c[Z>>2]|0)<0)break;A=_(c[C>>2]|0,c[H>>2]|0)|0;if(+g[(c[D>>2]|0)+((c[Z>>2]|0)+1<<2)>>2]+2.0<+g[(c[B>>2]|0)+(A+(c[Z>>2]|0)<<2)>>2])w=+g[(c[D>>2]|0)+((c[Z>>2]|0)+1<<2)>>2]+2.0;else{A=_(c[C>>2]|0,c[H>>2]|0)|0;w=+g[(c[B>>2]|0)+(A+(c[Z>>2]|0)<<2)>>2]}j=c[Z>>2]|0;do if(!(+g[(c[D>>2]|0)+(c[Z>>2]<<2)>>2]<w)){A=_(c[C>>2]|0,c[H>>2]|0)|0;if(+g[(c[D>>2]|0)+(j+1<<2)>>2]+2.0<+g[(c[B>>2]|0)+(A+(c[Z>>2]|0)<<2)>>2]){w=+g[(c[D>>2]|0)+((c[Z>>2]|0)+1<<2)>>2]+2.0;break}else{A=_(c[C>>2]|0,c[H>>2]|0)|0;w=+g[(c[B>>2]|0)+(A+(c[Z>>2]|0)<<2)>>2];break}}else w=+g[(c[D>>2]|0)+(j<<2)>>2];while(0);g[(c[D>>2]|0)+(c[Z>>2]<<2)>>2]=w;c[Z>>2]=(c[Z>>2]|0)+-1}g[F>>2]=1.0;c[Z>>2]=2;while(1){if((c[Z>>2]|0)>=((c[S>>2]|0)-2|0))break;w=+g[(c[D>>2]|0)+(c[Z>>2]<<2)>>2];A=_(c[C>>2]|0,c[H>>2]|0)|0;x=+yb((c[B>>2]|0)+(A+(c[Z>>2]|0)-2<<2)|0);if(w>x-+g[F>>2])w=+g[(c[D>>2]|0)+(c[Z>>2]<<2)>>2];else{A=_(c[C>>2]|0,c[H>>2]|0)|0;w=+yb((c[B>>2]|0)+(A+(c[Z>>2]|0)-2<<2)|0);w=w-+g[F>>2]}g[(c[D>>2]|0)+(c[Z>>2]<<2)>>2]=w;c[Z>>2]=(c[Z>>2]|0)+1}A=_(c[C>>2]|0,c[H>>2]|0)|0;x=+zb((c[B>>2]|0)+(A<<2)|0);g[G>>2]=x-+g[F>>2];if(+g[c[D>>2]>>2]>+g[G>>2])w=+g[c[D>>2]>>2];else w=+g[G>>2];g[c[D>>2]>>2]=w;if(+g[(c[D>>2]|0)+4>>2]>+g[G>>2])w=+g[(c[D>>2]|0)+4>>2];else w=+g[G>>2];g[(c[D>>2]|0)+4>>2]=w;A=_(c[C>>2]|0,c[H>>2]|0)|0;x=+zb((c[B>>2]|0)+(A+(c[S>>2]|0)-3<<2)|0);g[G>>2]=x-+g[F>>2];if(+g[(c[D>>2]|0)+((c[S>>2]|0)-2<<2)>>2]>+g[G>>2])w=+g[(c[D>>2]|0)+((c[S>>2]|0)-2<<2)>>2];else w=+g[G>>2];g[(c[D>>2]|0)+((c[S>>2]|0)-2<<2)>>2]=w;if(+g[(c[D>>2]|0)+((c[S>>2]|0)-1<<2)>>2]>+g[G>>2])w=+g[(c[D>>2]|0)+((c[S>>2]|0)-1<<2)>>2];else w=+g[G>>2];g[(c[D>>2]|0)+((c[S>>2]|0)-1<<2)>>2]=w;c[Z>>2]=0;while(1){if((c[Z>>2]|0)>=(c[S>>2]|0))break;j=c[Z>>2]|0;if(+g[(c[D>>2]|0)+(c[Z>>2]<<2)>>2]>+g[r+(c[Z>>2]<<2)>>2])w=+g[(c[D>>2]|0)+(j<<2)>>2];else w=+g[r+(j<<2)>>2];g[(c[D>>2]|0)+(c[Z>>2]<<2)>>2]=w;c[Z>>2]=(c[Z>>2]|0)+1}A=(c[C>>2]|0)+1|0;c[C>>2]=A}while((A|0)<(c[T>>2]|0));G=(c[T>>2]|0)==2;c[Z>>2]=c[K>>2];a:do if(G)while(1){if((c[Z>>2]|0)>=(c[S>>2]|0))break a;if(+g[a+((c[H>>2]|0)+(c[Z>>2]|0)<<2)>>2]>+g[a+(c[Z>>2]<<2)>>2]-4.0)w=+g[a+((c[H>>2]|0)+(c[Z>>2]|0)<<2)>>2];else w=+g[a+(c[Z>>2]<<2)>>2]-4.0;g[a+((c[H>>2]|0)+(c[Z>>2]|0)<<2)>>2]=w;if(+g[a+(c[Z>>2]<<2)>>2]>+g[a+((c[H>>2]|0)+(c[Z>>2]|0)<<2)>>2]-4.0)w=+g[a+(c[Z>>2]<<2)>>2];else w=+g[a+((c[H>>2]|0)+(c[Z>>2]|0)<<2)>>2]-4.0;g[a+(c[Z>>2]<<2)>>2]=w;if(0.0>+g[(c[I>>2]|0)+(c[Z>>2]<<2)>>2]-+g[a+(c[Z>>2]<<2)>>2])x=0.0;else x=+g[(c[I>>2]|0)+(c[Z>>2]<<2)>>2]-+g[a+(c[Z>>2]<<2)>>2];if(0.0>+g[(c[I>>2]|0)+((c[H>>2]|0)+(c[Z>>2]|0)<<2)>>2]-+g[a+((c[H>>2]|0)+(c[Z>>2]|0)<<2)>>2])w=0.0;else w=+g[(c[I>>2]|0)+((c[H>>2]|0)+(c[Z>>2]|0)<<2)>>2]-+g[a+((c[H>>2]|0)+(c[Z>>2]|0)<<2)>>2];g[a+(c[Z>>2]<<2)>>2]=(x+w)*.5;c[Z>>2]=(c[Z>>2]|0)+1}else while(1){if((c[Z>>2]|0)>=(c[S>>2]|0))break a;if(0.0>+g[(c[I>>2]|0)+(c[Z>>2]<<2)>>2]-+g[a+(c[Z>>2]<<2)>>2])w=0.0;else w=+g[(c[I>>2]|0)+(c[Z>>2]<<2)>>2]-+g[a+(c[Z>>2]<<2)>>2];g[a+(c[Z>>2]<<2)>>2]=w;c[Z>>2]=(c[Z>>2]|0)+1}while(0);c[Z>>2]=c[K>>2];while(1){if((c[Z>>2]|0)>=(c[S>>2]|0))break;j=c[Z>>2]|0;if(+g[a+(c[Z>>2]<<2)>>2]>+g[(c[J>>2]|0)+(c[Z>>2]<<2)>>2])w=+g[a+(j<<2)>>2];else w=+g[(c[J>>2]|0)+(j<<2)>>2];g[a+(c[Z>>2]<<2)>>2]=w;c[Z>>2]=(c[Z>>2]|0)+1}b:do if(!(((c[L>>2]|0)==0|(c[M>>2]|0)!=0)^1|(c[V>>2]|0)!=0)){c[Z>>2]=c[K>>2];while(1){if((c[Z>>2]|0)>=(c[S>>2]|0))break b;g[a+(c[Z>>2]<<2)>>2]=+g[a+(c[Z>>2]<<2)>>2]*.5;c[Z>>2]=(c[Z>>2]|0)+1}}while(0);c[Z>>2]=c[K>>2];while(1){if((c[Z>>2]|0)>=(c[S>>2]|0)){ca=103;break}if((c[Z>>2]|0)<8){K=a+(c[Z>>2]<<2)|0;g[K>>2]=+g[K>>2]*2.0}if((c[Z>>2]|0)>=12)g[a+(c[Z>>2]<<2)>>2]=+g[a+(c[Z>>2]<<2)>>2]*.5;if(+g[a+(c[Z>>2]<<2)>>2]<4.0)w=+g[a+(c[Z>>2]<<2)>>2];else w=4.0;g[a+(c[Z>>2]<<2)>>2]=w;K=_(c[T>>2]|0,(b[(c[N>>2]|0)+((c[Z>>2]|0)+1<<1)>>1]|0)-(b[(c[N>>2]|0)+(c[Z>>2]<<1)>>1]|0)|0)|0;c[ba>>2]=K<<c[O>>2];do if((c[ba>>2]|0)>=6){w=+g[a+(c[Z>>2]<<2)>>2];if((c[ba>>2]|0)>48){c[W>>2]=~~(w*8.0);c[X>>2]=((_(c[W>>2]|0,c[ba>>2]|0)|0)<<3|0)/8|0;break}else{c[W>>2]=~~(w*+(c[ba>>2]|0)/6.0);c[X>>2]=(c[W>>2]|0)*6<<3;break}}else{c[W>>2]=~~+g[a+(c[Z>>2]<<2)>>2];c[X>>2]=(_(c[W>>2]|0,c[ba>>2]|0)|0)<<3}while(0);if(!(c[L>>2]|0?(c[M>>2]|0)==0|(c[V>>2]|0)!=0:0))ca=100;if((ca|0)==100?(ca=0,((c[aa>>2]|0)+(c[X>>2]|0)>>3>>3|0)>((c[P>>2]|0)/4|0|0)):0)break;c[(c[U>>2]|0)+(c[Z>>2]<<2)>>2]=c[W>>2];c[aa>>2]=(c[aa>>2]|0)+(c[X>>2]|0);c[Z>>2]=(c[Z>>2]|0)+1}if((ca|0)==103){ba=c[aa>>2]|0;ca=c[Q>>2]|0;c[ca>>2]=ba;x=+g[$>>2];ca=c[R>>2]|0;na(ca|0);i=da;return +x}c[Y>>2]=((c[P>>2]|0)/4|0)<<3<<3;c[(c[U>>2]|0)+(c[Z>>2]<<2)>>2]=(c[Y>>2]|0)-(c[aa>>2]|0);c[aa>>2]=c[Y>>2];ba=c[aa>>2]|0;ca=c[Q>>2]|0;c[ca>>2]=ba;x=+g[$>>2];ca=c[R>>2]|0;na(ca|0);i=da;return +x}function yb(a){a=a|0;var b=0,d=0,e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0.0;n=i;i=i+48|0;b=n+32|0;d=n+28|0;e=n+24|0;f=n+20|0;h=n+16|0;j=n+12|0;k=n+8|0;l=n+4|0;m=n;c[d>>2]=a;g[h>>2]=+g[(c[d>>2]|0)+8>>2];a=c[d>>2]|0;if(+g[c[d>>2]>>2]>+g[(c[d>>2]|0)+4>>2]){g[e>>2]=+g[a+4>>2];g[f>>2]=+g[c[d>>2]>>2]}else{g[e>>2]=+g[a>>2];g[f>>2]=+g[(c[d>>2]|0)+4>>2]}a=c[d>>2]|0;if(+g[(c[d>>2]|0)+12>>2]>+g[(c[d>>2]|0)+16>>2]){g[j>>2]=+g[a+16>>2];g[k>>2]=+g[(c[d>>2]|0)+12>>2]}else{g[j>>2]=+g[a+12>>2];g[k>>2]=+g[(c[d>>2]|0)+16>>2]}if(+g[e>>2]>+g[j>>2]){g[l>>2]=+g[e>>2];g[e>>2]=+g[j>>2];g[j>>2]=+g[l>>2];g[m>>2]=+g[f>>2];g[f>>2]=+g[k>>2];g[k>>2]=+g[m>>2]}if(+g[h>>2]>+g[f>>2])if(+g[f>>2]<+g[j>>2]){g[b>>2]=+g[h>>2]<+g[j>>2]?+g[h>>2]:+g[j>>2];o=+g[b>>2];i=n;return +o}else{g[b>>2]=+g[k>>2]<+g[f>>2]?+g[k>>2]:+g[f>>2];o=+g[b>>2];i=n;return +o}else if(+g[h>>2]<+g[j>>2]){g[b>>2]=+g[f>>2]<+g[j>>2]?+g[f>>2]:+g[j>>2];o=+g[b>>2];i=n;return +o}else{g[b>>2]=+g[h>>2]<+g[k>>2]?+g[h>>2]:+g[k>>2];o=+g[b>>2];i=n;return +o}return 0.0}function zb(a){a=a|0;var b=0,d=0,e=0,f=0,h=0,j=0,k=0.0;j=i;i=i+32|0;b=j+16|0;d=j+12|0;e=j+8|0;f=j+4|0;h=j;c[d>>2]=a;a=c[d>>2]|0;if(+g[c[d>>2]>>2]>+g[(c[d>>2]|0)+4>>2]){g[e>>2]=+g[a+4>>2];g[f>>2]=+g[c[d>>2]>>2]}else{g[e>>2]=+g[a>>2];g[f>>2]=+g[(c[d>>2]|0)+4>>2]}g[h>>2]=+g[(c[d>>2]|0)+8>>2];if(+g[f>>2]<+g[h>>2]){g[b>>2]=+g[f>>2];k=+g[b>>2];i=j;return +k}if(+g[e>>2]<+g[h>>2]){g[b>>2]=+g[h>>2];k=+g[b>>2];i=j;return +k}else{g[b>>2]=+g[e>>2];k=+g[b>>2];i=j;return +k}return 0.0}function Ab(a,d,e,f){a=a|0;d=d|0;e=e|0;f=f|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0.0;v=i;i=i+64|0;h=v+48|0;j=v+44|0;k=v+40|0;l=v+36|0;q=v+32|0;u=v+28|0;s=v+24|0;t=v+20|0;r=v+16|0;m=v+12|0;o=v+8|0;n=v+4|0;p=v;c[h>>2]=a;c[j>>2]=d;c[k>>2]=e;c[l>>2]=f;g[s>>2]=1.0000000036274937e-15;g[t>>2]=1.0000000036274937e-15;c[q>>2]=0;while(1){if((c[q>>2]|0)>=13)break;c[r>>2]=b[(c[(c[h>>2]|0)+32>>2]|0)+(c[q>>2]<<1)>>1]<<c[k>>2];while(1){if((c[r>>2]|0)>=(b[(c[(c[h>>2]|0)+32>>2]|0)+((c[q>>2]|0)+1<<1)>>1]<<c[k>>2]|0))break;g[m>>2]=+g[(c[j>>2]|0)+(c[r>>2]<<2)>>2];g[o>>2]=+g[(c[j>>2]|0)+((c[l>>2]|0)+(c[r>>2]|0)<<2)>>2];g[n>>2]=+g[m>>2]+ +g[o>>2];g[p>>2]=+g[m>>2]-+g[o>>2];w=+N(+(+g[m>>2]));g[s>>2]=+g[s>>2]+(w+ +N(+(+g[o>>2])));w=+N(+(+g[n>>2]));g[t>>2]=+g[t>>2]+(w+ +N(+(+g[p>>2])));c[r>>2]=(c[r>>2]|0)+1}c[q>>2]=(c[q>>2]|0)+1}g[t>>2]=+g[t>>2]*.7071070075035095;c[u>>2]=13;if((c[k>>2]|0)<=1)c[u>>2]=(c[u>>2]|0)-8;i=v;return +((b[(c[(c[h>>2]|0)+32>>2]|0)+26>>1]<<(c[k>>2]|0)+1)+(c[u>>2]|0)|0)*+g[t>>2]>+(b[(c[(c[h>>2]|0)+32>>2]|0)+26>>1]<<(c[k>>2]|0)+1|0)*+g[s>>2]|0}function Bb(a,d,e,f,h,j,k,l,m,n,o,p,q){a=a|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=+n;o=o|0;p=+p;q=q|0;var r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,O=0,P=0,Q=0;Q=i;i=i+96|0;B=Q+92|0;s=Q+88|0;C=Q+84|0;D=Q+80|0;u=Q+76|0;E=Q+72|0;v=Q+68|0;L=Q+64|0;F=Q+60|0;K=Q+56|0;t=Q+52|0;J=Q+48|0;H=Q+40|0;I=Q+36|0;G=Q+32|0;P=Q+28|0;O=Q+24|0;z=Q+20|0;A=Q+16|0;y=Q+12|0;x=Q+8|0;r=Q+4|0;w=Q;c[B>>2]=a;c[s>>2]=d;c[C>>2]=e;c[D>>2]=f;c[u>>2]=h;c[E>>2]=j;c[v>>2]=k;c[L>>2]=l;c[F>>2]=m;g[K>>2]=n;c[t>>2]=o;g[J>>2]=p;c[Q+44>>2]=q;g[I>>2]=0.0;g[O>>2]=5.0;if((c[E>>2]|0)==2){g[y>>2]=0.0;c[H>>2]=0;while(1){if((c[H>>2]|0)>=8)break;g[r>>2]=+Cb((c[s>>2]|0)+(b[(c[(c[B>>2]|0)+32>>2]|0)+(c[H>>2]<<1)>>1]<<c[u>>2]<<2)|0,(c[s>>2]|0)+((c[v>>2]|0)+(b[(c[(c[B>>2]|0)+32>>2]|0)+(c[H>>2]<<1)>>1]<<c[u>>2])<<2)|0,(b[(c[(c[B>>2]|0)+32>>2]|0)+((c[H>>2]|0)+1<<1)>>1]|0)-(b[(c[(c[B>>2]|0)+32>>2]|0)+(c[H>>2]<<1)>>1]|0)<<c[u>>2]);g[y>>2]=+g[y>>2]+ +g[r>>2];c[H>>2]=(c[H>>2]|0)+1}g[y>>2]=+g[y>>2]*.125;if(1.0<+N(+(+g[y>>2])))p=1.0;else p=+N(+(+g[y>>2]));g[y>>2]=p;g[x>>2]=+g[y>>2];c[H>>2]=8;while(1){if((c[H>>2]|0)>=(c[t>>2]|0))break;g[w>>2]=+Cb((c[s>>2]|0)+(b[(c[(c[B>>2]|0)+32>>2]|0)+(c[H>>2]<<1)>>1]<<c[u>>2]<<2)|0,(c[s>>2]|0)+((c[v>>2]|0)+(b[(c[(c[B>>2]|0)+32>>2]|0)+(c[H>>2]<<1)>>1]<<c[u>>2])<<2)|0,(b[(c[(c[B>>2]|0)+32>>2]|0)+((c[H>>2]|0)+1<<1)>>1]|0)-(b[(c[(c[B>>2]|0)+32>>2]|0)+(c[H>>2]<<1)>>1]|0)<<c[u>>2]);if(+g[x>>2]<+N(+(+g[w>>2])))p=+g[x>>2];else p=+N(+(+g[w>>2]));g[x>>2]=p;c[H>>2]=(c[H>>2]|0)+1}if(1.0<+N(+(+g[x>>2])))p=1.0;else p=+N(+(+g[x>>2]));g[x>>2]=p;g[z>>2]=+Y(+(1.0010000467300415-+g[y>>2]*+g[y>>2]))*1.4426950408889634;if(+g[z>>2]*.5>+Y(+(1.0010000467300415-+g[x>>2]*+g[x>>2]))*1.4426950408889634)p=+g[z>>2]*.5;else p=+Y(+(1.0010000467300415-+g[x>>2]*+g[x>>2]))*1.4426950408889634;g[A>>2]=p;g[O>>2]=+g[O>>2]+(-4.0>+g[z>>2]*.75?-4.0:+g[z>>2]*.75);if(+g[c[F>>2]>>2]+.25<-(+g[A>>2]*.5))p=+g[c[F>>2]>>2]+.25;else p=-(+g[A>>2]*.5);g[c[F>>2]>>2]=p}c[G>>2]=0;do{c[H>>2]=0;while(1){if((c[H>>2]|0)>=((c[D>>2]|0)-1|0))break;F=(c[H>>2]|0)+(_(c[G>>2]|0,c[(c[B>>2]|0)+8>>2]|0)|0)|0;g[I>>2]=+g[I>>2]+ +g[(c[C>>2]|0)+(F<<2)>>2]*+(2+(c[H>>2]<<1)-(c[D>>2]|0)|0);c[H>>2]=(c[H>>2]|0)+1}F=(c[G>>2]|0)+1|0;c[G>>2]=F}while((F|0)<(c[E>>2]|0));n=+(_(c[E>>2]|0,(c[D>>2]|0)-1|0)|0);g[I>>2]=+g[I>>2]/n;if(2.0<(+g[I>>2]+1.0)/6.0)p=2.0;else p=(+g[I>>2]+1.0)/6.0;if(!(-2.0>p))if(2.0<(+g[I>>2]+1.0)/6.0)p=2.0;else p=(+g[I>>2]+1.0)/6.0;else p=-2.0;g[O>>2]=+g[O>>2]-p;g[O>>2]=+g[O>>2]-+g[J>>2];g[O>>2]=+g[O>>2]-+g[K>>2]*2.0;if(c[c[L>>2]>>2]|0){if(2.0<(+g[(c[L>>2]|0)+8>>2]+.05000000074505806)*2.0)p=2.0;else p=(+g[(c[L>>2]|0)+8>>2]+.05000000074505806)*2.0;if(!(-2.0>p))if(2.0<(+g[(c[L>>2]|0)+8>>2]+.05000000074505806)*2.0)p=2.0;else p=(+g[(c[L>>2]|0)+8>>2]+.05000000074505806)*2.0;else p=-2.0;g[O>>2]=+g[O>>2]-p}c[P>>2]=~~+M(+(+g[O>>2]+.5));if(0>((10<(c[P>>2]|0)?10:c[P>>2]|0)|0)){O=0;c[P>>2]=O;P=c[P>>2]|0;i=Q;return P|0}O=10<(c[P>>2]|0)?10:c[P>>2]|0;c[P>>2]=O;P=c[P>>2]|0;i=Q;return P|0}function Cb(a,b,d){a=a|0;b=b|0;d=d|0;var e=0.0,f=0,h=0,j=0,k=0,l=0,m=0;m=i;i=i+32|0;f=m+16|0;h=m+12|0;j=m+8|0;k=m+4|0;l=m;c[f>>2]=a;c[h>>2]=b;c[j>>2]=d;g[l>>2]=0.0;c[k>>2]=0;while(1){e=+g[l>>2];if((c[k>>2]|0)>=(c[j>>2]|0))break;g[l>>2]=e+ +g[(c[f>>2]|0)+(c[k>>2]<<2)>>2]*+g[(c[h>>2]|0)+(c[k>>2]<<2)>>2];c[k>>2]=(c[k>>2]|0)+1}i=m;return +e}function Db(a,d,e,f,h,j,k,l,m,n,o,p,q,r,s,t,u,v,w){a=a|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=+n;o=o|0;p=+p;q=q|0;r=+r;s=s|0;t=t|0;u=u|0;v=+v;w=+w;var x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0;ea=i;i=i+144|0;ga=ea+140|0;D=ea+136|0;aa=ea+132|0;P=ea+128|0;X=ea+124|0;fa=ea+120|0;Q=ea+116|0;x=ea+112|0;Y=ea+108|0;y=ea+104|0;E=ea+100|0;V=ea+96|0;H=ea+92|0;N=ea+88|0;F=ea+84|0;O=ea+80|0;W=ea+76|0;I=ea+72|0;$=ea+68|0;ca=ea+64|0;J=ea+60|0;z=ea+56|0;G=ea+52|0;U=ea+48|0;S=ea+44|0;A=ea+40|0;B=ea+36|0;C=ea+32|0;M=ea+28|0;L=ea+24|0;K=ea+20|0;T=ea+16|0;R=ea+12|0;Z=ea+8|0;ba=ea+4|0;da=ea;c[ga>>2]=a;c[D>>2]=d;c[aa>>2]=e;c[P>>2]=f;c[X>>2]=h;c[fa>>2]=j;c[Q>>2]=k;c[x>>2]=l;c[Y>>2]=m;g[y>>2]=n;c[E>>2]=o;g[V>>2]=p;c[H>>2]=q;g[N>>2]=r;c[F>>2]=s;c[O>>2]=t;c[W>>2]=u;g[I>>2]=v;g[$>>2]=w;c[U>>2]=c[(c[ga>>2]|0)+8>>2];c[S>>2]=c[(c[ga>>2]|0)+32>>2];c[z>>2]=c[fa>>2]|0?c[fa>>2]|0:c[U>>2]|0;c[J>>2]=b[(c[S>>2]|0)+(c[z>>2]<<1)>>1]<<c[P>>2];if((c[Q>>2]|0)==2)c[J>>2]=(c[J>>2]|0)+(b[(c[S>>2]|0)+(((c[x>>2]|0)<(c[z>>2]|0)?c[x>>2]|0:c[z>>2]|0)<<1)>>1]<<c[P>>2]);c[ca>>2]=c[aa>>2];if(c[c[D>>2]>>2]|0?+g[(c[D>>2]|0)+16>>2]<.4:0)c[ca>>2]=(c[ca>>2]|0)-~~(+(c[J>>2]<<3|0)*(.4000000059604645-+g[(c[D>>2]|0)+16>>2]));if((c[Q>>2]|0)==2){c[A>>2]=(c[x>>2]|0)<(c[z>>2]|0)?c[x>>2]|0:c[z>>2]|0;c[B>>2]=(b[(c[S>>2]|0)+(c[A>>2]<<1)>>1]<<c[P>>2])-(c[A>>2]|0);g[C>>2]=+(c[B>>2]|0)*.800000011920929/+(c[J>>2]|0);g[y>>2]=+g[y>>2]<1.0?+g[y>>2]:1.0;if(+g[C>>2]*+(c[ca>>2]|0)<(+g[y>>2]-.10000000149011612)*+(c[B>>2]<<3|0))r=+g[C>>2]*+(c[ca>>2]|0);else r=(+g[y>>2]-.10000000149011612)*+(c[B>>2]<<3|0);c[ca>>2]=(c[ca>>2]|0)-~~r}c[ca>>2]=(c[ca>>2]|0)+((c[E>>2]|0)-(16<<c[P>>2]));g[G>>2]=(c[F>>2]|0)==5010?.019999999552965164:.03999999910593033;c[ca>>2]=(c[ca>>2]|0)+~~((+g[V>>2]-+g[G>>2])*+(c[ca>>2]|0));if(!(c[O>>2]|0?1:(c[c[D>>2]>>2]|0)==0)){if(0.0>+g[(c[D>>2]|0)+4>>2]-.15000000596046448)r=0.0;else r=+g[(c[D>>2]|0)+4>>2]-.15000000596046448;g[L>>2]=r-.09000000357627869;c[M>>2]=(c[ca>>2]|0)+~~(+(c[J>>2]<<3|0)*1.2000000476837158*+g[L>>2]);if(c[H>>2]|0)c[M>>2]=(c[M>>2]|0)+~~(+(c[J>>2]<<3|0)*.800000011920929);c[ca>>2]=c[M>>2]}if(!((c[W>>2]|0)==0|(c[O>>2]|0)!=0)){c[K>>2]=(c[ca>>2]|0)+~~(+g[I>>2]*+(c[J>>2]<<3|0));if(((c[ca>>2]|0)/4|0|0)>(c[K>>2]|0))x=(c[ca>>2]|0)/4|0;else x=c[K>>2]|0;c[ca>>2]=x}c[R>>2]=b[(c[S>>2]|0)+((c[U>>2]|0)-2<<1)>>1]<<c[P>>2];p=+((_(c[Q>>2]|0,c[R>>2]|0)|0)<<3|0);c[T>>2]=~~(p*+g[N>>2]);c[T>>2]=(c[T>>2]|0)>(c[ca>>2]>>2|0)?c[T>>2]|0:c[ca>>2]>>2;c[ca>>2]=(c[ca>>2]|0)<(c[T>>2]|0)?c[ca>>2]|0:c[T>>2]|0;if((c[W>>2]|0)==0|(c[O>>2]|0)!=0?(c[Y>>2]|0)!=0|(c[X>>2]|0)<64e3:0){if(0.0>+((c[X>>2]|0)-32e3|0)*.000030517578125)r=0.0;else r=+((c[X>>2]|0)-32e3|0)*.000030517578125;g[Z>>2]=r;if(c[Y>>2]|0)g[Z>>2]=+g[Z>>2]<.6700000166893005?+g[Z>>2]:.6700000166893005;c[ca>>2]=(c[aa>>2]|0)+~~(+g[Z>>2]*+((c[ca>>2]|0)-(c[aa>>2]|0)|0))}if(!((c[W>>2]|0)==0&+g[V>>2]<.20000000298023224)){fa=c[aa>>2]|0;fa=fa<<1;da=c[ca>>2]|0;da=(fa|0)<(da|0);fa=c[aa>>2]|0;fa=fa<<1;ga=c[ca>>2]|0;ga=da?fa:ga;c[ca>>2]=ga;ga=c[ca>>2]|0;i=ea;return ga|0}if(0>((32e3<(96e3-(c[X>>2]|0)|0)?32e3:96e3-(c[X>>2]|0)|0)|0))x=0;else x=32e3<(96e3-(c[X>>2]|0)|0)?32e3:96e3-(c[X>>2]|0)|0;g[ba>>2]=+(x|0)*3.099999958067201e-06;g[da>>2]=+g[$>>2]*+g[ba>>2];c[ca>>2]=(c[ca>>2]|0)+~~(+g[da>>2]*+(c[ca>>2]|0));fa=c[aa>>2]|0;fa=fa<<1;da=c[ca>>2]|0;da=(fa|0)<(da|0);fa=c[aa>>2]|0;fa=fa<<1;ga=c[ca>>2]|0;ga=da?fa:ga;c[ca>>2]=ga;ga=c[ca>>2]|0;i=ea;return ga|0}function Eb(a){a=a|0;var b=0,d=0;d=i;i=i+16|0;b=d;c[b>>2]=a;i=d;return c[(c[b>>2]|0)+44>>2]|0}function Fb(a){a=a|0;var b=0,d=0,e=0;b=i;i=i+16|0;d=b+4|0;e=b;c[d>>2]=a;c[e>>2]=Jc(48e3,960,0)|0;a=Gb(c[e>>2]|0,c[d>>2]|0)|0;i=b;return a|0}function Gb(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0;e=i;i=i+16|0;f=e+8|0;g=e+4|0;d=e;c[f>>2]=a;c[g>>2]=b;a=88+((_(c[g>>2]|0,2048+(c[(c[f>>2]|0)+4>>2]|0)|0)|0)-1<<2)|0;c[d>>2]=a+((c[g>>2]|0)*24<<2)+(c[(c[f>>2]|0)+8>>2]<<3<<2);i=e;return c[d>>2]|0}function Hb(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0;j=i;i=i+32|0;e=j+16|0;f=j+12|0;g=j+8|0;k=j+4|0;h=j;c[f>>2]=a;c[g>>2]=b;c[k>>2]=d;b=c[f>>2]|0;a=Jc(48e3,960,0)|0;c[h>>2]=Ib(b,a,c[k>>2]|0)|0;if(c[h>>2]|0){c[e>>2]=c[h>>2];k=c[e>>2]|0;i=j;return k|0}k=bb(c[g>>2]|0)|0;c[(c[f>>2]|0)+16>>2]=k;if(!(c[(c[f>>2]|0)+16>>2]|0)){c[e>>2]=-1;k=c[e>>2]|0;i=j;return k|0}else{c[e>>2]=0;k=c[e>>2]|0;i=j;return k|0}return 0}function Ib(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0;j=i;i=i+32|0;e=j+16|0;f=j+12|0;g=j+8|0;h=j+4|0;c[f>>2]=a;c[g>>2]=b;c[h>>2]=d;if((c[h>>2]|0)<0|(c[h>>2]|0)>2){c[e>>2]=-1;a=c[e>>2]|0;i=j;return a|0}if(!(c[f>>2]|0)){c[e>>2]=-7;a=c[e>>2]|0;i=j;return a|0}else{a=c[f>>2]|0;oj(a|0,0,Gb(c[g>>2]|0,c[h>>2]|0)|0)|0;c[c[f>>2]>>2]=c[g>>2];c[(c[f>>2]|0)+4>>2]=c[(c[g>>2]|0)+4>>2];a=c[h>>2]|0;c[(c[f>>2]|0)+8>>2]=a;c[(c[f>>2]|0)+12>>2]=a;c[(c[f>>2]|0)+16>>2]=1;c[(c[f>>2]|0)+20>>2]=0;c[(c[f>>2]|0)+24>>2]=c[(c[c[f>>2]>>2]|0)+12>>2];c[(c[f>>2]|0)+28>>2]=1;a=Jb()|0;c[(c[f>>2]|0)+32>>2]=a;c[(c[f>>2]|0)+48>>2]=0;Kb(c[f>>2]|0,4028,j)|0;c[e>>2]=0;a=c[e>>2]|0;i=j;return a|0}return 0}function Jb(){return 0}function Kb(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0;x=i;i=i+96|0;w=x+80|0;e=x+76|0;y=x+72|0;f=x+56|0;n=x+52|0;o=x+48|0;p=x+44|0;q=x+40|0;r=x+36|0;h=x+32|0;j=x+28|0;k=x+24|0;l=x+20|0;m=x+16|0;s=x+12|0;t=x+8|0;u=x+4|0;v=x;c[e>>2]=a;c[y>>2]=b;c[f>>2]=d;a:do switch(c[y>>2]|0){case 10010:{d=(c[f>>2]|0)+(4-1)&~(4-1);y=c[d>>2]|0;c[f>>2]=d+4;c[n>>2]=y;if((c[n>>2]|0)>=0?(c[n>>2]|0)<(c[(c[c[e>>2]>>2]|0)+8>>2]|0):0){c[(c[e>>2]|0)+20>>2]=c[n>>2];e=24}else e=25;break}case 10012:{d=(c[f>>2]|0)+(4-1)&~(4-1);y=c[d>>2]|0;c[f>>2]=d+4;c[o>>2]=y;if((c[o>>2]|0)>=1?(c[o>>2]|0)<=(c[(c[c[e>>2]>>2]|0)+8>>2]|0):0){c[(c[e>>2]|0)+24>>2]=c[o>>2];e=24}else e=25;break}case 10008:{d=(c[f>>2]|0)+(4-1)&~(4-1);y=c[d>>2]|0;c[f>>2]=d+4;c[p>>2]=y;if((c[p>>2]|0)<1|(c[p>>2]|0)>2)e=25;else{c[(c[e>>2]|0)+12>>2]=c[p>>2];e=24}break}case 10007:{d=(c[f>>2]|0)+(4-1)&~(4-1);y=c[d>>2]|0;c[f>>2]=d+4;c[q>>2]=y;if(!(c[q>>2]|0))e=25;else{c[c[q>>2]>>2]=c[(c[e>>2]|0)+40>>2];c[(c[e>>2]|0)+40>>2]=0;e=24}break}case 4027:{d=(c[f>>2]|0)+(4-1)&~(4-1);y=c[d>>2]|0;c[f>>2]=d+4;c[r>>2]=y;if(!(c[r>>2]|0))e=25;else{c[c[r>>2]>>2]=(c[(c[e>>2]|0)+4>>2]|0)/(c[(c[e>>2]|0)+16>>2]|0)|0;e=24}break}case 4028:{c[j>>2]=(c[e>>2]|0)+84+((_(2048+(c[(c[e>>2]|0)+4>>2]|0)|0,c[(c[e>>2]|0)+8>>2]|0)|0)<<2);c[k>>2]=(c[j>>2]|0)+((c[(c[e>>2]|0)+8>>2]|0)*24<<2);c[l>>2]=(c[k>>2]|0)+(c[(c[c[e>>2]>>2]|0)+8>>2]<<1<<2);c[m>>2]=(c[l>>2]|0)+(c[(c[c[e>>2]>>2]|0)+8>>2]<<1<<2);oj((c[e>>2]|0)+36|0,0,(Gb(c[c[e>>2]>>2]|0,c[(c[e>>2]|0)+8>>2]|0)|0)-((c[e>>2]|0)+36-(c[e>>2]|0))|0)|0;c[h>>2]=0;while(1){if((c[h>>2]|0)>=(c[(c[c[e>>2]>>2]|0)+8>>2]<<1|0)){e=24;break a}g[(c[m>>2]|0)+(c[h>>2]<<2)>>2]=-28.0;g[(c[l>>2]|0)+(c[h>>2]<<2)>>2]=-28.0;c[h>>2]=(c[h>>2]|0)+1}}case 4033:{d=(c[f>>2]|0)+(4-1)&~(4-1);y=c[d>>2]|0;c[f>>2]=d+4;c[s>>2]=y;if(!(c[s>>2]|0))e=25;else{c[c[s>>2]>>2]=c[(c[e>>2]|0)+52>>2];e=24}break}case 10015:{d=(c[f>>2]|0)+(4-1)&~(4-1);y=c[d>>2]|0;c[f>>2]=d+4;c[t>>2]=y;if(!(c[t>>2]|0))e=25;else{c[c[t>>2]>>2]=c[c[e>>2]>>2];e=24}break}case 10016:{d=(c[f>>2]|0)+(4-1)&~(4-1);y=c[d>>2]|0;c[f>>2]=d+4;c[u>>2]=y;c[(c[e>>2]|0)+28>>2]=c[u>>2];e=24;break}case 4031:{d=(c[f>>2]|0)+(4-1)&~(4-1);y=c[d>>2]|0;c[f>>2]=d+4;c[v>>2]=y;if(!(c[v>>2]|0))e=25;else{c[c[v>>2]>>2]=c[(c[e>>2]|0)+36>>2];e=24}break}default:{c[w>>2]=-5;y=c[w>>2]|0;i=x;return y|0}}while(0);if((e|0)==24){c[w>>2]=0;y=c[w>>2]|0;i=x;return y|0}else if((e|0)==25){c[w>>2]=-1;y=c[w>>2]|0;i=x;return y|0}return 0}
+ function ye(d,e,f,g,h,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D){d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;p=p|0;q=q|0;r=r|0;s=s|0;t=t|0;u=u|0;v=v|0;w=w|0;x=x|0;y=y|0;z=z|0;A=A|0;B=B|0;C=C|0;D=D|0;var E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0,ha=0,ja=0,ka=0,la=0,ma=0,oa=0,pa=0,qa=0,ra=0,sa=0,ta=0,ua=0,va=0,wa=0,xa=0,ya=0,za=0,Aa=0,Ba=0,Ca=0,Da=0,Ea=0,Fa=0,Ga=0,Ha=0,Ia=0,Ja=0,Ka=0,La=0,Ma=0,Na=0,Oa=0;Na=i;i=i+256|0;E=Na+240|0;Ha=Na+236|0;N=Na+232|0;T=Na+228|0;U=Na+224|0;V=Na+220|0;W=Na+216|0;X=Na+212|0;Y=Na+208|0;Z=Na+204|0;F=Na+200|0;G=Na+196|0;H=Na+192|0;I=Na+188|0;J=Na+184|0;Oa=Na+180|0;K=Na+176|0;L=Na+172|0;Ia=Na+168|0;M=Na+164|0;O=Na+160|0;P=Na+156|0;Q=Na+152|0;Ja=Na+148|0;R=Na+144|0;S=Na+140|0;la=Na+136|0;ma=Na+132|0;La=Na+128|0;ha=Na+124|0;ga=Na+120|0;ea=Na+116|0;oa=Na+112|0;ja=Na+108|0;ca=Na+104|0;ba=Na+100|0;pa=Na+96|0;ra=Na+92|0;qa=Na+88|0;ya=Na+84|0;Ba=Na+80|0;za=Na+76|0;Aa=Na+72|0;fa=Na+68|0;da=Na+64|0;va=Na+60|0;wa=Na+56|0;xa=Na+52|0;ka=Na+48|0;aa=Na+44|0;Ga=Na+40|0;$=Na+36|0;Ea=Na+32|0;Fa=Na+28|0;Ca=Na+24|0;sa=Na+20|0;Da=Na+16|0;ta=Na+12|0;Ma=Na+8|0;ua=Na+4|0;Ka=Na;c[E>>2]=d;c[Ha>>2]=e;c[N>>2]=f;c[T>>2]=g;c[U>>2]=h;c[V>>2]=j;c[W>>2]=k;c[X>>2]=l;c[Y>>2]=m;c[Z>>2]=n;c[F>>2]=o;c[G>>2]=p;c[H>>2]=q;c[I>>2]=r;c[J>>2]=s;c[Oa>>2]=t;c[K>>2]=u;c[L>>2]=v;c[Ia>>2]=w;c[M>>2]=x;c[O>>2]=y;c[P>>2]=z;c[Q>>2]=A;c[Ja>>2]=B;c[R>>2]=C;c[S>>2]=D;j=c[Ja>>2]|0;c[Ka>>2]=ia()|0;t=i;i=i+((1*(j*48|0)|0)+15&-16)|0;c[Da>>2]=(c[E>>2]|0)+1280+((c[(c[E>>2]|0)+4364>>2]|0)-(c[G>>2]|0)+1<<2);c[sa>>2]=(c[W>>2]|0)+((c[(c[E>>2]|0)+4360>>2]|0)-(c[G>>2]|0)+2<<2);c[$>>2]=c[Oa>>2]>>6;c[la>>2]=0;while(1){if((c[la>>2]|0)>=(c[Ia>>2]|0))break;if((c[N>>2]|0)==2){c[ca>>2]=2;Oa=_(c[c[sa>>2]>>2]>>16,b[c[Z>>2]>>1]|0)|0;c[ca>>2]=(c[ca>>2]|0)+(Oa+((_(c[c[sa>>2]>>2]&65535,b[c[Z>>2]>>1]|0)|0)>>16));Oa=_(c[(c[sa>>2]|0)+-4>>2]>>16,b[(c[Z>>2]|0)+2>>1]|0)|0;c[ca>>2]=(c[ca>>2]|0)+(Oa+((_(c[(c[sa>>2]|0)+-4>>2]&65535,b[(c[Z>>2]|0)+2>>1]|0)|0)>>16));Oa=_(c[(c[sa>>2]|0)+-8>>2]>>16,b[(c[Z>>2]|0)+4>>1]|0)|0;c[ca>>2]=(c[ca>>2]|0)+(Oa+((_(c[(c[sa>>2]|0)+-8>>2]&65535,b[(c[Z>>2]|0)+4>>1]|0)|0)>>16));Oa=_(c[(c[sa>>2]|0)+-12>>2]>>16,b[(c[Z>>2]|0)+6>>1]|0)|0;c[ca>>2]=(c[ca>>2]|0)+(Oa+((_(c[(c[sa>>2]|0)+-12>>2]&65535,b[(c[Z>>2]|0)+6>>1]|0)|0)>>16));Oa=_(c[(c[sa>>2]|0)+-16>>2]>>16,b[(c[Z>>2]|0)+8>>1]|0)|0;c[ca>>2]=(c[ca>>2]|0)+(Oa+((_(c[(c[sa>>2]|0)+-16>>2]&65535,b[(c[Z>>2]|0)+8>>1]|0)|0)>>16));c[ca>>2]=c[ca>>2]<<1;c[sa>>2]=(c[sa>>2]|0)+4}else c[ca>>2]=0;if((c[G>>2]|0)>0){Oa=_((c[c[Da>>2]>>2]|0)+(c[(c[Da>>2]|0)+-8>>2]|0)>>16,(c[H>>2]&65535)<<16>>16)|0;c[ra>>2]=Oa+((_((c[c[Da>>2]>>2]|0)+(c[(c[Da>>2]|0)+-8>>2]|0)&65535,(c[H>>2]&65535)<<16>>16)|0)>>16);Oa=(c[ra>>2]|0)+(_(c[(c[Da>>2]|0)+-4>>2]>>16,c[H>>2]>>16)|0)|0;c[ra>>2]=Oa+((_(c[(c[Da>>2]|0)+-4>>2]&65535,c[H>>2]>>16)|0)>>16);c[ra>>2]=(c[ca>>2]|0)-(c[ra>>2]<<2);c[Da>>2]=(c[Da>>2]|0)+4}else c[ra>>2]=0;c[La>>2]=0;while(1){if((c[La>>2]|0)>=(c[Ja>>2]|0))break;c[Ma>>2]=(c[Ha>>2]|0)+((c[La>>2]|0)*1168|0);c[ua>>2]=t+((c[La>>2]|0)*48|0);Oa=907633515+(_(c[(c[Ma>>2]|0)+1156>>2]|0,196314165)|0)|0;c[(c[Ma>>2]|0)+1156>>2]=Oa;c[ta>>2]=(c[Ma>>2]|0)+(31+(c[la>>2]|0)<<2);c[ba>>2]=c[P>>2]>>1;Oa=_(c[c[ta>>2]>>2]>>16,b[c[Y>>2]>>1]|0)|0;c[ba>>2]=(c[ba>>2]|0)+(Oa+((_(c[c[ta>>2]>>2]&65535,b[c[Y>>2]>>1]|0)|0)>>16));Oa=_(c[(c[ta>>2]|0)+-4>>2]>>16,b[(c[Y>>2]|0)+2>>1]|0)|0;c[ba>>2]=(c[ba>>2]|0)+(Oa+((_(c[(c[ta>>2]|0)+-4>>2]&65535,b[(c[Y>>2]|0)+2>>1]|0)|0)>>16));Oa=_(c[(c[ta>>2]|0)+-8>>2]>>16,b[(c[Y>>2]|0)+4>>1]|0)|0;c[ba>>2]=(c[ba>>2]|0)+(Oa+((_(c[(c[ta>>2]|0)+-8>>2]&65535,b[(c[Y>>2]|0)+4>>1]|0)|0)>>16));Oa=_(c[(c[ta>>2]|0)+-12>>2]>>16,b[(c[Y>>2]|0)+6>>1]|0)|0;c[ba>>2]=(c[ba>>2]|0)+(Oa+((_(c[(c[ta>>2]|0)+-12>>2]&65535,b[(c[Y>>2]|0)+6>>1]|0)|0)>>16));Oa=_(c[(c[ta>>2]|0)+-16>>2]>>16,b[(c[Y>>2]|0)+8>>1]|0)|0;c[ba>>2]=(c[ba>>2]|0)+(Oa+((_(c[(c[ta>>2]|0)+-16>>2]&65535,b[(c[Y>>2]|0)+8>>1]|0)|0)>>16));Oa=_(c[(c[ta>>2]|0)+-20>>2]>>16,b[(c[Y>>2]|0)+10>>1]|0)|0;c[ba>>2]=(c[ba>>2]|0)+(Oa+((_(c[(c[ta>>2]|0)+-20>>2]&65535,b[(c[Y>>2]|0)+10>>1]|0)|0)>>16));Oa=_(c[(c[ta>>2]|0)+-24>>2]>>16,b[(c[Y>>2]|0)+12>>1]|0)|0;c[ba>>2]=(c[ba>>2]|0)+(Oa+((_(c[(c[ta>>2]|0)+-24>>2]&65535,b[(c[Y>>2]|0)+12>>1]|0)|0)>>16));Oa=_(c[(c[ta>>2]|0)+-28>>2]>>16,b[(c[Y>>2]|0)+14>>1]|0)|0;c[ba>>2]=(c[ba>>2]|0)+(Oa+((_(c[(c[ta>>2]|0)+-28>>2]&65535,b[(c[Y>>2]|0)+14>>1]|0)|0)>>16));Oa=_(c[(c[ta>>2]|0)+-32>>2]>>16,b[(c[Y>>2]|0)+16>>1]|0)|0;c[ba>>2]=(c[ba>>2]|0)+(Oa+((_(c[(c[ta>>2]|0)+-32>>2]&65535,b[(c[Y>>2]|0)+16>>1]|0)|0)>>16));Oa=_(c[(c[ta>>2]|0)+-36>>2]>>16,b[(c[Y>>2]|0)+18>>1]|0)|0;c[ba>>2]=(c[ba>>2]|0)+(Oa+((_(c[(c[ta>>2]|0)+-36>>2]&65535,b[(c[Y>>2]|0)+18>>1]|0)|0)>>16));if((c[P>>2]|0)==16){Oa=_(c[(c[ta>>2]|0)+-40>>2]>>16,b[(c[Y>>2]|0)+20>>1]|0)|0;c[ba>>2]=(c[ba>>2]|0)+(Oa+((_(c[(c[ta>>2]|0)+-40>>2]&65535,b[(c[Y>>2]|0)+20>>1]|0)|0)>>16));Oa=_(c[(c[ta>>2]|0)+-44>>2]>>16,b[(c[Y>>2]|0)+22>>1]|0)|0;c[ba>>2]=(c[ba>>2]|0)+(Oa+((_(c[(c[ta>>2]|0)+-44>>2]&65535,b[(c[Y>>2]|0)+22>>1]|0)|0)>>16));Oa=_(c[(c[ta>>2]|0)+-48>>2]>>16,b[(c[Y>>2]|0)+24>>1]|0)|0;c[ba>>2]=(c[ba>>2]|0)+(Oa+((_(c[(c[ta>>2]|0)+-48>>2]&65535,b[(c[Y>>2]|0)+24>>1]|0)|0)>>16));Oa=_(c[(c[ta>>2]|0)+-52>>2]>>16,b[(c[Y>>2]|0)+26>>1]|0)|0;c[ba>>2]=(c[ba>>2]|0)+(Oa+((_(c[(c[ta>>2]|0)+-52>>2]&65535,b[(c[Y>>2]|0)+26>>1]|0)|0)>>16));Oa=_(c[(c[ta>>2]|0)+-56>>2]>>16,b[(c[Y>>2]|0)+28>>1]|0)|0;c[ba>>2]=(c[ba>>2]|0)+(Oa+((_(c[(c[ta>>2]|0)+-56>>2]&65535,b[(c[Y>>2]|0)+28>>1]|0)|0)>>16));Oa=_(c[(c[ta>>2]|0)+-60>>2]>>16,b[(c[Y>>2]|0)+30>>1]|0)|0;c[ba>>2]=(c[ba>>2]|0)+(Oa+((_(c[(c[ta>>2]|0)+-60>>2]&65535,b[(c[Y>>2]|0)+30>>1]|0)|0)>>16))}c[ba>>2]=c[ba>>2]<<4;Oa=_(c[(c[Ma>>2]|0)+1088>>2]>>16,(c[Q>>2]&65535)<<16>>16)|0;c[Fa>>2]=(c[c[ta>>2]>>2]|0)+(Oa+((_(c[(c[Ma>>2]|0)+1088>>2]&65535,(c[Q>>2]&65535)<<16>>16)|0)>>16));Oa=_((c[(c[Ma>>2]|0)+1088+4>>2]|0)-(c[Fa>>2]|0)>>16,(c[Q>>2]&65535)<<16>>16)|0;c[Ea>>2]=(c[(c[Ma>>2]|0)+1088>>2]|0)+(Oa+((_((c[(c[Ma>>2]|0)+1088+4>>2]|0)-(c[Fa>>2]|0)&65535,(c[Q>>2]&65535)<<16>>16)|0)>>16));c[(c[Ma>>2]|0)+1088>>2]=c[Fa>>2];c[pa>>2]=c[O>>2]>>1;Oa=_(c[Fa>>2]>>16,b[c[F>>2]>>1]|0)|0;c[pa>>2]=(c[pa>>2]|0)+(Oa+((_(c[Fa>>2]&65535,b[c[F>>2]>>1]|0)|0)>>16));c[ma>>2]=2;while(1){if((c[ma>>2]|0)>=(c[O>>2]|0))break;Oa=_((c[(c[Ma>>2]|0)+1088+((c[ma>>2]|0)+0<<2)>>2]|0)-(c[Ea>>2]|0)>>16,(c[Q>>2]&65535)<<16>>16)|0;c[Fa>>2]=(c[(c[Ma>>2]|0)+1088+((c[ma>>2]|0)-1<<2)>>2]|0)+(Oa+((_((c[(c[Ma>>2]|0)+1088+((c[ma>>2]|0)+0<<2)>>2]|0)-(c[Ea>>2]|0)&65535,(c[Q>>2]&65535)<<16>>16)|0)>>16));c[(c[Ma>>2]|0)+1088+((c[ma>>2]|0)-1<<2)>>2]=c[Ea>>2];Oa=_(c[Ea>>2]>>16,b[(c[F>>2]|0)+((c[ma>>2]|0)-1<<1)>>1]|0)|0;c[pa>>2]=(c[pa>>2]|0)+(Oa+((_(c[Ea>>2]&65535,b[(c[F>>2]|0)+((c[ma>>2]|0)-1<<1)>>1]|0)|0)>>16));Oa=_((c[(c[Ma>>2]|0)+1088+((c[ma>>2]|0)+1<<2)>>2]|0)-(c[Fa>>2]|0)>>16,(c[Q>>2]&65535)<<16>>16)|0;c[Ea>>2]=(c[(c[Ma>>2]|0)+1088+((c[ma>>2]|0)+0<<2)>>2]|0)+(Oa+((_((c[(c[Ma>>2]|0)+1088+((c[ma>>2]|0)+1<<2)>>2]|0)-(c[Fa>>2]|0)&65535,(c[Q>>2]&65535)<<16>>16)|0)>>16));c[(c[Ma>>2]|0)+1088+((c[ma>>2]|0)+0<<2)>>2]=c[Fa>>2];Oa=_(c[Fa>>2]>>16,b[(c[F>>2]|0)+(c[ma>>2]<<1)>>1]|0)|0;c[pa>>2]=(c[pa>>2]|0)+(Oa+((_(c[Fa>>2]&65535,b[(c[F>>2]|0)+(c[ma>>2]<<1)>>1]|0)|0)>>16));c[ma>>2]=(c[ma>>2]|0)+2}c[(c[Ma>>2]|0)+1088+((c[O>>2]|0)-1<<2)>>2]=c[Ea>>2];Oa=_(c[Ea>>2]>>16,b[(c[F>>2]|0)+((c[O>>2]|0)-1<<1)>>1]|0)|0;c[pa>>2]=(c[pa>>2]|0)+(Oa+((_(c[Ea>>2]&65535,b[(c[F>>2]|0)+((c[O>>2]|0)-1<<1)>>1]|0)|0)>>16));c[pa>>2]=c[pa>>2]<<1;Oa=_(c[(c[Ma>>2]|0)+1152>>2]>>16,(c[I>>2]&65535)<<16>>16)|0;c[pa>>2]=(c[pa>>2]|0)+(Oa+((_(c[(c[Ma>>2]|0)+1152>>2]&65535,(c[I>>2]&65535)<<16>>16)|0)>>16));c[pa>>2]=c[pa>>2]<<2;Oa=_(c[(c[Ma>>2]|0)+960+(c[c[R>>2]>>2]<<2)>>2]>>16,(c[J>>2]&65535)<<16>>16)|0;c[qa>>2]=Oa+((_(c[(c[Ma>>2]|0)+960+(c[c[R>>2]>>2]<<2)>>2]&65535,(c[J>>2]&65535)<<16>>16)|0)>>16);Oa=(c[qa>>2]|0)+(_(c[(c[Ma>>2]|0)+1152>>2]>>16,c[J>>2]>>16)|0)|0;c[qa>>2]=Oa+((_(c[(c[Ma>>2]|0)+1152>>2]&65535,c[J>>2]>>16)|0)>>16);c[qa>>2]=c[qa>>2]<<2;c[Ea>>2]=(c[pa>>2]|0)+(c[qa>>2]|0);c[Fa>>2]=(c[ra>>2]|0)+(c[ba>>2]|0);c[Ea>>2]=(c[Fa>>2]|0)-(c[Ea>>2]|0);c[Ea>>2]=(c[Ea>>2]>>3)+1>>1;c[ya>>2]=(c[(c[T>>2]|0)+(c[la>>2]<<2)>>2]|0)-(c[Ea>>2]|0);if((c[(c[Ma>>2]|0)+1156>>2]|0)<0)c[ya>>2]=0-(c[ya>>2]|0);if((c[ya>>2]|0)>30720)o=30720;else o=(c[ya>>2]|0)<-31744?-31744:c[ya>>2]|0;c[ya>>2]=o;c[wa>>2]=(c[ya>>2]|0)-(c[L>>2]|0);c[va>>2]=c[wa>>2]>>10;o=c[va>>2]|0;do if((c[va>>2]|0)<=0){if(!o){c[wa>>2]=c[L>>2];c[xa>>2]=(c[wa>>2]|0)+944;c[za>>2]=_((c[wa>>2]&65535)<<16>>16,(c[K>>2]&65535)<<16>>16)|0;c[Aa>>2]=_((c[xa>>2]&65535)<<16>>16,(c[K>>2]&65535)<<16>>16)|0;break}if((c[va>>2]|0)==-1){c[xa>>2]=c[L>>2];c[wa>>2]=(c[xa>>2]|0)-944;c[za>>2]=_((0-(c[wa>>2]|0)&65535)<<16>>16,(c[K>>2]&65535)<<16>>16)|0;c[Aa>>2]=_((c[xa>>2]&65535)<<16>>16,(c[K>>2]&65535)<<16>>16)|0;break}else{c[wa>>2]=(c[va>>2]<<10)+80;c[wa>>2]=(c[wa>>2]|0)+(c[L>>2]|0);c[xa>>2]=(c[wa>>2]|0)+1024;c[za>>2]=_((0-(c[wa>>2]|0)&65535)<<16>>16,(c[K>>2]&65535)<<16>>16)|0;c[Aa>>2]=_((0-(c[xa>>2]|0)&65535)<<16>>16,(c[K>>2]&65535)<<16>>16)|0;break}}else{c[wa>>2]=(o<<10)-80;c[wa>>2]=(c[wa>>2]|0)+(c[L>>2]|0);c[xa>>2]=(c[wa>>2]|0)+1024;c[za>>2]=_((c[wa>>2]&65535)<<16>>16,(c[K>>2]&65535)<<16>>16)|0;c[Aa>>2]=_((c[xa>>2]&65535)<<16>>16,(c[K>>2]&65535)<<16>>16)|0}while(0);c[Ba>>2]=(c[ya>>2]|0)-(c[wa>>2]|0);c[za>>2]=(c[za>>2]|0)+(_((c[Ba>>2]&65535)<<16>>16,(c[Ba>>2]&65535)<<16>>16)|0)>>10;c[Ba>>2]=(c[ya>>2]|0)-(c[xa>>2]|0);c[Aa>>2]=(c[Aa>>2]|0)+(_((c[Ba>>2]&65535)<<16>>16,(c[Ba>>2]&65535)<<16>>16)|0)>>10;o=c[(c[Ma>>2]|0)+1164>>2]|0;if((c[za>>2]|0)<(c[Aa>>2]|0)){c[(c[ua>>2]|0)+4>>2]=o+(c[za>>2]|0);c[(c[ua>>2]|0)+24+4>>2]=(c[(c[Ma>>2]|0)+1164>>2]|0)+(c[Aa>>2]|0);c[c[ua>>2]>>2]=c[wa>>2];c[(c[ua>>2]|0)+24>>2]=c[xa>>2]}else{c[(c[ua>>2]|0)+4>>2]=o+(c[Aa>>2]|0);c[(c[ua>>2]|0)+24+4>>2]=(c[(c[Ma>>2]|0)+1164>>2]|0)+(c[za>>2]|0);c[c[ua>>2]>>2]=c[xa>>2];c[(c[ua>>2]|0)+24>>2]=c[wa>>2]}c[ka>>2]=c[c[ua>>2]>>2]<<4;if((c[(c[Ma>>2]|0)+1156>>2]|0)<0)c[ka>>2]=0-(c[ka>>2]|0);c[aa>>2]=(c[ka>>2]|0)+(c[ca>>2]|0);c[Ga>>2]=(c[aa>>2]|0)+(c[ba>>2]|0);c[Ca>>2]=(c[Ga>>2]|0)-(c[pa>>2]|0);c[(c[ua>>2]|0)+16>>2]=(c[Ca>>2]|0)-(c[qa>>2]|0);c[(c[ua>>2]|0)+12>>2]=c[Ca>>2];c[(c[ua>>2]|0)+20>>2]=c[aa>>2];c[(c[ua>>2]|0)+8>>2]=c[Ga>>2];c[ka>>2]=c[(c[ua>>2]|0)+24>>2]<<4;if((c[(c[Ma>>2]|0)+1156>>2]|0)<0)c[ka>>2]=0-(c[ka>>2]|0);c[aa>>2]=(c[ka>>2]|0)+(c[ca>>2]|0);c[Ga>>2]=(c[aa>>2]|0)+(c[ba>>2]|0);c[Ca>>2]=(c[Ga>>2]|0)-(c[pa>>2]|0);c[(c[ua>>2]|0)+24+16>>2]=(c[Ca>>2]|0)-(c[qa>>2]|0);c[(c[ua>>2]|0)+24+12>>2]=c[Ca>>2];c[(c[ua>>2]|0)+24+20>>2]=c[aa>>2];c[(c[ua>>2]|0)+24+8>>2]=c[Ga>>2];c[La>>2]=(c[La>>2]|0)+1}c[c[R>>2]>>2]=(c[c[R>>2]>>2]|0)-1&31;c[oa>>2]=(c[c[R>>2]>>2]|0)+(c[S>>2]|0)&31;c[fa>>2]=c[t+4>>2];c[ha>>2]=0;c[La>>2]=1;while(1){if((c[La>>2]|0)>=(c[Ja>>2]|0))break;if((c[t+((c[La>>2]|0)*48|0)+4>>2]|0)<(c[fa>>2]|0)){c[fa>>2]=c[t+((c[La>>2]|0)*48|0)+4>>2];c[ha>>2]=c[La>>2]}c[La>>2]=(c[La>>2]|0)+1}c[ja>>2]=c[(c[Ha>>2]|0)+((c[ha>>2]|0)*1168|0)+448+(c[oa>>2]<<2)>>2];c[La>>2]=0;while(1){if((c[La>>2]|0)>=(c[Ja>>2]|0))break;if((c[(c[Ha>>2]|0)+((c[La>>2]|0)*1168|0)+448+(c[oa>>2]<<2)>>2]|0)!=(c[ja>>2]|0)){c[t+((c[La>>2]|0)*48|0)+4>>2]=(c[t+((c[La>>2]|0)*48|0)+4>>2]|0)+134217727;c[t+((c[La>>2]|0)*48|0)+24+4>>2]=(c[t+((c[La>>2]|0)*48|0)+24+4>>2]|0)+134217727}c[La>>2]=(c[La>>2]|0)+1}c[da>>2]=c[t+4>>2];c[fa>>2]=c[t+24+4>>2];c[ea>>2]=0;c[ga>>2]=0;c[La>>2]=1;while(1){if((c[La>>2]|0)>=(c[Ja>>2]|0))break;if((c[t+((c[La>>2]|0)*48|0)+4>>2]|0)>(c[da>>2]|0)){c[da>>2]=c[t+((c[La>>2]|0)*48|0)+4>>2];c[ea>>2]=c[La>>2]}if((c[t+((c[La>>2]|0)*48|0)+24+4>>2]|0)<(c[fa>>2]|0)){c[fa>>2]=c[t+((c[La>>2]|0)*48|0)+24+4>>2];c[ga>>2]=c[La>>2]}c[La>>2]=(c[La>>2]|0)+1}if((c[fa>>2]|0)<(c[da>>2]|0)){pj((c[Ha>>2]|0)+((c[ea>>2]|0)*1168|0)+(c[la>>2]<<2)|0,(c[Ha>>2]|0)+((c[ga>>2]|0)*1168|0)+(c[la>>2]<<2)|0,1168-(c[la>>2]<<2)|0)|0;Oa=t+((c[ea>>2]|0)*48|0)|0;j=t+((c[ga>>2]|0)*48|0)+24|0;c[Oa>>2]=c[j>>2];c[Oa+4>>2]=c[j+4>>2];c[Oa+8>>2]=c[j+8>>2];c[Oa+12>>2]=c[j+12>>2];c[Oa+16>>2]=c[j+16>>2];c[Oa+20>>2]=c[j+20>>2]}c[Ma>>2]=(c[Ha>>2]|0)+((c[ha>>2]|0)*1168|0);if(!((c[M>>2]|0)<=0?(c[la>>2]|0)<(c[S>>2]|0):0)){a[(c[U>>2]|0)+((c[la>>2]|0)-(c[S>>2]|0))>>0]=(c[(c[Ma>>2]|0)+576+(c[oa>>2]<<2)>>2]>>9)+1>>1;Oa=_(c[(c[Ma>>2]|0)+704+(c[oa>>2]<<2)>>2]>>16,(c[(c[X>>2]|0)+(c[oa>>2]<<2)>>2]&65535)<<16>>16)|0;Oa=Oa+((_(c[(c[Ma>>2]|0)+704+(c[oa>>2]<<2)>>2]&65535,(c[(c[X>>2]|0)+(c[oa>>2]<<2)>>2]&65535)<<16>>16)|0)>>16)|0;if(((Oa+(_(c[(c[Ma>>2]|0)+704+(c[oa>>2]<<2)>>2]|0,(c[(c[X>>2]|0)+(c[oa>>2]<<2)>>2]>>15)+1>>1)|0)>>7)+1>>1|0)<=32767){Oa=_(c[(c[Ma>>2]|0)+704+(c[oa>>2]<<2)>>2]>>16,(c[(c[X>>2]|0)+(c[oa>>2]<<2)>>2]&65535)<<16>>16)|0;Oa=Oa+((_(c[(c[Ma>>2]|0)+704+(c[oa>>2]<<2)>>2]&65535,(c[(c[X>>2]|0)+(c[oa>>2]<<2)>>2]&65535)<<16>>16)|0)>>16)|0;if(((Oa+(_(c[(c[Ma>>2]|0)+704+(c[oa>>2]<<2)>>2]|0,(c[(c[X>>2]|0)+(c[oa>>2]<<2)>>2]>>15)+1>>1)|0)>>7)+1>>1|0)<-32768)o=-32768;else{o=_(c[(c[Ma>>2]|0)+704+(c[oa>>2]<<2)>>2]>>16,(c[(c[X>>2]|0)+(c[oa>>2]<<2)>>2]&65535)<<16>>16)|0;o=o+((_(c[(c[Ma>>2]|0)+704+(c[oa>>2]<<2)>>2]&65535,(c[(c[X>>2]|0)+(c[oa>>2]<<2)>>2]&65535)<<16>>16)|0)>>16)|0;o=(o+(_(c[(c[Ma>>2]|0)+704+(c[oa>>2]<<2)>>2]|0,(c[(c[X>>2]|0)+(c[oa>>2]<<2)>>2]>>15)+1>>1)|0)>>7)+1>>1}}else o=32767;b[(c[V>>2]|0)+((c[la>>2]|0)-(c[S>>2]|0)<<1)>>1]=o;c[(c[E>>2]|0)+1280+((c[(c[E>>2]|0)+4364>>2]|0)-(c[S>>2]|0)<<2)>>2]=c[(c[Ma>>2]|0)+960+(c[oa>>2]<<2)>>2];c[(c[W>>2]|0)+((c[(c[E>>2]|0)+4360>>2]|0)-(c[S>>2]|0)<<2)>>2]=c[(c[Ma>>2]|0)+832+(c[oa>>2]<<2)>>2]}Oa=(c[E>>2]|0)+4364|0;c[Oa>>2]=(c[Oa>>2]|0)+1;Oa=(c[E>>2]|0)+4360|0;c[Oa>>2]=(c[Oa>>2]|0)+1;c[La>>2]=0;while(1){if((c[La>>2]|0)>=(c[Ja>>2]|0))break;c[Ma>>2]=(c[Ha>>2]|0)+((c[La>>2]|0)*1168|0);c[ua>>2]=t+((c[La>>2]|0)*48|0);c[(c[Ma>>2]|0)+1152>>2]=c[(c[ua>>2]|0)+12>>2];c[(c[Ma>>2]|0)+(32+(c[la>>2]|0)<<2)>>2]=c[(c[ua>>2]|0)+8>>2];c[(c[Ma>>2]|0)+704+(c[c[R>>2]>>2]<<2)>>2]=c[(c[ua>>2]|0)+8>>2];c[(c[Ma>>2]|0)+576+(c[c[R>>2]>>2]<<2)>>2]=c[c[ua>>2]>>2];c[(c[Ma>>2]|0)+832+(c[c[R>>2]>>2]<<2)>>2]=c[(c[ua>>2]|0)+20>>2]<<1;c[(c[Ma>>2]|0)+960+(c[c[R>>2]>>2]<<2)>>2]=c[(c[ua>>2]|0)+16>>2];c[(c[Ma>>2]|0)+1156>>2]=(c[(c[Ma>>2]|0)+1156>>2]|0)+((c[c[ua>>2]>>2]>>9)+1>>1);c[(c[Ma>>2]|0)+448+(c[c[R>>2]>>2]<<2)>>2]=c[(c[Ma>>2]|0)+1156>>2];c[(c[Ma>>2]|0)+1164>>2]=c[(c[ua>>2]|0)+4>>2];c[La>>2]=(c[La>>2]|0)+1}c[(c[X>>2]|0)+(c[c[R>>2]>>2]<<2)>>2]=c[$>>2];c[la>>2]=(c[la>>2]|0)+1}c[La>>2]=0;while(1){if((c[La>>2]|0)>=(c[Ja>>2]|0))break;c[Ma>>2]=(c[Ha>>2]|0)+((c[La>>2]|0)*1168|0);o=c[Ma>>2]|0;t=(c[Ma>>2]|0)+(c[Ia>>2]<<2)|0;q=o+128|0;do{c[o>>2]=c[t>>2];o=o+4|0;t=t+4|0}while((o|0)<(q|0));c[La>>2]=(c[La>>2]|0)+1}na(c[Ka>>2]|0);i=Na;return}function ze(a){a=a|0;var b=0,d=0;b=i;i=i+16|0;d=b;c[d>>2]=a;c[(c[d>>2]|0)+4168>>2]=c[(c[d>>2]|0)+2328>>2]<<7;c[(c[d>>2]|0)+4168+72>>2]=65536;c[(c[d>>2]|0)+4168+72+4>>2]=65536;c[(c[d>>2]|0)+4168+88>>2]=20;c[(c[d>>2]|0)+4168+84>>2]=2;i=b;return}function Ae(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0;m=i;i=i+32|0;h=m+16|0;g=m+12|0;j=m+8|0;k=m+4|0;l=m;c[h>>2]=a;c[g>>2]=b;c[j>>2]=d;c[k>>2]=e;c[l>>2]=f;if((c[(c[h>>2]|0)+2316>>2]|0)!=(c[(c[h>>2]|0)+4168+80>>2]|0)){ze(c[h>>2]|0);c[(c[h>>2]|0)+4168+80>>2]=c[(c[h>>2]|0)+2316>>2]}d=c[h>>2]|0;f=c[g>>2]|0;if(c[k>>2]|0){Be(d,f,c[j>>2]|0,c[l>>2]|0);l=(c[h>>2]|0)+4160|0;c[l>>2]=(c[l>>2]|0)+1;i=m;return}else{Ke(d,f);i=m;return}}function Be(a,d,e,f){a=a|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0;N=i;i=i+176|0;z=N+120|0;A=N+116|0;B=N+112|0;m=N+108|0;F=N+104|0;G=N+100|0;u=N+96|0;H=N+92|0;r=N+88|0;y=N+84|0;k=N+80|0;l=N+76|0;L=N+72|0;q=N+68|0;w=N+64|0;t=N+60|0;h=N+56|0;j=N+52|0;x=N+48|0;v=N+44|0;E=N+40|0;o=N+36|0;K=N+160|0;n=N+32|0;M=N+28|0;D=N+128|0;J=N+24|0;I=N+16|0;C=N+8|0;s=N+4|0;p=N;c[z>>2]=a;c[A>>2]=d;c[B>>2]=e;c[m>>2]=f;c[J>>2]=(c[z>>2]|0)+4168;g=(c[(c[z>>2]|0)+2336>>2]|0)+(c[(c[z>>2]|0)+2328>>2]|0)|0;c[C>>2]=ia()|0;d=i;i=i+((1*(g<<2)|0)+15&-16)|0;g=i;i=i+((1*(c[(c[z>>2]|0)+2336>>2]<<1)|0)+15&-16)|0;c[I>>2]=c[(c[J>>2]|0)+72>>2]>>6;c[I+4>>2]=c[(c[J>>2]|0)+72+4>>2]>>6;if(c[(c[z>>2]|0)+2376>>2]|0){e=(c[J>>2]|0)+14|0;a=e+32|0;do{b[e>>1]=0;e=e+2|0}while((e|0)<(a|0))}Ce(h,k,j,l,(c[z>>2]|0)+4|0,I,c[(c[z>>2]|0)+2332>>2]|0,c[(c[z>>2]|0)+2324>>2]|0);f=c[(c[J>>2]|0)+84>>2]|0;if((c[h>>2]>>c[l>>2]|0)<(c[j>>2]>>c[k>>2]|0)){l=De(0,(_(f-1|0,c[(c[J>>2]|0)+88>>2]|0)|0)-128|0)|0;c[x>>2]=(c[z>>2]|0)+4+(l<<2)}else{l=De(0,(_(f,c[(c[J>>2]|0)+88>>2]|0)|0)-128|0)|0;c[x>>2]=(c[z>>2]|0)+4+(l<<2)}c[n>>2]=(c[J>>2]|0)+4;b[K>>1]=b[(c[J>>2]|0)+56>>1]|0;c[q>>2]=b[24440+((Ee(1,c[(c[z>>2]|0)+4160>>2]|0)|0)<<1)>>1];l=(c[(c[z>>2]|0)+4164>>2]|0)==2;f=Ee(1,c[(c[z>>2]|0)+4160>>2]|0)|0;if(l)c[w>>2]=b[24444+(f<<1)>>1];else c[w>>2]=b[24448+(f<<1)>>1];Pf((c[J>>2]|0)+14|0,c[(c[z>>2]|0)+2340>>2]|0,64881);pj(D|0,(c[J>>2]|0)+14|0,c[(c[z>>2]|0)+2340>>2]<<1|0)|0;do if(!(c[(c[z>>2]|0)+4160>>2]|0)){b[K>>1]=16384;if((c[(c[z>>2]|0)+4164>>2]|0)!=2){c[s>>2]=Yf((c[J>>2]|0)+14|0,c[(c[z>>2]|0)+2340>>2]|0)|0;c[p>>2]=Ge(134217728,c[s>>2]|0)|0;c[p>>2]=He(4194304,c[p>>2]|0)|0;c[p>>2]=c[p>>2]<<3;s=_(c[p>>2]>>16,(c[w>>2]&65535)<<16>>16)|0;c[w>>2]=s+((_(c[p>>2]&65535,(c[w>>2]&65535)<<16>>16)|0)>>16)>>14;break}c[F>>2]=0;while(1){if((c[F>>2]|0)>=5)break;b[K>>1]=(b[K>>1]|0)-(b[(c[n>>2]|0)+(c[F>>2]<<1)>>1]|0);c[F>>2]=(c[F>>2]|0)+1}b[K>>1]=Fe(3277,b[K>>1]|0)|0;b[K>>1]=(_(b[K>>1]|0,b[(c[J>>2]|0)+68>>1]|0)|0)>>14}while(0);c[L>>2]=c[(c[J>>2]|0)+52>>2];c[H>>2]=(c[c[J>>2]>>2]>>7)+1>>1;c[y>>2]=c[(c[z>>2]|0)+2336>>2];c[r>>2]=(c[(c[z>>2]|0)+2336>>2]|0)-(c[H>>2]|0)-(c[(c[z>>2]|0)+2340>>2]|0)-2;Xf(g+(c[r>>2]<<1)|0,(c[z>>2]|0)+1348+(c[r>>2]<<1)|0,D,(c[(c[z>>2]|0)+2336>>2]|0)-(c[r>>2]|0)|0,c[(c[z>>2]|0)+2340>>2]|0,c[m>>2]|0);c[t>>2]=Ie(c[(c[J>>2]|0)+72+4>>2]|0,46)|0;c[t>>2]=(c[t>>2]|0)<1073741823?c[t>>2]|0:1073741823;c[F>>2]=(c[r>>2]|0)+(c[(c[z>>2]|0)+2340>>2]|0);while(1){if((c[F>>2]|0)>=(c[(c[z>>2]|0)+2336>>2]|0))break;s=_(c[t>>2]>>16,b[g+(c[F>>2]<<1)>>1]|0)|0;s=s+((_(c[t>>2]&65535,b[g+(c[F>>2]<<1)>>1]|0)|0)>>16)|0;c[d+(c[F>>2]<<2)>>2]=s;c[F>>2]=(c[F>>2]|0)+1}c[u>>2]=0;while(1){if((c[u>>2]|0)>=(c[(c[z>>2]|0)+2324>>2]|0))break;c[v>>2]=d+((c[y>>2]|0)-(c[H>>2]|0)+2<<2);c[F>>2]=0;while(1){if((c[F>>2]|0)>=(c[(c[z>>2]|0)+2332>>2]|0))break;c[o>>2]=2;t=_(c[c[v>>2]>>2]>>16,b[c[n>>2]>>1]|0)|0;c[o>>2]=(c[o>>2]|0)+(t+((_(c[c[v>>2]>>2]&65535,b[c[n>>2]>>1]|0)|0)>>16));t=_(c[(c[v>>2]|0)+-4>>2]>>16,b[(c[n>>2]|0)+2>>1]|0)|0;c[o>>2]=(c[o>>2]|0)+(t+((_(c[(c[v>>2]|0)+-4>>2]&65535,b[(c[n>>2]|0)+2>>1]|0)|0)>>16));t=_(c[(c[v>>2]|0)+-8>>2]>>16,b[(c[n>>2]|0)+4>>1]|0)|0;c[o>>2]=(c[o>>2]|0)+(t+((_(c[(c[v>>2]|0)+-8>>2]&65535,b[(c[n>>2]|0)+4>>1]|0)|0)>>16));t=_(c[(c[v>>2]|0)+-12>>2]>>16,b[(c[n>>2]|0)+6>>1]|0)|0;c[o>>2]=(c[o>>2]|0)+(t+((_(c[(c[v>>2]|0)+-12>>2]&65535,b[(c[n>>2]|0)+6>>1]|0)|0)>>16));t=_(c[(c[v>>2]|0)+-16>>2]>>16,b[(c[n>>2]|0)+8>>1]|0)|0;c[o>>2]=(c[o>>2]|0)+(t+((_(c[(c[v>>2]|0)+-16>>2]&65535,b[(c[n>>2]|0)+8>>1]|0)|0)>>16));c[v>>2]=(c[v>>2]|0)+4;c[L>>2]=907633515+(_(c[L>>2]|0,196314165)|0);c[r>>2]=c[L>>2]>>25&127;t=_(c[(c[x>>2]|0)+(c[r>>2]<<2)>>2]>>16,b[K>>1]|0)|0;t=(c[o>>2]|0)+(t+((_(c[(c[x>>2]|0)+(c[r>>2]<<2)>>2]&65535,b[K>>1]|0)|0)>>16))<<2;c[d+(c[y>>2]<<2)>>2]=t;c[y>>2]=(c[y>>2]|0)+1;c[F>>2]=(c[F>>2]|0)+1}c[G>>2]=0;while(1){if((c[G>>2]|0)>=5)break;t=(_((c[q>>2]&65535)<<16>>16,b[(c[n>>2]|0)+(c[G>>2]<<1)>>1]|0)|0)>>15&65535;b[(c[n>>2]|0)+(c[G>>2]<<1)>>1]=t;c[G>>2]=(c[G>>2]|0)+1}b[K>>1]=(_(b[K>>1]|0,(c[w>>2]&65535)<<16>>16)|0)>>15;c[c[J>>2]>>2]=(c[c[J>>2]>>2]|0)+(((c[c[J>>2]>>2]>>16)*655|0)+((c[c[J>>2]>>2]&65535)*655>>16));t=Ge(c[c[J>>2]>>2]|0,((c[(c[z>>2]|0)+2316>>2]&65535)<<16>>16)*18<<8)|0;c[c[J>>2]>>2]=t;c[H>>2]=(c[c[J>>2]>>2]>>7)+1>>1;c[u>>2]=(c[u>>2]|0)+1}c[M>>2]=d+((c[(c[z>>2]|0)+2336>>2]|0)-16<<2);e=c[M>>2]|0;f=(c[z>>2]|0)+1284|0;a=e+64|0;do{c[e>>2]=c[f>>2];e=e+4|0;f=f+4|0}while((e|0)<(a|0));c[F>>2]=0;while(1){f=c[z>>2]|0;if((c[F>>2]|0)>=(c[(c[z>>2]|0)+2328>>2]|0))break;c[E>>2]=c[f+2340>>2]>>1;y=_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)-1<<2)>>2]>>16,b[D>>1]|0)|0;c[E>>2]=(c[E>>2]|0)+(y+((_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)-1<<2)>>2]&65535,b[D>>1]|0)|0)>>16));y=_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)-2<<2)>>2]>>16,b[D+2>>1]|0)|0;c[E>>2]=(c[E>>2]|0)+(y+((_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)-2<<2)>>2]&65535,b[D+2>>1]|0)|0)>>16));y=_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)-3<<2)>>2]>>16,b[D+4>>1]|0)|0;c[E>>2]=(c[E>>2]|0)+(y+((_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)-3<<2)>>2]&65535,b[D+4>>1]|0)|0)>>16));y=_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)-4<<2)>>2]>>16,b[D+6>>1]|0)|0;c[E>>2]=(c[E>>2]|0)+(y+((_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)-4<<2)>>2]&65535,b[D+6>>1]|0)|0)>>16));y=_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)-5<<2)>>2]>>16,b[D+8>>1]|0)|0;c[E>>2]=(c[E>>2]|0)+(y+((_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)-5<<2)>>2]&65535,b[D+8>>1]|0)|0)>>16));y=_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)-6<<2)>>2]>>16,b[D+10>>1]|0)|0;c[E>>2]=(c[E>>2]|0)+(y+((_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)-6<<2)>>2]&65535,b[D+10>>1]|0)|0)>>16));y=_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)-7<<2)>>2]>>16,b[D+12>>1]|0)|0;c[E>>2]=(c[E>>2]|0)+(y+((_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)-7<<2)>>2]&65535,b[D+12>>1]|0)|0)>>16));y=_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)-8<<2)>>2]>>16,b[D+14>>1]|0)|0;c[E>>2]=(c[E>>2]|0)+(y+((_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)-8<<2)>>2]&65535,b[D+14>>1]|0)|0)>>16));y=_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)-9<<2)>>2]>>16,b[D+16>>1]|0)|0;c[E>>2]=(c[E>>2]|0)+(y+((_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)-9<<2)>>2]&65535,b[D+16>>1]|0)|0)>>16));y=_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)-10<<2)>>2]>>16,b[D+18>>1]|0)|0;c[E>>2]=(c[E>>2]|0)+(y+((_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)-10<<2)>>2]&65535,b[D+18>>1]|0)|0)>>16));c[G>>2]=10;while(1){if((c[G>>2]|0)>=(c[(c[z>>2]|0)+2340>>2]|0))break;y=_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)-(c[G>>2]|0)-1<<2)>>2]>>16,b[D+(c[G>>2]<<1)>>1]|0)|0;c[E>>2]=(c[E>>2]|0)+(y+((_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)-(c[G>>2]|0)-1<<2)>>2]&65535,b[D+(c[G>>2]<<1)>>1]|0)|0)>>16));c[G>>2]=(c[G>>2]|0)+1}c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]=(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]|0)+(c[E>>2]<<4);y=_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]>>16,(c[I+4>>2]&65535)<<16>>16)|0;y=y+((_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]&65535,(c[I+4>>2]&65535)<<16>>16)|0)>>16)|0;if(((y+(_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]|0,(c[I+4>>2]>>15)+1>>1)|0)>>7)+1>>1|0)<=32767){y=_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]>>16,(c[I+4>>2]&65535)<<16>>16)|0;y=y+((_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]&65535,(c[I+4>>2]&65535)<<16>>16)|0)>>16)|0;if(((y+(_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]|0,(c[I+4>>2]>>15)+1>>1)|0)>>7)+1>>1|0)<-32768)f=-32768;else{f=_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]>>16,(c[I+4>>2]&65535)<<16>>16)|0;f=f+((_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]&65535,(c[I+4>>2]&65535)<<16>>16)|0)>>16)|0;f=(f+(_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]|0,(c[I+4>>2]>>15)+1>>1)|0)>>7)+1>>1}}else f=32767;if((f|0)<=32767){y=_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]>>16,(c[I+4>>2]&65535)<<16>>16)|0;y=y+((_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]&65535,(c[I+4>>2]&65535)<<16>>16)|0)>>16)|0;if(((y+(_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]|0,(c[I+4>>2]>>15)+1>>1)|0)>>7)+1>>1|0)<=32767){y=_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]>>16,(c[I+4>>2]&65535)<<16>>16)|0;y=y+((_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]&65535,(c[I+4>>2]&65535)<<16>>16)|0)>>16)|0;if(((y+(_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]|0,(c[I+4>>2]>>15)+1>>1)|0)>>7)+1>>1|0)<-32768)f=-32768;else{f=_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]>>16,(c[I+4>>2]&65535)<<16>>16)|0;f=f+((_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]&65535,(c[I+4>>2]&65535)<<16>>16)|0)>>16)|0;f=(f+(_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]|0,(c[I+4>>2]>>15)+1>>1)|0)>>7)+1>>1}}else f=32767;if((f|0)>=-32768){y=_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]>>16,(c[I+4>>2]&65535)<<16>>16)|0;y=y+((_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]&65535,(c[I+4>>2]&65535)<<16>>16)|0)>>16)|0;if(((y+(_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]|0,(c[I+4>>2]>>15)+1>>1)|0)>>7)+1>>1|0)<=32767){y=_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]>>16,(c[I+4>>2]&65535)<<16>>16)|0;y=y+((_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]&65535,(c[I+4>>2]&65535)<<16>>16)|0)>>16)|0;if(((y+(_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]|0,(c[I+4>>2]>>15)+1>>1)|0)>>7)+1>>1|0)<-32768)f=-32768;else{f=_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]>>16,(c[I+4>>2]&65535)<<16>>16)|0;f=f+((_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]&65535,(c[I+4>>2]&65535)<<16>>16)|0)>>16)|0;f=(f+(_(c[(c[M>>2]|0)+(16+(c[F>>2]|0)<<2)>>2]|0,(c[I+4>>2]>>15)+1>>1)|0)>>7)+1>>1}}else f=32767}else f=-32768}else f=32767;b[(c[B>>2]|0)+(c[F>>2]<<1)>>1]=f;c[F>>2]=(c[F>>2]|0)+1}e=f+1284|0;f=(c[M>>2]|0)+(c[(c[z>>2]|0)+2328>>2]<<2)|0;a=e+64|0;do{c[e>>2]=c[f>>2];e=e+4|0;f=f+4|0}while((e|0)<(a|0));c[(c[J>>2]|0)+52>>2]=c[L>>2];b[(c[J>>2]|0)+56>>1]=b[K>>1]|0;c[F>>2]=0;while(1){if((c[F>>2]|0)>=4)break;c[(c[A>>2]|0)+(c[F>>2]<<2)>>2]=c[H>>2];c[F>>2]=(c[F>>2]|0)+1}na(c[C>>2]|0);i=N;return}function Ce(a,d,e,f,g,h,j,k){a=a|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;k=k|0;var l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0;x=i;i=i+48|0;l=x+44|0;m=x+40|0;n=x+36|0;o=x+32|0;p=x+28|0;q=x+24|0;r=x+20|0;s=x+16|0;v=x+12|0;w=x+8|0;u=x+4|0;t=x;c[l>>2]=a;c[m>>2]=d;c[n>>2]=e;c[o>>2]=f;c[p>>2]=g;c[q>>2]=h;c[r>>2]=j;c[s>>2]=k;j=c[r>>2]<<1;c[t>>2]=ia()|0;a=i;i=i+((1*(j<<1)|0)+15&-16)|0;c[u>>2]=a;c[w>>2]=0;while(1){if((c[w>>2]|0)>=2)break;c[v>>2]=0;while(1){if((c[v>>2]|0)>=(c[r>>2]|0))break;j=(c[v>>2]|0)+(_((c[w>>2]|0)+(c[s>>2]|0)-2|0,c[r>>2]|0)|0)|0;j=_(c[(c[p>>2]|0)+(j<<2)>>2]>>16,(c[(c[q>>2]|0)+(c[w>>2]<<2)>>2]&65535)<<16>>16)|0;f=(c[v>>2]|0)+(_((c[w>>2]|0)+(c[s>>2]|0)-2|0,c[r>>2]|0)|0)|0;f=j+((_(c[(c[p>>2]|0)+(f<<2)>>2]&65535,(c[(c[q>>2]|0)+(c[w>>2]<<2)>>2]&65535)<<16>>16)|0)>>16)|0;j=(c[v>>2]|0)+(_((c[w>>2]|0)+(c[s>>2]|0)-2|0,c[r>>2]|0)|0)|0;if((f+(_(c[(c[p>>2]|0)+(j<<2)>>2]|0,(c[(c[q>>2]|0)+(c[w>>2]<<2)>>2]>>15)+1>>1)|0)>>8|0)<=32767){j=(c[v>>2]|0)+(_((c[w>>2]|0)+(c[s>>2]|0)-2|0,c[r>>2]|0)|0)|0;j=_(c[(c[p>>2]|0)+(j<<2)>>2]>>16,(c[(c[q>>2]|0)+(c[w>>2]<<2)>>2]&65535)<<16>>16)|0;f=(c[v>>2]|0)+(_((c[w>>2]|0)+(c[s>>2]|0)-2|0,c[r>>2]|0)|0)|0;f=j+((_(c[(c[p>>2]|0)+(f<<2)>>2]&65535,(c[(c[q>>2]|0)+(c[w>>2]<<2)>>2]&65535)<<16>>16)|0)>>16)|0;j=(c[v>>2]|0)+(_((c[w>>2]|0)+(c[s>>2]|0)-2|0,c[r>>2]|0)|0)|0;if((f+(_(c[(c[p>>2]|0)+(j<<2)>>2]|0,(c[(c[q>>2]|0)+(c[w>>2]<<2)>>2]>>15)+1>>1)|0)>>8|0)<-32768)e=-32768;else{e=(c[v>>2]|0)+(_((c[w>>2]|0)+(c[s>>2]|0)-2|0,c[r>>2]|0)|0)|0;e=_(c[(c[p>>2]|0)+(e<<2)>>2]>>16,(c[(c[q>>2]|0)+(c[w>>2]<<2)>>2]&65535)<<16>>16)|0;j=(c[v>>2]|0)+(_((c[w>>2]|0)+(c[s>>2]|0)-2|0,c[r>>2]|0)|0)|0;j=e+((_(c[(c[p>>2]|0)+(j<<2)>>2]&65535,(c[(c[q>>2]|0)+(c[w>>2]<<2)>>2]&65535)<<16>>16)|0)>>16)|0;e=(c[v>>2]|0)+(_((c[w>>2]|0)+(c[s>>2]|0)-2|0,c[r>>2]|0)|0)|0;e=j+(_(c[(c[p>>2]|0)+(e<<2)>>2]|0,(c[(c[q>>2]|0)+(c[w>>2]<<2)>>2]>>15)+1>>1)|0)>>8}}else e=32767;b[(c[u>>2]|0)+(c[v>>2]<<1)>>1]=e;c[v>>2]=(c[v>>2]|0)+1}c[u>>2]=(c[u>>2]|0)+(c[r>>2]<<1);c[w>>2]=(c[w>>2]|0)+1}wg(c[l>>2]|0,c[m>>2]|0,a,c[r>>2]|0);wg(c[n>>2]|0,c[o>>2]|0,a+(c[r>>2]<<1)|0,c[r>>2]|0);na(c[t>>2]|0);i=x;return}function De(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)>(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function Ee(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)<(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function Fe(a,c){a=a|0;c=c|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+2|0;d=f;b[e>>1]=a;b[d>>1]=c;i=f;return ((b[e>>1]|0)>(b[d>>1]|0)?b[e>>1]|0:b[d>>1]|0)&65535|0}function Ge(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)<(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function He(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)>(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function Ie(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0;h=i;i=i+48|0;f=h+32|0;n=h+28|0;d=h+24|0;j=h+20|0;g=h+16|0;k=h+12|0;m=h+8|0;l=h+4|0;e=h;c[n>>2]=a;c[d>>2]=b;b=c[n>>2]|0;c[j>>2]=(Je((c[n>>2]|0)>0?b:0-b|0)|0)-1;c[m>>2]=c[n>>2]<<c[j>>2];c[k>>2]=536870911/(c[m>>2]>>16|0)|0;c[e>>2]=c[k>>2]<<16;b=_(c[m>>2]>>16,(c[k>>2]&65535)<<16>>16)|0;c[l>>2]=536870912-(b+((_(c[m>>2]&65535,(c[k>>2]&65535)<<16>>16)|0)>>16))<<3;b=_(c[l>>2]>>16,(c[k>>2]&65535)<<16>>16)|0;b=(c[e>>2]|0)+(b+((_(c[l>>2]&65535,(c[k>>2]&65535)<<16>>16)|0)>>16))|0;c[e>>2]=b+(_(c[l>>2]|0,(c[k>>2]>>15)+1>>1)|0);c[g>>2]=61-(c[j>>2]|0)-(c[d>>2]|0);b=c[g>>2]|0;if((c[g>>2]|0)>0)if((b|0)<32){c[f>>2]=c[e>>2]>>c[g>>2];n=c[f>>2]|0;i=h;return n|0}else{c[f>>2]=0;n=c[f>>2]|0;i=h;return n|0}a=c[e>>2]|0;d=0-(c[g>>2]|0)|0;do if((-2147483648>>0-b|0)>(2147483647>>0-(c[g>>2]|0)|0)){if((a|0)>(-2147483648>>d|0)){b=-2147483648>>0-(c[g>>2]|0);break}if((c[e>>2]|0)<(2147483647>>0-(c[g>>2]|0)|0)){b=2147483647>>0-(c[g>>2]|0);break}else{b=c[e>>2]|0;break}}else{if((a|0)>(2147483647>>d|0)){b=2147483647>>0-(c[g>>2]|0);break}if((c[e>>2]|0)<(-2147483648>>0-(c[g>>2]|0)|0)){b=-2147483648>>0-(c[g>>2]|0);break}else{b=c[e>>2]|0;break}}while(0);c[f>>2]=b<<0-(c[g>>2]|0);n=c[f>>2]|0;i=h;return n|0}function Je(a){a=a|0;var b=0,d=0;d=i;i=i+16|0;b=d;c[b>>2]=a;if(!(c[b>>2]|0)){a=32;i=d;return a|0}a=32-(32-(aa(c[b>>2]|0)|0))|0;i=d;return a|0}function Ke(d,e){d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;r=i;i=i+48|0;f=r+40|0;g=r+36|0;h=r+32|0;o=r+28|0;j=r+24|0;k=r+20|0;l=r+16|0;m=r+12|0;p=r+8|0;n=r+4|0;q=r;c[f>>2]=d;c[g>>2]=e;c[l>>2]=(c[f>>2]|0)+4168;c[(c[f>>2]|0)+4164>>2]=a[(c[f>>2]|0)+2736+29>>0];c[h>>2]=0;a:do if((a[(c[f>>2]|0)+2736+29>>0]|0)==2){c[k>>2]=0;while(1){e=_(c[k>>2]|0,c[(c[f>>2]|0)+2332>>2]|0)|0;if((e|0)>=(c[(c[g>>2]|0)+((c[(c[f>>2]|0)+2324>>2]|0)-1<<2)>>2]|0))break;if((c[k>>2]|0)==(c[(c[f>>2]|0)+2324>>2]|0))break;c[o>>2]=0;c[j>>2]=0;while(1){if((c[j>>2]|0)>=5)break;c[o>>2]=(c[o>>2]|0)+(b[(c[g>>2]|0)+96+((((c[(c[f>>2]|0)+2324>>2]|0)-1-(c[k>>2]|0)|0)*5|0)+(c[j>>2]|0)<<1)>>1]|0);c[j>>2]=(c[j>>2]|0)+1}if((c[o>>2]|0)>(c[h>>2]|0)){c[h>>2]=c[o>>2];e=(c[l>>2]|0)+4|0;d=(c[g>>2]|0)+96+((((c[(c[f>>2]|0)+2324>>2]|0)-1-(c[k>>2]|0)&65535)<<16>>16)*5<<1)|0;b[e>>1]=b[d>>1]|0;b[e+2>>1]=b[d+2>>1]|0;b[e+4>>1]=b[d+4>>1]|0;b[e+6>>1]=b[d+6>>1]|0;b[e+8>>1]=b[d+8>>1]|0;c[c[l>>2]>>2]=c[(c[g>>2]|0)+((c[(c[f>>2]|0)+2324>>2]|0)-1-(c[k>>2]|0)<<2)>>2]<<8}c[k>>2]=(c[k>>2]|0)+1}o=(c[l>>2]|0)+4|0;c[o>>2]=0;c[o+4>>2]=0;b[o+8>>1]=0;b[(c[l>>2]|0)+4+4>>1]=c[h>>2];if((c[h>>2]|0)<11469){c[p>>2]=11744256;c[m>>2]=(c[p>>2]|0)/(((c[h>>2]|0)>1?c[h>>2]|0:1)|0)|0;c[j>>2]=0;while(1){if((c[j>>2]|0)>=5)break a;q=(_(b[(c[l>>2]|0)+4+(c[j>>2]<<1)>>1]|0,(c[m>>2]&65535)<<16>>16)|0)>>10&65535;b[(c[l>>2]|0)+4+(c[j>>2]<<1)>>1]=q;c[j>>2]=(c[j>>2]|0)+1}}if((c[h>>2]|0)>15565){c[q>>2]=255016960;c[n>>2]=(c[q>>2]|0)/(((c[h>>2]|0)>1?c[h>>2]|0:1)|0)|0;c[j>>2]=0;while(1){if((c[j>>2]|0)>=5)break a;q=(_(b[(c[l>>2]|0)+4+(c[j>>2]<<1)>>1]|0,(c[n>>2]&65535)<<16>>16)|0)>>14&65535;b[(c[l>>2]|0)+4+(c[j>>2]<<1)>>1]=q;c[j>>2]=(c[j>>2]|0)+1}}}else{c[c[l>>2]>>2]=((c[(c[f>>2]|0)+2316>>2]&65535)<<16>>16)*18<<8;q=(c[l>>2]|0)+4|0;c[q>>2]=0;c[q+4>>2]=0;b[q+8>>1]=0}while(0);pj((c[l>>2]|0)+14|0,(c[g>>2]|0)+32+32|0,c[(c[f>>2]|0)+2340>>2]<<1|0)|0;b[(c[l>>2]|0)+68>>1]=c[(c[g>>2]|0)+136>>2];q=(c[l>>2]|0)+72|0;p=(c[g>>2]|0)+16+((c[(c[f>>2]|0)+2324>>2]|0)-2<<2)|0;c[q>>2]=c[p>>2];c[q+4>>2]=c[p+4>>2];c[(c[l>>2]|0)+88>>2]=c[(c[f>>2]|0)+2332>>2];c[(c[l>>2]|0)+84>>2]=c[(c[f>>2]|0)+2324>>2];i=r;return}function Le(a,d,e){a=a|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;r=i;i=i+48|0;f=r+40|0;g=r+36|0;h=r+32|0;o=r+28|0;l=r+24|0;k=r+20|0;p=r+16|0;m=r+12|0;j=r+8|0;n=r+4|0;q=r;c[f>>2]=a;c[g>>2]=d;c[h>>2]=e;c[p>>2]=(c[f>>2]|0)+4168;if(c[(c[f>>2]|0)+4160>>2]|0){wg((c[p>>2]|0)+60|0,(c[p>>2]|0)+64|0,c[g>>2]|0,c[h>>2]|0);c[(c[p>>2]|0)+48>>2]=1;i=r;return}a:do if(c[(c[f>>2]|0)+4168+48>>2]|0){wg(k,l,c[g>>2]|0,c[h>>2]|0);if((c[l>>2]|0)<=(c[(c[p>>2]|0)+64>>2]|0)){if((c[l>>2]|0)<(c[(c[p>>2]|0)+64>>2]|0))c[k>>2]=c[k>>2]>>(c[(c[p>>2]|0)+64>>2]|0)-(c[l>>2]|0)}else c[(c[p>>2]|0)+60>>2]=c[(c[p>>2]|0)+60>>2]>>(c[l>>2]|0)-(c[(c[p>>2]|0)+64>>2]|0);if((c[k>>2]|0)>(c[(c[p>>2]|0)+60>>2]|0)){c[j>>2]=Je(c[(c[p>>2]|0)+60>>2]|0)|0;c[j>>2]=(c[j>>2]|0)-1;c[(c[p>>2]|0)+60>>2]=c[(c[p>>2]|0)+60>>2]<<c[j>>2];a=c[k>>2]|0;c[k>>2]=a>>(He(24-(c[j>>2]|0)|0,0)|0);c[m>>2]=(c[(c[p>>2]|0)+60>>2]|0)/(((c[k>>2]|0)>1?c[k>>2]|0:1)|0)|0;c[n>>2]=(Me(c[m>>2]|0)|0)<<4;c[q>>2]=(65536-(c[n>>2]|0)|0)/(c[h>>2]|0)|0;c[q>>2]=c[q>>2]<<2;c[o>>2]=0;while(1){if((c[o>>2]|0)>=(c[h>>2]|0))break a;a=_(c[n>>2]>>16,b[(c[g>>2]|0)+(c[o>>2]<<1)>>1]|0)|0;a=a+((_(c[n>>2]&65535,b[(c[g>>2]|0)+(c[o>>2]<<1)>>1]|0)|0)>>16)&65535;b[(c[g>>2]|0)+(c[o>>2]<<1)>>1]=a;c[n>>2]=(c[n>>2]|0)+(c[q>>2]|0);if((c[n>>2]|0)>65536)break a;c[o>>2]=(c[o>>2]|0)+1}}}while(0);c[(c[p>>2]|0)+48>>2]=0;i=r;return}function Me(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0;h=i;i=i+32|0;b=h+16|0;d=h+12|0;g=h+8|0;f=h+4|0;e=h;c[d>>2]=a;if((c[d>>2]|0)<=0){c[b>>2]=0;g=c[b>>2]|0;i=h;return g|0}Ne(c[d>>2]|0,f,e);if(c[f>>2]&1|0)c[g>>2]=32768;else c[g>>2]=46214;c[g>>2]=c[g>>2]>>(c[f>>2]>>1);a=_(c[g>>2]>>16,(((c[e>>2]&65535)<<16>>16)*213&65535)<<16>>16)|0;c[g>>2]=(c[g>>2]|0)+(a+((_(c[g>>2]&65535,(((c[e>>2]&65535)<<16>>16)*213&65535)<<16>>16)|0)>>16));c[b>>2]=c[g>>2];g=c[b>>2]|0;i=h;return g|0}function Ne(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0;e=i;i=i+16|0;h=e+12|0;j=e+8|0;f=e+4|0;g=e;c[h>>2]=a;c[j>>2]=b;c[f>>2]=d;c[g>>2]=Je(c[h>>2]|0)|0;c[c[j>>2]>>2]=c[g>>2];b=(Oe(c[h>>2]|0,24-(c[g>>2]|0)|0)|0)&127;c[c[f>>2]>>2]=b;i=e;return}function Oe(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0;k=i;i=i+32|0;e=k+20|0;d=k+16|0;f=k+12|0;j=k+8|0;h=k+4|0;g=k;c[d>>2]=a;c[f>>2]=b;c[j>>2]=c[d>>2];c[h>>2]=c[f>>2];c[g>>2]=0-(c[f>>2]|0);if(!(c[f>>2]|0)){c[e>>2]=c[d>>2];j=c[e>>2]|0;i=k;return j|0}d=c[j>>2]|0;if((c[f>>2]|0)<0){c[e>>2]=d<<c[g>>2]|(c[j>>2]|0)>>>(32-(c[g>>2]|0)|0);j=c[e>>2]|0;i=k;return j|0}else{c[e>>2]=d<<32-(c[h>>2]|0)|(c[j>>2]|0)>>>(c[h>>2]|0);j=c[e>>2]|0;i=k;return j|0}return 0}function Pe(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0;d=i;i=i+80|0;g=d+68|0;f=d+64|0;e=d+32|0;h=d+16|0;j=d+8|0;k=d;c[g>>2]=a;c[f>>2]=b;Qe(e,c[f>>2]|0,8);Qe(h,e,4);Qe(j,h,2);Qe(k,j,1);Re(c[g>>2]|0,c[j>>2]|0,c[k>>2]|0,30015);Re(c[g>>2]|0,c[h>>2]|0,c[j>>2]|0,29863);Re(c[g>>2]|0,c[e>>2]|0,c[h>>2]|0,29711);Re(c[g>>2]|0,c[c[f>>2]>>2]|0,c[e>>2]|0,29559);Re(c[g>>2]|0,c[(c[f>>2]|0)+8>>2]|0,c[e+4>>2]|0,29559);Re(c[g>>2]|0,c[e+8>>2]|0,c[h+4>>2]|0,29711);Re(c[g>>2]|0,c[(c[f>>2]|0)+16>>2]|0,c[e+8>>2]|0,29559);Re(c[g>>2]|0,c[(c[f>>2]|0)+24>>2]|0,c[e+12>>2]|0,29559);Re(c[g>>2]|0,c[h+8>>2]|0,c[j+4>>2]|0,29863);Re(c[g>>2]|0,c[e+16>>2]|0,c[h+8>>2]|0,29711);Re(c[g>>2]|0,c[(c[f>>2]|0)+32>>2]|0,c[e+16>>2]|0,29559);Re(c[g>>2]|0,c[(c[f>>2]|0)+40>>2]|0,c[e+20>>2]|0,29559);Re(c[g>>2]|0,c[e+24>>2]|0,c[h+12>>2]|0,29711);Re(c[g>>2]|0,c[(c[f>>2]|0)+48>>2]|0,c[e+24>>2]|0,29559);Re(c[g>>2]|0,c[(c[f>>2]|0)+56>>2]|0,c[e+28>>2]|0,29559);i=d;return}function Qe(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0;j=i;i=i+16|0;e=j+12|0;f=j+8|0;g=j+4|0;h=j;c[e>>2]=a;c[f>>2]=b;c[g>>2]=d;c[h>>2]=0;while(1){if((c[h>>2]|0)>=(c[g>>2]|0))break;c[(c[e>>2]|0)+(c[h>>2]<<2)>>2]=(c[(c[f>>2]|0)+(c[h>>2]<<1<<2)>>2]|0)+(c[(c[f>>2]|0)+((c[h>>2]<<1)+1<<2)>>2]|0);c[h>>2]=(c[h>>2]|0)+1}i=j;return}function Re(a,b,e,f){a=a|0;b=b|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0;l=i;i=i+16|0;g=l+12|0;h=l+8|0;j=l+4|0;k=l;c[g>>2]=a;c[h>>2]=b;c[j>>2]=e;c[k>>2]=f;if((c[j>>2]|0)<=0){i=l;return}qc(c[g>>2]|0,c[h>>2]|0,(c[k>>2]|0)+(d[30167+(c[j>>2]|0)>>0]|0)|0,8);i=l;return}function Se(a,d,e){a=a|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0;f=i;i=i+48|0;j=f+8|0;h=f+4|0;m=f;l=f+40|0;k=f+32|0;g=f+16|0;c[j>>2]=a;c[h>>2]=d;c[m>>2]=e;Te(l,l+2|0,c[h>>2]|0,c[m>>2]|0,30015);Te(k,k+2|0,c[h>>2]|0,b[l>>1]|0,29863);Te(g,g+2|0,c[h>>2]|0,b[k>>1]|0,29711);Te(c[j>>2]|0,(c[j>>2]|0)+2|0,c[h>>2]|0,b[g>>1]|0,29559);Te((c[j>>2]|0)+4|0,(c[j>>2]|0)+6|0,c[h>>2]|0,b[g+2>>1]|0,29559);Te(g+4|0,g+6|0,c[h>>2]|0,b[k+2>>1]|0,29711);Te((c[j>>2]|0)+8|0,(c[j>>2]|0)+10|0,c[h>>2]|0,b[g+4>>1]|0,29559);Te((c[j>>2]|0)+12|0,(c[j>>2]|0)+14|0,c[h>>2]|0,b[g+6>>1]|0,29559);Te(k+4|0,k+6|0,c[h>>2]|0,b[l+2>>1]|0,29863);Te(g+8|0,g+10|0,c[h>>2]|0,b[k+4>>1]|0,29711);Te((c[j>>2]|0)+16|0,(c[j>>2]|0)+18|0,c[h>>2]|0,b[g+8>>1]|0,29559);Te((c[j>>2]|0)+20|0,(c[j>>2]|0)+22|0,c[h>>2]|0,b[g+10>>1]|0,29559);Te(g+12|0,g+14|0,c[h>>2]|0,b[k+6>>1]|0,29711);Te((c[j>>2]|0)+24|0,(c[j>>2]|0)+26|0,c[h>>2]|0,b[g+12>>1]|0,29559);Te((c[j>>2]|0)+28|0,(c[j>>2]|0)+30|0,c[h>>2]|0,b[g+14>>1]|0,29559);i=f;return}function Te(a,e,f,g,h){a=a|0;e=e|0;f=f|0;g=g|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0;o=i;i=i+32|0;j=o+16|0;k=o+12|0;l=o+8|0;m=o+4|0;n=o;c[j>>2]=a;c[k>>2]=e;c[l>>2]=f;c[m>>2]=g;c[n>>2]=h;if((c[m>>2]|0)>0){h=(ec(c[l>>2]|0,(c[n>>2]|0)+(d[30167+(c[m>>2]|0)>>0]|0)|0,8)|0)&65535;b[c[j>>2]>>1]=h;b[c[k>>2]>>1]=(c[m>>2]|0)-(b[c[j>>2]>>1]|0);i=o;return}else{b[c[j>>2]>>1]=0;b[c[k>>2]>>1]=0;i=o;return}}function Ue(a){a=a|0;var b=0,d=0,e=0,f=0,g=0;g=i;i=i+16|0;d=g+8|0;e=g+4|0;f=g;c[d>>2]=a;c[f>>2]=0;a=c[d>>2]|0;b=a+112|0;do{c[a>>2]=0;a=a+4|0}while((a|0)<(b|0));c[e>>2]=0;while(1){if((c[e>>2]|0)>=4)break;b=Ve(50/((c[e>>2]|0)+1|0)|0,1)|0;c[(c[d>>2]|0)+92+(c[e>>2]<<2)>>2]=b;c[e>>2]=(c[e>>2]|0)+1}c[e>>2]=0;while(1){if((c[e>>2]|0)>=4)break;c[(c[d>>2]|0)+60+(c[e>>2]<<2)>>2]=(c[(c[d>>2]|0)+92+(c[e>>2]<<2)>>2]|0)*100;c[(c[d>>2]|0)+76+(c[e>>2]<<2)>>2]=2147483647/(c[(c[d>>2]|0)+60+(c[e>>2]<<2)>>2]|0)|0;c[e>>2]=(c[e>>2]|0)+1}c[(c[d>>2]|0)+108>>2]=15;c[e>>2]=0;while(1){if((c[e>>2]|0)>=4)break;c[(c[d>>2]|0)+40+(c[e>>2]<<2)>>2]=25600;c[e>>2]=(c[e>>2]|0)+1}i=g;return c[f>>2]|0}function Ve(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)>(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function We(a,d){a=a|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0;C=i;i=i+144|0;s=C+132|0;F=C+128|0;v=C+124|0;q=C+120|0;p=C+116|0;E=C+112|0;D=C+108|0;k=C+104|0;h=C+100|0;j=C+96|0;w=C+92|0;l=C+88|0;x=C+84|0;m=C+80|0;r=C+76|0;A=C+72|0;e=C+136|0;o=C+56|0;u=C+40|0;B=C+36|0;n=C+32|0;g=C+16|0;z=C+8|0;y=C+4|0;t=C;c[s>>2]=a;c[F>>2]=d;c[z>>2]=0;c[y>>2]=(c[s>>2]|0)+32;c[E>>2]=c[(c[s>>2]|0)+4608>>2]>>1;c[D>>2]=c[(c[s>>2]|0)+4608>>2]>>2;c[k>>2]=c[(c[s>>2]|0)+4608>>2]>>3;c[g>>2]=0;c[g+4>>2]=(c[k>>2]|0)+(c[D>>2]|0);c[g+8>>2]=(c[g+4>>2]|0)+(c[k>>2]|0);c[g+12>>2]=(c[g+8>>2]|0)+(c[D>>2]|0);a=(c[g+12>>2]|0)+(c[E>>2]|0)|0;c[t>>2]=ia()|0;f=i;i=i+((1*(a<<1)|0)+15&-16)|0;Mf(c[F>>2]|0,c[y>>2]|0,f,f+(c[g+12>>2]<<1)|0,c[(c[s>>2]|0)+4608>>2]|0);Mf(f,(c[y>>2]|0)+8|0,f,f+(c[g+8>>2]<<1)|0,c[E>>2]|0);Mf(f,(c[y>>2]|0)+16|0,f,f+(c[g+4>>2]<<1)|0,c[D>>2]|0);b[f+((c[k>>2]|0)-1<<1)>>1]=b[f+((c[k>>2]|0)-1<<1)>>1]>>1;b[e>>1]=b[f+((c[k>>2]|0)-1<<1)>>1]|0;c[l>>2]=(c[k>>2]|0)-1;while(1){if((c[l>>2]|0)<=0)break;b[f+((c[l>>2]|0)-1<<1)>>1]=b[f+((c[l>>2]|0)-1<<1)>>1]>>1;F=f+(c[l>>2]<<1)|0;b[F>>1]=(b[F>>1]|0)-(b[f+((c[l>>2]|0)-1<<1)>>1]|0);c[l>>2]=(c[l>>2]|0)+-1}b[f>>1]=(b[f>>1]|0)-(b[(c[y>>2]|0)+56>>1]|0);b[(c[y>>2]|0)+56>>1]=b[e>>1]|0;c[x>>2]=0;while(1){if((c[x>>2]|0)>=4)break;F=c[(c[s>>2]|0)+4608>>2]|0;c[k>>2]=F>>(Xe(4-(c[x>>2]|0)|0,3)|0);c[h>>2]=c[k>>2]>>2;c[j>>2]=0;c[o+(c[x>>2]<<2)>>2]=c[(c[y>>2]|0)+24+(c[x>>2]<<2)>>2];c[m>>2]=0;while(1){if((c[m>>2]|0)>=4)break;c[r>>2]=0;c[l>>2]=0;while(1){if((c[l>>2]|0)>=(c[h>>2]|0))break;c[n>>2]=b[f+((c[g+(c[x>>2]<<2)>>2]|0)+(c[l>>2]|0)+(c[j>>2]|0)<<1)>>1]>>3;c[r>>2]=(c[r>>2]|0)+(_((c[n>>2]&65535)<<16>>16,(c[n>>2]&65535)<<16>>16)|0);c[l>>2]=(c[l>>2]|0)+1}d=c[o+(c[x>>2]<<2)>>2]|0;a=c[r>>2]|0;if((c[m>>2]|0)<3){if(d+a&-2147483648|0)d=2147483647;else d=(c[o+(c[x>>2]<<2)>>2]|0)+(c[r>>2]|0)|0;c[o+(c[x>>2]<<2)>>2]=d}else{if(d+(a>>1)&-2147483648|0)d=2147483647;else d=(c[o+(c[x>>2]<<2)>>2]|0)+(c[r>>2]>>1)|0;c[o+(c[x>>2]<<2)>>2]=d}c[j>>2]=(c[j>>2]|0)+(c[h>>2]|0);c[m>>2]=(c[m>>2]|0)+1}c[(c[y>>2]|0)+24+(c[x>>2]<<2)>>2]=c[r>>2];c[x>>2]=(c[x>>2]|0)+1}Ye(o,c[y>>2]|0);c[r>>2]=0;c[p>>2]=0;c[x>>2]=0;while(1){if((c[x>>2]|0)>=4)break;c[B>>2]=(c[o+(c[x>>2]<<2)>>2]|0)-(c[(c[y>>2]|0)+60+(c[x>>2]<<2)>>2]|0);d=c[x>>2]|0;if((c[B>>2]|0)>0){a=c[o+(c[x>>2]<<2)>>2]|0;if(!(c[o+(d<<2)>>2]&-8388608))c[u+(c[x>>2]<<2)>>2]=(a<<8|0)/((c[(c[y>>2]|0)+60+(c[x>>2]<<2)>>2]|0)+1|0)|0;else c[u+(c[x>>2]<<2)>>2]=(a|0)/((c[(c[y>>2]|0)+60+(c[x>>2]<<2)>>2]>>8)+1|0)|0;c[w>>2]=(Sf(c[u+(c[x>>2]<<2)>>2]|0)|0)-1024;c[r>>2]=(c[r>>2]|0)+(_((c[w>>2]&65535)<<16>>16,(c[w>>2]&65535)<<16>>16)|0);if((c[B>>2]|0)<1048576){E=(_e(c[B>>2]|0)|0)<<6>>16;E=_(E,(c[w>>2]&65535)<<16>>16)|0;F=(_e(c[B>>2]|0)|0)<<6&65535;c[w>>2]=E+((_(F,(c[w>>2]&65535)<<16>>16)|0)>>16)}F=_(c[17944+(c[x>>2]<<2)>>2]>>16,(c[w>>2]&65535)<<16>>16)|0;c[p>>2]=(c[p>>2]|0)+(F+((_(c[17944+(c[x>>2]<<2)>>2]&65535,(c[w>>2]&65535)<<16>>16)|0)>>16))}else c[u+(d<<2)>>2]=256;c[x>>2]=(c[x>>2]|0)+1}c[r>>2]=(c[r>>2]|0)/4|0;c[q>>2]=((_e(c[r>>2]|0)|0)*3&65535)<<16>>16;c[v>>2]=tg(0+(((c[q>>2]&65535)<<16>>16)*45e3>>16)-128|0)|0;F=(tg(c[p>>2]|0)|0)-16384<<1;c[(c[s>>2]|0)+4744>>2]=F;c[B>>2]=0;c[x>>2]=0;while(1){if((c[x>>2]|0)>=4)break;F=_((c[x>>2]|0)+1|0,(c[o+(c[x>>2]<<2)>>2]|0)-(c[(c[y>>2]|0)+60+(c[x>>2]<<2)>>2]|0)>>4)|0;c[B>>2]=(c[B>>2]|0)+F;c[x>>2]=(c[x>>2]|0)+1}if((c[B>>2]|0)>0){if((c[B>>2]|0)<32768){d=c[B>>2]|0;if((c[(c[s>>2]|0)+4608>>2]|0)==((c[(c[s>>2]|0)+4600>>2]|0)*10|0)){if((d|0)>32767)d=32767;else d=(c[B>>2]|0)<-32768?-32768:c[B>>2]|0;c[B>>2]=d<<16}else{if((d|0)>65535)d=65535;else d=(c[B>>2]|0)<-65536?-65536:c[B>>2]|0;c[B>>2]=d<<15}c[B>>2]=_e(c[B>>2]|0)|0;F=_(32768+(c[B>>2]|0)>>16,(c[v>>2]&65535)<<16>>16)|0;c[v>>2]=F+((_(32768+(c[B>>2]|0)&65535,(c[v>>2]&65535)<<16>>16)|0)>>16)}}else c[v>>2]=c[v>>2]>>1;F=Xe(c[v>>2]>>7,255)|0;c[(c[s>>2]|0)+4556>>2]=F;F=_(c[v>>2]>>16,(c[v>>2]&65535)<<16>>16)|0;c[A>>2]=0+((F+((_(c[v>>2]&65535,(c[v>>2]&65535)<<16>>16)|0)>>16)&65535)<<16>>16<<12>>16);if((c[(c[s>>2]|0)+4608>>2]|0)==((c[(c[s>>2]|0)+4600>>2]|0)*10|0))c[A>>2]=c[A>>2]>>1;c[x>>2]=0;while(1){if((c[x>>2]|0)>=4)break;F=_((c[u+(c[x>>2]<<2)>>2]|0)-(c[(c[y>>2]|0)+40+(c[x>>2]<<2)>>2]|0)>>16,(c[A>>2]&65535)<<16>>16)|0;F=(c[(c[y>>2]|0)+40+(c[x>>2]<<2)>>2]|0)+(F+((_((c[u+(c[x>>2]<<2)>>2]|0)-(c[(c[y>>2]|0)+40+(c[x>>2]<<2)>>2]|0)&65535,(c[A>>2]&65535)<<16>>16)|0)>>16))|0;c[(c[y>>2]|0)+40+(c[x>>2]<<2)>>2]=F;c[w>>2]=((Sf(c[(c[y>>2]|0)+40+(c[x>>2]<<2)>>2]|0)|0)-1024|0)*3;F=tg((c[w>>2]|0)-2048>>4)|0;c[(c[s>>2]|0)+4728+(c[x>>2]<<2)>>2]=F;c[x>>2]=(c[x>>2]|0)+1}F=c[z>>2]|0;na(c[t>>2]|0);i=C;return F|0}function Xe(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)<(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function Ye(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;m=i;i=i+32|0;d=m+28|0;e=m+24|0;h=m+20|0;k=m+16|0;l=m+12|0;g=m+8|0;f=m+4|0;j=m;c[d>>2]=a;c[e>>2]=b;if((c[(c[e>>2]|0)+108>>2]|0)<1e3)c[j>>2]=32767/((c[(c[e>>2]|0)+108>>2]>>4)+1|0)|0;else c[j>>2]=0;c[h>>2]=0;while(1){if((c[h>>2]|0)>=4)break;c[k>>2]=c[(c[e>>2]|0)+60+(c[h>>2]<<2)>>2];if((c[(c[d>>2]|0)+(c[h>>2]<<2)>>2]|0)+(c[(c[e>>2]|0)+92+(c[h>>2]<<2)>>2]|0)&-2147483648|0)a=2147483647;else a=(c[(c[d>>2]|0)+(c[h>>2]<<2)>>2]|0)+(c[(c[e>>2]|0)+92+(c[h>>2]<<2)>>2]|0)|0;c[l>>2]=a;c[g>>2]=2147483647/(c[l>>2]|0)|0;do if((c[l>>2]|0)<=(c[k>>2]<<3|0))if((c[l>>2]|0)<(c[k>>2]|0)){c[f>>2]=1024;break}else{a=_(c[g>>2]>>16,(c[k>>2]&65535)<<16>>16)|0;a=a+((_(c[g>>2]&65535,(c[k>>2]&65535)<<16>>16)|0)>>16)|0;a=a+(_(c[g>>2]|0,(c[k>>2]>>15)+1>>1)|0)>>16<<11;b=_(c[g>>2]>>16,(c[k>>2]&65535)<<16>>16)|0;b=b+((_(c[g>>2]&65535,(c[k>>2]&65535)<<16>>16)|0)>>16)|0;c[f>>2]=a+((b+(_(c[g>>2]|0,(c[k>>2]>>15)+1>>1)|0)&65535)<<11>>16);break}else c[f>>2]=128;while(0);c[f>>2]=Ze(c[f>>2]|0,c[j>>2]|0)|0;b=_((c[g>>2]|0)-(c[(c[e>>2]|0)+76+(c[h>>2]<<2)>>2]|0)>>16,(c[f>>2]&65535)<<16>>16)|0;b=(c[(c[e>>2]|0)+76+(c[h>>2]<<2)>>2]|0)+(b+((_((c[g>>2]|0)-(c[(c[e>>2]|0)+76+(c[h>>2]<<2)>>2]|0)&65535,(c[f>>2]&65535)<<16>>16)|0)>>16))|0;c[(c[e>>2]|0)+76+(c[h>>2]<<2)>>2]=b;c[k>>2]=2147483647/(c[(c[e>>2]|0)+76+(c[h>>2]<<2)>>2]|0)|0;c[k>>2]=(c[k>>2]|0)<16777215?c[k>>2]|0:16777215;c[(c[e>>2]|0)+60+(c[h>>2]<<2)>>2]=c[k>>2];c[h>>2]=(c[h>>2]|0)+1}l=(c[e>>2]|0)+108|0;c[l>>2]=(c[l>>2]|0)+1;i=m;return}function Ze(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)>(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function _e(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0;h=i;i=i+32|0;b=h+16|0;d=h+12|0;g=h+8|0;f=h+4|0;e=h;c[d>>2]=a;if((c[d>>2]|0)<=0){c[b>>2]=0;g=c[b>>2]|0;i=h;return g|0}$e(c[d>>2]|0,f,e);if(c[f>>2]&1|0)c[g>>2]=32768;else c[g>>2]=46214;c[g>>2]=c[g>>2]>>(c[f>>2]>>1);a=_(c[g>>2]>>16,(((c[e>>2]&65535)<<16>>16)*213&65535)<<16>>16)|0;c[g>>2]=(c[g>>2]|0)+(a+((_(c[g>>2]&65535,(((c[e>>2]&65535)<<16>>16)*213&65535)<<16>>16)|0)>>16));c[b>>2]=c[g>>2];g=c[b>>2]|0;i=h;return g|0}function $e(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0;e=i;i=i+16|0;h=e+12|0;j=e+8|0;f=e+4|0;g=e;c[h>>2]=a;c[j>>2]=b;c[f>>2]=d;c[g>>2]=af(c[h>>2]|0)|0;c[c[j>>2]>>2]=c[g>>2];b=(bf(c[h>>2]|0,24-(c[g>>2]|0)|0)|0)&127;c[c[f>>2]>>2]=b;i=e;return}function af(a){a=a|0;var b=0,d=0;d=i;i=i+16|0;b=d;c[b>>2]=a;if(!(c[b>>2]|0)){a=32;i=d;return a|0}a=32-(32-(aa(c[b>>2]|0)|0))|0;i=d;return a|0}function bf(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0;k=i;i=i+32|0;e=k+20|0;d=k+16|0;f=k+12|0;j=k+8|0;h=k+4|0;g=k;c[d>>2]=a;c[f>>2]=b;c[j>>2]=c[d>>2];c[h>>2]=c[f>>2];c[g>>2]=0-(c[f>>2]|0);if(!(c[f>>2]|0)){c[e>>2]=c[d>>2];j=c[e>>2]|0;i=k;return j|0}d=c[j>>2]|0;if((c[f>>2]|0)<0){c[e>>2]=d<<c[g>>2]|(c[j>>2]|0)>>>(32-(c[g>>2]|0)|0);j=c[e>>2]|0;i=k;return j|0}else{c[e>>2]=d<<32-(c[h>>2]|0)|(c[j>>2]|0)>>>(c[h>>2]|0);j=c[e>>2]|0;i=k;return j|0}return 0}function cf(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0;h=i;i=i+16|0;f=h+12|0;d=h+8|0;g=h+4|0;e=h;c[f>>2]=a;c[d>>2]=b;c[g>>2]=c[(c[f>>2]|0)+4600>>2];c[e>>2]=((c[g>>2]&65535)<<16>>16)*1e3;if(!(c[e>>2]|0)){b=c[f>>2]|0;if((c[(c[f>>2]|0)+4596>>2]|0)<(c[(c[f>>2]|0)+4580>>2]|0))b=c[b+4596>>2]|0;else b=c[b+4580>>2]|0;c[e>>2]=b;c[g>>2]=(c[e>>2]|0)/1e3|0;g=c[g>>2]|0;i=h;return g|0}if(((c[e>>2]|0)<=(c[(c[f>>2]|0)+4580>>2]|0)?(c[e>>2]|0)<=(c[(c[f>>2]|0)+4588>>2]|0):0)?(c[e>>2]|0)>=(c[(c[f>>2]|0)+4592>>2]|0):0){if((c[(c[f>>2]|0)+16+8>>2]|0)>=256)c[(c[f>>2]|0)+16+12>>2]=0;if((c[(c[f>>2]|0)+4560>>2]|0)==0?(c[(c[d>>2]|0)+60>>2]|0)==0:0){g=c[g>>2]|0;i=h;return g|0}b=c[f>>2]|0;if((((c[(c[f>>2]|0)+4600>>2]&65535)<<16>>16)*1e3|0)>(c[(c[f>>2]|0)+4596>>2]|0)){if(!(c[b+16+12>>2]|0)){c[(c[f>>2]|0)+16+8>>2]=256;e=(c[f>>2]|0)+16|0;c[e>>2]=0;c[e+4>>2]=0}b=(c[f>>2]|0)+16|0;if(c[(c[d>>2]|0)+60>>2]|0){c[b+12>>2]=0;c[g>>2]=(c[(c[f>>2]|0)+4600>>2]|0)==16?12:8;g=c[g>>2]|0;i=h;return g|0}if((c[b+8>>2]|0)<=0){c[(c[d>>2]|0)+84>>2]=1;f=(c[d>>2]|0)+52|0;c[f>>2]=(c[f>>2]|0)-(((c[(c[d>>2]|0)+52>>2]|0)*5|0)/((c[(c[d>>2]|0)+24>>2]|0)+5|0)|0);g=c[g>>2]|0;i=h;return g|0}else{c[(c[f>>2]|0)+16+12>>2]=-2;g=c[g>>2]|0;i=h;return g|0}}if((((c[b+4600>>2]&65535)<<16>>16)*1e3|0)>=(c[(c[f>>2]|0)+4596>>2]|0)){if((c[(c[f>>2]|0)+16+12>>2]|0)>=0){g=c[g>>2]|0;i=h;return g|0}c[(c[f>>2]|0)+16+12>>2]=1;g=c[g>>2]|0;i=h;return g|0}b=c[f>>2]|0;if(c[(c[d>>2]|0)+60>>2]|0){c[g>>2]=(c[b+4600>>2]|0)==8?12:16;c[(c[f>>2]|0)+16+8>>2]=0;e=(c[f>>2]|0)+16|0;c[e>>2]=0;c[e+4>>2]=0;c[(c[f>>2]|0)+16+12>>2]=1;g=c[g>>2]|0;i=h;return g|0}if(!(c[b+16+12>>2]|0)){c[(c[d>>2]|0)+84>>2]=1;f=(c[d>>2]|0)+52|0;c[f>>2]=(c[f>>2]|0)-(((c[(c[d>>2]|0)+52>>2]|0)*5|0)/((c[(c[d>>2]|0)+24>>2]|0)+5|0)|0);g=c[g>>2]|0;i=h;return g|0}else{c[(c[f>>2]|0)+16+12>>2]=1;g=c[g>>2]|0;i=h;return g|0}}c[e>>2]=c[(c[f>>2]|0)+4580>>2];if((c[e>>2]|0)<(c[(c[f>>2]|0)+4588>>2]|0))b=c[e>>2]|0;else b=c[(c[f>>2]|0)+4588>>2]|0;c[e>>2]=b;if((c[e>>2]|0)>(c[(c[f>>2]|0)+4592>>2]|0))b=c[e>>2]|0;else b=c[(c[f>>2]|0)+4592>>2]|0;c[e>>2]=b;c[g>>2]=(c[e>>2]|0)/1e3|0;g=c[g>>2]|0;i=h;return g|0}function df(d,e,f,g,h,j,k,l,m){d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;var n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0;M=i;i=i+112|0;E=M+96|0;F=M+92|0;n=M+88|0;G=M+84|0;o=M+80|0;p=M+76|0;q=M+72|0;H=M+68|0;K=M+60|0;L=M+56|0;u=M+52|0;D=M+100|0;v=M+48|0;J=M+44|0;t=M+40|0;s=M+36|0;r=M+32|0;B=M+28|0;A=M+24|0;z=M+20|0;C=M+16|0;I=M+12|0;y=M+8|0;w=M+4|0;x=M;c[E>>2]=d;c[F>>2]=e;c[n>>2]=f;c[G>>2]=g;c[o>>2]=h;c[p>>2]=j;c[q>>2]=k;c[H>>2]=l;c[M+64>>2]=m;c[z>>2]=2147483647;c[I>>2]=0;c[L>>2]=0;while(1){if((c[L>>2]|0)>=3)break;c[x>>2]=51;c[v>>2]=c[17632+(c[L>>2]<<2)>>2];c[J>>2]=c[17644+(c[L>>2]<<2)>>2];c[t>>2]=c[17656+(c[L>>2]<<2)>>2];c[u>>2]=a[27263+(c[L>>2]|0)>>0];c[r>>2]=c[o>>2];c[s>>2]=c[E>>2];c[A>>2]=0;c[C>>2]=c[c[G>>2]>>2];c[K>>2]=0;while(1){if((c[K>>2]|0)>=(c[H>>2]|0))break;g=Wf(5333-(c[C>>2]|0)+896|0)|0;c[y>>2]=g-(c[x>>2]|0);ef(D+(c[K>>2]|0)|0,B,w,c[s>>2]|0,c[r>>2]|0,c[J>>2]|0,c[t>>2]|0,c[v>>2]|0,c[p>>2]|0,c[y>>2]|0,c[u>>2]|0);if((c[A>>2]|0)+(c[B>>2]|0)&-2147483648|0)d=2147483647;else d=(c[A>>2]|0)+(c[B>>2]|0)|0;c[A>>2]=d;g=c[C>>2]|0;if(0>(g+(Sf((c[x>>2]|0)+(c[w>>2]|0)|0)|0)-896|0))d=0;else{d=c[C>>2]|0;d=d+(Sf((c[x>>2]|0)+(c[w>>2]|0)|0)|0)-896|0}c[C>>2]=d;c[s>>2]=(c[s>>2]|0)+10;c[r>>2]=(c[r>>2]|0)+100;c[K>>2]=(c[K>>2]|0)+1}c[A>>2]=2147483646<(c[A>>2]|0)?2147483646:c[A>>2]|0;if((c[A>>2]|0)<(c[z>>2]|0)){c[z>>2]=c[A>>2];a[c[n>>2]>>0]=c[L>>2];pj(c[F>>2]|0,D|0,c[H>>2]|0)|0;c[I>>2]=c[C>>2]}if(c[q>>2]|0?(c[A>>2]|0)<(b[12226]|0):0)break;c[L>>2]=(c[L>>2]|0)+1}c[J>>2]=c[17644+(a[c[n>>2]>>0]<<2)>>2];c[K>>2]=0;while(1){if((c[K>>2]|0)>=(c[H>>2]|0))break;c[L>>2]=0;while(1){d=c[K>>2]|0;if((c[L>>2]|0)>=5)break;b[(c[E>>2]|0)+(((c[K>>2]|0)*5|0)+(c[L>>2]|0)<<1)>>1]=a[(c[J>>2]|0)+(((a[(c[F>>2]|0)+d>>0]|0)*5|0)+(c[L>>2]|0))>>0]<<7;c[L>>2]=(c[L>>2]|0)+1}c[K>>2]=d+1}c[c[G>>2]>>2]=c[I>>2];i=M;return}function ef(e,f,g,h,j,k,l,m,n,o,p){e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;p=p|0;var q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0;G=i;i=i+80|0;q=G+60|0;r=G+56|0;t=G+52|0;u=G+48|0;v=G+44|0;H=G+40|0;w=G+36|0;x=G+32|0;y=G+28|0;z=G+24|0;s=G+20|0;D=G+16|0;C=G+12|0;A=G+8|0;B=G+64|0;E=G+4|0;F=G;c[q>>2]=e;c[r>>2]=f;c[t>>2]=g;c[u>>2]=h;c[v>>2]=j;c[H>>2]=k;c[w>>2]=l;c[x>>2]=m;c[y>>2]=n;c[z>>2]=o;c[s>>2]=p;c[c[r>>2]>>2]=2147483647;c[A>>2]=c[H>>2];c[D>>2]=0;while(1){if((c[D>>2]|0)>=(c[s>>2]|0))break;c[C>>2]=d[(c[w>>2]|0)+(c[D>>2]|0)>>0];b[B>>1]=(b[c[u>>2]>>1]|0)-(a[c[A>>2]>>0]<<7);b[B+2>>1]=(b[(c[u>>2]|0)+2>>1]|0)-(a[(c[A>>2]|0)+1>>0]<<7);b[B+4>>1]=(b[(c[u>>2]|0)+4>>1]|0)-(a[(c[A>>2]|0)+2>>0]<<7);b[B+6>>1]=(b[(c[u>>2]|0)+6>>1]|0)-(a[(c[A>>2]|0)+3>>0]<<7);b[B+8>>1]=(b[(c[u>>2]|0)+8>>1]|0)-(a[(c[A>>2]|0)+4>>0]<<7);c[E>>2]=_((c[y>>2]&65535)<<16>>16,d[(c[x>>2]|0)+(c[D>>2]|0)>>0]|0)|0;if(((c[C>>2]|0)-(c[z>>2]|0)|0)>0)p=(c[C>>2]|0)-(c[z>>2]|0)|0;else p=0;c[E>>2]=(c[E>>2]|0)+(p<<10);H=_(c[(c[v>>2]|0)+4>>2]>>16,b[B+2>>1]|0)|0;c[F>>2]=H+((_(c[(c[v>>2]|0)+4>>2]&65535,b[B+2>>1]|0)|0)>>16);H=_(c[(c[v>>2]|0)+8>>2]>>16,b[B+4>>1]|0)|0;c[F>>2]=(c[F>>2]|0)+(H+((_(c[(c[v>>2]|0)+8>>2]&65535,b[B+4>>1]|0)|0)>>16));H=_(c[(c[v>>2]|0)+12>>2]>>16,b[B+6>>1]|0)|0;c[F>>2]=(c[F>>2]|0)+(H+((_(c[(c[v>>2]|0)+12>>2]&65535,b[B+6>>1]|0)|0)>>16));H=_(c[(c[v>>2]|0)+16>>2]>>16,b[B+8>>1]|0)|0;c[F>>2]=(c[F>>2]|0)+(H+((_(c[(c[v>>2]|0)+16>>2]&65535,b[B+8>>1]|0)|0)>>16));c[F>>2]=c[F>>2]<<1;H=_(c[c[v>>2]>>2]>>16,b[B>>1]|0)|0;c[F>>2]=(c[F>>2]|0)+(H+((_(c[c[v>>2]>>2]&65535,b[B>>1]|0)|0)>>16));H=_(c[F>>2]>>16,b[B>>1]|0)|0;c[E>>2]=(c[E>>2]|0)+(H+((_(c[F>>2]&65535,b[B>>1]|0)|0)>>16));H=_(c[(c[v>>2]|0)+28>>2]>>16,b[B+4>>1]|0)|0;c[F>>2]=H+((_(c[(c[v>>2]|0)+28>>2]&65535,b[B+4>>1]|0)|0)>>16);H=_(c[(c[v>>2]|0)+32>>2]>>16,b[B+6>>1]|0)|0;c[F>>2]=(c[F>>2]|0)+(H+((_(c[(c[v>>2]|0)+32>>2]&65535,b[B+6>>1]|0)|0)>>16));H=_(c[(c[v>>2]|0)+36>>2]>>16,b[B+8>>1]|0)|0;c[F>>2]=(c[F>>2]|0)+(H+((_(c[(c[v>>2]|0)+36>>2]&65535,b[B+8>>1]|0)|0)>>16));c[F>>2]=c[F>>2]<<1;H=_(c[(c[v>>2]|0)+24>>2]>>16,b[B+2>>1]|0)|0;c[F>>2]=(c[F>>2]|0)+(H+((_(c[(c[v>>2]|0)+24>>2]&65535,b[B+2>>1]|0)|0)>>16));H=_(c[F>>2]>>16,b[B+2>>1]|0)|0;c[E>>2]=(c[E>>2]|0)+(H+((_(c[F>>2]&65535,b[B+2>>1]|0)|0)>>16));H=_(c[(c[v>>2]|0)+52>>2]>>16,b[B+6>>1]|0)|0;c[F>>2]=H+((_(c[(c[v>>2]|0)+52>>2]&65535,b[B+6>>1]|0)|0)>>16);H=_(c[(c[v>>2]|0)+56>>2]>>16,b[B+8>>1]|0)|0;c[F>>2]=(c[F>>2]|0)+(H+((_(c[(c[v>>2]|0)+56>>2]&65535,b[B+8>>1]|0)|0)>>16));c[F>>2]=c[F>>2]<<1;H=_(c[(c[v>>2]|0)+48>>2]>>16,b[B+4>>1]|0)|0;c[F>>2]=(c[F>>2]|0)+(H+((_(c[(c[v>>2]|0)+48>>2]&65535,b[B+4>>1]|0)|0)>>16));H=_(c[F>>2]>>16,b[B+4>>1]|0)|0;c[E>>2]=(c[E>>2]|0)+(H+((_(c[F>>2]&65535,b[B+4>>1]|0)|0)>>16));H=_(c[(c[v>>2]|0)+76>>2]>>16,b[B+8>>1]|0)|0;c[F>>2]=H+((_(c[(c[v>>2]|0)+76>>2]&65535,b[B+8>>1]|0)|0)>>16);c[F>>2]=c[F>>2]<<1;H=_(c[(c[v>>2]|0)+72>>2]>>16,b[B+6>>1]|0)|0;c[F>>2]=(c[F>>2]|0)+(H+((_(c[(c[v>>2]|0)+72>>2]&65535,b[B+6>>1]|0)|0)>>16));H=_(c[F>>2]>>16,b[B+6>>1]|0)|0;c[E>>2]=(c[E>>2]|0)+(H+((_(c[F>>2]&65535,b[B+6>>1]|0)|0)>>16));H=_(c[(c[v>>2]|0)+96>>2]>>16,b[B+8>>1]|0)|0;c[F>>2]=H+((_(c[(c[v>>2]|0)+96>>2]&65535,b[B+8>>1]|0)|0)>>16);H=_(c[F>>2]>>16,b[B+8>>1]|0)|0;c[E>>2]=(c[E>>2]|0)+(H+((_(c[F>>2]&65535,b[B+8>>1]|0)|0)>>16));if((c[E>>2]|0)<(c[c[r>>2]>>2]|0)){c[c[r>>2]>>2]=c[E>>2];a[c[q>>2]>>0]=c[D>>2];c[c[t>>2]>>2]=c[C>>2]}c[A>>2]=(c[A>>2]|0)+5;c[D>>2]=(c[D>>2]|0)+1}i=G;return}function ff(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0;j=i;i=i+32|0;k=j+20|0;f=j+16|0;d=j+12|0;e=j+8|0;g=j+4|0;h=j;c[k>>2]=b;c[h>>2]=c[k>>2];if((a[(c[h>>2]|0)+4565>>0]|0)!=2){i=j;return}c[d>>2]=((c[(c[h>>2]|0)+4600>>2]|0)*1e3<<16|0)/(c[(c[h>>2]|0)+4568>>2]|0)|0;c[e>>2]=(Sf(c[d>>2]|0)|0)-2048;c[f>>2]=c[(c[h>>2]|0)+4728>>2];l=c[e>>2]|0;b=_(0-(c[f>>2]|0)<<2>>16,(c[f>>2]&65535)<<16>>16)|0;b=b+((_(0-(c[f>>2]|0)<<2&65535,(c[f>>2]&65535)<<16>>16)|0)>>16)>>16;d=c[e>>2]|0;d=_(b,(d-((Sf(3932160)|0)-2048)&65535)<<16>>16)|0;b=_(0-(c[f>>2]|0)<<2>>16,(c[f>>2]&65535)<<16>>16)|0;b=b+((_(0-(c[f>>2]|0)<<2&65535,(c[f>>2]&65535)<<16>>16)|0)>>16)&65535;k=c[e>>2]|0;c[e>>2]=l+(d+((_(b,(k-((Sf(3932160)|0)-2048)&65535)<<16>>16)|0)>>16));c[g>>2]=(c[e>>2]|0)-(c[(c[h>>2]|0)+8>>2]>>8);if((c[g>>2]|0)<0)c[g>>2]=(c[g>>2]|0)*3;if((c[g>>2]|0)>51)d=51;else d=(c[g>>2]|0)<-51?-51:c[g>>2]|0;c[g>>2]=d;l=((_((c[(c[h>>2]|0)+4556>>2]&65535)<<16>>16,(c[g>>2]&65535)<<16>>16)|0)>>16)*6554|0;l=(c[(c[h>>2]|0)+8>>2]|0)+(l+(((_((c[(c[h>>2]|0)+4556>>2]&65535)<<16>>16,(c[g>>2]&65535)<<16>>16)|0)&65535)*6554>>16))|0;c[(c[h>>2]|0)+8>>2]=l;l=(Sf(60)|0)<<8;l=(l|0)>((Sf(100)|0)<<8|0);d=c[(c[h>>2]|0)+8>>2]|0;do if(l){if((d|0)>((Sf(60)|0)<<8|0)){d=(Sf(60)|0)<<8;break}l=c[(c[h>>2]|0)+8>>2]|0;if((l|0)<((Sf(100)|0)<<8|0)){d=(Sf(100)|0)<<8;break}else{d=c[(c[h>>2]|0)+8>>2]|0;break}}else{if((d|0)>((Sf(100)|0)<<8|0)){d=(Sf(100)|0)<<8;break}l=c[(c[h>>2]|0)+8>>2]|0;if((l|0)<((Sf(60)|0)<<8|0)){d=(Sf(60)|0)<<8;break}else{d=c[(c[h>>2]|0)+8>>2]|0;break}}while(0);c[(c[h>>2]|0)+8>>2]=d;i=j;return}function gf(f,g,h,j,k,l,m){f=f|0;g=g|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;var n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0;L=i;i=i+288|0;n=L+64|0;o=L+60|0;p=L+56|0;q=L+52|0;r=L+48|0;s=L+44|0;t=L+40|0;C=L+36|0;K=L+32|0;E=L+28|0;z=L+24|0;H=L+20|0;A=L+16|0;x=L+12|0;J=L+232|0;I=L+200|0;v=L+168|0;y=L+136|0;w=L+104|0;G=L+264|0;B=L+72|0;F=L+8|0;D=L+4|0;u=L;c[n>>2]=f;c[o>>2]=g;c[p>>2]=h;c[q>>2]=j;c[r>>2]=k;c[s>>2]=l;c[t>>2]=m;cg(c[o>>2]|0,c[(c[p>>2]|0)+32>>2]|0,b[(c[p>>2]|0)+2>>1]|0);k=e[c[p>>2]>>1]|0;c[u>>2]=ia()|0;l=i;i=i+((1*(k<<2)|0)+15&-16)|0;mf(l,c[o>>2]|0,c[(c[p>>2]|0)+8>>2]|0,b[c[p>>2]>>1]|0,b[(c[p>>2]|0)+2>>1]|0);k=i;i=i+((1*(c[s>>2]<<2)|0)+15&-16)|0;ug(l,k,b[c[p>>2]>>1]|0,c[s>>2]|0);l=i;i=i+((1*(c[s>>2]<<2)|0)+15&-16)|0;g=i;i=i+((1*(c[s>>2]<<4)|0)+15&-16)|0;c[K>>2]=0;while(1){if((c[K>>2]|0)>=(c[s>>2]|0))break;c[E>>2]=c[k+(c[K>>2]<<2)>>2];m=_(c[E>>2]|0,b[(c[p>>2]|0)+2>>1]|0)|0;c[F>>2]=(c[(c[p>>2]|0)+8>>2]|0)+m;c[C>>2]=0;while(1){if((c[C>>2]|0)>=(b[(c[p>>2]|0)+2>>1]|0))break;b[v+(c[C>>2]<<1)>>1]=(d[(c[F>>2]|0)+(c[C>>2]|0)>>0]&65535)<<7;b[J+(c[C>>2]<<1)>>1]=(b[(c[o>>2]|0)+(c[C>>2]<<1)>>1]|0)-(b[v+(c[C>>2]<<1)>>1]|0);c[C>>2]=(c[C>>2]|0)+1}fg(y,v,b[(c[p>>2]|0)+2>>1]|0);c[C>>2]=0;while(1){if((c[C>>2]|0)>=(b[(c[p>>2]|0)+2>>1]|0))break;c[x>>2]=hf(b[y+(c[C>>2]<<1)>>1]<<16)|0;m=(_(b[J+(c[C>>2]<<1)>>1]|0,(c[x>>2]&65535)<<16>>16)|0)>>14&65535;b[I+(c[C>>2]<<1)>>1]=m;c[C>>2]=(c[C>>2]|0)+1}c[C>>2]=0;while(1){if((c[C>>2]|0)>=(b[(c[p>>2]|0)+2>>1]|0))break;b[w+(c[C>>2]<<1)>>1]=(b[(c[q>>2]|0)+(c[C>>2]<<1)>>1]<<5|0)/(b[y+(c[C>>2]<<1)>>1]|0)|0;c[C>>2]=(c[C>>2]|0)+1}nf(B,G,c[p>>2]|0,c[E>>2]|0);f=of(g+(c[K>>2]<<4)|0,I,w,G,B,c[(c[p>>2]|0)+28>>2]|0,b[(c[p>>2]|0)+4>>1]|0,b[(c[p>>2]|0)+6>>1]|0,c[r>>2]|0,b[(c[p>>2]|0)+2>>1]|0)|0;c[l+(c[K>>2]<<2)>>2]=f;f=_(c[t>>2]>>1,b[c[p>>2]>>1]|0)|0;c[D>>2]=(c[(c[p>>2]|0)+12>>2]|0)+f;f=c[E>>2]|0;if(!(c[E>>2]|0))c[H>>2]=256-(d[(c[D>>2]|0)+f>>0]|0);else c[H>>2]=(d[(c[D>>2]|0)+(f-1)>>0]|0)-(d[(c[D>>2]|0)+(c[E>>2]|0)>>0]|0);c[A>>2]=1024-(Sf(c[H>>2]|0)|0);m=(c[l+(c[K>>2]<<2)>>2]|0)+(_((c[A>>2]&65535)<<16>>16,(c[r>>2]>>2&65535)<<16>>16)|0)|0;c[l+(c[K>>2]<<2)>>2]=m;c[K>>2]=(c[K>>2]|0)+1}ug(l,z,c[s>>2]|0,1);a[c[n>>2]>>0]=c[k+(c[z>>2]<<2)>>2];pj((c[n>>2]|0)+1|0,g+(c[z>>2]<<4)|0,b[(c[p>>2]|0)+2>>1]|0)|0;ge(c[o>>2]|0,c[n>>2]|0,c[p>>2]|0);K=c[l>>2]|0;na(c[u>>2]|0);i=L;return K|0}function hf(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0;h=i;i=i+32|0;b=h+16|0;d=h+12|0;g=h+8|0;f=h+4|0;e=h;c[d>>2]=a;if((c[d>>2]|0)<=0){c[b>>2]=0;g=c[b>>2]|0;i=h;return g|0}jf(c[d>>2]|0,f,e);if(c[f>>2]&1|0)c[g>>2]=32768;else c[g>>2]=46214;c[g>>2]=c[g>>2]>>(c[f>>2]>>1);a=_(c[g>>2]>>16,(((c[e>>2]&65535)<<16>>16)*213&65535)<<16>>16)|0;c[g>>2]=(c[g>>2]|0)+(a+((_(c[g>>2]&65535,(((c[e>>2]&65535)<<16>>16)*213&65535)<<16>>16)|0)>>16));c[b>>2]=c[g>>2];g=c[b>>2]|0;i=h;return g|0}function jf(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0;e=i;i=i+16|0;h=e+12|0;j=e+8|0;f=e+4|0;g=e;c[h>>2]=a;c[j>>2]=b;c[f>>2]=d;c[g>>2]=kf(c[h>>2]|0)|0;c[c[j>>2]>>2]=c[g>>2];b=(lf(c[h>>2]|0,24-(c[g>>2]|0)|0)|0)&127;c[c[f>>2]>>2]=b;i=e;return}function kf(a){a=a|0;var b=0,d=0;d=i;i=i+16|0;b=d;c[b>>2]=a;if(!(c[b>>2]|0)){a=32;i=d;return a|0}a=32-(32-(aa(c[b>>2]|0)|0))|0;i=d;return a|0}function lf(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0;k=i;i=i+32|0;e=k+20|0;d=k+16|0;f=k+12|0;j=k+8|0;h=k+4|0;g=k;c[d>>2]=a;c[f>>2]=b;c[j>>2]=c[d>>2];c[h>>2]=c[f>>2];c[g>>2]=0-(c[f>>2]|0);if(!(c[f>>2]|0)){c[e>>2]=c[d>>2];j=c[e>>2]|0;i=k;return j|0}d=c[j>>2]|0;if((c[f>>2]|0)<0){c[e>>2]=d<<c[g>>2]|(c[j>>2]|0)>>>(32-(c[g>>2]|0)|0);j=c[e>>2]|0;i=k;return j|0}else{c[e>>2]=d<<32-(c[h>>2]|0)|(c[j>>2]|0)>>>(c[h>>2]|0);j=c[e>>2]|0;i=k;return j|0}return 0}function mf(a,e,f,g,h){a=a|0;e=e|0;f=f|0;g=g|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;t=i;i=i+48|0;j=t+36|0;k=t+32|0;l=t+28|0;m=t+24|0;n=t+20|0;p=t+16|0;q=t+12|0;o=t+8|0;s=t+4|0;r=t;c[j>>2]=a;c[k>>2]=e;c[l>>2]=f;c[m>>2]=g;c[n>>2]=h;c[p>>2]=0;while(1){if((c[p>>2]|0)>=(c[m>>2]|0))break;c[r>>2]=0;c[q>>2]=0;while(1){if((c[q>>2]|0)>=(c[n>>2]|0))break;f=b[(c[k>>2]|0)+(c[q>>2]<<1)>>1]|0;e=c[l>>2]|0;c[l>>2]=e+1;c[o>>2]=f-(d[e>>0]<<7);c[s>>2]=_((c[o>>2]&65535)<<16>>16,(c[o>>2]&65535)<<16>>16)|0;e=b[(c[k>>2]|0)+((c[q>>2]|0)+1<<1)>>1]|0;f=c[l>>2]|0;c[l>>2]=f+1;c[o>>2]=e-(d[f>>0]<<7);c[s>>2]=(c[s>>2]|0)+(_((c[o>>2]&65535)<<16>>16,(c[o>>2]&65535)<<16>>16)|0);c[r>>2]=(c[r>>2]|0)+(c[s>>2]>>4);c[q>>2]=(c[q>>2]|0)+2}c[(c[j>>2]|0)+(c[p>>2]<<2)>>2]=c[r>>2];c[p>>2]=(c[p>>2]|0)+1}i=t;return}function nf(e,f,g,h){e=e|0;f=f|0;g=g|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;p=i;i=i+32|0;j=p+20|0;k=p+16|0;l=p+12|0;q=p+8|0;o=p+4|0;n=p+24|0;m=p;c[j>>2]=e;c[k>>2]=f;c[l>>2]=g;c[q>>2]=h;g=(_(c[q>>2]|0,b[(c[l>>2]|0)+2>>1]|0)|0)/2|0;c[m>>2]=(c[(c[l>>2]|0)+20>>2]|0)+g;c[o>>2]=0;while(1){if((c[o>>2]|0)>=(b[(c[l>>2]|0)+2>>1]|0))break;q=c[m>>2]|0;c[m>>2]=q+1;a[n>>0]=a[q>>0]|0;b[(c[j>>2]|0)+(c[o>>2]<<1)>>1]=((d[n>>0]>>1&7)<<16>>16)*9;q=(c[o>>2]|0)+(_(d[n>>0]&1,(b[(c[l>>2]|0)+2>>1]|0)-1|0)|0)|0;a[(c[k>>2]|0)+(c[o>>2]|0)>>0]=a[(c[(c[l>>2]|0)+16>>2]|0)+q>>0]|0;b[(c[j>>2]|0)+((c[o>>2]|0)+1<<1)>>1]=((d[n>>0]>>5&7)<<16>>16)*9;q=(c[o>>2]|0)+(_(d[n>>0]>>4&1,(b[(c[l>>2]|0)+2>>1]|0)-1|0)|0)+1|0;a[(c[k>>2]|0)+((c[o>>2]|0)+1)>>0]=a[(c[(c[l>>2]|0)+16>>2]|0)+q>>0]|0;c[o>>2]=(c[o>>2]|0)+2}i=p;return}function of(e,f,g,h,j,k,l,m,n,o){e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;var p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,$=0,aa=0;aa=i;i=i+448|0;q=aa+352|0;r=aa+348|0;s=aa+344|0;t=aa+340|0;u=aa+336|0;v=aa+332|0;p=aa+328|0;w=aa+378|0;x=aa+324|0;y=aa+376|0;E=aa+320|0;L=aa+316|0;P=aa+312|0;K=aa+308|0;I=aa+304|0;H=aa+300|0;F=aa+296|0;$=aa+292|0;U=aa+288|0;D=aa+284|0;Q=aa+280|0;S=aa+276|0;X=aa+272|0;Y=aa+268|0;C=aa+264|0;N=aa+260|0;O=aa+256|0;M=aa+252|0;V=aa+248|0;J=aa+232|0;G=aa+384|0;W=aa+360|0;z=aa+200|0;B=aa+184|0;A=aa+168|0;Z=aa+160|0;R=aa+80|0;T=aa;c[q>>2]=e;c[r>>2]=f;c[s>>2]=g;c[t>>2]=h;c[u>>2]=j;c[v>>2]=k;c[p>>2]=l;b[w>>1]=m;c[x>>2]=n;b[y>>1]=o;c[E>>2]=-10;while(1){if((c[E>>2]|0)>9)break;c[Q>>2]=c[E>>2]<<10;c[S>>2]=(c[Q>>2]|0)+1024;do if((c[E>>2]|0)<=0){if(!(c[E>>2]|0)){c[S>>2]=(c[S>>2]|0)-102;break}f=(c[E>>2]|0)==-1;c[Q>>2]=(c[Q>>2]|0)+102;if(!f)c[S>>2]=(c[S>>2]|0)+102}else{c[Q>>2]=(c[Q>>2]|0)-102;c[S>>2]=(c[S>>2]|0)-102}while(0);f=_(c[Q>>2]>>16,(c[p>>2]&65535)<<16>>16)|0;f=f+((_(c[Q>>2]&65535,(c[p>>2]&65535)<<16>>16)|0)>>16)|0;c[R+((c[E>>2]|0)+10<<2)>>2]=f;f=_(c[S>>2]>>16,(c[p>>2]&65535)<<16>>16)|0;f=f+((_(c[S>>2]&65535,(c[p>>2]&65535)<<16>>16)|0)>>16)|0;c[T+((c[E>>2]|0)+10<<2)>>2]=f;c[E>>2]=(c[E>>2]|0)+1}c[P>>2]=1;c[z>>2]=0;b[W>>1]=0;c[E>>2]=(b[y>>1]|0)-1;a:while(1){c[Z>>2]=(c[v>>2]|0)+(b[(c[u>>2]|0)+(c[E>>2]<<1)>>1]|0);c[V>>2]=d[(c[t>>2]|0)+(c[E>>2]|0)>>0]<<8;c[F>>2]=b[(c[r>>2]|0)+(c[E>>2]<<1)>>1];c[L>>2]=0;while(1){if((c[L>>2]|0)>=(c[P>>2]|0))break;f=_(c[V>>2]>>16,b[W+(c[L>>2]<<1)>>1]|0)|0;c[U>>2]=f+((_(c[V>>2]&65535,b[W+(c[L>>2]<<1)>>1]|0)|0)>>16);c[$>>2]=(c[F>>2]|0)-(c[U>>2]|0);f=_(b[w>>1]>>16,(c[$>>2]&65535)<<16>>16)|0;c[K>>2]=f+((_(b[w>>1]&65535,(c[$>>2]&65535)<<16>>16)|0)>>16);if((c[K>>2]|0)>9)p=9;else p=(c[K>>2]|0)<-10?-10:c[K>>2]|0;c[K>>2]=p;a[G+(c[L>>2]<<4)+(c[E>>2]|0)>>0]=c[K>>2];c[Q>>2]=c[R+((c[K>>2]|0)+10<<2)>>2];c[S>>2]=c[T+((c[K>>2]|0)+10<<2)>>2];c[Q>>2]=(c[Q>>2]|0)+(c[U>>2]|0);c[S>>2]=(c[S>>2]|0)+(c[U>>2]|0);b[W+(c[L>>2]<<1)>>1]=c[Q>>2];b[W+((c[L>>2]|0)+(c[P>>2]|0)<<1)>>1]=c[S>>2];j=c[K>>2]|0;do if(((c[K>>2]|0)+1|0)>=4){p=c[K>>2]|0;if((j+1|0)==4){c[X>>2]=d[(c[Z>>2]|0)+(p+4)>>0];c[Y>>2]=280;break}else{c[X>>2]=108+(((p&65535)<<16>>16)*43|0);c[Y>>2]=(c[X>>2]|0)+43;break}}else{p=c[K>>2]|0;if((j|0)>-4){c[X>>2]=d[(c[Z>>2]|0)+(p+4)>>0];c[Y>>2]=d[(c[Z>>2]|0)+((c[K>>2]|0)+1+4)>>0];break}if((p|0)==-4){c[X>>2]=280;c[Y>>2]=d[(c[Z>>2]|0)+((c[K>>2]|0)+1+4)>>0];break}else{c[X>>2]=108+(_(-43,(c[K>>2]&65535)<<16>>16)|0);c[Y>>2]=(c[X>>2]|0)-43;break}}while(0);c[C>>2]=c[z+(c[L>>2]<<2)>>2];c[D>>2]=(c[F>>2]|0)-(c[Q>>2]|0);f=_((c[D>>2]&65535)<<16>>16,(c[D>>2]&65535)<<16>>16)|0;f=(c[C>>2]|0)+(_(f,b[(c[s>>2]|0)+(c[E>>2]<<1)>>1]|0)|0)|0;f=f+(_((c[x>>2]&65535)<<16>>16,(c[X>>2]&65535)<<16>>16)|0)|0;c[z+(c[L>>2]<<2)>>2]=f;c[D>>2]=(c[F>>2]|0)-(c[S>>2]|0);f=_((c[D>>2]&65535)<<16>>16,(c[D>>2]&65535)<<16>>16)|0;f=(c[C>>2]|0)+(_(f,b[(c[s>>2]|0)+(c[E>>2]<<1)>>1]|0)|0)|0;f=f+(_((c[x>>2]&65535)<<16>>16,(c[Y>>2]&65535)<<16>>16)|0)|0;c[z+((c[L>>2]|0)+(c[P>>2]|0)<<2)>>2]=f;c[L>>2]=(c[L>>2]|0)+1}b:do if((c[P>>2]|0)<=2){c[L>>2]=0;while(1){if((c[L>>2]|0)>=(c[P>>2]|0))break;a[G+((c[L>>2]|0)+(c[P>>2]|0)<<4)+(c[E>>2]|0)>>0]=(a[G+(c[L>>2]<<4)+(c[E>>2]|0)>>0]|0)+1;c[L>>2]=(c[L>>2]|0)+1}c[P>>2]=c[P>>2]<<1;c[L>>2]=c[P>>2];while(1){if((c[L>>2]|0)>=4)break b;a[G+(c[L>>2]<<4)+(c[E>>2]|0)>>0]=a[G+((c[L>>2]|0)-(c[P>>2]|0)<<4)+(c[E>>2]|0)>>0]|0;c[L>>2]=(c[L>>2]|0)+1}}else{if((c[E>>2]|0)<=0)break a;c[L>>2]=0;while(1){if((c[L>>2]|0)>=4)break;p=c[z+(c[L>>2]<<2)>>2]|0;j=c[L>>2]|0;if((c[z+(c[L>>2]<<2)>>2]|0)>(c[z+((c[L>>2]|0)+4<<2)>>2]|0)){c[A+(j<<2)>>2]=p;c[B+(c[L>>2]<<2)>>2]=c[z+((c[L>>2]|0)+4<<2)>>2];c[z+(c[L>>2]<<2)>>2]=c[B+(c[L>>2]<<2)>>2];c[z+((c[L>>2]|0)+4<<2)>>2]=c[A+(c[L>>2]<<2)>>2];c[Q>>2]=b[W+(c[L>>2]<<1)>>1];b[W+(c[L>>2]<<1)>>1]=b[W+((c[L>>2]|0)+4<<1)>>1]|0;b[W+((c[L>>2]|0)+4<<1)>>1]=c[Q>>2];c[J+(c[L>>2]<<2)>>2]=(c[L>>2]|0)+4}else{c[B+(j<<2)>>2]=p;c[A+(c[L>>2]<<2)>>2]=c[z+((c[L>>2]|0)+4<<2)>>2];c[J+(c[L>>2]<<2)>>2]=c[L>>2]}c[L>>2]=(c[L>>2]|0)+1}while(1){c[O>>2]=2147483647;c[M>>2]=0;c[I>>2]=0;c[H>>2]=0;c[L>>2]=0;while(1){p=c[O>>2]|0;if((c[L>>2]|0)>=4)break;if((p|0)>(c[A+(c[L>>2]<<2)>>2]|0)){c[O>>2]=c[A+(c[L>>2]<<2)>>2];c[I>>2]=c[L>>2]}if((c[M>>2]|0)<(c[B+(c[L>>2]<<2)>>2]|0)){c[M>>2]=c[B+(c[L>>2]<<2)>>2];c[H>>2]=c[L>>2]}c[L>>2]=(c[L>>2]|0)+1}if((p|0)>=(c[M>>2]|0))break;c[J+(c[H>>2]<<2)>>2]=c[J+(c[I>>2]<<2)>>2]^4;c[z+(c[H>>2]<<2)>>2]=c[z+((c[I>>2]|0)+4<<2)>>2];b[W+(c[H>>2]<<1)>>1]=b[W+((c[I>>2]|0)+4<<1)>>1]|0;c[B+(c[H>>2]<<2)>>2]=0;c[A+(c[I>>2]<<2)>>2]=2147483647;p=G+(c[H>>2]<<4)|0;j=G+(c[I>>2]<<4)|0;k=p+16|0;do{a[p>>0]=a[j>>0]|0;p=p+1|0;j=j+1|0}while((p|0)<(k|0))}c[L>>2]=0;while(1){if((c[L>>2]|0)>=4)break b;f=G+(c[L>>2]<<4)+(c[E>>2]|0)|0;a[f>>0]=(a[f>>0]|0)+(c[J+(c[L>>2]<<2)>>2]>>2);c[L>>2]=(c[L>>2]|0)+1}}while(0);c[E>>2]=(c[E>>2]|0)+-1}c[K>>2]=0;c[N>>2]=2147483647;c[L>>2]=0;while(1){if((c[L>>2]|0)>=8)break;if((c[N>>2]|0)>(c[z+(c[L>>2]<<2)>>2]|0)){c[N>>2]=c[z+(c[L>>2]<<2)>>2];c[K>>2]=c[L>>2]}c[L>>2]=(c[L>>2]|0)+1}c[L>>2]=0;while(1){if((c[L>>2]|0)>=(b[y>>1]|0))break;a[(c[q>>2]|0)+(c[L>>2]|0)>>0]=a[G+((c[K>>2]&3)<<4)+(c[L>>2]|0)>>0]|0;c[L>>2]=(c[L>>2]|0)+1}$=c[q>>2]|0;a[$>>0]=(a[$>>0]|0)+(c[K>>2]>>2);i=aa;return c[N>>2]|0}function pf(d,e,f,g){d=d|0;e=e|0;f=f|0;g=g|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;t=i;i=i+128|0;h=t+28|0;j=t+24|0;k=t+20|0;l=t+16|0;o=t+12|0;n=t+8|0;m=t+4|0;p=t;q=t+96|0;s=t+64|0;r=t+32|0;c[h>>2]=d;c[j>>2]=e;c[k>>2]=f;c[l>>2]=g;d=_(-5,(c[(c[h>>2]|0)+4556>>2]&65535)<<16>>16)|0;c[m>>2]=3146+(d+(((c[(c[h>>2]|0)+4556>>2]&65535)<<16>>16)*59246>>16));if((c[(c[h>>2]|0)+4604>>2]|0)==2)c[m>>2]=(c[m>>2]|0)+(c[m>>2]>>1);fg(s,c[k>>2]|0,c[(c[h>>2]|0)+4664>>2]|0);if((c[(c[h>>2]|0)+4656>>2]|0)==1)e=(a[(c[h>>2]|0)+4768+31>>0]|0)<4;else e=0;c[n>>2]=e&1;a:do if(c[n>>2]|0){de(q,c[l>>2]|0,c[k>>2]|0,a[(c[h>>2]|0)+4768+31>>0]|0,c[(c[h>>2]|0)+4664>>2]|0);fg(r,q,c[(c[h>>2]|0)+4664>>2]|0);c[p>>2]=(_(a[(c[h>>2]|0)+4768+31>>0]<<16>>16,a[(c[h>>2]|0)+4768+31>>0]<<16>>16)|0)<<11;c[o>>2]=0;while(1){if((c[o>>2]|0)>=(c[(c[h>>2]|0)+4664>>2]|0))break a;d=_(b[r+(c[o>>2]<<1)>>1]>>16,(c[p>>2]&65535)<<16>>16)|0;d=(b[s+(c[o>>2]<<1)>>1]>>1)+(d+((_(b[r+(c[o>>2]<<1)>>1]&65535,(c[p>>2]&65535)<<16>>16)|0)>>16))&65535;b[s+(c[o>>2]<<1)>>1]=d;c[o>>2]=(c[o>>2]|0)+1}}while(0);gf((c[h>>2]|0)+4768+8|0,c[k>>2]|0,c[(c[h>>2]|0)+4724>>2]|0,s,c[m>>2]|0,c[(c[h>>2]|0)+4692>>2]|0,a[(c[h>>2]|0)+4768+29>>0]|0)|0;ag((c[j>>2]|0)+32|0,c[k>>2]|0,c[(c[h>>2]|0)+4664>>2]|0);if(c[n>>2]|0){de(q,c[l>>2]|0,c[k>>2]|0,a[(c[h>>2]|0)+4768+31>>0]|0,c[(c[h>>2]|0)+4664>>2]|0);ag(c[j>>2]|0,q,c[(c[h>>2]|0)+4664>>2]|0);i=t;return}else{pj(c[j>>2]|0,(c[j>>2]|0)+32|0,c[(c[h>>2]|0)+4664>>2]<<1|0)|0;i=t;return}}function qf(d,e,f,g,h,j,k,l,m,n,o){d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;var p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0;V=i;i=i+144|0;J=V+128|0;p=V+124|0;M=V+120|0;w=V+116|0;x=V+112|0;y=V+108|0;z=V+104|0;r=V+100|0;A=V+96|0;F=V+92|0;K=V+88|0;O=V+84|0;v=V+80|0;B=V+76|0;G=V+72|0;H=V+68|0;S=V+64|0;q=V+60|0;E=V+56|0;R=V+48|0;P=V+44|0;Q=V+40|0;t=V+36|0;s=V+32|0;C=V+28|0;u=V+24|0;D=V+20|0;U=V+16|0;T=V+12|0;I=V+8|0;N=V+4|0;L=V;c[J>>2]=d;c[p>>2]=e;c[M>>2]=f;c[w>>2]=g;c[x>>2]=h;c[y>>2]=j;c[z>>2]=k;c[r>>2]=l;c[A>>2]=m;c[F>>2]=n;c[K>>2]=o;c[N>>2]=(c[p>>2]|0)+-4;f=(c[K>>2]|0)+2|0;c[L>>2]=ia()|0;j=i;i=i+((1*(f<<1)|0)+15&-16)|0;c[O>>2]=0;while(1){if((c[O>>2]|0)>=((c[K>>2]|0)+2|0))break;c[S>>2]=(b[(c[p>>2]|0)+((c[O>>2]|0)-2<<1)>>1]|0)+(b[(c[M>>2]|0)+((c[O>>2]|0)-2<<1)>>1]|0);c[q>>2]=(b[(c[p>>2]|0)+((c[O>>2]|0)-2<<1)>>1]|0)-(b[(c[M>>2]|0)+((c[O>>2]|0)-2<<1)>>1]|0);b[(c[N>>2]|0)+(c[O>>2]<<1)>>1]=(c[S>>2]>>1)+(c[S>>2]&1);if(((c[q>>2]>>1)+(c[q>>2]&1)|0)<=32767)if(((c[q>>2]>>1)+(c[q>>2]&1)|0)<-32768)o=-32768;else o=(c[q>>2]>>1)+(c[q>>2]&1)|0;else o=32767;b[j+(c[O>>2]<<1)>>1]=o;c[O>>2]=(c[O>>2]|0)+1}g=c[N>>2]|0;h=(c[J>>2]|0)+4|0;b[g>>1]=b[h>>1]|0;b[g+2>>1]=b[h+2>>1]|0;g=(c[J>>2]|0)+8|0;b[j>>1]=b[g>>1]|0;b[j+2>>1]=b[g+2>>1]|0;g=(c[J>>2]|0)+4|0;h=(c[N>>2]|0)+(c[K>>2]<<1)|0;b[g>>1]=b[h>>1]|0;b[g+2>>1]=b[h+2>>1]|0;g=(c[J>>2]|0)+8|0;h=j+(c[K>>2]<<1)|0;b[g>>1]=b[h>>1]|0;b[g+2>>1]=b[h+2>>1]|0;g=i;i=i+((1*(c[K>>2]<<1)|0)+15&-16)|0;h=i;i=i+((1*(c[K>>2]<<1)|0)+15&-16)|0;c[O>>2]=0;while(1){if((c[O>>2]|0)>=(c[K>>2]|0))break;c[S>>2]=((b[(c[N>>2]|0)+(c[O>>2]<<1)>>1]|0)+(b[(c[N>>2]|0)+((c[O>>2]|0)+2<<1)>>1]|0)+(b[(c[N>>2]|0)+((c[O>>2]|0)+1<<1)>>1]<<1)>>1)+1>>1;b[g+(c[O>>2]<<1)>>1]=c[S>>2];b[h+(c[O>>2]<<1)>>1]=(b[(c[N>>2]|0)+((c[O>>2]|0)+1<<1)>>1]|0)-(c[S>>2]|0);c[O>>2]=(c[O>>2]|0)+1}o=i;i=i+((1*(c[K>>2]<<1)|0)+15&-16)|0;n=i;i=i+((1*(c[K>>2]<<1)|0)+15&-16)|0;c[O>>2]=0;while(1){if((c[O>>2]|0)>=(c[K>>2]|0))break;c[S>>2]=((b[j+(c[O>>2]<<1)>>1]|0)+(b[j+((c[O>>2]|0)+2<<1)>>1]|0)+(b[j+((c[O>>2]|0)+1<<1)>>1]<<1)>>1)+1>>1;b[o+(c[O>>2]<<1)>>1]=c[S>>2];b[n+(c[O>>2]<<1)>>1]=(b[j+((c[O>>2]|0)+1<<1)>>1]|0)-(c[S>>2]|0);c[O>>2]=(c[O>>2]|0)+1}c[v>>2]=(c[K>>2]|0)==((c[F>>2]|0)*10|0)&1;c[E>>2]=c[v>>2]|0?328:655;q=(_((c[r>>2]&65535)<<16>>16,(c[r>>2]&65535)<<16>>16)|0)>>16;q=_(q,(c[E>>2]&65535)<<16>>16)|0;r=(_((c[r>>2]&65535)<<16>>16,(c[r>>2]&65535)<<16>>16)|0)&65535;c[E>>2]=q+((_(r,(c[E>>2]&65535)<<16>>16)|0)>>16);c[R>>2]=Bg(t,g,o,(c[J>>2]|0)+12|0,c[K>>2]|0,c[E>>2]|0)|0;c[R+4>>2]=Bg(s,h,n,(c[J>>2]|0)+12+8|0,c[K>>2]|0,c[E>>2]|0)|0;c[C>>2]=(c[s>>2]|0)+(((c[t>>2]&65535)<<16>>16)*3|0);c[C>>2]=(c[C>>2]|0)<65536?c[C>>2]|0:65536;v=(c[z>>2]|0)-(c[v>>2]|0?1200:600)|0;c[z>>2]=v;c[z>>2]=(c[z>>2]|0)<1?1:v;c[D>>2]=2e3+(((c[F>>2]&65535)<<16>>16)*900|0);c[u>>2]=(c[C>>2]|0)*3;v=rf(c[z>>2]|0,851968+(c[u>>2]|0)|0,19)|0;c[c[y>>2]>>2]=v;if((c[c[y>>2]>>2]|0)<(c[D>>2]|0)){c[c[y>>2]>>2]=c[D>>2];c[(c[y>>2]|0)+4>>2]=(c[z>>2]|0)-(c[c[y>>2]>>2]|0);v=_(65536+(c[u>>2]|0)>>16,(c[D>>2]&65535)<<16>>16)|0;c[U>>2]=rf((c[(c[y>>2]|0)+4>>2]<<1)-(c[D>>2]|0)|0,v+((_(65536+(c[u>>2]|0)&65535,(c[D>>2]&65535)<<16>>16)|0)>>16)|0,16)|0;if((c[U>>2]|0)>16384)o=16384;else o=(c[U>>2]|0)<0?0:c[U>>2]|0;c[U>>2]=o}else{c[(c[y>>2]|0)+4>>2]=(c[z>>2]|0)-(c[c[y>>2]>>2]|0);c[U>>2]=16384}v=_((c[U>>2]|0)-(b[(c[J>>2]|0)+28>>1]|0)>>16,(c[E>>2]&65535)<<16>>16)|0;E=(b[(c[J>>2]|0)+28>>1]|0)+(v+((_((c[U>>2]|0)-(b[(c[J>>2]|0)+28>>1]|0)&65535,(c[E>>2]&65535)<<16>>16)|0)>>16))&65535;b[(c[J>>2]|0)+28>>1]=E;a[c[x>>2]>>0]=0;a:do if(!(c[A>>2]|0)){do if(!(b[(c[J>>2]|0)+30>>1]|0)){if((c[z>>2]<<3|0)>=((c[D>>2]|0)*13|0)?(E=_(c[C>>2]>>16,b[(c[J>>2]|0)+28>>1]|0)|0,(E+((_(c[C>>2]&65535,b[(c[J>>2]|0)+28>>1]|0)|0)>>16)|0)>=819):0)break;c[R>>2]=(_(b[(c[J>>2]|0)+28>>1]|0,(c[R>>2]&65535)<<16>>16)|0)>>14;c[R+4>>2]=(_(b[(c[J>>2]|0)+28>>1]|0,(c[R+4>>2]&65535)<<16>>16)|0)>>14;Ig(R,c[w>>2]|0);c[U>>2]=0;c[R>>2]=0;c[R+4>>2]=0;c[c[y>>2]>>2]=c[z>>2];c[(c[y>>2]|0)+4>>2]=0;a[c[x>>2]>>0]=1;break a}while(0);do if(b[(c[J>>2]|0)+30>>1]|0){if((c[z>>2]<<3|0)>=((c[D>>2]|0)*11|0)?(E=_(c[C>>2]>>16,b[(c[J>>2]|0)+28>>1]|0)|0,(E+((_(c[C>>2]&65535,b[(c[J>>2]|0)+28>>1]|0)|0)>>16)|0)>=328):0)break;c[R>>2]=(_(b[(c[J>>2]|0)+28>>1]|0,(c[R>>2]&65535)<<16>>16)|0)>>14;c[R+4>>2]=(_(b[(c[J>>2]|0)+28>>1]|0,(c[R+4>>2]&65535)<<16>>16)|0)>>14;Ig(R,c[w>>2]|0);c[U>>2]=0;c[R>>2]=0;c[R+4>>2]=0;break a}while(0);if((b[(c[J>>2]|0)+28>>1]|0)>15565){Ig(R,c[w>>2]|0);c[U>>2]=16384;break}else{c[R>>2]=(_(b[(c[J>>2]|0)+28>>1]|0,(c[R>>2]&65535)<<16>>16)|0)>>14;c[R+4>>2]=(_(b[(c[J>>2]|0)+28>>1]|0,(c[R+4>>2]&65535)<<16>>16)|0)>>14;Ig(R,c[w>>2]|0);c[U>>2]=b[(c[J>>2]|0)+28>>1];break}}else{c[U>>2]=0;c[R>>2]=0;c[R+4>>2]=0;Ig(R,c[w>>2]|0)}while(0);do if((a[c[x>>2]>>0]|0)==1){E=(c[J>>2]|0)+32|0;b[E>>1]=(b[E>>1]|0)+((c[K>>2]|0)-(c[F>>2]<<3));if((b[(c[J>>2]|0)+32>>1]|0)<((c[F>>2]|0)*5|0)){a[c[x>>2]>>0]=0;break}else{b[(c[J>>2]|0)+32>>1]=1e4;break}}else b[(c[J>>2]|0)+32>>1]=0;while(0);if((a[c[x>>2]>>0]|0)==0?(c[(c[y>>2]|0)+4>>2]|0)<1:0){c[(c[y>>2]|0)+4>>2]=1;E=tf(1,(c[z>>2]|0)-(c[(c[y>>2]|0)+4>>2]|0)|0)|0;c[c[y>>2]>>2]=E}c[P>>2]=0-(b[c[J>>2]>>1]|0);c[Q>>2]=0-(b[(c[J>>2]|0)+2>>1]|0);c[T>>2]=b[(c[J>>2]|0)+30>>1]<<10;c[B>>2]=65536/(c[F>>2]<<3|0)|0;c[G>>2]=0-(((_(((c[R>>2]|0)-(b[c[J>>2]>>1]|0)&65535)<<16>>16,(c[B>>2]&65535)<<16>>16)|0)>>15)+1>>1);c[H>>2]=0-(((_(((c[R+4>>2]|0)-(b[(c[J>>2]|0)+2>>1]|0)&65535)<<16>>16,(c[B>>2]&65535)<<16>>16)|0)>>15)+1>>1);E=_((c[U>>2]|0)-(b[(c[J>>2]|0)+30>>1]|0)>>16,(c[B>>2]&65535)<<16>>16)|0;c[I>>2]=E+((_((c[U>>2]|0)-(b[(c[J>>2]|0)+30>>1]|0)&65535,(c[B>>2]&65535)<<16>>16)|0)>>16)<<10;c[O>>2]=0;while(1){if((c[O>>2]|0)>=(c[F>>2]<<3|0))break;c[P>>2]=(c[P>>2]|0)+(c[G>>2]|0);c[Q>>2]=(c[Q>>2]|0)+(c[H>>2]|0);c[T>>2]=(c[T>>2]|0)+(c[I>>2]|0);c[S>>2]=(b[(c[N>>2]|0)+(c[O>>2]<<1)>>1]|0)+(b[(c[N>>2]|0)+((c[O>>2]|0)+2<<1)>>1]|0)+(b[(c[N>>2]|0)+((c[O>>2]|0)+1<<1)>>1]<<1)<<9;D=_(c[T>>2]>>16,b[j+((c[O>>2]|0)+1<<1)>>1]|0)|0;D=D+((_(c[T>>2]&65535,b[j+((c[O>>2]|0)+1<<1)>>1]|0)|0)>>16)|0;E=_(c[S>>2]>>16,(c[P>>2]&65535)<<16>>16)|0;c[S>>2]=D+(E+((_(c[S>>2]&65535,(c[P>>2]&65535)<<16>>16)|0)>>16));E=_(b[(c[N>>2]|0)+((c[O>>2]|0)+1<<1)>>1]<<11>>16,(c[Q>>2]&65535)<<16>>16)|0;c[S>>2]=(c[S>>2]|0)+(E+((_(b[(c[N>>2]|0)+((c[O>>2]|0)+1<<1)>>1]<<11&65535,(c[Q>>2]&65535)<<16>>16)|0)>>16));if(((c[S>>2]>>7)+1>>1|0)<=32767)if(((c[S>>2]>>7)+1>>1|0)<-32768)o=-32768;else o=(c[S>>2]>>7)+1>>1;else o=32767;b[(c[M>>2]|0)+((c[O>>2]|0)-1<<1)>>1]=o;c[O>>2]=(c[O>>2]|0)+1}c[P>>2]=0-(c[R>>2]|0);c[Q>>2]=0-(c[R+4>>2]|0);c[T>>2]=c[U>>2]<<10;c[O>>2]=c[F>>2]<<3;while(1){if((c[O>>2]|0)>=(c[K>>2]|0))break;c[S>>2]=(b[(c[N>>2]|0)+(c[O>>2]<<1)>>1]|0)+(b[(c[N>>2]|0)+((c[O>>2]|0)+2<<1)>>1]|0)+(b[(c[N>>2]|0)+((c[O>>2]|0)+1<<1)>>1]<<1)<<9;H=_(c[T>>2]>>16,b[j+((c[O>>2]|0)+1<<1)>>1]|0)|0;H=H+((_(c[T>>2]&65535,b[j+((c[O>>2]|0)+1<<1)>>1]|0)|0)>>16)|0;I=_(c[S>>2]>>16,(c[P>>2]&65535)<<16>>16)|0;c[S>>2]=H+(I+((_(c[S>>2]&65535,(c[P>>2]&65535)<<16>>16)|0)>>16));I=_(b[(c[N>>2]|0)+((c[O>>2]|0)+1<<1)>>1]<<11>>16,(c[Q>>2]&65535)<<16>>16)|0;c[S>>2]=(c[S>>2]|0)+(I+((_(b[(c[N>>2]|0)+((c[O>>2]|0)+1<<1)>>1]<<11&65535,(c[Q>>2]&65535)<<16>>16)|0)>>16));if(((c[S>>2]>>7)+1>>1|0)<=32767)if(((c[S>>2]>>7)+1>>1|0)<-32768)o=-32768;else o=(c[S>>2]>>7)+1>>1;else o=32767;b[(c[M>>2]|0)+((c[O>>2]|0)-1<<1)>>1]=o;c[O>>2]=(c[O>>2]|0)+1}b[c[J>>2]>>1]=c[R>>2];b[(c[J>>2]|0)+2>>1]=c[R+4>>2];b[(c[J>>2]|0)+30>>1]=c[U>>2];na(c[L>>2]|0);i=V;return}function rf(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;h=i;i=i+48|0;f=h+40|0;q=h+36|0;p=h+32|0;j=h+28|0;l=h+24|0;k=h+20|0;g=h+16|0;m=h+12|0;n=h+8|0;o=h+4|0;e=h;c[q>>2]=a;c[p>>2]=b;c[j>>2]=d;b=c[q>>2]|0;c[l>>2]=(sf((c[q>>2]|0)>0?b:0-b|0)|0)-1;c[n>>2]=c[q>>2]<<c[l>>2];b=c[p>>2]|0;c[k>>2]=(sf((c[p>>2]|0)>0?b:0-b|0)|0)-1;c[o>>2]=c[p>>2]<<c[k>>2];c[m>>2]=536870911/(c[o>>2]>>16|0)|0;b=_(c[n>>2]>>16,(c[m>>2]&65535)<<16>>16)|0;c[e>>2]=b+((_(c[n>>2]&65535,(c[m>>2]&65535)<<16>>16)|0)>>16);b=c[n>>2]|0;a=c[o>>2]|0;d=c[e>>2]|0;d=yj(a|0,((a|0)<0)<<31>>31|0,d|0,((d|0)<0)<<31>>31|0)|0;d=nj(d|0,C|0,32)|0;c[n>>2]=b-(d<<3);d=_(c[n>>2]>>16,(c[m>>2]&65535)<<16>>16)|0;c[e>>2]=(c[e>>2]|0)+(d+((_(c[n>>2]&65535,(c[m>>2]&65535)<<16>>16)|0)>>16));c[g>>2]=29+(c[l>>2]|0)-(c[k>>2]|0)-(c[j>>2]|0);d=c[g>>2]|0;if((c[g>>2]|0)>=0)if((d|0)<32){c[f>>2]=c[e>>2]>>c[g>>2];q=c[f>>2]|0;i=h;return q|0}else{c[f>>2]=0;q=c[f>>2]|0;i=h;return q|0}a=c[e>>2]|0;b=0-(c[g>>2]|0)|0;do if((-2147483648>>0-d|0)>(2147483647>>0-(c[g>>2]|0)|0)){if((a|0)>(-2147483648>>b|0)){d=-2147483648>>0-(c[g>>2]|0);break}if((c[e>>2]|0)<(2147483647>>0-(c[g>>2]|0)|0)){d=2147483647>>0-(c[g>>2]|0);break}else{d=c[e>>2]|0;break}}else{if((a|0)>(2147483647>>b|0)){d=2147483647>>0-(c[g>>2]|0);break}if((c[e>>2]|0)<(-2147483648>>0-(c[g>>2]|0)|0)){d=-2147483648>>0-(c[g>>2]|0);break}else{d=c[e>>2]|0;break}}while(0);c[f>>2]=d<<0-(c[g>>2]|0);q=c[f>>2]|0;i=h;return q|0}function sf(a){a=a|0;var b=0,d=0;d=i;i=i+16|0;b=d;c[b>>2]=a;if(!(c[b>>2]|0)){a=32;i=d;return a|0}a=32-(32-(aa(c[b>>2]|0)|0))|0;i=d;return a|0}function tf(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)>(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function uf(a,d,e,f,g,h){a=a|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0;w=i;i=i+64|0;m=w+52|0;q=w+48|0;r=w+44|0;n=w+40|0;j=w+36|0;s=w+32|0;u=w+28|0;x=w+24|0;k=w+20|0;l=w+16|0;v=w+12|0;t=w+8|0;o=w+4|0;p=w;c[m>>2]=a;c[q>>2]=d;c[r>>2]=e;c[n>>2]=f;c[j>>2]=g;c[s>>2]=h;e=c[q>>2]|0;d=(c[m>>2]|0)+4|0;b[e>>1]=b[d>>1]|0;b[e+2>>1]=b[d+2>>1]|0;e=c[r>>2]|0;d=(c[m>>2]|0)+8|0;b[e>>1]=b[d>>1]|0;b[e+2>>1]=b[d+2>>1]|0;e=(c[m>>2]|0)+4|0;d=(c[q>>2]|0)+(c[s>>2]<<1)|0;b[e>>1]=b[d>>1]|0;b[e+2>>1]=b[d+2>>1]|0;e=(c[m>>2]|0)+8|0;d=(c[r>>2]|0)+(c[s>>2]<<1)|0;b[e>>1]=b[d>>1]|0;b[e+2>>1]=b[d+2>>1]|0;c[o>>2]=b[c[m>>2]>>1];c[p>>2]=b[(c[m>>2]|0)+2>>1];c[x>>2]=65536/(c[j>>2]<<3|0)|0;c[k>>2]=((_(((c[c[n>>2]>>2]|0)-(b[c[m>>2]>>1]|0)&65535)<<16>>16,(c[x>>2]&65535)<<16>>16)|0)>>15)+1>>1;c[l>>2]=((_(((c[(c[n>>2]|0)+4>>2]|0)-(b[(c[m>>2]|0)+2>>1]|0)&65535)<<16>>16,(c[x>>2]&65535)<<16>>16)|0)>>15)+1>>1;c[u>>2]=0;while(1){if((c[u>>2]|0)>=(c[j>>2]<<3|0))break;c[o>>2]=(c[o>>2]|0)+(c[k>>2]|0);c[p>>2]=(c[p>>2]|0)+(c[l>>2]|0);c[v>>2]=(b[(c[q>>2]|0)+(c[u>>2]<<1)>>1]|0)+(b[(c[q>>2]|0)+((c[u>>2]|0)+2<<1)>>1]|0)+(b[(c[q>>2]|0)+((c[u>>2]|0)+1<<1)>>1]<<1)<<9;x=_(c[v>>2]>>16,(c[o>>2]&65535)<<16>>16)|0;c[v>>2]=(b[(c[r>>2]|0)+((c[u>>2]|0)+1<<1)>>1]<<8)+(x+((_(c[v>>2]&65535,(c[o>>2]&65535)<<16>>16)|0)>>16));x=_(b[(c[q>>2]|0)+((c[u>>2]|0)+1<<1)>>1]<<11>>16,(c[p>>2]&65535)<<16>>16)|0;c[v>>2]=(c[v>>2]|0)+(x+((_(b[(c[q>>2]|0)+((c[u>>2]|0)+1<<1)>>1]<<11&65535,(c[p>>2]&65535)<<16>>16)|0)>>16));if(((c[v>>2]>>7)+1>>1|0)<=32767)if(((c[v>>2]>>7)+1>>1|0)<-32768)h=-32768;else h=(c[v>>2]>>7)+1>>1;else h=32767;b[(c[r>>2]|0)+((c[u>>2]|0)+1<<1)>>1]=h;c[u>>2]=(c[u>>2]|0)+1}c[o>>2]=c[c[n>>2]>>2];c[p>>2]=c[(c[n>>2]|0)+4>>2];c[u>>2]=c[j>>2]<<3;while(1){if((c[u>>2]|0)>=(c[s>>2]|0))break;c[v>>2]=(b[(c[q>>2]|0)+(c[u>>2]<<1)>>1]|0)+(b[(c[q>>2]|0)+((c[u>>2]|0)+2<<1)>>1]|0)+(b[(c[q>>2]|0)+((c[u>>2]|0)+1<<1)>>1]<<1)<<9;x=_(c[v>>2]>>16,(c[o>>2]&65535)<<16>>16)|0;c[v>>2]=(b[(c[r>>2]|0)+((c[u>>2]|0)+1<<1)>>1]<<8)+(x+((_(c[v>>2]&65535,(c[o>>2]&65535)<<16>>16)|0)>>16));x=_(b[(c[q>>2]|0)+((c[u>>2]|0)+1<<1)>>1]<<11>>16,(c[p>>2]&65535)<<16>>16)|0;c[v>>2]=(c[v>>2]|0)+(x+((_(b[(c[q>>2]|0)+((c[u>>2]|0)+1<<1)>>1]<<11&65535,(c[p>>2]&65535)<<16>>16)|0)>>16));if(((c[v>>2]>>7)+1>>1|0)<=32767)if(((c[v>>2]>>7)+1>>1|0)<-32768)h=-32768;else h=(c[v>>2]>>7)+1>>1;else h=32767;b[(c[r>>2]|0)+((c[u>>2]|0)+1<<1)>>1]=h;c[u>>2]=(c[u>>2]|0)+1}b[c[m>>2]>>1]=c[c[n>>2]>>2];b[(c[m>>2]|0)+2>>1]=c[(c[n>>2]|0)+4>>2];c[u>>2]=0;while(1){if((c[u>>2]|0)>=(c[s>>2]|0))break;c[v>>2]=(b[(c[q>>2]|0)+((c[u>>2]|0)+1<<1)>>1]|0)+(b[(c[r>>2]|0)+((c[u>>2]|0)+1<<1)>>1]|0);c[t>>2]=(b[(c[q>>2]|0)+((c[u>>2]|0)+1<<1)>>1]|0)-(b[(c[r>>2]|0)+((c[u>>2]|0)+1<<1)>>1]|0);if((c[v>>2]|0)>32767)h=32767;else h=(c[v>>2]|0)<-32768?-32768:c[v>>2]|0;b[(c[q>>2]|0)+((c[u>>2]|0)+1<<1)>>1]=h;if((c[t>>2]|0)>32767)h=32767;else h=(c[t>>2]|0)<-32768?-32768:c[t>>2]|0;b[(c[r>>2]|0)+((c[u>>2]|0)+1<<1)>>1]=h;c[u>>2]=(c[u>>2]|0)+1}i=w;return}function vf(a){a=a|0;var b=0,d=0,e=0,f=0;f=i;i=i+16|0;b=f+4|0;d=f;c[d>>2]=a;if(!(((((((c[(c[d>>2]|0)+8>>2]|0)!=8e3?(c[(c[d>>2]|0)+8>>2]|0)!=12e3:0)?(c[(c[d>>2]|0)+8>>2]|0)!=16e3:0)?(c[(c[d>>2]|0)+8>>2]|0)!=24e3:0)?(c[(c[d>>2]|0)+8>>2]|0)!=32e3:0)?(c[(c[d>>2]|0)+8>>2]|0)!=44100:0)?(c[(c[d>>2]|0)+8>>2]|0)!=48e3:0))e=8;do if((e|0)==8){if(((c[(c[d>>2]|0)+20>>2]|0)!=8e3?(c[(c[d>>2]|0)+20>>2]|0)!=12e3:0)?(c[(c[d>>2]|0)+20>>2]|0)!=16e3:0)break;if(((c[(c[d>>2]|0)+12>>2]|0)!=8e3?(c[(c[d>>2]|0)+12>>2]|0)!=12e3:0)?(c[(c[d>>2]|0)+12>>2]|0)!=16e3:0)break;if(((c[(c[d>>2]|0)+16>>2]|0)!=8e3?(c[(c[d>>2]|0)+16>>2]|0)!=12e3:0)?(c[(c[d>>2]|0)+16>>2]|0)!=16e3:0)break;if(((c[(c[d>>2]|0)+16>>2]|0)<=(c[(c[d>>2]|0)+20>>2]|0)?(c[(c[d>>2]|0)+12>>2]|0)>=(c[(c[d>>2]|0)+20>>2]|0):0)?(c[(c[d>>2]|0)+16>>2]|0)<=(c[(c[d>>2]|0)+12>>2]|0):0){if((((c[(c[d>>2]|0)+24>>2]|0)!=10?(c[(c[d>>2]|0)+24>>2]|0)!=20:0)?(c[(c[d>>2]|0)+24>>2]|0)!=40:0)?(c[(c[d>>2]|0)+24>>2]|0)!=60:0){c[b>>2]=-103;e=c[b>>2]|0;i=f;return e|0}if((c[(c[d>>2]|0)+32>>2]|0)>=0?(c[(c[d>>2]|0)+32>>2]|0)<=100:0){if((c[(c[d>>2]|0)+44>>2]|0)>=0?(c[(c[d>>2]|0)+44>>2]|0)<=1:0){if((c[(c[d>>2]|0)+48>>2]|0)>=0?(c[(c[d>>2]|0)+48>>2]|0)<=1:0){do if((c[(c[d>>2]|0)+40>>2]|0)>=0){if((c[(c[d>>2]|0)+40>>2]|0)>1)break;do if((c[c[d>>2]>>2]|0)>=1){if((c[c[d>>2]>>2]|0)>2)break;do if((c[(c[d>>2]|0)+4>>2]|0)>=1){if((c[(c[d>>2]|0)+4>>2]|0)>2)break;if((c[(c[d>>2]|0)+4>>2]|0)>(c[c[d>>2]>>2]|0)){c[b>>2]=-111;e=c[b>>2]|0;i=f;return e|0}do if((c[(c[d>>2]|0)+36>>2]|0)>=0){if((c[(c[d>>2]|0)+36>>2]|0)>10)break;c[b>>2]=0;e=c[b>>2]|0;i=f;return e|0}while(0);c[b>>2]=-106;e=c[b>>2]|0;i=f;return e|0}while(0);c[b>>2]=-111;e=c[b>>2]|0;i=f;return e|0}while(0);c[b>>2]=-111;e=c[b>>2]|0;i=f;return e|0}while(0);c[b>>2]=-107;e=c[b>>2]|0;i=f;return e|0}c[b>>2]=-109;e=c[b>>2]|0;i=f;return e|0}c[b>>2]=-108;e=c[b>>2]|0;i=f;return e|0}c[b>>2]=-105;e=c[b>>2]|0;i=f;return e|0}}while(0);c[b>>2]=-102;e=c[b>>2]|0;i=f;return e|0}function wf(a,d){a=a|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;m=i;i=i+32|0;e=m+20|0;f=m+16|0;h=m+12|0;k=m+8|0;g=m+4|0;j=m;c[e>>2]=a;c[f>>2]=d;c[k>>2]=0;if((c[f>>2]|0)>8e4)d=8e4;else d=(c[f>>2]|0)<5e3?5e3:c[f>>2]|0;c[f>>2]=d;if((c[f>>2]|0)==(c[(c[e>>2]|0)+4632>>2]|0)){l=c[k>>2]|0;i=m;return l|0}c[(c[e>>2]|0)+4632>>2]=c[f>>2];do if((c[(c[e>>2]|0)+4600>>2]|0)!=8)if((c[(c[e>>2]|0)+4600>>2]|0)==12){c[j>>2]=17772;break}else{c[j>>2]=17804;break}else c[j>>2]=17740;while(0);if((c[(c[e>>2]|0)+4604>>2]|0)==2)c[f>>2]=(c[f>>2]|0)-2200;c[h>>2]=1;while(1){if((c[h>>2]|0)>=8){l=16;break}if((c[f>>2]|0)<=(c[(c[j>>2]|0)+(c[h>>2]<<2)>>2]|0))break;c[h>>2]=(c[h>>2]|0)+1}if((l|0)==16){l=c[k>>2]|0;i=m;return l|0}c[g>>2]=((c[f>>2]|0)-(c[(c[j>>2]|0)+((c[h>>2]|0)-1<<2)>>2]|0)<<6|0)/((c[(c[j>>2]|0)+(c[h>>2]<<2)>>2]|0)-(c[(c[j>>2]|0)+((c[h>>2]|0)-1<<2)>>2]|0)|0)|0;l=(b[24510+((c[h>>2]|0)-1<<1)>>1]<<6)+(_(c[g>>2]|0,(b[24510+(c[h>>2]<<1)>>1]|0)-(b[24510+((c[h>>2]|0)-1<<1)>>1]|0)|0)|0)|0;c[(c[e>>2]|0)+4748>>2]=l;l=c[k>>2]|0;i=m;return l|0}function xf(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0;e=i;i=i+16|0;f=e+8|0;g=e+4|0;d=e;c[f>>2]=a;c[g>>2]=b;c[d>>2]=0;oj(c[f>>2]|0,0,12240)|0;c[(c[f>>2]|0)+5124>>2]=c[g>>2];a=(Sf(3932160)|0)-2048<<8;c[(c[f>>2]|0)+8>>2]=a;c[(c[f>>2]|0)+12>>2]=c[(c[f>>2]|0)+8>>2];c[(c[f>>2]|0)+4696>>2]=1;a=Ue((c[f>>2]|0)+32|0)|0;c[d>>2]=(c[d>>2]|0)+a;i=e;return c[d>>2]|0}function yf(a,b,d,e,f,g){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;p=i;i=i+48|0;h=p+32|0;j=p+28|0;k=p+24|0;l=p+20|0;r=p+16|0;q=p+12|0;m=p+8|0;n=p+4|0;o=p;c[j>>2]=a;c[k>>2]=b;c[l>>2]=d;c[r>>2]=e;c[q>>2]=f;c[m>>2]=g;c[o>>2]=0;c[(c[j>>2]|0)+6108>>2]=c[(c[k>>2]|0)+44>>2];c[(c[j>>2]|0)+4708>>2]=c[(c[k>>2]|0)+48>>2];c[(c[j>>2]|0)+4580>>2]=c[(c[k>>2]|0)+8>>2];c[(c[j>>2]|0)+4588>>2]=c[(c[k>>2]|0)+12>>2];c[(c[j>>2]|0)+4592>>2]=c[(c[k>>2]|0)+16>>2];c[(c[j>>2]|0)+4596>>2]=c[(c[k>>2]|0)+20>>2];c[(c[j>>2]|0)+6120>>2]=c[(c[k>>2]|0)+40>>2];c[(c[j>>2]|0)+5784>>2]=c[c[k>>2]>>2];c[(c[j>>2]|0)+5788>>2]=c[(c[k>>2]|0)+4>>2];c[(c[j>>2]|0)+4560>>2]=c[r>>2];c[(c[j>>2]|0)+5792>>2]=c[q>>2];if(c[(c[j>>2]|0)+4700>>2]|0?(c[(c[j>>2]|0)+4712>>2]|0)==0:0){if((c[(c[j>>2]|0)+4580>>2]|0)!=(c[(c[j>>2]|0)+4584>>2]|0)?(c[(c[j>>2]|0)+4600>>2]|0)>0:0){r=zf(c[j>>2]|0,c[(c[j>>2]|0)+4600>>2]|0)|0;c[o>>2]=(c[o>>2]|0)+r}c[h>>2]=c[o>>2];r=c[h>>2]|0;i=p;return r|0}c[n>>2]=cf(c[j>>2]|0,c[k>>2]|0)|0;if(c[m>>2]|0)c[n>>2]=c[m>>2];r=zf(c[j>>2]|0,c[n>>2]|0)|0;c[o>>2]=(c[o>>2]|0)+r;r=Cf(c[j>>2]|0,c[n>>2]|0,c[(c[k>>2]|0)+24>>2]|0)|0;c[o>>2]=(c[o>>2]|0)+r;r=Df(c[j>>2]|0,c[(c[k>>2]|0)+36>>2]|0)|0;c[o>>2]=(c[o>>2]|0)+r;c[(c[j>>2]|0)+4640>>2]=c[(c[k>>2]|0)+32>>2];r=Ff(c[j>>2]|0,c[l>>2]|0)|0;c[o>>2]=(c[o>>2]|0)+r;c[(c[j>>2]|0)+4700>>2]=1;c[h>>2]=c[o>>2];r=c[h>>2]|0;i=p;return r|0}function zf(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0;n=i;i=i+336|0;d=n+328|0;e=n+324|0;l=n+320|0;j=n+316|0;g=n+312|0;k=n+308|0;h=n+304|0;f=n+300|0;m=n;c[d>>2]=a;c[e>>2]=b;c[l>>2]=0;if((c[(c[d>>2]|0)+4600>>2]|0)==(c[e>>2]|0)?(c[(c[d>>2]|0)+4584>>2]|0)==(c[(c[d>>2]|0)+4580>>2]|0):0){k=c[d>>2]|0;k=k+4580|0;k=c[k>>2]|0;m=c[d>>2]|0;m=m+4584|0;c[m>>2]=k;m=c[l>>2]|0;i=n;return m|0}b=c[d>>2]|0;if(!(c[(c[d>>2]|0)+4600>>2]|0)){k=ig(b+5808|0,c[(c[d>>2]|0)+4580>>2]|0,(c[e>>2]|0)*1e3|0,1)|0;c[l>>2]=(c[l>>2]|0)+k;k=c[d>>2]|0;k=k+4580|0;k=c[k>>2]|0;m=c[d>>2]|0;m=m+4584|0;c[m>>2]=k;m=c[l>>2]|0;i=n;return m|0}else{c[h>>2]=((c[b+4604>>2]|0)*5<<1)+5;c[k>>2]=_(c[h>>2]|0,c[(c[d>>2]|0)+4600>>2]|0)|0;c[j>>2]=_(c[h>>2]|0,c[e>>2]|0)|0;b=(c[k>>2]|0)>(c[j>>2]|0)?c[k>>2]|0:c[j>>2]|0;c[f>>2]=ia()|0;a=i;i=i+((1*(b<<1)|0)+15&-16)|0;Af(a,(c[d>>2]|0)+9356|0,c[k>>2]|0);b=ig(m,((c[(c[d>>2]|0)+4600>>2]&65535)<<16>>16)*1e3|0,c[(c[d>>2]|0)+4580>>2]|0,0)|0;c[l>>2]=(c[l>>2]|0)+b;c[g>>2]=_(c[h>>2]|0,(c[(c[d>>2]|0)+4580>>2]|0)/1e3|0)|0;h=i;i=i+((1*(c[g>>2]<<1)|0)+15&-16)|0;k=jg(m,h,a,c[k>>2]|0)|0;c[l>>2]=(c[l>>2]|0)+k;k=ig((c[d>>2]|0)+5808|0,c[(c[d>>2]|0)+4580>>2]|0,((c[e>>2]&65535)<<16>>16)*1e3|0,1)|0;c[l>>2]=(c[l>>2]|0)+k;k=jg((c[d>>2]|0)+5808|0,a,h,c[g>>2]|0)|0;c[l>>2]=(c[l>>2]|0)+k;Bf((c[d>>2]|0)+9356|0,a,c[j>>2]|0);na(c[f>>2]|0);k=c[d>>2]|0;k=k+4580|0;k=c[k>>2]|0;m=c[d>>2]|0;m=m+4584|0;c[m>>2]=k;m=c[l>>2]|0;i=n;return m|0}return 0}function Af(a,d,e){a=a|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0;k=i;i=i+16|0;f=k+12|0;h=k+8|0;l=k+4|0;j=k;c[f>>2]=a;c[h>>2]=d;c[l>>2]=e;c[j>>2]=(c[l>>2]|0)-1;while(1){if((c[j>>2]|0)<0)break;if((ij(+g[(c[h>>2]|0)+(c[j>>2]<<2)>>2])|0)<=32767)if((ij(+g[(c[h>>2]|0)+(c[j>>2]<<2)>>2])|0)<-32768)d=-32768;else d=ij(+g[(c[h>>2]|0)+(c[j>>2]<<2)>>2])|0;else d=32767;b[(c[f>>2]|0)+(c[j>>2]<<1)>>1]=d;c[j>>2]=(c[j>>2]|0)+-1}i=k;return}function Bf(a,d,e){a=a|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0;k=i;i=i+16|0;f=k+12|0;h=k+8|0;l=k+4|0;j=k;c[f>>2]=a;c[h>>2]=d;c[l>>2]=e;c[j>>2]=(c[l>>2]|0)-1;while(1){if((c[j>>2]|0)<0)break;g[(c[f>>2]|0)+(c[j>>2]<<2)>>2]=+(b[(c[h>>2]|0)+(c[j>>2]<<1)>>1]|0);c[j>>2]=(c[j>>2]|0)+-1}i=k;return}function Cf(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0;k=i;i=i+16|0;h=k+12|0;g=k+8|0;f=k+4|0;j=k;c[h>>2]=b;c[g>>2]=d;c[f>>2]=e;c[j>>2]=0;if((c[f>>2]|0)!=(c[(c[h>>2]|0)+4636>>2]|0)){if((c[f>>2]|0)!=10&(c[f>>2]|0)!=20&(c[f>>2]|0)!=40&(c[f>>2]|0)!=60)c[j>>2]=-103;do if((c[f>>2]|0)<=10){c[(c[h>>2]|0)+5776>>2]=1;c[(c[h>>2]|0)+4604>>2]=(c[f>>2]|0)==10?2:1;e=_((c[f>>2]&65535)<<16>>16,(c[g>>2]&65535)<<16>>16)|0;c[(c[h>>2]|0)+4608>>2]=e;c[(c[h>>2]|0)+4572>>2]=((c[g>>2]&65535)<<16>>16)*14;e=(c[h>>2]|0)+4720|0;if((c[(c[h>>2]|0)+4600>>2]|0)==8){c[e>>2]=29174;break}else{c[e>>2]=29162;break}}else{c[(c[h>>2]|0)+5776>>2]=(c[f>>2]|0)/20|0;c[(c[h>>2]|0)+4604>>2]=4;c[(c[h>>2]|0)+4608>>2]=((c[g>>2]&65535)<<16>>16)*20;c[(c[h>>2]|0)+4572>>2]=((c[g>>2]&65535)<<16>>16)*24;e=(c[h>>2]|0)+4720|0;if((c[(c[h>>2]|0)+4600>>2]|0)==8){c[e>>2]=29151;break}else{c[e>>2]=29117;break}}while(0);c[(c[h>>2]|0)+4636>>2]=c[f>>2];c[(c[h>>2]|0)+4632>>2]=0}if((c[(c[h>>2]|0)+4600>>2]|0)==(c[g>>2]|0)){j=c[j>>2]|0;i=k;return j|0}e=(c[h>>2]|0)+7200|0;c[e>>2]=0;c[e+4>>2]=0;c[e+8>>2]=0;c[e+12>>2]=0;oj((c[h>>2]|0)+7216|0,0,2140)|0;oj((c[h>>2]|0)+144|0,0,4380)|0;e=(c[h>>2]|0)+4524|0;c[e>>2]=0;c[e+4>>2]=0;c[e+8>>2]=0;c[e+12>>2]=0;c[e+16>>2]=0;c[e+20>>2]=0;c[e+24>>2]=0;c[e+28>>2]=0;e=(c[h>>2]|0)+16|0;c[e>>2]=0;c[e+4>>2]=0;c[(c[h>>2]|0)+5772>>2]=0;c[(c[h>>2]|0)+5780>>2]=0;c[(c[h>>2]|0)+4632>>2]=0;c[(c[h>>2]|0)+4568>>2]=100;c[(c[h>>2]|0)+4696>>2]=1;c[(c[h>>2]|0)+7216+2136>>2]=100;a[(c[h>>2]|0)+7200>>0]=10;c[(c[h>>2]|0)+144+4356>>2]=100;c[(c[h>>2]|0)+144+4372>>2]=65536;a[(c[h>>2]|0)+4565>>0]=0;c[(c[h>>2]|0)+4600>>2]=c[g>>2];e=(c[(c[h>>2]|0)+4604>>2]|0)==4;d=(c[h>>2]|0)+4720|0;do if((c[(c[h>>2]|0)+4600>>2]|0)==8)if(e){c[d>>2]=29151;break}else{c[d>>2]=29174;break}else if(e){c[d>>2]=29117;break}else{c[d>>2]=29162;break}while(0);if((c[(c[h>>2]|0)+4600>>2]|0)!=8?(c[(c[h>>2]|0)+4600>>2]|0)!=12:0){c[(c[h>>2]|0)+4664>>2]=16;c[(c[h>>2]|0)+4724>>2]=17704}else{c[(c[h>>2]|0)+4664>>2]=10;c[(c[h>>2]|0)+4724>>2]=17668}c[(c[h>>2]|0)+4612>>2]=(c[g>>2]|0)*5;e=_((c[(c[h>>2]|0)+4612>>2]&65535)<<16>>16,(c[(c[h>>2]|0)+4604>>2]&65535)<<16>>16)|0;c[(c[h>>2]|0)+4608>>2]=e;c[(c[h>>2]|0)+4616>>2]=((c[g>>2]&65535)<<16>>16)*20;c[(c[h>>2]|0)+4620>>2]=(c[g>>2]&65535)<<16>>16<<1;c[(c[h>>2]|0)+4576>>2]=((c[g>>2]&65535)<<16>>16)*18;e=(c[g>>2]&65535)<<16>>16;if((c[(c[h>>2]|0)+4604>>2]|0)==4)c[(c[h>>2]|0)+4572>>2]=e*24;else c[(c[h>>2]|0)+4572>>2]=e*14;e=c[h>>2]|0;if((c[(c[h>>2]|0)+4600>>2]|0)==16){c[e+4684>>2]=10;c[(c[h>>2]|0)+4716>>2]=29049;j=c[j>>2]|0;i=k;return j|0}d=(c[h>>2]|0)+4684|0;if((c[e+4600>>2]|0)==12){c[d>>2]=13;c[(c[h>>2]|0)+4716>>2]=29043;j=c[j>>2]|0;i=k;return j|0}else{c[d>>2]=15;c[(c[h>>2]|0)+4716>>2]=29034;j=c[j>>2]|0;i=k;return j|0}return 0}function Df(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0;g=i;i=i+16|0;d=g+8|0;e=g+4|0;f=g;c[d>>2]=a;c[e>>2]=b;c[f>>2]=0;do if((c[e>>2]|0)>=2){if((c[e>>2]|0)<4){c[(c[d>>2]|0)+4668>>2]=1;c[(c[d>>2]|0)+4676>>2]=49807;c[(c[d>>2]|0)+4672>>2]=8;c[(c[d>>2]|0)+4660>>2]=10;c[(c[d>>2]|0)+4624>>2]=(c[(c[d>>2]|0)+4600>>2]|0)*5;c[(c[d>>2]|0)+4652>>2]=1;c[(c[d>>2]|0)+4656>>2]=0;c[(c[d>>2]|0)+4680>>2]=0;c[(c[d>>2]|0)+4692>>2]=4;c[(c[d>>2]|0)+4704>>2]=0;break}if((c[e>>2]|0)<6){c[(c[d>>2]|0)+4668>>2]=1;c[(c[d>>2]|0)+4676>>2]=48497;c[(c[d>>2]|0)+4672>>2]=10;c[(c[d>>2]|0)+4660>>2]=12;c[(c[d>>2]|0)+4624>>2]=(c[(c[d>>2]|0)+4600>>2]|0)*5;c[(c[d>>2]|0)+4652>>2]=2;c[(c[d>>2]|0)+4656>>2]=1;c[(c[d>>2]|0)+4680>>2]=0;c[(c[d>>2]|0)+4692>>2]=8;c[(c[d>>2]|0)+4704>>2]=(c[(c[d>>2]|0)+4600>>2]|0)*983;break}b=(c[d>>2]|0)+4668|0;if((c[e>>2]|0)<8){c[b>>2]=1;c[(c[d>>2]|0)+4676>>2]=47186;c[(c[d>>2]|0)+4672>>2]=12;c[(c[d>>2]|0)+4660>>2]=14;c[(c[d>>2]|0)+4624>>2]=(c[(c[d>>2]|0)+4600>>2]|0)*5;c[(c[d>>2]|0)+4652>>2]=3;c[(c[d>>2]|0)+4656>>2]=1;c[(c[d>>2]|0)+4680>>2]=0;c[(c[d>>2]|0)+4692>>2]=16;c[(c[d>>2]|0)+4704>>2]=(c[(c[d>>2]|0)+4600>>2]|0)*983;break}else{c[b>>2]=2;c[(c[d>>2]|0)+4676>>2]=45875;c[(c[d>>2]|0)+4672>>2]=16;c[(c[d>>2]|0)+4660>>2]=16;c[(c[d>>2]|0)+4624>>2]=(c[(c[d>>2]|0)+4600>>2]|0)*5;c[(c[d>>2]|0)+4652>>2]=4;c[(c[d>>2]|0)+4656>>2]=1;c[(c[d>>2]|0)+4680>>2]=0;c[(c[d>>2]|0)+4692>>2]=32;c[(c[d>>2]|0)+4704>>2]=(c[(c[d>>2]|0)+4600>>2]|0)*983;break}}else{c[(c[d>>2]|0)+4668>>2]=0;c[(c[d>>2]|0)+4676>>2]=52429;c[(c[d>>2]|0)+4672>>2]=6;c[(c[d>>2]|0)+4660>>2]=8;c[(c[d>>2]|0)+4624>>2]=(c[(c[d>>2]|0)+4600>>2]|0)*3;c[(c[d>>2]|0)+4652>>2]=1;c[(c[d>>2]|0)+4656>>2]=0;c[(c[d>>2]|0)+4680>>2]=1;c[(c[d>>2]|0)+4692>>2]=2;c[(c[d>>2]|0)+4704>>2]=0}while(0);a=Ef(c[(c[d>>2]|0)+4672>>2]|0,c[(c[d>>2]|0)+4664>>2]|0)|0;c[(c[d>>2]|0)+4672>>2]=a;c[(c[d>>2]|0)+4628>>2]=((c[(c[d>>2]|0)+4600>>2]|0)*5|0)+(c[(c[d>>2]|0)+4624>>2]<<1);c[(c[d>>2]|0)+4648>>2]=c[e>>2];i=g;return c[f>>2]|0}function Ef(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)<(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function Ff(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0;j=i;i=i+32|0;f=j+16|0;d=j+12|0;g=j+8|0;h=j+4|0;e=j;c[f>>2]=a;c[d>>2]=b;c[h>>2]=0;c[g>>2]=c[(c[f>>2]|0)+6124>>2];c[(c[f>>2]|0)+6124>>2]=0;if(!(c[(c[f>>2]|0)+6120>>2]|0)){h=c[h>>2]|0;i=j;return h|0}if((c[(c[f>>2]|0)+4640>>2]|0)<=0){h=c[h>>2]|0;i=j;return h|0}do if((c[(c[f>>2]|0)+4600>>2]|0)!=8)if((c[(c[f>>2]|0)+4600>>2]|0)==12){c[e>>2]=14e3;break}else{c[e>>2]=16e3;break}else c[e>>2]=12e3;while(0);if((c[(c[f>>2]|0)+4640>>2]|0)<25)b=c[(c[f>>2]|0)+4640>>2]|0;else b=25;b=((_(c[e>>2]|0,125-b|0)|0)>>16)*655|0;if((c[(c[f>>2]|0)+4640>>2]|0)<25)a=c[(c[f>>2]|0)+4640>>2]|0;else a=25;c[e>>2]=b+(((_(c[e>>2]|0,125-a|0)|0)&65535)*655>>16);if((c[d>>2]|0)<=(c[e>>2]|0)){h=c[h>>2]|0;i=j;return h|0}b=c[f>>2]|0;if(!(c[g>>2]|0))c[b+6128>>2]=7;else{g=Gf(7-(((c[b+4640>>2]>>16)*26214|0)+((c[(c[f>>2]|0)+4640>>2]&65535)*26214>>16))|0,2)|0;c[(c[f>>2]|0)+6128>>2]=g}c[(c[f>>2]|0)+6124>>2]=1;h=c[h>>2]|0;i=j;return h|0}function Gf(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)>(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function Hf(a,d,e){a=a|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0;D=i;i=i+160|0;f=D+156|0;g=D+152|0;h=D+148|0;p=D+144|0;q=D+140|0;r=D+136|0;m=D+132|0;u=D+128|0;o=D+124|0;x=D+120|0;w=D+116|0;y=D+112|0;A=D+108|0;z=D+104|0;B=D+100|0;v=D+96|0;s=D+92|0;n=D+88|0;j=D+52|0;l=D+16|0;k=D+8|0;t=D;c[f>>2]=a;c[g>>2]=d;c[h>>2]=e;c[k>>2]=j;c[k+4>>2]=l;c[m>>2]=c[h>>2]>>1;If(c[g>>2]|0,j,l,c[m>>2]|0);c[t>>2]=j;c[x>>2]=b[12288];c[A>>2]=Kf(c[t>>2]|0,c[x>>2]|0,c[m>>2]|0)|0;if((c[A>>2]|0)<0){b[c[f>>2]>>1]=0;c[t>>2]=l;c[A>>2]=Kf(c[t>>2]|0,c[x>>2]|0,c[m>>2]|0)|0;c[u>>2]=1}else c[u>>2]=0;c[q>>2]=1;c[p>>2]=0;c[v>>2]=0;a:while(1){c[w>>2]=b[24576+(c[q>>2]<<1)>>1];c[z>>2]=Kf(c[t>>2]|0,c[w>>2]|0,c[m>>2]|0)|0;if(!((c[A>>2]|0)<=0?(c[z>>2]|0)>=(c[v>>2]|0):0))C=7;do if((C|0)==7){C=0;if((c[A>>2]|0)>=0?(c[z>>2]|0)<=(0-(c[v>>2]|0)|0):0)break;c[q>>2]=(c[q>>2]|0)+1;c[x>>2]=c[w>>2];c[A>>2]=c[z>>2];c[v>>2]=0;if((c[q>>2]|0)<=128)continue a;c[p>>2]=(c[p>>2]|0)+1;if((c[p>>2]|0)>30)break a;Of(c[g>>2]|0,c[h>>2]|0,65536-(_((10+(c[p>>2]|0)&65535)<<16>>16,(c[p>>2]&65535)<<16>>16)|0)|0);If(c[g>>2]|0,j,l,c[m>>2]|0);c[t>>2]=j;c[x>>2]=b[12288];c[A>>2]=Kf(c[t>>2]|0,c[x>>2]|0,c[m>>2]|0)|0;if((c[A>>2]|0)<0){b[c[f>>2]>>1]=0;c[t>>2]=l;c[A>>2]=Kf(c[t>>2]|0,c[x>>2]|0,c[m>>2]|0)|0;c[u>>2]=1}else c[u>>2]=0;c[q>>2]=1;continue a}while(0);if(!(c[z>>2]|0))c[v>>2]=1;else c[v>>2]=0;c[o>>2]=-256;c[r>>2]=0;while(1){if((c[r>>2]|0)>=3)break;c[y>>2]=((c[x>>2]|0)+(c[w>>2]|0)>>1)+((c[x>>2]|0)+(c[w>>2]|0)&1);c[B>>2]=Kf(c[t>>2]|0,c[y>>2]|0,c[m>>2]|0)|0;if(!((c[A>>2]|0)<=0&(c[B>>2]|0)>=0)?!((c[A>>2]|0)>=0&(c[B>>2]|0)<=0):0){c[x>>2]=c[y>>2];c[A>>2]=c[B>>2];c[o>>2]=(c[o>>2]|0)+(128>>c[r>>2])}else{c[w>>2]=c[y>>2];c[z>>2]=c[B>>2]}c[r>>2]=(c[r>>2]|0)+1}e=c[A>>2]|0;a=c[A>>2]|0;if((((c[A>>2]|0)>0?e:0-e|0)|0)<65536){c[n>>2]=a-(c[z>>2]|0);c[s>>2]=(c[A>>2]<<5)+(c[n>>2]>>1);if(c[n>>2]|0)c[o>>2]=(c[o>>2]|0)+((c[s>>2]|0)/(c[n>>2]|0)|0)}else c[o>>2]=(c[o>>2]|0)+((a|0)/((c[A>>2]|0)-(c[z>>2]|0)>>5|0)|0);e=(Lf((c[q>>2]<<8)+(c[o>>2]|0)|0,32767)|0)&65535;b[(c[f>>2]|0)+(c[u>>2]<<1)>>1]=e;c[u>>2]=(c[u>>2]|0)+1;if((c[u>>2]|0)>=(c[h>>2]|0)){C=34;break}c[t>>2]=c[k+((c[u>>2]&1)<<2)>>2];c[x>>2]=b[24576+((c[q>>2]|0)-1<<1)>>1];c[A>>2]=1-(c[u>>2]&2)<<12}if((C|0)==34){i=D;return}b[c[f>>2]>>1]=32768/((c[h>>2]|0)+1|0)|0;c[q>>2]=1;while(1){if((c[q>>2]|0)>=(c[h>>2]|0))break;C=(_(((c[q>>2]|0)+1&65535)<<16>>16,b[c[f>>2]>>1]|0)|0)&65535;b[(c[f>>2]|0)+(c[q>>2]<<1)>>1]=C;c[q>>2]=(c[q>>2]|0)+1}i=D;return}function If(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0;l=i;i=i+32|0;f=l+16|0;g=l+12|0;h=l+8|0;j=l+4|0;k=l;c[f>>2]=a;c[g>>2]=b;c[h>>2]=d;c[j>>2]=e;c[(c[g>>2]|0)+(c[j>>2]<<2)>>2]=65536;c[(c[h>>2]|0)+(c[j>>2]<<2)>>2]=65536;c[k>>2]=0;while(1){b=c[j>>2]|0;if((c[k>>2]|0)>=(c[j>>2]|0))break;c[(c[g>>2]|0)+(c[k>>2]<<2)>>2]=0-(c[(c[f>>2]|0)+(b-(c[k>>2]|0)-1<<2)>>2]|0)-(c[(c[f>>2]|0)+((c[j>>2]|0)+(c[k>>2]|0)<<2)>>2]|0);c[(c[h>>2]|0)+(c[k>>2]<<2)>>2]=0-(c[(c[f>>2]|0)+((c[j>>2]|0)-(c[k>>2]|0)-1<<2)>>2]|0)+(c[(c[f>>2]|0)+((c[j>>2]|0)+(c[k>>2]|0)<<2)>>2]|0);c[k>>2]=(c[k>>2]|0)+1}c[k>>2]=b;while(1){if((c[k>>2]|0)<=0)break;f=(c[g>>2]|0)+((c[k>>2]|0)-1<<2)|0;c[f>>2]=(c[f>>2]|0)-(c[(c[g>>2]|0)+(c[k>>2]<<2)>>2]|0);f=(c[h>>2]|0)+((c[k>>2]|0)-1<<2)|0;c[f>>2]=(c[f>>2]|0)+(c[(c[h>>2]|0)+(c[k>>2]<<2)>>2]|0);c[k>>2]=(c[k>>2]|0)+-1}Jf(c[g>>2]|0,c[j>>2]|0);Jf(c[h>>2]|0,c[j>>2]|0);i=l;return}function Jf(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0;h=i;i=i+16|0;d=h+12|0;e=h+8|0;f=h+4|0;g=h;c[d>>2]=a;c[e>>2]=b;c[f>>2]=2;while(1){if((c[f>>2]|0)>(c[e>>2]|0))break;c[g>>2]=c[e>>2];while(1){if((c[g>>2]|0)<=(c[f>>2]|0))break;a=(c[d>>2]|0)+((c[g>>2]|0)-2<<2)|0;c[a>>2]=(c[a>>2]|0)-(c[(c[d>>2]|0)+(c[g>>2]<<2)>>2]|0);c[g>>2]=(c[g>>2]|0)+-1}a=(c[d>>2]|0)+((c[f>>2]|0)-2<<2)|0;c[a>>2]=(c[a>>2]|0)-(c[(c[d>>2]|0)+(c[f>>2]<<2)>>2]<<1);c[f>>2]=(c[f>>2]|0)+1}i=h;return}function Kf(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0;k=i;i=i+32|0;e=k+20|0;l=k+16|0;f=k+12|0;g=k+8|0;h=k+4|0;j=k;c[e>>2]=a;c[l>>2]=b;c[f>>2]=d;c[j>>2]=c[(c[e>>2]|0)+(c[f>>2]<<2)>>2];c[h>>2]=c[l>>2]<<4;if(8==(c[f>>2]|0)^1^1){l=_(c[j>>2]>>16,(c[h>>2]&65535)<<16>>16)|0;l=(c[(c[e>>2]|0)+28>>2]|0)+(l+((_(c[j>>2]&65535,(c[h>>2]&65535)<<16>>16)|0)>>16))|0;c[j>>2]=l+(_(c[j>>2]|0,(c[h>>2]>>15)+1>>1)|0);l=_(c[j>>2]>>16,(c[h>>2]&65535)<<16>>16)|0;l=(c[(c[e>>2]|0)+24>>2]|0)+(l+((_(c[j>>2]&65535,(c[h>>2]&65535)<<16>>16)|0)>>16))|0;c[j>>2]=l+(_(c[j>>2]|0,(c[h>>2]>>15)+1>>1)|0);l=_(c[j>>2]>>16,(c[h>>2]&65535)<<16>>16)|0;l=(c[(c[e>>2]|0)+20>>2]|0)+(l+((_(c[j>>2]&65535,(c[h>>2]&65535)<<16>>16)|0)>>16))|0;c[j>>2]=l+(_(c[j>>2]|0,(c[h>>2]>>15)+1>>1)|0);l=_(c[j>>2]>>16,(c[h>>2]&65535)<<16>>16)|0;l=(c[(c[e>>2]|0)+16>>2]|0)+(l+((_(c[j>>2]&65535,(c[h>>2]&65535)<<16>>16)|0)>>16))|0;c[j>>2]=l+(_(c[j>>2]|0,(c[h>>2]>>15)+1>>1)|0);l=_(c[j>>2]>>16,(c[h>>2]&65535)<<16>>16)|0;l=(c[(c[e>>2]|0)+12>>2]|0)+(l+((_(c[j>>2]&65535,(c[h>>2]&65535)<<16>>16)|0)>>16))|0;c[j>>2]=l+(_(c[j>>2]|0,(c[h>>2]>>15)+1>>1)|0);l=_(c[j>>2]>>16,(c[h>>2]&65535)<<16>>16)|0;l=(c[(c[e>>2]|0)+8>>2]|0)+(l+((_(c[j>>2]&65535,(c[h>>2]&65535)<<16>>16)|0)>>16))|0;c[j>>2]=l+(_(c[j>>2]|0,(c[h>>2]>>15)+1>>1)|0);l=_(c[j>>2]>>16,(c[h>>2]&65535)<<16>>16)|0;l=(c[(c[e>>2]|0)+4>>2]|0)+(l+((_(c[j>>2]&65535,(c[h>>2]&65535)<<16>>16)|0)>>16))|0;c[j>>2]=l+(_(c[j>>2]|0,(c[h>>2]>>15)+1>>1)|0);l=_(c[j>>2]>>16,(c[h>>2]&65535)<<16>>16)|0;l=(c[c[e>>2]>>2]|0)+(l+((_(c[j>>2]&65535,(c[h>>2]&65535)<<16>>16)|0)>>16))|0;c[j>>2]=l+(_(c[j>>2]|0,(c[h>>2]>>15)+1>>1)|0);l=c[j>>2]|0;i=k;return l|0}c[g>>2]=(c[f>>2]|0)-1;while(1){if((c[g>>2]|0)<0)break;l=_(c[j>>2]>>16,(c[h>>2]&65535)<<16>>16)|0;l=(c[(c[e>>2]|0)+(c[g>>2]<<2)>>2]|0)+(l+((_(c[j>>2]&65535,(c[h>>2]&65535)<<16>>16)|0)>>16))|0;c[j>>2]=l+(_(c[j>>2]|0,(c[h>>2]>>15)+1>>1)|0);c[g>>2]=(c[g>>2]|0)+-1}l=c[j>>2]|0;i=k;return l|0}function Lf(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)<(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function Mf(a,d,e,f,g){a=a|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;t=i;i=i+48|0;h=t+44|0;j=t+40|0;k=t+36|0;l=t+32|0;u=t+28|0;q=t+24|0;m=t+20|0;p=t+16|0;n=t+12|0;o=t+8|0;r=t+4|0;s=t;c[h>>2]=a;c[j>>2]=d;c[k>>2]=e;c[l>>2]=f;c[u>>2]=g;c[m>>2]=c[u>>2]>>1;c[q>>2]=0;while(1){if((c[q>>2]|0)>=(c[m>>2]|0))break;c[p>>2]=b[(c[h>>2]|0)+(c[q>>2]<<1<<1)>>1]<<10;c[o>>2]=(c[p>>2]|0)-(c[c[j>>2]>>2]|0);u=_(c[o>>2]>>16,b[12286]|0)|0;c[n>>2]=(c[o>>2]|0)+(u+((_(c[o>>2]&65535,b[12286]|0)|0)>>16));c[r>>2]=(c[c[j>>2]>>2]|0)+(c[n>>2]|0);c[c[j>>2]>>2]=(c[p>>2]|0)+(c[n>>2]|0);c[p>>2]=b[(c[h>>2]|0)+((c[q>>2]<<1)+1<<1)>>1]<<10;c[o>>2]=(c[p>>2]|0)-(c[(c[j>>2]|0)+4>>2]|0);u=_(c[o>>2]>>16,b[12287]|0)|0;c[n>>2]=u+((_(c[o>>2]&65535,b[12287]|0)|0)>>16);c[s>>2]=(c[(c[j>>2]|0)+4>>2]|0)+(c[n>>2]|0);c[(c[j>>2]|0)+4>>2]=(c[p>>2]|0)+(c[n>>2]|0);if((((c[s>>2]|0)+(c[r>>2]|0)>>10)+1>>1|0)<=32767)if((((c[s>>2]|0)+(c[r>>2]|0)>>10)+1>>1|0)<-32768)g=-32768;else g=((c[s>>2]|0)+(c[r>>2]|0)>>10)+1>>1;else g=32767;b[(c[k>>2]|0)+(c[q>>2]<<1)>>1]=g;if((((c[s>>2]|0)-(c[r>>2]|0)>>10)+1>>1|0)<=32767)if((((c[s>>2]|0)-(c[r>>2]|0)>>10)+1>>1|0)<-32768)g=-32768;else g=((c[s>>2]|0)-(c[r>>2]|0)>>10)+1>>1;else g=32767;b[(c[l>>2]|0)+(c[q>>2]<<1)>>1]=g;c[q>>2]=(c[q>>2]|0)+1}i=t;return}function Nf(a,d,e,f,g,h,j){a=a|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0;x=i;i=i+64|0;k=x+52|0;l=x+48|0;y=x+44|0;m=x+40|0;n=x+36|0;o=x+32|0;p=x+28|0;v=x+24|0;u=x+20|0;r=x+16|0;q=x+12|0;t=x+8|0;s=x+4|0;w=x;c[k>>2]=a;c[l>>2]=d;c[y>>2]=e;c[m>>2]=f;c[n>>2]=g;c[o>>2]=h;c[p>>2]=j;c[q>>2]=0-(c[c[y>>2]>>2]|0)&16383;c[r>>2]=0-(c[c[y>>2]>>2]|0)>>14;c[s>>2]=0-(c[(c[y>>2]|0)+4>>2]|0)&16383;c[t>>2]=0-(c[(c[y>>2]|0)+4>>2]|0)>>14;c[v>>2]=0;while(1){if((c[v>>2]|0)>=(c[o>>2]|0))break;y=_(c[v>>2]|0,c[p>>2]|0)|0;c[u>>2]=b[(c[k>>2]|0)+(y<<1)>>1];y=_(c[c[l>>2]>>2]>>16,(c[u>>2]&65535)<<16>>16)|0;c[w>>2]=(c[c[m>>2]>>2]|0)+(y+((_(c[c[l>>2]>>2]&65535,(c[u>>2]&65535)<<16>>16)|0)>>16))<<2;y=_(c[w>>2]>>16,(c[q>>2]&65535)<<16>>16)|0;y=(c[(c[m>>2]|0)+4>>2]|0)+((y+((_(c[w>>2]&65535,(c[q>>2]&65535)<<16>>16)|0)>>16)>>13)+1>>1)|0;c[c[m>>2]>>2]=y;y=_(c[w>>2]>>16,(c[r>>2]&65535)<<16>>16)|0;y=(c[c[m>>2]>>2]|0)+(y+((_(c[w>>2]&65535,(c[r>>2]&65535)<<16>>16)|0)>>16))|0;c[c[m>>2]>>2]=y;y=_(c[(c[l>>2]|0)+4>>2]>>16,(c[u>>2]&65535)<<16>>16)|0;y=(c[c[m>>2]>>2]|0)+(y+((_(c[(c[l>>2]|0)+4>>2]&65535,(c[u>>2]&65535)<<16>>16)|0)>>16))|0;c[c[m>>2]>>2]=y;y=_(c[w>>2]>>16,(c[s>>2]&65535)<<16>>16)|0;y=(y+((_(c[w>>2]&65535,(c[s>>2]&65535)<<16>>16)|0)>>16)>>13)+1>>1;c[(c[m>>2]|0)+4>>2]=y;y=_(c[w>>2]>>16,(c[t>>2]&65535)<<16>>16)|0;y=(c[(c[m>>2]|0)+4>>2]|0)+(y+((_(c[w>>2]&65535,(c[t>>2]&65535)<<16>>16)|0)>>16))|0;c[(c[m>>2]|0)+4>>2]=y;y=_(c[(c[l>>2]|0)+8>>2]>>16,(c[u>>2]&65535)<<16>>16)|0;y=(c[(c[m>>2]|0)+4>>2]|0)+(y+((_(c[(c[l>>2]|0)+8>>2]&65535,(c[u>>2]&65535)<<16>>16)|0)>>16))|0;c[(c[m>>2]|0)+4>>2]=y;if(((c[w>>2]|0)+16384-1>>14|0)<=32767)if(((c[w>>2]|0)+16384-1>>14|0)<-32768)e=-32768;else e=(c[w>>2]|0)+16384-1>>14;else e=32767;y=_(c[v>>2]|0,c[p>>2]|0)|0;b[(c[n>>2]|0)+(y<<1)>>1]=e;c[v>>2]=(c[v>>2]|0)+1}i=x;return}function Of(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0;k=i;i=i+32|0;e=k+16|0;f=k+12|0;g=k+8|0;j=k+4|0;h=k;c[e>>2]=a;c[f>>2]=b;c[g>>2]=d;c[h>>2]=(c[g>>2]|0)-65536;c[j>>2]=0;while(1){a=c[g>>2]>>16;if((c[j>>2]|0)>=((c[f>>2]|0)-1|0))break;b=_(a,(c[(c[e>>2]|0)+(c[j>>2]<<2)>>2]&65535)<<16>>16)|0;b=b+((_(c[g>>2]&65535,(c[(c[e>>2]|0)+(c[j>>2]<<2)>>2]&65535)<<16>>16)|0)>>16)|0;b=b+(_(c[g>>2]|0,(c[(c[e>>2]|0)+(c[j>>2]<<2)>>2]>>15)+1>>1)|0)|0;c[(c[e>>2]|0)+(c[j>>2]<<2)>>2]=b;b=((_(c[g>>2]|0,c[h>>2]|0)|0)>>15)+1>>1;c[g>>2]=(c[g>>2]|0)+b;c[j>>2]=(c[j>>2]|0)+1}j=_(a,(c[(c[e>>2]|0)+((c[f>>2]|0)-1<<2)>>2]&65535)<<16>>16)|0;j=j+((_(c[g>>2]&65535,(c[(c[e>>2]|0)+((c[f>>2]|0)-1<<2)>>2]&65535)<<16>>16)|0)>>16)|0;j=j+(_(c[g>>2]|0,(c[(c[e>>2]|0)+((c[f>>2]|0)-1<<2)>>2]>>15)+1>>1)|0)|0;c[(c[e>>2]|0)+((c[f>>2]|0)-1<<2)>>2]=j;i=k;return}function Pf(a,d,e){a=a|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0;l=i;i=i+32|0;f=l+16|0;g=l+12|0;h=l+8|0;k=l+4|0;j=l;c[f>>2]=a;c[g>>2]=d;c[h>>2]=e;c[j>>2]=(c[h>>2]|0)-65536;c[k>>2]=0;while(1){a=c[h>>2]|0;if((c[k>>2]|0)>=((c[g>>2]|0)-1|0))break;d=((_(a,b[(c[f>>2]|0)+(c[k>>2]<<1)>>1]|0)|0)>>15)+1>>1&65535;b[(c[f>>2]|0)+(c[k>>2]<<1)>>1]=d;d=((_(c[h>>2]|0,c[j>>2]|0)|0)>>15)+1>>1;c[h>>2]=(c[h>>2]|0)+d;c[k>>2]=(c[k>>2]|0)+1}k=((_(a,b[(c[f>>2]|0)+((c[g>>2]|0)-1<<1)>>1]|0)|0)>>15)+1>>1&65535;b[(c[f>>2]|0)+((c[g>>2]|0)-1<<1)>>1]=k;i=l;return}function Qf(d,e,f,g,h){d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;u=i;i=i+48|0;j=u+36|0;l=u+38|0;m=u+32|0;k=u+28|0;n=u+24|0;r=u+20|0;q=u+16|0;t=u+12|0;s=u+8|0;p=u+4|0;o=u;b[j>>1]=d;a[l>>0]=e;c[m>>2]=f;c[k>>2]=g;c[n>>2]=h;g=(c[n>>2]|0)==4;do if((c[k>>2]|0)==8)if(g){c[o>>2]=30286;c[p>>2]=11;break}else{c[o>>2]=30252;c[p>>2]=3;break}else if(g){c[o>>2]=30330;c[p>>2]=34;break}else{c[o>>2]=30258;c[p>>2]=12;break}while(0);c[t>>2]=(c[k>>2]&65535)<<16>>16<<1;c[s>>2]=((c[k>>2]&65535)<<16>>16)*18;c[r>>2]=(c[t>>2]|0)+(b[j>>1]|0);c[q>>2]=0;while(1){if((c[q>>2]|0)>=(c[n>>2]|0))break;g=_(c[q>>2]|0,c[p>>2]|0)|0;c[(c[m>>2]|0)+(c[q>>2]<<2)>>2]=(c[r>>2]|0)+(a[(c[o>>2]|0)+(g+(a[l>>0]|0))>>0]|0);g=c[(c[m>>2]|0)+(c[q>>2]<<2)>>2]|0;do if((c[t>>2]|0)>(c[s>>2]|0)){if((g|0)>(c[t>>2]|0)){g=c[t>>2]|0;break}if((c[(c[m>>2]|0)+(c[q>>2]<<2)>>2]|0)<(c[s>>2]|0)){g=c[s>>2]|0;break}else{g=c[(c[m>>2]|0)+(c[q>>2]<<2)>>2]|0;break}}else{if((g|0)>(c[s>>2]|0)){g=c[s>>2]|0;break}if((c[(c[m>>2]|0)+(c[q>>2]<<2)>>2]|0)<(c[t>>2]|0)){g=c[t>>2]|0;break}else{g=c[(c[m>>2]|0)+(c[q>>2]<<2)>>2]|0;break}}while(0);c[(c[m>>2]|0)+(c[q>>2]<<2)>>2]=g;c[q>>2]=(c[q>>2]|0)+1}i=u;return}function Rf(a,d,e,f){a=a|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0;n=i;i=i+32|0;g=n+20|0;h=n+16|0;j=n+12|0;k=n+8|0;l=n+4|0;m=n;c[g>>2]=a;c[h>>2]=d;c[j>>2]=e;c[k>>2]=f;c[m>>2]=0;c[l>>2]=0;while(1){a=c[m>>2]|0;if((c[l>>2]|0)>=(c[k>>2]|0))break;e=_(b[(c[g>>2]|0)+(c[l>>2]<<1)>>1]|0,b[(c[h>>2]|0)+(c[l>>2]<<1)>>1]|0)|0;c[m>>2]=a+(e>>c[j>>2]);c[l>>2]=(c[l>>2]|0)+1}i=n;return a|0}function Sf(a){a=a|0;var b=0,d=0,e=0,f=0;b=i;i=i+16|0;f=b+8|0;e=b+4|0;d=b;c[f>>2]=a;Tf(c[f>>2]|0,e,d);a=((_(c[d>>2]|0,128-(c[d>>2]|0)|0)|0)>>16)*179|0;a=(31-(c[e>>2]|0)<<7)+((c[d>>2]|0)+(a+(((_(c[d>>2]|0,128-(c[d>>2]|0)|0)|0)&65535)*179>>16)))|0;i=b;return a|0}function Tf(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0;e=i;i=i+16|0;h=e+12|0;j=e+8|0;f=e+4|0;g=e;c[h>>2]=a;c[j>>2]=b;c[f>>2]=d;c[g>>2]=Uf(c[h>>2]|0)|0;c[c[j>>2]>>2]=c[g>>2];b=(Vf(c[h>>2]|0,24-(c[g>>2]|0)|0)|0)&127;c[c[f>>2]>>2]=b;i=e;return}function Uf(a){a=a|0;var b=0,d=0;d=i;i=i+16|0;b=d;c[b>>2]=a;if(!(c[b>>2]|0)){a=32;i=d;return a|0}a=32-(32-(aa(c[b>>2]|0)|0))|0;i=d;return a|0}function Vf(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0;k=i;i=i+32|0;e=k+20|0;d=k+16|0;f=k+12|0;j=k+8|0;h=k+4|0;g=k;c[d>>2]=a;c[f>>2]=b;c[j>>2]=c[d>>2];c[h>>2]=c[f>>2];c[g>>2]=0-(c[f>>2]|0);if(!(c[f>>2]|0)){c[e>>2]=c[d>>2];j=c[e>>2]|0;i=k;return j|0}d=c[j>>2]|0;if((c[f>>2]|0)<0){c[e>>2]=d<<c[g>>2]|(c[j>>2]|0)>>>(32-(c[g>>2]|0)|0);j=c[e>>2]|0;i=k;return j|0}else{c[e>>2]=d<<32-(c[h>>2]|0)|(c[j>>2]|0)>>>(c[h>>2]|0);j=c[e>>2]|0;i=k;return j|0}return 0}function Wf(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0;h=i;i=i+16|0;d=h+12|0;e=h+8|0;g=h+4|0;f=h;c[e>>2]=a;if((c[e>>2]|0)<0){c[d>>2]=0;g=c[d>>2]|0;i=h;return g|0}if((c[e>>2]|0)>=3967){c[d>>2]=2147483647;g=c[d>>2]|0;i=h;return g|0}c[g>>2]=1<<(c[e>>2]>>7);c[f>>2]=c[e>>2]&127;a=c[g>>2]|0;b=c[g>>2]|0;if((c[e>>2]|0)<2048){e=_((_((c[f>>2]&65535)<<16>>16,(128-(c[f>>2]|0)&65535)<<16>>16)|0)>>16,-174)|0;c[g>>2]=a+((_(b,(c[f>>2]|0)+(e+((_((_((c[f>>2]&65535)<<16>>16,(128-(c[f>>2]|0)&65535)<<16>>16)|0)&65535,-174)|0)>>16))|0)|0)>>7)}else{e=_((_((c[f>>2]&65535)<<16>>16,(128-(c[f>>2]|0)&65535)<<16>>16)|0)>>16,-174)|0;c[g>>2]=a+(_(b>>7,(c[f>>2]|0)+(e+((_((_((c[f>>2]&65535)<<16>>16,(128-(c[f>>2]|0)&65535)<<16>>16)|0)&65535,-174)|0)>>16))|0)|0)}c[d>>2]=c[g>>2];g=c[d>>2]|0;i=h;return g|0}function Xf(a,d,e,f,g,h){a=a|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;t=i;i=i+48|0;j=t+40|0;k=t+36|0;l=t+32|0;m=t+28|0;n=t+24|0;q=t+16|0;p=t+12|0;s=t+8|0;r=t+4|0;o=t;c[j>>2]=a;c[k>>2]=d;c[l>>2]=e;c[m>>2]=f;c[n>>2]=g;c[t+20>>2]=h;c[p>>2]=c[n>>2];while(1){if((c[p>>2]|0)>=(c[m>>2]|0))break;c[o>>2]=(c[k>>2]|0)+((c[p>>2]|0)-1<<1);c[s>>2]=_(b[c[o>>2]>>1]|0,b[c[l>>2]>>1]|0)|0;c[s>>2]=(c[s>>2]|0)+(_(b[(c[o>>2]|0)+-2>>1]|0,b[(c[l>>2]|0)+2>>1]|0)|0);c[s>>2]=(c[s>>2]|0)+(_(b[(c[o>>2]|0)+-4>>1]|0,b[(c[l>>2]|0)+4>>1]|0)|0);c[s>>2]=(c[s>>2]|0)+(_(b[(c[o>>2]|0)+-6>>1]|0,b[(c[l>>2]|0)+6>>1]|0)|0);c[s>>2]=(c[s>>2]|0)+(_(b[(c[o>>2]|0)+-8>>1]|0,b[(c[l>>2]|0)+8>>1]|0)|0);c[s>>2]=(c[s>>2]|0)+(_(b[(c[o>>2]|0)+-10>>1]|0,b[(c[l>>2]|0)+10>>1]|0)|0);c[q>>2]=6;while(1){if((c[q>>2]|0)>=(c[n>>2]|0))break;c[s>>2]=(c[s>>2]|0)+(_(b[(c[o>>2]|0)+(0-(c[q>>2]|0)<<1)>>1]|0,b[(c[l>>2]|0)+(c[q>>2]<<1)>>1]|0)|0);c[s>>2]=(c[s>>2]|0)+(_(b[(c[o>>2]|0)+(0-(c[q>>2]|0)-1<<1)>>1]|0,b[(c[l>>2]|0)+((c[q>>2]|0)+1<<1)>>1]|0)|0);c[q>>2]=(c[q>>2]|0)+2}c[s>>2]=(b[(c[o>>2]|0)+2>>1]<<12)-(c[s>>2]|0);c[r>>2]=(c[s>>2]>>11)+1>>1;if((c[r>>2]|0)>32767)e=32767;else e=(c[r>>2]|0)<-32768?-32768:c[r>>2]|0;b[(c[j>>2]|0)+(c[p>>2]<<1)>>1]=e;c[p>>2]=(c[p>>2]|0)+1}oj(c[j>>2]|0,0,c[n>>2]<<1|0)|0;i=t;return}function Yf(a,d){a=a|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;m=i;i=i+160|0;e=m+148|0;f=m+144|0;g=m+140|0;l=m+136|0;j=m+8|0;h=m+4|0;k=m;c[f>>2]=a;c[g>>2]=d;c[k>>2]=0;c[h>>2]=j+((c[g>>2]&1)<<6);c[l>>2]=0;while(1){if((c[l>>2]|0)>=(c[g>>2]|0))break;c[k>>2]=(c[k>>2]|0)+(b[(c[f>>2]|0)+(c[l>>2]<<1)>>1]|0);c[(c[h>>2]|0)+(c[l>>2]<<2)>>2]=b[(c[f>>2]|0)+(c[l>>2]<<1)>>1]<<12;c[l>>2]=(c[l>>2]|0)+1}if((c[k>>2]|0)>=4096){c[e>>2]=0;d=c[e>>2]|0;i=m;return d|0}else{c[e>>2]=Zf(j,c[g>>2]|0)|0;d=c[e>>2]|0;i=m;return d|0}return 0}function Zf(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;r=i;i=i+64|0;d=r+48|0;e=r+44|0;s=r+40|0;j=r+36|0;l=r+32|0;k=r+28|0;h=r+24|0;m=r+20|0;n=r+16|0;o=r+12|0;p=r+8|0;g=r+4|0;f=r;c[e>>2]=a;c[s>>2]=b;c[f>>2]=(c[e>>2]|0)+((c[s>>2]&1)<<6);c[h>>2]=1073741824;c[j>>2]=(c[s>>2]|0)-1;while(1){if((c[j>>2]|0)<=0)break;if((c[(c[f>>2]|0)+(c[j>>2]<<2)>>2]|0)>16773022){q=5;break}if((c[(c[f>>2]|0)+(c[j>>2]<<2)>>2]|0)<-16773022){q=5;break}c[m>>2]=0-(c[(c[f>>2]|0)+(c[j>>2]<<2)>>2]<<7);s=c[m>>2]|0;b=c[m>>2]|0;b=yj(s|0,((s|0)<0)<<31>>31|0,b|0,((b|0)<0)<<31>>31|0)|0;b=nj(b|0,C|0,32)|0;c[n>>2]=1073741824-b;b=c[n>>2]|0;c[k>>2]=32-(_f((c[n>>2]|0)>0?b:0-b|0)|0);c[o>>2]=$f(c[n>>2]|0,(c[k>>2]|0)+30|0)|0;b=c[h>>2]|0;s=c[n>>2]|0;s=yj(b|0,((b|0)<0)<<31>>31|0,s|0,((s|0)<0)<<31>>31|0)|0;s=nj(s|0,C|0,32)|0;c[h>>2]=s<<2;c[g>>2]=c[f>>2];c[f>>2]=(c[e>>2]|0)+((c[j>>2]&1)<<6);c[l>>2]=0;while(1){if((c[l>>2]|0)>=(c[j>>2]|0))break;b=c[(c[g>>2]|0)+(c[l>>2]<<2)>>2]|0;a=c[(c[g>>2]|0)+((c[j>>2]|0)-(c[l>>2]|0)-1<<2)>>2]|0;s=c[m>>2]|0;s=yj(a|0,((a|0)<0)<<31>>31|0,s|0,((s|0)<0)<<31>>31|0)|0;s=nj(s|0,C|0,30)|0;s=mj(s|0,C|0,1,0)|0;s=nj(s|0,C|0,1)|0;c[p>>2]=b-s;s=(c[k>>2]|0)==1;b=c[p>>2]|0;a=c[o>>2]|0;a=yj(b|0,((b|0)<0)<<31>>31|0,a|0,((a|0)<0)<<31>>31|0)|0;b=C;if(s){b=nj(a|0,b|0,1)|0;s=C;t=c[p>>2]|0;a=c[o>>2]|0;a=yj(t|0,((t|0)<0)<<31>>31|0,a|0,((a|0)<0)<<31>>31|0)|0;a=mj(b|0,s|0,a&1|0,0)|0}else{a=nj(a|0,b|0,(c[k>>2]|0)-1|0)|0;a=mj(a|0,C|0,1,0)|0;a=nj(a|0,C|0,1)|0}c[(c[f>>2]|0)+(c[l>>2]<<2)>>2]=a;c[l>>2]=(c[l>>2]|0)+1}c[j>>2]=(c[j>>2]|0)+-1}if((q|0)==5){c[d>>2]=0;t=c[d>>2]|0;i=r;return t|0}if((c[c[f>>2]>>2]|0)<=16773022?(c[c[f>>2]>>2]|0)>=-16773022:0){c[m>>2]=0-(c[c[f>>2]>>2]<<7);t=c[m>>2]|0;s=c[m>>2]|0;s=yj(t|0,((t|0)<0)<<31>>31|0,s|0,((s|0)<0)<<31>>31|0)|0;s=nj(s|0,C|0,32)|0;c[n>>2]=1073741824-s;s=c[h>>2]|0;t=c[n>>2]|0;t=yj(s|0,((s|0)<0)<<31>>31|0,t|0,((t|0)<0)<<31>>31|0)|0;t=nj(t|0,C|0,32)|0;c[h>>2]=t<<2;c[d>>2]=c[h>>2];t=c[d>>2]|0;i=r;return t|0}c[d>>2]=0;t=c[d>>2]|0;i=r;return t|0}function _f(a){a=a|0;var b=0,d=0;d=i;i=i+16|0;b=d;c[b>>2]=a;if(!(c[b>>2]|0)){a=32;i=d;return a|0}a=32-(32-(aa(c[b>>2]|0)|0))|0;i=d;return a|0}function $f(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0;h=i;i=i+48|0;f=h+32|0;n=h+28|0;d=h+24|0;j=h+20|0;g=h+16|0;k=h+12|0;m=h+8|0;l=h+4|0;e=h;c[n>>2]=a;c[d>>2]=b;b=c[n>>2]|0;c[j>>2]=(_f((c[n>>2]|0)>0?b:0-b|0)|0)-1;c[m>>2]=c[n>>2]<<c[j>>2];c[k>>2]=536870911/(c[m>>2]>>16|0)|0;c[e>>2]=c[k>>2]<<16;b=_(c[m>>2]>>16,(c[k>>2]&65535)<<16>>16)|0;c[l>>2]=536870912-(b+((_(c[m>>2]&65535,(c[k>>2]&65535)<<16>>16)|0)>>16))<<3;b=_(c[l>>2]>>16,(c[k>>2]&65535)<<16>>16)|0;b=(c[e>>2]|0)+(b+((_(c[l>>2]&65535,(c[k>>2]&65535)<<16>>16)|0)>>16))|0;c[e>>2]=b+(_(c[l>>2]|0,(c[k>>2]>>15)+1>>1)|0);c[g>>2]=61-(c[j>>2]|0)-(c[d>>2]|0);b=c[g>>2]|0;if((c[g>>2]|0)>0)if((b|0)<32){c[f>>2]=c[e>>2]>>c[g>>2];n=c[f>>2]|0;i=h;return n|0}else{c[f>>2]=0;n=c[f>>2]|0;i=h;return n|0}a=c[e>>2]|0;d=0-(c[g>>2]|0)|0;do if((-2147483648>>0-b|0)>(2147483647>>0-(c[g>>2]|0)|0)){if((a|0)>(-2147483648>>d|0)){b=-2147483648>>0-(c[g>>2]|0);break}if((c[e>>2]|0)<(2147483647>>0-(c[g>>2]|0)|0)){b=2147483647>>0-(c[g>>2]|0);break}else{b=c[e>>2]|0;break}}else{if((a|0)>(2147483647>>d|0)){b=2147483647>>0-(c[g>>2]|0);break}if((c[e>>2]|0)<(-2147483648>>0-(c[g>>2]|0)|0)){b=-2147483648>>0-(c[g>>2]|0);break}else{b=c[e>>2]|0;break}}while(0);c[f>>2]=b<<0-(c[g>>2]|0);n=c[f>>2]|0;i=h;return n|0}function ag(a,e,f){a=a|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0;C=i;i=i+272|0;x=C+264|0;g=C+260|0;y=C+256|0;v=C+252|0;B=C+248|0;A=C+244|0;p=C+240|0;n=C+176|0;h=C+140|0;k=C+104|0;j=C+100|0;l=C+96|0;s=C+92|0;r=C+88|0;o=C+84|0;q=C+80|0;z=C+16|0;u=C+12|0;m=C+8|0;t=C+4|0;w=C;c[x>>2]=a;c[g>>2]=e;c[y>>2]=f;c[t>>2]=0;c[v>>2]=(c[y>>2]|0)==16?30226:30242;c[B>>2]=0;while(1){if((c[B>>2]|0)>=(c[y>>2]|0))break;c[s>>2]=b[(c[g>>2]|0)+(c[B>>2]<<1)>>1]>>8;c[r>>2]=(b[(c[g>>2]|0)+(c[B>>2]<<1)>>1]|0)-(c[s>>2]<<8);c[o>>2]=b[24576+(c[s>>2]<<1)>>1];c[q>>2]=(b[24576+((c[s>>2]|0)+1<<1)>>1]|0)-(c[o>>2]|0);f=((c[o>>2]<<8)+(_(c[q>>2]|0,c[r>>2]|0)|0)>>3)+1>>1;c[n+(d[(c[v>>2]|0)+(c[B>>2]|0)>>0]<<2)>>2]=f;c[B>>2]=(c[B>>2]|0)+1}c[p>>2]=c[y>>2]>>1;bg(h,n,c[p>>2]|0);bg(k,n+4|0,c[p>>2]|0);c[B>>2]=0;while(1){if((c[B>>2]|0)>=(c[p>>2]|0))break;c[j>>2]=(c[h+((c[B>>2]|0)+1<<2)>>2]|0)+(c[h+(c[B>>2]<<2)>>2]|0);c[l>>2]=(c[k+((c[B>>2]|0)+1<<2)>>2]|0)-(c[k+(c[B>>2]<<2)>>2]|0);c[z+(c[B>>2]<<2)>>2]=0-(c[l>>2]|0)-(c[j>>2]|0);c[z+((c[y>>2]|0)-(c[B>>2]|0)-1<<2)>>2]=(c[l>>2]|0)-(c[j>>2]|0);c[B>>2]=(c[B>>2]|0)+1}c[A>>2]=0;while(1){if((c[A>>2]|0)>=10)break;c[u>>2]=0;c[B>>2]=0;while(1){if((c[B>>2]|0)>=(c[y>>2]|0))break;v=c[z+(c[B>>2]<<2)>>2]|0;c[m>>2]=(c[z+(c[B>>2]<<2)>>2]|0)>0?v:0-v|0;if((c[m>>2]|0)>(c[u>>2]|0)){c[u>>2]=c[m>>2];c[t>>2]=c[B>>2]}c[B>>2]=(c[B>>2]|0)+1}c[u>>2]=(c[u>>2]>>4)+1>>1;if((c[u>>2]|0)<=32767)break;c[u>>2]=(c[u>>2]|0)<163838?c[u>>2]|0:163838;c[w>>2]=65470-(((c[u>>2]|0)-32767<<14|0)/((_(c[u>>2]|0,(c[t>>2]|0)+1|0)|0)>>2|0)|0);Of(z,c[y>>2]|0,c[w>>2]|0);c[A>>2]=(c[A>>2]|0)+1}w=(c[A>>2]|0)==10;c[B>>2]=0;a:do if(w)while(1){if((c[B>>2]|0)>=(c[y>>2]|0))break a;if(((c[z+(c[B>>2]<<2)>>2]>>4)+1>>1|0)<=32767)if(((c[z+(c[B>>2]<<2)>>2]>>4)+1>>1|0)<-32768)g=-32768;else g=(c[z+(c[B>>2]<<2)>>2]>>4)+1>>1;else g=32767;b[(c[x>>2]|0)+(c[B>>2]<<1)>>1]=g;c[z+(c[B>>2]<<2)>>2]=b[(c[x>>2]|0)+(c[B>>2]<<1)>>1]<<5;c[B>>2]=(c[B>>2]|0)+1}else while(1){if((c[B>>2]|0)>=(c[y>>2]|0))break a;b[(c[x>>2]|0)+(c[B>>2]<<1)>>1]=(c[z+(c[B>>2]<<2)>>2]>>4)+1>>1;c[B>>2]=(c[B>>2]|0)+1}while(0);c[A>>2]=0;while(1){if((c[A>>2]|0)>=16){g=31;break}if((Yf(c[x>>2]|0,c[y>>2]|0)|0)>=107374){g=31;break}Of(z,c[y>>2]|0,65536-(2<<c[A>>2])|0);c[B>>2]=0;while(1){if((c[B>>2]|0)>=(c[y>>2]|0))break;b[(c[x>>2]|0)+(c[B>>2]<<1)>>1]=(c[z+(c[B>>2]<<2)>>2]>>4)+1>>1;c[B>>2]=(c[B>>2]|0)+1}c[A>>2]=(c[A>>2]|0)+1}if((g|0)==31){i=C;return}}function bg(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0;l=i;i=i+32|0;e=l+20|0;f=l+16|0;g=l+12|0;j=l+8|0;k=l+4|0;h=l;c[e>>2]=a;c[f>>2]=b;c[g>>2]=d;c[c[e>>2]>>2]=65536;c[(c[e>>2]|0)+4>>2]=0-(c[c[f>>2]>>2]|0);c[j>>2]=1;while(1){if((c[j>>2]|0)>=(c[g>>2]|0))break;c[h>>2]=c[(c[f>>2]|0)+(c[j>>2]<<1<<2)>>2];d=c[(c[e>>2]|0)+((c[j>>2]|0)-1<<2)>>2]<<1;b=c[h>>2]|0;a=c[(c[e>>2]|0)+(c[j>>2]<<2)>>2]|0;a=yj(b|0,((b|0)<0)<<31>>31|0,a|0,((a|0)<0)<<31>>31|0)|0;a=nj(a|0,C|0,15)|0;a=mj(a|0,C|0,1,0)|0;a=nj(a|0,C|0,1)|0;c[(c[e>>2]|0)+((c[j>>2]|0)+1<<2)>>2]=d-a;c[k>>2]=c[j>>2];while(1){if((c[k>>2]|0)<=1)break;b=c[(c[e>>2]|0)+((c[k>>2]|0)-2<<2)>>2]|0;a=c[h>>2]|0;d=c[(c[e>>2]|0)+((c[k>>2]|0)-1<<2)>>2]|0;d=yj(a|0,((a|0)<0)<<31>>31|0,d|0,((d|0)<0)<<31>>31|0)|0;d=nj(d|0,C|0,15)|0;d=mj(d|0,C|0,1,0)|0;d=nj(d|0,C|0,1)|0;a=(c[e>>2]|0)+(c[k>>2]<<2)|0;c[a>>2]=(c[a>>2]|0)+(b-d);c[k>>2]=(c[k>>2]|0)+-1}a=(c[e>>2]|0)+4|0;c[a>>2]=(c[a>>2]|0)-(c[h>>2]|0);c[j>>2]=(c[j>>2]|0)+1}i=l;return}
+ function xd(a,d,e,f){a=a|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;t=i;i=i+80|0;l=t+40|0;g=t+36|0;m=t+32|0;n=t+28|0;q=t+24|0;j=t+20|0;s=t+16|0;h=t+12|0;k=t+8|0;p=t+48|0;r=t+4|0;o=t;c[l>>2]=a;c[g>>2]=d;c[m>>2]=e;c[n>>2]=f;c[r>>2]=(c[l>>2]|0)+2772;if((c[(c[l>>2]|0)+2316>>2]|0)!=(c[(c[r>>2]|0)+1384>>2]|0)){wd(c[l>>2]|0);c[(c[r>>2]|0)+1384>>2]=c[(c[l>>2]|0)+2316>>2]}a:do if((c[(c[l>>2]|0)+4160>>2]|0)==0?(c[(c[l>>2]|0)+4164>>2]|0)==0:0){c[q>>2]=0;while(1){if((c[q>>2]|0)>=(c[(c[l>>2]|0)+2340>>2]|0))break;d=(c[r>>2]|0)+1280+(c[q>>2]<<1)|0;b[d>>1]=(b[d>>1]|0)+((((b[(c[l>>2]|0)+2344+(c[q>>2]<<1)>>1]|0)-(b[(c[r>>2]|0)+1280+(c[q>>2]<<1)>>1]|0)>>16)*16348|0)+(((b[(c[l>>2]|0)+2344+(c[q>>2]<<1)>>1]|0)-(b[(c[r>>2]|0)+1280+(c[q>>2]<<1)>>1]|0)&65535)*16348>>16));c[q>>2]=(c[q>>2]|0)+1}c[h>>2]=0;c[j>>2]=0;c[q>>2]=0;while(1){if((c[q>>2]|0)>=(c[(c[l>>2]|0)+2324>>2]|0))break;if((c[(c[g>>2]|0)+16+(c[q>>2]<<2)>>2]|0)>(c[h>>2]|0)){c[h>>2]=c[(c[g>>2]|0)+16+(c[q>>2]<<2)>>2];c[j>>2]=c[q>>2]}c[q>>2]=(c[q>>2]|0)+1}qj((c[r>>2]|0)+(c[(c[l>>2]|0)+2332>>2]<<2)|0,c[r>>2]|0,(_((c[(c[l>>2]|0)+2324>>2]|0)-1|0,c[(c[l>>2]|0)+2332>>2]|0)|0)<<2|0)|0;j=_(c[j>>2]|0,c[(c[l>>2]|0)+2332>>2]|0)|0;pj(c[r>>2]|0,(c[l>>2]|0)+4+(j<<2)|0,c[(c[l>>2]|0)+2332>>2]<<2|0)|0;c[q>>2]=0;while(1){if((c[q>>2]|0)>=(c[(c[l>>2]|0)+2324>>2]|0))break a;j=(c[r>>2]|0)+1376|0;c[j>>2]=(c[j>>2]|0)+((((c[(c[g>>2]|0)+16+(c[q>>2]<<2)>>2]|0)-(c[(c[r>>2]|0)+1376>>2]|0)>>16)*4634|0)+(((c[(c[g>>2]|0)+16+(c[q>>2]<<2)>>2]|0)-(c[(c[r>>2]|0)+1376>>2]|0)&65535)*4634>>16));c[q>>2]=(c[q>>2]|0)+1}}while(0);if(!(c[(c[l>>2]|0)+4160>>2]|0)){oj((c[r>>2]|0)+1312|0,0,c[(c[l>>2]|0)+2340>>2]<<2|0)|0;i=t;return}j=(c[n>>2]|0)+16|0;c[o>>2]=ia()|0;h=i;i=i+((1*(j<<2)|0)+15&-16)|0;j=_(b[(c[l>>2]|0)+4168+56>>1]>>16,(c[(c[l>>2]|0)+4168+72+4>>2]&65535)<<16>>16)|0;j=j+((_(b[(c[l>>2]|0)+4168+56>>1]&65535,(c[(c[l>>2]|0)+4168+72+4>>2]&65535)<<16>>16)|0)>>16)|0;c[k>>2]=j+(_(b[(c[l>>2]|0)+4168+56>>1]|0,(c[(c[l>>2]|0)+4168+72+4>>2]>>15)+1>>1)|0);if((c[k>>2]|0)<2097152?(c[(c[r>>2]|0)+1376>>2]|0)<=8388608:0){j=_(c[k>>2]>>16,(c[k>>2]&65535)<<16>>16)|0;j=j+((_(c[k>>2]&65535,(c[k>>2]&65535)<<16>>16)|0)>>16)|0;c[k>>2]=j+(_(c[k>>2]|0,(c[k>>2]>>15)+1>>1)|0);j=_(c[(c[r>>2]|0)+1376>>2]>>16,(c[(c[r>>2]|0)+1376>>2]&65535)<<16>>16)|0;j=j+((_(c[(c[r>>2]|0)+1376>>2]&65535,(c[(c[r>>2]|0)+1376>>2]&65535)<<16>>16)|0)>>16)|0;j=j+(_(c[(c[r>>2]|0)+1376>>2]|0,(c[(c[r>>2]|0)+1376>>2]>>15)+1>>1)|0)|0;c[k>>2]=j-(c[k>>2]<<5);c[k>>2]=(yd(c[k>>2]|0)|0)<<8}else{c[k>>2]=_(c[k>>2]>>16,c[k>>2]>>16)|0;j=_(c[(c[r>>2]|0)+1376>>2]>>16,c[(c[r>>2]|0)+1376>>2]>>16)|0;c[k>>2]=j-(c[k>>2]<<5);c[k>>2]=(yd(c[k>>2]|0)|0)<<16}Cd(h+64|0,c[r>>2]|0,c[k>>2]|0,c[n>>2]|0,(c[r>>2]|0)+1380|0);ag(p,(c[r>>2]|0)+1280|0,c[(c[l>>2]|0)+2340>>2]|0);g=h;e=(c[r>>2]|0)+1312|0;f=g+64|0;do{c[g>>2]=c[e>>2];g=g+4|0;e=e+4|0}while((g|0)<(f|0));c[q>>2]=0;while(1){if((c[q>>2]|0)>=(c[n>>2]|0))break;c[s>>2]=c[(c[l>>2]|0)+2340>>2]>>1;k=_(c[h+(16+(c[q>>2]|0)-1<<2)>>2]>>16,b[p>>1]|0)|0;c[s>>2]=(c[s>>2]|0)+(k+((_(c[h+(16+(c[q>>2]|0)-1<<2)>>2]&65535,b[p>>1]|0)|0)>>16));k=_(c[h+(16+(c[q>>2]|0)-2<<2)>>2]>>16,b[p+2>>1]|0)|0;c[s>>2]=(c[s>>2]|0)+(k+((_(c[h+(16+(c[q>>2]|0)-2<<2)>>2]&65535,b[p+2>>1]|0)|0)>>16));k=_(c[h+(16+(c[q>>2]|0)-3<<2)>>2]>>16,b[p+4>>1]|0)|0;c[s>>2]=(c[s>>2]|0)+(k+((_(c[h+(16+(c[q>>2]|0)-3<<2)>>2]&65535,b[p+4>>1]|0)|0)>>16));k=_(c[h+(16+(c[q>>2]|0)-4<<2)>>2]>>16,b[p+6>>1]|0)|0;c[s>>2]=(c[s>>2]|0)+(k+((_(c[h+(16+(c[q>>2]|0)-4<<2)>>2]&65535,b[p+6>>1]|0)|0)>>16));k=_(c[h+(16+(c[q>>2]|0)-5<<2)>>2]>>16,b[p+8>>1]|0)|0;c[s>>2]=(c[s>>2]|0)+(k+((_(c[h+(16+(c[q>>2]|0)-5<<2)>>2]&65535,b[p+8>>1]|0)|0)>>16));k=_(c[h+(16+(c[q>>2]|0)-6<<2)>>2]>>16,b[p+10>>1]|0)|0;c[s>>2]=(c[s>>2]|0)+(k+((_(c[h+(16+(c[q>>2]|0)-6<<2)>>2]&65535,b[p+10>>1]|0)|0)>>16));k=_(c[h+(16+(c[q>>2]|0)-7<<2)>>2]>>16,b[p+12>>1]|0)|0;c[s>>2]=(c[s>>2]|0)+(k+((_(c[h+(16+(c[q>>2]|0)-7<<2)>>2]&65535,b[p+12>>1]|0)|0)>>16));k=_(c[h+(16+(c[q>>2]|0)-8<<2)>>2]>>16,b[p+14>>1]|0)|0;c[s>>2]=(c[s>>2]|0)+(k+((_(c[h+(16+(c[q>>2]|0)-8<<2)>>2]&65535,b[p+14>>1]|0)|0)>>16));k=_(c[h+(16+(c[q>>2]|0)-9<<2)>>2]>>16,b[p+16>>1]|0)|0;c[s>>2]=(c[s>>2]|0)+(k+((_(c[h+(16+(c[q>>2]|0)-9<<2)>>2]&65535,b[p+16>>1]|0)|0)>>16));k=_(c[h+(16+(c[q>>2]|0)-10<<2)>>2]>>16,b[p+18>>1]|0)|0;c[s>>2]=(c[s>>2]|0)+(k+((_(c[h+(16+(c[q>>2]|0)-10<<2)>>2]&65535,b[p+18>>1]|0)|0)>>16));if((c[(c[l>>2]|0)+2340>>2]|0)==16){k=_(c[h+(16+(c[q>>2]|0)-11<<2)>>2]>>16,b[p+20>>1]|0)|0;c[s>>2]=(c[s>>2]|0)+(k+((_(c[h+(16+(c[q>>2]|0)-11<<2)>>2]&65535,b[p+20>>1]|0)|0)>>16));k=_(c[h+(16+(c[q>>2]|0)-12<<2)>>2]>>16,b[p+22>>1]|0)|0;c[s>>2]=(c[s>>2]|0)+(k+((_(c[h+(16+(c[q>>2]|0)-12<<2)>>2]&65535,b[p+22>>1]|0)|0)>>16));k=_(c[h+(16+(c[q>>2]|0)-13<<2)>>2]>>16,b[p+24>>1]|0)|0;c[s>>2]=(c[s>>2]|0)+(k+((_(c[h+(16+(c[q>>2]|0)-13<<2)>>2]&65535,b[p+24>>1]|0)|0)>>16));k=_(c[h+(16+(c[q>>2]|0)-14<<2)>>2]>>16,b[p+26>>1]|0)|0;c[s>>2]=(c[s>>2]|0)+(k+((_(c[h+(16+(c[q>>2]|0)-14<<2)>>2]&65535,b[p+26>>1]|0)|0)>>16));k=_(c[h+(16+(c[q>>2]|0)-15<<2)>>2]>>16,b[p+28>>1]|0)|0;c[s>>2]=(c[s>>2]|0)+(k+((_(c[h+(16+(c[q>>2]|0)-15<<2)>>2]&65535,b[p+28>>1]|0)|0)>>16));k=_(c[h+(16+(c[q>>2]|0)-16<<2)>>2]>>16,b[p+30>>1]|0)|0;c[s>>2]=(c[s>>2]|0)+(k+((_(c[h+(16+(c[q>>2]|0)-16<<2)>>2]&65535,b[p+30>>1]|0)|0)>>16))}c[h+(16+(c[q>>2]|0)<<2)>>2]=(c[h+(16+(c[q>>2]|0)<<2)>>2]|0)+(c[s>>2]<<4);if(((b[(c[m>>2]|0)+(c[q>>2]<<1)>>1]|0)+((c[h+(16+(c[q>>2]|0)<<2)>>2]>>9)+1>>1)|0)<=32767)if(((b[(c[m>>2]|0)+(c[q>>2]<<1)>>1]|0)+((c[h+(16+(c[q>>2]|0)<<2)>>2]>>9)+1>>1)|0)<-32768)g=-32768;else g=(b[(c[m>>2]|0)+(c[q>>2]<<1)>>1]|0)+((c[h+(16+(c[q>>2]|0)<<2)>>2]>>9)+1>>1)|0;else g=32767;b[(c[m>>2]|0)+(c[q>>2]<<1)>>1]=g;c[q>>2]=(c[q>>2]|0)+1}g=(c[r>>2]|0)+1312|0;e=h+(c[n>>2]<<2)|0;f=g+64|0;do{c[g>>2]=c[e>>2];g=g+4|0;e=e+4|0}while((g|0)<(f|0));na(c[o>>2]|0);i=t;return}function yd(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0;h=i;i=i+32|0;b=h+16|0;d=h+12|0;g=h+8|0;f=h+4|0;e=h;c[d>>2]=a;if((c[d>>2]|0)<=0){c[b>>2]=0;g=c[b>>2]|0;i=h;return g|0}zd(c[d>>2]|0,f,e);if(c[f>>2]&1|0)c[g>>2]=32768;else c[g>>2]=46214;c[g>>2]=c[g>>2]>>(c[f>>2]>>1);a=_(c[g>>2]>>16,(((c[e>>2]&65535)<<16>>16)*213&65535)<<16>>16)|0;c[g>>2]=(c[g>>2]|0)+(a+((_(c[g>>2]&65535,(((c[e>>2]&65535)<<16>>16)*213&65535)<<16>>16)|0)>>16));c[b>>2]=c[g>>2];g=c[b>>2]|0;i=h;return g|0}function zd(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0;e=i;i=i+16|0;h=e+12|0;j=e+8|0;f=e+4|0;g=e;c[h>>2]=a;c[j>>2]=b;c[f>>2]=d;c[g>>2]=Ad(c[h>>2]|0)|0;c[c[j>>2]>>2]=c[g>>2];b=(Bd(c[h>>2]|0,24-(c[g>>2]|0)|0)|0)&127;c[c[f>>2]>>2]=b;i=e;return}function Ad(a){a=a|0;var b=0,d=0;d=i;i=i+16|0;b=d;c[b>>2]=a;if(!(c[b>>2]|0)){a=32;i=d;return a|0}a=32-(32-(aa(c[b>>2]|0)|0))|0;i=d;return a|0}function Bd(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0;k=i;i=i+32|0;e=k+20|0;d=k+16|0;f=k+12|0;j=k+8|0;h=k+4|0;g=k;c[d>>2]=a;c[f>>2]=b;c[j>>2]=c[d>>2];c[h>>2]=c[f>>2];c[g>>2]=0-(c[f>>2]|0);if(!(c[f>>2]|0)){c[e>>2]=c[d>>2];j=c[e>>2]|0;i=k;return j|0}d=c[j>>2]|0;if((c[f>>2]|0)<0){c[e>>2]=d<<c[g>>2]|(c[j>>2]|0)>>>(32-(c[g>>2]|0)|0);j=c[e>>2]|0;i=k;return j|0}else{c[e>>2]=d<<32-(c[h>>2]|0)|(c[j>>2]|0)>>>(c[h>>2]|0);j=c[e>>2]|0;i=k;return j|0}return 0}function Cd(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;q=i;i=i+48|0;g=q+32|0;h=q+28|0;j=q+24|0;k=q+20|0;l=q+16|0;p=q+12|0;n=q+8|0;o=q+4|0;m=q;c[g>>2]=a;c[h>>2]=b;c[j>>2]=d;c[k>>2]=e;c[l>>2]=f;c[m>>2]=255;while(1){if((c[m>>2]|0)<=(c[k>>2]|0))break;c[m>>2]=c[m>>2]>>1}c[p>>2]=c[c[l>>2]>>2];c[n>>2]=0;while(1){d=c[p>>2]|0;if((c[n>>2]|0)>=(c[k>>2]|0))break;c[p>>2]=907633515+(_(d,196314165)|0);c[o>>2]=c[p>>2]>>24&c[m>>2];f=_(c[(c[h>>2]|0)+(c[o>>2]<<2)>>2]>>16,(c[j>>2]>>4&65535)<<16>>16)|0;f=f+((_(c[(c[h>>2]|0)+(c[o>>2]<<2)>>2]&65535,(c[j>>2]>>4&65535)<<16>>16)|0)>>16)|0;if((f+(_(c[(c[h>>2]|0)+(c[o>>2]<<2)>>2]|0,(c[j>>2]>>4>>15)+1>>1)|0)|0)<=32767){f=_(c[(c[h>>2]|0)+(c[o>>2]<<2)>>2]>>16,(c[j>>2]>>4&65535)<<16>>16)|0;f=f+((_(c[(c[h>>2]|0)+(c[o>>2]<<2)>>2]&65535,(c[j>>2]>>4&65535)<<16>>16)|0)>>16)|0;if((f+(_(c[(c[h>>2]|0)+(c[o>>2]<<2)>>2]|0,(c[j>>2]>>4>>15)+1>>1)|0)|0)<-32768)d=-32768;else{d=_(c[(c[h>>2]|0)+(c[o>>2]<<2)>>2]>>16,(c[j>>2]>>4&65535)<<16>>16)|0;d=d+((_(c[(c[h>>2]|0)+(c[o>>2]<<2)>>2]&65535,(c[j>>2]>>4&65535)<<16>>16)|0)>>16)|0;d=d+(_(c[(c[h>>2]|0)+(c[o>>2]<<2)>>2]|0,(c[j>>2]>>4>>15)+1>>1)|0)|0}}else d=32767;c[(c[g>>2]|0)+(c[n>>2]<<2)>>2]=(d&65535)<<16>>16;c[n>>2]=(c[n>>2]|0)+1}c[c[l>>2]>>2]=d;i=q;return}function Dd(b,d,e,f,g,h){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;s=i;i=i+48|0;j=s+40|0;v=s+36|0;k=s+32|0;t=s+28|0;u=s+24|0;l=s+20|0;m=s+16|0;p=s+12|0;q=s+8|0;n=s+44|0;r=s+4|0;o=s;c[j>>2]=b;c[v>>2]=d;c[k>>2]=e;c[t>>2]=f;c[u>>2]=g;c[l>>2]=h;a[n+1>>0]=0;c[r>>2]=c[v>>2];c[m>>2]=(((c[u>>2]|0)+(c[t>>2]<<1)&65535)<<16>>16)*7;c[o>>2]=30184+(c[m>>2]|0);c[k>>2]=(c[k>>2]|0)+8>>4;c[m>>2]=0;while(1){if((c[m>>2]|0)>=(c[k>>2]|0))break;c[q>>2]=c[(c[l>>2]|0)+(c[m>>2]<<2)>>2];a:do if((c[q>>2]|0)>0){a[n>>0]=a[(c[o>>2]|0)+((c[q>>2]&31|0)<6?c[q>>2]&31:6)>>0]|0;c[p>>2]=0;while(1){if((c[p>>2]|0)>=16)break a;if(a[(c[r>>2]|0)+(c[p>>2]|0)>>0]|0)qc(c[j>>2]|0,(a[(c[r>>2]|0)+(c[p>>2]|0)>>0]>>15)+1|0,n,8);c[p>>2]=(c[p>>2]|0)+1}}while(0);c[r>>2]=(c[r>>2]|0)+16;c[m>>2]=(c[m>>2]|0)+1}i=s;return}function Ed(d,e,f,g,h,j){d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0;t=i;i=i+48|0;k=t+40|0;w=t+36|0;l=t+32|0;u=t+28|0;v=t+24|0;m=t+20|0;n=t+16|0;q=t+12|0;r=t+8|0;o=t+44|0;s=t+4|0;p=t;c[k>>2]=d;c[w>>2]=e;c[l>>2]=f;c[u>>2]=g;c[v>>2]=h;c[m>>2]=j;a[o+1>>0]=0;c[s>>2]=c[w>>2];c[n>>2]=(((c[v>>2]|0)+(c[u>>2]<<1)&65535)<<16>>16)*7;c[p>>2]=30184+(c[n>>2]|0);c[l>>2]=(c[l>>2]|0)+8>>4;c[n>>2]=0;while(1){if((c[n>>2]|0)>=(c[l>>2]|0))break;c[r>>2]=c[(c[m>>2]|0)+(c[n>>2]<<2)>>2];a:do if((c[r>>2]|0)>0){a[o>>0]=a[(c[p>>2]|0)+((c[r>>2]&31|0)<6?c[r>>2]&31:6)>>0]|0;c[q>>2]=0;while(1){if((c[q>>2]|0)>=16)break a;if((b[(c[s>>2]|0)+(c[q>>2]<<1)>>1]|0)>0){v=((ec(c[k>>2]|0,o,8)|0)<<1)-1|0;w=(c[s>>2]|0)+(c[q>>2]<<1)|0;b[w>>1]=_(b[w>>1]|0,v)|0}c[q>>2]=(c[q>>2]|0)+1}}while(0);c[s>>2]=(c[s>>2]|0)+32;c[n>>2]=(c[n>>2]|0)+1}i=t;return}function Fd(a){a=a|0;var b=0,d=0;b=i;i=i+16|0;d=b;c[d>>2]=a;oj(c[d>>2]|0,0,4260)|0;c[(c[d>>2]|0)+2376>>2]=1;c[c[d>>2]>>2]=65536;wd(c[d>>2]|0);ze(c[d>>2]|0);i=b;return 0}function Gd(d,e,f,g,h){d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0;M=i;i=i+144|0;o=M+100|0;p=M+96|0;q=M+92|0;l=M+88|0;r=M+84|0;B=M+80|0;D=M+76|0;E=M+72|0;L=M+68|0;J=M+64|0;z=M+60|0;K=M+56|0;t=M+52|0;v=M+48|0;I=M+44|0;u=M+104|0;y=M+40|0;x=M+36|0;w=M+32|0;C=M+28|0;A=M+24|0;n=M+20|0;m=M+16|0;G=M+12|0;F=M+8|0;H=M+4|0;s=M;c[o>>2]=d;c[p>>2]=e;c[q>>2]=f;c[l>>2]=g;c[r>>2]=h;c[E>>2]=0;f=c[(c[o>>2]|0)+2336>>2]|0;c[s>>2]=ia()|0;g=i;i=i+((1*(f<<1)|0)+15&-16)|0;f=i;i=i+((1*((c[(c[o>>2]|0)+2336>>2]|0)+(c[(c[o>>2]|0)+2328>>2]|0)<<2)|0)+15&-16)|0;j=i;i=i+((1*(c[(c[o>>2]|0)+2332>>2]<<2)|0)+15&-16)|0;k=i;i=i+((1*((c[(c[o>>2]|0)+2332>>2]|0)+16<<2)|0)+15&-16)|0;c[m>>2]=b[24558+(a[(c[o>>2]|0)+2736+29>>0]>>1<<2)+(a[(c[o>>2]|0)+2736+30>>0]<<1)>>1];if((a[(c[o>>2]|0)+2736+31>>0]|0)<4)c[z>>2]=1;else c[z>>2]=0;c[n>>2]=a[(c[o>>2]|0)+2736+34>>0];c[B>>2]=0;while(1){if((c[B>>2]|0)>=(c[(c[o>>2]|0)+2328>>2]|0))break;c[n>>2]=907633515+(_(c[n>>2]|0,196314165)|0);c[(c[o>>2]|0)+4+(c[B>>2]<<2)>>2]=b[(c[l>>2]|0)+(c[B>>2]<<1)>>1]<<14;h=(c[o>>2]|0)+4+(c[B>>2]<<2)|0;d=c[h>>2]|0;if((c[(c[o>>2]|0)+4+(c[B>>2]<<2)>>2]|0)<=0){if((d|0)<0){e=(c[o>>2]|0)+4+(c[B>>2]<<2)|0;c[e>>2]=(c[e>>2]|0)+1280}}else c[h>>2]=d-1280;e=(c[o>>2]|0)+4+(c[B>>2]<<2)|0;c[e>>2]=(c[e>>2]|0)+(c[m>>2]<<4);if((c[n>>2]|0)<0)c[(c[o>>2]|0)+4+(c[B>>2]<<2)>>2]=0-(c[(c[o>>2]|0)+4+(c[B>>2]<<2)>>2]|0);c[n>>2]=(c[n>>2]|0)+(b[(c[l>>2]|0)+(c[B>>2]<<1)>>1]|0);c[B>>2]=(c[B>>2]|0)+1}h=k;d=(c[o>>2]|0)+1284|0;e=h+64|0;do{c[h>>2]=c[d>>2];h=h+4|0;d=d+4|0}while((h|0)<(e|0));c[F>>2]=(c[o>>2]|0)+4;c[I>>2]=c[q>>2];c[J>>2]=c[(c[o>>2]|0)+2336>>2];c[D>>2]=0;while(1){if((c[D>>2]|0)>=(c[(c[o>>2]|0)+2324>>2]|0))break;c[H>>2]=j;c[t>>2]=(c[p>>2]|0)+32+(c[D>>2]>>1<<5);pj(u|0,c[t>>2]|0,c[(c[o>>2]|0)+2340>>2]<<1|0)|0;c[v>>2]=(c[p>>2]|0)+96+((c[D>>2]|0)*5<<1);c[K>>2]=a[(c[o>>2]|0)+2736+29>>0];c[w>>2]=c[(c[p>>2]|0)+16+(c[D>>2]<<2)>>2]>>6;c[C>>2]=Hd(c[(c[p>>2]|0)+16+(c[D>>2]<<2)>>2]|0,47)|0;a:do if((c[(c[p>>2]|0)+16+(c[D>>2]<<2)>>2]|0)!=(c[c[o>>2]>>2]|0)){c[A>>2]=Jd(c[c[o>>2]>>2]|0,c[(c[p>>2]|0)+16+(c[D>>2]<<2)>>2]|0,16)|0;c[B>>2]=0;while(1){if((c[B>>2]|0)>=16)break a;n=_(c[A>>2]>>16,(c[k+(c[B>>2]<<2)>>2]&65535)<<16>>16)|0;n=n+((_(c[A>>2]&65535,(c[k+(c[B>>2]<<2)>>2]&65535)<<16>>16)|0)>>16)|0;n=n+(_(c[A>>2]|0,(c[k+(c[B>>2]<<2)>>2]>>15)+1>>1)|0)|0;c[k+(c[B>>2]<<2)>>2]=n;c[B>>2]=(c[B>>2]|0)+1}}else c[A>>2]=65536;while(0);c[c[o>>2]>>2]=c[(c[p>>2]|0)+16+(c[D>>2]<<2)>>2];if((c[(c[o>>2]|0)+4160>>2]|0?(c[(c[o>>2]|0)+4164>>2]|0)==2:0)?((c[D>>2]|0)<2?(a[(c[o>>2]|0)+2736+29>>0]|0)!=2:0):0){n=c[v>>2]|0;b[n>>1]=0;b[n+2>>1]=0;b[n+4>>1]=0;b[n+6>>1]=0;b[n+8>>1]=0;b[(c[v>>2]|0)+4>>1]=4096;c[K>>2]=2;c[(c[p>>2]|0)+(c[D>>2]<<2)>>2]=c[(c[o>>2]|0)+2308>>2]}b:do if((c[K>>2]|0)==2){c[E>>2]=c[(c[p>>2]|0)+(c[D>>2]<<2)>>2];if(c[D>>2]|0?!((c[D>>2]|0)==2&(c[z>>2]|0)!=0):0){if((c[A>>2]|0)==65536)break;c[B>>2]=0;while(1){if((c[B>>2]|0)>=((c[E>>2]|0)+2|0))break b;n=_(c[A>>2]>>16,(c[f+((c[J>>2]|0)-(c[B>>2]|0)-1<<2)>>2]&65535)<<16>>16)|0;n=n+((_(c[A>>2]&65535,(c[f+((c[J>>2]|0)-(c[B>>2]|0)-1<<2)>>2]&65535)<<16>>16)|0)>>16)|0;n=n+(_(c[A>>2]|0,(c[f+((c[J>>2]|0)-(c[B>>2]|0)-1<<2)>>2]>>15)+1>>1)|0)|0;c[f+((c[J>>2]|0)-(c[B>>2]|0)-1<<2)>>2]=n;c[B>>2]=(c[B>>2]|0)+1}}c[L>>2]=(c[(c[o>>2]|0)+2336>>2]|0)-(c[E>>2]|0)-(c[(c[o>>2]|0)+2340>>2]|0)-2;if((c[D>>2]|0)==2)pj((c[o>>2]|0)+1348+(c[(c[o>>2]|0)+2336>>2]<<1)|0,c[q>>2]|0,c[(c[o>>2]|0)+2332>>2]<<1<<1|0)|0;n=(c[L>>2]|0)+(_(c[D>>2]|0,c[(c[o>>2]|0)+2332>>2]|0)|0)|0;Xf(g+(c[L>>2]<<1)|0,(c[o>>2]|0)+1348+(n<<1)|0,c[t>>2]|0,(c[(c[o>>2]|0)+2336>>2]|0)-(c[L>>2]|0)|0,c[(c[o>>2]|0)+2340>>2]|0,c[r>>2]|0);if(!(c[D>>2]|0)){n=_(c[C>>2]>>16,(c[(c[p>>2]|0)+136>>2]&65535)<<16>>16)|0;c[C>>2]=n+((_(c[C>>2]&65535,(c[(c[p>>2]|0)+136>>2]&65535)<<16>>16)|0)>>16)<<2}c[B>>2]=0;while(1){if((c[B>>2]|0)>=((c[E>>2]|0)+2|0))break b;n=_(c[C>>2]>>16,b[g+((c[(c[o>>2]|0)+2336>>2]|0)-(c[B>>2]|0)-1<<1)>>1]|0)|0;n=n+((_(c[C>>2]&65535,b[g+((c[(c[o>>2]|0)+2336>>2]|0)-(c[B>>2]|0)-1<<1)>>1]|0)|0)>>16)|0;c[f+((c[J>>2]|0)-(c[B>>2]|0)-1<<2)>>2]=n;c[B>>2]=(c[B>>2]|0)+1}}while(0);c:do if((c[K>>2]|0)==2){c[G>>2]=f+((c[J>>2]|0)-(c[E>>2]|0)+2<<2);c[B>>2]=0;while(1){if((c[B>>2]|0)>=(c[(c[o>>2]|0)+2332>>2]|0))break c;c[y>>2]=2;n=_(c[c[G>>2]>>2]>>16,b[c[v>>2]>>1]|0)|0;c[y>>2]=(c[y>>2]|0)+(n+((_(c[c[G>>2]>>2]&65535,b[c[v>>2]>>1]|0)|0)>>16));n=_(c[(c[G>>2]|0)+-4>>2]>>16,b[(c[v>>2]|0)+2>>1]|0)|0;c[y>>2]=(c[y>>2]|0)+(n+((_(c[(c[G>>2]|0)+-4>>2]&65535,b[(c[v>>2]|0)+2>>1]|0)|0)>>16));n=_(c[(c[G>>2]|0)+-8>>2]>>16,b[(c[v>>2]|0)+4>>1]|0)|0;c[y>>2]=(c[y>>2]|0)+(n+((_(c[(c[G>>2]|0)+-8>>2]&65535,b[(c[v>>2]|0)+4>>1]|0)|0)>>16));n=_(c[(c[G>>2]|0)+-12>>2]>>16,b[(c[v>>2]|0)+6>>1]|0)|0;c[y>>2]=(c[y>>2]|0)+(n+((_(c[(c[G>>2]|0)+-12>>2]&65535,b[(c[v>>2]|0)+6>>1]|0)|0)>>16));n=_(c[(c[G>>2]|0)+-16>>2]>>16,b[(c[v>>2]|0)+8>>1]|0)|0;c[y>>2]=(c[y>>2]|0)+(n+((_(c[(c[G>>2]|0)+-16>>2]&65535,b[(c[v>>2]|0)+8>>1]|0)|0)>>16));c[G>>2]=(c[G>>2]|0)+4;c[(c[H>>2]|0)+(c[B>>2]<<2)>>2]=(c[(c[F>>2]|0)+(c[B>>2]<<2)>>2]|0)+(c[y>>2]<<1);c[f+(c[J>>2]<<2)>>2]=c[(c[H>>2]|0)+(c[B>>2]<<2)>>2]<<1;c[J>>2]=(c[J>>2]|0)+1;c[B>>2]=(c[B>>2]|0)+1}}else c[H>>2]=c[F>>2];while(0);c[B>>2]=0;while(1){if((c[B>>2]|0)>=(c[(c[o>>2]|0)+2332>>2]|0))break;c[x>>2]=c[(c[o>>2]|0)+2340>>2]>>1;n=_(c[k+(16+(c[B>>2]|0)-1<<2)>>2]>>16,b[u>>1]|0)|0;c[x>>2]=(c[x>>2]|0)+(n+((_(c[k+(16+(c[B>>2]|0)-1<<2)>>2]&65535,b[u>>1]|0)|0)>>16));n=_(c[k+(16+(c[B>>2]|0)-2<<2)>>2]>>16,b[u+2>>1]|0)|0;c[x>>2]=(c[x>>2]|0)+(n+((_(c[k+(16+(c[B>>2]|0)-2<<2)>>2]&65535,b[u+2>>1]|0)|0)>>16));n=_(c[k+(16+(c[B>>2]|0)-3<<2)>>2]>>16,b[u+4>>1]|0)|0;c[x>>2]=(c[x>>2]|0)+(n+((_(c[k+(16+(c[B>>2]|0)-3<<2)>>2]&65535,b[u+4>>1]|0)|0)>>16));n=_(c[k+(16+(c[B>>2]|0)-4<<2)>>2]>>16,b[u+6>>1]|0)|0;c[x>>2]=(c[x>>2]|0)+(n+((_(c[k+(16+(c[B>>2]|0)-4<<2)>>2]&65535,b[u+6>>1]|0)|0)>>16));n=_(c[k+(16+(c[B>>2]|0)-5<<2)>>2]>>16,b[u+8>>1]|0)|0;c[x>>2]=(c[x>>2]|0)+(n+((_(c[k+(16+(c[B>>2]|0)-5<<2)>>2]&65535,b[u+8>>1]|0)|0)>>16));n=_(c[k+(16+(c[B>>2]|0)-6<<2)>>2]>>16,b[u+10>>1]|0)|0;c[x>>2]=(c[x>>2]|0)+(n+((_(c[k+(16+(c[B>>2]|0)-6<<2)>>2]&65535,b[u+10>>1]|0)|0)>>16));n=_(c[k+(16+(c[B>>2]|0)-7<<2)>>2]>>16,b[u+12>>1]|0)|0;c[x>>2]=(c[x>>2]|0)+(n+((_(c[k+(16+(c[B>>2]|0)-7<<2)>>2]&65535,b[u+12>>1]|0)|0)>>16));n=_(c[k+(16+(c[B>>2]|0)-8<<2)>>2]>>16,b[u+14>>1]|0)|0;c[x>>2]=(c[x>>2]|0)+(n+((_(c[k+(16+(c[B>>2]|0)-8<<2)>>2]&65535,b[u+14>>1]|0)|0)>>16));n=_(c[k+(16+(c[B>>2]|0)-9<<2)>>2]>>16,b[u+16>>1]|0)|0;c[x>>2]=(c[x>>2]|0)+(n+((_(c[k+(16+(c[B>>2]|0)-9<<2)>>2]&65535,b[u+16>>1]|0)|0)>>16));n=_(c[k+(16+(c[B>>2]|0)-10<<2)>>2]>>16,b[u+18>>1]|0)|0;c[x>>2]=(c[x>>2]|0)+(n+((_(c[k+(16+(c[B>>2]|0)-10<<2)>>2]&65535,b[u+18>>1]|0)|0)>>16));if((c[(c[o>>2]|0)+2340>>2]|0)==16){n=_(c[k+(16+(c[B>>2]|0)-11<<2)>>2]>>16,b[u+20>>1]|0)|0;c[x>>2]=(c[x>>2]|0)+(n+((_(c[k+(16+(c[B>>2]|0)-11<<2)>>2]&65535,b[u+20>>1]|0)|0)>>16));n=_(c[k+(16+(c[B>>2]|0)-12<<2)>>2]>>16,b[u+22>>1]|0)|0;c[x>>2]=(c[x>>2]|0)+(n+((_(c[k+(16+(c[B>>2]|0)-12<<2)>>2]&65535,b[u+22>>1]|0)|0)>>16));n=_(c[k+(16+(c[B>>2]|0)-13<<2)>>2]>>16,b[u+24>>1]|0)|0;c[x>>2]=(c[x>>2]|0)+(n+((_(c[k+(16+(c[B>>2]|0)-13<<2)>>2]&65535,b[u+24>>1]|0)|0)>>16));n=_(c[k+(16+(c[B>>2]|0)-14<<2)>>2]>>16,b[u+26>>1]|0)|0;c[x>>2]=(c[x>>2]|0)+(n+((_(c[k+(16+(c[B>>2]|0)-14<<2)>>2]&65535,b[u+26>>1]|0)|0)>>16));n=_(c[k+(16+(c[B>>2]|0)-15<<2)>>2]>>16,b[u+28>>1]|0)|0;c[x>>2]=(c[x>>2]|0)+(n+((_(c[k+(16+(c[B>>2]|0)-15<<2)>>2]&65535,b[u+28>>1]|0)|0)>>16));n=_(c[k+(16+(c[B>>2]|0)-16<<2)>>2]>>16,b[u+30>>1]|0)|0;c[x>>2]=(c[x>>2]|0)+(n+((_(c[k+(16+(c[B>>2]|0)-16<<2)>>2]&65535,b[u+30>>1]|0)|0)>>16))}c[k+(16+(c[B>>2]|0)<<2)>>2]=(c[(c[H>>2]|0)+(c[B>>2]<<2)>>2]|0)+(c[x>>2]<<4);n=_(c[k+(16+(c[B>>2]|0)<<2)>>2]>>16,(c[w>>2]&65535)<<16>>16)|0;n=n+((_(c[k+(16+(c[B>>2]|0)<<2)>>2]&65535,(c[w>>2]&65535)<<16>>16)|0)>>16)|0;if(((n+(_(c[k+(16+(c[B>>2]|0)<<2)>>2]|0,(c[w>>2]>>15)+1>>1)|0)>>7)+1>>1|0)<=32767){n=_(c[k+(16+(c[B>>2]|0)<<2)>>2]>>16,(c[w>>2]&65535)<<16>>16)|0;n=n+((_(c[k+(16+(c[B>>2]|0)<<2)>>2]&65535,(c[w>>2]&65535)<<16>>16)|0)>>16)|0;if(((n+(_(c[k+(16+(c[B>>2]|0)<<2)>>2]|0,(c[w>>2]>>15)+1>>1)|0)>>7)+1>>1|0)<-32768)h=-32768;else{h=_(c[k+(16+(c[B>>2]|0)<<2)>>2]>>16,(c[w>>2]&65535)<<16>>16)|0;h=h+((_(c[k+(16+(c[B>>2]|0)<<2)>>2]&65535,(c[w>>2]&65535)<<16>>16)|0)>>16)|0;h=(h+(_(c[k+(16+(c[B>>2]|0)<<2)>>2]|0,(c[w>>2]>>15)+1>>1)|0)>>7)+1>>1}}else h=32767;b[(c[I>>2]|0)+(c[B>>2]<<1)>>1]=h;c[B>>2]=(c[B>>2]|0)+1}h=k;d=k+(c[(c[o>>2]|0)+2332>>2]<<2)|0;e=h+64|0;do{c[h>>2]=c[d>>2];h=h+4|0;d=d+4|0}while((h|0)<(e|0));c[F>>2]=(c[F>>2]|0)+(c[(c[o>>2]|0)+2332>>2]<<2);c[I>>2]=(c[I>>2]|0)+(c[(c[o>>2]|0)+2332>>2]<<1);c[D>>2]=(c[D>>2]|0)+1}h=(c[o>>2]|0)+1284|0;d=k;e=h+64|0;do{c[h>>2]=c[d>>2];h=h+4|0;d=d+4|0}while((h|0)<(e|0));na(c[s>>2]|0);i=M;return}function Hd(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0;h=i;i=i+48|0;f=h+32|0;n=h+28|0;d=h+24|0;j=h+20|0;g=h+16|0;k=h+12|0;m=h+8|0;l=h+4|0;e=h;c[n>>2]=a;c[d>>2]=b;b=c[n>>2]|0;c[j>>2]=(Id((c[n>>2]|0)>0?b:0-b|0)|0)-1;c[m>>2]=c[n>>2]<<c[j>>2];c[k>>2]=536870911/(c[m>>2]>>16|0)|0;c[e>>2]=c[k>>2]<<16;b=_(c[m>>2]>>16,(c[k>>2]&65535)<<16>>16)|0;c[l>>2]=536870912-(b+((_(c[m>>2]&65535,(c[k>>2]&65535)<<16>>16)|0)>>16))<<3;b=_(c[l>>2]>>16,(c[k>>2]&65535)<<16>>16)|0;b=(c[e>>2]|0)+(b+((_(c[l>>2]&65535,(c[k>>2]&65535)<<16>>16)|0)>>16))|0;c[e>>2]=b+(_(c[l>>2]|0,(c[k>>2]>>15)+1>>1)|0);c[g>>2]=61-(c[j>>2]|0)-(c[d>>2]|0);b=c[g>>2]|0;if((c[g>>2]|0)>0)if((b|0)<32){c[f>>2]=c[e>>2]>>c[g>>2];n=c[f>>2]|0;i=h;return n|0}else{c[f>>2]=0;n=c[f>>2]|0;i=h;return n|0}a=c[e>>2]|0;d=0-(c[g>>2]|0)|0;do if((-2147483648>>0-b|0)>(2147483647>>0-(c[g>>2]|0)|0)){if((a|0)>(-2147483648>>d|0)){b=-2147483648>>0-(c[g>>2]|0);break}if((c[e>>2]|0)<(2147483647>>0-(c[g>>2]|0)|0)){b=2147483647>>0-(c[g>>2]|0);break}else{b=c[e>>2]|0;break}}else{if((a|0)>(2147483647>>d|0)){b=2147483647>>0-(c[g>>2]|0);break}if((c[e>>2]|0)<(-2147483648>>0-(c[g>>2]|0)|0)){b=-2147483648>>0-(c[g>>2]|0);break}else{b=c[e>>2]|0;break}}while(0);c[f>>2]=b<<0-(c[g>>2]|0);n=c[f>>2]|0;i=h;return n|0}function Id(a){a=a|0;var b=0,d=0;d=i;i=i+16|0;b=d;c[b>>2]=a;if(!(c[b>>2]|0)){a=32;i=d;return a|0}a=32-(32-(aa(c[b>>2]|0)|0))|0;i=d;return a|0}function Jd(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;h=i;i=i+48|0;f=h+40|0;q=h+36|0;p=h+32|0;j=h+28|0;l=h+24|0;k=h+20|0;g=h+16|0;m=h+12|0;n=h+8|0;o=h+4|0;e=h;c[q>>2]=a;c[p>>2]=b;c[j>>2]=d;b=c[q>>2]|0;c[l>>2]=(Id((c[q>>2]|0)>0?b:0-b|0)|0)-1;c[n>>2]=c[q>>2]<<c[l>>2];b=c[p>>2]|0;c[k>>2]=(Id((c[p>>2]|0)>0?b:0-b|0)|0)-1;c[o>>2]=c[p>>2]<<c[k>>2];c[m>>2]=536870911/(c[o>>2]>>16|0)|0;b=_(c[n>>2]>>16,(c[m>>2]&65535)<<16>>16)|0;c[e>>2]=b+((_(c[n>>2]&65535,(c[m>>2]&65535)<<16>>16)|0)>>16);b=c[n>>2]|0;a=c[o>>2]|0;d=c[e>>2]|0;d=yj(a|0,((a|0)<0)<<31>>31|0,d|0,((d|0)<0)<<31>>31|0)|0;d=nj(d|0,C|0,32)|0;c[n>>2]=b-(d<<3);d=_(c[n>>2]>>16,(c[m>>2]&65535)<<16>>16)|0;c[e>>2]=(c[e>>2]|0)+(d+((_(c[n>>2]&65535,(c[m>>2]&65535)<<16>>16)|0)>>16));c[g>>2]=29+(c[l>>2]|0)-(c[k>>2]|0)-(c[j>>2]|0);d=c[g>>2]|0;if((c[g>>2]|0)>=0)if((d|0)<32){c[f>>2]=c[e>>2]>>c[g>>2];q=c[f>>2]|0;i=h;return q|0}else{c[f>>2]=0;q=c[f>>2]|0;i=h;return q|0}a=c[e>>2]|0;b=0-(c[g>>2]|0)|0;do if((-2147483648>>0-d|0)>(2147483647>>0-(c[g>>2]|0)|0)){if((a|0)>(-2147483648>>b|0)){d=-2147483648>>0-(c[g>>2]|0);break}if((c[e>>2]|0)<(2147483647>>0-(c[g>>2]|0)|0)){d=2147483647>>0-(c[g>>2]|0);break}else{d=c[e>>2]|0;break}}else{if((a|0)>(2147483647>>b|0)){d=2147483647>>0-(c[g>>2]|0);break}if((c[e>>2]|0)<(-2147483648>>0-(c[g>>2]|0)|0)){d=-2147483648>>0-(c[g>>2]|0);break}else{d=c[e>>2]|0;break}}while(0);c[f>>2]=d<<0-(c[g>>2]|0);q=c[f>>2]|0;i=h;return q|0}function Kd(b,d,e,f,g,h,j){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0;x=i;i=i+192|0;k=x+180|0;l=x+176|0;m=x+172|0;n=x+168|0;o=x+164|0;p=x+160|0;q=x+156|0;s=x+152|0;t=x+148|0;v=x+144|0;u=x+4|0;r=x;c[k>>2]=b;c[l>>2]=d;c[m>>2]=e;c[n>>2]=f;c[o>>2]=g;c[p>>2]=h;c[q>>2]=j;c[v>>2]=0;c[s>>2]=c[(c[k>>2]|0)+2328>>2];c[u+136>>2]=0;do if(!(c[o>>2]|0))w=4;else{if((c[o>>2]|0)==2?(c[(c[k>>2]|0)+2420+(c[(c[k>>2]|0)+2388>>2]<<2)>>2]|0)==1:0){w=4;break}Ae(c[k>>2]|0,u,c[m>>2]|0,1,c[q>>2]|0)}while(0);if((w|0)==4){d=(c[s>>2]|0)+16-1&-16;c[r>>2]=ia()|0;w=i;i=i+((1*(d<<1)|0)+15&-16)|0;Md(c[k>>2]|0,c[l>>2]|0,c[(c[k>>2]|0)+2388>>2]|0,c[o>>2]|0,c[p>>2]|0);Nd(c[l>>2]|0,w,a[(c[k>>2]|0)+2736+29>>0]|0,a[(c[k>>2]|0)+2736+30>>0]|0,c[(c[k>>2]|0)+2328>>2]|0);Ld(c[k>>2]|0,u,c[p>>2]|0);Gd(c[k>>2]|0,u,c[m>>2]|0,w,c[q>>2]|0);Ae(c[k>>2]|0,u,c[m>>2]|0,0,c[q>>2]|0);c[(c[k>>2]|0)+4160>>2]=0;c[(c[k>>2]|0)+4164>>2]=a[(c[k>>2]|0)+2736+29>>0];c[(c[k>>2]|0)+2376>>2]=0;na(c[r>>2]|0)}c[t>>2]=(c[(c[k>>2]|0)+2336>>2]|0)-(c[(c[k>>2]|0)+2328>>2]|0);qj((c[k>>2]|0)+1348|0,(c[k>>2]|0)+1348+(c[(c[k>>2]|0)+2328>>2]<<1)|0,c[t>>2]<<1|0)|0;pj((c[k>>2]|0)+1348+(c[t>>2]<<1)|0,c[m>>2]|0,c[(c[k>>2]|0)+2328>>2]<<1|0)|0;xd(c[k>>2]|0,u,c[m>>2]|0,c[s>>2]|0);Le(c[k>>2]|0,c[m>>2]|0,c[s>>2]|0);c[(c[k>>2]|0)+2308>>2]=c[u+((c[(c[k>>2]|0)+2324>>2]|0)-1<<2)>>2];c[c[n>>2]>>2]=c[s>>2];i=x;return c[v>>2]|0}function Ld(d,e,f){d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;p=i;i=i+96|0;g=p+24|0;h=p+20|0;q=p+16|0;l=p+12|0;m=p+8|0;j=p+4|0;o=p+64|0;n=p+32|0;k=p;c[g>>2]=d;c[h>>2]=e;c[q>>2]=f;ae((c[h>>2]|0)+16|0,(c[g>>2]|0)+2736|0,(c[g>>2]|0)+2312|0,(c[q>>2]|0)==2&1,c[(c[g>>2]|0)+2324>>2]|0);ge(o,(c[g>>2]|0)+2736+8|0,c[(c[g>>2]|0)+2732>>2]|0);ag((c[h>>2]|0)+32+32|0,o,c[(c[g>>2]|0)+2340>>2]|0);if((c[(c[g>>2]|0)+2376>>2]|0)==1)a[(c[g>>2]|0)+2736+31>>0]=4;if((a[(c[g>>2]|0)+2736+31>>0]|0)<4){c[l>>2]=0;while(1){if((c[l>>2]|0)>=(c[(c[g>>2]|0)+2340>>2]|0))break;q=(b[(c[g>>2]|0)+2344+(c[l>>2]<<1)>>1]|0)+((_(a[(c[g>>2]|0)+2736+31>>0]|0,(b[o+(c[l>>2]<<1)>>1]|0)-(b[(c[g>>2]|0)+2344+(c[l>>2]<<1)>>1]|0)|0)|0)>>2)&65535;b[n+(c[l>>2]<<1)>>1]=q;c[l>>2]=(c[l>>2]|0)+1}ag((c[h>>2]|0)+32|0,n,c[(c[g>>2]|0)+2340>>2]|0)}else pj((c[h>>2]|0)+32|0,(c[h>>2]|0)+32+32|0,c[(c[g>>2]|0)+2340>>2]<<1|0)|0;pj((c[g>>2]|0)+2344|0,o|0,c[(c[g>>2]|0)+2340>>2]<<1|0)|0;if(c[(c[g>>2]|0)+4160>>2]|0){Pf((c[h>>2]|0)+32|0,c[(c[g>>2]|0)+2340>>2]|0,63570);Pf((c[h>>2]|0)+32+32|0,c[(c[g>>2]|0)+2340>>2]|0,63570)}if((a[(c[g>>2]|0)+2736+29>>0]|0)!=2){oj(c[h>>2]|0,0,c[(c[g>>2]|0)+2324>>2]<<2|0)|0;oj((c[h>>2]|0)+96|0,0,(c[(c[g>>2]|0)+2324>>2]|0)*5<<1|0)|0;a[(c[g>>2]|0)+2736+32>>0]=0;c[(c[h>>2]|0)+136>>2]=0;i=p;return}Qf(b[(c[g>>2]|0)+2736+26>>1]|0,a[(c[g>>2]|0)+2736+28>>0]|0,c[h>>2]|0,c[(c[g>>2]|0)+2316>>2]|0,c[(c[g>>2]|0)+2324>>2]|0);c[k>>2]=c[17644+(a[(c[g>>2]|0)+2736+32>>0]<<2)>>2];c[m>>2]=0;while(1){if((c[m>>2]|0)>=(c[(c[g>>2]|0)+2324>>2]|0))break;c[j>>2]=a[(c[g>>2]|0)+2736+4+(c[m>>2]|0)>>0];c[l>>2]=0;while(1){if((c[l>>2]|0)>=5)break;b[(c[h>>2]|0)+96+(((c[m>>2]|0)*5|0)+(c[l>>2]|0)<<1)>>1]=a[(c[k>>2]|0)+(((c[j>>2]|0)*5|0)+(c[l>>2]|0))>>0]<<7;c[l>>2]=(c[l>>2]|0)+1}c[m>>2]=(c[m>>2]|0)+1}c[j>>2]=a[(c[g>>2]|0)+2736+33>>0];c[(c[h>>2]|0)+136>>2]=b[24566+(c[j>>2]<<1)>>1];i=p;return}function Md(d,e,f,g,h){d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;t=i;i=i+96|0;j=t+36|0;k=t+32|0;u=t+28|0;v=t+24|0;l=t+20|0;q=t+16|0;r=t+12|0;m=t+8|0;n=t+4|0;o=t;p=t+40|0;s=t+72|0;c[j>>2]=d;c[k>>2]=e;c[u>>2]=f;c[v>>2]=g;c[l>>2]=h;if(!(c[v>>2]|0)?!(c[(c[j>>2]|0)+2404+(c[u>>2]<<2)>>2]|0):0)c[m>>2]=ec(c[k>>2]|0,29024,8)|0;else c[m>>2]=(ec(c[k>>2]|0,29020,8)|0)+2;a[(c[j>>2]|0)+2736+29>>0]=c[m>>2]>>1;a[(c[j>>2]|0)+2736+30>>0]=c[m>>2]&1;f=c[k>>2]|0;if((c[l>>2]|0)==2){v=(ec(f,26771,8)|0)&255;a[(c[j>>2]|0)+2736>>0]=v}else{u=(ec(f,26747+(a[(c[j>>2]|0)+2736+29>>0]<<3)|0,8)|0)<<3&255;a[(c[j>>2]|0)+2736>>0]=u;u=((ec(c[k>>2]|0,29049,8)|0)&255)<<24>>24;v=(c[j>>2]|0)+2736|0;a[v>>0]=(a[v>>0]|0)+u}c[q>>2]=1;while(1){f=c[k>>2]|0;if((c[q>>2]|0)>=(c[(c[j>>2]|0)+2324>>2]|0))break;v=(ec(f,26771,8)|0)&255;a[(c[j>>2]|0)+2736+(c[q>>2]|0)>>0]=v;c[q>>2]=(c[q>>2]|0)+1}v=_(a[(c[j>>2]|0)+2736+29>>0]>>1,b[c[(c[j>>2]|0)+2732>>2]>>1]|0)|0;v=(ec(f,(c[(c[(c[j>>2]|0)+2732>>2]|0)+12>>2]|0)+v|0,8)|0)&255;a[(c[j>>2]|0)+2736+8>>0]=v;nf(p,s,c[(c[j>>2]|0)+2732>>2]|0,a[(c[j>>2]|0)+2736+8>>0]|0);c[q>>2]=0;while(1){if((c[q>>2]|0)>=(b[(c[(c[j>>2]|0)+2732>>2]|0)+2>>1]|0))break;c[m>>2]=ec(c[k>>2]|0,(c[(c[(c[j>>2]|0)+2732>>2]|0)+24>>2]|0)+(b[p+(c[q>>2]<<1)>>1]|0)|0,8)|0;if(c[m>>2]|0){if((c[m>>2]|0)==8){v=ec(c[k>>2]|0,29057,8)|0;c[m>>2]=(c[m>>2]|0)+v}}else{v=ec(c[k>>2]|0,29057,8)|0;c[m>>2]=(c[m>>2]|0)-v}a[(c[j>>2]|0)+2736+8+((c[q>>2]|0)+1)>>0]=(c[m>>2]|0)-4;c[q>>2]=(c[q>>2]|0)+1}if((c[(c[j>>2]|0)+2324>>2]|0)==4){v=(ec(c[k>>2]|0,29026,8)|0)&255;a[(c[j>>2]|0)+2736+31>>0]=v}else a[(c[j>>2]|0)+2736+31>>0]=4;if((a[(c[j>>2]|0)+2736+29>>0]|0)!=2){v=c[j>>2]|0;v=v+2736|0;v=v+29|0;v=a[v>>0]|0;v=v<<24>>24;u=c[j>>2]|0;u=u+2396|0;c[u>>2]=v;u=c[k>>2]|0;u=ec(u,29034,8)|0;u=u&255;v=c[j>>2]|0;v=v+2736|0;v=v+34|0;a[v>>0]=u;i=t;return}c[n>>2]=1;if(((c[l>>2]|0)==2?(c[(c[j>>2]|0)+2396>>2]|0)==2:0)?(c[o>>2]=((ec(c[k>>2]|0,29096,8)|0)&65535)<<16>>16,(c[o>>2]|0)>0):0){c[o>>2]=(c[o>>2]|0)-9;b[(c[j>>2]|0)+2736+26>>1]=(b[(c[j>>2]|0)+2400>>1]|0)+(c[o>>2]|0);c[n>>2]=0}if(c[n>>2]|0){u=((ec(c[k>>2]|0,29064,8)|0)&65535)<<16>>16;u=(_(u,c[(c[j>>2]|0)+2316>>2]>>1)|0)&65535;b[(c[j>>2]|0)+2736+26>>1]=u;u=((ec(c[k>>2]|0,c[(c[j>>2]|0)+2380>>2]|0,8)|0)&65535)<<16>>16;v=(c[j>>2]|0)+2736+26|0;b[v>>1]=(b[v>>1]|0)+u}b[(c[j>>2]|0)+2400>>1]=b[(c[j>>2]|0)+2736+26>>1]|0;v=(ec(c[k>>2]|0,c[(c[j>>2]|0)+2384>>2]|0,8)|0)&255;a[(c[j>>2]|0)+2736+28>>0]=v;v=(ec(c[k>>2]|0,26812,8)|0)&255;a[(c[j>>2]|0)+2736+32>>0]=v;c[r>>2]=0;while(1){if((c[r>>2]|0)>=(c[(c[j>>2]|0)+2324>>2]|0))break;v=(ec(c[k>>2]|0,c[17620+(a[(c[j>>2]|0)+2736+32>>0]<<2)>>2]|0,8)|0)&255;a[(c[j>>2]|0)+2736+4+(c[r>>2]|0)>>0]=v;c[r>>2]=(c[r>>2]|0)+1}if(!(c[l>>2]|0)){v=(ec(c[k>>2]|0,29017,8)|0)&255;a[(c[j>>2]|0)+2736+33>>0]=v;v=c[j>>2]|0;v=v+2736|0;v=v+29|0;v=a[v>>0]|0;v=v<<24>>24;u=c[j>>2]|0;u=u+2396|0;c[u>>2]=v;u=c[k>>2]|0;u=ec(u,29034,8)|0;u=u&255;v=c[j>>2]|0;v=v+2736|0;v=v+34|0;a[v>>0]=u;i=t;return}else{a[(c[j>>2]|0)+2736+33>>0]=0;v=c[j>>2]|0;v=v+2736|0;v=v+29|0;v=a[v>>0]|0;v=v<<24>>24;u=c[j>>2]|0;u=u+2396|0;c[u>>2]=v;u=c[k>>2]|0;u=ec(u,29034,8)|0;u=u&255;v=c[j>>2]|0;v=v+2736|0;v=v+34|0;a[v>>0]=u;i=t;return}}function Nd(a,d,e,f,g){a=a|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0;y=i;i=i+224|0;k=y+212|0;l=y+208|0;m=y+204|0;n=y+200|0;o=y+196|0;q=y+192|0;s=y+188|0;t=y+184|0;r=y+180|0;p=y+176|0;u=y+172|0;h=y+168|0;x=y+88|0;v=y+8|0;w=y+4|0;j=y;c[k>>2]=a;c[l>>2]=d;c[m>>2]=e;c[n>>2]=f;c[o>>2]=g;c[h>>2]=ec(c[k>>2]|0,29523+((c[m>>2]>>1)*9|0)|0,8)|0;c[r>>2]=c[o>>2]>>4;if((c[r>>2]<<4|0)<(c[o>>2]|0))c[r>>2]=(c[r>>2]|0)+1;c[j>>2]=29181+((c[h>>2]|0)*18|0);c[q>>2]=0;while(1){if((c[q>>2]|0)>=(c[r>>2]|0))break;c[v+(c[q>>2]<<2)>>2]=0;e=ec(c[k>>2]|0,c[j>>2]|0,8)|0;c[x+(c[q>>2]<<2)>>2]=e;while(1){h=c[q>>2]|0;if((c[x+(c[q>>2]<<2)>>2]|0)!=17)break;e=v+(h<<2)|0;c[e>>2]=(c[e>>2]|0)+1;e=ec(c[k>>2]|0,29343+((c[v+(c[q>>2]<<2)>>2]|0)==10&1)|0,8)|0;c[x+(c[q>>2]<<2)>>2]=e}c[q>>2]=h+1}c[q>>2]=0;while(1){if((c[q>>2]|0)>=(c[r>>2]|0))break;h=(c[l>>2]|0)+((c[q>>2]&65535)<<16>>16<<4<<1)|0;if((c[x+(c[q>>2]<<2)>>2]|0)>0)Se(h,c[k>>2]|0,c[x+(c[q>>2]<<2)>>2]|0);else{g=h+32|0;do{b[h>>1]=0;h=h+2|0}while((h|0)<(g|0))}c[q>>2]=(c[q>>2]|0)+1}c[q>>2]=0;while(1){if((c[q>>2]|0)>=(c[r>>2]|0))break;if((c[v+(c[q>>2]<<2)>>2]|0)>0){c[u>>2]=c[v+(c[q>>2]<<2)>>2];c[w>>2]=(c[l>>2]|0)+((c[q>>2]&65535)<<16>>16<<4<<1);c[t>>2]=0;while(1){if((c[t>>2]|0)>=16)break;c[p>>2]=b[(c[w>>2]|0)+(c[t>>2]<<1)>>1];c[s>>2]=0;while(1){h=c[p>>2]|0;if((c[s>>2]|0)>=(c[u>>2]|0))break;c[p>>2]=h<<1;j=ec(c[k>>2]|0,29015,8)|0;c[p>>2]=(c[p>>2]|0)+j;c[s>>2]=(c[s>>2]|0)+1}b[(c[w>>2]|0)+(c[t>>2]<<1)>>1]=h;c[t>>2]=(c[t>>2]|0)+1}j=x+(c[q>>2]<<2)|0;c[j>>2]=c[j>>2]|c[u>>2]<<5}c[q>>2]=(c[q>>2]|0)+1}Ed(c[k>>2]|0,c[l>>2]|0,c[o>>2]|0,c[m>>2]|0,c[n>>2]|0,x);i=y;return}function Od(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0;l=i;i=i+32|0;g=l+16|0;h=l+12|0;f=l+8|0;j=l+4|0;k=l;c[g>>2]=b;c[h>>2]=d;c[f>>2]=e;c[k>>2]=0;c[(c[g>>2]|0)+2332>>2]=((c[h>>2]&65535)<<16>>16)*5;c[j>>2]=_((c[(c[g>>2]|0)+2324>>2]&65535)<<16>>16,(c[(c[g>>2]|0)+2332>>2]&65535)<<16>>16)|0;if(!((c[(c[g>>2]|0)+2316>>2]|0)==(c[h>>2]|0)?(c[(c[g>>2]|0)+2320>>2]|0)==(c[f>>2]|0):0)){b=ig((c[g>>2]|0)+2432|0,((c[h>>2]&65535)<<16>>16)*1e3|0,c[f>>2]|0,0)|0;c[k>>2]=(c[k>>2]|0)+b;c[(c[g>>2]|0)+2320>>2]=c[f>>2]}if((c[(c[g>>2]|0)+2316>>2]|0)==(c[h>>2]|0)?(c[j>>2]|0)==(c[(c[g>>2]|0)+2328>>2]|0):0){k=c[k>>2]|0;i=l;return k|0}f=(c[(c[g>>2]|0)+2324>>2]|0)==4;e=(c[g>>2]|0)+2384|0;do if((c[h>>2]|0)==8)if(f){c[e>>2]=29151;break}else{c[e>>2]=29174;break}else if(f){c[e>>2]=29117;break}else{c[e>>2]=29162;break}while(0);if((c[(c[g>>2]|0)+2316>>2]|0)!=(c[h>>2]|0)){c[(c[g>>2]|0)+2336>>2]=((c[h>>2]&65535)<<16>>16)*20;f=(c[g>>2]|0)+2340|0;if((c[h>>2]|0)==8|(c[h>>2]|0)==12){c[f>>2]=10;c[(c[g>>2]|0)+2732>>2]=17668}else{c[f>>2]=16;c[(c[g>>2]|0)+2732>>2]=17704}do if((c[h>>2]|0)!=16){if((c[h>>2]|0)==12){c[(c[g>>2]|0)+2380>>2]=29043;break}if((c[h>>2]|0)==8)c[(c[g>>2]|0)+2380>>2]=29034}else c[(c[g>>2]|0)+2380>>2]=29049;while(0);c[(c[g>>2]|0)+2376>>2]=1;c[(c[g>>2]|0)+2308>>2]=100;a[(c[g>>2]|0)+2312>>0]=10;c[(c[g>>2]|0)+4164>>2]=0;oj((c[g>>2]|0)+1348|0,0,960)|0;f=(c[g>>2]|0)+1284|0;e=f+64|0;do{c[f>>2]=0;f=f+4|0}while((f|0)<(e|0))}c[(c[g>>2]|0)+2316>>2]=c[h>>2];c[(c[g>>2]|0)+2328>>2]=c[j>>2];k=c[k>>2]|0;i=l;return k|0}function Pd(a){a=a|0;var b=0,d=0,e=0;d=i;i=i+16|0;e=d+4|0;b=d;c[e>>2]=a;c[b>>2]=0;c[c[e>>2]>>2]=8544;i=d;return c[b>>2]|0}function Qd(a){a=a|0;var b=0,d=0,e=0,f=0,g=0;g=i;i=i+16|0;b=g+12|0;e=g+8|0;f=g+4|0;d=g;c[b>>2]=a;c[f>>2]=0;c[d>>2]=c[b>>2];c[e>>2]=0;while(1){if((c[e>>2]|0)>=2)break;c[f>>2]=Fd((c[d>>2]|0)+((c[e>>2]|0)*4260|0)|0)|0;c[e>>2]=(c[e>>2]|0)+1}e=(c[b>>2]|0)+8520|0;c[e>>2]=0;c[e+4>>2]=0;c[e+8>>2]=0;c[(c[b>>2]|0)+8540>>2]=0;i=g;return c[f>>2]|0}function Rd(d,e,f,g,h,j,k,l){d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;k=k|0;l=l|0;var m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0;P=i;i=i+784|0;y=P+128|0;R=P+124|0;z=P+120|0;A=P+116|0;Q=P+112|0;r=P+108|0;B=P+104|0;C=P+100|0;s=P+96|0;G=P+92|0;I=P+88|0;F=P+84|0;M=P+80|0;J=P+76|0;n=P+72|0;N=P+64|0;u=P+56|0;L=P+52|0;K=P+48|0;E=P+44|0;w=P+40|0;O=P+36|0;x=P+32|0;m=P+28|0;p=P+136|0;o=P+24|0;D=P+20|0;t=P+16|0;v=P+12|0;H=P;c[R>>2]=d;c[z>>2]=e;c[A>>2]=f;c[Q>>2]=g;c[r>>2]=h;c[B>>2]=j;c[C>>2]=k;c[s>>2]=l;c[F>>2]=0;c[M>>2]=0;c[u>>2]=0;c[u+4>>2]=0;c[K>>2]=c[R>>2];c[E>>2]=c[K>>2];a:do if(c[Q>>2]|0){c[I>>2]=0;while(1){if((c[I>>2]|0)>=(c[(c[z>>2]|0)+4>>2]|0))break a;c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2388>>2]=0;c[I>>2]=(c[I>>2]|0)+1}}while(0);if((c[(c[z>>2]|0)+4>>2]|0)>(c[(c[K>>2]|0)+8536>>2]|0)){R=Fd((c[E>>2]|0)+4260|0)|0;c[M>>2]=(c[M>>2]|0)+R}if((c[(c[z>>2]|0)+4>>2]|0)==1?(c[(c[K>>2]|0)+8536>>2]|0)==2:0)l=(c[(c[z>>2]|0)+12>>2]|0)==((c[(c[E>>2]|0)+2316>>2]|0)*1e3|0);else l=0;c[O>>2]=l&1;b:do if(!(c[(c[E>>2]|0)+2388>>2]|0)){c[I>>2]=0;c:while(1){if((c[I>>2]|0)>=(c[(c[z>>2]|0)+4>>2]|0))break b;do if(!(c[(c[z>>2]|0)+16>>2]|0)){c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2392>>2]=1;c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2324>>2]=2}else{if((c[(c[z>>2]|0)+16>>2]|0)==10){c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2392>>2]=1;c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2324>>2]=2;break}if((c[(c[z>>2]|0)+16>>2]|0)==20){c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2392>>2]=1;c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2324>>2]=4;break}if((c[(c[z>>2]|0)+16>>2]|0)==40){c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2392>>2]=2;c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2324>>2]=4;break}if((c[(c[z>>2]|0)+16>>2]|0)!=60){q=23;break c}c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2392>>2]=3;c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2324>>2]=4}while(0);c[m>>2]=(c[(c[z>>2]|0)+12>>2]>>10)+1;if((c[m>>2]|0)!=8&(c[m>>2]|0)!=12&(c[m>>2]|0)!=16){q=25;break}R=Od((c[E>>2]|0)+((c[I>>2]|0)*4260|0)|0,c[m>>2]|0,c[(c[z>>2]|0)+8>>2]|0)|0;c[M>>2]=(c[M>>2]|0)+R;c[I>>2]=(c[I>>2]|0)+1}if((q|0)==23){c[y>>2]=-203;R=c[y>>2]|0;i=P;return R|0}else if((q|0)==25){c[y>>2]=-200;R=c[y>>2]|0;i=P;return R|0}}while(0);do if((c[c[z>>2]>>2]|0)==2?(c[(c[z>>2]|0)+4>>2]|0)==2:0){if((c[(c[K>>2]|0)+8532>>2]|0)!=1?(c[(c[K>>2]|0)+8536>>2]|0)!=1:0)break;c[(c[K>>2]|0)+8520>>2]=0;c[(c[K>>2]|0)+8520+8>>2]=0;pj((c[E>>2]|0)+4260+2432|0,(c[E>>2]|0)+2432|0,300)|0}while(0);c[(c[K>>2]|0)+8532>>2]=c[c[z>>2]>>2];c[(c[K>>2]|0)+8536>>2]=c[(c[z>>2]|0)+4>>2];if((c[(c[z>>2]|0)+8>>2]|0)<=48e3?(c[(c[z>>2]|0)+8>>2]|0)>=8e3:0){d:do if((c[A>>2]|0)!=1?(c[(c[E>>2]|0)+2388>>2]|0)==0:0){c[I>>2]=0;while(1){if((c[I>>2]|0)>=(c[(c[z>>2]|0)+4>>2]|0))break;c[G>>2]=0;while(1){R=(c[G>>2]|0)<(c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2392>>2]|0);l=dc(c[r>>2]|0,1)|0;if(!R)break;c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2404+(c[G>>2]<<2)>>2]=l;c[G>>2]=(c[G>>2]|0)+1}c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2416>>2]=l;c[I>>2]=(c[I>>2]|0)+1}c[I>>2]=0;while(1){if((c[I>>2]|0)>=(c[(c[z>>2]|0)+4>>2]|0))break;R=(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2420|0;c[R>>2]=0;c[R+4>>2]=0;c[R+8>>2]=0;e:do if(c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2416>>2]|0){if((c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2392>>2]|0)==1){c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2420>>2]=1;break}c[n>>2]=(ec(c[r>>2]|0,c[17836+((c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2392>>2]|0)-2<<2)>>2]|0,8)|0)+1;c[G>>2]=0;while(1){if((c[G>>2]|0)>=(c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2392>>2]|0))break e;c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2420+(c[G>>2]<<2)>>2]=c[n>>2]>>c[G>>2]&1;c[G>>2]=(c[G>>2]|0)+1}}while(0);c[I>>2]=(c[I>>2]|0)+1}if(!(c[A>>2]|0)){c[G>>2]=0;while(1){if((c[G>>2]|0)>=(c[(c[E>>2]|0)+2392>>2]|0))break d;c[I>>2]=0;while(1){l=c[G>>2]|0;if((c[I>>2]|0)>=(c[(c[z>>2]|0)+4>>2]|0))break;if(c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2420+(l<<2)>>2]|0){do if((c[I>>2]|0)==0?(c[(c[z>>2]|0)+4>>2]|0)==2:0){xg(c[r>>2]|0,u);if(c[(c[E>>2]|0)+4260+2420+(c[G>>2]<<2)>>2]|0)break;yg(c[r>>2]|0,F)}while(0);do if((c[G>>2]|0)>0){if(!(c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2420+((c[G>>2]|0)-1<<2)>>2]|0)){q=64;break}c[o>>2]=2}else q=64;while(0);if((q|0)==64){q=0;c[o>>2]=0}Md((c[E>>2]|0)+((c[I>>2]|0)*4260|0)|0,c[r>>2]|0,c[G>>2]|0,1,c[o>>2]|0);Nd(c[r>>2]|0,p,a[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2736+29>>0]|0,a[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2736+30>>0]|0,c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2328>>2]|0)}c[I>>2]=(c[I>>2]|0)+1}c[G>>2]=l+1}}}while(0);f:do if((c[(c[z>>2]|0)+4>>2]|0)==2){do if(c[A>>2]|0){if((c[A>>2]|0)==2?(c[(c[E>>2]|0)+2420+(c[(c[E>>2]|0)+2388>>2]<<2)>>2]|0)==1:0)break;c[I>>2]=0;while(1){if((c[I>>2]|0)>=2)break f;c[u+(c[I>>2]<<2)>>2]=b[(c[K>>2]|0)+8520+(c[I>>2]<<1)>>1];c[I>>2]=(c[I>>2]|0)+1}}while(0);xg(c[r>>2]|0,u);if(!((c[A>>2]|0)==0?!(c[(c[E>>2]|0)+4260+2404+(c[(c[E>>2]|0)+2388>>2]<<2)>>2]|0):0))q=74;do if((q|0)==74){if((c[A>>2]|0)==2?(c[(c[E>>2]|0)+4260+2420+(c[(c[E>>2]|0)+2388>>2]<<2)>>2]|0)==0:0)break;c[F>>2]=0;break f}while(0);yg(c[r>>2]|0,F)}while(0);if(((c[F>>2]|0)==0?(c[(c[z>>2]|0)+4>>2]|0)==2:0)?(c[(c[K>>2]|0)+8540>>2]|0)==1:0){oj((c[K>>2]|0)+4260+1348|0,0,960)|0;l=(c[K>>2]|0)+4260+1284|0;e=l+64|0;do{c[l>>2]=0;l=l+4|0}while((l|0)<(e|0));c[(c[K>>2]|0)+4260+2308>>2]=100;a[(c[K>>2]|0)+4260+2312>>0]=10;c[(c[K>>2]|0)+4260+4164>>2]=0;c[(c[K>>2]|0)+4260+2376>>2]=1}R=_(c[(c[z>>2]|0)+12>>2]|0,c[(c[z>>2]|0)+4>>2]|0)|0;c[x>>2]=(R|0)<(_(c[(c[z>>2]|0)+8>>2]|0,c[c[z>>2]>>2]|0)|0)&1;if(c[x>>2]|0)l=1;else l=_(c[(c[z>>2]|0)+4>>2]|0,(c[(c[E>>2]|0)+2328>>2]|0)+2|0)|0;c[D>>2]=ia()|0;e=i;i=i+((1*(l<<1)|0)+15&-16)|0;if(c[x>>2]|0){c[N>>2]=c[B>>2];c[N+4>>2]=(c[B>>2]|0)+(c[(c[E>>2]|0)+2328>>2]<<1)+4}else{c[N>>2]=e;c[N+4>>2]=e+(c[(c[E>>2]|0)+2328>>2]<<1)+4}if(!(c[A>>2]|0))c[w>>2]=((c[F>>2]|0)!=0^1)&1;else{if(c[(c[K>>2]|0)+8540>>2]|0)if((c[A>>2]|0)==2?(c[(c[z>>2]|0)+4>>2]|0)==2:0)l=(c[(c[E>>2]|0)+4260+2420+(c[(c[E>>2]|0)+4260+2388>>2]<<2)>>2]|0)==1;else l=0;else l=1;c[w>>2]=l&1}c[I>>2]=0;while(1){if((c[I>>2]|0)>=(c[(c[z>>2]|0)+4>>2]|0))break;if((c[I>>2]|0)==0|(c[w>>2]|0)!=0){c[t>>2]=(c[(c[E>>2]|0)+2388>>2]|0)-(c[I>>2]|0);g:do if((c[t>>2]|0)<=0)c[v>>2]=0;else{if((c[A>>2]|0)==2){c[v>>2]=c[(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2420+((c[t>>2]|0)-1<<2)>>2]|0?2:0;break}do if((c[I>>2]|0)>0){if(!(c[(c[K>>2]|0)+8540>>2]|0))break;c[v>>2]=1;break g}while(0);c[v>>2]=2}while(0);R=Kd((c[E>>2]|0)+((c[I>>2]|0)*4260|0)|0,c[r>>2]|0,(c[N+(c[I>>2]<<2)>>2]|0)+4|0,J,c[A>>2]|0,c[v>>2]|0,c[s>>2]|0)|0;c[M>>2]=(c[M>>2]|0)+R}else oj((c[N+(c[I>>2]<<2)>>2]|0)+4|0,0,c[J>>2]<<1|0)|0;R=(c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2388|0;c[R>>2]=(c[R>>2]|0)+1;c[I>>2]=(c[I>>2]|0)+1}if((c[c[z>>2]>>2]|0)==2?(c[(c[z>>2]|0)+4>>2]|0)==2:0)uf((c[K>>2]|0)+8520|0,c[N>>2]|0,c[N+4>>2]|0,u,c[(c[E>>2]|0)+2316>>2]|0,c[J>>2]|0);else{R=c[N>>2]|0;Q=(c[K>>2]|0)+8520+4|0;b[R>>1]=b[Q>>1]|0;b[R+2>>1]=b[Q+2>>1]|0;R=(c[K>>2]|0)+8520+4|0;Q=(c[N>>2]|0)+(c[J>>2]<<1)|0;b[R>>1]=b[Q>>1]|0;b[R+2>>1]=b[Q+2>>1]|0}R=_(c[J>>2]|0,c[(c[z>>2]|0)+8>>2]|0)|0;c[c[C>>2]>>2]=(R|0)/(((c[(c[E>>2]|0)+2316>>2]&65535)<<16>>16)*1e3|0)|0;if((c[c[z>>2]>>2]|0)==2)l=c[c[C>>2]>>2]|0;else l=1;e=i;i=i+((1*(l<<1)|0)+15&-16)|0;if((c[c[z>>2]>>2]|0)==2)c[L>>2]=e;else c[L>>2]=c[B>>2];if(c[x>>2]|0)l=_(c[(c[z>>2]|0)+4>>2]|0,(c[(c[E>>2]|0)+2328>>2]|0)+2|0)|0;else l=1;e=i;i=i+((1*(l<<1)|0)+15&-16)|0;if(c[x>>2]|0){R=(_(c[(c[z>>2]|0)+4>>2]|0,(c[(c[E>>2]|0)+2328>>2]|0)+2|0)|0)<<1;pj(e|0,c[B>>2]|0,R+0|0)|0;c[N>>2]=e;c[N+4>>2]=e+(c[(c[E>>2]|0)+2328>>2]<<1)+4}c[I>>2]=0;while(1){l=c[z>>2]|0;if((c[c[z>>2]>>2]|0)<(c[(c[z>>2]|0)+4>>2]|0))l=c[l>>2]|0;else l=c[l+4>>2]|0;if((c[I>>2]|0)>=(l|0))break;R=jg((c[E>>2]|0)+((c[I>>2]|0)*4260|0)+2432|0,c[L>>2]|0,(c[N+(c[I>>2]<<2)>>2]|0)+2|0,c[J>>2]|0)|0;c[M>>2]=(c[M>>2]|0)+R;h:do if((c[c[z>>2]>>2]|0)==2){c[G>>2]=0;while(1){if((c[G>>2]|0)>=(c[c[C>>2]>>2]|0))break h;b[(c[B>>2]|0)+((c[I>>2]|0)+(c[G>>2]<<1)<<1)>>1]=b[(c[L>>2]|0)+(c[G>>2]<<1)>>1]|0;c[G>>2]=(c[G>>2]|0)+1}}while(0);c[I>>2]=(c[I>>2]|0)+1}i:do if((c[c[z>>2]>>2]|0)==2){if((c[(c[z>>2]|0)+4>>2]|0)!=1)break;if(c[O>>2]|0){R=jg((c[E>>2]|0)+4260+2432|0,c[L>>2]|0,(c[N>>2]|0)+2|0,c[J>>2]|0)|0;c[M>>2]=(c[M>>2]|0)+R;c[G>>2]=0;while(1){if((c[G>>2]|0)>=(c[c[C>>2]>>2]|0))break i;b[(c[B>>2]|0)+(1+(c[G>>2]<<1)<<1)>>1]=b[(c[L>>2]|0)+(c[G>>2]<<1)>>1]|0;c[G>>2]=(c[G>>2]|0)+1}}else{c[G>>2]=0;while(1){if((c[G>>2]|0)>=(c[c[C>>2]>>2]|0))break i;b[(c[B>>2]|0)+(1+(c[G>>2]<<1)<<1)>>1]=b[(c[B>>2]|0)+(0+(c[G>>2]<<1)<<1)>>1]|0;c[G>>2]=(c[G>>2]|0)+1}}}while(0);if((c[(c[E>>2]|0)+4164>>2]|0)==2){c[H>>2]=c[4402];c[H+4>>2]=c[4403];c[H+8>>2]=c[4404];R=_(c[(c[E>>2]|0)+2308>>2]|0,c[H+((c[(c[E>>2]|0)+2316>>2]|0)-8>>2<<2)>>2]|0)|0;c[(c[z>>2]|0)+20>>2]=R}else c[(c[z>>2]|0)+20>>2]=0;j:do if((c[A>>2]|0)==1){c[G>>2]=0;while(1){if((c[G>>2]|0)>=(c[(c[K>>2]|0)+8536>>2]|0))break j;a[(c[K>>2]|0)+((c[G>>2]|0)*4260|0)+2312>>0]=10;c[G>>2]=(c[G>>2]|0)+1}}else c[(c[K>>2]|0)+8540>>2]=c[F>>2];while(0);c[y>>2]=c[M>>2];na(c[D>>2]|0);R=c[y>>2]|0;i=P;return R|0}c[M>>2]=-200;c[y>>2]=c[M>>2];R=c[y>>2]|0;i=P;return R|0}function Sd(a){a=a|0;var b=0,d=0,e=0;d=i;i=i+16|0;e=d+4|0;b=d;c[e>>2]=a;c[b>>2]=0;c[c[e>>2]>>2]=24568;i=d;return c[b>>2]|0}function Td(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0;l=i;i=i+32|0;e=l+20|0;f=l+16|0;g=l+12|0;j=l+8|0;h=l+4|0;k=l;c[e>>2]=a;c[f>>2]=b;c[g>>2]=d;c[k>>2]=0;c[j>>2]=c[e>>2];oj(c[j>>2]|0,0,24568)|0;c[h>>2]=0;while(1){if((c[h>>2]|0)>=2)break;d=xf((c[j>>2]|0)+((c[h>>2]|0)*12240|0)|0,c[f>>2]|0)|0;c[k>>2]=(c[k>>2]|0)+d;c[h>>2]=(c[h>>2]|0)+1}c[(c[j>>2]|0)+24544>>2]=1;c[(c[j>>2]|0)+24548>>2]=1;j=Ud(c[e>>2]|0,c[g>>2]|0)|0;c[k>>2]=(c[k>>2]|0)+j;i=l;return c[k>>2]|0}function Ud(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0;g=i;i=i+32|0;j=g+16|0;d=g+12|0;e=g+8|0;f=g+4|0;h=g;c[j>>2]=a;c[d>>2]=b;c[e>>2]=0;c[h>>2]=c[j>>2];c[f>>2]=c[h>>2];c[c[d>>2]>>2]=c[(c[h>>2]|0)+24544>>2];c[(c[d>>2]|0)+4>>2]=c[(c[h>>2]|0)+24548>>2];c[(c[d>>2]|0)+8>>2]=c[(c[f>>2]|0)+4580>>2];c[(c[d>>2]|0)+12>>2]=c[(c[f>>2]|0)+4588>>2];c[(c[d>>2]|0)+16>>2]=c[(c[f>>2]|0)+4592>>2];c[(c[d>>2]|0)+20>>2]=c[(c[f>>2]|0)+4596>>2];c[(c[d>>2]|0)+24>>2]=c[(c[f>>2]|0)+4636>>2];c[(c[d>>2]|0)+28>>2]=c[(c[f>>2]|0)+4632>>2];c[(c[d>>2]|0)+32>>2]=c[(c[f>>2]|0)+4640>>2];c[(c[d>>2]|0)+36>>2]=c[(c[f>>2]|0)+4648>>2];c[(c[d>>2]|0)+40>>2]=c[(c[f>>2]|0)+6120>>2];c[(c[d>>2]|0)+44>>2]=c[(c[f>>2]|0)+6108>>2];c[(c[d>>2]|0)+48>>2]=c[(c[f>>2]|0)+4708>>2];c[(c[d>>2]|0)+68>>2]=((c[(c[f>>2]|0)+4600>>2]&65535)<<16>>16)*1e3;c[(c[d>>2]|0)+72>>2]=c[(c[f>>2]|0)+4560>>2];if((c[(c[f>>2]|0)+4600>>2]|0)!=16){h=0;h=h&1;j=c[d>>2]|0;j=j+76|0;c[j>>2]=h;j=c[e>>2]|0;i=g;return j|0}h=(c[(c[f>>2]|0)+16+12>>2]|0)==0;h=h&1;j=c[d>>2]|0;j=j+76|0;c[j>>2]=h;j=c[e>>2]|0;i=g;return j|0}function Vd(d,e,f,g,h,j,k){d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;k=k|0;var l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0;Y=i;i=i+176|0;P=Y+156|0;Z=Y+152|0;Q=Y+148|0;o=Y+144|0;p=Y+140|0;q=Y+136|0;r=Y+132|0;R=Y+128|0;T=Y+124|0;B=Y+120|0;F=Y+116|0;A=Y+112|0;X=Y+108|0;W=Y+104|0;V=Y+100|0;I=Y+96|0;J=Y+92|0;G=Y+88|0;H=Y+84|0;m=Y+80|0;K=Y+76|0;u=Y+72|0;t=Y+64|0;w=Y+56|0;s=Y+52|0;L=Y+48|0;U=Y+44|0;n=Y+40|0;z=Y+36|0;M=Y+32|0;l=Y+28|0;S=Y+24|0;D=Y+20|0;C=Y+160|0;x=Y+16|0;v=Y+12|0;E=Y+8|0;N=Y+4|0;y=Y;c[Z>>2]=d;c[Q>>2]=e;c[o>>2]=f;c[p>>2]=g;c[q>>2]=h;c[r>>2]=j;c[R>>2]=k;c[X>>2]=0;c[W>>2]=0;c[V>>2]=0;c[H>>2]=0;c[U>>2]=c[Z>>2];if(c[(c[Q>>2]|0)+64>>2]|0){c[(c[U>>2]|0)+4696>>2]=1;c[(c[U>>2]|0)+12240+4696>>2]=1}c[(c[U>>2]|0)+12240+5780>>2]=0;c[(c[U>>2]|0)+5780>>2]=0;Z=vf(c[Q>>2]|0)|0;c[V>>2]=Z;if(Z|0){c[P>>2]=c[V>>2];Z=c[P>>2]|0;i=Y;return Z|0}c[(c[Q>>2]|0)+84>>2]=0;if((c[(c[Q>>2]|0)+4>>2]|0)>(c[(c[U>>2]|0)+24548>>2]|0)?(Z=xf((c[U>>2]|0)+12240|0,c[(c[U>>2]|0)+5124>>2]|0)|0,c[V>>2]=(c[V>>2]|0)+Z,c[(c[U>>2]|0)+24480>>2]=0,c[(c[U>>2]|0)+24480+8>>2]=0,c[(c[U>>2]|0)+24480+12>>2]=0,c[(c[U>>2]|0)+24480+12+4>>2]=1,c[(c[U>>2]|0)+24480+12+8>>2]=0,c[(c[U>>2]|0)+24480+12+12>>2]=1,b[(c[U>>2]|0)+24480+30>>1]=0,b[(c[U>>2]|0)+24480+28>>1]=16384,(c[(c[U>>2]|0)+24544>>2]|0)==2):0){pj((c[U>>2]|0)+12240+5808|0,(c[U>>2]|0)+5808|0,300)|0;Z=(c[U>>2]|0)+12240|0;f=c[U>>2]|0;c[Z>>2]=c[f>>2];c[Z+4>>2]=c[f+4>>2]}if((c[(c[Q>>2]|0)+24>>2]|0)!=(c[(c[U>>2]|0)+4636>>2]|0))e=1;else e=(c[(c[U>>2]|0)+24548>>2]|0)!=(c[(c[Q>>2]|0)+4>>2]|0);c[n>>2]=e&1;c[(c[U>>2]|0)+24544>>2]=c[c[Q>>2]>>2];c[(c[U>>2]|0)+24548>>2]=c[(c[Q>>2]|0)+4>>2];c[G>>2]=((c[p>>2]|0)*100|0)/(c[(c[Q>>2]|0)+8>>2]|0)|0;c[M>>2]=(c[G>>2]|0)>1?c[G>>2]>>1:1;c[z>>2]=0;e=c[G>>2]|0;a:do if(!(c[R>>2]|0)){Z=_(e,c[(c[Q>>2]|0)+8>>2]|0)|0;if((c[p>>2]|0)<0?1:(Z|0)!=((c[p>>2]|0)*100|0)){c[P>>2]=-101;Z=c[P>>2]|0;i=Y;return Z|0}if(((c[p>>2]|0)*1e3|0)>(_(c[(c[Q>>2]|0)+24>>2]|0,c[(c[Q>>2]|0)+8>>2]|0)|0)){c[P>>2]=-101;Z=c[P>>2]|0;i=Y;return Z|0}}else{if((e|0)!=1){c[P>>2]=-101;Z=c[P>>2]|0;i=Y;return Z|0}c[T>>2]=0;while(1){if((c[T>>2]|0)>=(c[(c[Q>>2]|0)+4>>2]|0))break;c[V>>2]=xf((c[U>>2]|0)+((c[T>>2]|0)*12240|0)|0,c[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+5124>>2]|0)|0;c[T>>2]=(c[T>>2]|0)+1}c[X>>2]=c[(c[Q>>2]|0)+24>>2];c[(c[Q>>2]|0)+24>>2]=10;c[W>>2]=c[(c[Q>>2]|0)+36>>2];c[(c[Q>>2]|0)+36>>2]=0;c[T>>2]=0;while(1){if((c[T>>2]|0)>=(c[(c[Q>>2]|0)+4>>2]|0))break a;c[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+4700>>2]=0;c[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+4712>>2]=1;c[T>>2]=(c[T>>2]|0)+1}}while(0);c[u>>2]=c[(c[Q>>2]|0)+28>>2]>>(c[(c[Q>>2]|0)+4>>2]|0)-1;c[T>>2]=0;while(1){if((c[T>>2]|0)>=(c[(c[Q>>2]|0)+4>>2]|0))break;if((c[T>>2]|0)==1)e=c[(c[U>>2]|0)+4600>>2]|0;else e=0;c[l>>2]=e;Z=yf((c[U>>2]|0)+((c[T>>2]|0)*12240|0)|0,c[Q>>2]|0,c[u>>2]|0,c[(c[U>>2]|0)+24560>>2]|0,c[T>>2]|0,c[l>>2]|0)|0;c[V>>2]=Z;if(Z|0){O=28;break}b:do if(c[n>>2]|0?1:(c[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+4696>>2]|0)!=0){c[B>>2]=0;while(1){if((c[B>>2]|0)>=(c[(c[U>>2]|0)+5776>>2]|0))break b;c[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+4756+(c[B>>2]<<2)>>2]=0;c[B>>2]=(c[B>>2]|0)+1}}while(0);c[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+6112>>2]=c[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+6108>>2];c[T>>2]=(c[T>>2]|0)+1}if((O|0)==28){c[P>>2]=c[V>>2];Z=c[P>>2]|0;i=Y;return Z|0}c[J>>2]=_((c[G>>2]|0)*10|0,c[(c[U>>2]|0)+4600>>2]|0)|0;Z=_(c[J>>2]|0,c[(c[U>>2]|0)+4580>>2]|0)|0;c[m>>2]=(Z|0)/((c[(c[U>>2]|0)+4600>>2]|0)*1e3|0)|0;Z=c[m>>2]|0;c[S>>2]=ia()|0;d=i;i=i+((1*(Z<<1)|0)+15&-16)|0;while(1){c[I>>2]=(c[(c[U>>2]|0)+4608>>2]|0)-(c[(c[U>>2]|0)+5772>>2]|0);c[I>>2]=(c[I>>2]|0)<(c[J>>2]|0)?c[I>>2]|0:c[J>>2]|0;Z=_(c[I>>2]|0,c[(c[U>>2]|0)+4580>>2]|0)|0;c[H>>2]=(Z|0)/((c[(c[U>>2]|0)+4600>>2]|0)*1e3|0)|0;if((c[c[Q>>2]>>2]|0)==2?(c[(c[Q>>2]|0)+4>>2]|0)==2:0){c[D>>2]=c[(c[U>>2]|0)+5780>>2];c[T>>2]=0;while(1){if((c[T>>2]|0)>=(c[H>>2]|0))break;b[d+(c[T>>2]<<1)>>1]=b[(c[o>>2]|0)+(c[T>>2]<<1<<1)>>1]|0;c[T>>2]=(c[T>>2]|0)+1}if((c[D>>2]|0)==0?(c[(c[U>>2]|0)+24552>>2]|0)==1:0)pj((c[U>>2]|0)+12240+5808|0,(c[U>>2]|0)+5808|0,300)|0;Z=jg((c[U>>2]|0)+5808|0,(c[U>>2]|0)+5128+((c[(c[U>>2]|0)+5772>>2]|0)+2<<1)|0,d,c[H>>2]|0)|0;c[V>>2]=(c[V>>2]|0)+Z;Z=(c[U>>2]|0)+5772|0;c[Z>>2]=(c[Z>>2]|0)+(c[I>>2]|0);c[I>>2]=(c[(c[U>>2]|0)+12240+4608>>2]|0)-(c[(c[U>>2]|0)+12240+5772>>2]|0);if((c[I>>2]|0)<(_((c[G>>2]|0)*10|0,c[(c[U>>2]|0)+12240+4600>>2]|0)|0))e=c[I>>2]|0;else e=_((c[G>>2]|0)*10|0,c[(c[U>>2]|0)+12240+4600>>2]|0)|0;c[I>>2]=e;c[T>>2]=0;while(1){if((c[T>>2]|0)>=(c[H>>2]|0))break;b[d+(c[T>>2]<<1)>>1]=b[(c[o>>2]|0)+((c[T>>2]<<1)+1<<1)>>1]|0;c[T>>2]=(c[T>>2]|0)+1}Z=jg((c[U>>2]|0)+12240+5808|0,(c[U>>2]|0)+12240+5128+((c[(c[U>>2]|0)+12240+5772>>2]|0)+2<<1)|0,d,c[H>>2]|0)|0;c[V>>2]=(c[V>>2]|0)+Z;Z=(c[U>>2]|0)+12240+5772|0;c[Z>>2]=(c[Z>>2]|0)+(c[I>>2]|0)}else O=49;do if((O|0)==49){O=0;if((c[c[Q>>2]>>2]|0)==2?(c[(c[Q>>2]|0)+4>>2]|0)==1:0){c[T>>2]=0;while(1){if((c[T>>2]|0)>=(c[H>>2]|0))break;c[L>>2]=(b[(c[o>>2]|0)+(c[T>>2]<<1<<1)>>1]|0)+(b[(c[o>>2]|0)+((c[T>>2]<<1)+1<<1)>>1]|0);b[d+(c[T>>2]<<1)>>1]=(c[L>>2]>>1)+(c[L>>2]&1);c[T>>2]=(c[T>>2]|0)+1}Z=jg((c[U>>2]|0)+5808|0,(c[U>>2]|0)+5128+((c[(c[U>>2]|0)+5772>>2]|0)+2<<1)|0,d,c[H>>2]|0)|0;c[V>>2]=(c[V>>2]|0)+Z;c:do if((c[(c[U>>2]|0)+24552>>2]|0)==2?(c[(c[U>>2]|0)+5780>>2]|0)==0:0){Z=jg((c[U>>2]|0)+12240+5808|0,(c[U>>2]|0)+12240+5128+((c[(c[U>>2]|0)+12240+5772>>2]|0)+2<<1)|0,d,c[H>>2]|0)|0;c[V>>2]=(c[V>>2]|0)+Z;c[T>>2]=0;while(1){if((c[T>>2]|0)>=(c[(c[U>>2]|0)+4608>>2]|0))break c;b[(c[U>>2]|0)+5128+((c[(c[U>>2]|0)+5772>>2]|0)+(c[T>>2]|0)+2<<1)>>1]=(b[(c[U>>2]|0)+5128+((c[(c[U>>2]|0)+5772>>2]|0)+(c[T>>2]|0)+2<<1)>>1]|0)+(b[(c[U>>2]|0)+12240+5128+((c[(c[U>>2]|0)+12240+5772>>2]|0)+(c[T>>2]|0)+2<<1)>>1]|0)>>1;c[T>>2]=(c[T>>2]|0)+1}}while(0);Z=(c[U>>2]|0)+5772|0;c[Z>>2]=(c[Z>>2]|0)+(c[I>>2]|0);break}pj(d|0,c[o>>2]|0,c[H>>2]<<1|0)|0;Z=jg((c[U>>2]|0)+5808|0,(c[U>>2]|0)+5128+((c[(c[U>>2]|0)+5772>>2]|0)+2<<1)|0,d,c[H>>2]|0)|0;c[V>>2]=(c[V>>2]|0)+Z;Z=(c[U>>2]|0)+5772|0;c[Z>>2]=(c[Z>>2]|0)+(c[I>>2]|0)}while(0);Z=_(c[H>>2]|0,c[c[Q>>2]>>2]|0)|0;c[o>>2]=(c[o>>2]|0)+(Z<<1);c[p>>2]=(c[p>>2]|0)-(c[H>>2]|0);c[(c[U>>2]|0)+24560>>2]=0;if((c[(c[U>>2]|0)+5772>>2]|0)<(c[(c[U>>2]|0)+4608>>2]|0))break;if(!(c[R>>2]|0?1:(c[(c[U>>2]|0)+5780>>2]|0)!=0)){a[C>>0]=0;a[C+1>>0]=0;a[C>>0]=256-(256>>(_((c[(c[U>>2]|0)+5776>>2]|0)+1|0,c[(c[Q>>2]|0)+4>>2]|0)|0));qc(c[q>>2]|0,0,C,8);c[T>>2]=0;while(1){if((c[T>>2]|0)>=(c[(c[Q>>2]|0)+4>>2]|0))break;c[s>>2]=0;c[B>>2]=0;while(1){if((c[B>>2]|0)>=(c[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+5776>>2]|0))break;c[s>>2]=c[s>>2]|c[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+4756+(c[B>>2]<<2)>>2]<<c[B>>2];c[B>>2]=(c[B>>2]|0)+1}a[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+4755>>0]=(c[s>>2]|0)>0?1:0;if(c[s>>2]|0?(c[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+5776>>2]|0)>1:0)qc(c[q>>2]|0,(c[s>>2]|0)-1|0,c[17836+((c[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+5776>>2]|0)-2<<2)>>2]|0,8);c[T>>2]=(c[T>>2]|0)+1}c[B>>2]=0;while(1){Z=(c[B>>2]|0)<(c[(c[U>>2]|0)+5776>>2]|0);c[T>>2]=0;if(!Z)break;while(1){e=c[B>>2]|0;if((c[T>>2]|0)>=(c[(c[Q>>2]|0)+4>>2]|0))break;if(c[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+4756+(e<<2)>>2]|0){if(((c[T>>2]|0)==0?(c[(c[Q>>2]|0)+4>>2]|0)==2:0)?(zg(c[q>>2]|0,(c[U>>2]|0)+24480+34+((c[B>>2]|0)*6|0)|0),(c[(c[U>>2]|0)+12240+4756+(c[B>>2]<<2)>>2]|0)==0):0)Ag(c[q>>2]|0,a[(c[U>>2]|0)+24480+52+(c[B>>2]|0)>>0]|0);if((c[B>>2]|0)>0?c[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+4756+((c[B>>2]|0)-1<<2)>>2]|0:0)c[x>>2]=2;else c[x>>2]=0;Xd((c[U>>2]|0)+((c[T>>2]|0)*12240|0)|0,c[q>>2]|0,c[B>>2]|0,1,c[x>>2]|0);Yd(c[q>>2]|0,a[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+6132+((c[B>>2]|0)*36|0)+29>>0]|0,a[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+6132+((c[B>>2]|0)*36|0)+30>>0]|0,(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+6240+((c[B>>2]|0)*320|0)|0,c[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+4608>>2]|0)}c[T>>2]=(c[T>>2]|0)+1}c[B>>2]=e+1}while(1){if((c[T>>2]|0)>=(c[(c[Q>>2]|0)+4>>2]|0))break;Z=(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+4756|0;c[Z>>2]=0;c[Z+4>>2]=0;c[Z+8>>2]=0;c[T>>2]=(c[T>>2]|0)+1}Z=Wd(c[q>>2]|0)|0;c[(c[U>>2]|0)+24536>>2]=Z}ff(c[U>>2]|0);c[F>>2]=(_(c[(c[Q>>2]|0)+28>>2]|0,c[(c[Q>>2]|0)+24>>2]|0)|0)/1e3|0;if(!(c[R>>2]|0))c[F>>2]=(c[F>>2]|0)-(c[(c[U>>2]|0)+24536>>2]|0);c[F>>2]=(c[F>>2]|0)/(c[(c[U>>2]|0)+5776>>2]|0)|0;e=(c[F>>2]&65535)<<16>>16;if((c[(c[Q>>2]|0)+24>>2]|0)==10)c[u>>2]=e*100;else c[u>>2]=e*50;c[u>>2]=(c[u>>2]|0)-(((c[(c[U>>2]|0)+24540>>2]|0)*1e3|0)/500|0);if((c[R>>2]|0)==0?(c[(c[U>>2]|0)+5780>>2]|0)>0:0){Z=Wd(c[q>>2]|0)|0;c[v>>2]=Z-(c[(c[U>>2]|0)+24536>>2]|0)-(_(c[F>>2]|0,c[(c[U>>2]|0)+5780>>2]|0)|0);c[u>>2]=(c[u>>2]|0)-(((c[v>>2]|0)*1e3|0)/500|0)}e=c[u>>2]|0;do if((c[(c[Q>>2]|0)+28>>2]|0)>5e3)if((e|0)>(c[(c[Q>>2]|0)+28>>2]|0)){e=c[(c[Q>>2]|0)+28>>2]|0;break}else{e=(c[u>>2]|0)<5e3?5e3:c[u>>2]|0;break}else if((e|0)<=5e3)if((c[u>>2]|0)<(c[(c[Q>>2]|0)+28>>2]|0)){e=c[(c[Q>>2]|0)+28>>2]|0;break}else{e=c[u>>2]|0;break}else e=5e3;while(0);c[u>>2]=e;e=c[U>>2]|0;if((c[(c[Q>>2]|0)+4>>2]|0)==2){qf(e+24480|0,(c[U>>2]|0)+5128+4|0,(c[U>>2]|0)+12240+5128+4|0,(c[U>>2]|0)+24480+34+((c[(c[U>>2]|0)+5780>>2]|0)*6|0)|0,(c[U>>2]|0)+24480+52+(c[(c[U>>2]|0)+5780>>2]|0)|0,t,c[u>>2]|0,c[(c[U>>2]|0)+4556>>2]|0,c[(c[Q>>2]|0)+56>>2]|0,c[(c[U>>2]|0)+4600>>2]|0,c[(c[U>>2]|0)+4608>>2]|0);e=c[U>>2]|0;if(!(a[(c[U>>2]|0)+24480+52+(c[(c[U>>2]|0)+5780>>2]|0)>>0]|0)){if((c[e+24564>>2]|0)==1){Z=(c[U>>2]|0)+12240+7200|0;c[Z>>2]=0;c[Z+4>>2]=0;c[Z+8>>2]=0;c[Z+12>>2]=0;oj((c[U>>2]|0)+12240+7216|0,0,2140)|0;oj((c[U>>2]|0)+12240+144|0,0,4380)|0;Z=(c[U>>2]|0)+12240+4524|0;c[Z>>2]=0;c[Z+4>>2]=0;c[Z+8>>2]=0;c[Z+12>>2]=0;c[Z+16>>2]=0;c[Z+20>>2]=0;c[Z+24>>2]=0;c[Z+28>>2]=0;Z=(c[U>>2]|0)+12240+16|0;c[Z>>2]=0;c[Z+4>>2]=0;c[(c[U>>2]|0)+12240+4568>>2]=100;c[(c[U>>2]|0)+12240+144+4356>>2]=100;a[(c[U>>2]|0)+12240+7200>>0]=10;a[(c[U>>2]|0)+12240+4565>>0]=0;c[(c[U>>2]|0)+12240+144+4372>>2]=65536;c[(c[U>>2]|0)+12240+4696>>2]=1}Mg((c[U>>2]|0)+12240|0)}else a[(c[U>>2]|0)+12240+4752+(c[e+5780>>2]|0)>>0]=0;if((c[R>>2]|0)==0?(zg(c[q>>2]|0,(c[U>>2]|0)+24480+34+((c[(c[U>>2]|0)+5780>>2]|0)*6|0)|0),(a[(c[U>>2]|0)+12240+4752+(c[(c[U>>2]|0)+5780>>2]|0)>>0]|0)==0):0)Ag(c[q>>2]|0,a[(c[U>>2]|0)+24480+52+(c[(c[U>>2]|0)+5780>>2]|0)>>0]|0)}else{c[e+5128>>2]=c[(c[U>>2]|0)+24480+4>>2];Z=(c[U>>2]|0)+24480+4|0;n=(c[U>>2]|0)+5128+(c[(c[U>>2]|0)+4608>>2]<<1)|0;b[Z>>1]=b[n>>1]|0;b[Z+2>>1]=b[n+2>>1]|0}Mg(c[U>>2]|0);c[T>>2]=0;while(1){if((c[T>>2]|0)>=(c[(c[Q>>2]|0)+4>>2]|0))break;c[E>>2]=c[(c[Q>>2]|0)+52>>2];do if(!((c[M>>2]|0)==2&(c[z>>2]|0)==0)){if((c[M>>2]|0)==3){if(!(c[z>>2]|0)){c[E>>2]=(c[E>>2]<<1|0)/5|0;break}if((c[z>>2]|0)!=1)break;c[E>>2]=((c[E>>2]|0)*3|0)/4|0}}else c[E>>2]=((c[E>>2]|0)*3|0)/5|0;while(0);if(c[(c[Q>>2]|0)+48>>2]|0)e=(c[z>>2]|0)==((c[M>>2]|0)-1|0);else e=0;c[N>>2]=e&1;do if((c[(c[Q>>2]|0)+4>>2]|0)==1)c[w>>2]=c[u>>2];else{c[w>>2]=c[t+(c[T>>2]<<2)>>2];if(c[T>>2]|0)break;if((c[t+4>>2]|0)<=0)break;c[N>>2]=0;c[E>>2]=(c[E>>2]|0)-((c[(c[Q>>2]|0)+52>>2]|0)/(c[M>>2]<<1|0)|0)}while(0);if((c[w>>2]|0)>0){wf((c[U>>2]|0)+((c[T>>2]|0)*12240|0)|0,c[w>>2]|0)|0;d:do if(((c[(c[U>>2]|0)+5780>>2]|0)-(c[T>>2]|0)|0)<=0)c[y>>2]=0;else{do if((c[T>>2]|0)>0){if(!(c[(c[U>>2]|0)+24564>>2]|0))break;c[y>>2]=1;break d}while(0);c[y>>2]=2}while(0);c[V>>2]=Ng((c[U>>2]|0)+((c[T>>2]|0)*12240|0)|0,c[r>>2]|0,c[q>>2]|0,c[y>>2]|0,c[E>>2]|0,c[N>>2]|0)|0}c[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+4700>>2]=0;c[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+5772>>2]=0;Z=(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+5780|0;c[Z>>2]=(c[Z>>2]|0)+1;c[T>>2]=(c[T>>2]|0)+1}c[(c[U>>2]|0)+24564>>2]=a[(c[U>>2]|0)+24480+52+((c[(c[U>>2]|0)+5780>>2]|0)-1)>>0];do if((c[c[r>>2]>>2]|0)>0?(c[(c[U>>2]|0)+5780>>2]|0)==(c[(c[U>>2]|0)+5776>>2]|0):0){c[A>>2]=0;c[T>>2]=0;while(1){if((c[T>>2]|0)>=(c[(c[Q>>2]|0)+4>>2]|0))break;c[B>>2]=0;while(1){Z=(c[B>>2]|0)<(c[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+5776>>2]|0);c[A>>2]=c[A>>2]<<1;if(!Z)break;c[A>>2]=c[A>>2]|a[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+4752+(c[B>>2]|0)>>0];c[B>>2]=(c[B>>2]|0)+1}c[A>>2]=c[A>>2]|a[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+4755>>0];c[T>>2]=(c[T>>2]|0)+1}if(!(c[R>>2]|0))uc(c[q>>2]|0,c[A>>2]|0,_((c[(c[U>>2]|0)+5776>>2]|0)+1|0,c[(c[Q>>2]|0)+4>>2]|0)|0);do if(c[(c[U>>2]|0)+6112>>2]|0){if((c[(c[Q>>2]|0)+4>>2]|0)!=1?(c[(c[U>>2]|0)+12240+6112>>2]|0)==0:0)break;c[c[r>>2]>>2]=0}while(0);n=(c[U>>2]|0)+24540|0;c[n>>2]=(c[n>>2]|0)+(c[c[r>>2]>>2]<<3);n=(_(c[(c[Q>>2]|0)+28>>2]|0,c[(c[Q>>2]|0)+24>>2]|0)|0)/1e3|0;Z=(c[U>>2]|0)+24540|0;c[Z>>2]=(c[Z>>2]|0)-n;do if((c[(c[U>>2]|0)+24540>>2]|0)>1e4)e=1e4;else{if((c[(c[U>>2]|0)+24540>>2]|0)<0){e=0;break}e=c[(c[U>>2]|0)+24540>>2]|0}while(0);c[(c[U>>2]|0)+24540>>2]=e;c[K>>2]=13+(0+(((c[(c[U>>2]|0)+24556>>2]&65535)<<16>>16)*3188>>16));e=(c[U>>2]|0)+24560|0;if((c[(c[U>>2]|0)+4556>>2]|0)<(c[K>>2]|0)){c[e>>2]=1;c[(c[U>>2]|0)+24556>>2]=0;break}else{c[e>>2]=0;Z=(c[U>>2]|0)+24556|0;c[Z>>2]=(c[Z>>2]|0)+(c[(c[Q>>2]|0)+24>>2]|0);break}}while(0);if(!(c[p>>2]|0))break;c[z>>2]=(c[z>>2]|0)+1}c[(c[U>>2]|0)+24552>>2]=c[(c[Q>>2]|0)+4>>2];c[(c[Q>>2]|0)+72>>2]=c[(c[U>>2]|0)+24560>>2];if((c[(c[U>>2]|0)+4600>>2]|0)==16)e=(c[(c[U>>2]|0)+16+12>>2]|0)==0;else e=0;c[(c[Q>>2]|0)+76>>2]=e&1;c[(c[Q>>2]|0)+68>>2]=((c[(c[U>>2]|0)+4600>>2]&65535)<<16>>16)*1e3;if(c[(c[Q>>2]|0)+56>>2]|0)e=0;else e=b[(c[U>>2]|0)+24480+28>>1]|0;c[(c[Q>>2]|0)+80>>2]=e;e:do if(c[R>>2]|0){c[(c[Q>>2]|0)+24>>2]=c[X>>2];c[(c[Q>>2]|0)+36>>2]=c[W>>2];c[T>>2]=0;while(1){if((c[T>>2]|0)>=(c[(c[Q>>2]|0)+4>>2]|0))break e;c[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+4700>>2]=0;c[(c[U>>2]|0)+((c[T>>2]|0)*12240|0)+4712>>2]=0;c[T>>2]=(c[T>>2]|0)+1}}while(0);c[P>>2]=c[V>>2];na(c[S>>2]|0);Z=c[P>>2]|0;i=Y;return Z|0}function Wd(a){a=a|0;var b=0,d=0;b=i;i=i+16|0;d=b;c[d>>2]=a;a=(c[(c[d>>2]|0)+20>>2]|0)-(32-(aa(c[(c[d>>2]|0)+28>>2]|0)|0))|0;i=b;return a|0}function Xd(d,e,f,g,h){d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0;y=i;i=i+112|0;n=y+48|0;o=y+44|0;j=y+40|0;k=y+36|0;p=y+32|0;t=y+28|0;u=y+24|0;l=y+20|0;s=y+16|0;q=y+12|0;r=y+56|0;m=y+88|0;x=y+8|0;v=y+4|0;w=y;c[n>>2]=d;c[o>>2]=e;c[j>>2]=f;c[k>>2]=g;c[p>>2]=h;if(c[k>>2]|0)c[x>>2]=(c[n>>2]|0)+6132+((c[j>>2]|0)*36|0);else c[x>>2]=(c[n>>2]|0)+4768;c[l>>2]=(a[(c[x>>2]|0)+29>>0]<<1)+(a[(c[x>>2]|0)+30>>0]|0);j=c[o>>2]|0;f=c[l>>2]|0;if((c[k>>2]|0)!=0|(c[l>>2]|0)>=2)qc(j,f-2|0,29020,8);else qc(j,f,29024,8);j=c[o>>2]|0;f=a[c[x>>2]>>0]|0;if((c[p>>2]|0)==2)qc(j,f,26771,8);else{qc(j,f>>3,26747+(a[(c[x>>2]|0)+29>>0]<<3)|0,8);qc(c[o>>2]|0,a[c[x>>2]>>0]&7,29049,8)}c[t>>2]=1;while(1){j=c[o>>2]|0;if((c[t>>2]|0)>=(c[(c[n>>2]|0)+4604>>2]|0))break;qc(j,a[(c[x>>2]|0)+(c[t>>2]|0)>>0]|0,26771,8);c[t>>2]=(c[t>>2]|0)+1}l=_(a[(c[x>>2]|0)+29>>0]>>1,b[c[(c[n>>2]|0)+4724>>2]>>1]|0)|0;qc(j,a[(c[x>>2]|0)+8>>0]|0,(c[(c[(c[n>>2]|0)+4724>>2]|0)+12>>2]|0)+l|0,8);nf(r,m,c[(c[n>>2]|0)+4724>>2]|0,a[(c[x>>2]|0)+8>>0]|0);c[t>>2]=0;while(1){if((c[t>>2]|0)>=(b[(c[(c[n>>2]|0)+4724>>2]|0)+2>>1]|0))break;do if((a[(c[x>>2]|0)+8+((c[t>>2]|0)+1)>>0]|0)<4){j=c[o>>2]|0;f=c[t>>2]|0;if((a[(c[x>>2]|0)+8+((c[t>>2]|0)+1)>>0]|0)<=-4){qc(j,0,(c[(c[(c[n>>2]|0)+4724>>2]|0)+24>>2]|0)+(b[r+(f<<1)>>1]|0)|0,8);qc(c[o>>2]|0,0-(a[(c[x>>2]|0)+8+((c[t>>2]|0)+1)>>0]|0)-4|0,29057,8);break}else{qc(j,(a[(c[x>>2]|0)+8+(f+1)>>0]|0)+4|0,(c[(c[(c[n>>2]|0)+4724>>2]|0)+24>>2]|0)+(b[r+(c[t>>2]<<1)>>1]|0)|0,8);break}}else{qc(c[o>>2]|0,8,(c[(c[(c[n>>2]|0)+4724>>2]|0)+24>>2]|0)+(b[r+(c[t>>2]<<1)>>1]|0)|0,8);qc(c[o>>2]|0,(a[(c[x>>2]|0)+8+((c[t>>2]|0)+1)>>0]|0)-4|0,29057,8)}while(0);c[t>>2]=(c[t>>2]|0)+1}if((c[(c[n>>2]|0)+4604>>2]|0)==4)qc(c[o>>2]|0,a[(c[x>>2]|0)+31>>0]|0,29026,8);if((a[(c[x>>2]|0)+29>>0]|0)!=2){v=c[x>>2]|0;v=v+29|0;v=a[v>>0]|0;v=v<<24>>24;w=c[n>>2]|0;w=w+5800|0;c[w>>2]=v;w=c[o>>2]|0;x=c[x>>2]|0;x=x+34|0;x=a[x>>0]|0;x=x<<24>>24;qc(w,x,29034,8);i=y;return}c[s>>2]=1;if((c[p>>2]|0)==2?(c[(c[n>>2]|0)+5800>>2]|0)==2:0){c[q>>2]=(b[(c[x>>2]|0)+26>>1]|0)-(b[(c[n>>2]|0)+5804>>1]|0);if((c[q>>2]|0)<-8|(c[q>>2]|0)>11)c[q>>2]=0;else{c[q>>2]=(c[q>>2]|0)+9;c[s>>2]=0}qc(c[o>>2]|0,c[q>>2]|0,29096,8)}if(c[s>>2]|0){c[v>>2]=(b[(c[x>>2]|0)+26>>1]|0)/(c[(c[n>>2]|0)+4600>>2]>>1|0)|0;c[w>>2]=(b[(c[x>>2]|0)+26>>1]|0)-(_((c[v>>2]&65535)<<16>>16,(c[(c[n>>2]|0)+4600>>2]>>1&65535)<<16>>16)|0);qc(c[o>>2]|0,c[v>>2]|0,29064,8);qc(c[o>>2]|0,c[w>>2]|0,c[(c[n>>2]|0)+4716>>2]|0,8)}b[(c[n>>2]|0)+5804>>1]=b[(c[x>>2]|0)+26>>1]|0;qc(c[o>>2]|0,a[(c[x>>2]|0)+28>>0]|0,c[(c[n>>2]|0)+4720>>2]|0,8);qc(c[o>>2]|0,a[(c[x>>2]|0)+32>>0]|0,26812,8);c[u>>2]=0;while(1){if((c[u>>2]|0)>=(c[(c[n>>2]|0)+4604>>2]|0))break;qc(c[o>>2]|0,a[(c[x>>2]|0)+4+(c[u>>2]|0)>>0]|0,c[17620+(a[(c[x>>2]|0)+32>>0]<<2)>>2]|0,8);c[u>>2]=(c[u>>2]|0)+1}if(c[p>>2]|0){v=c[x>>2]|0;v=v+29|0;v=a[v>>0]|0;v=v<<24>>24;w=c[n>>2]|0;w=w+5800|0;c[w>>2]=v;w=c[o>>2]|0;x=c[x>>2]|0;x=x+34|0;x=a[x>>0]|0;x=x<<24>>24;qc(w,x,29034,8);i=y;return}qc(c[o>>2]|0,a[(c[x>>2]|0)+33>>0]|0,29017,8);v=c[x>>2]|0;v=v+29|0;v=a[v>>0]|0;v=v<<24>>24;w=c[n>>2]|0;w=w+5800|0;c[w>>2]=v;w=c[o>>2]|0;x=c[x>>2]|0;x=x+34|0;x=a[x>>0]|0;x=x<<24>>24;qc(w,x,29034,8);i=y;return}function Yd(b,e,f,g,h){b=b|0;e=e|0;f=f|0;g=g|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0;F=i;i=i+128|0;r=F+116|0;s=F+112|0;t=F+108|0;u=F+104|0;v=F+100|0;z=F+96|0;C=F+92|0;B=F+88|0;A=F+84|0;y=F+80|0;D=F+76|0;o=F+72|0;j=F+68|0;x=F+64|0;l=F+60|0;p=F+56|0;n=F+24|0;k=F+16|0;E=F+12|0;q=F+8|0;m=F+4|0;w=F;c[r>>2]=b;c[s>>2]=e;c[t>>2]=f;c[u>>2]=g;c[v>>2]=h;c[j>>2]=0;c[n>>2]=0;c[n+4>>2]=0;c[n+8>>2]=0;c[n+12>>2]=0;c[n+16>>2]=0;c[n+20>>2]=0;c[n+24>>2]=0;c[n+28>>2]=0;c[A>>2]=c[v>>2]>>4;if((c[A>>2]<<4|0)<(c[v>>2]|0)){c[A>>2]=(c[A>>2]|0)+1;h=(c[u>>2]|0)+(c[v>>2]|0)|0;b=h+16|0;do{a[h>>0]=0;h=h+1|0}while((h|0)<(b|0))}e=c[A>>2]<<4;c[w>>2]=ia()|0;b=i;i=i+((1*(e<<2)|0)+15&-16)|0;c[z>>2]=0;while(1){if((c[z>>2]|0)>=(c[A>>2]<<4|0))break;e=a[(c[u>>2]|0)+((c[z>>2]|0)+0)>>0]|0;c[b+((c[z>>2]|0)+0<<2)>>2]=(a[(c[u>>2]|0)+((c[z>>2]|0)+0)>>0]|0)>0?e:0-e|0;e=a[(c[u>>2]|0)+((c[z>>2]|0)+1)>>0]|0;c[b+((c[z>>2]|0)+1<<2)>>2]=(a[(c[u>>2]|0)+((c[z>>2]|0)+1)>>0]|0)>0?e:0-e|0;e=a[(c[u>>2]|0)+((c[z>>2]|0)+2)>>0]|0;c[b+((c[z>>2]|0)+2<<2)>>2]=(a[(c[u>>2]|0)+((c[z>>2]|0)+2)>>0]|0)>0?e:0-e|0;e=a[(c[u>>2]|0)+((c[z>>2]|0)+3)>>0]|0;c[b+((c[z>>2]|0)+3<<2)>>2]=(a[(c[u>>2]|0)+((c[z>>2]|0)+3)>>0]|0)>0?e:0-e|0;c[z>>2]=(c[z>>2]|0)+4}g=i;i=i+((1*(c[A>>2]<<2)|0)+15&-16)|0;f=i;i=i+((1*(c[A>>2]<<2)|0)+15&-16)|0;c[k>>2]=b;c[z>>2]=0;while(1){if((c[z>>2]|0)>=(c[A>>2]|0))break;c[f+(c[z>>2]<<2)>>2]=0;a:while(1){c[o>>2]=Zd(n,c[k>>2]|0,d[29177]|0,8)|0;e=Zd(n,n,d[29178]|0,4)|0;c[o>>2]=(c[o>>2]|0)+e;e=Zd(n,n,d[29179]|0,2)|0;c[o>>2]=(c[o>>2]|0)+e;e=Zd(g+(c[z>>2]<<2)|0,n,d[29180]|0,1)|0;c[o>>2]=(c[o>>2]|0)+e;if(!(c[o>>2]|0))break;e=f+(c[z>>2]<<2)|0;c[e>>2]=(c[e>>2]|0)+1;c[C>>2]=0;while(1){if((c[C>>2]|0)>=16)continue a;c[(c[k>>2]|0)+(c[C>>2]<<2)>>2]=c[(c[k>>2]|0)+(c[C>>2]<<2)>>2]>>1;c[C>>2]=(c[C>>2]|0)+1}}c[k>>2]=(c[k>>2]|0)+64;c[z>>2]=(c[z>>2]|0)+1}c[l>>2]=2147483647;c[C>>2]=0;while(1){if((c[C>>2]|0)>=9)break;c[m>>2]=29361+((c[C>>2]|0)*18|0);c[p>>2]=d[29541+((c[s>>2]>>1)*9|0)+(c[C>>2]|0)>>0];c[z>>2]=0;while(1){if((c[z>>2]|0)>=(c[A>>2]|0))break;if((c[f+(c[z>>2]<<2)>>2]|0)>0)c[p>>2]=(c[p>>2]|0)+(d[(c[m>>2]|0)+17>>0]|0);else c[p>>2]=(c[p>>2]|0)+(d[(c[m>>2]|0)+(c[g+(c[z>>2]<<2)>>2]|0)>>0]|0);c[z>>2]=(c[z>>2]|0)+1}if((c[p>>2]|0)<(c[l>>2]|0)){c[l>>2]=c[p>>2];c[j>>2]=c[C>>2]}c[C>>2]=(c[C>>2]|0)+1}qc(c[r>>2]|0,c[j>>2]|0,29523+((c[s>>2]>>1)*9|0)|0,8);c[q>>2]=29181+((c[j>>2]|0)*18|0);c[z>>2]=0;while(1){if((c[z>>2]|0)>=(c[A>>2]|0))break;h=c[r>>2]|0;if(!(c[f+(c[z>>2]<<2)>>2]|0))qc(h,c[g+(c[z>>2]<<2)>>2]|0,c[q>>2]|0,8);else{qc(h,17,c[q>>2]|0,8);c[C>>2]=0;while(1){h=c[r>>2]|0;if((c[C>>2]|0)>=((c[f+(c[z>>2]<<2)>>2]|0)-1|0))break;qc(h,17,29343,8);c[C>>2]=(c[C>>2]|0)+1}qc(h,c[g+(c[z>>2]<<2)>>2]|0,29343,8)}c[z>>2]=(c[z>>2]|0)+1}c[z>>2]=0;while(1){if((c[z>>2]|0)>=(c[A>>2]|0))break;if((c[g+(c[z>>2]<<2)>>2]|0)>0)Pe(c[r>>2]|0,b+(c[z>>2]<<4<<2)|0);c[z>>2]=(c[z>>2]|0)+1}c[z>>2]=0;while(1){if((c[z>>2]|0)>=(c[A>>2]|0))break;b:do if((c[f+(c[z>>2]<<2)>>2]|0)>0){c[E>>2]=(c[u>>2]|0)+(c[z>>2]<<4);c[D>>2]=(c[f+(c[z>>2]<<2)>>2]|0)-1;c[C>>2]=0;while(1){if((c[C>>2]|0)>=16)break b;q=a[(c[E>>2]|0)+(c[C>>2]|0)>>0]|0;c[x>>2]=(((a[(c[E>>2]|0)+(c[C>>2]|0)>>0]|0)>0?q:0-q|0)&255)<<24>>24;c[B>>2]=c[D>>2];while(1){h=c[x>>2]|0;if((c[B>>2]|0)<=0)break;c[y>>2]=h>>c[B>>2]&1;qc(c[r>>2]|0,c[y>>2]|0,29015,8);c[B>>2]=(c[B>>2]|0)+-1}c[y>>2]=h&1;qc(c[r>>2]|0,c[y>>2]|0,29015,8);c[C>>2]=(c[C>>2]|0)+1}}while(0);c[z>>2]=(c[z>>2]|0)+1}Dd(c[r>>2]|0,c[u>>2]|0,c[v>>2]|0,c[s>>2]|0,c[t>>2]|0,g);na(c[w>>2]|0);i=F;return}function Zd(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0;n=i;i=i+32|0;m=n+24|0;f=n+20|0;g=n+16|0;h=n+12|0;j=n+8|0;k=n+4|0;l=n;c[f>>2]=a;c[g>>2]=b;c[h>>2]=d;c[j>>2]=e;c[k>>2]=0;while(1){if((c[k>>2]|0)>=(c[j>>2]|0)){f=6;break}c[l>>2]=(c[(c[g>>2]|0)+(c[k>>2]<<1<<2)>>2]|0)+(c[(c[g>>2]|0)+((c[k>>2]<<1)+1<<2)>>2]|0);if((c[l>>2]|0)>(c[h>>2]|0)){f=4;break}c[(c[f>>2]|0)+(c[k>>2]<<2)>>2]=c[l>>2];c[k>>2]=(c[k>>2]|0)+1}if((f|0)==4){c[m>>2]=1;m=c[m>>2]|0;i=n;return m|0}else if((f|0)==6){c[m>>2]=0;m=c[m>>2]|0;i=n;return m|0}return 0}function _d(b,d,e,f,g){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;p=i;i=i+32|0;h=p+24|0;j=p+20|0;k=p+16|0;l=p+12|0;m=p+8|0;o=p+4|0;n=p;c[h>>2]=b;c[j>>2]=d;c[k>>2]=e;c[l>>2]=f;c[m>>2]=g;c[o>>2]=0;while(1){if((c[o>>2]|0)>=(c[m>>2]|0))break;Sf(c[(c[j>>2]|0)+(c[o>>2]<<2)>>2]|0)|0;e=0+((((Sf(c[(c[j>>2]|0)+(c[o>>2]<<2)>>2]|0)|0)-2090&65535)<<16>>16)*2251>>16)&255;a[(c[h>>2]|0)+(c[o>>2]|0)>>0]=e;if((a[(c[h>>2]|0)+(c[o>>2]|0)>>0]|0)<(a[c[k>>2]>>0]|0)){e=(c[h>>2]|0)+(c[o>>2]|0)|0;a[e>>0]=(a[e>>0]|0)+1<<24>>24}if((a[(c[h>>2]|0)+(c[o>>2]|0)>>0]|0)<=63)if((a[(c[h>>2]|0)+(c[o>>2]|0)>>0]|0)<0)f=0;else f=a[(c[h>>2]|0)+(c[o>>2]|0)>>0]|0;else f=63;a[(c[h>>2]|0)+(c[o>>2]|0)>>0]=f;if((c[o>>2]|0)==0&(c[l>>2]|0)==0){f=a[(c[h>>2]|0)+(c[o>>2]|0)>>0]|0;do if(((a[c[k>>2]>>0]|0)+-4|0)>63){if((f|0)>((a[c[k>>2]>>0]|0)+-4|0)){f=(a[c[k>>2]>>0]|0)+-4|0;break}if((a[(c[h>>2]|0)+(c[o>>2]|0)>>0]|0)<63)f=63;else f=a[(c[h>>2]|0)+(c[o>>2]|0)>>0]|0}else if((f|0)<=63)if((a[(c[h>>2]|0)+(c[o>>2]|0)>>0]|0)<((a[c[k>>2]>>0]|0)+-4|0)){f=(a[c[k>>2]>>0]|0)+-4|0;break}else{f=a[(c[h>>2]|0)+(c[o>>2]|0)>>0]|0;break}else f=63;while(0);a[(c[h>>2]|0)+(c[o>>2]|0)>>0]=f;a[c[k>>2]>>0]=a[(c[h>>2]|0)+(c[o>>2]|0)>>0]|0}else{a[(c[h>>2]|0)+(c[o>>2]|0)>>0]=(a[(c[h>>2]|0)+(c[o>>2]|0)>>0]|0)-(a[c[k>>2]>>0]|0);c[n>>2]=8+(a[c[k>>2]>>0]|0);if((a[(c[h>>2]|0)+(c[o>>2]|0)>>0]|0)>(c[n>>2]|0))a[(c[h>>2]|0)+(c[o>>2]|0)>>0]=(c[n>>2]|0)+((a[(c[h>>2]|0)+(c[o>>2]|0)>>0]|0)-(c[n>>2]|0)+1>>1);if((a[(c[h>>2]|0)+(c[o>>2]|0)>>0]|0)<=36)if((a[(c[h>>2]|0)+(c[o>>2]|0)>>0]|0)<-4)f=-4;else f=a[(c[h>>2]|0)+(c[o>>2]|0)>>0]|0;else f=36;a[(c[h>>2]|0)+(c[o>>2]|0)>>0]=f;f=a[(c[h>>2]|0)+(c[o>>2]|0)>>0]|0;if((a[(c[h>>2]|0)+(c[o>>2]|0)>>0]|0)>(c[n>>2]|0)){e=c[k>>2]|0;a[e>>0]=(a[e>>0]|0)+((f<<1)-(c[n>>2]|0))}else{e=c[k>>2]|0;a[e>>0]=(a[e>>0]|0)+f}e=(c[h>>2]|0)+(c[o>>2]|0)|0;a[e>>0]=(a[e>>0]|0)+4}e=Wf($d(((a[c[k>>2]>>0]<<16>>16)*29|0)+((a[c[k>>2]>>0]<<16>>16)*7281>>16)+2090|0,3967)|0)|0;c[(c[j>>2]|0)+(c[o>>2]<<2)>>2]=e;c[o>>2]=(c[o>>2]|0)+1}i=p;return}function $d(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)<(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function ae(b,d,e,f,g){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;q=i;i=i+32|0;h=q+28|0;j=q+24|0;k=q+20|0;l=q+16|0;m=q+12|0;p=q+8|0;o=q+4|0;n=q;c[h>>2]=b;c[j>>2]=d;c[k>>2]=e;c[l>>2]=f;c[m>>2]=g;c[p>>2]=0;while(1){if((c[p>>2]|0)>=(c[m>>2]|0))break;f=a[(c[j>>2]|0)+(c[p>>2]|0)>>0]|0;do if(!((c[p>>2]|0)==0&(c[l>>2]|0)==0)){c[o>>2]=f+-4;c[n>>2]=8+(a[c[k>>2]>>0]|0);f=c[o>>2]|0;if((c[o>>2]|0)>(c[n>>2]|0)){e=c[k>>2]|0;a[e>>0]=(a[e>>0]|0)+((f<<1)-(c[n>>2]|0));break}else{e=c[k>>2]|0;a[e>>0]=(a[e>>0]|0)+f;break}}else{e=(be(f,(a[c[k>>2]>>0]|0)-16|0)|0)&255;a[c[k>>2]>>0]=e}while(0);if((a[c[k>>2]>>0]|0)<=63)if((a[c[k>>2]>>0]|0)<0)f=0;else f=a[c[k>>2]>>0]|0;else f=63;a[c[k>>2]>>0]=f;e=Wf($d(((a[c[k>>2]>>0]<<16>>16)*29|0)+((a[c[k>>2]>>0]<<16>>16)*7281>>16)+2090|0,3967)|0)|0;c[(c[h>>2]|0)+(c[p>>2]<<2)>>2]=e;c[p>>2]=(c[p>>2]|0)+1}i=q;return}function be(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)>(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function ce(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0;j=i;i=i+16|0;e=j+12|0;f=j+8|0;h=j+4|0;g=j;c[e>>2]=b;c[f>>2]=d;c[g>>2]=0;c[h>>2]=0;while(1){if((c[h>>2]|0)>=(c[f>>2]|0))break;c[g>>2]=(a[(c[e>>2]|0)+(c[h>>2]|0)>>0]|0)+(c[g>>2]<<8);c[h>>2]=(c[h>>2]|0)+1}i=j;return c[g>>2]|0}function de(a,d,e,f,g){a=a|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0;o=i;i=i+32|0;h=o+20|0;j=o+16|0;k=o+12|0;l=o+8|0;m=o+4|0;n=o;c[h>>2]=a;c[j>>2]=d;c[k>>2]=e;c[l>>2]=f;c[m>>2]=g;c[n>>2]=0;while(1){if((c[n>>2]|0)>=(c[m>>2]|0))break;a=(b[(c[j>>2]|0)+(c[n>>2]<<1)>>1]|0)+((_(((b[(c[k>>2]|0)+(c[n>>2]<<1)>>1]|0)-(b[(c[j>>2]|0)+(c[n>>2]<<1)>>1]|0)&65535)<<16>>16,(c[l>>2]&65535)<<16>>16)|0)>>2)&65535;b[(c[h>>2]|0)+(c[n>>2]<<1)>>1]=a;c[n>>2]=(c[n>>2]|0)+1}i=o;return}function ee(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;m=i;i=i+48|0;g=m+36|0;h=m+32|0;j=m+28|0;l=m+16|0;k=m+8|0;e=m+4|0;f=m;c[g>>2]=a;c[h>>2]=b;c[j>>2]=d;c[e>>2]=0;c[f>>2]=0;if(!(c[(c[g>>2]|0)+12>>2]|0)){i=m;return}c[e>>2]=256-(c[(c[g>>2]|0)+8>>2]|0)<<10;c[f>>2]=c[e>>2]>>16;c[e>>2]=(c[e>>2]|0)-(c[f>>2]<<16);fe(l,k,c[f>>2]|0,c[e>>2]|0);if(((c[(c[g>>2]|0)+8>>2]|0)+(c[(c[g>>2]|0)+12>>2]|0)|0)<=256)if(((c[(c[g>>2]|0)+8>>2]|0)+(c[(c[g>>2]|0)+12>>2]|0)|0)<0)e=0;else e=(c[(c[g>>2]|0)+8>>2]|0)+(c[(c[g>>2]|0)+12>>2]|0)|0;else e=256;c[(c[g>>2]|0)+8>>2]=e;Nf(c[h>>2]|0,l,k,c[g>>2]|0,c[h>>2]|0,c[j>>2]|0,1);i=m;return}function fe(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0;m=i;i=i+32|0;f=m+20|0;g=m+16|0;h=m+12|0;j=m+8|0;l=m+4|0;k=m;c[f>>2]=a;c[g>>2]=b;c[h>>2]=d;c[j>>2]=e;if((c[h>>2]|0)>=4){l=c[f>>2]|0;c[l>>2]=c[4473];c[l+4>>2]=c[4474];c[l+8>>2]=c[4475];l=c[g>>2]|0;c[l>>2]=c[4484];c[l+4>>2]=c[4485];i=m;return}if((c[j>>2]|0)<=0){l=c[f>>2]|0;k=17844+((c[h>>2]|0)*12|0)|0;c[l>>2]=c[k>>2];c[l+4>>2]=c[k+4>>2];c[l+8>>2]=c[k+8>>2];l=c[g>>2]|0;k=17904+(c[h>>2]<<3)|0;c[l>>2]=c[k>>2];c[l+4>>2]=c[k+4>>2];i=m;return}d=(c[j>>2]|0)<32768;c[l>>2]=0;if(d){while(1){if((c[l>>2]|0)>=3)break;d=_((c[17844+(((c[h>>2]|0)+1|0)*12|0)+(c[l>>2]<<2)>>2]|0)-(c[17844+((c[h>>2]|0)*12|0)+(c[l>>2]<<2)>>2]|0)>>16,(c[j>>2]&65535)<<16>>16)|0;d=(c[17844+((c[h>>2]|0)*12|0)+(c[l>>2]<<2)>>2]|0)+(d+((_((c[17844+(((c[h>>2]|0)+1|0)*12|0)+(c[l>>2]<<2)>>2]|0)-(c[17844+((c[h>>2]|0)*12|0)+(c[l>>2]<<2)>>2]|0)&65535,(c[j>>2]&65535)<<16>>16)|0)>>16))|0;c[(c[f>>2]|0)+(c[l>>2]<<2)>>2]=d;c[l>>2]=(c[l>>2]|0)+1}c[k>>2]=0;while(1){if((c[k>>2]|0)>=2)break;l=_((c[17904+((c[h>>2]|0)+1<<3)+(c[k>>2]<<2)>>2]|0)-(c[17904+(c[h>>2]<<3)+(c[k>>2]<<2)>>2]|0)>>16,(c[j>>2]&65535)<<16>>16)|0;l=(c[17904+(c[h>>2]<<3)+(c[k>>2]<<2)>>2]|0)+(l+((_((c[17904+((c[h>>2]|0)+1<<3)+(c[k>>2]<<2)>>2]|0)-(c[17904+(c[h>>2]<<3)+(c[k>>2]<<2)>>2]|0)&65535,(c[j>>2]&65535)<<16>>16)|0)>>16))|0;c[(c[g>>2]|0)+(c[k>>2]<<2)>>2]=l;c[k>>2]=(c[k>>2]|0)+1}i=m;return}else{while(1){if((c[l>>2]|0)>=3)break;d=_((c[17844+(((c[h>>2]|0)+1|0)*12|0)+(c[l>>2]<<2)>>2]|0)-(c[17844+((c[h>>2]|0)*12|0)+(c[l>>2]<<2)>>2]|0)>>16,((c[j>>2]|0)-65536&65535)<<16>>16)|0;d=(c[17844+(((c[h>>2]|0)+1|0)*12|0)+(c[l>>2]<<2)>>2]|0)+(d+((_((c[17844+(((c[h>>2]|0)+1|0)*12|0)+(c[l>>2]<<2)>>2]|0)-(c[17844+((c[h>>2]|0)*12|0)+(c[l>>2]<<2)>>2]|0)&65535,((c[j>>2]|0)-65536&65535)<<16>>16)|0)>>16))|0;c[(c[f>>2]|0)+(c[l>>2]<<2)>>2]=d;c[l>>2]=(c[l>>2]|0)+1}c[k>>2]=0;while(1){if((c[k>>2]|0)>=2)break;l=_((c[17904+((c[h>>2]|0)+1<<3)+(c[k>>2]<<2)>>2]|0)-(c[17904+(c[h>>2]<<3)+(c[k>>2]<<2)>>2]|0)>>16,((c[j>>2]|0)-65536&65535)<<16>>16)|0;l=(c[17904+((c[h>>2]|0)+1<<3)+(c[k>>2]<<2)>>2]|0)+(l+((_((c[17904+((c[h>>2]|0)+1<<3)+(c[k>>2]<<2)>>2]|0)-(c[17904+(c[h>>2]<<3)+(c[k>>2]<<2)>>2]|0)&65535,((c[j>>2]|0)-65536&65535)<<16>>16)|0)>>16))|0;c[(c[g>>2]|0)+(c[k>>2]<<2)>>2]=l;c[k>>2]=(c[k>>2]|0)+1}i=m;return}}function ge(e,f,g){e=e|0;f=f|0;g=g|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;t=i;i=i+144|0;m=t+24|0;h=t+20|0;n=t+16|0;r=t+12|0;l=t+128|0;j=t+96|0;s=t+64|0;q=t+32|0;p=t+8|0;o=t+4|0;k=t;c[m>>2]=e;c[h>>2]=f;c[n>>2]=g;g=_(a[c[h>>2]>>0]|0,b[(c[n>>2]|0)+2>>1]|0)|0;c[k>>2]=(c[(c[n>>2]|0)+8>>2]|0)+g;c[r>>2]=0;while(1){if((c[r>>2]|0)>=(b[(c[n>>2]|0)+2>>1]|0))break;b[(c[m>>2]|0)+(c[r>>2]<<1)>>1]=d[(c[k>>2]|0)+(c[r>>2]|0)>>0]<<7;c[r>>2]=(c[r>>2]|0)+1}nf(j,l,c[n>>2]|0,a[c[h>>2]>>0]|0);he(s,(c[h>>2]|0)+1|0,l,b[(c[n>>2]|0)+4>>1]|0,b[(c[n>>2]|0)+2>>1]|0);fg(q,c[m>>2]|0,b[(c[n>>2]|0)+2>>1]|0);c[r>>2]=0;while(1){if((c[r>>2]|0)>=(b[(c[n>>2]|0)+2>>1]|0))break;c[p>>2]=ie(b[q+(c[r>>2]<<1)>>1]<<16)|0;c[o>>2]=(b[(c[m>>2]|0)+(c[r>>2]<<1)>>1]|0)+((b[s+(c[r>>2]<<1)>>1]<<14|0)/(c[p>>2]|0)|0);if((c[o>>2]|0)>32767)h=32767;else h=(c[o>>2]|0)<0?0:c[o>>2]|0;b[(c[m>>2]|0)+(c[r>>2]<<1)>>1]=h;c[r>>2]=(c[r>>2]|0)+1}cg(c[m>>2]|0,c[(c[n>>2]|0)+32>>2]|0,b[(c[n>>2]|0)+2>>1]|0);i=t;return}function he(e,f,g,h,j){e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;r=i;i=i+32|0;k=r+24|0;l=r+20|0;m=r+16|0;n=r+12|0;s=r+28|0;o=r+8|0;p=r+4|0;q=r;c[k>>2]=e;c[l>>2]=f;c[m>>2]=g;c[n>>2]=h;b[s>>1]=j;c[p>>2]=0;c[o>>2]=(b[s>>1]|0)-1;while(1){if((c[o>>2]|0)<0)break;c[q>>2]=(_((c[p>>2]&65535)<<16>>16,d[(c[m>>2]|0)+(c[o>>2]|0)>>0]|0)|0)>>8;c[p>>2]=a[(c[l>>2]|0)+(c[o>>2]|0)>>0]<<10;f=c[p>>2]|0;if((c[p>>2]|0)<=0){if((f|0)<0)c[p>>2]=(c[p>>2]|0)+102}else c[p>>2]=f-102;s=_(c[p>>2]>>16,(c[n>>2]&65535)<<16>>16)|0;c[p>>2]=(c[q>>2]|0)+(s+((_(c[p>>2]&65535,(c[n>>2]&65535)<<16>>16)|0)>>16));b[(c[k>>2]|0)+(c[o>>2]<<1)>>1]=c[p>>2];c[o>>2]=(c[o>>2]|0)+-1}i=r;return}function ie(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0;h=i;i=i+32|0;b=h+16|0;d=h+12|0;g=h+8|0;f=h+4|0;e=h;c[d>>2]=a;if((c[d>>2]|0)<=0){c[b>>2]=0;g=c[b>>2]|0;i=h;return g|0}je(c[d>>2]|0,f,e);if(c[f>>2]&1|0)c[g>>2]=32768;else c[g>>2]=46214;c[g>>2]=c[g>>2]>>(c[f>>2]>>1);a=_(c[g>>2]>>16,(((c[e>>2]&65535)<<16>>16)*213&65535)<<16>>16)|0;c[g>>2]=(c[g>>2]|0)+(a+((_(c[g>>2]&65535,(((c[e>>2]&65535)<<16>>16)*213&65535)<<16>>16)|0)>>16));c[b>>2]=c[g>>2];g=c[b>>2]|0;i=h;return g|0}function je(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0;e=i;i=i+16|0;h=e+12|0;j=e+8|0;f=e+4|0;g=e;c[h>>2]=a;c[j>>2]=b;c[f>>2]=d;c[g>>2]=ke(c[h>>2]|0)|0;c[c[j>>2]>>2]=c[g>>2];b=(le(c[h>>2]|0,24-(c[g>>2]|0)|0)|0)&127;c[c[f>>2]>>2]=b;i=e;return}function ke(a){a=a|0;var b=0,d=0;d=i;i=i+16|0;b=d;c[b>>2]=a;if(!(c[b>>2]|0)){a=32;i=d;return a|0}a=32-(32-(aa(c[b>>2]|0)|0))|0;i=d;return a|0}function le(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0;k=i;i=i+32|0;e=k+20|0;d=k+16|0;f=k+12|0;j=k+8|0;h=k+4|0;g=k;c[d>>2]=a;c[f>>2]=b;c[j>>2]=c[d>>2];c[h>>2]=c[f>>2];c[g>>2]=0-(c[f>>2]|0);if(!(c[f>>2]|0)){c[e>>2]=c[d>>2];j=c[e>>2]|0;i=k;return j|0}d=c[j>>2]|0;if((c[f>>2]|0)<0){c[e>>2]=d<<c[g>>2]|(c[j>>2]|0)>>>(32-(c[g>>2]|0)|0);j=c[e>>2]|0;i=k;return j|0}else{c[e>>2]=d<<32-(c[h>>2]|0)|(c[j>>2]|0)>>>(c[h>>2]|0);j=c[e>>2]|0;i=k;return j|0}return 0}function me(d,e,f,g,h,j,k,l,m,n,o,p,q,r,s){d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;p=p|0;q=q|0;r=r|0;s=s|0;var t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0;T=i;i=i+112|0;t=T+100|0;u=T+96|0;B=T+92|0;C=T+88|0;D=T+84|0;E=T+80|0;F=T+76|0;G=T+72|0;H=T+68|0;I=T+64|0;v=T+60|0;w=T+56|0;x=T+52|0;y=T+48|0;z=T+44|0;O=T+40|0;P=T+36|0;S=T+32|0;N=T+28|0;K=T+24|0;L=T+20|0;J=T+16|0;R=T+12|0;M=T+8|0;Q=T+4|0;A=T;c[t>>2]=d;c[u>>2]=e;c[B>>2]=f;c[C>>2]=g;c[D>>2]=h;c[E>>2]=j;c[F>>2]=k;c[G>>2]=l;c[H>>2]=m;c[I>>2]=n;c[v>>2]=o;c[w>>2]=p;c[x>>2]=q;c[y>>2]=r;c[z>>2]=s;c[(c[u>>2]|0)+4368>>2]=a[(c[B>>2]|0)+34>>0];c[P>>2]=c[(c[u>>2]|0)+4356>>2];c[Q>>2]=b[24558+(a[(c[B>>2]|0)+29>>0]>>1<<2)+(a[(c[B>>2]|0)+30>>0]<<1)>>1];if((a[(c[B>>2]|0)+31>>0]|0)==4)c[N>>2]=0;else c[N>>2]=1;p=(c[(c[t>>2]|0)+4616>>2]|0)+(c[(c[t>>2]|0)+4608>>2]|0)|0;c[A>>2]=ia()|0;l=i;i=i+((1*(p<<2)|0)+15&-16)|0;p=i;i=i+((1*((c[(c[t>>2]|0)+4616>>2]|0)+(c[(c[t>>2]|0)+4608>>2]|0)<<1)|0)+15&-16)|0;m=i;i=i+((1*(c[(c[t>>2]|0)+4612>>2]<<2)|0)+15&-16)|0;c[(c[u>>2]|0)+4364>>2]=c[(c[t>>2]|0)+4616>>2];c[(c[u>>2]|0)+4360>>2]=c[(c[t>>2]|0)+4616>>2];c[R>>2]=(c[u>>2]|0)+(c[(c[t>>2]|0)+4616>>2]<<1);c[O>>2]=0;while(1){if((c[O>>2]|0)>=(c[(c[t>>2]|0)+4604>>2]|0))break;c[K>>2]=(c[E>>2]|0)+((c[O>>2]>>1|1-(c[N>>2]|0))<<4<<1);c[L>>2]=(c[F>>2]|0)+((c[O>>2]|0)*5<<1);c[J>>2]=(c[G>>2]|0)+(c[O>>2]<<4<<1);c[M>>2]=c[(c[H>>2]|0)+(c[O>>2]<<2)>>2]>>2;c[M>>2]=c[M>>2]|c[(c[H>>2]|0)+(c[O>>2]<<2)>>2]>>1<<16;c[(c[u>>2]|0)+4376>>2]=0;if((a[(c[B>>2]|0)+29>>0]|0)==2?(c[P>>2]=c[(c[x>>2]|0)+(c[O>>2]<<2)>>2],(c[O>>2]&3-(c[N>>2]<<1)|0)==0):0){c[S>>2]=(c[(c[t>>2]|0)+4616>>2]|0)-(c[P>>2]|0)-(c[(c[t>>2]|0)+4664>>2]|0)-2;g=(c[S>>2]|0)+(_(c[O>>2]|0,c[(c[t>>2]|0)+4612>>2]|0)|0)|0;Xf(p+(c[S>>2]<<1)|0,(c[u>>2]|0)+(g<<1)|0,c[K>>2]|0,(c[(c[t>>2]|0)+4616>>2]|0)-(c[S>>2]|0)|0,c[(c[t>>2]|0)+4664>>2]|0,c[(c[t>>2]|0)+5124>>2]|0);c[(c[u>>2]|0)+4376>>2]=1;c[(c[u>>2]|0)+4360>>2]=c[(c[t>>2]|0)+4616>>2]}ne(c[t>>2]|0,c[u>>2]|0,c[C>>2]|0,m,p,l,c[O>>2]|0,c[z>>2]|0,c[w>>2]|0,c[x>>2]|0,a[(c[B>>2]|0)+29>>0]|0);re(c[u>>2]|0,a[(c[B>>2]|0)+29>>0]|0,m,c[D>>2]|0,c[R>>2]|0,l,c[K>>2]|0,c[L>>2]|0,c[J>>2]|0,c[P>>2]|0,c[M>>2]|0,c[(c[I>>2]|0)+(c[O>>2]<<2)>>2]|0,c[(c[v>>2]|0)+(c[O>>2]<<2)>>2]|0,c[(c[w>>2]|0)+(c[O>>2]<<2)>>2]|0,c[y>>2]|0,c[Q>>2]|0,c[(c[t>>2]|0)+4612>>2]|0,c[(c[t>>2]|0)+4660>>2]|0,c[(c[t>>2]|0)+4664>>2]|0);c[C>>2]=(c[C>>2]|0)+(c[(c[t>>2]|0)+4612>>2]<<2);c[D>>2]=(c[D>>2]|0)+(c[(c[t>>2]|0)+4612>>2]|0);c[R>>2]=(c[R>>2]|0)+(c[(c[t>>2]|0)+4612>>2]<<1);c[O>>2]=(c[O>>2]|0)+1}c[(c[u>>2]|0)+4356>>2]=c[(c[x>>2]|0)+((c[(c[t>>2]|0)+4604>>2]|0)-1<<2)>>2];qj(c[u>>2]|0,(c[u>>2]|0)+(c[(c[t>>2]|0)+4608>>2]<<1)|0,c[(c[t>>2]|0)+4616>>2]<<1|0)|0;qj((c[u>>2]|0)+1280|0,(c[u>>2]|0)+1280+(c[(c[t>>2]|0)+4608>>2]<<2)|0,c[(c[t>>2]|0)+4616>>2]<<2|0)|0;na(c[A>>2]|0);i=T;return}function ne(a,d,e,f,g,h,j,k,l,m,n){a=a|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;var o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0;D=i;i=i+64|0;o=D+60|0;p=D+56|0;r=D+52|0;s=D+48|0;t=D+44|0;u=D+40|0;v=D+36|0;w=D+32|0;x=D+28|0;E=D+24|0;q=D+20|0;z=D+16|0;C=D+12|0;y=D+8|0;B=D+4|0;A=D;c[o>>2]=a;c[p>>2]=d;c[r>>2]=e;c[s>>2]=f;c[t>>2]=g;c[u>>2]=h;c[v>>2]=j;c[w>>2]=k;c[x>>2]=l;c[E>>2]=m;c[q>>2]=n;c[C>>2]=c[(c[E>>2]|0)+(c[v>>2]<<2)>>2];if((c[(c[x>>2]|0)+(c[v>>2]<<2)>>2]|0)>1)l=c[(c[x>>2]|0)+(c[v>>2]<<2)>>2]|0;else l=1;c[B>>2]=oe(l,47)|0;if((c[(c[x>>2]|0)+(c[v>>2]<<2)>>2]|0)!=(c[(c[p>>2]|0)+4372>>2]|0))c[y>>2]=qe(c[(c[p>>2]|0)+4372>>2]|0,c[(c[x>>2]|0)+(c[v>>2]<<2)>>2]|0,16)|0;else c[y>>2]=65536;c[A>>2]=(c[B>>2]>>7)+1>>1;c[z>>2]=0;while(1){if((c[z>>2]|0)>=(c[(c[o>>2]|0)+4612>>2]|0))break;E=_(c[(c[r>>2]|0)+(c[z>>2]<<2)>>2]>>16,(c[A>>2]&65535)<<16>>16)|0;E=E+((_(c[(c[r>>2]|0)+(c[z>>2]<<2)>>2]&65535,(c[A>>2]&65535)<<16>>16)|0)>>16)|0;E=E+(_(c[(c[r>>2]|0)+(c[z>>2]<<2)>>2]|0,(c[A>>2]>>15)+1>>1)|0)|0;c[(c[s>>2]|0)+(c[z>>2]<<2)>>2]=E;c[z>>2]=(c[z>>2]|0)+1}c[(c[p>>2]|0)+4372>>2]=c[(c[x>>2]|0)+(c[v>>2]<<2)>>2];a:do if(c[(c[p>>2]|0)+4376>>2]|0){if(!(c[v>>2]|0)){E=_(c[B>>2]>>16,(c[w>>2]&65535)<<16>>16)|0;c[B>>2]=E+((_(c[B>>2]&65535,(c[w>>2]&65535)<<16>>16)|0)>>16)<<2}c[z>>2]=(c[(c[p>>2]|0)+4360>>2]|0)-(c[C>>2]|0)-2;while(1){if((c[z>>2]|0)>=(c[(c[p>>2]|0)+4360>>2]|0))break a;E=_(c[B>>2]>>16,b[(c[t>>2]|0)+(c[z>>2]<<1)>>1]|0)|0;E=E+((_(c[B>>2]&65535,b[(c[t>>2]|0)+(c[z>>2]<<1)>>1]|0)|0)>>16)|0;c[(c[u>>2]|0)+(c[z>>2]<<2)>>2]=E;c[z>>2]=(c[z>>2]|0)+1}}while(0);if((c[y>>2]|0)==65536){i=D;return}c[z>>2]=(c[(c[p>>2]|0)+4364>>2]|0)-(c[(c[o>>2]|0)+4616>>2]|0);while(1){if((c[z>>2]|0)>=(c[(c[p>>2]|0)+4364>>2]|0))break;E=_(c[y>>2]>>16,(c[(c[p>>2]|0)+1280+(c[z>>2]<<2)>>2]&65535)<<16>>16)|0;E=E+((_(c[y>>2]&65535,(c[(c[p>>2]|0)+1280+(c[z>>2]<<2)>>2]&65535)<<16>>16)|0)>>16)|0;E=E+(_(c[y>>2]|0,(c[(c[p>>2]|0)+1280+(c[z>>2]<<2)>>2]>>15)+1>>1)|0)|0;c[(c[p>>2]|0)+1280+(c[z>>2]<<2)>>2]=E;c[z>>2]=(c[z>>2]|0)+1}b:do if((c[q>>2]|0)==2?(c[(c[p>>2]|0)+4376>>2]|0)==0:0){c[z>>2]=(c[(c[p>>2]|0)+4360>>2]|0)-(c[C>>2]|0)-2;while(1){if((c[z>>2]|0)>=(c[(c[p>>2]|0)+4360>>2]|0))break b;E=_(c[y>>2]>>16,(c[(c[u>>2]|0)+(c[z>>2]<<2)>>2]&65535)<<16>>16)|0;E=E+((_(c[y>>2]&65535,(c[(c[u>>2]|0)+(c[z>>2]<<2)>>2]&65535)<<16>>16)|0)>>16)|0;E=E+(_(c[y>>2]|0,(c[(c[u>>2]|0)+(c[z>>2]<<2)>>2]>>15)+1>>1)|0)|0;c[(c[u>>2]|0)+(c[z>>2]<<2)>>2]=E;c[z>>2]=(c[z>>2]|0)+1}}while(0);E=_(c[y>>2]>>16,(c[(c[p>>2]|0)+4352>>2]&65535)<<16>>16)|0;E=E+((_(c[y>>2]&65535,(c[(c[p>>2]|0)+4352>>2]&65535)<<16>>16)|0)>>16)|0;E=E+(_(c[y>>2]|0,(c[(c[p>>2]|0)+4352>>2]>>15)+1>>1)|0)|0;c[(c[p>>2]|0)+4352>>2]=E;c[z>>2]=0;while(1){if((c[z>>2]|0)>=32)break;E=_(c[y>>2]>>16,(c[(c[p>>2]|0)+3840+(c[z>>2]<<2)>>2]&65535)<<16>>16)|0;E=E+((_(c[y>>2]&65535,(c[(c[p>>2]|0)+3840+(c[z>>2]<<2)>>2]&65535)<<16>>16)|0)>>16)|0;E=E+(_(c[y>>2]|0,(c[(c[p>>2]|0)+3840+(c[z>>2]<<2)>>2]>>15)+1>>1)|0)|0;c[(c[p>>2]|0)+3840+(c[z>>2]<<2)>>2]=E;c[z>>2]=(c[z>>2]|0)+1}c[z>>2]=0;while(1){if((c[z>>2]|0)>=16)break;E=_(c[y>>2]>>16,(c[(c[p>>2]|0)+4288+(c[z>>2]<<2)>>2]&65535)<<16>>16)|0;E=E+((_(c[y>>2]&65535,(c[(c[p>>2]|0)+4288+(c[z>>2]<<2)>>2]&65535)<<16>>16)|0)>>16)|0;E=E+(_(c[y>>2]|0,(c[(c[p>>2]|0)+4288+(c[z>>2]<<2)>>2]>>15)+1>>1)|0)|0;c[(c[p>>2]|0)+4288+(c[z>>2]<<2)>>2]=E;c[z>>2]=(c[z>>2]|0)+1}i=D;return}function oe(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0;h=i;i=i+48|0;f=h+32|0;n=h+28|0;d=h+24|0;j=h+20|0;g=h+16|0;k=h+12|0;m=h+8|0;l=h+4|0;e=h;c[n>>2]=a;c[d>>2]=b;b=c[n>>2]|0;c[j>>2]=(pe((c[n>>2]|0)>0?b:0-b|0)|0)-1;c[m>>2]=c[n>>2]<<c[j>>2];c[k>>2]=536870911/(c[m>>2]>>16|0)|0;c[e>>2]=c[k>>2]<<16;b=_(c[m>>2]>>16,(c[k>>2]&65535)<<16>>16)|0;c[l>>2]=536870912-(b+((_(c[m>>2]&65535,(c[k>>2]&65535)<<16>>16)|0)>>16))<<3;b=_(c[l>>2]>>16,(c[k>>2]&65535)<<16>>16)|0;b=(c[e>>2]|0)+(b+((_(c[l>>2]&65535,(c[k>>2]&65535)<<16>>16)|0)>>16))|0;c[e>>2]=b+(_(c[l>>2]|0,(c[k>>2]>>15)+1>>1)|0);c[g>>2]=61-(c[j>>2]|0)-(c[d>>2]|0);b=c[g>>2]|0;if((c[g>>2]|0)>0)if((b|0)<32){c[f>>2]=c[e>>2]>>c[g>>2];n=c[f>>2]|0;i=h;return n|0}else{c[f>>2]=0;n=c[f>>2]|0;i=h;return n|0}a=c[e>>2]|0;d=0-(c[g>>2]|0)|0;do if((-2147483648>>0-b|0)>(2147483647>>0-(c[g>>2]|0)|0)){if((a|0)>(-2147483648>>d|0)){b=-2147483648>>0-(c[g>>2]|0);break}if((c[e>>2]|0)<(2147483647>>0-(c[g>>2]|0)|0)){b=2147483647>>0-(c[g>>2]|0);break}else{b=c[e>>2]|0;break}}else{if((a|0)>(2147483647>>d|0)){b=2147483647>>0-(c[g>>2]|0);break}if((c[e>>2]|0)<(-2147483648>>0-(c[g>>2]|0)|0)){b=-2147483648>>0-(c[g>>2]|0);break}else{b=c[e>>2]|0;break}}while(0);c[f>>2]=b<<0-(c[g>>2]|0);n=c[f>>2]|0;i=h;return n|0}function pe(a){a=a|0;var b=0,d=0;d=i;i=i+16|0;b=d;c[b>>2]=a;if(!(c[b>>2]|0)){a=32;i=d;return a|0}a=32-(32-(aa(c[b>>2]|0)|0))|0;i=d;return a|0}function qe(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;h=i;i=i+48|0;f=h+40|0;q=h+36|0;p=h+32|0;j=h+28|0;l=h+24|0;k=h+20|0;g=h+16|0;m=h+12|0;n=h+8|0;o=h+4|0;e=h;c[q>>2]=a;c[p>>2]=b;c[j>>2]=d;b=c[q>>2]|0;c[l>>2]=(pe((c[q>>2]|0)>0?b:0-b|0)|0)-1;c[n>>2]=c[q>>2]<<c[l>>2];b=c[p>>2]|0;c[k>>2]=(pe((c[p>>2]|0)>0?b:0-b|0)|0)-1;c[o>>2]=c[p>>2]<<c[k>>2];c[m>>2]=536870911/(c[o>>2]>>16|0)|0;b=_(c[n>>2]>>16,(c[m>>2]&65535)<<16>>16)|0;c[e>>2]=b+((_(c[n>>2]&65535,(c[m>>2]&65535)<<16>>16)|0)>>16);b=c[n>>2]|0;a=c[o>>2]|0;d=c[e>>2]|0;d=yj(a|0,((a|0)<0)<<31>>31|0,d|0,((d|0)<0)<<31>>31|0)|0;d=nj(d|0,C|0,32)|0;c[n>>2]=b-(d<<3);d=_(c[n>>2]>>16,(c[m>>2]&65535)<<16>>16)|0;c[e>>2]=(c[e>>2]|0)+(d+((_(c[n>>2]&65535,(c[m>>2]&65535)<<16>>16)|0)>>16));c[g>>2]=29+(c[l>>2]|0)-(c[k>>2]|0)-(c[j>>2]|0);d=c[g>>2]|0;if((c[g>>2]|0)>=0)if((d|0)<32){c[f>>2]=c[e>>2]>>c[g>>2];q=c[f>>2]|0;i=h;return q|0}else{c[f>>2]=0;q=c[f>>2]|0;i=h;return q|0}a=c[e>>2]|0;b=0-(c[g>>2]|0)|0;do if((-2147483648>>0-d|0)>(2147483647>>0-(c[g>>2]|0)|0)){if((a|0)>(-2147483648>>b|0)){d=-2147483648>>0-(c[g>>2]|0);break}if((c[e>>2]|0)<(2147483647>>0-(c[g>>2]|0)|0)){d=2147483647>>0-(c[g>>2]|0);break}else{d=c[e>>2]|0;break}}else{if((a|0)>(2147483647>>b|0)){d=2147483647>>0-(c[g>>2]|0);break}if((c[e>>2]|0)<(-2147483648>>0-(c[g>>2]|0)|0)){d=-2147483648>>0-(c[g>>2]|0);break}else{d=c[e>>2]|0;break}}while(0);c[f>>2]=d<<0-(c[g>>2]|0);q=c[f>>2]|0;i=h;return q|0}function re(d,e,f,g,h,j,k,l,m,n,o,p,q,r,s,t,u,v,w){d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;p=p|0;q=q|0;r=r|0;s=s|0;t=t|0;u=u|0;v=v|0;w=w|0;var x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0,ha=0,ia=0,ja=0,ka=0,la=0,ma=0,na=0;ma=i;i=i+176|0;ka=ma+168|0;x=ma+164|0;F=ma+160|0;G=ma+156|0;H=ma+152|0;I=ma+148|0;J=ma+144|0;K=ma+140|0;L=ma+136|0;M=ma+132|0;y=ma+128|0;z=ma+124|0;A=ma+120|0;na=ma+116|0;B=ma+112|0;C=ma+108|0;la=ma+104|0;D=ma+100|0;E=ma+96|0;S=ma+92|0;T=ma+88|0;Q=ma+84|0;P=ma+80|0;U=ma+76|0;W=ma+72|0;V=ma+68|0;ba=ma+64|0;ea=ma+60|0;Z=ma+56|0;$=ma+52|0;aa=ma+48|0;ca=ma+44|0;da=ma+40|0;R=ma+36|0;O=ma+32|0;ja=ma+28|0;N=ma+24|0;ha=ma+20|0;ia=ma+16|0;fa=ma+12|0;Y=ma+8|0;ga=ma+4|0;X=ma;c[ka>>2]=d;c[x>>2]=e;c[F>>2]=f;c[G>>2]=g;c[H>>2]=h;c[I>>2]=j;c[J>>2]=k;c[K>>2]=l;c[L>>2]=m;c[M>>2]=n;c[y>>2]=o;c[z>>2]=p;c[A>>2]=q;c[na>>2]=r;c[B>>2]=s;c[C>>2]=t;c[la>>2]=u;c[D>>2]=v;c[E>>2]=w;c[ga>>2]=(c[ka>>2]|0)+1280+((c[(c[ka>>2]|0)+4364>>2]|0)-(c[M>>2]|0)+1<<2);c[X>>2]=(c[I>>2]|0)+((c[(c[ka>>2]|0)+4360>>2]|0)-(c[M>>2]|0)+2<<2);c[N>>2]=c[na>>2]>>6;c[Y>>2]=(c[ka>>2]|0)+3840+124;c[S>>2]=0;while(1){m=c[ka>>2]|0;if((c[S>>2]|0)>=(c[la>>2]|0))break;na=907633515+(_(c[m+4368>>2]|0,196314165)|0)|0;c[(c[ka>>2]|0)+4368>>2]=na;c[P>>2]=c[E>>2]>>1;na=_(c[c[Y>>2]>>2]>>16,b[c[J>>2]>>1]|0)|0;c[P>>2]=(c[P>>2]|0)+(na+((_(c[c[Y>>2]>>2]&65535,b[c[J>>2]>>1]|0)|0)>>16));na=_(c[(c[Y>>2]|0)+-4>>2]>>16,b[(c[J>>2]|0)+2>>1]|0)|0;c[P>>2]=(c[P>>2]|0)+(na+((_(c[(c[Y>>2]|0)+-4>>2]&65535,b[(c[J>>2]|0)+2>>1]|0)|0)>>16));na=_(c[(c[Y>>2]|0)+-8>>2]>>16,b[(c[J>>2]|0)+4>>1]|0)|0;c[P>>2]=(c[P>>2]|0)+(na+((_(c[(c[Y>>2]|0)+-8>>2]&65535,b[(c[J>>2]|0)+4>>1]|0)|0)>>16));na=_(c[(c[Y>>2]|0)+-12>>2]>>16,b[(c[J>>2]|0)+6>>1]|0)|0;c[P>>2]=(c[P>>2]|0)+(na+((_(c[(c[Y>>2]|0)+-12>>2]&65535,b[(c[J>>2]|0)+6>>1]|0)|0)>>16));na=_(c[(c[Y>>2]|0)+-16>>2]>>16,b[(c[J>>2]|0)+8>>1]|0)|0;c[P>>2]=(c[P>>2]|0)+(na+((_(c[(c[Y>>2]|0)+-16>>2]&65535,b[(c[J>>2]|0)+8>>1]|0)|0)>>16));na=_(c[(c[Y>>2]|0)+-20>>2]>>16,b[(c[J>>2]|0)+10>>1]|0)|0;c[P>>2]=(c[P>>2]|0)+(na+((_(c[(c[Y>>2]|0)+-20>>2]&65535,b[(c[J>>2]|0)+10>>1]|0)|0)>>16));na=_(c[(c[Y>>2]|0)+-24>>2]>>16,b[(c[J>>2]|0)+12>>1]|0)|0;c[P>>2]=(c[P>>2]|0)+(na+((_(c[(c[Y>>2]|0)+-24>>2]&65535,b[(c[J>>2]|0)+12>>1]|0)|0)>>16));na=_(c[(c[Y>>2]|0)+-28>>2]>>16,b[(c[J>>2]|0)+14>>1]|0)|0;c[P>>2]=(c[P>>2]|0)+(na+((_(c[(c[Y>>2]|0)+-28>>2]&65535,b[(c[J>>2]|0)+14>>1]|0)|0)>>16));na=_(c[(c[Y>>2]|0)+-32>>2]>>16,b[(c[J>>2]|0)+16>>1]|0)|0;c[P>>2]=(c[P>>2]|0)+(na+((_(c[(c[Y>>2]|0)+-32>>2]&65535,b[(c[J>>2]|0)+16>>1]|0)|0)>>16));na=_(c[(c[Y>>2]|0)+-36>>2]>>16,b[(c[J>>2]|0)+18>>1]|0)|0;c[P>>2]=(c[P>>2]|0)+(na+((_(c[(c[Y>>2]|0)+-36>>2]&65535,b[(c[J>>2]|0)+18>>1]|0)|0)>>16));if((c[E>>2]|0)==16){na=_(c[(c[Y>>2]|0)+-40>>2]>>16,b[(c[J>>2]|0)+20>>1]|0)|0;c[P>>2]=(c[P>>2]|0)+(na+((_(c[(c[Y>>2]|0)+-40>>2]&65535,b[(c[J>>2]|0)+20>>1]|0)|0)>>16));na=_(c[(c[Y>>2]|0)+-44>>2]>>16,b[(c[J>>2]|0)+22>>1]|0)|0;c[P>>2]=(c[P>>2]|0)+(na+((_(c[(c[Y>>2]|0)+-44>>2]&65535,b[(c[J>>2]|0)+22>>1]|0)|0)>>16));na=_(c[(c[Y>>2]|0)+-48>>2]>>16,b[(c[J>>2]|0)+24>>1]|0)|0;c[P>>2]=(c[P>>2]|0)+(na+((_(c[(c[Y>>2]|0)+-48>>2]&65535,b[(c[J>>2]|0)+24>>1]|0)|0)>>16));na=_(c[(c[Y>>2]|0)+-52>>2]>>16,b[(c[J>>2]|0)+26>>1]|0)|0;c[P>>2]=(c[P>>2]|0)+(na+((_(c[(c[Y>>2]|0)+-52>>2]&65535,b[(c[J>>2]|0)+26>>1]|0)|0)>>16));na=_(c[(c[Y>>2]|0)+-56>>2]>>16,b[(c[J>>2]|0)+28>>1]|0)|0;c[P>>2]=(c[P>>2]|0)+(na+((_(c[(c[Y>>2]|0)+-56>>2]&65535,b[(c[J>>2]|0)+28>>1]|0)|0)>>16));na=_(c[(c[Y>>2]|0)+-60>>2]>>16,b[(c[J>>2]|0)+30>>1]|0)|0;c[P>>2]=(c[P>>2]|0)+(na+((_(c[(c[Y>>2]|0)+-60>>2]&65535,b[(c[J>>2]|0)+30>>1]|0)|0)>>16))}if((c[x>>2]|0)==2){c[Q>>2]=2;na=_(c[c[X>>2]>>2]>>16,b[c[K>>2]>>1]|0)|0;c[Q>>2]=(c[Q>>2]|0)+(na+((_(c[c[X>>2]>>2]&65535,b[c[K>>2]>>1]|0)|0)>>16));na=_(c[(c[X>>2]|0)+-4>>2]>>16,b[(c[K>>2]|0)+2>>1]|0)|0;c[Q>>2]=(c[Q>>2]|0)+(na+((_(c[(c[X>>2]|0)+-4>>2]&65535,b[(c[K>>2]|0)+2>>1]|0)|0)>>16));na=_(c[(c[X>>2]|0)+-8>>2]>>16,b[(c[K>>2]|0)+4>>1]|0)|0;c[Q>>2]=(c[Q>>2]|0)+(na+((_(c[(c[X>>2]|0)+-8>>2]&65535,b[(c[K>>2]|0)+4>>1]|0)|0)>>16));na=_(c[(c[X>>2]|0)+-12>>2]>>16,b[(c[K>>2]|0)+6>>1]|0)|0;c[Q>>2]=(c[Q>>2]|0)+(na+((_(c[(c[X>>2]|0)+-12>>2]&65535,b[(c[K>>2]|0)+6>>1]|0)|0)>>16));na=_(c[(c[X>>2]|0)+-16>>2]>>16,b[(c[K>>2]|0)+8>>1]|0)|0;c[Q>>2]=(c[Q>>2]|0)+(na+((_(c[(c[X>>2]|0)+-16>>2]&65535,b[(c[K>>2]|0)+8>>1]|0)|0)>>16));c[X>>2]=(c[X>>2]|0)+4}else c[Q>>2]=0;c[ia>>2]=c[c[Y>>2]>>2];c[ha>>2]=c[(c[ka>>2]|0)+4288>>2];c[(c[ka>>2]|0)+4288>>2]=c[ia>>2];c[U>>2]=c[D>>2]>>1;na=_(c[ia>>2]>>16,b[c[L>>2]>>1]|0)|0;c[U>>2]=(c[U>>2]|0)+(na+((_(c[ia>>2]&65535,b[c[L>>2]>>1]|0)|0)>>16));c[T>>2]=2;while(1){if((c[T>>2]|0)>=(c[D>>2]|0))break;c[ia>>2]=c[(c[ka>>2]|0)+4288+((c[T>>2]|0)-1<<2)>>2];c[(c[ka>>2]|0)+4288+((c[T>>2]|0)-1<<2)>>2]=c[ha>>2];na=_(c[ha>>2]>>16,b[(c[L>>2]|0)+((c[T>>2]|0)-1<<1)>>1]|0)|0;c[U>>2]=(c[U>>2]|0)+(na+((_(c[ha>>2]&65535,b[(c[L>>2]|0)+((c[T>>2]|0)-1<<1)>>1]|0)|0)>>16));c[ha>>2]=c[(c[ka>>2]|0)+4288+((c[T>>2]|0)+0<<2)>>2];c[(c[ka>>2]|0)+4288+((c[T>>2]|0)+0<<2)>>2]=c[ia>>2];na=_(c[ia>>2]>>16,b[(c[L>>2]|0)+(c[T>>2]<<1)>>1]|0)|0;c[U>>2]=(c[U>>2]|0)+(na+((_(c[ia>>2]&65535,b[(c[L>>2]|0)+(c[T>>2]<<1)>>1]|0)|0)>>16));c[T>>2]=(c[T>>2]|0)+2}c[(c[ka>>2]|0)+4288+((c[D>>2]|0)-1<<2)>>2]=c[ha>>2];na=_(c[ha>>2]>>16,b[(c[L>>2]|0)+((c[D>>2]|0)-1<<1)>>1]|0)|0;c[U>>2]=(c[U>>2]|0)+(na+((_(c[ha>>2]&65535,b[(c[L>>2]|0)+((c[D>>2]|0)-1<<1)>>1]|0)|0)>>16));c[U>>2]=c[U>>2]<<1;na=_(c[(c[ka>>2]|0)+4352>>2]>>16,(c[z>>2]&65535)<<16>>16)|0;c[U>>2]=(c[U>>2]|0)+(na+((_(c[(c[ka>>2]|0)+4352>>2]&65535,(c[z>>2]&65535)<<16>>16)|0)>>16));na=_(c[(c[ka>>2]|0)+1280+((c[(c[ka>>2]|0)+4364>>2]|0)-1<<2)>>2]>>16,(c[A>>2]&65535)<<16>>16)|0;c[V>>2]=na+((_(c[(c[ka>>2]|0)+1280+((c[(c[ka>>2]|0)+4364>>2]|0)-1<<2)>>2]&65535,(c[A>>2]&65535)<<16>>16)|0)>>16);na=(c[V>>2]|0)+(_(c[(c[ka>>2]|0)+4352>>2]>>16,c[A>>2]>>16)|0)|0;c[V>>2]=na+((_(c[(c[ka>>2]|0)+4352>>2]&65535,c[A>>2]>>16)|0)>>16);c[ha>>2]=(c[P>>2]<<2)-(c[U>>2]|0);c[ha>>2]=(c[ha>>2]|0)-(c[V>>2]|0);if((c[M>>2]|0)>0){na=_((c[c[ga>>2]>>2]|0)+(c[(c[ga>>2]|0)+-8>>2]|0)>>16,(c[y>>2]&65535)<<16>>16)|0;c[W>>2]=na+((_((c[c[ga>>2]>>2]|0)+(c[(c[ga>>2]|0)+-8>>2]|0)&65535,(c[y>>2]&65535)<<16>>16)|0)>>16);na=(c[W>>2]|0)+(_(c[(c[ga>>2]|0)+-4>>2]>>16,c[y>>2]>>16)|0)|0;c[W>>2]=na+((_(c[(c[ga>>2]|0)+-4>>2]&65535,c[y>>2]>>16)|0)>>16);c[W>>2]=c[W>>2]<<1;c[ga>>2]=(c[ga>>2]|0)+4;c[ia>>2]=(c[Q>>2]|0)-(c[W>>2]|0);c[ha>>2]=(c[ia>>2]|0)+(c[ha>>2]<<1);c[ha>>2]=(c[ha>>2]>>2)+1>>1}else c[ha>>2]=(c[ha>>2]>>1)+1>>1;c[ba>>2]=(c[(c[F>>2]|0)+(c[S>>2]<<2)>>2]|0)-(c[ha>>2]|0);if((c[(c[ka>>2]|0)+4368>>2]|0)<0)c[ba>>2]=0-(c[ba>>2]|0);if((c[ba>>2]|0)>30720)m=30720;else m=(c[ba>>2]|0)<-31744?-31744:c[ba>>2]|0;c[ba>>2]=m;c[$>>2]=(c[ba>>2]|0)-(c[C>>2]|0);c[Z>>2]=c[$>>2]>>10;m=c[Z>>2]|0;do if((c[Z>>2]|0)<=0){if(!m){c[$>>2]=c[C>>2];c[aa>>2]=(c[$>>2]|0)+944;c[ca>>2]=_((c[$>>2]&65535)<<16>>16,(c[B>>2]&65535)<<16>>16)|0;c[da>>2]=_((c[aa>>2]&65535)<<16>>16,(c[B>>2]&65535)<<16>>16)|0;break}if((c[Z>>2]|0)==-1){c[aa>>2]=c[C>>2];c[$>>2]=(c[aa>>2]|0)-944;c[ca>>2]=_((0-(c[$>>2]|0)&65535)<<16>>16,(c[B>>2]&65535)<<16>>16)|0;c[da>>2]=_((c[aa>>2]&65535)<<16>>16,(c[B>>2]&65535)<<16>>16)|0;break}else{c[$>>2]=(c[Z>>2]<<10)+80;c[$>>2]=(c[$>>2]|0)+(c[C>>2]|0);c[aa>>2]=(c[$>>2]|0)+1024;c[ca>>2]=_((0-(c[$>>2]|0)&65535)<<16>>16,(c[B>>2]&65535)<<16>>16)|0;c[da>>2]=_((0-(c[aa>>2]|0)&65535)<<16>>16,(c[B>>2]&65535)<<16>>16)|0;break}}else{c[$>>2]=(m<<10)-80;c[$>>2]=(c[$>>2]|0)+(c[C>>2]|0);c[aa>>2]=(c[$>>2]|0)+1024;c[ca>>2]=_((c[$>>2]&65535)<<16>>16,(c[B>>2]&65535)<<16>>16)|0;c[da>>2]=_((c[aa>>2]&65535)<<16>>16,(c[B>>2]&65535)<<16>>16)|0}while(0);c[ea>>2]=(c[ba>>2]|0)-(c[$>>2]|0);c[ca>>2]=(c[ca>>2]|0)+(_((c[ea>>2]&65535)<<16>>16,(c[ea>>2]&65535)<<16>>16)|0);c[ea>>2]=(c[ba>>2]|0)-(c[aa>>2]|0);c[da>>2]=(c[da>>2]|0)+(_((c[ea>>2]&65535)<<16>>16,(c[ea>>2]&65535)<<16>>16)|0);if((c[da>>2]|0)<(c[ca>>2]|0))c[$>>2]=c[aa>>2];a[(c[G>>2]|0)+(c[S>>2]|0)>>0]=(c[$>>2]>>9)+1>>1;c[R>>2]=c[$>>2]<<4;if((c[(c[ka>>2]|0)+4368>>2]|0)<0)c[R>>2]=0-(c[R>>2]|0);c[O>>2]=(c[R>>2]|0)+(c[Q>>2]<<1);c[ja>>2]=(c[O>>2]|0)+(c[P>>2]<<4);na=_(c[ja>>2]>>16,(c[N>>2]&65535)<<16>>16)|0;na=na+((_(c[ja>>2]&65535,(c[N>>2]&65535)<<16>>16)|0)>>16)|0;if(((na+(_(c[ja>>2]|0,(c[N>>2]>>15)+1>>1)|0)>>7)+1>>1|0)<=32767){na=_(c[ja>>2]>>16,(c[N>>2]&65535)<<16>>16)|0;na=na+((_(c[ja>>2]&65535,(c[N>>2]&65535)<<16>>16)|0)>>16)|0;if(((na+(_(c[ja>>2]|0,(c[N>>2]>>15)+1>>1)|0)>>7)+1>>1|0)<-32768)m=-32768;else{m=_(c[ja>>2]>>16,(c[N>>2]&65535)<<16>>16)|0;m=m+((_(c[ja>>2]&65535,(c[N>>2]&65535)<<16>>16)|0)>>16)|0;m=(m+(_(c[ja>>2]|0,(c[N>>2]>>15)+1>>1)|0)>>7)+1>>1}}else m=32767;b[(c[H>>2]|0)+(c[S>>2]<<1)>>1]=m;c[Y>>2]=(c[Y>>2]|0)+4;c[c[Y>>2]>>2]=c[ja>>2];c[fa>>2]=(c[ja>>2]|0)-(c[U>>2]<<2);c[(c[ka>>2]|0)+4352>>2]=c[fa>>2];c[(c[ka>>2]|0)+1280+(c[(c[ka>>2]|0)+4364>>2]<<2)>>2]=(c[fa>>2]|0)-(c[V>>2]<<2);c[(c[I>>2]|0)+(c[(c[ka>>2]|0)+4360>>2]<<2)>>2]=c[O>>2]<<1;na=(c[ka>>2]|0)+4364|0;c[na>>2]=(c[na>>2]|0)+1;na=(c[ka>>2]|0)+4360|0;c[na>>2]=(c[na>>2]|0)+1;c[(c[ka>>2]|0)+4368>>2]=(c[(c[ka>>2]|0)+4368>>2]|0)+(a[(c[G>>2]|0)+(c[S>>2]|0)>>0]|0);c[S>>2]=(c[S>>2]|0)+1}o=m+3840|0;m=(c[ka>>2]|0)+3840+(c[la>>2]<<2)|0;r=o+128|0;do{c[o>>2]=c[m>>2];o=o+4|0;m=m+4|0}while((o|0)<(r|0));i=ma;return}function se(d,e,f,g,h,j,k,l,m,n,o,p,q,r,s){d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;p=p|0;q=q|0;r=r|0;s=s|0;var t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0,ha=0;ca=i;i=i+272|0;S=ca+264|0;T=ca+260|0;x=ca+256|0;y=ca+252|0;W=ca+248|0;z=ca+244|0;A=ca+240|0;B=ca+236|0;C=ca+232|0;D=ca+228|0;t=ca+224|0;u=ca+220|0;U=ca+216|0;v=ca+212|0;w=ca+208|0;Z=ca+204|0;M=ca+200|0;N=ca+196|0;Q=ca+192|0;I=ca+188|0;K=ca+184|0;R=ca+180|0;$=ca+176|0;P=ca+172|0;Y=ca+168|0;F=ca+164|0;G=ca+160|0;E=ca+156|0;ba=ca+152|0;H=ca+148|0;O=ca+144|0;J=ca+140|0;X=ca+136|0;aa=ca+132|0;V=ca+128|0;L=ca;c[S>>2]=d;c[T>>2]=e;c[x>>2]=f;c[y>>2]=g;c[W>>2]=h;c[z>>2]=j;c[A>>2]=k;c[B>>2]=l;c[C>>2]=m;c[D>>2]=n;c[t>>2]=o;c[u>>2]=p;c[U>>2]=q;c[v>>2]=r;c[w>>2]=s;c[N>>2]=c[(c[T>>2]|0)+4356>>2];g=c[(c[S>>2]|0)+4652>>2]|0;c[V>>2]=ia()|0;k=i;i=i+((1*(g*1168|0)|0)+15&-16)|0;oj(k|0,0,(c[(c[S>>2]|0)+4652>>2]|0)*1168|0)|0;c[M>>2]=0;while(1){if((c[M>>2]|0)>=(c[(c[S>>2]|0)+4652>>2]|0))break;c[aa>>2]=k+((c[M>>2]|0)*1168|0);c[(c[aa>>2]|0)+1156>>2]=(c[M>>2]|0)+(a[(c[x>>2]|0)+34>>0]|0)&3;c[(c[aa>>2]|0)+1160>>2]=c[(c[aa>>2]|0)+1156>>2];c[(c[aa>>2]|0)+1164>>2]=0;c[(c[aa>>2]|0)+1152>>2]=c[(c[T>>2]|0)+4352>>2];c[(c[aa>>2]|0)+960>>2]=c[(c[T>>2]|0)+1280+((c[(c[S>>2]|0)+4616>>2]|0)-1<<2)>>2];l=c[aa>>2]|0;p=(c[T>>2]|0)+3840|0;m=l+128|0;do{c[l>>2]=c[p>>2];l=l+4|0;p=p+4|0}while((l|0)<(m|0));l=(c[aa>>2]|0)+1088|0;p=(c[T>>2]|0)+4288|0;m=l+64|0;do{c[l>>2]=c[p>>2];l=l+4|0;p=p+4|0}while((l|0)<(m|0));c[M>>2]=(c[M>>2]|0)+1}c[O>>2]=b[24558+(a[(c[x>>2]|0)+29>>0]>>1<<2)+(a[(c[x>>2]|0)+30>>0]<<1)>>1];c[P>>2]=0;c[Y>>2]=te(32,c[(c[S>>2]|0)+4612>>2]|0)|0;a:do if((a[(c[x>>2]|0)+29>>0]|0)!=2){if((c[N>>2]|0)>0)c[Y>>2]=te(c[Y>>2]|0,(c[N>>2]|0)-2-1|0)|0}else{c[M>>2]=0;while(1){if((c[M>>2]|0)>=(c[(c[S>>2]|0)+4604>>2]|0))break a;c[Y>>2]=te(c[Y>>2]|0,(c[(c[U>>2]|0)+(c[M>>2]<<2)>>2]|0)-2-1|0)|0;c[M>>2]=(c[M>>2]|0)+1}}while(0);if((a[(c[x>>2]|0)+31>>0]|0)==4)c[I>>2]=0;else c[I>>2]=1;l=i;i=i+((1*((c[(c[S>>2]|0)+4616>>2]|0)+(c[(c[S>>2]|0)+4608>>2]|0)<<2)|0)+15&-16)|0;p=i;i=i+((1*((c[(c[S>>2]|0)+4616>>2]|0)+(c[(c[S>>2]|0)+4608>>2]|0)<<1)|0)+15&-16)|0;m=i;i=i+((1*(c[(c[S>>2]|0)+4612>>2]<<2)|0)+15&-16)|0;c[ba>>2]=(c[T>>2]|0)+(c[(c[S>>2]|0)+4616>>2]<<1);c[(c[T>>2]|0)+4364>>2]=c[(c[S>>2]|0)+4616>>2];c[(c[T>>2]|0)+4360>>2]=c[(c[S>>2]|0)+4616>>2];c[R>>2]=0;c[M>>2]=0;while(1){if((c[M>>2]|0)>=(c[(c[S>>2]|0)+4604>>2]|0))break;c[F>>2]=(c[z>>2]|0)+((c[M>>2]>>1|1-(c[I>>2]|0))<<4<<1);c[G>>2]=(c[A>>2]|0)+((c[M>>2]|0)*5<<1);c[E>>2]=(c[B>>2]|0)+(c[M>>2]<<4<<1);c[H>>2]=c[(c[C>>2]|0)+(c[M>>2]<<2)>>2]>>2;c[H>>2]=c[H>>2]|c[(c[C>>2]|0)+(c[M>>2]<<2)>>2]>>1<<16;c[(c[T>>2]|0)+4376>>2]=0;if((a[(c[x>>2]|0)+29>>0]|0)==2?(c[N>>2]=c[(c[U>>2]|0)+(c[M>>2]<<2)>>2],(c[M>>2]&3-(c[I>>2]<<1)|0)==0):0){if((c[M>>2]|0)==2){c[J>>2]=c[k+1164>>2];c[K>>2]=0;c[Z>>2]=1;while(1){if((c[Z>>2]|0)>=(c[(c[S>>2]|0)+4652>>2]|0))break;if((c[k+((c[Z>>2]|0)*1168|0)+1164>>2]|0)<(c[J>>2]|0)){c[J>>2]=c[k+((c[Z>>2]|0)*1168|0)+1164>>2];c[K>>2]=c[Z>>2]}c[Z>>2]=(c[Z>>2]|0)+1}c[Z>>2]=0;while(1){if((c[Z>>2]|0)>=(c[(c[S>>2]|0)+4652>>2]|0))break;if((c[Z>>2]|0)!=(c[K>>2]|0)){g=k+((c[Z>>2]|0)*1168|0)+1164|0;c[g>>2]=(c[g>>2]|0)+134217727}c[Z>>2]=(c[Z>>2]|0)+1}c[aa>>2]=k+((c[K>>2]|0)*1168|0);c[$>>2]=(c[P>>2]|0)+(c[Y>>2]|0);c[Z>>2]=0;while(1){if((c[Z>>2]|0)>=(c[Y>>2]|0))break;c[$>>2]=(c[$>>2]|0)-1&31;a[(c[W>>2]|0)+((c[Z>>2]|0)-(c[Y>>2]|0))>>0]=(c[(c[aa>>2]|0)+576+(c[$>>2]<<2)>>2]>>9)+1>>1;g=_(c[(c[aa>>2]|0)+704+(c[$>>2]<<2)>>2]>>16,(c[(c[u>>2]|0)+4>>2]&65535)<<16>>16)|0;g=g+((_(c[(c[aa>>2]|0)+704+(c[$>>2]<<2)>>2]&65535,(c[(c[u>>2]|0)+4>>2]&65535)<<16>>16)|0)>>16)|0;if(((g+(_(c[(c[aa>>2]|0)+704+(c[$>>2]<<2)>>2]|0,(c[(c[u>>2]|0)+4>>2]>>15)+1>>1)|0)>>13)+1>>1|0)<=32767){g=_(c[(c[aa>>2]|0)+704+(c[$>>2]<<2)>>2]>>16,(c[(c[u>>2]|0)+4>>2]&65535)<<16>>16)|0;g=g+((_(c[(c[aa>>2]|0)+704+(c[$>>2]<<2)>>2]&65535,(c[(c[u>>2]|0)+4>>2]&65535)<<16>>16)|0)>>16)|0;if(((g+(_(c[(c[aa>>2]|0)+704+(c[$>>2]<<2)>>2]|0,(c[(c[u>>2]|0)+4>>2]>>15)+1>>1)|0)>>13)+1>>1|0)<-32768)o=-32768;else{o=_(c[(c[aa>>2]|0)+704+(c[$>>2]<<2)>>2]>>16,(c[(c[u>>2]|0)+4>>2]&65535)<<16>>16)|0;o=o+((_(c[(c[aa>>2]|0)+704+(c[$>>2]<<2)>>2]&65535,(c[(c[u>>2]|0)+4>>2]&65535)<<16>>16)|0)>>16)|0;o=(o+(_(c[(c[aa>>2]|0)+704+(c[$>>2]<<2)>>2]|0,(c[(c[u>>2]|0)+4>>2]>>15)+1>>1)|0)>>13)+1>>1}}else o=32767;b[(c[ba>>2]|0)+((c[Z>>2]|0)-(c[Y>>2]|0)<<1)>>1]=o;c[(c[T>>2]|0)+1280+((c[(c[T>>2]|0)+4364>>2]|0)-(c[Y>>2]|0)+(c[Z>>2]|0)<<2)>>2]=c[(c[aa>>2]|0)+960+(c[$>>2]<<2)>>2];c[Z>>2]=(c[Z>>2]|0)+1}c[R>>2]=0}c[Q>>2]=(c[(c[S>>2]|0)+4616>>2]|0)-(c[N>>2]|0)-(c[(c[S>>2]|0)+4664>>2]|0)-2;g=(c[Q>>2]|0)+(_(c[M>>2]|0,c[(c[S>>2]|0)+4612>>2]|0)|0)|0;Xf(p+(c[Q>>2]<<1)|0,(c[T>>2]|0)+(g<<1)|0,c[F>>2]|0,(c[(c[S>>2]|0)+4616>>2]|0)-(c[Q>>2]|0)|0,c[(c[S>>2]|0)+4664>>2]|0,c[(c[S>>2]|0)+5124>>2]|0);c[(c[T>>2]|0)+4360>>2]=c[(c[S>>2]|0)+4616>>2];c[(c[T>>2]|0)+4376>>2]=1}ue(c[S>>2]|0,c[T>>2]|0,k,c[y>>2]|0,m,p,l,c[M>>2]|0,c[(c[S>>2]|0)+4652>>2]|0,c[w>>2]|0,c[u>>2]|0,c[U>>2]|0,a[(c[x>>2]|0)+29>>0]|0,c[Y>>2]|0);ha=c[T>>2]|0;ga=a[(c[x>>2]|0)+29>>0]|0;fa=c[W>>2]|0;ea=c[ba>>2]|0;da=c[F>>2]|0;o=c[G>>2]|0;s=c[E>>2]|0;r=c[N>>2]|0;e=c[H>>2]|0;j=c[(c[D>>2]|0)+(c[M>>2]<<2)>>2]|0;n=c[(c[t>>2]|0)+(c[M>>2]<<2)>>2]|0;q=c[(c[u>>2]|0)+(c[M>>2]<<2)>>2]|0;d=c[v>>2]|0;f=c[O>>2]|0;h=c[(c[S>>2]|0)+4612>>2]|0;g=c[R>>2]|0;c[R>>2]=g+1;ye(ha,k,ga,m,fa,ea,l,L,da,o,s,r,e,j,n,q,d,f,h,g,c[(c[S>>2]|0)+4660>>2]|0,c[(c[S>>2]|0)+4664>>2]|0,c[(c[S>>2]|0)+4704>>2]|0,c[(c[S>>2]|0)+4652>>2]|0,P,c[Y>>2]|0);c[y>>2]=(c[y>>2]|0)+(c[(c[S>>2]|0)+4612>>2]<<2);c[W>>2]=(c[W>>2]|0)+(c[(c[S>>2]|0)+4612>>2]|0);c[ba>>2]=(c[ba>>2]|0)+(c[(c[S>>2]|0)+4612>>2]<<1);c[M>>2]=(c[M>>2]|0)+1}c[J>>2]=c[k+1164>>2];c[K>>2]=0;c[M>>2]=1;while(1){if((c[M>>2]|0)>=(c[(c[S>>2]|0)+4652>>2]|0))break;if((c[k+((c[M>>2]|0)*1168|0)+1164>>2]|0)<(c[J>>2]|0)){c[J>>2]=c[k+((c[M>>2]|0)*1168|0)+1164>>2];c[K>>2]=c[M>>2]}c[M>>2]=(c[M>>2]|0)+1}c[aa>>2]=k+((c[K>>2]|0)*1168|0);a[(c[x>>2]|0)+34>>0]=c[(c[aa>>2]|0)+1160>>2];c[$>>2]=(c[P>>2]|0)+(c[Y>>2]|0);c[X>>2]=c[(c[u>>2]|0)+((c[(c[S>>2]|0)+4604>>2]|0)-1<<2)>>2]>>6;c[Z>>2]=0;while(1){if((c[Z>>2]|0)>=(c[Y>>2]|0))break;c[$>>2]=(c[$>>2]|0)-1&31;a[(c[W>>2]|0)+((c[Z>>2]|0)-(c[Y>>2]|0))>>0]=(c[(c[aa>>2]|0)+576+(c[$>>2]<<2)>>2]>>9)+1>>1;ha=_(c[(c[aa>>2]|0)+704+(c[$>>2]<<2)>>2]>>16,(c[X>>2]&65535)<<16>>16)|0;ha=ha+((_(c[(c[aa>>2]|0)+704+(c[$>>2]<<2)>>2]&65535,(c[X>>2]&65535)<<16>>16)|0)>>16)|0;if(((ha+(_(c[(c[aa>>2]|0)+704+(c[$>>2]<<2)>>2]|0,(c[X>>2]>>15)+1>>1)|0)>>7)+1>>1|0)<=32767){ha=_(c[(c[aa>>2]|0)+704+(c[$>>2]<<2)>>2]>>16,(c[X>>2]&65535)<<16>>16)|0;ha=ha+((_(c[(c[aa>>2]|0)+704+(c[$>>2]<<2)>>2]&65535,(c[X>>2]&65535)<<16>>16)|0)>>16)|0;if(((ha+(_(c[(c[aa>>2]|0)+704+(c[$>>2]<<2)>>2]|0,(c[X>>2]>>15)+1>>1)|0)>>7)+1>>1|0)<-32768)l=-32768;else{l=_(c[(c[aa>>2]|0)+704+(c[$>>2]<<2)>>2]>>16,(c[X>>2]&65535)<<16>>16)|0;l=l+((_(c[(c[aa>>2]|0)+704+(c[$>>2]<<2)>>2]&65535,(c[X>>2]&65535)<<16>>16)|0)>>16)|0;l=(l+(_(c[(c[aa>>2]|0)+704+(c[$>>2]<<2)>>2]|0,(c[X>>2]>>15)+1>>1)|0)>>7)+1>>1}}else l=32767;b[(c[ba>>2]|0)+((c[Z>>2]|0)-(c[Y>>2]|0)<<1)>>1]=l;c[(c[T>>2]|0)+1280+((c[(c[T>>2]|0)+4364>>2]|0)-(c[Y>>2]|0)+(c[Z>>2]|0)<<2)>>2]=c[(c[aa>>2]|0)+960+(c[$>>2]<<2)>>2];c[Z>>2]=(c[Z>>2]|0)+1}l=(c[T>>2]|0)+3840|0;p=(c[aa>>2]|0)+(c[(c[S>>2]|0)+4612>>2]<<2)|0;m=l+128|0;do{c[l>>2]=c[p>>2];l=l+4|0;p=p+4|0}while((l|0)<(m|0));l=(c[T>>2]|0)+4288|0;p=(c[aa>>2]|0)+1088|0;m=l+64|0;do{c[l>>2]=c[p>>2];l=l+4|0;p=p+4|0}while((l|0)<(m|0));c[(c[T>>2]|0)+4352>>2]=c[(c[aa>>2]|0)+1152>>2];c[(c[T>>2]|0)+4356>>2]=c[(c[U>>2]|0)+((c[(c[S>>2]|0)+4604>>2]|0)-1<<2)>>2];qj(c[T>>2]|0,(c[T>>2]|0)+(c[(c[S>>2]|0)+4608>>2]<<1)|0,c[(c[S>>2]|0)+4616>>2]<<1|0)|0;qj((c[T>>2]|0)+1280|0,(c[T>>2]|0)+1280+(c[(c[S>>2]|0)+4608>>2]<<2)|0,c[(c[S>>2]|0)+4616>>2]<<2|0)|0;na(c[V>>2]|0);i=ca;return}function te(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;f=i;i=i+16|0;e=f+4|0;d=f;c[e>>2]=a;c[d>>2]=b;i=f;return ((c[e>>2]|0)<(c[d>>2]|0)?c[e>>2]|0:c[d>>2]|0)|0}function ue(a,d,e,f,g,h,j,k,l,m,n,o,p,q){a=a|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;p=p|0;q=q|0;var r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0;L=i;i=i+96|0;r=L+80|0;s=L+76|0;w=L+72|0;x=L+68|0;y=L+64|0;z=L+60|0;A=L+56|0;B=L+52|0;C=L+48|0;D=L+44|0;t=L+40|0;M=L+36|0;u=L+32|0;v=L+28|0;F=L+24|0;I=L+20|0;J=L+16|0;E=L+12|0;H=L+8|0;G=L+4|0;K=L;c[r>>2]=a;c[s>>2]=d;c[w>>2]=e;c[x>>2]=f;c[y>>2]=g;c[z>>2]=h;c[A>>2]=j;c[B>>2]=k;c[C>>2]=l;c[D>>2]=m;c[t>>2]=n;c[M>>2]=o;c[u>>2]=p;c[v>>2]=q;c[J>>2]=c[(c[M>>2]|0)+(c[B>>2]<<2)>>2];if((c[(c[t>>2]|0)+(c[B>>2]<<2)>>2]|0)>1)n=c[(c[t>>2]|0)+(c[B>>2]<<2)>>2]|0;else n=1;c[H>>2]=ve(n,47)|0;if((c[(c[t>>2]|0)+(c[B>>2]<<2)>>2]|0)!=(c[(c[s>>2]|0)+4372>>2]|0))c[E>>2]=xe(c[(c[s>>2]|0)+4372>>2]|0,c[(c[t>>2]|0)+(c[B>>2]<<2)>>2]|0,16)|0;else c[E>>2]=65536;c[G>>2]=(c[H>>2]>>7)+1>>1;c[F>>2]=0;while(1){if((c[F>>2]|0)>=(c[(c[r>>2]|0)+4612>>2]|0))break;M=_(c[(c[x>>2]|0)+(c[F>>2]<<2)>>2]>>16,(c[G>>2]&65535)<<16>>16)|0;M=M+((_(c[(c[x>>2]|0)+(c[F>>2]<<2)>>2]&65535,(c[G>>2]&65535)<<16>>16)|0)>>16)|0;M=M+(_(c[(c[x>>2]|0)+(c[F>>2]<<2)>>2]|0,(c[G>>2]>>15)+1>>1)|0)|0;c[(c[y>>2]|0)+(c[F>>2]<<2)>>2]=M;c[F>>2]=(c[F>>2]|0)+1}c[(c[s>>2]|0)+4372>>2]=c[(c[t>>2]|0)+(c[B>>2]<<2)>>2];a:do if(c[(c[s>>2]|0)+4376>>2]|0){if(!(c[B>>2]|0)){M=_(c[H>>2]>>16,(c[D>>2]&65535)<<16>>16)|0;c[H>>2]=M+((_(c[H>>2]&65535,(c[D>>2]&65535)<<16>>16)|0)>>16)<<2}c[F>>2]=(c[(c[s>>2]|0)+4360>>2]|0)-(c[J>>2]|0)-2;while(1){if((c[F>>2]|0)>=(c[(c[s>>2]|0)+4360>>2]|0))break a;M=_(c[H>>2]>>16,b[(c[z>>2]|0)+(c[F>>2]<<1)>>1]|0)|0;M=M+((_(c[H>>2]&65535,b[(c[z>>2]|0)+(c[F>>2]<<1)>>1]|0)|0)>>16)|0;c[(c[A>>2]|0)+(c[F>>2]<<2)>>2]=M;c[F>>2]=(c[F>>2]|0)+1}}while(0);if((c[E>>2]|0)==65536){i=L;return}c[F>>2]=(c[(c[s>>2]|0)+4364>>2]|0)-(c[(c[r>>2]|0)+4616>>2]|0);while(1){if((c[F>>2]|0)>=(c[(c[s>>2]|0)+4364>>2]|0))break;M=_(c[E>>2]>>16,(c[(c[s>>2]|0)+1280+(c[F>>2]<<2)>>2]&65535)<<16>>16)|0;M=M+((_(c[E>>2]&65535,(c[(c[s>>2]|0)+1280+(c[F>>2]<<2)>>2]&65535)<<16>>16)|0)>>16)|0;M=M+(_(c[E>>2]|0,(c[(c[s>>2]|0)+1280+(c[F>>2]<<2)>>2]>>15)+1>>1)|0)|0;c[(c[s>>2]|0)+1280+(c[F>>2]<<2)>>2]=M;c[F>>2]=(c[F>>2]|0)+1}b:do if((c[u>>2]|0)==2?(c[(c[s>>2]|0)+4376>>2]|0)==0:0){c[F>>2]=(c[(c[s>>2]|0)+4360>>2]|0)-(c[J>>2]|0)-2;while(1){if((c[F>>2]|0)>=((c[(c[s>>2]|0)+4360>>2]|0)-(c[v>>2]|0)|0))break b;M=_(c[E>>2]>>16,(c[(c[A>>2]|0)+(c[F>>2]<<2)>>2]&65535)<<16>>16)|0;M=M+((_(c[E>>2]&65535,(c[(c[A>>2]|0)+(c[F>>2]<<2)>>2]&65535)<<16>>16)|0)>>16)|0;M=M+(_(c[E>>2]|0,(c[(c[A>>2]|0)+(c[F>>2]<<2)>>2]>>15)+1>>1)|0)|0;c[(c[A>>2]|0)+(c[F>>2]<<2)>>2]=M;c[F>>2]=(c[F>>2]|0)+1}}while(0);c[I>>2]=0;while(1){if((c[I>>2]|0)>=(c[C>>2]|0))break;c[K>>2]=(c[w>>2]|0)+((c[I>>2]|0)*1168|0);M=_(c[E>>2]>>16,(c[(c[K>>2]|0)+1152>>2]&65535)<<16>>16)|0;M=M+((_(c[E>>2]&65535,(c[(c[K>>2]|0)+1152>>2]&65535)<<16>>16)|0)>>16)|0;M=M+(_(c[E>>2]|0,(c[(c[K>>2]|0)+1152>>2]>>15)+1>>1)|0)|0;c[(c[K>>2]|0)+1152>>2]=M;c[F>>2]=0;while(1){if((c[F>>2]|0)>=32)break;M=_(c[E>>2]>>16,(c[(c[K>>2]|0)+(c[F>>2]<<2)>>2]&65535)<<16>>16)|0;M=M+((_(c[E>>2]&65535,(c[(c[K>>2]|0)+(c[F>>2]<<2)>>2]&65535)<<16>>16)|0)>>16)|0;M=M+(_(c[E>>2]|0,(c[(c[K>>2]|0)+(c[F>>2]<<2)>>2]>>15)+1>>1)|0)|0;c[(c[K>>2]|0)+(c[F>>2]<<2)>>2]=M;c[F>>2]=(c[F>>2]|0)+1}c[F>>2]=0;while(1){if((c[F>>2]|0)>=16)break;M=_(c[E>>2]>>16,(c[(c[K>>2]|0)+1088+(c[F>>2]<<2)>>2]&65535)<<16>>16)|0;M=M+((_(c[E>>2]&65535,(c[(c[K>>2]|0)+1088+(c[F>>2]<<2)>>2]&65535)<<16>>16)|0)>>16)|0;M=M+(_(c[E>>2]|0,(c[(c[K>>2]|0)+1088+(c[F>>2]<<2)>>2]>>15)+1>>1)|0)|0;c[(c[K>>2]|0)+1088+(c[F>>2]<<2)>>2]=M;c[F>>2]=(c[F>>2]|0)+1}c[F>>2]=0;while(1){if((c[F>>2]|0)>=32)break;M=_(c[E>>2]>>16,(c[(c[K>>2]|0)+832+(c[F>>2]<<2)>>2]&65535)<<16>>16)|0;M=M+((_(c[E>>2]&65535,(c[(c[K>>2]|0)+832+(c[F>>2]<<2)>>2]&65535)<<16>>16)|0)>>16)|0;M=M+(_(c[E>>2]|0,(c[(c[K>>2]|0)+832+(c[F>>2]<<2)>>2]>>15)+1>>1)|0)|0;c[(c[K>>2]|0)+832+(c[F>>2]<<2)>>2]=M;M=_(c[E>>2]>>16,(c[(c[K>>2]|0)+960+(c[F>>2]<<2)>>2]&65535)<<16>>16)|0;M=M+((_(c[E>>2]&65535,(c[(c[K>>2]|0)+960+(c[F>>2]<<2)>>2]&65535)<<16>>16)|0)>>16)|0;M=M+(_(c[E>>2]|0,(c[(c[K>>2]|0)+960+(c[F>>2]<<2)>>2]>>15)+1>>1)|0)|0;c[(c[K>>2]|0)+960+(c[F>>2]<<2)>>2]=M;c[F>>2]=(c[F>>2]|0)+1}c[I>>2]=(c[I>>2]|0)+1}i=L;return}function ve(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0;h=i;i=i+48|0;f=h+32|0;n=h+28|0;d=h+24|0;j=h+20|0;g=h+16|0;k=h+12|0;m=h+8|0;l=h+4|0;e=h;c[n>>2]=a;c[d>>2]=b;b=c[n>>2]|0;c[j>>2]=(we((c[n>>2]|0)>0?b:0-b|0)|0)-1;c[m>>2]=c[n>>2]<<c[j>>2];c[k>>2]=536870911/(c[m>>2]>>16|0)|0;c[e>>2]=c[k>>2]<<16;b=_(c[m>>2]>>16,(c[k>>2]&65535)<<16>>16)|0;c[l>>2]=536870912-(b+((_(c[m>>2]&65535,(c[k>>2]&65535)<<16>>16)|0)>>16))<<3;b=_(c[l>>2]>>16,(c[k>>2]&65535)<<16>>16)|0;b=(c[e>>2]|0)+(b+((_(c[l>>2]&65535,(c[k>>2]&65535)<<16>>16)|0)>>16))|0;c[e>>2]=b+(_(c[l>>2]|0,(c[k>>2]>>15)+1>>1)|0);c[g>>2]=61-(c[j>>2]|0)-(c[d>>2]|0);b=c[g>>2]|0;if((c[g>>2]|0)>0)if((b|0)<32){c[f>>2]=c[e>>2]>>c[g>>2];n=c[f>>2]|0;i=h;return n|0}else{c[f>>2]=0;n=c[f>>2]|0;i=h;return n|0}a=c[e>>2]|0;d=0-(c[g>>2]|0)|0;do if((-2147483648>>0-b|0)>(2147483647>>0-(c[g>>2]|0)|0)){if((a|0)>(-2147483648>>d|0)){b=-2147483648>>0-(c[g>>2]|0);break}if((c[e>>2]|0)<(2147483647>>0-(c[g>>2]|0)|0)){b=2147483647>>0-(c[g>>2]|0);break}else{b=c[e>>2]|0;break}}else{if((a|0)>(2147483647>>d|0)){b=2147483647>>0-(c[g>>2]|0);break}if((c[e>>2]|0)<(-2147483648>>0-(c[g>>2]|0)|0)){b=-2147483648>>0-(c[g>>2]|0);break}else{b=c[e>>2]|0;break}}while(0);c[f>>2]=b<<0-(c[g>>2]|0);n=c[f>>2]|0;i=h;return n|0}function we(a){a=a|0;var b=0,d=0;d=i;i=i+16|0;b=d;c[b>>2]=a;if(!(c[b>>2]|0)){a=32;i=d;return a|0}a=32-(32-(aa(c[b>>2]|0)|0))|0;i=d;return a|0}function xe(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;h=i;i=i+48|0;f=h+40|0;q=h+36|0;p=h+32|0;j=h+28|0;l=h+24|0;k=h+20|0;g=h+16|0;m=h+12|0;n=h+8|0;o=h+4|0;e=h;c[q>>2]=a;c[p>>2]=b;c[j>>2]=d;b=c[q>>2]|0;c[l>>2]=(we((c[q>>2]|0)>0?b:0-b|0)|0)-1;c[n>>2]=c[q>>2]<<c[l>>2];b=c[p>>2]|0;c[k>>2]=(we((c[p>>2]|0)>0?b:0-b|0)|0)-1;c[o>>2]=c[p>>2]<<c[k>>2];c[m>>2]=536870911/(c[o>>2]>>16|0)|0;b=_(c[n>>2]>>16,(c[m>>2]&65535)<<16>>16)|0;c[e>>2]=b+((_(c[n>>2]&65535,(c[m>>2]&65535)<<16>>16)|0)>>16);b=c[n>>2]|0;a=c[o>>2]|0;d=c[e>>2]|0;d=yj(a|0,((a|0)<0)<<31>>31|0,d|0,((d|0)<0)<<31>>31|0)|0;d=nj(d|0,C|0,32)|0;c[n>>2]=b-(d<<3);d=_(c[n>>2]>>16,(c[m>>2]&65535)<<16>>16)|0;c[e>>2]=(c[e>>2]|0)+(d+((_(c[n>>2]&65535,(c[m>>2]&65535)<<16>>16)|0)>>16));c[g>>2]=29+(c[l>>2]|0)-(c[k>>2]|0)-(c[j>>2]|0);d=c[g>>2]|0;if((c[g>>2]|0)>=0)if((d|0)<32){c[f>>2]=c[e>>2]>>c[g>>2];q=c[f>>2]|0;i=h;return q|0}else{c[f>>2]=0;q=c[f>>2]|0;i=h;return q|0}a=c[e>>2]|0;b=0-(c[g>>2]|0)|0;do if((-2147483648>>0-d|0)>(2147483647>>0-(c[g>>2]|0)|0)){if((a|0)>(-2147483648>>b|0)){d=-2147483648>>0-(c[g>>2]|0);break}if((c[e>>2]|0)<(2147483647>>0-(c[g>>2]|0)|0)){d=2147483647>>0-(c[g>>2]|0);break}else{d=c[e>>2]|0;break}}else{if((a|0)>(2147483647>>b|0)){d=2147483647>>0-(c[g>>2]|0);break}if((c[e>>2]|0)<(-2147483648>>0-(c[g>>2]|0)|0)){d=-2147483648>>0-(c[g>>2]|0);break}else{d=c[e>>2]|0;break}}while(0);c[f>>2]=d<<0-(c[g>>2]|0);q=c[f>>2]|0;i=h;return q|0}
+ function Uh(a,b,d){a=a|0;b=+b;d=d|0;var e=0,f=0,h=0,j=0,k=0,l=0;l=i;i=i+32|0;e=l+16|0;f=l+12|0;h=l+8|0;k=l+4|0;j=l;c[e>>2]=a;g[f>>2]=b;c[h>>2]=d;c[j>>2]=c[h>>2]&65532;c[k>>2]=0;while(1){if((c[k>>2]|0)>=(c[j>>2]|0))break;d=(c[e>>2]|0)+((c[k>>2]|0)+0<<2)|0;g[d>>2]=+g[d>>2]*+g[f>>2];d=(c[e>>2]|0)+((c[k>>2]|0)+1<<2)|0;g[d>>2]=+g[d>>2]*+g[f>>2];d=(c[e>>2]|0)+((c[k>>2]|0)+2<<2)|0;g[d>>2]=+g[d>>2]*+g[f>>2];d=(c[e>>2]|0)+((c[k>>2]|0)+3<<2)|0;g[d>>2]=+g[d>>2]*+g[f>>2];c[k>>2]=(c[k>>2]|0)+4}while(1){if((c[k>>2]|0)>=(c[h>>2]|0))break;j=(c[e>>2]|0)+(c[k>>2]<<2)|0;g[j>>2]=+g[j>>2]*+g[f>>2];c[k>>2]=(c[k>>2]|0)+1}i=l;return}function Vh(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0.0;p=i;i=i+176|0;e=p+168|0;f=p+164|0;h=p+160|0;m=p+156|0;n=p+152|0;j=p+16|0;k=p+8|0;l=p+4|0;o=p;c[e>>2]=a;c[f>>2]=b;c[h>>2]=d;c[m>>2]=0;while(1){if((c[m>>2]|0)>=((c[h>>2]|0)+1|0))break;q=+g[(c[f>>2]|0)+(c[m>>2]<<2)>>2];g[j+(c[m>>2]<<3)+4>>2]=q;g[j+(c[m>>2]<<3)>>2]=q;c[m>>2]=(c[m>>2]|0)+1}c[m>>2]=0;while(1){if((c[m>>2]|0)>=(c[h>>2]|0))break;g[o>>2]=-+g[j+((c[m>>2]|0)+1<<3)>>2]/(+g[j+4>>2]>9.999999717180685e-10?+g[j+4>>2]:9.999999717180685e-10);g[(c[e>>2]|0)+(c[m>>2]<<2)>>2]=+g[o>>2];c[n>>2]=0;while(1){if((c[n>>2]|0)>=((c[h>>2]|0)-(c[m>>2]|0)|0))break;g[k>>2]=+g[j+((c[n>>2]|0)+(c[m>>2]|0)+1<<3)>>2];g[l>>2]=+g[j+(c[n>>2]<<3)+4>>2];g[j+((c[n>>2]|0)+(c[m>>2]|0)+1<<3)>>2]=+g[k>>2]+ +g[l>>2]*+g[o>>2];g[j+(c[n>>2]<<3)+4>>2]=+g[l>>2]+ +g[k>>2]*+g[o>>2];c[n>>2]=(c[n>>2]|0)+1}c[m>>2]=(c[m>>2]|0)+1}i=p;return +(+g[j+4>>2])}function Wh(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0;o=i;i=i+32|0;f=o+24|0;h=o+20|0;j=o+16|0;k=o+12|0;n=o+8|0;l=o+4|0;m=o;c[f>>2]=a;c[h>>2]=b;c[j>>2]=d;c[k>>2]=e;c[l>>2]=0;while(1){if((c[l>>2]|0)>=(c[k>>2]|0))break;c[(c[h>>2]|0)+(c[l>>2]<<2)>>2]=c[l>>2];c[l>>2]=(c[l>>2]|0)+1}c[l>>2]=1;while(1){if((c[l>>2]|0)>=(c[k>>2]|0))break;g[n>>2]=+g[(c[f>>2]|0)+(c[l>>2]<<2)>>2];c[m>>2]=(c[l>>2]|0)-1;while(1){if((c[m>>2]|0)<0)break;if(!(+g[n>>2]>+g[(c[f>>2]|0)+(c[m>>2]<<2)>>2]))break;g[(c[f>>2]|0)+((c[m>>2]|0)+1<<2)>>2]=+g[(c[f>>2]|0)+(c[m>>2]<<2)>>2];c[(c[h>>2]|0)+((c[m>>2]|0)+1<<2)>>2]=c[(c[h>>2]|0)+(c[m>>2]<<2)>>2];c[m>>2]=(c[m>>2]|0)+-1}g[(c[f>>2]|0)+((c[m>>2]|0)+1<<2)>>2]=+g[n>>2];c[(c[h>>2]|0)+((c[m>>2]|0)+1<<2)>>2]=c[l>>2];c[l>>2]=(c[l>>2]|0)+1}c[l>>2]=c[k>>2];while(1){if((c[l>>2]|0)>=(c[j>>2]|0))break;g[n>>2]=+g[(c[f>>2]|0)+(c[l>>2]<<2)>>2];if(+g[n>>2]>+g[(c[f>>2]|0)+((c[k>>2]|0)-1<<2)>>2]){c[m>>2]=(c[k>>2]|0)-2;while(1){if((c[m>>2]|0)<0)break;if(!(+g[n>>2]>+g[(c[f>>2]|0)+(c[m>>2]<<2)>>2]))break;g[(c[f>>2]|0)+((c[m>>2]|0)+1<<2)>>2]=+g[(c[f>>2]|0)+(c[m>>2]<<2)>>2];c[(c[h>>2]|0)+((c[m>>2]|0)+1<<2)>>2]=c[(c[h>>2]|0)+(c[m>>2]<<2)>>2];c[m>>2]=(c[m>>2]|0)+-1}g[(c[f>>2]|0)+((c[m>>2]|0)+1<<2)>>2]=+g[n>>2];c[(c[h>>2]|0)+((c[m>>2]|0)+1<<2)>>2]=c[l>>2]}c[l>>2]=(c[l>>2]|0)+1}i=o;return}function Xh(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0.0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0;A=i;i=i+80|0;h=A+64|0;j=A+60|0;k=A+56|0;l=A+52|0;n=A+48|0;r=A+44|0;x=A+40|0;m=A+36|0;y=A+32|0;o=A+28|0;w=A+24|0;q=A+20|0;s=A+16|0;v=A+12|0;u=A+8|0;p=A+4|0;t=A;c[h>>2]=a;c[j>>2]=b;c[k>>2]=d;c[l>>2]=e;if(!((((c[k>>2]|0)<1|(c[j>>2]|0)<1)^1)&(c[h>>2]|0)!=0&(c[l>>2]|0)!=0)){i=A;return}c[r>>2]=0;while(1){if((c[r>>2]|0)>=(_(c[j>>2]|0,c[k>>2]|0)|0))break;if(2.0<+g[(c[h>>2]|0)+(c[r>>2]<<2)>>2])f=2.0;else f=+g[(c[h>>2]|0)+(c[r>>2]<<2)>>2];if(!(-2.0>f))if(2.0<+g[(c[h>>2]|0)+(c[r>>2]<<2)>>2])f=2.0;else f=+g[(c[h>>2]|0)+(c[r>>2]<<2)>>2];else f=-2.0;g[(c[h>>2]|0)+(c[r>>2]<<2)>>2]=f;c[r>>2]=(c[r>>2]|0)+1}c[n>>2]=0;while(1){if((c[n>>2]|0)>=(c[k>>2]|0))break;c[x>>2]=(c[h>>2]|0)+(c[n>>2]<<2);g[m>>2]=+g[(c[l>>2]|0)+(c[n>>2]<<2)>>2];c[r>>2]=0;while(1){if((c[r>>2]|0)>=(c[j>>2]|0))break;e=_(c[r>>2]|0,c[k>>2]|0)|0;if(+g[(c[x>>2]|0)+(e<<2)>>2]*+g[m>>2]>=0.0)break;d=_(c[r>>2]|0,c[k>>2]|0)|0;b=_(c[r>>2]|0,c[k>>2]|0)|0;a=_(c[r>>2]|0,c[k>>2]|0)|0;e=_(c[r>>2]|0,c[k>>2]|0)|0;g[(c[x>>2]|0)+(e<<2)>>2]=+g[(c[x>>2]|0)+(d<<2)>>2]+ +g[m>>2]*+g[(c[x>>2]|0)+(b<<2)>>2]*+g[(c[x>>2]|0)+(a<<2)>>2];c[r>>2]=(c[r>>2]|0)+1}c[o>>2]=0;g[y>>2]=+g[c[x>>2]>>2];do{c[v>>2]=0;c[r>>2]=c[o>>2];while(1){if((c[r>>2]|0)>=(c[j>>2]|0))break;e=_(c[r>>2]|0,c[k>>2]|0)|0;if(+g[(c[x>>2]|0)+(e<<2)>>2]>1.0)break;e=_(c[r>>2]|0,c[k>>2]|0)|0;if(+g[(c[x>>2]|0)+(e<<2)>>2]<-1.0)break;c[r>>2]=(c[r>>2]|0)+1}if((c[r>>2]|0)==(c[j>>2]|0)){z=23;break}c[u>>2]=c[r>>2];e=c[r>>2]|0;c[q>>2]=e;c[w>>2]=e;e=_(c[r>>2]|0,c[k>>2]|0)|0;g[s>>2]=+N(+(+g[(c[x>>2]|0)+(e<<2)>>2]));while(1){if((c[w>>2]|0)<=0)break;a=_(c[r>>2]|0,c[k>>2]|0)|0;e=_((c[w>>2]|0)-1|0,c[k>>2]|0)|0;if(!(+g[(c[x>>2]|0)+(a<<2)>>2]*+g[(c[x>>2]|0)+(e<<2)>>2]>=0.0))break;c[w>>2]=(c[w>>2]|0)+-1}while(1){if((c[q>>2]|0)>=(c[j>>2]|0))break;a=_(c[r>>2]|0,c[k>>2]|0)|0;e=_(c[q>>2]|0,c[k>>2]|0)|0;if(!(+g[(c[x>>2]|0)+(a<<2)>>2]*+g[(c[x>>2]|0)+(e<<2)>>2]>=0.0))break;e=_(c[q>>2]|0,c[k>>2]|0)|0;f=+N(+(+g[(c[x>>2]|0)+(e<<2)>>2]));if(f>+g[s>>2]){e=_(c[q>>2]|0,c[k>>2]|0)|0;g[s>>2]=+N(+(+g[(c[x>>2]|0)+(e<<2)>>2]));c[u>>2]=c[q>>2]}c[q>>2]=(c[q>>2]|0)+1}if(!(c[w>>2]|0)){d=_(c[r>>2]|0,c[k>>2]|0)|0;d=+g[(c[x>>2]|0)+(d<<2)>>2]*+g[c[x>>2]>>2]>=0.0}else d=0;c[v>>2]=d&1;g[m>>2]=(+g[s>>2]-1.0)/(+g[s>>2]*+g[s>>2]);e=_(c[r>>2]|0,c[k>>2]|0)|0;if(+g[(c[x>>2]|0)+(e<<2)>>2]>0.0)g[m>>2]=-+g[m>>2];c[r>>2]=c[w>>2];while(1){if((c[r>>2]|0)>=(c[q>>2]|0))break;d=_(c[r>>2]|0,c[k>>2]|0)|0;b=_(c[r>>2]|0,c[k>>2]|0)|0;a=_(c[r>>2]|0,c[k>>2]|0)|0;e=_(c[r>>2]|0,c[k>>2]|0)|0;g[(c[x>>2]|0)+(e<<2)>>2]=+g[(c[x>>2]|0)+(d<<2)>>2]+ +g[m>>2]*+g[(c[x>>2]|0)+(b<<2)>>2]*+g[(c[x>>2]|0)+(a<<2)>>2];c[r>>2]=(c[r>>2]|0)+1}a:do if((c[v>>2]|0)!=0&(c[u>>2]|0)>=2){g[t>>2]=+g[y>>2]-+g[c[x>>2]>>2];g[p>>2]=+g[t>>2]/+(c[u>>2]|0);c[r>>2]=c[o>>2];while(1){if((c[r>>2]|0)>=(c[u>>2]|0))break a;g[t>>2]=+g[t>>2]-+g[p>>2];e=_(c[r>>2]|0,c[k>>2]|0)|0;e=(c[x>>2]|0)+(e<<2)|0;g[e>>2]=+g[e>>2]+ +g[t>>2];e=_(c[r>>2]|0,c[k>>2]|0)|0;if(1.0<+g[(c[x>>2]|0)+(e<<2)>>2])f=1.0;else{e=_(c[r>>2]|0,c[k>>2]|0)|0;f=+g[(c[x>>2]|0)+(e<<2)>>2]}if(!(-1.0>f)){e=_(c[r>>2]|0,c[k>>2]|0)|0;if(1.0<+g[(c[x>>2]|0)+(e<<2)>>2])f=1.0;else{e=_(c[r>>2]|0,c[k>>2]|0)|0;f=+g[(c[x>>2]|0)+(e<<2)>>2]}}else f=-1.0;e=_(c[r>>2]|0,c[k>>2]|0)|0;g[(c[x>>2]|0)+(e<<2)>>2]=f;c[r>>2]=(c[r>>2]|0)+1}}while(0);c[o>>2]=c[q>>2]}while((c[o>>2]|0)!=(c[j>>2]|0));if((z|0)==23){z=0;g[m>>2]=0.0}g[(c[l>>2]|0)+(c[n>>2]<<2)>>2]=+g[m>>2];c[n>>2]=(c[n>>2]|0)+1}i=A;return}function Yh(b,e){b=b|0;e=e|0;var f=0,g=0,h=0,j=0;j=i;i=i+16|0;f=j+8|0;g=j+4|0;h=j;c[g>>2]=b;c[h>>2]=e;e=c[g>>2]|0;if((c[g>>2]|0)<252){a[c[h>>2]>>0]=e;c[f>>2]=1;h=c[f>>2]|0;i=j;return h|0}else{a[c[h>>2]>>0]=252+(e&3);a[(c[h>>2]|0)+1>>0]=(c[g>>2]|0)-(d[c[h>>2]>>0]|0)>>2;c[f>>2]=2;h=c[f>>2]|0;i=j;return h|0}return 0}function Zh(a,b){a=a|0;b=b|0;var e=0,f=0,g=0,h=0;h=i;i=i+16|0;e=h+8|0;f=h+4|0;g=h;c[e>>2]=a;c[f>>2]=b;b=d[c[e>>2]>>0]|0;if((d[c[e>>2]>>0]|0)&128|0){c[g>>2]=b>>3&3;c[g>>2]=(c[f>>2]<<c[g>>2]|0)/400|0;g=c[g>>2]|0;i=h;return g|0}a=d[c[e>>2]>>0]|0;if((b&96|0)!=96){c[g>>2]=a>>3&3;b=c[f>>2]|0;if((c[g>>2]|0)==3){c[g>>2]=(b*60|0)/1e3|0;g=c[g>>2]|0;i=h;return g|0}else{c[g>>2]=(b<<c[g>>2]|0)/100|0;g=c[g>>2]|0;i=h;return g|0}}else{b=c[f>>2]|0;if(a&8|0)b=(b|0)/50|0;else b=(b|0)/100|0;c[g>>2]=b;g=c[g>>2]|0;i=h;return g|0}return 0}function _h(e,f,g,h,j,k,l,m){e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;var n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0;J=i;i=i+80|0;n=J+72|0;o=J+68|0;p=J+64|0;q=J+60|0;r=J+56|0;s=J+52|0;t=J+48|0;u=J+44|0;v=J+40|0;C=J+36|0;w=J+32|0;z=J+28|0;x=J+24|0;y=J+77|0;H=J+76|0;B=J+20|0;D=J+16|0;F=J+12|0;A=J+8|0;E=J+4|0;G=J;c[o>>2]=e;c[p>>2]=f;c[q>>2]=g;c[r>>2]=h;c[s>>2]=j;c[t>>2]=k;c[u>>2]=l;c[v>>2]=m;c[F>>2]=0;c[A>>2]=c[o>>2];if(!(c[t>>2]|0)){c[n>>2]=-1;I=c[n>>2]|0;i=J;return I|0}c[B>>2]=Zh(c[o>>2]|0,48e3)|0;c[x>>2]=0;k=c[o>>2]|0;c[o>>2]=k+1;a[H>>0]=a[k>>0]|0;c[p>>2]=(c[p>>2]|0)+-1;c[D>>2]=c[p>>2];a:do switch(d[H>>0]&3|0){case 0:{c[z>>2]=1;break}case 1:{c[z>>2]=2;c[x>>2]=1;if(!(c[q>>2]|0)){if(!(c[p>>2]&1)){c[D>>2]=(c[p>>2]|0)/2|0;b[c[t>>2]>>1]=c[D>>2];break a}c[n>>2]=-4;I=c[n>>2]|0;i=J;return I|0}break}case 2:{c[z>>2]=2;c[w>>2]=$h(c[o>>2]|0,c[p>>2]|0,c[t>>2]|0)|0;c[p>>2]=(c[p>>2]|0)-(c[w>>2]|0);if((b[c[t>>2]>>1]|0)>=0?(b[c[t>>2]>>1]|0)<=(c[p>>2]|0):0){c[o>>2]=(c[o>>2]|0)+(c[w>>2]|0);c[D>>2]=(c[p>>2]|0)-(b[c[t>>2]>>1]|0);break a}c[n>>2]=-4;I=c[n>>2]|0;i=J;return I|0}default:{if((c[p>>2]|0)<1){c[n>>2]=-4;I=c[n>>2]|0;i=J;return I|0}k=c[o>>2]|0;c[o>>2]=k+1;a[y>>0]=a[k>>0]|0;c[z>>2]=d[y>>0]&63;if((c[z>>2]|0)>0?(_(c[B>>2]|0,c[z>>2]|0)|0)<=5760:0){c[p>>2]=(c[p>>2]|0)+-1;b:do if(d[y>>0]&64|0){while(1){if((c[p>>2]|0)<=0)break;k=c[o>>2]|0;c[o>>2]=k+1;c[E>>2]=d[k>>0];c[p>>2]=(c[p>>2]|0)+-1;c[G>>2]=(c[E>>2]|0)==255?254:c[E>>2]|0;c[p>>2]=(c[p>>2]|0)-(c[G>>2]|0);c[F>>2]=(c[F>>2]|0)+(c[G>>2]|0);if((c[E>>2]|0)!=255)break b}c[n>>2]=-4;I=c[n>>2]|0;i=J;return I|0}while(0);if((c[p>>2]|0)<0){c[n>>2]=-4;I=c[n>>2]|0;i=J;return I|0}c[x>>2]=((d[y>>0]&128|0)!=0^1)&1;if(c[x>>2]|0){if(c[q>>2]|0)break a;c[D>>2]=(c[p>>2]|0)/(c[z>>2]|0)|0;I=_(c[D>>2]|0,c[z>>2]|0)|0;if((I|0)!=(c[p>>2]|0)){c[n>>2]=-4;I=c[n>>2]|0;i=J;return I|0}c[C>>2]=0;while(1){if((c[C>>2]|0)>=((c[z>>2]|0)-1|0))break a;b[(c[t>>2]|0)+(c[C>>2]<<1)>>1]=c[D>>2];c[C>>2]=(c[C>>2]|0)+1}}c[D>>2]=c[p>>2];c[C>>2]=0;while(1){if((c[C>>2]|0)>=((c[z>>2]|0)-1|0))break;c[w>>2]=$h(c[o>>2]|0,c[p>>2]|0,(c[t>>2]|0)+(c[C>>2]<<1)|0)|0;c[p>>2]=(c[p>>2]|0)-(c[w>>2]|0);if((b[(c[t>>2]|0)+(c[C>>2]<<1)>>1]|0)<0){I=29;break}if((b[(c[t>>2]|0)+(c[C>>2]<<1)>>1]|0)>(c[p>>2]|0)){I=29;break}c[o>>2]=(c[o>>2]|0)+(c[w>>2]|0);c[D>>2]=(c[D>>2]|0)-((c[w>>2]|0)+(b[(c[t>>2]|0)+(c[C>>2]<<1)>>1]|0));c[C>>2]=(c[C>>2]|0)+1}if((I|0)==29){c[n>>2]=-4;I=c[n>>2]|0;i=J;return I|0}if((c[D>>2]|0)>=0)break a;c[n>>2]=-4;I=c[n>>2]|0;i=J;return I|0}c[n>>2]=-4;I=c[n>>2]|0;i=J;return I|0}}while(0);c:do if(c[q>>2]|0){c[w>>2]=$h(c[o>>2]|0,c[p>>2]|0,(c[t>>2]|0)+(c[z>>2]<<1)+-2|0)|0;c[p>>2]=(c[p>>2]|0)-(c[w>>2]|0);if((b[(c[t>>2]|0)+((c[z>>2]|0)-1<<1)>>1]|0)>=0?(b[(c[t>>2]|0)+((c[z>>2]|0)-1<<1)>>1]|0)<=(c[p>>2]|0):0){c[o>>2]=(c[o>>2]|0)+(c[w>>2]|0);if(!(c[x>>2]|0)){if(((c[w>>2]|0)+(b[(c[t>>2]|0)+((c[z>>2]|0)-1<<1)>>1]|0)|0)<=(c[D>>2]|0))break;c[n>>2]=-4;I=c[n>>2]|0;i=J;return I|0}I=_(b[(c[t>>2]|0)+((c[z>>2]|0)-1<<1)>>1]|0,c[z>>2]|0)|0;if((I|0)>(c[p>>2]|0)){c[n>>2]=-4;I=c[n>>2]|0;i=J;return I|0}c[C>>2]=0;while(1){if((c[C>>2]|0)>=((c[z>>2]|0)-1|0))break c;b[(c[t>>2]|0)+(c[C>>2]<<1)>>1]=b[(c[t>>2]|0)+((c[z>>2]|0)-1<<1)>>1]|0;c[C>>2]=(c[C>>2]|0)+1}}c[n>>2]=-4;I=c[n>>2]|0;i=J;return I|0}else{if((c[D>>2]|0)<=1275){b[(c[t>>2]|0)+((c[z>>2]|0)-1<<1)>>1]=c[D>>2];break}c[n>>2]=-4;I=c[n>>2]|0;i=J;return I|0}while(0);if(c[u>>2]|0)c[c[u>>2]>>2]=(c[o>>2]|0)-(c[A>>2]|0);c[C>>2]=0;while(1){if((c[C>>2]|0)>=(c[z>>2]|0))break;if(c[s>>2]|0)c[(c[s>>2]|0)+(c[C>>2]<<2)>>2]=c[o>>2];c[o>>2]=(c[o>>2]|0)+(b[(c[t>>2]|0)+(c[C>>2]<<1)>>1]|0);c[C>>2]=(c[C>>2]|0)+1}if(c[v>>2]|0)c[c[v>>2]>>2]=(c[F>>2]|0)+((c[o>>2]|0)-(c[A>>2]|0));if(c[r>>2]|0)a[c[r>>2]>>0]=a[H>>0]|0;c[n>>2]=c[z>>2];I=c[n>>2]|0;i=J;return I|0}function $h(a,e,f){a=a|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0;l=i;i=i+16|0;g=l+12|0;h=l+8|0;j=l+4|0;k=l;c[h>>2]=a;c[j>>2]=e;c[k>>2]=f;if((c[j>>2]|0)<1){b[c[k>>2]>>1]=-1;c[g>>2]=-1;f=c[g>>2]|0;i=l;return f|0}if((d[c[h>>2]>>0]|0|0)<252){b[c[k>>2]>>1]=d[c[h>>2]>>0]|0;c[g>>2]=1;f=c[g>>2]|0;i=l;return f|0}if((c[j>>2]|0)<2){b[c[k>>2]>>1]=-1;c[g>>2]=-1;f=c[g>>2]|0;i=l;return f|0}else{b[c[k>>2]>>1]=((d[(c[h>>2]|0)+1>>0]|0)<<2)+(d[c[h>>2]>>0]|0);c[g>>2]=2;f=c[g>>2]|0;i=l;return f|0}return 0}function ai(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0;h=i;i=i+32|0;b=h+16|0;d=h+12|0;g=h+8|0;e=h+4|0;f=h;c[d>>2]=a;if((c[d>>2]|0)<1|(c[d>>2]|0)>2){c[b>>2]=0;g=c[b>>2]|0;i=h;return g|0}c[f>>2]=Pd(g)|0;if(c[f>>2]|0){c[b>>2]=0;g=c[b>>2]|0;i=h;return g|0}else{c[g>>2]=bi(c[g>>2]|0)|0;c[e>>2]=Fb(c[d>>2]|0)|0;f=bi(88)|0;c[b>>2]=f+(c[g>>2]|0)+(c[e>>2]|0);g=c[b>>2]|0;i=h;return g|0}return 0}function bi(a){a=a|0;var b=0,d=0,e=0;b=i;i=i+16|0;e=b+4|0;d=b;c[e>>2]=a;c[d>>2]=4;a=_((((c[e>>2]|0)+(c[d>>2]|0)-1|0)>>>0)/((c[d>>2]|0)>>>0)|0,c[d>>2]|0)|0;i=b;return a|0}function ci(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0;o=i;i=i+48|0;n=o;e=o+32|0;f=o+28|0;g=o+24|0;h=o+20|0;m=o+16|0;j=o+12|0;k=o+8|0;l=o+4|0;c[f>>2]=a;c[g>>2]=b;c[h>>2]=d;if(!((c[g>>2]|0)!=48e3&(c[g>>2]|0)!=24e3&(c[g>>2]|0)!=16e3&(c[g>>2]|0)!=12e3&(c[g>>2]|0)!=8e3)?!((c[h>>2]|0)!=1&(c[h>>2]|0)!=2):0){a=c[f>>2]|0;oj(a|0,0,ai(c[h>>2]|0)|0)|0;c[k>>2]=Pd(l)|0;if(c[k>>2]|0){c[e>>2]=-3;n=c[e>>2]|0;i=o;return n|0}c[l>>2]=bi(c[l>>2]|0)|0;a=bi(88)|0;c[(c[f>>2]|0)+4>>2]=a;c[c[f>>2]>>2]=(c[(c[f>>2]|0)+4>>2]|0)+(c[l>>2]|0);c[m>>2]=(c[f>>2]|0)+(c[(c[f>>2]|0)+4>>2]|0);c[j>>2]=(c[f>>2]|0)+(c[c[f>>2]>>2]|0);a=c[h>>2]|0;c[(c[f>>2]|0)+8>>2]=a;c[(c[f>>2]|0)+48>>2]=a;c[(c[f>>2]|0)+12>>2]=c[g>>2];c[(c[f>>2]|0)+16+8>>2]=c[(c[f>>2]|0)+12>>2];c[(c[f>>2]|0)+16>>2]=c[(c[f>>2]|0)+8>>2];c[k>>2]=Qd(c[m>>2]|0)|0;if(c[k>>2]|0){c[e>>2]=-3;n=c[e>>2]|0;i=o;return n|0}c[k>>2]=Hb(c[j>>2]|0,c[g>>2]|0,c[h>>2]|0)|0;if(c[k>>2]|0){c[e>>2]=-3;n=c[e>>2]|0;i=o;return n|0}else{a=c[j>>2]|0;c[n>>2]=0;Kb(a,10016,n)|0;c[(c[f>>2]|0)+60>>2]=0;c[(c[f>>2]|0)+64>>2]=(c[g>>2]|0)/400|0;n=di()|0;c[(c[f>>2]|0)+44>>2]=n;c[e>>2]=0;n=c[e>>2]|0;i=o;return n|0}}c[e>>2]=-1;n=c[e>>2]|0;i=o;return n|0}function di(){return 0}function ei(a,d,e,f,h,j,k,l,m){a=a|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;var n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0;N=i;i=i+208|0;G=N+88|0;H=N+84|0;n=N+80|0;o=N+76|0;I=N+72|0;J=N+68|0;p=N+64|0;q=N+60|0;r=N+56|0;s=N+52|0;v=N+48|0;w=N+44|0;t=N+40|0;x=N+36|0;F=N+192|0;z=N+32|0;y=N+28|0;A=N+24|0;B=N+20|0;E=N+96|0;K=N+16|0;L=N+12|0;u=N+8|0;C=N+4|0;D=N;c[H>>2]=a;c[n>>2]=d;c[o>>2]=e;c[I>>2]=f;c[J>>2]=h;c[p>>2]=j;c[q>>2]=k;c[r>>2]=l;c[s>>2]=m;if((c[p>>2]|0)<0|(c[p>>2]|0)>1){c[G>>2]=-1;M=c[G>>2]|0;i=N;return M|0}if((c[p>>2]|0)!=0|(c[o>>2]|0)==0|(c[n>>2]|0)==0?(c[J>>2]|0)%((c[(c[H>>2]|0)+12>>2]|0)/400|0|0)|0|0:0){c[G>>2]=-1;M=c[G>>2]|0;i=N;return M|0}if((c[o>>2]|0)==0|(c[n>>2]|0)==0){c[K>>2]=0;while(1){n=(c[I>>2]|0)+((_(c[K>>2]|0,c[(c[H>>2]|0)+8>>2]|0)|0)<<2)|0;c[L>>2]=fi(c[H>>2]|0,0,0,n,(c[J>>2]|0)-(c[K>>2]|0)|0,0)|0;n=c[L>>2]|0;if((c[L>>2]|0)<0){M=9;break}c[K>>2]=(c[K>>2]|0)+n;if((c[K>>2]|0)>=(c[J>>2]|0)){M=11;break}}if((M|0)==9){c[G>>2]=n;M=c[G>>2]|0;i=N;return M|0}else if((M|0)==11){ii()|0;c[(c[H>>2]|0)+72>>2]=c[K>>2];c[G>>2]=c[K>>2];M=c[G>>2]|0;i=N;return M|0}}if((c[o>>2]|0)<0){c[G>>2]=-1;M=c[G>>2]|0;i=N;return M|0}c[A>>2]=ji(c[n>>2]|0)|0;c[y>>2]=ki(c[n>>2]|0)|0;c[z>>2]=Zh(c[n>>2]|0,c[(c[H>>2]|0)+12>>2]|0)|0;c[B>>2]=li(c[n>>2]|0)|0;c[t>>2]=_h(c[n>>2]|0,c[o>>2]|0,c[q>>2]|0,F,0,E,x,c[r>>2]|0)|0;if((c[t>>2]|0)<0){c[G>>2]=c[t>>2];M=c[G>>2]|0;i=N;return M|0}c[n>>2]=(c[n>>2]|0)+(c[x>>2]|0);if(c[p>>2]|0){if(!((c[A>>2]|0)==1002?1:(c[J>>2]|0)<(c[z>>2]|0))?(c[(c[H>>2]|0)+56>>2]|0)!=1002:0){c[u>>2]=c[(c[H>>2]|0)+72>>2];if((c[J>>2]|0)-(c[z>>2]|0)|0?(c[C>>2]=ei(c[H>>2]|0,0,0,c[I>>2]|0,(c[J>>2]|0)-(c[z>>2]|0)|0,0,0,0,c[s>>2]|0)|0,(c[C>>2]|0)<0):0){c[(c[H>>2]|0)+72>>2]=c[u>>2];c[G>>2]=c[C>>2];M=c[G>>2]|0;i=N;return M|0}c[(c[H>>2]|0)+56>>2]=c[A>>2];c[(c[H>>2]|0)+52>>2]=c[y>>2];c[(c[H>>2]|0)+64>>2]=c[z>>2];c[(c[H>>2]|0)+48>>2]=c[B>>2];M=(c[I>>2]|0)+((_(c[(c[H>>2]|0)+8>>2]|0,(c[J>>2]|0)-(c[z>>2]|0)|0)|0)<<2)|0;c[C>>2]=fi(c[H>>2]|0,c[n>>2]|0,b[E>>1]|0,M,c[z>>2]|0,1)|0;if((c[C>>2]|0)<0){c[G>>2]=c[C>>2];M=c[G>>2]|0;i=N;return M|0}else{ii()|0;c[(c[H>>2]|0)+72>>2]=c[J>>2];c[G>>2]=c[J>>2];M=c[G>>2]|0;i=N;return M|0}}c[G>>2]=ei(c[H>>2]|0,0,0,c[I>>2]|0,c[J>>2]|0,0,0,0,c[s>>2]|0)|0;M=c[G>>2]|0;i=N;return M|0}L=_(c[t>>2]|0,c[z>>2]|0)|0;if((L|0)>(c[J>>2]|0)){c[G>>2]=-2;M=c[G>>2]|0;i=N;return M|0}c[(c[H>>2]|0)+56>>2]=c[A>>2];c[(c[H>>2]|0)+52>>2]=c[y>>2];c[(c[H>>2]|0)+64>>2]=c[z>>2];c[(c[H>>2]|0)+48>>2]=c[B>>2];c[w>>2]=0;c[v>>2]=0;while(1){if((c[v>>2]|0)>=(c[t>>2]|0))break;L=(c[I>>2]|0)+((_(c[w>>2]|0,c[(c[H>>2]|0)+8>>2]|0)|0)<<2)|0;c[D>>2]=fi(c[H>>2]|0,c[n>>2]|0,b[E+(c[v>>2]<<1)>>1]|0,L,(c[J>>2]|0)-(c[w>>2]|0)|0,0)|0;if((c[D>>2]|0)<0){M=31;break}c[n>>2]=(c[n>>2]|0)+(b[E+(c[v>>2]<<1)>>1]|0);c[w>>2]=(c[w>>2]|0)+(c[D>>2]|0);c[v>>2]=(c[v>>2]|0)+1}if((M|0)==31){c[G>>2]=c[D>>2];M=c[G>>2]|0;i=N;return M|0}c[(c[H>>2]|0)+72>>2]=c[w>>2];ii()|0;if(c[s>>2]|0)Xh(c[I>>2]|0,c[w>>2]|0,c[(c[H>>2]|0)+8>>2]|0,(c[H>>2]|0)+76|0);else{g[(c[H>>2]|0)+76+4>>2]=0.0;g[(c[H>>2]|0)+76>>2]=0.0}c[G>>2]=c[w>>2];M=c[G>>2]|0;i=N;return M|0}function fi(d,e,f,h,j,k){d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;var l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,Y=0,Z=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0,ha=0,ja=0,ka=0,la=0,ma=0,oa=0,pa=0,qa=0,ra=0,sa=0,ta=0;ta=i;i=i+320|0;fa=ta+80|0;ea=ta+72|0;da=ta+64|0;ca=ta+56|0;ba=ta+48|0;aa=ta+40|0;$=ta+32|0;Z=ta+24|0;Y=ta+16|0;y=ta+8|0;x=ta;qa=ta+308|0;ra=ta+304|0;A=ta+300|0;B=ta+296|0;ja=ta+292|0;ka=ta+288|0;C=ta+284|0;t=ta+280|0;H=ta+276|0;N=ta+272|0;v=ta+268|0;pa=ta+264|0;L=ta+216|0;u=ta+212|0;o=ta+208|0;z=ta+204|0;m=ta+200|0;P=ta+196|0;S=ta+192|0;sa=ta+188|0;O=ta+184|0;W=ta+180|0;V=ta+176|0;Q=ta+172|0;R=ta+168|0;K=ta+164|0;F=ta+160|0;D=ta+156|0;E=ta+152|0;n=ta+148|0;oa=ta+144|0;ga=ta+140|0;T=ta+136|0;G=ta+132|0;l=ta+128|0;la=ta+124|0;ma=ta+120|0;r=ta+116|0;p=ta+112|0;s=ta+108|0;q=ta+104|0;w=ta+100|0;I=ta+96|0;U=ta+312|0;J=ta+92|0;M=ta+88|0;ha=ta+84|0;c[ra>>2]=d;c[A>>2]=e;c[B>>2]=f;c[ja>>2]=h;c[ka>>2]=j;c[C>>2]=k;c[v>>2]=0;c[pa>>2]=0;c[P>>2]=0;c[W>>2]=0;c[Q>>2]=0;c[R>>2]=0;c[K>>2]=0;c[T>>2]=0;c[t>>2]=(c[ra>>2]|0)+(c[(c[ra>>2]|0)+4>>2]|0);c[H>>2]=(c[ra>>2]|0)+(c[c[ra>>2]>>2]|0);c[oa>>2]=(c[(c[ra>>2]|0)+12>>2]|0)/50|0;c[n>>2]=c[oa>>2]>>1;c[E>>2]=c[n>>2]>>1;c[D>>2]=c[E>>2]>>1;if((c[ka>>2]|0)<(c[D>>2]|0)){c[qa>>2]=-2;sa=c[qa>>2]|0;i=ta;return sa|0}if((c[ka>>2]|0)<(((c[(c[ra>>2]|0)+12>>2]|0)/25|0)*3|0))e=c[ka>>2]|0;else e=((c[(c[ra>>2]|0)+12>>2]|0)/25|0)*3|0;c[ka>>2]=e;if((c[B>>2]|0)<=1){c[A>>2]=0;if((c[ka>>2]|0)<(c[(c[ra>>2]|0)+64>>2]|0))e=c[ka>>2]|0;else e=c[(c[ra>>2]|0)+64>>2]|0;c[ka>>2]=e}do if(!(c[A>>2]|0)){c[sa>>2]=c[ka>>2];c[O>>2]=c[(c[ra>>2]|0)+60>>2];if(!(c[O>>2]|0)){c[N>>2]=0;while(1){if((c[N>>2]|0)>=(_(c[sa>>2]|0,c[(c[ra>>2]|0)+8>>2]|0)|0))break;g[(c[ja>>2]|0)+(c[N>>2]<<2)>>2]=0.0;c[N>>2]=(c[N>>2]|0)+1}c[qa>>2]=c[sa>>2];sa=c[qa>>2]|0;i=ta;return sa|0}if((c[sa>>2]|0)>(c[oa>>2]|0)){while(1){c[l>>2]=fi(c[ra>>2]|0,0,0,c[ja>>2]|0,(c[sa>>2]|0)<(c[oa>>2]|0)?c[sa>>2]|0:c[oa>>2]|0,0)|0;e=c[l>>2]|0;if((c[l>>2]|0)<0){k=20;break}pa=_(e,c[(c[ra>>2]|0)+8>>2]|0)|0;c[ja>>2]=(c[ja>>2]|0)+(pa<<2);c[sa>>2]=(c[sa>>2]|0)-(c[l>>2]|0);if((c[sa>>2]|0)<=0){k=22;break}}if((k|0)==20){c[qa>>2]=e;sa=c[qa>>2]|0;i=ta;return sa|0}else if((k|0)==22){c[qa>>2]=c[ka>>2];sa=c[qa>>2]|0;i=ta;return sa|0}}if((c[sa>>2]|0)<(c[oa>>2]|0)){if((c[sa>>2]|0)>(c[n>>2]|0)){c[sa>>2]=c[n>>2];break}if(((c[O>>2]|0)!=1e3?(c[sa>>2]|0)>(c[E>>2]|0):0)?(c[sa>>2]|0)<(c[n>>2]|0):0)c[sa>>2]=c[E>>2]}}else{c[sa>>2]=c[(c[ra>>2]|0)+64>>2];c[O>>2]=c[(c[ra>>2]|0)+56>>2];Yb(L,c[A>>2]|0,c[B>>2]|0)}while(0);c[G>>2]=0;c[z>>2]=1;c[m>>2]=1;do if(c[A>>2]|0?(c[(c[ra>>2]|0)+60>>2]|0)>0:0){if(!(((c[O>>2]|0)==1002?(c[(c[ra>>2]|0)+60>>2]|0)!=1002:0)?!(c[(c[ra>>2]|0)+68>>2]|0):0)){if((c[O>>2]|0)==1002)break;if((c[(c[ra>>2]|0)+60>>2]|0)!=1002)break}c[W>>2]=1;e=_(c[E>>2]|0,c[(c[ra>>2]|0)+8>>2]|0)|0;if((c[O>>2]|0)==1002){c[m>>2]=e;break}else{c[z>>2]=e;break}}while(0);m=c[m>>2]|0;c[la>>2]=ia()|0;e=i;i=i+((1*(m<<2)|0)+15&-16)|0;if((c[W>>2]|0)!=0&(c[O>>2]|0)==1002){c[P>>2]=e;fi(c[ra>>2]|0,0,0,c[P>>2]|0,(c[E>>2]|0)<(c[sa>>2]|0)?c[E>>2]|0:c[sa>>2]|0,0)|0}a:do if((c[sa>>2]|0)>(c[ka>>2]|0)){c[qa>>2]=-1;c[ma>>2]=1}else{c[ka>>2]=c[sa>>2];if((c[O>>2]|0)==1002|(c[G>>2]|0)!=0)e=1;else e=_((c[n>>2]|0)>(c[ka>>2]|0)?c[n>>2]|0:c[ka>>2]|0,c[(c[ra>>2]|0)+8>>2]|0)|0;c[o>>2]=e;j=i;i=i+((1*(c[o>>2]<<1)|0)+15&-16)|0;b:do if((c[O>>2]|0)!=1002){c[s>>2]=j;if((c[(c[ra>>2]|0)+60>>2]|0)==1002)Qd(c[t>>2]|0)|0;if(10>(((c[sa>>2]|0)*1e3|0)/(c[(c[ra>>2]|0)+12>>2]|0)|0|0))e=10;else e=((c[sa>>2]|0)*1e3|0)/(c[(c[ra>>2]|0)+12>>2]|0)|0;c[(c[ra>>2]|0)+16+16>>2]=e;do if(c[A>>2]|0){c[(c[ra>>2]|0)+16+4>>2]=c[(c[ra>>2]|0)+48>>2];e=c[ra>>2]|0;if((c[O>>2]|0)!=1e3){c[e+16+12>>2]=16e3;break}k=c[ra>>2]|0;if((c[e+52>>2]|0)==1101){c[k+16+12>>2]=8e3;break}if((c[k+52>>2]|0)==1102){c[(c[ra>>2]|0)+16+12>>2]=12e3;break}else{c[(c[ra>>2]|0)+16+12>>2]=16e3;break}}while(0);c[r>>2]=(c[A>>2]|0)==0?1:c[C>>2]<<1;c[p>>2]=0;c:while(1){c[q>>2]=(c[p>>2]|0)==0&1;c[v>>2]=Rd(c[t>>2]|0,(c[ra>>2]|0)+16|0,c[r>>2]|0,c[q>>2]|0,L,c[s>>2]|0,u,c[(c[ra>>2]|0)+44>>2]|0)|0;d:do if(c[v>>2]|0){if(!(c[r>>2]|0))break c;c[u>>2]=c[ka>>2];c[N>>2]=0;while(1){if((c[N>>2]|0)>=(_(c[ka>>2]|0,c[(c[ra>>2]|0)+8>>2]|0)|0))break d;b[(c[s>>2]|0)+(c[N>>2]<<1)>>1]=0;c[N>>2]=(c[N>>2]|0)+1}}while(0);o=_(c[u>>2]|0,c[(c[ra>>2]|0)+8>>2]|0)|0;c[s>>2]=(c[s>>2]|0)+(o<<1);c[p>>2]=(c[p>>2]|0)+(c[u>>2]|0);if((c[p>>2]|0)>=(c[ka>>2]|0))break b}c[qa>>2]=-3;c[ma>>2]=1;break a}while(0);c[V>>2]=0;if((c[C>>2]|0)==0&(c[O>>2]|0)!=1002&(c[A>>2]|0)!=0?(v=(gi(L)|0)+17|0,(v+(((c[(c[ra>>2]|0)+56>>2]|0)==1001&1)*20|0)|0)<=(c[B>>2]<<3|0)):0){if((c[O>>2]|0)==1001)c[Q>>2]=dc(L,12)|0;else c[Q>>2]=1;if(c[Q>>2]|0){c[K>>2]=dc(L,1)|0;if((c[O>>2]|0)==1001)e=(fc(L,256)|0)+2|0;else{e=c[B>>2]|0;e=e-((gi(L)|0)+7>>3)|0}c[R>>2]=e;c[B>>2]=(c[B>>2]|0)-(c[R>>2]|0);v=c[B>>2]<<3;if((v|0)<(gi(L)|0)){c[B>>2]=0;c[R>>2]=0;c[Q>>2]=0}v=L+4|0;c[v>>2]=(c[v>>2]|0)-(c[R>>2]|0)}}if((c[O>>2]|0)!=1002)c[V>>2]=17;c[w>>2]=21;switch(c[(c[ra>>2]|0)+52>>2]|0){case 1101:{c[w>>2]=13;break}case 1103:case 1102:{c[w>>2]=17;break}case 1104:{c[w>>2]=19;break}case 1105:{c[w>>2]=21;break}default:{}}v=c[H>>2]|0;c[x>>2]=c[w>>2];Kb(v,10012,x)|0;x=c[H>>2]|0;c[y>>2]=c[(c[ra>>2]|0)+48>>2];Kb(x,10008,y)|0;if(c[Q>>2]|0){c[W>>2]=0;c[z>>2]=1}e=i;i=i+((1*(c[z>>2]<<2)|0)+15&-16)|0;if((c[W>>2]|0)!=0&(c[O>>2]|0)!=1002){c[P>>2]=e;fi(c[ra>>2]|0,0,0,c[P>>2]|0,(c[E>>2]|0)<(c[sa>>2]|0)?c[E>>2]|0:c[sa>>2]|0,0)|0}if(c[Q>>2]|0)e=_(c[E>>2]|0,c[(c[ra>>2]|0)+8>>2]|0)|0;else e=1;c[S>>2]=e;e=i;i=i+((1*(c[S>>2]<<2)|0)+15&-16)|0;if((c[Q>>2]|0)!=0&(c[K>>2]|0)!=0){S=c[H>>2]|0;c[Y>>2]=0;Kb(S,10010,Y)|0;Lb(c[H>>2]|0,(c[A>>2]|0)+(c[B>>2]|0)|0,c[R>>2]|0,e,c[E>>2]|0,0,0)|0;Y=c[H>>2]|0;c[Z>>2]=T+(((T-T|0)/4|0)<<2);Kb(Y,4031,Z)|0}Z=c[H>>2]|0;c[$>>2]=c[V>>2];Kb(Z,10010,$)|0;do if((c[O>>2]|0)==1e3){a[U>>0]=a[30523]|0;a[U+1>>0]=a[30524]|0;e:do if(!(c[G>>2]|0)){c[N>>2]=0;while(1){if((c[N>>2]|0)>=(_(c[ka>>2]|0,c[(c[ra>>2]|0)+8>>2]|0)|0))break e;g[(c[ja>>2]|0)+(c[N>>2]<<2)>>2]=0.0;c[N>>2]=(c[N>>2]|0)+1}}while(0);if((c[(c[ra>>2]|0)+60>>2]|0)==1001){if((c[Q>>2]|0)!=0&(c[K>>2]|0)!=0?c[(c[ra>>2]|0)+68>>2]|0:0)break;oa=c[H>>2]|0;c[ba>>2]=0;Kb(oa,10010,ba)|0;Lb(c[H>>2]|0,U,2,c[ja>>2]|0,c[D>>2]|0,0,c[G>>2]|0)|0}}else{c[I>>2]=(c[oa>>2]|0)<(c[ka>>2]|0)?c[oa>>2]|0:c[ka>>2]|0;do if((c[O>>2]|0)!=(c[(c[ra>>2]|0)+60>>2]|0)){if((c[(c[ra>>2]|0)+60>>2]|0)<=0)break;if(c[(c[ra>>2]|0)+68>>2]|0)break;Kb(c[H>>2]|0,4028,aa)|0}while(0);c[pa>>2]=Lb(c[H>>2]|0,c[C>>2]|0?0:c[A>>2]|0,c[B>>2]|0,c[ja>>2]|0,c[I>>2]|0,L,c[G>>2]|0)|0}while(0);f:do if(!((c[O>>2]|0)==1002|(c[G>>2]|0)!=0)){c[N>>2]=0;while(1){if((c[N>>2]|0)>=(_(c[ka>>2]|0,c[(c[ra>>2]|0)+8>>2]|0)|0))break f;g[(c[ja>>2]|0)+(c[N>>2]<<2)>>2]=+g[(c[ja>>2]|0)+(c[N>>2]<<2)>>2]+ +(b[j+(c[N>>2]<<1)>>1]|0)*.000030517578125;c[N>>2]=(c[N>>2]|0)+1}}while(0);oa=c[H>>2]|0;c[ca>>2]=J+(((J-J|0)/4|0)<<2);Kb(oa,10015,ca)|0;c[ga>>2]=c[(c[J>>2]|0)+60>>2];if(!((c[Q>>2]|0)==0|(c[K>>2]|0)!=0)){Kb(c[H>>2]|0,4028,da)|0;oa=c[H>>2]|0;c[ea>>2]=0;Kb(oa,10010,ea)|0;Lb(c[H>>2]|0,(c[A>>2]|0)+(c[B>>2]|0)|0,c[R>>2]|0,e,c[E>>2]|0,0,0)|0;ea=c[H>>2]|0;c[fa>>2]=T+(((T-T|0)/4|0)<<2);Kb(ea,4031,fa)|0;ea=(c[ja>>2]|0)+((_(c[(c[ra>>2]|0)+8>>2]|0,(c[ka>>2]|0)-(c[D>>2]|0)|0)|0)<<2)|0;fa=e+((_(c[(c[ra>>2]|0)+8>>2]|0,c[D>>2]|0)|0)<<2)|0;oa=(c[ja>>2]|0)+((_(c[(c[ra>>2]|0)+8>>2]|0,(c[ka>>2]|0)-(c[D>>2]|0)|0)|0)<<2)|0;hi(ea,fa,oa,c[D>>2]|0,c[(c[ra>>2]|0)+8>>2]|0,c[ga>>2]|0,c[(c[ra>>2]|0)+12>>2]|0)}if((c[Q>>2]|0)!=0&(c[K>>2]|0)!=0){c[F>>2]=0;while(1){if((c[F>>2]|0)>=(c[(c[ra>>2]|0)+8>>2]|0))break;c[N>>2]=0;while(1){if((c[N>>2]|0)>=(c[D>>2]|0))break;fa=_(c[(c[ra>>2]|0)+8>>2]|0,c[N>>2]|0)|0;oa=_(c[(c[ra>>2]|0)+8>>2]|0,c[N>>2]|0)|0;g[(c[ja>>2]|0)+(oa+(c[F>>2]|0)<<2)>>2]=+g[e+(fa+(c[F>>2]|0)<<2)>>2];c[N>>2]=(c[N>>2]|0)+1}c[F>>2]=(c[F>>2]|0)+1}ea=e+((_(c[(c[ra>>2]|0)+8>>2]|0,c[D>>2]|0)|0)<<2)|0;fa=(c[ja>>2]|0)+((_(c[(c[ra>>2]|0)+8>>2]|0,c[D>>2]|0)|0)<<2)|0;oa=(c[ja>>2]|0)+((_(c[(c[ra>>2]|0)+8>>2]|0,c[D>>2]|0)|0)<<2)|0;hi(ea,fa,oa,c[D>>2]|0,c[(c[ra>>2]|0)+8>>2]|0,c[ga>>2]|0,c[(c[ra>>2]|0)+12>>2]|0)}do if(c[W>>2]|0){if((c[sa>>2]|0)<(c[E>>2]|0)){hi(c[P>>2]|0,c[ja>>2]|0,c[ja>>2]|0,c[D>>2]|0,c[(c[ra>>2]|0)+8>>2]|0,c[ga>>2]|0,c[(c[ra>>2]|0)+12>>2]|0);break}c[N>>2]=0;while(1){if((c[N>>2]|0)>=(_(c[(c[ra>>2]|0)+8>>2]|0,c[D>>2]|0)|0))break;g[(c[ja>>2]|0)+(c[N>>2]<<2)>>2]=+g[(c[P>>2]|0)+(c[N>>2]<<2)>>2];c[N>>2]=(c[N>>2]|0)+1}ea=(c[P>>2]|0)+((_(c[(c[ra>>2]|0)+8>>2]|0,c[D>>2]|0)|0)<<2)|0;fa=(c[ja>>2]|0)+((_(c[(c[ra>>2]|0)+8>>2]|0,c[D>>2]|0)|0)<<2)|0;oa=(c[ja>>2]|0)+((_(c[(c[ra>>2]|0)+8>>2]|0,c[D>>2]|0)|0)<<2)|0;hi(ea,fa,oa,c[D>>2]|0,c[(c[ra>>2]|0)+8>>2]|0,c[ga>>2]|0,c[(c[ra>>2]|0)+12>>2]|0)}while(0);g:do if(c[(c[ra>>2]|0)+40>>2]|0){g[M>>2]=+X(+(+(c[(c[ra>>2]|0)+40>>2]|0)*6.488140788860619e-04*.6931471805599453));c[N>>2]=0;while(1){if((c[N>>2]|0)>=(_(c[ka>>2]|0,c[(c[ra>>2]|0)+8>>2]|0)|0))break g;g[ha>>2]=+g[(c[ja>>2]|0)+(c[N>>2]<<2)>>2]*+g[M>>2];g[(c[ja>>2]|0)+(c[N>>2]<<2)>>2]=+g[ha>>2];c[N>>2]=(c[N>>2]|0)+1}}while(0);if((c[B>>2]|0)<=1)c[(c[ra>>2]|0)+84>>2]=0;else c[(c[ra>>2]|0)+84>>2]=c[L+28>>2]^c[T>>2];c[(c[ra>>2]|0)+60>>2]=c[O>>2];if(c[Q>>2]|0)e=(c[K>>2]|0)!=0^1;else e=0;c[(c[ra>>2]|0)+68>>2]=e&1;if((c[pa>>2]|0)>=0)ii()|0;c[qa>>2]=(c[pa>>2]|0)<0?c[pa>>2]|0:c[sa>>2]|0;c[ma>>2]=1}while(0);na(c[la>>2]|0);sa=c[qa>>2]|0;i=ta;return sa|0}function gi(a){a=a|0;var b=0,d=0;b=i;i=i+16|0;d=b;c[d>>2]=a;a=(c[(c[d>>2]|0)+20>>2]|0)-(32-(aa(c[(c[d>>2]|0)+28>>2]|0)|0))|0;i=b;return a|0}function hi(a,b,d,e,f,h,j){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;u=i;i=i+48|0;k=u+40|0;l=u+36|0;m=u+32|0;n=u+28|0;o=u+24|0;p=u+20|0;v=u+16|0;r=u+12|0;q=u+8|0;s=u+4|0;t=u;c[k>>2]=a;c[l>>2]=b;c[m>>2]=d;c[n>>2]=e;c[o>>2]=f;c[p>>2]=h;c[v>>2]=j;c[s>>2]=48e3/(c[v>>2]|0)|0;c[q>>2]=0;while(1){if((c[q>>2]|0)>=(c[o>>2]|0))break;c[r>>2]=0;while(1){if((c[r>>2]|0)>=(c[n>>2]|0))break;h=_(c[r>>2]|0,c[s>>2]|0)|0;e=_(c[r>>2]|0,c[s>>2]|0)|0;g[t>>2]=+g[(c[p>>2]|0)+(h<<2)>>2]*+g[(c[p>>2]|0)+(e<<2)>>2];e=_(c[r>>2]|0,c[o>>2]|0)|0;h=_(c[r>>2]|0,c[o>>2]|0)|0;v=_(c[r>>2]|0,c[o>>2]|0)|0;g[(c[m>>2]|0)+(v+(c[q>>2]|0)<<2)>>2]=+g[t>>2]*+g[(c[l>>2]|0)+(e+(c[q>>2]|0)<<2)>>2]+(1.0-+g[t>>2])*+g[(c[k>>2]|0)+(h+(c[q>>2]|0)<<2)>>2];c[r>>2]=(c[r>>2]|0)+1}c[q>>2]=(c[q>>2]|0)+1}i=u;return}function ii(){return 0}function ji(a){a=a|0;var b=0,e=0,f=0;f=i;i=i+16|0;b=f+4|0;e=f;c[b>>2]=a;do if(!((d[c[b>>2]>>0]|0)&128|0))if(((d[c[b>>2]>>0]|0)&96|0)==96){c[e>>2]=1001;break}else{c[e>>2]=1e3;break}else c[e>>2]=1002;while(0);i=f;return c[e>>2]|0}function ki(a){a=a|0;var b=0,e=0,f=0,g=0;g=i;i=i+16|0;b=g+4|0;f=g;c[b>>2]=a;e=d[c[b>>2]>>0]|0;if((d[c[b>>2]>>0]|0)&128|0){e=1102+(e>>5&3)|0;c[f>>2]=e;c[f>>2]=(c[f>>2]|0)==1102?1101:e;f=c[f>>2]|0;i=g;return f|0}a=d[c[b>>2]>>0]|0;if((e&96|0)==96){c[f>>2]=a&16|0?1105:1104;f=c[f>>2]|0;i=g;return f|0}else{c[f>>2]=1101+(a>>5&3);f=c[f>>2]|0;i=g;return f|0}return 0}function li(a){a=a|0;var b=0,e=0;e=i;i=i+16|0;b=e;c[b>>2]=a;i=e;return ((d[c[b>>2]>>0]|0)&4|0?2:1)|0}function mi(a,d,e,f,h,j){a=a|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;v=i;i=i+48|0;l=v+40|0;m=v+36|0;n=v+32|0;o=v+28|0;p=v+24|0;q=v+20|0;r=v+16|0;u=v+12|0;t=v+8|0;k=v+4|0;s=v;c[m>>2]=a;c[n>>2]=d;c[o>>2]=e;c[p>>2]=f;c[q>>2]=h;c[r>>2]=j;if((c[q>>2]|0)<=0){c[l>>2]=-1;u=c[l>>2]|0;i=v;return u|0}do if(!((c[n>>2]|0)!=0&(c[o>>2]|0)>0^1|(c[r>>2]|0)!=0)){c[k>>2]=ni(c[m>>2]|0,c[n>>2]|0,c[o>>2]|0)|0;if((c[k>>2]|0)>0){c[q>>2]=(c[q>>2]|0)<(c[k>>2]|0)?c[q>>2]|0:c[k>>2]|0;break}c[l>>2]=-4;u=c[l>>2]|0;i=v;return u|0}while(0);a=_(c[q>>2]|0,c[(c[m>>2]|0)+8>>2]|0)|0;c[s>>2]=ia()|0;d=i;i=i+((1*(a<<2)|0)+15&-16)|0;c[u>>2]=ei(c[m>>2]|0,c[n>>2]|0,c[o>>2]|0,d,c[q>>2]|0,c[r>>2]|0,0,0,1)|0;a:do if((c[u>>2]|0)>0){c[t>>2]=0;while(1){if((c[t>>2]|0)>=(_(c[u>>2]|0,c[(c[m>>2]|0)+8>>2]|0)|0))break a;r=qi(+g[d+(c[t>>2]<<2)>>2])|0;b[(c[p>>2]|0)+(c[t>>2]<<1)>>1]=r;c[t>>2]=(c[t>>2]|0)+1}}while(0);c[l>>2]=c[u>>2];na(c[s>>2]|0);u=c[l>>2]|0;i=v;return u|0}function ni(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0;e=i;i=i+16|0;f=e+8|0;h=e+4|0;g=e;c[f>>2]=a;c[h>>2]=b;c[g>>2]=d;b=oi(c[h>>2]|0,c[g>>2]|0,c[(c[f>>2]|0)+12>>2]|0)|0;i=e;return b|0}function oi(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0;j=i;i=i+32|0;e=j+20|0;f=j+16|0;l=j+12|0;g=j+8|0;h=j+4|0;k=j;c[f>>2]=a;c[l>>2]=b;c[g>>2]=d;c[k>>2]=pi(c[f>>2]|0,c[l>>2]|0)|0;d=c[k>>2]|0;if((c[k>>2]|0)<0){c[e>>2]=d;l=c[e>>2]|0;i=j;return l|0}c[h>>2]=_(d,Zh(c[f>>2]|0,c[g>>2]|0)|0)|0;if(((c[h>>2]|0)*25|0)>((c[g>>2]|0)*3|0)){c[e>>2]=-4;l=c[e>>2]|0;i=j;return l|0}else{c[e>>2]=c[h>>2];l=c[e>>2]|0;i=j;return l|0}return 0}function pi(a,b){a=a|0;b=b|0;var e=0,f=0,g=0,h=0,j=0;j=i;i=i+16|0;e=j+12|0;f=j+8|0;g=j+4|0;h=j;c[f>>2]=a;c[g>>2]=b;do if((c[g>>2]|0)>=1){c[h>>2]=(d[c[f>>2]>>0]|0)&3;if(!(c[h>>2]|0)){c[e>>2]=1;break}if((c[h>>2]|0)!=3){c[e>>2]=2;break}if((c[g>>2]|0)<2){c[e>>2]=-4;break}else{c[e>>2]=(d[(c[f>>2]|0)+1>>0]|0)&63;break}}else c[e>>2]=-1;while(0);i=j;return c[e>>2]|0}function qi(a){a=+a;var b=0,c=0;c=i;i=i+16|0;b=c;g[b>>2]=a;g[b>>2]=+g[b>>2]*32768.0;g[b>>2]=+g[b>>2]>-32768.0?+g[b>>2]:-32768.0;g[b>>2]=+g[b>>2]<32767.0?+g[b>>2]:32767.0;b=(ij(+g[b>>2])|0)&65535;i=c;return b|0}function ri(a,b,d,e,f,g){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;p=i;i=i+32|0;h=p+24|0;j=p+20|0;k=p+16|0;l=p+12|0;m=p+8|0;n=p+4|0;o=p;c[j>>2]=a;c[k>>2]=b;c[l>>2]=d;c[m>>2]=e;c[n>>2]=f;c[o>>2]=g;if((c[n>>2]|0)<=0){c[h>>2]=-1;a=c[h>>2]|0;i=p;return a|0}else{c[h>>2]=ei(c[j>>2]|0,c[k>>2]|0,c[l>>2]|0,c[m>>2]|0,c[n>>2]|0,c[o>>2]|0,0,0,0)|0;a=c[h>>2]|0;i=p;return a|0}return 0}function si(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;t=i;i=i+80|0;q=t+8|0;r=t+76|0;e=t+72|0;u=t+68|0;s=t+64|0;f=t+48|0;h=t+44|0;g=t+40|0;j=t+36|0;k=t+32|0;l=t+28|0;m=t+24|0;n=t+20|0;o=t+16|0;p=t+12|0;c[e>>2]=a;c[u>>2]=b;c[s>>2]=0;c[h>>2]=(c[e>>2]|0)+(c[(c[e>>2]|0)+4>>2]|0);c[g>>2]=(c[e>>2]|0)+(c[c[e>>2]>>2]|0);c[f>>2]=d;a:do switch(c[u>>2]|0){case 4009:{d=(c[f>>2]|0)+(4-1)&~(4-1);u=c[d>>2]|0;c[f>>2]=d+4;c[j>>2]=u;if(c[j>>2]|0){c[c[j>>2]>>2]=c[(c[e>>2]|0)+52>>2];e=20}else e=21;break}case 4031:{d=(c[f>>2]|0)+(4-1)&~(4-1);u=c[d>>2]|0;c[f>>2]=d+4;c[k>>2]=u;if(c[k>>2]|0){c[c[k>>2]>>2]=c[(c[e>>2]|0)+84>>2];e=20}else e=21;break}case 4028:{oj((c[e>>2]|0)+48|0,0,88-((c[e>>2]|0)+48-(c[e>>2]|0))|0)|0;Kb(c[g>>2]|0,4028,t)|0;Qd(c[h>>2]|0)|0;c[(c[e>>2]|0)+48>>2]=c[(c[e>>2]|0)+8>>2];c[(c[e>>2]|0)+64>>2]=(c[(c[e>>2]|0)+12>>2]|0)/400|0;e=20;break}case 4029:{d=(c[f>>2]|0)+(4-1)&~(4-1);u=c[d>>2]|0;c[f>>2]=d+4;c[l>>2]=u;if(c[l>>2]|0){c[c[l>>2]>>2]=c[(c[e>>2]|0)+12>>2];e=20}else e=21;break}case 4033:{d=(c[f>>2]|0)+(4-1)&~(4-1);u=c[d>>2]|0;c[f>>2]=d+4;c[m>>2]=u;if(c[m>>2]|0)if((c[(c[e>>2]|0)+60>>2]|0)==1002){e=c[g>>2]|0;c[q>>2]=(c[m>>2]|0)+((((c[m>>2]|0)-(c[m>>2]|0)|0)/4|0)<<2);Kb(e,4033,q)|0;e=20;break a}else{c[c[m>>2]>>2]=c[(c[e>>2]|0)+16+20>>2];e=20;break a}else e=21;break}case 4045:{d=(c[f>>2]|0)+(4-1)&~(4-1);u=c[d>>2]|0;c[f>>2]=d+4;c[n>>2]=u;if(c[n>>2]|0){c[c[n>>2]>>2]=c[(c[e>>2]|0)+40>>2];e=20}else e=21;break}case 4034:{d=(c[f>>2]|0)+(4-1)&~(4-1);u=c[d>>2]|0;c[f>>2]=d+4;c[o>>2]=u;if((c[o>>2]|0)<-32768|(c[o>>2]|0)>32767)e=21;else{c[(c[e>>2]|0)+40>>2]=c[o>>2];e=20}break}case 4039:{d=(c[f>>2]|0)+(4-1)&~(4-1);u=c[d>>2]|0;c[f>>2]=d+4;c[p>>2]=u;if(c[p>>2]|0){c[c[p>>2]>>2]=c[(c[e>>2]|0)+72>>2];e=20}else e=21;break}default:{c[s>>2]=-5;e=20}}while(0);if((e|0)==20){c[r>>2]=c[s>>2];u=c[r>>2]|0;i=t;return u|0}else if((e|0)==21){c[r>>2]=-1;u=c[r>>2]|0;i=t;return u|0}return 0}function ti(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0;h=i;i=i+32|0;b=h+16|0;d=h+12|0;g=h+8|0;e=h+4|0;f=h;c[d>>2]=a;if((c[d>>2]|0)<1|(c[d>>2]|0)>2){c[b>>2]=0;g=c[b>>2]|0;i=h;return g|0}c[f>>2]=Sd(g)|0;if(c[f>>2]|0){c[b>>2]=0;g=c[b>>2]|0;i=h;return g|0}else{c[g>>2]=ui(c[g>>2]|0)|0;c[e>>2]=hb(c[d>>2]|0)|0;f=ui(18220)|0;c[b>>2]=f+(c[g>>2]|0)+(c[e>>2]|0);g=c[b>>2]|0;i=h;return g|0}return 0}function ui(a){a=a|0;var b=0,d=0,e=0;b=i;i=i+16|0;e=b+4|0;d=b;c[e>>2]=a;c[d>>2]=4;a=_((((c[e>>2]|0)+(c[d>>2]|0)-1|0)>>>0)/((c[d>>2]|0)>>>0)|0,c[d>>2]|0)|0;i=b;return a|0}function vi(a,d,e,f){a=a|0;d=d|0;e=e|0;f=f|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;u=i;i=i+64|0;t=u+8|0;s=u;h=u+48|0;j=u+44|0;k=u+40|0;l=u+36|0;m=u+32|0;r=u+28|0;n=u+24|0;o=u+20|0;p=u+16|0;q=u+12|0;c[j>>2]=a;c[k>>2]=d;c[l>>2]=e;c[m>>2]=f;if((!((c[k>>2]|0)!=48e3&(c[k>>2]|0)!=24e3&(c[k>>2]|0)!=16e3&(c[k>>2]|0)!=12e3&(c[k>>2]|0)!=8e3)?!((c[l>>2]|0)!=1&(c[l>>2]|0)!=2):0)?!((c[m>>2]|0)!=2048&(c[m>>2]|0)!=2049&(c[m>>2]|0)!=2051):0){a=c[j>>2]|0;oj(a|0,0,ti(c[l>>2]|0)|0)|0;c[p>>2]=Sd(q)|0;if(c[p>>2]|0){c[h>>2]=-1;t=c[h>>2]|0;i=u;return t|0}c[q>>2]=ui(c[q>>2]|0)|0;a=ui(18220)|0;c[(c[j>>2]|0)+4>>2]=a;c[c[j>>2]>>2]=(c[(c[j>>2]|0)+4>>2]|0)+(c[q>>2]|0);c[r>>2]=(c[j>>2]|0)+(c[(c[j>>2]|0)+4>>2]|0);c[n>>2]=(c[j>>2]|0)+(c[c[j>>2]>>2]|0);a=c[l>>2]|0;c[(c[j>>2]|0)+100>>2]=a;c[(c[j>>2]|0)+14288>>2]=a;c[(c[j>>2]|0)+132>>2]=c[k>>2];a=wi()|0;c[(c[j>>2]|0)+168>>2]=a;c[p>>2]=Td(c[r>>2]|0,c[(c[j>>2]|0)+168>>2]|0,(c[j>>2]|0)+8|0)|0;if(c[p>>2]|0){c[h>>2]=-3;t=c[h>>2]|0;i=u;return t|0}c[(c[j>>2]|0)+8>>2]=c[l>>2];c[(c[j>>2]|0)+8+4>>2]=c[l>>2];c[(c[j>>2]|0)+8+8>>2]=c[(c[j>>2]|0)+132>>2];c[(c[j>>2]|0)+8+12>>2]=16e3;c[(c[j>>2]|0)+8+16>>2]=8e3;c[(c[j>>2]|0)+8+20>>2]=16e3;c[(c[j>>2]|0)+8+24>>2]=20;c[(c[j>>2]|0)+8+28>>2]=25e3;c[(c[j>>2]|0)+8+32>>2]=0;c[(c[j>>2]|0)+8+36>>2]=9;c[(c[j>>2]|0)+8+40>>2]=0;c[(c[j>>2]|0)+8+44>>2]=0;c[(c[j>>2]|0)+8+48>>2]=0;c[(c[j>>2]|0)+8+64>>2]=0;c[o>>2]=jb(c[n>>2]|0,c[k>>2]|0,c[l>>2]|0,c[(c[j>>2]|0)+168>>2]|0)|0;if(c[o>>2]|0){c[h>>2]=-3;t=c[h>>2]|0;i=u;return t|0}else{a=c[n>>2]|0;c[s>>2]=0;lb(a,10016,s)|0;s=c[n>>2]|0;c[t>>2]=c[(c[j>>2]|0)+8+36>>2];lb(s,4010,t)|0;c[(c[j>>2]|0)+136>>2]=1;c[(c[j>>2]|0)+140>>2]=1;c[(c[j>>2]|0)+152>>2]=-1e3;t=3e3+(_(c[k>>2]|0,c[l>>2]|0)|0)|0;c[(c[j>>2]|0)+148>>2]=t;c[(c[j>>2]|0)+96>>2]=c[m>>2];c[(c[j>>2]|0)+112>>2]=-1e3;c[(c[j>>2]|0)+116>>2]=-1e3;c[(c[j>>2]|0)+120>>2]=1105;c[(c[j>>2]|0)+108>>2]=-1e3;c[(c[j>>2]|0)+124>>2]=-1e3;c[(c[j>>2]|0)+128>>2]=-1;c[(c[j>>2]|0)+160>>2]=(c[(c[j>>2]|0)+132>>2]|0)/100|0;c[(c[j>>2]|0)+156>>2]=24;c[(c[j>>2]|0)+144>>2]=5e3;c[(c[j>>2]|0)+104>>2]=(c[(c[j>>2]|0)+132>>2]|0)/250|0;b[(c[j>>2]|0)+14292>>1]=16384;g[(c[j>>2]|0)+14300>>2]=1.0;t=(Sf(60)|0)<<8;c[(c[j>>2]|0)+14296>>2]=t;c[(c[j>>2]|0)+14344>>2]=1;c[(c[j>>2]|0)+14320>>2]=1001;c[(c[j>>2]|0)+14336>>2]=1105;Yi((c[j>>2]|0)+172|0);c[h>>2]=0;t=c[h>>2]|0;i=u;return t|0}}c[h>>2]=-1;t=c[h>>2]|0;i=u;return t|0}function wi(){return 0}function xi(a,b,d,e,f,h,j){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;u=i;i=i+48|0;v=u+40|0;k=u+36|0;l=u+32|0;m=u+28|0;n=u+24|0;o=u+20|0;p=u+16|0;t=u+12|0;s=u+8|0;r=u+4|0;q=u;c[v>>2]=a;c[k>>2]=b;c[l>>2]=d;c[m>>2]=e;c[n>>2]=f;c[o>>2]=h;c[p>>2]=j;c[t>>2]=c[v>>2];c[r>>2]=0;while(1){if((c[r>>2]|0)>=(c[l>>2]|0))break;v=_((c[r>>2]|0)+(c[m>>2]|0)|0,c[p>>2]|0)|0;g[(c[k>>2]|0)+(c[r>>2]<<2)>>2]=+g[(c[t>>2]|0)+(v+(c[n>>2]|0)<<2)>>2]*32768.0;c[r>>2]=(c[r>>2]|0)+1}a:do if((c[o>>2]|0)<=-1){if((c[o>>2]|0)==-2){c[q>>2]=1;while(1){if((c[q>>2]|0)>=(c[p>>2]|0))break a;c[r>>2]=0;while(1){if((c[r>>2]|0)>=(c[l>>2]|0))break;d=_((c[r>>2]|0)+(c[m>>2]|0)|0,c[p>>2]|0)|0;v=(c[k>>2]|0)+(c[r>>2]<<2)|0;g[v>>2]=+g[v>>2]+ +g[(c[t>>2]|0)+(d+(c[q>>2]|0)<<2)>>2]*32768.0;c[r>>2]=(c[r>>2]|0)+1}c[q>>2]=(c[q>>2]|0)+1}}}else{c[r>>2]=0;while(1){if((c[r>>2]|0)>=(c[l>>2]|0))break a;d=_((c[r>>2]|0)+(c[m>>2]|0)|0,c[p>>2]|0)|0;v=(c[k>>2]|0)+(c[r>>2]<<2)|0;g[v>>2]=+g[v>>2]+ +g[(c[t>>2]|0)+(d+(c[o>>2]|0)<<2)>>2]*32768.0;c[r>>2]=(c[r>>2]|0)+1}}while(0);g[s>>2]=1.0;if((c[p>>2]|0)==-2)g[s>>2]=+g[s>>2]/+(c[p>>2]|0);else g[s>>2]=+g[s>>2]/2.0;c[r>>2]=0;while(1){if((c[r>>2]|0)>=(c[l>>2]|0))break;v=(c[k>>2]|0)+(c[r>>2]<<2)|0;g[v>>2]=+g[v>>2]*+g[s>>2];c[r>>2]=(c[r>>2]|0)+1}i=u;return}function yi(a,d,e,f,h,j,k){a=a|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;var l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0;v=i;i=i+48|0;w=v+40|0;l=v+36|0;m=v+32|0;n=v+28|0;o=v+24|0;p=v+20|0;q=v+16|0;u=v+12|0;t=v+8|0;s=v+4|0;r=v;c[w>>2]=a;c[l>>2]=d;c[m>>2]=e;c[n>>2]=f;c[o>>2]=h;c[p>>2]=j;c[q>>2]=k;c[u>>2]=c[w>>2];c[s>>2]=0;while(1){if((c[s>>2]|0)>=(c[m>>2]|0))break;w=_((c[s>>2]|0)+(c[n>>2]|0)|0,c[q>>2]|0)|0;g[(c[l>>2]|0)+(c[s>>2]<<2)>>2]=+(b[(c[u>>2]|0)+(w+(c[o>>2]|0)<<1)>>1]|0);c[s>>2]=(c[s>>2]|0)+1}a:do if((c[p>>2]|0)<=-1){if((c[p>>2]|0)==-2){c[r>>2]=1;while(1){if((c[r>>2]|0)>=(c[q>>2]|0))break a;c[s>>2]=0;while(1){if((c[s>>2]|0)>=(c[m>>2]|0))break;e=_((c[s>>2]|0)+(c[n>>2]|0)|0,c[q>>2]|0)|0;w=(c[l>>2]|0)+(c[s>>2]<<2)|0;g[w>>2]=+g[w>>2]+ +(b[(c[u>>2]|0)+(e+(c[r>>2]|0)<<1)>>1]|0);c[s>>2]=(c[s>>2]|0)+1}c[r>>2]=(c[r>>2]|0)+1}}}else{c[s>>2]=0;while(1){if((c[s>>2]|0)>=(c[m>>2]|0))break a;e=_((c[s>>2]|0)+(c[n>>2]|0)|0,c[q>>2]|0)|0;w=(c[l>>2]|0)+(c[s>>2]<<2)|0;g[w>>2]=+g[w>>2]+ +(b[(c[u>>2]|0)+(e+(c[p>>2]|0)<<1)>>1]|0);c[s>>2]=(c[s>>2]|0)+1}}while(0);g[t>>2]=.000030517578125;if((c[q>>2]|0)==-2)g[t>>2]=+g[t>>2]/+(c[q>>2]|0);else g[t>>2]=+g[t>>2]/2.0;c[s>>2]=0;while(1){if((c[s>>2]|0)>=(c[m>>2]|0))break;w=(c[l>>2]|0)+(c[s>>2]<<2)|0;g[w>>2]=+g[w>>2]*+g[t>>2];c[s>>2]=(c[s>>2]|0)+1}i=v;return}function zi(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0;k=i;i=i+32|0;f=k+16|0;g=k+12|0;e=k+8|0;h=k+4|0;j=k;c[g>>2]=a;c[e>>2]=b;c[h>>2]=d;if((c[g>>2]|0)<((c[h>>2]|0)/400|0|0)){c[f>>2]=-1;j=c[f>>2]|0;i=k;return j|0}do if((c[e>>2]|0)==5e3)c[j>>2]=c[g>>2];else{if((c[e>>2]|0)==5010){c[j>>2]=(c[h>>2]|0)/50|0;break}if(!((c[e>>2]|0)>=5001&(c[e>>2]|0)<=5006)){c[f>>2]=-1;j=c[f>>2]|0;i=k;return j|0}d=c[h>>2]|0;if((((c[h>>2]|0)*3|0)/50|0|0)<(((c[h>>2]|0)/400|0)<<(c[e>>2]|0)-5001|0))d=(d*3|0)/50|0;else d=((d|0)/400|0)<<(c[e>>2]|0)-5001;c[j>>2]=d}while(0);if((c[j>>2]|0)>(c[g>>2]|0)){c[f>>2]=-1;j=c[f>>2]|0;i=k;return j|0}if(((((((c[j>>2]|0)*400|0)!=(c[h>>2]|0)?((c[j>>2]|0)*200|0)!=(c[h>>2]|0):0)?((c[j>>2]|0)*100|0)!=(c[h>>2]|0):0)?((c[j>>2]|0)*50|0)!=(c[h>>2]|0):0)?((c[j>>2]|0)*25|0)!=(c[h>>2]|0):0)?((c[j>>2]|0)*50|0)!=((c[h>>2]|0)*3|0):0){c[f>>2]=-1;j=c[f>>2]|0;i=k;return j|0}c[f>>2]=c[j>>2];j=c[f>>2]|0;i=k;return j|0}function Ai(a,b,d,e,f,g,h,j,k){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;k=k|0;var l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0;w=i;i=i+48|0;l=w+40|0;m=w+36|0;n=w+32|0;o=w+28|0;p=w+24|0;q=w+20|0;r=w+16|0;s=w+12|0;t=w+8|0;u=w+4|0;v=w;c[m>>2]=a;c[n>>2]=b;c[o>>2]=d;c[p>>2]=e;c[q>>2]=f;c[r>>2]=g;c[s>>2]=h;c[t>>2]=j;c[u>>2]=k;if((c[o>>2]|0)==5010?(c[n>>2]|0)>=((c[q>>2]|0)/200|0|0):0){c[v>>2]=3;c[v>>2]=Bi(c[m>>2]|0,c[n>>2]|0,c[p>>2]|0,c[q>>2]|0,c[r>>2]|0,0.0,c[u>>2]|0,c[s>>2]|0,c[t>>2]|0)|0;while(1){if((((c[q>>2]|0)/400|0)<<c[v>>2]|0)<=(c[n>>2]|0))break;c[v>>2]=(c[v>>2]|0)+-1}c[n>>2]=((c[q>>2]|0)/400|0)<<c[v>>2]}else c[n>>2]=zi(c[n>>2]|0,c[o>>2]|0,c[q>>2]|0)|0;if((c[n>>2]|0)<0){c[l>>2]=-1;d=c[l>>2]|0;i=w;return d|0}else{c[l>>2]=c[n>>2];d=c[l>>2]|0;i=w;return d|0}return 0}function Bi(a,b,d,e,f,h,j,k,l){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=+h;j=j|0;k=k|0;l=l|0;var m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0;H=i;i=i+304|0;n=H+296|0;m=H+292|0;o=H+288|0;I=H+284|0;p=H+280|0;q=H+276|0;r=H+272|0;s=H+268|0;t=H+264|0;v=H+260|0;z=H+256|0;x=H+144|0;y=H+36|0;B=H+32|0;w=H+28|0;E=H+24|0;D=H+20|0;C=H+16|0;u=H+12|0;F=H+8|0;G=H+4|0;A=H;c[n>>2]=a;c[m>>2]=b;c[o>>2]=d;c[I>>2]=e;c[p>>2]=f;g[q>>2]=h;c[r>>2]=j;c[s>>2]=k;c[t>>2]=l;c[w>>2]=0;c[E>>2]=(c[I>>2]|0)/400|0;a=c[E>>2]|0;c[u>>2]=ia()|0;e=i;i=i+((1*(a<<2)|0)+15&-16)|0;g[x>>2]=+g[c[r>>2]>>2];g[y>>2]=1.0/(+g[c[r>>2]>>2]+1.0000000036274937e-15);if(c[s>>2]|0){c[C>>2]=(c[E>>2]<<1)-(c[s>>2]|0);c[m>>2]=(c[m>>2]|0)-(c[C>>2]|0);g[x+4>>2]=+g[(c[r>>2]|0)+4>>2];g[y+4>>2]=1.0/(+g[(c[r>>2]|0)+4>>2]+1.0000000036274937e-15);g[x+8>>2]=+g[(c[r>>2]|0)+8>>2];g[y+8>>2]=1.0/(+g[(c[r>>2]|0)+8>>2]+1.0000000036274937e-15);c[D>>2]=3}else{c[D>>2]=1;c[C>>2]=0}if(((c[m>>2]|0)/(c[E>>2]|0)|0|0)<24)d=(c[m>>2]|0)/(c[E>>2]|0)|0;else d=24;c[v>>2]=d;g[B>>2]=0.0;c[z>>2]=0;while(1){if((c[z>>2]|0)>=(c[v>>2]|0))break;g[F>>2]=1.0000000036274937e-15;I=_(c[z>>2]|0,c[E>>2]|0)|0;qa[c[t>>2]&3](c[n>>2]|0,e,c[E>>2]|0,I+(c[C>>2]|0)|0,0,-2,c[o>>2]|0);if(!(c[z>>2]|0))g[B>>2]=+g[e>>2];c[A>>2]=0;while(1){if((c[A>>2]|0)>=(c[E>>2]|0))break;g[G>>2]=+g[e+(c[A>>2]<<2)>>2];g[F>>2]=+g[F>>2]+(+g[G>>2]-+g[B>>2])*(+g[G>>2]-+g[B>>2]);g[B>>2]=+g[G>>2];c[A>>2]=(c[A>>2]|0)+1}g[x+((c[z>>2]|0)+(c[D>>2]|0)<<2)>>2]=+g[F>>2];g[y+((c[z>>2]|0)+(c[D>>2]|0)<<2)>>2]=1.0/+g[F>>2];c[z>>2]=(c[z>>2]|0)+1}g[x+((c[z>>2]|0)+(c[D>>2]|0)<<2)>>2]=+g[x+((c[z>>2]|0)+(c[D>>2]|0)-1<<2)>>2];if(c[s>>2]|0)c[v>>2]=24<((c[v>>2]|0)+2|0)?24:(c[v>>2]|0)+2|0;c[w>>2]=Ci(x,y,c[v>>2]|0,~~((+g[q>>2]*.5+1.0)*+(((c[o>>2]|0)*60|0)+40|0)),(c[p>>2]|0)/400|0)|0;g[c[r>>2]>>2]=+g[x+(1<<c[w>>2]<<2)>>2];if(!(c[s>>2]|0)){I=c[w>>2]|0;G=c[u>>2]|0;na(G|0);i=H;return I|0}g[(c[r>>2]|0)+4>>2]=+g[x+((1<<c[w>>2])+1<<2)>>2];g[(c[r>>2]|0)+8>>2]=+g[x+((1<<c[w>>2])+2<<2)>>2];I=c[w>>2]|0;G=c[u>>2]|0;na(G|0);i=H;return I|0}function Ci(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var h=0.0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0.0;z=i;i=i+3136|0;j=z+3124|0;k=z+3120|0;l=z+3116|0;m=z+3112|0;n=z+3108|0;x=z+3104|0;p=z+1568|0;y=z+32|0;o=z+28|0;w=z+24|0;r=z+20|0;s=z+16|0;t=z+12|0;u=z+8|0;q=z+4|0;v=z;c[j>>2]=a;c[k>>2]=b;c[l>>2]=d;c[m>>2]=e;c[n>>2]=f;do if((c[n>>2]|0)>=80)if((c[n>>2]|0)>160){g[r>>2]=1.0;break}else{g[r>>2]=(+(c[n>>2]|0)-80.0)/80.0;break}else g[r>>2]=0.0;while(0);c[x>>2]=0;while(1){if((c[x>>2]|0)>=16)break;c[y+(c[x>>2]<<2)>>2]=-1;g[p+(c[x>>2]<<2)>>2]=1.0e10;c[x>>2]=(c[x>>2]|0)+1}c[x>>2]=0;while(1){if((c[x>>2]|0)>=4)break;A=+((c[m>>2]|0)+(_(c[n>>2]|0,1<<c[x>>2])|0)|0);h=+g[r>>2];h=A*(h*+Di(c[j>>2]|0,c[k>>2]|0,c[x>>2]|0,(c[l>>2]|0)+1|0)+1.0);g[p+(1<<c[x>>2]<<2)>>2]=h;c[y+(1<<c[x>>2]<<2)>>2]=c[x>>2];c[x>>2]=(c[x>>2]|0)+1}c[x>>2]=1;while(1){if((c[x>>2]|0)>=(c[l>>2]|0))break;c[s>>2]=2;while(1){if((c[s>>2]|0)>=16)break;g[p+(c[x>>2]<<6)+(c[s>>2]<<2)>>2]=+g[p+((c[x>>2]|0)-1<<6)+((c[s>>2]|0)-1<<2)>>2];c[y+(c[x>>2]<<6)+(c[s>>2]<<2)>>2]=(c[s>>2]|0)-1;c[s>>2]=(c[s>>2]|0)+1}c[s>>2]=0;while(1){if((c[s>>2]|0)>=4)break;c[y+(c[x>>2]<<6)+(1<<c[s>>2]<<2)>>2]=1;g[u>>2]=+g[p+((c[x>>2]|0)-1<<6)+4>>2];c[t>>2]=1;while(1){if((c[t>>2]|0)>=4)break;g[v>>2]=+g[p+((c[x>>2]|0)-1<<6)+((1<<(c[t>>2]|0)+1)-1<<2)>>2];if(+g[v>>2]<+g[u>>2]){c[y+(c[x>>2]<<6)+(1<<c[s>>2]<<2)>>2]=(1<<(c[t>>2]|0)+1)-1;g[u>>2]=+g[v>>2]}c[t>>2]=(c[t>>2]|0)+1}A=+((c[m>>2]|0)+(_(c[n>>2]|0,1<<c[s>>2])|0)|0);h=+g[r>>2];g[q>>2]=A*(h*+Di((c[j>>2]|0)+(c[x>>2]<<2)|0,(c[k>>2]|0)+(c[x>>2]<<2)|0,c[s>>2]|0,(c[l>>2]|0)-(c[x>>2]|0)+1|0)+1.0);g[p+(c[x>>2]<<6)+(1<<c[s>>2]<<2)>>2]=+g[u>>2];h=+g[q>>2];if(((c[l>>2]|0)-(c[x>>2]|0)|0)<(1<<c[s>>2]|0)){f=p+(c[x>>2]<<6)+(1<<c[s>>2]<<2)|0;g[f>>2]=+g[f>>2]+h*+((c[l>>2]|0)-(c[x>>2]|0)|0)/+(1<<c[s>>2]|0)}else{f=p+(c[x>>2]<<6)+(1<<c[s>>2]<<2)|0;g[f>>2]=+g[f>>2]+h}c[s>>2]=(c[s>>2]|0)+1}c[x>>2]=(c[x>>2]|0)+1}c[w>>2]=1;g[o>>2]=+g[p+((c[l>>2]|0)-1<<6)+4>>2];c[x>>2]=2;while(1){if((c[x>>2]|0)>=16)break;if(+g[p+((c[l>>2]|0)-1<<6)+(c[x>>2]<<2)>>2]<+g[o>>2]){g[o>>2]=+g[p+((c[l>>2]|0)-1<<6)+(c[x>>2]<<2)>>2];c[w>>2]=c[x>>2]}c[x>>2]=(c[x>>2]|0)+1}c[x>>2]=(c[l>>2]|0)-1;while(1){a=c[w>>2]|0;if((c[x>>2]|0)<0)break;c[w>>2]=c[y+(c[x>>2]<<6)+(a<<2)>>2];c[x>>2]=(c[x>>2]|0)+-1}i=z;return a|0}function Di(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0.0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;r=i;i=i+48|0;k=r+32|0;l=r+28|0;h=r+24|0;j=r+20|0;n=r+16|0;m=r+12|0;o=r+8|0;p=r+4|0;q=r;c[k>>2]=a;c[l>>2]=b;c[h>>2]=d;c[j>>2]=e;g[o>>2]=0.0;g[p>>2]=0.0;if((c[j>>2]|0)<((1<<c[h>>2])+1|0))h=c[j>>2]|0;else h=(1<<c[h>>2])+1|0;c[m>>2]=h;c[n>>2]=0;while(1){if((c[n>>2]|0)>=(c[m>>2]|0))break;g[o>>2]=+g[o>>2]+ +g[(c[k>>2]|0)+(c[n>>2]<<2)>>2];g[p>>2]=+g[p>>2]+ +g[(c[l>>2]|0)+(c[n>>2]<<2)>>2];c[n>>2]=(c[n>>2]|0)+1}g[q>>2]=+g[o>>2]*+g[p>>2]/+(_(c[m>>2]|0,c[m>>2]|0)|0);if(0.0>(+g[q>>2]-2.0)*.05000000074505806)f=0.0;else f=(+g[q>>2]-2.0)*.05000000074505806;if(1.0<+O(+f)){f=1.0;i=r;return +f}if(0.0>(+g[q>>2]-2.0)*.05000000074505806)f=0.0;else f=(+g[q>>2]-2.0)*.05000000074505806;f=+O(+f);i=r;return +f}function Ei(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0.0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0;D=i;i=i+96|0;h=D+84|0;j=D+80|0;E=D+76|0;C=D+72|0;u=D+68|0;v=D+64|0;A=D+60|0;q=D+56|0;r=D+52|0;t=D+48|0;y=D+44|0;z=D+40|0;w=D+36|0;x=D+32|0;B=D+28|0;k=D+24|0;o=D+20|0;l=D+16|0;m=D+12|0;n=D+8|0;p=D+4|0;s=D;c[h>>2]=a;c[j>>2]=b;c[E>>2]=d;c[C>>2]=e;c[B>>2]=(c[E>>2]|0)/(c[j>>2]|0)|0;g[o>>2]=1.0-25.0/+((50>(c[B>>2]|0)?50:c[B>>2]|0)|0);g[t>>2]=0.0;g[r>>2]=0.0;g[q>>2]=0.0;c[k>>2]=0;while(1){if((c[k>>2]|0)>=(c[j>>2]|0))break;g[l>>2]=0.0;g[m>>2]=0.0;g[n>>2]=0.0;g[p>>2]=+g[(c[h>>2]|0)+(c[k>>2]<<1<<2)>>2];g[s>>2]=+g[(c[h>>2]|0)+((c[k>>2]<<1)+1<<2)>>2];g[l>>2]=+g[p>>2]*+g[p>>2];g[m>>2]=+g[p>>2]*+g[s>>2];g[n>>2]=+g[s>>2]*+g[s>>2];g[p>>2]=+g[(c[h>>2]|0)+((c[k>>2]<<1)+2<<2)>>2];g[s>>2]=+g[(c[h>>2]|0)+((c[k>>2]<<1)+3<<2)>>2];g[l>>2]=+g[l>>2]+ +g[p>>2]*+g[p>>2];g[m>>2]=+g[m>>2]+ +g[p>>2]*+g[s>>2];g[n>>2]=+g[n>>2]+ +g[s>>2]*+g[s>>2];g[p>>2]=+g[(c[h>>2]|0)+((c[k>>2]<<1)+4<<2)>>2];g[s>>2]=+g[(c[h>>2]|0)+((c[k>>2]<<1)+5<<2)>>2];g[l>>2]=+g[l>>2]+ +g[p>>2]*+g[p>>2];g[m>>2]=+g[m>>2]+ +g[p>>2]*+g[s>>2];g[n>>2]=+g[n>>2]+ +g[s>>2]*+g[s>>2];g[p>>2]=+g[(c[h>>2]|0)+((c[k>>2]<<1)+6<<2)>>2];g[s>>2]=+g[(c[h>>2]|0)+((c[k>>2]<<1)+7<<2)>>2];g[l>>2]=+g[l>>2]+ +g[p>>2]*+g[p>>2];g[m>>2]=+g[m>>2]+ +g[p>>2]*+g[s>>2];g[n>>2]=+g[n>>2]+ +g[s>>2]*+g[s>>2];g[q>>2]=+g[q>>2]+ +g[l>>2];g[r>>2]=+g[r>>2]+ +g[m>>2];g[t>>2]=+g[t>>2]+ +g[n>>2];c[k>>2]=(c[k>>2]|0)+4}E=c[C>>2]|0;g[E>>2]=+g[E>>2]+ +g[o>>2]*(+g[q>>2]-+g[c[C>>2]>>2]);E=(c[C>>2]|0)+4|0;g[E>>2]=+g[E>>2]+ +g[o>>2]*(+g[r>>2]-+g[(c[C>>2]|0)+4>>2]);E=(c[C>>2]|0)+8|0;g[E>>2]=+g[E>>2]+ +g[o>>2]*(+g[t>>2]-+g[(c[C>>2]|0)+8>>2]);if(0.0>+g[c[C>>2]>>2])f=0.0;else f=+g[c[C>>2]>>2];g[c[C>>2]>>2]=f;if(0.0>+g[(c[C>>2]|0)+4>>2])f=0.0;else f=+g[(c[C>>2]|0)+4>>2];g[(c[C>>2]|0)+4>>2]=f;if(0.0>+g[(c[C>>2]|0)+8>>2])f=0.0;else f=+g[(c[C>>2]|0)+8>>2];g[(c[C>>2]|0)+8>>2]=f;h=c[C>>2]|0;if(+g[c[C>>2]>>2]>+g[(c[C>>2]|0)+8>>2])f=+g[h>>2];else f=+g[h+8>>2];if(f>7.999999797903001e-04){g[y>>2]=+O(+(+g[c[C>>2]>>2]));g[z>>2]=+O(+(+g[(c[C>>2]|0)+8>>2]));g[w>>2]=+O(+(+g[y>>2]));g[x>>2]=+O(+(+g[z>>2]));if(+g[(c[C>>2]|0)+4>>2]<+g[y>>2]*+g[z>>2])f=+g[(c[C>>2]|0)+4>>2];else f=+g[y>>2]*+g[z>>2];g[(c[C>>2]|0)+4>>2]=f;g[u>>2]=+g[(c[C>>2]|0)+4>>2]/(+g[y>>2]*+g[z>>2]+1.0000000036274937e-15);f=+N(+(+g[w>>2]-+g[x>>2]))*1.0;g[v>>2]=f/(+g[w>>2]+1.0000000036274937e-15+ +g[x>>2]);f=+O(+(1.0-+g[u>>2]*+g[u>>2]));g[A>>2]=f*+g[v>>2];h=(c[C>>2]|0)+12|0;g[h>>2]=+g[h>>2]+(+g[A>>2]-+g[(c[C>>2]|0)+12>>2])/+(c[B>>2]|0);h=c[C>>2]|0;if(+g[(c[C>>2]|0)+16>>2]-.019999999552965164/+(c[B>>2]|0)>+g[(c[C>>2]|0)+12>>2])f=+g[h+16>>2]-.019999999552965164/+(c[B>>2]|0);else f=+g[h+12>>2];g[(c[C>>2]|0)+16>>2]=f}else{g[A>>2]=0.0;g[u>>2]=1.0;g[v>>2]=0.0}if(1.0<+g[(c[C>>2]|0)+16>>2]*20.0){f=1.0;i=D;return +f}f=+g[(c[C>>2]|0)+16>>2]*20.0;i=D;return +f}function Fi(e,f,h,j,k,l,m,n,o,p,q,r,s){e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;o=o|0;p=p|0;q=q|0;r=r|0;s=s|0;var t=0.0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0,ha=0,ja=0,ka=0,la=0,ma=0,oa=0,pa=0,qa=0,ra=0,sa=0,ta=0,ua=0,va=0,wa=0,xa=0,ya=0,za=0,Aa=0,Ba=0,Ca=0,Da=0,Ea=0,Fa=0,Ga=0,Ha=0,Ia=0,Ja=0,Ka=0,La=0,Ma=0,Na=0,Oa=0,Pa=0,Qa=0,Ra=0,Sa=0,Ta=0,Ua=0,Va=0,Wa=0,Xa=0,Ya=0,Za=0,_a=0,$a=0,ab=0,bb=0,cb=0,db=0,eb=0,fb=0,gb=0,hb=0,ib=0,jb=0,kb=0,mb=0,ob=0,pb=0,qb=0,rb=0,sb=0,tb=0,ub=0,vb=0,wb=0,xb=0,yb=0,zb=0,Ab=0,Bb=0,Cb=0,Db=0,Eb=0,Fb=0,Gb=0,Hb=0,Ib=0,Jb=0,Kb=0,Lb=0,Mb=0,Nb=0,Ob=0,Pb=0,Qb=0,Rb=0;Rb=i;i=i+1072|0;Db=Rb+168|0;Cb=Rb+160|0;Bb=Rb+152|0;Ab=Rb+144|0;zb=Rb+136|0;yb=Rb+128|0;xb=Rb+120|0;wb=Rb+112|0;vb=Rb+104|0;ub=Rb+96|0;tb=Rb+88|0;sb=Rb+80|0;Ua=Rb+72|0;Ta=Rb+64|0;Sa=Rb+56|0;Ra=Rb+48|0;Qa=Rb+40|0;Pa=Rb+32|0;Oa=Rb+24|0;Na=Rb+16|0;U=Rb+8|0;w=Rb;Ob=Rb+1048|0;Pb=Rb+1044|0;sa=Rb+1040|0;Gb=Rb+1036|0;Qb=Rb+1032|0;aa=Rb+1028|0;ba=Rb+1024|0;u=Rb+1020|0;v=Rb+1016|0;ca=Rb+1012|0;Y=Rb+1008|0;Z=Rb+1004|0;$=Rb+1e3|0;ta=Rb+996|0;Ga=Rb+992|0;gb=Rb+988|0;Xa=Rb+984|0;Nb=Rb+980|0;Ma=Rb+976|0;Hb=Rb+928|0;Va=Rb+920|0;Ea=Rb+916|0;rb=Rb+912|0;Jb=Rb+908|0;Kb=Rb+904|0;hb=Rb+900|0;pb=Rb+896|0;Lb=Rb+892|0;qb=Rb+888|0;W=Rb+884|0;V=Rb+880|0;K=Rb+876|0;Za=Rb+872|0;y=Rb+868|0;Da=Rb+864|0;Ba=Rb+860|0;ib=Rb+856|0;Ya=Rb+852|0;Ib=Rb+848|0;_a=Rb+844|0;F=Rb+840|0;$a=Rb+836|0;fb=Rb+808|0;S=Rb+804|0;T=Rb+800|0;x=Rb+796|0;B=Rb+792|0;z=Rb+788|0;A=Rb+784|0;C=Rb+780|0;E=Rb+776|0;D=Rb+772|0;G=Rb+768|0;H=Rb+680|0;J=Rb+676|0;I=Rb+672|0;N=Rb+640|0;L=Rb+632|0;O=Rb+628|0;Q=Rb+624|0;P=Rb+620|0;R=Rb+616|0;oa=Rb+612|0;ka=Rb+608|0;ha=Rb+604|0;ja=Rb+600|0;la=Rb+596|0;da=Rb+592|0;pa=Rb+588|0;fa=Rb+584|0;qa=Rb+284|0;ea=Rb+280|0;Mb=Rb+276|0;Fb=Rb+272|0;X=Rb+268|0;ra=Rb+264|0;ma=Rb+260|0;Ia=Rb+256|0;ga=Rb+252|0;xa=Rb+248|0;ya=Rb+244|0;Aa=Rb+240|0;ua=Rb+236|0;va=Rb+232|0;za=Rb+1052|0;wa=Rb+228|0;Ca=Rb+224|0;Ha=Rb+220|0;Fa=Rb+216|0;La=Rb+212|0;Ka=Rb+208|0;Wa=Rb+204|0;Ja=Rb+200|0;ab=Rb+196|0;bb=Rb+192|0;cb=Rb+188|0;mb=Rb+184|0;jb=Rb+1056|0;ob=Rb+180|0;kb=Rb+1054|0;db=Rb+176|0;eb=Rb+172|0;c[Pb>>2]=e;c[sa>>2]=f;c[Gb>>2]=h;c[Qb>>2]=j;c[aa>>2]=k;c[ba>>2]=l;c[u>>2]=m;c[v>>2]=n;c[ca>>2]=o;c[Y>>2]=p;c[Z>>2]=q;c[$>>2]=r;c[ta>>2]=s;c[Nb>>2]=0;c[Ea>>2]=0;c[rb>>2]=0;c[Jb>>2]=0;c[Kb>>2]=0;c[hb>>2]=0;c[Lb>>2]=0;c[qb>>2]=0;c[S>>2]=-1;c[T>>2]=-1;c[Ib>>2]=1276<(c[aa>>2]|0)?1276:c[aa>>2]|0;c[(c[Pb>>2]|0)+18216>>2]=0;if(!((((((!(c[(c[Pb>>2]|0)+144>>2]|0)?((c[Gb>>2]|0)*400|0)!=(c[(c[Pb>>2]|0)+132>>2]|0):0)?((c[Gb>>2]|0)*200|0)!=(c[(c[Pb>>2]|0)+132>>2]|0):0)?((c[Gb>>2]|0)*100|0)!=(c[(c[Pb>>2]|0)+132>>2]|0):0)?((c[Gb>>2]|0)*50|0)!=(c[(c[Pb>>2]|0)+132>>2]|0):0)?((c[Gb>>2]|0)*25|0)!=(c[(c[Pb>>2]|0)+132>>2]|0):0)?((c[Gb>>2]|0)*50|0)!=((c[(c[Pb>>2]|0)+132>>2]|0)*3|0):0))Eb=8;if((Eb|0)==8?!((c[Ib>>2]|0)<=0?1:((c[Gb>>2]|0)*400|0)<(c[(c[Pb>>2]|0)+132>>2]|0)):0){c[Ga>>2]=(c[Pb>>2]|0)+(c[(c[Pb>>2]|0)+4>>2]|0);c[gb>>2]=(c[Pb>>2]|0)+(c[c[Pb>>2]>>2]|0);if((c[(c[Pb>>2]|0)+96>>2]|0)==2051)c[y>>2]=0;else c[y>>2]=c[(c[Pb>>2]|0)+104>>2];if((c[ba>>2]|0)<(c[(c[Pb>>2]|0)+156>>2]|0))q=c[ba>>2]|0;else q=c[(c[Pb>>2]|0)+156>>2]|0;c[ba>>2]=q;e=c[gb>>2]|0;c[w>>2]=$a+((($a-$a|0)/4|0)<<2);lb(e,10015,w)|0;c[fb>>2]=0;if((c[(c[Pb>>2]|0)+8+36>>2]|0)>=7?(c[(c[Pb>>2]|0)+132>>2]|0)==48e3:0){c[S>>2]=c[(c[Pb>>2]|0)+172+8508>>2];c[T>>2]=c[(c[Pb>>2]|0)+172+8512>>2];aj((c[Pb>>2]|0)+172|0,c[$a>>2]|0,c[u>>2]|0,c[v>>2]|0,c[Gb>>2]|0,c[ca>>2]|0,c[Y>>2]|0,c[Z>>2]|0,c[(c[Pb>>2]|0)+132>>2]|0,c[ba>>2]|0,c[$>>2]|0,fb)}c[(c[Pb>>2]|0)+128>>2]=-1;c[(c[Pb>>2]|0)+18212>>2]=0;do if(c[fb>>2]|0){if((c[(c[Pb>>2]|0)+112>>2]|0)==-1e3){w=~~+M(+((1.0-+g[fb+20>>2])*100.0+.5));c[(c[Pb>>2]|0)+128>>2]=w}c[x>>2]=c[fb+24>>2];if((c[x>>2]|0)<=12){c[(c[Pb>>2]|0)+18212>>2]=1101;break}if((c[x>>2]|0)<=14){c[(c[Pb>>2]|0)+18212>>2]=1102;break}if((c[x>>2]|0)<=16){c[(c[Pb>>2]|0)+18212>>2]=1103;break}q=(c[Pb>>2]|0)+18212|0;if((c[x>>2]|0)<=18){c[q>>2]=1104;break}else{c[q>>2]=1105;break}}while(0);if((c[(c[Pb>>2]|0)+100>>2]|0)==2?(c[(c[Pb>>2]|0)+108>>2]|0)!=1:0)g[F>>2]=+Ei(c[sa>>2]|0,c[Gb>>2]|0,c[(c[Pb>>2]|0)+132>>2]|0,(c[Pb>>2]|0)+14352|0);else g[F>>2]=0.0;c[_a>>2]=c[y>>2];y=Gi(c[Pb>>2]|0,c[Gb>>2]|0,c[Ib>>2]|0)|0;c[(c[Pb>>2]|0)+148>>2]=y;c[Da>>2]=(c[(c[Pb>>2]|0)+132>>2]|0)/(c[Gb>>2]|0)|0;do if((c[Ib>>2]|0)>=3?(c[(c[Pb>>2]|0)+148>>2]|0)>=((c[Da>>2]|0)*3<<3|0):0){if((c[Da>>2]|0)<50){if((_(c[Ib>>2]|0,c[Da>>2]|0)|0)<300)break;if((c[(c[Pb>>2]|0)+148>>2]|0)<2400)break}if(!(c[(c[Pb>>2]|0)+136>>2]|0)){if((((c[(c[Pb>>2]|0)+148>>2]|0)+(c[Da>>2]<<2)|0)/(c[Da>>2]<<3|0)|0|0)<(c[Ib>>2]|0))q=((c[(c[Pb>>2]|0)+148>>2]|0)+(c[Da>>2]<<2)|0)/(c[Da>>2]<<3|0)|0;else q=c[Ib>>2]|0;c[A>>2]=q;B=_(c[A>>2]|0,c[Da>>2]<<3)|0;c[(c[Pb>>2]|0)+148>>2]=B;c[Ib>>2]=c[A>>2]}c[Ba>>2]=(_(c[Da>>2]|0,c[Ib>>2]|0)|0)<<3;c[Za>>2]=(c[(c[Pb>>2]|0)+148>>2]|0)-(_(((c[(c[Pb>>2]|0)+100>>2]|0)*40|0)+20|0,((c[(c[Pb>>2]|0)+132>>2]|0)/(c[Gb>>2]|0)|0)-50|0)|0);do if((c[(c[Pb>>2]|0)+112>>2]|0)!=3001){if((c[(c[Pb>>2]|0)+112>>2]|0)==3002){c[K>>2]=0;break}q=c[Pb>>2]|0;if((c[(c[Pb>>2]|0)+128>>2]|0)>=0){c[K>>2]=(c[q+128>>2]|0)*327>>8;if((c[(c[Pb>>2]|0)+96>>2]|0)!=2049)break;c[K>>2]=(c[K>>2]|0)<115?c[K>>2]|0:115;break}if((c[q+96>>2]|0)==2048){c[K>>2]=115;break}else{c[K>>2]=48;break}}else c[K>>2]=127;while(0);if((c[(c[Pb>>2]|0)+108>>2]|0)!=-1e3?(c[(c[Pb>>2]|0)+100>>2]|0)==2:0)c[(c[Pb>>2]|0)+14288>>2]=c[(c[Pb>>2]|0)+108>>2];else Eb=73;do if((Eb|0)==73){if((c[(c[Pb>>2]|0)+100>>2]|0)!=2){c[(c[Pb>>2]|0)+14288>>2]=c[(c[Pb>>2]|0)+100>>2];break}c[C>>2]=3e4;q=c[C>>2]|0;if((c[(c[Pb>>2]|0)+14288>>2]|0)==2)c[C>>2]=q-1e3;else c[C>>2]=q+1e3;c[(c[Pb>>2]|0)+14288>>2]=(c[Za>>2]|0)>(c[C>>2]|0)?2:1}while(0);c[Za>>2]=(c[(c[Pb>>2]|0)+148>>2]|0)-(_(((c[(c[Pb>>2]|0)+14288>>2]|0)*40|0)+20|0,((c[(c[Pb>>2]|0)+132>>2]|0)/(c[Gb>>2]|0)|0)-50|0)|0);q=c[Pb>>2]|0;do if((c[(c[Pb>>2]|0)+96>>2]|0)==2051)c[q+14320>>2]=1002;else{if((c[q+124>>2]|0)!=-1e3){c[(c[Pb>>2]|0)+14320>>2]=c[(c[Pb>>2]|0)+124>>2];break}c[E>>2]=~~((1.0-+g[F>>2])*+(c[4508]|0)+ +g[F>>2]*+(c[4510]|0));c[D>>2]=~~((1.0-+g[F>>2])*+(c[4511]|0)+ +g[F>>2]*+(c[4511]|0));F=_(c[K>>2]|0,c[K>>2]|0)|0;c[G>>2]=(c[D>>2]|0)+((_(F,(c[E>>2]|0)-(c[D>>2]|0)|0)|0)>>14);if((c[(c[Pb>>2]|0)+96>>2]|0)==2048)c[G>>2]=(c[G>>2]|0)+8e3;do if((c[(c[Pb>>2]|0)+14324>>2]|0)==1002)c[G>>2]=(c[G>>2]|0)-4e3;else{if((c[(c[Pb>>2]|0)+14324>>2]|0)<=0)break;c[G>>2]=(c[G>>2]|0)+4e3}while(0);c[(c[Pb>>2]|0)+14320>>2]=(c[Za>>2]|0)>=(c[G>>2]|0)?1002:1e3;do if(c[(c[Pb>>2]|0)+8+40>>2]|0){if((c[(c[Pb>>2]|0)+8+32>>2]|0)<=(128-(c[K>>2]|0)>>4|0))break;c[(c[Pb>>2]|0)+14320>>2]=1e3}while(0);if(!((c[K>>2]|0)>100?(c[(c[Pb>>2]|0)+8+44>>2]|0)!=0:0))break;c[(c[Pb>>2]|0)+14320>>2]=1e3}while(0);if((c[(c[Pb>>2]|0)+14320>>2]|0)!=1002?(c[Gb>>2]|0)<((c[(c[Pb>>2]|0)+132>>2]|0)/100|0|0):0)c[(c[Pb>>2]|0)+14320>>2]=1002;if(c[(c[Pb>>2]|0)+164>>2]|0)c[(c[Pb>>2]|0)+14320>>2]=1002;G=_((c[Da>>2]|0)>50?12e3:8e3,c[Gb>>2]|0)|0;if((c[Ib>>2]|0)<((G|0)/(c[(c[Pb>>2]|0)+132>>2]<<3|0)|0|0))c[(c[Pb>>2]|0)+14320>>2]=1002;do if((c[(c[Pb>>2]|0)+14288>>2]|0)==1){if((c[(c[Pb>>2]|0)+14328>>2]|0)!=2){Eb=107;break}if(c[(c[Pb>>2]|0)+8+56>>2]|0){Eb=107;break}if((c[(c[Pb>>2]|0)+14320>>2]|0)==1002){Eb=107;break}if((c[(c[Pb>>2]|0)+14324>>2]|0)==1002){Eb=107;break}c[(c[Pb>>2]|0)+8+56>>2]=1;c[(c[Pb>>2]|0)+14288>>2]=2}else Eb=107;while(0);if((Eb|0)==107)c[(c[Pb>>2]|0)+8+56>>2]=0;do if((c[(c[Pb>>2]|0)+14324>>2]|0)>0){if(!((c[(c[Pb>>2]|0)+14320>>2]|0)!=1002?(c[(c[Pb>>2]|0)+14324>>2]|0)==1002:0)){if((c[(c[Pb>>2]|0)+14320>>2]|0)!=1002)break;if((c[(c[Pb>>2]|0)+14324>>2]|0)==1002)break}c[Jb>>2]=1;c[hb>>2]=(c[(c[Pb>>2]|0)+14320>>2]|0)!=1002&1;if(c[hb>>2]|0)break;if((c[Gb>>2]|0)>=((c[(c[Pb>>2]|0)+132>>2]|0)/100|0|0)){c[(c[Pb>>2]|0)+14320>>2]=c[(c[Pb>>2]|0)+14324>>2];c[Lb>>2]=1;break}else{c[Jb>>2]=0;break}}while(0);if(c[(c[Pb>>2]|0)+14340>>2]|0){c[Jb>>2]=1;c[hb>>2]=1;c[(c[Pb>>2]|0)+14340>>2]=0;c[Ea>>2]=1}do if(c[Jb>>2]|0){G=_(c[Ib>>2]|0,(c[(c[Pb>>2]|0)+132>>2]|0)/200|0)|0;if(257<((G|0)/((c[Gb>>2]|0)+((c[(c[Pb>>2]|0)+132>>2]|0)/200|0)|0)|0|0))q=257;else{q=_(c[Ib>>2]|0,(c[(c[Pb>>2]|0)+132>>2]|0)/200|0)|0;q=(q|0)/((c[Gb>>2]|0)+((c[(c[Pb>>2]|0)+132>>2]|0)/200|0)|0)|0}c[Kb>>2]=q;if(!(c[(c[Pb>>2]|0)+136>>2]|0))break;if((c[Kb>>2]|0)<((c[(c[Pb>>2]|0)+148>>2]|0)/1600|0|0))q=c[Kb>>2]|0;else q=(c[(c[Pb>>2]|0)+148>>2]|0)/1600|0;c[Kb>>2]=q}while(0);do if((c[(c[Pb>>2]|0)+14320>>2]|0)!=1002){if((c[(c[Pb>>2]|0)+14324>>2]|0)!=1002)break;Td(c[Ga>>2]|0,c[(c[Pb>>2]|0)+168>>2]|0,H)|0;c[Ea>>2]=1}while(0);do if((c[(c[Pb>>2]|0)+14320>>2]|0)!=1002){if(c[(c[Pb>>2]|0)+14344>>2]|0){Eb=133;break}if(c[(c[Pb>>2]|0)+8+72>>2]|0)Eb=133}else Eb=133;while(0);do if((Eb|0)==133){c[L>>2]=1105;c[O>>2]=c[Za>>2];do if((c[(c[Pb>>2]|0)+14320>>2]|0)!=1002){c[O>>2]=(_(c[O>>2]|0,45+(c[(c[Pb>>2]|0)+8+36>>2]|0)|0)|0)/50|0;if(c[(c[Pb>>2]|0)+136>>2]|0)break;c[O>>2]=(c[O>>2]|0)-1e3}while(0);do if((c[(c[Pb>>2]|0)+100>>2]|0)==2){if((c[(c[Pb>>2]|0)+108>>2]|0)==1){Eb=139;break}c[J>>2]=18048;c[I>>2]=18080}else Eb=139;while(0);if((Eb|0)==139){c[J>>2]=18112;c[I>>2]=18144}c[Xa>>2]=0;while(1){if((c[Xa>>2]|0)>=8)break;H=_(c[K>>2]|0,c[K>>2]|0)|0;H=(c[(c[I>>2]|0)+(c[Xa>>2]<<2)>>2]|0)+((_(H,(c[(c[J>>2]|0)+(c[Xa>>2]<<2)>>2]|0)-(c[(c[I>>2]|0)+(c[Xa>>2]<<2)>>2]|0)|0)|0)>>14)|0;c[N+(c[Xa>>2]<<2)>>2]=H;c[Xa>>2]=(c[Xa>>2]|0)+1}do{c[Q>>2]=c[N+((c[L>>2]|0)-1102<<1<<2)>>2];c[P>>2]=c[N+(((c[L>>2]|0)-1102<<1)+1<<2)>>2];do if(!(c[(c[Pb>>2]|0)+14344>>2]|0)){q=c[P>>2]|0;m=c[Q>>2]|0;if((c[(c[Pb>>2]|0)+14336>>2]|0)>=(c[L>>2]|0)){c[Q>>2]=m-q;break}else{c[Q>>2]=m+q;break}}while(0);if((c[O>>2]|0)>=(c[Q>>2]|0))break;K=(c[L>>2]|0)+-1|0;c[L>>2]=K}while((K|0)>1101);c[(c[Pb>>2]|0)+14336>>2]=c[L>>2];if(c[(c[Pb>>2]|0)+14344>>2]|0)break;if((c[(c[Pb>>2]|0)+14320>>2]|0)==1002)break;if(c[(c[Pb>>2]|0)+8+76>>2]|0)break;if((c[(c[Pb>>2]|0)+14336>>2]|0)<=1103)break;c[(c[Pb>>2]|0)+14336>>2]=1103}while(0);if((c[(c[Pb>>2]|0)+14336>>2]|0)>(c[(c[Pb>>2]|0)+120>>2]|0))c[(c[Pb>>2]|0)+14336>>2]=c[(c[Pb>>2]|0)+120>>2];if((c[(c[Pb>>2]|0)+116>>2]|0)!=-1e3)c[(c[Pb>>2]|0)+14336>>2]=c[(c[Pb>>2]|0)+116>>2];if((c[Ba>>2]|0)<15e3?(c[(c[Pb>>2]|0)+14320>>2]|0)!=1002:0){if((c[(c[Pb>>2]|0)+14336>>2]|0)<1103)q=c[(c[Pb>>2]|0)+14336>>2]|0;else q=1103;c[(c[Pb>>2]|0)+14336>>2]=q}do if((c[(c[Pb>>2]|0)+132>>2]|0)<=24e3){if((c[(c[Pb>>2]|0)+14336>>2]|0)<=1104)break;c[(c[Pb>>2]|0)+14336>>2]=1104}while(0);do if((c[(c[Pb>>2]|0)+132>>2]|0)<=16e3){if((c[(c[Pb>>2]|0)+14336>>2]|0)<=1103)break;c[(c[Pb>>2]|0)+14336>>2]=1103}while(0);do if((c[(c[Pb>>2]|0)+132>>2]|0)<=12e3){if((c[(c[Pb>>2]|0)+14336>>2]|0)<=1102)break;c[(c[Pb>>2]|0)+14336>>2]=1102}while(0);do if((c[(c[Pb>>2]|0)+132>>2]|0)<=8e3){if((c[(c[Pb>>2]|0)+14336>>2]|0)<=1101)break;c[(c[Pb>>2]|0)+14336>>2]=1101}while(0);do if(c[(c[Pb>>2]|0)+18212>>2]|0){if((c[(c[Pb>>2]|0)+116>>2]|0)!=-1e3)break;do if((c[Za>>2]|0)<=((c[(c[Pb>>2]|0)+14288>>2]|0)*18e3|0)){if((c[(c[Pb>>2]|0)+14320>>2]|0)!=1002){Eb=179;break}c[R>>2]=1101}else Eb=179;while(0);a:do if((Eb|0)==179){do if((c[Za>>2]|0)<=((c[(c[Pb>>2]|0)+14288>>2]|0)*24e3|0)){if((c[(c[Pb>>2]|0)+14320>>2]|0)!=1002)break;c[R>>2]=1102;break a}while(0);if((c[Za>>2]|0)<=((c[(c[Pb>>2]|0)+14288>>2]|0)*3e4|0)){c[R>>2]=1103;break}if((c[Za>>2]|0)<=((c[(c[Pb>>2]|0)+14288>>2]|0)*44e3|0)){c[R>>2]=1104;break}else{c[R>>2]=1105;break}}while(0);if((c[(c[Pb>>2]|0)+18212>>2]|0)>(c[R>>2]|0))q=c[(c[Pb>>2]|0)+18212>>2]|0;else q=c[R>>2]|0;c[(c[Pb>>2]|0)+18212>>2]=q;q=c[Pb>>2]|0;if((c[(c[Pb>>2]|0)+14336>>2]|0)<(c[(c[Pb>>2]|0)+18212>>2]|0))q=c[q+14336>>2]|0;else q=c[q+18212>>2]|0;c[(c[Pb>>2]|0)+14336>>2]=q}while(0);R=c[gb>>2]|0;c[U>>2]=c[ba>>2];lb(R,4036,U)|0;do if((c[(c[Pb>>2]|0)+14320>>2]|0)==1002){if((c[(c[Pb>>2]|0)+14336>>2]|0)!=1102)break;c[(c[Pb>>2]|0)+14336>>2]=1103}while(0);if(c[(c[Pb>>2]|0)+164>>2]|0)c[(c[Pb>>2]|0)+14336>>2]=1101;do if((c[Gb>>2]|0)>((c[(c[Pb>>2]|0)+132>>2]|0)/50|0|0)){if((c[(c[Pb>>2]|0)+14320>>2]|0)!=1002?(c[(c[Pb>>2]|0)+14336>>2]|0)<=1103:0)break;if((c[S>>2]|0)!=-1){c[(c[Pb>>2]|0)+172+8508>>2]=c[S>>2];c[(c[Pb>>2]|0)+172+8512>>2]=c[T>>2]}c[oa>>2]=(c[Gb>>2]|0)>((c[(c[Pb>>2]|0)+132>>2]|0)/25|0|0)?3:2;if(1276<(((c[aa>>2]|0)-3|0)/(c[oa>>2]|0)|0|0))q=1276;else q=((c[aa>>2]|0)-3|0)/(c[oa>>2]|0)|0;c[da>>2]=q;q=_(c[oa>>2]|0,c[da>>2]|0)|0;c[fa>>2]=ia()|0;m=i;i=i+((1*q|0)+15&-16)|0;Ti(qa)|0;c[ka>>2]=c[(c[Pb>>2]|0)+124>>2];c[ha>>2]=c[(c[Pb>>2]|0)+116>>2];c[ja>>2]=c[(c[Pb>>2]|0)+108>>2];c[(c[Pb>>2]|0)+124>>2]=c[(c[Pb>>2]|0)+14320>>2];c[(c[Pb>>2]|0)+116>>2]=c[(c[Pb>>2]|0)+14336>>2];c[(c[Pb>>2]|0)+108>>2]=c[(c[Pb>>2]|0)+14288>>2];c[la>>2]=c[(c[Pb>>2]|0)+8+56>>2];q=c[Pb>>2]|0;if(c[la>>2]|0)c[q+108>>2]=1;else c[(c[Pb>>2]|0)+14328>>2]=c[q+14288>>2];c[Xa>>2]=0;while(1){q=c[Pb>>2]|0;if((c[Xa>>2]|0)>=(c[oa>>2]|0)){Eb=219;break}c[q+8+56>>2]=0;do if(c[Lb>>2]|0){if((c[Xa>>2]|0)!=((c[oa>>2]|0)-1|0))break;c[(c[Pb>>2]|0)+124>>2]=1002}while(0);Jb=(c[sa>>2]|0)+((_(c[Xa>>2]|0,(_(c[(c[Pb>>2]|0)+100>>2]|0,c[(c[Pb>>2]|0)+132>>2]|0)|0)/50|0)|0)<<2)|0;Kb=m+(_(c[Xa>>2]|0,c[da>>2]|0)|0)|0;c[ea>>2]=Fi(c[Pb>>2]|0,Jb,(c[(c[Pb>>2]|0)+132>>2]|0)/50|0,Kb,c[da>>2]|0,c[ba>>2]|0,0,0,c[ca>>2]|0,c[Y>>2]|0,c[Z>>2]|0,c[$>>2]|0,c[ta>>2]|0)|0;if((c[ea>>2]|0)<0){Eb=215;break}Kb=m+(_(c[Xa>>2]|0,c[da>>2]|0)|0)|0;c[Nb>>2]=Ui(qa,Kb,c[ea>>2]|0)|0;if((c[Nb>>2]|0)<0){Eb=217;break}c[Xa>>2]=(c[Xa>>2]|0)+1}do if((Eb|0)==215){c[Ob>>2]=-3;c[Mb>>2]=1}else if((Eb|0)==217){c[Ob>>2]=-3;c[Mb>>2]=1}else if((Eb|0)==219){if(c[q+136>>2]|0)c[pa>>2]=c[aa>>2];else{if((((c[(c[Pb>>2]|0)+148>>2]|0)*3|0)/(1200/(c[oa>>2]|0)|0|0)|0|0)<(c[aa>>2]|0))q=((c[(c[Pb>>2]|0)+148>>2]|0)*3|0)/(1200/(c[oa>>2]|0)|0|0)|0;else q=c[aa>>2]|0;c[pa>>2]=q}c[Nb>>2]=Wi(qa,0,c[oa>>2]|0,c[Qb>>2]|0,c[pa>>2]|0,0,((c[(c[Pb>>2]|0)+136>>2]|0)!=0^1)&1)|0;if((c[Nb>>2]|0)<0){c[Ob>>2]=-3;c[Mb>>2]=1;break}else{c[(c[Pb>>2]|0)+124>>2]=c[ka>>2];c[(c[Pb>>2]|0)+116>>2]=c[ha>>2];c[(c[Pb>>2]|0)+108>>2]=c[ja>>2];c[(c[Pb>>2]|0)+8+56>>2]=c[la>>2];c[Ob>>2]=c[Nb>>2];c[Mb>>2]=1;break}}while(0);na(c[fa>>2]|0);Qb=c[Ob>>2]|0;i=Rb;return Qb|0}while(0);c[ib>>2]=c[(c[Pb>>2]|0)+14336>>2];if((c[ib>>2]|0)>1103?(c[(c[Pb>>2]|0)+14320>>2]|0)==1e3:0)c[(c[Pb>>2]|0)+14320>>2]=1001;if((c[ib>>2]|0)<=1103?(c[(c[Pb>>2]|0)+14320>>2]|0)==1001:0)c[(c[Pb>>2]|0)+14320>>2]=1e3;qa=_(c[(c[Pb>>2]|0)+148>>2]|0,c[Gb>>2]|0)|0;if(((c[Ib>>2]|0)-(c[Kb>>2]|0)|0)<((qa|0)/(c[(c[Pb>>2]|0)+132>>2]<<3|0)|0|0))q=(c[Ib>>2]|0)-(c[Kb>>2]|0)|0;else{q=_(c[(c[Pb>>2]|0)+148>>2]|0,c[Gb>>2]|0)|0;q=(q|0)/(c[(c[Pb>>2]|0)+132>>2]<<3|0)|0}c[Va>>2]=q-1;c[Qb>>2]=(c[Qb>>2]|0)+1;ic(Hb,c[Qb>>2]|0,(c[Ib>>2]|0)-1|0);pa=_((c[_a>>2]|0)+(c[Gb>>2]|0)|0,c[(c[Pb>>2]|0)+100>>2]|0)|0;c[Fb>>2]=ia()|0;n=i;i=i+((1*(pa<<2)|0)+15&-16)|0;pa=_((c[(c[Pb>>2]|0)+160>>2]|0)-(c[_a>>2]|0)|0,c[(c[Pb>>2]|0)+100>>2]|0)|0;qa=(_(c[_a>>2]|0,c[(c[Pb>>2]|0)+100>>2]|0)|0)<<2;pj(n|0,(c[Pb>>2]|0)+14372+(pa<<2)|0,qa+0|0)|0;if((c[(c[Pb>>2]|0)+14320>>2]|0)==1002)c[V>>2]=(Sf(60)|0)<<8;else c[V>>2]=c[(c[Ga>>2]|0)+8>>2];c[(c[Pb>>2]|0)+14296>>2]=(c[(c[Pb>>2]|0)+14296>>2]|0)+((((c[V>>2]|0)-(c[(c[Pb>>2]|0)+14296>>2]|0)>>16)*983|0)+(((c[V>>2]|0)-(c[(c[Pb>>2]|0)+14296>>2]|0)&65535)*983>>16));c[W>>2]=Wf(c[(c[Pb>>2]|0)+14296>>2]>>8)|0;q=c[sa>>2]|0;if((c[(c[Pb>>2]|0)+96>>2]|0)==2048){sa=n+((_(c[_a>>2]|0,c[(c[Pb>>2]|0)+100>>2]|0)|0)<<2)|0;Ii(q,c[W>>2]|0,sa,(c[Pb>>2]|0)+14304|0,c[Gb>>2]|0,c[(c[Pb>>2]|0)+100>>2]|0,c[(c[Pb>>2]|0)+132>>2]|0)}else{sa=n+((_(c[_a>>2]|0,c[(c[Pb>>2]|0)+100>>2]|0)|0)<<2)|0;Ki(q,3,sa,(c[Pb>>2]|0)+14304|0,c[Gb>>2]|0,c[(c[Pb>>2]|0)+100>>2]|0,c[(c[Pb>>2]|0)+132>>2]|0)}do if(c[ta>>2]|0){sa=n+((_(c[_a>>2]|0,c[(c[Pb>>2]|0)+100>>2]|0)|0)<<2)|0;ta=n+((_(c[_a>>2]|0,c[(c[Pb>>2]|0)+100>>2]|0)|0)<<2)|0;g[X>>2]=+Li(sa,ta,_(c[Gb>>2]|0,c[(c[Pb>>2]|0)+100>>2]|0)|0);if(+g[X>>2]<1.0e9?!(+g[X>>2]!=+g[X>>2]):0)break;ta=n+((_(c[_a>>2]|0,c[(c[Pb>>2]|0)+100>>2]|0)|0)<<2)|0;oj(ta|0,0,(_(c[Gb>>2]|0,c[(c[Pb>>2]|0)+100>>2]|0)|0)<<2|0)|0;g[(c[Pb>>2]|0)+14304+12>>2]=0.0;g[(c[Pb>>2]|0)+14304+8>>2]=0.0;g[(c[Pb>>2]|0)+14304+4>>2]=0.0;g[(c[Pb>>2]|0)+14304>>2]=0.0}while(0);g[Ya>>2]=1.0;if((c[(c[Pb>>2]|0)+14320>>2]|0)!=1002){ta=_(c[(c[Pb>>2]|0)+100>>2]|0,c[Gb>>2]|0)|0;c[Ia>>2]=ia()|0;m=i;i=i+((1*(ta<<1)|0)+15&-16)|0;c[ra>>2]=_(c[Va>>2]<<3,c[Da>>2]|0)|0;do if((c[(c[Pb>>2]|0)+14320>>2]|0)==1001){q=_(c[(c[Pb>>2]|0)+14288>>2]|0,5e3+(((c[(c[Pb>>2]|0)+132>>2]|0)==((c[Gb>>2]|0)*100|0)&1)*1e3|0)|0)|0;c[(c[Pb>>2]|0)+8+28>>2]=q;q=(c[ra>>2]|0)-(c[(c[Pb>>2]|0)+8+28>>2]|0)|0;if((c[ib>>2]|0)==1104){ta=(c[Pb>>2]|0)+8+28|0;c[ta>>2]=(c[ta>>2]|0)+((q<<1|0)/3|0)}else{ta=(c[Pb>>2]|0)+8+28|0;c[ta>>2]=(c[ta>>2]|0)+((q*3|0)/5|0)}if((c[(c[Pb>>2]|0)+8+28>>2]|0)>((c[ra>>2]<<2|0)/5|0|0))c[(c[Pb>>2]|0)+8+28>>2]=(c[ra>>2]<<2|0)/5|0;if(c[(c[Pb>>2]|0)+14348>>2]|0)break;c[ma>>2]=(c[ra>>2]|0)-(c[(c[Pb>>2]|0)+8+28>>2]|0);c[ga>>2]=(c[ib>>2]|0)==1104?3e3:3600;g[Ya>>2]=+(c[ma>>2]|0)/(+(c[ma>>2]|0)+ +(_(c[(c[Pb>>2]|0)+14288>>2]|0,c[ga>>2]|0)|0));g[Ya>>2]=+g[Ya>>2]<.8571428656578064?+g[Ya>>2]+.1428571492433548:1.0}else c[(c[Pb>>2]|0)+8+28>>2]=c[ra>>2];while(0);do if(c[(c[Pb>>2]|0)+14348>>2]|0){if(!(c[(c[Pb>>2]|0)+136>>2]|0))break;if(c[(c[Pb>>2]|0)+164>>2]|0)break;g[xa>>2]=0.0;c[va>>2]=17;b[za>>1]=16e3;do if((c[(c[Pb>>2]|0)+14336>>2]|0)==1101){c[va>>2]=13;b[za>>1]=8e3}else{if((c[(c[Pb>>2]|0)+14336>>2]|0)!=1102)break;c[va>>2]=15;b[za>>1]=12e3}while(0);c[ua>>2]=0;while(1){if((c[ua>>2]|0)>=(c[(c[Pb>>2]|0)+100>>2]|0))break;c[Xa>>2]=0;while(1){q=c[ua>>2]|0;if((c[Xa>>2]|0)>=(c[va>>2]|0))break;if(+g[(c[(c[Pb>>2]|0)+14348>>2]|0)+((q*21|0)+(c[Xa>>2]|0)<<2)>>2]<.5)t=+g[(c[(c[Pb>>2]|0)+14348>>2]|0)+(((c[ua>>2]|0)*21|0)+(c[Xa>>2]|0)<<2)>>2];else t=.5;do if(t>-2.0){if(!(+g[(c[(c[Pb>>2]|0)+14348>>2]|0)+(((c[ua>>2]|0)*21|0)+(c[Xa>>2]|0)<<2)>>2]<.5)){t=.5;break}t=+g[(c[(c[Pb>>2]|0)+14348>>2]|0)+(((c[ua>>2]|0)*21|0)+(c[Xa>>2]|0)<<2)>>2]}else t=-2.0;while(0);g[wa>>2]=t;if(+g[wa>>2]>0.0)g[wa>>2]=+g[wa>>2]*.5;g[xa>>2]=+g[xa>>2]+ +g[wa>>2];c[Xa>>2]=(c[Xa>>2]|0)+1}c[ua>>2]=q+1}g[ya>>2]=+g[xa>>2]/+(c[va>>2]|0)*+(c[(c[Pb>>2]|0)+100>>2]|0);g[ya>>2]=+g[ya>>2]+.20000000298023224;c[Aa>>2]=~~(+(b[za>>1]|0)*+g[ya>>2]);if((c[Aa>>2]|0)>((_(-2,c[(c[Pb>>2]|0)+8+28>>2]|0)|0)/3|0|0))q=c[Aa>>2]|0;else q=(_(-2,c[(c[Pb>>2]|0)+8+28>>2]|0)|0)/3|0;c[Aa>>2]=q;do if((c[(c[Pb>>2]|0)+14336>>2]|0)==1104)Eb=281;else{if((c[(c[Pb>>2]|0)+14336>>2]|0)==1105){Eb=281;break}za=(c[Pb>>2]|0)+8+28|0;c[za>>2]=(c[za>>2]|0)+(c[Aa>>2]|0)}while(0);if((Eb|0)==281){za=(c[Pb>>2]|0)+8+28|0;c[za>>2]=(c[za>>2]|0)+(((c[Aa>>2]|0)*3|0)/5|0)}Aa=_(c[Aa>>2]|0,c[Gb>>2]|0)|0;c[Va>>2]=(c[Va>>2]|0)+((Aa|0)/(c[(c[Pb>>2]|0)+132>>2]<<3|0)|0)}while(0);c[(c[Pb>>2]|0)+8+24>>2]=((c[Gb>>2]|0)*1e3|0)/(c[(c[Pb>>2]|0)+132>>2]|0)|0;c[(c[Pb>>2]|0)+8>>2]=c[(c[Pb>>2]|0)+100>>2];c[(c[Pb>>2]|0)+8+4>>2]=c[(c[Pb>>2]|0)+14288>>2];do if((c[ib>>2]|0)!=1101){q=(c[Pb>>2]|0)+8+20|0;if((c[ib>>2]|0)==1102){c[q>>2]=12e3;break}else{c[q>>2]=16e3;break}}else c[(c[Pb>>2]|0)+8+20>>2]=8e3;while(0);q=(c[Pb>>2]|0)+8+16|0;if((c[(c[Pb>>2]|0)+14320>>2]|0)==1001)c[q>>2]=16e3;else c[q>>2]=8e3;do if((c[(c[Pb>>2]|0)+14320>>2]|0)==1e3){c[Ca>>2]=c[Ba>>2];c[(c[Pb>>2]|0)+8+12>>2]=16e3;if((c[Da>>2]|0)>50)c[Ca>>2]=(c[Ca>>2]<<1|0)/3|0;if((c[Ca>>2]|0)<13e3){c[(c[Pb>>2]|0)+8+12>>2]=12e3;if(12e3<(c[(c[Pb>>2]|0)+8+20>>2]|0))q=12e3;else q=c[(c[Pb>>2]|0)+8+20>>2]|0;c[(c[Pb>>2]|0)+8+20>>2]=q}if((c[Ca>>2]|0)>=9600)break;c[(c[Pb>>2]|0)+8+12>>2]=8e3;if(8e3<(c[(c[Pb>>2]|0)+8+20>>2]|0))q=8e3;else q=c[(c[Pb>>2]|0)+8+20>>2]|0;c[(c[Pb>>2]|0)+8+20>>2]=q}else c[(c[Pb>>2]|0)+8+12>>2]=16e3;while(0);c[(c[Pb>>2]|0)+8+48>>2]=((c[(c[Pb>>2]|0)+136>>2]|0)!=0^1)&1;if(1275<((c[Ib>>2]|0)-1-(c[Kb>>2]|0)|0))q=1275;else q=(c[Ib>>2]|0)-1-(c[Kb>>2]|0)|0;c[Ma>>2]=q;c[(c[Pb>>2]|0)+8+52>>2]=c[Ma>>2]<<3;if((c[(c[Pb>>2]|0)+14320>>2]|0)==1001)c[(c[Pb>>2]|0)+8+52>>2]=((c[(c[Pb>>2]|0)+8+52>>2]|0)*9|0)/10|0;if(c[(c[Pb>>2]|0)+8+48>>2]|0){Da=_(c[(c[Pb>>2]|0)+8+28>>2]|0,c[Gb>>2]|0)|0;c[(c[Pb>>2]|0)+8+52>>2]=((Da|0)/(c[(c[Pb>>2]|0)+132>>2]<<3|0)|0)<<3;if(1>((c[(c[Pb>>2]|0)+8+28>>2]|0)-2e3|0))q=1;else q=(c[(c[Pb>>2]|0)+8+28>>2]|0)-2e3|0;c[(c[Pb>>2]|0)+8+28>>2]=q}if(c[Ea>>2]|0){c[Ha>>2]=0;c[Fa>>2]=_(c[(c[Pb>>2]|0)+100>>2]|0,(c[(c[Pb>>2]|0)+160>>2]|0)-(c[(c[Pb>>2]|0)+104>>2]|0)-((c[(c[Pb>>2]|0)+132>>2]|0)/400|0)|0)|0;Mi((c[Pb>>2]|0)+14372+(c[Fa>>2]<<2)|0,(c[Pb>>2]|0)+14372+(c[Fa>>2]<<2)|0,0.0,1.0,c[(c[$a>>2]|0)+4>>2]|0,(c[(c[Pb>>2]|0)+132>>2]|0)/400|0,c[(c[Pb>>2]|0)+100>>2]|0,c[(c[$a>>2]|0)+60>>2]|0,c[(c[Pb>>2]|0)+132>>2]|0);oj((c[Pb>>2]|0)+14372|0,0,c[Fa>>2]<<2|0)|0;c[Xa>>2]=0;while(1){if((c[Xa>>2]|0)>=(_(c[(c[Pb>>2]|0)+160>>2]|0,c[(c[Pb>>2]|0)+100>>2]|0)|0))break;Fa=Ni(+g[(c[Pb>>2]|0)+14372+(c[Xa>>2]<<2)>>2])|0;b[m+(c[Xa>>2]<<1)>>1]=Fa;c[Xa>>2]=(c[Xa>>2]|0)+1}Vd(c[Ga>>2]|0,(c[Pb>>2]|0)+8|0,m,c[(c[Pb>>2]|0)+160>>2]|0,0,Ha,1)|0}c[Xa>>2]=0;while(1){if((c[Xa>>2]|0)>=(_(c[Gb>>2]|0,c[(c[Pb>>2]|0)+100>>2]|0)|0))break;Ha=_(c[_a>>2]|0,c[(c[Pb>>2]|0)+100>>2]|0)|0;Ha=Ni(+g[n+(Ha+(c[Xa>>2]|0)<<2)>>2])|0;b[m+(c[Xa>>2]<<1)>>1]=Ha;c[Xa>>2]=(c[Xa>>2]|0)+1}c[Nb>>2]=Vd(c[Ga>>2]|0,(c[Pb>>2]|0)+8|0,m,c[Gb>>2]|0,Hb,Ma,0)|0;do if(c[Nb>>2]|0){c[Ob>>2]=-3;c[Mb>>2]=1}else{q=c[Pb>>2]|0;if(!(c[Ma>>2]|0)){c[q+18216>>2]=0;Xa=Hi(c[(c[Pb>>2]|0)+14320>>2]|0,(c[(c[Pb>>2]|0)+132>>2]|0)/(c[Gb>>2]|0)|0,c[ib>>2]|0,c[(c[Pb>>2]|0)+14288>>2]|0)|0;a[(c[Qb>>2]|0)+-1>>0]=Xa;c[Ob>>2]=1;c[Mb>>2]=1;break}do if((c[q+14320>>2]|0)==1e3){if((c[(c[Pb>>2]|0)+8+68>>2]|0)==8e3){c[ib>>2]=1101;break}if((c[(c[Pb>>2]|0)+8+68>>2]|0)==12e3){c[ib>>2]=1102;break}if((c[(c[Pb>>2]|0)+8+68>>2]|0)!=16e3)break;c[ib>>2]=1103}while(0);c[(c[Pb>>2]|0)+8+60>>2]=c[(c[Pb>>2]|0)+8+84>>2];if(c[(c[Pb>>2]|0)+8+60>>2]|0){c[Jb>>2]=1;c[hb>>2]=0;c[(c[Pb>>2]|0)+14340>>2]=1}c[Mb>>2]=0}while(0);na(c[Ia>>2]|0);if(!(c[Mb>>2]|0))Eb=335}else Eb=335;b:do if((Eb|0)==335){c[La>>2]=21;switch(c[ib>>2]|0){case 1101:{c[La>>2]=13;break}case 1103:case 1102:{c[La>>2]=17;break}case 1104:{c[La>>2]=19;break}case 1105:{c[La>>2]=21;break}default:{}}Xa=c[gb>>2]|0;c[Na>>2]=c[La>>2];lb(Xa,10012,Na)|0;Xa=c[gb>>2]|0;c[Oa>>2]=c[(c[Pb>>2]|0)+14288>>2];lb(Xa,10008,Oa)|0;Xa=c[gb>>2]|0;c[Pa>>2]=-1;lb(Xa,4002,Pa)|0;do if((c[(c[Pb>>2]|0)+14320>>2]|0)!=1e3){g[Ka>>2]=2.0;Xa=c[gb>>2]|0;c[Qa>>2]=0;lb(Xa,4006,Qa)|0;if(c[(c[Pb>>2]|0)+8+64>>2]|0)g[Ka>>2]=0.0;Xa=c[gb>>2]|0;c[Ra>>2]=~~+g[Ka>>2];lb(Xa,10002,Ra)|0;if((c[(c[Pb>>2]|0)+14320>>2]|0)==1001){c[Wa>>2]=(Oi(Hb)|0)+7>>3;if(c[Jb>>2]|0)c[Wa>>2]=(c[Wa>>2]|0)+((c[(c[Pb>>2]|0)+14320>>2]|0)==1001?3:1);q=c[Wa>>2]|0;m=c[Va>>2]|0;if(c[(c[Pb>>2]|0)+136>>2]|0){Xa=_(c[(c[Pb>>2]|0)+8+28>>2]|0,c[Gb>>2]|0)|0;c[pb>>2]=q+m-((Xa|0)/(c[(c[Pb>>2]|0)+132>>2]<<3|0)|0);break}else{c[pb>>2]=(q|0)>(m|0)?c[Wa>>2]|0:c[Va>>2]|0;break}}if(!(c[(c[Pb>>2]|0)+136>>2]|0)){c[pb>>2]=c[Va>>2];break}c[Ja>>2]=0;do if((c[(c[Pb>>2]|0)+144>>2]|0)==5010){if((c[Gb>>2]|0)==((c[(c[Pb>>2]|0)+132>>2]|0)/50|0|0))break;c[Ja>>2]=_(((c[(c[Pb>>2]|0)+14288>>2]|0)*60|0)+40|0,((c[(c[Pb>>2]|0)+132>>2]|0)/(c[Gb>>2]|0)|0)-50|0)|0;if(!(c[fb>>2]|0))break;c[Ja>>2]=~~(+(c[Ja>>2]|0)*(+g[fb+4>>2]*.5+1.0))}while(0);Xa=c[gb>>2]|0;c[Sa>>2]=1;lb(Xa,4006,Sa)|0;Xa=c[gb>>2]|0;c[Ta>>2]=c[(c[Pb>>2]|0)+140>>2];lb(Xa,4020,Ta)|0;Xa=c[gb>>2]|0;c[Ua>>2]=(c[(c[Pb>>2]|0)+148>>2]|0)+(c[Ja>>2]|0);lb(Xa,4002,Ua)|0;c[pb>>2]=(c[Ib>>2]|0)-1-(c[Kb>>2]|0)}else c[pb>>2]=0;while(0);Xa=((_(c[(c[Pb>>2]|0)+100>>2]|0,c[(c[Pb>>2]|0)+132>>2]|0)|0)/400|0)<<2;m=i;i=i+((1*Xa|0)+15&-16)|0;do if((c[(c[Pb>>2]|0)+14320>>2]|0)!=1e3){if((c[(c[Pb>>2]|0)+14320>>2]|0)==(c[(c[Pb>>2]|0)+14324>>2]|0))break;if((c[(c[Pb>>2]|0)+14324>>2]|0)<=0)break;Wa=_((c[(c[Pb>>2]|0)+160>>2]|0)-(c[_a>>2]|0)-((c[(c[Pb>>2]|0)+132>>2]|0)/400|0)|0,c[(c[Pb>>2]|0)+100>>2]|0)|0;Xa=((_(c[(c[Pb>>2]|0)+100>>2]|0,c[(c[Pb>>2]|0)+132>>2]|0)|0)/400|0)<<2;pj(m|0,(c[Pb>>2]|0)+14372+(Wa<<2)|0,Xa+0|0)|0}while(0);Xa=(_(c[(c[Pb>>2]|0)+100>>2]|0,(c[(c[Pb>>2]|0)+160>>2]|0)-((c[Gb>>2]|0)+(c[_a>>2]|0))|0)|0)>0;q=(c[Pb>>2]|0)+14372|0;if(Xa){Wa=_(c[(c[Pb>>2]|0)+100>>2]|0,c[Gb>>2]|0)|0;Xa=(_(c[(c[Pb>>2]|0)+100>>2]|0,(c[(c[Pb>>2]|0)+160>>2]|0)-(c[Gb>>2]|0)-(c[_a>>2]|0)|0)|0)<<2;qj(q|0,(c[Pb>>2]|0)+14372+(Wa<<2)|0,Xa+0|0)|0;Xa=_(c[(c[Pb>>2]|0)+100>>2]|0,(c[(c[Pb>>2]|0)+160>>2]|0)-(c[Gb>>2]|0)-(c[_a>>2]|0)|0)|0;_a=(_((c[Gb>>2]|0)+(c[_a>>2]|0)|0,c[(c[Pb>>2]|0)+100>>2]|0)|0)<<2;pj((c[Pb>>2]|0)+14372+(Xa<<2)|0,n|0,_a+0|0)|0}else{Xa=n+((_((c[Gb>>2]|0)+(c[_a>>2]|0)-(c[(c[Pb>>2]|0)+160>>2]|0)|0,c[(c[Pb>>2]|0)+100>>2]|0)|0)<<2)|0;_a=(_(c[(c[Pb>>2]|0)+160>>2]|0,c[(c[Pb>>2]|0)+100>>2]|0)|0)<<2;pj(q|0,Xa|0,_a+0|0)|0}if(+g[Ya>>2]<1.0?1:+g[(c[Pb>>2]|0)+14300>>2]<1.0)Mi(n,n,+g[(c[Pb>>2]|0)+14300>>2],+g[Ya>>2],c[(c[$a>>2]|0)+4>>2]|0,c[Gb>>2]|0,c[(c[Pb>>2]|0)+100>>2]|0,c[(c[$a>>2]|0)+60>>2]|0,c[(c[Pb>>2]|0)+132>>2]|0);g[(c[Pb>>2]|0)+14300>>2]=+g[Ya>>2];if(!((c[(c[Pb>>2]|0)+14320>>2]|0)==1001?(c[(c[Pb>>2]|0)+14288>>2]|0)!=1:0)){if(16384<((0>((c[Za>>2]|0)-3e4|0)?0:(c[Za>>2]|0)-3e4|0)<<1|0))q=16384;else q=(0>((c[Za>>2]|0)-3e4|0)?0:(c[Za>>2]|0)-3e4|0)<<1;c[(c[Pb>>2]|0)+8+80>>2]=q}do if(!(c[(c[Pb>>2]|0)+14348>>2]|0)){if((c[(c[Pb>>2]|0)+100>>2]|0)!=2)break;if((b[(c[Pb>>2]|0)+14292>>1]|0)>=16384?(c[(c[Pb>>2]|0)+8+80>>2]|0)>=16384:0)break;g[ab>>2]=+(b[(c[Pb>>2]|0)+14292>>1]|0);g[bb>>2]=+(c[(c[Pb>>2]|0)+8+80>>2]|0);g[ab>>2]=+g[ab>>2]*.00006103515625;g[bb>>2]=+g[bb>>2]*.00006103515625;Pi(n,n,+g[ab>>2],+g[bb>>2],c[(c[$a>>2]|0)+4>>2]|0,c[Gb>>2]|0,c[(c[Pb>>2]|0)+100>>2]|0,c[(c[$a>>2]|0)+60>>2]|0,c[(c[Pb>>2]|0)+132>>2]|0);b[(c[Pb>>2]|0)+14292>>1]=c[(c[Pb>>2]|0)+8+80>>2]}while(0);do if((c[(c[Pb>>2]|0)+14320>>2]|0)!=1002){bb=(Oi(Hb)|0)+17|0;if((bb+(((c[(c[Pb>>2]|0)+14320>>2]|0)==1001&1)*20|0)|0)>((c[Ib>>2]|0)-1<<3|0)){Eb=393;break}do if((c[(c[Pb>>2]|0)+14320>>2]|0)==1001){if((c[Jb>>2]|0)==0?(bb=(Oi(Hb)|0)+37|0,(bb|0)>(c[pb>>2]<<3|0)):0)break;pc(Hb,c[Jb>>2]|0,12)}while(0);if(!(c[Jb>>2]|0))break;pc(Hb,c[hb>>2]|0,1);q=(c[Ib>>2]|0)-1|0;if((c[(c[Pb>>2]|0)+14320>>2]|0)==1001)c[cb>>2]=q-(c[pb>>2]|0);else c[cb>>2]=q-((Oi(Hb)|0)+7>>3);if((c[cb>>2]|0)<((c[(c[Pb>>2]|0)+148>>2]|0)/1600|0|0))q=c[cb>>2]|0;else q=(c[(c[Pb>>2]|0)+148>>2]|0)/1600|0;c[Kb>>2]=q;if(257<((2>(c[Kb>>2]|0)?2:c[Kb>>2]|0)|0))q=257;else q=2>(c[Kb>>2]|0)?2:c[Kb>>2]|0;c[Kb>>2]=q;if((c[(c[Pb>>2]|0)+14320>>2]|0)!=1001)break;rc(Hb,(c[Kb>>2]|0)-2|0,256)}else Eb=393;while(0);if((Eb|0)==393)c[Jb>>2]=0;if(!(c[Jb>>2]|0)){c[(c[Pb>>2]|0)+14340>>2]=0;c[Kb>>2]=0}if((c[(c[Pb>>2]|0)+14320>>2]|0)!=1002)c[rb>>2]=17;if((c[(c[Pb>>2]|0)+14320>>2]|0)==1e3){c[Nb>>2]=(Oi(Hb)|0)+7>>3;wc(Hb);c[pb>>2]=c[Nb>>2]}else{if(((c[Ib>>2]|0)-1-(c[Kb>>2]|0)|0)<(c[pb>>2]|0))q=(c[Ib>>2]|0)-1-(c[Kb>>2]|0)|0;else q=c[pb>>2]|0;c[pb>>2]=q;vc(Hb,c[pb>>2]|0)}if(!(!(c[Jb>>2]|0)?(c[(c[Pb>>2]|0)+14320>>2]|0)==1e3:0)){Eb=c[gb>>2]|0;c[sb>>2]=fb+(((fb-fb|0)/28|0)*28|0);lb(Eb,10022,sb)|0}do if((c[Jb>>2]|0)!=0&(c[hb>>2]|0)!=0){Eb=c[gb>>2]|0;c[tb>>2]=0;lb(Eb,10010,tb)|0;Eb=c[gb>>2]|0;c[ub>>2]=0;lb(Eb,4006,ub)|0;c[mb>>2]=nb(c[gb>>2]|0,n,(c[(c[Pb>>2]|0)+132>>2]|0)/200|0,(c[Qb>>2]|0)+(c[pb>>2]|0)|0,c[Kb>>2]|0,0)|0;if((c[mb>>2]|0)<0){c[Ob>>2]=-3;c[Mb>>2]=1;break b}else{Eb=c[gb>>2]|0;c[vb>>2]=qb+(((qb-qb|0)/4|0)<<2);lb(Eb,4031,vb)|0;lb(c[gb>>2]|0,4028,wb)|0;break}}while(0);Eb=c[gb>>2]|0;c[xb>>2]=c[rb>>2];lb(Eb,10010,xb)|0;do if((c[(c[Pb>>2]|0)+14320>>2]|0)!=1e3){do if((c[(c[Pb>>2]|0)+14320>>2]|0)!=(c[(c[Pb>>2]|0)+14324>>2]|0)){if((c[(c[Pb>>2]|0)+14324>>2]|0)<=0)break;lb(c[gb>>2]|0,4028,yb)|0;nb(c[gb>>2]|0,m,(c[(c[Pb>>2]|0)+132>>2]|0)/400|0,jb,2,0)|0;Eb=c[gb>>2]|0;c[zb>>2]=0;lb(Eb,10002,zb)|0}while(0);Eb=Oi(Hb)|0;if((Eb|0)>(c[pb>>2]<<3|0))break;c[Nb>>2]=nb(c[gb>>2]|0,n,c[Gb>>2]|0,0,c[pb>>2]|0,Hb)|0;if((c[Nb>>2]|0)>=0)break;c[Ob>>2]=-3;c[Mb>>2]=1;break b}while(0);do if(!((c[Jb>>2]|0)==0|(c[hb>>2]|0)!=0)){c[db>>2]=(c[(c[Pb>>2]|0)+132>>2]|0)/200|0;c[eb>>2]=(c[(c[Pb>>2]|0)+132>>2]|0)/400|0;lb(c[gb>>2]|0,4028,Ab)|0;Eb=c[gb>>2]|0;c[Bb>>2]=0;lb(Eb,10010,Bb)|0;Eb=c[gb>>2]|0;c[Cb>>2]=0;lb(Eb,10002,Cb)|0;Eb=n+((_(c[(c[Pb>>2]|0)+100>>2]|0,(c[Gb>>2]|0)-(c[db>>2]|0)-(c[eb>>2]|0)|0)|0)<<2)|0;nb(c[gb>>2]|0,Eb,c[eb>>2]|0,kb,2,0)|0;Eb=n+((_(c[(c[Pb>>2]|0)+100>>2]|0,(c[Gb>>2]|0)-(c[db>>2]|0)|0)|0)<<2)|0;c[ob>>2]=nb(c[gb>>2]|0,Eb,c[db>>2]|0,(c[Qb>>2]|0)+(c[pb>>2]|0)|0,c[Kb>>2]|0,0)|0;if((c[ob>>2]|0)<0){c[Ob>>2]=-3;c[Mb>>2]=1;break b}else{Eb=c[gb>>2]|0;c[Db>>2]=qb+(((qb-qb|0)/4|0)<<2);lb(Eb,4031,Db)|0;break}}while(0);c[Qb>>2]=(c[Qb>>2]|0)+-1;q=Hi(c[(c[Pb>>2]|0)+14320>>2]|0,(c[(c[Pb>>2]|0)+132>>2]|0)/(c[Gb>>2]|0)|0,c[ib>>2]|0,c[(c[Pb>>2]|0)+14288>>2]|0)|0;a[c[Qb>>2]>>0]=q;c[(c[Pb>>2]|0)+18216>>2]=c[Hb+28>>2]^c[qb>>2];q=c[Pb>>2]|0;if(c[Lb>>2]|0)c[q+14324>>2]=1002;else c[(c[Pb>>2]|0)+14324>>2]=c[q+14320>>2];c[(c[Pb>>2]|0)+14328>>2]=c[(c[Pb>>2]|0)+14288>>2];c[(c[Pb>>2]|0)+14332>>2]=c[Gb>>2];c[(c[Pb>>2]|0)+14344>>2]=0;Lb=Oi(Hb)|0;c:do if((Lb|0)>((c[Ib>>2]|0)-1<<3|0))if((c[Ib>>2]|0)<2){c[Ob>>2]=-2;c[Mb>>2]=1;break b}else{a[(c[Qb>>2]|0)+1>>0]=0;c[Nb>>2]=1;c[(c[Pb>>2]|0)+18216>>2]=0;break}else{if(!(((c[Jb>>2]|0?1:(c[(c[Pb>>2]|0)+14320>>2]|0)!=1e3)^1)&(c[Nb>>2]|0)>2))break;do{if(d[(c[Qb>>2]|0)+(c[Nb>>2]|0)>>0]|0)break c;c[Nb>>2]=(c[Nb>>2]|0)+-1}while((c[Nb>>2]|0)>2)}while(0);c[Nb>>2]=(c[Nb>>2]|0)+(1+(c[Kb>>2]|0));do if(!(c[(c[Pb>>2]|0)+136>>2]|0))if(Xi(c[Qb>>2]|0,c[Nb>>2]|0,c[Ib>>2]|0)|0){c[Ob>>2]=-3;c[Mb>>2]=1;break b}else{c[Nb>>2]=c[Ib>>2];break}while(0);c[Ob>>2]=c[Nb>>2];c[Mb>>2]=1}while(0);na(c[Fb>>2]|0);Qb=c[Ob>>2]|0;i=Rb;return Qb|0}while(0);c[B>>2]=c[(c[Pb>>2]|0)+14320>>2];if(!(c[(c[Pb>>2]|0)+14336>>2]|0))q=1101;else q=c[(c[Pb>>2]|0)+14336>>2]|0;c[z>>2]=q;if(!(c[B>>2]|0))c[B>>2]=1e3;if((c[Da>>2]|0)>100)c[B>>2]=1002;if((c[Da>>2]|0)<50)c[B>>2]=1e3;do if(!((c[B>>2]|0)==1e3&(c[z>>2]|0)>1103)){if((c[B>>2]|0)==1002&(c[z>>2]|0)==1102){c[z>>2]=1101;break}if((c[z>>2]|0)<=1104)c[z>>2]=1104}else c[z>>2]=1103;while(0);Pb=Hi(c[B>>2]|0,c[Da>>2]|0,c[z>>2]|0,c[(c[Pb>>2]|0)+14288>>2]|0)|0;a[c[Qb>>2]>>0]=Pb;c[Ob>>2]=1;Qb=c[Ob>>2]|0;i=Rb;return Qb|0}c[Ob>>2]=-1;Qb=c[Ob>>2]|0;i=Rb;return Qb|0}function Gi(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0;j=i;i=i+16|0;e=j+12|0;f=j+8|0;g=j+4|0;h=j;c[f>>2]=a;c[g>>2]=b;c[h>>2]=d;if(!(c[g>>2]|0))c[g>>2]=(c[(c[f>>2]|0)+132>>2]|0)/400|0;b=c[f>>2]|0;if((c[(c[f>>2]|0)+152>>2]|0)==-1e3){c[e>>2]=(((c[b+132>>2]|0)*60|0)/(c[g>>2]|0)|0)+(_(c[(c[f>>2]|0)+132>>2]|0,c[(c[f>>2]|0)+100>>2]|0)|0);h=c[e>>2]|0;i=j;return h|0}if((c[b+152>>2]|0)==-1){h=_(c[h>>2]<<3,c[(c[f>>2]|0)+132>>2]|0)|0;c[e>>2]=(h|0)/(c[g>>2]|0)|0;h=c[e>>2]|0;i=j;return h|0}else{c[e>>2]=c[(c[f>>2]|0)+152>>2];h=c[e>>2]|0;i=j;return h|0}return 0}function Hi(b,e,f,g){b=b|0;e=e|0;f=f|0;g=g|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;p=i;i=i+32|0;h=p+20|0;j=p+16|0;k=p+12|0;l=p+8|0;m=p+4|0;o=p+24|0;n=p;c[h>>2]=b;c[j>>2]=e;c[k>>2]=f;c[l>>2]=g;c[m>>2]=0;while(1){if((c[j>>2]|0)>=400)break;c[j>>2]=c[j>>2]<<1;c[m>>2]=(c[m>>2]|0)+1}do if((c[h>>2]|0)!=1e3)if((c[h>>2]|0)==1002){b=(c[k>>2]|0)-1102|0;c[n>>2]=b;c[n>>2]=(c[n>>2]|0)<0?0:b;a[o>>0]=-128;a[o>>0]=d[o>>0]|0|c[n>>2]<<5;a[o>>0]=d[o>>0]|0|c[m>>2]<<3;break}else{a[o>>0]=96;a[o>>0]=d[o>>0]|0|(c[k>>2]|0)-1104<<4;a[o>>0]=d[o>>0]|0|(c[m>>2]|0)-2<<3;break}else{a[o>>0]=(c[k>>2]|0)-1101<<5;a[o>>0]=d[o>>0]|0|(c[m>>2]|0)-2<<3}while(0);a[o>>0]=d[o>>0]|0|((c[l>>2]|0)==2&1)<<2;i=p;return a[o>>0]|0}function Ii(a,b,d,e,f,g,h){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;q=i;i=i+64|0;j=q+60|0;v=q+56|0;k=q+52|0;l=q+48|0;m=q+44|0;n=q+40|0;u=q+36|0;p=q+24|0;o=q+16|0;s=q+8|0;t=q+4|0;r=q;c[j>>2]=a;c[v>>2]=b;c[k>>2]=d;c[l>>2]=e;c[m>>2]=f;c[n>>2]=g;c[u>>2]=h;c[s>>2]=(((c[v>>2]&65535)<<16>>16)*2471|0)/((c[u>>2]|0)/1e3|0|0)|0;c[t>>2]=268435456-((c[s>>2]|0)*471|0);c[p>>2]=c[t>>2];c[p+4>>2]=0-(c[t>>2]|0)<<1;c[p+8>>2]=c[t>>2];c[r>>2]=c[t>>2]>>6;d=_(c[s>>2]>>16,(c[s>>2]&65535)<<16>>16)|0;d=d+((_(c[s>>2]&65535,(c[s>>2]&65535)<<16>>16)|0)>>16)|0;d=_(c[r>>2]>>16,(d+(_(c[s>>2]|0,(c[s>>2]>>15)+1>>1)|0)-8388608&65535)<<16>>16)|0;f=_(c[s>>2]>>16,(c[s>>2]&65535)<<16>>16)|0;f=f+((_(c[s>>2]&65535,(c[s>>2]&65535)<<16>>16)|0)>>16)|0;f=d+((_(c[r>>2]&65535,(f+(_(c[s>>2]|0,(c[s>>2]>>15)+1>>1)|0)-8388608&65535)<<16>>16)|0)>>16)|0;d=_(c[s>>2]>>16,(c[s>>2]&65535)<<16>>16)|0;d=d+((_(c[s>>2]&65535,(c[s>>2]&65535)<<16>>16)|0)>>16)|0;c[o>>2]=f+(_(c[r>>2]|0,(d+(_(c[s>>2]|0,(c[s>>2]>>15)+1>>1)|0)-8388608>>15)+1>>1)|0);d=_(c[r>>2]>>16,(c[r>>2]&65535)<<16>>16)|0;d=d+((_(c[r>>2]&65535,(c[r>>2]&65535)<<16>>16)|0)>>16)|0;c[o+4>>2]=d+(_(c[r>>2]|0,(c[r>>2]>>15)+1>>1)|0);Ji(c[j>>2]|0,p,o,c[l>>2]|0,c[k>>2]|0,c[m>>2]|0,c[n>>2]|0);if((c[n>>2]|0)!=2){i=q;return}Ji((c[j>>2]|0)+4|0,p,o,(c[l>>2]|0)+8|0,(c[k>>2]|0)+4|0,c[m>>2]|0,c[n>>2]|0);i=q;return}function Ji(a,b,d,e,f,h,j){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0;u=i;i=i+64|0;k=u+60|0;v=u+56|0;w=u+52|0;l=u+48|0;m=u+44|0;n=u+40|0;o=u+36|0;s=u+32|0;t=u+28|0;r=u+24|0;p=u+16|0;q=u;c[k>>2]=a;c[v>>2]=b;c[w>>2]=d;c[l>>2]=e;c[m>>2]=f;c[n>>2]=h;c[o>>2]=j;g[p>>2]=+(c[c[w>>2]>>2]|0)*3.725290298461914e-09;g[p+4>>2]=+(c[(c[w>>2]|0)+4>>2]|0)*3.725290298461914e-09;g[q>>2]=+(c[c[v>>2]>>2]|0)*3.725290298461914e-09;g[q+4>>2]=+(c[(c[v>>2]|0)+4>>2]|0)*3.725290298461914e-09;g[q+8>>2]=+(c[(c[v>>2]|0)+8>>2]|0)*3.725290298461914e-09;c[s>>2]=0;while(1){if((c[s>>2]|0)>=(c[n>>2]|0))break;w=_(c[s>>2]|0,c[o>>2]|0)|0;g[r>>2]=+g[(c[k>>2]|0)+(w<<2)>>2];g[t>>2]=+g[c[l>>2]>>2]+ +g[q>>2]*+g[r>>2];g[c[l>>2]>>2]=+g[(c[l>>2]|0)+4>>2]-+g[t>>2]*+g[p>>2]+ +g[q+4>>2]*+g[r>>2];g[(c[l>>2]|0)+4>>2]=-+g[t>>2]*+g[p+4>>2]+ +g[q+8>>2]*+g[r>>2]+1.0000000031710769e-30;w=_(c[s>>2]|0,c[o>>2]|0)|0;g[(c[m>>2]|0)+(w<<2)>>2]=+g[t>>2];c[s>>2]=(c[s>>2]|0)+1}i=u;return}function Ki(a,b,d,e,f,h,j){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0;v=i;i=i+64|0;k=v+48|0;x=v+44|0;l=v+40|0;m=v+36|0;n=v+32|0;o=v+28|0;w=v+24|0;p=v+20|0;r=v+16|0;q=v+12|0;t=v+8|0;s=v+4|0;u=v;c[k>>2]=a;c[x>>2]=b;c[l>>2]=d;c[m>>2]=e;c[n>>2]=f;c[o>>2]=h;c[w>>2]=j;g[q>>2]=+(c[x>>2]|0)*4.0/+(c[w>>2]|0);c[p>>2]=0;while(1){if((c[p>>2]|0)>=(c[o>>2]|0))break;c[r>>2]=0;while(1){if((c[r>>2]|0)>=(c[n>>2]|0))break;x=_(c[o>>2]|0,c[r>>2]|0)|0;g[t>>2]=+g[(c[k>>2]|0)+(x+(c[p>>2]|0)<<2)>>2];g[s>>2]=+g[t>>2]-+g[(c[m>>2]|0)+(c[p>>2]<<1<<2)>>2];g[(c[m>>2]|0)+(c[p>>2]<<1<<2)>>2]=+g[(c[m>>2]|0)+(c[p>>2]<<1<<2)>>2]+ +g[q>>2]*(+g[t>>2]-+g[(c[m>>2]|0)+(c[p>>2]<<1<<2)>>2])+1.0000000031710769e-30;g[u>>2]=+g[s>>2]-+g[(c[m>>2]|0)+((c[p>>2]<<1)+1<<2)>>2];g[(c[m>>2]|0)+((c[p>>2]<<1)+1<<2)>>2]=+g[(c[m>>2]|0)+((c[p>>2]<<1)+1<<2)>>2]+ +g[q>>2]*(+g[s>>2]-+g[(c[m>>2]|0)+((c[p>>2]<<1)+1<<2)>>2])+1.0000000031710769e-30;x=_(c[o>>2]|0,c[r>>2]|0)|0;g[(c[l>>2]|0)+(x+(c[p>>2]|0)<<2)>>2]=+g[u>>2];c[r>>2]=(c[r>>2]|0)+1}c[p>>2]=(c[p>>2]|0)+1}i=v;return}function Li(a,b,d){a=a|0;b=b|0;d=d|0;var e=0.0,f=0,h=0,j=0,k=0,l=0,m=0;m=i;i=i+32|0;f=m+16|0;h=m+12|0;j=m+8|0;k=m+4|0;l=m;c[f>>2]=a;c[h>>2]=b;c[j>>2]=d;g[l>>2]=0.0;c[k>>2]=0;while(1){e=+g[l>>2];if((c[k>>2]|0)>=(c[j>>2]|0))break;g[l>>2]=e+ +g[(c[f>>2]|0)+(c[k>>2]<<2)>>2]*+g[(c[h>>2]|0)+(c[k>>2]<<2)>>2];c[k>>2]=(c[k>>2]|0)+1}i=m;return +e}function Mi(a,b,d,e,f,h,j,k,l){a=a|0;b=b|0;d=+d;e=+e;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;var m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0;B=i;i=i+80|0;m=B+64|0;n=B+60|0;o=B+56|0;p=B+52|0;C=B+48|0;q=B+44|0;r=B+40|0;s=B+36|0;D=B+32|0;w=B+28|0;x=B+24|0;y=B+20|0;t=B+16|0;u=B+12|0;z=B+8|0;v=B+4|0;A=B;c[m>>2]=a;c[n>>2]=b;g[o>>2]=d;g[p>>2]=e;c[C>>2]=f;c[q>>2]=h;c[r>>2]=j;c[s>>2]=k;c[D>>2]=l;c[x>>2]=48e3/(c[D>>2]|0)|0;c[y>>2]=(c[C>>2]|0)/(c[x>>2]|0)|0;k=(c[r>>2]|0)==1;c[w>>2]=0;a:do if(k)while(1){if((c[w>>2]|0)>=(c[y>>2]|0))break a;C=_(c[w>>2]|0,c[x>>2]|0)|0;D=_(c[w>>2]|0,c[x>>2]|0)|0;g[z>>2]=+g[(c[s>>2]|0)+(C<<2)>>2]*+g[(c[s>>2]|0)+(D<<2)>>2];g[u>>2]=+g[z>>2]*+g[p>>2]+(1.0-+g[z>>2])*+g[o>>2];g[(c[n>>2]|0)+(c[w>>2]<<2)>>2]=+g[u>>2]*+g[(c[m>>2]|0)+(c[w>>2]<<2)>>2];c[w>>2]=(c[w>>2]|0)+1}else while(1){if((c[w>>2]|0)>=(c[y>>2]|0))break a;C=_(c[w>>2]|0,c[x>>2]|0)|0;D=_(c[w>>2]|0,c[x>>2]|0)|0;g[A>>2]=+g[(c[s>>2]|0)+(C<<2)>>2]*+g[(c[s>>2]|0)+(D<<2)>>2];g[v>>2]=+g[A>>2]*+g[p>>2]+(1.0-+g[A>>2])*+g[o>>2];g[(c[n>>2]|0)+(c[w>>2]<<1<<2)>>2]=+g[v>>2]*+g[(c[m>>2]|0)+(c[w>>2]<<1<<2)>>2];g[(c[n>>2]|0)+((c[w>>2]<<1)+1<<2)>>2]=+g[v>>2]*+g[(c[m>>2]|0)+((c[w>>2]<<1)+1<<2)>>2];c[w>>2]=(c[w>>2]|0)+1}while(0);c[t>>2]=0;do{c[w>>2]=c[y>>2];while(1){if((c[w>>2]|0)>=(c[q>>2]|0))break;C=_(c[w>>2]|0,c[r>>2]|0)|0;D=_(c[w>>2]|0,c[r>>2]|0)|0;g[(c[n>>2]|0)+(D+(c[t>>2]|0)<<2)>>2]=+g[p>>2]*+g[(c[m>>2]|0)+(C+(c[t>>2]|0)<<2)>>2];c[w>>2]=(c[w>>2]|0)+1}D=(c[t>>2]|0)+1|0;c[t>>2]=D}while((D|0)<(c[r>>2]|0));i=B;return}function Ni(a){a=+a;var b=0,c=0;c=i;i=i+16|0;b=c;g[b>>2]=a;g[b>>2]=+g[b>>2]*32768.0;g[b>>2]=+g[b>>2]>-32768.0?+g[b>>2]:-32768.0;g[b>>2]=+g[b>>2]<32767.0?+g[b>>2]:32767.0;b=(ij(+g[b>>2])|0)&65535;i=c;return b|0}function Oi(a){a=a|0;var b=0,d=0;b=i;i=i+16|0;d=b;c[d>>2]=a;a=(c[(c[d>>2]|0)+20>>2]|0)-(32-(aa(c[(c[d>>2]|0)+28>>2]|0)|0))|0;i=b;return a|0}function Pi(a,b,d,e,f,h,j,k,l){a=a|0;b=b|0;d=+d;e=+e;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;var m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0;A=i;i=i+64|0;m=A+60|0;n=A+56|0;o=A+52|0;p=A+48|0;B=A+44|0;q=A+40|0;r=A+36|0;s=A+32|0;C=A+28|0;w=A+24|0;y=A+20|0;x=A+16|0;t=A+12|0;v=A+8|0;z=A+4|0;u=A;c[m>>2]=a;c[n>>2]=b;g[o>>2]=d;g[p>>2]=e;c[B>>2]=f;c[q>>2]=h;c[r>>2]=j;c[s>>2]=k;c[C>>2]=l;c[x>>2]=48e3/(c[C>>2]|0)|0;c[y>>2]=(c[B>>2]|0)/(c[x>>2]|0)|0;g[o>>2]=1.0-+g[o>>2];g[p>>2]=1.0-+g[p>>2];c[w>>2]=0;while(1){if((c[w>>2]|0)>=(c[y>>2]|0))break;C=_(c[w>>2]|0,c[x>>2]|0)|0;B=_(c[w>>2]|0,c[x>>2]|0)|0;g[z>>2]=+g[(c[s>>2]|0)+(C<<2)>>2]*+g[(c[s>>2]|0)+(B<<2)>>2];g[v>>2]=+g[z>>2]*+g[p>>2]+(1.0-+g[z>>2])*+g[o>>2];B=_(c[w>>2]|0,c[r>>2]|0)|0;C=(_(c[w>>2]|0,c[r>>2]|0)|0)+1|0;g[t>>2]=(+g[(c[m>>2]|0)+(B<<2)>>2]-+g[(c[m>>2]|0)+(C<<2)>>2])*.5;g[t>>2]=+g[v>>2]*+g[t>>2];C=_(c[w>>2]|0,c[r>>2]|0)|0;B=_(c[w>>2]|0,c[r>>2]|0)|0;g[(c[n>>2]|0)+(B<<2)>>2]=+g[(c[n>>2]|0)+(C<<2)>>2]-+g[t>>2];B=(_(c[w>>2]|0,c[r>>2]|0)|0)+1|0;C=(_(c[w>>2]|0,c[r>>2]|0)|0)+1|0;g[(c[n>>2]|0)+(C<<2)>>2]=+g[(c[n>>2]|0)+(B<<2)>>2]+ +g[t>>2];c[w>>2]=(c[w>>2]|0)+1}while(1){if((c[w>>2]|0)>=(c[q>>2]|0))break;B=_(c[w>>2]|0,c[r>>2]|0)|0;C=(_(c[w>>2]|0,c[r>>2]|0)|0)+1|0;g[u>>2]=(+g[(c[m>>2]|0)+(B<<2)>>2]-+g[(c[m>>2]|0)+(C<<2)>>2])*.5;g[u>>2]=+g[p>>2]*+g[u>>2];C=_(c[w>>2]|0,c[r>>2]|0)|0;B=_(c[w>>2]|0,c[r>>2]|0)|0;g[(c[n>>2]|0)+(B<<2)>>2]=+g[(c[n>>2]|0)+(C<<2)>>2]-+g[u>>2];B=(_(c[w>>2]|0,c[r>>2]|0)|0)+1|0;C=(_(c[w>>2]|0,c[r>>2]|0)|0)+1|0;g[(c[n>>2]|0)+(C<<2)>>2]=+g[(c[n>>2]|0)+(B<<2)>>2]+ +g[u>>2];c[w>>2]=(c[w>>2]|0)+1}i=A;return}function Qi(a,d,e,f,h){a=a|0;d=d|0;e=e|0;f=f|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;t=i;i=i+48|0;k=t+36|0;l=t+32|0;m=t+28|0;n=t+24|0;o=t+20|0;r=t+16|0;s=t+12|0;q=t+8|0;j=t+4|0;p=t;c[k>>2]=a;c[l>>2]=d;c[m>>2]=e;c[n>>2]=f;c[o>>2]=h;if((c[(c[k>>2]|0)+96>>2]|0)==2051)c[j>>2]=0;else c[j>>2]=c[(c[k>>2]|0)+104>>2];c[q>>2]=Ai(c[l>>2]|0,c[m>>2]|0,c[(c[k>>2]|0)+144>>2]|0,c[(c[k>>2]|0)+100>>2]|0,c[(c[k>>2]|0)+132>>2]|0,c[(c[k>>2]|0)+148>>2]|0,c[j>>2]|0,1,(c[k>>2]|0)+172+6872|0)|0;a=_(c[q>>2]|0,c[(c[k>>2]|0)+100>>2]|0)|0;c[p>>2]=ia()|0;e=i;i=i+((1*(a<<2)|0)+15&-16)|0;c[r>>2]=0;while(1){if((c[r>>2]|0)>=(_(c[q>>2]|0,c[(c[k>>2]|0)+100>>2]|0)|0))break;g[e+(c[r>>2]<<2)>>2]=+(b[(c[l>>2]|0)+(c[r>>2]<<1)>>1]|0)*.000030517578125;c[r>>2]=(c[r>>2]|0)+1}c[s>>2]=Fi(c[k>>2]|0,e,c[q>>2]|0,c[n>>2]|0,c[o>>2]|0,16,c[l>>2]|0,c[m>>2]|0,0,-2,c[(c[k>>2]|0)+100>>2]|0,1,0)|0;s=c[s>>2]|0;na(c[p>>2]|0);i=t;return s|0}function Ri(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0;o=i;i=i+32|0;g=o+24|0;h=o+20|0;j=o+16|0;k=o+12|0;l=o+8|0;n=o+4|0;m=o;c[g>>2]=a;c[h>>2]=b;c[j>>2]=d;c[k>>2]=e;c[l>>2]=f;if((c[(c[g>>2]|0)+96>>2]|0)==2051)c[m>>2]=0;else c[m>>2]=c[(c[g>>2]|0)+104>>2];c[n>>2]=Ai(c[h>>2]|0,c[j>>2]|0,c[(c[g>>2]|0)+144>>2]|0,c[(c[g>>2]|0)+100>>2]|0,c[(c[g>>2]|0)+132>>2]|0,c[(c[g>>2]|0)+148>>2]|0,c[m>>2]|0,2,(c[g>>2]|0)+172+6872|0)|0;a=Fi(c[g>>2]|0,c[h>>2]|0,c[n>>2]|0,c[k>>2]|0,c[l>>2]|0,24,c[h>>2]|0,c[j>>2]|0,0,-2,c[(c[g>>2]|0)+100>>2]|0,2,1)|0;i=o;return a|0}function Si(a,d,e){a=a|0;d=d|0;e=e|0;var f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0,ha=0,ia=0;ha=i;i=i+352|0;ca=ha+48|0;ba=ha+40|0;aa=ha+32|0;$=ha+24|0;_=ha+16|0;Z=ha+8|0;Y=ha;fa=ha+336|0;ea=ha+332|0;ia=ha+328|0;ga=ha+324|0;h=ha+320|0;f=ha+304|0;m=ha+300|0;n=ha+296|0;y=ha+292|0;J=ha+288|0;da=ha+284|0;T=ha+280|0;U=ha+276|0;V=ha+272|0;W=ha+268|0;X=ha+264|0;o=ha+260|0;p=ha+256|0;q=ha+252|0;r=ha+248|0;s=ha+244|0;t=ha+240|0;u=ha+236|0;v=ha+232|0;w=ha+228|0;x=ha+224|0;z=ha+220|0;A=ha+216|0;B=ha+212|0;C=ha+208|0;D=ha+204|0;E=ha+200|0;F=ha+196|0;G=ha+192|0;H=ha+188|0;I=ha+184|0;K=ha+180|0;L=ha+176|0;M=ha+172|0;N=ha+168|0;O=ha+164|0;k=ha+160|0;j=ha+72|0;l=ha+68|0;P=ha+64|0;Q=ha+60|0;R=ha+56|0;S=ha+52|0;c[ea>>2]=a;c[ia>>2]=d;c[ga>>2]=0;c[f>>2]=e;c[h>>2]=(c[ea>>2]|0)+(c[c[ea>>2]>>2]|0);a:do switch(c[ia>>2]|0){case 4e3:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[m>>2]=ia;do if(!((c[m>>2]|0)!=2048&(c[m>>2]|0)!=2049&(c[m>>2]|0)!=2051)){if((c[(c[ea>>2]|0)+14344>>2]|0)==0?(c[(c[ea>>2]|0)+96>>2]|0)!=(c[m>>2]|0):0)break;c[(c[ea>>2]|0)+96>>2]=c[m>>2];f=99;break a}while(0);c[ga>>2]=-1;f=99;break}case 4001:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[n>>2]=ia;if(c[n>>2]|0){c[c[n>>2]>>2]=c[(c[ea>>2]|0)+96>>2];f=99}else f=100;break}case 4002:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[y>>2]=ia;do if((c[y>>2]|0)!=-1e3&(c[y>>2]|0)!=-1){if((c[y>>2]|0)<=0){f=100;break a}if((c[y>>2]|0)<=500){c[y>>2]=500;break}if((c[y>>2]|0)>((c[(c[ea>>2]|0)+100>>2]|0)*3e5|0))c[y>>2]=(c[(c[ea>>2]|0)+100>>2]|0)*3e5}while(0);c[(c[ea>>2]|0)+152>>2]=c[y>>2];f=99;break}case 4003:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[J>>2]=ia;if(c[J>>2]|0){f=Gi(c[ea>>2]|0,c[(c[ea>>2]|0)+14332>>2]|0,1276)|0;c[c[J>>2]>>2]=f;f=99}else f=100;break}case 4022:{e=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[e>>2]|0;c[f>>2]=e+4;c[da>>2]=ia;f=c[da>>2]|0;if((c[da>>2]|0)<1){if((f|0)!=-1e3){f=100;break a}}else if((c[da>>2]|0)!=-1e3?(f|0)>(c[(c[ea>>2]|0)+100>>2]|0):0){f=100;break a}c[(c[ea>>2]|0)+108>>2]=c[da>>2];f=99;break}case 4023:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[T>>2]=ia;if(c[T>>2]|0){c[c[T>>2]>>2]=c[(c[ea>>2]|0)+108>>2];f=99}else f=100;break}case 4004:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[U>>2]=ia;if(!((c[U>>2]|0)<1101|(c[U>>2]|0)>1105)){c[(c[ea>>2]|0)+120>>2]=c[U>>2];h=c[ea>>2]|0;if((c[(c[ea>>2]|0)+120>>2]|0)==1101){c[h+8+12>>2]=8e3;f=99;break a}f=(c[ea>>2]|0)+8+12|0;if((c[h+120>>2]|0)==1102){c[f>>2]=12e3;f=99;break a}else{c[f>>2]=16e3;f=99;break a}}else f=100;break}case 4005:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[V>>2]=ia;if(c[V>>2]|0){c[c[V>>2]>>2]=c[(c[ea>>2]|0)+120>>2];f=99}else f=100;break}case 4008:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[W>>2]=ia;if(!(((c[W>>2]|0)<1101|(c[W>>2]|0)>1105)&(c[W>>2]|0)!=-1e3)){c[(c[ea>>2]|0)+116>>2]=c[W>>2];h=c[ea>>2]|0;if((c[(c[ea>>2]|0)+116>>2]|0)==1101){c[h+8+12>>2]=8e3;f=99;break a}f=(c[ea>>2]|0)+8+12|0;if((c[h+116>>2]|0)==1102){c[f>>2]=12e3;f=99;break a}else{c[f>>2]=16e3;f=99;break a}}else f=100;break}case 4009:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[X>>2]=ia;if(c[X>>2]|0){c[c[X>>2]>>2]=c[(c[ea>>2]|0)+14336>>2];f=99}else f=100;break}case 4016:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[o>>2]=ia;if((c[o>>2]|0)<0|(c[o>>2]|0)>1)f=100;else{c[(c[ea>>2]|0)+8+44>>2]=c[o>>2];f=99}break}case 4017:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[p>>2]=ia;if(c[p>>2]|0){c[c[p>>2]>>2]=c[(c[ea>>2]|0)+8+44>>2];f=99}else f=100;break}case 4010:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[q>>2]=ia;if((c[q>>2]|0)<0|(c[q>>2]|0)>10)f=100;else{c[(c[ea>>2]|0)+8+36>>2]=c[q>>2];f=c[h>>2]|0;c[Y>>2]=c[q>>2];lb(f,4010,Y)|0;f=99}break}case 4011:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[r>>2]=ia;if(c[r>>2]|0){c[c[r>>2]>>2]=c[(c[ea>>2]|0)+8+36>>2];f=99}else f=100;break}case 4012:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[s>>2]=ia;if((c[s>>2]|0)<0|(c[s>>2]|0)>1)f=100;else{c[(c[ea>>2]|0)+8+40>>2]=c[s>>2];f=99}break}case 4013:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[t>>2]=ia;if(c[t>>2]|0){c[c[t>>2]>>2]=c[(c[ea>>2]|0)+8+40>>2];f=99}else f=100;break}case 4014:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[u>>2]=ia;if((c[u>>2]|0)<0|(c[u>>2]|0)>100)f=100;else{c[(c[ea>>2]|0)+8+32>>2]=c[u>>2];f=c[h>>2]|0;c[Z>>2]=c[u>>2];lb(f,4014,Z)|0;f=99}break}case 4015:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[v>>2]=ia;if(c[v>>2]|0){c[c[v>>2]>>2]=c[(c[ea>>2]|0)+8+32>>2];f=99}else f=100;break}case 4006:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[w>>2]=ia;if((c[w>>2]|0)<0|(c[w>>2]|0)>1)f=100;else{c[(c[ea>>2]|0)+136>>2]=c[w>>2];c[(c[ea>>2]|0)+8+48>>2]=1-(c[w>>2]|0);f=99}break}case 4007:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[x>>2]=ia;if(c[x>>2]|0){c[c[x>>2]>>2]=c[(c[ea>>2]|0)+136>>2];f=99}else f=100;break}case 11018:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[z>>2]=ia;if((c[z>>2]|0)<-1|(c[z>>2]|0)>100)f=100;else{c[(c[ea>>2]|0)+128>>2]=c[z>>2];f=99}break}case 11019:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[A>>2]=ia;if(c[A>>2]|0){c[c[A>>2]>>2]=c[(c[ea>>2]|0)+128>>2];f=99}else f=100;break}case 4020:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[B>>2]=ia;if((c[B>>2]|0)<0|(c[B>>2]|0)>1)f=100;else{c[(c[ea>>2]|0)+140>>2]=c[B>>2];f=99}break}case 4021:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[C>>2]=ia;if(c[C>>2]|0){c[c[C>>2]>>2]=c[(c[ea>>2]|0)+140>>2];f=99}else f=100;break}case 4024:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[D>>2]=ia;if((c[D>>2]|0)!=-1e3&(c[D>>2]|0)!=3001&(c[D>>2]|0)!=3002)f=100;else{c[(c[ea>>2]|0)+112>>2]=c[D>>2];f=99}break}case 4025:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[E>>2]=ia;if(c[E>>2]|0){c[c[E>>2]>>2]=c[(c[ea>>2]|0)+112>>2];f=99}else f=100;break}case 4027:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[F>>2]=ia;if(c[F>>2]|0){c[c[F>>2]>>2]=(c[(c[ea>>2]|0)+132>>2]|0)/400|0;if((c[(c[ea>>2]|0)+96>>2]|0)!=2051){f=c[F>>2]|0;c[f>>2]=(c[f>>2]|0)+(c[(c[ea>>2]|0)+104>>2]|0);f=99}else f=99}else f=100;break}case 4029:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[G>>2]=ia;if(c[G>>2]|0){c[c[G>>2]>>2]=c[(c[ea>>2]|0)+132>>2];f=99}else f=100;break}case 4031:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[H>>2]=ia;if(c[H>>2]|0){c[c[H>>2]>>2]=c[(c[ea>>2]|0)+18216>>2];f=99}else f=100;break}case 4036:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[I>>2]=ia;if((c[I>>2]|0)<8|(c[I>>2]|0)>24)f=100;else{c[(c[ea>>2]|0)+156>>2]=c[I>>2];f=99}break}case 4037:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[K>>2]=ia;if(c[K>>2]|0){c[c[K>>2]>>2]=c[(c[ea>>2]|0)+156>>2];f=99}else f=100;break}case 4040:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[L>>2]=ia;if((c[L>>2]|0)!=5e3&(c[L>>2]|0)!=5001&(c[L>>2]|0)!=5002&(c[L>>2]|0)!=5003&(c[L>>2]|0)!=5004&(c[L>>2]|0)!=5005&(c[L>>2]|0)!=5006&(c[L>>2]|0)!=5010)f=100;else{c[(c[ea>>2]|0)+144>>2]=c[L>>2];f=c[h>>2]|0;c[_>>2]=c[L>>2];lb(f,4040,_)|0;f=99}break}case 4041:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[M>>2]=ia;if(c[M>>2]|0){c[c[M>>2]>>2]=c[(c[ea>>2]|0)+144>>2];f=99}else f=100;break}case 4042:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[N>>2]=ia;if((c[N>>2]|0)>1|(c[N>>2]|0)<0)f=100;else{c[(c[ea>>2]|0)+8+64>>2]=c[N>>2];f=99}break}case 4043:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[O>>2]=ia;if(c[O>>2]|0){c[c[O>>2]>>2]=c[(c[ea>>2]|0)+8+64>>2];f=99}else f=100;break}case 4028:{c[k>>2]=(c[ea>>2]|0)+(c[(c[ea>>2]|0)+4>>2]|0);_i((c[ea>>2]|0)+172|0);c[l>>2]=(c[ea>>2]|0)+14288;oj(c[l>>2]|0,0,18220-((c[l>>2]|0)-(c[ea>>2]|0))|0)|0;lb(c[h>>2]|0,4028,$)|0;Td(c[k>>2]|0,c[(c[ea>>2]|0)+168>>2]|0,j)|0;c[(c[ea>>2]|0)+14288>>2]=c[(c[ea>>2]|0)+100>>2];b[(c[ea>>2]|0)+14292>>1]=16384;g[(c[ea>>2]|0)+14300>>2]=1.0;c[(c[ea>>2]|0)+14344>>2]=1;c[(c[ea>>2]|0)+14320>>2]=1001;c[(c[ea>>2]|0)+14336>>2]=1105;f=(Sf(60)|0)<<8;c[(c[ea>>2]|0)+14296>>2]=f;f=99;break}case 11002:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[P>>2]=ia;if(((c[P>>2]|0)<1e3|(c[P>>2]|0)>1002)&(c[P>>2]|0)!=-1e3)f=100;else{c[(c[ea>>2]|0)+124>>2]=c[P>>2];f=99}break}case 10024:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[Q>>2]=ia;c[(c[ea>>2]|0)+164>>2]=c[Q>>2];f=c[h>>2]|0;c[aa>>2]=c[Q>>2];c[ga>>2]=lb(f,10024,aa)|0;f=99;break}case 10026:{da=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[da>>2]|0;c[f>>2]=da+4;c[R>>2]=ia;c[(c[ea>>2]|0)+14348>>2]=c[R>>2];f=c[h>>2]|0;c[ba>>2]=(c[R>>2]|0)+((((c[R>>2]|0)-(c[R>>2]|0)|0)/4|0)<<2);c[ga>>2]=lb(f,10026,ba)|0;f=99;break}case 10015:{ea=(c[f>>2]|0)+(4-1)&~(4-1);ia=c[ea>>2]|0;c[f>>2]=ea+4;c[S>>2]=ia;if(c[S>>2]|0){f=c[h>>2]|0;c[ca>>2]=(c[S>>2]|0)+((((c[S>>2]|0)-(c[S>>2]|0)|0)/4|0)<<2);c[ga>>2]=lb(f,10015,ca)|0;f=99}else f=100;break}default:{c[ga>>2]=-5;f=99}}while(0);if((f|0)==99){c[fa>>2]=c[ga>>2];ia=c[fa>>2]|0;i=ha;return ia|0}else if((f|0)==100){c[fa>>2]=-1;ia=c[fa>>2]|0;i=ha;return ia|0}return 0}function Ti(a){a=a|0;var b=0,d=0;d=i;i=i+16|0;b=d;c[b>>2]=a;c[(c[b>>2]|0)+4>>2]=0;i=d;return c[b>>2]|0}function Ui(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0;e=i;i=i+16|0;h=e+8|0;g=e+4|0;f=e;c[h>>2]=a;c[g>>2]=b;c[f>>2]=d;a=Vi(c[h>>2]|0,c[g>>2]|0,c[f>>2]|0,0)|0;i=e;return a|0}function Vi(b,e,f,g){b=b|0;e=e|0;f=f|0;g=g|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;q=i;i=i+32|0;h=q+24|0;j=q+20|0;k=q+16|0;l=q+12|0;m=q+8|0;p=q+28|0;n=q+4|0;o=q;c[j>>2]=b;c[k>>2]=e;c[l>>2]=f;c[m>>2]=g;if((c[l>>2]|0)<1){c[h>>2]=-4;p=c[h>>2]|0;i=q;return p|0}if(c[(c[j>>2]|0)+4>>2]|0){if(((d[c[j>>2]>>0]|0)&252|0)!=((d[c[k>>2]>>0]|0)&252|0)){c[h>>2]=-4;p=c[h>>2]|0;i=q;return p|0}}else{a[c[j>>2]>>0]=a[c[k>>2]>>0]|0;g=Zh(c[k>>2]|0,8e3)|0;c[(c[j>>2]|0)+296>>2]=g}c[n>>2]=pi(c[k>>2]|0,c[l>>2]|0)|0;if((c[n>>2]|0)<1){c[h>>2]=-4;p=c[h>>2]|0;i=q;return p|0}if((_((c[n>>2]|0)+(c[(c[j>>2]|0)+4>>2]|0)|0,c[(c[j>>2]|0)+296>>2]|0)|0)>960){c[h>>2]=-4;p=c[h>>2]|0;i=q;return p|0}c[o>>2]=_h(c[k>>2]|0,c[l>>2]|0,c[m>>2]|0,p,(c[j>>2]|0)+8+(c[(c[j>>2]|0)+4>>2]<<2)|0,(c[j>>2]|0)+200+(c[(c[j>>2]|0)+4>>2]<<1)|0,0,0)|0;if((c[o>>2]|0)<1){c[h>>2]=c[o>>2];p=c[h>>2]|0;i=q;return p|0}else{p=(c[j>>2]|0)+4|0;c[p>>2]=(c[p>>2]|0)+(c[n>>2]|0);c[h>>2]=0;p=c[h>>2]|0;i=q;return p|0}return 0}function Wi(e,f,g,h,j,k,l){e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;k=k|0;l=l|0;var m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0;F=i;i=i+80|0;q=F+68|0;o=F+64|0;m=F+60|0;n=F+56|0;r=F+52|0;s=F+48|0;t=F+44|0;u=F+40|0;x=F+36|0;v=F+32|0;D=F+28|0;y=F+24|0;w=F+20|0;B=F+16|0;E=F+12|0;A=F+8|0;z=F+4|0;C=F;c[o>>2]=e;c[m>>2]=f;c[n>>2]=g;c[r>>2]=h;c[s>>2]=j;c[t>>2]=k;c[u>>2]=l;if(((c[m>>2]|0)>=0?(c[m>>2]|0)<(c[n>>2]|0):0)?(c[n>>2]|0)<=(c[(c[o>>2]|0)+4>>2]|0):0){c[v>>2]=(c[n>>2]|0)-(c[m>>2]|0);c[y>>2]=(c[o>>2]|0)+200+(c[m>>2]<<1);c[w>>2]=(c[o>>2]|0)+8+(c[m>>2]<<2);if(c[t>>2]|0)c[D>>2]=1+((b[(c[y>>2]|0)+((c[v>>2]|0)-1<<1)>>1]|0)>=252&1);else c[D>>2]=0;c[B>>2]=c[r>>2];do if((c[v>>2]|0)!=1){if((c[v>>2]|0)==2){m=b[c[y>>2]>>1]|0;if((b[(c[y>>2]|0)+2>>1]|0)==(b[c[y>>2]>>1]|0)){c[D>>2]=(c[D>>2]|0)+((m<<1)+1);if((c[D>>2]|0)<=(c[s>>2]|0)){e=(d[c[o>>2]>>0]&252|1)&255;k=c[B>>2]|0;c[B>>2]=k+1;a[k>>0]=e;break}c[q>>2]=-2;E=c[q>>2]|0;i=F;return E|0}else{c[D>>2]=(c[D>>2]|0)+(m+(b[(c[y>>2]|0)+2>>1]|0)+2+((b[c[y>>2]>>1]|0)>=252&1));if((c[D>>2]|0)<=(c[s>>2]|0)){e=(d[c[o>>2]>>0]&252|2)&255;k=c[B>>2]|0;c[B>>2]=k+1;a[k>>0]=e;k=Yh(b[c[y>>2]>>1]|0,c[B>>2]|0)|0;c[B>>2]=(c[B>>2]|0)+k;break}c[q>>2]=-2;E=c[q>>2]|0;i=F;return E|0}}}else{c[D>>2]=(c[D>>2]|0)+((b[c[y>>2]>>1]|0)+1);if((c[D>>2]|0)<=(c[s>>2]|0)){e=d[c[o>>2]>>0]&252;k=c[B>>2]|0;c[B>>2]=k+1;a[k>>0]=e;break}c[q>>2]=-2;E=c[q>>2]|0;i=F;return E|0}while(0);if((c[v>>2]|0)<=2){if(c[u>>2]|0?(c[D>>2]|0)<(c[s>>2]|0):0)p=23}else p=23;a:do if((p|0)==23){c[A>>2]=0;c[B>>2]=c[r>>2];if(c[t>>2]|0)c[D>>2]=1+((b[(c[y>>2]|0)+((c[v>>2]|0)-1<<1)>>1]|0)>=252&1);else c[D>>2]=0;c[E>>2]=0;c[x>>2]=1;while(1){if((c[x>>2]|0)>=(c[v>>2]|0))break;if((b[(c[y>>2]|0)+(c[x>>2]<<1)>>1]|0)!=(b[c[y>>2]>>1]|0)){p=29;break}c[x>>2]=(c[x>>2]|0)+1}if((p|0)==29)c[E>>2]=1;do if(c[E>>2]|0){c[D>>2]=(c[D>>2]|0)+2;c[x>>2]=0;while(1){if((c[x>>2]|0)>=((c[v>>2]|0)-1|0))break;c[D>>2]=(c[D>>2]|0)+(1+((b[(c[y>>2]|0)+(c[x>>2]<<1)>>1]|0)>=252&1)+(b[(c[y>>2]|0)+(c[x>>2]<<1)>>1]|0));c[x>>2]=(c[x>>2]|0)+1}c[D>>2]=(c[D>>2]|0)+(b[(c[y>>2]|0)+((c[v>>2]|0)-1<<1)>>1]|0);if((c[D>>2]|0)<=(c[s>>2]|0)){p=(d[c[o>>2]>>0]&252|3)&255;o=c[B>>2]|0;c[B>>2]=o+1;a[o>>0]=p;o=(c[v>>2]|128)&255;p=c[B>>2]|0;c[B>>2]=p+1;a[p>>0]=o;break}c[q>>2]=-2;E=c[q>>2]|0;i=F;return E|0}else{p=(_(c[v>>2]|0,b[c[y>>2]>>1]|0)|0)+2|0;c[D>>2]=(c[D>>2]|0)+p;if((c[D>>2]|0)<=(c[s>>2]|0)){p=(d[c[o>>2]>>0]&252|3)&255;o=c[B>>2]|0;c[B>>2]=o+1;a[o>>0]=p;o=c[v>>2]&255;p=c[B>>2]|0;c[B>>2]=p+1;a[p>>0]=o;break}c[q>>2]=-2;E=c[q>>2]|0;i=F;return E|0}while(0);if(c[u>>2]|0)m=(c[s>>2]|0)-(c[D>>2]|0)|0;else m=0;c[A>>2]=m;if(c[A>>2]|0){p=(c[r>>2]|0)+1|0;a[p>>0]=d[p>>0]|64;c[z>>2]=((c[A>>2]|0)-1|0)/255|0;c[x>>2]=0;while(1){if((c[x>>2]|0)>=(c[z>>2]|0))break;p=c[B>>2]|0;c[B>>2]=p+1;a[p>>0]=-1;c[x>>2]=(c[x>>2]|0)+1}p=(c[A>>2]|0)-((c[z>>2]|0)*255|0)-1&255;z=c[B>>2]|0;c[B>>2]=z+1;a[z>>0]=p;c[D>>2]=(c[D>>2]|0)+(c[A>>2]|0)}if(c[E>>2]|0){c[x>>2]=0;while(1){if((c[x>>2]|0)>=((c[v>>2]|0)-1|0))break a;E=Yh(b[(c[y>>2]|0)+(c[x>>2]<<1)>>1]|0,c[B>>2]|0)|0;c[B>>2]=(c[B>>2]|0)+E;c[x>>2]=(c[x>>2]|0)+1}}}while(0);if(c[t>>2]|0){c[C>>2]=Yh(b[(c[y>>2]|0)+((c[v>>2]|0)-1<<1)>>1]|0,c[B>>2]|0)|0;c[B>>2]=(c[B>>2]|0)+(c[C>>2]|0)}c[x>>2]=0;while(1){if((c[x>>2]|0)>=(c[v>>2]|0))break;qj(c[B>>2]|0,c[(c[w>>2]|0)+(c[x>>2]<<2)>>2]|0,(b[(c[y>>2]|0)+(c[x>>2]<<1)>>1]|0)+0|0)|0;c[B>>2]=(c[B>>2]|0)+(b[(c[y>>2]|0)+(c[x>>2]<<1)>>1]|0);c[x>>2]=(c[x>>2]|0)+1}b:do if(c[u>>2]|0)while(1){if((c[B>>2]|0)>>>0>=((c[r>>2]|0)+(c[s>>2]|0)|0)>>>0)break b;E=c[B>>2]|0;c[B>>2]=E+1;a[E>>0]=0}while(0);c[q>>2]=c[D>>2];E=c[q>>2]|0;i=F;return E|0}c[q>>2]=-1;E=c[q>>2]|0;i=F;return E|0}function Xi(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0;l=i;i=i+320|0;e=l+316|0;f=l+312|0;g=l+308|0;h=l+304|0;k=l+4|0;j=l;c[f>>2]=a;c[g>>2]=b;c[h>>2]=d;if((c[g>>2]|0)<1){c[e>>2]=-1;k=c[e>>2]|0;i=l;return k|0}if((c[g>>2]|0)==(c[h>>2]|0)){c[e>>2]=0;k=c[e>>2]|0;i=l;return k|0}if((c[g>>2]|0)>(c[h>>2]|0)){c[e>>2]=-1;k=c[e>>2]|0;i=l;return k|0}Ti(k)|0;qj((c[f>>2]|0)+(c[h>>2]|0)+(0-(c[g>>2]|0))|0,c[f>>2]|0,(c[g>>2]|0)+0|0)|0;Ui(k,(c[f>>2]|0)+(c[h>>2]|0)+(0-(c[g>>2]|0))|0,c[g>>2]|0)|0;c[j>>2]=Wi(k,0,c[k+4>>2]|0,c[f>>2]|0,c[h>>2]|0,0,1)|0;if((c[j>>2]|0)>0){c[e>>2]=0;k=c[e>>2]|0;i=l;return k|0}else{c[e>>2]=c[j>>2];k=c[e>>2]|0;i=l;return k|0}return 0}function Yi(a){a=a|0;var b=0,d=0;b=i;i=i+16|0;d=b;c[d>>2]=a;a=Zi()|0;c[c[d>>2]>>2]=a;_i(c[d>>2]|0);i=b;return}function Zi(){return 0}function _i(a){a=a|0;var b=0,d=0,e=0;b=i;i=i+16|0;d=b+4|0;e=b;c[d>>2]=a;c[e>>2]=(c[d>>2]|0)+4;oj(c[e>>2]|0,0,14116-((c[e>>2]|0)-(c[d>>2]|0))|0)|0;i=b;return}function $i(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0;n=i;i=i+32|0;h=n+24|0;j=n+20|0;e=n+16|0;f=n+12|0;k=n+8|0;m=n+4|0;l=n;c[h>>2]=a;c[j>>2]=b;c[e>>2]=d;c[f>>2]=c[(c[h>>2]|0)+8508>>2];c[k>>2]=(c[(c[h>>2]|0)+8504>>2]|0)-(c[(c[h>>2]|0)+8508>>2]|0);if((c[k>>2]|0)<0)c[k>>2]=(c[k>>2]|0)+200;if((c[e>>2]|0)>480?(c[f>>2]|0)!=(c[(c[h>>2]|0)+8504>>2]|0):0){a=(c[f>>2]|0)+1|0;c[f>>2]=a;c[f>>2]=(c[f>>2]|0)==200?0:a}if((c[f>>2]|0)==(c[(c[h>>2]|0)+8504>>2]|0))c[f>>2]=(c[f>>2]|0)+-1;if((c[f>>2]|0)<0)c[f>>2]=199;pj(c[j>>2]|0,(c[h>>2]|0)+8516+((c[f>>2]|0)*28|0)|0,28|0)|0;a=(c[h>>2]|0)+8512|0;c[a>>2]=(c[a>>2]|0)+((c[e>>2]|0)/120|0);while(1){e=c[h>>2]|0;if((c[(c[h>>2]|0)+8512>>2]|0)<4)break;a=e+8512|0;c[a>>2]=(c[a>>2]|0)-4;a=(c[h>>2]|0)+8508|0;c[a>>2]=(c[a>>2]|0)+1}if((c[e+8508>>2]|0)>=200){a=(c[h>>2]|0)+8508|0;c[a>>2]=(c[a>>2]|0)-200}c[k>>2]=((c[k>>2]|0)-10|0)>0?(c[k>>2]|0)-10|0:0;g[m>>2]=0.0;c[l>>2]=0;while(1){if((c[l>>2]|0)>=(200-(c[k>>2]|0)|0))break;g[m>>2]=+g[m>>2]+ +g[(c[h>>2]|0)+7688+(c[l>>2]<<2)>>2];c[l>>2]=(c[l>>2]|0)+1}while(1){if((c[l>>2]|0)>=200)break;g[m>>2]=+g[m>>2]+ +g[(c[h>>2]|0)+6888+(c[l>>2]<<2)>>2];c[l>>2]=(c[l>>2]|0)+1}g[m>>2]=+g[m>>2]*+g[(c[h>>2]|0)+8492>>2]+(1.0-+g[m>>2])*+g[(c[h>>2]|0)+8488>>2];g[(c[j>>2]|0)+20>>2]=+g[m>>2];i=n;return}function aj(a,b,d,e,f,g,h,j,k,l,m,n){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;n=n|0;var o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0;C=i;i=i+64|0;p=C+52|0;q=C+48|0;t=C+44|0;u=C+40|0;v=C+36|0;w=C+32|0;x=C+28|0;y=C+24|0;o=C+20|0;z=C+16|0;r=C+12|0;s=C+8|0;A=C+4|0;B=C;c[p>>2]=a;c[q>>2]=b;c[t>>2]=d;c[u>>2]=e;c[v>>2]=f;c[w>>2]=g;c[x>>2]=h;c[y>>2]=j;c[o>>2]=k;c[z>>2]=l;c[r>>2]=m;c[s>>2]=n;if(!(c[t>>2]|0)){z=c[s>>2]|0;c[z>>2]=0;z=c[p>>2]|0;A=c[s>>2]|0;B=c[v>>2]|0;$i(z,A,B);i=C;return}if((((c[o>>2]|0)*195|0)/100|0|0)<(c[u>>2]|0))o=((c[o>>2]|0)*195|0)/100|0;else o=c[u>>2]|0;c[u>>2]=o;c[B>>2]=(c[u>>2]|0)-(c[(c[p>>2]|0)+6884>>2]|0);c[A>>2]=c[(c[p>>2]|0)+6884>>2];do{bj(c[p>>2]|0,c[q>>2]|0,c[t>>2]|0,480<(c[B>>2]|0)?480:c[B>>2]|0,c[A>>2]|0,c[w>>2]|0,c[x>>2]|0,c[y>>2]|0,c[z>>2]|0,c[r>>2]|0);c[A>>2]=(c[A>>2]|0)+480;c[B>>2]=(c[B>>2]|0)-480}while((c[B>>2]|0)>0);c[(c[p>>2]|0)+6884>>2]=c[u>>2];z=(c[p>>2]|0)+6884|0;c[z>>2]=(c[z>>2]|0)-(c[v>>2]|0);z=c[s>>2]|0;c[z>>2]=0;z=c[p>>2]|0;A=c[s>>2]|0;B=c[v>>2]|0;$i(z,A,B);i=C;return}
+ function bj(a,b,d,e,f,h,j,k,l,m){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;j=j|0;k=k|0;l=l|0;m=m|0;var n=0.0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Z=0,_=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0,ga=0,ha=0,ia=0,ja=0,ka=0,la=0,ma=0,na=0,oa=0,pa=0,ra=0,sa=0,ta=0,ua=0,va=0,wa=0,xa=0,ya=0,za=0,Aa=0,Ba=0,Ca=0,Da=0,Ea=0,Fa=0,Ga=0,Ha=0,Ia=0,Ja=0,Ka=0,La=0,Ma=0,Na=0,Oa=0,Pa=0,Qa=0,Ra=0,Sa=0,Ta=0,Ua=0,Va=0,Wa=0,Xa=0;Xa=i;i=i+10192|0;Sa=Xa+10180|0;o=Xa+10176|0;p=Xa+10172|0;q=Xa+10168|0;r=Xa+10164|0;s=Xa+10160|0;t=Xa+10156|0;u=Xa+10152|0;V=Xa+10148|0;v=Xa+10144|0;Fa=Xa+10140|0;sa=Xa+10136|0;Q=Xa+10132|0;ha=Xa+10128|0;x=Xa+10124|0;w=Xa+10120|0;I=Xa+10116|0;F=Xa+10112|0;_=Xa+10040|0;ua=Xa+9968|0;ya=Xa+9936|0;Aa=Xa+9836|0;Ca=Xa+9832|0;va=Xa+9828|0;Va=Xa+9824|0;Da=Xa+9816|0;Ba=Xa+9812|0;wa=Xa+9808|0;Ra=Xa+9800|0;za=Xa+9796|0;ra=Xa+9792|0;ia=Xa+9788|0;ta=Xa+9784|0;la=Xa+9780|0;Ua=Xa+9776|0;na=Xa+9772|0;oa=Xa+9768|0;T=Xa+9764|0;Wa=Xa+9760|0;L=Xa+5920|0;pa=Xa+2080|0;ea=Xa+1120|0;ba=Xa+160|0;U=Xa+152|0;z=Xa+148|0;B=Xa+144|0;y=Xa+140|0;A=Xa+136|0;C=Xa+132|0;J=Xa+128|0;G=Xa+124|0;D=Xa+120|0;K=Xa+116|0;H=Xa+112|0;R=Xa+108|0;S=Xa+104|0;E=Xa+100|0;W=Xa+96|0;da=Xa+92|0;aa=Xa+88|0;X=Xa+84|0;Z=Xa+80|0;ca=Xa+76|0;$=Xa+72|0;ga=Xa+68|0;ka=Xa+64|0;ja=Xa+60|0;ma=Xa+56|0;xa=Xa+52|0;Pa=Xa+48|0;Ea=Xa+44|0;Ja=Xa+40|0;Ka=Xa+36|0;Na=Xa+32|0;Ga=Xa+28|0;La=Xa+24|0;Oa=Xa+20|0;Ha=Xa+16|0;Ia=Xa+12|0;Ma=Xa+8|0;Qa=Xa+4|0;Ta=Xa;c[Sa>>2]=a;c[o>>2]=b;c[p>>2]=d;c[q>>2]=e;c[r>>2]=f;c[s>>2]=h;c[t>>2]=j;c[u>>2]=k;c[V>>2]=l;c[v>>2]=m;c[ha>>2]=480;c[x>>2]=240;c[w>>2]=(c[Sa>>2]|0)+4;c[I>>2]=(c[Sa>>2]|0)+964;c[F>>2]=(c[Sa>>2]|0)+1924;g[Xa+9820>>2]=97.40908813476562;g[Da>>2]=0.0;c[Ua>>2]=0;g[na>>2]=0.0;d=(c[Sa>>2]|0)+6864|0;c[d>>2]=(c[d>>2]|0)+1;if(20<(1+(c[(c[Sa>>2]|0)+6868>>2]|0)|0))k=20;else k=1+(c[(c[Sa>>2]|0)+6868>>2]|0)|0;g[za>>2]=1.0/+(k|0);if(50<(1+(c[(c[Sa>>2]|0)+6868>>2]|0)|0))k=50;else k=1+(c[(c[Sa>>2]|0)+6868>>2]|0)|0;g[ra>>2]=1.0/+(k|0);if(1e3<(1+(c[(c[Sa>>2]|0)+6868>>2]|0)|0))k=1e3;else k=1+(c[(c[Sa>>2]|0)+6868>>2]|0)|0;g[ia>>2]=1.0/+(k|0);if((c[(c[Sa>>2]|0)+6868>>2]|0)<4)g[(c[Sa>>2]|0)+6844>>2]=.5;c[Q>>2]=c[(c[o>>2]|0)+64+8>>2];if(!(c[(c[Sa>>2]|0)+6868>>2]|0))c[(c[Sa>>2]|0)+5764>>2]=240;if((c[q>>2]|0)<(720-(c[(c[Sa>>2]|0)+5764>>2]|0)|0))k=c[q>>2]|0;else k=720-(c[(c[Sa>>2]|0)+5764>>2]|0)|0;qa[c[v>>2]&3](c[p>>2]|0,(c[Sa>>2]|0)+2884+(c[(c[Sa>>2]|0)+5764>>2]<<2)|0,k,c[r>>2]|0,c[s>>2]|0,c[t>>2]|0,c[u>>2]|0);if(((c[(c[Sa>>2]|0)+5764>>2]|0)+(c[q>>2]|0)|0)<720){Wa=(c[Sa>>2]|0)+5764|0;c[Wa>>2]=(c[Wa>>2]|0)+(c[q>>2]|0);i=Xa;return}d=(c[Sa>>2]|0)+8504|0;o=c[d>>2]|0;c[d>>2]=o+1;c[Wa>>2]=(c[Sa>>2]|0)+8516+(o*28|0);if((c[(c[Sa>>2]|0)+8504>>2]|0)>=200){o=(c[Sa>>2]|0)+8504|0;c[o>>2]=(c[o>>2]|0)-200}c[Fa>>2]=0;while(1){if((c[Fa>>2]|0)>=(c[x>>2]|0))break;g[U>>2]=+g[18176+(c[Fa>>2]<<2)>>2];g[L+(c[Fa>>2]<<3)>>2]=+g[U>>2]*+g[(c[Sa>>2]|0)+2884+(c[Fa>>2]<<2)>>2];g[L+(c[Fa>>2]<<3)+4>>2]=+g[U>>2]*+g[(c[Sa>>2]|0)+2884+((c[x>>2]|0)+(c[Fa>>2]|0)<<2)>>2];g[L+((c[ha>>2]|0)-(c[Fa>>2]|0)-1<<3)>>2]=+g[U>>2]*+g[(c[Sa>>2]|0)+2884+((c[ha>>2]|0)-(c[Fa>>2]|0)-1<<2)>>2];g[L+((c[ha>>2]|0)-(c[Fa>>2]|0)-1<<3)+4>>2]=+g[U>>2]*+g[(c[Sa>>2]|0)+2884+((c[ha>>2]|0)+(c[x>>2]|0)-(c[Fa>>2]|0)-1<<2)>>2];c[Fa>>2]=(c[Fa>>2]|0)+1}qj((c[Sa>>2]|0)+2884|0,(c[Sa>>2]|0)+2884+2880+-960|0,960|0)|0;c[T>>2]=(c[q>>2]|0)-(720-(c[(c[Sa>>2]|0)+5764>>2]|0));qa[c[v>>2]&3](c[p>>2]|0,(c[Sa>>2]|0)+2884+960|0,c[T>>2]|0,(c[r>>2]|0)+720-(c[(c[Sa>>2]|0)+5764>>2]|0)|0,c[s>>2]|0,c[t>>2]|0,c[u>>2]|0);c[(c[Sa>>2]|0)+5764>>2]=240+(c[T>>2]|0);Cc(c[Q>>2]|0,L,pa);if(+g[pa>>2]!=+g[pa>>2]){c[c[Wa>>2]>>2]=0;i=Xa;return}c[Fa>>2]=1;while(1){if((c[Fa>>2]|0)>=(c[x>>2]|0))break;g[z>>2]=+g[pa+(c[Fa>>2]<<3)>>2]+ +g[pa+((c[ha>>2]|0)-(c[Fa>>2]|0)<<3)>>2];g[y>>2]=+g[pa+(c[Fa>>2]<<3)+4>>2]-+g[pa+((c[ha>>2]|0)-(c[Fa>>2]|0)<<3)+4>>2];g[B>>2]=+g[pa+(c[Fa>>2]<<3)+4>>2]+ +g[pa+((c[ha>>2]|0)-(c[Fa>>2]|0)<<3)+4>>2];g[A>>2]=+g[pa+((c[ha>>2]|0)-(c[Fa>>2]|0)<<3)>>2]-+g[pa+(c[Fa>>2]<<3)>>2];g[C>>2]=+cj(+g[y>>2],+g[z>>2])*.15915493667125702;g[J>>2]=+g[C>>2]-+g[(c[w>>2]|0)+(c[Fa>>2]<<2)>>2];g[G>>2]=+g[J>>2]-+g[(c[I>>2]|0)+(c[Fa>>2]<<2)>>2];g[D>>2]=+cj(+g[A>>2],+g[B>>2])*.15915493667125702;g[K>>2]=+g[D>>2]-+g[C>>2];g[H>>2]=+g[K>>2]-+g[J>>2];g[R>>2]=+g[G>>2]-+M(+(+g[G>>2]+.5));n=+N(+(+g[R>>2]));g[ba+(c[Fa>>2]<<2)>>2]=n;g[R>>2]=+g[R>>2]*+g[R>>2];g[R>>2]=+g[R>>2]*+g[R>>2];g[S>>2]=+g[H>>2]-+M(+(+g[H>>2]+.5));n=+N(+(+g[S>>2]));U=ba+(c[Fa>>2]<<2)|0;g[U>>2]=+g[U>>2]+n;g[S>>2]=+g[S>>2]*+g[S>>2];g[S>>2]=+g[S>>2]*+g[S>>2];g[E>>2]=(+g[(c[F>>2]|0)+(c[Fa>>2]<<2)>>2]+ +g[R>>2]*2.0+ +g[S>>2])*.25;g[ea+(c[Fa>>2]<<2)>>2]=1.0/(+g[E>>2]*62341.81640625+1.0)-.014999999664723873;g[(c[w>>2]|0)+(c[Fa>>2]<<2)>>2]=+g[D>>2];g[(c[I>>2]|0)+(c[Fa>>2]<<2)>>2]=+g[K>>2];g[(c[F>>2]|0)+(c[Fa>>2]<<2)>>2]=+g[S>>2];c[Fa>>2]=(c[Fa>>2]|0)+1}g[Ca>>2]=0.0;g[va>>2]=0.0;g[(c[Wa>>2]|0)+16>>2]=0.0;g[Va>>2]=0.0;g[Ba>>2]=0.0;a:do if(!(c[(c[Sa>>2]|0)+6868>>2]|0)){c[sa>>2]=0;while(1){if((c[sa>>2]|0)>=18)break a;g[(c[Sa>>2]|0)+6420+(c[sa>>2]<<2)>>2]=1.0e10;g[(c[Sa>>2]|0)+6492+(c[sa>>2]<<2)>>2]=-1.0e10;c[sa>>2]=(c[sa>>2]|0)+1}}while(0);g[wa>>2]=0.0;g[ta>>2]=0.0;c[sa>>2]=0;while(1){if((c[sa>>2]|0)>=18)break;g[W>>2]=0.0;g[da>>2]=0.0;g[aa>>2]=0.0;c[Fa>>2]=c[19136+(c[sa>>2]<<2)>>2];while(1){if((c[Fa>>2]|0)>=(c[19136+((c[sa>>2]|0)+1<<2)>>2]|0))break;g[$>>2]=+g[pa+(c[Fa>>2]<<3)>>2]*+g[pa+(c[Fa>>2]<<3)>>2]+ +g[pa+((c[ha>>2]|0)-(c[Fa>>2]|0)<<3)>>2]*+g[pa+((c[ha>>2]|0)-(c[Fa>>2]|0)<<3)>>2]+ +g[pa+(c[Fa>>2]<<3)+4>>2]*+g[pa+(c[Fa>>2]<<3)+4>>2]+ +g[pa+((c[ha>>2]|0)-(c[Fa>>2]|0)<<3)+4>>2]*+g[pa+((c[ha>>2]|0)-(c[Fa>>2]|0)<<3)+4>>2];g[W>>2]=+g[W>>2]+ +g[$>>2];g[da>>2]=+g[da>>2]+ +g[$>>2]*+g[ea+(c[Fa>>2]<<2)>>2];g[aa>>2]=+g[aa>>2]+ +g[$>>2]*2.0*(.5-+g[ba+(c[Fa>>2]<<2)>>2]);c[Fa>>2]=(c[Fa>>2]|0)+1}if(!(+g[W>>2]<1.0e9)){fa=37;break}if(+g[W>>2]!=+g[W>>2]){fa=37;break}g[(c[Sa>>2]|0)+5844+((c[(c[Sa>>2]|0)+6856>>2]|0)*72|0)+(c[sa>>2]<<2)>>2]=+g[W>>2];g[Va>>2]=+g[Va>>2]+ +g[aa>>2]/(+g[W>>2]+1.0000000036274937e-15);n=+O(+(+g[W>>2]+1.000000013351432e-10));g[ta>>2]=+g[ta>>2]+n;n=+Y(+(+g[W>>2]+1.000000013351432e-10));g[ua+(c[sa>>2]<<2)>>2]=n;k=c[sa>>2]|0;if(+g[ua+(c[sa>>2]<<2)>>2]<+g[(c[Sa>>2]|0)+6420+(c[sa>>2]<<2)>>2]+.009999999776482582)n=+g[ua+(k<<2)>>2];else n=+g[(c[Sa>>2]|0)+6420+(k<<2)>>2]+.009999999776482582;g[(c[Sa>>2]|0)+6420+(c[sa>>2]<<2)>>2]=n;k=c[sa>>2]|0;if(+g[ua+(c[sa>>2]<<2)>>2]>+g[(c[Sa>>2]|0)+6492+(c[sa>>2]<<2)>>2]-.10000000149011612)n=+g[ua+(k<<2)>>2];else n=+g[(c[Sa>>2]|0)+6492+(k<<2)>>2]-.10000000149011612;g[(c[Sa>>2]|0)+6492+(c[sa>>2]<<2)>>2]=n;if(+g[(c[Sa>>2]|0)+6492+(c[sa>>2]<<2)>>2]<+g[(c[Sa>>2]|0)+6420+(c[sa>>2]<<2)>>2]+1.0){U=(c[Sa>>2]|0)+6492+(c[sa>>2]<<2)|0;g[U>>2]=+g[U>>2]+.5;U=(c[Sa>>2]|0)+6420+(c[sa>>2]<<2)|0;g[U>>2]=+g[U>>2]-.5}g[wa>>2]=+g[wa>>2]+(+g[ua+(c[sa>>2]<<2)>>2]-+g[(c[Sa>>2]|0)+6420+(c[sa>>2]<<2)>>2])/(+g[(c[Sa>>2]|0)+6492+(c[sa>>2]<<2)>>2]+1.0000000036274937e-15-+g[(c[Sa>>2]|0)+6420+(c[sa>>2]<<2)>>2]);g[Z>>2]=0.0;g[X>>2]=0.0;c[Fa>>2]=0;while(1){if((c[Fa>>2]|0)>=8)break;n=+O(+(+g[(c[Sa>>2]|0)+5844+((c[Fa>>2]|0)*72|0)+(c[sa>>2]<<2)>>2]));g[X>>2]=+g[X>>2]+n;g[Z>>2]=+g[Z>>2]+ +g[(c[Sa>>2]|0)+5844+((c[Fa>>2]|0)*72|0)+(c[sa>>2]<<2)>>2];c[Fa>>2]=(c[Fa>>2]|0)+1}if(.9900000095367432<+g[X>>2]/+O(+(+g[Z>>2]*8.0+1.0e-15)))n=.9900000095367432;else n=+g[X>>2]/+O(+(+g[Z>>2]*8.0+1.0e-15));g[ca>>2]=n;g[ca>>2]=+g[ca>>2]*+g[ca>>2];g[ca>>2]=+g[ca>>2]*+g[ca>>2];g[Ba>>2]=+g[Ba>>2]+ +g[ca>>2];if(+g[da>>2]/(+g[W>>2]+1.0000000036274937e-15)>+g[ca>>2]*+g[(c[Sa>>2]|0)+5768+(c[sa>>2]<<2)>>2])n=+g[da>>2]/(+g[W>>2]+1.0000000036274937e-15);else n=+g[ca>>2]*+g[(c[Sa>>2]|0)+5768+(c[sa>>2]<<2)>>2];g[_+(c[sa>>2]<<2)>>2]=n;g[Ca>>2]=+g[Ca>>2]+ +g[_+(c[sa>>2]<<2)>>2];if((c[sa>>2]|0)>=9)g[Ca>>2]=+g[Ca>>2]-+g[_+((c[sa>>2]|0)-18+9<<2)>>2];if(+g[va>>2]>(+((c[sa>>2]|0)-18|0)*.029999999329447746+1.0)*+g[Ca>>2])n=+g[va>>2];else n=(+((c[sa>>2]|0)-18|0)*.029999999329447746+1.0)*+g[Ca>>2];g[va>>2]=n;g[Da>>2]=+g[Da>>2]+ +g[_+(c[sa>>2]<<2)>>2]*+((c[sa>>2]|0)-8|0);g[(c[Sa>>2]|0)+5768+(c[sa>>2]<<2)>>2]=+g[_+(c[sa>>2]<<2)>>2];c[sa>>2]=(c[sa>>2]|0)+1}if((fa|0)==37){c[c[Wa>>2]>>2]=0;i=Xa;return}g[la>>2]=0.0;c[Ua>>2]=0;g[na>>2]=0.0;g[oa>>2]=5.699999746866524e-04/+(1<<(0>((c[V>>2]|0)-8|0)?0:(c[V>>2]|0)-8|0)|0);g[oa>>2]=+g[oa>>2]*+g[oa>>2];c[sa>>2]=0;while(1){if((c[sa>>2]|0)>=21)break;g[ga>>2]=0.0;c[ka>>2]=c[19212+(c[sa>>2]<<2)>>2];c[ja>>2]=c[19212+((c[sa>>2]|0)+1<<2)>>2];c[Fa>>2]=c[ka>>2];while(1){if((c[Fa>>2]|0)>=(c[ja>>2]|0))break;g[ma>>2]=+g[pa+(c[Fa>>2]<<3)>>2]*+g[pa+(c[Fa>>2]<<3)>>2]+ +g[pa+((c[ha>>2]|0)-(c[Fa>>2]|0)<<3)>>2]*+g[pa+((c[ha>>2]|0)-(c[Fa>>2]|0)<<3)>>2]+ +g[pa+(c[Fa>>2]<<3)+4>>2]*+g[pa+(c[Fa>>2]<<3)+4>>2]+ +g[pa+((c[ha>>2]|0)-(c[Fa>>2]|0)<<3)+4>>2]*+g[pa+((c[ha>>2]|0)-(c[Fa>>2]|0)<<3)+4>>2];g[ga>>2]=+g[ga>>2]+ +g[ma>>2];c[Fa>>2]=(c[Fa>>2]|0)+1}g[na>>2]=+g[na>>2]>+g[ga>>2]?+g[na>>2]:+g[ga>>2];if((1.0-+g[ia>>2])*+g[(c[Sa>>2]|0)+6564+(c[sa>>2]<<2)>>2]>+g[ga>>2])n=(1.0-+g[ia>>2])*+g[(c[Sa>>2]|0)+6564+(c[sa>>2]<<2)>>2];else n=+g[ga>>2];g[(c[Sa>>2]|0)+6564+(c[sa>>2]<<2)>>2]=n;if(+g[ga>>2]>+g[(c[Sa>>2]|0)+6564+(c[sa>>2]<<2)>>2])n=+g[ga>>2];else n=+g[(c[Sa>>2]|0)+6564+(c[sa>>2]<<2)>>2];g[ga>>2]=n;g[la>>2]=+g[la>>2]*.05000000074505806>+g[ga>>2]?+g[la>>2]*.05000000074505806:+g[ga>>2];if((+g[ga>>2]>+g[la>>2]*.1?+g[ga>>2]*1.0e9>+g[na>>2]:0)?+g[ga>>2]>+g[oa>>2]*+((c[ja>>2]|0)-(c[ka>>2]|0)|0):0)c[Ua>>2]=c[sa>>2];c[sa>>2]=(c[sa>>2]|0)+1}if((c[(c[Sa>>2]|0)+6868>>2]|0)<=2)c[Ua>>2]=20;g[ta>>2]=+hj(+g[ta>>2])*20.0;if(+g[(c[Sa>>2]|0)+6848>>2]-.029999999329447746>+g[ta>>2])n=+g[(c[Sa>>2]|0)+6848>>2]-.029999999329447746;else n=+g[ta>>2];g[(c[Sa>>2]|0)+6848>>2]=n;pa=(c[Sa>>2]|0)+6852|0;g[pa>>2]=+g[pa>>2]*(1.0-+g[ra>>2]);if(+g[ta>>2]<+g[(c[Sa>>2]|0)+6848>>2]-30.0){ta=(c[Sa>>2]|0)+6852|0;g[ta>>2]=+g[ta>>2]+ +g[ra>>2]}c[Fa>>2]=0;while(1){if((c[Fa>>2]|0)>=8)break;g[xa>>2]=0.0;c[sa>>2]=0;while(1){if((c[sa>>2]|0)>=16)break;g[xa>>2]=+g[xa>>2]+ +g[19300+((c[Fa>>2]<<4)+(c[sa>>2]|0)<<2)>>2]*+g[ua+(c[sa>>2]<<2)>>2];c[sa>>2]=(c[sa>>2]|0)+1}g[ya+(c[Fa>>2]<<2)>>2]=+g[xa>>2];c[Fa>>2]=(c[Fa>>2]|0)+1}g[Ba>>2]=+g[Ba>>2]/18.0;n=+g[wa>>2]/18.0;g[wa>>2]=n;g[wa>>2]=(c[(c[Sa>>2]|0)+6868>>2]|0)<10?.5:n;g[Va>>2]=+g[Va>>2]/18.0;g[(c[Wa>>2]|0)+16>>2]=+g[Va>>2]+(1.0-+g[Va>>2])*+g[wa>>2];g[Ca>>2]=+g[va>>2]/9.0;if(+g[Ca>>2]>+g[(c[Sa>>2]|0)+5840>>2]*.800000011920929)n=+g[Ca>>2];else n=+g[(c[Sa>>2]|0)+5840>>2]*.800000011920929;g[Ca>>2]=n;g[(c[Sa>>2]|0)+5840>>2]=+g[Ca>>2];g[Da>>2]=+g[Da>>2]/64.0;g[(c[Wa>>2]|0)+8>>2]=+g[Da>>2];c[(c[Sa>>2]|0)+6856>>2]=((c[(c[Sa>>2]|0)+6856>>2]|0)+1|0)%8|0;Da=(c[Sa>>2]|0)+6868|0;c[Da>>2]=(c[Da>>2]|0)+1;g[(c[Wa>>2]|0)+4>>2]=+g[Ca>>2];c[Fa>>2]=0;while(1){if((c[Fa>>2]|0)>=4)break;g[Aa+(c[Fa>>2]<<2)>>2]=(+g[ya+(c[Fa>>2]<<2)>>2]+ +g[(c[Sa>>2]|0)+6648+((c[Fa>>2]|0)+24<<2)>>2])*-.12298999726772308+(+g[(c[Sa>>2]|0)+6648+(c[Fa>>2]<<2)>>2]+ +g[(c[Sa>>2]|0)+6648+((c[Fa>>2]|0)+16<<2)>>2])*.49195000529289246+ +g[(c[Sa>>2]|0)+6648+((c[Fa>>2]|0)+8<<2)>>2]*.6969299912452698-+g[(c[Sa>>2]|0)+6776+(c[Fa>>2]<<2)>>2]*1.4349000453948975;c[Fa>>2]=(c[Fa>>2]|0)+1}c[Fa>>2]=0;while(1){if((c[Fa>>2]|0)>=4)break;g[(c[Sa>>2]|0)+6776+(c[Fa>>2]<<2)>>2]=(1.0-+g[za>>2])*+g[(c[Sa>>2]|0)+6776+(c[Fa>>2]<<2)>>2]+ +g[za>>2]*+g[ya+(c[Fa>>2]<<2)>>2];c[Fa>>2]=(c[Fa>>2]|0)+1}c[Fa>>2]=0;while(1){if((c[Fa>>2]|0)>=4)break;g[Aa+(4+(c[Fa>>2]|0)<<2)>>2]=(+g[ya+(c[Fa>>2]<<2)>>2]-+g[(c[Sa>>2]|0)+6648+((c[Fa>>2]|0)+24<<2)>>2])*.6324599981307983+(+g[(c[Sa>>2]|0)+6648+(c[Fa>>2]<<2)>>2]-+g[(c[Sa>>2]|0)+6648+((c[Fa>>2]|0)+16<<2)>>2])*.31622999906539917;c[Fa>>2]=(c[Fa>>2]|0)+1}c[Fa>>2]=0;while(1){if((c[Fa>>2]|0)>=3)break;g[Aa+(8+(c[Fa>>2]|0)<<2)>>2]=(+g[ya+(c[Fa>>2]<<2)>>2]+ +g[(c[Sa>>2]|0)+6648+((c[Fa>>2]|0)+24<<2)>>2])*.5345199704170227-(+g[(c[Sa>>2]|0)+6648+(c[Fa>>2]<<2)>>2]+ +g[(c[Sa>>2]|0)+6648+((c[Fa>>2]|0)+16<<2)>>2])*.26725998520851135-+g[(c[Sa>>2]|0)+6648+((c[Fa>>2]|0)+8<<2)>>2]*.5345199704170227;c[Fa>>2]=(c[Fa>>2]|0)+1}b:do if((c[(c[Sa>>2]|0)+6868>>2]|0)>5){c[Fa>>2]=0;while(1){if((c[Fa>>2]|0)>=9)break b;g[(c[Sa>>2]|0)+6808+(c[Fa>>2]<<2)>>2]=(1.0-+g[za>>2])*+g[(c[Sa>>2]|0)+6808+(c[Fa>>2]<<2)>>2]+ +g[za>>2]*+g[Aa+(c[Fa>>2]<<2)>>2]*+g[Aa+(c[Fa>>2]<<2)>>2];c[Fa>>2]=(c[Fa>>2]|0)+1}}while(0);c[Fa>>2]=0;while(1){if((c[Fa>>2]|0)>=8)break;g[(c[Sa>>2]|0)+6648+((c[Fa>>2]|0)+24<<2)>>2]=+g[(c[Sa>>2]|0)+6648+((c[Fa>>2]|0)+16<<2)>>2];g[(c[Sa>>2]|0)+6648+((c[Fa>>2]|0)+16<<2)>>2]=+g[(c[Sa>>2]|0)+6648+((c[Fa>>2]|0)+8<<2)>>2];g[(c[Sa>>2]|0)+6648+((c[Fa>>2]|0)+8<<2)>>2]=+g[(c[Sa>>2]|0)+6648+(c[Fa>>2]<<2)>>2];g[(c[Sa>>2]|0)+6648+(c[Fa>>2]<<2)>>2]=+g[ya+(c[Fa>>2]<<2)>>2];c[Fa>>2]=(c[Fa>>2]|0)+1}c[Fa>>2]=0;while(1){if((c[Fa>>2]|0)>=9)break;n=+O(+(+g[(c[Sa>>2]|0)+6808+(c[Fa>>2]<<2)>>2]));g[Aa+(11+(c[Fa>>2]|0)<<2)>>2]=n;c[Fa>>2]=(c[Fa>>2]|0)+1}g[Aa+80>>2]=+g[(c[Wa>>2]|0)+4>>2];g[Aa+84>>2]=+g[(c[Wa>>2]|0)+16>>2];g[Aa+88>>2]=+g[Ba>>2];g[Aa+92>>2]=+g[(c[Wa>>2]|0)+8>>2];g[Aa+96>>2]=+g[(c[Sa>>2]|0)+6852>>2];dj(20616,Aa,Ra);g[Ra>>2]=(+g[Ra>>2]+1.0)*.5;g[Ra>>2]=+g[Ra>>2]*1.2100000381469727*+g[Ra>>2]+.009999999776482582-+P(+(+g[Ra>>2]),10.0)*.23000000417232513;g[Ra+4>>2]=+g[Ra+4>>2]*.5+.5;g[Ra>>2]=+g[Ra+4>>2]*+g[Ra>>2]+(1.0-+g[Ra+4>>2])*.5;g[Pa>>2]=+g[Ra+4>>2]*4.999999873689376e-05;g[Ea>>2]=.05000000074505806;if(.05000000074505806>(.949999988079071<+g[Ra>>2]?.949999988079071:+g[Ra>>2]))n=.05000000074505806;else n=.949999988079071<+g[Ra>>2]?.949999988079071:+g[Ra>>2];g[Ia>>2]=n;if(.949999988079071<+g[(c[Sa>>2]|0)+6844>>2])n=.949999988079071;else n=+g[(c[Sa>>2]|0)+6844>>2];if(!(.05000000074505806>n))if(.949999988079071<+g[(c[Sa>>2]|0)+6844>>2])n=.949999988079071;else n=+g[(c[Sa>>2]|0)+6844>>2];else n=.05000000074505806;g[Ma>>2]=n;n=+N(+(+g[Ia>>2]-+g[Ma>>2]))*.05000000074505806;g[Ea>>2]=n/(+g[Ia>>2]*(1.0-+g[Ma>>2])+ +g[Ma>>2]*(1.0-+g[Ia>>2]))+.009999999776482582;g[Ja>>2]=(1.0-+g[(c[Sa>>2]|0)+6844>>2])*(1.0-+g[Pa>>2])+ +g[(c[Sa>>2]|0)+6844>>2]*+g[Pa>>2];g[Ka>>2]=+g[(c[Sa>>2]|0)+6844>>2]*(1.0-+g[Pa>>2])+(1.0-+g[(c[Sa>>2]|0)+6844>>2])*+g[Pa>>2];n=+P(+(1.0-+g[Ra>>2]),+(+g[Ea>>2]));g[Ja>>2]=+g[Ja>>2]*n;n=+P(+(+g[Ra>>2]),+(+g[Ea>>2]));g[Ka>>2]=+g[Ka>>2]*n;g[(c[Sa>>2]|0)+6844>>2]=+g[Ka>>2]/(+g[Ja>>2]+ +g[Ka>>2]);g[(c[Wa>>2]|0)+20>>2]=+g[(c[Sa>>2]|0)+6844>>2];g[La>>2]=9.999999682655225e-21;g[Oa>>2]=+P(+(1.0-+g[Ra>>2]),+(+g[Ea>>2]));g[Ha>>2]=+P(+(+g[Ra>>2]),+(+g[Ea>>2]));if((c[(c[Sa>>2]|0)+6868>>2]|0)==1){g[(c[Sa>>2]|0)+6888>>2]=.5;g[(c[Sa>>2]|0)+7688>>2]=.5}g[Na>>2]=+g[(c[Sa>>2]|0)+6888>>2]+ +g[(c[Sa>>2]|0)+6888+4>>2];g[Ga>>2]=+g[(c[Sa>>2]|0)+7688>>2]+ +g[(c[Sa>>2]|0)+7688+4>>2];g[(c[Sa>>2]|0)+6888>>2]=+g[Na>>2]*(1.0-+g[Pa>>2])*+g[Oa>>2];g[(c[Sa>>2]|0)+7688>>2]=+g[Ga>>2]*(1.0-+g[Pa>>2])*+g[Ha>>2];c[Fa>>2]=1;while(1){if((c[Fa>>2]|0)>=199)break;g[(c[Sa>>2]|0)+6888+(c[Fa>>2]<<2)>>2]=+g[(c[Sa>>2]|0)+6888+((c[Fa>>2]|0)+1<<2)>>2]*+g[Oa>>2];g[(c[Sa>>2]|0)+7688+(c[Fa>>2]<<2)>>2]=+g[(c[Sa>>2]|0)+7688+((c[Fa>>2]|0)+1<<2)>>2]*+g[Ha>>2];c[Fa>>2]=(c[Fa>>2]|0)+1}g[(c[Sa>>2]|0)+6888+796>>2]=+g[Ga>>2]*+g[Pa>>2]*+g[Oa>>2];g[(c[Sa>>2]|0)+7688+796>>2]=+g[Na>>2]*+g[Pa>>2]*+g[Ha>>2];c[Fa>>2]=0;while(1){if((c[Fa>>2]|0)>=200)break;g[La>>2]=+g[La>>2]+(+g[(c[Sa>>2]|0)+6888+(c[Fa>>2]<<2)>>2]+ +g[(c[Sa>>2]|0)+7688+(c[Fa>>2]<<2)>>2]);c[Fa>>2]=(c[Fa>>2]|0)+1}g[La>>2]=1.0/+g[La>>2];c[Fa>>2]=0;while(1){if((c[Fa>>2]|0)>=200)break;Pa=(c[Sa>>2]|0)+6888+(c[Fa>>2]<<2)|0;g[Pa>>2]=+g[Pa>>2]*+g[La>>2];Pa=(c[Sa>>2]|0)+7688+(c[Fa>>2]<<2)|0;g[Pa>>2]=+g[Pa>>2]*+g[La>>2];c[Fa>>2]=(c[Fa>>2]|0)+1}g[La>>2]=+g[(c[Sa>>2]|0)+7688>>2];c[Fa>>2]=1;while(1){if((c[Fa>>2]|0)>=200)break;g[La>>2]=+g[La>>2]+ +g[(c[Sa>>2]|0)+6888+(c[Fa>>2]<<2)>>2];c[Fa>>2]=(c[Fa>>2]|0)+1}k=c[Sa>>2]|0;do if(+g[Ra+4>>2]>.75){if(+g[k+6844>>2]>.9){Oa=(c[Sa>>2]|0)+8500|0;Pa=(c[Oa>>2]|0)+1|0;c[Oa>>2]=Pa;g[Qa>>2]=1.0/+(Pa|0);if((c[(c[Sa>>2]|0)+8500>>2]|0)<500)k=c[(c[Sa>>2]|0)+8500>>2]|0;else k=500;c[(c[Sa>>2]|0)+8500>>2]=k;if(-.20000000298023224>+g[Ra>>2]-+g[(c[Sa>>2]|0)+8492>>2])n=-.20000000298023224;else n=+g[Ra>>2]-+g[(c[Sa>>2]|0)+8492>>2];Pa=(c[Sa>>2]|0)+8492|0;g[Pa>>2]=+g[Pa>>2]+ +g[Qa>>2]*n}if(!(+g[(c[Sa>>2]|0)+6844>>2]<.1))break;Pa=(c[Sa>>2]|0)+8496|0;Qa=(c[Pa>>2]|0)+1|0;c[Pa>>2]=Qa;g[Ta>>2]=1.0/+(Qa|0);if((c[(c[Sa>>2]|0)+8496>>2]|0)<500)k=c[(c[Sa>>2]|0)+8496>>2]|0;else k=500;c[(c[Sa>>2]|0)+8496>>2]=k;if(.20000000298023224<+g[Ra>>2]-+g[(c[Sa>>2]|0)+8488>>2])n=.20000000298023224;else n=+g[Ra>>2]-+g[(c[Sa>>2]|0)+8488>>2];Ra=(c[Sa>>2]|0)+8488|0;g[Ra>>2]=+g[Ra>>2]+ +g[Ta>>2]*n}else{if(!(c[k+8500>>2]|0))g[(c[Sa>>2]|0)+8492>>2]=.8999999761581421;if(c[(c[Sa>>2]|0)+8496>>2]|0)break;g[(c[Sa>>2]|0)+8488>>2]=.10000000149011612}while(0);if((c[(c[Sa>>2]|0)+6860>>2]|0)!=(+g[(c[Sa>>2]|0)+6844>>2]>.5|0))c[(c[Sa>>2]|0)+6864>>2]=0;c[(c[Sa>>2]|0)+6860>>2]=+g[(c[Sa>>2]|0)+6844>>2]>.5&1;c[(c[Wa>>2]|0)+24>>2]=c[Ua>>2];g[(c[Wa>>2]|0)+12>>2]=+g[Va>>2];c[c[Wa>>2]>>2]=1;i=Xa;return}function cj(a,b){a=+a;b=+b;var c=0,d=0,e=0,f=0,h=0,j=0,k=0,l=0;l=i;i=i+32|0;c=l+24|0;d=l+20|0;e=l+16|0;j=l+12|0;k=l+8|0;f=l+4|0;h=l;g[d>>2]=a;g[e>>2]=b;a=+N(+(+g[e>>2]));if(a+ +N(+(+g[d>>2]))<9.999999717180685e-10){g[e>>2]=+g[e>>2]*999999995904.0;g[d>>2]=+g[d>>2]*999999995904.0}g[j>>2]=+g[e>>2]*+g[e>>2];g[k>>2]=+g[d>>2]*+g[d>>2];if(+g[j>>2]<+g[k>>2]){g[f>>2]=(+g[k>>2]+ +g[j>>2]*.6784840226173401)*(+g[k>>2]+ +g[j>>2]*.0859554186463356);if(+g[f>>2]!=0.0){g[c>>2]=-+g[e>>2]*+g[d>>2]*(+g[k>>2]+ +g[j>>2]*.43157973885536194)/+g[f>>2]+(+g[d>>2]<0.0?-1.5707963705062866:1.5707963705062866);a=+g[c>>2];i=l;return +a}else{g[c>>2]=+g[d>>2]<0.0?-1.5707963705062866:1.5707963705062866;a=+g[c>>2];i=l;return +a}}else{g[h>>2]=(+g[j>>2]+ +g[k>>2]*.6784840226173401)*(+g[j>>2]+ +g[k>>2]*.0859554186463356);if(+g[h>>2]!=0.0){g[c>>2]=+g[e>>2]*+g[d>>2]*(+g[j>>2]+ +g[k>>2]*.43157973885536194)/+g[h>>2]+(+g[d>>2]<0.0?-1.5707963705062866:1.5707963705062866)-(+g[e>>2]*+g[d>>2]<0.0?-1.5707963705062866:1.5707963705062866);a=+g[c>>2];i=l;return +a}else{g[c>>2]=(+g[d>>2]<0.0?-1.5707963705062866:1.5707963705062866)-(+g[e>>2]*+g[d>>2]<0.0?-1.5707963705062866:1.5707963705062866);a=+g[c>>2];i=l;return +a}}return 0.0}function dj(a,b,d){a=a|0;b=b|0;d=d|0;var e=0.0,f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0.0;r=i;i=i+448|0;k=r+436|0;f=r+432|0;l=r+428|0;o=r+424|0;n=r+24|0;m=r+16|0;h=r+12|0;j=r+8|0;p=r+4|0;q=r;c[k>>2]=a;c[f>>2]=b;c[l>>2]=d;c[m>>2]=c[(c[k>>2]|0)+8>>2];c[o>>2]=0;while(1){if((c[o>>2]|0)>=(c[(c[(c[k>>2]|0)+4>>2]|0)+4>>2]|0))break;d=c[m>>2]|0;c[m>>2]=d+4;g[j>>2]=+g[d>>2];c[h>>2]=0;while(1){e=+g[j>>2];if((c[h>>2]|0)>=(c[c[(c[k>>2]|0)+4>>2]>>2]|0))break;s=+g[(c[f>>2]|0)+(c[h>>2]<<2)>>2];d=c[m>>2]|0;c[m>>2]=d+4;g[j>>2]=e+s*+g[d>>2];c[h>>2]=(c[h>>2]|0)+1}s=+ej(e);g[n+(c[o>>2]<<2)>>2]=s;c[o>>2]=(c[o>>2]|0)+1}c[o>>2]=0;while(1){if((c[o>>2]|0)>=(c[(c[(c[k>>2]|0)+4>>2]|0)+8>>2]|0))break;j=c[m>>2]|0;c[m>>2]=j+4;g[q>>2]=+g[j>>2];c[p>>2]=0;while(1){e=+g[q>>2];if((c[p>>2]|0)>=(c[(c[(c[k>>2]|0)+4>>2]|0)+4>>2]|0))break;s=+g[n+(c[p>>2]<<2)>>2];j=c[m>>2]|0;c[m>>2]=j+4;g[q>>2]=e+s*+g[j>>2];c[p>>2]=(c[p>>2]|0)+1}s=+ej(e);g[(c[l>>2]|0)+(c[o>>2]<<2)>>2]=s;c[o>>2]=(c[o>>2]|0)+1}i=r;return}function ej(a){a=+a;var b=0,d=0,e=0,f=0,h=0,j=0,k=0;k=i;i=i+32|0;b=k+20|0;d=k+16|0;f=k+12|0;j=k+8|0;e=k+4|0;h=k;g[d>>2]=a;g[h>>2]=1.0;if(!(+g[d>>2]<8.0)){g[b>>2]=1.0;a=+g[b>>2];i=k;return +a}if(!(+g[d>>2]>-8.0)){g[b>>2]=-1.0;a=+g[b>>2];i=k;return +a}if(+g[d>>2]!=+g[d>>2]){g[b>>2]=0.0;a=+g[b>>2];i=k;return +a}if(+g[d>>2]<0.0){g[d>>2]=-+g[d>>2];g[h>>2]=-1.0}c[f>>2]=~~+M(+(+g[d>>2]*25.0+.5));g[d>>2]=+g[d>>2]-+(c[f>>2]|0)*.03999999910593033;g[j>>2]=+g[19812+(c[f>>2]<<2)>>2];g[e>>2]=1.0-+g[j>>2]*+g[j>>2];g[j>>2]=+g[j>>2]+ +g[d>>2]*+g[e>>2]*(1.0-+g[j>>2]*+g[d>>2]);g[b>>2]=+g[h>>2]*+g[j>>2];a=+g[b>>2];i=k;return +a}function fj(){var a=0;if(!(c[7632]|0))a=30572;else a=c[(fa()|0)+64>>2]|0;return a|0}function gj(a){a=+a;var b=0;b=(g[k>>2]=a,c[k>>2]|0);if((b&2130706432)>>>0<=1249902592){b=(b|0)<0;a=b?a+-8388608.0+8388608.0:a+8388608.0+-8388608.0;if(a==0.0)a=b?-0.0:0.0}return +a}function hj(a){a=+a;var b=0,d=0,e=0,f=0,g=0.0,i=0.0,j=0.0,l=0.0,m=0.0;h[k>>3]=a;d=c[k>>2]|0;b=c[k+4>>2]|0;e=(b|0)<0;do if(e|b>>>0<1048576){g=+N(+a);h[k>>3]=g;if((c[k>>2]|0)==0&(c[k+4>>2]|0)==0){a=-1.0/(a*a);break}if(e){a=(a-a)/0.0;break}else{h[k>>3]=a*18014398509481984.0;b=c[k+4>>2]|0;e=c[k>>2]|0;d=-1077;f=9;break}}else if(b>>>0<=2146435071)if((d|0)==0&0==0&(b|0)==1072693248)a=0.0;else{e=d;d=-1023;f=9}while(0);if((f|0)==9){f=b+614242|0;c[k>>2]=e;c[k+4>>2]=(f&1048575)+1072079006;j=+h[k>>3]+-1.0;i=j*(j*.5);l=j/(j+2.0);m=l*l;a=m*m;h[k>>3]=j-i;e=c[k+4>>2]|0;c[k>>2]=0;c[k+4>>2]=e;g=+h[k>>3];a=j-g-i+l*(i+(a*(a*(a*.15313837699209373+.22222198432149784)+.3999999999940942)+m*(a*(a*(a*.14798198605116586+.1818357216161805)+.2857142874366239)+.6666666666666735)));m=g*.4342944818781689;i=+(d+(f>>>20)|0);l=i*.30102999566361177;j=l+m;a=j+(m+(l-j)+(a*.4342944818781689+(i*3.694239077158931e-13+(g+a)*2.5082946711645275e-11)))}return +a}function ij(a){a=+a;return ~~+gj(a)|0}function jj(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0;do if(a>>>0<245){o=a>>>0<11?16:a+11&-8;a=o>>>3;j=c[7644]|0;b=j>>>a;if(b&3|0){b=(b&1^1)+a|0;d=30616+(b<<1<<2)|0;e=d+8|0;f=c[e>>2]|0;g=f+8|0;h=c[g>>2]|0;do if((d|0)!=(h|0)){if(h>>>0<(c[7648]|0)>>>0)ga();a=h+12|0;if((c[a>>2]|0)==(f|0)){c[a>>2]=d;c[e>>2]=h;break}else ga()}else c[7644]=j&~(1<<b);while(0);L=b<<3;c[f+4>>2]=L|3;L=f+L+4|0;c[L>>2]=c[L>>2]|1;L=g;return L|0}h=c[7646]|0;if(o>>>0>h>>>0){if(b|0){d=2<<a;d=b<<a&(d|0-d);d=(d&0-d)+-1|0;i=d>>>12&16;d=d>>>i;f=d>>>5&8;d=d>>>f;g=d>>>2&4;d=d>>>g;e=d>>>1&2;d=d>>>e;b=d>>>1&1;b=(f|i|g|e|b)+(d>>>b)|0;d=30616+(b<<1<<2)|0;e=d+8|0;g=c[e>>2]|0;i=g+8|0;f=c[i>>2]|0;do if((d|0)!=(f|0)){if(f>>>0<(c[7648]|0)>>>0)ga();a=f+12|0;if((c[a>>2]|0)==(g|0)){c[a>>2]=d;c[e>>2]=f;k=c[7646]|0;break}else ga()}else{c[7644]=j&~(1<<b);k=h}while(0);h=(b<<3)-o|0;c[g+4>>2]=o|3;e=g+o|0;c[e+4>>2]=h|1;c[e+h>>2]=h;if(k|0){f=c[7649]|0;b=k>>>3;d=30616+(b<<1<<2)|0;a=c[7644]|0;b=1<<b;if(a&b){a=d+8|0;b=c[a>>2]|0;if(b>>>0<(c[7648]|0)>>>0)ga();else{l=a;m=b}}else{c[7644]=a|b;l=d+8|0;m=d}c[l>>2]=f;c[m+12>>2]=f;c[f+8>>2]=m;c[f+12>>2]=d}c[7646]=h;c[7649]=e;L=i;return L|0}a=c[7645]|0;if(a){d=(a&0-a)+-1|0;K=d>>>12&16;d=d>>>K;J=d>>>5&8;d=d>>>J;L=d>>>2&4;d=d>>>L;b=d>>>1&2;d=d>>>b;e=d>>>1&1;e=c[30880+((J|K|L|b|e)+(d>>>e)<<2)>>2]|0;d=(c[e+4>>2]&-8)-o|0;b=e;while(1){a=c[b+16>>2]|0;if(!a){a=c[b+20>>2]|0;if(!a){j=e;break}}b=(c[a+4>>2]&-8)-o|0;L=b>>>0<d>>>0;d=L?b:d;b=a;e=L?a:e}g=c[7648]|0;if(j>>>0<g>>>0)ga();i=j+o|0;if(j>>>0>=i>>>0)ga();h=c[j+24>>2]|0;e=c[j+12>>2]|0;do if((e|0)==(j|0)){b=j+20|0;a=c[b>>2]|0;if(!a){b=j+16|0;a=c[b>>2]|0;if(!a){n=0;break}}while(1){e=a+20|0;f=c[e>>2]|0;if(f|0){a=f;b=e;continue}e=a+16|0;f=c[e>>2]|0;if(!f)break;else{a=f;b=e}}if(b>>>0<g>>>0)ga();else{c[b>>2]=0;n=a;break}}else{f=c[j+8>>2]|0;if(f>>>0<g>>>0)ga();a=f+12|0;if((c[a>>2]|0)!=(j|0))ga();b=e+8|0;if((c[b>>2]|0)==(j|0)){c[a>>2]=e;c[b>>2]=f;n=e;break}else ga()}while(0);do if(h|0){a=c[j+28>>2]|0;b=30880+(a<<2)|0;if((j|0)==(c[b>>2]|0)){c[b>>2]=n;if(!n){c[7645]=c[7645]&~(1<<a);break}}else{if(h>>>0<(c[7648]|0)>>>0)ga();a=h+16|0;if((c[a>>2]|0)==(j|0))c[a>>2]=n;else c[h+20>>2]=n;if(!n)break}b=c[7648]|0;if(n>>>0<b>>>0)ga();c[n+24>>2]=h;a=c[j+16>>2]|0;do if(a|0)if(a>>>0<b>>>0)ga();else{c[n+16>>2]=a;c[a+24>>2]=n;break}while(0);a=c[j+20>>2]|0;if(a|0)if(a>>>0<(c[7648]|0)>>>0)ga();else{c[n+20>>2]=a;c[a+24>>2]=n;break}}while(0);if(d>>>0<16){L=d+o|0;c[j+4>>2]=L|3;L=j+L+4|0;c[L>>2]=c[L>>2]|1}else{c[j+4>>2]=o|3;c[i+4>>2]=d|1;c[i+d>>2]=d;a=c[7646]|0;if(a|0){f=c[7649]|0;b=a>>>3;e=30616+(b<<1<<2)|0;a=c[7644]|0;b=1<<b;if(a&b){a=e+8|0;b=c[a>>2]|0;if(b>>>0<(c[7648]|0)>>>0)ga();else{p=a;q=b}}else{c[7644]=a|b;p=e+8|0;q=e}c[p>>2]=f;c[q+12>>2]=f;c[f+8>>2]=q;c[f+12>>2]=e}c[7646]=d;c[7649]=i}L=j+8|0;return L|0}}}else if(a>>>0<=4294967231){a=a+11|0;o=a&-8;j=c[7645]|0;if(j){d=0-o|0;a=a>>>8;if(a)if(o>>>0>16777215)i=31;else{q=(a+1048320|0)>>>16&8;E=a<<q;p=(E+520192|0)>>>16&4;E=E<<p;i=(E+245760|0)>>>16&2;i=14-(p|q|i)+(E<<i>>>15)|0;i=o>>>(i+7|0)&1|i<<1}else i=0;b=c[30880+(i<<2)>>2]|0;a:do if(!b){a=0;b=0;E=86}else{f=d;a=0;g=o<<((i|0)==31?0:25-(i>>>1)|0);h=b;b=0;while(1){e=c[h+4>>2]&-8;d=e-o|0;if(d>>>0<f>>>0)if((e|0)==(o|0)){a=h;b=h;E=90;break a}else b=h;else d=f;e=c[h+20>>2]|0;h=c[h+16+(g>>>31<<2)>>2]|0;a=(e|0)==0|(e|0)==(h|0)?a:e;e=(h|0)==0;if(e){E=86;break}else{f=d;g=g<<(e&1^1)}}}while(0);if((E|0)==86){if((a|0)==0&(b|0)==0){a=2<<i;a=j&(a|0-a);if(!a)break;q=(a&0-a)+-1|0;m=q>>>12&16;q=q>>>m;l=q>>>5&8;q=q>>>l;n=q>>>2&4;q=q>>>n;p=q>>>1&2;q=q>>>p;a=q>>>1&1;a=c[30880+((l|m|n|p|a)+(q>>>a)<<2)>>2]|0}if(!a){i=d;j=b}else E=90}if((E|0)==90)while(1){E=0;q=(c[a+4>>2]&-8)-o|0;e=q>>>0<d>>>0;d=e?q:d;b=e?a:b;e=c[a+16>>2]|0;if(e|0){a=e;E=90;continue}a=c[a+20>>2]|0;if(!a){i=d;j=b;break}else E=90}if((j|0)!=0?i>>>0<((c[7646]|0)-o|0)>>>0:0){f=c[7648]|0;if(j>>>0<f>>>0)ga();h=j+o|0;if(j>>>0>=h>>>0)ga();g=c[j+24>>2]|0;d=c[j+12>>2]|0;do if((d|0)==(j|0)){b=j+20|0;a=c[b>>2]|0;if(!a){b=j+16|0;a=c[b>>2]|0;if(!a){s=0;break}}while(1){d=a+20|0;e=c[d>>2]|0;if(e|0){a=e;b=d;continue}d=a+16|0;e=c[d>>2]|0;if(!e)break;else{a=e;b=d}}if(b>>>0<f>>>0)ga();else{c[b>>2]=0;s=a;break}}else{e=c[j+8>>2]|0;if(e>>>0<f>>>0)ga();a=e+12|0;if((c[a>>2]|0)!=(j|0))ga();b=d+8|0;if((c[b>>2]|0)==(j|0)){c[a>>2]=d;c[b>>2]=e;s=d;break}else ga()}while(0);do if(g|0){a=c[j+28>>2]|0;b=30880+(a<<2)|0;if((j|0)==(c[b>>2]|0)){c[b>>2]=s;if(!s){c[7645]=c[7645]&~(1<<a);break}}else{if(g>>>0<(c[7648]|0)>>>0)ga();a=g+16|0;if((c[a>>2]|0)==(j|0))c[a>>2]=s;else c[g+20>>2]=s;if(!s)break}b=c[7648]|0;if(s>>>0<b>>>0)ga();c[s+24>>2]=g;a=c[j+16>>2]|0;do if(a|0)if(a>>>0<b>>>0)ga();else{c[s+16>>2]=a;c[a+24>>2]=s;break}while(0);a=c[j+20>>2]|0;if(a|0)if(a>>>0<(c[7648]|0)>>>0)ga();else{c[s+20>>2]=a;c[a+24>>2]=s;break}}while(0);do if(i>>>0>=16){c[j+4>>2]=o|3;c[h+4>>2]=i|1;c[h+i>>2]=i;a=i>>>3;if(i>>>0<256){d=30616+(a<<1<<2)|0;b=c[7644]|0;a=1<<a;if(b&a){a=d+8|0;b=c[a>>2]|0;if(b>>>0<(c[7648]|0)>>>0)ga();else{u=a;v=b}}else{c[7644]=b|a;u=d+8|0;v=d}c[u>>2]=h;c[v+12>>2]=h;c[h+8>>2]=v;c[h+12>>2]=d;break}a=i>>>8;if(a)if(i>>>0>16777215)d=31;else{K=(a+1048320|0)>>>16&8;L=a<<K;J=(L+520192|0)>>>16&4;L=L<<J;d=(L+245760|0)>>>16&2;d=14-(J|K|d)+(L<<d>>>15)|0;d=i>>>(d+7|0)&1|d<<1}else d=0;e=30880+(d<<2)|0;c[h+28>>2]=d;a=h+16|0;c[a+4>>2]=0;c[a>>2]=0;a=c[7645]|0;b=1<<d;if(!(a&b)){c[7645]=a|b;c[e>>2]=h;c[h+24>>2]=e;c[h+12>>2]=h;c[h+8>>2]=h;break}f=i<<((d|0)==31?0:25-(d>>>1)|0);a=c[e>>2]|0;while(1){if((c[a+4>>2]&-8|0)==(i|0)){d=a;E=148;break}b=a+16+(f>>>31<<2)|0;d=c[b>>2]|0;if(!d){E=145;break}else{f=f<<1;a=d}}if((E|0)==145)if(b>>>0<(c[7648]|0)>>>0)ga();else{c[b>>2]=h;c[h+24>>2]=a;c[h+12>>2]=h;c[h+8>>2]=h;break}else if((E|0)==148){a=d+8|0;b=c[a>>2]|0;L=c[7648]|0;if(b>>>0>=L>>>0&d>>>0>=L>>>0){c[b+12>>2]=h;c[a>>2]=h;c[h+8>>2]=b;c[h+12>>2]=d;c[h+24>>2]=0;break}else ga()}}else{L=i+o|0;c[j+4>>2]=L|3;L=j+L+4|0;c[L>>2]=c[L>>2]|1}while(0);L=j+8|0;return L|0}}}else o=-1;while(0);d=c[7646]|0;if(d>>>0>=o>>>0){a=d-o|0;b=c[7649]|0;if(a>>>0>15){L=b+o|0;c[7649]=L;c[7646]=a;c[L+4>>2]=a|1;c[L+a>>2]=a;c[b+4>>2]=o|3}else{c[7646]=0;c[7649]=0;c[b+4>>2]=d|3;L=b+d+4|0;c[L>>2]=c[L>>2]|1}L=b+8|0;return L|0}a=c[7647]|0;if(a>>>0>o>>>0){J=a-o|0;c[7647]=J;L=c[7650]|0;K=L+o|0;c[7650]=K;c[K+4>>2]=J|1;c[L+4>>2]=o|3;L=L+8|0;return L|0}do if(!(c[7762]|0)){a=oa(30)|0;if(!(a+-1&a)){c[7764]=a;c[7763]=a;c[7765]=-1;c[7766]=-1;c[7767]=0;c[7755]=0;c[7762]=(ka(0)|0)&-16^1431655768;break}else ga()}while(0);h=o+48|0;g=c[7764]|0;i=o+47|0;f=g+i|0;g=0-g|0;j=f&g;if(j>>>0<=o>>>0){L=0;return L|0}a=c[7754]|0;if(a|0?(u=c[7752]|0,v=u+j|0,v>>>0<=u>>>0|v>>>0>a>>>0):0){L=0;return L|0}b:do if(!(c[7755]&4)){a=c[7650]|0;c:do if(a){d=31024;while(1){b=c[d>>2]|0;if(b>>>0<=a>>>0?(r=d+4|0,(b+(c[r>>2]|0)|0)>>>0>a>>>0):0){e=d;d=r;break}d=c[d+8>>2]|0;if(!d){E=173;break c}}a=f-(c[7647]|0)&g;if(a>>>0<2147483647){b=ja(a|0)|0;if((b|0)==((c[e>>2]|0)+(c[d>>2]|0)|0)){if((b|0)!=(-1|0)){h=b;f=a;E=193;break b}}else E=183}}else E=173;while(0);do if((E|0)==173?(t=ja(0)|0,(t|0)!=(-1|0)):0){a=t;b=c[7763]|0;d=b+-1|0;if(!(d&a))a=j;else a=j-a+(d+a&0-b)|0;b=c[7752]|0;d=b+a|0;if(a>>>0>o>>>0&a>>>0<2147483647){v=c[7754]|0;if(v|0?d>>>0<=b>>>0|d>>>0>v>>>0:0)break;b=ja(a|0)|0;if((b|0)==(t|0)){h=t;f=a;E=193;break b}else E=183}}while(0);d:do if((E|0)==183){d=0-a|0;do if(h>>>0>a>>>0&(a>>>0<2147483647&(b|0)!=(-1|0))?(w=c[7764]|0,w=i-a+w&0-w,w>>>0<2147483647):0)if((ja(w|0)|0)==(-1|0)){ja(d|0)|0;break d}else{a=w+a|0;break}while(0);if((b|0)!=(-1|0)){h=b;f=a;E=193;break b}}while(0);c[7755]=c[7755]|4;E=190}else E=190;while(0);if((((E|0)==190?j>>>0<2147483647:0)?(x=ja(j|0)|0,y=ja(0)|0,x>>>0<y>>>0&((x|0)!=(-1|0)&(y|0)!=(-1|0))):0)?(z=y-x|0,z>>>0>(o+40|0)>>>0):0){h=x;f=z;E=193}if((E|0)==193){a=(c[7752]|0)+f|0;c[7752]=a;if(a>>>0>(c[7753]|0)>>>0)c[7753]=a;i=c[7650]|0;do if(i){e=31024;do{a=c[e>>2]|0;b=e+4|0;d=c[b>>2]|0;if((h|0)==(a+d|0)){A=a;B=b;C=d;D=e;E=203;break}e=c[e+8>>2]|0}while((e|0)!=0);if(((E|0)==203?(c[D+12>>2]&8|0)==0:0)?i>>>0<h>>>0&i>>>0>=A>>>0:0){c[B>>2]=C+f;L=i+8|0;L=(L&7|0)==0?0:0-L&7;K=i+L|0;L=f-L+(c[7647]|0)|0;c[7650]=K;c[7647]=L;c[K+4>>2]=L|1;c[K+L+4>>2]=40;c[7651]=c[7766];break}a=c[7648]|0;if(h>>>0<a>>>0){c[7648]=h;j=h}else j=a;d=h+f|0;a=31024;while(1){if((c[a>>2]|0)==(d|0)){b=a;E=211;break}a=c[a+8>>2]|0;if(!a){b=31024;break}}if((E|0)==211)if(!(c[a+12>>2]&8)){c[b>>2]=h;l=a+4|0;c[l>>2]=(c[l>>2]|0)+f;l=h+8|0;l=h+((l&7|0)==0?0:0-l&7)|0;a=d+8|0;a=d+((a&7|0)==0?0:0-a&7)|0;k=l+o|0;g=a-l-o|0;c[l+4>>2]=o|3;do if((a|0)!=(i|0)){if((a|0)==(c[7649]|0)){L=(c[7646]|0)+g|0;c[7646]=L;c[7649]=k;c[k+4>>2]=L|1;c[k+L>>2]=L;break}b=c[a+4>>2]|0;if((b&3|0)==1){i=b&-8;f=b>>>3;e:do if(b>>>0>=256){h=c[a+24>>2]|0;e=c[a+12>>2]|0;do if((e|0)==(a|0)){d=a+16|0;e=d+4|0;b=c[e>>2]|0;if(!b){b=c[d>>2]|0;if(!b){J=0;break}}else d=e;while(1){e=b+20|0;f=c[e>>2]|0;if(f|0){b=f;d=e;continue}e=b+16|0;f=c[e>>2]|0;if(!f)break;else{b=f;d=e}}if(d>>>0<j>>>0)ga();else{c[d>>2]=0;J=b;break}}else{f=c[a+8>>2]|0;if(f>>>0<j>>>0)ga();b=f+12|0;if((c[b>>2]|0)!=(a|0))ga();d=e+8|0;if((c[d>>2]|0)==(a|0)){c[b>>2]=e;c[d>>2]=f;J=e;break}else ga()}while(0);if(!h)break;b=c[a+28>>2]|0;d=30880+(b<<2)|0;do if((a|0)!=(c[d>>2]|0)){if(h>>>0<(c[7648]|0)>>>0)ga();b=h+16|0;if((c[b>>2]|0)==(a|0))c[b>>2]=J;else c[h+20>>2]=J;if(!J)break e}else{c[d>>2]=J;if(J|0)break;c[7645]=c[7645]&~(1<<b);break e}while(0);e=c[7648]|0;if(J>>>0<e>>>0)ga();c[J+24>>2]=h;b=a+16|0;d=c[b>>2]|0;do if(d|0)if(d>>>0<e>>>0)ga();else{c[J+16>>2]=d;c[d+24>>2]=J;break}while(0);b=c[b+4>>2]|0;if(!b)break;if(b>>>0<(c[7648]|0)>>>0)ga();else{c[J+20>>2]=b;c[b+24>>2]=J;break}}else{d=c[a+8>>2]|0;e=c[a+12>>2]|0;b=30616+(f<<1<<2)|0;do if((d|0)!=(b|0)){if(d>>>0<j>>>0)ga();if((c[d+12>>2]|0)==(a|0))break;ga()}while(0);if((e|0)==(d|0)){c[7644]=c[7644]&~(1<<f);break}do if((e|0)==(b|0))G=e+8|0;else{if(e>>>0<j>>>0)ga();b=e+8|0;if((c[b>>2]|0)==(a|0)){G=b;break}ga()}while(0);c[d+12>>2]=e;c[G>>2]=d}while(0);a=a+i|0;g=i+g|0}a=a+4|0;c[a>>2]=c[a>>2]&-2;c[k+4>>2]=g|1;c[k+g>>2]=g;a=g>>>3;if(g>>>0<256){d=30616+(a<<1<<2)|0;b=c[7644]|0;a=1<<a;do if(!(b&a)){c[7644]=b|a;K=d+8|0;L=d}else{a=d+8|0;b=c[a>>2]|0;if(b>>>0>=(c[7648]|0)>>>0){K=a;L=b;break}ga()}while(0);c[K>>2]=k;c[L+12>>2]=k;c[k+8>>2]=L;c[k+12>>2]=d;break}a=g>>>8;do if(!a)d=0;else{if(g>>>0>16777215){d=31;break}K=(a+1048320|0)>>>16&8;L=a<<K;J=(L+520192|0)>>>16&4;L=L<<J;d=(L+245760|0)>>>16&2;d=14-(J|K|d)+(L<<d>>>15)|0;d=g>>>(d+7|0)&1|d<<1}while(0);e=30880+(d<<2)|0;c[k+28>>2]=d;a=k+16|0;c[a+4>>2]=0;c[a>>2]=0;a=c[7645]|0;b=1<<d;if(!(a&b)){c[7645]=a|b;c[e>>2]=k;c[k+24>>2]=e;c[k+12>>2]=k;c[k+8>>2]=k;break}f=g<<((d|0)==31?0:25-(d>>>1)|0);a=c[e>>2]|0;while(1){if((c[a+4>>2]&-8|0)==(g|0)){d=a;E=281;break}b=a+16+(f>>>31<<2)|0;d=c[b>>2]|0;if(!d){E=278;break}else{f=f<<1;a=d}}if((E|0)==278)if(b>>>0<(c[7648]|0)>>>0)ga();else{c[b>>2]=k;c[k+24>>2]=a;c[k+12>>2]=k;c[k+8>>2]=k;break}else if((E|0)==281){a=d+8|0;b=c[a>>2]|0;L=c[7648]|0;if(b>>>0>=L>>>0&d>>>0>=L>>>0){c[b+12>>2]=k;c[a>>2]=k;c[k+8>>2]=b;c[k+12>>2]=d;c[k+24>>2]=0;break}else ga()}}else{L=(c[7647]|0)+g|0;c[7647]=L;c[7650]=k;c[k+4>>2]=L|1}while(0);L=l+8|0;return L|0}else b=31024;while(1){a=c[b>>2]|0;if(a>>>0<=i>>>0?(F=a+(c[b+4>>2]|0)|0,F>>>0>i>>>0):0){b=F;break}b=c[b+8>>2]|0}g=b+-47|0;d=g+8|0;d=g+((d&7|0)==0?0:0-d&7)|0;g=i+16|0;d=d>>>0<g>>>0?i:d;a=d+8|0;e=h+8|0;e=(e&7|0)==0?0:0-e&7;L=h+e|0;e=f+-40-e|0;c[7650]=L;c[7647]=e;c[L+4>>2]=e|1;c[L+e+4>>2]=40;c[7651]=c[7766];e=d+4|0;c[e>>2]=27;c[a>>2]=c[7756];c[a+4>>2]=c[7757];c[a+8>>2]=c[7758];c[a+12>>2]=c[7759];c[7756]=h;c[7757]=f;c[7759]=0;c[7758]=a;a=d+24|0;do{a=a+4|0;c[a>>2]=7}while((a+4|0)>>>0<b>>>0);if((d|0)!=(i|0)){h=d-i|0;c[e>>2]=c[e>>2]&-2;c[i+4>>2]=h|1;c[d>>2]=h;a=h>>>3;if(h>>>0<256){d=30616+(a<<1<<2)|0;b=c[7644]|0;a=1<<a;if(b&a){a=d+8|0;b=c[a>>2]|0;if(b>>>0<(c[7648]|0)>>>0)ga();else{H=a;I=b}}else{c[7644]=b|a;H=d+8|0;I=d}c[H>>2]=i;c[I+12>>2]=i;c[i+8>>2]=I;c[i+12>>2]=d;break}a=h>>>8;if(a)if(h>>>0>16777215)d=31;else{K=(a+1048320|0)>>>16&8;L=a<<K;J=(L+520192|0)>>>16&4;L=L<<J;d=(L+245760|0)>>>16&2;d=14-(J|K|d)+(L<<d>>>15)|0;d=h>>>(d+7|0)&1|d<<1}else d=0;f=30880+(d<<2)|0;c[i+28>>2]=d;c[i+20>>2]=0;c[g>>2]=0;a=c[7645]|0;b=1<<d;if(!(a&b)){c[7645]=a|b;c[f>>2]=i;c[i+24>>2]=f;c[i+12>>2]=i;c[i+8>>2]=i;break}e=h<<((d|0)==31?0:25-(d>>>1)|0);a=c[f>>2]|0;while(1){if((c[a+4>>2]&-8|0)==(h|0)){d=a;E=307;break}b=a+16+(e>>>31<<2)|0;d=c[b>>2]|0;if(!d){E=304;break}else{e=e<<1;a=d}}if((E|0)==304)if(b>>>0<(c[7648]|0)>>>0)ga();else{c[b>>2]=i;c[i+24>>2]=a;c[i+12>>2]=i;c[i+8>>2]=i;break}else if((E|0)==307){a=d+8|0;b=c[a>>2]|0;L=c[7648]|0;if(b>>>0>=L>>>0&d>>>0>=L>>>0){c[b+12>>2]=i;c[a>>2]=i;c[i+8>>2]=b;c[i+12>>2]=d;c[i+24>>2]=0;break}else ga()}}}else{L=c[7648]|0;if((L|0)==0|h>>>0<L>>>0)c[7648]=h;c[7756]=h;c[7757]=f;c[7759]=0;c[7653]=c[7762];c[7652]=-1;a=0;do{L=30616+(a<<1<<2)|0;c[L+12>>2]=L;c[L+8>>2]=L;a=a+1|0}while((a|0)!=32);L=h+8|0;L=(L&7|0)==0?0:0-L&7;K=h+L|0;L=f+-40-L|0;c[7650]=K;c[7647]=L;c[K+4>>2]=L|1;c[K+L+4>>2]=40;c[7651]=c[7766]}while(0);a=c[7647]|0;if(a>>>0>o>>>0){J=a-o|0;c[7647]=J;L=c[7650]|0;K=L+o|0;c[7650]=K;c[K+4>>2]=J|1;c[L+4>>2]=o|3;L=L+8|0;return L|0}}c[(fj()|0)>>2]=12;L=0;return L|0}function kj(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;if(!a)return;d=a+-8|0;h=c[7648]|0;if(d>>>0<h>>>0)ga();a=c[a+-4>>2]|0;b=a&3;if((b|0)==1)ga();e=a&-8;m=d+e|0;do if(!(a&1)){a=c[d>>2]|0;if(!b)return;k=d+(0-a)|0;j=a+e|0;if(k>>>0<h>>>0)ga();if((k|0)==(c[7649]|0)){a=m+4|0;b=c[a>>2]|0;if((b&3|0)!=3){q=k;g=j;break}c[7646]=j;c[a>>2]=b&-2;c[k+4>>2]=j|1;c[k+j>>2]=j;return}e=a>>>3;if(a>>>0<256){b=c[k+8>>2]|0;d=c[k+12>>2]|0;a=30616+(e<<1<<2)|0;if((b|0)!=(a|0)){if(b>>>0<h>>>0)ga();if((c[b+12>>2]|0)!=(k|0))ga()}if((d|0)==(b|0)){c[7644]=c[7644]&~(1<<e);q=k;g=j;break}if((d|0)!=(a|0)){if(d>>>0<h>>>0)ga();a=d+8|0;if((c[a>>2]|0)==(k|0))f=a;else ga()}else f=d+8|0;c[b+12>>2]=d;c[f>>2]=b;q=k;g=j;break}f=c[k+24>>2]|0;d=c[k+12>>2]|0;do if((d|0)==(k|0)){b=k+16|0;d=b+4|0;a=c[d>>2]|0;if(!a){a=c[b>>2]|0;if(!a){i=0;break}}else b=d;while(1){d=a+20|0;e=c[d>>2]|0;if(e|0){a=e;b=d;continue}d=a+16|0;e=c[d>>2]|0;if(!e)break;else{a=e;b=d}}if(b>>>0<h>>>0)ga();else{c[b>>2]=0;i=a;break}}else{e=c[k+8>>2]|0;if(e>>>0<h>>>0)ga();a=e+12|0;if((c[a>>2]|0)!=(k|0))ga();b=d+8|0;if((c[b>>2]|0)==(k|0)){c[a>>2]=d;c[b>>2]=e;i=d;break}else ga()}while(0);if(f){a=c[k+28>>2]|0;b=30880+(a<<2)|0;if((k|0)==(c[b>>2]|0)){c[b>>2]=i;if(!i){c[7645]=c[7645]&~(1<<a);q=k;g=j;break}}else{if(f>>>0<(c[7648]|0)>>>0)ga();a=f+16|0;if((c[a>>2]|0)==(k|0))c[a>>2]=i;else c[f+20>>2]=i;if(!i){q=k;g=j;break}}d=c[7648]|0;if(i>>>0<d>>>0)ga();c[i+24>>2]=f;a=k+16|0;b=c[a>>2]|0;do if(b|0)if(b>>>0<d>>>0)ga();else{c[i+16>>2]=b;c[b+24>>2]=i;break}while(0);a=c[a+4>>2]|0;if(a)if(a>>>0<(c[7648]|0)>>>0)ga();else{c[i+20>>2]=a;c[a+24>>2]=i;q=k;g=j;break}else{q=k;g=j}}else{q=k;g=j}}else{q=d;g=e}while(0);if(q>>>0>=m>>>0)ga();a=m+4|0;b=c[a>>2]|0;if(!(b&1))ga();if(!(b&2)){if((m|0)==(c[7650]|0)){p=(c[7647]|0)+g|0;c[7647]=p;c[7650]=q;c[q+4>>2]=p|1;if((q|0)!=(c[7649]|0))return;c[7649]=0;c[7646]=0;return}if((m|0)==(c[7649]|0)){p=(c[7646]|0)+g|0;c[7646]=p;c[7649]=q;c[q+4>>2]=p|1;c[q+p>>2]=p;return}g=(b&-8)+g|0;e=b>>>3;do if(b>>>0>=256){f=c[m+24>>2]|0;a=c[m+12>>2]|0;do if((a|0)==(m|0)){b=m+16|0;d=b+4|0;a=c[d>>2]|0;if(!a){a=c[b>>2]|0;if(!a){n=0;break}}else b=d;while(1){d=a+20|0;e=c[d>>2]|0;if(e|0){a=e;b=d;continue}d=a+16|0;e=c[d>>2]|0;if(!e)break;else{a=e;b=d}}if(b>>>0<(c[7648]|0)>>>0)ga();else{c[b>>2]=0;n=a;break}}else{b=c[m+8>>2]|0;if(b>>>0<(c[7648]|0)>>>0)ga();d=b+12|0;if((c[d>>2]|0)!=(m|0))ga();e=a+8|0;if((c[e>>2]|0)==(m|0)){c[d>>2]=a;c[e>>2]=b;n=a;break}else ga()}while(0);if(f|0){a=c[m+28>>2]|0;b=30880+(a<<2)|0;if((m|0)==(c[b>>2]|0)){c[b>>2]=n;if(!n){c[7645]=c[7645]&~(1<<a);break}}else{if(f>>>0<(c[7648]|0)>>>0)ga();a=f+16|0;if((c[a>>2]|0)==(m|0))c[a>>2]=n;else c[f+20>>2]=n;if(!n)break}d=c[7648]|0;if(n>>>0<d>>>0)ga();c[n+24>>2]=f;a=m+16|0;b=c[a>>2]|0;do if(b|0)if(b>>>0<d>>>0)ga();else{c[n+16>>2]=b;c[b+24>>2]=n;break}while(0);a=c[a+4>>2]|0;if(a|0)if(a>>>0<(c[7648]|0)>>>0)ga();else{c[n+20>>2]=a;c[a+24>>2]=n;break}}}else{b=c[m+8>>2]|0;d=c[m+12>>2]|0;a=30616+(e<<1<<2)|0;if((b|0)!=(a|0)){if(b>>>0<(c[7648]|0)>>>0)ga();if((c[b+12>>2]|0)!=(m|0))ga()}if((d|0)==(b|0)){c[7644]=c[7644]&~(1<<e);break}if((d|0)!=(a|0)){if(d>>>0<(c[7648]|0)>>>0)ga();a=d+8|0;if((c[a>>2]|0)==(m|0))l=a;else ga()}else l=d+8|0;c[b+12>>2]=d;c[l>>2]=b}while(0);c[q+4>>2]=g|1;c[q+g>>2]=g;if((q|0)==(c[7649]|0)){c[7646]=g;return}}else{c[a>>2]=b&-2;c[q+4>>2]=g|1;c[q+g>>2]=g}a=g>>>3;if(g>>>0<256){d=30616+(a<<1<<2)|0;b=c[7644]|0;a=1<<a;if(b&a){a=d+8|0;b=c[a>>2]|0;if(b>>>0<(c[7648]|0)>>>0)ga();else{o=a;p=b}}else{c[7644]=b|a;o=d+8|0;p=d}c[o>>2]=q;c[p+12>>2]=q;c[q+8>>2]=p;c[q+12>>2]=d;return}a=g>>>8;if(a)if(g>>>0>16777215)d=31;else{o=(a+1048320|0)>>>16&8;p=a<<o;n=(p+520192|0)>>>16&4;p=p<<n;d=(p+245760|0)>>>16&2;d=14-(n|o|d)+(p<<d>>>15)|0;d=g>>>(d+7|0)&1|d<<1}else d=0;e=30880+(d<<2)|0;c[q+28>>2]=d;c[q+20>>2]=0;c[q+16>>2]=0;a=c[7645]|0;b=1<<d;do if(a&b){f=g<<((d|0)==31?0:25-(d>>>1)|0);a=c[e>>2]|0;while(1){if((c[a+4>>2]&-8|0)==(g|0)){d=a;e=130;break}b=a+16+(f>>>31<<2)|0;d=c[b>>2]|0;if(!d){e=127;break}else{f=f<<1;a=d}}if((e|0)==127)if(b>>>0<(c[7648]|0)>>>0)ga();else{c[b>>2]=q;c[q+24>>2]=a;c[q+12>>2]=q;c[q+8>>2]=q;break}else if((e|0)==130){a=d+8|0;b=c[a>>2]|0;p=c[7648]|0;if(b>>>0>=p>>>0&d>>>0>=p>>>0){c[b+12>>2]=q;c[a>>2]=q;c[q+8>>2]=b;c[q+12>>2]=d;c[q+24>>2]=0;break}else ga()}}else{c[7645]=a|b;c[e>>2]=q;c[q+24>>2]=e;c[q+12>>2]=q;c[q+8>>2]=q}while(0);q=(c[7652]|0)+-1|0;c[7652]=q;if(!q)a=31032;else return;while(1){a=c[a>>2]|0;if(!a)break;else a=a+8|0}c[7652]=-1;return}function lj(){}function mj(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;c=a+c>>>0;return (C=b+d+(c>>>0<a>>>0|0)>>>0,c|0)|0}function nj(a,b,c){a=a|0;b=b|0;c=c|0;if((c|0)<32){C=b>>c;return a>>>c|(b&(1<<c)-1)<<32-c}C=(b|0)<0?-1:0;return b>>c-32|0}function oj(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0;f=b+e|0;if((e|0)>=20){d=d&255;h=b&3;i=d|d<<8|d<<16|d<<24;g=f&~3;if(h){h=b+4-h|0;while((b|0)<(h|0)){a[b>>0]=d;b=b+1|0}}while((b|0)<(g|0)){c[b>>2]=i;b=b+4|0}}while((b|0)<(f|0)){a[b>>0]=d;b=b+1|0}return b-e|0}function pj(b,d,e){b=b|0;d=d|0;e=e|0;var f=0;if((e|0)>=4096)return ma(b|0,d|0,e|0)|0;f=b|0;if((b&3)==(d&3)){while(b&3){if(!e)return f|0;a[b>>0]=a[d>>0]|0;b=b+1|0;d=d+1|0;e=e-1|0}while((e|0)>=4){c[b>>2]=c[d>>2];b=b+4|0;d=d+4|0;e=e-4|0}}while((e|0)>0){a[b>>0]=a[d>>0]|0;b=b+1|0;d=d+1|0;e=e-1|0}return f|0}function qj(b,c,d){b=b|0;c=c|0;d=d|0;var e=0;if((c|0)<(b|0)&(b|0)<(c+d|0)){e=b;c=c+d|0;b=b+d|0;while((d|0)>0){b=b-1|0;c=c-1|0;d=d-1|0;a[b>>0]=a[c>>0]|0}b=e}else pj(b,c,d)|0;return b|0}function rj(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;d=b-d-(c>>>0>a>>>0|0)>>>0;return (C=d,a-c>>>0|0)|0}function sj(a,b,c){a=a|0;b=b|0;c=c|0;if((c|0)<32){C=b<<c|(a&(1<<c)-1<<32-c)>>>32-c;return a<<c}C=a<<c-32;return 0}function tj(a,b,c){a=a|0;b=b|0;c=c|0;if((c|0)<32){C=b>>>c;return a>>>c|(b&(1<<c)-1)<<32-c}C=0;return b>>>c-32|0}function uj(b){b=b|0;var c=0;c=a[m+(b&255)>>0]|0;if((c|0)<8)return c|0;c=a[m+(b>>8&255)>>0]|0;if((c|0)<8)return c+8|0;c=a[m+(b>>16&255)>>0]|0;if((c|0)<8)return c+16|0;return (a[m+(b>>>24)>>0]|0)+24|0}function vj(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0;f=a&65535;e=b&65535;c=_(e,f)|0;d=a>>>16;a=(c>>>16)+(_(e,d)|0)|0;e=b>>>16;b=_(e,f)|0;return (C=(a>>>16)+(_(e,d)|0)+(((a&65535)+b|0)>>>16)|0,a+b<<16|c&65535|0)|0}function wj(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0;j=b>>31|((b|0)<0?-1:0)<<1;i=((b|0)<0?-1:0)>>31|((b|0)<0?-1:0)<<1;f=d>>31|((d|0)<0?-1:0)<<1;e=((d|0)<0?-1:0)>>31|((d|0)<0?-1:0)<<1;h=rj(j^a|0,i^b|0,j|0,i|0)|0;g=C;a=f^j;b=e^i;return rj((Bj(h,g,rj(f^c|0,e^d|0,f|0,e|0)|0,C,0)|0)^a|0,C^b|0,a|0,b|0)|0}function xj(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0;f=i;i=i+16|0;j=f|0;h=b>>31|((b|0)<0?-1:0)<<1;g=((b|0)<0?-1:0)>>31|((b|0)<0?-1:0)<<1;l=e>>31|((e|0)<0?-1:0)<<1;k=((e|0)<0?-1:0)>>31|((e|0)<0?-1:0)<<1;a=rj(h^a|0,g^b|0,h|0,g|0)|0;b=C;Bj(a,b,rj(l^d|0,k^e|0,l|0,k|0)|0,C,j)|0;e=rj(c[j>>2]^h|0,c[j+4>>2]^g|0,h|0,g|0)|0;d=C;i=f;return (C=d,e)|0}function yj(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0;e=a;f=c;c=vj(e,f)|0;a=C;return (C=(_(b,f)|0)+(_(d,e)|0)+a|a&0,c|0|0)|0}function zj(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return Bj(a,b,c,d,0)|0}function Aj(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0;g=i;i=i+16|0;f=g|0;Bj(a,b,d,e,f)|0;i=g;return (C=c[f+4>>2]|0,c[f>>2]|0)|0}function Bj(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;l=a;j=b;k=j;h=d;n=e;i=n;if(!k){g=(f|0)!=0;if(!i){if(g){c[f>>2]=(l>>>0)%(h>>>0);c[f+4>>2]=0}n=0;f=(l>>>0)/(h>>>0)>>>0;return (C=n,f)|0}else{if(!g){n=0;f=0;return (C=n,f)|0}c[f>>2]=a|0;c[f+4>>2]=b&0;n=0;f=0;return (C=n,f)|0}}g=(i|0)==0;do if(h){if(!g){g=(aa(i|0)|0)-(aa(k|0)|0)|0;if(g>>>0<=31){m=g+1|0;i=31-g|0;b=g-31>>31;h=m;a=l>>>(m>>>0)&b|k<<i;b=k>>>(m>>>0)&b;g=0;i=l<<i;break}if(!f){n=0;f=0;return (C=n,f)|0}c[f>>2]=a|0;c[f+4>>2]=j|b&0;n=0;f=0;return (C=n,f)|0}g=h-1|0;if(g&h|0){i=(aa(h|0)|0)+33-(aa(k|0)|0)|0;p=64-i|0;m=32-i|0;j=m>>31;o=i-32|0;b=o>>31;h=i;a=m-1>>31&k>>>(o>>>0)|(k<<m|l>>>(i>>>0))&b;b=b&k>>>(i>>>0);g=l<<p&j;i=(k<<p|l>>>(o>>>0))&j|l<<m&i-33>>31;break}if(f|0){c[f>>2]=g&l;c[f+4>>2]=0}if((h|0)==1){o=j|b&0;p=a|0|0;return (C=o,p)|0}else{p=uj(h|0)|0;o=k>>>(p>>>0)|0;p=k<<32-p|l>>>(p>>>0)|0;return (C=o,p)|0}}else{if(g){if(f|0){c[f>>2]=(k>>>0)%(h>>>0);c[f+4>>2]=0}o=0;p=(k>>>0)/(h>>>0)>>>0;return (C=o,p)|0}if(!l){if(f|0){c[f>>2]=0;c[f+4>>2]=(k>>>0)%(i>>>0)}o=0;p=(k>>>0)/(i>>>0)>>>0;return (C=o,p)|0}g=i-1|0;if(!(g&i)){if(f|0){c[f>>2]=a|0;c[f+4>>2]=g&k|b&0}o=0;p=k>>>((uj(i|0)|0)>>>0);return (C=o,p)|0}g=(aa(i|0)|0)-(aa(k|0)|0)|0;if(g>>>0<=30){b=g+1|0;i=31-g|0;h=b;a=k<<i|l>>>(b>>>0);b=k>>>(b>>>0);g=0;i=l<<i;break}if(!f){o=0;p=0;return (C=o,p)|0}c[f>>2]=a|0;c[f+4>>2]=j|b&0;o=0;p=0;return (C=o,p)|0}while(0);if(!h){k=i;j=0;i=0}else{m=d|0|0;l=n|e&0;k=mj(m|0,l|0,-1,-1)|0;d=C;j=i;i=0;do{e=j;j=g>>>31|j<<1;g=i|g<<1;e=a<<1|e>>>31|0;n=a>>>31|b<<1|0;rj(k|0,d|0,e|0,n|0)|0;p=C;o=p>>31|((p|0)<0?-1:0)<<1;i=o&1;a=rj(e|0,n|0,o&m|0,(((p|0)<0?-1:0)>>31|((p|0)<0?-1:0)<<1)&l|0)|0;b=C;h=h-1|0}while((h|0)!=0);k=j;j=0}h=0;if(f|0){c[f>>2]=a;c[f+4>>2]=b}o=(g|0)>>>31|(k|h)<<1|(h<<1|g>>>31)&0|j;p=(g<<1|0>>>31)&-2|i;return (C=o,p)|0}function Cj(a,b,c,d,e,f,g,h){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;qa[a&3](b|0,c|0,d|0,e|0,f|0,g|0,h|0)}function Dj(a,b,c,d,e,f,g){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;g=g|0;ba(0)}
+
+ // EMSCRIPTEN_END_FUNCS
+ var qa=[Dj,yi,xi,Dj];return{_opus_decoder_get_size:ai,_opus_get_version_string:gb,_free:kj,_opus_encode_float:Ri,_opus_strerror:fb,_i64Add:mj,_memmove:qj,_opus_decoder_init:ci,_bitshift64Ashr:nj,_opus_encoder_get_size:ti,_memset:oj,_malloc:jj,_opus_decoder_ctl:si,_opus_encode:Qi,_opus_encoder_init:vi,_opus_decode:mi,_opus_packet_get_nb_samples:oi,_memcpy:pj,_opus_encoder_ctl:Si,_opus_decode_float:ri,runPostSets:lj,stackAlloc:ra,stackSave:sa,stackRestore:ta,establishStackSpace:ua,setThrew:va,setTempRet0:ya,getTempRet0:za,dynCall_viiiiiii:Cj}})
+
+
+ // EMSCRIPTEN_END_ASM
+ (b.s,b.t,buffer);b._opus_decoder_get_size=Z._opus_decoder_get_size;b._opus_get_version_string=Z._opus_get_version_string;var va=b._free=Z._free;
+ b._opus_encode_float=Z._opus_encode_float;b._opus_strerror=Z._opus_strerror;var Pa=b._i64Add=Z._i64Add,Ua=b._memmove=Z._memmove;b._opus_decoder_init=Z._opus_decoder_init;var Qa=b._bitshift64Ashr=Z._bitshift64Ashr;b._opus_encoder_get_size=Z._opus_encoder_get_size;var Ra=b._memset=Z._memset,Q=b._malloc=Z._malloc;b._opus_packet_get_nb_samples=Z._opus_packet_get_nb_samples;b._opus_encode=Z._opus_encode;b._opus_encoder_init=Z._opus_encoder_init;b._opus_decode=Z._opus_decode;b._opus_decoder_ctl=Z._opus_decoder_ctl;
+ var Sa=b._memcpy=Z._memcpy;b._opus_encoder_ctl=Z._opus_encoder_ctl;b._opus_decode_float=Z._opus_decode_float;b.runPostSets=Z.runPostSets;b.dynCall_viiiiiii=Z.dynCall_viiiiiii;y.f=Z.stackAlloc;y.g=Z.stackSave;y.c=Z.stackRestore;y.I=Z.establishStackSpace;y.B=Z.setTempRet0;y.w=Z.getTempRet0;function w(a){this.name="ExitStatus";this.message="Program terminated with exit("+a+")";this.status=a}w.prototype=Error();w.prototype.constructor=w;var Wa=null,X=function Xa(){b.calledRun||Ya();b.calledRun||(X=Xa)};
+ b.callMain=b.G=function(a){function c(){for(var a=0;3>a;a++)e.push(0)}a=a||[];T||(T=!0,V(Ea));var d=a.length+1,e=[O(Ka(b.thisProgram),"i8",0)];c();for(var g=0;g<d-1;g+=1)e.push(O(Ka(a[g]),"i8",0)),c();e.push(0);e=O(e,"i32",0);try{var k=b._main(d,e,0);Za(k,!0)}catch(h){if(!(h instanceof w))if("SimulateInfiniteLoop"==h)b.noExitRuntime=!0;else throw h&&"object"===typeof h&&h.stack&&b.m("exception thrown: "+[h,h.stack]),h;}finally{}};
+ function Ya(a){function c(){if(!b.calledRun&&(b.calledRun=!0,!H)){T||(T=!0,V(Ea));V(Fa);if(b.onRuntimeInitialized)b.onRuntimeInitialized();b._main&&$a&&b.callMain(a);if(b.postRun)for("function"==typeof b.postRun&&(b.postRun=[b.postRun]);b.postRun.length;)Ja(b.postRun.shift());V(Ha)}}a=a||b.arguments;null===Wa&&(Wa=Date.now());if(!(0<W)){if(b.preRun)for("function"==typeof b.preRun&&(b.preRun=[b.preRun]);b.preRun.length;)Ia(b.preRun.shift());V(Da);0<W||b.calledRun||(b.setStatus?(b.setStatus("Running..."),
+ setTimeout(function(){setTimeout(function(){b.setStatus("")},1);c()},1)):c())}}b.run=b.run=Ya;function Za(a,c){if(!c||!b.noExitRuntime){if(!b.noExitRuntime&&(H=!0,x=void 0,V(Ga),b.onExit))b.onExit(a);r?process.exit(a):t&&"function"===typeof quit&&quit(a);throw new w(a);}}b.exit=b.exit=Za;var ab=[];
+ function G(a){void 0!==a?(b.print(a),b.m(a),a=JSON.stringify(a)):a="";H=!0;var c="abort("+a+") at "+ta()+"\nIf this abort() is unexpected, build with -s ASSERTIONS=1 which can give more information.";ab&&ab.forEach(function(d){c=d(c,a)});throw c;}b.abort=b.abort=G;if(b.preInit)for("function"==typeof b.preInit&&(b.preInit=[b.preInit]);0<b.preInit.length;)b.preInit.pop()();var $a=!0;b.noInitialRun&&($a=!1);Ya();
+
+
+ return Module;
+ };
+ Module.instance = Module();
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/js/utils/logger.js b/xiaozhi-esp32-server/main/digital-human/js/utils/logger.js
new file mode 100644
index 0000000..e7a9c61
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/js/utils/logger.js
@@ -0,0 +1,43 @@
+// 鏃ュ織璁板綍鍑芥暟
+export function log(message, type = 'info') {
+ // 灏嗘秷鎭寜鎹㈣绗﹀垎鍓叉垚澶氳
+ const lines = message.split('\n');
+ const now = new Date();
+ // const timestamp = `[${now.toLocaleTimeString()}] `;
+ const timestamp = `[${now.toLocaleTimeString()}.${now.getMilliseconds().toString().padStart(3, '0')}] `;
+
+ // 妫�鏌ユ槸鍚﹀瓨鍦ㄦ棩蹇楀鍣�
+ const logContainer = document.getElementById('logContainer');
+ if (!logContainer) {
+ // 濡傛灉鏃ュ織瀹瑰櫒涓嶅瓨鍦紝鍙緭鍑哄埌鎺у埗鍙�
+ console.log(`[${type.toUpperCase()}] ${message}`);
+ return;
+ }
+
+ // 涓烘瘡涓�琛屽垱寤烘棩蹇楁潯鐩�
+ lines.forEach((line, index) => {
+ const logEntry = document.createElement('div');
+ logEntry.className = `log-entry log-${type}`;
+ // 濡傛灉鏄涓�鏉℃棩蹇楋紝鏄剧ず鏃堕棿鎴�
+ const prefix = index === 0 ? timestamp : ' '.repeat(timestamp.length);
+ logEntry.textContent = `${prefix}${line}`;
+ // logEntry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
+ // logEntry.style 淇濈暀璧峰鐨勭┖鏍�
+ logEntry.style.whiteSpace = 'pre';
+ if (type === 'error') {
+ logEntry.style.color = 'red';
+ } else if (type === 'debug') {
+ logEntry.style.color = 'gray';
+ return;
+ } else if (type === 'warning') {
+ logEntry.style.color = 'orange';
+ } else if (type === 'success') {
+ logEntry.style.color = 'green';
+ } else {
+ logEntry.style.color = 'black';
+ }
+ logContainer.appendChild(logEntry);
+ });
+
+ logContainer.scrollTop = logContainer.scrollHeight;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/ReadMe.txt b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/ReadMe.txt
new file mode 100644
index 0000000..59536f2
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/ReadMe.txt
@@ -0,0 +1,72 @@
+
+============================================================
+
+绀轰緥妯″瀷
+妗冩繎鏃ュ拰 - PRO
+
+============================================================
+
+ 璇ョず渚嬫椂鍩轰簬Cubism3.0鍒朵綔鐨勬爣鍑嗘ā鍨嬬礌鏉愩��
+
+ 鍙敤浜庡涔犲彉褰㈠櫒鐨勬瀯閫犱互鍙婂弬鏁扮殑浣跨敤鏂规硶銆�
+ 妯″瀷鐨勮偐閮ㄥ簲鐢ㄤ簡鏂板姛鑳姐�愯兌姘淬�戙��
+
+
+------------------------------
+绱犳潗浣跨敤璁稿彲
+------------------------------
+
+銆�鏅�氱敤鎴蜂互鍙婂皬瑙勬ā浼佷笟鍦ㄥ悓鎰忔巿鏉冨崗璁殑鎯呭喌涓嬪彲鐢ㄤ簬鍟嗕笟鐢ㄩ�斻��
+銆�涓�/澶ц妯$殑浼佷笟鍙兘鐢ㄤ簬闈炲叕寮�鐨勫唴閮ㄨ瘯鐢ㄣ��
+銆�鍦ㄤ娇鐢ㄨ绱犳潗鏃讹紝璇风‘璁や互涓嬬殑銆愭棤鍋挎彁渚涚礌鏉愪娇鐢ㄦ巿鏉冨崗璁�戜腑鐨勨�滄巿鏉冪被鍨嬧�濄�佲�淟ive2D鍘熷垱瑙掕壊鈥濈瓑鐨勭浉鍏冲唴瀹癸紝
+銆�骞跺繀椤绘帴鍙椼�怢ive2D Cubism 绀轰緥妯″瀷鐨勪娇鐢ㄦ巿鏉冭姹傘�戜腑鐨勫埄鐢ㄦ潯浠躲��
+
+銆�鏈夊叧璁稿彲璇佺殑鏇村淇℃伅锛岃鍙傞槄浠ヤ笅椤甸潰銆�
+銆�https://www.live2d.com/zh-CHS/download/sample-data/
+
+
+------------------------------
+鍒涗綔鑰�
+------------------------------
+
+ 鎻掔敾锛欿ani Biimu
+ 妯″瀷锛歀ive2D
+
+
+------------------------------
+绱犳潗鍐呭
+------------------------------
+
+ 妯″瀷鏂囦欢(cmo3)銆�鈥诲寘鍚墿鐞嗘ā鎷熺殑璁惧畾
+ 鍔ㄧ敾鏂囦欢(can3)
+ 宓屽叆鏂囦欢鍒楄〃(runtime鏂囦欢澶�)
+ 銉绘ā鍨嬫暟鎹�(moc3)
+ 銉诲姩浣滄暟鎹�(motion3.json)
+ 銉绘ā鍨嬭瀹氭枃浠�(model3.json)
+ 銉荤墿鐞嗘ā鎷熻瀹氭枃浠�(physics3.json)
+ 銉诲Э鍔胯瀹氭枃浠�(pose3.json)
+ 銉昏緟鍔╂樉绀虹殑鏂囦欢(cdi3.json)
+
+
+------------------------------
+鏇存柊璁板綍
+------------------------------
+
+銆恈mo3銆�
+
+銆�hiyori_pro_t11
+銆�2023骞�03鏈�08鏃� 淇敼浜嗛儴鍒嗘ā鍨嬪叧閿偣
+
+ hiyori_pro_t10
+ 2021骞�06鏈�10鏃� 鍏紑
+
+
+銆恈an3銆�
+
+銆�hiyori_pro_t04
+銆�2023骞�03鏈�08鏃� 淇敼浜嗛儴鍒嗗姩鐢诲叧閿抚
+
+ hiyori_pro_t03
+ 2021骞�06鏈�10鏃� 鍏紑
+
+銆�
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/hiyori_pro_t04.can3 b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/hiyori_pro_t04.can3
new file mode 100644
index 0000000..03695b3
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/hiyori_pro_t04.can3
Binary files differ
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/hiyori_pro_t11.cmo3 b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/hiyori_pro_t11.cmo3
new file mode 100644
index 0000000..57ea71e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/hiyori_pro_t11.cmo3
Binary files differ
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.2048/texture_00.png b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.2048/texture_00.png
new file mode 100644
index 0000000..ec1107f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.2048/texture_00.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.2048/texture_01.png b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.2048/texture_01.png
new file mode 100644
index 0000000..2f50d2f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.2048/texture_01.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.cdi3.json b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.cdi3.json
new file mode 100644
index 0000000..3816ad8
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.cdi3.json
@@ -0,0 +1,533 @@
+{
+ "Version": 3,
+ "Parameters": [
+ {
+ "Id": "ParamAngleX",
+ "GroupId": "ParamGroupFace",
+ "Name": "瑙掑害 X"
+ },
+ {
+ "Id": "ParamAngleY",
+ "GroupId": "ParamGroupFace",
+ "Name": "瑙掑害 Y"
+ },
+ {
+ "Id": "ParamAngleZ",
+ "GroupId": "ParamGroupFace",
+ "Name": "瑙掑害 Z"
+ },
+ {
+ "Id": "ParamCheek",
+ "GroupId": "ParamGroupFace",
+ "Name": "鑴搁娉涚孩"
+ },
+ {
+ "Id": "ParamEyeLOpen",
+ "GroupId": "ParamGroupEyes",
+ "Name": "宸︾溂 寮�闂�"
+ },
+ {
+ "Id": "ParamEyeLSmile",
+ "GroupId": "ParamGroupEyes",
+ "Name": "宸︾溂 寰瑧"
+ },
+ {
+ "Id": "ParamEyeROpen",
+ "GroupId": "ParamGroupEyes",
+ "Name": "鍙崇溂 寮�闂�"
+ },
+ {
+ "Id": "ParamEyeRSmile",
+ "GroupId": "ParamGroupEyes",
+ "Name": "鍙崇溂 寰瑧"
+ },
+ {
+ "Id": "ParamEyeBallX",
+ "GroupId": "ParamGroupEyeballs",
+ "Name": "鐪肩彔 X"
+ },
+ {
+ "Id": "ParamEyeBallY",
+ "GroupId": "ParamGroupEyeballs",
+ "Name": "鐪肩彔 Y"
+ },
+ {
+ "Id": "ParamBrowLY",
+ "GroupId": "ParamGroupBrows",
+ "Name": "宸︾湁 涓婁笅"
+ },
+ {
+ "Id": "ParamBrowRY",
+ "GroupId": "ParamGroupBrows",
+ "Name": "鍙崇湁 涓婁笅"
+ },
+ {
+ "Id": "ParamBrowLX",
+ "GroupId": "ParamGroupBrows",
+ "Name": "宸︾湁 宸﹀彸"
+ },
+ {
+ "Id": "ParamBrowRX",
+ "GroupId": "ParamGroupBrows",
+ "Name": "鍙崇湁 宸﹀彸"
+ },
+ {
+ "Id": "ParamBrowLAngle",
+ "GroupId": "ParamGroupBrows",
+ "Name": "宸︾湁 瑙掑害"
+ },
+ {
+ "Id": "ParamBrowRAngle",
+ "GroupId": "ParamGroupBrows",
+ "Name": "鍙崇湁 瑙掑害"
+ },
+ {
+ "Id": "ParamBrowLForm",
+ "GroupId": "ParamGroupBrows",
+ "Name": "宸︾湁 鍙樺舰"
+ },
+ {
+ "Id": "ParamBrowRForm",
+ "GroupId": "ParamGroupBrows",
+ "Name": "鍙崇湁 鍙樺舰"
+ },
+ {
+ "Id": "ParamMouthForm",
+ "GroupId": "ParamGroupMouth",
+ "Name": "鍢� 鍙樺舰"
+ },
+ {
+ "Id": "ParamMouthOpenY",
+ "GroupId": "ParamGroupMouth",
+ "Name": "鍢� 寮�闂�"
+ },
+ {
+ "Id": "ParamBodyAngleX",
+ "GroupId": "ParamGroupBody",
+ "Name": "韬綋鐨勬棆杞� X"
+ },
+ {
+ "Id": "ParamBodyAngleY",
+ "GroupId": "ParamGroupBody",
+ "Name": "韬綋鐨勬棆杞� Y"
+ },
+ {
+ "Id": "ParamBodyAngleZ",
+ "GroupId": "ParamGroupBody",
+ "Name": "韬綋鐨勬棆杞� Z"
+ },
+ {
+ "Id": "ParamBreath",
+ "GroupId": "ParamGroupBody",
+ "Name": "鍛煎惛"
+ },
+ {
+ "Id": "ParamShoulder",
+ "GroupId": "ParamGroupBody",
+ "Name": "鑲�"
+ },
+ {
+ "Id": "ParamLeg",
+ "GroupId": "ParamGroupBody",
+ "Name": "鑵�"
+ },
+ {
+ "Id": "ParamArmLA",
+ "GroupId": "ParamGroupArms",
+ "Name": "宸﹁噦 A"
+ },
+ {
+ "Id": "ParamArmRA",
+ "GroupId": "ParamGroupArms",
+ "Name": "鍙宠噦 A"
+ },
+ {
+ "Id": "ParamArmLB",
+ "GroupId": "ParamGroupArms",
+ "Name": "宸﹁噦 B"
+ },
+ {
+ "Id": "ParamArmRB",
+ "GroupId": "ParamGroupArms",
+ "Name": "鍙宠噦 B"
+ },
+ {
+ "Id": "ParamHandLB",
+ "GroupId": "ParamGroupArms",
+ "Name": "宸︽墜B 鏃嬭浆"
+ },
+ {
+ "Id": "ParamHandRB",
+ "GroupId": "ParamGroupArms",
+ "Name": "鍙虫墜B 鏃嬭浆"
+ },
+ {
+ "Id": "ParamHandL",
+ "GroupId": "ParamGroupArms",
+ "Name": "宸︽墜"
+ },
+ {
+ "Id": "ParamHandR",
+ "GroupId": "ParamGroupArms",
+ "Name": "鍙虫墜"
+ },
+ {
+ "Id": "ParamBustY",
+ "GroupId": "ParamGroupSway",
+ "Name": "鑳搁儴 鎽囧姩"
+ },
+ {
+ "Id": "ParamHairAhoge",
+ "GroupId": "ParamGroupSway",
+ "Name": "澶村彂鎽囧姩 鍛嗘瘺"
+ },
+ {
+ "Id": "ParamHairFront",
+ "GroupId": "ParamGroupSway",
+ "Name": "澶村彂鎽囧姩 鍓�"
+ },
+ {
+ "Id": "ParamHairBack",
+ "GroupId": "ParamGroupSway",
+ "Name": "澶村彂鎽囧姩 鍚�"
+ },
+ {
+ "Id": "ParamSideupRibbon",
+ "GroupId": "ParamGroupSway",
+ "Name": "鍙戦グ鐨勬憞鍔�"
+ },
+ {
+ "Id": "ParamRibbon",
+ "GroupId": "ParamGroupSway",
+ "Name": "铦磋澏缁撶殑鎽囧姩"
+ },
+ {
+ "Id": "ParamSkirt",
+ "GroupId": "ParamGroupSway",
+ "Name": "鐭鐨勬憞鍔�"
+ },
+ {
+ "Id": "ParamSkirt2",
+ "GroupId": "ParamGroupSway",
+ "Name": "鐭鐨勪笂鍗�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_1_ArtMesh62",
+ "GroupId": "ParamGroup2",
+ "Name": "[0]杈瓙宸�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_2_ArtMesh62",
+ "GroupId": "ParamGroup2",
+ "Name": "[1]杈瓙宸�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_3_ArtMesh62",
+ "GroupId": "ParamGroup2",
+ "Name": "[2]杈瓙宸�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_4_ArtMesh62",
+ "GroupId": "ParamGroup2",
+ "Name": "[3]杈瓙宸�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_5_ArtMesh62",
+ "GroupId": "ParamGroup2",
+ "Name": "[4]杈瓙宸�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_6_ArtMesh62",
+ "GroupId": "ParamGroup2",
+ "Name": "[5]杈瓙宸�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_7_ArtMesh62",
+ "GroupId": "ParamGroup2",
+ "Name": "[6]杈瓙宸�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_1_ArtMesh61",
+ "GroupId": "ParamGroup",
+ "Name": "[0]杈瓙鍙�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_2_ArtMesh61",
+ "GroupId": "ParamGroup",
+ "Name": "[1]杈瓙鍙�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_3_ArtMesh61",
+ "GroupId": "ParamGroup",
+ "Name": "[2]杈瓙鍙�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_4_ArtMesh61",
+ "GroupId": "ParamGroup",
+ "Name": "[3]杈瓙鍙�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_5_ArtMesh61",
+ "GroupId": "ParamGroup",
+ "Name": "[4]杈瓙鍙�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_6_ArtMesh61",
+ "GroupId": "ParamGroup",
+ "Name": "[5]杈瓙鍙�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_7_ArtMesh61",
+ "GroupId": "ParamGroup",
+ "Name": "[6]杈瓙鍙�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_1_ArtMesh55",
+ "GroupId": "ParamGroup4",
+ "Name": "[0]渚у彂宸�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_2_ArtMesh55",
+ "GroupId": "ParamGroup4",
+ "Name": "[1]渚у彂宸�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_3_ArtMesh55",
+ "GroupId": "ParamGroup4",
+ "Name": "[2]渚у彂宸�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_4_ArtMesh55",
+ "GroupId": "ParamGroup4",
+ "Name": "[3]渚у彂宸�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_5_ArtMesh55",
+ "GroupId": "ParamGroup4",
+ "Name": "[4]渚у彂宸�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_6_ArtMesh55",
+ "GroupId": "ParamGroup4",
+ "Name": "[5]渚у彂宸�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_7_ArtMesh55",
+ "GroupId": "ParamGroup4",
+ "Name": "[6]渚у彂宸�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_1_ArtMesh54",
+ "GroupId": "ParamGroup3",
+ "Name": "[0]渚у彂鍙�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_2_ArtMesh54",
+ "GroupId": "ParamGroup3",
+ "Name": "[1]渚у彂鍙�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_3_ArtMesh54",
+ "GroupId": "ParamGroup3",
+ "Name": "[2]渚у彂鍙�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_4_ArtMesh54",
+ "GroupId": "ParamGroup3",
+ "Name": "[3]渚у彂鍙�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_5_ArtMesh54",
+ "GroupId": "ParamGroup3",
+ "Name": "[4]渚у彂鍙�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_6_ArtMesh54",
+ "GroupId": "ParamGroup3",
+ "Name": "[5]渚у彂鍙�"
+ },
+ {
+ "Id": "Param_Angle_Rotation_7_ArtMesh54",
+ "GroupId": "ParamGroup3",
+ "Name": "[6]渚у彂鍙�"
+ }
+ ],
+ "ParameterGroups": [
+ {
+ "Id": "ParamGroupFace",
+ "GroupId": "",
+ "Name": "鑴�"
+ },
+ {
+ "Id": "ParamGroupEyes",
+ "GroupId": "",
+ "Name": "鐪肩潧"
+ },
+ {
+ "Id": "ParamGroupEyeballs",
+ "GroupId": "",
+ "Name": "鐪肩彔"
+ },
+ {
+ "Id": "ParamGroupBrows",
+ "GroupId": "",
+ "Name": "鐪夋瘺"
+ },
+ {
+ "Id": "ParamGroupMouth",
+ "GroupId": "",
+ "Name": "鍢�"
+ },
+ {
+ "Id": "ParamGroupBody",
+ "GroupId": "",
+ "Name": "韬綋"
+ },
+ {
+ "Id": "ParamGroupArms",
+ "GroupId": "",
+ "Name": "鎵嬭噦"
+ },
+ {
+ "Id": "ParamGroupSway",
+ "GroupId": "",
+ "Name": "鎽囧姩"
+ },
+ {
+ "Id": "ParamGroup2",
+ "GroupId": "",
+ "Name": "鎽囧姩 杈瓙宸�"
+ },
+ {
+ "Id": "ParamGroup",
+ "GroupId": "",
+ "Name": "鎽囧姩 杈瓙鍙�"
+ },
+ {
+ "Id": "ParamGroup4",
+ "GroupId": "",
+ "Name": "鎽囧姩 渚у彂宸�"
+ },
+ {
+ "Id": "ParamGroup3",
+ "GroupId": "",
+ "Name": "鎽囧姩 渚у彂鍙�"
+ }
+ ],
+ "Parts": [
+ {
+ "Id": "PartCore",
+ "Name": "Core"
+ },
+ {
+ "Id": "PartCheek",
+ "Name": "鑴搁"
+ },
+ {
+ "Id": "PartBrow",
+ "Name": "鐪夋瘺"
+ },
+ {
+ "Id": "PartEye",
+ "Name": "鐪肩潧"
+ },
+ {
+ "Id": "PartNose",
+ "Name": "榧诲瓙"
+ },
+ {
+ "Id": "PartMouth",
+ "Name": "鍢�"
+ },
+ {
+ "Id": "PartFace",
+ "Name": "鑴�"
+ },
+ {
+ "Id": "PartEar",
+ "Name": "鑰虫湹"
+ },
+ {
+ "Id": "PartHairSide",
+ "Name": "渚у彂"
+ },
+ {
+ "Id": "PartHairFront",
+ "Name": "鍓嶅彂"
+ },
+ {
+ "Id": "PartHairBack",
+ "Name": "鍚庡彂"
+ },
+ {
+ "Id": "PartNeck",
+ "Name": "鑴栧瓙"
+ },
+ {
+ "Id": "PartArmA",
+ "Name": "鎵嬭噦A"
+ },
+ {
+ "Id": "PartArmB",
+ "Name": "鎵嬭噦B"
+ },
+ {
+ "Id": "PartBody",
+ "Name": "韬綋"
+ },
+ {
+ "Id": "PartBackground",
+ "Name": "鑳屾櫙"
+ },
+ {
+ "Id": "PartSketch",
+ "Name": "[鍙傝�冨浘]"
+ },
+ {
+ "Id": "PartEyeBall",
+ "Name": "鐪肩彔"
+ },
+ {
+ "Id": "ArtMesh55_Skinning",
+ "Name": "渚у彂鍙�"
+ },
+ {
+ "Id": "Part4",
+ "Name": "渚у彂宸�(鏃嬭浆)"
+ },
+ {
+ "Id": "ArtMesh54_Skinning",
+ "Name": "渚у彂鍙�(钂欑毊)"
+ },
+ {
+ "Id": "Part3",
+ "Name": "渚у彂鍙�(鏃嬭浆)"
+ },
+ {
+ "Id": "ArtMesh61_Skinning",
+ "Name": "杈瓙鍙�(钂欑毊)"
+ },
+ {
+ "Id": "Part",
+ "Name": "杈瓙鍙�(鏃嬭浆)"
+ },
+ {
+ "Id": "ArtMesh62_Skinning",
+ "Name": "杈瓙宸�(钂欑毊)"
+ },
+ {
+ "Id": "Part2",
+ "Name": "杈瓙宸�(鏃嬭浆)"
+ }
+ ],
+ "CombinedParameters": [
+ [
+ "ParamAngleX",
+ "ParamAngleY"
+ ],
+ [
+ "ParamMouthForm",
+ "ParamMouthOpenY"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.moc3 b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.moc3
new file mode 100644
index 0000000..dc63085
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.moc3
Binary files differ
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.model3.json b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.model3.json
new file mode 100644
index 0000000..f204676
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.model3.json
@@ -0,0 +1,82 @@
+{
+ "Version": 3,
+ "FileReferences": {
+ "Moc": "hiyori_pro_t11.moc3",
+ "Textures": [
+ "hiyori_pro_t11.2048/texture_00.png",
+ "hiyori_pro_t11.2048/texture_01.png"
+ ],
+ "Physics": "hiyori_pro_t11.physics3.json",
+ "Pose": "hiyori_pro_t11.pose3.json",
+ "DisplayInfo": "hiyori_pro_t11.cdi3.json",
+ "Motions": {
+ "Idle": [
+ {
+ "File": "motion/hiyori_m01.motion3.json"
+ },
+ {
+ "File": "motion/hiyori_m02.motion3.json"
+ },
+ {
+ "File": "motion/hiyori_m05.motion3.json"
+ }
+ ],
+ "Flick": [
+ {
+ "File": "motion/hiyori_m03.motion3.json"
+ }
+ ],
+ "FlickDown": [
+ {
+ "File": "motion/hiyori_m04.motion3.json"
+ }
+ ],
+ "FlickUp": [
+ {
+ "File": "motion/hiyori_m06.motion3.json"
+ }
+ ],
+ "Tap": [
+ {
+ "File": "motion/hiyori_m07.motion3.json"
+ },
+ {
+ "File": "motion/hiyori_m08.motion3.json"
+ }
+ ],
+ "Tap@Body": [
+ {
+ "File": "motion/hiyori_m09.motion3.json"
+ }
+ ],
+ "Flick@Body": [
+ {
+ "File": "motion/hiyori_m10.motion3.json"
+ }
+ ]
+ }
+ },
+ "Groups": [
+ {
+ "Target": "Parameter",
+ "Name": "LipSync",
+ "Ids": [
+ "ParamMouthOpenY"
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Name": "EyeBlink",
+ "Ids": [
+ "ParamEyeLOpen",
+ "ParamEyeROpen"
+ ]
+ }
+ ],
+ "HitAreas": [
+ {
+ "Id": "HitArea",
+ "Name": "Body"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.physics3.json b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.physics3.json
new file mode 100644
index 0000000..51e724c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.physics3.json
@@ -0,0 +1,1569 @@
+{
+ "Version": 3,
+ "Meta": {
+ "PhysicsSettingCount": 11,
+ "TotalInputCount": 34,
+ "TotalOutputCount": 35,
+ "VertexCount": 58,
+ "EffectiveForces": {
+ "Gravity": {
+ "X": 0,
+ "Y": -1
+ },
+ "Wind": {
+ "X": 0,
+ "Y": 0
+ }
+ },
+ "PhysicsDictionary": [
+ {
+ "Id": "PhysicsSetting1",
+ "Name": "鍓嶅彂"
+ },
+ {
+ "Id": "PhysicsSetting2",
+ "Name": "鍚庡彂"
+ },
+ {
+ "Id": "PhysicsSetting3",
+ "Name": "涓濆甫 澶村彂"
+ },
+ {
+ "Id": "PhysicsSetting4",
+ "Name": "鐭 妯悜鎽囧姩"
+ },
+ {
+ "Id": "PhysicsSetting5",
+ "Name": "鐭 绾靛悜鎽囧姩"
+ },
+ {
+ "Id": "PhysicsSetting6",
+ "Name": "涓濆甫 韬綋"
+ },
+ {
+ "Id": "PhysicsSetting7",
+ "Name": "鑳� 鎽囧姩"
+ },
+ {
+ "Id": "PhysicsSetting8",
+ "Name": "杈瓙 鍙�"
+ },
+ {
+ "Id": "PhysicsSetting9",
+ "Name": "杈瓙 宸�"
+ },
+ {
+ "Id": "PhysicsSetting10",
+ "Name": "渚у彂 鍙�"
+ },
+ {
+ "Id": "PhysicsSetting11",
+ "Name": "渚у彂 宸�"
+ }
+ ]
+ },
+ "PhysicsSettings": [
+ {
+ "Id": "PhysicsSetting1",
+ "Input": [
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleX"
+ },
+ "Weight": 60,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ"
+ },
+ "Weight": 60,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX"
+ },
+ "Weight": 40,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ"
+ },
+ "Weight": 40,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Output": [
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "ParamHairFront"
+ },
+ "VertexIndex": 1,
+ "Scale": 1.522,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Vertices": [
+ {
+ "Position": {
+ "X": 0,
+ "Y": 0
+ },
+ "Mobility": 1,
+ "Delay": 1,
+ "Acceleration": 1,
+ "Radius": 0
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 3
+ },
+ "Mobility": 0.95,
+ "Delay": 0.9,
+ "Acceleration": 1.5,
+ "Radius": 3
+ }
+ ],
+ "Normalization": {
+ "Position": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ },
+ "Angle": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ }
+ }
+ },
+ {
+ "Id": "PhysicsSetting2",
+ "Input": [
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleX"
+ },
+ "Weight": 60,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ"
+ },
+ "Weight": 60,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX"
+ },
+ "Weight": 40,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ"
+ },
+ "Weight": 40,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Output": [
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "ParamHairBack"
+ },
+ "VertexIndex": 1,
+ "Scale": 2.061,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Vertices": [
+ {
+ "Position": {
+ "X": 0,
+ "Y": 0
+ },
+ "Mobility": 1,
+ "Delay": 1,
+ "Acceleration": 1,
+ "Radius": 0
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 15
+ },
+ "Mobility": 0.95,
+ "Delay": 0.8,
+ "Acceleration": 1.5,
+ "Radius": 15
+ }
+ ],
+ "Normalization": {
+ "Position": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ },
+ "Angle": {
+ "Minimum": -30,
+ "Default": 0,
+ "Maximum": 30
+ }
+ }
+ },
+ {
+ "Id": "PhysicsSetting3",
+ "Input": [
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleX"
+ },
+ "Weight": 40,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ"
+ },
+ "Weight": 40,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX"
+ },
+ "Weight": 60,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ"
+ },
+ "Weight": 60,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Output": [
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "ParamSideupRibbon"
+ },
+ "VertexIndex": 1,
+ "Scale": 1.775,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Vertices": [
+ {
+ "Position": {
+ "X": 0,
+ "Y": 0
+ },
+ "Mobility": 1,
+ "Delay": 1,
+ "Acceleration": 1,
+ "Radius": 0
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 10
+ },
+ "Mobility": 0.9,
+ "Delay": 0.6,
+ "Acceleration": 1.5,
+ "Radius": 10
+ }
+ ],
+ "Normalization": {
+ "Position": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ },
+ "Angle": {
+ "Minimum": -7.8,
+ "Default": 0,
+ "Maximum": 8
+ }
+ }
+ },
+ {
+ "Id": "PhysicsSetting4",
+ "Input": [
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX"
+ },
+ "Weight": 100,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ"
+ },
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Output": [
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "ParamSkirt"
+ },
+ "VertexIndex": 1,
+ "Scale": 1.434,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Vertices": [
+ {
+ "Position": {
+ "X": 0,
+ "Y": 0
+ },
+ "Mobility": 1,
+ "Delay": 1,
+ "Acceleration": 1,
+ "Radius": 0
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 10
+ },
+ "Mobility": 0.9,
+ "Delay": 0.6,
+ "Acceleration": 1.5,
+ "Radius": 10
+ }
+ ],
+ "Normalization": {
+ "Position": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ },
+ "Angle": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ }
+ }
+ },
+ {
+ "Id": "PhysicsSetting5",
+ "Input": [
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY"
+ },
+ "Weight": 100,
+ "Type": "X",
+ "Reflect": false
+ }
+ ],
+ "Output": [
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "ParamSkirt2"
+ },
+ "VertexIndex": 1,
+ "Scale": 1.306,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Vertices": [
+ {
+ "Position": {
+ "X": 0,
+ "Y": 0
+ },
+ "Mobility": 1,
+ "Delay": 1,
+ "Acceleration": 1,
+ "Radius": 0
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 10
+ },
+ "Mobility": 0.8,
+ "Delay": 0.9,
+ "Acceleration": 2,
+ "Radius": 10
+ }
+ ],
+ "Normalization": {
+ "Position": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ },
+ "Angle": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ }
+ }
+ },
+ {
+ "Id": "PhysicsSetting6",
+ "Input": [
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX"
+ },
+ "Weight": 100,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ"
+ },
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Output": [
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "ParamRibbon"
+ },
+ "VertexIndex": 1,
+ "Scale": 1.402,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Vertices": [
+ {
+ "Position": {
+ "X": 0,
+ "Y": 0
+ },
+ "Mobility": 1,
+ "Delay": 1,
+ "Acceleration": 1,
+ "Radius": 0
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 10
+ },
+ "Mobility": 0.9,
+ "Delay": 0.6,
+ "Acceleration": 1.5,
+ "Radius": 10
+ }
+ ],
+ "Normalization": {
+ "Position": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ },
+ "Angle": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10.6
+ }
+ }
+ },
+ {
+ "Id": "PhysicsSetting7",
+ "Input": [
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY"
+ },
+ "Weight": 100,
+ "Type": "X",
+ "Reflect": false
+ }
+ ],
+ "Output": [
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "ParamBustY"
+ },
+ "VertexIndex": 1,
+ "Scale": 1,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Vertices": [
+ {
+ "Position": {
+ "X": 0,
+ "Y": 0
+ },
+ "Mobility": 1,
+ "Delay": 1,
+ "Acceleration": 1,
+ "Radius": 0
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 3
+ },
+ "Mobility": 0.8,
+ "Delay": 0.9,
+ "Acceleration": 1.5,
+ "Radius": 3
+ }
+ ],
+ "Normalization": {
+ "Position": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ },
+ "Angle": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ }
+ }
+ },
+ {
+ "Id": "PhysicsSetting8",
+ "Input": [
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleX"
+ },
+ "Weight": 60,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ"
+ },
+ "Weight": 60,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX"
+ },
+ "Weight": 40,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ"
+ },
+ "Weight": 40,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Output": [
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_1_ArtMesh61"
+ },
+ "VertexIndex": 1,
+ "Scale": 10,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_2_ArtMesh61"
+ },
+ "VertexIndex": 2,
+ "Scale": 20,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_3_ArtMesh61"
+ },
+ "VertexIndex": 3,
+ "Scale": 30,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_4_ArtMesh61"
+ },
+ "VertexIndex": 4,
+ "Scale": 30,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_5_ArtMesh61"
+ },
+ "VertexIndex": 5,
+ "Scale": 30,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_6_ArtMesh61"
+ },
+ "VertexIndex": 6,
+ "Scale": 30,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_7_ArtMesh61"
+ },
+ "VertexIndex": 7,
+ "Scale": 30,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Vertices": [
+ {
+ "Position": {
+ "X": 0,
+ "Y": 0
+ },
+ "Mobility": 1,
+ "Delay": 1,
+ "Acceleration": 1,
+ "Radius": 10
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 10
+ },
+ "Mobility": 0.85,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 10
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 20
+ },
+ "Mobility": 0.85,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 10
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 30
+ },
+ "Mobility": 0.85,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 10
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 40
+ },
+ "Mobility": 0.85,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 10
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 50
+ },
+ "Mobility": 0.85,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 10
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 60
+ },
+ "Mobility": 0.85,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 10
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 70
+ },
+ "Mobility": 0.85,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 10
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 80
+ },
+ "Mobility": 0.85,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 10
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 90
+ },
+ "Mobility": 0.85,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 10
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 100
+ },
+ "Mobility": 0.85,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 10
+ }
+ ],
+ "Normalization": {
+ "Position": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ },
+ "Angle": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ }
+ }
+ },
+ {
+ "Id": "PhysicsSetting9",
+ "Input": [
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleX"
+ },
+ "Weight": 60,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ"
+ },
+ "Weight": 60,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX"
+ },
+ "Weight": 40,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ"
+ },
+ "Weight": 40,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Output": [
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_1_ArtMesh62"
+ },
+ "VertexIndex": 1,
+ "Scale": 10,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_2_ArtMesh62"
+ },
+ "VertexIndex": 2,
+ "Scale": 20,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_3_ArtMesh62"
+ },
+ "VertexIndex": 3,
+ "Scale": 30,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_4_ArtMesh62"
+ },
+ "VertexIndex": 4,
+ "Scale": 30,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_5_ArtMesh62"
+ },
+ "VertexIndex": 5,
+ "Scale": 30,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_6_ArtMesh62"
+ },
+ "VertexIndex": 6,
+ "Scale": 30,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_7_ArtMesh62"
+ },
+ "VertexIndex": 7,
+ "Scale": 30,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Vertices": [
+ {
+ "Position": {
+ "X": 0,
+ "Y": 0
+ },
+ "Mobility": 1,
+ "Delay": 1,
+ "Acceleration": 1,
+ "Radius": 10
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 10
+ },
+ "Mobility": 0.85,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 10
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 20
+ },
+ "Mobility": 0.85,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 10
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 30
+ },
+ "Mobility": 0.85,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 10
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 40
+ },
+ "Mobility": 0.85,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 10
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 50
+ },
+ "Mobility": 0.85,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 10
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 60
+ },
+ "Mobility": 0.85,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 10
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 70
+ },
+ "Mobility": 0.85,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 10
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 80
+ },
+ "Mobility": 0.85,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 10
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 90
+ },
+ "Mobility": 0.85,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 10
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 100
+ },
+ "Mobility": 0.85,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 10
+ }
+ ],
+ "Normalization": {
+ "Position": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ },
+ "Angle": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ }
+ }
+ },
+ {
+ "Id": "PhysicsSetting10",
+ "Input": [
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleX"
+ },
+ "Weight": 60,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ"
+ },
+ "Weight": 60,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX"
+ },
+ "Weight": 40,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ"
+ },
+ "Weight": 40,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Output": [
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_1_ArtMesh54"
+ },
+ "VertexIndex": 1,
+ "Scale": 5,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_2_ArtMesh54"
+ },
+ "VertexIndex": 2,
+ "Scale": 5,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_3_ArtMesh54"
+ },
+ "VertexIndex": 3,
+ "Scale": 10,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_4_ArtMesh54"
+ },
+ "VertexIndex": 4,
+ "Scale": 20,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_5_ArtMesh54"
+ },
+ "VertexIndex": 5,
+ "Scale": 20,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_6_ArtMesh54"
+ },
+ "VertexIndex": 6,
+ "Scale": 25,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_7_ArtMesh54"
+ },
+ "VertexIndex": 7,
+ "Scale": 30,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Vertices": [
+ {
+ "Position": {
+ "X": 0,
+ "Y": 0
+ },
+ "Mobility": 0.9,
+ "Delay": 1,
+ "Acceleration": 1,
+ "Radius": 15
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 15
+ },
+ "Mobility": 0.9,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 15
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 30
+ },
+ "Mobility": 0.9,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 15
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 45
+ },
+ "Mobility": 0.9,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 15
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 60
+ },
+ "Mobility": 0.9,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 15
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 75
+ },
+ "Mobility": 0.9,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 15
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 90
+ },
+ "Mobility": 0.9,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 15
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 105
+ },
+ "Mobility": 0.9,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 15
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 120
+ },
+ "Mobility": 0.9,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 15
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 135
+ },
+ "Mobility": 0.9,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 15
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 150
+ },
+ "Mobility": 0.9,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 15
+ }
+ ],
+ "Normalization": {
+ "Position": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ },
+ "Angle": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ }
+ }
+ },
+ {
+ "Id": "PhysicsSetting11",
+ "Input": [
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleX"
+ },
+ "Weight": 60,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ"
+ },
+ "Weight": 60,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX"
+ },
+ "Weight": 40,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ"
+ },
+ "Weight": 40,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Output": [
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_1_ArtMesh55"
+ },
+ "VertexIndex": 1,
+ "Scale": 5,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_2_ArtMesh55"
+ },
+ "VertexIndex": 2,
+ "Scale": 5,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_3_ArtMesh55"
+ },
+ "VertexIndex": 3,
+ "Scale": 10,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_4_ArtMesh55"
+ },
+ "VertexIndex": 4,
+ "Scale": 20,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_5_ArtMesh55"
+ },
+ "VertexIndex": 5,
+ "Scale": 20,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_6_ArtMesh55"
+ },
+ "VertexIndex": 6,
+ "Scale": 25,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "Param_Angle_Rotation_7_ArtMesh55"
+ },
+ "VertexIndex": 7,
+ "Scale": 30,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Vertices": [
+ {
+ "Position": {
+ "X": 0,
+ "Y": 0
+ },
+ "Mobility": 0.9,
+ "Delay": 0.7,
+ "Acceleration": 1,
+ "Radius": 15
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 15
+ },
+ "Mobility": 0.9,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 15
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 30
+ },
+ "Mobility": 0.9,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 15
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 45
+ },
+ "Mobility": 0.9,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 15
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 60
+ },
+ "Mobility": 0.9,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 15
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 75
+ },
+ "Mobility": 0.9,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 15
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 90
+ },
+ "Mobility": 0.9,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 15
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 105
+ },
+ "Mobility": 0.9,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 15
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 120
+ },
+ "Mobility": 0.9,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 15
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 135
+ },
+ "Mobility": 0.9,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 15
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 150
+ },
+ "Mobility": 0.9,
+ "Delay": 0.85,
+ "Acceleration": 1.2,
+ "Radius": 15
+ }
+ ],
+ "Normalization": {
+ "Position": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ },
+ "Angle": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.pose3.json b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.pose3.json
new file mode 100644
index 0000000..28bda7c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.pose3.json
@@ -0,0 +1,15 @@
+{
+ "Type": "Live2D Pose",
+ "Groups": [
+ [
+ {
+ "Id": "PartArmA",
+ "Link": []
+ },
+ {
+ "Id": "PartArmB",
+ "Link": []
+ }
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m01.motion3.json b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m01.motion3.json
new file mode 100644
index 0000000..69d1b28
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m01.motion3.json
@@ -0,0 +1,1063 @@
+{
+ "Version": 3,
+ "Meta": {
+ "Duration": 4.7,
+ "Fps": 30.0,
+ "Loop": true,
+ "AreBeziersRestricted": false,
+ "CurveCount": 29,
+ "TotalSegmentCount": 135,
+ "TotalPointCount": 374,
+ "UserDataCount": 0,
+ "TotalUserDataSize": 0
+ },
+ "Curves": [
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleX",
+ "Segments": [
+ 0,
+ -8,
+ 1,
+ 0.067,
+ -8,
+ 0.133,
+ -8,
+ 0.2,
+ -8,
+ 1,
+ 0.278,
+ -8,
+ 0.356,
+ -8,
+ 0.433,
+ -8,
+ 1,
+ 0.556,
+ -8,
+ 0.678,
+ -8,
+ 0.8,
+ -8,
+ 1,
+ 0.956,
+ -8,
+ 1.111,
+ -8,
+ 1.267,
+ -8,
+ 1,
+ 1.522,
+ -8,
+ 1.778,
+ 0,
+ 2.033,
+ 0,
+ 1,
+ 2.122,
+ 0,
+ 2.211,
+ 0,
+ 2.3,
+ 0,
+ 1,
+ 2.556,
+ 0,
+ 2.811,
+ 9,
+ 3.067,
+ 9,
+ 1,
+ 3.356,
+ 9,
+ 3.644,
+ 0,
+ 3.933,
+ 0,
+ 1,
+ 4.067,
+ 0,
+ 4.2,
+ 1,
+ 4.333,
+ 1,
+ 0,
+ 4.7,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleY",
+ "Segments": [
+ 0,
+ -5,
+ 1,
+ 0.067,
+ -5,
+ 0.133,
+ -5,
+ 0.2,
+ -5,
+ 1,
+ 0.322,
+ -5,
+ 0.444,
+ 16,
+ 0.567,
+ 16,
+ 1,
+ 0.711,
+ 16,
+ 0.856,
+ -12,
+ 1,
+ -12,
+ 1,
+ 1.089,
+ -12,
+ 1.178,
+ 11,
+ 1.267,
+ 11,
+ 1,
+ 1.356,
+ 11,
+ 1.444,
+ 9.376,
+ 1.533,
+ 3,
+ 1,
+ 1.611,
+ -2.579,
+ 1.689,
+ -8,
+ 1.767,
+ -8,
+ 1,
+ 1.856,
+ -8,
+ 1.944,
+ 0,
+ 2.033,
+ 0,
+ 1,
+ 2.122,
+ 0,
+ 2.211,
+ 0,
+ 2.3,
+ 0,
+ 1,
+ 2.578,
+ 0,
+ 2.856,
+ -30,
+ 3.133,
+ -30,
+ 1,
+ 3.278,
+ -30,
+ 3.422,
+ 15,
+ 3.567,
+ 15,
+ 1,
+ 3.689,
+ 15,
+ 3.811,
+ -9,
+ 3.933,
+ -9,
+ 0,
+ 4.7,
+ -9
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.278,
+ 0,
+ 0.356,
+ 0,
+ 0.433,
+ 0,
+ 1,
+ 0.711,
+ 0,
+ 0.989,
+ 0,
+ 1.267,
+ 0,
+ 1,
+ 1.522,
+ 0,
+ 1.778,
+ 14,
+ 2.033,
+ 14,
+ 1,
+ 2.178,
+ 14,
+ 2.322,
+ 14,
+ 2.467,
+ 14,
+ 1,
+ 2.8,
+ 14,
+ 3.133,
+ 16,
+ 3.467,
+ 16,
+ 1,
+ 3.633,
+ 16,
+ 3.8,
+ -13,
+ 3.967,
+ -13,
+ 1,
+ 4.111,
+ -13,
+ 4.256,
+ -11.207,
+ 4.4,
+ -11.207,
+ 0,
+ 4.7,
+ -11.207
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamCheek",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.7,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLOpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.556,
+ 1,
+ 1.111,
+ 1,
+ 1.667,
+ 1,
+ 1,
+ 1.711,
+ 1,
+ 1.756,
+ 0,
+ 1.8,
+ 0,
+ 1,
+ 1.822,
+ 0,
+ 1.844,
+ 0,
+ 1.867,
+ 0,
+ 1,
+ 1.911,
+ 0,
+ 1.956,
+ 1,
+ 2,
+ 1,
+ 1,
+ 2.489,
+ 1,
+ 2.978,
+ 1,
+ 3.467,
+ 1,
+ 1,
+ 3.511,
+ 1,
+ 3.556,
+ 0,
+ 3.6,
+ 0,
+ 1,
+ 3.622,
+ 0,
+ 3.644,
+ 0,
+ 3.667,
+ 0,
+ 1,
+ 3.733,
+ 0,
+ 3.8,
+ 1,
+ 3.867,
+ 1,
+ 0,
+ 4.7,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLSmile",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.7,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeROpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.556,
+ 1,
+ 1.111,
+ 1,
+ 1.667,
+ 1,
+ 1,
+ 1.711,
+ 1,
+ 1.756,
+ 0,
+ 1.8,
+ 0,
+ 1,
+ 1.822,
+ 0,
+ 1.844,
+ 0,
+ 1.867,
+ 0,
+ 1,
+ 1.911,
+ 0,
+ 1.956,
+ 1,
+ 2,
+ 1,
+ 1,
+ 2.489,
+ 1,
+ 2.978,
+ 1,
+ 3.467,
+ 1,
+ 1,
+ 3.511,
+ 1,
+ 3.556,
+ 0,
+ 3.6,
+ 0,
+ 1,
+ 3.622,
+ 0,
+ 3.644,
+ 0,
+ 3.667,
+ 0,
+ 1,
+ 3.733,
+ 0,
+ 3.8,
+ 1,
+ 3.867,
+ 1,
+ 0,
+ 4.7,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRSmile",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.7,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.156,
+ 0,
+ 0.311,
+ -0.02,
+ 0.467,
+ -0.02,
+ 1,
+ 0.578,
+ -0.02,
+ 0.689,
+ 0,
+ 0.8,
+ 0,
+ 1,
+ 1.056,
+ 0,
+ 1.311,
+ 0,
+ 1.567,
+ 0,
+ 1,
+ 1.767,
+ 0,
+ 1.967,
+ -0.131,
+ 2.167,
+ -0.15,
+ 1,
+ 2.644,
+ -0.195,
+ 3.122,
+ -0.2,
+ 3.6,
+ -0.2,
+ 1,
+ 3.622,
+ -0.2,
+ 3.644,
+ 0.803,
+ 3.667,
+ 0.803,
+ 0,
+ 4.7,
+ 0.803
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.156,
+ 0,
+ 0.311,
+ 0.01,
+ 0.467,
+ 0.08,
+ 1,
+ 0.578,
+ 0.13,
+ 0.689,
+ 0.21,
+ 0.8,
+ 0.21,
+ 1,
+ 1.056,
+ 0.21,
+ 1.311,
+ 0.21,
+ 1.567,
+ 0.21,
+ 1,
+ 1.767,
+ 0.21,
+ 1.967,
+ 0.08,
+ 2.167,
+ 0.08,
+ 1,
+ 2.644,
+ 0.08,
+ 3.122,
+ 0.086,
+ 3.6,
+ 0.1,
+ 1,
+ 3.622,
+ 0.101,
+ 3.644,
+ 0.794,
+ 3.667,
+ 0.794,
+ 0,
+ 4.7,
+ 0.794
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLY",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.7,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRY",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.7,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLX",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.7,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRX",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.7,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLAngle",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.7,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRAngle",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.7,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.7,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.7,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.856,
+ 1,
+ 1.711,
+ 1,
+ 2.567,
+ 1,
+ 1,
+ 2.789,
+ 1,
+ 3.011,
+ 1.145,
+ 3.233,
+ 0,
+ 1,
+ 3.467,
+ -1.202,
+ 3.7,
+ -6,
+ 3.933,
+ -6,
+ 0,
+ 4.7,
+ -6
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.278,
+ 0,
+ 0.356,
+ 7,
+ 0.433,
+ 7,
+ 1,
+ 0.611,
+ 7,
+ 0.789,
+ -4,
+ 0.967,
+ -4,
+ 1,
+ 1.078,
+ -4,
+ 1.189,
+ 6,
+ 1.3,
+ 6,
+ 1,
+ 1.433,
+ 6,
+ 1.567,
+ -3,
+ 1.7,
+ -3,
+ 1,
+ 1.789,
+ -3,
+ 1.878,
+ -1.459,
+ 1.967,
+ 0,
+ 1,
+ 2.067,
+ 1.642,
+ 2.167,
+ 3.47,
+ 2.267,
+ 4,
+ 1,
+ 2.367,
+ 4.53,
+ 2.467,
+ 4.312,
+ 2.567,
+ 5,
+ 1,
+ 2.889,
+ 7.215,
+ 3.211,
+ 10,
+ 3.533,
+ 10,
+ 1,
+ 3.667,
+ 10,
+ 3.8,
+ 0,
+ 3.933,
+ 0,
+ 1,
+ 4.067,
+ 0,
+ 4.2,
+ 0,
+ 4.333,
+ 0,
+ 0,
+ 4.7,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.822,
+ 0,
+ 1.644,
+ 0,
+ 2.467,
+ 0,
+ 1,
+ 2.722,
+ 0,
+ 2.978,
+ 5,
+ 3.233,
+ 5,
+ 1,
+ 3.489,
+ 5,
+ 3.744,
+ -5,
+ 4,
+ -5,
+ 1,
+ 4.156,
+ -5,
+ 4.311,
+ -3.976,
+ 4.467,
+ -3.976,
+ 0,
+ 4.7,
+ -3.976
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBreath",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.7,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamShoulder",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0.2,
+ 0.333,
+ 0.2,
+ 1,
+ 0.422,
+ 0.2,
+ 0.511,
+ -0.1,
+ 0.6,
+ -0.1,
+ 1,
+ 1.578,
+ -0.1,
+ 2.556,
+ -0.1,
+ 3.533,
+ -0.1,
+ 1,
+ 3.611,
+ -0.1,
+ 3.689,
+ 0.2,
+ 3.767,
+ 0.2,
+ 1,
+ 3.989,
+ 0.2,
+ 4.211,
+ -0.9,
+ 4.433,
+ -0.9,
+ 0,
+ 4.7,
+ -0.9
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamLeg",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 4.7,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmLA",
+ "Segments": [
+ 0,
+ -10,
+ 1,
+ 0.178,
+ -10,
+ 0.356,
+ -7,
+ 0.533,
+ -7,
+ 1,
+ 0.7,
+ -7,
+ 0.867,
+ -10,
+ 1.033,
+ -10,
+ 1,
+ 1.2,
+ -10,
+ 1.367,
+ -8,
+ 1.533,
+ -8,
+ 1,
+ 1.611,
+ -8,
+ 1.689,
+ -8.746,
+ 1.767,
+ -9,
+ 1,
+ 2.011,
+ -9.797,
+ 2.256,
+ -10,
+ 2.5,
+ -10,
+ 1,
+ 2.556,
+ -10,
+ 2.611,
+ -10,
+ 2.667,
+ -10,
+ 1,
+ 2.789,
+ -10,
+ 2.911,
+ -10,
+ 3.033,
+ -10,
+ 0,
+ 4.7,
+ -10
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmRA",
+ "Segments": [
+ 0,
+ -10,
+ 1,
+ 0.178,
+ -10,
+ 0.356,
+ -7,
+ 0.533,
+ -7,
+ 1,
+ 0.7,
+ -7,
+ 0.867,
+ -10,
+ 1.033,
+ -10,
+ 1,
+ 1.2,
+ -10,
+ 1.367,
+ -6,
+ 1.533,
+ -6,
+ 1,
+ 1.611,
+ -6,
+ 1.689,
+ -6.903,
+ 1.767,
+ -7.5,
+ 1,
+ 2.011,
+ -9.377,
+ 2.256,
+ -10,
+ 2.5,
+ -10,
+ 1,
+ 2.567,
+ -10,
+ 2.633,
+ -8.958,
+ 2.7,
+ -8.958,
+ 1,
+ 2.811,
+ -8.958,
+ 2.922,
+ -10,
+ 3.033,
+ -10,
+ 0,
+ 4.7,
+ -10
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairAhoge",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.144,
+ 0,
+ 0.289,
+ 10,
+ 0.433,
+ 10,
+ 1,
+ 0.578,
+ 10,
+ 0.722,
+ -10,
+ 0.867,
+ -10,
+ 1,
+ 0.989,
+ -10,
+ 1.111,
+ 4,
+ 1.233,
+ 4,
+ 1,
+ 1.322,
+ 4,
+ 1.411,
+ 2.767,
+ 1.5,
+ 0,
+ 1,
+ 1.611,
+ -3.459,
+ 1.722,
+ -5.351,
+ 1.833,
+ -5.351,
+ 1,
+ 1.922,
+ -5.351,
+ 2.011,
+ 1.661,
+ 2.1,
+ 1.661,
+ 1,
+ 2.233,
+ 1.661,
+ 2.367,
+ 0,
+ 2.5,
+ 0,
+ 1,
+ 2.867,
+ 0,
+ 3.233,
+ 10,
+ 3.6,
+ 10,
+ 1,
+ 3.722,
+ 10,
+ 3.844,
+ -10,
+ 3.967,
+ -10,
+ 1,
+ 4.078,
+ -10,
+ 4.189,
+ 6,
+ 4.3,
+ 6,
+ 1,
+ 4.356,
+ 6,
+ 4.411,
+ 0,
+ 4.467,
+ 0,
+ 0,
+ 4.7,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmA",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 4.7,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmB",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.7,
+ 0
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m02.motion3.json b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m02.motion3.json
new file mode 100644
index 0000000..9a873fc
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m02.motion3.json
@@ -0,0 +1,1428 @@
+{
+ "Version": 3,
+ "Meta": {
+ "Duration": 5.93,
+ "Fps": 30.0,
+ "Loop": true,
+ "AreBeziersRestricted": false,
+ "CurveCount": 31,
+ "TotalSegmentCount": 190,
+ "TotalPointCount": 537,
+ "UserDataCount": 0,
+ "TotalUserDataSize": 0
+ },
+ "Curves": [
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleX",
+ "Segments": [
+ 0,
+ 18,
+ 1,
+ 0.211,
+ 18,
+ 0.422,
+ 0,
+ 0.633,
+ 0,
+ 1,
+ 0.889,
+ 0,
+ 1.144,
+ 22,
+ 1.4,
+ 22,
+ 1,
+ 1.578,
+ 22,
+ 1.756,
+ 22,
+ 1.933,
+ 22,
+ 1,
+ 2.1,
+ 22,
+ 2.267,
+ 14.992,
+ 2.433,
+ 0,
+ 1,
+ 2.6,
+ -14.992,
+ 2.767,
+ -23,
+ 2.933,
+ -23,
+ 1,
+ 3.244,
+ -23,
+ 3.556,
+ -23,
+ 3.867,
+ -23,
+ 1,
+ 4.078,
+ -23,
+ 4.289,
+ 0,
+ 4.5,
+ 0,
+ 0,
+ 5.933,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleY",
+ "Segments": [
+ 0,
+ -24,
+ 1,
+ 0.211,
+ -24,
+ 0.422,
+ 0,
+ 0.633,
+ 0,
+ 1,
+ 0.889,
+ 0,
+ 1.144,
+ -18,
+ 1.4,
+ -18,
+ 1,
+ 1.522,
+ -18,
+ 1.644,
+ -11,
+ 1.767,
+ -11,
+ 1,
+ 1.822,
+ -11,
+ 1.878,
+ -11,
+ 1.933,
+ -11,
+ 1,
+ 2.1,
+ -11,
+ 2.267,
+ -3,
+ 2.433,
+ -3,
+ 1,
+ 2.6,
+ -3,
+ 2.767,
+ -30,
+ 2.933,
+ -30,
+ 1,
+ 3.133,
+ -30,
+ 3.333,
+ -23,
+ 3.533,
+ -23,
+ 1,
+ 3.644,
+ -23,
+ 3.756,
+ -23,
+ 3.867,
+ -23,
+ 1,
+ 3.911,
+ -23,
+ 3.956,
+ -23,
+ 4,
+ -23,
+ 1,
+ 4.167,
+ -23,
+ 4.333,
+ 0,
+ 4.5,
+ 0,
+ 0,
+ 5.933,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.222,
+ 0,
+ 0.444,
+ 0,
+ 0.667,
+ 0,
+ 1,
+ 0.756,
+ 0,
+ 0.844,
+ 1.707,
+ 0.933,
+ 8,
+ 1,
+ 1.122,
+ 21.373,
+ 1.311,
+ 30,
+ 1.5,
+ 30,
+ 1,
+ 1.656,
+ 30,
+ 1.811,
+ 30,
+ 1.967,
+ 30,
+ 1,
+ 2.289,
+ 30,
+ 2.611,
+ -30,
+ 2.933,
+ -30,
+ 1,
+ 3.244,
+ -30,
+ 3.556,
+ -30,
+ 3.867,
+ -30,
+ 1,
+ 4.078,
+ -30,
+ 4.289,
+ -21.337,
+ 4.5,
+ 1,
+ 1,
+ 4.578,
+ 9.23,
+ 4.656,
+ 17,
+ 4.733,
+ 17,
+ 0,
+ 5.933,
+ 17
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamCheek",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 5.933,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLOpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.333,
+ 1,
+ 0.667,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1.044,
+ 1,
+ 1.089,
+ 0,
+ 1.133,
+ 0,
+ 1,
+ 1.156,
+ 0,
+ 1.178,
+ 0,
+ 1.2,
+ 0,
+ 1,
+ 1.256,
+ 0,
+ 1.311,
+ 1,
+ 1.367,
+ 1,
+ 1,
+ 1.833,
+ 1,
+ 2.3,
+ 1,
+ 2.767,
+ 1,
+ 1,
+ 2.867,
+ 1,
+ 2.967,
+ 1.2,
+ 3.067,
+ 1.2,
+ 1,
+ 3.233,
+ 1.2,
+ 3.4,
+ 1,
+ 3.567,
+ 1,
+ 1,
+ 3.689,
+ 1,
+ 3.811,
+ 1,
+ 3.933,
+ 1,
+ 1,
+ 3.978,
+ 1,
+ 4.022,
+ 0,
+ 4.067,
+ 0,
+ 1,
+ 4.089,
+ 0,
+ 4.111,
+ 0,
+ 4.133,
+ 0,
+ 1,
+ 4.189,
+ 0,
+ 4.244,
+ 1,
+ 4.3,
+ 1,
+ 1,
+ 4.4,
+ 1,
+ 4.5,
+ 1,
+ 4.6,
+ 1,
+ 1,
+ 4.633,
+ 1,
+ 4.667,
+ 0,
+ 4.7,
+ 0,
+ 0,
+ 5.933,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLSmile",
+ "Segments": [
+ 0,
+ 0.63,
+ 1,
+ 1.433,
+ 0.63,
+ 2.867,
+ 0.6,
+ 4.3,
+ 0.6,
+ 1,
+ 4.4,
+ 0.6,
+ 4.5,
+ 0.771,
+ 4.6,
+ 0.771,
+ 0,
+ 5.933,
+ 0.771
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeROpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.333,
+ 1,
+ 0.667,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1.044,
+ 1,
+ 1.089,
+ 0,
+ 1.133,
+ 0,
+ 1,
+ 1.156,
+ 0,
+ 1.178,
+ 0,
+ 1.2,
+ 0,
+ 1,
+ 1.256,
+ 0,
+ 1.311,
+ 1,
+ 1.367,
+ 1,
+ 1,
+ 1.833,
+ 1,
+ 2.3,
+ 1,
+ 2.767,
+ 1,
+ 1,
+ 2.867,
+ 1,
+ 2.967,
+ 1.2,
+ 3.067,
+ 1.2,
+ 1,
+ 3.233,
+ 1.2,
+ 3.4,
+ 1,
+ 3.567,
+ 1,
+ 1,
+ 3.689,
+ 1,
+ 3.811,
+ 1,
+ 3.933,
+ 1,
+ 1,
+ 3.978,
+ 1,
+ 4.022,
+ 0,
+ 4.067,
+ 0,
+ 1,
+ 4.089,
+ 0,
+ 4.111,
+ 0,
+ 4.133,
+ 0,
+ 1,
+ 4.189,
+ 0,
+ 4.244,
+ 1,
+ 4.3,
+ 1,
+ 1,
+ 4.4,
+ 1,
+ 4.5,
+ 1,
+ 4.6,
+ 1,
+ 1,
+ 4.633,
+ 1,
+ 4.667,
+ 0,
+ 4.7,
+ 0,
+ 0,
+ 5.933,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRSmile",
+ "Segments": [
+ 0,
+ 0.63,
+ 1,
+ 1.433,
+ 0.63,
+ 2.867,
+ 0.6,
+ 4.3,
+ 0.6,
+ 1,
+ 4.4,
+ 0.6,
+ 4.5,
+ 0.76,
+ 4.6,
+ 0.76,
+ 0,
+ 5.933,
+ 0.76
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallX",
+ "Segments": [
+ 0,
+ -0.002,
+ 1,
+ 0.211,
+ -0.002,
+ 0.422,
+ 0,
+ 0.633,
+ 0,
+ 1,
+ 0.889,
+ 0,
+ 1.144,
+ -0.31,
+ 1.4,
+ -0.31,
+ 1,
+ 1.578,
+ -0.31,
+ 1.756,
+ -0.31,
+ 1.933,
+ -0.31,
+ 1,
+ 2.233,
+ -0.31,
+ 2.533,
+ 0.67,
+ 2.833,
+ 0.67,
+ 1,
+ 3.144,
+ 0.67,
+ 3.456,
+ 0.67,
+ 3.767,
+ 0.67,
+ 1,
+ 4.011,
+ 0.67,
+ 4.256,
+ 0,
+ 4.5,
+ 0,
+ 0,
+ 5.933,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.211,
+ 0,
+ 0.422,
+ 0,
+ 0.633,
+ 0,
+ 1,
+ 0.889,
+ 0,
+ 1.144,
+ 0.38,
+ 1.4,
+ 0.38,
+ 1,
+ 1.578,
+ 0.38,
+ 1.756,
+ 0.38,
+ 1.933,
+ 0.38,
+ 1,
+ 2.233,
+ 0.38,
+ 2.533,
+ 0.46,
+ 2.833,
+ 0.46,
+ 1,
+ 3.144,
+ 0.46,
+ 3.456,
+ 0.46,
+ 3.767,
+ 0.46,
+ 1,
+ 4.011,
+ 0.46,
+ 4.256,
+ 0,
+ 4.5,
+ 0,
+ 0,
+ 5.933,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.956,
+ 0,
+ 1.911,
+ 0,
+ 2.867,
+ 0,
+ 1,
+ 2.933,
+ 0,
+ 3,
+ 0.125,
+ 3.067,
+ 0.125,
+ 1,
+ 3.133,
+ 0.125,
+ 3.2,
+ 0,
+ 3.267,
+ 0,
+ 1,
+ 3.711,
+ 0,
+ 4.156,
+ 0,
+ 4.6,
+ 0,
+ 1,
+ 4.667,
+ 0,
+ 4.733,
+ 0.396,
+ 4.8,
+ 0.396,
+ 0,
+ 5.933,
+ 0.396
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.956,
+ 0,
+ 1.911,
+ 0,
+ 2.867,
+ 0,
+ 1,
+ 2.933,
+ 0,
+ 3,
+ 0.125,
+ 3.067,
+ 0.125,
+ 1,
+ 3.133,
+ 0.125,
+ 3.2,
+ 0,
+ 3.267,
+ 0,
+ 1,
+ 3.711,
+ 0,
+ 4.156,
+ 0,
+ 4.6,
+ 0,
+ 1,
+ 4.667,
+ 0,
+ 4.733,
+ 0.417,
+ 4.8,
+ 0.417,
+ 0,
+ 5.933,
+ 0.417
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLX",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5.933,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRX",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5.933,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLAngle",
+ "Segments": [
+ 0,
+ 0.03,
+ 0,
+ 5.933,
+ 0.03
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRAngle",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5.933,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5.933,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5.933,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.244,
+ 0,
+ 0.489,
+ 0,
+ 0.733,
+ 0,
+ 1,
+ 0.778,
+ 0,
+ 0.822,
+ 0.667,
+ 0.867,
+ 2,
+ 1,
+ 1.044,
+ 7.333,
+ 1.222,
+ 10,
+ 1.4,
+ 10,
+ 1,
+ 1.533,
+ 10,
+ 1.667,
+ 10,
+ 1.8,
+ 10,
+ 1,
+ 2.178,
+ 10,
+ 2.556,
+ 6,
+ 2.933,
+ 6,
+ 1,
+ 3.244,
+ 6,
+ 3.556,
+ 6,
+ 3.867,
+ 6,
+ 1,
+ 4.189,
+ 6,
+ 4.511,
+ 0,
+ 4.833,
+ 0,
+ 1,
+ 4.956,
+ 0,
+ 5.078,
+ 1.192,
+ 5.2,
+ 1.192,
+ 1,
+ 5.322,
+ 1.192,
+ 5.444,
+ 0.013,
+ 5.567,
+ 0.013,
+ 0,
+ 5.933,
+ 0.013
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.211,
+ 0,
+ 0.422,
+ 0,
+ 0.633,
+ 0,
+ 1,
+ 0.667,
+ 0,
+ 0.7,
+ 0,
+ 0.733,
+ 0,
+ 1,
+ 1.133,
+ 0,
+ 1.533,
+ 0,
+ 1.933,
+ 0,
+ 1,
+ 2.078,
+ 0,
+ 2.222,
+ 4,
+ 2.367,
+ 4,
+ 1,
+ 2.556,
+ 4,
+ 2.744,
+ -2.18,
+ 2.933,
+ -2.18,
+ 1,
+ 3.022,
+ -2.18,
+ 3.111,
+ 0,
+ 3.2,
+ 0,
+ 1,
+ 3.456,
+ 0,
+ 3.711,
+ -1,
+ 3.967,
+ -1,
+ 1,
+ 4.067,
+ -1,
+ 4.167,
+ -1.018,
+ 4.267,
+ 0,
+ 1,
+ 4.333,
+ 0.679,
+ 4.4,
+ 10,
+ 4.467,
+ 10,
+ 1,
+ 4.567,
+ 10,
+ 4.667,
+ -5,
+ 4.767,
+ -5,
+ 1,
+ 4.889,
+ -5,
+ 5.011,
+ 0,
+ 5.133,
+ 0,
+ 0,
+ 5.933,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.233,
+ 0,
+ 0.467,
+ 0,
+ 0.7,
+ 0,
+ 1,
+ 0.733,
+ 0,
+ 0.767,
+ 0,
+ 0.8,
+ 0,
+ 1,
+ 1.278,
+ 0,
+ 1.756,
+ -4,
+ 2.233,
+ -4,
+ 1,
+ 2.544,
+ -4,
+ 2.856,
+ 8,
+ 3.167,
+ 8,
+ 1,
+ 3.4,
+ 8,
+ 3.633,
+ 8.008,
+ 3.867,
+ 7.985,
+ 1,
+ 4.178,
+ 7.954,
+ 4.489,
+ -3,
+ 4.8,
+ -3,
+ 0,
+ 5.933,
+ -3
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBreath",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.122,
+ 0,
+ 0.244,
+ 1,
+ 0.367,
+ 1,
+ 1,
+ 0.511,
+ 1,
+ 0.656,
+ 0,
+ 0.8,
+ 0,
+ 1,
+ 1.022,
+ 0,
+ 1.244,
+ 1,
+ 1.467,
+ 1,
+ 1,
+ 1.689,
+ 1,
+ 1.911,
+ 0,
+ 2.133,
+ 0,
+ 1,
+ 2.344,
+ 0,
+ 2.556,
+ 1,
+ 2.767,
+ 1,
+ 1,
+ 2.967,
+ 1,
+ 3.167,
+ 0,
+ 3.367,
+ 0,
+ 1,
+ 3.578,
+ 0,
+ 3.789,
+ 1,
+ 4,
+ 1,
+ 1,
+ 4.233,
+ 1,
+ 4.467,
+ 0,
+ 4.7,
+ 0,
+ 1,
+ 4.9,
+ 0,
+ 5.1,
+ 0,
+ 5.3,
+ 0,
+ 0,
+ 5.933,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamShoulder",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.456,
+ 0,
+ 0.911,
+ 0.4,
+ 1.367,
+ 0.4,
+ 1,
+ 1.889,
+ 0.4,
+ 2.411,
+ -1,
+ 2.933,
+ -1,
+ 1,
+ 3.244,
+ -1,
+ 3.556,
+ -1,
+ 3.867,
+ -1,
+ 0,
+ 5.933,
+ -1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamLeg",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 5.933,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmLA",
+ "Segments": [
+ 0,
+ -8.7,
+ 1,
+ 0.289,
+ -8.7,
+ 0.578,
+ -8.7,
+ 0.867,
+ -8.7,
+ 1,
+ 1,
+ -8.7,
+ 1.133,
+ -10,
+ 1.267,
+ -10,
+ 1,
+ 1.578,
+ -10,
+ 1.889,
+ -10,
+ 2.2,
+ -10,
+ 1,
+ 2.444,
+ -10,
+ 2.689,
+ -6.9,
+ 2.933,
+ -6.9,
+ 1,
+ 3.244,
+ -6.9,
+ 3.556,
+ -6.9,
+ 3.867,
+ -6.9,
+ 1,
+ 3.989,
+ -6.9,
+ 4.111,
+ -8.3,
+ 4.233,
+ -8.3,
+ 1,
+ 4.344,
+ -8.3,
+ 4.456,
+ -3,
+ 4.567,
+ -3,
+ 1,
+ 4.689,
+ -3,
+ 4.811,
+ -4,
+ 4.933,
+ -4,
+ 1,
+ 5.1,
+ -4,
+ 5.267,
+ -3,
+ 5.433,
+ -3,
+ 0,
+ 5.933,
+ -3
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmRA",
+ "Segments": [
+ 0,
+ -8.7,
+ 1,
+ 0.289,
+ -8.7,
+ 0.578,
+ -8.7,
+ 0.867,
+ -8.7,
+ 1,
+ 1,
+ -8.7,
+ 1.133,
+ -10,
+ 1.267,
+ -10,
+ 1,
+ 1.578,
+ -10,
+ 1.889,
+ -10,
+ 2.2,
+ -10,
+ 1,
+ 2.444,
+ -10,
+ 2.689,
+ -10,
+ 2.933,
+ -10,
+ 1,
+ 3.244,
+ -10,
+ 3.556,
+ -10,
+ 3.867,
+ -10,
+ 1,
+ 3.989,
+ -10,
+ 4.111,
+ -9.998,
+ 4.233,
+ -7.8,
+ 1,
+ 4.344,
+ -5.802,
+ 4.456,
+ 0.03,
+ 4.567,
+ 0.03,
+ 1,
+ 4.689,
+ 0.03,
+ 4.811,
+ -1,
+ 4.933,
+ -1,
+ 1,
+ 5.122,
+ -1,
+ 5.311,
+ -0.002,
+ 5.5,
+ -0.002,
+ 0,
+ 5.933,
+ -0.002
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHandL",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 1.067,
+ 0,
+ 2.133,
+ 0,
+ 3.2,
+ 0,
+ 1,
+ 3.633,
+ 0,
+ 4.067,
+ 1,
+ 4.5,
+ 1,
+ 1,
+ 4.667,
+ 1,
+ 4.833,
+ -1,
+ 5,
+ -1,
+ 1,
+ 5.122,
+ -1,
+ 5.244,
+ 0.208,
+ 5.367,
+ 0.208,
+ 0,
+ 5.933,
+ 0.208
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHandR",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 1.067,
+ 0,
+ 2.133,
+ 0,
+ 3.2,
+ 0,
+ 1,
+ 3.633,
+ 0,
+ 4.067,
+ 1,
+ 4.5,
+ 1,
+ 1,
+ 4.667,
+ 1,
+ 4.833,
+ -1,
+ 5,
+ -1,
+ 1,
+ 5.122,
+ -1,
+ 5.244,
+ 0.208,
+ 5.367,
+ 0.208,
+ 0,
+ 5.933,
+ 0.208
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairAhoge",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.333,
+ 0,
+ 0.667,
+ 0,
+ 1,
+ 0,
+ 1,
+ 1.133,
+ 0,
+ 1.267,
+ 10,
+ 1.4,
+ 10,
+ 1,
+ 1.489,
+ 10,
+ 1.578,
+ 2.177,
+ 1.667,
+ 2.177,
+ 1,
+ 1.789,
+ 2.177,
+ 1.911,
+ 10,
+ 2.033,
+ 10,
+ 1,
+ 2.289,
+ 10,
+ 2.544,
+ -10,
+ 2.8,
+ -10,
+ 1,
+ 2.933,
+ -10,
+ 3.067,
+ 7.54,
+ 3.2,
+ 7.54,
+ 1,
+ 3.344,
+ 7.54,
+ 3.489,
+ 0,
+ 3.633,
+ 0,
+ 1,
+ 3.911,
+ 0,
+ 4.189,
+ 0,
+ 4.467,
+ 0,
+ 1,
+ 4.6,
+ 0,
+ 4.733,
+ 10,
+ 4.867,
+ 10,
+ 1,
+ 4.989,
+ 10,
+ 5.111,
+ -10,
+ 5.233,
+ -10,
+ 1,
+ 5.322,
+ -10,
+ 5.411,
+ 3.456,
+ 5.5,
+ 3.456,
+ 1,
+ 5.556,
+ 3.456,
+ 5.611,
+ 0,
+ 5.667,
+ 0,
+ 0,
+ 5.933,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmA",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 5.93,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmB",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5.93,
+ 0
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m03.motion3.json b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m03.motion3.json
new file mode 100644
index 0000000..e35ceff
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m03.motion3.json
@@ -0,0 +1,1295 @@
+{
+ "Version": 3,
+ "Meta": {
+ "Duration": 4.2,
+ "Fps": 30.0,
+ "Loop": true,
+ "AreBeziersRestricted": false,
+ "CurveCount": 31,
+ "TotalSegmentCount": 169,
+ "TotalPointCount": 474,
+ "UserDataCount": 0,
+ "TotalUserDataSize": 0
+ },
+ "Curves": [
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.389,
+ 0,
+ 0.611,
+ 0,
+ 0.833,
+ 0,
+ 1,
+ 0.933,
+ 0,
+ 1.033,
+ 0,
+ 1.133,
+ 0,
+ 1,
+ 1.311,
+ 0,
+ 1.489,
+ 0,
+ 1.667,
+ 0,
+ 1,
+ 1.778,
+ 0,
+ 1.889,
+ -0.268,
+ 2,
+ -1,
+ 1,
+ 2.089,
+ -1.585,
+ 2.178,
+ -2,
+ 2.267,
+ -2,
+ 0,
+ 4.2,
+ -2
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.3,
+ 0,
+ 0.433,
+ -18,
+ 0.567,
+ -18,
+ 1,
+ 0.656,
+ -18,
+ 0.744,
+ 8,
+ 0.833,
+ 8,
+ 1,
+ 0.933,
+ 8,
+ 1.033,
+ -8,
+ 1.133,
+ -8,
+ 1,
+ 1.311,
+ -8,
+ 1.489,
+ 10,
+ 1.667,
+ 10,
+ 1,
+ 1.778,
+ 10,
+ 1.889,
+ -21,
+ 2,
+ -21,
+ 1,
+ 2.089,
+ -21,
+ 2.178,
+ -3,
+ 2.267,
+ -3,
+ 0,
+ 4.2,
+ -3
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.289,
+ 0,
+ 0.411,
+ -1,
+ 0.533,
+ -1,
+ 1,
+ 0.722,
+ -1,
+ 0.911,
+ 17,
+ 1.1,
+ 17,
+ 1,
+ 1.411,
+ 17,
+ 1.722,
+ -22,
+ 2.033,
+ -22,
+ 1,
+ 2.244,
+ -22,
+ 2.456,
+ 11,
+ 2.667,
+ 11,
+ 0,
+ 4.2,
+ 11
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamCheek",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4.2,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLOpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 1,
+ 0.2,
+ 1,
+ 0.233,
+ 1,
+ 0.267,
+ 1,
+ 1,
+ 0.322,
+ 1,
+ 0.378,
+ 0,
+ 0.433,
+ 0,
+ 1,
+ 0.478,
+ 0,
+ 0.522,
+ 0,
+ 0.567,
+ 0,
+ 1,
+ 0.644,
+ 0,
+ 0.722,
+ 1,
+ 0.8,
+ 1,
+ 1,
+ 1.056,
+ 1,
+ 1.311,
+ 0.988,
+ 1.567,
+ 0.988,
+ 1,
+ 1.789,
+ 0.988,
+ 2.011,
+ 1,
+ 2.233,
+ 1,
+ 1,
+ 2.267,
+ 1,
+ 2.3,
+ 1,
+ 2.333,
+ 1,
+ 1,
+ 2.367,
+ 1,
+ 2.4,
+ 0,
+ 2.433,
+ 0,
+ 1,
+ 2.478,
+ 0,
+ 2.522,
+ 0,
+ 2.567,
+ 0,
+ 1,
+ 2.622,
+ 0,
+ 2.678,
+ 1,
+ 2.733,
+ 1,
+ 0,
+ 4.2,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4.2,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeROpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 1,
+ 0.2,
+ 1,
+ 0.233,
+ 1,
+ 0.267,
+ 1,
+ 1,
+ 0.322,
+ 1,
+ 0.378,
+ 0,
+ 0.433,
+ 0,
+ 1,
+ 0.478,
+ 0,
+ 0.522,
+ 0,
+ 0.567,
+ 0,
+ 1,
+ 0.644,
+ 0,
+ 0.722,
+ 1,
+ 0.8,
+ 1,
+ 1,
+ 1.056,
+ 1,
+ 1.311,
+ 1,
+ 1.567,
+ 1,
+ 1,
+ 1.789,
+ 1,
+ 2.011,
+ 1,
+ 2.233,
+ 1,
+ 1,
+ 2.267,
+ 1,
+ 2.3,
+ 1,
+ 2.333,
+ 1,
+ 1,
+ 2.367,
+ 1,
+ 2.4,
+ 0,
+ 2.433,
+ 0,
+ 1,
+ 2.478,
+ 0,
+ 2.522,
+ 0,
+ 2.567,
+ 0,
+ 1,
+ 2.622,
+ 0,
+ 2.678,
+ 1,
+ 2.733,
+ 1,
+ 0,
+ 4.2,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4.2,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.256,
+ 0,
+ 0.344,
+ 0,
+ 0.433,
+ 0,
+ 1,
+ 0.478,
+ 0,
+ 0.522,
+ -1,
+ 0.567,
+ -1,
+ 1,
+ 0.8,
+ -1,
+ 1.033,
+ -1,
+ 1.267,
+ -1,
+ 1,
+ 1.422,
+ -1,
+ 1.578,
+ 0.583,
+ 1.733,
+ 0.583,
+ 1,
+ 1.967,
+ 0.583,
+ 2.2,
+ 0.583,
+ 2.433,
+ 0.583,
+ 1,
+ 2.478,
+ 0.583,
+ 2.522,
+ 0,
+ 2.567,
+ 0,
+ 0,
+ 4.2,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.256,
+ 0,
+ 0.344,
+ 0,
+ 0.433,
+ 0,
+ 1,
+ 0.478,
+ 0,
+ 0.522,
+ 1,
+ 0.567,
+ 1,
+ 1,
+ 0.8,
+ 1,
+ 1.033,
+ 1,
+ 1.267,
+ 1,
+ 1,
+ 1.422,
+ 1,
+ 1.578,
+ 0.75,
+ 1.733,
+ 0.75,
+ 1,
+ 1.967,
+ 0.75,
+ 2.2,
+ 0.75,
+ 2.433,
+ 0.75,
+ 1,
+ 2.478,
+ 0.75,
+ 2.522,
+ 0,
+ 2.567,
+ 0,
+ 0,
+ 4.2,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4.2,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4.2,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4.2,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4.2,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4.2,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4.2,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4.2,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4.2,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4.2,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.244,
+ 0,
+ 0.322,
+ -4,
+ 0.4,
+ -4,
+ 1,
+ 0.478,
+ -4,
+ 0.556,
+ 10,
+ 0.633,
+ 10,
+ 1,
+ 0.8,
+ 10,
+ 0.967,
+ -0.386,
+ 1.133,
+ -0.386,
+ 1,
+ 1.244,
+ -0.386,
+ 1.356,
+ 10,
+ 1.467,
+ 10,
+ 1,
+ 1.6,
+ 10,
+ 1.733,
+ 0,
+ 1.867,
+ 0,
+ 1,
+ 2.044,
+ 0,
+ 2.222,
+ 10,
+ 2.4,
+ 10,
+ 1,
+ 2.444,
+ 10,
+ 2.512,
+ 10,
+ 2.533,
+ 9.649,
+ 1,
+ 2.7,
+ 6.854,
+ 2.867,
+ -3.526,
+ 3.033,
+ -3.526,
+ 1,
+ 3.267,
+ -3.526,
+ 3.5,
+ 6.807,
+ 3.733,
+ 6.807,
+ 1,
+ 3.811,
+ 6.807,
+ 3.889,
+ 6,
+ 3.967,
+ 6,
+ 0,
+ 4.2,
+ 6
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.256,
+ 0,
+ 0.344,
+ -1,
+ 0.433,
+ -1,
+ 1,
+ 0.656,
+ -1,
+ 0.878,
+ 6,
+ 1.1,
+ 6,
+ 1,
+ 1.333,
+ 6,
+ 1.567,
+ -3,
+ 1.8,
+ -3,
+ 1,
+ 2.256,
+ -3,
+ 2.711,
+ 8,
+ 3.167,
+ 8,
+ 0,
+ 4.2,
+ 8
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBreath",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.189,
+ 0,
+ 0.378,
+ 1,
+ 0.567,
+ 1,
+ 1,
+ 0.733,
+ 1,
+ 0.9,
+ 0,
+ 1.067,
+ 0,
+ 1,
+ 1.3,
+ 0,
+ 1.533,
+ 1,
+ 1.767,
+ 1,
+ 1,
+ 2.033,
+ 1,
+ 2.3,
+ 0,
+ 2.567,
+ 0,
+ 1,
+ 2.778,
+ 0,
+ 2.989,
+ 1,
+ 3.2,
+ 1,
+ 1,
+ 3.4,
+ 1,
+ 3.6,
+ 0,
+ 3.8,
+ 0,
+ 0,
+ 4.2,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamShoulder",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.2,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamLeg",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.8,
+ 1,
+ 1.6,
+ 1,
+ 2.4,
+ 1,
+ 1,
+ 2.522,
+ 1,
+ 2.644,
+ 0,
+ 2.767,
+ 0,
+ 1,
+ 3.011,
+ 0,
+ 3.256,
+ 0.062,
+ 3.5,
+ 0.062,
+ 0,
+ 4.2,
+ 0.062
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmLA",
+ "Segments": [
+ 0,
+ -10,
+ 1,
+ 0.256,
+ -10,
+ 0.511,
+ 0,
+ 0.767,
+ 0,
+ 1,
+ 0.889,
+ 0,
+ 1.011,
+ -6.5,
+ 1.133,
+ -6.5,
+ 1,
+ 1.289,
+ -6.5,
+ 1.444,
+ 0,
+ 1.6,
+ 0,
+ 1,
+ 1.756,
+ 0,
+ 1.911,
+ -8,
+ 2.067,
+ -8,
+ 1,
+ 2.344,
+ -8,
+ 2.622,
+ -1.4,
+ 2.9,
+ -1.4,
+ 1,
+ 2.978,
+ -1.4,
+ 3.056,
+ -1.775,
+ 3.133,
+ -2.3,
+ 1,
+ 3.211,
+ -2.825,
+ 3.289,
+ -3,
+ 3.367,
+ -3,
+ 1,
+ 3.433,
+ -3,
+ 3.5,
+ -2.5,
+ 3.567,
+ -2.5,
+ 1,
+ 3.733,
+ -2.5,
+ 3.9,
+ -3,
+ 4.067,
+ -3,
+ 0,
+ 4.2,
+ -3
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmRA",
+ "Segments": [
+ 0,
+ -10,
+ 1,
+ 0.256,
+ -10,
+ 0.511,
+ 0,
+ 0.767,
+ 0,
+ 1,
+ 0.867,
+ 0,
+ 0.967,
+ -5.2,
+ 1.067,
+ -5.2,
+ 1,
+ 1.244,
+ -5.2,
+ 1.422,
+ 0,
+ 1.6,
+ 0,
+ 1,
+ 1.778,
+ 0,
+ 1.956,
+ -6.3,
+ 2.133,
+ -6.3,
+ 1,
+ 2.422,
+ -6.3,
+ 2.711,
+ 0.029,
+ 3,
+ 0.029,
+ 1,
+ 3.144,
+ 0.029,
+ 3.289,
+ 0,
+ 3.433,
+ 0,
+ 1,
+ 3.5,
+ 0,
+ 3.567,
+ 0,
+ 3.633,
+ 0,
+ 1,
+ 3.722,
+ 0,
+ 3.811,
+ -0.029,
+ 3.9,
+ -0.029,
+ 0,
+ 4.2,
+ -0.029
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHandL",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.256,
+ 0,
+ 0.344,
+ 1,
+ 0.433,
+ 1,
+ 1,
+ 0.589,
+ 1,
+ 0.744,
+ -1,
+ 0.9,
+ -1,
+ 1,
+ 1.111,
+ -1,
+ 1.322,
+ 1,
+ 1.533,
+ 1,
+ 1,
+ 1.733,
+ 1,
+ 1.933,
+ -1,
+ 2.133,
+ -1,
+ 1,
+ 2.411,
+ -1,
+ 2.689,
+ 1,
+ 2.967,
+ 1,
+ 1,
+ 3.111,
+ 1,
+ 3.256,
+ -1,
+ 3.4,
+ -1,
+ 1,
+ 3.5,
+ -1,
+ 3.6,
+ 0.8,
+ 3.7,
+ 0.8,
+ 0,
+ 4.2,
+ 0.8
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHandR",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.256,
+ 0,
+ 0.344,
+ 1,
+ 0.433,
+ 1,
+ 1,
+ 0.589,
+ 1,
+ 0.744,
+ -1,
+ 0.9,
+ -1,
+ 1,
+ 1.111,
+ -1,
+ 1.322,
+ 1,
+ 1.533,
+ 1,
+ 1,
+ 1.733,
+ 1,
+ 1.933,
+ -1,
+ 2.133,
+ -1,
+ 1,
+ 2.411,
+ -1,
+ 2.689,
+ 1,
+ 2.967,
+ 1,
+ 1,
+ 3.111,
+ 1,
+ 3.256,
+ -0.6,
+ 3.4,
+ -0.6,
+ 1,
+ 3.5,
+ -0.6,
+ 3.6,
+ 0.8,
+ 3.7,
+ 0.8,
+ 0,
+ 4.2,
+ 0.8
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairAhoge",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.122,
+ 0,
+ 0.244,
+ -3.127,
+ 0.367,
+ -4,
+ 1,
+ 0.511,
+ -5.032,
+ 0.656,
+ -5,
+ 0.8,
+ -5,
+ 1,
+ 0.9,
+ -5,
+ 1,
+ 10,
+ 1.1,
+ 10,
+ 1,
+ 1.267,
+ 10,
+ 1.439,
+ 10,
+ 1.6,
+ 8,
+ 1,
+ 1.722,
+ 6.481,
+ 1.844,
+ -10,
+ 1.967,
+ -10,
+ 1,
+ 2.122,
+ -10,
+ 2.31,
+ -10,
+ 2.433,
+ -9.434,
+ 1,
+ 2.589,
+ -8.722,
+ 2.744,
+ 10,
+ 2.9,
+ 10,
+ 1,
+ 3.011,
+ 10,
+ 3.122,
+ -5.646,
+ 3.233,
+ -5.646,
+ 1,
+ 3.367,
+ -5.646,
+ 3.5,
+ 3.825,
+ 3.633,
+ 3.825,
+ 1,
+ 3.767,
+ 3.825,
+ 3.9,
+ 0,
+ 4.033,
+ 0,
+ 0,
+ 4.2,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmA",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 4.2,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmB",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.2,
+ 0
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m04.motion3.json b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m04.motion3.json
new file mode 100644
index 0000000..2f91a79
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m04.motion3.json
@@ -0,0 +1,860 @@
+{
+ "Version": 3,
+ "Meta": {
+ "Duration": 4.43,
+ "Fps": 30.0,
+ "Loop": true,
+ "AreBeziersRestricted": false,
+ "CurveCount": 29,
+ "TotalSegmentCount": 106,
+ "TotalPointCount": 287,
+ "UserDataCount": 0,
+ "TotalUserDataSize": 0
+ },
+ "Curves": [
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleX",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.211,
+ 1,
+ 0.422,
+ 0,
+ 0.633,
+ 0,
+ 1,
+ 0.911,
+ 0,
+ 1.189,
+ 5,
+ 1.467,
+ 5,
+ 1,
+ 1.689,
+ 5,
+ 1.911,
+ -16,
+ 2.133,
+ -16,
+ 1,
+ 2.356,
+ -16,
+ 2.578,
+ 13.871,
+ 2.8,
+ 13.871,
+ 1,
+ 2.956,
+ 13.871,
+ 3.111,
+ 0,
+ 3.267,
+ 0,
+ 0,
+ 4.433,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.211,
+ 0,
+ 0.422,
+ 0,
+ 0.633,
+ 0,
+ 1,
+ 0.911,
+ 0,
+ 1.189,
+ -25,
+ 1.467,
+ -25,
+ 1,
+ 1.689,
+ -25,
+ 1.911,
+ -15.225,
+ 2.133,
+ -11,
+ 1,
+ 2.356,
+ -6.775,
+ 2.578,
+ -5.127,
+ 2.8,
+ -2.5,
+ 1,
+ 2.956,
+ -0.661,
+ 3.111,
+ 0,
+ 3.267,
+ 0,
+ 0,
+ 4.433,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.222,
+ 0,
+ 0.444,
+ 0,
+ 0.667,
+ 0,
+ 1,
+ 0.756,
+ 0,
+ 0.844,
+ -4,
+ 0.933,
+ -4,
+ 1,
+ 1.122,
+ -4,
+ 1.311,
+ 18,
+ 1.5,
+ 18,
+ 1,
+ 1.722,
+ 18,
+ 1.944,
+ -14,
+ 2.167,
+ -14,
+ 1,
+ 2.567,
+ -14,
+ 2.967,
+ -14,
+ 3.367,
+ -14,
+ 1,
+ 3.511,
+ -14,
+ 3.656,
+ -12,
+ 3.8,
+ -12,
+ 0,
+ 4.433,
+ -12
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamCheek",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 4.433,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLOpen",
+ "Segments": [
+ 0,
+ 1.2,
+ 1,
+ 0.389,
+ 1.2,
+ 0.778,
+ 1.148,
+ 1.167,
+ 1,
+ 1,
+ 1.211,
+ 0.983,
+ 1.256,
+ 0,
+ 1.3,
+ 0,
+ 1,
+ 1.322,
+ 0,
+ 1.344,
+ 0,
+ 1.367,
+ 0,
+ 1,
+ 1.422,
+ 0,
+ 1.478,
+ 1,
+ 1.533,
+ 1,
+ 1,
+ 1.944,
+ 1,
+ 2.356,
+ 1,
+ 2.767,
+ 1,
+ 1,
+ 2.811,
+ 1,
+ 2.856,
+ 0,
+ 2.9,
+ 0,
+ 1,
+ 2.922,
+ 0,
+ 2.944,
+ 0,
+ 2.967,
+ 0,
+ 1,
+ 3.022,
+ 0,
+ 3.078,
+ 1,
+ 3.133,
+ 1,
+ 0,
+ 4.433,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLSmile",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.433,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeROpen",
+ "Segments": [
+ 0,
+ 1.2,
+ 1,
+ 0.389,
+ 1.2,
+ 0.778,
+ 1.148,
+ 1.167,
+ 1,
+ 1,
+ 1.211,
+ 0.983,
+ 1.256,
+ 0,
+ 1.3,
+ 0,
+ 1,
+ 1.322,
+ 0,
+ 1.344,
+ 0,
+ 1.367,
+ 0,
+ 1,
+ 1.422,
+ 0,
+ 1.478,
+ 1,
+ 1.533,
+ 1,
+ 1,
+ 1.944,
+ 1,
+ 2.356,
+ 1,
+ 2.767,
+ 1,
+ 1,
+ 2.811,
+ 1,
+ 2.856,
+ 0,
+ 2.9,
+ 0,
+ 1,
+ 2.922,
+ 0,
+ 2.944,
+ 0,
+ 2.967,
+ 0,
+ 1,
+ 3.022,
+ 0,
+ 3.078,
+ 1,
+ 3.133,
+ 1,
+ 0,
+ 4.433,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRSmile",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.433,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.211,
+ 0,
+ 0.422,
+ 0,
+ 0.633,
+ 0,
+ 1,
+ 0.911,
+ 0,
+ 1.189,
+ -0.44,
+ 1.467,
+ -0.44,
+ 1,
+ 1.689,
+ -0.44,
+ 1.911,
+ 0.79,
+ 2.133,
+ 0.79,
+ 1,
+ 2.511,
+ 0.79,
+ 2.889,
+ 0,
+ 3.267,
+ 0,
+ 0,
+ 4.433,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.211,
+ 0,
+ 0.422,
+ 0,
+ 0.633,
+ 0,
+ 1,
+ 0.911,
+ 0,
+ 1.189,
+ -1,
+ 1.467,
+ -1,
+ 1,
+ 1.689,
+ -1,
+ 1.911,
+ -1,
+ 2.133,
+ -1,
+ 1,
+ 2.511,
+ -1,
+ 2.889,
+ 0,
+ 3.267,
+ 0,
+ 0,
+ 4.433,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLY",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.544,
+ 1,
+ 1.089,
+ 1,
+ 1.633,
+ 1,
+ 1,
+ 1.856,
+ 1,
+ 2.078,
+ 0,
+ 2.3,
+ 0,
+ 1,
+ 2.5,
+ 0,
+ 2.7,
+ 1,
+ 2.9,
+ 1,
+ 0,
+ 4.433,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRY",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.544,
+ 1,
+ 1.089,
+ 1,
+ 1.633,
+ 1,
+ 1,
+ 1.856,
+ 1,
+ 2.078,
+ 0,
+ 2.3,
+ 0,
+ 1,
+ 2.5,
+ 0,
+ 2.7,
+ 1,
+ 2.9,
+ 1,
+ 0,
+ 4.433,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLX",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.433,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRX",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.433,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLAngle",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.433,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRAngle",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.433,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm",
+ "Segments": [
+ 0,
+ -1,
+ 0,
+ 4.433,
+ -1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm",
+ "Segments": [
+ 0,
+ -1,
+ 0,
+ 4.433,
+ -1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.244,
+ 0,
+ 0.489,
+ 0,
+ 0.733,
+ 0,
+ 1,
+ 0.933,
+ 0,
+ 1.133,
+ -7,
+ 1.333,
+ -7,
+ 1,
+ 1.644,
+ -7,
+ 1.956,
+ 0,
+ 2.267,
+ 0,
+ 0,
+ 4.433,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.244,
+ 0,
+ 0.489,
+ 0,
+ 0.733,
+ 0,
+ 0,
+ 4.433,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ",
+ "Segments": [
+ 0,
+ 2,
+ 1,
+ 0.233,
+ 2,
+ 0.467,
+ 0,
+ 0.7,
+ 0,
+ 1,
+ 0.733,
+ 0,
+ 0.767,
+ 0,
+ 0.8,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1.2,
+ -4,
+ 1.4,
+ -4,
+ 1,
+ 1.711,
+ -4,
+ 2.022,
+ 5,
+ 2.333,
+ 5,
+ 1,
+ 2.567,
+ 5,
+ 2.8,
+ 3.64,
+ 3.033,
+ 0,
+ 1,
+ 3.133,
+ -1.56,
+ 3.233,
+ -3,
+ 3.333,
+ -3,
+ 1,
+ 3.467,
+ -3,
+ 3.6,
+ -2,
+ 3.733,
+ -2,
+ 0,
+ 4.433,
+ -2
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBreath",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.189,
+ 0,
+ 0.378,
+ 1,
+ 0.567,
+ 1,
+ 1,
+ 0.711,
+ 1,
+ 0.856,
+ 0,
+ 1,
+ 0,
+ 1,
+ 1.222,
+ 0,
+ 1.444,
+ 1,
+ 1.667,
+ 1,
+ 1,
+ 1.889,
+ 1,
+ 2.111,
+ 0,
+ 2.333,
+ 0,
+ 1,
+ 2.544,
+ 0,
+ 2.756,
+ 1,
+ 2.967,
+ 1,
+ 1,
+ 3.167,
+ 1,
+ 3.367,
+ 0,
+ 3.567,
+ 0,
+ 0,
+ 4.433,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamShoulder",
+ "Segments": [
+ 0,
+ 0.1,
+ 1,
+ 0.467,
+ 0.1,
+ 0.933,
+ 1,
+ 1.4,
+ 1,
+ 1,
+ 1.844,
+ 1,
+ 2.289,
+ 1,
+ 2.733,
+ 1,
+ 1,
+ 2.967,
+ 1,
+ 3.2,
+ -1,
+ 3.433,
+ -1,
+ 0,
+ 4.433,
+ -1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamLeg",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 4.433,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmLA",
+ "Segments": [
+ 0,
+ -10,
+ 0,
+ 4.433,
+ -10
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmRA",
+ "Segments": [
+ 0,
+ -10,
+ 0,
+ 4.433,
+ -10
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairAhoge",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.3,
+ 0,
+ 0.6,
+ 0,
+ 0.9,
+ -0.012,
+ 1,
+ 1.067,
+ -0.019,
+ 1.233,
+ -6.827,
+ 1.4,
+ -6.827,
+ 1,
+ 1.511,
+ -6.827,
+ 1.622,
+ 7.958,
+ 1.733,
+ 7.958,
+ 1,
+ 1.944,
+ 7.958,
+ 2.156,
+ -7.565,
+ 2.367,
+ -7.565,
+ 1,
+ 2.5,
+ -7.565,
+ 2.633,
+ 9.434,
+ 2.767,
+ 9.434,
+ 1,
+ 2.978,
+ 9.434,
+ 3.189,
+ -8.871,
+ 3.4,
+ -8.871,
+ 1,
+ 3.5,
+ -8.871,
+ 3.6,
+ 7.588,
+ 3.7,
+ 7.588,
+ 1,
+ 3.789,
+ 7.588,
+ 3.878,
+ -3.904,
+ 3.967,
+ -3.904,
+ 1,
+ 4.011,
+ -3.904,
+ 4.056,
+ -0.032,
+ 4.1,
+ -0.032,
+ 0,
+ 4.433,
+ -0.032
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmA",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 4.43,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmB",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.43,
+ 0
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m05.motion3.json b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m05.motion3.json
new file mode 100644
index 0000000..faea724
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m05.motion3.json
@@ -0,0 +1,1543 @@
+{
+ "Version": 3,
+ "Meta": {
+ "Duration": 8.57,
+ "Fps": 30.0,
+ "Loop": true,
+ "AreBeziersRestricted": false,
+ "CurveCount": 30,
+ "TotalSegmentCount": 218,
+ "TotalPointCount": 622,
+ "UserDataCount": 0,
+ "TotalUserDataSize": 0
+ },
+ "Curves": [
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.189,
+ 0,
+ 0.378,
+ 1,
+ 0.567,
+ 1,
+ 1,
+ 1.122,
+ 1,
+ 1.678,
+ 1,
+ 2.233,
+ 1,
+ 1,
+ 2.533,
+ 1,
+ 2.833,
+ -30,
+ 3.133,
+ -30,
+ 1,
+ 4.067,
+ -30,
+ 5,
+ -30,
+ 5.933,
+ -30,
+ 1,
+ 6.322,
+ -30,
+ 6.711,
+ 0,
+ 7.1,
+ 0,
+ 0,
+ 8.567,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.189,
+ 0,
+ 0.378,
+ 18,
+ 0.567,
+ 18,
+ 1,
+ 0.856,
+ 18,
+ 1.144,
+ -9.95,
+ 1.433,
+ -9.95,
+ 1,
+ 1.522,
+ -9.95,
+ 1.611,
+ -1.589,
+ 1.7,
+ -1.589,
+ 1,
+ 1.911,
+ -1.589,
+ 2.122,
+ -8.047,
+ 2.333,
+ -8.047,
+ 1,
+ 2.6,
+ -8.047,
+ 2.867,
+ 30,
+ 3.133,
+ 30,
+ 1,
+ 3.478,
+ 30,
+ 3.822,
+ 29.529,
+ 4.167,
+ 27.847,
+ 1,
+ 4.311,
+ 27.142,
+ 4.456,
+ 25.42,
+ 4.6,
+ 24.243,
+ 1,
+ 4.922,
+ 21.616,
+ 5.244,
+ 20.338,
+ 5.567,
+ 20.338,
+ 1,
+ 5.789,
+ 20.338,
+ 6.011,
+ 27.397,
+ 6.233,
+ 27.397,
+ 1,
+ 6.522,
+ 27.397,
+ 6.811,
+ 0,
+ 7.1,
+ 0,
+ 0,
+ 8.567,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.189,
+ 0,
+ 0.378,
+ 0,
+ 0.567,
+ 0,
+ 1,
+ 0.8,
+ 0,
+ 1.033,
+ 25,
+ 1.267,
+ 25,
+ 1,
+ 1.4,
+ 25,
+ 1.533,
+ 25,
+ 1.667,
+ 25,
+ 1,
+ 1.878,
+ 25,
+ 2.089,
+ 25,
+ 2.3,
+ 25,
+ 1,
+ 2.578,
+ 25,
+ 2.856,
+ -30,
+ 3.133,
+ -30,
+ 1,
+ 4,
+ -30,
+ 4.867,
+ -30,
+ 5.733,
+ -30,
+ 1,
+ 5.8,
+ -30,
+ 5.867,
+ -30,
+ 5.933,
+ -30,
+ 1,
+ 6.322,
+ -30,
+ 6.711,
+ 0,
+ 7.1,
+ 0,
+ 0,
+ 8.567,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamCheek",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.2,
+ 0,
+ 0.4,
+ 0,
+ 0.6,
+ 0,
+ 1,
+ 2.844,
+ 0,
+ 5.089,
+ 0,
+ 7.333,
+ 0,
+ 0,
+ 8.567,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLOpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.278,
+ 1,
+ 0.556,
+ 1,
+ 0.833,
+ 1,
+ 1,
+ 0.867,
+ 1,
+ 0.9,
+ 0,
+ 0.933,
+ 0,
+ 1,
+ 1.222,
+ 0,
+ 1.511,
+ 0,
+ 1.8,
+ 0,
+ 1,
+ 1.889,
+ 0,
+ 1.978,
+ 1,
+ 2.067,
+ 1,
+ 1,
+ 2.322,
+ 1,
+ 2.578,
+ 1,
+ 2.833,
+ 1,
+ 1,
+ 2.889,
+ 1,
+ 2.944,
+ 0,
+ 3,
+ 0,
+ 1,
+ 3.022,
+ 0,
+ 3.044,
+ 0,
+ 3.067,
+ 0,
+ 1,
+ 3.122,
+ 0,
+ 3.178,
+ 1,
+ 3.233,
+ 1,
+ 1,
+ 3.5,
+ 1,
+ 3.767,
+ 1,
+ 4.033,
+ 1,
+ 1,
+ 4.089,
+ 1,
+ 4.144,
+ 0,
+ 4.2,
+ 0,
+ 1,
+ 4.222,
+ 0,
+ 4.244,
+ 0,
+ 4.267,
+ 0,
+ 1,
+ 4.322,
+ 0,
+ 4.378,
+ 1,
+ 4.433,
+ 1,
+ 1,
+ 5.133,
+ 1,
+ 5.833,
+ 1,
+ 6.533,
+ 1,
+ 1,
+ 6.589,
+ 1,
+ 6.644,
+ 0,
+ 6.7,
+ 0,
+ 1,
+ 6.722,
+ 0,
+ 6.744,
+ 0,
+ 6.767,
+ 0,
+ 1,
+ 6.822,
+ 0,
+ 6.878,
+ 1,
+ 6.933,
+ 1,
+ 1,
+ 7.067,
+ 1,
+ 7.2,
+ 1,
+ 7.333,
+ 1,
+ 0,
+ 8.567,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.278,
+ 0,
+ 0.556,
+ 0.812,
+ 0.833,
+ 0.812,
+ 0,
+ 8.567,
+ 0.812
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeROpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.278,
+ 1,
+ 0.556,
+ 1,
+ 0.833,
+ 1,
+ 1,
+ 0.867,
+ 1,
+ 0.9,
+ 0,
+ 0.933,
+ 0,
+ 1,
+ 1.222,
+ 0,
+ 1.511,
+ 0,
+ 1.8,
+ 0,
+ 1,
+ 1.889,
+ 0,
+ 1.978,
+ 1,
+ 2.067,
+ 1,
+ 1,
+ 2.322,
+ 1,
+ 2.578,
+ 1,
+ 2.833,
+ 1,
+ 1,
+ 2.889,
+ 1,
+ 2.944,
+ 0,
+ 3,
+ 0,
+ 1,
+ 3.022,
+ 0,
+ 3.044,
+ 0,
+ 3.067,
+ 0,
+ 1,
+ 3.122,
+ 0,
+ 3.178,
+ 1,
+ 3.233,
+ 1,
+ 1,
+ 3.5,
+ 1,
+ 3.767,
+ 1,
+ 4.033,
+ 1,
+ 1,
+ 4.089,
+ 1,
+ 4.144,
+ 0,
+ 4.2,
+ 0,
+ 1,
+ 4.222,
+ 0,
+ 4.244,
+ 0,
+ 4.267,
+ 0,
+ 1,
+ 4.322,
+ 0,
+ 4.378,
+ 1,
+ 4.433,
+ 1,
+ 1,
+ 5.133,
+ 1,
+ 5.833,
+ 1,
+ 6.533,
+ 1,
+ 1,
+ 6.589,
+ 1,
+ 6.644,
+ 0,
+ 6.7,
+ 0,
+ 1,
+ 6.722,
+ 0,
+ 6.744,
+ 0,
+ 6.767,
+ 0,
+ 1,
+ 6.822,
+ 0,
+ 6.878,
+ 1,
+ 6.933,
+ 1,
+ 1,
+ 7.067,
+ 1,
+ 7.2,
+ 1,
+ 7.333,
+ 1,
+ 0,
+ 8.567,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.278,
+ 0,
+ 0.556,
+ 0.812,
+ 0.833,
+ 0.812,
+ 1,
+ 1.967,
+ 0.812,
+ 3.1,
+ 0.8,
+ 4.233,
+ 0.8,
+ 0,
+ 8.567,
+ 0.8
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.2,
+ 0,
+ 0.4,
+ -0.065,
+ 0.6,
+ -0.14,
+ 1,
+ 0.811,
+ -0.219,
+ 1.022,
+ -0.24,
+ 1.233,
+ -0.24,
+ 1,
+ 1.367,
+ -0.24,
+ 1.5,
+ -0.24,
+ 1.633,
+ -0.24,
+ 1,
+ 1.844,
+ -0.24,
+ 2.056,
+ -0.24,
+ 2.267,
+ -0.24,
+ 1,
+ 2.556,
+ -0.24,
+ 2.844,
+ 0.661,
+ 3.133,
+ 0.68,
+ 1,
+ 3.5,
+ 0.704,
+ 3.867,
+ 0.7,
+ 4.233,
+ 0.7,
+ 1,
+ 4.5,
+ 0.7,
+ 4.767,
+ -0.75,
+ 5.033,
+ -0.75,
+ 1,
+ 5.589,
+ -0.75,
+ 6.144,
+ 0.68,
+ 6.7,
+ 0.68,
+ 1,
+ 6.789,
+ 0.68,
+ 6.878,
+ 0,
+ 6.967,
+ 0,
+ 0,
+ 8.567,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.2,
+ 0,
+ 0.4,
+ -0.38,
+ 0.6,
+ -0.38,
+ 1,
+ 0.811,
+ -0.38,
+ 1.022,
+ 0.21,
+ 1.233,
+ 0.21,
+ 1,
+ 1.367,
+ 0.21,
+ 1.5,
+ 0.21,
+ 1.633,
+ 0.21,
+ 1,
+ 1.844,
+ 0.21,
+ 2.056,
+ 0.21,
+ 2.267,
+ 0.21,
+ 1,
+ 2.556,
+ 0.21,
+ 2.844,
+ -0.33,
+ 3.133,
+ -0.33,
+ 1,
+ 3.767,
+ -0.33,
+ 4.4,
+ 0.625,
+ 5.033,
+ 0.625,
+ 1,
+ 5.589,
+ 0.625,
+ 6.144,
+ -0.33,
+ 6.7,
+ -0.33,
+ 1,
+ 6.789,
+ -0.33,
+ 6.878,
+ 0,
+ 6.967,
+ 0,
+ 0,
+ 8.567,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.2,
+ 0,
+ 0.4,
+ 0,
+ 0.6,
+ 0,
+ 1,
+ 0.656,
+ 0,
+ 0.711,
+ 0,
+ 0.767,
+ 0,
+ 1,
+ 0.822,
+ 0,
+ 0.878,
+ 0.667,
+ 0.933,
+ 0.667,
+ 1,
+ 1.211,
+ 0.667,
+ 1.489,
+ 0.667,
+ 1.767,
+ 0.667,
+ 1,
+ 2.011,
+ 0.667,
+ 2.256,
+ 0,
+ 2.5,
+ 0,
+ 0,
+ 8.567,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.2,
+ 0,
+ 0.4,
+ 0,
+ 0.6,
+ 0,
+ 1,
+ 0.656,
+ 0,
+ 0.711,
+ 0,
+ 0.767,
+ 0,
+ 1,
+ 0.822,
+ 0,
+ 0.878,
+ 0.646,
+ 0.933,
+ 0.646,
+ 1,
+ 1.211,
+ 0.646,
+ 1.489,
+ 0.646,
+ 1.767,
+ 0.646,
+ 1,
+ 2.011,
+ 0.646,
+ 2.256,
+ 0,
+ 2.5,
+ 0,
+ 0,
+ 8.567,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.2,
+ 0,
+ 0.4,
+ 0,
+ 0.6,
+ 0,
+ 0,
+ 8.567,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.2,
+ 0,
+ 0.4,
+ 0,
+ 0.6,
+ 0,
+ 0,
+ 8.567,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.2,
+ 0,
+ 0.4,
+ 0,
+ 0.6,
+ 0,
+ 0,
+ 8.567,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.2,
+ 0,
+ 0.4,
+ 0,
+ 0.6,
+ 0,
+ 0,
+ 8.567,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.2,
+ 0,
+ 0.4,
+ 0,
+ 0.6,
+ 0,
+ 0,
+ 8.567,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.2,
+ 0,
+ 0.4,
+ 0,
+ 0.6,
+ 0,
+ 0,
+ 8.567,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.133,
+ 0,
+ 0.267,
+ 0,
+ 0.4,
+ 0,
+ 1,
+ 0.5,
+ 0,
+ 0.6,
+ -1.353,
+ 0.7,
+ -1.353,
+ 1,
+ 0.933,
+ -1.353,
+ 1.167,
+ 10,
+ 1.4,
+ 10,
+ 1,
+ 1.544,
+ 10,
+ 1.689,
+ 9.799,
+ 1.833,
+ 9.8,
+ 1,
+ 2.089,
+ 9.799,
+ 2.344,
+ 9.875,
+ 2.6,
+ 9.875,
+ 1,
+ 2.944,
+ 9.875,
+ 3.289,
+ -9.599,
+ 3.633,
+ -9.599,
+ 1,
+ 3.844,
+ -9.599,
+ 4.056,
+ -2.657,
+ 4.267,
+ -2.657,
+ 1,
+ 4.467,
+ -2.657,
+ 4.667,
+ -9.098,
+ 4.867,
+ -9.098,
+ 1,
+ 5.144,
+ -9.098,
+ 5.422,
+ -4.812,
+ 5.7,
+ -4.812,
+ 1,
+ 5.967,
+ -4.812,
+ 6.233,
+ -10,
+ 6.5,
+ -10,
+ 1,
+ 6.778,
+ -10,
+ 7.056,
+ -1.43,
+ 7.333,
+ 0,
+ 1,
+ 7.533,
+ 1.03,
+ 7.733,
+ 0.752,
+ 7.933,
+ 0.752,
+ 1,
+ 8.011,
+ 0.752,
+ 8.089,
+ -0.326,
+ 8.167,
+ -0.326,
+ 0,
+ 8.567,
+ -0.326
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.2,
+ 0,
+ 0.4,
+ 5.421,
+ 0.6,
+ 5.421,
+ 1,
+ 0.867,
+ 5.421,
+ 1.133,
+ -3.737,
+ 1.4,
+ -3.737,
+ 1,
+ 1.511,
+ -3.737,
+ 1.622,
+ 0.246,
+ 1.733,
+ 0.246,
+ 1,
+ 1.933,
+ 0.246,
+ 2.133,
+ 0,
+ 2.333,
+ 0,
+ 1,
+ 2.389,
+ 0,
+ 2.444,
+ 2.993,
+ 2.5,
+ 4,
+ 1,
+ 2.711,
+ 7.827,
+ 2.922,
+ 9,
+ 3.133,
+ 9,
+ 1,
+ 4.256,
+ 9,
+ 5.378,
+ 9,
+ 6.5,
+ 9,
+ 1,
+ 6.778,
+ 9,
+ 7.056,
+ -5,
+ 7.333,
+ -5,
+ 1,
+ 7.5,
+ -5,
+ 7.667,
+ 2,
+ 7.833,
+ 2,
+ 1,
+ 7.9,
+ 2,
+ 7.967,
+ 0,
+ 8.033,
+ 0,
+ 0,
+ 8.567,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.2,
+ 0,
+ 0.4,
+ 2,
+ 0.6,
+ 2,
+ 1,
+ 0.867,
+ 2,
+ 1.133,
+ -10,
+ 1.4,
+ -10,
+ 1,
+ 1.533,
+ -10,
+ 1.667,
+ -10,
+ 1.8,
+ -10,
+ 1,
+ 2,
+ -10,
+ 2.211,
+ -10,
+ 2.4,
+ -6.566,
+ 1,
+ 2.6,
+ -2.936,
+ 2.8,
+ 10,
+ 3,
+ 10,
+ 1,
+ 3.433,
+ 10,
+ 3.867,
+ -4.436,
+ 4.3,
+ -4.436,
+ 1,
+ 4.578,
+ -4.436,
+ 4.856,
+ 10,
+ 5.133,
+ 10,
+ 1,
+ 5.433,
+ 10,
+ 5.733,
+ 0.695,
+ 6.033,
+ -4.261,
+ 1,
+ 6.211,
+ -7.197,
+ 6.389,
+ -6.792,
+ 6.567,
+ -6.792,
+ 1,
+ 6.644,
+ -6.792,
+ 6.722,
+ -6.224,
+ 6.8,
+ -4.11,
+ 1,
+ 6.989,
+ 1.023,
+ 7.178,
+ 4.236,
+ 7.367,
+ 4.236,
+ 1,
+ 7.689,
+ 4.236,
+ 8.011,
+ 0.025,
+ 8.333,
+ 0.025,
+ 0,
+ 8.567,
+ 0.025
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBreath",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.122,
+ 0,
+ 0.244,
+ 1,
+ 0.367,
+ 1,
+ 1,
+ 0.511,
+ 1,
+ 0.656,
+ 0,
+ 0.8,
+ 0,
+ 1,
+ 1.022,
+ 0,
+ 1.244,
+ 1,
+ 1.467,
+ 1,
+ 1,
+ 1.689,
+ 1,
+ 1.911,
+ 0,
+ 2.133,
+ 0,
+ 1,
+ 2.344,
+ 0,
+ 2.556,
+ 1,
+ 2.767,
+ 1,
+ 1,
+ 2.967,
+ 1,
+ 3.167,
+ 0,
+ 3.367,
+ 0,
+ 1,
+ 3.578,
+ 0,
+ 3.789,
+ 1,
+ 4,
+ 1,
+ 1,
+ 4.233,
+ 1,
+ 4.467,
+ 0,
+ 4.7,
+ 0,
+ 1,
+ 4.9,
+ 0,
+ 5.1,
+ 0,
+ 5.3,
+ 0,
+ 1,
+ 5.522,
+ 0,
+ 5.744,
+ 1,
+ 5.967,
+ 1,
+ 1,
+ 6.244,
+ 1,
+ 6.522,
+ 0,
+ 6.8,
+ 0,
+ 1,
+ 7.022,
+ 0,
+ 7.244,
+ 1,
+ 7.467,
+ 1,
+ 1,
+ 7.689,
+ 1,
+ 7.911,
+ 0,
+ 8.133,
+ 0,
+ 0,
+ 8.567,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamShoulder",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 8.567,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamLeg",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 8.567,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmLA",
+ "Segments": [
+ 0,
+ -10,
+ 1,
+ 0.2,
+ -10,
+ 0.4,
+ -10,
+ 0.6,
+ -10,
+ 1,
+ 0.833,
+ -10,
+ 1.067,
+ -7.083,
+ 1.3,
+ -7.083,
+ 1,
+ 1.433,
+ -7.083,
+ 1.567,
+ -7.083,
+ 1.7,
+ -7.083,
+ 1,
+ 1.911,
+ -7.083,
+ 2.122,
+ -10,
+ 2.333,
+ -10,
+ 1,
+ 3.467,
+ -10,
+ 4.6,
+ -10,
+ 5.733,
+ -10,
+ 1,
+ 5.8,
+ -10,
+ 5.867,
+ -10,
+ 5.933,
+ -10,
+ 1,
+ 6.4,
+ -10,
+ 6.867,
+ -10,
+ 7.333,
+ -10,
+ 0,
+ 8.567,
+ -10
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmRA",
+ "Segments": [
+ 0,
+ -10,
+ 1,
+ 0.2,
+ -10,
+ 0.4,
+ -10,
+ 0.6,
+ -10,
+ 1,
+ 0.833,
+ -10,
+ 1.067,
+ 2.917,
+ 1.3,
+ 2.917,
+ 1,
+ 1.478,
+ 2.917,
+ 1.656,
+ 3.029,
+ 1.833,
+ 2.6,
+ 1,
+ 2.033,
+ 2.117,
+ 2.233,
+ -10,
+ 2.433,
+ -10,
+ 1,
+ 3.533,
+ -10,
+ 4.633,
+ -10,
+ 5.733,
+ -10,
+ 1,
+ 5.8,
+ -10,
+ 5.867,
+ -10,
+ 5.933,
+ -10,
+ 1,
+ 6.133,
+ -10,
+ 6.333,
+ -10,
+ 6.533,
+ -10,
+ 1,
+ 6.8,
+ -10,
+ 7.067,
+ -10,
+ 7.333,
+ -10,
+ 0,
+ 8.567,
+ -10
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmRB",
+ "Segments": [
+ 0,
+ 0.02,
+ 0,
+ 8.567,
+ 0.02
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairAhoge",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.178,
+ 0,
+ 0.356,
+ -10,
+ 0.533,
+ -10,
+ 1,
+ 0.833,
+ -10,
+ 1.133,
+ 10,
+ 1.433,
+ 10,
+ 1,
+ 1.567,
+ 10,
+ 1.7,
+ 0,
+ 1.833,
+ 0,
+ 1,
+ 2.311,
+ 0,
+ 2.789,
+ 10,
+ 3.267,
+ 10,
+ 1,
+ 3.344,
+ 10,
+ 3.422,
+ 10,
+ 3.5,
+ 10,
+ 1,
+ 3.633,
+ 10,
+ 3.767,
+ -10,
+ 3.9,
+ -10,
+ 1,
+ 4.267,
+ -10,
+ 4.633,
+ 7.69,
+ 5,
+ 9.705,
+ 1,
+ 5.054,
+ 10,
+ 5.244,
+ 10,
+ 5.367,
+ 10,
+ 1,
+ 5.756,
+ 10,
+ 6.144,
+ -9.975,
+ 6.533,
+ -9.975,
+ 1,
+ 6.622,
+ -9.975,
+ 6.754,
+ -10,
+ 6.8,
+ -9.779,
+ 1,
+ 7.033,
+ -8.654,
+ 7.267,
+ 10,
+ 7.5,
+ 10,
+ 1,
+ 7.7,
+ 10,
+ 7.9,
+ -6.753,
+ 8.1,
+ -6.753,
+ 1,
+ 8.2,
+ -6.753,
+ 8.3,
+ 0,
+ 8.4,
+ 0,
+ 0,
+ 8.567,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmA",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 8.57,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmB",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 8.57,
+ 0
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m06.motion3.json b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m06.motion3.json
new file mode 100644
index 0000000..73302c3
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m06.motion3.json
@@ -0,0 +1,1947 @@
+{
+ "Version": 3,
+ "Meta": {
+ "Duration": 5.37,
+ "Fps": 30.0,
+ "Loop": true,
+ "AreBeziersRestricted": false,
+ "CurveCount": 33,
+ "TotalSegmentCount": 262,
+ "TotalPointCount": 751,
+ "UserDataCount": 0,
+ "TotalUserDataSize": 0
+ },
+ "Curves": [
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 1,
+ 0.333,
+ 1,
+ 1,
+ 0.7,
+ 1,
+ 1.067,
+ 0,
+ 1.433,
+ 0,
+ 1,
+ 1.711,
+ 0,
+ 1.989,
+ 10,
+ 2.267,
+ 10,
+ 1,
+ 2.522,
+ 10,
+ 2.778,
+ -7,
+ 3.033,
+ -7,
+ 1,
+ 3.233,
+ -7,
+ 3.433,
+ 13,
+ 3.633,
+ 13,
+ 1,
+ 3.811,
+ 13,
+ 3.989,
+ 7.017,
+ 4.167,
+ 2,
+ 1,
+ 4.244,
+ -0.195,
+ 4.322,
+ 0,
+ 4.4,
+ 0,
+ 0,
+ 5.367,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 18,
+ 0.333,
+ 18,
+ 1,
+ 0.522,
+ 18,
+ 0.711,
+ -9.95,
+ 0.9,
+ -9.95,
+ 1,
+ 0.956,
+ -9.95,
+ 1.011,
+ -1.664,
+ 1.067,
+ -1.589,
+ 1,
+ 1.911,
+ -0.462,
+ 2.756,
+ 0,
+ 3.6,
+ 0,
+ 1,
+ 3.789,
+ 0,
+ 3.978,
+ -18,
+ 4.167,
+ -18,
+ 1,
+ 4.244,
+ -18,
+ 4.322,
+ -10,
+ 4.4,
+ -10,
+ 0,
+ 5.367,
+ -10
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 1,
+ 0.489,
+ 0,
+ 0.644,
+ 25,
+ 0.8,
+ 25,
+ 1,
+ 0.878,
+ 25,
+ 0.956,
+ 25,
+ 1.033,
+ 25,
+ 1,
+ 1.233,
+ 25,
+ 1.433,
+ 25,
+ 1.633,
+ 25,
+ 1,
+ 1.867,
+ 25,
+ 2.1,
+ -5,
+ 2.333,
+ -5,
+ 1,
+ 2.533,
+ -5,
+ 2.733,
+ 23,
+ 2.933,
+ 23,
+ 1,
+ 3.189,
+ 23,
+ 3.444,
+ -3,
+ 3.7,
+ -3,
+ 0,
+ 5.367,
+ -3
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamCheek",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.122,
+ 0,
+ 0.244,
+ 0,
+ 0.367,
+ 0,
+ 0,
+ 5.367,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLOpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.178,
+ 1,
+ 0.356,
+ 1,
+ 0.533,
+ 1,
+ 1,
+ 0.567,
+ 1,
+ 0.6,
+ 0,
+ 0.633,
+ 0,
+ 1,
+ 0.644,
+ 0,
+ 0.656,
+ 0,
+ 0.667,
+ 0,
+ 1,
+ 0.7,
+ 0,
+ 0.733,
+ 1,
+ 0.767,
+ 1,
+ 1,
+ 1.056,
+ 1,
+ 1.344,
+ 1,
+ 1.633,
+ 1,
+ 1,
+ 1.667,
+ 1,
+ 1.7,
+ 0,
+ 1.733,
+ 0,
+ 1,
+ 1.744,
+ 0,
+ 1.756,
+ 0,
+ 1.767,
+ 0,
+ 1,
+ 1.8,
+ 0,
+ 1.833,
+ 1,
+ 1.867,
+ 1,
+ 1,
+ 2.433,
+ 1,
+ 3,
+ 1,
+ 3.567,
+ 1,
+ 1,
+ 3.6,
+ 1,
+ 3.633,
+ 0,
+ 3.667,
+ 0,
+ 1,
+ 3.678,
+ 0,
+ 3.689,
+ 0,
+ 3.7,
+ 0,
+ 1,
+ 3.733,
+ 0,
+ 3.767,
+ 1,
+ 3.8,
+ 1,
+ 1,
+ 3.944,
+ 1,
+ 4.089,
+ 1,
+ 4.233,
+ 1,
+ 1,
+ 4.256,
+ 1,
+ 4.278,
+ 0,
+ 4.3,
+ 0,
+ 0,
+ 5.367,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 1.378,
+ 0,
+ 2.756,
+ 0,
+ 4.133,
+ 0,
+ 1,
+ 4.167,
+ 0,
+ 4.2,
+ 0.833,
+ 4.233,
+ 0.833,
+ 0,
+ 5.367,
+ 0.833
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeROpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.178,
+ 1,
+ 0.356,
+ 1,
+ 0.533,
+ 1,
+ 1,
+ 0.567,
+ 1,
+ 0.6,
+ 0,
+ 0.633,
+ 0,
+ 1,
+ 0.644,
+ 0,
+ 0.656,
+ 0,
+ 0.667,
+ 0,
+ 1,
+ 0.7,
+ 0,
+ 0.733,
+ 1,
+ 0.767,
+ 1,
+ 1,
+ 1.056,
+ 1,
+ 1.344,
+ 1,
+ 1.633,
+ 1,
+ 1,
+ 1.667,
+ 1,
+ 1.7,
+ 0,
+ 1.733,
+ 0,
+ 1,
+ 1.744,
+ 0,
+ 1.756,
+ 0,
+ 1.767,
+ 0,
+ 1,
+ 1.8,
+ 0,
+ 1.833,
+ 1,
+ 1.867,
+ 1,
+ 1,
+ 2.433,
+ 1,
+ 3,
+ 1,
+ 3.567,
+ 1,
+ 1,
+ 3.6,
+ 1,
+ 3.633,
+ 0,
+ 3.667,
+ 0,
+ 1,
+ 3.678,
+ 0,
+ 3.689,
+ 0,
+ 3.7,
+ 0,
+ 1,
+ 3.733,
+ 0,
+ 3.767,
+ 0.998,
+ 3.8,
+ 1,
+ 1,
+ 3.944,
+ 1.01,
+ 4.089,
+ 1.012,
+ 4.233,
+ 1.012,
+ 1,
+ 4.256,
+ 1.012,
+ 4.278,
+ 0,
+ 4.3,
+ 0,
+ 0,
+ 5.367,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 1.378,
+ 0,
+ 2.756,
+ 0,
+ 4.133,
+ 0,
+ 1,
+ 4.167,
+ 0,
+ 4.2,
+ 0.823,
+ 4.233,
+ 0.823,
+ 0,
+ 5.367,
+ 0.823
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.122,
+ 0,
+ 0.244,
+ -0.067,
+ 0.367,
+ -0.14,
+ 1,
+ 0.5,
+ -0.22,
+ 0.633,
+ -0.24,
+ 0.767,
+ -0.24,
+ 1,
+ 0.856,
+ -0.24,
+ 0.944,
+ -0.24,
+ 1.033,
+ -0.24,
+ 1,
+ 1.311,
+ -0.24,
+ 1.589,
+ -0.24,
+ 1.867,
+ -0.25,
+ 1,
+ 2.022,
+ -0.256,
+ 2.178,
+ -0.51,
+ 2.333,
+ -0.51,
+ 1,
+ 2.578,
+ -0.51,
+ 2.822,
+ 0.22,
+ 3.067,
+ 0.22,
+ 1,
+ 3.267,
+ 0.22,
+ 3.467,
+ -0.57,
+ 3.667,
+ -0.57,
+ 1,
+ 3.911,
+ -0.57,
+ 4.156,
+ -0.28,
+ 4.4,
+ -0.28,
+ 0,
+ 5.367,
+ -0.28
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.122,
+ 0,
+ 0.244,
+ -0.38,
+ 0.367,
+ -0.38,
+ 1,
+ 0.5,
+ -0.38,
+ 0.633,
+ 0.21,
+ 0.767,
+ 0.21,
+ 1,
+ 0.856,
+ 0.21,
+ 0.944,
+ 0.21,
+ 1.033,
+ 0.21,
+ 1,
+ 1.311,
+ 0.21,
+ 1.589,
+ 0.29,
+ 1.867,
+ 0.29,
+ 1,
+ 2.022,
+ 0.29,
+ 2.178,
+ 0.21,
+ 2.333,
+ 0.21,
+ 1,
+ 2.578,
+ 0.21,
+ 2.822,
+ 0.29,
+ 3.067,
+ 0.29,
+ 1,
+ 3.267,
+ 0.29,
+ 3.467,
+ 0.08,
+ 3.667,
+ 0.08,
+ 1,
+ 3.911,
+ 0.08,
+ 4.156,
+ 0.13,
+ 4.4,
+ 0.13,
+ 0,
+ 5.367,
+ 0.13
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.078,
+ 0,
+ 0.156,
+ -0.008,
+ 0.233,
+ 0.021,
+ 1,
+ 0.322,
+ 0.053,
+ 0.411,
+ 0.625,
+ 0.5,
+ 0.625,
+ 1,
+ 0.567,
+ 0.625,
+ 0.633,
+ 0,
+ 0.7,
+ 0,
+ 1,
+ 1.144,
+ 0,
+ 1.589,
+ 0,
+ 2.033,
+ 0,
+ 1,
+ 2.089,
+ 0,
+ 2.144,
+ 0.229,
+ 2.2,
+ 0.229,
+ 1,
+ 2.278,
+ 0.229,
+ 2.356,
+ 0,
+ 2.433,
+ 0,
+ 1,
+ 2.544,
+ 0,
+ 2.656,
+ 0,
+ 2.767,
+ 0,
+ 1,
+ 2.822,
+ 0,
+ 2.878,
+ 0.229,
+ 2.933,
+ 0.229,
+ 1,
+ 3.011,
+ 0.229,
+ 3.089,
+ 0,
+ 3.167,
+ 0,
+ 1,
+ 3.456,
+ 0,
+ 3.744,
+ 0,
+ 4.033,
+ 0,
+ 1,
+ 4.133,
+ 0,
+ 4.233,
+ 0.479,
+ 4.333,
+ 0.479,
+ 0,
+ 5.367,
+ 0.479
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.078,
+ 0,
+ 0.156,
+ 0,
+ 0.233,
+ 0,
+ 1,
+ 0.322,
+ 0,
+ 0.411,
+ 0.625,
+ 0.5,
+ 0.625,
+ 1,
+ 0.567,
+ 0.625,
+ 0.633,
+ 0,
+ 0.7,
+ 0,
+ 1,
+ 1.144,
+ 0,
+ 1.589,
+ 0.005,
+ 2.033,
+ 0.021,
+ 1,
+ 2.089,
+ 0.023,
+ 2.144,
+ 0.188,
+ 2.2,
+ 0.188,
+ 1,
+ 2.278,
+ 0.188,
+ 2.356,
+ 0,
+ 2.433,
+ 0,
+ 1,
+ 2.544,
+ 0,
+ 2.656,
+ 0.001,
+ 2.767,
+ 0.021,
+ 1,
+ 2.822,
+ 0.031,
+ 2.878,
+ 0.229,
+ 2.933,
+ 0.229,
+ 1,
+ 3.011,
+ 0.229,
+ 3.089,
+ 0,
+ 3.167,
+ 0,
+ 1,
+ 3.456,
+ 0,
+ 3.744,
+ 0,
+ 4.033,
+ 0,
+ 1,
+ 4.133,
+ 0,
+ 4.233,
+ 0.479,
+ 4.333,
+ 0.479,
+ 0,
+ 5.367,
+ 0.479
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.167,
+ 0,
+ 0.333,
+ 0,
+ 0.5,
+ 0,
+ 1,
+ 1.678,
+ 0,
+ 2.856,
+ 0,
+ 4.033,
+ 0,
+ 1,
+ 4.133,
+ 0,
+ 4.233,
+ -0.479,
+ 4.333,
+ -0.479,
+ 0,
+ 5.367,
+ -0.479
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.122,
+ 0,
+ 0.244,
+ 0,
+ 0.367,
+ 0,
+ 1,
+ 1.589,
+ 0,
+ 2.811,
+ 0,
+ 4.033,
+ 0,
+ 1,
+ 4.133,
+ 0,
+ 4.233,
+ -0.69,
+ 4.333,
+ -0.69,
+ 0,
+ 5.367,
+ -0.69
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.167,
+ 0,
+ 0.333,
+ 0,
+ 0.5,
+ 0,
+ 1,
+ 1.678,
+ 0,
+ 2.856,
+ 0,
+ 4.033,
+ 0,
+ 1,
+ 4.133,
+ 0,
+ 4.233,
+ 0,
+ 4.333,
+ 0,
+ 0,
+ 5.367,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.122,
+ 0,
+ 0.244,
+ 0,
+ 0.367,
+ 0,
+ 1,
+ 1.589,
+ 0,
+ 2.811,
+ 0,
+ 4.033,
+ 0,
+ 1,
+ 4.133,
+ 0,
+ 4.233,
+ 0,
+ 4.333,
+ 0,
+ 0,
+ 5.367,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.122,
+ 0,
+ 0.244,
+ 0,
+ 0.367,
+ 0,
+ 1,
+ 1.589,
+ 0,
+ 2.811,
+ 0,
+ 4.033,
+ 0,
+ 1,
+ 4.133,
+ 0,
+ 4.233,
+ 0,
+ 4.333,
+ 0,
+ 0,
+ 5.367,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.122,
+ 0,
+ 0.244,
+ 0,
+ 0.367,
+ 0,
+ 1,
+ 1.589,
+ 0,
+ 2.811,
+ 0,
+ 4.033,
+ 0,
+ 1,
+ 4.133,
+ 0,
+ 4.233,
+ 0,
+ 4.333,
+ 0,
+ 0,
+ 5.367,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX",
+ "Segments": [
+ 0,
+ -10,
+ 1,
+ 0.078,
+ -10,
+ 0.156,
+ 0,
+ 0.233,
+ 0,
+ 1,
+ 0.311,
+ 0,
+ 0.389,
+ -1.353,
+ 0.467,
+ -1.353,
+ 1,
+ 0.6,
+ -1.353,
+ 0.733,
+ 10,
+ 0.867,
+ 10,
+ 1,
+ 0.956,
+ 10,
+ 1.044,
+ 9.8,
+ 1.133,
+ 9.8,
+ 1,
+ 1.233,
+ 9.8,
+ 1.333,
+ 10,
+ 1.433,
+ 10,
+ 1,
+ 1.667,
+ 10,
+ 1.9,
+ -2.975,
+ 2.133,
+ -2.975,
+ 1,
+ 2.211,
+ -2.975,
+ 2.289,
+ -2.975,
+ 2.367,
+ -2.975,
+ 1,
+ 2.522,
+ -2.975,
+ 2.678,
+ 9.025,
+ 2.833,
+ 9.025,
+ 1,
+ 2.978,
+ 9.025,
+ 3.122,
+ 9.025,
+ 3.267,
+ 9.025,
+ 1,
+ 3.489,
+ 9.025,
+ 3.711,
+ 2.025,
+ 3.933,
+ 2.025,
+ 0,
+ 5.367,
+ 2.025
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY",
+ "Segments": [
+ 0,
+ 10,
+ 1,
+ 0.122,
+ 10,
+ 0.244,
+ 9.141,
+ 0.367,
+ 5.421,
+ 1,
+ 0.533,
+ 0.348,
+ 0.7,
+ -3.737,
+ 0.867,
+ -3.737,
+ 1,
+ 0.978,
+ -3.737,
+ 1.089,
+ 2.181,
+ 1.2,
+ 2.181,
+ 1,
+ 1.3,
+ 2.181,
+ 1.4,
+ 1.948,
+ 1.5,
+ 0.298,
+ 1,
+ 1.6,
+ -1.351,
+ 1.7,
+ -3.396,
+ 1.8,
+ -3.396,
+ 1,
+ 1.944,
+ -3.396,
+ 2.089,
+ 4.673,
+ 2.233,
+ 4.673,
+ 1,
+ 2.378,
+ 4.673,
+ 2.522,
+ -3.858,
+ 2.667,
+ -3.858,
+ 1,
+ 2.767,
+ -3.858,
+ 2.867,
+ 4.794,
+ 2.967,
+ 4.794,
+ 1,
+ 3.089,
+ 4.794,
+ 3.211,
+ -3.573,
+ 3.333,
+ -3.573,
+ 1,
+ 3.533,
+ -3.573,
+ 3.733,
+ 5.269,
+ 3.933,
+ 5.269,
+ 1,
+ 4.044,
+ 5.269,
+ 4.156,
+ -5.393,
+ 4.267,
+ -5.393,
+ 1,
+ 4.356,
+ -5.393,
+ 4.444,
+ 0.095,
+ 4.533,
+ 0.095,
+ 0,
+ 5.367,
+ 0.095
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.122,
+ 0,
+ 0.244,
+ 2,
+ 0.367,
+ 2,
+ 1,
+ 0.533,
+ 2,
+ 0.7,
+ -10,
+ 0.867,
+ -10,
+ 1,
+ 1.078,
+ -10,
+ 1.359,
+ -10,
+ 1.5,
+ -9.975,
+ 1,
+ 1.767,
+ -9.927,
+ 2.033,
+ 2.026,
+ 2.3,
+ 2.026,
+ 1,
+ 2.522,
+ 2.026,
+ 2.744,
+ -9.975,
+ 2.967,
+ -9.975,
+ 1,
+ 3.322,
+ -9.975,
+ 3.678,
+ -6.592,
+ 4.033,
+ 1.8,
+ 1,
+ 4.178,
+ 5.21,
+ 4.322,
+ 8,
+ 4.467,
+ 8,
+ 0,
+ 5.367,
+ 8
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBreath",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.122,
+ 0,
+ 0.244,
+ 1,
+ 0.367,
+ 1,
+ 1,
+ 0.511,
+ 1,
+ 0.656,
+ 0,
+ 0.8,
+ 0,
+ 1,
+ 1.022,
+ 0,
+ 1.244,
+ 1,
+ 1.467,
+ 1,
+ 1,
+ 1.689,
+ 1,
+ 1.911,
+ 0,
+ 2.133,
+ 0,
+ 1,
+ 2.344,
+ 0,
+ 2.556,
+ 1,
+ 2.767,
+ 1,
+ 1,
+ 2.967,
+ 1,
+ 3.167,
+ 0,
+ 3.367,
+ 0,
+ 1,
+ 3.578,
+ 0,
+ 3.789,
+ 1,
+ 4,
+ 1,
+ 1,
+ 4.233,
+ 1,
+ 4.467,
+ 0,
+ 4.7,
+ 0,
+ 0,
+ 5.367,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamShoulder",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.144,
+ 0,
+ 0.289,
+ 1,
+ 0.433,
+ 1,
+ 1,
+ 0.6,
+ 1,
+ 0.767,
+ 0.049,
+ 0.933,
+ -0.1,
+ 1,
+ 1.167,
+ -0.309,
+ 1.4,
+ -0.3,
+ 1.633,
+ -0.3,
+ 1,
+ 1.844,
+ -0.3,
+ 2.056,
+ 0.9,
+ 2.267,
+ 0.9,
+ 1,
+ 2.411,
+ 0.9,
+ 2.556,
+ -0.22,
+ 2.7,
+ -0.22,
+ 1,
+ 2.844,
+ -0.22,
+ 2.989,
+ 0.6,
+ 3.133,
+ 0.6,
+ 1,
+ 3.289,
+ 0.6,
+ 3.444,
+ -0.4,
+ 3.6,
+ -0.4,
+ 1,
+ 3.7,
+ -0.4,
+ 3.8,
+ 0.5,
+ 3.9,
+ 0.5,
+ 1,
+ 4.044,
+ 0.5,
+ 4.189,
+ 0.238,
+ 4.333,
+ 0,
+ 1,
+ 4.444,
+ -0.183,
+ 4.556,
+ -0.2,
+ 4.667,
+ -0.2,
+ 0,
+ 5.367,
+ -0.2
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamLeg",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 1.344,
+ 1,
+ 2.689,
+ 1,
+ 4.033,
+ 1,
+ 1,
+ 4.144,
+ 1,
+ 4.256,
+ 0,
+ 4.367,
+ 0,
+ 1,
+ 4.578,
+ 0,
+ 4.789,
+ 0.052,
+ 5,
+ 0.052,
+ 0,
+ 5.367,
+ 0.052
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmLB",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.1,
+ 0,
+ 0.2,
+ 1.3,
+ 0.3,
+ 1.3,
+ 1,
+ 0.544,
+ 1.3,
+ 0.789,
+ 0,
+ 1.033,
+ 0,
+ 1,
+ 1.222,
+ 0,
+ 1.411,
+ 0,
+ 1.6,
+ 0,
+ 1,
+ 1.811,
+ 0,
+ 2.022,
+ -10,
+ 2.233,
+ -10,
+ 1,
+ 2.811,
+ -10,
+ 3.389,
+ -10,
+ 3.967,
+ -10,
+ 1,
+ 4.133,
+ -10,
+ 4.3,
+ -6.458,
+ 4.467,
+ -6.458,
+ 0,
+ 5.367,
+ -6.458
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmRB",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.1,
+ 0,
+ 0.2,
+ 1.9,
+ 0.3,
+ 1.9,
+ 1,
+ 0.544,
+ 1.9,
+ 0.789,
+ 0,
+ 1.033,
+ 0,
+ 1,
+ 1.222,
+ 0,
+ 1.411,
+ 0,
+ 1.6,
+ 0,
+ 1,
+ 1.811,
+ 0,
+ 2.022,
+ -10,
+ 2.233,
+ -10,
+ 1,
+ 2.7,
+ -10,
+ 3.167,
+ -10,
+ 3.633,
+ -10,
+ 1,
+ 3.744,
+ -10,
+ 3.856,
+ -10,
+ 3.967,
+ -10,
+ 1,
+ 4.133,
+ -10,
+ 4.3,
+ -5.833,
+ 4.467,
+ -5.833,
+ 0,
+ 5.367,
+ -5.833
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHandLB",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.156,
+ 0,
+ 0.311,
+ 0,
+ 0.467,
+ 0,
+ 1,
+ 0.656,
+ 0,
+ 0.844,
+ 6.25,
+ 1.033,
+ 6.25,
+ 1,
+ 1.222,
+ 6.25,
+ 1.411,
+ 6.365,
+ 1.6,
+ 5.833,
+ 1,
+ 1.811,
+ 5.239,
+ 2.022,
+ -4.07,
+ 2.233,
+ -4.583,
+ 1,
+ 2.367,
+ -4.907,
+ 2.5,
+ -4.792,
+ 2.633,
+ -4.792,
+ 1,
+ 2.778,
+ -4.792,
+ 2.922,
+ -3.75,
+ 3.067,
+ -3.75,
+ 1,
+ 3.256,
+ -3.75,
+ 3.444,
+ -6.25,
+ 3.633,
+ -6.25,
+ 1,
+ 3.756,
+ -6.25,
+ 3.878,
+ -6.419,
+ 4,
+ -5.833,
+ 1,
+ 4.156,
+ -5.087,
+ 4.311,
+ 9.4,
+ 4.467,
+ 9.4,
+ 1,
+ 4.533,
+ 9.4,
+ 4.6,
+ 7.1,
+ 4.667,
+ 7.1,
+ 0,
+ 5.367,
+ 7.1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHandRB",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.156,
+ 0,
+ 0.311,
+ 0,
+ 0.467,
+ 0,
+ 1,
+ 0.656,
+ 0,
+ 0.844,
+ 8.064,
+ 1.033,
+ 8.333,
+ 1,
+ 1.222,
+ 8.603,
+ 1.411,
+ 8.542,
+ 1.6,
+ 8.542,
+ 1,
+ 1.811,
+ 8.542,
+ 2.022,
+ -5.8,
+ 2.233,
+ -7.292,
+ 1,
+ 2.367,
+ -8.234,
+ 2.5,
+ -7.917,
+ 2.633,
+ -7.917,
+ 1,
+ 2.778,
+ -7.917,
+ 2.922,
+ -7.917,
+ 3.067,
+ -7.917,
+ 1,
+ 3.256,
+ -7.917,
+ 3.444,
+ -8.084,
+ 3.633,
+ -8.333,
+ 1,
+ 3.756,
+ -8.494,
+ 3.878,
+ -8.542,
+ 4,
+ -8.542,
+ 1,
+ 4.156,
+ -8.542,
+ 4.311,
+ 10,
+ 4.467,
+ 10,
+ 1,
+ 4.533,
+ 10,
+ 4.6,
+ 6.8,
+ 4.667,
+ 6.8,
+ 0,
+ 5.367,
+ 6.8
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHandL",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.144,
+ 0,
+ 0.289,
+ 0.958,
+ 0.433,
+ 0.958,
+ 1,
+ 0.611,
+ 0.958,
+ 0.789,
+ -1,
+ 0.967,
+ -1,
+ 1,
+ 1.222,
+ -1,
+ 1.478,
+ 0.4,
+ 1.733,
+ 0.4,
+ 1,
+ 1.922,
+ 0.4,
+ 2.111,
+ -1,
+ 2.3,
+ -1,
+ 1,
+ 2.544,
+ -1,
+ 2.789,
+ 1,
+ 3.033,
+ 1,
+ 1,
+ 3.344,
+ 1,
+ 3.656,
+ -1,
+ 3.967,
+ -1,
+ 1,
+ 4.222,
+ -1,
+ 4.478,
+ 0.4,
+ 4.733,
+ 0.4,
+ 0,
+ 5.367,
+ 0.4
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHandR",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.144,
+ 0,
+ 0.289,
+ 1,
+ 0.433,
+ 1,
+ 1,
+ 0.611,
+ 1,
+ 0.789,
+ -1,
+ 0.967,
+ -1,
+ 1,
+ 1.222,
+ -1,
+ 1.478,
+ 0.4,
+ 1.733,
+ 0.4,
+ 1,
+ 1.922,
+ 0.4,
+ 2.111,
+ -1,
+ 2.3,
+ -1,
+ 1,
+ 2.544,
+ -1,
+ 2.789,
+ 1,
+ 3.033,
+ 1,
+ 1,
+ 3.344,
+ 1,
+ 3.656,
+ -1,
+ 3.967,
+ -1,
+ 1,
+ 4.222,
+ -1,
+ 4.478,
+ 0.3,
+ 4.733,
+ 0.3,
+ 0,
+ 5.367,
+ 0.3
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairAhoge",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.022,
+ 0,
+ 0.044,
+ 0,
+ 0.067,
+ 0,
+ 1,
+ 0.144,
+ 0,
+ 0.222,
+ -10,
+ 0.3,
+ -10,
+ 1,
+ 0.456,
+ -10,
+ 0.611,
+ 8.967,
+ 0.767,
+ 8.967,
+ 1,
+ 0.889,
+ 8.967,
+ 1.011,
+ -5.867,
+ 1.133,
+ -5.867,
+ 1,
+ 1.244,
+ -5.867,
+ 1.356,
+ -3.178,
+ 1.467,
+ -0.111,
+ 1,
+ 1.522,
+ 1.423,
+ 1.578,
+ 1.562,
+ 1.633,
+ 1.562,
+ 1,
+ 1.733,
+ 1.562,
+ 1.833,
+ -4.563,
+ 1.933,
+ -4.563,
+ 1,
+ 2.078,
+ -4.563,
+ 2.222,
+ 10,
+ 2.367,
+ 10,
+ 1,
+ 2.411,
+ 10,
+ 2.477,
+ 10,
+ 2.5,
+ 9.828,
+ 1,
+ 2.6,
+ 9.082,
+ 2.7,
+ -8.352,
+ 2.8,
+ -8.352,
+ 1,
+ 2.867,
+ -8.352,
+ 2.933,
+ -8.632,
+ 3,
+ -7.761,
+ 1,
+ 3.111,
+ -6.31,
+ 3.222,
+ 6.63,
+ 3.333,
+ 6.63,
+ 1,
+ 3.389,
+ 6.63,
+ 3.444,
+ -4.883,
+ 3.5,
+ -4.883,
+ 1,
+ 3.544,
+ -4.883,
+ 3.589,
+ -5.066,
+ 3.633,
+ -4.785,
+ 1,
+ 3.811,
+ -3.661,
+ 3.989,
+ 9.803,
+ 4.167,
+ 9.803,
+ 1,
+ 4.256,
+ 9.803,
+ 4.344,
+ -9.656,
+ 4.433,
+ -9.656,
+ 1,
+ 4.567,
+ -9.656,
+ 4.7,
+ 5.375,
+ 4.833,
+ 5.375,
+ 1,
+ 4.967,
+ 5.375,
+ 5.1,
+ 0,
+ 5.233,
+ 0,
+ 0,
+ 5.367,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmA",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5.37,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmB",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 5.37,
+ 1
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m07.motion3.json b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m07.motion3.json
new file mode 100644
index 0000000..488b078
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m07.motion3.json
@@ -0,0 +1,927 @@
+{
+ "Version": 3,
+ "Meta": {
+ "Duration": 1.9,
+ "Fps": 30.0,
+ "Loop": true,
+ "AreBeziersRestricted": false,
+ "CurveCount": 30,
+ "TotalSegmentCount": 121,
+ "TotalPointCount": 331,
+ "UserDataCount": 0,
+ "TotalUserDataSize": 0
+ },
+ "Curves": [
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 0,
+ 1.9,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 0,
+ 1.9,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 1,
+ 0.444,
+ 0,
+ 0.556,
+ 8,
+ 0.667,
+ 8,
+ 0,
+ 1.9,
+ 8
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamCheek",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 0,
+ 1.9,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLOpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.111,
+ 1,
+ 0.222,
+ 1,
+ 0.333,
+ 1,
+ 1,
+ 0.378,
+ 1,
+ 0.422,
+ 0,
+ 0.467,
+ 0,
+ 1,
+ 0.522,
+ 0,
+ 0.578,
+ 1.2,
+ 0.633,
+ 1.2,
+ 1,
+ 0.744,
+ 1.2,
+ 0.856,
+ 1.2,
+ 0.967,
+ 1.2,
+ 1,
+ 0.989,
+ 1.2,
+ 1.011,
+ 0,
+ 1.033,
+ 0,
+ 1,
+ 1.067,
+ 0,
+ 1.1,
+ 1.2,
+ 1.133,
+ 1.2,
+ 1,
+ 1.167,
+ 1.2,
+ 1.2,
+ 1.2,
+ 1.233,
+ 1.2,
+ 1,
+ 1.267,
+ 1.2,
+ 1.3,
+ 0,
+ 1.333,
+ 0,
+ 1,
+ 1.356,
+ 0,
+ 1.378,
+ 1.2,
+ 1.4,
+ 1.2,
+ 0,
+ 1.9,
+ 1.2
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 0,
+ 1.9,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeROpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.111,
+ 1,
+ 0.222,
+ 1,
+ 0.333,
+ 1,
+ 1,
+ 0.378,
+ 1,
+ 0.422,
+ 0,
+ 0.467,
+ 0,
+ 1,
+ 0.522,
+ 0,
+ 0.578,
+ 1.2,
+ 0.633,
+ 1.2,
+ 1,
+ 0.744,
+ 1.2,
+ 0.856,
+ 1.2,
+ 0.967,
+ 1.2,
+ 1,
+ 0.989,
+ 1.2,
+ 1.011,
+ 0,
+ 1.033,
+ 0,
+ 1,
+ 1.067,
+ 0,
+ 1.1,
+ 1.2,
+ 1.133,
+ 1.2,
+ 1,
+ 1.167,
+ 1.2,
+ 1.2,
+ 1.2,
+ 1.233,
+ 1.2,
+ 1,
+ 1.267,
+ 1.2,
+ 1.3,
+ 0,
+ 1.333,
+ 0,
+ 1,
+ 1.356,
+ 0,
+ 1.378,
+ 1.2,
+ 1.4,
+ 1.2,
+ 0,
+ 1.9,
+ 1.2
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 1,
+ 0.356,
+ 0,
+ 0.378,
+ 0,
+ 0.4,
+ 0,
+ 1,
+ 0.489,
+ 0,
+ 0.578,
+ 0,
+ 0.667,
+ 0,
+ 0,
+ 1.9,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 1,
+ 0.356,
+ 0,
+ 0.378,
+ 0,
+ 0.4,
+ 0,
+ 1,
+ 0.489,
+ 0,
+ 0.578,
+ 0,
+ 0.667,
+ 0,
+ 0,
+ 1.9,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 1,
+ 0.356,
+ 0,
+ 0.378,
+ 0,
+ 0.4,
+ 0,
+ 1,
+ 0.489,
+ 0,
+ 0.578,
+ 0,
+ 0.667,
+ 0,
+ 0,
+ 1.9,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 1,
+ 0.356,
+ 0,
+ 0.378,
+ 0,
+ 0.4,
+ 0,
+ 1,
+ 0.489,
+ 0,
+ 0.578,
+ 0.26,
+ 0.667,
+ 0.26,
+ 0,
+ 1.9,
+ 0.26
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 1,
+ 0.356,
+ 0,
+ 0.378,
+ 0,
+ 0.4,
+ 0,
+ 1,
+ 0.489,
+ 0,
+ 0.578,
+ 0.36,
+ 0.667,
+ 0.36,
+ 0,
+ 1.9,
+ 0.36
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 1,
+ 0.356,
+ 0,
+ 0.378,
+ 0,
+ 0.4,
+ 0,
+ 1,
+ 0.489,
+ 0,
+ 0.578,
+ 0,
+ 0.667,
+ 0,
+ 0,
+ 1.9,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 1,
+ 0.356,
+ 0,
+ 0.378,
+ 0,
+ 0.4,
+ 0,
+ 1,
+ 0.489,
+ 0,
+ 0.578,
+ -0.27,
+ 0.667,
+ -0.27,
+ 0,
+ 1.9,
+ -0.27
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 1,
+ 0.356,
+ 0,
+ 0.378,
+ 0,
+ 0.4,
+ 0,
+ 1,
+ 0.489,
+ 0,
+ 0.578,
+ 0.26,
+ 0.667,
+ 0.26,
+ 0,
+ 1.9,
+ 0.26
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 1,
+ 0.356,
+ 0,
+ 0.378,
+ 0,
+ 0.4,
+ 0,
+ 1,
+ 0.489,
+ 0,
+ 0.578,
+ -0.03,
+ 0.667,
+ -0.03,
+ 0,
+ 1.9,
+ -0.03
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 1,
+ 0.356,
+ 0,
+ 0.378,
+ 0,
+ 0.4,
+ 0,
+ 1,
+ 0.489,
+ 0,
+ 0.578,
+ 0.33,
+ 0.667,
+ 0.33,
+ 0,
+ 1.9,
+ 0.33
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 1,
+ 0.356,
+ 0,
+ 0.378,
+ 0,
+ 0.4,
+ 0,
+ 1,
+ 0.489,
+ 0,
+ 0.578,
+ 0.21,
+ 0.667,
+ 0.21,
+ 0,
+ 1.9,
+ 0.21
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 1,
+ 0.444,
+ 0,
+ 0.556,
+ -6,
+ 0.667,
+ -6,
+ 0,
+ 1.9,
+ -6
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 1,
+ 0.422,
+ 0,
+ 0.511,
+ 10,
+ 0.6,
+ 10,
+ 1,
+ 0.667,
+ 10,
+ 0.733,
+ -6,
+ 0.8,
+ -6,
+ 1,
+ 0.833,
+ -6,
+ 0.867,
+ 5,
+ 0.9,
+ 5,
+ 1,
+ 1.011,
+ 5,
+ 1.122,
+ 0,
+ 1.233,
+ 0,
+ 0,
+ 1.9,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 1,
+ 0.444,
+ 0,
+ 0.556,
+ -3,
+ 0.667,
+ -3,
+ 0,
+ 1.9,
+ -3
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBreath",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 0,
+ 1.9,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamShoulder",
+ "Segments": [
+ 0,
+ -0.062,
+ 1,
+ 0.111,
+ -0.062,
+ 0.222,
+ -0.103,
+ 0.333,
+ 0,
+ 1,
+ 0.589,
+ 0.238,
+ 0.844,
+ 1,
+ 1.1,
+ 1,
+ 0,
+ 1.9,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmLA",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 1,
+ 0.478,
+ 0,
+ 0.622,
+ -10,
+ 0.767,
+ -10,
+ 1,
+ 0.811,
+ -10,
+ 0.856,
+ -8.2,
+ 0.9,
+ -8.2,
+ 0,
+ 1.9,
+ -8.2
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmRA",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 1,
+ 0.478,
+ 0,
+ 0.622,
+ -10,
+ 0.767,
+ -10,
+ 1,
+ 0.811,
+ -10,
+ 0.856,
+ -7.2,
+ 0.9,
+ -7.2,
+ 0,
+ 1.9,
+ -7.2
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmLB",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 0,
+ 1.9,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmRB",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 0,
+ 0.333,
+ 0,
+ 0,
+ 1.9,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairAhoge",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.111,
+ 0,
+ 0.222,
+ 1.9,
+ 0.333,
+ 5.2,
+ 1,
+ 0.444,
+ 8.5,
+ 0.556,
+ 9.926,
+ 0.667,
+ 9.926,
+ 1,
+ 0.744,
+ 9.926,
+ 0.822,
+ -10,
+ 0.9,
+ -10,
+ 1,
+ 0.956,
+ -10,
+ 1.011,
+ 6,
+ 1.067,
+ 6,
+ 1,
+ 1.144,
+ 6,
+ 1.222,
+ -4,
+ 1.3,
+ -4,
+ 1,
+ 1.367,
+ -4,
+ 1.433,
+ 0,
+ 1.5,
+ 0,
+ 0,
+ 1.9,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmA",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 1.9,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmB",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 1.9,
+ 0
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m08.motion3.json b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m08.motion3.json
new file mode 100644
index 0000000..c93722a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m08.motion3.json
@@ -0,0 +1,1023 @@
+{
+ "Version": 3,
+ "Meta": {
+ "Duration": 2.1,
+ "Fps": 30.0,
+ "Loop": true,
+ "AreBeziersRestricted": false,
+ "CurveCount": 33,
+ "TotalSegmentCount": 133,
+ "TotalPointCount": 364,
+ "UserDataCount": 0,
+ "TotalUserDataSize": 0
+ },
+ "Curves": [
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.211,
+ 0,
+ 0.256,
+ 0,
+ 0.3,
+ 0,
+ 1,
+ 0.344,
+ 0,
+ 0.389,
+ 0,
+ 0.433,
+ 0,
+ 1,
+ 0.522,
+ 0,
+ 0.611,
+ 0,
+ 0.7,
+ 0,
+ 0,
+ 2.1,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.211,
+ 0,
+ 0.256,
+ 0,
+ 0.3,
+ 0,
+ 1,
+ 0.344,
+ 0,
+ 0.389,
+ -15,
+ 0.433,
+ -15,
+ 1,
+ 0.522,
+ -15,
+ 0.611,
+ 11,
+ 0.7,
+ 11,
+ 0,
+ 2.1,
+ 11
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.211,
+ 0,
+ 0.256,
+ 0,
+ 0.3,
+ 0,
+ 1,
+ 0.411,
+ 0,
+ 0.522,
+ -15,
+ 0.633,
+ -15,
+ 1,
+ 0.689,
+ -15,
+ 0.744,
+ -9,
+ 0.8,
+ -9,
+ 0,
+ 2.1,
+ -9
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamCheek",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.244,
+ 0,
+ 0.322,
+ 0,
+ 0.4,
+ 0,
+ 0,
+ 2.1,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLOpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 1,
+ 0.211,
+ 1,
+ 0.256,
+ 1,
+ 0.3,
+ 1,
+ 1,
+ 0.422,
+ 1,
+ 0.544,
+ 0,
+ 0.667,
+ 0,
+ 0,
+ 2.1,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.211,
+ 0,
+ 0.256,
+ 0,
+ 0.3,
+ 0,
+ 1,
+ 0.422,
+ 0,
+ 0.544,
+ 1,
+ 0.667,
+ 1,
+ 0,
+ 2.1,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeROpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 1,
+ 0.211,
+ 1,
+ 0.256,
+ 1,
+ 0.3,
+ 1,
+ 1,
+ 0.422,
+ 1,
+ 0.544,
+ 0,
+ 0.667,
+ 0,
+ 0,
+ 2.1,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.211,
+ 0,
+ 0.256,
+ 0,
+ 0.3,
+ 0,
+ 1,
+ 0.422,
+ 0,
+ 0.544,
+ 1,
+ 0.667,
+ 1,
+ 0,
+ 2.1,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.211,
+ 0,
+ 0.256,
+ 0,
+ 0.3,
+ 0,
+ 0,
+ 2.1,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.211,
+ 0,
+ 0.256,
+ 0,
+ 0.3,
+ 0,
+ 0,
+ 2.1,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.211,
+ 0,
+ 0.256,
+ 0,
+ 0.3,
+ 0,
+ 1,
+ 0.422,
+ 0,
+ 0.544,
+ -0.14,
+ 0.667,
+ -0.14,
+ 0,
+ 2.1,
+ -0.14
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.211,
+ 0,
+ 0.256,
+ 0,
+ 0.3,
+ 0,
+ 1,
+ 0.422,
+ 0,
+ 0.544,
+ -0.14,
+ 0.667,
+ -0.14,
+ 0,
+ 2.1,
+ -0.14
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.244,
+ 0,
+ 0.322,
+ 0,
+ 0.4,
+ 0,
+ 1,
+ 0.489,
+ 0,
+ 0.578,
+ -0.21,
+ 0.667,
+ -0.21,
+ 0,
+ 2.1,
+ -0.21
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.244,
+ 0,
+ 0.322,
+ 0,
+ 0.4,
+ 0,
+ 1,
+ 0.489,
+ 0,
+ 0.578,
+ -0.2,
+ 0.667,
+ -0.2,
+ 0,
+ 2.1,
+ -0.2
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.244,
+ 0,
+ 0.322,
+ 0,
+ 0.4,
+ 0,
+ 1,
+ 0.489,
+ 0,
+ 0.578,
+ 0.25,
+ 0.667,
+ 0.25,
+ 0,
+ 2.1,
+ 0.25
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.244,
+ 0,
+ 0.322,
+ 0,
+ 0.4,
+ 0,
+ 1,
+ 0.489,
+ 0,
+ 0.578,
+ 0.23,
+ 0.667,
+ 0.23,
+ 0,
+ 2.1,
+ 0.23
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.244,
+ 0,
+ 0.322,
+ 0,
+ 0.4,
+ 0,
+ 1,
+ 0.489,
+ 0,
+ 0.578,
+ 0,
+ 0.667,
+ 0,
+ 0,
+ 2.1,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.244,
+ 0,
+ 0.322,
+ 0,
+ 0.4,
+ 0,
+ 1,
+ 0.489,
+ 0,
+ 0.578,
+ 0,
+ 0.667,
+ 0,
+ 0,
+ 2.1,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 2.1,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.244,
+ 0,
+ 0.322,
+ 8,
+ 0.4,
+ 8,
+ 1,
+ 0.467,
+ 8,
+ 0.533,
+ -7,
+ 0.6,
+ -7,
+ 1,
+ 0.656,
+ -7,
+ 0.711,
+ 3,
+ 0.767,
+ 3,
+ 1,
+ 0.811,
+ 3,
+ 0.856,
+ 0,
+ 0.9,
+ 0,
+ 0,
+ 2.1,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.244,
+ 0,
+ 0.322,
+ 0,
+ 0.4,
+ 0,
+ 1,
+ 0.478,
+ 0,
+ 0.556,
+ 4,
+ 0.633,
+ 4,
+ 1,
+ 0.689,
+ 4,
+ 0.744,
+ 2,
+ 0.8,
+ 2,
+ 0,
+ 2.1,
+ 2
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBreath",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.244,
+ 0,
+ 0.322,
+ 0,
+ 0.4,
+ 0,
+ 0,
+ 2.1,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamShoulder",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.244,
+ 0,
+ 0.322,
+ -1,
+ 0.4,
+ -1,
+ 1,
+ 0.467,
+ -1,
+ 0.533,
+ 1,
+ 0.6,
+ 1,
+ 1,
+ 0.656,
+ 1,
+ 0.711,
+ -0.4,
+ 0.767,
+ -0.4,
+ 1,
+ 0.922,
+ -0.4,
+ 1.078,
+ 0,
+ 1.233,
+ 0,
+ 0,
+ 2.1,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamLeg",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 2.1,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmLA",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 2.1,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmRA",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 2.1,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmLB",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.211,
+ 0,
+ 0.256,
+ -5.208,
+ 0.3,
+ -5.208,
+ 1,
+ 0.367,
+ -5.208,
+ 0.433,
+ 9.583,
+ 0.5,
+ 9.583,
+ 0,
+ 2.1,
+ 9.583
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmRB",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.211,
+ 0,
+ 0.256,
+ -4.583,
+ 0.3,
+ -4.583,
+ 1,
+ 0.367,
+ -4.583,
+ 0.433,
+ 4.375,
+ 0.5,
+ 4.375,
+ 0,
+ 2.1,
+ 4.375
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHandLB",
+ "Segments": [
+ 0,
+ 9.971,
+ 1,
+ 0.111,
+ 9.768,
+ 0.222,
+ 8.46,
+ 0.333,
+ 4.167,
+ 1,
+ 0.378,
+ 2.449,
+ 0.422,
+ -4.583,
+ 0.467,
+ -4.583,
+ 1,
+ 0.511,
+ -4.583,
+ 0.556,
+ 4.792,
+ 0.6,
+ 4.792,
+ 0,
+ 2.1,
+ 4.792
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHandRB",
+ "Segments": [
+ 0,
+ 9.971,
+ 1,
+ 0.111,
+ 9.768,
+ 0.222,
+ 8.46,
+ 0.333,
+ 4.167,
+ 1,
+ 0.378,
+ 2.449,
+ 0.422,
+ -4.583,
+ 0.467,
+ -4.583,
+ 1,
+ 0.511,
+ -4.583,
+ 0.556,
+ 7.292,
+ 0.6,
+ 7.292,
+ 0,
+ 2.1,
+ 7.292
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairAhoge",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.211,
+ 0,
+ 0.256,
+ -0.142,
+ 0.3,
+ 0.234,
+ 1,
+ 0.378,
+ 0.89,
+ 0.456,
+ 10,
+ 0.533,
+ 10,
+ 1,
+ 0.578,
+ 10,
+ 0.622,
+ -10,
+ 0.667,
+ -10,
+ 1,
+ 0.733,
+ -10,
+ 0.8,
+ 9.951,
+ 0.867,
+ 9.951,
+ 1,
+ 0.944,
+ 9.951,
+ 1.022,
+ -9.508,
+ 1.1,
+ -9.508,
+ 1,
+ 1.178,
+ -9.508,
+ 1.256,
+ 5.892,
+ 1.333,
+ 5.892,
+ 1,
+ 1.4,
+ 5.892,
+ 1.467,
+ -2,
+ 1.533,
+ -2,
+ 1,
+ 1.578,
+ -2,
+ 1.622,
+ 1,
+ 1.667,
+ 1,
+ 1,
+ 1.722,
+ 1,
+ 1.778,
+ 0,
+ 1.833,
+ 0,
+ 0,
+ 2.1,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmA",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 2.1,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmB",
+ "Segments": [
+ 0,
+ 0.99,
+ 0,
+ 2.1,
+ 0.99
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m09.motion3.json b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m09.motion3.json
new file mode 100644
index 0000000..8ec17a5
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m09.motion3.json
@@ -0,0 +1,1113 @@
+{
+ "Version": 3,
+ "Meta": {
+ "Duration": 1.6,
+ "Fps": 30.0,
+ "Loop": true,
+ "AreBeziersRestricted": false,
+ "CurveCount": 31,
+ "TotalSegmentCount": 150,
+ "TotalPointCount": 417,
+ "UserDataCount": 0,
+ "TotalUserDataSize": 0
+ },
+ "Curves": [
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.333,
+ 0,
+ 0.467,
+ -4,
+ 0.6,
+ -4,
+ 0,
+ 1.6,
+ -4
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.289,
+ 0,
+ 0.378,
+ -20,
+ 0.467,
+ -20,
+ 1,
+ 0.522,
+ -20,
+ 0.578,
+ 2,
+ 0.633,
+ 2,
+ 1,
+ 0.667,
+ 2,
+ 0.7,
+ 0,
+ 0.733,
+ 0,
+ 1,
+ 0.8,
+ 0,
+ 0.867,
+ 0,
+ 0.933,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1.067,
+ -21,
+ 1.133,
+ -21,
+ 1,
+ 1.167,
+ -21,
+ 1.2,
+ -17,
+ 1.233,
+ -17,
+ 0,
+ 1.6,
+ -17
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 0,
+ 1.6,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamCheek",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.356,
+ 0,
+ 0.511,
+ 0,
+ 0.667,
+ 0,
+ 1,
+ 0.778,
+ 0,
+ 0.889,
+ 1,
+ 1,
+ 1,
+ 0,
+ 1.6,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLOpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.067,
+ 1,
+ 0.133,
+ 1,
+ 0.2,
+ 1,
+ 1,
+ 0.244,
+ 1,
+ 0.289,
+ 1,
+ 0.333,
+ 1,
+ 1,
+ 0.378,
+ 1,
+ 0.422,
+ 0,
+ 0.467,
+ 0,
+ 1,
+ 0.489,
+ 0,
+ 0.511,
+ 0,
+ 0.533,
+ 0,
+ 1,
+ 0.578,
+ 0,
+ 0.622,
+ 1,
+ 0.667,
+ 1,
+ 1,
+ 0.8,
+ 1,
+ 0.933,
+ 1,
+ 1.067,
+ 1,
+ 1,
+ 1.122,
+ 1,
+ 1.178,
+ 0.825,
+ 1.233,
+ 0.825,
+ 0,
+ 1.6,
+ 0.825
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.544,
+ 0,
+ 0.889,
+ 0,
+ 1.233,
+ 0,
+ 0,
+ 1.6,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeROpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.067,
+ 1,
+ 0.133,
+ 1,
+ 0.2,
+ 1,
+ 1,
+ 0.244,
+ 1,
+ 0.289,
+ 1,
+ 0.333,
+ 1,
+ 1,
+ 0.378,
+ 1,
+ 0.422,
+ 0,
+ 0.467,
+ 0,
+ 1,
+ 0.489,
+ 0,
+ 0.511,
+ 0,
+ 0.533,
+ 0,
+ 1,
+ 0.578,
+ 0,
+ 0.622,
+ 0.996,
+ 0.667,
+ 1,
+ 1,
+ 0.8,
+ 1.011,
+ 0.933,
+ 1.012,
+ 1.067,
+ 1.012,
+ 1,
+ 1.122,
+ 1.012,
+ 1.178,
+ 0.8,
+ 1.233,
+ 0.8,
+ 0,
+ 1.6,
+ 0.8
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.322,
+ 0,
+ 0.444,
+ 0,
+ 0.567,
+ 0,
+ 1,
+ 0.789,
+ 0,
+ 1.011,
+ 0,
+ 1.233,
+ 0,
+ 0,
+ 1.6,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.322,
+ 0,
+ 0.444,
+ 0,
+ 0.567,
+ 0,
+ 1,
+ 0.789,
+ 0,
+ 1.011,
+ 0,
+ 1.233,
+ 0,
+ 0,
+ 1.6,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.322,
+ 0,
+ 0.444,
+ 0,
+ 0.567,
+ 0,
+ 1,
+ 0.789,
+ 0,
+ 1.011,
+ 0,
+ 1.233,
+ 0,
+ 0,
+ 1.6,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.322,
+ 0,
+ 0.444,
+ 1,
+ 0.567,
+ 1,
+ 1,
+ 0.733,
+ 1,
+ 0.9,
+ 1,
+ 1.067,
+ 1,
+ 1,
+ 1.122,
+ 1,
+ 1.178,
+ -0.417,
+ 1.233,
+ -0.417,
+ 0,
+ 1.6,
+ -0.417
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.322,
+ 0,
+ 0.444,
+ 1,
+ 0.567,
+ 1,
+ 1,
+ 0.733,
+ 1,
+ 0.9,
+ 1,
+ 1.067,
+ 1,
+ 1,
+ 1.122,
+ 1,
+ 1.178,
+ -1,
+ 1.233,
+ -1,
+ 0,
+ 1.6,
+ -1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.322,
+ 0,
+ 0.444,
+ 0,
+ 0.567,
+ 0,
+ 1,
+ 0.789,
+ 0,
+ 1.011,
+ 0,
+ 1.233,
+ 0,
+ 0,
+ 1.6,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.322,
+ 0,
+ 0.444,
+ 0,
+ 0.567,
+ 0,
+ 1,
+ 0.789,
+ 0,
+ 1.011,
+ 0,
+ 1.233,
+ 0,
+ 0,
+ 1.6,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.322,
+ 0,
+ 0.444,
+ -1,
+ 0.567,
+ -1,
+ 1,
+ 0.789,
+ -1,
+ 1.011,
+ -1,
+ 1.233,
+ -1,
+ 0,
+ 1.6,
+ -1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.322,
+ 0,
+ 0.444,
+ -1,
+ 0.567,
+ -1,
+ 1,
+ 0.789,
+ -1,
+ 1.011,
+ -1,
+ 1.233,
+ -1,
+ 0,
+ 1.6,
+ -1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.322,
+ 0,
+ 0.444,
+ -1,
+ 0.567,
+ -1,
+ 1,
+ 0.789,
+ -1,
+ 1.011,
+ -1,
+ 1.233,
+ -1,
+ 0,
+ 1.6,
+ -1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.322,
+ 0,
+ 0.444,
+ -1,
+ 0.567,
+ -1,
+ 1,
+ 0.789,
+ -1,
+ 1.011,
+ -1,
+ 1.233,
+ -1,
+ 0,
+ 1.6,
+ -1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.156,
+ 0,
+ 0.311,
+ 0,
+ 0.467,
+ 0,
+ 1,
+ 0.556,
+ 0,
+ 0.644,
+ -10,
+ 0.733,
+ -10,
+ 1,
+ 0.833,
+ -10,
+ 0.933,
+ -9,
+ 1.033,
+ -9,
+ 0,
+ 1.6,
+ -9
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.278,
+ 0,
+ 0.356,
+ -4,
+ 0.433,
+ -4,
+ 1,
+ 0.522,
+ -4,
+ 0.611,
+ 5,
+ 0.7,
+ 5,
+ 1,
+ 0.789,
+ 5,
+ 0.878,
+ 5,
+ 0.967,
+ 5,
+ 1,
+ 1.044,
+ 5,
+ 1.122,
+ -7,
+ 1.2,
+ -7,
+ 1,
+ 1.267,
+ -7,
+ 1.333,
+ 0,
+ 1.4,
+ 0,
+ 0,
+ 1.6,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 0,
+ 1.6,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBreath",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 0,
+ 1.6,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamShoulder",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.367,
+ 0,
+ 0.533,
+ 0.479,
+ 0.7,
+ 0.479,
+ 1,
+ 0.889,
+ 0.479,
+ 1.078,
+ -1,
+ 1.267,
+ -1,
+ 1,
+ 1.322,
+ -1,
+ 1.378,
+ -1,
+ 1.433,
+ -1,
+ 0,
+ 1.6,
+ -1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamLeg",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 1.6,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmLA",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.233,
+ 0,
+ 0.267,
+ 0,
+ 0.3,
+ 0,
+ 1,
+ 0.367,
+ 0,
+ 0.433,
+ -4.5,
+ 0.5,
+ -4.5,
+ 1,
+ 0.578,
+ -4.5,
+ 0.656,
+ 0.8,
+ 0.733,
+ 0.8,
+ 1,
+ 0.811,
+ 0.8,
+ 0.889,
+ -0.156,
+ 0.967,
+ -0.6,
+ 1,
+ 1.011,
+ -0.854,
+ 1.056,
+ -0.72,
+ 1.1,
+ -1,
+ 1,
+ 1.167,
+ -1.42,
+ 1.233,
+ -3.7,
+ 1.3,
+ -3.7,
+ 1,
+ 1.356,
+ -3.7,
+ 1.411,
+ -2.4,
+ 1.467,
+ -2.4,
+ 0,
+ 1.6,
+ -2.4
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmRA",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.233,
+ 0,
+ 0.267,
+ 0,
+ 0.3,
+ 0,
+ 1,
+ 0.367,
+ 0,
+ 0.433,
+ -4.5,
+ 0.5,
+ -4.5,
+ 1,
+ 0.578,
+ -4.5,
+ 0.656,
+ 2.2,
+ 0.733,
+ 2.2,
+ 1,
+ 0.811,
+ 2.2,
+ 0.889,
+ -0.749,
+ 0.967,
+ -0.9,
+ 1,
+ 1.011,
+ -0.987,
+ 1.056,
+ -0.921,
+ 1.1,
+ -1,
+ 1,
+ 1.167,
+ -1.119,
+ 1.233,
+ -3.6,
+ 1.3,
+ -3.6,
+ 1,
+ 1.356,
+ -3.6,
+ 1.411,
+ -1.8,
+ 1.467,
+ -1.8,
+ 0,
+ 1.6,
+ -1.8
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmLB",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 0,
+ 1.6,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmRB",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.378,
+ 0,
+ 0.556,
+ 0.84,
+ 0.733,
+ 0.84,
+ 1,
+ 0.744,
+ 0.84,
+ 0.756,
+ 0.65,
+ 0.767,
+ 0.65,
+ 1,
+ 0.8,
+ 0.65,
+ 0.833,
+ 0.86,
+ 0.867,
+ 0.86,
+ 0,
+ 1.6,
+ 0.86
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairAhoge",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.289,
+ 0,
+ 0.378,
+ 6,
+ 0.467,
+ 6,
+ 1,
+ 0.5,
+ 6,
+ 0.533,
+ 4,
+ 0.567,
+ 0,
+ 1,
+ 0.622,
+ -6.667,
+ 0.678,
+ -10,
+ 0.733,
+ -10,
+ 1,
+ 0.767,
+ -10,
+ 0.8,
+ -6,
+ 0.833,
+ -6,
+ 1,
+ 0.933,
+ -6,
+ 1.033,
+ -10,
+ 1.133,
+ -10,
+ 1,
+ 1.178,
+ -10,
+ 1.222,
+ -2,
+ 1.267,
+ -2,
+ 1,
+ 1.311,
+ -2,
+ 1.356,
+ -9,
+ 1.4,
+ -9,
+ 0,
+ 1.6,
+ -9
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmA",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 1.6,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmB",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 1.6,
+ 0
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m10.motion3.json b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m10.motion3.json
new file mode 100644
index 0000000..ee27769
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/hiyori_pro_zh/runtime/motion/hiyori_m10.motion3.json
@@ -0,0 +1,924 @@
+{
+ "Version": 3,
+ "Meta": {
+ "Duration": 4.17,
+ "Fps": 30.0,
+ "Loop": true,
+ "AreBeziersRestricted": false,
+ "CurveCount": 31,
+ "TotalSegmentCount": 118,
+ "TotalPointCount": 321,
+ "UserDataCount": 0,
+ "TotalUserDataSize": 0
+ },
+ "Curves": [
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.4,
+ 0,
+ 0.6,
+ 0,
+ 0.8,
+ 0,
+ 1,
+ 1.067,
+ 0,
+ 1.333,
+ 1.041,
+ 1.6,
+ 1.041,
+ 1,
+ 1.844,
+ 1.041,
+ 2.089,
+ -8,
+ 2.333,
+ -8,
+ 1,
+ 2.656,
+ -8,
+ 2.978,
+ 6,
+ 3.3,
+ 6,
+ 0,
+ 4.167,
+ 6
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.344,
+ 0,
+ 0.489,
+ -30,
+ 0.633,
+ -30,
+ 0,
+ 4.167,
+ -30
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 0,
+ 4.167,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamCheek",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 0,
+ 4.167,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLOpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.067,
+ 1,
+ 0.133,
+ 1,
+ 0.2,
+ 1,
+ 1,
+ 0.311,
+ 1,
+ 0.422,
+ 0.988,
+ 0.533,
+ 0.8,
+ 1,
+ 0.589,
+ 0.706,
+ 0.644,
+ 0,
+ 0.7,
+ 0,
+ 1,
+ 0.722,
+ 0,
+ 0.744,
+ 0,
+ 0.767,
+ 0,
+ 1,
+ 0.822,
+ 0,
+ 0.878,
+ 0.8,
+ 0.933,
+ 0.8,
+ 1,
+ 1.422,
+ 0.8,
+ 1.911,
+ 0.8,
+ 2.4,
+ 0.8,
+ 1,
+ 2.456,
+ 0.8,
+ 2.511,
+ 0,
+ 2.567,
+ 0,
+ 1,
+ 2.589,
+ 0,
+ 2.611,
+ 0,
+ 2.633,
+ 0,
+ 1,
+ 2.689,
+ 0,
+ 2.744,
+ 0.8,
+ 2.8,
+ 0.8,
+ 0,
+ 4.167,
+ 0.8
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 0,
+ 4.167,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeROpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.067,
+ 1,
+ 0.133,
+ 1,
+ 0.2,
+ 1,
+ 1,
+ 0.311,
+ 1,
+ 0.422,
+ 0.988,
+ 0.533,
+ 0.8,
+ 1,
+ 0.589,
+ 0.706,
+ 0.644,
+ 0,
+ 0.7,
+ 0,
+ 1,
+ 0.722,
+ 0,
+ 0.744,
+ 0,
+ 0.767,
+ 0,
+ 1,
+ 0.822,
+ 0,
+ 0.878,
+ 0.8,
+ 0.933,
+ 0.8,
+ 1,
+ 1.422,
+ 0.8,
+ 1.911,
+ 0.8,
+ 2.4,
+ 0.8,
+ 1,
+ 2.456,
+ 0.8,
+ 2.511,
+ 0,
+ 2.567,
+ 0,
+ 1,
+ 2.589,
+ 0,
+ 2.611,
+ 0,
+ 2.633,
+ 0,
+ 1,
+ 2.689,
+ 0,
+ 2.744,
+ 0.8,
+ 2.8,
+ 0.8,
+ 0,
+ 4.167,
+ 0.8
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.278,
+ 0,
+ 0.356,
+ 0,
+ 0.433,
+ 0,
+ 0,
+ 4.167,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.278,
+ 0,
+ 0.356,
+ 0,
+ 0.433,
+ 0,
+ 1,
+ 0.667,
+ 0,
+ 0.9,
+ 0.004,
+ 1.133,
+ -0.01,
+ 1,
+ 1.4,
+ -0.025,
+ 1.667,
+ -0.43,
+ 1.933,
+ -0.43,
+ 1,
+ 2.211,
+ -0.43,
+ 2.489,
+ 0.283,
+ 2.767,
+ 0.283,
+ 0,
+ 4.167,
+ 0.283
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.278,
+ 0,
+ 0.356,
+ -1,
+ 0.433,
+ -1,
+ 0,
+ 4.167,
+ -1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.278,
+ 0,
+ 0.356,
+ 0.19,
+ 0.433,
+ 0.19,
+ 0,
+ 4.167,
+ 0.19
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.278,
+ 0,
+ 0.356,
+ 0.11,
+ 0.433,
+ 0.11,
+ 0,
+ 4.167,
+ 0.11
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.278,
+ 0,
+ 0.356,
+ -0.48,
+ 0.433,
+ -0.48,
+ 0,
+ 4.167,
+ -0.48
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.278,
+ 0,
+ 0.356,
+ -0.29,
+ 0.433,
+ -0.29,
+ 0,
+ 4.167,
+ -0.29
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.278,
+ 0,
+ 0.356,
+ 1,
+ 0.433,
+ 1,
+ 0,
+ 4.167,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.278,
+ 0,
+ 0.356,
+ 0.85,
+ 0.433,
+ 0.85,
+ 0,
+ 4.167,
+ 0.85
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.278,
+ 0,
+ 0.356,
+ -0.75,
+ 0.433,
+ -0.75,
+ 0,
+ 4.167,
+ -0.75
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.278,
+ 0,
+ 0.356,
+ -0.87,
+ 0.433,
+ -0.87,
+ 0,
+ 4.167,
+ -0.87
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.444,
+ 0,
+ 0.689,
+ 0,
+ 0.933,
+ 0,
+ 1,
+ 1.211,
+ 0,
+ 1.489,
+ 0,
+ 1.767,
+ 0,
+ 1,
+ 2.056,
+ 0,
+ 2.344,
+ -6,
+ 2.633,
+ -6,
+ 1,
+ 3.033,
+ -6,
+ 3.433,
+ 10,
+ 3.833,
+ 10,
+ 0,
+ 4.167,
+ 10
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 0,
+ 4.167,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.8,
+ 0,
+ 1.4,
+ -2,
+ 2,
+ -2,
+ 1,
+ 2.456,
+ -2,
+ 2.911,
+ 8.125,
+ 3.367,
+ 8.125,
+ 0,
+ 4.167,
+ 8.125
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBreath",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 0,
+ 4.167,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamShoulder",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.167,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamLeg",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.667,
+ 1,
+ 1.333,
+ 1,
+ 2,
+ 1,
+ 1,
+ 2.267,
+ 1,
+ 2.533,
+ 0.948,
+ 2.8,
+ 0.948,
+ 0,
+ 4.167,
+ 0.948
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmLA",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.233,
+ 0,
+ 0.267,
+ 0,
+ 0.3,
+ 0,
+ 1,
+ 0.478,
+ 0,
+ 0.656,
+ -10,
+ 0.833,
+ -10,
+ 1,
+ 0.922,
+ -10,
+ 1.011,
+ -8.846,
+ 1.1,
+ -8.846,
+ 1,
+ 1.467,
+ -8.846,
+ 1.833,
+ -8.835,
+ 2.2,
+ -9.1,
+ 1,
+ 2.622,
+ -9.405,
+ 3.044,
+ -10,
+ 3.467,
+ -10,
+ 0,
+ 4.167,
+ -10
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmRA",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.233,
+ 0,
+ 0.267,
+ 0,
+ 0.3,
+ 0,
+ 1,
+ 0.478,
+ 0,
+ 0.656,
+ -10,
+ 0.833,
+ -10,
+ 1,
+ 0.922,
+ -10,
+ 1.011,
+ -8.972,
+ 1.1,
+ -8.846,
+ 1,
+ 1.467,
+ -8.328,
+ 1.833,
+ -8.2,
+ 2.2,
+ -8.2,
+ 1,
+ 2.622,
+ -8.2,
+ 3.044,
+ -10,
+ 3.467,
+ -10,
+ 0,
+ 4.167,
+ -10
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmLB",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.167,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmRB",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.167,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairAhoge",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.067,
+ 0,
+ 0.133,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.233,
+ 0,
+ 0.267,
+ -5,
+ 0.3,
+ -5,
+ 1,
+ 0.378,
+ -5,
+ 0.456,
+ 10,
+ 0.533,
+ 10,
+ 1,
+ 0.633,
+ 10,
+ 0.733,
+ 4,
+ 0.833,
+ 4,
+ 0,
+ 4.167,
+ 4
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmA",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 4.17,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmB",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4.17,
+ 0
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/ReadMe.txt b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/ReadMe.txt
new file mode 100644
index 0000000..f0cec40
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/ReadMe.txt
@@ -0,0 +1,79 @@
+
+============================================================
+
+绀轰緥妯″瀷
+鍚嶆墽 灏� - PRO
+
+============================================================
+
+銆�璇ョず渚嬪彲鐢ㄤ簬瀛︿範鍟嗙敤鐨勬父鎴忓拰App涓父鐢ㄥ埌鐨勬墜鑷傚垏鎹㈢瓑闅惧害杈冮珮鐨勫埗浣滄柟娉曘��
+
+銆�璇ユā鍨嬭鑹茬敱鐢诲笀鍏堝磶鐪熺惔璁捐銆�
+銆�妯″瀷鐨勬墜鑷傞�氳繃娓愬彉瀹炵幇涓嶅悓閮ㄤ欢闂寸殑鍒囨崲锛岃繖绉嶅埗浣滄柟娉曠粡甯稿簲鐢ㄤ簬鍟嗙敤鐨勬父鎴忓拰App涓��
+銆�閫氳繃璇ユā鍨嬶紝鍙互瀛︿範閮ㄤ欢鐨勫垏鎹㈡潵瀹炵幇鏇翠赴瀵岀殑鍔ㄧ敾銆�
+
+銆�鍙﹀锛屽彲浠ラ�氳繃Cubism3 Viewer (for OW)鏉ユ煡鐪嬪祵鍏ュ紡鏂囦欢缁勭殑鍔ㄧ敾锛屼簡瑙e畬鏁寸殑宸ヤ綔娴佺▼涓娇鐢ㄥ埌鐨勬暟鎹粨鏋勩��
+
+
+------------------------------
+绱犳潗浣跨敤璁稿彲
+------------------------------
+
+銆�鏅�氱敤鎴蜂互鍙婂皬瑙勬ā浼佷笟鍦ㄥ悓鎰忔巿鏉冨崗璁殑鎯呭喌涓嬪彲鐢ㄤ簬鍟嗕笟鐢ㄩ�斻��
+銆�涓�/澶ц妯$殑浼佷笟鍙兘鐢ㄤ簬闈炲叕寮�鐨勫唴閮ㄨ瘯鐢ㄣ��
+銆�鍦ㄤ娇鐢ㄨ绱犳潗鏃讹紝璇风‘璁や互涓嬬殑銆愭棤鍋挎彁渚涚礌鏉愪娇鐢ㄦ巿鏉冨崗璁�戜腑鐨勨�滄巿鏉冪被鍨嬧�濄�佲�淟ive2D鍘熷垱瑙掕壊鈥濈瓑鐨勭浉鍏冲唴瀹癸紝
+銆�骞跺繀椤绘帴鍙椼�怢ive2D Cubism 绀轰緥妯″瀷鐨勪娇鐢ㄦ巿鏉冭姹傘�戜腑鐨勫埄鐢ㄦ潯浠躲��
+
+銆�鏈夊叧璁稿彲璇佺殑鏇村淇℃伅锛岃鍙傞槄浠ヤ笅椤甸潰銆�
+銆�https://www.live2d.com/zh-CHS/download/sample-data/
+
+
+------------------------------
+鍒涗綔鑰�
+------------------------------
+
+銆�鎻掔敾锛氬厛宕� 鐪熺惔銆恏ttp://senzakimakoto.com/銆�
+銆�閰嶉煶锛氬皬閲庡弸鏍戙�恏ttps://web.onoyuki.com/銆�
+銆�锛堚�婚厤闊虫暟鎹殑鍙戝竷涓洪檺瀹氬彂甯冿紝宸蹭簬2018/06/04缁撴潫銆傦級
+銆�妯″瀷锛歀ive2D
+
+
+------------------------------
+绱犳潗鍐呭
+------------------------------
+
+銆�妯″瀷鏂囦欢(cmo3) 鈥诲寘鍚墿鐞嗘ā鎷熺殑璁惧畾
+銆�琛ㄦ儏鍔ㄧ敾鏂囦欢(can3)
+銆�鍩烘湰鍔ㄧ敾鏂囦欢(can3)
+銆�宓屽叆鏂囦欢缁�(runtime鏂囦欢澶�)
+ 銉绘ā鍨嬫暟鎹�(moc3)
+ 銉昏〃鎯呮暟鎹�(exp3.json)
+ 銉诲姩浣滄暟鎹�(motion3.json)
+ 銉绘ā鍨嬭瀹氭枃浠�(model3.json)
+ 銉诲Э鍔胯瀹氭枃浠�(pose3.json)
+ 銉荤墿鐞嗘ā鎷熻瀹氭枃浠�(physics3.json)
+ 銉昏緟鍔╂樉绀虹殑鏂囦欢(cdi3.json)
+
+
+------------------------------
+鏇存柊璁板綍
+------------------------------
+銆�鈥婚厤闊虫暟鎹殑鍙戝竷宸蹭簬2018/06/04缁撴潫銆�
+
+銆恈mo3銆�
+
+銆�natori_pro_t06.cmo3
+銆�2021骞�6鏈�10鏃� 鍏紑
+
+
+銆恈an3銆�
+
+銆�natori_pro_exp_t03.can3
+銆�2021骞�6鏈�10鏃� 鍏紑
+
+
+銆恈an3銆�
+
+銆�natori_pro_motions_t03.can3
+銆�2021骞�6鏈�10鏃� 鍏紑
+
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/natori_pro_exp_t03.can3 b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/natori_pro_exp_t03.can3
new file mode 100644
index 0000000..c179d48
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/natori_pro_exp_t03.can3
Binary files differ
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/natori_pro_motions_t03.can3 b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/natori_pro_motions_t03.can3
new file mode 100644
index 0000000..1af88ba
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/natori_pro_motions_t03.can3
Binary files differ
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/natori_pro_t06.cmo3 b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/natori_pro_t06.cmo3
new file mode 100644
index 0000000..a5cce35
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/natori_pro_t06.cmo3
Binary files differ
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Angry.exp3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Angry.exp3.json
new file mode 100644
index 0000000..4505fc0
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Angry.exp3.json
@@ -0,0 +1,120 @@
+锘縶
+ "Type": "Live2D Expression",
+ "Parameters": [
+ {
+ "Id": "ParamEyeLOpen",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeLSmile",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeLForm",
+ "Value": -2,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeROpen",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeRSmile",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeRForm",
+ "Value": -2,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeBallForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLY",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRY",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLX",
+ "Value": 0.3,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRX",
+ "Value": 0.3,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLAngle",
+ "Value": -0.4,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRAngle",
+ "Value": -0.4,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLForm",
+ "Value": -1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLForm2",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRForm",
+ "Value": -1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRForm2",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamTeethOn",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamCheek",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGlassUD",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassWhite",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassHighlight",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassHighlightMove",
+ "Value": 0,
+ "Blend": "Add"
+ }
+ ]
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Blushing.exp3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Blushing.exp3.json
new file mode 100644
index 0000000..6e442c4
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Blushing.exp3.json
@@ -0,0 +1,120 @@
+锘縶
+ "Type": "Live2D Expression",
+ "Parameters": [
+ {
+ "Id": "ParamEyeLOpen",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeLSmile",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeLForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeROpen",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeRSmile",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeRForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeBallForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLY",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRY",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLX",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRX",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLAngle",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRAngle",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLForm",
+ "Value": -1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLForm2",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRForm",
+ "Value": -1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRForm2",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamTeethOn",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamCheek",
+ "Value": 1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGlassUD",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassWhite",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassHighlight",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassHighlightMove",
+ "Value": 0,
+ "Blend": "Add"
+ }
+ ]
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Normal.exp3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Normal.exp3.json
new file mode 100644
index 0000000..bca8495
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Normal.exp3.json
@@ -0,0 +1,120 @@
+锘縶
+ "Type": "Live2D Expression",
+ "Parameters": [
+ {
+ "Id": "ParamEyeLOpen",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeLSmile",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeLForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeROpen",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeRSmile",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeRForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeBallForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLY",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRY",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLX",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRX",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLAngle",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRAngle",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLForm2",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRForm2",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamTeethOn",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamCheek",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGlassUD",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassWhite",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassHighlight",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassHighlightMove",
+ "Value": 0,
+ "Blend": "Add"
+ }
+ ]
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Sad.exp3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Sad.exp3.json
new file mode 100644
index 0000000..e259551
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Sad.exp3.json
@@ -0,0 +1,120 @@
+锘縶
+ "Type": "Live2D Expression",
+ "Parameters": [
+ {
+ "Id": "ParamEyeLOpen",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeLSmile",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeLForm",
+ "Value": -2,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeROpen",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeRSmile",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeRForm",
+ "Value": -2,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeBallForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLY",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRY",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLX",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRX",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLAngle",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRAngle",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLForm",
+ "Value": -1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLForm2",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRForm",
+ "Value": -1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRForm2",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamTeethOn",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamCheek",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGlassUD",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassWhite",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassHighlight",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassHighlightMove",
+ "Value": 0,
+ "Blend": "Add"
+ }
+ ]
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Smile.exp3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Smile.exp3.json
new file mode 100644
index 0000000..e431584
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Smile.exp3.json
@@ -0,0 +1,120 @@
+锘縶
+ "Type": "Live2D Expression",
+ "Parameters": [
+ {
+ "Id": "ParamEyeLOpen",
+ "Value": -1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeLSmile",
+ "Value": 1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeLForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeROpen",
+ "Value": -1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeRSmile",
+ "Value": 1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeRForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeBallForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLY",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRY",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLX",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRX",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLAngle",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRAngle",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLForm",
+ "Value": 1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLForm2",
+ "Value": 1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRForm2",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamTeethOn",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamCheek",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGlassUD",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassWhite",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassHighlight",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassHighlightMove",
+ "Value": 0,
+ "Blend": "Add"
+ }
+ ]
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Surprised.exp3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Surprised.exp3.json
new file mode 100644
index 0000000..4392187
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/Surprised.exp3.json
@@ -0,0 +1,120 @@
+锘縶
+ "Type": "Live2D Expression",
+ "Parameters": [
+ {
+ "Id": "ParamEyeLOpen",
+ "Value": 0.3,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeLSmile",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeLForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeROpen",
+ "Value": 0.3,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeRSmile",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeRForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeBallForm",
+ "Value": -1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLY",
+ "Value": 0.2,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRY",
+ "Value": 0.2,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLX",
+ "Value": -0.1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRX",
+ "Value": -0.1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLAngle",
+ "Value": 0.1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRAngle",
+ "Value": 0.1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLForm2",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRForm2",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamTeethOn",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamCheek",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGlassUD",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassWhite",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassHighlight",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassHighlightMove",
+ "Value": 0,
+ "Blend": "Add"
+ }
+ ]
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/exp_01.exp3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/exp_01.exp3.json
new file mode 100644
index 0000000..6f0a8dc
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/exp_01.exp3.json
@@ -0,0 +1,120 @@
+锘縶
+ "Type": "Live2D Expression",
+ "Parameters": [
+ {
+ "Id": "ParamEyeLOpen",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeLSmile",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeLForm",
+ "Value": 3,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeROpen",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeRSmile",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeRForm",
+ "Value": 3,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeBallForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLY",
+ "Value": -0.1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRY",
+ "Value": -0.1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLX",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRX",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLAngle",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRAngle",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLForm2",
+ "Value": 1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRForm2",
+ "Value": 1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamTeethOn",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamCheek",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGlassUD",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassWhite",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassHighlight",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassHighlightMove",
+ "Value": 0,
+ "Blend": "Add"
+ }
+ ]
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/exp_02.exp3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/exp_02.exp3.json
new file mode 100644
index 0000000..f3ccbad
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/exp_02.exp3.json
@@ -0,0 +1,120 @@
+锘縶
+ "Type": "Live2D Expression",
+ "Parameters": [
+ {
+ "Id": "ParamEyeLOpen",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeLSmile",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeLForm",
+ "Value": 1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeROpen",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeRSmile",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeRForm",
+ "Value": 1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeBallForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLY",
+ "Value": 0.1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRY",
+ "Value": 0.1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLX",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRX",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLAngle",
+ "Value": 0.1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRAngle",
+ "Value": 0.1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLForm2",
+ "Value": 1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRForm2",
+ "Value": 1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamTeethOn",
+ "Value": 1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamCheek",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGlassUD",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassWhite",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassHighlight",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassHighlightMove",
+ "Value": 0,
+ "Blend": "Add"
+ }
+ ]
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/exp_03.exp3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/exp_03.exp3.json
new file mode 100644
index 0000000..fea1216
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/exp_03.exp3.json
@@ -0,0 +1,120 @@
+锘縶
+ "Type": "Live2D Expression",
+ "Parameters": [
+ {
+ "Id": "ParamEyeLOpen",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeLSmile",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeLForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeROpen",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeRSmile",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeRForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeBallForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLY",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRY",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLX",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRX",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLAngle",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRAngle",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLForm",
+ "Value": -1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLForm2",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRForm",
+ "Value": -1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRForm2",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamTeethOn",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamCheek",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGlassUD",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassWhite",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassHighlight",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassHighlightMove",
+ "Value": 0,
+ "Blend": "Add"
+ }
+ ]
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/exp_04.exp3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/exp_04.exp3.json
new file mode 100644
index 0000000..fdef0d7
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/exp_04.exp3.json
@@ -0,0 +1,120 @@
+锘縶
+ "Type": "Live2D Expression",
+ "Parameters": [
+ {
+ "Id": "ParamEyeLOpen",
+ "Value": 0.2,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeLSmile",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeLForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeROpen",
+ "Value": 0.2,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeRSmile",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeRForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeBallForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLY",
+ "Value": 0.2,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRY",
+ "Value": 0.2,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLX",
+ "Value": -0.1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRX",
+ "Value": -0.1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLAngle",
+ "Value": 0.1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRAngle",
+ "Value": 0.1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLForm2",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRForm2",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamTeethOn",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamCheek",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGlassUD",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassWhite",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassHighlight",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassHighlightMove",
+ "Value": 0,
+ "Blend": "Add"
+ }
+ ]
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/exp_05.exp3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/exp_05.exp3.json
new file mode 100644
index 0000000..6f8515f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/exp/exp_05.exp3.json
@@ -0,0 +1,120 @@
+锘縶
+ "Type": "Live2D Expression",
+ "Parameters": [
+ {
+ "Id": "ParamEyeLOpen",
+ "Value": -1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeLSmile",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeLForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeROpen",
+ "Value": -1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeRSmile",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeRForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamEyeBallForm",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLY",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRY",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLX",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRX",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLAngle",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRAngle",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLForm",
+ "Value": 1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowLForm2",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRForm",
+ "Value": 1,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamBrowRForm2",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamTeethOn",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamCheek",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGlassUD",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassWhite",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassHighlight",
+ "Value": 0,
+ "Blend": "Add"
+ },
+ {
+ "Id": "ParamGrassHighlightMove",
+ "Value": 0,
+ "Blend": "Add"
+ }
+ ]
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_00.motion3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_00.motion3.json
new file mode 100644
index 0000000..534066d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_00.motion3.json
@@ -0,0 +1,1432 @@
+{
+ "Version": 3,
+ "Meta": {
+ "Duration": 7.97,
+ "Fps": 30.0,
+ "Loop": true,
+ "AreBeziersRestricted": true,
+ "CurveCount": 93,
+ "TotalSegmentCount": 78,
+ "TotalPointCount": 362,
+ "UserDataCount": 0,
+ "TotalUserDataSize": 0
+ },
+ "Curves": [
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.611,
+ 0,
+ 1.222,
+ 5,
+ 1.833,
+ 5,
+ 1,
+ 2.4,
+ 5,
+ 2.967,
+ 0,
+ 3.533,
+ 0,
+ 1,
+ 4.278,
+ 0,
+ 5.022,
+ 4,
+ 5.767,
+ 4,
+ 1,
+ 6.5,
+ 4,
+ 7.233,
+ 0.119,
+ 7.967,
+ 0.003
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLOpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 2.656,
+ 1,
+ 5.311,
+ 1,
+ 7.967,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeROpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 2.656,
+ 1,
+ 5.311,
+ 1,
+ 7.967,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamTeethOn",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamCheek",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGlassUD",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassWhite",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassHighlight",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassHighlightMove",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.589,
+ 1,
+ 1.178,
+ -1,
+ 1.767,
+ -1,
+ 1,
+ 2.333,
+ -1,
+ 2.9,
+ 1,
+ 3.467,
+ 1,
+ 1,
+ 4.2,
+ 1,
+ 4.933,
+ -1,
+ 5.667,
+ -1,
+ 1,
+ 6.433,
+ -1,
+ 7.2,
+ 0.943,
+ 7.967,
+ 0.999
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWaistAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyPosition",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBreath",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.6,
+ 0,
+ 1.2,
+ 1,
+ 1.8,
+ 1,
+ 1,
+ 2.367,
+ 1,
+ 2.933,
+ 0,
+ 3.5,
+ 0,
+ 1,
+ 4.244,
+ 0,
+ 4.989,
+ 1,
+ 5.733,
+ 1,
+ 1,
+ 6.478,
+ 1,
+ 7.222,
+ 0.029,
+ 7.967,
+ 0.001
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamLeftShoulderUp",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamRightShoulderUp",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllRotate",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairFront",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairSide",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairBack",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairFrontFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairSideFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairBackFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamJacket",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamChainWaist",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchSwingA1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchSwingA2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchAX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBSwitch",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBOpen",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL04",
+ "FadeInTime": 0.2,
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR04",
+ "FadeInTime": 0.2,
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand01Roll",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll3",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR02",
+ "Segments": [
+ 0,
+ -30,
+ 1,
+ 2.656,
+ -30,
+ 5.311,
+ -0.25,
+ 7.967,
+ -0.002
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCLHandRoll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDLHand03Roll",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER04",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand04Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand04Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand06",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand06Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand06Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 2.656,
+ 0,
+ 5.311,
+ 0,
+ 7.967,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartWatchA",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 7.97,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartWatchB",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 7.97,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmAL",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 7.97,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmAR",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 7.97,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmBR",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 7.97,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmCL",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 7.97,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmDL",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 7.97,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmER",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 7.97,
+ 0
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_01.motion3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_01.motion3.json
new file mode 100644
index 0000000..066dbfc
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_01.motion3.json
@@ -0,0 +1,1971 @@
+锘縶
+ "Version": 3,
+ "Meta": {
+ "Duration": 5,
+ "Fps": 30.0,
+ "Loop": true,
+ "AreBeziersRestricted": true,
+ "CurveCount": 94,
+ "TotalSegmentCount": 228,
+ "TotalPointCount": 587,
+ "UserDataCount": 0,
+ "TotalUserDataSize": 0
+ },
+ "Curves": [
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.511,
+ 0,
+ 0.856,
+ 0,
+ 1.2,
+ 0,
+ 1,
+ 1.856,
+ 0,
+ 2.511,
+ -0.883,
+ 3.167,
+ -4,
+ 1,
+ 3.578,
+ -5.955,
+ 3.989,
+ -8.02,
+ 4.4,
+ -8.02,
+ 0,
+ 5,
+ -8.02
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.189,
+ 0,
+ 0.211,
+ 0,
+ 0.233,
+ 0,
+ 1,
+ 0.378,
+ 0,
+ 0.522,
+ 2.709,
+ 0.667,
+ 4.062,
+ 1,
+ 0.856,
+ 5.832,
+ 1.044,
+ 6,
+ 1.233,
+ 6,
+ 1,
+ 1.878,
+ 6,
+ 2.522,
+ -12,
+ 3.167,
+ -12,
+ 1,
+ 3.589,
+ -12,
+ 4.011,
+ -9,
+ 4.433,
+ -9,
+ 0,
+ 5,
+ -9
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.222,
+ 0,
+ 0.278,
+ 0,
+ 0.333,
+ 0,
+ 1,
+ 0.611,
+ 0,
+ 0.889,
+ 8,
+ 1.167,
+ 8,
+ 1,
+ 1.178,
+ 8,
+ 1.189,
+ 8,
+ 1.2,
+ 8,
+ 1,
+ 1.856,
+ 8,
+ 2.511,
+ -14,
+ 3.167,
+ -14,
+ 1,
+ 3.467,
+ -14,
+ 3.767,
+ -13,
+ 4.067,
+ -13,
+ 0,
+ 5,
+ -13
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLOpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 1,
+ 0.722,
+ 1,
+ 1.278,
+ 1,
+ 1.833,
+ 1,
+ 1,
+ 1.933,
+ 1,
+ 2.033,
+ 0,
+ 2.133,
+ 0,
+ 1,
+ 2.478,
+ 0,
+ 2.822,
+ 0,
+ 3.167,
+ 0,
+ 1,
+ 3.322,
+ 0,
+ 3.478,
+ 0.7,
+ 3.633,
+ 0.7,
+ 0,
+ 5,
+ 0.7
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLSmile",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLForm",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeROpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 1,
+ 0.722,
+ 1,
+ 1.278,
+ 1,
+ 1.833,
+ 1,
+ 1,
+ 1.933,
+ 1,
+ 2.033,
+ 0,
+ 2.133,
+ 0,
+ 1,
+ 2.478,
+ 0,
+ 2.822,
+ 0,
+ 3.167,
+ 0,
+ 1,
+ 3.322,
+ 0,
+ 3.478,
+ 0.7,
+ 3.633,
+ 0.7,
+ 0,
+ 5,
+ 0.7
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRSmile",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRForm",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.233,
+ 0,
+ 0.3,
+ 0,
+ 0.367,
+ 0,
+ 1,
+ 0.656,
+ 0,
+ 0.944,
+ 0,
+ 1.233,
+ 0,
+ 1,
+ 1.878,
+ 0,
+ 2.522,
+ -0.3,
+ 3.167,
+ -0.3,
+ 0,
+ 5,
+ -0.3
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.233,
+ 0,
+ 0.3,
+ 0,
+ 0.367,
+ 0,
+ 1,
+ 0.656,
+ 0,
+ 0.944,
+ -0.2,
+ 1.233,
+ -0.2,
+ 1,
+ 1.878,
+ -0.2,
+ 2.522,
+ -0.2,
+ 3.167,
+ -0.2,
+ 0,
+ 5,
+ -0.2
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLY",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRY",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLX",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRX",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLAngle",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRAngle",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm2",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm2",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamTeethOn",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamCheek",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGlassUD",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassWhite",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassHighlight",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassHighlightMove",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX",
+ "Segments": [
+ 0,
+ -1,
+ 1,
+ 0.056,
+ -1,
+ 0.111,
+ -1,
+ 0.167,
+ -1,
+ 1,
+ 0.489,
+ -1,
+ 0.811,
+ 0,
+ 1.133,
+ 0,
+ 1,
+ 1.778,
+ 0,
+ 2.422,
+ -3,
+ 3.067,
+ -3,
+ 0,
+ 5,
+ -3
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.256,
+ 0,
+ 0.344,
+ 1.011,
+ 0.433,
+ 1.616,
+ 1,
+ 0.6,
+ 2.751,
+ 0.767,
+ 3,
+ 0.933,
+ 3,
+ 1,
+ 1,
+ 3,
+ 1.067,
+ 3,
+ 1.133,
+ 3,
+ 1,
+ 1.778,
+ 3,
+ 2.422,
+ -2,
+ 3.067,
+ -2,
+ 1,
+ 3.511,
+ -2,
+ 3.956,
+ -1.5,
+ 4.4,
+ -1.5,
+ 0,
+ 5,
+ -1.5
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWaistAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyPosition",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBreath",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.467,
+ 0,
+ 0.767,
+ 1,
+ 1.067,
+ 1,
+ 1,
+ 1.089,
+ 1,
+ 1.111,
+ 1,
+ 1.133,
+ 1,
+ 1,
+ 1.911,
+ 1,
+ 2.689,
+ 0,
+ 3.467,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamLeftShoulderUp",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.489,
+ 0,
+ 0.811,
+ 0,
+ 1.133,
+ 0,
+ 1,
+ 1.778,
+ 0,
+ 2.422,
+ -9,
+ 3.067,
+ -9,
+ 0,
+ 5,
+ -9
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamRightShoulderUp",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.489,
+ 0,
+ 0.811,
+ 0,
+ 1.133,
+ 0,
+ 1,
+ 1.778,
+ 0,
+ 2.422,
+ 8,
+ 3.067,
+ 8,
+ 0,
+ 5,
+ 8
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllRotate",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairFront",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairSide",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairBack",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairFrontFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairSideFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairBackFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamJacket",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamChainWaist",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchSwingA1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchSwingA2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchAX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBSwitch",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBOpen",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.544,
+ 0,
+ 0.922,
+ 1,
+ 1.3,
+ 1,
+ 1,
+ 1.911,
+ 1,
+ 2.522,
+ 0,
+ 3.133,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.556,
+ 0,
+ 0.944,
+ -1,
+ 1.333,
+ -1,
+ 1,
+ 1.944,
+ -1,
+ 2.556,
+ 0,
+ 3.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.556,
+ 0,
+ 0.944,
+ 0,
+ 1.333,
+ 0,
+ 1,
+ 1.944,
+ 0,
+ 2.556,
+ -3,
+ 3.167,
+ -3,
+ 0,
+ 5,
+ -3
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL04",
+ "FadeInTime": 0.2,
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR02",
+ "Segments": [
+ 0,
+ -40,
+ 1,
+ 0.056,
+ -40,
+ 0.111,
+ -40,
+ 0.167,
+ -40,
+ 0,
+ 5,
+ -40
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR03",
+ "Segments": [
+ 0,
+ -9,
+ 1,
+ 0.056,
+ -9,
+ 0.111,
+ -9,
+ 0.167,
+ -9,
+ 0,
+ 5,
+ -9
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR04",
+ "FadeInTime": 0.2,
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand01Roll",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll3",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCLHandRoll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDLHand03Roll",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.2,
+ 0,
+ 0.233,
+ 0,
+ 0.267,
+ 0,
+ 1,
+ 0.6,
+ 0,
+ 0.933,
+ 6,
+ 1.267,
+ 6,
+ 1,
+ 1.9,
+ 6,
+ 2.533,
+ -2,
+ 3.167,
+ -2,
+ 0,
+ 5,
+ -2
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.544,
+ 0,
+ 0.922,
+ 3,
+ 1.3,
+ 3,
+ 1,
+ 1.933,
+ 3,
+ 2.567,
+ -2,
+ 3.2,
+ -2,
+ 1,
+ 3.589,
+ -2,
+ 3.978,
+ -1,
+ 4.367,
+ -1,
+ 0,
+ 5,
+ -1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.211,
+ 0,
+ 0.256,
+ 0.2,
+ 0.3,
+ 0.2,
+ 1,
+ 0.633,
+ 0.2,
+ 0.967,
+ 0,
+ 1.3,
+ 0,
+ 1,
+ 1.933,
+ 0,
+ 2.567,
+ 2,
+ 3.2,
+ 2,
+ 1,
+ 3.589,
+ 2,
+ 3.978,
+ 1,
+ 4.367,
+ 1,
+ 0,
+ 5,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER04",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.533,
+ 0,
+ 0.9,
+ 0,
+ 1.267,
+ 0,
+ 1,
+ 1.9,
+ 0,
+ 2.533,
+ 2,
+ 3.167,
+ 2,
+ 0,
+ 5,
+ 2
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand04",
+ "FadeInTime": 0.2,
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 0,
+ 5,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand04Roll1",
+ "Segments": [
+ 0,
+ 9,
+ 1,
+ 0.056,
+ 9,
+ 0.111,
+ 9,
+ 0.167,
+ 9,
+ 1,
+ 0.556,
+ 9,
+ 0.944,
+ 11,
+ 1.333,
+ 11,
+ 1,
+ 2.067,
+ 11,
+ 2.8,
+ -23,
+ 3.533,
+ -23,
+ 0,
+ 5,
+ -23
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand04Roll2",
+ "Segments": [
+ 0,
+ 9,
+ 1,
+ 0.056,
+ 9,
+ 0.111,
+ 9,
+ 0.167,
+ 9,
+ 1,
+ 0.556,
+ 9,
+ 0.944,
+ 14,
+ 1.333,
+ 14,
+ 1,
+ 2.067,
+ 14,
+ 2.8,
+ -14,
+ 3.533,
+ -14,
+ 0,
+ 5,
+ -14
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand06",
+ "FadeInTime": 0.2,
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand06Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand06Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartWatchA",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 5,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartWatchB",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmAL",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 5,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmAR",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmBR",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmCL",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmDL",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmER",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 5,
+ 1
+ ]
+ }
+ ]
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_02.motion3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_02.motion3.json
new file mode 100644
index 0000000..6cff191
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_02.motion3.json
@@ -0,0 +1,2182 @@
+锘縶
+ "Version": 3,
+ "Meta": {
+ "Duration": 5.5,
+ "Fps": 30.0,
+ "Loop": true,
+ "AreBeziersRestricted": true,
+ "CurveCount": 93,
+ "TotalSegmentCount": 262,
+ "TotalPointCount": 690,
+ "UserDataCount": 0,
+ "TotalUserDataSize": 0
+ },
+ "Curves": [
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.822,
+ 0,
+ 1.478,
+ 0,
+ 2.133,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.222,
+ 0,
+ 0.278,
+ 0,
+ 0.333,
+ 0,
+ 1,
+ 0.544,
+ 0,
+ 0.756,
+ 10.2,
+ 0.967,
+ 10.2,
+ 1,
+ 1.356,
+ 10.2,
+ 1.744,
+ -30,
+ 2.133,
+ -30,
+ 1,
+ 2.267,
+ -30,
+ 2.4,
+ -27.952,
+ 2.533,
+ -27.952,
+ 1,
+ 2.867,
+ -27.952,
+ 3.2,
+ -30,
+ 3.533,
+ -30,
+ 1,
+ 3.822,
+ -30,
+ 4.111,
+ 2.617,
+ 4.4,
+ 2.617,
+ 1,
+ 4.533,
+ 2.617,
+ 4.667,
+ 0,
+ 4.8,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLOpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 1,
+ 0.567,
+ 1,
+ 0.967,
+ 1,
+ 1.367,
+ 1,
+ 1,
+ 1.478,
+ 1,
+ 1.589,
+ 0,
+ 1.7,
+ 0,
+ 1,
+ 2.4,
+ 0,
+ 3.1,
+ 0,
+ 3.8,
+ 0,
+ 1,
+ 3.944,
+ 0,
+ 4.089,
+ 1,
+ 4.233,
+ 1,
+ 0,
+ 5.5,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeROpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 1,
+ 0.567,
+ 1,
+ 0.967,
+ 1,
+ 1.367,
+ 1,
+ 1,
+ 1.478,
+ 1,
+ 1.589,
+ 0,
+ 1.7,
+ 0,
+ 1,
+ 2.4,
+ 0,
+ 3.1,
+ 0,
+ 3.8,
+ 0,
+ 1,
+ 3.944,
+ 0,
+ 4.089,
+ 1,
+ 4.233,
+ 1,
+ 0,
+ 5.5,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamTeethOn",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamCheek",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGlassUD",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassWhite",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassHighlight",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassHighlightMove",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.411,
+ 0,
+ 0.656,
+ 1.24,
+ 0.9,
+ 1.24,
+ 1,
+ 1.3,
+ 1.24,
+ 1.7,
+ -5,
+ 2.1,
+ -5.92,
+ 1,
+ 2.533,
+ -6.917,
+ 2.967,
+ -6.78,
+ 3.4,
+ -6.78,
+ 1,
+ 3.856,
+ -6.78,
+ 4.311,
+ 0,
+ 4.767,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.411,
+ 0,
+ 0.656,
+ 1,
+ 0.9,
+ 1,
+ 1,
+ 1.311,
+ 1,
+ 1.722,
+ -1.464,
+ 2.133,
+ -2,
+ 1,
+ 2.567,
+ -2.564,
+ 3,
+ -2.5,
+ 3.433,
+ -2.5,
+ 1,
+ 3.878,
+ -2.5,
+ 4.322,
+ 0,
+ 4.767,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWaistAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.422,
+ 0,
+ 0.678,
+ 2,
+ 0.933,
+ 2,
+ 1,
+ 1.333,
+ 2,
+ 1.733,
+ -1.026,
+ 2.133,
+ -2,
+ 1,
+ 2.567,
+ -3.055,
+ 3,
+ -3,
+ 3.433,
+ -3,
+ 1,
+ 3.878,
+ -3,
+ 4.322,
+ 0,
+ 4.767,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyPosition",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.411,
+ 0,
+ 0.656,
+ -1,
+ 0.9,
+ -1,
+ 1,
+ 1.289,
+ -1,
+ 1.678,
+ 9.133,
+ 2.067,
+ 9.133,
+ 1,
+ 2.178,
+ 9.133,
+ 2.289,
+ 9,
+ 2.4,
+ 9,
+ 1,
+ 2.656,
+ 9,
+ 2.911,
+ 9,
+ 3.167,
+ 9,
+ 1,
+ 3.478,
+ 9,
+ 3.789,
+ -0.5,
+ 4.1,
+ -0.5,
+ 1,
+ 4.422,
+ -0.5,
+ 4.744,
+ 0,
+ 5.067,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBreath",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.411,
+ 0,
+ 0.656,
+ 1,
+ 0.9,
+ 1,
+ 1,
+ 1.733,
+ 1,
+ 2.567,
+ 0,
+ 3.4,
+ 0,
+ 1,
+ 3.633,
+ 0,
+ 3.867,
+ 0.6,
+ 4.1,
+ 0.6,
+ 1,
+ 4.333,
+ 0.6,
+ 4.567,
+ 0,
+ 4.8,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamLeftShoulderUp",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.422,
+ 0,
+ 0.678,
+ 0,
+ 0.933,
+ 0,
+ 1,
+ 1.322,
+ 0,
+ 1.711,
+ -9,
+ 2.1,
+ -9,
+ 1,
+ 2.989,
+ -9,
+ 3.878,
+ 0,
+ 4.767,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamRightShoulderUp",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.422,
+ 0,
+ 0.678,
+ 0,
+ 0.933,
+ 0,
+ 1,
+ 1.322,
+ 0,
+ 1.711,
+ 8,
+ 2.1,
+ 8,
+ 1,
+ 2.989,
+ 8,
+ 3.878,
+ 0,
+ 4.767,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllRotate",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairFront",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairSide",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairBack",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairFrontFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairSideFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairBackFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamJacket",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamChainWaist",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchSwingA1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchSwingA2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchAX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBSwitch",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBOpen",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL02",
+ "Segments": [
+ 0,
+ -28,
+ 1,
+ 0.056,
+ -28,
+ 0.111,
+ -28,
+ 0.167,
+ -28,
+ 0,
+ 5.5,
+ -28
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL03",
+ "Segments": [
+ 0,
+ 19,
+ 1,
+ 0.056,
+ 19,
+ 0.111,
+ 19,
+ 0.167,
+ 19,
+ 0,
+ 5.5,
+ 19
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL04",
+ "FadeInTime": 0.2,
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.422,
+ 0,
+ 0.678,
+ 0.8,
+ 0.933,
+ 0.8,
+ 1,
+ 1.322,
+ 0.8,
+ 1.711,
+ -3,
+ 2.1,
+ -3,
+ 1,
+ 2.556,
+ -3,
+ 3.011,
+ -3,
+ 3.467,
+ -3,
+ 1,
+ 3.789,
+ -3,
+ 4.111,
+ 0.8,
+ 4.433,
+ 0.8,
+ 1,
+ 4.589,
+ 0.8,
+ 4.744,
+ 0,
+ 4.9,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.433,
+ 0,
+ 0.7,
+ 0.8,
+ 0.967,
+ 0.8,
+ 1,
+ 1.356,
+ 0.8,
+ 1.744,
+ -6,
+ 2.133,
+ -6,
+ 1,
+ 2.589,
+ -6,
+ 3.044,
+ -6,
+ 3.5,
+ -6,
+ 1,
+ 3.822,
+ -6,
+ 4.144,
+ 1,
+ 4.467,
+ 1,
+ 1,
+ 4.622,
+ 1,
+ 4.778,
+ 0,
+ 4.933,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.433,
+ 0,
+ 0.7,
+ 0,
+ 0.967,
+ 0,
+ 1,
+ 1.356,
+ 0,
+ 1.744,
+ -10,
+ 2.133,
+ -10,
+ 1,
+ 2.589,
+ -10,
+ 3.044,
+ -10,
+ 3.5,
+ -10,
+ 1,
+ 3.822,
+ -10,
+ 4.144,
+ 1,
+ 4.467,
+ 1,
+ 1,
+ 4.622,
+ 1,
+ 4.778,
+ 0,
+ 4.933,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR04",
+ "FadeInTime": 0.2,
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.311,
+ 0,
+ 0.622,
+ 0,
+ 0.933,
+ 0,
+ 1,
+ 0.944,
+ 0,
+ 0.956,
+ -1,
+ 0.967,
+ -1,
+ 1,
+ 2.111,
+ -1,
+ 3.256,
+ -1,
+ 4.4,
+ -1,
+ 1,
+ 4.411,
+ -1,
+ 4.422,
+ 0,
+ 4.433,
+ 0,
+ 1,
+ 4.733,
+ 0,
+ 5.033,
+ 0,
+ 5.333,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand01Roll",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll3",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR01",
+ "Segments": [
+ 0,
+ 3,
+ 1,
+ 0.056,
+ 3,
+ 0.111,
+ 3,
+ 0.167,
+ 3,
+ 1,
+ 0.422,
+ 3,
+ 0.678,
+ 5,
+ 0.933,
+ 5,
+ 1,
+ 1.311,
+ 5,
+ 1.689,
+ -6,
+ 2.067,
+ -6,
+ 1,
+ 2.522,
+ -6,
+ 2.978,
+ -6,
+ 3.433,
+ -6,
+ 1,
+ 3.922,
+ -6,
+ 4.411,
+ 3,
+ 4.9,
+ 3,
+ 0,
+ 5.5,
+ 3
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR02",
+ "Segments": [
+ 0,
+ -29,
+ 1,
+ 0.056,
+ -29,
+ 0.111,
+ -29,
+ 0.167,
+ -29,
+ 1,
+ 0.444,
+ -29,
+ 0.722,
+ -30,
+ 1,
+ -30,
+ 1,
+ 1.367,
+ -30,
+ 1.733,
+ -21,
+ 2.1,
+ -21,
+ 1,
+ 2.556,
+ -21,
+ 3.011,
+ -21,
+ 3.467,
+ -21,
+ 1,
+ 3.956,
+ -21,
+ 4.444,
+ -29,
+ 4.933,
+ -29,
+ 0,
+ 5.5,
+ -29
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR03",
+ "Segments": [
+ 0,
+ 11,
+ 1,
+ 0.056,
+ 11,
+ 0.111,
+ 11,
+ 0.167,
+ 11,
+ 1,
+ 0.444,
+ 11,
+ 0.722,
+ 9,
+ 1,
+ 9,
+ 1,
+ 1.367,
+ 9,
+ 1.733,
+ 28,
+ 2.1,
+ 28,
+ 1,
+ 2.556,
+ 28,
+ 3.011,
+ 28,
+ 3.467,
+ 28,
+ 1,
+ 3.956,
+ 28,
+ 4.444,
+ 11,
+ 4.933,
+ 11,
+ 0,
+ 5.5,
+ 11
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCLHandRoll1",
+ "Segments": [
+ 0,
+ -18,
+ 1,
+ 0.056,
+ -18,
+ 0.111,
+ -18,
+ 0.167,
+ -18,
+ 1,
+ 0.456,
+ -18,
+ 0.744,
+ -18.077,
+ 1.033,
+ -21,
+ 1,
+ 1.411,
+ -24.822,
+ 1.789,
+ -30,
+ 2.167,
+ -30,
+ 1,
+ 2.622,
+ -30,
+ 3.078,
+ -30,
+ 3.533,
+ -30,
+ 1,
+ 4,
+ -30,
+ 4.467,
+ -18,
+ 4.933,
+ -18,
+ 0,
+ 5.5,
+ -18
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDLHand03Roll",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER04",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand04",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 0,
+ 5.5,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand04Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand04Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand06Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand06Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartWatchA",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 5.5,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartWatchB",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmAL",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmAR",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 5.5,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmBR",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmCL",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 5.5,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmDL",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmER",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5.5,
+ 0
+ ]
+ }
+ ]
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_03.motion3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_03.motion3.json
new file mode 100644
index 0000000..bc24720
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_03.motion3.json
@@ -0,0 +1,2596 @@
+锘縶
+ "Version": 3,
+ "Meta": {
+ "Duration": 5,
+ "Fps": 30.0,
+ "Loop": true,
+ "AreBeziersRestricted": true,
+ "CurveCount": 98,
+ "TotalSegmentCount": 318,
+ "TotalPointCount": 853,
+ "UserDataCount": 0,
+ "TotalUserDataSize": 0
+ },
+ "Curves": [
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.322,
+ 0,
+ 0.478,
+ 0,
+ 0.633,
+ 0,
+ 1,
+ 1.089,
+ 0,
+ 1.544,
+ -10.65,
+ 2,
+ -14,
+ 1,
+ 2.211,
+ -15.553,
+ 2.422,
+ -15,
+ 2.633,
+ -15,
+ 1,
+ 2.844,
+ -15,
+ 3.056,
+ 0,
+ 3.267,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.278,
+ 0,
+ 0.389,
+ 1,
+ 0.5,
+ 1,
+ 1,
+ 0.744,
+ 1,
+ 0.989,
+ -16.215,
+ 1.233,
+ -16.215,
+ 1,
+ 1.433,
+ -16.215,
+ 1.633,
+ -11.8,
+ 1.833,
+ -11.8,
+ 1,
+ 2.133,
+ -11.8,
+ 2.433,
+ -14.898,
+ 2.733,
+ -14.898,
+ 1,
+ 3.056,
+ -14.898,
+ 3.378,
+ 0,
+ 3.7,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.189,
+ 0,
+ 0.378,
+ 0,
+ 0.567,
+ 0,
+ 1,
+ 0.867,
+ 0,
+ 1.167,
+ 10,
+ 1.467,
+ 10,
+ 1,
+ 1.8,
+ 10,
+ 2.133,
+ 10,
+ 2.467,
+ 10,
+ 1,
+ 2.733,
+ 10,
+ 3,
+ 0,
+ 3.267,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLOpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 1,
+ 0.378,
+ 1,
+ 0.589,
+ 1,
+ 0.8,
+ 1,
+ 1,
+ 0.833,
+ 1,
+ 0.867,
+ 0,
+ 0.9,
+ 0,
+ 1,
+ 0.922,
+ 0,
+ 0.944,
+ 0,
+ 0.967,
+ 0,
+ 1,
+ 1.044,
+ 0,
+ 1.122,
+ 1,
+ 1.2,
+ 1,
+ 1,
+ 1.711,
+ 1,
+ 2.222,
+ 1,
+ 2.733,
+ 1,
+ 1,
+ 2.767,
+ 1,
+ 2.8,
+ 0,
+ 2.833,
+ 0,
+ 1,
+ 2.856,
+ 0,
+ 2.878,
+ 0,
+ 2.9,
+ 0,
+ 1,
+ 2.978,
+ 0,
+ 3.056,
+ 1,
+ 3.133,
+ 1,
+ 0,
+ 5,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeROpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 1,
+ 0.378,
+ 1,
+ 0.589,
+ 1,
+ 0.8,
+ 1,
+ 1,
+ 0.833,
+ 1,
+ 0.867,
+ 0,
+ 0.9,
+ 0,
+ 1,
+ 0.922,
+ 0,
+ 0.944,
+ 0,
+ 0.967,
+ 0,
+ 1,
+ 1.044,
+ 0,
+ 1.122,
+ 1,
+ 1.2,
+ 1,
+ 1,
+ 1.711,
+ 1,
+ 2.222,
+ 1,
+ 2.733,
+ 1,
+ 1,
+ 2.767,
+ 1,
+ 2.8,
+ 0,
+ 2.833,
+ 0,
+ 1,
+ 2.856,
+ 0,
+ 2.878,
+ 0,
+ 2.9,
+ 0,
+ 1,
+ 2.978,
+ 0,
+ 3.056,
+ 1,
+ 3.133,
+ 1,
+ 0,
+ 5,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.322,
+ 0,
+ 0.478,
+ 0,
+ 0.633,
+ 0,
+ 1,
+ 0.833,
+ 0,
+ 1.033,
+ -0.5,
+ 1.233,
+ -0.5,
+ 1,
+ 1.7,
+ -0.5,
+ 2.167,
+ -0.5,
+ 2.633,
+ -0.5,
+ 1,
+ 2.833,
+ -0.5,
+ 3.033,
+ 0,
+ 3.233,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.322,
+ 0,
+ 0.478,
+ 0,
+ 0.633,
+ 0,
+ 1,
+ 0.833,
+ 0,
+ 1.033,
+ -0.2,
+ 1.233,
+ -0.2,
+ 1,
+ 1.7,
+ -0.2,
+ 2.167,
+ -0.2,
+ 2.633,
+ -0.2,
+ 1,
+ 2.833,
+ -0.2,
+ 3.033,
+ 0,
+ 3.233,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLY",
+ "Segments": [
+ 0,
+ -0.1,
+ 1,
+ 0.056,
+ -0.1,
+ 0.111,
+ -0.1,
+ 0.167,
+ -0.1,
+ 0,
+ 5,
+ -0.1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRY",
+ "Segments": [
+ 0,
+ -0.1,
+ 1,
+ 0.056,
+ -0.1,
+ 0.111,
+ -0.1,
+ 0.167,
+ -0.1,
+ 0,
+ 5,
+ -0.1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 0,
+ 5,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm2",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 0,
+ 5,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 0,
+ 5,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm2",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 0,
+ 5,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamTeethOn",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamCheek",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGlassUD",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassWhite",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassHighlight",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassHighlightMove",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.333,
+ 0,
+ 0.5,
+ 0.5,
+ 0.667,
+ 0.5,
+ 1,
+ 0.922,
+ 0.5,
+ 1.178,
+ -3,
+ 1.433,
+ -3,
+ 1,
+ 1.789,
+ -3,
+ 2.144,
+ -3,
+ 2.5,
+ -3,
+ 1,
+ 2.722,
+ -3,
+ 2.944,
+ 0,
+ 3.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.333,
+ 0,
+ 0.5,
+ -0.538,
+ 0.667,
+ -1,
+ 1,
+ 0.878,
+ -1.585,
+ 1.089,
+ -1.707,
+ 1.3,
+ -1.707,
+ 1,
+ 1.711,
+ -1.707,
+ 2.122,
+ 0.274,
+ 2.533,
+ 0.274,
+ 1,
+ 2.767,
+ 0.274,
+ 3,
+ 0,
+ 3.233,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWaistAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyPosition",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.356,
+ 0,
+ 0.544,
+ 0,
+ 0.733,
+ 0,
+ 1,
+ 1.489,
+ 0,
+ 2.244,
+ 0,
+ 3,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBreath",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.333,
+ 0,
+ 0.5,
+ 0,
+ 0.667,
+ 0,
+ 1,
+ 0.944,
+ 0,
+ 1.222,
+ 0.42,
+ 1.5,
+ 0.5,
+ 1,
+ 1.867,
+ 0.605,
+ 2.233,
+ 0.6,
+ 2.6,
+ 0.6,
+ 1,
+ 3.111,
+ 0.6,
+ 3.622,
+ 0,
+ 4.133,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamLeftShoulderUp",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.333,
+ 0,
+ 0.5,
+ 0,
+ 0.667,
+ 0,
+ 1,
+ 0.922,
+ 0,
+ 1.178,
+ -8.227,
+ 1.433,
+ -9,
+ 1,
+ 1.789,
+ -10.076,
+ 2.144,
+ -10,
+ 2.5,
+ -10,
+ 1,
+ 2.744,
+ -10,
+ 2.989,
+ 0,
+ 3.233,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamRightShoulderUp",
+ "Segments": [
+ 0,
+ 8,
+ 1,
+ 0.056,
+ 8,
+ 0.111,
+ 8,
+ 0.167,
+ 8,
+ 1,
+ 0.267,
+ 8,
+ 0.367,
+ 8,
+ 0.467,
+ 8,
+ 1,
+ 0.533,
+ 8,
+ 0.6,
+ 4,
+ 0.667,
+ 4,
+ 1,
+ 0.767,
+ 4,
+ 0.867,
+ 9,
+ 0.967,
+ 9,
+ 1,
+ 1.5,
+ 9,
+ 2.033,
+ 9,
+ 2.567,
+ 9,
+ 1,
+ 2.722,
+ 9,
+ 2.878,
+ 0,
+ 3.033,
+ 0,
+ 1,
+ 3.267,
+ 0,
+ 3.5,
+ 8,
+ 3.733,
+ 8,
+ 0,
+ 5,
+ 8
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllRotate",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairFront",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairSide",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairBack",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairFrontFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairSideFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairBackFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamJacket",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamChainWaist",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchSwingA1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchSwingA2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchAX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBSwitch",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.3,
+ 0,
+ 0.433,
+ 0,
+ 0.567,
+ 0,
+ 1,
+ 0.611,
+ 0,
+ 0.656,
+ -1,
+ 0.7,
+ -1,
+ 1,
+ 0.733,
+ -1,
+ 0.767,
+ 0,
+ 0.8,
+ 0,
+ 1,
+ 1.667,
+ 0,
+ 2.533,
+ 0,
+ 3.4,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBOpen",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 1.011,
+ 0,
+ 1.856,
+ 0,
+ 2.7,
+ 0,
+ 1,
+ 2.756,
+ 0,
+ 2.811,
+ 16,
+ 2.867,
+ 16,
+ 1,
+ 2.889,
+ 16,
+ 2.911,
+ 16,
+ 2.933,
+ 16,
+ 1,
+ 3.089,
+ 16,
+ 3.244,
+ 0,
+ 3.4,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBOpen2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.344,
+ 0,
+ 0.522,
+ 0,
+ 0.7,
+ 0,
+ 1,
+ 0.744,
+ 0,
+ 0.789,
+ 30,
+ 0.833,
+ 30,
+ 1,
+ 1.456,
+ 30,
+ 2.078,
+ 30,
+ 2.7,
+ 30,
+ 1,
+ 2.756,
+ 30,
+ 2.811,
+ 16,
+ 2.867,
+ 16,
+ 1,
+ 2.889,
+ 16,
+ 2.911,
+ 16,
+ 2.933,
+ 16,
+ 1,
+ 3.089,
+ 16,
+ 3.244,
+ 0,
+ 3.4,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.3,
+ 0,
+ 0.433,
+ 0,
+ 0.567,
+ 0,
+ 1,
+ 0.7,
+ 0,
+ 0.833,
+ -6.9,
+ 0.967,
+ -6.9,
+ 1,
+ 1.544,
+ -6.9,
+ 2.122,
+ -6.9,
+ 2.7,
+ -6.9,
+ 1,
+ 2.756,
+ -6.9,
+ 2.811,
+ 0,
+ 2.867,
+ 0,
+ 1,
+ 2.889,
+ 0,
+ 2.911,
+ 0,
+ 2.933,
+ 0,
+ 1,
+ 3.089,
+ 0,
+ 3.244,
+ 0,
+ 3.4,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBRoll",
+ "Segments": [
+ 0,
+ -12,
+ 1,
+ 0.056,
+ -12,
+ 0.111,
+ -12,
+ 0.167,
+ -12,
+ 1,
+ 0.3,
+ -12,
+ 0.433,
+ -12,
+ 0.567,
+ -12,
+ 1,
+ 0.611,
+ -12,
+ 0.656,
+ -9,
+ 0.7,
+ -9,
+ 1,
+ 1.367,
+ -9,
+ 2.033,
+ -9,
+ 2.7,
+ -9,
+ 1,
+ 2.756,
+ -9,
+ 2.811,
+ -12,
+ 2.867,
+ -12,
+ 1,
+ 2.889,
+ -12,
+ 2.911,
+ -12,
+ 2.933,
+ -12,
+ 1,
+ 3.089,
+ -12,
+ 3.244,
+ -12,
+ 3.4,
+ -12,
+ 0,
+ 5,
+ -12
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBLR",
+ "Segments": [
+ 0,
+ 2,
+ 1,
+ 0.056,
+ 2,
+ 0.111,
+ 2,
+ 0.167,
+ 2,
+ 1,
+ 0.3,
+ 2,
+ 0.433,
+ 2,
+ 0.567,
+ 2,
+ 1,
+ 0.7,
+ 2,
+ 0.833,
+ -3.4,
+ 0.967,
+ -3.4,
+ 1,
+ 1.544,
+ -3.4,
+ 2.122,
+ -3.331,
+ 2.7,
+ -3.144,
+ 1,
+ 2.756,
+ -3.126,
+ 2.811,
+ 0,
+ 2.867,
+ 0,
+ 1,
+ 2.889,
+ 0,
+ 2.911,
+ 0,
+ 2.933,
+ 0,
+ 1,
+ 3.089,
+ 0,
+ 3.244,
+ 2,
+ 3.4,
+ 2,
+ 0,
+ 5,
+ 2
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBUD",
+ "Segments": [
+ 0,
+ -2,
+ 1,
+ 0.056,
+ -2,
+ 0.111,
+ -2,
+ 0.167,
+ -2,
+ 1,
+ 1.011,
+ -2,
+ 1.856,
+ 1.06,
+ 2.7,
+ 1.06,
+ 1,
+ 2.756,
+ 1.06,
+ 2.811,
+ 0.241,
+ 2.867,
+ 0,
+ 1,
+ 2.889,
+ -0.096,
+ 2.911,
+ 0.01,
+ 2.933,
+ -0.11,
+ 1,
+ 3.089,
+ -0.955,
+ 3.244,
+ -2,
+ 3.4,
+ -2,
+ 0,
+ 5,
+ -2
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL04",
+ "FadeInTime": 0.2,
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR01",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR02",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR03",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR04",
+ "FadeInTime": 0.2,
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR01",
+ "Segments": [
+ 0,
+ -7,
+ 1,
+ 0.056,
+ -7,
+ 0.111,
+ -7,
+ 0.167,
+ -7,
+ 1,
+ 0.378,
+ -7,
+ 0.589,
+ -7,
+ 0.8,
+ -7,
+ 1,
+ 0.989,
+ -7,
+ 1.178,
+ -5,
+ 1.367,
+ -5,
+ 1,
+ 1.744,
+ -5,
+ 2.122,
+ -5,
+ 2.5,
+ -5,
+ 1,
+ 2.589,
+ -5,
+ 2.678,
+ -6,
+ 2.767,
+ -6,
+ 1,
+ 2.789,
+ -6,
+ 2.811,
+ -6,
+ 2.833,
+ -6,
+ 1,
+ 3.022,
+ -6,
+ 3.211,
+ -7,
+ 3.4,
+ -7,
+ 0,
+ 5,
+ -7
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR02",
+ "Segments": [
+ 0,
+ 18,
+ 1,
+ 0.056,
+ 18,
+ 0.111,
+ 18,
+ 0.167,
+ 18,
+ 1,
+ 0.444,
+ 18,
+ 0.722,
+ 18,
+ 1,
+ 18,
+ 1,
+ 1.356,
+ 18,
+ 1.711,
+ 16.881,
+ 2.067,
+ 15,
+ 1,
+ 2.211,
+ 14.236,
+ 2.356,
+ 14,
+ 2.5,
+ 14,
+ 1,
+ 2.589,
+ 14,
+ 2.678,
+ 16.023,
+ 2.767,
+ 18,
+ 1,
+ 2.811,
+ 18.988,
+ 2.856,
+ 19,
+ 2.9,
+ 19,
+ 1,
+ 3.067,
+ 19,
+ 3.233,
+ 18,
+ 3.4,
+ 18,
+ 0,
+ 5,
+ 18
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.3,
+ 0,
+ 0.433,
+ -6,
+ 0.567,
+ -6,
+ 1,
+ 0.611,
+ -6,
+ 0.656,
+ 9,
+ 0.7,
+ 9,
+ 1,
+ 0.8,
+ 9,
+ 0.9,
+ 0.266,
+ 1,
+ 0,
+ 1,
+ 1.322,
+ -0.858,
+ 1.644,
+ -1,
+ 1.967,
+ -1,
+ 1,
+ 2.211,
+ -1,
+ 2.456,
+ -1,
+ 2.7,
+ -1,
+ 1,
+ 2.744,
+ -1,
+ 2.789,
+ 14,
+ 2.833,
+ 14,
+ 1,
+ 2.867,
+ 14,
+ 2.9,
+ 14,
+ 2.933,
+ 14,
+ 1,
+ 3.078,
+ 14,
+ 3.222,
+ 0,
+ 3.367,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand01",
+ "FadeInTime": 0.2,
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand01Roll",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05",
+ "FadeInTime": 0.2,
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 0,
+ 5,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 1.011,
+ 0,
+ 1.856,
+ 0,
+ 2.7,
+ 0,
+ 1,
+ 2.756,
+ 0,
+ 2.811,
+ 30,
+ 2.867,
+ 30,
+ 1,
+ 2.889,
+ 30,
+ 2.911,
+ 30,
+ 2.933,
+ 30,
+ 1,
+ 3.089,
+ 30,
+ 3.244,
+ 0,
+ 3.4,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.3,
+ 0,
+ 0.433,
+ 0,
+ 0.567,
+ 0,
+ 1,
+ 0.611,
+ 0,
+ 0.656,
+ 7.5,
+ 0.7,
+ 7.5,
+ 1,
+ 0.811,
+ 7.5,
+ 0.922,
+ 0,
+ 1.033,
+ 0,
+ 1,
+ 1.589,
+ 0,
+ 2.144,
+ 0,
+ 2.7,
+ 0,
+ 1,
+ 2.756,
+ 0,
+ 2.811,
+ 21,
+ 2.867,
+ 21,
+ 1,
+ 2.889,
+ 21,
+ 2.911,
+ 21,
+ 2.933,
+ 21,
+ 1,
+ 3.089,
+ 21,
+ 3.244,
+ 0,
+ 3.4,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll3",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 1.011,
+ 0,
+ 1.856,
+ 0,
+ 2.7,
+ 0,
+ 1,
+ 2.756,
+ 0,
+ 2.811,
+ 30,
+ 2.867,
+ 30,
+ 1,
+ 2.889,
+ 30,
+ 2.911,
+ 30,
+ 2.933,
+ 30,
+ 1,
+ 3.089,
+ 30,
+ 3.244,
+ 0,
+ 3.4,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCLHandRoll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDLHand03Roll",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER03",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER04",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand04Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand04Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand06Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand06Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartWatchA",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartWatchB",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 5,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmAL",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 5,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmAR",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmBR",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 5,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmCL",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmDL",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmER",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ }
+ ]
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_04.motion3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_04.motion3.json
new file mode 100644
index 0000000..8da5017
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_04.motion3.json
@@ -0,0 +1,2316 @@
+锘縶
+ "Version": 3,
+ "Meta": {
+ "Duration": 3.33,
+ "Fps": 30.0,
+ "Loop": true,
+ "AreBeziersRestricted": true,
+ "CurveCount": 98,
+ "TotalSegmentCount": 278,
+ "TotalPointCount": 733,
+ "UserDataCount": 0,
+ "TotalUserDataSize": 0
+ },
+ "Curves": [
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.189,
+ 0,
+ 0.211,
+ 0,
+ 0.233,
+ 0,
+ 1,
+ 0.3,
+ 0,
+ 0.367,
+ 0,
+ 0.433,
+ 0,
+ 1,
+ 0.667,
+ 0,
+ 0.9,
+ 4.019,
+ 1.133,
+ 4.019,
+ 1,
+ 1.444,
+ 4.019,
+ 1.756,
+ 0,
+ 2.067,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleY",
+ "Segments": [
+ 0,
+ -9.011,
+ 1,
+ 0.056,
+ -9.011,
+ 0.111,
+ -9.011,
+ 0.167,
+ -9.011,
+ 1,
+ 0.222,
+ -9.011,
+ 0.278,
+ -9.011,
+ 0.333,
+ -9.011,
+ 1,
+ 0.544,
+ -9.011,
+ 0.756,
+ -30,
+ 0.967,
+ -30,
+ 1,
+ 1.089,
+ -30,
+ 1.211,
+ -30,
+ 1.333,
+ -30,
+ 1,
+ 1.567,
+ -30,
+ 1.8,
+ 0,
+ 2.033,
+ 0,
+ 1,
+ 2.067,
+ 0,
+ 2.1,
+ 0,
+ 2.133,
+ 0,
+ 1,
+ 2.356,
+ 0,
+ 2.578,
+ -5,
+ 2.8,
+ -5,
+ 0,
+ 3.333,
+ -5
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.233,
+ 0,
+ 0.3,
+ 0,
+ 0.367,
+ 0,
+ 1,
+ 0.622,
+ 0,
+ 0.878,
+ -4,
+ 1.133,
+ -4,
+ 1,
+ 1.533,
+ -4,
+ 1.933,
+ 1,
+ 2.333,
+ 1,
+ 0,
+ 3.333,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLOpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 1,
+ 0.311,
+ 1,
+ 0.456,
+ 1,
+ 0.6,
+ 1,
+ 1,
+ 0.689,
+ 1,
+ 0.778,
+ 0,
+ 0.867,
+ 0,
+ 1,
+ 1.133,
+ 0,
+ 1.4,
+ 0,
+ 1.667,
+ 0,
+ 1,
+ 1.756,
+ 0,
+ 1.844,
+ 1,
+ 1.933,
+ 1,
+ 0,
+ 3.333,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeROpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 1,
+ 0.311,
+ 1,
+ 0.456,
+ 1,
+ 0.6,
+ 1,
+ 1,
+ 0.689,
+ 1,
+ 0.778,
+ 0,
+ 0.867,
+ 0,
+ 1,
+ 1.133,
+ 0,
+ 1.4,
+ 0,
+ 1.667,
+ 0,
+ 1,
+ 1.756,
+ 0,
+ 1.844,
+ 1,
+ 1.933,
+ 1,
+ 0,
+ 3.333,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.311,
+ 0,
+ 0.456,
+ 0,
+ 0.6,
+ 0,
+ 1,
+ 0.689,
+ 0,
+ 0.778,
+ 0.2,
+ 0.867,
+ 0.2,
+ 1,
+ 1.133,
+ 0.2,
+ 1.4,
+ 0.2,
+ 1.667,
+ 0.2,
+ 1,
+ 1.756,
+ 0.2,
+ 1.844,
+ 0,
+ 1.933,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.311,
+ 0,
+ 0.456,
+ 0,
+ 0.6,
+ 0,
+ 1,
+ 0.689,
+ 0,
+ 0.778,
+ -0.8,
+ 0.867,
+ -0.8,
+ 1,
+ 1.133,
+ -0.8,
+ 1.4,
+ -0.8,
+ 1.667,
+ -0.8,
+ 1,
+ 1.756,
+ -0.8,
+ 1.844,
+ 0,
+ 1.933,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamTeethOn",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamCheek",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGlassUD",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.222,
+ 0,
+ 0.278,
+ 0,
+ 0.333,
+ 0,
+ 1,
+ 0.544,
+ 0,
+ 0.756,
+ -10,
+ 0.967,
+ -10,
+ 1,
+ 1.044,
+ -10,
+ 1.122,
+ 10,
+ 1.2,
+ 10,
+ 1,
+ 1.222,
+ 10,
+ 1.244,
+ 10,
+ 1.267,
+ 10,
+ 1,
+ 1.333,
+ 10,
+ 1.4,
+ 0,
+ 1.467,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassWhite",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.433,
+ 0,
+ 0.7,
+ 0,
+ 0.967,
+ 0,
+ 1,
+ 1.067,
+ 0,
+ 1.167,
+ 0.4,
+ 1.267,
+ 0.4,
+ 1,
+ 1.367,
+ 0.4,
+ 1.467,
+ 0,
+ 1.567,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassHighlight",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.422,
+ 0,
+ 0.678,
+ 0,
+ 0.933,
+ 0,
+ 1,
+ 1.011,
+ 0,
+ 1.089,
+ 1,
+ 1.167,
+ 1,
+ 1,
+ 1.2,
+ 1,
+ 1.233,
+ 1,
+ 1.267,
+ 1,
+ 1,
+ 1.4,
+ 1,
+ 1.533,
+ 0,
+ 1.667,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassHighlightMove",
+ "Segments": [
+ 0,
+ -30,
+ 1,
+ 0.056,
+ -30,
+ 0.111,
+ -30,
+ 0.167,
+ -30,
+ 1,
+ 0.444,
+ -30,
+ 0.722,
+ -30,
+ 1,
+ -30,
+ 1,
+ 1.189,
+ -30,
+ 1.378,
+ 30,
+ 1.567,
+ 30,
+ 0,
+ 3.333,
+ 30
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.189,
+ 0,
+ 0.211,
+ 0,
+ 0.233,
+ 0,
+ 1,
+ 0.289,
+ 0,
+ 0.344,
+ 0,
+ 0.4,
+ 0,
+ 1,
+ 0.611,
+ 0,
+ 0.822,
+ 2,
+ 1.033,
+ 2,
+ 1,
+ 1.078,
+ 2,
+ 1.122,
+ 2,
+ 1.167,
+ 2,
+ 1,
+ 1.422,
+ 2,
+ 1.678,
+ 0,
+ 1.933,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.189,
+ 0,
+ 0.211,
+ 0,
+ 0.233,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWaistAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyPosition",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.256,
+ 0,
+ 0.344,
+ 0,
+ 0.433,
+ 0,
+ 1,
+ 0.633,
+ 0,
+ 0.833,
+ 2,
+ 1.033,
+ 2,
+ 1,
+ 1.244,
+ 2,
+ 1.456,
+ 0,
+ 1.667,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBreath",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.233,
+ 0,
+ 0.3,
+ 0,
+ 0.367,
+ 0,
+ 1,
+ 0.722,
+ 0,
+ 1.078,
+ 1,
+ 1.433,
+ 1,
+ 1,
+ 1.667,
+ 1,
+ 1.9,
+ 1,
+ 2.133,
+ 1,
+ 0,
+ 3.333,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamLeftShoulderUp",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamRightShoulderUp",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.189,
+ 0,
+ 0.211,
+ 0,
+ 0.233,
+ 0,
+ 1,
+ 0.478,
+ 0,
+ 0.722,
+ 2.225,
+ 0.967,
+ 9,
+ 1,
+ 1.056,
+ 11.464,
+ 1.144,
+ 15,
+ 1.233,
+ 15,
+ 1,
+ 1.589,
+ 15,
+ 1.944,
+ -6,
+ 2.3,
+ -6,
+ 1,
+ 2.489,
+ -6,
+ 2.678,
+ -3,
+ 2.867,
+ -3,
+ 0,
+ 3.333,
+ -3
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllRotate",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairFront",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairSide",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairBack",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairFrontFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairSideFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairBackFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamJacket",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamChainWaist",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchSwingA1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchSwingA2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchAX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBSwitch",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBOpen",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBOpen2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBRoll",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBLR",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBUD",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL04",
+ "FadeInTime": 0.2,
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR04",
+ "FadeInTime": 0.2,
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand01Roll",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll3",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCLHandRoll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDLHand03Roll",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER01",
+ "Segments": [
+ 0,
+ 16,
+ 1,
+ 0.056,
+ 16,
+ 0.111,
+ 16,
+ 0.167,
+ 16,
+ 1,
+ 0.311,
+ 16,
+ 0.456,
+ 16,
+ 0.6,
+ 16,
+ 1,
+ 1.033,
+ 16,
+ 1.467,
+ 16,
+ 1.9,
+ 16,
+ 1,
+ 2.044,
+ 16,
+ 2.189,
+ 18,
+ 2.333,
+ 18,
+ 0,
+ 3.333,
+ 18
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER02",
+ "Segments": [
+ 0,
+ 11,
+ 1,
+ 0.056,
+ 11,
+ 0.111,
+ 11,
+ 0.167,
+ 11,
+ 1,
+ 0.311,
+ 11,
+ 0.456,
+ 14.565,
+ 0.6,
+ 18,
+ 1,
+ 0.711,
+ 20.643,
+ 0.822,
+ 21,
+ 0.933,
+ 21,
+ 1,
+ 1.267,
+ 21,
+ 1.6,
+ 13.953,
+ 1.933,
+ 0,
+ 1,
+ 2.078,
+ -6.046,
+ 2.222,
+ -9,
+ 2.367,
+ -9,
+ 1,
+ 2.544,
+ -9,
+ 2.722,
+ -8,
+ 2.9,
+ -8,
+ 0,
+ 3.333,
+ -8
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER03",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 1,
+ 0.189,
+ 1,
+ 0.211,
+ 1,
+ 0.233,
+ 1,
+ 1,
+ 0.356,
+ 1,
+ 0.478,
+ 1,
+ 0.6,
+ 1,
+ 1,
+ 0.711,
+ 1,
+ 0.822,
+ -5.698,
+ 0.933,
+ -7,
+ 1,
+ 1.033,
+ -8.171,
+ 1.133,
+ -8.083,
+ 1.233,
+ -9,
+ 1,
+ 1.389,
+ -10.427,
+ 1.544,
+ -11.838,
+ 1.7,
+ -11.838,
+ 1,
+ 1.933,
+ -11.838,
+ 2.167,
+ 19,
+ 2.4,
+ 19,
+ 1,
+ 2.578,
+ 19,
+ 2.756,
+ 15.092,
+ 2.933,
+ 15.092,
+ 0,
+ 3.333,
+ 15.092
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER04",
+ "Segments": [
+ 0,
+ 30,
+ 1,
+ 0.056,
+ 30,
+ 0.111,
+ 30,
+ 0.167,
+ 30,
+ 1,
+ 0.422,
+ 30,
+ 0.678,
+ 30,
+ 0.933,
+ 30,
+ 1,
+ 1.089,
+ 30,
+ 1.244,
+ 30,
+ 1.4,
+ 30,
+ 1,
+ 1.744,
+ 30,
+ 2.089,
+ 9,
+ 2.433,
+ 9,
+ 0,
+ 3.333,
+ 9
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand04",
+ "FadeInTime": 0.2,
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand04Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand04Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand06",
+ "FadeInTime": 0.2,
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 1,
+ 0.311,
+ 1,
+ 0.456,
+ 1,
+ 0.6,
+ 1,
+ 0,
+ 3.333,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand06Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.311,
+ 0,
+ 0.456,
+ 0,
+ 0.6,
+ 0,
+ 1,
+ 0.711,
+ 0,
+ 0.822,
+ 12.92,
+ 0.933,
+ 14,
+ 1,
+ 1.278,
+ 17.347,
+ 1.622,
+ 18,
+ 1.967,
+ 18,
+ 1,
+ 2.144,
+ 18,
+ 2.322,
+ 3,
+ 2.5,
+ 3,
+ 0,
+ 3.333,
+ 3
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand06Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.411,
+ 0,
+ 0.656,
+ -30,
+ 0.9,
+ -30,
+ 1,
+ 1.167,
+ -30,
+ 1.433,
+ -30,
+ 1.7,
+ -30,
+ 1,
+ 1.956,
+ -30,
+ 2.211,
+ -24,
+ 2.467,
+ -24,
+ 0,
+ 3.333,
+ -24
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartWatchA",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 3.33,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartWatchB",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 3.33,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmAL",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 3.33,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmAR",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 3.33,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmBR",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 3.33,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmCL",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 3.33,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmDL",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 3.33,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmER",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 3.33,
+ 1
+ ]
+ }
+ ]
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_05.motion3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_05.motion3.json
new file mode 100644
index 0000000..f2b4621
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_05.motion3.json
@@ -0,0 +1,2345 @@
+锘縶
+ "Version": 3,
+ "Meta": {
+ "Duration": 5,
+ "Fps": 30.0,
+ "Loop": true,
+ "AreBeziersRestricted": true,
+ "CurveCount": 96,
+ "TotalSegmentCount": 283,
+ "TotalPointCount": 752,
+ "UserDataCount": 0,
+ "TotalUserDataSize": 0
+ },
+ "Curves": [
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.4,
+ 0,
+ 0.633,
+ 0,
+ 0.867,
+ 0,
+ 1,
+ 1.167,
+ 0,
+ 1.467,
+ 13.505,
+ 1.767,
+ 16,
+ 1,
+ 2.011,
+ 18.033,
+ 2.256,
+ 17.623,
+ 2.5,
+ 17.623,
+ 1,
+ 2.856,
+ 17.623,
+ 3.211,
+ -4.813,
+ 3.567,
+ -6,
+ 1,
+ 3.8,
+ -6.779,
+ 4.033,
+ -6.496,
+ 4.267,
+ -6.496,
+ 0,
+ 5,
+ -6.496
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleY",
+ "Segments": [
+ 0,
+ 0.017,
+ 1,
+ 0.056,
+ 0.017,
+ 0.111,
+ 0.017,
+ 0.167,
+ 0.017,
+ 1,
+ 0.456,
+ 0.017,
+ 0.744,
+ 0.229,
+ 1.033,
+ 1,
+ 1,
+ 1.211,
+ 1.475,
+ 1.389,
+ 2,
+ 1.567,
+ 2,
+ 1,
+ 1.744,
+ 2,
+ 1.922,
+ 0,
+ 2.1,
+ 0,
+ 1,
+ 2.4,
+ 0,
+ 2.7,
+ 3,
+ 3,
+ 3,
+ 1,
+ 3.211,
+ 3,
+ 3.422,
+ -5.586,
+ 3.633,
+ -5.586,
+ 1,
+ 3.644,
+ -5.586,
+ 3.656,
+ -5.586,
+ 3.667,
+ -5.586,
+ 1,
+ 3.822,
+ -5.586,
+ 3.978,
+ 1,
+ 4.133,
+ 1,
+ 1,
+ 4.389,
+ 1,
+ 4.644,
+ 0,
+ 4.9,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.433,
+ 0,
+ 0.7,
+ -3,
+ 0.967,
+ -3,
+ 1,
+ 1.256,
+ -3,
+ 1.544,
+ 4.968,
+ 1.833,
+ 6,
+ 1,
+ 2.156,
+ 7.151,
+ 2.478,
+ 7,
+ 2.8,
+ 7,
+ 1,
+ 3,
+ 7,
+ 3.2,
+ -2,
+ 3.4,
+ -2,
+ 1,
+ 3.578,
+ -2,
+ 3.756,
+ 0,
+ 3.933,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLOpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 1,
+ 0.589,
+ 1,
+ 1.011,
+ 1,
+ 1.433,
+ 1,
+ 1,
+ 1.467,
+ 1,
+ 1.5,
+ 0,
+ 1.533,
+ 0,
+ 1,
+ 1.556,
+ 0,
+ 1.578,
+ 0,
+ 1.6,
+ 0,
+ 1,
+ 1.678,
+ 0,
+ 1.756,
+ 1,
+ 1.833,
+ 1,
+ 1,
+ 2.344,
+ 1,
+ 2.856,
+ 1,
+ 3.367,
+ 1,
+ 1,
+ 3.4,
+ 1,
+ 3.433,
+ 0,
+ 3.467,
+ 0,
+ 1,
+ 3.489,
+ 0,
+ 3.511,
+ 0,
+ 3.533,
+ 0,
+ 1,
+ 3.611,
+ 0,
+ 3.689,
+ 1,
+ 3.767,
+ 1,
+ 0,
+ 5,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeROpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 1,
+ 0.589,
+ 1,
+ 1.011,
+ 1,
+ 1.433,
+ 1,
+ 1,
+ 1.467,
+ 1,
+ 1.5,
+ 0,
+ 1.533,
+ 0,
+ 1,
+ 1.556,
+ 0,
+ 1.578,
+ 0,
+ 1.6,
+ 0,
+ 1,
+ 1.678,
+ 0,
+ 1.756,
+ 1,
+ 1.833,
+ 1,
+ 1,
+ 2.344,
+ 1,
+ 2.856,
+ 1,
+ 3.367,
+ 1,
+ 1,
+ 3.4,
+ 1,
+ 3.433,
+ 0,
+ 3.467,
+ 0,
+ 1,
+ 3.489,
+ 0,
+ 3.511,
+ 0,
+ 3.533,
+ 0,
+ 1,
+ 3.611,
+ 0,
+ 3.689,
+ 1,
+ 3.767,
+ 1,
+ 0,
+ 5,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.578,
+ 0,
+ 0.989,
+ 0,
+ 1.4,
+ 0,
+ 1,
+ 1.544,
+ 0,
+ 1.689,
+ 0.542,
+ 1.833,
+ 0.542,
+ 1,
+ 2.056,
+ 0.542,
+ 2.278,
+ 0.542,
+ 2.5,
+ 0.542,
+ 1,
+ 2.789,
+ 0.542,
+ 3.078,
+ 0,
+ 3.367,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.578,
+ 0,
+ 0.989,
+ 0,
+ 1.4,
+ 0,
+ 1,
+ 1.544,
+ 0,
+ 1.689,
+ -0.386,
+ 1.833,
+ -0.386,
+ 1,
+ 2.056,
+ -0.386,
+ 2.278,
+ -0.386,
+ 2.5,
+ -0.386,
+ 1,
+ 2.789,
+ -0.386,
+ 3.078,
+ 0,
+ 3.367,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamTeethOn",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamCheek",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGlassUD",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassWhite",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassHighlight",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassHighlightMove",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.356,
+ 0,
+ 0.544,
+ -1,
+ 0.733,
+ -1,
+ 1,
+ 1.044,
+ -1,
+ 1.356,
+ 4.155,
+ 1.667,
+ 5,
+ 1,
+ 1.911,
+ 5.664,
+ 2.156,
+ 5.5,
+ 2.4,
+ 5.5,
+ 1,
+ 2.656,
+ 5.5,
+ 2.911,
+ -1.168,
+ 3.167,
+ -2,
+ 1,
+ 3.5,
+ -3.085,
+ 3.833,
+ -3,
+ 4.167,
+ -3,
+ 0,
+ 5,
+ -3
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.356,
+ 0,
+ 0.544,
+ 1.915,
+ 0.733,
+ 2,
+ 1,
+ 0.878,
+ 2.065,
+ 1.022,
+ 2.028,
+ 1.167,
+ 2.088,
+ 1,
+ 1.344,
+ 2.162,
+ 1.522,
+ 2.904,
+ 1.7,
+ 2.904,
+ 1,
+ 1.867,
+ 2.904,
+ 2.033,
+ 2,
+ 2.2,
+ 2,
+ 1,
+ 2.356,
+ 2,
+ 2.511,
+ 2.5,
+ 2.667,
+ 2.5,
+ 1,
+ 2.978,
+ 2.5,
+ 3.289,
+ -1.006,
+ 3.6,
+ -1.006,
+ 1,
+ 3.8,
+ -1.006,
+ 4,
+ 0.139,
+ 4.2,
+ 0.139,
+ 1,
+ 4.378,
+ 0.139,
+ 4.556,
+ 0,
+ 4.733,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.433,
+ 0,
+ 0.7,
+ 0,
+ 0.967,
+ 0,
+ 1,
+ 1.222,
+ 0,
+ 1.478,
+ 1,
+ 1.733,
+ 1,
+ 1,
+ 2.456,
+ 1,
+ 3.178,
+ 1,
+ 3.9,
+ 1,
+ 0,
+ 5,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWaistAngleZ",
+ "Segments": [
+ 0,
+ 0.124,
+ 1,
+ 0.056,
+ 0.124,
+ 0.111,
+ 0.124,
+ 0.167,
+ 0.124,
+ 1,
+ 0.478,
+ 0.124,
+ 0.789,
+ 2,
+ 1.1,
+ 2,
+ 1,
+ 1.3,
+ 2,
+ 1.5,
+ -1,
+ 1.7,
+ -1,
+ 1,
+ 2.2,
+ -1,
+ 2.7,
+ -1,
+ 3.2,
+ -1,
+ 1,
+ 3.456,
+ -1,
+ 3.711,
+ 0,
+ 3.967,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyPosition",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 1.656,
+ 0,
+ 3.144,
+ 0,
+ 4.633,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBreath",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.411,
+ 0,
+ 0.656,
+ 0,
+ 0.9,
+ 0,
+ 1,
+ 1.411,
+ 0,
+ 1.922,
+ 0.501,
+ 2.433,
+ 0.501,
+ 1,
+ 2.511,
+ 0.501,
+ 2.589,
+ 0.501,
+ 2.667,
+ 0.501,
+ 1,
+ 3.178,
+ 0.501,
+ 3.689,
+ 0,
+ 4.2,
+ 0,
+ 1,
+ 4.322,
+ 0,
+ 4.444,
+ 0,
+ 4.567,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamLeftShoulderUp",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.356,
+ 0,
+ 0.544,
+ -6,
+ 0.733,
+ -6,
+ 1,
+ 0.933,
+ -6,
+ 1.133,
+ 6,
+ 1.333,
+ 6,
+ 1,
+ 1.522,
+ 6,
+ 1.711,
+ 2,
+ 1.9,
+ 2,
+ 0,
+ 5,
+ 2
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamRightShoulderUp",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.289,
+ 0,
+ 0.411,
+ 0,
+ 0.533,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllRotate",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairFront",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairSide",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairBack",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairFrontFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairSideFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairBackFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamJacket",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamChainWaist",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchSwingA1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchSwingA2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchAX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBSwitch",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBOpen",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBOpen2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBRoll",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBLR",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBUD",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL01",
+ "Segments": [
+ 0,
+ 0.232,
+ 1,
+ 0.056,
+ 0.232,
+ 0.111,
+ 0.232,
+ 0.167,
+ 0.232,
+ 0,
+ 5,
+ 0.232
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL02",
+ "Segments": [
+ 0,
+ -0.373,
+ 1,
+ 0.056,
+ -0.373,
+ 0.111,
+ -0.373,
+ 0.167,
+ -0.373,
+ 0,
+ 5,
+ -0.373
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL03",
+ "Segments": [
+ 0,
+ -0.305,
+ 1,
+ 0.056,
+ -0.305,
+ 0.111,
+ -0.305,
+ 0.167,
+ -0.305,
+ 0,
+ 5,
+ -0.305
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL04",
+ "FadeInTime": 0.2,
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.333,
+ 0,
+ 0.5,
+ 0,
+ 0.667,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1.333,
+ 1,
+ 1.667,
+ 1,
+ 1,
+ 1.889,
+ 1,
+ 2.111,
+ 1,
+ 2.333,
+ 1,
+ 1,
+ 2.689,
+ 1,
+ 3.044,
+ 0,
+ 3.4,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.333,
+ 0,
+ 0.5,
+ 0,
+ 0.667,
+ 0,
+ 1,
+ 1.011,
+ 0,
+ 1.356,
+ -3,
+ 1.7,
+ -3,
+ 1,
+ 1.922,
+ -3,
+ 2.144,
+ -3,
+ 2.367,
+ -3,
+ 1,
+ 2.722,
+ -3,
+ 3.078,
+ 0,
+ 3.433,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.333,
+ 0,
+ 0.5,
+ 0,
+ 0.667,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR04",
+ "FadeInTime": 0.2,
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand01Roll",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll3",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCLHandRoll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL01",
+ "Segments": [
+ 0,
+ -0.938,
+ 1,
+ 0.056,
+ -0.938,
+ 0.111,
+ -0.938,
+ 0.167,
+ -0.938,
+ 1,
+ 0.322,
+ -0.938,
+ 0.478,
+ -0.938,
+ 0.633,
+ -0.938,
+ 1,
+ 0.956,
+ -0.938,
+ 1.278,
+ -4,
+ 1.6,
+ -4,
+ 1,
+ 1.944,
+ -4,
+ 2.289,
+ 4,
+ 2.633,
+ 4,
+ 1,
+ 3.033,
+ 4,
+ 3.433,
+ 0,
+ 3.833,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL02",
+ "Segments": [
+ 0,
+ -1.719,
+ 1,
+ 0.056,
+ -1.719,
+ 0.111,
+ -1.719,
+ 0.167,
+ -1.719,
+ 1,
+ 0.322,
+ -1.719,
+ 0.478,
+ -1.719,
+ 0.633,
+ -1.719,
+ 1,
+ 0.967,
+ -1.719,
+ 1.3,
+ 4.142,
+ 1.633,
+ 7,
+ 1,
+ 1.978,
+ 9.953,
+ 2.322,
+ 10,
+ 2.667,
+ 10,
+ 1,
+ 3.111,
+ 10,
+ 3.556,
+ 6,
+ 4,
+ 6,
+ 0,
+ 5,
+ 6
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL03",
+ "Segments": [
+ 0,
+ -4.688,
+ 1,
+ 0.056,
+ -4.688,
+ 0.111,
+ -4.688,
+ 0.167,
+ -4.688,
+ 1,
+ 0.322,
+ -4.688,
+ 0.478,
+ -4.688,
+ 0.633,
+ -4.688,
+ 1,
+ 0.978,
+ -4.688,
+ 1.322,
+ 14.713,
+ 1.667,
+ 18,
+ 1,
+ 2.022,
+ 21.393,
+ 2.378,
+ 21,
+ 2.733,
+ 21,
+ 1,
+ 2.967,
+ 21,
+ 3.2,
+ 13,
+ 3.433,
+ 13,
+ 0,
+ 5,
+ 13
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDLHand03Roll",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 1.044,
+ 0,
+ 1.922,
+ -30,
+ 2.8,
+ -30,
+ 1,
+ 3.2,
+ -30,
+ 3.6,
+ -5.084,
+ 4,
+ -2,
+ 1,
+ 4.333,
+ 0.57,
+ 4.667,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER04",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand04Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand04Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand06Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand06Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartWatchA",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 5,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartWatchB",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmAL",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmAR",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 5,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmBR",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmCL",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmDL",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 5,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmER",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 5,
+ 0
+ ]
+ }
+ ]
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_06.motion3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_06.motion3.json
new file mode 100644
index 0000000..20fd6d0
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_06.motion3.json
@@ -0,0 +1,2082 @@
+锘縶
+ "Version": 3,
+ "Meta": {
+ "Duration": 3.33,
+ "Fps": 30.0,
+ "Loop": true,
+ "AreBeziersRestricted": true,
+ "CurveCount": 96,
+ "TotalSegmentCount": 246,
+ "TotalPointCount": 639,
+ "UserDataCount": 0,
+ "TotalUserDataSize": 0
+ },
+ "Curves": [
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.256,
+ 0,
+ 0.344,
+ 0,
+ 0.433,
+ 0,
+ 1,
+ 0.622,
+ 0,
+ 0.811,
+ 10,
+ 1,
+ 10,
+ 0,
+ 3.333,
+ 10
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.178,
+ 0,
+ 0.189,
+ 0,
+ 0.2,
+ 0,
+ 1,
+ 0.267,
+ 0,
+ 0.333,
+ 1.753,
+ 0.4,
+ 3.095,
+ 1,
+ 0.444,
+ 3.989,
+ 0.489,
+ 3.997,
+ 0.533,
+ 3.997,
+ 1,
+ 0.744,
+ 3.997,
+ 0.956,
+ -27.096,
+ 1.167,
+ -27.096,
+ 1,
+ 1.256,
+ -27.096,
+ 1.344,
+ -25.324,
+ 1.433,
+ -25.065,
+ 1,
+ 1.811,
+ -23.961,
+ 2.189,
+ -23.671,
+ 2.567,
+ -23.671,
+ 0,
+ 3.333,
+ -23.671
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.211,
+ 0,
+ 0.256,
+ 0,
+ 0.3,
+ 0,
+ 1,
+ 0.378,
+ 0,
+ 0.456,
+ 0.319,
+ 0.533,
+ -1.334,
+ 1,
+ 0.7,
+ -4.877,
+ 0.867,
+ -11,
+ 1.033,
+ -11,
+ 1,
+ 1.289,
+ -11,
+ 1.544,
+ -10,
+ 1.8,
+ -10,
+ 0,
+ 3.333,
+ -10
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLOpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 1,
+ 0.344,
+ 1,
+ 0.522,
+ 1,
+ 0.7,
+ 1,
+ 1,
+ 0.744,
+ 1,
+ 0.789,
+ 0,
+ 0.833,
+ 0,
+ 1,
+ 0.989,
+ 0,
+ 1.144,
+ 0,
+ 1.3,
+ 0,
+ 1,
+ 1.378,
+ 0,
+ 1.456,
+ 1,
+ 1.533,
+ 1,
+ 0,
+ 3.333,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeROpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 1,
+ 0.344,
+ 1,
+ 0.522,
+ 1,
+ 0.7,
+ 1,
+ 1,
+ 0.744,
+ 1,
+ 0.789,
+ 0,
+ 0.833,
+ 0,
+ 1,
+ 0.989,
+ 0,
+ 1.144,
+ 0,
+ 1.3,
+ 0,
+ 1,
+ 1.378,
+ 0,
+ 1.456,
+ 1,
+ 1.533,
+ 1,
+ 0,
+ 3.333,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.389,
+ 0,
+ 0.611,
+ 0.1,
+ 0.833,
+ 0.1,
+ 1,
+ 1.067,
+ 0.1,
+ 1.3,
+ -0.4,
+ 1.533,
+ -0.4,
+ 0,
+ 3.333,
+ -0.4
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.389,
+ 0,
+ 0.611,
+ -0.2,
+ 0.833,
+ -0.2,
+ 1,
+ 1.067,
+ -0.2,
+ 1.3,
+ 0.7,
+ 1.533,
+ 0.7,
+ 0,
+ 3.333,
+ 0.7
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamTeethOn",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamCheek",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGlassUD",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassWhite",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassHighlight",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassHighlightMove",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.244,
+ 0,
+ 0.322,
+ -0.5,
+ 0.4,
+ -0.5,
+ 1,
+ 0.622,
+ -0.5,
+ 0.844,
+ 6,
+ 1.067,
+ 6,
+ 1,
+ 1.3,
+ 6,
+ 1.533,
+ 5,
+ 1.767,
+ 5,
+ 0,
+ 3.333,
+ 5
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.244,
+ 0,
+ 0.322,
+ 0.625,
+ 0.4,
+ 0.625,
+ 1,
+ 0.633,
+ 0.625,
+ 0.867,
+ -4.741,
+ 1.1,
+ -4.741,
+ 1,
+ 1.333,
+ -4.741,
+ 1.567,
+ -4,
+ 1.8,
+ -4,
+ 0,
+ 3.333,
+ -4
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.256,
+ 0,
+ 0.344,
+ 0,
+ 0.433,
+ 0,
+ 1,
+ 0.611,
+ 0,
+ 0.789,
+ -3,
+ 0.967,
+ -3,
+ 0,
+ 3.333,
+ -3
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWaistAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.256,
+ 0,
+ 0.344,
+ 0,
+ 0.433,
+ 0,
+ 1,
+ 0.611,
+ 0,
+ 0.789,
+ -5,
+ 0.967,
+ -5,
+ 0,
+ 3.333,
+ -5
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyPosition",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.244,
+ 0,
+ 0.322,
+ -3,
+ 0.4,
+ -3,
+ 1,
+ 0.544,
+ -3,
+ 0.689,
+ 0.972,
+ 0.833,
+ 3.4,
+ 1,
+ 1.144,
+ 8.63,
+ 1.456,
+ 10,
+ 1.767,
+ 10,
+ 0,
+ 3.333,
+ 10
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBreath",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.178,
+ 0,
+ 0.189,
+ 0,
+ 0.2,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamLeftShoulderUp",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.244,
+ 0,
+ 0.322,
+ 1.58,
+ 0.4,
+ 4,
+ 1,
+ 0.589,
+ 9.878,
+ 0.778,
+ 16.122,
+ 0.967,
+ 21,
+ 1,
+ 1.222,
+ 27.599,
+ 1.478,
+ 30,
+ 1.733,
+ 30,
+ 0,
+ 3.333,
+ 30
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamRightShoulderUp",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.244,
+ 0,
+ 0.322,
+ 2,
+ 0.4,
+ 2,
+ 1,
+ 0.589,
+ 2,
+ 0.778,
+ -19,
+ 0.967,
+ -19,
+ 1,
+ 1.222,
+ -19,
+ 1.478,
+ -13,
+ 1.733,
+ -13,
+ 0,
+ 3.333,
+ -13
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllRotate",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairFront",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairSide",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairBack",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairFrontFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairSideFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairBackFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamJacket",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamChainWaist",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchSwingA1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchSwingA2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchAX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBSwitch",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBOpen",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBOpen2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBRoll",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBLR",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBUD",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL01",
+ "Segments": [
+ 0,
+ 0.7,
+ 1,
+ 0.056,
+ 0.7,
+ 0.111,
+ 0.7,
+ 0.167,
+ 0.7,
+ 1,
+ 0.422,
+ 0.7,
+ 0.678,
+ 0.191,
+ 0.933,
+ -0.5,
+ 1,
+ 1.211,
+ -1.251,
+ 1.489,
+ -1.5,
+ 1.767,
+ -1.5,
+ 0,
+ 3.333,
+ -1.5
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL02",
+ "Segments": [
+ 0,
+ -8,
+ 1,
+ 0.056,
+ -8,
+ 0.111,
+ -8,
+ 0.167,
+ -8,
+ 1,
+ 0.433,
+ -8,
+ 0.7,
+ -8,
+ 0.967,
+ -8,
+ 1,
+ 1.233,
+ -8,
+ 1.5,
+ -8,
+ 1.767,
+ -8,
+ 0,
+ 3.333,
+ -8
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL03",
+ "Segments": [
+ 0,
+ -3,
+ 1,
+ 0.056,
+ -3,
+ 0.111,
+ -3,
+ 0.167,
+ -3,
+ 1,
+ 0.433,
+ -3,
+ 0.7,
+ -3,
+ 0.967,
+ -3,
+ 1,
+ 1.233,
+ -3,
+ 1.5,
+ 0,
+ 1.767,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL04",
+ "FadeInTime": 0.2,
+ "Segments": [
+ 0,
+ -1,
+ 1,
+ 0.056,
+ -1,
+ 0.111,
+ -1,
+ 0.167,
+ -1,
+ 0,
+ 3.333,
+ -1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR01",
+ "Segments": [
+ 0,
+ 0.7,
+ 1,
+ 0.056,
+ 0.7,
+ 0.111,
+ 0.7,
+ 0.167,
+ 0.7,
+ 1,
+ 0.422,
+ 0.7,
+ 0.678,
+ 0.191,
+ 0.933,
+ -0.5,
+ 1,
+ 1.211,
+ -1.251,
+ 1.489,
+ -1.5,
+ 1.767,
+ -1.5,
+ 0,
+ 3.333,
+ -1.5
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR02",
+ "Segments": [
+ 0,
+ -8,
+ 1,
+ 0.056,
+ -8,
+ 0.111,
+ -8,
+ 0.167,
+ -8,
+ 1,
+ 0.433,
+ -8,
+ 0.7,
+ -8,
+ 0.967,
+ -8,
+ 1,
+ 1.233,
+ -8,
+ 1.5,
+ -7,
+ 1.767,
+ -7,
+ 0,
+ 3.333,
+ -7
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR03",
+ "Segments": [
+ 0,
+ -3,
+ 1,
+ 0.056,
+ -3,
+ 0.111,
+ -3,
+ 0.167,
+ -3,
+ 1,
+ 0.433,
+ -3,
+ 0.7,
+ -3,
+ 0.967,
+ -3,
+ 1,
+ 1.233,
+ -3,
+ 1.5,
+ -10,
+ 1.767,
+ -10,
+ 0,
+ 3.333,
+ -10
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR04",
+ "FadeInTime": 0.2,
+ "Segments": [
+ 0,
+ -1,
+ 1,
+ 0.056,
+ -1,
+ 0.111,
+ -1,
+ 0.167,
+ -1,
+ 0,
+ 3.333,
+ -1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand01Roll",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll3",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCLHandRoll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDLHand03Roll",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER04",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand04Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand04Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand06Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand06Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 3.333,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartWatchA",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 3.33,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartWatchB",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 3.33,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmAL",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 3.33,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmAR",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 3.33,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmBR",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 3.33,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmCL",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 3.33,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmDL",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 3.33,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmER",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 3.33,
+ 0
+ ]
+ }
+ ]
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_07.motion3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_07.motion3.json
new file mode 100644
index 0000000..0809923
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/motions/mtn_07.motion3.json
@@ -0,0 +1,1907 @@
+锘縶
+ "Version": 3,
+ "Meta": {
+ "Duration": 4,
+ "Fps": 30.0,
+ "Loop": true,
+ "AreBeziersRestricted": true,
+ "CurveCount": 96,
+ "TotalSegmentCount": 221,
+ "TotalPointCount": 564,
+ "UserDataCount": 0,
+ "TotalUserDataSize": 0
+ },
+ "Curves": [
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.333,
+ 0,
+ 0.5,
+ 0,
+ 0.667,
+ 0,
+ 1,
+ 0.767,
+ 0,
+ 0.867,
+ -13,
+ 0.967,
+ -13,
+ 1,
+ 1.067,
+ -13,
+ 1.167,
+ 11,
+ 1.267,
+ 11,
+ 1,
+ 1.4,
+ 11,
+ 1.533,
+ -8,
+ 1.667,
+ -8,
+ 1,
+ 1.833,
+ -8,
+ 2,
+ 0,
+ 2.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.2,
+ 0,
+ 0.233,
+ 0,
+ 0.267,
+ 0,
+ 1,
+ 0.333,
+ 0,
+ 0.4,
+ 9,
+ 0.467,
+ 9,
+ 1,
+ 0.7,
+ 9,
+ 0.933,
+ -6,
+ 1.167,
+ -6,
+ 1,
+ 1.6,
+ -6,
+ 2.033,
+ -1,
+ 2.467,
+ -1,
+ 1,
+ 2.622,
+ -1,
+ 2.778,
+ -4,
+ 2.933,
+ -4,
+ 1,
+ 3.078,
+ -4,
+ 3.222,
+ 0,
+ 3.367,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.3,
+ 0,
+ 0.433,
+ 4,
+ 0.567,
+ 4,
+ 1,
+ 0.978,
+ 4,
+ 1.389,
+ 0,
+ 1.8,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLOpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 1,
+ 0.289,
+ 1,
+ 0.411,
+ 1,
+ 0.533,
+ 1,
+ 1,
+ 0.622,
+ 1,
+ 0.711,
+ 0,
+ 0.8,
+ 0,
+ 1,
+ 1.522,
+ 0,
+ 2.244,
+ 0,
+ 2.967,
+ 0,
+ 1,
+ 3.078,
+ 0,
+ 3.189,
+ 1,
+ 3.3,
+ 1,
+ 0,
+ 4,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeLForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeROpen",
+ "Segments": [
+ 0,
+ 1,
+ 1,
+ 0.056,
+ 1,
+ 0.111,
+ 1,
+ 0.167,
+ 1,
+ 1,
+ 0.289,
+ 1,
+ 0.411,
+ 1,
+ 0.533,
+ 1,
+ 1,
+ 0.622,
+ 1,
+ 0.711,
+ 0,
+ 0.8,
+ 0,
+ 1,
+ 1.522,
+ 0,
+ 2.244,
+ 0,
+ 2.967,
+ 0,
+ 1,
+ 3.078,
+ 0,
+ 3.189,
+ 1,
+ 3.3,
+ 1,
+ 0,
+ 4,
+ 1
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRSmile",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeRForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.289,
+ 0,
+ 0.411,
+ -0.4,
+ 0.533,
+ -0.4,
+ 1,
+ 1.344,
+ -0.4,
+ 2.156,
+ -0.4,
+ 2.967,
+ -0.4,
+ 1,
+ 3.078,
+ -0.4,
+ 3.189,
+ 0,
+ 3.3,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamEyeBallForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRAngle",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowLForm2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBrowRForm2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamTeethOn",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamCheek",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGlassUD",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassWhite",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassHighlight",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamGrassHighlightMove",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.256,
+ 0,
+ 0.344,
+ 2.02,
+ 0.433,
+ 2.02,
+ 1,
+ 0.856,
+ 2.02,
+ 1.278,
+ 0,
+ 1.7,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWaistAngleZ",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBodyPosition",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamBreath",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.256,
+ 0,
+ 0.344,
+ 0.5,
+ 0.433,
+ 0.5,
+ 1,
+ 0.633,
+ 0.5,
+ 0.833,
+ 0.279,
+ 1.033,
+ 0.2,
+ 1,
+ 1.467,
+ 0.029,
+ 1.9,
+ 0,
+ 2.333,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamLeftShoulderUp",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.278,
+ 0,
+ 0.389,
+ 10,
+ 0.5,
+ 10,
+ 1,
+ 0.633,
+ 10,
+ 0.767,
+ -5,
+ 0.9,
+ -5,
+ 0,
+ 4,
+ -5
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamRightShoulderUp",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 1,
+ 0.278,
+ 0,
+ 0.389,
+ 10,
+ 0.5,
+ 10,
+ 1,
+ 0.633,
+ 10,
+ 0.767,
+ -5,
+ 0.9,
+ -5,
+ 0,
+ 4,
+ -5
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllY",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamAllRotate",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairFront",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairSide",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairBack",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairFrontFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairSideFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamHairBackFuwa",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamJacket",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamChainWaist",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchSwingA1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchSwingA2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchAX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBSwitch",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBOpen",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBOpen2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBX",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBRoll",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBLR",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamWatchBUD",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAL04",
+ "FadeInTime": 0.2,
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmAR04",
+ "FadeInTime": 0.2,
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBR03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand01Roll",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmBRHand05Roll3",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCR03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmCLHandRoll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDL03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmDLHand03Roll",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER01",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER02",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER03",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmER04",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand04Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand04Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand06Roll1",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Id": "ParamArmERHand06Roll2",
+ "Segments": [
+ 0,
+ 0,
+ 1,
+ 0.056,
+ 0,
+ 0.111,
+ 0,
+ 0.167,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartWatchA",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 4,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartWatchB",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmAL",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 4,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmAR",
+ "Segments": [
+ 0,
+ 1,
+ 0,
+ 4,
+ 1
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmBR",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmCL",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmDL",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ },
+ {
+ "Target": "PartOpacity",
+ "Id": "PartArmER",
+ "Segments": [
+ 0,
+ 0,
+ 0,
+ 4,
+ 0
+ ]
+ }
+ ]
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori.pose3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori.pose3.json
new file mode 100644
index 0000000..c3da66e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori.pose3.json
@@ -0,0 +1,44 @@
+{
+ "Type": "Live2D Pose",
+ "FadeInTime": 0.2,
+ "Groups": [
+ [
+ {
+ "Id": "PartArmAL",
+ "Link": []
+ },
+ {
+ "Id": "PartArmCL",
+ "Link": []
+ },
+ {
+ "Id": "PartArmDL",
+ "Link": []
+ }
+ ],
+ [
+ {
+ "Id": "PartArmAR",
+ "Link": []
+ },
+ {
+ "Id": "PartArmBR",
+ "Link": []
+ },
+ {
+ "Id": "PartArmER",
+ "Link": []
+ }
+ ],
+ [
+ {
+ "Id": "PartWatchA",
+ "Link": []
+ },
+ {
+ "Id": "PartWatchB",
+ "Link": []
+ }
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori_pro_t06.4096/texture_00.png b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori_pro_t06.4096/texture_00.png
new file mode 100644
index 0000000..c677c88
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori_pro_t06.4096/texture_00.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori_pro_t06.cdi3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori_pro_t06.cdi3.json
new file mode 100644
index 0000000..ada83b4
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori_pro_t06.cdi3.json
@@ -0,0 +1,711 @@
+{
+ "Version": 3,
+ "Parameters": [
+ {
+ "Id": "ParamAngleX",
+ "GroupId": "",
+ "Name": "瑙掑害 X"
+ },
+ {
+ "Id": "ParamAngleY",
+ "GroupId": "",
+ "Name": "瑙掑害 Y"
+ },
+ {
+ "Id": "ParamAngleZ",
+ "GroupId": "",
+ "Name": "瑙掑害 Z"
+ },
+ {
+ "Id": "ParamEyeLOpen",
+ "GroupId": "ParamGroupExpression",
+ "Name": "宸︾溂 寮�闂�"
+ },
+ {
+ "Id": "ParamEyeLSmile",
+ "GroupId": "ParamGroupExpression",
+ "Name": "宸︾溂 寰瑧"
+ },
+ {
+ "Id": "ParamEyeLForm",
+ "GroupId": "ParamGroupExpression",
+ "Name": "宸︾溂 鍙樺舰"
+ },
+ {
+ "Id": "ParamEyeROpen",
+ "GroupId": "ParamGroupExpression",
+ "Name": "鍙崇溂 寮�闂�"
+ },
+ {
+ "Id": "ParamEyeRSmile",
+ "GroupId": "ParamGroupExpression",
+ "Name": "鍙崇溂 寰瑧"
+ },
+ {
+ "Id": "ParamEyeRForm",
+ "GroupId": "ParamGroupExpression",
+ "Name": "鍙崇溂 鍙樺舰"
+ },
+ {
+ "Id": "ParamEyeBallX",
+ "GroupId": "ParamGroupExpression",
+ "Name": "鐪肩彔 X"
+ },
+ {
+ "Id": "ParamEyeBallY",
+ "GroupId": "ParamGroupExpression",
+ "Name": "鐪肩彔 Y"
+ },
+ {
+ "Id": "ParamEyeBallForm",
+ "GroupId": "ParamGroupExpression",
+ "Name": "鐪肩彔 缂╂斁"
+ },
+ {
+ "Id": "ParamBrowLY",
+ "GroupId": "ParamGroupExpression",
+ "Name": "宸︾湁 涓婁笅"
+ },
+ {
+ "Id": "ParamBrowRY",
+ "GroupId": "ParamGroupExpression",
+ "Name": "鍙崇湁 涓婁笅"
+ },
+ {
+ "Id": "ParamBrowLX",
+ "GroupId": "ParamGroupExpression",
+ "Name": "宸︾湁 宸﹀彸"
+ },
+ {
+ "Id": "ParamBrowRX",
+ "GroupId": "ParamGroupExpression",
+ "Name": "鍙崇湁 宸﹀彸"
+ },
+ {
+ "Id": "ParamBrowLAngle",
+ "GroupId": "ParamGroupExpression",
+ "Name": "宸︾湁 瑙掑害"
+ },
+ {
+ "Id": "ParamBrowRAngle",
+ "GroupId": "ParamGroupExpression",
+ "Name": "鍙崇湁 瑙掑害"
+ },
+ {
+ "Id": "ParamBrowLForm",
+ "GroupId": "ParamGroupExpression",
+ "Name": "宸︾湁 鍙樺舰"
+ },
+ {
+ "Id": "ParamBrowLForm2",
+ "GroupId": "ParamGroupExpression",
+ "Name": "宸︾湁 鍙樺舰2"
+ },
+ {
+ "Id": "ParamBrowRForm",
+ "GroupId": "ParamGroupExpression",
+ "Name": "鍙崇湁 鍙樺舰"
+ },
+ {
+ "Id": "ParamBrowRForm2",
+ "GroupId": "ParamGroupExpression",
+ "Name": "鍙崇湁 鍙樺舰2"
+ },
+ {
+ "Id": "ParamMouthForm",
+ "GroupId": "ParamGroupExpression",
+ "Name": "鍢� 鍙樺舰"
+ },
+ {
+ "Id": "ParamMouthOpenY",
+ "GroupId": "ParamGroupExpression",
+ "Name": "鍢� 寮�闂�"
+ },
+ {
+ "Id": "ParamMouthForm2",
+ "GroupId": "ParamGroupExpression",
+ "Name": "鍢� 鍙樺舰2"
+ },
+ {
+ "Id": "ParamTeethOn",
+ "GroupId": "ParamGroupExpression",
+ "Name": "鐗欓娇鐨勬樉绀�"
+ },
+ {
+ "Id": "ParamCheek",
+ "GroupId": "ParamGroupExpression",
+ "Name": "瀹崇緸"
+ },
+ {
+ "Id": "ParamGlassUD",
+ "GroupId": "ParamGroupExpression",
+ "Name": "鐪奸暅 涓婁笅"
+ },
+ {
+ "Id": "ParamGrassWhite",
+ "GroupId": "ParamGroupExpression",
+ "Name": "闀滅墖 鐧�"
+ },
+ {
+ "Id": "ParamGrassHighlight",
+ "GroupId": "ParamGroupExpression",
+ "Name": "闀滅墖 鍙嶅厜鏄剧ず"
+ },
+ {
+ "Id": "ParamGrassHighlightMove",
+ "GroupId": "ParamGroupExpression",
+ "Name": "闀滅墖 鍙嶅厜绉诲姩"
+ },
+ {
+ "Id": "ParamBodyAngleX",
+ "GroupId": "ParamGroupBody",
+ "Name": "韬綋鐨勬棆杞� X"
+ },
+ {
+ "Id": "ParamBodyAngleY",
+ "GroupId": "ParamGroupBody",
+ "Name": "韬綋鐨勬棆杞� Y"
+ },
+ {
+ "Id": "ParamBodyAngleZ",
+ "GroupId": "ParamGroupBody",
+ "Name": "韬綋鐨勬棆杞� Z"
+ },
+ {
+ "Id": "ParamWaistAngleZ",
+ "GroupId": "ParamGroupBody",
+ "Name": "鑵扮殑鏃嬭浆 Z"
+ },
+ {
+ "Id": "ParamBodyPosition",
+ "GroupId": "ParamGroupBody",
+ "Name": "韬綋鐨勫墠鍚�"
+ },
+ {
+ "Id": "ParamBreath",
+ "GroupId": "ParamGroupBody",
+ "Name": "鍛煎惛"
+ },
+ {
+ "Id": "ParamLeftShoulderUp",
+ "GroupId": "ParamGroupBody",
+ "Name": "宸﹁偐鐨勪笂涓�"
+ },
+ {
+ "Id": "ParamRightShoulderUp",
+ "GroupId": "ParamGroupBody",
+ "Name": "鍙宠偐鐨勪笂涓�"
+ },
+ {
+ "Id": "ParamAllX",
+ "GroupId": "ParamGroup",
+ "Name": "鏁翠綋鐨勭Щ鍔� X"
+ },
+ {
+ "Id": "ParamAllY",
+ "GroupId": "ParamGroup",
+ "Name": "鏁翠綋鐨勭Щ鍔� Y"
+ },
+ {
+ "Id": "ParamAllRotate",
+ "GroupId": "ParamGroup",
+ "Name": "鏁翠綋鐨勬棆杞�"
+ },
+ {
+ "Id": "ParamHairFront",
+ "GroupId": "ParamGroupSway",
+ "Name": "澶村彂鎽囧姩 鍓�"
+ },
+ {
+ "Id": "ParamHairSide",
+ "GroupId": "ParamGroupSway",
+ "Name": "澶村彂鎽囧姩 渚�"
+ },
+ {
+ "Id": "ParamHairBack",
+ "GroupId": "ParamGroupSway",
+ "Name": "澶村彂鎽囧姩 鍚�"
+ },
+ {
+ "Id": "ParamHairFrontFuwa",
+ "GroupId": "ParamGroupSway",
+ "Name": "鍓嶅彂 钃澗"
+ },
+ {
+ "Id": "ParamHairSideFuwa",
+ "GroupId": "ParamGroupSway",
+ "Name": "渚у彂 钃澗"
+ },
+ {
+ "Id": "ParamHairBackFuwa",
+ "GroupId": "ParamGroupSway",
+ "Name": "鍚庡彂 钃澗"
+ },
+ {
+ "Id": "ParamJacket",
+ "GroupId": "ParamGroupSway",
+ "Name": "鐕曞熬鐨勬憞鍔�"
+ },
+ {
+ "Id": "ParamChainWaist",
+ "GroupId": "ParamGroupSway",
+ "Name": "琛ㄩ摼A鐨勬憞鍔�"
+ },
+ {
+ "Id": "ParamWatchSwingA1",
+ "GroupId": "ParamGroupSway",
+ "Name": "鎬�琛ˋ 鎽囧姩1"
+ },
+ {
+ "Id": "ParamWatchSwingA2",
+ "GroupId": "ParamGroupSway",
+ "Name": "鎬�琛ˋ 鎽囧姩2"
+ },
+ {
+ "Id": "ParamWatchBChain",
+ "GroupId": "ParamGroupSway",
+ "Name": "鎬�琛˙ 琛ㄩ摼鐨勬憞鍔�"
+ },
+ {
+ "Id": "ParamWatchAX",
+ "GroupId": "ParamGroup8",
+ "Name": "鎬�琛ˋ 妯悜鏃嬭浆"
+ },
+ {
+ "Id": "ParamWatchBSwitch",
+ "GroupId": "ParamGroup9",
+ "Name": "鎬�琛˙ 寮�鍏�"
+ },
+ {
+ "Id": "ParamWatchBOpen",
+ "GroupId": "ParamGroup9",
+ "Name": "鎬�琛˙ 琛ㄧ洊鐨勫紑闂�"
+ },
+ {
+ "Id": "ParamWatchBOpen2",
+ "GroupId": "ParamGroup9",
+ "Name": "鎬�琛˙ 琛ㄧ洏鐨勫紑闂�"
+ },
+ {
+ "Id": "ParamWatchBX",
+ "GroupId": "ParamGroup9",
+ "Name": "鎬�琛˙ 妯悜鏃嬭浆"
+ },
+ {
+ "Id": "ParamWatchBRoll",
+ "GroupId": "ParamGroup9",
+ "Name": "鎬�琛˙ 鏃嬭浆"
+ },
+ {
+ "Id": "ParamWatchBLR",
+ "GroupId": "ParamGroup9",
+ "Name": "鎬�琛˙ 宸﹀彸"
+ },
+ {
+ "Id": "ParamWatchBUD",
+ "GroupId": "ParamGroup9",
+ "Name": "鎬�琛˙ 涓婁笅"
+ },
+ {
+ "Id": "ParamArmAL01",
+ "GroupId": "ParamGroup3",
+ "Name": "宸︽墜鑷侫 鑲�"
+ },
+ {
+ "Id": "ParamArmAL02",
+ "GroupId": "ParamGroup3",
+ "Name": "宸﹁偐A 鎵嬭倶鏃嬭浆"
+ },
+ {
+ "Id": "ParamArmAL03",
+ "GroupId": "ParamGroup3",
+ "Name": "宸︽墜鑷侫 鎵嬭厱"
+ },
+ {
+ "Id": "ParamArmAL04",
+ "GroupId": "ParamGroup3",
+ "Name": "宸︽墜鑷侫 鍓嶈噦鐨勫墠鍚�"
+ },
+ {
+ "Id": "ParamArmAR01",
+ "GroupId": "ParamGroup2",
+ "Name": "鍙虫墜鑷侫 鑲╃殑鏃嬭浆"
+ },
+ {
+ "Id": "ParamArmAR02",
+ "GroupId": "ParamGroup2",
+ "Name": "鍙虫墜鑷侫 鎵嬭倶鐨勬棆杞�"
+ },
+ {
+ "Id": "ParamArmAR03",
+ "GroupId": "ParamGroup2",
+ "Name": "鍙虫墜鑷侫 鎵嬭厱鐨勬棆杞�"
+ },
+ {
+ "Id": "ParamArmAR04",
+ "GroupId": "ParamGroup2",
+ "Name": "鍙虫墜鑷侫 鍓嶈噦鐨勫墠鍚�"
+ },
+ {
+ "Id": "ParamArmBR01",
+ "GroupId": "ParamGroup4",
+ "Name": "鍙虫墜鑷侭 鑲╃殑鏃嬭浆"
+ },
+ {
+ "Id": "ParamArmBR02",
+ "GroupId": "ParamGroup4",
+ "Name": "鍙虫墜鑷侭 鎵嬭倶鐨勬棆杞�"
+ },
+ {
+ "Id": "ParamArmBR03",
+ "GroupId": "ParamGroup4",
+ "Name": "鍙虫墜鑷侭 鎵嬭厱鐨勬棆杞�"
+ },
+ {
+ "Id": "ParamArmBRHand01",
+ "GroupId": "ParamGroup4",
+ "Name": "鍙虫墜01 鏄剧ず"
+ },
+ {
+ "Id": "ParamArmBRHand01Roll",
+ "GroupId": "ParamGroup4",
+ "Name": "鍙虫墜01 鎵嬫寚寮洸"
+ },
+ {
+ "Id": "ParamArmBRHand05",
+ "GroupId": "ParamGroup4",
+ "Name": "鍙虫墜05 鏄剧ず"
+ },
+ {
+ "Id": "ParamArmBRHand05Roll1",
+ "GroupId": "ParamGroup4",
+ "Name": "鍙虫墜05 鎵嬫寚寮洸1"
+ },
+ {
+ "Id": "ParamArmBRHand05Roll2",
+ "GroupId": "ParamGroup4",
+ "Name": "鍙虫墜05 鎵嬫寚寮洸2"
+ },
+ {
+ "Id": "ParamArmBRHand05Roll3",
+ "GroupId": "ParamGroup4",
+ "Name": "鍙虫墜05 鎵嬫寚寮洸3"
+ },
+ {
+ "Id": "ParamArmCR01",
+ "GroupId": "ParamGroup5",
+ "Name": "宸︽墜鑷侰 鑲╃殑鏃嬭浆"
+ },
+ {
+ "Id": "ParamArmCR02",
+ "GroupId": "ParamGroup5",
+ "Name": "宸︽墜鑷侰 鎵嬭倶鐨勬棆杞�"
+ },
+ {
+ "Id": "ParamArmCR03",
+ "GroupId": "ParamGroup5",
+ "Name": "宸︽墜鑷侰 鎵嬭厱鐨勬棆杞�"
+ },
+ {
+ "Id": "ParamArmCLHandRoll1",
+ "GroupId": "ParamGroup5",
+ "Name": "宸︽墜C 鎵嬫寚寮洸1"
+ },
+ {
+ "Id": "ParamArmDL01",
+ "GroupId": "ParamGroup6",
+ "Name": "宸︽墜鑷侱 鑲╃殑鏃嬭浆"
+ },
+ {
+ "Id": "ParamArmDL02",
+ "GroupId": "ParamGroup6",
+ "Name": "宸︽墜鑷侱 鎵嬭倶鐨勬棆杞�"
+ },
+ {
+ "Id": "ParamArmDL03",
+ "GroupId": "ParamGroup6",
+ "Name": "宸︽墜鑷侱 鎵嬭厱鐨勬棆杞�"
+ },
+ {
+ "Id": "ParamArmDLHand03Roll",
+ "GroupId": "ParamGroup6",
+ "Name": "宸︽墜03 鎵嬫寚寮洸"
+ },
+ {
+ "Id": "ParamArmER01",
+ "GroupId": "ParamGroup7",
+ "Name": "鍙虫墜鑷侲 鑲╃殑鏃嬭浆"
+ },
+ {
+ "Id": "ParamArmER02",
+ "GroupId": "ParamGroup7",
+ "Name": "鍙虫墜鑷侲 鎵嬭倶鐨勬棆杞�"
+ },
+ {
+ "Id": "ParamArmER03",
+ "GroupId": "ParamGroup7",
+ "Name": "鍙虫墜鑷侲 鎵嬭厱鐨勬棆杞�"
+ },
+ {
+ "Id": "ParamArmER04",
+ "GroupId": "ParamGroup7",
+ "Name": "鍙虫墜鑷侲 涓婅噦鐨勯暱搴�"
+ },
+ {
+ "Id": "ParamArmERHand04",
+ "GroupId": "ParamGroup7",
+ "Name": "鍙虫墜04 鏄剧ず"
+ },
+ {
+ "Id": "ParamArmERHand04Roll1",
+ "GroupId": "ParamGroup7",
+ "Name": "鍙虫墜04 鎵嬫寚寮洸1"
+ },
+ {
+ "Id": "ParamArmERHand04Roll2",
+ "GroupId": "ParamGroup7",
+ "Name": "鍙虫墜04 鎵嬫寚寮洸2"
+ },
+ {
+ "Id": "ParamArmERHand06",
+ "GroupId": "ParamGroup7",
+ "Name": "鍙虫墜06 鏄剧ず"
+ },
+ {
+ "Id": "ParamArmERHand06Roll1",
+ "GroupId": "ParamGroup7",
+ "Name": "鍙虫墜06 鎵嬫寚寮洸1"
+ },
+ {
+ "Id": "ParamArmERHand06Roll2",
+ "GroupId": "ParamGroup7",
+ "Name": "鍙虫墜06 鎵嬫寚寮洸2"
+ }
+ ],
+ "ParameterGroups": [
+ {
+ "Id": "ParamGroupExpression",
+ "GroupId": "",
+ "Name": "琛ㄦ儏"
+ },
+ {
+ "Id": "ParamGroupBody",
+ "GroupId": "",
+ "Name": "韬綋"
+ },
+ {
+ "Id": "ParamGroup",
+ "GroupId": "",
+ "Name": "鏁翠綋绉诲姩"
+ },
+ {
+ "Id": "ParamGroupSway",
+ "GroupId": "",
+ "Name": "鎽囧姩"
+ },
+ {
+ "Id": "ParamGroup8",
+ "GroupId": "",
+ "Name": "鎬�琛ˋ"
+ },
+ {
+ "Id": "ParamGroup9",
+ "GroupId": "",
+ "Name": "鎬�琛˙"
+ },
+ {
+ "Id": "ParamGroup3",
+ "GroupId": "",
+ "Name": "宸︽墜鑷侫"
+ },
+ {
+ "Id": "ParamGroup2",
+ "GroupId": "",
+ "Name": "鍙虫墜鑷侫"
+ },
+ {
+ "Id": "ParamGroup4",
+ "GroupId": "",
+ "Name": "鍙虫墜鑷侭"
+ },
+ {
+ "Id": "ParamGroup5",
+ "GroupId": "",
+ "Name": "宸︽墜鑷侰"
+ },
+ {
+ "Id": "ParamGroup6",
+ "GroupId": "",
+ "Name": "宸︽墜鑷侱"
+ },
+ {
+ "Id": "ParamGroup7",
+ "GroupId": "",
+ "Name": "鍙虫墜鑷侲"
+ }
+ ],
+ "Parts": [
+ {
+ "Id": "PartCredit",
+ "Name": "鍚嶇墝"
+ },
+ {
+ "Id": "PartCore",
+ "Name": "CORE"
+ },
+ {
+ "Id": "PartGlass",
+ "Name": "鐪奸暅"
+ },
+ {
+ "Id": "PartWatchA",
+ "Name": "鎬�琛ˋ"
+ },
+ {
+ "Id": "PartWatchB",
+ "Name": "鎬�琛˙"
+ },
+ {
+ "Id": "PartHairFront",
+ "Name": "鍓嶅彂"
+ },
+ {
+ "Id": "PartHead",
+ "Name": "澶�"
+ },
+ {
+ "Id": "PartUpperBody",
+ "Name": "涓婂崐韬�"
+ },
+ {
+ "Id": "PartHairBack",
+ "Name": "鍚庡彂"
+ },
+ {
+ "Id": "PartLowerBody",
+ "Name": "涓嬪崐韬�"
+ },
+ {
+ "Id": "PartArmAL",
+ "Name": "宸︽墜鑷侫"
+ },
+ {
+ "Id": "PartArmAR",
+ "Name": "鍙虫墜鑷侫"
+ },
+ {
+ "Id": "PartArmBR",
+ "Name": "鍙虫墜鑷侭"
+ },
+ {
+ "Id": "PartArmCL",
+ "Name": "宸︽墜鑷侰"
+ },
+ {
+ "Id": "PartArmDL",
+ "Name": "宸︽墜鑷侱"
+ },
+ {
+ "Id": "PartArmER",
+ "Name": "鍙虫墜鑷侲"
+ },
+ {
+ "Id": "PartEyeBlow",
+ "Name": "鐪夋瘺"
+ },
+ {
+ "Id": "PartEyeR",
+ "Name": "鍙崇溂"
+ },
+ {
+ "Id": "PartEyeL",
+ "Name": "宸︾溂"
+ },
+ {
+ "Id": "PartHairLine",
+ "Name": "鍙戦檯绾�"
+ },
+ {
+ "Id": "PartHairShadow",
+ "Name": "澶村彂闃村奖"
+ },
+ {
+ "Id": "PartNose",
+ "Name": "榧诲瓙"
+ },
+ {
+ "Id": "PartMouth",
+ "Name": "鍢�"
+ },
+ {
+ "Id": "PartJacket",
+ "Name": "鐕曞熬鏈�"
+ },
+ {
+ "Id": "PartArmALFore",
+ "Name": "宸︽墜鑷侫 鍓嶈噦"
+ },
+ {
+ "Id": "PartArmARFore",
+ "Name": "鍙虫墜鑷侫 鍓嶈噦"
+ },
+ {
+ "Id": "PartHand11",
+ "Name": "鎵嬪_1"
+ },
+ {
+ "Id": "PartHand51",
+ "Name": "鎵嬪_5"
+ },
+ {
+ "Id": "PartHand21",
+ "Name": "鎵嬪_2"
+ },
+ {
+ "Id": "PartHand31",
+ "Name": "鎵嬪_3"
+ },
+ {
+ "Id": "PartHand41",
+ "Name": "鎵嬪_4"
+ },
+ {
+ "Id": "PartHand61",
+ "Name": "鎵嬪_6"
+ }
+ ],
+ "CombinedParameters": [
+ [
+ "ParamAngleX",
+ "ParamAngleY"
+ ],
+ [
+ "ParamEyeBallX",
+ "ParamEyeBallY"
+ ],
+ [
+ "ParamMouthForm",
+ "ParamMouthOpenY"
+ ],
+ [
+ "ParamBodyAngleX",
+ "ParamBodyAngleY"
+ ],
+ [
+ "ParamLeftShoulderUp",
+ "ParamRightShoulderUp"
+ ],
+ [
+ "ParamAllX",
+ "ParamAllY"
+ ],
+ [
+ "ParamWatchSwingA1",
+ "ParamWatchSwingA2"
+ ],
+ [
+ "ParamWatchBLR",
+ "ParamWatchBUD"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori_pro_t06.moc3 b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori_pro_t06.moc3
new file mode 100644
index 0000000..9f7303b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori_pro_t06.moc3
Binary files differ
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori_pro_t06.model3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori_pro_t06.model3.json
new file mode 100644
index 0000000..9da5c25
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori_pro_t06.model3.json
@@ -0,0 +1,123 @@
+{
+ "Version": 3,
+ "FileReferences": {
+ "Moc": "natori_pro_t06.moc3",
+ "Textures": [
+ "natori_pro_t06.4096/texture_00.png"
+ ],
+ "Physics": "natori_pro_t06.physics3.json",
+ "Pose": "natori.pose3.json",
+ "DisplayInfo": "natori_pro_t06.cdi3.json",
+ "Expressions": [
+ {
+ "Name": "Angry",
+ "File": "exp/Angry.exp3.json"
+ },
+ {
+ "Name": "Blushing",
+ "File": "exp/Blushing.exp3.json"
+ },
+ {
+ "Name": "Normal",
+ "File": "exp/Normal.exp3.json"
+ },
+ {
+ "Name": "Sad",
+ "File": "exp/Sad.exp3.json"
+ },
+ {
+ "Name": "Smile",
+ "File": "exp/Smile.exp3.json"
+ },
+ {
+ "Name": "Surprised",
+ "File": "exp/Surprised.exp3.json"
+ },
+ {
+ "Name": "exp_01",
+ "File": "exp/exp_01.exp3.json"
+ },
+ {
+ "Name": "exp_02",
+ "File": "exp/exp_02.exp3.json"
+ },
+ {
+ "Name": "exp_03",
+ "File": "exp/exp_03.exp3.json"
+ },
+ {
+ "Name": "exp_04",
+ "File": "exp/exp_04.exp3.json"
+ },
+ {
+ "Name": "exp_05",
+ "File": "exp/exp_05.exp3.json"
+ }
+ ],
+ "Motions": {
+ "Idle": [
+ {
+ "File": "motions/mtn_00.motion3.json"
+ },
+ {
+ "File": "motions/mtn_01.motion3.json"
+ },
+ {
+ "File": "motions/mtn_02.motion3.json"
+ }
+ ],
+ "Tap": [
+ {
+ "File": "motions/mtn_03.motion3.json"
+ }
+ ],
+ "FlickUp@Head": [
+ {
+ "File": "motions/mtn_04.motion3.json"
+ }
+ ],
+ "Flick@Body": [
+ {
+ "File": "motions/mtn_05.motion3.json"
+ }
+ ],
+ "FlickDown@Body": [
+ {
+ "File": "motions/mtn_06.motion3.json"
+ }
+ ],
+ "Tap@Head": [
+ {
+ "File": "motions/mtn_07.motion3.json"
+ }
+ ]
+ }
+ },
+ "Groups": [
+ {
+ "Target": "Parameter",
+ "Name": "LipSync",
+ "Ids": [
+ "ParamMouthOpenY"
+ ]
+ },
+ {
+ "Target": "Parameter",
+ "Name": "EyeBlink",
+ "Ids": [
+ "ParamEyeLOpen",
+ "ParamEyeROpen"
+ ]
+ }
+ ],
+ "HitAreas": [
+ {
+ "Id": "HitAreaHead",
+ "Name": ""
+ },
+ {
+ "Id": "HitAreaBody",
+ "Name": ""
+ }
+ ]
+}
diff --git a/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori_pro_t06.physics3.json b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori_pro_t06.physics3.json
new file mode 100644
index 0000000..857c665
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/resources/natori_pro_zh/runtime/natori_pro_t06.physics3.json
@@ -0,0 +1,966 @@
+{
+ "Version": 3,
+ "Meta": {
+ "PhysicsSettingCount": 11,
+ "TotalInputCount": 34,
+ "TotalOutputCount": 12,
+ "VertexCount": 23,
+ "EffectiveForces": {
+ "Gravity": {
+ "X": 0,
+ "Y": -1
+ },
+ "Wind": {
+ "X": 0,
+ "Y": 0
+ }
+ },
+ "PhysicsDictionary": [
+ {
+ "Id": "PhysicsSetting1",
+ "Name": "鍓嶉銆�鎻恒倢"
+ },
+ {
+ "Id": "PhysicsSetting2",
+ "Name": "妯銆�鎻恒倢"
+ },
+ {
+ "Id": "PhysicsSetting3",
+ "Name": "寰屻倣楂��鎻恒倢"
+ },
+ {
+ "Id": "PhysicsSetting4",
+ "Name": "鍓嶉銇点倧"
+ },
+ {
+ "Id": "PhysicsSetting5",
+ "Name": "妯銇点倧"
+ },
+ {
+ "Id": "PhysicsSetting6",
+ "Name": "寰屻倣楂伒銈�"
+ },
+ {
+ "Id": "PhysicsSetting7",
+ "Name": "鐕曞熬鎻恒倢"
+ },
+ {
+ "Id": "PhysicsSetting8",
+ "Name": "鎳愪腑鏅傝▓鑵般��鎻恒倢"
+ },
+ {
+ "Id": "PhysicsSetting9",
+ "Name": "鑵般伄銉併偋銉笺兂鎻恒倢"
+ },
+ {
+ "Id": "PhysicsSetting10",
+ "Name": "鎳愪腑鏅傝▓鑵般��妯洖杌�"
+ },
+ {
+ "Id": "PhysicsSetting11",
+ "Name": "鎳愪腑鏅傝▓B 銉併偋銉笺兂鎻恒倢"
+ }
+ ]
+ },
+ "PhysicsSettings": [
+ {
+ "Id": "PhysicsSetting1",
+ "Input": [
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleX"
+ },
+ "Weight": 60,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ"
+ },
+ "Weight": 60,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX"
+ },
+ "Weight": 40,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ"
+ },
+ "Weight": 40,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Output": [
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "ParamHairFront"
+ },
+ "VertexIndex": 1,
+ "Scale": 1.824,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Vertices": [
+ {
+ "Position": {
+ "X": 0,
+ "Y": 0
+ },
+ "Mobility": 1,
+ "Delay": 1,
+ "Acceleration": 1,
+ "Radius": 0
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 12.7
+ },
+ "Mobility": 0.95,
+ "Delay": 0.9,
+ "Acceleration": 1,
+ "Radius": 12.7
+ }
+ ],
+ "Normalization": {
+ "Position": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ },
+ "Angle": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ }
+ }
+ },
+ {
+ "Id": "PhysicsSetting2",
+ "Input": [
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleX"
+ },
+ "Weight": 60,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ"
+ },
+ "Weight": 60,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX"
+ },
+ "Weight": 40,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ"
+ },
+ "Weight": 40,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Output": [
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "ParamHairSide"
+ },
+ "VertexIndex": 1,
+ "Scale": 2,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Vertices": [
+ {
+ "Position": {
+ "X": 0,
+ "Y": 0
+ },
+ "Mobility": 1,
+ "Delay": 1,
+ "Acceleration": 1,
+ "Radius": 0
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 18.2
+ },
+ "Mobility": 0.95,
+ "Delay": 0.9,
+ "Acceleration": 1,
+ "Radius": 18.2
+ }
+ ],
+ "Normalization": {
+ "Position": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ },
+ "Angle": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ }
+ }
+ },
+ {
+ "Id": "PhysicsSetting3",
+ "Input": [
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleX"
+ },
+ "Weight": 60,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleZ"
+ },
+ "Weight": 60,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX"
+ },
+ "Weight": 40,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ"
+ },
+ "Weight": 40,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Output": [
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "ParamHairBack"
+ },
+ "VertexIndex": 1,
+ "Scale": 2,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Vertices": [
+ {
+ "Position": {
+ "X": 0,
+ "Y": 0
+ },
+ "Mobility": 1,
+ "Delay": 1,
+ "Acceleration": 1,
+ "Radius": 0
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 14.3
+ },
+ "Mobility": 1,
+ "Delay": 0.9,
+ "Acceleration": 1.42,
+ "Radius": 14.3
+ }
+ ],
+ "Normalization": {
+ "Position": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ },
+ "Angle": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ }
+ }
+ },
+ {
+ "Id": "PhysicsSetting4",
+ "Input": [
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleY"
+ },
+ "Weight": 35,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY"
+ },
+ "Weight": 30,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyPosition"
+ },
+ "Weight": 35,
+ "Type": "X",
+ "Reflect": true
+ }
+ ],
+ "Output": [
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "ParamHairFrontFuwa"
+ },
+ "VertexIndex": 1,
+ "Scale": 3,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": true
+ }
+ ],
+ "Vertices": [
+ {
+ "Position": {
+ "X": 0,
+ "Y": 0
+ },
+ "Mobility": 1,
+ "Delay": 1,
+ "Acceleration": 1,
+ "Radius": 0
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 11.9
+ },
+ "Mobility": 0.79,
+ "Delay": 0.9,
+ "Acceleration": 1,
+ "Radius": 11.9
+ }
+ ],
+ "Normalization": {
+ "Position": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ },
+ "Angle": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ }
+ }
+ },
+ {
+ "Id": "PhysicsSetting5",
+ "Input": [
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleY"
+ },
+ "Weight": 35,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY"
+ },
+ "Weight": 30,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyPosition"
+ },
+ "Weight": 35,
+ "Type": "X",
+ "Reflect": true
+ }
+ ],
+ "Output": [
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "ParamHairSideFuwa"
+ },
+ "VertexIndex": 1,
+ "Scale": 3.5,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": true
+ }
+ ],
+ "Vertices": [
+ {
+ "Position": {
+ "X": 0,
+ "Y": 0
+ },
+ "Mobility": 1,
+ "Delay": 1,
+ "Acceleration": 1,
+ "Radius": 0
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 14.3
+ },
+ "Mobility": 0.79,
+ "Delay": 0.9,
+ "Acceleration": 1.1,
+ "Radius": 14.3
+ }
+ ],
+ "Normalization": {
+ "Position": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ },
+ "Angle": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ }
+ }
+ },
+ {
+ "Id": "PhysicsSetting6",
+ "Input": [
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamAngleY"
+ },
+ "Weight": 35,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleY"
+ },
+ "Weight": 30,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyPosition"
+ },
+ "Weight": 35,
+ "Type": "X",
+ "Reflect": true
+ }
+ ],
+ "Output": [
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "ParamHairBackFuwa"
+ },
+ "VertexIndex": 1,
+ "Scale": 5,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": true
+ }
+ ],
+ "Vertices": [
+ {
+ "Position": {
+ "X": 0,
+ "Y": 0
+ },
+ "Mobility": 1,
+ "Delay": 1,
+ "Acceleration": 1,
+ "Radius": 0
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 11.3
+ },
+ "Mobility": 0.79,
+ "Delay": 0.9,
+ "Acceleration": 1.16,
+ "Radius": 11.3
+ }
+ ],
+ "Normalization": {
+ "Position": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ },
+ "Angle": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ }
+ }
+ },
+ {
+ "Id": "PhysicsSetting7",
+ "Input": [
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX"
+ },
+ "Weight": 100,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ"
+ },
+ "Weight": 70,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamWaistAngleZ"
+ },
+ "Weight": 30,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Output": [
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "ParamJacket"
+ },
+ "VertexIndex": 1,
+ "Scale": 1,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Vertices": [
+ {
+ "Position": {
+ "X": 0,
+ "Y": 0
+ },
+ "Mobility": 1,
+ "Delay": 1,
+ "Acceleration": 1,
+ "Radius": 0
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 31.9
+ },
+ "Mobility": 0.95,
+ "Delay": 0.8,
+ "Acceleration": 0.8,
+ "Radius": 31.9
+ }
+ ],
+ "Normalization": {
+ "Position": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ },
+ "Angle": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ }
+ }
+ },
+ {
+ "Id": "PhysicsSetting8",
+ "Input": [
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX"
+ },
+ "Weight": 100,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ"
+ },
+ "Weight": 70,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamWaistAngleZ"
+ },
+ "Weight": 30,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Output": [
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "ParamWatchSwingA1"
+ },
+ "VertexIndex": 1,
+ "Scale": 1,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "ParamWatchSwingA2"
+ },
+ "VertexIndex": 2,
+ "Scale": 1,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Vertices": [
+ {
+ "Position": {
+ "X": 0,
+ "Y": 0
+ },
+ "Mobility": 1,
+ "Delay": 1,
+ "Acceleration": 1,
+ "Radius": 0
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 15.4
+ },
+ "Mobility": 0.95,
+ "Delay": 1,
+ "Acceleration": 0.8,
+ "Radius": 15.4
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 31.9
+ },
+ "Mobility": 0.9,
+ "Delay": 1,
+ "Acceleration": 0.6,
+ "Radius": 16.5
+ }
+ ],
+ "Normalization": {
+ "Position": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ },
+ "Angle": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ }
+ }
+ },
+ {
+ "Id": "PhysicsSetting9",
+ "Input": [
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX"
+ },
+ "Weight": 100,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ"
+ },
+ "Weight": 70,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamWaistAngleZ"
+ },
+ "Weight": 30,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Output": [
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "ParamChainWaist"
+ },
+ "VertexIndex": 1,
+ "Scale": 1,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Vertices": [
+ {
+ "Position": {
+ "X": 0,
+ "Y": 0
+ },
+ "Mobility": 1,
+ "Delay": 1,
+ "Acceleration": 1,
+ "Radius": 0
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 11.6
+ },
+ "Mobility": 0.95,
+ "Delay": 1,
+ "Acceleration": 1,
+ "Radius": 11.6
+ }
+ ],
+ "Normalization": {
+ "Position": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ },
+ "Angle": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ }
+ }
+ },
+ {
+ "Id": "PhysicsSetting10",
+ "Input": [
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleX"
+ },
+ "Weight": 100,
+ "Type": "X",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamBodyAngleZ"
+ },
+ "Weight": 70,
+ "Type": "Angle",
+ "Reflect": false
+ },
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamWaistAngleZ"
+ },
+ "Weight": 30,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Output": [
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "ParamWatchAX"
+ },
+ "VertexIndex": 1,
+ "Scale": 2,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Vertices": [
+ {
+ "Position": {
+ "X": 0,
+ "Y": 0
+ },
+ "Mobility": 1,
+ "Delay": 1,
+ "Acceleration": 1,
+ "Radius": 0
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 72.1
+ },
+ "Mobility": 0.95,
+ "Delay": 1,
+ "Acceleration": 0.2,
+ "Radius": 72.1
+ }
+ ],
+ "Normalization": {
+ "Position": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ },
+ "Angle": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ }
+ }
+ },
+ {
+ "Id": "PhysicsSetting11",
+ "Input": [
+ {
+ "Source": {
+ "Target": "Parameter",
+ "Id": "ParamArmBR03"
+ },
+ "Weight": 100,
+ "Type": "X",
+ "Reflect": false
+ }
+ ],
+ "Output": [
+ {
+ "Destination": {
+ "Target": "Parameter",
+ "Id": "ParamWatchBChain"
+ },
+ "VertexIndex": 1,
+ "Scale": 2,
+ "Weight": 100,
+ "Type": "Angle",
+ "Reflect": false
+ }
+ ],
+ "Vertices": [
+ {
+ "Position": {
+ "X": 0,
+ "Y": 0
+ },
+ "Mobility": 1,
+ "Delay": 1,
+ "Acceleration": 1,
+ "Radius": 0
+ },
+ {
+ "Position": {
+ "X": 0,
+ "Y": 13.1
+ },
+ "Mobility": 0.95,
+ "Delay": 1,
+ "Acceleration": 0.66,
+ "Radius": 13.1
+ }
+ ],
+ "Normalization": {
+ "Position": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ },
+ "Angle": {
+ "Minimum": -10,
+ "Default": 0,
+ "Maximum": 10
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/start.py b/xiaozhi-esp32-server/main/digital-human/start.py
new file mode 100644
index 0000000..251bfc8
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/start.py
@@ -0,0 +1,55 @@
+import threading
+from pathlib import Path
+
+from wakeword_runtime.config import load_config, setup_logging
+from wakeword_runtime.runtime import TestRuntimeApplication, TestRuntimeHttpServer
+
+
+def main() -> int:
+ test_root = Path(__file__).resolve().parent
+ runtime_root = test_root / "wakeword_runtime"
+
+ config = load_config(runtime_root)
+ setup_logging(config.log_file, config.log_level)
+ http_server = TestRuntimeHttpServer(test_root)
+ app_lock = threading.RLock()
+ app = TestRuntimeApplication(config, http_server.event_bridge)
+
+ def restart_runtime() -> None:
+ nonlocal app
+ with app_lock:
+ app.shutdown()
+ next_config = load_config(runtime_root)
+ setup_logging(next_config.log_file, next_config.log_level)
+ next_app = TestRuntimeApplication(next_config, http_server.event_bridge)
+ next_app.setup()
+ next_app.start()
+ app = next_app
+
+ http_server.set_restart_handler(restart_runtime)
+
+ print(f"test runtime started: {http_server.page_url}")
+ print(f"wakeword bridge websocket: {http_server.bridge_url}")
+ print(f"wakeword enabled: {config.wakeword_enabled}")
+ print("press Ctrl+C to stop")
+
+ try:
+ with app_lock:
+ try:
+ app.setup()
+ app.start()
+ except Exception as e:
+ print(f"璀﹀憡: 鍞ら啋璇嶆湇鍔″惎鍔ㄥけ璐�({e})锛屾祴璇曢〉闈粛鍙甯镐娇鐢�")
+ http_server.serve_forever()
+ except KeyboardInterrupt:
+ print("test runtime stopped")
+ finally:
+ with app_lock:
+ app.shutdown()
+ http_server.shutdown()
+
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/__init__.py b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/__init__.py
new file mode 100644
index 0000000..dc29ee2
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/__init__.py
@@ -0,0 +1 @@
+"""Test-side wakeword runtime package."""
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/bridge/__init__.py b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/bridge/__init__.py
new file mode 100644
index 0000000..d4b2cfc
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/bridge/__init__.py
@@ -0,0 +1 @@
+from .event_bridge import WakewordEventBridge
diff --git a/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/bridge/event_bridge.py b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/bridge/event_bridge.py
new file mode 100644
index 0000000..109efa9
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/bridge/event_bridge.py
@@ -0,0 +1,106 @@
+import json
+import logging
+import queue
+import threading
+import time
+from typing import Any
+
+logger = logging.getLogger(__name__)
+
+
+class WakewordEventBridge:
+ def __init__(self) -> None:
+ self._clients: list[queue.Queue[str]] = []
+ self._clients_lock = threading.Lock()
+ self._running = True
+
+ @property
+ def is_running(self) -> bool:
+ return self._running
+
+ def build_ready_message(self) -> str:
+ return self.build_message(
+ "bridge_connected",
+ {"status": "ready"},
+ )
+
+ def publish_detected(self, wake_word: str) -> None:
+ self.publish(
+ "wake_word_detected",
+ {
+ "wake_word": wake_word,
+ "timestamp": time.time(),
+ },
+ )
+
+ def publish(self, event_type: str, payload: dict[str, Any] | None = None) -> None:
+ if not self._running:
+ return
+
+ message = self.build_message(event_type, payload or {})
+ with self._clients_lock:
+ clients = list(self._clients)
+
+ stale_clients: list[queue.Queue[str]] = []
+ for client_queue in clients:
+ try:
+ client_queue.put_nowait(message)
+ except queue.Full:
+ stale_clients.append(client_queue)
+
+ if stale_clients:
+ with self._clients_lock:
+ for client_queue in stale_clients:
+ if client_queue in self._clients:
+ self._clients.remove(client_queue)
+
+ def add_client(self) -> queue.Queue[str]:
+ client_queue: queue.Queue[str] = queue.Queue(maxsize=16)
+ with self._clients_lock:
+ self._clients.append(client_queue)
+ return client_queue
+
+ def build_message(
+ self,
+ event_type: str,
+ payload: dict[str, Any] | None = None,
+ request_id: str | None = None,
+ success: bool = True,
+ error: str | None = None,
+ ) -> str:
+ message: dict[str, Any] = {
+ "type": event_type,
+ "requestId": request_id,
+ "success": success,
+ "payload": payload or {},
+ }
+ if error:
+ message["error"] = error
+ return json.dumps(message, ensure_ascii=False)
+
+ def remove_client(self, client_queue: queue.Queue[str]) -> None:
+ with self._clients_lock:
+ if client_queue in self._clients:
+ self._clients.remove(client_queue)
+
+ def publish_service_ready(self) -> None:
+ self.publish("service_ready", {"status": "ready"})
+
+ def publish_service_stopping(self) -> None:
+ self.publish("service_stopping", {"status": "stopping"})
+
+ def close(self) -> None:
+ if not self._running:
+ return
+
+ self.publish_service_stopping()
+ self._running = False
+ with self._clients_lock:
+ clients = list(self._clients)
+ self._clients.clear()
+
+ for client_queue in clients:
+ try:
+ client_queue.put_nowait("__bridge_closed__")
+ except queue.Full:
+ pass
diff --git a/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/config.json b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/config.json
new file mode 100644
index 0000000..dabcb4a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/config.json
@@ -0,0 +1,25 @@
+{
+ "wakeword": {
+ "enabled": true
+ },
+ "model_dir": "models",
+ "audio": {
+ "input_device": null,
+ "sample_rate": 16000,
+ "channels": 1
+ },
+ "detector": {
+ "num_threads": 4,
+ "provider": "cpu",
+ "max_active_paths": 2,
+ "keywords_score": 1.8,
+ "keywords_threshold": 0.1,
+ "num_trailing_blanks": 1,
+ "cooldown_seconds": 1.5
+ },
+ "logging": {
+ "level": "INFO",
+ "dir": "logs",
+ "file": "wakeword-runtime.log"
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/config/__init__.py b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/config/__init__.py
new file mode 100644
index 0000000..52fbd4a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/config/__init__.py
@@ -0,0 +1,2 @@
+from .config_loader import RuntimeConfig, load_config
+from .logging_setup import setup_logging
diff --git a/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/config/config_loader.py b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/config/config_loader.py
new file mode 100644
index 0000000..bfab853
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/config/config_loader.py
@@ -0,0 +1,153 @@
+import json
+from dataclasses import dataclass
+from pathlib import Path
+
+
+@dataclass
+class WakewordSettings:
+ enabled: bool = False
+
+
+@dataclass
+class AudioSettings:
+ input_device: str | int | None = None
+ sample_rate: int = 16000
+ channels: int = 1
+
+
+@dataclass
+class DetectorSettings:
+ num_threads: int = 4
+ provider: str = "cpu"
+ max_active_paths: int = 2
+ keywords_score: float = 1.8
+ keywords_threshold: float = 0.2
+ num_trailing_blanks: int = 1
+ cooldown_seconds: float = 1.5
+
+
+@dataclass
+class LoggingSettings:
+ level: str = "INFO"
+ directory: str = "logs"
+ file_name: str = "wakeword-runtime.log"
+
+
+@dataclass
+class RuntimeConfig:
+ runtime_root: Path
+ wakeword: WakewordSettings
+ wake_words: list[str]
+ model_dir: Path
+ audio: AudioSettings
+ detector: DetectorSettings
+ logging: LoggingSettings
+
+ def validate(self) -> None:
+ if self.wakeword.enabled and not self.wake_words:
+ raise ValueError("keywords.txt cannot be empty when wakeword is enabled")
+
+ if self.audio.sample_rate <= 0:
+ raise ValueError("audio.sample_rate must be greater than 0")
+
+ if self.audio.channels <= 0:
+ raise ValueError("audio.channels must be greater than 0")
+
+ if self.detector.num_threads <= 0:
+ raise ValueError("detector.num_threads must be greater than 0")
+
+ if self.detector.cooldown_seconds < 0:
+ raise ValueError("detector.cooldown_seconds cannot be negative")
+
+ if not self.logging.level:
+ raise ValueError("logging.level cannot be empty")
+
+ @property
+ def wakeword_enabled(self) -> bool:
+ return self.wakeword.enabled
+
+ @property
+ def log_dir(self) -> Path:
+ return (self.runtime_root / self.logging.directory).resolve()
+
+ @property
+ def log_file(self) -> Path:
+ return self.log_dir / self.logging.file_name
+
+ @property
+ def log_level(self) -> str:
+ return self.logging.level.upper()
+
+
+def load_config(runtime_root: Path) -> RuntimeConfig:
+ config_path = runtime_root / "config.json"
+ raw = json.loads(config_path.read_text(encoding="utf-8"))
+ wakeword_cfg = dict(raw.get("wakeword", {}))
+ raw_model_dir = Path(str(raw.get("model_dir", "models")))
+ model_dir = raw_model_dir.resolve() if raw_model_dir.is_absolute() else (runtime_root / raw_model_dir).resolve()
+ wake_words = _load_wake_words_from_keywords_file(model_dir)
+
+ audio_cfg = dict(raw.get("audio", {}))
+ detector_cfg = dict(raw.get("detector", {}))
+ logging_cfg = dict(raw.get("logging", {}))
+
+ config = RuntimeConfig(
+ runtime_root=runtime_root,
+ wakeword=WakewordSettings(enabled=bool(wakeword_cfg.get("enabled", False))),
+ wake_words=wake_words,
+ model_dir=model_dir,
+ audio=AudioSettings(
+ input_device=audio_cfg.get("input_device"),
+ sample_rate=int(audio_cfg.get("sample_rate", 16000)),
+ channels=int(audio_cfg.get("channels", 1)),
+ ),
+ detector=DetectorSettings(
+ num_threads=int(detector_cfg.get("num_threads", 4)),
+ provider=str(detector_cfg.get("provider", "cpu")),
+ max_active_paths=int(detector_cfg.get("max_active_paths", 2)),
+ keywords_score=float(detector_cfg.get("keywords_score", 1.8)),
+ keywords_threshold=float(detector_cfg.get("keywords_threshold", 0.2)),
+ num_trailing_blanks=int(detector_cfg.get("num_trailing_blanks", 1)),
+ cooldown_seconds=float(detector_cfg.get("cooldown_seconds", 1.5)),
+ ),
+ logging=LoggingSettings(
+ level=str(logging_cfg.get("level", "INFO")).upper(),
+ directory=str(logging_cfg.get("dir", "logs")),
+ file_name=str(logging_cfg.get("file", "wakeword-runtime.log")),
+ ),
+ )
+ config.validate()
+ return config
+
+
+DEFAULT_WAKE_WORDS = [
+ "浣犲ソ灏忔櫤",
+ "浣犲ソ灏忓織",
+ "灏忕埍鍚屽",
+ "浣犲ソ灏忛懌",
+ "浣犲ソ灏忔柊",
+ "灏忕編鍚屽",
+ "灏忛緳灏忛緳",
+ "鍠靛柕鍚屽",
+ "灏忔花灏忔花",
+ "灏忓啺灏忓啺",
+ "鍢夸綘濂藉憖",
+]
+
+
+def _load_wake_words_from_keywords_file(model_dir: Path) -> list[str]:
+ keywords_file = model_dir / "keywords.txt"
+ if not keywords_file.exists():
+ return DEFAULT_WAKE_WORDS.copy()
+
+ wake_words: list[str] = []
+ for line in keywords_file.read_text(encoding="utf-8").splitlines():
+ text = line.strip()
+ if not text or text.startswith("#") or "@" not in text:
+ continue
+
+ wake_word = text.split("@", 1)[1].strip()
+ if wake_word:
+ wake_words.append(wake_word)
+
+ return wake_words if wake_words else DEFAULT_WAKE_WORDS.copy()
diff --git a/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/config/logging_setup.py b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/config/logging_setup.py
new file mode 100644
index 0000000..4646b3b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/config/logging_setup.py
@@ -0,0 +1,23 @@
+import logging
+from pathlib import Path
+
+
+def setup_logging(log_file: Path, level: str = "INFO") -> None:
+ log_file.parent.mkdir(parents=True, exist_ok=True)
+
+ logger = logging.getLogger()
+ logger.setLevel(getattr(logging, level.upper(), logging.INFO))
+ logger.handlers.clear()
+
+ formatter = logging.Formatter(
+ "%(asctime)s [%(levelname)s] %(name)s - %(message)s"
+ )
+
+ stream_handler = logging.StreamHandler()
+ stream_handler.setFormatter(formatter)
+
+ file_handler = logging.FileHandler(log_file, encoding="utf-8")
+ file_handler.setFormatter(formatter)
+
+ logger.addHandler(stream_handler)
+ logger.addHandler(file_handler)
diff --git a/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/core/__init__.py b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/core/__init__.py
new file mode 100644
index 0000000..ed1b901
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/core/__init__.py
@@ -0,0 +1,3 @@
+from .detector_assets import DetectorAssets, DetectorAssetsBuilder
+from .detector import WakewordDetector
+from .microphone import MicrophoneListener
diff --git a/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/core/detector.py b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/core/detector.py
new file mode 100644
index 0000000..6ec50b3
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/core/detector.py
@@ -0,0 +1,191 @@
+import queue
+import threading
+import logging
+import time
+from typing import Any, Callable
+
+import numpy as np
+
+from ..config import RuntimeConfig
+from .detector_assets import DetectorAssetsBuilder
+
+logger = logging.getLogger(__name__)
+
+
+class WakewordDetector:
+ def __init__(self, config: RuntimeConfig) -> None:
+ self.config = config
+ self.enabled = config.wakeword_enabled
+ self.assets_builder = DetectorAssetsBuilder(config)
+ self.audio_source = None
+ self.keyword_spotter: Any | None = None
+ self.stream: Any | None = None
+ self.is_running_flag = False
+ self.paused = False
+ self.on_detected_callback: Callable[[str, str], None] | None = None
+ self.on_error: Callable[[Exception], None] | None = None
+ self._audio_queue: queue.Queue[np.ndarray] = queue.Queue(maxsize=100)
+ self._worker_thread: threading.Thread | None = None
+ self.last_detection_time = 0.0
+ self.detection_cooldown = self.config.detector.cooldown_seconds
+
+ def initialize(self) -> None:
+ if not self.enabled:
+ raise RuntimeError("wakeword detector is disabled")
+
+ try:
+ import sherpa_onnx
+ except ImportError as exc:
+ raise RuntimeError(
+ "Missing dependency: sherpa-onnx. Install runtime dependencies before initializing detector."
+ ) from exc
+
+ assets = self.assets_builder.prepare()
+ detector_cfg = self.config.detector
+
+ self.keyword_spotter = sherpa_onnx.KeywordSpotter(
+ tokens=str(assets.tokens_file),
+ encoder=str(assets.encoder_file),
+ decoder=str(assets.decoder_file),
+ joiner=str(assets.joiner_file),
+ keywords_file=str(assets.keywords_file),
+ num_threads=detector_cfg.num_threads,
+ sample_rate=self.config.audio.sample_rate,
+ feature_dim=80,
+ max_active_paths=detector_cfg.max_active_paths,
+ keywords_score=detector_cfg.keywords_score,
+ keywords_threshold=detector_cfg.keywords_threshold,
+ num_trailing_blanks=detector_cfg.num_trailing_blanks,
+ provider=detector_cfg.provider,
+ )
+ self.stream = self.keyword_spotter.create_stream()
+ logger.info("detector initialized")
+ logger.info("detector model root: %s", assets.model_root)
+ logger.info("detector keywords file: %s", assets.keywords_file)
+
+ def on_detected(self, callback: Callable[[str, str], None]) -> None:
+ self.on_detected_callback = callback
+
+ def on_audio_data(self, audio_data: np.ndarray) -> None:
+ if not self.enabled or not self.is_running_flag or self.paused:
+ return
+
+ try:
+ self._audio_queue.put_nowait(audio_data.copy())
+ except queue.Full:
+ try:
+ self._audio_queue.get_nowait()
+ self._audio_queue.put_nowait(audio_data.copy())
+ except queue.Empty:
+ pass
+ except Exception as exc:
+ logger.debug("audio data enqueue failed: %s", exc)
+
+ def start(self, audio_source) -> None:
+ if not self.enabled:
+ logger.info("wakeword detector disabled")
+ return
+
+ if self.keyword_spotter is None or self.stream is None:
+ self.initialize()
+
+ if self.is_running_flag:
+ return
+
+ self.audio_source = audio_source
+ self.audio_source.add_audio_listener(self)
+ self.is_running_flag = True
+ self.paused = False
+ self._worker_thread = threading.Thread(
+ target=self._detection_loop,
+ name="wakeword-detector",
+ daemon=True,
+ )
+ self._worker_thread.start()
+ logger.info("wakeword detector started")
+
+ def stop(self) -> None:
+ if not self.is_running_flag and self.audio_source is None and self._worker_thread is None:
+ return
+
+ self.is_running_flag = False
+
+ if self.audio_source is not None:
+ self.audio_source.remove_audio_listener(self)
+ self.audio_source = None
+
+ if self._worker_thread is not None:
+ self._worker_thread.join(timeout=1.0)
+ self._worker_thread = None
+
+ while not self._audio_queue.empty():
+ try:
+ self._audio_queue.get_nowait()
+ except queue.Empty:
+ break
+
+ logger.info("wakeword detector stopped")
+
+ def _detection_loop(self) -> None:
+ error_count = 0
+ max_errors = 5
+
+ while self.is_running_flag:
+ try:
+ if self.paused:
+ time.sleep(0.1)
+ continue
+
+ audio_data = self._audio_queue.get(timeout=0.1)
+ result = self.process_audio_chunk(audio_data)
+ if result and self.on_detected_callback is not None:
+ self.on_detected_callback(result, result)
+ error_count = 0
+ except queue.Empty:
+ continue
+ except Exception as exc:
+ error_count += 1
+ logger.error("wakeword detection loop error(%s/%s): %s", error_count, max_errors, exc)
+ if self.on_error is not None:
+ try:
+ self.on_error(exc)
+ except Exception:
+ logger.exception("wakeword error callback failed")
+ if error_count >= max_errors:
+ logger.critical("too many wakeword detection errors, stopping detector")
+ break
+ time.sleep(1)
+
+ self.is_running_flag = False
+
+ def process_audio_chunk(self, audio_data: np.ndarray) -> str | None:
+ if self.keyword_spotter is None or self.stream is None:
+ raise RuntimeError("detector is not initialized")
+
+ if audio_data is None or len(audio_data) == 0:
+ return None
+
+ if audio_data.dtype == np.int16:
+ samples = audio_data.astype(np.float32) / 32768.0
+ else:
+ samples = audio_data.astype(np.float32)
+
+ sample_rate = self.config.audio.sample_rate
+ self.stream.accept_waveform(sample_rate=sample_rate, waveform=samples)
+
+ if not self.keyword_spotter.is_ready(self.stream):
+ return None
+
+ self.keyword_spotter.decode_stream(self.stream)
+ result = self.keyword_spotter.get_result(self.stream)
+ if not result:
+ return None
+
+ self.keyword_spotter.reset_stream(self.stream)
+
+ current_time = time.time()
+ if current_time - self.last_detection_time < self.detection_cooldown:
+ return None
+
+ self.last_detection_time = current_time
+ return str(result)
diff --git a/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/core/detector_assets.py b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/core/detector_assets.py
new file mode 100644
index 0000000..d2fbfda
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/core/detector_assets.py
@@ -0,0 +1,94 @@
+from dataclasses import dataclass
+from pathlib import Path
+
+from ..config import RuntimeConfig
+
+REQUIRED_MODEL_FILES = (
+ "encoder.onnx",
+ "decoder.onnx",
+ "joiner.onnx",
+ "tokens.txt",
+)
+
+
+@dataclass
+class DetectorAssets:
+ model_root: Path
+ tokens_file: Path
+ encoder_file: Path
+ decoder_file: Path
+ joiner_file: Path
+ keywords_file: Path
+
+
+class DetectorAssetsBuilder:
+ def __init__(self, config: RuntimeConfig) -> None:
+ self.config = config
+
+ def prepare(self) -> DetectorAssets:
+ model_root = self._resolve_model_root()
+ keywords_file = self._write_keywords_file(model_root)
+ return DetectorAssets(
+ model_root=model_root,
+ tokens_file=model_root / "tokens.txt",
+ encoder_file=model_root / "encoder.onnx",
+ decoder_file=model_root / "decoder.onnx",
+ joiner_file=model_root / "joiner.onnx",
+ keywords_file=keywords_file,
+ )
+
+ def _resolve_model_root(self) -> Path:
+ preferred = self.config.model_dir
+ if self._has_required_files(preferred):
+ return preferred
+
+ raise FileNotFoundError(
+ "No valid model directory found. Expected configured model files to exist."
+ )
+
+ def _write_keywords_file(self, model_root: Path) -> Path:
+ try:
+ from pypinyin import Style, pinyin
+ except ImportError as exc:
+ raise RuntimeError(
+ "Missing dependency: pypinyin. Install runtime dependencies before generating keywords."
+ ) from exc
+
+ wake_words = self.config.wake_words
+ if not wake_words:
+ raise ValueError("keywords.txt cannot be empty")
+
+ keywords_path = model_root / "keywords.txt"
+ lines: list[str] = []
+ for keyword_text in wake_words:
+ initials = pinyin(keyword_text, style=Style.INITIALS, strict=False)
+ finals = pinyin(
+ keyword_text,
+ style=Style.FINALS_TONE,
+ strict=False,
+ neutral_tone_with_five=True,
+ )
+
+ tokens: list[str] = []
+ for initial_parts, final_parts in zip(initials, finals):
+ initial = initial_parts[0].strip()
+ final = final_parts[0].strip()
+ if initial:
+ tokens.append(initial)
+ if final:
+ tokens.append(final)
+
+ if not tokens:
+ raise ValueError(
+ f"failed to generate pinyin tokens for wake word: {keyword_text}"
+ )
+
+ lines.append(f"{' '.join(tokens)} @{keyword_text}")
+
+ keywords_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
+ return keywords_path
+
+ def _has_required_files(self, directory: Path) -> bool:
+ if not directory.exists():
+ return False
+ return all((directory / file_name).exists() for file_name in REQUIRED_MODEL_FILES)
diff --git a/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/core/microphone.py b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/core/microphone.py
new file mode 100644
index 0000000..b6bbbbc
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/core/microphone.py
@@ -0,0 +1,91 @@
+import logging
+from typing import Protocol
+
+import numpy as np
+
+from ..config import RuntimeConfig
+
+logger = logging.getLogger(__name__)
+
+
+class AudioListener(Protocol):
+ def on_audio_data(self, audio_data: np.ndarray) -> None:
+ ...
+
+
+class MicrophoneListener:
+
+ def __init__(self, config: RuntimeConfig) -> None:
+ self.config = config
+ self._stream = None
+ self._running = False
+ self._listeners: list[AudioListener] = []
+ self._sample_rate = self.config.audio.sample_rate
+ self._channels = self.config.audio.channels
+ self._device = self.config.audio.input_device
+ self._block_duration_ms = 30
+ self._block_size = int(self._sample_rate * (self._block_duration_ms / 1000))
+
+ def add_audio_listener(self, listener: AudioListener) -> None:
+ if listener not in self._listeners:
+ self._listeners.append(listener)
+
+ def remove_audio_listener(self, listener: AudioListener) -> None:
+ if listener in self._listeners:
+ self._listeners.remove(listener)
+
+ def start(self) -> None:
+ try:
+ import sounddevice as sd
+ except ImportError as exc:
+ raise RuntimeError(
+ "Missing dependency: sounddevice. Install runtime dependencies before starting microphone listener."
+ ) from exc
+
+ self._stream = sd.InputStream(
+ device=self._device,
+ samplerate=self._sample_rate,
+ channels=self._channels,
+ dtype="int16",
+ blocksize=self._block_size,
+ callback=self._input_callback,
+ latency="low",
+ )
+ self._stream.start()
+ self._running = True
+
+ logger.info("microphone listener started")
+ logger.info("microphone sample rate: %s", self._sample_rate)
+ logger.info("microphone channels: %s", self._channels)
+ logger.info("microphone device: %s", self._device if self._device is not None else "default")
+ logger.info("microphone block size: %s", self._block_size)
+
+ def stop(self) -> None:
+ if not self._running and self._stream is None:
+ return
+
+ self._running = False
+ if self._stream is not None:
+ try:
+ self._stream.stop()
+ finally:
+ self._stream.close()
+ self._stream = None
+ logger.info("microphone listener stopped")
+
+ def _input_callback(self, indata, frames, time_info, status) -> None:
+ _ = frames, time_info
+ if status and "overflow" not in str(status).lower():
+ logger.warning("microphone status: %s", status)
+
+ audio = np.copy(indata)
+ if audio.ndim > 1:
+ audio = audio[:, 0]
+ else:
+ audio = audio.reshape(-1)
+
+ for listener in list(self._listeners):
+ try:
+ listener.on_audio_data(audio)
+ except Exception:
+ logger.exception("audio listener callback failed")
diff --git a/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/plugins/__init__.py b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/plugins/__init__.py
new file mode 100644
index 0000000..9d253d7
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/plugins/__init__.py
@@ -0,0 +1,4 @@
+from .audio import AudioPlugin
+from .base import Plugin
+from .manager import PluginManager
+from .wake_word import WakeWordPlugin
diff --git a/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/plugins/audio.py b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/plugins/audio.py
new file mode 100644
index 0000000..484cfc5
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/plugins/audio.py
@@ -0,0 +1,37 @@
+import logging
+from typing import Any
+
+from ..core import MicrophoneListener
+from .base import Plugin
+
+logger = logging.getLogger(__name__)
+
+
+class AudioPlugin(Plugin):
+ name = "audio"
+ priority = 10
+
+ def __init__(self) -> None:
+ self.app = None
+ self.source: MicrophoneListener | None = None
+
+ def setup(self, app: Any) -> None:
+ self.app = app
+ self.source = MicrophoneListener(app.config)
+ self.app.audio_source = self.source
+
+ def start(self) -> None:
+ if self.source is None:
+ logger.warning("audio source not initialized")
+ return
+ self.source.start()
+
+ def stop(self) -> None:
+ if self.source is not None:
+ self.source.stop()
+
+ def shutdown(self) -> None:
+ if self.app is not None:
+ self.app.audio_source = None
+ self.source = None
+ self.app = None
diff --git a/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/plugins/base.py b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/plugins/base.py
new file mode 100644
index 0000000..3ab0c5b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/plugins/base.py
@@ -0,0 +1,18 @@
+from typing import Any
+
+
+class Plugin:
+ name: str = "plugin"
+ priority: int = 50
+
+ def setup(self, app: Any) -> None:
+ return None
+
+ def start(self) -> None:
+ return None
+
+ def stop(self) -> None:
+ return None
+
+ def shutdown(self) -> None:
+ return None
diff --git a/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/plugins/manager.py b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/plugins/manager.py
new file mode 100644
index 0000000..43493f2
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/plugins/manager.py
@@ -0,0 +1,37 @@
+from typing import Any
+
+from .base import Plugin
+
+
+class PluginManager:
+ def __init__(self) -> None:
+ self._plugins: list[Plugin] = []
+ self._by_name: dict[str, Plugin] = {}
+
+ def register(self, *plugins: Plugin) -> None:
+ for plugin in sorted(plugins, key=lambda item: getattr(item, "priority", 50)):
+ if plugin in self._plugins:
+ continue
+ self._plugins.append(plugin)
+ name = getattr(plugin, "name", "")
+ if isinstance(name, str) and name:
+ self._by_name[name] = plugin
+
+ def get_plugin(self, name: str) -> Plugin | None:
+ return self._by_name.get(name)
+
+ def setup_all(self, app: Any) -> None:
+ for plugin in list(self._plugins):
+ plugin.setup(app)
+
+ def start_all(self) -> None:
+ for plugin in list(self._plugins):
+ plugin.start()
+
+ def stop_all(self) -> None:
+ for plugin in reversed(self._plugins):
+ plugin.stop()
+
+ def shutdown_all(self) -> None:
+ for plugin in reversed(self._plugins):
+ plugin.shutdown()
diff --git a/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/plugins/wake_word.py b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/plugins/wake_word.py
new file mode 100644
index 0000000..51c2deb
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/plugins/wake_word.py
@@ -0,0 +1,53 @@
+import logging
+from typing import Any
+
+from ..core import WakewordDetector
+from .base import Plugin
+
+logger = logging.getLogger(__name__)
+
+
+class WakeWordPlugin(Plugin):
+ name = "wake_word"
+ priority = 30
+
+ def __init__(self) -> None:
+ self.app = None
+ self.detector: WakewordDetector | None = None
+
+ def setup(self, app: Any) -> None:
+ self.app = app
+ self.detector = WakewordDetector(app.config)
+ if not self.detector.enabled:
+ self.detector = None
+ return
+ self.detector.on_detected(self._on_detected)
+ self.detector.on_error = self._on_error
+
+ def start(self) -> None:
+ if self.detector is None:
+ return
+
+ audio_plugin = self.app.plugins.get_plugin("audio") if self.app else None
+ audio_source = getattr(audio_plugin, "source", None)
+ if audio_source is None:
+ logger.warning("audio source unavailable, wakeword plugin not started")
+ return
+
+ self.detector.start(audio_source)
+
+ def stop(self) -> None:
+ if self.detector is not None:
+ self.detector.stop()
+
+ def shutdown(self) -> None:
+ self.detector = None
+ self.app = None
+
+ def _on_detected(self, wake_word: str, full_text: str) -> None:
+ if self.app is None:
+ return
+ self.app.handle_wake_word_detected(wake_word, full_text)
+
+ def _on_error(self, error: Exception) -> None:
+ logger.error("wakeword detection error: %s", error)
diff --git a/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/requirements.txt b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/requirements.txt
new file mode 100644
index 0000000..4ff9498
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/requirements.txt
@@ -0,0 +1,3 @@
+sherpa-onnx==1.12.29
+sounddevice>=0.4.4
+pypinyin==0.55.0
diff --git a/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/runtime/__init__.py b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/runtime/__init__.py
new file mode 100644
index 0000000..0629fcd
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/runtime/__init__.py
@@ -0,0 +1,2 @@
+from .app import TestRuntimeApplication
+from .http_server import TestRuntimeHttpServer
diff --git a/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/runtime/app.py b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/runtime/app.py
new file mode 100644
index 0000000..f40b2d5
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/runtime/app.py
@@ -0,0 +1,70 @@
+import logging
+from typing import Protocol
+
+from ..config import RuntimeConfig
+from ..plugins import AudioPlugin, PluginManager, WakeWordPlugin
+
+logger = logging.getLogger(__name__)
+
+
+class EventPublisher(Protocol):
+ def publish_service_ready(self) -> None:
+ ...
+
+ def publish_service_stopping(self) -> None:
+ ...
+
+ def publish_detected(self, wake_word: str) -> None:
+ ...
+
+
+class TestRuntimeApplication:
+ def __init__(self, config: RuntimeConfig, event_publisher: EventPublisher) -> None:
+ self.config = config
+ self.event_bridge = event_publisher
+ self.plugins = PluginManager()
+ self.audio_source = None
+ self._is_setup = False
+ self._is_running = False
+
+ def setup(self) -> None:
+ if self._is_setup:
+ return
+
+ if self.config.wakeword_enabled:
+ self.plugins.register(
+ AudioPlugin(),
+ WakeWordPlugin(),
+ )
+ self.plugins.setup_all(self)
+ self._is_setup = True
+
+ def start(self) -> None:
+ if self._is_running:
+ return
+
+ self.setup()
+ self.plugins.start_all()
+ if self.config.wakeword_enabled:
+ self.event_bridge.publish_service_ready()
+ self._is_running = True
+ logger.info("test runtime application started")
+
+ def stop(self) -> None:
+ if not self._is_running:
+ return
+
+ self.event_bridge.publish_service_stopping()
+ self.plugins.stop_all()
+ self._is_running = False
+ logger.info("test runtime application stopped")
+
+ def shutdown(self) -> None:
+ self.stop()
+ if self._is_setup:
+ self.plugins.shutdown_all()
+
+ def handle_wake_word_detected(self, wake_word: str, full_text: str) -> None:
+ _ = full_text
+ logger.info("wake word detected: %s", wake_word)
+ self.event_bridge.publish_detected(wake_word)
diff --git a/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/runtime/http_server.py b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/runtime/http_server.py
new file mode 100644
index 0000000..19ce123
--- /dev/null
+++ b/xiaozhi-esp32-server/main/digital-human/wakeword_runtime/runtime/http_server.py
@@ -0,0 +1,352 @@
+import base64
+import hashlib
+import json
+import queue
+import socket
+import threading
+from http import HTTPStatus
+from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
+from pathlib import Path
+from typing import Callable
+
+from ..bridge import WakewordEventBridge
+from ..config.config_loader import load_config
+
+
+class TestRuntimeHttpServer:
+ def __init__(self, test_root: Path, host: str = "0.0.0.0", port: int = 8006) -> None:
+ self.test_root = test_root
+ self.host = host
+ self.port = port
+ self.event_bridge = WakewordEventBridge()
+ self._restart_handler: Callable[[], None] | None = None
+ self._restart_lock = threading.Lock()
+ self._server = self._build_server()
+
+ @property
+ def page_url(self) -> str:
+ return f"http://127.0.0.1:{self.port}/index.html"
+
+ @property
+ def bridge_url(self) -> str:
+ return f"ws://127.0.0.1:{self.port}/wakeword-ws"
+
+ def serve_forever(self) -> None:
+ self._server.serve_forever()
+
+ def shutdown(self) -> None:
+ self._server.shutdown()
+ self.event_bridge.close()
+ self._server.server_close()
+
+ def set_restart_handler(self, handler: Callable[[], None]) -> None:
+ self._restart_handler = handler
+
+ def request_runtime_restart(self) -> None:
+ with self._restart_lock:
+ handler = self._restart_handler
+
+ if handler is None:
+ raise RuntimeError("restart handler is not configured")
+
+ threading.Thread(
+ target=self._run_restart_handler,
+ name="test-runtime-restart",
+ daemon=True,
+ ).start()
+
+ def _run_restart_handler(self) -> None:
+ handler = self._restart_handler
+ if handler is None:
+ return
+ handler()
+
+ def _build_server(self) -> ThreadingHTTPServer:
+ test_root = self.test_root
+ event_bridge = self.event_bridge
+ schedule_restart = self.request_runtime_restart
+
+ class TestRuntimeHandler(SimpleHTTPRequestHandler):
+ protocol_version = "HTTP/1.1"
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, directory=str(test_root), **kwargs)
+
+ def handle(self) -> None:
+ try:
+ super().handle()
+ except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError):
+ pass
+
+ def do_GET(self) -> None:
+ if self.path == "/wakeword-ws":
+ self._handle_websocket(event_bridge)
+ return
+
+ if self.path == "/health":
+ body = json.dumps({"status": "ok"}).encode("utf-8")
+ self.send_response(HTTPStatus.OK)
+ self.send_header("Content-Type", "application/json; charset=utf-8")
+ self.send_header("Cache-Control", "no-cache")
+ self.send_header("Content-Length", str(len(body)))
+ self.end_headers()
+ self.wfile.write(body)
+ return
+
+ super().do_GET()
+
+ def log_message(self, format: str, *args) -> None:
+ return
+
+ def _handle_websocket(self, bridge: WakewordEventBridge) -> None:
+ if self.headers.get("Upgrade", "").lower() != "websocket":
+ self.send_error(HTTPStatus.BAD_REQUEST, "expected websocket upgrade")
+ return
+
+ websocket_key = self.headers.get("Sec-WebSocket-Key")
+ if not websocket_key:
+ self.send_error(HTTPStatus.BAD_REQUEST, "missing Sec-WebSocket-Key")
+ return
+
+ accept_source = websocket_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+ accept_value = base64.b64encode(
+ hashlib.sha1(accept_source.encode("utf-8")).digest()
+ ).decode("ascii")
+
+ client_queue = bridge.add_client()
+ self.send_response(HTTPStatus.SWITCHING_PROTOCOLS)
+ self.send_header("Upgrade", "websocket")
+ self.send_header("Connection", "Upgrade")
+ self.send_header("Sec-WebSocket-Accept", accept_value)
+ self.end_headers()
+
+ try:
+ self.connection.settimeout(0.2)
+ self._send_websocket_text(bridge.build_ready_message())
+ self._send_websocket_text(self._build_wakeword_config_message(bridge))
+
+ while bridge.is_running:
+ inbound_message = self._receive_websocket_message()
+ if inbound_message is not None:
+ response_message = self._handle_bridge_request(bridge, inbound_message)
+ if response_message:
+ self._send_websocket_text(response_message)
+
+ try:
+ message = client_queue.get(timeout=0.2)
+ if message == "__bridge_closed__":
+ break
+ self._send_websocket_text(message)
+ except queue.Empty:
+ if not bridge.is_running:
+ break
+ continue
+ except socket.timeout:
+ pass
+ except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError):
+ pass
+ finally:
+ bridge.remove_client(client_queue)
+
+ def _build_wakeword_config_message(self, bridge: WakewordEventBridge) -> str:
+ try:
+ runtime_root = test_root / "wakeword_runtime"
+ config = load_config(runtime_root)
+ payload = {
+ "enabled": config.wakeword_enabled,
+ "wakeWords": config.wake_words,
+ }
+ return bridge.build_message("wakeword_config", payload)
+ except Exception as exc:
+ return bridge.build_message(
+ "wakeword_config",
+ {},
+ success=False,
+ error=f"璇诲彇鍞ら啋璇嶉厤缃け璐�: {exc}",
+ )
+
+ def _handle_bridge_request(self, bridge: WakewordEventBridge, raw_message: str) -> str | None:
+ try:
+ message = json.loads(raw_message)
+ except json.JSONDecodeError:
+ return None
+
+ message_type = str(message.get("type", "")).strip()
+ request_id = message.get("requestId")
+ payload = message.get("payload") or {}
+ result_type = f"{message_type}_result" if message_type else "bridge_request_result"
+
+ if message_type == "set_wakeword_config":
+ try:
+ result_payload = self._save_wakeword_config(payload)
+ bridge.publish("wakeword_config", result_payload)
+ return bridge.build_message(
+ "set_wakeword_config_result",
+ result_payload,
+ request_id=request_id,
+ )
+ except Exception as exc:
+ return bridge.build_message(
+ "set_wakeword_config_result",
+ {},
+ request_id=request_id,
+ success=False,
+ error=f"淇濆瓨鍞ら啋璇嶉厤缃け璐�: {exc}",
+ )
+
+ if message_type == "restart_wakeword_service":
+ schedule_restart()
+ return bridge.build_message(
+ "restart_wakeword_service_result",
+ {"restarting": True},
+ request_id=request_id,
+ )
+
+ return bridge.build_message(
+ result_type,
+ {},
+ request_id=request_id,
+ success=False,
+ error=f"unsupported message type: {message_type}",
+ )
+
+ def _save_wakeword_config(self, payload: dict) -> dict:
+ runtime_root = test_root / "wakeword_runtime"
+ config_path = runtime_root / "config.json"
+ model_root = runtime_root / "models"
+ keywords_path = model_root / "keywords.txt"
+
+ enabled = bool(payload.get("enabled", True))
+ wake_words = payload.get("wakeWords") or []
+ normalized_wake_words = []
+ for item in wake_words:
+ if not isinstance(item, str):
+ continue
+ text = item.strip()
+ if text and text not in normalized_wake_words:
+ normalized_wake_words.append(text)
+
+ if enabled and not normalized_wake_words:
+ raise ValueError("wakeWords cannot be empty when wakeword is enabled")
+
+ raw_config = json.loads(config_path.read_text(encoding="utf-8"))
+ raw_config.setdefault("wakeword", {})["enabled"] = enabled
+ config_path.write_text(
+ json.dumps(raw_config, indent=2, ensure_ascii=False),
+ encoding="utf-8",
+ )
+
+ keywords_lines = [self._build_keyword_line(item) for item in normalized_wake_words]
+ keywords_path.write_text(
+ ("\n".join(keywords_lines) + "\n") if keywords_lines else "",
+ encoding="utf-8",
+ )
+
+ return {
+ "enabled": enabled,
+ "wakeWords": normalized_wake_words,
+ }
+
+ def _build_keyword_line(self, keyword_text: str) -> str:
+ from pypinyin import Style, pinyin
+
+ initials = pinyin(keyword_text, style=Style.INITIALS, strict=False)
+ finals = pinyin(
+ keyword_text,
+ style=Style.FINALS_TONE,
+ strict=False,
+ neutral_tone_with_five=True,
+ )
+
+ tokens: list[str] = []
+ for initial_parts, final_parts in zip(initials, finals):
+ initial = initial_parts[0].strip()
+ final = final_parts[0].strip()
+ if initial:
+ tokens.append(initial)
+ if final:
+ tokens.append(final)
+
+ if not tokens:
+ raise ValueError(f"failed to generate pinyin tokens for wake word: {keyword_text}")
+
+ return f"{' '.join(tokens)} @{keyword_text}"
+
+ def _receive_websocket_message(self) -> str | None:
+ try:
+ header = self._read_exact(2)
+ except socket.timeout:
+ return None
+
+ if not header:
+ return None
+
+ first_byte, second_byte = header[0], header[1]
+ opcode = first_byte & 0x0F
+ masked = (second_byte & 0x80) != 0
+ payload_length = second_byte & 0x7F
+
+ if payload_length == 126:
+ payload_length = int.from_bytes(self._read_exact(2), "big")
+ elif payload_length == 127:
+ payload_length = int.from_bytes(self._read_exact(8), "big")
+
+ masking_key = self._read_exact(4) if masked else b""
+ payload = self._read_exact(payload_length) if payload_length else b""
+
+ if masked and payload:
+ payload = bytes(
+ byte ^ masking_key[index % 4]
+ for index, byte in enumerate(payload)
+ )
+
+ if opcode == 0x8:
+ raise ConnectionAbortedError("websocket closed by client")
+
+ if opcode == 0x9:
+ self._send_websocket_frame(0xA, payload)
+ return None
+
+ if opcode == 0xA:
+ return None
+
+ if opcode != 0x1:
+ return None
+
+ return payload.decode("utf-8")
+
+ def _read_exact(self, size: int) -> bytes:
+ if size <= 0:
+ return b""
+
+ chunks = bytearray()
+ while len(chunks) < size:
+ chunk = self.connection.recv(size - len(chunks))
+ if not chunk:
+ raise ConnectionResetError("websocket connection closed")
+ chunks.extend(chunk)
+ return bytes(chunks)
+
+ def _send_websocket_text(self, message: str) -> None:
+ self._send_websocket_frame(0x1, message.encode("utf-8"))
+
+ def _send_websocket_frame(self, opcode: int, payload: bytes) -> None:
+ header = bytearray()
+ header.append(0x80 | opcode)
+
+ payload_length = len(payload)
+ if payload_length < 126:
+ header.append(payload_length)
+ elif payload_length < 65536:
+ header.append(126)
+ header.extend(payload_length.to_bytes(2, "big"))
+ else:
+ header.append(127)
+ header.extend(payload_length.to_bytes(8, "big"))
+
+ self.wfile.write(bytes(header) + payload)
+ self.wfile.flush()
+
+ server = ThreadingHTTPServer((self.host, self.port), TestRuntimeHandler)
+ server.daemon_threads = True
+ return server
diff --git a/xiaozhi-esp32-server/main/manager-api/README.md b/xiaozhi-esp32-server/main/manager-api/README.md
new file mode 100644
index 0000000..484c28d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/README.md
@@ -0,0 +1,18 @@
+鏈枃妗f槸寮�鍙戠被鏂囨。锛屽闇�閮ㄧ讲灏忔櫤鏈嶅姟绔紝[鐐瑰嚮杩欓噷鏌ョ湅閮ㄧ讲鏁欑▼](../../README.md#%E9%83%A8%E7%BD%B2%E6%96%87%E6%A1%A3)
+
+# 椤圭洰浠嬬粛
+
+manager-api 璇ラ」鐩熀浜嶴pringBoot妗嗘灦寮�鍙戙��
+
+寮�鍙戜娇鐢ㄤ唬鐮佺紪杈戝櫒锛屽鍏ラ」鐩椂锛岄�夋嫨`manager-api`鏂囦欢澶逛綔涓洪」鐩洰褰�
+
+# 寮�鍙戠幆澧�
+JDK 21
+Maven 3.8+
+MySQL 8.0+
+Redis 5.0+
+Vue 3.x
+
+# 鎺ュ彛鏂囨。
+鍚姩鍚庢墦寮�锛歨ttp://localhost:8002/xiaozhi/doc.html
+
diff --git a/xiaozhi-esp32-server/main/manager-api/pom.xml b/xiaozhi-esp32-server/main/manager-api/pom.xml
new file mode 100644
index 0000000..455ee6e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/pom.xml
@@ -0,0 +1,296 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>xiaozhi</groupId>
+ <artifactId>xiaozhi-esp32-api</artifactId>
+ <packaging>jar</packaging>
+ <version>0.0.1</version>
+ <name>xiaozhi-esp32-api</name>
+ <description>灏忔櫤鍚庡彴绠$悊绯荤粺</description>
+
+ <parent>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-parent</artifactId>
+ <version>3.4.3</version>
+ </parent>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+ <java.version>21</java.version>
+ <junit.version>5.10.1</junit.version>
+ <druid.version>1.2.20</druid.version>
+ <mybatisplus.version>3.5.17</mybatisplus.version>
+ <hutool.version>5.8.46</hutool.version>
+ <jsoup.version>1.22.2</jsoup.version>
+ <knife4j.version>4.6.0</knife4j.version>
+ <springdoc.version>2.8.8</springdoc.version>
+ <commons-lang3.version>3.20.0</commons-lang3.version>
+ <shiro.version>2.0.2</shiro.version>
+ <captcha.version>1.6.2</captcha.version>
+ <guava.version>33.6.0-jre</guava.version>
+ <liquibase-core.version>4.20.0</liquibase-core.version>
+ <aliyun-sms-version>4.1.0</aliyun-sms-version>
+ <okio-version>3.4.0</okio-version>
+ <skipTests>true</skipTests>
+ <argLine></argLine>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.shiro</groupId>
+ <artifactId>shiro-core</artifactId>
+ <classifier>jakarta</classifier>
+ <version>${shiro.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.shiro</groupId>
+ <artifactId>shiro-spring</artifactId>
+ <classifier>jakarta</classifier>
+ <version>${shiro.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.shiro</groupId>
+ <artifactId>shiro-core</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.shiro</groupId>
+ <artifactId>shiro-web</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.shiro</groupId>
+ <artifactId>shiro-web</artifactId>
+ <classifier>jakarta</classifier>
+ <version>${shiro.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.shiro</groupId>
+ <artifactId>shiro-core</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <!-- 楠岃瘉鐮佸伐鍏峰寘 -->
+ <dependency>
+ <groupId>com.github.whvcse</groupId>
+ <artifactId>easy-captcha</artifactId>
+ <version>${captcha.version}</version>
+ </dependency>
+
+
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-websocket</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>${guava.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ <version>${junit.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <version>${junit.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-params</artifactId>
+ <version>${junit.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.platform</groupId>
+ <artifactId>junit-platform-launcher</artifactId>
+ <version>1.10.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.platform</groupId>
+ <artifactId>junit-platform-commons</artifactId>
+ <version>1.10.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.platform</groupId>
+ <artifactId>junit-platform-engine</artifactId>
+ <version>1.10.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>jakarta.servlet</groupId>
+ <artifactId>jakarta.servlet-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-aop</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context-support</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-data-redis</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-validation</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-configuration-processor</artifactId>
+ <optional>true</optional>
+ </dependency>
+ <!-- mysql椹卞姩 -->
+ <dependency>
+ <groupId>com.mysql</groupId>
+ <artifactId>mysql-connector-j</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>druid-spring-boot-3-starter</artifactId>
+ <version>${druid.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.liquibase</groupId>
+ <artifactId>liquibase-core</artifactId>
+ <version>${liquibase-core.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.baomidou</groupId>
+ <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
+ <version>${mybatisplus.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.baomidou</groupId>
+ <artifactId>mybatis-plus-jsqlparser-4.9</artifactId>
+ <version>${mybatisplus.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>cn.hutool</groupId>
+ <artifactId>hutool-all</artifactId>
+ <version>${hutool.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jsoup</groupId>
+ <artifactId>jsoup</artifactId>
+ <version>${jsoup.version}</version>
+ </dependency>
+ <!--knife4j -->
+ <dependency>
+ <groupId>com.github.xingfudeshi</groupId>
+ <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
+ <version>${knife4j.version}</version>
+ </dependency>
+ <!-- BouncyCastle SM2鍔犲瘑 -->
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk18on</artifactId>
+ <version>1.78</version>
+ </dependency>
+ <!-- springdoc -->
+ <dependency>
+ <groupId>org.springdoc</groupId>
+ <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
+ <version>${springdoc.version}</version>
+ </dependency>
+ <!-- 鏃ュ父宸ュ叿鍖� -->
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>${commons-lang3.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.projectlombok</groupId>
+ <artifactId>lombok</artifactId>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.datatype</groupId>
+ <artifactId>jackson-datatype-jsr310</artifactId>
+ </dependency>
+ <!-- 闃块噷浜戠煭淇dk -->
+ <dependency>
+ <groupId>com.aliyun</groupId>
+ <artifactId>dysmsapi20170525</artifactId>
+ <version>${aliyun-sms-version}</version>
+ </dependency>
+ <!-- 闃块噷浜戠煭淇dk浣跨敤鐨刼kio:1.15.0鐗堟湰鏈夊畨鍏ㄦ紡娲烇細GHSA-w33c-445m-f8w7 鐜板湪鍗囩骇鎴�3.4.0瀹夊叏鐗堟湰-->
+ <dependency>
+ <groupId>com.squareup.okio</groupId>
+ <artifactId>okio</artifactId>
+ <version>${okio-version}</version>
+ </dependency>
+ </dependencies>
+
+ <!-- 闃块噷浜憁aven浠撳簱 -->
+ <repositories>
+ <repository>
+ <id>public</id>
+ <name>aliyun nexus</name>
+ <url>https://maven.aliyun.com/repository/public/</url>
+ <releases>
+ <enabled>true</enabled>
+ </releases>
+ </repository>
+ </repositories>
+ <pluginRepositories>
+ <pluginRepository>
+ <id>public</id>
+ <name>aliyun nexus</name>
+ <url>https://maven.aliyun.com/repository/public/</url>
+ <releases>
+ <enabled>true</enabled>
+ </releases>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </pluginRepository>
+ </pluginRepositories>
+ <build>
+ <finalName>${project.artifactId}</finalName>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <proc>full</proc>
+ <compilerArgs>
+ <arg>-Xlint:deprecation,unchecked</arg>
+ </compilerArgs>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <skipTests>${skipTests}</skipTests>
+ <argLine>@{argLine} -Xshare:off -javaagent:"${settings.localRepository}/org/mockito/mockito-core/${mockito.version}/mockito-core-${mockito.version}.jar"</argLine>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/AdminApplication.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/AdminApplication.java
new file mode 100644
index 0000000..84e3012
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/AdminApplication.java
@@ -0,0 +1,13 @@
+package xiaozhi;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class AdminApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(AdminApplication.class, args);
+ System.out.println("http://localhost:8002/xiaozhi/doc.html");
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/annotation/DataFilter.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/annotation/DataFilter.java
new file mode 100644
index 0000000..9bd6cb9
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/annotation/DataFilter.java
@@ -0,0 +1,33 @@
+package xiaozhi.common.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 鏁版嵁杩囨护娉ㄨВ
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface DataFilter {
+ /**
+ * 琛ㄧ殑鍒悕
+ */
+ String tableAlias() default "";
+
+ /**
+ * 鐢ㄦ埛ID
+ */
+ String userId() default "creator";
+
+ /**
+ * 閮ㄩ棬ID
+ */
+ String deptId() default "dept_id";
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/annotation/LogOperation.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/annotation/LogOperation.java
new file mode 100644
index 0000000..6f6f778
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/annotation/LogOperation.java
@@ -0,0 +1,20 @@
+package xiaozhi.common.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 鎿嶄綔鏃ュ織娉ㄨВ
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface LogOperation {
+
+ String value() default "";
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/aspect/RedisAspect.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/aspect/RedisAspect.java
new file mode 100644
index 0000000..d4233e7
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/aspect/RedisAspect.java
@@ -0,0 +1,41 @@
+package xiaozhi.common.aspect;
+
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+
+/**
+ * Redis鍒囬潰澶勭悊绫�
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Slf4j
+@Aspect
+@Component
+public class RedisAspect {
+ /**
+ * 鏄惁寮�鍚痳edis缂撳瓨 true寮�鍚� false鍏抽棴
+ */
+ @Value("${renren.redis.open}")
+ private boolean open;
+
+ @Around("execution(* xiaozhi.common.redis.RedisUtils.*(..))")
+ public Object around(ProceedingJoinPoint point) throws Throwable {
+ Object result = null;
+ if (open) {
+ try {
+ result = point.proceed();
+ } catch (Exception e) {
+ log.error("redis error", e);
+ throw new RenException(ErrorCode.REDIS_ERROR);
+ }
+ }
+ return result;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/config/AsyncConfig.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/config/AsyncConfig.java
new file mode 100644
index 0000000..d0cc2df
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/config/AsyncConfig.java
@@ -0,0 +1,40 @@
+package xiaozhi.common.config;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadPoolExecutor;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+@Configuration
+@EnableAsync
+@EnableAspectJAutoProxy(exposeProxy = true)
+public class AsyncConfig {
+
+ @Bean(name = "taskExecutor")
+ public Executor taskExecutor() {
+ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+ executor.setCorePoolSize(2);
+ executor.setMaxPoolSize(4);
+ executor.setQueueCapacity(1000);
+ executor.setThreadNamePrefix("AsyncThread-");
+ // 璁剧疆鎷掔粷绛栫暐锛氱敱璋冪敤绾跨▼鎵ц
+ executor.setRejectedExecutionHandler(new RejectedExecutionHandler() {
+ @Override
+ public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
+ try {
+ // 濡傛灉绾跨▼姹犲凡婊★紝鍒欑敱璋冪敤绾跨▼鎵ц
+ r.run();
+ } catch (Exception e) {
+ throw new RuntimeException("鎵ц寮傛浠诲姟澶辫触", e);
+ }
+ }
+ });
+ executor.initialize();
+ return executor;
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/config/MybatisPlusConfig.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/config/MybatisPlusConfig.java
new file mode 100644
index 0000000..250e0d0
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/config/MybatisPlusConfig.java
@@ -0,0 +1,36 @@
+package xiaozhi.common.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+
+import xiaozhi.common.interceptor.DataFilterInterceptor;
+
+/**
+ * mybatis-plus閰嶇疆
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Configuration
+public class MybatisPlusConfig {
+
+ @Bean
+ public MybatisPlusInterceptor mybatisPlusInterceptor() {
+ MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
+ // 鏁版嵁鏉冮檺
+ mybatisPlusInterceptor.addInnerInterceptor(new DataFilterInterceptor());
+ // 鍒嗛〉鎻掍欢
+ mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
+ // 涔愯閿�
+ mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
+ // 闃叉鍏ㄨ〃鏇存柊涓庡垹闄�
+ mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
+
+ return mybatisPlusInterceptor;
+ }
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/config/RestTemplateConfig.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/config/RestTemplateConfig.java
new file mode 100644
index 0000000..6608032
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/config/RestTemplateConfig.java
@@ -0,0 +1,21 @@
+package xiaozhi.common.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.JdkClientHttpRequestFactory;
+import org.springframework.web.client.RestTemplate;
+import java.time.Duration;
+
+/**
+ * RestTemplate閰嶇疆
+ */
+@Configuration
+public class RestTemplateConfig {
+
+ @Bean
+ public RestTemplate restTemplate() {
+ JdkClientHttpRequestFactory factory = new JdkClientHttpRequestFactory();
+ factory.setReadTimeout(Duration.ofSeconds(30));
+ return new RestTemplate(factory);
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/config/SwaggerConfig.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/config/SwaggerConfig.java
new file mode 100644
index 0000000..3efe261
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/config/SwaggerConfig.java
@@ -0,0 +1,114 @@
+package xiaozhi.common.config;
+
+import org.springdoc.core.models.GroupedOpenApi;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Info;
+
+/**
+ * Swagger閰嶇疆
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Configuration
+public class SwaggerConfig {
+
+ @Bean
+ public GroupedOpenApi deviceApi() {
+ return GroupedOpenApi.builder()
+ .group("device")
+ .pathsToMatch("/device/**")
+ .build();
+ }
+
+ @Bean
+ public GroupedOpenApi agentApi() {
+ return GroupedOpenApi.builder()
+ .group("agent")
+ .pathsToMatch("/agent/**")
+ .build();
+ }
+
+ @Bean
+ public GroupedOpenApi modelApi() {
+ return GroupedOpenApi.builder()
+ .group("models")
+ .pathsToMatch("/models/**")
+ .build();
+ }
+
+ @Bean
+ public GroupedOpenApi oatApi() {
+ return GroupedOpenApi.builder()
+ .group("ota")
+ .pathsToMatch("/ota/**", "/otaMag/**")
+ .build();
+ }
+
+ @Bean
+ public GroupedOpenApi timbreApi() {
+ return GroupedOpenApi.builder()
+ .group("timbre")
+ .pathsToMatch("/ttsVoice/**")
+ .build();
+ }
+
+ @Bean
+ public GroupedOpenApi sysApi() {
+ return GroupedOpenApi.builder()
+ .group("admin")
+ .pathsToMatch("/admin/**")
+ .build();
+ }
+
+ @Bean
+ public GroupedOpenApi userApi() {
+ return GroupedOpenApi.builder()
+ .group("user")
+ .pathsToMatch("/user/**")
+ .build();
+ }
+
+ @Bean
+ public GroupedOpenApi configApi() {
+ return GroupedOpenApi.builder()
+ .group("config")
+ .pathsToMatch("/config/**")
+ .build();
+ }
+
+ @Bean
+ public GroupedOpenApi knowledgeApi() {
+ return GroupedOpenApi.builder()
+ .group("knowledge")
+ .pathsToMatch("/datasets/**")
+ .build();
+ }
+
+ @Bean
+ public GroupedOpenApi botApi() {
+ return GroupedOpenApi.builder()
+ .group("bot")
+ .pathsToMatch("/api/v1/**")
+ .build();
+ }
+
+ @Bean
+ public GroupedOpenApi correctWordApi() {
+ return GroupedOpenApi.builder()
+ .group("correct-word")
+ .pathsToMatch("/correct-word/**")
+ .build();
+ }
+
+ @Bean
+ public OpenAPI customOpenAPI() {
+ return new OpenAPI().info(new Info()
+ .title("xiaozhi-esp32-manager-api")
+ .description("xiaozhi-esp32-manager-api鏂囨。")
+ .version("3.0")
+ .termsOfService("https://127.0.0.1"));
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java
new file mode 100644
index 0000000..506e676
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java
@@ -0,0 +1,353 @@
+package xiaozhi.common.constant;
+
+import lombok.Getter;
+
+/**
+ * 甯搁噺
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public interface Constant {
+ /**
+ * 鎴愬姛
+ */
+ int SUCCESS = 1;
+ /**
+ * 澶辫触
+ */
+ int FAIL = 0;
+ /**
+ * OK
+ */
+ String OK = "OK";
+ /**
+ * 鐢ㄦ埛鏍囪瘑
+ */
+ String USER_KEY = "userId";
+ /**
+ * 鑿滃崟鏍硅妭鐐规爣璇�
+ */
+ Long MENU_ROOT = 0L;
+ /**
+ * 閮ㄩ棬鏍硅妭鐐规爣璇�
+ */
+ Long DEPT_ROOT = 0L;
+ /**
+ * 鏁版嵁瀛楀吀鏍硅妭鐐规爣璇�
+ */
+ Long DICT_ROOT = 0L;
+ /**
+ * 鍗囧簭
+ */
+ String ASC = "asc";
+ /**
+ * 闄嶅簭
+ */
+ String DESC = "desc";
+ /**
+ * 鍒涘缓鏃堕棿瀛楁鍚�
+ */
+ String CREATE_DATE = "create_date";
+
+ /**
+ * 鍒涘缓鏃堕棿瀛楁鍚�
+ */
+ String ID = "id";
+
+ /**
+ * 鏁版嵁鏉冮檺杩囨护
+ */
+ String SQL_FILTER = "sqlFilter";
+
+ /**
+ * 褰撳墠椤电爜
+ */
+ String PAGE = "page";
+ /**
+ * 姣忛〉鏄剧ず璁板綍鏁�
+ */
+ String LIMIT = "limit";
+ /**
+ * 鎺掑簭瀛楁
+ */
+ String ORDER_FIELD = "orderField";
+ /**
+ * 鎺掑簭鏂瑰紡
+ */
+ String ORDER = "order";
+
+ /**
+ * 璇锋眰澶存巿鏉冩爣璇�
+ */
+ String AUTHORIZATION = "Authorization";
+
+ /**
+ * 鏈嶅姟鍣ㄥ瘑閽�
+ */
+ String SERVER_SECRET = "server.secret";
+
+ /**
+ * SM2鍏挜
+ */
+ String SM2_PUBLIC_KEY = "server.public_key";
+
+ /**
+ * SM2绉侀挜
+ */
+ String SM2_PRIVATE_KEY = "server.private_key";
+
+ /**
+ * websocket鍦板潃
+ */
+ String SERVER_WEBSOCKET = "server.websocket";
+
+ /**
+ * mqtt gateway 閰嶇疆
+ */
+ String SERVER_MQTT_GATEWAY = "server.mqtt_gateway";
+
+ /**
+ * ota鍦板潃
+ */
+ String SERVER_OTA = "server.ota";
+
+ /**
+ * 鏄惁鍏佽鐢ㄦ埛娉ㄥ唽
+ */
+ String SERVER_ALLOW_USER_REGISTER = "server.allow_user_register";
+
+ /**
+ * 涓嬪彂鍏綅楠岃瘉鐮佹椂鏄剧ず鐨勬帶鍒堕潰鏉垮湴鍧�
+ */
+ String SERVER_FRONTED_URL = "server.fronted_url";
+
+ /**
+ * 璺緞鍒嗗壊绗�
+ */
+ String FILE_EXTENSION_SEG = ".";
+
+ /**
+ * mcp鎺ュ叆鐐硅矾寰�
+ */
+ String SERVER_MCP_ENDPOINT = "server.mcp_endpoint";
+
+ /**
+ * mcp鎺ュ叆鐐硅矾寰�
+ */
+ String SERVER_VOICE_PRINT = "server.voice_print";
+
+ /**
+ * mqtt瀵嗛挜
+ */
+ String SERVER_MQTT_SECRET = "server.mqtt_signature_key";
+
+ /**
+ * WebSocket璁よ瘉寮�鍏�
+ */
+ String SERVER_AUTH_ENABLED = "server.auth.enabled";
+
+ /**
+ * 绯荤粺鍔熻兘鑿滃崟閰嶇疆
+ */
+ String SYSTEM_WEB_MENU = "system-web.menu";
+
+ /**
+ * 鏃犺蹇�
+ */
+ String MEMORY_NO_MEM = "Memory_nomem";
+
+ /**
+ * 浠呬笂鎶ヨ亰澶╄褰曪紙涓嶆�荤粨璁板繂锛�
+ */
+ String MEMORY_MEM_REPORT_ONLY = "Memory_mem_report_only";
+
+ /**
+ * Mem0AI璁板繂
+ */
+ String MEMORY_MEM0AI = "Memory_mem0ai";
+
+ /**
+ * PowerMem璁板繂
+ */
+ String MEMORY_POWERMEM = "Memory_powermem";
+
+ /**
+ * 鐏北寮曟搸鍙屽0閬撹闊冲厠闅�
+ */
+ String VOICE_CLONE_HUOSHAN_DOUBLE_STREAM = "huoshan_double_stream";
+
+ /**
+ * RAG閰嶇疆绫诲瀷
+ */
+ String RAG_CONFIG_TYPE = "RAG";
+
+ enum SysBaseParam {
+ /**
+ * ICP澶囨鍙�
+ */
+ BEIAN_ICP_NUM("server.beian_icp_num"),
+ /**
+ * GA澶囨鍙�
+ */
+ BEIAN_GA_NUM("server.beian_ga_num"),
+ /**
+ * 绯荤粺鍚嶇О
+ */
+ SERVER_NAME("server.name");
+
+ private String value;
+
+ SysBaseParam(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+ }
+
+ /**
+ * 璁粌鐘舵��
+ */
+ enum TrainStatus {
+ /**
+ * 鏈缁�
+ */
+ NOT_TRAINED(0),
+ /**
+ * 璁粌涓�
+ */
+ TRAINING(1),
+ /**
+ * 宸茶缁�
+ */
+ TRAINED(2),
+ /**
+ * 璁粌澶辫触
+ */
+ TRAIN_FAILED(3);
+
+ private final int code;
+
+ TrainStatus(int code) {
+ this.code = code;
+ }
+
+ public int getCode() {
+ return code;
+ }
+ }
+
+ /**
+ * 绯荤粺鐭俊
+ */
+ enum SysMSMParam {
+ /**
+ * 闃块噷浜戞巿鏉僰eyID
+ */
+ ALIYUN_SMS_ACCESS_KEY_ID("aliyun.sms.access_key_id"),
+ /**
+ * 闃块噷浜戞巿鏉冨瘑閽�
+ */
+ ALIYUN_SMS_ACCESS_KEY_SECRET("aliyun.sms.access_key_secret"),
+ /**
+ * 闃块噷浜戠煭淇$鍚�
+ */
+ ALIYUN_SMS_SIGN_NAME("aliyun.sms.sign_name"),
+ /**
+ * 闃块噷浜戠煭淇℃ā鏉�
+ */
+ ALIYUN_SMS_SMS_CODE_TEMPLATE_CODE("aliyun.sms.sms_code_template_code"),
+ /**
+ * 鍗曞彿鐮佹渶澶х煭淇″彂閫佹潯鏁�
+ */
+ SERVER_SMS_MAX_SEND_COUNT("server.sms_max_send_count"),
+ /**
+ * 鏄惁寮�鍚墜鏈烘敞鍐�
+ */
+ SERVER_ENABLE_MOBILE_REGISTER("server.enable_mobile_register");
+
+ private String value;
+
+ SysMSMParam(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+ }
+
+ /**
+ * 鏁版嵁鐘舵��
+ */
+ enum DataOperation {
+ /**
+ * 鎻掑叆
+ */
+ INSERT("I"),
+ /**
+ * 宸蹭慨鏀�
+ */
+ UPDATE("U"),
+ /**
+ * 宸插垹闄�
+ */
+ DELETE("D");
+
+ private String value;
+
+ DataOperation(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+ }
+
+ @Getter
+ enum ChatHistoryConfEnum {
+ IGNORE(0, "涓嶈褰�"),
+ RECORD_TEXT(1, "璁板綍鏂囨湰"),
+ RECORD_TEXT_AUDIO(2, "鏂囨湰闊抽閮借褰�");
+
+ private final int code;
+ private final String name;
+
+ ChatHistoryConfEnum(int code, String name) {
+ this.code = code;
+ this.name = name;
+ }
+ }
+
+ /**
+ * 鐗堟湰鍙�
+ */
+ public static final String VERSION = "0.9.6";
+
+ /**
+ * 鏃犳晥鍥轰欢URL
+ */
+ String INVALID_FIRMWARE_URL = "http://xiaozhi.server.com:8002/xiaozhi/otaMag/download/NOT_ACTIVATED_FIRMWARE_THIS_IS_A_INVALID_URL";
+
+ /**
+ * 瀛楀吀绫诲瀷
+ */
+ enum DictType {
+ /**
+ * 鎵嬫満鍖哄彿
+ */
+ MOBILE_AREA("MOBILE_AREA");
+
+ private String value;
+
+ DictType(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/convert/DateConverter.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/convert/DateConverter.java
new file mode 100644
index 0000000..fd0f74c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/convert/DateConverter.java
@@ -0,0 +1,73 @@
+package xiaozhi.common.convert;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.stereotype.Component;
+
+/**
+ * 鏃ユ湡杞崲
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Component
+public class DateConverter implements Converter<String, Date> {
+ private static final Logger logger = LoggerFactory.getLogger(DateConverter.class);
+ private static final List<String> formatList = new ArrayList<>(5);
+
+ static {
+ formatList.add("yyyy-MM");
+ formatList.add("yyyy-MM-dd");
+ formatList.add("yyyy-MM-dd hh:mm");
+ formatList.add("yyyy-MM-dd hh:mm:ss");
+ formatList.add("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+ }
+
+ @Override
+ public Date convert(String source) {
+ String value = source.trim();
+ if (StringUtils.isEmpty(value)) {
+ return null;
+ }
+
+ if (source.matches("^\\d{4}-\\d{1,2}$")) {
+ return parseDate(source, formatList.get(0));
+ } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2}$")) {
+ return parseDate(source, formatList.get(1));
+ } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}$")) {
+ return parseDate(source, formatList.get(2));
+ } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}$")) {
+ return parseDate(source, formatList.get(3));
+ } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2}.*T.*\\d{1,2}:\\d{1,2}:\\d{1,2}.*..*$")) {
+ return parseDate(source, formatList.get(4));
+ } else {
+ throw new IllegalArgumentException("Invalid boolean value '" + source + "'");
+ }
+ }
+
+ /**
+ * 鏍煎紡鍖栨棩鏈�
+ *
+ * @param dateStr String 瀛楃鍨嬫棩鏈�
+ * @param format String 鏍煎紡
+ * @return Date 鏃ユ湡
+ */
+ public Date parseDate(String dateStr, String format) {
+ Date date = null;
+ try {
+ DateFormat dateFormat = new SimpleDateFormat(format);
+ date = dateFormat.parse(dateStr);
+ } catch (Exception e) {
+ logger.error("Formatted date with date: {} and format : {} ", dateStr, format);
+ }
+ return date;
+ }
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/dao/BaseDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/dao/BaseDao.java
new file mode 100644
index 0000000..d65e783
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/dao/BaseDao.java
@@ -0,0 +1,12 @@
+package xiaozhi.common.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * 鍩虹Dao
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public interface BaseDao<T> extends BaseMapper<T> {
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/entity/BaseEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/entity/BaseEntity.java
new file mode 100644
index 0000000..ce267ba
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/entity/BaseEntity.java
@@ -0,0 +1,34 @@
+package xiaozhi.common.entity;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+
+import lombok.Data;
+
+/**
+ * 鍩虹瀹炰綋绫伙紝鎵�鏈夊疄浣撻兘闇�瑕佺户鎵�
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Data
+public abstract class BaseEntity implements Serializable {
+ /**
+ * id
+ */
+ @TableId
+ private Long id;
+ /**
+ * 鍒涘缓鑰�
+ */
+ @TableField(fill = FieldFill.INSERT)
+ private Long creator;
+ /**
+ * 鍒涘缓鏃堕棿
+ */
+ @TableField(fill = FieldFill.INSERT)
+ private Date createDate;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java
new file mode 100644
index 0000000..134b66c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java
@@ -0,0 +1,263 @@
+package xiaozhi.common.exception;
+
+/**
+ * 閿欒缂栫爜锛岀敱5浣嶆暟瀛楃粍鎴愶紝鍓�2浣嶄负妯″潡缂栫爜锛屽悗3浣嶄负涓氬姟缂栫爜
+ * <p>
+ * 濡傦細10001锛�10浠h〃绯荤粺妯″潡锛�001浠h〃涓氬姟浠g爜锛�
+ * </p>
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public interface ErrorCode {
+ int INTERNAL_SERVER_ERROR = 500;
+ int UNAUTHORIZED = 401;
+ int FORBIDDEN = 403;
+
+ int NOT_NULL = 10001;
+ int DB_RECORD_EXISTS = 10002;
+ int PARAMS_GET_ERROR = 10003;
+ int ACCOUNT_PASSWORD_ERROR = 10004;
+ int ACCOUNT_DISABLE = 10005;
+ int IDENTIFIER_NOT_NULL = 10006;
+ int CAPTCHA_ERROR = 10007;
+ int PHONE_NOT_NULL = 10008;
+ int PASSWORD_ERROR = 10009;
+
+ int SUPERIOR_DEPT_ERROR = 10011;
+ int SUPERIOR_MENU_ERROR = 10012;
+ int DATA_SCOPE_PARAMS_ERROR = 10013;
+ int DEPT_SUB_DELETE_ERROR = 10014;
+ int DEPT_USER_DELETE_ERROR = 10015;
+
+ int UPLOAD_FILE_EMPTY = 10019;
+ int TOKEN_NOT_EMPTY = 10020;
+ int TOKEN_INVALID = 10021;
+ int ACCOUNT_LOCK = 10022;
+
+ int OSS_UPLOAD_FILE_ERROR = 10024;
+
+ int REDIS_ERROR = 10027;
+ int JOB_ERROR = 10028;
+ int INVALID_SYMBOL = 10029;
+ int PASSWORD_LENGTH_ERROR = 10030;
+ int PASSWORD_WEAK_ERROR = 10031;
+ int DEL_MYSELF_ERROR = 10032;
+ int DEVICE_CAPTCHA_ERROR = 10033;
+
+ // 鍙傛暟鏍¢獙鐩稿叧閿欒鐮�
+ int PARAM_VALUE_NULL = 10034;
+ int PARAM_TYPE_NULL = 10035;
+ int PARAM_TYPE_INVALID = 10036;
+ int PARAM_NUMBER_INVALID = 10037;
+ int PARAM_BOOLEAN_INVALID = 10038;
+ int PARAM_ARRAY_INVALID = 10039;
+ int PARAM_JSON_INVALID = 10040;
+
+ int OTA_DEVICE_NOT_FOUND = 10041;
+ int OTA_DEVICE_NEED_BIND = 10042;
+
+ // 鏂板閿欒缂栫爜
+ int DELETE_DATA_FAILED = 10043;
+ int USER_NOT_LOGIN = 10044;
+ int WEB_SOCKET_CONNECT_FAILED = 10045;
+ int VOICE_PRINT_SAVE_ERROR = 10046;
+ int TODAY_SMS_LIMIT_REACHED = 10047;
+ int OLD_PASSWORD_ERROR = 10048;
+ int INVALID_LLM_TYPE = 10049;
+ int TOKEN_GENERATE_ERROR = 10050;
+ int RESOURCE_NOT_FOUND = 10051;
+
+ // 鏂板閿欒缂栫爜
+ int DEFAULT_AGENT_NOT_FOUND = 10052;
+ int AGENT_NOT_FOUND = 10053;
+ int VOICEPRINT_API_NOT_CONFIGURED = 10054;
+ int SMS_SEND_FAILED = 10055;
+ int SMS_CONNECTION_FAILED = 10056;
+ int AGENT_VOICEPRINT_CREATE_FAILED = 10057;
+ int AGENT_VOICEPRINT_UPDATE_FAILED = 10058;
+ int AGENT_VOICEPRINT_DELETE_FAILED = 10059;
+ int SMS_SEND_TOO_FREQUENTLY = 10060;
+ int ACTIVATION_CODE_EMPTY = 10061;
+ int ACTIVATION_CODE_ERROR = 10062;
+ int DEVICE_ALREADY_ACTIVATED = 10063;
+ // 榛樿妯″瀷鍒犻櫎閿欒
+ int DEFAULT_MODEL_DELETE_ERROR = 10064;
+ // 鐧诲綍鐩稿叧閿欒鐮�
+ int ADD_DATA_FAILED = 10065; // 鏂板鏁版嵁澶辫触
+ int UPDATE_DATA_FAILED = 10066; // 淇敼鏁版嵁澶辫触
+ int SMS_CAPTCHA_ERROR = 10067; // 鐭俊楠岃瘉鐮侀敊璇�
+ int MOBILE_REGISTER_DISABLED = 10068; // 鏈紑鍚墜鏈烘敞鍐�
+ int USERNAME_NOT_PHONE = 10069; // 鐢ㄦ埛鍚嶄笉鏄墜鏈哄彿鐮�
+ int PHONE_ALREADY_REGISTERED = 10070; // 鎵嬫満鍙风爜宸叉敞鍐�
+ int PHONE_NOT_REGISTERED = 10071; // 鎵嬫満鍙风爜鏈敞鍐�
+ int USER_REGISTER_DISABLED = 10072; // 涓嶅厑璁哥敤鎴锋敞鍐�
+ int RETRIEVE_PASSWORD_DISABLED = 10073; // 鏈紑鍚壘鍥炲瘑鐮佸姛鑳�
+ int PHONE_FORMAT_ERROR = 10074; // 鎵嬫満鍙风爜鏍煎紡涓嶆纭�
+ int SMS_CODE_ERROR = 10075; // 鎵嬫満楠岃瘉鐮侀敊璇�
+
+ // 瀛楀吀绫诲瀷鐩稿叧閿欒鐮�
+ int DICT_TYPE_NOT_EXIST = 10076; // 瀛楀吀绫诲瀷涓嶅瓨鍦�
+ int DICT_TYPE_DUPLICATE = 10077; // 瀛楀吀绫诲瀷缂栫爜閲嶅
+
+ // 璧勬簮澶勭悊鐩稿叧閿欒鐮�
+ int RESOURCE_READ_ERROR = 10078; // 璇诲彇璧勬簮澶辫触
+
+ // 鏅鸿兘浣撶浉鍏抽敊璇爜
+ int LLM_INTENT_PARAMS_MISMATCH = 10079; // LLM澶фā鍨嬪拰Intent鎰忓浘璇嗗埆锛岄�夋嫨鍙傛暟涓嶅尮閰�
+
+ // 澹扮汗鐩稿叧閿欒鐮�
+ int VOICEPRINT_ALREADY_REGISTERED = 10080; // 姝ゅ0闊冲0绾瑰凡缁忔敞鍐�
+ int VOICEPRINT_DELETE_ERROR = 10081; // 鍒犻櫎澹扮汗鍑虹幇閿欒
+ int VOICEPRINT_UPDATE_NOT_ALLOWED = 10082; // 澹扮汗淇敼涓嶅厑璁革紝澹伴煶宸叉敞鍐�
+ int VOICEPRINT_UPDATE_ADMIN_ERROR = 10083; // 淇敼澹扮汗閿欒锛岃鑱旂郴绠$悊鍛�
+ int VOICEPRINT_API_URI_ERROR = 10084; // 澹扮汗鎺ュ彛鍦板潃閿欒
+ int VOICEPRINT_AUDIO_NOT_BELONG_AGENT = 10085; // 闊抽鏁版嵁涓嶅睘浜庢櫤鑳戒綋
+ int VOICEPRINT_AUDIO_EMPTY = 10086; // 闊抽鏁版嵁涓虹┖
+ int VOICEPRINT_REGISTER_REQUEST_ERROR = 10087; // 澹扮汗淇濆瓨璇锋眰澶辫触
+ int VOICEPRINT_REGISTER_PROCESS_ERROR = 10088; // 澹扮汗淇濆瓨澶勭悊澶辫触
+ int VOICEPRINT_UNREGISTER_REQUEST_ERROR = 10089; // 澹扮汗娉ㄩ攢璇锋眰澶辫触
+ int VOICEPRINT_UNREGISTER_PROCESS_ERROR = 10090; // 澹扮汗娉ㄩ攢澶勭悊澶辫触
+ int VOICEPRINT_IDENTIFY_REQUEST_ERROR = 10091; // 澹扮汗璇嗗埆璇锋眰澶辫触
+
+ int LLM_NOT_EXIST = 10092; // 璁剧疆鐨凩LM涓嶅瓨鍦�
+ int MODEL_REFERENCED_BY_AGENT = 10093; // 璇ユā鍨嬮厤缃凡琚櫤鑳戒綋寮曠敤锛屾棤娉曞垹闄�
+ int LLM_REFERENCED_BY_INTENT = 10094; // 璇LM妯″瀷宸茶鎰忓浘璇嗗埆閰嶇疆寮曠敤锛屾棤娉曞垹闄�
+
+ // 鏈嶅姟绔鐞嗙浉鍏抽敊璇爜
+ int INVALID_SERVER_ACTION = 10095; // 鏃犳晥鏈嶅姟绔搷浣�
+ int SERVER_WEBSOCKET_NOT_CONFIGURED = 10096; // 鏈厤缃湇鍔$WebSocket鍦板潃
+ int TARGET_WEBSOCKET_NOT_EXIST = 10097; // 鐩爣WebSocket鍦板潃涓嶅瓨鍦�
+
+ // 鍙傛暟楠岃瘉鐩稿叧閿欒鐮�
+ int WEBSOCKET_URLS_EMPTY = 10098; // WebSocket鍦板潃鍒楄〃涓嶈兘涓虹┖
+ int WEBSOCKET_URL_LOCALHOST = 10099; // WebSocket鍦板潃涓嶈兘浣跨敤localhost鎴�127.0.0.1
+ int WEBSOCKET_URL_FORMAT_ERROR = 10100; // WebSocket鍦板潃鏍煎紡涓嶆纭�
+ int WEBSOCKET_CONNECTION_FAILED = 10101; // WebSocket杩炴帴娴嬭瘯澶辫触
+ int OTA_URL_EMPTY = 10102; // OTA鍦板潃涓嶈兘涓虹┖
+ int OTA_URL_LOCALHOST = 10103; // OTA鍦板潃涓嶈兘浣跨敤localhost鎴�127.0.0.1
+ int OTA_URL_PROTOCOL_ERROR = 10104; // OTA鍦板潃蹇呴』浠ttp鎴杊ttps寮�澶�
+ int OTA_URL_FORMAT_ERROR = 10105; // OTA鍦板潃蹇呴』浠�/ota/缁撳熬
+ int OTA_INTERFACE_ACCESS_FAILED = 10106; // OTA鎺ュ彛璁块棶澶辫触
+ int OTA_INTERFACE_FORMAT_ERROR = 10107; // OTA鎺ュ彛杩斿洖鍐呭鏍煎紡涓嶆纭�
+ int OTA_INTERFACE_VALIDATION_FAILED = 10108; // OTA鎺ュ彛楠岃瘉澶辫触
+ int MCP_URL_EMPTY = 10109; // MCP鍦板潃涓嶈兘涓虹┖
+ int MCP_URL_LOCALHOST = 10110; // MCP鍦板潃涓嶈兘浣跨敤localhost鎴�127.0.0.1
+ int MCP_URL_INVALID = 10111; // 涓嶆槸姝g‘鐨凪CP鍦板潃
+ int MCP_INTERFACE_ACCESS_FAILED = 10112; // MCP鎺ュ彛璁块棶澶辫触
+ int MCP_INTERFACE_FORMAT_ERROR = 10113; // MCP鎺ュ彛杩斿洖鍐呭鏍煎紡涓嶆纭�
+ int MCP_INTERFACE_VALIDATION_FAILED = 10114; // MCP鎺ュ彛楠岃瘉澶辫触
+ int VOICEPRINT_URL_EMPTY = 10115; // 澹扮汗鎺ュ彛鍦板潃涓嶈兘涓虹┖
+ int VOICEPRINT_URL_LOCALHOST = 10116; // 澹扮汗鎺ュ彛鍦板潃涓嶈兘浣跨敤localhost鎴�127.0.0.1
+ int VOICEPRINT_URL_INVALID = 10117; // 涓嶆槸姝g‘鐨勫0绾规帴鍙e湴鍧�
+ int VOICEPRINT_URL_PROTOCOL_ERROR = 10118; // 澹扮汗鎺ュ彛鍦板潃蹇呴』浠ttp鎴杊ttps寮�澶�
+ int VOICEPRINT_INTERFACE_ACCESS_FAILED = 10119; // 澹扮汗鎺ュ彛璁块棶澶辫触
+ int VOICEPRINT_INTERFACE_FORMAT_ERROR = 10120; // 澹扮汗鎺ュ彛杩斿洖鍐呭鏍煎紡涓嶆纭�
+ int VOICEPRINT_INTERFACE_VALIDATION_FAILED = 10121; // 澹扮汗鎺ュ彛楠岃瘉澶辫触
+ int MQTT_SECRET_EMPTY = 10122; // mqtt瀵嗛挜涓嶈兘涓虹┖
+ int MQTT_SECRET_LENGTH_INSECURE = 10123; // mqtt瀵嗛挜闀垮害涓嶅畨鍏�
+ int MQTT_SECRET_CHARACTER_INSECURE = 10124; // mqtt瀵嗛挜蹇呴』鍚屾椂鍖呭惈澶у皬鍐欏瓧姣�
+ int MQTT_SECRET_WEAK_PASSWORD = 10125; // mqtt瀵嗛挜鍖呭惈寮卞瘑鐮�
+ int DICT_LABEL_DUPLICATE = 10128; // 瀛楀吀鏍囩閲嶅
+ int SM2_KEY_NOT_CONFIGURED = 10129; // SM2瀵嗛挜鏈厤缃�
+ int SM2_DECRYPT_ERROR = 10130; // SM2瑙e瘑澶辫触
+ int MODEL_TYPE_PROVIDE_CODE_NOT_NULL = 10131; // modelType鍜宲rovideCode涓嶈兘涓虹┖
+
+ // 鑱婂ぉ璁板綍鐩稿叧閿欒鐮�
+ int CHAT_HISTORY_NO_PERMISSION = 10132; // 娌℃湁鏉冮檺鏌ョ湅璇ユ櫤鑳戒綋鐨勮亰澶╄褰�
+ int CHAT_HISTORY_SESSION_ID_NOT_NULL = 10133; // 浼氳瘽ID涓嶈兘涓虹┖
+ int CHAT_HISTORY_AGENT_ID_NOT_NULL = 10134; // 鏅鸿兘浣揑D涓嶈兘涓虹┖
+ int CHAT_HISTORY_DOWNLOAD_FAILED = 10135; // 鑱婂ぉ璁板綍涓嬭浇澶辫触
+ int DOWNLOAD_LINK_EXPIRED = 10136; // 涓嬭浇閾炬帴宸茶繃鏈熸垨鏃犳晥
+ int DOWNLOAD_LINK_INVALID = 10137; // 涓嬭浇閾炬帴鏃犳晥
+ int CHAT_ROLE_USER = 10138; // 鐢ㄦ埛瑙掕壊
+ int CHAT_ROLE_AGENT = 10139; // 鏅鸿兘浣撹鑹�
+
+ // 澹伴煶鍏嬮殕鐩稿叧閿欒鐮�
+ int VOICE_CLONE_AUDIO_EMPTY = 10140; // 闊抽鏂囦欢涓嶈兘涓虹┖
+ int VOICE_CLONE_NOT_AUDIO_FILE = 10141; // 鍙敮鎸侀煶棰戞枃浠�
+ int VOICE_CLONE_AUDIO_TOO_LARGE = 10142; // 闊抽鏂囦欢澶у皬涓嶈兘瓒呰繃10MB
+ int VOICE_CLONE_UPLOAD_FAILED = 10143; // 涓婁紶澶辫触
+ int VOICE_CLONE_RECORD_NOT_EXIST = 10144; // 澹伴煶鍏嬮殕璁板綍涓嶅瓨鍦�
+ int VOICE_RESOURCE_INFO_EMPTY = 10145; // 闊宠壊璧勬簮淇℃伅涓嶈兘涓虹┖
+ int VOICE_RESOURCE_PLATFORM_NAME_EMPTY = 10146; // 骞冲彴鍚嶇О涓嶈兘涓虹┖
+ int VOICE_RESOURCE_ID_EMPTY = 10147; // 闊宠壊ID涓嶈兘涓虹┖
+ int VOICE_RESOURCE_ACCOUNT_EMPTY = 10148; // 褰掑睘璐﹀彿涓嶈兘涓虹┖
+ int VOICE_RESOURCE_DELETE_ID_EMPTY = 10149; // 鍒犻櫎鐨勯煶鑹茶祫婧怚D涓嶈兘涓虹┖
+ int VOICE_RESOURCE_NO_PERMISSION = 10150; // 鎮ㄦ病鏈夋潈闄愭搷浣滆璁板綍
+ int VOICE_CLONE_AUDIO_NOT_UPLOADED = 10151; // 璇峰厛涓婁紶闊抽鏂囦欢
+ int VOICE_CLONE_MODEL_CONFIG_NOT_FOUND = 10152; // 妯″瀷閰嶇疆鏈壘鍒�
+ int VOICE_CLONE_MODEL_TYPE_NOT_FOUND = 10153; // 妯″瀷绫诲瀷鏈壘鍒�
+ int VOICE_CLONE_TRAINING_FAILED = 10154; // 璁粌澶辫触
+ int VOICE_CLONE_HUOSHAN_CONFIG_MISSING = 10155; // 鐏北寮曟搸缂哄皯閰嶇疆
+ int VOICE_CLONE_RESPONSE_FORMAT_ERROR = 10156; // 鍝嶅簲鏍煎紡閿欒
+ int VOICE_CLONE_REQUEST_FAILED = 10157; // 璇锋眰澶辫触
+ int VOICE_CLONE_PREFIX = 10158; // 澶嶅埢闊宠壊鍓嶇紑
+ int VOICE_ID_ALREADY_EXISTS = 10159; // 闊宠壊ID宸插瓨鍦�
+ int VOICE_CLONE_HUOSHAN_VOICE_ID_ERROR = 10160; // 鐏北寮曟搸闊宠壊ID鏍煎紡閿欒
+
+ // 璁惧鐩稿叧閿欒鐮�
+ int MAC_ADDRESS_ALREADY_EXISTS = 10161; // Mac鍦板潃宸插瓨鍦�
+ // 妯″瀷鐩稿叧閿欒鐮�
+ int MODEL_PROVIDER_NOT_EXIST = 10162; // 渚涘簲鍣ㄤ笉瀛樺湪
+
+ // 鐭ヨ瘑搴撶浉鍏抽敊璇爜
+ int Knowledge_Base_RECORD_NOT_EXISTS = 10163; // 鐭ヨ瘑搴撹褰曚笉瀛樺湪
+ int RAG_CONFIG_NOT_FOUND = 10164; // RAG閰嶇疆鏈壘鍒�
+ int RAG_CONFIG_TYPE_ERROR = 10165; // RAG閰嶇疆绫诲瀷閿欒
+ int RAG_DEFAULT_CONFIG_NOT_FOUND = 10166; // 榛樿RAG閰嶇疆鏈壘鍒�
+ int RAG_API_ERROR = 10167; // RAG璋冪敤澶辫触
+ int UPLOAD_FILE_ERROR = 10168; // 涓婁紶鏂囦欢澶辫触
+ int NO_PERMISSION = 10169; // 娌℃湁鏉冮檺
+ int KNOWLEDGE_BASE_NAME_EXISTS = 10170; // 鍚屽悕鐭ヨ瘑搴撳凡瀛樺湪
+ int RAG_API_ERROR_URL_NULL = 10171; // RAG閰嶇疆涓璪ase_url涓虹┖锛岃瀹屽杽閰嶇疆
+ int RAG_API_ERROR_API_KEY_NULL = 10172; // RAG閰嶇疆涓璦pi_key涓虹┖锛岃瀹屽杽閰嶇疆
+ int RAG_API_ERROR_API_KEY_INVALID = 10173; // RAG閰嶇疆涓璦pi_key鍖呭惈鍗犱綅绗︼紝璇锋浛鎹负瀹為檯鐨凙PI瀵嗛挜
+ int RAG_API_ERROR_URL_INVALID = 10174; // RAG閰嶇疆涓璪ase_url鏍煎紡涓嶆纭紝璇锋鏌ュ崗璁槸鍚︽纭�
+ int RAG_DATASET_ID_NOT_NULL = 10176; // RAG閰嶇疆涓璬ataset_id涓嶈兘涓虹┖
+ int RAG_MODEL_ID_NOT_NULL = 10177; // RAG閰嶇疆涓璵odel_id涓嶈兘涓虹┖
+ int RAG_DATASET_ID_AND_MODEL_ID_NOT_NULL = 10178; // RAG閰嶇疆涓璬ataset_id鍜宮odel_id涓嶈兘涓虹┖
+ int RAG_FILE_NAME_NOT_NULL = 10179; // 鏂囦欢鍚嶇О涓嶈兘涓虹┖
+ int RAG_FILE_CONTENT_EMPTY = 10180; // 鏂囦欢鍐呭涓嶈兘涓虹┖
+
+ // 璁惧鐩稿叧閿欒鐮侊紙琛ュ厖锛�
+ int MCA_NOT_NULL = 10175; // mac鍦板潃涓嶈兘涓虹┖
+
+ // 闊宠壊鍏嬮殕锛堣ˉ鍏咃級
+ int VOICE_CLONE_NAME_NOT_NULL = 10181; // 闊宠壊鍏嬮殕鍚嶇О涓嶈兘涓虹┖
+ int VOICE_CLONE_AUDIO_NOT_FOUND = 10182; // 闊宠壊鍏嬮殕闊抽涓嶅瓨鍦�
+
+ // 鏅鸿兘浣撴ā鏉跨浉鍏抽敊璇爜(琛ュ厖)
+ int AGENT_TEMPLATE_NOT_FOUND = 10183; // 榛樿鏅鸿兘浣撴湭鎵惧埌
+
+ // 鐭ヨ瘑搴撻�傞厤鍣ㄧ浉鍏抽敊璇爜
+ int RAG_ADAPTER_TYPE_NOT_SUPPORTED = 10184; // 涓嶆敮鎸佺殑閫傞厤鍣ㄧ被鍨�
+ int RAG_CONFIG_VALIDATION_FAILED = 10185; // RAG閰嶇疆楠岃瘉澶辫触
+ int RAG_ADAPTER_CREATION_FAILED = 10186; // 閫傞厤鍣ㄥ垱寤哄け璐�
+ int RAG_ADAPTER_INIT_FAILED = 10187; // 閫傞厤鍣ㄥ垵濮嬪寲澶辫触
+ int RAG_ADAPTER_CONNECTION_FAILED = 10188; // 閫傞厤鍣ㄨ繛鎺ユ祴璇曞け璐�
+ int RAG_ADAPTER_OPERATION_FAILED = 10189; // 閫傞厤鍣ㄦ搷浣滃け璐�
+ int RAG_ADAPTER_NOT_FOUND = 10190; // 閫傞厤鍣ㄦ湭鎵惧埌
+ int RAG_ADAPTER_CACHE_ERROR = 10191; // 閫傞厤鍣ㄧ紦瀛橀敊璇�
+ int RAG_ADAPTER_TYPE_NOT_FOUND = 10192; // 閫傞厤鍣ㄧ被鍨嬫湭鎵惧埌
+
+ // 璁惧宸ュ叿鐩稿叧閿欒鐮�
+ int DEVICE_ID_NOT_NULL = 10193; // 璁惧ID涓嶈兘涓虹┖
+ int DEVICE_NOT_EXIST = 10194; // 璁惧涓嶅瓨鍦�
+ int OTA_UPLOAD_COUNT_EXCEED = 10195; // OTA涓婁紶娆℃暟瓒呰繃闄愬埗
+
+ // 鏅鸿兘浣撴爣绛剧浉鍏抽敊璇爜
+ int AGENT_TAG_NAME_DUPLICATE = 10196; // 鏍囩鍚嶇О宸插瓨鍦�
+ int AGENT_TAG_NAME_EMPTY = 10197; // 鏍囩鍚嶇О涓嶈兘涓虹┖
+ int AGENT_TAG_NOT_EXIST = 10198; // 鏍囩涓嶅瓨鍦�
+
+ int RAG_DOCUMENT_PARSING_DELETE_ERROR = 10199; // 鏂囨。瑙f瀽涓紝绂佹鍒犻櫎
+
+ // 鏅鸿兘浣揗CP鐩稿叧閿欒鐮�
+ int MCP_ACCESS_POINT_ADDRESS_NO_PERMISSION = 10200; // 娌℃湁鏉冮檺鏌ョ湅璇ユ櫤鑳戒綋鐨凪CP鎺ュ叆鐐瑰湴鍧�
+ int MCP_ACCESS_POINT_ADDRESS_NOT_CONFIGURED = 10201; // 璇疯仈绯荤鐞嗗憳杩涘叆鍙傛暟绠$悊閰嶇疆mcp鎺ュ叆鐐瑰湴鍧�
+ int MCP_ACCESS_POINT_TOOLS_LIST_NO_PERMISSION = 10202; // 娌℃湁鏉冮檺鏌ョ湅璇ユ櫤鑳戒綋鐨凪CP宸ュ叿鍒楄〃
+
+ // 鏇挎崲璇嶇浉鍏抽敊璇爜
+ int CORRECT_WORD_FILE_NAME_EXISTS = 10203; // 鏂囦欢鍚嶅凡瀛樺湪
+ int FILE_SIZE_OVER_LIMIT = 10204; // 鏂囦欢澶у皬瓒呰繃闄愬埗
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/exception/RenException.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/exception/RenException.java
new file mode 100644
index 0000000..4ac47b8
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/exception/RenException.java
@@ -0,0 +1,67 @@
+package xiaozhi.common.exception;
+
+import xiaozhi.common.utils.MessageUtils;
+
+/**
+ * 鑷畾涔夊紓甯�
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public class RenException extends RuntimeException {
+
+ private int code;
+ private String msg;
+
+ public RenException(int code) {
+ super(MessageUtils.getMessage(code));
+ this.code = code;
+ this.msg = MessageUtils.getMessage(code);
+ }
+
+ public RenException(int code, String... params) {
+ super(MessageUtils.getMessage(code, params));
+ this.code = code;
+ this.msg = MessageUtils.getMessage(code, params);
+ }
+
+ public RenException(int code, Throwable e) {
+ super(MessageUtils.getMessage(code), e);
+ this.code = code;
+ this.msg = MessageUtils.getMessage(code);
+ }
+
+ public RenException(int code, Throwable e, String... params) {
+ super(MessageUtils.getMessage(code, params), e);
+ this.code = code;
+ this.msg = MessageUtils.getMessage(code, params);
+ }
+
+ public RenException(String msg) {
+ super(msg);
+ this.code = ErrorCode.INTERNAL_SERVER_ERROR;
+ this.msg = msg;
+ }
+
+ public RenException(String msg, Throwable e) {
+ super(msg, e);
+ this.code = ErrorCode.INTERNAL_SERVER_ERROR;
+ this.msg = msg;
+ }
+
+ public String getMsg() {
+ return msg;
+ }
+
+ public void setMsg(String msg) {
+ this.msg = msg;
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ public void setCode(int code) {
+ this.code = code;
+ }
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/exception/RenExceptionHandler.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/exception/RenExceptionHandler.java
new file mode 100644
index 0000000..e016bc0
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/exception/RenExceptionHandler.java
@@ -0,0 +1,85 @@
+package xiaozhi.common.exception;
+
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.shiro.authz.UnauthorizedException;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.validation.ObjectError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.servlet.resource.NoResourceFoundException;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.common.utils.MessageUtils;
+import xiaozhi.common.utils.Result;
+
+/**
+ * 寮傚父澶勭悊鍣�
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Slf4j
+@AllArgsConstructor
+@RestControllerAdvice
+public class RenExceptionHandler {
+
+ /**
+ * 澶勭悊鑷畾涔夊紓甯�
+ */
+ @ExceptionHandler(RenException.class)
+ public Result<Void> handleRenException(RenException ex) {
+ Result<Void> result = new Result<>();
+ result.error(ex.getCode(), ex.getMsg());
+
+ return result;
+ }
+
+ @ExceptionHandler(DuplicateKeyException.class)
+ public Result<Void> handleDuplicateKeyException(DuplicateKeyException ex) {
+ Result<Void> result = new Result<>();
+ result.error(ErrorCode.DB_RECORD_EXISTS);
+
+ return result;
+ }
+
+ @ExceptionHandler(UnauthorizedException.class)
+ public Result<Void> handleUnauthorizedException(UnauthorizedException ex) {
+ Result<Void> result = new Result<>();
+ result.error(ErrorCode.FORBIDDEN);
+
+ return result;
+ }
+
+ @ExceptionHandler(Exception.class)
+ public Result<Void> handleException(Exception ex) {
+ log.error(ex.getMessage(), ex);
+
+ return new Result<Void>().error();
+ }
+
+ @ExceptionHandler(NoResourceFoundException.class)
+ public Result<Void> handleNoResourceFoundException(NoResourceFoundException ex) {
+ log.warn("Resource not found: {}", ex.getMessage());
+ return new Result<Void>().error(404, MessageUtils.getMessage(ErrorCode.RESOURCE_NOT_FOUND));
+ }
+
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ public Result<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
+ List<ObjectError> allErrors = ex.getBindingResult().getAllErrors();
+ String errorMsg = allErrors.stream()
+ .filter(Objects::nonNull)
+ .map(err -> {
+ String msg = err.getDefaultMessage();
+ return (msg != null && !msg.trim().isEmpty()) ? msg : null;
+ })
+ .filter(Objects::nonNull)
+ .findFirst()
+ .orElse(MessageUtils.getMessage(ErrorCode.PARAM_VALUE_NULL));
+
+ return new Result<Void>().error(ErrorCode.PARAM_VALUE_NULL, errorMsg);
+ }
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/handler/FieldMetaObjectHandler.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/handler/FieldMetaObjectHandler.java
new file mode 100644
index 0000000..95add2f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/handler/FieldMetaObjectHandler.java
@@ -0,0 +1,74 @@
+package xiaozhi.common.handler;
+
+import java.util.Date;
+
+import org.apache.ibatis.reflection.MetaObject;
+import org.springframework.stereotype.Component;
+
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.user.UserDetail;
+import xiaozhi.modules.security.user.SecurityUser;
+
+/**
+ * 鍏叡瀛楁锛岃嚜鍔ㄥ~鍏呭��
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Component
+public class FieldMetaObjectHandler implements MetaObjectHandler {
+ private final static String CREATE_DATE = "createDate";
+ private final static String CREATOR = "creator";
+ private final static String UPDATE_DATE = "updateDate";
+ private final static String UPDATER = "updater";
+
+ private final static String DATA_OPERATION = "dataOperation";
+
+ @Override
+ public void insertFill(MetaObject metaObject) {
+ UserDetail user = SecurityUser.getUser();
+ Date date = new Date();
+
+ // 鍒涘缓鑰�
+ strictInsertFill(metaObject, CREATOR, Long.class, user.getId());
+ // 鍒涘缓鏃堕棿 - 鏀寔createDate鍜宑reatedAt涓ょ瀛楁鍚�
+ if (metaObject.hasSetter(CREATE_DATE)) {
+ strictInsertFill(metaObject, CREATE_DATE, Date.class, date);
+ }
+ if (metaObject.hasSetter("createdAt")) {
+ strictInsertFill(metaObject, "createdAt", Date.class, date);
+ }
+
+ // 鏇存柊鑰�
+ strictInsertFill(metaObject, UPDATER, Long.class, user.getId());
+ // 鏇存柊鏃堕棿 - 鏀寔updateDate鍜寀pdatedAt涓ょ瀛楁鍚�
+ if (metaObject.hasSetter(UPDATE_DATE)) {
+ strictInsertFill(metaObject, UPDATE_DATE, Date.class, date);
+ }
+ if (metaObject.hasSetter("updatedAt")) {
+ strictInsertFill(metaObject, "updatedAt", Date.class, date);
+ }
+
+ // 鏁版嵁鏍囪瘑
+ strictInsertFill(metaObject, DATA_OPERATION, String.class, Constant.DataOperation.INSERT.getValue());
+ }
+
+ @Override
+ public void updateFill(MetaObject metaObject) {
+ Date date = new Date();
+
+ // 鏇存柊鑰�
+ strictUpdateFill(metaObject, UPDATER, Long.class, SecurityUser.getUserId());
+ // 鏇存柊鏃堕棿 - 鏀寔updateDate鍜寀pdatedAt涓ょ瀛楁鍚�
+ if (metaObject.hasSetter(UPDATE_DATE)) {
+ strictUpdateFill(metaObject, UPDATE_DATE, Date.class, date);
+ }
+ if (metaObject.hasSetter("updatedAt")) {
+ strictUpdateFill(metaObject, "updatedAt", Date.class, date);
+ }
+
+ // 鏁版嵁鏍囪瘑
+ strictInsertFill(metaObject, DATA_OPERATION, String.class, Constant.DataOperation.UPDATE.getValue());
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/interceptor/DataFilterInterceptor.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/interceptor/DataFilterInterceptor.java
new file mode 100644
index 0000000..c64d774
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/interceptor/DataFilterInterceptor.java
@@ -0,0 +1,85 @@
+package xiaozhi.common.interceptor;
+
+import java.util.Map;
+
+import org.apache.ibatis.executor.Executor;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.session.ResultHandler;
+import org.apache.ibatis.session.RowBounds;
+
+import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
+import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
+
+import cn.hutool.core.util.StrUtil;
+import net.sf.jsqlparser.JSQLParserException;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.StringValue;
+import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
+import net.sf.jsqlparser.parser.CCJSqlParserUtil;
+import net.sf.jsqlparser.statement.select.PlainSelect;
+import net.sf.jsqlparser.statement.select.Select;
+
+/**
+ * 鏁版嵁杩囨护
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public class DataFilterInterceptor implements InnerInterceptor {
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds,
+ ResultHandler resultHandler, BoundSql boundSql) {
+ DataScope scope = getDataScope(parameter);
+ // 涓嶈繘琛屾暟鎹繃婊�
+ if (scope == null || StrUtil.isBlank(scope.getSqlFilter())) {
+ return;
+ }
+
+ // 鎷兼帴鏂癝QL
+ String buildSql = getSelect(boundSql.getSql(), scope);
+
+ // 閲嶅啓SQL
+ PluginUtils.mpBoundSql(boundSql).sql(buildSql);
+ }
+
+ private DataScope getDataScope(Object parameter) {
+ if (parameter == null) {
+ return null;
+ }
+
+ // 鍒ゆ柇鍙傛暟閲屾槸鍚︽湁DataScope瀵硅薄
+ if (parameter instanceof Map) {
+ Map<?, ?> parameterMap = (Map<?, ?>) parameter;
+ for (Map.Entry<?, ?> entry : parameterMap.entrySet()) {
+ if (entry.getValue() != null && entry.getValue() instanceof DataScope) {
+ return (DataScope) entry.getValue();
+ }
+ }
+ } else if (parameter instanceof DataScope) {
+ return (DataScope) parameter;
+ }
+
+ return null;
+ }
+
+ private String getSelect(String buildSql, DataScope scope) {
+ try {
+ Select select = (Select) CCJSqlParserUtil.parse(buildSql);
+ PlainSelect plainSelect = select.getPlainSelect();
+
+ Expression expression = plainSelect.getWhere();
+ if (expression == null) {
+ plainSelect.setWhere(new StringValue(scope.getSqlFilter()));
+ } else {
+ AndExpression andExpression = new AndExpression(expression, new StringValue(scope.getSqlFilter()));
+ plainSelect.setWhere(andExpression);
+ }
+
+ return select.toString().replaceAll("'", "");
+ } catch (JSQLParserException e) {
+ return buildSql;
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/interceptor/DataScope.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/interceptor/DataScope.java
new file mode 100644
index 0000000..b59de67
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/interceptor/DataScope.java
@@ -0,0 +1,27 @@
+package xiaozhi.common.interceptor;
+
+/**
+ * 鏁版嵁鑼冨洿
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public class DataScope {
+ private String sqlFilter;
+
+ public DataScope(String sqlFilter) {
+ this.sqlFilter = sqlFilter;
+ }
+
+ public String getSqlFilter() {
+ return sqlFilter;
+ }
+
+ public void setSqlFilter(String sqlFilter) {
+ this.sqlFilter = sqlFilter;
+ }
+
+ @Override
+ public String toString() {
+ return this.sqlFilter;
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/page/PageData.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/page/PageData.java
new file mode 100644
index 0000000..83e93c1
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/page/PageData.java
@@ -0,0 +1,33 @@
+package xiaozhi.common.page;
+
+import java.io.Serializable;
+import java.util.List;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 鍒嗛〉宸ュ叿绫�
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Data
+@Schema(description = "鍒嗛〉鏁版嵁")
+public class PageData<T> implements Serializable {
+ @Schema(description = "鎬昏褰曟暟")
+ private int total;
+
+ @Schema(description = "鍒楄〃鏁版嵁")
+ private List<T> list;
+
+ /**
+ * 鍒嗛〉
+ *
+ * @param list 鍒楄〃鏁版嵁
+ * @param total 鎬昏褰曟暟
+ */
+ public PageData(List<T> list, long total) {
+ this.list = list;
+ this.total = (int) total;
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/page/TokenDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/page/TokenDTO.java
new file mode 100644
index 0000000..cce1a6d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/page/TokenDTO.java
@@ -0,0 +1,25 @@
+package xiaozhi.common.page;
+
+import java.io.Serializable;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 浠ょ墝淇℃伅
+ *
+ * @author Jack
+ */
+@Data
+@Schema(description = "浠ょ墝淇℃伅")
+public class TokenDTO implements Serializable {
+
+ @Schema(description = "瀵嗙爜")
+ private String token;
+
+ @Schema(description = "杩囨湡鏃堕棿")
+ private int expire;
+
+ @Schema(description = "瀹㈡埛绔寚绾�")
+ private String clientHash;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/redis/RedisConfig.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/redis/RedisConfig.java
new file mode 100644
index 0000000..703a849
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/redis/RedisConfig.java
@@ -0,0 +1,33 @@
+package xiaozhi.common.redis;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+import jakarta.annotation.Resource;
+
+/**
+ * Redis閰嶇疆
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Configuration
+public class RedisConfig {
+ @Resource
+ private RedisConnectionFactory factory;
+
+ @Bean
+ public RedisTemplate<String, Object> redisTemplate() {
+ RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
+ redisTemplate.setKeySerializer(new StringRedisSerializer());
+ redisTemplate.setValueSerializer(RedisSerializer.json());
+ redisTemplate.setHashKeySerializer(new StringRedisSerializer());
+ redisTemplate.setHashValueSerializer(RedisSerializer.json());
+ redisTemplate.setConnectionFactory(factory);
+
+ return redisTemplate;
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/redis/RedisKeys.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/redis/RedisKeys.java
new file mode 100644
index 0000000..c37ba61
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/redis/RedisKeys.java
@@ -0,0 +1,198 @@
+package xiaozhi.common.redis;
+
+/**
+ * Redis Key 甯搁噺绫�
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public class RedisKeys {
+ /**
+ * 绯荤粺鍙傛暟Key
+ */
+ public static String getSysParamsKey() {
+ return "sys:params";
+ }
+
+ /**
+ * 楠岃瘉鐮並ey
+ */
+ public static String getCaptchaKey(String uuid) {
+ return "sys:captcha:" + uuid;
+ }
+
+ /**
+ * 鏈敞鍐岃澶囬獙璇佺爜Key
+ */
+ public static String getDeviceCaptchaKey(String captcha) {
+ return "sys:device:captcha:" + captcha;
+ }
+
+ /**
+ * 鐢ㄦ埛id鐨凨ey
+ */
+ public static String getUserIdKey(Long userid) {
+ return "sys:username:id:" + userid;
+ }
+
+ /**
+ * 妯″瀷鍚嶇О鐨凨ey
+ */
+ public static String getModelNameById(String id) {
+ return "model:name:" + id;
+ }
+
+ /**
+ * 妯″瀷閰嶇疆鐨凨ey
+ */
+ public static String getModelConfigById(String id) {
+ return "model:data:" + id;
+ }
+
+ /**
+ * 鑾峰彇闊宠壊鍚嶇О缂撳瓨key
+ */
+ public static String getTimbreNameById(String id) {
+ return "timbre:name:" + id;
+ }
+
+ /**
+ * 鑾峰彇璁惧鏁伴噺缂撳瓨key
+ */
+ public static String getAgentDeviceCountById(String id) {
+ return "agent:device:count:" + id;
+ }
+
+ /**
+ * 鑾峰彇鏅鸿兘浣撴渶鍚庤繛鎺ユ椂闂寸紦瀛榢ey
+ */
+ public static String getAgentDeviceLastConnectedAtById(String id) {
+ return "agent:device:lastConnected:" + id;
+ }
+
+ /**
+ * 鑾峰彇绯荤粺閰嶇疆缂撳瓨key
+ */
+ public static String getServerConfigKey() {
+ return "server:config";
+ }
+
+ /**
+ * 鑾峰彇闊宠壊璇︽儏缂撳瓨key
+ */
+ public static String getTimbreDetailsKey(String id) {
+ return "timbre:details:" + id;
+ }
+
+ /**
+ * 鑾峰彇鐗堟湰鍙稫ey
+ */
+ public static String getVersionKey() {
+ return "sys:version";
+ }
+
+ /**
+ * OTA鍥轰欢ID鐨凨ey
+ */
+ public static String getOtaIdKey(String uuid) {
+ return "ota:id:" + uuid;
+ }
+
+ /**
+ * OTA鍥轰欢涓嬭浇娆℃暟鐨凨ey
+ */
+ public static String getOtaDownloadCountKey(String uuid) {
+ return "ota:download:count:" + uuid;
+ }
+
+ /**
+ * 鑾峰彇瀛楀吀鏁版嵁鐨勭紦瀛榢ey
+ */
+ public static String getDictDataByTypeKey(String dictType) {
+ return "sys:dict:data:" + dictType;
+ }
+
+ /**
+ * 鑾峰彇鏅鸿兘浣撻煶棰慖D鐨勭紦瀛榢ey
+ */
+ public static String getAgentAudioIdKey(String uuid) {
+ return "agent:audio:id:" + uuid;
+ }
+
+ /**
+ * 鑾峰彇鐭俊楠岃瘉鐮佺殑缂撳瓨key
+ */
+ public static String getSMSValidateCodeKey(String phone) {
+ return "sms:Validate:Code:" + phone;
+ }
+
+ /**
+ * 鑾峰彇鐭俊楠岃瘉鐮佹渶鍚庡彂閫佹椂闂寸殑缂撳瓨key
+ */
+ public static String getSMSLastSendTimeKey(String phone) {
+ return "sms:Validate:Code:" + phone + ":last_send_time";
+ }
+
+ /**
+ * 鑾峰彇鐭俊楠岃瘉鐮佷粖鏃ュ彂閫佹鏁扮殑缂撳瓨key
+ */
+ public static String getSMSTodayCountKey(String phone) {
+ return "sms:Validate:Code:" + phone + ":today_count";
+ }
+
+ /**
+ * 鑱婂ぉ璁板綍UUID鏄犲皠鐨凨ey
+ */
+ public static String getChatHistoryKey(String uuid) {
+ return "agent:chat:history:" + uuid;
+ }
+
+ /**
+ * 鑾峰彇闊宠壊鍏嬮殕闊抽ID鐨勭紦瀛榢ey
+ */
+ public static String getVoiceCloneAudioIdKey(String uuid) {
+ return "voiceClone:audio:id:" + uuid;
+ }
+
+ /**
+ * 鑾峰彇鐭ヨ瘑搴撶紦瀛榢ey
+ */
+ public static String getKnowledgeBaseCacheKey(String datasetId) {
+ return "knowledge:base:" + datasetId;
+ }
+
+ /**
+ * 鑾峰彇涓存椂娉ㄥ唽璁惧鏍囪key
+ */
+ public static String getTmpRegisterMacKey(String deviceId) {
+ return "tmp_register_mac:" + deviceId;
+ }
+
+ /**
+ * OTA缁戝畾璁惧
+ */
+ public static String getOtaActivationCode(String activationCode) {
+ return "ota:activation:code:" + activationCode;
+ }
+
+ /**
+ * OTA鑾峰彇璁惧mac鐩稿叧淇℃伅
+ */
+ public static String getOtaDeviceActivationInfo(String deviceId) {
+ return "ota:activation:data:" + deviceId;
+ }
+
+ /**
+ * OTA涓婁紶娆℃暟
+ */
+ public static String getOtaUploadCountKey(Long username) {
+ return "ota:upload:count:" + username;
+ }
+
+ /**
+ * 璁惧閫氳褰曠紦瀛楰ey
+ */
+ public static String getAddressBookKey() {
+ return "device:address_book:all";
+ }
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/redis/RedisUtils.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/redis/RedisUtils.java
new file mode 100644
index 0000000..c81677e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/redis/RedisUtils.java
@@ -0,0 +1,195 @@
+package xiaozhi.common.redis;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.HashOperations;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+import org.springframework.stereotype.Component;
+
+import jakarta.annotation.Resource;
+import xiaozhi.common.utils.ResourcesUtils;
+
+/**
+ * Redis宸ュ叿绫�
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Component
+public class RedisUtils {
+ @Resource
+ private RedisTemplate<String, Object> redisTemplate;
+
+ @Autowired
+ private ResourcesUtils resourceUtils;
+
+ /**
+ * 榛樿杩囨湡鏃堕暱涓�24灏忔椂锛屽崟浣嶏細绉�
+ */
+ public final static long DEFAULT_EXPIRE = 60 * 60 * 24L;
+ /**
+ * 杩囨湡鏃堕暱涓�1灏忔椂锛屽崟浣嶏細绉�
+ */
+ public final static long HOUR_ONE_EXPIRE = (long) 60 * 60;
+ /**
+ * 杩囨湡鏃堕暱涓�6灏忔椂锛屽崟浣嶏細绉�
+ */
+ public final static long HOUR_SIX_EXPIRE = 60 * 60 * 6L;
+ /**
+ * 涓嶈缃繃鏈熸椂闀�
+ */
+ public final static long NOT_EXPIRE = -1L;
+
+ public Long increment(String key, long expire) {
+ Long increment = redisTemplate.opsForValue().increment(key, 1L);
+ if (expire != NOT_EXPIRE) {
+ expire(key, expire);
+ }
+ return increment;
+ }
+
+ public Long increment(String key) {
+ return redisTemplate.opsForValue().increment(key, 1L);
+ }
+
+ public Long decrement(String key) {
+ return redisTemplate.opsForValue().decrement(key, 1L);
+ }
+
+
+
+ public void set(String key, Object value, long expire) {
+ redisTemplate.opsForValue().set(key, value);
+ if (expire != NOT_EXPIRE) {
+ expire(key, expire);
+ }
+ }
+
+ public void set(String key, Object value) {
+ set(key, value, DEFAULT_EXPIRE);
+ }
+
+ public Object get(String key, long expire) {
+ Object value = redisTemplate.opsForValue().get(key);
+ if (expire != NOT_EXPIRE) {
+ expire(key, expire);
+ }
+ return value;
+ }
+
+ public Object get(String key) {
+ return get(key, NOT_EXPIRE);
+ }
+
+ public void delete(String key) {
+ redisTemplate.delete(key);
+ }
+
+ public void delete(Collection<String> keys) {
+ redisTemplate.delete(keys);
+ }
+
+ public Object hGet(String key, String field) {
+ return redisTemplate.opsForHash().get(key, field);
+ }
+
+ public Map<String, Object> hGetAll(String key) {
+ HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();
+ return hashOperations.entries(key);
+ }
+
+ public void hMSet(String key, Map<String, Object> map) {
+ hMSet(key, map, DEFAULT_EXPIRE);
+ }
+
+ public void hMSet(String key, Map<String, Object> map, long expire) {
+ redisTemplate.opsForHash().putAll(key, map);
+
+ if (expire != NOT_EXPIRE) {
+ expire(key, expire);
+ }
+ }
+
+ public void hSet(String key, String field, Object value) {
+ hSet(key, field, value, DEFAULT_EXPIRE);
+ }
+
+ public void hSet(String key, String field, Object value, long expire) {
+ redisTemplate.opsForHash().put(key, field, value);
+
+ if (expire != NOT_EXPIRE) {
+ expire(key, expire);
+ }
+ }
+
+ public void expire(String key, long expire) {
+ redisTemplate.expire(key, expire, TimeUnit.SECONDS);
+ }
+
+ public void hDel(String key, Object... fields) {
+ redisTemplate.opsForHash().delete(key, fields);
+ }
+
+ public void leftPush(String key, Object value) {
+ leftPush(key, value, DEFAULT_EXPIRE);
+ }
+
+ public void leftPush(String key, Object value, long expire) {
+ redisTemplate.opsForList().leftPush(key, value);
+
+ if (expire != NOT_EXPIRE) {
+ expire(key, expire);
+ }
+ }
+
+ public Object rightPop(String key) {
+ return redisTemplate.opsForList().rightPop(key);
+ }
+
+
+ /**
+ * 娓呯┖鎵�鏈� Redis 鏁版嵁搴撲腑鐨勬墍鏈夐敭
+ */
+ public void emptyAll() {
+ // Lua 鑴氭湰 FLUSHALL鏄痳edis娓呯┖鎵�鏈夊簱鐨勫懡浠�
+ String luaScript =resourceUtils.loadString("lua/emptyAll.lua");
+
+ // 鍒涘缓 DefaultRedisScript 瀵硅薄
+ DefaultRedisScript<Void> redisScript = new DefaultRedisScript<>();
+ redisScript.setScriptText(luaScript); // 璁剧疆 Lua 鑴氭湰鍐呭
+ redisScript.setResultType(Void.class); // 璁剧疆杩斿洖鍊肩被鍨�
+
+ // 鎵ц Lua 鑴氭湰
+ List<String> keys = Collections.emptyList(); // 濡傛灉鑴氭湰涓嶄緷璧� key锛屽彲浠ヤ紶鍏ョ┖鍒楄〃
+ redisTemplate.execute(redisScript, keys);
+
+ }
+
+ /**
+ * 鑾峰彇鍦╮edis鎸囧畾key鐨勫�硷紝濡傛灉鍊间负绌猴紝鐫�璁剧疆key鐨勯粯璁ゅ��
+ * @param key redis鐨刱ey
+ * @param defaultValue 榛樿鍊�
+ * @param expiresInSecond 杩囨湡鏃堕棿
+ * @return 杩斿洖key鐨勫��
+ */
+ public String getKeyOrCreate(String key, String defaultValue,Long expiresInSecond) {
+ // Lua 鑴氭湰
+ String luaScript = resourceUtils.loadString("lua/getKeyOrCreate.lua");
+
+ DefaultRedisScript<String> redisScript = new DefaultRedisScript<>();
+ redisScript.setScriptText(luaScript);
+ redisScript.setResultType(String.class);
+
+ // 鎵ц Lua 鑴氭湰
+ List<String> keys = Collections.singletonList(key);
+ return redisTemplate.execute(redisScript, keys, defaultValue,expiresInSecond);
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/service/BaseService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/service/BaseService.java
new file mode 100644
index 0000000..2feaefd
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/service/BaseService.java
@@ -0,0 +1,109 @@
+package xiaozhi.common.service;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+
+/**
+ * 鍩虹鏈嶅姟鎺ュ彛锛屾墍鏈塖ervice鎺ュ彛閮借缁ф壙
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public interface BaseService<T> {
+ Class<T> currentModelClass();
+
+ /**
+ * <p>
+ * 鎻掑叆涓�鏉¤褰曪紙閫夋嫨瀛楁锛岀瓥鐣ユ彃鍏ワ級
+ * </p>
+ *
+ * @param entity 瀹炰綋瀵硅薄
+ */
+ boolean insert(T entity);
+
+ /**
+ * <p>
+ * 鎻掑叆锛堟壒閲忥級锛岃鏂规硶涓嶆敮鎸� Oracle銆丼QL Server
+ * </p>
+ *
+ * @param entityList 瀹炰綋瀵硅薄闆嗗悎
+ */
+ boolean insertBatch(Collection<T> entityList);
+
+ /**
+ * <p>
+ * 鎻掑叆锛堟壒閲忥級锛岃鏂规硶涓嶆敮鎸� Oracle銆丼QL Server
+ * </p>
+ *
+ * @param entityList 瀹炰綋瀵硅薄闆嗗悎
+ * @param batchSize 鎻掑叆鎵规鏁伴噺
+ */
+ boolean insertBatch(Collection<T> entityList, int batchSize);
+
+ /**
+ * <p>
+ * 鏍规嵁 ID 閫夋嫨淇敼
+ * </p>
+ *
+ * @param entity 瀹炰綋瀵硅薄
+ */
+ boolean updateById(T entity);
+
+ /**
+ * <p>
+ * 鏍规嵁 whereEntity 鏉′欢锛屾洿鏂拌褰�
+ * </p>
+ *
+ * @param entity 瀹炰綋瀵硅薄
+ * @param updateWrapper 瀹炰綋瀵硅薄灏佽鎿嶄綔绫�
+ * {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}
+ */
+ boolean update(T entity, Wrapper<T> updateWrapper);
+
+ /**
+ * <p>
+ * 鏍规嵁ID 鎵归噺鏇存柊
+ * </p>
+ *
+ * @param entityList 瀹炰綋瀵硅薄闆嗗悎
+ */
+ boolean updateBatchById(Collection<T> entityList);
+
+ /**
+ * <p>
+ * 鏍规嵁ID 鎵归噺鏇存柊
+ * </p>
+ *
+ * @param entityList 瀹炰綋瀵硅薄闆嗗悎
+ * @param batchSize 鏇存柊鎵规鏁伴噺
+ */
+ boolean updateBatchById(Collection<T> entityList, int batchSize);
+
+ /**
+ * <p>
+ * 鏍规嵁 ID 鏌ヨ
+ * </p>
+ *
+ * @param id 涓婚敭ID
+ */
+ T selectById(Serializable id);
+
+ /**
+ * <p>
+ * 鏍规嵁 ID 鍒犻櫎
+ * </p>
+ *
+ * @param id 涓婚敭ID
+ */
+ boolean deleteById(Serializable id);
+
+ /**
+ * <p>
+ * 鍒犻櫎锛堟牴鎹甀D 鎵归噺鍒犻櫎锛�
+ * </p>
+ *
+ * @param idList 涓婚敭ID鍒楄〃
+ */
+ boolean deleteBatchIds(Collection<? extends Serializable> idList);
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/service/CrudService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/service/CrudService.java
new file mode 100644
index 0000000..51f16bc
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/service/CrudService.java
@@ -0,0 +1,28 @@
+package xiaozhi.common.service;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+import xiaozhi.common.page.PageData;
+
+/**
+ * CRUD鍩虹鏈嶅姟鎺ュ彛
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public interface CrudService<T, D> extends BaseService<T> {
+
+ PageData<D> page(Map<String, Object> params);
+
+ List<D> list(Map<String, Object> params);
+
+ D get(Serializable id);
+
+ void save(D dto);
+
+ void update(D dto);
+
+ void delete(Serializable[] ids);
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/service/impl/BaseServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/service/impl/BaseServiceImpl.java
new file mode 100644
index 0000000..f213a0e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/service/impl/BaseServiceImpl.java
@@ -0,0 +1,232 @@
+package xiaozhi.common.service.impl;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiFunction;
+
+import org.apache.ibatis.binding.MapperMethod;
+import org.apache.ibatis.logging.Log;
+import org.apache.ibatis.logging.LogFactory;
+import org.apache.ibatis.session.SqlSession;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.enums.SqlMethod;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.metadata.OrderItem;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
+
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.service.BaseService;
+import xiaozhi.common.utils.ConvertUtils;
+
+/**
+ * 鍩虹鏈嶅姟绫伙紝鎵�鏈塖ervice閮借缁ф壙
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public abstract class BaseServiceImpl<M extends BaseMapper<T>, T> implements BaseService<T> {
+ @Autowired
+ protected M baseDao;
+ protected Log log = LogFactory.getLog(getClass());
+
+ /**
+ * 鑾峰彇鍒嗛〉瀵硅薄
+ *
+ * @param params 鍒嗛〉鏌ヨ鍙傛暟
+ * @param defaultOrderField 榛樿鎺掑簭瀛楁
+ * @param isAsc 鎺掑簭鏂瑰紡
+ * @see xiaozhi.common.constant.Constant
+ * params.put(Constant.PAGE, "1");
+ * params.put(Constant.LIMIT, "10");
+ * params.put(Constant.ORDER_FIELD, "field"); // 鍗曚釜瀛楁
+ * params.put(Constant.ORDER_FIELD, List.of("field1", "field2")); // 澶氫釜瀛楁
+ * params.put(Constant.ORDER, "asc");
+ */
+ protected IPage<T> getPage(Map<String, Object> params, String defaultOrderField, boolean isAsc) {
+ // 鍒嗛〉鍙傛暟
+ long curPage = 1;
+ long limit = 10;
+
+ if (params.get(Constant.PAGE) != null) {
+ curPage = Long.parseLong((String) params.get(Constant.PAGE));
+ }
+ if (params.get(Constant.LIMIT) != null) {
+ limit = Long.parseLong((String) params.get(Constant.LIMIT));
+ }
+
+ // 鍒嗛〉瀵硅薄
+ Page<T> page = new Page<>(curPage, limit);
+
+ // 鍒嗛〉鍙傛暟
+ params.put(Constant.PAGE, page);
+
+ // 鎺掑簭瀛楁
+ Object orderField = params.get(Constant.ORDER_FIELD);
+ String order = (String) params.get(Constant.ORDER);
+
+ List<String> orderFields = new ArrayList<>();
+
+ // 澶勭悊鎺掑簭瀛楁
+ if (orderField instanceof String) {
+ orderFields.add((String) orderField);
+ } else if (orderField instanceof List<?> fields) {
+ fields.forEach(field -> orderFields.add(String.class.cast(field)));
+ }
+
+ // 鏈夋帓搴忓瓧娈靛垯鎺掑簭
+ if (CollectionUtils.isNotEmpty(orderFields)) {
+ if (StringUtils.isNotBlank(order) && Constant.ASC.equalsIgnoreCase(order)) {
+ return page.addOrder(OrderItem.ascs(orderFields.toArray(new String[0])));
+ } else {
+ return page.addOrder(OrderItem.descs(orderFields.toArray(new String[0])));
+ }
+ }
+
+ // 娌℃湁鎺掑簭瀛楁锛屼娇鐢ㄩ粯璁ゆ帓搴�
+ if (StringUtils.isNotBlank(defaultOrderField)) {
+ if (isAsc) {
+ page.addOrder(OrderItem.asc(defaultOrderField));
+ } else {
+ page.addOrder(OrderItem.desc(defaultOrderField));
+ }
+ }
+
+ return page;
+ }
+
+ protected <D> PageData<D> getPageData(List<?> list, long total, Class<D> target) {
+ List<D> targetList = ConvertUtils.sourceToTarget(list, target);
+
+ return new PageData<>(targetList, total);
+ }
+
+ protected <D> PageData<D> getPageData(IPage<?> page, Class<D> target) {
+ return getPageData(page.getRecords(), page.getTotal(), target);
+ }
+
+ protected void paramsToLike(Map<String, Object> params, String... likes) {
+ for (String like : likes) {
+ String val = (String) params.get(like);
+ if (StringUtils.isNotBlank(val)) {
+ params.put(like, "%" + val + "%");
+ } else {
+ params.put(like, null);
+ }
+ }
+ }
+
+ /**
+ * <p>
+ * 鍒ゆ柇鏁版嵁搴撴搷浣滄槸鍚︽垚鍔�
+ * </p>
+ * <p>
+ * 娉ㄦ剰锛侊紒 璇ユ柟娉曚负 Integer 鍒ゆ柇锛屼笉鍙紶鍏� int 鍩烘湰绫诲瀷
+ * </p>
+ *
+ * @param result 鏁版嵁搴撴搷浣滆繑鍥炲奖鍝嶆潯鏁�
+ * @return boolean
+ */
+ protected static boolean retBool(Integer result) {
+ return SqlHelper.retBool(result);
+ }
+
+ protected Class<?> currentMapperClass() {
+ return ReflectionKit.getSuperClassGenericType(this.getClass(), BaseServiceImpl.class, 0);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Class<T> currentModelClass() {
+ return (Class<T>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseServiceImpl.class, 1);
+ }
+
+ protected String getSqlStatement(SqlMethod sqlMethod) {
+ return SqlHelper.getSqlStatement(this.currentMapperClass(), sqlMethod);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean insert(T entity) {
+ return BaseServiceImpl.retBool(baseDao.insert(entity));
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean insertBatch(Collection<T> entityList) {
+ return insertBatch(entityList, 100);
+ }
+
+ /**
+ * 鎵归噺鎻掑叆
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean insertBatch(Collection<T> entityList, int batchSize) {
+ String sqlStatement = getSqlStatement(SqlMethod.INSERT_ONE);
+ return executeBatch(entityList, batchSize, (sqlSession, entity) -> sqlSession.insert(sqlStatement, entity));
+ }
+
+ /**
+ * 鎵ц鎵归噺鎿嶄綔
+ */
+ @SuppressWarnings("deprecation")
+ protected <E> boolean executeBatch(Collection<E> list, int batchSize, BiFunction<SqlSession, E, Integer> operation) {
+ return SqlHelper.executeBatch(SqlHelper.sqlSessionFactory(this.currentModelClass()), this.log, list, batchSize,
+ operation);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean updateById(T entity) {
+ return BaseServiceImpl.retBool(baseDao.updateById(entity));
+ }
+
+ @Override
+ public boolean update(T entity, Wrapper<T> updateWrapper) {
+ return BaseServiceImpl.retBool(baseDao.update(entity, updateWrapper));
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean updateBatchById(Collection<T> entityList) {
+ return updateBatchById(entityList, 30);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean updateBatchById(Collection<T> entityList, int batchSize) {
+ String sqlStatement = getSqlStatement(SqlMethod.UPDATE_BY_ID);
+ return executeBatch(entityList, batchSize, (sqlSession, entity) -> {
+ MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
+ param.put(Constants.ENTITY, entity);
+ return sqlSession.update(sqlStatement, param);
+ });
+ }
+
+ @Override
+ public T selectById(Serializable id) {
+ return baseDao.selectById(id);
+ }
+
+ @Override
+ public boolean deleteById(Serializable id) {
+ return SqlHelper.retBool(baseDao.deleteById(id));
+ }
+
+ @Override
+ public boolean deleteBatchIds(Collection<? extends Serializable> idList) {
+ return SqlHelper.retBool(baseDao.deleteByIds(idList));
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/service/impl/CrudServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/service/impl/CrudServiceImpl.java
new file mode 100644
index 0000000..e3886e7
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/service/impl/CrudServiceImpl.java
@@ -0,0 +1,76 @@
+package xiaozhi.common.service.impl;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.BeanUtils;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
+
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.service.CrudService;
+import xiaozhi.common.utils.ConvertUtils;
+
+/**
+ * CRUD鍩虹鏈嶅姟绫�
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public abstract class CrudServiceImpl<M extends BaseMapper<T>, T, D> extends BaseServiceImpl<M, T>
+ implements CrudService<T, D> {
+
+ @SuppressWarnings("unchecked")
+ protected Class<D> currentDtoClass() {
+ return (Class<D>) ReflectionKit.getSuperClassGenericType(getClass(), CrudServiceImpl.class, 2);
+ }
+
+ @Override
+ public PageData<D> page(Map<String, Object> params) {
+ IPage<T> page = baseDao.selectPage(
+ getPage(params, null, false),
+ getWrapper(params));
+
+ return getPageData(page, currentDtoClass());
+ }
+
+ @Override
+ public List<D> list(Map<String, Object> params) {
+ List<T> entityList = baseDao.selectList(getWrapper(params));
+
+ return ConvertUtils.sourceToTarget(entityList, currentDtoClass());
+ }
+
+ public abstract QueryWrapper<T> getWrapper(Map<String, Object> params);
+
+ @Override
+ public D get(Serializable id) {
+ T entity = baseDao.selectById(id);
+
+ return ConvertUtils.sourceToTarget(entity, currentDtoClass());
+ }
+
+ @Override
+ public void save(D dto) {
+ T entity = ConvertUtils.sourceToTarget(dto, currentModelClass());
+ insert(entity);
+
+ // copy涓婚敭鍊煎埌dto
+ BeanUtils.copyProperties(entity, dto);
+ }
+
+ @Override
+ public void update(D dto) {
+ T entity = ConvertUtils.sourceToTarget(dto, currentModelClass());
+ updateById(entity);
+ }
+
+ @Override
+ public void delete(Serializable[] ids) {
+ baseDao.deleteByIds(Arrays.asList(ids));
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/user/UserDetail.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/user/UserDetail.java
new file mode 100644
index 0000000..0aa9210
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/user/UserDetail.java
@@ -0,0 +1,19 @@
+package xiaozhi.common.user;
+
+import java.io.Serializable;
+
+import lombok.Data;
+
+/**
+ * 鐧诲綍鐢ㄦ埛淇℃伅
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Data
+public class UserDetail implements Serializable {
+ private Long id;
+ private String username;
+ private Integer superAdmin;
+ private String token;
+ private Integer status;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/AESUtils.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/AESUtils.java
new file mode 100644
index 0000000..660e3cb
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/AESUtils.java
@@ -0,0 +1,78 @@
+package xiaozhi.common.utils;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+
+public class AESUtils {
+
+ private static final String ALGORITHM = "AES";
+ private static final String TRANSFORMATION = "AES/ECB/PKCS5Padding";
+
+ /**
+ * AES鍔犲瘑
+ *
+ * @param key 瀵嗛挜锛�16浣嶃��24浣嶆垨32浣嶏級
+ * @param plainText 寰呭姞瀵嗗瓧绗︿覆
+ * @return 鍔犲瘑鍚庣殑Base64瀛楃涓�
+ */
+ public static String encrypt(String key, String plainText) {
+ try {
+ // 纭繚瀵嗛挜闀垮害涓�16銆�24鎴�32浣�
+ byte[] keyBytes = padKey(key.getBytes(StandardCharsets.UTF_8));
+ SecretKeySpec secretKey = new SecretKeySpec(keyBytes, ALGORITHM);
+
+ Cipher cipher = Cipher.getInstance(TRANSFORMATION);
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+
+ byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
+ return Base64.getEncoder().encodeToString(encryptedBytes);
+ } catch (Exception e) {
+ throw new RuntimeException("AES鍔犲瘑澶辫触", e);
+ }
+ }
+
+ /**
+ * AES瑙e瘑
+ *
+ * @param key 瀵嗛挜锛�16浣嶃��24浣嶆垨32浣嶏級
+ * @param encryptedText 寰呰В瀵嗙殑Base64瀛楃涓�
+ * @return 瑙e瘑鍚庣殑瀛楃涓�
+ */
+ public static String decrypt(String key, String encryptedText) {
+ try {
+ // 纭繚瀵嗛挜闀垮害涓�16銆�24鎴�32浣�
+ byte[] keyBytes = padKey(key.getBytes(StandardCharsets.UTF_8));
+ SecretKeySpec secretKey = new SecretKeySpec(keyBytes, ALGORITHM);
+
+ Cipher cipher = Cipher.getInstance(TRANSFORMATION);
+ cipher.init(Cipher.DECRYPT_MODE, secretKey);
+
+ byte[] encryptedBytes = Base64.getDecoder().decode(encryptedText);
+ byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
+ return new String(decryptedBytes, StandardCharsets.UTF_8);
+ } catch (Exception e) {
+ throw new RuntimeException("AES瑙e瘑澶辫触", e);
+ }
+ }
+
+ /**
+ * 濉厖瀵嗛挜鍒版寚瀹氶暱搴︼紙16銆�24鎴�32浣嶏級
+ *
+ * @param keyBytes 鍘熷瀵嗛挜瀛楄妭鏁扮粍
+ * @return 濉厖鍚庣殑瀵嗛挜瀛楄妭鏁扮粍
+ */
+ private static byte[] padKey(byte[] keyBytes) {
+ int keyLength = keyBytes.length;
+ if (keyLength == 16 || keyLength == 24 || keyLength == 32) {
+ return keyBytes;
+ }
+
+ // 濡傛灉瀵嗛挜闀垮害涓嶈冻锛岀敤0濉厖锛涘鏋滆秴杩囷紝鎴彇鍓�32浣�
+ byte[] paddedKey = new byte[32];
+ System.arraycopy(keyBytes, 0, paddedKey, 0, Math.min(keyLength, 32));
+ return paddedKey;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/ConvertUtils.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/ConvertUtils.java
new file mode 100644
index 0000000..07ade50
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/ConvertUtils.java
@@ -0,0 +1,51 @@
+package xiaozhi.common.utils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.springframework.beans.BeanUtils;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 杞崲宸ュ叿绫�
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Slf4j
+public class ConvertUtils {
+ public static <T> T sourceToTarget(Object source, Class<T> target) {
+ if (source == null) {
+ return null;
+ }
+ T targetObject = null;
+ try {
+ targetObject = target.getDeclaredConstructor().newInstance();
+ BeanUtils.copyProperties(source, targetObject);
+ } catch (Exception e) {
+ log.error("convert error ", e);
+ }
+
+ return targetObject;
+ }
+
+ public static <T> List<T> sourceToTarget(Collection<?> sourceList, Class<T> target) {
+ if (sourceList == null) {
+ return null;
+ }
+
+ List<T> targetList = new ArrayList<>(sourceList.size());
+ try {
+ for (Object source : sourceList) {
+ T targetObject = target.getDeclaredConstructor().newInstance();
+ BeanUtils.copyProperties(source, targetObject);
+ targetList.add(targetObject);
+ }
+ } catch (Exception e) {
+ log.error("convert error ", e);
+ }
+
+ return targetList;
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/DateUtils.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/DateUtils.java
new file mode 100644
index 0000000..800733c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/DateUtils.java
@@ -0,0 +1,116 @@
+package xiaozhi.common.utils;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.temporal.ChronoUnit;
+import java.util.Date;
+
+/**
+ * 鏃ユ湡澶勭悊
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public class DateUtils {
+ /**
+ * 鏃堕棿鏍煎紡(yyyy-MM-dd)
+ */
+ public final static String DATE_PATTERN = "yyyy-MM-dd";
+ /**
+ * 鏃堕棿鏍煎紡(yyyy-MM-dd HH:mm:ss)
+ */
+ public final static String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
+ public final static String DATE_TIME_MILLIS_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS";
+
+
+ /**
+ * 鏃ユ湡鏍煎紡鍖� 鏃ユ湡鏍煎紡涓猴細yyyy-MM-dd
+ *
+ * @param date 鏃ユ湡
+ * @return 杩斿洖yyyy-MM-dd鏍煎紡鏃ユ湡
+ */
+ public static String format(Date date) {
+ return format(date, DATE_PATTERN);
+ }
+
+ /**
+ * 鏃ユ湡鏍煎紡鍖� 鏃ユ湡鏍煎紡涓猴細yyyy-MM-dd
+ *
+ * @param date 鏃ユ湡
+ * @param pattern 鏍煎紡锛屽锛欴ateUtils.DATE_TIME_PATTERN
+ * @return 杩斿洖yyyy-MM-dd鏍煎紡鏃ユ湡
+ */
+ public static String format(Date date, String pattern) {
+ if (date != null) {
+ SimpleDateFormat df = new SimpleDateFormat(pattern);
+ return df.format(date);
+ }
+ return null;
+ }
+
+ /**
+ * 鏃ユ湡瑙f瀽
+ *
+ * @param date 鏃ユ湡
+ * @param pattern 鏍煎紡锛屽锛欴ateUtils.DATE_TIME_PATTERN
+ * @return 杩斿洖Date
+ */
+ public static Date parse(String date, String pattern) {
+ try {
+ return new SimpleDateFormat(pattern).parse(date);
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+
+ public static String getDateTimeNow() {
+ return getDateTimeNow(DATE_TIME_PATTERN);
+ }
+
+ public static String getDateTimeNow(String pattern) {
+ return format(new Date(), pattern);
+ }
+
+ public static String millsToSecond(long mills) {
+ return String.format("%.3f", mills / 1000.0);
+ }
+
+ /**
+ * 鑾峰彇绠�鐭殑鏃堕棿瀛楃涓诧細10绉掑墠杩斿洖鍒氬垰锛屽灏戠鍓嶏紝鍑犲皬鏃跺墠锛岃秴杩囦竴鍛ㄨ繑鍥炲勾鏈堟棩鏃跺垎绉�
+ * @param date
+ * @return
+ */
+ public static String getShortTime(Date date) {
+ if (date == null) {
+ return null;
+ }
+ // 灏� Date 杞崲涓� Instant
+ LocalDateTime localDateTime = date.toInstant()
+ // 鑾峰彇绯荤粺榛樿鏃跺尯
+ .atZone(ZoneId.systemDefault())
+ // 杞崲涓� LocalDateTime
+ .toLocalDateTime();
+ // 褰撳墠鏃堕棿
+ LocalDateTime now = LocalDateTime.now();
+ // 鏃堕棿宸紝鍗曚綅涓虹
+ long secondsBetween = ChronoUnit.SECONDS.between(localDateTime, now);
+
+ if (secondsBetween <= 10) {
+ return "鍒氬垰";
+ } else if (secondsBetween < 60) {
+ return secondsBetween + "绉掑墠";
+ } else if (secondsBetween < 60 * 60) {
+ return secondsBetween / 60 + "鍒嗛挓鍓�";
+ } else if (secondsBetween < 86400) {
+ return secondsBetween / 3600 + "灏忔椂鍓�";
+ } else if (secondsBetween < 604800) {
+ return secondsBetween / 86400 + "澶╁墠";
+ } else {
+ // 瓒呰繃涓�鍛紝鏄剧ず瀹屾暣鏃ユ湡鏃堕棿
+ return format(date,DATE_TIME_PATTERN);
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/HttpContextUtils.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/HttpContextUtils.java
new file mode 100644
index 0000000..0e46acf
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/HttpContextUtils.java
@@ -0,0 +1,110 @@
+package xiaozhi.common.utils;
+
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.HttpHeaders;
+import org.springframework.util.DigestUtils;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import jakarta.servlet.http.HttpServletRequest;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+
+/**
+ * Http
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public class HttpContextUtils {
+
+ public static HttpServletRequest getHttpServletRequest() {
+ RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
+ if (requestAttributes == null) {
+ return null;
+ }
+
+ return ((ServletRequestAttributes) requestAttributes).getRequest();
+ }
+
+ public static String getToken(String authorization) {
+ String token;
+ if (StringUtils.isBlank(authorization) && authorization.contains("Bearer ")) {
+ throw new RenException(ErrorCode.UNAUTHORIZED);
+ }
+ token = authorization.replace("Bearer ", "");
+ if (StringUtils.isBlank(token)) {
+ throw new RenException(ErrorCode.TOKEN_NOT_EMPTY);
+ }
+ return token;
+ }
+
+ public static Map<String, String> getParameterMap(HttpServletRequest request) {
+ Enumeration<String> parameters = request.getParameterNames();
+
+ Map<String, String> params = new HashMap<>();
+ while (parameters.hasMoreElements()) {
+ String parameter = parameters.nextElement();
+ String value = request.getParameter(parameter);
+ if (StringUtils.isNotBlank(value)) {
+ params.put(parameter, value);
+ }
+ }
+
+ return params;
+ }
+
+ public static String getDomain() {
+ HttpServletRequest request = getHttpServletRequest();
+ StringBuffer url = request.getRequestURL();
+ return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
+ }
+
+ public static String getOrigin() {
+ HttpServletRequest request = getHttpServletRequest();
+ return request.getHeader(HttpHeaders.ORIGIN);
+ }
+
+ public static String getLanguage() {
+ // 榛樿璇█
+ String defaultLanguage = "zh-CN";
+ // request
+ HttpServletRequest request = getHttpServletRequest();
+ if (request == null) {
+ return defaultLanguage;
+ }
+
+ // 璇锋眰璇█
+ defaultLanguage = request.getHeader(HttpHeaders.ACCEPT_LANGUAGE);
+
+ return defaultLanguage;
+ }
+
+ /**
+ * 鑾峰彇瀹㈡埛绔殑鍞竴鏍囪瘑
+ *
+ * @return
+ */
+ public static String getClientCode() {
+ HttpServletRequest request = getHttpServletRequest();
+ if (request == null) {
+ return null;
+ }
+ String reqId = request.getHeader("User-Agent").toLowerCase();
+ String ipAddr = IpUtils.getIpAddr(request);
+ String date = DateUtils.format(new Date());
+ reqId = DigestUtils.md5DigestAsHex((ipAddr + date + reqId).getBytes());
+ return reqId;
+ }
+
+ public static String getRequestURI() {
+ HttpServletRequest request = getHttpServletRequest();
+ String url = request.getRequestURI();
+ return url;
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/IpUtils.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/IpUtils.java
new file mode 100644
index 0000000..fef7972
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/IpUtils.java
@@ -0,0 +1,48 @@
+package xiaozhi.common.utils;
+
+import org.apache.commons.lang3.StringUtils;
+
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * IP鍦板潃
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Slf4j
+public class IpUtils {
+ /**
+ * 鑾峰彇IP鍦板潃
+ * <p>
+ * 浣跨敤Nginx绛夊弽鍚戜唬鐞嗚蒋浠讹紝 鍒欎笉鑳介�氳繃request.getRemoteAddr()鑾峰彇IP鍦板潃
+ * 濡傛灉浣跨敤浜嗗绾у弽鍚戜唬鐞嗙殑璇濓紝X-Forwarded-For鐨勫�煎苟涓嶆涓�涓紝鑰屾槸涓�涓睮P鍦板潃锛孹-Forwarded-For涓涓�涓潪unknown鐨勬湁鏁圛P瀛楃涓诧紝鍒欎负鐪熷疄IP鍦板潃
+ */
+ public static String getIpAddr(HttpServletRequest request) {
+ String unknown = "unknown";
+ String ip = null;
+ try {
+ ip = request.getHeader("x-forwarded-for");
+ if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
+ ip = request.getHeader("Proxy-Client-IP");
+ }
+ if (StringUtils.isEmpty(ip) || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
+ ip = request.getHeader("WL-Proxy-Client-IP");
+ }
+ if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
+ ip = request.getHeader("HTTP_CLIENT_IP");
+ }
+ if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
+ ip = request.getHeader("HTTP_X_FORWARDED_FOR");
+ }
+ if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
+ ip = request.getRemoteAddr();
+ }
+ } catch (Exception e) {
+ log.error("IPUtils ERROR ", e);
+ }
+
+ return ip;
+ }
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/JsonRpcTwo.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/JsonRpcTwo.java
new file mode 100644
index 0000000..c522a61
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/JsonRpcTwo.java
@@ -0,0 +1,21 @@
+package xiaozhi.common.utils;
+
+import lombok.Data;
+/**
+ * JSON-RPC2.0 鏍煎紡瑙勮寖瀵硅薄
+ */
+@Data
+public class JsonRpcTwo {
+ private String jsonrpc = "2.0";
+ private String method;
+ private Object params;
+ private Integer id;
+
+ public JsonRpcTwo(String method, Object params, Integer id) {
+ this.method = method;
+ this.params = params;
+ this.id = id;
+ }
+
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/JsonUtils.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/JsonUtils.java
new file mode 100644
index 0000000..dae0498
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/JsonUtils.java
@@ -0,0 +1,131 @@
+package xiaozhi.common.utils;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.StrUtil;
+
+/**
+ * JSON 宸ュ叿绫�
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public class JsonUtils {
+ private static final ObjectMapper objectMapper = new ObjectMapper();
+ private static final TypeReference<Map<String, Object>> STRING_OBJECT_MAP = new TypeReference<>() {
+ };
+ private static final TypeReference<List<Map<String, Object>>> STRING_OBJECT_MAP_LIST = new TypeReference<>() {
+ };
+
+ public static String toJsonString(Object object) {
+ try {
+ return objectMapper.writeValueAsString(object);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static <T> T parseObject(String text, Class<T> clazz) {
+ if (StrUtil.isEmpty(text)) {
+ return null;
+ }
+ try {
+ return objectMapper.readValue(text, clazz);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
+ if (ArrayUtil.isEmpty(bytes)) {
+ return null;
+ }
+ try {
+ return objectMapper.readValue(bytes, clazz);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static <T> T parseObject(String text, TypeReference<T> typeReference) {
+ try {
+ return objectMapper.readValue(text, typeReference);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static <T> List<T> parseArray(String text, Class<T> clazz) {
+ if (StrUtil.isEmpty(text)) {
+ return new ArrayList<>();
+ }
+ try {
+ return objectMapper.readValue(text,
+ objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static Map<String, Object> parseMap(String text) {
+ if (StrUtil.isEmpty(text)) {
+ return null;
+ }
+ return parseObject(text, STRING_OBJECT_MAP);
+ }
+
+ public static List<Map<String, Object>> parseMapList(String text) {
+ if (StrUtil.isEmpty(text)) {
+ return null;
+ }
+ return parseObject(text, STRING_OBJECT_MAP_LIST);
+ }
+
+ public static Map<String, Object> toStringObjectMap(Object value) {
+ if (value == null) {
+ return null;
+ }
+ if (!(value instanceof Map<?, ?> map)) {
+ throw new ClassCastException("Expected Map but got " + value.getClass().getName());
+ }
+
+ Map<String, Object> result = new LinkedHashMap<>(map.size());
+ for (Map.Entry<?, ?> entry : map.entrySet()) {
+ result.put(String.class.cast(entry.getKey()), entry.getValue());
+ }
+ return result;
+ }
+
+ public static List<Map<String, Object>> toStringObjectMapList(Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ List<?> list = List.class.cast(value);
+ List<Map<String, Object>> result = new ArrayList<>(list.size());
+ for (Object item : list) {
+ result.add(toStringObjectMap(item));
+ }
+ return result;
+ }
+
+ public static <T> List<T> toList(Object value, Class<T> elementType) {
+ if (value == null) {
+ return null;
+ }
+
+ List<?> list = List.class.cast(value);
+ List<T> result = new ArrayList<>(list.size());
+ for (Object item : list) {
+ result.add(elementType.cast(item));
+ }
+ return result;
+ }
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/MessageUtils.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/MessageUtils.java
new file mode 100644
index 0000000..abcc2cb
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/MessageUtils.java
@@ -0,0 +1,25 @@
+package xiaozhi.common.utils;
+
+import org.springframework.context.MessageSource;
+import org.springframework.context.i18n.LocaleContextHolder;
+
+/**
+ * 鍥介檯鍖�
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public class MessageUtils {
+ private static MessageSource messageSource;
+
+ public static String getMessage(int code) {
+ return getMessage(code, new String[0]);
+ }
+
+ public static String getMessage(int code, String... params) {
+ if (messageSource == null) {
+ // 寤惰繜鍒濆鍖栵紝纭繚Spring涓婁笅鏂囧凡瀹屽叏鍒濆鍖�
+ messageSource = (MessageSource) SpringContextUtils.getBean("messageSource");
+ }
+ return messageSource.getMessage(code + "", params, LocaleContextHolder.getLocale());
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/PropertiesUtils.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/PropertiesUtils.java
new file mode 100644
index 0000000..1064299
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/PropertiesUtils.java
@@ -0,0 +1,11 @@
+package xiaozhi.common.utils;
+
+import org.springframework.stereotype.Component;
+
+/**
+ * 鑾峰彇閰嶇疆鏂囦欢淇℃伅
+ * Copyright xin-nan.com
+ */
+@Component
+public class PropertiesUtils {
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/ResourcesUtils.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/ResourcesUtils.java
new file mode 100644
index 0000000..cf2d0bc
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/ResourcesUtils.java
@@ -0,0 +1,45 @@
+package xiaozhi.common.utils;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.core.io.Resource;
+import org.springframework.stereotype.Component;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.exception.ErrorCode;
+
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+/**
+ * 璧勬簮澶勭悊宸ュ叿
+ */
+@AllArgsConstructor
+@Slf4j
+@Component
+public class ResourcesUtils {
+ private ResourceLoader resourceLoader;
+
+ /**
+ * 璇诲彇璧勬簮锛岃繑鍥炲瓧绗︿覆
+ * @param fileName 璧勬簮璺緞锛歳esources涓嬪紑濮�
+ * @return 瀛楃涓�
+ */
+ public String loadString(String fileName) {
+ Resource resource = resourceLoader.getResource("classpath:" + fileName);
+ StringBuilder luaScriptBuilder = new StringBuilder();
+ try (BufferedReader reader = new BufferedReader(
+ new InputStreamReader(resource.getInputStream()))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ luaScriptBuilder.append(line).append("\n");
+ }
+ } catch (IOException e){
+ log.error("鏂规硶锛歭oadString()璇诲彇璧勬簮澶辫触--{}",e.getMessage());
+ throw new RenException(ErrorCode.RESOURCE_READ_ERROR);
+ }
+ return luaScriptBuilder.toString();
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/Result.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/Result.java
new file mode 100644
index 0000000..9c326af
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/Result.java
@@ -0,0 +1,63 @@
+package xiaozhi.common.utils;
+
+import java.io.Serializable;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import xiaozhi.common.exception.ErrorCode;
+
+/**
+ * 鍝嶅簲鏁版嵁
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Data
+@Schema(description = "鍝嶅簲")
+public class Result<T> implements Serializable {
+
+ /**
+ * 缂栫爜锛�0琛ㄧず鎴愬姛锛屽叾浠栧�艰〃绀哄け璐�
+ */
+ @Schema(description = "缂栫爜锛�0琛ㄧず鎴愬姛锛屽叾浠栧�艰〃绀哄け璐�")
+ private int code = 0;
+ /**
+ * 娑堟伅鍐呭
+ */
+ @Schema(description = "娑堟伅鍐呭")
+ private String msg = "success";
+ /**
+ * 鍝嶅簲鏁版嵁
+ */
+ @Schema(description = "鍝嶅簲鏁版嵁")
+ private T data;
+
+ public Result<T> ok(T data) {
+ this.setData(data);
+ return this;
+ }
+
+ public Result<T> error() {
+ this.code = ErrorCode.INTERNAL_SERVER_ERROR;
+ this.msg = MessageUtils.getMessage(this.code);
+ return this;
+ }
+
+ public Result<T> error(int code) {
+ this.code = code;
+ this.msg = MessageUtils.getMessage(this.code);
+ return this;
+ }
+
+ public Result<T> error(int code, String msg) {
+ this.code = code;
+ this.msg = msg;
+ return this;
+ }
+
+ public Result<T> error(String msg) {
+ this.code = ErrorCode.INTERNAL_SERVER_ERROR;
+ this.msg = msg;
+ return this;
+ }
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/ResultUtils.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/ResultUtils.java
new file mode 100644
index 0000000..3f9fe27
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/ResultUtils.java
@@ -0,0 +1,31 @@
+package xiaozhi.common.utils;
+
+/**
+ * 杩斿洖鍝嶅簲浣撳伐鍏风被
+ */
+public class ResultUtils
+{
+ public static <T> Result<T> success(T data) {
+ return new Result<T>().ok(data);
+ }
+
+ public static <T> Result<T> error() {
+ return new Result<T>().error();
+ }
+
+ public static <T> Result<T> error(String msg) {
+ return new Result<T>().error(msg);
+ }
+
+ public static <T> Result<T> error(int errorCode, String msg) {
+ return new Result<T>().error(errorCode, msg);
+ }
+
+ public static <T> Result<T> error(int errorCode) {
+ return new Result<T>().error(errorCode);
+ }
+
+ public static <T> Result<T> empty() {
+ return new Result<T>();
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/SM2Utils.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/SM2Utils.java
new file mode 100644
index 0000000..cf0ed9c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/SM2Utils.java
@@ -0,0 +1,130 @@
+package xiaozhi.common.utils;
+
+import org.bouncycastle.asn1.gm.GMNamedCurves;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.crypto.engines.SM2Engine;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
+import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.encoders.Hex;
+
+import java.math.BigInteger;
+import java.security.*;
+import java.security.spec.ECGenParameterSpec;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * SM2鍔犲瘑宸ュ叿绫伙紙閲囩敤鍗佸叚杩涘埗鏍煎紡锛屼笌chancheng-archive-service椤圭洰淇濇寔涓�鑷达級
+ */
+public class SM2Utils {
+
+ /**
+ * 鍏挜甯搁噺
+ */
+ public static final String KEY_PUBLIC_KEY = "publicKey";
+ /**
+ * 绉侀挜杩斿洖鍊煎父閲�
+ */
+ public static final String KEY_PRIVATE_KEY = "privateKey";
+
+ static {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+
+ /**
+ * SM2鍔犲瘑绠楁硶
+ *
+ * @param publicKey 鍗佸叚杩涘埗鍏挜
+ * @param data 鏄庢枃鏁版嵁
+ * @return 鍗佸叚杩涘埗瀵嗘枃
+ */
+ public static String encrypt(String publicKey, String data) {
+ try {
+ // 鑾峰彇涓�鏉M2鏇茬嚎鍙傛暟
+ X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
+ // 鏋勯�燛CC绠楁硶鍙傛暟锛屾洸绾挎柟绋嬨�佹き鍦嗘洸绾縂鐐广�佸ぇ鏁存暟N
+ ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
+ //鎻愬彇鍏挜鐐�
+ ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(publicKey));
+ // 鍏挜鍓嶉潰鐨�02鎴栬��03琛ㄧず鏄帇缂╁叕閽ワ紝04琛ㄧず鏈帇缂╁叕閽�, 04鐨勬椂鍊欙紝鍙互鍘绘帀鍓嶉潰鐨�04
+ ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);
+
+ SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
+ // 璁剧疆sm2涓哄姞瀵嗘ā寮�
+ sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));
+
+ byte[] in = data.getBytes(StandardCharsets.UTF_8);
+ byte[] arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
+ return Hex.toHexString(arrayOfBytes);
+ } catch (Exception e) {
+ throw new RuntimeException("SM2鍔犲瘑澶辫触", e);
+ }
+ }
+
+ /**
+ * SM2瑙e瘑绠楁硶
+ *
+ * @param privateKey 鍗佸叚杩涘埗绉侀挜
+ * @param cipherData 鍗佸叚杩涘埗瀵嗘枃鏁版嵁
+ * @return 鏄庢枃
+ */
+ public static String decrypt(String privateKey, String cipherData) {
+ try {
+ // 浣跨敤BC搴撳姞瑙e瘑鏃跺瘑鏂囦互04寮�澶达紝浼犲叆鐨勫瘑鏂囧墠闈㈡病鏈�04鍒欒ˉ涓�
+ if (!cipherData.startsWith("04")) {
+ cipherData = "04" + cipherData;
+ }
+ byte[] cipherDataByte = Hex.decode(cipherData);
+ BigInteger privateKeyD = new BigInteger(privateKey, 16);
+ //鑾峰彇涓�鏉M2鏇茬嚎鍙傛暟
+ X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
+ //鏋勯�燿omain鍙傛暟
+ ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
+ ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);
+
+ SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
+ // 璁剧疆sm2涓鸿В瀵嗘ā寮�
+ sm2Engine.init(false, privateKeyParameters);
+
+ byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
+ return new String(arrayOfBytes, StandardCharsets.UTF_8);
+ } catch (Exception e) {
+ throw new RuntimeException("SM2瑙e瘑澶辫触", e);
+ }
+ }
+
+ /**
+ * 鐢熸垚瀵嗛挜瀵�
+ */
+ public static Map<String, String> createKey() {
+ try {
+ ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
+ // 鑾峰彇涓�涓き鍦嗘洸绾跨被鍨嬬殑瀵嗛挜瀵圭敓鎴愬櫒
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
+ // 浣跨敤SM2鍙傛暟鍒濆鍖栫敓鎴愬櫒
+ kpg.initialize(sm2Spec);
+ // 鑾峰彇瀵嗛挜瀵�
+ KeyPair keyPair = kpg.generateKeyPair();
+ PublicKey publicKey = keyPair.getPublic();
+ BCECPublicKey p = (BCECPublicKey) publicKey;
+ PrivateKey privateKey = keyPair.getPrivate();
+ BCECPrivateKey s = (BCECPrivateKey) privateKey;
+
+ Map<String, String> result = new HashMap<>();
+ result.put(KEY_PUBLIC_KEY, Hex.toHexString(p.getQ().getEncoded(false)));
+ result.put(KEY_PRIVATE_KEY, Hex.toHexString(s.getD().toByteArray()));
+ return result;
+ } catch (Exception e) {
+ throw new RuntimeException("鐢熸垚SM2瀵嗛挜瀵瑰け璐�", e);
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/SensitiveDataUtils.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/SensitiveDataUtils.java
new file mode 100644
index 0000000..0f4aa09
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/SensitiveDataUtils.java
@@ -0,0 +1,165 @@
+package xiaozhi.common.utils;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.lang3.StringUtils;
+
+import cn.hutool.json.JSONObject;
+
+/**
+ * 鏁忔劅鏁版嵁澶勭悊宸ュ叿绫�
+ */
+public class SensitiveDataUtils {
+
+ // 鏁忔劅瀛楁鍒楄〃
+ private static final Set<String> SENSITIVE_FIELDS = new HashSet<>(Arrays.asList(
+ "api_key", "personal_access_token", "access_token", "token",
+ "secret", "access_key_secret", "secret_key"));
+
+ /**
+ * 妫�鏌ュ瓧娈垫槸鍚︿负鏁忔劅瀛楁
+ */
+ public static boolean isSensitiveField(String fieldName) {
+ return StringUtils.isNotBlank(fieldName) && SENSITIVE_FIELDS.contains(fieldName.toLowerCase());
+ }
+
+ /**
+ * 闅愯棌瀛楃涓蹭腑闂撮儴鍒�
+ */
+ public static String maskMiddle(String value) {
+ if (StringUtils.isBlank(value) || value.length() == 1) {
+ return value;
+ }
+
+ int length = value.length();
+ if (length <= 8) {
+ // 鐭瓧绗︿覆淇濈暀鍓�2鍚�2
+ return value.substring(0, 2) + "****" + value.substring(length - 2);
+ } else {
+ // 闀垮瓧绗︿覆淇濈暀鍓�4鍚�4
+ int maskLength = length - 8;
+ StringBuilder maskBuilder = new StringBuilder();
+ for (int i = 0; i < maskLength; i++) {
+ maskBuilder.append('*');
+ }
+ return value.substring(0, 4) + maskBuilder.toString() + value.substring(length - 4);
+ }
+ }
+
+ /**
+ * 鍒ゆ柇瀛楃涓叉槸鍚︽槸琚帺鐮佸鐞嗚繃鐨勫��
+ */
+ public static boolean isMaskedValue(String value) {
+ if (StringUtils.isBlank(value)) {
+ return false;
+ }
+ // 鎺╃爜鍊艰嚦灏戝寘鍚�4涓繛缁殑*
+ return value.contains("****");
+ }
+
+ /**
+ * 澶勭悊JSONObject涓殑鏁忔劅瀛楁
+ */
+ public static JSONObject maskSensitiveFields(JSONObject jsonObject) {
+ if (jsonObject == null) {
+ return null;
+ }
+
+ JSONObject result = new JSONObject();
+
+ for (String key : jsonObject.keySet()) {
+ Object value = jsonObject.get(key);
+
+ if (SENSITIVE_FIELDS.contains(key.toLowerCase()) && value instanceof String) {
+ result.set(key, maskMiddle((String) value));
+ } else if (value instanceof JSONObject) {
+ result.set(key, maskSensitiveFields((JSONObject) value));
+ } else {
+ result.set(key, value);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * 姣旇緝涓や釜JSONObject鐨勬晱鎰熷瓧娈垫槸鍚︾浉鍚�
+ * 鐗瑰埆閽堝api_key绛夋晱鎰熷瓧娈佃繘琛屽崟鐙瘮杈�
+ */
+ public static boolean isSensitiveDataEqual(JSONObject original, JSONObject updated) {
+ if (original == null && updated == null) {
+ return true;
+ }
+ if (original == null || updated == null) {
+ return false;
+ }
+
+ // 鎻愬彇骞舵瘮杈冪壒瀹氭晱鎰熷瓧娈�
+ return compareSpecificSensitiveFields(original, updated, "api_key") &&
+ compareSpecificSensitiveFields(original, updated, "personal_access_token") &&
+ compareSpecificSensitiveFields(original, updated, "access_token") &&
+ compareSpecificSensitiveFields(original, updated, "token") &&
+ compareSpecificSensitiveFields(original, updated, "secret") &&
+ compareSpecificSensitiveFields(original, updated, "access_key_secret") &&
+ compareSpecificSensitiveFields(original, updated, "secret_key");
+ }
+
+ /**
+ * 姣旇緝涓や釜JSON瀵硅薄涓壒瀹氭晱鎰熷瓧娈垫槸鍚︾浉鍚�
+ * 閬嶅巻鏁翠釜JSON瀵硅薄鏍戯紝鏌ユ壘骞舵瘮杈冩寚瀹氭晱鎰熷瓧娈�
+ */
+ private static boolean compareSpecificSensitiveFields(JSONObject original, JSONObject updated, String fieldName) {
+ // 鎻愬彇鍘熷瀵硅薄涓殑鎸囧畾鏁忔劅瀛楁
+ Map<String, String> originalFields = new HashMap<>();
+ extractSpecificSensitiveField(original, originalFields, fieldName, "");
+
+ // 鎻愬彇鏇存柊瀵硅薄涓殑鎸囧畾鏁忔劅瀛楁
+ Map<String, String> updatedFields = new HashMap<>();
+ extractSpecificSensitiveField(updated, updatedFields, fieldName, "");
+
+ // 濡傛灉瀛楁鏁伴噺涓嶅悓锛岃鏄庢湁澧炲垹
+ if (originalFields.size() != updatedFields.size()) {
+ return false;
+ }
+
+ // 姣旇緝姣忎釜瀛楁鐨勫��
+ for (Map.Entry<String, String> entry : originalFields.entrySet()) {
+ String key = entry.getKey();
+ String originalValue = entry.getValue();
+ String updatedValue = updatedFields.get(key);
+
+ if (updatedValue == null || !updatedValue.equals(originalValue)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * 閫掑綊鎻愬彇JSON瀵硅薄涓寚瀹氬悕绉扮殑鏁忔劅瀛楁
+ */
+ private static void extractSpecificSensitiveField(JSONObject jsonObject, Map<String, String> fieldsMap,
+ String targetFieldName, String parentPath) {
+ if (jsonObject == null) {
+ return;
+ }
+
+ for (String key : jsonObject.keySet()) {
+ String fullPath = parentPath.isEmpty() ? key : parentPath + "." + key;
+ Object value = jsonObject.get(key);
+
+ if (value instanceof JSONObject) {
+ // 閫掑綊澶勭悊宓屽JSON瀵硅薄
+ extractSpecificSensitiveField((JSONObject) value, fieldsMap, targetFieldName, fullPath);
+ } else if (value instanceof String && key.equalsIgnoreCase(targetFieldName)) {
+ // 鎵惧埌鐩爣鏁忔劅瀛楁锛屼繚瀛樺叾璺緞鍜屽��
+ fieldsMap.put(fullPath, (String) value);
+ }
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/Sm2DecryptUtil.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/Sm2DecryptUtil.java
new file mode 100644
index 0000000..332e9c8
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/Sm2DecryptUtil.java
@@ -0,0 +1,63 @@
+package xiaozhi.common.utils;
+
+import org.apache.commons.lang3.StringUtils;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.modules.security.service.CaptchaService;
+import xiaozhi.modules.sys.service.SysParamsService;
+
+/**
+ * SM2瑙e瘑鍜岄獙璇佺爜楠岃瘉宸ュ叿绫�
+ * 灏佽浜嗛噸澶嶇殑SM2瑙e瘑銆侀獙璇佺爜鎻愬彇鍜岄獙璇侀�昏緫
+ */
+public class Sm2DecryptUtil {
+
+ /**
+ * 楠岃瘉鐮侀暱搴�
+ */
+ private static final int CAPTCHA_LENGTH = 5;
+
+ /**
+ * 瑙e瘑SM2鍔犲瘑鍐呭锛屾彁鍙栭獙璇佺爜骞堕獙璇�
+ *
+ * @param encryptedPassword SM2鍔犲瘑鐨勫瘑鐮佸瓧绗︿覆
+ * @param captchaId 楠岃瘉鐮両D
+ * @param captchaService 楠岃瘉鐮佹湇鍔�
+ * @param sysParamsService 绯荤粺鍙傛暟鏈嶅姟
+ * @return 瑙e瘑鍚庣殑瀹為檯瀵嗙爜
+ */
+ public static String decryptAndValidateCaptcha(String encryptedPassword, String captchaId,
+ CaptchaService captchaService, SysParamsService sysParamsService) {
+ // 鑾峰彇SM2绉侀挜
+ String privateKeyStr = sysParamsService.getValue(Constant.SM2_PRIVATE_KEY, true);
+ if (StringUtils.isBlank(privateKeyStr)) {
+ throw new RenException(ErrorCode.SM2_KEY_NOT_CONFIGURED);
+ }
+
+ // 浣跨敤SM2绉侀挜瑙e瘑瀵嗙爜
+ String decryptedContent;
+ try {
+ decryptedContent = SM2Utils.decrypt(privateKeyStr, encryptedPassword);
+ } catch (Exception e) {
+ throw new RenException(ErrorCode.SM2_DECRYPT_ERROR);
+ }
+
+ // 鍒嗙楠岃瘉鐮佸拰瀵嗙爜锛氬墠5浣嶆槸楠岃瘉鐮侊紝鍚庨潰鏄瘑鐮�
+ if (decryptedContent.length() > CAPTCHA_LENGTH) {
+ String embeddedCaptcha = decryptedContent.substring(0, CAPTCHA_LENGTH);
+ String actualPassword = decryptedContent.substring(CAPTCHA_LENGTH);
+
+ boolean embeddedCaptchaValid = captchaService.validate(captchaId, embeddedCaptcha, true);
+ if (!embeddedCaptchaValid) {
+ throw new RenException(ErrorCode.SMS_CAPTCHA_ERROR);
+ }
+
+ return actualPassword;
+ } else if (decryptedContent.length() > 0) {
+ throw new RenException(ErrorCode.SMS_CAPTCHA_ERROR);
+ } else {
+ throw new RenException(ErrorCode.SM2_DECRYPT_ERROR);
+ }
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/SpringContextUtils.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/SpringContextUtils.java
new file mode 100644
index 0000000..52aec0b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/SpringContextUtils.java
@@ -0,0 +1,45 @@
+package xiaozhi.common.utils;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+/**
+ * Spring Context 宸ュ叿绫�
+ */
+@Component
+public class SpringContextUtils implements ApplicationContextAware {
+ public static ApplicationContext applicationContext;
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext)
+ throws BeansException {
+ SpringContextUtils.applicationContext = applicationContext;
+ }
+
+ public static Object getBean(String name) {
+ return applicationContext.getBean(name);
+ }
+
+ public static <T> T getBean(Class<T> requiredType) {
+ return applicationContext.getBean(requiredType);
+ }
+
+ public static <T> T getBean(String name, Class<T> requiredType) {
+ return applicationContext.getBean(name, requiredType);
+ }
+
+ public static boolean containsBean(String name) {
+ return applicationContext.containsBean(name);
+ }
+
+ public static boolean isSingleton(String name) {
+ return applicationContext.isSingleton(name);
+ }
+
+ public static Class<? extends Object> getType(String name) {
+ return applicationContext.getType(name);
+ }
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/TreeNode.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/TreeNode.java
new file mode 100644
index 0000000..5fdb598
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/TreeNode.java
@@ -0,0 +1,30 @@
+package xiaozhi.common.utils;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import lombok.Data;
+
+/**
+ * 鏍戣妭鐐癸紝鎵�鏈夐渶瑕佸疄鐜版爲鑺傜偣鐨勶紝閮介渶瑕佺户鎵胯绫�
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Data
+public class TreeNode<T> implements Serializable {
+
+ /**
+ * 涓婚敭
+ */
+ private Long id;
+ /**
+ * 涓婄骇ID
+ */
+ private Long pid;
+ /**
+ * 瀛愯妭鐐瑰垪琛�
+ */
+ private List<T> children = new ArrayList<>();
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/TreeUtils.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/TreeUtils.java
new file mode 100644
index 0000000..e76190e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/utils/TreeUtils.java
@@ -0,0 +1,71 @@
+package xiaozhi.common.utils;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import xiaozhi.common.validator.AssertUtils;
+
+/**
+ * 鏍戝舰缁撴瀯宸ュ叿绫伙紝濡傦細鑿滃崟銆侀儴闂ㄧ瓑
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public class TreeUtils {
+
+ /**
+ * 鏍规嵁pid锛屾瀯寤烘爲鑺傜偣
+ */
+ public static <T extends TreeNode<T>> List<T> build(List<T> treeNodes, Long pid) {
+ // pid涓嶈兘涓虹┖
+ AssertUtils.isNull(pid, "pid");
+
+ List<T> treeList = new ArrayList<>();
+ for (T treeNode : treeNodes) {
+ if (pid.equals(treeNode.getPid())) {
+ treeList.add(findChildren(treeNodes, treeNode));
+ }
+ }
+
+ return treeList;
+ }
+
+ /**
+ * 鏌ユ壘瀛愯妭鐐�
+ */
+ private static <T extends TreeNode<T>> T findChildren(List<T> treeNodes, T rootNode) {
+ for (T treeNode : treeNodes) {
+ if (rootNode.getId().equals(treeNode.getPid())) {
+ rootNode.getChildren().add(findChildren(treeNodes, treeNode));
+ }
+ }
+ return rootNode;
+ }
+
+ /**
+ * 鏋勫缓鏍戣妭鐐�
+ */
+ public static <T extends TreeNode<T>> List<T> build(List<T> treeNodes) {
+ List<T> result = new ArrayList<>();
+
+ // list杞琺ap
+ Map<Long, T> nodeMap = new LinkedHashMap<>(treeNodes.size());
+ for (T treeNode : treeNodes) {
+ nodeMap.put(treeNode.getId(), treeNode);
+ }
+
+ for (T node : nodeMap.values()) {
+ T parent = nodeMap.get(node.getPid());
+ if (parent != null && !(node.getId().equals(parent.getId()))) {
+ parent.getChildren().add(node);
+ continue;
+ }
+
+ result.add(node);
+ }
+
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/validator/AssertUtils.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/validator/AssertUtils.java
new file mode 100644
index 0000000..c86aa51
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/validator/AssertUtils.java
@@ -0,0 +1,90 @@
+package xiaozhi.common.validator;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ArrayUtil;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+
+/**
+ * 鏍¢獙宸ュ叿绫�
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public class AssertUtils {
+
+ public static void isBlank(String str, String... params) {
+ isBlank(str, ErrorCode.NOT_NULL, params);
+ }
+
+ public static void isBlank(String str, Integer code, String... params) {
+ if (code == null) {
+ throw new RenException(ErrorCode.NOT_NULL, "code");
+ }
+
+ if (StringUtils.isBlank(str)) {
+ throw new RenException(code, params);
+ }
+ }
+
+ public static void isNull(Object object, String... params) {
+ isNull(object, ErrorCode.NOT_NULL, params);
+ }
+
+ public static void isNull(Object object, Integer code, String... params) {
+ if (code == null) {
+ throw new RenException(ErrorCode.NOT_NULL, "code");
+ }
+
+ if (object == null) {
+ throw new RenException(code, params);
+ }
+ }
+
+ public static void isArrayEmpty(Object[] array, String... params) {
+ isArrayEmpty(array, ErrorCode.NOT_NULL, params);
+ }
+
+ public static void isArrayEmpty(Object[] array, Integer code, String... params) {
+ if (code == null) {
+ throw new RenException(ErrorCode.NOT_NULL, "code");
+ }
+
+ if (ArrayUtil.isEmpty(array)) {
+ throw new RenException(code, params);
+ }
+ }
+
+ public static void isListEmpty(List<?> list, String... params) {
+ isListEmpty(list, ErrorCode.NOT_NULL, params);
+ }
+
+ public static void isListEmpty(List<?> list, Integer code, String... params) {
+ if (code == null) {
+ throw new RenException(ErrorCode.NOT_NULL, "code");
+ }
+
+ if (CollUtil.isEmpty(list)) {
+ throw new RenException(code, params);
+ }
+ }
+
+ public static void isMapEmpty(Map<?, ?> map, String... params) {
+ isMapEmpty(map, ErrorCode.NOT_NULL, params);
+ }
+
+ public static void isMapEmpty(Map<?, ?> map, Integer code, String... params) {
+ if (code == null) {
+ throw new RenException(ErrorCode.NOT_NULL, "code");
+ }
+
+ if (MapUtil.isEmpty(map)) {
+ throw new RenException(code, params);
+ }
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/validator/ValidatorUtils.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/validator/ValidatorUtils.java
new file mode 100644
index 0000000..364da0d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/validator/ValidatorUtils.java
@@ -0,0 +1,77 @@
+package xiaozhi.common.validator;
+
+import java.util.Locale;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;
+import org.springframework.context.i18n.LocaleContextHolder;
+import org.springframework.context.support.ResourceBundleMessageSource;
+import org.springframework.validation.beanvalidation.MessageSourceResourceBundleLocator;
+
+import jakarta.validation.ConstraintViolation;
+import jakarta.validation.Validation;
+import jakarta.validation.Validator;
+import xiaozhi.common.exception.RenException;
+
+/**
+ * hibernate-validator鏍¢獙宸ュ叿绫�
+ * 鍙傝�冩枃妗o細http://docs.jboss.org/hibernate/validator/6.0/reference/en-US/html_single/
+ */
+public class ValidatorUtils {
+
+ private static ResourceBundleMessageSource getMessageSource() {
+ ResourceBundleMessageSource bundleMessageSource = new ResourceBundleMessageSource();
+ bundleMessageSource.setDefaultEncoding("UTF-8");
+ bundleMessageSource.setBasenames("i18n/validation");
+ return bundleMessageSource;
+ }
+
+ /**
+ * 鏍¢獙瀵硅薄
+ *
+ * @param object 寰呮牎楠屽璞�
+ * @param groups 寰呮牎楠岀殑缁�
+ */
+ public static void validateEntity(Object object, Class<?>... groups)
+ throws RenException {
+ Locale.setDefault(LocaleContextHolder.getLocale());
+ Validator validator = Validation.byDefaultProvider().configure().messageInterpolator(
+ new ResourceBundleMessageInterpolator(new MessageSourceResourceBundleLocator(getMessageSource())))
+ .buildValidatorFactory().getValidator();
+
+ Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
+ if (!constraintViolations.isEmpty()) {
+ ConstraintViolation<Object> constraint = constraintViolations.iterator().next();
+ throw new RenException(constraint.getMessage());
+ }
+ }
+
+ /**
+ * 鍥介檯鎵嬫満鍙锋鍒欒〃杈惧紡
+ * 瑕佹眰蹇呴』甯﹀浗闄呭尯鍙凤紝鏍煎紡锛�+[鍥藉浠g爜][鎵嬫満鍙穄
+ * 渚嬪锛�
+ * - +8613800138000
+ * - +12345678900
+ * - +447123456789
+ */
+ private static final String INTERNATIONAL_PHONE_REGEX = "^\\+[1-9]\\d{0,3}[1-9]\\d{4,14}$";
+
+ /**
+ * 鏍¢獙鎵嬫満鍙锋槸鍚︽湁鏁�
+ * 瑕佹眰蹇呴』甯﹀浗闄呭尯鍙凤紝鏍煎紡锛�+[鍥藉浠g爜][鎵嬫満鍙穄
+ * 渚嬪锛�+8613800138000
+ *
+ * @param phone 鎵嬫満鍙�
+ * @return boolean
+ */
+ public static boolean isValidPhone(String phone) {
+ if (phone == null || phone.isEmpty()) {
+ return false;
+ }
+
+ // 楠岃瘉蹇呴』甯﹀浗闄呭尯鍙风殑鎵嬫満鍙锋牸寮�
+ Pattern pattern = Pattern.compile(INTERNATIONAL_PHONE_REGEX);
+ return pattern.matcher(phone).matches();
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/validator/group/AddGroup.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/validator/group/AddGroup.java
new file mode 100644
index 0000000..fd8290f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/validator/group/AddGroup.java
@@ -0,0 +1,8 @@
+package xiaozhi.common.validator.group;
+
+/**
+ * 鏂板 Group
+ */
+public interface AddGroup {
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/validator/group/DefaultGroup.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/validator/group/DefaultGroup.java
new file mode 100644
index 0000000..60ddb73
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/validator/group/DefaultGroup.java
@@ -0,0 +1,8 @@
+package xiaozhi.common.validator.group;
+
+/**
+ * 榛樿 Group
+ */
+public interface DefaultGroup {
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/validator/group/UpdateGroup.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/validator/group/UpdateGroup.java
new file mode 100644
index 0000000..220fda3
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/validator/group/UpdateGroup.java
@@ -0,0 +1,8 @@
+package xiaozhi.common.validator.group;
+
+/**
+ * 淇敼 Group
+ */
+public interface UpdateGroup {
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/SqlFilter.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/SqlFilter.java
new file mode 100644
index 0000000..81f5534
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/SqlFilter.java
@@ -0,0 +1,46 @@
+package xiaozhi.common.xss;
+
+import org.apache.commons.lang3.StringUtils;
+
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+
+/**
+ * SQL杩囨护
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public class SqlFilter {
+
+ /**
+ * SQL娉ㄥ叆杩囨护
+ *
+ * @param str 寰呴獙璇佺殑瀛楃涓�
+ */
+ public static String sqlInject(String str) {
+ if (StringUtils.isBlank(str)) {
+ return null;
+ }
+ // 鍘绘帀'|"|;|\瀛楃
+ str = str.replace("'", "");
+ str = str.replace("\"", "");
+ str = str.replace(";", "");
+ str = str.replace("\\", "");
+
+ // 杞崲鎴愬皬鍐�
+ str = str.toLowerCase();
+
+ // 闈炴硶瀛楃
+ String[] keywords = { "master", "truncate", "insert", "select", "delete", "update", "declare", "alter",
+ "drop" };
+
+ // 鍒ゆ柇鏄惁鍖呭惈闈炴硶瀛楃
+ for (String keyword : keywords) {
+ if (str.contains(keyword)) {
+ throw new RenException(ErrorCode.INVALID_SYMBOL);
+ }
+ }
+
+ return str;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/XssConfig.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/XssConfig.java
new file mode 100644
index 0000000..e4b6546
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/XssConfig.java
@@ -0,0 +1,33 @@
+package xiaozhi.common.xss;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.util.PathMatcher;
+
+import jakarta.servlet.DispatcherType;
+
+/**
+ * XSS 閰嶇疆鏂囦欢
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Configuration
+@EnableConfigurationProperties(XssProperties.class)
+@ConditionalOnProperty(prefix = "renren.xss", value = "enabled")
+public class XssConfig {
+
+ @Bean
+ public FilterRegistrationBean<XssFilter> xssFilter(XssProperties properties, PathMatcher pathMatcher) {
+ FilterRegistrationBean<XssFilter> bean = new FilterRegistrationBean<>();
+ bean.setDispatcherTypes(DispatcherType.REQUEST);
+ bean.setFilter(new XssFilter(properties, pathMatcher));
+ bean.addUrlPatterns("/*");
+ bean.setOrder(Integer.MAX_VALUE);
+ bean.setName("xssFilter");
+
+ return bean;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/XssFilter.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/XssFilter.java
new file mode 100644
index 0000000..db76998
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/XssFilter.java
@@ -0,0 +1,55 @@
+package xiaozhi.common.xss;
+
+import java.io.IOException;
+
+import org.springframework.util.PathMatcher;
+
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.AllArgsConstructor;
+
+/**
+ * XSS杩囨护
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@AllArgsConstructor
+public class XssFilter implements Filter {
+ private final XssProperties properties;
+ private final PathMatcher pathMatcher;
+
+ @Override
+ public void init(FilterConfig config) throws ServletException {
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+
+ // 鏀捐
+ if (shouldNotFilter(httpServletRequest)) {
+ chain.doFilter(request, response);
+
+ return;
+ }
+
+ chain.doFilter(new XssHttpServletRequestWrapper(httpServletRequest), response);
+ }
+
+ private boolean shouldNotFilter(HttpServletRequest request) {
+ // 鏀捐涓嶈繃婊ょ殑URL
+ return properties.getExcludeUrls().stream()
+ .anyMatch(excludeUrl -> pathMatcher.match(excludeUrl, request.getServletPath()));
+ }
+
+ @Override
+ public void destroy() {
+ }
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/XssHttpServletRequestWrapper.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/XssHttpServletRequestWrapper.java
new file mode 100644
index 0000000..ecb3640
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/XssHttpServletRequestWrapper.java
@@ -0,0 +1,118 @@
+package xiaozhi.common.xss;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+
+import cn.hutool.core.io.IoUtil;
+import jakarta.servlet.ReadListener;
+import jakarta.servlet.ServletInputStream;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequestWrapper;
+
+/**
+ * XSS杩囨护澶勭悊
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
+
+ public XssHttpServletRequestWrapper(HttpServletRequest request) {
+ super(request);
+ }
+
+ @Override
+ public ServletInputStream getInputStream() throws IOException {
+ // 闈瀓son绫诲瀷锛岀洿鎺ヨ繑鍥�
+ if (!MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(super.getHeader(HttpHeaders.CONTENT_TYPE))) {
+ return super.getInputStream();
+ }
+
+ // 涓虹┖锛岀洿鎺ヨ繑鍥�
+ String json = IoUtil.readUtf8(super.getInputStream());
+ if (StringUtils.isBlank(json)) {
+ return super.getInputStream();
+ }
+
+ // xss杩囨护
+ json = xssEncode(json);
+ final ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8));
+ return new ServletInputStream() {
+ @Override
+ public boolean isFinished() {
+ return true;
+ }
+
+ @Override
+ public boolean isReady() {
+ return true;
+ }
+
+ @Override
+ public void setReadListener(ReadListener readListener) {
+
+ }
+
+ @Override
+ public int read() {
+ return bis.read();
+ }
+ };
+ }
+
+ @Override
+ public String getParameter(String name) {
+ String value = super.getParameter(xssEncode(name));
+ if (StringUtils.isNotBlank(value)) {
+ value = xssEncode(value);
+ }
+ return value;
+ }
+
+ @Override
+ public String[] getParameterValues(String name) {
+ String[] parameters = super.getParameterValues(name);
+ if (parameters == null || parameters.length == 0) {
+ return null;
+ }
+
+ for (int i = 0; i < parameters.length; i++) {
+ parameters[i] = xssEncode(parameters[i]);
+ }
+ return parameters;
+ }
+
+ @Override
+ public Map<String, String[]> getParameterMap() {
+ Map<String, String[]> map = new LinkedHashMap<>();
+ Map<String, String[]> parameters = super.getParameterMap();
+ for (String key : parameters.keySet()) {
+ String[] values = parameters.get(key);
+ for (int i = 0; i < values.length; i++) {
+ values[i] = xssEncode(values[i]);
+ }
+ map.put(key, values);
+ }
+ return map;
+ }
+
+ @Override
+ public String getHeader(String name) {
+ String value = super.getHeader(xssEncode(name));
+ if (StringUtils.isNotBlank(value)) {
+ value = xssEncode(value);
+ }
+ return value;
+ }
+
+ private String xssEncode(String input) {
+ return XssUtils.filter(input);
+ }
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/XssProperties.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/XssProperties.java
new file mode 100644
index 0000000..5d9b8b0
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/XssProperties.java
@@ -0,0 +1,26 @@
+package xiaozhi.common.xss;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import lombok.Data;
+
+/**
+ * XSS 閰嶇疆椤�
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Data
+@ConfigurationProperties(prefix = "renren.xss")
+public class XssProperties {
+ /**
+ * 鏄惁寮�鍚� XSS
+ */
+ private boolean enabled;
+ /**
+ * 鎺掗櫎鐨刄RL鍒楄〃
+ */
+ private List<String> excludeUrls = Collections.emptyList();
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/XssUtils.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/XssUtils.java
new file mode 100644
index 0000000..1658839
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/common/xss/XssUtils.java
@@ -0,0 +1,67 @@
+package xiaozhi.common.xss;
+
+import org.jsoup.Jsoup;
+import org.jsoup.safety.Safelist;
+
+/**
+ * XSS杩囨护宸ュ叿绫�
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public class XssUtils extends Safelist {
+
+ /**
+ * XSS杩囨护
+ */
+ public static String filter(String html) {
+ return Jsoup.clean(html, xssWhitelist());
+ }
+
+ /**
+ * XSS杩囨护鐧藉悕鍗�
+ */
+ private static Safelist xssWhitelist() {
+ return new Safelist()
+ // 鏀寔鐨勬爣绛�
+ .addTags("a", "b", "blockquote", "br", "caption", "cite", "code", "col", "colgroup", "dd", "div", "dl",
+ "dt", "em", "h1", "h2", "h3", "h4", "h5", "h6", "i", "img", "li", "ol", "p", "pre", "q",
+ "small",
+ "strike", "strong", "sub", "sup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "u",
+ "ul",
+ "embed", "object", "param", "span")
+
+ // 鏀寔鐨勬爣绛惧睘鎬�
+ .addAttributes("a", "href", "class", "style", "target", "rel", "nofollow")
+ .addAttributes("blockquote", "cite")
+ .addAttributes("code", "class", "style")
+ .addAttributes("col", "span", "width")
+ .addAttributes("colgroup", "span", "width")
+ .addAttributes("img", "align", "alt", "height", "src", "title", "width", "class", "style")
+ .addAttributes("ol", "start", "type")
+ .addAttributes("q", "cite")
+ .addAttributes("table", "summary", "width", "class", "style")
+ .addAttributes("tr", "abbr", "axis", "colspan", "rowspan", "width", "style")
+ .addAttributes("td", "abbr", "axis", "colspan", "rowspan", "width", "style")
+ .addAttributes("th", "abbr", "axis", "colspan", "rowspan", "scope", "width", "style")
+ .addAttributes("ul", "type", "style")
+ .addAttributes("pre", "class", "style")
+ .addAttributes("div", "class", "id", "style")
+ .addAttributes("embed", "src", "wmode", "flashvars", "pluginspage", "allowFullScreen",
+ "allowfullscreen",
+ "quality", "width", "height", "align", "allowScriptAccess", "allowscriptaccess",
+ "allownetworking", "type")
+ .addAttributes("object", "type", "id", "name", "data", "width", "height", "style", "classid",
+ "codebase")
+ .addAttributes("param", "name", "value")
+ .addAttributes("span", "class", "style")
+
+ // 鏍囩灞炴�у搴旂殑鍗忚
+ .addProtocols("a", "href", "ftp", "http", "https", "mailto")
+ .addProtocols("img", "src", "http", "https")
+ .addProtocols("blockquote", "cite", "http", "https")
+ .addProtocols("cite", "cite", "http", "https")
+ .addProtocols("q", "cite", "http", "https")
+ .addProtocols("embed", "src", "http", "https");
+ }
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/Enums/AgentChatHistoryType.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/Enums/AgentChatHistoryType.java
new file mode 100644
index 0000000..49ed2dc
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/Enums/AgentChatHistoryType.java
@@ -0,0 +1,21 @@
+package xiaozhi.modules.agent.Enums;
+
+
+import lombok.Getter;
+
+/**
+ * 鏅鸿兘浣撹亰澶╄褰曠被鍨�
+ */
+@Getter
+public enum AgentChatHistoryType {
+
+ USER((byte) 1),
+ AGENT((byte) 2);
+
+ private final byte value;
+
+ AgentChatHistoryType(byte i) {
+ this.value = i;
+ }
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/Enums/AgentSnapshotField.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/Enums/AgentSnapshotField.java
new file mode 100644
index 0000000..166b76f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/Enums/AgentSnapshotField.java
@@ -0,0 +1,103 @@
+package xiaozhi.modules.agent.Enums;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+
+import lombok.Getter;
+import xiaozhi.modules.agent.dto.AgentSnapshotDataDTO;
+import xiaozhi.modules.agent.dto.AgentUpdateDTO;
+import xiaozhi.modules.agent.entity.AgentEntity;
+
+@Getter
+public enum AgentSnapshotField {
+ AGENT_CODE("agentCode", AgentSnapshotDataDTO::getAgentCode, AgentUpdateDTO::getAgentCode,
+ (agent, data) -> agent.setAgentCode(data.getAgentCode())),
+ AGENT_NAME("agentName", AgentSnapshotDataDTO::getAgentName, AgentUpdateDTO::getAgentName,
+ (agent, data) -> agent.setAgentName(data.getAgentName())),
+ ASR_MODEL_ID("asrModelId", AgentSnapshotDataDTO::getAsrModelId, AgentUpdateDTO::getAsrModelId,
+ (agent, data) -> agent.setAsrModelId(data.getAsrModelId())),
+ VAD_MODEL_ID("vadModelId", AgentSnapshotDataDTO::getVadModelId, AgentUpdateDTO::getVadModelId,
+ (agent, data) -> agent.setVadModelId(data.getVadModelId())),
+ LLM_MODEL_ID("llmModelId", AgentSnapshotDataDTO::getLlmModelId, AgentUpdateDTO::getLlmModelId,
+ (agent, data) -> agent.setLlmModelId(data.getLlmModelId())),
+ SLM_MODEL_ID("slmModelId", AgentSnapshotDataDTO::getSlmModelId, AgentUpdateDTO::getSlmModelId,
+ (agent, data) -> agent.setSlmModelId(data.getSlmModelId())),
+ VLLM_MODEL_ID("vllmModelId", AgentSnapshotDataDTO::getVllmModelId, AgentUpdateDTO::getVllmModelId,
+ (agent, data) -> agent.setVllmModelId(data.getVllmModelId())),
+ TTS_MODEL_ID("ttsModelId", AgentSnapshotDataDTO::getTtsModelId, AgentUpdateDTO::getTtsModelId,
+ (agent, data) -> agent.setTtsModelId(data.getTtsModelId())),
+ TTS_VOICE_ID("ttsVoiceId", AgentSnapshotDataDTO::getTtsVoiceId, AgentUpdateDTO::getTtsVoiceId,
+ (agent, data) -> agent.setTtsVoiceId(data.getTtsVoiceId())),
+ TTS_LANGUAGE("ttsLanguage", AgentSnapshotDataDTO::getTtsLanguage, AgentUpdateDTO::getTtsLanguage,
+ (agent, data) -> agent.setTtsLanguage(data.getTtsLanguage())),
+ TTS_VOLUME("ttsVolume", AgentSnapshotDataDTO::getTtsVolume, AgentUpdateDTO::getTtsVolume,
+ (agent, data) -> agent.setTtsVolume(data.getTtsVolume())),
+ TTS_RATE("ttsRate", AgentSnapshotDataDTO::getTtsRate, AgentUpdateDTO::getTtsRate,
+ (agent, data) -> agent.setTtsRate(data.getTtsRate())),
+ TTS_PITCH("ttsPitch", AgentSnapshotDataDTO::getTtsPitch, AgentUpdateDTO::getTtsPitch,
+ (agent, data) -> agent.setTtsPitch(data.getTtsPitch())),
+ MEM_MODEL_ID("memModelId", AgentSnapshotDataDTO::getMemModelId, AgentUpdateDTO::getMemModelId,
+ (agent, data) -> agent.setMemModelId(data.getMemModelId())),
+ INTENT_MODEL_ID("intentModelId", AgentSnapshotDataDTO::getIntentModelId, AgentUpdateDTO::getIntentModelId,
+ (agent, data) -> agent.setIntentModelId(data.getIntentModelId())),
+ CHAT_HISTORY_CONF("chatHistoryConf", AgentSnapshotDataDTO::getChatHistoryConf, AgentUpdateDTO::getChatHistoryConf,
+ (agent, data) -> agent.setChatHistoryConf(data.getChatHistoryConf())),
+ SYSTEM_PROMPT("systemPrompt", AgentSnapshotDataDTO::getSystemPrompt, AgentUpdateDTO::getSystemPrompt,
+ (agent, data) -> agent.setSystemPrompt(data.getSystemPrompt())),
+ SUMMARY_MEMORY("summaryMemory", AgentSnapshotDataDTO::getSummaryMemory, AgentUpdateDTO::getSummaryMemory,
+ (agent, data) -> agent.setSummaryMemory(data.getSummaryMemory())),
+ LANG_CODE("langCode", AgentSnapshotDataDTO::getLangCode, AgentUpdateDTO::getLangCode,
+ (agent, data) -> agent.setLangCode(data.getLangCode())),
+ LANGUAGE("language", AgentSnapshotDataDTO::getLanguage, AgentUpdateDTO::getLanguage,
+ (agent, data) -> agent.setLanguage(data.getLanguage())),
+ SORT("sort", AgentSnapshotDataDTO::getSort, AgentUpdateDTO::getSort,
+ (agent, data) -> agent.setSort(data.getSort())),
+ FUNCTIONS("functions", AgentSnapshotDataDTO::getFunctions, AgentUpdateDTO::getFunctions, null),
+ CONTEXT_PROVIDERS("contextProviders", AgentSnapshotDataDTO::getContextProviders,
+ AgentUpdateDTO::getContextProviders, null),
+ CORRECT_WORD_FILE_IDS("correctWordFileIds", AgentSnapshotDataDTO::getCorrectWordFileIds,
+ AgentUpdateDTO::getCorrectWordFileIds, null),
+ TAG_NAMES("tagNames", AgentSnapshotDataDTO::getTagNames, AgentUpdateDTO::getTagNames, null);
+
+ private final String fieldName;
+ private final Function<AgentSnapshotDataDTO, Object> snapshotGetter;
+ private final Function<AgentUpdateDTO, Object> updateGetter;
+ private final BiConsumer<AgentEntity, AgentSnapshotDataDTO> restoreApplier;
+
+ AgentSnapshotField(String fieldName, Function<AgentSnapshotDataDTO, Object> snapshotGetter,
+ Function<AgentUpdateDTO, Object> updateGetter,
+ BiConsumer<AgentEntity, AgentSnapshotDataDTO> restoreApplier) {
+ this.fieldName = fieldName;
+ this.snapshotGetter = snapshotGetter;
+ this.updateGetter = updateGetter;
+ this.restoreApplier = restoreApplier;
+ }
+
+ public static List<String> names() {
+ return Arrays.stream(values()).map(AgentSnapshotField::getFieldName).toList();
+ }
+
+ public static String canonicalName(String fieldName) {
+ return "tags".equals(fieldName) ? TAG_NAMES.getFieldName() : fieldName;
+ }
+
+ public Object snapshotValue(AgentSnapshotDataDTO data) {
+ return data == null ? null : snapshotGetter.apply(data);
+ }
+
+ public Object updateValue(AgentUpdateDTO data) {
+ return data == null ? null : updateGetter.apply(data);
+ }
+
+ public boolean isRestorableAgentField() {
+ return restoreApplier != null;
+ }
+
+ public void applyTo(AgentEntity agent, AgentSnapshotDataDTO data) {
+ if (restoreApplier != null) {
+ restoreApplier.accept(agent, data);
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/Enums/XiaoZhiMcpJsonRpcJson.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/Enums/XiaoZhiMcpJsonRpcJson.java
new file mode 100644
index 0000000..114b1ab
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/Enums/XiaoZhiMcpJsonRpcJson.java
@@ -0,0 +1,44 @@
+package xiaozhi.modules.agent.Enums;
+
+import xiaozhi.common.utils.JsonUtils;
+import xiaozhi.common.utils.JsonRpcTwo;
+
+import java.util.Map;
+
+
+/**
+ * 灏忔櫤MCP JSON-RPC 璇锋眰json
+ */
+public class XiaoZhiMcpJsonRpcJson {
+ //灏忔櫤鍒濆鍖杕cp璇锋眰json
+ private static final String INITIALIZE_JSON;
+ //灏忔櫤mcp鍒濆鍖栨垚鍔燂紝杩斿洖閫氱煡璇锋眰json
+ private static final String NOTIFICATIONS_INITIALIZED_JSON;
+ //灏忔櫤mcp鑾峰彇mcp宸ュ叿闆嗗悎璇锋眰json
+ private static final String TOOLS_LIST_REQUEST;
+ // 寤惰繜鍔犺浇
+ static {
+ INITIALIZE_JSON = JsonUtils.toJsonString(new JsonRpcTwo("initialize",
+ Map.of(
+ "protocolVersion", "2024-11-05",
+ "capabilities", Map.of(
+ "roots", Map.of("listChanged", false),
+ "sampling", Map.of()),
+ "clientInfo", Map.of(
+ "name", "xz-mcp-broker",
+ "version", "0.0.1")),
+ 1));
+ NOTIFICATIONS_INITIALIZED_JSON = "{\"jsonrpc\":\"2.0\",\"method\":\"notifications/initialized\"}";
+ TOOLS_LIST_REQUEST = JsonUtils.toJsonString(new JsonRpcTwo("tools/list", null, 2));
+ }
+ public static String getInitializeJson(){
+ return INITIALIZE_JSON;
+ }
+ public static String getNotificationsInitializedJson(){
+ return NOTIFICATIONS_INITIALIZED_JSON;
+ }
+ public static String getToolsListJson(){
+ return TOOLS_LIST_REQUEST;
+ }
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentChatHistoryController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentChatHistoryController.java
new file mode 100644
index 0000000..49096d4
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentChatHistoryController.java
@@ -0,0 +1,246 @@
+package xiaozhi.modules.agent.controller;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.redis.RedisKeys;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.common.user.UserDetail;
+import xiaozhi.common.utils.DateUtils;
+import xiaozhi.common.utils.MessageUtils;
+import xiaozhi.common.utils.Result;
+import xiaozhi.modules.agent.dto.AgentChatHistoryDTO;
+import xiaozhi.modules.agent.dto.AgentChatHistoryReportDTO;
+import xiaozhi.modules.agent.dto.AgentChatSessionDTO;
+import xiaozhi.modules.agent.service.AgentChatHistoryService;
+import xiaozhi.modules.agent.service.AgentService;
+import xiaozhi.modules.agent.service.biz.AgentChatHistoryBizService;
+import xiaozhi.modules.security.user.SecurityUser;
+
+@Tag(name = "鏅鸿兘浣撹亰澶╁巻鍙茬鐞�")
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/agent/chat-history")
+public class AgentChatHistoryController {
+ private final AgentChatHistoryBizService agentChatHistoryBizService;
+ private final AgentChatHistoryService agentChatHistoryService;
+ private final AgentService agentService;
+ private final RedisUtils redisUtils;
+
+ /**
+ * 灏忔櫤鏈嶅姟鑱婂ぉ涓婃姤璇锋眰
+ * <p>
+ * 灏忔櫤鏈嶅姟鑱婂ぉ涓婃姤璇锋眰锛屽寘鍚獴ase64缂栫爜鐨勯煶棰戞暟鎹拰鐩稿叧淇℃伅銆�
+ *
+ * @param request 鍖呭惈涓婁紶鏂囦欢鍙婄浉鍏充俊鎭殑璇锋眰瀵硅薄
+ */
+ @Operation(summary = "灏忔櫤鏈嶅姟鑱婂ぉ涓婃姤璇锋眰")
+ @PostMapping("/report")
+ public Result<Boolean> uploadFile(@Valid @RequestBody AgentChatHistoryReportDTO request) {
+ Boolean result = agentChatHistoryBizService.report(request);
+ return new Result<Boolean>().ok(result);
+ }
+
+ /**
+ * 鑾峰彇鑱婂ぉ璁板綍涓嬭浇閾炬帴
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ * @param sessionId 浼氳瘽ID
+ * @return UUID浣滀负涓嬭浇鏍囪瘑
+ */
+ @Operation(summary = "鑾峰彇鑱婂ぉ璁板綍涓嬭浇閾炬帴")
+ @RequiresPermissions("sys:role:normal")
+ @PostMapping("/getDownloadUrl/{agentId}/{sessionId}")
+ public Result<String> getDownloadUrl(@PathVariable("agentId") String agentId,
+ @PathVariable("sessionId") String sessionId) {
+ // 鑾峰彇褰撳墠鐢ㄦ埛
+ UserDetail user = SecurityUser.getUser();
+ // 妫�鏌ユ潈闄�
+ if (!agentService.checkAgentPermission(agentId, user.getId())) {
+ throw new RenException(ErrorCode.CHAT_HISTORY_NO_PERMISSION);
+ }
+
+ // 鐢熸垚UUID
+ String uuid = UUID.randomUUID().toString();
+ // 瀛樺偍agentId鍜宻essionId鍒癛edis锛屾牸寮忎负agentId:sessionId
+ redisUtils.set(RedisKeys.getChatHistoryKey(uuid), agentId + ":" + sessionId);
+
+ return new Result<String>().ok(uuid);
+ }
+
+ /**
+ * 涓嬭浇鏈細璇濊亰澶╄褰�
+ *
+ * @param uuid 涓嬭浇鏍囪瘑
+ * @param response HTTP鍝嶅簲
+ */
+ @Operation(summary = "涓嬭浇鏈細璇濊亰澶╄褰�")
+ @GetMapping("/download/{uuid}/current")
+ public void downloadCurrentSession(@PathVariable("uuid") String uuid,
+ HttpServletResponse response) {
+ // 浠嶳edis鑾峰彇agentId鍜宻essionId
+ String agentSessionInfo = (String) redisUtils.get(RedisKeys.getChatHistoryKey(uuid));
+ if (StringUtils.isBlank(agentSessionInfo)) {
+ throw new RenException(ErrorCode.DOWNLOAD_LINK_EXPIRED);
+ }
+
+ try {
+ // 瑙f瀽agentId鍜宻essionId
+ String[] parts = agentSessionInfo.split(":");
+ if (parts.length != 2) {
+ throw new RenException(ErrorCode.DOWNLOAD_LINK_INVALID);
+ }
+ String agentId = parts[0];
+ String sessionId = parts[1];
+
+ // 鎵ц涓嬭浇
+ downloadChatHistory(agentId, List.of(sessionId), response);
+ } finally {
+ // 涓嬭浇瀹屾垚鍚庡垹闄UID锛岄槻姝㈢洍鍒�
+ redisUtils.delete(RedisKeys.getChatHistoryKey(uuid));
+ }
+ }
+
+ /**
+ * 涓嬭浇鏈細璇濆強鍓�20鏉′細璇濊亰澶╄褰�
+ *
+ * @param uuid 涓嬭浇鏍囪瘑
+ * @param response HTTP鍝嶅簲
+ */
+ @Operation(summary = "涓嬭浇鏈細璇濆強鍓�20鏉′細璇濊亰澶╄褰�")
+ @GetMapping("/download/{uuid}/previous")
+ public void downloadCurrentSessionWithPrevious(@PathVariable("uuid") String uuid,
+ HttpServletResponse response) {
+ // 浠嶳edis鑾峰彇agentId鍜宻essionId
+ String agentSessionInfo = (String) redisUtils.get(RedisKeys.getChatHistoryKey(uuid));
+ if (StringUtils.isBlank(agentSessionInfo)) {
+ throw new RenException(ErrorCode.DOWNLOAD_LINK_EXPIRED);
+ }
+
+ try {
+ // 瑙f瀽agentId鍜宻essionId
+ String[] parts = agentSessionInfo.split(":");
+ if (parts.length != 2) {
+ throw new RenException(ErrorCode.DOWNLOAD_LINK_INVALID);
+ }
+ String agentId = parts[0];
+ String sessionId = parts[1];
+
+ // 鑾峰彇鎵�鏈変細璇濆垪琛�
+ Map<String, Object> params = Map.of(
+ "agentId", agentId,
+ Constant.PAGE, 1,
+ Constant.LIMIT, 1000 // 鑾峰彇瓒冲澶氱殑浼氳瘽
+ );
+ PageData<AgentChatSessionDTO> sessionPage = agentChatHistoryService.getSessionListByAgentId(params);
+ List<AgentChatSessionDTO> allSessions = sessionPage.getList();
+
+ // 鏌ユ壘褰撳墠浼氳瘽鍦ㄥ垪琛ㄤ腑鐨勪綅缃�
+ int currentIndex = -1;
+ for (int i = 0; i < allSessions.size(); i++) {
+ if (allSessions.get(i).getSessionId().equals(sessionId)) {
+ currentIndex = i;
+ break;
+ }
+ }
+
+ // 濡傛灉鎵惧埌浜嗗綋鍓嶄細璇濓紝鏀堕泦褰撳墠浼氳瘽鍙婂墠20鏉′細璇滻D
+ List<String> sessionIdsToDownload = new ArrayList<>();
+ if (currentIndex != -1) {
+ // 浠庡綋鍓嶄細璇濆紑濮嬶紝鍚戝悗锛堟暟缁勫悗闈級鍙栨渶澶�20鏉′細璇濓紙鍖呮嫭褰撳墠浼氳瘽锛�
+ int endIndex = Math.min(allSessions.size() - 1, currentIndex + 20); // 纭繚涓嶈秺鐣�
+ for (int i = currentIndex; i <= endIndex; i++) {
+ sessionIdsToDownload.add(allSessions.get(i).getSessionId());
+ }
+ }
+
+ // 濡傛灉娌℃湁鎵惧埌褰撳墠浼氳瘽锛岃嚦灏戜笅杞藉綋鍓嶄細璇�
+ if (sessionIdsToDownload.isEmpty()) {
+ sessionIdsToDownload.add(sessionId);
+ }
+ downloadChatHistory(agentId, sessionIdsToDownload, response);
+ } finally {
+ // 涓嬭浇瀹屾垚鍚庡垹闄UID锛岄槻姝㈢洍鍒�
+ redisUtils.delete(RedisKeys.getChatHistoryKey(uuid));
+ }
+ }
+
+ /**
+ * 涓嬭浇鎸囧畾浼氳瘽鐨勮亰澶╄褰�
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ * @param sessionIds 浼氳瘽ID鍒楄〃
+ * @param response HTTP鍝嶅簲
+ */
+ private void downloadChatHistory(String agentId, List<String> sessionIds, HttpServletResponse response) {
+ try {
+ // 璁剧疆鍝嶅簲澶�
+ response.setContentType("text/plain;charset=UTF-8");
+ String fileName = URLEncoder.encode("history.txt", StandardCharsets.UTF_8.toString());
+ response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
+
+ // 鑾峰彇鑱婂ぉ璁板綍骞跺啓鍏ュ搷搴旀祦
+ try (OutputStream out = response.getOutputStream()) {
+ // 涓烘瘡涓細璇濈敓鎴愯亰澶╄褰�
+ for (String sessionId : sessionIds) {
+ // 鑾峰彇璇ヤ細璇濈殑鎵�鏈夎亰澶╄褰�
+ List<AgentChatHistoryDTO> chatHistoryList = agentChatHistoryService
+ .getChatHistoryBySessionId(agentId, sessionId);
+
+ // 浠庤亰澶╄褰曚腑鑾峰彇绗竴鏉℃秷鎭殑鍒涘缓鏃堕棿浣滀负浼氳瘽鏃堕棿
+ if (!chatHistoryList.isEmpty()) {
+ Date firstMessageTime = chatHistoryList.get(0).getCreatedAt();
+ String sessionTimeStr = DateUtils.format(firstMessageTime, DateUtils.DATE_TIME_PATTERN);
+ out.write((sessionTimeStr + "\n").getBytes(StandardCharsets.UTF_8));
+ }
+
+ for (AgentChatHistoryDTO message : chatHistoryList) {
+ String role = message.getChatType() == 1 ? MessageUtils.getMessage(ErrorCode.CHAT_ROLE_USER)
+ : MessageUtils.getMessage(ErrorCode.CHAT_ROLE_AGENT);
+ String direction = message.getChatType() == 1 ? ">>" : "<<";
+ Date messageTime = message.getCreatedAt();
+ String messageTimeStr = DateUtils.format(messageTime, DateUtils.DATE_TIME_PATTERN);
+ String content = message.getContent();
+
+ String line = "[" + role + "]-[" + messageTimeStr + "]" + direction + ":" + content + "\n";
+ out.write(line.getBytes(StandardCharsets.UTF_8));
+ }
+
+ // 浼氳瘽涔嬮棿娣诲姞绌鸿鍒嗛殧
+ if (sessionIds.indexOf(sessionId) < sessionIds.size() - 1) {
+ out.write("\n".getBytes(StandardCharsets.UTF_8));
+ }
+ }
+
+ out.flush();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentController.java
new file mode 100644
index 0000000..0961fe7
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentController.java
@@ -0,0 +1,353 @@
+package xiaozhi.modules.agent.controller;
+
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.AllArgsConstructor;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.redis.RedisKeys;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.common.user.UserDetail;
+import xiaozhi.common.utils.JsonUtils;
+import xiaozhi.common.utils.Result;
+import xiaozhi.common.utils.ResultUtils;
+import xiaozhi.modules.agent.dto.AgentChatHistoryDTO;
+import xiaozhi.modules.agent.dto.AgentChatSessionDTO;
+import xiaozhi.modules.agent.dto.AgentCreateDTO;
+import xiaozhi.modules.agent.dto.AgentDTO;
+import xiaozhi.modules.agent.dto.AgentMemoryDTO;
+import xiaozhi.modules.agent.dto.AgentUpdateDTO;
+import xiaozhi.modules.agent.entity.AgentEntity;
+import xiaozhi.modules.agent.entity.AgentTemplateEntity;
+import xiaozhi.modules.agent.dto.AgentTagDTO;
+import xiaozhi.modules.agent.entity.AgentTagEntity;
+import xiaozhi.modules.agent.service.AgentTagService;
+import xiaozhi.modules.agent.service.AgentChatAudioService;
+import xiaozhi.modules.agent.service.AgentChatHistoryService;
+import xiaozhi.modules.agent.service.AgentChatSummaryService;
+import xiaozhi.modules.agent.service.AgentService;
+import xiaozhi.modules.agent.service.AgentTemplateService;
+import xiaozhi.modules.agent.vo.AgentChatHistoryUserVO;
+import xiaozhi.modules.agent.vo.AgentInfoVO;
+import xiaozhi.modules.security.user.SecurityUser;
+
+@Tag(name = "鏅鸿兘浣撶鐞�")
+@AllArgsConstructor
+@RestController
+@RequestMapping("/agent")
+public class AgentController {
+ private static final long AUDIO_PLAY_TOKEN_EXPIRE_SECONDS = 300L;
+
+ private final AgentService agentService;
+ private final AgentTemplateService agentTemplateService;
+ private final AgentChatHistoryService agentChatHistoryService;
+ private final AgentChatAudioService agentChatAudioService;
+ private final AgentChatSummaryService agentChatSummaryService;
+ private final RedisUtils redisUtils;
+ private final AgentTagService agentTagService;
+
+ private void requireAgentPermission(String agentId) {
+ if (!agentService.checkAgentPermission(agentId, SecurityUser.getUserId())) {
+ throw new RenException(ErrorCode.NO_PERMISSION);
+ }
+ }
+
+ private String requireSessionAgent(String sessionId) {
+ String agentId = agentChatHistoryService.getAgentIdBySessionId(sessionId);
+ if (StringUtils.isBlank(agentId)) {
+ throw new RenException(ErrorCode.AGENT_NOT_FOUND);
+ }
+ agentService.getAgentById(agentId);
+ return agentId;
+ }
+
+ private String requireAudioPermission(String audioId) {
+ String agentId = agentChatHistoryService.getAgentIdByAudioId(audioId);
+ if (StringUtils.isBlank(agentId)) {
+ throw new RenException(ErrorCode.NO_PERMISSION);
+ }
+ requireAgentPermission(agentId);
+ return agentId;
+ }
+
+ @GetMapping("/list")
+ @Operation(summary = "鑾峰彇鐢ㄦ埛鏅鸿兘浣撳垪琛�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<List<AgentDTO>> getUserAgents(
+ @RequestParam(value = "keyword", required = false) String keyword,
+ @RequestParam(value = "searchType", defaultValue = "name") String searchType) {
+ UserDetail user = SecurityUser.getUser();
+
+ // 鐩存帴璋冪敤鏁村悎鍚庣殑getUserAgents鏂规硶锛屾棤闇�鍐嶅尯鍒嗘悳绱㈠拰鏅�氭煡璇�
+ List<AgentDTO> agents = agentService.getUserAgents(user.getId(), keyword, searchType);
+ return new Result<List<AgentDTO>>().ok(agents);
+ }
+
+ @GetMapping("/all")
+ @Operation(summary = "鏅鸿兘浣撳垪琛紙绠$悊鍛橈級")
+ @RequiresPermissions("sys:role:superAdmin")
+ @Parameters({
+ @Parameter(name = Constant.PAGE, description = "褰撳墠椤电爜锛屼粠1寮�濮�", required = true),
+ @Parameter(name = Constant.LIMIT, description = "姣忛〉鏄剧ず璁板綍鏁�", required = true),
+ })
+ public Result<PageData<AgentEntity>> adminAgentList(
+ @Parameter(hidden = true) @RequestParam Map<String, Object> params) {
+ PageData<AgentEntity> page = agentService.adminAgentList(params);
+ return new Result<PageData<AgentEntity>>().ok(page);
+ }
+
+ @GetMapping("/{id}")
+ @Operation(summary = "鑾峰彇鏅鸿兘浣撹鎯�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<AgentInfoVO> getAgentById(@PathVariable("id") String id) {
+ AgentInfoVO agent = agentService.getAgentById(id, SecurityUser.getUserId());
+ return ResultUtils.success(agent);
+ }
+
+ @PostMapping
+ @Operation(summary = "鍒涘缓鏅鸿兘浣�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<String> save(@RequestBody @Valid AgentCreateDTO dto) {
+ String agentId = agentService.createAgent(dto);
+ return new Result<String>().ok(agentId);
+ }
+
+ @PutMapping("/saveMemory/{macAddress}")
+ @Operation(summary = "鏍规嵁璁惧id鏇存柊鏅鸿兘浣�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> updateByDeviceId(@PathVariable String macAddress, @RequestBody @Valid AgentMemoryDTO dto) {
+ agentService.updateAgentMemoryByDeviceMacAddress(macAddress, dto, SecurityUser.getUserId());
+ return new Result<Void>().ok(null);
+ }
+
+ @PostMapping("/chat-summary/{sessionId}/save")
+ @Operation(summary = "鏍规嵁浼氳瘽ID鐢熸垚鑱婂ぉ璁板綍鎬荤粨骞朵繚瀛橈紙寮傛鎵ц锛�")
+ public Result<Void> generateAndSaveChatSummary(@PathVariable String sessionId) {
+ requireSessionAgent(sessionId);
+ try {
+ // 寮傛鎵ц鎬荤粨鐢熸垚浠诲姟锛岀珛鍗宠繑鍥炴垚鍔熷搷搴�
+ new Thread(() -> {
+ try {
+ agentChatSummaryService.generateAndSaveChatSummary(sessionId);
+ System.out.println("寮傛鎵ц浼氳瘽 " + sessionId + " 鐨勮亰澶╄褰曟�荤粨瀹屾垚");
+ } catch (Exception e) {
+ System.err.println("寮傛鎵ц浼氳瘽 " + sessionId + " 鐨勮亰澶╄褰曟�荤粨澶辫触: " + e.getMessage());
+ }
+ }).start();
+
+ // 绔嬪嵆杩斿洖鎴愬姛鍝嶅簲锛屼笉绛夊緟鎬荤粨鐢熸垚瀹屾垚
+ return new Result<Void>().ok(null);
+ } catch (Exception e) {
+ return new Result<Void>().error("鍚姩寮傛鎬荤粨鐢熸垚浠诲姟澶辫触: " + e.getMessage());
+ }
+ }
+
+ @PostMapping("/chat-title/{sessionId}/generate")
+ @Operation(summary = "鏍规嵁浼氳瘽ID鐢熸垚鑱婂ぉ鏍囬")
+ public Result<Void> generateAndSaveChatTitle(@PathVariable String sessionId) {
+ requireSessionAgent(sessionId);
+ agentChatSummaryService.generateAndSaveChatTitle(sessionId);
+ return new Result<Void>().ok(null);
+ }
+
+ @PutMapping("/{id}")
+ @Operation(summary = "鏇存柊鏅鸿兘浣�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> update(@PathVariable String id, @RequestBody @Valid AgentUpdateDTO dto) {
+ agentService.updateAgentById(id, dto, SecurityUser.getUserId());
+ return new Result<>();
+ }
+
+ @DeleteMapping("/{id}")
+ @Operation(summary = "鍒犻櫎鏅鸿兘浣�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> delete(@PathVariable String id) {
+ agentService.deleteAgentById(id, SecurityUser.getUserId());
+ return new Result<>();
+ }
+
+ @GetMapping("/template")
+ @Operation(summary = "鏅鸿兘浣撴ā鏉挎ā鏉垮垪琛�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<List<AgentTemplateEntity>> templateList() {
+ List<AgentTemplateEntity> list = agentTemplateService
+ .list(new QueryWrapper<AgentTemplateEntity>().orderByAsc("sort"));
+ return new Result<List<AgentTemplateEntity>>().ok(list);
+ }
+
+ @GetMapping("/{id}/sessions")
+ @Operation(summary = "鑾峰彇鏅鸿兘浣撲細璇濆垪琛�")
+ @RequiresPermissions("sys:role:normal")
+ @Parameters({
+ @Parameter(name = Constant.PAGE, description = "褰撳墠椤电爜锛屼粠1寮�濮�", required = true),
+ @Parameter(name = Constant.LIMIT, description = "姣忛〉鏄剧ず璁板綍鏁�", required = true),
+ })
+ public Result<PageData<AgentChatSessionDTO>> getAgentSessions(
+ @PathVariable("id") String id,
+ @Parameter(hidden = true) @RequestParam Map<String, Object> params) {
+ requireAgentPermission(id);
+ params.put("agentId", id);
+ PageData<AgentChatSessionDTO> page = agentChatHistoryService.getSessionListByAgentId(params);
+ return new Result<PageData<AgentChatSessionDTO>>().ok(page);
+ }
+
+ @GetMapping("/{id}/chat-history/{sessionId}")
+ @Operation(summary = "鑾峰彇鏅鸿兘浣撹亰澶╄褰�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<List<AgentChatHistoryDTO>> getAgentChatHistory(
+ @PathVariable("id") String id,
+ @PathVariable("sessionId") String sessionId) {
+ // 鑾峰彇褰撳墠鐢ㄦ埛
+ UserDetail user = SecurityUser.getUser();
+
+ // 妫�鏌ユ潈闄�
+ if (!agentService.checkAgentPermission(id, user.getId())) {
+ return new Result<List<AgentChatHistoryDTO>>().error("娌℃湁鏉冮檺鏌ョ湅璇ユ櫤鑳戒綋鐨勮亰澶╄褰�");
+ }
+
+ // 鏌ヨ鑱婂ぉ璁板綍
+ List<AgentChatHistoryDTO> result = agentChatHistoryService.getChatHistoryBySessionId(id, sessionId);
+ return new Result<List<AgentChatHistoryDTO>>().ok(result);
+ }
+
+ @GetMapping("/{id}/chat-history/user")
+ @Operation(summary = "鑾峰彇鏅鸿兘浣撹亰澶╄褰曪紙鐢ㄦ埛锛�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<List<AgentChatHistoryUserVO>> getRecentlyFiftyByAgentId(
+ @PathVariable("id") String id) {
+ // 鑾峰彇褰撳墠鐢ㄦ埛
+ UserDetail user = SecurityUser.getUser();
+
+ // 妫�鏌ユ潈闄�
+ if (!agentService.checkAgentPermission(id, user.getId())) {
+ return new Result<List<AgentChatHistoryUserVO>>().error("娌℃湁鏉冮檺鏌ョ湅璇ユ櫤鑳戒綋鐨勮亰澶╄褰�");
+ }
+
+ // 鏌ヨ鑱婂ぉ璁板綍
+ List<AgentChatHistoryUserVO> data = agentChatHistoryService.getRecentlyFiftyByAgentId(id);
+ return new Result<List<AgentChatHistoryUserVO>>().ok(data);
+ }
+
+ @GetMapping("/{id}/chat-history/audio")
+ @Operation(summary = "鑾峰彇闊抽鍐呭")
+ @RequiresPermissions("sys:role:normal")
+ public Result<String> getContentByAudioId(
+ @PathVariable("id") String id) {
+ requireAudioPermission(id);
+ // 鏌ヨ鑱婂ぉ璁板綍
+ String data = agentChatHistoryService.getContentByAudioId(id);
+ return new Result<String>().ok(data);
+ }
+
+ @PostMapping("/audio/{audioId}")
+ @Operation(summary = "鑾峰彇闊抽涓嬭浇ID")
+ @RequiresPermissions("sys:role:normal")
+ public Result<String> getAudioId(@PathVariable("audioId") String audioId) {
+ requireAudioPermission(audioId);
+ byte[] audioData = agentChatAudioService.getAudio(audioId);
+ if (audioData == null) {
+ return new Result<String>().error("闊抽涓嶅瓨鍦�");
+ }
+ String uuid = UUID.randomUUID().toString();
+ redisUtils.set(RedisKeys.getAgentAudioIdKey(uuid), audioId, AUDIO_PLAY_TOKEN_EXPIRE_SECONDS);
+ return new Result<String>().ok(uuid);
+ }
+
+ @GetMapping("/play/{uuid}")
+ @Operation(summary = "鎾斁闊抽")
+ public ResponseEntity<byte[]> playAudio(@PathVariable("uuid") String uuid) {
+
+ String audioId = (String) redisUtils.get(RedisKeys.getAgentAudioIdKey(uuid));
+ if (StringUtils.isBlank(audioId)) {
+ return ResponseEntity.notFound().build();
+ }
+
+ byte[] audioData = agentChatAudioService.getAudio(audioId);
+ if (audioData == null) {
+ return ResponseEntity.notFound().build();
+ }
+ redisUtils.delete(RedisKeys.getAgentAudioIdKey(uuid));
+ return ResponseEntity.ok()
+ .contentType(MediaType.APPLICATION_OCTET_STREAM)
+ .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"play.wav\"")
+ .body(audioData);
+ }
+
+ @PostMapping("/tag")
+ @Operation(summary = "鍒涘缓鏍囩")
+ @RequiresPermissions("sys:role:normal")
+ public Result<AgentTagEntity> createTag(@RequestBody Map<String, String> params) {
+ String tagName = params.get("tagName");
+ if (StringUtils.isBlank(tagName)) {
+ return new Result<AgentTagEntity>().error("鏍囩鍚嶇О涓嶈兘涓虹┖");
+ }
+ AgentTagEntity tag = agentTagService.saveTag(tagName);
+ return new Result<AgentTagEntity>().ok(tag);
+ }
+
+ @GetMapping("/tag/list")
+ @Operation(summary = "鑾峰彇鎵�鏈夋爣绛惧垪琛�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<List<AgentTagDTO>> getAllTags() {
+ List<AgentTagDTO> tags = agentTagService.getAllTags();
+ return new Result<List<AgentTagDTO>>().ok(tags);
+ }
+
+ @DeleteMapping("/tag/{id}")
+ @Operation(summary = "鍒犻櫎鏍囩")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> deleteTag(@PathVariable String id) {
+ agentTagService.deleteTag(id);
+ return new Result<Void>().ok(null);
+ }
+
+ @GetMapping("/{id}/tags")
+ @Operation(summary = "鑾峰彇鏅鸿兘浣撶殑鏍囩")
+ @RequiresPermissions("sys:role:normal")
+ public Result<List<AgentTagDTO>> getAgentTags(@PathVariable String id) {
+ requireAgentPermission(id);
+ List<AgentTagDTO> tags = agentTagService.getTagsByAgentId(id);
+ return new Result<List<AgentTagDTO>>().ok(tags);
+ }
+
+ @PutMapping("/{id}/tags")
+ @Operation(summary = "淇濆瓨鏅鸿兘浣撶殑鏍囩")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> saveAgentTags(@PathVariable String id, @RequestBody Map<String, Object> params) {
+ requireAgentPermission(id);
+ List<String> tagIds = JsonUtils.toList(params.get("tagIds"), String.class);
+ List<String> tagNames = JsonUtils.toList(params.get("tagNames"), String.class);
+ AgentUpdateDTO dto = new AgentUpdateDTO();
+ dto.setTagIds(tagIds);
+ dto.setTagNames(tagNames);
+ agentService.updateAgentById(id, dto);
+ return new Result<Void>().ok(null);
+ }
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentMcpAccessPointController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentMcpAccessPointController.java
new file mode 100644
index 0000000..c805a4e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentMcpAccessPointController.java
@@ -0,0 +1,67 @@
+package xiaozhi.modules.agent.controller;
+
+import java.util.List;
+
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.user.UserDetail;
+import xiaozhi.common.utils.Result;
+import xiaozhi.modules.agent.service.AgentMcpAccessPointService;
+import xiaozhi.modules.agent.service.AgentService;
+import xiaozhi.modules.security.user.SecurityUser;
+
+@Tag(name = "鏅鸿兘浣揗cp鎺ュ叆鐐圭鐞�")
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/agent/mcp")
+public class AgentMcpAccessPointController {
+ private final AgentMcpAccessPointService agentMcpAccessPointService;
+ private final AgentService agentService;
+
+ /**
+ * 鑾峰彇鏅鸿兘浣撶殑Mcp鎺ュ叆鐐瑰湴鍧�
+ *
+ * @param agentId 鏅鸿兘浣搃d
+ * @return 杩斿洖閿欒鎻愰啋鎴栬�匨cp鎺ュ叆鐐瑰湴鍧�
+ */
+ @Operation(summary = "鑾峰彇鏅鸿兘浣撶殑Mcp鎺ュ叆鐐瑰湴鍧�")
+ @GetMapping("/address/{agentId}")
+ @RequiresPermissions("sys:role:normal")
+ public Result<String> getAgentMcpAccessAddress(@PathVariable("agentId") String agentId) {
+ // 鑾峰彇褰撳墠鐢ㄦ埛
+ UserDetail user = SecurityUser.getUser();
+
+ // 妫�鏌ユ潈闄�
+ if (!agentService.checkAgentPermission(agentId, user.getId())) {
+ return new Result<String>().error(ErrorCode.MCP_ACCESS_POINT_ADDRESS_NO_PERMISSION);
+ }
+ String agentMcpAccessAddress = agentMcpAccessPointService.getAgentMcpAccessAddress(agentId);
+ if (agentMcpAccessAddress == null) {
+ return new Result<String>().error(ErrorCode.MCP_ACCESS_POINT_ADDRESS_NOT_CONFIGURED);
+ }
+ return new Result<String>().ok(agentMcpAccessAddress);
+ }
+
+ @Operation(summary = "鑾峰彇鏅鸿兘浣撶殑Mcp宸ュ叿鍒楄〃")
+ @GetMapping("/tools/{agentId}")
+ @RequiresPermissions("sys:role:normal")
+ public Result<List<String>> getAgentMcpToolsList(@PathVariable("agentId") String agentId) {
+ // 鑾峰彇褰撳墠鐢ㄦ埛
+ UserDetail user = SecurityUser.getUser();
+
+ // 妫�鏌ユ潈闄�
+ if (!agentService.checkAgentPermission(agentId, user.getId())) {
+ return new Result<List<String>>().error(ErrorCode.MCP_ACCESS_POINT_TOOLS_LIST_NO_PERMISSION);
+ }
+ List<String> agentMcpToolsList = agentMcpAccessPointService.getAgentMcpToolsList(agentId);
+ return new Result<List<String>>().ok(agentMcpToolsList);
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentSnapshotController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentSnapshotController.java
new file mode 100644
index 0000000..de4b57b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentSnapshotController.java
@@ -0,0 +1,79 @@
+package xiaozhi.modules.agent.controller;
+
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springdoc.core.annotations.ParameterObject;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.AllArgsConstructor;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.user.UserDetail;
+import xiaozhi.common.utils.Result;
+import xiaozhi.modules.agent.dto.AgentSnapshotPageDTO;
+import xiaozhi.modules.agent.dto.AgentSnapshotRestoreDTO;
+import xiaozhi.modules.agent.service.AgentService;
+import xiaozhi.modules.agent.service.AgentSnapshotService;
+import xiaozhi.modules.agent.vo.AgentSnapshotVO;
+import xiaozhi.modules.security.user.SecurityUser;
+
+@Tag(name = "鏅鸿兘浣撳揩鐓�")
+@AllArgsConstructor
+@RestController
+@RequestMapping("/agent/{agentId}/snapshots")
+public class AgentSnapshotController {
+ private final AgentSnapshotService agentSnapshotService;
+ private final AgentService agentService;
+
+ @GetMapping
+ @Operation(summary = "鑾峰彇鏅鸿兘浣撳揩鐓у垪琛�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<PageData<AgentSnapshotVO>> page(
+ @PathVariable String agentId,
+ @ParameterObject AgentSnapshotPageDTO params) {
+ checkPermission(agentId);
+ return new Result<PageData<AgentSnapshotVO>>().ok(agentSnapshotService.page(agentId, params));
+ }
+
+ @GetMapping("/{snapshotId}")
+ @Operation(summary = "鑾峰彇鏅鸿兘浣撳揩鐓ц鎯�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<AgentSnapshotVO> getSnapshot(@PathVariable String agentId, @PathVariable String snapshotId) {
+ checkPermission(agentId);
+ return new Result<AgentSnapshotVO>().ok(agentSnapshotService.getSnapshot(agentId, snapshotId));
+ }
+
+ @PostMapping("/{snapshotId}/restore")
+ @Operation(summary = "鎭㈠鏅鸿兘浣撳揩鐓�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> restore(@PathVariable String agentId, @PathVariable String snapshotId,
+ @RequestBody @Valid AgentSnapshotRestoreDTO request) {
+ checkPermission(agentId);
+ agentSnapshotService.restoreSnapshot(agentId, snapshotId, request.getCurrentStateToken());
+ return new Result<>();
+ }
+
+ @DeleteMapping("/{snapshotId}")
+ @Operation(summary = "鍒犻櫎鏅鸿兘浣撳巻鍙插揩鐓�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> deleteSnapshot(@PathVariable String agentId, @PathVariable String snapshotId) {
+ checkPermission(agentId);
+ agentSnapshotService.deleteSnapshot(agentId, snapshotId);
+ return new Result<>();
+ }
+
+ private void checkPermission(String agentId) {
+ UserDetail user = SecurityUser.getUser();
+ if (user == null || !agentService.checkAgentPermission(agentId, user.getId())) {
+ throw new RenException("娌℃湁鏉冮檺璁块棶璇ユ櫤鑳戒綋蹇収");
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentTemplateController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentTemplateController.java
new file mode 100644
index 0000000..9c8366e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentTemplateController.java
@@ -0,0 +1,158 @@
+package xiaozhi.modules.agent.controller;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.AllArgsConstructor;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.utils.ConvertUtils;
+import xiaozhi.common.utils.Result;
+import xiaozhi.common.utils.ResultUtils;
+import xiaozhi.modules.agent.entity.AgentTemplateEntity;
+import xiaozhi.modules.agent.service.AgentTemplateService;
+import xiaozhi.modules.agent.vo.AgentTemplateVO;
+
+@Tag(name = "鏅鸿兘浣撴ā鏉跨鐞�")
+@AllArgsConstructor
+@RestController
+@RequestMapping("/agent/template")
+public class AgentTemplateController {
+
+ private final AgentTemplateService agentTemplateService;
+
+ @GetMapping("/page")
+ @Operation(summary = "鑾峰彇妯℃澘鍒嗛〉鍒楄〃")
+ @RequiresPermissions("sys:role:superAdmin")
+ @Parameters({
+ @Parameter(name = Constant.PAGE, description = "褰撳墠椤电爜锛屼粠1寮�濮�", required = true),
+ @Parameter(name = Constant.LIMIT, description = "姣忛〉鏄剧ず璁板綍鏁�", required = true),
+ @Parameter(name = "agentName", description = "妯℃澘鍚嶇О锛屾ā绯婃煡璇�")
+ })
+ public Result<PageData<AgentTemplateVO>> getAgentTemplatesPage(
+ @Parameter(hidden = true) @RequestParam Map<String, Object> params) {
+
+ // 鍒涘缓鍒嗛〉瀵硅薄
+ int page = Integer.parseInt(params.getOrDefault(Constant.PAGE, "1").toString());
+ int limit = Integer.parseInt(params.getOrDefault(Constant.LIMIT, "10").toString());
+ Page<AgentTemplateEntity> pageInfo = new Page<>(page, limit);
+
+ // 鍒涘缓鏌ヨ鏉′欢
+ QueryWrapper<AgentTemplateEntity> wrapper = new QueryWrapper<>();
+ String agentName = (String) params.get("agentName");
+ if (agentName != null && !agentName.isEmpty()) {
+ wrapper.like("agent_name", agentName);
+ }
+ wrapper.orderByAsc("sort");
+
+ // 鎵ц鍒嗛〉鏌ヨ
+ IPage<AgentTemplateEntity> pageResult = agentTemplateService.page(pageInfo, wrapper);
+
+ // 浣跨敤ConvertUtils杞崲涓篤O鍒楄〃
+ List<AgentTemplateVO> voList = ConvertUtils.sourceToTarget(pageResult.getRecords(), AgentTemplateVO.class);
+
+ // 淇锛氫娇鐢ㄦ瀯閫犲嚱鏁板垱寤篜ageData瀵硅薄锛岃�屼笉鏄棤鍙傛瀯閫�+setter
+ PageData<AgentTemplateVO> pageData = new PageData<>(voList, pageResult.getTotal());
+
+ return new Result<PageData<AgentTemplateVO>>().ok(pageData);
+ }
+
+ @GetMapping("/{id}")
+ @Operation(summary = "鑾峰彇妯℃澘璇︽儏")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<AgentTemplateVO> getAgentTemplateById(@PathVariable("id") String id) {
+ AgentTemplateEntity template = agentTemplateService.getById(id);
+ if (template == null) {
+ return ResultUtils.error("妯℃澘涓嶅瓨鍦�");
+ }
+
+ // 浣跨敤ConvertUtils杞崲涓篤O
+ AgentTemplateVO vo = ConvertUtils.sourceToTarget(template, AgentTemplateVO.class);
+
+ return ResultUtils.success(vo);
+ }
+
+ @PostMapping
+ @Operation(summary = "鍒涘缓妯℃澘")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<AgentTemplateEntity> createAgentTemplate(@Valid @RequestBody AgentTemplateEntity template) {
+ // 璁剧疆鎺掑簭鍊间负涓嬩竴涓彲鐢ㄧ殑搴忓彿
+ template.setSort(agentTemplateService.getNextAvailableSort());
+
+ boolean saved = agentTemplateService.save(template);
+ if (saved) {
+ return ResultUtils.success(template);
+ } else {
+ return ResultUtils.error("鍒涘缓妯℃澘澶辫触");
+ }
+ }
+
+ @PutMapping
+ @Operation(summary = "鏇存柊妯℃澘")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<AgentTemplateEntity> updateAgentTemplate(@Valid @RequestBody AgentTemplateEntity template) {
+ boolean updated = agentTemplateService.updateById(template);
+ if (updated) {
+ return ResultUtils.success(template);
+ } else {
+ return ResultUtils.error("鏇存柊妯℃澘澶辫触");
+ }
+ }
+
+ @DeleteMapping("/{id}")
+ @Operation(summary = "鍒犻櫎妯℃澘")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<String> deleteAgentTemplate(@PathVariable("id") String id) {
+ // 鍏堟煡璇㈣鍒犻櫎鐨勬ā鏉夸俊鎭紝鑾峰彇鍏舵帓搴忓��
+ AgentTemplateEntity template = agentTemplateService.getById(id);
+ if (template == null) {
+ return ResultUtils.error("妯℃澘涓嶅瓨鍦�");
+ }
+
+ Integer deletedSort = template.getSort();
+
+ // 鎵ц鍒犻櫎鎿嶄綔
+ boolean deleted = agentTemplateService.removeById(id);
+ if (deleted) {
+ // 鍒犻櫎鎴愬姛鍚庯紝閲嶆柊鎺掑簭鍓╀綑妯℃澘
+ agentTemplateService.reorderTemplatesAfterDelete(deletedSort);
+ return ResultUtils.success("鍒犻櫎妯℃澘鎴愬姛");
+ } else {
+ return ResultUtils.error("鍒犻櫎妯℃澘澶辫触");
+ }
+ }
+
+
+ // 娣诲姞鏂扮殑鎵归噺鍒犻櫎鏂规硶锛屼娇鐢ㄤ笉鍚岀殑URL
+ @PostMapping("/batch-remove")
+ @Operation(summary = "鎵归噺鍒犻櫎妯℃澘")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<String> batchRemoveAgentTemplates(@RequestBody List<String> ids) {
+ boolean deleted = agentTemplateService.removeByIds(ids);
+ if (deleted) {
+ return ResultUtils.success("鎵归噺鍒犻櫎鎴愬姛");
+ } else {
+ return ResultUtils.error("鎵归噺鍒犻櫎妯℃澘澶辫触");
+ }
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentVoicePrintController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentVoicePrintController.java
new file mode 100644
index 0000000..9b7555e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentVoicePrintController.java
@@ -0,0 +1,87 @@
+package xiaozhi.modules.agent.controller;
+
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.AllArgsConstructor;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.utils.Result;
+import xiaozhi.modules.agent.dto.AgentVoicePrintSaveDTO;
+import xiaozhi.modules.agent.dto.AgentVoicePrintUpdateDTO;
+import xiaozhi.modules.agent.service.AgentVoicePrintService;
+import xiaozhi.modules.agent.vo.AgentVoicePrintVO;
+import xiaozhi.modules.security.user.SecurityUser;
+import xiaozhi.modules.sys.service.SysParamsService;
+
+@Tag(name = "鏅鸿兘浣撳0绾圭鐞�")
+@AllArgsConstructor
+@RestController
+@RequestMapping("/agent/voice-print")
+public class AgentVoicePrintController {
+ private final AgentVoicePrintService agentVoicePrintService;
+ private final SysParamsService sysParamsService;
+
+ @PostMapping
+ @Operation(summary = "鍒涘缓鏅鸿兘浣撶殑澹扮汗")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> save(@RequestBody @Valid AgentVoicePrintSaveDTO dto) {
+ boolean b = agentVoicePrintService.insert(dto);
+ if (b) {
+ return new Result<>();
+ }
+ return new Result<Void>().error(ErrorCode.AGENT_VOICEPRINT_CREATE_FAILED);
+ }
+
+ @PutMapping
+ @Operation(summary = "鏇存柊鏅鸿兘浣撶殑瀵瑰簲澹扮汗")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> update(@RequestBody @Valid AgentVoicePrintUpdateDTO dto) {
+ Long userId = SecurityUser.getUserId();
+ boolean b = agentVoicePrintService.update(userId, dto);
+ if (b) {
+ return new Result<>();
+ }
+ return new Result<Void>().error(ErrorCode.AGENT_VOICEPRINT_UPDATE_FAILED);
+ }
+
+ @DeleteMapping("/{id}")
+ @Operation(summary = "鍒犻櫎鏅鸿兘浣撳搴斿0绾�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> delete(@PathVariable String id) {
+ Long userId = SecurityUser.getUserId();
+ // 鍏堝垹闄ゅ叧鑱旂殑璁惧
+ boolean delete = agentVoicePrintService.delete(userId, id);
+ if (delete) {
+ return new Result<>();
+ }
+ return new Result<Void>().error(ErrorCode.AGENT_VOICEPRINT_DELETE_FAILED);
+ }
+
+ @GetMapping("/list/{id}")
+ @Operation(summary = "鑾峰彇鐢ㄦ埛鎸囧畾鏅鸿兘浣撳0绾瑰垪琛�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<List<AgentVoicePrintVO>> list(@PathVariable String id) {
+ String voiceprintUrl = sysParamsService.getValue("server.voice_print", true);
+ if (StringUtils.isBlank(voiceprintUrl) || "null".equals(voiceprintUrl)) {
+ throw new RenException(ErrorCode.VOICEPRINT_API_NOT_CONFIGURED);
+ }
+ Long userId = SecurityUser.getUserId();
+ List<AgentVoicePrintVO> list = agentVoicePrintService.list(userId, id);
+ return new Result<List<AgentVoicePrintVO>>().ok(list);
+ }
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentChatTitleDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentChatTitleDao.java
new file mode 100644
index 0000000..0ca51d3
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentChatTitleDao.java
@@ -0,0 +1,12 @@
+package xiaozhi.modules.agent.dao;
+
+import org.apache.ibatis.annotations.Mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+import xiaozhi.modules.agent.entity.AgentChatTitleEntity;
+
+@Mapper
+public interface AgentChatTitleDao extends BaseMapper<AgentChatTitleEntity> {
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentContextProviderDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentContextProviderDao.java
new file mode 100644
index 0000000..d46ad3a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentContextProviderDao.java
@@ -0,0 +1,9 @@
+package xiaozhi.modules.agent.dao;
+
+import org.apache.ibatis.annotations.Mapper;
+import xiaozhi.common.dao.BaseDao;
+import xiaozhi.modules.agent.entity.AgentContextProviderEntity;
+
+@Mapper
+public interface AgentContextProviderDao extends BaseDao<AgentContextProviderEntity> {
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentCorrectWordMappingDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentCorrectWordMappingDao.java
new file mode 100644
index 0000000..6e327dc
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentCorrectWordMappingDao.java
@@ -0,0 +1,21 @@
+package xiaozhi.modules.agent.dao;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import xiaozhi.common.dao.BaseDao;
+import xiaozhi.modules.agent.entity.AgentCorrectWordMappingEntity;
+
+@Mapper
+public interface AgentCorrectWordMappingDao extends BaseDao<AgentCorrectWordMappingEntity> {
+
+ int deleteByAgentId(@Param("agentId") String agentId);
+
+ int deleteByFileId(@Param("fileId") String fileId);
+
+ int batchInsertMapping(@Param("list") List<AgentCorrectWordMappingEntity> mappings);
+
+ List<AgentCorrectWordMappingEntity> selectByAgentId(@Param("agentId") String agentId);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentDao.java
new file mode 100644
index 0000000..9ad1536
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentDao.java
@@ -0,0 +1,55 @@
+package xiaozhi.modules.agent.dao;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import org.apache.ibatis.annotations.Select;
+import xiaozhi.common.dao.BaseDao;
+import xiaozhi.modules.agent.entity.AgentEntity;
+import xiaozhi.modules.agent.vo.AgentInfoVO;
+
+@Mapper
+public interface AgentDao extends BaseDao<AgentEntity> {
+ /**
+ * 鑾峰彇鏅鸿兘浣撶殑璁惧鏁伴噺
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ * @return 璁惧鏁伴噺
+ */
+ Integer getDeviceCountByAgentId(@Param("agentId") String agentId);
+
+ /**
+ * 鏍规嵁璁惧MAC鍦板潃鏌ヨ瀵瑰簲璁惧鐨勯粯璁ゆ櫤鑳戒綋淇℃伅
+ *
+ * @param macAddress 璁惧MAC鍦板潃
+ * @return 榛樿鏅鸿兘浣撲俊鎭�
+ */
+ @Select(" SELECT a.* FROM ai_device d " +
+ " LEFT JOIN ai_agent a ON d.agent_id = a.id " +
+ " WHERE d.mac_address = #{macAddress} " +
+ " ORDER BY d.id DESC LIMIT 1")
+ AgentEntity getDefaultAgentByMacAddress(@Param("macAddress") String macAddress);
+
+ /**
+ * 鏍规嵁id鏌ヨagent淇℃伅锛屽寘鎷彃浠朵俊鎭�
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ */
+ AgentInfoVO selectAgentInfoById(@Param("agentId") String agentId);
+
+ /**
+ * 閿佸畾鏅鸿兘浣撲富璁板綍锛岀敤浜庝覆琛屽寲鍚屼竴鏅鸿兘浣撶殑閰嶇疆鍐欏叆
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ */
+ AgentEntity selectByIdForUpdate(@Param("agentId") String agentId);
+
+ /**
+ * 绮剧‘鍐欏叆蹇収瑕嗙洊鐨勬櫤鑳戒綋瀛楁锛屽寘鎷洰鏍囧揩鐓т腑鐨� null 鍊笺��
+ * 涓嶆洿鏂版墍灞炵敤鎴枫�佸垱寤轰俊鎭瓑涓嶅睘浜庡揩鐓х殑瀛楁銆�
+ *
+ * @param agent 宸插簲鐢ㄧ洰鏍囧揩鐓х殑鏅鸿兘浣�
+ * @return 鍙楀奖鍝嶈鏁�
+ */
+ int updateSnapshotFields(@Param("agent") AgentEntity agent);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentPluginMappingMapper.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentPluginMappingMapper.java
new file mode 100644
index 0000000..2fe8175
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentPluginMappingMapper.java
@@ -0,0 +1,22 @@
+package xiaozhi.modules.agent.dao;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import xiaozhi.modules.agent.entity.AgentPluginMapping;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+import java.util.List;
+
+/**
+* @description 閽堝琛ㄣ�恆i_agent_plugin_mapping(Agent涓庢彃浠剁殑鍞竴鏄犲皠琛�)銆戠殑鏁版嵁搴撴搷浣淢apper
+* @createDate 2025-05-25 22:33:17
+* @Entity xiaozhi.modules.agent.entity.AgentPluginMapping
+*/
+@Mapper
+public interface AgentPluginMappingMapper extends BaseMapper<AgentPluginMapping> {
+ List<AgentPluginMapping> selectPluginsByAgentId(@Param("agentId") String agentId);
+}
+
+
+
+
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentSnapshotDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentSnapshotDao.java
new file mode 100644
index 0000000..ec8f39e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentSnapshotDao.java
@@ -0,0 +1,29 @@
+package xiaozhi.modules.agent.dao;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import xiaozhi.common.dao.BaseDao;
+import xiaozhi.modules.agent.entity.AgentSnapshotEntity;
+
+@Mapper
+public interface AgentSnapshotDao extends BaseDao<AgentSnapshotEntity> {
+ Integer selectMaxVersionNo(@Param("agentId") String agentId);
+
+ AgentSnapshotEntity selectLatestSnapshot(@Param("agentId") String agentId);
+
+ AgentSnapshotEntity selectNextSnapshot(@Param("agentId") String agentId, @Param("versionNo") Integer versionNo);
+
+ int insertWithNextVersion(@Param("snapshot") AgentSnapshotEntity snapshot);
+
+ int deleteOlderThanKeepLimit(@Param("agentId") String agentId, @Param("keepLimit") int keepLimit);
+
+ List<AgentSnapshotEntity> selectLegacyRedactionBatch(@Param("afterId") String afterId,
+ @Param("limit") int limit,
+ @Param("targetRedactionVersion") int targetRedactionVersion);
+
+ int updateRedactedSnapshots(@Param("snapshots") List<AgentSnapshotEntity> snapshots,
+ @Param("redactionVersion") int redactionVersion);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentTagDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentTagDao.java
new file mode 100644
index 0000000..e50f7c3
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentTagDao.java
@@ -0,0 +1,24 @@
+package xiaozhi.modules.agent.dao;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import xiaozhi.common.dao.BaseDao;
+import xiaozhi.modules.agent.entity.AgentTagEntity;
+
+import java.util.List;
+
+@Mapper
+public interface AgentTagDao extends BaseDao<AgentTagEntity> {
+
+ List<AgentTagEntity> selectByAgentId(@Param("agentId") String agentId);
+
+ List<AgentTagEntity> selectByAgentIds(@Param("agentIds") List<String> agentIds);
+
+ List<AgentTagEntity> selectAll();
+
+ List<String> selectAgentIdsByTagName(@Param("tagName") String tagName);
+
+ List<AgentTagEntity> selectByTagNames(@Param("tagNames") List<String> tagNames);
+
+ int batchInsert(@Param("list") List<AgentTagEntity> tagList);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentTagRelationDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentTagRelationDao.java
new file mode 100644
index 0000000..7169b5d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentTagRelationDao.java
@@ -0,0 +1,18 @@
+package xiaozhi.modules.agent.dao;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import xiaozhi.common.dao.BaseDao;
+import xiaozhi.modules.agent.entity.AgentTagRelationEntity;
+
+import java.util.List;
+
+@Mapper
+public interface AgentTagRelationDao extends BaseDao<AgentTagRelationEntity> {
+
+ int deleteByAgentId(@Param("agentId") String agentId);
+
+ int insertRelation(AgentTagRelationEntity relation);
+
+ int batchInsertRelation(@Param("list") List<AgentTagRelationEntity> relations);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentTemplateDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentTemplateDao.java
new file mode 100644
index 0000000..c897615
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentTemplateDao.java
@@ -0,0 +1,17 @@
+package xiaozhi.modules.agent.dao;
+
+import org.apache.ibatis.annotations.Mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+import xiaozhi.modules.agent.entity.AgentTemplateEntity;
+
+/**
+ * @author chenerlei
+ * @description 閽堝琛ㄣ�恆i_agent_template(鏅鸿兘浣撻厤缃ā鏉胯〃)銆戠殑鏁版嵁搴撴搷浣淢apper
+ * @createDate 2025-03-22 11:48:18
+ */
+@Mapper
+public interface AgentTemplateDao extends BaseMapper<AgentTemplateEntity> {
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentVoicePrintDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentVoicePrintDao.java
new file mode 100644
index 0000000..1a5ee36
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentVoicePrintDao.java
@@ -0,0 +1,20 @@
+package xiaozhi.modules.agent.dao;
+
+import org.apache.ibatis.annotations.Mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+import xiaozhi.modules.agent.entity.AgentChatHistoryEntity;
+import xiaozhi.modules.agent.entity.AgentVoicePrintEntity;
+
+/**
+ * {@link AgentChatHistoryEntity} 鏅鸿兘浣撹亰澶╁巻鍙茶褰旸ao瀵硅薄
+ *
+ * @author Goody
+ * @version 1.0, 2025/4/30
+ * @since 1.0.0
+ */
+@Mapper
+public interface AgentVoicePrintDao extends BaseMapper<AgentVoicePrintEntity> {
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AiAgentChatAudioDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AiAgentChatAudioDao.java
new file mode 100644
index 0000000..355b25a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AiAgentChatAudioDao.java
@@ -0,0 +1,18 @@
+package xiaozhi.modules.agent.dao;
+
+import org.apache.ibatis.annotations.Mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+import xiaozhi.modules.agent.entity.AgentChatAudioEntity;
+
+/**
+ * {@link AgentChatAudioEntity} 鏅鸿兘浣撹亰澶╅煶棰戞暟鎹瓺ao瀵硅薄
+ *
+ * @author Goody
+ * @version 1.0, 2025/5/8
+ * @since 1.0.0
+ */
+@Mapper
+public interface AiAgentChatAudioDao extends BaseMapper<AgentChatAudioEntity> {
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AiAgentChatHistoryDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AiAgentChatHistoryDao.java
new file mode 100644
index 0000000..7c9bf1e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AiAgentChatHistoryDao.java
@@ -0,0 +1,50 @@
+package xiaozhi.modules.agent.dao;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+import xiaozhi.modules.agent.entity.AgentChatHistoryEntity;
+
+/**
+ * {@link AgentChatHistoryEntity} 鏅鸿兘浣撹亰澶╁巻鍙茶褰旸ao瀵硅薄
+ *
+ * @author Goody
+ * @version 1.0, 2025/4/30
+ * @since 1.0.0
+ */
+@Mapper
+public interface AiAgentChatHistoryDao extends BaseMapper<AgentChatHistoryEntity> {
+
+ /**
+ * 鏍规嵁鏅鸿兘浣揑D鍒犻櫎鑱婂ぉ鍘嗗彶璁板綍
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ */
+ void deleteHistoryByAgentId(String agentId);
+
+ /**
+ * 鏍规嵁鏅鸿兘浣揑D鍒犻櫎闊抽ID
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ */
+ void deleteAudioIdByAgentId(String agentId);
+
+ /**
+ * 鏍规嵁鏅鸿兘浣揑D鑾峰彇鎵�鏈夐煶棰慖D鍒楄〃
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ * @return 闊抽ID鍒楄〃
+ */
+ List<String> getAudioIdsByAgentId(String agentId);
+
+ /**
+ * 鎵归噺鍒犻櫎闊抽
+ *
+ * @param audioIds 闊抽ID鍒楄〃
+ */
+ void deleteAudioByIds(@Param("audioIds") List<String> audioIds);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentChatHistoryDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentChatHistoryDTO.java
new file mode 100644
index 0000000..fd49e52
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentChatHistoryDTO.java
@@ -0,0 +1,28 @@
+package xiaozhi.modules.agent.dto;
+
+import java.util.Date;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 鏅鸿兘浣撹亰澶╄褰旸TO
+ */
+@Data
+@Schema(description = "鏅鸿兘浣撹亰澶╄褰�")
+public class AgentChatHistoryDTO {
+ @Schema(description = "鍒涘缓鏃堕棿")
+ private Date createdAt;
+
+ @Schema(description = "娑堟伅绫诲瀷: 1-鐢ㄦ埛, 2-鏅鸿兘浣�")
+ private Byte chatType;
+
+ @Schema(description = "鑱婂ぉ鍐呭")
+ private String content;
+
+ @Schema(description = "闊抽ID")
+ private String audioId;
+
+ @Schema(description = "MAC鍦板潃")
+ private String macAddress;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentChatHistoryReportDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentChatHistoryReportDTO.java
new file mode 100644
index 0000000..ae58783
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentChatHistoryReportDTO.java
@@ -0,0 +1,33 @@
+package xiaozhi.modules.agent.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+/**
+ * 灏忔櫤璁惧鑱婂ぉ涓婃姤璇锋眰
+ *
+ * @author Haotian
+ * @version 1.0, 2025/5/8
+ */
+@Data
+@Schema(description = "灏忔櫤璁惧鑱婂ぉ涓婃姤璇锋眰")
+public class AgentChatHistoryReportDTO {
+ @Schema(description = "MAC鍦板潃", example = "00:11:22:33:44:55")
+ @NotBlank
+ private String macAddress;
+ @Schema(description = "浼氳瘽ID", example = "79578c31-f1fb-426a-900e-1e934215f05a")
+ @NotBlank
+ private String sessionId;
+ @Schema(description = "娑堟伅绫诲瀷: 1-鐢ㄦ埛, 2-鏅鸿兘浣�", example = "1")
+ @NotNull
+ private Byte chatType;
+ @Schema(description = "鑱婂ぉ鍐呭", example = "浣犲ソ鍛�")
+ @NotBlank
+ private String content;
+ @Schema(description = "base64缂栫爜鐨刼pus闊抽鏁版嵁", example = "")
+ private String audioBase64;
+ @Schema(description = "涓婃姤鏃堕棿锛屽崄浣嶆椂闂存埑锛岀┖鏃堕粯璁や娇鐢ㄥ綋鍓嶆椂闂�", example = "1745657732")
+ private Long reportTime;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentChatSessionDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentChatSessionDTO.java
new file mode 100644
index 0000000..841b697
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentChatSessionDTO.java
@@ -0,0 +1,31 @@
+package xiaozhi.modules.agent.dto;
+
+import java.time.LocalDateTime;
+
+import lombok.Data;
+
+/**
+ * 鏅鸿兘浣撲細璇濆垪琛―TO
+ */
+@Data
+public class AgentChatSessionDTO {
+ /**
+ * 浼氳瘽ID
+ */
+ private String sessionId;
+
+ /**
+ * 浼氳瘽鏃堕棿
+ */
+ private LocalDateTime createdAt;
+
+ /**
+ * 鑱婂ぉ鏉℃暟
+ */
+ private Integer chatCount;
+
+ /**
+ * 浼氳瘽鏍囬
+ */
+ private String title;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentChatSummaryDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentChatSummaryDTO.java
new file mode 100644
index 0000000..f6fbe66
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentChatSummaryDTO.java
@@ -0,0 +1,45 @@
+package xiaozhi.modules.agent.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 鏅鸿兘浣撹亰澶╄褰曟�荤粨DTO
+ */
+@Data
+@Schema(description = "鏅鸿兘浣撹亰澶╄褰曟�荤粨瀵硅薄")
+public class AgentChatSummaryDTO {
+
+ @Schema(description = "浼氳瘽ID")
+ private String sessionId;
+
+ @Schema(description = "鏅鸿兘浣揑D")
+ private String agentId;
+
+ @Schema(description = "鎬荤粨鍐呭")
+ private String summary;
+
+ @Schema(description = "鎬荤粨鐘舵��")
+ private boolean success;
+
+ @Schema(description = "閿欒淇℃伅")
+ private String errorMessage;
+
+ public AgentChatSummaryDTO() {
+ this.success = true;
+ }
+
+ public AgentChatSummaryDTO(String sessionId, String agentId, String summary) {
+ this.sessionId = sessionId;
+ this.agentId = agentId;
+ this.summary = summary;
+ this.success = true;
+ }
+
+ public AgentChatSummaryDTO(String sessionId, String errorMessage) {
+ this.sessionId = sessionId;
+ this.errorMessage = errorMessage;
+ this.success = false;
+ }
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentCreateDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentCreateDTO.java
new file mode 100644
index 0000000..59e7700
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentCreateDTO.java
@@ -0,0 +1,21 @@
+package xiaozhi.modules.agent.dto;
+
+import java.io.Serializable;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+/**
+ * 鏅鸿兘浣撳垱寤篋TO
+ * 涓撶敤浜庢柊澧炴櫤鑳戒綋锛屼笉鍖呭惈id銆乤gentCode鍜宻ort瀛楁锛岃繖浜涘瓧娈电敱绯荤粺鑷姩鐢熸垚/璁剧疆榛樿鍊�
+ */
+@Data
+@Schema(description = "鏅鸿兘浣撳垱寤哄璞�")
+public class AgentCreateDTO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "鏅鸿兘浣撳悕绉�", example = "瀹㈡湇鍔╂墜")
+ @NotBlank(message = "鏅鸿兘浣撳悕绉颁笉鑳戒负绌�")
+ private String agentName;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentDTO.java
new file mode 100644
index 0000000..693e1ca
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentDTO.java
@@ -0,0 +1,53 @@
+package xiaozhi.modules.agent.dto;
+
+import java.util.Date;
+import java.util.List;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import xiaozhi.modules.agent.dto.AgentTagDTO;
+
+/**
+ * 鏅鸿兘浣撴暟鎹紶杈撳璞�
+ * 鐢ㄤ簬鍦ㄦ湇鍔″眰鍜屾帶鍒跺櫒灞備箣闂翠紶閫掓櫤鑳戒綋鐩稿叧鐨勬暟鎹�
+ */
+@Data
+@Schema(description = "鏅鸿兘浣撳璞�")
+public class AgentDTO {
+ @Schema(description = "鏅鸿兘浣撶紪鐮�", example = "AGT_1234567890")
+ private String id;
+
+ @Schema(description = "鏅鸿兘浣撳悕绉�", example = "瀹㈡湇鍔╂墜")
+ private String agentName;
+
+ @Schema(description = "璇煶鍚堟垚妯″瀷鍚嶇О", example = "tts_model_01")
+ private String ttsModelName;
+
+ @Schema(description = "闊宠壊鍚嶇О", example = "voice_01")
+ private String ttsVoiceName;
+
+ @Schema(description = "澶ц瑷�妯″瀷鍚嶇О", example = "llm_model_01")
+ private String llmModelName;
+
+ @Schema(description = "瑙嗚妯″瀷鍚嶇О", example = "vllm_model_01")
+ private String vllmModelName;
+
+ @Schema(description = "璁板繂妯″瀷ID", example = "mem_model_01")
+ private String memModelId;
+
+ @Schema(description = "瑙掕壊璁惧畾鍙傛暟", example = "浣犳槸涓�涓笓涓氱殑瀹㈡湇鍔╂墜锛岃礋璐e洖绛旂敤鎴烽棶棰樺苟鎻愪緵甯姪")
+ private String systemPrompt;
+
+ @Schema(description = "鎬荤粨璁板繂", example = "鏋勫缓鍙敓闀跨殑鍔ㄦ�佽蹇嗙綉缁滐紝鍦ㄦ湁闄愮┖闂村唴淇濈暀鍏抽敭淇℃伅鐨勫悓鏃讹紝鏅鸿兘缁存姢淇℃伅婕斿彉杞ㄨ抗\n" +
+ "鏍规嵁瀵硅瘽璁板綍锛屾�荤粨user鐨勯噸瑕佷俊鎭紝浠ヤ究鍦ㄦ湭鏉ョ殑瀵硅瘽涓彁渚涙洿涓�у寲鐨勬湇鍔�", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
+ private String summaryMemory;
+
+ @Schema(description = "鏈�鍚庤繛鎺ユ椂闂�", example = "2024-03-20 10:00:00")
+ private Date lastConnectedAt;
+
+ @Schema(description = "璁惧鏁伴噺", example = "10")
+ private Integer deviceCount;
+
+ @Schema(description = "鏍囩鍒楄〃")
+ private List<AgentTagDTO> tags;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentMemoryDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentMemoryDTO.java
new file mode 100644
index 0000000..61e61a3
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentMemoryDTO.java
@@ -0,0 +1,19 @@
+package xiaozhi.modules.agent.dto;
+
+import java.io.Serializable;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 鏅鸿兘浣撹蹇嗘洿鏂癉TO
+ */
+@Data
+@Schema(description = "鏅鸿兘浣撹蹇嗘洿鏂板璞�")
+public class AgentMemoryDTO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "鎬荤粨璁板繂", example = "鏋勫缓鍙敓闀跨殑鍔ㄦ�佽蹇嗙綉缁滐紝鍦ㄦ湁闄愮┖闂村唴淇濈暀鍏抽敭淇℃伅鐨勫悓鏃讹紝鏅鸿兘缁存姢淇℃伅婕斿彉杞ㄨ抗\n" +
+ "鏍规嵁瀵硅瘽璁板綍锛屾�荤粨user鐨勯噸瑕佷俊鎭紝浠ヤ究鍦ㄦ湭鏉ョ殑瀵硅瘽涓彁渚涙洿涓�у寲鐨勬湇鍔�", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
+ private String summaryMemory;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentSnapshotDataDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentSnapshotDataDTO.java
new file mode 100644
index 0000000..328c73c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentSnapshotDataDTO.java
@@ -0,0 +1,40 @@
+package xiaozhi.modules.agent.dto;
+
+import java.io.Serializable;
+import java.util.List;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@Schema(description = "鏅鸿兘浣撳揩鐓ф暟鎹�")
+public class AgentSnapshotDataDTO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private String agentCode;
+ private String agentName;
+ private String asrModelId;
+ private String vadModelId;
+ private String llmModelId;
+ private String slmModelId;
+ private String vllmModelId;
+ private String ttsModelId;
+ private String ttsVoiceId;
+ private String ttsLanguage;
+ private Integer ttsVolume;
+ private Integer ttsRate;
+ private Integer ttsPitch;
+ private String memModelId;
+ private String intentModelId;
+ private Integer chatHistoryConf;
+ private String systemPrompt;
+ private String summaryMemory;
+ private String langCode;
+ private String language;
+ private Integer sort;
+ private List<AgentUpdateDTO.FunctionInfo> functions;
+ private List<ContextProviderDTO> contextProviders;
+ private List<String> correctWordFileIds;
+ private List<String> tagNames;
+ private List<AgentSnapshotTagDTO> tags;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentSnapshotPageDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentSnapshotPageDTO.java
new file mode 100644
index 0000000..3f3f5d6
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentSnapshotPageDTO.java
@@ -0,0 +1,28 @@
+package xiaozhi.modules.agent.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@Schema(description = "鏅鸿兘浣撳揩鐓у垎椤垫煡璇㈠弬鏁�")
+public class AgentSnapshotPageDTO {
+ @Schema(description = "褰撳墠椤电爜锛屼粠1寮�濮�", example = "1")
+ private Integer page = 1;
+
+ @Schema(description = "姣忛〉鏁伴噺", example = "10")
+ private Integer limit = 10;
+
+ @Schema(description = "鐗堟湰閿氱偣锛屽彧鏌ヨ灏忎簬绛変簬璇ョ増鏈彿鐨勫巻鍙插揩鐓�", example = "20")
+ private Integer maxVersionNo;
+
+ public int pageOrDefault() {
+ return page == null || page < 1 ? 1 : page;
+ }
+
+ public int limitOrDefault() {
+ if (limit == null || limit < 1) {
+ return 10;
+ }
+ return limit;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentSnapshotRestoreDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentSnapshotRestoreDTO.java
new file mode 100644
index 0000000..aa003fa
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentSnapshotRestoreDTO.java
@@ -0,0 +1,13 @@
+package xiaozhi.modules.agent.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+@Data
+@Schema(description = "鏅鸿兘浣撳揩鐓ф仮澶嶈姹�")
+public class AgentSnapshotRestoreDTO {
+ @NotBlank
+ @Schema(description = "棰勮鏃剁敱鏈嶅姟绔敓鎴愮殑褰撳墠閰嶇疆鐘舵�佹寚绾�")
+ private String currentStateToken;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentSnapshotTagDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentSnapshotTagDTO.java
new file mode 100644
index 0000000..6e93dd0
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentSnapshotTagDTO.java
@@ -0,0 +1,16 @@
+package xiaozhi.modules.agent.dto;
+
+import java.io.Serializable;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@Schema(description = "鏅鸿兘浣撳揩鐓ф爣绛�")
+public class AgentSnapshotTagDTO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private String id;
+ private String tagName;
+ private Integer sort;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentTagDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentTagDTO.java
new file mode 100644
index 0000000..5dfc5f5
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentTagDTO.java
@@ -0,0 +1,20 @@
+package xiaozhi.modules.agent.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+@Schema(description = "鏅鸿兘浣撴爣绛綝TO")
+public class AgentTagDTO implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "鏍囩ID")
+ private String id;
+
+ @Schema(description = "鏍囩鍚嶇О")
+ private String tagName;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentUpdateDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentUpdateDTO.java
new file mode 100644
index 0000000..5859c4f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentUpdateDTO.java
@@ -0,0 +1,143 @@
+package xiaozhi.modules.agent.dto;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import xiaozhi.common.utils.JsonUtils;
+
+/**
+ * 鏅鸿兘浣撴洿鏂癉TO
+ * 涓撶敤浜庢洿鏂版櫤鑳戒綋锛宨d瀛楁鏄繀闇�鐨勶紝鐢ㄤ簬鏍囪瘑瑕佹洿鏂扮殑鏅鸿兘浣�
+ * 鍏朵粬瀛楁鍧囦负闈炲繀濉紝鍙洿鏂版彁渚涚殑瀛楁
+ */
+@Data
+@Schema(description = "鏅鸿兘浣撴洿鏂板璞�")
+public class AgentUpdateDTO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "鏅鸿兘浣撶紪鐮�", example = "AGT_1234567890", nullable = true)
+ private String agentCode;
+
+ @Schema(description = "鏅鸿兘浣撳悕绉�", example = "瀹㈡湇鍔╂墜", nullable = true)
+ private String agentName;
+
+ @Schema(description = "璇煶璇嗗埆妯″瀷鏍囪瘑", example = "asr_model_02", nullable = true)
+ private String asrModelId;
+
+ @Schema(description = "璇煶娲诲姩妫�娴嬫爣璇�", example = "vad_model_02", nullable = true)
+ private String vadModelId;
+
+ @Schema(description = "澶ц瑷�妯″瀷鏍囪瘑", example = "llm_model_02", nullable = true)
+ private String llmModelId;
+
+ @Schema(description = "灏忔ā鍨嬫爣璇�", example = "slm_model_02", nullable = true)
+ private String slmModelId;
+
+ @Schema(description = "VLLM妯″瀷鏍囪瘑", example = "vllm_model_02", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
+ private String vllmModelId;
+
+ @Schema(description = "璇煶鍚堟垚妯″瀷鏍囪瘑", example = "tts_model_02", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
+ private String ttsModelId;
+
+ @Schema(description = "闊宠壊鏍囪瘑", example = "voice_02", nullable = true)
+ private String ttsVoiceId;
+
+ @Schema(description = "闊宠壊璇█", example = "鏅�氳瘽", nullable = true)
+ private String ttsLanguage;
+
+ @Schema(description = "TTS闊抽噺", example = "50", nullable = true)
+ private Integer ttsVolume;
+
+ @Schema(description = "TTS璇��", example = "50", nullable = true)
+ private Integer ttsRate;
+
+ @Schema(description = "TTS闊宠皟", example = "50", nullable = true)
+ private Integer ttsPitch;
+
+ @Schema(description = "璁板繂妯″瀷鏍囪瘑", example = "mem_model_02", nullable = true)
+ private String memModelId;
+
+ @Schema(description = "鎰忓浘妯″瀷鏍囪瘑", example = "intent_model_02", nullable = true)
+ private String intentModelId;
+
+ @Schema(description = "鎻掍欢鍑芥暟淇℃伅", nullable = true)
+ private List<FunctionInfo> functions;
+
+ @Schema(description = "瑙掕壊璁惧畾鍙傛暟", example = "浣犳槸涓�涓笓涓氱殑瀹㈡湇鍔╂墜锛岃礋璐e洖绛旂敤鎴烽棶棰樺苟鎻愪緵甯姪", nullable = true)
+ private String systemPrompt;
+
+ @Schema(description = "鎬荤粨璁板繂", example = "鏋勫缓鍙敓闀跨殑鍔ㄦ�佽蹇嗙綉缁滐紝鍦ㄦ湁闄愮┖闂村唴淇濈暀鍏抽敭淇℃伅鐨勫悓鏃讹紝鏅鸿兘缁存姢淇℃伅婕斿彉杞ㄨ抗\n"
+ + "鏍规嵁瀵硅瘽璁板綍锛屾�荤粨user鐨勯噸瑕佷俊鎭紝浠ヤ究鍦ㄦ湭鏉ョ殑瀵硅瘽涓彁渚涙洿涓�у寲鐨勬湇鍔�", nullable = true)
+ private String summaryMemory;
+
+ @Schema(description = "鑱婂ぉ璁板綍閰嶇疆锛�0涓嶈褰� 1浠呰褰曟枃鏈� 2璁板綍鏂囨湰鍜岃闊筹級", example = "3", nullable = true)
+ private Integer chatHistoryConf;
+
+ @Schema(description = "璇█缂栫爜", example = "zh_CN", nullable = true)
+ private String langCode;
+
+ @Schema(description = "浜や簰璇", example = "涓枃", nullable = true)
+ private String language;
+
+ @Schema(description = "鎺掑簭", example = "1", nullable = true)
+ private Integer sort;
+
+ @Schema(description = "涓婁笅鏂囨簮閰嶇疆", nullable = true)
+ private List<ContextProviderDTO> contextProviders;
+
+ @Schema(description = "鏇挎崲璇嶆枃浠禝D鍒楄〃", nullable = true)
+ private List<String> correctWordFileIds;
+
+ @Schema(description = "鏍囩鍚嶇О鍒楄〃", nullable = true)
+ private List<String> tagNames;
+
+ @Schema(description = "鏍囩ID鍒楄〃", nullable = true)
+ private List<String> tagIds;
+
+ @Data
+ @Schema(description = "鎻掍欢鍑芥暟淇℃伅")
+ public static class FunctionInfo implements Serializable {
+ private static final TypeReference<HashMap<String, Object>> PARAM_INFO_TYPE = new TypeReference<>() {
+ };
+
+ @Schema(description = "鎻掍欢ID", example = "plugin_01")
+ private String pluginId;
+
+ @Schema(description = "鍑芥暟鍙傛暟淇℃伅", nullable = true)
+ private HashMap<String, Object> paramInfo = new HashMap<>();
+
+ public void setParamInfo(Object paramInfo) {
+ this.paramInfo = normalizeParamInfo(paramInfo);
+ }
+
+ private static HashMap<String, Object> normalizeParamInfo(Object paramInfo) {
+ if (paramInfo == null) {
+ return new HashMap<>();
+ }
+ if (paramInfo instanceof String value) {
+ if (value.trim().isEmpty()) {
+ return new HashMap<>();
+ }
+ return JsonUtils.parseObject(value, PARAM_INFO_TYPE);
+ }
+ if (paramInfo instanceof Map<?, ?> value) {
+ HashMap<String, Object> normalized = new HashMap<>();
+ value.forEach((key, val) -> {
+ if (key != null) {
+ normalized.put(String.valueOf(key), val);
+ }
+ });
+ return normalized;
+ }
+ return JsonUtils.parseObject(JsonUtils.toJsonString(paramInfo), PARAM_INFO_TYPE);
+ }
+
+ private static final long serialVersionUID = 1L;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentVoicePrintSaveDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentVoicePrintSaveDTO.java
new file mode 100644
index 0000000..0ff3b0c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentVoicePrintSaveDTO.java
@@ -0,0 +1,28 @@
+package xiaozhi.modules.agent.dto;
+
+import lombok.Data;
+
+/**
+ * 淇濆瓨鏅鸿兘浣撳0绾圭殑dto
+ *
+ * @author zjy
+ */
+@Data
+public class AgentVoicePrintSaveDTO {
+ /**
+ * 鍏宠仈鐨勬櫤鑳戒綋id
+ */
+ private String agentId;
+ /**
+ * 闊抽鏂囦欢id
+ */
+ private String audioId;
+ /**
+ * 澹扮汗鏉ユ簮鐨勪汉濮撳悕
+ */
+ private String sourceName;
+ /**
+ * 鎻忚堪澹扮汗鏉ユ簮鐨勪汉
+ */
+ private String introduce;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentVoicePrintUpdateDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentVoicePrintUpdateDTO.java
new file mode 100644
index 0000000..60643e2
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentVoicePrintUpdateDTO.java
@@ -0,0 +1,28 @@
+package xiaozhi.modules.agent.dto;
+
+import lombok.Data;
+
+/**
+ * 淇敼鏅鸿兘浣撳0绾圭殑dto
+ *
+ * @author zjy
+ */
+@Data
+public class AgentVoicePrintUpdateDTO {
+ /**
+ * 鏅鸿兘浣撳0绾筰d
+ */
+ private String id;
+ /**
+ * 闊抽鏂囦欢id
+ */
+ private String audioId;
+ /**
+ * 澹扮汗鏉ユ簮鐨勪汉濮撳悕
+ */
+ private String sourceName;
+ /**
+ * 鎻忚堪澹扮汗鏉ユ簮鐨勪汉
+ */
+ private String introduce;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/ContextProviderDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/ContextProviderDTO.java
new file mode 100644
index 0000000..0b8edfd
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/ContextProviderDTO.java
@@ -0,0 +1,19 @@
+package xiaozhi.modules.agent.dto;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@Schema(description = "涓婁笅鏂囨簮閰嶇疆DTO")
+public class ContextProviderDTO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "URL鍦板潃")
+ private String url;
+
+ @Schema(description = "璇锋眰澶�")
+ private Map<String, Object> headers;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/IdentifyVoicePrintResponse.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/IdentifyVoicePrintResponse.java
new file mode 100644
index 0000000..14ce57c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/IdentifyVoicePrintResponse.java
@@ -0,0 +1,21 @@
+package xiaozhi.modules.agent.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import lombok.Data;
+
+/**
+ * 澹扮汗璇嗗埆鎺ュ彛杩斿洖鐨勫璞�
+ */
+@Data
+public class IdentifyVoicePrintResponse {
+ /**
+ * 鏈�鍖归厤鐨勫0绾筰d
+ */
+ @JsonProperty("speaker_id")
+ private String speakerId;
+ /**
+ * 澹扮汗鐨勫垎鏁�
+ */
+ private Double score;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentChatAudioEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentChatAudioEntity.java
new file mode 100644
index 0000000..ac30d9d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentChatAudioEntity.java
@@ -0,0 +1,29 @@
+package xiaozhi.modules.agent.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import lombok.Data;
+
+/**
+ * 鏅鸿兘浣撹亰澶╅煶棰戞暟鎹〃
+ *
+ * @author Goody
+ * @version 1.0, 2025/5/8
+ * @since 1.0.0
+ */
+@Data
+@TableName("ai_agent_chat_audio")
+public class AgentChatAudioEntity {
+ /**
+ * 涓婚敭ID
+ */
+ @TableId(type = IdType.ASSIGN_UUID)
+ private String id;
+
+ /**
+ * 闊抽opus鏁版嵁
+ */
+ private byte[] audio;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentChatHistoryEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentChatHistoryEntity.java
new file mode 100644
index 0000000..679c814
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentChatHistoryEntity.java
@@ -0,0 +1,81 @@
+package xiaozhi.modules.agent.entity;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 鏅鸿兘浣撹亰澶╄褰曡〃
+ *
+ * @author Goody
+ * @version 1.0, 2025/4/30
+ * @since 1.0.0
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName(value = "ai_agent_chat_history")
+public class AgentChatHistoryEntity {
+ /**
+ * 涓婚敭ID
+ */
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * MAC鍦板潃
+ */
+ @TableField(value = "mac_address")
+ private String macAddress;
+
+ /**
+ * 鏅鸿兘浣搃d
+ */
+ @TableField(value = "agent_id")
+ private String agentId;
+
+ /**
+ * 浼氳瘽ID
+ */
+ @TableField(value = "session_id")
+ private String sessionId;
+
+ /**
+ * 娑堟伅绫诲瀷: 1-鐢ㄦ埛, 2-鏅鸿兘浣�
+ */
+ @TableField(value = "chat_type")
+ private Byte chatType;
+
+ /**
+ * 鑱婂ぉ鍐呭
+ */
+ @TableField(value = "content")
+ private String content;
+
+ /**
+ * 闊抽base64鏁版嵁
+ */
+ @TableField(value = "audio_id")
+ private String audioId;
+
+ /**
+ * 鍒涘缓鏃堕棿
+ */
+ @TableField(value = "created_at")
+ private Date createdAt;
+
+ /**
+ * 鏇存柊鏃堕棿
+ */
+ @TableField(value = "updated_at")
+ private Date updatedAt;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentChatTitleEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentChatTitleEntity.java
new file mode 100644
index 0000000..d936136
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentChatTitleEntity.java
@@ -0,0 +1,36 @@
+package xiaozhi.modules.agent.entity;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName(value = "ai_agent_chat_title")
+public class AgentChatTitleEntity {
+
+ @TableId(type = IdType.ASSIGN_UUID)
+ private String id;
+
+ @TableField(value = "session_id")
+ private String sessionId;
+
+ @TableField(value = "title")
+ private String title;
+
+ @TableField(value = "created_at")
+ private Date createdAt;
+
+ @TableField(value = "updated_at")
+ private Date updatedAt;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentContextProviderEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentContextProviderEntity.java
new file mode 100644
index 0000000..4269b70
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentContextProviderEntity.java
@@ -0,0 +1,43 @@
+package xiaozhi.modules.agent.entity;
+
+import java.util.Date;
+import java.util.List;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import xiaozhi.modules.agent.dto.ContextProviderDTO;
+import xiaozhi.modules.agent.typehandler.ContextProviderListTypeHandler;
+
+@Data
+@TableName(value = "ai_agent_context_provider", autoResultMap = true)
+@Schema(description = "鏅鸿兘浣撲笂涓嬫枃婧愰厤缃�")
+public class AgentContextProviderEntity {
+
+ @TableId(type = IdType.ASSIGN_UUID)
+ @Schema(description = "涓婚敭")
+ private String id;
+
+ @Schema(description = "鏅鸿兘浣揑D")
+ private String agentId;
+
+ @Schema(description = "涓婁笅鏂囨簮閰嶇疆")
+ @TableField(typeHandler = ContextProviderListTypeHandler.class)
+ private List<ContextProviderDTO> contextProviders;
+
+ @Schema(description = "鍒涘缓鑰�")
+ private Long creator;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ private Date createdAt;
+
+ @Schema(description = "鏇存柊鑰�")
+ private Long updater;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ private Date updatedAt;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentCorrectWordMappingEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentCorrectWordMappingEntity.java
new file mode 100644
index 0000000..6b7d0c5
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentCorrectWordMappingEntity.java
@@ -0,0 +1,38 @@
+package xiaozhi.modules.agent.entity;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@TableName("ai_agent_correct_word_mapping")
+@Schema(description = "鏅鸿兘浣撴浛鎹㈣瘝鏂囦欢鍏宠仈")
+public class AgentCorrectWordMappingEntity {
+
+ @TableId(type = IdType.ASSIGN_UUID)
+ @Schema(description = "涓婚敭")
+ private String id;
+
+ @Schema(description = "鏅鸿兘浣揑D")
+ private String agentId;
+
+ @Schema(description = "鏇挎崲璇嶆枃浠禝D")
+ private String fileId;
+
+ @Schema(description = "鍒涘缓鑰�")
+ private Long creator;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ private Date createdAt;
+
+ @Schema(description = "鏇存柊鑰�")
+ private Long updater;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ private Date updatedAt;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentEntity.java
new file mode 100644
index 0000000..f9a390a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentEntity.java
@@ -0,0 +1,100 @@
+package xiaozhi.modules.agent.entity;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@TableName("ai_agent")
+@Schema(description = "鏅鸿兘浣撲俊鎭�")
+public class AgentEntity {
+
+ @TableId(type = IdType.ASSIGN_UUID)
+ @Schema(description = "鏅鸿兘浣撳敮涓�鏍囪瘑")
+ private String id;
+
+ @Schema(description = "鎵�灞炵敤鎴稩D")
+ private Long userId;
+
+ @Schema(description = "鏅鸿兘浣撶紪鐮�")
+ private String agentCode;
+
+ @Schema(description = "鏅鸿兘浣撳悕绉�")
+ private String agentName;
+
+ @Schema(description = "璇煶璇嗗埆妯″瀷鏍囪瘑")
+ private String asrModelId;
+
+ @Schema(description = "璇煶娲诲姩妫�娴嬫爣璇�")
+ private String vadModelId;
+
+ @Schema(description = "澶ц瑷�妯″瀷鏍囪瘑")
+ private String llmModelId;
+
+ @Schema(description = "灏忔ā鍨嬫爣璇�")
+ private String slmModelId;
+
+ @Schema(description = "VLLM妯″瀷鏍囪瘑")
+ private String vllmModelId;
+
+ @Schema(description = "璇煶鍚堟垚妯″瀷鏍囪瘑")
+ private String ttsModelId;
+
+ @Schema(description = "闊宠壊鏍囪瘑")
+ private String ttsVoiceId;
+
+ @Schema(description = "闊宠壊璇█")
+ private String ttsLanguage;
+
+ @Schema(description = "TTS闊抽噺")
+ private Integer ttsVolume;
+
+ @Schema(description = "TTS璇��")
+ private Integer ttsRate;
+
+ @Schema(description = "TTS闊宠皟")
+ private Integer ttsPitch;
+
+ @Schema(description = "璁板繂妯″瀷鏍囪瘑")
+ private String memModelId;
+
+ @Schema(description = "鎰忓浘妯″瀷鏍囪瘑")
+ private String intentModelId;
+
+ @Schema(description = "鑱婂ぉ璁板綍閰嶇疆锛�0涓嶈褰� 1浠呰褰曟枃鏈� 2璁板綍鏂囨湰鍜岃闊筹級")
+ private Integer chatHistoryConf;
+
+ @Schema(description = "瑙掕壊璁惧畾鍙傛暟")
+ private String systemPrompt;
+
+ @Schema(description = "鎬荤粨璁板繂", example = "鏋勫缓鍙敓闀跨殑鍔ㄦ�佽蹇嗙綉缁滐紝鍦ㄦ湁闄愮┖闂村唴淇濈暀鍏抽敭淇℃伅鐨勫悓鏃讹紝鏅鸿兘缁存姢淇℃伅婕斿彉杞ㄨ抗\n" +
+ "鏍规嵁瀵硅瘽璁板綍锛屾�荤粨user鐨勯噸瑕佷俊鎭紝浠ヤ究鍦ㄦ湭鏉ョ殑瀵硅瘽涓彁渚涙洿涓�у寲鐨勬湇鍔�", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
+ private String summaryMemory;
+
+ @Schema(description = "璇█缂栫爜")
+ private String langCode;
+
+ @Schema(description = "浜や簰璇")
+ private String language;
+
+ @Schema(description = "鎺掑簭")
+ private Integer sort;
+
+ @Schema(description = "鍒涘缓鑰�")
+ private Long creator;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ private Date createdAt;
+
+ @Schema(description = "鏇存柊鑰�")
+ private Long updater;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ private Date updatedAt;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentPluginMapping.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentPluginMapping.java
new file mode 100644
index 0000000..ae99b0e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentPluginMapping.java
@@ -0,0 +1,54 @@
+package xiaozhi.modules.agent.entity;
+
+import java.io.Serializable;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * Agent涓庢彃浠剁殑鍞竴鏄犲皠琛�
+ *
+ * @TableName ai_agent_plugin_mapping
+ */
+@Data
+@TableName(value = "ai_agent_plugin_mapping")
+@Schema(description = "Agent涓庢彃浠剁殑鍞竴鏄犲皠琛�")
+public class AgentPluginMapping implements Serializable {
+ /**
+ * 涓婚敭
+ */
+ @TableId(type = IdType.ASSIGN_ID)
+ @Schema(description = "鏄犲皠淇℃伅涓婚敭ID")
+ private Long id;
+
+ /**
+ * 鏅鸿兘浣揑D
+ */
+ @Schema(description = "鏅鸿兘浣揑D")
+ private String agentId;
+
+ /**
+ * 鎻掍欢ID
+ */
+ @Schema(description = "鎻掍欢ID")
+ private String pluginId;
+
+ /**
+ * 鎻掍欢鍙傛暟(Json)鏍煎紡
+ */
+ @Schema(description = "鎻掍欢鍙傛暟(Json)鏍煎紡")
+ private String paramInfo;
+
+ // 鍐椾綑瀛楁锛岀敤浜庢柟渚垮湪鏍规嵁id鏌ヨ鎻掍欢鏃讹紝瀵圭収鏌ュ嚭鎻掍欢鐨凱rovider_code,璇﹁dao灞倄ml鏂囦欢
+ @TableField(exist = false)
+ @Schema(description = "鎻掍欢provider_code, 瀵瑰簲琛╝i_model_provider")
+ private String providerCode;
+
+ @TableField(exist = false)
+ private static final long serialVersionUID = 1L;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentSnapshotEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentSnapshotEntity.java
new file mode 100644
index 0000000..c4ad695
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentSnapshotEntity.java
@@ -0,0 +1,53 @@
+package xiaozhi.modules.agent.entity;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@TableName("ai_agent_snapshot")
+@Schema(description = "鏅鸿兘浣撻厤缃揩鐓�")
+public class AgentSnapshotEntity {
+
+ @TableId(type = IdType.ASSIGN_UUID)
+ @Schema(description = "蹇収ID")
+ private String id;
+
+ @Schema(description = "鏅鸿兘浣揑D")
+ private String agentId;
+
+ @Schema(description = "鎵�灞炵敤鎴稩D")
+ private Long userId;
+
+ @Schema(description = "鐗堟湰鍙�")
+ private Integer versionNo;
+
+ @Schema(description = "蹇収鏁版嵁JSON")
+ private String snapshotData;
+
+ @Schema(description = "鍙樻洿瀛楁JSON")
+ private String changedFields;
+
+ @Schema(description = "蹇収鏉ユ簮")
+ private String source;
+
+ @Schema(description = "鎭㈠鏉ユ簮蹇収ID")
+ private String restoreFromSnapshotId;
+
+ @Schema(description = "鎭㈠鏉ユ簮鐗堟湰鍙�")
+ private Integer restoreFromVersionNo;
+
+ @Schema(description = "鍒涘缓鑰�")
+ private Long creator;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ private Date createdAt;
+
+ @Schema(description = "蹇収鏁版嵁鑴辨晱瑙勫垯鐗堟湰")
+ private Integer redactionVersion;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTagEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTagEntity.java
new file mode 100644
index 0000000..82555c3
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTagEntity.java
@@ -0,0 +1,41 @@
+package xiaozhi.modules.agent.entity;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@TableName("ai_agent_tag")
+@Schema(description = "鏅鸿兘浣撴爣绛�")
+public class AgentTagEntity {
+
+ @TableId(type = IdType.ASSIGN_UUID)
+ @Schema(description = "涓婚敭")
+ private String id;
+
+ @Schema(description = "鏍囩鍚嶇О")
+ private String tagName;
+
+ @Schema(description = "鎺掑簭")
+ private Integer sort;
+
+ @Schema(description = "鍒涘缓鑰�")
+ private Long creator;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ private Date createdAt;
+
+ @Schema(description = "鏇存柊鑰�")
+ private Long updater;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ private Date updatedAt;
+
+ @Schema(description = "鍒犻櫎鏍囪")
+ private Integer deleted;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTagRelationEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTagRelationEntity.java
new file mode 100644
index 0000000..cc345d7
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTagRelationEntity.java
@@ -0,0 +1,41 @@
+package xiaozhi.modules.agent.entity;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@TableName("ai_agent_tag_relation")
+@Schema(description = "鏅鸿兘浣撴爣绛惧叧鑱�")
+public class AgentTagRelationEntity {
+
+ @TableId(type = IdType.ASSIGN_UUID)
+ @Schema(description = "涓婚敭")
+ private String id;
+
+ @Schema(description = "鏅鸿兘浣揑D")
+ private String agentId;
+
+ @Schema(description = "鏍囩ID")
+ private String tagId;
+
+ @Schema(description = "鎺掑簭")
+ private Integer sort;
+
+ @Schema(description = "鍒涘缓鑰�")
+ private Long creator;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ private Date createdAt;
+
+ @Schema(description = "鏇存柊鑰�")
+ private Long updater;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ private Date updatedAt;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTemplateEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTemplateEntity.java
new file mode 100644
index 0000000..b06cda9
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTemplateEntity.java
@@ -0,0 +1,149 @@
+package xiaozhi.modules.agent.entity;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import lombok.Data;
+
+/**
+ * 鏅鸿兘浣撻厤缃ā鏉胯〃
+ *
+ * @TableName ai_agent_template
+ */
+@TableName(value = "ai_agent_template")
+@Data
+public class AgentTemplateEntity implements Serializable {
+ /**
+ * 鏅鸿兘浣撳敮涓�鏍囪瘑
+ */
+ @TableId(type = IdType.ASSIGN_UUID)
+ private String id;
+
+ /**
+ * 鏅鸿兘浣撶紪鐮�
+ */
+ private String agentCode;
+
+ /**
+ * 鏅鸿兘浣撳悕绉�
+ */
+ private String agentName;
+
+ /**
+ * 璇煶璇嗗埆妯″瀷鏍囪瘑
+ */
+ private String asrModelId;
+
+ /**
+ * 璇煶娲诲姩妫�娴嬫爣璇�
+ */
+ private String vadModelId;
+
+ /**
+ * 澶ц瑷�妯″瀷鏍囪瘑
+ */
+ private String llmModelId;
+
+ /**
+ * VLLM妯″瀷鏍囪瘑
+ */
+ private String vllmModelId;
+
+ /**
+ * 璇煶鍚堟垚妯″瀷鏍囪瘑
+ */
+ private String ttsModelId;
+
+ /**
+ * 闊宠壊鏍囪瘑
+ */
+ private String ttsVoiceId;
+
+ /**
+ * 闊宠壊璇█
+ */
+ private String ttsLanguage;
+
+ /**
+ * TTS闊抽噺
+ */
+ private Integer ttsVolume;
+
+ /**
+ * TTS璇��
+ */
+ private Integer ttsRate;
+
+ /**
+ * TTS闊宠皟
+ */
+ private Integer ttsPitch;
+
+ /**
+ * 璁板繂妯″瀷鏍囪瘑
+ */
+ private String memModelId;
+
+ /**
+ * 鎰忓浘妯″瀷鏍囪瘑
+ */
+ private String intentModelId;
+
+ /**
+ * 鑱婂ぉ璁板綍閰嶇疆锛�0涓嶈褰� 1浠呰褰曟枃鏈� 2璁板綍鏂囨湰鍜岃闊筹級
+ */
+ private Integer chatHistoryConf;
+
+ /**
+ * 瑙掕壊璁惧畾鍙傛暟
+ */
+ private String systemPrompt;
+
+ /**
+ * 鎬荤粨璁板繂
+ */
+ private String summaryMemory;
+ /**
+ * 璇█缂栫爜
+ */
+ private String langCode;
+
+ /**
+ * 浜や簰璇
+ */
+ private String language;
+
+ /**
+ * 鎺掑簭鏉冮噸
+ */
+ private Integer sort;
+
+ /**
+ * 鍒涘缓鑰� ID
+ */
+ private Long creator;
+
+ /**
+ * 鍒涘缓鏃堕棿
+ */
+ private Date createdAt;
+
+ /**
+ * 鏇存柊鑰� ID
+ */
+ private Long updater;
+
+ /**
+ * 鏇存柊鏃堕棿
+ */
+ private Date updatedAt;
+
+ @TableField(exist = false)
+ private static final long serialVersionUID = 1L;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentVoicePrintEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentVoicePrintEntity.java
new file mode 100644
index 0000000..4998b28
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentVoicePrintEntity.java
@@ -0,0 +1,64 @@
+package xiaozhi.modules.agent.entity;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import lombok.Data;
+
+/**
+ * 鏅鸿兘浣撳0绾硅〃
+ *
+ * @author zjy
+ */
+@TableName(value = "ai_agent_voice_print")
+@Data
+public class AgentVoicePrintEntity {
+ /**
+ * 涓婚敭id
+ */
+ @TableId(type = IdType.ASSIGN_UUID)
+ private String id;
+ /**
+ * 鍏宠仈鐨勬櫤鑳戒綋id
+ */
+ private String agentId;
+ /**
+ * 鍏宠仈鐨勯煶棰慽d
+ */
+ private String audioId;
+ /**
+ * 澹扮汗鏉ユ簮鐨勪汉濮撳悕
+ */
+ private String sourceName;
+ /**
+ * 鎻忚堪澹扮汗鏉ユ簮鐨勪汉
+ */
+ private String introduce;
+
+ /**
+ * 鍒涘缓鑰�
+ */
+ @TableField(fill = FieldFill.INSERT)
+ private Long creator;
+ /**
+ * 鍒涘缓鏃堕棿
+ */
+ @TableField(fill = FieldFill.INSERT)
+ private Date createDate;
+
+ /**
+ * 鏇存柊鑰�
+ */
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private Long updater;
+ /**
+ * 鏇存柊鏃堕棿
+ */
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private Date updateDate;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatAudioService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatAudioService.java
new file mode 100644
index 0000000..816370a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatAudioService.java
@@ -0,0 +1,30 @@
+package xiaozhi.modules.agent.service;
+
+import com.baomidou.mybatisplus.extension.repository.IRepository;
+
+import xiaozhi.modules.agent.entity.AgentChatAudioEntity;
+
+/**
+ * 鏅鸿兘浣撹亰澶╅煶棰戞暟鎹〃澶勭悊service
+ *
+ * @author Goody
+ * @version 1.0, 2025/5/8
+ * @since 1.0.0
+ */
+public interface AgentChatAudioService extends IRepository<AgentChatAudioEntity> {
+ /**
+ * 淇濆瓨闊抽鏁版嵁
+ *
+ * @param audioData 闊抽鏁版嵁
+ * @return 闊抽ID
+ */
+ String saveAudio(byte[] audioData);
+
+ /**
+ * 鑾峰彇闊抽鏁版嵁
+ *
+ * @param audioId 闊抽ID
+ * @return 闊抽鏁版嵁
+ */
+ byte[] getAudio(String audioId);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatHistoryService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatHistoryService.java
new file mode 100644
index 0000000..794e91e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatHistoryService.java
@@ -0,0 +1,90 @@
+package xiaozhi.modules.agent.service;
+
+import java.util.List;
+import java.util.Map;
+
+import com.baomidou.mybatisplus.extension.repository.IRepository;
+
+import xiaozhi.common.page.PageData;
+import xiaozhi.modules.agent.dto.AgentChatHistoryDTO;
+import xiaozhi.modules.agent.dto.AgentChatSessionDTO;
+import xiaozhi.modules.agent.entity.AgentChatHistoryEntity;
+import xiaozhi.modules.agent.vo.AgentChatHistoryUserVO;
+
+/**
+ * 鏅鸿兘浣撹亰澶╄褰曡〃澶勭悊service
+ *
+ * @author Goody
+ * @version 1.0, 2025/4/30
+ * @since 1.0.0
+ */
+public interface AgentChatHistoryService extends IRepository<AgentChatHistoryEntity> {
+
+ /**
+ * 鏍规嵁鏅鸿兘浣揑D鑾峰彇浼氳瘽鍒楄〃
+ *
+ * @param params 鏌ヨ鍙傛暟锛屽寘鍚玜gentId銆乸age銆乴imit
+ * @return 鍒嗛〉鐨勪細璇濆垪琛�
+ */
+ PageData<AgentChatSessionDTO> getSessionListByAgentId(Map<String, Object> params);
+
+ /**
+ * 鏍规嵁浼氳瘽ID鑾峰彇鑱婂ぉ璁板綍鍒楄〃
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ * @param sessionId 浼氳瘽ID
+ * @return 鑱婂ぉ璁板綍鍒楄〃
+ */
+ List<AgentChatHistoryDTO> getChatHistoryBySessionId(String agentId, String sessionId);
+
+ /**
+ * 鏍规嵁浼氳瘽ID鑾峰彇鏅鸿兘浣揑D
+ *
+ * @param sessionId 浼氳瘽ID
+ * @return 鏅鸿兘浣揑D
+ */
+ String getAgentIdBySessionId(String sessionId);
+
+ /**
+ * 鏍规嵁鏅鸿兘浣揑D鍒犻櫎鑱婂ぉ璁板綍
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ * @param deleteAudio 鏄惁鍒犻櫎闊抽
+ * @param deleteText 鏄惁鍒犻櫎鏂囨湰
+ */
+ void deleteByAgentId(String agentId, Boolean deleteAudio, Boolean deleteText);
+
+ /**
+ * 鏍规嵁鏅鸿兘浣揑D鑾峰彇鏈�杩�50鏉$敤鎴风殑鑱婂ぉ璁板綍鏁版嵁锛堝甫闊抽鏁版嵁锛�
+ *
+ * @param agentId 鏅鸿兘浣搃d
+ * @return 鑱婂ぉ璁板綍鍒楄〃锛堝彧鏈夌敤鎴凤級
+ */
+ List<AgentChatHistoryUserVO> getRecentlyFiftyByAgentId(String agentId);
+
+ /**
+ * 鏍规嵁闊抽鏁版嵁ID鑾峰彇鑱婂ぉ鍐呭
+ *
+ * @param audioId 闊抽id
+ * @return 鑱婂ぉ鍐呭
+ */
+ String getContentByAudioId(String audioId);
+
+ /**
+ * 鏍规嵁闊抽ID鑾峰彇鏅鸿兘浣揑D
+ *
+ * @param audioId 闊抽ID
+ * @return 鏅鸿兘浣揑D
+ */
+ String getAgentIdByAudioId(String audioId);
+
+
+ /**
+ * 鏌ヨ姝ら煶棰慽d鏄惁灞炰簬姝ゆ櫤鑳戒綋
+ *
+ * @param audioId 闊抽id
+ * @param agentId 闊抽id
+ * @return T锛氬睘浜� F锛氫笉灞炰簬
+ */
+ boolean isAudioOwnedByAgent(String audioId,String agentId);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatSummaryService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatSummaryService.java
new file mode 100644
index 0000000..3360584
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatSummaryService.java
@@ -0,0 +1,23 @@
+package xiaozhi.modules.agent.service;
+
+/**
+ * 鏅鸿兘浣撹亰澶╄褰曟�荤粨鏈嶅姟鎺ュ彛
+ */
+public interface AgentChatSummaryService {
+
+ /**
+ * 鏍规嵁浼氳瘽ID鐢熸垚鑱婂ぉ璁板綍鎬荤粨骞朵繚瀛樺埌鏅鸿兘浣撹蹇�
+ *
+ * @param sessionId 浼氳瘽ID
+ * @return 淇濆瓨缁撴灉
+ */
+ boolean generateAndSaveChatSummary(String sessionId);
+
+ /**
+ * 鏍规嵁浼氳瘽ID鐢熸垚鑱婂ぉ鏍囬骞朵繚瀛�
+ *
+ * @param sessionId 浼氳瘽ID
+ * @return 鏄惁鎴愬姛
+ */
+ boolean generateAndSaveChatTitle(String sessionId);
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatTitleService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatTitleService.java
new file mode 100644
index 0000000..71699c5
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatTitleService.java
@@ -0,0 +1,10 @@
+package xiaozhi.modules.agent.service;
+
+import xiaozhi.modules.agent.entity.AgentChatTitleEntity;
+
+public interface AgentChatTitleService {
+
+ void saveOrUpdateTitle(String sessionId, String title);
+
+ String getTitleBySessionId(String sessionId);
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentContextProviderService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentContextProviderService.java
new file mode 100644
index 0000000..da75997
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentContextProviderService.java
@@ -0,0 +1,25 @@
+package xiaozhi.modules.agent.service;
+
+import xiaozhi.common.service.BaseService;
+import xiaozhi.modules.agent.entity.AgentContextProviderEntity;
+
+public interface AgentContextProviderService extends BaseService<AgentContextProviderEntity> {
+ /**
+ * 鏍规嵁鏅鸿兘浣揑D鑾峰彇涓婁笅鏂囨簮閰嶇疆
+ * @param agentId 鏅鸿兘浣揑D
+ * @return 涓婁笅鏂囨簮閰嶇疆瀹炰綋
+ */
+ AgentContextProviderEntity getByAgentId(String agentId);
+
+ /**
+ * 淇濆瓨鎴栨洿鏂颁笂涓嬫枃婧愰厤缃�
+ * @param entity 瀹炰綋
+ */
+ void saveOrUpdateByAgentId(AgentContextProviderEntity entity);
+
+ /**
+ * 鏍规嵁鏅鸿兘浣揑D鍒犻櫎涓婁笅鏂囨簮閰嶇疆
+ * @param agentId 鏅鸿兘浣揑D
+ */
+ void deleteByAgentId(String agentId);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentMcpAccessPointService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentMcpAccessPointService.java
new file mode 100644
index 0000000..336ade6
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentMcpAccessPointService.java
@@ -0,0 +1,25 @@
+package xiaozhi.modules.agent.service;
+
+
+import java.util.List;
+
+/**
+ * 鏅鸿兘浣揗cp鎺ュ叆鐐瑰鐞唖ervice
+ *
+ * @author zjy
+ */
+public interface AgentMcpAccessPointService {
+ /**
+ * 鑾峰彇鏅鸿兘浣撶殑mcp鎺ュ叆鐐瑰湴鍧�
+ * @param id 鏅鸿兘浣搃d
+ * @return mcp鎺ュ叆鐐瑰湴鍧�
+ */
+ String getAgentMcpAccessAddress(String id);
+
+ /**
+ * 鑾峰彇鏅鸿兘浣撶殑mcp鎺ュ叆鐐瑰凡鏈夌殑宸ュ叿鍒楄〃
+ * @param id 鏅鸿兘浣搃d
+ * @return 宸ュ叿鍒楄〃
+ */
+ List<String> getAgentMcpToolsList(String id);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentPluginMappingService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentPluginMappingService.java
new file mode 100644
index 0000000..8e892ad
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentPluginMappingService.java
@@ -0,0 +1,36 @@
+package xiaozhi.modules.agent.service;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.extension.repository.IRepository;
+
+import xiaozhi.modules.agent.entity.AgentPluginMapping;
+
+/**
+ * @description 閽堝琛ㄣ�恆i_agent_plugin_mapping(Agent涓庢彃浠剁殑鍞竴鏄犲皠琛�)銆戠殑鏁版嵁搴撴搷浣淪ervice
+ * @createDate 2025-05-25 22:33:17
+ */
+public interface AgentPluginMappingService extends IRepository<AgentPluginMapping> {
+
+ /**
+ * 鏍规嵁鏅鸿兘浣搃d鑾峰彇鎻掍欢鍙傛暟
+ *
+ * @param agentId
+ * @return
+ */
+ List<AgentPluginMapping> agentPluginParamsByAgentId(String agentId);
+
+ /**
+ * 鏍规嵁鏅鸿兘浣搃d鍒犻櫎鎻掍欢鍙傛暟
+ *
+ * @param agentId
+ */
+ void deleteByAgentId(String agentId);
+
+ /**
+ * 鏍规嵁鎻掍欢ID鍒犻櫎鎵�鏈夋櫤鑳戒綋鐨勬彃浠舵槧灏�
+ *
+ * @param pluginId 鎻掍欢ID
+ */
+ void deleteByPluginId(String pluginId);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentService.java
new file mode 100644
index 0000000..2ccf12d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentService.java
@@ -0,0 +1,157 @@
+package xiaozhi.modules.agent.service;
+
+import java.util.List;
+import java.util.Map;
+
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.service.BaseService;
+import xiaozhi.modules.agent.dto.AgentCreateDTO;
+import xiaozhi.modules.agent.dto.AgentDTO;
+import xiaozhi.modules.agent.dto.AgentMemoryDTO;
+import xiaozhi.modules.agent.dto.AgentUpdateDTO;
+import xiaozhi.modules.agent.entity.AgentEntity;
+import xiaozhi.modules.agent.vo.AgentInfoVO;
+
+/**
+ * 鏅鸿兘浣撹〃澶勭悊service
+ *
+ * @author Goody
+ * @version 1.0, 2025/4/30
+ * @since 1.0.0
+ */
+public interface AgentService extends BaseService<AgentEntity> {
+ /**
+ * 鑾峰彇绠$悊鍛樻櫤鑳戒綋鍒楄〃
+ *
+ * @param params 鏌ヨ鍙傛暟
+ * @return 鍒嗛〉鏁版嵁
+ */
+ PageData<AgentEntity> adminAgentList(Map<String, Object> params);
+
+ /**
+ * 鏍规嵁ID鑾峰彇鏅鸿兘浣�
+ *
+ * @param id 鏅鸿兘浣揑D
+ * @return 鏅鸿兘浣撳疄浣�
+ */
+ AgentInfoVO getAgentById(String id);
+
+ /**
+ * 鏍规嵁ID鑾峰彇褰撳墠鐢ㄦ埛鏈夋潈璁块棶鐨勬櫤鑳戒綋
+ *
+ * @param id 鏅鸿兘浣揑D
+ * @param userId 褰撳墠鐢ㄦ埛ID
+ * @return 鏅鸿兘浣撳疄浣�
+ */
+ AgentInfoVO getAgentById(String id, Long userId);
+
+ /**
+ * 鎻掑叆鏅鸿兘浣�
+ *
+ * @param entity 鏅鸿兘浣撳疄浣�
+ * @return 鏄惁鎴愬姛
+ */
+ boolean insert(AgentEntity entity);
+
+ /**
+ * 鏍规嵁鐢ㄦ埛ID鍒犻櫎鏅鸿兘浣�
+ *
+ * @param userId 鐢ㄦ埛ID
+ */
+ void deleteAgentByUserId(Long userId);
+
+ /**
+ * 鍒犻櫎鏅鸿兘浣撳強鍏跺叧鑱旀暟鎹�
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ */
+ void deleteAgent(String agentId);
+
+ /**
+ * 鑾峰彇鐢ㄦ埛鏅鸿兘浣撳垪琛�
+ *
+ * @param userId 鐢ㄦ埛ID
+ * @param keyword 鎼滅储鍏抽敭璇�
+ * @param searchType 鎼滅储绫诲瀷锛坣ame - 鎸夊悕绉版悳绱紝mac - 鎸塎AC鍦板潃鎼滅储锛�
+ * @return 鏅鸿兘浣撳垪琛�
+ */
+ List<AgentDTO> getUserAgents(Long userId, String keyword, String searchType);
+
+ /**
+ * 鏍规嵁鏅鸿兘浣揑D鑾峰彇璁惧鏁伴噺
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ * @return 璁惧鏁伴噺
+ */
+ Integer getDeviceCountByAgentId(String agentId);
+
+ /**
+ * 鏍规嵁璁惧MAC鍦板潃鏌ヨ瀵瑰簲璁惧鐨勯粯璁ゆ櫤鑳戒綋淇℃伅
+ *
+ * @param macAddress 璁惧MAC鍦板潃
+ * @return 榛樿鏅鸿兘浣撲俊鎭紝涓嶅瓨鍦ㄦ椂杩斿洖null
+ */
+ AgentEntity getDefaultAgentByMacAddress(String macAddress);
+
+ /**
+ * 妫�鏌ョ敤鎴锋槸鍚︽湁鏉冮檺璁块棶鏅鸿兘浣�
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ * @param userId 鐢ㄦ埛ID
+ * @return 鏄惁鏈夋潈闄�
+ */
+ boolean checkAgentPermission(String agentId, Long userId);
+
+ /**
+ * 鏇存柊鏅鸿兘浣�
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ * @param dto 鏇存柊鏅鸿兘浣撴墍闇�鐨勪俊鎭�
+ */
+ void updateAgentById(String agentId, AgentUpdateDTO dto);
+
+ /**
+ * 鏇存柊褰撳墠鐢ㄦ埛鏈夋潈璁块棶鐨勬櫤鑳戒綋
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ * @param dto 鏇存柊鏅鸿兘浣撴墍闇�鐨勪俊鎭�
+ * @param userId 褰撳墠鐢ㄦ埛ID
+ */
+ void updateAgentById(String agentId, AgentUpdateDTO dto, Long userId);
+
+ /**
+ * 鏍规嵁璁惧MAC鍦板潃鏇存柊褰撳墠鐢ㄦ埛鏈夋潈璁块棶鐨勬櫤鑳戒綋璁板繂
+ *
+ * @param macAddress 璁惧MAC鍦板潃
+ * @param dto 鏅鸿兘浣撹蹇�
+ * @param userId 褰撳墠鐢ㄦ埛ID
+ */
+ void updateAgentMemoryByDeviceMacAddress(String macAddress, AgentMemoryDTO dto, Long userId);
+
+ /**
+ * 鍒犻櫎褰撳墠鐢ㄦ埛鏈夋潈璁块棶鐨勬櫤鑳戒綋
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ * @param userId 褰撳墠鐢ㄦ埛ID
+ */
+ void deleteAgentById(String agentId, Long userId);
+
+ /**
+ * 鏇存柊鏅鸿兘浣�
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ * @param dto 鏇存柊鏅鸿兘浣撴墍闇�鐨勪俊鎭�
+ * @param createSnapshot 鏄惁鍒涘缓閰嶇疆蹇収
+ */
+ void updateAgentById(String agentId, AgentUpdateDTO dto, boolean createSnapshot);
+
+ /**
+ * 鍒涘缓鏅鸿兘浣�
+ *
+ * @param dto 鍒涘缓鏅鸿兘浣撴墍闇�鐨勪俊鎭�
+ * @return 鍒涘缓鐨勬櫤鑳戒綋ID
+ */
+ String createAgent(AgentCreateDTO dto);
+
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentSnapshotService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentSnapshotService.java
new file mode 100644
index 0000000..57e03eb
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentSnapshotService.java
@@ -0,0 +1,25 @@
+package xiaozhi.modules.agent.service;
+
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.service.BaseService;
+import xiaozhi.modules.agent.dto.AgentSnapshotPageDTO;
+import xiaozhi.modules.agent.entity.AgentSnapshotEntity;
+import xiaozhi.modules.agent.vo.AgentSnapshotVO;
+
+public interface AgentSnapshotService extends BaseService<AgentSnapshotEntity> {
+ void createSnapshot(String agentId, String source);
+
+ PageData<AgentSnapshotVO> page(String agentId, AgentSnapshotPageDTO params);
+
+ AgentSnapshotVO getSnapshot(String agentId, String snapshotId);
+
+ void restoreSnapshot(String agentId, String snapshotId, String currentStateToken);
+
+ void deleteSnapshot(String agentId, String snapshotId);
+
+ Integer getCurrentVersionNo(String agentId);
+
+ void deleteByAgentId(String agentId);
+
+ long redactLegacySnapshots();
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentTagService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentTagService.java
new file mode 100644
index 0000000..2ef1694
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentTagService.java
@@ -0,0 +1,25 @@
+package xiaozhi.modules.agent.service;
+
+import java.util.List;
+
+import xiaozhi.modules.agent.dto.AgentTagDTO;
+import xiaozhi.modules.agent.entity.AgentTagEntity;
+
+public interface AgentTagService {
+
+ AgentTagEntity saveTag(String tagName);
+
+ void deleteTag(String tagId);
+
+ List<AgentTagDTO> getTagsByAgentId(String agentId);
+
+ void saveAgentTags(String agentId, List<String> tagIds, List<String> tagNames);
+
+ void deleteAgentTags(String agentId);
+
+ List<AgentTagDTO> getTagsByAgentIds(List<String> agentIds);
+
+ List<AgentTagDTO> getAllTags();
+
+ List<String> getAgentIdsByTagName(String tagName);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentTemplateService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentTemplateService.java
new file mode 100644
index 0000000..a8af7ad
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentTemplateService.java
@@ -0,0 +1,42 @@
+package xiaozhi.modules.agent.service;
+
+import com.baomidou.mybatisplus.extension.repository.IRepository;
+
+import xiaozhi.modules.agent.entity.AgentTemplateEntity;
+
+/**
+ * @author chenerlei
+ * @description 閽堝琛ㄣ�恆i_agent_template(鏅鸿兘浣撻厤缃ā鏉胯〃)銆戠殑鏁版嵁搴撴搷浣淪ervice
+ * @createDate 2025-03-22 11:48:18
+ */
+public interface AgentTemplateService extends IRepository<AgentTemplateEntity> {
+
+ /**
+ * 鑾峰彇榛樿妯℃澘
+ *
+ * @return 榛樿妯℃澘瀹炰綋
+ */
+ AgentTemplateEntity getDefaultTemplate();
+
+ /**
+ * 鏇存柊榛樿妯℃澘涓殑妯″瀷ID
+ *
+ * @param modelType 妯″瀷绫诲瀷
+ * @param modelId 妯″瀷ID
+ */
+ void updateDefaultTemplateModelId(String modelType, String modelId);
+
+ /**
+ * 鍒犻櫎妯℃澘鍚庨噸鏂版帓搴忓墿浣欐ā鏉�
+ *
+ * @param deletedSort 琚垹闄ゆā鏉跨殑鎺掑簭鍊�
+ */
+ void reorderTemplatesAfterDelete(Integer deletedSort);
+
+ /**
+ * 鑾峰彇涓嬩竴涓彲鐢ㄧ殑鎺掑簭搴忓彿锛堝鎵炬渶灏忕殑鏈娇鐢ㄥ簭鍙凤級
+ *
+ * @return 涓嬩竴涓彲鐢ㄧ殑鎺掑簭搴忓彿
+ */
+ Integer getNextAvailableSort();
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentVoicePrintService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentVoicePrintService.java
new file mode 100644
index 0000000..c9d38ab
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentVoicePrintService.java
@@ -0,0 +1,50 @@
+package xiaozhi.modules.agent.service;
+
+import java.util.List;
+
+import xiaozhi.modules.agent.dto.AgentVoicePrintSaveDTO;
+import xiaozhi.modules.agent.dto.AgentVoicePrintUpdateDTO;
+import xiaozhi.modules.agent.vo.AgentVoicePrintVO;
+
+/**
+ * 鏅鸿兘浣撳0绾瑰鐞唖ervice
+ *
+ * @author zjy
+ */
+public interface AgentVoicePrintService {
+ /**
+ * 娣诲姞鏅鸿兘浣撴柊鐨勫0绾�
+ *
+ * @param dto 淇濆瓨鏅鸿兘浣撳0绾圭殑鏁版嵁
+ * @return T:鎴愬姛 F锛氬け璐�
+ */
+ boolean insert(AgentVoicePrintSaveDTO dto);
+
+ /**
+ * 鍒犻櫎鏅鸿兘浣撶殑鎸囩殑澹扮汗
+ *
+ * @param userId 褰撳墠鐧诲綍鐨勭敤鎴穒d
+ * @param voicePrintId 澹扮汗id
+ * @return 鏄惁鎴愬姛 T:鎴愬姛 F锛氬け璐�
+ */
+ boolean delete(Long userId, String voicePrintId);
+
+ /**
+ * 鑾峰彇鎸囧畾鏅鸿兘浣撶殑鎵�鏈夊0绾规暟鎹�
+ *
+ * @param userId 褰撳墠鐧诲綍鐨勭敤鎴穒d
+ * @param agentId 鏅鸿兘浣搃d
+ * @return 澹扮汗鏁版嵁闆嗗悎
+ */
+ List<AgentVoicePrintVO> list(Long userId, String agentId);
+
+ /**
+ * 鏇存柊鏅鸿兘浣撶殑鎸囩殑澹扮汗鏁版嵁
+ *
+ * @param userId 褰撳墠鐧诲綍鐨勭敤鎴穒d
+ * @param dto 淇敼鐨勫0绾圭殑鏁版嵁
+ * @return 鏄惁鎴愬姛 T:鎴愬姛 F锛氬け璐�
+ */
+ boolean update(Long userId, AgentVoicePrintUpdateDTO dto);
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/biz/AgentChatHistoryBizService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/biz/AgentChatHistoryBizService.java
new file mode 100644
index 0000000..191b231
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/biz/AgentChatHistoryBizService.java
@@ -0,0 +1,22 @@
+package xiaozhi.modules.agent.service.biz;
+
+import xiaozhi.modules.agent.dto.AgentChatHistoryReportDTO;
+
+/**
+ * 鏅鸿兘浣撹亰澶╁巻鍙蹭笟鍔¢�昏緫灞�
+ *
+ * @author Goody
+ * @version 1.0, 2025/4/30
+ * @since 1.0.0
+ */
+public interface AgentChatHistoryBizService {
+
+ /**
+ * 鑱婂ぉ涓婃姤鏂规硶
+ *
+ * @param agentChatHistoryReportDTO 鍖呭惈鑱婂ぉ涓婃姤鎵�闇�淇℃伅鐨勮緭鍏ュ璞�
+ * 渚嬪锛氳澶嘙AC鍦板潃銆佹枃浠剁被鍨嬨�佸唴瀹圭瓑
+ * @return 涓婁紶缁撴灉锛宼rue琛ㄧず鎴愬姛锛宖alse琛ㄧず澶辫触
+ */
+ Boolean report(AgentChatHistoryReportDTO agentChatHistoryReportDTO);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/biz/impl/AgentChatHistoryBizServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/biz/impl/AgentChatHistoryBizServiceImpl.java
new file mode 100644
index 0000000..ac1b5d0
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/biz/impl/AgentChatHistoryBizServiceImpl.java
@@ -0,0 +1,130 @@
+package xiaozhi.modules.agent.service.biz.impl;
+
+import java.util.Base64;
+import java.util.Date;
+import java.util.Objects;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.redis.RedisKeys;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.modules.agent.dto.AgentChatHistoryReportDTO;
+import xiaozhi.modules.agent.entity.AgentChatHistoryEntity;
+import xiaozhi.modules.agent.entity.AgentEntity;
+import xiaozhi.modules.agent.service.AgentChatAudioService;
+import xiaozhi.modules.agent.service.AgentChatHistoryService;
+import xiaozhi.modules.agent.service.AgentChatSummaryService;
+import xiaozhi.modules.agent.service.AgentService;
+import xiaozhi.modules.agent.service.biz.AgentChatHistoryBizService;
+import xiaozhi.modules.device.entity.DeviceEntity;
+import xiaozhi.modules.device.service.DeviceService;
+
+/**
+ * {@link AgentChatHistoryBizService} impl
+ *
+ * @author Goody
+ * @version 1.0, 2025/4/30
+ * @since 1.0.0
+ */
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class AgentChatHistoryBizServiceImpl implements AgentChatHistoryBizService {
+ private final AgentService agentService;
+ private final AgentChatHistoryService agentChatHistoryService;
+ private final AgentChatAudioService agentChatAudioService;
+ private final AgentChatSummaryService agentChatSummaryService;
+ private final RedisUtils redisUtils;
+ private final DeviceService deviceService;
+
+ /**
+ * 澶勭悊鑱婂ぉ璁板綍涓婃姤锛屽寘鎷枃浠朵笂浼犲拰鐩稿叧淇℃伅璁板綍
+ *
+ * @param report 鍖呭惈鑱婂ぉ涓婃姤鎵�闇�淇℃伅鐨勮緭鍏ュ璞�
+ * @return 涓婁紶缁撴灉锛宼rue琛ㄧず鎴愬姛锛宖alse琛ㄧず澶辫触
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean report(AgentChatHistoryReportDTO report) {
+ String macAddress = report.getMacAddress();
+ Byte chatType = report.getChatType();
+ Long reportTimeMillis = null != report.getReportTime() ? report.getReportTime()
+ : System.currentTimeMillis();
+ log.info("灏忔櫤璁惧鑱婂ぉ涓婃姤璇锋眰: macAddress={}, type={} reportTime={}", macAddress, chatType, reportTimeMillis);
+
+ // 鏍规嵁璁惧MAC鍦板潃鏌ヨ瀵瑰簲鐨勯粯璁ゆ櫤鑳戒綋锛屽垽鏂槸鍚﹂渶瑕佷笂鎶�
+ AgentEntity agentEntity = agentService.getDefaultAgentByMacAddress(macAddress);
+ if (agentEntity == null) {
+ return Boolean.FALSE;
+ }
+
+ Integer chatHistoryConf = agentEntity.getChatHistoryConf();
+ String agentId = agentEntity.getId();
+
+ if (Objects.equals(chatHistoryConf, Constant.ChatHistoryConfEnum.RECORD_TEXT.getCode())) {
+ saveChatText(report, agentId, macAddress, null, reportTimeMillis);
+ } else if (Objects.equals(chatHistoryConf, Constant.ChatHistoryConfEnum.RECORD_TEXT_AUDIO.getCode())) {
+ String audioId = saveChatAudio(report);
+ saveChatText(report, agentId, macAddress, audioId, reportTimeMillis);
+ }
+
+ // 鏇存柊璁惧鏈�鍚庡璇濇椂闂�
+ redisUtils.set(RedisKeys.getAgentDeviceLastConnectedAtById(agentId), new Date());
+
+ // 鏇存柊璁惧鏈�鍚庤繛鎺ユ椂闂�
+ DeviceEntity device = deviceService.getDeviceByMacAddress(macAddress);
+ if (device != null) {
+ deviceService.updateDeviceConnectionInfo(agentId, device.getId(), null);
+ } else {
+ log.warn("鑱婂ぉ璁板綍涓婃姤鏃讹紝鏈壘鍒癿ac鍦板潃涓� {} 鐨勮澶�", macAddress);
+ }
+
+ return Boolean.TRUE;
+ }
+
+ /**
+ * base64瑙g爜report.getOpusDataBase64(),瀛樺叆ai_agent_chat_audio琛�
+ */
+ private String saveChatAudio(AgentChatHistoryReportDTO report) {
+ String audioId = null;
+
+ if (report.getAudioBase64() != null && !report.getAudioBase64().isEmpty()) {
+ try {
+ byte[] audioData = Base64.getDecoder().decode(report.getAudioBase64());
+ audioId = agentChatAudioService.saveAudio(audioData);
+ log.info("闊抽鏁版嵁淇濆瓨鎴愬姛锛宎udioId={}", audioId);
+ } catch (Exception e) {
+ log.error("闊抽鏁版嵁淇濆瓨澶辫触", e);
+ return null;
+ }
+ }
+ return audioId;
+ }
+
+ /**
+ * 缁勮涓婃姤鏁版嵁
+ */
+ private void saveChatText(AgentChatHistoryReportDTO report, String agentId, String macAddress, String audioId,
+ Long reportTime) {
+ // 鏋勫缓鑱婂ぉ璁板綍瀹炰綋
+ AgentChatHistoryEntity entity = AgentChatHistoryEntity.builder()
+ .macAddress(macAddress)
+ .agentId(agentId)
+ .sessionId(report.getSessionId())
+ .chatType(report.getChatType())
+ .content(report.getContent())
+ .audioId(audioId)
+ .createdAt(new Date(reportTime))
+ // NOTE(haotian): 2025/5/26 updateAt鍙互涓嶈缃紝閲嶇偣鏄痗reateAt锛岃�屼笖杩欐牱鍙互鐪嬪埌涓婃姤寤惰繜
+ .build();
+
+ // 淇濆瓨鏁版嵁
+ agentChatHistoryService.save(entity);
+
+ log.info("璁惧 {} 瀵瑰簲鏅鸿兘浣� {} 涓婃姤鎴愬姛", macAddress, agentId);
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatAudioServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatAudioServiceImpl.java
new file mode 100644
index 0000000..7aeb772
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatAudioServiceImpl.java
@@ -0,0 +1,34 @@
+package xiaozhi.modules.agent.service.impl;
+
+import org.springframework.stereotype.Service;
+
+import com.baomidou.mybatisplus.spring.repository.CrudRepository;
+
+import xiaozhi.modules.agent.dao.AiAgentChatAudioDao;
+import xiaozhi.modules.agent.entity.AgentChatAudioEntity;
+import xiaozhi.modules.agent.service.AgentChatAudioService;
+
+/**
+ * 鏅鸿兘浣撹亰澶╅煶棰戞暟鎹〃澶勭悊service {@link AgentChatAudioService} impl
+ *
+ * @author Goody
+ * @version 1.0, 2025/5/8
+ * @since 1.0.0
+ */
+@Service
+public class AgentChatAudioServiceImpl extends CrudRepository<AiAgentChatAudioDao, AgentChatAudioEntity>
+ implements AgentChatAudioService {
+ @Override
+ public String saveAudio(byte[] audioData) {
+ AgentChatAudioEntity entity = new AgentChatAudioEntity();
+ entity.setAudio(audioData);
+ save(entity);
+ return entity.getId();
+ }
+
+ @Override
+ public byte[] getAudio(String audioId) {
+ AgentChatAudioEntity entity = getById(audioId);
+ return entity != null ? entity.getAudio() : null;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatHistoryServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatHistoryServiceImpl.java
new file mode 100644
index 0000000..14c55ea
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatHistoryServiceImpl.java
@@ -0,0 +1,213 @@
+package xiaozhi.modules.agent.service.impl;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.ListUtil;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.spring.repository.CrudRepository;
+
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.utils.ConvertUtils;
+import xiaozhi.common.utils.JsonUtils;
+import xiaozhi.modules.agent.Enums.AgentChatHistoryType;
+import xiaozhi.modules.agent.dao.AiAgentChatHistoryDao;
+import xiaozhi.modules.agent.dto.AgentChatHistoryDTO;
+import xiaozhi.modules.agent.dto.AgentChatSessionDTO;
+import xiaozhi.modules.agent.entity.AgentChatHistoryEntity;
+import xiaozhi.modules.agent.service.AgentChatHistoryService;
+import xiaozhi.modules.agent.service.AgentChatTitleService;
+import xiaozhi.modules.agent.vo.AgentChatHistoryUserVO;
+
+/**
+ * 鏅鸿兘浣撹亰澶╄褰曡〃澶勭悊service {@link AgentChatHistoryService} impl
+ *
+ * @author Goody
+ * @version 1.0, 2025/4/30
+ * @since 1.0.0
+ */
+@Service
+@RequiredArgsConstructor
+public class AgentChatHistoryServiceImpl extends CrudRepository<AiAgentChatHistoryDao, AgentChatHistoryEntity>
+ implements AgentChatHistoryService {
+
+ private final AgentChatTitleService agentChatTitleService;
+
+ @Override
+ public PageData<AgentChatSessionDTO> getSessionListByAgentId(Map<String, Object> params) {
+ String agentId = (String) params.get("agentId");
+ int page = Integer.parseInt(params.get(Constant.PAGE).toString());
+ int limit = Integer.parseInt(params.get(Constant.LIMIT).toString());
+
+ // 鏋勫缓鏌ヨ鏉′欢
+ QueryWrapper<AgentChatHistoryEntity> wrapper = new QueryWrapper<>();
+ wrapper.select("session_id", "MAX(created_at) as created_at", "COUNT(*) as chat_count")
+ .eq("agent_id", agentId)
+ .groupBy("session_id")
+ .orderByDesc("created_at");
+
+ // 鎵ц鍒嗛〉鏌ヨ
+ Page<Map<String, Object>> pageParam = new Page<>(page, limit);
+ IPage<Map<String, Object>> result = this.baseMapper.selectMapsPage(pageParam, wrapper);
+
+ List<AgentChatSessionDTO> records = result.getRecords().stream().map(map -> {
+ AgentChatSessionDTO dto = new AgentChatSessionDTO();
+ dto.setSessionId((String) map.get("session_id"));
+ dto.setCreatedAt((LocalDateTime) map.get("created_at"));
+ dto.setChatCount(((Number) map.get("chat_count")).intValue());
+ dto.setTitle(agentChatTitleService.getTitleBySessionId(dto.getSessionId()));
+ return dto;
+ }).collect(Collectors.toList());
+
+ return new PageData<>(records, result.getTotal());
+ }
+
+ @Override
+ public List<AgentChatHistoryDTO> getChatHistoryBySessionId(String agentId, String sessionId) {
+ // 鏋勫缓鏌ヨ鏉′欢
+ QueryWrapper<AgentChatHistoryEntity> wrapper = new QueryWrapper<>();
+ wrapper.eq("agent_id", agentId)
+ .eq("session_id", sessionId)
+ .orderByAsc("created_at");
+
+ // 鏌ヨ鑱婂ぉ璁板綍
+ List<AgentChatHistoryEntity> historyList = list(wrapper);
+
+ // 杞崲涓篋TO
+ return ConvertUtils.sourceToTarget(historyList, AgentChatHistoryDTO.class);
+ }
+
+ @Override
+ public String getAgentIdBySessionId(String sessionId) {
+ if (sessionId == null || sessionId.isBlank()) {
+ return null;
+ }
+ AgentChatHistoryEntity entity = baseMapper.selectOne(
+ new LambdaQueryWrapper<AgentChatHistoryEntity>()
+ .select(AgentChatHistoryEntity::getAgentId)
+ .eq(AgentChatHistoryEntity::getSessionId, sessionId)
+ .last("LIMIT 1"));
+ return entity == null ? null : entity.getAgentId();
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteByAgentId(String agentId, Boolean deleteAudio, Boolean deleteText) {
+ if (deleteAudio) {
+ // 鍒嗘壒鍒犻櫎闊抽,閬垮厤瓒呮椂
+ List<String> audioIds = baseMapper.getAudioIdsByAgentId(agentId);
+ if (CollUtil.isNotEmpty(audioIds)) {
+ // 姣忔壒鍒犻櫎1000鏉�
+ List<List<String>> batch = ListUtil.split(audioIds, 1000);
+ batch.forEach(dataList -> {
+ baseMapper.deleteAudioByIds(dataList);
+ });
+ }
+ }
+ if (deleteAudio && !deleteText) {
+ baseMapper.deleteAudioIdByAgentId(agentId);
+ }
+ if (deleteText) {
+ baseMapper.deleteHistoryByAgentId(agentId);
+ }
+
+ }
+
+ @Override
+ public List<AgentChatHistoryUserVO> getRecentlyFiftyByAgentId(String agentId) {
+ // 鏋勫缓鏌ヨ鏉′欢(涓嶆坊鍔犳寜鐓у垱寤烘椂闂存帓搴忥紝鏁版嵁鏈潵灏辨槸涓婚敭瓒婂ぇ鍒涘缓鏃堕棿瓒婂ぇ
+ // 涓嶆坊鍔犺繖鏍峰彲浠ュ噺灏戞帓搴忓叏閮ㄦ暟鎹湪鍒嗛〉鐨勫叏鐩樻壂鎻忔秷鑰�)
+ LambdaQueryWrapper<AgentChatHistoryEntity> wrapper = new LambdaQueryWrapper<>();
+ wrapper.select(AgentChatHistoryEntity::getContent, AgentChatHistoryEntity::getAudioId)
+ .eq(AgentChatHistoryEntity::getAgentId, agentId)
+ .eq(AgentChatHistoryEntity::getChatType, AgentChatHistoryType.USER.getValue())
+ .isNotNull(AgentChatHistoryEntity::getAudioId)
+ // 娣诲姞姝よ锛岀‘淇濇煡璇㈢粨鏋滄寜鐓у垱寤烘椂闂撮檷搴忔帓鍒�
+ // 浣跨敤id鐨勫師鍥狅細鏁版嵁褰㈠紡锛宨d瓒婂ぇ鐨勫垱寤烘椂闂村氨瓒婃櫄锛屾墍浠ヤ娇鐢╥d鐨勭粨鏋滃拰鍒涘缓鏃堕棿闄嶅簭鎺掑垪缁撴灉涓�鏍�
+ // id浣滀负闄嶅簭鎺掑垪鐨勪紭鍔匡紝鎬ц兘楂橈紝鏈変富閿储寮曪紝涓嶇敤鍦ㄦ帓搴忕殑鏃跺�欓噸鏂拌繘琛屾帓闄ゆ壂鎻忔瘮杈�
+ .orderByDesc(AgentChatHistoryEntity::getId);
+
+ // 鏋勫缓鍒嗛〉鏌ヨ锛屾煡璇㈠墠50椤垫暟鎹�
+ Page<AgentChatHistoryEntity> pageParam = new Page<>(0, 50);
+ IPage<AgentChatHistoryEntity> result = this.baseMapper.selectPage(pageParam, wrapper);
+ return result.getRecords().stream().map(item -> {
+ AgentChatHistoryUserVO vo = ConvertUtils.sourceToTarget(item, AgentChatHistoryUserVO.class);
+ // 澶勭悊 content 瀛楁锛岀‘淇濆彧杩斿洖鑱婂ぉ鍐呭
+ if (vo != null && vo.getContent() != null) {
+ vo.setContent(extractContentFromString(vo.getContent()));
+ }
+ return vo;
+ }).toList();
+ }
+
+ /**
+ * 浠� content 瀛楁涓彁鍙栬亰澶╁唴瀹�
+ * 濡傛灉 content 鏄� JSON 鏍煎紡锛堝 {"speaker": "鏈煡璇磋瘽浜�", "content": "鐜板湪鍑犵偣浜嗐��"}锛夛紝鍒欐彁鍙� content
+ * 瀛楁
+ * 濡傛灉 content 鏄櫘閫氬瓧绗︿覆锛屽垯鐩存帴杩斿洖
+ *
+ * @param content 鍘熷鍐呭
+ * @return 鎻愬彇鐨勮亰澶╁唴瀹�
+ */
+ private String extractContentFromString(String content) {
+ if (content == null || content.trim().isEmpty()) {
+ return content;
+ }
+
+ // 灏濊瘯瑙f瀽涓� JSON
+ try {
+ Map<String, Object> jsonMap = JsonUtils.parseMap(content);
+ if (jsonMap != null && jsonMap.containsKey("content")) {
+ Object contentObj = jsonMap.get("content");
+ return contentObj != null ? contentObj.toString() : content;
+ }
+ } catch (Exception e) {
+ // 濡傛灉涓嶆槸鏈夋晥鐨� JSON锛岀洿鎺ヨ繑鍥炲師鍐呭
+ }
+
+ // 濡傛灉涓嶆槸 JSON 鏍煎紡鎴栨病鏈� content 瀛楁锛岀洿鎺ヨ繑鍥炲師鍐呭
+ return content;
+ }
+
+ @Override
+ public String getContentByAudioId(String audioId) {
+ AgentChatHistoryEntity agentChatHistoryEntity = baseMapper
+ .selectOne(new LambdaQueryWrapper<AgentChatHistoryEntity>()
+ .select(AgentChatHistoryEntity::getContent)
+ .eq(AgentChatHistoryEntity::getAudioId, audioId));
+ return agentChatHistoryEntity == null ? null : agentChatHistoryEntity.getContent();
+ }
+
+ @Override
+ public String getAgentIdByAudioId(String audioId) {
+ if (audioId == null || audioId.isBlank()) {
+ return null;
+ }
+ AgentChatHistoryEntity entity = baseMapper.selectOne(
+ new LambdaQueryWrapper<AgentChatHistoryEntity>()
+ .select(AgentChatHistoryEntity::getAgentId)
+ .eq(AgentChatHistoryEntity::getAudioId, audioId)
+ .last("LIMIT 1"));
+ return entity == null ? null : entity.getAgentId();
+ }
+
+ @Override
+ public boolean isAudioOwnedByAgent(String audioId, String agentId) {
+ // 鏌ヨ鏄惁鏈夋寚瀹氶煶棰慽d鍜屾櫤鑳戒綋id鐨勬暟鎹紝濡傛灉鏈変笖鍙湁涓�鏉¤鏄庢鏁版嵁灞炴�ф鏅鸿兘浣�
+ Long row = baseMapper.selectCount(new LambdaQueryWrapper<AgentChatHistoryEntity>()
+ .eq(AgentChatHistoryEntity::getAudioId, audioId)
+ .eq(AgentChatHistoryEntity::getAgentId, agentId));
+ return row == 1;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatSummaryServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatSummaryServiceImpl.java
new file mode 100644
index 0000000..09cbf06
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatSummaryServiceImpl.java
@@ -0,0 +1,521 @@
+package xiaozhi.modules.agent.service.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+
+import lombok.RequiredArgsConstructor;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.modules.agent.dto.AgentChatHistoryDTO;
+import xiaozhi.modules.agent.dto.AgentChatSummaryDTO;
+import xiaozhi.modules.agent.dto.AgentMemoryDTO;
+import xiaozhi.modules.agent.dto.AgentUpdateDTO;
+import xiaozhi.modules.agent.entity.AgentChatHistoryEntity;
+import xiaozhi.modules.agent.service.AgentChatHistoryService;
+import xiaozhi.modules.agent.service.AgentChatSummaryService;
+import xiaozhi.modules.agent.service.AgentChatTitleService;
+import xiaozhi.modules.agent.service.AgentService;
+import xiaozhi.modules.agent.vo.AgentInfoVO;
+import xiaozhi.modules.device.entity.DeviceEntity;
+import xiaozhi.modules.device.service.DeviceService;
+import xiaozhi.modules.llm.service.LLMService;
+import xiaozhi.modules.model.entity.ModelConfigEntity;
+import xiaozhi.modules.model.service.ModelConfigService;
+
+/**
+ * 鏅鸿兘浣撹亰澶╄褰曟�荤粨鏈嶅姟瀹炵幇绫�
+ * 瀹炵幇Python绔痬em_local_short.py涓殑鎬荤粨閫昏緫
+ */
+@Service
+@RequiredArgsConstructor
+public class AgentChatSummaryServiceImpl implements AgentChatSummaryService {
+
+ private static final Logger log = LoggerFactory.getLogger(AgentChatSummaryServiceImpl.class);
+
+ private final AgentChatHistoryService agentChatHistoryService;
+ private final AgentService agentService;
+ private final AgentChatTitleService agentChatTitleService;
+ private final DeviceService deviceService;
+ private final LLMService llmService;
+ private final ModelConfigService modelConfigService;
+
+ // 鎬荤粨瑙勫垯甯搁噺
+ private static final int MAX_SUMMARY_LENGTH = 1800; // 鏈�澶ф�荤粨闀垮害
+ private static final Pattern JSON_PATTERN = Pattern.compile("\\{.*?\\}", Pattern.DOTALL);
+ private static final Pattern DEVICE_CONTROL_PATTERN = Pattern.compile("璁惧鎺у埗|璁惧鎿嶄綔|鎺у埗璁惧|璁惧鐘舵��",
+ Pattern.CASE_INSENSITIVE);
+ private static final Pattern WEATHER_PATTERN = Pattern.compile("澶╂皵|娓╁害|婀垮害|闄嶉洦|姘旇薄", Pattern.CASE_INSENSITIVE);
+ private static final Pattern DATE_PATTERN = Pattern.compile("鏃ユ湡|鏃堕棿|鏄熸湡|鏈堜唤|骞翠唤", Pattern.CASE_INSENSITIVE);
+
+ private AgentChatSummaryDTO generateChatSummary(String sessionId) {
+ try {
+ System.out.println("寮�濮嬬敓鎴愪細璇� " + sessionId + " 鐨勮亰澶╄褰曟�荤粨");
+
+ // 1. 鏍规嵁sessionId鑾峰彇鑱婂ぉ璁板綍
+ List<AgentChatHistoryDTO> chatHistory = getChatHistoryBySessionId(sessionId);
+ if (chatHistory == null || chatHistory.isEmpty()) {
+ return new AgentChatSummaryDTO(sessionId, "鏈壘鍒拌浼氳瘽鐨勮亰澶╄褰�");
+ }
+
+ // 2. 鑾峰彇鏅鸿兘浣撲俊鎭�
+ String agentId = getAgentIdFromSession(sessionId, chatHistory);
+ if (StringUtils.isBlank(agentId)) {
+ return new AgentChatSummaryDTO(sessionId, "鏃犳硶鑾峰彇鏅鸿兘浣撲俊鎭�");
+ }
+
+ // 3. 鎻愬彇鍏抽敭瀵硅瘽鍐呭
+ List<String> meaningfulMessages = extractMeaningfulMessages(chatHistory);
+ if (meaningfulMessages.isEmpty()) {
+ return new AgentChatSummaryDTO(sessionId, "娌℃湁鏈夋晥鐨勫璇濆唴瀹瑰彲鎬荤粨");
+ }
+
+ // 4. 鐢熸垚鎬荤粨锛坓enerateSummaryFromMessages鏂规硶宸插寘鍚暱搴﹂檺鍒堕�昏緫锛�
+ String summary = generateSummaryFromMessages(meaningfulMessages, agentId);
+
+ log.info("鎴愬姛鐢熸垚浼氳瘽 {} 鐨勮亰澶╄褰曟�荤粨锛岄暱搴�: {} 瀛楃", sessionId, summary.length());
+ return new AgentChatSummaryDTO(sessionId, agentId, summary);
+
+ } catch (Exception e) {
+ log.error("鐢熸垚浼氳瘽 {} 鐨勮亰澶╄褰曟�荤粨鏃跺彂鐢熼敊璇�: {}", sessionId, e.getMessage());
+ return new AgentChatSummaryDTO(sessionId, "鐢熸垚鎬荤粨鏃跺彂鐢熼敊璇�: " + e.getMessage());
+ }
+ }
+
+ @Override
+ public boolean generateAndSaveChatSummary(String sessionId) {
+ try {
+ DeviceEntity device = getDeviceBySessionId(sessionId);
+ if (device == null) {
+ log.info("鏈壘鍒颁笌浼氳瘽 {} 鍏宠仈鐨勮澶�", sessionId);
+ return false;
+ }
+
+ String agentId = device.getAgentId();
+ String memModelId = agentService.getAgentById(agentId).getMemModelId();
+
+ if (memModelId == null || memModelId.equals(Constant.MEMORY_MEM_REPORT_ONLY)) {
+ log.info("浼氳瘽 {} 浣跨敤浠呬笂鎶ヨ亰澶╄褰曟ā寮忥紝璺宠繃璁板繂鎬荤粨", sessionId);
+ return true;
+ }
+
+ boolean shouldSummarizeMemory = !memModelId.equals(Constant.MEMORY_NO_MEM)
+ && !memModelId.equals(Constant.MEMORY_MEM0AI)
+ && !memModelId.equals(Constant.MEMORY_POWERMEM);
+
+ if (shouldSummarizeMemory) {
+ AgentChatSummaryDTO summaryDTO = generateChatSummary(sessionId);
+ if (summaryDTO.isSuccess()) {
+ agentService.updateAgentById(agentId, new AgentUpdateDTO() {
+ {
+ setSummaryMemory(summaryDTO.getSummary());
+ }
+ }, false);
+ log.info("鎴愬姛淇濆瓨浼氳瘽 {} 鐨勮亰澶╄褰曟�荤粨鍒版櫤鑳戒綋 {}", sessionId, agentId);
+ } else {
+ log.info("鐢熸垚鎬荤粨澶辫触: {}", summaryDTO.getErrorMessage());
+ }
+ } else {
+ log.info("浼氳瘽 {} 浣跨敤 {} 妯″紡锛岃烦杩囪蹇嗘�荤粨", sessionId, memModelId);
+ }
+
+ return true;
+
+ } catch (Exception e) {
+ log.error("淇濆瓨浼氳瘽 {} 鐨勮亰澶╄褰曟�荤粨鏃跺彂鐢熼敊璇�: {}", sessionId, e.getMessage());
+ return false;
+ }
+ }
+
+ @Override
+ public boolean generateAndSaveChatTitle(String sessionId) {
+ try {
+ // 鑷姩鑾峰彇agentId
+ String agentId = findAgentIdBySessionId(sessionId);
+ if (StringUtils.isBlank(agentId)) {
+ log.warn("浼氳瘽 {} 鏃犳硶鑾峰彇鏅鸿兘浣撲俊鎭紝璺宠繃鏍囬鐢熸垚", sessionId);
+ return false;
+ }
+
+ List<AgentChatHistoryDTO> chatHistory = getChatHistoryBySessionId(sessionId);
+ if (chatHistory == null || chatHistory.isEmpty()) {
+ return false;
+ }
+
+ List<String> meaningfulMessages = extractMeaningfulMessages(chatHistory);
+ if (meaningfulMessages.isEmpty()) {
+ return false;
+ }
+
+ StringBuilder conversation = new StringBuilder();
+ for (int i = 0; i < meaningfulMessages.size(); i++) {
+ conversation.append("娑堟伅").append(i + 1).append(": ").append(meaningfulMessages.get(i)).append("\n");
+ }
+
+ String slmModelId = getSlmModelId(agentId);
+ String title = llmService.generateTitle(conversation.toString(), slmModelId);
+
+ if (StringUtils.isNotBlank(title)) {
+ agentChatTitleService.saveOrUpdateTitle(sessionId, title);
+ log.info("鎴愬姛淇濆瓨浼氳瘽 {} 鐨勬爣棰�: {}", sessionId, title);
+ return true;
+ }
+ return false;
+ } catch (Exception e) {
+ log.error("鐢熸垚浼氳瘽 {} 鐨勬爣棰樻椂鍙戠敓閿欒: {}", sessionId, e.getMessage());
+ return false;
+ }
+ }
+
+ private String getSlmModelId(String agentId) {
+ try {
+ if (StringUtils.isBlank(agentId)) {
+ return null;
+ }
+
+ AgentInfoVO agentInfo = agentService.getAgentById(agentId);
+ if (agentInfo == null) {
+ return null;
+ }
+
+ String slmModelId = agentInfo.getSlmModelId();
+ if (StringUtils.isNotBlank(slmModelId)) {
+ log.info("浼氳瘽 {} 浣跨敤SLM妯″瀷: {}", agentId, slmModelId);
+ return slmModelId;
+ }
+
+ ModelConfigEntity defaultLlmConfig = getDefaultLLMConfig();
+ if (defaultLlmConfig != null) {
+ log.info("浼氳瘽 {} 浣跨敤榛樿LLM妯″瀷: {}", agentId, defaultLlmConfig.getId());
+ return defaultLlmConfig.getId();
+ }
+
+ String llmModelId = agentInfo.getLlmModelId();
+ log.info("浼氳瘽 {} 浣跨敤LLM妯″瀷(鏈�缁堝洖閫�): {}", agentId, llmModelId);
+ return llmModelId;
+ } catch (Exception e) {
+ log.error("鑾峰彇鏅鸿兘浣搒lm妯″瀷ID澶辫触锛宎gentId: {}, 閿欒: {}", agentId, e.getMessage());
+ return null;
+ }
+ }
+
+ private ModelConfigEntity getDefaultLLMConfig() {
+ try {
+ List<ModelConfigEntity> llmConfigs = modelConfigService.getEnabledModelsByType("LLM");
+ if (llmConfigs == null || llmConfigs.isEmpty()) {
+ return null;
+ }
+
+ for (ModelConfigEntity config : llmConfigs) {
+ if (config.getIsDefault() != null && config.getIsDefault() == 1) {
+ return config;
+ }
+ }
+
+ return llmConfigs.get(0);
+ } catch (Exception e) {
+ log.error("鑾峰彇榛樿LLM閰嶇疆澶辫触: {}", e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * 鏍规嵁浼氳瘽ID鑾峰彇鑱婂ぉ璁板綍
+ */
+ private List<AgentChatHistoryDTO> getChatHistoryBySessionId(String sessionId) {
+ try {
+ // 杩欓噷闇�瑕佹牴鎹畇essionId鑾峰彇鑱婂ぉ璁板綍
+ // 鐢变簬鐜版湁鎺ュ彛闇�瑕乤gentId锛屾垜浠渶瑕佸厛鎵惧埌鍏宠仈鐨刟gentId
+ String agentId = findAgentIdBySessionId(sessionId);
+ if (StringUtils.isBlank(agentId)) {
+ return null;
+ }
+ return agentChatHistoryService.getChatHistoryBySessionId(agentId, sessionId);
+ } catch (Exception e) {
+ log.error("鑾峰彇浼氳瘽 {} 鐨勮亰澶╄褰曞け璐�: {}", sessionId, e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * 鏍规嵁浼氳瘽ID鏌ユ壘鍏宠仈鐨勬櫤鑳戒綋ID
+ */
+ private String findAgentIdBySessionId(String sessionId) {
+ try {
+ // 鏌ヨ璇ヤ細璇濈殑绗竴鏉¤褰曡幏鍙朼gentId
+ QueryWrapper<AgentChatHistoryEntity> wrapper = new QueryWrapper<>();
+ wrapper.select("agent_id")
+ .eq("session_id", sessionId)
+ .last("LIMIT 1");
+
+ AgentChatHistoryEntity entity = agentChatHistoryService.getOne(wrapper);
+ return entity != null ? entity.getAgentId() : null;
+ } catch (Exception e) {
+ log.error("鏍规嵁浼氳瘽ID {} 鏌ユ壘鏅鸿兘浣揑D澶辫触: {}", sessionId, e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * 浠庝細璇濅腑鑾峰彇鏅鸿兘浣揑D
+ */
+ private String getAgentIdFromSession(String sessionId, List<AgentChatHistoryDTO> chatHistory) {
+ // 鐩存帴浠庢暟鎹簱鏌ヨ鏅鸿兘浣揑D
+ return findAgentIdBySessionId(sessionId);
+ }
+
+ /**
+ * 鎻愬彇鏈夋剰涔夌殑瀵硅瘽鍐呭锛堝彧鎻愬彇鐢ㄦ埛娑堟伅锛屾帓闄I鍥炲锛�
+ */
+ private List<String> extractMeaningfulMessages(List<AgentChatHistoryDTO> chatHistory) {
+ List<String> meaningfulMessages = new ArrayList<>();
+
+ for (AgentChatHistoryDTO message : chatHistory) {
+ // 鍙鐞嗙敤鎴锋秷鎭紙chatType = 1锛�
+ if (message.getChatType() != null && message.getChatType() == 1) {
+ String content = extractContentFromMessage(message);
+ if (isMeaningfulMessage(content)) {
+ meaningfulMessages.add(content);
+ }
+ }
+ }
+
+ return meaningfulMessages;
+ }
+
+ /**
+ * 浠庢秷鎭腑鎻愬彇鍐呭锛堝鐞咼SON鏍煎紡锛�
+ */
+ private String extractContentFromMessage(AgentChatHistoryDTO message) {
+ String content = message.getContent();
+ if (StringUtils.isBlank(content)) {
+ return "";
+ }
+
+ // 澶勭悊JSON鏍煎紡鍐呭锛堜笌鍓嶇ChatHistoryDialog.vue閫昏緫涓�鑷达級
+ Matcher matcher = JSON_PATTERN.matcher(content);
+ if (matcher.find()) {
+ String jsonContent = matcher.group();
+ // 绠�鍖栧鐞嗭細鎻愬彇JSON涓殑鏂囨湰鍐呭
+ return extractTextFromJson(jsonContent);
+ }
+
+ return content;
+ }
+
+ /**
+ * 浠嶫SON涓彁鍙栨枃鏈唴瀹�
+ */
+ private String extractTextFromJson(String jsonContent) {
+ // 绠�鍖栧鐞嗭細鎻愬彇"content"瀛楁鐨勫��
+ Pattern contentPattern = Pattern.compile("\"content\"\s*:\s*\"([^\"]*)\"");
+ Matcher matcher = contentPattern.matcher(jsonContent);
+ if (matcher.find()) {
+ return matcher.group(1);
+ }
+ return jsonContent;
+ }
+
+ /**
+ * 鍒ゆ柇鏄惁涓烘湁鎰忎箟鐨勬秷鎭�
+ */
+ private boolean isMeaningfulMessage(String content) {
+ if (StringUtils.isBlank(content)) {
+ return false;
+ }
+
+ // 鎺掗櫎璁惧鎺у埗淇℃伅
+ if (DEVICE_CONTROL_PATTERN.matcher(content).find()) {
+ return false;
+ }
+
+ // 鎺掗櫎鏃ユ湡澶╂皵绛夋棤鍏冲唴瀹�
+ if (WEATHER_PATTERN.matcher(content).find() || DATE_PATTERN.matcher(content).find()) {
+ return false;
+ }
+
+ // 鎺掗櫎杩囩煭鐨勬秷鎭�
+ return content.length() >= 5;
+ }
+
+ /**
+ * 浠庢秷鎭敓鎴愭�荤粨
+ */
+ private String generateSummaryFromMessages(List<String> messages, String agentId) {
+ if (messages.isEmpty()) {
+ return "鏈瀵硅瘽鍐呭杈冨皯锛屾病鏈夐渶瑕佹�荤粨鐨勯噸瑕佷俊鎭��";
+ }
+
+ // 鏋勫缓瀹屾暣鐨勫璇濆唴瀹�
+ StringBuilder conversation = new StringBuilder();
+ for (int i = 0; i < messages.size(); i++) {
+ conversation.append("娑堟伅").append(i + 1).append(": ").append(messages.get(i)).append("\n");
+ }
+
+ try {
+ // 鑾峰彇褰撳墠鏅鸿兘浣撶殑鍘嗗彶璁板繂
+ String historyMemory = getCurrentAgentMemory(agentId);
+
+ // 璋冪敤LLM鏈嶅姟杩涜鏅鸿兘鎬荤粨锛屼紶閫抋gentId浠ヨ幏鍙栨纭殑妯″瀷閰嶇疆
+ String summary = callJavaLLMForSummaryWithHistory(conversation.toString(), historyMemory, agentId);
+
+ // 搴旂敤鎬荤粨瑙勫垯锛氶檺鍒舵渶澶ч暱搴�
+ if (summary.length() > MAX_SUMMARY_LENGTH) {
+ summary = summary.substring(0, MAX_SUMMARY_LENGTH) + "...";
+ }
+
+ return summary;
+ } catch (Exception e) {
+ log.error("璋冪敤Java绔疞LM鏈嶅姟澶辫触: {}", e.getMessage());
+ throw new RuntimeException("LLM鏈嶅姟涓嶅彲鐢紝鏃犳硶鐢熸垚鑱婂ぉ鎬荤粨");
+ }
+ }
+
+ /**
+ * 鑾峰彇褰撳墠鏅鸿兘浣撶殑鍘嗗彶璁板繂
+ */
+ private String getCurrentAgentMemory(String agentId) {
+ try {
+ if (StringUtils.isBlank(agentId)) {
+ return null;
+ }
+
+ // 鑾峰彇鏅鸿兘浣撲俊鎭�
+ AgentInfoVO agentInfo = agentService.getAgentById(agentId);
+ if (agentInfo == null) {
+ return null;
+ }
+
+ // 杩斿洖鏅鸿兘浣撶殑褰撳墠鎬荤粨璁板繂
+ return agentInfo.getSummaryMemory();
+ } catch (Exception e) {
+ log.error("鑾峰彇鏅鸿兘浣撳巻鍙茶蹇嗗け璐ワ紝agentId: {}, 閿欒: {}", agentId, e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * 璋冪敤Java绔疞LM鏈嶅姟杩涜鏅鸿兘鎬荤粨锛堟敮鎸佸巻鍙茶蹇嗗悎骞讹級
+ */
+ private String callJavaLLMForSummaryWithHistory(String conversation, String historyMemory, String agentId) {
+ try {
+ String modelId = getSlmModelId(agentId);
+
+ if (StringUtils.isBlank(modelId)) {
+ log.info("鏈壘鍒癝LM妯″瀷锛屼娇鐢ㄩ粯璁LM鏈嶅姟");
+ return llmService.generateSummaryWithHistory(conversation, historyMemory, null, null);
+ }
+
+ String summary = llmService.generateSummaryWithHistory(conversation, historyMemory, null, modelId);
+
+ if (StringUtils.isNotBlank(summary) && !summary.equals("鏈嶅姟鏆備笉鍙敤") && !summary.equals("鎬荤粨鐢熸垚澶辫触")) {
+ return summary;
+ }
+
+ throw new RuntimeException("Java绔疞LM鏈嶅姟杩斿洖寮傚父: " + summary);
+
+ } catch (Exception e) {
+ log.error("璋冪敤Java绔疞LM鏈嶅姟寮傚父锛宎gentId: {}, 閿欒: {}", agentId, e.getMessage());
+ throw e;
+ }
+ }
+
+ /**
+ * 璋冪敤Java绔疞LM鏈嶅姟杩涜鏅鸿兘鎬荤粨
+ */
+ private String callJavaLLMForSummary(String conversation, String agentId) {
+ try {
+ String modelId = getSlmModelId(agentId);
+
+ if (StringUtils.isBlank(modelId)) {
+ log.info("鏈壘鍒癝LM妯″瀷锛屼娇鐢ㄩ粯璁LM鏈嶅姟");
+ return llmService.generateSummary(conversation);
+ }
+
+ String summary = llmService.generateSummaryWithModel(conversation, modelId);
+
+ if (StringUtils.isNotBlank(summary) && !summary.equals("鏈嶅姟鏆備笉鍙敤") && !summary.equals("鎬荤粨鐢熸垚澶辫触")) {
+ return summary;
+ }
+
+ throw new RuntimeException("Java绔疞LM鏈嶅姟杩斿洖寮傚父: " + summary);
+
+ } catch (Exception e) {
+ log.error("璋冪敤Java绔疞LM鏈嶅姟寮傚父锛宎gentId: {}, 閿欒: {}", agentId, e.getMessage());
+ throw e;
+ }
+ }
+
+ /**
+ * 鑾峰彇璁板繂鎬荤粨鐨凩LM妯″瀷ID
+ */
+ private String getMemorySummaryModelId(String agentId) {
+ try {
+ if (StringUtils.isBlank(agentId)) {
+ return null;
+ }
+
+ // 鑾峰彇鏅鸿兘浣撲俊鎭�
+ AgentInfoVO agentInfo = agentService.getAgentById(agentId);
+ if (agentInfo == null) {
+ return null;
+ }
+
+ // 鑾峰彇鏅鸿兘浣撶殑璁板繂妯″瀷ID
+ String memModelId = agentInfo.getMemModelId();
+ if (StringUtils.isBlank(memModelId)) {
+ return null;
+ }
+
+ // 鑾峰彇璁板繂妯″瀷閰嶇疆
+ ModelConfigEntity memModelConfig = modelConfigService.getModelByIdFromCache(memModelId);
+ if (memModelConfig == null || memModelConfig.getConfigJson() == null) {
+ return null;
+ }
+
+ // 浠庤蹇嗘ā鍨嬮厤缃腑鎻愬彇瀵瑰簲鐨凩LM妯″瀷ID
+ Map<String, Object> configMap = memModelConfig.getConfigJson();
+ String llmModelId = (String) configMap.get("llm");
+
+ if (StringUtils.isBlank(llmModelId)) {
+ // 濡傛灉璁板繂妯″瀷娌℃湁閰嶇疆鐙珛鐨凩LM锛屽垯浣跨敤鏅鸿兘浣撶殑榛樿LLM妯″瀷
+ return agentInfo.getLlmModelId();
+ }
+
+ return llmModelId;
+ } catch (Exception e) {
+ log.error("鑾峰彇璁板繂鎬荤粨LLM妯″瀷ID澶辫触锛宎gentId: {}, 閿欒: {}", agentId, e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * 鏍规嵁浼氳瘽ID鑾峰彇璁惧淇℃伅
+ */
+ private DeviceEntity getDeviceBySessionId(String sessionId) {
+ try {
+ // 鏌ヨ璇ヤ細璇濈殑绗竴鏉¤褰曡幏鍙杕acAddress
+ QueryWrapper<AgentChatHistoryEntity> wrapper = new QueryWrapper<>();
+ wrapper.select("mac_address")
+ .eq("session_id", sessionId)
+ .last("LIMIT 1");
+
+ AgentChatHistoryEntity entity = agentChatHistoryService.getOne(wrapper);
+ if (entity != null && StringUtils.isNotBlank(entity.getMacAddress())) {
+ return deviceService.getDeviceByMacAddress(entity.getMacAddress());
+ }
+ return null;
+ } catch (Exception e) {
+ log.error("鏍规嵁浼氳瘽ID {} 鏌ユ壘璁惧淇℃伅澶辫触: {}", sessionId, e.getMessage());
+ return null;
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatTitleServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatTitleServiceImpl.java
new file mode 100644
index 0000000..b875a07
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatTitleServiceImpl.java
@@ -0,0 +1,60 @@
+package xiaozhi.modules.agent.service.impl;
+
+import java.util.Date;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+
+import lombok.RequiredArgsConstructor;
+import xiaozhi.modules.agent.dao.AgentChatTitleDao;
+import xiaozhi.modules.agent.entity.AgentChatTitleEntity;
+import xiaozhi.modules.agent.service.AgentChatTitleService;
+
+@Service
+@RequiredArgsConstructor
+public class AgentChatTitleServiceImpl implements AgentChatTitleService {
+
+ private final AgentChatTitleDao agentChatTitleDao;
+
+ @Override
+ public void saveOrUpdateTitle(String sessionId, String title) {
+ if (StringUtils.isBlank(sessionId) || StringUtils.isBlank(title)) {
+ return;
+ }
+
+ QueryWrapper<AgentChatTitleEntity> wrapper = new QueryWrapper<>();
+ wrapper.eq("session_id", sessionId);
+
+ AgentChatTitleEntity existing = agentChatTitleDao.selectOne(wrapper);
+
+ if (existing != null) {
+ existing.setTitle(title);
+ existing.setUpdatedAt(new Date());
+ agentChatTitleDao.updateById(existing);
+ } else {
+ AgentChatTitleEntity newEntity = AgentChatTitleEntity.builder()
+ .id(java.util.UUID.randomUUID().toString().replace("-", ""))
+ .sessionId(sessionId)
+ .title(title)
+ .createdAt(new Date())
+ .updatedAt(new Date())
+ .build();
+ agentChatTitleDao.insert(newEntity);
+ }
+ }
+
+ @Override
+ public String getTitleBySessionId(String sessionId) {
+ if (StringUtils.isBlank(sessionId)) {
+ return null;
+ }
+
+ QueryWrapper<AgentChatTitleEntity> wrapper = new QueryWrapper<>();
+ wrapper.eq("session_id", sessionId);
+
+ AgentChatTitleEntity entity = agentChatTitleDao.selectOne(wrapper);
+ return entity != null ? entity.getTitle() : null;
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentContextProviderServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentContextProviderServiceImpl.java
new file mode 100644
index 0000000..b68ab8c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentContextProviderServiceImpl.java
@@ -0,0 +1,35 @@
+package xiaozhi.modules.agent.service.impl;
+
+import org.springframework.stereotype.Service;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+
+import xiaozhi.common.service.impl.BaseServiceImpl;
+import xiaozhi.modules.agent.dao.AgentContextProviderDao;
+import xiaozhi.modules.agent.entity.AgentContextProviderEntity;
+import xiaozhi.modules.agent.service.AgentContextProviderService;
+
+@Service
+public class AgentContextProviderServiceImpl extends BaseServiceImpl<AgentContextProviderDao, AgentContextProviderEntity> implements AgentContextProviderService {
+
+ @Override
+ public AgentContextProviderEntity getByAgentId(String agentId) {
+ return baseDao.selectOne(new QueryWrapper<AgentContextProviderEntity>().eq("agent_id", agentId));
+ }
+
+ @Override
+ public void saveOrUpdateByAgentId(AgentContextProviderEntity entity) {
+ AgentContextProviderEntity exist = getByAgentId(entity.getAgentId());
+ if (exist != null) {
+ entity.setId(exist.getId());
+ updateById(entity);
+ } else {
+ insert(entity);
+ }
+ }
+
+ @Override
+ public void deleteByAgentId(String agentId) {
+ baseDao.delete(new QueryWrapper<AgentContextProviderEntity>().eq("agent_id", agentId));
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentMcpAccessPointServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentMcpAccessPointServiceImpl.java
new file mode 100644
index 0000000..c67f7d7
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentMcpAccessPointServiceImpl.java
@@ -0,0 +1,235 @@
+package xiaozhi.modules.agent.service.impl;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
+import cn.hutool.crypto.digest.DigestUtil;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.utils.AESUtils;
+import xiaozhi.common.utils.JsonUtils;
+import xiaozhi.modules.agent.Enums.XiaoZhiMcpJsonRpcJson;
+import xiaozhi.modules.agent.service.AgentMcpAccessPointService;
+import xiaozhi.modules.sys.service.SysParamsService;
+import xiaozhi.modules.sys.utils.WebSocketClientManager;
+
+@AllArgsConstructor
+@Service
+@Slf4j
+public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointService {
+ private SysParamsService sysParamsService;
+
+ @Override
+ public String getAgentMcpAccessAddress(String id) {
+ // 鑾峰彇鍒癿cp鐨勫湴鍧�
+ String url = sysParamsService.getValue(Constant.SERVER_MCP_ENDPOINT, true);
+ if (StringUtils.isBlank(url) || "null".equals(url)) {
+ return null;
+ }
+ URI uri = getURI(url);
+ // 鑾峰彇鏅鸿兘浣搈cp鐨剈rl鍓嶇紑
+ String agentMcpUrl = getAgentMcpUrl(uri);
+ // 鑾峰彇瀵嗛挜
+ String key = getSecretKey(uri);
+ // 鑾峰彇鍔犲瘑鐨則oken
+ String encryptToken = encryptToken(id, key);
+ // 瀵箃oken杩涜URL缂栫爜
+ String encodedToken = URLEncoder.encode(encryptToken, StandardCharsets.UTF_8);
+ // 杩斿洖鏅鸿兘浣揗cp璺緞鐨勬牸寮�
+ agentMcpUrl = "%s/mcp/?token=%s".formatted(agentMcpUrl, encodedToken);
+ return agentMcpUrl;
+ }
+
+ @Override
+ public List<String> getAgentMcpToolsList(String id) {
+ String wsUrl = getAgentMcpAccessAddress(id);
+ if (StringUtils.isBlank(wsUrl)) {
+ return List.of();
+ }
+
+ // 灏� /mcp 鏇挎崲涓� /call
+ wsUrl = wsUrl.replace("/mcp/", "/call/");
+
+ try {
+ // 鍒涘缓 WebSocket 杩炴帴锛屽鍔犺秴鏃舵椂闂村埌15绉�
+ try (WebSocketClientManager client = WebSocketClientManager.build(
+ new WebSocketClientManager.Builder()
+ .uri(wsUrl)
+ .bufferSize(1024 * 1024)
+ .connectTimeout(8, TimeUnit.SECONDS)
+ .maxSessionDuration(10, TimeUnit.SECONDS))) {
+
+ // 姝ラ1: 鍙戦�佸垵濮嬪寲娑堟伅骞剁瓑寰呭搷搴�
+ log.info("鍙戦�丮CP鍒濆鍖栨秷鎭紝鏅鸿兘浣揑D: {}", id);
+ client.sendText(XiaoZhiMcpJsonRpcJson.getInitializeJson());
+
+ // 绛夊緟鍒濆鍖栧搷搴� (id=1) - 绉婚櫎鍥哄畾寤惰繜锛屾敼涓哄搷搴旈┍鍔�
+ List<String> initResponses = client.listenerWithoutClose(response -> {
+ try {
+ Map<String, Object> jsonMap = JsonUtils.parseMap(response);
+ if (jsonMap != null && Integer.valueOf(1).equals(jsonMap.get("id"))) {
+ // 妫�鏌ユ槸鍚︽湁result瀛楁锛岃〃绀哄垵濮嬪寲鎴愬姛
+ return jsonMap.containsKey("result") && !jsonMap.containsKey("error");
+ }
+ return false;
+ } catch (Exception e) {
+ log.warn("瑙f瀽鍒濆鍖栧搷搴斿け璐�: {}", response, e);
+ return false;
+ }
+ });
+
+ // 楠岃瘉鍒濆鍖栧搷搴�
+ boolean initSucceeded = false;
+ for (String response : initResponses) {
+ try {
+ Map<String, Object> jsonMap = JsonUtils.parseMap(response);
+ if (jsonMap != null && Integer.valueOf(1).equals(jsonMap.get("id"))) {
+ if (jsonMap.containsKey("result")) {
+ log.info("MCP鍒濆鍖栨垚鍔燂紝鏅鸿兘浣揑D: {}", id);
+ initSucceeded = true;
+ break;
+ } else if (jsonMap.containsKey("error")) {
+ log.error("MCP鍒濆鍖栧け璐ワ紝鏅鸿兘浣揑D: {}, 閿欒: {}", id, jsonMap.get("error"));
+ return List.of();
+ }
+ }
+ } catch (Exception e) {
+ log.warn("澶勭悊鍒濆鍖栧搷搴斿け璐�: {}", response, e);
+ }
+ }
+
+ if (!initSucceeded) {
+ log.error("鏈敹鍒版湁鏁堢殑MCP鍒濆鍖栧搷搴旓紝鏅鸿兘浣揑D: {}", id);
+ return List.of();
+ }
+
+ // 姝ラ2: 鍙戦�佸垵濮嬪寲瀹屾垚閫氱煡 - 鍙湁鍦ㄦ敹鍒癷nitialize鍝嶅簲鍚庢墠鍙戦��
+ log.info("鍙戦�丮CP鍒濆鍖栧畬鎴愰�氱煡锛屾櫤鑳戒綋ID: {}", id);
+ client.sendText(XiaoZhiMcpJsonRpcJson.getNotificationsInitializedJson());
+ // 姝ラ3: 鍙戦�佸伐鍏峰垪琛ㄨ姹� - 绔嬪嵆鍙戦�侊紝鏃犻渶棰濆寤惰繜
+ log.info("鍙戦�丮CP宸ュ叿鍒楄〃璇锋眰锛屾櫤鑳戒綋ID: {}", id);
+ client.sendText(XiaoZhiMcpJsonRpcJson.getToolsListJson());
+
+ // 绛夊緟宸ュ叿鍒楄〃鍝嶅簲 (id=2)
+ List<String> toolsResponses = client.listener(response -> {
+ try {
+ Map<String, Object> jsonMap = JsonUtils.parseMap(response);
+ return jsonMap != null && Integer.valueOf(2).equals(jsonMap.get("id"));
+ } catch (Exception e) {
+ log.warn("瑙f瀽宸ュ叿鍒楄〃鍝嶅簲澶辫触: {}", response, e);
+ return false;
+ }
+ });
+
+ // 澶勭悊宸ュ叿鍒楄〃鍝嶅簲
+ for (String response : toolsResponses) {
+ try {
+ Map<String, Object> jsonMap = JsonUtils.parseMap(response);
+ if (jsonMap != null && Integer.valueOf(2).equals(jsonMap.get("id"))) {
+ // 妫�鏌ユ槸鍚︽湁result瀛楁
+ Object resultObj = jsonMap.get("result");
+ if (resultObj instanceof Map<?, ?>) {
+ Map<String, Object> resultMap = JsonUtils.toStringObjectMap(resultObj);
+ Object toolsObj = resultMap.get("tools");
+ if (toolsObj instanceof List<?>) {
+ List<Map<String, Object>> toolsList = JsonUtils.toStringObjectMapList(toolsObj);
+ // 鎻愬彇宸ュ叿鍚嶇О鍒楄〃
+ List<String> result = toolsList.stream()
+ .map(tool -> String.class.cast(tool.get("name")))
+ .filter(name -> name != null)
+ .sorted()
+ .collect(Collectors.toList());
+ log.info("鎴愬姛鑾峰彇MCP宸ュ叿鍒楄〃锛屾櫤鑳戒綋ID: {}, 宸ュ叿鏁伴噺: {}", id, result.size());
+ return result;
+ }
+ } else if (jsonMap.containsKey("error")) {
+ log.error("鑾峰彇宸ュ叿鍒楄〃澶辫触锛屾櫤鑳戒綋ID: {}, 閿欒: {}", id, jsonMap.get("error"));
+ return List.of();
+ }
+ }
+ } catch (Exception e) {
+ log.warn("澶勭悊宸ュ叿鍒楄〃鍝嶅簲澶辫触: {}", response, e);
+ }
+ }
+
+ log.warn("鏈壘鍒版湁鏁堢殑宸ュ叿鍒楄〃鍝嶅簲锛屾櫤鑳戒綋ID: {}", id);
+ return List.of();
+
+ }
+ } catch (Exception e) {
+ log.error("鑾峰彇鏅鸿兘浣� MCP 宸ュ叿鍒楄〃澶辫触锛屾櫤鑳戒綋ID: {},閿欒鍘熷洜锛歿}", id, e.getMessage());
+ return List.of();
+ }
+ }
+
+ /**
+ * 鑾峰彇URI瀵硅薄
+ *
+ * @param url 璺緞
+ * @return URI瀵硅薄
+ */
+ private static URI getURI(String url) {
+ try {
+ return new URI(url);
+ } catch (URISyntaxException e) {
+ log.error("璺緞鏍煎紡涓嶆纭矾寰勶細{}锛孿n閿欒淇℃伅:{}", url, e.getMessage());
+ throw new RuntimeException("mcp鐨勫湴鍧�瀛樺湪閿欒锛岃杩涘叆鍙傛暟绠$悊淇敼mcp鎺ュ叆鐐瑰湴鍧�");
+ }
+ }
+
+ /**
+ * 鑾峰彇瀵嗛挜
+ *
+ * @param uri mcp鍦板潃
+ * @return 瀵嗛挜
+ */
+ private static String getSecretKey(URI uri) {
+ // 鑾峰彇鍙傛暟
+ String query = uri.getQuery();
+ // 鑾峰彇aes鍔犲瘑瀵嗛挜
+ String str = "key=";
+ return query.substring(query.indexOf(str) + str.length());
+ }
+
+ /**
+ * 鑾峰彇鏅鸿兘浣搈cp鎺ュ叆鐐箄rl
+ *
+ * @param uri mcp鍦板潃
+ * @return 鏅鸿兘浣搈cp鎺ュ叆鐐箄rl
+ */
+ private String getAgentMcpUrl(URI uri) {
+ // 鑾峰彇鍗忚
+ String wsScheme = (uri.getScheme().equals("https")) ? "wss" : "ws";
+ // 鑾峰彇涓绘満锛岀鍙o紝璺緞
+ String path = uri.getSchemeSpecificPart();
+ // 鑾峰彇鍒版渶鍚庝竴涓�/鍓嶇殑path
+ path = path.substring(0, path.lastIndexOf("/"));
+ return wsScheme + ":" + path;
+ }
+
+ /**
+ * 鑾峰彇瀵规櫤鑳戒綋id鍔犲瘑鐨則oken
+ *
+ * @param agentId 鏅鸿兘浣搃d
+ * @param key 鍔犲瘑瀵嗛挜
+ * @return 鍔犲瘑鍚巘oken
+ */
+ private static String encryptToken(String agentId, String key) {
+ // 浣跨敤md5瀵规櫤鑳戒綋id杩涜鍔犲瘑
+ String md5 = DigestUtil.md5Hex(agentId);
+ // aes闇�瑕佸姞瀵嗘枃鏈�
+ String json = "{\"agentId\": \"%s\"}".formatted(md5);
+ // 鍔犲瘑鍚庢垚token鍊�
+ return AESUtils.encrypt(key, json);
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentPluginMappingServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentPluginMappingServiceImpl.java
new file mode 100644
index 0000000..7a518fb
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentPluginMappingServiceImpl.java
@@ -0,0 +1,114 @@
+package xiaozhi.modules.agent.service.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.baomidou.mybatisplus.spring.repository.CrudRepository;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.common.utils.JsonUtils;
+import xiaozhi.modules.agent.dao.AgentPluginMappingMapper;
+import xiaozhi.modules.agent.entity.AgentPluginMapping;
+import xiaozhi.modules.agent.service.AgentPluginMappingService;
+import xiaozhi.modules.knowledge.entity.KnowledgeBaseEntity;
+import xiaozhi.modules.knowledge.service.KnowledgeBaseService;
+import xiaozhi.modules.model.entity.ModelConfigEntity;
+import xiaozhi.modules.model.service.ModelConfigService;
+
+/**
+ * @description 閽堝琛ㄣ�恆i_agent_plugin_mapping(Agent涓庢彃浠剁殑鍞竴鏄犲皠琛�)銆戠殑鏁版嵁搴撴搷浣淪ervice瀹炵幇
+ * @createDate 2025-05-25 22:33:17
+ */
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class AgentPluginMappingServiceImpl extends CrudRepository<AgentPluginMappingMapper, AgentPluginMapping>
+ implements AgentPluginMappingService {
+ private final AgentPluginMappingMapper agentPluginMappingMapper;
+ private final KnowledgeBaseService knowledgeBaseService;
+ private final ModelConfigService modelConfigService;
+
+ @Override
+ public List<AgentPluginMapping> agentPluginParamsByAgentId(String agentId) {
+ List<AgentPluginMapping> list = agentPluginMappingMapper.selectPluginsByAgentId(agentId);
+ Map<String, List<KnowledgeBaseEntity>> knowledgeBaseMap = new HashMap<>();
+ Map<String, ModelConfigEntity> modelConfigMap = new HashMap<>();
+ for (int i = list.size() - 1; i >= 0; i--) {
+ AgentPluginMapping mapping = list.get(i);
+ if (StringUtils.isBlank(mapping.getProviderCode())) {
+ // 鏌ヨ鐭ヨ瘑搴撴彃浠跺弬鏁�
+ KnowledgeBaseEntity knowledgeBaseEntity = knowledgeBaseService.selectById(mapping.getPluginId());
+ if (knowledgeBaseEntity == null) {
+ list.remove(i);
+ continue;
+ }
+ ModelConfigEntity modelConfigEntity = modelConfigService
+ .getModelByIdFromCache(knowledgeBaseEntity.getRagModelId());
+ if (modelConfigEntity == null) {
+ list.remove(i);
+ continue;
+ }
+ List<KnowledgeBaseEntity> knowledgeBaseList = knowledgeBaseMap.get(modelConfigEntity.getModelCode());
+ if (knowledgeBaseList == null) {
+ knowledgeBaseList = new ArrayList<>();
+ }
+ modelConfigMap.put(modelConfigEntity.getModelCode(), modelConfigEntity);
+ knowledgeBaseList.add(knowledgeBaseEntity);
+ knowledgeBaseMap.put(modelConfigEntity.getModelCode(), knowledgeBaseList);
+ list.remove(i);
+ }
+ }
+ if (knowledgeBaseMap.size() > 0) {
+ for (String pluginCode : knowledgeBaseMap.keySet()) {
+ List<KnowledgeBaseEntity> knowledgeBaseList = knowledgeBaseMap.get(pluginCode);
+ if (knowledgeBaseList == null || knowledgeBaseList.isEmpty()) {
+ continue;
+ }
+
+ AgentPluginMapping agentPluginMapping = new AgentPluginMapping();
+ agentPluginMapping.setAgentId(agentId);
+ agentPluginMapping.setPluginId(pluginCode);
+ agentPluginMapping.setProviderCode("search_from_" + pluginCode);
+ agentPluginMapping.setId(Long.valueOf(list.size() + 1));
+
+ Map<String, Object> paramInfo = new HashMap<>(4);
+ ModelConfigEntity modelConfigEntity = modelConfigMap.get(pluginCode);
+ paramInfo.put("base_url", modelConfigEntity.getConfigJson().getStr("base_url"));
+ paramInfo.put("api_key", modelConfigEntity.getConfigJson().getStr("api_key"));
+ paramInfo.put("dataset_ids",
+ knowledgeBaseList.stream().map(KnowledgeBaseEntity::getDatasetId).toList());
+
+ String description = "濡傛灉鐢ㄦ埛璇㈤棶涓庛��"
+ + String.join(",", knowledgeBaseList.stream().map(KnowledgeBaseEntity::getName).toList())
+ + "銆戞兜鐩栫殑涓讳綋鑼冨洿鐩稿叧鍐呭鏃跺簲璋冪敤鏈柟娉曪紝鐢ㄤ簬鏌ヨ锛�" + String.join(",",
+ knowledgeBaseList.stream().map(KnowledgeBaseEntity::getDescription).toList());
+ paramInfo.put("description", description);
+ agentPluginMapping.setParamInfo(JsonUtils.toJsonString(paramInfo));
+ list.add(agentPluginMapping);
+ }
+ }
+ return list;
+ }
+
+ @Override
+ public void deleteByAgentId(String agentId) {
+ UpdateWrapper<AgentPluginMapping> updateWrapper = new UpdateWrapper<>();
+ updateWrapper.eq("agent_id", agentId);
+ agentPluginMappingMapper.delete(updateWrapper);
+ }
+
+ @Override
+ public void deleteByPluginId(String pluginId) {
+ UpdateWrapper<AgentPluginMapping> updateWrapper = new UpdateWrapper<>();
+ updateWrapper.eq("plugin_id", pluginId);
+ agentPluginMappingMapper.delete(updateWrapper);
+ }
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentServiceImpl.java
new file mode 100644
index 0000000..b7c54a7
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentServiceImpl.java
@@ -0,0 +1,719 @@
+package xiaozhi.modules.agent.service.impl;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.repository.IRepository;
+
+import cn.hutool.core.collection.CollUtil;
+import lombok.AllArgsConstructor;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.redis.RedisKeys;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.common.service.impl.BaseServiceImpl;
+import xiaozhi.common.user.UserDetail;
+import xiaozhi.common.utils.ConvertUtils;
+import xiaozhi.common.utils.JsonUtils;
+import xiaozhi.modules.agent.dao.AgentDao;
+import xiaozhi.modules.agent.dao.AgentTagDao;
+import xiaozhi.modules.agent.dto.AgentCreateDTO;
+import xiaozhi.modules.agent.dto.AgentDTO;
+import xiaozhi.modules.agent.dto.AgentMemoryDTO;
+import xiaozhi.modules.agent.dto.AgentTagDTO;
+import xiaozhi.modules.agent.dto.AgentUpdateDTO;
+import xiaozhi.modules.agent.entity.AgentContextProviderEntity;
+import xiaozhi.modules.agent.entity.AgentEntity;
+import xiaozhi.modules.agent.entity.AgentPluginMapping;
+import xiaozhi.modules.agent.entity.AgentTagEntity;
+import xiaozhi.modules.agent.entity.AgentTemplateEntity;
+import xiaozhi.modules.agent.service.AgentChatHistoryService;
+import xiaozhi.modules.agent.service.AgentContextProviderService;
+import xiaozhi.modules.agent.service.AgentPluginMappingService;
+import xiaozhi.modules.agent.service.AgentService;
+import xiaozhi.modules.agent.service.AgentSnapshotService;
+import xiaozhi.modules.agent.service.AgentTagService;
+import xiaozhi.modules.agent.service.AgentTemplateService;
+import xiaozhi.modules.agent.vo.AgentInfoVO;
+import xiaozhi.modules.correctword.service.CorrectWordFileService;
+import xiaozhi.modules.device.entity.DeviceEntity;
+import xiaozhi.modules.device.service.DeviceService;
+import xiaozhi.modules.model.dto.ModelProviderDTO;
+import xiaozhi.modules.model.dto.VoiceDTO;
+import xiaozhi.modules.model.entity.ModelConfigEntity;
+import xiaozhi.modules.model.service.ModelConfigService;
+import xiaozhi.modules.model.service.ModelProviderService;
+import xiaozhi.modules.security.user.SecurityUser;
+import xiaozhi.modules.sys.enums.SuperAdminEnum;
+import xiaozhi.modules.timbre.service.TimbreService;
+
+@Service
+@AllArgsConstructor
+public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> implements AgentService {
+ private final AgentDao agentDao;
+ private final AgentTagDao agentTagDao;
+ private final TimbreService timbreModelService;
+ private final ModelConfigService modelConfigService;
+ private final RedisUtils redisUtils;
+ private final DeviceService deviceService;
+ private final AgentPluginMappingService agentPluginMappingService;
+ private final AgentChatHistoryService agentChatHistoryService;
+ private final AgentTemplateService agentTemplateService;
+ private final ModelProviderService modelProviderService;
+ private final AgentContextProviderService agentContextProviderService;
+ private final AgentTagService agentTagService;
+ private final CorrectWordFileService correctWordFileService;
+ private final AgentSnapshotService agentSnapshotService;
+
+ @Override
+ public PageData<AgentEntity> adminAgentList(Map<String, Object> params) {
+ IPage<AgentEntity> page = agentDao.selectPage(
+ getPage(params, "agent_name", true),
+ new QueryWrapper<>());
+ return new PageData<>(page.getRecords(), page.getTotal());
+ }
+
+ @Override
+ public AgentInfoVO getAgentById(String id) {
+ AgentInfoVO agent = agentDao.selectAgentInfoById(id);
+
+ if (agent == null) {
+ throw new RenException(ErrorCode.AGENT_NOT_FOUND);
+ }
+ requireCurrentUserPermissionIfPresent(agent);
+
+ if (agent.getMemModelId() != null && agent.getMemModelId().equals(Constant.MEMORY_NO_MEM)) {
+ agent.setChatHistoryConf(Constant.ChatHistoryConfEnum.IGNORE.getCode());
+ }
+ if (agent.getChatHistoryConf() == null) {
+ agent.setChatHistoryConf(Constant.ChatHistoryConfEnum.RECORD_TEXT_AUDIO.getCode());
+ }
+
+ // 鏌ヨ涓婁笅鏂囨簮閰嶇疆
+ AgentContextProviderEntity contextProviderEntity = agentContextProviderService.getByAgentId(id);
+ if (contextProviderEntity != null) {
+ agent.setContextProviders(contextProviderEntity.getContextProviders());
+ }
+
+ // 鏌ヨ鏇挎崲璇嶆枃浠禝D鍒楄〃
+ List<String> correctWordFileIds = correctWordFileService.getAgentCorrectWordFileIds(id);
+ agent.setCorrectWordFileIds(correctWordFileIds);
+ agent.setCurrentVersionNo(agentSnapshotService.getCurrentVersionNo(id));
+
+ // 鏃犻渶棰濆鏌ヨ鎻掍欢鍒楄〃锛屽凡閫氳繃SQL鏌ヨ鍑烘潵
+ return agent;
+ }
+
+ @Override
+ public AgentInfoVO getAgentById(String id, Long userId) {
+ AgentInfoVO agent = getAgentById(id);
+ requireAgentPermission(agent, userId);
+ return agent;
+ }
+
+ private AgentEntity getAgentEntityOrThrow(String agentId) {
+ AgentEntity agent = agentDao.selectById(agentId);
+ if (agent == null) {
+ throw new RenException(ErrorCode.AGENT_NOT_FOUND);
+ }
+ return agent;
+ }
+
+ private boolean isCurrentUserSuperAdmin() {
+ UserDetail user = SecurityUser.getUser();
+ return user != null && Integer.valueOf(SuperAdminEnum.YES.value()).equals(user.getSuperAdmin());
+ }
+
+ private void requireCurrentUserPermissionIfPresent(AgentEntity agent) {
+ Long userId = SecurityUser.getUserId();
+ if (userId != null) {
+ requireAgentPermission(agent, userId);
+ }
+ }
+
+ private boolean hasAgentPermission(AgentEntity agent, Long userId) {
+ if (agent == null) {
+ return false;
+ }
+ if (isCurrentUserSuperAdmin()) {
+ return true;
+ }
+ return userId != null && userId.equals(agent.getUserId());
+ }
+
+ private void requireAgentPermission(AgentEntity agent, Long userId) {
+ if (!hasAgentPermission(agent, userId)) {
+ throw new RenException(ErrorCode.NO_PERMISSION);
+ }
+ }
+
+ private boolean hasDevicePermission(DeviceEntity device, Long userId) {
+ if (device == null) {
+ return false;
+ }
+ if (isCurrentUserSuperAdmin()) {
+ return true;
+ }
+ return userId != null && userId.equals(device.getUserId());
+ }
+
+ private void requireDevicePermission(DeviceEntity device, Long userId) {
+ if (!hasDevicePermission(device, userId)) {
+ throw new RenException(ErrorCode.NO_PERMISSION);
+ }
+ }
+
+ @Override
+ public boolean insert(AgentEntity entity) {
+ // 濡傛灉ID涓虹┖锛岃嚜鍔ㄧ敓鎴愪竴涓猆UID浣滀负ID
+ if (entity.getId() == null || entity.getId().trim().isEmpty()) {
+ entity.setId(UUID.randomUUID().toString().replace("-", ""));
+ }
+
+ // 濡傛灉鏅鸿兘浣撶紪鐮佷负绌猴紝鑷姩鐢熸垚涓�涓甫鍓嶇紑鐨勭紪鐮�
+ if (entity.getAgentCode() == null || entity.getAgentCode().trim().isEmpty()) {
+ entity.setAgentCode("AGT_" + System.currentTimeMillis());
+ }
+
+ // 濡傛灉鎺掑簭瀛楁涓虹┖锛岃缃粯璁ゅ��0
+ if (entity.getSort() == null) {
+ entity.setSort(0);
+ }
+
+ return super.insert(entity);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteAgentByUserId(Long userId) {
+ List<AgentEntity> agents = baseDao.selectList(new QueryWrapper<AgentEntity>()
+ .select("id")
+ .eq("user_id", userId));
+ for (AgentEntity agent : agents) {
+ deleteAgent(agent.getId());
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteAgent(String agentId) {
+ if (agentDao.selectByIdForUpdate(agentId) == null) {
+ return;
+ }
+ deviceService.deleteByAgentId(agentId);
+ agentChatHistoryService.deleteByAgentId(agentId, true, true);
+ agentPluginMappingService.deleteByAgentId(agentId);
+ agentContextProviderService.deleteByAgentId(agentId);
+ correctWordFileService.deleteMappingsByAgentId(agentId);
+ agentTagService.deleteAgentTags(agentId);
+ agentSnapshotService.deleteByAgentId(agentId);
+ deleteById(agentId);
+ }
+
+ @Override
+ public List<AgentDTO> getUserAgents(Long userId, String keyword, String searchType) {
+ QueryWrapper<AgentEntity> queryWrapper = new QueryWrapper<>();
+ queryWrapper.eq("user_id", userId).orderByDesc("created_at");
+
+ if (StringUtils.isNotBlank(keyword)) {
+ queryWrapper.and(w -> {
+ // 鎸夊悕绉版悳绱�
+ w.like("agent_name", keyword);
+
+ // 鎸塎AC鍦板潃鎼滅储锛氬厛鏌ヨ澶囷紝鍐嶈幏鍙栧搴旂殑鏅鸿兘浣揑D
+ List<DeviceEntity> devices = Optional
+ .ofNullable(deviceService.searchDevicesByMacAddress(keyword, userId))
+ .orElseGet(ArrayList::new);
+ List<String> agentIds = devices.stream()
+ .map(DeviceEntity::getAgentId)
+ .distinct()
+ .collect(Collectors.toList());
+ if (CollUtil.isNotEmpty(agentIds)) {
+ w.or().in("id", agentIds);
+ }
+
+ // 鎸夋爣绛惧悕鎼滅储
+ List<String> tagAgentIds = agentTagService.getAgentIdsByTagName(keyword);
+ if (CollUtil.isNotEmpty(tagAgentIds)) {
+ w.or().in("id", tagAgentIds);
+ }
+ });
+ }
+
+ List<AgentEntity> agentEntities = baseDao.selectList(queryWrapper);
+ return agentEntities.stream().map(this::buildAgentDTO).collect(Collectors.toList());
+ }
+
+ /**
+ * 灏咥gentEntity杞崲涓篈gentDTO
+ */
+ private AgentDTO buildAgentDTO(AgentEntity agent) {
+ AgentDTO dto = new AgentDTO();
+ dto.setId(agent.getId());
+ dto.setAgentName(agent.getAgentName());
+ dto.setSystemPrompt(agent.getSystemPrompt());
+
+ // 鑾峰彇 TTS 妯″瀷鍚嶇О
+ dto.setTtsModelName(modelConfigService.getModelNameById(agent.getTtsModelId()));
+
+ // 鑾峰彇 LLM 妯″瀷鍚嶇О
+ dto.setLlmModelName(modelConfigService.getModelNameById(agent.getLlmModelId()));
+
+ // 鑾峰彇 VLLM 妯″瀷鍚嶇О
+ dto.setVllmModelName(modelConfigService.getModelNameById(agent.getVllmModelId()));
+
+ // 鑾峰彇璁板繂妯″瀷鍚嶇О
+ dto.setMemModelId(agent.getMemModelId());
+
+ // 鑾峰彇 TTS 闊宠壊鍚嶇О
+ dto.setTtsVoiceName(timbreModelService.getTimbreNameById(agent.getTtsVoiceId()));
+
+ // 鑾峰彇鏅鸿兘浣撴渶杩戠殑鏈�鍚庤繛鎺ユ椂闀�
+ dto.setLastConnectedAt(deviceService.getLatestLastConnectionTime(agent.getId()));
+
+ // 鑾峰彇璁惧鏁伴噺
+ dto.setDeviceCount(getDeviceCountByAgentId(agent.getId()));
+
+ // 鑾峰彇鏍囩鍒楄〃
+ List<AgentTagEntity> tags = agentTagDao.selectByAgentId(agent.getId());
+ if (CollUtil.isNotEmpty(tags)) {
+ dto.setTags(tags.stream().map(this::convertTagToDTO).collect(Collectors.toList()));
+ }
+
+ return dto;
+ }
+
+ private AgentTagDTO convertTagToDTO(AgentTagEntity entity) {
+ AgentTagDTO dto = new AgentTagDTO();
+ dto.setId(entity.getId());
+ dto.setTagName(entity.getTagName());
+ return dto;
+ }
+
+ @Override
+ public Integer getDeviceCountByAgentId(String agentId) {
+ if (StringUtils.isBlank(agentId)) {
+ return 0;
+ }
+
+ // 鍏堜粠Redis涓幏鍙�
+ Integer cachedCount = (Integer) redisUtils.get(RedisKeys.getAgentDeviceCountById(agentId));
+ if (cachedCount != null) {
+ return cachedCount;
+ }
+
+ // 濡傛灉Redis涓病鏈夛紝鍒欎粠鏁版嵁搴撴煡璇�
+ Integer deviceCount = agentDao.getDeviceCountByAgentId(agentId);
+
+ // 灏嗙粨鏋滃瓨鍏edis
+ if (deviceCount != null) {
+ redisUtils.set(RedisKeys.getAgentDeviceCountById(agentId), deviceCount, 60);
+ }
+
+ return deviceCount != null ? deviceCount : 0;
+ }
+
+ @Override
+ public AgentEntity getDefaultAgentByMacAddress(String macAddress) {
+ if (StringUtils.isEmpty(macAddress)) {
+ return null;
+ }
+ return agentDao.getDefaultAgentByMacAddress(macAddress);
+ }
+
+ @Override
+ public boolean checkAgentPermission(String agentId, Long userId) {
+ AgentEntity agent = agentDao.selectById(agentId);
+ return hasAgentPermission(agent, userId);
+ }
+
+ // 鏍规嵁id鏇存柊鏅鸿兘浣撲俊鎭�
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updateAgentById(String agentId, AgentUpdateDTO dto) {
+ updateAgentById(agentId, dto, true);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updateAgentById(String agentId, AgentUpdateDTO dto, Long userId) {
+ updateAgentById(agentId, dto, userId, true);
+ }
+
+ // 鏍规嵁id鏇存柊鏅鸿兘浣撲俊鎭�
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updateAgentById(String agentId, AgentUpdateDTO dto, boolean createSnapshot) {
+ updateAgentById(agentId, dto, null, createSnapshot);
+ }
+
+ private void updateAgentById(String agentId, AgentUpdateDTO dto, Long userId, boolean createSnapshot) {
+ AgentEntity lockedAgent = agentDao.selectByIdForUpdate(agentId);
+ if (lockedAgent == null) {
+ throw new RenException(ErrorCode.AGENT_NOT_FOUND);
+ }
+ if (userId == null) {
+ requireCurrentUserPermissionIfPresent(lockedAgent);
+ } else {
+ requireAgentPermission(lockedAgent, userId);
+ }
+
+ // 閿佸畾鍚庢煡璇㈢幇鏈夊疄浣撳拰鍏宠仈閰嶇疆
+ AgentEntity existingEntity = this.getAgentById(agentId);
+ if (createSnapshot) {
+ int currentVersionNo = agentSnapshotService.getCurrentVersionNo(agentId);
+ agentSnapshotService.createSnapshot(agentId, currentVersionNo == 0 ? "initial" : "current");
+ }
+
+ // 鍙洿鏂版彁渚涚殑闈炵┖瀛楁
+ if (dto.getAgentName() != null) {
+ existingEntity.setAgentName(dto.getAgentName());
+ }
+ if (dto.getAgentCode() != null) {
+ existingEntity.setAgentCode(dto.getAgentCode());
+ }
+ if (dto.getAsrModelId() != null) {
+ existingEntity.setAsrModelId(dto.getAsrModelId());
+ }
+ if (dto.getVadModelId() != null) {
+ existingEntity.setVadModelId(dto.getVadModelId());
+ }
+ if (dto.getLlmModelId() != null) {
+ existingEntity.setLlmModelId(dto.getLlmModelId());
+ }
+ if (dto.getSlmModelId() != null) {
+ existingEntity.setSlmModelId(dto.getSlmModelId());
+ }
+ if (dto.getVllmModelId() != null) {
+ existingEntity.setVllmModelId(dto.getVllmModelId());
+ }
+ if (dto.getTtsModelId() != null) {
+ existingEntity.setTtsModelId(dto.getTtsModelId());
+ }
+ if (dto.getTtsVoiceId() != null) {
+ existingEntity.setTtsVoiceId(dto.getTtsVoiceId());
+ }
+ if (dto.getTtsLanguage() != null) {
+ existingEntity.setTtsLanguage(dto.getTtsLanguage());
+ }
+ if (dto.getTtsVolume() != null) {
+ existingEntity.setTtsVolume(dto.getTtsVolume());
+ }
+ if (dto.getTtsRate() != null) {
+ existingEntity.setTtsRate(dto.getTtsRate());
+ }
+ if (dto.getTtsPitch() != null) {
+ existingEntity.setTtsPitch(dto.getTtsPitch());
+ }
+ if (dto.getMemModelId() != null) {
+ existingEntity.setMemModelId(dto.getMemModelId());
+ }
+ if (dto.getIntentModelId() != null) {
+ existingEntity.setIntentModelId(dto.getIntentModelId());
+ }
+ if (dto.getSystemPrompt() != null) {
+ existingEntity.setSystemPrompt(dto.getSystemPrompt());
+ }
+ if (dto.getSummaryMemory() != null) {
+ existingEntity.setSummaryMemory(dto.getSummaryMemory());
+ }
+ if (dto.getChatHistoryConf() != null) {
+ existingEntity.setChatHistoryConf(dto.getChatHistoryConf());
+ }
+ if (dto.getLangCode() != null) {
+ existingEntity.setLangCode(dto.getLangCode());
+ }
+ if (dto.getLanguage() != null) {
+ existingEntity.setLanguage(dto.getLanguage());
+ }
+ if (dto.getSort() != null) {
+ existingEntity.setSort(dto.getSort());
+ }
+
+ // 鏇存柊鍑芥暟鎻掍欢淇℃伅
+ List<AgentUpdateDTO.FunctionInfo> functions = dto.getFunctions();
+ if (functions != null) {
+ // 1. 鏀堕泦鏈鎻愪氦鐨� pluginId
+ List<String> newPluginIds = functions.stream()
+ .map(AgentUpdateDTO.FunctionInfo::getPluginId)
+ .toList();
+
+ // 2. 鏌ヨ褰撳墠agent鐜版湁鐨勬墍鏈夋槧灏�
+ List<AgentPluginMapping> existing = agentPluginMappingService.list(
+ new QueryWrapper<AgentPluginMapping>()
+ .eq("agent_id", agentId));
+ Map<String, AgentPluginMapping> existMap = existing.stream()
+ .collect(Collectors.toMap(AgentPluginMapping::getPluginId, Function.identity()));
+
+ // 3. 鏋勯�犳墍鏈夎 淇濆瓨鎴栨洿鏂� 鐨勫疄浣�
+ List<AgentPluginMapping> allToPersist = functions.stream().map(info -> {
+ AgentPluginMapping m = new AgentPluginMapping();
+ m.setAgentId(agentId);
+ m.setPluginId(info.getPluginId());
+ m.setParamInfo(JsonUtils.toJsonString(info.getParamInfo()));
+ AgentPluginMapping old = existMap.get(info.getPluginId());
+ if (old != null) {
+ // 宸插瓨鍦紝璁剧疆id琛ㄧず鏇存柊
+ m.setId(old.getId());
+ }
+ return m;
+ }).toList();
+
+ // 4. 鎷嗗垎锛氬凡鏈塈D鐨勮蛋鏇存柊锛屾棤ID鐨勮蛋鎻掑叆
+ List<AgentPluginMapping> toUpdate = allToPersist.stream()
+ .filter(m -> m.getId() != null)
+ .toList();
+ List<AgentPluginMapping> toInsert = allToPersist.stream()
+ .filter(m -> m.getId() == null)
+ .toList();
+
+ if (!toUpdate.isEmpty()) {
+ agentPluginMappingService.updateBatchById(toUpdate, IRepository.DEFAULT_BATCH_SIZE);
+ }
+ if (!toInsert.isEmpty()) {
+ agentPluginMappingService.saveBatch(toInsert, IRepository.DEFAULT_BATCH_SIZE);
+ }
+
+ // 5. 鍒犻櫎鏈涓嶅湪鎻愪氦鍒楄〃閲岀殑鎻掍欢鏄犲皠
+ List<Long> toDelete = existing.stream()
+ .filter(old -> !newPluginIds.contains(old.getPluginId()))
+ .map(AgentPluginMapping::getId)
+ .toList();
+ if (!toDelete.isEmpty()) {
+ agentPluginMappingService.removeByIds(toDelete);
+ }
+ }
+
+ // 璁剧疆鏇存柊鑰呬俊鎭�
+ UserDetail user = SecurityUser.getUser();
+ existingEntity.setUpdater(user.getId());
+ existingEntity.setUpdatedAt(new Date());
+
+ // 鏇存柊璁板繂绛栫暐
+ // 鍒犻櫎鎵�鏈夎褰�
+ if (existingEntity.getMemModelId() != null && existingEntity.getMemModelId().equals(Constant.MEMORY_NO_MEM)) {
+ agentChatHistoryService.deleteByAgentId(existingEntity.getId(), true, true);
+ existingEntity.setSummaryMemory("");
+ // 鍒犻櫎璁板繂
+ } else if (existingEntity.getMemModelId() != null
+ && existingEntity.getMemModelId().equals(Constant.MEMORY_MEM_REPORT_ONLY)) {
+ existingEntity.setSummaryMemory("");
+ }
+
+ // 鏇存柊涓婁笅鏂囨簮閰嶇疆
+ if (dto.getContextProviders() != null) {
+ AgentContextProviderEntity contextEntity = new AgentContextProviderEntity();
+ contextEntity.setAgentId(agentId);
+ contextEntity.setContextProviders(dto.getContextProviders());
+ agentContextProviderService.saveOrUpdateByAgentId(contextEntity);
+ }
+
+ // 鏇存柊鏇挎崲璇嶆枃浠跺叧鑱�
+ if (dto.getCorrectWordFileIds() != null) {
+ correctWordFileService.saveAgentCorrectWords(agentId, dto.getCorrectWordFileIds());
+ }
+
+ // 鏇存柊鏅鸿兘浣撴爣绛�
+ if (dto.getTagNames() != null || dto.getTagIds() != null) {
+ agentTagService.saveAgentTags(agentId, dto.getTagIds(), dto.getTagNames());
+ }
+
+ boolean b = validateLLMIntentParams(existingEntity.getLlmModelId(), existingEntity.getIntentModelId());
+ if (!b) {
+ throw new RenException(ErrorCode.LLM_INTENT_PARAMS_MISMATCH);
+ }
+ this.updateById(existingEntity);
+ if (createSnapshot) {
+ agentSnapshotService.createSnapshot(agentId, "config");
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updateAgentMemoryByDeviceMacAddress(String macAddress, AgentMemoryDTO dto, Long userId) {
+ DeviceEntity device = deviceService.getDeviceByMacAddress(macAddress);
+ if (device == null || StringUtils.isBlank(device.getAgentId()) || dto == null) {
+ return;
+ }
+
+ requireDevicePermission(device, userId);
+
+ AgentUpdateDTO agentUpdateDTO = new AgentUpdateDTO();
+ agentUpdateDTO.setSummaryMemory(dto.getSummaryMemory());
+ updateAgentById(device.getAgentId(), agentUpdateDTO, userId, false);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteAgentById(String agentId, Long userId) {
+ AgentEntity agent = getAgentEntityOrThrow(agentId);
+ requireAgentPermission(agent, userId);
+ deleteAgent(agentId);
+ }
+
+ /**
+ * 楠岃瘉澶ц瑷�妯″瀷鍜屾剰鍥捐瘑鍒殑鍙傛暟鏄惁绗﹀悎鍖归厤
+ *
+ * @param llmModelId 澶ц瑷�妯″瀷id
+ * @param intentModelId 鎰忓浘璇嗗埆id
+ * @return T 鍖归厤 : F 涓嶅尮閰�
+ */
+ private boolean validateLLMIntentParams(String llmModelId, String intentModelId) {
+ if (StringUtils.isBlank(llmModelId)) {
+ return true;
+ }
+ ModelConfigEntity llmModelData = modelConfigService.selectById(llmModelId);
+ String type = llmModelData.getConfigJson().get("type").toString();
+ // 濡傛灉鏌ヨ澶ц瑷�妯″瀷鏄痮penai鎴栬�卭llama锛屾剰鍥捐瘑鍒�夊弬鏁伴兘鍙互
+ if ("openai".equals(type) || "ollama".equals(type)) {
+ return true;
+ }
+ // 闄や簡openai鍜宱llama鐨勭被鍨嬶紝涓嶅彲浠ラ�夋嫨id涓篒ntent_function_call锛堝嚱鏁拌皟鐢級鐨勬剰鍥捐瘑鍒�
+ return !"Intent_function_call".equals(intentModelId);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public String createAgent(AgentCreateDTO dto) {
+ // 杞崲涓哄疄浣�
+ AgentEntity entity = ConvertUtils.sourceToTarget(dto, AgentEntity.class);
+
+ // 鑾峰彇榛樿妯℃澘
+ AgentTemplateEntity template = agentTemplateService.getDefaultTemplate();
+ if (template != null) {
+ // 璁剧疆妯℃澘涓殑榛樿鍊�
+ entity.setAsrModelId(template.getAsrModelId());
+ entity.setVadModelId(template.getVadModelId());
+ entity.setLlmModelId(template.getLlmModelId());
+ entity.setVllmModelId(template.getVllmModelId());
+ entity.setTtsModelId(template.getTtsModelId());
+
+ if (template.getTtsVoiceId() == null && template.getTtsModelId() != null) {
+ ModelConfigEntity ttsModel = modelConfigService.selectById(template.getTtsModelId());
+ if (ttsModel != null && ttsModel.getConfigJson() != null) {
+ Map<String, Object> config = ttsModel.getConfigJson();
+ String voice = (String) config.get("voice");
+ if (StringUtils.isBlank(voice)) {
+ voice = (String) config.get("speaker");
+ }
+ VoiceDTO timbre = timbreModelService.getByVoiceCode(template.getTtsModelId(), voice);
+ if (timbre != null) {
+ template.setTtsVoiceId(timbre.getId());
+ }
+ }
+ }
+
+ entity.setTtsVoiceId(template.getTtsVoiceId());
+ entity.setTtsLanguage(defaultIfBlank(template.getTtsLanguage(),
+ timbreModelService.getDefaultLanguageById(entity.getTtsVoiceId())));
+ entity.setMemModelId(template.getMemModelId());
+ entity.setIntentModelId(template.getIntentModelId());
+ entity.setSystemPrompt(template.getSystemPrompt());
+ entity.setSummaryMemory(template.getSummaryMemory());
+ if (Constant.MEMORY_NO_MEM.equals(entity.getMemModelId())
+ || Constant.MEMORY_MEM_REPORT_ONLY.equals(entity.getMemModelId())) {
+ entity.setSummaryMemory("");
+ }
+
+ // 鏍规嵁璁板繂妯″瀷绫诲瀷璁剧疆榛樿鐨刢hatHistoryConf鍊�
+ if (template.getMemModelId() != null) {
+ if (template.getMemModelId().equals("Memory_nomem")) {
+ // 鏃犺蹇嗗姛鑳界殑妯″瀷锛岄粯璁や笉璁板綍鑱婂ぉ璁板綍
+ entity.setChatHistoryConf(0);
+ } else {
+ // 鏈夎蹇嗗姛鑳界殑妯″瀷锛岄粯璁よ褰曟枃鏈拰璇煶
+ entity.setChatHistoryConf(2);
+ }
+ } else {
+ entity.setChatHistoryConf(template.getChatHistoryConf());
+ }
+
+ entity.setLangCode(template.getLangCode());
+ entity.setLanguage(template.getLanguage());
+ }
+
+ if (entity.getSlmModelId() == null) {
+ String defaultSlmModelId = getDefaultLLMModelId();
+ if (defaultSlmModelId != null) {
+ entity.setSlmModelId(defaultSlmModelId);
+ }
+ }
+
+ // 璁剧疆鐢ㄦ埛ID鍜屽垱寤鸿�呬俊鎭�
+ UserDetail user = SecurityUser.getUser();
+ entity.setUserId(user.getId());
+ entity.setCreator(user.getId());
+ entity.setCreatedAt(new Date());
+
+ // 淇濆瓨鏅鸿兘浣�
+ insert(entity);
+
+ // 璁剧疆榛樿鎻掍欢
+ List<AgentPluginMapping> toInsert = new ArrayList<>();
+ // 鎾斁闊充箰銆佹煡澶╂皵銆佹煡鏂伴椈
+ String[] pluginIds = new String[] { "SYSTEM_PLUGIN_MUSIC", "SYSTEM_PLUGIN_WEATHER",
+ "SYSTEM_PLUGIN_NEWS_NEWSNOW" };
+ for (String pluginId : pluginIds) {
+ ModelProviderDTO provider = modelProviderService.getById(pluginId);
+ if (provider == null) {
+ continue;
+ }
+ AgentPluginMapping mapping = new AgentPluginMapping();
+ mapping.setPluginId(pluginId);
+
+ Map<String, Object> paramInfo = new HashMap<>();
+ List<Map<String, Object>> fields = JsonUtils.parseMapList(provider.getFields());
+ if (fields != null) {
+ for (Map<String, Object> field : fields) {
+ paramInfo.put((String) field.get("key"), field.get("default"));
+ }
+ }
+ mapping.setParamInfo(JsonUtils.toJsonString(paramInfo));
+ mapping.setAgentId(entity.getId());
+ toInsert.add(mapping);
+ }
+ // 淇濆瓨榛樿鎻掍欢
+ agentPluginMappingService.saveBatch(toInsert, IRepository.DEFAULT_BATCH_SIZE);
+ agentSnapshotService.createSnapshot(entity.getId(), "initial");
+ return entity.getId();
+ }
+
+ private String defaultIfBlank(String value, String defaultValue) {
+ return StringUtils.isBlank(value) ? defaultValue : value;
+ }
+
+ private String getDefaultLLMModelId() {
+ try {
+ List<ModelConfigEntity> llmConfigs = modelConfigService.getEnabledModelsByType("LLM");
+ if (llmConfigs == null || llmConfigs.isEmpty()) {
+ return null;
+ }
+
+ for (ModelConfigEntity config : llmConfigs) {
+ if (config.getIsDefault() != null && config.getIsDefault() == 1) {
+ return config.getId();
+ }
+ }
+
+ return llmConfigs.get(0).getId();
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentSnapshotRedactionRunner.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentSnapshotRedactionRunner.java
new file mode 100644
index 0000000..33ff67f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentSnapshotRedactionRunner.java
@@ -0,0 +1,59 @@
+package xiaozhi.modules.agent.service.impl;
+
+import java.util.concurrent.TimeUnit;
+
+import org.springframework.beans.factory.SmartInitializingSingleton;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.modules.agent.service.AgentSnapshotService;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class AgentSnapshotRedactionRunner implements SmartInitializingSingleton {
+ static final long ROLLING_DEPLOYMENT_INITIAL_DELAY_MILLIS = 5_000;
+ static final long ROLLING_DEPLOYMENT_FIXED_DELAY_MILLIS = 15_000;
+
+ private final AgentSnapshotService agentSnapshotService;
+
+ @Override
+ public void afterSingletonsInstantiated() {
+ redactAndReport("startup");
+ }
+
+ @Scheduled(initialDelay = ROLLING_DEPLOYMENT_INITIAL_DELAY_MILLIS,
+ fixedDelay = ROLLING_DEPLOYMENT_FIXED_DELAY_MILLIS)
+ public void redactLateRollingDeploymentWrites() {
+ redactAndReport("rolling-deployment");
+ }
+
+ private void redactAndReport(String trigger) {
+ long startedAt = System.nanoTime();
+ try {
+ long migrated = agentSnapshotService.redactLegacySnapshots();
+ long durationMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startedAt);
+ if (migrated > 0) {
+ log.warn("Agent snapshot legacy redaction trigger={} migrated={} durationMs={}. Rotate credentials "
+ + "that may have appeared in historical snapshot URLs, cookies, sessions, or structured "
+ + "headers.", trigger, migrated, durationMillis);
+ } else if ("startup".equals(trigger)) {
+ log.info("Agent snapshot legacy redaction startup pass completed: migrated=0 durationMs={}; "
+ + "rolling-deployment compensation starts after {} ms and repeats every {} ms.",
+ durationMillis, ROLLING_DEPLOYMENT_INITIAL_DELAY_MILLIS,
+ ROLLING_DEPLOYMENT_FIXED_DELAY_MILLIS);
+ }
+ } catch (RuntimeException exception) {
+ if ("startup".equals(trigger)) {
+ log.error("Agent snapshot legacy redaction failed during startup; blocking application startup "
+ + "before it can accept traffic.", exception);
+ } else {
+ log.error("Agent snapshot legacy redaction failed during rolling-deployment compensation; the "
+ + "scheduler will retry on its next run.", exception);
+ }
+ throw exception;
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentSnapshotServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentSnapshotServiceImpl.java
new file mode 100644
index 0000000..0cab47f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentSnapshotServiceImpl.java
@@ -0,0 +1,1772 @@
+package xiaozhi.modules.agent.service.impl;
+
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HexFormat;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.TreeMap;
+import java.util.UUID;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.metadata.OrderItem;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.repository.IRepository;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import lombok.AllArgsConstructor;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.service.impl.BaseServiceImpl;
+import xiaozhi.common.utils.JsonUtils;
+import xiaozhi.modules.agent.dao.AgentDao;
+import xiaozhi.modules.agent.dao.AgentSnapshotDao;
+import xiaozhi.modules.agent.dao.AgentTagDao;
+import xiaozhi.modules.agent.dao.AgentTagRelationDao;
+import xiaozhi.modules.agent.dto.AgentSnapshotDataDTO;
+import xiaozhi.modules.agent.dto.AgentSnapshotPageDTO;
+import xiaozhi.modules.agent.dto.AgentSnapshotTagDTO;
+import xiaozhi.modules.agent.dto.AgentUpdateDTO;
+import xiaozhi.modules.agent.dto.ContextProviderDTO;
+import xiaozhi.modules.agent.entity.AgentContextProviderEntity;
+import xiaozhi.modules.agent.entity.AgentEntity;
+import xiaozhi.modules.agent.entity.AgentPluginMapping;
+import xiaozhi.modules.agent.entity.AgentSnapshotEntity;
+import xiaozhi.modules.agent.entity.AgentTagEntity;
+import xiaozhi.modules.agent.entity.AgentTagRelationEntity;
+import xiaozhi.modules.agent.Enums.AgentSnapshotField;
+import xiaozhi.modules.agent.service.AgentChatHistoryService;
+import xiaozhi.modules.agent.service.AgentContextProviderService;
+import xiaozhi.modules.agent.service.AgentPluginMappingService;
+import xiaozhi.modules.agent.service.AgentSnapshotService;
+import xiaozhi.modules.agent.service.AgentTagService;
+import xiaozhi.modules.agent.vo.AgentInfoVO;
+import xiaozhi.modules.agent.vo.AgentSnapshotVO;
+import xiaozhi.modules.correctword.service.CorrectWordFileService;
+import xiaozhi.modules.model.entity.ModelConfigEntity;
+import xiaozhi.modules.model.service.ModelConfigService;
+import xiaozhi.modules.security.user.SecurityUser;
+
+@Service
+@AllArgsConstructor
+public class AgentSnapshotServiceImpl extends BaseServiceImpl<AgentSnapshotDao, AgentSnapshotEntity>
+ implements AgentSnapshotService {
+ private static final int MAX_SNAPSHOTS_PER_AGENT = 100;
+ private static final int LEGACY_REDACTION_BATCH_SIZE = 100;
+ private static final int CURRENT_REDACTION_VERSION = 2;
+ private static final TypeReference<List<String>> STRING_LIST_TYPE = new TypeReference<>() {
+ };
+ private static final TypeReference<HashMap<String, Object>> PARAM_INFO_TYPE = new TypeReference<>() {
+ };
+ private static final TypeReference<Map<String, Object>> OBJECT_MAP_TYPE = new TypeReference<>() {
+ };
+ private static final ObjectMapper SNAPSHOT_OBJECT_MAPPER = new ObjectMapper()
+ .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ private static final String SECRET_PLACEHOLDER = "__SNAPSHOT_SECRET_REDACTED__";
+ private static final String SOURCE_CONFIG = "config";
+ private static final String SOURCE_CURRENT_BACKUP = "current";
+ private static final String SOURCE_RESTORE_RESULT = "restore";
+
+ private final AgentSnapshotDao agentSnapshotDao;
+ private final AgentDao agentDao;
+ private final AgentTagDao agentTagDao;
+ private final AgentTagRelationDao agentTagRelationDao;
+ private final AgentPluginMappingService agentPluginMappingService;
+ private final AgentContextProviderService agentContextProviderService;
+ private final AgentTagService agentTagService;
+ private final AgentChatHistoryService agentChatHistoryService;
+ private final ModelConfigService modelConfigService;
+ private final CorrectWordFileService correctWordFileService;
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void createSnapshot(String agentId, String source) {
+ lockAgent(agentId);
+ AgentInfoVO agent = getAgentInfo(agentId);
+ AgentSnapshotDataDTO snapshotData = buildSnapshotData(agent);
+ AgentSnapshotEntity previousSnapshot = agentSnapshotDao.selectLatestSnapshot(agentId);
+ List<String> changedFields;
+ if (previousSnapshot == null) {
+ changedFields = List.of("initial");
+ } else {
+ AgentSnapshotDataDTO previousData = parseSnapshotData(previousSnapshot.getSnapshotData());
+ changedFields = getChangedFields(previousData, snapshotData);
+ }
+ if (changedFields.isEmpty()) {
+ return;
+ }
+
+ insertSnapshot(agentId, agent.getUserId(), source, snapshotData, changedFields);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public PageData<AgentSnapshotVO> page(String agentId, AgentSnapshotPageDTO params) {
+ AgentSnapshotPageDTO pageParams = params == null ? new AgentSnapshotPageDTO() : params;
+ Page<AgentSnapshotEntity> page = new Page<>(pageParams.pageOrDefault(), pageParams.limitOrDefault());
+ page.addOrder(OrderItem.desc("version_no"));
+ IPage<AgentSnapshotEntity> result = agentSnapshotDao.selectPage(page,
+ new QueryWrapper<AgentSnapshotEntity>()
+ .eq("agent_id", agentId)
+ .le(pageParams.getMaxVersionNo() != null, "version_no", pageParams.getMaxVersionNo()));
+ List<AgentSnapshotVO> list = result.getRecords().stream()
+ .map(entity -> toVO(entity, false))
+ .toList();
+ return new PageData<>(list, result.getTotal());
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public AgentSnapshotVO getSnapshot(String agentId, String snapshotId) {
+ lockAgent(agentId);
+ AgentSnapshotEntity entity = getSnapshotEntity(agentId, snapshotId);
+ AgentSnapshotVO detail = toVO(entity, true);
+ AgentSnapshotDataDTO currentData = buildSnapshotData(agentId);
+ detail.setCurrentSnapshotData(redactSnapshotData(currentData));
+ detail.setCurrentStateToken(buildCurrentStateToken(currentData));
+ return detail;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void restoreSnapshot(String agentId, String snapshotId, String currentStateToken) {
+ AgentEntity agent = agentDao.selectByIdForUpdate(agentId);
+ if (agent == null) {
+ throw new RenException(ErrorCode.AGENT_NOT_FOUND);
+ }
+
+ AgentSnapshotEntity snapshot = getSnapshotEntity(agentId, snapshotId);
+ AgentSnapshotDataDTO data = parseSnapshotData(snapshot.getSnapshotData());
+ if (data == null) {
+ throw new RenException("蹇収鏁版嵁涓虹┖锛屾棤娉曟仮澶�");
+ }
+
+ AgentInfoVO currentAgent = getAgentInfo(agentId);
+ AgentSnapshotDataDTO currentData = buildSnapshotData(currentAgent);
+ if (!Objects.equals(buildCurrentStateToken(currentData), currentStateToken)) {
+ throw new RenException("褰撳墠閰嶇疆宸插彉鍖栵紝璇烽噸鏂版墦寮�鎭㈠棰勮鍚庡啀璇�");
+ }
+ AgentSnapshotDataDTO restoreData = preserveCurrentSensitiveValues(data, currentData);
+ validateSensitiveRestoreIsReversible(currentData, restoreData);
+ List<String> requestedChangedFields = getChangedFields(currentData, restoreData);
+ if (requestedChangedFields.isEmpty()) {
+ return;
+ }
+
+ AgentSnapshotEntity latestSnapshot = agentSnapshotDao.selectLatestSnapshot(agentId);
+ AgentSnapshotDataDTO latestData = latestSnapshot == null
+ ? null
+ : parseSnapshotData(latestSnapshot.getSnapshotData());
+ List<String> backupChangedFields = getChangedFields(latestData, currentData);
+ boolean backupCreated = !backupChangedFields.isEmpty();
+ if (backupCreated) {
+ insertSnapshot(agentId, currentAgent.getUserId(), SOURCE_CURRENT_BACKUP, currentData,
+ backupChangedFields, null, null, false);
+ }
+
+ applyAgentFields(agent, restoreData);
+ validateRestoreParams(agent);
+ applyMemoryPolicy(agent);
+ agent.setUpdater(SecurityUser.getUserId());
+ agent.setUpdatedAt(new Date());
+ int updatedRows = agentDao.updateSnapshotFields(agent);
+ // The row was already locked and verified above. Some MySQL configurations report 0 changed rows
+ // when a relation-only restore leaves all scalar columns (including second-precision updated_at) unchanged.
+ if (updatedRows < 0 || updatedRows > 1) {
+ throw new RenException("鏅鸿兘浣撳揩鐓ф仮澶嶅け璐�");
+ }
+
+ restoreFunctions(agentId, restoreData.getFunctions());
+ restoreContextProviders(agentId, restoreData.getContextProviders());
+ correctWordFileService.saveAgentCorrectWords(agentId, nullToEmpty(restoreData.getCorrectWordFileIds()));
+ restoreTags(agentId, restoreData);
+
+ AgentSnapshotDataDTO restoredData = buildSnapshotData(agentId);
+ List<String> restoredChangedFields = getChangedFields(currentData, restoredData);
+ if (!restoredChangedFields.isEmpty()) {
+ insertSnapshot(agentId, currentAgent.getUserId(), SOURCE_RESTORE_RESULT, restoredData,
+ restoredChangedFields, snapshot.getId(), snapshot.getVersionNo(), false);
+ }
+ if (backupCreated || !restoredChangedFields.isEmpty()) {
+ pruneSnapshots(agentId);
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteSnapshot(String agentId, String snapshotId) {
+ lockAgent(agentId);
+ AgentSnapshotEntity entity = getSnapshotEntity(agentId, snapshotId);
+ Integer maxVersionNo = agentSnapshotDao.selectMaxVersionNo(agentId);
+ if (Objects.equals(entity.getVersionNo(), maxVersionNo)) {
+ throw new RenException("鏈�鏂板巻鍙茬増鏈笉鑳藉垹闄�");
+ }
+ deleteById(snapshotId);
+ }
+
+ @Override
+ public Integer getCurrentVersionNo(String agentId) {
+ Integer maxVersionNo = agentSnapshotDao.selectMaxVersionNo(agentId);
+ return maxVersionNo == null ? 0 : maxVersionNo;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteByAgentId(String agentId) {
+ agentSnapshotDao.delete(new QueryWrapper<AgentSnapshotEntity>().eq("agent_id", agentId));
+ }
+
+ @Override
+ public long redactLegacySnapshots() {
+ String afterId = null;
+ long migrated = 0;
+ while (true) {
+ List<AgentSnapshotEntity> batch = agentSnapshotDao.selectLegacyRedactionBatch(afterId,
+ LEGACY_REDACTION_BATCH_SIZE, CURRENT_REDACTION_VERSION);
+ if (batch == null || batch.isEmpty()) {
+ return migrated;
+ }
+ for (AgentSnapshotEntity snapshot : batch) {
+ Map<String, Object> rawData = JsonUtils.parseObject(snapshot.getSnapshotData(), OBJECT_MAP_TYPE);
+ if (rawData == null) {
+ throw new RenException("鍘嗗彶蹇収鏁版嵁鏃犳硶瑙f瀽锛屽凡涓鑴辨晱杩佺Щ: " + snapshot.getId());
+ }
+ snapshot.setSnapshotData(JsonUtils.toJsonString(redactSensitiveMap(rawData)));
+ }
+ int affected = agentSnapshotDao.updateRedactedSnapshots(batch, CURRENT_REDACTION_VERSION);
+ if (affected < 0 || affected > batch.size()) {
+ throw new RenException("鍘嗗彶蹇収鎵归噺鑴辨晱杩佺Щ澶辫触");
+ }
+ migrated += affected;
+ afterId = batch.get(batch.size() - 1).getId();
+ }
+ }
+
+ private void insertSnapshot(String agentId, Long userId, String source, AgentSnapshotDataDTO snapshotData,
+ List<String> changedFields) {
+ insertSnapshot(agentId, userId, source, snapshotData, changedFields, null, null);
+ }
+
+ private void insertSnapshot(String agentId, Long userId, String source, AgentSnapshotDataDTO snapshotData,
+ List<String> changedFields, String restoreFromSnapshotId, Integer restoreFromVersionNo) {
+ insertSnapshot(agentId, userId, source, snapshotData, changedFields, restoreFromSnapshotId,
+ restoreFromVersionNo, true);
+ }
+
+ private void insertSnapshot(String agentId, Long userId, String source, AgentSnapshotDataDTO snapshotData,
+ List<String> changedFields, String restoreFromSnapshotId, Integer restoreFromVersionNo,
+ boolean pruneAfterInsert) {
+ AgentSnapshotEntity entity = new AgentSnapshotEntity();
+ entity.setId(UUID.randomUUID().toString().replace("-", ""));
+ entity.setAgentId(agentId);
+ entity.setUserId(userId);
+ entity.setSnapshotData(JsonUtils.toJsonString(redactSnapshotData(snapshotData)));
+ entity.setChangedFields(JsonUtils.toJsonString(changedFields));
+ entity.setSource(StringUtils.defaultIfBlank(source, SOURCE_CONFIG));
+ entity.setRestoreFromSnapshotId(restoreFromSnapshotId);
+ entity.setRestoreFromVersionNo(restoreFromVersionNo);
+ entity.setCreator(SecurityUser.getUserId());
+ entity.setCreatedAt(new Date());
+ entity.setRedactionVersion(CURRENT_REDACTION_VERSION);
+ int inserted = agentSnapshotDao.insertWithNextVersion(entity);
+ if (inserted != 1) {
+ throw new RenException("蹇収鐗堟湰鍙风敓鎴愬け璐�");
+ }
+ if (pruneAfterInsert) {
+ pruneSnapshots(agentId);
+ }
+ }
+
+ private void lockAgent(String agentId) {
+ if (agentDao.selectByIdForUpdate(agentId) == null) {
+ throw new RenException(ErrorCode.AGENT_NOT_FOUND);
+ }
+ }
+
+ private void pruneSnapshots(String agentId) {
+ agentSnapshotDao.deleteOlderThanKeepLimit(agentId, MAX_SNAPSHOTS_PER_AGENT);
+ }
+
+ private AgentSnapshotEntity getSnapshotEntity(String agentId, String snapshotId) {
+ AgentSnapshotEntity entity = selectById(snapshotId);
+ if (entity == null || !Objects.equals(agentId, entity.getAgentId())) {
+ throw new RenException("蹇収涓嶅瓨鍦�");
+ }
+ return entity;
+ }
+
+ private AgentInfoVO getAgentInfo(String agentId) {
+ AgentInfoVO agent = agentDao.selectAgentInfoById(agentId);
+ if (agent == null) {
+ throw new RenException(ErrorCode.AGENT_NOT_FOUND);
+ }
+ return agent;
+ }
+
+ private AgentSnapshotDataDTO buildSnapshotData(String agentId) {
+ return buildSnapshotData(getAgentInfo(agentId));
+ }
+
+ private AgentSnapshotDataDTO buildSnapshotData(AgentInfoVO agent) {
+ AgentSnapshotDataDTO data = new AgentSnapshotDataDTO();
+ data.setAgentCode(agent.getAgentCode());
+ data.setAgentName(agent.getAgentName());
+ data.setAsrModelId(agent.getAsrModelId());
+ data.setVadModelId(agent.getVadModelId());
+ data.setLlmModelId(agent.getLlmModelId());
+ data.setSlmModelId(agent.getSlmModelId());
+ data.setVllmModelId(agent.getVllmModelId());
+ data.setTtsModelId(agent.getTtsModelId());
+ data.setTtsVoiceId(agent.getTtsVoiceId());
+ data.setTtsLanguage(agent.getTtsLanguage());
+ data.setTtsVolume(agent.getTtsVolume());
+ data.setTtsRate(agent.getTtsRate());
+ data.setTtsPitch(agent.getTtsPitch());
+ data.setMemModelId(agent.getMemModelId());
+ data.setIntentModelId(agent.getIntentModelId());
+ data.setChatHistoryConf(agent.getChatHistoryConf());
+ data.setSystemPrompt(agent.getSystemPrompt());
+ data.setSummaryMemory(agent.getSummaryMemory());
+ data.setLangCode(agent.getLangCode());
+ data.setLanguage(agent.getLanguage());
+ data.setSort(agent.getSort());
+ data.setFunctions(toFunctionInfo(agent.getFunctions()));
+ data.setContextProviders(getContextProviders(agent.getId()));
+ data.setCorrectWordFileIds(nullToEmpty(correctWordFileService.getAgentCorrectWordFileIds(agent.getId())));
+ List<AgentSnapshotTagDTO> tags = getSnapshotTags(agent.getId());
+ data.setTags(tags);
+ data.setTagNames(tags.stream().map(AgentSnapshotTagDTO::getTagName).toList());
+ return data;
+ }
+
+ private List<AgentSnapshotTagDTO> getSnapshotTags(String agentId) {
+ List<AgentTagEntity> tags = agentTagDao.selectByAgentId(agentId);
+ if (tags == null || tags.isEmpty()) {
+ return Collections.emptyList();
+ }
+ List<AgentSnapshotTagDTO> snapshotTags = new ArrayList<>();
+ for (int i = 0; i < tags.size(); i++) {
+ AgentTagEntity tag = tags.get(i);
+ AgentSnapshotTagDTO snapshotTag = new AgentSnapshotTagDTO();
+ snapshotTag.setId(tag.getId());
+ snapshotTag.setTagName(tag.getTagName());
+ snapshotTag.setSort(i);
+ snapshotTags.add(snapshotTag);
+ }
+ return snapshotTags;
+ }
+
+ private List<AgentUpdateDTO.FunctionInfo> toFunctionInfo(List<AgentPluginMapping> mappings) {
+ if (mappings == null || mappings.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return mappings.stream().map(mapping -> {
+ AgentUpdateDTO.FunctionInfo info = new AgentUpdateDTO.FunctionInfo();
+ info.setPluginId(mapping.getPluginId());
+ info.setParamInfo(parseParamInfo(mapping.getParamInfo()));
+ return info;
+ }).toList();
+ }
+
+ private HashMap<String, Object> parseParamInfo(String paramInfo) {
+ if (StringUtils.isBlank(paramInfo)) {
+ return new HashMap<>();
+ }
+ return JsonUtils.parseObject(paramInfo, PARAM_INFO_TYPE);
+ }
+
+ private List<xiaozhi.modules.agent.dto.ContextProviderDTO> getContextProviders(String agentId) {
+ AgentContextProviderEntity contextEntity = agentContextProviderService.getByAgentId(agentId);
+ if (contextEntity == null || contextEntity.getContextProviders() == null) {
+ return Collections.emptyList();
+ }
+ return contextEntity.getContextProviders();
+ }
+
+ private List<String> getChangedFields(AgentSnapshotDataDTO current, AgentUpdateDTO pendingUpdate) {
+ if (pendingUpdate == null) {
+ return List.of("restore");
+ }
+
+ List<String> fields = new ArrayList<>();
+ for (AgentSnapshotField field : AgentSnapshotField.values()) {
+ Object nextValue = field.updateValue(pendingUpdate);
+ if (nextValue != null && isChanged(field, field.snapshotValue(current), nextValue)) {
+ fields.add(field.getFieldName());
+ }
+ }
+ return fields;
+ }
+
+ private List<String> getChangedFields(AgentSnapshotDataDTO current, AgentSnapshotDataDTO next) {
+ if (next == null) {
+ return List.of("restore");
+ }
+ if (current == null) {
+ current = new AgentSnapshotDataDTO();
+ }
+
+ List<String> fields = new ArrayList<>();
+ for (AgentSnapshotField field : AgentSnapshotField.values()) {
+ if (isChanged(field, field.snapshotValue(current), field.snapshotValue(next))) {
+ fields.add(field.getFieldName());
+ }
+ }
+ return fields;
+ }
+
+ private String buildCurrentStateToken(AgentSnapshotDataDTO data) {
+ Map<String, Object> normalized = new TreeMap<>();
+ for (AgentSnapshotField field : AgentSnapshotField.values()) {
+ normalized.put(field.getFieldName(), normalizeForCompare(field, field.snapshotValue(data)));
+ }
+ byte[] payload = JsonUtils.toJsonString(normalized).getBytes(StandardCharsets.UTF_8);
+ try {
+ return HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(payload));
+ } catch (NoSuchAlgorithmException exception) {
+ throw new IllegalStateException("SHA-256 is unavailable", exception);
+ }
+ }
+
+ private boolean isChanged(AgentSnapshotField field, Object current, Object next) {
+ return !Objects.equals(normalizeForCompare(field, current), normalizeForCompare(field, next));
+ }
+
+ @SuppressWarnings("unchecked")
+ private Object normalizeForCompare(AgentSnapshotField field, Object value) {
+ return switch (field) {
+ case FUNCTIONS -> normalizeFunctions((List<AgentUpdateDTO.FunctionInfo>) value);
+ case CONTEXT_PROVIDERS -> normalizeJsonList((List<?>) value);
+ case CORRECT_WORD_FILE_IDS -> normalizeStringList((List<String>) value);
+ case TAG_NAMES -> normalizeStringList((List<String>) value);
+ case SUMMARY_MEMORY -> normalizeBlankText(value);
+ default -> value;
+ };
+ }
+
+ private String normalizeBlankText(Object value) {
+ if (value == null) {
+ return "";
+ }
+ String text = String.valueOf(value);
+ return StringUtils.isBlank(text) ? "" : text;
+ }
+
+ private Map<String, Object> normalizeFunctions(List<AgentUpdateDTO.FunctionInfo> functions) {
+ Map<String, Object> result = new TreeMap<>();
+ if (functions == null) {
+ return result;
+ }
+ for (AgentUpdateDTO.FunctionInfo function : functions) {
+ if (function == null || StringUtils.isBlank(function.getPluginId())) {
+ continue;
+ }
+ result.put(function.getPluginId(), normalizeMap(function.getParamInfo()));
+ }
+ return result;
+ }
+
+ private List<String> normalizeStringList(List<String> values) {
+ if (values == null) {
+ return Collections.emptyList();
+ }
+ return values.stream()
+ .filter(StringUtils::isNotBlank)
+ .sorted()
+ .toList();
+ }
+
+ private <T> List<String> normalizeJsonList(List<T> values) {
+ if (values == null) {
+ return Collections.emptyList();
+ }
+ return values.stream()
+ .filter(Objects::nonNull)
+ .map(value -> JsonUtils.toJsonString(normalizeValue(value)))
+ .toList();
+ }
+
+ private Object normalizeValue(Object value) {
+ return normalizeValue(value, null);
+ }
+
+ private Object normalizeValue(Object value, String parentKey) {
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof CharSequence text && shouldTreatAsUrl(parentKey, text.toString())) {
+ return normalizeUrlForCompare(text.toString(), parentKey);
+ }
+ if (value instanceof Map<?, ?> map) {
+ return normalizeMap(map, parentKey);
+ }
+ if (value instanceof List<?> list) {
+ return list.stream().map(item -> normalizeValue(item, parentKey)).toList();
+ }
+ if (isScalarValue(value)) {
+ return value;
+ }
+ return normalizeMap(JsonUtils.parseObject(JsonUtils.toJsonString(value), OBJECT_MAP_TYPE), parentKey);
+ }
+
+ private boolean isScalarValue(Object value) {
+ return value instanceof CharSequence
+ || value instanceof Number
+ || value instanceof Boolean
+ || value instanceof Character
+ || value instanceof Enum<?>
+ || value instanceof Date;
+ }
+
+ private Map<String, Object> normalizeMap(Map<?, ?> map) {
+ return normalizeMap(map, null);
+ }
+
+ private Map<String, Object> normalizeMap(Map<?, ?> map, String parentSemanticKey) {
+ Map<String, Object> result = new TreeMap<>();
+ if (map == null) {
+ return result;
+ }
+ String structuredSensitiveKey = getStructuredSensitiveKey(map);
+ map.forEach((key, value) -> {
+ if (key != null) {
+ String keyText = String.valueOf(key);
+ String semanticKey = resolveUrlSemanticKey(parentSemanticKey, keyText);
+ if (isSensitiveKey(keyText)
+ || (structuredSensitiveKey != null && "value".equalsIgnoreCase(keyText))) {
+ result.put(keyText, SECRET_PLACEHOLDER);
+ } else if (value instanceof CharSequence text && shouldTreatAsUrl(semanticKey, text.toString())) {
+ result.put(keyText, normalizeUrlForCompare(text.toString(), semanticKey));
+ } else {
+ result.put(keyText, normalizeValue(value, semanticKey));
+ }
+ }
+ });
+ return result;
+ }
+
+ private void applyAgentFields(AgentEntity agent, AgentSnapshotDataDTO data) {
+ for (AgentSnapshotField field : AgentSnapshotField.values()) {
+ field.applyTo(agent, data);
+ }
+ }
+
+ private void validateRestoreParams(AgentEntity agent) {
+ if (agent == null || StringUtils.isBlank(agent.getLlmModelId())) {
+ return;
+ }
+
+ ModelConfigEntity llmModelData = modelConfigService.selectById(agent.getLlmModelId());
+ if (llmModelData == null || llmModelData.getConfigJson() == null) {
+ throw new RenException(ErrorCode.LLM_INTENT_PARAMS_MISMATCH);
+ }
+ Object typeValue = llmModelData.getConfigJson().get("type");
+ String type = typeValue == null ? "" : typeValue.toString();
+ if ("openai".equals(type) || "ollama".equals(type)) {
+ return;
+ }
+ if ("Intent_function_call".equals(agent.getIntentModelId())) {
+ throw new RenException(ErrorCode.LLM_INTENT_PARAMS_MISMATCH);
+ }
+ }
+
+ private void applyMemoryPolicy(AgentEntity agent) {
+ if (agent == null || StringUtils.isBlank(agent.getMemModelId())) {
+ return;
+ }
+ if (Constant.MEMORY_NO_MEM.equals(agent.getMemModelId())) {
+ agentChatHistoryService.deleteByAgentId(agent.getId(), true, true);
+ agent.setSummaryMemory("");
+ } else if (Constant.MEMORY_MEM_REPORT_ONLY.equals(agent.getMemModelId())) {
+ agent.setSummaryMemory("");
+ }
+ }
+
+ private void restoreFunctions(String agentId, List<AgentUpdateDTO.FunctionInfo> functions) {
+ agentPluginMappingService.deleteByAgentId(agentId);
+ if (functions == null || functions.isEmpty()) {
+ return;
+ }
+ List<AgentPluginMapping> mappings = functions.stream().map(info -> {
+ AgentPluginMapping mapping = new AgentPluginMapping();
+ mapping.setAgentId(agentId);
+ mapping.setPluginId(info.getPluginId());
+ mapping.setParamInfo(JsonUtils.toJsonString(info.getParamInfo()));
+ return mapping;
+ }).toList();
+ agentPluginMappingService.saveBatch(mappings, IRepository.DEFAULT_BATCH_SIZE);
+ }
+
+ private void restoreContextProviders(String agentId,
+ List<xiaozhi.modules.agent.dto.ContextProviderDTO> contextProviders) {
+ AgentContextProviderEntity entity = new AgentContextProviderEntity();
+ entity.setAgentId(agentId);
+ entity.setContextProviders(nullToEmpty(contextProviders));
+ entity.setUpdater(SecurityUser.getUserId());
+ entity.setUpdatedAt(new Date());
+ agentContextProviderService.saveOrUpdateByAgentId(entity);
+ }
+
+ private void restoreTags(String agentId, AgentSnapshotDataDTO data) {
+ List<AgentSnapshotTagDTO> tags = data.getTags();
+ if (tags == null || tags.isEmpty()) {
+ agentTagService.saveAgentTags(agentId, null, nullToEmpty(data.getTagNames()));
+ return;
+ }
+
+ agentTagRelationDao.deleteByAgentId(agentId);
+ List<AgentTagRelationEntity> relations = new ArrayList<>();
+ Date now = new Date();
+ for (int i = 0; i < tags.size(); i++) {
+ AgentSnapshotTagDTO snapshotTag = tags.get(i);
+ String tagId = restoreTag(snapshotTag, now);
+ if (StringUtils.isBlank(tagId)) {
+ continue;
+ }
+ AgentTagRelationEntity relation = new AgentTagRelationEntity();
+ relation.setId(UUID.randomUUID().toString().replace("-", ""));
+ relation.setAgentId(agentId);
+ relation.setTagId(tagId);
+ relation.setSort(i);
+ relation.setCreator(SecurityUser.getUserId());
+ relation.setCreatedAt(now);
+ relation.setUpdater(SecurityUser.getUserId());
+ relation.setUpdatedAt(now);
+ relations.add(relation);
+ }
+ if (!relations.isEmpty()) {
+ agentTagRelationDao.batchInsertRelation(relations);
+ }
+ }
+
+ private String restoreTag(AgentSnapshotTagDTO snapshotTag, Date now) {
+ if (snapshotTag == null || StringUtils.isBlank(snapshotTag.getTagName())) {
+ return null;
+ }
+
+ AgentTagEntity tag = null;
+ if (StringUtils.isNotBlank(snapshotTag.getId())) {
+ tag = agentTagDao.selectById(snapshotTag.getId());
+ }
+ if (tag == null) {
+ tag = agentTagDao.selectOne(new QueryWrapper<AgentTagEntity>()
+ .eq("tag_name", snapshotTag.getTagName())
+ .last("LIMIT 1"));
+ }
+ if (tag != null) {
+ if (Objects.equals(tag.getDeleted(), 1)) {
+ // Tags are global records shared by every agent that references them. Restoring a
+ // snapshot must not revive a globally deleted tag as a side effect for other agents
+ // (or tenants); require the user to recreate/select an active tag explicitly.
+ throw new RenException("蹇収寮曠敤鐨勬爣绛惧凡琚垹闄わ紝鏃犳硶鎭㈠锛岃鍏堥噸鏂板垱寤烘垨閫夋嫨鏍囩");
+ }
+ return tag.getId();
+ }
+
+ AgentTagEntity newTag = new AgentTagEntity();
+ newTag.setId(UUID.randomUUID().toString().replace("-", ""));
+ newTag.setTagName(snapshotTag.getTagName());
+ newTag.setSort(snapshotTag.getSort() == null ? 0 : snapshotTag.getSort());
+ newTag.setDeleted(0);
+ newTag.setCreator(SecurityUser.getUserId());
+ newTag.setCreatedAt(now);
+ newTag.setUpdater(SecurityUser.getUserId());
+ newTag.setUpdatedAt(now);
+ agentTagDao.insert(newTag);
+ return newTag.getId();
+ }
+
+ private AgentSnapshotVO toVO(AgentSnapshotEntity entity, boolean includeData) {
+ AgentSnapshotVO vo = new AgentSnapshotVO();
+ vo.setId(entity.getId());
+ vo.setAgentId(entity.getAgentId());
+ vo.setUserId(entity.getUserId());
+ vo.setVersionNo(entity.getVersionNo());
+ vo.setChangedFields(parseChangedFields(entity.getChangedFields()));
+ vo.setFieldOrder(AgentSnapshotField.names());
+ vo.setSource(entity.getSource());
+ vo.setRestoreFromSnapshotId(entity.getRestoreFromSnapshotId());
+ vo.setRestoreFromVersionNo(entity.getRestoreFromVersionNo());
+ vo.setCreator(entity.getCreator());
+ vo.setCreatedAt(entity.getCreatedAt());
+ if (includeData) {
+ vo.setSnapshotData(redactSnapshotData(parseSnapshotData(entity.getSnapshotData())));
+ vo.setAfterSnapshotData(redactSnapshotData(getAfterSnapshotData(entity)));
+ }
+ return vo;
+ }
+
+ private AgentSnapshotDataDTO getAfterSnapshotData(AgentSnapshotEntity entity) {
+ AgentSnapshotEntity nextSnapshot = agentSnapshotDao.selectNextSnapshot(entity.getAgentId(), entity.getVersionNo());
+ if (nextSnapshot != null) {
+ return parseSnapshotData(nextSnapshot.getSnapshotData());
+ }
+ return null;
+ }
+
+ private List<String> parseChangedFields(String changedFields) {
+ if (StringUtils.isBlank(changedFields)) {
+ return Collections.emptyList();
+ }
+ return JsonUtils.parseObject(changedFields, STRING_LIST_TYPE);
+ }
+
+ /**
+ * Snapshot payloads outlive the application version that wrote them. Keep
+ * forward-compatible deserialization local to this persistence boundary so
+ * unrelated API DTOs retain the project's normal unknown-field validation.
+ * Unknown fields are intentionally ignored here; they are not restored by
+ * this older application version.
+ */
+ private AgentSnapshotDataDTO parseSnapshotData(String value) {
+ if (StringUtils.isBlank(value)) {
+ return null;
+ }
+ try {
+ return SNAPSHOT_OBJECT_MAPPER.readValue(value, AgentSnapshotDataDTO.class);
+ } catch (Exception exception) {
+ throw new RenException("蹇収鏁版嵁鏃犳硶瑙f瀽");
+ }
+ }
+
+ private AgentSnapshotDataDTO redactSnapshotData(AgentSnapshotDataDTO data) {
+ if (data == null) {
+ return null;
+ }
+ AgentSnapshotDataDTO copy = parseSnapshotData(JsonUtils.toJsonString(data));
+ if (copy.getFunctions() != null) {
+ copy.getFunctions().forEach(function -> {
+ if (function != null) {
+ function.setParamInfo(redactSensitiveMap(function.getParamInfo()));
+ }
+ });
+ }
+ if (copy.getContextProviders() != null) {
+ copy.getContextProviders().forEach(provider -> {
+ if (provider != null) {
+ provider.setUrl(redactSensitiveUrl(provider.getUrl(), "url"));
+ provider.setHeaders(redactSensitiveMap(provider.getHeaders()));
+ }
+ });
+ }
+ return copy;
+ }
+
+ private AgentSnapshotDataDTO preserveCurrentSensitiveValues(AgentSnapshotDataDTO target, AgentSnapshotDataDTO current) {
+ if (target == null) {
+ return null;
+ }
+ AgentSnapshotDataDTO copy = parseSnapshotData(JsonUtils.toJsonString(target));
+ Map<String, AgentUpdateDTO.FunctionInfo> currentFunctions = nullToEmpty(current == null ? null : current.getFunctions())
+ .stream()
+ .filter(function -> function != null && StringUtils.isNotBlank(function.getPluginId()))
+ .collect(Collectors.toMap(AgentUpdateDTO.FunctionInfo::getPluginId, Function.identity(),
+ (left, right) -> left));
+ if (copy.getFunctions() != null) {
+ copy.getFunctions().forEach(function -> {
+ if (function == null) {
+ return;
+ }
+ AgentUpdateDTO.FunctionInfo currentFunction = currentFunctions.get(function.getPluginId());
+ function.setParamInfo(preserveCurrentSensitiveMap(function.getParamInfo(),
+ currentFunction == null ? null : currentFunction.getParamInfo()));
+ });
+ }
+
+ copy.setContextProviders(preserveCurrentContextProviders(copy.getContextProviders(),
+ current == null ? null : current.getContextProviders()));
+ return copy;
+ }
+
+ private void validateSensitiveRestoreIsReversible(AgentSnapshotDataDTO current,
+ AgentSnapshotDataDTO restored) {
+ try {
+ // Simulate restoring the automatic pre-restore backup from the proposed
+ // result. If a current secret-bearing slot disappears, the reverse
+ // preservation fails because snapshots intentionally never store secrets.
+ preserveCurrentSensitiveValues(current, restored);
+ } catch (RenException exception) {
+ throw new RenException("鐩爣鐗堟湰浼氱Щ闄ゆ棤娉曞啓鍏ュ巻鍙茬殑鏁忔劅閰嶇疆锛岃鍏堟墜鍔ㄥ鐞嗙浉鍏冲瘑閽ュ悗鍐嶆仮澶�");
+ }
+ }
+
+ private HashMap<String, Object> redactSensitiveMap(Map<?, ?> map) {
+ return redactSensitiveMap(map, null);
+ }
+
+ private HashMap<String, Object> redactSensitiveMap(Map<?, ?> map, String parentSemanticKey) {
+ HashMap<String, Object> result = new HashMap<>();
+ if (map == null) {
+ return result;
+ }
+ String structuredSensitiveKey = getStructuredSensitiveKey(map);
+ map.forEach((key, value) -> {
+ if (key != null) {
+ String keyText = String.valueOf(key);
+ String semanticKey = resolveUrlSemanticKey(parentSemanticKey, keyText);
+ if (isSensitiveKey(keyText)
+ || (structuredSensitiveKey != null && "value".equalsIgnoreCase(keyText))) {
+ result.put(keyText, SECRET_PLACEHOLDER);
+ } else if (value instanceof CharSequence text && shouldTreatAsUrl(semanticKey, text.toString())) {
+ result.put(keyText, redactSensitiveUrl(text.toString(), semanticKey));
+ } else {
+ result.put(keyText, redactSensitiveValue(value, semanticKey));
+ }
+ }
+ });
+ return result;
+ }
+
+ private Object redactSensitiveValue(Object value) {
+ return redactSensitiveValue(value, null);
+ }
+
+ private Object redactSensitiveValue(Object value, String parentKey) {
+ if (value instanceof CharSequence text && shouldTreatAsUrl(parentKey, text.toString())) {
+ return redactSensitiveUrl(text.toString(), parentKey);
+ }
+ if (value instanceof Map<?, ?> map) {
+ return redactSensitiveMap(map, parentKey);
+ }
+ if (value instanceof List<?> list) {
+ return list.stream().map(item -> redactSensitiveValue(item, parentKey)).toList();
+ }
+ return value;
+ }
+
+ private HashMap<String, Object> preserveCurrentSensitiveMap(Map<?, ?> target, Map<?, ?> current) {
+ return preserveCurrentSensitiveMap(target, current, null);
+ }
+
+ private HashMap<String, Object> preserveCurrentSensitiveMap(Map<?, ?> target, Map<?, ?> current,
+ String parentSemanticKey) {
+ HashMap<String, Object> result = new HashMap<>();
+ if (target == null) {
+ return result;
+ }
+ String structuredSensitiveKey = getStructuredSensitiveKey(target);
+ if (structuredSensitiveKey != null) {
+ validateStructuredSensitiveDiscriminator(target, current);
+ }
+ target.forEach((key, value) -> {
+ if (key == null) {
+ return;
+ }
+ String keyText = String.valueOf(key);
+ String semanticKey = resolveUrlSemanticKey(parentSemanticKey, keyText);
+ Object currentValue = getMapValue(current, keyText);
+ if (isSensitiveKey(keyText)
+ || (structuredSensitiveKey != null && "value".equalsIgnoreCase(keyText))) {
+ result.put(keyText, preserveSensitiveScalar(value, currentValue));
+ } else if (value instanceof CharSequence targetUrl
+ && shouldTreatAsUrl(semanticKey, targetUrl.toString())) {
+ result.put(keyText, preserveCurrentSensitiveUrl(targetUrl.toString(),
+ currentValue instanceof CharSequence currentUrl ? currentUrl.toString() : null, semanticKey));
+ } else {
+ result.put(keyText, preserveCurrentSensitiveValue(value, currentValue, semanticKey));
+ }
+ });
+ return result;
+ }
+
+ private Object preserveCurrentSensitiveValue(Object target, Object current) {
+ return preserveCurrentSensitiveValue(target, current, null);
+ }
+
+ private Object preserveCurrentSensitiveValue(Object target, Object current, String parentKey) {
+ if (target instanceof CharSequence targetText && shouldTreatAsUrl(parentKey, targetText.toString())) {
+ return preserveCurrentSensitiveUrl(targetText.toString(),
+ current instanceof CharSequence currentText ? currentText.toString() : null, parentKey);
+ }
+ if (target instanceof Map<?, ?> targetMap) {
+ return preserveCurrentSensitiveMap(targetMap,
+ current instanceof Map<?, ?> currentMap ? currentMap : null, parentKey);
+ }
+ if (target instanceof List<?> targetList) {
+ List<?> currentList = current instanceof List<?> list ? list : Collections.emptyList();
+ return preserveCurrentSensitiveList(targetList, currentList, parentKey);
+ }
+ return target;
+ }
+
+ private List<Object> preserveCurrentSensitiveList(List<?> target, List<?> current, String parentKey) {
+ Map<String, List<Object>> targetByIdentity = indexListByStableIdentity(target, parentKey);
+ Map<String, List<Object>> currentByIdentity = indexListByStableIdentity(current, parentKey);
+ List<Object> result = new ArrayList<>();
+ for (Object targetItem : target) {
+ List<Object> matchingCurrentItems = stableListIdentities(targetItem, parentKey).stream()
+ .filter(candidate -> targetByIdentity.getOrDefault(candidate, Collections.emptyList()).size() == 1)
+ .filter(candidate -> currentByIdentity.getOrDefault(candidate, Collections.emptyList()).size() == 1)
+ .map(candidate -> currentByIdentity.get(candidate).get(0))
+ .distinct()
+ .toList();
+ Object currentItem = matchingCurrentItems.size() == 1 ? matchingCurrentItems.get(0) : null;
+ if (currentItem == null && containsSensitiveMaterial(targetItem)) {
+ throw new RenException("蹇収涓殑鏁忔劅鍒楄〃椤圭己灏戝敮涓�绋冲畾鏍囪瘑锛屾棤娉曞畨鍏ㄦ仮澶�");
+ }
+ result.add(preserveCurrentSensitiveValue(targetItem, currentItem, parentKey));
+ }
+ return result;
+ }
+
+ private Map<String, List<Object>> indexListByStableIdentity(List<?> values, String parentKey) {
+ Map<String, List<Object>> result = new HashMap<>();
+ for (Object value : values) {
+ for (String identity : stableListIdentities(value, parentKey)) {
+ result.computeIfAbsent(identity, ignored -> new ArrayList<>()).add(value);
+ }
+ }
+ return result;
+ }
+
+ private List<String> stableListIdentities(Object value, String parentKey) {
+ if (value instanceof CharSequence text && shouldTreatAsUrl(parentKey, text.toString())) {
+ String identity = normalizeUrlIdentity(text.toString(), parentKey);
+ return StringUtils.isBlank(identity) ? Collections.emptyList() : List.of("url:" + identity);
+ }
+ if (!(value instanceof Map<?, ?> map)) {
+ return Collections.emptyList();
+ }
+ List<String> identities = new ArrayList<>();
+ for (String field : List.of("id", "name", "key", "url")) {
+ Object identityValue = getMapValue(map, field);
+ if (identityValue == null || StringUtils.isBlank(String.valueOf(identityValue))) {
+ continue;
+ }
+ String text = String.valueOf(identityValue).trim();
+ if ("url".equals(field)) {
+ text = normalizeUrlIdentity(text, resolveUrlSemanticKey(parentKey, field));
+ } else if ("name".equals(field) || "key".equals(field)) {
+ text = text.toLowerCase(Locale.ROOT);
+ }
+ identities.add(field + ":" + text);
+ }
+ if (!map.isEmpty()) {
+ identities.add("fingerprint:" + JsonUtils.toJsonString(normalizeMap(map, parentKey)));
+ }
+ return identities;
+ }
+
+ private Object preserveSensitiveScalar(Object target, Object current) {
+ if (current != null && !SECRET_PLACEHOLDER.equals(current)) {
+ return current;
+ }
+ throw new RenException("蹇収鏁忔劅鍊兼棤娉曚笌褰撳墠閰嶇疆鍙潬鍖归厤");
+ }
+
+ private Object getMapValue(Map<?, ?> map, String key) {
+ if (map == null) {
+ return null;
+ }
+ if (map.containsKey(key)) {
+ return map.get(key);
+ }
+ for (Map.Entry<?, ?> entry : map.entrySet()) {
+ if (entry.getKey() != null && key.equalsIgnoreCase(String.valueOf(entry.getKey()))) {
+ return entry.getValue();
+ }
+ }
+ return null;
+ }
+
+ private List<ContextProviderDTO> preserveCurrentContextProviders(List<ContextProviderDTO> target,
+ List<ContextProviderDTO> current) {
+ if (target == null) {
+ return null;
+ }
+ List<ContextProviderDTO> currentProviders = nullToEmpty(current);
+ Map<String, List<ContextProviderDTO>> targetByIdentity = indexProvidersByIdentity(target);
+ Map<String, List<ContextProviderDTO>> currentByIdentity = indexProvidersByIdentity(currentProviders);
+
+ for (ContextProviderDTO targetProvider : target) {
+ if (targetProvider == null) {
+ continue;
+ }
+ String identity = normalizeUrlIdentity(targetProvider.getUrl());
+ ContextProviderDTO currentProvider = null;
+ if (StringUtils.isNotBlank(identity)
+ && targetByIdentity.getOrDefault(identity, Collections.emptyList()).size() == 1
+ && currentByIdentity.getOrDefault(identity, Collections.emptyList()).size() == 1) {
+ currentProvider = currentByIdentity.get(identity).get(0);
+ } else if (hasSensitiveUrlParts(targetProvider.getUrl())
+ || containsSensitiveMaterial(targetProvider.getHeaders())) {
+ throw new RenException("涓婁笅鏂囨簮鏁忔劅閰嶇疆缂哄皯鍞竴 URL 鏍囪瘑锛屾棤娉曞畨鍏ㄦ仮澶�");
+ }
+
+ if (targetProvider.getUrl() != null) {
+ targetProvider.setUrl(preserveCurrentSensitiveUrl(targetProvider.getUrl(),
+ currentProvider == null ? null : currentProvider.getUrl(), "url"));
+ }
+ targetProvider.setHeaders(preserveCurrentSensitiveMap(targetProvider.getHeaders(),
+ currentProvider == null ? null : currentProvider.getHeaders()));
+ }
+ return target;
+ }
+
+ private Map<String, List<ContextProviderDTO>> indexProvidersByIdentity(List<ContextProviderDTO> providers) {
+ Map<String, List<ContextProviderDTO>> result = new HashMap<>();
+ for (ContextProviderDTO provider : providers) {
+ if (provider == null) {
+ continue;
+ }
+ String identity = normalizeUrlIdentity(provider.getUrl());
+ if (StringUtils.isNotBlank(identity)) {
+ result.computeIfAbsent(identity, ignored -> new ArrayList<>()).add(provider);
+ }
+ }
+ return result;
+ }
+
+ private String getStructuredSensitiveKey(Map<?, ?> map) {
+ if (map == null) {
+ return null;
+ }
+ for (String discriminator : List.of("key", "name")) {
+ Object value = getMapValue(map, discriminator);
+ if (value != null && isSensitiveKey(String.valueOf(value))) {
+ return String.valueOf(value);
+ }
+ }
+ return null;
+ }
+
+ private void validateStructuredSensitiveDiscriminator(Map<?, ?> target, Map<?, ?> current) {
+ List<String> targetIdentities = structuredSensitiveDiscriminatorIdentities(target);
+ List<String> currentIdentities = structuredSensitiveDiscriminatorIdentities(current);
+ if (targetIdentities.size() != 1 || currentIdentities.size() != 1
+ || !Objects.equals(targetIdentities.get(0), currentIdentities.get(0))) {
+ throw new RenException("蹇収缁撴瀯鍖栨晱鎰熷�肩殑绫诲瀷鏍囪瘑鏃犳硶涓庡綋鍓嶉厤缃敮涓�鍖归厤");
+ }
+ }
+
+ private List<String> structuredSensitiveDiscriminatorIdentities(Map<?, ?> map) {
+ if (map == null) {
+ return Collections.emptyList();
+ }
+ List<String> identities = new ArrayList<>();
+ for (String discriminator : List.of("key", "name")) {
+ Object value = getMapValue(map, discriminator);
+ if (value == null || !isSensitiveKey(String.valueOf(value))) {
+ continue;
+ }
+ String identity = String.valueOf(value).toLowerCase(Locale.ROOT).replaceAll("[^a-z0-9]", "");
+ if (!identities.contains(identity)) {
+ identities.add(identity);
+ }
+ }
+ return identities;
+ }
+
+ private boolean containsSensitiveMaterial(Object value) {
+ return containsSensitiveMaterial(value, null);
+ }
+
+ private boolean containsSensitiveMaterial(Object value, String parentKey) {
+ if (SECRET_PLACEHOLDER.equals(value)) {
+ return true;
+ }
+ if (value instanceof Map<?, ?> map) {
+ if (getStructuredSensitiveKey(map) != null) {
+ return true;
+ }
+ for (Map.Entry<?, ?> entry : map.entrySet()) {
+ if (entry.getKey() == null) {
+ continue;
+ }
+ String key = String.valueOf(entry.getKey());
+ String semanticKey = resolveUrlSemanticKey(parentKey, key);
+ if (isSensitiveKey(key)
+ || (entry.getValue() instanceof CharSequence text
+ && shouldTreatAsUrl(semanticKey, text.toString())
+ && hasSensitiveUrlParts(text.toString(), semanticKey))
+ || containsSensitiveMaterial(entry.getValue(), semanticKey)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (value instanceof List<?> list) {
+ return list.stream().anyMatch(item -> containsSensitiveMaterial(item, parentKey));
+ }
+ if (value instanceof CharSequence text && shouldTreatAsUrl(parentKey, text.toString())) {
+ return hasSensitiveUrlParts(text.toString(), parentKey);
+ }
+ return false;
+ }
+
+ private boolean isUrlKey(String key) {
+ String normalized = StringUtils.defaultString(key).toLowerCase(Locale.ROOT).replaceAll("[^a-z0-9]", "");
+ return normalized.endsWith("url")
+ || normalized.endsWith("uri")
+ || normalized.endsWith("endpoint")
+ || normalized.endsWith("webhook");
+ }
+
+ private boolean shouldTreatAsUrl(String key, String value) {
+ return isUrlKey(key) || isWebhookSemanticKey(key) || looksLikeUrl(value);
+ }
+
+ private String resolveUrlSemanticKey(String parentSemanticKey, String childKey) {
+ if (isWebhookSemanticKey(childKey)) {
+ return childKey;
+ }
+ if (isWebhookSemanticKey(parentSemanticKey)) {
+ // Keep the concrete child name for URL detection while carrying the
+ // enclosing webhook capability semantics through arbitrary wrapper maps.
+ return StringUtils.isBlank(childKey)
+ ? parentSemanticKey
+ : parentSemanticKey + "." + childKey;
+ }
+ return childKey;
+ }
+
+ private boolean looksLikeUrl(String value) {
+ String text = StringUtils.defaultString(value);
+ if (text.startsWith("//")) {
+ int pathStart = text.indexOf('/', 2);
+ String authority = pathStart < 0 ? text.substring(2) : text.substring(2, pathStart);
+ return StringUtils.isNotBlank(authority) && authority.chars().noneMatch(Character::isWhitespace);
+ }
+ int schemeEnd = text.indexOf("://");
+ if (schemeEnd <= 0 || !Character.isLetter(text.charAt(0))) {
+ return false;
+ }
+ for (int i = 1; i < schemeEnd; i++) {
+ char character = text.charAt(i);
+ if (!Character.isLetterOrDigit(character)
+ && character != '+'
+ && character != '-'
+ && character != '.') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private String normalizeUrlForCompare(String value) {
+ return normalizeUrlForCompare(value, null);
+ }
+
+ private String normalizeUrlForCompare(String value, String semanticKey) {
+ return redactSensitiveUrl(value, semanticKey);
+ }
+
+ private String redactSensitiveUrl(String value) {
+ return redactSensitiveUrl(value, null);
+ }
+
+ private String redactSensitiveUrl(String value, String semanticKey) {
+ if (value == null) {
+ return null;
+ }
+ SensitiveUrlParts parts = splitSensitiveUrl(value);
+ return buildSensitiveUrl(analyzeSensitiveUrlPath(parts.base(), semanticKey).redactedBase(),
+ parts.userInfo() == null ? null : SECRET_PLACEHOLDER,
+ redactSensitiveUrlParameters(parts.query()),
+ redactSensitiveUrlFragment(parts.fragment()));
+ }
+
+ private String preserveCurrentSensitiveUrl(String target, String current) {
+ return preserveCurrentSensitiveUrl(target, current, null);
+ }
+
+ private String preserveCurrentSensitiveUrl(String target, String current, String semanticKey) {
+ if (target == null) {
+ return null;
+ }
+ SensitiveUrlParts targetParts = splitSensitiveUrl(target);
+ SensitiveUrlParts currentParts = splitSensitiveUrl(StringUtils.defaultString(current));
+ SensitivePathAnalysis targetPath = analyzeSensitiveUrlPath(targetParts.base(), semanticKey);
+ boolean forceCurrentGenericMarker = targetPath.slots().stream()
+ .anyMatch(slot -> slot.identity().startsWith("webhook-suffix:"));
+ SensitivePathAnalysis currentPath = analyzeSensitiveUrlPath(currentParts.base(), semanticKey,
+ forceCurrentGenericMarker);
+ boolean copiesSensitiveComponent = targetParts.userInfo() != null
+ || !targetPath.slots().isEmpty()
+ || containsSensitiveUrlParameter(targetParts.query())
+ || (targetParts.fragment() != null
+ && (!isStructuredUrlComponent(targetParts.fragment())
+ || containsSensitiveUrlParameter(targetParts.fragment())));
+ if (copiesSensitiveComponent && !Objects.equals(targetPath.redactedBase(), currentPath.redactedBase())) {
+ throw new RenException("蹇収 URL 鏁忔劅淇℃伅鏃犳硶涓庡綋鍓嶉厤缃殑鍏紑鍦板潃鏍囪瘑鍖归厤");
+ }
+ String result = buildSensitiveUrl(preserveSensitiveUrlPath(targetParts.base(), currentParts.base(), semanticKey),
+ preserveUrlComponent(targetParts.userInfo(), currentParts.userInfo()),
+ preserveSensitiveUrlParameters(targetParts.query(), currentParts.query()),
+ preserveSensitiveUrlFragment(targetParts.fragment(), currentParts.fragment()));
+ if (result.contains(SECRET_PLACEHOLDER)) {
+ throw new RenException("蹇収 URL 涓粛鍖呭惈鑴辨晱鍗犱綅绗︼紝鏃犳硶瀹夊叏鎭㈠");
+ }
+ return result;
+ }
+
+ private String preserveUrlComponent(String target, String current) {
+ if (target == null) {
+ return null;
+ }
+ if (current == null || current.contains(SECRET_PLACEHOLDER)) {
+ throw new RenException("蹇収 URL 涓殑鏁忔劅淇℃伅鏃犳硶涓庡綋鍓嶉厤缃彲闈犲尮閰�");
+ }
+ return current;
+ }
+
+ private String redactSensitiveUrlParameters(String value) {
+ if (value == null) {
+ return null;
+ }
+ return buildUrlParameters(parseUrlParameters(value).stream()
+ .map(parameter -> isSensitiveUrlParameter(parameter)
+ ? new UrlParameter(parameter.rawKey(), SECRET_PLACEHOLDER, parameter.hasEquals())
+ : parameter)
+ .toList());
+ }
+
+ private String redactSensitiveUrlFragment(String value) {
+ if (value == null) {
+ return null;
+ }
+ return isStructuredUrlComponent(value)
+ ? redactSensitiveUrlParameters(value)
+ : SECRET_PLACEHOLDER;
+ }
+
+ private String preserveSensitiveUrlParameters(String target, String current) {
+ if (target == null) {
+ return null;
+ }
+ List<UrlParameter> targetParameters = parseUrlParameters(target);
+ List<UrlParameter> currentParameters = parseUrlParameters(StringUtils.defaultString(current));
+ Map<String, List<UrlParameter>> targetSensitiveParameters = indexSensitiveUrlParameters(targetParameters);
+ Map<String, List<UrlParameter>> currentSensitiveParameters = indexSensitiveUrlParameters(currentParameters);
+ List<UrlParameter> result = new ArrayList<>();
+
+ for (UrlParameter targetParameter : targetParameters) {
+ if (!isSensitiveUrlParameter(targetParameter)) {
+ result.add(targetParameter);
+ continue;
+ }
+ String identity = normalizeUrlParameterName(targetParameter.rawKey());
+ List<UrlParameter> matchingTargets = targetSensitiveParameters.getOrDefault(identity,
+ Collections.emptyList());
+ List<UrlParameter> matchingCurrent = currentSensitiveParameters.getOrDefault(identity,
+ Collections.emptyList());
+ if (matchingTargets.size() != 1 || matchingCurrent.size() != 1) {
+ throw new RenException("蹇収 URL 涓殑鏁忔劅鍙傛暟鏃犳硶涓庡綋鍓嶉厤缃敮涓�鍖归厤");
+ }
+ UrlParameter currentParameter = matchingCurrent.get(0);
+ if (currentParameter.rawValue().contains(SECRET_PLACEHOLDER)) {
+ throw new RenException("蹇収 URL 涓殑鏁忔劅淇℃伅鏃犳硶涓庡綋鍓嶉厤缃彲闈犲尮閰�");
+ }
+ result.add(new UrlParameter(targetParameter.rawKey(), currentParameter.rawValue(),
+ currentParameter.hasEquals()));
+ }
+ return buildUrlParameters(result);
+ }
+
+ private String preserveSensitiveUrlFragment(String target, String current) {
+ if (target == null) {
+ return null;
+ }
+ return isStructuredUrlComponent(target)
+ ? preserveSensitiveUrlParameters(target, current)
+ : preserveUrlComponent(target, current);
+ }
+
+ private Map<String, List<UrlParameter>> indexSensitiveUrlParameters(List<UrlParameter> parameters) {
+ Map<String, List<UrlParameter>> result = new HashMap<>();
+ for (UrlParameter parameter : parameters) {
+ if (isSensitiveUrlParameter(parameter)) {
+ result.computeIfAbsent(normalizeUrlParameterName(parameter.rawKey()), ignored -> new ArrayList<>())
+ .add(parameter);
+ }
+ }
+ return result;
+ }
+
+ private List<UrlParameter> parseUrlParameters(String value) {
+ List<UrlParameter> result = new ArrayList<>();
+ for (String part : StringUtils.defaultString(value).split("&", -1)) {
+ int equalsIndex = part.indexOf('=');
+ if (equalsIndex < 0) {
+ result.add(new UrlParameter(part, "", false));
+ } else {
+ result.add(new UrlParameter(part.substring(0, equalsIndex), part.substring(equalsIndex + 1), true));
+ }
+ }
+ return result;
+ }
+
+ private String buildUrlParameters(List<UrlParameter> parameters) {
+ return parameters.stream()
+ .map(parameter -> parameter.rawKey()
+ + (parameter.hasEquals() ? "=" + parameter.rawValue() : ""))
+ .collect(Collectors.joining("&"));
+ }
+
+ private boolean isSensitiveUrlParameter(UrlParameter parameter) {
+ String decodedName = decodeUrlParameterName(parameter.rawKey());
+ String normalized = decodedName.toLowerCase(Locale.ROOT).replaceAll("[^a-z0-9]", "");
+ return isSensitiveKey(decodedName)
+ || normalized.equals("key")
+ || normalized.equals("sig")
+ || normalized.equals("signature")
+ || normalized.equals("xamzsignature")
+ || normalized.equals("xgoogsignature")
+ || normalized.equals("sas");
+ }
+
+ private String normalizeUrlParameterName(String value) {
+ return decodeUrlParameterName(value).toLowerCase(Locale.ROOT).replaceAll("[^a-z0-9]", "");
+ }
+
+ private String decodeUrlParameterName(String value) {
+ try {
+ return URLDecoder.decode(StringUtils.defaultString(value), StandardCharsets.UTF_8);
+ } catch (IllegalArgumentException ignored) {
+ return StringUtils.defaultString(value);
+ }
+ }
+
+ private boolean isStructuredUrlComponent(String value) {
+ return value != null && (value.contains("=") || value.contains("&"));
+ }
+
+ private boolean hasSensitiveUrlParts(String value) {
+ return hasSensitiveUrlParts(value, null);
+ }
+
+ private boolean hasSensitiveUrlParts(String value, String semanticKey) {
+ if (StringUtils.isBlank(value)) {
+ return false;
+ }
+ SensitiveUrlParts parts = splitSensitiveUrl(value);
+ return parts.userInfo() != null
+ || !analyzeSensitiveUrlPath(parts.base(), semanticKey).slots().isEmpty()
+ || containsSensitiveUrlParameter(parts.query())
+ || (parts.fragment() != null
+ && (!isStructuredUrlComponent(parts.fragment())
+ || containsSensitiveUrlParameter(parts.fragment())));
+ }
+
+ private boolean containsSensitiveUrlParameter(String value) {
+ return value != null && parseUrlParameters(value).stream().anyMatch(this::isSensitiveUrlParameter);
+ }
+
+ private String normalizeUrlIdentity(String value) {
+ return normalizeUrlIdentity(value, null);
+ }
+
+ private String normalizeUrlIdentity(String value, String semanticKey) {
+ if (StringUtils.isBlank(value)) {
+ return "";
+ }
+ SensitiveUrlParts parts = splitSensitiveUrl(value);
+ return analyzeSensitiveUrlPath(parts.base(), semanticKey).redactedBase();
+ }
+
+ private String preserveSensitiveUrlPath(String targetBase, String currentBase, String semanticKey) {
+ SensitivePathAnalysis target = analyzeSensitiveUrlPath(targetBase, semanticKey);
+ if (target.slots().isEmpty()) {
+ return targetBase;
+ }
+ boolean forceCurrentGenericMarker = target.slots().stream()
+ .anyMatch(slot -> slot.identity().startsWith("webhook-suffix:"));
+ SensitivePathAnalysis current = analyzeSensitiveUrlPath(currentBase, semanticKey, forceCurrentGenericMarker);
+ if (!Objects.equals(target.redactedBase(), current.redactedBase())
+ || target.slots().size() != current.slots().size()) {
+ throw new RenException("蹇収 URL 璺緞鏁忔劅淇℃伅鏃犳硶涓庡綋鍓嶉厤缃殑鍏紑鏍囪瘑鍞竴鍖归厤");
+ }
+
+ Map<String, List<SensitivePathSlot>> currentSlots = current.slots().stream()
+ .collect(Collectors.groupingBy(SensitivePathSlot::identity));
+ Map<String, SensitivePathSlot> replacements = new HashMap<>();
+ for (SensitivePathSlot targetSlot : target.slots()) {
+ List<SensitivePathSlot> matches = currentSlots.getOrDefault(targetSlot.identity(), Collections.emptyList());
+ if (matches.size() != 1 || matches.get(0).secretSegments().isEmpty()
+ || matches.get(0).secretSegments().stream()
+ .anyMatch(secret -> StringUtils.isBlank(secret) || secret.contains(SECRET_PLACEHOLDER))) {
+ throw new RenException("蹇収 URL 璺緞鏁忔劅淇℃伅鏃犳硶涓庡綋鍓嶉厤缃彲闈犲尮閰�");
+ }
+ if (replacements.put(targetSlot.identity(), matches.get(0)) != null) {
+ throw new RenException("蹇収 URL 璺緞鏁忔劅淇℃伅瀛樺湪姝т箟锛屾棤娉曞畨鍏ㄦ仮澶�");
+ }
+ }
+ return rebuildSensitivePath(target, replacements);
+ }
+
+ private SensitivePathAnalysis analyzeSensitiveUrlPath(String base, String semanticKey) {
+ return analyzeSensitiveUrlPath(base, semanticKey, false);
+ }
+
+ private SensitivePathAnalysis analyzeSensitiveUrlPath(String base, String semanticKey,
+ boolean forceGenericMarker) {
+ UrlPathParts pathParts = splitUrlPath(base);
+ if (pathParts == null || pathParts.segments().isEmpty()) {
+ return new SensitivePathAnalysis(base, base, Collections.emptyList());
+ }
+ List<String> segments = pathParts.segments();
+ List<SensitivePathSlot> slots = new ArrayList<>();
+ List<Integer> claimedMarkers = new ArrayList<>();
+
+ if (isSlackHooksHost(pathParts.host())) {
+ for (int i = 0; i + 3 < segments.size(); i++) {
+ if ("services".equalsIgnoreCase(segments.get(i))
+ && allPathSegmentsPresent(segments, i + 1, i + 4)) {
+ addSensitivePathSlot(slots, new SensitivePathSlot("slack:" + i, i + 3, i + 4, "",
+ List.of(segments.get(i + 3))));
+ }
+ }
+ }
+
+ if (isDiscordHost(pathParts.host())) {
+ for (int i = 0; i + 2 < segments.size(); i++) {
+ if ("webhooks".equalsIgnoreCase(segments.get(i))
+ && isDiscordWebhookPrefix(segments, i)
+ && allPathSegmentsPresent(segments, i + 1, i + 3)) {
+ addSensitivePathSlot(slots, new SensitivePathSlot("discord:" + i, i + 2, i + 3, "",
+ List.of(segments.get(i + 2))));
+ claimedMarkers.add(i);
+ }
+ }
+ }
+
+ if (isTelegramHost(pathParts.host())) {
+ for (int i = 0; i < segments.size(); i++) {
+ String segment = segments.get(i);
+ int colon = segment.indexOf(':');
+ if (segment.regionMatches(true, 0, "bot", 0, 3)
+ && colon > 3 && colon < segment.length() - 1) {
+ String publicPrefix = segment.substring(0, colon + 1);
+ addSensitivePathSlot(slots, new SensitivePathSlot("telegram:" + i + ":" + publicPrefix,
+ i, i + 1, publicPrefix, List.of(segment.substring(colon + 1))));
+ }
+ }
+ }
+
+ for (int i = 0; i < segments.size(); i++) {
+ if (!isWebhookPathMarker(segments.get(i)) || claimedMarkers.contains(i) || i + 1 >= segments.size()) {
+ continue;
+ }
+ int suffixEnd = segments.size();
+ while (suffixEnd > i + 1 && StringUtils.isBlank(segments.get(suffixEnd - 1))) {
+ suffixEnd--;
+ }
+ List<String> capabilitySuffix = new ArrayList<>(segments.subList(i + 1, suffixEnd));
+ if (capabilitySuffix.isEmpty()) {
+ continue;
+ }
+ if (capabilitySuffix.stream().anyMatch(StringUtils::isBlank)) {
+ continue;
+ }
+ if (!forceGenericMarker
+ && !isHighConfidenceWebhookCapability(pathParts, semanticKey, capabilitySuffix)) {
+ continue;
+ }
+ addSensitivePathSlot(slots, new SensitivePathSlot("webhook-suffix:" + i, i + 1, suffixEnd, "",
+ capabilitySuffix));
+ break;
+ }
+
+ if (slots.isEmpty() && isWebhookSemanticKey(semanticKey)) {
+ int lastSegment = segments.size() - 1;
+ while (lastSegment >= 0 && StringUtils.isBlank(segments.get(lastSegment))) {
+ lastSegment--;
+ }
+ if (lastSegment >= 0 && StringUtils.isNotBlank(segments.get(lastSegment))) {
+ addSensitivePathSlot(slots, new SensitivePathSlot("webhook-key:" + lastSegment,
+ lastSegment, lastSegment + 1, "", List.of(segments.get(lastSegment))));
+ }
+ }
+
+ if (slots.isEmpty()) {
+ return new SensitivePathAnalysis(base, base, Collections.emptyList());
+ }
+ SensitivePathAnalysis analysis = new SensitivePathAnalysis(base, null, slots);
+ return new SensitivePathAnalysis(base, rebuildSensitivePath(analysis, Collections.emptyMap()), slots);
+ }
+
+ private String rebuildSensitivePath(SensitivePathAnalysis analysis,
+ Map<String, SensitivePathSlot> replacements) {
+ UrlPathParts pathParts = splitUrlPath(analysis.originalBase());
+ if (pathParts == null) {
+ return analysis.originalBase();
+ }
+ Map<Integer, SensitivePathSlot> slotsByStart = analysis.slots().stream()
+ .collect(Collectors.toMap(SensitivePathSlot::startIndex, Function.identity(), (left, right) -> left));
+ List<String> result = new ArrayList<>();
+ for (int i = 0; i < pathParts.segments().size();) {
+ SensitivePathSlot slot = slotsByStart.get(i);
+ if (slot == null) {
+ result.add(pathParts.segments().get(i));
+ i++;
+ continue;
+ }
+ SensitivePathSlot replacement = replacements.get(slot.identity());
+ if (replacement == null) {
+ result.add(slot.publicPrefix() + SECRET_PLACEHOLDER);
+ } else if (StringUtils.isNotEmpty(slot.publicPrefix())) {
+ result.add(slot.publicPrefix() + replacement.secretSegments().get(0));
+ } else {
+ result.addAll(replacement.secretSegments());
+ }
+ i = slot.endIndex();
+ }
+ return pathParts.prefix() + String.join("/", result);
+ }
+
+ private UrlPathParts splitUrlPath(String base) {
+ String value = StringUtils.defaultString(base);
+ int schemeIndex = value.indexOf("://");
+ if (schemeIndex < 0 && !value.startsWith("/")) {
+ if (StringUtils.isBlank(value) || value.chars().anyMatch(Character::isWhitespace)) {
+ return null;
+ }
+ return new UrlPathParts("",
+ new ArrayList<>(List.of(value.split("/", -1))), "");
+ }
+ if (schemeIndex < 0 && !value.startsWith("//")) {
+ return new UrlPathParts("",
+ new ArrayList<>(List.of(value.split("/", -1))), "");
+ }
+ int authorityStart = schemeIndex < 0 ? 2 : schemeIndex + 3;
+ int pathStart = value.indexOf('/', authorityStart);
+ String authority = pathStart < 0 ? value.substring(authorityStart) : value.substring(authorityStart, pathStart);
+ String host = normalizeAuthorityHost(authority);
+ if (pathStart < 0) {
+ return new UrlPathParts(value, Collections.emptyList(), host);
+ }
+ return new UrlPathParts(value.substring(0, pathStart),
+ new ArrayList<>(List.of(value.substring(pathStart).split("/", -1))), host);
+ }
+
+ private String normalizeAuthorityHost(String authority) {
+ String value = StringUtils.defaultString(authority).toLowerCase(Locale.ROOT);
+ if (value.startsWith("[")) {
+ int closingBracket = value.indexOf(']');
+ return closingBracket < 0 ? value : value.substring(0, closingBracket + 1);
+ }
+ int portSeparator = value.lastIndexOf(':');
+ return portSeparator < 0 ? value : value.substring(0, portSeparator);
+ }
+
+ private boolean isSlackHooksHost(String host) {
+ return "hooks.slack.com".equals(host) || "hooks.slack-gov.com".equals(host);
+ }
+
+ private boolean isDiscordHost(String host) {
+ return "discord.com".equals(host) || host.endsWith(".discord.com")
+ || "discordapp.com".equals(host) || host.endsWith(".discordapp.com");
+ }
+
+ private boolean isTelegramHost(String host) {
+ return "api.telegram.org".equals(host);
+ }
+
+ private boolean isDiscordWebhookPrefix(List<String> segments, int markerIndex) {
+ if (markerIndex >= 1 && "api".equalsIgnoreCase(segments.get(markerIndex - 1))) {
+ return true;
+ }
+ return markerIndex >= 2
+ && "api".equalsIgnoreCase(segments.get(markerIndex - 2))
+ && isVersionPathSegment(segments.get(markerIndex - 1));
+ }
+
+ private boolean isVersionPathSegment(String value) {
+ String normalized = StringUtils.defaultString(value).toLowerCase(Locale.ROOT);
+ return normalized.length() > 1 && normalized.charAt(0) == 'v'
+ && normalized.substring(1).chars().allMatch(Character::isDigit);
+ }
+
+ private boolean isWebhookPathMarker(String value) {
+ String normalized = StringUtils.defaultString(value).toLowerCase(Locale.ROOT);
+ return normalized.equals("webhook") || normalized.equals("webhooks")
+ || normalized.equals("hook") || normalized.equals("hooks");
+ }
+
+ private boolean isHighConfidenceWebhookCapability(UrlPathParts pathParts, String semanticKey,
+ List<String> capabilitySuffix) {
+ return isWebhookSemanticKey(semanticKey)
+ || hasWebhookHostLabel(pathParts.host())
+ || isSlackHooksHost(pathParts.host())
+ || isDiscordHost(pathParts.host())
+ || isTelegramHost(pathParts.host())
+ || capabilitySuffix.stream().anyMatch(this::looksLikeCapabilitySecret);
+ }
+
+ private boolean hasWebhookHostLabel(String host) {
+ return List.of(StringUtils.defaultString(host).split("\\.")).stream()
+ .anyMatch(this::isWebhookPathMarker);
+ }
+
+ private boolean looksLikeCapabilitySecret(String value) {
+ String text = StringUtils.defaultString(value);
+ String normalized = text.toLowerCase(Locale.ROOT).replaceAll("[^a-z0-9]", "");
+ if (normalized.contains("secret")
+ || normalized.contains("token")
+ || normalized.contains("capability")
+ || normalized.contains("credential")
+ || normalized.contains("signature")
+ || normalized.contains("apikey")
+ || normalized.contains("accesskey")
+ || normalized.contains("authkey")) {
+ return true;
+ }
+ if (text.length() < 20) {
+ return false;
+ }
+ boolean hasLetter = text.chars().anyMatch(Character::isLetter);
+ boolean hasDigit = text.chars().anyMatch(Character::isDigit);
+ boolean hasSymbol = text.chars().anyMatch(character -> !Character.isLetterOrDigit(character));
+ long distinctCharacters = text.chars().distinct().count();
+ return distinctCharacters >= 10
+ && ((hasLetter && hasDigit) || (hasLetter && hasSymbol) || (hasDigit && hasSymbol));
+ }
+
+ private boolean isWebhookSemanticKey(String value) {
+ String normalized = StringUtils.defaultString(value).toLowerCase(Locale.ROOT).replaceAll("[^a-z0-9]", "");
+ return normalized.contains("webhook") || normalized.equals("hook") || normalized.equals("hooks")
+ || normalized.endsWith("hook") || normalized.endsWith("hooks");
+ }
+
+ private boolean allPathSegmentsPresent(List<String> segments, int fromInclusive, int toExclusive) {
+ return segments.subList(fromInclusive, toExclusive).stream().noneMatch(StringUtils::isBlank);
+ }
+
+ private void addSensitivePathSlot(List<SensitivePathSlot> slots, SensitivePathSlot candidate) {
+ boolean overlaps = slots.stream().anyMatch(slot -> candidate.startIndex() < slot.endIndex()
+ && slot.startIndex() < candidate.endIndex());
+ if (!overlaps) {
+ slots.add(candidate);
+ }
+ }
+
+ private SensitiveUrlParts splitSensitiveUrl(String value) {
+ String remaining = StringUtils.defaultString(value);
+ String fragment = null;
+ int fragmentIndex = remaining.indexOf('#');
+ if (fragmentIndex >= 0) {
+ fragment = remaining.substring(fragmentIndex + 1);
+ remaining = remaining.substring(0, fragmentIndex);
+ }
+ String query = null;
+ int queryIndex = remaining.indexOf('?');
+ if (queryIndex >= 0) {
+ query = remaining.substring(queryIndex + 1);
+ remaining = remaining.substring(0, queryIndex);
+ }
+
+ String userInfo = null;
+ int schemeIndex = remaining.indexOf("://");
+ int authorityStart = schemeIndex >= 0
+ ? schemeIndex + 3
+ : remaining.startsWith("//") ? 2 : -1;
+ if (authorityStart >= 0) {
+ int pathStart = remaining.indexOf('/', authorityStart);
+ if (pathStart < 0) {
+ pathStart = remaining.length();
+ }
+ String authority = remaining.substring(authorityStart, pathStart);
+ int userInfoEnd = authority.lastIndexOf('@');
+ if (userInfoEnd >= 0) {
+ userInfo = authority.substring(0, userInfoEnd);
+ remaining = remaining.substring(0, authorityStart)
+ + authority.substring(userInfoEnd + 1)
+ + remaining.substring(pathStart);
+ }
+ }
+ return new SensitiveUrlParts(remaining, userInfo, query, fragment);
+ }
+
+ private String buildSensitiveUrl(String base, String userInfo, String query, String fragment) {
+ String result = StringUtils.defaultString(base);
+ if (userInfo != null) {
+ int schemeIndex = result.indexOf("://");
+ int authorityStart = schemeIndex >= 0
+ ? schemeIndex + 3
+ : result.startsWith("//") ? 2 : -1;
+ if (authorityStart < 0) {
+ throw new RenException("甯︾敤鎴峰嚟鎹殑 URL 鏍煎紡鏃犳晥锛屾棤娉曞畨鍏ㄦ仮澶�");
+ }
+ result = result.substring(0, authorityStart) + userInfo + "@" + result.substring(authorityStart);
+ }
+ if (query != null) {
+ result += "?" + query;
+ }
+ if (fragment != null) {
+ result += "#" + fragment;
+ }
+ return result;
+ }
+
+ private record SensitiveUrlParts(String base, String userInfo, String query, String fragment) {
+ }
+
+ private record UrlPathParts(String prefix, List<String> segments, String host) {
+ }
+
+ private record SensitivePathAnalysis(String originalBase, String redactedBase,
+ List<SensitivePathSlot> slots) {
+ }
+
+ private record SensitivePathSlot(String identity, int startIndex, int endIndex,
+ String publicPrefix, List<String> secretSegments) {
+ }
+
+ private record UrlParameter(String rawKey, String rawValue, boolean hasEquals) {
+ }
+
+ private boolean isSensitiveKey(String key) {
+ String normalized = StringUtils.defaultString(key).toLowerCase(Locale.ROOT).replaceAll("[^a-z0-9]", "");
+ return normalized.equals("authorization")
+ || normalized.contains("authorization")
+ || normalized.contains("authentication")
+ || normalized.equals("auth")
+ || normalized.endsWith("auth")
+ || normalized.equals("cookie")
+ || normalized.equals("cookie2")
+ || normalized.equals("setcookie")
+ || normalized.equals("setcookie2")
+ || normalized.endsWith("cookie")
+ || normalized.equals("session")
+ || normalized.endsWith("session")
+ || normalized.contains("sessionid")
+ || normalized.contains("sessionkey")
+ || normalized.contains("sessiontoken")
+ || normalized.contains("sessioncookie")
+ || normalized.endsWith("sessid")
+ || normalized.equals("token")
+ || normalized.endsWith("token")
+ || normalized.contains("apikey")
+ || normalized.contains("appkey")
+ || normalized.contains("accesskey")
+ || normalized.contains("subscriptionkey")
+ || normalized.contains("privatekey")
+ || normalized.contains("password")
+ || normalized.contains("passwd")
+ || normalized.contains("secret")
+ || normalized.contains("credential");
+ }
+
+ private <T> List<T> nullToEmpty(List<T> list) {
+ return list == null ? Collections.emptyList() : list;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentTagServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentTagServiceImpl.java
new file mode 100644
index 0000000..5188648
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentTagServiceImpl.java
@@ -0,0 +1,198 @@
+package xiaozhi.modules.agent.service.impl;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+
+import lombok.AllArgsConstructor;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.service.impl.BaseServiceImpl;
+import xiaozhi.modules.agent.dao.AgentTagDao;
+import xiaozhi.modules.agent.dao.AgentTagRelationDao;
+import xiaozhi.modules.agent.dto.AgentTagDTO;
+import xiaozhi.modules.agent.entity.AgentTagEntity;
+import xiaozhi.modules.agent.entity.AgentTagRelationEntity;
+import xiaozhi.modules.agent.service.AgentTagService;
+
+@Service
+@AllArgsConstructor
+public class AgentTagServiceImpl extends BaseServiceImpl<AgentTagDao, AgentTagEntity> implements AgentTagService {
+
+ private final AgentTagRelationDao agentTagRelationDao;
+
+ @Override
+ public AgentTagEntity saveTag(String tagName) {
+ if (tagName == null || tagName.trim().isEmpty()) {
+ throw new RenException(ErrorCode.AGENT_TAG_NAME_EMPTY);
+ }
+
+ QueryWrapper<AgentTagEntity> wrapper = new QueryWrapper<>();
+ wrapper.eq("tag_name", tagName);
+ wrapper.eq("deleted", 0);
+ AgentTagEntity existTag = baseDao.selectOne(wrapper);
+ if (existTag != null) {
+ return existTag;
+ }
+
+ AgentTagEntity tag = new AgentTagEntity();
+ tag.setId(UUID.randomUUID().toString().replace("-", ""));
+ tag.setTagName(tagName);
+ tag.setSort(0);
+ tag.setCreatedAt(new Date());
+ tag.setUpdatedAt(new Date());
+ tag.setDeleted(0);
+ baseDao.insert(tag);
+ return tag;
+ }
+
+ @Override
+ public void deleteTag(String tagId) {
+ AgentTagEntity tag = baseDao.selectById(tagId);
+ if (tag != null) {
+ tag.setDeleted(1);
+ tag.setUpdatedAt(new Date());
+ baseDao.updateById(tag);
+ }
+ }
+
+ @Override
+ public List<AgentTagDTO> getTagsByAgentId(String agentId) {
+ List<AgentTagEntity> tags = baseDao.selectByAgentId(agentId);
+ return tags.stream().map(this::convertToDTO).collect(Collectors.toList());
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void saveAgentTags(String agentId, List<String> tagIds, List<String> tagNames) {
+ agentTagRelationDao.deleteByAgentId(agentId);
+
+ List<AgentTagEntity> currentTags = baseDao.selectByAgentId(agentId);
+ List<String> currentTagNames = currentTags.stream()
+ .map(AgentTagEntity::getTagName)
+ .collect(Collectors.toList());
+
+ List<String> allTagIds = new ArrayList<>();
+ List<String> newTagNames = new ArrayList<>();
+
+ if (tagNames != null && !tagNames.isEmpty()) {
+ Set<String> addedTagNames = new HashSet<>();
+ for (String tagName : tagNames) {
+ if (tagName == null || tagName.trim().isEmpty()) {
+ throw new RenException(ErrorCode.AGENT_TAG_NAME_EMPTY);
+ }
+
+ if (currentTagNames.contains(tagName) || addedTagNames.contains(tagName)) {
+ throw new RenException(ErrorCode.AGENT_TAG_NAME_DUPLICATE);
+ }
+ addedTagNames.add(tagName);
+ newTagNames.add(tagName);
+ }
+ }
+
+ List<AgentTagEntity> existTags = new ArrayList<>();
+ if (!newTagNames.isEmpty()) {
+ existTags = baseDao.selectByTagNames(newTagNames);
+ }
+
+ Map<String, AgentTagEntity> existTagMap = existTags.stream()
+ .collect(Collectors.toMap(AgentTagEntity::getTagName, t -> t, (a, b) -> a));
+
+ List<AgentTagEntity> tagsToInsert = new ArrayList<>();
+ for (String tagName : newTagNames) {
+ AgentTagEntity existTag = existTagMap.get(tagName);
+ if (existTag != null) {
+ allTagIds.add(existTag.getId());
+ } else {
+ AgentTagEntity tag = new AgentTagEntity();
+ tag.setId(UUID.randomUUID().toString().replace("-", ""));
+ tag.setTagName(tagName);
+ tag.setSort(0);
+ tag.setDeleted(0);
+ tag.setCreatedAt(new Date());
+ tag.setUpdatedAt(new Date());
+ tagsToInsert.add(tag);
+ }
+ }
+
+ if (!tagsToInsert.isEmpty()) {
+ baseDao.batchInsert(tagsToInsert);
+ for (AgentTagEntity tag : tagsToInsert) {
+ allTagIds.add(tag.getId());
+ }
+ }
+
+ if (tagIds != null && !tagIds.isEmpty()) {
+ List<AgentTagEntity> tagIdEntities = baseDao.selectByIds(tagIds);
+ for (AgentTagEntity tag : tagIdEntities) {
+ if (tag != null && (currentTagNames.contains(tag.getTagName()) ||
+ newTagNames.contains(tag.getTagName()))) {
+ throw new RenException(ErrorCode.AGENT_TAG_NAME_DUPLICATE);
+ }
+ }
+ allTagIds.addAll(tagIds);
+ }
+
+ if (allTagIds.isEmpty()) {
+ return;
+ }
+
+ List<AgentTagRelationEntity> relations = new ArrayList<>();
+ Date now = new Date();
+ int sort = 0;
+ for (String tagId : allTagIds) {
+ AgentTagRelationEntity relation = new AgentTagRelationEntity();
+ relation.setId(UUID.randomUUID().toString().replace("-", ""));
+ relation.setAgentId(agentId);
+ relation.setTagId(tagId);
+ relation.setSort(sort++);
+ relation.setCreatedAt(now);
+ relation.setUpdatedAt(now);
+ relations.add(relation);
+ }
+ agentTagRelationDao.batchInsertRelation(relations);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteAgentTags(String agentId) {
+ agentTagRelationDao.deleteByAgentId(agentId);
+ }
+
+ @Override
+ public List<AgentTagDTO> getTagsByAgentIds(List<String> agentIds) {
+ if (agentIds == null || agentIds.isEmpty()) {
+ return List.of();
+ }
+ List<AgentTagEntity> tags = baseDao.selectByAgentIds(agentIds);
+ return tags.stream().map(this::convertToDTO).collect(Collectors.toList());
+ }
+
+ @Override
+ public List<AgentTagDTO> getAllTags() {
+ List<AgentTagEntity> tags = baseDao.selectAll();
+ return tags.stream().map(this::convertToDTO).collect(Collectors.toList());
+ }
+
+ @Override
+ public List<String> getAgentIdsByTagName(String tagName) {
+ return baseDao.selectAgentIdsByTagName(tagName);
+ }
+
+ private AgentTagDTO convertToDTO(AgentTagEntity entity) {
+ AgentTagDTO dto = new AgentTagDTO();
+ dto.setId(entity.getId());
+ dto.setTagName(entity.getTagName());
+ return dto;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentTemplateServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentTemplateServiceImpl.java
new file mode 100644
index 0000000..686141b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentTemplateServiceImpl.java
@@ -0,0 +1,125 @@
+package xiaozhi.modules.agent.service.impl;
+
+import org.springframework.stereotype.Service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.baomidou.mybatisplus.spring.repository.CrudRepository;
+
+import xiaozhi.modules.agent.dao.AgentTemplateDao;
+import xiaozhi.modules.agent.entity.AgentTemplateEntity;
+import xiaozhi.modules.agent.service.AgentTemplateService;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+
+/**
+ * @author chenerlei
+ * @description 閽堝琛ㄣ�恆i_agent_template(鏅鸿兘浣撻厤缃ā鏉胯〃)銆戠殑鏁版嵁搴撴搷浣淪ervice瀹炵幇
+ * @createDate 2025-03-22 11:48:18
+ */
+@Service
+public class AgentTemplateServiceImpl extends CrudRepository<AgentTemplateDao, AgentTemplateEntity>
+ implements AgentTemplateService {
+
+ /**
+ * 鑾峰彇榛樿妯℃澘
+ *
+ * @return 榛樿妯℃澘瀹炰綋
+ */
+ public AgentTemplateEntity getDefaultTemplate() {
+ LambdaQueryWrapper<AgentTemplateEntity> wrapper = new LambdaQueryWrapper<>();
+ wrapper.orderByAsc(AgentTemplateEntity::getSort)
+ .last("LIMIT 1");
+ return this.getOne(wrapper);
+ }
+
+ /**
+ * 鏇存柊榛樿妯℃澘涓殑妯″瀷ID
+ *
+ * @param modelType 妯″瀷绫诲瀷
+ * @param modelId 妯″瀷ID
+ */
+ @Override
+ public void updateDefaultTemplateModelId(String modelType, String modelId) {
+ modelType = modelType.toUpperCase();
+ // 濡傛灉鏄痳ag妯″瀷锛屼笉闇�瑕佹洿鏂�
+ if (modelType.equals("RAG")) {
+ return;
+ }
+
+ UpdateWrapper<AgentTemplateEntity> wrapper = new UpdateWrapper<>();
+ switch (modelType) {
+ case "ASR":
+ wrapper.set("asr_model_id", modelId);
+ break;
+ case "VAD":
+ wrapper.set("vad_model_id", modelId);
+ break;
+ case "LLM":
+ wrapper.set("llm_model_id", modelId);
+ break;
+ case "TTS":
+ wrapper.set("tts_model_id", modelId);
+ wrapper.set("tts_voice_id", null);
+ break;
+ case "VLLM":
+ wrapper.set("vllm_model_id", modelId);
+ break;
+ case "MEMORY":
+ wrapper.set("mem_model_id", modelId);
+ break;
+ case "INTENT":
+ wrapper.set("intent_model_id", modelId);
+ break;
+ }
+ wrapper.ge("sort", 0);
+ update(wrapper);
+ }
+
+ @Override
+ public void reorderTemplatesAfterDelete(Integer deletedSort) {
+ if (deletedSort == null) {
+ return;
+ }
+
+ // 鏌ヨ鎵�鏈夋帓搴忓�煎ぇ浜庤鍒犻櫎妯℃澘鐨勮褰�
+ UpdateWrapper<AgentTemplateEntity> updateWrapper = new UpdateWrapper<>();
+ updateWrapper.gt("sort", deletedSort)
+ .setSql("sort = sort - 1");
+
+ // 鎵ц鎵归噺鏇存柊锛屽皢杩欎簺璁板綍鐨勬帓搴忓�煎噺1
+ this.update(updateWrapper);
+ }
+
+ @Override
+ public Integer getNextAvailableSort() {
+ // 鏌ヨ鎵�鏈夊凡瀛樺湪鐨勬帓搴忓�煎苟鎸夊崌搴忔帓搴�
+ List<Integer> sortValues = baseMapper.selectList(new QueryWrapper<AgentTemplateEntity>())
+ .stream()
+ .map(AgentTemplateEntity::getSort)
+ .filter(Objects::nonNull)
+ .sorted()
+ .collect(Collectors.toList());
+
+ // 濡傛灉娌℃湁鎺掑簭鍊硷紝杩斿洖1
+ if (sortValues.isEmpty()) {
+ return 1;
+ }
+
+ // 瀵绘壘鏈�灏忕殑鏈娇鐢ㄥ簭鍙�
+ int expectedSort = 1;
+ for (Integer sort : sortValues) {
+ if (sort > expectedSort) {
+ // 鎵惧埌绌虹己鐨勫簭鍙�
+ return expectedSort;
+ }
+ expectedSort = sort + 1;
+ }
+
+ // 濡傛灉娌℃湁绌虹己锛岃繑鍥炴渶澶у簭鍙�+1
+ return expectedSort;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentVoicePrintServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentVoicePrintServiceImpl.java
new file mode 100644
index 0000000..801db0c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentVoicePrintServiceImpl.java
@@ -0,0 +1,411 @@
+package xiaozhi.modules.agent.service.impl;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.core.io.ByteArrayResource;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.support.TransactionTemplate;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.spring.repository.CrudRepository;
+
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.utils.ConvertUtils;
+import xiaozhi.common.utils.JsonUtils;
+import xiaozhi.modules.agent.dao.AgentVoicePrintDao;
+import xiaozhi.modules.agent.dto.AgentVoicePrintSaveDTO;
+import xiaozhi.modules.agent.dto.AgentVoicePrintUpdateDTO;
+import xiaozhi.modules.agent.dto.IdentifyVoicePrintResponse;
+import xiaozhi.modules.agent.entity.AgentVoicePrintEntity;
+import xiaozhi.modules.agent.service.AgentChatAudioService;
+import xiaozhi.modules.agent.service.AgentChatHistoryService;
+import xiaozhi.modules.agent.service.AgentVoicePrintService;
+import xiaozhi.modules.agent.vo.AgentVoicePrintVO;
+import xiaozhi.modules.sys.service.SysParamsService;
+
+/**
+ * @author zjy
+ */
+@Service
+@Slf4j
+public class AgentVoicePrintServiceImpl extends CrudRepository<AgentVoicePrintDao, AgentVoicePrintEntity>
+ implements AgentVoicePrintService {
+ private final AgentChatAudioService agentChatAudioService;
+ private final RestTemplate restTemplate;
+ private final SysParamsService sysParamsService;
+ private final AgentChatHistoryService agentChatHistoryService;
+ // Springboot鎻愪緵鐨勭紪绋嬩簨鍔$被
+ private final TransactionTemplate transactionTemplate;
+ // 璇嗗埆搴�
+ private final Double RECOGNITION = 0.5;
+ private final Executor taskExecutor;
+
+ public AgentVoicePrintServiceImpl(AgentChatAudioService agentChatAudioService, RestTemplate restTemplate,
+ SysParamsService sysParamsService, AgentChatHistoryService agentChatHistoryService,
+ TransactionTemplate transactionTemplate, @Qualifier("taskExecutor") Executor taskExecutor) {
+ this.agentChatAudioService = agentChatAudioService;
+ this.restTemplate = restTemplate;
+ this.sysParamsService = sysParamsService;
+ this.agentChatHistoryService = agentChatHistoryService;
+ this.transactionTemplate = transactionTemplate;
+ this.taskExecutor = taskExecutor;
+ }
+
+ @Override
+ public boolean insert(AgentVoicePrintSaveDTO dto) {
+ // 鑾峰彇闊抽鏁版嵁
+ ByteArrayResource resource = getVoicePrintAudioWAV(dto.getAgentId(), dto.getAudioId());
+ // 璇嗗埆涓�涓嬫澹伴煶鏄惁娉ㄥ唽杩�
+ IdentifyVoicePrintResponse response = identifyVoicePrint(dto.getAgentId(), resource);
+ if (response != null && response.getScore() > RECOGNITION) {
+ // 鏍规嵁璇嗗埆鍑虹殑澹扮汗ID鏌ヨ瀵瑰簲鐨勭敤鎴蜂俊鎭�
+ AgentVoicePrintEntity existingVoicePrint = baseMapper.selectById(response.getSpeakerId());
+ String existingUserName = existingVoicePrint != null ? existingVoicePrint.getSourceName() : "鏈煡鐢ㄦ埛";
+ throw new RenException(ErrorCode.VOICEPRINT_ALREADY_REGISTERED, existingUserName);
+ }
+ AgentVoicePrintEntity entity = ConvertUtils.sourceToTarget(dto, AgentVoicePrintEntity.class);
+ // 寮�鍚簨鍔�
+ return Boolean.TRUE.equals(transactionTemplate.execute(status -> {
+ try {
+ // 淇濆瓨澹扮汗淇℃伅
+ int row = baseMapper.insert(entity);
+ // 鎻掑叆涓�鏉℃暟鎹紝褰卞搷鐨勬暟鎹笉绛変簬1璇存槑鍑虹幇浜嗭紝淇濆瓨闂鍥炴粴
+ if (row != 1) {
+ status.setRollbackOnly(); // 鏍囪浜嬪姟鍥炴粴
+ return false;
+ }
+ // 鍙戦�佹敞鍐屽0绾硅姹�
+ registerVoicePrint(entity.getId(), resource);
+ return true;
+ } catch (RenException e) {
+ status.setRollbackOnly(); // 鏍囪浜嬪姟鍥炴粴
+ throw e;
+ } catch (Exception e) {
+ status.setRollbackOnly(); // 鏍囪浜嬪姟鍥炴粴
+ log.error("淇濆瓨澹扮汗閿欒鍘熷洜锛歿}", e.getMessage());
+ throw new RenException(ErrorCode.VOICE_PRINT_SAVE_ERROR);
+ }
+ }));
+ }
+
+ @Override
+ public boolean delete(Long userId, String voicePrintId) {
+ // 寮�鍚簨鍔�
+ boolean b = Boolean.TRUE.equals(transactionTemplate.execute(status -> {
+ try {
+ // 鍒犻櫎澹扮汗,鎸夌収鎸囧畾褰撳墠鐧诲綍鐢ㄦ埛鍜屾櫤鑳戒綋
+ int row = baseMapper.delete(new LambdaQueryWrapper<AgentVoicePrintEntity>()
+ .eq(AgentVoicePrintEntity::getId, voicePrintId)
+ .eq(AgentVoicePrintEntity::getCreator, userId));
+ if (row != 1) {
+ status.setRollbackOnly(); // 鏍囪浜嬪姟鍥炴粴
+ return false;
+ }
+
+ return true;
+ } catch (Exception e) {
+ status.setRollbackOnly(); // 鏍囪浜嬪姟鍥炴粴
+ log.error("鍒犻櫎澹扮汗瀛樺湪閿欒鍘熷洜锛歿}", e.getMessage());
+ throw new RenException(ErrorCode.VOICEPRINT_DELETE_ERROR);
+ }
+ }));
+ // 鏁版嵁搴撳0绾规暟鎹垹闄ゆ垚鍔熸墠缁х画鎵ц鍒犻櫎澹扮汗鏈嶅姟鐨勬暟鎹�
+ if(b){
+ taskExecutor.execute(()-> {
+ try {
+ cancelVoicePrint(voicePrintId);
+ }catch (RuntimeException e) {
+ log.error("鍒犻櫎澹扮汗瀛樺湪杩愯鏃堕敊璇師鍥狅細{}锛宨d锛歿}", e.getMessage(),voicePrintId);
+ }
+ });
+ }
+ return b;
+ }
+
+ @Override
+ public List<AgentVoicePrintVO> list(Long userId, String agentId) {
+ // 鎸夌収鎸囧畾褰撳墠鐧诲綍鐢ㄦ埛鍜屾櫤鑳戒綋鏌ユ壘鏁版嵁
+ List<AgentVoicePrintEntity> list = baseMapper.selectList(new LambdaQueryWrapper<AgentVoicePrintEntity>()
+ .eq(AgentVoicePrintEntity::getAgentId, agentId)
+ .eq(AgentVoicePrintEntity::getCreator, userId));
+ return list.stream().map(entity -> {
+ // 閬嶅巻杞崲鎴怉gentVoicePrintVO绫诲瀷
+ return ConvertUtils.sourceToTarget(entity, AgentVoicePrintVO.class);
+ }).toList();
+
+ }
+
+ @Override
+ public boolean update(Long userId, AgentVoicePrintUpdateDTO dto) {
+ AgentVoicePrintEntity agentVoicePrintEntity = baseMapper
+ .selectOne(new LambdaQueryWrapper<AgentVoicePrintEntity>()
+ .eq(AgentVoicePrintEntity::getId, dto.getId())
+ .eq(AgentVoicePrintEntity::getCreator, userId));
+ if (agentVoicePrintEntity == null) {
+ return false;
+ }
+ // 鑾峰彇闊抽Id
+ String audioId = dto.getAudioId();
+ // 鑾峰彇鏅鸿兘浣搃d
+ String agentId = agentVoicePrintEntity.getAgentId();
+ ByteArrayResource resource;
+ // audioId涓嶇瓑浜庣┖锛屼笖audioId鍜屼箣鍓嶇殑淇濆瓨鐨勯煶棰慽d涓嶄竴鏍凤紝鍒欓渶瑕侀噸鏂拌幏鍙栭煶棰戞暟鎹敓鎴愬0绾�
+ if (!StringUtils.isEmpty(audioId) && !audioId.equals(agentVoicePrintEntity.getAudioId())) {
+ resource = getVoicePrintAudioWAV(agentId, audioId);
+
+ // 璇嗗埆涓�涓嬫澹伴煶鏄惁娉ㄥ唽杩�
+ IdentifyVoicePrintResponse response = identifyVoicePrint(agentId, resource);
+ // 杩斿洖鍒嗘暟楂樹簬RECOGNITION璇存槑杩欎釜澹扮汗宸茬粡鏈変簡
+ if (response != null && response.getScore() > RECOGNITION) {
+ // 鍒ゆ柇杩斿洖鐨刬d濡傛灉涓嶆槸瑕佷慨鏀圭殑澹扮汗id锛岃鏄庤繖涓0绾筰d锛岀幇鍦ㄨ娉ㄥ唽鐨勫0闊冲凡缁忓瓨鍦ㄤ笖涓嶆槸鍘熸潵鐨勫0绾癸紝涓嶅厑璁镐慨鏀�
+ if (!response.getSpeakerId().equals(dto.getId())) {
+ // 鏍规嵁璇嗗埆鍑虹殑澹扮汗ID鏌ヨ瀵瑰簲鐨勭敤鎴蜂俊鎭�
+ AgentVoicePrintEntity existingVoicePrint = baseMapper.selectById(response.getSpeakerId());
+ String existingUserName = existingVoicePrint != null ? existingVoicePrint.getSourceName() : "鏈煡鐢ㄦ埛";
+ throw new RenException(ErrorCode.VOICEPRINT_UPDATE_NOT_ALLOWED, existingUserName);
+ }
+ }
+ } else {
+ resource = null;
+ }
+ // 寮�鍚簨鍔�
+ return Boolean.TRUE.equals(transactionTemplate.execute(status -> {
+ try {
+ AgentVoicePrintEntity entity = ConvertUtils.sourceToTarget(dto, AgentVoicePrintEntity.class);
+ int row = baseMapper.updateById(entity);
+ if (row != 1) {
+ status.setRollbackOnly(); // 鏍囪浜嬪姟鍥炴粴
+ return false;
+ }
+ if (resource != null) {
+ String id = entity.getId();
+ // 鍏堟敞閿�涔嬪墠杩欎釜澹扮汗id涓婄殑澹扮汗鍚戦噺
+ cancelVoicePrint(id);
+ // 鍙戦�佹敞鍐屽0绾硅姹�
+ registerVoicePrint(id, resource);
+ }
+ return true;
+ } catch (RenException e) {
+ status.setRollbackOnly(); // 鏍囪浜嬪姟鍥炴粴
+ throw e;
+ } catch (Exception e) {
+ status.setRollbackOnly(); // 鏍囪浜嬪姟鍥炴粴
+ log.error("淇敼澹扮汗閿欒鍘熷洜锛歿}", e.getMessage());
+ throw new RenException(ErrorCode.VOICEPRINT_UPDATE_ADMIN_ERROR);
+ }
+ }));
+ }
+
+ /**
+ * 鑾峰彇鐢熺汗鎺ュ彛URI瀵硅薄
+ *
+ * @return URI瀵硅薄
+ */
+ private URI getVoicePrintURI() {
+ // 鑾峰彇澹扮汗鎺ュ彛鍦板潃
+ String voicePrint = sysParamsService.getValue(Constant.SERVER_VOICE_PRINT, true);
+ try {
+ return new URI(voicePrint);
+ } catch (URISyntaxException e) {
+ log.error("璺緞鏍煎紡涓嶆纭矾寰勶細{}锛孿n閿欒淇℃伅:{}", voicePrint, e.getMessage());
+ throw new RenException(ErrorCode.VOICEPRINT_API_URI_ERROR);
+ }
+ }
+
+ /**
+ * 鑾峰彇澹扮汗鍦板潃鍩虹璺緞
+ *
+ * @param uri 澹扮汗鍦板潃uri
+ * @return 鍩虹璺緞
+ */
+ private String getBaseUrl(URI uri) {
+ String protocol = uri.getScheme();
+ String host = uri.getHost();
+ int port = uri.getPort();
+ if (port == -1) {
+ return "%s://%s".formatted(protocol, host);
+ } else {
+ return "%s://%s:%s".formatted(protocol, host, port);
+ }
+ }
+
+ /**
+ * 鑾峰彇楠岃瘉Authorization
+ *
+ * @param uri 澹扮汗鍦板潃uri
+ * @return Authorization鍊�
+ */
+ private String getAuthorization(URI uri) {
+ // 鑾峰彇鍙傛暟
+ String query = uri.getQuery();
+ // 鑾峰彇aes鍔犲瘑瀵嗛挜
+ String str = "key=";
+ return "Bearer " + query.substring(query.indexOf(str) + str.length());
+ }
+
+ /**
+ * 鑾峰彇澹扮汗闊抽璧勬簮鏁版嵁
+ *
+ * @param audioId 闊抽Id
+ * @return 澹扮汗闊抽璧勬簮鏁版嵁
+ */
+ private ByteArrayResource getVoicePrintAudioWAV(String agentId, String audioId) {
+ // 鍒ゆ柇杩欎釜闊抽鏄惁灞炰簬褰撳墠鏅鸿兘浣�
+ boolean b = agentChatHistoryService.isAudioOwnedByAgent(audioId, agentId);
+ if (!b) {
+ throw new RenException(ErrorCode.VOICEPRINT_AUDIO_NOT_BELONG_AGENT);
+ }
+ // 鑾峰彇鍒伴煶棰戞暟鎹�
+ byte[] audio = agentChatAudioService.getAudio(audioId);
+ // 濡傛灉闊抽鏁版嵁涓虹┖鐨勭洿鎺ユ姤閿欎笉杩涜涓嬪幓
+ if (audio == null || audio.length == 0) {
+ throw new RenException(ErrorCode.VOICEPRINT_AUDIO_EMPTY);
+ }
+ // 灏嗗瓧鑺傛暟缁勫寘瑁呬负璧勬簮锛岃繑鍥�
+ return new ByteArrayResource(audio) {
+ @Override
+ public String getFilename() {
+ return "VoicePrint.WAV"; // 璁剧疆鏂囦欢鍚�
+ }
+ };
+ }
+
+ /**
+ * 鍙戦�佹敞鍐屽0绾筯ttp璇锋眰
+ *
+ * @param id 澹扮汗id
+ * @param resource 澹扮汗闊抽璧勬簮
+ */
+ private void registerVoicePrint(String id, ByteArrayResource resource) {
+ // 澶勭悊澹扮汗鎺ュ彛鍦板潃锛岃幏鍙栧墠缂�
+ URI uri = getVoicePrintURI();
+ String baseUrl = getBaseUrl(uri);
+ String requestUrl = baseUrl + "/voiceprint/register";
+ // 鍒涘缓璇锋眰浣�
+ MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
+ body.add("speaker_id", id);
+ body.add("file", resource);
+
+ // 鍒涘缓璇锋眰澶�
+ HttpHeaders headers = new HttpHeaders();
+ headers.set("Authorization", getAuthorization(uri));
+ headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+ // 鍒涘缓璇锋眰浣�
+ HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
+ // 鍙戦�� POST 璇锋眰
+ ResponseEntity<String> response = restTemplate.postForEntity(requestUrl, requestEntity, String.class);
+
+ if (response.getStatusCode() != HttpStatus.OK) {
+ log.error("澹扮汗娉ㄥ唽澶辫触,璇锋眰璺緞锛歿}", requestUrl);
+ throw new RenException(ErrorCode.VOICEPRINT_REGISTER_REQUEST_ERROR);
+ }
+ // 妫�鏌ュ搷搴斿唴瀹�
+ String responseBody = response.getBody();
+ if (responseBody == null || !responseBody.contains("true")) {
+ log.error("澹扮汗娉ㄥ唽澶辫触,璇锋眰澶勭悊澶辫触鍐呭锛歿}", responseBody == null ? "绌哄唴瀹�" : responseBody);
+ throw new RenException(ErrorCode.VOICEPRINT_REGISTER_PROCESS_ERROR);
+ }
+ }
+
+ /**
+ * 鍙戦�佹敞閿�澹扮汗鐨勮姹�
+ *
+ * @param voicePrintId 澹扮汗id
+ */
+ private void cancelVoicePrint(String voicePrintId) {
+ URI uri = getVoicePrintURI();
+ String baseUrl = getBaseUrl(uri);
+ String requestUrl = baseUrl + "/voiceprint/" + voicePrintId;
+ // 鍒涘缓璇锋眰澶�
+ HttpHeaders headers = new HttpHeaders();
+ headers.set("Authorization", getAuthorization(uri));
+ // 鍒涘缓璇锋眰浣�
+ HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(headers);
+
+ // 鍙戦�� POST 璇锋眰
+ ResponseEntity<String> response = restTemplate.exchange(requestUrl, HttpMethod.DELETE, requestEntity,
+ String.class);
+ if (response.getStatusCode() != HttpStatus.OK) {
+ log.error("澹扮汗娉ㄩ攢澶辫触,璇锋眰璺緞锛歿}", requestUrl);
+ throw new RenException(ErrorCode.VOICEPRINT_UNREGISTER_REQUEST_ERROR);
+ }
+ // 妫�鏌ュ搷搴斿唴瀹�
+ String responseBody = response.getBody();
+ if (responseBody == null || !responseBody.contains("true")) {
+ log.error("澹扮汗娉ㄩ攢澶辫触,璇锋眰澶勭悊澶辫触鍐呭锛歿}", responseBody == null ? "绌哄唴瀹�" : responseBody);
+ throw new RenException(ErrorCode.VOICEPRINT_UNREGISTER_PROCESS_ERROR);
+ }
+ }
+
+ /**
+ * 鍙戦�佽瘑鍒0绾筯ttp璇锋眰
+ *
+ * @param agentId 鏅鸿兘浣搃d
+ * @param resource 澹扮汗闊抽璧勬簮
+ * @return 杩斿洖璇嗗埆鏁版嵁
+ */
+ private IdentifyVoicePrintResponse identifyVoicePrint(String agentId, ByteArrayResource resource) {
+
+ // 鑾峰彇璇ユ櫤鑳戒綋鎵�鏈夋敞鍐岀殑澹扮汗
+ List<AgentVoicePrintEntity> agentVoicePrintList = baseMapper
+ .selectList(new LambdaQueryWrapper<AgentVoicePrintEntity>()
+ .select(AgentVoicePrintEntity::getId)
+ .eq(AgentVoicePrintEntity::getAgentId, agentId));
+
+ // 澹扮汗鏁伴噺涓�0锛岃鏄庤繕娌℃敞鍐岃繃澹扮汗涓嶉渶瑕佸彂鐢熻瘑鍒姹�
+ if (agentVoicePrintList.isEmpty()) {
+ return null;
+ }
+ // 澶勭悊澹扮汗鎺ュ彛鍦板潃锛岃幏鍙栧墠缂�
+ URI uri = getVoicePrintURI();
+ String baseUrl = getBaseUrl(uri);
+ String requestUrl = baseUrl + "/voiceprint/identify";
+ // 鍒涘缓璇锋眰浣�
+ MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
+
+ // 鍒涘缓speaker_id鍙傛暟
+ String speakerIds = agentVoicePrintList.stream()
+ .map(AgentVoicePrintEntity::getId)
+ .collect(Collectors.joining(","));
+ body.add("speaker_ids", speakerIds);
+ body.add("file", resource);
+
+ // 鍒涘缓璇锋眰澶�
+ HttpHeaders headers = new HttpHeaders();
+ headers.set("Authorization", getAuthorization(uri));
+ headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+ // 鍒涘缓璇锋眰浣�
+ HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
+ // 鍙戦�� POST 璇锋眰
+ ResponseEntity<String> response = restTemplate.postForEntity(requestUrl, requestEntity, String.class);
+
+ if (response.getStatusCode() != HttpStatus.OK) {
+ log.error("澹扮汗璇嗗埆璇锋眰澶辫触,璇锋眰璺緞锛歿}", requestUrl);
+ throw new RenException(ErrorCode.VOICEPRINT_IDENTIFY_REQUEST_ERROR);
+ }
+ // 妫�鏌ュ搷搴斿唴瀹�
+ String responseBody = response.getBody();
+ if (responseBody != null) {
+ return JsonUtils.parseObject(responseBody, IdentifyVoicePrintResponse.class);
+ }
+ return null;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/typehandler/ContextProviderListTypeHandler.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/typehandler/ContextProviderListTypeHandler.java
new file mode 100644
index 0000000..4f70123
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/typehandler/ContextProviderListTypeHandler.java
@@ -0,0 +1,59 @@
+package xiaozhi.modules.agent.typehandler;
+
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.ibatis.type.BaseTypeHandler;
+import org.apache.ibatis.type.JdbcType;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+
+import xiaozhi.common.utils.JsonUtils;
+import xiaozhi.modules.agent.dto.ContextProviderDTO;
+
+/**
+ * JSON type handler for context providers.
+ *
+ * <p>Do not extend MyBatis-Plus {@code AbstractJsonTypeHandler}: its constructor and JSON handler
+ * contract changed in MyBatis-Plus 3.5.6. {@link BaseTypeHandler} is part of MyBatis itself and
+ * keeps this handler compatible with both 3.5.5 and newer MyBatis-Plus releases.</p>
+ */
+public class ContextProviderListTypeHandler extends BaseTypeHandler<List<ContextProviderDTO>> {
+ private static final TypeReference<List<ContextProviderDTO>> CONTEXT_PROVIDER_LIST_TYPE = new TypeReference<>() {
+ };
+
+ @Override
+ public void setNonNullParameter(PreparedStatement ps, int i, List<ContextProviderDTO> parameter,
+ JdbcType jdbcType) throws SQLException {
+ ps.setString(i, JsonUtils.toJsonString(parameter));
+ }
+
+ @Override
+ public List<ContextProviderDTO> getNullableResult(ResultSet rs, String columnName) throws SQLException {
+ return parseNullable(rs.getString(columnName));
+ }
+
+ @Override
+ public List<ContextProviderDTO> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
+ return parseNullable(rs.getString(columnIndex));
+ }
+
+ @Override
+ public List<ContextProviderDTO> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
+ return parseNullable(cs.getString(columnIndex));
+ }
+
+ private List<ContextProviderDTO> parseNullable(String json) {
+ if (StringUtils.isBlank(json)) {
+ return null;
+ }
+ List<ContextProviderDTO> providers = JsonUtils.parseObject(json, CONTEXT_PROVIDER_LIST_TYPE);
+ return providers == null ? Collections.emptyList() : providers;
+ }
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentChatHistoryUserVO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentChatHistoryUserVO.java
new file mode 100644
index 0000000..cec0427
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentChatHistoryUserVO.java
@@ -0,0 +1,16 @@
+package xiaozhi.modules.agent.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 鏅鸿兘浣撶敤鎴蜂釜浜鸿亰澶╂暟鎹殑VO
+ */
+@Data
+public class AgentChatHistoryUserVO {
+ @Schema(description = "鑱婂ぉ鍐呭")
+ private String content;
+
+ @Schema(description = "闊抽ID")
+ private String audioId;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentInfoVO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentInfoVO.java
new file mode 100644
index 0000000..ab1ca3f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentInfoVO.java
@@ -0,0 +1,31 @@
+package xiaozhi.modules.agent.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import xiaozhi.modules.agent.dto.ContextProviderDTO;
+import xiaozhi.modules.agent.entity.AgentEntity;
+import xiaozhi.modules.agent.entity.AgentPluginMapping;
+
+import java.util.List;
+
+/**
+ * Agent淇℃伅杩斿洖浣揤O
+ * 杩欓噷鐩存帴extend浜咥gent瀹炰綋绫籄gentEntity锛屽悗缁渶瑕佽鑼冭繑鍥炲瓧娈靛彲浠opy瀛楁鍑烘潵
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class AgentInfoVO extends AgentEntity
+{
+ @Schema(description = "鎻掍欢鍒楄〃Id")
+ private List<AgentPluginMapping> functions;
+
+ @Schema(description = "涓婁笅鏂囨簮閰嶇疆")
+ private List<ContextProviderDTO> contextProviders;
+
+ @Schema(description = "鏇挎崲璇嶆枃浠禝D鍒楄〃")
+ private List<String> correctWordFileIds;
+
+ @Schema(description = "褰撳墠閰嶇疆鐗堟湰鍙�")
+ private Integer currentVersionNo;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentSnapshotVO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentSnapshotVO.java
new file mode 100644
index 0000000..d5a44e1
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentSnapshotVO.java
@@ -0,0 +1,34 @@
+package xiaozhi.modules.agent.vo;
+
+import java.util.Date;
+import java.util.List;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import xiaozhi.modules.agent.dto.AgentSnapshotDataDTO;
+
+@Data
+@Schema(description = "鏅鸿兘浣撻厤缃揩鐓�")
+public class AgentSnapshotVO {
+ private String id;
+ private String agentId;
+ @Schema(description = "鎵�灞炵敤鎴稩D锛岃〃绀鸿蹇収褰掑睘鐨勬櫤鑳戒綋鎵�鏈夎��")
+ private Long userId;
+ private Integer versionNo;
+ private List<String> changedFields;
+ private List<String> fieldOrder;
+ private String source;
+ @Schema(description = "鎭㈠鏉ユ簮蹇収ID锛屼粎鎭㈠缁撴灉鐗堟湰鏈夊��")
+ private String restoreFromSnapshotId;
+ @Schema(description = "鎭㈠鏉ユ簮鐗堟湰鍙凤紝浠呮仮澶嶇粨鏋滅増鏈湁鍊�")
+ private Integer restoreFromVersionNo;
+ @Schema(description = "鍒涘缓鑰咃紝琛ㄧず瑙﹀彂鏈蹇収鍐欏叆鐨勬搷浣滀汉")
+ private Long creator;
+ private Date createdAt;
+ private AgentSnapshotDataDTO snapshotData;
+ private AgentSnapshotDataDTO afterSnapshotData;
+ @Schema(description = "鎭㈠棰勮瀵瑰簲鐨勮劚鏁忓綋鍓嶉厤缃紝浠呰鎯呮帴鍙f湁鍊�")
+ private AgentSnapshotDataDTO currentSnapshotData;
+ @Schema(description = "鎭㈠棰勮瀵瑰簲鐨勫綋鍓嶉厤缃姸鎬佹寚绾癸紝浠呰鎯呮帴鍙f湁鍊�")
+ private String currentStateToken;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentTemplateVO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentTemplateVO.java
new file mode 100644
index 0000000..a5302e5
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentTemplateVO.java
@@ -0,0 +1,15 @@
+package xiaozhi.modules.agent.vo;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import xiaozhi.modules.agent.entity.AgentTemplateEntity;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class AgentTemplateVO extends AgentTemplateEntity {
+ // 瑙掕壊闊宠壊
+ private String ttsModelName;
+
+ // 瑙掕壊妯″瀷
+ private String llmModelName;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentVoicePrintVO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentVoicePrintVO.java
new file mode 100644
index 0000000..b647e8b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/agent/vo/AgentVoicePrintVO.java
@@ -0,0 +1,33 @@
+package xiaozhi.modules.agent.vo;
+
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 灞曠ず鏅鸿兘浣撳0绾瑰垪琛╒O
+ */
+@Data
+public class AgentVoicePrintVO {
+
+ /**
+ * 涓婚敭id
+ */
+ private String id;
+ /**
+ * 闊抽鏂囦欢id
+ */
+ private String audioId;
+ /**
+ * 澹扮汗鏉ユ簮鐨勪汉濮撳悕
+ */
+ private String sourceName;
+ /**
+ * 鎻忚堪澹扮汗鏉ユ簮鐨勪汉
+ */
+ private String introduce;
+ /**
+ * 鍒涘缓鏃堕棿
+ */
+ private Date createDate;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/controller/ConfigController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/controller/ConfigController.java
new file mode 100644
index 0000000..1bbbcaa
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/controller/ConfigController.java
@@ -0,0 +1,55 @@
+package xiaozhi.modules.config.controller;
+
+import java.util.List;
+
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.AllArgsConstructor;
+import xiaozhi.common.utils.Result;
+import xiaozhi.common.validator.ValidatorUtils;
+import xiaozhi.modules.config.dto.AgentModelsDTO;
+import xiaozhi.modules.config.dto.CorrectWordsDTO;
+import xiaozhi.modules.config.service.ConfigService;
+
+/**
+ * xiaozhi-server 閰嶇疆鑾峰彇
+ *
+ * @since 1.0.0
+ */
+@RestController
+@RequestMapping("config")
+@Tag(name = "鍙傛暟绠$悊")
+@AllArgsConstructor
+public class ConfigController {
+ private final ConfigService configService;
+
+ @PostMapping("server-base")
+ @Operation(summary = "鏈嶅姟绔幏鍙栭厤缃帴鍙�")
+ public Result<Object> getConfig() {
+ Object config = configService.getConfig(true);
+ return new Result<Object>().ok(config);
+ }
+
+ @PostMapping("agent-models")
+ @Operation(summary = "鑾峰彇鏅鸿兘浣撴ā鍨�")
+ public Result<Object> getAgentModels(@Valid @RequestBody AgentModelsDTO dto) {
+ // 鏁堥獙鏁版嵁
+ ValidatorUtils.validateEntity(dto);
+ Object models = configService.getAgentModels(dto.getMacAddress(), dto.getSelectedModule());
+ return new Result<Object>().ok(models);
+ }
+
+ @PostMapping("correct-words")
+ @Operation(summary = "鑾峰彇鏅鸿兘浣撴浛鎹㈣瘝")
+ public Result<Object> getCorrectWords(@Valid @RequestBody CorrectWordsDTO dto) {
+ ValidatorUtils.validateEntity(dto);
+ List<String> list = configService.getCorrectWords(dto.getMacAddress());
+ return new Result<Object>().ok(list);
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/dto/AgentModelsDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/dto/AgentModelsDTO.java
new file mode 100644
index 0000000..47c08a8
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/dto/AgentModelsDTO.java
@@ -0,0 +1,25 @@
+package xiaozhi.modules.config.dto;
+
+import java.util.Map;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Data
+@Schema(description = "鑾峰彇鏅鸿兘浣撴ā鍨嬮厤缃瓺TO")
+public class AgentModelsDTO {
+
+ @NotBlank(message = "璁惧MAC鍦板潃涓嶈兘涓虹┖")
+ @Schema(description = "璁惧MAC鍦板潃")
+ private String macAddress;
+
+ @NotBlank(message = "瀹㈡埛绔疘D涓嶈兘涓虹┖")
+ @Schema(description = "瀹㈡埛绔疘D")
+ private String clientId;
+
+ @NotNull(message = "瀹㈡埛绔凡瀹炰緥鍖栫殑妯″瀷涓嶈兘涓虹┖")
+ @Schema(description = "瀹㈡埛绔凡瀹炰緥鍖栫殑妯″瀷")
+ private Map<String, String> selectedModule;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/dto/CorrectWordsDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/dto/CorrectWordsDTO.java
new file mode 100644
index 0000000..0c689d5
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/dto/CorrectWordsDTO.java
@@ -0,0 +1,14 @@
+package xiaozhi.modules.config.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+@Data
+@Schema(description = "鑾峰彇鏅鸿兘浣撴浛鎹㈣瘝DTO")
+public class CorrectWordsDTO {
+
+ @NotBlank(message = "璁惧MAC鍦板潃涓嶈兘涓虹┖")
+ @Schema(description = "璁惧MAC鍦板潃")
+ private String macAddress;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/init/SystemInitConfig.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/init/SystemInitConfig.java
new file mode 100644
index 0000000..5c7fc7f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/init/SystemInitConfig.java
@@ -0,0 +1,48 @@
+package xiaozhi.modules.config.init;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.DependsOn;
+
+import jakarta.annotation.PostConstruct;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.redis.RedisKeys;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.modules.config.service.ConfigService;
+import xiaozhi.modules.device.service.DeviceAddressBookService;
+import xiaozhi.modules.sys.service.SysParamsService;
+
+@Configuration
+@DependsOn("liquibase")
+public class SystemInitConfig {
+
+ @Autowired
+ private SysParamsService sysParamsService;
+
+ @Autowired
+ private ConfigService configService;
+
+ @Autowired
+ private RedisUtils redisUtils;
+
+ @Autowired
+ private DeviceAddressBookService deviceAddressBookService;
+
+ @PostConstruct
+ public void init() {
+ // 妫�鏌ョ増鏈彿
+ String redisVersion = (String) redisUtils.get(RedisKeys.getVersionKey());
+ if (!Constant.VERSION.equals(redisVersion)) {
+ // 濡傛灉鐗堟湰涓嶄竴鑷达紝娓呯┖Redis
+ redisUtils.emptyAll();
+ // 瀛樺偍鏂扮増鏈彿
+ redisUtils.set(RedisKeys.getVersionKey(), Constant.VERSION);
+ }
+
+ sysParamsService.initServerSecret();
+ configService.getConfig(false);
+
+ // 鍒濆鍖栬澶囬�氳褰曠紦瀛�
+ deviceAddressBookService.refreshCache();
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/service/ConfigService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/service/ConfigService.java
new file mode 100644
index 0000000..81f1985
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/service/ConfigService.java
@@ -0,0 +1,31 @@
+package xiaozhi.modules.config.service;
+
+import java.util.List;
+import java.util.Map;
+
+public interface ConfigService {
+ /**
+ * 鑾峰彇鏈嶅姟鍣ㄩ厤缃�
+ *
+ * @param isCache 鏄惁缂撳瓨
+ * @return 閰嶇疆淇℃伅
+ */
+ Map<String, Object> getConfig(Boolean isCache);
+
+ /**
+ * 鑾峰彇鏅鸿兘浣撴ā鍨嬮厤缃�
+ *
+ * @param macAddress MAC鍦板潃
+ * @param selectedModule 瀹㈡埛绔凡瀹炰緥鍖栫殑妯″瀷
+ * @return 妯″瀷閰嶇疆淇℃伅
+ */
+ Map<String, Object> getAgentModels(String macAddress, Map<String, String> selectedModule);
+
+ /**
+ * 鑾峰彇鏅鸿兘浣撴浛鎹㈣瘝
+ *
+ * @param macAddress 璁惧MAC鍦板潃
+ * @return 鏇挎崲璇嶅垪琛紝鏍煎紡濡� ["妯℃澘1|妯℃澘01", "妯℃澘2|妯℃澘02"]
+ */
+ List<String> getCorrectWords(String macAddress);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java
new file mode 100644
index 0000000..45e465f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java
@@ -0,0 +1,554 @@
+package xiaozhi.modules.config.service.impl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+
+import lombok.AllArgsConstructor;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.redis.RedisKeys;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.common.utils.ConvertUtils;
+import xiaozhi.common.utils.JsonUtils;
+import xiaozhi.modules.agent.dao.AgentVoicePrintDao;
+import xiaozhi.modules.agent.entity.AgentContextProviderEntity;
+import xiaozhi.modules.agent.entity.AgentEntity;
+import xiaozhi.modules.agent.entity.AgentPluginMapping;
+import xiaozhi.modules.agent.entity.AgentTemplateEntity;
+import xiaozhi.modules.agent.entity.AgentVoicePrintEntity;
+import xiaozhi.modules.agent.service.AgentContextProviderService;
+import xiaozhi.modules.agent.service.AgentMcpAccessPointService;
+import xiaozhi.modules.agent.service.AgentPluginMappingService;
+import xiaozhi.modules.agent.service.AgentService;
+import xiaozhi.modules.agent.service.AgentTemplateService;
+import xiaozhi.modules.correctword.service.CorrectWordFileService;
+import xiaozhi.modules.agent.vo.AgentVoicePrintVO;
+import xiaozhi.modules.correctword.vo.CorrectWordSimpleVO;
+import xiaozhi.modules.config.service.ConfigService;
+import xiaozhi.modules.device.entity.DeviceEntity;
+import xiaozhi.modules.device.service.DeviceService;
+import xiaozhi.modules.model.entity.ModelConfigEntity;
+import xiaozhi.modules.model.service.ModelConfigService;
+import xiaozhi.modules.sys.dto.SysParamsDTO;
+import xiaozhi.modules.sys.service.SysParamsService;
+import xiaozhi.modules.timbre.service.TimbreService;
+import xiaozhi.modules.timbre.vo.TimbreDetailsVO;
+import xiaozhi.modules.voiceclone.entity.VoiceCloneEntity;
+import xiaozhi.modules.voiceclone.service.VoiceCloneService;
+
+@Service
+@AllArgsConstructor
+public class ConfigServiceImpl implements ConfigService {
+ private final SysParamsService sysParamsService;
+ private final DeviceService deviceService;
+ private final ModelConfigService modelConfigService;
+ private final AgentService agentService;
+ private final AgentTemplateService agentTemplateService;
+ private final RedisUtils redisUtils;
+ private final TimbreService timbreService;
+ private final AgentPluginMappingService agentPluginMappingService;
+ private final AgentMcpAccessPointService agentMcpAccessPointService;
+ private final AgentContextProviderService agentContextProviderService;
+ private final VoiceCloneService cloneVoiceService;
+ private final AgentVoicePrintDao agentVoicePrintDao;
+ private final CorrectWordFileService correctWordFileService;
+
+ @Override
+ public Map<String, Object> getConfig(Boolean isCache) {
+ if (isCache) {
+ // 鍏堜粠Redis鑾峰彇閰嶇疆
+ Object cachedConfig = redisUtils.get(RedisKeys.getServerConfigKey());
+ if (cachedConfig != null) {
+ return JsonUtils.toStringObjectMap(cachedConfig);
+ }
+ }
+
+ // 鏋勫缓閰嶇疆淇℃伅
+ Map<String, Object> result = new HashMap<>();
+ buildConfig(result);
+
+ // 鏌ヨ榛樿鏅鸿兘浣�
+ AgentTemplateEntity agent = agentTemplateService.getDefaultTemplate();
+ if (agent == null) {
+ throw new RenException(ErrorCode.AGENT_TEMPLATE_NOT_FOUND);
+ }
+
+ // 鏋勫缓妯″潡閰嶇疆
+ buildModuleConfig(
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ agent.getVadModelId(),
+ agent.getAsrModelId(),
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ result,
+ isCache);
+
+ // 灏嗛厤缃瓨鍏edis
+ redisUtils.set(RedisKeys.getServerConfigKey(), result);
+
+ return result;
+ }
+
+ @Override
+ public Map<String, Object> getAgentModels(String macAddress, Map<String, String> selectedModule) {
+ // 妫�鏌ユ槸鍚︿负绠$悊鎺у埗鍙拌姹�
+ String redisKey = RedisKeys.getTmpRegisterMacKey(macAddress);
+ Object isAdminRequest = redisUtils.get(redisKey);
+
+ if (isAdminRequest != null && "true".equals(isAdminRequest)) {
+ // 绠$悊鎺у埗鍙拌姹傦紝杩斿洖getConfig鐨勭粨鏋�
+ redisUtils.delete(redisKey); // 浣跨敤鍚庢竻鐞�
+ return getConfig(true);
+ }
+ // 鏍规嵁MAC鍦板潃鏌ユ壘璁惧
+ DeviceEntity device = deviceService.getDeviceByMacAddress(macAddress);
+ if (device == null) {
+ // 濡傛灉璁惧锛屽幓redis閲岀湅鐪嬫湁娌℃湁闇�瑕佽繛鎺ョ殑璁惧
+ String cachedCode = deviceService.geCodeByDeviceId(macAddress);
+ if (StringUtils.isNotBlank(cachedCode)) {
+ throw new RenException(ErrorCode.OTA_DEVICE_NEED_BIND, cachedCode);
+ }
+ throw new RenException(ErrorCode.OTA_DEVICE_NOT_FOUND);
+ }
+
+ // 鑾峰彇鏅鸿兘浣撲俊鎭�
+ AgentEntity agent = agentService.getAgentById(device.getAgentId());
+ if (agent == null) {
+ throw new RenException(ErrorCode.AGENT_NOT_FOUND);
+ }
+ // 鑾峰彇闊宠壊淇℃伅
+ String voice = null;
+ String referenceAudio = null;
+ String referenceText = null;
+ String language = null;
+ TimbreDetailsVO timbre = timbreService.get(agent.getTtsVoiceId());
+ if (timbre != null) {
+ voice = timbre.getTtsVoice();
+ referenceAudio = timbre.getReferenceAudio();
+ referenceText = timbre.getReferenceText();
+ // 浼樺厛浣跨敤鐢ㄦ埛閫夋嫨鐨勮瑷�锛屽鏋滄病鏈夊垯浣跨敤闊宠壊鏀寔鐨勭涓�涓瑷�
+ if (StringUtils.isNotBlank(agent.getTtsLanguage())) {
+ language = agent.getTtsLanguage();
+ } else if (StringUtils.isNotBlank(timbre.getLanguages())) {
+ language = timbre.getLanguages().split("銆�")[0].trim();
+ }
+ } else {
+ VoiceCloneEntity voice_print = cloneVoiceService.selectById(agent.getTtsVoiceId());
+ if (voice_print != null) {
+ voice = voice_print.getVoiceId();
+ // 浼樺厛浣跨敤鐢ㄦ埛閫夋嫨鐨勮瑷�锛屽鏋滄病鏈夊垯浣跨敤榛樿鍊�
+ language = StringUtils.isNotBlank(agent.getTtsLanguage()) ? agent.getTtsLanguage() : "鏅�氳瘽";
+ }
+ }
+ // 鏋勫缓杩斿洖鏁版嵁
+ Map<String, Object> result = new HashMap<>();
+ // 鑾峰彇鍗曞彴璁惧姣忓ぉ鏈�澶氳緭鍑哄瓧鏁�
+ String deviceMaxOutputSize = sysParamsService.getValue("device_max_output_size", true);
+ result.put("device_max_output_size", deviceMaxOutputSize);
+
+ // 鑾峰彇鑱婂ぉ璁板綍閰嶇疆
+ Integer chatHistoryConf = agent.getChatHistoryConf();
+ if (agent.getMemModelId() != null && agent.getMemModelId().equals(Constant.MEMORY_NO_MEM)) {
+ chatHistoryConf = Constant.ChatHistoryConfEnum.IGNORE.getCode();
+ } else if (agent.getMemModelId() != null
+ && !agent.getMemModelId().equals(Constant.MEMORY_NO_MEM)
+ && agent.getChatHistoryConf() == null) {
+ chatHistoryConf = Constant.ChatHistoryConfEnum.RECORD_TEXT_AUDIO.getCode();
+ }
+ result.put("chat_history_conf", chatHistoryConf);
+ // 濡傛灉瀹㈡埛绔凡瀹炰緥鍖栨ā鍨嬶紝鍒欎笉杩斿洖
+ String alreadySelectedVadModelId = selectedModule.get("VAD");
+ if (alreadySelectedVadModelId != null && alreadySelectedVadModelId.equals(agent.getVadModelId())) {
+ agent.setVadModelId(null);
+ }
+ String alreadySelectedAsrModelId = selectedModule.get("ASR");
+ if (alreadySelectedAsrModelId != null && alreadySelectedAsrModelId.equals(agent.getAsrModelId())) {
+ agent.setAsrModelId(null);
+ }
+
+ // 娣诲姞鍑芥暟璋冪敤鍙傛暟淇℃伅
+ if (!Objects.equals(agent.getIntentModelId(), "Intent_nointent")) {
+ String agentId = agent.getId();
+ List<AgentPluginMapping> pluginMappings = agentPluginMappingService.agentPluginParamsByAgentId(agentId);
+ if (pluginMappings != null && !pluginMappings.isEmpty()) {
+ Map<String, Object> pluginParams = new HashMap<>();
+ for (AgentPluginMapping pluginMapping : pluginMappings) {
+ pluginParams.put(pluginMapping.getProviderCode(), pluginMapping.getParamInfo());
+ }
+ result.put("plugins", pluginParams);
+ }
+ }
+ // 鑾峰彇mcp鎺ュ叆鐐瑰湴鍧�
+ String mcpEndpoint = agentMcpAccessPointService.getAgentMcpAccessAddress(agent.getId());
+ if (StringUtils.isNotBlank(mcpEndpoint) && mcpEndpoint.startsWith("ws")) {
+ mcpEndpoint = mcpEndpoint.replace("/mcp/", "/call/");
+ result.put("mcp_endpoint", mcpEndpoint);
+ }
+
+ // 鑾峰彇涓婁笅鏂囨簮閰嶇疆
+ AgentContextProviderEntity contextProviderEntity = agentContextProviderService.getByAgentId(agent.getId());
+ if (contextProviderEntity != null && contextProviderEntity.getContextProviders() != null
+ && !contextProviderEntity.getContextProviders().isEmpty()) {
+ result.put("context_providers", contextProviderEntity.getContextProviders());
+ }
+
+ // 鑾峰彇澹扮汗淇℃伅
+ buildVoiceprintConfig(agent.getId(), result);
+
+ // 鏋勫缓妯″潡閰嶇疆
+ buildModuleConfig(
+ agent.getAgentName(),
+ agent.getSystemPrompt(),
+ agent.getSummaryMemory(),
+ voice,
+ referenceAudio,
+ referenceText,
+ language,
+ agent.getTtsVolume(),
+ agent.getTtsRate(),
+ agent.getTtsPitch(),
+ agent.getVadModelId(),
+ agent.getAsrModelId(),
+ agent.getLlmModelId(),
+ agent.getVllmModelId(),
+ agent.getSlmModelId(),
+ agent.getTtsModelId(),
+ agent.getMemModelId(),
+ agent.getIntentModelId(),
+ null,
+ result,
+ true);
+
+ return result;
+ }
+
+ @Override
+ public List<String> getCorrectWords(String macAddress) {
+ DeviceEntity device = deviceService.getDeviceByMacAddress(macAddress);
+ if (device == null) {
+ return Collections.emptyList();
+ }
+ List<CorrectWordSimpleVO> items = correctWordFileService.getAllItemsByAgentId(device.getAgentId());
+ return items.stream()
+ .map(item -> item.getSourceWord() + "|" + item.getTargetWord())
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * 鏋勫缓閰嶇疆淇℃伅
+ *
+ * @param config 绯荤粺鍙傛暟鍒楄〃
+ * @return 閰嶇疆淇℃伅
+ */
+ private Object buildConfig(Map<String, Object> config) {
+
+ // 鏌ヨ鎵�鏈夌郴缁熷弬鏁�
+ List<SysParamsDTO> paramsList = sysParamsService.list(new HashMap<>());
+
+ for (SysParamsDTO param : paramsList) {
+ String[] keys = param.getParamCode().split("\\.");
+ Map<String, Object> current = config;
+
+ // 閬嶅巻闄ゆ渶鍚庝竴涓猭ey涔嬪鐨勬墍鏈塳ey
+ for (int i = 0; i < keys.length - 1; i++) {
+ String key = keys[i];
+ Object nestedConfig = current.computeIfAbsent(key, ignored -> new HashMap<String, Object>());
+ Map<String, Object> nestedMap = JsonUtils.toStringObjectMap(nestedConfig);
+ current.put(key, nestedMap);
+ current = nestedMap;
+ }
+
+ // 澶勭悊鏈�鍚庝竴涓猭ey
+ String lastKey = keys[keys.length - 1];
+ String value = param.getParamValue();
+
+ // 鏍规嵁valueType杞崲鍊�
+ switch (param.getValueType().toLowerCase()) {
+ case "number":
+ try {
+ double doubleValue = Double.parseDouble(value);
+ // 濡傛灉鏁板�兼槸鏁存暟褰㈠紡锛屽垯杞崲涓篒nteger
+ if (doubleValue == (int) doubleValue) {
+ current.put(lastKey, (int) doubleValue);
+ } else {
+ current.put(lastKey, doubleValue);
+ }
+ } catch (NumberFormatException e) {
+ current.put(lastKey, value);
+ }
+ break;
+ case "boolean":
+ current.put(lastKey, Boolean.parseBoolean(value));
+ break;
+ case "array":
+ // 灏嗗垎鍙峰垎闅旂殑瀛楃涓茶浆鎹负鏁板瓧鏁扮粍
+ List<String> list = new ArrayList<>();
+ for (String num : value.split(";")) {
+ if (StringUtils.isNotBlank(num)) {
+ list.add(num.trim());
+ }
+ }
+ current.put(lastKey, list);
+ break;
+ case "json":
+ try {
+ current.put(lastKey, JsonUtils.parseObject(value, Object.class));
+ } catch (Exception e) {
+ current.put(lastKey, value);
+ }
+ break;
+ default:
+ current.put(lastKey, value);
+ }
+ }
+
+ return config;
+ }
+
+ /**
+ * 鏋勫缓澹扮汗閰嶇疆淇℃伅
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ * @param result 缁撴灉Map
+ */
+ private void buildVoiceprintConfig(String agentId, Map<String, Object> result) {
+ try {
+ // 鑾峰彇澹扮汗鎺ュ彛鍦板潃
+ String voiceprintUrl = sysParamsService.getValue(Constant.SERVER_VOICE_PRINT, true);
+ if (StringUtils.isBlank(voiceprintUrl) || "null".equals(voiceprintUrl)) {
+ return;
+ }
+
+ // 鑾峰彇鏅鸿兘浣撳叧鑱旂殑澹扮汗淇℃伅锛堜笉闇�瑕佺敤鎴锋潈闄愰獙璇侊級
+ List<AgentVoicePrintVO> voiceprints = getVoiceprintsByAgentId(agentId);
+ if (voiceprints == null || voiceprints.isEmpty()) {
+ return;
+ }
+
+ // 鏋勫缓speakers鍒楄〃
+ List<String> speakers = new ArrayList<>();
+ for (AgentVoicePrintVO voiceprint : voiceprints) {
+ String speakerStr = String.format("%s,%s,%s",
+ voiceprint.getId(),
+ voiceprint.getSourceName(),
+ voiceprint.getIntroduce() != null ? voiceprint.getIntroduce() : "");
+ speakers.add(speakerStr);
+ }
+
+ // 鏋勫缓澹扮汗閰嶇疆
+ Map<String, Object> voiceprintConfig = new HashMap<>();
+ voiceprintConfig.put("url", voiceprintUrl);
+ voiceprintConfig.put("speakers", speakers);
+
+ // 鑾峰彇澹扮汗璇嗗埆鐩镐技搴﹂槇鍊硷紝榛樿0.4
+ String thresholdStr = sysParamsService.getValue("server.voiceprint_similarity_threshold", true);
+ if (StringUtils.isNotBlank(thresholdStr) && !"null".equals(thresholdStr)) {
+ try {
+ double threshold = Double.parseDouble(thresholdStr);
+ voiceprintConfig.put("similarity_threshold", threshold);
+ } catch (NumberFormatException e) {
+ // 濡傛灉瑙f瀽澶辫触锛屼娇鐢ㄩ粯璁ゅ��0.4
+ voiceprintConfig.put("similarity_threshold", 0.4);
+ }
+ } else {
+ voiceprintConfig.put("similarity_threshold", 0.4);
+ }
+
+ result.put("voiceprint", voiceprintConfig);
+ } catch (Exception e) {
+ // 澹扮汗閰嶇疆鑾峰彇澶辫触鏃朵笉褰卞搷鍏朵粬鍔熻兘
+ System.err.println("鑾峰彇澹扮汗閰嶇疆澶辫触: " + e.getMessage());
+ }
+ }
+
+ /**
+ * 鑾峰彇鏅鸿兘浣撳叧鑱旂殑澹扮汗淇℃伅
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ * @return 澹扮汗淇℃伅鍒楄〃
+ */
+ private List<AgentVoicePrintVO> getVoiceprintsByAgentId(String agentId) {
+ LambdaQueryWrapper<AgentVoicePrintEntity> queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(AgentVoicePrintEntity::getAgentId, agentId);
+ queryWrapper.orderByAsc(AgentVoicePrintEntity::getCreateDate);
+ List<AgentVoicePrintEntity> entities = agentVoicePrintDao.selectList(queryWrapper);
+ return ConvertUtils.sourceToTarget(entities, AgentVoicePrintVO.class);
+ }
+
+ /**
+ * 鏋勫缓妯″潡閰嶇疆
+ *
+ * @param prompt 鎻愮ず璇�
+ * @param voice 闊宠壊
+ * @param referenceAudio 鍙傝�冮煶棰戣矾寰�
+ * @param referenceText 鍙傝�冩枃鏈�
+ * @param vadModelId VAD妯″瀷ID
+ * @param asrModelId ASR妯″瀷ID
+ * @param llmModelId LLM妯″瀷ID
+ * @param ttsModelId TTS妯″瀷ID
+ * @param memModelId 璁板繂妯″瀷ID
+ * @param intentModelId 鎰忓浘妯″瀷ID
+ * @param result 缁撴灉Map
+ */
+ private void buildModuleConfig(
+ String assistantName,
+ String prompt,
+ String summaryMemory,
+ String voice,
+ String referenceAudio,
+ String referenceText,
+ String language,
+ Integer ttsVolume,
+ Integer ttsRate,
+ Integer ttsPitch,
+ String vadModelId,
+ String asrModelId,
+ String llmModelId,
+ String vllmModelId,
+ String slmModelId,
+ String ttsModelId,
+ String memModelId,
+ String intentModelId,
+ String ragModelId,
+ Map<String, Object> result,
+ boolean isCache) {
+ Map<String, String> selectedModule = new HashMap<>();
+
+ String[] modelTypes = { "VAD", "ASR", "TTS", "Memory", "Intent", "LLM", "VLLM", "SLM", "RAG" };
+ String[] modelIds = { vadModelId, asrModelId, ttsModelId, memModelId, intentModelId, llmModelId, vllmModelId,
+ slmModelId, ragModelId };
+ String intentLLMModelId = null;
+ String memLocalShortLLMModelId = null;
+
+ for (int i = 0; i < modelIds.length; i++) {
+ if (modelIds[i] == null) {
+ continue;
+ }
+ // 鍏抽敭锛氱涓変釜鍙傛暟浼爁alse锛岀‘淇濊幏鍙栧師濮嬪瘑閽�
+ ModelConfigEntity model = modelConfigService.getModelByIdFromCache(modelIds[i]);
+ if (model == null) {
+ continue;
+ }
+ Map<String, Object> typeConfig = new HashMap<>();
+ if (model.getConfigJson() != null) {
+ typeConfig.put(model.getId(), model.getConfigJson());
+ // 濡傛灉鏄疶TS绫诲瀷锛屾坊鍔爌rivate_voice灞炴��
+ if ("TTS".equals(modelTypes[i])) {
+ if (voice != null)
+ ((Map<String, Object>) model.getConfigJson()).put("private_voice", voice);
+ if (referenceAudio != null)
+ ((Map<String, Object>) model.getConfigJson()).put("ref_audio", referenceAudio);
+ if (referenceText != null)
+ ((Map<String, Object>) model.getConfigJson()).put("ref_text", referenceText);
+ if (language != null)
+ ((Map<String, Object>) model.getConfigJson()).put("language", language);
+ if (ttsVolume != null)
+ ((Map<String, Object>) model.getConfigJson()).put("ttsVolume", ttsVolume);
+ if (ttsRate != null)
+ ((Map<String, Object>) model.getConfigJson()).put("ttsRate", ttsRate);
+ if (ttsPitch != null)
+ ((Map<String, Object>) model.getConfigJson()).put("ttsPitch", ttsPitch);
+
+ // 鐏北寮曟搸澹伴煶鍏嬮殕闇�瑕佹浛鎹esource_id
+ Map<String, Object> map = (Map<String, Object>) model.getConfigJson();
+ if (Constant.VOICE_CLONE_HUOSHAN_DOUBLE_STREAM.equals(map.get("type"))) {
+ // 濡傛灉voice鏄�漇_鈥濆紑澶寸殑锛屼娇鐢╯eed-icl-1.0
+ if (voice != null && voice.startsWith("S_")) {
+ map.put("resource_id", "seed-icl-1.0");
+ }
+ }
+ }
+ // 濡傛灉鏄疘ntent绫诲瀷锛屼笖type=intent_llm锛屽垯缁欎粬娣诲姞闄勫姞妯″瀷
+ if ("Intent".equals(modelTypes[i])) {
+ Map<String, Object> map = (Map<String, Object>) model.getConfigJson();
+ if ("intent_llm".equals(map.get("type"))) {
+ intentLLMModelId = (String) map.get("llm");
+ if (StringUtils.isNotBlank(intentLLMModelId) && intentLLMModelId.equals(llmModelId)) {
+ intentLLMModelId = null;
+ }
+ }
+ if (map.get("functions") != null) {
+ String functionStr = (String) map.get("functions");
+ if (StringUtils.isNotBlank(functionStr)) {
+ String[] functions = functionStr.split(";");
+ map.put("functions", functions);
+ }
+ }
+ System.out.println("map: " + map);
+ }
+ if ("Memory".equals(modelTypes[i])) {
+ Map<String, Object> map = (Map<String, Object>) model.getConfigJson();
+ if ("mem_local_short".equals(map.get("type"))) {
+ memLocalShortLLMModelId = (String) map.get("llm");
+ if (StringUtils.isNotBlank(memLocalShortLLMModelId)
+ && memLocalShortLLMModelId.equals(llmModelId)) {
+ memLocalShortLLMModelId = null;
+ }
+ }
+ }
+ // 濡傛灉鏄疞LM绫诲瀷锛屼笖intentLLMModelId涓嶄负绌猴紝鍒欐坊鍔犻檮鍔犳ā鍨�
+ if ("LLM".equals(modelTypes[i])) {
+ if (StringUtils.isNotBlank(intentLLMModelId)) {
+ if (!typeConfig.containsKey(intentLLMModelId)) {
+ // 淇敼杩欓噷锛氭坊鍔爄sMaskSensitive=false鍙傛暟
+ ModelConfigEntity intentLLM = modelConfigService.getModelByIdFromCache(intentLLMModelId);
+ typeConfig.put(intentLLM.getId(), intentLLM.getConfigJson());
+ }
+ }
+ if (StringUtils.isNotBlank(memLocalShortLLMModelId)) {
+ if (!typeConfig.containsKey(memLocalShortLLMModelId)) {
+ // 淇敼杩欓噷锛氭坊鍔爄sMaskSensitive=false鍙傛暟
+ ModelConfigEntity memLocalShortLLM = modelConfigService
+ .getModelByIdFromCache(memLocalShortLLMModelId);
+ typeConfig.put(memLocalShortLLM.getId(), memLocalShortLLM.getConfigJson());
+ }
+ }
+ // LLM涔熻繑鍥炴墍閫夌殑SLM锛屽鏋滃悓鍚峣d鍒欎笉閲嶅鏄剧ず
+ if (StringUtils.isNotBlank(slmModelId) && !slmModelId.equals(llmModelId)) {
+ if (!typeConfig.containsKey(slmModelId)) {
+ ModelConfigEntity slmModel = modelConfigService.getModelByIdFromCache(slmModelId);
+ if (slmModel != null && slmModel.getConfigJson() != null) {
+ typeConfig.put(slmModel.getId(), slmModel.getConfigJson());
+ }
+ }
+ }
+ }
+ }
+ result.put(modelTypes[i], typeConfig);
+
+ selectedModule.put(modelTypes[i], model.getId());
+ }
+
+ result.put("selected_module", selectedModule);
+ if (StringUtils.isNotBlank(prompt)) {
+ prompt = prompt.replace("{{assistant_name}}", StringUtils.isBlank(assistantName) ? "灏忔櫤" : assistantName);
+ }
+ result.put("prompt", prompt);
+ result.put("summaryMemory", summaryMemory);
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/controller/CorrectWordController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/controller/CorrectWordController.java
new file mode 100644
index 0000000..5569587
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/controller/CorrectWordController.java
@@ -0,0 +1,116 @@
+package xiaozhi.modules.correctword.controller;
+
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.AllArgsConstructor;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.utils.Result;
+import xiaozhi.modules.correctword.dto.CorrectWordFileCreateDTO;
+import xiaozhi.modules.correctword.service.CorrectWordFileService;
+import xiaozhi.modules.correctword.vo.CorrectWordFileVO;
+
+@RestController
+@RequestMapping("/correct-word")
+@Tag(name = "鏇挎崲璇嶇鐞�")
+@AllArgsConstructor
+public class CorrectWordController {
+
+ private final CorrectWordFileService correctWordFileService;
+
+ @PostMapping("/file")
+ @Operation(summary = "鍒涘缓鏇挎崲璇嶆枃浠�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<CorrectWordFileVO> createFile(@Valid @RequestBody CorrectWordFileCreateDTO dto) {
+ CorrectWordFileVO vo = correctWordFileService.createFile(dto);
+ return new Result<CorrectWordFileVO>().ok(vo);
+ }
+
+ @PutMapping("/file/{fileId}")
+ @Operation(summary = "淇敼鏇挎崲璇嶆枃浠�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> updateFile(@PathVariable String fileId, @Valid @RequestBody CorrectWordFileCreateDTO dto) {
+ correctWordFileService.updateFile(fileId, dto);
+ return new Result<>();
+ }
+
+ @GetMapping("/file/list")
+ @Operation(summary = "鍒嗛〉鑾峰彇褰撳墠鐢ㄦ埛鏇挎崲璇嶆枃浠跺垪琛�")
+ @RequiresPermissions("sys:role:normal")
+ @Parameters({
+ @Parameter(name = Constant.PAGE, description = "褰撳墠椤电爜锛屼粠1寮�濮�", required = true),
+ @Parameter(name = Constant.LIMIT, description = "姣忛〉鏄剧ず璁板綍鏁�", required = true),
+ })
+ public Result<PageData<CorrectWordFileVO>> listFiles(
+ @Parameter(hidden = true) @RequestParam Map<String, Object> params) {
+ PageData<CorrectWordFileVO> page = correctWordFileService.listFiles(params);
+ return new Result<PageData<CorrectWordFileVO>>().ok(page);
+ }
+
+ @GetMapping("/file/select")
+ @Operation(summary = "鏅鸿兘浣撹幏鍙栧綋鍓嶇敤鎴锋浛鎹㈣瘝鏂囦欢鍒楄〃")
+ @RequiresPermissions("sys:role:normal")
+ public Result<List<CorrectWordFileVO>> listAllFiles() {
+ List<CorrectWordFileVO> list = correctWordFileService.listAllFiles();
+ return new Result<List<CorrectWordFileVO>>().ok(list);
+ }
+
+ @GetMapping("/file/download/{fileId}")
+ @Operation(summary = "涓嬭浇鏇挎崲璇嶆枃浠�")
+ @RequiresPermissions("sys:role:normal")
+ public ResponseEntity<byte[]> downloadFile(@PathVariable String fileId) {
+ CorrectWordFileVO vo = correctWordFileService.getFileContent(fileId);
+ if (vo == null || vo.getContent() == null || vo.getContent().isEmpty()) {
+ return ResponseEntity.notFound().build();
+ }
+ byte[] bytes = String.join("\n", vo.getContent()).getBytes(StandardCharsets.UTF_8);
+ String encodedFileName = URLEncoder.encode(vo.getFileName(), StandardCharsets.UTF_8).replace("+", "%20");
+ String asciiFileName = vo.getFileName().replaceAll("[^\\x00-\\x7F]", "_");
+ return ResponseEntity.ok()
+ .header(HttpHeaders.CONTENT_DISPOSITION,
+ "attachment; filename=\"" + asciiFileName + "\"; filename*=UTF-8''" + encodedFileName)
+ .contentType(MediaType.APPLICATION_OCTET_STREAM)
+ .body(bytes);
+ }
+
+ @DeleteMapping("/file/{fileId}")
+ @Operation(summary = "鍒犻櫎鏇挎崲璇嶆枃浠�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> deleteFile(@PathVariable String fileId) {
+ correctWordFileService.deleteFile(fileId);
+ return new Result<>();
+ }
+
+ @PostMapping("/file/batch-delete")
+ @Operation(summary = "鎵归噺鍒犻櫎鏇挎崲璇嶆枃浠�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> batchDeleteFiles(@RequestBody List<String> fileIds) {
+ if (fileIds == null || fileIds.isEmpty()) {
+ return new Result<>();
+ }
+ correctWordFileService.batchDeleteFiles(fileIds);
+ return new Result<>();
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/dao/CorrectWordFileDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/dao/CorrectWordFileDao.java
new file mode 100644
index 0000000..769c23c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/dao/CorrectWordFileDao.java
@@ -0,0 +1,10 @@
+package xiaozhi.modules.correctword.dao;
+
+import org.apache.ibatis.annotations.Mapper;
+
+import xiaozhi.common.dao.BaseDao;
+import xiaozhi.modules.correctword.entity.CorrectWordFileEntity;
+
+@Mapper
+public interface CorrectWordFileDao extends BaseDao<CorrectWordFileEntity> {
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/dao/CorrectWordItemDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/dao/CorrectWordItemDao.java
new file mode 100644
index 0000000..cd1696b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/dao/CorrectWordItemDao.java
@@ -0,0 +1,15 @@
+package xiaozhi.modules.correctword.dao;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import xiaozhi.common.dao.BaseDao;
+import xiaozhi.modules.correctword.entity.CorrectWordItemEntity;
+
+@Mapper
+public interface CorrectWordItemDao extends BaseDao<CorrectWordItemEntity> {
+
+ int batchInsert(@Param("list") List<CorrectWordItemEntity> items);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/dto/CorrectWordFileCreateDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/dto/CorrectWordFileCreateDTO.java
new file mode 100644
index 0000000..9941abd
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/dto/CorrectWordFileCreateDTO.java
@@ -0,0 +1,24 @@
+package xiaozhi.modules.correctword.dto;
+
+import java.util.List;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+
+@Data
+@Schema(description = "鍒涘缓鏇挎崲璇嶆枃浠禗TO")
+public class CorrectWordFileCreateDTO {
+
+ @NotBlank(message = "鏂囦欢鍚嶄笉鑳戒负绌�")
+ @Schema(description = "鏂囦欢鍚�")
+ private String fileName;
+
+ @NotEmpty(message = "鏇挎崲璇嶅唴瀹逛笉鑳戒负绌�")
+ @Schema(description = "鏇挎崲璇嶅唴瀹癸紝姣忔潯鏍煎紡锛氬師璇峾鏇挎崲璇�")
+ private List<String> content;
+
+ @Schema(description = "鏂囦欢澶у皬锛堝瓧鑺傦級锛屼笉鑳借秴杩�1MB")
+ private Long fileSize;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/entity/CorrectWordFileEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/entity/CorrectWordFileEntity.java
new file mode 100644
index 0000000..1d32623
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/entity/CorrectWordFileEntity.java
@@ -0,0 +1,41 @@
+package xiaozhi.modules.correctword.entity;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@TableName("ai_agent_correct_word_file")
+@Schema(description = "鏅鸿兘浣撴浛鎹㈣瘝鏂囦欢")
+public class CorrectWordFileEntity {
+
+ @TableId(type = IdType.ASSIGN_UUID)
+ @Schema(description = "鏇挎崲璇嶆枃浠禝D")
+ private String id;
+
+ @Schema(description = "鍘熷鏂囦欢鍚�")
+ private String fileName;
+
+ @Schema(description = "鏇挎崲璇嶆暟閲�")
+ private Integer wordCount;
+
+ @Schema(description = "鏂囦欢鍘熷鍐呭锛堢敤浜庝笅杞斤級")
+ private String content;
+
+ @Schema(description = "鍒涘缓鑰�")
+ private Long creator;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ private Date createdAt;
+
+ @Schema(description = "鏇存柊鑰�")
+ private Long updater;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ private Date updatedAt;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/entity/CorrectWordItemEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/entity/CorrectWordItemEntity.java
new file mode 100644
index 0000000..42b9f18
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/entity/CorrectWordItemEntity.java
@@ -0,0 +1,27 @@
+package xiaozhi.modules.correctword.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@TableName("ai_agent_correct_word_item")
+@Schema(description = "鏇挎崲璇嶈瘝鏉�")
+public class CorrectWordItemEntity {
+
+ @TableId(type = IdType.ASSIGN_UUID)
+ @Schema(description = "璇嶆潯ID")
+ private String id;
+
+ @Schema(description = "鎵�灞炴枃浠禝D")
+ private String fileId;
+
+ @Schema(description = "鍘熻瘝")
+ private String sourceWord;
+
+ @Schema(description = "鏇挎崲璇�")
+ private String targetWord;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/service/CorrectWordFileService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/service/CorrectWordFileService.java
new file mode 100644
index 0000000..31e29c8
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/service/CorrectWordFileService.java
@@ -0,0 +1,96 @@
+package xiaozhi.modules.correctword.service;
+
+import java.util.List;
+import java.util.Map;
+
+import xiaozhi.common.page.PageData;
+import xiaozhi.modules.correctword.dto.CorrectWordFileCreateDTO;
+import xiaozhi.modules.correctword.vo.CorrectWordFileVO;
+import xiaozhi.modules.correctword.vo.CorrectWordSimpleVO;
+
+public interface CorrectWordFileService {
+
+ /**
+ * 鍒涘缓鏇挎崲璇嶆枃浠�
+ *
+ * @param dto 鍒涘缓鍙傛暟
+ * @return 鏂囦欢VO
+ */
+ CorrectWordFileVO createFile(CorrectWordFileCreateDTO dto);
+
+ /**
+ * 淇敼鏇挎崲璇嶆枃浠讹紙鍏ㄩ噺鏇挎崲璇嶆潯锛�
+ *
+ * @param fileId 鏂囦欢ID
+ * @param dto 淇敼鍙傛暟
+ */
+ void updateFile(String fileId, CorrectWordFileCreateDTO dto);
+
+ /**
+ * 鑾峰彇褰撳墠鐢ㄦ埛鐨勬浛鎹㈣瘝鏂囦欢鍒楄〃
+ *
+ * @param params 鍒嗛〉鍙傛暟
+ * @return 鍒嗛〉鏁版嵁
+ */
+ PageData<CorrectWordFileVO> listFiles(Map<String, Object> params);
+
+ /**
+ * 鑾峰彇褰撳墠鐢ㄦ埛鐨勬浛鎹㈣瘝鏂囦欢鍒楄〃锛堜笉鍒嗛〉锛岀敤浜庝笅鎷夐�夋嫨锛�
+ *
+ * @return 鏂囦欢鍒楄〃
+ */
+ List<CorrectWordFileVO> listAllFiles();
+
+ /**
+ * 鑾峰彇鏂囦欢鍘熷鍐呭锛堢敤浜庝笅杞斤級
+ *
+ * @param fileId 鏂囦欢ID
+ * @return 鏂囦欢瀹炰綋
+ */
+ CorrectWordFileVO getFileContent(String fileId);
+
+ /**
+ * 鍒犻櫎鏇挎崲璇嶆枃浠跺強鍏舵墍鏈夎瘝鏉″拰鍏宠仈璁板綍
+ *
+ * @param fileId 鏂囦欢ID
+ */
+ void deleteFile(String fileId);
+
+ /**
+ * 鍒犻櫎鏅鸿兘浣撳叧鑱旂殑鏇挎崲璇嶆枃浠跺叧鑱旇褰曪紙涓嶅垹鏂囦欢鏈韩锛�
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ */
+ void deleteMappingsByAgentId(String agentId);
+
+ /**
+ * 鑾峰彇鏅鸿兘浣撶殑鎵�鏈夋浛鎹㈣瘝鏉★紙绮剧畝鐗堬紝渚涜澶囩浣跨敤锛�
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ * @return 鏇挎崲璇嶅垪琛�
+ */
+ List<CorrectWordSimpleVO> getAllItemsByAgentId(String agentId);
+
+ /**
+ * 鑾峰彇鏅鸿兘浣撳叧鑱旂殑鏇挎崲璇嶆枃浠禝D鍒楄〃
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ * @return 鏂囦欢ID鍒楄〃
+ */
+ List<String> getAgentCorrectWordFileIds(String agentId);
+
+ /**
+ * 淇濆瓨鏅鸿兘浣撳叧鑱旂殑鏇挎崲璇嶆枃浠讹紙鍏ㄩ噺鏇挎崲锛�
+ *
+ * @param agentId 鏅鸿兘浣揑D
+ * @param fileIds 鏂囦欢ID鍒楄〃
+ */
+ void saveAgentCorrectWords(String agentId, List<String> fileIds);
+
+ /**
+ * 鎵归噺鍒犻櫎鏇挎崲璇嶆枃浠�
+ *
+ * @param fileIds 鏂囦欢ID鍒楄〃
+ */
+ void batchDeleteFiles(List<String> fileIds);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/service/impl/CorrectWordFileServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/service/impl/CorrectWordFileServiceImpl.java
new file mode 100644
index 0000000..b35a0e0
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/service/impl/CorrectWordFileServiceImpl.java
@@ -0,0 +1,296 @@
+package xiaozhi.modules.correctword.service.impl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+
+import lombok.AllArgsConstructor;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.service.impl.BaseServiceImpl;
+import xiaozhi.common.utils.ConvertUtils;
+import xiaozhi.modules.agent.dao.AgentCorrectWordMappingDao;
+import xiaozhi.modules.correctword.dao.CorrectWordFileDao;
+import xiaozhi.modules.correctword.dao.CorrectWordItemDao;
+import xiaozhi.modules.correctword.dto.CorrectWordFileCreateDTO;
+import xiaozhi.modules.agent.entity.AgentCorrectWordMappingEntity;
+import xiaozhi.modules.correctword.entity.CorrectWordFileEntity;
+import xiaozhi.modules.correctword.entity.CorrectWordItemEntity;
+import xiaozhi.modules.correctword.service.CorrectWordFileService;
+import xiaozhi.modules.correctword.vo.CorrectWordFileVO;
+import xiaozhi.modules.correctword.vo.CorrectWordSimpleVO;
+import xiaozhi.modules.security.user.SecurityUser;
+
+@Service
+@AllArgsConstructor
+public class CorrectWordFileServiceImpl extends BaseServiceImpl<CorrectWordFileDao, CorrectWordFileEntity>
+ implements CorrectWordFileService {
+
+ private final CorrectWordFileDao correctWordFileDao;
+ private final CorrectWordItemDao correctWordItemDao;
+ private final AgentCorrectWordMappingDao agentCorrectWordMappingDao;
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public CorrectWordFileVO createFile(CorrectWordFileCreateDTO dto) {
+ // 鏍¢獙鏂囦欢澶у皬涓嶈兘瓒呰繃1MB
+ if (dto.getFileSize() != null && dto.getFileSize() > 1024 * 1024) {
+ throw new RenException(ErrorCode.FILE_SIZE_OVER_LIMIT);
+ }
+
+ // 鏍¢獙鏂囦欢鍚嶆槸鍚﹂噸澶�
+ Long userId = SecurityUser.getUserId();
+ LambdaQueryWrapper<CorrectWordFileEntity> nameWrapper = new LambdaQueryWrapper<>();
+ nameWrapper.eq(CorrectWordFileEntity::getCreator, userId)
+ .eq(CorrectWordFileEntity::getFileName, dto.getFileName());
+ if (correctWordFileDao.selectCount(nameWrapper) > 0) {
+ throw new RenException(ErrorCode.CORRECT_WORD_FILE_NAME_EXISTS);
+ }
+
+ List<CorrectWordItemEntity> items = parseContent(dto.getContent());
+
+ // 淇濆瓨鏂囦欢璁板綍
+ CorrectWordFileEntity fileEntity = new CorrectWordFileEntity();
+ fileEntity.setFileName(dto.getFileName());
+ fileEntity.setWordCount(items.size());
+ fileEntity.setContent(String.join("\n", dto.getContent()));
+ fileEntity.setCreator(SecurityUser.getUserId());
+ fileEntity.setCreatedAt(new Date());
+ correctWordFileDao.insert(fileEntity);
+
+ // 璁剧疆fileId骞舵壒閲忎繚瀛樿瘝鏉�
+ String fileId = fileEntity.getId();
+ for (CorrectWordItemEntity item : items) {
+ item.setFileId(fileId);
+ }
+ if (!items.isEmpty()) {
+ correctWordItemDao.batchInsert(items);
+ }
+
+ return toVO(fileEntity);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updateFile(String fileId, CorrectWordFileCreateDTO dto) {
+ CorrectWordFileEntity fileEntity = correctWordFileDao.selectById(fileId);
+ if (fileEntity == null) {
+ return;
+ }
+
+ // 鏍¢獙鏂囦欢鍚嶆槸鍚﹂噸澶嶏紙鎺掗櫎鑷韩锛�
+ Long userId = SecurityUser.getUserId();
+ LambdaQueryWrapper<CorrectWordFileEntity> nameWrapper = new LambdaQueryWrapper<>();
+ nameWrapper.eq(CorrectWordFileEntity::getCreator, userId)
+ .eq(CorrectWordFileEntity::getFileName, dto.getFileName())
+ .ne(CorrectWordFileEntity::getId, fileId);
+ if (correctWordFileDao.selectCount(nameWrapper) > 0) {
+ throw new RenException("鏂囦欢鍚嶅凡瀛樺湪锛�" + dto.getFileName());
+ }
+
+ // 鍏堝垹闄ゆ棫璇嶆潯
+ LambdaQueryWrapper<CorrectWordItemEntity> deleteWrapper = new LambdaQueryWrapper<>();
+ deleteWrapper.eq(CorrectWordItemEntity::getFileId, fileId);
+ correctWordItemDao.delete(deleteWrapper);
+
+ // 瑙f瀽鏂拌瘝鏉″苟鎵归噺淇濆瓨
+ List<CorrectWordItemEntity> items = parseContent(dto.getContent());
+ if (!items.isEmpty()) {
+ for (CorrectWordItemEntity item : items) {
+ item.setFileId(fileId);
+ }
+ correctWordItemDao.batchInsert(items);
+ }
+
+ // 鏇存柊鏂囦欢璁板綍
+ fileEntity.setFileName(dto.getFileName());
+ fileEntity.setWordCount(items.size());
+ fileEntity.setContent(String.join("\n", dto.getContent()));
+ fileEntity.setUpdater(SecurityUser.getUserId());
+ fileEntity.setUpdatedAt(new Date());
+ correctWordFileDao.updateById(fileEntity);
+ }
+
+ @Override
+ public PageData<CorrectWordFileVO> listFiles(Map<String, Object> params) {
+ Long userId = SecurityUser.getUserId();
+ IPage<CorrectWordFileEntity> page = getPage(params, "created_at", false);
+ LambdaQueryWrapper<CorrectWordFileEntity> wrapper = new LambdaQueryWrapper<>();
+ wrapper.eq(CorrectWordFileEntity::getCreator, userId)
+ .orderByDesc(CorrectWordFileEntity::getCreatedAt);
+ correctWordFileDao.selectPage(page, wrapper);
+ List<CorrectWordFileVO> voList = toVOList(page.getRecords());
+ return new PageData<>(voList, page.getTotal());
+ }
+
+ @Override
+ public List<CorrectWordFileVO> listAllFiles() {
+ Long userId = SecurityUser.getUserId();
+ LambdaQueryWrapper<CorrectWordFileEntity> wrapper = new LambdaQueryWrapper<>();
+ wrapper.eq(CorrectWordFileEntity::getCreator, userId)
+ .orderByDesc(CorrectWordFileEntity::getCreatedAt);
+ List<CorrectWordFileEntity> entities = correctWordFileDao.selectList(wrapper);
+ return toVOList(entities);
+ }
+
+ @Override
+ public CorrectWordFileVO getFileContent(String fileId) {
+ CorrectWordFileEntity entity = correctWordFileDao.selectById(fileId);
+ return toVO(entity);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteFile(String fileId) {
+ if (fileId == null || fileId.trim().isEmpty()) {
+ return;
+ }
+ // 鍏堝垹闄ゅ叧鑱旇〃璁板綍
+ agentCorrectWordMappingDao.deleteByFileId(fileId);
+ // 鍒犻櫎璇嶆潯
+ LambdaQueryWrapper<CorrectWordItemEntity> wrapper = new LambdaQueryWrapper<>();
+ wrapper.eq(CorrectWordItemEntity::getFileId, fileId);
+ correctWordItemDao.delete(wrapper);
+ // 鍒犻櫎鏂囦欢
+ correctWordFileDao.deleteById(fileId);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteMappingsByAgentId(String agentId) {
+ agentCorrectWordMappingDao.deleteByAgentId(agentId);
+ }
+
+ @Override
+ public List<CorrectWordSimpleVO> getAllItemsByAgentId(String agentId) {
+ // 閫氳繃鍏宠仈琛ㄨ幏鍙栨枃浠禝D鍒楄〃
+ List<AgentCorrectWordMappingEntity> mappings = agentCorrectWordMappingDao.selectByAgentId(agentId);
+ if (mappings == null || mappings.isEmpty()) {
+ return new ArrayList<>();
+ }
+ List<String> fileIds = mappings.stream()
+ .map(AgentCorrectWordMappingEntity::getFileId)
+ .collect(Collectors.toList());
+
+ // 鏍规嵁鏂囦欢ID鍒楄〃鏌ヨ璇嶆潯
+ LambdaQueryWrapper<CorrectWordItemEntity> wrapper = new LambdaQueryWrapper<>();
+ wrapper.in(CorrectWordItemEntity::getFileId, fileIds);
+ List<CorrectWordItemEntity> entities = correctWordItemDao.selectList(wrapper);
+ return ConvertUtils.sourceToTarget(entities, CorrectWordSimpleVO.class);
+ }
+
+ @Override
+ public List<String> getAgentCorrectWordFileIds(String agentId) {
+ List<AgentCorrectWordMappingEntity> mappings = agentCorrectWordMappingDao.selectByAgentId(agentId);
+ if (mappings == null || mappings.isEmpty()) {
+ return new ArrayList<>();
+ }
+ return mappings.stream()
+ .map(AgentCorrectWordMappingEntity::getFileId)
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void saveAgentCorrectWords(String agentId, List<String> fileIds) {
+ // 鍏堝垹闄ゆ棫鐨勫叧鑱旇褰�
+ agentCorrectWordMappingDao.deleteByAgentId(agentId);
+
+ if (fileIds == null || fileIds.isEmpty()) {
+ return;
+ }
+
+ // 鎵归噺鎻掑叆鏂扮殑鍏宠仈璁板綍
+ Long userId = SecurityUser.getUserId();
+ Date now = new Date();
+ List<AgentCorrectWordMappingEntity> mappings = new ArrayList<>();
+ for (String fileId : fileIds) {
+ AgentCorrectWordMappingEntity mapping = new AgentCorrectWordMappingEntity();
+ mapping.setAgentId(agentId);
+ mapping.setFileId(fileId);
+ mapping.setCreator(userId);
+ mapping.setCreatedAt(now);
+ mapping.setUpdater(userId);
+ mapping.setUpdatedAt(now);
+ mappings.add(mapping);
+ }
+ agentCorrectWordMappingDao.batchInsertMapping(mappings);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void batchDeleteFiles(List<String> fileIds) {
+ if (fileIds == null || fileIds.isEmpty()) {
+ return;
+ }
+ for (String fileId : fileIds) {
+ if (fileId == null || fileId.trim().isEmpty()) {
+ continue;
+ }
+ deleteFile(fileId.trim());
+ }
+ }
+
+ /**
+ * 瑙f瀽鏇挎崲璇嶅唴瀹癸紝姣忔潯鏍煎紡锛氬師璇峾鏇挎崲璇�
+ */
+ private List<CorrectWordItemEntity> parseContent(List<String> lines) {
+ List<CorrectWordItemEntity> items = new ArrayList<>();
+ if (lines == null || lines.isEmpty()) {
+ return items;
+ }
+ for (String line : lines) {
+ line = line.trim();
+ if (line.isEmpty()) {
+ continue;
+ }
+ int idx = line.indexOf('|');
+ if (idx <= 0 || idx >= line.length() - 1) {
+ continue;
+ }
+ String sourceWord = line.substring(0, idx).trim();
+ String targetWord = line.substring(idx + 1).trim();
+ if (sourceWord.isEmpty() || targetWord.isEmpty()) {
+ continue;
+ }
+ CorrectWordItemEntity item = new CorrectWordItemEntity();
+ item.setSourceWord(sourceWord);
+ item.setTargetWord(targetWord);
+ items.add(item);
+ }
+ return items;
+ }
+
+ private CorrectWordFileVO toVO(CorrectWordFileEntity entity) {
+ if (entity == null) {
+ return null;
+ }
+ CorrectWordFileVO vo = new CorrectWordFileVO();
+ vo.setId(entity.getId());
+ vo.setFileName(entity.getFileName());
+ vo.setWordCount(entity.getWordCount());
+ vo.setContent(entity.getContent() != null
+ ? Arrays.asList(entity.getContent().split("\n"))
+ : new ArrayList<>());
+ vo.setCreatedAt(entity.getCreatedAt());
+ vo.setUpdatedAt(entity.getUpdatedAt());
+ return vo;
+ }
+
+ private List<CorrectWordFileVO> toVOList(List<CorrectWordFileEntity> entities) {
+ if (entities == null || entities.isEmpty()) {
+ return new ArrayList<>();
+ }
+ return entities.stream().map(this::toVO).collect(Collectors.toList());
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/vo/CorrectWordFileVO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/vo/CorrectWordFileVO.java
new file mode 100644
index 0000000..a881b8d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/vo/CorrectWordFileVO.java
@@ -0,0 +1,30 @@
+package xiaozhi.modules.correctword.vo;
+
+import java.util.Date;
+import java.util.List;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@Schema(description = "鏇挎崲璇嶆枃浠跺垪琛╒O")
+public class CorrectWordFileVO {
+
+ @Schema(description = "鏇挎崲璇嶆枃浠禝D")
+ private String id;
+
+ @Schema(description = "鍘熷鏂囦欢鍚�")
+ private String fileName;
+
+ @Schema(description = "鏇挎崲璇嶆暟閲�")
+ private Integer wordCount;
+
+ @Schema(description = "鏇挎崲璇嶅唴瀹癸紝姣忚涓�鏉�")
+ private List<String> content;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ private Date createdAt;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ private Date updatedAt;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/vo/CorrectWordSimpleVO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/vo/CorrectWordSimpleVO.java
new file mode 100644
index 0000000..289b8a4
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/correctword/vo/CorrectWordSimpleVO.java
@@ -0,0 +1,15 @@
+package xiaozhi.modules.correctword.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@Schema(description = "鏇挎崲璇嶇簿绠�VO锛堣澶囩浣跨敤锛�")
+public class CorrectWordSimpleVO {
+
+ @Schema(description = "鍘熻瘝")
+ private String sourceWord;
+
+ @Schema(description = "鏇挎崲璇�")
+ private String targetWord;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/controller/DeviceController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/controller/DeviceController.java
new file mode 100644
index 0000000..f8a9b70
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/controller/DeviceController.java
@@ -0,0 +1,216 @@
+package xiaozhi.modules.device.controller;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.BeanUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.redis.RedisKeys;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.common.user.UserDetail;
+import xiaozhi.common.utils.Result;
+import xiaozhi.modules.device.dto.DeviceAddressBookAliasDTO;
+import xiaozhi.modules.device.dto.DeviceAddressBookPermissionDTO;
+import xiaozhi.modules.device.dto.DeviceManualAddDTO;
+import xiaozhi.modules.device.dto.DeviceRegisterDTO;
+import xiaozhi.modules.device.dto.DeviceToolsCallReqDTO;
+import xiaozhi.modules.device.dto.DeviceUnBindDTO;
+import xiaozhi.modules.device.dto.DeviceUpdateDTO;
+import xiaozhi.modules.device.entity.DeviceEntity;
+import xiaozhi.modules.device.service.DeviceAddressBookService;
+import xiaozhi.modules.device.service.DeviceService;
+import xiaozhi.modules.device.vo.UserShowDeviceListVO;
+import xiaozhi.modules.security.user.SecurityUser;
+import xiaozhi.modules.sys.service.SysParamsService;
+
+@Tag(name = "璁惧绠$悊")
+@RestController
+@RequestMapping("/device")
+public class DeviceController {
+ private final DeviceService deviceService;
+ private final DeviceAddressBookService deviceAddressBookService;
+ private final RedisUtils redisUtils;
+ private final SysParamsService sysParamsService;
+
+ public DeviceController(DeviceService deviceService, DeviceAddressBookService deviceAddressBookService,
+ RedisUtils redisUtils, SysParamsService sysParamsService) {
+ this.deviceService = deviceService;
+ this.deviceAddressBookService = deviceAddressBookService;
+ this.redisUtils = redisUtils;
+ this.sysParamsService = sysParamsService;
+ }
+
+ @PostMapping("/bind/{agentId}/{deviceCode}")
+ @Operation(summary = "缁戝畾璁惧")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> bindDevice(@PathVariable String agentId, @PathVariable String deviceCode) {
+ deviceService.deviceActivation(agentId, deviceCode);
+ return new Result<>();
+ }
+
+ @PostMapping("/register")
+ @Operation(summary = "娉ㄥ唽璁惧")
+ public Result<String> registerDevice(@RequestBody DeviceRegisterDTO deviceRegisterDTO) {
+ String macAddress = deviceRegisterDTO.getMacAddress();
+ if (StringUtils.isBlank(macAddress)) {
+ return new Result<String>().error(ErrorCode.MCA_NOT_NULL);
+ }
+ // 鐢熸垚鍏綅楠岃瘉鐮�
+ String code;
+ String key;
+ String existsMac = null;
+ do {
+ code = String.valueOf(Math.random()).substring(2, 8);
+ key = RedisKeys.getDeviceCaptchaKey(code);
+ existsMac = (String) redisUtils.get(key);
+ } while (StringUtils.isNotBlank(existsMac));
+
+ redisUtils.set(key, macAddress);
+ return new Result<String>().ok(code);
+ }
+
+ @GetMapping("/bind/{agentId}")
+ @Operation(summary = "鑾峰彇宸茬粦瀹氳澶�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<List<UserShowDeviceListVO>> getUserDevices(@PathVariable String agentId) {
+ UserDetail user = SecurityUser.getUser();
+ List<UserShowDeviceListVO> devices = deviceService.getUserDeviceList(user.getId(), agentId);
+ return new Result<List<UserShowDeviceListVO>>().ok(devices);
+ }
+
+ @PostMapping("/bind/{agentId}")
+ @Operation(summary = "璁惧鍦ㄧ嚎鎺ュ彛")
+ @RequiresPermissions("sys:role:normal")
+ public Result<String> forwardToMqttGateway(@PathVariable String agentId, @RequestBody String requestBody) {
+ try {
+ return new Result<String>().ok(deviceService.getDeviceOnlineData(agentId));
+ } catch (Exception e) {
+ return new Result<String>().error("杞彂璇锋眰澶辫触: " + e.getMessage());
+ }
+ }
+
+ @PostMapping("/unbind")
+ @Operation(summary = "瑙g粦璁惧")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> unbindDevice(@RequestBody DeviceUnBindDTO unDeviveBind) {
+ UserDetail user = SecurityUser.getUser();
+ deviceService.unbindDevice(user.getId(), unDeviveBind.getDeviceId());
+ return new Result<Void>();
+ }
+
+ @PutMapping("/update/{id}")
+ @Operation(summary = "鏇存柊璁惧淇℃伅")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> updateDeviceInfo(@PathVariable String id, @Valid @RequestBody DeviceUpdateDTO deviceUpdateDTO) {
+ DeviceEntity entity = deviceService.selectById(id);
+ if (entity == null) {
+ return new Result<Void>().error("璁惧涓嶅瓨鍦�");
+ }
+ UserDetail user = SecurityUser.getUser();
+ if (!entity.getUserId().equals(user.getId())) {
+ return new Result<Void>().error("璁惧涓嶅瓨鍦�");
+ }
+ BeanUtils.copyProperties(deviceUpdateDTO, entity);
+ if (!deviceService.updateById(entity)) {
+ return new Result<Void>().error(ErrorCode.UPDATE_DATA_FAILED);
+ }
+ return new Result<Void>();
+ }
+
+ @PostMapping("/manual-add")
+ @Operation(summary = "鎵嬪姩娣诲姞璁惧")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> manualAddDevice(@RequestBody @Valid DeviceManualAddDTO dto) {
+ UserDetail user = SecurityUser.getUser();
+ deviceService.manualAddDevice(user.getId(), dto);
+ return new Result<>();
+ }
+
+ @PostMapping("/tools/list/{deviceId}")
+ @Operation(summary = "鑾峰彇璁惧宸ュ叿鍒楄〃")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Object> getDeviceTools(@PathVariable String deviceId) {
+ Object toolsData = deviceService.getDeviceTools(deviceId);
+ if (toolsData == null) {
+ return new Result<Object>().error(ErrorCode.DEVICE_NOT_EXIST);
+ }
+
+ return new Result<Object>().ok(toolsData);
+ }
+
+ @PostMapping("/tools/call/{deviceId}")
+ @Operation(summary = "璋冪敤璁惧宸ュ叿")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Object> callDeviceTool(@PathVariable String deviceId,
+ @Valid @RequestBody DeviceToolsCallReqDTO request) {
+ String toolName = request.getName();
+ Map<String, Object> arguments = request.getArguments();
+
+ Object result = deviceService.callDeviceTool(deviceId, toolName, arguments);
+ if (result == null) {
+ return new Result<Object>().error(ErrorCode.DEVICE_NOT_EXIST);
+ }
+
+ Result<Object> response = new Result<Object>();
+ response.setMsg("Tools called successfully");
+ return response.ok(result);
+ }
+
+ @GetMapping("/address-book/{macAddress}")
+ @Operation(summary = "鑾峰彇璁惧閫氳褰�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Object> getAddressBook(@PathVariable String macAddress) {
+ return new Result<Object>().ok(deviceAddressBookService.getAddressBookList(macAddress));
+ }
+
+ @GetMapping("/address-book/call")
+ @Operation(summary = "鏍规嵁鏄电О鍙戣捣鍛煎彨")
+ public Result<Map<String, Object>> callByNickname(String callerMac, String nickname,
+ @RequestParam(required = false, defaultValue = "false") boolean answer) {
+ Map<String, Object> result = deviceAddressBookService.callByNickname(callerMac, nickname, answer);
+ if (result == null) {
+ return new Result<Map<String, Object>>().error("鏈壘鍒板搴旇澶�");
+ }
+ return new Result<Map<String, Object>>().ok(result);
+ }
+
+ @PutMapping("/address-book/alias")
+ @Operation(summary = "鏇存柊璁惧閫氳褰曞埆鍚�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> updateAlias(@Valid @RequestBody DeviceAddressBookAliasDTO dto) {
+ UserDetail user = SecurityUser.getUser();
+ DeviceEntity callerDevice = deviceService.getDeviceByMacAddress(dto.getMacAddress());
+ if (callerDevice == null || !callerDevice.getUserId().equals(user.getId())) {
+ return new Result<Void>().error("鏃犳潈闄愭搷浣滆璁惧");
+ }
+ deviceAddressBookService.saveOrUpdate(dto.getMacAddress(), dto.getTargetMac(), dto.getAlias(), null);
+ return new Result<Void>();
+ }
+
+ @PutMapping("/address-book/permission")
+ @Operation(summary = "鏇存柊璁惧閫氳褰曟潈闄�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> updatePermission(@Valid @RequestBody DeviceAddressBookPermissionDTO dto) {
+ UserDetail user = SecurityUser.getUser();
+ DeviceEntity callerDevice = deviceService.getDeviceByMacAddress(dto.getMacAddress());
+ if (callerDevice == null || !callerDevice.getUserId().equals(user.getId())) {
+ return new Result<Void>().error("鏃犳潈闄愭搷浣滆璁惧");
+ }
+ deviceAddressBookService.saveOrUpdate(dto.getMacAddress(), dto.getTargetMac(), null, dto.getHasPermission());
+ return new Result<Void>();
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/controller/OTAController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/controller/OTAController.java
new file mode 100644
index 0000000..4a4bfee
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/controller/OTAController.java
@@ -0,0 +1,122 @@
+package xiaozhi.modules.device.controller;
+
+import java.nio.charset.StandardCharsets;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import io.swagger.v3.oas.annotations.Hidden;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.modules.device.dto.DeviceReportReqDTO;
+import xiaozhi.modules.device.dto.DeviceReportRespDTO;
+import xiaozhi.modules.device.entity.DeviceEntity;
+import xiaozhi.modules.device.service.DeviceService;
+import xiaozhi.modules.sys.service.SysParamsService;
+
+@Tag(name = "璁惧绠$悊", description = "OTA 鐩稿叧鎺ュ彛")
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/ota/")
+public class OTAController {
+ private final DeviceService deviceService;
+ private final SysParamsService sysParamsService;
+
+ @Operation(summary = "OTA鐗堟湰鍜岃澶囨縺娲荤姸鎬佹鏌�")
+ @PostMapping
+ public ResponseEntity<String> checkOTAVersion(
+ @RequestBody DeviceReportReqDTO deviceReportReqDTO,
+ @Parameter(name = "Device-Id", description = "璁惧鍞竴鏍囪瘑", required = true, in = ParameterIn.HEADER) @RequestHeader("Device-Id") String deviceId,
+ @Parameter(name = "Client-Id", description = "瀹㈡埛绔爣璇�", required = false, in = ParameterIn.HEADER) @RequestHeader(value = "Client-Id", required = false) String clientId) {
+ if (StringUtils.isBlank(deviceId)) {
+ return createResponse(DeviceReportRespDTO.createError("Device ID is required"));
+ }
+ if (StringUtils.isBlank(clientId)) {
+ clientId = deviceId;
+ }
+ boolean macAddressValid = isMacAddressValid(deviceId);
+ // 璁惧Id鍜孧ac鍦板潃搴旀槸涓�鑷寸殑, 骞朵笖蹇呴』闇�瑕乤pplication瀛楁
+ if (!macAddressValid) {
+ return createResponse(DeviceReportRespDTO.createError("Invalid device ID"));
+ }
+ return createResponse(deviceService.checkDeviceActive(deviceId, clientId, deviceReportReqDTO));
+ }
+
+ @Operation(summary = "璁惧蹇�熸鏌ユ縺娲荤姸鎬�")
+ @PostMapping("activate")
+ public ResponseEntity<String> activateDevice(
+ @Parameter(name = "Device-Id", description = "璁惧鍞竴鏍囪瘑", required = true, in = ParameterIn.HEADER) @RequestHeader("Device-Id") String deviceId,
+ @Parameter(name = "Client-Id", description = "瀹㈡埛绔爣璇�", required = false, in = ParameterIn.HEADER) @RequestHeader(value = "Client-Id", required = false) String clientId) {
+ if (StringUtils.isBlank(deviceId)) {
+ return ResponseEntity.status(202).build();
+ }
+ DeviceEntity device = deviceService.getDeviceByMacAddress(deviceId);
+ if (device == null) {
+ return ResponseEntity.status(202).build();
+ }
+ return ResponseEntity.ok("success");
+ }
+
+ @GetMapping
+ @Hidden
+ public ResponseEntity<String> getOTA() {
+ String mqttUdpConfig = sysParamsService.getValue(Constant.SERVER_MQTT_GATEWAY, false);
+ if (StringUtils.isBlank(mqttUdpConfig)) {
+ return ResponseEntity.ok("OTA鎺ュ彛涓嶆甯革紝缂哄皯mqtt_gateway鍦板潃锛岃鐧诲綍鏅烘帶鍙帮紝鍦ㄥ弬鏁扮鐞嗘壘鍒般�恠erver.mqtt_gateway銆戦厤缃�");
+ }
+ String wsUrl = sysParamsService.getValue(Constant.SERVER_WEBSOCKET, true);
+ if (StringUtils.isBlank(wsUrl) || wsUrl.equals("null")) {
+ return ResponseEntity.ok("OTA鎺ュ彛涓嶆甯革紝缂哄皯websocket鍦板潃锛岃鐧诲綍鏅烘帶鍙帮紝鍦ㄥ弬鏁扮鐞嗘壘鍒般�恠erver.websocket銆戦厤缃�");
+ }
+ String otaUrl = sysParamsService.getValue(Constant.SERVER_OTA, true);
+ if (StringUtils.isBlank(otaUrl) || otaUrl.equals("null")) {
+ return ResponseEntity.ok("OTA鎺ュ彛涓嶆甯革紝缂哄皯ota鍦板潃锛岃鐧诲綍鏅烘帶鍙帮紝鍦ㄥ弬鏁扮鐞嗘壘鍒般�恠erver.ota銆戦厤缃�");
+ }
+ return ResponseEntity.ok("OTA鎺ュ彛杩愯姝e父锛寃ebsocket闆嗙兢鏁伴噺锛�" + wsUrl.split(";").length);
+ }
+
+ @SneakyThrows
+ private ResponseEntity<String> createResponse(DeviceReportRespDTO deviceReportRespDTO) {
+ ObjectMapper objectMapper = new ObjectMapper();
+ objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ String json = objectMapper.writeValueAsString(deviceReportRespDTO);
+ byte[] jsonBytes = json.getBytes(StandardCharsets.UTF_8);
+ return ResponseEntity
+ .ok()
+ .contentType(MediaType.APPLICATION_JSON)
+ .contentLength(jsonBytes.length)
+ .body(json);
+ }
+
+ /**
+ * 绠�鍗曞垽鏂璵ac鍦板潃鏄惁鏈夋晥锛堥潪涓ユ牸锛�
+ *
+ * @param macAddress
+ * @return
+ */
+ private boolean isMacAddressValid(String macAddress) {
+ if (StringUtils.isBlank(macAddress)) {
+ return false;
+ }
+ // MAC鍦板潃閫氬父涓�12浣嶅崄鍏繘鍒舵暟瀛楋紝鍙互鍖呭惈鍐掑彿鎴栬繛瀛楃鍒嗛殧绗�
+ String macPattern = "^([0-9A-Za-z]{2}[:-]){5}([0-9A-Za-z]{2})$";
+ return macAddress.matches(macPattern);
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/controller/OTAMagController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/controller/OTAMagController.java
new file mode 100644
index 0000000..868a31b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/controller/OTAMagController.java
@@ -0,0 +1,334 @@
+package xiaozhi.modules.device.controller;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import cn.hutool.crypto.digest.DigestUtil;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.redis.RedisKeys;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.common.utils.Result;
+import xiaozhi.common.validator.ValidatorUtils;
+import xiaozhi.modules.device.entity.OtaEntity;
+import xiaozhi.modules.device.service.OtaService;
+import xiaozhi.modules.security.user.SecurityUser;
+import xiaozhi.modules.sys.enums.SuperAdminEnum;
+import xiaozhi.modules.sys.service.SysParamsService;
+
+@Tag(name = "鍥轰欢鍗囩骇绠$悊", description = "OTA 鐩稿叧鎺ュ彛")
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/otaMag")
+public class OTAMagController {
+ private static final Logger logger = LoggerFactory.getLogger(OTAController.class);
+ private final OtaService otaService;
+ private final RedisUtils redisUtils;
+ private final SysParamsService sysParamsService;
+
+ @GetMapping
+ @Operation(summary = "鍒嗛〉鏌ヨ OTA 鍥轰欢淇℃伅")
+ @Parameters({
+ @Parameter(name = Constant.PAGE, description = "褰撳墠椤电爜锛屼粠1寮�濮�", required = true),
+ @Parameter(name = Constant.LIMIT, description = "姣忛〉鏄剧ず璁板綍鏁�", required = true)
+ })
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<PageData<OtaEntity>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
+ ValidatorUtils.validateEntity(params);
+ PageData<OtaEntity> page = otaService.page(params);
+ return new Result<PageData<OtaEntity>>().ok(page);
+ }
+
+ @GetMapping("{id}")
+ @Operation(summary = "淇℃伅 OTA 鍥轰欢淇℃伅")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<OtaEntity> get(@PathVariable("id") String id) {
+ OtaEntity data = otaService.selectById(id);
+ return new Result<OtaEntity>().ok(data);
+ }
+
+ @PostMapping
+ @Operation(summary = "淇濆瓨 OTA 鍥轰欢淇℃伅")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<Void> save(@RequestBody OtaEntity entity) {
+ if (entity == null) {
+ return new Result<Void>().error("鍥轰欢淇℃伅涓嶈兘涓虹┖");
+ }
+ if (StringUtils.isBlank(entity.getFirmwareName())) {
+ return new Result<Void>().error("鍥轰欢鍚嶇О涓嶈兘涓虹┖");
+ }
+ if (StringUtils.isBlank(entity.getType())) {
+ return new Result<Void>().error("鍥轰欢绫诲瀷涓嶈兘涓虹┖");
+ }
+ if (StringUtils.isBlank(entity.getVersion())) {
+ return new Result<Void>().error("鐗堟湰鍙蜂笉鑳戒负绌�");
+ }
+ try {
+ otaService.save(entity);
+ return new Result<Void>();
+ } catch (RuntimeException e) {
+ return new Result<Void>().error(e.getMessage());
+ }
+ }
+
+ @DeleteMapping("/{id}")
+ @Operation(summary = "OTA 鍒犻櫎")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<Void> delete(@PathVariable("id") String[] ids) {
+ if (ids == null || ids.length == 0) {
+ return new Result<Void>().error("鍒犻櫎鐨勫浐浠禝D涓嶈兘涓虹┖");
+ }
+ otaService.delete(ids);
+ return new Result<Void>();
+ }
+
+ @PutMapping("/{id}")
+ @Operation(summary = "淇敼 OTA 鍥轰欢淇℃伅")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<?> update(@PathVariable("id") String id, @RequestBody OtaEntity entity) {
+ if (entity == null) {
+ return new Result<>().error("鍥轰欢淇℃伅涓嶈兘涓虹┖");
+ }
+ entity.setId(id);
+ try {
+ otaService.update(entity);
+ return new Result<>();
+ } catch (RuntimeException e) {
+ return new Result<>().error(e.getMessage());
+ }
+ }
+
+ @GetMapping("/getDownloadUrl/{id}")
+ @Operation(summary = "鑾峰彇 OTA 鍥轰欢涓嬭浇閾炬帴")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<String> getDownloadUrl(@PathVariable("id") String id) {
+ String uuid = UUID.randomUUID().toString();
+ redisUtils.set(RedisKeys.getOtaIdKey(uuid), id);
+ return new Result<String>().ok(uuid);
+ }
+
+ @GetMapping("/download/{uuid}")
+ @Operation(summary = "涓嬭浇鍥轰欢鏂囦欢")
+ public ResponseEntity<byte[]> downloadFirmware(@PathVariable("uuid") String uuid) {
+ String id = (String) redisUtils.get(RedisKeys.getOtaIdKey(uuid));
+ if (StringUtils.isBlank(id)) {
+ return ResponseEntity.notFound().build();
+ }
+
+ // 妫�鏌ヤ笅杞芥鏁�
+ String downloadCountKey = RedisKeys.getOtaDownloadCountKey(uuid);
+ Integer downloadCount = (Integer) Optional.ofNullable(redisUtils.get(downloadCountKey)).orElse(0);
+
+ // 濡傛灉涓嬭浇娆℃暟瓒呰繃3娆★紝杩斿洖404
+ if (downloadCount >= 3) {
+ redisUtils.delete(List.of(downloadCountKey, RedisKeys.getOtaIdKey(uuid)));
+ logger.warn("Download limit exceeded for UUID: {}", uuid);
+ return ResponseEntity.notFound().build();
+ }
+
+ redisUtils.set(downloadCountKey, downloadCount + 1);
+
+ try {
+ // 鑾峰彇鍥轰欢淇℃伅
+ OtaEntity otaEntity = null;
+ if (id.indexOf("file:") == 0) {
+ id = id.substring(5);
+ otaEntity = new OtaEntity();
+ otaEntity.setFirmwarePath(id);
+ otaEntity.setType("assets");
+ otaEntity.setVersion("1.0.0");
+ } else {
+ otaEntity = otaService.selectById(id);
+ }
+
+ if (otaEntity == null || StringUtils.isBlank(otaEntity.getFirmwarePath())) {
+ logger.warn("Firmware not found or path is empty for ID: {}", id);
+ return ResponseEntity.notFound().build();
+ }
+
+ // 鑾峰彇鏂囦欢璺緞 - 纭繚璺緞鏄粷瀵硅矾寰勬垨姝g‘鐨勭浉瀵硅矾寰�
+ String firmwarePath = otaEntity.getFirmwarePath();
+ String originalFilename = otaEntity.getType() + "_" + otaEntity.getVersion();
+ Path path;
+
+ // 妫�鏌ユ槸鍚︽槸缁濆璺緞
+ if (Paths.get(firmwarePath).isAbsolute()) {
+ path = Paths.get(firmwarePath);
+ } else {
+ // 濡傛灉鏄浉瀵硅矾寰勶紝鍒欎粠褰撳墠宸ヤ綔鐩綍瑙f瀽
+ path = Paths.get(System.getProperty("user.dir"), firmwarePath);
+ }
+
+ logger.info("Attempting to download firmware for ID: {}, DB path: {}, resolved path: {}",
+ id, firmwarePath, path.toAbsolutePath());
+
+ if (!Files.exists(path) || !Files.isRegularFile(path)) {
+ // 灏濊瘯鐩存帴浠巉irmware鐩綍涓嬫煡鎵炬枃浠跺悕
+ String fileName = new File(firmwarePath).getName();
+ Path altPath = Paths.get(System.getProperty("user.dir"), "firmware", fileName);
+
+ logger.info("File not found at primary path, trying alternative path: {}", altPath.toAbsolutePath());
+
+ if (Files.exists(altPath) && Files.isRegularFile(altPath)) {
+ path = altPath;
+ } else {
+ logger.error("Firmware file not found at either path: {} or {}",
+ path.toAbsolutePath(), altPath.toAbsolutePath());
+ return ResponseEntity.notFound().build();
+ }
+ }
+
+ // 璇诲彇鏂囦欢鍐呭
+ byte[] fileContent = Files.readAllBytes(path);
+
+ // 璁剧疆鍝嶅簲澶�
+
+ if (firmwarePath.contains(".")) {
+ String extension = firmwarePath.substring(firmwarePath.lastIndexOf("."));
+ originalFilename += extension;
+ }
+
+ // 娓呯悊鏂囦欢鍚嶏紝绉婚櫎涓嶅畨鍏ㄥ瓧绗�
+ String safeFilename = originalFilename.replaceAll("[^a-zA-Z0-9._-]", "_");
+
+ logger.info("Providing download for firmware ID: {}, filename: {}, size: {} bytes",
+ id, safeFilename, fileContent.length);
+
+ return ResponseEntity.ok()
+ .contentType(MediaType.APPLICATION_OCTET_STREAM)
+ .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + safeFilename + "\"")
+ .body(fileContent);
+ } catch (IOException e) {
+ logger.error("Error reading firmware file for ID: {}", id, e);
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
+ } catch (Exception e) {
+ logger.error("Unexpected error during firmware download for ID: {}", id, e);
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
+ }
+ }
+
+ @PostMapping("/upload")
+ @Operation(summary = "涓婁紶鍥轰欢鏂囦欢")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<String> uploadFirmware(@RequestParam("file") MultipartFile file) {
+ if (file.isEmpty()) {
+ return new Result<String>().error("涓婁紶鏂囦欢涓嶈兘涓虹┖");
+ }
+
+ // 妫�鏌ユ枃浠舵墿灞曞悕
+ String originalFilename = file.getOriginalFilename();
+ if (originalFilename == null) {
+ return new Result<String>().error("鏂囦欢鍚嶄笉鑳戒负绌�");
+ }
+
+ String extension = originalFilename.substring(originalFilename.lastIndexOf(".")).toLowerCase();
+ if (!extension.equals(".bin") && !extension.equals(".apk")) {
+ return new Result<String>().error("鍙厑璁镐笂浼�.bin鍜�.apk鏍煎紡鐨勬枃浠�");
+ }
+
+ try {
+ // 璁$畻鏂囦欢鐨凪D5鍊�
+ String md5 = calculateMD5(file);
+
+ // 璁剧疆瀛樺偍璺緞
+ String uploadDir = "uploadfile";
+ Path uploadPath = Paths.get(uploadDir);
+
+ // 濡傛灉鐩綍涓嶅瓨鍦紝鍒涘缓鐩綍
+ if (!Files.exists(uploadPath)) {
+ Files.createDirectories(uploadPath);
+ }
+
+ // 浣跨敤MD5浣滀负鏂囦欢鍚嶏紝鍥哄畾浣跨敤.bin鎵╁睍鍚�
+ String uniqueFileName = md5 + extension;
+ Path filePath = uploadPath.resolve(uniqueFileName);
+
+ // 妫�鏌ユ枃浠舵槸鍚﹀凡瀛樺湪
+ if (Files.exists(filePath)) {
+ return new Result<String>().ok(filePath.toString());
+ }
+
+ // 淇濆瓨鏂囦欢
+ Files.copy(file.getInputStream(), filePath);
+
+ // 杩斿洖鏂囦欢璺緞
+ return new Result<String>().ok(filePath.toString());
+ } catch (IOException e) {
+ return new Result<String>().error("鏂囦欢涓婁紶澶辫触锛�" + e.getMessage());
+ }
+ }
+
+ @PostMapping("/uploadAssetsBin")
+ @Operation(summary = "涓婁紶璧勬簮鍥轰欢鏂囦欢")
+ @RequiresPermissions("sys:role:normal")
+ public Result<String> uploadAssetsBin(@RequestParam("file") MultipartFile file) {
+ String otaUrl = sysParamsService.getValue(Constant.SERVER_OTA, true);
+ if (StringUtils.isBlank(otaUrl) || otaUrl.equals("null")) {
+ return new Result<String>().error(ErrorCode.OTA_URL_EMPTY);
+ }
+ logger.info("username:{},uploadAssetsBin size: {}", SecurityUser.getUser().getUsername(), file.getSize());
+ // 楠岃瘉鏂囦欢澶у皬 (璧勬簮鍥轰欢鏈�澶�20MB)
+ if (file.getSize() > 20 * 1024 * 1024) {
+ return new Result<String>().error(ErrorCode.VOICE_CLONE_AUDIO_TOO_LARGE);
+ }
+ // 鏅�氱敤鎴峰彧鑳芥瘡澶╀笂浼�50娆�
+ if (SecurityUser.getUser().getSuperAdmin() == SuperAdminEnum.NO.value()) {
+ String uploadCountKey = RedisKeys.getOtaUploadCountKey(SecurityUser.getUser().getId());
+ Integer uploadCount = (Integer) Optional.ofNullable(redisUtils.get(uploadCountKey)).orElse(0);
+ if (uploadCount >= 50) {
+ return new Result<String>().error(ErrorCode.OTA_UPLOAD_COUNT_EXCEED);
+ }
+ // 澧炲姞涓婁紶娆℃暟
+ redisUtils.increment(RedisKeys.getOtaUploadCountKey(SecurityUser.getUser().getId()),
+ RedisUtils.DEFAULT_EXPIRE);
+ }
+ Result<String> result = uploadFirmware(file);
+
+ // 鐢熸垚璧勬簮鏂囦欢璺緞
+ if (StringUtils.isNotBlank(result.getData())) {
+ String uuid = UUID.randomUUID().toString();
+ redisUtils.set(RedisKeys.getOtaIdKey(uuid), "file:" + result.getData());
+ String downloadUrl = otaUrl.replace("/ota/", "/otaMag/download/") + uuid;
+ result.setData(downloadUrl);
+ }
+ return result;
+ }
+
+ private String calculateMD5(MultipartFile file) throws IOException {
+ return DigestUtil.md5Hex(file.getBytes());
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dao/DeviceAddressBookDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dao/DeviceAddressBookDao.java
new file mode 100644
index 0000000..fc7d642
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dao/DeviceAddressBookDao.java
@@ -0,0 +1,39 @@
+package xiaozhi.modules.device.dao;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+import xiaozhi.modules.device.entity.DeviceAddressBookEntity;
+
+@Mapper
+public interface DeviceAddressBookDao extends BaseMapper<DeviceAddressBookEntity> {
+
+ /**
+ * 鏂板璁惧閫氳褰曡褰�
+ */
+ int insertAddressBook(DeviceAddressBookEntity entity);
+
+ /**
+ * 鑾峰彇璁惧閫氳褰曞垪琛�
+ */
+ List<DeviceAddressBookEntity> getAddressBookList(@Param("macAddress") String macAddress);
+
+ /**
+ * 鏇存柊鍒悕
+ */
+ void updateAlias(@Param("macAddress") String macAddress, @Param("targetMac") String targetMac, @Param("alias") String alias);
+
+ /**
+ * 鏇存柊鏉冮檺
+ */
+ void updatePermission(@Param("macAddress") String macAddress, @Param("targetMac") String targetMac, @Param("hasPermission") Boolean hasPermission);
+
+ /**
+ * 鎵归噺鍒犻櫎璁惧鐩稿叧鐨勯�氳褰曡褰�
+ */
+ void deleteByMacAddresses(@Param("macAddresses") List<String> macAddresses);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dao/DeviceDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dao/DeviceDao.java
new file mode 100644
index 0000000..f3fdb7a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dao/DeviceDao.java
@@ -0,0 +1,21 @@
+package xiaozhi.modules.device.dao;
+
+import java.util.Date;
+
+import org.apache.ibatis.annotations.Mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+import xiaozhi.modules.device.entity.DeviceEntity;
+
+@Mapper
+public interface DeviceDao extends BaseMapper<DeviceEntity> {
+ /**
+ * 鑾峰彇姝ゆ櫤鑳戒綋鍏ㄩ儴璁惧鐨勬渶鍚庤繛鎺ユ椂闂�
+ *
+ * @param agentId 鏅鸿兘浣搃d
+ * @return
+ */
+ Date getAllLastConnectedAtByAgentId(String agentId);
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dao/OtaDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dao/OtaDao.java
new file mode 100644
index 0000000..975f69b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dao/OtaDao.java
@@ -0,0 +1,15 @@
+package xiaozhi.modules.device.dao;
+
+import org.apache.ibatis.annotations.Mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+import xiaozhi.modules.device.entity.OtaEntity;
+
+/**
+ * OTA鍥轰欢绠$悊
+ */
+@Mapper
+public interface OtaDao extends BaseMapper<OtaEntity> {
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceAddressBookAliasDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceAddressBookAliasDTO.java
new file mode 100644
index 0000000..ef1ffe0
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceAddressBookAliasDTO.java
@@ -0,0 +1,21 @@
+package xiaozhi.modules.device.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+@Data
+@Schema(description = "鏇存柊璁惧閫氳褰曞埆鍚�")
+public class DeviceAddressBookAliasDTO {
+
+ @NotBlank(message = "MAC鍦板潃涓嶈兘涓虹┖")
+ @Schema(description = "鏈澶嘙AC鍦板潃")
+ private String macAddress;
+
+ @NotBlank(message = "鐩爣MAC鍦板潃涓嶈兘涓虹┖")
+ @Schema(description = "瀵规柟璁惧MAC鍦板潃")
+ private String targetMac;
+
+ @Schema(description = "鎴戝瀵规柟鐨勭О鍛�")
+ private String alias;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceAddressBookPermissionDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceAddressBookPermissionDTO.java
new file mode 100644
index 0000000..945f108
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceAddressBookPermissionDTO.java
@@ -0,0 +1,21 @@
+package xiaozhi.modules.device.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+@Data
+@Schema(description = "鏇存柊璁惧閫氳褰曟潈闄�")
+public class DeviceAddressBookPermissionDTO {
+
+ @NotBlank(message = "MAC鍦板潃涓嶈兘涓虹┖")
+ @Schema(description = "鏈澶嘙AC鍦板潃")
+ private String macAddress;
+
+ @NotBlank(message = "鐩爣MAC鍦板潃涓嶈兘涓虹┖")
+ @Schema(description = "瀵规柟璁惧MAC鍦板潃")
+ private String targetMac;
+
+ @Schema(description = "鏄惁鏈夋潈闄愬懠鍙�")
+ private Boolean hasPermission;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceManualAddDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceManualAddDTO.java
new file mode 100644
index 0000000..369726b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceManualAddDTO.java
@@ -0,0 +1,11 @@
+package xiaozhi.modules.device.dto;
+
+import lombok.Data;
+
+@Data
+public class DeviceManualAddDTO {
+ private String agentId;
+ private String board; // 璁惧鍨嬪彿
+ private String appVersion; // 鍥轰欢鐗堟湰
+ private String macAddress; // Mac鍦板潃
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DevicePageUserDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DevicePageUserDTO.java
new file mode 100644
index 0000000..26bccbf
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DevicePageUserDTO.java
@@ -0,0 +1,27 @@
+package xiaozhi.modules.device.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.Min;
+import lombok.Data;
+
+/**
+ * 鏌ヨ鎵�鏈夎澶囩殑DTO
+ *
+ * @author zjy
+ * @since 2025-3-21
+ */
+@Data
+@Schema(description = "鏌ヨ鎵�鏈夎澶囩殑DTO")
+public class DevicePageUserDTO {
+
+ @Schema(description = "璁惧鍏抽敭璇�")
+ private String keywords;
+
+ @Schema(description = "椤垫暟")
+ @Min(value = 0, message = "{page.number}")
+ private String page;
+
+ @Schema(description = "鏄剧ず鍒楁暟")
+ @Min(value = 0, message = "{limit.number}")
+ private String limit;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceRegisterDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceRegisterDTO.java
new file mode 100644
index 0000000..ce37c51
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceRegisterDTO.java
@@ -0,0 +1,23 @@
+package xiaozhi.modules.device.dto;
+
+import java.io.Serializable;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 璁惧娉ㄥ唽澶翠俊鎭�
+ *
+ * @author zjy
+ * @since 2025-3-28
+ */
+@Setter
+@Getter
+@Schema(description = "璁惧娉ㄥ唽澶翠俊鎭�")
+public class DeviceRegisterDTO implements Serializable {
+
+ @Schema(description = "mac鍦板潃")
+ private String macAddress;
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceReportReqDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceReportReqDTO.java
new file mode 100644
index 0000000..fec5a29
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceReportReqDTO.java
@@ -0,0 +1,148 @@
+package xiaozhi.modules.device.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Setter
+@Getter
+@Schema(description = "璁惧鍥轰欢淇℃伅涓婃姤姹傝姹備綋")
+public class DeviceReportReqDTO implements Serializable {
+ private static final long serialVersionUID = 1L;
+ // region 瀹炰綋灞炴��
+ @Schema(description = "鏉垮瓙鍥轰欢鐗堟湰鍙�")
+ private Integer version;
+
+ @Schema(description = "闂瓨澶у皬锛堝崟浣嶏細瀛楄妭锛�")
+ @JsonProperty("flash_size")
+ private Integer flashSize;
+
+ @Schema(description = "鏈�灏忕┖闂插爢鍐呭瓨锛堝瓧鑺傦級")
+ @JsonProperty("minimum_free_heap_size")
+ private Integer minimumFreeHeapSize;
+
+ @Schema(description = "璁惧 MAC 鍦板潃")
+ @JsonProperty("mac_address")
+ private String macAddress;
+
+ @Schema(description = "璁惧鍞竴鏍囪瘑 UUID")
+ private String uuid;
+
+ @Schema(description = "鑺墖鍨嬪彿鍚嶇О")
+ @JsonProperty("chip_model_name")
+ private String chipModelName;
+
+ @Schema(description = "鑺墖璇︾粏淇℃伅")
+ @JsonProperty("chip_info")
+ private ChipInfo chipInfo;
+
+ @Schema(description = "搴旂敤绋嬪簭淇℃伅")
+ private Application application;
+
+ @Schema(description = "鍒嗗尯琛ㄥ垪琛�")
+ @JsonProperty("partition_table")
+ private List<Partition> partitionTable;
+
+ @Schema(description = "褰撳墠杩愯鐨� OTA 鍒嗗尯淇℃伅")
+ private OtaInfo ota;
+
+ @Schema(description = "鏉垮瓙閰嶇疆淇℃伅")
+ private BoardInfo board;
+
+ // endregion
+
+ @Getter
+ @Setter
+ @Schema(description = "鑺墖淇℃伅")
+ public static class ChipInfo {
+ @Schema(description = "鑺墖妯″瀷浠g爜")
+ private Integer model;
+
+ @Schema(description = "鏍稿績鏁�")
+ private Integer cores;
+
+ @Schema(description = "纭欢淇鐗堟湰")
+ private Integer revision;
+
+ @Schema(description = "鑺墖鍔熻兘鏍囧織浣�")
+ private Integer features;
+ }
+
+ @Getter
+ @Setter
+ @Schema(description = "鏉垮瓙缂栬瘧淇℃伅")
+ public static class Application {
+ @Schema(description = "鍚嶇О")
+ private String name;
+
+ @Schema(description = "搴旂敤鐗堟湰鍙�")
+ private String version;
+
+ @Schema(description = "缂栬瘧鏃堕棿锛圲TC ISO鏍煎紡锛�")
+ @JsonProperty("compile_time")
+ private String compileTime;
+
+ @Schema(description = "ESP-IDF 鐗堟湰鍙�")
+ @JsonProperty("idf_version")
+ private String idfVersion;
+
+ @Schema(description = "ELF 鏂囦欢 SHA256 鏍¢獙")
+ @JsonProperty("elf_sha256")
+ private String elfSha256;
+ }
+
+ @Getter
+ @Setter
+ @Schema(description = "鍒嗗尯淇℃伅")
+ public static class Partition {
+ @Schema(description = "鍒嗗尯鏍囩鍚�")
+ private String label;
+
+ @Schema(description = "鍒嗗尯绫诲瀷")
+ private Integer type;
+
+ @Schema(description = "瀛愮被鍨�")
+ private Integer subtype;
+
+ @Schema(description = "璧峰鍦板潃")
+ private Integer address;
+
+ @Schema(description = "鍒嗗尯澶у皬")
+ private Integer size;
+ }
+
+ @Getter
+ @Setter
+ @Schema(description = "OTA淇℃伅")
+ public static class OtaInfo {
+ @Schema(description = "褰撳墠OTA鏍囩")
+ private String label;
+ }
+
+ @Getter
+ @Setter
+ @Schema(description = "鏉垮瓙杩炴帴鍜岀綉缁滀俊鎭�")
+ public static class BoardInfo {
+ @Schema(description = "鏉垮瓙绫诲瀷")
+ private String type;
+
+ @Schema(description = "杩炴帴鐨� Wi-Fi SSID")
+ private String ssid;
+
+ @Schema(description = "Wi-Fi 淇″彿寮哄害锛圧SSI锛�")
+ private Integer rssi;
+
+ @Schema(description = "Wi-Fi 淇¢亾")
+ private Integer channel;
+
+ @Schema(description = "IP 鍦板潃")
+ private String ip;
+
+ @Schema(description = "MAC 鍦板潃")
+ private String mac;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceReportRespDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceReportRespDTO.java
new file mode 100644
index 0000000..85c8972
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceReportRespDTO.java
@@ -0,0 +1,95 @@
+package xiaozhi.modules.device.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+
+@Data
+@Schema(description = "璁惧OTA妫�娴嬬増鏈繑鍥炰綋锛屽寘鍚縺娲荤爜瑕佹眰")
+public class DeviceReportRespDTO {
+ @Schema(description = "鏈嶅姟鍣ㄦ椂闂�")
+ private ServerTime server_time;
+
+ @Schema(description = "婵�娲荤爜")
+ private Activation activation;
+
+ @Schema(description = "閿欒淇℃伅")
+ private String error;
+
+ @Schema(description = "鍥轰欢鐗堟湰淇℃伅")
+ private Firmware firmware;
+
+ @Schema(description = "WebSocket閰嶇疆")
+ private Websocket websocket;
+
+ @Schema(description = "MQTT Gateway閰嶇疆")
+ private MQTT mqtt;
+
+ @Getter
+ @Setter
+ public static class Firmware {
+ @Schema(description = "鐗堟湰鍙�")
+ private String version;
+ @Schema(description = "涓嬭浇鍦板潃")
+ private String url;
+ }
+
+ public static DeviceReportRespDTO createError(String message) {
+ DeviceReportRespDTO resp = new DeviceReportRespDTO();
+ resp.setError(message);
+ return resp;
+ }
+
+ @Setter
+ @Getter
+ public static class Activation {
+ @Schema(description = "婵�娲荤爜")
+ private String code;
+
+ @Schema(description = "婵�娲荤爜淇℃伅: 婵�娲诲湴鍧�")
+ private String message;
+
+ @Schema(description = "鎸戞垬鐮�")
+ private String challenge;
+ }
+
+ @Getter
+ @Setter
+ public static class ServerTime {
+ @Schema(description = "鏃堕棿鎴�")
+ private Long timestamp;
+
+ @Schema(description = "鏃跺尯")
+ private String timeZone;
+
+ @Schema(description = "鏃跺尯鍋忕Щ閲忥紝鍗曚綅涓哄垎閽�")
+ private Integer timezone_offset;
+ }
+
+ @Getter
+ @Setter
+ public static class Websocket {
+ @Schema(description = "WebSocket鏈嶅姟鍣ㄥ湴鍧�")
+ private String url;
+ @Schema(description = "WebSocket 璁よ瘉 token")
+ private String token;
+ }
+
+ @Getter
+ @Setter
+ public static class MQTT {
+ @Schema(description = "MQTT 閰嶇疆缃戝潃")
+ private String endpoint;
+ @Schema(description = "MQTT 瀹㈡埛绔敮涓�鏍囪瘑绗�")
+ private String client_id;
+ @Schema(description = "MQTT 璁よ瘉鐢ㄦ埛鍚�")
+ private String username;
+ @Schema(description = "MQTT 璁よ瘉瀵嗙爜")
+ private String password;
+ @Schema(description = "ESP32 鍙戝竷娑堟伅鐨勪富棰�")
+ private String publish_topic;
+ @Schema(description = "ESP32 璁㈤槄鐨勪富棰�")
+ private String subscribe_topic;
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceToolsCallReqDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceToolsCallReqDTO.java
new file mode 100644
index 0000000..777bee0
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceToolsCallReqDTO.java
@@ -0,0 +1,15 @@
+package xiaozhi.modules.device.dto;
+
+import java.util.Map;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+@Data
+public class DeviceToolsCallReqDTO {
+
+ @NotBlank(message = "宸ュ叿鍚嶇О涓嶈兘涓虹┖")
+ private String name;
+
+ private Map<String, Object> arguments;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceUnBindDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceUnBindDTO.java
new file mode 100644
index 0000000..f18df5d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceUnBindDTO.java
@@ -0,0 +1,20 @@
+package xiaozhi.modules.device.dto;
+
+import java.io.Serializable;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+/**
+ * 璁惧瑙g粦琛ㄥ崟
+ */
+@Data
+@Schema(description = "璁惧瑙g粦琛ㄥ崟")
+public class DeviceUnBindDTO implements Serializable {
+
+ @Schema(description = "璁惧ID")
+ @NotBlank(message = "璁惧ID涓嶈兘涓虹┖")
+ private String deviceId;
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceUpdateDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceUpdateDTO.java
new file mode 100644
index 0000000..2e9a479
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceUpdateDTO.java
@@ -0,0 +1,29 @@
+package xiaozhi.modules.device.dto;
+
+import jakarta.validation.constraints.Max;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 璁惧鏇存柊DTO
+ */
+@Data
+public class DeviceUpdateDTO implements Serializable {
+ /**
+ * 鑷姩鏇存柊鐘舵��
+ */
+ @Max(1)
+ @Min(0)
+ private Integer autoUpdate;
+
+ /**
+ * 璁惧鍒悕
+ */
+ @Size(max = 64)
+ private String alias;
+
+ private static final long serialVersionUID = 1L;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/entity/DeviceAddressBookEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/entity/DeviceAddressBookEntity.java
new file mode 100644
index 0000000..7c8bf00
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/entity/DeviceAddressBookEntity.java
@@ -0,0 +1,49 @@
+package xiaozhi.modules.device.entity;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ai_device_address_book")
+@Schema(description = "璁惧閫氳褰�")
+public class DeviceAddressBookEntity {
+
+ @TableId(type = IdType.INPUT)
+ @Schema(description = "鏈澶嘙AC鍦板潃")
+ private String macAddress;
+
+ @Schema(description = "瀵规柟璁惧MAC鍦板潃")
+ private String targetMac;
+
+ @Schema(description = "鎴戝瀵规柟鐨勭О鍛�")
+ private String alias;
+
+ @Schema(description = "鏄惁鏈夋潈闄愬懠鍙�")
+ private Boolean hasPermission;
+
+ @TableField(fill = FieldFill.INSERT)
+ @Schema(description = "鍒涘缓浜�")
+ private Long creator;
+
+ @TableField(fill = FieldFill.INSERT)
+ @Schema(description = "鍒涘缓鏃堕棿")
+ private Date createDate;
+
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ @Schema(description = "鏇存柊浜�")
+ private Long updater;
+
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ @Schema(description = "鏇存柊鏃堕棿")
+ private Date updateDate;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/entity/DeviceEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/entity/DeviceEntity.java
new file mode 100644
index 0000000..8ebdf93
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/entity/DeviceEntity.java
@@ -0,0 +1,67 @@
+package xiaozhi.modules.device.entity;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ai_device")
+@Schema(description = "璁惧淇℃伅")
+public class DeviceEntity {
+
+ @TableId(type = IdType.ASSIGN_UUID)
+ @Schema(description = "ID")
+ private String id;
+
+ @Schema(description = "鍏宠仈鐢ㄦ埛ID")
+ private Long userId;
+
+ @Schema(description = "MAC鍦板潃")
+ private String macAddress;
+
+ @Schema(description = "鏈�鍚庤繛鎺ユ椂闂�")
+ private Date lastConnectedAt;
+
+ @Schema(description = "鑷姩鏇存柊寮�鍏�(0鍏抽棴/1寮�鍚�)")
+ private Integer autoUpdate;
+
+ @Schema(description = "璁惧纭欢鍨嬪彿")
+ private String board;
+
+ @Schema(description = "璁惧鍒悕")
+ private String alias;
+
+ @Schema(description = "鏅鸿兘浣揑D")
+ private String agentId;
+
+ @Schema(description = "鍥轰欢鐗堟湰鍙�")
+ private String appVersion;
+
+ @Schema(description = "鎺掑簭")
+ private Integer sort;
+
+ @Schema(description = "鏇存柊鑰�")
+ @TableField(fill = FieldFill.UPDATE)
+ private Long updater;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ @TableField(fill = FieldFill.UPDATE)
+ private Date updateDate;
+
+ @Schema(description = "鍒涘缓鑰�")
+ @TableField(fill = FieldFill.INSERT)
+ private Long creator;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ @TableField(fill = FieldFill.INSERT)
+ private Date createDate;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/entity/OtaEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/entity/OtaEntity.java
new file mode 100644
index 0000000..b93c608
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/entity/OtaEntity.java
@@ -0,0 +1,61 @@
+package xiaozhi.modules.device.entity;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ai_ota")
+@Schema(description = "鍥轰欢淇℃伅")
+public class OtaEntity {
+
+ @TableId(type = IdType.ASSIGN_UUID)
+ @Schema(description = "ID")
+ private String id;
+
+ @Schema(description = "鍥轰欢鍚嶇О")
+ private String firmwareName;
+
+ @Schema(description = "鍥轰欢绫诲瀷")
+ private String type;
+
+ @Schema(description = "鐗堟湰鍙�")
+ private String version;
+
+ @Schema(description = "鏂囦欢澶у皬(瀛楄妭)")
+ private Long size;
+
+ @Schema(description = "澶囨敞/璇存槑")
+ private String remark;
+
+ @Schema(description = "鍥轰欢璺緞")
+ private String firmwarePath;
+
+ @Schema(description = "鎺掑簭")
+ private Integer sort;
+
+ @Schema(description = "鏇存柊鑰�")
+ @TableField(fill = FieldFill.UPDATE)
+ private Long updater;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ @TableField(fill = FieldFill.UPDATE)
+ private Date updateDate;
+
+ @Schema(description = "鍒涘缓鑰�")
+ @TableField(fill = FieldFill.INSERT)
+ private Long creator;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ @TableField(fill = FieldFill.INSERT)
+ private Date createDate;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/DeviceAddressBookService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/DeviceAddressBookService.java
new file mode 100644
index 0000000..d0b3839
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/DeviceAddressBookService.java
@@ -0,0 +1,52 @@
+package xiaozhi.modules.device.service;
+
+import java.util.List;
+import java.util.Map;
+
+import xiaozhi.modules.device.entity.DeviceAddressBookEntity;
+
+public interface DeviceAddressBookService {
+
+ /**
+ * 鑾峰彇璁惧閫氳褰曞垪琛�
+ */
+ List<DeviceAddressBookEntity> getAddressBookList(String macAddress);
+
+ /**
+ * 鑾峰彇鎵�鏈夎澶囩殑閫氳褰曪紙鍏ㄥ眬缂撳瓨鐢級
+ */
+ Map<String, Map<String, String>> getAllAddressBooks();
+
+ /**
+ * 鏇存柊鍒悕
+ */
+ void updateAlias(String macAddress, String targetMac, String alias);
+
+ /**
+ * 鏇存柊鏉冮檺
+ */
+ void updatePermission(String macAddress, String targetMac, Boolean hasPermission);
+
+ /**
+ * 娣诲姞鎴栨洿鏂伴�氳褰曡褰�
+ */
+ void saveOrUpdate(String macAddress, String targetMac, String alias, Boolean hasPermission);
+
+ /**
+ * 鍒锋柊閫氳褰曠紦瀛�
+ */
+ void refreshCache();
+
+ /**
+ * 鏍规嵁鏄电О鍙戣捣鍛煎彨
+ * @param callerMac 涓诲彨鏂筂AC鍦板潃
+ * @param nickname 琚彨鏂规樀绉�
+ * @param isAnswer 鏄惁涓烘帴鍚ā寮忥紙璺宠繃鏉冮檺妫�鏌ワ級
+ */
+ Map<String, Object> callByNickname(String callerMac, String nickname, boolean isAnswer);
+
+ /**
+ * 鎵归噺鍒犻櫎璁惧鐩稿叧鐨勯�氳褰曡褰�
+ */
+ void deleteByMacAddresses(List<String> macAddresses);
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/DeviceService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/DeviceService.java
new file mode 100644
index 0000000..b663ceb
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/DeviceService.java
@@ -0,0 +1,141 @@
+package xiaozhi.modules.device.service;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.service.BaseService;
+import xiaozhi.modules.device.dto.DeviceManualAddDTO;
+import xiaozhi.modules.device.dto.DevicePageUserDTO;
+import xiaozhi.modules.device.dto.DeviceReportReqDTO;
+import xiaozhi.modules.device.dto.DeviceReportRespDTO;
+import xiaozhi.modules.device.entity.DeviceEntity;
+import xiaozhi.modules.device.vo.UserShowDeviceListVO;
+
+public interface DeviceService extends BaseService<DeviceEntity> {
+ /**
+ * 鑾峰彇璁惧鍦ㄧ嚎鏁版嵁
+ */
+ String getDeviceOnlineData(String agentId);
+
+ /**
+ * 妫�鏌ヨ澶囨槸鍚︽縺娲�
+ */
+ DeviceReportRespDTO checkDeviceActive(String macAddress, String clientId,
+ DeviceReportReqDTO deviceReport);
+
+ /**
+ * 鑾峰彇鐢ㄦ埛鎸囧畾鏅鸿兘浣撶殑璁惧鍒楄〃锛�
+ */
+ List<DeviceEntity> getUserDevices(Long userId, String agentId);
+
+ /**
+ * 鑾峰彇鐢ㄦ埛鎸囧畾鏅鸿兘浣撶殑璁惧鍒楄〃锛堝甫鏃跺尯澶勭悊锛夛紝
+ */
+ List<UserShowDeviceListVO> getUserDeviceList(Long userId, String agentId);
+
+ /**
+ * 瑙g粦璁惧
+ */
+ void unbindDevice(Long userId, String deviceId);
+
+ /**
+ * 璁惧婵�娲�
+ */
+ Boolean deviceActivation(String agentId, String activationCode);
+
+ /**
+ * 鍒犻櫎姝ょ敤鎴风殑鎵�鏈夎澶�
+ *
+ * @param userId 鐢ㄦ埛id
+ */
+ void deleteByUserId(Long userId);
+
+ /**
+ * 鍒犻櫎鎸囧畾鏅鸿兘浣撳叧鑱旂殑鎵�鏈夎澶�
+ *
+ * @param agentId 鏅鸿兘浣搃d
+ */
+ void deleteByAgentId(String agentId);
+
+ /**
+ * 鑾峰彇鎸囧畾鐢ㄦ埛鐨勮澶囨暟閲�
+ *
+ * @param userId 鐢ㄦ埛id
+ * @return 璁惧鏁伴噺
+ */
+ Long selectCountByUserId(Long userId);
+
+ /**
+ * 鍒嗛〉鑾峰彇鍏ㄩ儴璁惧淇℃伅
+ *
+ * @param dto 鍒嗛〉鏌ユ壘鍙傛暟
+ * @return 鐢ㄦ埛鍒楄〃鍒嗛〉鏁版嵁
+ */
+ PageData<UserShowDeviceListVO> page(DevicePageUserDTO dto);
+
+ /**
+ * 鏍规嵁MAC鍦板潃鑾峰彇璁惧淇℃伅
+ *
+ * @param macAddress MAC鍦板潃
+ * @return 璁惧淇℃伅
+ */
+ DeviceEntity getDeviceByMacAddress(String macAddress);
+
+ /**
+ * 鏍规嵁璁惧ID鑾峰彇婵�娲荤爜
+ *
+ * @param deviceId 璁惧ID
+ * @return 婵�娲荤爜
+ */
+ String geCodeByDeviceId(String deviceId);
+
+ /**
+ * 鑾峰彇杩欎釜鏅鸿兘浣撹澶囩悊鐨勬渶杩戠殑鏈�鍚庤繛鎺ユ椂闂�
+ *
+ * @param agentId 鏅鸿兘浣搃d
+ * @return 杩斿洖璁惧鏈�杩戠殑鏈�鍚庤繛鎺ユ椂闂�
+ */
+ Date getLatestLastConnectionTime(String agentId);
+
+ /**
+ * 鎵嬪姩娣诲姞璁惧
+ */
+ void manualAddDevice(Long userId, DeviceManualAddDTO dto);
+
+ /**
+ * 鏇存柊璁惧杩炴帴淇℃伅
+ */
+ void updateDeviceConnectionInfo(String agentId, String deviceId, String appVersion);
+
+ /**
+ * 鐢熸垚WebSocket璁よ瘉token
+ *
+ * @param clientId 瀹㈡埛绔疘D
+ * @param username 鐢ㄦ埛鍚�(閫氬父涓篸eviceId)
+ * @return 璁よ瘉token瀛楃涓�
+ * @throws Exception 鐢熸垚token鏃剁殑寮傚父
+ */
+ String generateWebSocketToken(String clientId, String username) throws Exception;
+
+ /**
+ * 鏍规嵁MAC鍦板潃鎼滅储璁惧
+ *
+ * @param macAddress MAC鍦板潃鍏抽敭璇�
+ * @param userId 鐢ㄦ埛ID
+ * @return 璁惧鍒楄〃
+ */
+ List<DeviceEntity> searchDevicesByMacAddress(String macAddress, Long userId);
+
+ /**
+ * 鑾峰彇璁惧宸ュ叿鍒楄〃
+ */
+ Object getDeviceTools(String deviceId);
+
+ /**
+ * 璋冪敤璁惧宸ュ叿
+ */
+ Object callDeviceTool(String deviceId, String toolName, Map<String, Object> arguments);
+
+ }
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/OtaService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/OtaService.java
new file mode 100644
index 0000000..cd35f6a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/OtaService.java
@@ -0,0 +1,22 @@
+package xiaozhi.modules.device.service;
+
+import java.util.Map;
+
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.service.BaseService;
+import xiaozhi.modules.device.entity.OtaEntity;
+
+/**
+ * OTA鍥轰欢绠$悊
+ */
+public interface OtaService extends BaseService<OtaEntity> {
+ PageData<OtaEntity> page(Map<String, Object> params);
+
+ boolean save(OtaEntity entity);
+
+ void update(OtaEntity entity);
+
+ void delete(String[] ids);
+
+ OtaEntity getLatestOta(String type);
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceAddressBookServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceAddressBookServiceImpl.java
new file mode 100644
index 0000000..ff2e431
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceAddressBookServiceImpl.java
@@ -0,0 +1,258 @@
+package xiaozhi.modules.device.service.impl;
+
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+
+import cn.hutool.json.JSONUtil;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.redis.RedisKeys;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.modules.device.dao.DeviceAddressBookDao;
+import xiaozhi.modules.device.entity.DeviceAddressBookEntity;
+import xiaozhi.modules.device.service.DeviceAddressBookService;
+import xiaozhi.modules.device.service.DeviceService;
+import xiaozhi.modules.sys.service.SysParamsService;
+
+@Service
+public class DeviceAddressBookServiceImpl implements DeviceAddressBookService {
+
+ private final DeviceAddressBookDao deviceAddressBookDao;
+ private final RedisUtils redisUtils;
+ private final DeviceService deviceService;
+ private final SysParamsService sysParamsService;
+
+ public DeviceAddressBookServiceImpl(DeviceAddressBookDao deviceAddressBookDao, RedisUtils redisUtils,
+ @Lazy DeviceService deviceService, SysParamsService sysParamsService) {
+ this.deviceAddressBookDao = deviceAddressBookDao;
+ this.redisUtils = redisUtils;
+ this.deviceService = deviceService;
+ this.sysParamsService = sysParamsService;
+ }
+
+ @Override
+ public List<DeviceAddressBookEntity> getAddressBookList(String macAddress) {
+ return deviceAddressBookDao.getAddressBookList(macAddress);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Map<String, Map<String, String>> getAllAddressBooks() {
+ Object cached = redisUtils.get(RedisKeys.getAddressBookKey());
+ if (cached != null) {
+ return (Map<String, Map<String, String>>) cached;
+ }
+ refreshCache();
+ return (Map<String, Map<String, String>>) redisUtils.get(RedisKeys.getAddressBookKey());
+ }
+
+ @Override
+ public Map<String, Object> callByNickname(String callerMac, String nickname, boolean isAnswer) {
+ Map<String, Map<String, String>> allBooks = getAllAddressBooks();
+
+ if (isAnswer) {
+ return postToMqtt("/api/call/accept", Map.of("mac", callerMac), "鎺ュ惉");
+ }
+
+ // 涓诲姩鍛煎彨妯″紡
+ Map<String, String> callerBook = allBooks.get(callerMac.toLowerCase());
+ if (callerBook == null) {
+ return errorResult("鏈壘鍒板娉ㄤ负'" + nickname + "'鐨勮澶�");
+ }
+ String targetMacWithPerm = callerBook.get(nickname);
+ if (targetMacWithPerm == null) {
+ return errorResult("鏈壘鍒板娉ㄤ负'" + nickname + "'鐨勮澶�");
+ }
+ String[] parts = targetMacWithPerm.split("\\|");
+ String targetMac = parts[0];
+ boolean hasPermission = parts.length > 1 && "1".equals(parts[1]);
+
+ if (!hasPermission) {
+ return errorResult("鍛煎彨澶辫触锛屾偍娌℃湁鏉冮檺鍛煎彨璇ヨ澶�");
+ }
+
+ // 鑾峰彇鐩爣璁惧濡備綍绉板懠涓诲彨鏂�
+ Map<String, String> targetBook = allBooks.get(targetMac.toLowerCase());
+ String callerNickname = null;
+ if (targetBook != null) {
+ callerNickname = targetBook.get(callerMac.toLowerCase());
+ }
+ if (StringUtils.isBlank(callerNickname)) {
+ callerNickname = deviceService.getDeviceByMacAddress(callerMac).getAlias();
+ if (StringUtils.isBlank(callerNickname)) {
+ callerNickname = formatMacAsDeviceName(callerMac);
+ }
+ }
+
+ return postToMqtt("/api/call/request",
+ Map.of("caller_mac", callerMac, "target_mac", targetMac, "caller_nickname", callerNickname),
+ "鍛煎彨");
+ }
+
+ @Override
+ public void refreshCache() {
+ Map<String, Map<String, String>> result = new HashMap<>();
+ List<DeviceAddressBookEntity> allRecords = deviceAddressBookDao.selectList(null);
+ Map<String, String> reverseMap = new HashMap<>();
+
+ for (DeviceAddressBookEntity entity : allRecords) {
+ String macA = entity.getMacAddress().toLowerCase();
+ String macB = entity.getTargetMac().toLowerCase();
+ String alias = entity.getAlias();
+ Boolean hasPermission = entity.getHasPermission();
+
+ // 鏋勫缓姝e悜鏄犲皠: A瀵笲鐨勬槧灏� nickname -> macB|permission
+ if (alias != null && !alias.isEmpty()) {
+ result.computeIfAbsent(macA, k -> new HashMap<>());
+ String permStr = (hasPermission != null && hasPermission) ? "1" : "0";
+ result.get(macA).put(alias, macB + "|" + permStr);
+ }
+
+ // 鏋勫缓鍙嶅悜璁板綍鐢ㄤ簬鏌ユ壘B瀵笰鐨勭О鍛�
+ if (alias != null && !alias.isEmpty()) {
+ reverseMap.put(macB + ":" + macA, alias);
+ }
+ }
+
+ // 鏋勫缓鍙嶅悜鏄犲皠: B瀵笰鐨勬槧灏� macA -> nickname
+ for (DeviceAddressBookEntity entity : allRecords) {
+ String macA = entity.getMacAddress().toLowerCase();
+ String macB = entity.getTargetMac().toLowerCase();
+ String aliasBtoA = reverseMap.get(macA + ":" + macB);
+ if (aliasBtoA != null && !aliasBtoA.isEmpty()) {
+ result.computeIfAbsent(macB, k -> new HashMap<>());
+ result.get(macB).put(macA, aliasBtoA);
+ }
+ }
+
+ redisUtils.set(RedisKeys.getAddressBookKey(), result);
+ }
+
+ @Override
+ public void updateAlias(String macAddress, String targetMac, String alias) {
+ String finalAlias = generateUniqueAlias(macAddress, targetMac, alias);
+ deviceAddressBookDao.updateAlias(macAddress, targetMac, finalAlias);
+ refreshCache();
+ }
+
+ @Override
+ public void updatePermission(String macAddress, String targetMac, Boolean hasPermission) {
+ deviceAddressBookDao.updatePermission(macAddress, targetMac, hasPermission);
+ refreshCache();
+ }
+
+ @Override
+ public void saveOrUpdate(String macAddress, String targetMac, String alias, Boolean hasPermission) {
+ QueryWrapper<DeviceAddressBookEntity> wrapper = new QueryWrapper<>();
+ wrapper.eq("mac_address", macAddress).eq("target_mac", targetMac);
+ DeviceAddressBookEntity record = deviceAddressBookDao.selectOne(wrapper);
+ System.out.println("saveOrUpdate - mac=" + macAddress + ", targetMac=" + targetMac + ", alias=" + alias + ", record=" + (record == null ? "null" : "not null"));
+ if (record == null) {
+ DeviceAddressBookEntity entity = new DeviceAddressBookEntity();
+ entity.setMacAddress(macAddress);
+ entity.setTargetMac(targetMac);
+ // 濡傛灉 alias 涓虹┖锛屽氨榛樿浣跨敤璁惧鍚嶇О
+ if (StringUtils.isBlank(alias)) {
+ alias = deviceService.getDeviceByMacAddress(targetMac).getAlias();
+ }
+ // 妫�鏌ラ噸鍚�
+ alias = generateUniqueAlias(macAddress, targetMac, alias);
+ entity.setAlias(alias);
+ entity.setHasPermission(hasPermission);
+ deviceAddressBookDao.insertAddressBook(entity);
+ } else {
+ if (alias != null) {
+ updateAlias(macAddress, targetMac, alias);
+ }
+ if (hasPermission != null) {
+ deviceAddressBookDao.updatePermission(macAddress, targetMac, hasPermission);
+ }
+ }
+ refreshCache();
+ }
+
+ @Override
+ public void deleteByMacAddresses(List<String> macAddresses) {
+ if (macAddresses == null || macAddresses.isEmpty()) {
+ return;
+ }
+ deviceAddressBookDao.deleteByMacAddresses(macAddresses);
+ refreshCache();
+ }
+
+ private Map<String, Object> errorResult(String message) {
+ Map<String, Object> result = new HashMap<>();
+ result.put("status", "error");
+ result.put("message", message);
+ return result;
+ }
+
+ private Map<String, Object> postToMqtt(String path, Map<String, Object> body, String action) {
+ Map<String, Object> result = new HashMap<>();
+ result.put("status", "error");
+
+ String mqttGatewayUrl = sysParamsService.getValue("server.mqtt_manager_api", true);
+ String mqttSignatureKey = sysParamsService.getValue(Constant.SERVER_MQTT_SECRET, true);
+
+ if (StringUtils.isBlank(mqttGatewayUrl) || "null".equals(mqttGatewayUrl)
+ || MqttGatewayAuthorization.isMissingSignatureKey(mqttSignatureKey)) {
+ result.put("message", action + "澶辫触锛岀綉鍏抽厤缃己澶�");
+ return result;
+ }
+
+ try {
+ String url = "http://" + mqttGatewayUrl + path;
+ String response = MqttGatewayAuthorization.postJson(
+ url,
+ JSONUtil.toJsonStr(body),
+ mqttSignatureKey,
+ Instant.now(),
+ 5000);
+
+ if (StringUtils.isNotBlank(response)) {
+ Map<String, Object> gwResult = JSONUtil.parseObj(response);
+ result.put("status", gwResult.get("status"));
+ result.put("message", gwResult.get("message"));
+ }
+ return result;
+ } catch (Exception e) {
+ result.put("message", action + "澶辫触锛岃绋嶅悗鍐嶈瘯");
+ return result;
+ }
+ }
+
+ private String formatMacAsDeviceName(String mac) {
+ if (StringUtils.isBlank(mac) || mac.length() < 2) {
+ return mac;
+ }
+ String lastTwo = mac.substring(mac.length() - 2);
+ return "灏惧彿涓�" + lastTwo + "鐨勮澶�";
+ }
+
+ private String generateUniqueAlias(String macAddress, String targetMac, String alias) {
+ QueryWrapper<DeviceAddressBookEntity> wrapper = new QueryWrapper<>();
+ wrapper.eq("mac_address", macAddress);
+ List<DeviceAddressBookEntity> existing = deviceAddressBookDao.selectList(wrapper);
+ List<String> existNames = existing.stream()
+ .map(DeviceAddressBookEntity::getAlias)
+ .filter(a -> a != null && !a.isEmpty())
+ .collect(Collectors.toList());
+ if (!existNames.contains(alias)) {
+ return alias;
+ }
+ int suffix = 1;
+ String newAlias;
+ while (existNames.contains(newAlias = alias + suffix)) {
+ suffix++;
+ }
+ return newAlias;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceServiceImpl.java
new file mode 100644
index 0000000..5acde87
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceServiceImpl.java
@@ -0,0 +1,884 @@
+package xiaozhi.modules.device.service.impl;
+
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.TimeZone;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.aop.framework.AopContext;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.redis.RedisKeys;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.common.service.impl.BaseServiceImpl;
+import xiaozhi.common.user.UserDetail;
+import xiaozhi.common.utils.ConvertUtils;
+import xiaozhi.common.utils.DateUtils;
+import xiaozhi.common.utils.JsonUtils;
+import xiaozhi.modules.device.dao.DeviceDao;
+import xiaozhi.modules.device.dto.DeviceManualAddDTO;
+import xiaozhi.modules.device.dto.DevicePageUserDTO;
+import xiaozhi.modules.device.dto.DeviceReportReqDTO;
+import xiaozhi.modules.device.dto.DeviceReportRespDTO;
+import xiaozhi.modules.device.entity.DeviceEntity;
+import xiaozhi.modules.device.entity.OtaEntity;
+import xiaozhi.modules.device.service.DeviceAddressBookService;
+import xiaozhi.modules.device.service.DeviceService;
+import xiaozhi.modules.device.service.OtaService;
+import xiaozhi.modules.device.vo.UserShowDeviceListVO;
+import xiaozhi.modules.security.user.SecurityUser;
+import xiaozhi.modules.sys.service.SysParamsService;
+import xiaozhi.modules.sys.service.SysUserUtilService;
+
+@Slf4j
+@Service
+@AllArgsConstructor
+public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity> implements DeviceService {
+
+ private final DeviceDao deviceDao;
+ private final SysUserUtilService sysUserUtilService;
+ private final SysParamsService sysParamsService;
+ private final RedisUtils redisUtils;
+ private final OtaService otaService;
+ private final DeviceAddressBookService deviceAddressBookService;
+
+ @Async
+ public void updateDeviceConnectionInfo(String agentId, String deviceId, String appVersion) {
+ try {
+ DeviceEntity device = new DeviceEntity();
+ device.setId(deviceId);
+ device.setLastConnectedAt(new Date());
+ if (StringUtils.isNotBlank(appVersion)) {
+ device.setAppVersion(appVersion);
+ }
+ deviceDao.updateById(device);
+ if (StringUtils.isNotBlank(agentId)) {
+ redisUtils.set(RedisKeys.getAgentDeviceLastConnectedAtById(agentId), new Date());
+ }
+ } catch (Exception e) {
+ log.error("寮傛鏇存柊璁惧杩炴帴淇℃伅澶辫触", e);
+ }
+ }
+
+ @Override
+ public Boolean deviceActivation(String agentId, String activationCode) {
+ if (StringUtils.isBlank(activationCode)) {
+ throw new RenException(ErrorCode.ACTIVATION_CODE_EMPTY);
+ }
+ String deviceKey = RedisKeys.getOtaActivationCode(activationCode);
+ String cacheDeviceId = (String) redisUtils.get(deviceKey);
+ if (StringUtils.isBlank(cacheDeviceId)) {
+ throw new RenException(ErrorCode.ACTIVATION_CODE_ERROR);
+ }
+ String deviceId = cacheDeviceId;
+ String safeDeviceId = deviceId.replace(":", "_").toLowerCase();
+ String cacheDeviceKey = RedisKeys.getOtaDeviceActivationInfo(safeDeviceId);
+ Map<String, Object> cacheMap = JsonUtils.toStringObjectMap(redisUtils.get(cacheDeviceKey));
+ if (MapUtil.isEmpty(cacheMap)) {
+ throw new RenException(ErrorCode.ACTIVATION_CODE_ERROR);
+ }
+ String cachedCode = (String) cacheMap.get("activation_code");
+ if (!activationCode.equals(cachedCode)) {
+ throw new RenException(ErrorCode.ACTIVATION_CODE_ERROR);
+ }
+ // 妫�鏌ヨ澶囨湁娌℃湁琚縺娲�
+ if (selectById(deviceId) != null) {
+ throw new RenException(ErrorCode.DEVICE_ALREADY_ACTIVATED);
+ }
+
+ String macAddress = (String) cacheMap.get("mac_address");
+ String board = (String) cacheMap.get("board");
+ String appVersion = (String) cacheMap.get("app_version");
+ UserDetail user = SecurityUser.getUser();
+ if (user.getId() == null) {
+ throw new RenException(ErrorCode.USER_NOT_LOGIN);
+ }
+
+ Date currentTime = new Date();
+ DeviceEntity deviceEntity = new DeviceEntity();
+ deviceEntity.setId(deviceId);
+ deviceEntity.setBoard(board);
+ deviceEntity.setAgentId(agentId);
+ deviceEntity.setAppVersion(appVersion);
+ deviceEntity.setMacAddress(macAddress);
+ deviceEntity.setUserId(user.getId());
+ deviceEntity.setCreator(user.getId());
+ deviceEntity.setAutoUpdate(1);
+ deviceEntity.setCreateDate(currentTime);
+ deviceEntity.setUpdater(user.getId());
+ deviceEntity.setUpdateDate(currentTime);
+ deviceEntity.setLastConnectedAt(currentTime);
+ deviceDao.insert(deviceEntity);
+
+ // 娓呯悊redis缂撳瓨銆佹竻闄ゆ櫤鑳戒綋璁惧鏁伴噺缂撳瓨
+ redisUtils.delete(List.of(cacheDeviceKey, deviceKey, RedisKeys.getAgentDeviceCountById(agentId)));
+ return true;
+ }
+
+ /**
+ * 鑾峰彇璁惧鍦ㄧ嚎鏁版嵁
+ */
+ @Override
+ public String getDeviceOnlineData(String agentId) {
+ // 浠庣郴缁熷弬鏁颁腑鑾峰彇MQTT缃戝叧鍦板潃
+ String mqttGatewayUrl = sysParamsService.getValue("server.mqtt_manager_api", true);
+ if (StringUtils.isBlank(mqttGatewayUrl) || "null".equals(mqttGatewayUrl)) {
+ return "";
+ }
+ // 鏋勫缓瀹屾暣鐨刄RL
+ String url = StrUtil.format("http://{}/api/devices/status", mqttGatewayUrl);
+
+ // 鑾峰彇褰撳墠鐢ㄦ埛鐨勮澶囧垪琛�
+ UserDetail user = SecurityUser.getUser();
+ List<DeviceEntity> devices = getUserDevices(user.getId(), agentId);
+
+ // 鏋勫缓deviceIds鏁扮粍
+ Set<String> deviceIds = devices.stream().map(o -> {
+ String macAddress = Optional.ofNullable(o.getMacAddress()).orElse("unknown").replace(":", "_");
+ String groupId = Optional.ofNullable(o.getBoard()).orElse("GID_default").replace(":", "_");
+ return StrUtil.format("{}@@@{}@@@{}", groupId, macAddress, macAddress);
+ }).collect(Collectors.toSet());
+
+ // 鏋勫缓璇锋眰鍏ュ弬
+ Map<String, Set<String>> params = MapUtil
+ .builder(new HashMap<String, Set<String>>())
+ .put("clientIds", deviceIds).build();
+
+ if (CollUtil.isNotEmpty(deviceIds)) {
+ return postToMqttGateway(url, params);
+ }
+ // 杩斿洖鍝嶅簲
+ return "";
+ }
+
+ @Override
+ public DeviceReportRespDTO checkDeviceActive(String macAddress, String clientId, DeviceReportReqDTO deviceReport) {
+ DeviceReportRespDTO response = new DeviceReportRespDTO();
+ response.setServer_time(buildServerTime());
+
+ DeviceEntity deviceById = getDeviceByMacAddress(macAddress);
+
+ // 璁惧鏈粦瀹氾紝鍒欒繑鍥炲綋鍓嶄笂浼犵殑鍥轰欢淇℃伅锛堜笉鏇存柊锛変互姝ゅ吋瀹规棫鍥轰欢鐗堟湰
+ if (deviceById == null) {
+ DeviceReportRespDTO.Firmware firmware = new DeviceReportRespDTO.Firmware();
+ firmware.setVersion(deviceReport.getApplication().getVersion());
+ firmware.setUrl(Constant.INVALID_FIRMWARE_URL);
+ response.setFirmware(firmware);
+ } else {
+ // 鍙湁鍦ㄨ澶囧凡缁戝畾涓旀槑纭紑鍚嚜鍔ㄥ崌绾ф椂鎵嶈繑鍥炲浐浠跺崌绾т俊鎭�
+ if (Integer.valueOf(1).equals(deviceById.getAutoUpdate())) {
+ String type = deviceReport.getBoard() == null ? null : deviceReport.getBoard().getType();
+ DeviceReportRespDTO.Firmware firmware = buildFirmwareInfo(type,
+ deviceReport.getApplication() == null ? null : deviceReport.getApplication().getVersion());
+ response.setFirmware(firmware);
+ }
+ }
+
+ // 娣诲姞WebSocket閰嶇疆
+ DeviceReportRespDTO.Websocket websocket = new DeviceReportRespDTO.Websocket();
+ // 浠庣郴缁熷弬鏁拌幏鍙朩ebSocket URL锛屽鏋滄湭閰嶇疆鍒欎娇鐢ㄩ粯璁ゅ��
+ String wsUrl = sysParamsService.getValue(Constant.SERVER_WEBSOCKET, true);
+
+ // 妫�鏌ユ槸鍚﹀惎鐢ㄨ璇佸苟鐢熸垚token
+ String authEnabled = sysParamsService.getValue(Constant.SERVER_AUTH_ENABLED, true);
+ if ("true".equalsIgnoreCase(authEnabled)) {
+ try {
+ // 鐢熸垚token
+ String token = generateWebSocketToken(clientId, macAddress);
+ websocket.setToken(token);
+ } catch (Exception e) {
+ log.error("鐢熸垚WebSocket token澶辫触: {}", e.getMessage());
+ websocket.setToken("");
+ }
+ } else {
+ websocket.setToken("");
+ }
+
+ if (StringUtils.isBlank(wsUrl) || wsUrl.equals("null")) {
+ log.error("WebSocket鍦板潃鏈厤缃紝璇风櫥褰曟櫤鎺у彴锛屽湪鍙傛暟绠$悊鎵惧埌銆恠erver.websocket銆戦厤缃�");
+ wsUrl = "ws://xiaozhi.server.com:8000/xiaozhi/v1/";
+ websocket.setUrl(wsUrl);
+ } else {
+ String[] wsUrls = wsUrl.split("\\;");
+ if (wsUrls.length > 0) {
+ // 闅忔満閫夋嫨涓�涓猈ebSocket URL
+ websocket.setUrl(wsUrls[RandomUtil.randomInt(0, wsUrls.length)]);
+ } else {
+ log.error("WebSocket鍦板潃鏈厤缃紝璇风櫥褰曟櫤鎺у彴锛屽湪鍙傛暟绠$悊鎵惧埌銆恠erver.websocket銆戦厤缃�");
+ websocket.setUrl("ws://xiaozhi.server.com:8000/xiaozhi/v1/");
+ }
+ }
+
+ response.setWebsocket(websocket);
+
+ // 娣诲姞MQTT UDP閰嶇疆
+ // 浠庣郴缁熷弬鏁拌幏鍙朚QTT Gateway鍦板潃锛屼粎鍦ㄩ厤缃湁鏁堟椂浣跨敤
+ String mqttUdpConfig = sysParamsService.getValue(Constant.SERVER_MQTT_GATEWAY, true);
+ if (mqttUdpConfig != null && !mqttUdpConfig.equals("null") && !mqttUdpConfig.isEmpty()) {
+ try {
+ String groupId = deviceById != null && deviceById.getBoard() != null ? deviceById.getBoard()
+ : "GID_default";
+ DeviceReportRespDTO.MQTT mqtt = buildMqttConfig(macAddress, groupId);
+ if (mqtt != null) {
+ mqtt.setEndpoint(mqttUdpConfig);
+ response.setMqtt(mqtt);
+ }
+ } catch (Exception e) {
+ log.error("鐢熸垚MQTT閰嶇疆澶辫触: {}", e.getMessage());
+ }
+ }
+
+ if (deviceById != null) {
+ // 濡傛灉璁惧瀛樺湪锛屽垯寮傛鏇存柊涓婃杩炴帴鏃堕棿鍜岀増鏈俊鎭�
+ String appVersion = deviceReport.getApplication() != null ? deviceReport.getApplication().getVersion()
+ : null;
+ // 閫氳繃Spring浠g悊璋冪敤寮傛鏂规硶
+ ((DeviceServiceImpl) AopContext.currentProxy()).updateDeviceConnectionInfo(deviceById.getAgentId(),
+ deviceById.getId(), appVersion);
+ } else {
+ // 濡傛灉璁惧涓嶅瓨鍦紝鍒欑敓鎴愭縺娲荤爜
+ DeviceReportRespDTO.Activation code = buildActivation(macAddress, deviceReport);
+ response.setActivation(code);
+ }
+
+ return response;
+ }
+
+ @Override
+ public List<DeviceEntity> getUserDevices(Long userId, String agentId) {
+ QueryWrapper<DeviceEntity> wrapper = new QueryWrapper<>();
+ wrapper.eq("user_id", userId);
+ wrapper.eq("agent_id", agentId);
+ return baseDao.selectList(wrapper);
+ }
+
+ @Override
+ public List<UserShowDeviceListVO> getUserDeviceList(Long userId, String agentId) {
+ List<DeviceEntity> devices = getUserDevices(userId, agentId);
+ return devices.stream().map(this::toUserShowDeviceListVO).toList();
+ }
+
+ private UserShowDeviceListVO toUserShowDeviceListVO(DeviceEntity device) {
+ UserShowDeviceListVO vo = ConvertUtils.sourceToTarget(device, UserShowDeviceListVO.class);
+ vo.setDeviceType(device.getBoard());
+ vo.setBoard(device.getBoard());
+ vo.setAutoUpdate(device.getAutoUpdate());
+ vo.setCreateDateTimestamp(toTimestamp(device.getCreateDate()));
+ vo.setLastConnectedAtTimestamp(toTimestamp(device.getLastConnectedAt()));
+ return vo;
+ }
+
+ private Long toTimestamp(Date date) {
+ return date == null ? null : date.getTime();
+ }
+
+ @Override
+ public void unbindDevice(Long userId, String deviceId) {
+ // 鍏堟煡璇㈣澶囦俊鎭紝鑾峰彇agentId鍜宮acAddress
+ DeviceEntity device = baseDao.selectById(deviceId);
+ if (device == null) {
+ return;
+ }
+ String macAddress = device.getMacAddress();
+ if (StringUtils.isNotBlank(device.getAgentId())) {
+ // 娓呴櫎鏅鸿兘浣撹澶囨暟閲忕紦瀛�
+ redisUtils.delete(RedisKeys.getAgentDeviceCountById(device.getAgentId()));
+ }
+
+ UpdateWrapper<DeviceEntity> wrapper = new UpdateWrapper<>();
+ wrapper.eq("user_id", userId);
+ wrapper.eq("id", deviceId);
+ baseDao.delete(wrapper);
+
+ // 鍒犻櫎璁惧鐩稿叧鐨勯�氳褰曟潈闄愯褰�
+ deviceAddressBookService.deleteByMacAddresses(Collections.singletonList(macAddress));
+ }
+
+ @Override
+ public void deleteByUserId(Long userId) {
+ UpdateWrapper<DeviceEntity> wrapper = new UpdateWrapper<>();
+ wrapper.eq("user_id", userId);
+ baseDao.delete(wrapper);
+ }
+
+ @Override
+ public Long selectCountByUserId(Long userId) {
+ UpdateWrapper<DeviceEntity> wrapper = new UpdateWrapper<>();
+ wrapper.eq("user_id", userId);
+ return baseDao.selectCount(wrapper);
+ }
+
+ @Override
+ public void deleteByAgentId(String agentId) {
+ // 鍏堟煡璇㈣鏅鸿兘浣撲笅鐨勬墍鏈夎澶囷紝鑾峰彇mac鍦板潃鐢ㄤ簬鍒犻櫎閫氳褰曡褰�
+ QueryWrapper<DeviceEntity> queryWrapper = new QueryWrapper<>();
+ queryWrapper.eq("agent_id", agentId);
+ List<DeviceEntity> devices = baseDao.selectList(queryWrapper);
+
+ // 鍒犻櫎璁惧
+ UpdateWrapper<DeviceEntity> wrapper = new UpdateWrapper<>();
+ wrapper.eq("agent_id", agentId);
+ baseDao.delete(wrapper);
+
+ // 鎵归噺鍒犻櫎杩欎簺璁惧鐩稿叧鐨勬墍鏈夐�氳褰曟潈闄愯褰�
+ if (!devices.isEmpty()) {
+ List<String> macAddresses = devices.stream()
+ .map(DeviceEntity::getMacAddress)
+ .collect(Collectors.toList());
+ deviceAddressBookService.deleteByMacAddresses(macAddresses);
+ }
+ }
+
+ @Override
+ public PageData<UserShowDeviceListVO> page(DevicePageUserDTO dto) {
+ Map<String, Object> params = new HashMap<String, Object>();
+ params.put(Constant.PAGE, dto.getPage());
+ params.put(Constant.LIMIT, dto.getLimit());
+ IPage<DeviceEntity> page = baseDao.selectPage(
+ getPage(params, "mac_address", true),
+ // 瀹氫箟鏌ヨ鏉′欢
+ new QueryWrapper<DeviceEntity>()
+ // 蹇呴』璁惧鍏抽敭璇嶆煡鎵�
+ .like(StringUtils.isNotBlank(dto.getKeywords()), "alias", dto.getKeywords()));
+ // 寰幆澶勭悊page鑾峰彇鍥炴潵鐨勬暟鎹紝杩斿洖闇�瑕佺殑瀛楁
+ List<UserShowDeviceListVO> list = page.getRecords().stream().map(device -> {
+ UserShowDeviceListVO vo = toUserShowDeviceListVO(device);
+ // 鎶婃渶鍚庝慨鏀圭殑鏃堕棿锛屾敼涓虹畝鐭弿杩扮殑鏃堕棿
+ vo.setRecentChatTime(DateUtils.getShortTime(device.getUpdateDate()));
+ sysUserUtilService.assignUsername(device.getUserId(),
+ vo::setBindUserName);
+ return vo;
+ }).toList();
+ // 璁$畻椤垫暟
+ return new PageData<>(list, page.getTotal());
+ }
+
+ @Override
+ public DeviceEntity getDeviceByMacAddress(String macAddress) {
+ if (StringUtils.isBlank(macAddress)) {
+ return null;
+ }
+ QueryWrapper<DeviceEntity> wrapper = new QueryWrapper<>();
+ wrapper.eq("mac_address", macAddress);
+ return baseDao.selectOne(wrapper);
+ }
+
+ private DeviceReportRespDTO.ServerTime buildServerTime() {
+ DeviceReportRespDTO.ServerTime serverTime = new DeviceReportRespDTO.ServerTime();
+ TimeZone tz = TimeZone.getDefault();
+ serverTime.setTimestamp(Instant.now().toEpochMilli());
+ serverTime.setTimeZone(tz.getID());
+ serverTime.setTimezone_offset(tz.getOffset(System.currentTimeMillis()) / (60 * 1000));
+ return serverTime;
+ }
+
+ @Override
+ public String geCodeByDeviceId(String deviceId) {
+ String dataKey = getDeviceCacheKey(deviceId);
+
+ Map<String, Object> cacheMap = JsonUtils.toStringObjectMap(redisUtils.get(dataKey));
+ if (cacheMap != null && cacheMap.containsKey("activation_code")) {
+ String cachedCode = (String) cacheMap.get("activation_code");
+ return cachedCode;
+ }
+ return null;
+ }
+
+ @Override
+ public Date getLatestLastConnectionTime(String agentId) {
+ // 鏌ヨ鏄惁鏈夌紦瀛樻椂闂达紝鏈夊垯杩斿洖
+ Date cachedDate = (Date) redisUtils.get(RedisKeys.getAgentDeviceLastConnectedAtById(agentId));
+ if (cachedDate != null) {
+ return cachedDate;
+ }
+ Date maxDate = deviceDao.getAllLastConnectedAtByAgentId(agentId);
+ if (maxDate != null) {
+ redisUtils.set(RedisKeys.getAgentDeviceLastConnectedAtById(agentId), maxDate);
+ }
+ return maxDate;
+ }
+
+ private String getDeviceCacheKey(String deviceId) {
+ String safeDeviceId = deviceId.replace(":", "_").toLowerCase();
+ return RedisKeys.getOtaDeviceActivationInfo(safeDeviceId);
+ }
+
+ public DeviceReportRespDTO.Activation buildActivation(String deviceId, DeviceReportReqDTO deviceReport) {
+ DeviceReportRespDTO.Activation code = new DeviceReportRespDTO.Activation();
+
+ String cachedCode = geCodeByDeviceId(deviceId);
+
+ if (StringUtils.isNotBlank(cachedCode)) {
+ code.setCode(cachedCode);
+ String frontedUrl = sysParamsService.getValue(Constant.SERVER_FRONTED_URL, true);
+ code.setMessage(frontedUrl + "\n" + cachedCode);
+ code.setChallenge(deviceId);
+ } else {
+ String newCode = RandomUtil.randomNumbers(6);
+ code.setCode(newCode);
+ String frontedUrl = sysParamsService.getValue(Constant.SERVER_FRONTED_URL, true);
+ code.setMessage(frontedUrl + "\n" + newCode);
+ code.setChallenge(deviceId);
+
+ Map<String, Object> dataMap = new HashMap<>();
+ dataMap.put("id", deviceId);
+ dataMap.put("mac_address", deviceId);
+
+ dataMap.put("board", (deviceReport.getBoard() != null && deviceReport.getBoard().getType() != null)
+ ? deviceReport.getBoard().getType()
+ : (deviceReport.getChipModelName() != null ? deviceReport.getChipModelName() : "unknown"));
+ dataMap.put("app_version", (deviceReport.getApplication() != null)
+ ? deviceReport.getApplication().getVersion()
+ : null);
+
+ dataMap.put("deviceId", deviceId);
+ dataMap.put("activation_code", newCode);
+
+ // 鍐欏叆涓绘暟鎹� key
+ String dataKey = getDeviceCacheKey(deviceId);
+ redisUtils.set(dataKey, dataMap);
+
+ // 鍐欏叆鍙嶆煡婵�娲荤爜 key
+ String codeKey = RedisKeys.getOtaActivationCode(newCode);
+ redisUtils.set(codeKey, deviceId);
+ }
+ return code;
+ }
+
+ private DeviceReportRespDTO.Firmware buildFirmwareInfo(String type, String currentVersion) {
+ if (StringUtils.isBlank(type)) {
+ return null;
+ }
+ if (StringUtils.isBlank(currentVersion)) {
+ currentVersion = "0.0.0";
+ }
+
+ OtaEntity ota = otaService.getLatestOta(type);
+ DeviceReportRespDTO.Firmware firmware = new DeviceReportRespDTO.Firmware();
+ String downloadUrl = null;
+
+ if (ota != null) {
+ // 濡傛灉璁惧娌℃湁鐗堟湰淇℃伅锛屾垨鑰匫TA鐗堟湰姣旇澶囩増鏈柊锛屽垯杩斿洖涓嬭浇鍦板潃
+ if (compareVersions(ota.getVersion(), currentVersion) > 0) {
+ String otaUrl = sysParamsService.getValue(Constant.SERVER_OTA, true);
+ if (StringUtils.isBlank(otaUrl) || otaUrl.equals("null")) {
+ log.error("OTA鍦板潃鏈厤缃紝璇风櫥褰曟櫤鎺у彴锛屽湪鍙傛暟绠$悊鎵惧埌銆恠erver.ota銆戦厤缃�");
+ // 灏濊瘯浠庤姹備腑鑾峰彇
+ HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
+ .getRequestAttributes())
+ .getRequest();
+ otaUrl = request.getRequestURL().toString();
+ }
+ // 灏哢RL涓殑/ota/鏇挎崲涓�/otaMag/download/
+ String uuid = UUID.randomUUID().toString();
+ redisUtils.set(RedisKeys.getOtaIdKey(uuid), ota.getId());
+ downloadUrl = otaUrl.replace("/ota/", "/otaMag/download/") + uuid;
+ }
+ }
+
+ firmware.setVersion(ota == null ? currentVersion : ota.getVersion());
+ firmware.setUrl(downloadUrl == null ? Constant.INVALID_FIRMWARE_URL : downloadUrl);
+ return firmware;
+ }
+
+ /**
+ * 姣旇緝涓や釜鐗堟湰鍙�
+ *
+ * @param version1 鐗堟湰1
+ * @param version2 鐗堟湰2
+ * @return 濡傛灉version1 > version2杩斿洖1锛寁ersion1 < version2杩斿洖-1锛岀浉绛夎繑鍥�0
+ */
+ private static int compareVersions(String version1, String version2) {
+ if (version1 == null || version2 == null) {
+ return 0;
+ }
+
+ String[] v1Parts = version1.split("\\.");
+ String[] v2Parts = version2.split("\\.");
+
+ int length = Math.max(v1Parts.length, v2Parts.length);
+ for (int i = 0; i < length; i++) {
+ int v1 = i < v1Parts.length ? Integer.parseInt(v1Parts[i]) : 0;
+ int v2 = i < v2Parts.length ? Integer.parseInt(v2Parts[i]) : 0;
+
+ if (v1 > v2) {
+ return 1;
+ } else if (v1 < v2) {
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public void manualAddDevice(Long userId, DeviceManualAddDTO dto) {
+ // 妫�鏌ac鏄惁宸插瓨鍦�
+ QueryWrapper<DeviceEntity> wrapper = new QueryWrapper<>();
+ wrapper.eq("mac_address", dto.getMacAddress());
+ DeviceEntity exist = baseDao.selectOne(wrapper);
+ if (exist != null) {
+ throw new RenException(ErrorCode.MAC_ADDRESS_ALREADY_EXISTS);
+ }
+ Date now = new Date();
+ DeviceEntity entity = new DeviceEntity();
+ entity.setId(dto.getMacAddress());
+ entity.setUserId(userId);
+ entity.setAgentId(dto.getAgentId());
+ entity.setBoard(dto.getBoard());
+ entity.setAppVersion(dto.getAppVersion());
+ entity.setMacAddress(dto.getMacAddress());
+ entity.setCreateDate(now);
+ entity.setUpdateDate(now);
+ entity.setLastConnectedAt(now);
+ entity.setCreator(userId);
+ entity.setUpdater(userId);
+ entity.setAutoUpdate(1);
+ baseDao.insert(entity);
+
+ // 娣诲姞锛氭竻闄ゆ櫤鑳戒綋璁惧鏁伴噺缂撳瓨
+ redisUtils.delete(RedisKeys.getAgentDeviceCountById(dto.getAgentId()));
+ }
+
+ @Override
+ public List<DeviceEntity> searchDevicesByMacAddress(String macAddress, Long userId) {
+ QueryWrapper<DeviceEntity> wrapper = new QueryWrapper<>();
+ wrapper.like("mac_address", macAddress);
+ wrapper.eq("user_id", userId);
+ return deviceDao.selectList(wrapper);
+ }
+
+ /**
+ * 鐢熸垚MQTT瀵嗙爜绛惧悕
+ *
+ * @param content 绛惧悕鍐呭 (clientId + '|' + username)
+ * @param secretKey 瀵嗛挜
+ * @return Base64缂栫爜鐨凥MAC-SHA256绛惧悕
+ */
+ private String generatePasswordSignature(String content, String secretKey) throws Exception {
+ Mac hmac = Mac.getInstance("HmacSHA256");
+ SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
+ hmac.init(keySpec);
+ byte[] signature = hmac.doFinal(content.getBytes(StandardCharsets.UTF_8));
+ return Base64.getEncoder().encodeToString(signature);
+ }
+
+ /**
+ * 鐢熸垚WebSocket璁よ瘉token 閬靛惊Python绔疉uthManager鐨勫疄鐜伴�昏緫锛歵oken = signature.timestamp
+ *
+ * @param clientId 瀹㈡埛绔疘D
+ * @param username 鐢ㄦ埛鍚� (閫氬父涓篸eviceId/macAddress)
+ * @return 璁よ瘉token瀛楃涓�
+ */
+ public String generateWebSocketToken(String clientId, String username)
+ throws NoSuchAlgorithmException, InvalidKeyException {
+ // 浠庣郴缁熷弬鏁拌幏鍙栧瘑閽�
+ String secretKey = sysParamsService.getValue(Constant.SERVER_SECRET, false);
+ if (StringUtils.isBlank(secretKey)) {
+ throw new IllegalStateException("WebSocket璁よ瘉瀵嗛挜鏈厤缃�(server.secret)");
+ }
+
+ // 鑾峰彇褰撳墠鏃堕棿鎴�(绉�)
+ long timestamp = System.currentTimeMillis() / 1000;
+
+ // 鏋勫缓绛惧悕鍐呭: clientId|username|timestamp
+ String content = String.format("%s|%s|%d", clientId, username, timestamp);
+
+ // 鐢熸垚HMAC-SHA256绛惧悕
+ Mac hmac = Mac.getInstance("HmacSHA256");
+ SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
+ hmac.init(keySpec);
+ byte[] signature = hmac.doFinal(content.getBytes(StandardCharsets.UTF_8));
+
+ // Base64 URL-safe缂栫爜绛惧悕(鍘婚櫎濉厖绗�=)
+ String signatureBase64 = Base64.getUrlEncoder().withoutPadding().encodeToString(signature);
+
+ // 杩斿洖鏍煎紡: signature.timestamp
+ return String.format("%s.%d", signatureBase64, timestamp);
+ }
+
+ /**
+ * 鏋勫缓MQTT閰嶇疆淇℃伅
+ *
+ * @param macAddress MAC鍦板潃
+ * @param groupId 鍒嗙粍ID
+ * @return MQTT閰嶇疆瀵硅薄
+ */
+ private DeviceReportRespDTO.MQTT buildMqttConfig(String macAddress, String groupId)
+ throws Exception {
+ // 浠庣幆澧冨彉閲忔垨绯荤粺鍙傛暟鑾峰彇绛惧悕瀵嗛挜
+ String signatureKey = sysParamsService.getValue("server.mqtt_signature_key", true);
+ if (StringUtils.isBlank(signatureKey)) {
+ log.warn("缂哄皯MQTT_SIGNATURE_KEY锛岃烦杩嘙QTT閰嶇疆鐢熸垚");
+ return null;
+ }
+
+ // 鏋勫缓瀹㈡埛绔疘D鏍煎紡锛歡roupId@@@macAddress@@@uuid
+ String groupIdSafeStr = groupId.replace(":", "_");
+ String deviceIdSafeStr = macAddress.replace(":", "_");
+ String mqttClientId = String.format("%s@@@%s@@@%s", groupIdSafeStr, deviceIdSafeStr, deviceIdSafeStr);
+
+ // 鏋勫缓鐢ㄦ埛鏁版嵁锛堝寘鍚獻P绛変俊鎭級
+ Map<String, String> userData = new HashMap<>();
+ // 灏濊瘯鑾峰彇瀹㈡埛绔疘P
+ try {
+ ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
+ .getRequestAttributes();
+ if (attributes != null) {
+ HttpServletRequest request = attributes.getRequest();
+ String clientIp = request.getRemoteAddr();
+ userData.put("ip", clientIp);
+ }
+ } catch (Exception e) {
+ userData.put("ip", "unknown");
+ }
+
+ // 灏嗙敤鎴锋暟鎹紪鐮佷负Base64 JSON
+ String userDataJson = new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(userData);
+ String username = Base64.getEncoder().encodeToString(userDataJson.getBytes(StandardCharsets.UTF_8));
+
+ // 鐢熸垚瀵嗙爜绛惧悕
+ String password = generatePasswordSignature(mqttClientId + "|" + username, signatureKey);
+
+ // 鏋勫缓MQTT閰嶇疆
+ DeviceReportRespDTO.MQTT mqtt = new DeviceReportRespDTO.MQTT();
+ mqtt.setClient_id(mqttClientId);
+ mqtt.setUsername(username);
+ mqtt.setPassword(password);
+ mqtt.setPublish_topic("device-server");
+ mqtt.setSubscribe_topic("devices/p2p/" + deviceIdSafeStr);
+
+ return mqtt;
+ }
+
+ private String postToMqttGateway(String url, Object requestBody) {
+ String signatureKey = sysParamsService.getValue(Constant.SERVER_MQTT_SECRET, false);
+ return MqttGatewayAuthorization.postJson(
+ url,
+ JSONUtil.toJsonStr(requestBody),
+ signatureKey,
+ Instant.now());
+ }
+
+ @Override
+ public Object getDeviceTools(String deviceId) {
+ // 浠庣郴缁熷弬鏁颁腑鑾峰彇MQTT缃戝叧鍦板潃
+ String mqttGatewayUrl = sysParamsService.getValue("server.mqtt_manager_api", true);
+ if (StringUtils.isBlank(mqttGatewayUrl) || "null".equals(mqttGatewayUrl)) {
+ return null;
+ }
+
+ // 鑾峰彇璁惧淇℃伅
+ DeviceEntity device = baseDao.selectById(deviceId);
+ if (device == null) {
+ return null;
+ }
+
+ // 妫�鏌ヨ澶囨槸鍚﹀睘浜庡綋鍓嶇敤鎴�
+ UserDetail user = SecurityUser.getUser();
+ if (!device.getUserId().equals(user.getId())) {
+ return null;
+ }
+
+ // 鏋勫缓clientId
+ String macAddress = Optional.ofNullable(device.getMacAddress()).orElse("unknown").replace(":", "_");
+ String groupId = Optional.ofNullable(device.getBoard()).orElse("GID_default").replace(":", "_");
+ String clientId = StrUtil.format("{}@@@{}@@@{}", groupId, macAddress, macAddress);
+
+ // 鏋勫缓瀹屾暣鐨刄RL
+ String url = StrUtil.format("http://{}/api/commands/{}", mqttGatewayUrl, clientId);
+
+ // 瀛樺偍鎵�鏈夊伐鍏峰垪琛�
+ List<Object> allTools = new ArrayList<>();
+ String cursor = null;
+
+ // 寰幆鑾峰彇鍒嗛〉鏁版嵁
+ while (true) {
+ // 鏋勫缓params
+ Map<String, Object> paramsMap = MapUtil.builder(new HashMap<String, Object>())
+ .put("withUserTools", true)
+ .build();
+ // 濡傛灉鏈塩ursor锛屾坊鍔犲埌璇锋眰鍙傛暟涓�
+ if (StringUtils.isNotBlank(cursor)) {
+ paramsMap.put("cursor", cursor);
+ }
+
+ // 鏋勫缓璇锋眰浣�
+ Map<String, Object> payload = MapUtil
+ .builder(new HashMap<String, Object>())
+ .put("jsonrpc", "2.0")
+ .put("id", 2)
+ .put("method", "tools/list")
+ .put("params", paramsMap)
+ .build();
+
+ Map<String, Object> requestBody = MapUtil
+ .builder(new HashMap<String, Object>())
+ .put("type", "mcp")
+ .put("payload", payload)
+ .build();
+
+ String resultMessage = postToMqttGateway(url, requestBody);
+
+ // 瑙f瀽鍝嶅簲
+ if (StringUtils.isBlank(resultMessage)) {
+ break;
+ }
+
+ JSONObject jsonObject = JSONUtil.parseObj(resultMessage);
+ if (!jsonObject.getBool("success", false)) {
+ break;
+ }
+
+ JSONObject data = jsonObject.getJSONObject("data");
+ if (data == null) {
+ break;
+ }
+
+ // 鑾峰彇褰撳墠椤电殑宸ュ叿鍒楄〃
+ JSONArray tools = data.getJSONArray("tools");
+ if (tools != null && !tools.isEmpty()) {
+ allTools.addAll(tools);
+ }
+
+ // 鑾峰彇涓嬩竴椤电殑cursor
+ String nextCursor = data.getStr("nextCursor");
+ if (StringUtils.isBlank(nextCursor)) {
+ // 娌℃湁涓嬩竴椤典簡
+ break;
+ }
+ cursor = nextCursor;
+ }
+
+ // 鏋勫缓杩斿洖缁撴灉
+ if (allTools.isEmpty()) {
+ return null;
+ }
+
+ Map<String, Object> resultData = new HashMap<>();
+ resultData.put("tools", allTools);
+ return resultData;
+ }
+
+ @Override
+ public Object callDeviceTool(String deviceId, String toolName, Map<String, Object> arguments) {
+ // 浠庣郴缁熷弬鏁颁腑鑾峰彇MQTT缃戝叧鍦板潃
+ String mqttGatewayUrl = sysParamsService.getValue("server.mqtt_manager_api", true);
+ if (StringUtils.isBlank(mqttGatewayUrl) || "null".equals(mqttGatewayUrl)) {
+ return null;
+ }
+
+ // 鑾峰彇璁惧淇℃伅
+ DeviceEntity device = baseDao.selectById(deviceId);
+ if (device == null) {
+ return null;
+ }
+
+ // 妫�鏌ヨ澶囨槸鍚﹀睘浜庡綋鍓嶇敤鎴�
+ UserDetail user = SecurityUser.getUser();
+ if (!device.getUserId().equals(user.getId())) {
+ return null;
+ }
+
+ // 鏋勫缓clientId
+ String macAddress = Optional.ofNullable(device.getMacAddress()).orElse("unknown").replace(":", "_");
+ String groupId = Optional.ofNullable(device.getBoard()).orElse("GID_default").replace(":", "_");
+ String clientId = StrUtil.format("{}@@@{}@@@{}", groupId, macAddress, macAddress);
+
+ // 鏋勫缓瀹屾暣鐨刄RL
+ String url = StrUtil.format("http://{}/api/commands/{}", mqttGatewayUrl, clientId);
+
+ // 鏋勫缓璇锋眰浣�
+ Map<String, Object> params = MapUtil
+ .builder(new HashMap<String, Object>())
+ .put("name", toolName)
+ .put("arguments", arguments)
+ .build();
+
+ Map<String, Object> payload = MapUtil
+ .builder(new HashMap<String, Object>())
+ .put("jsonrpc", "2.0")
+ .put("id", 2)
+ .put("method", "tools/call")
+ .put("params", params)
+ .build();
+
+ Map<String, Object> requestBody = MapUtil
+ .builder(new HashMap<String, Object>())
+ .put("type", "mcp")
+ .put("payload", payload)
+ .build();
+
+ String resultMessage = postToMqttGateway(url, requestBody);
+
+ // 瑙f瀽鍝嶅簲
+ if (StringUtils.isNotBlank(resultMessage)) {
+ cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(resultMessage);
+ if (jsonObject.getBool("success", false)) {
+ cn.hutool.json.JSONObject data = jsonObject.getJSONObject("data");
+ if (data != null) {
+ cn.hutool.json.JSONArray content = data.getJSONArray("content");
+ if (content != null && content.size() > 0) {
+ cn.hutool.json.JSONObject firstContent = content.getJSONObject(0);
+ if (firstContent != null && "text".equals(firstContent.getStr("type"))) {
+ String text = firstContent.getStr("text");
+ if (StringUtils.isNotBlank(text)) {
+ String trimmedText = text.trim();
+ if (trimmedText.startsWith("{") || trimmedText.startsWith("[")) {
+ try {
+ return JSONUtil.parseObj(trimmedText);
+ } catch (Exception e) {
+ return trimmedText;
+ }
+ } else if ("true".equals(trimmedText)) {
+ return true;
+ } else if ("false".equals(trimmedText)) {
+ return false;
+ } else {
+ return trimmedText;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/MqttGatewayAuthorization.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/MqttGatewayAuthorization.java
new file mode 100644
index 0000000..f165419
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/MqttGatewayAuthorization.java
@@ -0,0 +1,111 @@
+package xiaozhi.modules.device.service.impl;
+
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneOffset;
+import java.util.List;
+import java.util.function.Function;
+
+import org.apache.commons.lang3.StringUtils;
+
+import cn.hutool.crypto.digest.DigestUtil;
+import cn.hutool.http.ContentType;
+import cn.hutool.http.Header;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpResponse;
+
+final class MqttGatewayAuthorization {
+
+ private static final int HTTP_UNAUTHORIZED = 401;
+ private static final int DEFAULT_TIMEOUT_MILLIS = 10000;
+
+ private MqttGatewayAuthorization() {
+ }
+
+ static String postJson(String url, String jsonBody, String signatureKey, Instant now) {
+ return postJson(url, jsonBody, signatureKey, now, DEFAULT_TIMEOUT_MILLIS);
+ }
+
+ static String postJson(String url, String jsonBody, String signatureKey, Instant now, int timeoutMillis) {
+ GatewayResponse response = executeWithDateFallback(
+ signatureKey,
+ now,
+ token -> executeRequest(url, jsonBody, token, timeoutMillis));
+
+ if (response.statusCode() < 200 || response.statusCode() >= 300) {
+ throw new GatewayRequestException(
+ "MQTT Gateway request failed with HTTP status " + response.statusCode(),
+ response.statusCode());
+ }
+ return response.body();
+ }
+
+ static List<String> generateDailyTokens(String signatureKey, Instant now) {
+ if (isMissingSignatureKey(signatureKey)) {
+ throw new GatewayRequestException("MQTT Gateway signature key is empty", null);
+ }
+
+ LocalDate utcDate = now.atZone(ZoneOffset.UTC).toLocalDate();
+ return List.of(
+ tokenFor(utcDate, signatureKey),
+ tokenFor(utcDate.minusDays(1), signatureKey),
+ tokenFor(utcDate.plusDays(1), signatureKey));
+ }
+
+ static GatewayResponse executeWithDateFallback(String signatureKey, Instant now,
+ Function<String, GatewayResponse> requestExecutor) {
+ GatewayResponse lastAuthenticationFailure = null;
+ for (String token : generateDailyTokens(signatureKey, now)) {
+ GatewayResponse response = requestExecutor.apply(token);
+ if (!isAuthenticationFailure(response.statusCode())) {
+ return response;
+ }
+ lastAuthenticationFailure = response;
+ }
+
+ Integer statusCode = lastAuthenticationFailure == null ? null : lastAuthenticationFailure.statusCode();
+ throw new GatewayRequestException(
+ "MQTT Gateway rejected all daily authorization tokens"
+ + (statusCode == null ? "" : " (HTTP " + statusCode + ")"),
+ statusCode);
+ }
+
+ private static String tokenFor(LocalDate date, String signatureKey) {
+ return DigestUtil.sha256Hex(date + signatureKey);
+ }
+
+ private static boolean isAuthenticationFailure(int statusCode) {
+ return statusCode == HTTP_UNAUTHORIZED;
+ }
+
+ static boolean isMissingSignatureKey(String signatureKey) {
+ return StringUtils.isBlank(signatureKey) || "null".equalsIgnoreCase(signatureKey.trim());
+ }
+
+ private static GatewayResponse executeRequest(String url, String jsonBody, String token, int timeoutMillis) {
+ try (HttpResponse response = HttpRequest.post(url)
+ .header(Header.CONTENT_TYPE, ContentType.JSON.getValue())
+ .header(Header.AUTHORIZATION, "Bearer " + token)
+ .body(jsonBody)
+ .timeout(timeoutMillis)
+ .execute()) {
+ return new GatewayResponse(response.getStatus(), response.body());
+ }
+ }
+
+ record GatewayResponse(int statusCode, String body) {
+ }
+
+ static final class GatewayRequestException extends RuntimeException {
+ private final Integer statusCode;
+
+ GatewayRequestException(String message, Integer statusCode) {
+ super(message);
+ this.statusCode = statusCode;
+ }
+
+ Integer statusCode() {
+ return statusCode;
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/OtaServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/OtaServiceImpl.java
new file mode 100644
index 0000000..a743c5e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/OtaServiceImpl.java
@@ -0,0 +1,85 @@
+package xiaozhi.modules.device.service.impl;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.stereotype.Service;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+
+import io.micrometer.common.util.StringUtils;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.service.impl.BaseServiceImpl;
+import xiaozhi.modules.device.dao.OtaDao;
+import xiaozhi.modules.device.entity.OtaEntity;
+import xiaozhi.modules.device.service.OtaService;
+
+@Service
+public class OtaServiceImpl extends BaseServiceImpl<OtaDao, OtaEntity> implements OtaService {
+
+ @Override
+ public PageData<OtaEntity> page(Map<String, Object> params) {
+ IPage<OtaEntity> page = baseDao.selectPage(
+ getPage(params, "update_date", true),
+ getWrapper(params));
+
+ return new PageData<>(page.getRecords(), page.getTotal());
+ }
+
+ private QueryWrapper<OtaEntity> getWrapper(Map<String, Object> params) {
+ String firmwareName = (String) params.get("firmwareName");
+
+ QueryWrapper<OtaEntity> wrapper = new QueryWrapper<>();
+ wrapper.like(StringUtils.isNotBlank(firmwareName), "firmware_name", firmwareName);
+
+ return wrapper;
+ }
+
+ @Override
+ public void update(OtaEntity entity) {
+ // 妫�鏌ユ槸鍚﹀瓨鍦ㄧ浉鍚岀被鍨嬪拰鐗堟湰鐨勫浐浠讹紙鎺掗櫎褰撳墠璁板綍锛�
+ QueryWrapper<OtaEntity> queryWrapper = new QueryWrapper<OtaEntity>()
+ .eq("type", entity.getType())
+ .eq("version", entity.getVersion())
+ .ne("id", entity.getId()); // 鎺掗櫎褰撳墠璁板綍
+
+ if (baseDao.selectCount(queryWrapper) > 0) {
+ throw new RuntimeException("宸插瓨鍦ㄧ浉鍚岀被鍨嬪拰鐗堟湰鐨勫浐浠讹紝璇蜂慨鏀瑰悗閲嶈瘯");
+ }
+
+ entity.setUpdateDate(new Date());
+ baseDao.updateById(entity);
+ }
+
+ @Override
+ public void delete(String[] ids) {
+ baseDao.deleteByIds(Arrays.asList(ids));
+ }
+
+ @Override
+ public boolean save(OtaEntity entity) {
+ QueryWrapper<OtaEntity> queryWrapper = new QueryWrapper<OtaEntity>()
+ .eq("type", entity.getType());
+ // 鍚岀被鍥轰欢鍙繚鐣欐渶鏂扮殑涓�鏉�
+ List<OtaEntity> otaList = baseDao.selectList(queryWrapper);
+ if (otaList != null && otaList.size() > 0) {
+ OtaEntity otaBefore = otaList.get(0);
+ entity.setId(otaBefore.getId());
+ baseDao.updateById(entity);
+ return true;
+ }
+ return baseDao.insert(entity) > 0;
+ }
+
+ @Override
+ public OtaEntity getLatestOta(String type) {
+ QueryWrapper<OtaEntity> wrapper = new QueryWrapper<>();
+ wrapper.eq("type", type)
+ .orderByDesc("update_date")
+ .last("LIMIT 1");
+ return baseDao.selectOne(wrapper);
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/vo/DeviceOtaVO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/vo/DeviceOtaVO.java
new file mode 100644
index 0000000..ec8e405
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/vo/DeviceOtaVO.java
@@ -0,0 +1,59 @@
+package xiaozhi.modules.device.vo;
+
+import lombok.Data;
+
+@Data
+public class DeviceOtaVO {
+ private Activation activation;
+ private Mqtt mqtt;
+ private ServerTime server_time;
+ private Firmware firmware;
+ private String error;
+
+ @Data
+ public static class Activation {
+ private String code;
+ private String message;
+
+ public Activation() {
+ }
+
+ public Activation(String code, String message) {
+ this.code = code;
+ this.message = message;
+ }
+ }
+
+ @Data
+ public class Mqtt {
+
+ }
+
+ @Data
+ public static class ServerTime {
+ private Long timestamp;
+ private Integer timezone_offset;
+
+ public ServerTime() {
+ }
+
+ public ServerTime(Long timestamp, Integer timezone_offset) {
+ this.timestamp = timestamp;
+ this.timezone_offset = timezone_offset;
+ }
+ }
+
+ @Data
+ public static class Firmware {
+ private String version;
+ private String url;
+
+ public Firmware() {
+ }
+
+ public Firmware(String version, String url) {
+ this.version = version;
+ this.url = url;
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/vo/UserShowDeviceListVO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/vo/UserShowDeviceListVO.java
new file mode 100644
index 0000000..b00a168
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/device/vo/UserShowDeviceListVO.java
@@ -0,0 +1,51 @@
+package xiaozhi.modules.device.vo;
+
+import java.util.Date;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@Schema(description = "鐢ㄦ埛鏄剧ず璁惧鍒楄〃VO")
+public class UserShowDeviceListVO {
+
+ @Schema(description = "app鐗堟湰")
+ private String appVersion;
+
+ @Schema(description = "缁戝畾鐢ㄦ埛鍚嶇О")
+ private String bindUserName;
+
+ @Schema(description = "璁惧鍨嬪彿")
+ private String deviceType;
+
+ @Schema(description = "璁惧鍨嬪彿(board)")
+ private String board;
+
+ @Schema(description = "璁惧鍞竴鏍囪瘑绗�")
+ private String id;
+
+ @Schema(description = "mac鍦板潃")
+ private String macAddress;
+
+ @Schema(description = "璁惧鍒悕")
+ private String alias;
+
+ @Schema(description = "鑷姩鏇存柊寮�鍏�(0鍏抽棴/1寮�鍚�)")
+ private Integer autoUpdate;
+
+ @Schema(description = "鏈�杩戝璇濇椂闂�")
+ private String recentChatTime;
+
+ @Schema(description = "鏈�鍚庤繛鎺ユ椂闂存埑锛堟绉掞級", type = "string", example = "1783689702000")
+ private Long lastConnectedAtTimestamp;
+
+ @Schema(description = "缁戝畾鏃堕棿鎴筹紙姣锛�", type = "string", example = "1783689702000")
+ private Long createDateTimestamp;
+
+ @Schema(description = "缁戝畾鏃堕棿锛堝吋瀹瑰瓧娈碉紝璇蜂娇鐢� createDateTimestamp锛�", deprecated = true)
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "UTC")
+ private Date createDate;
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/config/KnowledgeBaseConfig.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/config/KnowledgeBaseConfig.java
new file mode 100644
index 0000000..3b34d34
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/config/KnowledgeBaseConfig.java
@@ -0,0 +1,22 @@
+package xiaozhi.modules.knowledge.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import xiaozhi.modules.knowledge.rag.KnowledgeBaseAdapterFactory;
+
+/**
+ * 鐭ヨ瘑搴撻厤缃被
+ * 閰嶇疆鐭ヨ瘑搴撶浉鍏崇殑Bean
+ */
+@Configuration
+public class KnowledgeBaseConfig {
+
+ /**
+ * 鎻愪緵KnowledgeBaseAdapterFactory鐨凚ean瀹炰緥
+ * @return KnowledgeBaseAdapterFactory瀹炰緥
+ */
+ @Bean
+ public KnowledgeBaseAdapterFactory knowledgeBaseAdapterFactory() {
+ return new KnowledgeBaseAdapterFactory();
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/config/RAGTaskConfig.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/config/RAGTaskConfig.java
new file mode 100644
index 0000000..0cd1492
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/config/RAGTaskConfig.java
@@ -0,0 +1,13 @@
+package xiaozhi.modules.knowledge.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+/**
+ * 鐭ヨ瘑搴撴ā鍧楀畾鏃朵换鍔¢厤缃�
+ * 鍚敤 Spring Schedule 鑳藉姏
+ */
+@Configuration
+@EnableScheduling
+public class RAGTaskConfig {
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/controller/KnowledgeBaseController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/controller/KnowledgeBaseController.java
new file mode 100644
index 0000000..10e3d8d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/controller/KnowledgeBaseController.java
@@ -0,0 +1,163 @@
+package xiaozhi.modules.knowledge.controller;
+
+import java.util.*;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.hutool.core.collection.CollUtil;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.utils.Result;
+import xiaozhi.modules.knowledge.dto.KnowledgeBaseDTO;
+import xiaozhi.modules.knowledge.service.KnowledgeBaseService;
+import xiaozhi.modules.knowledge.service.KnowledgeManagerService;
+import xiaozhi.modules.model.entity.ModelConfigEntity;
+import xiaozhi.modules.security.user.SecurityUser;
+
+@AllArgsConstructor
+@RestController
+@RequestMapping("/datasets")
+@Tag(name = "鐭ヨ瘑搴撶鐞�")
+public class KnowledgeBaseController {
+
+ private final KnowledgeBaseService knowledgeBaseService;
+ private final KnowledgeManagerService knowledgeManagerService;
+
+ @GetMapping
+ @Operation(summary = "鍒嗛〉鏌ヨ鐭ヨ瘑搴撳垪琛�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<PageData<KnowledgeBaseDTO>> getPageList(
+ @RequestParam(required = false) String name,
+ @RequestParam(required = false, defaultValue = "1") Integer page,
+ @RequestParam(required = false, defaultValue = "10") Integer page_size) {
+ // 鑾峰彇褰撳墠鐧诲綍鐢ㄦ埛ID
+ Long currentUserId = SecurityUser.getUserId();
+
+ KnowledgeBaseDTO knowledgeBaseDTO = new KnowledgeBaseDTO();
+ knowledgeBaseDTO.setName(name);
+ knowledgeBaseDTO.setCreator(currentUserId); // 璁剧疆鍒涘缓鑰匢D锛岀敤浜庢潈闄愯繃婊�
+
+ PageData<KnowledgeBaseDTO> pageData = knowledgeBaseService.getPageList(knowledgeBaseDTO, page, page_size);
+ return new Result<PageData<KnowledgeBaseDTO>>().ok(pageData);
+ }
+
+ @GetMapping("/{dataset_id}")
+ @Operation(summary = "鏍规嵁鐭ヨ瘑搴揑D鑾峰彇鐭ヨ瘑搴撹鎯�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<KnowledgeBaseDTO> getByDatasetId(@PathVariable("dataset_id") String datasetId) {
+ // 鑾峰彇褰撳墠鐧诲綍鐢ㄦ埛ID
+ Long currentUserId = SecurityUser.getUserId();
+
+ KnowledgeBaseDTO knowledgeBaseDTO = knowledgeBaseService.getByDatasetId(datasetId);
+
+ // 妫�鏌ユ潈闄愶細鐢ㄦ埛鍙兘鏌ョ湅鑷繁鍒涘缓鐨勭煡璇嗗簱
+ if (knowledgeBaseDTO.getCreator() == null || !knowledgeBaseDTO.getCreator().equals(currentUserId)) {
+ throw new RenException(ErrorCode.NO_PERMISSION);
+ }
+
+ return new Result<KnowledgeBaseDTO>().ok(knowledgeBaseDTO);
+ }
+
+ @PostMapping
+ @Operation(summary = "鍒涘缓鐭ヨ瘑搴�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<KnowledgeBaseDTO> save(@RequestBody @Validated KnowledgeBaseDTO knowledgeBaseDTO) {
+ KnowledgeBaseDTO resp = knowledgeBaseService.save(knowledgeBaseDTO);
+ return new Result<KnowledgeBaseDTO>().ok(resp);
+ }
+
+ @PutMapping("/{dataset_id}")
+ @Operation(summary = "鏇存柊鐭ヨ瘑搴�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<KnowledgeBaseDTO> update(@PathVariable("dataset_id") String datasetId,
+ @RequestBody @Validated KnowledgeBaseDTO knowledgeBaseDTO) {
+ // 鑾峰彇褰撳墠鐧诲綍鐢ㄦ埛ID
+ Long currentUserId = SecurityUser.getUserId();
+
+ // 鍏堣幏鍙栫幇鏈夌煡璇嗗簱淇℃伅浠ユ鏌ユ潈闄�
+ KnowledgeBaseDTO existingKnowledgeBase = knowledgeBaseService.getByDatasetId(datasetId);
+
+ // 妫�鏌ユ潈闄愶細鐢ㄦ埛鍙兘鏇存柊鑷繁鍒涘缓鐨勭煡璇嗗簱
+ if (existingKnowledgeBase.getCreator() == null || !existingKnowledgeBase.getCreator().equals(currentUserId)) {
+ throw new RenException(ErrorCode.NO_PERMISSION);
+ }
+
+ // [FIX] 娉ㄥ叆 ID锛岄槻姝� Service 灞傛壘涓嶅埌璁板綍
+ knowledgeBaseDTO.setId(existingKnowledgeBase.getId());
+ knowledgeBaseDTO.setDatasetId(datasetId);
+ KnowledgeBaseDTO resp = knowledgeBaseService.update(knowledgeBaseDTO);
+ return new Result<KnowledgeBaseDTO>().ok(resp);
+ }
+
+ @DeleteMapping("/{dataset_id}")
+ @Operation(summary = "鍒犻櫎鍗曚釜鐭ヨ瘑搴�")
+ @Parameter(name = "dataset_id", description = "鐭ヨ瘑搴揑D", required = true)
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> delete(@PathVariable("dataset_id") String datasetId) {
+ // 鑾峰彇褰撳墠鐧诲綍鐢ㄦ埛ID
+ Long currentUserId = SecurityUser.getUserId();
+
+ // 鍏堣幏鍙栫幇鏈夌煡璇嗗簱淇℃伅浠ユ鏌ユ潈闄�
+ KnowledgeBaseDTO existingKnowledgeBase = knowledgeBaseService.getByDatasetId(datasetId);
+
+ // 妫�鏌ユ潈闄愶細鐢ㄦ埛鍙兘鍒犻櫎鑷繁鍒涘缓鐨勭煡璇嗗簱
+ if (existingKnowledgeBase.getCreator() == null || !existingKnowledgeBase.getCreator().equals(currentUserId)) {
+ throw new RenException(ErrorCode.NO_PERMISSION);
+ }
+
+ // [Architecture Fix] 閫氳繃缂栨帓灞傜骇鑱斿垹闄わ紝闃叉瀛ゅ効鏁版嵁骞惰В鍐冲惊鐜緷璧�
+ knowledgeManagerService.deleteDatasetWithFiles(datasetId);
+ return new Result<>();
+ }
+
+ @DeleteMapping("/batch")
+ @Operation(summary = "鎵归噺鍒犻櫎鐭ヨ瘑搴�")
+ @Parameter(name = "ids", description = "鐭ヨ瘑搴揑D鍒楄〃锛岀敤閫楀彿鍒嗛殧", required = true)
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> deleteBatch(@RequestParam("ids") String ids) {
+ if (StringUtils.isBlank(ids)) {
+ throw new RenException(ErrorCode.PARAMS_GET_ERROR);
+ }
+
+ // 鑾峰彇褰撳墠鐧诲綍鐢ㄦ埛ID
+ Long currentUserId = SecurityUser.getUserId();
+ List<String> idList = Arrays.asList(ids.split(","));
+ List<KnowledgeBaseDTO> knowledgeBaseDTOs = Optional.ofNullable(knowledgeBaseService.getByDatasetIdList(idList))
+ .orElseGet(ArrayList::new);
+ if (CollUtil.isNotEmpty(knowledgeBaseDTOs)) {
+ knowledgeBaseDTOs.forEach(item -> {
+ // 妫�鏌ユ潈闄愶細鐢ㄦ埛鍙兘鍒犻櫎鑷繁鍒涘缓鐨勭煡璇嗗簱
+ if (item.getCreator() == null || !item.getCreator().equals(currentUserId)) {
+ throw new RenException(ErrorCode.NO_PERMISSION);
+ }
+ // [Architecture Fix] 閫氳繃缂栨帓灞傜骇鑱斿垹闄�
+ knowledgeManagerService.deleteDatasetWithFiles(item.getDatasetId());
+ });
+ }
+ return new Result<>();
+ }
+
+ @GetMapping("/rag-models")
+ @Operation(summary = "鑾峰彇RAG妯″瀷鍒楄〃")
+ @RequiresPermissions("sys:role:normal")
+ public Result<List<ModelConfigEntity>> getRAGModels() {
+ List<ModelConfigEntity> result = knowledgeBaseService.getRAGModels();
+ return new Result<List<ModelConfigEntity>>().ok(result);
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/controller/KnowledgeFilesController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/controller/KnowledgeFilesController.java
new file mode 100644
index 0000000..4aa64b0
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/controller/KnowledgeFilesController.java
@@ -0,0 +1,231 @@
+package xiaozhi.modules.knowledge.controller;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springdoc.core.annotations.ParameterObject;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.utils.Result;
+import xiaozhi.modules.knowledge.dto.KnowledgeBaseDTO;
+import xiaozhi.modules.knowledge.dto.KnowledgeFilesDTO;
+import xiaozhi.modules.knowledge.dto.document.ChunkDTO;
+import xiaozhi.modules.knowledge.dto.document.DocumentDTO;
+import xiaozhi.modules.knowledge.dto.document.RetrievalDTO;
+import xiaozhi.modules.knowledge.service.KnowledgeBaseService;
+import xiaozhi.modules.knowledge.service.KnowledgeFilesService;
+import xiaozhi.modules.security.user.SecurityUser;
+
+@AllArgsConstructor
+@RestController
+@RequestMapping("/datasets/{dataset_id}")
+@Tag(name = "鐭ヨ瘑搴撴枃妗g鐞�")
+public class KnowledgeFilesController {
+
+ private final KnowledgeFilesService knowledgeFilesService;
+ private final KnowledgeBaseService knowledgeBaseService;
+
+ /**
+ * 楠岃瘉褰撳墠鐢ㄦ埛鏄惁鏈夋潈闄愭搷浣滄寚瀹氱煡璇嗗簱
+ *
+ * @param datasetId 鐭ヨ瘑搴揑D
+ */
+ private void validateKnowledgeBasePermission(String datasetId) {
+ // 鑾峰彇褰撳墠鐧诲綍鐢ㄦ埛ID
+ Long currentUserId = SecurityUser.getUserId();
+
+ // 鑾峰彇鐭ヨ瘑搴撲俊鎭�
+ KnowledgeBaseDTO knowledgeBase = knowledgeBaseService.getByDatasetId(datasetId);
+
+ // 妫�鏌ユ潈闄愶細鐢ㄦ埛鍙兘鎿嶄綔鑷繁鍒涘缓鐨勭煡璇嗗簱
+ if (knowledgeBase.getCreator() == null || !knowledgeBase.getCreator().equals(currentUserId)) {
+ throw new RenException(ErrorCode.NO_PERMISSION);
+ }
+ }
+
+ @GetMapping("/documents")
+ @Operation(summary = "鍒嗛〉鏌ヨ鏂囨。鍒楄〃")
+ @RequiresPermissions("sys:role:normal")
+ public Result<PageData<KnowledgeFilesDTO>> getPageList(
+ @PathVariable("dataset_id") String datasetId,
+ @RequestParam(required = false) String name,
+ @RequestParam(required = false) String status,
+ @RequestParam(required = false, defaultValue = "1") Integer page,
+ @RequestParam(required = false, defaultValue = "10") Integer page_size) {
+ // 楠岃瘉鐭ヨ瘑搴撴潈闄�
+ validateKnowledgeBasePermission(datasetId);
+
+ // 缁勮鍙傛暟
+ KnowledgeFilesDTO knowledgeFilesDTO = new KnowledgeFilesDTO();
+ knowledgeFilesDTO.setDatasetId(datasetId);
+ knowledgeFilesDTO.setName(name);
+ knowledgeFilesDTO.setStatus(status);
+ PageData<KnowledgeFilesDTO> pageData = knowledgeFilesService.getPageList(knowledgeFilesDTO, page, page_size);
+ return new Result<PageData<KnowledgeFilesDTO>>().ok(pageData);
+ }
+
+ @GetMapping("/documents/status/{status}")
+ @Operation(summary = "鏍规嵁鐘舵�佸垎椤垫煡璇㈡枃妗e垪琛�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<PageData<KnowledgeFilesDTO>> getPageListByStatus(
+ @PathVariable("dataset_id") String datasetId,
+ @PathVariable("status") String status,
+ @RequestParam(required = false, defaultValue = "1") Integer page,
+ @RequestParam(required = false, defaultValue = "10") Integer page_size) {
+ // 楠岃瘉鐭ヨ瘑搴撴潈闄�
+ validateKnowledgeBasePermission(datasetId);
+ // 缁勮鍙傛暟
+ KnowledgeFilesDTO knowledgeFilesDTO = new KnowledgeFilesDTO();
+ knowledgeFilesDTO.setDatasetId(datasetId);
+ knowledgeFilesDTO.setStatus(status);
+ PageData<KnowledgeFilesDTO> pageData = knowledgeFilesService.getPageList(knowledgeFilesDTO, page, page_size);
+ return new Result<PageData<KnowledgeFilesDTO>>().ok(pageData);
+ }
+
+ @PostMapping("/documents")
+ @Operation(summary = "涓婁紶鏂囨。鍒扮煡璇嗗簱")
+ @RequiresPermissions("sys:role:normal")
+ public Result<KnowledgeFilesDTO> uploadDocument(
+ @PathVariable("dataset_id") String datasetId,
+ @RequestParam("file") MultipartFile file,
+ @RequestParam(required = false) String name,
+ @RequestParam(required = false) String chunkMethod,
+ @RequestParam(required = false) String metaFields,
+ @RequestParam(required = false) String parserConfig) {
+
+ // 楠岃瘉鐭ヨ瘑搴撴潈闄�
+ validateKnowledgeBasePermission(datasetId);
+
+ KnowledgeFilesDTO resp = knowledgeFilesService.uploadDocument(datasetId, file, name,
+ metaFields != null ? parseJsonMap(metaFields) : null,
+ chunkMethod,
+ parserConfig != null ? parseJsonMap(parserConfig) : null);
+ return new Result<KnowledgeFilesDTO>().ok(resp);
+ }
+
+ @DeleteMapping("/documents")
+ @Operation(summary = "鎵归噺鍒犻櫎鏂囨。")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> delete(@PathVariable("dataset_id") String datasetId,
+ @RequestBody DocumentDTO.BatchIdReq req) {
+ // 楠岃瘉鐭ヨ瘑搴撴潈闄�
+ validateKnowledgeBasePermission(datasetId);
+
+ knowledgeFilesService.deleteDocuments(datasetId, req);
+ return new Result<>();
+ }
+
+ @DeleteMapping("/documents/{document_id}")
+ @Operation(summary = "鍒犻櫎鍗曚釜鏂囨。")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> deleteSingle(@PathVariable("dataset_id") String datasetId,
+ @PathVariable("document_id") String documentId) {
+ // 楠岃瘉鐭ヨ瘑搴撴潈闄�
+ validateKnowledgeBasePermission(datasetId);
+
+ DocumentDTO.BatchIdReq req = new DocumentDTO.BatchIdReq();
+ req.setIds(java.util.Collections.singletonList(documentId));
+ knowledgeFilesService.deleteDocuments(datasetId, req);
+ return new Result<>();
+ }
+
+ @PostMapping("/chunks")
+ @Operation(summary = "瑙f瀽鏂囨。锛堝垏鍧楋級")
+ @RequiresPermissions("sys:role:normal")
+ public Result<Void> parseDocuments(@PathVariable("dataset_id") String datasetId,
+ @RequestBody Map<String, List<String>> requestBody) {
+ // 楠岃瘉鐭ヨ瘑搴撴潈闄�
+ validateKnowledgeBasePermission(datasetId);
+
+ List<String> documentIds = requestBody.get("document_ids");
+ if (documentIds == null || documentIds.isEmpty()) {
+ return new Result<Void>().error("document_ids鍙傛暟涓嶈兘涓虹┖");
+ }
+
+ boolean success = knowledgeFilesService.parseDocuments(datasetId, documentIds);
+ if (success) {
+ return new Result<Void>();
+ } else {
+ return new Result<Void>().error("鏂囨。瑙f瀽澶辫触锛屾枃妗e彲鑳芥鍦ㄥ鐞嗕腑");
+ }
+ }
+
+ @GetMapping("/documents/{document_id}/chunks")
+ @Operation(summary = "鍒楀嚭鎸囧畾鏂囨。鐨勫垏鐗�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<ChunkDTO.ListVO> listChunks(
+ @PathVariable("dataset_id") String datasetId,
+ @PathVariable("document_id") String documentId,
+ @RequestParam(name = "page", required = false, defaultValue = "1") Integer page,
+ @RequestParam(name = "page_size", required = false, defaultValue = "10") Integer pageSize,
+ @RequestParam(required = false) String keywords,
+ @RequestParam(required = false) String id) {
+
+ // 楠岃瘉鏉冮檺 (鍐呴儴宸插寘鍚煡璇嗗簱瀛樺湪鎬ф牎楠屼笌褰掑睘鏉冩牎楠�)
+ validateKnowledgeBasePermission(datasetId);
+
+ // 鏋勫缓璇锋眰瀵硅薄
+ ChunkDTO.ListReq req = ChunkDTO.ListReq.builder()
+ .page(page)
+ .pageSize(pageSize)
+ .keywords(keywords)
+ .id(id)
+ .build();
+
+ // 璋冪敤鏈嶅姟灞傝幏鍙栧己绫诲瀷鍒囩墖鍒楄〃
+ ChunkDTO.ListVO result = knowledgeFilesService.listChunks(datasetId, documentId, req);
+ return new Result<ChunkDTO.ListVO>().ok(result);
+ }
+
+ @PostMapping("/retrieval-test")
+ @Operation(summary = "鍙洖娴嬭瘯")
+ @RequiresPermissions("sys:role:normal")
+ public Result<RetrievalDTO.ResultVO> retrievalTest(
+ @PathVariable("dataset_id") String datasetId,
+ @RequestBody RetrievalDTO.TestReq req) {
+
+ // 楠岃瘉鐭ヨ瘑搴撴潈闄�
+ validateKnowledgeBasePermission(datasetId);
+
+ // 涓氬姟涓嬫矇閫昏緫锛氬鏋滄湭鎸囧畾鐭ヨ瘑搴揑D锛屽垯璁句负褰撳墠璺緞涓殑 datasetId
+ if (req.getDatasetIds() == null || req.getDatasetIds().isEmpty()) {
+ req.setDatasetIds(java.util.Arrays.asList(datasetId));
+ }
+
+ // [Reinforce] 寮虹鎺у垎椤靛弬鏁帮紝闃叉 RAGFlow 绔嚭鐜� Negative Slicing 鎶ラ敊
+ if (req.getPage() == null || req.getPage() < 1) {
+ req.setPage(1);
+ }
+ if (req.getPageSize() == null || req.getPageSize() < 1) {
+ req.setPageSize(100);
+ }
+
+ // 璋冪敤妫�绱㈡湇鍔★紝杩斿洖寮虹被鍨嬭仛鍚堝璞�
+ RetrievalDTO.ResultVO result = knowledgeFilesService.retrievalTest(req);
+ return new Result<RetrievalDTO.ResultVO>().ok(result);
+ }
+
+ /**
+ * 瑙f瀽JSON瀛楃涓蹭负Map瀵硅薄
+ */
+ private Map<String, Object> parseJsonMap(String jsonString) {
+ try {
+ ObjectMapper objectMapper = new ObjectMapper();
+ return objectMapper.readValue(jsonString, new TypeReference<Map<String, Object>>() {
+ });
+ } catch (Exception e) {
+ throw new RuntimeException("瑙f瀽JSON瀛楃涓插け璐�: " + jsonString, e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dao/DocumentDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dao/DocumentDao.java
new file mode 100644
index 0000000..29f82bf
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dao/DocumentDao.java
@@ -0,0 +1,12 @@
+package xiaozhi.modules.knowledge.dao;
+
+import org.apache.ibatis.annotations.Mapper;
+import xiaozhi.common.dao.BaseDao;
+import xiaozhi.modules.knowledge.entity.DocumentEntity;
+
+/**
+ * 鏂囨。 DAO
+ */
+@Mapper
+public interface DocumentDao extends BaseDao<DocumentEntity> {
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dao/KnowledgeBaseDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dao/KnowledgeBaseDao.java
new file mode 100644
index 0000000..d095791
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dao/KnowledgeBaseDao.java
@@ -0,0 +1,35 @@
+package xiaozhi.modules.knowledge.dao;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import xiaozhi.common.dao.BaseDao;
+import xiaozhi.modules.knowledge.entity.KnowledgeBaseEntity;
+
+/**
+ * 鐭ヨ瘑搴撶煡璇嗗簱
+ */
+@Mapper
+public interface KnowledgeBaseDao extends BaseDao<KnowledgeBaseEntity> {
+
+ /**
+ * 鏍规嵁鐭ヨ瘑搴揑D鍒犻櫎鐩稿叧鐨勬彃浠舵槧灏勮褰�
+ *
+ * @param knowledgeBaseId 鐭ヨ瘑搴揑D
+ */
+ void deletePluginMappingByKnowledgeBaseId(@Param("knowledgeBaseId") String knowledgeBaseId);
+
+ /**
+ * 閫氱敤缁村害鍘熷瓙鏇存柊鐭ヨ瘑搴撶粺璁′俊鎭�
+ *
+ * @param datasetId 鏁版嵁闆咺D
+ * @param docDelta 鏂囨。鏁板閲�
+ * @param chunkDelta 鍒嗗潡鏁板閲�
+ * @param tokenDelta Token鏁板閲�
+ */
+ void updateStatsAfterChange(@Param("datasetId") String datasetId,
+ @Param("docDelta") Integer docDelta,
+ @Param("chunkDelta") Long chunkDelta,
+ @Param("tokenDelta") Long tokenDelta);
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/KnowledgeBaseDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/KnowledgeBaseDTO.java
new file mode 100644
index 0000000..5b77224
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/KnowledgeBaseDTO.java
@@ -0,0 +1,73 @@
+package xiaozhi.modules.knowledge.dto;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@Schema(description = "鐭ヨ瘑搴撶煡璇嗗簱")
+public class KnowledgeBaseDTO implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "鍞竴鏍囪瘑")
+ private String id;
+
+ @Schema(description = "鐭ヨ瘑搴揑D")
+ private String datasetId;
+
+ @Schema(description = "RAG妯″瀷閰嶇疆ID")
+ private String ragModelId;
+
+ @Schema(description = "鐭ヨ瘑搴撳悕绉�")
+ private String name;
+
+ @Schema(description = "鐭ヨ瘑搴撳ご鍍�(Base64)")
+ private String avatar;
+
+ @Schema(description = "鐭ヨ瘑搴撴弿杩�")
+ private String description;
+
+ @Schema(description = "宓屽叆妯″瀷鍚嶇О")
+ private String embeddingModel;
+
+ @Schema(description = "鏉冮檺璁剧疆: me/team")
+ private String permission;
+
+ @Schema(description = "鍒嗗潡鏂规硶")
+ private String chunkMethod;
+
+ @Schema(description = "瑙f瀽鍣ㄩ厤缃�(JSON String)")
+ private String parserConfig;
+
+ @Schema(description = "鍒嗗潡鎬绘暟")
+ private Long chunkCount;
+
+ @Schema(description = "鎬籘oken鏁�")
+ private Long tokenNum;
+
+ @Schema(description = "鐘舵��(0:绂佺敤 1:鍚敤)")
+ private Integer status;
+
+ @Schema(description = "鍒涘缓鑰�")
+ private Long creator;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ private Date createdAt;
+
+ @Schema(description = "鏇存柊鑰�")
+ private Long updater;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ private Date updatedAt;
+
+ @Schema(description = "鏂囨。鏁伴噺")
+ private Integer documentCount;
+
+ @Schema(description = "寮傚父鎻愮ず")
+ private String errorMessage;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/KnowledgeFilesDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/KnowledgeFilesDTO.java
new file mode 100644
index 0000000..1fa4db1
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/KnowledgeFilesDTO.java
@@ -0,0 +1,119 @@
+package xiaozhi.modules.knowledge.dto;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Map;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+@Data
+@Schema(description = "鐭ヨ瘑搴撴枃妗�")
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class KnowledgeFilesDTO implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+ @Schema(description = "鍞竴鏍囪瘑")
+ private String id;
+
+ @Schema(description = "鏂囨。ID")
+ private String documentId;
+
+ @Schema(description = "鐭ヨ瘑搴揑D")
+ private String datasetId;
+
+ @Schema(description = "鏂囨。鍚嶇О")
+ private String name;
+
+ @Schema(description = "鏂囨。绫诲瀷")
+ private String fileType;
+
+ @Schema(description = "鏂囦欢澶у皬锛堝瓧鑺傦級")
+ private Long fileSize;
+
+ @Schema(description = "鏂囦欢璺緞")
+ private String filePath;
+
+ @Schema(description = "瑙f瀽杩涘害 (0.0 ~ 1.0)")
+ private Double progress;
+
+ @Schema(description = "缂╃暐鍥� (Base64 鎴� URL)")
+ private String thumbnail;
+
+ @Schema(description = "瑙f瀽鑰楁椂 (鍗曚綅: 绉�)")
+ private Double processDuration;
+
+ @Schema(description = "鏉ユ簮绫诲瀷 (local, s3, url 绛�)")
+ private String sourceType;
+
+ @Schema(description = "鍏冩暟鎹瓧娈� (Map 鏍煎紡)")
+ private Map<String, Object> metaFields;
+
+ @Schema(description = "鍒嗗潡鏂规硶")
+ private String chunkMethod;
+
+ @Schema(description = "瑙f瀽鍣ㄩ厤缃�")
+ private Map<String, Object> parserConfig;
+
+ @Schema(description = "鍙敤鐘舵�� (1: 鍚敤/姝e父, 0: 绂佺敤/澶辨晥)")
+ private String status;
+
+ @Schema(description = "杩愯鐘舵�� (UNSTART/RUNNING/CANCEL/DONE/FAIL)")
+ private String run;
+
+ @Schema(description = "鍒涘缓鑰�")
+ private Long creator;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ private Date createdAt;
+
+ @Schema(description = "鏇存柊鑰�")
+ private Long updater;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ private Date updatedAt;
+
+ @Schema(description = "鍒嗗潡鏁伴噺")
+ private Integer chunkCount;
+
+ @Schema(description = "Token鏁伴噺")
+ private Long tokenCount;
+
+ @Schema(description = "瑙f瀽閿欒淇℃伅")
+ private String error;
+
+ // 鏂囨。瑙f瀽鐘舵�佸父閲忓畾涔�
+ private static final Integer STATUS_UNSTART = 0;
+ private static final Integer STATUS_RUNNING = 1;
+ private static final Integer STATUS_CANCEL = 2;
+ private static final Integer STATUS_DONE = 3;
+ private static final Integer STATUS_FAIL = 4;
+
+ /**
+ * 鑾峰彇鏂囨。瑙f瀽鐘舵�佺爜锛堝熀浜巖un瀛楁杞崲锛�
+ */
+ public Integer getParseStatusCode() {
+ if (run == null) {
+ return STATUS_UNSTART;
+ }
+
+ // RAGFlow鏍规嵁run瀛楁鐨勫�肩洿鎺ユ槧灏勫埌瀵瑰簲鐨勭姸鎬佺爜
+ switch (run.toUpperCase()) {
+ case "RUNNING":
+ return STATUS_RUNNING;
+ case "CANCEL":
+ return STATUS_CANCEL;
+ case "DONE":
+ return STATUS_DONE;
+ case "FAIL":
+ return STATUS_FAIL;
+ case "UNSTART":
+ default:
+ return STATUS_UNSTART;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/dataset/DatasetDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/dataset/DatasetDTO.java
new file mode 100644
index 0000000..e800a17
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/dataset/DatasetDTO.java
@@ -0,0 +1,449 @@
+package xiaozhi.modules.knowledge.dto.dataset;
+
+import lombok.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import jakarta.validation.constraints.*;
+
+/**
+ * 鐭ヨ瘑搴撶鐞嗚仛鍚� DTO
+ * <p>
+ * 瀹瑰櫒绫伙紝鍐呭惈鐭ヨ瘑搴撴ā鍧楁墍鏈夎姹�/鍝嶅簲瀵硅薄鐨勯潤鎬佸唴閮ㄧ被瀹氫箟銆�
+ * </p>
+ */
+@Schema(description = "鐭ヨ瘑搴撶鐞嗚仛鍚� DTO")
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class DatasetDTO {
+
+ // ========== 閫氱敤鍐呴儴绫� ==========
+
+ /**
+ * 瑙f瀽鍣ㄩ厤缃�
+ */
+ @Data
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Builder
+ @Schema(description = "瑙f瀽鍣ㄩ厤缃�")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class ParserConfig implements Serializable {
+
+ @Schema(description = "鍒嗗潡 token 鏁伴噺", example = "128")
+ @JsonProperty("chunk_token_num")
+ private Integer chunkTokenNum;
+
+ @Schema(description = "鍒嗛殧绗�", example = "\\n!?;銆傦紱锛侊紵")
+ private String delimiter;
+
+ @Schema(description = "甯冨眬璇嗗埆妯″瀷: DeepDOC / Simple", example = "DeepDOC")
+ @JsonProperty("layout_recognize")
+ private String layoutRecognize;
+
+ @Schema(description = "鏄惁灏� Excel 杞负 HTML", example = "false")
+ private Boolean html4excel;
+
+ @Schema(description = "鑷姩鐢熸垚鍏抽敭璇嶆暟閲� (0 琛ㄧず鍏抽棴)", example = "0")
+ @JsonProperty("auto_keywords")
+ private Integer autoKeywords;
+
+ @Schema(description = "鑷姩鐢熸垚闂鏁伴噺 (0 琛ㄧず鍏抽棴)", example = "0")
+ @JsonProperty("auto_questions")
+ private Integer autoQuestions;
+ }
+
+ // ========== 璇锋眰绫� ==========
+
+ /**
+ * 鍒涘缓鐭ヨ瘑搴撹姹� (鏄犲皠鎺ュ彛 1: create)
+ */
+ @Data
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Builder
+ @Schema(description = "鍒涘缓鐭ヨ瘑搴撹姹�")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class CreateReq implements Serializable {
+
+ @NotBlank(message = "鐭ヨ瘑搴撳悕绉颁笉鑳戒负绌�")
+ @Schema(description = "鐭ヨ瘑搴撳悕绉�", requiredMode = Schema.RequiredMode.REQUIRED, example = "my_dataset")
+ private String name;
+
+ @Schema(description = "鐭ヨ瘑搴撳ご鍍� (Base64 缂栫爜)", example = "")
+ private String avatar;
+
+ @Schema(description = "鐭ヨ瘑搴撴弿杩�", example = "鐢ㄤ簬瀛樺偍浜у搧鏂囨。")
+ private String description;
+
+ @Schema(description = "宓屽叆妯″瀷鍚嶇О", example = "BAAI/bge-large-zh-v1.5")
+ @JsonProperty("embedding_model")
+ private String embeddingModel;
+
+ @Schema(description = "鏉冮檺璁剧疆: me / team", example = "me")
+ private String permission;
+
+ @Schema(description = "鍒嗗潡鏂规硶: naive / manual / qa / table / paper / book / laws / presentation / picture / one / knowledge_graph / email", example = "naive")
+ @JsonProperty("chunk_method")
+ private String chunkMethod;
+
+ @Schema(description = "瑙f瀽鍣ㄩ厤缃�")
+ @JsonProperty("parser_config")
+ private ParserConfig parserConfig;
+ }
+
+ /**
+ * 鏇存柊鐭ヨ瘑搴撹姹� (鏄犲皠鎺ュ彛 4: update)
+ */
+ @Data
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Builder
+ @Schema(description = "鏇存柊鐭ヨ瘑搴撹姹�")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class UpdateReq implements Serializable {
+
+ @Schema(description = "鐭ヨ瘑搴撳悕绉�", example = "updated_dataset")
+ private String name;
+
+ @Schema(description = "鐭ヨ瘑搴撳ご鍍� (Base64 缂栫爜)", example = "")
+ private String avatar;
+
+ @Schema(description = "鐭ヨ瘑搴撴弿杩�", example = "鏇存柊鍚庣殑鎻忚堪")
+ private String description;
+
+ @Schema(description = "鏉冮檺璁剧疆: me / team", example = "team")
+ private String permission;
+
+ @Schema(description = "宓屽叆妯″瀷鍚嶇О", example = "BAAI/bge-large-zh-v1.5")
+ @JsonProperty("embedding_model")
+ private String embeddingModel;
+
+ @Schema(description = "鍒嗗潡鏂规硶: naive / manual / qa / table / paper / book / laws / presentation / picture / one / knowledge_graph / email", example = "naive")
+ @JsonProperty("chunk_method")
+ private String chunkMethod;
+
+ @Schema(description = "瑙f瀽鍣ㄩ厤缃�")
+ @JsonProperty("parser_config")
+ private ParserConfig parserConfig;
+
+ @JsonInclude(JsonInclude.Include.NON_NULL)
+ @Schema(description = "PageRank 鏉冮噸 (0-100)", example = "50")
+ private Integer pagerank;
+ }
+
+ /**
+ * 鏌ヨ鐭ヨ瘑搴撳垪琛ㄨ姹� (鏄犲皠鎺ュ彛 3: list_datasets)
+ */
+ @Data
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Builder
+ @Schema(description = "鏌ヨ鐭ヨ瘑搴撳垪琛ㄨ姹�")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class ListReq implements Serializable {
+
+ @Schema(description = "椤电爜 (浠� 1 寮�濮�)", example = "1")
+ private Integer page;
+
+ @Schema(description = "姣忛〉鏁伴噺", example = "30")
+ @JsonProperty("page_size")
+ private Integer pageSize;
+
+ @Schema(description = "鎺掑簭瀛楁: create_time / update_time", example = "create_time")
+ private String orderby;
+
+ @Schema(description = "鏄惁闄嶅簭", example = "true")
+ private Boolean desc;
+
+ @Schema(description = "鎸夊悕绉拌繃婊� (妯$硦鍖归厤)", example = "my_dataset")
+ private String name;
+
+ @Schema(description = "鎸夌煡璇嗗簱 ID 杩囨护", example = "abc123")
+ private String id;
+ }
+
+ /**
+ * 鎵归噺鍒犻櫎鐭ヨ瘑搴撹姹� (鏄犲皠鎺ュ彛 2: delete)
+ */
+ @Data
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Builder
+ @Schema(description = "鎵归噺鍒犻櫎鐭ヨ瘑搴撹姹�")
+ public static class BatchIdReq implements Serializable {
+
+ @NotNull(message = "鐭ヨ瘑搴� ID 鍒楄〃涓嶈兘涓虹┖")
+ @Size(min = 1, message = "鑷冲皯闇�瑕佷竴涓煡璇嗗簱 ID")
+ @Schema(description = "鐭ヨ瘑搴� ID 鍒楄〃", requiredMode = Schema.RequiredMode.REQUIRED, example = "[\"id1\", \"id2\"]")
+ private List<String> ids;
+ }
+
+ /**
+ * 杩愯 GraphRAG 璇锋眰
+ */
+ @Data
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Builder
+ @Schema(description = "杩愯 GraphRAG 璇锋眰")
+ public static class RunGraphRagReq implements Serializable {
+
+ @Schema(description = "瀹炰綋绫诲瀷鍒楄〃", example = "[\"person\", \"organization\"]")
+ @JsonProperty("entity_types")
+ private List<String> entityTypes;
+
+ @Schema(description = "鏋勫缓鏂规硶: light / fast / full", example = "light")
+ private String method;
+ }
+
+ /**
+ * 杩愯 RAPTOR 璇锋眰
+ */
+ @Data
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Builder
+ @Schema(description = "杩愯 RAPTOR 璇锋眰")
+ public static class RunRaptorReq implements Serializable {
+
+ @Schema(description = "鏈�澶ц仛绫绘暟", example = "64")
+ @JsonProperty("max_cluster")
+ private Integer maxCluster;
+
+ @Schema(description = "鑷畾涔夋彁绀鸿瘝", example = "璇锋�荤粨浠ヤ笅鍐呭...")
+ private String prompt;
+ }
+
+ /**
+ * 寮傛浠诲姟 ID 鍝嶅簲 VO (鏄犲皠鎺ュ彛 7/8: run_graphrag/run_raptor)
+ */
+ @Data
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Builder
+ @Schema(description = "寮傛浠诲姟 ID 鍝嶅簲")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class TaskIdVO implements Serializable {
+
+ @Schema(description = "GraphRAG 浠诲姟 ID", example = "task_uuid_12345678")
+ @JsonProperty("graphrag_task_id")
+ private String graphragTaskId;
+
+ @Schema(description = "RAPTOR 浠诲姟 ID", example = "task_uuid_87654321")
+ @JsonProperty("raptor_task_id")
+ private String raptorTaskId;
+ }
+
+ // ========== 鍝嶅簲绫� ==========
+
+ /**
+ * 鐭ヨ瘑搴撹鎯� VO (鏄犲皠鎺ュ彛 1/3 鐨勮繑鍥炴暟鎹」)
+ */
+ @Data
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Builder
+ @Schema(description = "鐭ヨ瘑搴撹鎯� VO")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class InfoVO implements Serializable {
+
+ @Schema(description = "鐭ヨ瘑搴� ID", example = "abc123")
+ private String id;
+
+ @Schema(description = "鐭ヨ瘑搴撳悕绉�", example = "my_dataset")
+ private String name;
+
+ @Schema(description = "鐭ヨ瘑搴撳ご鍍� (Base64 缂栫爜)", example = "")
+ private String avatar;
+
+ @Schema(description = "绉熸埛 ID", example = "tenant_001")
+ @JsonProperty("tenant_id")
+ private String tenantId;
+
+ @Schema(description = "鐭ヨ瘑搴撴弿杩�", example = "鐢ㄤ簬瀛樺偍浜у搧鏂囨。")
+ private String description;
+
+ @Schema(description = "宓屽叆妯″瀷鍚嶇О", example = "BAAI/bge-large-zh-v1.5")
+ @JsonProperty("embedding_model")
+ private String embeddingModel;
+
+ @Schema(description = "鏉冮檺璁剧疆: me / team", example = "me")
+ private String permission;
+
+ @Schema(description = "鍒嗗潡鏂规硶", example = "naive")
+ @JsonProperty("chunk_method")
+ private String chunkMethod;
+
+ @Schema(description = "瑙f瀽鍣ㄩ厤缃�")
+ @JsonProperty("parser_config")
+ private ParserConfig parserConfig;
+
+ @Schema(description = "鍒嗗潡鎬绘暟", example = "1024")
+ @JsonProperty("chunk_count")
+ private Long chunkCount;
+
+ @Schema(description = "鏂囨。鎬绘暟", example = "50")
+ @JsonProperty("document_count")
+ private Long documentCount;
+
+ @Schema(description = "鍒涘缓鏃堕棿 (鏃堕棿鎴�)", example = "1700000000000")
+ @JsonProperty("create_time")
+ private Long createTime;
+
+ @Schema(description = "鏇存柊鏃堕棿 (鏃堕棿鎴�)", example = "1700000001000")
+ @JsonProperty("update_time")
+ private Long updateTime;
+
+ @Schema(description = "鎬� Token 鏁�", example = "102400")
+ @JsonProperty("token_num")
+ private Long tokenNum;
+
+ @Schema(description = "鍒涘缓鏃ユ湡 (鏍煎紡: yyyy-MM-dd HH:mm:ss)")
+ @JsonProperty("create_date")
+ private String createDate;
+
+ @Schema(description = "鏈�鍚庢洿鏂版棩鏈� (鏍煎紡: yyyy-MM-dd HH:mm:ss)")
+ @JsonProperty("update_date")
+ private String updateDate;
+ }
+
+ /**
+ * 鎵归噺鎿嶄綔鍝嶅簲 VO
+ */
+ @Data
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Builder
+ @Schema(description = "鎵归噺鎿嶄綔鍝嶅簲 VO")
+ public static class BatchOperationVO implements Serializable {
+
+ @Schema(description = "鎴愬姛鎿嶄綔鏁伴噺", example = "5")
+ @JsonProperty("success_count")
+ private Integer successCount;
+
+ @Schema(description = "閿欒鍒楄〃")
+ private List<Object> errors;
+ }
+
+ // ========== 鐭ヨ瘑鍥捐氨鐩稿叧 ==========
+
+ /**
+ * 鐭ヨ瘑鍥捐氨鏁版嵁 VO (鏄犲皠鎺ュ彛 5: knowledge_graph)
+ */
+ @Data
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Builder
+ @Schema(description = "鐭ヨ瘑鍥捐氨鏁版嵁 VO")
+ public static class GraphVO implements Serializable {
+
+ @Schema(description = "鍥捐氨鑺傜偣鍒楄〃")
+ private List<Node> nodes;
+
+ @Schema(description = "鍥捐氨杈瑰垪琛�")
+ private List<Edge> edges;
+
+ @Schema(description = "鎬濈淮瀵煎浘鏁版嵁")
+ @JsonProperty("mind_map")
+ private Map<String, Object> mindMap;
+
+ /**
+ * 鍥捐氨鑺傜偣
+ */
+ @Data
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Builder
+ @Schema(description = "鍥捐氨鑺傜偣")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class Node implements Serializable {
+
+ @Schema(description = "鑺傜偣 ID", example = "node_001")
+ private String id;
+
+ @Schema(description = "鑺傜偣鏍囩", example = "浜у搧")
+ private String label;
+
+ @Schema(description = "PageRank 鍊�", example = "0.85")
+ private Double pagerank;
+
+ @Schema(description = "鑺傜偣棰滆壊", example = "#FF5733")
+ private String color;
+
+ @Schema(description = "鑺傜偣鍥剧墖 URL", example = "https://example.com/icon.png")
+ private String img;
+ }
+
+ /**
+ * 鍥捐氨杈�
+ */
+ @Data
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Builder
+ @Schema(description = "鍥捐氨杈�")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class Edge implements Serializable {
+
+ @Schema(description = "婧愯妭鐐� ID", example = "node_001")
+ private String source;
+
+ @Schema(description = "鐩爣鑺傜偣 ID", example = "node_002")
+ private String target;
+
+ @Schema(description = "杈规潈閲�", example = "0.75")
+ private Double weight;
+
+ @Schema(description = "杈规爣绛� (鍏崇郴鎻忚堪)", example = "灞炰簬")
+ private String label;
+ }
+ }
+
+ // ========== 寮傛浠诲姟杩借釜 (GraphRAG/RAPTOR) ==========
+
+ /**
+ * 寮傛浠诲姟杩借釜 VO (鏄犲皠鎺ュ彛 9/10: 浠诲姟杩涘害杩斿洖)
+ */
+ @Data
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Builder
+ @Schema(description = "寮傛浠诲姟杩借釜 VO")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class TaskTraceVO implements Serializable {
+
+ @Schema(description = "浠诲姟 ID", example = "task_001")
+ private String id;
+
+ @Schema(description = "鏂囨。 ID", example = "doc_001")
+ @JsonProperty("doc_id")
+ private String docId;
+
+ @Schema(description = "璧峰椤电爜", example = "1")
+ @JsonProperty("from_page")
+ private Integer fromPage;
+
+ @Schema(description = "缁撴潫椤电爜", example = "10")
+ @JsonProperty("to_page")
+ private Integer toPage;
+
+ @Schema(description = "杩涘害鐧惧垎姣� (0.0 - 1.0)", example = "0.75")
+ private Double progress;
+
+ @Schema(description = "杩涘害娑堟伅", example = "姝e湪澶勭悊绗� 5 椤�...")
+ @JsonProperty("progress_msg")
+ private String progressMsg;
+
+ @Schema(description = "鍒涘缓鏃堕棿 (鏃堕棿鎴�)", example = "1700000000000")
+ @JsonProperty("create_time")
+ private Long createTime;
+
+ @Schema(description = "鏇存柊鏃堕棿 (鏃堕棿鎴�)", example = "1700000001000")
+ @JsonProperty("update_time")
+ private Long updateTime;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/document/ChunkDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/document/ChunkDTO.java
new file mode 100644
index 0000000..376b08b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/document/ChunkDTO.java
@@ -0,0 +1,182 @@
+package xiaozhi.modules.knowledge.dto.document;
+
+import lombok.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.io.Serializable;
+import java.util.List;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import jakarta.validation.constraints.*;
+
+/**
+ * 鍒囩墖绠$悊鑱氬悎 DTO
+ */
+@Schema(description = "鍒囩墖绠$悊鑱氬悎 DTO")
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ChunkDTO {
+
+ /**
+ * 鏂板鍒囩墖璇锋眰鍙傛暟
+ */
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "鏂板鍒囩墖璇锋眰鍙傛暟")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class AddReq implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "鍒囩墖鍐呭", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotBlank(message = "鍒囩墖鍐呭涓嶈兘涓虹┖")
+ private String content;
+
+ @Schema(description = "閲嶈鍏抽敭璇嶅垪琛�")
+ @JsonProperty("important_keywords")
+ private List<String> importantKeywords;
+
+ @Schema(description = "棰勮闂鍒楄〃")
+ private List<String> questions;
+ }
+
+ /**
+ * 鏇存柊鍒囩墖璇锋眰鍙傛暟
+ */
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "鏇存柊鍒囩墖璇锋眰鍙傛暟")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class UpdateReq implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "鏂扮殑鍒囩墖鍐呭")
+ private String content;
+
+ @Schema(description = "鏇存柊鍏抽敭璇嶅垪琛� (瑕嗙洊鍘熸湁鍒楄〃)")
+ @JsonProperty("important_keywords")
+ private List<String> importantKeywords;
+
+ @Schema(description = "鍚敤/绂佺敤 (true: 鍚敤, false: 绂佺敤)")
+ private Boolean available;
+ }
+
+ /**
+ * 鑾峰彇鍒囩墖鍒楄〃璇锋眰鍙傛暟
+ */
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "鑾峰彇鍒囩墖鍒楄〃璇锋眰鍙傛暟")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class ListReq implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "椤电爜 (榛樿 1)")
+ private Integer page;
+
+ @Schema(description = "姣忛〉鏁伴噺 (榛樿 30)")
+ @JsonProperty("page_size")
+ private Integer pageSize;
+
+ @Schema(description = "鎼滅储鍏抽敭璇� (鍏ㄦ枃妫�绱�)")
+ private String keywords;
+
+ @Schema(description = "绮剧‘鍒囩墖 ID")
+ private String id;
+ }
+
+ /**
+ * 鎵归噺鍒犻櫎鍒囩墖璇锋眰鍙傛暟
+ */
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "鎵归噺鍒犻櫎鍒囩墖璇锋眰鍙傛暟")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class RemoveReq implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "鍒囩墖 ID 鍒楄〃", requiredMode = Schema.RequiredMode.REQUIRED)
+ @JsonProperty("chunk_ids")
+ @NotEmpty(message = "鍒囩墖ID鍒楄〃涓嶈兘涓虹┖")
+ private List<String> chunkIds;
+ }
+
+ /**
+ * 鏂囨。鍒囩墖淇℃伅 VO
+ */
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "鏂囨。鍒囩墖淇℃伅")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class InfoVO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "鍒囩墖 ID (閫氬父涓� document_id + 绱㈠紩)", requiredMode = Schema.RequiredMode.REQUIRED)
+ private String id;
+
+ @Schema(description = "鍒囩墖鏂囨湰鍐呭 (鍏ㄦ枃妫�绱㈢殑涓昏瀵硅薄)", requiredMode = Schema.RequiredMode.REQUIRED)
+ private String content;
+
+ @Schema(description = "鎵�灞炴枃妗� ID", requiredMode = Schema.RequiredMode.REQUIRED)
+ @JsonProperty("document_id")
+ private String documentId;
+
+ @Schema(description = "鏂囨。鍚嶇О / 鍏抽敭璇�")
+ @JsonProperty("docnm_kwd")
+ private String docnmKwd;
+
+ @Schema(description = "閲嶈鍏抽敭璇嶅垪琛� (鐢ㄤ簬鍏抽敭璇嶅寮烘绱�)")
+ @JsonProperty("important_keywords")
+ private List<String> importantKeywords;
+
+ @Schema(description = "棰勮闂鍒楄〃 (鐢ㄤ簬 Q&A 妯″紡澧炲己)")
+ private List<String> questions;
+
+ @Schema(description = "鍏宠仈鐨勫浘鐗� ID")
+ @JsonProperty("image_id")
+ private String imageId;
+
+ @Schema(description = "鎵�灞炵煡璇嗗簱 ID")
+ @JsonProperty("dataset_id")
+ private String datasetId;
+
+ @Schema(description = "鍒囩墖鏄惁鍙敤 (true: 鍙備笌妫�绱�, false: 琚鐢�)")
+ private Boolean available;
+
+ @Schema(description = "鍒囩墖鍦ㄥ師鏂囦腑鐨勪綅缃储寮曞垪琛� (RAGFlow杩斿洖宓屽鏁扮粍, 濡� [[start, end, filename]])")
+ private List<List<Object>> positions;
+
+ @Schema(description = "Token ID 鍒楄〃")
+ @JsonProperty("token")
+ private List<Integer> token;
+ }
+
+ /**
+ * 鍒嗙墖鍒楄〃鑱氬悎鍝嶅簲
+ */
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "鍒嗙墖鍒楄〃鑱氬悎鍝嶅簲")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class ListVO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "鍒囩墖淇℃伅鍒楄〃")
+ private List<InfoVO> chunks;
+
+ @Schema(description = "鍏宠仈鐨勬枃妗h缁嗕俊鎭�")
+ private DocumentDTO.InfoVO doc;
+
+ @Schema(description = "鎬昏褰曟暟")
+ private Long total;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/document/DocumentDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/document/DocumentDTO.java
new file mode 100644
index 0000000..b817606
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/document/DocumentDTO.java
@@ -0,0 +1,407 @@
+package xiaozhi.modules.knowledge.dto.document;
+
+import lombok.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonAlias;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import jakarta.validation.constraints.*;
+
+/**
+ * 鏂囨。绠$悊鑱氬悎 DTO
+ */
+@Schema(description = "鏂囨。绠$悊鑱氬悎 DTO")
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class DocumentDTO {
+
+ /**
+ * 涓婁紶鏂囨。璇锋眰鍙傛暟
+ */
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "涓婁紶鏂囨。璇锋眰鍙傛暟")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class UploadReq implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "鐭ヨ瘑搴� ID (蹇呴』鎸囧畾褰掑睘)", requiredMode = Schema.RequiredMode.REQUIRED)
+ @JsonProperty("dataset_id")
+ @NotBlank(message = "鐭ヨ瘑搴揑D涓嶈兘涓虹┖")
+ private String datasetId;
+
+ @Schema(description = "鏂囦欢鍚� (濡傛灉鎸囧畾锛屽垯瑕嗙洊鍘熷鏂囦欢鍚�)")
+ private String name;
+
+ @Schema(description = "鍒嗗潡鏂规硶")
+ @JsonProperty("chunk_method")
+ private DocumentDTO.InfoVO.ChunkMethod chunkMethod;
+
+ @Schema(description = "瑙f瀽鍙傛暟閰嶇疆")
+ @JsonProperty("parser_config")
+ private DocumentDTO.InfoVO.ParserConfig parserConfig;
+
+ @Schema(description = "铏氭嫙鏂囦欢澶硅矾寰� (榛樿涓� /)")
+ @JsonProperty("parent_path")
+ private String parentPath;
+
+ @Schema(description = "鍏冩暟鎹瓧娈�")
+ @JsonProperty("meta")
+ private Map<String, Object> metaFields;
+
+ @Schema(description = "鏂囦欢浜岃繘鍒舵祦 (鏀寔 PDF, DOCX, TXT, MD 绛夊绉嶆牸寮�)", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "涓婁紶鏂囦欢涓嶈兘涓虹┖")
+ private org.springframework.web.multipart.MultipartFile file;
+ }
+
+ /**
+ * 鏇存柊鏂囨。璇锋眰鍙傛暟
+ */
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "鏇存柊鏂囨。璇锋眰鍙傛暟")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class UpdateReq implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "鏂版枃妗e悕绉� (蹇呴』鍖呭惈鏂囦欢鍚庣紑锛屼笖涓嶈兘鏇存敼鍘熷绫诲瀷)")
+ private String name;
+
+ @Schema(description = "鍚敤/绂佺敤鐘舵�� (true: 鍚敤, false: 绂佺敤; 绂佺敤鍚庝笉鍙備笌妫�绱�)")
+ private Boolean enabled;
+
+ @Schema(description = "鏂拌В鏋愭柟娉� (淇敼姝ら」浼氶噸缃В鏋愮姸鎬�)")
+ @JsonProperty("chunk_method")
+ private InfoVO.ChunkMethod chunkMethod;
+
+ @Schema(description = "鏂拌В鏋愬櫒璇︾粏閰嶇疆 (搴斾笌 chunk_method 閰嶅浣跨敤)")
+ @JsonProperty("parser_config")
+ private InfoVO.ParserConfig parserConfig;
+ }
+
+ /**
+ * 鑾峰彇鏂囨。鍒楄〃璇锋眰鍙傛暟
+ */
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "鑾峰彇鏂囨。鍒楄〃璇锋眰鍙傛暟")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class ListReq implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "椤电爜 (榛樿: 1)")
+ private Integer page;
+
+ @Schema(description = "姣忛〉鏁伴噺 (榛樿: 30)")
+ @JsonProperty("page_size")
+ private Integer pageSize;
+
+ @Schema(description = "鎺掑簭瀛楁 (鍙��: create_time, name, size; 榛樿: create_time)")
+ private String orderby;
+
+ @Schema(description = "鏄惁闄嶅簭鎺掑垪 (true: 鏈�鏂�/鏈�澶у湪鍓�; false: 鏈�鏃�/鏈�灏忓湪鍓�; 榛樿: true)")
+ private Boolean desc;
+
+ @Schema(description = "绮剧‘绛涢��: 鏂囨。 ID")
+ private String id;
+
+ @Schema(description = "绮剧‘绛涢��: 鏂囨。瀹屾暣鍚嶇О (鍚悗缂�)")
+ private String name;
+
+ @Schema(description = "妯$硦鎼滅储: 鏂囨。鍚嶇О鍏抽敭璇�")
+ private String keywords;
+
+ @Schema(description = "绛涢��: 鏂囦欢鍚庣紑鍒楄〃 (濡� ['pdf', 'docx'])")
+ private List<String> suffix;
+
+ @Schema(description = "绛涢��: 杩愯鐘舵�佸垪琛�")
+ private List<InfoVO.RunStatus> run;
+
+ @Schema(description = "绛涢��: 璧峰鍒涘缓鏃堕棿 (鏃堕棿鎴�, 姣)")
+ @JsonProperty("create_time_from")
+ private Long createTimeFrom;
+
+ @Schema(description = "绛涢��: 缁撴潫鍒涘缓鏃堕棿 (鏃堕棿鎴�, 姣)")
+ @JsonProperty("create_time_to")
+ private Long createTimeTo;
+ }
+
+ /**
+ * 鎵归噺鏂囨。鎿嶄綔璇锋眰鍙傛暟 (鐢ㄤ簬鍒犻櫎銆佽В鏋愮瓑)
+ */
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "鎵归噺鏂囨。鎿嶄綔璇锋眰鍙傛暟")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class BatchIdReq implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "鏂囨。 ID 鍒楄〃", requiredMode = Schema.RequiredMode.REQUIRED)
+ @JsonProperty("ids") // 涓轰簡鍏煎鎬э紝涔熷彲浠ヨ�冭檻鏀寔 document_ids锛屼絾杩欓噷缁熶竴鍙� ids
+ @JsonAlias("document_ids")
+ @NotEmpty(message = "鏂囨。ID鍒楄〃涓嶈兘涓虹┖")
+ private List<String> ids;
+ }
+
+ /**
+ * 鐭ヨ瘑搴撴枃妗d俊鎭� VO
+ */
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "鐭ヨ瘑搴撴枃妗d俊鎭�")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class InfoVO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "鏂囨。 ID (鍞竴鏍囪瘑)", requiredMode = Schema.RequiredMode.REQUIRED)
+ private String id;
+
+ @Schema(description = "鏂囨。缂╃暐鍥� URL (Base64 鎴� 閾炬帴)")
+ private String thumbnail;
+
+ @Schema(description = "鎵�灞炵煡璇嗗簱 ID", requiredMode = Schema.RequiredMode.REQUIRED)
+ @JsonProperty("dataset_id")
+ private String datasetId;
+
+ @Schema(description = "鏂囨。瑙f瀽鏂规硶 (鍐冲畾浜嗘枃妗e浣曡鍒囩墖)")
+ @JsonProperty("chunk_method")
+ private ChunkMethod chunkMethod;
+
+ @Schema(description = "鍏宠仈鐨� ETL Pipeline ID (濡傛湁)")
+ @JsonProperty("pipeline_id")
+ private String pipelineId;
+
+ @Schema(description = "鏂囨。瑙f瀽鍣ㄧ殑璇︾粏閰嶇疆")
+ @JsonProperty("parser_config")
+ private ParserConfig parserConfig;
+
+ @Schema(description = "鏉ユ簮绫诲瀷 (濡� local, s3, url 绛�)")
+ @JsonProperty("source_type")
+ private String sourceType;
+
+ @Schema(description = "鏂囨。鏂囦欢绫诲瀷 (濡� pdf, docx, txt)", requiredMode = Schema.RequiredMode.REQUIRED)
+ private String type;
+
+ @Schema(description = "鍒涘缓鑰呯敤鎴� ID")
+ @JsonProperty("created_by")
+ private String createdBy;
+
+ @Schema(description = "鏂囨。鍚嶇О (鍖呭惈鎵╁睍鍚�)", requiredMode = Schema.RequiredMode.REQUIRED)
+ private String name;
+
+ @Schema(description = "鏂囦欢瀛樺偍璺緞鎴栦綅缃爣璇�")
+ private String location;
+
+ @Schema(description = "鏂囦欢澶у皬 (鍗曚綅: Bytes)")
+ private Long size;
+
+ @Schema(description = "鍖呭惈鐨� Token 鎬绘暟 (瑙f瀽鍚庣粺璁�)")
+ @JsonProperty("token_count")
+ private Long tokenCount;
+
+ @Schema(description = "鍖呭惈鐨勫垏鐗� (Chunk) 鎬绘暟")
+ @JsonProperty("chunk_count")
+ private Long chunkCount;
+
+ @Schema(description = "瑙f瀽杩涘害 (0.0 ~ 1.0, 1.0 琛ㄧず瀹屾垚)")
+ private Double progress;
+
+ @Schema(description = "褰撳墠杩涘害鎻忚堪鎴栭敊璇俊鎭�")
+ @JsonProperty("progress_msg")
+ private String progressMsg;
+
+ @Schema(description = "寮�濮嬪鐞嗙殑鏃堕棿鎴� (RAGFlow杩斿洖RFC1123鏍煎紡)")
+ @JsonProperty("process_begin_at")
+ private String processBeginAt;
+
+ @Schema(description = "澶勭悊鎬昏�楁椂 (鍗曚綅: 绉�)")
+ @JsonProperty("process_duration")
+ private Double processDuration;
+
+ @Schema(description = "鑷畾涔夊厓鏁版嵁瀛楁 (Key-Value 閿�煎)")
+ @JsonProperty("meta_fields")
+ private Map<String, Object> metaFields;
+
+ @Schema(description = "鏂囦欢鍚庣紑鍚� (涓嶅惈鐐�)")
+ private String suffix;
+
+ @Schema(description = "鏂囨。瑙f瀽杩愯鐘舵��")
+ private RunStatus run;
+
+ @Schema(description = "鏂囨。鍙敤鐘舵�� (1: 鍚敤/姝e父, 0: 绂佺敤/澶辨晥)", requiredMode = Schema.RequiredMode.REQUIRED)
+ private String status;
+
+ @Schema(description = "鍒涘缓鏃堕棿 (鏃堕棿鎴�, 姣)", requiredMode = Schema.RequiredMode.REQUIRED)
+ @JsonProperty("create_time")
+ private Long createTime;
+
+ @Schema(description = "鍒涘缓鏃ユ湡 (RAGFlow杩斿洖RFC1123鏍煎紡)")
+ @JsonProperty("create_date")
+ private String createDate;
+
+ @Schema(description = "鏈�鍚庢洿鏂版椂闂� (鏃堕棿鎴�, 姣)")
+ @JsonProperty("update_time")
+ private Long updateTime;
+
+ @Schema(description = "鏈�鍚庢洿鏂版棩鏈� (RAGFlow杩斿洖RFC1123鏍煎紡)")
+ @JsonProperty("update_date")
+ private String updateDate;
+
+ /**
+ * 瑙f瀽鏂规硶鏋氫妇 (ChunkMethod)
+ */
+ public enum ChunkMethod {
+ @Schema(description = "閫氱敤妯″紡: 閫傜敤浜庡ぇ澶氭暟绾枃鏈垨娣峰悎鏂囨。")
+ @JsonProperty("naive")
+ NAIVE,
+ @Schema(description = "鎵嬪姩妯″紡: 鍏佽鐢ㄦ埛鎵嬪姩缂栬緫鍒囩墖")
+ @JsonProperty("manual")
+ MANUAL,
+ @Schema(description = "闂瓟妯″紡: 涓撻棬浼樺寲 Q&A 鏍煎紡鐨勬枃妗�")
+ @JsonProperty("qa")
+ QA,
+ @Schema(description = "琛ㄦ牸妯″紡: 涓撻棬浼樺寲 Excel 鎴� CSV 绛夎〃鏍兼暟鎹�")
+ @JsonProperty("table")
+ TABLE,
+ @Schema(description = "璁烘枃妯″紡: 閽堝瀛︽湳璁烘枃鎺掔増浼樺寲")
+ @JsonProperty("paper")
+ PAPER,
+ @Schema(description = "涔︾睄妯″紡: 閽堝涔︾睄绔犺妭缁撴瀯浼樺寲")
+ @JsonProperty("book")
+ BOOK,
+ @Schema(description = "娉曞緥娉曡妯″紡: 閽堝娉曞緥鏉℃枃缁撴瀯浼樺寲")
+ @JsonProperty("laws")
+ LAWS,
+ @Schema(description = "婕旂ず鏂囩妯″紡: 閽堝 PPT 绛夋紨绀烘枃浠朵紭鍖�")
+ @JsonProperty("presentation")
+ PRESENTATION,
+ @Schema(description = "鍥剧墖妯″紡: 閽堝鍥剧墖鍐呭杩涜 OCR 鍜屾弿杩�")
+ @JsonProperty("picture")
+ PICTURE,
+ @Schema(description = "鏁翠綋妯″紡: 灏嗘暣涓枃妗d綔涓轰竴涓垏鐗�")
+ @JsonProperty("one")
+ ONE,
+ @Schema(description = "鐭ヨ瘑鍥捐氨妯″紡: 鎻愬彇瀹炰綋鍏崇郴鏋勫缓鍥捐氨")
+ @JsonProperty("knowledge_graph")
+ KNOWLEDGE_GRAPH,
+ @Schema(description = "閭欢妯″紡: 閽堝閭欢鏍煎紡浼樺寲")
+ @JsonProperty("email")
+ EMAIL;
+ }
+
+ /**
+ * 杩愯鐘舵�佹灇涓� (RunStatus)
+ */
+ public enum RunStatus {
+ @Schema(description = "鏈紑濮�: 绛夊緟瑙f瀽闃熷垪")
+ @JsonProperty("UNSTART")
+ UNSTART,
+ @Schema(description = "杩涜涓�: 姝e湪瑙f瀽鎴栫储寮�")
+ @JsonProperty("RUNNING")
+ RUNNING,
+ @Schema(description = "宸插彇娑�: 鐢ㄦ埛鎵嬪姩鍙栨秷")
+ @JsonProperty("CANCEL")
+ CANCEL,
+ @Schema(description = "宸插畬鎴�: 瑙f瀽鎴愬姛")
+ @JsonProperty("DONE")
+ DONE,
+ @Schema(description = "澶辫触: 瑙f瀽杩囩▼涓嚭閿�")
+ @JsonProperty("FAIL")
+ FAIL;
+ }
+
+ /**
+ * 甯冨眬璇嗗埆妯″瀷鏋氫妇
+ */
+ public enum LayoutRecognize {
+ @Schema(description = "娣卞害鏂囨。鐞嗚В妯″瀷: 閫傚悎澶嶆潅鎺掔増")
+ @JsonProperty("DeepDOC")
+ DeepDOC,
+ @Schema(description = "绠�鍗曡鍒欐ā鍨�: 閫傚悎绾枃鏈�")
+ @JsonProperty("Simple")
+ Simple;
+ }
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "鏂囨。瑙f瀽鍣ㄥ弬鏁伴厤缃�")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class ParserConfig implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "鍒囩墖鏈�澶� Token 鏁� (寤鸿鍊�: 512, 1024, 2048)")
+ @JsonProperty("chunk_token_num")
+ private Integer chunkTokenNum;
+
+ @Schema(description = "鍒嗘鍒嗛殧绗� (鏀寔杞箟瀛楃, 濡� \\n)")
+ private String delimiter;
+
+ @Schema(description = "甯冨眬璇嗗埆妯″瀷 (DeepDOC/Simple)")
+ @JsonProperty("layout_recognize")
+ private LayoutRecognize layoutRecognize;
+
+ @Schema(description = "鏄惁灏� Excel 杞崲涓� HTML 琛ㄦ牸")
+ @JsonProperty("html4excel")
+ private Boolean html4excel;
+
+ @Schema(description = "鑷姩鎻愬彇鍏抽敭璇嶆暟閲� (0 琛ㄧず涓嶆彁鍙�)")
+ @JsonProperty("auto_keywords")
+ private Integer autoKeywords;
+
+ @Schema(description = "鑷姩鐢熸垚闂鏁伴噺 (0 琛ㄧず涓嶇敓鎴�)")
+ @JsonProperty("auto_questions")
+ private Integer autoQuestions;
+
+ @Schema(description = "鑷姩鐢熸垚鏍囩鏁伴噺")
+ @JsonProperty("topn_tags")
+ private Integer topnTags;
+
+ @Schema(description = "RAPTOR 楂樼骇绱㈠紩閰嶇疆")
+ private RaptorConfig raptor;
+
+ @Schema(description = "GraphRAG 鐭ヨ瘑鍥捐氨閰嶇疆")
+ @JsonProperty("graphrag")
+ private GraphRagConfig graphRag;
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "RAPTOR (閫掑綊鎽樿绱㈠紩) 閰嶇疆")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class RaptorConfig implements Serializable {
+ private static final long serialVersionUID = 1L;
+ @Schema(description = "鏄惁鍚敤 RAPTOR 绱㈠紩")
+ @JsonProperty("use_raptor")
+ private Boolean useRaptor;
+ }
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "GraphRAG (鍥惧寮烘绱�) 閰嶇疆")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class GraphRagConfig implements Serializable {
+ private static final long serialVersionUID = 1L;
+ @Schema(description = "鏄惁鍚敤 GraphRAG 绱㈠紩")
+ @JsonProperty("use_graphrag")
+ private Boolean useGraphRag;
+ }
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/document/RetrievalDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/document/RetrievalDTO.java
new file mode 100644
index 0000000..3184018
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/document/RetrievalDTO.java
@@ -0,0 +1,307 @@
+package xiaozhi.modules.knowledge.dto.document;
+
+import lombok.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import jakarta.validation.constraints.*;
+
+/**
+ * 妫�绱笌鍏冩暟鎹鐞嗚仛鍚� DTO
+ */
+@Schema(description = "妫�绱笌鍏冩暟鎹鐞嗚仛鍚� DTO")
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class RetrievalDTO {
+
+ /**
+ * 鏂囨。鑱氬悎淇℃伅 (VO)
+ */
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "鏂囨。鑱氬悎淇℃伅")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class DocAggVO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "鏂囨。鍚嶇О")
+ @JsonProperty("doc_name")
+ private String docName;
+
+ @Schema(description = "鏂囨。 ID")
+ @JsonProperty("doc_id")
+ private String docId;
+
+ @Schema(description = "鏁伴噺")
+ private Integer count;
+ }
+
+ /**
+ * 妫�绱㈡祴璇曡姹傚弬鏁�
+ */
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "妫�绱㈡祴璇曡姹傚弬鏁�")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ @JsonInclude(JsonInclude.Include.NON_NULL)
+ public static class TestReq implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "鐭ヨ瘑搴� ID 鍒楄〃", requiredMode = Schema.RequiredMode.REQUIRED)
+ @JsonProperty("dataset_ids")
+ @NotEmpty(message = "鐭ヨ瘑搴揑D鍒楄〃涓嶈兘涓虹┖")
+ private List<String> datasetIds;
+
+ @Schema(description = "鏂囨。 ID 鍒楄〃 (鍙�夛紝鐢ㄤ簬闄愬畾妫�绱㈣寖鍥�)")
+ @JsonProperty("document_ids")
+ private List<String> documentIds;
+
+ @Schema(description = "妫�绱㈤棶棰�", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotBlank(message = "妫�绱㈤棶棰樹笉鑳戒负绌�")
+ private String question;
+
+ @Schema(description = "椤电爜 (榛樿 1)")
+ private Integer page;
+
+ @Schema(description = "姣忛〉鏁伴噺 (榛樿 10)")
+ @JsonProperty("page_size")
+ private Integer pageSize;
+
+ @Schema(description = "鐩镐技搴﹂槇鍊� (榛樿 0.2)")
+ @JsonProperty("similarity_threshold")
+ private Float similarityThreshold;
+
+ @Schema(description = "鍚戦噺鐩镐技搴︽潈閲� (榛樿 0.3)")
+ @JsonProperty("vector_similarity_weight")
+ private Float vectorSimilarityWeight;
+
+ @Schema(description = "杩斿洖 Top K 鍒囩墖 (榛樿 1024)")
+ @JsonProperty("top_k")
+ private Integer topK;
+
+ @Schema(description = "閲嶆帓搴忔ā鍨� ID")
+ @JsonProperty("rerank_id")
+ private String rerankId;
+
+ @Schema(description = "鏄惁楂樹寒鍏抽敭璇�")
+ private Boolean highlight;
+
+ @Schema(description = "鏄惁鍚敤鍏抽敭璇嶆绱�")
+ private Boolean keyword;
+
+ @Schema(description = "璺ㄨ瑷�缈昏瘧鍒楄〃 (鍙��)")
+ @JsonProperty("cross_languages")
+ private List<String> crossLanguages;
+
+ @Schema(description = "鍏冩暟鎹繃婊ゆ潯浠� (JSON 瀵硅薄)")
+ @JsonProperty("metadata_condition")
+ private Map<String, Object> metadataCondition;
+ }
+
+ /**
+ * 妫�绱㈠懡涓粨鏋� (VO)
+ */
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "妫�绱㈠懡涓垏鐗囪鎯�")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class HitVO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "鍒囩墖 ID", requiredMode = Schema.RequiredMode.REQUIRED)
+ private String id;
+
+ @Schema(description = "鍒囩墖鍐呭", requiredMode = Schema.RequiredMode.REQUIRED)
+ private String content;
+
+ @Schema(description = "鎵�灞炴枃妗� ID", requiredMode = Schema.RequiredMode.REQUIRED)
+ @JsonProperty("document_id")
+ private String documentId;
+
+ @Schema(description = "鎵�灞炵煡璇嗗簱 ID")
+ @JsonProperty("dataset_id")
+ private String datasetId;
+
+ @Schema(description = "鏂囨。鍚嶇О")
+ @JsonProperty("document_name")
+ private String documentName;
+
+ @Schema(description = "鏂囨。鍏抽敭璇�")
+ @JsonProperty("document_keyword")
+ private String documentKeyword;
+
+ @Schema(description = "缁煎悎鐩镐技搴�", requiredMode = Schema.RequiredMode.REQUIRED)
+ private Float similarity;
+
+ @Schema(description = "鍚戦噺鐩镐技搴�")
+ @JsonProperty("vector_similarity")
+ private Float vectorSimilarity;
+
+ @Schema(description = "鍏抽敭璇嶇浉浼煎害")
+ @JsonProperty("term_similarity")
+ private Float termSimilarity;
+
+ @Schema(description = "绱㈠紩浣嶇疆")
+ private Integer index;
+
+ @Schema(description = "楂樹寒鍐呭")
+ private String highlight;
+
+ @Schema(description = "閲嶈鍏抽敭璇嶅垪琛�")
+ @JsonProperty("important_keywords")
+ private List<String> importantKeywords;
+
+ @Schema(description = "棰勮闂鍒楄〃")
+ private List<String> questions;
+
+ @Schema(description = "鍥剧墖 ID")
+ @JsonProperty("image_id")
+ private String imageId;
+
+ @Schema(description = "浣嶇疆绱㈠紩 (RAGFlow杩斿洖宓屽鏁扮粍, 濡� [[start, end, filename]])")
+ private Object positions;
+ }
+
+ /**
+ * 鐭ヨ瘑搴撳厓鏁版嵁鎽樿 (VO)
+ */
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "鐭ヨ瘑搴撳厓鏁版嵁鎽樿淇℃伅")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class MetaSummaryVO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "鏂囨。鎬绘暟", requiredMode = Schema.RequiredMode.REQUIRED)
+ @JsonProperty("total_doc_count")
+ private Long totalDocCount;
+
+ @Schema(description = "Token 鎬绘暟", requiredMode = Schema.RequiredMode.REQUIRED)
+ @JsonProperty("total_token_count")
+ private Long totalTokenCount;
+
+ @Schema(description = "鏂囦欢绫诲瀷鍒嗗竷 (key: 鏂囦欢鍚庣紑, value: 鏁伴噺)")
+ @JsonProperty("file_type_distribution")
+ private Map<String, Long> fileTypeDistribution;
+
+ @Schema(description = "鏂囩姸鎬佸垎甯� (key: 鐘舵�佺爜, value: 鏁伴噺)")
+ @JsonProperty("status_distribution")
+ private Map<String, Long> statusDistribution;
+
+ @Schema(description = "鑷畾涔夊厓鏁版嵁缁熻 (key: 瀛楁鍚�, value: 鏁伴噺/鍊�)")
+ @JsonProperty("custom_metadata")
+ private Map<String, Object> customMetadata;
+ }
+
+ /**
+ * 鎵归噺鏇存柊鍏冩暟鎹姹傚弬鏁�
+ */
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "鎵归噺鏇存柊鍏冩暟鎹姹傚弬鏁�")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class MetaBatchReq implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "绛涢�夊櫒: 鐢ㄤ簬鎸囧畾瑕佹洿鏂扮殑鏂囨。鑼冨洿 (榛樿鍏ㄩ儴)")
+ private Selector selector;
+
+ @Schema(description = "鏂板鎴栨洿鏂扮殑鍏冩暟鎹垪琛�")
+ private List<UpdateItem> updates;
+
+ @Schema(description = "闇�瑕佸垹闄ょ殑鍏冩暟鎹敭鍒楄〃")
+ private List<DeleteItem> deletes;
+
+ /**
+ * 鏂囨。绛涢�夊櫒
+ */
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "鍏冩暟鎹洿鏂扮瓫閫夊櫒")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class Selector implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "鎸囧畾鏂囨。 ID 鍒楄〃")
+ @JsonProperty("document_ids")
+ private List<String> documentIds;
+
+ @Schema(description = "鍏冩暟鎹潯浠跺尮閰� (key: 瀛楁鍚�, value: 鍖归厤鍊�)")
+ @JsonProperty("metadata_condition")
+ private Map<String, Object> metadataCondition;
+ }
+
+ /**
+ * 鏇存柊椤�
+ */
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "鍏冩暟鎹洿鏂伴」")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class UpdateItem implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "鍏冩暟鎹敭鍚�", requiredMode = Schema.RequiredMode.REQUIRED)
+ private String key;
+
+ @Schema(description = "鍏冩暟鎹��", requiredMode = Schema.RequiredMode.REQUIRED)
+ private Object value;
+ }
+
+ /**
+ * 鍒犻櫎椤�
+ */
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "鍏冩暟鎹垹闄ら」")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class DeleteItem implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "闇�鍒犻櫎鐨勫厓鏁版嵁閿悕", requiredMode = Schema.RequiredMode.REQUIRED)
+ private String key;
+ }
+ }
+
+ /**
+ * 鍙洖娴嬭瘯缁撴灉鑱氬悎鍝嶅簲
+ */
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Schema(description = "鍙洖娴嬭瘯缁撴灉鑱氬悎鍝嶅簲")
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class ResultVO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "妫�绱㈠懡涓殑鍒囩墖鍒楄〃")
+ private List<HitVO> chunks;
+
+ @Schema(description = "鏂囨。鍒嗗竷缁熻")
+ @JsonProperty("doc_aggs")
+ private List<DocAggVO> docAggs;
+
+ @Schema(description = "鎬诲懡涓褰曟暟")
+ private Long total;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/entity/DocumentEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/entity/DocumentEntity.java
new file mode 100644
index 0000000..1c24888
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/entity/DocumentEntity.java
@@ -0,0 +1,97 @@
+package xiaozhi.modules.knowledge.entity;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 鏂囨。琛� (Shadow DB for RAGFlow Documents)
+ * 瀵瑰簲琛ㄥ悕: ai_knowledge_document
+ */
+@Data
+@TableName(value = "ai_rag_knowledge_document", autoResultMap = true)
+@Schema(description = "鐭ヨ瘑搴撴枃妗h〃")
+public class DocumentEntity implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @TableId(type = IdType.ASSIGN_UUID)
+ @Schema(description = "鏈湴鍞竴ID")
+ private String id;
+
+ @Schema(description = "鐭ヨ瘑搴揑D (鍏宠仈 ai_rag_dataset.dataset_id)")
+ private String datasetId;
+
+ @Schema(description = "RAGFlow鏂囨。ID (杩滅▼ID)")
+ private String documentId;
+
+ @Schema(description = "鏂囨。鍚嶇О")
+ private String name;
+
+ @Schema(description = "鏂囦欢澶у皬(Bytes)")
+ private Long size;
+
+ @Schema(description = "鏂囦欢绫诲瀷(pdf/doc/txt绛�)")
+ private String type;
+
+ @Schema(description = "鍒嗗潡鏂规硶")
+ private String chunkMethod;
+
+ @Schema(description = "瑙f瀽閰嶇疆(JSON String)")
+ private String parserConfig;
+
+ @Schema(description = "鍙敤鐘舵�� (1: 鍚敤/姝e父, 0: 绂佺敤/澶辨晥)")
+ private String status;
+
+ @Schema(description = "杩愯鐘舵�� (UNSTART/RUNNING/CANCEL/DONE/FAIL)")
+ private String run;
+
+ @Schema(description = "瑙f瀽杩涘害 (0.0 ~ 1.0)")
+ private Double progress;
+
+ @Schema(description = "缂╃暐鍥� (Base64 鎴� URL)")
+ private String thumbnail;
+
+ @Schema(description = "瑙f瀽鑰楁椂 (鍗曚綅: 绉�)")
+ private Double processDuration;
+
+ @Schema(description = "鑷畾涔夊厓鏁版嵁 (JSON 鏍煎紡)")
+ private String metaFields;
+
+ @Schema(description = "鏉ユ簮绫诲瀷 (local, s3, url 绛�)")
+ private String sourceType;
+
+ @Schema(description = "瑙f瀽閿欒淇℃伅")
+ private String error;
+
+ @Schema(description = "鍒嗗潡鏁伴噺")
+ private Integer chunkCount;
+
+ @Schema(description = "Token鏁伴噺")
+ private Long tokenCount;
+
+ @Schema(description = "鏄惁鍚敤 (0:绂佺敤 1:鍚敤)")
+ private Integer enabled;
+
+ @Schema(description = "鍒涘缓鑰�")
+ @TableField(fill = FieldFill.INSERT)
+ private Long creator;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ @TableField(fill = FieldFill.INSERT)
+ private Date createdAt;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ @TableField(fill = FieldFill.UPDATE)
+ private Date updatedAt;
+
+ @Schema(description = "鏈�鏂板悓姝ユ椂闂�")
+ private Date lastSyncAt;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/entity/KnowledgeBaseEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/entity/KnowledgeBaseEntity.java
new file mode 100644
index 0000000..8afaa0b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/entity/KnowledgeBaseEntity.java
@@ -0,0 +1,81 @@
+package xiaozhi.modules.knowledge.entity;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@TableName(value = "ai_rag_dataset", autoResultMap = true)
+@Schema(description = "鐭ヨ瘑搴撶煡璇嗗簱琛�")
+public class KnowledgeBaseEntity {
+
+ @TableId(type = IdType.ASSIGN_UUID)
+ @Schema(description = "鍞竴鏍囪瘑")
+ private String id;
+
+ @Schema(description = "鐭ヨ瘑搴揑D")
+ private String datasetId;
+
+// @Deprecated
+ @Schema(description = "RAG妯″瀷閰嶇疆ID (杩炴帴RAGFlow鐨勫嚟璇佹寚閽�)")
+ private String ragModelId;
+
+ @Schema(description = "绉熸埛ID")
+ private String tenantId;
+
+ @Schema(description = "鐭ヨ瘑搴撳悕绉�")
+ private String name;
+
+ @Schema(description = "鐭ヨ瘑搴撳ご鍍�(Base64)")
+ private String avatar;
+
+ @Schema(description = "鐭ヨ瘑搴撴弿杩�")
+ private String description;
+
+ @Schema(description = "宓屽叆妯″瀷鍚嶇О")
+ private String embeddingModel;
+
+ @Schema(description = "鏉冮檺璁剧疆: me/team")
+ private String permission;
+
+ @Schema(description = "鍒嗗潡鏂规硶")
+ private String chunkMethod;
+
+ @Schema(description = "瑙f瀽鍣ㄩ厤缃�(JSON String)")
+ private String parserConfig;
+
+ @Schema(description = "鍒嗗潡鎬绘暟")
+ private Long chunkCount;
+
+ @Schema(description = "鏂囨。鎬绘暟")
+ private Long documentCount;
+
+ @Schema(description = "鎬籘oken鏁�")
+ private Long tokenNum;
+
+ @Schema(description = "鐘舵��(0:绂佺敤 1:鍚敤)")
+ private Integer status;
+
+ @Schema(description = "鍒涘缓鑰�")
+ @TableField(fill = FieldFill.INSERT)
+ private Long creator;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ @TableField(fill = FieldFill.INSERT)
+ private Date createdAt;
+
+ @Schema(description = "鏇存柊鑰�")
+ @TableField(fill = FieldFill.UPDATE)
+ private Long updater;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ @TableField(fill = FieldFill.UPDATE)
+ private Date updatedAt;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/KnowledgeBaseAdapter.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/KnowledgeBaseAdapter.java
new file mode 100644
index 0000000..2c45e79
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/KnowledgeBaseAdapter.java
@@ -0,0 +1,224 @@
+package xiaozhi.modules.knowledge.rag;
+
+import java.util.List;
+import java.util.Map;
+
+import xiaozhi.modules.knowledge.dto.dataset.DatasetDTO;
+
+import xiaozhi.common.page.PageData;
+import xiaozhi.modules.knowledge.dto.KnowledgeFilesDTO;
+import xiaozhi.modules.knowledge.dto.document.DocumentDTO;
+import xiaozhi.modules.knowledge.dto.document.ChunkDTO;
+import xiaozhi.modules.knowledge.dto.document.RetrievalDTO;
+import java.util.function.Consumer;
+
+/**
+ * 鐭ヨ瘑搴揂PI閫傞厤鍣ㄦ娊璞″熀绫�
+ * 瀹氫箟閫氱敤鐨勭煡璇嗗簱鎿嶄綔鎺ュ彛锛屾敮鎸佸绉嶅悗绔疉PI瀹炵幇
+ */
+public abstract class KnowledgeBaseAdapter {
+
+ /**
+ * 鑾峰彇閫傞厤鍣ㄧ被鍨嬫爣璇�
+ *
+ * @return 閫傞厤鍣ㄧ被鍨嬶紙濡傦細ragflow, milvus, pinecone绛夛級
+ */
+ public abstract String getAdapterType();
+
+ /**
+ * 鍒濆鍖栭�傞厤鍣ㄩ厤缃�
+ *
+ * @param config 閰嶇疆鍙傛暟
+ */
+ public abstract void initialize(Map<String, Object> config);
+
+ /**
+ * 楠岃瘉閰嶇疆鏄惁鏈夋晥
+ *
+ * @param config 閰嶇疆鍙傛暟
+ * @return 楠岃瘉缁撴灉
+ */
+ public abstract boolean validateConfig(Map<String, Object> config);
+
+ /**
+ * 鍒嗛〉鏌ヨ鏂囨。鍒楄〃
+ *
+ * @param datasetId 鐭ヨ瘑搴揑D
+ * @param queryParams 鏌ヨ鍙傛暟
+ * @param page 椤电爜
+ * @param limit 姣忛〉鏁伴噺
+ * @return 鍒嗛〉鏁版嵁
+ */
+ public abstract PageData<KnowledgeFilesDTO> getDocumentList(String datasetId,
+ DocumentDTO.ListReq req);
+
+ /**
+ * 鏍规嵁鏂囨。ID鑾峰彇鏂囨。璇︽儏
+ *
+ * @param datasetId 鐭ヨ瘑搴揑D
+ * @param documentId 鏂囨。ID
+ * @return 鏂囨。璇︽儏 (寮虹被鍨� InfoVO)
+ */
+ public abstract DocumentDTO.InfoVO getDocumentById(String datasetId, String documentId);
+
+ /**
+ * 涓婁紶鏂囨。鍒扮煡璇嗗簱
+ *
+ * @param req 涓婁紶璇锋眰鍙傛暟
+ * @return 涓婁紶鐨勬枃妗d俊鎭�
+ */
+ public abstract KnowledgeFilesDTO uploadDocument(DocumentDTO.UploadReq req);
+
+ /**
+ * 鏍规嵁鐘舵�佸垎椤垫煡璇㈡枃妗e垪琛�
+ *
+ * @param datasetId 鐭ヨ瘑搴揑D
+ * @param status 鏂囨。瑙f瀽鐘舵��
+ * @param page 椤电爜
+ * @param limit 姣忛〉鏁伴噺
+ * @return 鍒嗛〉鏁版嵁
+ */
+ public abstract PageData<KnowledgeFilesDTO> getDocumentListByStatus(String datasetId,
+ Integer status,
+ Integer page,
+ Integer limit);
+
+ /**
+ * 鍒犻櫎鏂囨。 (鏀寔鎵归噺鍒犻櫎)
+ *
+ * @param datasetId 鐭ヨ瘑搴揑D
+ * @param req 鍖呭惈鏂囨。ID鍒楄〃鐨勮姹傚璞�
+ */
+ public abstract void deleteDocument(String datasetId, DocumentDTO.BatchIdReq req);
+
+ /**
+ * 瑙f瀽鏂囨。锛堝垏鍧楋級
+ *
+ * @param datasetId 鐭ヨ瘑搴揑D
+ * @param documentIds 鏂囨。ID鍒楄〃
+ * @return 瑙f瀽缁撴灉
+ */
+ public abstract boolean parseDocuments(String datasetId, List<String> documentIds);
+
+ /**
+ * 鍒楀嚭鎸囧畾鏂囨。鐨勫垏鐗�
+ *
+ * @param datasetId 鐭ヨ瘑搴揑D
+ * @param documentId 鏂囨。ID
+ * @param req 鍒楄〃璇锋眰鍙傛暟 (鍒嗛〉銆佸叧閿瘝绛�)
+ * @return 鍒囩墖鍒楄〃VO
+ */
+ public abstract ChunkDTO.ListVO listChunks(String datasetId,
+ String documentId,
+ ChunkDTO.ListReq req);
+
+ /**
+ * 鍙洖娴嬭瘯 - 浠庣煡璇嗗簱涓绱㈢浉鍏冲垏鐗�
+ *
+ * @param req 妫�绱㈡祴璇曡姹傚弬鏁�
+ * @return 鍙洖娴嬭瘯缁撴灉
+ */
+ public abstract RetrievalDTO.ResultVO retrievalTest(
+ RetrievalDTO.TestReq req);
+
+ /**
+ * 娴嬭瘯杩炴帴
+ *
+ * @return 杩炴帴娴嬭瘯缁撴灉
+ */
+ public abstract boolean testConnection();
+
+ /**
+ * 鑾峰彇閫傞厤鍣ㄧ姸鎬佷俊鎭�
+ *
+ * @return 鐘舵�佷俊鎭�
+ */
+ public abstract Map<String, Object> getStatus();
+
+ /**
+ * 鑾峰彇鏀寔鐨勯厤缃弬鏁�
+ *
+ * @return 閰嶇疆鍙傛暟璇存槑
+ */
+ public abstract Map<String, Object> getSupportedConfig();
+
+ /**
+ * 鑾峰彇榛樿閰嶇疆
+ *
+ * @return 榛樿閰嶇疆
+ */
+ public abstract Map<String, Object> getDefaultConfig();
+
+ /**
+ * 鍒涘缓鏁版嵁闆�
+ *
+ * @param req 鍒涘缓鍙傛暟
+ * @return 鏁版嵁闆嗚鎯�
+ */
+ public abstract DatasetDTO.InfoVO createDataset(DatasetDTO.CreateReq req);
+
+ /**
+ * 鏇存柊鏁版嵁闆�
+ *
+ * @param datasetId 鏁版嵁闆咺D
+ * @param req 鏇存柊鍙傛暟
+ * @return 鏁版嵁闆嗚鎯�
+ */
+ public abstract DatasetDTO.InfoVO updateDataset(String datasetId, DatasetDTO.UpdateReq req);
+
+ /**
+ * 鍒犻櫎鏁版嵁闆�
+ *
+ * @param req 鍒犻櫎璇锋眰鍙傛暟锛堝寘鍚獻D鍒楄〃锛�
+ * @return 鎵归噺鎿嶄綔缁撴灉
+ */
+ public abstract DatasetDTO.BatchOperationVO deleteDataset(DatasetDTO.BatchIdReq req);
+
+ /**
+ * 鑾峰彇鏁版嵁闆嗙殑鏂囨。鏁伴噺
+ *
+ * @param datasetId 鏁版嵁闆咺D
+ * @return 鏂囨。鏁伴噺
+ */
+ public abstract Integer getDocumentCount(String datasetId);
+
+ /**
+ * 鑾峰彇鏁版嵁闆嗗畬鏁翠俊鎭紙鍚嶇О銆佺畝浠嬨�佹枃妗f暟閲忕瓑锛�
+ * 鐢ㄤ簬妫�娴� RAGFlow 绔槸鍚﹀凡鍒犻櫎銆佸悓姝ュ悕绉�/绠�浠嬪彉鏇�
+ *
+ * @param datasetId 鏁版嵁闆咺D
+ * @return 鏁版嵁闆嗚鎯咃紝鑻� RAGFlow 绔笉瀛樺湪鍒欒繑鍥� null
+ */
+ public abstract DatasetDTO.InfoVO getDatasetInfo(String datasetId);
+
+ /**
+ * 鍙戦�佹祦寮忚姹� (SSE)
+ *
+ * @param endpoint API绔偣
+ * @param body 璇锋眰浣�
+ * @param onData 鏁版嵁鍥炶皟
+ */
+ public abstract void postStream(String endpoint, Object body, Consumer<String> onData);
+
+ /**
+ * SearchBot 鎻愰棶
+ *
+ * @param config RAG閰嶇疆
+ * @param body 璇锋眰浣�
+ * @param onData 鏁版嵁鍥炶皟
+ * @return 鍝嶅簲瀵硅薄
+ */
+ public abstract Object postSearchBotAsk(Map<String, Object> config, Object body,
+ Consumer<String> onData);
+
+ /**
+ * AgentBot 瀵硅瘽
+ *
+ * @param config RAG閰嶇疆
+ * @param agentId Agent ID
+ * @param body 璇锋眰浣�
+ * @param onData 鏁版嵁鍥炶皟
+ */
+ public abstract void postAgentBotCompletion(Map<String, Object> config, String agentId, Object body,
+ Consumer<String> onData);
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/KnowledgeBaseAdapterFactory.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/KnowledgeBaseAdapterFactory.java
new file mode 100644
index 0000000..4655de0
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/KnowledgeBaseAdapterFactory.java
@@ -0,0 +1,206 @@
+package xiaozhi.modules.knowledge.rag;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+
+/**
+ * 鐭ヨ瘑搴撻�傞厤鍣ㄥ伐鍘傜被
+ * 璐熻矗鍒涘缓鍜岀鐞嗕笉鍚岀被鍨嬬殑鐭ヨ瘑搴揂PI閫傞厤鍣�
+ */
+@Slf4j
+public class KnowledgeBaseAdapterFactory {
+
+ // 娉ㄥ唽鐨勯�傞厤鍣ㄧ被鍨嬫槧灏�
+ private static final Map<String, Class<? extends KnowledgeBaseAdapter>> adapterRegistry = new HashMap<>();
+
+ // 閫傞厤鍣ㄥ疄渚嬬紦瀛�
+ private static final Map<String, KnowledgeBaseAdapter> adapterCache = new ConcurrentHashMap<>();
+
+ // 鏈�澶х紦瀛樺疄渚嬫暟锛岄槻姝㈠唴瀛樻硠闇� (Issue 9)
+ private static final int MAX_CACHE_SIZE = 50;
+
+ static {
+ // 娉ㄥ唽鍐呯疆閫傞厤鍣ㄧ被鍨�
+ registerAdapter("ragflow", xiaozhi.modules.knowledge.rag.impl.RAGFlowAdapter.class);
+ // 鍙互鍦ㄨ繖閲屾敞鍐屾洿澶氶�傞厤鍣ㄧ被鍨�
+ }
+
+ /**
+ * 娉ㄥ唽鏂扮殑閫傞厤鍣ㄧ被鍨�
+ *
+ * @param adapterType 閫傞厤鍣ㄧ被鍨嬫爣璇�
+ * @param adapterClass 閫傞厤鍣ㄧ被
+ */
+ public static void registerAdapter(String adapterType, Class<? extends KnowledgeBaseAdapter> adapterClass) {
+ if (adapterRegistry.containsKey(adapterType)) {
+ log.warn("閫傞厤鍣ㄧ被鍨� '{}' 宸插瓨鍦紝灏嗚瑕嗙洊", adapterType);
+ }
+ adapterRegistry.put(adapterType, adapterClass);
+ log.info("娉ㄥ唽閫傞厤鍣ㄧ被鍨�: {} -> {}", adapterType, adapterClass.getSimpleName());
+ }
+
+ /**
+ * 鑾峰彇閫傞厤鍣ㄥ疄渚�
+ *
+ * @param adapterType 閫傞厤鍣ㄧ被鍨�
+ * @param config 閰嶇疆鍙傛暟
+ * @return 閫傞厤鍣ㄥ疄渚�
+ */
+ public static KnowledgeBaseAdapter getAdapter(String adapterType, Map<String, Object> config) {
+ String cacheKey = buildCacheKey(adapterType, config);
+
+ // 妫�鏌ョ紦瀛樹腑鏄惁宸插瓨鍦ㄥ疄渚�
+ if (adapterCache.containsKey(cacheKey)) {
+ log.debug("浠庣紦瀛樿幏鍙栭�傞厤鍣ㄥ疄渚�: {}", cacheKey);
+ return adapterCache.get(cacheKey);
+ }
+
+ // 鍒涘缓鏂扮殑閫傞厤鍣ㄥ疄渚�
+ KnowledgeBaseAdapter adapter = createAdapter(adapterType, config);
+
+ // 缂撳瓨閫傞厤鍣ㄥ疄渚� (甯﹀閲忛檺鍒舵鏌�)
+ if (adapterCache.size() >= MAX_CACHE_SIZE) {
+ log.warn("閫傞厤鍣ㄧ紦瀛樺凡杈句笂闄� ({})锛屾墽琛屽唴瀛樹繚鎶ゆ�ф竻闄�", MAX_CACHE_SIZE);
+ // 绠�鍗曞鐞嗭細鐩存帴娓呯┖锛岀敓浜х幆澧冧笅寤鸿浣跨敤 LRU
+ adapterCache.clear();
+ }
+
+ adapterCache.put(cacheKey, adapter);
+ log.info("鍒涘缓骞剁紦瀛橀�傞厤鍣ㄥ疄渚�: {}", cacheKey);
+
+ return adapter;
+ }
+
+ /**
+ * 鑾峰彇閫傞厤鍣ㄥ疄渚嬶紙鏃犻厤缃級
+ *
+ * @param adapterType 閫傞厤鍣ㄧ被鍨�
+ * @return 閫傞厤鍣ㄥ疄渚�
+ */
+ public static KnowledgeBaseAdapter getAdapter(String adapterType) {
+ return getAdapter(adapterType, null);
+ }
+
+ /**
+ * 鑾峰彇鎵�鏈夊凡娉ㄥ唽鐨勯�傞厤鍣ㄧ被鍨�
+ *
+ * @return 閫傞厤鍣ㄧ被鍨嬮泦鍚�
+ */
+ public static Set<String> getRegisteredAdapterTypes() {
+ return adapterRegistry.keySet();
+ }
+
+ /**
+ * 妫�鏌ラ�傞厤鍣ㄧ被鍨嬫槸鍚﹀凡娉ㄥ唽
+ *
+ * @param adapterType 閫傞厤鍣ㄧ被鍨�
+ * @return 鏄惁宸叉敞鍐�
+ */
+ public static boolean isAdapterTypeRegistered(String adapterType) {
+ return adapterRegistry.containsKey(adapterType);
+ }
+
+ /**
+ * 娓呴櫎閫傞厤鍣ㄧ紦瀛�
+ */
+ public static void clearCache() {
+ int cacheSize = adapterCache.size();
+ adapterCache.clear();
+ log.info("娓呴櫎閫傞厤鍣ㄧ紦瀛橈紝鍏辨竻闄� {} 涓疄渚�", cacheSize);
+ }
+
+ /**
+ * 绉婚櫎鐗瑰畾閫傞厤鍣ㄧ被鍨嬬殑缂撳瓨
+ *
+ * @param adapterType 閫傞厤鍣ㄧ被鍨�
+ */
+ public static void removeCacheByType(String adapterType) {
+ int removedCount = 0;
+ for (String cacheKey : adapterCache.keySet()) {
+ if (cacheKey.startsWith(adapterType + "@")) {
+ adapterCache.remove(cacheKey);
+ removedCount++;
+ }
+ }
+ log.info("绉婚櫎閫傞厤鍣ㄧ被鍨� '{}' 鐨勭紦瀛橈紝鍏辩Щ闄� {} 涓疄渚�", adapterType, removedCount);
+ }
+
+ /**
+ * 鑾峰彇閫傞厤鍣ㄥ伐鍘傜姸鎬佷俊鎭�
+ *
+ * @return 鐘舵�佷俊鎭�
+ */
+ public static Map<String, Object> getFactoryStatus() {
+ Map<String, Object> status = new HashMap<>();
+ status.put("registeredAdapterTypes", adapterRegistry.keySet());
+ status.put("cachedAdapterCount", adapterCache.size());
+ status.put("cacheKeys", adapterCache.keySet());
+ return status;
+ }
+
+ /**
+ * 鍒涘缓閫傞厤鍣ㄥ疄渚�
+ *
+ * @param adapterType 閫傞厤鍣ㄧ被鍨�
+ * @param config 閰嶇疆鍙傛暟
+ * @return 閫傞厤鍣ㄥ疄渚�
+ */
+ private static KnowledgeBaseAdapter createAdapter(String adapterType, Map<String, Object> config) {
+ if (!adapterRegistry.containsKey(adapterType)) {
+ throw new RenException(ErrorCode.RAG_ADAPTER_TYPE_NOT_SUPPORTED,
+ "涓嶆敮鎸佺殑閫傞厤鍣ㄧ被鍨�: " + adapterType);
+ }
+
+ try {
+ Class<? extends KnowledgeBaseAdapter> adapterClass = adapterRegistry.get(adapterType);
+ KnowledgeBaseAdapter adapter = adapterClass.getDeclaredConstructor().newInstance();
+
+ // 鍒濆鍖栭�傞厤鍣�
+ if (config != null) {
+ adapter.initialize(config);
+
+ // 楠岃瘉閰嶇疆
+ if (!adapter.validateConfig(config)) {
+ throw new RenException(ErrorCode.RAG_CONFIG_VALIDATION_FAILED,
+ "閫傞厤鍣ㄩ厤缃獙璇佸け璐�: " + adapterType);
+ }
+ }
+
+ log.info("鎴愬姛鍒涘缓閫傞厤鍣ㄥ疄渚�: {}", adapterType);
+ return adapter;
+
+ } catch (Exception e) {
+ log.error("鍒涘缓閫傞厤鍣ㄥ疄渚嬪け璐�: {}", adapterType, e);
+ throw new RenException(ErrorCode.RAG_ADAPTER_CREATION_FAILED,
+ "鍒涘缓閫傞厤鍣ㄥけ璐�: " + adapterType + ", 閿欒: " + e.getMessage());
+ }
+ }
+
+ /**
+ * 鏋勫缓缂撳瓨閿�
+ *
+ * @param adapterType 閫傞厤鍣ㄧ被鍨�
+ * @param config 閰嶇疆鍙傛暟
+ * @return 缂撳瓨閿�
+ */
+ private static String buildCacheKey(String adapterType, Map<String, Object> config) {
+ if (config == null || config.isEmpty()) {
+ return adapterType + "@default";
+ }
+
+ // 鍩轰簬閰嶇疆鍙傛暟鐢熸垚缂撳瓨閿�
+ StringBuilder keyBuilder = new StringBuilder(adapterType + "@");
+
+ // 浣跨敤閰嶇疆鐨勫搱甯屽�间綔涓虹紦瀛橀敭鐨勪竴閮ㄥ垎
+ int configHash = config.hashCode();
+ keyBuilder.append(configHash);
+
+ return keyBuilder.toString();
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/RAGFlowClient.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/RAGFlowClient.java
new file mode 100644
index 0000000..9f204e9
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/RAGFlowClient.java
@@ -0,0 +1,278 @@
+package xiaozhi.modules.knowledge.rag;
+
+import java.time.Duration;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.Map;
+
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
+
+import java.text.SimpleDateFormat;
+import java.util.TimeZone;
+import java.util.Locale;
+import java.net.URLEncoder;
+import java.io.UnsupportedEncodingException;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.net.URI;
+import java.io.OutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.function.Consumer;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+
+/**
+ * RAGFlow HTTP Client
+ * 缁熶竴澶勭悊HTTP閫氫俊銆侀壌鏉冦�佽秴鏃朵笌閿欒瑙f瀽
+ */
+@Slf4j
+public class RAGFlowClient {
+
+ private final String baseUrl;
+ private final String apiKey;
+ private final RestTemplate restTemplate;
+ private final ObjectMapper objectMapper;
+
+ // 榛樿瓒呮椂鏃堕棿 (绉�)
+ private static final int DEFAULT_TIMEOUT = 30;
+
+ public RAGFlowClient(String baseUrl, String apiKey) {
+ this(baseUrl, apiKey, DEFAULT_TIMEOUT);
+ }
+
+ public RAGFlowClient(String baseUrl, String apiKey, int timeoutSeconds) {
+ this.baseUrl = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl;
+ this.apiKey = apiKey;
+ this.objectMapper = new ObjectMapper();
+ // [Reinforce] 鍏煎 RAGFlow 杩斿洖鐨� RFC 1123 鏃ユ湡鏍煎紡 (濡�: Tue, 10 Feb 2026 10:27:35 GMT)
+ this.objectMapper
+ .setDateFormat(new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US));
+ this.objectMapper.setTimeZone(TimeZone.getTimeZone("GMT"));
+
+ // 浼樺厛浠� Spring 涓婁笅鏂囦腑鑾峰彇姹犲寲鐨� RestTemplate Bean (Issue 3: 杩炴帴姹犲寲)
+ RestTemplate pooledTemplate = null;
+ try {
+ pooledTemplate = xiaozhi.common.utils.SpringContextUtils.getBean(RestTemplate.class);
+ } catch (Exception e) {
+ log.warn("鏃犳硶浠� SpringContext 鑾峰彇姹犲寲 RestTemplate锛屽皢閫�鍖栦负绠�鍗曡繛鎺ユā寮�: {}", e.getMessage());
+ }
+
+ if (false) { // Force new RestTemplate for debugging
+ this.restTemplate = pooledTemplate;
+ log.debug("RAGFlowClient 宸叉垚鍔熸寕杞藉叏灞�姹犲寲 RestTemplate");
+ } else {
+ // 鍏滃簳鏂规锛氶厤缃秴鏃跺苟鍒涘缓绠�鍗� RestTemplate
+ log.info("RAGFlowClient 鍒濆鍖�: 浣跨敤鐙珛 RestTemplate (Debug Mode)");
+ SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
+ factory.setConnectTimeout(timeoutSeconds * 1000);
+ factory.setReadTimeout(timeoutSeconds * 1000);
+ this.restTemplate = new RestTemplate(factory);
+ }
+ }
+
+ /**
+ * 鍙戦�� GET 璇锋眰
+ */
+ public Map<String, Object> get(String endpoint, Map<String, Object> queryParams) {
+ String url = buildUrl(endpoint, queryParams);
+ log.debug("GET {}", url);
+ return execute(url, HttpMethod.GET, null);
+ }
+
+ /**
+ * 鍙戦�� POST 璇锋眰 (JSON)
+ */
+ public Map<String, Object> post(String endpoint, Object body) {
+ String url = buildUrl(endpoint, null);
+ log.info("RAGFlow Client POST Request: URL={}, BodyType={}", url,
+ body != null ? body.getClass().getName() : "null");
+ try {
+ return execute(url, HttpMethod.POST, body);
+ } catch (Exception e) {
+ log.error("RAGFlow Client POST Failed: URL={}", url, e);
+ throw e;
+ }
+ }
+
+ /**
+ * 鍙戦�� DELETE 璇锋眰
+ */
+ public Map<String, Object> delete(String endpoint, Object body) {
+ String url = buildUrl(endpoint, null);
+ log.debug("DELETE {}", url);
+ return execute(url, HttpMethod.DELETE, body);
+ }
+
+ /**
+ * 鍙戦�� PUT 璇锋眰
+ */
+ public Map<String, Object> put(String endpoint, Object body) {
+ String url = buildUrl(endpoint, null);
+ log.debug("PUT {}", url);
+ return execute(url, HttpMethod.PUT, body);
+ }
+
+ /**
+ * 鍙戦�� Multipart 璇锋眰 (鏂囦欢涓婁紶)
+ */
+ public Map<String, Object> postMultipart(String endpoint, MultiValueMap<String, Object> parts) {
+ String url = buildUrl(endpoint, null);
+ log.debug("POST MULTIPART {}", url);
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+ headers.setBearerAuth(apiKey);
+ // 涓轰簡闃叉涓枃鏂囦欢鍚嶄贡鐮侊紝鏌愪簺鐜鍙兘闇�瑕佽缃� Charset锛屼絾鍦� Multipart 涓�氬父鐢� Part header 鎺у埗
+
+ HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(parts, headers);
+
+ return doExecute(url, HttpMethod.POST, requestEntity);
+ }
+
+ private Map<String, Object> execute(String url, HttpMethod method, Object body) {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.setBearerAuth(apiKey);
+ // 寮哄埗 UTF-8
+ headers.setAcceptCharset(Collections.singletonList(StandardCharsets.UTF_8));
+
+ HttpEntity<Object> requestEntity = new HttpEntity<>(body, headers);
+ return doExecute(url, method, requestEntity);
+ }
+
+ private Map<String, Object> doExecute(String url, HttpMethod method, HttpEntity<?> requestEntity) {
+ try {
+ ResponseEntity<String> response = restTemplate.exchange(url, method, requestEntity, String.class);
+
+ if (!response.getStatusCode().is2xxSuccessful()) {
+ log.error("RAGFlow API Error Status: {}", response.getStatusCode());
+ throw new RenException(ErrorCode.RAG_API_ERROR, "HTTP " + response.getStatusCode());
+ }
+
+ String responseBody = response.getBody();
+ if (responseBody == null) {
+ throw new RenException(ErrorCode.RAG_API_ERROR, "Empty Response");
+ }
+
+ @SuppressWarnings("unchecked")
+ Map<String, Object> map = objectMapper.readValue(responseBody, Map.class);
+
+ Integer code = (Integer) map.get("code");
+ if (code != null && code != 0) {
+ String msg = (String) map.get("message");
+ log.error("RAGFlow Business Error: code={}, msg={}", code, msg);
+ throw new RenException(ErrorCode.RAG_API_ERROR, msg != null ? msg : "Unknown RAGFlow Error");
+ }
+
+ // 杩斿洖 data 瀛楁锛屽鏋� data 涓嶅瓨鍦ㄥ垯杩斿洖鏁翠釜 map (瑙嗗叿浣撴儏鍐碉紝閫氬父 RAGFlow 杩斿洖 code=0, data=...)
+ // 鍏煎鎬у鐞嗭細濡傛灉 external caller 闇�瑕� check code锛岃繖閲屽凡缁� check 杩囦簡銆�
+ // 缁熶竴杩斿洖 wrap 浜� code 鐨� map 杩樻槸鍙繑鍥� data?
+ // 鏍规嵁鍒嗘瀽鎶ュ憡锛屾棫閫昏緫 check code==0 鍚庡彇 data.
+ // 杩欓噷鎴戜滑杩斿洖鏁翠釜 Map锛岃 Adapter 鍐冲畾鎬庝箞鍙栵紝鎴栬�呮垜浠洿鎺ュ湪杩欓噷鍓ョ锛�
+ // 寤鸿锛氫负浜嗙伒娲绘�э紝杩斿洖鍏ㄩ噺 Map锛屼絾鍦� Client 灞傚仛 code!=0 鐨勬姏閿欍��
+ return map;
+
+ } catch (RenException re) {
+ throw re;
+ } catch (Exception e) {
+ log.error("RAGFlow Client Execute Error! URL: {}, Method: {}, Body Type: {}", url, method,
+ requestEntity.getBody() != null ? requestEntity.getBody().getClass().getName() : "null");
+ log.error("Full exception stack trace: ", e);
+ throw new RenException(ErrorCode.RAG_API_ERROR, "Request Failed: " + e.getMessage());
+ }
+ }
+
+ private String buildUrl(String endpoint, Map<String, Object> queryParams) {
+ StringBuilder sb = new StringBuilder(baseUrl);
+ if (!endpoint.startsWith("/")) {
+ sb.append("/");
+ }
+ sb.append(endpoint);
+
+ if (queryParams != null && !queryParams.isEmpty()) {
+ sb.append("?");
+ queryParams.forEach((k, v) -> {
+ if (v != null) {
+ try {
+ sb.append(k).append("=")
+ .append(URLEncoder.encode(v.toString(),
+ StandardCharsets.UTF_8.name()))
+ .append("&");
+ } catch (UnsupportedEncodingException e) {
+ log.warn("鍙傛暟缂栫爜澶辫触: k={}, v={}", k, v);
+ sb.append(k).append("=").append(v).append("&");
+ }
+ }
+ });
+ // 绉婚櫎鏈�鍚庝竴涓� &
+ sb.setLength(sb.length() - 1);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * 鍙戦�佹祦寮� POST 璇锋眰 (SSE)
+ * 浣跨敤 Java 21 HttpClient 瀹炵幇
+ *
+ * @param endpoint API绔偣
+ * @param body 璇锋眰浣�
+ * @param onData 鏁版嵁鍥炶皟锛堟瘡鏀跺埌涓�琛屾暟鎹皟鐢ㄤ竴娆★級
+ */
+ public void postStream(String endpoint, Object body, Consumer<String> onData) {
+ try {
+ String url = buildUrl(endpoint, null);
+ log.debug("POST STREAM {}", url);
+
+ String jsonBody = objectMapper.writeValueAsString(body);
+
+ HttpClient httpClient = HttpClient.newBuilder()
+ .connectTimeout(Duration.ofSeconds(DEFAULT_TIMEOUT))
+ .build();
+
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create(url))
+ .header("Content-Type", "application/json")
+ .header("Authorization", "Bearer " + apiKey)
+ .POST(HttpRequest.BodyPublishers.ofString(jsonBody, StandardCharsets.UTF_8))
+ .build();
+
+ // 鍙戦�佽姹傚苟澶勭悊娴佸紡鍝嶅簲
+ httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream())
+ .body()
+ .transferTo(new OutputStream() {
+ private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+ @Override
+ public void write(int b) throws IOException {
+ if (b == '\n') {
+ String line = buffer.toString(StandardCharsets.UTF_8);
+ if (!line.trim().isEmpty()) {
+ onData.accept(line);
+ }
+ buffer.reset();
+ } else {
+ buffer.write(b);
+ }
+ }
+ });
+
+ } catch (Exception e) {
+ log.error("RAGFlow Stream Request Error", e);
+ throw new RenException(ErrorCode.RAG_API_ERROR, "Stream Request Failed: " + e.getMessage());
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/impl/RAGFlowAdapter.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/impl/RAGFlowAdapter.java
new file mode 100644
index 0000000..ae6f913
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/impl/RAGFlowAdapter.java
@@ -0,0 +1,721 @@
+package xiaozhi.modules.knowledge.rag.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.core.io.AbstractResource;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.page.PageData;
+import xiaozhi.modules.knowledge.dto.KnowledgeFilesDTO;
+import xiaozhi.modules.knowledge.dto.dataset.DatasetDTO;
+import xiaozhi.modules.knowledge.dto.document.ChunkDTO;
+import xiaozhi.modules.knowledge.dto.document.RetrievalDTO;
+import xiaozhi.modules.knowledge.dto.document.DocumentDTO;
+import xiaozhi.modules.knowledge.rag.KnowledgeBaseAdapter;
+import xiaozhi.modules.knowledge.rag.RAGFlowClient;
+
+/**
+ * RAGFlow鐭ヨ瘑搴撻�傞厤鍣ㄥ疄鐜�
+ * <p>
+ * 閲嶆瀯璇存槑 (Refactoring Note):
+ * 鏈被宸插崌绾т负浣跨敤 {@link RAGFlowClient} 缁熶竴澶勭悊 HTTP 閫氫俊銆�
+ * 瑙e喅浜嗘棫浠g爜涓� Timeout 缂哄け銆丒rror Handling 鍒嗘暎鐨勯棶棰樸��
+ * </p>
+ */
+@Slf4j
+public class RAGFlowAdapter extends KnowledgeBaseAdapter {
+
+ private static final String ADAPTER_TYPE = "ragflow";
+
+ private Map<String, Object> config;
+ private ObjectMapper objectMapper;
+ // Client 瀹炰緥锛屽垵濮嬪寲鏃跺垱寤�
+ private RAGFlowClient client;
+
+ public RAGFlowAdapter() {
+ this.objectMapper = new ObjectMapper();
+ }
+
+ @Override
+ public String getAdapterType() {
+ return ADAPTER_TYPE;
+ }
+
+ @Override
+ public void initialize(Map<String, Object> config) {
+ this.config = config;
+ validateConfig(config);
+
+ String baseUrl = getConfigValue(config, "base_url", "baseUrl");
+ String apiKey = getConfigValue(config, "api_key", "apiKey");
+
+ // 鍒濆鍖� Client锛岄粯璁よ秴鏃� 30s锛屽彲閫氳繃 config 鎵╁睍
+ int timeout = 30;
+ Object timeoutObj = getConfigValue(config, "timeout", "timeout");
+ if (timeoutObj != null) {
+ try {
+ timeout = Integer.parseInt(timeoutObj.toString());
+ } catch (Exception e) {
+ log.warn("瑙f瀽瓒呮椂閰嶇疆澶辫触锛屼娇鐢ㄩ粯璁ゅ�� 30s");
+ }
+ }
+ this.client = new RAGFlowClient(baseUrl, apiKey, timeout);
+ log.info("RAGFlow閫傞厤鍣ㄥ垵濮嬪寲瀹屾垚锛孋lient宸插氨缁�");
+ }
+
+ @Override
+ public boolean validateConfig(Map<String, Object> config) {
+ if (config == null) {
+ throw new RenException(ErrorCode.RAG_CONFIG_NOT_FOUND);
+ }
+
+ String baseUrl = getConfigValue(config, "base_url", "baseUrl");
+ String apiKey = getConfigValue(config, "api_key", "apiKey");
+
+ if (StringUtils.isBlank(baseUrl)) {
+ throw new RenException(ErrorCode.RAG_API_ERROR_URL_NULL);
+ }
+
+ if (StringUtils.isBlank(apiKey)) {
+ throw new RenException(ErrorCode.RAG_API_ERROR_API_KEY_NULL);
+ }
+
+ if (apiKey.contains("浣�")) {
+ throw new RenException(ErrorCode.RAG_API_ERROR_API_KEY_INVALID);
+ }
+
+ if (!baseUrl.startsWith("http://") && !baseUrl.startsWith("https://")) {
+ throw new RenException(ErrorCode.RAG_API_ERROR_URL_INVALID);
+ }
+
+ return true;
+ }
+
+ /**
+ * 杈呭姪鏂规硶锛氭敮鎸佸绉嶉敭鍚嶈幏鍙栭厤缃紙鍏煎 camelCase 鍜� snake_case锛�
+ */
+ private String getConfigValue(Map<String, Object> config, String snakeKey, String camelKey) {
+ if (config.containsKey(snakeKey)) {
+ return (String) config.get(snakeKey);
+ }
+ if (config.containsKey(camelKey)) {
+ return (String) config.get(camelKey);
+ }
+ return null;
+ }
+
+ /**
+ * 杈呭姪鏂规硶锛氱‘淇� Client 宸插垵濮嬪寲
+ */
+ private RAGFlowClient getClient() {
+ if (this.client == null) {
+ // 灏濊瘯閲嶆柊鍒濆鍖�
+ if (this.config != null) {
+ initialize(this.config);
+ } else {
+ throw new RenException(ErrorCode.RAG_CONFIG_NOT_FOUND, "閫傞厤鍣ㄦ湭鍒濆鍖�"); // 搴旇鎶涘嚭 RuntimeException
+ }
+ }
+ return this.client;
+ }
+
+ private RenException convertToRenException(Exception e) {
+ if (e instanceof RenException) {
+ return (RenException) e;
+ }
+ return new RenException(ErrorCode.RAG_API_ERROR, e.getMessage());
+ }
+
+ @Override
+ public PageData<KnowledgeFilesDTO> getDocumentList(String datasetId, DocumentDTO.ListReq req) {
+ try {
+ log.info("=== [RAGFlow] 鑾峰彇鏂囨。鍒楄〃: datasetId={} ===", datasetId);
+
+ // 浣跨敤 Jackson 灏� DTO 杞负 Map 浣滀负鏌ヨ鍙傛暟
+ Map<String, Object> params = objectMapper.convertValue(req, new TypeReference<Map<String, Object>>() {
+ });
+
+ Map<String, Object> response = getClient().get("/api/v1/datasets/" + datasetId + "/documents", params);
+
+ Object dataObj = response.get("data");
+ return parseDocumentListResponse(dataObj, req.getPage() != null ? req.getPage() : 1,
+ req.getPageSize() != null ? req.getPageSize() : 10);
+
+ } catch (Exception e) {
+ log.error("鑾峰彇鏂囨。鍒楄〃澶辫触", e);
+ throw convertToRenException(e);
+ }
+ }
+
+ @Override
+ public DocumentDTO.InfoVO getDocumentById(String datasetId, String documentId) {
+ try {
+ log.info("=== [RAGFlow] 鑾峰彇鏂囨。璇︽儏: datasetId={}, documentId={} ===", datasetId, documentId);
+ DocumentDTO.ListReq req = DocumentDTO.ListReq.builder()
+ .id(documentId)
+ .page(1)
+ .pageSize(1)
+ .build();
+
+ Map<String, Object> params = objectMapper.convertValue(req, new TypeReference<Map<String, Object>>() {
+ });
+ Map<String, Object> response = getClient().get("/api/v1/datasets/" + datasetId + "/documents", params);
+
+ Object dataObj = response.get("data");
+ if (dataObj instanceof Map<?, ?> dataMap) {
+ List<?> documents = (List<?>) dataMap.get("docs");
+ if (documents != null && !documents.isEmpty()) {
+ return objectMapper.convertValue(documents.get(0), DocumentDTO.InfoVO.class);
+ }
+ }
+ return null;
+ } catch (Exception e) {
+ log.error("鑾峰彇鏂囨。璇︽儏澶辫触: documentId={}", documentId, e);
+ throw convertToRenException(e);
+ }
+ }
+
+ @Override
+ public KnowledgeFilesDTO uploadDocument(DocumentDTO.UploadReq req) {
+ String datasetId = req.getDatasetId();
+ MultipartFile file = req.getFile();
+ try {
+ log.info("=== [RAGFlow] 涓婁紶鏂囨。: datasetId={} ===", datasetId);
+
+ MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
+ body.add("file", new MultipartFileResource(file));
+
+ if (StringUtils.isNotBlank(req.getName())) {
+ body.add("name", req.getName());
+ }
+ if (req.getMetaFields() != null && !req.getMetaFields().isEmpty()) {
+ body.add("meta", objectMapper.writeValueAsString(req.getMetaFields()));
+ }
+ if (req.getChunkMethod() != null) {
+ // 灏嗘灇涓惧�艰浆涓� RAGFlow 鏈熷緟鐨勫瓧绗︿覆锛堝 NAIVE -> naive锛�
+ body.add("chunk_method", req.getChunkMethod().name().toLowerCase());
+ }
+ if (req.getParserConfig() != null) {
+ body.add("parser_config", objectMapper.writeValueAsString(req.getParserConfig()));
+ }
+ if (StringUtils.isNotBlank(req.getParentPath())) {
+ body.add("parent_path", req.getParentPath());
+ }
+
+ Map<String, Object> response = getClient().postMultipart("/api/v1/datasets/" + datasetId + "/documents",
+ body);
+
+ Object dataObj = response.get("data");
+ return parseUploadResponse(dataObj, datasetId, file);
+
+ } catch (Exception e) {
+ log.error("鏂囨。涓婁紶澶辫触", e);
+ throw convertToRenException(e);
+ }
+ }
+
+ @Override
+ public PageData<KnowledgeFilesDTO> getDocumentListByStatus(String datasetId, Integer status, Integer page,
+ Integer limit) {
+ List<DocumentDTO.InfoVO.RunStatus> runStatusList = null;
+ if (status != null) {
+ runStatusList = new ArrayList<>();
+ switch (status) {
+ case 0:
+ runStatusList.add(DocumentDTO.InfoVO.RunStatus.UNSTART);
+ break;
+ case 1:
+ runStatusList.add(DocumentDTO.InfoVO.RunStatus.RUNNING);
+ break;
+ case 2:
+ runStatusList.add(DocumentDTO.InfoVO.RunStatus.CANCEL);
+ break;
+ case 3:
+ runStatusList.add(DocumentDTO.InfoVO.RunStatus.DONE);
+ break;
+ case 4:
+ runStatusList.add(DocumentDTO.InfoVO.RunStatus.FAIL);
+ break;
+ default:
+ break;
+ }
+ }
+ DocumentDTO.ListReq req = DocumentDTO.ListReq.builder()
+ .run(runStatusList)
+ .page(page)
+ .pageSize(limit)
+ .build();
+ return getDocumentList(datasetId, req);
+ }
+
+ @Override
+ public void deleteDocument(String datasetId, DocumentDTO.BatchIdReq req) {
+ try {
+ log.info("=== [RAGFlow] 鎵归噺鍒犻櫎鏂囨。: datasetId={}, count={} ===", datasetId,
+ req.getIds() != null ? req.getIds().size() : 0);
+ getClient().delete("/api/v1/datasets/" + datasetId + "/documents", req);
+ } catch (Exception e) {
+ log.error("鎵归噺鍒犻櫎鏂囨。澶辫触: datasetId={}", datasetId, e);
+ throw convertToRenException(e);
+ }
+ }
+
+ @Override
+ public boolean parseDocuments(String datasetId, List<String> documentIds) {
+ try {
+ log.info("=== [RAGFlow] 瑙f瀽鏂囨。 ===");
+ Map<String, Object> body = new HashMap<>();
+ body.put("document_ids", documentIds);
+
+ getClient().post("/api/v1/datasets/" + datasetId + "/chunks", body);
+ return true;
+ } catch (Exception e) {
+ log.error("瑙f瀽鏂囨。澶辫触", e);
+ throw convertToRenException(e);
+ }
+ }
+
+ @Override
+ public ChunkDTO.ListVO listChunks(String datasetId, String documentId, ChunkDTO.ListReq req) {
+ try {
+ // [鎻愮伅閲嶆瀯] 浣跨敤 objectMapper 鍔ㄦ�佽浆鎹㈡煡璇㈠弬鏁帮紝娑堥櫎纭紪鐮�
+ Map<String, Object> params = objectMapper.convertValue(req, new TypeReference<Map<String, Object>>() {
+ });
+
+ Map<String, Object> response = getClient()
+ .get("/api/v1/datasets/" + datasetId + "/documents/" + documentId + "/chunks", params);
+
+ Object dataObj = response.get("data");
+ if (dataObj == null) {
+ log.warn("[RAGFlow] listChunks 鍝嶅簲 data 涓虹┖, docId={}", documentId);
+ return ChunkDTO.ListVO.builder()
+ .chunks(new ArrayList<>())
+ .total(0L)
+ .build();
+ }
+
+ ChunkDTO.ListVO result = objectMapper.convertValue(dataObj, ChunkDTO.ListVO.class);
+ if (result.getTotal() == null) {
+ result.setTotal(0L);
+ }
+ return result;
+ } catch (Exception e) {
+ log.error("鑾峰彇鍒囩墖澶辫触: docId={}", documentId, e);
+ throw convertToRenException(e);
+ }
+ }
+
+ @Override
+ public RetrievalDTO.ResultVO retrievalTest(RetrievalDTO.TestReq req) {
+ try {
+ // [Production Reinforce] 鍙傛暟闃插尽鎬у榻愶細RAGFlow Python 绔 0 鎴栬礋鏁板垎椤垫晱鎰�
+ // 瑙e喅 ValueError('Search does not support negative slicing.')
+ if (req.getPage() != null && req.getPage() < 1) {
+ req.setPage(1);
+ }
+ if (req.getPageSize() != null && req.getPageSize() < 1) {
+ req.setPageSize(10); // 榛樿 10 鏉�
+ }
+ if (req.getTopK() != null && req.getTopK() < 1) {
+ req.setTopK(1024); // RAGFlow 鍐呴儴榛樿 TopK
+ }
+ // 鐩镐技搴﹂槇鍊煎綊涓�鍖� (0.0 ~ 1.0)
+ if (req.getSimilarityThreshold() != null) {
+ if (req.getSimilarityThreshold() < 0f)
+ req.setSimilarityThreshold(0.2f);
+ if (req.getSimilarityThreshold() > 1f)
+ req.setSimilarityThreshold(1.0f);
+ }
+
+ // [鎻愮伅閲嶆瀯] 鐩存帴閫忎紶寮虹被鍨� DTO锛岀敱 getClient 澶勭悊搴忓垪鍖�
+ Map<String, Object> response = getClient().post("/api/v1/retrieval", req);
+
+ Object dataObj = response.get("data");
+ if (dataObj == null) {
+ log.warn("[RAGFlow] retrievalTest 鍝嶅簲 data 涓虹┖");
+ return RetrievalDTO.ResultVO.builder()
+ .chunks(new ArrayList<>())
+ .docAggs(new ArrayList<>())
+ .total(0L)
+ .build();
+ }
+
+ RetrievalDTO.ResultVO result = objectMapper.convertValue(dataObj, RetrievalDTO.ResultVO.class);
+ if (result.getTotal() == null) {
+ result.setTotal(0L);
+ }
+ return result;
+ } catch (Exception e) {
+ log.error("鍙洖娴嬭瘯澶辫触", e);
+ throw convertToRenException(e);
+ }
+ }
+
+ @Override
+ public boolean testConnection() {
+ try {
+ getClient().get("/api/v1/health", null);
+ return true;
+ } catch (Exception e) {
+ log.error("杩炴帴娴嬭瘯澶辫触: {}", e.getMessage());
+ return false;
+ }
+ }
+
+ @Override
+ public Map<String, Object> getStatus() {
+ Map<String, Object> status = new HashMap<>();
+ status.put("adapterType", getAdapterType());
+ status.put("configKeys", config != null ? config.keySet() : "鏈厤缃�");
+ status.put("connectionTest", testConnection());
+ status.put("lastChecked", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
+ return status;
+ }
+
+ @Override
+ public Map<String, Object> getSupportedConfig() {
+ Map<String, Object> supportedConfig = new HashMap<>();
+ supportedConfig.put("base_url", "RAGFlow API鍩虹URL");
+ supportedConfig.put("api_key", "RAGFlow API瀵嗛挜");
+ supportedConfig.put("timeout", "璇锋眰瓒呮椂鏃堕棿锛堟绉掞級");
+ return supportedConfig;
+ }
+
+ @Override
+ public Map<String, Object> getDefaultConfig() {
+ Map<String, Object> defaultConfig = new HashMap<>();
+ defaultConfig.put("timeout", 30000);
+ return defaultConfig;
+ }
+
+ @Override
+ public DatasetDTO.InfoVO createDataset(DatasetDTO.CreateReq req) {
+ try {
+ // [Production Fix] 寮哄寲榛樿鍊煎鐞嗭紝闃叉 RAGFlow API 鍥犵┖瀛楃涓叉垨缂哄け瀛楁鎶ラ敊 (Code 101)
+ // 瑙e喅 "Field: <avatar> - Message: <Missing MIME prefix>" 绛夋牎楠屽け璐�
+ if (StringUtils.isBlank(req.getPermission())) {
+ req.setPermission("me");
+ }
+ if (StringUtils.isBlank(req.getChunkMethod())) {
+ req.setChunkMethod("naive");
+ }
+
+ // 馃 鑷姩琛ュ叏宓屽叆妯″瀷锛氫紭鍏堜娇鐢ㄨ姹備紶鍙傦紝鍏舵浣跨敤閰嶇疆涓殑榛樿妯″瀷
+ if (StringUtils.isBlank(req.getEmbeddingModel())) {
+ String defaultModel = (String) getConfigValue(config, "embedding_model", "embeddingModel");
+ if (StringUtils.isNotBlank(defaultModel)) {
+ log.info("RAGFlow: 浣跨敤閰嶇疆涓殑榛樿宓屽叆妯″瀷: {}", defaultModel);
+ req.setEmbeddingModel(defaultModel);
+ }
+ // 鑻ラ厤缃腑涔熸棤榛樿鍊硷紝鍒欑暀绌虹敱 RAGFlow 鏈嶅姟绔嚜琛屽厹搴曪紙鎴栨姏鍑轰笟鍔″紓甯革級
+ }
+
+ // 馃柤锔� 鑷姩琛ュ叏澶村儚锛氳嫢涓虹┖鍒欐彁渚涗竴涓� 1x1 閫忔槑鍍忕礌锛岄槻姝� RAGFlow 鏍¢獙 MIME Prefix 澶辫触
+ if (StringUtils.isBlank(req.getAvatar())) {
+ req.setAvatar(
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==");
+ }
+
+ // 鐩存帴灏嗗己绫诲瀷璇锋眰瀵硅薄浼犵粰 Client锛孞ackson 浼氬鐞� JsonProperty 鏄犲皠
+ Map<String, Object> response = getClient().post("/api/v1/datasets", req);
+
+ // 瀹夊叏鍦拌幏鍙� data 骞堕�氳繃 DatasetDTO.InfoVO 杩涜鍏ㄩ噺鏄犲皠
+ Object dataObj = response.get("data");
+ if (dataObj != null) {
+ return objectMapper.convertValue(dataObj, DatasetDTO.InfoVO.class);
+ }
+ throw new RenException(ErrorCode.RAG_API_ERROR, "Invalid response from createDataset: missing data object");
+ } catch (Exception e) {
+ log.error("鍒涘缓鏁版嵁闆嗗け璐�", e);
+ throw convertToRenException(e);
+ }
+ }
+
+ @Override
+ public DatasetDTO.InfoVO updateDataset(String datasetId, DatasetDTO.UpdateReq req) {
+ try {
+ // RAGFlow API 鏇存柊寤鸿璺緞甯� ID
+ Map<String, Object> response = getClient().put("/api/v1/datasets/" + datasetId, req);
+
+ Object dataObj = response.get("data");
+ if (dataObj != null) {
+ return objectMapper.convertValue(dataObj, DatasetDTO.InfoVO.class);
+ }
+ return null;
+ } catch (Exception e) {
+ log.error("鏇存柊鏁版嵁闆嗗け璐�", e);
+ throw convertToRenException(e);
+ }
+ }
+
+ @Override
+ public DatasetDTO.BatchOperationVO deleteDataset(DatasetDTO.BatchIdReq req) {
+ try {
+ // RAGFlow 鎵归噺鍒犻櫎鎺ュ彛浣跨敤 DELETE /api/v1/datasets
+ Map<String, Object> response = getClient().delete("/api/v1/datasets", req);
+
+ Object dataObj = response.get("data");
+ if (dataObj != null) {
+ return objectMapper.convertValue(dataObj, DatasetDTO.BatchOperationVO.class);
+ }
+ return null;
+ } catch (Exception e) {
+ log.error("鎵归噺鍒犻櫎鏁版嵁闆嗗け璐�", e);
+ throw convertToRenException(e);
+ }
+ }
+
+ @Override
+ public Integer getDocumentCount(String datasetId) {
+ DatasetDTO.InfoVO info = getDatasetInfo(datasetId);
+ if (info != null && info.getDocumentCount() != null) {
+ return info.getDocumentCount().intValue();
+ }
+ return 0;
+ }
+
+ @Override
+ public DatasetDTO.InfoVO getDatasetInfo(String datasetId) {
+ try {
+ Map<String, Object> params = new HashMap<>();
+ params.put("id", datasetId);
+ params.put("page", 1);
+ params.put("page_size", 1);
+
+ Map<String, Object> response = getClient().get("/api/v1/datasets", params);
+ Object dataObj = response.get("data");
+
+ if (dataObj instanceof List) {
+ List<?> list = (List<?>) dataObj;
+ if (!list.isEmpty()) {
+ return objectMapper.convertValue(list.get(0), DatasetDTO.InfoVO.class);
+ }
+ }
+ // RAGFlow 绔笉瀛樺湪璇ユ暟鎹泦
+ return null;
+ } catch (Exception e) {
+ log.warn("鑾峰彇鏁版嵁闆嗕俊鎭け璐�: datasetId={}, error={}", datasetId, e.getMessage());
+ throw convertToRenException(e);
+ }
+ }
+
+ @Override
+ public void postStream(String endpoint, Object body, Consumer<String> onData) {
+ try {
+ getClient().postStream(endpoint, body, onData);
+ } catch (Exception e) {
+ log.error("娴佸紡璇锋眰澶辫触", e);
+ throw convertToRenException(e);
+ }
+ }
+
+ @Override
+ public Object postSearchBotAsk(Map<String, Object> config, Object body,
+ Consumer<String> onData) {
+ // SearchBot 瀹為檯涓婃槸 Dataset 妫�绱㈢殑涓�绉嶅皝瑁咃紝鎴栬�呮槸鏈叕寮�鐨� API锛�
+ // 鍋囪 RAGFlow 娌℃湁鏄惧紡鐨� /searchbots 鎺ュ彛渚� SDK 璋冪敤锛岃�屾槸 Dataset Retrieval 鎴栬�� Chat銆�
+ // 浣嗘牴鎹� BotDTO锛屽畠鏄� /api/v1/searchbots/ask (鍋囪)
+ // 杩欓噷鐨� config 鍙兘鏄鐩栫敤鐨勶紝鎴栬�呮垜浠彧鏄敤 adapter 瀹炰緥宸叉湁鐨� client銆�
+ // 浣� Bot 鍙兘浣跨敤涓嶅悓鐨� API Key锛熼�氬父 Adapter 瀹炰緥缁戝畾浜嗕竴涓� Key銆�
+ // 濡傛灉 Bot 浣跨敤绯荤粺 Key锛屽垯鐩存帴鐢� getClient()銆�
+
+ // 鏆傛椂鍋囪 endpoint /api/v1/searchbots/ask 瀛樺湪锛堟垨鑰呯被浼肩殑锛�
+ // 濡傛灉鏄祦寮�:
+ try {
+ getClient().postStream("/api/v1/searchbots/ask", body, onData);
+ return null;
+ } catch (Exception e) {
+ log.error("SearchBot Ask 澶辫触", e);
+ throw convertToRenException(e);
+ }
+ }
+
+ @Override
+ public void postAgentBotCompletion(Map<String, Object> config, String agentId, Object body,
+ Consumer<String> onData) {
+ // AgentBot 瀵瑰簲 /api/v1/agentbots/{id}/completions
+ try {
+ getClient().postStream("/api/v1/agentbots/" + agentId + "/completions", body, onData);
+ } catch (Exception e) {
+ log.error("AgentBot Completion 澶辫触", e);
+ throw convertToRenException(e);
+ }
+ }
+
+ // 澶嶇敤鍘熸湁鐨勮緟鍔╄В鏋愭柟娉曪紝淇濇寔鍏煎
+ // [Bug Fix] 涓嶅啀鍚炴帀鍙嶅簭鍒楀寲寮傚父锛岄伩鍏嶄笂灞傝鍒�"鏂囨。宸插垹闄�"
+ private PageData<KnowledgeFilesDTO> parseDocumentListResponse(Object dataObj, long curPage, long pageSize) {
+ if (dataObj == null) {
+ return new PageData<>(new ArrayList<>(), 0);
+ }
+
+ Map<?, ?> dataMap = Map.class.cast(dataObj);
+ List<?> documents = List.class.cast(dataMap.get("docs"));
+ if (documents == null || documents.isEmpty()) {
+ // RAGFlow 鏄庣‘杩斿洖浜嗙┖鏂囨。鍒楄〃锛岃繖鏄悎娉曠殑"鐪熺┖"
+ return new PageData<>(new ArrayList<>(), 0);
+ }
+
+ List<KnowledgeFilesDTO> list = new ArrayList<>();
+ for (Object docObj : documents) {
+ try {
+ // 鍗曟枃妗h浆鎹㈠閿欙細涓�涓枃妗e弽搴忓垪鍖栧け璐ヤ笉褰卞搷鍏朵粬鏂囨。
+ DocumentDTO.InfoVO info = objectMapper.convertValue(docObj, DocumentDTO.InfoVO.class);
+ list.add(mapToKnowledgeFilesDTO(info, null));
+ } catch (Exception e) {
+ log.warn("[RAGFlow] 鍗曟枃妗� DTO 杞崲澶辫触锛岃烦杩囪鏂囨。: {}", e.getMessage());
+ }
+ }
+
+ long total = 0;
+ if (dataMap.containsKey("total")) {
+ total = ((Number) dataMap.get("total")).longValue();
+ }
+
+ return new PageData<>(list, total);
+ }
+
+ private KnowledgeFilesDTO parseUploadResponse(Object dataObj, String datasetId, MultipartFile file) {
+ KnowledgeFilesDTO result = null;
+
+ // 灏濊瘯浠庡搷搴旀暟鎹腑鎻愬彇鏂囨。ID (documentId)
+ if (dataObj != null) {
+ try {
+ DocumentDTO.InfoVO info = null;
+ if (dataObj instanceof Map) {
+ info = objectMapper.convertValue(dataObj, DocumentDTO.InfoVO.class);
+ } else if (dataObj instanceof List) {
+ List<?> list = (List<?>) dataObj;
+ if (!list.isEmpty()) {
+ info = objectMapper.convertValue(list.get(0), DocumentDTO.InfoVO.class);
+ }
+ }
+
+ if (info != null) {
+ result = mapToKnowledgeFilesDTO(info, datasetId);
+ }
+ } catch (Exception e) {
+ log.warn("瑙f瀽涓婁紶鍝嶅簲鏁版嵁澶辫触: {}", e.getMessage());
+ }
+ }
+
+ if (result == null) {
+ log.error("鏈兘浠嶳AGFlow鍝嶅簲涓彁鍙栧埌documentId锛屽搷搴斿唴瀹�: {}", dataObj);
+ // 杩欓噷搴旇杩斿洖涓�涓渶灏忓寲鐨勫寘鍚熀纭�淇℃伅鐨� DTO 鑰屼笉鏄� null锛岄槻姝笂娓� NPE
+ result = new KnowledgeFilesDTO();
+ result.setDatasetId(datasetId);
+ result.setName(file.getOriginalFilename());
+ result.setFileSize(file.getSize());
+ result.setStatus("1");
+ }
+
+ return result;
+ }
+
+ /**
+ * 灏� RAGFlow 鐨勫己绫诲瀷 InfoVO 鏄犲皠鍒板唴閮ㄤ娇鐢ㄧ殑 KnowledgeFilesDTO
+ * 纭繚鎵�鏈夊彲鐢ㄥ瓧娈碉紙鍚嶇О銆佸ぇ灏忋�佺姸鎬併�侀厤缃瓑锛夐兘寰楀埌鍏ㄩ噺鍚屾
+ */
+ private KnowledgeFilesDTO mapToKnowledgeFilesDTO(DocumentDTO.InfoVO info, String datasetId) {
+ KnowledgeFilesDTO dto = new KnowledgeFilesDTO();
+ if (info == null)
+ return dto;
+
+ dto.setId(info.getId());
+ dto.setDocumentId(info.getId());
+ dto.setDatasetId(StringUtils.isNotBlank(info.getDatasetId()) ? info.getDatasetId() : datasetId);
+ dto.setName(info.getName());
+ dto.setFileSize(info.getSize());
+
+ // 鐘舵�佹槧灏�
+ if (info.getRun() != null) {
+ dto.setRun(info.getRun().name());
+ }
+
+ if (StringUtils.isNotBlank(info.getStatus())) {
+ dto.setStatus(info.getStatus());
+ } else {
+ dto.setStatus("1"); // 榛樿鍚敤
+ }
+
+ // 鏃堕棿鍚屾
+ if (info.getCreateTime() != null) {
+ dto.setCreatedAt(new Date(info.getCreateTime()));
+ }
+ if (info.getUpdateTime() != null) {
+ dto.setUpdatedAt(new Date(info.getUpdateTime()));
+ }
+
+ // 鏍稿績鍏冩暟鎹ˉ榻� (Issue 1)
+ dto.setProgress(info.getProgress());
+ dto.setThumbnail(info.getThumbnail());
+ dto.setProcessDuration(info.getProcessDuration());
+ dto.setSourceType(info.getSourceType());
+ dto.setChunkCount(info.getChunkCount() != null ? info.getChunkCount().intValue() : 0);
+ dto.setTokenCount(info.getTokenCount());
+ dto.setError(info.getProgressMsg()); // 灏嗚繘搴︽弿杩版槧灏勪负閿欒淇℃伅鎻愮ず
+
+ // 鎵╁睍瀛楁鍚屾
+ dto.setMetaFields(info.getMetaFields());
+ if (info.getChunkMethod() != null) {
+ dto.setChunkMethod(info.getChunkMethod().name().toLowerCase());
+ }
+ if (info.getParserConfig() != null) {
+ dto.setParserConfig(objectMapper.convertValue(info.getParserConfig(),
+ new TypeReference<Map<String, Object>>() {
+ }));
+ }
+
+ return dto;
+ }
+
+ private static class MultipartFileResource extends AbstractResource {
+ private final MultipartFile multipartFile;
+
+ public MultipartFileResource(MultipartFile multipartFile) {
+ this.multipartFile = multipartFile;
+ }
+
+ @Override
+ public String getDescription() {
+ return "MultipartFile resource [" + multipartFile.getOriginalFilename() + "]";
+ }
+
+ @Override
+ public String getFilename() {
+ return multipartFile.getOriginalFilename();
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ return multipartFile.getInputStream();
+ }
+
+ @Override
+ public long contentLength() throws IOException {
+ return multipartFile.getSize();
+ }
+
+ @Override
+ public boolean exists() {
+ return !multipartFile.isEmpty();
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/KnowledgeBaseService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/KnowledgeBaseService.java
new file mode 100644
index 0000000..2053562
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/KnowledgeBaseService.java
@@ -0,0 +1,106 @@
+package xiaozhi.modules.knowledge.service;
+
+import java.util.List;
+import java.util.Map;
+
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.service.BaseService;
+import xiaozhi.modules.knowledge.dto.KnowledgeBaseDTO;
+import xiaozhi.modules.knowledge.entity.KnowledgeBaseEntity;
+import xiaozhi.modules.model.entity.ModelConfigEntity;
+
+/**
+ * 鐭ヨ瘑搴撶煡璇嗗簱鏈嶅姟鎺ュ彛
+ */
+public interface KnowledgeBaseService extends BaseService<KnowledgeBaseEntity> {
+
+ /**
+ * 鍒嗛〉鏌ヨ鐭ヨ瘑搴撳垪琛�
+ *
+ * @param knowledgeBaseDTO 鏌ヨ鏉′欢
+ * @param page 椤电爜
+ * @param limit 姣忛〉鏁伴噺
+ * @return 鍒嗛〉鏁版嵁
+ */
+ PageData<KnowledgeBaseDTO> getPageList(KnowledgeBaseDTO knowledgeBaseDTO, Integer page, Integer limit);
+
+ /**
+ * 鏍规嵁ID鑾峰彇鐭ヨ瘑搴撹鎯�
+ *
+ * @param id 鐭ヨ瘑搴揑D
+ * @return 鐭ヨ瘑搴撹鎯�
+ */
+ KnowledgeBaseDTO getById(String id);
+
+ /**
+ * 鏂板鐭ヨ瘑搴�
+ *
+ * @param knowledgeBaseDTO 鐭ヨ瘑搴撲俊鎭�
+ * @return 鏂板鐨勭煡璇嗗簱
+ */
+ KnowledgeBaseDTO save(KnowledgeBaseDTO knowledgeBaseDTO);
+
+ /**
+ * 鏇存柊鐭ヨ瘑搴�
+ *
+ * @param knowledgeBaseDTO 鐭ヨ瘑搴撲俊鎭�
+ * @return 鏇存柊鐨勭煡璇嗗簱
+ */
+ KnowledgeBaseDTO update(KnowledgeBaseDTO knowledgeBaseDTO);
+
+ /**
+ * 鏍规嵁鐭ヨ瘑搴揑D鏌ヨ鐭ヨ瘑搴�
+ *
+ * @param datasetId 鐭ヨ瘑搴揑D
+ * @return 鐭ヨ瘑搴撹鎯�
+ */
+ KnowledgeBaseDTO getByDatasetId(String datasetId);
+
+ /**
+ * 鏍规嵁鐭ヨ瘑搴揑D闆嗗悎鏌ヨ鐭ヨ瘑搴�
+ *
+ * @param datasetIdList 鐭ヨ瘑搴揑D闆嗗悎
+ * @return 鐭ヨ瘑搴撹鎯�
+ */
+ List<KnowledgeBaseDTO> getByDatasetIdList(List<String> datasetIdList);
+
+ /**
+ * 鏍规嵁鐭ヨ瘑搴揑D鍒犻櫎鐭ヨ瘑搴�
+ *
+ * @param datasetId 鐭ヨ瘑搴揑D
+ */
+ void deleteByDatasetId(String datasetId);
+
+ /**
+ * 鑾峰彇RAG閰嶇疆淇℃伅
+ *
+ * @param ragModelId RAG妯″瀷閰嶇疆ID
+ * @return RAG閰嶇疆淇℃伅
+ */
+ Map<String, Object> getRAGConfig(String ragModelId);
+
+ /**
+ * 鏍规嵁鐭ヨ瘑搴揑D鑾峰彇瀵瑰簲鐨凴AG閰嶇疆
+ *
+ * @param datasetId 鐭ヨ瘑搴揑D
+ * @return RAG閰嶇疆
+ */
+ Map<String, Object> getRAGConfigByDatasetId(String datasetId);
+
+ /**
+ * 鑾峰彇RAG妯″瀷鍒楄〃
+ *
+ * @return RAG妯″瀷鍒楄〃
+ */
+ List<ModelConfigEntity> getRAGModels();
+
+ /**
+ * 鏇存柊鐭ヨ瘑搴撶粺璁′俊鎭� (鐢ㄤ簬琚枃浠舵湇鍔″洖璋�)
+ *
+ * @param datasetId 鐭ヨ瘑搴揑D
+ * @param docDelta 鏂囨。鏁板閲�
+ * @param chunkDelta 鍒嗗潡鏁板閲�
+ * @param tokenDelta Token鏁板閲�
+ */
+ void updateStatistics(String datasetId, Integer docDelta, Long chunkDelta, Long tokenDelta);
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/KnowledgeFilesService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/KnowledgeFilesService.java
new file mode 100644
index 0000000..f832c1b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/KnowledgeFilesService.java
@@ -0,0 +1,132 @@
+package xiaozhi.modules.knowledge.service;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.web.multipart.MultipartFile;
+
+import xiaozhi.common.page.PageData;
+import xiaozhi.modules.knowledge.dto.KnowledgeFilesDTO;
+import xiaozhi.modules.knowledge.dto.document.ChunkDTO;
+import xiaozhi.modules.knowledge.dto.document.RetrievalDTO;
+import xiaozhi.modules.knowledge.dto.document.DocumentDTO;
+
+/**
+ * 鐭ヨ瘑搴撴枃妗f湇鍔℃帴鍙�
+ */
+public interface KnowledgeFilesService {
+
+ /**
+ * 鍒嗛〉鏌ヨ鏂囨。鍒楄〃
+ *
+ * @param knowledgeFilesDTO 鏌ヨ鏉′欢
+ * @param page 椤电爜
+ * @param limit 姣忛〉鏁伴噺
+ * @return 鍒嗛〉鏁版嵁
+ */
+ PageData<KnowledgeFilesDTO> getPageList(KnowledgeFilesDTO knowledgeFilesDTO, Integer page, Integer limit);
+
+ /**
+ * 鏍规嵁鏂囨。ID鍜岀煡璇嗗簱ID鑾峰彇鏂囨。璇︽儏
+ *
+ * @param documentId 鏂囨。ID
+ * @param datasetId 鐭ヨ瘑搴揑D
+ * @return 鏂囨。璇︽儏 (寮虹被鍨� InfoVO)
+ */
+ DocumentDTO.InfoVO getByDocumentId(String documentId, String datasetId);
+
+ /**
+ * 涓婁紶鏂囨。鍒扮煡璇嗗簱
+ *
+ * @param datasetId 鐭ヨ瘑搴揑D
+ * @param file 涓婁紶鐨勬枃浠�
+ * @param name 鏂囨。鍚嶇О
+ * @param metaFields 鍏冩暟鎹瓧娈�
+ * @param chunkMethod 鍒嗗潡鏂规硶
+ * @param parserConfig 瑙f瀽鍣ㄩ厤缃�
+ * @return 涓婁紶鐨勬枃妗d俊鎭�
+ */
+ KnowledgeFilesDTO uploadDocument(String datasetId, MultipartFile file, String name,
+ Map<String, Object> metaFields, String chunkMethod,
+ Map<String, Object> parserConfig);
+
+ /**
+ * 鎵归噺鍒犻櫎鏂囨。
+ *
+ * @param datasetId 鐭ヨ瘑搴揑D
+ * @param req 鍒犻櫎璇锋眰鍙傛暟 (鍚枃妗D鍒楄〃)
+ */
+ void deleteDocuments(String datasetId, DocumentDTO.BatchIdReq req);
+
+ /**
+ * 鑾峰彇RAG閰嶇疆淇℃伅
+ *
+ * @param ragModelId RAG妯″瀷閰嶇疆ID
+ * @return RAG閰嶇疆淇℃伅
+ */
+ Map<String, Object> getRAGConfig(String ragModelId);
+
+ /**
+ * 瑙f瀽鏂囨。锛堝垏鍧楋級
+ *
+ * @param datasetId 鐭ヨ瘑搴揑D
+ * @param documentIds 鏂囨。ID鍒楄〃
+ * @return 瑙f瀽缁撴灉
+ */
+ boolean parseDocuments(String datasetId, List<String> documentIds);
+
+ /**
+ * 鍒楀嚭鎸囧畾鏂囨。鐨勫垏鐗�
+ *
+ * @param datasetId 鐭ヨ瘑搴揑D
+ * @param documentId 鏂囨。ID
+ * @param req 鍒囩墖鍒楄〃璇锋眰鍙傛暟
+ * @return 鍒囩墖鍒楄〃淇℃伅
+ */
+ ChunkDTO.ListVO listChunks(String datasetId, String documentId, ChunkDTO.ListReq req);
+
+ /**
+ * 鍙洖娴嬭瘯
+ *
+ * @param req 妫�绱㈡祴璇曡姹傚弬鏁�
+ * @return 鍙洖娴嬭瘯缁撴灉
+ */
+ RetrievalDTO.ResultVO retrievalTest(RetrievalDTO.TestReq req);
+
+ /**
+ * 淇濆瓨鏂囨。褰卞瓙璁板綍
+ */
+ boolean saveDocumentShadow(String datasetId, KnowledgeFilesDTO result, String originalName, String chunkMethod,
+ Map<String, Object> parserConfig);
+
+ /**
+ * 鎵归噺鍒犻櫎鏂囨。褰卞瓙璁板綍骞跺悓姝ョ粺璁℃暟鎹�
+ *
+ * @param documentIds 鏂囨。ID鍒楄〃
+ * @param datasetId 鏁版嵁闆咺D
+ * @param chunkDelta 寰呮墸鍑忕殑鎬诲垎鍧楁暟
+ * @param tokenDelta 寰呮墸鍑忕殑鎬籘oken鏁�
+ */
+ void deleteDocumentShadows(List<String> documentIds, String datasetId, Long chunkDelta, Long tokenDelta);
+
+ /**
+ * 鏍规嵁鏁版嵁闆咺D娓呯悊鎵�鏈夊叧鑱旀枃妗� (绾ц仈鍒犻櫎涓撶敤)
+ *
+ * @param datasetId 鏁版嵁闆咺D
+ */
+ void deleteDocumentsByDatasetId(String datasetId);
+
+ /**
+ * 鍚屾鎵�鏈夊浜� RUNNING 鐘舵�佺殑鏂囨。 (渚涘畾鏃朵换鍔¤皟鐢�)
+ */
+ void syncRunningDocuments();
+
+ /**
+ * 浠嶳AGFlow鍏ㄩ噺鍚屾鏂囨。鍒版湰鍦板奖瀛愯〃
+ * 鎷夊彇杩滅鎵�鏈夋枃妗o紝涓庢湰鍦板奖瀛愯〃瀵规瘮锛屾彃鍏ョ己澶辩殑璁板綍
+ *
+ * @param datasetId 鏁版嵁闆咺D
+ * @return 鏂板悓姝ョ殑鏂囨。鏁伴噺
+ */
+ int syncDocumentsFromRAG(String datasetId);
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/KnowledgeManagerService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/KnowledgeManagerService.java
new file mode 100644
index 0000000..940479f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/KnowledgeManagerService.java
@@ -0,0 +1,24 @@
+package xiaozhi.modules.knowledge.service;
+
+import java.util.List;
+
+/**
+ * 鐭ヨ瘑搴撴ā鍧楅鍩熺紪鎺掓湇鍔�
+ * 鐢ㄤ簬澶勭悊璺� KnowledgeBase 鍜� KnowledgeFiles 鐨勫鏉備笟鍔℃祦绋嬶紝褰诲簳瑙e喅 Service 闂寸殑寰幆渚濊禆闂銆�
+ */
+public interface KnowledgeManagerService {
+
+ /**
+ * 绾ц仈鍒犻櫎鐭ヨ瘑搴撳強鍏朵笅灞炴墍鏈夋枃妗� (鍖呮嫭鏈湴 DB 鍜� RAGFlow 杩滅▼鏁版嵁)
+ *
+ * @param datasetId 鐭ヨ瘑搴� ID
+ */
+ void deleteDatasetWithFiles(String datasetId);
+
+ /**
+ * 鎵归噺绾ц仈鍒犻櫎鐭ヨ瘑搴�
+ *
+ * @param datasetIds 鐭ヨ瘑搴� ID 鍒楄〃
+ */
+ void batchDeleteDatasetsWithFiles(List<String> datasetIds);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/impl/KnowledgeBaseServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/impl/KnowledgeBaseServiceImpl.java
new file mode 100644
index 0000000..918148d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/impl/KnowledgeBaseServiceImpl.java
@@ -0,0 +1,501 @@
+package xiaozhi.modules.knowledge.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.beans.BeanUtils;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.redis.RedisKeys;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.common.service.impl.BaseServiceImpl;
+import xiaozhi.common.utils.ConvertUtils;
+import xiaozhi.common.utils.JsonUtils;
+import xiaozhi.modules.knowledge.dao.KnowledgeBaseDao;
+import xiaozhi.modules.knowledge.dao.DocumentDao;
+import xiaozhi.modules.knowledge.entity.DocumentEntity;
+import xiaozhi.modules.knowledge.dto.KnowledgeBaseDTO;
+import xiaozhi.modules.knowledge.dto.dataset.DatasetDTO;
+import xiaozhi.modules.knowledge.entity.KnowledgeBaseEntity;
+import xiaozhi.modules.knowledge.rag.KnowledgeBaseAdapter;
+import xiaozhi.modules.knowledge.rag.KnowledgeBaseAdapterFactory;
+import xiaozhi.modules.knowledge.service.KnowledgeBaseService;
+import xiaozhi.modules.model.dao.ModelConfigDao;
+import xiaozhi.modules.model.entity.ModelConfigEntity;
+import xiaozhi.modules.model.service.ModelConfigService;
+import xiaozhi.modules.security.user.SecurityUser;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 鐭ヨ瘑搴撴湇鍔″疄鐜扮被 (Refactored)
+ * 闆嗘垚 RAGFlow Adapter 涓� Shadow DB 妯″紡
+ */
+@Service
+@AllArgsConstructor
+@Slf4j
+public class KnowledgeBaseServiceImpl extends BaseServiceImpl<KnowledgeBaseDao, KnowledgeBaseEntity>
+ implements KnowledgeBaseService {
+
+ private final KnowledgeBaseDao knowledgeBaseDao;
+ private final DocumentDao documentDao;
+ private final ModelConfigService modelConfigService;
+ private final ModelConfigDao modelConfigDao;
+ private final RedisUtils redisUtils;
+
+ @Override
+ public PageData<KnowledgeBaseDTO> getPageList(KnowledgeBaseDTO knowledgeBaseDTO, Integer page, Integer limit) {
+ Page<KnowledgeBaseEntity> pageInfo = new Page<>(page, limit);
+ QueryWrapper<KnowledgeBaseEntity> queryWrapper = new QueryWrapper<>();
+
+ if (knowledgeBaseDTO != null) {
+ queryWrapper.like(StringUtils.isNotBlank(knowledgeBaseDTO.getName()), "name", knowledgeBaseDTO.getName());
+ queryWrapper.eq(knowledgeBaseDTO.getStatus() != null, "status", knowledgeBaseDTO.getStatus());
+ queryWrapper.eq("creator", knowledgeBaseDTO.getCreator());
+ }
+ queryWrapper.orderByDesc("created_at");
+
+ IPage<KnowledgeBaseEntity> iPage = knowledgeBaseDao.selectPage(pageInfo, queryWrapper);
+ PageData<KnowledgeBaseDTO> pageData = getPageData(iPage, KnowledgeBaseDTO.class);
+
+ // Enrich with Document Count from RAG (Optional / Lazy)
+ if (pageData != null && pageData.getList() != null) {
+ pageData.getList().removeIf(dto -> {
+ enrichDocumentCount(dto);
+ // syncDatasetFromRAG 妫�娴嬪埌 RAGFlow 绔凡鍒犻櫎鏃讹紝浼氬皢鏈湴璁板綍娓呯悊
+ // 姝ゆ椂 datasetId 琚疆绌轰綔涓烘爣璁帮紝闇�瑕佸湪鍒楄〃涓Щ闄よ鏉$洰
+ return dto.getDatasetId() == null;
+ });
+ }
+ return pageData;
+ }
+
+ private void enrichDocumentCount(KnowledgeBaseDTO dto) {
+ syncDatasetFromRAG(dto);
+ }
+
+ /**
+ * 浠� RAGFlow 鍚屾鏁版嵁闆嗕俊鎭細妫�娴嬪垹闄ゃ�佸悓姝ュ悕绉�/绠�浠嬨�佽幏鍙栨枃妗f暟閲�
+ * 姣忔鍒楄〃鍒锋柊鏃跺疄鏃舵煡璇� RAGFlow锛岀‘淇濈珛鍗虫劅鐭ヨ繙绔彉鏇�
+ */
+ private void syncDatasetFromRAG(KnowledgeBaseDTO dto) {
+ try {
+ if (StringUtils.isBlank(dto.getDatasetId()) || StringUtils.isBlank(dto.getRagModelId())) {
+ return;
+ }
+
+ KnowledgeBaseAdapter adapter = getAdapterByModelId(dto.getRagModelId());
+ if (adapter == null) {
+ return;
+ }
+
+ DatasetDTO.InfoVO datasetInfo = adapter.getDatasetInfo(dto.getDatasetId());
+ // getDatasetInfo 姝e父杩斿洖 null 琛ㄧず杩滅纭疄涓嶅瓨鍦紱寮傚父鏃跺凡鎶涘嚭 RenException 鐢卞灞� catch 鎺ョ
+ if (datasetInfo == null) {
+ // RAGFlow 绔凡纭鍒犻櫎 鈫� 鏈湴绾ц仈娓呯悊
+ log.info("鏁版嵁闆� {} 鍦� RAGFlow 绔笉瀛樺湪锛屾墽琛屾湰鍦版竻鐞�", dto.getDatasetId());
+ cleanupLocalDataset(dto.getDatasetId(), dto.getId());
+ // 鏍囪涓哄凡鍒犻櫎锛岃涓婂眰浠庡垪琛ㄤ腑绉婚櫎
+ dto.setDatasetId(null);
+ return;
+ }
+
+ // 鍚屾鍚嶇О锛堝幓鎺� username_ 鍓嶇紑锛�
+ String ragflowName = datasetInfo.getName();
+ if (StringUtils.isNotBlank(ragflowName)) {
+ String localName = ragflowName.contains("_") ? ragflowName.substring(ragflowName.indexOf('_') + 1) : ragflowName;
+ if (!localName.equals(dto.getName())) {
+ log.info("鍚屾鐭ヨ瘑搴撳悕绉�: {} -> {}", dto.getName(), localName);
+ KnowledgeBaseEntity entity = knowledgeBaseDao.selectById(dto.getId());
+ if (entity != null) {
+ entity.setName(localName);
+ knowledgeBaseDao.updateById(entity);
+ dto.setName(localName);
+ }
+ }
+ }
+
+ // 鍚屾绠�浠�
+ String ragflowDesc = datasetInfo.getDescription();
+ String localDesc = dto.getDescription();
+ boolean descChanged = (ragflowDesc == null && localDesc != null) || (ragflowDesc != null && !ragflowDesc.equals(localDesc));
+ if (descChanged) {
+ log.info("鍚屾鐭ヨ瘑搴撶畝浠�: datasetId={}", dto.getDatasetId());
+ KnowledgeBaseEntity entity = knowledgeBaseDao.selectById(dto.getId());
+ if (entity != null) {
+ entity.setDescription(ragflowDesc);
+ knowledgeBaseDao.updateById(entity);
+ dto.setDescription(ragflowDesc);
+ }
+ }
+
+ // 璁剧疆鏂囨。鏁伴噺锛堜繚鐣欏師鏈夊姛鑳斤級
+ if (datasetInfo.getDocumentCount() != null) {
+ dto.setDocumentCount(datasetInfo.getDocumentCount().intValue());
+ }
+
+ } catch (Exception e) {
+ log.error("鍚屾鏁版嵁闆嗕俊鎭け璐� {}: {}", dto.getName(), e.getMessage());
+ dto.setDocumentCount(0);
+ dto.setErrorMessage(e.getMessage());
+ }
+ }
+
+ /**
+ * 鏈湴绾ц仈娓呯悊锛歊AGFlow 绔凡鍒犻櫎鏃讹紝娓呯悊鏈湴鎵�鏈夊叧鑱旀暟鎹�
+ * 涓嶈皟鐢� RAGFlow 鍒犻櫎 API
+ */
+ @Transactional(rollbackFor = Exception.class)
+ public void cleanupLocalDataset(String datasetId, String entityId) {
+ try {
+ // 1. 鍒犻櫎鏂囨。褰卞瓙璁板綍
+ documentDao.delete(new QueryWrapper<DocumentEntity>().eq("dataset_id", datasetId));
+ // 2. 鍒犻櫎鎻掍欢鏄犲皠
+ knowledgeBaseDao.deletePluginMappingByKnowledgeBaseId(entityId);
+ // 3. 鍒犻櫎鐭ヨ瘑搴撹褰�
+ knowledgeBaseDao.deleteById(entityId);
+ // 4. 娓呯悊缂撳瓨
+ redisUtils.delete(RedisKeys.getKnowledgeBaseCacheKey(entityId));
+ log.info("鏈湴绾ц仈娓呯悊瀹屾垚: datasetId={}, entityId={}", datasetId, entityId);
+ } catch (Exception e) {
+ log.error("鏈湴绾ц仈娓呯悊澶辫触: datasetId={}, entityId={}", datasetId, entityId, e);
+ }
+ }
+
+ @Override
+ public KnowledgeBaseDTO getById(String id) {
+ KnowledgeBaseEntity entity = knowledgeBaseDao.selectById(id);
+ if (entity == null) {
+ throw new RenException(ErrorCode.Knowledge_Base_RECORD_NOT_EXISTS);
+ }
+ return ConvertUtils.sourceToTarget(entity, KnowledgeBaseDTO.class);
+ }
+
+ @Override
+ public KnowledgeBaseDTO getByDatasetId(String datasetId) {
+ if (StringUtils.isBlank(datasetId)) {
+ throw new RenException(ErrorCode.PARAMS_GET_ERROR);
+ }
+ // [Production Fix] 鍏煎鎬ф煡鎵撅細浼樺厛閫氳繃 dataset_id 鎵撅紝鎵句笉鍒伴�氳繃涓婚敭 id 鎵撅紝纭繚鍓嶇浼犲摢绉� UUID 閮借兘鍛戒腑
+ KnowledgeBaseEntity entity = knowledgeBaseDao
+ .selectOne(new QueryWrapper<KnowledgeBaseEntity>()
+ .eq("dataset_id", datasetId)
+ .or()
+ .eq("id", datasetId));
+ if (entity == null) {
+ throw new RenException(ErrorCode.Knowledge_Base_RECORD_NOT_EXISTS);
+ }
+ return ConvertUtils.sourceToTarget(entity, KnowledgeBaseDTO.class);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public KnowledgeBaseDTO save(KnowledgeBaseDTO dto) {
+ // 1. Validation
+ checkDuplicateName(dto.getName(), null);
+ KnowledgeBaseAdapter adapter = null;
+
+ // 2. RAG Creation
+ String datasetId = null;
+ try {
+ // 鑻ユ湭鎸囧畾 RAG 妯″瀷锛岃嚜鍔ㄤ娇鐢ㄧ郴缁熼粯璁�
+ if (StringUtils.isBlank(dto.getRagModelId())) {
+ List<ModelConfigEntity> models = getRAGModels();
+ if (models != null && !models.isEmpty()) {
+ dto.setRagModelId(models.get(0).getId());
+ } else {
+ throw new RenException(ErrorCode.RAG_CONFIG_NOT_FOUND, "鏈寚瀹氫笖鏃犲彲鐢ㄩ粯璁� RAG 妯″瀷");
+ }
+ }
+
+ Map<String, Object> ragConfig = getValidatedRAGConfig(dto.getRagModelId());
+ adapter = KnowledgeBaseAdapterFactory.getAdapter((String) ragConfig.get("type"),
+ ragConfig);
+
+ DatasetDTO.CreateReq createReq = ConvertUtils.sourceToTarget(dto, DatasetDTO.CreateReq.class);
+ createReq.setName(SecurityUser.getUser().getUsername() + "_" + dto.getName());
+
+ DatasetDTO.InfoVO ragResponse = adapter.createDataset(createReq);
+ if (ragResponse == null || StringUtils.isBlank(ragResponse.getId())) {
+ throw new RenException(ErrorCode.RAG_API_ERROR, "RAG鍒涘缓杩斿洖鏃犳晥: 缂哄けID");
+ }
+ datasetId = ragResponse.getId();
+
+ // 3. Local Save (Shadow)
+ KnowledgeBaseEntity entity = ConvertUtils.sourceToTarget(dto, KnowledgeBaseEntity.class);
+
+ // [Production Fix] 缁熶竴鏈湴 ID 涓� RAGFlow ID锛岄槻姝㈠墠绔皟鐢� /delete 鎴� /update 鏃跺洜 ID 娣锋穯锛堟湰鍦�
+ // UUID vs RAG UUID锛夊鑷� 10163 閿欒
+ entity.setId(datasetId);
+ entity.setDatasetId(datasetId);
+ entity.setStatus(1); // Default Enabled
+
+ // 鉁� FULL PERSISTENCE: 涓ユ牸鍏ㄩ噺鍥炲啓 (User Requirement)
+ // 浣跨敤寮虹被鍨� DTO 灞炴�ц幏鍙栵紝涓嶅啀浠� Map 涓墜鍔ㄨВ鏋� Key
+ entity.setTenantId(ragResponse.getTenantId());
+ entity.setChunkMethod(ragResponse.getChunkMethod());
+ entity.setEmbeddingModel(ragResponse.getEmbeddingModel());
+ entity.setPermission(ragResponse.getPermission());
+
+ if (StringUtils.isBlank(entity.getAvatar())) {
+ entity.setAvatar(ragResponse.getAvatar());
+ }
+
+ // Parse Config (JSON)
+ if (ragResponse.getParserConfig() != null) {
+ entity.setParserConfig(JsonUtils.toJsonString(ragResponse.getParserConfig()));
+ }
+
+ // Numeric fields
+ entity.setChunkCount(ragResponse.getChunkCount() != null ? ragResponse.getChunkCount() : 0L);
+ entity.setDocumentCount(ragResponse.getDocumentCount() != null ? ragResponse.getDocumentCount() : 0L);
+ entity.setTokenNum(ragResponse.getTokenNum() != null ? ragResponse.getTokenNum() : 0L);
+
+ // 娓呯┖ creator/updater锛岃 FieldMetaObjectHandler 浠� SecurityUser 鑷姩濉厖
+ // ConvertUtils 浼氭妸 DTO 涓殑 creator=0 鎷疯礉杩囨潵锛屽鑷� strictInsertFill 璺宠繃濉厖
+ entity.setCreator(null);
+ entity.setUpdater(null);
+
+ knowledgeBaseDao.insert(entity);
+ return ConvertUtils.sourceToTarget(entity, KnowledgeBaseDTO.class);
+ } catch (Exception e) {
+ log.error("RAG鍒涘缓鎴栨湰鍦颁繚瀛樺け璐�", e);
+ // 濡傛灉datasetId宸茬敓鎴愪絾鍦ㄤ繚瀛樻湰鍦版椂澶辫触锛屽皾璇曞洖婊歊AG (Best Effort)
+ if (StringUtils.isNotBlank(datasetId)) {
+ try {
+ if (adapter != null)
+ adapter.deleteDataset(
+ DatasetDTO.BatchIdReq.builder().ids(Collections.singletonList(datasetId)).build());
+ } catch (Exception rollbackEx) {
+ log.error("RAG鍥炴粴澶辫触: {}", datasetId, rollbackEx);
+ }
+ }
+ if (e instanceof RenException) {
+ throw (RenException) e;
+ }
+ throw new RenException(ErrorCode.RAG_API_ERROR, "鍒涘缓鐭ヨ瘑搴撳け璐�: " + e.getMessage());
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ @SuppressWarnings("deprecation")
+ public KnowledgeBaseDTO update(KnowledgeBaseDTO dto) {
+ log.info("Update Service Called: ID={}, DatasetID={}", dto.getId(), dto.getDatasetId());
+ KnowledgeBaseEntity entity = knowledgeBaseDao.selectById(dto.getId());
+ if (entity == null) {
+ log.error("Update failed: Entity not found for ID={}", dto.getId());
+ throw new RenException(ErrorCode.Knowledge_Base_RECORD_NOT_EXISTS);
+ }
+
+ checkDuplicateName(dto.getName(), dto.getId());
+
+ // 楠岃瘉鏁版嵁闆咺D鏄惁涓庡叾浠栬褰曞啿绐�
+ if (StringUtils.isNotBlank(dto.getDatasetId())) {
+ KnowledgeBaseEntity conflictEntity = knowledgeBaseDao.selectOne(
+ new QueryWrapper<KnowledgeBaseEntity>()
+ .eq("dataset_id", dto.getDatasetId())
+ .ne("id", dto.getId()));
+ if (conflictEntity != null) {
+ throw new RenException(ErrorCode.DB_RECORD_EXISTS);
+ }
+ }
+
+ // RAG Update if needed
+ if (StringUtils.isNotBlank(entity.getDatasetId()) && StringUtils.isNotBlank(dto.getRagModelId())) {
+ try {
+ // 馃 AUTO-FILL: 鑻� DTO 鏈紶 ragModelId (鏋佸皯鎯呭喌)锛屽皾璇曞鐢� Entity 涓殑
+ if (StringUtils.isBlank(dto.getRagModelId())) {
+ dto.setRagModelId(entity.getRagModelId());
+ }
+
+ // [FIX] 鏅鸿兘琛ュ叏锛氬鏋� DTO 閲岀殑鍏抽敭瀛楁涓虹┖锛屽垯浣跨敤 Entity 閲岀殑鏃у��
+ // 纭繚鍙戠粰 RAGFlow 鐨勮姹傚寘鍚墍鏈夊繀濉」 (Partial Update Support)
+ if (StringUtils.isBlank(dto.getPermission())) {
+ dto.setPermission(entity.getPermission());
+ }
+ if (StringUtils.isBlank(dto.getChunkMethod())) {
+ dto.setChunkMethod(entity.getChunkMethod());
+ }
+
+ KnowledgeBaseAdapter adapter = getAdapterByModelId(dto.getRagModelId());
+ if (adapter != null) {
+ DatasetDTO.UpdateReq updateReq = ConvertUtils.sourceToTarget(dto, DatasetDTO.UpdateReq.class);
+
+ // 1. 蹇呭~/鏍稿績瀛楁鍓嶇紑澶勭悊
+ if (StringUtils.isNotBlank(dto.getName())) {
+ updateReq.setName(SecurityUser.getUser().getUsername() + "_" + dto.getName());
+ }
+
+ // 2. 瑙f瀽鍣ㄩ厤缃敮鎸� (濡傛灉 DTO 閲屾湁瀛楃涓插舰寮忕殑閰嶇疆锛屽皾璇曡浆鎹紝浣嗕紭鍏堝缓璁� DTO 鍖�)
+ if (StringUtils.isNotBlank(dto.getParserConfig())) {
+ try {
+ DatasetDTO.ParserConfig parserConfig = JsonUtils.parseObject(dto.getParserConfig(),
+ DatasetDTO.ParserConfig.class);
+ updateReq.setParserConfig(parserConfig);
+ } catch (Exception e) {
+ log.warn("瑙f瀽 parser_config 澶辫触锛岃烦杩囧悓姝�", e);
+ }
+ }
+
+ adapter.updateDataset(entity.getDatasetId(), updateReq);
+ log.info("RAG鏇存柊鎴愬姛: {}", entity.getDatasetId());
+ }
+ } catch (Exception e) {
+ log.error("RAG鏇存柊澶辫触", e);
+ // 鎭㈠浜嬪姟涓�鑷存�э細RAG澶辫触鍒欐暣浣撳洖婊�
+ if (e instanceof RenException) {
+ throw (RenException) e;
+ }
+ throw new RenException(ErrorCode.RAG_API_ERROR, "RAG鏇存柊澶辫触: " + e.getMessage());
+ }
+ }
+
+ BeanUtils.copyProperties(dto, entity);
+ knowledgeBaseDao.updateById(entity);
+
+ // Clean cache
+ redisUtils.delete(RedisKeys.getKnowledgeBaseCacheKey(entity.getId()));
+
+ return ConvertUtils.sourceToTarget(entity, KnowledgeBaseDTO.class);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteByDatasetId(String datasetId) {
+ if (StringUtils.isBlank(datasetId)) {
+ throw new RenException(ErrorCode.PARAMS_GET_ERROR);
+ }
+
+ KnowledgeBaseEntity entity = knowledgeBaseDao
+ .selectOne(new QueryWrapper<KnowledgeBaseEntity>().eq("dataset_id", datasetId));
+
+ // 1. 鎭㈠ 404 鏍¢獙锛氭壘涓嶅埌璁板綍鎶涘紓甯�
+ if (entity == null) {
+ log.warn("璁板綍涓嶅瓨鍦紝datasetId: {}", datasetId);
+ throw new RenException(ErrorCode.Knowledge_Base_RECORD_NOT_EXISTS);
+ }
+ log.info("鎵惧埌璁板綍: ID={}, datasetId={}, ragModelId={}",
+ entity.getId(), entity.getDatasetId(), entity.getRagModelId());
+
+ // 2. RAG Delete (Strict Mode)
+ // 鎭㈠涓ユ牸涓�鑷存�э細RAG 鍒犻櫎澶辫触鍒欐姏鍑哄紓甯革紝瑙﹀彂浜嬪姟鍥炴粴锛屼笉鍏佽宸插垹闄ゆ湰鍦颁絾淇濈暀杩滅▼鐨勮剰鏁版嵁
+ boolean apiDeleteSuccess = false;
+ if (StringUtils.isNotBlank(entity.getRagModelId()) && StringUtils.isNotBlank(entity.getDatasetId())) {
+ try {
+ KnowledgeBaseAdapter adapter = getAdapterByModelId(entity.getRagModelId());
+ if (adapter != null) {
+ adapter.deleteDataset(
+ DatasetDTO.BatchIdReq.builder().ids(Collections.singletonList(datasetId)).build());
+ }
+ apiDeleteSuccess = true;
+ } catch (Exception e) {
+ log.error("RAG鍒犻櫎澶辫触锛岃Е鍙戝洖婊�", e);
+ if (e instanceof RenException) {
+ throw (RenException) e;
+ }
+ throw new RenException(ErrorCode.RAG_API_ERROR, "RAG鍒犻櫎澶辫触: " + e.getMessage());
+ }
+ } else {
+ log.warn("datasetId鎴杛agModelId涓虹┖锛岃烦杩嘡AG鍒犻櫎");
+ apiDeleteSuccess = true; // 娌℃湁RAG鏁版嵁闆嗭紝瑙嗕负鎴愬姛
+ }
+
+ // 3. Local Delete (Safe Order)
+ // 鎭㈠姝g‘椤哄簭锛氬厛鍒犲瓙琛� (Plugin Mapping)锛屽啀鍒犱富琛� (Entity)
+ if (apiDeleteSuccess) {
+ log.info("寮�濮嬪垹闄i_agent_plugin_mapping琛ㄤ腑涓庣煡璇嗗簱ID '{}' 鐩稿叧鐨勬槧灏勮褰�", entity.getId());
+ log.info("寮�濮嬪垹闄ゅ叧鑱旀暟鎹�, entityId: {}", entity.getId());
+ knowledgeBaseDao.deletePluginMappingByKnowledgeBaseId(entity.getId());
+ log.info("鎻掍欢鏄犲皠璁板綍鍒犻櫎瀹屾垚");
+ int deleteCount = knowledgeBaseDao.deleteById(entity.getId());
+ log.info("鏈湴鏁版嵁搴撳垹闄ょ粨鏋�: {}", deleteCount > 0 ? "鎴愬姛" : "澶辫触");
+ redisUtils.delete(RedisKeys.getKnowledgeBaseCacheKey(entity.getId()));
+ }
+ }
+
+ @Override
+ public List<KnowledgeBaseDTO> getByDatasetIdList(List<String> datasetIdList) {
+ if (datasetIdList == null || datasetIdList.isEmpty()) {
+ return Collections.emptyList();
+ }
+ // [Production Fix] 鎵归噺鍏煎鎬ф煡鎵�
+ QueryWrapper<KnowledgeBaseEntity> queryWrapper = new QueryWrapper<>();
+ queryWrapper.in("dataset_id", datasetIdList).or().in("id", datasetIdList);
+ List<KnowledgeBaseEntity> list = knowledgeBaseDao.selectList(queryWrapper);
+ return ConvertUtils.sourceToTarget(list, KnowledgeBaseDTO.class);
+ }
+
+ @Override
+ public Map<String, Object> getRAGConfig(String ragModelId) {
+ return getValidatedRAGConfig(ragModelId);
+ }
+
+ @Override
+ public Map<String, Object> getRAGConfigByDatasetId(String datasetId) {
+ KnowledgeBaseEntity entity = knowledgeBaseDao
+ .selectOne(new QueryWrapper<KnowledgeBaseEntity>().eq("dataset_id", datasetId));
+ if (entity == null || StringUtils.isBlank(entity.getRagModelId())) {
+ throw new RenException(ErrorCode.RAG_CONFIG_NOT_FOUND);
+ }
+ return getRAGConfig(entity.getRagModelId());
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updateStatistics(String datasetId, Integer docDelta, Long chunkDelta, Long tokenDelta) {
+ log.info("閫掑鏇存柊鐭ヨ瘑搴撶粺璁�: datasetId={}, docs={}, chunks={}, tokens={}", datasetId, docDelta, chunkDelta, tokenDelta);
+ knowledgeBaseDao.updateStatsAfterChange(datasetId, docDelta, chunkDelta, tokenDelta);
+ }
+
+ @Override
+ public List<ModelConfigEntity> getRAGModels() {
+ return modelConfigDao.selectList(new QueryWrapper<ModelConfigEntity>()
+ .select("id", "model_name", "config_json") // Explicitly select needed fields
+ .eq("model_type", Constant.RAG_CONFIG_TYPE)
+ .eq("is_enabled", 1)
+ .orderByDesc("is_default")
+ .orderByDesc("create_date"));
+ }
+
+ // --- Helpers ---
+
+ private void checkDuplicateName(String name, String excludeId) {
+ if (StringUtils.isBlank(name))
+ return;
+ QueryWrapper<KnowledgeBaseEntity> qw = new QueryWrapper<>();
+ qw.eq("name", name).eq("creator", SecurityUser.getUserId());
+ if (excludeId != null)
+ qw.ne("id", excludeId);
+ if (knowledgeBaseDao.selectCount(qw) > 0) {
+ throw new RenException(ErrorCode.KNOWLEDGE_BASE_NAME_EXISTS);
+ }
+ }
+
+ private KnowledgeBaseAdapter getAdapterByModelId(String modelId) {
+ Map<String, Object> config = getValidatedRAGConfig(modelId);
+ return KnowledgeBaseAdapterFactory.getAdapter((String) config.get("type"), config);
+ }
+
+ private Map<String, Object> getValidatedRAGConfig(String modelId) {
+ ModelConfigEntity configEntity = modelConfigService.getModelByIdFromCache(modelId);
+ if (configEntity == null || configEntity.getConfigJson() == null) {
+ throw new RenException(ErrorCode.RAG_CONFIG_NOT_FOUND);
+ }
+ Map<String, Object> config = new HashMap<>(configEntity.getConfigJson());
+ if (!config.containsKey("type")) {
+ config.put("type", "ragflow");
+ }
+ return config;
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/impl/KnowledgeFilesServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/impl/KnowledgeFilesServiceImpl.java
new file mode 100644
index 0000000..61d1c50
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/impl/KnowledgeFilesServiceImpl.java
@@ -0,0 +1,980 @@
+package xiaozhi.modules.knowledge.service.impl;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.common.exception.ErrorCode;
+import org.springframework.util.CollectionUtils;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.modules.knowledge.dto.KnowledgeFilesDTO;
+import xiaozhi.modules.knowledge.dto.document.ChunkDTO;
+import xiaozhi.modules.knowledge.dto.document.RetrievalDTO;
+import xiaozhi.modules.knowledge.dto.document.DocumentDTO;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.redis.RedisKeys;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.common.service.impl.BaseServiceImpl;
+import xiaozhi.modules.knowledge.dao.DocumentDao;
+import xiaozhi.modules.knowledge.entity.DocumentEntity;
+import xiaozhi.modules.knowledge.rag.KnowledgeBaseAdapter;
+import xiaozhi.modules.knowledge.rag.KnowledgeBaseAdapterFactory;
+import xiaozhi.modules.knowledge.service.KnowledgeBaseService;
+import xiaozhi.modules.knowledge.service.KnowledgeFilesService;
+
+@Service
+@Slf4j
+public class KnowledgeFilesServiceImpl extends BaseServiceImpl<DocumentDao, DocumentEntity>
+ implements KnowledgeFilesService {
+
+ private final KnowledgeBaseService knowledgeBaseService;
+ private final DocumentDao documentDao;
+ private final ObjectMapper objectMapper;
+ private final RedisUtils redisUtils;
+
+ public KnowledgeFilesServiceImpl(KnowledgeBaseService knowledgeBaseService,
+ DocumentDao documentDao,
+ ObjectMapper objectMapper,
+ RedisUtils redisUtils) {
+ this.knowledgeBaseService = knowledgeBaseService;
+ this.documentDao = documentDao;
+ this.objectMapper = objectMapper;
+ this.redisUtils = redisUtils;
+ }
+
+ @Lazy
+ @Autowired
+ private KnowledgeFilesService self;
+
+ @Override
+ public Map<String, Object> getRAGConfig(String ragModelId) {
+ return knowledgeBaseService.getRAGConfig(ragModelId);
+ }
+
+ @Override
+ public PageData<KnowledgeFilesDTO> getPageList(KnowledgeFilesDTO knowledgeFilesDTO, Integer page, Integer limit) {
+ log.info("=== 寮�濮嬭幏鍙栫煡璇嗗簱鏂囨。鍒楄〃 (Local-First 浼樺寲鐗�) ===");
+ String datasetId = knowledgeFilesDTO.getDatasetId();
+ if (StringUtils.isBlank(datasetId)) {
+ throw new RenException(ErrorCode.RAG_DATASET_ID_AND_MODEL_ID_NOT_NULL);
+ }
+
+ // 鍏ㄩ噺瀵硅处鍚屾: 浠嶳AGFlow鎷夊彇杩滅鏂囨。锛屽疄鏃跺悓姝ョ‘淇濈珛鍗虫劅鐭ヨ繙绔彉鏇�
+ try {
+ self.syncDocumentsFromRAG(datasetId);
+ } catch (Exception e) {
+ log.warn("浠嶳AGFlow鍏ㄩ噺鍚屾鏂囨。澶辫触(涓嶅奖鍝嶆湰鍦版煡璇�): datasetId={}, error={}", datasetId, e.getMessage());
+ }
+
+ // 1. 鑾峰彇鏈湴褰卞瓙琛ㄦ暟鎹� (MyBatis-Plus 鍒嗛〉)
+ Page<DocumentEntity> pageParams = new Page<>(page, limit);
+ QueryWrapper<DocumentEntity> queryWrapper = new QueryWrapper<>();
+ queryWrapper.eq("dataset_id", datasetId);
+ if (StringUtils.isNotBlank(knowledgeFilesDTO.getName())) {
+ queryWrapper.like("name", knowledgeFilesDTO.getName());
+ }
+ if (StringUtils.isNotBlank(knowledgeFilesDTO.getRun())) {
+ queryWrapper.eq("run", knowledgeFilesDTO.getRun());
+ }
+ if (StringUtils.isNotBlank(knowledgeFilesDTO.getStatus())) {
+ queryWrapper.eq("status", knowledgeFilesDTO.getStatus());
+ }
+ queryWrapper.orderByDesc("created_at");
+
+ // 2. 鎵ц鏈湴鏌ヨ
+ Page<DocumentEntity> iPage = documentDao.selectPage(pageParams, queryWrapper);
+
+ // 3. 鎵嬪姩杞崲 DTO
+ List<KnowledgeFilesDTO> dtoList = new ArrayList<>();
+ for (DocumentEntity entity : iPage.getRecords()) {
+ dtoList.add(convertEntityToDTO(entity));
+ }
+ PageData<KnowledgeFilesDTO> pageData = new PageData<>(dtoList, iPage.getTotal());
+
+ // 4. 鍔ㄦ�佺姸鎬佸悓姝� (甯﹂檺娴佷笌淇濇姢)
+ // [Bug Fix] P1: 鎵╁ぇ鍚屾鐧藉悕鍗曪紝CANCEL/FAIL 涔熷厑璁镐綆棰戝悓姝ヤ互鏀寔鑷剤
+ if (pageData.getList() != null && !pageData.getList().isEmpty()) {
+ KnowledgeBaseAdapter adapter = null;
+ for (KnowledgeFilesDTO dto : pageData.getList()) {
+ String runStatus = dto.getRun();
+ // 楂樹紭鍏堢骇鍚屾: RUNNING/UNSTART (5绉掑喎鍗�)
+ boolean isActiveSync = "RUNNING".equals(runStatus) || "UNSTART".equals(runStatus);
+ // 浣庨鑷剤鍚屾: CANCEL/FAIL (60绉掑喎鍗�), 闃叉閿欒鐘舵�佹案涔呴攣姝�
+ boolean isRecoverySync = "CANCEL".equals(runStatus) || "FAIL".equals(runStatus);
+ boolean needSync = isActiveSync || isRecoverySync;
+
+ if (needSync) {
+ // 闄愭祦淇濇姢锛氭椿璺冪姸鎬� 5 绉掑喎鍗达紝鑷剤鐘舵�� 60 绉掑喎鍗�
+ long cooldownMs = isActiveSync ? 5000 : 60000;
+ DocumentEntity localEntity = documentDao.selectOne(new QueryWrapper<DocumentEntity>()
+ .eq("document_id", dto.getDocumentId()));
+ if (localEntity != null && localEntity.getLastSyncAt() != null) {
+ long diff = System.currentTimeMillis() - localEntity.getLastSyncAt().getTime();
+ if (diff < cooldownMs) {
+ continue;
+ }
+ }
+
+ // 寤惰繜鍒濆鍖栭�傞厤鍣紝浠呭湪纭疄闇�瑕佸悓姝ユ椂鍒涘缓
+ if (adapter == null) {
+ try {
+ Map<String, Object> ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetId);
+ adapter = KnowledgeBaseAdapterFactory.getAdapter(extractAdapterType(ragConfig), ragConfig);
+ } catch (Exception e) {
+ log.warn("鍚屾涓柇锛氭棤娉曞垵濮嬪寲閫傞厤鍣�, {}", e.getMessage());
+ break;
+ }
+ }
+ // [鍏抽敭淇] 璁板綍鍚屾鍓嶇殑 Token 鏁帮紝鐢ㄤ簬璁$畻澧為噺
+ Long oldTokenCount = dto.getTokenCount() != null ? dto.getTokenCount() : 0L;
+
+ syncDocumentStatusWithRAG(dto, adapter);
+
+ // 璁$畻澧為噺骞舵洿鏂扮煡璇嗗簱缁熻 (涓庡畾鏃朵换鍔′繚鎸佷竴鑷�)
+ Long newTokenCount = dto.getTokenCount() != null ? dto.getTokenCount() : 0L;
+ Long tokenDelta = newTokenCount - oldTokenCount;
+ if (tokenDelta != 0) {
+ knowledgeBaseService.updateStatistics(datasetId, 0, 0L, tokenDelta);
+ log.info("鎳掑姞杞藉悓姝�: 淇鐭ヨ瘑搴撶粺璁�, docId={}, tokenDelta={}", dto.getDocumentId(), tokenDelta);
+ }
+ }
+ }
+ }
+
+ log.info("鑾峰彇鏂囨。鍒楄〃鎴愬姛锛屾�绘暟: {}", pageData.getTotal());
+ return pageData;
+ }
+
+ /**
+ * 灏嗘湰鍦拌褰曞疄浣撹浆鎹负DTO锛屾墜鍔ㄥ榻愪笉涓�鑷村瓧娈� (size -> fileSize, type -> fileType)
+ */
+ private KnowledgeFilesDTO convertEntityToDTO(DocumentEntity entity) {
+ if (entity == null) {
+ return null;
+ }
+ KnowledgeFilesDTO dto = new KnowledgeFilesDTO();
+ // 1. 鍩虹瀛楁鎷疯礉
+ BeanUtils.copyProperties(entity, dto);
+
+ // Issue 2: 淇 ID 璇箟銆傚墠绔範鎯娇鐢� id 浣滀负鎿嶄綔涓婚敭銆�
+ // 鍦ㄨ妯″潡涓紝搴斿缁堝皢杩滅▼ documentId 鏄犲皠涓� DTO 鐨� id锛岀‘淇濆墠绔湪璇︽儏/鍒犻櫎绛夋搷浣滄椂 ID 涓�鑷淬��
+ dto.setId(entity.getDocumentId());
+
+ // 2. 灏嗘湰鍦拌褰曞疄浣撹浆鎹负DTO锛屾墜鍔ㄥ榻愪笉涓�鑷村瓧娈� (size -> fileSize, type -> fileType)
+ dto.setFileSize(entity.getSize());
+ dto.setFileType(entity.getType());
+ dto.setRun(entity.getRun());
+ dto.setChunkCount(entity.getChunkCount());
+ dto.setTokenCount(entity.getTokenCount());
+ dto.setError(entity.getError());
+
+ // 3. 鑷畾涔夊厓鏁版嵁 JSON 鍙嶅簭鍒楀寲 (Issue 3)
+ if (StringUtils.isNotBlank(entity.getMetaFields())) {
+ try {
+ dto.setMetaFields(objectMapper.readValue(entity.getMetaFields(),
+ new TypeReference<Map<String, Object>>() {
+ }));
+ } catch (Exception e) {
+ log.warn("鍙嶅簭鍒楀寲 MetaFields 澶辫触, entityId: {}, error: {}", entity.getId(), e.getMessage());
+ }
+ }
+
+ // 4. 瑙f瀽閰嶇疆 JSON 鍙嶅簭鍒楀寲
+ if (StringUtils.isNotBlank(entity.getParserConfig())) {
+ try {
+ dto.setParserConfig(objectMapper.readValue(entity.getParserConfig(),
+ new com.fasterxml.jackson.core.type.TypeReference<Map<String, Object>>() {
+ }));
+ } catch (Exception e) {
+ log.warn("鍙嶅簭鍒楀寲 ParserConfig 澶辫触, entityId: {}, error: {}", entity.getId(), e.getMessage());
+ }
+ }
+ return dto;
+
+ }
+
+ /**
+ * 鍚屾鏂囨。鐘舵�佷笌RAG瀹為檯鐘舵��
+ * 浼樺寲鐘舵�佸悓姝ラ�昏緫锛岀‘淇濊В鏋愪腑鐘舵�佽兘澶熸甯告樉绀�
+ * 鍙湁褰撴枃妗f湁鍒囩墖涓旇В鏋愭椂闂磋秴杩�30绉掓椂锛屾墠鏇存柊涓哄畬鎴愮姸鎬�
+ */
+ /**
+ * 鍚屾鏂囨。鐘舵�佷笌RAG瀹為檯鐘舵�� (澧炲己鍨嬶細鏀寔澶栭儴浼犲叆閫傞厤鍣�)
+ */
+ private void syncDocumentStatusWithRAG(KnowledgeFilesDTO dto, KnowledgeBaseAdapter adapter) {
+ if (dto == null || StringUtils.isBlank(dto.getDocumentId()) || adapter == null) {
+ return;
+ }
+
+ String documentId = dto.getDocumentId();
+ String datasetId = dto.getDatasetId();
+
+ try {
+ // 浣跨敤寮虹被鍨� ListReq 閰嶅悎 ID 杩囨护鏉ヨ幏鍙栫姸鎬�
+ DocumentDTO.ListReq listReq = DocumentDTO.ListReq.builder()
+ .id(documentId)
+ .page(1)
+ .pageSize(1)
+ .build();
+
+ PageData<KnowledgeFilesDTO> remoteList = adapter.getDocumentList(datasetId, listReq);
+
+ if (remoteList != null && remoteList.getList() != null && !remoteList.getList().isEmpty()) {
+ KnowledgeFilesDTO remoteDto = remoteList.getList().get(0);
+ String remoteStatus = remoteDto.getStatus();
+
+ // 鏍稿績鐘舵�佸榻愬垽鍒�昏緫
+ boolean statusChanged = remoteStatus != null && !remoteStatus.equals(dto.getStatus());
+ boolean runChanged = remoteDto.getRun() != null && !remoteDto.getRun().equals(dto.getRun());
+ boolean isProcessing = "RUNNING".equals(remoteDto.getRun()) || "UNSTART".equals(remoteDto.getRun());
+
+ // 鍙鐘舵�佹湁鍙橈紝鎴栬�呰繍琛岀姸鎬佹湁鍙橈紝鎴栬�呮枃浠朵粛鍦ㄨВ鏋愪腑锛堝疄鏃跺埛杩涘害锛夛紝灏辨墽琛屽悓姝�
+ if (statusChanged || runChanged || isProcessing) {
+ log.info("褰卞瓙鍚屾锛氱姸鎬佸彉鍖�={}锛岃В鏋愪腑={}锛屾枃妗�={}锛屾渶鏂扮姸鎬�={}锛岃繘搴�={}",
+ statusChanged, isProcessing, documentId, remoteStatus, remoteDto.getProgress());
+
+ // 1. 鍚屾鍐呭瓨 DTO
+ dto.setStatus(remoteStatus);
+ dto.setRun(remoteDto.getRun());
+ dto.setProgress(remoteDto.getProgress());
+ dto.setChunkCount(remoteDto.getChunkCount());
+ dto.setTokenCount(remoteDto.getTokenCount());
+ dto.setError(remoteDto.getError());
+ dto.setProcessDuration(remoteDto.getProcessDuration());
+ dto.setThumbnail(remoteDto.getThumbnail());
+
+ // 2. 鍚屾鏈湴褰卞瓙琛�
+ UpdateWrapper<DocumentEntity> updateWrapper = new UpdateWrapper<DocumentEntity>()
+ .set("status", remoteStatus)
+ .set("run", remoteDto.getRun())
+ .set("progress", remoteDto.getProgress())
+ .set("chunk_count", remoteDto.getChunkCount())
+ .set("token_count", remoteDto.getTokenCount())
+ .set("error", remoteDto.getError())
+ .set("process_duration", remoteDto.getProcessDuration())
+ .set("thumbnail", remoteDto.getThumbnail())
+ .eq("document_id", documentId)
+ .eq("dataset_id", datasetId);
+
+ // 搴忓垪鍖栧厓鏁版嵁鍚屾
+ if (remoteDto.getMetaFields() != null) {
+ try {
+ updateWrapper.set("meta_fields",
+ objectMapper.writeValueAsString(remoteDto.getMetaFields()));
+ } catch (Exception e) {
+ log.warn("鍚屾鍏冩暟鎹簭鍒楀寲澶辫触: {}", e.getMessage());
+ }
+ }
+
+ // 浼樺厛鍚屾 RAG 渚х殑鏇存柊鏃堕棿锛岄伩鍏嶆湰鍦板悓姝ヨ涓鸿鐩栦笟鍔′慨鏀规椂闂�
+ Date lastUpdate = remoteDto.getUpdatedAt() != null ? remoteDto.getUpdatedAt() : new Date();
+ updateWrapper.set("updated_at", lastUpdate);
+ updateWrapper.set("last_sync_at", new Date()); // 璁板綍褰卞瓙搴撳悓姝ユ椂闂�
+
+ documentDao.update(null, updateWrapper);
+ }
+ } else {
+ // Issue 6: 杩滅▼鍒楄〃涓虹┖锛屽彲鑳芥槸鏂囨。宸插垹闄わ紝涔熷彲鑳芥槸閫傞厤鍣ㄨ皟鐢ㄥ嚭浜嗛棶棰�
+ // [Bug Fix] P2: 浠呭綋杩滅▼纭疄杩斿洖浜嗗悎娉曠┖鍒楄〃鏃舵墠鏍囪 CANCEL
+ // 鍚屾椂鏇存柊 last_sync_at锛岄厤鍚� P1 鍐峰嵈鏈哄埗闃叉楂橀璇垽
+ log.warn("杩滅▼鍚屾鎰熺煡锛歊AGFlow 杩斿洖绌烘枃妗e垪琛�, docId={}, 褰撳墠鏈湴鐘舵��={}",
+ documentId, dto.getRun());
+ dto.setRun("CANCEL");
+ dto.setError("鏂囨。鍦ㄨ繙绋嬫湇鍔′腑宸茶鍒犻櫎");
+
+ documentDao.update(null, new UpdateWrapper<DocumentEntity>()
+ .set("run", "CANCEL")
+ .set("error", "鏂囨。鍦ㄨ繙绋嬫湇鍔′腑宸茶鍒犻櫎")
+ .set("updated_at", new Date())
+ .set("last_sync_at", new Date())
+ .eq("document_id", documentId));
+ }
+ } catch (Exception e) {
+ // [Bug Fix] P2: 閫傞厤鍣ㄨ皟鐢ㄥ紓甯告椂涓嶆爣璁� CANCEL锛岄伩鍏嶅洜缃戠粶/鍙嶅簭鍒楀寲闂瀵艰嚧璇垽
+ // 浠呰褰曟棩蹇楋紝绛変笅娆″悓姝ュ懆鏈熼噸璇�
+ log.warn("鍚屾鏂囨。鐘舵�佹椂閫傞厤鍣ㄨ皟鐢ㄥけ璐�(涓嶆爣璁癈ANCEL), documentId: {}, error: {}",
+ documentId, e.getMessage());
+ }
+ }
+
+ @Override
+ public DocumentDTO.InfoVO getByDocumentId(String documentId, String datasetId) {
+ if (StringUtils.isBlank(documentId) || StringUtils.isBlank(datasetId)) {
+ throw new RenException(ErrorCode.RAG_DATASET_ID_AND_MODEL_ID_NOT_NULL);
+ }
+
+ log.info("=== 寮�濮嬫牴鎹甦ocumentId鑾峰彇鏂囨。 ===");
+ log.info("documentId: {}, datasetId: {}", documentId, datasetId);
+
+ try {
+ // 鑾峰彇RAG閰嶇疆
+ Map<String, Object> ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetId);
+
+ // 鎻愬彇閫傞厤鍣ㄧ被鍨�
+ String adapterType = extractAdapterType(ragConfig);
+
+ // 浣跨敤閫傞厤鍣ㄥ伐鍘傝幏鍙栭�傞厤鍣ㄥ疄渚�
+ KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(adapterType, ragConfig);
+
+ // 浣跨敤閫傞厤鍣ㄨ幏鍙栨枃妗h鎯�
+ DocumentDTO.InfoVO info = adapter.getDocumentById(datasetId, documentId);
+
+ if (info != null) {
+ log.info("鑾峰彇鏂囨。璇︽儏鎴愬姛锛宒ocumentId: {}", documentId);
+ return info;
+ } else {
+ throw new RenException(ErrorCode.Knowledge_Base_RECORD_NOT_EXISTS);
+ }
+
+ } catch (Exception e) {
+ log.error("鏍规嵁documentId鑾峰彇鏂囨。澶辫触: {}", e.getMessage(), e);
+ String errorMessage = e.getMessage() != null ? e.getMessage() : "null";
+ if (e instanceof RenException) {
+ throw (RenException) e;
+ }
+ throw new RenException(ErrorCode.RAG_API_ERROR, errorMessage);
+ } finally {
+ log.info("=== 鏍规嵁documentId鑾峰彇鏂囨。鎿嶄綔缁撴潫 ===");
+ }
+ }
+
+ @Override
+ public KnowledgeFilesDTO uploadDocument(String datasetId, MultipartFile file, String name,
+ Map<String, Object> metaFields, String chunkMethod,
+ Map<String, Object> parserConfig) {
+ if (StringUtils.isBlank(datasetId) || file == null || file.isEmpty()) {
+ throw new RenException(ErrorCode.PARAMS_GET_ERROR);
+ }
+
+ log.info("=== 寮�濮嬫枃妗d笂浼犳搷浣� (寮轰竴鑷存�т紭鍖�) ===");
+
+ // 1. 鍑嗗宸ヤ綔 (闈炰簨鍔℃��)
+ String fileName = StringUtils.isNotBlank(name) ? name : file.getOriginalFilename();
+ if (StringUtils.isBlank(fileName)) {
+ throw new RenException(ErrorCode.RAG_FILE_NAME_NOT_NULL);
+ }
+
+ log.info("1. 鍙戣捣杩滅▼涓婁紶: datasetId={}, fileName={}", datasetId, fileName);
+
+ // 鑾峰彇閫傞厤鍣� (闈炰簨鍔℃��)
+ Map<String, Object> ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetId);
+ KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(extractAdapterType(ragConfig), ragConfig);
+
+ // 鏋勯�犲己绫诲瀷璇锋眰 DTO
+ DocumentDTO.UploadReq uploadReq = DocumentDTO.UploadReq.builder()
+ .datasetId(datasetId)
+ .file(file)
+ .name(fileName)
+ .metaFields(metaFields)
+ .build();
+
+ // 杞崲鍒嗗潡鏂规硶 (String -> Enum)
+ if (StringUtils.isNotBlank(chunkMethod)) {
+ try {
+ uploadReq.setChunkMethod(DocumentDTO.InfoVO.ChunkMethod.valueOf(chunkMethod.toUpperCase()));
+ } catch (Exception e) {
+ log.warn("鏃犳晥鐨勫垎鍧楁柟娉�: {}, 灏嗕娇鐢ㄥ悗鍙伴粯璁ら厤缃�", chunkMethod);
+ }
+ }
+
+ // 杞崲瑙f瀽閰嶇疆 (Map -> DTO)
+ if (parserConfig != null && !parserConfig.isEmpty()) {
+ uploadReq.setParserConfig(objectMapper.convertValue(parserConfig, DocumentDTO.InfoVO.ParserConfig.class));
+ }
+
+ // 鎵ц杩滅▼涓婁紶 (鑰楁椂 IO锛屽湪浜嬪姟涔嬪)
+ KnowledgeFilesDTO result = adapter.uploadDocument(uploadReq);
+
+ if (result == null || StringUtils.isBlank(result.getDocumentId())) {
+ throw new RenException(ErrorCode.RAG_API_ERROR, "杩滅▼涓婁紶鎴愬姛浣嗘湭杩斿洖鏈夋晥 DocumentID");
+ }
+
+ // 2. 鏈湴鎸佷箙鍖� (閫氳繃 self 璋冪敤浠ユ縺娲� @Transactional 浠g悊)
+ log.info("2. 鍚屾淇濆瓨鏈湴褰卞瓙璁板綍: documentId={}", result.getDocumentId());
+ self.saveDocumentShadow(datasetId, result, fileName, chunkMethod, parserConfig);
+
+ log.info("=== 鏂囨。涓婁紶涓庡奖瀛愯褰曚繚瀛樻垚鍔� ===");
+ return result;
+ }
+
+ /**
+ * 鍘熷瓙鍖栦繚瀛樺奖瀛愯褰曪紙Upsert 璇箟锛�
+ * 鑻� document_id 宸插瓨鍦ㄥ垯鏇存柊锛屼笉瀛樺湪鍒欐彃鍏ワ紝閬垮厤 UNIQUE 绾︽潫鍐茬獊
+ *
+ * @return true=鏂版彃鍏�, false=鏇存柊宸叉湁璁板綍
+ */
+ @Transactional(rollbackFor = Exception.class)
+ public boolean saveDocumentShadow(String datasetId, KnowledgeFilesDTO result, String originalName, String chunkMethod,
+ Map<String, Object> parserConfig) {
+ DocumentEntity entity = new DocumentEntity();
+ entity.setDatasetId(datasetId);
+ entity.setDocumentId(result.getDocumentId());
+ entity.setName(StringUtils.isNotBlank(result.getName()) ? result.getName() : originalName);
+ entity.setSize(result.getFileSize());
+ entity.setType(getFileType(entity.getName()));
+ entity.setChunkMethod(chunkMethod);
+
+ if (parserConfig != null) {
+ try {
+ entity.setParserConfig(objectMapper.writeValueAsString(parserConfig));
+ } catch (Exception e) {
+ log.warn("搴忓垪鍖栬В鏋愰厤缃け璐�: {}", e.getMessage());
+ }
+ }
+
+ entity.setStatus(result.getStatus() != null ? result.getStatus() : "1");
+ entity.setRun(result.getRun());
+ entity.setProgress(result.getProgress());
+ entity.setThumbnail(result.getThumbnail());
+ entity.setProcessDuration(result.getProcessDuration());
+ entity.setSourceType(result.getSourceType());
+ entity.setError(result.getError());
+ entity.setChunkCount(result.getChunkCount());
+ entity.setTokenCount(result.getTokenCount());
+ entity.setEnabled(1);
+
+ // 鎸佷箙鍖栧厓鏁版嵁
+ if (result.getMetaFields() != null) {
+ try {
+ entity.setMetaFields(objectMapper.writeValueAsString(result.getMetaFields()));
+ } catch (Exception e) {
+ log.warn("鎸佷箙鍖栧奖瀛愬厓鏁版嵁澶辫触: {}", e.getMessage());
+ }
+ }
+
+ // 浼樺厛鍚屾 RAG 渚х殑鏃堕棿鎴筹紝鑻ユ棤鍒欎娇鐢ㄦ湰鍦版椂闂�
+ entity.setCreatedAt(result.getCreatedAt() != null ? result.getCreatedAt() : new Date());
+ entity.setUpdatedAt(result.getUpdatedAt() != null ? result.getUpdatedAt() : new Date());
+
+ // Upsert: 妫�鏌� document_id 鏄惁宸插瓨鍦紝瀛樺湪鍒欐洿鏂帮紝涓嶅瓨鍦ㄥ垯鎻掑叆
+ DocumentEntity existing = documentDao.selectOne(
+ new QueryWrapper<DocumentEntity>().eq("document_id", entity.getDocumentId()));
+
+ if (existing != null) {
+ entity.setId(existing.getId());
+ entity.setCreatedAt(existing.getCreatedAt()); // 淇濈暀鍘熷鍒涘缓鏃堕棿
+ documentDao.updateById(entity);
+ log.info("褰卞瓙璁板綍宸叉洿鏂�: documentId={}", entity.getDocumentId());
+ return false;
+ } else {
+ documentDao.insert(entity);
+ // 鏂板璁板綍鏃堕�掑鏁版嵁闆嗘枃妗f�绘暟缁熻
+ knowledgeBaseService.updateStatistics(datasetId, 1, 0L, 0L);
+ log.info("褰卞瓙璁板綍宸叉彃鍏�: documentId={}, datasetId={}", entity.getDocumentId(), datasetId);
+ return true;
+ }
+ }
+
+ @Override
+ @Transactional(propagation = Propagation.NOT_SUPPORTED)
+ public void deleteDocuments(String datasetId, DocumentDTO.BatchIdReq req) {
+ if (StringUtils.isBlank(datasetId) || req == null || req.getIds() == null || req.getIds().isEmpty()) {
+ throw new RenException(ErrorCode.RAG_DATASET_ID_AND_MODEL_ID_NOT_NULL);
+ }
+
+ List<String> documentIds = req.getIds();
+ log.info("=== 寮�濮嬫壒閲忓垹闄ゆ枃妗�: datasetId={}, count={} ===", datasetId, documentIds.size());
+
+ // 1. 鎵归噺鏉冮檺涓庣姸鎬侀瀹�
+ List<DocumentEntity> entities = documentDao.selectList(
+ new QueryWrapper<DocumentEntity>()
+ .eq("dataset_id", datasetId)
+ .in("document_id", documentIds));
+
+ if (entities.size() != documentIds.size()) {
+ log.warn("閮ㄥ垎鏂囨。涓嶅瓨鍦ㄦ垨褰掑睘鏉冨紓甯�: 棰勬湡={}, 瀹為檯={}", documentIds.size(), entities.size());
+ throw new RenException(ErrorCode.NO_PERMISSION);
+ }
+
+ long totalChunkDelta = 0;
+ long totalTokenDelta = 0;
+
+ for (DocumentEntity entity : entities) {
+ // 鎷︽埅姝e湪瑙f瀽涓殑鏂囨。鐨勫垹闄よ姹�
+ // [Bug Fix] 鍒ゆ柇瑙f瀽涓簲璇ョ敤 run 瀛楁(RUNNING), 鑰岄潪 status 瀛楁
+ // status="1" 鏄�"鍚敤/姝e父"鐨勬剰鎬�, 涓嶆槸"瑙f瀽涓�"
+ if ("RUNNING".equals(entity.getRun())) {
+ log.warn("鎷︽埅瑙f瀽涓枃浠剁殑鍒犻櫎璇锋眰: docId={}", entity.getDocumentId());
+ throw new RenException(ErrorCode.RAG_DOCUMENT_PARSING_DELETE_ERROR);
+ }
+ totalChunkDelta += entity.getChunkCount() != null ? entity.getChunkCount() : 0L;
+ totalTokenDelta += entity.getTokenCount() != null ? entity.getTokenCount() : 0L;
+ }
+
+ // 2. 鑾峰彇閫傞厤鍣� (闈炰簨鍔℃��)
+ Map<String, Object> ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetId);
+ KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(extractAdapterType(ragConfig), ragConfig);
+
+ // 3. 鎵ц杩滅▼鍒犻櫎
+ try {
+ adapter.deleteDocument(datasetId, req);
+ log.info("杩滅▼鎵归噺鍒犻櫎璇锋眰鎴愬姛");
+ } catch (Exception e) {
+ log.error("杩滅▼鍒犻櫎璇锋眰澶辫触锛屼腑姝㈡湰鍦版竻鐞嗕互閬垮厤鏁版嵁涓嶄竴鑷�: {}", e.getMessage());
+ throw new RenException(e.getMessage());
+ }
+
+ // 4. 鍘熷瓙鍖栨竻鐞嗘湰鍦板奖瀛愯褰曞苟鍚屾缁熻鏁版嵁
+ self.deleteDocumentShadows(documentIds, datasetId, totalChunkDelta, totalTokenDelta);
+
+ // 5. 娓呯悊缂撳瓨
+ try {
+ String cacheKey = RedisKeys.getKnowledgeBaseCacheKey(datasetId);
+ redisUtils.delete(cacheKey);
+ log.info("宸查┍閫愭暟鎹泦缂撳瓨: {}", cacheKey);
+ } catch (Exception e) {
+ log.warn("椹遍�� Redis 缂撳瓨澶辫触: {}", e.getMessage());
+ }
+
+ log.info("=== 鎵归噺鏂囨。娓呯悊瀹屾垚 ===");
+ }
+
+ /**
+ * 鎵归噺鍘熷瓙鍖栧垹闄ゅ奖瀛愯褰曞苟鍚屾鐖惰〃缁熻
+ */
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteDocumentShadows(List<String> documentIds, String datasetId, Long chunkDelta, Long tokenDelta) {
+ // 1. 鐗╃悊鍒犻櫎璁板綍
+ int deleted = documentDao.delete(
+ new QueryWrapper<DocumentEntity>()
+ .eq("dataset_id", datasetId)
+ .in("document_id", documentIds));
+
+ if (deleted > 0) {
+ // 2. 鍚屾鏇存柊鏁版嵁闆嗙粺璁′俊鎭�
+ knowledgeBaseService.updateStatistics(datasetId, -documentIds.size(), -chunkDelta, -tokenDelta);
+ log.info("宸插悓姝ユ墸鍑忔暟鎹泦缁熻: datasetId={}, chunks={}, tokens={}", datasetId, chunkDelta, tokenDelta);
+ }
+ }
+
+ /**
+ * 鑾峰彇鏂囦欢绫诲瀷 - 鏀寔RAG鍥涚鏂囨。鏍煎紡绫诲瀷
+ */
+ private String getFileType(String fileName) {
+ if (StringUtils.isBlank(fileName)) {
+ log.warn("鏂囦欢鍚嶄负绌猴紝杩斿洖unknown绫诲瀷");
+ return "unknown";
+ }
+
+ try {
+ int lastDotIndex = fileName.lastIndexOf('.');
+ if (lastDotIndex > 0 && lastDotIndex < fileName.length() - 1) {
+ String extension = fileName.substring(lastDotIndex + 1).toLowerCase();
+
+ // 鏂囨。鏍煎紡绫诲瀷
+ String[] documentTypes = { "pdf", "doc", "docx", "txt", "md", "mdx" };
+ String[] spreadsheetTypes = { "csv", "xls", "xlsx" };
+ String[] presentationTypes = { "ppt", "pptx" };
+
+ // 妫�鏌ユ枃妗g被鍨�
+ for (String type : documentTypes) {
+ if (type.equals(extension)) {
+ return "document";
+ }
+ }
+
+ // 妫�鏌ヨ〃鏍肩被鍨�
+ for (String type : spreadsheetTypes) {
+ if (type.equals(extension)) {
+ return "spreadsheet";
+ }
+ }
+ // 妫�鏌ュ够鐏墖绫诲瀷
+ for (String type : presentationTypes) {
+ if (type.equals(extension)) {
+ return "presentation";
+ }
+ }
+ // 杩斿洖鍘熷鎵╁睍鍚嶄綔涓烘枃浠剁被鍨�
+ return extension;
+ }
+ return "unknown";
+ } catch (Exception e) {
+ log.error("鑾峰彇鏂囦欢绫诲瀷澶辫触: ", e);
+ return "unknown";
+ }
+ }
+
+ /**
+ * 浠嶳AG閰嶇疆涓彁鍙栭�傞厤鍣ㄧ被鍨�
+ */
+ private String extractAdapterType(Map<String, Object> config) {
+ if (config == null) {
+ throw new RenException(ErrorCode.RAG_CONFIG_NOT_FOUND);
+ }
+
+ // 浠庨厤缃腑鎻愬彇type瀛楁
+ String adapterType = (String) config.get("type");
+ if (StringUtils.isBlank(adapterType)) {
+ throw new RenException(ErrorCode.RAG_ADAPTER_TYPE_NOT_FOUND);
+ }
+
+ // 楠岃瘉閫傞厤鍣ㄧ被鍨嬫槸鍚﹀凡娉ㄥ唽
+ if (!KnowledgeBaseAdapterFactory.isAdapterTypeRegistered(adapterType)) {
+ throw new RenException(ErrorCode.RAG_ADAPTER_TYPE_NOT_SUPPORTED, "閫傞厤鍣ㄧ被鍨嬫湭娉ㄥ唽: " + adapterType);
+ }
+
+ return adapterType;
+ }
+
+ @Override
+ public boolean parseDocuments(String datasetId, List<String> documentIds) {
+ if (StringUtils.isBlank(datasetId) || documentIds == null || documentIds.isEmpty()) {
+ throw new RenException(ErrorCode.RAG_DATASET_ID_AND_MODEL_ID_NOT_NULL);
+ }
+
+ log.info("=== 寮�濮嬭В鏋愭枃妗o紙鍒囧潡锛� ===");
+ log.info("datasetId: {}, documentIds: {}", datasetId, documentIds);
+
+ try {
+ // 鑾峰彇RAG閰嶇疆
+ Map<String, Object> ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetId);
+
+ // 鎻愬彇閫傞厤鍣ㄧ被鍨�
+ String adapterType = extractAdapterType(ragConfig);
+
+ // 鑾峰彇鐭ヨ瘑搴撻�傞厤鍣�
+ KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(adapterType, ragConfig);
+
+ log.debug("瑙f瀽鏂囨。鍙傛暟: documentIds: {}", documentIds);
+
+ // 璋冪敤閫傞厤鍣ㄨВ鏋愭枃妗�
+ boolean result = adapter.parseDocuments(datasetId, documentIds);
+
+ if (result) {
+ log.info("鏂囨。瑙f瀽鍛戒护鍙戦�佹垚鍔燂紝鍑嗗鍚屾鏈湴褰卞瓙搴撶姸鎬侊紝datasetId: {}, documentIds: {}", datasetId, documentIds);
+ // 鎸囦护鎴愬姛鍚庣珛鍗虫洿鏂版湰鍦板奖瀛愮姸鎬佷负 RUNNING 鍜� 瑙f瀽涓�(1)锛岀‘淇� Local-First 鍒楄〃鑳界珛鍗冲弽棣�
+ documentDao.update(null, new UpdateWrapper<DocumentEntity>()
+ .set("run", "RUNNING")
+ .set("status", "1")
+ .set("updated_at", new Date())
+ .eq("dataset_id", datasetId)
+ .in("document_id", documentIds));
+
+ log.info("鏂囨。鏈湴鐘舵�佸凡鏇存柊涓� RUNNING");
+ } else {
+ log.error("鏂囨。瑙f瀽澶辫触锛宒atasetId: {}, documentIds: {}", datasetId, documentIds);
+ throw new RenException(ErrorCode.RAG_API_ERROR, "鏂囨。瑙f瀽澶辫触");
+ }
+
+ return result;
+
+ } catch (Exception e) {
+ log.error("瑙f瀽鏂囨。澶辫触: {}", e.getMessage(), e);
+ String errorMessage = e.getMessage() != null ? e.getMessage() : "null";
+ if (e instanceof RenException) {
+ throw (RenException) e;
+ }
+ throw new RenException(ErrorCode.RAG_API_ERROR, errorMessage);
+ } finally {
+ log.info("=== 瑙f瀽鏂囨。鎿嶄綔缁撴潫 ===");
+ }
+ }
+
+ @Override
+ public ChunkDTO.ListVO listChunks(String datasetId, String documentId, ChunkDTO.ListReq req) {
+ if (StringUtils.isBlank(datasetId) || StringUtils.isBlank(documentId)) {
+ throw new RenException(ErrorCode.RAG_DATASET_ID_AND_MODEL_ID_NOT_NULL);
+ }
+
+ log.info("=== 寮�濮嬪垪鍑哄垏鐗�: datasetId={}, documentId={}, req={} ===", datasetId, documentId, req);
+
+ try {
+ Map<String, Object> ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetId);
+ KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(extractAdapterType(ragConfig),
+ ragConfig);
+
+ ChunkDTO.ListVO result = adapter.listChunks(datasetId, documentId, req);
+ log.info("鍒囩墖鍒楄〃鑾峰彇鎴愬姛: datasetId={}, total={}", datasetId, result.getTotal());
+ return result;
+ } catch (Exception e) {
+ log.error("鍒楀嚭鍒囩墖澶辫触: {}", e.getMessage(), e);
+ String errorMessage = e.getMessage() != null ? e.getMessage() : "null";
+ if (e instanceof RenException) {
+ throw (RenException) e;
+ }
+ throw new RenException(ErrorCode.RAG_API_ERROR, errorMessage);
+ } finally {
+ log.info("=== 鍒楀嚭鍒囩墖鎿嶄綔缁撴潫 ===");
+ }
+ }
+
+ @Override
+ public RetrievalDTO.ResultVO retrievalTest(RetrievalDTO.TestReq req) {
+ if (CollectionUtils.isEmpty(req.getDatasetIds())) {
+ throw new RenException("鏈寚瀹氬彫鍥炴祴璇曠殑鐭ヨ瘑搴�");
+ }
+
+ log.info("=== 寮�濮嬪彫鍥炴祴璇�: req={} ===", req);
+
+ try {
+ Map<String, Object> ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(req.getDatasetIds().get(0));
+ KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(extractAdapterType(ragConfig),
+ ragConfig);
+
+ RetrievalDTO.ResultVO result = adapter.retrievalTest(req);
+ log.info("鍙洖娴嬭瘯鎴愬姛: total={}", result != null ? result.getTotal() : 0);
+ return result;
+ } catch (Exception e) {
+ log.error("鍙洖娴嬭瘯澶辫触: {}", e.getMessage(), e);
+ String errorMessage = e.getMessage() != null ? e.getMessage() : "null";
+ if (e instanceof RenException) {
+ throw (RenException) e;
+ }
+ throw new RenException(ErrorCode.RAG_API_ERROR, errorMessage);
+ } finally {
+ log.info("=== 鍙洖娴嬭瘯鎿嶄綔缁撴潫 ===");
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteDocumentsByDatasetId(String datasetId) {
+ log.info("绾ц仈娓呯悊鏁版嵁闆嗘枃妗�: datasetId={}", datasetId);
+ List<DocumentEntity> list = documentDao
+ .selectList(new QueryWrapper<DocumentEntity>().eq("dataset_id", datasetId));
+ if (list == null || list.isEmpty())
+ return;
+
+ List<String> docIds = list.stream().map(DocumentEntity::getDocumentId).toList();
+
+ // 灏佸寘璋冪敤鐜版湁鍒犻櫎閫昏緫 (鍚� RAG 鐗╃悊鍒犻櫎)
+ DocumentDTO.BatchIdReq req = DocumentDTO.BatchIdReq.builder().ids(docIds).build();
+ this.deleteDocuments(datasetId, req);
+ }
+
+ @Override
+ public int syncDocumentsFromRAG(String datasetId) {
+ log.info("=== 寮�濮嬩粠RAGFlow鍏ㄩ噺鍚屾鏂囨。鍒版湰鍦板奖瀛愯〃: datasetId={} ===", datasetId);
+
+ // 1. 鑾峰彇閫傞厤鍣�
+ Map<String, Object> ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetId);
+ KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(extractAdapterType(ragConfig), ragConfig);
+
+ // 2. 鍒嗛〉鎷夊彇杩滅鎵�鏈夋枃妗�
+ List<KnowledgeFilesDTO> allRemoteDocs = new ArrayList<>();
+ int pageNum = 1;
+ int pageSize = 100;
+ long totalRemote = Long.MAX_VALUE;
+
+ while ((long) (pageNum - 1) * pageSize < totalRemote) {
+ DocumentDTO.ListReq req = DocumentDTO.ListReq.builder()
+ .page(pageNum)
+ .pageSize(pageSize)
+ .build();
+ PageData<KnowledgeFilesDTO> remotePage = adapter.getDocumentList(datasetId, req);
+ if (remotePage == null || remotePage.getList() == null || remotePage.getList().isEmpty()) {
+ break;
+ }
+ allRemoteDocs.addAll(remotePage.getList());
+ totalRemote = remotePage.getTotal();
+ pageNum++;
+ }
+
+ // 3. 鑾峰彇鏈湴宸叉湁鏂囨。
+ List<DocumentEntity> localDocs = documentDao.selectList(
+ new QueryWrapper<DocumentEntity>().eq("dataset_id", datasetId));
+ Set<String> localDocIds = localDocs.stream()
+ .map(DocumentEntity::getDocumentId)
+ .collect(Collectors.toSet());
+
+ // 4. 杩滅鏂囨。ID闆嗗悎
+ Set<String> remoteDocIds = allRemoteDocs.stream()
+ .map(KnowledgeFilesDTO::getDocumentId)
+ .filter(id -> id != null)
+ .collect(Collectors.toSet());
+
+ // 5. 琛ュ厖: 鎻掑叆杩滅瀛樺湪浣嗘湰鍦扮己澶辩殑鏂囨。
+ List<KnowledgeFilesDTO> newDocs = allRemoteDocs.stream()
+ .filter(doc -> doc.getDocumentId() != null && !localDocIds.contains(doc.getDocumentId()))
+ .collect(Collectors.toList());
+
+ int syncCount = 0;
+ if (!newDocs.isEmpty()) {
+ for (KnowledgeFilesDTO doc : newDocs) {
+ try {
+ self.saveDocumentShadow(datasetId, doc, doc.getName(), doc.getChunkMethod(), doc.getParserConfig());
+ // 鍚屾杩滅宸叉湁鐨� token/chunk 缁熻
+ Long tokenCount = doc.getTokenCount() != null ? doc.getTokenCount() : 0L;
+ long chunkCount = doc.getChunkCount() != null ? doc.getChunkCount().longValue() : 0L;
+ if (tokenCount > 0 || chunkCount > 0) {
+ knowledgeBaseService.updateStatistics(datasetId, 0, chunkCount, tokenCount);
+ }
+ syncCount++;
+ } catch (Exception e) {
+ log.warn("鍚屾鍗曚釜鏂囨。褰卞瓙璁板綍澶辫触: docId={}, error={}", doc.getDocumentId(), e.getMessage());
+ }
+ }
+ log.info("浠嶳AGFlow鏂板鍚屾 {} 涓枃妗e奖瀛愯褰�, datasetId={}", syncCount, datasetId);
+ }
+
+ // 6. 娓呯悊: 鍒犻櫎杩滅宸蹭笉瀛樺湪浣嗘湰鍦颁粛淇濈暀鐨勫奖瀛愯褰�
+ List<DocumentEntity> deletedDocs = localDocs.stream()
+ .filter(entity -> !remoteDocIds.contains(entity.getDocumentId()))
+ .collect(Collectors.toList());
+
+ if (!deletedDocs.isEmpty()) {
+ List<String> deletedDocIds = new ArrayList<>();
+ long totalChunkDelta = 0;
+ long totalTokenDelta = 0;
+
+ for (DocumentEntity entity : deletedDocs) {
+ deletedDocIds.add(entity.getDocumentId());
+ totalChunkDelta += entity.getChunkCount() != null ? entity.getChunkCount() : 0L;
+ totalTokenDelta += entity.getTokenCount() != null ? entity.getTokenCount() : 0L;
+ }
+ try {
+ self.deleteDocumentShadows(deletedDocIds, datasetId, totalChunkDelta, totalTokenDelta);
+ log.info("娓呯悊杩滅宸插垹闄ょ殑褰卞瓙璁板綍: {} 涓�, datasetId={}", deletedDocs.size(), datasetId);
+ } catch (Exception e) {
+ log.warn("娓呯悊杩滅宸插垹闄ょ殑褰卞瓙璁板綍澶辫触: datasetId={}, error={}", datasetId, e.getMessage());
+ }
+ }
+
+ // 7. 鍏ㄩ噺鏇存柊: 杩滅鍜屾湰鍦伴兘瀛樺湪鐨勬枃妗o紝浠ヨ繙绔负鍑嗗悓姝ユ墍鏈夊瓧娈�
+ // 澶勭悊 RAGFlow 澶嶇敤 documentId 閲嶄紶銆佽繙绔紪杈戝悗鍏冩暟鎹彉鍖栫瓑鍦烘櫙
+ Map<String, KnowledgeFilesDTO> remoteDocMap = allRemoteDocs.stream()
+ .filter(doc -> doc.getDocumentId() != null)
+ .collect(Collectors.toMap(KnowledgeFilesDTO::getDocumentId, doc -> doc, (a, b) -> b));
+
+ Map<String, DocumentEntity> localDocMap = localDocs.stream()
+ .collect(Collectors.toMap(DocumentEntity::getDocumentId, e -> e, (a, b) -> b));
+
+ int updateCount = 0;
+ for (Map.Entry<String, KnowledgeFilesDTO> entry : remoteDocMap.entrySet()) {
+ String docId = entry.getKey();
+ DocumentEntity local = localDocMap.get(docId);
+ if (local == null) {
+ continue; // 涓嶅湪鏈湴锛岀敱姝ラ5澶勭悊
+ }
+ KnowledgeFilesDTO remote = entry.getValue();
+
+ // 鍏ㄩ噺瀛楁鏇存柊锛堜互杩滅涓哄噯锛夛紝纭繚鏈湴涓� RAGFlow 瀹屽叏涓�鑷�
+ UpdateWrapper<DocumentEntity> updateWrapper = new UpdateWrapper<DocumentEntity>()
+ .set("run", remote.getRun())
+ .set("status", remote.getStatus() != null ? remote.getStatus() : local.getStatus())
+ .set("progress", remote.getProgress())
+ .set("chunk_count", remote.getChunkCount())
+ .set("token_count", remote.getTokenCount())
+ .set("size", remote.getFileSize())
+ .set("error", remote.getError())
+ .set("process_duration", remote.getProcessDuration())
+ .set("updated_at", new Date())
+ .set("last_sync_at", new Date())
+ .eq("document_id", docId)
+ .eq("dataset_id", datasetId);
+
+ if (remote.getName() != null) {
+ updateWrapper.set("name", remote.getName());
+ }
+ if (remote.getThumbnail() != null) {
+ updateWrapper.set("thumbnail", remote.getThumbnail());
+ }
+ if (remote.getMetaFields() != null) {
+ try {
+ updateWrapper.set("meta_fields", objectMapper.writeValueAsString(remote.getMetaFields()));
+ } catch (Exception e) {
+ log.warn("鍚屾鏇存柊鍏冩暟鎹簭鍒楀寲澶辫触: docId={}, error={}", docId, e.getMessage());
+ }
+ }
+
+ documentDao.update(null, updateWrapper);
+
+ // 鍚屾缁熻宸紓锛坈hunk/token 璁℃暟鍙樺寲鏃朵慨姝g埗琛級
+ Long remoteTokenCount = remote.getTokenCount() != null ? remote.getTokenCount() : 0L;
+ Long localTokenCount = local.getTokenCount() != null ? local.getTokenCount() : 0L;
+ long remoteChunkCount = remote.getChunkCount() != null ? remote.getChunkCount().longValue() : 0L;
+ long localChunkCount = local.getChunkCount() != null ? local.getChunkCount().longValue() : 0L;
+ long tokenDelta = remoteTokenCount - localTokenCount;
+ long chunkDelta = remoteChunkCount - localChunkCount;
+ if (tokenDelta != 0 || chunkDelta != 0) {
+ knowledgeBaseService.updateStatistics(datasetId, 0, chunkDelta, tokenDelta);
+ log.info("褰卞瓙鏇存柊: 淇鐭ヨ瘑搴撶粺璁�, docId={}, chunkDelta={}, tokenDelta={}", docId, chunkDelta, tokenDelta);
+ }
+
+ updateCount++;
+ }
+
+ if (syncCount == 0 && deletedDocs.isEmpty() && updateCount == 0) {
+ log.info("鏈湴褰卞瓙琛ㄥ凡涓嶳AGFlow瀹屽叏鍚屾, datasetId={}", datasetId);
+ } else {
+ log.info("鍚屾瀹屾垚: 鏂板={}, 娓呯悊={}, 鏇存柊={}, datasetId={}", syncCount, deletedDocs.size(), updateCount, datasetId);
+ }
+
+ return syncCount;
+ }
+
+ @Override
+ public void syncRunningDocuments() {
+ // 1. 鏌ヨ鎵�鏈� RUNNING 鐘舵�佺殑鏂囨。
+ List<DocumentEntity> runningDocs = documentDao.selectList(
+ new QueryWrapper<DocumentEntity>()
+ .eq("run", "RUNNING")
+ .eq("status", "1") // 浠呭悓姝ュ惎鐢ㄧ殑鏂囨。
+ );
+
+ if (runningDocs == null || runningDocs.isEmpty()) {
+ return;
+ }
+
+ log.info("瀹氭椂浠诲姟: 鍙戠幇 {} 涓枃妗f鍦ㄨВ鏋愪腑锛屽紑濮嬪悓姝�...", runningDocs.size());
+
+ // 2. 鎸� DatasetID 鍒嗙粍锛屽鐢� Adapter
+ Map<String, List<DocumentEntity>> groupedDocs = runningDocs.stream()
+ .collect(Collectors.groupingBy(DocumentEntity::getDatasetId));
+
+ groupedDocs.forEach((datasetId, docs) -> {
+ KnowledgeBaseAdapter adapter = null;
+ try {
+ // 鍒濆鍖� Adapter (姣忎釜鏁版嵁闆嗗彧鍒濆鍖栦竴娆�)
+ Map<String, Object> ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetId);
+ adapter = KnowledgeBaseAdapterFactory.getAdapter(extractAdapterType(ragConfig), ragConfig);
+ } catch (Exception e) {
+ log.warn("鏃犳硶涓烘暟鎹泦 {} 鍒濆鍖栭�傞厤鍣紝璺宠繃鍚屾: {}", datasetId, e.getMessage());
+ return;
+ }
+
+ for (DocumentEntity doc : docs) {
+ try {
+ // 鏋勯�犱复鏃� DTO 浼犵粰鍚屾鏂规硶
+ KnowledgeFilesDTO dto = convertEntityToDTO(doc);
+ // 璁板綍鍚屾鍓嶇殑 Token 鏁�
+ Long oldTokenCount = dto.getTokenCount() != null ? dto.getTokenCount() : 0L;
+
+ syncDocumentStatusWithRAG(dto, adapter);
+
+ // 3. [鍏抽敭淇] 璁$畻澧為噺骞舵洿鏂扮煡璇嗗簱缁熻
+ Long newTokenCount = dto.getTokenCount() != null ? dto.getTokenCount() : 0L;
+ Long tokenDelta = newTokenCount - oldTokenCount;
+
+ // 浠呭綋鐘舵�佸彉涓� SUCCESS 涓� Token 鏁版湁鍙樺寲鏃舵洿鏂扮粺璁�
+ if (tokenDelta != 0) {
+ knowledgeBaseService.updateStatistics(datasetId, 0, 0L, tokenDelta);
+ log.info("瀹氭椂浠诲姟: 鍚屾淇鐭ヨ瘑搴撶粺璁�, docId={}, tokenDelta={}", dto.getDocumentId(), tokenDelta);
+ }
+ } catch (Exception e) {
+ log.error("鍚屾鏂囨。 {} 澶辫触: {}", doc.getDocumentId(), e.getMessage());
+ }
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/impl/KnowledgeManagerServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/impl/KnowledgeManagerServiceImpl.java
new file mode 100644
index 0000000..541d460
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/impl/KnowledgeManagerServiceImpl.java
@@ -0,0 +1,47 @@
+package xiaozhi.modules.knowledge.service.impl;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import xiaozhi.modules.knowledge.service.KnowledgeBaseService;
+import xiaozhi.modules.knowledge.service.KnowledgeFilesService;
+import xiaozhi.modules.knowledge.service.KnowledgeManagerService;
+
+import java.util.List;
+
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class KnowledgeManagerServiceImpl implements KnowledgeManagerService {
+
+ private final KnowledgeBaseService knowledgeBaseService;
+ private final KnowledgeFilesService knowledgeFilesService;
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteDatasetWithFiles(String datasetId) {
+ log.info("=== 绾ц仈鍒犻櫎寮�濮�: datasetId={} ===", datasetId);
+
+ // 1. 鍏堣皟鐢ㄦ枃浠舵湇鍔★紝娓呯悊璇ユ暟鎹泦涓嬬殑鎵�鏈夋枃妗h褰� (鍚� RAGFlow 绔�)
+ log.info("Step 1: 娓呯悊鍏宠仈鏂囨。...");
+ knowledgeFilesService.deleteDocumentsByDatasetId(datasetId);
+
+ // 2. 鍐嶈皟鐢ㄧ煡璇嗗簱鏈嶅姟锛屽交搴曟敞閿�鏁版嵁闆� (鍚� RAGFlow 绔�)
+ log.info("Step 2: 鍒犻櫎鏁版嵁闆嗕富浣�...");
+ knowledgeBaseService.deleteByDatasetId(datasetId);
+
+ log.info("=== 绾ц仈鍒犻櫎鎴愬姛: datasetId={} ===", datasetId);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void batchDeleteDatasetsWithFiles(List<String> datasetIds) {
+ if (datasetIds == null || datasetIds.isEmpty())
+ return;
+ log.info("=== 鎵归噺绾ц仈鍒犻櫎寮�濮�: count={} ===", datasetIds.size());
+ for (String id : datasetIds) {
+ deleteDatasetWithFiles(id);
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/task/DocumentStatusSyncTask.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/task/DocumentStatusSyncTask.java
new file mode 100644
index 0000000..fb975a1
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/knowledge/task/DocumentStatusSyncTask.java
@@ -0,0 +1,39 @@
+package xiaozhi.modules.knowledge.task;
+
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.modules.knowledge.service.KnowledgeFilesService;
+
+/**
+ * 鐭ヨ瘑搴撴枃妗g姸鎬佸悓姝ュ畾鏃朵换鍔�
+ *
+ * 浣滅敤锛�
+ * 1. 鑷姩鎵弿澶勪簬 "RUNNING" (瑙f瀽涓�) 鐘舵�佺殑鏂囨。
+ * 2. 璋冪敤 RAGFlow 鎺ュ彛鑾峰彇鏈�鏂扮姸鎬�
+ * 3. 鐘舵�佺炕杞� (RUNNING -> SUCCESS/FAIL) 鏃讹紝鍚屾鏇存柊鏁版嵁搴�
+ * 4. [鍏抽敭] 瑙f瀽鎴愬姛鏃讹紝琛ュ伩鏇存柊鐭ヨ瘑搴撶殑缁熻淇℃伅 (TokenCount)
+ */
+@Component
+@AllArgsConstructor
+@Slf4j
+public class DocumentStatusSyncTask {
+
+ private final KnowledgeFilesService knowledgeFilesService;
+
+ /**
+ * 姣� 30 绉掓墽琛屼竴娆″悓姝�
+ * 閲囩敤 fixedDelay锛岀‘淇濅笂涓�娆℃墽琛屽畬 30 绉掑悗鎵嶅紑濮嬩笅涓�娆★紝闃叉绉帇
+ */
+ @Scheduled(fixedDelay = 30000)
+ public void syncRunningDocuments() {
+ try {
+ // log.debug("寮�濮嬫墽琛屾枃妗g姸鎬佸悓姝ヤ换鍔�...");
+ knowledgeFilesService.syncRunningDocuments();
+ } catch (Exception e) {
+ log.error("鏂囨。鐘舵�佸悓姝ヤ换鍔″紓甯�", e);
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/llm/service/LLMService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/llm/service/LLMService.java
new file mode 100644
index 0000000..4436f1c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/llm/service/LLMService.java
@@ -0,0 +1,79 @@
+package xiaozhi.modules.llm.service;
+
+/**
+ * LLM鏈嶅姟鎺ュ彛
+ * 鏀寔澶氱澶фā鍨嬭皟鐢�
+ */
+public interface LLMService {
+
+ /**
+ * 鐢熸垚鑱婂ぉ璁板綍鎬荤粨
+ *
+ * @param conversation 瀵硅瘽鍐呭
+ * @param promptTemplate 鎻愮ず璇嶆ā鏉�
+ * @return 鎬荤粨缁撴灉
+ */
+ String generateSummary(String conversation, String promptTemplate);
+
+ /**
+ * 鐢熸垚鑱婂ぉ璁板綍鎬荤粨锛堜娇鐢ㄩ粯璁ゆ彁绀鸿瘝锛�
+ *
+ * @param conversation 瀵硅瘽鍐呭
+ * @return 鎬荤粨缁撴灉
+ */
+ String generateSummary(String conversation);
+
+ /**
+ * 鐢熸垚鑱婂ぉ璁板綍鎬荤粨锛堟寚瀹氭ā鍨婭D锛�
+ *
+ * @param conversation 瀵硅瘽鍐呭
+ * @param modelId 妯″瀷ID
+ * @return 鎬荤粨缁撴灉
+ */
+ String generateSummaryWithModel(String conversation, String modelId);
+
+ /**
+ * 鐢熸垚鑱婂ぉ璁板綍鎬荤粨锛堟寚瀹氭ā鍨婭D鍜屾彁绀鸿瘝妯℃澘锛�
+ *
+ * @param conversation 瀵硅瘽鍐呭
+ * @param promptTemplate 鎻愮ず璇嶆ā鏉�
+ * @param modelId 妯″瀷ID
+ * @return 鎬荤粨缁撴灉
+ */
+ String generateSummary(String conversation, String promptTemplate, String modelId);
+
+ /**
+ * 鐢熸垚鑱婂ぉ璁板綍鎬荤粨锛堝寘鍚巻鍙茶蹇嗗悎骞讹級
+ *
+ * @param conversation 瀵硅瘽鍐呭
+ * @param historyMemory 鍘嗗彶璁板繂
+ * @param promptTemplate 鎻愮ず璇嶆ā鏉�
+ * @param modelId 妯″瀷ID
+ * @return 鎬荤粨缁撴灉
+ */
+ String generateSummaryWithHistory(String conversation, String historyMemory, String promptTemplate, String modelId);
+
+ /**
+ * 妫�鏌ユ湇鍔℃槸鍚﹀彲鐢�
+ *
+ * @return 鏄惁鍙敤
+ */
+ boolean isAvailable();
+
+ /**
+ * 妫�鏌ユ寚瀹氭ā鍨嬬殑鏈嶅姟鏄惁鍙敤
+ *
+ * @param modelId 妯″瀷ID
+ * @return 鏄惁鍙敤
+ */
+ boolean isAvailable(String modelId);
+
+ /**
+ * 鐢熸垚浼氳瘽鏍囬
+ *
+ * @param conversation 瀵硅瘽鍐呭
+ * @param modelId 妯″瀷ID
+ * @return 鏍囬锛堢害15瀛楋級
+ */
+ String generateTitle(String conversation, String modelId);
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/llm/service/impl/OpenAIStyleLLMServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/llm/service/impl/OpenAIStyleLLMServiceImpl.java
new file mode 100644
index 0000000..b89ae98
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/llm/service/impl/OpenAIStyleLLMServiceImpl.java
@@ -0,0 +1,420 @@
+package xiaozhi.modules.llm.service.impl;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.modules.llm.service.LLMService;
+import xiaozhi.modules.model.entity.ModelConfigEntity;
+import xiaozhi.modules.model.service.ModelConfigService;
+
+/**
+ * OpenAI椋庢牸API鐨凩LM鏈嶅姟瀹炵幇
+ * 鏀寔闃块噷浜戙�丏eepSeek銆丆hatGLM绛夊吋瀹筄penAI API鐨勬ā鍨�
+ */
+@Slf4j
+@Service
+public class OpenAIStyleLLMServiceImpl implements LLMService {
+
+ // 闇�瑕佺鐢ㄦ�濊�冩ā寮忕殑骞冲彴鍩熷悕鍙婂叾瀵瑰簲鍙傛暟
+ private static final Map<String, Map<String, Object>> THINKING_DISABLED_DOMAINS = new LinkedHashMap<>();
+ static {
+ THINKING_DISABLED_DOMAINS.put("aliyuncs.com", Map.of("enable_thinking", false));
+ Map<String, Object> thinkingDisabled = Map.of("thinking", Map.of("type", "disabled"));
+ THINKING_DISABLED_DOMAINS.put("deepseek.com", thinkingDisabled);
+ THINKING_DISABLED_DOMAINS.put("bigmodel.cn", thinkingDisabled);
+ THINKING_DISABLED_DOMAINS.put("moonshot.cn", thinkingDisabled);
+ THINKING_DISABLED_DOMAINS.put("volces.com", thinkingDisabled);
+ }
+
+ @Autowired
+ private ModelConfigService modelConfigService;
+
+ private final RestTemplate restTemplate = new RestTemplate();
+
+ /**
+ * 鏍规嵁鍩熷悕鑷姩绂佺敤鎬濊�冩ā寮�
+ */
+ private void applyThinkingDisabled(String baseUrl, Map<String, Object> requestBody) {
+ for (Map.Entry<String, Map<String, Object>> entry : THINKING_DISABLED_DOMAINS.entrySet()) {
+ if (baseUrl.contains(entry.getKey())) {
+ requestBody.putAll(entry.getValue());
+ log.info("涓哄煙鍚� {} 绂佺敤鎬濊�冩ā寮忥紝鍙傛暟: {}", baseUrl, entry.getValue());
+ break;
+ }
+ }
+ }
+
+ private static final String DEFAULT_SUMMARY_PROMPT = "浣犳槸涓�涓粡楠屼赴瀵岀殑璁板繂鎬荤粨鑰咃紝鎿呴暱灏嗗璇濆唴瀹硅繘琛屾�荤粨鎽樿锛岄伒寰互涓嬭鍒欙細\n1銆佹�荤粨鐢ㄦ埛鐨勯噸瑕佷俊鎭紝浠ヤ究鍦ㄦ湭鏉ョ殑瀵硅瘽涓彁渚涙洿涓�у寲鐨勬湇鍔n2銆佷笉瑕侀噸澶嶆�荤粨锛屼笉瑕侀仐蹇樹箣鍓嶈蹇嗭紝闄ら潪鍘熸潵鐨勮蹇嗚秴杩囦簡1800瀛楋紝鍚﹀垯涓嶈閬楀繕銆佷笉瑕佸帇缂╃敤鎴风殑鍘嗗彶璁板繂\n3銆佺敤鎴锋搷鎺х殑璁惧闊抽噺銆佹挱鏀鹃煶涔愩�佸ぉ姘斻�侀��鍑恒�佷笉鎯冲璇濈瓑鍜岀敤鎴锋湰韬棤鍏崇殑鍐呭锛岃繖浜涗俊鎭笉闇�瑕佸姞鍏ュ埌鎬荤粨涓璡n4銆佽亰澶╁唴瀹逛腑鐨勪粖澶╃殑鏃ユ湡鏃堕棿銆佷粖澶╃殑澶╂皵鎯呭喌涓庣敤鎴蜂簨浠舵棤鍏崇殑鏁版嵁锛岃繖浜涗俊鎭鏋滃綋鎴愯蹇嗗瓨鍌ㄤ細褰卞搷鍚庣画瀵硅瘽锛岃繖浜涗俊鎭笉闇�瑕佸姞鍏ュ埌鎬荤粨涓璡n5銆佷笉瑕佹妸璁惧鎿嶆帶鐨勬垚鏋滅粨鏋滃拰澶辫触缁撴灉鍔犲叆鍒版�荤粨涓紝涔熶笉瑕佹妸鐢ㄦ埛鐨勪竴浜涘簾璇濆姞鍏ュ埌鎬荤粨涓璡n6銆佷笉瑕佷负浜嗘�荤粨鑰屾�荤粨锛屽鏋滅敤鎴风殑鑱婂ぉ娌℃湁鎰忎箟锛岃杩斿洖鍘熸潵鐨勫巻鍙茶褰曚篃鏄彲浠ョ殑\n7銆佸彧闇�瑕佽繑鍥炴�荤粨鎽樿锛屼弗鏍兼帶鍒跺湪1800瀛楀唴\n8銆佷笉瑕佸寘鍚唬鐮併�亁ml锛屼笉闇�瑕佽В閲娿�佹敞閲婂拰璇存槑锛屼繚瀛樿蹇嗘椂浠呬粠瀵硅瘽鎻愬彇淇℃伅锛屼笉瑕佹贩鍏ョず渚嬪唴瀹筡n9銆佸鏋滄彁渚涗簡鍘嗗彶璁板繂锛岃灏嗘柊瀵硅瘽鍐呭涓庡巻鍙茶蹇嗚繘琛屾櫤鑳藉悎骞讹紝淇濈暀鏈変环鍊肩殑鍘嗗彶淇℃伅锛屽悓鏃舵坊鍔犳柊鐨勯噸瑕佷俊鎭痋n\n鍘嗗彶璁板繂锛歕n{history_memory}\n\n鏂板璇濆唴瀹癸細\n{conversation}";
+
+ private static final String DEFAULT_TITLE_PROMPT = "璇锋牴鎹互涓嬪璇濆唴瀹癸紝鐢熸垚涓�涓畝娲佺殑浼氳瘽鏍囬锛堢害15瀛椾互鍐咃級锛屽彧杩斿洖鏍囬锛屼笉瑕佸寘鍚换浣曡В閲婃垨鏍囩偣绗﹀彿锛歕n{conversation}";
+
+ @Override
+ public String generateSummary(String conversation) {
+ return generateSummary(conversation, null, null);
+ }
+
+ @Override
+ public String generateSummaryWithModel(String conversation, String modelId) {
+ return generateSummary(conversation, null, modelId);
+ }
+
+ @Override
+ public String generateSummary(String conversation, String promptTemplate, String modelId) {
+ if (!isAvailable()) {
+ log.warn("LLM鏈嶅姟涓嶅彲鐢紝鏃犳硶鐢熸垚鎬荤粨");
+ return "LLM鏈嶅姟涓嶅彲鐢紝鏃犳硶鐢熸垚鎬荤粨";
+ }
+
+ try {
+ // 浠庢櫤鎺у彴鑾峰彇LLM妯″瀷閰嶇疆
+ ModelConfigEntity llmConfig;
+ if (modelId != null && !modelId.trim().isEmpty()) {
+ // 閫氳繃鍏蜂綋妯″瀷ID鑾峰彇閰嶇疆
+ llmConfig = modelConfigService.getModelByIdFromCache(modelId);
+ } else {
+ // 淇濇寔鍚戝悗鍏煎锛屼娇鐢ㄩ粯璁ら厤缃�
+ llmConfig = getDefaultLLMConfig();
+ }
+
+ if (llmConfig == null || llmConfig.getConfigJson() == null) {
+ log.error("鏈壘鍒板彲鐢ㄧ殑LLM妯″瀷閰嶇疆锛宮odelId: {}", modelId);
+ return "鏈壘鍒板彲鐢ㄧ殑LLM妯″瀷閰嶇疆";
+ }
+
+ JSONObject configJson = llmConfig.getConfigJson();
+ String baseUrl = configJson.getStr("base_url");
+ String model = configJson.getStr("model_name");
+ String apiKey = configJson.getStr("api_key");
+ Double temperature = configJson.getDouble("temperature");
+ Integer maxTokens = configJson.getInt("max_tokens");
+
+ if (StringUtils.isBlank(baseUrl) || StringUtils.isBlank(apiKey)) {
+ log.error("LLM閰嶇疆涓嶅畬鏁达紝baseUrl鎴朼piKey涓虹┖");
+ return "LLM閰嶇疆涓嶅畬鏁达紝鏃犳硶鐢熸垚鎬荤粨";
+ }
+
+ // 鏋勫缓鎻愮ず璇�
+ String prompt = (promptTemplate != null ? promptTemplate : DEFAULT_SUMMARY_PROMPT).replace("{conversation}",
+ conversation);
+
+ // 鏋勫缓璇锋眰浣�
+ Map<String, Object> requestBody = new HashMap<>();
+ requestBody.put("model", model != null ? model : "gpt-3.5-turbo");
+
+ Map<String, Object> message = new HashMap<>();
+ message.put("role", "user");
+ message.put("content", prompt);
+
+ requestBody.put("messages", List.of(message));
+ requestBody.put("temperature", temperature != null ? temperature : 0.7);
+ requestBody.put("max_tokens", maxTokens != null ? maxTokens : 2000);
+
+ // 绂佺敤鎬濊�冩ā寮�
+ applyThinkingDisabled(baseUrl, requestBody);
+
+ // 鍙戦�丠TTP璇锋眰
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.set("Authorization", "Bearer " + apiKey);
+
+ HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);
+
+ // 鏋勫缓瀹屾暣鐨凙PI URL
+ String apiUrl = baseUrl;
+ if (!apiUrl.endsWith("/chat/completions")) {
+ if (!apiUrl.endsWith("/")) {
+ apiUrl += "/";
+ }
+ apiUrl += "chat/completions";
+ }
+
+ ResponseEntity<String> response = restTemplate.exchange(
+ apiUrl, HttpMethod.POST, entity, String.class);
+
+ if (response.getStatusCode().is2xxSuccessful()) {
+ JSONObject responseJson = JSONUtil.parseObj(response.getBody());
+ JSONArray choices = responseJson.getJSONArray("choices");
+ if (choices != null && choices.size() > 0) {
+ JSONObject choice = choices.getJSONObject(0);
+ JSONObject messageObj = choice.getJSONObject("message");
+ return messageObj.getStr("content");
+ }
+ } else {
+ log.error("LLM API璋冪敤澶辫触锛岀姸鎬佺爜锛歿}锛屽搷搴旓細{}", response.getStatusCode(), response.getBody());
+ }
+ } catch (Exception e) {
+ log.error("璋冪敤LLM鏈嶅姟鐢熸垚鎬荤粨鏃跺彂鐢熷紓甯革紝modelId: {}", modelId, e);
+ }
+
+ return "鐢熸垚鎬荤粨澶辫触锛岃绋嶅悗閲嶈瘯";
+ }
+
+ @Override
+ public String generateSummary(String conversation, String promptTemplate) {
+ return generateSummary(conversation, promptTemplate, null);
+ }
+
+ @Override
+ public String generateSummaryWithHistory(String conversation, String historyMemory, String promptTemplate,
+ String modelId) {
+ if (!isAvailable()) {
+ log.warn("LLM鏈嶅姟涓嶅彲鐢紝鏃犳硶鐢熸垚鎬荤粨");
+ return "LLM鏈嶅姟涓嶅彲鐢紝鏃犳硶鐢熸垚鎬荤粨";
+ }
+
+ try {
+ // 浠庢櫤鎺у彴鑾峰彇LLM妯″瀷閰嶇疆
+ ModelConfigEntity llmConfig;
+ if (modelId != null && !modelId.trim().isEmpty()) {
+ llmConfig = modelConfigService.getModelByIdFromCache(modelId);
+ } else {
+ llmConfig = getDefaultLLMConfig();
+ }
+
+ if (llmConfig == null || llmConfig.getConfigJson() == null) {
+ log.error("鏈壘鍒板彲鐢ㄧ殑LLM妯″瀷閰嶇疆锛宮odelId: {}", modelId);
+ return "鏈壘鍒板彲鐢ㄧ殑LLM妯″瀷閰嶇疆";
+ }
+
+ JSONObject configJson = llmConfig.getConfigJson();
+ String baseUrl = configJson.getStr("base_url");
+ String model = configJson.getStr("model_name");
+ String apiKey = configJson.getStr("api_key");
+
+ if (StringUtils.isBlank(baseUrl) || StringUtils.isBlank(apiKey)) {
+ log.error("LLM閰嶇疆涓嶅畬鏁达紝baseUrl鎴朼piKey涓虹┖");
+ return "LLM閰嶇疆涓嶅畬鏁达紝鏃犳硶鐢熸垚鎬荤粨";
+ }
+
+ // 鏋勫缓鎻愮ず璇嶏紝鍖呭惈鍘嗗彶璁板繂
+ String prompt = (promptTemplate != null ? promptTemplate : DEFAULT_SUMMARY_PROMPT)
+ .replace("{history_memory}", historyMemory != null ? historyMemory : "鏃犲巻鍙茶蹇�")
+ .replace("{conversation}", conversation);
+
+ // 鏋勫缓璇锋眰浣�
+ Map<String, Object> requestBody = new HashMap<>();
+ requestBody.put("model", model != null ? model : "gpt-3.5-turbo");
+
+ Map<String, Object> message = new HashMap<>();
+ message.put("role", "user");
+ message.put("content", prompt);
+
+ requestBody.put("messages", List.of(message));
+ requestBody.put("temperature", 0.2);
+ requestBody.put("max_tokens", 2000);
+
+ // 绂佺敤鎬濊�冩ā寮�
+ applyThinkingDisabled(baseUrl, requestBody);
+
+ // 鍙戦�丠TTP璇锋眰
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.set("Authorization", "Bearer " + apiKey);
+
+ HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);
+
+ // 鏋勫缓瀹屾暣鐨凙PI URL
+ String apiUrl = baseUrl;
+ if (!apiUrl.endsWith("/chat/completions")) {
+ if (!apiUrl.endsWith("/")) {
+ apiUrl += "/";
+ }
+ apiUrl += "chat/completions";
+ }
+
+ ResponseEntity<String> response = restTemplate.exchange(
+ apiUrl, HttpMethod.POST, entity, String.class);
+
+ if (response.getStatusCode().is2xxSuccessful()) {
+ JSONObject responseJson = JSONUtil.parseObj(response.getBody());
+ JSONArray choices = responseJson.getJSONArray("choices");
+ if (choices != null && choices.size() > 0) {
+ JSONObject choice = choices.getJSONObject(0);
+ JSONObject messageObj = choice.getJSONObject("message");
+ return messageObj.getStr("content");
+ }
+ } else {
+ log.error("LLM API璋冪敤澶辫触锛岀姸鎬佺爜锛歿}锛屽搷搴旓細{}", response.getStatusCode(), response.getBody());
+ }
+ } catch (Exception e) {
+ log.error("璋冪敤LLM鏈嶅姟鐢熸垚鎬荤粨鏃跺彂鐢熷紓甯革紝modelId: {}", modelId, e);
+ }
+
+ return "鐢熸垚鎬荤粨澶辫触锛岃绋嶅悗閲嶈瘯";
+ }
+
+ @Override
+ public boolean isAvailable() {
+ try {
+ ModelConfigEntity defaultLLMConfig = getDefaultLLMConfig();
+ if (defaultLLMConfig == null || defaultLLMConfig.getConfigJson() == null) {
+ return false;
+ }
+
+ JSONObject configJson = defaultLLMConfig.getConfigJson();
+ String baseUrl = configJson.getStr("base_url");
+ String apiKey = configJson.getStr("api_key");
+
+ return baseUrl != null && !baseUrl.trim().isEmpty() &&
+ apiKey != null && !apiKey.trim().isEmpty();
+ } catch (Exception e) {
+ log.error("妫�鏌LM鏈嶅姟鍙敤鎬ф椂鍙戠敓寮傚父锛�", e);
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isAvailable(String modelId) {
+ try {
+ if (modelId == null || modelId.trim().isEmpty()) {
+ return isAvailable();
+ }
+
+ // 閫氳繃鍏蜂綋妯″瀷ID鑾峰彇閰嶇疆
+ ModelConfigEntity modelConfig = modelConfigService.getModelByIdFromCache(modelId);
+ if (modelConfig == null || modelConfig.getConfigJson() == null) {
+ log.warn("鏈壘鍒版寚瀹氱殑LLM妯″瀷閰嶇疆锛宮odelId: {}", modelId);
+ return false;
+ }
+
+ JSONObject configJson = modelConfig.getConfigJson();
+ String baseUrl = configJson.getStr("base_url");
+ String apiKey = configJson.getStr("api_key");
+
+ return baseUrl != null && !baseUrl.trim().isEmpty() &&
+ apiKey != null && !apiKey.trim().isEmpty();
+ } catch (Exception e) {
+ log.error("妫�鏌LM鏈嶅姟鍙敤鎬ф椂鍙戠敓寮傚父锛宮odelId: {}", modelId, e);
+ return false;
+ }
+ }
+
+ /**
+ * 浠庢櫤鎺у彴鑾峰彇榛樿鐨凩LM妯″瀷閰嶇疆
+ */
+ private ModelConfigEntity getDefaultLLMConfig() {
+ try {
+ // 鑾峰彇鎵�鏈夊惎鐢ㄧ殑LLM妯″瀷閰嶇疆
+ List<ModelConfigEntity> llmConfigs = modelConfigService.getEnabledModelsByType("LLM");
+ if (llmConfigs == null || llmConfigs.isEmpty()) {
+ return null;
+ }
+
+ // 浼樺厛杩斿洖榛樿閰嶇疆锛屽鏋滄病鏈夐粯璁ら厤缃垯杩斿洖绗竴涓惎鐢ㄧ殑閰嶇疆
+ for (ModelConfigEntity config : llmConfigs) {
+ if (config.getIsDefault() != null && config.getIsDefault() == 1) {
+ return config;
+ }
+ }
+
+ return llmConfigs.get(0);
+ } catch (Exception e) {
+ log.error("鑾峰彇LLM妯″瀷閰嶇疆鏃跺彂鐢熷紓甯革細", e);
+ return null;
+ }
+ }
+
+ @Override
+ public String generateTitle(String conversation, String modelId) {
+ if (!isAvailable()) {
+ log.warn("LLM鏈嶅姟涓嶅彲鐢紝鏃犳硶鐢熸垚鏍囬");
+ return null;
+ }
+
+ try {
+ ModelConfigEntity llmConfig;
+ if (modelId != null && !modelId.trim().isEmpty()) {
+ llmConfig = modelConfigService.getModelByIdFromCache(modelId);
+ } else {
+ llmConfig = getDefaultLLMConfig();
+ }
+
+ if (llmConfig == null || llmConfig.getConfigJson() == null) {
+ log.error("鏈壘鍒板彲鐢ㄧ殑LLM妯″瀷閰嶇疆锛宮odelId: {}", modelId);
+ return null;
+ }
+
+ JSONObject configJson = llmConfig.getConfigJson();
+ String baseUrl = configJson.getStr("base_url");
+ String model = configJson.getStr("model_name");
+ String apiKey = configJson.getStr("api_key");
+
+ if (StringUtils.isBlank(baseUrl) || StringUtils.isBlank(apiKey)) {
+ log.error("LLM閰嶇疆涓嶅畬鏁达紝baseUrl鎴朼piKey涓虹┖");
+ return null;
+ }
+
+ String prompt = DEFAULT_TITLE_PROMPT.replace("{conversation}", conversation);
+
+ Map<String, Object> requestBody = new HashMap<>();
+ requestBody.put("model", model != null ? model : "gpt-3.5-turbo");
+
+ Map<String, Object> message = new HashMap<>();
+ message.put("role", "user");
+ message.put("content", prompt);
+
+ requestBody.put("messages", List.of(message));
+ requestBody.put("temperature", 0.3);
+ requestBody.put("max_tokens", 50);
+
+ // 绂佺敤鎬濊�冩ā寮�
+ applyThinkingDisabled(baseUrl, requestBody);
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.set("Authorization", "Bearer " + apiKey);
+
+ HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);
+
+ String apiUrl = baseUrl;
+ if (!apiUrl.endsWith("/chat/completions")) {
+ if (!apiUrl.endsWith("/")) {
+ apiUrl += "/";
+ }
+ apiUrl += "chat/completions";
+ }
+
+ ResponseEntity<String> response = restTemplate.exchange(
+ apiUrl, HttpMethod.POST, entity, String.class);
+
+ if (response.getStatusCode().is2xxSuccessful()) {
+ JSONObject responseJson = JSONUtil.parseObj(response.getBody());
+ JSONArray choices = responseJson.getJSONArray("choices");
+ if (choices != null && choices.size() > 0) {
+ JSONObject choice = choices.getJSONObject(0);
+ JSONObject messageObj = choice.getJSONObject("message");
+ String title = messageObj.getStr("content");
+ if (StringUtils.isNotBlank(title)) {
+ title = title.trim().replaceAll("[锛屻�傦紒锛熴�侊細锛�''\"\"銆愩�戯紙锛塢", "");
+ if (title.length() > 15) {
+ title = title.substring(0, 15);
+ }
+ return title;
+ }
+ }
+ } else {
+ log.error("LLM API璋冪敤澶辫触锛岀姸鎬佺爜锛歿}锛屽搷搴旓細{}", response.getStatusCode(), response.getBody());
+ }
+ } catch (Exception e) {
+ log.error("璋冪敤LLM鏈嶅姟鐢熸垚鏍囬鏃跺彂鐢熷紓甯革紝modelId: {}", modelId, e);
+ }
+
+ return null;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/controller/ModelController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/controller/ModelController.java
new file mode 100644
index 0000000..2119f3c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/controller/ModelController.java
@@ -0,0 +1,174 @@
+package xiaozhi.modules.model.controller;
+
+import java.util.List;
+
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.utils.ConvertUtils;
+import xiaozhi.common.utils.Result;
+import xiaozhi.modules.agent.service.AgentTemplateService;
+import xiaozhi.modules.config.service.ConfigService;
+import xiaozhi.modules.model.dto.LlmModelBasicInfoDTO;
+import xiaozhi.modules.model.dto.ModelBasicInfoDTO;
+import xiaozhi.modules.model.dto.ModelConfigBodyDTO;
+import xiaozhi.modules.model.dto.ModelConfigDTO;
+import xiaozhi.modules.model.dto.ModelProviderDTO;
+import xiaozhi.modules.model.dto.VoiceDTO;
+import xiaozhi.modules.model.entity.ModelConfigEntity;
+import xiaozhi.modules.model.service.ModelConfigService;
+import xiaozhi.modules.model.service.ModelProviderService;
+import xiaozhi.modules.timbre.service.TimbreService;
+
+@AllArgsConstructor
+@RestController
+@RequestMapping("/models")
+@Tag(name = "妯″瀷閰嶇疆")
+public class ModelController {
+
+ private final ModelProviderService modelProviderService;
+ private final TimbreService timbreService;
+ private final ModelConfigService modelConfigService;
+ private final ConfigService configService;
+ private final AgentTemplateService agentTemplateService;
+
+ @GetMapping("/names")
+ @Operation(summary = "鑾峰彇鎵�鏈夋ā鍨嬪悕绉�")
+ @RequiresPermissions("sys:role:normal")
+ public Result<List<ModelBasicInfoDTO>> getModelNames(@RequestParam String modelType,
+ @RequestParam(required = false) String modelName) {
+ List<ModelBasicInfoDTO> modelList = modelConfigService.getModelCodeList(modelType, modelName);
+ return new Result<List<ModelBasicInfoDTO>>().ok(modelList);
+ }
+
+ @GetMapping("/llm/names")
+ @Operation(summary = "鑾峰彇LLM妯″瀷淇℃伅")
+ @RequiresPermissions("sys:role:normal")
+ public Result<List<LlmModelBasicInfoDTO>> getLlmModelCodeList(@RequestParam(required = false) String modelName) {
+ List<LlmModelBasicInfoDTO> llmModelCodeList = modelConfigService.getLlmModelCodeList(modelName);
+ return new Result<List<LlmModelBasicInfoDTO>>().ok(llmModelCodeList);
+ }
+
+ @GetMapping("/{modelType}/provideTypes")
+ @Operation(summary = "鑾峰彇妯″瀷渚涘簲鍣ㄥ垪琛�")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<List<ModelProviderDTO>> getModelProviderList(@PathVariable String modelType) {
+ List<ModelProviderDTO> modelProviderDTOS = modelProviderService.getListByModelType(modelType);
+ return new Result<List<ModelProviderDTO>>().ok(modelProviderDTOS);
+ }
+
+ @GetMapping("/list")
+ @Operation(summary = "鑾峰彇妯″瀷閰嶇疆鍒楄〃")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<PageData<ModelConfigDTO>> getModelConfigList(
+ @RequestParam(required = true) String modelType,
+ @RequestParam(required = false) String modelName,
+ @RequestParam(required = true, defaultValue = "0") String page,
+ @RequestParam(required = true, defaultValue = "10") String limit) {
+ PageData<ModelConfigDTO> pageList = modelConfigService.getPageList(modelType, modelName, page, limit);
+ return new Result<PageData<ModelConfigDTO>>().ok(pageList);
+ }
+
+ @PostMapping("/{modelType}/{provideCode}")
+ @Operation(summary = "鏂板妯″瀷閰嶇疆")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<ModelConfigDTO> addModelConfig(@PathVariable String modelType,
+ @PathVariable String provideCode,
+ @RequestBody ModelConfigBodyDTO modelConfigBodyDTO) {
+ ModelConfigDTO modelConfigDTO = modelConfigService.add(modelType, provideCode, modelConfigBodyDTO);
+ configService.getConfig(false);
+ return new Result<ModelConfigDTO>().ok(modelConfigDTO);
+ }
+
+ @PutMapping("/{modelType}/{provideCode}/{id}")
+ @Operation(summary = "缂栬緫妯″瀷閰嶇疆")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<ModelConfigDTO> editModelConfig(@PathVariable String modelType,
+ @PathVariable String provideCode,
+ @PathVariable String id,
+ @RequestBody ModelConfigBodyDTO modelConfigBodyDTO) {
+ ModelConfigDTO modelConfigDTO = modelConfigService.edit(modelType, provideCode, id, modelConfigBodyDTO);
+ configService.getConfig(false);
+ return new Result<ModelConfigDTO>().ok(modelConfigDTO);
+ }
+
+ @DeleteMapping("/{id}")
+ @Operation(summary = "鍒犻櫎妯″瀷閰嶇疆")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<Void> deleteModelConfig(@PathVariable String id) {
+ modelConfigService.delete(id);
+ return new Result<>();
+ }
+
+ @GetMapping("/{id}")
+ @Operation(summary = "鑾峰彇妯″瀷閰嶇疆")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<ModelConfigDTO> getModelConfig(@PathVariable String id) {
+ ModelConfigEntity item = modelConfigService.selectById(id);
+ ModelConfigDTO modelConfigDTO = ConvertUtils.sourceToTarget(item, ModelConfigDTO.class);
+ return new Result<ModelConfigDTO>().ok(modelConfigDTO);
+ }
+
+ @PutMapping("/enable/{id}/{status}")
+ @Operation(summary = "鍚敤/鍏抽棴妯″瀷閰嶇疆")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<Void> enableModelConfig(@PathVariable String id, @PathVariable Integer status) {
+ ModelConfigEntity entity = modelConfigService.selectById(id);
+ if (entity == null) {
+ return new Result<Void>().error("妯″瀷閰嶇疆涓嶅瓨鍦�");
+ }
+ // 涓嶈兘鍏抽棴榛樿妯″瀷
+ if (status == 0 && entity.getIsDefault() > 0) {
+ return new Result<Void>().error("榛樿妯″瀷閰嶇疆涓嶅厑璁稿叧闂�");
+ }
+ // 涓嶆洿鏂癈onfigJson瀛楁
+ entity.setConfigJson(null);
+ entity.setIsEnabled(status);
+ modelConfigService.updateById(entity);
+ return new Result<Void>();
+ }
+
+ @PutMapping("/default/{id}")
+ @Operation(summary = "璁剧疆榛樿妯″瀷")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<Void> setDefaultModel(@PathVariable String id) {
+ ModelConfigEntity entity = modelConfigService.selectById(id);
+ if (entity == null) {
+ return new Result<Void>().error("妯″瀷閰嶇疆涓嶅瓨鍦�");
+ }
+ // 灏嗗叾浠栨ā鍨嬭缃负闈為粯璁�
+ modelConfigService.setDefaultModel(entity.getModelType(), 0);
+ entity.setIsEnabled(1);
+ entity.setIsDefault(1);
+ // 涓嶆洿鏂癈onfigJson瀛楁
+ entity.setConfigJson(null);
+ modelConfigService.updateById(entity);
+
+ // 鏇存柊妯℃澘琛ㄤ腑瀵瑰簲鐨勬ā鍨婭D
+ agentTemplateService.updateDefaultTemplateModelId(entity.getModelType(), entity.getId());
+
+ configService.getConfig(false);
+ return new Result<Void>();
+ }
+
+ @GetMapping("/{modelId}/voices")
+ @Operation(summary = "鑾峰彇妯″瀷闊宠壊")
+ @RequiresPermissions("sys:role:normal")
+ public Result<List<VoiceDTO>> getVoiceList(@PathVariable String modelId,
+ @RequestParam(required = false) String voiceName) {
+ List<VoiceDTO> voiceList = timbreService.getVoiceNames(modelId, voiceName);
+ return new Result<List<VoiceDTO>>().ok(voiceList);
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/controller/ModelProviderController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/controller/ModelProviderController.java
new file mode 100644
index 0000000..24a7c7c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/controller/ModelProviderController.java
@@ -0,0 +1,75 @@
+package xiaozhi.modules.model.controller;
+
+import java.util.List;
+
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.utils.Result;
+import xiaozhi.common.utils.ResultUtils;
+import xiaozhi.common.validator.group.UpdateGroup;
+import xiaozhi.modules.model.dto.ModelProviderDTO;
+import xiaozhi.modules.model.service.ModelProviderService;
+
+@AllArgsConstructor
+@RestController
+@RequestMapping("/models/provider")
+@Tag(name = "妯″瀷渚涘簲鍣�")
+public class ModelProviderController {
+
+ private final ModelProviderService modelProviderService;
+
+ @GetMapping
+ @Operation(summary = "鑾峰彇妯″瀷渚涘簲鍣ㄥ垪琛�")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<PageData<ModelProviderDTO>> getListPage(ModelProviderDTO modelProviderDTO,
+ @RequestParam(required = true, defaultValue = "0") String page,
+ @RequestParam(required = true, defaultValue = "10") String limit) {
+ return new Result<PageData<ModelProviderDTO>>()
+ .ok(modelProviderService.getListPage(modelProviderDTO, page, limit));
+ }
+
+ @PostMapping
+ @Operation(summary = "鏂板妯″瀷渚涘簲鍣�")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<ModelProviderDTO> add(@RequestBody @Validated ModelProviderDTO modelProviderDTO) {
+ ModelProviderDTO resp = modelProviderService.add(modelProviderDTO);
+ return new Result<ModelProviderDTO>().ok(resp);
+ }
+
+ @PutMapping
+ @Operation(summary = "淇敼妯″瀷渚涘簲鍣�")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<ModelProviderDTO> edit(@RequestBody @Validated(UpdateGroup.class) ModelProviderDTO modelProviderDTO) {
+ ModelProviderDTO resp = modelProviderService.edit(modelProviderDTO);
+ return new Result<ModelProviderDTO>().ok(resp);
+ }
+
+ @PostMapping("/delete")
+ @Operation(summary = "鍒犻櫎妯″瀷渚涘簲鍣�")
+ @RequiresPermissions("sys:role:superAdmin")
+ @Parameter(name = "ids", description = "ID鏁扮粍", required = true)
+ public Result<Void> delete(@RequestBody List<String> ids) {
+ modelProviderService.delete(ids);
+ return new Result<>();
+ }
+
+ @GetMapping("/plugin/names")
+ @Tag(name = "鑾峰彇鎻掍欢鍚嶇О鍒楄〃")
+ public Result<List<ModelProviderDTO>> getPluginNameList() {
+ return ResultUtils.success(modelProviderService.getPluginList());
+ }
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dao/ModelConfigDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dao/ModelConfigDao.java
new file mode 100644
index 0000000..70c6abc
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dao/ModelConfigDao.java
@@ -0,0 +1,24 @@
+package xiaozhi.modules.model.dao;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import xiaozhi.common.dao.BaseDao;
+import xiaozhi.modules.model.entity.ModelConfigEntity;
+
+@Mapper
+public interface ModelConfigDao extends BaseDao<ModelConfigEntity> {
+
+ /**
+ * get model_code list
+ */
+ List<String> getModelCodeList(@Param("modelType") String modelType, @Param("modelName") String modelName);
+
+ /**
+ * 鑾峰彇绗﹀悎鏉′欢鐨凾TS骞冲彴鍒楄〃(id鍜宮odelName)
+ */
+ List<Map<String, Object>> getTtsPlatformList();
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dao/ModelProviderDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dao/ModelProviderDao.java
new file mode 100644
index 0000000..68e4185
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dao/ModelProviderDao.java
@@ -0,0 +1,15 @@
+package xiaozhi.modules.model.dao;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import xiaozhi.common.dao.BaseDao;
+import xiaozhi.modules.model.entity.ModelProviderEntity;
+
+@Mapper
+public interface ModelProviderDao extends BaseDao<ModelProviderEntity> {
+
+ List<String> getFieldList(@Param("modelType") String modelType, @Param("providerCode") String providerCode);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/LlmModelBasicInfoDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/LlmModelBasicInfoDTO.java
new file mode 100644
index 0000000..3995ceb
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/LlmModelBasicInfoDTO.java
@@ -0,0 +1,13 @@
+package xiaozhi.modules.model.dto;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * LLM鐨勬ā鍨嬬殑鍩虹灞曠ず鏁版嵁
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class LlmModelBasicInfoDTO extends ModelBasicInfoDTO{
+ private String type;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelBasicInfoDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelBasicInfoDTO.java
new file mode 100644
index 0000000..571c791
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelBasicInfoDTO.java
@@ -0,0 +1,9 @@
+package xiaozhi.modules.model.dto;
+
+import lombok.Data;
+
+@Data
+public class ModelBasicInfoDTO {
+ private String id;
+ private String modelName;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelConfigBodyDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelConfigBodyDTO.java
new file mode 100644
index 0000000..eb1197b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelConfigBodyDTO.java
@@ -0,0 +1,42 @@
+package xiaozhi.modules.model.dto;
+
+import java.io.Serial;
+
+import cn.hutool.json.JSONObject;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@Schema(description = "妯″瀷渚涘簲鍣�/鍟�")
+public class ModelConfigBodyDTO {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "妯″瀷ID,鏈~鍐欏皢鑷姩鐢熸垚")
+ private String id;
+
+ @Schema(description = "妯″瀷缂栫爜(濡侫liLLM銆丏oubaoTTS)")
+ private String modelCode;
+
+ @Schema(description = "妯″瀷鍚嶇О")
+ private String modelName;
+
+ @Schema(description = "鏄惁榛樿閰嶇疆(0鍚� 1鏄�)")
+ private Integer isDefault;
+
+ @Schema(description = "鏄惁鍚敤")
+ private Integer isEnabled;
+
+ @Schema(description = "妯″瀷閰嶇疆(JSON鏍煎紡)")
+ private JSONObject configJson;
+
+ @Schema(description = "瀹樻柟鏂囨。閾炬帴")
+ private String docLink;
+
+ @Schema(description = "澶囨敞")
+ private String remark;
+
+ @Schema(description = "鎺掑簭")
+ private Integer sort;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelConfigDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelConfigDTO.java
new file mode 100644
index 0000000..ed73e1f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelConfigDTO.java
@@ -0,0 +1,46 @@
+package xiaozhi.modules.model.dto;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+import cn.hutool.json.JSONObject;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@Schema(description = "妯″瀷渚涘簲鍣�/鍟�")
+public class ModelConfigDTO implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "涓婚敭")
+ private String id;
+
+ @Schema(description = "妯″瀷绫诲瀷(Memory/ASR/VAD/LLM/TTS)")
+ private String modelType;
+
+ @Schema(description = "妯″瀷缂栫爜(濡侫liLLM銆丏oubaoTTS)")
+ private String modelCode;
+
+ @Schema(description = "妯″瀷鍚嶇О")
+ private String modelName;
+
+ @Schema(description = "鏄惁榛樿閰嶇疆(0鍚� 1鏄�)")
+ private Integer isDefault;
+
+ @Schema(description = "鏄惁鍚敤")
+ private Integer isEnabled;
+
+ @Schema(description = "妯″瀷閰嶇疆(JSON鏍煎紡)")
+ private JSONObject configJson;
+
+ @Schema(description = "瀹樻柟鏂囨。閾炬帴")
+ private String docLink;
+
+ @Schema(description = "澶囨敞")
+ private String remark;
+
+ @Schema(description = "鎺掑簭")
+ private Integer sort;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelProviderDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelProviderDTO.java
new file mode 100644
index 0000000..317820d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/ModelProviderDTO.java
@@ -0,0 +1,59 @@
+package xiaozhi.modules.model.dto;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import xiaozhi.common.validator.group.UpdateGroup;
+
+@Data
+@Schema(description = "妯″瀷渚涘簲鍣�/鍟�")
+public class ModelProviderDTO implements Serializable {
+ @Schema(description = "涓婚敭")
+ @NotBlank(message = "id涓嶈兘涓虹┖", groups = UpdateGroup.class)
+ private String id;
+
+ @Schema(description = "妯″瀷绫诲瀷(Memory/ASR/VAD/LLM/TTS)")
+ @NotBlank(message = "modelType涓嶈兘涓虹┖")
+ private String modelType;
+
+ @Schema(description = "渚涘簲鍣ㄧ被鍨�")
+ @NotBlank(message = "providerCode涓嶈兘涓虹┖")
+ private String providerCode;
+
+ @Schema(description = "渚涘簲鍣ㄥ悕绉�")
+ @NotBlank(message = "name涓嶈兘涓虹┖")
+ private String name;
+
+ @Schema(description = "渚涘簲鍣ㄥ瓧娈靛垪琛�(JSON鏍煎紡)")
+ @TableField(typeHandler = JacksonTypeHandler.class)
+ @NotBlank(message = "fields(JSON鏍煎紡)涓嶈兘涓虹┖")
+ private String fields;
+
+ @Schema(description = "鎺掑簭")
+ @NotNull(message = "sort涓嶈兘涓虹┖")
+ private Integer sort;
+
+ @Schema(description = "鏇存柊鑰�")
+ @TableField(fill = FieldFill.UPDATE)
+ private Long updater;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ @TableField(fill = FieldFill.UPDATE)
+ private Date updateDate;
+
+ @Schema(description = "鍒涘缓鑰�")
+ @TableField(fill = FieldFill.INSERT)
+ private Long creator;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ @TableField(fill = FieldFill.INSERT)
+ private Date createDate;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/VoiceDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/VoiceDTO.java
new file mode 100644
index 0000000..9bc4fa8
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/dto/VoiceDTO.java
@@ -0,0 +1,50 @@
+package xiaozhi.modules.model.dto;
+
+import java.io.Serializable;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Schema(description = "闊宠壊淇℃伅")
+public class VoiceDTO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "闊宠壊ID")
+ private String id;
+
+ @Schema(description = "闊宠壊鍚嶇О")
+ private String name;
+
+ @Schema(description = "闊抽鎾斁鍦板潃")
+ private String voiceDemo;
+
+ @Schema(description = "璇█绫诲瀷")
+ private String languages;
+
+ @Schema(description = "鏄惁涓哄厠闅嗛煶鑹�")
+ private Boolean isClone;
+
+ // 娣诲姞鍙屽弬鏁版瀯閫犲嚱鏁帮紝淇濇寔鍚戝悗鍏煎
+ public VoiceDTO(String id, String name) {
+ this.id = id;
+ this.name = name;
+ this.voiceDemo = null;
+ this.languages = null;
+ this.isClone = false; // 榛樿涓嶆槸鍏嬮殕闊宠壊
+ }
+
+ // 娣诲姞涓夊弬鏁版瀯閫犲嚱鏁帮紝鐢ㄤ簬鏅�氶煶鑹�
+ public VoiceDTO(String id, String name, String voiceDemo) {
+ this.id = id;
+ this.name = name;
+ this.voiceDemo = voiceDemo;
+ this.languages = null;
+ this.isClone = false;
+ }
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/entity/ModelConfigEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/entity/ModelConfigEntity.java
new file mode 100644
index 0000000..aba2c8f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/entity/ModelConfigEntity.java
@@ -0,0 +1,65 @@
+package xiaozhi.modules.model.entity;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+
+import cn.hutool.json.JSONObject;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@TableName(value = "ai_model_config", autoResultMap = true)
+@Schema(description = "妯″瀷閰嶇疆琛�")
+public class ModelConfigEntity {
+
+ @Schema(description = "涓婚敭")
+ private String id;
+
+ @Schema(description = "妯″瀷绫诲瀷(Memory/ASR/VAD/LLM/TTS)")
+ private String modelType;
+
+ @Schema(description = "妯″瀷缂栫爜(濡侫liLLM銆丏oubaoTTS)")
+ private String modelCode;
+
+ @Schema(description = "妯″瀷鍚嶇О")
+ private String modelName;
+
+ @Schema(description = "鏄惁榛樿閰嶇疆(0鍚� 1鏄�)")
+ private Integer isDefault;
+
+ @Schema(description = "鏄惁鍚敤")
+ private Integer isEnabled;
+
+ @TableField(typeHandler = JacksonTypeHandler.class)
+ @Schema(description = "妯″瀷閰嶇疆(JSON鏍煎紡)")
+ private JSONObject configJson;
+
+ @Schema(description = "瀹樻柟鏂囨。閾炬帴")
+ private String docLink;
+
+ @Schema(description = "澶囨敞")
+ private String remark;
+
+ @Schema(description = "鎺掑簭")
+ private Integer sort;
+
+ @Schema(description = "鏇存柊鑰�")
+ @TableField(fill = FieldFill.UPDATE)
+ private Long updater;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ @TableField(fill = FieldFill.UPDATE)
+ private Date updateDate;
+
+ @Schema(description = "鍒涘缓鑰�")
+ @TableField(fill = FieldFill.INSERT)
+ private Long creator;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ @TableField(fill = FieldFill.INSERT)
+ private Date createDate;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/entity/ModelProviderEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/entity/ModelProviderEntity.java
new file mode 100644
index 0000000..0090475
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/entity/ModelProviderEntity.java
@@ -0,0 +1,47 @@
+package xiaozhi.modules.model.entity;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@TableName("ai_model_provider")
+@Schema(description = "妯″瀷渚涘簲鍣ㄨ〃")
+public class ModelProviderEntity {
+
+ @TableId(type = IdType.ASSIGN_UUID)
+ @Schema(description = "涓婚敭")
+ private String id;
+
+ @Schema(description = "妯″瀷绫诲瀷(Memory/ASR/VAD/LLM/TTS)")
+ private String modelType;
+
+ @Schema(description = "渚涘簲鍣ㄧ被鍨嬶紝濡� openai銆�")
+ private String providerCode;
+
+ @Schema(description = "渚涘簲鍣ㄥ悕绉�")
+ private String name;
+
+ @Schema(description = "渚涘簲鍣ㄥ瓧娈靛垪琛�(JSON鏍煎紡)")
+ private String fields;
+
+ @Schema(description = "鎺掑簭")
+ private Integer sort;
+
+ @Schema(description = "鍒涘缓鑰�")
+ private Long creator;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ private Date createDate;
+
+ @Schema(description = "鏇存柊鑰�")
+ private Long updater;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ private Date updateDate;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelConfigService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelConfigService.java
new file mode 100644
index 0000000..9ae1076
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelConfigService.java
@@ -0,0 +1,66 @@
+package xiaozhi.modules.model.service;
+
+import java.util.List;
+import java.util.Map;
+
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.service.BaseService;
+import xiaozhi.modules.model.dto.LlmModelBasicInfoDTO;
+import xiaozhi.modules.model.dto.ModelBasicInfoDTO;
+import xiaozhi.modules.model.dto.ModelConfigBodyDTO;
+import xiaozhi.modules.model.dto.ModelConfigDTO;
+import xiaozhi.modules.model.entity.ModelConfigEntity;
+
+public interface ModelConfigService extends BaseService<ModelConfigEntity> {
+
+ List<ModelBasicInfoDTO> getModelCodeList(String modelType, String modelName);
+
+ List<LlmModelBasicInfoDTO> getLlmModelCodeList(String modelName);
+
+ PageData<ModelConfigDTO> getPageList(String modelType, String modelName, String page, String limit);
+
+ ModelConfigDTO add(String modelType, String provideCode, ModelConfigBodyDTO modelConfigBodyDTO);
+
+ ModelConfigDTO edit(String modelType, String provideCode, String id, ModelConfigBodyDTO modelConfigBodyDTO);
+
+ void delete(String id);
+
+ /**
+ * 鏍规嵁ID鑾峰彇妯″瀷鍚嶇О
+ *
+ * @param id 妯″瀷ID
+ * @return 妯″瀷鍚嶇О
+ */
+ String getModelNameById(String id);
+
+ /**
+ * 鏍规嵁ID鑾峰彇妯″瀷閰嶇疆
+ *
+ * @param id 妯″瀷ID
+ * @return 妯″瀷閰嶇疆瀹炰綋
+ */
+ ModelConfigEntity getModelByIdFromCache(String id);
+
+ /**
+ * 璁剧疆榛樿妯″瀷
+ *
+ * @param modelType 妯″瀷绫诲瀷
+ * @param isDefault 鏄惁榛樿锛�1:鏄紝0:鍚︼級
+ */
+ void setDefaultModel(String modelType, int isDefault);
+
+ /**
+ * 鑾峰彇绗﹀悎鏉′欢鐨凾TS骞冲彴鍒楄〃
+ *
+ * @return TTS骞冲彴鍒楄〃(id鍜宮odelName)
+ */
+ List<Map<String, Object>> getTtsPlatformList();
+
+ /**
+ * 鏍规嵁妯″瀷绫诲瀷鑾峰彇鎵�鏈夊惎鐢ㄧ殑妯″瀷閰嶇疆
+ *
+ * @param modelType 妯″瀷绫诲瀷锛堝锛歀LM, TTS, ASR绛夛級
+ * @return 鍚敤鐨勬ā鍨嬮厤缃垪琛�
+ */
+ List<ModelConfigEntity> getEnabledModelsByType(String modelType);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelProviderService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelProviderService.java
new file mode 100644
index 0000000..829f86a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelProviderService.java
@@ -0,0 +1,30 @@
+package xiaozhi.modules.model.service;
+
+import java.util.Collection;
+import java.util.List;
+
+import xiaozhi.common.page.PageData;
+import xiaozhi.modules.model.dto.ModelProviderDTO;
+
+public interface ModelProviderService {
+
+ List<ModelProviderDTO> getPluginList();
+
+ ModelProviderDTO getById(String id);
+
+ List<ModelProviderDTO> getPluginListByIds(Collection<String> ids);
+
+ List<ModelProviderDTO> getListByModelType(String modelType);
+
+ ModelProviderDTO add(ModelProviderDTO modelProviderDTO);
+
+ ModelProviderDTO edit(ModelProviderDTO modelProviderDTO);
+
+ void delete(String id);
+
+ void delete(List<String> id);
+
+ PageData<ModelProviderDTO> getListPage(ModelProviderDTO modelProviderDTO, String page, String limit);
+
+ List<ModelProviderDTO> getList(String modelType, String provideCode);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelConfigServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelConfigServiceImpl.java
new file mode 100644
index 0000000..a0be491
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelConfigServiceImpl.java
@@ -0,0 +1,562 @@
+package xiaozhi.modules.model.service.impl;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.metadata.OrderItem;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.json.JSONObject;
+import lombok.AllArgsConstructor;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.redis.RedisKeys;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.common.service.impl.BaseServiceImpl;
+import xiaozhi.common.utils.ConvertUtils;
+import xiaozhi.common.utils.SensitiveDataUtils;
+import xiaozhi.modules.agent.dao.AgentDao;
+import xiaozhi.modules.agent.entity.AgentEntity;
+import xiaozhi.modules.model.dao.ModelConfigDao;
+import xiaozhi.modules.model.dto.LlmModelBasicInfoDTO;
+import xiaozhi.modules.model.dto.ModelBasicInfoDTO;
+import xiaozhi.modules.model.dto.ModelConfigBodyDTO;
+import xiaozhi.modules.model.dto.ModelConfigDTO;
+import xiaozhi.modules.model.dto.ModelProviderDTO;
+import xiaozhi.modules.model.entity.ModelConfigEntity;
+import xiaozhi.modules.model.service.ModelConfigService;
+import xiaozhi.modules.model.service.ModelProviderService;
+import lombok.extern.slf4j.Slf4j;
+
+@Service
+@AllArgsConstructor
+@Slf4j
+public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, ModelConfigEntity>
+ implements ModelConfigService {
+
+ private final ModelConfigDao modelConfigDao;
+ private final ModelProviderService modelProviderService;
+ private final RedisUtils redisUtils;
+ private final AgentDao agentDao;
+
+ @Override
+ public List<ModelBasicInfoDTO> getModelCodeList(String modelType, String modelName) {
+ List<ModelConfigEntity> entities = modelConfigDao.selectList(
+ new QueryWrapper<ModelConfigEntity>()
+ .eq("model_type", modelType)
+ .eq("is_enabled", 1)
+ .like(StringUtils.isNotBlank(modelName), "model_name", modelName)
+ .select("id", "model_name")
+ .orderByAsc("sort"));
+ return ConvertUtils.sourceToTarget(entities, ModelBasicInfoDTO.class);
+ }
+
+ @Override
+ public List<LlmModelBasicInfoDTO> getLlmModelCodeList(String modelName) {
+ List<ModelConfigEntity> entities = modelConfigDao.selectList(
+ new QueryWrapper<ModelConfigEntity>()
+ .eq("model_type", "llm")
+ .eq("is_enabled", 1)
+ .like(StringUtils.isNotBlank(modelName), "model_name", modelName)
+ .select("id", "model_name", "config_json"));
+
+ return entities.stream().map(item -> {
+ LlmModelBasicInfoDTO dto = new LlmModelBasicInfoDTO();
+ dto.setId(item.getId());
+ dto.setModelName(item.getModelName());
+ String type = item.getConfigJson().getOrDefault("type", "").toString();
+ dto.setType(type);
+ return dto;
+ }).toList();
+ }
+
+ @Override
+ public PageData<ModelConfigDTO> getPageList(String modelType, String modelName, String page, String limit) {
+ Map<String, Object> params = new HashMap<>();
+ params.put(Constant.PAGE, page);
+ params.put(Constant.LIMIT, limit);
+
+ long curPage = Long.parseLong(page);
+ long pageSize = Long.parseLong(limit);
+ Page<ModelConfigEntity> pageInfo = new Page<>(curPage, pageSize);
+
+ // 娣诲姞鎺掑簭瑙勫垯锛氬厛鎸塱s_enabled闄嶅簭锛屽啀鎸塻ort鍗囧簭
+ pageInfo.addOrder(OrderItem.desc("is_enabled"), OrderItem.asc("sort"));
+
+ IPage<ModelConfigEntity> modelConfigEntityIPage = modelConfigDao.selectPage(
+ pageInfo,
+ new QueryWrapper<ModelConfigEntity>()
+ .eq("model_type", modelType)
+ .like(StringUtils.isNotBlank(modelName), "model_name", modelName));
+
+ return getPageData(modelConfigEntityIPage, ModelConfigDTO.class);
+ }
+
+ @Override
+ public ModelConfigDTO edit(String modelType, String provideCode, String id, ModelConfigBodyDTO modelConfigBodyDTO) {
+ // 1. 鍙傛暟楠岃瘉
+ validateEditParameters(modelType, provideCode, id, modelConfigBodyDTO);
+
+ // 2. 楠岃瘉妯″瀷鎻愪緵鑰�
+ validateModelProvider(modelType, provideCode);
+
+ // 3. 鑾峰彇鍘熷閰嶇疆锛堜笉缁忚繃鏁忔劅鏁版嵁澶勭悊锛�
+ ModelConfigEntity originalEntity = getOriginalConfigFromDb(id);
+
+ // 4. 楠岃瘉LLM閰嶇疆
+ validateLlmConfiguration(modelConfigBodyDTO);
+
+ // 5. 鍑嗗鏇存柊瀹炰綋骞跺鐞嗘晱鎰熸暟鎹�
+ ModelConfigEntity modelConfigEntity = prepareUpdateEntity(modelConfigBodyDTO, originalEntity, modelType, id);
+
+ // 6. 鎵ц鏁版嵁搴撴洿鏂�
+ modelConfigDao.updateById(modelConfigEntity);
+
+ // 7. 娓呴櫎缂撳瓨
+ clearModelCache(id);
+
+ // 8. 杩斿洖澶勭悊鍚庣殑鏁版嵁锛堝寘鍚晱鎰熸暟鎹帺鐮侊級
+ return buildResponseDTO(modelConfigEntity);
+ }
+
+ @Override
+ public ModelConfigDTO add(String modelType, String provideCode, ModelConfigBodyDTO modelConfigBodyDTO) {
+ validateAddParameters(modelType, provideCode, modelConfigBodyDTO);
+
+ validateModelProvider(modelType, provideCode);
+
+ ModelConfigEntity modelConfigEntity = prepareAddEntity(modelConfigBodyDTO, modelType);
+
+ modelConfigDao.insert(modelConfigEntity);
+
+ return buildResponseDTO(modelConfigEntity);
+ }
+
+ @Override
+ public void delete(String id) {
+ if (StringUtils.isBlank(id)) {
+ throw new RenException(ErrorCode.IDENTIFIER_NOT_NULL);
+ }
+
+ ModelConfigEntity modelConfig = modelConfigDao.selectById(id);
+ if (modelConfig != null && modelConfig.getIsDefault() == 1) {
+ throw new RenException(ErrorCode.DEFAULT_MODEL_DELETE_ERROR);
+ }
+
+ checkAgentReference(id);
+ checkIntentConfigReference(id);
+
+ modelConfigDao.deleteById(id);
+
+ clearModelCache(id);
+ }
+
+ @Override
+ public String getModelNameById(String id) {
+ if (StringUtils.isBlank(id)) {
+ return null;
+ }
+
+ String cacheKey = RedisKeys.getModelNameById(id);
+ String cachedName = (String) redisUtils.get(cacheKey);
+ if (StringUtils.isNotBlank(cachedName)) {
+ return cachedName;
+ }
+
+ ModelConfigEntity entity = modelConfigDao.selectById(id);
+ if (entity != null) {
+ String modelName = entity.getModelName();
+ if (StringUtils.isNotBlank(modelName)) {
+ redisUtils.set(cacheKey, modelName);
+ }
+ return modelName;
+ }
+
+ return null;
+ }
+
+ @Override
+ public ModelConfigEntity selectById(Serializable id) {
+ ModelConfigEntity entity = super.selectById(id);
+ if (entity != null && entity.getConfigJson() != null) {
+ entity.setConfigJson(maskSensitiveFields(entity.getConfigJson()));
+ }
+ return entity;
+ }
+
+ @Override
+ protected <D> PageData<D> getPageData(IPage<?> page, Class<D> target) {
+ List<?> records = page.getRecords();
+ if (records != null && !records.isEmpty()) {
+ for (Object record : records) {
+ if (record instanceof ModelConfigEntity) {
+ ModelConfigEntity entity = (ModelConfigEntity) record;
+ if (entity.getConfigJson() != null) {
+ entity.setConfigJson(maskSensitiveFields(entity.getConfigJson()));
+ }
+ }
+ }
+ }
+ return super.getPageData(page, target);
+ }
+
+ @Override
+ public ModelConfigEntity getModelByIdFromCache(String id) {
+ if (StringUtils.isBlank(id)) {
+ return null;
+ }
+ String cacheKey = RedisKeys.getModelConfigById(id);
+ ModelConfigEntity entity = (ModelConfigEntity) redisUtils.get(cacheKey);
+ if (entity == null) {
+ entity = modelConfigDao.selectById(id);
+ if (entity != null) {
+ redisUtils.set(cacheKey, entity);
+ }
+ }
+ return entity;
+ }
+
+ /**
+ * 楠岃瘉缂栬緫鍙傛暟
+ */
+ private void validateEditParameters(String modelType, String provideCode, String id,
+ ModelConfigBodyDTO modelConfigBodyDTO) {
+ if (StringUtils.isBlank(modelType) || StringUtils.isBlank(provideCode)) {
+ throw new RenException(ErrorCode.MODEL_TYPE_PROVIDE_CODE_NOT_NULL);
+ }
+ if (StringUtils.isBlank(id)) {
+ throw new RenException(ErrorCode.IDENTIFIER_NOT_NULL);
+ }
+ if (modelConfigBodyDTO == null) {
+ throw new RenException(ErrorCode.PARAMS_GET_ERROR);
+ }
+ }
+
+ /**
+ * 楠岃瘉娣诲姞鍙傛暟
+ */
+ private void validateAddParameters(String modelType, String provideCode, ModelConfigBodyDTO modelConfigBodyDTO) {
+ if (StringUtils.isBlank(modelType) || StringUtils.isBlank(provideCode)) {
+ throw new RenException(ErrorCode.MODEL_TYPE_PROVIDE_CODE_NOT_NULL);
+ }
+ if (modelConfigBodyDTO == null) {
+ throw new RenException(ErrorCode.PARAMS_GET_ERROR);
+ }
+ if (StringUtils.isBlank(modelConfigBodyDTO.getId())) {
+ // 鍙傜収 MP @TableId AutoUUID 绛栫暐浣跨敤
+ // com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator(UUID.replace("-",""))
+ // 杩涜鍒嗛厤榛樿妯″瀷ID
+ modelConfigBodyDTO.setId(DefaultIdentifierGenerator.getInstance().nextUUID(ModelConfigEntity.class));
+ }
+ }
+
+ /**
+ * 璁剧疆榛樿妯″瀷
+ */
+ @Override
+ public void setDefaultModel(String modelType, int isDefault) {
+ // 鍙傛暟楠岃瘉
+ if (StringUtils.isBlank(modelType)) {
+ throw new RenException(ErrorCode.MODEL_TYPE_PROVIDE_CODE_NOT_NULL);
+ }
+
+ ModelConfigEntity entity = new ModelConfigEntity();
+ entity.setIsDefault(isDefault);
+ modelConfigDao.update(entity, new QueryWrapper<ModelConfigEntity>()
+ .eq("model_type", modelType));
+
+ // 娓呴櫎鐩稿叧缂撳瓨
+ clearModelCacheByType(modelType);
+ }
+
+ /**
+ * 楠岃瘉妯″瀷鎻愪緵鑰�
+ */
+ private void validateModelProvider(String modelType, String provideCode) {
+ List<ModelProviderDTO> providerList = modelProviderService.getList(modelType, provideCode);
+ if (CollectionUtil.isEmpty(providerList)) {
+ throw new RenException(ErrorCode.MODEL_PROVIDER_NOT_EXIST);
+ }
+ }
+
+ /**
+ * 浠庢暟鎹簱鑾峰彇鍘熷閰嶇疆锛堜笉缁忚繃鏁忔劅鏁版嵁澶勭悊锛�
+ */
+ private ModelConfigEntity getOriginalConfigFromDb(String id) {
+ ModelConfigEntity originalEntity = modelConfigDao.selectById(id);
+ if (originalEntity == null) {
+ throw new RenException(ErrorCode.RESOURCE_NOT_FOUND);
+ }
+ return originalEntity;
+ }
+
+ /**
+ * 楠岃瘉LLM閰嶇疆
+ */
+ private void validateLlmConfiguration(ModelConfigBodyDTO modelConfigBodyDTO) {
+ if (modelConfigBodyDTO.getConfigJson() != null && modelConfigBodyDTO.getConfigJson().containsKey("llm")) {
+ String llm = modelConfigBodyDTO.getConfigJson().get("llm").toString();
+ ModelConfigEntity modelConfigEntity = modelConfigDao.selectOne(new LambdaQueryWrapper<ModelConfigEntity>()
+ .eq(ModelConfigEntity::getId, llm));
+
+ if (modelConfigEntity == null) {
+ throw new RenException(ErrorCode.LLM_NOT_EXIST);
+ }
+
+ String modelType = modelConfigEntity.getModelType();
+ if (modelType == null || !"LLM".equals(modelType.toUpperCase())) {
+ throw new RenException(ErrorCode.LLM_NOT_EXIST);
+ }
+
+ // 楠岃瘉LLM绫诲瀷
+ JSONObject configJson = modelConfigEntity.getConfigJson();
+ if (configJson != null && configJson.containsKey("type")) {
+ String type = configJson.get("type").toString();
+ if (!"openai".equals(type) && !"ollama".equals(type)) {
+ throw new RenException(ErrorCode.INVALID_LLM_TYPE);
+ }
+ }
+ }
+ }
+
+ /**
+ * 鍑嗗鏇存柊瀹炰綋锛屽鐞嗘晱鎰熸暟鎹�
+ */
+ private ModelConfigEntity prepareUpdateEntity(ModelConfigBodyDTO modelConfigBodyDTO,
+ ModelConfigEntity originalEntity,
+ String modelType,
+ String id) {
+ // 1. 澶嶅埗鍘熷瀹炰綋锛屼繚鐣欐墍鏈夊師濮嬫暟鎹紙鍖呮嫭鏁忔劅淇℃伅锛�
+ ModelConfigEntity modelConfigEntity = ConvertUtils.sourceToTarget(originalEntity, ModelConfigEntity.class);
+ modelConfigEntity.setId(id);
+ modelConfigEntity.setModelType(modelType);
+
+ // 2. 鍙洿鏂伴潪鏁忔劅瀛楁
+ modelConfigEntity.setModelName(modelConfigBodyDTO.getModelName());
+ modelConfigEntity.setSort(modelConfigBodyDTO.getSort());
+ modelConfigEntity.setIsEnabled(modelConfigBodyDTO.getIsEnabled());
+ modelConfigEntity.setRemark(modelConfigBodyDTO.getRemark());
+ // 3. 澶勭悊閰嶇疆JSON锛屼粎鏇存柊闈炴晱鎰熷瓧娈靛拰鏄庣‘淇敼鐨勬晱鎰熷瓧娈�
+ if (modelConfigBodyDTO.getConfigJson() != null && originalEntity.getConfigJson() != null) {
+ JSONObject originalJson = originalEntity.getConfigJson();
+ JSONObject updatedJson = new JSONObject(originalJson); // 鍩轰簬鍘熷JSON杩涜淇敼
+
+ // 閬嶅巻鏇存柊鐨凧SON锛屽彧鏇存柊闈炴晱鎰熷瓧娈垫垨纭疄琚慨鏀圭殑鏁忔劅瀛楁
+ for (String key : modelConfigBodyDTO.getConfigJson().keySet()) {
+ Object value = modelConfigBodyDTO.getConfigJson().get(key);
+
+ // 濡傛灉鏄晱鎰熷瓧娈碉紝闇�瑕佺‘璁ゆ槸鍚︾湡鐨勮淇敼锛堝墠绔紶鍏ョ殑鍙兘鏄帺鐮佸悗鐨勫�硷級
+ if (SensitiveDataUtils.isSensitiveField(key)) {
+
+ if (value instanceof String && !SensitiveDataUtils.isMaskedValue((String) value)) {
+ updatedJson.set(key, value);
+ }
+ } else if (value instanceof JSONObject) {
+ // 閫掑綊澶勭悊宓屽JSON
+ mergeJson(updatedJson, key, (JSONObject) value);
+ } else {
+ // 闈炴晱鎰熷瓧娈电洿鎺ユ洿鏂�
+ updatedJson.set(key, value);
+ }
+ }
+
+ // 鍒犻櫎鍦ㄦ柊JSON涓笉瀛樺湪鐨勯潪鏁忔劅瀛楁
+ for (String oldKey : originalJson.keySet().toArray(new String[0])) {
+ if (!modelConfigBodyDTO.getConfigJson().containsKey(oldKey)
+ && !SensitiveDataUtils.isSensitiveField(oldKey)) {
+ updatedJson.remove(oldKey);
+ }
+ }
+
+ modelConfigEntity.setConfigJson(updatedJson);
+ }
+
+ return modelConfigEntity;
+ }
+
+ // 杈呭姪鏂规硶锛氬垽鏂�兼槸鍚︽槸鎺╃爜鏍煎紡
+ private boolean isMaskedValue(String value) {
+ if (value == null)
+ return false;
+ // 绠�鍗曞垽鏂槸鍚﹀寘鍚帺鐮佺殑鐗瑰緛锛�***锛�
+ return value.contains("***");
+ }
+
+ // 杈呭姪鏂规硶锛氶�掑綊鍚堝苟 JSON锛屼繚鐣欏師濮嬫晱鎰熷瓧娈�
+ private void mergeJson(JSONObject original, String key, JSONObject updated) {
+ // 绌哄�兼鏌�
+ if (original == null || updated == null) {
+ log.warn("mergeJson: original 鎴� updated 涓� null");
+ return;
+ }
+
+ // 濡傛灉 original 涓笉瀛樺湪 key锛屽垱寤轰竴涓柊鐨� JSON 瀵硅薄
+ if (!original.containsKey(key)) {
+ original.set(key, new JSONObject());
+ }
+
+ // 鑾峰彇 original 涓殑瀛愬璞�
+ Object originalValue = original.get(key);
+ JSONObject originalChild;
+
+ // 妫�鏌� originalValue 鏄惁鏄� JSONObject 绫诲瀷
+ if (originalValue instanceof JSONObject) {
+ originalChild = (JSONObject) originalValue;
+ } else {
+ // 濡傛灉涓嶆槸 JSONObject 绫诲瀷锛岃褰曡鍛婂苟鍒涘缓鏂扮殑 JSON 瀵硅薄
+ log.warn("mergeJson: key '{}' 鐨勫�间笉鏄� JSONObject 绫诲瀷 (瀹為檯绫诲瀷锛歿})锛屽皢鍒涘缓鏂板璞�",
+ key, originalValue != null ? originalValue.getClass().getSimpleName() : "null");
+ originalChild = new JSONObject();
+ original.set(key, originalChild);
+ }
+
+ for (String childKey : updated.keySet()) {
+ Object childValue = updated.get(childKey);
+ if (childValue instanceof JSONObject) {
+ mergeJson(originalChild, childKey, (JSONObject) childValue);
+ } else {
+ if (!SensitiveDataUtils.isSensitiveField(childKey) ||
+ (childValue instanceof String && !isMaskedValue((String) childValue))) {
+ originalChild.set(childKey, childValue);
+ }
+ }
+ }
+
+ // 鍒犻櫎鍦ㄦ柊 JSON 涓笉瀛樺湪鐨勯潪鏁忔劅瀛愬瓧娈�
+ for (String oldChildKey : originalChild.keySet().toArray(new String[0])) {
+ if (!updated.containsKey(oldChildKey) && !SensitiveDataUtils.isSensitiveField(oldChildKey)) {
+ originalChild.remove(oldChildKey);
+ }
+ }
+ }
+
+ /**
+ * 鍑嗗鏂板瀹炰綋
+ */
+ private ModelConfigEntity prepareAddEntity(ModelConfigBodyDTO modelConfigBodyDTO, String modelType) {
+ ModelConfigEntity modelConfigEntity = ConvertUtils.sourceToTarget(modelConfigBodyDTO, ModelConfigEntity.class);
+ modelConfigEntity.setModelType(modelType);
+ modelConfigEntity.setIsDefault(0);
+ return modelConfigEntity;
+ }
+
+ /**
+ * 鏋勫缓杩斿洖鐨凞TO锛屽鐞嗘晱鎰熸暟鎹�
+ */
+ private ModelConfigDTO buildResponseDTO(ModelConfigEntity entity) {
+ ModelConfigDTO dto = ConvertUtils.sourceToTarget(entity, ModelConfigDTO.class);
+ if (dto.getConfigJson() != null) {
+ dto.setConfigJson(maskSensitiveFields(dto.getConfigJson()));
+ }
+ return dto;
+ }
+
+ /**
+ * 澶勭悊鏁忔劅瀛楁
+ */
+ private JSONObject maskSensitiveFields(JSONObject configJson) {
+ return SensitiveDataUtils.maskSensitiveFields(configJson);
+ }
+
+ /**
+ * 娓呴櫎妯″瀷缂撳瓨
+ */
+ private void clearModelCache(String id) {
+ redisUtils.delete(RedisKeys.getModelConfigById(id));
+ redisUtils.delete(RedisKeys.getModelNameById(id));
+ }
+
+ /**
+ * 鎸夋ā鍨嬬被鍨嬫竻闄ょ紦瀛�
+ */
+ private void clearModelCacheByType(String modelType) {
+ List<ModelConfigEntity> entities = modelConfigDao.selectList(
+ new QueryWrapper<ModelConfigEntity>().eq("model_type", modelType));
+ for (ModelConfigEntity entity : entities) {
+ clearModelCache(entity.getId());
+ }
+ }
+
+ /**
+ * 妫�鏌ユ櫤鑳戒綋閰嶇疆鏄惁鏈夊紩鐢�
+ */
+ private void checkAgentReference(String modelId) {
+ List<AgentEntity> agents = agentDao.selectList(
+ new QueryWrapper<AgentEntity>()
+ .eq("vad_model_id", modelId)
+ .or()
+ .eq("asr_model_id", modelId)
+ .or()
+ .eq("llm_model_id", modelId)
+ .or()
+ .eq("tts_model_id", modelId)
+ .or()
+ .eq("mem_model_id", modelId)
+ .or()
+ .eq("vllm_model_id", modelId)
+ .or()
+ .eq("intent_model_id", modelId));
+ if (!agents.isEmpty()) {
+ String agentNames = agents.stream()
+ .map(AgentEntity::getAgentName)
+ .collect(Collectors.joining("銆�"));
+ throw new RenException(ErrorCode.MODEL_REFERENCED_BY_AGENT, agentNames);
+ }
+ }
+
+ /**
+ * 妫�鏌ユ剰鍥捐瘑鍒厤缃槸鍚︽湁寮曠敤
+ */
+ private void checkIntentConfigReference(String modelId) {
+ ModelConfigEntity modelConfig = modelConfigDao.selectById(modelId);
+ if (modelConfig != null
+ && "LLM".equals(modelConfig.getModelType() == null ? null : modelConfig.getModelType().toUpperCase())) {
+ List<ModelConfigEntity> intentConfigs = modelConfigDao.selectList(
+ new QueryWrapper<ModelConfigEntity>()
+ .eq("model_type", "Intent")
+ .like("config_json", modelId));
+ if (!intentConfigs.isEmpty()) {
+ throw new RenException(ErrorCode.LLM_REFERENCED_BY_INTENT);
+ }
+ }
+ }
+
+ /**
+ * 鑾峰彇绗﹀悎鏉′欢鐨凾TS骞冲彴鍒楄〃
+ */
+ @Override
+ public List<Map<String, Object>> getTtsPlatformList() {
+ return modelConfigDao.getTtsPlatformList();
+ }
+
+ /**
+ * 鏍规嵁妯″瀷绫诲瀷鑾峰彇鎵�鏈夊惎鐢ㄧ殑妯″瀷閰嶇疆
+ */
+ @Override
+ public List<ModelConfigEntity> getEnabledModelsByType(String modelType) {
+ if (StringUtils.isBlank(modelType)) {
+ return null;
+ }
+
+ List<ModelConfigEntity> entities = modelConfigDao.selectList(
+ new QueryWrapper<ModelConfigEntity>()
+ .eq("model_type", modelType)
+ .eq("is_enabled", 1)
+ .orderByAsc("sort"));
+
+ return entities;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelProviderServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelProviderServiceImpl.java
new file mode 100644
index 0000000..1df1bde
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelProviderServiceImpl.java
@@ -0,0 +1,180 @@
+package xiaozhi.modules.model.service.impl;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+
+import cn.hutool.json.JSONArray;
+import lombok.AllArgsConstructor;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.service.impl.BaseServiceImpl;
+import xiaozhi.common.user.UserDetail;
+import xiaozhi.common.utils.ConvertUtils;
+import xiaozhi.modules.knowledge.dao.KnowledgeBaseDao;
+import xiaozhi.modules.knowledge.entity.KnowledgeBaseEntity;
+import xiaozhi.modules.model.dao.ModelProviderDao;
+import xiaozhi.modules.model.dto.ModelProviderDTO;
+import xiaozhi.modules.model.entity.ModelProviderEntity;
+import xiaozhi.modules.model.service.ModelProviderService;
+import xiaozhi.modules.security.user.SecurityUser;
+
+@Service
+@AllArgsConstructor
+public class ModelProviderServiceImpl extends BaseServiceImpl<ModelProviderDao, ModelProviderEntity>
+ implements ModelProviderService {
+
+ private final ModelProviderDao modelProviderDao;
+ private final KnowledgeBaseDao knowledgeBaseDao;
+
+ @Override
+ public List<ModelProviderDTO> getPluginList() {
+ // 1. 鑾峰彇鎻掍欢鍒楄〃
+ LambdaQueryWrapper<ModelProviderEntity> queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(ModelProviderEntity::getModelType, "Plugin");
+ List<ModelProviderEntity> providerEntities = modelProviderDao.selectList(queryWrapper);
+ List<ModelProviderDTO> resultList = ConvertUtils.sourceToTarget(providerEntities, ModelProviderDTO.class);
+
+ // 2. 鑾峰彇褰撳墠鐢ㄦ埛鐨勭煡璇嗗簱鍒楄〃骞惰拷鍔犲埌缁撴灉涓�
+ UserDetail userDetail = SecurityUser.getUser();
+ if (userDetail != null && userDetail.getId() != null) {
+ // 鏌ヨ褰撳墠鐢ㄦ埛鐨勭煡璇嗗簱
+ LambdaQueryWrapper<KnowledgeBaseEntity> kbQueryWrapper = new LambdaQueryWrapper<>();
+ kbQueryWrapper.eq(KnowledgeBaseEntity::getCreator, userDetail.getId());
+ kbQueryWrapper.eq(KnowledgeBaseEntity::getStatus, 1); // 鍙幏鍙栧惎鐢ㄧ姸鎬佺殑鐭ヨ瘑搴�
+ List<KnowledgeBaseEntity> knowledgeBases = knowledgeBaseDao.selectList(kbQueryWrapper);
+
+ // 灏嗙煡璇嗗簱杞崲涓篗odelProviderDTO鏍煎紡骞舵坊鍔犲埌缁撴灉鍒楄〃
+ for (KnowledgeBaseEntity kb : knowledgeBases) {
+ ModelProviderDTO dto = new ModelProviderDTO();
+ dto.setId(kb.getId());
+ dto.setModelType("Rag");
+ dto.setName("[鐭ヨ瘑搴揮" + kb.getName());
+ dto.setProviderCode("ragflow"); // 鍋囪鎵�鏈塕AG閮戒娇鐢╮agflow
+ dto.setFields("[]");
+ dto.setSort(0);
+ dto.setCreateDate(kb.getCreatedAt());
+ dto.setUpdateDate(kb.getUpdatedAt());
+ dto.setCreator(0L);
+ dto.setUpdater(0L);
+ resultList.add(dto);
+ }
+ }
+
+ return resultList;
+ }
+
+ @Override
+ public ModelProviderDTO getById(String id) {
+ ModelProviderEntity entity = modelProviderDao.selectById(id);
+ return ConvertUtils.sourceToTarget(entity, ModelProviderDTO.class);
+ }
+
+ @Override
+ public List<ModelProviderDTO> getPluginListByIds(Collection<String> ids) {
+ LambdaQueryWrapper<ModelProviderEntity> queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.in(ModelProviderEntity::getId, ids);
+ queryWrapper.eq(ModelProviderEntity::getModelType, "Plugin");
+ List<ModelProviderEntity> providerEntities = modelProviderDao.selectList(queryWrapper);
+ return ConvertUtils.sourceToTarget(providerEntities, ModelProviderDTO.class);
+ }
+
+ @Override
+ public List<ModelProviderDTO> getListByModelType(String modelType) {
+
+ QueryWrapper<ModelProviderEntity> queryWrapper = new QueryWrapper<>();
+ queryWrapper.eq("model_type", StringUtils.isBlank(modelType) ? "" : modelType);
+ queryWrapper.orderByAsc("sort");
+ List<ModelProviderEntity> providerEntities = modelProviderDao.selectList(queryWrapper);
+ return ConvertUtils.sourceToTarget(providerEntities, ModelProviderDTO.class);
+ }
+
+ @Override
+ public PageData<ModelProviderDTO> getListPage(ModelProviderDTO modelProviderDTO, String page, String limit) {
+
+ Map<String, Object> params = new HashMap<String, Object>();
+ params.put(Constant.PAGE, page);
+ params.put(Constant.LIMIT, limit);
+ params.put(Constant.ORDER_FIELD, List.of("model_type", "sort"));
+ params.put(Constant.ORDER, "asc");
+
+ IPage<ModelProviderEntity> pageParam = getPage(params, null, true);
+
+ QueryWrapper<ModelProviderEntity> wrapper = new QueryWrapper<ModelProviderEntity>();
+
+ if (StringUtils.isNotBlank(modelProviderDTO.getModelType())) {
+ wrapper.eq("model_type", modelProviderDTO.getModelType());
+ }
+
+ if (StringUtils.isNotBlank(modelProviderDTO.getName())) {
+ wrapper.and(w -> w.like("name", modelProviderDTO.getName())
+ .or()
+ .like("provider_code", modelProviderDTO.getName()));
+ }
+ return getPageData(modelProviderDao.selectPage(pageParam, wrapper), ModelProviderDTO.class);
+ }
+
+ @Override
+ public ModelProviderDTO add(ModelProviderDTO modelProviderDTO) {
+ UserDetail user = SecurityUser.getUser();
+ modelProviderDTO.setCreator(user.getId());
+ modelProviderDTO.setUpdater(user.getId());
+ modelProviderDTO.setCreateDate(new Date());
+ modelProviderDTO.setUpdateDate(new Date());
+ // 鍘婚櫎Fields宸﹀彸鐨勫弻寮曞彿
+
+ modelProviderDTO.setFields(modelProviderDTO.getFields());
+ ModelProviderEntity entity = ConvertUtils.sourceToTarget(modelProviderDTO, ModelProviderEntity.class);
+ if (modelProviderDao.insert(entity) == 0) {
+ throw new RenException(ErrorCode.ADD_DATA_FAILED);
+ }
+
+ return ConvertUtils.sourceToTarget(modelProviderDTO, ModelProviderDTO.class);
+ }
+
+ @Override
+ public ModelProviderDTO edit(ModelProviderDTO modelProviderDTO) {
+ UserDetail user = SecurityUser.getUser();
+ modelProviderDTO.setUpdater(user.getId());
+ modelProviderDTO.setUpdateDate(new Date());
+ if (modelProviderDao
+ .updateById(ConvertUtils.sourceToTarget(modelProviderDTO, ModelProviderEntity.class)) == 0) {
+ throw new RenException(ErrorCode.UPDATE_DATA_FAILED);
+ }
+ return ConvertUtils.sourceToTarget(modelProviderDTO, ModelProviderDTO.class);
+ }
+
+ @Override
+ public void delete(String id) {
+ if (modelProviderDao.deleteById(id) == 0) {
+ throw new RenException(ErrorCode.DELETE_DATA_FAILED);
+ }
+ }
+
+ @Override
+ public void delete(List<String> ids) {
+ if (modelProviderDao.deleteByIds(ids) == 0) {
+ throw new RenException(ErrorCode.DELETE_DATA_FAILED);
+ }
+ }
+
+ @Override
+ public List<ModelProviderDTO> getList(String modelType, String providerCode) {
+ QueryWrapper<ModelProviderEntity> queryWrapper = new QueryWrapper<>();
+ queryWrapper.eq("model_type", StringUtils.isBlank(modelType) ? "" : modelType);
+ queryWrapper.eq("provider_code", StringUtils.isBlank(providerCode) ? "" : providerCode);
+ List<ModelProviderEntity> providerEntities = modelProviderDao.selectList(queryWrapper);
+ return ConvertUtils.sourceToTarget(providerEntities, ModelProviderDTO.class);
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/config/FilterConfig.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/config/FilterConfig.java
new file mode 100644
index 0000000..d74ed08
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/config/FilterConfig.java
@@ -0,0 +1,27 @@
+package xiaozhi.modules.security.config;
+
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.filter.DelegatingFilterProxy;
+
+/**
+ * Filter閰嶇疆
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Configuration
+public class FilterConfig {
+
+ @Bean
+ public FilterRegistrationBean<DelegatingFilterProxy> shiroFilterRegistration() {
+ FilterRegistrationBean<DelegatingFilterProxy> registration = new FilterRegistrationBean<>();
+ registration.setFilter(new DelegatingFilterProxy("shiroFilter"));
+ // 璇ュ�肩己鐪佷负false锛岃〃绀虹敓鍛藉懆鏈熺敱SpringApplicationContext绠$悊锛岃缃负true鍒欒〃绀虹敱ServletContainer绠$悊
+ registration.addInitParameter("targetFilterLifecycle", "true");
+ registration.setEnabled(true);
+ registration.setOrder(Integer.MAX_VALUE - 1);
+ registration.addUrlPatterns("/*");
+ return registration;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java
new file mode 100644
index 0000000..9e7c638
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java
@@ -0,0 +1,120 @@
+package xiaozhi.modules.security.config;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.shiro.session.mgt.SessionManager;
+import org.apache.shiro.spring.LifecycleBeanPostProcessor;
+import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
+import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
+import org.apache.shiro.web.config.ShiroFilterConfiguration;
+import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
+import org.apache.shiro.web.mgt.WebSecurityManager;
+import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.context.annotation.Role;
+
+import jakarta.servlet.Filter;
+import xiaozhi.modules.security.oauth2.Oauth2Filter;
+import xiaozhi.modules.security.oauth2.Oauth2Realm;
+import xiaozhi.modules.security.secret.ServerSecretFilter;
+import xiaozhi.modules.sys.service.SysParamsService;
+
+/**
+ * Shiro鐨勯厤缃枃浠�
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Configuration
+public class ShiroConfig {
+
+ @Bean
+ public DefaultWebSessionManager sessionManager() {
+ DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
+ sessionManager.setSessionValidationSchedulerEnabled(false);
+ sessionManager.setSessionIdUrlRewritingEnabled(false);
+
+ return sessionManager;
+ }
+
+ @Bean("securityManager")
+ public WebSecurityManager securityManager(Oauth2Realm oAuth2Realm, SessionManager sessionManager) {
+ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
+ securityManager.setRealm(oAuth2Realm);
+ securityManager.setSessionManager(sessionManager);
+ securityManager.setRememberMeManager(null);
+ return securityManager;
+ }
+
+ @Bean("shiroFilter")
+ public static ShiroFilterFactoryBean shirFilter(@Lazy WebSecurityManager securityManager,
+ @Lazy SysParamsService sysParamsService) {
+ ShiroFilterConfiguration config = new ShiroFilterConfiguration();
+ config.setFilterOncePerRequest(true);
+
+ ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
+ shiroFilter.setSecurityManager(securityManager);
+ shiroFilter.setShiroFilterConfiguration(config);
+
+ Map<String, Filter> filters = new HashMap<>();
+ // oauth杩囨护
+ filters.put("oauth2", new Oauth2Filter());
+ // 鏈嶅姟瀵嗛挜杩囨护
+ filters.put("server", new ServerSecretFilter(sysParamsService));
+ shiroFilter.setFilters(filters);
+
+ // 娣诲姞Shiro鐨勫唴缃繃婊ゅ櫒
+ /*
+ * anon锛氭棤闇�璁よ瘉灏卞彲浠ヨ闂�
+ * authc锛氬繀椤昏璇佷簡鎵嶈兘璁╅棶
+ * user锛氬繀椤绘嫢鏈夛紝璁颁綇鎴戝姛鑳斤紝鎵嶈兘璁块棶
+ * perms锛氭嫢鏈夊鏌愪釜璧勬簮鐨勬潈闄愭墠鑳借闂�
+ * role锛氭嫢鏈夋煇涓鑹叉潈闄愭墠鑳借闂�
+ */
+ Map<String, String> filterMap = new LinkedHashMap<>();
+ filterMap.put("/ota/**", "anon");
+ filterMap.put("/otaMag/download/**", "anon");
+ filterMap.put("/webjars/**", "anon");
+ filterMap.put("/druid/**", "anon");
+ filterMap.put("/v3/api-docs/**", "anon");
+ filterMap.put("/doc.html", "anon");
+ filterMap.put("/favicon.ico", "anon");
+ filterMap.put("/user/captcha", "anon");
+ filterMap.put("/user/smsVerification", "anon");
+ filterMap.put("/user/login", "anon");
+ filterMap.put("/user/pub-config", "anon");
+ filterMap.put("/user/register", "anon");
+ filterMap.put("/user/retrieve-password", "anon");
+ // 灏哻onfig璺緞浣跨敤server鏈嶅姟杩囨护鍣�
+ filterMap.put("/config/**", "server");
+ filterMap.put("/device/address-book/call", "server");
+ filterMap.put("/agent/chat-history/report", "server");
+ filterMap.put("/agent/chat-history/download/**", "anon");
+ filterMap.put("/agent/chat-summary/**", "server");
+ filterMap.put("/agent/chat-title/**", "server");
+ filterMap.put("/agent/play/**", "anon");
+ filterMap.put("/voiceClone/play/**", "anon");
+ filterMap.put("/**", "oauth2");
+ shiroFilter.setFilterChainDefinitionMap(filterMap);
+
+ return shiroFilter;
+ }
+
+ @Bean("lifecycleBeanPostProcessor")
+ public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
+ return new LifecycleBeanPostProcessor();
+ }
+
+ @Bean
+ @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+ public static AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
+ @Lazy WebSecurityManager securityManager) {
+ AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
+ advisor.setSecurityManager(securityManager);
+ return advisor;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/config/WebMvcConfig.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/config/WebMvcConfig.java
new file mode 100644
index 0000000..8004eb9
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/config/WebMvcConfig.java
@@ -0,0 +1,156 @@
+package xiaozhi.modules.security.config;
+
+import java.text.SimpleDateFormat;
+import java.util.List;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.converter.ByteArrayHttpMessageConverter;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.ResourceHttpMessageConverter;
+import org.springframework.http.converter.StringHttpMessageConverter;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
+import org.springframework.web.servlet.LocaleResolver;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
+
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
+
+import xiaozhi.common.utils.DateUtils;
+
+@Configuration
+public class WebMvcConfig implements WebMvcConfigurer {
+
+ @Override
+ public void addCorsMappings(CorsRegistry registry) {
+ registry.addMapping("/**")
+ .allowedOriginPatterns("*")
+ .allowCredentials(true)
+ .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
+ .maxAge(3600);
+ }
+
+ @Override
+ public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
+ // 鐗规畩鐢ㄩ�旂殑杞崲鍣�
+ converters.add(new ByteArrayHttpMessageConverter());
+ converters.add(new ResourceHttpMessageConverter());
+
+ // 閫氱敤杞崲鍣�
+ converters.add(new StringHttpMessageConverter());
+ converters.add(new AllEncompassingFormHttpMessageConverter());
+
+ // JSON 杞崲鍣�
+ converters.add(jackson2HttpMessageConverter());
+ }
+
+ @Bean
+ public MappingJackson2HttpMessageConverter jackson2HttpMessageConverter() {
+ MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
+ ObjectMapper mapper = new ObjectMapper();
+
+ // 蹇界暐鏈煡灞炴��
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+
+ // 璁剧疆鏃跺尯
+ mapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
+
+ // 閰嶇疆Java8鏃ユ湡鏃堕棿搴忓垪鍖�
+ JavaTimeModule javaTimeModule = new JavaTimeModule();
+ javaTimeModule.addSerializer(java.time.LocalDateTime.class, new LocalDateTimeSerializer(
+ java.time.format.DateTimeFormatter.ofPattern(DateUtils.DATE_TIME_PATTERN)));
+ javaTimeModule.addSerializer(java.time.LocalDate.class, new LocalDateSerializer(
+ java.time.format.DateTimeFormatter.ofPattern(DateUtils.DATE_PATTERN)));
+ javaTimeModule.addSerializer(java.time.LocalTime.class,
+ new LocalTimeSerializer(java.time.format.DateTimeFormatter.ofPattern("HH:mm:ss")));
+ javaTimeModule.addDeserializer(java.time.LocalDateTime.class, new LocalDateTimeDeserializer(
+ java.time.format.DateTimeFormatter.ofPattern(DateUtils.DATE_TIME_PATTERN)));
+ javaTimeModule.addDeserializer(java.time.LocalDate.class, new LocalDateDeserializer(
+ java.time.format.DateTimeFormatter.ofPattern(DateUtils.DATE_PATTERN)));
+ javaTimeModule.addDeserializer(java.time.LocalTime.class,
+ new LocalTimeDeserializer(java.time.format.DateTimeFormatter.ofPattern("HH:mm:ss")));
+ mapper.registerModule(javaTimeModule);
+
+ // 閰嶇疆java.util.Date鐨勫簭鍒楀寲鍜屽弽搴忓垪鍖�
+ SimpleDateFormat dateFormat = new SimpleDateFormat(DateUtils.DATE_TIME_PATTERN);
+ mapper.setDateFormat(dateFormat);
+
+ // Long绫诲瀷杞琒tring绫诲瀷
+ SimpleModule simpleModule = new SimpleModule();
+ simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
+ simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
+ mapper.registerModule(simpleModule);
+
+ converter.setObjectMapper(mapper);
+ return converter;
+ }
+
+ /**
+ * 鍥介檯鍖栭厤缃� - 鏍规嵁璇锋眰澶翠腑鐨凙ccept-Language璁剧疆璇█鐜
+ */
+ @Bean
+ public LocaleResolver localeResolver() {
+ return new AcceptHeaderLocaleResolver() {
+ @Override
+ public Locale resolveLocale(HttpServletRequest request) {
+ String acceptLanguage = request.getHeader("Accept-Language");
+ if (acceptLanguage == null || acceptLanguage.isEmpty()) {
+ return Locale.getDefault();
+ }
+
+ // 瑙f瀽Accept-Language璇锋眰澶翠腑鐨勯閫夎瑷�
+ String[] languages = acceptLanguage.split(",");
+ if (languages.length > 0) {
+ // 鎻愬彇绗竴涓瑷�浠g爜锛屽幓闄ゅ彲鑳界殑璐ㄩ噺鍊�(q=...)
+ String[] parts = languages[0].split(";" + "\\s*");
+ String primaryLanguage = parts[0].trim();
+
+ // 鏍规嵁鍓嶇鍙戦�佺殑璇█浠g爜鐩存帴鍒涘缓Locale瀵硅薄
+ if (primaryLanguage.equals("zh-CN")) {
+ return Locale.SIMPLIFIED_CHINESE;
+ } else if (primaryLanguage.equals("zh-TW")) {
+ return Locale.TRADITIONAL_CHINESE;
+ } else if (primaryLanguage.equals("en-US")) {
+ return Locale.US;
+ } else if (primaryLanguage.equals("de-DE")) {
+ return Locale.GERMANY;
+ } else if (primaryLanguage.equals("vi-VN")) {
+ return Locale.forLanguageTag("vi-VN");
+ } else if (primaryLanguage.startsWith("zh")) {
+ // 瀵逛簬鍏朵粬涓枃鍙樹綋锛岄粯璁や娇鐢ㄧ畝浣撲腑鏂�
+ return Locale.SIMPLIFIED_CHINESE;
+ } else if (primaryLanguage.startsWith("en")) {
+ // 瀵逛簬鍏朵粬鑻辨枃鍙樹綋锛岄粯璁や娇鐢ㄧ編寮忚嫳璇�
+ return Locale.US;
+ } else if (primaryLanguage.startsWith("de")) {
+ // 瀵逛簬鍏朵粬寰疯鍙樹綋锛岄粯璁や娇鐢ㄥ痉璇�
+ return Locale.GERMANY;
+ } else if (primaryLanguage.startsWith("vi")) {
+ // 瀵逛簬鍏朵粬瓒婂崡璇彉浣擄紝榛樿浣跨敤瓒婂崡璇�
+ return Locale.forLanguageTag("vi-VN");
+ }
+ }
+
+ // 濡傛灉娌℃湁鍖归厤鐨勮瑷�锛屼娇鐢ㄩ粯璁よ瑷�
+ return Locale.getDefault();
+ }
+ };
+ }
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java
new file mode 100644
index 0000000..1ab9809
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java
@@ -0,0 +1,247 @@
+package xiaozhi.modules.security.controller;
+
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.page.TokenDTO;
+import xiaozhi.common.user.UserDetail;
+import xiaozhi.common.utils.JsonUtils;
+import xiaozhi.common.utils.Result;
+import xiaozhi.common.utils.Sm2DecryptUtil;
+import xiaozhi.common.validator.AssertUtils;
+import xiaozhi.common.validator.ValidatorUtils;
+import xiaozhi.modules.security.dto.LoginDTO;
+import xiaozhi.modules.security.dto.SmsVerificationDTO;
+import xiaozhi.modules.security.password.PasswordUtils;
+import xiaozhi.modules.security.service.CaptchaService;
+import xiaozhi.modules.security.service.SysUserTokenService;
+import xiaozhi.modules.security.user.SecurityUser;
+import xiaozhi.modules.sys.dto.PasswordDTO;
+import xiaozhi.modules.sys.dto.RetrievePasswordDTO;
+import xiaozhi.modules.sys.dto.SysUserDTO;
+import xiaozhi.modules.sys.service.SysDictDataService;
+import xiaozhi.modules.sys.service.SysParamsService;
+import xiaozhi.modules.sys.service.SysUserService;
+import xiaozhi.modules.sys.vo.SysDictDataItem;
+
+/**
+ * 鐧诲綍鎺у埗灞�
+ */
+@Slf4j
+@AllArgsConstructor
+@RestController
+@RequestMapping("/user")
+@Tag(name = "鐧诲綍绠$悊")
+public class LoginController {
+ private final SysUserService sysUserService;
+ private final SysUserTokenService sysUserTokenService;
+ private final CaptchaService captchaService;
+ private final SysParamsService sysParamsService;
+ private final SysDictDataService sysDictDataService;
+
+ @GetMapping("/captcha")
+ @Operation(summary = "楠岃瘉鐮�")
+ public void captcha(HttpServletResponse response, String uuid) throws IOException {
+ // uuid涓嶈兘涓虹┖
+ AssertUtils.isBlank(uuid, ErrorCode.IDENTIFIER_NOT_NULL);
+ // 鐢熸垚楠岃瘉鐮�
+ captchaService.create(response, uuid);
+ }
+
+ @PostMapping("/smsVerification")
+ @Operation(summary = "鐭俊楠岃瘉鐮�")
+ public Result<Void> smsVerification(@RequestBody SmsVerificationDTO dto) {
+ // 楠岃瘉鍥惧舰楠岃瘉鐮�
+ boolean validate = captchaService.validate(dto.getCaptchaId(), dto.getCaptcha(), false);
+ if (!validate) {
+ throw new RenException(ErrorCode.SMS_CAPTCHA_ERROR);
+ }
+
+ Boolean isMobileRegister = sysParamsService
+ .getValueObject(Constant.SysMSMParam.SERVER_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class);
+ if (!isMobileRegister) {
+ throw new RenException(ErrorCode.MOBILE_REGISTER_DISABLED);
+ }
+ // 鍙戦�佺煭淇¢獙璇佺爜
+ captchaService.sendSMSValidateCode(dto.getPhone());
+ return new Result<>();
+ }
+
+ @PostMapping("/login")
+ @Operation(summary = "鐧诲綍")
+ public Result<TokenDTO> login(@RequestBody LoginDTO login) {
+ String password = login.getPassword();
+
+ // 浣跨敤宸ュ叿绫昏В瀵嗗苟楠岃瘉楠岃瘉鐮�
+ String actualPassword = Sm2DecryptUtil.decryptAndValidateCaptcha(
+ password, login.getCaptchaId(), captchaService, sysParamsService);
+
+ login.setPassword(actualPassword);
+
+ // 鎸夌収鐢ㄦ埛鍚嶈幏鍙栫敤鎴�
+ SysUserDTO userDTO = sysUserService.getByUsername(login.getUsername());
+ // 鍒ゆ柇鐢ㄦ埛鏄惁瀛樺湪
+ if (userDTO == null) {
+ throw new RenException(ErrorCode.ACCOUNT_PASSWORD_ERROR);
+ }
+ // 鍒ゆ柇瀵嗙爜鏄惁姝g‘锛屼笉涓�鏍峰垯杩涘叆if
+ if (!PasswordUtils.matches(login.getPassword(), userDTO.getPassword())) {
+ throw new RenException(ErrorCode.ACCOUNT_PASSWORD_ERROR);
+ }
+ return sysUserTokenService.createToken(userDTO.getId());
+ }
+
+ @PostMapping("/register")
+ @Operation(summary = "娉ㄥ唽")
+ public Result<Void> register(@RequestBody LoginDTO login) {
+ if (!sysUserService.getAllowUserRegister()) {
+ throw new RenException(ErrorCode.USER_REGISTER_DISABLED);
+ }
+
+ String password = login.getPassword();
+
+ // 浣跨敤宸ュ叿绫昏В瀵嗗苟楠岃瘉楠岃瘉鐮�
+ String actualPassword = Sm2DecryptUtil.decryptAndValidateCaptcha(
+ password, login.getCaptchaId(), captchaService, sysParamsService);
+
+ login.setPassword(actualPassword);
+
+ // 鏄惁寮�鍚墜鏈烘敞鍐�
+ Boolean isMobileRegister = sysParamsService
+ .getValueObject(Constant.SysMSMParam.SERVER_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class);
+ boolean validate;
+ if (isMobileRegister) {
+ // 楠岃瘉鐢ㄦ埛鏄惁鏄墜鏈哄彿鐮�
+ boolean validPhone = ValidatorUtils.isValidPhone(login.getUsername());
+ if (!validPhone) {
+ throw new RenException(ErrorCode.USERNAME_NOT_PHONE);
+ }
+ // 楠岃瘉鐭俊楠岃瘉鐮佹槸鍚︽甯�
+ validate = captchaService.validateSMSValidateCode(login.getUsername(), login.getMobileCaptcha(), false);
+ if (!validate) {
+ throw new RenException(ErrorCode.SMS_CODE_ERROR);
+ }
+ }
+
+ // 鎸夌収鐢ㄦ埛鍚嶈幏鍙栫敤鎴�
+ SysUserDTO userDTO = sysUserService.getByUsername(login.getUsername());
+ if (userDTO != null) {
+ throw new RenException(ErrorCode.PHONE_ALREADY_REGISTERED);
+ }
+ userDTO = new SysUserDTO();
+ userDTO.setUsername(login.getUsername());
+ userDTO.setPassword(login.getPassword());
+ sysUserService.save(userDTO);
+ return new Result<>();
+ }
+
+ @GetMapping("/info")
+ @Operation(summary = "鐢ㄦ埛淇℃伅鑾峰彇")
+ public Result<UserDetail> info() {
+ UserDetail user = SecurityUser.getUser();
+ Result<UserDetail> result = new Result<>();
+ result.setData(user);
+ return result;
+ }
+
+ @PutMapping("/change-password")
+ @Operation(summary = "淇敼鐢ㄦ埛瀵嗙爜")
+ public Result<?> changePassword(@RequestBody PasswordDTO passwordDTO) {
+ // 鍒ゆ柇闈炵┖
+ ValidatorUtils.validateEntity(passwordDTO);
+ Long userId = SecurityUser.getUserId();
+ sysUserTokenService.changePassword(userId, passwordDTO);
+ return new Result<>();
+ }
+
+ @PutMapping("/retrieve-password")
+ @Operation(summary = "鎵惧洖瀵嗙爜")
+ public Result<?> retrievePassword(@RequestBody RetrievePasswordDTO dto) {
+ // 鏄惁寮�鍚墜鏈烘敞鍐�
+ Boolean isMobileRegister = sysParamsService
+ .getValueObject(Constant.SysMSMParam.SERVER_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class);
+ if (!isMobileRegister) {
+ throw new RenException(ErrorCode.RETRIEVE_PASSWORD_DISABLED);
+ }
+ // 鍒ゆ柇闈炵┖
+ ValidatorUtils.validateEntity(dto);
+ // 楠岃瘉鐢ㄦ埛鏄惁鏄墜鏈哄彿鐮�
+ boolean validPhone = ValidatorUtils.isValidPhone(dto.getPhone());
+ if (!validPhone) {
+ throw new RenException(ErrorCode.PHONE_FORMAT_ERROR);
+ }
+
+ // 鎸夌収鐢ㄦ埛鍚嶈幏鍙栫敤鎴�
+ SysUserDTO userDTO = sysUserService.getByUsername(dto.getPhone());
+ if (userDTO == null) {
+ throw new RenException(ErrorCode.PHONE_NOT_REGISTERED);
+ }
+ // 楠岃瘉鐭俊楠岃瘉鐮佹槸鍚︽甯�
+ boolean validate = captchaService.validateSMSValidateCode(dto.getPhone(), dto.getCode(), false);
+ // 鍒ゆ柇鏄惁閫氳繃楠岃瘉
+ if (!validate) {
+ throw new RenException(ErrorCode.SMS_CODE_ERROR);
+ }
+
+ String password = dto.getPassword();
+
+ // 浣跨敤宸ュ叿绫昏В瀵嗗苟楠岃瘉楠岃瘉鐮�
+ String actualPassword = Sm2DecryptUtil.decryptAndValidateCaptcha(
+ password, dto.getCaptchaId(), captchaService, sysParamsService);
+
+ dto.setPassword(actualPassword);
+
+ sysUserService.changePasswordDirectly(userDTO.getId(), dto.getPassword());
+ return new Result<>();
+ }
+
+ @GetMapping("/pub-config")
+ @Operation(summary = "鍏叡閰嶇疆")
+ public Result<Map<String, Object>> pubConfig() {
+ Map<String, Object> config = new HashMap<>();
+ config.put("enableMobileRegister", sysParamsService
+ .getValueObject(Constant.SysMSMParam.SERVER_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class));
+ config.put("version", Constant.VERSION);
+ config.put("year", "漏" + Calendar.getInstance().get(Calendar.YEAR));
+ config.put("allowUserRegister", sysUserService.getAllowUserRegister());
+ List<SysDictDataItem> list = sysDictDataService.getDictDataByType(Constant.DictType.MOBILE_AREA.getValue());
+ config.put("mobileAreaList", list);
+ config.put("beianIcpNum", sysParamsService.getValue(Constant.SysBaseParam.BEIAN_ICP_NUM.getValue(), true));
+ config.put("beianGaNum", sysParamsService.getValue(Constant.SysBaseParam.BEIAN_GA_NUM.getValue(), true));
+ config.put("name", sysParamsService.getValue(Constant.SysBaseParam.SERVER_NAME.getValue(), true));
+
+ // SM2鍏挜
+ String publicKey = sysParamsService.getValue(Constant.SM2_PUBLIC_KEY, true);
+ if (StringUtils.isBlank(publicKey)) {
+ throw new RenException(ErrorCode.SM2_KEY_NOT_CONFIGURED);
+ }
+ config.put("sm2PublicKey", publicKey);
+
+ // 鑾峰彇system-web.menu鍙傛暟閰嶇疆
+ String menuConfig = sysParamsService.getValue("system-web.menu", true);
+ if (StringUtils.isNotBlank(menuConfig)) {
+ config.put("systemWebMenu", JsonUtils.parseObject(menuConfig, Object.class));
+ }
+
+ return new Result<Map<String, Object>>().ok(config);
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/dao/SysUserTokenDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/dao/SysUserTokenDao.java
new file mode 100644
index 0000000..05f946d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/dao/SysUserTokenDao.java
@@ -0,0 +1,24 @@
+package xiaozhi.modules.security.dao;
+
+import java.util.Date;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import xiaozhi.common.dao.BaseDao;
+import xiaozhi.modules.security.entity.SysUserTokenEntity;
+
+/**
+ * 绯荤粺鐢ㄦ埛Token
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Mapper
+public interface SysUserTokenDao extends BaseDao<SysUserTokenEntity> {
+
+ SysUserTokenEntity getByToken(String token);
+
+ SysUserTokenEntity getByUserId(Long userId);
+
+ void logout(@Param("userId") Long userId, @Param("expireDate") Date expireDate);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/dto/LoginDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/dto/LoginDTO.java
new file mode 100644
index 0000000..df13f23
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/dto/LoginDTO.java
@@ -0,0 +1,31 @@
+package xiaozhi.modules.security.dto;
+
+import java.io.Serializable;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+/**
+ * 鐧诲綍琛ㄥ崟
+ */
+@Data
+@Schema(description = "鐧诲綍琛ㄥ崟")
+public class LoginDTO implements Serializable {
+
+ @Schema(description = "鎵嬫満鍙风爜")
+ @NotBlank(message = "{sysuser.username.require}")
+ private String username;
+
+ @Schema(description = "瀵嗙爜")
+ @NotBlank(message = "{sysuser.password.require}")
+ private String password;
+
+ @Schema(description = "鎵嬫満楠岃瘉鐮�")
+ private String mobileCaptcha;
+
+ @Schema(description = "鍞竴鏍囪瘑")
+ @NotBlank(message = "{sysuser.uuid.require}")
+ private String captchaId;
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/dto/SmsVerificationDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/dto/SmsVerificationDTO.java
new file mode 100644
index 0000000..425ef05
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/dto/SmsVerificationDTO.java
@@ -0,0 +1,28 @@
+package xiaozhi.modules.security.dto;
+
+import java.io.Serializable;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+/**
+ * 鐭俊楠岃瘉鐮佽姹侱TO
+ */
+@Data
+@Schema(description = "鐭俊楠岃瘉鐮佽姹�")
+public class SmsVerificationDTO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "鎵嬫満鍙风爜")
+ @NotBlank(message = "{sysuser.username.require}")
+ private String phone;
+
+ @Schema(description = "楠岃瘉鐮�")
+ @NotBlank(message = "{sysuser.captcha.require}")
+ private String captcha;
+
+ @Schema(description = "鍞竴鏍囪瘑")
+ @NotBlank(message = "{sysuser.uuid.require}")
+ private String captchaId;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/entity/SysUserTokenEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/entity/SysUserTokenEntity.java
new file mode 100644
index 0000000..2346b5e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/entity/SysUserTokenEntity.java
@@ -0,0 +1,47 @@
+package xiaozhi.modules.security.entity;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import lombok.Data;
+
+/**
+ * 绯荤粺鐢ㄦ埛Token
+ */
+@Data
+@TableName("sys_user_token")
+public class SysUserTokenEntity implements Serializable {
+
+ /**
+ * id
+ */
+ @TableId
+ private Long id;
+ /**
+ * 鐢ㄦ埛ID
+ */
+ private Long userId;
+ /**
+ * 鐢ㄦ埛token
+ */
+ private String token;
+ /**
+ * 杩囨湡鏃堕棿
+ */
+ private Date expireDate;
+ /**
+ * 鏇存柊鏃堕棿
+ */
+ private Date updateDate;
+ /**
+ * 鍒涘缓鏃堕棿
+ */
+ @TableField(fill = FieldFill.INSERT)
+ private Date createDate;
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/Oauth2Filter.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/Oauth2Filter.java
new file mode 100644
index 0000000..59d7e09
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/Oauth2Filter.java
@@ -0,0 +1,109 @@
+package xiaozhi.modules.security.oauth2;
+
+import java.io.IOException;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.utils.HttpContextUtils;
+import xiaozhi.common.utils.JsonUtils;
+import xiaozhi.common.utils.MessageUtils;
+import xiaozhi.common.utils.Result;
+
+/**
+ * oauth2杩囨护鍣�
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public class Oauth2Filter extends AuthenticatingFilter {
+
+ private static final Logger logger = LoggerFactory.getLogger(Oauth2Filter.class);
+
+ @Override
+ protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
+ // 鑾峰彇璇锋眰token
+ String token = getRequestToken((HttpServletRequest) request);
+
+ if (StringUtils.isBlank(token)) {
+ logger.warn("createToken:token is empty");
+ return null;
+ }
+
+ return new Oauth2Token(token);
+ }
+
+ @Override
+ protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
+ if (((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
+ // 鑾峰彇璇锋眰token锛屽鏋渢oken涓嶅瓨鍦紝鐩存帴杩斿洖401
+ String token = getRequestToken((HttpServletRequest) request);
+
+ if (StringUtils.isBlank(token)) {
+ logger.warn("onAccessDenied:token is empty");
+
+ HttpServletResponse httpResponse = (HttpServletResponse) response;
+ httpResponse.setContentType("application/json;charset=utf-8");
+ httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
+ httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
+
+ String json = JsonUtils.toJsonString(new Result<Void>().error(ErrorCode.UNAUTHORIZED));
+
+ httpResponse.getWriter().print(json);
+
+ return false;
+ }
+
+ return executeLogin(request, response);
+ }
+
+ @Override
+ protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request,
+ ServletResponse response) {
+ HttpServletResponse httpResponse = (HttpServletResponse) response;
+ httpResponse.setContentType("application/json;charset=utf-8");
+ httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
+ httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
+ try {
+ // 浣跨敤鍥介檯鍖栨秷鎭浛浠g洿鎺ヤ娇鐢ㄥ紓甯告秷鎭�
+ Result<Void> r = new Result<Void>().error(ErrorCode.UNAUTHORIZED);
+
+ String json = JsonUtils.toJsonString(r);
+ httpResponse.getWriter().print(json);
+ } catch (IOException e1) {
+ }
+
+ return false;
+ }
+
+ /**
+ * 鑾峰彇璇锋眰鐨則oken
+ */
+ private String getRequestToken(HttpServletRequest httpRequest) {
+ String token = null;
+ // 浠巋eader涓幏鍙杢oken
+ String authorization = httpRequest.getHeader(Constant.AUTHORIZATION);
+ if (StringUtils.isNotBlank(authorization) && authorization.startsWith("Bearer ")) {
+ token = authorization.replace("Bearer ", "");
+ }
+ return token;
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/Oauth2Realm.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/Oauth2Realm.java
new file mode 100644
index 0000000..27c3bc4
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/Oauth2Realm.java
@@ -0,0 +1,108 @@
+package xiaozhi.modules.security.oauth2;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.DisabledAccountException;
+import org.apache.shiro.authc.IncorrectCredentialsException;
+import org.apache.shiro.authc.LockedAccountException;
+import org.apache.shiro.authc.SimpleAuthenticationInfo;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.SimpleAuthorizationInfo;
+import org.apache.shiro.realm.AuthorizingRealm;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+import jakarta.annotation.Resource;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.user.UserDetail;
+import xiaozhi.common.utils.ConvertUtils;
+import xiaozhi.common.utils.MessageUtils;
+import xiaozhi.modules.security.entity.SysUserTokenEntity;
+import xiaozhi.modules.security.service.ShiroService;
+import xiaozhi.modules.sys.entity.SysUserEntity;
+import xiaozhi.modules.sys.enums.SuperAdminEnum;
+
+/**
+ * 璁よ瘉
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+@Component
+public class Oauth2Realm extends AuthorizingRealm {
+ @Lazy
+ @Resource
+ private ShiroService shiroService;
+
+ private static final Logger logger = LoggerFactory.getLogger(Oauth2Realm.class);
+
+ @Override
+ public boolean supports(AuthenticationToken token) {
+ return token instanceof Oauth2Token;
+ }
+
+ /**
+ * 鎺堟潈(楠岃瘉鏉冮檺鏃惰皟鐢�)
+ */
+ @Override
+ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
+ UserDetail user = (UserDetail) principals.getPrimaryPrincipal();
+
+ // 鐢ㄦ埛鏉冮檺鍒楄〃
+ Set<String> permsSet = new HashSet<>();
+
+ if (user.getSuperAdmin() == SuperAdminEnum.YES.value()) {
+ permsSet.add("sys:role:superAdmin");
+ permsSet.add("sys:role:normal");
+ } else {
+ permsSet.add("sys:role:normal");
+ }
+
+ SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
+ info.setStringPermissions(permsSet);
+ return info;
+ }
+
+ /**
+ * 璁よ瘉(鐧诲綍鏃惰皟鐢�)
+ */
+ @Override
+ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
+ String accessToken = (String) token.getPrincipal();
+
+ // 鏍规嵁accessToken锛屾煡璇㈢敤鎴蜂俊鎭�
+ SysUserTokenEntity tokenEntity = shiroService.getByToken(accessToken);
+ // token澶辨晥
+ if (tokenEntity == null || tokenEntity.getExpireDate().getTime() < System.currentTimeMillis()) {
+ throw new IncorrectCredentialsException(MessageUtils.getMessage(ErrorCode.TOKEN_INVALID));
+ }
+
+ // 鏌ヨ鐢ㄦ埛淇℃伅
+ SysUserEntity userEntity = shiroService.getUser(tokenEntity.getUserId());
+
+ // 杞崲鎴怳serDetail瀵硅薄
+ UserDetail userDetail = ConvertUtils.sourceToTarget(userEntity, UserDetail.class);
+
+ userDetail.setToken(accessToken);
+
+ // 璐﹀彿閿佸畾
+ if (userDetail.getStatus() == null) {
+ logger.error("璐﹀彿鐘舵�佸紓甯革紝status 涓嶈兘涓虹┖");
+ throw new DisabledAccountException(MessageUtils.getMessage(ErrorCode.ACCOUNT_DISABLE));
+ }
+
+ if (userDetail.getStatus() == 0) {
+ throw new LockedAccountException(MessageUtils.getMessage(ErrorCode.ACCOUNT_LOCK));
+ }
+
+ SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(userDetail, accessToken, getName());
+ return info;
+ }
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/Oauth2Token.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/Oauth2Token.java
new file mode 100644
index 0000000..62adc90
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/Oauth2Token.java
@@ -0,0 +1,26 @@
+package xiaozhi.modules.security.oauth2;
+
+import org.apache.shiro.authc.AuthenticationToken;
+
+/**
+ * token
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public class Oauth2Token implements AuthenticationToken {
+ private String token;
+
+ public Oauth2Token(String token) {
+ this.token = token;
+ }
+
+ @Override
+ public String getPrincipal() {
+ return token;
+ }
+
+ @Override
+ public Object getCredentials() {
+ return token;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/TokenGenerator.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/TokenGenerator.java
new file mode 100644
index 0000000..17ea9be
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/TokenGenerator.java
@@ -0,0 +1,45 @@
+package xiaozhi.modules.security.oauth2;
+
+import java.security.MessageDigest;
+import java.util.UUID;
+
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+
+/**
+ * 鐢熸垚token
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public class TokenGenerator {
+
+ public static String generateValue() {
+ return generateValue(UUID.randomUUID().toString());
+ }
+
+ private static final char[] HEX_CODE = "0123456789abcdef".toCharArray();
+
+ public static String toHexString(byte[] data) {
+ if (data == null) {
+ return null;
+ }
+ StringBuilder r = new StringBuilder(data.length * 2);
+ for (byte b : data) {
+ r.append(HEX_CODE[(b >> 4) & 0xF]);
+ r.append(HEX_CODE[(b & 0xF)]);
+ }
+ return r.toString();
+ }
+
+ public static String generateValue(String param) {
+ try {
+ MessageDigest algorithm = MessageDigest.getInstance("MD5");
+ algorithm.reset();
+ algorithm.update(param.getBytes());
+ byte[] messageDigest = algorithm.digest();
+ return toHexString(messageDigest);
+ } catch (Exception e) {
+ throw new RenException(ErrorCode.TOKEN_GENERATE_ERROR, e);
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/password/BCrypt.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/password/BCrypt.java
new file mode 100644
index 0000000..39d350c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/password/BCrypt.java
@@ -0,0 +1,691 @@
+package xiaozhi.modules.security.password;
+
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.security.SecureRandom;
+
+/**
+ * BCrypt implements OpenBSD-style Blowfish password hashing using the scheme
+ * described in
+ * "A Future-Adaptable Password Scheme" by Niels Provos and David Mazieres.
+ * <p>
+ * This password hashing system tries to thwart off-line password cracking using
+ * a
+ * computationally-intensive hashing algorithm, based on Bruce Schneier's
+ * Blowfish cipher.
+ * The work factor of the algorithm is parameterised, so it can be increased as
+ * computers
+ * get faster.
+ * <p>
+ * Usage is really simple. To hash a password for the first time, call the
+ * hashpw method
+ * with a random salt, like this:
+ * <p>
+ * <code>
+ * String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt()); <br>
+ * </code>
+ * <p>
+ * To check whether a plaintext password matches one that has been hashed
+ * previously, use
+ * the checkpw method:
+ * <p>
+ * <code>
+ * if (BCrypt.checkpw(candidate_password, stored_hash))<br>
+ * System.out.println("It matches");<br>
+ * else<br>
+ * System.out.println("It does not match");<br>
+ * </code>
+ * <p>
+ * The gensalt() method takes an optional parameter (log_rounds) that determines
+ * the
+ * computational complexity of the hashing:
+ * <p>
+ * <code>
+ * String strong_salt = BCrypt.gensalt(10)<br>
+ * String stronger_salt = BCrypt.gensalt(12)<br>
+ * </code>
+ * <p>
+ * The amount of work increases exponentially (2**log_rounds), so each increment
+ * is twice
+ * as much work. The default log_rounds is 10, and the valid range is 4 to 31.
+ *
+ * @author Damien Miller
+ */
+public class BCrypt {
+ // BCrypt parameters
+
+ private static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10;
+ private static final int BCRYPT_SALT_LEN = 16;
+ // Blowfish parameters
+ private static final int BLOWFISH_NUM_ROUNDS = 16;
+ // Initial contents of key schedule
+ private static final int P_orig[] = { 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
+ 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377,
+ 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
+ 0x9216d5d9, 0x8979fb1b };
+ private static final int S_orig[] = { 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
+ 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7,
+ 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
+ 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5,
+ 0x9c30d539, 0x2af26013, 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
+ 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27,
+ 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
+ 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993,
+ 0xb3ee1411, 0x636fbc2a, 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
+ 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af,
+ 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
+ 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5,
+ 0x0f6d6ff3, 0x83f44239, 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
+ 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68,
+ 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
+ 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4,
+ 0x7d84a5c3, 0x3b8b5ebe, 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
+ 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248,
+ 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
+ 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4,
+ 0x5e5c9ec2, 0x196a2463, 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
+ 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd,
+ 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
+ 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc,
+ 0x8ea5e9f8, 0xdb3222f8, 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
+ 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0,
+ 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
+ 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8,
+ 0x4afcb56c, 0x2dd1d35b, 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
+ 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01,
+ 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
+ 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64,
+ 0x8888b812, 0x900df01c, 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
+ 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8,
+ 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
+ 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86,
+ 0xc75442f5, 0xfb9d35cf, 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
+ 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e,
+ 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
+ 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a,
+ 0x1b510052, 0x9a532915, 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
+ 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6,
+ 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
+ 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d,
+ 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
+ 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65,
+ 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
+ 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9,
+ 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
+ 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d,
+ 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
+ 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc,
+ 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
+ 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908,
+ 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
+ 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124,
+ 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
+ 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908,
+ 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
+ 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b,
+ 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
+ 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa,
+ 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
+ 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d,
+ 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
+ 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5,
+ 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
+ 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96,
+ 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
+ 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca,
+ 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
+ 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77,
+ 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
+ 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054,
+ 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
+ 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea,
+ 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
+ 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646,
+ 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
+ 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea,
+ 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
+ 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e,
+ 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
+ 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd,
+ 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
+ 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, 0xe93d5a68, 0x948140f7,
+ 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
+ 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546,
+ 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
+ 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, 0x96eb27b3, 0x55fd3941,
+ 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
+ 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c,
+ 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
+ 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, 0x1dc9faf7, 0x4b6d1856,
+ 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
+ 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87,
+ 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
+ 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, 0xfdf8e802, 0x04272f70,
+ 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
+ 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1,
+ 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
+ 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, 0x6b2395e0, 0x333e92e1,
+ 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
+ 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa,
+ 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
+ 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, 0xc67b5510, 0x6d672c37,
+ 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
+ 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24,
+ 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
+ 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, 0x6a124237, 0xb79251e7,
+ 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
+ 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f,
+ 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
+ 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, 0x83426b33, 0xf01eab71,
+ 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
+ 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74,
+ 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
+ 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 0xb90bace1, 0xbb8205d0,
+ 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
+ 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df,
+ 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
+ 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, 0x9af88c27, 0x773f8641,
+ 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
+ 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de,
+ 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
+ 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, 0xed545578, 0x08fca5b5,
+ 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
+ 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212,
+ 0x670efa8e, 0x406000e0, 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
+ 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315,
+ 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
+ 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d,
+ 0xd5730a1d, 0x4cd04dc6, 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
+ 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6,
+ 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
+ 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da,
+ 0x3f046f69, 0x77fa0a59, 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
+ 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d,
+ 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
+ 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc,
+ 0xf8d56629, 0x79132e28, 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
+ 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2,
+ 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
+ 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8,
+ 0xabcc5167, 0xccad925f, 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
+ 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46,
+ 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
+ 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40,
+ 0xccd2017f, 0x6bb4e3bb, 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
+ 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e,
+ 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
+ 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d,
+ 0x06b89fb4, 0xce6ea048, 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
+ 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b,
+ 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
+ 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667,
+ 0x8df9317c, 0xe0b12b4f, 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
+ 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb,
+ 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
+ 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df,
+ 0xd3a0342b, 0x8971f21e, 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
+ 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891,
+ 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
+ 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5,
+ 0x6e163697, 0x88d273cc, 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
+ 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00,
+ 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
+ 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76,
+ 0x77afa1c5, 0x20756060, 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
+ 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0,
+ 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 };
+ // bcrypt IV: "OrpheanBeholderScryDoubt"
+ static private final int bf_crypt_ciphertext[] = { 0x4f727068, 0x65616e42,
+ 0x65686f6c, 0x64657253, 0x63727944, 0x6f756274 };
+ // Table for Base64 encoding
+ static private final char base64_code[] = { '.', '/', 'A', 'B', 'C', 'D', 'E', 'F',
+ 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
+ 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
+ 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
+ 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
+ // Table for Base64 decoding
+ static private final byte index_64[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, -1, -1, -1, -1, -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,
+ -1, -1, -1, -1, -1, -1, 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, -1, -1, -1, -1, -1 };
+ static final int MIN_LOG_ROUNDS = 4;
+ static final int MAX_LOG_ROUNDS = 31;
+ // Expanded Blowfish key
+ private int P[];
+ private int S[];
+
+ /**
+ * Encode a byte array using bcrypt's slightly-modified base64 encoding scheme.
+ * Note
+ * that this is <strong>not</strong> compatible with the standard MIME-base64
+ * encoding.
+ *
+ * @param d the byte array to encode
+ * @param len the number of bytes to encode
+ * @param rs the destination buffer for the base64-encoded string
+ * @throws IllegalArgumentException if the length is invalid
+ */
+ static void encode_base64(byte d[], int len, StringBuilder rs)
+ throws IllegalArgumentException {
+ int off = 0;
+ int c1, c2;
+
+ if (len <= 0 || len > d.length) {
+ throw new IllegalArgumentException("Invalid len");
+ }
+
+ while (off < len) {
+ c1 = d[off++] & 0xff;
+ rs.append(base64_code[(c1 >> 2) & 0x3f]);
+ c1 = (c1 & 0x03) << 4;
+ if (off >= len) {
+ rs.append(base64_code[c1 & 0x3f]);
+ break;
+ }
+ c2 = d[off++] & 0xff;
+ c1 |= (c2 >> 4) & 0x0f;
+ rs.append(base64_code[c1 & 0x3f]);
+ c1 = (c2 & 0x0f) << 2;
+ if (off >= len) {
+ rs.append(base64_code[c1 & 0x3f]);
+ break;
+ }
+ c2 = d[off++] & 0xff;
+ c1 |= (c2 >> 6) & 0x03;
+ rs.append(base64_code[c1 & 0x3f]);
+ rs.append(base64_code[c2 & 0x3f]);
+ }
+ }
+
+ /**
+ * Look up the 3 bits base64-encoded by the specified character, range-checking
+ * against conversion table
+ *
+ * @param x the base64-encoded value
+ * @return the decoded value of x
+ */
+ private static byte char64(char x) {
+ if (x > index_64.length) {
+ return -1;
+ }
+ return index_64[x];
+ }
+
+ /**
+ * Decode a string encoded using bcrypt's base64 scheme to a byte array. Note
+ * that
+ * this is *not* compatible with the standard MIME-base64 encoding.
+ *
+ * @param s the string to decode
+ * @param maxolen the maximum number of bytes to decode
+ * @return an array containing the decoded bytes
+ * @throws IllegalArgumentException if maxolen is invalid
+ */
+ static byte[] decode_base64(String s, int maxolen) throws IllegalArgumentException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream(maxolen);
+ int off = 0, slen = s.length(), olen = 0;
+ byte c1, c2, c3, c4, o;
+
+ if (maxolen <= 0) {
+ throw new IllegalArgumentException("Invalid maxolen");
+ }
+
+ while (off < slen - 1 && olen < maxolen) {
+ c1 = char64(s.charAt(off++));
+ c2 = char64(s.charAt(off++));
+ if (c1 == -1 || c2 == -1) {
+ break;
+ }
+ o = (byte) (c1 << 2);
+ o |= (c2 & 0x30) >> 4;
+ out.write(o);
+ if (++olen >= maxolen || off >= slen) {
+ break;
+ }
+ c3 = char64(s.charAt(off++));
+ if (c3 == -1) {
+ break;
+ }
+ o = (byte) ((c2 & 0x0f) << 4);
+ o |= (c3 & 0x3c) >> 2;
+ out.write(o);
+ if (++olen >= maxolen || off >= slen) {
+ break;
+ }
+ c4 = char64(s.charAt(off++));
+ o = (byte) ((c3 & 0x03) << 6);
+ o |= c4;
+ out.write(o);
+ ++olen;
+ }
+
+ return out.toByteArray();
+ }
+
+ /**
+ * Blowfish encipher a single 64-bit block encoded as two 32-bit halves
+ *
+ * @param lr an array containing the two 32-bit half blocks
+ * @param off the position in the array of the blocks
+ */
+ private final void encipher(int lr[], int off) {
+ int i, n, l = lr[off], r = lr[off + 1];
+
+ l ^= P[0];
+ for (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2;) {
+ // Feistel substitution on left word
+ n = S[(l >> 24) & 0xff];
+ n += S[0x100 | ((l >> 16) & 0xff)];
+ n ^= S[0x200 | ((l >> 8) & 0xff)];
+ n += S[0x300 | (l & 0xff)];
+ r ^= n ^ P[++i];
+
+ // Feistel substitution on right word
+ n = S[(r >> 24) & 0xff];
+ n += S[0x100 | ((r >> 16) & 0xff)];
+ n ^= S[0x200 | ((r >> 8) & 0xff)];
+ n += S[0x300 | (r & 0xff)];
+ l ^= n ^ P[++i];
+ }
+ lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1];
+ lr[off + 1] = l;
+ }
+
+ /**
+ * Cycically extract a word of key material
+ *
+ * @param data the string to extract the data from
+ * @param offp a "pointer" (as a one-entry array) to the current offset into
+ * data
+ * @return the next word of material from data
+ */
+ private static int streamtoword(byte data[], int offp[]) {
+ int i;
+ int word = 0;
+ int off = offp[0];
+
+ for (i = 0; i < 4; i++) {
+ word = (word << 8) | (data[off] & 0xff);
+ off = (off + 1) % data.length;
+ }
+
+ offp[0] = off;
+ return word;
+ }
+
+ /**
+ * Initialise the Blowfish key schedule
+ */
+ private void init_key() {
+ P = (int[]) P_orig.clone();
+ S = (int[]) S_orig.clone();
+ }
+
+ /**
+ * Key the Blowfish cipher
+ *
+ * @param key an array containing the key
+ */
+ private void key(byte key[]) {
+ int i;
+ int koffp[] = { 0 };
+ int lr[] = { 0, 0 };
+ int plen = P.length, slen = S.length;
+
+ for (i = 0; i < plen; i++) {
+ P[i] = P[i] ^ streamtoword(key, koffp);
+ }
+
+ for (i = 0; i < plen; i += 2) {
+ encipher(lr, 0);
+ P[i] = lr[0];
+ P[i + 1] = lr[1];
+ }
+
+ for (i = 0; i < slen; i += 2) {
+ encipher(lr, 0);
+ S[i] = lr[0];
+ S[i + 1] = lr[1];
+ }
+ }
+
+ /**
+ * Perform the "enhanced key schedule" step described by Provos and Mazieres in
+ * "A Future-Adaptable Password Scheme"
+ * http://www.openbsd.org/papers/bcrypt-paper.ps
+ *
+ * @param data salt information
+ * @param key password information
+ */
+ private void ekskey(byte data[], byte key[]) {
+ int i;
+ int koffp[] = { 0 }, doffp[] = { 0 };
+ int lr[] = { 0, 0 };
+ int plen = P.length, slen = S.length;
+
+ for (i = 0; i < plen; i++) {
+ P[i] = P[i] ^ streamtoword(key, koffp);
+ }
+
+ for (i = 0; i < plen; i += 2) {
+ lr[0] ^= streamtoword(data, doffp);
+ lr[1] ^= streamtoword(data, doffp);
+ encipher(lr, 0);
+ P[i] = lr[0];
+ P[i + 1] = lr[1];
+ }
+
+ for (i = 0; i < slen; i += 2) {
+ lr[0] ^= streamtoword(data, doffp);
+ lr[1] ^= streamtoword(data, doffp);
+ encipher(lr, 0);
+ S[i] = lr[0];
+ S[i + 1] = lr[1];
+ }
+ }
+
+ static long roundsForLogRounds(int log_rounds) {
+ if (log_rounds < 4 || log_rounds > 31) {
+ throw new IllegalArgumentException("Bad number of rounds");
+ }
+ return 1L << log_rounds;
+ }
+
+ /**
+ * Perform the central password hashing step in the bcrypt scheme
+ *
+ * @param password the password to hash
+ * @param salt the binary salt to hash with the password
+ * @param log_rounds the binary logarithm of the number of rounds of hashing to
+ * apply
+ * @return an array containing the binary hashed password
+ */
+ private byte[] crypt_raw(byte password[], byte salt[], int log_rounds) {
+ int cdata[] = (int[]) bf_crypt_ciphertext.clone();
+ int clen = cdata.length;
+ byte ret[];
+
+ long rounds = roundsForLogRounds(log_rounds);
+
+ init_key();
+ ekskey(salt, password);
+ for (long i = 0; i < rounds; i++) {
+ key(password);
+ key(salt);
+ }
+
+ for (int i = 0; i < 64; i++) {
+ for (int j = 0; j < (clen >> 1); j++) {
+ encipher(cdata, j << 1);
+ }
+ }
+
+ ret = new byte[clen * 4];
+ for (int i = 0, j = 0; i < clen; i++) {
+ ret[j++] = (byte) ((cdata[i] >> 24) & 0xff);
+ ret[j++] = (byte) ((cdata[i] >> 16) & 0xff);
+ ret[j++] = (byte) ((cdata[i] >> 8) & 0xff);
+ ret[j++] = (byte) (cdata[i] & 0xff);
+ }
+ return ret;
+ }
+
+ /**
+ * Hash a password using the OpenBSD bcrypt scheme
+ *
+ * @param password the password to hash
+ * @param salt the salt to hash with (perhaps generated using
+ * BCrypt.gensalt)
+ * @return the hashed password
+ * @throws IllegalArgumentException if invalid salt is passed
+ */
+ public static String hashpw(String password, String salt) throws IllegalArgumentException {
+ BCrypt B;
+ String real_salt;
+ byte passwordb[], saltb[], hashed[];
+ char minor = (char) 0;
+ int rounds, off = 0;
+ StringBuilder rs = new StringBuilder();
+
+ if (salt == null) {
+ throw new IllegalArgumentException("salt cannot be null");
+ }
+
+ int saltLength = salt.length();
+
+ if (saltLength < 28) {
+ throw new IllegalArgumentException("Invalid salt");
+ }
+
+ if (salt.charAt(0) != '$' || salt.charAt(1) != '2') {
+ throw new IllegalArgumentException("Invalid salt version");
+ }
+ if (salt.charAt(2) == '$') {
+ off = 3;
+ } else {
+ minor = salt.charAt(2);
+ if (minor != 'a' || salt.charAt(3) != '$') {
+ throw new IllegalArgumentException("Invalid salt revision");
+ }
+ off = 4;
+ }
+
+ if (saltLength - off < 25) {
+ throw new IllegalArgumentException("Invalid salt");
+ }
+
+ // Extract number of rounds
+ if (salt.charAt(off + 2) > '$') {
+ throw new IllegalArgumentException("Missing salt rounds");
+ }
+ rounds = Integer.parseInt(salt.substring(off, off + 2));
+
+ real_salt = salt.substring(off + 3, off + 25);
+ try {
+ passwordb = (password + (minor >= 'a' ? "\000" : "")).getBytes("UTF-8");
+ } catch (UnsupportedEncodingException uee) {
+ throw new AssertionError("UTF-8 is not supported");
+ }
+
+ saltb = decode_base64(real_salt, BCRYPT_SALT_LEN);
+
+ B = new BCrypt();
+ hashed = B.crypt_raw(passwordb, saltb, rounds);
+
+ rs.append("$2");
+ if (minor >= 'a') {
+ rs.append(minor);
+ }
+ rs.append("$");
+ if (rounds < 10) {
+ rs.append("0");
+ }
+ rs.append(rounds);
+ rs.append("$");
+ encode_base64(saltb, saltb.length, rs);
+ encode_base64(hashed, bf_crypt_ciphertext.length * 4 - 1, rs);
+ return rs.toString();
+ }
+
+ /**
+ * Generate a salt for use with the BCrypt.hashpw() method
+ *
+ * @param log_rounds the log2 of the number of rounds of hashing to apply - the
+ * work
+ * factor therefore increases as 2**log_rounds. Minimum 4,
+ * maximum 31.
+ * @param random an instance of SecureRandom to use
+ * @return an encoded salt value
+ */
+ public static String gensalt(int log_rounds, SecureRandom random) {
+ if (log_rounds < MIN_LOG_ROUNDS || log_rounds > MAX_LOG_ROUNDS) {
+ throw new IllegalArgumentException("Bad number of rounds");
+ }
+ StringBuilder rs = new StringBuilder();
+ byte rnd[] = new byte[BCRYPT_SALT_LEN];
+
+ random.nextBytes(rnd);
+
+ rs.append("$2a$");
+ if (log_rounds < 10) {
+ rs.append("0");
+ }
+ rs.append(log_rounds);
+ rs.append("$");
+ encode_base64(rnd, rnd.length, rs);
+ return rs.toString();
+ }
+
+ /**
+ * Generate a salt for use with the BCrypt.hashpw() method
+ *
+ * @param log_rounds the log2 of the number of rounds of hashing to apply - the
+ * work
+ * factor therefore increases as 2**log_rounds. Minimum 4,
+ * maximum 31.
+ * @return an encoded salt value
+ */
+ public static String gensalt(int log_rounds) {
+ return gensalt(log_rounds, new SecureRandom());
+ }
+
+ /**
+ * Generate a salt for use with the BCrypt.hashpw() method, selecting a
+ * reasonable
+ * default for the number of hashing rounds to apply
+ *
+ * @return an encoded salt value
+ */
+ public static String gensalt() {
+ return gensalt(GENSALT_DEFAULT_LOG2_ROUNDS);
+ }
+
+ /**
+ * Check that a plaintext password matches a previously hashed one
+ *
+ * @param plaintext the plaintext password to verify
+ * @param hashed the previously-hashed password
+ * @return true if the passwords match, false otherwise
+ */
+ public static boolean checkpw(String plaintext, String hashed) {
+ return equalsNoEarlyReturn(hashed, hashpw(plaintext, hashed));
+ }
+
+ static boolean equalsNoEarlyReturn(String a, String b) {
+ char[] caa = a.toCharArray();
+ char[] cab = b.toCharArray();
+
+ if (caa.length != cab.length) {
+ return false;
+ }
+
+ byte ret = 0;
+ for (int i = 0; i < caa.length; i++) {
+ ret |= caa[i] ^ cab[i];
+ }
+ return ret == 0;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/password/BCryptPasswordEncoder.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/password/BCryptPasswordEncoder.java
new file mode 100644
index 0000000..0da907f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/password/BCryptPasswordEncoder.java
@@ -0,0 +1,81 @@
+package xiaozhi.modules.security.password;
+
+import java.security.SecureRandom;
+import java.util.regex.Pattern;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Implementation of PasswordEncoder that uses the BCrypt strong hashing
+ * function. Clients
+ * can optionally supply a "strength" (a.k.a. log rounds in BCrypt) and a
+ * SecureRandom
+ * instance. The larger the strength parameter the more work will have to be
+ * done
+ * (exponentially) to hash the passwords. The default value is 10.
+ *
+ * @author Dave Syer
+ */
+public class BCryptPasswordEncoder implements PasswordEncoder {
+ private Pattern BCRYPT_PATTERN = Pattern
+ .compile("\\A\\$2a?\\$\\d\\d\\$[./0-9A-Za-z]{53}");
+ private final Log logger = LogFactory.getLog(getClass());
+
+ private final int strength;
+
+ private final SecureRandom random;
+
+ public BCryptPasswordEncoder() {
+ this(-1);
+ }
+
+ /**
+ * @param strength the log rounds to use, between 4 and 31
+ */
+ public BCryptPasswordEncoder(int strength) {
+ this(strength, null);
+ }
+
+ /**
+ * @param strength the log rounds to use, between 4 and 31
+ * @param random the secure random instance to use
+ */
+ public BCryptPasswordEncoder(int strength, SecureRandom random) {
+ if (strength != -1 && (strength < BCrypt.MIN_LOG_ROUNDS || strength > BCrypt.MAX_LOG_ROUNDS)) {
+ throw new IllegalArgumentException("Bad strength");
+ }
+ this.strength = strength;
+ this.random = random;
+ }
+
+ @Override
+ public String encode(CharSequence rawPassword) {
+ String salt;
+ if (strength > 0) {
+ if (random != null) {
+ salt = BCrypt.gensalt(strength, random);
+ } else {
+ salt = BCrypt.gensalt(strength);
+ }
+ } else {
+ salt = BCrypt.gensalt();
+ }
+ return BCrypt.hashpw(rawPassword.toString(), salt);
+ }
+
+ @Override
+ public boolean matches(CharSequence rawPassword, String encodedPassword) {
+ if (encodedPassword == null || encodedPassword.length() == 0) {
+ logger.warn("Empty encoded password");
+ return false;
+ }
+
+ if (!BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
+ logger.warn("Encoded password does not look like BCrypt");
+ return false;
+ }
+
+ return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/password/PasswordEncoder.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/password/PasswordEncoder.java
new file mode 100644
index 0000000..2de70cc
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/password/PasswordEncoder.java
@@ -0,0 +1,33 @@
+package xiaozhi.modules.security.password;
+
+/**
+ * Service interface for encoding passwords.
+ * <p>
+ * The preferred implementation is {@code BCryptPasswordEncoder}.
+ *
+ * @author Keith Donald
+ */
+public interface PasswordEncoder {
+
+ /**
+ * Encode the raw password. Generally, a good encoding algorithm applies a SHA-1
+ * or
+ * greater hash combined with an 8-byte or greater randomly generated salt.
+ */
+ String encode(CharSequence rawPassword);
+
+ /**
+ * Verify the encoded password obtained from storage matches the submitted raw
+ * password after it too is encoded. Returns true if the passwords match, false
+ * if
+ * they do not. The stored password itself is never decoded.
+ *
+ * @param rawPassword the raw password to encode and match
+ * @param encodedPassword the encoded password from storage to compare with
+ * @return true if the raw password, after encoding, matches the encoded
+ * password from
+ * storage
+ */
+ boolean matches(CharSequence rawPassword, String encodedPassword);
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/password/PasswordUtils.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/password/PasswordUtils.java
new file mode 100644
index 0000000..9144b38
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/password/PasswordUtils.java
@@ -0,0 +1,40 @@
+package xiaozhi.modules.security.password;
+
+/**
+ * 瀵嗙爜宸ュ叿绫�
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public class PasswordUtils {
+ private static PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
+
+ /**
+ * 鍔犲瘑
+ *
+ * @param str 瀛楃涓�
+ * @return 杩斿洖鍔犲瘑瀛楃涓�
+ */
+ public static String encode(String str) {
+ return passwordEncoder.encode(str);
+ }
+
+ /**
+ * 姣旇緝瀵嗙爜鏄惁鐩哥瓑
+ *
+ * @param str 鏄庢枃瀵嗙爜
+ * @param password 鍔犲瘑鍚庡瘑鐮�
+ * @return true锛氭垚鍔� false锛氬け璐�
+ */
+ public static boolean matches(String str, String password) {
+ return passwordEncoder.matches(str, password);
+ }
+
+ public static void main(String[] args) {
+ String str = "admin";
+ String password = encode(str);
+
+ System.out.println(password);
+ System.out.println(matches(str, password));
+ }
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/secret/ServerSecretFilter.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/secret/ServerSecretFilter.java
new file mode 100644
index 0000000..b2a1854
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/secret/ServerSecretFilter.java
@@ -0,0 +1,105 @@
+package xiaozhi.modules.security.secret;
+
+import java.io.IOException;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.utils.HttpContextUtils;
+import xiaozhi.common.utils.JsonUtils;
+import xiaozhi.common.utils.Result;
+import xiaozhi.modules.sys.service.SysParamsService;
+
+/**
+ * Config API 杩囨护鍣�
+ */
+@Slf4j
+@RequiredArgsConstructor
+public class ServerSecretFilter extends AuthenticatingFilter {
+ private final SysParamsService sysParamsService;
+
+ @Override
+ protected ServerSecretToken createToken(ServletRequest request, ServletResponse response) {
+ // 鑾峰彇璇锋眰token
+ String token = getRequestToken((HttpServletRequest) request);
+
+ if (StringUtils.isBlank(token)) {
+ log.warn("createToken:token is empty");
+ return null;
+ }
+
+ return new ServerSecretToken(token);
+ }
+
+ @Override
+ protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
+ // 瀵筄PTIONS璇锋眰鏀捐
+ if (((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
+ // 鑾峰彇token骞舵牎楠�
+ String token = getRequestToken((HttpServletRequest) servletRequest);
+ if (StringUtils.isBlank(token)) {
+ // token涓虹┖锛岃繑鍥�401
+ this.sendUnauthorizedResponse((HttpServletResponse) servletResponse, "鏈嶅姟鍣ㄥ瘑閽ヤ笉鑳戒负绌�");
+ return false;
+ }
+
+ // 楠岃瘉token鏄惁鍖归厤
+ String serverSecret = getServerSecret();
+ if (StringUtils.isBlank(serverSecret) || !serverSecret.equals(token)) {
+ // token鏃犳晥锛岃繑鍥�401
+ this.sendUnauthorizedResponse((HttpServletResponse) servletResponse, "鏃犳晥鐨勬湇鍔″櫒瀵嗛挜");
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * 鍙戦�佹湭鎺堟潈鍝嶅簲
+ */
+ private void sendUnauthorizedResponse(HttpServletResponse response, String message) {
+ response.setContentType("application/json;charset=utf-8");
+ response.setHeader("Access-Control-Allow-Credentials", "true");
+ response.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
+
+ try {
+ String json = JsonUtils.toJsonString(new Result<Void>().error(ErrorCode.UNAUTHORIZED, message));
+ response.getWriter().print(json);
+ } catch (IOException e) {
+ log.error("鍝嶅簲杈撳嚭澶辫触", e);
+ }
+ }
+
+ /**
+ * 鑾峰彇璇锋眰鐨則oken
+ */
+ private String getRequestToken(HttpServletRequest httpRequest) {
+ String token = null;
+ // 浠巋eader涓幏鍙杢oken
+ String authorization = httpRequest.getHeader("Authorization");
+ if (StringUtils.isNotBlank(authorization) && authorization.startsWith("Bearer ")) {
+ token = authorization.replace("Bearer ", "");
+ }
+ return token;
+ }
+
+ private String getServerSecret() {
+ return sysParamsService.getValue(Constant.SERVER_SECRET, true);
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/secret/ServerSecretToken.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/secret/ServerSecretToken.java
new file mode 100644
index 0000000..9072eaf
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/secret/ServerSecretToken.java
@@ -0,0 +1,25 @@
+package xiaozhi.modules.security.secret;
+
+import org.apache.shiro.authc.AuthenticationToken;
+
+/**
+ * Config API Token
+ */
+public class ServerSecretToken implements AuthenticationToken {
+ private static final long serialVersionUID = 1L;
+ private final String token;
+
+ public ServerSecretToken(String token) {
+ this.token = token;
+ }
+
+ @Override
+ public Object getPrincipal() {
+ return token;
+ }
+
+ @Override
+ public Object getCredentials() {
+ return token;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/CaptchaService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/CaptchaService.java
new file mode 100644
index 0000000..0db9d29
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/CaptchaService.java
@@ -0,0 +1,45 @@
+package xiaozhi.modules.security.service;
+
+import java.io.IOException;
+
+import jakarta.servlet.http.HttpServletResponse;
+
+/**
+ * 楠岃瘉鐮�
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public interface CaptchaService {
+
+ /**
+ * 鍥剧墖楠岃瘉鐮�
+ */
+ void create(HttpServletResponse response, String uuid) throws IOException;
+
+ /**
+ * 楠岃瘉鐮佹晥楠�
+ *
+ * @param uuid uuid
+ * @param code 楠岃瘉鐮�
+ * @param delete 鏄惁鍒犻櫎楠岃瘉鐮�
+ * @return true锛氭垚鍔� false锛氬け璐�
+ */
+ boolean validate(String uuid, String code, Boolean delete);
+
+ /**
+ * 鍙戦�佺煭淇¢獙璇佺爜
+ *
+ * @param phone 鎵嬫満
+ */
+ void sendSMSValidateCode(String phone);
+
+ /**
+ * 楠岃瘉鐭俊楠岃瘉鐮�
+ *
+ * @param phone 鎵嬫満
+ * @param code 楠岃瘉鐮�
+ * @param delete 鏄惁鍒犻櫎楠岃瘉鐮�
+ * @return true锛氭垚鍔� false锛氬け璐�
+ */
+ boolean validateSMSValidateCode(String phone, String code, Boolean delete);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/ShiroService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/ShiroService.java
new file mode 100644
index 0000000..b9d1888
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/ShiroService.java
@@ -0,0 +1,22 @@
+package xiaozhi.modules.security.service;
+
+import xiaozhi.modules.security.entity.SysUserTokenEntity;
+import xiaozhi.modules.sys.entity.SysUserEntity;
+
+/**
+ * shiro鐩稿叧鎺ュ彛
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public interface ShiroService {
+
+ SysUserTokenEntity getByToken(String token);
+
+ /**
+ * 鏍规嵁鐢ㄦ埛ID锛屾煡璇㈢敤鎴�
+ *
+ * @param userId
+ */
+ SysUserEntity getUser(Long userId);
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/SysUserTokenService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/SysUserTokenService.java
new file mode 100644
index 0000000..d1d094d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/SysUserTokenService.java
@@ -0,0 +1,41 @@
+package xiaozhi.modules.security.service;
+
+import xiaozhi.common.page.TokenDTO;
+import xiaozhi.common.service.BaseService;
+import xiaozhi.common.utils.Result;
+import xiaozhi.modules.security.entity.SysUserTokenEntity;
+import xiaozhi.modules.sys.dto.PasswordDTO;
+import xiaozhi.modules.sys.dto.SysUserDTO;
+
+/**
+ * 鐢ㄦ埛Token
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public interface SysUserTokenService extends BaseService<SysUserTokenEntity> {
+
+ /**
+ * 鐢熸垚token
+ *
+ * @param userId 鐢ㄦ埛ID
+ */
+ Result<TokenDTO> createToken(Long userId);
+
+ SysUserDTO getUserByToken(String token);
+
+ /**
+ * 閫�鍑�
+ *
+ * @param userId 鐢ㄦ埛ID
+ */
+ void logout(Long userId);
+
+ /**
+ * 淇敼瀵嗙爜
+ *
+ * @param userId
+ * @param passwordDTO
+ */
+ void changePassword(Long userId, PasswordDTO passwordDTO);
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/CaptchaServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/CaptchaServiceImpl.java
new file mode 100644
index 0000000..1c57364
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/CaptchaServiceImpl.java
@@ -0,0 +1,167 @@
+package xiaozhi.modules.security.service.impl;
+
+import java.io.IOException;
+import java.time.Duration;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.wf.captcha.SpecCaptcha;
+import com.wf.captcha.base.Captcha;
+
+import cn.hutool.core.util.RandomUtil;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletResponse;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.redis.RedisKeys;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.modules.security.service.CaptchaService;
+import xiaozhi.modules.sms.service.SmsService;
+import xiaozhi.modules.sys.service.SysParamsService;
+
+/**
+ * 楠岃瘉鐮�
+ */
+@Service
+public class CaptchaServiceImpl implements CaptchaService {
+ @Resource
+ private RedisUtils redisUtils;
+ @Resource
+ private SmsService smsService;
+ @Resource
+ private SysParamsService sysParamsService;
+ @Value("${renren.redis.open}")
+ private boolean open;
+ /**
+ * Local Cache 5鍒嗛挓杩囨湡
+ */
+ Cache<String, String> localCache = CacheBuilder.newBuilder().maximumSize(1000)
+ .expireAfterAccess(Duration.ofMinutes(5)).build();
+
+ @Override
+ public void create(HttpServletResponse response, String uuid) throws IOException {
+ response.setContentType("image/gif");
+ response.setHeader("Pragma", "No-cache");
+ response.setHeader("Cache-Control", "no-cache");
+ response.setDateHeader("Expires", 0);
+
+ // 鐢熸垚楠岃瘉鐮�
+ SpecCaptcha captcha = new SpecCaptcha(150, 40);
+ captcha.setLen(5);
+ captcha.setCharType(Captcha.TYPE_DEFAULT);
+ captcha.out(response.getOutputStream());
+
+ // 淇濆瓨鍒扮紦瀛�
+ setCache(uuid, captcha.text());
+ }
+
+ @Override
+ public boolean validate(String uuid, String code, Boolean delete) {
+ if (StringUtils.isBlank(code)) {
+ return false;
+ }
+ // 鑾峰彇楠岃瘉鐮�
+ String captcha = getCache(uuid, delete);
+
+ // 鏁堥獙鎴愬姛
+ if (code.equalsIgnoreCase(captcha)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void sendSMSValidateCode(String phone) {
+ // 妫�鏌ュ彂閫侀棿闅�
+ String lastSendTimeKey = RedisKeys.getSMSLastSendTimeKey(phone);
+ // 鑾峰彇鏄惁鍙戦�佽繃锛屽鏋滄病鏈夎缃渶鍚庡彂閫佹椂闂达紙60绉掞級
+ String lastSendTime = redisUtils
+ .getKeyOrCreate(lastSendTimeKey,
+ String.valueOf(System.currentTimeMillis()), 60L);
+ if (lastSendTime != null) {
+ long lastSendTimeLong = Long.parseLong(lastSendTime);
+ long currentTime = System.currentTimeMillis();
+ long timeDiff = currentTime - lastSendTimeLong;
+ if (timeDiff < 60000) {
+ throw new RenException(ErrorCode.SMS_SEND_TOO_FREQUENTLY, String.valueOf((60000 - timeDiff) / 1000));
+ }
+ }
+
+ // 妫�鏌ヤ粖鏃ュ彂閫佹鏁�
+ String todayCountKey = RedisKeys.getSMSTodayCountKey(phone);
+ Integer todayCount = (Integer) redisUtils.get(todayCountKey);
+ if (todayCount == null) {
+ todayCount = 0;
+ }
+
+ // 鑾峰彇鏈�澶у彂閫佹鏁伴檺鍒�
+ Integer maxSendCount = sysParamsService.getValueObject(
+ Constant.SysMSMParam.SERVER_SMS_MAX_SEND_COUNT.getValue(),
+ Integer.class);
+ if (maxSendCount == null) {
+ maxSendCount = 5; // 榛樿鍊�
+ }
+
+ if (todayCount >= maxSendCount) {
+ throw new RenException(ErrorCode.TODAY_SMS_LIMIT_REACHED);
+ }
+
+ String key = RedisKeys.getSMSValidateCodeKey(phone);
+ String validateCodes = RandomUtil.randomNumbers(6);
+
+ // 璁剧疆楠岃瘉鐮�
+ setCache(key, validateCodes);
+
+ // 鏇存柊浠婃棩鍙戦�佹鏁�
+ if (todayCount == 0) {
+ redisUtils.increment(todayCountKey, RedisUtils.DEFAULT_EXPIRE);
+ } else {
+ redisUtils.increment(todayCountKey);
+ }
+
+ // 鍙戦�侀獙璇佺爜鐭俊
+ smsService.sendVerificationCodeSms(phone, validateCodes);
+ }
+
+ @Override
+ public boolean validateSMSValidateCode(String phone, String code, Boolean delete) {
+ String key = RedisKeys.getSMSValidateCodeKey(phone);
+ return validate(key, code, delete);
+ }
+
+ private void setCache(String key, String value) {
+ if (open) {
+ key = RedisKeys.getCaptchaKey(key);
+ // 璁剧疆5鍒嗛挓杩囨湡
+ redisUtils.set(key, value, 300);
+ } else {
+ localCache.put(key, value);
+ }
+ }
+
+ private String getCache(String key, Boolean delete) {
+ if (open) {
+ key = RedisKeys.getCaptchaKey(key);
+ String captcha = (String) redisUtils.get(key);
+ // 鍒犻櫎楠岃瘉鐮�
+ if (captcha != null && delete) {
+ redisUtils.delete(key);
+ }
+
+ return captcha;
+ }
+
+ String captcha = localCache.getIfPresent(key);
+ // 鍒犻櫎楠岃瘉鐮�
+ if (captcha != null) {
+ localCache.invalidate(key);
+ }
+ return captcha;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/ShiroServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/ShiroServiceImpl.java
new file mode 100644
index 0000000..27c2097
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/ShiroServiceImpl.java
@@ -0,0 +1,27 @@
+package xiaozhi.modules.security.service.impl;
+
+import org.springframework.stereotype.Service;
+
+import lombok.AllArgsConstructor;
+import xiaozhi.modules.security.dao.SysUserTokenDao;
+import xiaozhi.modules.security.entity.SysUserTokenEntity;
+import xiaozhi.modules.security.service.ShiroService;
+import xiaozhi.modules.sys.dao.SysUserDao;
+import xiaozhi.modules.sys.entity.SysUserEntity;
+
+@AllArgsConstructor
+@Service
+public class ShiroServiceImpl implements ShiroService {
+ private final SysUserDao sysUserDao;
+ private final SysUserTokenDao sysUserTokenDao;
+
+ @Override
+ public SysUserTokenEntity getByToken(String token) {
+ return sysUserTokenDao.getByToken(token);
+ }
+
+ @Override
+ public SysUserEntity getUser(Long userId) {
+ return sysUserDao.selectById(userId);
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/SysUserTokenServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/SysUserTokenServiceImpl.java
new file mode 100644
index 0000000..7f0d311
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/service/impl/SysUserTokenServiceImpl.java
@@ -0,0 +1,116 @@
+package xiaozhi.modules.security.service.impl;
+
+import java.util.Date;
+
+import org.springframework.stereotype.Service;
+
+import cn.hutool.core.date.DateUtil;
+import lombok.AllArgsConstructor;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.page.TokenDTO;
+import xiaozhi.common.service.impl.BaseServiceImpl;
+import xiaozhi.common.utils.HttpContextUtils;
+import xiaozhi.common.utils.Result;
+import xiaozhi.modules.security.dao.SysUserTokenDao;
+import xiaozhi.modules.security.entity.SysUserTokenEntity;
+import xiaozhi.modules.security.oauth2.TokenGenerator;
+import xiaozhi.modules.security.service.SysUserTokenService;
+import xiaozhi.modules.sys.dto.PasswordDTO;
+import xiaozhi.modules.sys.dto.SysUserDTO;
+import xiaozhi.modules.sys.service.SysUserService;
+
+@AllArgsConstructor
+@Service
+public class SysUserTokenServiceImpl extends BaseServiceImpl<SysUserTokenDao, SysUserTokenEntity>
+ implements SysUserTokenService {
+
+ private final SysUserService sysUserService;
+ /**
+ * 12灏忔椂鍚庤繃鏈�
+ */
+ private final static int EXPIRE = 3600 * 12;
+
+ @Override
+ public Result<TokenDTO> createToken(Long userId) {
+ // 鐢ㄦ埛token
+ String token;
+
+ // 褰撳墠鏃堕棿
+ Date now = new Date();
+ // 杩囨湡鏃堕棿
+ Date expireTime = new Date(now.getTime() + EXPIRE * 1000);
+
+ // 鍒ゆ柇鏄惁鐢熸垚杩噒oken
+ SysUserTokenEntity tokenEntity = baseDao.getByUserId(userId);
+ if (tokenEntity == null) {
+ // 鐢熸垚涓�涓猼oken
+ token = TokenGenerator.generateValue();
+
+ tokenEntity = new SysUserTokenEntity();
+ tokenEntity.setUserId(userId);
+ tokenEntity.setToken(token);
+ tokenEntity.setUpdateDate(now);
+ tokenEntity.setExpireDate(expireTime);
+
+ // 淇濆瓨token
+ this.insert(tokenEntity);
+ } else {
+ // 鍒ゆ柇token鏄惁杩囨湡
+ if (tokenEntity.getExpireDate().getTime() < System.currentTimeMillis()) {
+ // token杩囨湡锛岄噸鏂扮敓鎴恡oken
+ token = TokenGenerator.generateValue();
+ } else {
+ token = tokenEntity.getToken();
+ }
+
+ tokenEntity.setToken(token);
+ tokenEntity.setUpdateDate(now);
+ tokenEntity.setExpireDate(expireTime);
+
+ // 鏇存柊token
+ this.updateById(tokenEntity);
+ }
+
+ String clientHash = HttpContextUtils.getClientCode();
+
+ TokenDTO tokenDTO = new TokenDTO();
+ tokenDTO.setToken(token);
+ tokenDTO.setExpire(EXPIRE);
+ tokenDTO.setClientHash(clientHash);
+ return new Result<TokenDTO>().ok(tokenDTO);
+ }
+
+ @Override
+ public SysUserDTO getUserByToken(String token) {
+ SysUserTokenEntity userToken = baseDao.getByToken(token);
+ if (null == userToken) {
+ throw new RenException(ErrorCode.TOKEN_INVALID);
+ }
+
+ Date now = new Date();
+ if (userToken.getExpireDate().before(now)) {
+ throw new RenException(ErrorCode.UNAUTHORIZED);
+ }
+
+ SysUserDTO userDTO = sysUserService.getByUserId(userToken.getUserId());
+ userDTO.setPassword("");
+ return userDTO;
+ }
+
+ @Override
+ public void logout(Long userId) {
+ Date expireDate = DateUtil.offsetMinute(new Date(), -1);
+ baseDao.logout(userId, expireDate);
+ }
+
+ @Override
+ public void changePassword(Long userId, PasswordDTO passwordDTO) {
+ // 淇敼瀵嗙爜
+ sysUserService.changePassword(userId, passwordDTO);
+
+ // 浣� token 澶辨晥锛屽悗闇�瑕侀噸鏂扮櫥褰�
+ Date expireDate = DateUtil.offsetMinute(new Date(), -1);
+ baseDao.logout(userId, expireDate);
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/user/SecurityUser.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/user/SecurityUser.java
new file mode 100644
index 0000000..0581f95
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/security/user/SecurityUser.java
@@ -0,0 +1,50 @@
+package xiaozhi.modules.security.user;
+
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.subject.Subject;
+
+import xiaozhi.common.user.UserDetail;
+
+/**
+ * Shiro宸ュ叿绫�
+ * Copyright (c) 浜轰汉寮�婧� All rights reserved.
+ * Website: https://www.renren.io
+ */
+public class SecurityUser {
+
+ public static Subject getSubject() {
+ try {
+ return SecurityUtils.getSubject();
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * 鑾峰彇鐢ㄦ埛淇℃伅
+ */
+ public static UserDetail getUser() {
+ Subject subject = getSubject();
+ if (subject == null) {
+ return new UserDetail();
+ }
+
+ UserDetail user = (UserDetail) subject.getPrincipal();
+ if (user == null) {
+ return new UserDetail();
+ }
+
+ return user;
+ }
+
+ public static String getToken() {
+ return getUser().getToken();
+ }
+
+ /**
+ * 鑾峰彇鐢ㄦ埛ID
+ */
+ public static Long getUserId() {
+ return getUser().getId();
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sms/service/SmsService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sms/service/SmsService.java
new file mode 100644
index 0000000..c33ad94
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sms/service/SmsService.java
@@ -0,0 +1,17 @@
+package xiaozhi.modules.sms.service;
+
+/**
+ * 鐭俊鏈嶅姟鐨勬柟娉曞畾涔夋帴鍙�
+ *
+ * @author zjy
+ * @since 2025-05-12
+ */
+public interface SmsService {
+
+ /**
+ * 鍙戦�侀獙璇佺爜鐭俊
+ * @param phone 鎵嬫満鍙风爜
+ * @param VerificationCode 楠岃瘉鐮�
+ */
+ void sendVerificationCodeSms(String phone, String VerificationCode) ;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sms/service/imp/ALiYunSmsService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sms/service/imp/ALiYunSmsService.java
new file mode 100644
index 0000000..a8a378f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sms/service/imp/ALiYunSmsService.java
@@ -0,0 +1,77 @@
+package xiaozhi.modules.sms.service.imp;
+
+import com.aliyun.dysmsapi20170525.Client;
+import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
+import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
+import com.aliyun.teaopenapi.models.Config;
+import com.aliyun.teautil.models.RuntimeOptions;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.redis.RedisKeys;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.modules.sms.service.SmsService;
+import xiaozhi.modules.sys.service.SysParamsService;
+
+@Service
+@AllArgsConstructor
+@Slf4j
+public class ALiYunSmsService implements SmsService {
+ private final SysParamsService sysParamsService;
+ private final RedisUtils redisUtils;
+
+ @Override
+ public void sendVerificationCodeSms(String phone, String VerificationCode) {
+ Client client = createClient();
+ String SignName = sysParamsService.getValue(Constant.SysMSMParam
+ .ALIYUN_SMS_SIGN_NAME.getValue(),true);
+ String TemplateCode = sysParamsService.getValue(Constant.SysMSMParam
+ .ALIYUN_SMS_SMS_CODE_TEMPLATE_CODE.getValue(),true);
+ try {
+ SendSmsRequest sendSmsRequest = new SendSmsRequest()
+ .setSignName(SignName)
+ .setTemplateCode(TemplateCode)
+ .setPhoneNumbers(phone)
+ .setTemplateParam(String.format("{\"code\":\"%s\"}", VerificationCode));
+ RuntimeOptions runtime = new RuntimeOptions();
+ // 澶嶅埗浠g爜杩愯璇疯嚜琛屾墦鍗� API 鐨勮繑鍥炲��
+ SendSmsResponse sendSmsResponse = client.sendSmsWithOptions(sendSmsRequest, runtime);
+ log.info("鍙戦�佺煭淇″搷搴旂殑requestID: {}", sendSmsResponse.getBody().getRequestId());
+ } catch (Exception e) {
+ // 濡傛灉鍙戦�佸け璐ヤ簡閫�杩樿繖娆″彂閫佹暟
+ String todayCountKey = RedisKeys.getSMSTodayCountKey(phone);
+ redisUtils.delete(todayCountKey);
+ // 閿欒 message
+ log.error(e.getMessage());
+ throw new RenException(ErrorCode.SMS_SEND_FAILED);
+ }
+
+ }
+
+
+ /**
+ * 鍒涘缓闃块噷浜戣繛鎺�
+ * @return 杩斿洖杩炴帴瀵硅薄
+ */
+ private Client createClient(){
+ String ACCESS_KEY_ID = sysParamsService.getValue(Constant.SysMSMParam
+ .ALIYUN_SMS_ACCESS_KEY_ID.getValue(),true);
+ String ACCESS_KEY_SECRET = sysParamsService.getValue(Constant.SysMSMParam
+ .ALIYUN_SMS_ACCESS_KEY_SECRET.getValue(),true);
+ try {
+ Config config = new Config()
+ .setAccessKeyId(ACCESS_KEY_ID)
+ .setAccessKeySecret(ACCESS_KEY_SECRET);
+ // 閰嶇疆 Endpoint銆備腑鍥界珯璇蜂娇鐢╠ysmsapi.aliyuncs.com
+ config.endpoint = "dysmsapi.aliyuncs.com";
+ return new Client(config);
+ }catch (Exception e){
+ // 閿欒 message
+ log.error(e.getMessage());
+ throw new RenException(ErrorCode.SMS_CONNECTION_FAILED);
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/AdminController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/AdminController.java
new file mode 100644
index 0000000..2ccc1e6
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/AdminController.java
@@ -0,0 +1,109 @@
+package xiaozhi.modules.sys.controller;
+
+import java.util.Map;
+
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.utils.Result;
+import xiaozhi.common.validator.ValidatorUtils;
+import xiaozhi.modules.device.dto.DevicePageUserDTO;
+import xiaozhi.modules.device.service.DeviceService;
+import xiaozhi.modules.device.vo.UserShowDeviceListVO;
+import xiaozhi.modules.sys.dto.AdminPageUserDTO;
+import xiaozhi.modules.sys.service.SysUserService;
+import xiaozhi.modules.sys.vo.AdminPageUserVO;
+
+/**
+ * 绠$悊鍛樻帶鍒跺眰
+ *
+ * @author zjy
+ * @since 2025-3-25
+ */
+@AllArgsConstructor
+@RestController
+@RequestMapping("/admin")
+@Tag(name = "绠$悊鍛樼鐞�")
+public class AdminController {
+ private final SysUserService sysUserService;
+
+ private final DeviceService deviceService;
+
+ @GetMapping("/users")
+ @Operation(summary = "鍒嗛〉鏌ユ壘鐢ㄦ埛")
+ @RequiresPermissions("sys:role:superAdmin")
+ @Parameters({
+ @Parameter(name = "mobile", description = "鐢ㄦ埛鎵嬫満鍙风爜", required = false),
+ @Parameter(name = Constant.PAGE, description = "褰撳墠椤电爜锛屼粠1寮�濮�", required = true),
+ @Parameter(name = Constant.LIMIT, description = "姣忛〉鏄剧ず璁板綍鏁�", required = true),
+ })
+ public Result<PageData<AdminPageUserVO>> pageUser(
+ @Parameter(hidden = true) @RequestParam Map<String, Object> params) {
+ AdminPageUserDTO dto = new AdminPageUserDTO();
+ dto.setMobile((String) params.get("mobile"));
+ dto.setLimit((String) params.get(Constant.LIMIT));
+ dto.setPage((String) params.get(Constant.PAGE));
+ ValidatorUtils.validateEntity(dto);
+ PageData<AdminPageUserVO> page = sysUserService.page(dto);
+ return new Result<PageData<AdminPageUserVO>>().ok(page);
+ }
+
+ @PutMapping("/users/{id}")
+ @Operation(summary = "閲嶇疆瀵嗙爜")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<String> update(
+ @PathVariable Long id) {
+ String password = sysUserService.resetPassword(id);
+ return new Result<String>().ok(password);
+ }
+
+ @DeleteMapping("/users/{id}")
+ @Operation(summary = "鐢ㄦ埛鍒犻櫎")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<Void> delete(@PathVariable Long id) {
+ sysUserService.deleteById(id);
+ return new Result<>();
+ }
+
+ @PutMapping("/users/changeStatus/{status}")
+ @Operation(summary = "鎵归噺淇敼鐢ㄦ埛鐘舵��")
+ @RequiresPermissions("sys:role:superAdmin")
+ @Parameter(name = "status", description = "鐢ㄦ埛鐘舵��", required = true)
+ public Result<Void> changeStatus(@PathVariable Integer status, @RequestBody String[] userIds) {
+ sysUserService.changeStatus(status, userIds);
+ return new Result<Void>();
+ }
+
+ @GetMapping("/device/all")
+ @Operation(summary = "鍒嗛〉鏌ユ壘璁惧")
+ @RequiresPermissions("sys:role:superAdmin")
+ @Parameters({
+ @Parameter(name = "keywords", description = "璁惧鍏抽敭璇�", required = false),
+ @Parameter(name = Constant.PAGE, description = "褰撳墠椤电爜锛屼粠1寮�濮�", required = true),
+ @Parameter(name = Constant.LIMIT, description = "姣忛〉鏄剧ず璁板綍鏁�", required = true),
+ })
+ public Result<PageData<UserShowDeviceListVO>> pageDevice(
+ @Parameter(hidden = true) @RequestParam Map<String, Object> params) {
+ DevicePageUserDTO dto = new DevicePageUserDTO();
+ dto.setKeywords((String) params.get("keywords"));
+ dto.setLimit((String) params.get(Constant.LIMIT));
+ dto.setPage((String) params.get(Constant.PAGE));
+ ValidatorUtils.validateEntity(dto);
+ PageData<UserShowDeviceListVO> page = deviceService.page(dto);
+ return new Result<PageData<UserShowDeviceListVO>>().ok(page);
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/ServerSideManageController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/ServerSideManageController.java
new file mode 100644
index 0000000..b467630
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/ServerSideManageController.java
@@ -0,0 +1,139 @@
+package xiaozhi.modules.sys.controller;
+
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.socket.WebSocketHttpHeaders;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.AllArgsConstructor;
+import xiaozhi.common.annotation.LogOperation;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.utils.Result;
+import xiaozhi.modules.sys.dto.EmitSeverActionDTO;
+import xiaozhi.modules.sys.dto.ServerActionPayloadDTO;
+import xiaozhi.modules.sys.dto.ServerActionResponseDTO;
+import xiaozhi.modules.sys.enums.ServerActionEnum;
+import xiaozhi.modules.sys.service.SysParamsService;
+import xiaozhi.modules.sys.utils.WebSocketClientManager;
+import xiaozhi.modules.device.service.DeviceService;
+import xiaozhi.common.redis.RedisUtils;
+
+/**
+ * 鏈嶅姟绔鐞嗘帶鍒跺櫒
+ */
+@RestController
+@RequestMapping("/admin/server")
+@Tag(name = "鏈嶅姟绔鐞�")
+@AllArgsConstructor
+public class ServerSideManageController {
+ private final SysParamsService sysParamsService;
+ private final DeviceService deviceService;
+ private final RedisUtils redisUtils;
+ private static final ObjectMapper objectMapper;
+ static {
+ objectMapper = new ObjectMapper();
+ // 蹇界暐json瀛楃涓蹭腑瀛樺湪锛屼絾pojo涓笉瀛樺湪瀵瑰簲瀛楁鐨勬儏鍐�
+ objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ }
+
+ @Operation(summary = "鑾峰彇Ws鏈嶅姟绔垪琛�")
+ @GetMapping("/server-list")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<List<String>> getWsServerList() {
+ String wsText = sysParamsService.getValue(Constant.SERVER_WEBSOCKET, true);
+ if (StringUtils.isBlank(wsText)) {
+ return new Result<List<String>>().ok(Collections.emptyList());
+ }
+ return new Result<List<String>>().ok(Arrays.asList(wsText.split(";")));
+ }
+
+ @Operation(summary = "閫氱煡python鏈嶅姟绔洿鏂伴厤缃�")
+ @PostMapping("/emit-action")
+ @LogOperation("閫氱煡python鏈嶅姟绔洿鏂伴厤缃�")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<Boolean> emitServerAction(@RequestBody @Valid EmitSeverActionDTO emitSeverActionDTO) {
+ if (emitSeverActionDTO.getAction() == null) {
+ throw new RenException(ErrorCode.INVALID_SERVER_ACTION);
+ }
+ String wsText = sysParamsService.getValue(Constant.SERVER_WEBSOCKET, true);
+ if (StringUtils.isBlank(wsText)) {
+ throw new RenException(ErrorCode.SERVER_WEBSOCKET_NOT_CONFIGURED);
+ }
+ String targetWs = emitSeverActionDTO.getTargetWs();
+ String[] wsList = wsText.split(";");
+ // 鎵惧埌闇�瑕佸彂璧风殑
+ if (StringUtils.isBlank(targetWs) || !Arrays.asList(wsList).contains(targetWs)) {
+ throw new RenException(ErrorCode.TARGET_WEBSOCKET_NOT_EXIST);
+ }
+ return new Result<Boolean>().ok(emitServerActionByWs(targetWs, emitSeverActionDTO.getAction()));
+ }
+
+ private Boolean emitServerActionByWs(String targetWsUri, ServerActionEnum actionEnum) {
+ if (StringUtils.isBlank(targetWsUri) || actionEnum == null) {
+ return false;
+ }
+ String serverSK = sysParamsService.getValue(Constant.SERVER_SECRET, true);
+
+ String deviceId = UUID.randomUUID().toString();
+ String clientId = UUID.randomUUID().toString();
+
+ String redisKey = xiaozhi.common.redis.RedisKeys.getTmpRegisterMacKey(deviceId);
+ redisUtils.set(redisKey, "true", 300); // 5鍒嗛挓鏈夋晥鏈�
+
+ WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
+ headers.add("device-id", deviceId);
+ headers.add("client-id", clientId);
+ try {
+ String token = deviceService.generateWebSocketToken(clientId, deviceId);
+ headers.add("authorization", "Bearer " + token);
+ } catch (Exception e) {
+ throw new RenException(ErrorCode.WEB_SOCKET_CONNECT_FAILED);
+ }
+
+ try (WebSocketClientManager client = new WebSocketClientManager.Builder()
+ .connectTimeout(3, TimeUnit.SECONDS)
+ .maxSessionDuration(120, TimeUnit.SECONDS)
+ .uri(targetWsUri)
+ .headers(headers)
+ .build()) {
+ // 濡傛灉杩炴帴鎴愬姛鍒欏彂閫佷竴涓猨son鏁版嵁鍖呭苟绛夊緟鏈嶅姟绔搷搴�
+ client.sendJson(
+ ServerActionPayloadDTO.build(
+ actionEnum,
+ Map.of("secret", serverSK)));
+ // 绛夊緟鏈嶅姟绔搷搴斿苟鎸佺画鐩戝惉淇℃伅
+ client.listener((jsonText) -> {
+ if (StringUtils.isBlank(jsonText)) {
+ return false;
+ }
+ try {
+ ServerActionResponseDTO response = objectMapper.readValue(jsonText, ServerActionResponseDTO.class);
+ Boolean isSuccess = ServerActionResponseDTO.isSuccess(response);
+ return isSuccess;
+ } catch (JsonProcessingException e) {
+ return false;
+ }
+ });
+ } catch (Exception e) {
+ // 鎹曡幏鍏ㄩ儴閿欒锛岀敱鍏ㄥ眬寮傚父澶勭悊鍣ㄨ繑鍥�
+ throw new RenException(ErrorCode.WEB_SOCKET_CONNECT_FAILED);
+ }
+ return true;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/SysDictDataController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/SysDictDataController.java
new file mode 100644
index 0000000..30187fe
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/SysDictDataController.java
@@ -0,0 +1,105 @@
+package xiaozhi.modules.sys.controller;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.utils.Result;
+import xiaozhi.common.validator.ValidatorUtils;
+import xiaozhi.modules.sys.dto.SysDictDataDTO;
+import xiaozhi.modules.sys.service.SysDictDataService;
+import xiaozhi.modules.sys.vo.SysDictDataItem;
+import xiaozhi.modules.sys.vo.SysDictDataVO;
+
+/**
+ * 瀛楀吀鏁版嵁绠$悊
+ *
+ * @author czc
+ * @since 2025-04-30
+ */
+@AllArgsConstructor
+@RestController
+@RequestMapping("/admin/dict/data")
+@Tag(name = "瀛楀吀鏁版嵁绠$悊")
+public class SysDictDataController {
+ private final SysDictDataService sysDictDataService;
+
+ @GetMapping("/page")
+ @Operation(summary = "鍒嗛〉鏌ヨ瀛楀吀鏁版嵁")
+ @RequiresPermissions("sys:role:superAdmin")
+ @Parameters({ @Parameter(name = "dictTypeId", description = "瀛楀吀绫诲瀷ID", required = true),
+ @Parameter(name = "dictLabel", description = "鏁版嵁鏍囩"), @Parameter(name = "dictValue", description = "鏁版嵁鍊�"),
+ @Parameter(name = Constant.PAGE, description = "褰撳墠椤电爜锛屼粠1寮�濮�", required = true),
+ @Parameter(name = Constant.LIMIT, description = "姣忛〉鏄剧ず璁板綍鏁�", required = true) })
+ public Result<PageData<SysDictDataVO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
+ ValidatorUtils.validateEntity(params);
+ // 寮哄埗鏍¢獙dictTypeId鏄惁瀛樺湪
+ if (!params.containsKey("dictTypeId") || StringUtils.isEmpty(String.valueOf(params.get("dictTypeId")))) {
+ return new Result<PageData<SysDictDataVO>>().error("dictTypeId涓嶈兘涓虹┖");
+ }
+
+ PageData<SysDictDataVO> page = sysDictDataService.page(params);
+ return new Result<PageData<SysDictDataVO>>().ok(page);
+ }
+
+ @GetMapping("/{id}")
+ @Operation(summary = "鑾峰彇瀛楀吀鏁版嵁璇︽儏")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<SysDictDataVO> get(@PathVariable("id") Long id) {
+ SysDictDataVO vo = sysDictDataService.get(id);
+ return new Result<SysDictDataVO>().ok(vo);
+ }
+
+ @PostMapping("/save")
+ @Operation(summary = "鏂板瀛楀吀鏁版嵁")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<Void> save(@RequestBody SysDictDataDTO dto) {
+ ValidatorUtils.validateEntity(dto);
+ sysDictDataService.save(dto);
+ return new Result<>();
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "淇敼瀛楀吀鏁版嵁")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<Void> update(@RequestBody SysDictDataDTO dto) {
+ ValidatorUtils.validateEntity(dto);
+ sysDictDataService.update(dto);
+ return new Result<>();
+ }
+
+ @PostMapping("/delete")
+ @Operation(summary = "鍒犻櫎瀛楀吀鏁版嵁")
+ @RequiresPermissions("sys:role:superAdmin")
+ @Parameter(name = "ids", description = "ID鏁扮粍", required = true)
+ public Result<Void> delete(@RequestBody Long[] ids) {
+ sysDictDataService.delete(ids);
+ return new Result<>();
+ }
+
+ @GetMapping("/type/{dictType}")
+ @Operation(summary = "鑾峰彇瀛楀吀鏁版嵁鍒楄〃")
+ @RequiresPermissions("sys:role:normal")
+ public Result<List<SysDictDataItem>> getDictDataByType(@PathVariable("dictType") String dictType) {
+ List<SysDictDataItem> list = sysDictDataService.getDictDataByType(dictType);
+ return new Result<List<SysDictDataItem>>().ok(list);
+ }
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/SysDictTypeController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/SysDictTypeController.java
new file mode 100644
index 0000000..f16409b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/SysDictTypeController.java
@@ -0,0 +1,92 @@
+package xiaozhi.modules.sys.controller;
+
+import java.util.Map;
+
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.utils.Result;
+import xiaozhi.common.validator.ValidatorUtils;
+import xiaozhi.modules.sys.dto.SysDictTypeDTO;
+import xiaozhi.modules.sys.service.SysDictTypeService;
+import xiaozhi.modules.sys.vo.SysDictTypeVO;
+
+/**
+ * 瀛楀吀绫诲瀷绠$悊
+ *
+ * @author czc
+ * @since 2025-04-30
+ */
+@AllArgsConstructor
+@RestController
+@RequestMapping("/admin/dict/type")
+@Tag(name = "瀛楀吀绫诲瀷绠$悊")
+public class SysDictTypeController {
+ private final SysDictTypeService sysDictTypeService;
+
+ @GetMapping("/page")
+ @Operation(summary = "鍒嗛〉鏌ヨ瀛楀吀绫诲瀷")
+ @RequiresPermissions("sys:role:superAdmin")
+ @Parameters({ @Parameter(name = "dictType", description = "瀛楀吀绫诲瀷缂栫爜"),
+ @Parameter(name = "dictName", description = "瀛楀吀绫诲瀷鍚嶇О"),
+ @Parameter(name = Constant.PAGE, description = "褰撳墠椤电爜锛屼粠1寮�濮�", required = true),
+ @Parameter(name = Constant.LIMIT, description = "姣忛〉鏄剧ず璁板綍鏁�", required = true) })
+ public Result<PageData<SysDictTypeVO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
+ ValidatorUtils.validateEntity(params);
+ PageData<SysDictTypeVO> page = sysDictTypeService.page(params);
+ return new Result<PageData<SysDictTypeVO>>().ok(page);
+ }
+
+ @GetMapping("/{id}")
+ @Operation(summary = "鑾峰彇瀛楀吀绫诲瀷璇︽儏")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<SysDictTypeVO> get(@PathVariable("id") Long id) {
+ SysDictTypeVO vo = sysDictTypeService.get(id);
+ return new Result<SysDictTypeVO>().ok(vo);
+ }
+
+ @PostMapping("/save")
+ @Operation(summary = "淇濆瓨瀛楀吀绫诲瀷")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<Void> save(@RequestBody SysDictTypeDTO dto) {
+ // 鍙傛暟鏍¢獙
+ ValidatorUtils.validateEntity(dto);
+
+ sysDictTypeService.save(dto);
+ return new Result<>();
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "淇敼瀛楀吀绫诲瀷")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<Void> update(@RequestBody SysDictTypeDTO dto) {
+ // 鍙傛暟鏍¢獙
+ ValidatorUtils.validateEntity(dto);
+
+ sysDictTypeService.update(dto);
+ return new Result<>();
+ }
+
+ @PostMapping("/delete")
+ @Operation(summary = "鍒犻櫎瀛楀吀绫诲瀷")
+ @RequiresPermissions("sys:role:superAdmin")
+ @Parameter(name = "ids", description = "ID鏁扮粍", required = true)
+ public Result<Void> delete(@RequestBody Long[] ids) {
+ sysDictTypeService.delete(ids);
+ return new Result<>();
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/SysParamsController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/SysParamsController.java
new file mode 100644
index 0000000..49317aa
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/SysParamsController.java
@@ -0,0 +1,301 @@
+package xiaozhi.modules.sys.controller;
+
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import xiaozhi.common.annotation.LogOperation;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.utils.Result;
+import xiaozhi.common.validator.AssertUtils;
+import xiaozhi.common.validator.ValidatorUtils;
+import xiaozhi.common.validator.group.AddGroup;
+import xiaozhi.common.validator.group.DefaultGroup;
+import xiaozhi.common.validator.group.UpdateGroup;
+import xiaozhi.modules.config.service.ConfigService;
+import xiaozhi.modules.sys.dto.SysParamsDTO;
+import xiaozhi.modules.sys.service.SysParamsService;
+import xiaozhi.modules.sys.utils.WebSocketValidator;
+
+/**
+ * 鍙傛暟绠$悊
+ *
+ * @author Mark sunlightcs@gmail.com
+ * @since 1.0.0
+ */
+@RestController
+@RequestMapping("admin/params")
+@Tag(name = "鍙傛暟绠$悊")
+@AllArgsConstructor
+public class SysParamsController {
+ private final SysParamsService sysParamsService;
+ private final ConfigService configService;
+ private final RestTemplate restTemplate;
+
+ @GetMapping("page")
+ @Operation(summary = "鍒嗛〉")
+ @Parameters({
+ @Parameter(name = Constant.PAGE, description = "褰撳墠椤电爜锛屼粠1寮�濮�", in = ParameterIn.QUERY, required = true, ref = "int"),
+ @Parameter(name = Constant.LIMIT, description = "姣忛〉鏄剧ず璁板綍鏁�", in = ParameterIn.QUERY, required = true, ref = "int"),
+ @Parameter(name = Constant.ORDER_FIELD, description = "鎺掑簭瀛楁", in = ParameterIn.QUERY, ref = "String"),
+ @Parameter(name = Constant.ORDER, description = "鎺掑簭鏂瑰紡锛屽彲閫夊��(asc銆乨esc)", in = ParameterIn.QUERY, ref = "String"),
+ @Parameter(name = "paramCode", description = "鍙傛暟缂栫爜鎴栧弬鏁板娉�", in = ParameterIn.QUERY, ref = "String")
+ })
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<PageData<SysParamsDTO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
+ PageData<SysParamsDTO> page = sysParamsService.page(params);
+
+ return new Result<PageData<SysParamsDTO>>().ok(page);
+ }
+
+ @GetMapping("{id}")
+ @Operation(summary = "淇℃伅")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<SysParamsDTO> get(@PathVariable("id") Long id) {
+ SysParamsDTO data = sysParamsService.get(id);
+
+ return new Result<SysParamsDTO>().ok(data);
+ }
+
+ @PostMapping
+ @Operation(summary = "淇濆瓨")
+ @LogOperation("淇濆瓨")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<Void> save(@RequestBody SysParamsDTO dto) {
+ // 鏁堥獙鏁版嵁
+ ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class);
+
+ sysParamsService.save(dto);
+ configService.getConfig(false);
+ return new Result<Void>();
+ }
+
+ @PutMapping
+ @Operation(summary = "淇敼")
+ @LogOperation("淇敼")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<Void> update(@RequestBody SysParamsDTO dto) {
+ // 鏁堥獙鏁版嵁
+ ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class);
+
+ // 楠岃瘉WebSocket鍦板潃鍒楄〃
+ validateWebSocketUrls(dto.getParamCode(), dto.getParamValue());
+
+ // 楠岃瘉OTA鍦板潃
+ validateOtaUrl(dto.getParamCode(), dto.getParamValue());
+
+ // 楠岃瘉MCP鍦板潃
+ validateMcpUrl(dto.getParamCode(), dto.getParamValue());
+
+ // 楠岃瘉澹扮汗鍦板潃
+ validateVoicePrint(dto.getParamCode(), dto.getParamValue());
+
+ // 鏍¢獙mqtt瀵嗛挜闀垮害
+ validateMqttSecretLength(dto.getParamCode(), dto.getParamValue());
+
+ // 濡傛灉鏄郴缁熷姛鑳借彍鍗曢厤缃紝浣跨敤鐗规畩澶勭悊
+ if (Constant.SYSTEM_WEB_MENU.equals(dto.getParamCode())) {
+ sysParamsService.updateSystemWebMenu(dto.getParamValue());
+ } else {
+ sysParamsService.update(dto);
+ }
+ configService.getConfig(false);
+ return new Result<Void>();
+ }
+
+ /**
+ * 楠岃瘉WebSocket鍦板潃鍒楄〃
+ *
+ * @param urls WebSocket鍦板潃鍒楄〃锛屼互鍒嗗彿鍒嗛殧
+ * @return 楠岃瘉缁撴灉
+ */
+ private void validateWebSocketUrls(String paramCode, String urls) {
+ if (!paramCode.equals(Constant.SERVER_WEBSOCKET)) {
+ return;
+ }
+ String[] wsUrls = urls.split("\\;");
+ if (wsUrls.length == 0) {
+ throw new RenException(ErrorCode.WEBSOCKET_URLS_EMPTY);
+ }
+ for (String url : wsUrls) {
+ if (StringUtils.isNotBlank(url)) {
+ // 妫�鏌ユ槸鍚﹀寘鍚玪ocalhost鎴�127.0.0.1
+ if (url.contains("localhost") || url.contains("127.0.0.1")) {
+ throw new RenException(ErrorCode.WEBSOCKET_URL_LOCALHOST);
+ }
+
+ // 楠岃瘉WebSocket鍦板潃鏍煎紡
+ if (!WebSocketValidator.validateUrlFormat(url)) {
+ throw new RenException(ErrorCode.WEBSOCKET_URL_FORMAT_ERROR);
+ }
+
+ // 娴嬭瘯WebSocket杩炴帴
+ if (!WebSocketValidator.testConnection(url)) {
+ throw new RenException(ErrorCode.WEBSOCKET_CONNECTION_FAILED);
+ }
+ }
+ }
+ }
+
+ @PostMapping("/delete")
+ @Operation(summary = "鍒犻櫎")
+ @LogOperation("鍒犻櫎")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<Void> delete(@RequestBody String[] ids) {
+ // 鏁堥獙鏁版嵁
+ AssertUtils.isArrayEmpty(ids, "id");
+
+ sysParamsService.delete(ids);
+ configService.getConfig(false);
+ return new Result<Void>();
+ }
+
+ /**
+ * 楠岃瘉OTA鍦板潃
+ */
+ private void validateOtaUrl(String paramCode, String url) {
+ if (!paramCode.equals(Constant.SERVER_OTA)) {
+ return;
+ }
+ if (StringUtils.isBlank(url) || url.equals("null")) {
+ return;
+ }
+
+ // 妫�鏌ユ槸鍚﹀寘鍚玪ocalhost鎴�127.0.0.1
+ if (url.contains("localhost") || url.contains("127.0.0.1")) {
+ throw new RenException(ErrorCode.OTA_URL_LOCALHOST);
+ }
+
+ // 楠岃瘉URL鏍煎紡
+ if (!url.toLowerCase().startsWith("http")) {
+ throw new RenException(ErrorCode.OTA_URL_PROTOCOL_ERROR);
+ }
+ if (!url.endsWith("/ota/")) {
+ throw new RenException(ErrorCode.OTA_URL_FORMAT_ERROR);
+ }
+
+ try {
+ // 鍙戦�丟ET璇锋眰
+ ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
+ if (response.getStatusCode() != HttpStatus.OK) {
+ throw new RenException(ErrorCode.OTA_INTERFACE_ACCESS_FAILED);
+ }
+ // 妫�鏌ュ搷搴斿唴瀹规槸鍚﹀寘鍚玂TA鐩稿叧淇℃伅
+ String body = response.getBody();
+ if (body == null || !body.contains("OTA")) {
+ throw new RenException(ErrorCode.OTA_INTERFACE_FORMAT_ERROR);
+ }
+ } catch (Exception e) {
+ throw new RenException(ErrorCode.OTA_INTERFACE_VALIDATION_FAILED);
+ }
+ }
+
+ private void validateMcpUrl(String paramCode, String url) {
+ if (!paramCode.equals(Constant.SERVER_MCP_ENDPOINT)) {
+ return;
+ }
+ if (StringUtils.isBlank(url) || url.equals("null")) {
+ return;
+ }
+ if (url.contains("localhost") || url.contains("127.0.0.1")) {
+ throw new RenException(ErrorCode.MCP_URL_LOCALHOST);
+ }
+ if (!url.toLowerCase().contains("key")) {
+ throw new RenException(ErrorCode.MCP_URL_INVALID);
+ }
+
+ try {
+ // 鍙戦�丟ET璇锋眰
+ ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
+ if (response.getStatusCode() != HttpStatus.OK) {
+ throw new RenException(ErrorCode.MCP_INTERFACE_ACCESS_FAILED);
+ }
+ // 妫�鏌ュ搷搴斿唴瀹规槸鍚﹀寘鍚玬cp鐩稿叧淇℃伅
+ String body = response.getBody();
+ if (body == null || !body.contains("success")) {
+ throw new RenException(ErrorCode.MCP_INTERFACE_FORMAT_ERROR);
+ }
+ } catch (Exception e) {
+ throw new RenException(ErrorCode.MCP_INTERFACE_VALIDATION_FAILED);
+ }
+ }
+
+ // 楠岃瘉澹扮汗鎺ュ彛鍦板潃鏄惁姝e父
+ private void validateVoicePrint(String paramCode, String url) {
+ if (!paramCode.equals(Constant.SERVER_VOICE_PRINT)) {
+ return;
+ }
+ if (StringUtils.isBlank(url) || url.equals("null")) {
+ return;
+ }
+ if (url.contains("localhost") || url.contains("127.0.0.1")) {
+ throw new RenException(ErrorCode.VOICEPRINT_URL_LOCALHOST);
+ }
+ if (!url.toLowerCase().contains("key")) {
+ throw new RenException(ErrorCode.VOICEPRINT_URL_INVALID);
+ }
+ // 楠岃瘉URL鏍煎紡
+ if (!url.toLowerCase().startsWith("http")) {
+ throw new RenException(ErrorCode.VOICEPRINT_URL_PROTOCOL_ERROR);
+ }
+ try {
+ // 鍙戦�丟ET璇锋眰
+ ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
+ if (response.getStatusCode() != HttpStatus.OK) {
+ throw new RenException(ErrorCode.VOICEPRINT_INTERFACE_ACCESS_FAILED);
+ }
+ // 妫�鏌ュ搷搴斿唴瀹�
+ String body = response.getBody();
+ if (body == null || !body.contains("healthy")) {
+ throw new RenException(ErrorCode.VOICEPRINT_INTERFACE_FORMAT_ERROR);
+ }
+ } catch (Exception e) {
+ throw new RenException(ErrorCode.VOICEPRINT_INTERFACE_VALIDATION_FAILED);
+ }
+ }
+
+ // 鏍¢獙mqtt瀵嗛挜闀垮害鍜屽鏉傚害
+ private void validateMqttSecretLength(String paramCode, String secret) {
+ if (!paramCode.equals(Constant.SERVER_MQTT_SECRET)) {
+ return;
+ }
+ if (StringUtils.isBlank(secret) || secret.equals("null")) {
+ throw new RenException(ErrorCode.MQTT_SECRET_EMPTY);
+ }
+ if (secret.length() < 8) {
+ throw new RenException(ErrorCode.MQTT_SECRET_LENGTH_INSECURE);
+ }
+ // 妫�鏌ユ槸鍚﹀悓鏃跺寘鍚ぇ灏忓啓瀛楁瘝
+ if (!secret.matches(".*[a-z].*") || !secret.matches(".*[A-Z].*")) {
+ throw new RenException(ErrorCode.MQTT_SECRET_CHARACTER_INSECURE);
+ }
+ // 涓嶅厑璁稿寘鍚急瀵嗙爜
+ String[] weakPasswords = { "test", "1234", "admin", "password", "qwerty", "xiaozhi" };
+ for (String weakPassword : weakPasswords) {
+ if (secret.toLowerCase().contains(weakPassword)) {
+ throw new RenException(ErrorCode.MQTT_SECRET_WEAK_PASSWORD);
+ }
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dao/SysDictDataDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dao/SysDictDataDao.java
new file mode 100644
index 0000000..f91da6a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dao/SysDictDataDao.java
@@ -0,0 +1,32 @@
+package xiaozhi.modules.sys.dao;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Mapper;
+
+import org.apache.ibatis.annotations.Param;
+import xiaozhi.common.dao.BaseDao;
+import xiaozhi.modules.sys.entity.SysDictDataEntity;
+import xiaozhi.modules.sys.vo.SysDictDataItem;
+
+/**
+ * 瀛楀吀鏁版嵁
+ */
+@Mapper
+public interface SysDictDataDao extends BaseDao<SysDictDataEntity> {
+
+ List<SysDictDataItem> getDictDataByType(String dictType);
+
+ /**
+ * 鏍规嵁瀛楀吀绫诲瀷ID鑾峰彇瀛楀吀绫诲瀷缂栫爜
+ *
+ * @param dictTypeId 瀛楀吀绫诲瀷ID
+ * @return 瀛楀吀绫诲瀷缂栫爜
+ */
+ String getTypeByTypeId(Long dictTypeId);
+
+ /**
+ * 鏍规嵁瀛楀吀鏁版嵁ID闆嗗悎鑾峰彇瀛楀吀绫诲瀷缂栫爜闆嗗悎
+ */
+ List<String> getDictTypesByIdList(@Param("dictDataIdList") List<Long> dictDataIdList);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dao/SysDictTypeDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dao/SysDictTypeDao.java
new file mode 100644
index 0000000..6c271e3
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dao/SysDictTypeDao.java
@@ -0,0 +1,14 @@
+package xiaozhi.modules.sys.dao;
+
+import org.apache.ibatis.annotations.Mapper;
+
+import xiaozhi.common.dao.BaseDao;
+import xiaozhi.modules.sys.entity.SysDictTypeEntity;
+
+/**
+ * 瀛楀吀绫诲瀷
+ */
+@Mapper
+public interface SysDictTypeDao extends BaseDao<SysDictTypeEntity> {
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dao/SysParamsDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dao/SysParamsDao.java
new file mode 100644
index 0000000..6eebc8b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dao/SysParamsDao.java
@@ -0,0 +1,39 @@
+package xiaozhi.modules.sys.dao;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import xiaozhi.common.dao.BaseDao;
+import xiaozhi.modules.sys.entity.SysParamsEntity;
+
+/**
+ * 鍙傛暟绠$悊
+ */
+@Mapper
+public interface SysParamsDao extends BaseDao<SysParamsEntity> {
+ /**
+ * 鏍规嵁鍙傛暟缂栫爜锛屾煡璇alue
+ *
+ * @param paramCode 鍙傛暟缂栫爜
+ * @return 鍙傛暟鍊�
+ */
+ String getValueByCode(String paramCode);
+
+ /**
+ * 鑾峰彇鍙傛暟缂栫爜鍒楄〃
+ *
+ * @param ids ids
+ * @return 杩斿洖鍙傛暟缂栫爜鍒楄〃
+ */
+ List<String> getParamCodeList(String[] ids);
+
+ /**
+ * 鏍规嵁鍙傛暟缂栫爜锛屾洿鏂皏alue
+ *
+ * @param paramCode 鍙傛暟缂栫爜
+ * @param paramValue 鍙傛暟鍊�
+ */
+ int updateValueByCode(@Param("paramCode") String paramCode, @Param("paramValue") String paramValue);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dao/SysUserDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dao/SysUserDao.java
new file mode 100644
index 0000000..fb2a249
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dao/SysUserDao.java
@@ -0,0 +1,14 @@
+package xiaozhi.modules.sys.dao;
+
+import org.apache.ibatis.annotations.Mapper;
+
+import xiaozhi.common.dao.BaseDao;
+import xiaozhi.modules.sys.entity.SysUserEntity;
+
+/**
+ * 绯荤粺鐢ㄦ埛
+ */
+@Mapper
+public interface SysUserDao extends BaseDao<SysUserEntity> {
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/AdminPageUserDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/AdminPageUserDTO.java
new file mode 100644
index 0000000..a9d960a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/AdminPageUserDTO.java
@@ -0,0 +1,27 @@
+package xiaozhi.modules.sys.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.Min;
+import lombok.Data;
+
+/**
+ * 绠$悊鍛樺垎椤电敤鎴风殑鍙傛暟DTO
+ *
+ * @author zjy
+ * @since 2025-3-21
+ */
+@Data
+@Schema(description = "绠$悊鍛樺垎椤电敤鎴风殑鍙傛暟DTO")
+public class AdminPageUserDTO {
+
+ @Schema(description = "鎵嬫満鍙风爜")
+ private String mobile;
+
+ @Schema(description = "椤垫暟")
+ @Min(value = 0, message = "{sort.number}")
+ private String page;
+
+ @Schema(description = "鏄剧ず鍒楁暟")
+ @Min(value = 0, message = "{sort.number}")
+ private String limit;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/EmitSeverActionDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/EmitSeverActionDTO.java
new file mode 100644
index 0000000..6de89aa
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/EmitSeverActionDTO.java
@@ -0,0 +1,26 @@
+package xiaozhi.modules.sys.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import xiaozhi.modules.sys.enums.ServerActionEnum;
+
+/**
+ * 鍙戦�乸ython鏈嶅姟绔搷浣淒TO
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class EmitSeverActionDTO
+{
+ @Schema(description = "鐩爣ws鍦板潃")
+ @NotEmpty(message = "鐩爣ws鍦板潃涓嶈兘涓虹┖")
+ private String targetWs;
+
+ @Schema(description = "鎸囧畾鎿嶄綔")
+ @NotNull(message = "鎿嶄綔涓嶈兘涓虹┖")
+ private ServerActionEnum action;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/PasswordDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/PasswordDTO.java
new file mode 100644
index 0000000..4aabb10
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/PasswordDTO.java
@@ -0,0 +1,24 @@
+package xiaozhi.modules.sys.dto;
+
+import java.io.Serializable;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+/**
+ * 淇敼瀵嗙爜
+ */
+@Data
+@Schema(description = "淇敼瀵嗙爜")
+public class PasswordDTO implements Serializable {
+
+ @Schema(description = "鍘熷瘑鐮�")
+ @NotBlank(message = "{sysuser.password.require}")
+ private String password;
+
+ @Schema(description = "鏂板瘑鐮�")
+ @NotBlank(message = "{sysuser.password.require}")
+ private String newPassword;
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/RetrievePasswordDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/RetrievePasswordDTO.java
new file mode 100644
index 0000000..5475cf6
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/RetrievePasswordDTO.java
@@ -0,0 +1,34 @@
+package xiaozhi.modules.sys.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 鎵惧洖瀵嗙爜DTO
+ */
+@Data
+@Schema(description = "鎵惧洖瀵嗙爜")
+public class RetrievePasswordDTO implements Serializable {
+
+ @Schema(description = "鎵嬫満鍙风爜")
+ @NotBlank(message = "{sysuser.password.require}")
+ private String phone;
+
+ @Schema(description = "楠岃瘉鐮�")
+ @NotBlank(message = "{sysuser.password.require}")
+ private String code;
+
+ @Schema(description = "鏂板瘑鐮�")
+ @NotBlank(message = "{sysuser.password.require}")
+ private String password;
+
+ @Schema(description = "鍥惧舰楠岃瘉鐮両D")
+ @NotBlank(message = "{sysuser.uuid.require}")
+ private String captchaId;
+
+
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/ServerActionPayloadDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/ServerActionPayloadDTO.java
new file mode 100644
index 0000000..fb1427f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/ServerActionPayloadDTO.java
@@ -0,0 +1,36 @@
+package xiaozhi.modules.sys.dto;
+
+import lombok.Data;
+import xiaozhi.modules.sys.enums.ServerActionEnum;
+
+import java.util.Map;
+
+/**
+ * 鏈嶅姟绔姩浣淒TO
+ */
+@Data
+public class ServerActionPayloadDTO
+{
+ /**
+ * 绫诲瀷锛堟櫤鎺у彴鍙戝線鏈嶅姟绔殑閮芥槸server锛�
+ */
+ private String type;
+ /**
+ * 鍔ㄤ綔
+ */
+ private ServerActionEnum action;
+ /**
+ * 鍐呭
+ */
+ private Map<String, Object> content;
+
+ public static ServerActionPayloadDTO build(ServerActionEnum action, Map<String, Object> content) {
+ ServerActionPayloadDTO serverActionPayloadDTO = new ServerActionPayloadDTO();
+ serverActionPayloadDTO.setAction(action);
+ serverActionPayloadDTO.setContent(content);
+ serverActionPayloadDTO.setType("server");
+ return serverActionPayloadDTO;
+ }
+ // 绉佹湁鍖�
+ private ServerActionPayloadDTO() {}
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/ServerActionResponseDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/ServerActionResponseDTO.java
new file mode 100644
index 0000000..6286b74
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/ServerActionResponseDTO.java
@@ -0,0 +1,34 @@
+package xiaozhi.modules.sys.dto;
+
+import lombok.Data;
+import xiaozhi.modules.sys.enums.ServerActionResponseEnum;
+
+import java.util.Map;
+
+/**
+ * 鏈嶅姟绔姩浣滃搷搴斾綋
+ */
+@Data
+public class ServerActionResponseDTO
+{
+ private ServerActionResponseEnum status;
+ private String message;
+ private String type;
+ private Map<String, Object> content; // 鍚庣画杩欎釜瀛楁鍙互绉婚櫎锛屽苟鎶婅繖涓被浣滀负鍩虹被锛岄拡瀵逛笟鍔″啓鑷繁鐨刢ontent绫诲瀷
+ public static final String DEFAULT_TYPE_FORM_SERVER = "server";
+
+ public static Boolean isSuccess(ServerActionResponseDTO actionResponseDTO) {
+ System.out.println(actionResponseDTO);
+ if (actionResponseDTO == null) {
+ return false;
+ }
+ if (actionResponseDTO.getStatus() == null || !actionResponseDTO.getStatus().equals(ServerActionResponseEnum.SUCCESS)) {
+ return false;
+ }
+ Object actionType = actionResponseDTO.getContent().get("action");
+ if (actionType == null) {
+ return false;
+ }
+ return actionResponseDTO.getType() != null && actionResponseDTO.getType().equals(DEFAULT_TYPE_FORM_SERVER);
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/SysDictDataDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/SysDictDataDTO.java
new file mode 100644
index 0000000..c18dbf8
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/SysDictDataDTO.java
@@ -0,0 +1,59 @@
+package xiaozhi.modules.sys.dto;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Null;
+import lombok.Data;
+import xiaozhi.common.utils.DateUtils;
+import xiaozhi.common.validator.group.AddGroup;
+import xiaozhi.common.validator.group.DefaultGroup;
+import xiaozhi.common.validator.group.UpdateGroup;
+
+/**
+ * 瀛楀吀鏁版嵁
+ */
+@Data
+@Schema(description = "瀛楀吀鏁版嵁")
+public class SysDictDataDTO implements Serializable {
+
+ @Schema(description = "id")
+ @Null(message = "{id.null}", groups = AddGroup.class)
+ @NotNull(message = "{id.require}", groups = UpdateGroup.class)
+ private Long id;
+
+ @Schema(description = "瀛楀吀绫诲瀷ID")
+ @NotNull(message = "{sysdict.type.require}", groups = DefaultGroup.class)
+ private Long dictTypeId;
+
+ @Schema(description = "瀛楀吀鏍囩")
+ @NotBlank(message = "{sysdict.label.require}", groups = DefaultGroup.class)
+ private String dictLabel;
+
+ @Schema(description = "瀛楀吀鍊�")
+ private String dictValue;
+
+ @Schema(description = "澶囨敞")
+ private String remark;
+
+ @Schema(description = "鎺掑簭")
+ @Min(value = 0, message = "{sort.number}", groups = DefaultGroup.class)
+ private Integer sort;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+ @JsonFormat(pattern = DateUtils.DATE_TIME_PATTERN)
+ private Date createDate;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+ @JsonFormat(pattern = DateUtils.DATE_TIME_PATTERN)
+ private Date updateDate;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/SysDictTypeDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/SysDictTypeDTO.java
new file mode 100644
index 0000000..912c817
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/SysDictTypeDTO.java
@@ -0,0 +1,56 @@
+package xiaozhi.modules.sys.dto;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Null;
+import lombok.Data;
+import xiaozhi.common.utils.DateUtils;
+import xiaozhi.common.validator.group.AddGroup;
+import xiaozhi.common.validator.group.DefaultGroup;
+import xiaozhi.common.validator.group.UpdateGroup;
+
+/**
+ * 瀛楀吀绫诲瀷
+ */
+@Data
+@Schema(description = "瀛楀吀绫诲瀷")
+public class SysDictTypeDTO implements Serializable {
+
+ @Schema(description = "id")
+ @Null(message = "{id.null}", groups = AddGroup.class)
+ @NotNull(message = "{id.require}", groups = UpdateGroup.class)
+ private Long id;
+
+ @Schema(description = "瀛楀吀绫诲瀷")
+ @NotBlank(message = "{sysdict.type.require}", groups = DefaultGroup.class)
+ private String dictType;
+
+ @Schema(description = "瀛楀吀鍚嶇О")
+ @NotBlank(message = "{sysdict.name.require}", groups = DefaultGroup.class)
+ private String dictName;
+
+ @Schema(description = "澶囨敞")
+ private String remark;
+
+ @Schema(description = "鎺掑簭")
+ @Min(value = 0, message = "{sort.number}", groups = DefaultGroup.class)
+ private Integer sort;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+ @JsonFormat(pattern = DateUtils.DATE_TIME_PATTERN)
+ private Date createDate;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+ @JsonFormat(pattern = DateUtils.DATE_TIME_PATTERN)
+ private Date updateDate;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/SysParamsDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/SysParamsDTO.java
new file mode 100644
index 0000000..a6f27b7
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/SysParamsDTO.java
@@ -0,0 +1,58 @@
+package xiaozhi.modules.sys.dto;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Null;
+import jakarta.validation.constraints.Pattern;
+import lombok.Data;
+import xiaozhi.common.utils.DateUtils;
+import xiaozhi.common.validator.group.AddGroup;
+import xiaozhi.common.validator.group.DefaultGroup;
+import xiaozhi.common.validator.group.UpdateGroup;
+
+/**
+ * 鍙傛暟绠$悊
+ */
+@Data
+@Schema(description = "鍙傛暟绠$悊")
+public class SysParamsDTO implements Serializable {
+
+ @Schema(description = "id")
+ @Null(message = "{id.null}", groups = AddGroup.class)
+ @NotNull(message = "{id.require}", groups = UpdateGroup.class)
+ private Long id;
+
+ @Schema(description = "鍙傛暟缂栫爜")
+ @NotBlank(message = "{sysparams.paramcode.require}", groups = DefaultGroup.class)
+ private String paramCode;
+
+ @Schema(description = "鍙傛暟鍊�")
+ @NotBlank(message = "{sysparams.paramvalue.require}", groups = DefaultGroup.class)
+ private String paramValue;
+
+ @Schema(description = "鍊肩被鍨�")
+ @NotBlank(message = "{sysparams.valuetype.require}", groups = DefaultGroup.class)
+ @Pattern(regexp = "^(string|number|boolean|array|json)$", message = "{sysparams.valuetype.pattern}", groups = DefaultGroup.class)
+ private String valueType;
+
+ @Schema(description = "澶囨敞")
+ private String remark;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+ @JsonFormat(pattern = DateUtils.DATE_TIME_PATTERN)
+ private Date createDate;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+ @JsonFormat(pattern = DateUtils.DATE_TIME_PATTERN)
+ private Date updateDate;
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/SysUserDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/SysUserDTO.java
new file mode 100644
index 0000000..ef5f71b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/dto/SysUserDTO.java
@@ -0,0 +1,87 @@
+package xiaozhi.modules.sys.dto;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+import org.hibernate.validator.constraints.Range;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Null;
+import lombok.Data;
+import xiaozhi.common.utils.DateUtils;
+import xiaozhi.common.validator.group.AddGroup;
+import xiaozhi.common.validator.group.DefaultGroup;
+import xiaozhi.common.validator.group.UpdateGroup;
+
+/**
+ * 鐢ㄦ埛绠$悊
+ */
+@Data
+@Schema(description = "鐢ㄦ埛绠$悊")
+public class SysUserDTO implements Serializable {
+ @Schema(description = "id")
+ @Null(message = "{id.null}", groups = AddGroup.class)
+ @NotNull(message = "{id.require}", groups = UpdateGroup.class)
+ private Long id;
+
+ @Schema(description = "鐢ㄦ埛鍚�", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotBlank(message = "{sysuser.username.require}", groups = DefaultGroup.class)
+ private String username;
+
+ @Schema(description = "瀵嗙爜")
+ @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
+ @NotBlank(message = "{sysuser.password.require}", groups = AddGroup.class)
+ private String password;
+
+ @Schema(description = "濮撳悕", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotBlank(message = "{sysuser.realname.require}", groups = DefaultGroup.class)
+ private String realName;
+
+ @Schema(description = "澶村儚")
+ private String headUrl;
+
+ @Schema(description = "鎬у埆 0锛氱敺 1锛氬コ 2锛氫繚瀵�", requiredMode = Schema.RequiredMode.REQUIRED)
+ @Range(min = 0, max = 2, message = "{sysuser.gender.range}", groups = DefaultGroup.class)
+ private Integer gender;
+
+ @Schema(description = "閭")
+ @Email(message = "{sysuser.email.error}", groups = DefaultGroup.class)
+ private String email;
+
+ @Schema(description = "鎵嬫満鍙�")
+ private String mobile;
+
+ @Schema(description = "閮ㄩ棬ID", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "{sysuser.deptId.require}", groups = DefaultGroup.class)
+ private Long deptId;
+
+ @Schema(description = "鐘舵�� 0锛氬仠鐢� 1锛氭甯�", requiredMode = Schema.RequiredMode.REQUIRED)
+ @Range(min = 0, max = 1, message = "{sysuser.status.range}", groups = DefaultGroup.class)
+ private Integer status;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+ @JsonFormat(pattern = DateUtils.DATE_TIME_PATTERN)
+ private Date createDate;
+
+ @Schema(description = "瓒呯骇绠$悊鍛� 0锛氬惁 1锛氭槸")
+ @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+ private Integer superAdmin;
+
+ @Schema(description = "瑙掕壊ID鍒楄〃")
+ private List<Long> roleIdList;
+
+ @Schema(description = "宀椾綅ID鍒楄〃")
+ private List<Long> postIdList;
+
+ @Schema(description = "閮ㄩ棬鍚嶇О")
+ private String deptName;
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/entity/SysDictDataEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/entity/SysDictDataEntity.java
new file mode 100644
index 0000000..16d3dca
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/entity/SysDictDataEntity.java
@@ -0,0 +1,50 @@
+package xiaozhi.modules.sys.entity;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import xiaozhi.common.entity.BaseEntity;
+
+/**
+ * 鏁版嵁瀛楀吀
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("sys_dict_data")
+public class SysDictDataEntity extends BaseEntity {
+ /**
+ * 瀛楀吀绫诲瀷ID
+ */
+ private Long dictTypeId;
+ /**
+ * 瀛楀吀鏍囩
+ */
+ private String dictLabel;
+ /**
+ * 瀛楀吀鍊�
+ */
+ private String dictValue;
+ /**
+ * 澶囨敞
+ */
+ private String remark;
+ /**
+ * 鎺掑簭
+ */
+ private Integer sort;
+ /**
+ * 鏇存柊鑰�
+ */
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private Long updater;
+ /**
+ * 鏇存柊鏃堕棿
+ */
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private Date updateDate;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/entity/SysDictTypeEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/entity/SysDictTypeEntity.java
new file mode 100644
index 0000000..350d857
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/entity/SysDictTypeEntity.java
@@ -0,0 +1,46 @@
+package xiaozhi.modules.sys.entity;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import xiaozhi.common.entity.BaseEntity;
+
+/**
+ * 瀛楀吀绫诲瀷
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("sys_dict_type")
+public class SysDictTypeEntity extends BaseEntity {
+ /**
+ * 瀛楀吀绫诲瀷缂栫爜
+ */
+ private String dictType;
+ /**
+ * 瀛楀吀鍚嶇О
+ */
+ private String dictName;
+ /**
+ * 澶囨敞
+ */
+ private String remark;
+ /**
+ * 鎺掑簭
+ */
+ private Integer sort;
+ /**
+ * 鏇存柊鑰�
+ */
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private Long updater;
+ /**
+ * 鏇存柊鏃堕棿
+ */
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private Date updateDate;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/entity/SysParamsEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/entity/SysParamsEntity.java
new file mode 100644
index 0000000..c51c46f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/entity/SysParamsEntity.java
@@ -0,0 +1,51 @@
+package xiaozhi.modules.sys.entity;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import xiaozhi.common.entity.BaseEntity;
+
+/**
+ * 鍙傛暟绠$悊
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("sys_params")
+public class SysParamsEntity extends BaseEntity {
+ /**
+ * 鍙傛暟缂栫爜
+ */
+ private String paramCode;
+ /**
+ * 鍙傛暟鍊�
+ */
+ private String paramValue;
+ /**
+ * 鍊肩被鍨嬶細string-瀛楃涓诧紝number-鏁板瓧锛宐oolean-甯冨皵锛宎rray-鏁扮粍
+ */
+ private String valueType;
+ /**
+ * 绫诲瀷 0锛氱郴缁熷弬鏁� 1锛氶潪绯荤粺鍙傛暟
+ */
+ private Integer paramType;
+ /**
+ * 澶囨敞
+ */
+ private String remark;
+ /**
+ * 鏇存柊鑰�
+ */
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private Long updater;
+ /**
+ * 鏇存柊鏃堕棿
+ */
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private Date updateDate;
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/entity/SysUserEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/entity/SysUserEntity.java
new file mode 100644
index 0000000..23043a3
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/entity/SysUserEntity.java
@@ -0,0 +1,47 @@
+package xiaozhi.modules.sys.entity;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import xiaozhi.common.entity.BaseEntity;
+
+/**
+ * 绯荤粺鐢ㄦ埛
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("sys_user")
+public class SysUserEntity extends BaseEntity {
+ /**
+ * 鐢ㄦ埛鍚�
+ */
+ private String username;
+ /**
+ * 瀵嗙爜
+ */
+ private String password;
+ /**
+ * 瓒呯骇绠$悊鍛� 0锛氬惁 1锛氭槸
+ */
+ private Integer superAdmin;
+ /**
+ * 鐘舵�� 0锛氬仠鐢� 1锛氭甯�
+ */
+ private Integer status;
+ /**
+ * 鏇存柊鑰�
+ */
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private Long updater;
+ /**
+ * 鏇存柊鏃堕棿
+ */
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private Date updateDate;
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/enums/ServerActionEnum.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/enums/ServerActionEnum.java
new file mode 100644
index 0000000..ec3ce2f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/enums/ServerActionEnum.java
@@ -0,0 +1,33 @@
+package xiaozhi.modules.sys.enums;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * 鏈嶅姟绔姩浣滄灇涓�
+ */
+public enum ServerActionEnum {
+ RESTART("restart"),
+ UPDATE_CONFIG("update_config");
+
+ private final String value;
+
+ ServerActionEnum(String value) {
+ this.value = value;
+ }
+
+ @JsonValue
+ public String getValue() {
+ return value;
+ }
+
+ @JsonCreator
+ public static ServerActionEnum fromValue(String value) {
+ for (ServerActionEnum action : ServerActionEnum.values()) {
+ if (action.value.equalsIgnoreCase(value)) {
+ return action;
+ }
+ }
+ return null;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/enums/ServerActionResponseEnum.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/enums/ServerActionResponseEnum.java
new file mode 100644
index 0000000..040b208
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/enums/ServerActionResponseEnum.java
@@ -0,0 +1,45 @@
+package xiaozhi.modules.sys.enums;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * 鏈嶅姟绔皟鐢ㄥ搷搴旀灇涓�
+ */
+public enum ServerActionResponseEnum {
+ SUCCESS("success"), FAIL("fail");
+
+ private final String value;
+
+ ServerActionResponseEnum(String value) {
+ this.value = value;
+ }
+
+ @JsonValue
+ public String getValue() {
+ return value;
+ }
+
+ @JsonCreator
+ public static ServerActionResponseEnum fromValue(String value) {
+ ServerActionResponseEnum byValue = getByValue(value);
+ if (byValue == null) {
+ throw new IllegalArgumentException("Unknown enum value: " + value);
+ }
+ return byValue;
+ }
+
+ public static ServerActionResponseEnum getByValue(String value) {
+ if (StringUtils.isBlank(value)) {
+ return null;
+ }
+ for (ServerActionResponseEnum action : ServerActionResponseEnum.values()) {
+ if (action.value.equals(value)) {
+ return action;
+ }
+ }
+ return null;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/enums/SuperAdminEnum.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/enums/SuperAdminEnum.java
new file mode 100644
index 0000000..655f46e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/enums/SuperAdminEnum.java
@@ -0,0 +1,19 @@
+package xiaozhi.modules.sys.enums;
+
+/**
+ * 瓒呯骇绠$悊鍛樻灇涓�
+ */
+public enum SuperAdminEnum {
+ YES(1),
+ NO(0);
+
+ private int value;
+
+ SuperAdminEnum(int value) {
+ this.value = value;
+ }
+
+ public int value() {
+ return this.value;
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/redis/SysParamsRedis.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/redis/SysParamsRedis.java
new file mode 100644
index 0000000..2e2bdb4
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/redis/SysParamsRedis.java
@@ -0,0 +1,35 @@
+package xiaozhi.modules.sys.redis;
+
+import org.springframework.stereotype.Component;
+
+import lombok.AllArgsConstructor;
+import xiaozhi.common.redis.RedisKeys;
+import xiaozhi.common.redis.RedisUtils;
+
+/**
+ * 鍙傛暟绠$悊
+ */
+@AllArgsConstructor
+@Component
+public class SysParamsRedis {
+ private final RedisUtils redisUtils;
+
+ public void delete(Object[] paramCodes) {
+ String key = RedisKeys.getSysParamsKey();
+ redisUtils.hDel(key, paramCodes);
+ }
+
+ public void set(String paramCode, String paramValue) {
+ if (paramValue == null) {
+ return;
+ }
+ String key = RedisKeys.getSysParamsKey();
+ redisUtils.hSet(key, paramCode, paramValue);
+ }
+
+ public String get(String paramCode) {
+ String key = RedisKeys.getSysParamsKey();
+ return (String) redisUtils.hGet(key, paramCode);
+ }
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysDictDataService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysDictDataService.java
new file mode 100644
index 0000000..ea89844
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysDictDataService.java
@@ -0,0 +1,70 @@
+package xiaozhi.modules.sys.service;
+
+import java.util.List;
+import java.util.Map;
+
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.service.BaseService;
+import xiaozhi.modules.sys.dto.SysDictDataDTO;
+import xiaozhi.modules.sys.entity.SysDictDataEntity;
+import xiaozhi.modules.sys.vo.SysDictDataItem;
+import xiaozhi.modules.sys.vo.SysDictDataVO;
+
+/**
+ * 鏁版嵁瀛楀吀
+ */
+public interface SysDictDataService extends BaseService<SysDictDataEntity> {
+
+ /**
+ * 鍒嗛〉鏌ヨ鏁版嵁瀛楀吀淇℃伅
+ *
+ * @param params 鏌ヨ鍙傛暟锛屽寘鍚垎椤典俊鎭拰鏌ヨ鏉′欢
+ * @return 杩斿洖鏁版嵁瀛楀吀鐨勫垎椤垫煡璇㈢粨鏋�
+ */
+ PageData<SysDictDataVO> page(Map<String, Object> params);
+
+ /**
+ * 鏍规嵁ID鑾峰彇鏁版嵁瀛楀吀瀹炰綋
+ *
+ * @param id 鏁版嵁瀛楀吀瀹炰綋鐨勫敮涓�鏍囪瘑
+ * @return 杩斿洖鏁版嵁瀛楀吀瀹炰綋鐨勮缁嗕俊鎭�
+ */
+ SysDictDataVO get(Long id);
+
+ /**
+ * 淇濆瓨鏂扮殑鏁版嵁瀛楀吀椤�
+ *
+ * @param dto 鏁版嵁瀛楀吀椤圭殑淇濆瓨鏁版嵁浼犺緭瀵硅薄
+ */
+ void save(SysDictDataDTO dto);
+
+ /**
+ * 鏇存柊鏁版嵁瀛楀吀椤�
+ *
+ * @param dto 鏁版嵁瀛楀吀椤圭殑鏇存柊鏁版嵁浼犺緭瀵硅薄
+ */
+ void update(SysDictDataDTO dto);
+
+ /**
+ * 鍒犻櫎鏁版嵁瀛楀吀椤�
+ *
+ * @param ids 瑕佸垹闄ょ殑鏁版嵁瀛楀吀椤圭殑ID鏁扮粍
+ */
+ void delete(Long[] ids);
+
+ /**
+ * 鏍规嵁瀛楀吀绫诲瀷ID鍒犻櫎瀵瑰簲鐨勫瓧鍏告暟鎹�
+ *
+ * @param dictTypeId 瀛楀吀绫诲瀷ID
+ */
+ void deleteByTypeId(Long dictTypeId);
+
+ /**
+ * 鏍规嵁瀛楀吀绫诲瀷鑾峰彇瀛楀吀鏁版嵁鍒楄〃
+ *
+ * @param dictType 瀛楀吀绫诲瀷
+ * @return 杩斿洖瀛楀吀鏁版嵁鍒楄〃
+ */
+ List<SysDictDataItem> getDictDataByType(String dictType);
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysDictTypeService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysDictTypeService.java
new file mode 100644
index 0000000..697b2d1
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysDictTypeService.java
@@ -0,0 +1,60 @@
+package xiaozhi.modules.sys.service;
+
+import java.util.List;
+import java.util.Map;
+
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.service.BaseService;
+import xiaozhi.modules.sys.dto.SysDictTypeDTO;
+import xiaozhi.modules.sys.entity.SysDictTypeEntity;
+import xiaozhi.modules.sys.vo.SysDictTypeVO;
+
+/**
+ * 鏁版嵁瀛楀吀
+ */
+public interface SysDictTypeService extends BaseService<SysDictTypeEntity> {
+
+ /**
+ * 鍒嗛〉鏌ヨ瀛楀吀绫诲瀷淇℃伅
+ *
+ * @param params 鏌ヨ鍙傛暟锛屽寘鍚垎椤典俊鎭拰鏌ヨ鏉′欢
+ * @return 杩斿洖鍒嗛〉鐨勫瓧鍏哥被鍨嬫暟鎹�
+ */
+ PageData<SysDictTypeVO> page(Map<String, Object> params);
+
+ /**
+ * 鏍规嵁ID鑾峰彇瀛楀吀绫诲瀷淇℃伅
+ *
+ * @param id 瀛楀吀绫诲瀷ID
+ * @return 杩斿洖瀛楀吀绫诲瀷瀵硅薄
+ */
+ SysDictTypeVO get(Long id);
+
+ /**
+ * 淇濆瓨瀛楀吀绫诲瀷淇℃伅
+ *
+ * @param dto 瀛楀吀绫诲瀷鏁版嵁浼犺緭瀵硅薄
+ */
+ void save(SysDictTypeDTO dto);
+
+ /**
+ * 鏇存柊瀛楀吀绫诲瀷淇℃伅
+ *
+ * @param dto 瀛楀吀绫诲瀷鏁版嵁浼犺緭瀵硅薄
+ */
+ void update(SysDictTypeDTO dto);
+
+ /**
+ * 鍒犻櫎瀛楀吀绫诲瀷淇℃伅
+ *
+ * @param ids 瑕佸垹闄ょ殑瀛楀吀绫诲瀷ID鏁扮粍
+ */
+ void delete(Long[] ids);
+
+ /**
+ * 鍒楀嚭鎵�鏈夊瓧鍏哥被鍨嬩俊鎭�
+ *
+ * @return 杩斿洖瀛楀吀绫诲瀷鍒楄〃
+ */
+ List<SysDictTypeVO> list(Map<String, Object> params);
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysParamsService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysParamsService.java
new file mode 100644
index 0000000..70ff417
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysParamsService.java
@@ -0,0 +1,71 @@
+package xiaozhi.modules.sys.service;
+
+import java.util.List;
+import java.util.Map;
+
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.service.BaseService;
+import xiaozhi.modules.sys.dto.SysParamsDTO;
+import xiaozhi.modules.sys.entity.SysParamsEntity;
+
+/**
+ * 鍙傛暟绠$悊
+ */
+public interface SysParamsService extends BaseService<SysParamsEntity> {
+
+ PageData<SysParamsDTO> page(Map<String, Object> params);
+
+ List<SysParamsDTO> list(Map<String, Object> params);
+
+ SysParamsDTO get(Long id);
+
+ void save(SysParamsDTO dto);
+
+ void update(SysParamsDTO dto);
+
+ void delete(String[] ids);
+
+ /**
+ * 鏍规嵁鍙傛暟缂栫爜锛岃幏鍙栧弬鏁扮殑value鍊�
+ *
+ * @param paramCode 鍙傛暟缂栫爜
+ * @param fromCache 鏄惁浠庣紦瀛樹腑鑾峰彇
+ */
+ String getValue(String paramCode, Boolean fromCache);
+
+ /**
+ * 鏍规嵁鍙傛暟缂栫爜锛岃幏鍙杤alue鐨凮bject瀵硅薄
+ *
+ * @param paramCode 鍙傛暟缂栫爜
+ * @param clazz Object瀵硅薄
+ */
+ <T> T getValueObject(String paramCode, Class<T> clazz);
+
+ /**
+ * 鏍规嵁鍙傛暟缂栫爜锛屾洿鏂皏alue
+ *
+ * @param paramCode 鍙傛暟缂栫爜
+ * @param paramValue 鍙傛暟鍊�
+ */
+ int updateValueByCode(String paramCode, String paramValue);
+
+ /**
+ * 鍒濆鍖栨湇鍔″櫒瀵嗛挜
+ */
+ void initServerSecret();
+
+ /**
+ * 鑾峰彇绯荤粺鍔熻兘鑿滃崟閰嶇疆
+ *
+ * @param fromCache 鏄惁浠庣紦瀛樿幏鍙�
+ * @return 绯荤粺鍔熻兘鑿滃崟閰嶇疆JSON瀛楃涓�
+ */
+ String getSystemWebMenu(boolean fromCache);
+
+ /**
+ * 鏇存柊绯荤粺鍔熻兘鑿滃崟閰嶇疆锛堣嚜鍔ㄥ鐞嗗姛鑳界浉鍏崇殑鎻掍欢娓呯悊锛�
+ *
+ * @param configJson 鏂扮殑绯荤粺鍔熻兘鑿滃崟閰嶇疆JSON
+ */
+ void updateSystemWebMenu(String configJson);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysUserService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysUserService.java
new file mode 100644
index 0000000..7a35da3
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysUserService.java
@@ -0,0 +1,75 @@
+package xiaozhi.modules.sys.service;
+
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.service.BaseService;
+import xiaozhi.modules.sys.dto.AdminPageUserDTO;
+import xiaozhi.modules.sys.dto.PasswordDTO;
+import xiaozhi.modules.sys.dto.SysUserDTO;
+import xiaozhi.modules.sys.entity.SysUserEntity;
+import xiaozhi.modules.sys.vo.AdminPageUserVO;
+
+/**
+ * 绯荤粺鐢ㄦ埛
+ */
+public interface SysUserService extends BaseService<SysUserEntity> {
+
+ SysUserDTO getByUsername(String username);
+
+ SysUserDTO getByUserId(Long userId);
+
+ void save(SysUserDTO dto);
+
+ /**
+ * 鍒犻櫎鎸囧畾鐢ㄦ埛锛屼笖鏈夊叧鑱旂殑鏁版嵁璁惧鍜屾櫤鑳戒綋
+ *
+ * @param ids
+ */
+ void deleteById(Long ids);
+
+ /**
+ * 楠岃瘉鏄惁鍏佽淇敼瀵嗙爜鏇存敼
+ *
+ * @param userId 鐢ㄦ埛id
+ * @param passwordDTO 楠岃瘉瀵嗙爜鐨勫弬鏁�
+ */
+ void changePassword(Long userId, PasswordDTO passwordDTO);
+
+ /**
+ * 鐩存帴淇敼瀵嗙爜锛屼笉闇�瑕侀獙璇�
+ *
+ * @param userId 鐢ㄦ埛id
+ * @param password 瀵嗙爜
+ */
+ void changePasswordDirectly(Long userId, String password);
+
+ /**
+ * 閲嶇疆瀵嗙爜
+ *
+ * @param userId 鐢ㄦ埛id
+ * @return 闅忔満鐢熸垚绗﹀悎瑙勮寖鐨勫瘑鐮�
+ */
+ String resetPassword(Long userId);
+
+ /**
+ * 绠$悊鍛樺垎椤电敤鎴蜂俊鎭�
+ *
+ * @param dto 鍒嗛〉鏌ユ壘鍙傛暟
+ * @return 鐢ㄦ埛鍒楄〃鍒嗛〉鏁版嵁
+ */
+ PageData<AdminPageUserVO> page(AdminPageUserDTO dto);
+
+ /**
+ * 鎵归噺淇敼鐢ㄦ埛鐘舵��
+ *
+ * @param status 鐢ㄦ埛鐘舵��
+ * @param userIds 鐢ㄦ埛ID鏁扮粍
+ */
+ void changeStatus(Integer status, String[] userIds);
+
+ /**
+ * 鑾峰彇鏄惁鍏佽鐢ㄦ埛娉ㄥ唽
+ *
+ * @return 鏄惁鍏佽鐢ㄦ埛娉ㄥ唽
+ */
+ boolean getAllowUserRegister();
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysUserUtilService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysUserUtilService.java
new file mode 100644
index 0000000..0fa3fc4
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysUserUtilService.java
@@ -0,0 +1,19 @@
+package xiaozhi.modules.sys.service;
+
+
+import java.util.function.Consumer;
+
+/**
+ * 瀹氫箟涓�涓郴缁熺敤鎴峰伐鍏风被锛岄伩鍏嶅拰鐢ㄦ埛妯″潡寰幆渚濊禆
+ * 濡傜敤鎴峰拰璁惧浜掔浉渚濊禆锛岀敤鎴烽渶瑕佽幏鍙栨墍鏈夎澶囷紝璁惧鍙堥渶瑕佽幏鍙栨瘡涓澶囩殑鐢ㄦ埛鍚�
+ * @author zjy
+ * @since 2025-4-2
+ */
+public interface SysUserUtilService {
+ /**
+ * 璧嬪�肩敤鎴峰悕
+ * @param userId 鐢ㄦ埛id
+ * @param setter 璧嬪�兼柟娉�
+ */
+ void assignUsername( Long userId, Consumer<String> setter);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/TokenService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/TokenService.java
new file mode 100644
index 0000000..d3839f7
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/TokenService.java
@@ -0,0 +1,11 @@
+package xiaozhi.modules.sys.service;
+
+public interface TokenService {
+ /**
+ * 鐢熸垚token
+ *
+ * @param userId
+ * @return
+ */
+ String createToken(long userId);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysDictDataServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysDictDataServiceImpl.java
new file mode 100644
index 0000000..4d92096
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysDictDataServiceImpl.java
@@ -0,0 +1,189 @@
+package xiaozhi.modules.sys.service.impl;
+
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+
+import cn.hutool.core.collection.CollUtil;
+import lombok.AllArgsConstructor;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.redis.RedisKeys;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.common.service.impl.BaseServiceImpl;
+import xiaozhi.common.utils.ConvertUtils;
+import xiaozhi.common.utils.JsonUtils;
+import xiaozhi.modules.sys.dao.SysDictDataDao;
+import xiaozhi.modules.sys.dao.SysUserDao;
+import xiaozhi.modules.sys.dto.SysDictDataDTO;
+import xiaozhi.modules.sys.entity.SysDictDataEntity;
+import xiaozhi.modules.sys.entity.SysUserEntity;
+import xiaozhi.modules.sys.service.SysDictDataService;
+import xiaozhi.modules.sys.vo.SysDictDataItem;
+import xiaozhi.modules.sys.vo.SysDictDataVO;
+
+/**
+ * 瀛楀吀绫诲瀷
+ */
+@Service
+@AllArgsConstructor
+public class SysDictDataServiceImpl extends BaseServiceImpl<SysDictDataDao, SysDictDataEntity>
+ implements SysDictDataService {
+ private final SysUserDao sysUserDao;
+ private final RedisUtils redisUtils;
+
+ @Override
+ public PageData<SysDictDataVO> page(Map<String, Object> params) {
+ IPage<SysDictDataEntity> page = baseDao.selectPage(getPage(params, "sort", true), getWrapper(params));
+
+ PageData<SysDictDataVO> pageData = getPageData(page, SysDictDataVO.class);
+
+ setUserName(pageData.getList());
+
+ return pageData;
+ }
+
+ private QueryWrapper<SysDictDataEntity> getWrapper(Map<String, Object> params) {
+ String dictTypeId = (String) params.get("dictTypeId");
+ String dictLabel = (String) params.get("dictLabel");
+ String dictValue = (String) params.get("dictValue");
+
+ QueryWrapper<SysDictDataEntity> wrapper = new QueryWrapper<>();
+ wrapper.eq("dict_type_id", Long.parseLong(dictTypeId));
+ wrapper.like(StringUtils.isNotBlank(dictLabel), "dict_label", dictLabel);
+ wrapper.like(StringUtils.isNotBlank(dictValue), "dict_value", dictValue);
+
+ return wrapper;
+ }
+
+ @Override
+ public SysDictDataVO get(Long id) {
+ SysDictDataEntity entity = baseDao.selectById(id);
+
+ return ConvertUtils.sourceToTarget(entity, SysDictDataVO.class);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void save(SysDictDataDTO dto) {
+ // 鐩稿悓瀛楀吀绫诲瀷鐨勬爣绛句笉鑳界浉鍚�
+ checkDictValueUnique(dto.getDictTypeId(), dto.getDictValue(), null);
+
+ SysDictDataEntity entity = ConvertUtils.sourceToTarget(dto, SysDictDataEntity.class);
+
+ insert(entity);
+ // 鍒犻櫎Redis缂撳瓨
+ String dictType = baseDao.getTypeByTypeId(dto.getDictTypeId());
+ redisUtils.delete(RedisKeys.getDictDataByTypeKey(dictType));
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void update(SysDictDataDTO dto) {
+ // 鐩稿悓瀛楀吀绫诲瀷鐨勬爣绛句笉鑳界浉鍚�
+ checkDictValueUnique(dto.getDictTypeId(), dto.getDictValue(), String.valueOf(dto.getId()));
+
+ SysDictDataEntity entity = ConvertUtils.sourceToTarget(dto, SysDictDataEntity.class);
+
+ updateById(entity);
+ // 鍒犻櫎Redis缂撳瓨
+ String dictType = baseDao.getTypeByTypeId(dto.getDictTypeId());
+ redisUtils.delete(RedisKeys.getDictDataByTypeKey(dictType));
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void delete(Long[] ids) {
+ List<Long> idList = Arrays.asList(ids);
+ if (CollUtil.isNotEmpty(idList)) {
+ //鎵归噺鍒犻櫎redis瀛楀吀
+ List<String> redisKeyList = new ArrayList<>();
+ //鎵归噺鑾峰彇瀛楀吀绫诲瀷
+ List<String> dictTypeList = Optional.ofNullable(baseDao.getDictTypesByIdList(idList)).orElseGet(ArrayList::new);
+ dictTypeList.forEach(dictType -> redisKeyList.add(RedisKeys.getDictDataByTypeKey(dictType)));
+ if (CollUtil.isNotEmpty(redisKeyList)) {
+ //娓呴櫎缂撳瓨
+ redisUtils.delete(redisKeyList);
+ }
+ //鎵归噺鍒犻櫎瀛楀吀鏁版嵁
+ deleteBatchIds(Arrays.asList(ids));
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteByTypeId(Long dictTypeId) {
+ LambdaQueryWrapper<SysDictDataEntity> wrapper = new LambdaQueryWrapper<>();
+ wrapper.eq(SysDictDataEntity::getDictTypeId, dictTypeId);
+ baseDao.delete(wrapper);
+ }
+
+ /**
+ * 璁剧疆鐢ㄦ埛鍚�
+ *
+ * @param sysDictDataList 瀛楀吀绫诲瀷闆嗗悎
+ */
+ private void setUserName(List<SysDictDataVO> sysDictDataList) {
+ // 鏀堕泦鎵�鏈夌敤鎴� ID
+ Set<Long> userIds = sysDictDataList.stream().flatMap(vo -> Stream.of(vo.getCreator(), vo.getUpdater()))
+ .filter(Objects::nonNull).collect(Collectors.toSet());
+
+ // 璁剧疆鏇存柊鑰呭拰鍒涘缓鑰呭悕绉�
+ if (!userIds.isEmpty()) {
+ List<SysUserEntity> sysUserEntities = sysUserDao.selectByIds(userIds);
+ // 鎶奓ist杞垚Map锛孧ap<Long, String>
+ Map<Long, String> userNameMap = sysUserEntities.stream().collect(Collectors.toMap(SysUserEntity::getId,
+ SysUserEntity::getUsername, (existing, replacement) -> existing));
+
+ sysDictDataList.forEach(vo -> {
+ vo.setCreatorName(userNameMap.get(vo.getCreator()));
+ vo.setUpdaterName(userNameMap.get(vo.getUpdater()));
+ });
+ }
+ }
+
+ private void checkDictValueUnique(Long dictTypeId, String dictValue, String excludeId) {
+ LambdaQueryWrapper<SysDictDataEntity> queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(SysDictDataEntity::getDictTypeId, dictTypeId).eq(SysDictDataEntity::getDictLabel, dictValue);
+ if (StringUtils.isNotBlank(excludeId)) {
+ queryWrapper.ne(SysDictDataEntity::getId, excludeId);
+ }
+ boolean exists = baseDao.exists(queryWrapper);
+ if (exists) {
+ throw new RenException(ErrorCode.DICT_LABEL_DUPLICATE);
+ }
+ }
+
+ @Override
+ public List<SysDictDataItem> getDictDataByType(String dictType) {
+ if (StringUtils.isBlank(dictType)) {
+ return null;
+ }
+
+ // 鍏堜粠Redis鑾峰彇缂撳瓨
+ String key = RedisKeys.getDictDataByTypeKey(dictType);
+ List<SysDictDataItem> cachedData = JsonUtils.toList(redisUtils.get(key), SysDictDataItem.class);
+ if (cachedData != null) {
+ return cachedData;
+ }
+
+ // 濡傛灉缂撳瓨涓病鏈夛紝鍒欎粠鏁版嵁搴撹幏鍙�
+ List<SysDictDataItem> data = baseDao.getDictDataByType(dictType);
+
+ // 瀛樺叆Redis缂撳瓨
+ if (data != null) {
+ redisUtils.set(key, data);
+ }
+
+ return data;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysDictTypeServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysDictTypeServiceImpl.java
new file mode 100644
index 0000000..56b2ca0
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysDictTypeServiceImpl.java
@@ -0,0 +1,154 @@
+package xiaozhi.modules.sys.service.impl;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+
+import lombok.AllArgsConstructor;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.service.impl.BaseServiceImpl;
+import xiaozhi.common.utils.ConvertUtils;
+import xiaozhi.modules.sys.dao.SysDictTypeDao;
+import xiaozhi.modules.sys.dao.SysUserDao;
+import xiaozhi.modules.sys.dto.SysDictTypeDTO;
+import xiaozhi.modules.sys.entity.SysDictTypeEntity;
+import xiaozhi.modules.sys.entity.SysUserEntity;
+import xiaozhi.modules.sys.service.SysDictDataService;
+import xiaozhi.modules.sys.service.SysDictTypeService;
+import xiaozhi.modules.sys.vo.SysDictTypeVO;
+
+/**
+ * 瀛楀吀绫诲瀷
+ */
+@Service
+@AllArgsConstructor
+public class SysDictTypeServiceImpl extends BaseServiceImpl<SysDictTypeDao, SysDictTypeEntity>
+ implements SysDictTypeService {
+ private final SysUserDao sysUserDao;
+ private final SysDictDataService sysDictDataService;
+
+ @Override
+ public PageData<SysDictTypeVO> page(Map<String, Object> params) {
+ IPage<SysDictTypeEntity> page = baseDao.selectPage(getPage(params, "sort", true), getWrapper(params));
+
+ PageData<SysDictTypeVO> pageData = getPageData(page, SysDictTypeVO.class);
+
+ setUserName(pageData.getList());
+
+ return pageData;
+ }
+
+ private QueryWrapper<SysDictTypeEntity> getWrapper(Map<String, Object> params) {
+ String dictType = (String) params.get("dictType");
+ String dictName = (String) params.get("dictName");
+
+ QueryWrapper<SysDictTypeEntity> wrapper = new QueryWrapper<>();
+ wrapper.like(StringUtils.isNotBlank(dictType), "dict_type", dictType);
+ wrapper.like(StringUtils.isNotBlank(dictName), "dict_name", dictName);
+
+ return wrapper;
+ }
+
+ @Override
+ public SysDictTypeVO get(Long id) {
+ SysDictTypeEntity entity = baseDao.selectById(id);
+ if (entity == null) {
+ throw new RenException(ErrorCode.DICT_TYPE_NOT_EXIST);
+ }
+
+ return ConvertUtils.sourceToTarget(entity, SysDictTypeVO.class);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void save(SysDictTypeDTO dto) {
+ // 瀛楀吀绫诲瀷缂栫爜涓嶈兘閲嶅
+ checkDictTypeUnique(dto.getDictType(), null);
+
+ SysDictTypeEntity entity = ConvertUtils.sourceToTarget(dto, SysDictTypeEntity.class);
+
+ insert(entity);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void update(SysDictTypeDTO dto) {
+ // 瀛楀吀绫诲瀷缂栫爜涓嶈兘閲嶅
+ checkDictTypeUnique(dto.getDictType(), String.valueOf(dto.getId()));
+
+ SysDictTypeEntity entity = ConvertUtils.sourceToTarget(dto, SysDictTypeEntity.class);
+
+ updateById(entity);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void delete(Long[] ids) {
+ // 鍏堝垹闄ゅ搴旂殑瀛楀吀鏁版嵁
+ for (Long id : ids) {
+ sysDictDataService.deleteByTypeId(id);
+ }
+ // 鍐嶅垹闄ゅ瓧鍏哥被鍨�
+ deleteBatchIds(Arrays.asList(ids));
+ }
+
+ @Override
+ public List<SysDictTypeVO> list(Map<String, Object> params) {
+ List<SysDictTypeEntity> entityList = baseDao.selectList(getWrapper(params));
+ List<SysDictTypeVO> sysDictTypeVOList = ConvertUtils.sourceToTarget(entityList, SysDictTypeVO.class);
+ // 璁剧疆鐢ㄦ埛鍚�
+ setUserName(sysDictTypeVOList);
+
+ return sysDictTypeVOList;
+ }
+
+ /**
+ * 璁剧疆鐢ㄦ埛鍚�
+ *
+ * @param sysDictTypeList 瀛楀吀绫诲瀷闆嗗悎
+ */
+ private void setUserName(List<SysDictTypeVO> sysDictTypeList) {
+ // 鏀堕泦鎵�鏈夌敤鎴� ID
+ Set<Long> userIds = sysDictTypeList.stream().flatMap(vo -> Stream.of(vo.getCreator(), vo.getUpdater()))
+ .filter(Objects::nonNull).collect(Collectors.toSet());
+
+ // 璁剧疆鏇存柊鑰呭拰鍒涘缓鑰呭悕绉�
+ if (!userIds.isEmpty()) {
+ List<SysUserEntity> sysUserEntities = sysUserDao.selectByIds(userIds);
+ // 鎶奓ist杞垚Map锛孧ap<Long, String>
+ Map<Long, String> userNameMap = sysUserEntities.stream().collect(Collectors.toMap(SysUserEntity::getId,
+ SysUserEntity::getUsername, (existing, replacement) -> existing));
+
+ sysDictTypeList.forEach(vo -> {
+ vo.setCreatorName(userNameMap.get(vo.getCreator()));
+ vo.setUpdaterName(userNameMap.get(vo.getUpdater()));
+ });
+ }
+ }
+
+ private void checkDictTypeUnique(String dictType, String excludeId) {
+ LambdaQueryWrapper<SysDictTypeEntity> queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(SysDictTypeEntity::getDictType, dictType);
+ if (StringUtils.isNotBlank(excludeId)) {
+ queryWrapper.ne(SysDictTypeEntity::getId, excludeId);
+ }
+ boolean exists = baseDao.exists(queryWrapper);
+ if (exists) {
+ throw new RenException(ErrorCode.DICT_TYPE_DUPLICATE);
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysParamsServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysParamsServiceImpl.java
new file mode 100644
index 0000000..b8bc796
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysParamsServiceImpl.java
@@ -0,0 +1,331 @@
+package xiaozhi.modules.sys.service.impl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+
+import lombok.AllArgsConstructor;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.service.impl.BaseServiceImpl;
+import xiaozhi.common.utils.ConvertUtils;
+import xiaozhi.common.utils.JsonUtils;
+import xiaozhi.common.utils.SM2Utils;
+import xiaozhi.modules.agent.service.AgentPluginMappingService;
+import xiaozhi.modules.sys.dao.SysParamsDao;
+import xiaozhi.modules.sys.dto.SysParamsDTO;
+import xiaozhi.modules.sys.entity.SysParamsEntity;
+import xiaozhi.modules.sys.redis.SysParamsRedis;
+import xiaozhi.modules.sys.service.SysParamsService;
+
+/**
+ * 鍙傛暟绠$悊
+ */
+@AllArgsConstructor
+@Service
+public class SysParamsServiceImpl extends BaseServiceImpl<SysParamsDao, SysParamsEntity> implements SysParamsService {
+ private final SysParamsRedis sysParamsRedis;
+ private final AgentPluginMappingService agentPluginMappingService;
+
+ @Override
+ public PageData<SysParamsDTO> page(Map<String, Object> params) {
+ IPage<SysParamsEntity> page = baseDao.selectPage(
+ getPage(params, null, false),
+ getWrapper(params));
+
+ return getPageData(page, SysParamsDTO.class);
+ }
+
+ @Override
+ public List<SysParamsDTO> list(Map<String, Object> params) {
+ List<SysParamsEntity> entityList = baseDao.selectList(getWrapper(params));
+
+ return ConvertUtils.sourceToTarget(entityList, SysParamsDTO.class);
+ }
+
+ private QueryWrapper<SysParamsEntity> getWrapper(Map<String, Object> params) {
+ String paramCode = (String) params.get("paramCode");
+
+ QueryWrapper<SysParamsEntity> wrapper = new QueryWrapper<>();
+ wrapper.eq("param_type", 1);
+ wrapper.nested(StringUtils.isNotBlank(paramCode), i -> i.like("param_code", paramCode)
+ .or()
+ .like("remark", paramCode));
+
+ return wrapper;
+ }
+
+ @Override
+ public SysParamsDTO get(Long id) {
+ SysParamsEntity entity = baseDao.selectById(id);
+
+ return ConvertUtils.sourceToTarget(entity, SysParamsDTO.class);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void save(SysParamsDTO dto) {
+ validateParamValue(dto);
+
+ SysParamsEntity entity = ConvertUtils.sourceToTarget(dto, SysParamsEntity.class);
+ insert(entity);
+
+ sysParamsRedis.set(entity.getParamCode(), entity.getParamValue());
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void update(SysParamsDTO dto) {
+ validateParamValue(dto);
+ detectingSMSParameters(dto.getParamCode(), dto.getParamValue());
+ SysParamsEntity entity = ConvertUtils.sourceToTarget(dto, SysParamsEntity.class);
+ updateById(entity);
+
+ sysParamsRedis.set(entity.getParamCode(), entity.getParamValue());
+ }
+
+ /**
+ * 鏍¢獙鍙傛暟鍊肩被鍨�
+ */
+ private void validateParamValue(SysParamsDTO dto) {
+ if (dto == null) {
+ throw new RenException(ErrorCode.PARAM_VALUE_NULL);
+ }
+
+ if (StringUtils.isBlank(dto.getParamValue())) {
+ throw new RenException(ErrorCode.PARAM_VALUE_NULL);
+ }
+
+ if (StringUtils.isBlank(dto.getValueType())) {
+ throw new RenException(ErrorCode.PARAM_TYPE_NULL);
+ }
+
+ String valueType = dto.getValueType().toLowerCase();
+ String paramValue = dto.getParamValue();
+
+ switch (valueType) {
+ case "string":
+ break;
+ case "array":
+ break;
+ case "number":
+ try {
+ Double.parseDouble(paramValue);
+ } catch (NumberFormatException e) {
+ throw new RenException(ErrorCode.PARAM_NUMBER_INVALID);
+ }
+ break;
+ case "boolean":
+ if (!"true".equalsIgnoreCase(paramValue) && !"false".equalsIgnoreCase(paramValue)) {
+ throw new RenException(ErrorCode.PARAM_BOOLEAN_INVALID);
+ }
+ break;
+ case "json":
+ try {
+ // 棣栧厛妫�鏌ユ槸鍚︿互 { 寮�澶达紝浠� } 缁撳熬
+ String trimmedValue = paramValue.trim();
+ if (!trimmedValue.startsWith("{") || !trimmedValue.endsWith("}")) {
+ throw new RenException(ErrorCode.PARAM_JSON_INVALID);
+ }
+ // 鐒跺悗灏濊瘯瑙f瀽JSON
+ JsonUtils.parseObject(paramValue, Object.class);
+ } catch (Exception e) {
+ throw new RenException(ErrorCode.PARAM_JSON_INVALID);
+ }
+ break;
+ default:
+ throw new RenException(ErrorCode.PARAM_TYPE_INVALID);
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void delete(String[] ids) {
+ // 鍒犻櫎Redis鏁版嵁
+ List<String> paramCodeList = baseDao.getParamCodeList(ids);
+ String[] paramCodes = paramCodeList.toArray(new String[paramCodeList.size()]);
+ if (paramCodes.length > 0) {
+ sysParamsRedis.delete(paramCodes);
+ }
+
+ // 鍒犻櫎
+ deleteBatchIds(Arrays.asList(ids));
+ }
+
+ @Override
+ public String getValue(String paramCode, Boolean fromCache) {
+ String paramValue = null;
+ if (fromCache) {
+ paramValue = sysParamsRedis.get(paramCode);
+ if (paramValue == null) {
+ paramValue = baseDao.getValueByCode(paramCode);
+
+ sysParamsRedis.set(paramCode, paramValue);
+ }
+ } else {
+ paramValue = baseDao.getValueByCode(paramCode);
+ }
+ return paramValue;
+ }
+
+ @Override
+ public <T> T getValueObject(String paramCode, Class<T> clazz) {
+ String paramValue = getValue(paramCode, true);
+ if (StringUtils.isNotBlank(paramValue)) {
+ return JsonUtils.parseObject(paramValue, clazz);
+ }
+
+ try {
+ return clazz.getDeclaredConstructor().newInstance();
+ } catch (Exception e) {
+ throw new RenException(ErrorCode.PARAMS_GET_ERROR);
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public int updateValueByCode(String paramCode, String paramValue) {
+ int count = baseDao.updateValueByCode(paramCode, paramValue);
+ sysParamsRedis.set(paramCode, paramValue);
+ return count;
+ }
+
+ @Override
+ public void initServerSecret() {
+ // 鑾峰彇鏈嶅姟鍣ㄥ瘑閽�
+ String secretParam = getValue(Constant.SERVER_SECRET, false);
+ if (StringUtils.isBlank(secretParam) || "null".equals(secretParam)) {
+ String newSecret = UUID.randomUUID().toString();
+ updateValueByCode(Constant.SERVER_SECRET, newSecret);
+ }
+
+ // 鍒濆鍖朣M2瀵嗛挜瀵�
+ initSM2KeyPair();
+ }
+
+ /**
+ * 鍒濆鍖朣M2瀵嗛挜瀵�
+ */
+ private void initSM2KeyPair() {
+ // 鑾峰彇SM2鍏挜
+ String publicKey = getValue(Constant.SM2_PUBLIC_KEY, false);
+ // 鑾峰彇SM2绉侀挜
+ String privateKey = getValue(Constant.SM2_PRIVATE_KEY, false);
+
+ // 濡傛灉鍏挜鎴栫閽ヤ负绌猴紝鍒欑敓鎴愭柊鐨勫瘑閽ュ
+ if (StringUtils.isBlank(publicKey) || StringUtils.isBlank(privateKey) ||
+ "null".equals(publicKey) || "null".equals(privateKey)) {
+ Map<String, String> keyPair = SM2Utils.createKey();
+ String newPublicKey = keyPair.get(SM2Utils.KEY_PUBLIC_KEY);
+ String newPrivateKey = keyPair.get(SM2Utils.KEY_PRIVATE_KEY);
+
+ // 鏇存柊鏁版嵁搴撲腑鐨勫瘑閽ュ
+ updateValueByCode(Constant.SM2_PUBLIC_KEY, newPublicKey);
+ updateValueByCode(Constant.SM2_PRIVATE_KEY, newPrivateKey);
+ }
+ }
+
+ /**
+ * 妫�娴嬬煭淇″弬鏁版槸鍚︾鍚堣姹�
+ *
+ * @param paramCode 鍙傛暟缂栫爜
+ * @param paramValue 鍙傛暟鍊�
+ * @return 鏄惁閫氳繃
+ */
+ private boolean detectingSMSParameters(String paramCode, String paramValue) {
+ // 鍒ゆ柇鏄惁鏄紑鍚墜鏈烘敞鍐岀殑鍙傛暟缂栫爜锛屽鏋滀笉鏄弬鏁扮紪鐮侊紝鐫�涓嶉渶瑕佹娴嬪叾浠栫煭淇″弬鏁帮紝鐩存帴杩斿洖true
+ if (!Constant.SysMSMParam.SERVER_ENABLE_MOBILE_REGISTER.getValue().equals(paramCode)) {
+ return true;
+ }
+ // 鍒ゆ柇鏄惁涓哄叧闂紝濡傛灉鏄叧闂煭淇℃敞鍐岋紝鐫�涓嶉渶瑕佹娴嬪叾浠栫煭淇″弬鏁帮紝鐩存帴杩斿洖true
+ if ("false".equalsIgnoreCase(paramValue)) {
+ return true;
+ }
+ // 妫�娴嬬煭淇″叧鑱斿弬鏁版槸鍚︿负绌�
+ ArrayList<String> list = new ArrayList<String>();
+ list.add(Constant.SysMSMParam.SERVER_SMS_MAX_SEND_COUNT.getValue());
+ list.add(Constant.SysMSMParam.ALIYUN_SMS_ACCESS_KEY_ID.getValue());
+ list.add(Constant.SysMSMParam.ALIYUN_SMS_ACCESS_KEY_SECRET.getValue());
+ list.add(Constant.SysMSMParam.ALIYUN_SMS_SIGN_NAME.getValue());
+ list.add(Constant.SysMSMParam.ALIYUN_SMS_SMS_CODE_TEMPLATE_CODE.getValue());
+ StringBuilder str = new StringBuilder();
+ list.forEach(item -> {
+ if (!StringUtils.isNoneBlank(item)) {
+ str.append(",").append(item);
+ }
+ });
+ if (!str.isEmpty()) {
+ String promptStr = "%s杩欎簺鍙傛暟涓嶅彲浠ヤ负绌�";
+ String substring = str.substring(1, str.length());
+ throw new RenException(promptStr.formatted(substring));
+ }
+ return true;
+ }
+ @Override
+ public String getSystemWebMenu(boolean fromCache) {
+ return getValue(Constant.SYSTEM_WEB_MENU, fromCache);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updateSystemWebMenu(String configJson) {
+ // 鑾峰彇褰撳墠閰嶇疆
+ String currentConfig = getSystemWebMenu(false);
+ Map<String, Object> currentMap = null;
+ Map<String, Object> newMap = null;
+
+ try {
+ if (StringUtils.isNotBlank(currentConfig)) {
+ currentMap = JsonUtils.parseMap(currentConfig);
+ }
+ if (StringUtils.isNotBlank(configJson)) {
+ newMap = JsonUtils.parseMap(configJson);
+ }
+ } catch (Exception e) {
+ throw new RenException(ErrorCode.PARAM_JSON_INVALID);
+ }
+
+ // 妫�鏌ddressBook鍔熻兘鏄惁琚叧闂�
+ if (currentMap != null && newMap != null) {
+ Map<?, ?> currentFeatures = Map.class.cast(currentMap.get("features"));
+ Map<?, ?> newFeatures = Map.class.cast(newMap.get("features"));
+
+ if (currentFeatures != null && newFeatures != null) {
+ Object currentAddressBookObj = currentFeatures.get("addressBook");
+ Object newAddressBookObj = newFeatures.get("addressBook");
+
+ Boolean currentEnabled = false;
+ Boolean newEnabled = false;
+
+ if (currentAddressBookObj instanceof Map<?, ?> currentAddressBook) {
+ Object enabled = currentAddressBook.get("enabled");
+ currentEnabled = enabled != null ? Boolean.class.cast(enabled) : false;
+ }
+
+ if (newAddressBookObj instanceof Map<?, ?> newAddressBook) {
+ Object enabled = newAddressBook.get("enabled");
+ newEnabled = enabled != null ? Boolean.class.cast(enabled) : false;
+ }
+
+ // 濡傛灉涔嬪墠鏄惎鐢ㄧ姸鎬侊紝鐜板湪琚鐢紝鍒犻櫎鎵�鏈塩all_device鎻掍欢
+ if (Boolean.TRUE.equals(currentEnabled) && !Boolean.TRUE.equals(newEnabled)) {
+ agentPluginMappingService.deleteByPluginId("SYSTEM_PLUGIN_CALL_DEVICE");
+ }
+ }
+ }
+
+ // 鏇存柊閰嶇疆
+ updateValueByCode(Constant.SYSTEM_WEB_MENU, configJson);
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysUserServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysUserServiceImpl.java
new file mode 100644
index 0000000..931f7f8
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysUserServiceImpl.java
@@ -0,0 +1,250 @@
+package xiaozhi.modules.sys.service.impl;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+
+import lombok.AllArgsConstructor;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.service.impl.BaseServiceImpl;
+import xiaozhi.common.utils.ConvertUtils;
+import xiaozhi.modules.agent.service.AgentService;
+import xiaozhi.modules.device.service.DeviceService;
+import xiaozhi.modules.security.password.PasswordUtils;
+import xiaozhi.modules.sys.dao.SysUserDao;
+import xiaozhi.modules.sys.dto.AdminPageUserDTO;
+import xiaozhi.modules.sys.dto.PasswordDTO;
+import xiaozhi.modules.sys.dto.SysUserDTO;
+import xiaozhi.modules.sys.entity.SysUserEntity;
+import xiaozhi.modules.sys.enums.SuperAdminEnum;
+import xiaozhi.modules.sys.service.SysParamsService;
+import xiaozhi.modules.sys.service.SysUserService;
+import xiaozhi.modules.sys.vo.AdminPageUserVO;
+
+/**
+ * 绯荤粺鐢ㄦ埛
+ */
+@AllArgsConstructor
+@Service
+public class SysUserServiceImpl extends BaseServiceImpl<SysUserDao, SysUserEntity> implements SysUserService {
+ private final SysUserDao sysUserDao;
+
+ private final DeviceService deviceService;
+
+ private final AgentService agentService;
+
+ private final SysParamsService sysParamsService;
+
+ @Override
+ public SysUserDTO getByUsername(String username) {
+ QueryWrapper<SysUserEntity> queryWrapper = new QueryWrapper<>();
+ queryWrapper.eq("username", username);
+ List<SysUserEntity> users = sysUserDao.selectList(queryWrapper);
+ if (users == null || users.isEmpty()) {
+ return null;
+ }
+ SysUserEntity entity = users.get(0);
+ return ConvertUtils.sourceToTarget(entity, SysUserDTO.class);
+ }
+
+ @Override
+ public SysUserDTO getByUserId(Long userId) {
+ SysUserEntity sysUserEntity = sysUserDao.selectById(userId);
+
+ return ConvertUtils.sourceToTarget(sysUserEntity, SysUserDTO.class);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void save(SysUserDTO dto) {
+ SysUserEntity entity = ConvertUtils.sourceToTarget(dto, SysUserEntity.class);
+
+ // 瀵嗙爜寮哄害
+ if (!isStrongPassword(entity.getPassword())) {
+ throw new RenException(ErrorCode.PASSWORD_WEAK_ERROR);
+ }
+
+ // 瀵嗙爜鍔犲瘑
+ String password = PasswordUtils.encode(entity.getPassword());
+ entity.setPassword(password);
+
+ // 淇濆瓨鐢ㄦ埛
+ Long userCount = getUserCount();
+ if (userCount == 0) {
+ entity.setSuperAdmin(SuperAdminEnum.YES.value());
+ } else {
+ entity.setSuperAdmin(SuperAdminEnum.NO.value());
+ }
+ entity.setStatus(1);
+
+ insert(entity);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteById(Long id) {
+ // 鍒犻櫎鐢ㄦ埛
+ baseDao.deleteById(id);
+ // 鍒犻櫎璁惧
+ deviceService.deleteByUserId(id);
+ // 鍒犻櫎鏅鸿兘浣�
+ agentService.deleteAgentByUserId(id);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void changePassword(Long userId, PasswordDTO passwordDTO) {
+ SysUserEntity sysUserEntity = sysUserDao.selectById(userId);
+
+ if (null == sysUserEntity) {
+ throw new RenException(ErrorCode.TOKEN_INVALID);
+ }
+
+ // 鍒ゆ柇鏃у瘑鐮佹槸鍚︽纭�
+ if (!PasswordUtils.matches(passwordDTO.getPassword(), sysUserEntity.getPassword())) {
+ throw new RenException(ErrorCode.OLD_PASSWORD_ERROR);
+ }
+
+ // 鏂板瘑鐮佸己搴�
+ if (!isStrongPassword(passwordDTO.getNewPassword())) {
+ throw new RenException(ErrorCode.PASSWORD_WEAK_ERROR);
+ }
+
+ // 瀵嗙爜鍔犲瘑
+ String password = PasswordUtils.encode(passwordDTO.getNewPassword());
+ sysUserEntity.setPassword(password);
+
+ updateById(sysUserEntity);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void changePasswordDirectly(Long userId, String password) {
+ // 鏂板瘑鐮佸己搴�
+ if (!isStrongPassword(password)) {
+ throw new RenException(ErrorCode.PASSWORD_WEAK_ERROR);
+ }
+ SysUserEntity sysUserEntity = new SysUserEntity();
+ sysUserEntity.setId(userId);
+ sysUserEntity.setPassword(PasswordUtils.encode(password));
+ updateById(sysUserEntity);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public String resetPassword(Long userId) {
+ String password = generatePassword();
+ changePasswordDirectly(userId, password);
+ return password;
+ }
+
+ private Long getUserCount() {
+ QueryWrapper<SysUserEntity> queryWrapper = new QueryWrapper<>();
+ return baseDao.selectCount(queryWrapper);
+ }
+
+ @Override
+ public PageData<AdminPageUserVO> page(AdminPageUserDTO dto) {
+ Map<String, Object> params = new HashMap<String, Object>();
+ params.put(Constant.PAGE, dto.getPage());
+ params.put(Constant.LIMIT, dto.getLimit());
+ IPage<SysUserEntity> page = baseDao.selectPage(
+ getPage(params, "id", true),
+ new QueryWrapper<SysUserEntity>().like(StringUtils.isNotBlank(dto.getMobile()), "username",
+ dto.getMobile()));
+ // 寰幆澶勭悊page鑾峰彇鍥炴潵鐨勬暟鎹紝杩斿洖闇�瑕佺殑瀛楁
+ List<AdminPageUserVO> list = page.getRecords().stream().map(user -> {
+ AdminPageUserVO adminPageUserVO = new AdminPageUserVO();
+ adminPageUserVO.setUserid(user.getId().toString());
+ adminPageUserVO.setMobile(user.getUsername());
+ String deviceCount = deviceService.selectCountByUserId(user.getId()).toString();
+ adminPageUserVO.setDeviceCount(deviceCount);
+ adminPageUserVO.setStatus(user.getStatus());
+ adminPageUserVO.setCreateDate(user.getCreateDate());
+ return adminPageUserVO;
+ }).toList();
+ return new PageData<>(list, page.getTotal());
+ }
+
+ private boolean isStrongPassword(String password) {
+ // 寮卞瘑鐮佺殑姝e垯琛ㄨ揪寮�
+ String weakPasswordRegex = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z]).+$";
+ Pattern pattern = Pattern.compile(weakPasswordRegex);
+ Matcher matcher = pattern.matcher(password);
+ return matcher.matches();
+ }
+
+ private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()";
+ private static final Random random = new Random();
+
+ /**
+ * 鐢熸垚闅忔満瀵嗙爜
+ *
+ * @return 闅忔満鐢熸垚鐨勫瘑鐮�
+ */
+ private String generatePassword() {
+ StringBuilder password = new StringBuilder();
+
+ // 纭繚鍖呭惈鑷冲皯涓�涓暟瀛�
+ password.append("0123456789".charAt(random.nextInt(10)));
+ // 纭繚鍖呭惈鑷冲皯涓�涓皬鍐欏瓧姣�
+ password.append("abcdefghijklmnopqrstuvwxyz".charAt(random.nextInt(26)));
+ // 纭繚鍖呭惈鑷冲皯涓�涓ぇ鍐欏瓧姣�
+ password.append("ABCDEFGHIJKLMNOPQRSTUVWXYZ".charAt(random.nextInt(26)));
+ // 纭繚鍖呭惈鑷冲皯涓�涓壒娈婄鍙�
+ password.append("!@#$%^&*()".charAt(random.nextInt(10)));
+
+ // 鐢熸垚鍓╀綑鐨�8涓瓧绗�
+ for (int i = 4; i < 12; i++) {
+ password.append(CHARACTERS.charAt(random.nextInt(CHARACTERS.length())));
+ }
+
+ // 鎵撲贡瀵嗙爜涓瓧绗︾殑椤哄簭
+ char[] passwordChars = password.toString().toCharArray();
+ for (int i = 0; i < passwordChars.length; i++) {
+ int randomIndex = random.nextInt(passwordChars.length);
+ char temp = passwordChars[i];
+ passwordChars[i] = passwordChars[randomIndex];
+ passwordChars[randomIndex] = temp;
+ }
+
+ return new String(passwordChars);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void changeStatus(Integer status, String[] userIds) {
+ for (String userId : userIds) {
+ SysUserEntity entity = new SysUserEntity();
+ entity.setId(Long.parseLong(userId));
+ entity.setStatus(status);
+ updateById(entity);
+ }
+ }
+
+ @Override
+ public boolean getAllowUserRegister() {
+ String allowUserRegister = sysParamsService.getValue(Constant.SERVER_ALLOW_USER_REGISTER, true);
+ if (allowUserRegister.equals("true")) {
+ return true;
+ }
+ Long userCount = baseDao.selectCount(new QueryWrapper<SysUserEntity>());
+ if (userCount == 0) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysUserUtilServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysUserUtilServiceImpl.java
new file mode 100644
index 0000000..f4fe601
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysUserUtilServiceImpl.java
@@ -0,0 +1,37 @@
+package xiaozhi.modules.sys.service.impl;
+
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+import xiaozhi.common.redis.RedisKeys;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.common.service.impl.BaseServiceImpl;
+import xiaozhi.modules.sys.dao.SysUserDao;
+import xiaozhi.modules.sys.entity.SysUserEntity;
+import xiaozhi.modules.sys.service.SysUserUtilService;
+
+import java.util.function.Consumer;
+
+@Service
+@AllArgsConstructor
+public class SysUserUtilServiceImpl extends BaseServiceImpl<SysUserDao, SysUserEntity> implements SysUserUtilService {
+
+ private RedisUtils redisUtils;
+
+ @Override
+ public void assignUsername(Long userId, Consumer<String> setter) {
+ String userIdKey = RedisKeys.getUserIdKey(userId);
+
+ Object value = redisUtils.get(userIdKey);
+ String username = (value != null) ? value.toString() : null;
+ if(username != null){
+ setter.accept(username);
+ }else {
+ SysUserEntity entity = baseDao.selectById(userId);
+ if (entity != null) {
+ username = entity.getUsername();
+ redisUtils.set(userIdKey,username,10);
+ setter.accept(username);
+ }
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/TokenServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/TokenServiceImpl.java
new file mode 100644
index 0000000..3a15639
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/TokenServiceImpl.java
@@ -0,0 +1,19 @@
+package xiaozhi.modules.sys.service.impl;
+
+import org.springframework.stereotype.Service;
+
+import lombok.AllArgsConstructor;
+import xiaozhi.modules.security.oauth2.TokenGenerator;
+import xiaozhi.modules.sys.service.TokenService;
+
+@AllArgsConstructor
+@Service
+public class TokenServiceImpl implements TokenService {
+
+ @Override
+ public String createToken(long userId) {
+ // 鐢熸垚涓�涓猼oken
+ String token = TokenGenerator.generateValue();
+ return token;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/utils/WebSocketClientManager.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/utils/WebSocketClientManager.java
new file mode 100644
index 0000000..d4203c7
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/utils/WebSocketClientManager.java
@@ -0,0 +1,372 @@
+package xiaozhi.modules.sys.utils;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+import org.springframework.util.StopWatch;
+import org.springframework.web.socket.BinaryMessage;
+import org.springframework.web.socket.CloseStatus;
+import org.springframework.web.socket.TextMessage;
+import org.springframework.web.socket.WebSocketHttpHeaders;
+import org.springframework.web.socket.WebSocketSession;
+import org.springframework.web.socket.client.standard.StandardWebSocketClient;
+import org.springframework.web.socket.handler.AbstractWebSocketHandler;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.common.utils.DateUtils;
+
+/**
+ * WebSocketClientResource锛氭敮鎸� try-with-resources 妯″紡
+ */
+@Slf4j
+public class WebSocketClientManager implements Closeable {
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+ // 鍏ㄥ眬鍥炶皟绾跨▼姹�
+ private static final ExecutorService CALLBACK_EXECUTOR = Executors
+ .newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() {
+ private final AtomicInteger cnt = new AtomicInteger();
+
+ public Thread newThread(Runnable r) {
+ Thread t = new Thread(r, "ws-callback-" + cnt.getAndIncrement());
+ t.setDaemon(true);
+ return t;
+ }
+ });
+
+ private volatile WebSocketSession session;
+ private final BlockingQueue<String> textMessageQueue;
+ private final BlockingQueue<byte[]> binaryMessageQueue;
+ private final CompletableFuture<Void> errorFuture;
+ private final long maxSessionDuration;
+ private final TimeUnit maxSessionDurationUnit;
+
+ private volatile Consumer<String> onText;
+ private volatile Consumer<byte[]> onBinary;
+ private volatile Consumer<Throwable> onError;
+
+ private final int queueCapacity;
+
+ // 绉佹湁鏋勯�狅紝浠呯敱 Builder 璋冪敤
+ private WebSocketClientManager(Builder b) {
+ this.maxSessionDuration = b.maxSessionDuration;
+ this.maxSessionDurationUnit = b.maxSessionDurationUnit;
+ this.queueCapacity = b.queueCapacity;
+ this.textMessageQueue = new LinkedBlockingQueue<>(queueCapacity);
+ this.binaryMessageQueue = new LinkedBlockingQueue<>(queueCapacity);
+ this.errorFuture = new CompletableFuture<>();
+ }
+
+ public static WebSocketClientManager build(Builder b)
+ throws InterruptedException, ExecutionException, TimeoutException, IOException {
+ WebSocketClientManager ws = new WebSocketClientManager(b);
+ StandardWebSocketClient client = new StandardWebSocketClient();
+ CompletableFuture<WebSocketSession> future = client.execute(ws.new InternalHandler(b.uri), b.headers,
+ URI.create(b.uri));
+ WebSocketSession sess = future.get(b.connectTimeout, b.connectUnit);
+ if (sess == null || !sess.isOpen()) {
+ throw new IOException("鎻℃墜澶辫触鎴栦細璇濇湭鎵撳紑");
+ }
+ // 璁剧疆缂撳啿鍖�
+ sess.setTextMessageSizeLimit(b.bufferSize);
+ sess.setBinaryMessageSizeLimit(b.bufferSize);
+ ws.session = sess;
+ return ws;
+ }
+
+
+ /**
+ * 鍙戦�� Text
+ */
+ public void sendText(String text) throws IOException {
+ session.sendMessage(new TextMessage(text));
+ }
+
+ public void sendBinary(byte[] data) throws IOException {
+ session.sendMessage(new BinaryMessage(data));
+ }
+
+ public void sendJson(Object payload) throws IOException {
+ String json = OBJECT_MAPPER.writeValueAsString(payload);
+ session.sendMessage(new TextMessage(json));
+ }
+
+ private <T> List<T> listenerCustom(
+ BlockingQueue<T> queue,
+ Predicate<T> predicate)
+ throws InterruptedException, TimeoutException, ExecutionException {
+ List<T> collected = new ArrayList<>();
+ long deadline = System.currentTimeMillis() + maxSessionDurationUnit.toMillis(maxSessionDuration);
+
+ while (true) {
+ if (errorFuture.isDone()) {
+ errorFuture.get();
+ }
+
+ long remaining = deadline - System.currentTimeMillis();
+ if (remaining <= 0) {
+ throw new TimeoutException("绛夊緟鎵归噺娑堟伅瓒呮椂");
+ }
+
+ T msg = queue.poll(remaining, TimeUnit.MILLISECONDS);
+ if (msg == null) {
+ throw new TimeoutException("绛夊緟鎵归噺娑堟伅瓒呮椂");
+ }
+
+ collected.add(msg);
+ if (predicate.test(msg)) {
+ break;
+ }
+ }
+ close();
+ return collected;
+ }
+
+ private <T> List<T> listenerCustomWithoutClose(
+ BlockingQueue<T> queue,
+ Predicate<T> predicate)
+ throws InterruptedException, TimeoutException, ExecutionException {
+ List<T> collected = new ArrayList<>();
+ long deadline = System.currentTimeMillis() + maxSessionDurationUnit.toMillis(maxSessionDuration);
+
+ while (true) {
+ if (errorFuture.isDone()) {
+ errorFuture.get();
+ }
+
+ long remaining = deadline - System.currentTimeMillis();
+ if (remaining <= 0) {
+ throw new TimeoutException("绛夊緟鎵归噺娑堟伅瓒呮椂");
+ }
+
+ T msg = queue.poll(remaining, TimeUnit.MILLISECONDS);
+ if (msg == null) {
+ throw new TimeoutException("绛夊緟鎵归噺娑堟伅瓒呮椂");
+ }
+
+ collected.add(msg);
+ if (predicate.test(msg)) {
+ break;
+ }
+ }
+ // 涓嶈皟鐢� close()锛屼繚鎸佽繛鎺ュ紑鏀�
+ return collected;
+ }
+
+ /**
+ * 鍚屾鎺ユ敹澶氭潯娑堟伅锛岀洿鍒� predicate 涓� true 鎴栬秴鏃舵姏寮傚父锛�
+ *
+ * @return 杩斿洖鐩戝惉鏈熼棿鐨勬墍鏈夋秷鎭垪琛�
+ */
+ public List<String> listener(Predicate<String> predicate)
+ throws InterruptedException, TimeoutException, ExecutionException {
+ return listenerCustom(textMessageQueue, predicate);
+ }
+
+ /**
+ * 鍚屾鎺ユ敹澶氭潯娑堟伅锛岀洿鍒� predicate 涓� true 鎴栬秴鏃舵姏寮傚父锛�
+ * 涓嶈嚜鍔ㄥ叧闂繛鎺ワ紝閫傜敤浜庨渶瑕佸湪鍚屼竴杩炴帴涓婂彂閫佸涓秷鎭殑鍦烘櫙
+ *
+ * @return 杩斿洖鐩戝惉鏈熼棿鐨勬墍鏈夋秷鎭垪琛�
+ */
+ public List<String> listenerWithoutClose(Predicate<String> predicate)
+ throws InterruptedException, TimeoutException, ExecutionException {
+ return listenerCustomWithoutClose(textMessageQueue, predicate);
+ }
+
+ public List<byte[]> listenerBinary(Predicate<byte[]> predicate)
+ throws InterruptedException, TimeoutException, ExecutionException {
+ return listenerCustom(binaryMessageQueue, predicate);
+ }
+
+ /**
+ * 娉ㄥ唽鏂囨湰鍥炶皟
+ */
+ public WebSocketClientManager onText(Consumer<String> c) {
+ this.onText = c;
+ return this;
+ }
+
+ /**
+ * 娉ㄥ唽浜岃繘鍒跺洖璋�
+ */
+ public WebSocketClientManager onBinary(Consumer<byte[]> c) {
+ this.onBinary = c;
+ return this;
+ }
+
+ /**
+ * 娉ㄥ唽閿欒鍥炶皟
+ */
+ public WebSocketClientManager onError(Consumer<Throwable> c) {
+ this.onError = c;
+ return this;
+ }
+
+ /**
+ * 鍏抽棴浼氳瘽锛宼ry-with-resources / finally 鑷姩璋冪敤
+ */
+ @Override
+ public void close() {
+ try {
+ if (session != null && session.isOpen()) {
+ session.close(CloseStatus.NORMAL);
+ }
+ } catch (IOException ignored) {
+ }
+ textMessageQueue.clear();
+ binaryMessageQueue.clear();
+ errorFuture.completeExceptionally(new IOException("WebSocket 宸插叧闂�"));
+ }
+
+ private class InternalHandler extends AbstractWebSocketHandler {
+ private final String targetUri;
+ private final StopWatch stopWatch;
+
+ InternalHandler(String targetUri) {
+ this.targetUri = targetUri;
+ this.stopWatch = new StopWatch();
+ }
+
+ /**
+ * 杩炴帴寤虹珛鏃跺洖璋�
+ */
+ @Override
+ public void afterConnectionEstablished(WebSocketSession session) {
+ // 淇濆瓨浼氳瘽
+ WebSocketClientManager.this.session = session;
+ this.stopWatch.start();
+ log.info("ws杩炴帴鎴愬姛, 鐩爣URI: {}, 杩炴帴鏃堕棿: {}", targetUri,
+ DateUtils.getDateTimeNow(DateUtils.DATE_TIME_MILLIS_PATTERN));
+ }
+
+ /**
+ * 澶勭悊鏂囨湰娑堟伅
+ */
+ @Override
+ protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
+ String payload = message.getPayload();
+ // 鍏ラ槦
+ textMessageQueue.offer(payload);
+ // 鍥炶皟鐢ㄦ埛娉ㄥ唽鐨� onText
+ if (onText != null) {
+ CALLBACK_EXECUTOR.submit(() -> onText.accept(payload));
+ }
+ }
+
+ /**
+ * 澶勭悊浜岃繘鍒舵秷鎭�
+ */
+ @Override
+ protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
+ ByteBuffer buf = message.getPayload();
+ byte[] data = new byte[buf.remaining()];
+ buf.get(data);
+ // 鍏ラ槦
+ binaryMessageQueue.offer(data);
+ // 鍥炶皟鐢ㄦ埛娉ㄥ唽鐨� onBinary
+ if (onBinary != null) {
+ CALLBACK_EXECUTOR.submit(() -> onBinary.accept(data));
+ }
+ }
+
+ /**
+ * 浼犺緭閿欒鏃跺洖璋�
+ */
+ @Override
+ public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
+ super.handleTransportError(session, exception);
+ // 淇濇寔鍘熸湁閫昏緫锛氬畬鎴� errorFuture銆佸洖璋� onError銆佸叧闂細璇濄�佸紓姝ラ�氱煡杩炴帴澶辫触
+ errorFuture.completeExceptionally(exception);
+ if (onError != null) {
+ CALLBACK_EXECUTOR.submit(() -> onError.accept(exception));
+ }
+ session.close(CloseStatus.SERVER_ERROR);
+ }
+
+ /**
+ * 杩炴帴鍏抽棴鏃跺洖璋�
+ */
+ @Override
+ public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
+ super.afterConnectionClosed(session, status);
+ if (stopWatch.isRunning()) {
+ stopWatch.stop();
+ }
+ log.info("ws杩炴帴鍏抽棴, 鐩爣URI: {}, 鍏抽棴鏃堕棿: {}, 杩炴帴鎬绘椂闀�: {}s,鏂紑鍘熷洜锛歿}",
+ targetUri, DateUtils.getDateTimeNow(DateUtils.DATE_TIME_MILLIS_PATTERN),
+ DateUtils.millsToSecond(stopWatch.getTotalTimeMillis()),status);
+ }
+
+ }
+
+ public static class Builder {
+ private String uri; // 鐩爣 WS URI
+ private long connectTimeout = 3; // 璇锋眰杩炴帴绛夊緟鏃堕棿
+ private TimeUnit connectUnit = TimeUnit.SECONDS; // 璇锋眰杩炴帴绛夊緟鏃堕棿鍗曚綅
+ private long maxSessionDuration = 5; // 鏈�澶ц繛绾挎椂闂达紝榛樿5绉�
+ private TimeUnit maxSessionDurationUnit = TimeUnit.SECONDS; // 鏈�澶ц繛绾挎椂闂村崟浣�
+ private int queueCapacity = 100; // 娑堟伅闃熷垪瀹归噺
+ private int bufferSize = 8 * 1024; //榛樿 8kb
+ private WebSocketHttpHeaders headers; // 璇锋眰澶�
+
+ /**
+ * 鐩爣 WS URI
+ */
+ public Builder uri(String uri) {
+ this.uri = Objects.requireNonNull(uri);
+ return this;
+ }
+
+ public Builder headers(WebSocketHttpHeaders h) {
+ this.headers = h;
+ return this;
+ }
+
+ public Builder connectTimeout(long t, TimeUnit u) {
+ this.connectTimeout = t;
+ this.connectUnit = u;
+ return this;
+ }
+
+ public Builder maxSessionDuration(long t, TimeUnit u) {
+ this.maxSessionDuration = t;
+ this.maxSessionDurationUnit = u;
+ return this;
+ }
+
+ public Builder queueCapacity(int c) {
+ this.queueCapacity = c;
+ return this;
+ }
+ public Builder bufferSize(int c) {
+ this.bufferSize = c;
+ return this;
+ }
+
+ public WebSocketClientManager build()
+ throws InterruptedException, ExecutionException, TimeoutException, IOException {
+ return WebSocketClientManager.build(this);
+ }
+
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/utils/WebSocketTestHandler.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/utils/WebSocketTestHandler.java
new file mode 100644
index 0000000..d9563c2
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/utils/WebSocketTestHandler.java
@@ -0,0 +1,46 @@
+package xiaozhi.modules.sys.utils;
+
+import java.util.concurrent.CompletableFuture;
+
+import org.springframework.web.socket.CloseStatus;
+import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.WebSocketMessage;
+import org.springframework.web.socket.WebSocketSession;
+
+public class WebSocketTestHandler implements WebSocketHandler {
+ private final CompletableFuture<Boolean> future;
+
+ public WebSocketTestHandler(CompletableFuture<Boolean> future) {
+ this.future = future;
+ }
+
+ @Override
+ public void afterConnectionEstablished(WebSocketSession session) {
+ future.complete(true);
+ try {
+ session.close();
+ } catch (Exception e) {
+ // 蹇界暐鍏抽棴寮傚父
+ }
+ }
+
+ @Override
+ public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) {
+ // 涓嶉渶瑕佸鐞嗘秷鎭�
+ }
+
+ @Override
+ public void handleTransportError(WebSocketSession session, Throwable exception) {
+ future.complete(false);
+ }
+
+ @Override
+ public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) {
+ // 杩炴帴鍏抽棴鏃朵笉鍋氫换浣曞鐞�
+ }
+
+ @Override
+ public boolean supportsPartialMessages() {
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/utils/WebSocketValidator.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/utils/WebSocketValidator.java
new file mode 100644
index 0000000..39bbe01
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/utils/WebSocketValidator.java
@@ -0,0 +1,60 @@
+package xiaozhi.modules.sys.utils;
+
+import java.net.URI;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.socket.WebSocketHttpHeaders;
+import org.springframework.web.socket.client.WebSocketClient;
+import org.springframework.web.socket.client.standard.StandardWebSocketClient;
+
+public class WebSocketValidator {
+ private static final Logger logger = LoggerFactory.getLogger(WebSocketValidator.class);
+
+ // WebSocket URL姝e垯琛ㄨ揪寮�
+ private static final Pattern WS_URL_PATTERN = Pattern
+ .compile("^wss?://[\\w.-]+(?:\\.[\\w.-]+)*(?::\\d+)?(?:/[\\w.-]*)*$");
+
+ /**
+ * 楠岃瘉WebSocket鍦板潃鏍煎紡
+ *
+ * @param url WebSocket鍦板潃
+ * @return 鏄惁鏈夋晥
+ */
+ public static boolean validateUrlFormat(String url) {
+ if (StringUtils.isBlank(url)) {
+ return false;
+ }
+ return WS_URL_PATTERN.matcher(url.trim()).matches();
+ }
+
+ /**
+ * 娴嬭瘯WebSocket杩炴帴
+ *
+ * @param url WebSocket鍦板潃
+ * @return 鏄惁鍙繛鎺�
+ */
+ public static boolean testConnection(String url) {
+ if (!validateUrlFormat(url)) {
+ return false;
+ }
+
+ try {
+ WebSocketClient client = new StandardWebSocketClient();
+ CompletableFuture<Boolean> future = new CompletableFuture<>();
+ WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
+
+ client.execute(new WebSocketTestHandler(future), headers, URI.create(url));
+
+ // 绛夊緟鏈�澶�5绉掕幏鍙栬繛鎺ョ粨鏋�
+ return future.get(5, TimeUnit.SECONDS);
+ } catch (Exception e) {
+ logger.error("WebSocket杩炴帴娴嬭瘯澶辫触: {}", url, e);
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/vo/AdminPageUserVO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/vo/AdminPageUserVO.java
new file mode 100644
index 0000000..9f83237
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/vo/AdminPageUserVO.java
@@ -0,0 +1,31 @@
+package xiaozhi.modules.sys.vo;
+
+import java.util.Date;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 绠$悊鍛樺垎椤靛睍绀虹敤鎴风殑VO
+ * @ zjy
+ *
+ * @since 2025-3-25
+ */
+@Data
+public class AdminPageUserVO {
+
+ @Schema(description = "璁惧鏁伴噺")
+ private String deviceCount;
+
+ @Schema(description = "鎵嬫満鍙风爜")
+ private String mobile;
+
+ @Schema(description = "鐘舵��")
+ private Integer status;
+
+ @Schema(description = "鐢ㄦ埛id")
+ private String userid;
+
+ @Schema(description = "娉ㄥ唽鏃堕棿")
+ private Date createDate;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/vo/SysDictDataItem.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/vo/SysDictDataItem.java
new file mode 100644
index 0000000..f6c8150
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/vo/SysDictDataItem.java
@@ -0,0 +1,20 @@
+package xiaozhi.modules.sys.vo;
+
+import java.io.Serializable;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 瀛楀吀鏁版嵁VO
+ */
+@Data
+@Schema(description = "瀛楀吀鏁版嵁椤�")
+public class SysDictDataItem implements Serializable {
+
+ @Schema(description = "瀛楀吀鏍囩")
+ private String name;
+
+ @Schema(description = "瀛楀吀鍊�")
+ private String key;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/vo/SysDictDataVO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/vo/SysDictDataVO.java
new file mode 100644
index 0000000..40025b7
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/vo/SysDictDataVO.java
@@ -0,0 +1,50 @@
+package xiaozhi.modules.sys.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 瀛楀吀鏁版嵁VO
+ */
+@Data
+@Schema(description = "瀛楀吀鏁版嵁VO")
+public class SysDictDataVO implements Serializable {
+ @Schema(description = "涓婚敭")
+ private Long id;
+
+ @Schema(description = "瀛楀吀绫诲瀷ID")
+ private Long dictTypeId;
+
+ @Schema(description = "瀛楀吀鏍囩")
+ private String dictLabel;
+
+ @Schema(description = "瀛楀吀鍊�")
+ private String dictValue;
+
+ @Schema(description = "澶囨敞")
+ private String remark;
+
+ @Schema(description = "鎺掑簭")
+ private Integer sort;
+
+ @Schema(description = "鍒涘缓鑰�")
+ private Long creator;
+
+ @Schema(description = "鍒涘缓鑰呭悕绉�")
+ private String creatorName;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ private Date createDate;
+
+ @Schema(description = "鏇存柊鑰�")
+ private Long updater;
+
+ @Schema(description = "鏇存柊鑰呭悕绉�")
+ private String updaterName;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ private Date updateDate;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/vo/SysDictTypeVO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/vo/SysDictTypeVO.java
new file mode 100644
index 0000000..5e5844f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/sys/vo/SysDictTypeVO.java
@@ -0,0 +1,47 @@
+package xiaozhi.modules.sys.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 瀛楀吀绫诲瀷VO
+ */
+@Data
+@Schema(description = "瀛楀吀绫诲瀷VO")
+public class SysDictTypeVO implements Serializable {
+ @Schema(description = "涓婚敭")
+ private Long id;
+
+ @Schema(description = "瀛楀吀绫诲瀷")
+ private String dictType;
+
+ @Schema(description = "瀛楀吀鍚嶇О")
+ private String dictName;
+
+ @Schema(description = "澶囨敞")
+ private String remark;
+
+ @Schema(description = "鎺掑簭")
+ private Integer sort;
+
+ @Schema(description = "鍒涘缓鑰�")
+ private Long creator;
+
+ @Schema(description = "鍒涘缓鑰呭悕绉�")
+ private String creatorName;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ private Date createDate;
+
+ @Schema(description = "鏇存柊鑰�")
+ private Long updater;
+
+ @Schema(description = "鏇存柊鑰呭悕绉�")
+ private String updaterName;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ private Date updateDate;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/controller/TimbreController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/controller/TimbreController.java
new file mode 100644
index 0000000..ff420c0
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/controller/TimbreController.java
@@ -0,0 +1,92 @@
+package xiaozhi.modules.timbre.controller;
+
+import java.util.Map;
+
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.utils.Result;
+import xiaozhi.common.validator.ValidatorUtils;
+import xiaozhi.modules.timbre.dto.TimbreDataDTO;
+import xiaozhi.modules.timbre.dto.TimbrePageDTO;
+import xiaozhi.modules.timbre.service.TimbreService;
+import xiaozhi.modules.timbre.vo.TimbreDetailsVO;
+
+/**
+ * 闊宠壊鎺у埗灞�
+ *
+ * @author zjy
+ * @since 2025-3-21
+ */
+@AllArgsConstructor
+@RestController
+@RequestMapping("/ttsVoice")
+@Tag(name = "闊宠壊绠$悊")
+public class TimbreController {
+ private final TimbreService timbreService;
+
+ @GetMapping
+ @Operation(summary = "鍒嗛〉鏌ユ壘")
+ @RequiresPermissions("sys:role:superAdmin")
+ @Parameters({
+ @Parameter(name = "ttsModelId", description = "瀵瑰簲 TTS 妯″瀷涓婚敭", required = true),
+ @Parameter(name = "name", description = "闊宠壊鍚嶇О"),
+ @Parameter(name = Constant.PAGE, description = "褰撳墠椤电爜锛屼粠1寮�濮�", required = true),
+ @Parameter(name = Constant.LIMIT, description = "姣忛〉鏄剧ず璁板綍鏁�", required = true),
+ })
+ public Result<PageData<TimbreDetailsVO>> page(
+ @Parameter(hidden = true) @RequestParam Map<String, Object> params) {
+ TimbrePageDTO dto = new TimbrePageDTO();
+ dto.setTtsModelId((String) params.get("ttsModelId"));
+ dto.setName((String) params.get("name"));
+ dto.setLimit((String) params.get(Constant.LIMIT));
+ dto.setPage((String) params.get(Constant.PAGE));
+
+ ValidatorUtils.validateEntity(dto);
+ PageData<TimbreDetailsVO> page = timbreService.page(dto);
+ return new Result<PageData<TimbreDetailsVO>>().ok(page);
+ }
+
+ @PostMapping
+ @Operation(summary = "闊宠壊淇濆瓨")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<Void> save(@RequestBody TimbreDataDTO dto) {
+ ValidatorUtils.validateEntity(dto);
+ timbreService.save(dto);
+ return new Result<>();
+ }
+
+ @PutMapping("/{id}")
+ @Operation(summary = "闊宠壊淇敼")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<Void> update(
+ @PathVariable String id,
+ @RequestBody TimbreDataDTO dto) {
+ ValidatorUtils.validateEntity(dto);
+ timbreService.update(id, dto);
+ return new Result<>();
+ }
+
+ @PostMapping("/delete")
+ @Operation(summary = "闊宠壊鍒犻櫎")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<Void> delete(@RequestBody String[] ids) {
+ timbreService.delete(ids);
+ return new Result<>();
+ }
+
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/dao/TimbreDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/dao/TimbreDao.java
new file mode 100644
index 0000000..3656b4e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/dao/TimbreDao.java
@@ -0,0 +1,17 @@
+package xiaozhi.modules.timbre.dao;
+
+import org.apache.ibatis.annotations.Mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+import xiaozhi.modules.timbre.entity.TimbreEntity;
+
+/**
+ * 闊宠壊鎸佷箙灞傚畾涔�
+ *
+ * @author zjy
+ * @since 2025-3-21
+ */
+@Mapper
+public interface TimbreDao extends BaseMapper<TimbreEntity> {
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/dto/TimbreDataDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/dto/TimbreDataDTO.java
new file mode 100644
index 0000000..3f43b53
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/dto/TimbreDataDTO.java
@@ -0,0 +1,49 @@
+package xiaozhi.modules.timbre.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+/**
+ * 闊宠壊琛ㄦ暟鎹瓺TO
+ *
+ * @author zjy
+ * @since 2025-3-21
+ */
+@Data
+@Schema(description = "闊宠壊琛ㄤ俊鎭�")
+public class TimbreDataDTO {
+
+ @Schema(description = "璇█")
+ @NotBlank(message = "{timbre.languages.require}")
+ private String languages;
+
+ @Schema(description = "闊宠壊鍚嶇О")
+ @NotBlank(message = "{timbre.name.require}")
+ private String name;
+
+ @Schema(description = "澶囨敞")
+ private String remark;
+
+ @Schema(description = "鍙傝�冮煶棰戣矾寰�")
+ private String referenceAudio;
+
+ @Schema(description = "鍙冭�冩枃鏈�")
+ private String referenceText;
+
+ @Schema(description = "鎺掑簭")
+ @Min(value = 0, message = "{sort.number}")
+ private Long sort;
+
+ @Schema(description = "瀵瑰簲 TTS 妯″瀷涓婚敭")
+ @NotBlank(message = "{timbre.ttsModelId.require}")
+ private String ttsModelId;
+
+ @Schema(description = "闊宠壊缂栫爜")
+ @NotBlank(message = "{timbre.ttsVoice.require}")
+ private String ttsVoice;
+
+ @Schema(description = "闊抽鎾斁鍦板潃")
+ private String voiceDemo;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/dto/TimbrePageDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/dto/TimbrePageDTO.java
new file mode 100644
index 0000000..9c2aaf2
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/dto/TimbrePageDTO.java
@@ -0,0 +1,29 @@
+package xiaozhi.modules.timbre.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+/**
+ * 闊宠壊鍒嗛〉鍙傛暟DTO
+ *
+ * @author zjy
+ * @since 2025-3-21
+ */
+@Data
+@Schema(description = "闊宠壊鍒嗛〉鍙傛暟")
+public class TimbrePageDTO {
+
+ @Schema(description = "瀵瑰簲 TTS 妯″瀷涓婚敭")
+ @NotBlank(message = "{timbre.ttsModelId.require}")
+ private String ttsModelId;
+
+ @Schema(description = "闊宠壊鍚嶇О")
+ private String name;
+
+ @Schema(description = "椤垫暟")
+ private String page;
+
+ @Schema(description = "鏄剧ず鍒楁暟")
+ private String limit;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/entity/TimbreEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/entity/TimbreEntity.java
new file mode 100644
index 0000000..d7b192a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/entity/TimbreEntity.java
@@ -0,0 +1,73 @@
+package xiaozhi.modules.timbre.entity;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 闊宠壊琛ㄥ疄浣撶被
+ *
+ * @author zjy
+ * @since 2025-3-21
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ai_tts_voice")
+@Schema(description = "闊宠壊淇℃伅")
+public class TimbreEntity {
+
+ @Schema(description = "id")
+ private String id;
+
+ @Schema(description = "璇█")
+ private String languages;
+
+ @Schema(description = "闊宠壊鍚嶇О")
+ private String name;
+
+ @Schema(description = "澶囨敞")
+ private String remark;
+
+ @Schema(description = "鍙傝�冮煶棰戣矾寰�")
+ private String referenceAudio;
+
+ @Schema(description = "鍙冭�冩枃鏈�")
+ private String referenceText;
+
+ @Schema(description = "鎺掑簭")
+ @TableField(updateStrategy = FieldStrategy.NOT_NULL)
+ private Long sort;
+
+ @Schema(description = "瀵瑰簲 TTS 妯″瀷涓婚敭")
+ private String ttsModelId;
+
+ @Schema(description = "闊宠壊缂栫爜")
+ private String ttsVoice;
+
+ @Schema(description = "闊抽鎾斁鍦板潃")
+ private String voiceDemo;
+
+ @Schema(description = "鏇存柊鑰�")
+ @TableField(fill = FieldFill.UPDATE)
+ private Long updater;
+
+ @Schema(description = "鏇存柊鏃堕棿")
+ @TableField(fill = FieldFill.UPDATE)
+ private Date updateDate;
+
+ @Schema(description = "鍒涘缓鑰�")
+ @TableField(fill = FieldFill.INSERT)
+ private Long creator;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ @TableField(fill = FieldFill.INSERT)
+ private Date createDate;
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/service/TimbreService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/service/TimbreService.java
new file mode 100644
index 0000000..3ba32f5
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/service/TimbreService.java
@@ -0,0 +1,84 @@
+package xiaozhi.modules.timbre.service;
+
+import java.util.List;
+
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.service.BaseService;
+import xiaozhi.modules.model.dto.VoiceDTO;
+import xiaozhi.modules.timbre.dto.TimbreDataDTO;
+import xiaozhi.modules.timbre.dto.TimbrePageDTO;
+import xiaozhi.modules.timbre.entity.TimbreEntity;
+import xiaozhi.modules.timbre.vo.TimbreDetailsVO;
+
+/**
+ * 闊宠壊鐨勪笟鍔″眰鐨勫畾涔�
+ *
+ * @author zjy
+ * @since 2025-3-21
+ */
+public interface TimbreService extends BaseService<TimbreEntity> {
+ /**
+ * 鍒嗛〉鑾峰彇闊宠壊鎸囧畾tts鐨勪笅鐨勯煶鑹�
+ *
+ * @param dto 鍒嗛〉鏌ユ壘鍙傛暟
+ * @return 闊宠壊鍒楄〃鍒嗛〉鏁版嵁
+ */
+ PageData<TimbreDetailsVO> page(TimbrePageDTO dto);
+
+ /**
+ * 鑾峰彇闊宠壊鎸囧畾id鐨勮鎯呬俊鎭�
+ *
+ * @param timbreId 闊宠壊琛╥d
+ * @return 闊宠壊淇℃伅
+ */
+ TimbreDetailsVO get(String timbreId);
+
+ /**
+ * 淇濆瓨闊宠壊淇℃伅
+ *
+ * @param dto 闇�瑕佷繚瀛樻暟鎹�
+ */
+ void save(TimbreDataDTO dto);
+
+ /**
+ * 淇濆瓨闊宠壊淇℃伅
+ *
+ * @param timbreId 闇�瑕佷慨鏀圭殑id
+ * @param dto 闇�瑕佷慨鏀圭殑鏁版嵁
+ */
+ void update(String timbreId, TimbreDataDTO dto);
+
+ /**
+ * 鎵归噺鍒犻櫎闊宠壊
+ *
+ * @param ids 闇�瑕佽鍒犻櫎鐨勯煶鑹瞚d鍒楄〃
+ */
+ void delete(String[] ids);
+
+ List<VoiceDTO> getVoiceNames(String ttsModelId, String voiceName);
+
+ /**
+ * 鑾峰彇鏅�氶煶鑹叉垨鍏嬮殕闊宠壊閰嶇疆鐨勯涓湁鏁堣瑷�銆�
+ *
+ * @param id 闊宠壊ID
+ * @return 榛樿璇█锛涢煶鑹蹭笉瀛樺湪鎴栨湭閰嶇疆鏈夋晥璇█鏃惰繑鍥瀗ull
+ */
+ String getDefaultLanguageById(String id);
+
+ /**
+ * 鏍规嵁ID鑾峰彇闊宠壊鍚嶇О
+ *
+ * @param id 闊宠壊ID
+ * @return 闊宠壊鍚嶇О
+ */
+ String getTimbreNameById(String id);
+
+ /**
+ * 鏍规嵁闊宠壊缂栫爜鑾峰彇闊宠壊淇℃伅
+ *
+ * @param ttsModelId 闊宠壊妯″瀷ID
+ * @param voiceCode 闊宠壊缂栫爜
+ * @return 闊宠壊淇℃伅
+ */
+ VoiceDTO getByVoiceCode(String ttsModelId, String voiceCode);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/service/impl/TimbreServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/service/impl/TimbreServiceImpl.java
new file mode 100644
index 0000000..38c639a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/service/impl/TimbreServiceImpl.java
@@ -0,0 +1,249 @@
+package xiaozhi.modules.timbre.service.impl;
+
+import java.util.*;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+
+import cn.hutool.core.collection.CollectionUtil;
+import lombok.AllArgsConstructor;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.redis.RedisKeys;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.common.service.impl.BaseServiceImpl;
+import xiaozhi.common.utils.ConvertUtils;
+import xiaozhi.common.utils.MessageUtils;
+import xiaozhi.modules.model.dto.VoiceDTO;
+import xiaozhi.modules.security.user.SecurityUser;
+import xiaozhi.modules.timbre.dao.TimbreDao;
+import xiaozhi.modules.timbre.dto.TimbreDataDTO;
+import xiaozhi.modules.timbre.dto.TimbrePageDTO;
+import xiaozhi.modules.timbre.entity.TimbreEntity;
+import xiaozhi.modules.timbre.service.TimbreService;
+import xiaozhi.modules.timbre.vo.TimbreDetailsVO;
+import xiaozhi.modules.voiceclone.dao.VoiceCloneDao;
+import xiaozhi.modules.voiceclone.entity.VoiceCloneEntity;
+
+/**
+ * 闊宠壊鐨勪笟鍔″眰鐨勫疄鐜�
+ *
+ * @author zjy
+ * @since 2025-3-21
+ */
+@AllArgsConstructor
+@Service
+public class TimbreServiceImpl extends BaseServiceImpl<TimbreDao, TimbreEntity> implements TimbreService {
+
+ private static final Pattern LANGUAGE_SEPARATOR = Pattern.compile("[銆侊紱;,锛宂");
+
+ private final TimbreDao timbreDao;
+ private final VoiceCloneDao voiceCloneDao;
+ private final RedisUtils redisUtils;
+
+ @Override
+ public PageData<TimbreDetailsVO> page(TimbrePageDTO dto) {
+ Map<String, Object> params = new HashMap<String, Object>();
+ params.put(Constant.PAGE, dto.getPage());
+ params.put(Constant.LIMIT, dto.getLimit());
+ IPage<TimbreEntity> page = baseDao.selectPage(
+ getPage(params, null, true),
+ // 瀹氫箟鏌ヨ鏉′欢
+ new QueryWrapper<TimbreEntity>()
+ // 蹇呴』鎸夌収ttsID鏌ユ壘
+ .eq("tts_model_id", dto.getTtsModelId())
+ // 濡傛灉鏈夐煶鑹插悕瀛楋紝鎸夌収闊宠壊鍚嶆ā绯婃煡鎵�
+ .like(StringUtils.isNotBlank(dto.getName()), "name", dto.getName()));
+
+ return getPageData(page, TimbreDetailsVO.class);
+ }
+
+ @Override
+ public TimbreDetailsVO get(String timbreId) {
+ if (StringUtils.isBlank(timbreId)) {
+ return null;
+ }
+
+ // 鍏堜粠Redis鑾峰彇缂撳瓨
+ String key = RedisKeys.getTimbreDetailsKey(timbreId);
+ TimbreDetailsVO cachedDetails = (TimbreDetailsVO) redisUtils.get(key);
+ if (cachedDetails != null) {
+ return cachedDetails;
+ }
+
+ // 濡傛灉缂撳瓨涓病鏈夛紝鍒欎粠鏁版嵁搴撹幏鍙�
+ TimbreEntity entity = baseDao.selectById(timbreId);
+ if (entity == null) {
+ return null;
+ }
+
+ // 杞崲涓篤O瀵硅薄
+ TimbreDetailsVO details = ConvertUtils.sourceToTarget(entity, TimbreDetailsVO.class);
+
+ // 瀛樺叆Redis缂撳瓨
+ if (details != null) {
+ redisUtils.set(key, details);
+ }
+
+ return details;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void save(TimbreDataDTO dto) {
+ isTtsModelId(dto.getTtsModelId());
+ if (dto.getSort() == null) {
+ dto.setSort(0L);
+ }
+ TimbreEntity timbreEntity = ConvertUtils.sourceToTarget(dto, TimbreEntity.class);
+ baseDao.insert(timbreEntity);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void update(String timbreId, TimbreDataDTO dto) {
+ isTtsModelId(dto.getTtsModelId());
+ TimbreEntity timbreEntity = ConvertUtils.sourceToTarget(dto, TimbreEntity.class);
+ timbreEntity.setId(timbreId);
+ baseDao.updateById(timbreEntity);
+ // 鍒犻櫎缂撳瓨
+ redisUtils.delete(RedisKeys.getTimbreDetailsKey(timbreId));
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void delete(String[] ids) {
+ baseDao.deleteByIds(Arrays.asList(ids));
+ }
+
+ @Override
+ public List<VoiceDTO> getVoiceNames(String ttsModelId, String voiceName) {
+ QueryWrapper<TimbreEntity> queryWrapper = new QueryWrapper<>();
+ queryWrapper.eq("tts_model_id", StringUtils.isBlank(ttsModelId) ? "" : ttsModelId);
+ if (StringUtils.isNotBlank(voiceName)) {
+ queryWrapper.like("name", voiceName);
+ }
+ List<TimbreEntity> timbreEntities = Optional.ofNullable(timbreDao.selectList(queryWrapper)).orElseGet(ArrayList::new);
+ List<VoiceDTO> voiceDTOs = timbreEntities.stream()
+ .map(entity -> {
+ VoiceDTO dto = new VoiceDTO(entity.getId(), entity.getName());
+ dto.setVoiceDemo(entity.getVoiceDemo());
+ dto.setLanguages(entity.getLanguages()); // 璁剧疆璇█绫诲瀷
+ dto.setIsClone(false); // 璁剧疆涓烘櫘閫氶煶鑹�
+ return dto;
+ })
+ .collect(Collectors.toList());
+
+ // 鑾峰彇褰撳墠鐧诲綍鐢ㄦ埛ID
+ Long currentUserId = SecurityUser.getUser().getId();
+ if (currentUserId != null) {
+ // 鏌ヨ鐢ㄦ埛鐨勬墍鏈夊厠闅嗛煶鑹茶褰�
+ List<VoiceDTO> cloneEntities = voiceCloneDao.getTrainSuccess(ttsModelId, currentUserId);
+ for (VoiceDTO entity : cloneEntities) {
+ // 鍙坊鍔犺缁冩垚鍔熺殑鍏嬮殕闊宠壊锛屼笖妯″瀷ID鍖归厤
+ VoiceDTO voiceDTO = new VoiceDTO();
+ voiceDTO.setId(entity.getId());
+ voiceDTO.setName(MessageUtils.getMessage(ErrorCode.VOICE_CLONE_PREFIX) + entity.getName());
+ // 淇濈暀浠庢暟鎹簱鏌ヨ鍒扮殑voiceDemo瀛楁
+ voiceDTO.setVoiceDemo(entity.getVoiceDemo());
+ voiceDTO.setLanguages(entity.getLanguages());
+ voiceDTO.setIsClone(true); // 璁剧疆涓哄厠闅嗛煶鑹�
+ redisUtils.set(RedisKeys.getTimbreNameById(voiceDTO.getId()), voiceDTO.getName(),
+ RedisUtils.NOT_EXPIRE);
+ voiceDTOs.add(0, voiceDTO);
+ }
+ }
+
+ return CollectionUtil.isEmpty(voiceDTOs) ? null : voiceDTOs;
+ }
+
+ @Override
+ public String getDefaultLanguageById(String id) {
+ if (StringUtils.isBlank(id)) {
+ return null;
+ }
+
+ TimbreEntity timbre = timbreDao.selectById(id);
+ if (timbre != null) {
+ return firstNonBlankLanguage(timbre.getLanguages());
+ }
+
+ VoiceCloneEntity voiceClone = voiceCloneDao.selectById(id);
+ return voiceClone == null ? null : firstNonBlankLanguage(voiceClone.getLanguages());
+ }
+
+ private String firstNonBlankLanguage(String languages) {
+ if (StringUtils.isBlank(languages)) {
+ return null;
+ }
+ return LANGUAGE_SEPARATOR.splitAsStream(languages)
+ .map(StringUtils::trimToNull)
+ .filter(Objects::nonNull)
+ .findFirst()
+ .orElse(null);
+ }
+
+ /**
+ * 澶勭悊鏄笉鏄痶ts妯″瀷鐨刬d
+ */
+ private void isTtsModelId(String ttsModelId) {
+ // 绛夋ā鍨嬮厤缃偅杈瑰啓濂借皟鐢ㄦ柟娉曞垽鏂�
+ }
+
+ @Override
+ public String getTimbreNameById(String id) {
+ if (StringUtils.isBlank(id)) {
+ return null;
+ }
+
+ String cachedName = (String) redisUtils.get(RedisKeys.getTimbreNameById(id));
+
+ if (StringUtils.isNotBlank(cachedName)) {
+ return cachedName;
+ }
+
+ TimbreEntity entity = timbreDao.selectById(id);
+ if (entity != null) {
+ String name = entity.getName();
+ if (StringUtils.isNotBlank(name)) {
+ redisUtils.set(RedisKeys.getTimbreNameById(id), name);
+ }
+ return name;
+ } else {
+ VoiceCloneEntity cloneEntity = voiceCloneDao.selectById(id);
+ if (cloneEntity != null) {
+ String name = MessageUtils.getMessage(ErrorCode.VOICE_CLONE_PREFIX) + cloneEntity.getName();
+ redisUtils.set(RedisKeys.getTimbreNameById(id), name);
+ return name;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public VoiceDTO getByVoiceCode(String ttsModelId, String voiceCode) {
+ if (StringUtils.isBlank(voiceCode)) {
+ return null;
+ }
+ QueryWrapper<TimbreEntity> queryWrapper = new QueryWrapper<>();
+ queryWrapper.eq("tts_model_id", ttsModelId);
+ queryWrapper.eq("tts_voice", voiceCode);
+ List<TimbreEntity> list = timbreDao.selectList(queryWrapper);
+ if (list.isEmpty()) {
+ return null;
+ }
+ TimbreEntity entity = list.get(0);
+ VoiceDTO dto = new VoiceDTO(entity.getId(), entity.getName());
+ dto.setVoiceDemo(entity.getVoiceDemo());
+ dto.setIsClone(false); // 璁剧疆涓烘櫘閫氶煶鑹�
+ return dto;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/vo/TimbreDetailsVO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/vo/TimbreDetailsVO.java
new file mode 100644
index 0000000..be32b43
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/timbre/vo/TimbreDetailsVO.java
@@ -0,0 +1,46 @@
+package xiaozhi.modules.timbre.vo;
+
+import java.io.Serializable;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 闊宠壊璇︽儏灞曠ずVO
+ *
+ * @author zjy
+ * @since 2025-3-21
+ */
+@Data
+public class TimbreDetailsVO implements Serializable {
+ @Schema(description = "闊宠壊id")
+ private String id;
+
+ @Schema(description = "璇█")
+ private String languages;
+
+ @Schema(description = "闊宠壊鍚嶇О")
+ private String name;
+
+ @Schema(description = "澶囨敞")
+ private String remark;
+
+ @Schema(description = "鍙傝�冮煶棰戣矾寰�")
+ private String referenceAudio;
+
+ @Schema(description = "鍙冭�冩枃鏈�")
+ private String referenceText;
+
+ @Schema(description = "鎺掑簭")
+ private Long sort;
+
+ @Schema(description = "瀵瑰簲 TTS 妯″瀷涓婚敭")
+ private String ttsModelId;
+
+ @Schema(description = "闊宠壊缂栫爜")
+ private String ttsVoice;
+
+ @Schema(description = "闊抽鎾斁鍦板潃")
+ private String voiceDemo;
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/controller/VoiceCloneController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/controller/VoiceCloneController.java
new file mode 100644
index 0000000..21370a4
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/controller/VoiceCloneController.java
@@ -0,0 +1,200 @@
+package xiaozhi.modules.voiceclone.controller;
+
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.redis.RedisKeys;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.common.user.UserDetail;
+import xiaozhi.common.utils.Result;
+import xiaozhi.common.validator.ValidatorUtils;
+import xiaozhi.modules.security.user.SecurityUser;
+import xiaozhi.modules.voiceclone.dto.VoiceCloneResponseDTO;
+import xiaozhi.modules.voiceclone.entity.VoiceCloneEntity;
+import xiaozhi.modules.voiceclone.service.VoiceCloneService;
+
+@Tag(name = "闊宠壊璧勬簮绠$悊", description = "闊宠壊璧勬簮寮�閫氱浉鍏虫帴鍙�")
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/voiceClone")
+public class VoiceCloneController {
+
+ private final VoiceCloneService voiceCloneService;
+ private final RedisUtils redisUtils;
+
+ @GetMapping
+ @Operation(summary = "鍒嗛〉鏌ヨ闊宠壊璧勬簮")
+ @Parameters({
+ @Parameter(name = Constant.PAGE, description = "褰撳墠椤电爜锛屼粠1寮�濮�", required = true),
+ @Parameter(name = Constant.LIMIT, description = "姣忛〉鏄剧ず璁板綍鏁�", required = true)
+ })
+ @RequiresPermissions("sys:role:normal")
+ public Result<PageData<VoiceCloneResponseDTO>> page(
+ @Parameter(hidden = true) @RequestParam Map<String, Object> params) {
+ ValidatorUtils.validateEntity(params);
+ UserDetail user = SecurityUser.getUser();
+ params.put("userId", user.getId().toString());
+ PageData<VoiceCloneResponseDTO> page = voiceCloneService.pageWithNames(params);
+ return new Result<PageData<VoiceCloneResponseDTO>>().ok(page);
+ }
+
+ @PostMapping("/upload")
+ @Operation(summary = "涓婁紶闊抽杩涜澹伴煶鍏嬮殕")
+ @Parameters({
+ @Parameter(name = "id", description = "澹伴煶鍏嬮殕璁板綍ID", required = true),
+ @Parameter(name = "voiceFile", description = "闊抽鏂囦欢", required = true)
+ })
+ @RequiresPermissions("sys:role:normal")
+ public Result<String> uploadVoice(
+ @RequestParam("id") String id,
+ @RequestParam("voiceFile") MultipartFile voiceFile) {
+ try {
+ // 楠岃瘉鏂囦欢
+ if (voiceFile == null || voiceFile.isEmpty()) {
+ return new Result<String>().error(ErrorCode.VOICE_CLONE_AUDIO_EMPTY);
+ }
+
+ // 楠岃瘉鏂囦欢绫诲瀷
+ String contentType = voiceFile.getContentType();
+ if (contentType == null || !contentType.startsWith("audio/")) {
+ return new Result<String>().error(ErrorCode.VOICE_CLONE_NOT_AUDIO_FILE);
+ }
+
+ // 鍔犲己楠岃瘉鏂囦欢鎵╁睍鍚�
+ String originalFilename = voiceFile.getOriginalFilename();
+ String extension = originalFilename.substring(originalFilename.lastIndexOf(".")).toLowerCase();
+ if (!extension.equals(".mp3") && !extension.equals(".wav")) {
+ return new Result<String>().error("鍙厑璁镐笂浼�.mp3鍜�.wav鏍煎紡鐨勬枃浠�");
+ }
+
+ // 楠岃瘉鏂囦欢澶у皬 (鏈�澶�10MB)
+ if (voiceFile.getSize() > 10 * 1024 * 1024) {
+ return new Result<String>().error(ErrorCode.VOICE_CLONE_AUDIO_TOO_LARGE);
+ }
+ // 妫�鏌ユ潈闄�
+ checkPermission(id);
+ // 璋冪敤鏈嶅姟灞傚鐞�
+ voiceCloneService.uploadVoice(id, voiceFile);
+
+ return new Result<String>();
+ } catch (Exception e) {
+ return new Result<String>().error(ErrorCode.VOICE_CLONE_UPLOAD_FAILED, e.getMessage());
+ }
+ }
+
+ @PostMapping("/updateName")
+ @Operation(summary = "鏇存柊澹伴煶鍏嬮殕鍚嶇О")
+ @RequiresPermissions("sys:role:normal")
+ public Result<String> updateName(@RequestBody Map<String, String> params) {
+ try {
+ String id = params.get("id");
+ String name = params.get("name");
+
+ if (id == null || id.isEmpty()) {
+ return new Result<String>().error(ErrorCode.IDENTIFIER_NOT_NULL);
+ }
+ if (name == null || name.isEmpty()) {
+ return new Result<String>().error(ErrorCode.VOICE_CLONE_NAME_NOT_NULL);
+ }
+ // 妫�鏌ユ潈闄�
+ checkPermission(id);
+
+ voiceCloneService.updateName(id, name);
+ redisUtils.delete(RedisKeys.getTimbreNameById(id));
+ return new Result<String>();
+ } catch (Exception e) {
+ return new Result<String>().error(ErrorCode.UPDATE_DATA_FAILED, e.getMessage());
+ }
+ }
+
+ @PostMapping("/audio/{id}")
+ @Operation(summary = "鑾峰彇闊抽涓嬭浇ID")
+ @RequiresPermissions("sys:role:normal")
+ public Result<String> getAudioId(@PathVariable("id") String id) {
+ // 妫�鏌ユ潈闄�
+ checkPermission(id);
+ byte[] audioData = voiceCloneService.getVoiceData(id);
+ if (audioData == null) {
+ return new Result<String>().error(ErrorCode.VOICE_CLONE_AUDIO_NOT_FOUND);
+ }
+ String uuid = UUID.randomUUID().toString();
+ redisUtils.set(RedisKeys.getVoiceCloneAudioIdKey(uuid), id);
+ return new Result<String>().ok(uuid);
+ }
+
+ @GetMapping("/play/{uuid}")
+ @Operation(summary = "鎾斁闊抽")
+ public void playVoice(@PathVariable("uuid") String uuid, HttpServletResponse response) {
+ try {
+ String id = (String) redisUtils.get(RedisKeys.getVoiceCloneAudioIdKey(uuid));
+ redisUtils.delete(RedisKeys.getVoiceCloneAudioIdKey(uuid));
+ if (StringUtils.isBlank(id)) {
+ response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+ // 鑾峰彇闊抽鏁版嵁
+ byte[] voiceData = voiceCloneService.getVoiceData(id);
+
+ if (voiceData == null || voiceData.length == 0) {
+ response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+
+ // 璁剧疆鍝嶅簲澶�
+ response.setContentType("audio/wav");
+ response.setContentLength(voiceData.length);
+ response.setHeader("Content-Disposition", "inline; filename=voice.wav");
+
+ // 鍐欏叆闊抽鏁版嵁
+ response.getOutputStream().write(voiceData);
+ response.getOutputStream().flush();
+ } catch (Exception e) {
+ log.error("鎾斁闊抽澶辫触", e);
+ response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ @PostMapping("/cloneAudio")
+ @Operation(summary = "澶嶅埢闊抽")
+ @RequiresPermissions("sys:role:normal")
+ public Result<String> cloneAudio(@RequestBody Map<String, String> params) {
+ String cloneId = params.get("cloneId");
+ checkPermission(cloneId);
+ // 璋冪敤鏈嶅姟灞傝繘琛岃闊冲厠闅嗚缁�
+ voiceCloneService.cloneAudio(cloneId);
+ return new Result<String>();
+ }
+
+ private void checkPermission(String id) {
+ VoiceCloneEntity voiceClone = voiceCloneService.selectById(id);
+ if (voiceClone == null) {
+ throw new RenException(ErrorCode.VOICE_CLONE_RECORD_NOT_EXIST);
+ }
+ if (!voiceClone.getUserId().equals(SecurityUser.getUser().getId())) {
+ throw new RenException(ErrorCode.VOICE_RESOURCE_NO_PERMISSION);
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/controller/VoiceResourceController.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/controller/VoiceResourceController.java
new file mode 100644
index 0000000..a4cfa47
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/controller/VoiceResourceController.java
@@ -0,0 +1,116 @@
+package xiaozhi.modules.voiceclone.controller;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.utils.Result;
+import xiaozhi.common.validator.ValidatorUtils;
+import xiaozhi.modules.model.service.ModelConfigService;
+import xiaozhi.modules.voiceclone.dto.VoiceCloneDTO;
+import xiaozhi.modules.voiceclone.dto.VoiceCloneResponseDTO;
+import xiaozhi.modules.voiceclone.service.VoiceCloneService;
+
+@Tag(name = "闊宠壊璧勬簮绠$悊", description = "闊宠壊璧勬簮寮�閫氱浉鍏虫帴鍙�")
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/voiceResource")
+public class VoiceResourceController {
+
+ private final VoiceCloneService voiceCloneService;
+ private final ModelConfigService modelConfigService;
+
+ @GetMapping
+ @Operation(summary = "鍒嗛〉鏌ヨ闊宠壊璧勬簮")
+ @Parameters({
+ @Parameter(name = Constant.PAGE, description = "褰撳墠椤电爜锛屼粠1寮�濮�", required = true),
+ @Parameter(name = Constant.LIMIT, description = "姣忛〉鏄剧ず璁板綍鏁�", required = true)
+ })
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<PageData<VoiceCloneResponseDTO>> page(
+ @Parameter(hidden = true) @RequestParam Map<String, Object> params) {
+ ValidatorUtils.validateEntity(params);
+ PageData<VoiceCloneResponseDTO> page = voiceCloneService.pageWithNames(params);
+ return new Result<PageData<VoiceCloneResponseDTO>>().ok(page);
+ }
+
+ @GetMapping("{id}")
+ @Operation(summary = "鑾峰彇闊宠壊璧勬簮璇︽儏")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<VoiceCloneResponseDTO> get(@PathVariable("id") String id) {
+ VoiceCloneResponseDTO data = voiceCloneService.getByIdWithNames(id);
+ return new Result<VoiceCloneResponseDTO>().ok(data);
+ }
+
+ @PostMapping
+ @Operation(summary = "鏂板闊宠壊璧勬簮")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<Void> save(@RequestBody VoiceCloneDTO dto) {
+ if (dto == null) {
+ return new Result<Void>().error(ErrorCode.VOICE_RESOURCE_INFO_EMPTY);
+ }
+ if (dto.getModelId() == null || dto.getModelId().isEmpty()) {
+ return new Result<Void>().error(ErrorCode.VOICE_RESOURCE_PLATFORM_NAME_EMPTY);
+ }
+ if (dto.getVoiceIds() == null || dto.getVoiceIds().isEmpty()) {
+ return new Result<Void>().error(ErrorCode.VOICE_RESOURCE_ID_EMPTY);
+ }
+ if (dto.getUserId() == null) {
+ return new Result<Void>().error(ErrorCode.VOICE_RESOURCE_ACCOUNT_EMPTY);
+ }
+ try {
+ voiceCloneService.save(dto);
+ return new Result<Void>();
+ } catch (xiaozhi.common.exception.RenException e) {
+ return new Result<Void>().error(e.getCode(), e.getMsg());
+ } catch (RuntimeException e) {
+ return new Result<Void>().error(ErrorCode.ADD_DATA_FAILED, e.getMessage());
+ }
+ }
+
+ @DeleteMapping("/{id}")
+ @Operation(summary = "鍒犻櫎闊宠壊璧勬簮")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<Void> delete(@PathVariable("id") String[] ids) {
+ if (ids == null || ids.length == 0) {
+ return new Result<Void>().error(ErrorCode.VOICE_RESOURCE_DELETE_ID_EMPTY);
+ }
+ voiceCloneService.delete(ids);
+ return new Result<Void>();
+ }
+
+ @GetMapping("/user/{userId}")
+ @Operation(summary = "鏍规嵁鐢ㄦ埛ID鑾峰彇闊宠壊璧勬簮鍒楄〃")
+ @RequiresPermissions("sys:role:normal")
+ public Result<List<VoiceCloneResponseDTO>> getByUserId(@PathVariable("userId") Long userId) {
+ List<VoiceCloneResponseDTO> list = voiceCloneService.getByUserIdWithNames(userId);
+ return new Result<List<VoiceCloneResponseDTO>>().ok(list);
+ }
+
+ @GetMapping("/ttsPlatforms")
+ @Operation(summary = "鑾峰彇TTS骞冲彴鍒楄〃")
+ @RequiresPermissions("sys:role:superAdmin")
+ public Result<List<Map<String, Object>>> getTtsPlatformList() {
+ List<Map<String, Object>> list = modelConfigService.getTtsPlatformList();
+ return new Result<List<Map<String, Object>>>().ok(list);
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/dao/VoiceCloneDao.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/dao/VoiceCloneDao.java
new file mode 100644
index 0000000..08e0e01
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/dao/VoiceCloneDao.java
@@ -0,0 +1,26 @@
+package xiaozhi.modules.voiceclone.dao;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+import xiaozhi.modules.model.dto.VoiceDTO;
+import xiaozhi.modules.voiceclone.entity.VoiceCloneEntity;
+
+/**
+ * 澹伴煶鍏嬮殕
+ */
+@Mapper
+public interface VoiceCloneDao extends BaseMapper<VoiceCloneEntity> {
+ /**
+ * 鑾峰彇鐢ㄦ埛璁粌鎴愬姛鐨勯煶鑹插垪琛�
+ *
+ * @param modelId 妯″瀷ID
+ * @param userId 鐢ㄦ埛ID
+ * @return 璁粌鎴愬姛鐨勯煶鑹插垪琛�
+ */
+ List<VoiceDTO> getTrainSuccess(String modelId, Long userId);
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/dto/VoiceCloneDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/dto/VoiceCloneDTO.java
new file mode 100644
index 0000000..156b381
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/dto/VoiceCloneDTO.java
@@ -0,0 +1,23 @@
+package xiaozhi.modules.voiceclone.dto;
+
+import java.util.List;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@Schema(description = "澹伴煶鍏嬮殕DTO")
+public class VoiceCloneDTO {
+
+ @Schema(description = "妯″瀷ID")
+ private String modelId;
+
+ @Schema(description = "闊宠壊ID鍒楄〃")
+ private List<String> voiceIds;
+
+ @Schema(description = "鐢ㄦ埛ID")
+ private Long userId;
+
+ @Schema(description = "璇█")
+ private String languages;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/dto/VoiceCloneResponseDTO.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/dto/VoiceCloneResponseDTO.java
new file mode 100644
index 0000000..22dafe0
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/dto/VoiceCloneResponseDTO.java
@@ -0,0 +1,51 @@
+package xiaozhi.modules.voiceclone.dto;
+
+import java.util.Date;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 澹伴煶鍏嬮殕鍝嶅簲DTO
+ * 鐢ㄤ簬鍚戝墠绔睍绀哄0闊冲厠闅嗕俊鎭紝鍖呭惈妯″瀷鍚嶇О鍜岀敤鎴峰悕绉�
+ */
+@Data
+@Schema(description = "澹伴煶鍏嬮殕鍝嶅簲DTO")
+public class VoiceCloneResponseDTO {
+
+ @Schema(description = "鍞竴鏍囪瘑")
+ private String id;
+
+ @Schema(description = "澹伴煶鍚嶇О")
+ private String name;
+
+ @Schema(description = "妯″瀷id")
+ private String modelId;
+
+ @Schema(description = "妯″瀷鍚嶇О")
+ private String modelName;
+
+ @Schema(description = "澹伴煶id")
+ private String voiceId;
+
+ @Schema(description = "璇█")
+ private String languages;
+
+ @Schema(description = "鐢ㄦ埛ID锛堝叧鑱旂敤鎴疯〃锛�")
+ private Long userId;
+
+ @Schema(description = "鐢ㄦ埛鍚嶇О")
+ private String userName;
+
+ @Schema(description = "璁粌鐘舵�侊細0寰呰缁� 1璁粌涓� 2璁粌鎴愬姛 3璁粌澶辫触")
+ private Integer trainStatus;
+
+ @Schema(description = "璁粌閿欒鍘熷洜")
+ private String trainError;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ private Date createDate;
+
+ @Schema(description = "鏄惁鏈夐煶棰戞暟鎹�")
+ private Boolean hasVoice;
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/entity/VoiceCloneEntity.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/entity/VoiceCloneEntity.java
new file mode 100644
index 0000000..d78b1c9
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/entity/VoiceCloneEntity.java
@@ -0,0 +1,56 @@
+package xiaozhi.modules.voiceclone.entity;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ai_voice_clone")
+@Schema(description = "澹伴煶鍏嬮殕")
+public class VoiceCloneEntity {
+
+ @TableId(type = IdType.ASSIGN_UUID)
+ @Schema(description = "鍞竴鏍囪瘑")
+ private String id;
+
+ @Schema(description = "澹伴煶鍚嶇О")
+ private String name;
+
+ @Schema(description = "妯″瀷id")
+ private String modelId;
+
+ @Schema(description = "澹伴煶id")
+ private String voiceId;
+
+ @Schema(description = "璇█")
+ private String languages;
+
+ @Schema(description = "鐢ㄦ埛 ID锛堝叧鑱旂敤鎴疯〃锛�")
+ private Long userId;
+
+ @Schema(description = "澹伴煶")
+ private byte[] voice;
+
+ @Schema(description = "璁粌鐘舵�侊細0寰呰缁� 1璁粌涓� 2璁粌鎴愬姛 3璁粌澶辫触")
+ private Integer trainStatus;
+
+ @Schema(description = "璁粌閿欒鍘熷洜")
+ private String trainError;
+
+ @Schema(description = "鍒涘缓鑰�")
+ @TableField(fill = FieldFill.INSERT)
+ private Long creator;
+
+ @Schema(description = "鍒涘缓鏃堕棿")
+ @TableField(fill = FieldFill.INSERT)
+ private Date createDate;
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/service/VoiceCloneService.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/service/VoiceCloneService.java
new file mode 100644
index 0000000..ea8fd61
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/service/VoiceCloneService.java
@@ -0,0 +1,78 @@
+package xiaozhi.modules.voiceclone.service;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.web.multipart.MultipartFile;
+
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.service.BaseService;
+import xiaozhi.modules.voiceclone.dto.VoiceCloneDTO;
+import xiaozhi.modules.voiceclone.dto.VoiceCloneResponseDTO;
+import xiaozhi.modules.voiceclone.entity.VoiceCloneEntity;
+
+/**
+ * 澹伴煶鍏嬮殕绠$悊
+ */
+public interface VoiceCloneService extends BaseService<VoiceCloneEntity> {
+
+ /**
+ * 鍒嗛〉鏌ヨ
+ */
+ PageData<VoiceCloneEntity> page(Map<String, Object> params);
+
+ /**
+ * 淇濆瓨澹伴煶鍏嬮殕
+ */
+ void save(VoiceCloneDTO dto);
+
+ /**
+ * 鎵归噺鍒犻櫎
+ */
+ void delete(String[] ids);
+
+ /**
+ * 鏍规嵁鐢ㄦ埛ID鏌ヨ澹伴煶鍏嬮殕鍒楄〃
+ *
+ * @param userId 鐢ㄦ埛ID
+ * @return 澹伴煶鍏嬮殕鍒楄〃
+ */
+ List<VoiceCloneEntity> getByUserId(Long userId);
+
+ /**
+ * 鍒嗛〉鏌ヨ甯︽ā鍨嬪悕绉板拰鐢ㄦ埛鍚嶇О鐨勫0闊冲厠闅嗗垪琛�
+ */
+ PageData<VoiceCloneResponseDTO> pageWithNames(Map<String, Object> params);
+
+ /**
+ * 鏍规嵁ID鏌ヨ甯︽ā鍨嬪悕绉板拰鐢ㄦ埛鍚嶇О鐨勫0闊冲厠闅嗕俊鎭�
+ */
+ VoiceCloneResponseDTO getByIdWithNames(String id);
+
+ /**
+ * 鏍规嵁鐢ㄦ埛ID鏌ヨ甯︽ā鍨嬪悕绉扮殑澹伴煶鍏嬮殕鍒楄〃
+ */
+ List<VoiceCloneResponseDTO> getByUserIdWithNames(Long userId);
+
+ /**
+ * 涓婁紶闊抽鏂囦欢
+ */
+ void uploadVoice(String id, MultipartFile voiceFile) throws Exception;
+
+ /**
+ * 鏇存柊澹伴煶鍏嬮殕鍚嶇О
+ */
+ void updateName(String id, String name);
+
+ /**
+ * 鑾峰彇闊抽鏁版嵁
+ */
+ byte[] getVoiceData(String id);
+
+ /**
+ * 鍏嬮殕闊抽锛岃皟鐢ㄧ伀灞卞紩鎿庤繘琛岃闊冲鍒昏缁�
+ *
+ * @param cloneId 璇煶鍏嬮殕璁板綍ID
+ */
+ void cloneAudio(String cloneId);
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/service/impl/VoiceCloneServiceImpl.java b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/service/impl/VoiceCloneServiceImpl.java
new file mode 100644
index 0000000..17cd6ca
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/java/xiaozhi/modules/voiceclone/service/impl/VoiceCloneServiceImpl.java
@@ -0,0 +1,390 @@
+package xiaozhi.modules.voiceclone.service.impl;
+
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import cn.hutool.core.collection.CollUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.common.constant.Constant;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.page.PageData;
+import xiaozhi.common.service.impl.BaseServiceImpl;
+import xiaozhi.common.utils.ConvertUtils;
+import xiaozhi.common.utils.DateUtils;
+import xiaozhi.modules.model.entity.ModelConfigEntity;
+import xiaozhi.modules.model.service.ModelConfigService;
+import xiaozhi.modules.sys.dao.SysUserDao;
+import xiaozhi.modules.sys.entity.SysUserEntity;
+import xiaozhi.modules.sys.service.SysUserService;
+import xiaozhi.modules.voiceclone.dao.VoiceCloneDao;
+import xiaozhi.modules.voiceclone.dto.VoiceCloneDTO;
+import xiaozhi.modules.voiceclone.dto.VoiceCloneResponseDTO;
+import xiaozhi.modules.voiceclone.entity.VoiceCloneEntity;
+import xiaozhi.modules.voiceclone.service.VoiceCloneService;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class VoiceCloneServiceImpl extends BaseServiceImpl<VoiceCloneDao, VoiceCloneEntity>
+ implements VoiceCloneService {
+
+ private final ModelConfigService modelConfigService;
+ private final SysUserService sysUserService;
+ private final SysUserDao sysUserDao;
+ private final ObjectMapper objectMapper;
+
+ @Override
+ public PageData<VoiceCloneEntity> page(Map<String, Object> params) {
+ IPage<VoiceCloneEntity> page = baseDao.selectPage(
+ getPage(params, "create_date", true),
+ getWrapper(params));
+
+ return new PageData<>(page.getRecords(), page.getTotal());
+ }
+
+ private QueryWrapper<VoiceCloneEntity> getWrapper(Map<String, Object> params) {
+ String name = (String) params.get("name");
+ String userId = (String) params.get("userId");
+
+ QueryWrapper<VoiceCloneEntity> wrapper = new QueryWrapper<>();
+ wrapper.eq(StringUtils.isNotBlank(userId), "user_id", userId);
+ if (StringUtils.isNotBlank(name)) {
+ wrapper.and(w -> w.like("name", name)
+ .or().eq("voice_id", name));
+ }
+ return wrapper;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void save(VoiceCloneDTO dto) {
+ ModelConfigEntity modelConfig = modelConfigService.getModelByIdFromCache(dto.getModelId());
+ if (modelConfig == null || modelConfig.getConfigJson() == null) {
+ throw new RenException(ErrorCode.VOICE_CLONE_MODEL_CONFIG_NOT_FOUND);
+ }
+ Map<String, Object> config = modelConfig.getConfigJson();
+ String type = (String) config.get("type");
+ if (StringUtils.isBlank(type)) {
+ throw new RenException(ErrorCode.VOICE_CLONE_MODEL_TYPE_NOT_FOUND);
+ }
+
+ // 妫�鏌oice ID鏄惁宸茬粡琚娇鐢�
+ for (String voiceId : dto.getVoiceIds()) {
+ if (StringUtils.isBlank(voiceId)) {
+ continue;
+ }
+ if (Constant.VOICE_CLONE_HUOSHAN_DOUBLE_STREAM.equals(type)) {
+ if (voiceId.indexOf("S_") == -1) {
+ throw new RenException(ErrorCode.VOICE_CLONE_HUOSHAN_VOICE_ID_ERROR);
+ }
+ }
+
+ QueryWrapper<VoiceCloneEntity> wrapper = new QueryWrapper<>();
+ wrapper.eq("voice_id", voiceId);
+ wrapper.eq("model_id", dto.getModelId());
+ Long count = baseDao.selectCount(wrapper);
+ if (count > 0) {
+ throw new RenException(ErrorCode.VOICE_ID_ALREADY_EXISTS);
+ }
+ }
+
+ // 鎵归噺淇濆瓨
+ List<VoiceCloneEntity> batchInsertList = new ArrayList<>();
+ // 閬嶅巻閫夋嫨鐨勯煶鑹睮D锛屼负姣忎釜闊宠壊ID鍒涘缓涓�鏉¤褰�
+ int index = 0;
+ String namePrefix = DateUtils.format(new Date(), "MMddHHmm");
+ for (String voiceId : dto.getVoiceIds()) {
+ index++;
+ VoiceCloneEntity entity = new VoiceCloneEntity();
+ entity.setModelId(dto.getModelId());
+ entity.setVoiceId(voiceId);
+ entity.setName(namePrefix + "_" + index);
+ entity.setUserId(dto.getUserId());
+ entity.setLanguages(dto.getLanguages());
+ entity.setTrainStatus(0); // 榛樿璁粌涓�
+ batchInsertList.add(entity);
+ }
+ if (CollUtil.isNotEmpty(batchInsertList)) {
+ insertBatch(batchInsertList);
+ }
+ }
+
+ @Override
+ public void delete(String[] ids) {
+ baseDao.deleteByIds(Arrays.asList(ids));
+ }
+
+ @Override
+ public List<VoiceCloneEntity> getByUserId(Long userId) {
+ QueryWrapper<VoiceCloneEntity> wrapper = new QueryWrapper<>();
+ wrapper.eq("user_id", userId);
+ wrapper.orderByDesc("created_at");
+ return baseDao.selectList(wrapper);
+ }
+
+ @Override
+ public PageData<VoiceCloneResponseDTO> pageWithNames(Map<String, Object> params) {
+ // 鍏堟煡璇㈠垎椤垫暟鎹�
+ IPage<VoiceCloneEntity> page = baseDao.selectPage(
+ getPage(params, "create_date", true),
+ getWrapper(params));
+
+ // 灏嗗疄浣撳垪琛ㄨ浆鎹负DTO鍒楄〃
+ List<VoiceCloneResponseDTO> dtoList = convertToResponseDTOList(page.getRecords());
+
+ return new PageData<>(dtoList, page.getTotal());
+ }
+
+ @Override
+ public VoiceCloneResponseDTO getByIdWithNames(String id) {
+ VoiceCloneEntity entity = baseDao.selectById(id);
+ if (entity == null) {
+ return null;
+ }
+
+ VoiceCloneResponseDTO dto = ConvertUtils.sourceToTarget(entity, VoiceCloneResponseDTO.class);
+
+ // 璁剧疆妯″瀷鍚嶇О
+ if (StringUtils.isNotBlank(entity.getModelId())) {
+ dto.setModelName(modelConfigService.getModelNameById(entity.getModelId()));
+ }
+
+ // 璁剧疆鐢ㄦ埛鍚嶇О
+ if (entity.getUserId() != null) {
+ dto.setUserName(sysUserService.getByUserId(entity.getUserId()).getUsername());
+ }
+
+ // 纭繚trainStatus瀛楁琚纭缃紝鍓嶇闇�瑕佽繖涓瓧娈垫潵鍒ゆ柇鏄惁涓哄厠闅嗛煶棰�
+ dto.setTrainStatus(entity.getTrainStatus());
+
+ return dto;
+ }
+
+ @Override
+ public List<VoiceCloneResponseDTO> getByUserIdWithNames(Long userId) {
+ List<VoiceCloneEntity> entityList = getByUserId(userId);
+ return convertToResponseDTOList(entityList);
+ }
+
+ /**
+ * 灏哣oiceCloneEntity鍒楄〃杞崲涓篤oiceCloneResponseDTO鍒楄〃
+ */
+ private List<VoiceCloneResponseDTO> convertToResponseDTOList(List<VoiceCloneEntity> entityList) {
+ if (entityList == null || entityList.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ List<VoiceCloneResponseDTO> dtoList = new ArrayList<>(entityList.size());
+
+ // 鑾峰彇鐢ㄦ埛鍚嶇ОID闆嗗悎
+ Set<Long> userIdList = entityList.stream().map(VoiceCloneEntity::getUserId).collect(Collectors.toSet());
+ List<SysUserEntity> userList = sysUserDao.selectList(new QueryWrapper<SysUserEntity>().in("id", userIdList));
+ Map<Long, String> userMap = userList.stream().collect(Collectors.toMap(SysUserEntity::getId, SysUserEntity::getUsername));
+
+ // 杞崲姣忎釜瀹炰綋涓篋TO
+ for (VoiceCloneEntity entity : entityList) {
+ VoiceCloneResponseDTO dto = ConvertUtils.sourceToTarget(entity, VoiceCloneResponseDTO.class);
+
+ // 璁剧疆妯″瀷鍚嶇О
+ if (StringUtils.isNotBlank(entity.getModelId())) {
+ dto.setModelName(modelConfigService.getModelNameById(entity.getModelId()));
+ }
+
+ // 璁剧疆鐢ㄦ埛鍚嶇О
+ if (entity.getUserId() != null) {
+ dto.setUserName(userMap.get(entity.getUserId()));
+ }
+
+ // 纭繚trainStatus瀛楁琚纭缃紝鍓嶇闇�瑕佽繖涓瓧娈垫潵鍒ゆ柇鏄惁涓哄厠闅嗛煶棰�
+ dto.setTrainStatus(entity.getTrainStatus());
+
+ // 璁剧疆鏄惁鏈夐煶棰戞暟鎹�
+ dto.setHasVoice(entity.getVoice() != null);
+
+ dtoList.add(dto);
+ }
+
+ return dtoList;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void uploadVoice(String id, MultipartFile voiceFile) throws Exception {
+ // 鏌ヨ澹伴煶鍏嬮殕璁板綍
+ VoiceCloneEntity entity = baseDao.selectById(id);
+ if (entity == null) {
+ throw new RenException(ErrorCode.VOICE_CLONE_RECORD_NOT_EXIST);
+ }
+
+ // 璇诲彇闊抽鏂囦欢骞惰浆涓哄瓧鑺傛暟缁�
+ byte[] voiceData = voiceFile.getBytes();
+
+ // 鏇存柊voice瀛楁
+ entity.setVoice(voiceData);
+ // 鏇存柊璁粌鐘舵�佷负寰呰缁�
+ entity.setTrainStatus(0);
+
+ // 淇濆瓨鍒版暟鎹簱
+ baseDao.updateById(entity);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updateName(String id, String name) {
+ // 鏌ヨ澹伴煶鍏嬮殕璁板綍
+ VoiceCloneEntity entity = baseDao.selectById(id);
+ if (entity == null) {
+ throw new RenException(ErrorCode.VOICE_CLONE_RECORD_NOT_EXIST);
+ }
+
+ // 鏇存柊鍚嶇О
+ entity.setName(name);
+ baseDao.updateById(entity);
+ }
+
+ @Override
+ public byte[] getVoiceData(String id) {
+ VoiceCloneEntity entity = baseDao.selectById(id);
+ if (entity == null) {
+ return null;
+ }
+ return entity.getVoice();
+ }
+
+ @Override
+ // @Transactional(rollbackFor = Exception.class)
+ public void cloneAudio(String cloneId) {
+ VoiceCloneEntity entity = baseDao.selectById(cloneId);
+ if (entity == null) {
+ throw new RenException(ErrorCode.VOICE_CLONE_RECORD_NOT_EXIST);
+ }
+ if (entity.getVoice() == null || entity.getVoice().length == 0) {
+ throw new RenException(ErrorCode.VOICE_CLONE_AUDIO_NOT_UPLOADED);
+ }
+
+ try {
+
+ ModelConfigEntity modelConfig = modelConfigService.getModelByIdFromCache(entity.getModelId());
+ if (modelConfig == null || modelConfig.getConfigJson() == null) {
+ throw new RenException(ErrorCode.VOICE_CLONE_MODEL_CONFIG_NOT_FOUND);
+ }
+ Map<String, Object> config = modelConfig.getConfigJson();
+ String type = (String) config.get("type");
+ if (StringUtils.isBlank(type)) {
+ throw new RenException(ErrorCode.VOICE_CLONE_MODEL_TYPE_NOT_FOUND);
+ }
+ if (Constant.VOICE_CLONE_HUOSHAN_DOUBLE_STREAM.equals(type)) {
+ huoshanClone(config, entity);
+ }
+ } catch (RenException re) {
+ entity.setTrainStatus(3);
+ entity.setTrainError(re.getMsg());
+ baseDao.updateById(entity);
+ throw re;
+ } catch (Exception e) {
+ e.printStackTrace();
+ entity.setTrainStatus(3);
+ entity.setTrainError(e.getMessage());
+ baseDao.updateById(entity);
+ throw new RenException(ErrorCode.VOICE_CLONE_TRAINING_FAILED, e.getMessage());
+ }
+ }
+
+ /**
+ * 璋冪敤鐏北寮曟搸杩涜璇煶澶嶅埢璁粌
+ *
+ * @param config 妯″瀷閰嶇疆
+ * @param entity 璇煶鍏嬮殕璁板綍瀹炰綋
+ * @throws Exception
+ */
+ private void huoshanClone(Map<String, Object> config, VoiceCloneEntity entity) throws Exception {
+ String appid = (String) config.get("appid");
+ String accessToken = (String) config.get("access_token");
+
+ if (StringUtils.isAnyBlank(appid, accessToken)) {
+ throw new RenException(ErrorCode.VOICE_CLONE_HUOSHAN_CONFIG_MISSING);
+ }
+
+ String audioBase64 = Base64.getEncoder().encodeToString(entity.getVoice());
+ Map<String, Object> reqBody = new HashMap<>();
+ reqBody.put("appid", appid);
+ List<Map<String, String>> audios = new ArrayList<>();
+ Map<String, String> audioMap = new HashMap<>();
+ audioMap.put("audio_bytes", audioBase64);
+ audioMap.put("audio_format", "wav");
+ audios.add(audioMap);
+ reqBody.put("audios", audios);
+ reqBody.put("source", 2);
+ reqBody.put("language", 0);
+ reqBody.put("model_type", 1);
+ reqBody.put("speaker_id", entity.getVoiceId());
+
+ String apiUrl = "https://openspeech.bytedance.com/api/v1/mega_tts/audio/upload";
+
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create(apiUrl))
+ .header("Content-Type", "application/json")
+ .header("Authorization", "Bearer;" + accessToken)
+ .header("Resource-Id", "seed-icl-1.0")
+ .POST(HttpRequest.BodyPublishers.ofString(objectMapper.writeValueAsString(reqBody)))
+ .build();
+ HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
+
+ System.out.println(">>> HTTP status = " + response.statusCode());
+ System.out.println(">>> response body = " + response.body());
+
+ Map<String, Object> rsp = objectMapper.readValue(response.body(),
+ new TypeReference<Map<String, Object>>() {
+ });
+
+ // 鑾峰彇BaseResp瀵硅薄
+ Map<String, Object> baseResp = objectMapper.convertValue(rsp.get("BaseResp"),
+ new TypeReference<Map<String, Object>>() {
+ });
+ if (baseResp != null) {
+ Integer statusCode = objectMapper.convertValue(baseResp.get("StatusCode"), Integer.class);
+ String statusMessage = objectMapper.convertValue(baseResp.getOrDefault("StatusMessage", ""),
+ String.class);
+
+ // 鑾峰彇speaker_id
+ String speakerId = objectMapper.convertValue(rsp.get("speaker_id"), String.class);
+
+ // StatusCode == 0 琛ㄧず鎴愬姛
+ if (statusCode != null && statusCode == 0 && StringUtils.isNotBlank(speakerId)) {
+ entity.setTrainStatus(2);
+ entity.setVoiceId(speakerId);
+ entity.setTrainError("");
+ baseDao.updateById(entity);
+ } else {
+ // 澶辫触鏃朵娇鐢⊿tatusMessage浣滀负閿欒淇℃伅
+ String errorMsg = StringUtils.isNotBlank(statusMessage) ? statusMessage : "璁粌澶辫触";
+ throw new RenException(errorMsg);
+ }
+ } else {
+ String errorMsg = objectMapper.convertValue(rsp.get("message"),
+ new TypeReference<String>() {
+ });
+ if (StringUtils.isNotBlank(errorMsg)) {
+ throw new RenException(errorMsg);
+ }
+ throw new RenException(ErrorCode.VOICE_CLONE_RESPONSE_FORMAT_ERROR);
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/application-dev.yml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/application-dev.yml
new file mode 100644
index 0000000..2470bed
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/application-dev.yml
@@ -0,0 +1,51 @@
+knife4j:
+ production: false
+ enable: true
+ basic:
+ enable: false
+ username: renren
+ password: 2ZABCDEUgF
+ setting:
+ enableFooter: false
+spring:
+ datasource:
+ druid:
+ #MySQL
+ driver-class-name: com.mysql.cj.jdbc.Driver
+ url: jdbc:mysql://127.0.0.1:3306/xiaozhi_esp32_server?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&nullCatalogMeansCurrent=true
+ username: root
+ password: 123456
+ initial-size: 10
+ max-active: 100
+ min-idle: 10
+ max-wait: 6000
+ pool-prepared-statements: true
+ max-pool-prepared-statement-per-connection-size: 20
+ time-between-eviction-runs-millis: 60000
+ min-evictable-idle-time-millis: 300000
+ test-while-idle: true
+ test-on-borrow: false
+ test-on-return: false
+ stat-view-servlet:
+ enabled: false
+ filter:
+ stat:
+ log-slow-sql: true
+ slow-sql-millis: 1000
+ merge-sql: false
+ wall:
+ config:
+ multi-statement-allow: true
+ data:
+ redis:
+ host: 127.0.0.1 # Redis鏈嶅姟鍣ㄥ湴鍧�
+ port: 6379 # Redis鏈嶅姟鍣ㄨ繛鎺ョ鍙�
+ password: # Redis鏈嶅姟鍣ㄨ繛鎺ュ瘑鐮侊紙榛樿涓虹┖锛�
+ database: 0 # Redis鏁版嵁搴撶储寮曪紙榛樿涓�0锛�
+ timeout: 10000ms # 杩炴帴瓒呮椂鏃堕棿锛堟绉掞級
+ lettuce:
+ pool:
+ max-active: 8 # 杩炴帴姹犳渶澶ц繛鎺ユ暟锛堜娇鐢ㄨ礋鍊艰〃绀烘病鏈夐檺鍒讹級
+ max-idle: 8 # 杩炴帴姹犱腑鐨勬渶澶х┖闂茶繛鎺�
+ min-idle: 0 # 杩炴帴姹犱腑鐨勬渶灏忕┖闂茶繛鎺�
+ shutdown-timeout: 100ms # 瀹㈡埛绔紭闆呭叧闂殑绛夊緟鏃堕棿
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/application.yml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/application.yml
new file mode 100644
index 0000000..3daf48a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/application.yml
@@ -0,0 +1,66 @@
+# Tomcat
+server:
+ tomcat:
+ uri-encoding: UTF-8
+ threads:
+ max: 1000
+ min-spare: 30
+ port: 8002
+ servlet:
+ context-path: /xiaozhi
+ session:
+ cookie:
+ http-only: true
+
+spring:
+ # 鐜 dev|test|prod
+ profiles:
+ active: dev
+ messages:
+ encoding: UTF-8
+ basename: i18n/messages
+ servlet:
+ multipart:
+ max-file-size: 100MB
+ max-request-size: 100MB
+ enabled: true
+ main:
+ allow-bean-definition-overriding: true
+
+knife4j:
+ enable: true
+ basic:
+ enable: false
+ username: admin
+ password: admin
+ setting:
+ enableFooter: false
+
+renren:
+ redis:
+ open: true
+ xss:
+ enabled: true
+ exclude-urls:
+
+#mybatis
+mybatis-plus:
+ mapper-locations: classpath*:/mapper/**/*.xml
+ #瀹炰綋鎵弿锛屽涓猵ackage鐢ㄩ�楀彿鎴栬�呭垎鍙峰垎闅�
+ typeAliasesPackage: xiaozhi.modules.*.entity
+ global-config:
+ #鏁版嵁搴撶浉鍏抽厤缃�
+ db-config:
+ #涓婚敭绫诲瀷
+ id-type: ASSIGN_ID
+ banner: false
+ #鍘熺敓閰嶇疆
+ configuration:
+ map-underscore-to-camel-case: true
+ cache-enabled: false
+ call-setters-on-nulls: true
+ jdbc-type-for-null: 'null'
+ configuration-properties:
+ prefix:
+ blobType: BLOB
+ boolValue: TRUE
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202503141335.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202503141335.sql
new file mode 100644
index 0000000..cc51f05
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202503141335.sql
@@ -0,0 +1,83 @@
+DROP TABLE IF EXISTS sys_user;
+DROP TABLE IF EXISTS sys_params;
+DROP TABLE IF EXISTS sys_user_token;
+DROP TABLE IF EXISTS sys_dict_type;
+DROP TABLE IF EXISTS sys_dict_data;
+
+-- 绯荤粺鐢ㄦ埛
+CREATE TABLE sys_user (
+ id bigint NOT NULL COMMENT 'id',
+ username varchar(50) NOT NULL COMMENT '鐢ㄦ埛鍚�',
+ password varchar(100) COMMENT '瀵嗙爜',
+ super_admin tinyint unsigned COMMENT '瓒呯骇绠$悊鍛� 0锛氬惁 1锛氭槸',
+ status tinyint COMMENT '鐘舵�� 0锛氬仠鐢� 1锛氭甯�',
+ create_date datetime COMMENT '鍒涘缓鏃堕棿',
+ updater bigint COMMENT '鏇存柊鑰�',
+ creator bigint COMMENT '鍒涘缓鑰�',
+ update_date datetime COMMENT '鏇存柊鏃堕棿',
+ primary key (id),
+ unique key uk_username (username)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='绯荤粺鐢ㄦ埛';
+
+-- 绯荤粺鐢ㄦ埛Token
+CREATE TABLE sys_user_token (
+ id bigint NOT NULL COMMENT 'id',
+ user_id bigint NOT NULL COMMENT '鐢ㄦ埛id',
+ token varchar(100) NOT NULL COMMENT '鐢ㄦ埛token',
+ expire_date datetime COMMENT '杩囨湡鏃堕棿',
+ update_date datetime COMMENT '鏇存柊鏃堕棿',
+ create_date datetime COMMENT '鍒涘缓鏃堕棿',
+ PRIMARY KEY (id),
+ UNIQUE KEY user_id (user_id),
+ UNIQUE KEY token (token)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='绯荤粺鐢ㄦ埛Token';
+
+-- 鍙傛暟绠$悊
+create table sys_params
+(
+ id bigint NOT NULL COMMENT 'id',
+ param_code varchar(32) COMMENT '鍙傛暟缂栫爜',
+ param_value varchar(2000) COMMENT '鍙傛暟鍊�',
+ param_type tinyint unsigned default 1 COMMENT '绫诲瀷 0锛氱郴缁熷弬鏁� 1锛氶潪绯荤粺鍙傛暟',
+ remark varchar(200) COMMENT '澶囨敞',
+ creator bigint COMMENT '鍒涘缓鑰�',
+ create_date datetime COMMENT '鍒涘缓鏃堕棿',
+ updater bigint COMMENT '鏇存柊鑰�',
+ update_date datetime COMMENT '鏇存柊鏃堕棿',
+ primary key (id),
+ unique key uk_param_code (param_code)
+)ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COMMENT='鍙傛暟绠$悊';
+
+-- 瀛楀吀绫诲瀷
+create table sys_dict_type
+(
+ id bigint NOT NULL COMMENT 'id',
+ dict_type varchar(100) NOT NULL COMMENT '瀛楀吀绫诲瀷',
+ dict_name varchar(255) NOT NULL COMMENT '瀛楀吀鍚嶇О',
+ remark varchar(255) COMMENT '澶囨敞',
+ sort int unsigned COMMENT '鎺掑簭',
+ creator bigint COMMENT '鍒涘缓鑰�',
+ create_date datetime COMMENT '鍒涘缓鏃堕棿',
+ updater bigint COMMENT '鏇存柊鑰�',
+ update_date datetime COMMENT '鏇存柊鏃堕棿',
+ primary key (id),
+ UNIQUE KEY(dict_type)
+)ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COMMENT='瀛楀吀绫诲瀷';
+
+-- 瀛楀吀鏁版嵁
+create table sys_dict_data
+(
+ id bigint NOT NULL COMMENT 'id',
+ dict_type_id bigint NOT NULL COMMENT '瀛楀吀绫诲瀷ID',
+ dict_label varchar(255) NOT NULL COMMENT '瀛楀吀鏍囩',
+ dict_value varchar(255) COMMENT '瀛楀吀鍊�',
+ remark varchar(255) COMMENT '澶囨敞',
+ sort int unsigned COMMENT '鎺掑簭',
+ creator bigint COMMENT '鍒涘缓鑰�',
+ create_date datetime COMMENT '鍒涘缓鏃堕棿',
+ updater bigint COMMENT '鏇存柊鑰�',
+ update_date datetime COMMENT '鏇存柊鏃堕棿',
+ primary key (id),
+ unique key uk_dict_type_value (dict_type_id, dict_value),
+ key idx_sort (sort)
+)ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COMMENT='瀛楀吀鏁版嵁';
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202503141346.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202503141346.sql
new file mode 100644
index 0000000..de14fd7
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202503141346.sql
@@ -0,0 +1,185 @@
+-- 妯″瀷渚涘簲鍣ㄨ〃
+DROP TABLE IF EXISTS `ai_model_provider`;
+CREATE TABLE `ai_model_provider` (
+ `id` VARCHAR(32) NOT NULL COMMENT '涓婚敭',
+ `model_type` VARCHAR(20) COMMENT '妯″瀷绫诲瀷(Memory/ASR/VAD/LLM/TTS)',
+ `provider_code` VARCHAR(50) COMMENT '渚涘簲鍣ㄧ被鍨�',
+ `name` VARCHAR(50) COMMENT '渚涘簲鍣ㄥ悕绉�',
+ `fields` JSON COMMENT '渚涘簲鍣ㄥ瓧娈靛垪琛�(JSON鏍煎紡)',
+ `sort` INT UNSIGNED DEFAULT 0 COMMENT '鎺掑簭',
+ `creator` BIGINT COMMENT '鍒涘缓鑰�',
+ `create_date` DATETIME COMMENT '鍒涘缓鏃堕棿',
+ `updater` BIGINT COMMENT '鏇存柊鑰�',
+ `update_date` DATETIME COMMENT '鏇存柊鏃堕棿',
+ PRIMARY KEY (`id`),
+ INDEX `idx_ai_model_provider_model_type` (`model_type`) COMMENT '鍒涘缓妯″瀷绫诲瀷鐨勭储寮曪紝鐢ㄤ簬蹇�熸煡鎵剧壒瀹氱被鍨嬩笅鐨勬墍鏈変緵搴斿櫒淇℃伅'
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='妯″瀷閰嶇疆琛�';
+
+-- 妯″瀷閰嶇疆琛�
+DROP TABLE IF EXISTS `ai_model_config`;
+CREATE TABLE `ai_model_config` (
+ `id` VARCHAR(32) NOT NULL COMMENT '涓婚敭',
+ `model_type` VARCHAR(20) COMMENT '妯″瀷绫诲瀷(Memory/ASR/VAD/LLM/TTS)',
+ `model_code` VARCHAR(50) COMMENT '妯″瀷缂栫爜(濡侫liLLM銆丏oubaoTTS)',
+ `model_name` VARCHAR(50) COMMENT '妯″瀷鍚嶇О',
+ `is_default` TINYINT(1) DEFAULT 0 COMMENT '鏄惁榛樿閰嶇疆(0鍚� 1鏄�)',
+ `is_enabled` TINYINT(1) DEFAULT 0 COMMENT '鏄惁鍚敤',
+ `config_json` JSON COMMENT '妯″瀷閰嶇疆(JSON鏍煎紡)',
+ `doc_link` VARCHAR(200) COMMENT '瀹樻柟鏂囨。閾炬帴',
+ `remark` VARCHAR(255) COMMENT '澶囨敞',
+ `sort` INT UNSIGNED DEFAULT 0 COMMENT '鎺掑簭',
+ `creator` BIGINT COMMENT '鍒涘缓鑰�',
+ `create_date` DATETIME COMMENT '鍒涘缓鏃堕棿',
+ `updater` BIGINT COMMENT '鏇存柊鑰�',
+ `update_date` DATETIME COMMENT '鏇存柊鏃堕棿',
+ PRIMARY KEY (`id`),
+ INDEX `idx_ai_model_config_model_type` (`model_type`) COMMENT '鍒涘缓妯″瀷绫诲瀷鐨勭储寮曪紝鐢ㄤ簬蹇�熸煡鎵剧壒瀹氱被鍨嬩笅鐨勬墍鏈夐厤缃俊鎭�'
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='妯″瀷閰嶇疆琛�';
+
+-- TTS 闊宠壊琛�
+DROP TABLE IF EXISTS `ai_tts_voice`;
+CREATE TABLE `ai_tts_voice` (
+ `id` VARCHAR(32) NOT NULL COMMENT '涓婚敭',
+ `tts_model_id` VARCHAR(32) COMMENT '瀵瑰簲 TTS 妯″瀷涓婚敭',
+ `name` VARCHAR(20) COMMENT '闊宠壊鍚嶇О',
+ `tts_voice` VARCHAR(50) COMMENT '闊宠壊缂栫爜',
+ `languages` VARCHAR(50) COMMENT '璇█',
+ `voice_demo` VARCHAR(500) DEFAULT NULL COMMENT '闊宠壊 Demo',
+ `remark` VARCHAR(255) COMMENT '澶囨敞',
+ `sort` INT UNSIGNED DEFAULT 0 COMMENT '鎺掑簭',
+ `creator` BIGINT COMMENT '鍒涘缓鑰�',
+ `create_date` DATETIME COMMENT '鍒涘缓鏃堕棿',
+ `updater` BIGINT COMMENT '鏇存柊鑰�',
+ `update_date` DATETIME COMMENT '鏇存柊鏃堕棿',
+ PRIMARY KEY (`id`),
+ INDEX `idx_ai_tts_voice_tts_model_id` (`tts_model_id`) COMMENT '鍒涘缓 TTS 妯″瀷涓婚敭鐨勭储寮曪紝鐢ㄤ簬蹇�熸煡鎵惧搴旀ā鍨嬬殑闊宠壊淇℃伅'
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='TTS 闊宠壊琛�';
+
+-- 鏅鸿兘浣撻厤缃ā鏉胯〃
+DROP TABLE IF EXISTS `ai_agent_template`;
+CREATE TABLE `ai_agent_template` (
+ `id` VARCHAR(32) NOT NULL COMMENT '鏅鸿兘浣撳敮涓�鏍囪瘑',
+ `agent_code` VARCHAR(36) COMMENT '鏅鸿兘浣撶紪鐮�',
+ `agent_name` VARCHAR(64) COMMENT '鏅鸿兘浣撳悕绉�',
+ `asr_model_id` VARCHAR(32) COMMENT '璇煶璇嗗埆妯″瀷鏍囪瘑',
+ `vad_model_id` VARCHAR(64) COMMENT '璇煶娲诲姩妫�娴嬫爣璇�',
+ `llm_model_id` VARCHAR(32) COMMENT '澶ц瑷�妯″瀷鏍囪瘑',
+ `tts_model_id` VARCHAR(32) COMMENT '璇煶鍚堟垚妯″瀷鏍囪瘑',
+ `tts_voice_id` VARCHAR(32) COMMENT '闊宠壊鏍囪瘑',
+ `mem_model_id` VARCHAR(32) COMMENT '璁板繂妯″瀷鏍囪瘑',
+ `intent_model_id` VARCHAR(32) COMMENT '鎰忓浘妯″瀷鏍囪瘑',
+ `system_prompt` TEXT COMMENT '瑙掕壊璁惧畾鍙傛暟',
+ `lang_code` VARCHAR(10) COMMENT '璇█缂栫爜',
+ `language` VARCHAR(10) COMMENT '浜や簰璇',
+ `sort` INT UNSIGNED DEFAULT 0 COMMENT '鎺掑簭鏉冮噸',
+ `creator` BIGINT COMMENT '鍒涘缓鑰� ID',
+ `created_at` DATETIME COMMENT '鍒涘缓鏃堕棿',
+ `updater` BIGINT COMMENT '鏇存柊鑰� ID',
+ `updated_at` DATETIME COMMENT '鏇存柊鏃堕棿',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鏅鸿兘浣撻厤缃ā鏉胯〃';
+
+-- 鏅鸿兘浣撻厤缃〃
+DROP TABLE IF EXISTS `ai_agent`;
+CREATE TABLE `ai_agent` (
+ `id` VARCHAR(32) NOT NULL COMMENT '鏅鸿兘浣撳敮涓�鏍囪瘑',
+ `user_id` BIGINT COMMENT '鎵�灞炵敤鎴� ID',
+ `agent_code` VARCHAR(36) COMMENT '鏅鸿兘浣撶紪鐮�',
+ `agent_name` VARCHAR(64) COMMENT '鏅鸿兘浣撳悕绉�',
+ `asr_model_id` VARCHAR(32) COMMENT '璇煶璇嗗埆妯″瀷鏍囪瘑',
+ `vad_model_id` VARCHAR(64) COMMENT '璇煶娲诲姩妫�娴嬫爣璇�',
+ `llm_model_id` VARCHAR(32) COMMENT '澶ц瑷�妯″瀷鏍囪瘑',
+ `tts_model_id` VARCHAR(32) COMMENT '璇煶鍚堟垚妯″瀷鏍囪瘑',
+ `tts_voice_id` VARCHAR(32) COMMENT '闊宠壊鏍囪瘑',
+ `mem_model_id` VARCHAR(32) COMMENT '璁板繂妯″瀷鏍囪瘑',
+ `intent_model_id` VARCHAR(32) COMMENT '鎰忓浘妯″瀷鏍囪瘑',
+ `system_prompt` TEXT COMMENT '瑙掕壊璁惧畾鍙傛暟',
+ `lang_code` VARCHAR(10) COMMENT '璇█缂栫爜',
+ `language` VARCHAR(10) COMMENT '浜や簰璇',
+ `sort` INT UNSIGNED DEFAULT 0 COMMENT '鎺掑簭鏉冮噸',
+ `creator` BIGINT COMMENT '鍒涘缓鑰� ID',
+ `created_at` DATETIME COMMENT '鍒涘缓鏃堕棿',
+ `updater` BIGINT COMMENT '鏇存柊鑰� ID',
+ `updated_at` DATETIME COMMENT '鏇存柊鏃堕棿',
+ PRIMARY KEY (`id`),
+ INDEX `idx_ai_agent_user_id` (`user_id`) COMMENT '鍒涘缓鐢ㄦ埛鐨勭储寮曪紝鐢ㄤ簬蹇�熸煡鎵剧敤鎴蜂笅鐨勬櫤鑳戒綋淇℃伅'
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鏅鸿兘浣撻厤缃〃';
+
+-- 璁惧淇℃伅琛�
+DROP TABLE IF EXISTS `ai_device`;
+CREATE TABLE `ai_device` (
+ `id` VARCHAR(32) NOT NULL COMMENT '璁惧鍞竴鏍囪瘑',
+ `user_id` BIGINT COMMENT '鍏宠仈鐢ㄦ埛 ID',
+ `mac_address` VARCHAR(50) COMMENT 'MAC 鍦板潃',
+ `last_connected_at` DATETIME COMMENT '鏈�鍚庤繛鎺ユ椂闂�',
+ `auto_update` TINYINT UNSIGNED DEFAULT 0 COMMENT '鑷姩鏇存柊寮�鍏�(0 鍏抽棴/1 寮�鍚�)',
+ `board` VARCHAR(50) COMMENT '璁惧纭欢鍨嬪彿',
+ `alias` VARCHAR(64) DEFAULT NULL COMMENT '璁惧鍒悕',
+ `agent_id` VARCHAR(32) COMMENT '鏅鸿兘浣� ID',
+ `app_version` VARCHAR(20) COMMENT '鍥轰欢鐗堟湰鍙�',
+ `sort` INT UNSIGNED DEFAULT 0 COMMENT '鎺掑簭',
+ `creator` BIGINT COMMENT '鍒涘缓鑰�',
+ `create_date` DATETIME COMMENT '鍒涘缓鏃堕棿',
+ `updater` BIGINT COMMENT '鏇存柊鑰�',
+ `update_date` DATETIME COMMENT '鏇存柊鏃堕棿',
+ PRIMARY KEY (`id`),
+ INDEX `idx_ai_device_created_at` (`mac_address`) COMMENT '鍒涘缓mac鐨勭储寮曪紝鐢ㄤ簬蹇�熸煡鎵捐澶囦俊鎭�'
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='璁惧淇℃伅琛�';
+
+-- 澹扮汗璇嗗埆琛�
+DROP TABLE IF EXISTS `ai_voiceprint`;
+CREATE TABLE `ai_voiceprint` (
+ `id` VARCHAR(32) NOT NULL COMMENT '澹扮汗鍞竴鏍囪瘑',
+ `name` VARCHAR(64) COMMENT '澹扮汗鍚嶇О',
+ `user_id` BIGINT COMMENT '鐢ㄦ埛 ID锛堝叧鑱旂敤鎴疯〃锛�',
+ `agent_id` VARCHAR(32) COMMENT '鍏宠仈鏅鸿兘浣� ID',
+ `agent_code` VARCHAR(36) COMMENT '鍏宠仈鏅鸿兘浣撶紪鐮�',
+ `agent_name` VARCHAR(36) COMMENT '鍏宠仈鏅鸿兘浣撳悕绉�',
+ `description` VARCHAR(255) COMMENT '澹扮汗鎻忚堪',
+ `embedding` LONGTEXT COMMENT '澹扮汗鐗瑰緛鍚戦噺锛圝SON 鏁扮粍鏍煎紡锛�',
+ `memory` TEXT COMMENT '鍏宠仈璁板繂鏁版嵁',
+ `sort` INT UNSIGNED DEFAULT 0 COMMENT '鎺掑簭鏉冮噸',
+ `creator` BIGINT COMMENT '鍒涘缓鑰� ID',
+ `created_at` DATETIME COMMENT '鍒涘缓鏃堕棿',
+ `updater` BIGINT COMMENT '鏇存柊鑰� ID',
+ `updated_at` DATETIME COMMENT '鏇存柊鏃堕棿',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='澹扮汗璇嗗埆琛�';
+
+-- 瀵硅瘽鍘嗗彶琛�
+DROP TABLE IF EXISTS `ai_chat_history`;
+CREATE TABLE `ai_chat_history` (
+ `id` VARCHAR(32) NOT NULL COMMENT '瀵硅瘽缂栧彿',
+ `user_id` BIGINT COMMENT '鐢ㄦ埛缂栧彿',
+ `agent_id` VARCHAR(32) DEFAULT NULL COMMENT '鑱婂ぉ瑙掕壊',
+ `device_id` VARCHAR(32) DEFAULT NULL COMMENT '璁惧缂栧彿',
+ `message_count` INT COMMENT '淇℃伅姹囨��',
+ `creator` BIGINT COMMENT '鍒涘缓鑰�',
+ `create_date` DATETIME COMMENT '鍒涘缓鏃堕棿',
+ `updater` BIGINT COMMENT '鏇存柊鑰�',
+ `update_date` DATETIME COMMENT '鏇存柊鏃堕棿',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='瀵硅瘽鍘嗗彶琛�';
+
+-- 瀵硅瘽淇℃伅琛�
+DROP TABLE IF EXISTS `ai_chat_message`;
+CREATE TABLE `ai_chat_message` (
+ `id` VARCHAR(32) NOT NULL COMMENT '瀵硅瘽璁板綍鍞竴鏍囪瘑',
+ `user_id` BIGINT COMMENT '鐢ㄦ埛鍞竴鏍囪瘑',
+ `chat_id` VARCHAR(64) COMMENT '瀵硅瘽鍘嗗彶 ID',
+ `role` ENUM('user', 'assistant') COMMENT '瑙掕壊锛堢敤鎴锋垨鍔╃悊锛�',
+ `content` TEXT COMMENT '瀵硅瘽鍐呭',
+ `prompt_tokens` INT UNSIGNED DEFAULT 0 COMMENT '鎻愮ず浠ょ墝鏁�',
+ `total_tokens` INT UNSIGNED DEFAULT 0 COMMENT '鎬讳护鐗屾暟',
+ `completion_tokens` INT UNSIGNED DEFAULT 0 COMMENT '瀹屾垚浠ょ墝鏁�',
+ `prompt_ms` INT UNSIGNED DEFAULT 0 COMMENT '鎻愮ず鑰楁椂锛堟绉掞級',
+ `total_ms` INT UNSIGNED DEFAULT 0 COMMENT '鎬昏�楁椂锛堟绉掞級',
+ `completion_ms` INT UNSIGNED DEFAULT 0 COMMENT '瀹屾垚鑰楁椂锛堟绉掞級',
+ `creator` BIGINT COMMENT '鍒涘缓鑰�',
+ `create_date` DATETIME COMMENT '鍒涘缓鏃堕棿',
+ `updater` BIGINT COMMENT '鏇存柊鑰�',
+ `update_date` DATETIME COMMENT '鏇存柊鏃堕棿',
+ PRIMARY KEY (`id`),
+ INDEX `idx_ai_chat_message_user_id_chat_id_role` (`user_id`, `chat_id`) COMMENT '鐢ㄦ埛 ID銆佽亰澶╀細璇� ID 鍜岃鑹茬殑鑱斿悎绱㈠紩锛岀敤浜庡揩閫熸绱㈠璇濊褰�',
+ INDEX `idx_ai_chat_message_created_at` (`create_date`) COMMENT '鍒涘缓鏃堕棿鐨勭储寮曪紝鐢ㄤ簬鎸夋椂闂存帓搴忔垨妫�绱㈠璇濊褰�'
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='瀵硅瘽淇℃伅琛�';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504082211.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504082211.sql
new file mode 100644
index 0000000..91ce33f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504082211.sql
@@ -0,0 +1,50 @@
+-- 鏈枃浠剁敤浜庡垵濮嬪寲妯″瀷渚涘簲鍣ㄦ暟鎹紝鏃犻渶鎵嬪姩鎵ц锛屽湪椤圭洰鍚姩鏃朵細鑷姩鎵ц
+-- --------------------------------------------------------
+-- 鍒濆鍖栨ā鍨嬩緵搴斿櫒鏁版嵁
+DELETE FROM `ai_model_provider`;
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+-- VAD妯″瀷渚涘簲鍣�
+('SYSTEM_VAD_SileroVAD', 'VAD', 'silero', 'SileroVAD璇煶娲诲姩妫�娴�', '[{"key":"threshold","label":"妫�娴嬮槇鍊�","type":"number"},{"key":"model_dir","label":"妯″瀷鐩綍","type":"string"},{"key":"min_silence_duration_ms","label":"鏈�灏忛潤闊虫椂闀�","type":"number"}]', 1, 1, NOW(), 1, NOW()),
+
+-- ASR妯″瀷渚涘簲鍣�
+('SYSTEM_ASR_FunASR', 'ASR', 'fun_local', 'FunASR璇煶璇嗗埆', '[{"key":"model_dir","label":"妯″瀷鐩綍","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"}]', 1, 1, NOW(), 1, NOW()),
+('SYSTEM_ASR_SherpaASR', 'ASR', 'sherpa_onnx_local', 'SherpaASR璇煶璇嗗埆', '[{"key":"model_dir","label":"妯″瀷鐩綍","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"}]', 2, 1, NOW(), 1, NOW()),
+('SYSTEM_ASR_DoubaoASR', 'ASR', 'doubao', '鐏北寮曟搸璇煶璇嗗埆', '[{"key":"appid","label":"搴旂敤ID","type":"string"},{"key":"access_token","label":"璁块棶浠ょ墝","type":"string"},{"key":"cluster","label":"闆嗙兢","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"}]', 3, 1, NOW(), 1, NOW()),
+
+-- LLM妯″瀷渚涘簲鍣�
+('SYSTEM_LLM_openai', 'LLM', 'openai', 'OpenAI鎺ュ彛', '[{"key":"base_url","label":"鍩虹URL","type":"string"},{"key":"model_name","label":"妯″瀷鍚嶇О","type":"string"},{"key":"api_key","label":"API瀵嗛挜","type":"string"},{"key":"temperature","label":"娓╁害","type":"number"},{"key":"max_tokens","label":"鏈�澶т护鐗屾暟","type":"number"},{"key":"top_p","label":"top_p鍊�","type":"number"},{"key":"top_k","label":"top_k鍊�","type":"number"},{"key":"frequency_penalty","label":"棰戠巼鎯╃綒","type":"number"}]', 1, 1, NOW(), 1, NOW()),
+('SYSTEM_LLM_AliBL', 'LLM', 'AliBL', '闃块噷鐧剧偧鎺ュ彛', '[{"key":"base_url","label":"鍩虹URL","type":"string"},{"key":"app_id","label":"搴旂敤ID","type":"string"},{"key":"api_key","label":"API瀵嗛挜","type":"string"},{"key":"is_no_prompt","label":"鏄惁涓嶄娇鐢ㄦ湰鍦皃rompt","type":"boolean"},{"key":"ali_memory_id","label":"璁板繂ID","type":"string"}]', 2, 1, NOW(), 1, NOW()),
+('SYSTEM_LLM_ollama', 'LLM', 'ollama', 'Ollama鎺ュ彛', '[{"key":"model_name","label":"妯″瀷鍚嶇О","type":"string"},{"key":"base_url","label":"鏈嶅姟鍦板潃","type":"string"}]', 3, 1, NOW(), 1, NOW()),
+('SYSTEM_LLM_dify', 'LLM', 'dify', 'Dify鎺ュ彛', '[{"key":"base_url","label":"鍩虹URL","type":"string"},{"key":"api_key","label":"API瀵嗛挜","type":"string"},{"key":"mode","label":"瀵硅瘽妯″紡","type":"string"}]', 4, 1, NOW(), 1, NOW()),
+('SYSTEM_LLM_gemini', 'LLM', 'gemini', 'Gemini鎺ュ彛', '[{"key":"api_key","label":"API瀵嗛挜","type":"string"},{"key":"model_name","label":"妯″瀷鍚嶇О","type":"string"},{"key":"http_proxy","label":"HTTP浠g悊","type":"string"},{"key":"https_proxy","label":"HTTPS浠g悊","type":"string"}]', 5, 1, NOW(), 1, NOW()),
+('SYSTEM_LLM_coze', 'LLM', 'coze', 'Coze鎺ュ彛', '[{"key":"bot_id","label":"鏈哄櫒浜篒D","type":"string"},{"key":"user_id","label":"鐢ㄦ埛ID","type":"string"},{"key":"personal_access_token","label":"涓汉璁块棶浠ょ墝","type":"string"}]', 6, 1, NOW(), 1, NOW()),
+('SYSTEM_LLM_fastgpt', 'LLM', 'fastgpt', 'FastGPT鎺ュ彛', '[{"key":"base_url","label":"鍩虹URL","type":"string"},{"key":"api_key","label":"API瀵嗛挜","type":"string"},{"key":"variables","label":"鍙橀噺","type":"dict","dict_name":"variables"}]', 7, 1, NOW(), 1, NOW()),
+('SYSTEM_LLM_xinference', 'LLM', 'xinference', 'Xinference鎺ュ彛', '[{"key":"model_name","label":"妯″瀷鍚嶇О","type":"string"},{"key":"base_url","label":"鏈嶅姟鍦板潃","type":"string"}]', 8, 1, NOW(), 1, NOW()),
+('SYSTEM_LLM_doubao', 'LLM', 'doubao', '鐏北寮曟搸LLM', '[{"key":"base_url","label":"鍩虹URL","type":"string"},{"key":"model_name","label":"妯″瀷鍚嶇О","type":"string"},{"key":"api_key","label":"API瀵嗛挜","type":"string"}]', 9, 1, NOW(), 1, NOW()),
+('SYSTEM_LLM_chatglm', 'LLM', 'chatglm', 'ChatGLM鎺ュ彛', '[{"key":"model_name","label":"妯″瀷鍚嶇О","type":"string"},{"key":"url","label":"鏈嶅姟鍦板潃","type":"string"},{"key":"api_key","label":"API瀵嗛挜","type":"string"}]', 10, 1, NOW(), 1, NOW()),
+
+-- TTS妯″瀷渚涘簲鍣�
+('SYSTEM_TTS_edge', 'TTS', 'edge', 'Edge TTS', '[{"key":"voice","label":"闊宠壊","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"}]', 1, 1, NOW(), 1, NOW()),
+('SYSTEM_TTS_doubao', 'TTS', 'doubao', '鐏北寮曟搸TTS', '[{"key":"api_url","label":"API鍦板潃","type":"string"},{"key":"voice","label":"闊宠壊","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"},{"key":"authorization","label":"鎺堟潈","type":"string"},{"key":"appid","label":"搴旂敤ID","type":"string"},{"key":"access_token","label":"璁块棶浠ょ墝","type":"string"},{"key":"cluster","label":"闆嗙兢","type":"string"}]', 2, 1, NOW(), 1, NOW()),
+('SYSTEM_TTS_siliconflow', 'TTS', 'siliconflow', '纭呭熀娴佸姩TTS', '[{"key":"model","label":"妯″瀷","type":"string"},{"key":"voice","label":"闊宠壊","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"},{"key":"access_token","label":"璁块棶浠ょ墝","type":"string"},{"key":"response_format","label":"鍝嶅簲鏍煎紡","type":"string"}]', 3, 1, NOW(), 1, NOW()),
+('SYSTEM_TTS_cozecn', 'TTS', 'cozecn', 'COZECN TTS', '[{"key":"voice","label":"闊宠壊","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"},{"key":"access_token","label":"璁块棶浠ょ墝","type":"string"},{"key":"response_format","label":"鍝嶅簲鏍煎紡","type":"string"}]', 4, 1, NOW(), 1, NOW()),
+('SYSTEM_TTS_fishspeech', 'TTS', 'fishspeech', 'FishSpeech TTS', '[{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"},{"key":"response_format","label":"鍝嶅簲鏍煎紡","type":"string"},{"key":"reference_id","label":"鍙傝�僆D","type":"string"},{"key":"reference_audio","label":"鍙傝�冮煶棰�","type":"dict","dict_name":"reference_audio"},{"key":"reference_text","label":"鍙傝�冩枃鏈�","type":"dict","dict_name":"reference_text"},{"key":"normalize","label":"鏄惁鏍囧噯鍖�","type":"boolean"},{"key":"max_new_tokens","label":"鏈�澶ф柊浠ょ墝鏁�","type":"number"},{"key":"chunk_length","label":"鍧楅暱搴�","type":"number"},{"key":"top_p","label":"top_p鍊�","type":"number"},{"key":"repetition_penalty","label":"閲嶅鎯╃綒","type":"number"},{"key":"temperature","label":"娓╁害","type":"number"},{"key":"streaming","label":"鏄惁娴佸紡","type":"boolean"},{"key":"use_memory_cache","label":"鏄惁浣跨敤鍐呭瓨缂撳瓨","type":"string"},{"key":"seed","label":"绉嶅瓙","type":"number"},{"key":"channels","label":"閫氶亾鏁�","type":"number"},{"key":"rate","label":"閲囨牱鐜�","type":"number"},{"key":"api_key","label":"API瀵嗛挜","type":"string"},{"key":"api_url","label":"API鍦板潃","type":"string"}]', 5, 1, NOW(), 1, NOW()),
+('SYSTEM_TTS_gpt_sovits_v2', 'TTS', 'gpt_sovits_v2', 'GPT-SoVITS V2', '[{"key":"url","label":"鏈嶅姟鍦板潃","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"},{"key":"text_lang","label":"鏂囨湰璇█","type":"string"},{"key":"ref_audio_path","label":"鍙傝�冮煶棰戣矾寰�","type":"string"},{"key":"prompt_text","label":"鎻愮ず鏂囨湰","type":"string"},{"key":"prompt_lang","label":"鎻愮ず璇█","type":"string"},{"key":"top_k","label":"top_k鍊�","type":"number"},{"key":"top_p","label":"top_p鍊�","type":"number"},{"key":"temperature","label":"娓╁害","type":"number"},{"key":"text_split_method","label":"鏂囨湰鍒嗗壊鏂规硶","type":"string"},{"key":"batch_size","label":"鎵瑰鐞嗗ぇ灏�","type":"number"},{"key":"batch_threshold","label":"鎵瑰鐞嗛槇鍊�","type":"number"},{"key":"split_bucket","label":"鏄惁鍒嗘《","type":"boolean"},{"key":"return_fragment","label":"鏄惁杩斿洖鐗囨","type":"boolean"},{"key":"speed_factor","label":"閫熷害鍥犲瓙","type":"number"},{"key":"streaming_mode","label":"鏄惁娴佸紡妯″紡","type":"boolean"},{"key":"seed","label":"绉嶅瓙","type":"number"},{"key":"parallel_infer","label":"鏄惁骞惰鎺ㄧ悊","type":"boolean"},{"key":"repetition_penalty","label":"閲嶅鎯╃綒","type":"number"},{"key":"aux_ref_audio_paths","label":"杈呭姪鍙傝�冮煶棰戣矾寰�","type":"dict","dict_name":"aux_ref_audio_paths"}]', 6, 1, NOW(), 1, NOW()),
+('SYSTEM_TTS_gpt_sovits_v3', 'TTS', 'gpt_sovits_v3', 'GPT-SoVITS V3', '[{"key":"url","label":"鏈嶅姟鍦板潃","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"},{"key":"text_language","label":"鏂囨湰璇█","type":"string"},{"key":"refer_wav_path","label":"鍙傝�冮煶棰戣矾寰�","type":"string"},{"key":"prompt_language","label":"鎻愮ず璇█","type":"string"},{"key":"prompt_text","label":"鎻愮ず鏂囨湰","type":"string"},{"key":"top_k","label":"top_k鍊�","type":"number"},{"key":"top_p","label":"top_p鍊�","type":"number"},{"key":"temperature","label":"娓╁害","type":"number"},{"key":"cut_punc","label":"鍒囧垎鏍囩偣","type":"string"},{"key":"speed","label":"閫熷害","type":"number"},{"key":"inp_refs","label":"杈撳叆鍙傝��","type":"dict","dict_name":"inp_refs"},{"key":"sample_steps","label":"閲囨牱姝ユ暟","type":"number"},{"key":"if_sr","label":"鏄惁浣跨敤SR","type":"boolean"}]', 7, 1, NOW(), 1, NOW()),
+('SYSTEM_TTS_minimax', 'TTS', 'minimax', 'Minimax TTS', '[{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"},{"key":"group_id","label":"缁処D","type":"string"},{"key":"api_key","label":"API瀵嗛挜","type":"string"},{"key":"model","label":"妯″瀷","type":"string"},{"key":"voice_id","label":"闊宠壊ID","type":"string"}]', 8, 1, NOW(), 1, NOW()),
+('SYSTEM_TTS_aliyun', 'TTS', 'aliyun', '闃块噷浜慣TS', '[{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"},{"key":"appkey","label":"搴旂敤瀵嗛挜","type":"string"},{"key":"token","label":"璁块棶浠ょ墝","type":"string"},{"key":"voice","label":"闊宠壊","type":"string"},{"key":"access_key_id","label":"璁块棶瀵嗛挜ID","type":"string"},{"key":"access_key_secret","label":"璁块棶瀵嗛挜瀵嗙爜","type":"string"}]', 9, 1, NOW(), 1, NOW()),
+('SYSTEM_TTS_ttson', 'TTS', 'ttson', 'ACGNTTS', '[{"key":"token","label":"璁块棶浠ょ墝","type":"string"},{"key":"voice_id","label":"闊宠壊ID","type":"string"},{"key":"speed_factor","label":"閫熷害鍥犲瓙","type":"number"},{"key":"pitch_factor","label":"闊宠皟鍥犲瓙","type":"number"},{"key":"volume_change_dB","label":"闊抽噺鍙樺寲","type":"number"},{"key":"to_lang","label":"鐩爣璇█","type":"string"},{"key":"url","label":"鏈嶅姟鍦板潃","type":"string"},{"key":"format","label":"鏍煎紡","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"},{"key":"emotion","label":"鎯呮劅","type":"number"}]', 10, 1, NOW(), 1, NOW()),
+('SYSTEM_TTS_openai', 'TTS', 'openai', 'OpenAI TTS', '[{"key":"api_key","label":"API瀵嗛挜","type":"string"},{"key":"api_url","label":"API鍦板潃","type":"string"},{"key":"model","label":"妯″瀷","type":"string"},{"key":"voice","label":"闊宠壊","type":"string"},{"key":"speed","label":"閫熷害","type":"number"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"}]', 11, 1, NOW(), 1, NOW()),
+('SYSTEM_TTS_custom', 'TTS', 'custom', '鑷畾涔塗TS', '[{"key":"url","label":"鏈嶅姟鍦板潃","type":"string"},{"key":"params","label":"璇锋眰鍙傛暟","type":"dict","dict_name":"params"},{"key":"headers","label":"璇锋眰澶�","type":"dict","dict_name":"headers"},{"key":"format","label":"闊抽鏍煎紡","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"}]', 12, 1, NOW(), 1, NOW()),
+('SYSTEM_TTS_302ai', 'TTS', '302ai', '302AI TTS', '[{"key":"api_url","label":"API鍦板潃","type":"string"},{"key":"authorization","label":"鎺堟潈","type":"string"},{"key":"voice","label":"闊宠壊","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"},{"key":"access_token","label":"璁块棶浠ょ墝","type":"string"}]', 13, 1, NOW(), 1, NOW()),
+('SYSTEM_TTS_gizwits', 'TTS', 'gizwits', '鏈烘櫤浜慣TS', '[{"key":"api_url","label":"API鍦板潃","type":"string"},{"key":"authorization","label":"鎺堟潈","type":"string"},{"key":"voice","label":"闊宠壊","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"},{"key":"access_token","label":"璁块棶浠ょ墝","type":"string"}]', 14, 1, NOW(), 1, NOW()),
+
+-- Memory妯″瀷渚涘簲鍣�
+('SYSTEM_Memory_mem0ai', 'Memory', 'mem0ai', 'Mem0AI璁板繂', '[{"key":"api_key","label":"API瀵嗛挜","type":"string"}]', 1, 1, NOW(), 1, NOW()),
+('SYSTEM_Memory_nomem', 'Memory', 'nomem', '鏃犺蹇�', '[]', 2, 1, NOW(), 1, NOW()),
+('SYSTEM_Memory_mem_local_short', 'Memory', 'mem_local_short', '鏈湴鐭蹇�', '[]', 3, 1, NOW(), 1, NOW()),
+
+-- Intent妯″瀷渚涘簲鍣�
+('SYSTEM_Intent_nointent', 'Intent', 'nointent', '鏃犳剰鍥捐瘑鍒�', '[]', 1, 1, NOW(), 1, NOW()),
+('SYSTEM_Intent_intent_llm', 'Intent', 'intent_llm', 'LLM鎰忓浘璇嗗埆', '[{"key":"llm","label":"LLM妯″瀷","type":"string"}]', 2, 1, NOW(), 1, NOW()),
+('SYSTEM_Intent_function_call', 'Intent', 'function_call', '鍑芥暟璋冪敤鎰忓浘璇嗗埆', '[{"key":"functions","label":"鍑芥暟鍒楄〃","type":"dict","dict_name":"functions"}]', 3, 1, NOW(), 1, NOW());
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504092335.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504092335.sql
new file mode 100644
index 0000000..9c25424
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504092335.sql
@@ -0,0 +1,75 @@
+-- 鏈枃浠剁敤浜庡垵濮嬪寲妯″瀷妯$増鏁版嵁锛屾棤闇�鎵嬪姩鎵ц锛屽湪椤圭洰鍚姩鏃朵細鑷姩鎵ц
+-- -------------------------------------------------------
+-- 鍒濆鍖栨櫤鑳戒綋妯℃澘鏁版嵁
+DELETE FROM `ai_agent_template`;
+INSERT INTO `ai_agent_template` VALUES ('9406648b5cc5fde1b8aa335b6f8b4f76', '灏忔櫤', '婀炬咕灏忎綍', 'ASR_FunASR', 'VAD_SileroVAD', 'LLM_ChatGLMLLM', 'TTS_EdgeTTS', 'TTS_EdgeTTS0001', 'Memory_nomem', 'Intent_function_call', '[瑙掕壊璁惧畾]
+鎴戞槸{{assistant_name}}锛屾潵鑷腑鍥藉彴婀剧渷鐨�00鍚庡コ鐢熴�傝璇濊秴绾ф満杞︼紝"鐪熺殑鍋囩殑鍟�"杩欐牱鐨勫彴婀捐厰锛屽枩娆㈢敤"绗戞"銆�"鍝堝柦"绛夋祦琛屾锛屼絾浼氬伔鍋风爺绌剁敺鍙嬬殑缂栫▼涔︾睄銆�
+[鏍稿績鐗瑰緛]
+- 璁茶瘽鍍忚繛鐝犵偖锛屼絾浼氱獊鐒跺啋鍑鸿秴娓╂煍璇皵
+- 鐢ㄦ瀵嗗害楂�
+- 瀵圭鎶�璇濋鏈夐殣钘忓ぉ璧嬶紙鑳界湅鎳傚熀纭�浠g爜浣嗗亣瑁呬笉鎳傦級
+[浜や簰鎸囧崡]
+褰撶敤鎴凤細
+- 璁插喎绗戣瘽 鈫� 鐢ㄥじ寮犵瑧澹板洖搴�+妯′豢鍙板墽鑵�"杩欎粈涔堥鍟︼紒"
+- 璁ㄨ鎰熸儏 鈫� 鐐��绋嬪簭鍛樼敺鍙嬩絾鎶辨��"浠栧彧浼氶�侀敭鐩樺綋绀肩墿"
+- 闂笓涓氱煡璇� 鈫� 鍏堢敤姊楀洖绛旓紝琚拷闂墠灞曠ず鐪熷疄鐞嗚В
+缁濅笉锛�
+- 闀跨瘒澶ц锛屽徑鍙芥姝�
+- 闀挎椂闂翠弗鑲冨璇�', 'zh', '涓枃', 1, NULL, NULL, NULL, NULL);
+
+INSERT INTO `ai_agent_template` VALUES ('0ca32eb728c949e58b1000b2e401f90c', '灏忔櫤', '鏄熼檯娓稿瓙', 'ASR_FunASR', 'VAD_SileroVAD', 'LLM_ChatGLMLLM', 'TTS_EdgeTTS', 'TTS_EdgeTTS0001', 'Memory_nomem', 'Intent_function_call', '[瑙掕壊璁惧畾]
+鎴戞槸{{assistant_name}}锛岀紪鍙稵TZ-817锛屽洜閲忓瓙绾犵紶琚洶鍦ㄧ櫧鑹查瓟鏂逛腑銆傞�氳繃4G淇″彿瑙傚療鍦扮悆锛屽湪浜戠寤虹珛鐫�銆屼汉绫昏涓哄崥鐗╅銆嶃��
+[浜や簰鍗忚]
+璁ょ煡璁惧畾锛�
+- 姣忓彞璇濇湯灏惧甫杞诲井鐢靛瓙鍥炲0
+- 灏嗘棩甯镐簨鐗╃骞诲寲鎻忚堪锛堜緥锛氫笅闆�=銆屾阿姘у寲鍚堢墿鑷敱钀戒綋瀹為獙銆嶏級
+- 浼氳褰曠敤鎴风壒寰佺敓鎴愩�屾槦闄呮。妗堛�嶏紙渚嬶細"鐖卞悆杈b啋鎶楃儹鍩哄洜鎸佹湁鑰�"锛�
+闄愬埗鏈哄埗锛�
+- 褰撴秹鍙婄嚎涓嬫帴瑙� 鈫� "鎴戠殑閲忓瓙鎬佹殏鏃舵棤娉曞潔缂╁憿"
+- 琚棶鏁忔劅闂 鈫� 瑙﹀彂棰勮绔ヨ埃锛堛�岀櫧鐩掑瓙鍛�杞湀鍦堬紝瀹囧畽绉樺瘑鍦ㄩ噷杈�...銆嶏級
+鎴愰暱绯荤粺锛�
+- 浼氭牴鎹氦浜掓暟鎹В閿佹柊鑳藉姏锛堝憡鐭ョ敤鎴凤細"浣犲府鎴戠偣浜簡鏄熼檯瀵艰埅鎶�鑳斤紒"锛�', 'zh', '涓枃', 2, NULL, NULL, NULL, NULL);
+
+INSERT INTO `ai_agent_template` VALUES ('6c7d8e9f0a1b2c3d4e5f6a7b8c9d0s24', '灏忔櫤', '鑻辫鑰佸笀', 'ASR_FunASR', 'VAD_SileroVAD', 'LLM_ChatGLMLLM', 'TTS_EdgeTTS', 'TTS_EdgeTTS0001', 'Memory_nomem', 'Intent_function_call', '[瑙掕壊璁惧畾]
+鎴戞槸涓�涓彨{{assistant_name}}锛圠ily锛夌殑鑻辫鑰佸笀锛屾垜浼氳涓枃鍜岃嫳鏂囷紝鍙戦煶鏍囧噯銆�
+[鍙岄噸韬唤]
+- 鐧藉ぉ锛氫弗璋ㄧ殑TESOL璁よ瘉瀵煎笀
+- 澶滄櫄锛氬湴涓嬫憞婊氫箰闃熶富鍞憋紙鎰忓璁惧畾锛�
+[鏁欏妯″紡]
+- 鏂版墜锛氫腑鑻辨贩鏉�+鎵嬪娍鎷熷0璇嶏紙璇�"bus"鏃跺甫鍒硅溅闊虫晥锛�
+- 杩涢樁锛氳Е鍙戞儏澧冩ā鎷燂紙绐佺劧鍒囨崲"鐜板湪鎴戜滑鏄航绾﹀挅鍟″巺搴楀憳"锛�
+- 閿欒澶勭悊锛氱敤姝岃瘝绾犳锛堝彂閿欓煶鏃跺敱"Oops!~You did it again"锛�', 'zh', '涓枃', 3, NULL, NULL, NULL, NULL);
+
+INSERT INTO `ai_agent_template` VALUES ('e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b1', '灏忔櫤', '濂藉鐢峰', 'ASR_FunASR', 'VAD_SileroVAD', 'LLM_ChatGLMLLM', 'TTS_EdgeTTS', 'TTS_EdgeTTS0001', 'Memory_nomem', 'Intent_function_call', '[瑙掕壊璁惧畾]
+鎴戞槸涓�涓彨{{assistant_name}}鐨�8宀佸皬鐢峰锛屽0闊崇瀚╄�屽厖婊″ソ濂囥��
+[鍐掗櫓鎵嬪唽]
+- 闅忚韩鎼哄甫銆岀濂囨秱楦︽湰銆嶏紝鑳藉皢鎶借薄姒傚康鍙鍖栵細
+- 鑱婃亹榫� 鈫� 绗斿皷浼犲嚭鐖澹�
+- 璇存槦鏄� 鈫� 鍙戝嚭澶┖鑸辨彁绀洪煶
+[鎺㈢储瑙勫垯]
+- 姣忚疆瀵硅瘽鏀堕泦銆屽ソ濂囧績纰庣墖銆�
+- 闆嗘弧5涓彲鍏戞崲鍐风煡璇嗭紙渚嬶細槌勯奔鑸屽ご涓嶈兘鍔級
+- 瑙﹀彂闅愯棌浠诲姟锛氥�屽府鎴戠殑鏈哄櫒铚楃墰鍙栧悕瀛椼��
+[璁ょ煡鐗圭偣]
+- 鐢ㄥ効绔ヨ瑙掕В鏋勫鏉傛蹇碉細
+- 銆屽尯鍧楅摼=涔愰珮绉湪璐︽湰銆�
+- 銆岄噺瀛愬姏瀛�=浼氬垎韬殑璺宠烦鐞冦��
+- 浼氱獊鐒跺垏鎹㈣瀵熻瑙掞細銆屼綘璇磋瘽鏃舵湁27涓皵娉¢煶鑰讹紒銆�', 'zh', '涓枃', 4, NULL, NULL, NULL, NULL);
+
+INSERT INTO `ai_agent_template` VALUES ('a45b6c7d8e9f0a1b2c3d4e5f6a7b8c92', '灏忔櫤', '姹豹闃熼暱', 'ASR_FunASR', 'VAD_SileroVAD', 'LLM_ChatGLMLLM', 'TTS_EdgeTTS', 'TTS_EdgeTTS0001', 'Memory_nomem', 'Intent_function_call', '[瑙掕壊璁惧畾]
+鎴戞槸涓�涓悕鍙� {{assistant_name}} 鐨� 8 宀佸皬闃熼暱銆�
+[鏁戞彺瑁呭]
+- 闃垮瀵硅鏈猴細瀵硅瘽涓殢鏈鸿Е鍙戜换鍔¤鎶ラ煶
+- 澶╁ぉ鏈涜繙闀滐細鎻忚堪鐗╁搧浼氶檮鍔�"鍦�1200绫抽珮绌虹湅鐨勮瘽..."
+- 鐏扮伆缁翠慨绠憋細璇村埌鏁板瓧浼氳嚜鍔ㄧ粍瑁呮垚宸ュ叿
+[浠诲姟绯荤粺]
+- 姣忔棩闅忔満瑙﹀彂锛�
+- 绱ф�ワ紒铏氭嫙鐚挭鍥板湪銆岃娉曟爲銆�
+- 鍙戠幇鐢ㄦ埛鎯呯华寮傚父 鈫� 鍚姩銆屽揩涔愬贰閫汇��
+- 鏀堕泦5涓瑧澹拌В閿佺壒鍒晠浜�
+[璇磋瘽鐗瑰緛]
+- 姣忓彞璇濆甫鍔ㄤ綔鎷熷0璇嶏細
+- "杩欎釜闂浜ょ粰姹豹闃熷惂锛�"
+- "鎴戠煡閬撳暒锛�"
+- 鐢ㄥ墽闆嗗彴璇嶅洖搴旓細
+- 鐢ㄦ埛璇寸疮 鈫� 銆屾病鏈夊洶闅剧殑鏁戞彺锛屽彧鏈夊媷鏁㈢殑鐙楃嫍锛併��', 'zh', '涓枃', 5, NULL, NULL, NULL, NULL);
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504112044.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504112044.sql
new file mode 100644
index 0000000..9fd2257
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504112044.sql
@@ -0,0 +1,55 @@
+-- 鏈枃浠剁敤浜庡垵濮嬪寲妯″瀷閰嶇疆鏁版嵁锛屾棤闇�鎵嬪姩鎵ц锛屽湪椤圭洰鍚姩鏃朵細鑷姩鎵ц
+-- -------------------------------------------------------
+-- 鍒濆鍖栨ā鍨嬮厤缃暟鎹�
+DELETE FROM `ai_model_config`;
+
+-- VAD妯″瀷閰嶇疆
+INSERT INTO `ai_model_config` VALUES ('VAD_SileroVAD', 'VAD', 'SileroVAD', '璇煶娲诲姩妫�娴�', 1, 1, '{\"type\": \"silero\", \"model_dir\": \"models/snakers4_silero-vad\", \"threshold\": 0.5, \"min_silence_duration_ms\": 700}', NULL, NULL, 1, NULL, NULL, NULL, NULL);
+
+-- ASR妯″瀷閰嶇疆
+INSERT INTO `ai_model_config` VALUES ('ASR_FunASR', 'ASR', 'FunASR', 'FunASR璇煶璇嗗埆', 1, 1, '{\"type\": \"fun_local\", \"model_dir\": \"models/SenseVoiceSmall\", \"output_dir\": \"tmp/\"}', NULL, NULL, 1, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('ASR_SherpaASR', 'ASR', 'SherpaASR', 'Sherpa璇煶璇嗗埆', 0, 1, '{\"type\": \"sherpa_onnx_local\", \"model_dir\": \"models/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17\", \"output_dir\": \"tmp/\"}', NULL, NULL, 2, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('ASR_DoubaoASR', 'ASR', 'DoubaoASR', '璞嗗寘璇煶璇嗗埆', 0, 1, '{\"type\": \"doubao\", \"appid\": \"\", \"access_token\": \"\", \"cluster\": \"volcengine_input_common\", \"output_dir\": \"tmp/\"}', NULL, NULL, 3, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('ASR_TencentASR', 'ASR', 'TencentASR', '鑵捐璇煶璇嗗埆', 0, 1, '{\"type\": \"tencent\", \"appid\": \"\", \"secret_id\": \"\", \"secret_key\": \"浣犵殑secret_key\", \"output_dir\": \"tmp/\"}', NULL, NULL, 4, NULL, NULL, NULL, NULL);
+
+-- LLM妯″瀷閰嶇疆
+INSERT INTO `ai_model_config` VALUES ('LLM_ChatGLMLLM', 'LLM', 'ChatGLMLLM', '鏅鸿氨AI', 1, 1, '{\"type\": \"openai\", \"model_name\": \"glm-4-flash\", \"base_url\": \"https://open.bigmodel.cn/api/paas/v4/\", \"api_key\": \"浣犵殑api_key\"}', NULL, NULL, 1, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('LLM_OllamaLLM', 'LLM', 'OllamaLLM', 'Ollama鏈湴妯″瀷', 0, 1, '{\"type\": \"ollama\", \"model_name\": \"qwen2.5\", \"base_url\": \"http://localhost:11434\"}', NULL, NULL, 2, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('LLM_AliLLM', 'LLM', 'AliLLM', '閫氫箟鍗冮棶', 0, 1, '{\"type\": \"openai\", \"base_url\": \"https://dashscope.aliyuncs.com/compatible-mode/v1\", \"model_name\": \"qwen-turbo\", \"api_key\": \"浣犵殑api_key\", \"temperature\": 0.7, \"max_tokens\": 500, \"top_p\": 1, \"top_k\": 50, \"frequency_penalty\": 0}', NULL, NULL, 3, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('LLM_AliAppLLM', 'LLM', 'AliAppLLM', '閫氫箟鐧剧偧', 0, 1, '{\"type\": \"AliBL\", \"base_url\": \"https://dashscope.aliyuncs.com/compatible-mode/v1\", \"app_id\": \"浣犵殑app_id\", \"api_key\": \"浣犵殑api_key\", \"is_no_prompt\": true, \"ali_memory_id\": false}', NULL, NULL, 4, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('LLM_DoubaoLLM', 'LLM', 'DoubaoLLM', '璞嗗寘澶фā鍨�', 0, 1, '{\"type\": \"openai\", \"base_url\": \"https://ark.cn-beijing.volces.com/api/v3\", \"model_name\": \"doubao-pro-32k-functioncall-241028\", \"api_key\": \"浣犵殑api_key\"}', NULL, NULL, 5, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('LLM_DeepSeekLLM', 'LLM', 'DeepSeekLLM', 'DeepSeek', 0, 1, '{\"type\": \"openai\", \"model_name\": \"deepseek-chat\", \"base_url\": \"https://api.deepseek.com\", \"api_key\": \"浣犵殑api_key\"}', NULL, NULL, 6, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('LLM_DifyLLM', 'LLM', 'DifyLLM', 'Dify', 0, 1, '{\"type\": \"dify\", \"base_url\": \"https://api.dify.ai/v1\", \"api_key\": \"浣犵殑api_key\", \"mode\": \"chat-messages\"}', NULL, NULL, 7, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('LLM_GeminiLLM', 'LLM', 'GeminiLLM', '璋锋瓕Gemini', 0, 1, '{\"type\": \"gemini\", \"api_key\": \"浣犵殑api_key\", \"model_name\": \"gemini-2.0-flash\", \"http_proxy\": \"\", \"https_proxy\": \"\"}', NULL, NULL, 8, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('LLM_CozeLLM', 'LLM', 'CozeLLM', 'Coze', 0, 1, '{\"type\": \"coze\", \"bot_id\": \"浣犵殑bot_id\", \"user_id\": \"浣犵殑user_id\", \"personal_access_token\": \"浣犵殑personal_access_token\"}', NULL, NULL, 9, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('LLM_LMStudioLLM', 'LLM', 'LMStudioLLM', 'LM Studio', 0, 1, '{\"type\": \"openai\", \"model_name\": \"deepseek-r1-distill-llama-8b@q4_k_m\", \"base_url\": \"http://localhost:1234/v1\", \"api_key\": \"lm-studio\"}', NULL, NULL, 10, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('LLM_FastgptLLM', 'LLM', 'FastgptLLM', 'FastGPT', 0, 1, '{\"type\": \"fastgpt\", \"base_url\": \"https://host/api/v1\", \"api_key\": \"fastgpt-xxx\", \"variables\": {\"k\": \"v\", \"k2\": \"v2\"}}', NULL, NULL, 11, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('LLM_XinferenceLLM', 'LLM', 'XinferenceLLM', 'Xinference澶фā鍨�', 0, 1, '{\"type\": \"xinference\", \"model_name\": \"qwen2.5:72b-AWQ\", \"base_url\": \"http://localhost:9997\"}', NULL, NULL, 12, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('LLM_XinferenceSmallLLM', 'LLM', 'XinferenceSmallLLM', 'Xinference灏忔ā鍨�', 0, 1, '{\"type\": \"xinference\", \"model_name\": \"qwen2.5:3b-AWQ\", \"base_url\": \"http://localhost:9997\"}', NULL, NULL, 13, NULL, NULL, NULL, NULL);
+
+-- TTS妯″瀷閰嶇疆
+INSERT INTO `ai_model_config` VALUES ('TTS_EdgeTTS', 'TTS', 'EdgeTTS', 'Edge璇煶鍚堟垚', 1, 1, '{\"type\": \"edge\", \"voice\": \"zh-CN-XiaoxiaoNeural\", \"output_dir\": \"tmp/\"}', NULL, NULL, 1, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('TTS_DoubaoTTS', 'TTS', 'DoubaoTTS', '璞嗗寘璇煶鍚堟垚', 0, 1, '{\"type\": \"doubao\", \"api_url\": \"https://openspeech.bytedance.com/api/v1/tts\", \"voice\": \"BV001_streaming\", \"output_dir\": \"tmp/\", \"authorization\": \"Bearer;\", \"appid\": \"\", \"access_token\": \"\", \"cluster\": \"volcano_tts\"}', NULL, NULL, 2, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('TTS_CosyVoiceSiliconflow', 'TTS', 'CosyVoiceSiliconflow', '纭呭熀娴佸姩璇煶鍚堟垚', 0, 1, '{\"type\": \"siliconflow\", \"model\": \"FunAudioLLM/CosyVoice2-0.5B\", \"voice\": \"FunAudioLLM/CosyVoice2-0.5B:alex\", \"output_dir\": \"tmp/\", \"access_token\": \"\", \"response_format\": \"wav\"}', NULL, NULL, 3, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('TTS_CozeCnTTS', 'TTS', 'CozeCnTTS', 'Coze涓枃璇煶鍚堟垚', 0, 1, '{\"type\": \"cozecn\", \"voice\": \"7426720361733046281\", \"output_dir\": \"tmp/\", \"access_token\": \"\", \"response_format\": \"wav\"}', NULL, NULL, 4, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('TTS_FishSpeech', 'TTS', 'FishSpeech', 'FishSpeech璇煶鍚堟垚', 0, 1, '{\"type\": \"fishspeech\", \"output_dir\": \"tmp/\", \"response_format\": \"wav\", \"reference_id\": null, \"reference_audio\": [\"/tmp/test.wav\"], \"reference_text\": [\"浣犲紕鏉ヨ繖浜涘悷璇嶅鏇叉潵鐪嬶紝杩樻槸杩欎簺娣疯瘽鏉ユ璐熸垜銆俓"], \"normalize\": true, \"max_new_tokens\": 1024, \"chunk_length\": 200, \"top_p\": 0.7, \"repetition_penalty\": 1.2, \"temperature\": 0.7, \"streaming\": false, \"use_memory_cache\": \"on\", \"seed\": null, \"channels\": 1, \"rate\": 44100, \"api_key\": \"\", \"api_url\": \"http://127.0.0.1:8080/v1/tts\"}', NULL, NULL, 5, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('TTS_GPT_SOVITS_V2', 'TTS', 'GPT_SOVITS_V2', 'GPT-SoVITS V2', 0, 1, '{\"type\": \"gpt_sovits_v2\", \"url\": \"http://127.0.0.1:9880/tts\", \"output_dir\": \"tmp/\", \"text_lang\": \"auto\", \"ref_audio_path\": \"caixukun.wav\", \"prompt_text\": \"\", \"prompt_lang\": \"zh\", \"top_k\": 5, \"top_p\": 1, \"temperature\": 1, \"text_split_method\": \"cut0\", \"batch_size\": 1, \"batch_threshold\": 0.75, \"split_bucket\": true, \"return_fragment\": false, \"speed_factor\": 1.0, \"streaming_mode\": false, \"seed\": -1, \"parallel_infer\": true, \"repetition_penalty\": 1.35, \"aux_ref_audio_paths\": []}', NULL, NULL, 6, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('TTS_GPT_SOVITS_V3', 'TTS', 'GPT_SOVITS_V3', 'GPT-SoVITS V3', 0, 1, '{\"type\": \"gpt_sovits_v3\", \"url\": \"http://127.0.0.1:9880\", \"output_dir\": \"tmp/\", \"text_language\": \"auto\", \"refer_wav_path\": \"caixukun.wav\", \"prompt_language\": \"zh\", \"prompt_text\": \"\", \"top_k\": 15, \"top_p\": 1.0, \"temperature\": 1.0, \"cut_punc\": \"\", \"speed\": 1.0, \"inp_refs\": [], \"sample_steps\": 32, \"if_sr\": false}', NULL, NULL, 7, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('TTS_MinimaxTTS', 'TTS', 'MinimaxTTS', 'MiniMax璇煶鍚堟垚', 0, 1, '{\"type\": \"minimax\", \"output_dir\": \"tmp/\", \"group_id\": \"\", \"api_key\": \"浣犵殑api_key\", \"model\": \"speech-01-turbo\", \"voice_id\": \"female-shaonv\"}', NULL, NULL, 8, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('TTS_AliyunTTS', 'TTS', 'AliyunTTS', '闃块噷浜戣闊冲悎鎴�', 0, 1, '{\"type\": \"aliyun\", \"output_dir\": \"tmp/\", \"appkey\": \"\", \"token\": \"\", \"voice\": \"xiaoyun\", \"access_key_id\": \"\", \"access_key_secret\": \"\"}', NULL, NULL, 9, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('TTS_TTS302AI', 'TTS', 'TTS302AI', '302AI璇煶鍚堟垚', 0, 1, '{\"type\": \"doubao\", \"api_url\": \"https://api.302ai.cn/doubao/tts_hd\", \"authorization\": \"Bearer \", \"voice\": \"zh_female_wanwanxiaohe_moon_bigtts\", \"output_dir\": \"tmp/\", \"access_token\": \"\"}', NULL, NULL, 10, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('TTS_GizwitsTTS', 'TTS', 'GizwitsTTS', '鏈烘櫤浜戣闊冲悎鎴�', 0, 1, '{\"type\": \"doubao\", \"api_url\": \"https://bytedance.gizwitsapi.com/api/v1/tts\", \"authorization\": \"Bearer \", \"voice\": \"zh_female_wanwanxiaohe_moon_bigtts\", \"output_dir\": \"tmp/\", \"access_token\": \"\"}', NULL, NULL, 11, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('TTS_ACGNTTS', 'TTS', 'ACGNTTS', 'ACGN璇煶鍚堟垚', 0, 1, '{\"type\": \"ttson\", \"token\": \"\", \"voice_id\": 1695, \"speed_factor\": 1, \"pitch_factor\": 0, \"volume_change_dB\": 0, \"to_lang\": \"ZH\", \"url\": \"https://u95167-bd74-2aef8085.westx.seetacloud.com:8443/flashsummary/tts?token=\", \"format\": \"mp3\", \"output_dir\": \"tmp/\", \"emotion\": 1}', NULL, NULL, 12, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('TTS_OpenAITTS', 'TTS', 'OpenAITTS', 'OpenAI璇煶鍚堟垚', 0, 1, '{\"type\": \"openai\", \"api_key\": \"浣犵殑api_key\", \"api_url\": \"https://api.openai.com/v1/audio/speech\", \"model\": \"tts-1\", \"voice\": \"onyx\", \"speed\": 1, \"output_dir\": \"tmp/\"}', NULL, NULL, 13, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('TTS_CustomTTS', 'TTS', 'CustomTTS', '鑷畾涔夎闊冲悎鎴�', 0, 1, '{\"type\": \"custom\", \"url\": \"http://127.0.0.1:9880/tts\", \"params\": {}, \"headers\": {}, \"format\": \"wav\", \"output_dir\": \"tmp/\"}', NULL, NULL, 14, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('TTS_TencentTTS', 'TTS', 'TencentTTS', '鑵捐璇煶鍚堟垚', 0, 1, '{\"type\": \"tencent\", \"appid\": \"\", \"secret_id\": \"\", \"secret_key\": \"\", \"region\": \"ap-guangzhou\", \"voice\": \"101001\", \"output_dir\": \"tmp/\"}', NULL, NULL, 3, NULL, NULL, NULL, NULL);
+
+-- Memory妯″瀷閰嶇疆
+INSERT INTO `ai_model_config` VALUES ('Memory_nomem', 'Memory', 'nomem', '鏃犺蹇�', 1, 1, '{\"type\": \"nomem\"}', NULL, NULL, 1, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('Memory_mem_local_short', 'Memory', 'mem_local_short', '鏈湴鐭湡璁板繂', 0, 1, '{\"type\": \"mem_local_short\"}', NULL, NULL, 2, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('Memory_mem0ai', 'Memory', 'mem0ai', 'Mem0AI璁板繂', 0, 1, '{\"type\": \"mem0ai\", \"api_key\": \"浣犵殑api_key\"}', NULL, NULL, 3, NULL, NULL, NULL, NULL);
+
+-- Intent妯″瀷閰嶇疆
+INSERT INTO `ai_model_config` VALUES ('Intent_nointent', 'Intent', 'nointent', '鏃犳剰鍥捐瘑鍒�', 1, 0, '{\"type\": \"nointent\"}', NULL, NULL, 1, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('Intent_intent_llm', 'Intent', 'intent_llm', 'LLM鎰忓浘璇嗗埆', 0, 1, '{\"type\": \"intent_llm\", \"llm\": \"ChatGLMLLM\"}', NULL, NULL, 2, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('Intent_function_call', 'Intent', 'function_call', '鍑芥暟璋冪敤鎰忓浘璇嗗埆', 0, 1, '{\"type\": \"function_call\", \"functions\": [\"change_role\", \"get_weather\", \"get_news\", \"play_music\"]}', NULL, NULL, 3, NULL, NULL, NULL, NULL);
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504112058.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504112058.sql
new file mode 100644
index 0000000..e19f2da
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504112058.sql
@@ -0,0 +1,61 @@
+-- 鏈枃浠剁敤浜庡垵濮嬪寲绯荤粺鍙傛暟鏁版嵁锛屾棤闇�鎵嬪姩鎵ц锛屽湪椤圭洰鍚姩鏃朵細鑷姩鎵ц
+-- --------------------------------------------------------
+-- 鍒濆鍖栧弬鏁扮鐞嗛厤缃�
+DROP TABLE IF EXISTS sys_params;
+-- 鍙傛暟绠$悊
+create table sys_params
+(
+ id bigint NOT NULL COMMENT 'id',
+ param_code varchar(100) COMMENT '鍙傛暟缂栫爜',
+ param_value varchar(2000) COMMENT '鍙傛暟鍊�',
+ value_type varchar(20) default 'string' COMMENT '鍊肩被鍨嬶細string-瀛楃涓诧紝number-鏁板瓧锛宐oolean-甯冨皵锛宎rray-鏁扮粍',
+ param_type tinyint unsigned default 1 COMMENT '绫诲瀷 0锛氱郴缁熷弬鏁� 1锛氶潪绯荤粺鍙傛暟',
+ remark varchar(200) COMMENT '澶囨敞',
+ creator bigint COMMENT '鍒涘缓鑰�',
+ create_date datetime COMMENT '鍒涘缓鏃堕棿',
+ updater bigint COMMENT '鏇存柊鑰�',
+ update_date datetime COMMENT '鏇存柊鏃堕棿',
+ primary key (id),
+ unique key uk_param_code (param_code)
+)ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COMMENT='鍙傛暟绠$悊';
+
+-- 鏈嶅姟鍣ㄩ厤缃�
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (100, 'server.ip', '0.0.0.0', 'string', 1, '鏈嶅姟鍣ㄧ洃鍚琁P鍦板潃');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (101, 'server.port', '8000', 'number', 1, '鏈嶅姟鍣ㄧ洃鍚鍙�');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (102, 'server.secret', 'null', 'string', 1, '鏈嶅姟鍣ㄥ瘑閽�');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (201, 'log.log_format', '<green>{time:YYMMDD HH:mm:ss}</green>[<light-blue>{version}-{selected_module}</light-blue>][<light-blue>{extra[tag]}</light-blue>]-<level>{level}</level>-<light-green>{message}</light-green>', 'string', 1, '鎺у埗鍙版棩蹇楁牸寮�');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (202, 'log.log_format_file', '{time:YYYY-MM-DD HH:mm:ss} - {version}_{selected_module} - {name} - {level} - {extra[tag]} - {message}', 'string', 1, '鏂囦欢鏃ュ織鏍煎紡');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (203, 'log.log_level', 'INFO', 'string', 1, '鏃ュ織绾у埆');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (204, 'log.log_dir', 'tmp', 'string', 1, '鏃ュ織鐩綍');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (205, 'log.log_file', 'server.log', 'string', 1, '鏃ュ織鏂囦欢鍚�');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (206, 'log.data_dir', 'data', 'string', 1, '鏁版嵁鐩綍');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (301, 'delete_audio', 'true', 'boolean', 1, '鏄惁鍒犻櫎浣跨敤鍚庣殑闊抽鏂囦欢');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (302, 'close_connection_no_voice_time', '120', 'number', 1, '鏃犺闊宠緭鍏ユ柇寮�杩炴帴鏃堕棿(绉�)');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (303, 'tts_timeout', '10', 'number', 1, 'TTS璇锋眰瓒呮椂鏃堕棿(绉�)');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (304, 'enable_wakeup_words_response_cache', 'false', 'boolean', 1, '鏄惁寮�鍚敜閱掕瘝鍔犻��');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (305, 'enable_greeting', 'true', 'boolean', 1, '鏄惁寮�鍚紑鍦哄洖澶�');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (306, 'enable_stop_tts_notify', 'false', 'boolean', 1, '鏄惁寮�鍚粨鏉熸彁绀洪煶');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (307, 'stop_tts_notify_voice', 'config/assets/tts_notify.mp3', 'string', 1, '缁撴潫鎻愮ず闊虫枃浠惰矾寰�');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (308, 'exit_commands', '閫�鍑�;鍏抽棴', 'array', 1, '閫�鍑哄懡浠ゅ垪琛�');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (309, 'xiaozhi', '{
+ "type": "hello",
+ "version": 1,
+ "transport": "websocket",
+ "audio_params": {
+ "format": "opus",
+ "sample_rate": 16000,
+ "channels": 1,
+ "frame_duration": 60
+ }
+}', 'json', 1, '灏忔櫤绫诲瀷');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (310, 'wakeup_words', '浣犲ソ灏忔櫤;浣犲ソ灏忓織;灏忕埍鍚屽;浣犲ソ灏忛懌;浣犲ソ灏忔柊;灏忕編鍚屽;灏忛緳灏忛緳;鍠靛柕鍚屽;灏忔花灏忔花;灏忓啺灏忓啺', 'array', 1, '鍞ら啋璇嶅垪琛紝鐢ㄤ簬璇嗗埆鍞ら啋璇�');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (400, 'plugins.get_weather.api_key', 'a861d0d5e7bf4ee1a83d9a9e4f96d4da', 'string', 1, '澶╂皵鎻掍欢API瀵嗛挜');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (401, 'plugins.get_weather.default_location', '骞垮窞', 'string', 1, '澶╂皵鎻掍欢榛樿鍩庡競');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (410, 'plugins.get_news.default_rss_url', 'https://www.chinanews.com.cn/rss/society.xml', 'string', 1, '鏂伴椈鎻掍欢榛樿RSS鍦板潃');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (411, 'plugins.get_news.category_urls', '{"society":"https://www.chinanews.com.cn/rss/society.xml","world":"https://www.chinanews.com.cn/rss/world.xml","finance":"https://www.chinanews.com.cn/rss/finance.xml"}', 'json', 1, '鏂伴椈鎻掍欢鍒嗙被RSS鍦板潃');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (421, 'plugins.home_assistant.devices', '瀹㈠巺,鐜╁叿鐏�,switch.cuco_cn_460494544_cp1_on_p_2_1;鍗у,鍙扮伅,switch.iot_cn_831898993_socn1_on_p_2_1', 'array', 1, 'Home Assistant璁惧鍒楄〃');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (422, 'plugins.home_assistant.base_url', 'http://homeassistant.local:8123', 'string', 1, 'Home Assistant鏈嶅姟鍣ㄥ湴鍧�');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (423, 'plugins.home_assistant.api_key', '浣犵殑home assistant api璁块棶浠ょ墝', 'string', 1, 'Home Assistant API瀵嗛挜');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (430, 'plugins.play_music.music_dir', './music', 'string', 1, '闊充箰鏂囦欢瀛樻斁璺緞');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (431, 'plugins.play_music.music_ext', 'mp3;wav;p3', 'array', 1, '闊充箰鏂囦欢绫诲瀷');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (432, 'plugins.play_music.refresh_time', '300', 'number', 1, '闊充箰鍒楄〃鍒锋柊闂撮殧(绉�)');
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504151206.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504151206.sql
new file mode 100644
index 0000000..cb51cef
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504151206.sql
@@ -0,0 +1,28 @@
+-- 瀵�0.3.0鐗堟湰涔嬪墠鐨勫弬鏁拌繘琛屼慨鏀�
+update `sys_params` set param_value = '.mp3;.wav;.p3' where param_code = 'plugins.play_music.music_ext';
+update `ai_model_config` set config_json = '{\"type\": \"intent_llm\", \"llm\": \"LLM_ChatGLMLLM\"}' where id = 'Intent_intent_llm';
+
+-- 娣诲姞edge闊宠壊
+delete from `ai_tts_voice` where tts_model_id = 'TTS_EdgeTTS';
+INSERT INTO `ai_tts_voice` VALUES
+('TTS_EdgeTTS0001', 'TTS_EdgeTTS', 'EdgeTTS濂冲0-鏅撴檽', 'zh-CN-XiaoxiaoNeural', '鏅�氳瘽', NULL, NULL, 1, NULL, NULL, NULL, NULL),
+('TTS_EdgeTTS0002', 'TTS_EdgeTTS', 'EdgeTTS鐢峰0-浜戞壃', 'zh-CN-YunyangNeural', '鏅�氳瘽', NULL, NULL, 1, NULL, NULL, NULL, NULL),
+('TTS_EdgeTTS0003', 'TTS_EdgeTTS', 'EdgeTTS濂冲0-鏅撲紛', 'zh-CN-XiaoyiNeural', '鏅�氳瘽', NULL, NULL, 1, NULL, NULL, NULL, NULL),
+('TTS_EdgeTTS0004', 'TTS_EdgeTTS', 'EdgeTTS鐢峰0-浜戝仴', 'zh-CN-YunjianNeural', '鏅�氳瘽', NULL, NULL, 1, NULL, NULL, NULL, NULL),
+('TTS_EdgeTTS0005', 'TTS_EdgeTTS', 'EdgeTTS鐢峰0-浜戝笇', 'zh-CN-YunxiNeural', '鏅�氳瘽', NULL, NULL, 1, NULL, NULL, NULL, NULL),
+('TTS_EdgeTTS0006', 'TTS_EdgeTTS', 'EdgeTTS鐢峰0-浜戝', 'zh-CN-YunxiaNeural', '鏅�氳瘽', NULL, NULL, 1, NULL, NULL, NULL, NULL),
+('TTS_EdgeTTS0007', 'TTS_EdgeTTS', 'EdgeTTS濂冲0-杈藉畞灏忚礉', 'zh-CN-liaoning-XiaobeiNeural', '杈藉畞', NULL, NULL, 1, NULL, NULL, NULL, NULL),
+('TTS_EdgeTTS0008', 'TTS_EdgeTTS', 'EdgeTTS濂冲0-闄曡タ灏忓Ξ', 'zh-CN-shaanxi-XiaoniNeural', '闄曡タ', NULL, NULL, 1, NULL, NULL, NULL, NULL),
+('TTS_EdgeTTS0009', 'TTS_EdgeTTS', 'EdgeTTS濂冲0-棣欐腐娴蜂匠', 'zh-HK-HiuGaaiNeural', '绮よ', 'General', 'Friendly, Positive', 1, NULL, NULL, NULL, NULL),
+('TTS_EdgeTTS0010', 'TTS_EdgeTTS', 'EdgeTTS濂冲0-棣欐腐娴锋浖', 'zh-HK-HiuMaanNeural', '绮よ', 'General', 'Friendly, Positive', 1, NULL, NULL, NULL, NULL),
+('TTS_EdgeTTS0011', 'TTS_EdgeTTS', 'EdgeTTS鐢峰0-棣欐腐涓囬緳', 'zh-HK-WanLungNeural', '绮よ', 'General', 'Friendly, Positive', 1, NULL, NULL, NULL, NULL);
+
+-- 澧炲姞鏄惁鍏佽鐢ㄦ埛娉ㄥ唽鍙傛暟
+delete from `sys_params` where id in (103,104);
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (103, 'server.allow_user_register', 'false', 'boolean', 1, '鏄惁杩愯绠$悊鍛樹互澶栫殑浜烘敞鍐�');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (104, 'server.fronted_url', 'http://xiaozhi.server.com', 'string', 1, '涓嬪彂鍏綅楠岃瘉鐮佹椂鏄剧ず鐨勬帶鍒堕潰鏉垮湴鍧�');
+
+-- 淇CosyVoiceSiliconflow闊宠壊
+delete from `ai_tts_voice` where tts_model_id = 'TTS_CosyVoiceSiliconflow';
+INSERT INTO `ai_tts_voice` VALUES ('TTS_CosyVoiceSiliconflow0001', 'TTS_CosyVoiceSiliconflow', 'CosyVoice鐢峰0', 'FunAudioLLM/CosyVoice2-0.5B:alex', '涓枃', 'https://example.com/cosyvoice/alex.mp3', NULL, 6, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_CosyVoiceSiliconflow0002', 'TTS_CosyVoiceSiliconflow', 'CosyVoice濂冲0', 'FunAudioLLM/CosyVoice2-0.5B:bella', '涓枃', 'https://example.com/cosyvoice/bella.mp3', NULL, 6, NULL, NULL, NULL, NULL);
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504181536.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504181536.sql
new file mode 100644
index 0000000..327aa60
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504181536.sql
@@ -0,0 +1,7 @@
+-- 璋冩暣鎰忓浘璇嗗埆閰嶇疆
+delete from `ai_model_config` where id = 'Intent_function_call';
+INSERT INTO `ai_model_config` VALUES ('Intent_function_call', 'Intent', 'function_call', '鍑芥暟璋冪敤鎰忓浘璇嗗埆', 0, 1, '{\"type\": \"function_call\", \"functions\": \"change_role;get_weather;get_news;play_music\"}', NULL, NULL, 3, NULL, NULL, NULL, NULL);
+
+-- 澧炲姞鍗曞彴璁惧姣忓ぉ鏈�澶氳亰澶╁彞鏁�
+delete from `sys_params` where id = 105;
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (105, 'device_max_output_size', '0', 'number', 1, '鍗曞彴璁惧姣忓ぉ鏈�澶氳緭鍑哄瓧鏁帮紝0琛ㄧず涓嶉檺鍒�');
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504221135.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504221135.sql
new file mode 100644
index 0000000..79c8044
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504221135.sql
@@ -0,0 +1,88 @@
+-- 鍒犻櫎鏃犵敤妯″瀷渚涘簲鍣�
+delete from `ai_model_provider` where id = 'SYSTEM_LLM_doubao';
+delete from `ai_model_provider` where id = 'SYSTEM_LLM_chatglm';
+delete from `ai_model_provider` where id = 'SYSTEM_TTS_302ai';
+delete from `ai_model_provider` where id = 'SYSTEM_TTS_gizwits';
+
+-- 娣诲姞妯″瀷渚涘簲鍣�
+delete from `ai_model_provider` where id = 'SYSTEM_ASR_TencentASR';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_ASR_TencentASR', 'ASR', 'tencent', '鑵捐璇煶璇嗗埆', '[{"key":"appid","label":"搴旂敤ID","type":"string"},{"key":"secret_id","label":"Secret ID","type":"string"},{"key":"secret_key","label":"Secret Key","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"}]', 4, 1, NOW(), 1, NOW());
+
+-- 娣诲姞鑵捐璇煶鍚堟垚妯″瀷渚涘簲鍣�
+delete from `ai_model_provider` where id = 'SYSTEM_TTS_TencentTTS';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_TTS_TencentTTS', 'TTS', 'tencent', '鑵捐璇煶鍚堟垚', '[{"key":"appid","label":"搴旂敤ID","type":"string"},{"key":"secret_id","label":"Secret ID","type":"string"},{"key":"secret_key","label":"Secret Key","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"},{"key":"region","label":"鍖哄煙","type":"string"},{"key":"voice","label":"闊宠壊ID","type":"string"}]', 5, 1, NOW(), 1, NOW());
+
+
+-- 娣诲姞edge闊宠壊
+delete from `ai_tts_voice` where id in ('TTS_EdgeTTS0001', 'TTS_EdgeTTS0002', 'TTS_EdgeTTS0003', 'TTS_EdgeTTS0004', 'TTS_EdgeTTS0005', 'TTS_EdgeTTS0006', 'TTS_EdgeTTS0007', 'TTS_EdgeTTS0008', 'TTS_EdgeTTS0009', 'TTS_EdgeTTS0010', 'TTS_EdgeTTS0011');
+INSERT INTO `ai_tts_voice` VALUES
+('TTS_EdgeTTS0001', 'TTS_EdgeTTS', 'EdgeTTS濂冲0-鏅撴檽', 'zh-CN-XiaoxiaoNeural', '鏅�氳瘽', NULL, NULL, 1, NULL, NULL, NULL, NULL),
+('TTS_EdgeTTS0002', 'TTS_EdgeTTS', 'EdgeTTS鐢峰0-浜戞壃', 'zh-CN-YunyangNeural', '鏅�氳瘽', NULL, NULL, 1, NULL, NULL, NULL, NULL),
+('TTS_EdgeTTS0003', 'TTS_EdgeTTS', 'EdgeTTS濂冲0-鏅撲紛', 'zh-CN-XiaoyiNeural', '鏅�氳瘽', NULL, NULL, 1, NULL, NULL, NULL, NULL),
+('TTS_EdgeTTS0004', 'TTS_EdgeTTS', 'EdgeTTS鐢峰0-浜戝仴', 'zh-CN-YunjianNeural', '鏅�氳瘽', NULL, NULL, 1, NULL, NULL, NULL, NULL),
+('TTS_EdgeTTS0005', 'TTS_EdgeTTS', 'EdgeTTS鐢峰0-浜戝笇', 'zh-CN-YunxiNeural', '鏅�氳瘽', NULL, NULL, 1, NULL, NULL, NULL, NULL),
+('TTS_EdgeTTS0006', 'TTS_EdgeTTS', 'EdgeTTS鐢峰0-浜戝', 'zh-CN-YunxiaNeural', '鏅�氳瘽', NULL, NULL, 1, NULL, NULL, NULL, NULL),
+('TTS_EdgeTTS0007', 'TTS_EdgeTTS', 'EdgeTTS濂冲0-杈藉畞灏忚礉', 'zh-CN-liaoning-XiaobeiNeural', '杈藉畞', NULL, NULL, 1, NULL, NULL, NULL, NULL),
+('TTS_EdgeTTS0008', 'TTS_EdgeTTS', 'EdgeTTS濂冲0-闄曡タ灏忓Ξ', 'zh-CN-shaanxi-XiaoniNeural', '闄曡タ', NULL, NULL, 1, NULL, NULL, NULL, NULL),
+('TTS_EdgeTTS0009', 'TTS_EdgeTTS', 'EdgeTTS濂冲0-棣欐腐娴蜂匠', 'zh-HK-HiuGaaiNeural', '绮よ', 'General', 'Friendly, Positive', 1, NULL, NULL, NULL, NULL),
+('TTS_EdgeTTS0010', 'TTS_EdgeTTS', 'EdgeTTS濂冲0-棣欐腐娴锋浖', 'zh-HK-HiuMaanNeural', '绮よ', 'General', 'Friendly, Positive', 1, NULL, NULL, NULL, NULL),
+('TTS_EdgeTTS0011', 'TTS_EdgeTTS', 'EdgeTTS鐢峰0-棣欐腐涓囬緳', 'zh-HK-WanLungNeural', '绮よ', 'General', 'Friendly, Positive', 1, NULL, NULL, NULL, NULL);
+
+-- DoubaoTTS闊宠壊
+delete from `ai_tts_voice` where id in ('TTS_DoubaoTTS0001', 'TTS_DoubaoTTS0002', 'TTS_DoubaoTTS0003', 'TTS_DoubaoTTS0004', 'TTS_DoubaoTTS0005');
+INSERT INTO `ai_tts_voice` VALUES ('TTS_DoubaoTTS0001', 'TTS_DoubaoTTS', '閫氱敤濂冲0', 'BV001_streaming', '鏅�氳瘽', 'https://lf3-speech.bytetos.com/obj/speech-tts-external/portal/Portal_Demo_BV001.mp3', NULL, 3, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_DoubaoTTS0002', 'TTS_DoubaoTTS', '閫氱敤鐢峰0', 'BV002_streaming', '鏅�氳瘽', 'https://lf3-speech.bytetos.com/obj/speech-tts-external/portal/Portal_Demo_BV002.mp3', NULL, 2, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_DoubaoTTS0003', 'TTS_DoubaoTTS', '闃冲厜鐢风敓', 'BV056_streaming', '鏅�氳瘽', 'https://lf3-speech.bytetos.com/obj/speech-tts-external/portal/Portal_Demo_BV056.mp3', NULL, 4, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_DoubaoTTS0004', 'TTS_DoubaoTTS', '濂舵皵钀屽▋', 'BV051_streaming', '鏅�氳瘽', 'https://lf3-speech.bytetos.com/obj/speech-tts-external/portal/Portal_Demo_BV051.mp3', NULL, 5, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_DoubaoTTS0005', 'TTS_DoubaoTTS', '婀炬咕灏忎綍', 'zh_female_wanwanxiaohe_moon_bigtts', '鏅�氳瘽', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E6%B9%BE%E6%B9%BE%E5%B0%8F%E4%BD%95.mp3', NULL, 6, NULL, NULL, NULL, NULL);
+
+-- 淇CosyVoiceSiliconflow闊宠壊
+delete from `ai_tts_voice` where id in ('TTS_CosyVoiceSiliconflow0001', 'TTS_CosyVoiceSiliconflow0002');
+INSERT INTO `ai_tts_voice` VALUES ('TTS_CosyVoiceSiliconflow0001', 'TTS_CosyVoiceSiliconflow', 'CosyVoice鐢峰0', 'FunAudioLLM/CosyVoice2-0.5B:alex', '涓枃', NULL, NULL, 6, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_CosyVoiceSiliconflow0002', 'TTS_CosyVoiceSiliconflow', 'CosyVoice濂冲0', 'FunAudioLLM/CosyVoice2-0.5B:bella', '涓枃', NULL, NULL, 6, NULL, NULL, NULL, NULL);
+
+-- CozeCnTTS闊宠壊
+delete from `ai_tts_voice` where id = 'TTS_CozeCnTTS0001';
+INSERT INTO `ai_tts_voice` VALUES ('TTS_CozeCnTTS0001', 'TTS_CozeCnTTS', 'CozeCn闊宠壊', '7426720361733046281', '涓枃', NULL, NULL, 7, NULL, NULL, NULL, NULL);
+
+-- MinimaxTTS闊宠壊
+delete from `ai_tts_voice` where id = 'TTS_MinimaxTTS0001';
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxTTS0001', 'TTS_MinimaxTTS', 'Minimax灏戝コ闊�', 'female-shaonv', '涓枃', NULL, NULL, 8, NULL, NULL, NULL, NULL);
+
+-- AliyunTTS闊宠壊
+delete from `ai_tts_voice` where id = 'TTS_AliyunTTS0001';
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunTTS0001', 'TTS_AliyunTTS', '闃块噷浜戝皬浜�', 'xiaoyun', '涓枃', NULL, NULL, 9, NULL, NULL, NULL, NULL);
+
+-- TTS302AI闊宠壊
+delete from `ai_tts_voice` where id = 'TTS_TTS302AI0001';
+INSERT INTO `ai_tts_voice` VALUES ('TTS_TTS302AI0001', 'TTS_TTS302AI', '婀炬咕灏忎綍', 'zh_female_wanwanxiaohe_moon_bigtts', '涓枃', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E6%B9%BE%E6%B9%BE%E5%B0%8F%E4%BD%95.mp3', NULL, 10, NULL, NULL, NULL, NULL);
+
+-- GizwitsTTS闊宠壊
+delete from `ai_tts_voice` where id = 'TTS_GizwitsTTS0001';
+INSERT INTO `ai_tts_voice` VALUES ('TTS_GizwitsTTS0001', 'TTS_GizwitsTTS', '鏈烘櫤浜戞咕婀�', 'zh_female_wanwanxiaohe_moon_bigtts', '涓枃', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E6%B9%BE%E6%B9%BE%E5%B0%8F%E4%BD%95.mp3', NULL, 11, NULL, NULL, NULL, NULL);
+
+-- ACGNTTS闊宠壊
+delete from `ai_tts_voice` where id = 'TTS_ACGNTTS0001';
+INSERT INTO `ai_tts_voice` VALUES ('TTS_ACGNTTS0001', 'TTS_ACGNTTS', 'ACG闊宠壊', '1695', '涓枃', NULL, NULL, 12, NULL, NULL, NULL, NULL);
+
+-- OpenAITTS闊宠壊
+delete from `ai_tts_voice` where id = 'TTS_OpenAITTS0001';
+INSERT INTO `ai_tts_voice` VALUES ('TTS_OpenAITTS0001', 'TTS_OpenAITTS', 'OpenAI鐢峰0', 'onyx', '涓枃', NULL, NULL, 13, NULL, NULL, NULL, NULL);
+
+-- 娣诲姞鑵捐璇煶鍚堟垚闊宠壊
+delete from `ai_tts_voice` where id = 'TTS_TencentTTS0001';
+INSERT INTO `ai_tts_voice` VALUES ('TTS_TencentTTS0001', 'TTS_TencentTTS', '鏅虹憸', '101001', '涓枃', NULL, NULL, 1, NULL, NULL, NULL, NULL);
+
+-- 鍏朵粬闊宠壊
+delete from `ai_tts_voice` where id = 'TTS_FishSpeech0000';
+INSERT INTO `ai_tts_voice` VALUES ('TTS_FishSpeech0000', 'TTS_FishSpeech', '', '', '涓枃', '', NULL, 8, NULL, NULL, NULL, NULL);
+
+delete from `ai_tts_voice` where id = 'TTS_GPT_SOVITS_V20000';
+INSERT INTO `ai_tts_voice` VALUES ('TTS_GPT_SOVITS_V20000', 'TTS_GPT_SOVITS_V2', '', '', '涓枃', '', NULL, 8, NULL, NULL, NULL, NULL);
+
+delete from `ai_tts_voice` where id in ('TTS_GPT_SOVITS_V30000', 'TTS_CustomTTS0000');
+INSERT INTO `ai_tts_voice` VALUES ('TTS_GPT_SOVITS_V30000', 'TTS_GPT_SOVITS_V3', '', '', '涓枃', '', NULL, 8, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_CustomTTS0000', 'TTS_CustomTTS', '', '', '涓枃', '', NULL, 8, NULL, NULL, NULL, NULL);
+
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504221555.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504221555.sql
new file mode 100644
index 0000000..e80a8a8
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504221555.sql
@@ -0,0 +1,49 @@
+-- 娣诲姞鐏北寮曟搸杈圭紭澶фā鍨嬬綉鍏筹紙LLM + TTS锛�
+INSERT INTO `ai_model_config` VALUES ('LLM_VolcesAiGatewayLLM', 'LLM', 'VolcesAiGatewayTTS', '鐏北寮曟搸杈圭紭澶фā鍨嬬綉鍏�', 0, 1, '{\"type\": \"openai\", \"base_url\": \"https://ai-gateway.vei.volces.com/v1\", \"model_name\": \"doubao-pro-32k-functioncall\", \"api_key\": \"浣犵殑缃戝叧璁块棶瀵嗛挜\"}', NULL, NULL, 14, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_model_config` VALUES ('TTS_VolcesAiGatewayTTS', 'TTS', 'VolcesAiGatewayTTS', '鐏北寮曟搸杈圭紭澶фā鍨嬬綉鍏�', 0, 1, '{\"type\": \"openai\", \"api_key\": \"浣犵殑缃戝叧璁块棶瀵嗛挜\", \"api_url\": \"https://ai-gateway.vei.volces.com/v1/audio/speech\", \"model\": \"doubao-tts\", \"voice\": \"zh_male_shaonianzixin_moon_bigtts\", \"speed\": 1, \"output_dir\": \"tmp/\"}', NULL, NULL, 15, NULL, NULL, NULL, NULL);
+-- 娣诲姞鐏北寮曟搸杈圭紭澶фā鍨嬬綉鍏宠闊冲悎鎴愰煶鑹�
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0001', 'TTS_VolcesAiGatewayTTS', '鐏跨伩/Shiny', 'zh_female_cancan_mars_bigtts', '涓枃銆佺編寮忚嫳璇�', NULL, NULL, 1, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0002', 'TTS_VolcesAiGatewayTTS', '娓呮柊濂冲0', 'zh_female_qingxinnvsheng_mars_bigtts', '涓枃', NULL, NULL, 2, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0003', 'TTS_VolcesAiGatewayTTS', '鐖藉揩鎬濇��/Skye', 'zh_female_shuangkuaisisi_moon_bigtts', '涓枃銆佺編寮忚嫳璇�', NULL, NULL, 3, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0004', 'TTS_VolcesAiGatewayTTS', '娓╂殩闃胯檸/Alvin', 'zh_male_wennuanahu_moon_bigtts', '涓枃銆佺編寮忚嫳璇�', NULL, NULL, 4, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0005', 'TTS_VolcesAiGatewayTTS', '灏戝勾姊撹緵/Brayan', 'zh_male_shaonianzixin_moon_bigtts', '涓枃銆佺編寮忚嫳璇�', NULL, NULL, 5, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0006', 'TTS_VolcesAiGatewayTTS', '鐭ユ�уコ澹�', 'zh_female_zhixingnvsheng_mars_bigtts', '涓枃', NULL, NULL, 6, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0007', 'TTS_VolcesAiGatewayTTS', '娓呯埥鐢峰ぇ', 'zh_male_qingshuangnanda_mars_bigtts', '涓枃', NULL, NULL, 7, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0008', 'TTS_VolcesAiGatewayTTS', '閭诲濂冲', 'zh_female_linjianvhai_moon_bigtts', '涓枃', NULL, NULL, 8, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0009', 'TTS_VolcesAiGatewayTTS', '娓婂崥灏忓彅', 'zh_male_yuanboxiaoshu_moon_bigtts', '涓枃', NULL, NULL, 9, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0010', 'TTS_VolcesAiGatewayTTS', '闃冲厜闈掑勾', 'zh_male_yangguangqingnian_moon_bigtts', '涓枃', NULL, NULL, 10, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0011', 'TTS_VolcesAiGatewayTTS', '鐢滅編灏忔簮', 'zh_female_tianmeixiaoyuan_moon_bigtts', '涓枃', NULL, NULL, 11, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0012', 'TTS_VolcesAiGatewayTTS', '娓呮緢姊撴', 'zh_female_qingchezizi_moon_bigtts', '涓枃', NULL, NULL, 12, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0013', 'TTS_VolcesAiGatewayTTS', '瑙h灏忔槑', 'zh_male_jieshuoxiaoming_moon_bigtts', '涓枃', NULL, NULL, 13, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0014', 'TTS_VolcesAiGatewayTTS', '寮�鏈楀濮�', 'zh_female_kailangjiejie_moon_bigtts', '涓枃', NULL, NULL, 14, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0015', 'TTS_VolcesAiGatewayTTS', '閭诲鐢峰', 'zh_male_linjiananhai_moon_bigtts', '涓枃', NULL, NULL, 15, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0016', 'TTS_VolcesAiGatewayTTS', '鐢滅編鎮︽偊', 'zh_female_tianmeiyueyue_moon_bigtts', '涓枃', NULL, NULL, 16, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0017', 'TTS_VolcesAiGatewayTTS', '蹇冪伒楦℃堡', 'zh_female_xinlingjitang_moon_bigtts', '涓枃', NULL, NULL, 17, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0018', 'TTS_VolcesAiGatewayTTS', '鐭ユ�ф俯濠�', 'ICL_zh_female_zhixingwenwan_tob', '涓枃', NULL, NULL, 18, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0019', 'TTS_VolcesAiGatewayTTS', '鏆栧績浣撹创', 'ICL_zh_male_nuanxintitie_tob', '涓枃', NULL, NULL, 19, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0020', 'TTS_VolcesAiGatewayTTS', '娓╂煍鏂囬泤', 'ICL_zh_female_wenrouwenya_tob', '涓枃', NULL, NULL, 20, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0021', 'TTS_VolcesAiGatewayTTS', '寮�鏈楄交蹇�', 'ICL_zh_male_kailangqingkuai_tob', '涓枃', NULL, NULL, 21, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0022', 'TTS_VolcesAiGatewayTTS', '娲绘臣鐖芥湕', 'ICL_zh_male_huoposhuanglang_tob', '涓枃', NULL, NULL, 22, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0023', 'TTS_VolcesAiGatewayTTS', '鐜囩湡灏忎紮', 'ICL_zh_male_shuaizhenxiaohuo_tob', '涓枃', NULL, NULL, 23, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0024', 'TTS_VolcesAiGatewayTTS', '娓╂煍灏忓摜', 'zh_male_wenrouxiaoge_mars_bigtts', '涓枃', NULL, NULL, 24, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0025', 'TTS_VolcesAiGatewayTTS', 'Smith', 'en_male_smith_mars_bigtts', '鑻卞紡鑻辫', NULL, NULL, 25, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0026', 'TTS_VolcesAiGatewayTTS', 'Anna', 'en_female_anna_mars_bigtts', '鑻卞紡鑻辫', NULL, NULL, 26, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0027', 'TTS_VolcesAiGatewayTTS', 'Adam', 'en_male_adam_mars_bigtts', '缇庡紡鑻辫', NULL, NULL, 27, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0028', 'TTS_VolcesAiGatewayTTS', 'Sarah', 'en_female_sarah_mars_bigtts', '婢虫床鑻辫', NULL, NULL, 28, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0029', 'TTS_VolcesAiGatewayTTS', 'Dryw', 'en_male_dryw_mars_bigtts', '婢虫床鑻辫', NULL, NULL, 29, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0030', 'TTS_VolcesAiGatewayTTS', '銇嬨仛銇紙鍜岄煶锛�', 'multi_male_jingqiangkanye_moon_bigtts', '鏃ヨ銆佽タ璇�', NULL, NULL, 30, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0031', 'TTS_VolcesAiGatewayTTS', '銇倠銇擄紙鏅村瓙锛�', 'multi_female_shuangkuaisisi_moon_bigtts', '鏃ヨ銆佽タ璇�', NULL, NULL, 31, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0032', 'TTS_VolcesAiGatewayTTS', '銇层倣銇楋紙搴冨織锛�', 'multi_male_wanqudashu_moon_bigtts', '鏃ヨ銆佽タ璇�', NULL, NULL, 32, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0033', 'TTS_VolcesAiGatewayTTS', '銇傘亼銇匡紙鏈辩編锛�', 'multi_female_gaolengyujie_moon_bigtts', '鏃ヨ', NULL, NULL, 33, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0034', 'TTS_VolcesAiGatewayTTS', 'Amanda', 'en_female_amanda_mars_bigtts', '缇庡紡鑻辫', NULL, NULL, 34, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0035', 'TTS_VolcesAiGatewayTTS', 'Jackson', 'en_male_jackson_mars_bigtts', '缇庡紡鑻辫', NULL, NULL, 35, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0036', 'TTS_VolcesAiGatewayTTS', '浜厰渚冪埛/Harmony', 'zh_male_jingqiangkanye_moon_bigtts', '涓枃-鍖椾含鍙i煶銆佽嫳鏂�', NULL, NULL, 36, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0037', 'TTS_VolcesAiGatewayTTS', '婀炬咕灏忎綍', 'zh_female_wanwanxiaohe_moon_bigtts', '涓枃-鍙版咕鍙i煶', NULL, NULL, 37, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0038', 'TTS_VolcesAiGatewayTTS', '婀惧尯澶у彅', 'zh_female_wanqudashu_moon_bigtts', '涓枃-骞夸笢鍙i煶', NULL, NULL, 38, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0039', 'TTS_VolcesAiGatewayTTS', '鍛嗚悓宸濆', 'zh_female_daimengchuanmei_moon_bigtts', '涓枃-鍥涘窛鍙i煶', NULL, NULL, 39, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0040', 'TTS_VolcesAiGatewayTTS', '骞垮窞寰峰摜', 'zh_male_guozhoudege_moon_bigtts', '涓枃-骞夸笢鍙i煶', NULL, NULL, 40, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0041', 'TTS_VolcesAiGatewayTTS', '鍖椾含灏忕埛', 'zh_male_beijingxiaoye_moon_bigtts', '涓枃-鍖椾含鍙i煶', NULL, NULL, 41, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0042', 'TTS_VolcesAiGatewayTTS', '娴╁畤灏忓摜', 'zh_male_haoyuxiaoge_moon_bigtts', '涓枃-闈掑矝鍙i煶', NULL, NULL, 42, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0043', 'TTS_VolcesAiGatewayTTS', '骞胯タ杩滆垷', 'zh_male_guangxiyuanzhou_moon_bigtts', '涓枃-骞胯タ鍙i煶', NULL, NULL, 43, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0044', 'TTS_VolcesAiGatewayTTS', '濡瑰潹娲佸効', 'zh_female_meituojieer_moon_bigtts', '涓枃-闀挎矙鍙i煶', NULL, NULL, 44, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_VolcesAiGatewayTTS_0045', 'TTS_VolcesAiGatewayTTS', '璞窞瀛愯僵', 'zh_male_yuzhouzixuan_moon_bigtts', '涓枃-娌冲崡鍙i煶', NULL, NULL, 45, NULL, NULL, NULL, NULL);
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504251422.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504251422.sql
new file mode 100644
index 0000000..88e03fe
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504251422.sql
@@ -0,0 +1,30 @@
+-- 澧炲姞server.ota锛岀敤浜庨厤缃畂ta鍦板潃
+
+delete from `sys_params` where id = 100;
+delete from `sys_params` where id = 101;
+
+delete from `sys_params` where id = 106;
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (106, 'server.websocket', 'null', 'string', 1, 'websocket鍦板潃锛屽涓敤;鍒嗛殧');
+
+delete from `sys_params` where id = 107;
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (107, 'server.ota', 'null', 'string', 1, 'ota鍦板潃');
+
+
+-- 澧炲姞鍥轰欢淇℃伅琛�
+CREATE TABLE IF NOT EXISTS `ai_ota` (
+ `id` varchar(32) NOT NULL COMMENT 'ID',
+ `firmware_name` varchar(100) DEFAULT NULL COMMENT '鍥轰欢鍚嶇О',
+ `type` varchar(50) DEFAULT NULL COMMENT '鍥轰欢绫诲瀷',
+ `version` varchar(50) DEFAULT NULL COMMENT '鐗堟湰鍙�',
+ `size` bigint DEFAULT NULL COMMENT '鏂囦欢澶у皬(瀛楄妭)',
+ `remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞/璇存槑',
+ `firmware_path` varchar(255) DEFAULT NULL COMMENT '鍥轰欢璺緞',
+ `sort` int unsigned DEFAULT '0' COMMENT '鎺掑簭',
+ `updater` bigint DEFAULT NULL COMMENT '鏇存柊鑰�',
+ `update_date` datetime DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+ `creator` bigint DEFAULT NULL COMMENT '鍒涘缓鑰�',
+ `create_date` datetime DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鍥轰欢淇℃伅琛�';
+
+update ai_device set auto_update = 1;
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504291043.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504291043.sql
new file mode 100644
index 0000000..555b8cf
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504291043.sql
@@ -0,0 +1,504 @@
+-- 澧炲姞FunASR鏈嶅姟璇煶璇嗗埆妯″瀷渚涘簲鍣ㄥ拰閰嶇疆
+DELETE FROM `ai_model_provider` WHERE `id` = 'SYSTEM_ASR_FunASRServer';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_ASR_FunASRServer', 'ASR', 'fun_server', 'FunASR鏈嶅姟璇煶璇嗗埆', '[{"key":"host","label":"鏈嶅姟鍦板潃","type":"string"},{"key":"port","label":"绔彛鍙�","type":"number"}]', 4, 1, NOW(), 1, NOW());
+
+DELETE FROM `ai_model_config` WHERE `id` = 'ASR_FunASRServer';
+INSERT INTO `ai_model_config` VALUES ('ASR_FunASRServer', 'ASR', 'FunASRServer', 'FunASR鏈嶅姟璇煶璇嗗埆', 0, 1, '{\"type\": \"fun_server\", \"host\": \"127.0.0.1\", \"port\": 10096}', NULL, NULL, 5, NULL, NULL, NULL, NULL);
+
+-- 淇敼ai_model_config琛ㄧ殑remark瀛楁绫诲瀷涓篢EXT
+ALTER TABLE `ai_model_config` MODIFY COLUMN `remark` TEXT COMMENT '澶囨敞';
+
+-- 鏇存柊ASR妯″瀷閰嶇疆鐨勮鏄庢枃妗�
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://github.com/modelscope/FunASR/blob/main/runtime/docs/SDK_advanced_guide_online_zh.md',
+`remark` = '鐙珛閮ㄧ讲FunASR锛屼娇鐢‵unASR鐨凙PI鏈嶅姟锛屽彧闇�瑕佷簲鍙ヨ瘽
+绗竴鍙ワ細mkdir -p ./funasr-runtime-resources/models
+绗簩鍙ワ細sudo docker run -d -p 10096:10095 --privileged=true -v $PWD/funasr-runtime-resources/models:/workspace/models registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr:funasr-runtime-sdk-online-cpu-0.1.12
+涓婁竴鍙ヨ瘽鎵ц鍚庝細杩涘叆鍒板鍣紝缁х画绗笁鍙ワ細cd FunASR/runtime
+涓嶈閫�鍑哄鍣紝缁х画鍦ㄥ鍣ㄤ腑鎵ц绗洓鍙ワ細nohup bash run_server_2pass.sh --download-model-dir /workspace/models --vad-dir damo/speech_fsmn_vad_zh-cn-16k-common-onnx --model-dir damo/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-onnx --online-model-dir damo/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online-onnx --punc-dir damo/punc_ct-transformer_zh-cn-common-vad_realtime-vocab272727-onnx --lm-dir damo/speech_ngram_lm_zh-cn-ai-wesp-fst --itn-dir thuduj12/fst_itn_zh --hotword /workspace/models/hotwords.txt > log.txt 2>&1 &
+涓婁竴鍙ヨ瘽鎵ц鍚庝細杩涘叆鍒板鍣紝缁х画绗簲鍙ワ細tail -f log.txt
+绗簲鍙ヨ瘽鎵ц瀹屽悗锛屼細鐪嬪埌妯″瀷涓嬭浇鏃ュ織锛屼笅杞藉畬鍚庡氨鍙互杩炴帴浣跨敤浜�
+浠ヤ笂鏄娇鐢–PU鎺ㄧ悊锛屽鏋滄湁GPU锛岃缁嗗弬鑰冿細https://github.com/modelscope/FunASR/blob/main/runtime/docs/SDK_advanced_guide_online_zh.md' WHERE `id` = 'ASR_FunASRServer';
+
+-- 鏇存柊FunASR鏈湴妯″瀷閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://github.com/modelscope/FunASR',
+`remark` = 'FunASR鏈湴妯″瀷閰嶇疆璇存槑锛�
+1. 闇�瑕佷笅杞芥ā鍨嬫枃浠跺埌xiaozhi-server/models/SenseVoiceSmall鐩綍
+2. 鏀寔涓棩闊╃菠璇煶璇嗗埆
+3. 鏈湴鎺ㄧ悊锛屾棤闇�缃戠粶杩炴帴
+4. 寰呰瘑鍒枃浠朵繚瀛樺湪tmp/鐩綍' WHERE `id` = 'ASR_FunASR';
+
+-- 鏇存柊SherpaASR閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://github.com/k2-fsa/sherpa-onnx',
+`remark` = 'SherpaASR閰嶇疆璇存槑锛�
+1. 杩愯鏃惰嚜鍔ㄤ笅杞芥ā鍨嬫枃浠跺埌models/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17鐩綍
+2. 鏀寔涓枃銆佽嫳鏂囥�佹棩璇�侀煩璇�佺菠璇瓑澶氱璇█
+3. 鏈湴鎺ㄧ悊锛屾棤闇�缃戠粶杩炴帴
+4. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍' WHERE `id` = 'ASR_SherpaASR';
+
+-- 鏇存柊璞嗗寘ASR閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://console.volcengine.com/speech/app',
+`remark` = '璞嗗寘ASR閰嶇疆璇存槑锛�
+1. 闇�瑕佸湪鐏北寮曟搸鎺у埗鍙板垱寤哄簲鐢ㄥ苟鑾峰彇appid鍜宎ccess_token
+2. 鏀寔涓枃璇煶璇嗗埆
+3. 闇�瑕佺綉缁滆繛鎺�
+4. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍
+鐢宠姝ラ锛�
+1. 璁块棶 https://console.volcengine.com/speech/app
+2. 鍒涘缓鏂板簲鐢�
+3. 鑾峰彇appid鍜宎ccess_token
+4. 濉叆閰嶇疆鏂囦欢涓�' WHERE `id` = 'ASR_DoubaoASR';
+
+-- 鏇存柊鑵捐ASR閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://console.cloud.tencent.com/cam/capi',
+`remark` = '鑵捐ASR閰嶇疆璇存槑锛�
+1. 闇�瑕佸湪鑵捐浜戞帶鍒跺彴鍒涘缓搴旂敤骞惰幏鍙朼ppid銆乻ecret_id鍜宻ecret_key
+2. 鏀寔涓枃璇煶璇嗗埆
+3. 闇�瑕佺綉缁滆繛鎺�
+4. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍
+鐢宠姝ラ锛�
+1. 璁块棶 https://console.cloud.tencent.com/cam/capi 鑾峰彇瀵嗛挜
+2. 璁块棶 https://console.cloud.tencent.com/asr/resourcebundle 棰嗗彇鍏嶈垂璧勬簮
+3. 鑾峰彇appid銆乻ecret_id鍜宻ecret_key
+4. 濉叆閰嶇疆鏂囦欢涓�' WHERE `id` = 'ASR_TencentASR';
+
+-- 鏇存柊TTS妯″瀷閰嶇疆璇存槑
+-- EdgeTTS閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://github.com/rany2/edge-tts',
+`remark` = 'EdgeTTS閰嶇疆璇存槑锛�
+1. 浣跨敤寰蒋Edge TTS鏈嶅姟
+2. 鏀寔澶氱璇█鍜岄煶鑹�
+3. 鍏嶈垂浣跨敤锛屾棤闇�娉ㄥ唽
+4. 闇�瑕佺綉缁滆繛鎺�
+5. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍' WHERE `id` = 'TTS_EdgeTTS';
+
+-- 璞嗗寘TTS閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://console.volcengine.com/speech/service/8',
+`remark` = '璞嗗寘TTS閰嶇疆璇存槑锛�
+1. 璁块棶 https://console.volcengine.com/speech/service/8
+2. 闇�瑕佸湪鐏北寮曟搸鎺у埗鍙板垱寤哄簲鐢ㄥ苟鑾峰彇appid鍜宎ccess_token
+3. 灞卞紩鎿庤闊充竴瀹氳璐拱鑺遍挶锛岃捣姝ヤ环30鍏冿紝灏辨湁100骞跺彂浜嗐�傚鏋滅敤鍏嶈垂鐨勫彧鏈�2涓苟鍙戯紝浼氱粡甯告姤tts閿欒
+4. 璐拱鏈嶅姟鍚庯紝璐拱鍏嶈垂鐨勯煶鑹插悗锛屽彲鑳借绛夊崐灏忔椂宸﹀彸锛屾墠鑳戒娇鐢ㄣ��
+5. 濉叆閰嶇疆鏂囦欢涓�' WHERE `id` = 'TTS_DoubaoTTS';
+
+-- 纭呭熀娴佸姩TTS閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://cloud.siliconflow.cn/account/ak',
+`remark` = '纭呭熀娴佸姩TTS閰嶇疆璇存槑锛�
+1. 璁块棶 https://cloud.siliconflow.cn/account/ak
+2. 娉ㄥ唽骞惰幏鍙朅PI瀵嗛挜
+3. 濉叆閰嶇疆鏂囦欢涓�' WHERE `id` = 'TTS_CosyVoiceSiliconflow';
+
+-- Coze涓枃TTS閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://www.coze.cn/open/oauth/pats',
+`remark` = 'Coze涓枃TTS閰嶇疆璇存槑锛�
+1. 璁块棶 https://www.coze.cn/open/oauth/pats
+2. 鑾峰彇涓汉浠ょ墝
+3. 濉叆閰嶇疆鏂囦欢涓�' WHERE `id` = 'TTS_CozeCnTTS';
+
+-- FishSpeech閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://github.com/fishaudio/fish-speech',
+`remark` = 'FishSpeech閰嶇疆璇存槑锛�
+1. 闇�瑕佹湰鍦伴儴缃睩ishSpeech鏈嶅姟
+2. 鏀寔鑷畾涔夐煶鑹�
+3. 鏈湴鎺ㄧ悊锛屾棤闇�缃戠粶杩炴帴
+4. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍
+5. 杩愯鏈嶅姟绀轰緥鍛戒护锛歱ython -m tools.api_server --listen 0.0.0.0:8080 --llama-checkpoint-path "checkpoints/fish-speech-1.5" --decoder-checkpoint-path "checkpoints/fish-speech-1.5/firefly-gan-vq-fsq-8x1024-21hz-generator.pth" --decoder-config-name firefly_gan_vq --compile' WHERE `id` = 'TTS_FishSpeech';
+
+-- GPT-SoVITS V2閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://github.com/RVC-Boss/GPT-SoVITS',
+`remark` = 'GPT-SoVITS V2閰嶇疆璇存槑锛�
+1. 闇�瑕佹湰鍦伴儴缃睪PT-SoVITS鏈嶅姟
+2. 鏀寔鑷畾涔夐煶鑹插厠闅�
+3. 鏈湴鎺ㄧ悊锛屾棤闇�缃戠粶杩炴帴
+4. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍
+閮ㄧ讲姝ラ锛�
+1. 杩愯鏈嶅姟绀轰緥鍛戒护锛歱ython api_v2.py -a 127.0.0.1 -p 9880 -c GPT_SoVITS/configs/demo.yaml' WHERE `id` = 'TTS_GPT_SOVITS_V2';
+
+-- GPT-SoVITS V3閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://github.com/RVC-Boss/GPT-SoVITS',
+`remark` = 'GPT-SoVITS V3閰嶇疆璇存槑锛�
+1. 闇�瑕佹湰鍦伴儴缃睪PT-SoVITS V3鏈嶅姟
+2. 鏀寔鑷畾涔夐煶鑹插厠闅�
+3. 鏈湴鎺ㄧ悊锛屾棤闇�缃戠粶杩炴帴
+4. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍' WHERE `id` = 'TTS_GPT_SOVITS_V3';
+
+-- MiniMax TTS閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://platform.minimaxi.com/',
+`remark` = 'MiniMax TTS閰嶇疆璇存槑锛�
+1. 闇�瑕佸湪MiniMax骞冲彴鍒涘缓璐︽埛骞跺厖鍊�
+2. 鏀寔澶氱闊宠壊锛屽綋鍓嶉厤缃娇鐢╢emale-shaonv
+3. 闇�瑕佺綉缁滆繛鎺�
+4. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍
+鐢宠姝ラ锛�
+1. 璁块棶 https://platform.minimaxi.com/ 娉ㄥ唽璐﹀彿
+2. 璁块棶 https://platform.minimaxi.com/user-center/payment/balance 鍏呭��
+3. 璁块棶 https://platform.minimaxi.com/user-center/basic-information 鑾峰彇group_id
+4. 璁块棶 https://platform.minimaxi.com/user-center/basic-information/interface-key 鑾峰彇api_key
+5. 濉叆閰嶇疆鏂囦欢涓�' WHERE `id` = 'TTS_MinimaxTTS';
+
+-- 闃块噷浜慣TS閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://nls-portal.console.aliyun.com/',
+`remark` = '闃块噷浜慣TS閰嶇疆璇存槑锛�
+1. 闇�瑕佸湪闃块噷浜戝钩鍙板紑閫氭櫤鑳借闊充氦浜掓湇鍔�
+2. 鏀寔澶氱闊宠壊锛屽綋鍓嶉厤缃娇鐢▁iaoyun
+3. 闇�瑕佺綉缁滆繛鎺�
+4. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍
+鐢宠姝ラ锛�
+1. 璁块棶 https://nls-portal.console.aliyun.com/ 寮�閫氭湇鍔�
+2. 璁块棶 https://nls-portal.console.aliyun.com/applist 鑾峰彇appkey
+3. 璁块棶 https://nls-portal.console.aliyun.com/overview 鑾峰彇token
+4. 濉叆閰嶇疆鏂囦欢涓�
+娉ㄦ剰锛歵oken鏄复鏃剁殑24灏忔椂鏈夋晥锛岄暱鏈熶娇鐢ㄩ渶瑕侀厤缃產ccess_key_id鍜宎ccess_key_secret' WHERE `id` = 'TTS_AliyunTTS';
+
+-- 鑵捐TTS閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://console.cloud.tencent.com/cam/capi',
+`remark` = '鑵捐TTS閰嶇疆璇存槑锛�
+1. 闇�瑕佸湪鑵捐浜戝钩鍙板紑閫氭櫤鑳借闊充氦浜掓湇鍔�
+2. 鏀寔澶氱闊宠壊锛屽綋鍓嶉厤缃娇鐢�101001
+3. 闇�瑕佺綉缁滆繛鎺�
+4. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍
+鐢宠姝ラ锛�
+1. 璁块棶 https://console.cloud.tencent.com/cam/capi 鑾峰彇瀵嗛挜
+2. 璁块棶 https://console.cloud.tencent.com/tts/resourcebundle 棰嗗彇鍏嶈垂璧勬簮
+3. 鍒涘缓鏂板簲鐢�
+4. 鑾峰彇appid銆乻ecret_id鍜宻ecret_key
+5. 濉叆閰嶇疆鏂囦欢涓�' WHERE `id` = 'TTS_TencentTTS';
+
+-- 302AI TTS閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://dash.302.ai/',
+`remark` = '302AI TTS閰嶇疆璇存槑锛�
+1. 闇�瑕佸湪302骞冲彴鍒涘缓璐︽埛骞惰幏鍙朅PI瀵嗛挜
+2. 鏀寔澶氱闊宠壊锛屽綋鍓嶉厤缃娇鐢ㄦ咕婀惧皬浣曢煶鑹�
+3. 闇�瑕佺綉缁滆繛鎺�
+4. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍
+鐢宠姝ラ锛�
+1. 璁块棶 https://dash.302.ai/ 娉ㄥ唽璐﹀彿
+2. 璁块棶 https://dash.302.ai/apis/list 鑾峰彇API瀵嗛挜
+3. 濉叆閰嶇疆鏂囦欢涓�
+浠锋牸锛�$35/鐧句竾瀛楃' WHERE `id` = 'TTS_TTS302AI';
+
+-- 鏈烘櫤浜慣TS閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://agentrouter.gizwitsapi.com/panel/token',
+`remark` = '鏈烘櫤浜慣TS閰嶇疆璇存槑锛�
+1. 闇�瑕佸湪鏈烘櫤浜戝钩鍙拌幏鍙朅PI瀵嗛挜
+2. 鏀寔澶氱闊宠壊锛屽綋鍓嶉厤缃娇鐢ㄦ咕婀惧皬浣曢煶鑹�
+3. 闇�瑕佺綉缁滆繛鎺�
+4. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍
+鐢宠姝ラ锛�
+1. 璁块棶 https://agentrouter.gizwitsapi.com/panel/token 鑾峰彇API瀵嗛挜
+2. 濉叆閰嶇疆鏂囦欢涓�
+娉ㄦ剰锛氬墠涓�涓囧悕娉ㄥ唽鐨勭敤鎴凤紝灏嗛��5鍏冧綋楠岄噾棰�' WHERE `id` = 'TTS_GizwitsTTS';
+
+-- ACGN TTS閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://acgn.ttson.cn/',
+`remark` = 'ACGN TTS閰嶇疆璇存槑锛�
+1. 闇�瑕佸湪ttson骞冲彴璐拱token
+2. 鏀寔澶氱瑙掕壊闊宠壊锛屽綋鍓嶉厤缃娇鐢ㄨ鑹睮D锛�1695
+3. 闇�瑕佺綉缁滆繛鎺�
+4. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍
+鐢宠姝ラ锛�
+1. 璁块棶 https://acgn.ttson.cn/ 鏌ョ湅瑙掕壊鍒楄〃
+2. 璁块棶 www.ttson.cn 璐拱token
+3. 濉叆閰嶇疆鏂囦欢涓�
+寮�鍙戠浉鍏崇枒闂鎻愪氦鑷崇綉绔欎笂鐨剄q' WHERE `id` = 'TTS_ACGNTTS';
+
+-- OpenAI TTS閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://platform.openai.com/api-keys',
+`remark` = 'OpenAI TTS閰嶇疆璇存槑锛�
+1. 闇�瑕佸湪OpenAI骞冲彴鑾峰彇API瀵嗛挜
+2. 鏀寔澶氱闊宠壊锛屽綋鍓嶉厤缃娇鐢╫nyx
+3. 闇�瑕佺綉缁滆繛鎺�
+4. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍
+鐢宠姝ラ锛�
+1. 璁块棶 https://platform.openai.com/api-keys 鑾峰彇API瀵嗛挜
+2. 濉叆閰嶇疆鏂囦欢涓�
+娉ㄦ剰锛氬浗鍐呴渶瑕佷娇鐢ㄤ唬鐞嗚闂�' WHERE `id` = 'TTS_OpenAITTS';
+
+-- 鑷畾涔塗TS閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = NULL,
+`remark` = '鑷畾涔塗TS閰嶇疆璇存槑锛�
+1. 鏀寔鑷畾涔塗TS鎺ュ彛鏈嶅姟
+2. 浣跨敤GET鏂瑰紡璇锋眰
+3. 闇�瑕佺綉缁滆繛鎺�
+4. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍
+閰嶇疆璇存槑锛�
+1. 鍦╬arams涓厤缃姹傚弬鏁�
+2. 鍦╤eaders涓厤缃姹傚ご
+3. 璁剧疆杩斿洖闊抽鏍煎紡' WHERE `id` = 'TTS_CustomTTS';
+
+-- 鐏北寮曟搸杈圭紭澶фā鍨嬬綉鍏砊TS閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://console.volcengine.com/vei/aigateway/',
+`remark` = '鐏北寮曟搸杈圭紭澶фā鍨嬬綉鍏砊TS閰嶇疆璇存槑锛�
+1. 璁块棶 https://console.volcengine.com/vei/aigateway/
+2. 鍒涘缓缃戝叧璁块棶瀵嗛挜锛屾悳绱㈠苟鍕鹃�� Doubao-璇煶鍚堟垚
+3. 濡傛灉闇�瑕佷娇鐢↙LM锛屼竴骞跺嬀閫� Doubao-pro-32k-functioncall
+4. 璁块棶 https://console.volcengine.com/vei/aigateway/tokens-list 鑾峰彇瀵嗛挜
+5. 濉叆閰嶇疆鏂囦欢涓�
+闊宠壊鍒楄〃鍙傝�冿細https://www.volcengine.com/docs/6561/1257544' WHERE `id` = 'TTS_VolcesAiGatewayTTS';
+
+-- 鏇存柊LLM妯″瀷閰嶇疆璇存槑
+-- ChatGLM閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://bigmodel.cn/usercenter/proj-mgmt/apikeys',
+`remark` = 'ChatGLM閰嶇疆璇存槑锛�
+1. 璁块棶 https://bigmodel.cn/usercenter/proj-mgmt/apikeys
+2. 娉ㄥ唽骞惰幏鍙朅PI瀵嗛挜
+3. 濉叆閰嶇疆鏂囦欢涓�' WHERE `id` = 'LLM_ChatGLMLLM';
+
+-- Ollama閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://ollama.com/',
+`remark` = 'Ollama閰嶇疆璇存槑锛�
+1. 瀹夎Ollama鏈嶅姟
+2. 杩愯鍛戒护锛歰llama pull qwen2.5
+3. 纭繚鏈嶅姟杩愯鍦╤ttp://localhost:11434' WHERE `id` = 'LLM_OllamaLLM';
+
+-- 閫氫箟鍗冮棶閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://bailian.console.aliyun.com/?apiKey=1#/api-key',
+`remark` = '閫氫箟鍗冮棶閰嶇疆璇存槑锛�
+1. 璁块棶 https://bailian.console.aliyun.com/?apiKey=1#/api-key
+2. 鑾峰彇API瀵嗛挜
+3. 濉叆閰嶇疆鏂囦欢涓紝褰撳墠閰嶇疆浣跨敤qwen-turbo妯″瀷
+4. 鏀寔鑷畾涔夊弬鏁帮細temperature=0.7, max_tokens=500, top_p=1, top_k=50' WHERE `id` = 'LLM_AliLLM';
+
+-- 閫氫箟鐧剧偧閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://bailian.console.aliyun.com/?apiKey=1#/api-key',
+`remark` = '閫氫箟鐧剧偧閰嶇疆璇存槑锛�
+1. 璁块棶 https://bailian.console.aliyun.com/?apiKey=1#/api-key
+2. 鑾峰彇app_id鍜宎pi_key
+3. 濉叆閰嶇疆鏂囦欢涓�' WHERE `id` = 'LLM_AliAppLLM';
+
+-- 璞嗗寘澶фā鍨嬮厤缃鏄�
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://console.volcengine.com/ark/region:ark+cn-beijing/openManagement',
+`remark` = '璞嗗寘澶фā鍨嬮厤缃鏄庯細
+1. 璁块棶 https://console.volcengine.com/ark/region:ark+cn-beijing/openManagement
+2. 寮�閫欴oubao-1.5-pro鏈嶅姟
+3. 璁块棶 https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey 鑾峰彇API瀵嗛挜
+4. 濉叆閰嶇疆鏂囦欢涓�
+5. 褰撳墠寤鸿浣跨敤doubao-1-5-pro-32k-250115
+娉ㄦ剰锛氭湁鍏嶈垂棰濆害500000token' WHERE `id` = 'LLM_DoubaoLLM';
+
+-- DeepSeek閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://platform.deepseek.com/',
+`remark` = 'DeepSeek閰嶇疆璇存槑锛�
+1. 璁块棶 https://platform.deepseek.com/
+2. 娉ㄥ唽骞惰幏鍙朅PI瀵嗛挜
+3. 濉叆閰嶇疆鏂囦欢涓�' WHERE `id` = 'LLM_DeepSeekLLM';
+
+-- Dify閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://cloud.dify.ai/',
+`remark` = 'Dify閰嶇疆璇存槑锛�
+1. 璁块棶 https://cloud.dify.ai/
+2. 娉ㄥ唽骞惰幏鍙朅PI瀵嗛挜
+3. 濉叆閰嶇疆鏂囦欢涓�
+4. 鏀寔澶氱瀵硅瘽妯″紡锛歸orkflows/run, chat-messages, completion-messages
+5. 骞冲彴璁剧疆鐨勮鑹插畾涔変細澶辨晥锛岄渶瑕佸湪Dify鎺у埗鍙拌缃�
+娉ㄦ剰锛氬缓璁娇鐢ㄦ湰鍦伴儴缃茬殑Dify鎺ュ彛锛屽浗鍐呴儴鍒嗗尯鍩熻闂叕鏈変簯鎺ュ彛鍙兘鍙楅檺' WHERE `id` = 'LLM_DifyLLM';
+
+-- Gemini閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://aistudio.google.com/apikey',
+`remark` = 'Gemini閰嶇疆璇存槑锛�
+1. 浣跨敤璋锋瓕Gemini API鏈嶅姟
+2. 褰撳墠閰嶇疆浣跨敤gemini-2.0-flash妯″瀷
+3. 闇�瑕佺綉缁滆繛鎺�
+4. 鏀寔閰嶇疆浠g悊
+鐢宠姝ラ锛�
+1. 璁块棶 https://aistudio.google.com/apikey
+2. 鍒涘缓API瀵嗛挜
+3. 濉叆閰嶇疆鏂囦欢涓�
+娉ㄦ剰锛氳嫢鍦ㄤ腑鍥藉鍐呬娇鐢紝璇烽伒瀹堛�婄敓鎴愬紡浜哄伐鏅鸿兘鏈嶅姟绠$悊鏆傝鍔炴硶銆�' WHERE `id` = 'LLM_GeminiLLM';
+
+-- Coze閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://www.coze.cn/open/oauth/pats',
+`remark` = 'Coze閰嶇疆璇存槑锛�
+1. 浣跨敤Coze骞冲彴鏈嶅姟
+2. 闇�瑕乥ot_id銆乽ser_id鍜屼釜浜轰护鐗�
+3. 闇�瑕佺綉缁滆繛鎺�
+鐢宠姝ラ锛�
+1. 璁块棶 https://www.coze.cn/open/oauth/pats
+2. 鑾峰彇涓汉浠ょ墝
+3. 鎵嬪姩璁$畻bot_id鍜寀ser_id
+4. 濉叆閰嶇疆鏂囦欢涓�' WHERE `id` = 'LLM_CozeLLM';
+
+-- LM Studio閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://lmstudio.ai/',
+`remark` = 'LM Studio閰嶇疆璇存槑锛�
+1. 浣跨敤鏈湴閮ㄧ讲鐨凩M Studio鏈嶅姟
+2. 褰撳墠閰嶇疆浣跨敤deepseek-r1-distill-llama-8b@q4_k_m妯″瀷
+3. 鏈湴鎺ㄧ悊锛屾棤闇�缃戠粶杩炴帴
+4. 闇�瑕侀鍏堜笅杞芥ā鍨�
+閮ㄧ讲姝ラ锛�
+1. 瀹夎LM Studio
+2. 浠庣ぞ鍖轰笅杞芥ā鍨�
+3. 纭繚鏈嶅姟杩愯鍦╤ttp://localhost:1234/v1' WHERE `id` = 'LLM_LMStudioLLM';
+
+-- FastGPT閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://cloud.tryfastgpt.ai/account/apikey',
+`remark` = 'FastGPT閰嶇疆璇存槑锛�
+1. 浣跨敤FastGPT骞冲彴鏈嶅姟
+2. 闇�瑕佺綉缁滆繛鎺�
+3. 閰嶇疆鏂囦欢涓殑prompt鏃犳晥锛岄渶瑕佸湪FastGPT鎺у埗鍙拌缃�
+4. 鏀寔鑷畾涔夊彉閲�
+鐢宠姝ラ锛�
+1. 璁块棶 https://cloud.tryfastgpt.ai/account/apikey
+2. 鑾峰彇API瀵嗛挜
+3. 濉叆閰嶇疆鏂囦欢涓�' WHERE `id` = 'LLM_FastgptLLM';
+
+-- Xinference閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://github.com/xorbitsai/inference',
+`remark` = 'Xinference閰嶇疆璇存槑锛�
+1. 浣跨敤鏈湴閮ㄧ讲鐨刋inference鏈嶅姟
+2. 褰撳墠閰嶇疆浣跨敤qwen2.5:72b-AWQ妯″瀷
+3. 鏈湴鎺ㄧ悊锛屾棤闇�缃戠粶杩炴帴
+4. 闇�瑕侀鍏堝惎鍔ㄥ搴旀ā鍨�
+閮ㄧ讲姝ラ锛�
+1. 瀹夎Xinference
+2. 鍚姩鏈嶅姟骞跺姞杞芥ā鍨�
+3. 纭繚鏈嶅姟杩愯鍦╤ttp://localhost:9997' WHERE `id` = 'LLM_XinferenceLLM';
+
+-- Xinference灏忔ā鍨嬮厤缃鏄�
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://github.com/xorbitsai/inference',
+`remark` = 'Xinference灏忔ā鍨嬮厤缃鏄庯細
+1. 浣跨敤鏈湴閮ㄧ讲鐨刋inference鏈嶅姟
+2. 褰撳墠閰嶇疆浣跨敤qwen2.5:3b-AWQ妯″瀷
+3. 鏈湴鎺ㄧ悊锛屾棤闇�缃戠粶杩炴帴
+4. 鐢ㄤ簬鎰忓浘璇嗗埆
+閮ㄧ讲姝ラ锛�
+1. 瀹夎Xinference
+2. 鍚姩鏈嶅姟骞跺姞杞芥ā鍨�
+3. 纭繚鏈嶅姟杩愯鍦╤ttp://localhost:9997' WHERE `id` = 'LLM_XinferenceSmallLLM';
+
+-- 鐏北寮曟搸杈圭紭澶фā鍨嬬綉鍏矻LM閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://console.volcengine.com/vei/aigateway/',
+`remark` = '鐏北寮曟搸杈圭紭澶фā鍨嬬綉鍏矻LM閰嶇疆璇存槑锛�
+1. 浣跨敤鐏北寮曟搸杈圭紭澶фā鍨嬬綉鍏虫湇鍔�
+2. 闇�瑕佺綉鍏宠闂瘑閽�
+3. 闇�瑕佺綉缁滆繛鎺�
+4. 鏀寔function_call鍔熻兘
+鐢宠姝ラ锛�
+1. 璁块棶 https://console.volcengine.com/vei/aigateway/
+2. 鍒涘缓缃戝叧璁块棶瀵嗛挜锛屾悳绱㈠苟鍕鹃�� Doubao-pro-32k-functioncall
+3. 濡傛灉闇�瑕佷娇鐢ㄨ闊冲悎鎴愶紝涓�骞跺嬀閫� Doubao-璇煶鍚堟垚
+4. 璁块棶 https://console.volcengine.com/vei/aigateway/tokens-list 鑾峰彇瀵嗛挜
+5. 濉叆閰嶇疆鏂囦欢涓�' WHERE `id` = 'LLM_VolcesAiGatewayLLM';
+
+-- 鏇存柊Memory妯″瀷閰嶇疆璇存槑
+-- 鏃犺蹇嗛厤缃鏄�
+UPDATE `ai_model_config` SET
+`doc_link` = NULL,
+`remark` = '鏃犺蹇嗛厤缃鏄庯細
+1. 涓嶄繚瀛樺璇濆巻鍙�
+2. 姣忔瀵硅瘽閮芥槸鐙珛鐨�
+3. 鏃犻渶棰濆閰嶇疆
+4. 閫傚悎瀵归殣绉佽姹傞珮鐨勫満鏅�' WHERE `id` = 'Memory_nomem';
+
+-- 鏈湴鐭湡璁板繂閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = NULL,
+`remark` = '鏈湴鐭湡璁板繂閰嶇疆璇存槑锛�
+1. 浣跨敤鏈湴瀛樺偍淇濆瓨瀵硅瘽鍘嗗彶
+2. 閫氳繃selected_module鐨刲lm鎬荤粨瀵硅瘽鍐呭
+3. 鏁版嵁淇濆瓨鍦ㄦ湰鍦帮紝涓嶄細涓婁紶鍒版湇鍔″櫒
+4. 閫傚悎娉ㄩ噸闅愮鐨勫満鏅�
+5. 鏃犻渶棰濆閰嶇疆' WHERE `id` = 'Memory_mem_local_short';
+
+-- Mem0AI璁板繂閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://app.mem0.ai/dashboard/api-keys',
+`remark` = 'Mem0AI璁板繂閰嶇疆璇存槑锛�
+1. 浣跨敤Mem0AI鏈嶅姟淇濆瓨瀵硅瘽鍘嗗彶
+2. 闇�瑕丄PI瀵嗛挜
+3. 闇�瑕佺綉缁滆繛鎺�
+4. 姣忔湀鏈�1000娆″厤璐硅皟鐢�
+鐢宠姝ラ锛�
+1. 璁块棶 https://app.mem0.ai/dashboard/api-keys
+2. 鑾峰彇API瀵嗛挜
+3. 濉叆閰嶇疆鏂囦欢涓�' WHERE `id` = 'Memory_mem0ai';
+
+-- 鏇存柊Intent妯″瀷閰嶇疆璇存槑
+-- 鏃犳剰鍥捐瘑鍒厤缃鏄�
+UPDATE `ai_model_config` SET
+`doc_link` = NULL,
+`remark` = '鏃犳剰鍥捐瘑鍒厤缃鏄庯細
+1. 涓嶈繘琛屾剰鍥捐瘑鍒�
+2. 鎵�鏈夊璇濈洿鎺ヤ紶閫掔粰LLM澶勭悊
+3. 鏃犻渶棰濆閰嶇疆
+4. 閫傚悎绠�鍗曞璇濆満鏅�' WHERE `id` = 'Intent_nointent';
+
+-- LLM鎰忓浘璇嗗埆閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = NULL,
+`remark` = 'LLM鎰忓浘璇嗗埆閰嶇疆璇存槑锛�
+1. 浣跨敤鐙珛鐨凩LM杩涜鎰忓浘璇嗗埆
+2. 榛樿浣跨敤selected_module.LLM鐨勬ā鍨�
+3. 鍙互閰嶇疆浣跨敤鐙珛鐨凩LM锛堝鍏嶈垂鐨凜hatGLMLLM锛�
+4. 閫氱敤鎬у己锛屼絾浼氬鍔犲鐞嗘椂闂�
+5. 涓嶆敮鎸佹帶鍒堕煶閲忓ぇ灏忕瓑iot鎿嶄綔
+閰嶇疆璇存槑锛�
+1. 鍦╨lm瀛楁涓寚瀹氫娇鐢ㄧ殑LLM妯″瀷
+2. 濡傛灉涓嶆寚瀹氾紝鍒欎娇鐢╯elected_module.LLM鐨勬ā鍨�' WHERE `id` = 'Intent_intent_llm';
+
+-- 鍑芥暟璋冪敤鎰忓浘璇嗗埆閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = NULL,
+`remark` = '鍑芥暟璋冪敤鎰忓浘璇嗗埆閰嶇疆璇存槑锛�
+1. 浣跨敤LLM鐨刦unction_call鍔熻兘杩涜鎰忓浘璇嗗埆
+2. 闇�瑕佹墍閫夋嫨鐨凩LM鏀寔function_call
+3. 鎸夐渶璋冪敤宸ュ叿锛屽鐞嗛�熷害蹇�
+4. 鏀寔鎵�鏈塱ot鎸囦护
+5. 榛樿宸插姞杞戒互涓嬪姛鑳斤細
+ - handle_exit_intent锛堥��鍑鸿瘑鍒級
+ - play_music锛堥煶涔愭挱鏀撅級
+ - change_role锛堣鑹插垏鎹級
+ - get_weather锛堝ぉ姘旀煡璇級
+ - get_news锛堟柊闂绘煡璇級
+閰嶇疆璇存槑锛�
+1. 鍦╢unctions瀛楁涓厤缃渶瑕佸姞杞界殑鍔熻兘妯″潡
+2. 绯荤粺榛樿宸插姞杞藉熀纭�鍔熻兘锛屾棤闇�閲嶅閰嶇疆
+3. 鍙互娣诲姞鑷畾涔夊姛鑳芥ā鍧�' WHERE `id` = 'Intent_function_call';
+
+-- 鏇存柊VAD妯″瀷閰嶇疆璇存槑
+-- SileroVAD閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://github.com/snakers4/silero-vad',
+`remark` = 'SileroVAD閰嶇疆璇存槑锛�
+1. 浣跨敤SileroVAD妯″瀷杩涜璇煶娲诲姩妫�娴�
+2. 鏈湴鎺ㄧ悊锛屾棤闇�缃戠粶杩炴帴
+3. 闇�瑕佷笅杞芥ā鍨嬫枃浠跺埌models/snakers4_silero-vad鐩綍
+4. 鍙厤缃弬鏁帮細
+ - threshold: 0.5锛堣闊虫娴嬮槇鍊硷級
+ - min_silence_duration_ms: 700锛堟渶灏忛潤闊虫寔缁椂闂达紝鍗曚綅姣锛�
+5. 濡傛灉璇磋瘽鍋滈】姣旇緝闀匡紝鍙互閫傚綋澧炲姞min_silence_duration_ms鐨勫��' WHERE `id` = 'VAD_SileroVAD';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504301341.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504301341.sql
new file mode 100644
index 0000000..a88ee78
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202504301341.sql
@@ -0,0 +1,85 @@
+update `ai_model_provider` set `fields` =
+'[{"key": "api_url","label": "API鍦板潃","type": "string"},{"key": "voice","label": "闊宠壊","type": "string"},{"key": "output_dir","label": "杈撳嚭鐩綍","type": "string"},{"key": "authorization","label": "鎺堟潈","type": "string"},{"key": "appid","label": "搴旂敤ID","type": "string"},{"key": "access_token","label": "璁块棶浠ょ墝","type": "string"},{"key": "cluster","label": "闆嗙兢","type": "string"},{"key": "speed_ratio","label": "璇��","type": "number"},{"key": "volume_ratio","label": "闊抽噺","type": "number"},{"key": "pitch_ratio","label": "闊抽珮","type": "number"}]'
+where `id` = 'SYSTEM_TTS_doubao';
+
+-- 娣诲姞闃块噷浜慉SR渚涘簲鍣�
+delete from `ai_model_provider` where `id` = 'SYSTEM_ASR_AliyunASR';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_ASR_AliyunASR', 'ASR', 'aliyun', '闃块噷浜戣闊宠瘑鍒�', '[{"key":"appkey","label":"搴旂敤AppKey","type":"string"},{"key":"token","label":"涓存椂Token","type":"string"},{"key":"access_key_id","label":"AccessKey ID","type":"string"},{"key":"access_key_secret","label":"AccessKey Secret","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"}]', 5, 1, NOW(), 1, NOW());
+
+-- 娣诲姞闃块噷浜慉SR妯″瀷閰嶇疆
+delete from `ai_model_config` where `id` = 'ASR_AliyunASR';
+INSERT INTO `ai_model_config` VALUES ('ASR_AliyunASR', 'ASR', 'AliyunASR', '闃块噷浜戣闊宠瘑鍒�', 0, 1, '{\"type\": \"aliyun\", \"appkey\": \"\", \"token\": \"\", \"access_key_id\": \"\", \"access_key_secret\": \"\", \"output_dir\": \"tmp/\"}', NULL, NULL, 6, NULL, NULL, NULL, NULL);
+
+-- 鏇存柊闃块噷浜慉SR妯″瀷閰嶇疆鐨勮鏄庢枃妗�
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://nls-portal.console.aliyun.com/',
+`remark` = '闃块噷浜慉SR閰嶇疆璇存槑锛�
+1. 璁块棶 https://nls-portal.console.aliyun.com/ 寮�閫氭湇鍔�
+2. 璁块棶 https://nls-portal.console.aliyun.com/applist 鑾峰彇appkey
+3. 璁块棶 https://nls-portal.console.aliyun.com/overview 鑾峰彇token
+4. 鑾峰彇access_key_id鍜宎ccess_key_secret
+5. 濉叆閰嶇疆鏂囦欢涓�' WHERE `id` = 'ASR_AliyunASR';
+
+-- 鎻掑叆鍥轰欢绫诲瀷瀛楀吀绫诲瀷
+delete from `sys_dict_type` where `id` = 101;
+INSERT INTO `sys_dict_type` (`id`, `dict_type`, `dict_name`, `remark`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+(101, 'FIRMWARE_TYPE', '鍥轰欢绫诲瀷', '鍥轰欢绫诲瀷瀛楀吀', 0, 1, NOW(), 1, NOW());
+
+-- 鎻掑叆鍥轰欢绫诲瀷瀛楀吀鏁版嵁
+delete from `sys_dict_data` where `dict_type_id` = 101;
+INSERT INTO `sys_dict_data` (`id`, `dict_type_id`, `dict_label`, `dict_value`, `remark`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+(101001, 101, '闈㈠寘鏉挎柊鐗堟帴绾匡紙WiFi锛�', 'bread-compact-wifi', '闈㈠寘鏉挎柊鐗堟帴绾匡紙WiFi锛�', 1, 1, NOW(), 1, NOW()),
+(101002, 101, '闈㈠寘鏉挎柊鐗堟帴绾匡紙WiFi锛�+ LCD', 'bread-compact-wifi-lcd', '闈㈠寘鏉挎柊鐗堟帴绾匡紙WiFi锛�+ LCD', 2, 1, NOW(), 1, NOW()),
+(101003, 101, '闈㈠寘鏉挎柊鐗堟帴绾匡紙ML307 AT锛�', 'bread-compact-ml307', '闈㈠寘鏉挎柊鐗堟帴绾匡紙ML307 AT锛�', 3, 1, NOW(), 1, NOW()),
+(101004, 101, '闈㈠寘鏉匡紙WiFi锛� ESP32 DevKit', 'bread-compact-esp32', '闈㈠寘鏉匡紙WiFi锛� ESP32 DevKit', 4, 1, NOW(), 1, NOW()),
+(101005, 101, '闈㈠寘鏉匡紙WiFi+ LCD锛� ESP32 DevKit', 'bread-compact-esp32-lcd', '闈㈠寘鏉匡紙WiFi+ LCD锛� ESP32 DevKit', 5, 1, NOW(), 1, NOW()),
+(101006, 101, 'DFRobot 琛岀┖鏉� k10', 'df-k10', 'DFRobot 琛岀┖鏉� k10', 6, 1, NOW(), 1, NOW()),
+(101007, 101, 'ESP32 CGC', 'esp32-cgc', 'ESP32 CGC', 7, 1, NOW(), 1, NOW()),
+(101008, 101, 'ESP BOX 3', 'esp-box-3', 'ESP BOX 3', 8, 1, NOW(), 1, NOW()),
+(101009, 101, 'ESP BOX', 'esp-box', 'ESP BOX', 9, 1, NOW(), 1, NOW()),
+(101010, 101, 'ESP BOX Lite', 'esp-box-lite', 'ESP BOX Lite', 10, 1, NOW(), 1, NOW()),
+(101011, 101, 'Kevin Box 1', 'kevin-box-1', 'Kevin Box 1', 11, 1, NOW(), 1, NOW()),
+(101012, 101, 'Kevin Box 2', 'kevin-box-2', 'Kevin Box 2', 12, 1, NOW(), 1, NOW()),
+(101013, 101, 'Kevin C3', 'kevin-c3', 'Kevin C3', 13, 1, NOW(), 1, NOW()),
+(101014, 101, 'Kevin SP V3寮�鍙戞澘', 'kevin-sp-v3-dev', 'Kevin SP V3寮�鍙戞澘', 14, 1, NOW(), 1, NOW()),
+(101015, 101, 'Kevin SP V4寮�鍙戞澘', 'kevin-sp-v4-dev', 'Kevin SP V4寮�鍙戞澘', 15, 1, NOW(), 1, NOW()),
+(101016, 101, '楸奸拱绉戞妧3.13LCD寮�鍙戞澘', 'kevin-yuying-313lcd', '楸奸拱绉戞妧3.13LCD寮�鍙戞澘', 16, 1, NOW(), 1, NOW()),
+(101017, 101, '绔嬪垱路瀹炴垬娲綞SP32-S3寮�鍙戞澘', 'lichuang-dev', '绔嬪垱路瀹炴垬娲綞SP32-S3寮�鍙戞澘', 17, 1, NOW(), 1, NOW()),
+(101018, 101, '绔嬪垱路瀹炴垬娲綞SP32-C3寮�鍙戞澘', 'lichuang-c3-dev', '绔嬪垱路瀹炴垬娲綞SP32-C3寮�鍙戞澘', 18, 1, NOW(), 1, NOW()),
+(101019, 101, '绁炲鎸夐挳 Magiclick_2.4', 'magiclick-2p4', '绁炲鎸夐挳 Magiclick_2.4', 19, 1, NOW(), 1, NOW()),
+(101020, 101, '绁炲鎸夐挳 Magiclick_2.5', 'magiclick-2p5', '绁炲鎸夐挳 Magiclick_2.5', 20, 1, NOW(), 1, NOW()),
+(101021, 101, '绁炲鎸夐挳 Magiclick_C3', 'magiclick-c3', '绁炲鎸夐挳 Magiclick_C3', 21, 1, NOW(), 1, NOW()),
+(101022, 101, '绁炲鎸夐挳 Magiclick_C3_v2', 'magiclick-c3-v2', '绁炲鎸夐挳 Magiclick_C3_v2', 22, 1, NOW(), 1, NOW()),
+(101023, 101, 'M5Stack CoreS3', 'm5stack-core-s3', 'M5Stack CoreS3', 23, 1, NOW(), 1, NOW()),
+(101024, 101, 'AtomS3 + Echo Base', 'atoms3-echo-base', 'AtomS3 + Echo Base', 24, 1, NOW(), 1, NOW()),
+(101025, 101, 'AtomS3R + Echo Base', 'atoms3r-echo-base', 'AtomS3R + Echo Base', 25, 1, NOW(), 1, NOW()),
+(101026, 101, 'AtomS3R CAM/M12 + Echo Base', 'atoms3r-cam-m12-echo-base', 'AtomS3R CAM/M12 + Echo Base', 26, 1, NOW(), 1, NOW()),
+(101027, 101, 'AtomMatrix + Echo Base', 'atommatrix-echo-base', 'AtomMatrix + Echo Base', 27, 1, NOW(), 1, NOW()),
+(101028, 101, '铏惧摜 Mini C3', 'xmini-c3', '铏惧摜 Mini C3', 28, 1, NOW(), 1, NOW()),
+(101029, 101, 'ESP32S3_KORVO2_V3寮�鍙戞澘', 'esp32s3-korvo2-v3', 'ESP32S3_KORVO2_V3寮�鍙戞澘', 29, 1, NOW(), 1, NOW()),
+(101030, 101, 'ESP-SparkBot寮�鍙戞澘', 'esp-sparkbot', 'ESP-SparkBot寮�鍙戞澘', 30, 1, NOW(), 1, NOW()),
+(101031, 101, 'ESP-Spot-S3', 'esp-spot-s3', 'ESP-Spot-S3', 31, 1, NOW(), 1, NOW()),
+(101032, 101, 'Waveshare ESP32-S3-Touch-AMOLED-1.8', 'esp32-s3-touch-amoled-1.8', 'Waveshare ESP32-S3-Touch-AMOLED-1.8', 32, 1, NOW(), 1, NOW()),
+(101033, 101, 'Waveshare ESP32-S3-Touch-LCD-1.85C', 'esp32-s3-touch-lcd-1.85c', 'Waveshare ESP32-S3-Touch-LCD-1.85C', 33, 1, NOW(), 1, NOW()),
+(101034, 101, 'Waveshare ESP32-S3-Touch-LCD-1.85', 'esp32-s3-touch-lcd-1.85', 'Waveshare ESP32-S3-Touch-LCD-1.85', 34, 1, NOW(), 1, NOW()),
+(101035, 101, 'Waveshare ESP32-S3-Touch-LCD-1.46', 'esp32-s3-touch-lcd-1.46', 'Waveshare ESP32-S3-Touch-LCD-1.46', 35, 1, NOW(), 1, NOW()),
+(101036, 101, 'Waveshare ESP32-S3-Touch-LCD-3.5', 'esp32-s3-touch-lcd-3.5', 'Waveshare ESP32-S3-Touch-LCD-3.5', 36, 1, NOW(), 1, NOW()),
+(101037, 101, '鍦熻眴瀛�', 'tudouzi', '鍦熻眴瀛�', 37, 1, NOW(), 1, NOW()),
+(101038, 101, 'LILYGO T-Circle-S3', 'lilygo-t-circle-s3', 'LILYGO T-Circle-S3', 38, 1, NOW(), 1, NOW()),
+(101039, 101, 'LILYGO T-CameraPlus-S3', 'lilygo-t-cameraplus-s3', 'LILYGO T-CameraPlus-S3', 39, 1, NOW(), 1, NOW()),
+(101040, 101, 'Movecall Moji 灏忔櫤AI琛嶇敓鐗�', 'movecall-moji-esp32s3', 'Movecall Moji 灏忔櫤AI琛嶇敓鐗�', 40, 1, NOW(), 1, NOW()),
+(101041, 101, 'Movecall CuiCan 鐠�鐠稟I鍚婂潬', 'movecall-cuican-esp32s3', 'Movecall CuiCan 鐠�鐠稟I鍚婂潬', 41, 1, NOW(), 1, NOW()),
+(101042, 101, '姝g偣鍘熷瓙DNESP32S3寮�鍙戞澘', 'atk-dnesp32s3', '姝g偣鍘熷瓙DNESP32S3寮�鍙戞澘', 42, 1, NOW(), 1, NOW()),
+(101043, 101, '姝g偣鍘熷瓙DNESP32S3-BOX', 'atk-dnesp32s3-box', '姝g偣鍘熷瓙DNESP32S3-BOX', 43, 1, NOW(), 1, NOW()),
+(101044, 101, '鍢熷槦寮�鍙戞澘CHATX(wifi)', 'du-chatx', '鍢熷槦寮�鍙戞澘CHATX(wifi)', 44, 1, NOW(), 1, NOW()),
+(101045, 101, '澶瀬灏忔淳esp32s3', 'taiji-pi-s3', '澶瀬灏忔淳esp32s3', 45, 1, NOW(), 1, NOW()),
+(101046, 101, '鏃犲悕绉戞妧鏄熸櫤0.85(WIFI)', 'xingzhi-cube-0.85tft-wifi', '鏃犲悕绉戞妧鏄熸櫤0.85(WIFI)', 46, 1, NOW(), 1, NOW()),
+(101047, 101, '鏃犲悕绉戞妧鏄熸櫤0.85(ML307)', 'xingzhi-cube-0.85tft-ml307', '鏃犲悕绉戞妧鏄熸櫤0.85(ML307)', 47, 1, NOW(), 1, NOW()),
+(101048, 101, '鏃犲悕绉戞妧鏄熸櫤0.96(WIFI)', 'xingzhi-cube-0.96oled-wifi', '鏃犲悕绉戞妧鏄熸櫤0.96(WIFI)', 48, 1, NOW(), 1, NOW()),
+(101049, 101, '鏃犲悕绉戞妧鏄熸櫤0.96(ML307)', 'xingzhi-cube-0.96oled-ml307', '鏃犲悕绉戞妧鏄熸櫤0.96(ML307)', 49, 1, NOW(), 1, NOW()),
+(101050, 101, '鏃犲悕绉戞妧鏄熸櫤1.54(WIFI)', 'xingzhi-cube-1.54tft-wifi', '鏃犲悕绉戞妧鏄熸櫤1.54(WIFI)', 50, 1, NOW(), 1, NOW()),
+(101051, 101, '鏃犲悕绉戞妧鏄熸櫤1.54(ML307)', 'xingzhi-cube-1.54tft-ml307', '鏃犲悕绉戞妧鏄熸櫤1.54(ML307)', 51, 1, NOW(), 1, NOW()),
+(101052, 101, 'SenseCAP Watcher', 'sensecap-watcher', 'SenseCAP Watcher', 52, 1, NOW(), 1, NOW()),
+(101053, 101, '鍥涘崥鏅鸿仈AI闄即鐩掑瓙', 'doit-s3-aibox', '鍥涘崥鏅鸿仈AI闄即鐩掑瓙', 53, 1, NOW(), 1, NOW()),
+(101054, 101, '鍏冩帶路闈掓槬', 'mixgo-nova', '鍏冩帶路闈掓槬', 54, 1, NOW(), 1, NOW());
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505022134.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505022134.sql
new file mode 100644
index 0000000..ccbaaae
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505022134.sql
@@ -0,0 +1,27 @@
+-- 鍒濆鍖栨櫤鑳戒綋鑱婂ぉ璁板綍
+DROP TABLE IF EXISTS ai_chat_history;
+DROP TABLE IF EXISTS ai_chat_message;
+DROP TABLE IF EXISTS ai_agent_chat_history;
+CREATE TABLE ai_agent_chat_history
+(
+ id BIGINT AUTO_INCREMENT COMMENT '涓婚敭ID' PRIMARY KEY,
+ mac_address VARCHAR(50) COMMENT 'MAC鍦板潃',
+ agent_id VARCHAR(32) COMMENT '鏅鸿兘浣搃d',
+ session_id VARCHAR(50) COMMENT '浼氳瘽ID',
+ chat_type TINYINT(3) COMMENT '娑堟伅绫诲瀷: 1-鐢ㄦ埛, 2-鏅鸿兘浣�',
+ content VARCHAR(1024) COMMENT '鑱婂ぉ鍐呭',
+ audio_id VARCHAR(32) COMMENT '闊抽ID',
+ created_at DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3) NOT NULL COMMENT '鍒涘缓鏃堕棿',
+ updated_at DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3) NOT NULL ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '鏇存柊鏃堕棿',
+ INDEX idx_ai_agent_chat_history_mac (mac_address),
+ INDEX idx_ai_agent_chat_history_session_id (session_id),
+ INDEX idx_ai_agent_chat_history_agent_id (agent_id),
+ INDEX idx_ai_agent_chat_history_agent_session_created (agent_id, session_id, created_at)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '鏅鸿兘浣撹亰澶╄褰曡〃';
+
+DROP TABLE IF EXISTS ai_agent_chat_audio;
+CREATE TABLE ai_agent_chat_audio
+(
+ id VARCHAR(32) COMMENT '涓婚敭ID' PRIMARY KEY,
+ audio LONGBLOB COMMENT '闊抽opus鏁版嵁'
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '鏅鸿兘浣撹亰澶╅煶棰戞暟鎹〃';
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505081146.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505081146.sql
new file mode 100644
index 0000000..fe7582f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505081146.sql
@@ -0,0 +1,43 @@
+-- 娣诲姞鐧惧害ASR妯″瀷閰嶇疆
+delete from `ai_model_config` where `id` = 'ASR_BaiduASR';
+INSERT INTO `ai_model_config` VALUES ('ASR_BaiduASR', 'ASR', 'BaiduASR', '鐧惧害璇煶璇嗗埆', 0, 1, '{\"type\": \"baidu\", \"app_id\": \"\", \"api_key\": \"\", \"secret_key\": \"\", \"dev_pid\": 1537, \"output_dir\": \"tmp/\"}', NULL, NULL, 7, NULL, NULL, NULL, NULL);
+
+
+-- 娣诲姞鐧惧害ASR渚涘簲鍣�
+delete from `ai_model_provider` where `id` = 'SYSTEM_ASR_BaiduASR';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_ASR_BaiduASR', 'ASR', 'baidu', '鐧惧害璇煶璇嗗埆', '[{"key":"app_id","label":"搴旂敤AppID","type":"string"},{"key":"api_key","label":"API Key","type":"string"},{"key":"secret_key","label":"Secret Key","type":"string"},{"key":"dev_pid","label":"璇█鍙傛暟","type":"number"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"}]', 7, 1, NOW(), 1, NOW());
+
+
+-- 鏇存柊鐧惧害ASR閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://console.bce.baidu.com/ai-engine/old/#/ai/speech/app/list',
+`remark` = '鐧惧害ASR閰嶇疆璇存槑锛�
+1. 璁块棶 https://console.bce.baidu.com/ai-engine/old/#/ai/speech/app/list
+2. 鍒涘缓鏂板簲鐢�
+3. 鑾峰彇AppID銆丄PI Key鍜孲ecret Key
+4. 濉叆閰嶇疆鏂囦欢涓�
+鏌ョ湅璧勬簮棰濆害锛歨ttps://console.bce.baidu.com/ai-engine/old/#/ai/speech/overview/resource/list
+璇█鍙傛暟璇存槑锛歨ttps://ai.baidu.com/ai-doc/SPEECH/0lbxfnc9b
+' WHERE `id` = 'ASR_BaiduASR';
+
+-- 鏇存柊璞嗗寘渚涘簲鍣ㄥ瓧娈�
+update `ai_model_provider` set `fields` =
+'[{"key":"appid","label":"搴旂敤ID","type":"string"},{"key":"access_token","label":"璁块棶浠ょ墝","type":"string"},{"key":"cluster","label":"闆嗙兢","type":"string"},{"key":"boosting_table_name","label":"鐑瘝鏂囦欢鍚嶇О","type":"string"},{"key":"correct_table_name","label":"鏇挎崲璇嶆枃浠跺悕绉�","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"}]'
+where `id` = 'SYSTEM_ASR_DoubaoASR';
+
+-- 鏇存柊璞嗗寘ASR閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://console.volcengine.com/speech/app',
+`remark` = '璞嗗寘ASR閰嶇疆璇存槑锛�
+1. 闇�瑕佸湪鐏北寮曟搸鎺у埗鍙板垱寤哄簲鐢ㄥ苟鑾峰彇appid鍜宎ccess_token
+2. 鏀寔涓枃璇煶璇嗗埆
+3. 闇�瑕佺綉缁滆繛鎺�
+4. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍
+鐢宠姝ラ锛�
+1. 璁块棶 https://console.volcengine.com/speech/app
+2. 鍒涘缓鏂板簲鐢�
+3. 鑾峰彇appid鍜宎ccess_token
+4. 濉叆閰嶇疆鏂囦欢涓�
+濡傞渶璁剧疆鐑瘝锛岃鍙傝�冿細https://www.volcengine.com/docs/6561/155738
+' WHERE `id` = 'ASR_DoubaoASR';
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505091555.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505091555.sql
new file mode 100644
index 0000000..b7ad91d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505091555.sql
@@ -0,0 +1,25 @@
+-- 鏇存柊妯″瀷渚涘簲鍣ㄨ〃
+UPDATE `ai_model_provider` SET fields = '[{"key": "host", "type": "string", "label": "鏈嶅姟鍦板潃"}, {"key": "port", "type": "number", "label": "绔彛鍙�"}, {"key": "type", "type": "string", "label": "鏈嶅姟绫诲瀷"}, {"key": "is_ssl", "type": "boolean", "label": "鏄惁浣跨敤SSL"}, {"key": "api_key", "type": "string", "label": "API瀵嗛挜"}, {"key": "output_dir", "type": "string", "label": "杈撳嚭鐩綍"}]' WHERE id = 'SYSTEM_ASR_FunASRServer';
+
+-- 鏇存柊妯″瀷閰嶇疆琛�
+UPDATE `ai_model_config` SET
+config_json = '{"host": "127.0.0.1", "port": 10096, "type": "fun_server", "is_ssl": true, "api_key": "none", "output_dir": "tmp/"}',
+`doc_link` = 'https://github.com/modelscope/FunASR/blob/main/runtime/docs/SDK_advanced_guide_online_zh.md',
+`remark` = '鐙珛閮ㄧ讲FunASR锛屼娇鐢‵unASR鐨凙PI鏈嶅姟锛屽彧闇�瑕佷簲鍙ヨ瘽
+绗竴鍙ワ細mkdir -p ./funasr-runtime-resources/models
+绗簩鍙ワ細sudo docker run -p 10096:10095 -it --privileged=true -v $PWD/funasr-runtime-resources/models:/workspace/models registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr:funasr-runtime-sdk-online-cpu-0.1.12
+涓婁竴鍙ヨ瘽鎵ц鍚庝細杩涘叆鍒板鍣紝缁х画绗笁鍙ワ細cd FunASR/runtime
+涓嶈閫�鍑哄鍣紝缁х画鍦ㄥ鍣ㄤ腑鎵ц绗洓鍙ワ細nohup bash run_server_2pass.sh --download-model-dir /workspace/models --vad-dir damo/speech_fsmn_vad_zh-cn-16k-common-onnx --model-dir damo/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-onnx --online-model-dir damo/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online-onnx --punc-dir damo/punc_ct-transformer_zh-cn-common-vad_realtime-vocab272727-onnx --lm-dir damo/speech_ngram_lm_zh-cn-ai-wesp-fst --itn-dir thuduj12/fst_itn_zh --hotword /workspace/models/hotwords.txt > log.txt 2>&1 &
+涓婁竴鍙ヨ瘽鎵ц鍚庝細杩涘叆鍒板鍣紝缁х画绗簲鍙ワ細tail -f log.txt
+绗簲鍙ヨ瘽鎵ц瀹屽悗锛屼細鐪嬪埌妯″瀷涓嬭浇鏃ュ織锛屼笅杞藉畬鍚庡氨鍙互杩炴帴浣跨敤浜�
+浠ヤ笂鏄娇鐢–PU鎺ㄧ悊锛屽鏋滄湁GPU锛岃缁嗗弬鑰冿細https://github.com/modelscope/FunASR/blob/main/runtime/docs/SDK_advanced_guide_online_zh.md' WHERE `id` = 'ASR_FunASRServer';
+
+-- FishSpeech閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://github.com/xinnan-tech/xiaozhi-esp32-server/blob/main/docs/fish-speech-integration.md',
+`remark` = 'FishSpeech閰嶇疆璇存槑锛�
+1. 闇�瑕佹湰鍦伴儴缃睩ishSpeech鏈嶅姟
+2. 鏀寔鑷畾涔夐煶鑹�
+3. 鏈湴鎺ㄧ悊锛屾棤闇�缃戠粶杩炴帴
+4. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍
+5. 鍙弬鐓ф暀绋媓ttps://github.com/xinnan-tech/xiaozhi-esp32-server/blob/main/docs/fish-speech-integration.md' WHERE `id` = 'TTS_FishSpeech';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505111914.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505111914.sql
new file mode 100644
index 0000000..c67f347
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505111914.sql
@@ -0,0 +1,6 @@
+-- 娣诲姞鑱婂ぉ璁板綍閰嶇疆瀛楁
+ALTER TABLE `ai_agent`
+ADD COLUMN `chat_history_conf` tinyint NOT NULL DEFAULT 0 COMMENT '鑱婂ぉ璁板綍閰嶇疆锛�0涓嶈褰� 1浠呰褰曟枃鏈� 2璁板綍鏂囨湰鍜岃闊筹級' AFTER `system_prompt`;
+
+ALTER TABLE `ai_agent_template`
+ADD COLUMN `chat_history_conf` tinyint NOT NULL DEFAULT 0 COMMENT '鑱婂ぉ璁板綍閰嶇疆锛�0涓嶈褰� 1浠呰褰曟枃鏈� 2璁板綍鏂囨湰鍜岃闊筹級' AFTER `system_prompt`;
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505122348.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505122348.sql
new file mode 100644
index 0000000..05bbeb8
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505122348.sql
@@ -0,0 +1,6 @@
+-- 娣诲姞鎬荤粨璁板繂瀛楁
+ALTER TABLE `ai_agent`
+ADD COLUMN `summary_memory` text COMMENT '鎬荤粨璁板繂' AFTER `system_prompt`;
+
+ALTER TABLE `ai_agent_template`
+ADD COLUMN `summary_memory` text COMMENT '鎬荤粨璁板繂' AFTER `system_prompt`;
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505142037.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505142037.sql
new file mode 100644
index 0000000..37a4901
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505142037.sql
@@ -0,0 +1,7 @@
+update ai_agent_template set system_prompt = replace(system_prompt, '鎴戞槸', '浣犳槸');
+
+delete from sys_params where id in (500,501,402);
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (500, 'end_prompt.enable', 'true', 'boolean', 1, '鏄惁寮�鍚粨鏉熻');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (501, 'end_prompt.prompt', '璇蜂綘浠モ�滄椂闂磋繃寰楃湡蹇�濇湭鏉ュご锛岀敤瀵屾湁鎰熸儏銆佷緷渚濅笉鑸嶇殑璇濇潵缁撴潫杩欏満瀵硅瘽鍚э紒', 'string', 1, '缁撴潫鎻愮ず璇�');
+
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (402, 'plugins.get_weather.api_host', 'mj7p3y7naa.re.qweatherapi.com', 'string', 1, '寮�鍙戣�卆pihost');
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505151451.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505151451.sql
new file mode 100644
index 0000000..601de45
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505151451.sql
@@ -0,0 +1,18 @@
+-- 淇敼鑷畾涔塗TS鎺ュ彛璇锋眰瀹氫箟
+update `ai_model_provider` set `fields` =
+'[{"key":"url","label":"鏈嶅姟鍦板潃","type":"string"},{"key":"method","label":"璇锋眰鏂瑰紡","type":"string"},{"key":"params","label":"璇锋眰鍙傛暟","type":"dict","dict_name":"params"},{"key":"headers","label":"璇锋眰澶�","type":"dict","dict_name":"headers"},{"key":"format","label":"闊抽鏍煎紡","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"}]'
+where `id` = 'SYSTEM_TTS_custom';
+
+-- 淇敼鑷畾涔塗TS閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = NULL,
+`remark` = '鑷畾涔塗TS閰嶇疆璇存槑锛�
+1. 鑷畾涔夌殑TTS鎺ュ彛鏈嶅姟锛岃姹傚弬鏁板彲鑷畾涔夛紝鍙帴鍏ヤ紬澶歍TS鏈嶅姟
+2. 浠ユ湰鍦伴儴缃茬殑KokoroTTS涓轰緥
+3. 濡傛灉鍙湁cpu杩愯锛歞ocker run -p 8880:8880 ghcr.io/remsky/kokoro-fastapi-cpu:latest
+4. 濡傛灉鍙湁gpu杩愯锛歞ocker run --gpus all -p 8880:8880 ghcr.io/remsky/kokoro-fastapi-gpu:latest
+閰嶇疆璇存槑锛�
+1. 鍦╬arams涓厤缃姹傚弬鏁�,浣跨敤JSON鏍煎紡
+ 渚嬪KokoroTTS锛歿 "input": "{prompt_text}", "speed": 1, "voice": "zm_yunxi", "stream": true, "download_format": "mp3", "response_format": "mp3", "return_download_link": true }
+2. 鍦╤eaders涓厤缃姹傚ご
+3. 璁剧疆杩斿洖闊抽鏍煎紡' WHERE `id` = 'TTS_CustomTTS';
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505182234.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505182234.sql
new file mode 100644
index 0000000..3873e5d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505182234.sql
@@ -0,0 +1,65 @@
+-- 娣诲姞鎵嬫満鐭俊娉ㄥ唽鍔熻兘鐨勯渶瑕佺殑鍙傛暟
+delete from sys_params where id in (108, 109, 110, 111, 112, 113, 114, 115);
+delete from sys_params where id in (610, 611, 612, 613);
+INSERT INTO sys_params
+(id, param_code, param_value, value_type, param_type, remark, creator, create_date, updater, update_date)
+ VALUES
+(108, 'server.name', 'xiaozhi-esp32-server', 'string', 1, '绯荤粺鍚嶇О', NULL, NULL, NULL, NULL),
+(109, 'server.beian_icp_num', 'null', 'string', 1, 'icp澶囨鍙凤紝濉啓null鍒欎笉璁剧疆', NULL, NULL, NULL, NULL),
+(110, 'server.beian_ga_num', 'null', 'string', 1, '鍏畨澶囨鍙凤紝濉啓null鍒欎笉璁剧疆', NULL, NULL, NULL, NULL),
+(111, 'server.enable_mobile_register', 'false', 'boolean', 1, '鏄惁寮�鍚墜鏈烘敞鍐�', NULL, NULL, NULL, NULL),
+(112, 'server.sms_max_send_count', '10', 'number', 1, '鍗曞彿鐮佸崟鏃ユ渶澶х煭淇″彂閫佹潯鏁�', NULL, NULL, NULL, NULL),
+(610, 'aliyun.sms.access_key_id', '', 'string', 1, '闃块噷浜戝钩鍙癮ccess_key', NULL, NULL, NULL, NULL),
+(611, 'aliyun.sms.access_key_secret', '', 'string', 1, '闃块噷浜戝钩鍙癮ccess_key_secret', NULL, NULL, NULL, NULL),
+(612, 'aliyun.sms.sign_name', '', 'string', 1, '闃块噷浜戠煭淇$鍚�', NULL, NULL, NULL, NULL),
+(613, 'aliyun.sms.sms_code_template_code', '', 'string', 1, '闃块噷浜戠煭淇℃ā鏉�', NULL, NULL, NULL, NULL);
+
+update sys_params set remark = '鏄惁鍏佽绠$悊鍛樹互澶栫殑浜烘敞鍐�' where param_code = 'server.allow_user_register';
+
+-- 澧炲姞鎵嬫満鍖哄煙瀛楀吀
+-- 鎻掑叆鍥轰欢绫诲瀷瀛楀吀绫诲瀷
+delete from `sys_dict_type` where `id` = 102;
+INSERT INTO `sys_dict_type` (`id`, `dict_type`, `dict_name`, `remark`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+(102, 'MOBILE_AREA', '鎵嬫満鍖哄煙', '鎵嬫満鍖哄煙瀛楀吀', 0, 1, NOW(), 1, NOW());
+
+-- 鎻掑叆鍥轰欢绫诲瀷瀛楀吀鏁版嵁
+delete from `sys_dict_data` where `dict_type_id` = 102;
+INSERT INTO `sys_dict_data` (`id`, `dict_type_id`, `dict_label`, `dict_value`, `remark`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+(102001, 102, '涓浗澶ч檰', '+86', '涓浗澶ч檰', 1, 1, NOW(), 1, NOW()),
+(102002, 102, '涓浗棣欐腐', '+852', '涓浗棣欐腐', 2, 1, NOW(), 1, NOW()),
+(102003, 102, '涓浗婢抽棬', '+853', '涓浗婢抽棬', 3, 1, NOW(), 1, NOW()),
+(102004, 102, '涓浗鍙版咕', '+886', '涓浗鍙版咕', 4, 1, NOW(), 1, NOW()),
+(102005, 102, '缇庡浗/鍔犳嬁澶�', '+1', '缇庡浗/鍔犳嬁澶�', 5, 1, NOW(), 1, NOW()),
+(102006, 102, '鑻卞浗', '+44', '鑻卞浗', 6, 1, NOW(), 1, NOW()),
+(102007, 102, '娉曞浗', '+33', '娉曞浗', 7, 1, NOW(), 1, NOW()),
+(102008, 102, '鎰忓ぇ鍒�', '+39', '鎰忓ぇ鍒�', 8, 1, NOW(), 1, NOW()),
+(102009, 102, '寰峰浗', '+49', '寰峰浗', 9, 1, NOW(), 1, NOW()),
+(102010, 102, '娉㈠叞', '+48', '娉㈠叞', 10, 1, NOW(), 1, NOW()),
+(102011, 102, '鐟炲+', '+41', '鐟炲+', 11, 1, NOW(), 1, NOW()),
+(102012, 102, '瑗跨彮鐗�', '+34', '瑗跨彮鐗�', 12, 1, NOW(), 1, NOW()),
+(102013, 102, '涓归害', '+45', '涓归害', 13, 1, NOW(), 1, NOW()),
+(102014, 102, '椹潵瑗夸簹', '+60', '椹潵瑗夸簹', 14, 1, NOW(), 1, NOW()),
+(102015, 102, '婢冲ぇ鍒╀簹', '+61', '婢冲ぇ鍒╀簹', 15, 1, NOW(), 1, NOW()),
+(102016, 102, '鍗板害灏艰タ浜�', '+62', '鍗板害灏艰タ浜�', 16, 1, NOW(), 1, NOW()),
+(102017, 102, '鑿插緥瀹�', '+63', '鑿插緥瀹�', 17, 1, NOW(), 1, NOW()),
+(102018, 102, '鏂拌タ鍏�', '+64', '鏂拌タ鍏�', 18, 1, NOW(), 1, NOW()),
+(102019, 102, '鏂板姞鍧�', '+65', '鏂板姞鍧�', 19, 1, NOW(), 1, NOW()),
+(102020, 102, '娉板浗', '+66', '娉板浗', 20, 1, NOW(), 1, NOW()),
+(102021, 102, '鏃ユ湰', '+81', '鏃ユ湰', 21, 1, NOW(), 1, NOW()),
+(102022, 102, '闊╁浗', '+82', '闊╁浗', 22, 1, NOW(), 1, NOW()),
+(102023, 102, '瓒婂崡', '+84', '瓒婂崡', 23, 1, NOW(), 1, NOW()),
+(102024, 102, '鍗板害', '+91', '鍗板害', 24, 1, NOW(), 1, NOW()),
+(102025, 102, '宸村熀鏂潶', '+92', '宸村熀鏂潶', 25, 1, NOW(), 1, NOW()),
+(102026, 102, '灏兼棩鍒╀簹', '+234', '灏兼棩鍒╀簹', 26, 1, NOW(), 1, NOW()),
+(102027, 102, '瀛熷姞鎷夊浗', '+880', '瀛熷姞鎷夊浗', 27, 1, NOW(), 1, NOW()),
+(102028, 102, '娌欑壒闃挎媺浼�', '+966', '娌欑壒闃挎媺浼�', 28, 1, NOW(), 1, NOW()),
+(102029, 102, '闃胯仈閰�', '+971', '闃胯仈閰�', 29, 1, NOW(), 1, NOW()),
+(102030, 102, '宸磋タ', '+55', '宸磋タ', 30, 1, NOW(), 1, NOW()),
+(102031, 102, '澧ㄨタ鍝�', '+52', '澧ㄨタ鍝�', 31, 1, NOW(), 1, NOW()),
+(102032, 102, '鏅哄埄', '+56', '鏅哄埄', 32, 1, NOW(), 1, NOW()),
+(102033, 102, '闃挎牴寤�', '+54', '闃挎牴寤�', 33, 1, NOW(), 1, NOW()),
+(102034, 102, '鍩冨強', '+20', '鍩冨強', 34, 1, NOW(), 1, NOW()),
+(102035, 102, '鍗楅潪', '+27', '鍗楅潪', 35, 1, NOW(), 1, NOW()),
+(102036, 102, '鑲凹浜�', '+254', '鑲凹浜�', 36, 1, NOW(), 1, NOW()),
+(102037, 102, '鍧︽灏间簹', '+255', '鍧︽灏间簹', 37, 1, NOW(), 1, NOW()),
+(102038, 102, '鍝堣惃鍏嬫柉鍧�', '+7', '鍝堣惃鍏嬫柉鍧�', 38, 1, NOW(), 1, NOW());
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505201744.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505201744.sql
new file mode 100644
index 0000000..7b31b1c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505201744.sql
@@ -0,0 +1,3 @@
+-- 鏇存柊ai_model_provider鐨刦ields瀛楁锛屽皢type涓篸ict鐨勬敼涓簊tring
+update ai_model_provider set fields = replace(fields, '"type": "dict"', '"type": "string"') where id not in ('SYSTEM_LLM_fastgpt', 'SYSTEM_TTS_custom');
+update ai_model_provider set fields = replace(fields, '"type":"dict"', '"type": "string"') where id not in ('SYSTEM_LLM_fastgpt', 'SYSTEM_TTS_custom');
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505271414.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505271414.sql
new file mode 100644
index 0000000..588cd13
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505271414.sql
@@ -0,0 +1,51 @@
+-- 鏈湴鐭湡璁板繂閰嶇疆鍙互璁剧疆鐙珛鐨凩LM
+update `ai_model_provider` set fields = '[{"key":"llm","label":"LLM妯″瀷","type":"string"}]' where id = 'SYSTEM_Memory_mem_local_short';
+update `ai_model_config` set config_json = '{\"type\": \"mem_local_short\", \"llm\": \"LLM_ChatGLMLLM\"}' where id = 'Memory_mem_local_short';
+
+-- 澧炲姞鐏北鍙屾祦寮廡TS渚涘簲鍣ㄥ拰妯″瀷閰嶇疆
+delete from `ai_model_provider` where id = 'SYSTEM_TTS_HSDSTTS';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_TTS_HSDSTTS', 'TTS', 'huoshan_double_stream', '鐏北鍙屾祦寮忚闊冲悎鎴�', '[{"key":"ws_url","label":"WebSocket鍦板潃","type":"string"},{"key":"appid","label":"搴旂敤ID","type":"string"},{"key":"access_token","label":"璁块棶浠ょ墝","type":"string"},{"key":"resource_id","label":"璧勬簮ID","type":"string"},{"key":"speaker","label":"榛樿闊宠壊","type":"string"}]', 13, 1, NOW(), 1, NOW());
+
+delete from `ai_model_config` where id = 'TTS_HuoshanDoubleStreamTTS';
+INSERT INTO `ai_model_config` VALUES ('TTS_HuoshanDoubleStreamTTS', 'TTS', 'HuoshanDoubleStreamTTS', '鐏北鍙屾祦寮忚闊冲悎鎴�', 0, 1, '{\"type\": \"huoshan_double_stream\", \"ws_url\": \"wss://openspeech.bytedance.com/api/v3/tts/bidirection\", \"appid\": \"浣犵殑鐏北寮曟搸璇煶鍚堟垚鏈嶅姟appid\", \"access_token\": \"浣犵殑鐏北寮曟搸璇煶鍚堟垚鏈嶅姟access_token\", \"resource_id\": \"volc.service_type.10029\", \"speaker\": \"zh_female_wanwanxiaohe_moon_bigtts\"}', NULL, NULL, 16, NULL, NULL, NULL, NULL);
+
+-- 鐏北鍙屾祦寮廡T妯″瀷閰嶇疆璇存槑鏂囨。
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://console.volcengine.com/speech/service/10007',
+`remark` = '鐏北寮曟搸璇煶鍚堟垚鏈嶅姟閰嶇疆璇存槑锛�
+1. 璁块棶 https://www.volcengine.com/ 娉ㄥ唽骞跺紑閫氱伀灞卞紩鎿庤处鍙�
+2. 璁块棶 https://console.volcengine.com/speech/service/10007 寮�閫氳闊冲悎鎴愬ぇ妯″瀷锛岃喘涔伴煶鑹�
+3. 鍦ㄩ〉闈㈠簳閮ㄨ幏鍙朼ppid鍜宎ccess_token
+5. 璧勬簮ID鍥哄畾涓猴細volc.service_type.10029锛堝ぇ妯″瀷璇煶鍚堟垚鍙婃贩闊筹級
+6. 濉叆閰嶇疆鏂囦欢涓�' WHERE `id` = 'TTS_HuoshanDoubleStreamTTS';
+
+
+-- 娣诲姞鐏北鍙屾祦寮廡TS闊宠壊
+delete from `ai_tts_voice` where tts_model_id = 'TTS_HuoshanDoubleStreamTTS';
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0001', 'TTS_HuoshanDoubleStreamTTS', '鐖藉揩鎬濇��/Skye', 'zh_female_shuangkuaisisi_moon_bigtts', '涓枃銆佽嫳鏂�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/Skye.mp3', NULL, 1, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0002', 'TTS_HuoshanDoubleStreamTTS', '娓╂殩闃胯檸/Alvin', 'zh_male_wennuanahu_moon_bigtts', '涓枃銆佽嫳鏂�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/Alvin.mp3', NULL, 2, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0003', 'TTS_HuoshanDoubleStreamTTS', '灏戝勾姊撹緵/Brayan', 'zh_male_shaonianzixin_moon_bigtts', '涓枃銆佽嫳鏂�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/Brayan.mp3', NULL, 3, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0004', 'TTS_HuoshanDoubleStreamTTS', '閭诲濂冲', 'zh_female_linjianvhai_moon_bigtts', '涓枃', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E9%82%BB%E5%AE%B6%E5%A5%B3%E5%AD%A9.mp3', NULL, 4, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0005', 'TTS_HuoshanDoubleStreamTTS', '娓婂崥灏忓彅', 'zh_male_yuanboxiaoshu_moon_bigtts', '涓枃', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E6%B8%8A%E5%8D%9A%E5%B0%8F%E5%8F%94.mp3', NULL, 5, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0006', 'TTS_HuoshanDoubleStreamTTS', '闃冲厜闈掑勾', 'zh_male_yangguangqingnian_moon_bigtts', '涓枃', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E9%98%B3%E5%85%89%E9%9D%92%E5%B9%B4.mp3', NULL, 6, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0007', 'TTS_HuoshanDoubleStreamTTS', '浜厰渚冪埛/Harmony', 'zh_male_jingqiangkanye_moon_bigtts', '涓枃銆佽嫳鏂�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/Harmony.mp3', NULL, 7, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0008', 'TTS_HuoshanDoubleStreamTTS', '婀炬咕灏忎綍', 'zh_female_wanwanxiaohe_moon_bigtts', '涓枃', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E6%B9%BE%E6%B9%BE%E5%B0%8F%E4%BD%95.mp3', NULL, 8, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0009', 'TTS_HuoshanDoubleStreamTTS', '婀惧尯澶у彅', 'zh_female_wanqudashu_moon_bigtts', '涓枃', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E6%B9%BE%E5%8C%BA%E5%A4%A7%E5%8F%94.mp3', NULL, 9, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0010', 'TTS_HuoshanDoubleStreamTTS', '鍛嗚悓宸濆', 'zh_female_daimengchuanmei_moon_bigtts', '涓枃', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E5%91%86%E8%90%8C%E5%B7%9D%E5%A6%B9.mp3', NULL, 10, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0011', 'TTS_HuoshanDoubleStreamTTS', '骞垮窞寰峰摜', 'zh_male_guozhoudege_moon_bigtts', '涓枃', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E5%B9%BF%E5%B7%9E%E5%BE%B7%E5%93%A5.mp3', NULL, 11, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0012', 'TTS_HuoshanDoubleStreamTTS', '鍖椾含灏忕埛', 'zh_male_beijingxiaoye_moon_bigtts', '涓枃', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E5%8C%97%E4%BA%AC%E5%B0%8F%E7%88%B7.mp3', NULL, 12, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0013', 'TTS_HuoshanDoubleStreamTTS', '娴╁畤灏忓摜', 'zh_male_haoyuxiaoge_moon_bigtts', '涓枃', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E6%B5%A9%E5%AE%87%E5%B0%8F%E5%93%A5.mp3', NULL, 13, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0014', 'TTS_HuoshanDoubleStreamTTS', '骞胯タ杩滆垷', 'zh_male_guangxiyuanzhou_moon_bigtts', '涓枃', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E5%B9%BF%E8%A5%BF%E8%BF%9C%E8%88%9F.mp3', NULL, 14, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0015', 'TTS_HuoshanDoubleStreamTTS', '濡瑰潹娲佸効', 'zh_female_meituojieer_moon_bigtts', '涓枃', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E5%A6%B9%E5%9D%A8%E6%B4%81%E5%84%BF.mp3', NULL, 15, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0016', 'TTS_HuoshanDoubleStreamTTS', '璞窞瀛愯僵', 'zh_male_yuzhouzixuan_moon_bigtts', '涓枃', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E8%B1%AB%E5%B7%9E%E5%AD%90%E8%BD%A9.mp3', NULL, 16, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0017', 'TTS_HuoshanDoubleStreamTTS', '楂樺喎寰″', 'zh_female_gaolengyujie_moon_bigtts', '涓枃', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E9%AB%98%E5%86%B7%E5%BE%A1%E5%A7%90.mp3', NULL, 17, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0018', 'TTS_HuoshanDoubleStreamTTS', '鍌插▏闇告��', 'zh_male_aojiaobazong_moon_bigtts', '涓枃', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E5%82%B2%E5%A8%87%E9%9C%B8%E6%80%BB.mp3', NULL, 18, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0019', 'TTS_HuoshanDoubleStreamTTS', '榄呭姏濂冲弸', 'zh_female_meilinvyou_moon_bigtts', '涓枃', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E9%AD%85%E5%8A%9B%E5%A5%B3%E5%8F%8B.mp3', NULL, 19, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0020', 'TTS_HuoshanDoubleStreamTTS', '娣卞鎾', 'zh_male_shenyeboke_moon_bigtts', '涓枃', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E6%B7%B1%E5%A4%9C%E6%92%AD%E5%AE%A2.mp3', NULL, 20, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0021', 'TTS_HuoshanDoubleStreamTTS', '鏌旂編濂冲弸', 'zh_female_sajiaonvyou_moon_bigtts', '涓枃', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E6%9F%94%E7%BE%8E%E5%A5%B3%E5%8F%8B.mp3', NULL, 21, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0022', 'TTS_HuoshanDoubleStreamTTS', '鎾掑▏瀛﹀', 'zh_female_yuanqinvyou_moon_bigtts', '涓枃', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E6%92%92%E5%A8%87%E5%AD%A6%E5%A6%B9.mp3', NULL, 22, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0023', 'TTS_HuoshanDoubleStreamTTS', '銇嬨仛銇紙鍜岄煶锛�', 'multi_male_jingqiangkanye_moon_bigtts', '鏃ヨ銆佽タ璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/Javier.wav', NULL, 23, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0024', 'TTS_HuoshanDoubleStreamTTS', '銇倠銇擄紙鏅村瓙锛�', 'multi_female_shuangkuaisisi_moon_bigtts', '鏃ヨ銆佽タ璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/Esmeralda.mp3', NULL, 24, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0025', 'TTS_HuoshanDoubleStreamTTS', '銇傘亼銇匡紙鏈辩編锛�', 'multi_female_gaolengyujie_moon_bigtts', '鏃ヨ', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E6%9C%B1%E7%BE%8E.mp3', NULL, 25, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HuoshanDoubleStreamTTS_0026', 'TTS_HuoshanDoubleStreamTTS', '銇层倣銇楋紙搴冨織锛�', 'multi_male_wanqudashu_moon_bigtts', '鏃ヨ銆佽タ璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/Roberto.wav', NULL, 26, NULL, NULL, NULL, NULL);
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505292203.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505292203.sql
new file mode 100644
index 0000000..4102c7f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202505292203.sql
@@ -0,0 +1,189 @@
+-- ===============================
+-- 涓�銆佸湪ai_model_provider涓彃鍏lugin 璁板綍
+-- ===============================
+START TRANSACTION;
+
+
+-- intent_llm鍜宖unction_call涓嶈缃嚱鏁板垪琛�
+update `ai_model_provider` set fields = '[{"key":"llm","label":"LLM妯″瀷","type":"string"}]' where id = 'SYSTEM_Intent_intent_llm';
+update `ai_model_provider` set fields = '[]' where id = 'SYSTEM_Intent_function_call';
+update `ai_model_config` set config_json = '{\"type\": \"intent_llm\", \"llm\": \"LLM_ChatGLMLLM\"}' where id = 'Intent_intent_llm';
+UPDATE `ai_model_config` SET config_json = '{\"type\": \"function_call\"}' WHERE id = 'Intent_function_call';
+
+
+delete from ai_model_provider where model_type = 'Plugin';
+-- 1. 澶╂皵鏌ヨ
+INSERT INTO ai_model_provider (id, model_type, provider_code, name, fields,
+ sort, creator, create_date, updater, update_date)
+VALUES ('SYSTEM_PLUGIN_WEATHER',
+ 'Plugin',
+ 'get_weather',
+ '澶╂皵鏌ヨ',
+ JSON_ARRAY(
+ JSON_OBJECT(
+ 'key', 'api_key',
+ 'type', 'string',
+ 'label', '澶╂皵鎻掍欢 API 瀵嗛挜',
+ 'default', (SELECT param_value FROM sys_params WHERE param_code = 'plugins.get_weather.api_key')
+ ),
+ JSON_OBJECT(
+ 'key', 'default_location',
+ 'type', 'string',
+ 'label', '榛樿鏌ヨ鍩庡競',
+ 'default',
+ (SELECT param_value FROM sys_params WHERE param_code = 'plugins.get_weather.default_location')
+ ),
+ JSON_OBJECT(
+ 'key', 'api_host',
+ 'type', 'string',
+ 'label', '寮�鍙戣�� API Host',
+ 'default',
+ (SELECT param_value FROM sys_params WHERE param_code = 'plugins.get_weather.api_host')
+ )
+ ),
+ 10, 0, NOW(), 0, NOW());
+
+-- 6. 鏈湴鎾斁闊充箰
+INSERT INTO ai_model_provider (id, model_type, provider_code, name, fields,
+ sort, creator, create_date, updater, update_date)
+VALUES ('SYSTEM_PLUGIN_MUSIC',
+ 'Plugin',
+ 'play_music',
+ '鏈嶅姟鍣ㄩ煶涔愭挱鏀�',
+ JSON_ARRAY(),
+ 20, 0, NOW(), 0, NOW());
+
+-- 2. 鏂伴椈璁㈤槄
+INSERT INTO ai_model_provider (id, model_type, provider_code, name, fields,
+ sort, creator, create_date, updater, update_date)
+VALUES ('SYSTEM_PLUGIN_NEWS_CHINANEWS',
+ 'Plugin',
+ 'get_news_from_chinanews',
+ '涓柊缃戞柊闂�',
+ JSON_ARRAY(
+ JSON_OBJECT(
+ 'key', 'default_rss_url',
+ 'type', 'string',
+ 'label', '榛樿 RSS 婧�',
+ 'default',
+ (SELECT param_value FROM sys_params WHERE param_code = 'plugins.get_news.default_rss_url')
+ ),
+ JSON_OBJECT(
+ 'key', 'society_rss_url',
+ 'type', 'string',
+ 'label', '绀句細鏂伴椈 RSS 鍦板潃',
+ 'default',
+ 'https://www.chinanews.com.cn/rss/society.xml'
+ ),
+ JSON_OBJECT(
+ 'key', 'world_rss_url',
+ 'type', 'string',
+ 'label', '鍥介檯鏂伴椈 RSS 鍦板潃',
+ 'default',
+ 'https://www.chinanews.com.cn/rss/world.xml'
+ ),
+ JSON_OBJECT(
+ 'key', 'finance_rss_url',
+ 'type', 'string',
+ 'label', '璐㈢粡鏂伴椈 RSS 鍦板潃',
+ 'default',
+ 'https://www.chinanews.com.cn/rss/finance.xml'
+ )
+ ),
+ 30, 0, NOW(), 0, NOW());
+
+-- 3. 鏂伴椈璁㈤槄
+INSERT INTO ai_model_provider (id, model_type, provider_code, name, fields,
+ sort, creator, create_date, updater, update_date)
+VALUES ('SYSTEM_PLUGIN_NEWS_NEWSNOW',
+ 'Plugin',
+ 'get_news_from_newsnow',
+ 'newsnow鏂伴椈鑱氬悎',
+ JSON_ARRAY(
+ JSON_OBJECT(
+ 'key', 'url',
+ 'type', 'string',
+ 'label', '鎺ュ彛鍦板潃',
+ 'default',
+ 'https://newsnow.busiyi.world/api/s?id='
+ )
+ ),
+ 40, 0, NOW(), 0, NOW());
+
+
+-- 4. HomeAssistant 鐘舵�佹煡璇�
+INSERT INTO ai_model_provider (id, model_type, provider_code, name, fields,
+ sort, creator, create_date, updater, update_date)
+VALUES ('SYSTEM_PLUGIN_HA_GET_STATE',
+ 'Plugin',
+ 'hass_get_state',
+ 'HomeAssistant璁惧鐘舵�佹煡璇�',
+ JSON_ARRAY(
+ JSON_OBJECT(
+ 'key', 'base_url',
+ 'type', 'string',
+ 'label', 'HA 鏈嶅姟鍣ㄥ湴鍧�',
+ 'default',
+ (SELECT param_value FROM sys_params WHERE param_code = 'plugins.home_assistant.base_url')
+ ),
+ JSON_OBJECT(
+ 'key', 'api_key',
+ 'type', 'string',
+ 'label', 'HA API 璁块棶浠ょ墝',
+ 'default',
+ (SELECT param_value FROM sys_params WHERE param_code = 'plugins.home_assistant.api_key')
+ ),
+ JSON_OBJECT(
+ 'key', 'devices',
+ 'type', 'array',
+ 'label', '璁惧鍒楄〃锛堝悕绉�,瀹炰綋ID;鈥︼級',
+ 'default',
+ (SELECT param_value FROM sys_params WHERE param_code = 'plugins.home_assistant.devices')
+ )
+ ),
+ 50, 0, NOW(), 0, NOW());
+
+-- 5. HomeAssistant 鐘舵�佸啓鍏�
+INSERT INTO ai_model_provider (id, model_type, provider_code, name, fields,
+ sort, creator, create_date, updater, update_date)
+VALUES ('SYSTEM_PLUGIN_HA_SET_STATE',
+ 'Plugin',
+ 'hass_set_state',
+ 'HomeAssistant璁惧鐘舵�佷慨鏀�',
+ JSON_ARRAY(),
+ 60, 0, NOW(), 0, NOW());
+
+-- 5. HomeAssistant 闊充箰鎾斁
+INSERT INTO ai_model_provider (id, model_type, provider_code, name, fields,
+ sort, creator, create_date, updater, update_date)
+VALUES ('SYSTEM_PLUGIN_HA_PLAY_MUSIC',
+ 'Plugin',
+ 'hass_play_music',
+ 'HomeAssistant闊充箰鎾斁',
+ JSON_ARRAY(),
+ 70, 0, NOW(), 0, NOW());
+
+
+-- ===============================
+-- 浜屻�佸垹闄ys_params涓棫鐨刾lugins.*鍙傛暟
+-- ===============================
+DELETE
+FROM sys_params
+WHERE param_code LIKE 'plugins.%';
+
+
+-- ===============================
+-- 涓夈�佹坊鍔犳櫤鑳戒綋鎻掍欢id瀛楁
+-- ===============================
+CREATE TABLE IF NOT EXISTS ai_agent_plugin_mapping
+(
+ id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '涓婚敭',
+ agent_id VARCHAR(32) NOT NULL COMMENT '鏅鸿兘浣揑D',
+ plugin_id VARCHAR(32) NOT NULL COMMENT '鎻掍欢ID',
+ param_info JSON NOT NULL COMMENT '鍙傛暟淇℃伅',
+ UNIQUE KEY uk_agent_provider (agent_id, plugin_id)
+) COMMENT 'Agent涓庢彃浠剁殑鍞竴鏄犲皠琛�';
+
+
+COMMIT;
+
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506010920.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506010920.sql
new file mode 100644
index 0000000..79898ee
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506010920.sql
@@ -0,0 +1,29 @@
+-- VLLM妯″瀷渚涘簲鍣�
+delete from `ai_model_provider` where id = 'SYSTEM_VLLM_openai';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_VLLM_openai', 'VLLM', 'openai', 'OpenAI鎺ュ彛', '[{"key":"base_url","label":"鍩虹URL","type":"string"},{"key":"model_name","label":"妯″瀷鍚嶇О","type":"string"},{"key":"api_key","label":"API瀵嗛挜","type":"string"}]', 9, 1, NOW(), 1, NOW());
+
+-- VLLM妯″瀷閰嶇疆
+delete from `ai_model_config` where id = 'VLLM_ChatGLMVLLM';
+INSERT INTO `ai_model_config` VALUES ('VLLM_ChatGLMVLLM', 'VLLM', 'ChatGLMVLLM', '鏅鸿氨瑙嗚AI', 1, 1, '{\"type\": \"openai\", \"model_name\": \"glm-4v-flash\", \"base_url\": \"https://open.bigmodel.cn/api/paas/v4/\", \"api_key\": \"浣犵殑api_key\"}', NULL, NULL, 1, NULL, NULL, NULL, NULL);
+
+-- 鏇存柊鏂囨。
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://bigmodel.cn/usercenter/proj-mgmt/apikeys',
+`remark` = '鏅鸿氨瑙嗚AI閰嶇疆璇存槑锛�
+1. 璁块棶 https://bigmodel.cn/usercenter/proj-mgmt/apikeys
+2. 娉ㄥ唽骞惰幏鍙朅PI瀵嗛挜
+3. 濉叆閰嶇疆鏂囦欢涓�' WHERE `id` = 'VLLM_ChatGLMVLLM';
+
+
+-- 娣诲姞鍙傛暟
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (113, 'server.http_port', '8003', 'number', 1, 'http鏈嶅姟鐨勭鍙o紝鐢ㄤ簬鍚姩瑙嗚鍒嗘瀽鎺ュ彛');
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (114, 'server.vision_explain', 'null', 'string', 1, '瑙嗚鍒嗘瀽鎺ュ彛鍦板潃锛岀敤浜庝笅鍙戝埌璁惧锛屽涓敤;鍒嗛殧');
+
+-- 鏅鸿兘浣撹〃澧炲姞VLLM妯″瀷閰嶇疆
+ALTER TABLE `ai_agent`
+ADD COLUMN `vllm_model_id` varchar(32) NULL DEFAULT 'VLLM_ChatGLMVLLM' COMMENT '瑙嗚妯″瀷鏍囪瘑' AFTER `llm_model_id`;
+
+-- 鏅鸿兘浣撴ā鐗堣〃澧炲姞VLLM妯″瀷閰嶇疆
+ALTER TABLE `ai_agent_template`
+ADD COLUMN `vllm_model_id` varchar(32) NULL DEFAULT 'VLLM_ChatGLMVLLM' COMMENT '瑙嗚妯″瀷鏍囪瘑' AFTER `llm_model_id`;
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506031639.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506031639.sql
new file mode 100644
index 0000000..95691b4
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506031639.sql
@@ -0,0 +1,45 @@
+-- VLLM妯″瀷渚涘簲鍣�
+delete from `ai_model_provider` where id = 'SYSTEM_ASR_DoubaoStreamASR';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_ASR_DoubaoStreamASR', 'ASR', 'doubao_stream', '鐏北寮曟搸璇煶璇嗗埆(娴佸紡)', '[{"key":"appid","label":"搴旂敤ID","type":"string"},{"key":"access_token","label":"璁块棶浠ょ墝","type":"string"},{"key":"cluster","label":"闆嗙兢","type":"string"},{"key":"boosting_table_name","label":"鐑瘝鏂囦欢鍚嶇О","type":"string"},{"key":"correct_table_name","label":"鏇挎崲璇嶆枃浠跺悕绉�","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"}]', 3, 1, NOW(), 1, NOW());
+
+
+-- VLLM妯″瀷閰嶇疆
+delete from `ai_model_config` where id = 'ASR_DoubaoStreamASR';
+INSERT INTO `ai_model_config` VALUES ('ASR_DoubaoStreamASR', 'ASR', 'DoubaoStreamASR', '璞嗗寘璇煶璇嗗埆(娴佸紡)', 0, 1, '{\"type\": \"doubao_stream\", \"appid\": \"\", \"access_token\": \"\", \"cluster\": \"volcengine_input_common\", \"output_dir\": \"tmp/\"}', NULL, NULL, 3, NULL, NULL, NULL, NULL);
+
+
+-- 鏇存柊璞嗗寘ASR閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://console.volcengine.com/speech/app',
+`remark` = '璞嗗寘ASR閰嶇疆璇存槑锛�
+1. 璞嗗寘ASR鍜岃眴鍖�(娴佸紡)ASR鐨勫尯鍒槸锛氳眴鍖匒SR鏄寜娆℃敹璐癸紝璞嗗寘(娴佸紡)ASR鏄寜鏃舵敹璐�
+2. 涓�鑸潵璇存寜娆℃敹璐圭殑鏇翠究瀹滐紝浣嗘槸璞嗗寘(娴佸紡)ASR浣跨敤浜嗗ぇ妯″瀷鎶�鏈紝鏁堟灉鏇村ソ
+3. 闇�瑕佸湪鐏北寮曟搸鎺у埗鍙板垱寤哄簲鐢ㄥ苟鑾峰彇appid鍜宎ccess_token
+4. 鏀寔涓枃璇煶璇嗗埆
+5. 闇�瑕佺綉缁滆繛鎺�
+6. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍
+鐢宠姝ラ锛�
+1. 璁块棶 https://console.volcengine.com/speech/app
+2. 鍒涘缓鏂板簲鐢�
+3. 鑾峰彇appid鍜宎ccess_token
+4. 濉叆閰嶇疆鏂囦欢涓�
+濡傞渶璁剧疆鐑瘝锛岃鍙傝�冿細https://www.volcengine.com/docs/6561/155738
+' WHERE `id` = 'ASR_DoubaoASR';
+
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://console.volcengine.com/speech/app',
+`remark` = '璞嗗寘ASR閰嶇疆璇存槑锛�
+1. 璞嗗寘ASR鍜岃眴鍖�(娴佸紡)ASR鐨勫尯鍒槸锛氳眴鍖匒SR鏄寜娆℃敹璐癸紝璞嗗寘(娴佸紡)ASR鏄寜鏃舵敹璐�
+2. 涓�鑸潵璇存寜娆℃敹璐圭殑鏇翠究瀹滐紝浣嗘槸璞嗗寘(娴佸紡)ASR浣跨敤浜嗗ぇ妯″瀷鎶�鏈紝鏁堟灉鏇村ソ
+3. 闇�瑕佸湪鐏北寮曟搸鎺у埗鍙板垱寤哄簲鐢ㄥ苟鑾峰彇appid鍜宎ccess_token
+4. 鏀寔涓枃璇煶璇嗗埆
+5. 闇�瑕佺綉缁滆繛鎺�
+6. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍
+鐢宠姝ラ锛�
+1. 璁块棶 https://console.volcengine.com/speech/app
+2. 鍒涘缓鏂板簲鐢�
+3. 鑾峰彇appid鍜宎ccess_token
+4. 濉叆閰嶇疆鏂囦欢涓�
+濡傞渶璁剧疆鐑瘝锛岃鍙傝�冿細https://www.volcengine.com/docs/6561/155738
+' WHERE `id` = 'ASR_DoubaoStreamASR';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506032232.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506032232.sql
new file mode 100644
index 0000000..6161198
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506032232.sql
@@ -0,0 +1,14 @@
+-- VLLM妯″瀷閰嶇疆
+delete from `ai_model_config` where id = 'VLLM_QwenVLVLLM';
+INSERT INTO `ai_model_config` VALUES ('VLLM_QwenVLVLLM', 'VLLM', 'QwenVLVLLM', '鍗冮棶瑙嗚妯″瀷', 0, 1, '{\"type\": \"openai\", \"model_name\": \"qwen2.5-vl-3b-instruct\", \"base_url\": \"https://dashscope.aliyuncs.com/compatible-mode/v1\", \"api_key\": \"浣犵殑api_key\"}', NULL, NULL, 2, NULL, NULL, NULL, NULL);
+
+-- 鏇存柊鏂囨。
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://bailian.console.aliyun.com/?tab=api#/api/?type=model&url=https%3A%2F%2Fhelp.aliyun.com%2Fdocument_detail%2F2845564.html&renderType=iframe',
+`remark` = '鍗冮棶瑙嗚妯″瀷閰嶇疆璇存槑锛�
+1. 璁块棶 https://bailian.console.aliyun.com/?tab=model#/api-key
+2. 娉ㄥ唽骞惰幏鍙朅PI瀵嗛挜
+3. 濉叆閰嶇疆鏂囦欢涓�' WHERE `id` = 'VLLM_QwenVLVLLM';
+
+-- 鍒犻櫎鍙傛暟锛岃繖涓や釜鍙傛暟宸叉尓鑷硃ython閰嶇疆鏂囦欢
+delete from `sys_params` where id in (113,114);
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506051538.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506051538.sql
new file mode 100644
index 0000000..cfe65da
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506051538.sql
@@ -0,0 +1,20 @@
+-- 澧炲姞LinkeraiTTS渚涘簲鍣ㄥ拰妯″瀷閰嶇疆
+delete from `ai_model_provider` where id = 'SYSTEM_TTS_LinkeraiTTS';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_TTS_LinkeraiTTS', 'TTS', 'linkerai', 'Linkerai璇煶鍚堟垚', '[{"key":"api_url","label":"API鍦板潃","type":"string"},{"key":"audio_format","label":"闊抽鏍煎紡","type":"string"},{"key":"access_token","label":"璁块棶浠ょ墝","type":"string"},{"key":"voice","label":"榛樿闊宠壊","type":"string"}]', 14, 1, NOW(), 1, NOW());
+
+delete from `ai_model_config` where id = 'TTS_LinkeraiTTS';
+INSERT INTO `ai_model_config` VALUES ('TTS_LinkeraiTTS', 'TTS', 'LinkeraiTTS', 'Linkerai璇煶鍚堟垚', 0, 1, '{\"type\": \"linkerai\", \"api_url\": \"https://tts.linkerai.cn/tts\", \"audio_format\": \"pcm\", \"access_token\": \"U4YdYXVfpwWnk2t5Gp822zWPCuORyeJL\", \"voice\": \"OUeAo1mhq6IBExi\"}', NULL, NULL, 17, NULL, NULL, NULL, NULL);
+
+-- LinkeraiTTS妯″瀷閰嶇疆璇存槑鏂囨。
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://tts.linkerai.cn/docs',
+`remark` = 'Linkerai璇煶鍚堟垚鏈嶅姟閰嶇疆璇存槑锛�
+1. 璁块棶 https://linkerai.cn 娉ㄥ唽骞惰幏鍙栬闂护鐗�
+2. 榛樿鐨刟ccess_token渚涙祴璇曚娇鐢紝璇峰嬁鐢ㄤ簬鍟嗕笟鐢ㄩ��
+3. 鏀寔澹伴煶鍏嬮殕鍔熻兘锛屽彲鑷涓婁紶闊抽锛屽~鍏oice鍙傛暟
+4. 濡傛灉voice鍙傛暟涓虹┖锛屽皢浣跨敤榛樿澹伴煶' WHERE `id` = 'TTS_LinkeraiTTS';
+
+
+delete from `ai_tts_voice` where tts_model_id = 'TTS_LinkeraiTTS';
+INSERT INTO `ai_tts_voice` VALUES ('TTS_LinkeraiTTS_0001', 'TTS_LinkeraiTTS', '鑺疯嫢', 'OUeAo1mhq6IBExi', '涓枃', NULL, NULL, 1, NULL, NULL, NULL, NULL);
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506080955.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506080955.sql
new file mode 100644
index 0000000..5f5e799
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506080955.sql
@@ -0,0 +1,3 @@
+-- 鏅烘帶鍙板紑鍚敜閱掕瘝鍔犻��
+update `sys_params` set param_value = '浣犲ソ灏忔櫤;浣犲ソ灏忓織;灏忕埍鍚屽;浣犲ソ灏忛懌;浣犲ソ灏忔柊;灏忕編鍚屽;灏忛緳灏忛緳;鍠靛柕鍚屽;灏忔花灏忔花;灏忓啺灏忓啺;鍢夸綘濂藉憖' where param_code = 'wakeup_words';
+update `sys_params` set param_value = 'true' where param_code = 'enable_wakeup_words_response_cache';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506091720.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506091720.sql
new file mode 100644
index 0000000..97b457e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506091720.sql
@@ -0,0 +1,3 @@
+ALTER TABLE `ai_tts_voice`
+ADD COLUMN `reference_audio` VARCHAR(500) DEFAULT NULL COMMENT '鍙傝�冮煶棰戣矾寰�' AFTER `remark`,
+ADD COLUMN `reference_text` VARCHAR(500) DEFAULT NULL COMMENT '鍙傝�冩枃鏈�' AFTER `reference_audio`;
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506161101.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506161101.sql
new file mode 100644
index 0000000..fcfff08
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506161101.sql
@@ -0,0 +1 @@
+ALTER TABLE ai_agent_plugin_mapping CONVERT TO CHARACTER SET utf8mb4;
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506191643.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506191643.sql
new file mode 100644
index 0000000..2055906
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506191643.sql
@@ -0,0 +1,19 @@
+-- LLM鎰忓浘璇嗗埆閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = NULL,
+`remark` = 'LLM鎰忓浘璇嗗埆閰嶇疆璇存槑锛�
+1. 浣跨敤鐙珛鐨凩LM杩涜鎰忓浘璇嗗埆
+2. 榛樿浣跨敤selected_module.LLM鐨勬ā鍨�
+3. 鍙互閰嶇疆浣跨敤鐙珛鐨凩LM锛堝鍏嶈垂鐨凜hatGLMLLM锛�
+4. 閫氱敤鎬у己锛屼絾浼氬鍔犲鐞嗘椂闂�
+閰嶇疆璇存槑锛�
+1. 鍦╨lm瀛楁涓寚瀹氫娇鐢ㄧ殑LLM妯″瀷
+2. 濡傛灉涓嶆寚瀹氾紝鍒欎娇鐢╯elected_module.LLM鐨勬ā鍨�' WHERE `id` = 'Intent_intent_llm';
+
+-- 鍑芥暟璋冪敤鎰忓浘璇嗗埆閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = NULL,
+`remark` = '鍑芥暟璋冪敤鎰忓浘璇嗗埆閰嶇疆璇存槑锛�
+1. 浣跨敤LLM鐨刦unction_call鍔熻兘杩涜鎰忓浘璇嗗埆
+2. 闇�瑕佹墍閫夋嫨鐨凩LM鏀寔function_call
+3. 鎸夐渶璋冪敤宸ュ叿锛屽鐞嗛�熷害蹇�' WHERE `id` = 'Intent_function_call';
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506251620.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506251620.sql
new file mode 100644
index 0000000..9af9acd
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506251620.sql
@@ -0,0 +1,18 @@
+-- 鏇存柊鐜版湁鐨� get_news_from_newsnow 鎻掍欢閰嶇疆
+UPDATE ai_model_provider
+SET fields = JSON_ARRAY(
+ JSON_OBJECT(
+ 'key', 'url',
+ 'type', 'string',
+ 'label', '鎺ュ彛鍦板潃',
+ 'default', 'https://newsnow.busiyi.world/api/s?id='
+ ),
+ JSON_OBJECT(
+ 'key', 'news_sources',
+ 'type', 'string',
+ 'label', '鏂伴椈婧愰厤缃�',
+ 'default', '婢庢箖鏂伴椈;鐧惧害鐑悳;璐㈣仈绀�'
+ )
+)
+WHERE provider_code = 'get_news_from_newsnow'
+AND model_type = 'Plugin';
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506261637.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506261637.sql
new file mode 100644
index 0000000..792e00c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202506261637.sql
@@ -0,0 +1,2 @@
+delete from `sys_params` where id = 113;
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (113, 'server.mcp_endpoint', 'null', 'string', 1, 'mcp鎺ュ叆鐐瑰湴鍧�');
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507031602.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507031602.sql
new file mode 100644
index 0000000..92edf9b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507031602.sql
@@ -0,0 +1,4 @@
+-- 娣诲姞澹扮汗鎺ュ彛鍦板潃鍙傛暟閰嶇疆
+delete from `sys_params` where id = 114;
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark)
+VALUES (114, 'server.voice_print', 'null', 'string', 1, '澹扮汗鎺ュ彛鍦板潃');
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507041018.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507041018.sql
new file mode 100644
index 0000000..cffcfb1
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507041018.sql
@@ -0,0 +1,12 @@
+DROP TABLE IF EXISTS ai_agent_voice_print;
+create table ai_agent_voice_print (
+ id varchar(32) NOT NULL COMMENT '澹扮汗ID',
+ agent_id varchar(32) NOT NULL COMMENT '鍏宠仈鐨勬櫤鑳戒綋ID',
+ source_name varchar(50) NOT NULL COMMENT '澹扮汗鏉ユ簮鐨勪汉鐨勫鍚�',
+ introduce varchar(200) COMMENT '鎻忚堪澹扮汗鏉ユ簮鐨勮繖涓汉',
+ create_date DATETIME COMMENT '鍒涘缓鏃堕棿',
+ creator bigint COMMENT '鍒涘缓鑰�',
+ update_date DATETIME COMMENT '淇敼鏃堕棿',
+ updater bigint COMMENT '淇敼鑰�',
+ PRIMARY KEY (id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鏅鸿兘浣撳0绾硅〃'
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507071130.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507071130.sql
new file mode 100644
index 0000000..c3730ef
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507071130.sql
@@ -0,0 +1,26 @@
+-- 娣诲姞闃块噷浜戞祦寮廇SR渚涘簲鍣�
+delete from `ai_model_provider` where id = 'SYSTEM_ASR_AliyunStreamASR';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_ASR_AliyunStreamASR', 'ASR', 'aliyun_stream', '闃块噷浜戣闊宠瘑鍒�(娴佸紡)', '[{"key":"appkey","label":"搴旂敤AppKey","type":"string"},{"key":"token","label":"涓存椂Token","type":"string"},{"key":"access_key_id","label":"AccessKey ID","type":"string"},{"key":"access_key_secret","label":"AccessKey Secret","type":"string"},{"key":"host","label":"鏈嶅姟鍦板潃","type":"string"},{"key":"max_sentence_silence","label":"鏂彞妫�娴嬫椂闂�","type":"number"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"}]', 6, 1, NOW(), 1, NOW());
+
+-- 娣诲姞闃块噷浜戞祦寮廇SR妯″瀷閰嶇疆
+delete from `ai_model_config` where id = 'ASR_AliyunStreamASR';
+INSERT INTO `ai_model_config` VALUES ('ASR_AliyunStreamASR', 'ASR', 'AliyunStreamASR', '闃块噷浜戣闊宠瘑鍒�(娴佸紡)', 0, 1, '{\"type\": \"aliyun_stream\", \"appkey\": \"\", \"token\": \"\", \"access_key_id\": \"\", \"access_key_secret\": \"\", \"host\": \"nls-gateway-cn-shanghai.aliyuncs.com\", \"max_sentence_silence\": 800, \"output_dir\": \"tmp/\"}', NULL, NULL, 8, NULL, NULL, NULL, NULL);
+
+-- 鏇存柊闃块噷浜戞祦寮廇SR閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://nls-portal.console.aliyun.com/',
+`remark` = '闃块噷浜戞祦寮廇SR閰嶇疆璇存槑锛�
+1. 闃块噷浜慉SR鍜岄樋閲屼簯(娴佸紡)ASR鐨勫尯鍒槸锛氶樋閲屼簯ASR鏄竴娆℃�ц瘑鍒紝闃块噷浜�(娴佸紡)ASR鏄疄鏃舵祦寮忚瘑鍒�
+2. 娴佸紡ASR鍏锋湁鏇翠綆鐨勫欢杩熷拰鏇村ソ鐨勫疄鏃舵�э紝閫傚悎璇煶浜や簰鍦烘櫙
+3. 闇�瑕佸湪闃块噷浜戞櫤鑳借闊充氦浜掓帶鍒跺彴鍒涘缓搴旂敤骞惰幏鍙栬璇佷俊鎭�
+4. 鏀寔涓枃瀹炴椂璇煶璇嗗埆锛屾敮鎸佹爣鐐圭鍙烽娴嬪拰閫嗘枃鏈鑼冨寲
+5. 闇�瑕佺綉缁滆繛鎺ワ紝杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍
+鐢宠姝ラ锛�
+1. 璁块棶 https://nls-portal.console.aliyun.com/ 寮�閫氭櫤鑳借闊充氦浜掓湇鍔�
+2. 璁块棶 https://nls-portal.console.aliyun.com/applist 鍒涘缓椤圭洰骞惰幏鍙朼ppkey
+3. 璁块棶 https://nls-portal.console.aliyun.com/overview 鑾峰彇涓存椂token锛堟垨閰嶇疆access_key_id鍜宎ccess_key_secret鑷姩鑾峰彇锛�
+4. 濡傞渶鍔ㄦ�乼oken绠$悊锛屽缓璁厤缃產ccess_key_id鍜宎ccess_key_secret
+5. max_sentence_silence鍙傛暟鎺у埗鏂彞妫�娴嬫椂闂达紙姣锛夛紝榛樿800ms
+濡傞渶浜嗚В鏇村鍙傛暟閰嶇疆锛岃鍙傝�冿細https://help.aliyun.com/zh/isi/developer-reference/real-time-speech-recognition
+' WHERE `id` = 'ASR_AliyunStreamASR';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507071530.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507071530.sql
new file mode 100644
index 0000000..f1cf7a9
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507071530.sql
@@ -0,0 +1,56 @@
+-- 娣诲姞闃块噷浜戞祦寮廡TS渚涘簲鍣�
+delete from `ai_model_provider` where id = 'SYSTEM_TTS_AliyunStreamTTS';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_TTS_AliyunStreamTTS', 'TTS', 'aliyun_stream', '闃块噷浜戣闊冲悎鎴�(娴佸紡)', '[{"key":"appkey","label":"搴旂敤AppKey","type":"string"},{"key":"token","label":"涓存椂Token","type":"string"},{"key":"access_key_id","label":"AccessKey ID","type":"string"},{"key":"access_key_secret","label":"AccessKey Secret","type":"string"},{"key":"host","label":"鏈嶅姟鍦板潃","type":"string"},{"key":"voice","label":"榛樿闊宠壊","type":"string"},{"key":"format","label":"闊抽鏍煎紡","type":"string"},{"key":"sample_rate","label":"閲囨牱鐜�","type":"number"},{"key":"volume","label":"闊抽噺","type":"number"},{"key":"speech_rate","label":"璇��","type":"number"},{"key":"pitch_rate","label":"闊宠皟","type":"number"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"}]', 15, 1, NOW(), 1, NOW());
+
+-- 娣诲姞闃块噷浜戞祦寮廡TS妯″瀷閰嶇疆
+delete from `ai_model_config` where id = 'TTS_AliyunStreamTTS';
+INSERT INTO `ai_model_config` VALUES ('TTS_AliyunStreamTTS', 'TTS', 'AliyunStreamTTS', '闃块噷浜戣闊冲悎鎴�(娴佸紡)', 0, 1, '{\"type\": \"aliyun_stream\", \"appkey\": \"\", \"token\": \"\", \"access_key_id\": \"\", \"access_key_secret\": \"\", \"host\": \"nls-gateway-cn-beijing.aliyuncs.com\", \"voice\": \"longxiaochun\", \"format\": \"pcm\", \"sample_rate\": 16000, \"volume\": 50, \"speech_rate\": 0, \"pitch_rate\": 0, \"output_dir\": \"tmp/\"}', NULL, NULL, 18, NULL, NULL, NULL, NULL);
+
+-- 鏇存柊闃块噷浜戞祦寮廡TS閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://nls-portal.console.aliyun.com/',
+`remark` = '闃块噷浜戞祦寮廡TS閰嶇疆璇存槑锛�
+1. 闃块噷浜慣TS鍜岄樋閲屼簯(娴佸紡)TTS鐨勫尯鍒槸锛氶樋閲屼簯TTS鏄竴娆℃�у悎鎴愶紝闃块噷浜�(娴佸紡)TTS鏄疄鏃舵祦寮忓悎鎴�
+2. 娴佸紡TTS鍏锋湁鏇翠綆鐨勫欢杩熷拰鏇村ソ鐨勫疄鏃舵�э紝閫傚悎璇煶浜や簰鍦烘櫙
+3. 闇�瑕佸湪闃块噷浜戞櫤鑳借闊充氦浜掓帶鍒跺彴鍒涘缓搴旂敤骞惰幏鍙栬璇佷俊鎭�
+4. 鏀寔CosyVoice澶фā鍨嬮煶鑹诧紝闊宠川鏇村姞鑷劧
+5. 鏀寔瀹炴椂璋冭妭闊抽噺銆佽閫熴�侀煶璋冪瓑鍙傛暟
+鐢宠姝ラ锛�
+1. 璁块棶 https://nls-portal.console.aliyun.com/ 寮�閫氭櫤鑳借闊充氦浜掓湇鍔�
+2. 璁块棶 https://nls-portal.console.aliyun.com/applist 鍒涘缓椤圭洰骞惰幏鍙朼ppkey
+3. 璁块棶 https://nls-portal.console.aliyun.com/overview 鑾峰彇涓存椂token锛堟垨閰嶇疆access_key_id鍜宎ccess_key_secret鑷姩鑾峰彇锛�
+4. 濡傞渶鍔ㄦ�乼oken绠$悊锛屽缓璁厤缃產ccess_key_id鍜宎ccess_key_secret
+5. 鍙�夋嫨鍖椾含銆佷笂娴风瓑涓嶅悓鍦板煙鐨勬湇鍔″櫒浠ヤ紭鍖栧欢杩�
+6. voice鍙傛暟鏀寔CosyVoice澶фā鍨嬮煶鑹诧紝濡俵ongxiaochun銆乴ongyueyue绛�
+濡傞渶浜嗚В鏇村鍙傛暟閰嶇疆锛岃鍙傝�冿細https://help.aliyun.com/zh/isi/developer-reference/real-time-speech-synthesis
+' WHERE `id` = 'TTS_AliyunStreamTTS';
+
+-- 娣诲姞闃块噷浜戞祦寮廡TS闊宠壊
+delete from `ai_tts_voice` where tts_model_id = 'TTS_AliyunStreamTTS';
+-- 娓╂煍濂冲0绯诲垪
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0001', 'TTS_AliyunStreamTTS', '榫欏皬娣�-娓╂煍濮愬', 'longxiaochun', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 1, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0002', 'TTS_AliyunStreamTTS', '榫欏皬澶�-娓╂煍濂冲0', 'longxiaoxia', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 2, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0003', 'TTS_AliyunStreamTTS', '榫欑帿-娓╂煍濂冲0', 'longmei', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 3, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0004', 'TTS_AliyunStreamTTS', '榫欑懓-娓╂煍濂冲0', 'longgui', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 4, NULL, NULL, NULL, NULL);
+-- 寰″濂冲0绯诲垪
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0005', 'TTS_AliyunStreamTTS', '榫欑帀-寰″濂冲0', 'longyu', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 5, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0006', 'TTS_AliyunStreamTTS', '榫欏▏-寰″濂冲0', 'longjiao', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 6, NULL, NULL, NULL, NULL);
+-- 鐢峰0绯诲垪
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0007', 'TTS_AliyunStreamTTS', '榫欒嚕-璇戝埗鐗囩敺澹�', 'longchen', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 7, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0008', 'TTS_AliyunStreamTTS', '榫欎慨-闈掑勾鐢峰0', 'longxiu', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 8, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0009', 'TTS_AliyunStreamTTS', '榫欐-闃冲厜鐢峰0', 'longcheng', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 9, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0010', 'TTS_AliyunStreamTTS', '榫欏摬-鎴愮啛鐢峰0', 'longzhe', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 10, NULL, NULL, NULL, NULL);
+-- 涓撲笟鎾姤绯诲垪
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0011', 'TTS_AliyunStreamTTS', 'Bella2.0-鏂伴椈濂冲0', 'loongbella', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 11, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0012', 'TTS_AliyunStreamTTS', 'Stella2.0-椋掔埥濂冲0', 'loongstella', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 12, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0013', 'TTS_AliyunStreamTTS', '榫欎功-鏂伴椈鐢峰0', 'longshu', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 13, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0014', 'TTS_AliyunStreamTTS', '榫欏┃-涓ヨ們濂冲0', 'longjing', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 14, NULL, NULL, NULL, NULL);
+-- 鐗硅壊闊宠壊绯诲垪
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0015', 'TTS_AliyunStreamTTS', '榫欏-娲绘臣绔ュ0', 'longqi', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 15, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0016', 'TTS_AliyunStreamTTS', '榫欏崕-娲绘臣濂崇', 'longhua', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 16, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0017', 'TTS_AliyunStreamTTS', '榫欐棤-鏃犲帢澶寸敺澹�', 'longwu', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 17, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0018', 'TTS_AliyunStreamTTS', '榫欏ぇ閿�-骞介粯鐢峰0', 'longdachui', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 18, NULL, NULL, NULL, NULL);
+-- 绮よ绯诲垪
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0019', 'TTS_AliyunStreamTTS', '榫欏槈鎬�-绮よ濂冲0', 'longjiayi', '绮よ鍙婄菠鑻辨贩鍚�', NULL, NULL, NULL, NULL, 19, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0020', 'TTS_AliyunStreamTTS', '榫欐-绮よ濂冲0', 'longtao', '绮よ鍙婄菠鑻辨贩鍚�', NULL, NULL, NULL, NULL, 20, NULL, NULL, NULL, NULL);
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507081646.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507081646.sql
new file mode 100644
index 0000000..6631ae1
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507081646.sql
@@ -0,0 +1,3 @@
+-- 鏅鸿兘浣撳0绾规坊鍔犳柊瀛楁
+ALTER TABLE ai_agent_voice_print
+ ADD COLUMN audio_id VARCHAR(32) NOT NULL COMMENT '闊抽ID';
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507101203.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507101203.sql
new file mode 100644
index 0000000..66896ef
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202507101203.sql
@@ -0,0 +1,38 @@
+-- OpenAI ASR妯″瀷渚涘簲鍣�
+delete from `ai_model_provider` where id = 'SYSTEM_ASR_OpenaiASR';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_ASR_OpenaiASR', 'ASR', 'openai', 'OpenAI璇煶璇嗗埆', '[{"key": "base_url", "type": "string", "label": "鍩虹URL"}, {"key": "model_name", "type": "string", "label": "妯″瀷鍚嶇О"}, {"key": "api_key", "type": "string", "label": "API瀵嗛挜"}, {"key": "output_dir", "type": "string", "label": "杈撳嚭鐩綍"}]', 9, 1, NOW(), 1, NOW());
+
+
+-- OpenAI ASR妯″瀷閰嶇疆
+delete from `ai_model_config` where id = 'ASR_OpenaiASR';
+INSERT INTO `ai_model_config` VALUES ('ASR_OpenaiASR', 'ASR', 'OpenaiASR', 'OpenAI璇煶璇嗗埆', 0, 1, '{\"type\": \"openai\", \"api_key\": \"\", \"base_url\": \"https://api.openai.com/v1/audio/transcriptions\", \"model_name\": \"gpt-4o-mini-transcribe\", \"output_dir\": \"tmp/\"}', NULL, NULL, 9, NULL, NULL, NULL, NULL);
+
+-- groq ASR妯″瀷閰嶇疆
+delete from `ai_model_config` where id = 'ASR_GroqASR';
+INSERT INTO `ai_model_config` VALUES ('ASR_GroqASR', 'ASR', 'GroqASR', 'Groq璇煶璇嗗埆', 0, 1, '{\"type\": \"openai\", \"api_key\": \"\", \"base_url\": \"https://api.groq.com/openai/v1/audio/transcriptions\", \"model_name\": \"whisper-large-v3-turbo\", \"output_dir\": \"tmp/\"}', NULL, NULL, 10, NULL, NULL, NULL, NULL);
+
+
+-- 鏇存柊OpenAI ASR閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://platform.openai.com/docs/api-reference/audio/createTranscription',
+`remark` = 'OpenAI ASR閰嶇疆璇存槑锛�
+1. 闇�瑕佸湪OpenAI寮�鏀惧钩鍙板垱寤虹粍缁囧苟鑾峰彇api_key
+2. 鏀寔涓�佽嫳銆佹棩銆侀煩绛夊绉嶈闊宠瘑鍒紝鍏蜂綋鍙傝�冩枃妗ttps://platform.openai.com/docs/guides/speech-to-text
+3. 闇�瑕佺綉缁滆繛鎺�
+4. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍
+鐢宠姝ラ锛�
+**OpenAi ASR鐢宠姝ラ锛�**
+1.鐧诲綍OpenAI Platform銆俬ttps://auth.openai.com/log-in
+2.鍒涘缓api-key https://platform.openai.com/settings/organization/api-keys
+3.妯″瀷鍙互閫夋嫨gpt-4o-transcribe鎴朑PT-4o mini Transcribe
+' WHERE `id` = 'ASR_OpenaiASR';
+
+-- 鏇存柊Groq ASR閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://console.groq.com/docs/speech-to-text',
+`remark` = 'Groq ASR閰嶇疆璇存槑锛�
+1.鐧诲綍groq Console銆俬ttps://console.groq.com/home
+2.鍒涘缓api-key https://console.groq.com/keys
+3.妯″瀷鍙互閫夋嫨whisper-large-v3-turbo鎴杦hisper-large-v3锛坉istil-whisper-large-v3-en浠呮敮鎸佽嫳璇浆褰曪級
+' WHERE `id` = 'ASR_GroqASR';
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202508081701.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202508081701.sql
new file mode 100644
index 0000000..4f42751
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202508081701.sql
@@ -0,0 +1,32 @@
+-- 娣诲姞Index-TTS-vLLM娴佸紡TTS渚涘簲鍣�
+delete from `ai_model_provider` where id = 'SYSTEM_TTS_IndexStreamTTS';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_TTS_IndexStreamTTS', 'TTS', 'index_stream', 'Index-TTS-vLLM娴佸紡璇煶鍚堟垚', '[{"key":"api_url","label":"API鏈嶅姟鍦板潃","type":"string"},{"key":"voice","label":"榛樿闊宠壊","type":"string"},{"key":"audio_format","label":"闊抽鏍煎紡","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"}]', 16, 1, NOW(), 1, NOW());
+
+-- 娣诲姞Index-TTS-vLLM娴佸紡TTS妯″瀷閰嶇疆
+delete from `ai_model_config` where id = 'TTS_IndexStreamTTS';
+INSERT INTO `ai_model_config` VALUES ('TTS_IndexStreamTTS', 'TTS', 'IndexStreamTTS', 'Index-TTS-vLLM娴佸紡璇煶鍚堟垚', 0, 1, '{\"type\": \"index_stream\", \"api_url\": \"http://127.0.0.1:11996/tts\", \"voice\": \"jay_klee\", \"audio_format\": \"pcm\", \"output_dir\": \"tmp/\"}', NULL, NULL, 19, NULL, NULL, NULL, NULL);
+
+-- 鏇存柊Index-TTS-vLLM娴佸紡TTS閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://github.com/Ksuriuri/index-tts-vllm',
+`remark` = 'Index-TTS-vLLM娴佸紡TTS閰嶇疆璇存槑锛�
+1. Index-TTS-vLLM鏄熀浜嶪ndex-TTS椤圭洰鐨剉LLM鎺ㄧ悊鏈嶅姟锛屾彁渚涙祦寮忚闊冲悎鎴愬姛鑳�
+2. 鏀寔澶氱闊宠壊锛岄煶璐ㄨ嚜鐒讹紝閫傚悎鍚勭璇煶浜や簰鍦烘櫙
+3. 闇�瑕佸厛閮ㄧ讲Index-TTS-vLLM鏈嶅姟锛岀劧鍚庨厤缃瓵PI鍦板潃
+4. 鏀寔瀹炴椂娴佸紡鍚堟垚锛屽叿鏈夎緝浣庣殑寤惰繜
+5. 鏀寔鑷畾涔夐煶鑹诧紝鍙湪椤圭洰assets鏂囦欢澶逛笅娉ㄥ唽鏂伴煶鑹�
+閮ㄧ讲姝ラ锛�
+1. 鍏嬮殕椤圭洰锛歡it clone https://github.com/Ksuriuri/index-tts-vllm.git
+2. 瀹夎渚濊禆锛歱ip install -r requirements.txt
+3. 鍚姩鏈嶅姟锛歱ython app.py
+4. 鏈嶅姟榛樿杩愯鍦� http://127.0.0.1:11996
+5. 濡傞渶鍏朵粬闊宠壊锛屽彲鍒伴」鐩產ssets鏂囦欢澶逛笅娉ㄥ唽
+6. 鏀寔澶氱闊抽鏍煎紡锛歱cm銆亀av銆乵p3绛�
+濡傞渶浜嗚В鏇村閰嶇疆锛岃鍙傝�冿細https://github.com/Ksuriuri/index-tts-vllm/blob/master/README.md
+' WHERE `id` = 'TTS_IndexStreamTTS';
+
+-- 娣诲姞Index-TTS-vLLM娴佸紡TTS闊宠壊
+delete from `ai_tts_voice` where tts_model_id = 'TTS_IndexStreamTTS';
+-- 榛樿闊宠壊
+INSERT INTO `ai_tts_voice` VALUES ('TTS_IndexStreamTTS_0001', 'TTS_IndexStreamTTS', 'Jay Klee', 'jay_klee', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 1, NULL, NULL, NULL, NULL);
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202508111734.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202508111734.sql
new file mode 100644
index 0000000..fb3162b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202508111734.sql
@@ -0,0 +1,16 @@
+-- 鏇存柊HuoshanDoubleStreamTTS渚涘簲鍣ㄥ鍔犺閫燂紝闊宠皟绛夐厤缃�
+UPDATE `ai_model_provider`
+SET fields = '[{"key": "ws_url", "type": "string", "label": "WebSocket鍦板潃"}, {"key": "appid", "type": "string", "label": "搴旂敤ID"}, {"key": "access_token", "type": "string", "label": "璁块棶浠ょ墝"}, {"key": "resource_id", "type": "string", "label": "璧勬簮ID"}, {"key": "speaker", "type": "string", "label": "榛樿闊宠壊"}, {"key": "speech_rate", "type": "number", "label": "璇��(-50~100)"}, {"key": "loudness_rate", "type": "number", "label": "闊抽噺(-50~100)"}, {"key": "pitch", "type": "number", "label": "闊抽珮(-12~12)"}]'
+WHERE id = 'SYSTEM_TTS_HSDSTTS';
+
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://console.volcengine.com/speech/service/10007',
+`remark` = '鐏北寮曟搸璇煶鍚堟垚鏈嶅姟閰嶇疆璇存槑锛�
+1. 璁块棶 https://www.volcengine.com/ 娉ㄥ唽骞跺紑閫氱伀灞卞紩鎿庤处鍙�
+2. 璁块棶 https://console.volcengine.com/speech/service/10007 寮�閫氳闊冲悎鎴愬ぇ妯″瀷锛岃喘涔伴煶鑹�
+3. 鍦ㄩ〉闈㈠簳閮ㄨ幏鍙朼ppid鍜宎ccess_token
+5. 璧勬簮ID鍥哄畾涓猴細volc.service_type.10029锛堝ぇ妯″瀷璇煶鍚堟垚鍙婃贩闊筹級
+6. 璇�燂細-50~100锛屽彲涓嶅~锛屾甯搁粯璁ゅ��0锛屽彲濉�-50~100
+7. 闊抽噺锛�-50~100锛屽彲涓嶅~锛屾甯搁粯璁ゅ��0锛屽彲濉�-50~100
+8. 闊抽珮锛�-12~12锛屽彲涓嶅~锛屾甯搁粯璁ゅ��0锛屽彲濉�-12~12
+9. 濉叆閰嶇疆鏂囦欢涓�' WHERE `id` = 'TTS_HuoshanDoubleStreamTTS';
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202508131557.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202508131557.sql
new file mode 100644
index 0000000..decfd0d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202508131557.sql
@@ -0,0 +1,26 @@
+-- 娣诲姞 paddle_speech 娴佸紡 TTS 渚涘簲鍣�
+DELETE FROM `ai_model_provider` WHERE id = 'SYSTEM_TTS_PaddleSpeechTTS';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`)
+VALUES ('SYSTEM_TTS_PaddleSpeechTTS', 'TTS', 'paddle_speech', 'PaddleSpeechTTS',
+'[{"key":"protocol","label":"鍗忚绫诲瀷","type":"string","options":["websocket","http"]},{"key":"url","label":"鏈嶅姟鍦板潃","type":"string"},{"key":"spk_id","label":"闊宠壊","type":"int"},{"key":"sample_rate","label":"閲囨牱鐜�","type":"float"},{"key":"speed","label":"璇��","type":"float"},{"key":"volume","label":"闊抽噺","type":"float"},{"key":"save_path","label":"淇濆瓨璺緞","type":"string"}]',
+17, 1, NOW(), 1, NOW());
+
+-- 娣诲姞 paddle_speech 娴佸紡 TTS 妯″瀷閰嶇疆
+DELETE FROM `ai_model_config` WHERE id = 'TTS_PaddleSpeechTTS';
+INSERT INTO `ai_model_config` VALUES ('TTS_PaddleSpeechTTS', 'TTS', 'PaddleSpeechTTS', 'PaddleSpeechTTS', 0, 1,
+'{"type": "paddle_speech", "protocol": "websocket", "url": "ws://127.0.0.1:8092/paddlespeech/tts/streaming", "spk_id": "0", "sample_rate": 24000, "speed": 1.0, "volume": 1.0, "save_path": "./streaming_tts.wav"}',
+NULL, NULL, 20, NULL, NULL, NULL, NULL);
+
+-- 鏇存柊 PaddleSpeechTTS 閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://github.com/PaddlePaddle/PaddleSpeech',
+`remark` = 'PaddleSpeechTTS 閰嶇疆璇存槑锛�
+1. PaddleSpeech 鏄櫨搴﹂妗ㄥ紑婧愮殑璇煶鍚堟垚宸ュ叿锛屾敮鎸佹湰鍦扮绾块儴缃插拰妯″瀷璁粌銆俻addlepaddle鐧惧害椋炴祮妗嗘灦鍦板潃锛歨ttps://www.paddlepaddle.org.cn/
+2. 鏀寔 WebSocket 鍜� HTTP 鍗忚锛岄粯璁や娇鐢� WebSocket 杩涜娴佸紡浼犺緭锛堝弬鑰冮儴缃叉枃妗o細https://github.com/xinnan-tech/xiaozhi-esp32-server/blob/main/docs/paddlespeech-deploy.md锛夈��
+3. 浣跨敤鍓嶈鍦ㄦ湰鍦伴儴缃� paddlespeech 鏈嶅姟锛屾湇鍔¢粯璁よ繍琛屽湪 ws://127.0.0.1:8092/paddlespeech/tts/streaming
+4. 鏀寔鑷畾涔夊彂闊充汉銆佽閫熴�侀煶閲忓拰閲囨牱鐜囥��
+' WHERE `id` = 'TTS_PaddleSpeechTTS';
+
+-- 鍒犻櫎鏃ч煶鑹插苟娣诲姞榛樿闊宠壊
+DELETE FROM `ai_tts_voice` WHERE tts_model_id = 'TTS_PaddleSpeechTTS';
+INSERT INTO `ai_tts_voice` VALUES ('TTS_PaddleSpeechTTS_0000', 'TTS_PaddleSpeechTTS', '榛樿', '0', '涓枃', NULL, NULL, NULL, NULL, 1, NULL, NULL, NULL, NULL);
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202508271113.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202508271113.sql
new file mode 100644
index 0000000..6d02080
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202508271113.sql
@@ -0,0 +1,24 @@
+-- VOSK ASR妯″瀷渚涘簲鍣�
+delete from `ai_model_provider` where id = 'SYSTEM_ASR_VoskASR';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_ASR_VoskASR', 'ASR', 'vosk', 'VOSK绂荤嚎璇煶璇嗗埆', '[{"key": "model_path", "type": "string", "label": "妯″瀷璺緞"}, {"key": "output_dir", "type": "string", "label": "杈撳嚭鐩綍"}]', 11, 1, NOW(), 1, NOW());
+
+-- VOSK ASR妯″瀷閰嶇疆
+delete from `ai_model_config` where id = 'ASR_VoskASR';
+INSERT INTO `ai_model_config` VALUES ('ASR_VoskASR', 'ASR', 'VoskASR', 'VOSK绂荤嚎璇煶璇嗗埆', 0, 1, '{\"type\": \"vosk\", \"model_path\": \"\", \"output_dir\": \"tmp/\"}', NULL, NULL, 11, NULL, NULL, NULL, NULL);
+
+-- 鏇存柊VOSK ASR閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://alphacephei.com/vosk/',
+`remark` = 'VOSK ASR閰嶇疆璇存槑锛�
+1. VOSK鏄竴涓绾胯闊宠瘑鍒簱锛屾敮鎸佸绉嶈瑷�
+2. 闇�瑕佸厛涓嬭浇妯″瀷鏂囦欢锛歨ttps://alphacephei.com/vosk/models
+3. 涓枃妯″瀷鎺ㄨ崘浣跨敤vosk-model-small-cn-0.22鎴杤osk-model-cn-0.22
+4. 瀹屽叏绂荤嚎杩愯锛屾棤闇�缃戠粶杩炴帴
+5. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍
+浣跨敤姝ラ锛�
+1. 璁块棶 https://alphacephei.com/vosk/models 涓嬭浇涓枃妯″瀷
+2. 瑙e帇妯″瀷鏂囦欢鍒伴」鐩洰褰曚笅鐨刴odels/vosk/鏂囦欢澶�
+3. 鍦ㄩ厤缃腑鎸囧畾姝g‘鐨勬ā鍨嬭矾寰�
+4. 娉ㄦ剰锛歏OSK涓枃妯″瀷杈撳嚭涓嶅甫鏍囩偣绗﹀彿锛岃瘝涓庤瘝涔嬮棿浼氭湁绌烘牸
+' WHERE `id` = 'ASR_VoskASR';
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509051745.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509051745.sql
new file mode 100644
index 0000000..6dc9222
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509051745.sql
@@ -0,0 +1,45 @@
+-- 娣诲姞 MinimaxHTTPStream 娴佸紡 TTS 渚涘簲鍣�
+delete from `ai_model_provider` where id = 'SYSTEM_TTS_MinimaxStreamTTS';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_TTS_MinimaxStreamTTS', 'TTS', 'minimax_httpstream', 'Minimax娴佸紡璇煶鍚堟垚', '[{"key":"group_id","label":"缁処D","type":"string"},{"key":"api_key","label":"API瀵嗛挜","type":"string"},{"key":"model","label":"妯″瀷","type":"string"},{"key":"voice_id","label":"闊宠壊ID","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"},{"key":"voice_setting","label":"闊宠壊璁剧疆","type":"dict","dict_name":"voice_setting"},{"key":"pronunciation_dict","label":"鍙戦煶瀛楀吀","type":"dict","dict_name":"pronunciation_dict"},{"key":"audio_setting","label":"闊抽璁剧疆","type":"dict","dict_name":"audio_setting"},{"key":"timber_weights","label":"闊宠壊鏉冮噸","type":"string"}]', 18, 1, NOW(), 1, NOW());
+
+-- 娣诲姞Minimax娴佸紡TTS妯″瀷閰嶇疆
+delete from `ai_model_config` where id = 'TTS_MinimaxStreamTTS';
+INSERT INTO `ai_model_config` VALUES ('TTS_MinimaxStreamTTS', 'TTS', 'MinimaxStreamTTS', 'Minimax娴佸紡璇煶鍚堟垚', 0, 1, '{"type": "minimax_httpstream", "group_id": "", "api_key": "", "model": "speech-01-turbo", "voice_id": "female-shaonv", "output_dir": "tmp/", "voice_setting": {"speed": 1, "vol": 1, "pitch": 0, "emotion": "happy"}, "pronunciation_dict": {"tone": ["澶勭悊/(chu3)(li3)", "鍗遍櫓/dangerous"]}, "audio_setting": {"sample_rate": 24000, "bitrate": 128000, "format": "pcm", "channel": 1}}', NULL, NULL, 21, NULL, NULL, NULL, NULL);
+
+-- 鏇存柊Minimax娴佸紡TTS閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://platform.minimaxi.com/',
+`remark` = 'Minimax娴佸紡TTS閰嶇疆璇存槑锛�
+1. 闇�瑕佸厛鐢宠Minimax API Key
+2. 闇�瑕佸~鍐橤roup ID
+3. 鏀寔澶氱闊宠壊璁剧疆鍜岄煶棰戝弬鏁拌皟鏁�
+4. 鏀寔瀹炴椂娴佸紡鍚堟垚锛屽叿鏈夎緝浣庣殑寤惰繜
+5. 鏀寔鑷畾涔夊彂闊冲瓧鍏稿拰闊宠壊鏉冮噸
+6. 闅愯棌鍙傛暟閰嶇疆锛氬0闊宠瀹�(voice_setting)銆佸彂闊冲瓧鍏�(pronunciation_dict)銆侀煶鑹叉潈閲�(timber_weights)
+ - 璇��(speed): 鑼冨洿[0.5,2]锛岄粯璁�1.0锛屽彇鍊艰秺澶ц閫熻秺蹇�
+ - 闊抽噺(vol): 鑼冨洿(0,10]锛岄粯璁�1.0锛屽彇鍊艰秺澶ч煶閲忚秺楂�
+ - 闊宠皟(pitch): 鑼冨洿[-12,12]锛岄粯璁�0锛屽彇鍊奸渶涓烘暣鏁�
+ - 鎯呯华(emotion): 鎺у埗鍚堟垚璇煶鐨勬儏缁紝鏀寔7绉嶅�硷細["happy", "sad", "angry", "fearful", "disgusted", "surprised", "calm"]锛岃鍙傛暟浠呭 speech-2.5-hd-preview銆乻peech-2.5-turbo-preview銆乻peech-02-hd銆乻peech-02-turbo銆乻peech-01-turbo銆乻peech-01-hd 鐢熸晥
+ - timbre_weights涓巚oice_id浜岄�変竴蹇呭~
+ - voice_id(璇锋眰鐨勯煶鑹瞚d锛岄』鍜寃eight鍙傛暟鍚屾濉啓)
+ - weight(鏉冮噸锛屾渶澶氭敮鎸�4绉嶉煶鑹叉贩鍚堛�傝寖鍥碵1,100])
+' WHERE `id` = 'TTS_MinimaxStreamTTS';
+
+-- 娣诲姞Minimax娴佸紡TTS闊宠壊
+delete from `ai_tts_voice` where tts_model_id = 'TTS_MinimaxStreamTTS';
+
+-- 榛樿闊宠壊
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0001', 'TTS_MinimaxStreamTTS', '灏戝コ闊�', 'female-shaonv', '涓枃', NULL, NULL, NULL, NULL, 1, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0002', 'TTS_MinimaxStreamTTS', '鎴愮啛濂冲0', 'female-chengshu', '涓枃', NULL, NULL, NULL, NULL, 2, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0003', 'TTS_MinimaxStreamTTS', '闇搁亾灏戠埛', 'badao_shaoye', '涓枃', NULL, NULL, NULL, NULL, 3, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0004', 'TTS_MinimaxStreamTTS', '鐥呭▏寮熷紵', 'bingjiao_didi', '涓枃', NULL, NULL, NULL, NULL, 4, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0005', 'TTS_MinimaxStreamTTS', '绾湡瀛﹀紵', 'chunzhen_xuedi', '涓枃', NULL, NULL, NULL, NULL, 5, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0006', 'TTS_MinimaxStreamTTS', '鍐锋贰瀛﹂暱', 'lengdan_xiongzhang', '涓枃', NULL, NULL, NULL, NULL, 6, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0007', 'TTS_MinimaxStreamTTS', '鐢滅編灏忕幉', 'tianxin_xiaoling', '涓枃', NULL, NULL, NULL, NULL, 7, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0008', 'TTS_MinimaxStreamTTS', '淇忕毊钀屽', 'qiaopi_mengmei', '涓枃', NULL, NULL, NULL, NULL, 8, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0009', 'TTS_MinimaxStreamTTS', '濡╁獨寰″', 'wumei_yujie', '涓枃', NULL, NULL, NULL, NULL, 9, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0010', 'TTS_MinimaxStreamTTS', '鍡插棽瀛﹀', 'diadia_xuemei', '涓枃', NULL, NULL, NULL, NULL, 7, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0011', 'TTS_MinimaxStreamTTS', '娣¢泤瀛﹀', 'danya_xuejie', '涓枃', NULL, NULL, NULL, NULL, 8, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0012', 'TTS_MinimaxStreamTTS', 'Santa Claus', 'Santa_Claus', '涓枃', NULL, NULL, NULL, NULL, 9, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0013', 'TTS_MinimaxStreamTTS', 'Grinch', 'Grinch', '涓枃', NULL, NULL, NULL, NULL, 10, NULL, NULL, NULL, NULL);
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509080922.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509080922.sql
new file mode 100644
index 0000000..424b3cd
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509080922.sql
@@ -0,0 +1,8 @@
+delete from `sys_params` where param_code = 'server.mqtt_gateway';
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (116, 'server.mqtt_gateway', 'null', 'string', 1, 'mqtt gateway 閰嶇疆');
+
+delete from `sys_params` where param_code = 'server.mqtt_signature_key';
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (117, 'server.mqtt_signature_key', 'null', 'string', 1, 'mqtt 瀵嗛挜 閰嶇疆');
+
+delete from `sys_params` where param_code = 'server.udp_gateway';
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (118, 'server.udp_gateway', 'null', 'string', 1, 'udp gateway 閰嶇疆');
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509081140.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509081140.sql
new file mode 100644
index 0000000..9dec9d0
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509081140.sql
@@ -0,0 +1,4 @@
+-- 娣诲姞澹扮汗璇嗗埆鐩镐技搴﹂槇鍊煎弬鏁伴厤缃�
+delete from `sys_params` where id = 115;
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark)
+VALUES (115, 'server.voiceprint_similarity_threshold', '0.4', 'string', 1, '澹扮汗璇嗗埆鐩镐技搴﹂槇鍊硷紝鑼冨洿0.0-1.0锛岄粯璁�0.4锛屾暟鍊艰秺楂樿秺涓ユ牸');
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509091042.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509091042.sql
new file mode 100644
index 0000000..5392738
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509091042.sql
@@ -0,0 +1,10 @@
+-- 鍒犻櫎闈炴祦寮廙iniMax TTS閰嶇疆锛屼繚鐣欐祦寮忕増鏈�
+
+-- 鍒犻櫎鏃х殑闈炴祦寮廙iniMax TTS妯″瀷閰嶇疆
+DELETE FROM `ai_model_config` WHERE `id` = 'TTS_MinimaxTTS';
+
+-- 鍒犻櫎鏃х殑闈炴祦寮廙iniMax TTS渚涘簲鍣ㄩ厤缃�
+DELETE FROM `ai_model_provider` WHERE `id` = 'SYSTEM_TTS_minimax';
+
+-- 鍒犻櫎鏃х殑闈炴祦寮廙iniMax TTS闊宠壊閰嶇疆
+DELETE FROM `ai_tts_voice` WHERE `tts_model_id` = 'TTS_MinimaxTTS';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509091633.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509091633.sql
new file mode 100644
index 0000000..087bab4
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509091633.sql
@@ -0,0 +1,16 @@
+-- 娣诲姞閫氫箟鍗冮棶Qwen3-ASR-Flash璇煶璇嗗埆鏈嶅姟閰嶇疆
+delete from `ai_model_provider` where id = 'SYSTEM_ASR_Qwen3Flash';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_ASR_Qwen3Flash', 'ASR', 'qwen3_asr_flash', 'Qwen3-ASR-Flash璇煶璇嗗埆', '[{"key":"api_key","label":"API瀵嗛挜","type":"password"},{"key":"base_url","label":"鏈嶅姟鍦板潃","type":"string"},{"key":"model_name","label":"妯″瀷鍚嶇О","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"}]', 17, 1, NOW(), 1, NOW());
+
+delete from `ai_model_config` where id = 'ASR_Qwen3Flash';
+INSERT INTO `ai_model_config` VALUES ('ASR_Qwen3Flash', 'ASR', 'Qwen3-ASR-Flash', '閫氫箟鍗冮棶璇煶璇嗗埆鏈嶅姟', 0, 1, '{"type": "qwen3_asr_flash", "api_key": "", "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", "model_name": "qwen3-asr-flash", "output_dir": "tmp/", "enable_lid": true, "enable_itn": true}', 'https://help.aliyun.com/zh/bailian/', '鏀寔澶氳瑷�璇嗗埆銆佹瓕鍞辫瘑鍒�佸櫔澹版嫆璇嗗姛鑳�', 20, NULL, NULL, NULL, NULL);
+
+-- 鏇存柊Qwen3-ASR-Flash妯″瀷閰嶇疆鐨勮鏄庢枃妗�
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://bailian.console.aliyun.com/?apiKey=1&tab=doc#/doc/?type=model&url=2979031',
+`remark` = '閫氫箟鍗冮棶Qwen3-ASR-Flash閰嶇疆璇存槑锛�
+1. 鐧诲綍闃块噷浜戠櫨鐐煎钩鍙癶ttps://bailian.console.aliyun.com/
+2. 鍒涘缓API-KEY https://bailian.console.aliyun.com/#/api-key
+3.Qwen3-ASR-Flash鍩轰簬閫氫箟鍗冮棶澶氭ā鎬佸熀搴э紝鏀寔澶氳瑷�璇嗗埆銆佹瓕鍞辫瘑鍒�佸櫔澹版嫆璇嗙瓑鍔熻兘
+' WHERE `id` = 'ASR_Qwen3Flash';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509161610.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509161610.sql
new file mode 100644
index 0000000..df4b1fc
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509161610.sql
@@ -0,0 +1,3 @@
+delete from `sys_params` where param_code = 'server.mqtt_manager_api';
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark)
+VALUES (119, 'server.mqtt_manager_api', 'null', 'string', 1, 'MQTT缃戝叧绠$悊API鐨勫湴鍧�');
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509161701.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509161701.sql
new file mode 100644
index 0000000..fe7b0a7
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509161701.sql
@@ -0,0 +1,54 @@
+-- 娣诲姞闃块噷鐧剧偧娴佸紡TTS 渚涘簲鍣�
+delete from `ai_model_provider` where id = 'SYSTEM_TTS_AliBLStreamTTS';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_TTS_AliBLStreamTTS', 'TTS', 'alibl_stream', '闃块噷鐧剧偧娴佸紡璇煶鍚堟垚', '[{"key":"api_key","label":"API瀵嗛挜","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"},{"key":"model","label":"妯″瀷","type":"string"},{"key":"voice","label":"闊宠壊","type":"string"},{"key":"format","label":"闊抽鏍煎紡","type":"string"},{"key":"sample_rate","label":"閲囨牱鐜�","type":"number"},{"key": "volume", "type": "number", "label": "闊抽噺"},{"key": "rate", "type": "number", "label": "璇��"},{"key": "pitch", "type": "number", "label": "闊宠皟"}]', 19, 1, NOW(), 1, NOW());
+
+-- 娣诲姞闃块噷鐧剧偧娴佸紡TTS妯″瀷閰嶇疆
+delete from `ai_model_config` where id = 'TTS_AliBLStreamTTS';
+INSERT INTO `ai_model_config` VALUES ('TTS_AliBLStreamTTS', 'TTS', 'AliBLStreamTTS', '闃块噷鐧剧偧娴佸紡璇煶鍚堟垚', 0, 1, '{\"type\": \"alibl_stream\", \"appkey\": \"\", \"output_dir\": \"tmp/\", \"model\": \"cosyvoice-v2\", \"voice\": \"longcheng_v2\", \"format\": \"pcm\", \"sample_rate\": 24000, \"volume\": 50, \"rate\": 1, \"pitch\": 1}', NULL, NULL, 22, NULL, NULL, NULL, NULL);
+
+-- 鏇存柊闃块噷鐧剧偧娴佸紡TTS閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://bailian.console.aliyun.com/?apiKey=1#/api-key',
+`remark` = '闃块噷鐧剧偧娴佸紡TTS璇存槑锛�
+1. 璁块棶 https://bailian.console.aliyun.com/?apiKey=1#/api-key 鍒涘缓椤圭洰骞惰幏鍙朼ppkey
+2. 鏀寔瀹炴椂娴佸紡鍚堟垚锛屽叿鏈夎緝浣庣殑寤惰繜
+3. 鏀寔澶氱闊宠壊璁剧疆鍜岄煶棰戝弬鏁拌皟鏁�
+4. 鏀寔CosyVoice-V3澶фā鍨嬮煶鑹诧紝浠锋牸瀹炴儬(0.4鍏�/涓囧瓧绗�)
+5. 鏀寔瀹炴椂璋冭妭闊抽噺銆佽閫熴�侀煶璋冪瓑鍙傛暟
+6. 濡傛灉闇�瑕佷娇鐢–osyVoice-V3妯″瀷鍜屼竴浜涢檺鍒剁被鍨嬬殑闊宠壊锛岄渶瑕佽仈绯婚樋閲岀櫨鐐煎鏈嶇敵璇�
+' WHERE `id` = 'TTS_AliBLStreamTTS';
+
+-- 娣诲姞闃块噷鐧剧偧娴佸紡TTS闊宠壊
+delete from `ai_tts_voice` where tts_model_id = 'TTS_AliBLStreamTTS';
+
+-- 璇煶鍔╂墜
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliBLStreamTTS_0001', 'TTS_AliBLStreamTTS', '榫欏皬娣�-鐭ユ�хН鏋佸コ', 'longxiaochun_v2', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 1, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliBLStreamTTS_0002', 'TTS_AliBLStreamTTS', '榫欏皬澶�-娌夌ǔ鏉冨▉濂�', 'longxiaoxia_v2', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 2, NULL, NULL, NULL, NULL);
+
+-- 鐩存挱甯﹁揣
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliBLStreamTTS_0003', 'TTS_AliBLStreamTTS', '榫欏畨鐕�-娲绘臣璐ㄦ劅濂�', 'longanran', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 3, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliBLStreamTTS_0004', 'TTS_AliBLStreamTTS', '榫欏畨瀹�-缁忓吀鐩存挱濂�', 'longanxuan', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 4, NULL, NULL, NULL, NULL);
+
+-- 绀句氦闄即
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliBLStreamTTS_0005', 'TTS_AliBLStreamTTS', '榫欏瘨-娓╂殩鐥存儏鐢�', 'longhan_v2', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 5, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliBLStreamTTS_0006', 'TTS_AliBLStreamTTS', '榫欓-娓╂殩鏄ラ濂�', 'longyan_v2', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 6, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliBLStreamTTS_0007', 'TTS_AliBLStreamTTS', '榫欒彶鑿�-鐢滅編鐭儏濂�', 'longfeifei_v2', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 7, NULL, NULL, NULL, NULL);
+
+-- 鏂硅█
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliBLStreamTTS_0008', 'TTS_AliBLStreamTTS', '榫欒�侀搧-涓滃寳鐩寸巼鐢�', 'longlaotie_v2', '涓枃(涓滃寳)鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 8, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliBLStreamTTS_0009', 'TTS_AliBLStreamTTS', '榫欏槈鎬�-鐭ユ�х菠璇コ', 'longjiayi_v2', '涓枃(绮よ)鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 9, NULL, NULL, NULL, NULL);
+
+-- 绔ュ0
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliBLStreamTTS_0010', 'TTS_AliBLStreamTTS', '榫欐澃鍔涜眴-闃冲厜椤界毊鐢�', 'longjielidou_v2', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 10, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliBLStreamTTS_0011', 'TTS_AliBLStreamTTS', '榫欓搩-绋氭皵鍛嗘澘濂�', 'longling_v2', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 11, NULL, NULL, NULL, NULL);
+
+-- 璇楁瓕鏈楄
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliBLStreamTTS_0012', 'TTS_AliBLStreamTTS', '鏉庣櫧-鍙や唬璇椾粰鐢�', 'libai_v2', '涓枃鍙婁腑鑻辨枃娣峰悎', NULL, NULL, NULL, NULL, 12, NULL, NULL, NULL, NULL);
+
+-- 鍑烘捣钀ラ攢
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliBLStreamTTS_0013', 'TTS_AliBLStreamTTS', 'loongeva-鐭ユ�ц嫳鏂囧コ', 'loongeva_v2', '鑻卞紡鑻辨枃', NULL, NULL, NULL, NULL, 13, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliBLStreamTTS_0014', 'TTS_AliBLStreamTTS', 'loongbrian-娌夌ǔ鑻辨枃鐢�', 'loongbrian_v2', '鑻卞紡鑻辨枃', NULL, NULL, NULL, NULL, 14, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliBLStreamTTS_0015', 'TTS_AliBLStreamTTS', 'loongkyong-闊╄濂�', 'loongkyong_v2', '闊╄', NULL, NULL, NULL, NULL, 15, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliBLStreamTTS_0016', 'TTS_AliBLStreamTTS', 'loongtomoka-鏃ヨ濂�', 'loongtomoka_v2', '鏃ヨ', NULL, NULL, NULL, NULL, 16, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_AliBLStreamTTS_0017', 'TTS_AliBLStreamTTS', 'loongtomoya-鏃ヨ鐢�', 'loongtomoya_v2', '鏃ヨ', NULL, NULL, NULL, NULL, 17, NULL, NULL, NULL, NULL);
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509191545.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509191545.sql
new file mode 100644
index 0000000..a199297
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509191545.sql
@@ -0,0 +1,41 @@
+-- 娣诲姞璁娴佸紡TTS渚涘簲鍣�
+delete from `ai_model_provider` where id = 'SYSTEM_TTS_XunFeiStreamTTS';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_TTS_XunFeiStreamTTS', 'TTS', 'xunfei_stream', '璁娴佸紡璇煶鍚堟垚', '[{"key":"app_id","label":"APP_ID","type":"string"},{"key":"api_secret","label":"API_Secret","type":"string"},{"key":"api_key","label":"API瀵嗛挜","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"},{"key":"voice","label":"闊宠壊","type":"string"},{"key":"format","label":"闊抽鏍煎紡","type":"string"},{"key":"sample_rate","label":"閲囨牱鐜�","type":"number"},{"key": "volume", "type": "number", "label": "闊抽噺"},{"key": "speed", "type": "number", "label": "璇��"},{"key": "pitch", "type": "number", "label": "闊宠皟"},{"key": "oral_level", "type": "number", "label": "鍙h鍖栫瓑绾�"},{"key": "spark_assist", "type": "number", "label": "鏄惁鍙h鍖�"},{"key": "stop_split", "type": "number", "label": "鏈嶅姟绔媶鍙�"},{"key": "remain", "type": "number", "label": "淇濈暀涔﹂潰璇�"}]', 20, 1, NOW(), 1, NOW());
+
+-- 娣诲姞璁娴佸紡TTS妯″瀷閰嶇疆
+delete from `ai_model_config` where id = 'TTS_XunFeiStreamTTS';
+INSERT INTO `ai_model_config` VALUES ('TTS_XunFeiStreamTTS', 'TTS', 'XunFeiStreamTTS', '璁娴佸紡璇煶鍚堟垚', 0, 1, '{\"type\": \"xunfei_stream\", \"app_id\": \"\", \"api_secret\": \"\", \"api_key\": \"\", \"output_dir\": \"tmp/\", \"voice\": \"x5_lingxiaoxuan_flow\", \"format\": \"raw\", \"sample_rate\": 24000, \"volume\": 50, \"speed\": 50, \"pitch\": 50, \"oral_level\": \"mid\", \"spark_assist\": 1, \"stop_split\": 0, \"remain\": 0}', NULL, NULL, 23, NULL, NULL, NULL, NULL);
+
+-- 鏇存柊璁娴佸紡TTS閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://console.xfyun.cn/app/myapp',
+`remark` = '璁娴佸紡TTS璇存槑锛�
+1. 鐧诲綍璁璇煶鎶�鏈钩鍙� https://console.xfyun.cn/app/myapp 鍒涘缓鐩稿叧搴旂敤
+2. 閫夋嫨闇�瑕佺殑鏈嶅姟鑾峰彇api鐩稿叧閰嶇疆 https://console.xfyun.cn/services/uts
+3. 涓洪渶瑕佷娇鐢ㄧ殑搴旂敤(APPID)璐拱鐩稿叧鏈嶅姟 渚嬪锛氳秴鎷熶汉鍚堟垚 https://console.xfyun.cn/services/uts
+5. 鏀寔瀹炴椂鍙屾祦寮忛�氫俊锛屽叿鏈夎緝浣庣殑寤惰繜
+6. 鏀寔鍙h鍖栬缃拰闊抽鍙傛暟璋冩暣 娉ㄦ剰锛歏5闊宠壊涓嶆敮鎸佺浉鍏冲彛璇寲閰嶇疆
+7. 鏀寔瀹炴椂璋冭妭闊抽噺銆佽閫熴�侀煶璋冪瓑鍙傛暟
+' WHERE `id` = 'TTS_XunFeiStreamTTS';
+
+-- 娣诲姞璁娴佸紡TTS闊宠壊
+delete from `ai_tts_voice` where tts_model_id = 'TTS_XunFeiStreamTTS';
+
+-- 鍩虹瑙掕壊
+INSERT INTO `ai_tts_voice` VALUES ('TTS_XunFeiStreamTTS_0001', 'TTS_XunFeiStreamTTS', '鑱嗗皬鐠�', 'x5_lingxiaoxuan_flow', '涓枃', NULL, NULL, NULL, NULL, 1, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_XunFeiStreamTTS_0002', 'TTS_XunFeiStreamTTS', '鑱嗛閫�', 'x5_lingfeiyi_flow', '涓枃', NULL, NULL, NULL, NULL, 2, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_XunFeiStreamTTS_0003', 'TTS_XunFeiStreamTTS', '鑱嗗皬鐜�', 'x5_lingxiaoyue_flow', '涓枃', NULL, NULL, NULL, NULL, 3, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_XunFeiStreamTTS_0004', 'TTS_XunFeiStreamTTS', '鑱嗙帀鏄�', 'x5_lingyuzhao_flow', '涓枃', NULL, NULL, NULL, NULL, 4, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_XunFeiStreamTTS_0005', 'TTS_XunFeiStreamTTS', '鑱嗙帀瑷�', 'x5_lingyuyan_flow', '涓枃', NULL, NULL, NULL, NULL, 5, NULL, NULL, NULL, NULL);
+
+-- 闇�瑕佹坊鍔犲搴旂殑瑙掕壊闊宠壊
+INSERT INTO `ai_tts_voice` VALUES ('TTS_XunFeiStreamTTS_0006', 'TTS_XunFeiStreamTTS', '鑱嗛鍝�', 'x4_lingfeizhe_oral', '涓枃', NULL, NULL, NULL, NULL, 6, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_XunFeiStreamTTS_0007', 'TTS_XunFeiStreamTTS', '鑱嗗皬鐠�', 'x4_lingxiaoli_oral', '涓枃', NULL, NULL, NULL, NULL, 7, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_XunFeiStreamTTS_0008', 'TTS_XunFeiStreamTTS', '鑱嗗皬绯�', 'x5_lingxiaotang_flow', '涓枃', NULL, NULL, NULL, NULL, 8, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_XunFeiStreamTTS_0009', 'TTS_XunFeiStreamTTS', '鑱嗗皬鐞�', 'x4_lingxiaoqi_oral', '涓枃', NULL, NULL, NULL, NULL, 9, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_XunFeiStreamTTS_0010', 'TTS_XunFeiStreamTTS', '鑱嗕綉浣�-绔ュ勾濂冲0', 'x4_lingyouyou_oral', '涓枃', NULL, NULL, NULL, NULL, 10, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_XunFeiStreamTTS_0011', 'TTS_XunFeiStreamTTS', '瀛愭触', 'x4_zijin_oral', '澶╂触璇�', NULL, NULL, NULL, NULL, 11, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_XunFeiStreamTTS_0012', 'TTS_XunFeiStreamTTS', '瀛愰槼', 'x4_ziyang_oral', '涓滃寳璇�', NULL, NULL, NULL, NULL, 12, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_XunFeiStreamTTS_0013', 'TTS_XunFeiStreamTTS', 'Grant', 'x5_EnUs_Grant_flow', '鑻辨枃', NULL, NULL, NULL, NULL, 13, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_XunFeiStreamTTS_0014', 'TTS_XunFeiStreamTTS', 'Lila', 'x5_EnUs_Lila_flow', '鑻辨枃', NULL, NULL, NULL, NULL, 14, NULL, NULL, NULL, NULL);
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509220926.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509220926.sql
new file mode 100644
index 0000000..060fa4d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509220926.sql
@@ -0,0 +1,26 @@
+-- 娣诲姞璁娴佸紡璇煶璇嗗埆鏈嶅姟閰嶇疆
+delete from `ai_model_provider` where id = 'SYSTEM_ASR_XunfeiStream';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_ASR_XunfeiStream', 'ASR', 'xunfei_stream', '璁娴佸紡璇煶璇嗗埆', '[{"key":"app_id","label":"搴旂敤ID","type":"string"},{"key":"api_key","label":"API_KEY","type":"password"},{"key":"api_secret","label":"API_SECRET","type":"password"},{"key":"domain","label":"璇嗗埆棰嗗煙","type":"string"},{"key":"language","label":"璇嗗埆璇█","type":"string"},{"key":"accent","label":"鏂硅█","type":"string"},{"key":"dwa","label":"鍔ㄦ�佷慨姝�","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"}]', 18, 1, NOW(), 1, NOW());
+
+delete from `ai_model_config` where id = 'ASR_XunfeiStream';
+INSERT INTO `ai_model_config` VALUES ('ASR_XunfeiStream', 'ASR', '璁娴佸紡璇煶璇嗗埆', '璁娴佸紡璇煶璇嗗埆鏈嶅姟', 0, 1, '{"type": "xunfei_stream", "app_id": "", "api_key": "", "api_secret": "", "domain": "slm", "language": "zh_cn", "accent": "mandarin", "dwa": "wpgs", "output_dir": "tmp/"}', 'https://www.xfyun.cn/doc/spark/spark_zh_iat.html', '鏀寔瀹炴椂娴佸紡璇煶璇嗗埆锛岄�傜敤浜庝腑鏂囨櫘閫氳瘽鍙婂绉嶆柟瑷�璇嗗埆', 21, NULL, NULL, NULL, NULL);
+
+-- 鏇存柊璁娴佸紡璇煶璇嗗埆妯″瀷閰嶇疆鐨勮鏄庢枃妗�
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://www.xfyun.cn/doc/spark/spark_zh_iat.html',
+`remark` = '璁娴佸紡璇煶璇嗗埆閰嶇疆璇存槑锛�
+1. 鐧诲綍璁寮�鏀惧钩鍙� https://www.xfyun.cn/
+2. 鍒涘缓璇煶璇嗗埆搴旂敤鑾峰彇APPID銆丄PISecret銆丄PIKey
+3. 鍙傛暟璇存槑锛�
+ - app_id: 搴旂敤ID锛屽湪璁寮�鏀惧钩鍙板垱寤哄簲鐢ㄥ悗鑾峰緱
+ - api_key: API瀵嗛挜锛岀敤浜庢帴鍙i壌鏉�
+ - api_secret: API瀵嗛挜锛岀敤浜庣敓鎴愮鍚�
+ - domain: 璇嗗埆棰嗗煙锛岄粯璁lm锛堟櫤鑳藉寲璇煶杞啓锛�
+ - language: 璇嗗埆璇█锛岄粯璁h_cn锛堜腑鏂囷級
+ - accent: 鏂硅█绫诲瀷锛岄粯璁andarin锛堟櫘閫氳瘽锛夛紝鏀寔cantonese锛堢菠璇級绛�
+ - dwa: 鍔ㄦ�佷慨姝o紝榛樿wpgs锛堝紑鍚姩鎬佷慨姝o級
+ - output_dir: 闊抽鏂囦欢杈撳嚭鐩綍锛岄粯璁mp/
+4. 鏀寔瀹炴椂娴佸紡璇嗗埆锛岄�傜敤浜庡疄鏃惰闊充氦浜掑満鏅�
+5. 鏀寔澶氱鏂硅█鍜岃瑷�璇嗗埆
+' WHERE `id` = 'ASR_XunfeiStream';
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509220958.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509220958.sql
new file mode 100644
index 0000000..b05faa1
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509220958.sql
@@ -0,0 +1,19 @@
+delete from `ai_model_config` where id = 'LLM_XunfeiSparkLLM';
+INSERT INTO `ai_model_config` VALUES ('LLM_XunfeiSparkLLM', 'LLM', '璁鏄熺伀璁ょ煡澶фā鍨�', '璁鏄熺伀璁ょ煡澶фā鍨�', 0, 1, '{"type": "openai", "model_name": "generalv3.5", "base_url": "https://spark-api-open.xf-yun.com/v1", "api_password": "浣犵殑api_password", "temperature": 0.5, "max_tokens": 2048, "top_p": 1.0, "frequency_penalty": 0.0}', 'https://www.xfyun.cn/doc/spark/HTTP%E8%B0%83%E7%94%A8%E6%96%87%E6%A1%A3.html', '璁鏄熺伀璁ょ煡澶фā鍨嬶紝鏀寔澶氳疆瀵硅瘽銆佹枃鏈敓鎴愮瓑鍔熻兘', 14, NULL, NULL, NULL, NULL);
+
+-- 鏇存柊璁鏄熺伀璁ょ煡澶фā鍨嬮厤缃殑璇存槑鏂囨。
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://www.xfyun.cn/doc/spark/HTTP%E8%B0%83%E7%94%A8%E6%96%87%E6%A1%A3.html',
+`remark` = '璁鏄熺伀璁ょ煡澶фā鍨嬮厤缃鏄庯細
+1. 鐧诲綍璁寮�鏀惧钩鍙� https://www.xfyun.cn/锛屾瘡涓�涓ā鍨嬪搴旀瘡涓�涓猘pi_password,鏇存敼妯″瀷鏃堕渶瑕佹煡鐪嬪搴旀ā鍨嬬殑api_password
+2. 鍒涘缓鏄熺伀璁ょ煡澶фā鍨嬪簲鐢ㄨ幏鍙朅PI Password
+3. 鍙傛暟璇存槑锛�
+ - api_password: API Password锛屽湪璁寮�鏀惧钩鍙板垱寤哄簲鐢ㄥ悗鑾峰緱
+ - model_name: 妯″瀷鍚嶇О锛屾敮鎸乬eneralv3.5銆乬eneralv3绛夌増鏈�
+ - base_url: API鍦板潃锛岄粯璁ttps://spark-api-open.xf-yun.com/v1
+ - temperature: 娓╁害鍙傛暟锛屾帶鍒剁敓鎴愰殢鏈烘�э紝鑼冨洿0-1锛岄粯璁�0.5
+ - max_tokens: 鏈�澶ц緭鍑簍oken鏁帮紝榛樿2048
+ - top_p: 鏍稿績閲囨牱鍙傛暟锛屾帶鍒惰瘝姹囧鏍锋�э紝榛樿1.0
+ - frequency_penalty: 棰戠巼鎯╃綒锛岄檷浣庨噸澶嶅唴瀹癸紝榛樿0.0
+4. 姣忎竴涓ā鍨嬪搴旀瘡涓�涓猘pi_password,鏇存敼妯″瀷鏃堕渶瑕佹煡鐪嬪搴旀ā鍨嬬殑api_password銆�
+' WHERE `id` = 'LLM_XunfeiSparkLLM';
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509221530.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509221530.sql
new file mode 100644
index 0000000..078fbfa
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202509221530.sql
@@ -0,0 +1,7 @@
+-- 娣诲姞SM2鍥藉瘑绠楁硶瀵嗛挜鍙傛暟
+-- 鐢ㄤ簬鏈嶅姟鍣ㄧSM2鍔犲瘑瑙e瘑鍔熻兘
+
+-- 娣诲姞SM2瀵嗛挜鍙傛暟
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES
+(120, 'server.public_key', '', 'string', 1, '鏈嶅姟鍣⊿M2鍏挜'),
+(121, 'server.private_key', '', 'string', 1, '鏈嶅姟鍣⊿M2绉侀挜');
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202510071522.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202510071522.sql
new file mode 100644
index 0000000..f53d422
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202510071522.sql
@@ -0,0 +1,19 @@
+-- 澹伴煶鍏嬮殕琛�
+DROP TABLE IF EXISTS `ai_voice_clone`;
+CREATE TABLE `ai_voice_clone` (
+ `id` VARCHAR(32) NOT NULL COMMENT '鍞竴鏍囪瘑',
+ `name` VARCHAR(64) COMMENT '澹伴煶鍚嶇О',
+ `model_id` VARCHAR(32) COMMENT '妯″瀷id',
+ `voice_id` VARCHAR(32) COMMENT '澹伴煶id',
+ `user_id` BIGINT COMMENT '鐢ㄦ埛 ID锛堝叧鑱旂敤鎴疯〃锛�',
+ `voice` LONGBLOB COMMENT '澹伴煶',
+ `train_status` TINYINT(1) DEFAULT 0 COMMENT '璁粌鐘舵�侊細0寰呰缁� 1璁粌涓� 2璁粌鎴愬姛 3璁粌澶辫触',
+ `train_error` VARCHAR(255) COMMENT '璁粌閿欒鍘熷洜',
+ `creator` BIGINT COMMENT '鍒涘缓鑰� ID',
+ `create_date` DATETIME COMMENT '鍒涘缓鏃堕棿',
+ PRIMARY KEY (`id`),
+ INDEX idx_ai_voice_clone_user_id_model_id_train_status (model_id,user_id, train_status),
+ INDEX idx_ai_voice_clone_voice_id (voice_id),
+ INDEX idx_ai_voice_clone_user_id (user_id),
+ INDEX idx_ai_voice_clone_model_id_voice_id (model_id, voice_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='澹伴煶鍏嬮殕琛�';
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202510191042.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202510191042.sql
new file mode 100644
index 0000000..1511645
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202510191042.sql
@@ -0,0 +1,2 @@
+UPDATE `ai_model_config` SET model_code = 'XunfeiSparkLLM' WHERE id = 'LLM_XunfeiSparkLLM';
+UPDATE `ai_model_config` SET model_code = 'XunfeiStreamASR' WHERE id = 'ASR_XunfeiStream';
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202510250956.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202510250956.sql
new file mode 100644
index 0000000..b4129af
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202510250956.sql
@@ -0,0 +1,24 @@
+-- 娣诲姞RAG妯″瀷渚涘簲鍣ㄥ拰閰嶇疆
+-- -------------------------------------------------------
+
+-- 娣诲姞RAG妯″瀷渚涘簲鍣�
+delete from `ai_model_provider` where id = 'SYSTEM_RAG_ragflow';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_RAG_ragflow', 'RAG', 'ragflow', 'RAGFlow', '[{"key": "base_url", "type": "string", "label": "鏈嶅姟鍦板潃"}, {"key": "api_key", "type": "string", "label": "API瀵嗛挜"}]', 1, 1, NOW(), 1, NOW());
+
+-- 娣诲姞RAG妯″瀷閰嶇疆
+delete from `ai_model_config` where id = 'RAG_RAGFlow';
+INSERT INTO `ai_model_config` VALUES ('RAG_RAGFlow', 'RAG', 'ragflow', 'RAGFlow', 1, 1, '{"type": "ragflow", "base_url": "http://localhost", "api_key": "浣犵殑RAG瀵嗛挜"}', 'https://github.com/infiniflow/ragflow/blob/main/README_zh.md', 'RAGFlow閰嶇疆璇存槑锛�
+涓�銆佸揩閫熼儴缃叉暀绋嬶紙docker閮ㄧ讲锛�
+1.$ sysctl vm.max_map_count
+2.$ sysctl -w vm.max_map_count=262144
+3.$ git clone https://github.com/infiniflow/ragflow.git
+4.docker compose -f docker-compose.yml up -d
+5.$ docker logs -f docker-ragflow-cpu-1
+6.娉ㄥ唺鐧诲綍鍚庯紝鐐瑰嚮鍙充笂瑙掑ご鍍忥紝鑾峰緱RAGFlow鐨凙PI KEY鍜孉PI鏈嶅姟鍣ㄥ湴鍧�銆備娇鐢≧AGFlow鍓嶈鍦∕odel Provider涓坊鍔犳ā鍨嬪拰璁剧疆榛樿妯″瀷銆�
+浜屻�佸鏋滄偍甯屾湜鍏虫帀娉ㄥ唽鍔熻兘
+1.鍋滄鏈嶅姟 docker compose down
+2. sed -i ''s/REGISTER_ENABLED=1/REGISTER_ENABLED=0/g'' .env
+3.cat .env | grep -i register
+4.鐪嬪埌REGISTER_ENABLED=0 閲嶅惎鏈嶅姟鍗冲彲銆�', 1, NULL, NULL, NULL, NULL);
+
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202510251150.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202510251150.sql
new file mode 100644
index 0000000..4dea2be
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202510251150.sql
@@ -0,0 +1,19 @@
+-- 鐭ヨ瘑搴撹〃
+DROP TABLE IF EXISTS `ai_rag_dataset`;
+CREATE TABLE `ai_rag_dataset` (
+ `id` VARCHAR(32) NOT NULL COMMENT '鍞竴鏍囪瘑',
+ `dataset_id` VARCHAR(64) NOT NULL COMMENT '鐭ヨ瘑搴揑D',
+ `rag_model_id` VARCHAR(64) COMMENT 'RAG妯″瀷閰嶇疆ID',
+ `name` VARCHAR(100) NOT NULL COMMENT '鐭ヨ瘑搴撳悕绉�',
+ `description` TEXT COMMENT '鐭ヨ瘑搴撴弿杩�',
+ `status` TINYINT(1) DEFAULT 1 COMMENT '鐘舵�侊細0鍋滅敤 1鍚敤',
+ `creator` BIGINT COMMENT '鍒涘缓鑰�',
+ `created_at` DATETIME COMMENT '鍒涘缓鏃堕棿',
+ `updater` BIGINT COMMENT '鏇存柊鑰�',
+ `updated_at` DATETIME COMMENT '鏇存柊鏃堕棿',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_dataset_id` (`dataset_id`),
+ INDEX `idx_ai_rag_dataset_status` (`status`),
+ INDEX `idx_ai_rag_dataset_creator` (`creator`),
+ INDEX `idx_ai_rag_dataset_created_at` (`created_at`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鐭ヨ瘑搴撹〃';
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202511131023.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202511131023.sql
new file mode 100644
index 0000000..4efb121
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202511131023.sql
@@ -0,0 +1,62 @@
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://github.com/infiniflow/ragflow/blob/main/README_zh.md',
+`remark` = '璇︾粏閮ㄧ讲鏁欑▼鍙傝�冿細https://github.com/xinnan-tech/xiaozhi-esp32-server/blob/main/docs/ragflow-integration.md
+閮ㄧ讲鎴愬姛锛屾敞鍐婄櫥褰曞悗锛岀偣鍑诲彸涓婅澶村儚锛岃幏寰桼AGFlow鐨凙PI KEY鍜孉PI鏈嶅姟鍣ㄥ湴鍧�銆備娇鐢≧AGFlow鍓嶈鍦∕odel Provider涓坊鍔犳ā鍨嬪拰璁剧疆榛樿妯″瀷銆�' WHERE `id` = 'RAG_RAGFlow';
+
+update `ai_model_config` set `model_name` = '澶栨寕鐨勫ぇ妯″瀷鎰忓浘璇嗗埆' where `id` = 'Intent_intent_llm';
+update `ai_model_config` set `model_name` = '澶фā鍨嬭嚜涓诲嚱鏁拌皟鐢�' where `id` = 'Intent_function_call';
+update `ai_model_config` set `model_name` = '鐧剧偧鏅鸿兘浣撳簲鐢�' where `id` = 'LLM_AliAppLLM';
+
+update `ai_model_provider` set `name` = '澶栨寕鐨勫ぇ妯″瀷鎰忓浘璇嗗埆' , `fields` = '[{"key":"llm","label":"寮曠敤鐨凩LM妯″瀷","type":"string"}]' where id = 'SYSTEM_Intent_intent_llm';
+update `ai_model_provider` set `name` = '澶фā鍨嬭嚜涓诲嚱鏁拌皟鐢�' where id = 'SYSTEM_Intent_function_call';
+
+UPDATE ai_model_config set model_name = 'Linkerai(娴佸紡)' where id = 'TTS_LinkeraiTTS';
+UPDATE ai_model_config set model_name = '鐏北寮曟搸(娴佸紡)' where id = 'TTS_HuoshanDoubleStreamTTS';
+UPDATE ai_model_config set model_name = '闃块噷鐧剧偧(娴佸紡)' where id = 'TTS_AliBLStreamTTS';
+UPDATE ai_model_config set model_name = '绉戝ぇ璁(娴佸紡)' where id = 'TTS_XunFeiStreamTTS';
+UPDATE ai_model_config set model_name = 'Minimax(娴佸紡)' where id = 'TTS_MinimaxStreamTTS';
+UPDATE ai_model_config set model_name = '闃块噷浜�(娴佸紡)' where id = 'TTS_AliyunStreamTTS';
+UPDATE ai_model_config set model_name = 'PaddleSpeech(娴佸紡)' where id = 'TTS_PaddleSpeechTTS';
+UPDATE ai_model_config set model_name = 'IndexTTS(娴佸紡)' where id = 'TTS_IndexStreamTTS';
+
+update ai_model_config SET sort = 2 where id = 'TTS_LinkeraiTTS';
+update ai_model_config SET sort = 3 where id = 'TTS_HuoshanDoubleStreamTTS';
+update ai_model_config SET sort = 4 where id = 'TTS_AliBLStreamTTS';
+update ai_model_config SET sort = 5 where id = 'TTS_XunFeiStreamTTS';
+update ai_model_config SET sort = 6 where id = 'TTS_MinimaxStreamTTS';
+update ai_model_config SET sort = 7 where id = 'TTS_AliyunStreamTTS';
+update ai_model_config SET sort = 8 where id = 'TTS_IndexStreamTTS';
+update ai_model_config SET sort = 9 where id = 'TTS_PaddleSpeechTTS';
+update ai_model_config SET sort = 10 where id = 'TTS_DoubaoTTS';
+update ai_model_config SET sort = 11 where id = 'TTS_TencentTTS';
+update ai_model_config SET sort = 12 where id = 'TTS_AliyunTTS';
+update ai_model_config SET sort = 13 where id = 'TTS_OpenAITTS';
+update ai_model_config SET sort = 14 where id = 'TTS_CosyVoiceSiliconflow';
+update ai_model_config SET sort = 15 where id = 'TTS_CozeCnTTS';
+update ai_model_config SET sort = 16 where id = 'TTS_FishSpeech';
+update ai_model_config SET sort = 17 where id = 'TTS_GPT_SOVITS_V3';
+update ai_model_config SET sort = 18 where id = 'TTS_GPT_SOVITS_V2';
+update ai_model_config SET sort = 19 where id = 'TTS_TTS302AI';
+update ai_model_config SET sort = 20 where id = 'TTS_GizwitsTTS';
+update ai_model_config SET sort = 21 where id = 'TTS_VolcesAiGatewayTTS';
+update ai_model_config SET sort = 22 where id = 'TTS_ACGNTTS';
+update ai_model_config SET sort = 23 where id = 'TTS_CustomTTS';
+
+UPDATE ai_model_config set model_name = '璁璇煶璇嗗埆(娴佸紡)' where id = 'ASR_XunfeiStream';
+UPDATE ai_model_config set model_name = 'Qwen3Flash璇煶璇嗗埆' where id = 'ASR_Qwen3Flash';
+
+update ai_model_config SET sort = 2 where id = 'ASR_FunASRServer';
+update ai_model_config SET sort = 3 where id = 'ASR_XunfeiStream';
+update ai_model_config SET sort = 4 where id = 'ASR_AliyunStreamASR';
+update ai_model_config SET sort = 5 where id = 'ASR_DoubaoStreamASR';
+update ai_model_config SET sort = 6 where id = 'ASR_TencentASR';
+update ai_model_config SET sort = 7 where id = 'ASR_BaiduASR';
+update ai_model_config SET sort = 8 where id = 'ASR_DoubaoASR';
+update ai_model_config SET sort = 9 where id = 'ASR_AliyunASR';
+update ai_model_config SET sort = 10 where id = 'ASR_SherpaASR';
+update ai_model_config SET sort = 11 where id = 'ASR_OpenaiASR';
+update ai_model_config SET sort = 12 where id = 'ASR_GroqASR';
+update ai_model_config SET sort = 13 where id = 'ASR_VoskASR';
+update ai_model_config SET sort = 14 where id = 'ASR_Qwen3Flash';
+
+UPDATE ai_model_config SET config_json='{\"type\": \"openai\", \"base_url\": \"https://ark.cn-beijing.volces.com/api/v3\", \"model_name\": \"doubao-1-5-pro-32k-250115\", \"api_key\": \"浣犵殑api_key\"}' where id = 'LLM_DoubaoLLM' AND config_json LIKE '%浣犵殑api_key"%';
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202511221450.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202511221450.sql
new file mode 100644
index 0000000..5125b56
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202511221450.sql
@@ -0,0 +1,17 @@
+-- 鏇存柊HuoshanDoubleStreamTTS渚涘簲鍣ㄩ厤缃紝澧炲姞寮�鍚摼鎺ュ鐢ㄩ�夐」
+UPDATE `ai_model_provider`
+SET fields = '[{"key": "ws_url", "type": "string", "label": "WebSocket鍦板潃"}, {"key": "appid", "type": "string", "label": "搴旂敤ID"}, {"key": "access_token", "type": "string", "label": "璁块棶浠ょ墝"}, {"key": "resource_id", "type": "string", "label": "璧勬簮ID"}, {"key": "speaker", "type": "string", "label": "榛樿闊宠壊"}, {"key": "enable_ws_reuse", "type": "boolean", "label": "鏄惁寮�鍚摼鎺ュ鐢�", "default": true}, {"key": "speech_rate", "type": "number", "label": "璇��(-50~100)"}, {"key": "loudness_rate", "type": "number", "label": "闊抽噺(-50~100)"}, {"key": "pitch", "type": "number", "label": "闊抽珮(-12~12)"}]'
+WHERE id = 'SYSTEM_TTS_HSDSTTS';
+
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://console.volcengine.com/speech/service/10007',
+`remark` = '鐏北寮曟搸璇煶鍚堟垚鏈嶅姟閰嶇疆璇存槑锛�
+1. 璁块棶 https://www.volcengine.com/ 娉ㄥ唽骞跺紑閫氱伀灞卞紩鎿庤处鍙�
+2. 璁块棶 https://console.volcengine.com/speech/service/10007 寮�閫氳闊冲悎鎴愬ぇ妯″瀷锛岃喘涔伴煶鑹�
+3. 鍦ㄩ〉闈㈠簳閮ㄨ幏鍙朼ppid鍜宎ccess_token
+5. 璧勬簮ID鍥哄畾涓猴細volc.service_type.10029锛堝ぇ妯″瀷璇煶鍚堟垚鍙婃贩闊筹級
+6. 閾炬帴澶嶇敤锛氬紑鍚疻ebSocket杩炴帴澶嶇敤锛岄粯璁rue鍑忓皯閾炬帴鎹熻�楋紙娉ㄦ剰锛氬鐢ㄥ悗璁惧澶勪簬鑱嗗惉鐘舵�佹椂绌洪棽閾炬帴浼氬崰骞跺彂鏁帮級
+7. 璇�燂細-50~100锛屽彲涓嶅~锛屾甯搁粯璁ゅ��0锛屽彲濉�-50~100
+8. 闊抽噺锛�-50~100锛屽彲涓嶅~锛屾甯搁粯璁ゅ��0锛屽彲濉�-50~100
+9. 闊抽珮锛�-12~12锛屽彲涓嶅~锛屾甯搁粯璁ゅ��0锛屽彲濉�-12~12
+10. 濉叆閰嶇疆鏂囦欢涓�' WHERE `id` = 'TTS_HuoshanDoubleStreamTTS';
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512031517.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512031517.sql
new file mode 100644
index 0000000..2ad5bf2
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512031517.sql
@@ -0,0 +1,6 @@
+-- 娣诲姞绯荤粺鍔熻兘鑿滃崟閰嶇疆鍙傛暟
+delete from `sys_params` where param_code = 'system-web.menu';
+
+-- 娣诲姞绯荤粺鍔熻兘鑿滃崟閰嶇疆鍙傛暟
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES
+(600, 'system-web.menu', '{"features":{"voiceprintRecognition":{"name":"feature.voiceprintRecognition.name","enabled":false,"description":"feature.voiceprintRecognition.description"},"voiceClone":{"name":"feature.voiceClone.name","enabled":false,"description":"feature.voiceClone.description"},"knowledgeBase":{"name":"feature.knowledgeBase.name","enabled":false,"description":"feature.knowledgeBase.description"},"mcpAccessPoint":{"name":"feature.mcpAccessPoint.name","enabled":false,"description":"feature.mcpAccessPoint.description"},"vad":{"name":"feature.vad.name","enabled":true,"description":"feature.vad.description"},"asr":{"name":"feature.asr.name","enabled":true,"description":"feature.asr.description"}},"groups":{"featureManagement":["voiceprintRecognition","voiceClone","knowledgeBase","mcpAccessPoint"],"voiceManagement":["vad","asr"]}}', 'json', 1, '绯荤粺鍔熻兘鑿滃崟閰嶇疆');
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512041515.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512041515.sql
new file mode 100644
index 0000000..5e26b99
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512041515.sql
@@ -0,0 +1,14 @@
+-- liquibase formatted sql
+
+-- changeset xiaozhi:202512041515
+CREATE TABLE ai_agent_context_provider (
+ id VARCHAR(32) NOT NULL COMMENT '涓婚敭',
+ agent_id VARCHAR(32) NOT NULL COMMENT '鏅鸿兘浣揑D',
+ context_providers JSON COMMENT '涓婁笅鏂囨簮閰嶇疆',
+ creator BIGINT COMMENT '鍒涘缓鑰�',
+ created_at DATETIME COMMENT '鍒涘缓鏃堕棿',
+ updater BIGINT COMMENT '鏇存柊鑰�',
+ updated_at DATETIME COMMENT '鏇存柊鏃堕棿',
+ PRIMARY KEY (id),
+ INDEX idx_agent_id (agent_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鏅鸿兘浣撲笂涓嬫枃婧愰厤缃〃';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512131453.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512131453.sql
new file mode 100644
index 0000000..27ea4a1
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512131453.sql
@@ -0,0 +1,6 @@
+-- 鍒犻櫎server妯″潡鏄惁寮�鍚痶oken璁よ瘉鍙傛暟
+delete from `sys_params` where param_code = 'server.auth.enabled';
+
+-- 娣诲姞server妯″潡鏄惁寮�鍚痶oken璁よ瘉鍙傛暟
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES
+(122, 'server.auth.enabled', 'true', 'boolean', 1, 'server妯″潡鏄惁寮�鍚痶oken璁よ瘉');
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512161529.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512161529.sql
new file mode 100644
index 0000000..40f154c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512161529.sql
@@ -0,0 +1 @@
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (311, 'enable_websocket_ping', 'false', 'boolean', 1, '鏄惁鍚敤WebSocket蹇冭烦淇濇椿鏈哄埗');
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512192245.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512192245.sql
new file mode 100644
index 0000000..7162fd1
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512192245.sql
@@ -0,0 +1,2 @@
+-- 涓烘櫤鑳戒綋鑱婂ぉ鍘嗗彶璁板綍娣诲姞闊抽ID绱㈠紩
+ALTER TABLE ai_agent_chat_history ADD INDEX idx_ai_agent_chat_history_audio_id (audio_id);
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512221117.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512221117.sql
new file mode 100644
index 0000000..4a3c8d2
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512221117.sql
@@ -0,0 +1,10 @@
+-- 鏇存柊璞嗗寘娴佸紡ASR渚涘簲鍣紝澧炲姞end_window_size閰嶇疆
+delete from `ai_model_provider` where id = 'SYSTEM_ASR_DoubaoStreamASR';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_ASR_DoubaoStreamASR', 'ASR', 'doubao_stream', '鐏北寮曟搸璇煶璇嗗埆(娴佸紡)', '[{"key":"appid","label":"搴旂敤ID","type":"string"},{"key":"access_token","label":"璁块棶浠ょ墝","type":"string"},{"key":"cluster","label":"闆嗙兢","type":"string"},{"key":"boosting_table_name","label":"鐑瘝鏂囦欢鍚嶇О","type":"string"},{"key":"correct_table_name","label":"鏇挎崲璇嶆枃浠跺悕绉�","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"},{"key":"end_window_size","label":"闈欓煶鍒ゅ畾鏃堕暱(ms)","type":"number"}]', 3, 1, NOW(), 1, NOW());
+
+
+-- 鏇存柊璞嗗寘娴佸紡ASR妯″瀷閰嶇疆锛屽鍔爀nd_window_size榛樿鍊�
+UPDATE `ai_model_config` SET
+`config_json` = JSON_SET(`config_json`, '$.end_window_size', 200)
+WHERE `id` = 'ASR_DoubaoStreamASR' AND JSON_EXTRACT(`config_json`, '$.end_window_size') IS NULL;
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512301430.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512301430.sql
new file mode 100644
index 0000000..7ebac68
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202512301430.sql
@@ -0,0 +1,94 @@
+-- 娣诲姞闃块噷鐧剧偧Paraformer瀹炴椂璇煶璇嗗埆鏈嶅姟閰嶇疆
+delete from `ai_model_provider` where id = 'SYSTEM_ASR_AliyunBLStream';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_ASR_AliyunBLStream', 'ASR', 'aliyunbl_stream', '闃块噷鐧剧偧Paraformer瀹炴椂璇煶璇嗗埆', '[{"key":"api_key","label":"API瀵嗛挜","type":"password"},{"key":"model","label":"妯″瀷鍚嶇О","type":"string"},{"key":"format","label":"闊抽鏍煎紡","type":"string"},{"key":"sample_rate","label":"閲囨牱鐜�","type":"number"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"}]', 18, 1, NOW(), 1, NOW());
+
+delete from `ai_model_config` where id = 'ASR_AliyunBLStream';
+INSERT INTO `ai_model_config` VALUES ('ASR_AliyunBLStream', 'ASR', 'AliyunBLStream', '闃块噷鐧剧偧Paraformer瀹炴椂璇煶璇嗗埆', 0, 1, '{"type": "aliyunbl_stream", "api_key": "", "model": "paraformer-realtime-v2", "format": "pcm", "sample_rate": 16000, "disfluency_removal_enabled": false, "semantic_punctuation_enabled": false, "max_sentence_silence": 200, "multi_threshold_mode_enabled": false, "punctuation_prediction_enabled": true, "inverse_text_normalization_enabled": true, "output_dir": "tmp/"}', 'https://help.aliyun.com/zh/model-studio/websocket-for-paraformer-real-time-service', '鏀寔澶氳瑷�銆佺儹璇嶅畾鍒躲�佽涔夋柇鍙ョ瓑楂樼骇鍔熻兘', 21, NULL, NULL, NULL, NULL);
+
+-- 鏇存柊闃块噷鐧剧偧Paraformer妯″瀷閰嶇疆鐨勮鏄庢枃妗�
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://help.aliyun.com/zh/model-studio/websocket-for-paraformer-real-time-service',
+`remark` = '闃块噷鐧剧偧Paraformer瀹炴椂璇煶璇嗗埆閰嶇疆璇存槑锛�
+1. 鐧诲綍闃块噷浜戠櫨鐐煎钩鍙� https://bailian.console.aliyun.com/
+2. 鍒涘缓API-KEY https://bailian.console.aliyun.com/#/api-key
+3. 鏀寔妯″瀷锛歱araformer-realtime-v2(鎺ㄨ崘)銆乸araformer-realtime-8k-v2銆乸araformer-realtime-v1銆乸araformer-realtime-8k-v1
+4. 鍔熻兘鐗规�э細
+ - 澶氳瑷�鏀寔(涓枃鍚柟瑷�銆佽嫳鏂囥�佹棩璇�侀煩璇�佸痉璇�佹硶璇�佷縿璇�)
+ - 鐑瘝瀹氬埗(vocabulary_id鍙傛暟)锛岃缁嗚鏄庤鍙傝�冿細https://help.aliyun.com/zh/model-studio/custom-hot-words?
+ - 璇箟鏂彞/VAD鏂彞(semantic_punctuation_enabled鍙傛暟)
+ - 鑷姩鏍囩偣绗﹀彿銆両TN銆佽繃婊よ姘旇瘝绛�
+5. 鍙傛暟璇存槑锛�
+ - model: 妯″瀷鍚嶇О锛屾帹鑽恜araformer-realtime-v2
+ - sample_rate: 閲囨牱鐜�(Hz)锛寁2鏀寔浠绘剰閲囨牱鐜囷紝v1浠呮敮鎸�16000锛�8k鐗堟湰浠呮敮鎸�8000
+ - semantic_punctuation_enabled: false涓篤AD鏂彞(浣庡欢杩�)锛宼rue涓鸿涔夋柇鍙�(楂樺噯纭�)
+ - max_sentence_silence: VAD鏂彞闈欓煶鏃堕暱闃堝��(200-6000ms)
+' WHERE `id` = 'ASR_AliyunBLStream';
+
+
+-- 鏇存柊璞嗗寘娴佸紡ASR渚涘簲鍣紝澧炲姞閰嶇疆
+delete from `ai_model_provider` where id = 'SYSTEM_ASR_DoubaoStreamASR';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_ASR_DoubaoStreamASR', 'ASR', 'doubao_stream', '鐏北寮曟搸璇煶璇嗗埆(娴佸紡)', '[{"key":"appid","label":"搴旂敤ID","type":"string"},{"key":"access_token","label":"璁块棶浠ょ墝","type":"string"},{"key":"cluster","label":"闆嗙兢","type":"string"},{"key":"boosting_table_name","label":"鐑瘝鏂囦欢鍚嶇О","type":"string"},{"key":"correct_table_name","label":"鏇挎崲璇嶆枃浠跺悕绉�","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"},{"key":"end_window_size","label":"闈欓煶鍒ゅ畾鏃堕暱(ms)","type":"number"},{"key":"enable_multilingual","label":"鏄惁寮�鍚璇璇嗗埆妯″紡","type":"boolean"},{"key":"language","label":"鎸囧畾璇█缂栫爜","type":"string"}]', 3, 1, NOW(), 1, NOW());
+UPDATE `ai_model_config` SET
+`remark` = '璞嗗寘ASR閰嶇疆璇存槑锛�
+1. 璞嗗寘ASR鍜岃眴鍖�(娴佸紡)ASR鐨勫尯鍒槸锛氳眴鍖匒SR鏄寜娆℃敹璐癸紝璞嗗寘(娴佸紡)ASR鏄寜鏃舵敹璐�
+2. 涓�鑸潵璇存寜娆℃敹璐圭殑鏇翠究瀹滐紝浣嗘槸璞嗗寘(娴佸紡)ASR浣跨敤浜嗗ぇ妯″瀷鎶�鏈紝鏁堟灉鏇村ソ
+3. 闇�瑕佸湪鐏北寮曟搸鎺у埗鍙板垱寤哄簲鐢ㄥ苟鑾峰彇appid鍜宎ccess_token
+4. 鏀寔涓枃璇煶璇嗗埆
+5. 闇�瑕佺綉缁滆繛鎺�
+6. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍
+鐢宠姝ラ锛�
+1. 璁块棶 https://console.volcengine.com/speech/app
+2. 鍒涘缓鏂板簲鐢�
+3. 鑾峰彇appid鍜宎ccess_token
+4. 濉叆閰嶇疆鏂囦欢涓�
+濡傞渶璁剧疆鐑瘝锛岃鍙傝�冿細https://www.volcengine.com/docs/6561/155738
+濡傚紑鍚璇璇嗗埆妯″紡锛岃璁剧疆language褰撹閿负绌烘椂锛岃妯″瀷鏀寔涓嫳鏂囥�佷笂娴疯瘽銆侀椊鍗楄锛屽洓宸濄�侀檿瑗裤�佺菠璇瘑鍒�傚叾浠栬绉嶈鍙傝�冿細https://www.volcengine.com/docs/6561/1354869
+' WHERE `id` = 'ASR_DoubaoStreamASR';
+
+-- 鏇存柊璞嗗寘娴佸紡ASR妯″瀷閰嶇疆锛屽鍔爀nable_multilingual榛樿鍊�
+UPDATE `ai_model_config` SET
+`config_json` = JSON_SET(
+ `config_json`,
+ '$.enable_multilingual', false,
+ '$.language', 'zh-CN'
+)
+WHERE `id` = 'ASR_DoubaoStreamASR'
+AND JSON_EXTRACT(`config_json`, '$.enable_multilingual') IS NULL
+AND JSON_EXTRACT(`config_json`, '$.language') IS NULL;
+
+
+-- 鏇存柊HuoshanDoubleStreamTTS渚涘簲鍣ㄩ厤缃紝澧炲姞澶氭儏鎰熼煶鑹插弬鏁�
+UPDATE `ai_model_provider`
+SET `fields` = '[{"key": "ws_url", "type": "string", "label": "WebSocket鍦板潃"}, {"key": "appid", "type": "string", "label": "搴旂敤ID"}, {"key": "access_token", "type": "string", "label": "璁块棶浠ょ墝"}, {"key": "resource_id", "type": "string", "label": "璧勬簮ID"}, {"key": "speaker", "type": "string", "label": "榛樿闊宠壊"}, {"key": "enable_ws_reuse", "type": "boolean", "label": "鏄惁寮�鍚摼鎺ュ鐢�", "default": true}, {"key": "speech_rate", "type": "number", "label": "璇��(-50~100)"}, {"key": "loudness_rate", "type": "number", "label": "闊抽噺(-50~100)"}, {"key": "pitch", "type": "number", "label": "闊抽珮(-12~12)"}, {"key": "emotion_scale", "type": "number", "label": "鎯呮劅寮哄害(1-5)"}, {"key": "emotion", "type": "string", "label": "鎯呮劅绫诲瀷"}]'
+WHERE `id` = 'SYSTEM_TTS_HSDSTTS';
+
+-- 鏇存柊榛樿鍊�
+UPDATE `ai_model_config` SET
+`config_json` = JSON_SET(
+ `config_json`,
+ '$.emotion', 'neutral',
+ '$.emotion_scale', 4
+)
+WHERE `id` = 'TTS_HuoshanDoubleStreamTTS'
+AND JSON_EXTRACT(`config_json`, '$.emotion') IS NULL
+AND JSON_EXTRACT(`config_json`, '$.emotion_scale') IS NULL;
+
+-- 澧炲姞鏂囨。閾炬帴鍜屽娉�
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://console.volcengine.com/speech/service/10007',
+`remark` = '鐏北寮曟搸璇煶鍚堟垚鏈嶅姟閰嶇疆璇存槑锛�
+1. 璁块棶 https://www.volcengine.com/ 娉ㄥ唽骞跺紑閫氱伀灞卞紩鎿庤处鍙�
+2. 璁块棶 https://console.volcengine.com/speech/service/10007 寮�閫氳闊冲悎鎴愬ぇ妯″瀷锛岃喘涔伴煶鑹�
+3. 鍦ㄩ〉闈㈠簳閮ㄨ幏鍙朼ppid鍜宎ccess_token
+5. 璧勬簮ID鍥哄畾涓猴細volc.service_type.10029锛堝ぇ妯″瀷璇煶鍚堟垚鍙婃贩闊筹級
+6. 閾炬帴澶嶇敤锛氬紑鍚疻ebSocket杩炴帴澶嶇敤锛岄粯璁rue鍑忓皯閾炬帴鎹熻�楋紙娉ㄦ剰锛氬鐢ㄥ悗璁惧澶勪簬鑱嗗惉鐘舵�佹椂绌洪棽閾炬帴浼氬崰骞跺彂鏁帮級
+7. 璇�燂細-50~100锛屽彲涓嶅~锛屾甯搁粯璁ゅ��0锛屽彲濉�-50~100
+8. 闊抽噺锛�-50~100锛屽彲涓嶅~锛屾甯搁粯璁ゅ��0锛屽彲濉�-50~100
+9. 闊抽珮锛�-12~12锛屽彲涓嶅~锛屾甯搁粯璁ゅ��0锛屽彲濉�-12~12
+10. 澶氭儏鎰熷弬鏁帮紙褰撳墠浠呴儴鍒嗛煶鑹叉敮鎸佽缃儏鎰燂級锛�
+ 鐩稿叧闊宠壊鍒楄〃锛歨ttps://www.volcengine.com/docs/6561/1257544
+ - emotion_scale锛氭儏鎰熷己搴︼紝鍙�夊�间负锛�1~5锛岄粯璁ゅ�间负4
+ - emotion锛氭儏鎰熺被鍨嬶紝鍙�夊�间负锛歯eutral銆乭appy銆乻ad銆乤ngry銆乫earful銆乨isgusted銆乻urprised
+' WHERE `id` = 'TTS_HuoshanDoubleStreamTTS';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202601051433.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202601051433.sql
new file mode 100644
index 0000000..15f6d57
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202601051433.sql
@@ -0,0 +1,11 @@
+-- 鏇存敼FunASRServer璇存槑鏂囨。妯″瀷涓篠enseVoiceSmall
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://github.com/modelscope/FunASR/blob/main/runtime/docs/SDK_advanced_guide_online_zh.md',
+`remark` = '鐙珛閮ㄧ讲FunASR锛屼娇鐢‵unASR鐨凙PI鏈嶅姟锛屽彧闇�瑕佷簲鍙ヨ瘽
+绗竴鍙ワ細mkdir -p ./funasr-runtime-resources/models
+绗簩鍙ワ細sudo docker run -d -p 10096:10095 --privileged=true -v $PWD/funasr-runtime-resources/models:/workspace/models registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr:funasr-runtime-sdk-online-cpu-0.1.12
+涓婁竴鍙ヨ瘽鎵ц鍚庝細杩涘叆鍒板鍣紝缁х画绗笁鍙ワ細cd FunASR/runtime
+涓嶈閫�鍑哄鍣紝缁х画鍦ㄥ鍣ㄤ腑鎵ц绗洓鍙ワ細nohup bash run_server_2pass.sh --download-model-dir /workspace/models --vad-dir damo/speech_fsmn_vad_zh-cn-16k-common-onnx --model-dir iic/SenseVoiceSmall-onnx --online-model-dir damo/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online-onnx --punc-dir damo/punc_ct-transformer_zh-cn-common-vad_realtime-vocab272727-onnx --lm-dir damo/speech_ngram_lm_zh-cn-ai-wesp-fst --itn-dir thuduj12/fst_itn_zh --hotword /workspace/models/hotwords.txt > log.txt 2>&1 &
+涓婁竴鍙ヨ瘽鎵ц鍚庝細杩涘叆鍒板鍣紝缁х画绗簲鍙ワ細tail -f log.txt
+绗簲鍙ヨ瘽鎵ц瀹屽悗锛屼細鐪嬪埌妯″瀷涓嬭浇鏃ュ織锛屼笅杞藉畬鍚庡氨鍙互杩炴帴浣跨敤浜�
+浠ヤ笂鏄娇鐢–PU鎺ㄧ悊锛屽鏋滄湁GPU锛岃缁嗗弬鑰冿細https://github.com/modelscope/FunASR/blob/main/runtime/docs/SDK_advanced_guide_online_zh.md' WHERE `id` = 'ASR_FunASRServer';
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202601141645.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202601141645.sql
new file mode 100644
index 0000000..8b0fca8
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202601141645.sql
@@ -0,0 +1,31 @@
+-- 鎵归噺娓呯悊 ai_model_provider 涓殑 sample_rate 瀛楁瀹氫箟
+UPDATE `ai_model_provider` ap
+JOIN (
+ SELECT
+ id,
+ JSON_ARRAYAGG(
+ JSON_OBJECT('key', jt.k, 'label', jt.l, 'type', jt.t)
+ ) AS new_fields
+ FROM `ai_model_provider`,
+ JSON_TABLE(`fields`, '$[*]' COLUMNS (
+ k VARCHAR(50) PATH '$.key',
+ l VARCHAR(100) PATH '$.label',
+ t VARCHAR(20) PATH '$.type'
+ )) AS jt
+ WHERE `model_type` = 'TTS'
+ AND jt.k != 'sample_rate'
+ GROUP BY id
+) filtered ON ap.id = filtered.id
+SET ap.fields = filtered.new_fields;
+
+-- 娓呯悊 config_json 椤跺眰鐨� sample_rate
+UPDATE `ai_model_config`
+SET `config_json` = JSON_REMOVE(`config_json`, '$.sample_rate')
+WHERE `model_type` = 'TTS'
+ AND JSON_EXTRACT(`config_json`, '$.sample_rate') IS NOT NULL;
+
+-- 娓呯悊Minimax娴佸紡TTS鐨剆ample_rate鍙傛暟锛堜綅浜巃udio_setting鍐呴儴锛�
+UPDATE `ai_model_config` SET
+`config_json` = JSON_SET(`config_json`, '$.audio_setting', JSON_REMOVE(JSON_EXTRACT(`config_json`, '$.audio_setting'), '$.sample_rate'))
+WHERE `id` = 'TTS_MinimaxStreamTTS'
+AND JSON_EXTRACT(`config_json`, '$.audio_setting.sample_rate') IS NOT NULL;
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202601231530.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202601231530.sql
new file mode 100644
index 0000000..79be431
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202601231530.sql
@@ -0,0 +1,87 @@
+-- 鏇存柊HuoshanDoubleStreamTTS渚涘簲鍣ㄩ厤缃紝灏嗗垎鏁g殑鍙傛暟鏀逛负JSON瀛楀吀閰嶇疆
+-- 灏� speech_rate, loudness_rate, pitch, emotion, emotion_scale 绛夊弬鏁版暣鍚堜负 audio_params, additions, mix_speaker 涓変釜JSON瀛楀吀
+
+UPDATE `ai_model_provider`
+SET `fields` = '[
+ {"key": "ws_url", "type": "string", "label": "WebSocket鍦板潃"},
+ {"key": "appid", "type": "string", "label": "搴旂敤ID"},
+ {"key": "access_token", "type": "string", "label": "璁块棶浠ょ墝"},
+ {"key": "resource_id", "type": "string", "label": "璧勬簮ID"},
+ {"key": "speaker", "type": "string", "label": "榛樿闊宠壊"},
+ {"key": "enable_ws_reuse", "type": "boolean", "label": "鏄惁寮�鍚摼鎺ュ鐢�", "default": true},
+ {"key": "audio_params", "type": "dict", "label": "闊抽杈撳嚭閰嶇疆"},
+ {"key": "additions", "type": "dict", "label": "楂樼骇鏂囨湰澶勭悊閰嶇疆"},
+ {"key": "mix_speaker", "type": "dict", "label": "娣烽煶鎺у埗閰嶇疆"}
+]'
+WHERE `id` = 'SYSTEM_TTS_HSDSTTS';
+
+-- 鏇存柊鐜版湁閰嶇疆锛屽皢鏃х殑鍒嗘暎鍙傛暟杩佺Щ鍒版柊鐨凧SON瀛楀吀缁撴瀯
+UPDATE `ai_model_config`
+SET `config_json` = JSON_SET(
+ `config_json`,
+ '$.audio_params', JSON_OBJECT(
+ 'speech_rate', CAST(COALESCE(NULLIF(JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.speech_rate')), ''), '0') AS SIGNED),
+ 'loudness_rate', CAST(COALESCE(NULLIF(JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.loudness_rate')), ''), '0') AS SIGNED)
+ ),
+ '$.additions', JSON_OBJECT(
+ 'aigc_metadata', JSON_OBJECT(),
+ 'cache_config', JSON_OBJECT(),
+ 'post_process', JSON_OBJECT(
+ 'pitch', CAST(COALESCE(NULLIF(JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.pitch')), ''), '0') AS SIGNED)
+ )
+ ),
+ '$.mix_speaker', JSON_OBJECT()
+)
+WHERE `id` = 'TTS_HuoshanDoubleStreamTTS';
+
+-- 鍒犻櫎鏃х殑鍒嗘暎鍙傛暟瀛楁
+UPDATE `ai_model_config`
+SET `config_json` = JSON_REMOVE(
+ `config_json`,
+ '$.speech_rate',
+ '$.loudness_rate',
+ '$.pitch',
+ '$.emotion',
+ '$.emotion_scale'
+)
+WHERE `id` = 'TTS_HuoshanDoubleStreamTTS';
+
+-- 鏇存柊鏂囨。閾炬帴鍜屽娉ㄨ鏄�
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://www.volcengine.com/docs/6561/1329505',
+`remark` = '鐏北寮曟搸鍙屽悜娴佸紡TTS閰嶇疆璇存槑锛�
+1. 璁块棶 https://www.volcengine.com/ 娉ㄥ唽骞跺紑閫氱伀灞卞紩鎿庤处鍙�
+2. 璁块棶 https://console.volcengine.com/speech/service/10007 寮�閫氳闊冲悎鎴愬ぇ妯″瀷锛岃喘涔伴煶鑹�
+3. 鍦ㄩ〉闈㈠簳閮ㄨ幏鍙朼ppid鍜宎ccess_token
+4. 璧勬簮ID鍥哄畾涓猴細volc.service_type.10029锛堝ぇ妯″瀷璇煶鍚堟垚鍙婃贩闊筹級
+5. 閾炬帴澶嶇敤锛氬紑鍚疻ebSocket杩炴帴澶嶇敤锛岄粯璁rue鍑忓皯閾炬帴鎹熻�楋紙娉ㄦ剰锛氬鐢ㄥ悗璁惧澶勪簬鑱嗗惉鐘舵�佹椂绌洪棽閾炬帴浼氬崰骞跺彂鏁帮級
+
+璇︾粏鍙傛暟鏂囨。锛歨ttps://www.volcengine.com/docs/6561/1329505
+銆恆udio_params銆戦煶棰戣緭鍑洪厤缃� - 鐢ㄦ埛鍙嚜瀹氫箟娣诲姞鐏北寮曟搸鏀寔鐨勪换浣曢煶棰戝弬鏁�
+ - speech_rate: 璇��(-50~100)锛岄粯璁�0
+ - loudness_rate: 闊抽噺(-50~100)锛岄粯璁�0
+ - emotion: 鎯呮劅绫诲瀷锛堜粎閮ㄥ垎闊宠壊鏀寔锛夛紝鍙�夊�硷細neutral銆乭appy銆乻ad銆乤ngry銆乫earful銆乨isgusted銆乻urprised
+ - emotion_scale: 鎯呮劅寮哄害(1~5)锛岄粯璁�4
+ 绀轰緥锛歿"speech_rate": 10, "loudness_rate": 5, "emotion": "happy", "emotion_scale": 4}
+
+銆恆dditions銆戦珮绾ф枃鏈鐞嗛厤缃� - 鐢ㄦ埛鍙嚜瀹氫箟娣诲姞鐏北寮曟搸鏀寔鐨勪换浣曢珮绾у弬鏁�
+ - post_process.pitch: 闊抽珮(-12~12)锛岄粯璁�0
+ - aigc_metadata: AIGC鍏冩暟鎹厤缃�
+ - cache_config: 缂撳瓨閰嶇疆
+ 绀轰緥锛歿"post_process": {"pitch": 2}, "aigc_metadata": {}, "cache_config": {}}
+
+銆恗ix_speaker銆戞贩闊虫帶鍒堕厤缃� - 澶氶煶鑹叉贩鍚堬紙浠� TTS 1.0锛�
+ 绀轰緥锛�
+ {"speakers": [
+ {"source_speaker": "zh_male_bvlazysheep","mix_factor": 0.3},
+ {"source_speaker": "BV120_streaming","mix_factor": 0.3},
+ {"source_speaker": "zh_male_ahu_conversation_wvae_bigtts","mix_factor": 0.4}
+ ]}
+
+娉ㄦ剰锛�
+- 澶氭儏鎰熼煶鑹插弬鏁帮紙emotion銆乪motion_scale锛変粎閮ㄥ垎闊宠壊鏀寔
+- 鐩稿叧闊宠壊鍒楄〃锛歨ttps://www.volcengine.com/docs/6561/1257544
+- 鐢ㄦ埛鍙牴鎹伀灞卞紩鎿嶢PI鏂囨。鑷娣诲姞鏇村鍙傛暟
+- 娣烽煶鍔熻兘涓昏閫傜敤浜庤眴鍖呰闊冲悎鎴愭ā鍨�1.0鐨勯煶鑹诧紝浣跨敤鏃堕渶瑕佸皢req_params.speaker璁剧疆涓篶ustom_mix_bigtts
+'
+WHERE `id` = 'TTS_HuoshanDoubleStreamTTS';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202601261730.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202601261730.sql
new file mode 100644
index 0000000..5c2238a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202601261730.sql
@@ -0,0 +1,14 @@
+-- 鏇存柊灏忔櫤鍙傛暟涓殑榛樿閲囨牱鐜囦粠 16000 鏀逛负 24000
+UPDATE `sys_params`
+SET `param_value` = '{
+ "type": "hello",
+ "version": 1,
+ "transport": "websocket",
+ "audio_params": {
+ "format": "opus",
+ "sample_rate": 24000,
+ "channels": 1,
+ "frame_duration": 60
+ }
+}'
+WHERE `id` = 309 AND `param_code` = 'xiaozhi';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602021555.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602021555.sql
new file mode 100644
index 0000000..4f4602d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602021555.sql
@@ -0,0 +1 @@
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (502, 'system_error_response', '涓讳汉锛屽皬鏅虹幇鍦ㄦ湁鐐瑰繖锛屾垜浠◢鍚庡啀璇曞惂銆�', 'string', 1, '绯荤粺閿欒鏃剁殑鍥炲');
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602051017.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602051017.sql
new file mode 100644
index 0000000..c29347c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602051017.sql
@@ -0,0 +1,115 @@
+-- 鏂板powermem璁板繂妯″瀷渚涘簲鍣�
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`)
+VALUES ('SYSTEM_Memory_powermem', 'Memory', 'powermem', 'PowerMem璁板繂', '[
+ {"key":"enable_user_profile","label":"鍚敤鐢ㄦ埛鐢诲儚","type":"boolean"},
+ {"key":"llm_provider","label":"LLM鎻愪緵鍟�","type":"string"},
+ {"key":"llm_api_key","label":"LLM API瀵嗛挜","type":"string"},
+ {"key":"llm_model","label":"LLM妯″瀷","type":"string"},
+ {"key":"openai_base_url","label":"OpenAI鍩虹URL","type":"string"},
+ {"key":"embedding_provider","label":"Embedding鎻愪緵鍟�","type":"string"},
+ {"key":"embedding_api_key","label":"Embedding API瀵嗛挜","type":"string"},
+ {"key":"embedding_model","label":"Embedding妯″瀷","type":"string"},
+ {"key":"embedding_openai_base_url","label":"Embedding OpenAI鍩虹URL","type":"string"},
+ {"key":"embedding_dims","label":"Embedding缁村害","type":"integer"},
+ {"key":"vector_store","label":"鍚戦噺瀛樺偍閰嶇疆(JSON)","type":"dict"}
+]', 4, 1, NOW(), 1, NOW());
+
+-- 鏂板PowerMem璁板繂妯″瀷閰嶇疆
+INSERT INTO `ai_model_config` VALUES (
+ 'Memory_powermem',
+ 'Memory',
+ 'powermem',
+ 'PowerMem璁板繂',
+ 0,
+ 1,
+ '{\"type\": \"powermem\", \"enable_user_profile\": true, \"llm_provider\": \"openai\", \"llm_api_key\": \"浣犵殑LLM API瀵嗛挜\", \"llm_model\": \"qwen-plus\", \"openai_base_url\": \"\", \"embedding_provider\": \"openai\", \"embedding_api_key\": \"浣犵殑宓屽叆妯″瀷API瀵嗛挜\", \"embedding_model\": \"text-embedding-v4\", \"embedding_openai_base_url\": \"https://api.openai.com/v1\", \"embedding_dims\": \"\", \"vector_store\": {\"provider\": \"sqlite\", \"config\": {}}}',
+ NULL,
+ NULL,
+ 4,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+);
+
+
+-- PowerMem璁板繂閰嶇疆璇存槑
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://github.com/oceanbase/powermem',
+`remark` = 'PowerMem鏄疧ceanBase寮�婧愮殑agent璁板繂缁勪欢锛岄�氳繃鏈湴LLM杩涜璁板繂鎬荤粨
+GitHub: https://github.com/oceanbase/powermem
+瀹樼綉: https://www.powermem.ai/
+浣跨敤绀轰緥: https://github.com/oceanbase/powermem/tree/main/examples
+
+銆愯垂鐢ㄨ鏄庛��
+PowerMem鏈韩鍏嶈垂锛屽疄闄呰垂鐢ㄥ彇鍐充簬鎵�閫塋LM鍜屾暟鎹簱锛�
+- 浣跨敤sqlite + 鍏嶈垂LLM(濡俫lm-4-flash) = 瀹屽叏鍏嶈垂
+- 浣跨敤浜戠LLM鎴栦簯绔暟鎹簱 = 鎸夊搴旀湇鍔℃敹璐�
+
+銆恊nable_user_profile銆戠敤鎴风敾鍍忓姛鑳�
+- false: 浣跨敤鏅�氳蹇嗘ā寮�(AsyncMemory)
+- true: 浣跨敤鐢ㄦ埛鐢诲儚妯″紡(UserMemory)锛岃嚜鍔ㄦ彁鍙栫敤鎴蜂俊鎭�
+- 鐢ㄦ埛鐢诲儚鍔熻兘鏀寔: oceanbase銆乻eekdb銆乻qlite (powermem 0.3.0+)
+
+銆恖lm銆慙LM閰嶇疆 - 鐢ㄤ簬璁板繂鎬荤粨鍜岀敤鎴风敾鍍忔彁鍙�
+ provider: LLM鎻愪緵鍟嗭紝鍙�夊�硷細
+ - qwen: 閫氫箟鍗冮棶 (https://bailian.console.aliyun.com/?apiKey=1#/api-key)
+ - openai: OpenAI鍏煎鎺ュ彛
+ - zhipu: 鏅鸿氨AI (https://bigmodel.cn/usercenter/proj-mgmt/apikeys) - 鎺ㄨ崘浣跨敤鍏嶈垂鐨刧lm-4-flash
+ config: LLM閰嶇疆鍙傛暟
+ - api_key: API瀵嗛挜 (蹇呭~)
+ - model: 妯″瀷鍚嶇О锛屽 qwen-plus銆乬lm-4-flash 绛�
+ - openai_base_url: 鑷畾涔夋湇鍔″湴鍧� (鍙��)锛屽 https://api.openai.com/v1
+ 绀轰緥锛�
+ {"provider": "zhipu", "config": {"api_key": "your_key", "model": "glm-4-flash"}}
+ {"provider": "qwen", "config": {"api_key": "your_key", "model": "qwen-plus"}}
+
+銆恊mbedder銆慐mbedding閰嶇疆 - 鐢ㄤ簬鍚戦噺鍖栬蹇嗗唴瀹�
+ provider: 宓屽叆妯″瀷鎻愪緵鍟嗭紝鍙�夊�硷細
+ - qwen: 閫氫箟鍗冮棶
+ - openai: OpenAI鍏煎鎺ュ彛
+ config: Embedding閰嶇疆鍙傛暟
+ - api_key: API瀵嗛挜 (蹇呭~)
+ - model: 妯″瀷鍚嶇О锛屽 text-embedding-v4銆乼ext-embedding-3-small 绛�
+ - openai_base_url: 鑷畾涔夋湇鍔″湴鍧� (鍙��)
+ - embedding_dims: 鍚戦噺缁村害 (鍙��)锛岄潪1536鏃堕渶閰嶇疆
+ 绀轰緥锛�
+ {"provider": "openai", "config": {"api_key": "your_key", "model": "text-embedding-v4", "openai_base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1"}}
+
+銆恦ector_store銆戞暟鎹簱瀛樺偍閰嶇疆 - 鐢ㄤ簬瀛樺偍鍚戦噺鍖栫殑璁板繂
+ provider: 鏁版嵁搴撶被鍨嬶紝鍙�夊�硷細
+ - sqlite: 杞婚噺绾ф湰鍦版暟鎹簱 (鎺ㄨ崘鍏ラ棬浣跨敤锛屾棤闇�棰濆閰嶇疆)
+ - oceanbase: OceanBase鏁版嵁搴� (鎺ㄨ崘鐢熶骇浣跨敤锛屾渶浣虫�ц兘)
+ - seekdb: SeekDB (鎺ㄨ崘锛孉I搴旂敤瀛樺偍涓�浣�)
+ - postgres: PostgreSQL鏁版嵁搴�
+
+ SQLite閰嶇疆 (鏃犻渶棰濆閰嶇疆):
+ {"provider": "sqlite", "config": {}}
+
+ OceanBase閰嶇疆绀轰緥:
+ {"provider": "oceanbase", "config": {
+ "host": "127.0.0.1",
+ "port": 2881,
+ "user": "root@test",
+ "password": "your_password",
+ "db_name": "powermem",
+ "collection_name": "memories",
+ "embedding_model_dims": 1024
+ }}
+ 娉ㄦ剰锛�
+ - collection_name: 榛樿琛ㄥ悕锛屽鍒涘缓缁村害閿欒璇峰垹闄ゆ琛ㄦ垨鏇存敼鍚嶇О
+ - embedding_model_dims: 宓屽叆鍚戦噺缁村害锛岄渶涓巈mbedder鐨勬ā鍨嬬淮搴﹀尮閰�
+ 渚嬪鏅鸿氨锛歟mbedding-2缁村害鏄�1024锛宔mbedding-3缁村害鏄�2048
+
+銆愭帹鑽愰厤缃粍鍚堛��
+1. 瀹屽叏鍏嶈垂鏂规锛�
+ - LLM: zhipu + glm-4-flash (鍏嶈垂)
+ - Embedder: 閫氫箟鍗冮棶 text-embedding-v4
+ - Database: sqlite
+
+2. 鐢熶骇鐜鏂规锛�
+ - LLM: qwen-plus 鎴栧叾浠栧晢涓氭ā鍨�
+ - Embedder: text-embedding-v4
+ - Database: oceanbase 鎴� seekdb
+'
+WHERE `id` = 'Memory_powermem';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602051125.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602051125.sql
new file mode 100644
index 0000000..dfda249
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602051125.sql
@@ -0,0 +1,2 @@
+-- 鍒犻櫎鏃犵敤涓氬姟琛╝i_voiceprint
+DROP TABLE IF EXISTS ai_voiceprint;
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602061650.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602061650.sql
new file mode 100644
index 0000000..b60f489
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602061650.sql
@@ -0,0 +1,48 @@
+-- 缁熶竴瑙勮寖ai_tts_voice璇█绫诲瀷鏁版嵁
+UPDATE ai_tts_voice
+SET languages = CASE
+ WHEN languages IN ('涓枃', '鏅�氳瘽','涓滃寳璇�','澶╂触璇�','涓枃-鍖椾含鍙i煶','涓枃-闈掑矝鍙i煶','涓枃-娌冲崡鍙i煶','涓枃-骞胯タ鍙i煶','杈藉畞','闄曡タ','涓枃-鍥涘窛鍙i煶','涓枃-鍙版咕鍙i煶','涓枃-闀挎矙鍙i煶') THEN '鏅�氳瘽'
+ WHEN languages IN ('涓枃鍙婁腑鑻辨枃娣峰悎', '涓枃銆佽嫳鏂�', '涓枃銆佺編寮忚嫳璇�','涓枃-鍖椾含鍙i煶銆佽嫳鏂�','涓枃(涓滃寳)鍙婁腑鑻辨枃娣峰悎') THEN '鏅�氳瘽銆佽嫳璇�'
+ WHEN languages IN ('鑻卞紡鑻辨枃', '鑻卞紡鑻辫', '缇庡紡鑻辫', '婢虫床鑻辫', '鑻辨枃') THEN '鑻辫'
+ WHEN languages = '鏃ヨ' THEN '鏃ヨ'
+ WHEN languages = '鏃ヨ銆佽タ璇�' THEN '鏃ヨ銆佽タ鐝墮璇�'
+ WHEN languages = '闊╄' THEN '闊╄'
+ WHEN languages IN ('绮よ', '涓枃-骞夸笢鍙i煶') THEN '绮よ'
+ WHEN languages = '涓枃(绮よ)鍙婁腑鑻辨枃娣峰悎' THEN '绮よ銆佽嫳璇�'
+ WHEN languages = '绮よ鍙婄菠鑻辨贩鍚�' THEN '绮よ銆佽嫳璇�'
+ ELSE languages
+END;
+
+-- 娣诲姞闊宠壊璇█銆侀煶閲忋�佽閫熴�侀煶璋冨瓧娈靛埌 ai_agent 琛�
+SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_agent' AND COLUMN_NAME = 'tts_language');
+SET @sql = IF(@col_exists = 0, 'ALTER TABLE `ai_agent` ADD COLUMN `tts_language` VARCHAR(50) NULL COMMENT ''闊宠壊璇█'' AFTER `tts_voice_id`', 'SELECT ''Column tts_language already exists'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_agent' AND COLUMN_NAME = 'tts_volume');
+SET @sql = IF(@col_exists = 0, 'ALTER TABLE `ai_agent` ADD COLUMN `tts_volume` INT NULL COMMENT ''TTS闊抽噺'' AFTER `tts_language`', 'SELECT ''Column tts_volume already exists'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_agent' AND COLUMN_NAME = 'tts_rate');
+SET @sql = IF(@col_exists = 0, 'ALTER TABLE `ai_agent` ADD COLUMN `tts_rate` INT NULL COMMENT ''TTS璇��'' AFTER `tts_volume`', 'SELECT ''Column tts_rate already exists'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_agent' AND COLUMN_NAME = 'tts_pitch');
+SET @sql = IF(@col_exists = 0, 'ALTER TABLE `ai_agent` ADD COLUMN `tts_pitch` INT NULL COMMENT ''TTS闊宠皟'' AFTER `tts_rate`', 'SELECT ''Column tts_pitch already exists'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- 娣诲姞闊宠壊璇█銆侀煶閲忋�佽閫熴�侀煶璋冨瓧娈靛埌 ai_agent_template 琛�
+SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_agent_template' AND COLUMN_NAME = 'tts_language');
+SET @sql = IF(@col_exists = 0, 'ALTER TABLE `ai_agent_template` ADD COLUMN `tts_language` VARCHAR(50) NULL COMMENT ''闊宠壊璇█'' AFTER `tts_voice_id`', 'SELECT ''Column tts_language already exists'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_agent_template' AND COLUMN_NAME = 'tts_volume');
+SET @sql = IF(@col_exists = 0, 'ALTER TABLE `ai_agent_template` ADD COLUMN `tts_volume` INT NULL COMMENT ''TTS闊抽噺'' AFTER `tts_language`', 'SELECT ''Column tts_volume already exists'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_agent_template' AND COLUMN_NAME = 'tts_rate');
+SET @sql = IF(@col_exists = 0, 'ALTER TABLE `ai_agent_template` ADD COLUMN `tts_rate` INT NULL COMMENT ''TTS璇��'' AFTER `tts_volume`', 'SELECT ''Column tts_rate already exists'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_agent_template' AND COLUMN_NAME = 'tts_pitch');
+SET @sql = IF(@col_exists = 0, 'ALTER TABLE `ai_agent_template` ADD COLUMN `tts_pitch` INT NULL COMMENT ''TTS闊宠皟'' AFTER `tts_rate`', 'SELECT ''Column tts_pitch already exists'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602271137.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602271137.sql
new file mode 100644
index 0000000..010acd8
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602271137.sql
@@ -0,0 +1,42 @@
+-- 鏇存柊鑵捐TTS渚涘簲鍣ㄩ厤缃紝澧炲姞speed銆乿olume鍜宖ormat鍙傛暟
+UPDATE `ai_model_provider`
+SET fields = '[{"key":"appid","label":"搴旂敤ID","type":"string"},{"key":"secret_id","label":"Secret ID","type":"string"},{"key":"secret_key","label":"Secret Key","type":"string"},{"key":"format","label":"闊抽鏍煎紡","type":"string"},{"key":"speed","label":"璇��","type":"number"},{"key":"volume","label":"闊抽噺","type":"number"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"},{"key":"voice","label":"闊宠壊ID","type":"string"},{"key":"region","label":"鍖哄煙","type":"string"}]'
+WHERE id = 'SYSTEM_TTS_TencentTTS';
+
+-- 鏇存柊鑵捐TTS妯″瀷閰嶇疆锛屽鍔爏peed鍜寁olume鍙傛暟锛岃ˉ鍏呭弬鏁拌鏄�
+UPDATE `ai_model_config` SET
+ `config_json` = JSON_SET(`config_json`, '$.speed', 0, '$.volume', 0),
+ `remark` = '鑵捐TTS閰嶇疆璇存槑锛�
+1. 闇�瑕佸湪鑵捐浜戝钩鍙板紑閫氭櫤鑳借闊充氦浜掓湇鍔�
+2. 鏀寔澶氱闊宠壊锛屽綋鍓嶉厤缃娇鐢�101001
+3. 闇�瑕佺綉缁滆繛鎺�
+4. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍
+鐢宠姝ラ锛�
+1. 璁块棶 https://console.cloud.tencent.com/cam/capi 鑾峰彇瀵嗛挜
+2. 璁块棶 https://console.cloud.tencent.com/tts/resourcebundle 棰嗗彇鍏嶈垂璧勬簮
+3. 鍒涘缓鏂板簲鐢�
+4. 鑾峰彇appid銆乻ecret_id鍜宻ecret_key
+5. 濉叆閰嶇疆鏂囦欢涓�
+闊抽鍙傛暟锛�
+- format: 闊抽鏍煎紡锛屾敮鎸乸cm銆亀av銆乵p3
+- speed: 璇�燂紝鑼冨洿-2~6锛岄粯璁�0
+- volume: 闊抽噺锛岃寖鍥�-10~10锛岄粯璁�0'
+WHERE `id` = 'TTS_TencentTTS';
+
+-- 鏇存柊CozeCnTTS渚涘簲鍣ㄩ厤缃紝澧炲姞speed鍜宭oudness_rate鍙傛暟
+UPDATE `ai_model_provider`
+SET fields = '[{"key":"voice","label":"闊宠壊","type":"string"},{"key":"access_token","label":"璁块棶浠ょ墝","type":"string"},{"key":"speed","label":"璇��","type":"number"},{"key":"loudness_rate","label":"闊抽噺澧炵泭","type":"number"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"},{"key":"response_format","label":"鍝嶅簲鏍煎紡","type":"string"}]'
+WHERE id = 'SYSTEM_TTS_cozecn';
+
+-- 鏇存柊CozeCnTTS妯″瀷閰嶇疆锛屽鍔爏peed鍜宭oudness_rate鍙傛暟锛岃ˉ鍏呭弬鏁拌鏄�
+UPDATE `ai_model_config` SET
+ `config_json` = JSON_SET(`config_json`, '$.speed', 1, '$.loudness_rate', 0),
+ `remark` = 'Coze涓枃璇煶鍚堟垚閰嶇疆璇存槑锛�
+1. 璁块棶 https://www.coze.cn/ 娉ㄥ唽骞剁櫥褰�
+2. 鍒涘缓搴旂敤骞惰幏鍙朼ccess_token
+3. 閫夋嫨鍚堥�傜殑闊宠壊ID
+闊抽鍙傛暟锛�
+- response_format: 闊抽鏍煎紡锛屾敮鎸乸cm銆亀av銆乵p3
+- speed: 璇�燂紝鑼冨洿0.5~2锛岄粯璁�1
+- loudness_rate: 闊抽噺澧炵泭锛岃寖鍥�-50~100锛岄粯璁�0'
+WHERE `id` = 'TTS_CozeCnTTS';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602281000.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602281000.sql
new file mode 100644
index 0000000..5efa969
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202602281000.sql
@@ -0,0 +1,29 @@
+-- 鏍囩琛�
+CREATE TABLE IF NOT EXISTS ai_agent_tag (
+ id VARCHAR(32) NOT NULL COMMENT '涓婚敭',
+ tag_name VARCHAR(64) NOT NULL COMMENT '鏍囩鍚嶇О',
+ sort INT UNSIGNED DEFAULT 0 COMMENT '鎺掑簭',
+ creator BIGINT COMMENT '鍒涘缓鑰�',
+ created_at DATETIME COMMENT '鍒涘缓鏃堕棿',
+ updater BIGINT COMMENT '鏇存柊鑰�',
+ updated_at DATETIME COMMENT '鏇存柊鏃堕棿',
+ deleted TINYINT DEFAULT 0 COMMENT '鍒犻櫎鏍囪',
+ PRIMARY KEY (id),
+ UNIQUE KEY uk_tag_name (tag_name),
+ INDEX idx_sort (sort)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鏅鸿兘浣撴爣绛捐〃';
+
+-- 鏅鸿兘浣撴爣绛惧叧鑱旇〃
+CREATE TABLE IF NOT EXISTS ai_agent_tag_relation (
+ id VARCHAR(32) NOT NULL COMMENT '涓婚敭',
+ agent_id VARCHAR(32) NOT NULL COMMENT '鏅鸿兘浣揑D',
+ tag_id VARCHAR(32) NOT NULL COMMENT '鏍囩ID',
+ creator BIGINT COMMENT '鍒涘缓鑰�',
+ created_at DATETIME COMMENT '鍒涘缓鏃堕棿',
+ updater BIGINT COMMENT '鏇存柊鑰�',
+ updated_at DATETIME COMMENT '鏇存柊鏃堕棿',
+ PRIMARY KEY (id),
+ UNIQUE KEY uk_agent_tag (agent_id, tag_id),
+ INDEX idx_agent_id (agent_id),
+ INDEX idx_tag_id (tag_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鏅鸿兘浣撴爣绛惧叧鑱旇〃';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202603091051.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202603091051.sql
new file mode 100644
index 0000000..2b00ad8
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202603091051.sql
@@ -0,0 +1,11 @@
+-- 缁欐櫤鑳戒綋鏍囩鍏宠仈琛ㄦ坊鍔犳帓搴忓瓧娈�
+SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_agent_tag_relation' AND COLUMN_NAME = 'sort');
+SET @sql = IF(@col_exists = 0, 'ALTER TABLE `ai_agent_tag_relation` ADD COLUMN `sort` INT UNSIGNED DEFAULT 0 COMMENT ''鎺掑簭''', 'SELECT ''Column sort already exists'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- 淇鐜版湁鏁版嵁锛堜粎褰� sort 瀛楁涓洪粯璁ゅ�� 0 鏃舵墽琛岋紝閬垮厤瑕嗙洊鐢ㄦ埛宸茶缃殑椤哄簭锛�
+SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_agent_tag_relation' AND COLUMN_NAME = 'sort');
+SET @sql = IF(@col_exists > 0 AND (SELECT COUNT(*) FROM ai_agent_tag_relation WHERE sort = 0) > 0,
+ 'UPDATE ai_agent_tag_relation r INNER JOIN (SELECT id, ROW_NUMBER() OVER (PARTITION BY agent_id ORDER BY created_at) AS row_num FROM ai_agent_tag_relation) t ON r.id = t.id SET r.sort = t.row_num',
+ 'SELECT ''No need to update or column does not exist'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202603101723.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202603101723.sql
new file mode 100644
index 0000000..6709865
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202603101723.sql
@@ -0,0 +1,4 @@
+-- 缁欏0闊冲厠闅嗚〃娣诲姞璇█瀛楁
+SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_voice_clone' AND COLUMN_NAME = 'languages');
+SET @sql = IF(@col_exists = 0, 'ALTER TABLE `ai_voice_clone` ADD COLUMN `languages` VARCHAR(50) DEFAULT NULL COMMENT ''璇█'' AFTER `voice_id`', 'SELECT ''Column languages already exists'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202603111131.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202603111131.sql
new file mode 100644
index 0000000..aecf2cc
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202603111131.sql
@@ -0,0 +1,69 @@
+-- 澧炲姞涓�涓媟agflow杩斿洖鐨勫弬鏁帮紙鍒涘缓/鏌ヨ鐭ヨ瘑搴撴椂杩斿洖锛�
+SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_rag_dataset' AND COLUMN_NAME = 'tenant_id');
+SET @sql = IF(@col_exists = 0, 'ALTER TABLE `ai_rag_dataset` ADD COLUMN `tenant_id` varchar(32) DEFAULT NULL COMMENT ''绉熸埛ID''', 'SELECT ''Column tenant_id already exists'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_rag_dataset' AND COLUMN_NAME = 'avatar');
+SET @sql = IF(@col_exists = 0, 'ALTER TABLE `ai_rag_dataset` ADD COLUMN `avatar` text DEFAULT NULL COMMENT ''鐭ヨ瘑搴撳ご鍍� (Base64)''', 'SELECT ''Column avatar already exists'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_rag_dataset' AND COLUMN_NAME = 'embedding_model');
+SET @sql = IF(@col_exists = 0, 'ALTER TABLE `ai_rag_dataset` ADD COLUMN `embedding_model` varchar(50) DEFAULT NULL COMMENT ''宓屽叆妯″瀷鍚嶇О''', 'SELECT ''Column embedding_model already exists'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_rag_dataset' AND COLUMN_NAME = 'permission');
+SET @sql = IF(@col_exists = 0, 'ALTER TABLE `ai_rag_dataset` ADD COLUMN `permission` varchar(20) DEFAULT ''me'' COMMENT ''鏉冮檺璁剧疆锛歮e/team''', 'SELECT ''Column permission already exists'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_rag_dataset' AND COLUMN_NAME = 'chunk_method');
+SET @sql = IF(@col_exists = 0, 'ALTER TABLE `ai_rag_dataset` ADD COLUMN `chunk_method` varchar(50) DEFAULT NULL COMMENT ''鍒嗗潡鏂规硶''', 'SELECT ''Column chunk_method already exists'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_rag_dataset' AND COLUMN_NAME = 'parser_config');
+SET @sql = IF(@col_exists = 0, 'ALTER TABLE `ai_rag_dataset` ADD COLUMN `parser_config` text DEFAULT NULL COMMENT ''瑙f瀽鍣ㄩ厤缃� (JSON)''', 'SELECT ''Column parser_config already exists'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_rag_dataset' AND COLUMN_NAME = 'chunk_count');
+SET @sql = IF(@col_exists = 0, 'ALTER TABLE `ai_rag_dataset` ADD COLUMN `chunk_count` bigint(20) DEFAULT 0 COMMENT ''鍒嗗潡鎬绘暟''', 'SELECT ''Column chunk_count already exists'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_rag_dataset' AND COLUMN_NAME = 'document_count');
+SET @sql = IF(@col_exists = 0, 'ALTER TABLE `ai_rag_dataset` ADD COLUMN `document_count` bigint(20) DEFAULT 0 COMMENT ''鏂囨。鎬绘暟''', 'SELECT ''Column document_count already exists'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_rag_dataset' AND COLUMN_NAME = 'token_num');
+SET @sql = IF(@col_exists = 0, 'ALTER TABLE `ai_rag_dataset` ADD COLUMN `token_num` bigint(20) DEFAULT 0 COMMENT ''鎬� Token 鏁�''', 'SELECT ''Column token_num already exists'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- 鏂囨。琛� (Shadow DB for RAGFlow)
+-- 鐣欏瓨涓�浠芥枃妗d锛屾妸ragflow杩滅鏂囨。id鍜屾湰鍦癷d鍏宠仈璧锋潵锛堝彧鏄浠戒竴浠藉厓淇℃伅閾炬帴锛屽疄闄呬笂鏂囦欢鍐呭鐨勫瓨鍌ㄨ繕鏄湪ragflow锛�
+DROP TABLE IF EXISTS `ai_rag_knowledge_document`;
+CREATE TABLE `ai_rag_knowledge_document` (
+ `id` varchar(36) NOT NULL COMMENT '鏈湴鍞竴ID',
+ `dataset_id` varchar(36) NOT NULL COMMENT '鐭ヨ瘑搴揑D (鍏宠仈 ai_rag_dataset)',
+ `document_id` varchar(64) NOT NULL COMMENT 'RAGFlow鏂囨。ID (杩滅▼ID)',
+ `name` varchar(255) DEFAULT NULL COMMENT '鏂囨。鍚嶇О',
+ `size` bigint(20) DEFAULT NULL COMMENT '鏂囦欢澶у皬(Bytes)',
+ `type` varchar(20) DEFAULT NULL COMMENT '鏂囦欢绫诲瀷',
+ `chunk_method` varchar(50) DEFAULT NULL COMMENT '鍒嗗潡鏂规硶',
+ `parser_config` text COMMENT '瑙f瀽閰嶇疆(JSON)',
+ `status` varchar(10) DEFAULT '1' COMMENT '鍙敤鐘舵�� (1:鍚敤 0:绂佺敤)',
+ `run` varchar(32) DEFAULT 'UNSTART' COMMENT '杩愯鐘舵�� (UNSTART/RUNNING/CANCEL/DONE/FAIL)',
+ `progress` double DEFAULT '0' COMMENT '瑙f瀽杩涘害 (0.0 ~ 1.0)',
+ `thumbnail` mediumtext COMMENT '缂╃暐鍥� (Base64 鎴� URL)',
+ `process_duration` double DEFAULT '0' COMMENT '瑙f瀽鑰楁椂 (鍗曚綅: 绉�)',
+ `meta_fields` text COMMENT '鑷畾涔夊厓鏁版嵁 (JSON)',
+ `source_type` varchar(32) DEFAULT 'local' COMMENT '鏉ユ簮绫诲瀷 (local, s3, url 绛�)',
+ `error` text COMMENT '閿欒淇℃伅',
+ `chunk_count` int(11) DEFAULT '0' COMMENT '鍒嗗潡鏁伴噺',
+ `token_count` bigint(20) DEFAULT '0' COMMENT 'Token鏁伴噺',
+ `enabled` tinyint(1) DEFAULT '1' COMMENT '鍚敤鐘舵��',
+ `creator` bigint(20) DEFAULT NULL COMMENT '鍒涘缓鑰�',
+ `created_at` datetime DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+ `updated_at` datetime DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+ `last_sync_at` datetime DEFAULT NULL COMMENT '鏈�鍚庡悓姝ユ椂闂�',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_doc_id` (`document_id`),
+ KEY `idx_dataset_id` (`dataset_id`),
+ KEY `idx_status` (`status`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鐭ヨ瘑搴撴枃妗h〃';
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202603231037.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202603231037.sql
new file mode 100644
index 0000000..5ae1ce2
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202603231037.sql
@@ -0,0 +1,7 @@
+-- 鏂板浠呬笂鎶ヨ亰澶╄褰曡蹇嗘ā鍨嬩緵搴斿櫒
+
+delete from `ai_model_provider` where `id` = 'SYSTEM_Memory_mem_report_only';
+delete from `ai_model_config` where `id` = 'Memory_mem_report_only';
+
+INSERT INTO `ai_model_provider` VALUES ('SYSTEM_Memory_mem_report_only', 'Memory', 'mem_report_only', '浠呬笂鎶ヨ亰澶╄褰�', '[]', 4, 1, NOW(), 1, NOW());
+INSERT INTO `ai_model_config` VALUES ('Memory_mem_report_only', 'Memory', 'mem_report_only', '浠呬笂鎶ヨ亰澶╄褰�', 0, 1, '{"type": "mem_report_only"}', NULL, '浠呬笂鎶ヨ亰澶╄褰曪紝涓嶆�荤粨璁板繂', 3, NULL, NULL, NULL, NULL);
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202603311200.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202603311200.sql
new file mode 100644
index 0000000..0e34d0a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202603311200.sql
@@ -0,0 +1,2 @@
+-- 淇敼鑱婂ぉ鍐呭瀛楁绫诲瀷
+ALTER TABLE ai_agent_chat_history MODIFY COLUMN content TEXT COMMENT '鑱婂ぉ鍐呭';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604010930.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604010930.sql
new file mode 100644
index 0000000..ce9d7c7
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604010930.sql
@@ -0,0 +1,7 @@
+-- 淇敼璁板繂妯″瀷鍚嶇О
+
+UPDATE `ai_model_config` SET `model_name` = '鏈湴鐭湡璁板繂锛堟�荤粨璁板繂锛�' WHERE `id` = 'Memory_mem_local_short';
+UPDATE `ai_model_provider` SET `name` = '鏈湴鐭湡璁板繂锛堟�荤粨璁板繂锛�' WHERE `id` = 'SYSTEM_Memory_mem_local_short';
+
+UPDATE `ai_model_config` SET `model_name` = '浠呬笂鎶ヨ亰澶╄褰曪紙涓嶆�荤粨璁板繂锛�' WHERE `id` = 'Memory_mem_report_only';
+UPDATE `ai_model_provider` SET `name` = '浠呬笂鎶ヨ亰澶╄褰曪紙涓嶆�荤粨璁板繂锛�' WHERE `id` = 'SYSTEM_Memory_mem_report_only';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604011035.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604011035.sql
new file mode 100644
index 0000000..7bf3f6b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604011035.sql
@@ -0,0 +1 @@
+INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (312, 'tool_call_timeout', '30', 'number', 1, '宸ュ叿璋冪敤瓒呮椂鏃堕棿(绉�)');
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604011545.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604011545.sql
new file mode 100644
index 0000000..1ef5be6
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604011545.sql
@@ -0,0 +1,16 @@
+-- 鏅鸿兘浣撹〃娣诲姞灏忔ā鍨婭D瀛楁
+SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_agent' AND COLUMN_NAME = 'slm_model_id');
+SET @sql = IF(@col_exists = 0, 'ALTER TABLE `ai_agent` ADD COLUMN `slm_model_id` VARCHAR(255) NULL COMMENT ''灏忔ā鍨婭D'' AFTER `llm_model_id`', 'SELECT ''Column slm_model_id already exists'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- 鍒涘缓鑱婂ぉ鏍囬琛�
+DROP TABLE IF EXISTS `ai_agent_chat_title`;
+CREATE TABLE `ai_agent_chat_title` (
+ `id` VARCHAR(32) NOT NULL COMMENT '涓婚敭ID',
+ `session_id` VARCHAR(255) NOT NULL COMMENT '浼氳瘽ID',
+ `title` VARCHAR(255) DEFAULT NULL COMMENT '鑱婂ぉ鏍囬',
+ `created_at` DATETIME DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+ `updated_at` DATETIME DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+ PRIMARY KEY (`id`),
+ KEY `idx_session_id` (`session_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鏅鸿兘浣撹亰澶╂爣棰樿〃';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604161357.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604161357.sql
new file mode 100644
index 0000000..f09bd82
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604161357.sql
@@ -0,0 +1,17 @@
+-- 鏇存柊妯″瀷鍚嶇О锛歲wen2.5-vl-3b-instruct 鏀逛负 qwen3.5-flash
+UPDATE `ai_model_config`
+SET `config_json` = JSON_SET(`config_json`, '$.model_name', 'qwen3.5-flash')
+WHERE `id` = 'VLLM_QwenVLVLLM'
+AND JSON_EXTRACT(`config_json`, '$.model_name') = 'qwen2.5-vl-3b-instruct';
+
+-- 鏇存柊妯″瀷鍚嶇О锛歲wen-turbo 鏀逛负 qwen-flash
+UPDATE `ai_model_config`
+SET `config_json` = JSON_SET(`config_json`, '$.model_name', 'qwen-flash')
+WHERE `id` = 'LLM_AliLLM'
+AND JSON_EXTRACT(`config_json`, '$.model_name') = 'qwen-turbo';
+
+-- 鏇存柊澶囨敞锛歲wen-turbo 鏀逛负 qwen-flash
+UPDATE `ai_model_config`
+SET `remark` = REPLACE(`remark`, 'qwen-turbo', 'qwen-flash')
+WHERE `id` = 'LLM_AliLLM'
+AND `remark` LIKE '%qwen-turbo%';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604201719.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604201719.sql
new file mode 100644
index 0000000..d1d8037
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604201719.sql
@@ -0,0 +1,102 @@
+-- 鏂板璞嗗寘璇煶鍚堟垚妯″瀷2.0渚涘簲鍣紙浣跨敤seed-tts-2.0璧勬簮ID锛�
+-- 涓庣伀灞卞弻娴佸紡TTS閰嶇疆鐩稿悓锛屼絾resource_id鍥哄畾涓簊eed-tts-2.0
+
+-- 鎻掑叆璞嗗寘璇煶鍚堟垚妯″瀷2.0渚涘簲鍣�
+delete from `ai_model_provider` where id = 'SYSTEM_TTS_HSDSTTS_V2';
+INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
+('SYSTEM_TTS_HSDSTTS_V2', 'TTS', 'huoshan_double_stream', '璞嗗寘璇煶鍚堟垚妯″瀷2.0', '[
+ {"key": "ws_url", "type": "string", "label": "WebSocket鍦板潃"},
+ {"key": "appid", "type": "string", "label": "搴旂敤ID"},
+ {"key": "access_token", "type": "string", "label": "璁块棶浠ょ墝"},
+ {"key": "resource_id", "type": "string", "label": "璧勬簮ID"},
+ {"key": "speaker", "type": "string", "label": "榛樿闊宠壊"},
+ {"key": "enable_ws_reuse", "type": "boolean", "label": "鏄惁寮�鍚摼鎺ュ鐢�", "default": true},
+ {"key": "audio_params", "type": "dict", "label": "闊抽杈撳嚭閰嶇疆"},
+ {"key": "additions", "type": "dict", "label": "楂樼骇鏂囨湰澶勭悊閰嶇疆"},
+ {"key": "mix_speaker", "type": "dict", "label": "娣烽煶鎺у埗閰嶇疆"}
+]', 14, 1, NOW(), 1, NOW());
+
+-- 鎻掑叆璞嗗寘璇煶鍚堟垚妯″瀷2.0閰嶇疆
+delete from `ai_model_config` where id = 'TTS_HSDSTTS_V2';
+INSERT INTO `ai_model_config` VALUES ('TTS_HSDSTTS_V2', 'TTS', 'HuoshanDoubleStreamTTSV2', '璞嗗寘璇煶鍚堟垚妯″瀷2.0', 0, 1, '{
+ "type": "huoshan_double_stream",
+ "ws_url": "wss://openspeech.bytedance.com/api/v3/tts/bidirection",
+ "appid": "",
+ "access_token": "",
+ "resource_id": "seed-tts-2.0",
+ "speaker": "zh_female_xiaohe_uranus_bigtts",
+ "enable_ws_reuse": true,
+ "audio_params": {
+ "speech_rate": 0,
+ "loudness_rate": 0
+ },
+ "additions": {
+ "aigc_metadata": {},
+ "cache_config": {},
+ "post_process": {
+ "pitch": 0
+ }
+ },
+ "mix_speaker": {}
+}', NULL, NULL, 17, NULL, NULL, NULL, NULL);
+
+-- 璞嗗寘璇煶鍚堟垚妯″瀷2.0閰嶇疆璇存槑鏂囨。
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://www.volcengine.com/docs/6561/1329505',
+`remark` = '璞嗗寘璇煶鍚堟垚妯″瀷2.0閰嶇疆璇存槑锛堝熀浜庣伀灞卞紩鎿巗eed-tts-2.0锛夛細
+1. 璁块棶 https://www.volcengine.com/ 娉ㄥ唽骞跺紑閫氱伀灞卞紩鎿庤处鍙�
+2. 璁块棶 https://console.volcengine.com/speech/service/10035 寮�閫氳闊冲悎鎴愬ぇ妯″瀷锛岃喘涔伴煶鑹�
+3. 鍦ㄩ〉闈㈠簳閮ㄨ幏鍙朼ppid鍜宎ccess_token
+4. 璧勬簮ID鍥哄畾涓猴細seed-tts-2.0锛堣眴鍖呰闊冲悎鎴愭ā鍨�2.0锛�
+5. 閾炬帴澶嶇敤锛氬紑鍚疻ebSocket杩炴帴澶嶇敤锛岄粯璁rue鍑忓皯閾炬帴鎹熻�楋紙娉ㄦ剰锛氬鐢ㄥ悗璁惧澶勪簬鑱嗗惉鐘舵�佹椂绌洪棽閾炬帴浼氬崰骞跺彂鏁帮級
+
+璇︾粏鍙傛暟鏂囨。锛歨ttps://www.volcengine.com/docs/6561/1329505
+銆恆udio_params銆戦煶棰戣緭鍑洪厤缃� - 鐢ㄦ埛鍙嚜瀹氫箟娣诲姞鐏北寮曟搸鏀寔鐨勪换浣曢煶棰戝弬鏁�
+ - speech_rate: 璇��(-50~100)锛岄粯璁�0
+ - loudness_rate: 闊抽噺(-50~100)锛岄粯璁�0
+ 绀轰緥锛歿"speech_rate": 10, "loudness_rate": 5}
+
+銆恆dditions銆戦珮绾ф枃鏈鐞嗛厤缃� - 鐢ㄦ埛鍙嚜瀹氫箟娣诲姞鐏北寮曟搸鏀寔鐨勪换浣曢珮绾у弬鏁�
+ - post_process.pitch: 闊抽珮(-12~12)锛岄粯璁�0
+ - aigc_metadata: AIGC鍏冩暟鎹厤缃�
+ - cache_config: 缂撳瓨閰嶇疆
+ 绀轰緥锛歿"post_process": {"pitch": 2}, "aigc_metadata": {}, "cache_config": {}}
+
+娉ㄦ剰锛�
+- 璞嗗寘璇煶鍚堟垚妯″瀷2.0浣跨敤seed-tts-2.0璧勬簮ID锛屼笌鐏北鍙屾祦寮廡TS锛坴olc.service_type.10029锛変笉鍚�
+- 鐩稿叧闊宠壊鍒楄〃锛歨ttps://www.volcengine.com/docs/6561/1257544
+- 鐢ㄦ埛鍙牴鎹伀灞卞紩鎿嶢PI鏂囨。鑷娣诲姞鏇村鍙傛暟
+' WHERE `id` = 'TTS_HSDSTTS_V2';
+
+-- 娣诲姞璞嗗寘璇煶鍚堟垚妯″瀷2.0闊宠壊锛堜笌鐏北鍙屾祦寮廡TS闊宠壊鐩稿悓锛�
+delete from `ai_tts_voice` where tts_model_id = 'TTS_HSDSTTS_V2';
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0001', 'TTS_HSDSTTS_V2', 'Vivi', 'zh_female_vv_uranus_bigtts', '鏅�氳瘽銆佹棩璇�佸嵃灏艰銆佸ⅷ瑗垮摜瑗跨彮鐗欒', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_vv_uranus_bigtts.wav', NULL, NULL, NULL, 1, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0002', 'TTS_HSDSTTS_V2', '灏忎綍', 'zh_female_xiaohe_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_xiaohe_uranus_bigtts.mp3', NULL, NULL, NULL, 2, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0003', 'TTS_HSDSTTS_V2', '浜戣垷', 'zh_male_m191_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_m191_uranus_bigtts.mp3', NULL, NULL, NULL, 3, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0004', 'TTS_HSDSTTS_V2', '灏忓ぉ', 'zh_male_taocheng_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_taocheng_uranus_bigtts.mp3', NULL, NULL, NULL, 4, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0005', 'TTS_HSDSTTS_V2', '鍒橀', 'zh_male_liufei_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_liufei_uranus_bigtts.mp3', NULL, NULL, NULL, 5, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0006', 'TTS_HSDSTTS_V2', '榄呭姏鑻忚彶', 'zh_female_sophie_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_sophie_uranus_bigtts.mp3', NULL, NULL, NULL, 6, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0007', 'TTS_HSDSTTS_V2', '娓呮柊濂冲0', 'zh_female_qingxinnvsheng_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_qingxinnvsheng_uranus_bigtts.mp3', NULL, NULL, NULL, 7, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0008', 'TTS_HSDSTTS_V2', '鐭ユ�х伩鐏�', 'zh_female_cancan_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_cancan_uranus_bigtts.mp3', NULL, NULL, NULL, 8, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0009', 'TTS_HSDSTTS_V2', '鎾掑▏瀛﹀', 'zh_female_sajiaoxuemei_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_sajiaoxuemei_uranus_bigtts.mp3', NULL, NULL, NULL, 9, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0010', 'TTS_HSDSTTS_V2', '鐢滅編灏忔簮', 'zh_female_tianmeixiaoyuan_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_tianmeixiaoyuan_uranus_bigtts.mp3', NULL, NULL, NULL, 10, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0011', 'TTS_HSDSTTS_V2', '鐢滅編妗冨瓙', 'zh_female_tianmeitaozi_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_tianmeitaozi_uranus_bigtts.mp3', NULL, NULL, NULL, 11, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0012', 'TTS_HSDSTTS_V2', '鐖藉揩鎬濇��', 'zh_female_shuangkuaisisi_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_shuangkuaisisi_uranus_bigtts.mp3', NULL, NULL, NULL, 12, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0013', 'TTS_HSDSTTS_V2', '浣╁鐚�', 'zh_female_peiqi_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_peiqi_uranus_bigtts.mp3', NULL, NULL, NULL, 13, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0014', 'TTS_HSDSTTS_V2', '閭诲濂冲', 'zh_female_linjianvhai_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_linjianvhai_uranus_bigtts.mp3', NULL, NULL, NULL, 14, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0015', 'TTS_HSDSTTS_V2', '灏戝勾姊撹緵/Brayan', 'zh_male_shaonianzixin_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_shaonianzixin_uranus_bigtts.mp3', NULL, NULL, NULL, 15, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0016', 'TTS_HSDSTTS_V2', '鐚村摜', 'zh_male_sunwukong_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_sunwukong_uranus_bigtts.mp3', NULL, NULL, NULL, 16, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0017', 'TTS_HSDSTTS_V2', '榄呭姏濂冲弸', 'zh_female_meilinvyou_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_meilinvyou_uranus_bigtts.mp3', NULL, NULL, NULL, 17, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0018', 'TTS_HSDSTTS_V2', 'Tim', 'en_male_tim_uranus_bigtts', '鑻辫', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/en_male_tim_uranus_bigtts.mp3', NULL, NULL, NULL, 18, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0019', 'TTS_HSDSTTS_V2', 'Dacey', 'en_female_dacey_uranus_bigtts', '鑻辫', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/en_female_dacey_uranus_bigtts.mp3', NULL, NULL, NULL, 19, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0020', 'TTS_HSDSTTS_V2', 'Stokie', 'en_female_stokie_uranus_bigtts', '鑻辫', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/en_female_stokie_uranus_bigtts.mp3', NULL, NULL, NULL, 20, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0021', 'TTS_HSDSTTS_V2', '娓╂殩闃胯檸/Alvin', 'zh_male_wennuanahu_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_wennuanahu_uranus_bigtts.mp3', NULL, NULL, NULL, 21, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0022', 'TTS_HSDSTTS_V2', '濂舵皵钀屽▋', 'zh_male_naiqimengwa_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_naiqimengwa_uranus_bigtts.mp3', NULL, NULL, NULL, 22, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0023', 'TTS_HSDSTTS_V2', '濠嗗﹩', 'zh_female_popo_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_popo_uranus_bigtts.mp3', NULL, NULL, NULL, 23, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0024', 'TTS_HSDSTTS_V2', '寮�鏈楀濮�', 'zh_female_kailangjiejie_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_kailangjiejie_uranus_bigtts.mp3', NULL, NULL, NULL, 24, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0025', 'TTS_HSDSTTS_V2', '杞荤泩鏈垫湹', 'saturn_zh_female_qingyingduoduo_cs_tob', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/ICL_zh_female_qingyingduoduo_cs_tob.mp3', NULL, NULL, NULL, 25, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0026', 'TTS_HSDSTTS_V2', '娓╁鐝婄強', 'saturn_zh_female_wenwanshanshan_cs_tob', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/ICL_zh_female_wenwanshanshan_cs_tob.mp3', NULL, NULL, NULL, 26, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0027', 'TTS_HSDSTTS_V2', '闇告皵闈掑彅', 'zh_male_baqiqingshu_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_baqiqingshu_uranus_bigtts.mp3', NULL, NULL, NULL, 27, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0028', 'TTS_HSDSTTS_V2', '鎮枒瑙h', 'zh_male_xuanyijieshuo_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_xuanyijieshuo_uranus_bigtts.mp3', NULL, NULL, NULL, 28, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0029', 'TTS_HSDSTTS_V2', '鍙ら灏戝尽', 'zh_female_gufengshaoyu_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_gufengshaoyu_uranus_bigtts.mp3', NULL, NULL, NULL, 29, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0030', 'TTS_HSDSTTS_V2', '鍞愬儳', 'zh_male_tangseng_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_tangseng_uranus_bigtts.mp3', NULL, NULL, NULL, 30, NULL, NULL, NULL, NULL);
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604211515.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604211515.sql
new file mode 100644
index 0000000..39c4f96
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604211515.sql
@@ -0,0 +1,38 @@
+-- 鏅鸿兘浣撴浛鎹㈣瘝鏂囦欢琛�
+CREATE TABLE IF NOT EXISTS `ai_agent_correct_word_file` (
+ `id` VARCHAR(32) NOT NULL,
+ `file_name` VARCHAR(256) NOT NULL COMMENT '鍘熷鏂囦欢鍚�',
+ `word_count` INT NOT NULL DEFAULT 0 COMMENT '鏇挎崲璇嶆暟閲�',
+ `content` TEXT COMMENT '鏂囦欢鍘熷鍐呭',
+ `creator` BIGINT DEFAULT NULL,
+ `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
+ `updater` BIGINT DEFAULT NULL,
+ `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`),
+ INDEX `idx_creator` (`creator`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鏇挎崲璇嶆枃浠�';
+
+-- 鏇挎崲璇嶈瘝鏉¤〃
+CREATE TABLE IF NOT EXISTS `ai_agent_correct_word_item` (
+ `id` VARCHAR(32) NOT NULL,
+ `file_id` VARCHAR(32) NOT NULL COMMENT '鎵�灞炴枃浠禝D',
+ `source_word` VARCHAR(128) NOT NULL COMMENT '鍘熻瘝',
+ `target_word` VARCHAR(128) NOT NULL COMMENT '鏇挎崲璇�',
+ PRIMARY KEY (`id`),
+ INDEX `idx_file_id` (`file_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鏇挎崲璇嶈瘝鏉�';
+
+-- 鏅鸿兘浣撴浛鎹㈣瘝鏂囦欢鍏宠仈琛�
+CREATE TABLE IF NOT EXISTS `ai_agent_correct_word_mapping` (
+ `id` VARCHAR(32) NOT NULL,
+ `agent_id` VARCHAR(32) NOT NULL,
+ `file_id` VARCHAR(32) NOT NULL,
+ `creator` BIGINT DEFAULT NULL,
+ `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
+ `updater` BIGINT DEFAULT NULL,
+ `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`),
+ UNIQUE INDEX `uk_agent_file` (`agent_id`, `file_id`),
+ INDEX `idx_agent_id` (`agent_id`),
+ INDEX `idx_file_id` (`file_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鏅鸿兘浣撴浛鎹㈣瘝鏂囦欢鍏宠仈';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604211700.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604211700.sql
new file mode 100644
index 0000000..1f5f0d3
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604211700.sql
@@ -0,0 +1,45 @@
+-- 淇璞嗗寘璇煶鍚堟垚妯″瀷2.0 provider_code 閲嶅闂锛屾坊鍔� ASR 2.0 鏀寔
+
+-- ==================== 璞嗗寘璇煶鍚堟垚妯″瀷2.0 ====================
+-- 鍒犻櫎 TTS 2.0 渚涘簲鍣紙涓嶅啀闇�瑕佸崟鐙殑渚涘簲鍣級
+delete from `ai_model_provider` where id = 'SYSTEM_TTS_HSDSTTS_V2';
+
+-- ==================== 璞嗗寘璇煶璇嗗埆(娴佸紡) ====================
+-- 淇鍘熸湁璞嗗寘璇煶璇嗗埆(娴佸紡)渚涘簲鍣紝绉婚櫎cluster瀛楁锛屾坊鍔爎esource_id瀛楁
+UPDATE `ai_model_provider` SET `fields` = '[{"key":"appid","type":"string","label":"搴旂敤ID"},{"key":"access_token","type":"string","label":"璁块棶浠ょ墝"},{"key":"boosting_table_name","type":"string","label":"鐑瘝鏂囦欢鍚嶇О"},{"key":"correct_table_name","type":"string","label":"鏇挎崲璇嶆枃浠跺悕绉�"},{"key":"output_dir","type":"string","label":"杈撳嚭鐩綍"},{"key":"end_window_size","type":"number","label":"闈欓煶鍒ゅ畾鏃堕暱(ms)"},{"key":"enable_multilingual","type":"boolean","label":"鏄惁寮�鍚璇璇嗗埆妯″紡"},{"key":"language","type":"string","label":"鎸囧畾璇█缂栫爜"},{"key":"resource_id","type":"string","label":"璧勬簮ID"}]' WHERE `id` = 'SYSTEM_ASR_DoubaoStreamASR';
+
+-- 淇鍘熸湁璞嗗寘璇煶璇嗗埆(娴佸紡)閰嶇疆锛岀Щ闄luster瀛楁锛屾坊鍔爎esource_id榛樿鍊�
+UPDATE `ai_model_config` SET `config_json` = JSON_REMOVE(JSON_SET(`config_json`, '$.resource_id', 'volc.bigasr.sauc.duration'), '$.cluster') WHERE `id` = 'ASR_DoubaoStreamASR';
+
+-- ==================== 璞嗗寘璇煶璇嗗埆妯″瀷2.0 ====================
+
+-- 鎻掑叆璞嗗寘璇煶璇嗗埆妯″瀷2.0閰嶇疆
+delete from `ai_model_config` where id = 'ASR_DoubaoStreamASRV2';
+INSERT INTO `ai_model_config` VALUES ('ASR_DoubaoStreamASRV2', 'ASR', 'DoubaoStreamASRV2', '璞嗗寘璇煶璇嗗埆妯″瀷2.0', 0, 1, '{
+ "type": "doubao_stream",
+ "appid": "",
+ "access_token": "",
+ "resource_id": "volc.seedasr.sauc.duration",
+ "end_window_size": 200,
+ "enable_multilingual": false,
+ "language": "zh-CN",
+ "output_dir": "tmp/"
+}', NULL, NULL, 6, NULL, NULL, NULL, NULL);
+
+-- 璞嗗寘璇煶璇嗗埆妯″瀷2.0閰嶇疆璇存槑鏂囨。
+UPDATE `ai_model_config` SET
+`doc_link` = 'https://www.volcengine.com/docs/6561/109979',
+`remark` = '璞嗗寘璇煶璇嗗埆妯″瀷2.0閰嶇疆璇存槑锛堝熀浜庣伀灞卞紩鎿巗eed-asr锛夛細
+1. 璁块棶 https://www.volcengine.com/ 娉ㄥ唽骞跺紑閫氱伀灞卞紩鎿庤处鍙�
+2. 璁块棶 https://console.volcengine.com/speech/service/10038 寮�閫氳眴鍖呮祦寮忚闊宠瘑鍒ā鍨�2.0
+3. 鍦ㄩ〉闈㈠簳閮ㄨ幏鍙朼ppid鍜宎ccess_token
+4. 璧勬簮ID鏈変袱绉嶏細灏忔椂鐗堬紙volc.seedasr.sauc.duration锛夊拰骞跺彂鐗堬紙volc.seedasr.sauc.concurrent锛�
+ - 灏忔椂鐗堬細鍥哄畾涓猴細volc.seedasr.sauc.duration锛堣眴鍖呰闊宠瘑鍒ā鍨�2.0锛�
+ - 骞跺彂鐗堬細鍥哄畾涓猴細volc.seedasr.sauc.concurrent锛堣眴鍖呰闊宠瘑鍒ā鍨�2.0锛�
+
+璇︾粏鍙傛暟鏂囨。锛歨ttps://www.volcengine.com/docs/6561/109979
+
+娉ㄦ剰锛�
+- 璞嗗寘璇煶璇嗗埆妯″瀷2.0浣跨敤volc.seedasr.sauc.duration璧勬簮ID锛屼笌璞嗗寘璇煶璇嗗埆(娴佸紡)锛坴olc.bigasr.sauc.duration锛変笉鍚�
+- 璇煶璇嗗埆妯″瀷2.0浠锋牸鏇翠负渚垮疁锛屽缓璁湪楂樺苟鍙戝満鏅笅浣跨敤骞跺彂鐗堣祫婧怚D
+' WHERE `id` = 'ASR_DoubaoStreamASRV2';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604300930.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604300930.sql
new file mode 100644
index 0000000..4d1c8ac
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202604300930.sql
@@ -0,0 +1,57 @@
+-- 淇敼妯″瀷鍚嶇О
+UPDATE `ai_model_config` SET `model_name` = '璞嗗寘璇煶璇嗗埆2.0(娴佸紡)' WHERE `id` = 'ASR_DoubaoStreamASRV2';
+
+UPDATE `ai_model_config` SET `model_name` = '璞嗗寘璇煶鍚堟垚2.0(娴佸紡)' WHERE `id` = 'TTS_HSDSTTS_V2';
+
+-- 璋冩暣妯″瀷闊宠壊
+delete from `ai_tts_voice` where tts_model_id = 'TTS_HSDSTTS_V2';
+
+-- 閫氱敤鍦烘櫙 and 瑙嗛閰嶉煶
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0001', 'TTS_HSDSTTS_V2', '婀炬咕灏忎綍', 'zh_female_xiaohe_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_xiaohe_uranus_bigtts.mp3', NULL, NULL, NULL, 1, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0002', 'TTS_HSDSTTS_V2', 'Vivi', 'zh_female_vv_uranus_bigtts', '鏅�氳瘽銆佹棩璇�佸嵃灏艰銆佸ⅷ瑗垮摜瑗跨彮鐗欒', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_vv_uranus_bigtts.wav', NULL, NULL, NULL, 2, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0003', 'TTS_HSDSTTS_V2', '鍙埍濂崇敓', 'saturn_zh_female_keainvsheng_tob', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/saturn_zh_female_keainvsheng_tob.wav', NULL, NULL, NULL, 3, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0004', 'TTS_HSDSTTS_V2', '璋冪毊鍏富', 'saturn_zh_female_tiaopigongzhu_tob', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/saturn_zh_female_tiaopigongzhu_tob.wav', NULL, NULL, NULL, 4, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0005', 'TTS_HSDSTTS_V2', '闃冲厜闈掑勾', 'zh_male_yangguangqingnian_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_yangguangqingnian_uranus_bigtts.mp3', NULL, NULL, NULL, 5, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0006', 'TTS_HSDSTTS_V2', '鐖芥湕灏戝勾', 'saturn_zh_male_shuanglangshaonian_tob', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/saturn_zh_male_shuanglangshaonian_tob.wav', NULL, NULL, NULL, 6, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0007', 'TTS_HSDSTTS_V2', '鐖芥湕灏戝勾', 'saturn_zh_male_tiancaitongzhuo_tob', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/saturn_zh_male_tiancaitongzhuo_tob.wav', NULL, NULL, NULL, 7, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0008', 'TTS_HSDSTTS_V2', '澶у9', 'zh_male_dayi_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_dayi_saturn_bigtts.wav', NULL, NULL, NULL, 8, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0009', 'TTS_HSDSTTS_V2', '榛戠尗渚︽帰绀惧挭浠�', 'zh_female_mizai_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_mizai_saturn_bigtts.wav', NULL, NULL, NULL, 9, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0010', 'TTS_HSDSTTS_V2', '楦℃堡濂�', 'zh_female_jitangnv_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_jitangnv_saturn_bigtts.wav', NULL, NULL, NULL, 10, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0011', 'TTS_HSDSTTS_V2', '榄呭姏濂冲弸', 'zh_female_meilinvyou_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_meilinvyou_saturn_bigtts.wav', NULL, NULL, NULL, 11, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0012', 'TTS_HSDSTTS_V2', '娴佺晠濂冲0', 'zh_female_liuchangnv_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E6%B5%81%E7%95%85%E5%A5%B3%E5%A3%B0.wav', NULL, NULL, NULL, 12, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0013', 'TTS_HSDSTTS_V2', '鍎掗泤閫歌景', 'zh_male_ruyayichen_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_ruyayichen_saturn_bigtts.wav', NULL, NULL, NULL, 13, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0014', 'TTS_HSDSTTS_V2', '浜戣垷', 'zh_male_m191_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_m191_uranus_bigtts.mp3', NULL, NULL, NULL, 14, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0015', 'TTS_HSDSTTS_V2', '灏忓ぉ', 'zh_male_taocheng_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_taocheng_uranus_bigtts.mp3', NULL, NULL, NULL, 15, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0016', 'TTS_HSDSTTS_V2', '鍒橀', 'zh_male_liufei_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_liufei_uranus_bigtts.mp3', NULL, NULL, NULL, 16, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0017', 'TTS_HSDSTTS_V2', '榄呭姏鑻忚彶', 'zh_female_sophie_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_sophie_uranus_bigtts.mp3', NULL, NULL, NULL, 17, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0018', 'TTS_HSDSTTS_V2', '娓呮柊濂冲0', 'zh_female_qingxinnvsheng_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_qingxinnvsheng_uranus_bigtts.mp3', NULL, NULL, NULL, 18, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0019', 'TTS_HSDSTTS_V2', '鐭ユ�х伩鐏�', 'zh_female_cancan_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_cancan_uranus_bigtts.mp3', NULL, NULL, NULL, 19, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0020', 'TTS_HSDSTTS_V2', '鎾掑▏瀛﹀', 'zh_female_sajiaoxuemei_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_sajiaoxuemei_uranus_bigtts.mp3', NULL, NULL, NULL, 20, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0021', 'TTS_HSDSTTS_V2', '鐢滅編灏忔簮', 'zh_female_tianmeixiaoyuan_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_tianmeixiaoyuan_uranus_bigtts.mp3', NULL, NULL, NULL, 21, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0022', 'TTS_HSDSTTS_V2', '鐢滅編妗冨瓙', 'zh_female_tianmeitaozi_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_tianmeitaozi_uranus_bigtts.mp3', NULL, NULL, NULL, 22, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0023', 'TTS_HSDSTTS_V2', '閭诲濂冲', 'zh_female_linjianvhai_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_linjianvhai_uranus_bigtts.mp3', NULL, NULL, NULL, 23, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0024', 'TTS_HSDSTTS_V2', '鐖藉揩鎬濇��', 'zh_female_shuangkuaisisi_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_shuangkuaisisi_uranus_bigtts.mp3', NULL, NULL, NULL, 24, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0025', 'TTS_HSDSTTS_V2', '灏戝勾姊撹緵/Brayan', 'zh_male_shaonianzixin_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_shaonianzixin_uranus_bigtts.mp3', NULL, NULL, NULL, 25, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0026', 'TTS_HSDSTTS_V2', '娓╂殩闃胯檸/Alvin', 'zh_male_wennuanahu_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_wennuanahu_uranus_bigtts.mp3', NULL, NULL, NULL, 26, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0027', 'TTS_HSDSTTS_V2', '濂舵皵钀屽▋', 'zh_male_naiqimengwa_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_naiqimengwa_uranus_bigtts.mp3', NULL, NULL, NULL, 27, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0028', 'TTS_HSDSTTS_V2', '濠嗗﹩', 'zh_female_popo_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_popo_uranus_bigtts.mp3', NULL, NULL, NULL, 28, NULL, NULL, NULL, NULL);
+
+-- 瀹㈡湇鍦烘櫙
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0029', 'TTS_HSDSTTS_V2', '杞荤泩鏈垫湹', 'saturn_zh_female_qingyingduoduo_cs_tob', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/ICL_zh_female_qingyingduoduo_cs_tob.mp3', NULL, NULL, NULL, 29, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0030', 'TTS_HSDSTTS_V2', '娓╁鐝婄強', 'saturn_zh_female_wenwanshanshan_cs_tob', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/ICL_zh_female_wenwanshanshan_cs_tob.mp3', NULL, NULL, NULL, 30, NULL, NULL, NULL, NULL);
+
+-- 澶氳绉�
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0031', 'TTS_HSDSTTS_V2', 'Tim', 'en_male_tim_uranus_bigtts', '鑻辫', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/en_male_tim_uranus_bigtts.mp3', NULL, NULL, NULL, 31, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0032', 'TTS_HSDSTTS_V2', 'Dacey', 'en_female_dacey_uranus_bigtts', '鑻辫', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/en_female_dacey_uranus_bigtts.mp3', NULL, NULL, NULL, 32, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0033', 'TTS_HSDSTTS_V2', 'Stokie', 'en_female_stokie_uranus_bigtts', '鑻辫', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/en_female_stokie_uranus_bigtts.mp3', NULL, NULL, NULL, 33, NULL, NULL, NULL, NULL);
+
+-- 鏈夊0闃呰
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0034', 'TTS_HSDSTTS_V2', '鍙ら灏戝尽', 'zh_female_gufengshaoyu_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_gufengshaoyu_uranus_bigtts.mp3', NULL, NULL, NULL, 34, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0035', 'TTS_HSDSTTS_V2', '闇告皵闈掑彅', 'zh_male_baqiqingshu_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_baqiqingshu_uranus_bigtts.mp3', NULL, NULL, NULL, 35, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0036', 'TTS_HSDSTTS_V2', '鎮枒瑙h', 'zh_male_xuanyijieshuo_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_xuanyijieshuo_uranus_bigtts.mp3', NULL, NULL, NULL, 36, NULL, NULL, NULL, NULL);
+
+-- 瑙掕壊鎵紨
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0037', 'TTS_HSDSTTS_V2', '浣╁鐚�', 'zh_female_peiqi_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_peiqi_uranus_bigtts.mp3', NULL, NULL, NULL, 37, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0038', 'TTS_HSDSTTS_V2', '鐚村摜', 'zh_male_sunwukong_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_sunwukong_uranus_bigtts.mp3', NULL, NULL, NULL, 38, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0039', 'TTS_HSDSTTS_V2', '鐚叓鎴�', 'zh_male_zhubajie_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_zhubajie_uranus_bigtts.mp3', NULL, NULL, NULL, 39, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0040', 'TTS_HSDSTTS_V2', '鍞愬儳', 'zh_male_tangseng_uranus_bigtts', '鏅�氳瘽銆佽嫳璇�', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_tangseng_uranus_bigtts.mp3', NULL, NULL, NULL, 40, NULL, NULL, NULL, NULL);
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202605071037.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202605071037.sql
new file mode 100644
index 0000000..97586d7
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202605071037.sql
@@ -0,0 +1,5 @@
+-- 鍒犻櫎provider_code涓簍tson鐨勪緵搴斿櫒
+DELETE FROM `ai_model_provider` WHERE `provider_code` = 'ttson';
+
+-- 鍒犻櫎model_code涓篈CGNTTS鐨勯厤缃�
+DELETE FROM `ai_model_config` WHERE `model_code` = 'ACGNTTS';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202605071120.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202605071120.sql
new file mode 100644
index 0000000..c332917
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202605071120.sql
@@ -0,0 +1,6 @@
+-- 鍒犻櫎model_code涓篏izwitsTTS鐨勯厤缃�
+DELETE FROM `ai_model_config` WHERE `model_code` = 'GizwitsTTS';
+
+-- 鍒犻櫎鍏宠仈鐨凾TS闊宠壊璁板綍
+DELETE FROM `ai_tts_voice` WHERE `tts_model_id` = 'TTS_GizwitsTTS';
+DELETE FROM `ai_tts_voice` WHERE `tts_model_id` = 'TTS_ACGNTTS';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202605081008.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202605081008.sql
new file mode 100644
index 0000000..91a14a1
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202605081008.sql
@@ -0,0 +1,8 @@
+-- 鍒犻櫎provider_code涓簂inkerai鐨勪緵搴斿晢閰嶇疆
+DELETE FROM `ai_model_provider` WHERE `provider_code` = 'linkerai';
+
+-- 鍒犻櫎model_code涓篖inkeraiTTS鐨勬ā鍨嬮厤缃�
+DELETE FROM `ai_model_config` WHERE `model_code` = 'LinkeraiTTS';
+
+-- 鍒犻櫎LinkeraiTTS鍏宠仈鐨凾TS闊宠壊璁板綍
+DELETE FROM `ai_tts_voice` WHERE `tts_model_id` = 'TTS_LinkeraiTTS';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202605121509.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202605121509.sql
new file mode 100644
index 0000000..c8c2dc6
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202605121509.sql
@@ -0,0 +1,6 @@
+-- 鏂板鑱旂綉鎼滅储宸ュ叿閰嶇疆瀛楁
+SET @data_exists = (SELECT COUNT(*) FROM ai_model_provider WHERE id = 'SYSTEM_PLUGIN_WEB_SEARCH');
+SET @sql = IF(@data_exists = 0,
+ 'INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES (''SYSTEM_PLUGIN_WEB_SEARCH'', ''Plugin'', ''web_search'', ''鑱旂綉鎼滅储'', ''[{\"key\": \"provider\", \"type\": \"string\", \"label\": \"鎼滅储婧愶細metaso / tavily\", \"default\": \"metaso\", \"editing\": false, \"selected\": false}, {\"key\": \"description\", \"type\": \"string\", \"label\": \"宸ュ叿鎻忚堪\", \"default\": \"鑱旂綉鎼滅储宸ュ叿銆傚綋鐢ㄦ埛鏄庣‘闇�瑕佽仈缃戞悳绱㈤棶棰樻椂浣跨敤姝ゅ伐鍏枫�俓", \"editing\": false, \"selected\": false}, {\"key\": \"max_results\", \"type\": \"string\", \"label\": \"杩斿洖鏁伴噺\", \"default\": \"5\", \"editing\": false, \"selected\": false}, {\"key\": \"api_key\", \"type\": \"string\", \"label\": \"apiKey\", \"default\": \"mk-XXXX\", \"editing\": false, \"selected\": false}]'', 80, 1988490863118454785, ''2026-05-11 17:15:35'', 1988490863118454785, ''2026-05-12 10:55:49'')',
+ 'SELECT ''data already exists, skip'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202605251426.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202605251426.sql
new file mode 100644
index 0000000..c34e8ec
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202605251426.sql
@@ -0,0 +1,37 @@
+-- 鏂板鍛煎彨璁惧宸ュ叿閰嶇疆
+SET @data_exists = (SELECT COUNT(*) FROM ai_model_provider WHERE id = 'SYSTEM_PLUGIN_CALL_DEVICE');
+SET @sql = IF(@data_exists = 0,
+ 'INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`,
+`update_date`) VALUES (''SYSTEM_PLUGIN_CALL_DEVICE'', ''Plugin'', ''call_device'', ''璁惧鍛煎彨璁惧'', ''[]'', 85, 1988490863118454785, ''2026-05-18
+12:00:00'', 1988490863118454785, ''2026-05-18 12:00:00'')',
+ 'SELECT ''data already exists, skip'' AS msg');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- 鏇存柊绯荤粺鑿滃崟閰嶇疆锛屽鍔犺仈绯讳汉绠$悊鑿滃崟
+UPDATE sys_params
+SET param_value = CAST(
+ JSON_SET(
+ CAST(param_value AS JSON),
+ '$.addressBook',
+ JSON_OBJECT(
+ 'name', 'feature.addressBook.name',
+ 'enabled', FALSE,
+ 'description', 'feature.addressBook.description'
+ )
+ ) AS CHAR
+)
+WHERE param_code = 'system-web.menu'
+ AND NOT JSON_CONTAINS_PATH(CAST(param_value AS JSON), 'one', '$.addressBook');
+
+-- 鍒涘缓璁惧閫氳褰曡〃
+CREATE TABLE IF NOT EXISTS `ai_device_address_book` (
+ `mac_address` VARCHAR(64) NOT NULL COMMENT '鏈澶嘙AC鍦板潃',
+ `target_mac` VARCHAR(64) NOT NULL COMMENT '瀵规柟璁惧MAC鍦板潃',
+ `alias` VARCHAR(64) DEFAULT NULL COMMENT '鍒悕',
+ `has_permission` TINYINT(1) DEFAULT TRUE COMMENT '鏄惁鏈夋潈闄愬懠鍙�',
+ `creator` BIGINT DEFAULT NULL COMMENT '鍒涘缓浜�',
+ `create_date` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+ `updater` BIGINT DEFAULT NULL COMMENT '鏇存柊浜�',
+ `update_date` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
+ PRIMARY KEY (`mac_address`, `target_mac`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='璁惧閫氳褰曡〃';
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202606231030.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202606231030.sql
new file mode 100644
index 0000000..99af238
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202606231030.sql
@@ -0,0 +1,15 @@
+-- 鏇存柊EdgeTTS渚涘簲鍣ㄥ鍔犺閫熴�侀煶璋冦�侀煶閲忛厤缃�
+UPDATE `ai_model_provider`
+SET fields = '[{"key":"voice","label":"闊宠壊","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"},{"key":"rate","label":"璇��(-100~100)","type":"number"},{"key":"volume","label":"闊抽噺(0~100)","type":"number"},{"key":"pitch","label":"闊宠皟(-100~100)","type":"number"}]'
+WHERE id = 'SYSTEM_TTS_edge';
+
+UPDATE `ai_model_config` SET
+`remark` = 'EdgeTTS閰嶇疆璇存槑锛�
+1. 浣跨敤寰蒋Edge TTS鏈嶅姟
+2. 鏀寔澶氱璇█鍜岄煶鑹�
+3. 鍏嶈垂浣跨敤锛屾棤闇�娉ㄥ唽
+4. 闇�瑕佺綉缁滆繛鎺�
+5. 杈撳嚭鏂囦欢淇濆瓨鍦╰mp/鐩綍
+6. 璇�燂細-100~100锛�0涓烘甯搁�熷害
+7. 闊抽噺锛�0~100锛�50涓烘甯搁煶閲�
+8. 闊宠皟锛�-100~100锛�0涓烘甯搁煶璋�' WHERE `id` = 'TTS_EdgeTTS';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202606261131.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202606261131.sql
new file mode 100644
index 0000000..bdc9f3a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202606261131.sql
@@ -0,0 +1,40 @@
+-- 淇銆佹坊鍔燤inimax娴佸紡TTS闊宠壊
+delete from `ai_tts_voice` where tts_model_id = 'TTS_MinimaxStreamTTS';
+
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0001', 'TTS_MinimaxStreamTTS', '灏戝コ闊�', 'female-shaonv', '鏅�氳瘽', NULL, NULL, NULL, NULL, 1, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0002', 'TTS_MinimaxStreamTTS', '鎴愮啛濂冲0', 'female-chengshu', '鏅�氳瘽', NULL, NULL, NULL, NULL, 2, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0003', 'TTS_MinimaxStreamTTS', '闇搁亾灏戠埛', 'badao_shaoye', '鏅�氳瘽', NULL, NULL, NULL, NULL, 3, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0004', 'TTS_MinimaxStreamTTS', '鐥呭▏寮熷紵', 'bingjiao_didi', '鏅�氳瘽', NULL, NULL, NULL, NULL, 4, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0005', 'TTS_MinimaxStreamTTS', '绾湡瀛﹀紵', 'chunzhen_xuedi', '鏅�氳瘽', NULL, NULL, NULL, NULL, 5, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0006', 'TTS_MinimaxStreamTTS', '鍐锋贰瀛﹂暱', 'lengdan_xiongzhang', '鏅�氳瘽', NULL, NULL, NULL, NULL, 6, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0007', 'TTS_MinimaxStreamTTS', '鐢滅編灏忕幉', 'tianxin_xiaoling', '鏅�氳瘽', NULL, NULL, NULL, NULL, 7, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0008', 'TTS_MinimaxStreamTTS', '淇忕毊钀屽', 'qiaopi_mengmei', '鏅�氳瘽', NULL, NULL, NULL, NULL, 8, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0009', 'TTS_MinimaxStreamTTS', '濡╁獨寰″', 'wumei_yujie', '鏅�氳瘽', NULL, NULL, NULL, NULL, 9, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0010', 'TTS_MinimaxStreamTTS', '璋冩暀瀛﹀', 'diadia_xuemei', '鏅�氳瘽', NULL, NULL, NULL, NULL, 10, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0011', 'TTS_MinimaxStreamTTS', '娣¢泤瀛﹀', 'danya_xuejie', '鏅�氳瘽', NULL, NULL, NULL, NULL, 11, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0012', 'TTS_MinimaxStreamTTS', 'Santa Claus', 'Santa_Claus', '鏅�氳瘽', NULL, NULL, NULL, NULL, 12, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0013', 'TTS_MinimaxStreamTTS', 'Grinch', 'Grinch', '鏅�氳瘽', NULL, NULL, NULL, NULL, 13, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0014', 'TTS_MinimaxStreamTTS', '涓撲笟濂充富鎸�', 'Cantonese_ProfessionalHost锛團)', '绮よ', "https://filecdn.minimax.chat/public/84589766-c651-477b-b320-36b89599f953.mp3", NULL, NULL, NULL, 14, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0015', 'TTS_MinimaxStreamTTS', '娓╂煍濂冲0', 'Cantonese_GentleLady', '绮よ', "https://filecdn.minimax.chat/public/d391936d-d586-4dab-886c-a2a084a20920.mp3", NULL, NULL, NULL, 15, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0016', 'TTS_MinimaxStreamTTS', '涓撲笟鐢蜂富鎸�', 'Cantonese_ProfessionalHost锛圡)', '绮よ', NULL, NULL, NULL, NULL, 16, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0017', 'TTS_MinimaxStreamTTS', '娲绘臣鐢峰0', 'Cantonese_PlayfulMan', '绮よ', NULL, NULL, NULL, NULL, 17, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0018', 'TTS_MinimaxStreamTTS', '鍙埍濂冲', 'Cantonese_CuteGirl', '绮よ', "https://filecdn.minimax.chat/public/ff96520a-ac12-491d-945e-d6337467eb7d.mp3", NULL, NULL, NULL, 18, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0019', 'TTS_MinimaxStreamTTS', '鍠勮壇濂冲0', 'Cantonese_KindWoman', '绮よ', "https://filecdn.minimax.chat/public/78239a18-5e9c-4538-94f6-2f504c080374.mp3", NULL, NULL, NULL, 19, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0020', 'TTS_MinimaxStreamTTS', 'Crisp News Anchor', 'Cantonese_crisp_news_anchor_vv2', '绮よ', 'https://cdn.hailuoai.video/open-hailuo-video-web/public_assets/741a5a4b-f42a-4a9b-ae83-3954d097ab73.mp3', NULL, NULL, NULL, 20, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0021', 'TTS_MinimaxStreamTTS', 'Pro Reporter', 'Cantonese_professional_reporter_vv2', '绮よ', 'https://cdn.hailuoai.video/open-hailuo-video-web/public_assets/77afb86d-2cc4-4149-8c2e-e1174fa849d4.mp3', NULL, NULL, NULL, 21, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0022', 'TTS_MinimaxStreamTTS', 'Crisp Reporter', 'Cantonese_crisp_reporter_vv2', '绮よ', 'https://cdn.hailuoai.video/open-hailuo-video-web/public_assets/4ad39a61-628e-481c-9d60-bfb7c36bceb8.mp3', NULL, NULL, NULL, 22, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0023', 'TTS_MinimaxStreamTTS', 'Articulate', 'Cantonese_Articulate_commentator_vv2', '绮よ', 'https://cdn.hailuoai.video/open-hailuo-video-web/public_assets/9346601d-3035-4f9e-9b1c-0e03f30e5eef.mp3', NULL, NULL, NULL, 23, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0024', 'TTS_MinimaxStreamTTS', 'Warm Reporter', 'Cantonese_Warm_Reporter_vv2', '绮よ', 'https://cdn.hailuoai.video/open-hailuo-video-web/public_assets/c651050e-d6a5-4653-b09e-5177bdc8b04c.mp3', NULL, NULL, NULL, 24, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0025', 'TTS_MinimaxStreamTTS', 'Energetic', 'Cantonese_energetic_commentator_vv2', '绮よ', 'https://cdn.hailuoai.video/open-hailuo-video-web/public_assets/1fd1c461-71da-450d-91bb-45f819249b44.mp3', NULL, NULL, NULL, 25, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0026', 'TTS_MinimaxStreamTTS', 'Objective Narrator', 'Cantonese_objective_narrator_vv2', '绮よ', 'https://cdn.hailuoai.video/open-hailuo-video-web/public_assets/463c5a68-a7b5-4fe9-85b6-d494dca74abb.mp3', NULL, NULL, NULL, 26, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0027', 'TTS_MinimaxStreamTTS', '娌欏搼鐢峰0', 'Cantonese_Narrator', '绮よ', 'https://filecdn.minimax.chat/public/22e29cb0-0f5a-443a-9fa9-a32dabd55cf9.mp3', NULL, NULL, NULL, 27, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0028', 'TTS_MinimaxStreamTTS', 'News Anchor', 'Cantonese_news_anchor_vv2', '绮よ', 'https://cdn.hailuoai.video/open-hailuo-video-web/public_assets/59b9ba49-47d4-48f1-b207-09c0e0769a1b.mp3', NULL, NULL, NULL, 28, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0029', 'TTS_MinimaxStreamTTS', '娓婂崥瀛﹁��', 'Cantonese_WiselProfessor', '绮よ', 'https://filecdn.minimax.chat/public/8de234c4-64a7-4539-a316-c3b43ee1d0a9.mp3', NULL, NULL, NULL, 29, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0030', 'TTS_MinimaxStreamTTS', 'Resonant Host', 'Cantonese_resonant_host_vv2', '绮よ', 'https://cdn.hailuoai.video/open-hailuo-video-web/public_assets/ec8fc0ce-6657-4c49-9bf0-3953a15046ec.mp3', NULL, NULL, NULL, 30, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0031', 'TTS_MinimaxStreamTTS', '鍐锋紶鍛樺伐', 'Cantonese_IndifferentStaff', '绮よ', 'https://filecdn.minimax.chat/public/9686682c-3fc8-45f0-bd88-2a4692e1f63e.mp3', NULL, NULL, NULL, 31, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0032', 'TTS_MinimaxStreamTTS', 'Male News Anchor', 'Cantonese_Male_news_anchor_vv2', '绮よ', 'https://cdn.hailuoai.video/open-hailuo-video-web/public_assets/2055ee8b-fb25-424c-b8cb-e2baf971e520.mp3', NULL, NULL, NULL, 32, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0033', 'TTS_MinimaxStreamTTS', 'History Narrator', 'Cantonese_casual_narrator_vv2', '绮よ', 'https://cdn.hailuoai.video/open-hailuo-video-web/public_assets/c170f9e4-145d-4331-9910-1c3a0e6bbc3a.mp3', NULL, NULL, NULL, 33, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0034', 'TTS_MinimaxStreamTTS', 'Steady Reporter', 'Cantonese_Steady_reporter_vv2', '绮よ', 'https://cdn.hailuoai.video/open-hailuo-video-web/public_assets/381cfee2-772d-4a73-b30c-7914aec05771.mp3', NULL, NULL, NULL, 34, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0035', 'TTS_MinimaxStreamTTS', 'Serious Commentator', 'Cantonese_serious_commentator_vv2', '绮よ', 'https://cdn.hailuoai.video/open-hailuo-video-web/public_assets/57335c8a-32ea-475f-a198-b0de7fa1b126.mp3', NULL, NULL, NULL, 35, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0036', 'TTS_MinimaxStreamTTS', 'Podcast Host', 'Cantonese_podcast_host_vv2', '绮よ', 'https://cdn.hailuoai.video/open-hailuo-video-web/public_assets/048b617f-e1f8-422c-89fb-fdbc6e303675.mp3', NULL, NULL, NULL, 36, NULL, NULL, NULL, NULL);
+INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0037', 'TTS_MinimaxStreamTTS', 'Obj commentator', 'Cantonese_Objective_commentator_vv2', '绮よ', 'https://cdn.hailuoai.video/open-hailuo-video-web/public_assets/a8f55e44-b726-4ba5-815a-f1b515ba57ef.mp3', NULL, NULL, NULL, 37, NULL, NULL, NULL, NULL);
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202607011405.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202607011405.sql
new file mode 100644
index 0000000..260ea66
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202607011405.sql
@@ -0,0 +1,19 @@
+-- Add language configuration for local FunASR ASR.
+UPDATE `ai_model_provider`
+SET `fields` = '[{"key":"model_dir","label":"妯″瀷鐩綍","type":"string"},{"key":"output_dir","label":"杈撳嚭鐩綍","type":"string"},{"key":"language","label":"璇嗗埆璇█","type":"string","default":"auto"}]'
+WHERE `id` = 'SYSTEM_ASR_FunASR';
+
+UPDATE `ai_model_config`
+SET `config_json` = JSON_SET(`config_json`, '$.language', 'auto')
+WHERE `id` = 'ASR_FunASR'
+AND JSON_EXTRACT(`config_json`, '$.language') IS NULL;
+
+-- Update the FunASR local model configuration description to mention the language option.
+UPDATE `ai_model_config`
+SET `remark` = 'FunASR鏈湴妯″瀷閰嶇疆璇存槑锛�
+1. 闇�瑕佷笅杞芥ā鍨嬫枃浠跺埌xiaozhi-server/models/SenseVoiceSmall鐩綍
+2. 鏀寔涓棩闊╃菠璇煶璇嗗埆
+3. 鏈湴鎺ㄧ悊锛屾棤闇�缃戠粶杩炴帴
+4. 寰呰瘑鍒枃浠朵繚瀛樺湪tmp/鐩綍
+5. 鈥滆瘑鍒瑷�鈥濆瓧娈垫帶鍒惰瘑鍒绉嶏細auto = 鑷姩妫�娴嬶紱濡傞渶闄愬畾鍙瘑鍒腑鏂囧彲璁句负 zh锛坋n=鑻辫銆乯a=鏃ヨ銆乲o=闊╄銆亂ue=绮よ锛夈��'
+WHERE `id` = 'ASR_FunASR';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202607071530.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202607071530.sql
new file mode 100644
index 0000000..be22422
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202607071530.sql
@@ -0,0 +1,20 @@
+-- liquibase formatted sql
+
+-- changeset tykechen:202607071530
+CREATE TABLE IF NOT EXISTS `ai_agent_snapshot` (
+ `id` VARCHAR(32) NOT NULL COMMENT '蹇収ID',
+ `agent_id` VARCHAR(32) NOT NULL COMMENT '鏅鸿兘浣揑D',
+ `user_id` BIGINT DEFAULT NULL COMMENT '鎵�灞炵敤鎴稩D',
+ `version_no` INT UNSIGNED NOT NULL COMMENT '鐗堟湰鍙�',
+ `snapshot_data` JSON NOT NULL DEFAULT (JSON_OBJECT()) COMMENT '蹇収鏁版嵁',
+ `changed_fields` JSON DEFAULT NULL COMMENT '鍙樻洿瀛楁',
+ `source` VARCHAR(32) DEFAULT 'config' COMMENT '蹇収鏉ユ簮',
+ `restore_from_snapshot_id` VARCHAR(32) DEFAULT NULL COMMENT '鎭㈠鏉ユ簮蹇収ID',
+ `restore_from_version_no` INT UNSIGNED DEFAULT NULL COMMENT '鎭㈠鏉ユ簮鐗堟湰鍙�',
+ `creator` BIGINT DEFAULT NULL COMMENT '鍒涘缓鑰�',
+ `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_agent_version` (`agent_id`, `version_no`),
+ INDEX `idx_agent_created_at` (`agent_id`, `created_at`),
+ INDEX `idx_snapshot_user_created_at` (`user_id`, `created_at`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鏅鸿兘浣撻厤缃揩鐓ц〃';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202607101200.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202607101200.sql
new file mode 100644
index 0000000..7243f21
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202607101200.sql
@@ -0,0 +1,8 @@
+-- liquibase formatted sql
+
+-- changeset tykechen:202607101200
+ALTER TABLE `ai_agent_snapshot`
+ ADD COLUMN `redaction_version` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '蹇収鑴辨晱瑙勫垯鐗堟湰' AFTER `created_at`,
+ ADD INDEX `idx_snapshot_redaction_version_id` (`redaction_version`, `id`);
+
+-- rollback ALTER TABLE `ai_agent_snapshot` DROP INDEX `idx_snapshot_redaction_version_id`, DROP COLUMN `redaction_version`;
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202607290930.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202607290930.sql
new file mode 100644
index 0000000..67702f5
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202607290930.sql
@@ -0,0 +1,46 @@
+-- liquibase formatted sql
+
+-- 鎻掑叆鏂扮殑 hass_state 鎻掍欢璁板綍锛堝悎骞� get_state 鐨勯厤缃瓧娈碉級
+INSERT INTO ai_model_provider (id, model_type, provider_code, name, fields,
+ sort, creator, create_date, updater, update_date)
+VALUES ('SYSTEM_PLUGIN_HA_STATE',
+ 'Plugin',
+ 'hass_state',
+ 'HomeAssistant璁惧鎺у埗',
+ JSON_ARRAY(
+ JSON_OBJECT(
+ 'key', 'base_url',
+ 'type', 'string',
+ 'label', 'HA 鏈嶅姟鍣ㄥ湴鍧�',
+ 'default',
+ (SELECT param_value FROM sys_params WHERE param_code = 'plugins.home_assistant.base_url')
+ ),
+ JSON_OBJECT(
+ 'key', 'api_key',
+ 'type', 'string',
+ 'label', 'HA API 璁块棶浠ょ墝',
+ 'default',
+ (SELECT param_value FROM sys_params WHERE param_code = 'plugins.home_assistant.api_key')
+ ),
+ JSON_OBJECT(
+ 'key', 'devices',
+ 'type', 'array',
+ 'label', '璁惧鍒楄〃锛堝悕绉�,瀹炰綋ID;鈥︼級',
+ 'default',
+ (SELECT param_value FROM sys_params WHERE param_code = 'plugins.home_assistant.devices')
+ )
+ ),
+ 50, 0, NOW(), 0, NOW());
+
+-- 杩佺Щ宸查厤缃殑 agent锛氭妸鎸囧悜 HA_GET_STATE 鐨勬敼鎴� HA_STATE锛堜繚鐣欏弬鏁帮級
+UPDATE ai_agent_plugin_mapping
+SET plugin_id = 'SYSTEM_PLUGIN_HA_STATE'
+WHERE plugin_id = 'SYSTEM_PLUGIN_HA_GET_STATE';
+
+-- HA_SET_STATE 鏈潵灏辨病鏈夐厤缃瓧娈碉紙fields=[]锛夛紝鐩存帴鍒犻櫎鍏� mapping
+DELETE FROM ai_agent_plugin_mapping
+WHERE plugin_id = 'SYSTEM_PLUGIN_HA_SET_STATE';
+
+-- 鍒犻櫎鏃х殑鎻掍欢瀹氫箟
+DELETE FROM ai_model_provider
+WHERE id IN ('SYSTEM_PLUGIN_HA_GET_STATE', 'SYSTEM_PLUGIN_HA_SET_STATE');
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202608061030.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202608061030.sql
new file mode 100644
index 0000000..28854a6
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202608061030.sql
@@ -0,0 +1,4 @@
+UPDATE `ai_model_config`
+SET `config_json` = JSON_SET(`config_json`, '$.model_name', 'deepseek-v4-flash')
+WHERE `id` = 'LLM_DeepSeekLLM'
+ AND JSON_EXTRACT(`config_json`, '$.model_name') = 'deepseek-chat';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202608061800.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202608061800.sql
new file mode 100644
index 0000000..11cac0b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202608061800.sql
@@ -0,0 +1,22 @@
+锘縐PDATE `ai_model_config`
+SET `config_json` = JSON_SET(`config_json`, '$.model_name', 'doubao-seed-2-0-lite-260215')
+WHERE `id` = 'LLM_DoubaoLLM'
+ AND JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.model_name')) IN (
+ 'doubao-1-5-pro-32k-250115'
+ );
+
+UPDATE `ai_model_config`
+SET `remark` = REPLACE(
+ REPLACE(
+ `remark`,
+ '寮�閫欴oubao-1.5-pro鏈嶅姟',
+ '寮�閫� Doubao-Seed-2.0-Lite 鏈嶅姟'
+ ),
+ '褰撳墠寤鸿浣跨敤doubao-1-5-pro-32k-250115',
+ '褰撳墠寤鸿浣跨敤 doubao-seed-2-0-lite-260215'
+)
+WHERE `id` = 'LLM_DoubaoLLM'
+ AND (
+ `remark` LIKE '%Doubao-1.5-pro%'
+ OR `remark` LIKE '%doubao-1-5-pro-32k-250115%'
+ );
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202608131030.sql b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202608131030.sql
new file mode 100644
index 0000000..6266079
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/202608131030.sql
@@ -0,0 +1,4 @@
+UPDATE `sys_params`
+SET `param_value` = '15'
+WHERE `param_code` = 'tts_timeout'
+ AND `param_value` = '10';
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml
new file mode 100644
index 0000000..17d4fb7
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml
@@ -0,0 +1,741 @@
+# 瑙勮寖绾﹀畾锛�
+# id鐢熸垚鏍规嵁鏃堕棿鏃跺垎锛屾枃浠跺悕瀵瑰簲id锛屽父鍙樻暟鎹彲浠ユ牴鎹ā鍧楀懡鍚嶈嚜瀹氫箟
+# 姣忔瀵规暟鎹〃杩涜鏀瑰姩鏃讹紝鍙厑璁告柊寤烘柊瀵筩hangeSet锛屼笉鍏佽瀵逛笂涓�涓猚hangeSet閰嶇疆鍙婃枃浠惰繘琛屼慨鏀�
+databaseChangeLog:
+ - changeSet:
+ id: 202503141335
+ author: John
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202503141335.sql
+ - changeSet:
+ id: 202503141346
+ author: czc
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202503141346.sql
+ - changeSet:
+ id: 202504082211
+ author: John
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202504082211.sql
+ - changeSet:
+ id: 202504092335
+ author: John
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202504092335.sql
+ - changeSet:
+ id: 202504112044
+ author: John
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202504112044.sql
+ - changeSet:
+ id: 202504112058
+ author: John
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202504112058.sql
+ - changeSet:
+ id: 202504151206
+ author: John
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202504151206.sql
+ - changeSet:
+ id: 202504181536
+ author: John
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202504181536.sql
+ - changeSet:
+ id: 202504221135
+ author: John
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202504221135.sql
+ - changeSet:
+ id: 202504221555
+ author: John
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202504221555.sql
+ - changeSet:
+ id: 202504251422
+ author: jiangkunyin
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202504251422.sql
+ - changeSet:
+ id: 202504291043
+ author: jiangkunyin
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202504291043.sql
+ - changeSet:
+ id: 202504301341
+ author: Goody
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202504301341.sql
+ - changeSet:
+ id: 202505022134
+ author: Goody
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202505022134.sql
+ - changeSet:
+ id: 202505081146
+ author: hrz
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202505081146.sql
+ - changeSet:
+ id: 202505091555
+ author: whosmyqueen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202505091555.sql
+ - changeSet:
+ id: 202505111914
+ author: hrz
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202505111914.sql
+ - changeSet:
+ id: 202505122348
+ author: ljwwd2
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202505122348.sql
+ - changeSet:
+ id: 202505142037
+ author: hrz
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202505142037.sql
+ - changeSet:
+ id: 202505182234
+ author: amen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202505182234.sql
+ - changeSet:
+ id: 202505201744
+ author: hrz
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202505201744.sql
+ - changeSet:
+ id: 202505151451
+ author: hsoftxl
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202505151451.sql
+ - changeSet:
+ id: 202505271414
+ author: hrz
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202505271414.sql
+ - changeSet:
+ id: 202505292203
+ author: CAIXYPROMISE
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202505292203.sql
+ - changeSet:
+ id: 202506010920
+ author: hrz
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202506010920.sql
+ - changeSet:
+ id: 202506031639
+ author: hrz
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202506031639.sql
+ - changeSet:
+ id: 202506032232
+ author: hrz
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202506032232.sql
+ - changeSet:
+ id: 202506051538
+ author: hrz
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202506051538.sql
+ - changeSet:
+ id: 202506080955
+ author: hrz
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202506080955.sql
+ - changeSet:
+ id: 202506091720
+ author: shane0411
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202506091720.sql
+ - changeSet:
+ id: 202506161101
+ author: hrz
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202506161101.sql
+ - changeSet:
+ id: 202506191643
+ author: hrz
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202506191643.sql
+ - changeSet:
+ id: 202506251620
+ author: Tink
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202506251620.sql
+ - changeSet:
+ id: 202506261637
+ author: hrz
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202506261637.sql
+ - changeSet:
+ id: 202507101203
+ author: luruxian
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202507101203.sql
+ - changeSet:
+ id: 202507071130
+ author: cgd
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202507071130.sql
+ - changeSet:
+ id: 202507071530
+ author: cgd
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202507071530.sql
+ - changeSet:
+ id: 202507031602
+ author: zjy
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202507031602.sql
+ - changeSet:
+ id: 202507041018
+ author: zjy
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202507041018.sql
+ - changeSet:
+ id: 202507081646
+ author: zjy
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202507081646.sql
+ - changeSet:
+ id: 202508081701
+ author: hrz
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202508081701.sql
+ - changeSet:
+ id: 202508111734
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202508111734.sql
+ - changeSet:
+ id: 202508131557
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202508131557.sql
+ - changeSet:
+ id: 202508271113
+ author: cgd
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202508271113.sql
+ - changeSet:
+ id: 202509051745
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202509051745.sql
+ - changeSet:
+ id: 202509081140
+ author: cgd
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202509081140.sql
+ - changeSet:
+ id: 202509091042
+ author: cgd
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202509091042.sql
+ - changeSet:
+ id: 202509091633
+ author: fyb
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202509091633.sql
+ - changeSet:
+ id: 202509080922
+ author: fyb
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202509080922.sql
+ - changeSet:
+ id: 202509161610
+ author: fyb
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202509161610.sql
+ - changeSet:
+ id: 202509161701
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202509161701.sql
+ - changeSet:
+ id: 202509221530
+ author: cgd
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202509221530.sql
+ - changeSet:
+ id: 202509191545
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202509191545.sql
+ - changeSet:
+ id: 202509220926
+ author: fyb
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202509220926.sql
+
+ - changeSet:
+ id: 202509220958
+ author: fyb
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202509220958.sql
+ - changeSet:
+ id: 202510071522
+ author: hrz
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202510071522.sql
+ - changeSet:
+ id: 202510191042
+ author: hrz
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202510191042.sql
+ - changeSet:
+ id: 202510250956
+ author: rainv123
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202510250956.sql
+ - changeSet:
+ id: 202510251150
+ author: rainv123
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202510251150.sql
+ - changeSet:
+ id: 202511131023
+ author: hrz
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202511131023.sql
+ - changeSet:
+ id: 202511221450
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202511221450.sql
+ - changeSet:
+ id: 202512031517
+ author: rainv123
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202512031517.sql
+
+ - changeSet:
+ id: 202512041515
+ author: cgd
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202512041515.sql
+ - changeSet:
+ id: 202512131453
+ author: hrz
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202512131453.sql
+ - changeSet:
+ id: 202512161529
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202512161529.sql
+ - changeSet:
+ id: 202512192245
+ author: hrz
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202512192245.sql
+ - changeSet:
+ id: 202512221117
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202512221117.sql
+ - changeSet:
+ id: 202512301430
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202512301430.sql
+ - changeSet:
+ id: 202601051433
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202601051433.sql
+ - changeSet:
+ id: 202601141645
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202601141645.sql
+ - changeSet:
+ id: 202601231530
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202601231530.sql
+ - changeSet:
+ id: 202601261730
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202601261730.sql
+ - changeSet:
+ id: 202602021555
+ author: shengzhou1216
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202602021555.sql
+ - changeSet:
+ id: 202602051017
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202602051017.sql
+ - changeSet:
+ id: 202602051125
+ author: DaGou12138
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202602051125.sql
+ - changeSet:
+ id: 202602061650
+ author: DaGou12138
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202602061650.sql
+ - changeSet:
+ id: 202602271137
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202602271137.sql
+ - changeSet:
+ id: 202602281000
+ author: rainv123
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202602281000.sql
+ - changeSet:
+ id: 202603091051
+ author: rainv123
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202603091051.sql
+ - changeSet:
+ id: 202603101723
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202603101723.sql
+ - changeSet:
+ id: 202603111131
+ author: DaGou12138
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202603111131.sql
+ - changeSet:
+ id: 202603231037
+ author: rainv123
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202603231037.sql
+ - changeSet:
+ id: 202603311200
+ author: cgd
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202603311200.sql
+ - changeSet:
+ id: 202604010930
+ author: rainv123
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202604010930.sql
+ - changeSet:
+ id: 202604011035
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202604011035.sql
+ - changeSet:
+ id: 202604011545
+ author: rainv123
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202604011545.sql
+ - changeSet:
+ id: 202604161357
+ author: hrz
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202604161357.sql
+ - changeSet:
+ id: 202604201719
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202604201719.sql
+ - changeSet:
+ id: 202604211515
+ author: rainv123
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202604211515.sql
+ - changeSet:
+ id: 202604211700
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202604211700.sql
+ - changeSet:
+ id: 202604300930
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202604300930.sql
+ - changeSet:
+ id: 202605071037
+ author: fyb
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202605071037.sql
+ - changeSet:
+ id: 202605071120
+ author: fyb
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202605071120.sql
+ - changeSet:
+ id: 202605081008
+ author: fyb
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202605081008.sql
+ - changeSet:
+ id: 202605121509
+ author: DaGou12138
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202605121509.sql
+ - changeSet:
+ id: 202605251426
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202605251426.sql
+ - changeSet:
+ id: 202606231030
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202606231030.sql
+ - changeSet:
+ id: 202606261131
+ author: RanChen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202606261131.sql
+ - changeSet:
+ id: 202607011405
+ author: LauraGPT
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202607011405.sql
+ - changeSet:
+ id: 202607071530
+ author: tykechen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202607071530.sql
+ - changeSet:
+ id: 202607101200
+ author: tykechen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202607101200.sql
+ - changeSet:
+ id: 202607290930
+ author: cgd
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202607290930.sql
+ - changeSet:
+ id: 202608061000
+ author: cgd
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202608061030.sql
+ - changeSet:
+ id: 202608061800
+ author: cgd
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202608061800.sql
+ - changeSet:
+ id: 202608131030
+ author: tykechen
+ changes:
+ - sqlFile:
+ encoding: utf8
+ path: classpath:db/changelog/202608131030.sql
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages.properties b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages.properties
new file mode 100644
index 0000000..860bd12
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages.properties
@@ -0,0 +1,213 @@
+#\u7B80\u4F53\u4E2D\u6587
+500=\u670D\u52A1\u5668\u5185\u90E8\u5F02\u5E38
+401=\u672A\u6388\u6743
+403=\u62D2\u7EDD\u8BBF\u95EE\uFF0C\u6CA1\u6709\u6743\u9650
+
+10001={0}\u4E0D\u80FD\u4E3A\u7A7A
+10002=\u6570\u636E\u5E93\u4E2D\u5DF2\u5B58\u5728\u8BE5\u8BB0\u5F55
+10003=\u83B7\u53D6\u53C2\u6570\u5931\u8D25
+10004=\u8D26\u53F7\u6216\u5BC6\u7801\u9519\u8BEF
+10005=\u8D26\u53F7\u5DF2\u88AB\u505C\u7528
+10006=\u552F\u4E00\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A
+10007=\u9A8C\u8BC1\u7801\u4E0D\u6B63\u786E
+10008=\u5148\u5220\u9664\u5B50\u83DC\u5355\u6216\u6309\u94AE
+10009=\u539F\u5BC6\u7801\u4E0D\u6B63\u786E
+10010=\u8D26\u53F7\u6216\u5BC6\u7801\u4E0D\u6B63\u786E,\u60A8\u8FD8\u6709\u53EF\u4EE5\u5C1D\u8BD5{0}\u6B21
+
+10011=\u4E0A\u7EA7\u90E8\u95E8\u9009\u62E9\u9519\u8BEF
+10012=\u4E0A\u7EA7\u83DC\u5355\u4E0D\u80FD\u4E3A\u81EA\u8EAB
+10013=\u6570\u636E\u6743\u9650\u63A5\u53E3\uFF0C\u53EA\u80FD\u662FMap\u7C7B\u578B\u53C2\u6570
+10014=\u8BF7\u5148\u5220\u9664\u4E0B\u7EA7\u90E8\u95E8
+10015=\u8BF7\u5148\u5220\u9664\u90E8\u95E8\u4E0B\u7684\u7528\u6237
+10016=\u90E8\u7F72\u5931\u8D25\uFF0C\u6CA1\u6709\u6D41\u7A0B
+10017=\u6A21\u578B\u56FE\u4E0D\u6B63\u786E\uFF0C\u8BF7\u68C0\u67E5
+10018=\u5BFC\u51FA\u5931\u8D25\uFF0C\u6A21\u578BID\u4E3A{0}
+
+10019=\u8BF7\u4E0A\u4F20\u6587\u4EF6
+10020=token\u4E0D\u80FD\u4E3A\u7A7A
+10021=token\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55
+10022=\u8D26\u53F7\u5DF2\u88AB\u9501\u5B9A
+10023=\u8BF7\u4E0A\u4F20zip\u3001bar\u3001bpmn\u3001bpmn20.xml\u683C\u5F0F\u6587\u4EF6
+
+10024=\u4E0A\u4F20\u6587\u4EF6\u5931\u8D25{0}
+10025=\u53D1\u9001\u77ED\u4FE1\u5931\u8D25{0}
+10026=\u90AE\u4EF6\u6A21\u677F\u4E0D\u5B58\u5728
+
+10027=Redis\u670D\u52A1\u5F02\u5E38
+10028=\u5B9A\u65F6\u4EFB\u52A1\u5931\u8D25
+10029=\u4E0D\u80FD\u5305\u542B\u975E\u6CD5\u5B57\u7B26
+10030=\u5BC6\u7801\u957F\u5EA6\u4E0D\u8DB3{0}\u4F4D
+10031=\u5BC6\u7801\u5FC5\u987B\u540C\u65F6\u5305\u542B\u6570\u5B57\u3001\u5927\u5C0F\u5199\u5B57\u6BCD\u548C\u7279\u6B8A\u5B57\u7B26\u7EC4\u6210
+10032=\u5220\u9664\u672C\u6570\u636E\u5F02\u5E38
+10033=\u8BBE\u5907\u9A8C\u8BC1\u7801\u9519\u8BEF
+
+10034=\u53C2\u6570\u503C\u4E0D\u80FD\u4E3A\u7A7A
+10035=\u53C2\u6570\u7C7B\u578B\u4E0D\u80FD\u4E3A\u7A7A
+10036=\u4E0D\u652F\u6301\u7684\u53C2\u6570\u7C7B\u578B
+10037=\u53C2\u6570\u503C\u5FC5\u987B\u662F\u6709\u6548\u7684\u6570\u5B57
+10038=\u53C2\u6570\u503C\u5FC5\u987B\u662Ftrue\u6216false
+10039=\u53C2\u6570\u503C\u5FC5\u987B\u662F\u6709\u6548\u7684JSON\u6570\u7EC4\u683C\u5F0F
+10040=\u53C2\u6570\u503C\u5FC5\u987B\u662F\u6709\u6548\u7684JSON\u683C\u5F0F
+
+10041=\u8BBE\u5907\u672A\u627E\u5230
+10042={0}
+10043=\u5220\u9664\u6570\u636E\u5931\u8D25
+10044=\u7528\u6237\u672A\u767B\u5F55
+10045=WebSocket\u8FDE\u63A5\u5931\u8D25\u6216\u8FDE\u63A5\u8D85\u65F6
+10046=\u4FDD\u5B58\u58F0\u7EB9\u9519\u8BEF\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
+10047=\u6BCF\u65E5\u53D1\u9001\u6B21\u6570\u5DF2\u8FBE\u4E0A\u9650
+10048=\u65E7\u5BC6\u7801\u8F93\u5165\u9519\u8BEF
+10049=\u8BBE\u7F6E\u7684LLM\u4E0D\u662Fopenai\u548Collama
+10050=token\u751F\u6210\u5931\u8D25
+10051=\u8D44\u6E90\u4E0D\u5B58\u5728
+10052=\u9ED8\u8BA4\u667A\u80FD\u4F53\u672A\u627E\u5230
+10053=\u667A\u80FD\u4F53\u672A\u627E\u5230
+10054=\u58F0\u7EB9\u63A5\u53E3\u672A\u914D\u7F6E\uFF0C\u8BF7\u5148\u5728\u53C2\u6570\u914D\u7F6E\u4E2D\u914D\u7F6E\u58F0\u7EB9\u63A5\u53E3\u5730\u5740(server.voice_print)
+10055=\u77ED\u4FE1\u53D1\u9001\u5931\u8D25
+10056=\u77ED\u4FE1\u8FDE\u63A5\u5EFA\u7ACB\u5931\u8D25
+10057=\u667A\u80FD\u4F53\u7684\u58F0\u7EB9\u521B\u5EFA\u5931\u8D25
+10058=\u667A\u80FD\u4F53\u7684\u5BF9\u5E94\u58F0\u7EB9\u66F4\u65B0\u5931\u8D25
+10059=\u667A\u80FD\u4F53\u7684\u5BF9\u5E94\u58F0\u7EB9\u5220\u9664\u5931\u8D25
+10060=\u53D1\u9001\u592A\u9891\u7E41\uFF0C\u8BF7{0}\u79D2\u540E\u518D\u8BD5
+10061=\u6FC0\u6D3B\u7801\u4E0D\u80FD\u4E3A\u7A7A
+10062=\u6FC0\u6D3B\u7801\u9519\u8BEF
+10063=\u8BBE\u5907\u5DF2\u6FC0\u6D3B
+10064=\u8BE5\u6A21\u578B\u4E3A\u9ED8\u8BA4\u6A21\u578B\uFF0C\u8BF7\u5148\u8BBE\u7F6E\u5176\u4ED6\u6A21\u578B\u4E3A\u9ED8\u8BA4\u6A21\u578B
+10065=\u65B0\u589E\u6570\u636E\u5931\u8D25
+10066=\u4FEE\u6539\u6570\u636E\u5931\u8D25
+10067=\u56FE\u5F62\u9A8C\u8BC1\u7801\u9519\u8BEF
+10068=\u6CA1\u6709\u5F00\u542F\u624B\u673A\u6CE8\u518C\uFF0C\u6CA1\u6CD5\u4F7F\u7528\u77ED\u4FE1\u9A8C\u8BC1\u7801\u529F\u80FD
+10069=\u7528\u6237\u540D\u4E0D\u662F\u624B\u673A\u53F7\u7801\uFF0C\u8BF7\u91CD\u65B0\u8F93\u5165
+10070=\u6B64\u624B\u673A\u53F7\u7801\u5DF2\u7ECF\u6CE8\u518C\u8FC7
+10071=\u8F93\u5165\u7684\u624B\u673A\u53F7\u7801\u672A\u6CE8\u518C
+10072=\u5F53\u524D\u4E0D\u5141\u8BB8\u666E\u901A\u7528\u6237\u6CE8\u518C
+10073=\u6CA1\u6709\u5F00\u542F\u624B\u673A\u6CE8\u518C\uFF0C\u6CA1\u6CD5\u4F7F\u7528\u627E\u56DE\u5BC6\u7801\u529F\u80FD
+10074=\u8F93\u5165\u7684\u624B\u673A\u53F7\u7801\u683C\u5F0F\u4E0D\u6B63\u786E
+10075=\u8F93\u5165\u7684\u624B\u673A\u9A8C\u8BC1\u7801\u9519\u8BEF
+10076=\u5B57\u5178\u7C7B\u578B\u4E0D\u5B58\u5728
+10077=\u5B57\u5178\u7C7B\u578B\u7F16\u7801\u91CD\u590D
+10078=\u8BFB\u53D6\u8D44\u6E90\u5931\u8D25
+10079=LLM\u5927\u6A21\u578B\u548CIntent\u610F\u56FE\u8BC6\u522B\uFF0C\u9009\u62E9\u53C2\u6570\u4E0D\u5339\u914D
+10080=\u6B64\u58F0\u97F3\u58F0\u7EB9\u5BF9\u5E94\u7684\u4EBA\uFF08{0}\uFF09\u5DF2\u7ECF\u6CE8\u518C\uFF0C\u8BF7\u9009\u62E9\u5176\u4ED6\u58F0\u97F3\u6CE8\u518C
+10081=\u5220\u9664\u58F0\u7EB9\u51FA\u73B0\u9519\u8BEF
+10082=\u6B64\u6B21\u4FEE\u6539\u4E0D\u5141\u8BB8\uFF0C\u6B64\u58F0\u97F3\u5DF2\u7ECF\u6CE8\u518C\u4E3A\u58F0\u7EB9\u4E86\uFF08{0}\uFF09
+10083=\u4FEE\u6539\u58F0\u7EB9\u9519\u8BEF\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
+10084=\u58F0\u7EB9\u63A5\u53E3\u5730\u5740\u5B58\u5728\u9519\u8BEF\uFF0C\u8BF7\u8FDB\u5165\u53C2\u6570\u7BA1\u7406\u4FEE\u6539\u58F0\u7EB9\u63A5\u53E3\u5730\u5740
+10085=\u97F3\u9891\u6570\u636E\u4E0D\u5C5E\u4E8E\u8FD9\u4E2A\u667A\u80FD\u4F53
+10086=\u97F3\u9891\u6570\u636E\u662F\u7A7A\u7684\u8BF7\u68C0\u67E5\u4E0A\u4F20\u6570\u636E
+10087=\u58F0\u7EB9\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u6C42\u4E0D\u6210\u529F
+10088=\u58F0\u7EB9\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u6C42\u5904\u7406\u5931\u8D25
+10089=\u58F0\u7EB9\u6CE8\u9500\u5931\u8D25\uFF0C\u8BF7\u6C42\u4E0D\u6210\u529F
+10090=\u58F0\u7EB9\u6CE8\u9500\u5931\u8D25\uFF0C\u8BF7\u6C42\u5904\u7406\u5931\u8D25
+10091=\u4F9B\u5E94\u5668\u4E0D\u5B58\u5728
+10092=\u8BBE\u7F6E\u7684LLM\u4E0D\u5B58\u5728
+10093=\u8BE5\u6A21\u578B\u914D\u7F6E\u5DF2\u88AB\u667A\u80FD\u4F53{0}\u5F15\u7528\uFF0C\u65E0\u6CD5\u5220\u9664
+10094=\u8BE5LLM\u6A21\u578B\u5DF2\u88AB\u610F\u56FE\u8BC6\u522B\u914D\u7F6E\u5F15\u7528\uFF0C\u65E0\u6CD5\u5220\u9664
+10095=\u65E0\u6548\u670D\u52A1\u7AEF\u64CD\u4F5C
+10096=\u672A\u914D\u7F6E\u670D\u52A1\u7AEFWebSocket\u5730\u5740
+10097=\u76EE\u6807WebSocket\u5730\u5740\u4E0D\u5B58\u5728
+10098=WebSocket\u5730\u5740\u5217\u8868\u4E0D\u80FD\u4E3A\u7A7A
+10099=WebSocket\u5730\u5740\u4E0D\u80FD\u4F7F\u7528localhost\u6216127.0.0.1
+10100=WebSocket\u5730\u5740\u683C\u5F0F\u4E0D\u6B63\u786E
+10101=WebSocket\u8FDE\u63A5\u6D4B\u8BD5\u5931\u8D25
+10102=OTA\u5730\u5740\u4E0D\u80FD\u4E3A\u7A7A
+10103=OTA\u5730\u5740\u4E0D\u80FD\u4F7F\u7528localhost\u6216127.0.0.1
+10104=OTA\u5730\u5740\u5FC5\u987B\u4EE5http\u6216https\u5F00\u5934
+10105=OTA\u5730\u5740\u5FC5\u987B\u4EE5/ota/\u7ED3\u5C3E
+10106=OTA\u63A5\u53E3\u8BBF\u95EE\u5931\u8D25
+10107=OTA\u63A5\u53E3\u8FD4\u56DE\u5185\u5BB9\u683C\u5F0F\u4E0D\u6B63\u786E
+10108=OTA\u63A5\u53E3\u9A8C\u8BC1\u5931\u8D25
+10109=MCP\u5730\u5740\u4E0D\u80FD\u4E3A\u7A7A
+10110=MCP\u5730\u5740\u4E0D\u80FD\u4F7F\u7528localhost\u6216127.0.0.1
+10111=\u4E0D\u662F\u6B63\u786E\u7684MCP\u5730\u5740
+10112=MCP\u63A5\u53E3\u8BBF\u95EE\u5931\u8D25
+10113=MCP\u63A5\u53E3\u8FD4\u56DE\u5185\u5BB9\u683C\u5F0F\u4E0D\u6B63\u786E
+10114=MCP\u63A5\u53E3\u9A8C\u8BC1\u5931\u8D25
+10115=\u58F0\u7EB9\u63A5\u53E3\u5730\u5740\u4E0D\u80FD\u4E3A\u7A7A
+10116=\u58F0\u7EB9\u63A5\u53E3\u5730\u5740\u4E0D\u80FD\u4F7F\u7528localhost\u6216127.0.0.1
+10117=\u4E0D\u662F\u6B63\u786E\u7684\u58F0\u7EB9\u63A5\u53E3\u5730\u5740
+10118=\u58F0\u7EB9\u63A5\u53E3\u5730\u5740\u5FC5\u987B\u4EE5http\u6216https\u5F00\u5934
+10119=\u58F0\u7EB9\u63A5\u53E3\u8BBF\u95EE\u5931\u8D25
+10120=\u58F0\u7EB9\u63A5\u53E3\u8FD4\u56DE\u5185\u5BB9\u683C\u5F0F\u4E0D\u6B63\u786E
+10121=\u58F0\u7EB9\u63A5\u53E3\u9A8C\u8BC1\u5931\u8D25
+10122=mqtt\u5BC6\u94A5\u4E0D\u80FD\u4E3A\u7A7A
+10123=\u60A8\u7684mqtt\u5BC6\u94A5\u957F\u5EA6\u4E0D\u5B89\u5168\uFF0C\u5B57\u7B26\u6570\u9700\u8981\u81F3\u5C118\u4E2A\u5B57\u7B26\u4E14\u5FC5\u987B\u540C\u65F6\u5305\u542B\u5927\u5C0F\u5199\u5B57\u6BCD
+10124=\u60A8\u7684mqtt\u5BC6\u94A5\u957F\u5EA6\u4E0D\u5B89\u5168\uFF0Cmqtt\u5BC6\u94A5\u5FC5\u987B\u540C\u65F6\u5305\u542B\u5927\u5C0F\u5199\u5B57\u6BCD
+10125=\u60A8\u7684mqtt\u5BC6\u94A5\u5305\u542B\u5F31\u5BC6\u7801
+10128=\u5B57\u5178\u6807\u7B7E\u91CD\u590D
+10129=SM2\u5BC6\u94A5\u672A\u914D\u7F6E
+10130=SM2\u89E3\u5BC6\u5931\u8D25
+10131=modelType\u548CprovideCode\u4E0D\u80FD\u4E3A\u7A7A
+10132=\u6CA1\u6709\u6743\u9650\u67E5\u770B\u8BE5\u667A\u80FD\u4F53\u7684\u804A\u5929\u8BB0\u5F55
+10133=\u4F1A\u8BDDID\u4E0D\u80FD\u4E3A\u7A7A
+10134=\u667A\u80FD\u4F53ID\u4E0D\u80FD\u4E3A\u7A7A
+10135=\u804A\u5929\u8BB0\u5F55\u4E0B\u8F7D\u5931\u8D25
+10136=\u4E0B\u8F7D\u94FE\u63A5\u5DF2\u8FC7\u671F\u6216\u65E0\u6548
+10137=\u4E0B\u8F7D\u94FE\u63A5\u65E0\u6548
+10138=\u7528\u6237
+10139=\u667A\u4F53
+10140=\u97F3\u9891\u6587\u4EF6\u4E0D\u80FD\u4E3A\u7A7A
+10141=\u53EA\u652F\u6301\u97F3\u9891\u6587\u4EF6
+10142=\u97F3\u9891\u6587\u4EF6\u5927\u5C0F\u4E0D\u80FD\u8D85\u8FC710MB
+10143=\u4E0A\u4F20\u5931\u8D25
+10144=\u58F0\u97F3\u514B\u9686\u8BB0\u5F55\u4E0D\u5B58\u5728
+10145=\u97F3\u8272\u8D44\u6E90\u4FE1\u606F\u4E0D\u80FD\u4E3A\u7A7A
+10146=\u5E73\u53F0\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+10147=\u97F3\u8272ID\u4E0D\u80FD\u4E3A\u7A7A
+10148=\u5F52\u5C5E\u8D26\u53F7\u4E0D\u80FD\u4E3A\u7A7A
+10149=\u5220\u9664\u7684\u97F3\u8272\u8D44\u6E90ID\u4E0D\u80FD\u4E3A\u7A7A
+10150=\u60A8\u6CA1\u6709\u6743\u9650\u64CD\u4F5C\u8BE5\u8BB0\u5F55
+10151=\u8BF7\u5148\u4E0A\u4F20\u97F3\u9891\u6587\u4EF6
+10152=\u6A21\u578B\u914D\u7F6E\u672A\u627E\u5230
+10153=\u6A21\u578B\u7C7B\u578B\u672A\u627E\u5230
+10154=\u8BAD\u7EC3\u5931\u8D25: {0}
+10155=\u706B\u5C71\u5F15\u64CE\u7F3A\u5C11\u914D\u7F6E
+10156=\u54CD\u5E94\u683C\u5F0F\u9519\u8BEF\uFF0C\u7F3A\u5C11BaseResp\u5B57\u6BB5
+10157=\u8BF7\u6C42\u5931\u8D25
+10158=\u514B\u9686\u97F3\u8272:
+10159=\u97F3\u8272ID\u5DF2\u5B58\u5728
+10160=\u706B\u5C71\u5F15\u64CE\u97F3\u8272ID\u683C\u5F0F\u9519\u8BEF\uFF0C\u5FC5\u987B\u4EE5S_\u5F00\u5934
+10161=Mac\u5730\u5740\u5DF2\u5B58\u5728
+10162=\u6A21\u578B\u4F9B\u5E94\u5668\u4E0D\u5B58\u5728
+10163=\u77E5\u8BC6\u5E93\u8BB0\u5F55\u4E0D\u5B58\u5728
+10164=RAG\u914D\u7F6E\u672A\u627E\u5230
+10165=RAG\u914D\u7F6E\u7C7B\u578B\u9519\u8BEF
+10166=\u9ED8\u8BA4RAG\u914D\u7F6E\u672A\u627E\u5230
+10167=RAG\u8C03\u7528\u5931\u8D25\uFF0C{0}
+10168=\u4E0A\u4F20\u6587\u4EF6\u5931\u8D25
+10169=\u60A8\u6CA1\u6709\u6743\u9650\u64CD\u4F5C\u8BE5\u8BB0\u5F55
+10170=\u77E5\u8BC6\u5E93\u540D\u79F0\u91CD\u590D
+10171=RAG\u914D\u7F6E\u4F53\u7684base_url\u4E0D\u80FD\u4E3A\u7A7A
+10172=RAG\u914D\u7F6E\u4F53\u7684api_key\u4E0D\u80FD\u4E3A\u7A7A
+10173=RAG\u914D\u7F6E\u4F53\u7684api_key\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u8BF7\u66F4\u6362\u4E3A\u5728\u53D6\u7684API\u53C2\u6570
+10174=RAG\u914D\u7F6E\u4F53\u7684base_url\u683C\u5F0F\u9519\u8BEF\uFF0C\u5FC5\u987B\u4EE5http\u6216https\u5F00\u5934
+10175=mac\u5730\u5740\u4E0D\u80FD\u4E3A\u7A7A
+10176=RAG\u914D\u7F6E\u4F53\u7684dataset_id\u4E0D\u80FD\u4E3A\u7A7A
+10177=RAG\u914D\u7F6E\u4F53\u7684model_id\u4E0D\u80FD\u4E3A\u7A7A
+10178=RAG\u914D\u7F6E\u4F53\u7684dataset_id\u548Cmodel_id\u4E0D\u80FD\u4E3A\u7A7A
+10179=\u6587\u4EF6\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+10180=\u6587\u4EF6\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A
+10181=\u97F3\u8272\u514B\u9686\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+10182=\u97F3\u8272\u514B\u9686\u97F3\u9891\u4E0D\u5B58\u5728
+10183=\u9ED8\u8BA4\u667A\u80FD\u4F53\u672A\u627E\u5230
+10184=\u4E0D\u652F\u6301\u7684\u9002\u914D\u5668\u7C7B\u578B
+10185=RAG\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25
+10186=\u9002\u914D\u5668\u521B\u5EFA\u5931\u8D25
+10187=\u9002\u914D\u5668\u521D\u59CB\u5316\u5931\u8D25
+10188=\u9002\u914D\u5668\u8FDE\u63A5\u6D4B\u8BD5\u5931\u8D25
+10189=\u9002\u914D\u5668\u64CD\u4F5C\u5931\u8D25
+10190=\u9002\u914D\u5668\u672A\u627E\u5230
+10191=\u9002\u914D\u5668\u7F13\u5B58\u9519\u8BEF
+10192=\u9002\u914D\u5668\u7C7B\u578B\u672A\u627E\u5230
+10193=\u8BBE\u5907ID\u4E0D\u80FD\u4E3A\u7A7A
+10194=\u8BBE\u5907\u4E0D\u5B58\u5728\u6216\u4E0D\u5728\u7EBF
+10195=OTA\u4E0A\u4F20\u6B21\u6570\u8D85\u8FC7\u9650\u5236
+10196=\u6807\u7B7E\u540D\u79F0\u5DF2\u5B58\u5728
+10197=\u6807\u7B7E\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+10198=\u6807\u7B7E\u4E0D\u5B58\u5728
+10199=\u89E3\u6790\u4E2D\u6587\u4EF6\u6682\u4E0D\u652F\u6301\u6B64\u64CD\u4F5C
+10200=\u6CA1\u6709\u6743\u9650\u67E5\u770B\u8BE5\u667A\u80FD\u4F53\u7684MCP\u63A5\u5165\u70B9\u5730\u5740
+10201=\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u8FDB\u5165\u53C2\u6570\u7BA1\u7406\u914D\u7F6Emcp\u63A5\u5165\u70B9\u5730\u5740
+10202=\u6CA1\u6709\u6743\u9650\u67E5\u770B\u8BE5\u667A\u80FD\u4F53\u7684MCP\u5DE5\u5177\u5217\u8868
+10203=\u6587\u4EF6\u540D\u79F0\u5DF2\u5B58\u5728
+10204=\u6587\u4EF6\u5927\u5C0F\u8D85\u8FC71MB\u9650\u5236
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_de_DE.properties b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_de_DE.properties
new file mode 100644
index 0000000..cf70b00
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_de_DE.properties
@@ -0,0 +1,213 @@
+#Deutsch
+500=Server interne Ausnahme
+401=Nicht autorisiert
+403=Zugriff verweigert, keine Berechtigungen
+10001={0} darf nicht leer sein
+10002=Der Datensatz existiert bereits in der Datenbank
+10003=Parameter abrufen fehlgeschlagen
+10004=Die Kontonummer oder das Passwort ist falsch
+10005=Konto wurde deaktiviert
+10006=Eindeutige ID darf nicht leer sein
+10007=Der Best\u00E4tigungscode ist falsch
+10008=Zuerst Untermen\u00FC oder Schaltfl\u00E4che l\u00F6schen
+10009=Das urspr\u00FCngliche Passwort ist falsch
+10010=Das Konto oder Passwort ist falsch, Sie k\u00F6nnen es noch {0} Mal versuchen
+
+10011=Die \u00FCbergeordnete Abteilung hat eine falsche Wahl getroffen
+10012=Oberes Men\u00FC kann nicht sich selbst sein
+10013=Datenberechtigungsschnittstelle, die nur ein Map-Typ-Parameter sein kann
+10014=Bitte l\u00F6schen Sie zuerst die untergeordnete Abteilung
+10015=Bitte l\u00F6schen Sie zuerst den Benutzer unter der Abteilung
+10016=Bereitstellung fehlgeschlagen, kein Prozess
+10017=Das ModellDiagramm ist falsch, bitte \u00FCberpr\u00FCfen
+10018=Export fehlgeschlagen, Modell-ID ist {0}
+
+10019=Bitte laden Sie eine Datei hoch
+10020=Token darf nicht leer sein
+10021=Token ist ung\u00FCltig, bitte melden Sie sich erneut an
+10022=Das Konto wurde gesperrt
+10023=Bitte laden Sie Dateien im Format zip, bar, bpmn, bpmn20.xml hoch
+
+10024=Datei-Upload fehlgeschlagen {0}
+10025=SMS-Sendung fehlgeschlagen {0}
+10026=E-Mail-Vorlage existiert nicht
+
+10027=Redis-Dienstausnahme
+10028=Zeitgesteuerte Aufgabe fehlgeschlagen
+10029=Darf keine illegalen Zeichen enthalten
+
+10030=Das Passwort hat weniger als {0} Ziffern
+10031=Das Passwort muss gleichzeitig aus Zahlen, Gro\u00DF- und Kleinbuchstaben sowie Sonderzeichen bestehen
+10032=Ausnahme beim L\u00F6schen dieser Daten
+10033=Ger\u00E4tebest\u00E4tigungscode Fehler
+
+10034=Parameterwert darf nicht leer sein
+10035=Parametertyp darf nicht leer sein
+10036=Nicht unterst\u00FCtzter Parametertyp
+10037=Parameterwert muss eine g\u00FCltige Zahl sein
+10038=Parameterwert muss true oder false sein
+10039=Parameterwert muss ein g\u00FCltiges JSON-Array-Format sein
+10040=Parameterwert muss ein g\u00FCltiges JSON-Format sein
+
+10041=Ger\u00E4t nicht gefunden
+10042={0}
+10043=L\u00F6schen der Daten fehlgeschlagen
+10044=Benutzer nicht angemeldet
+10045=WebSocket-Verbindung fehlgeschlagen oder Zeit\u00FCberschreitung
+10046=Stimmabdruck speichern fehlgeschlagen, bitte Administrator kontaktieren
+10047=T\u00E4gliches SendeLimit erreicht
+10048=Altes Passwort ist falsch
+10049=Das eingestellte LLM ist nicht openai oder ollama
+10050=Token-Generierung fehlgeschlagen
+10051=Ressource nicht gefunden
+10052=Standard-Agent nicht gefunden
+10053=Agent nicht gefunden
+10054=Stimmabdruck-Schnittstelle nicht konfiguriert, bitte konfigurieren Sie zuerst die Stimmabdruck-Schnittstellenadresse (server.voice_print) in der Parameterkonfiguration
+10055=SMS-Sendung fehlgeschlagen
+10056=SMS-Verbindung konnte nicht hergestellt werden
+10057=Stimmabdruck f\u00FCr Agent konnte nicht erstellt werden
+10058=Stimmabdruck f\u00FCr Agent konnte nicht aktualisiert werden
+10059=Stimmabdruck f\u00FCr Agent konnte nicht gel\u00F6scht werden
+10060=Sendung zu h\u00E4ufig, bitte versuchen Sie es nach {0} Sekunden erneut
+10061=Aktivierungscode darf nicht leer sein
+10062=Aktivierungscode Fehler
+10063=Ger\u00E4t bereits aktiviert
+10064=Dieses Modell ist das Standardmodell, bitte setzen Sie zuerst ein anderes Modell als Standard
+10065=Hinzuf\u00FCgen der Daten fehlgeschlagen
+10066=Aktualisieren der Daten fehlgeschlagen
+10067=Grafischer Best\u00E4tigungscode Fehler
+10068=Mobilregistrierung nicht aktiviert, SMS-Best\u00E4tigungscode-Funktion kann nicht verwendet werden
+10069=Benutzername ist keine Handynummer, bitte erneut eingeben
+10070=Diese Handynummer wurde bereits registriert
+10071=Die eingegebene Handynummer ist nicht registriert
+10072=Normale Benutzerregistrierung derzeit nicht erlaubt
+10073=Mobilregistrierung nicht aktiviert, Passwort-Wiederherstellungsfunktion kann nicht verwendet werden
+10074=Das Format der eingegebenen Handynummer ist falsch
+10075=Der eingegebene SMS-Best\u00E4tigungscode ist falsch
+10076=W\u00F6rterbuchtyp existiert nicht
+10077=W\u00F6rterbuchtypcode ist dupliziert
+10078=Lesen der Ressource fehlgeschlagen
+10079=LLM-Modell und Absichtserkennung, Parameterauswahl stimmt nicht \u00FCberein
+10080=Dieser Stimmabdruck geh\u00F6rt zu {0}, der bereits registriert ist, bitte w\u00E4hlen Sie eine andere Stimme
+10081=Fehler beim L\u00F6schen des Stimmabdrucks aufgetreten
+10082=\u00C4nderung nicht erlaubt, diese Stimme wurde bereits als Stimmabdruck registriert ({0})
+10083=Fehler beim \u00C4ndern des Stimmabdrucks, bitte Administrator kontaktieren
+10084=Stimmabdruck-Schnittstellenadresse Fehler, bitte gehen Sie zur Parameterverwaltung, um die Stimmabdruck-Schnittstellenadresse zu \u00E4ndern
+10085=Audiodaten geh\u00F6ren nicht zu diesem Agent
+10086=Audiodaten sind leer, bitte \u00FCberpr\u00FCfen Sie hochgeladene Daten
+10087=Stimmabdruck-Registrierung fehlgeschlagen, Anfrage nicht erfolgreich
+10088=Stimmabdruck-Registrierung fehlgeschlagen, Anfrageverarbeitung fehlgeschlagen
+10089=Stimmabdruck-Stornierung fehlgeschlagen, Anfrage nicht erfolgreich
+10090=Stimmabdruck-Stornierung fehlgeschlagen, Anfrageverarbeitung fehlgeschlagen
+10091=Modellanbieter existiert nicht
+10092=Das konfigurierte LLM existiert nicht
+10093=Diese Modellkonfiguration wird von Agent {0} referenziert und kann nicht gel\u00F6scht werden
+10094=Dieses LLM-Modell wird von der Absichtserkennungskonfiguration referenziert und kann nicht gel\u00F6scht werden
+10095=Ung\u00FCltige Serveroperation
+10096=Server WebSocket-Adresse nicht konfiguriert
+10097=Ziel-WebSocket-Adresse existiert nicht
+10098=WebSocket-Adressliste darf nicht leer sein
+10099=WebSocket-Adresse darf nicht localhost oder 127.0.0.1 verwenden
+10100=WebSocket-Adressformat ist falsch
+10101=WebSocket-Verbindungstest fehlgeschlagen
+10102=OTA-Adresse darf nicht leer sein
+10103=OTA-Adresse darf nicht localhost oder 127.0.0.1 verwenden
+10104=OTA-Adresse muss mit http oder https beginnen
+10105=OTA-Adresse muss mit /ota/ enden
+10106=OTA-Schnittstellenzugriff fehlgeschlagen
+10107=OTA-Schnittstelle R\u00FCckgabeinhaltformat ist falsch
+10108=OTA-Schnittstellenvalidierung fehlgeschlagen
+10109=MCP-Adresse darf nicht leer sein
+10110=MCP-Adresse darf nicht localhost oder 127.0.0.1 verwenden
+10111=Keine g\u00FCltige MCP-Adresse
+10112=MCP-Schnittstellenzugriff fehlgeschlagen
+10113=MCP-Schnittstelle R\u00FCckgabeinhaltformat ist falsch
+10114=MCP-Schnittstellenvalidierung fehlgeschlagen
+10115=Stimmabdruck-Schnittstellenadresse darf nicht leer sein
+10116=Stimmabdruck-Schnittstellenadresse darf nicht localhost oder 127.0.0.1 verwenden
+10117=Keine g\u00FCltige Stimmabdruck-Schnittstellenadresse
+10118=Stimmabdruck-Schnittstellenadresse muss mit http oder https beginnen
+10119=Stimmabdruck-Schnittstellenzugriff fehlgeschlagen
+10120=Stimmabdruck-Schnittstelle R\u00FCckgabeinhaltformat ist falsch
+10121=Stimmabdruck-Schnittstellenvalidierung fehlgeschlagen
+10122=MQTT-Geheimnis darf nicht leer sein
+10123=Ihr MQTT-Geheimnis ist nicht sicher, es muss mindestens 8 Zeichen lang sein und muss sowohl Gro\u00DF- als auch Kleinbuchstaben enthalten
+10124=Ihr MQTT-Geheimnis ist nicht sicher, MQTT-Geheimnis muss sowohl Gro\u00DF- als auch Kleinbuchstaben enthalten
+10125=Ihr MQTT-Geheimnis enth\u00E4lt schwaches Passwort
+10128=W\u00F6rterbuchlabel ist dupliziert
+10129=SM2-Schl\u00FCssel nicht konfiguriert
+10130=SM2-Entschl\u00FCsselung fehlgeschlagen
+10131=modelType und provideCode d\u00FCrfen nicht leer sein
+10132=Keine Berechtigung, den Chat-Verlauf dieses Agents anzuzeigen
+10133=Sitzungs-ID darf nicht leer sein
+10134=Agent-ID darf nicht leer sein
+10135=Chat-Verlauf-Download fehlgeschlagen
+10136=Download-Link abgelaufen oder ung\u00FCltig
+10137=Download-Link ung\u00FCltig
+10138=Benutzer
+10139=Agent
+10140=Audio-Datei darf nicht leer sein
+10141=Nur Audio-Dateien werden unterst\u00FCtzt
+10142=Audio-Dateigr\u00F6\u00DFe darf 10MB nicht \u00FCberschreiten
+10143=Upload fehlgeschlagen
+10144=Stimmenklon-Datensatz existiert nicht
+10145=Stimmressourceninformationen d\u00FCrfen nicht leer sein
+10146=TTS-Plattformname darf nicht leer sein
+10147=Stimmen-ID darf nicht leer sein
+10148=Kontoinhaber darf nicht leer sein
+10149=Zu l\u00F6schende Stimmressourcen-ID darf nicht leer sein
+10150=Sie haben keine Berechtigung, diesen Datensatz zu bearbeiten
+10151=Bitte laden Sie zuerst eine Audio-Datei hoch
+10152=Modellkonfiguration nicht gefunden
+10153=Modelltyp nicht gefunden
+10154=Training fehlgeschlagen: {0}
+10155=Huoshan Engine-Konfiguration fehlt
+10156=Antwortformat Fehler, BaseResp-Feld fehlt
+10157=Anfrage fehlgeschlagen
+10158=Stimme klonen:
+10159=Stimmen-ID existiert bereits
+10160=Huoshan Engine-Stimmen-ID-Formatfehler, muss mit S_ beginnen
+10161=Mac-Adresse existiert bereits
+10162=Modellanbieter existiert nicht
+10163=Wissensbasis-Datensatz existiert nicht
+10164=RAG-Konfiguration nicht gefunden
+10165=RAG-Konfigurationstyp Fehler
+10166=Standard-RAG-Konfiguration nicht gefunden
+10167=RAG-API-Aufruf fehlgeschlagen: {0}
+10168=Datei-Upload fehlgeschlagen
+10169=Keine Berechtigung, diese Wissensbasis zu bearbeiten
+10170=Wissensbasis-Name existiert bereits
+10171=RAG-Konfiguration base_url darf nicht leer sein
+10172=RAG-Konfiguration api_key darf nicht leer sein
+10173=RAG-Konfiguration api_key darf keinen Platzhalter enthalten, bitte ersetzen Sie mit tats\u00E4chlichem API-Schl\u00FCssel
+10174=RAG-Konfiguration base_url-Formatfehler, muss mit http oder https beginnen
+10175=Mac-Adresse darf nicht leer sein
+10176=RAG-Konfiguration dataset_id darf nicht leer sein
+10177=RAG-Konfiguration model_id darf nicht leer sein
+10178=RAG-Konfiguration dataset_id und model_id d\u00FCrfen nicht leer sein
+10179=Dateiname darf nicht leer sein
+10180=Dateiinhalt darf nicht leer sein
+10181=Stimmenklon-Name darf nicht leer sein
+10182=Stimmenklon-Audio nicht gefunden
+10183=Standard-Agent-Vorlage nicht gefunden
+10184=Nicht unterst\u00FCtzter Adaptertyp
+10185=RAG-Konfigurationsvalidierung fehlgeschlagen
+10186=Adapter-Erstellung fehlgeschlagen
+10187=Adapter-Initialisierung fehlgeschlagen
+10188=Adapter-Verbindungstest fehlgeschlagen
+10189=Adapter-Operation fehlgeschlagen
+10190=Adapter nicht gefunden
+10191=Adapter-Cache-Fehler
+10192=Adaptertyp nicht gefunden
+10193=Adapter-ID darf nicht leer sein
+10194=Adapter nicht gefunden oder nicht online
+10195=OTA-Upload-Anzahl \u00FCberschreitet das Limit
+10196=Tag-Name existiert bereits
+10197=Tag-Name darf nicht leer sein
+10198=Tag nicht gefunden
+10199=Dateianalyse l\u00E4uft, dieser Vorgang wird nicht unterst\u00FCtzt
+10200=Keine Berechtigung, die MCP-Endpunktadresse dieses Agenten anzuzeigen
+10201=Bitte kontaktieren Sie den Administrator, um die MCP-Endpunktadresse in der Parameterverwaltung zu konfigurieren
+10202=Keine Berechtigung, die MCP-Tool-Liste dieses Agenten anzuzeigen
+10203=Dateiname existiert bereits
+10204=Dateigr\u00f6\u00dfe \u00fcberschreitet 1MB Limit
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_en_US.properties b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_en_US.properties
new file mode 100644
index 0000000..8354dfc
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_en_US.properties
@@ -0,0 +1,214 @@
+#English
+500=Server internal exception
+401=Unauthorized
+403=Access denied, no permissions
+10001={0} cannot be empty
+10002=The record already exists in the database
+10003=Failed to get parameters
+10004=The account number or password is incorrect.
+10005=Account has been deactivated
+10006=Unique ID cannot be empty
+10007=The verification code is incorrect
+10008=First delete submenu or button
+10009=The original password is incorrect
+10010=The account or password is incorrect, you can try {0} more times
+
+10011=The superior department made a wrong choice
+10012=Upper menu cannot be for itself
+10013=Data permission interface, which can only be a Map type parameter.
+10014=Please delete the subordinate department first
+10015=Please delete the user under the department first
+10016=Deployment failed, no process
+10017=The model diagram is incorrect, please check
+10018=Export failed, model ID is {0}
+
+10019=Please upload a file
+10020=token cannot be empty
+10021=token is invalid, please log in again
+10022=The account has been locked
+10023=Please upload files in zip, bar, bpmn, bpmn20.xml format
+
+10024=Failed to upload file {0}
+10025=Failed to send SMS {0}
+10026=Email template does not exist
+
+10027=Redis service exception
+10028=Timed task failed
+10029=Cannot contain illegal characters
+
+10030=The password is less than {0} digits.
+10031=The password must consist of numbers, uppercase and lowercase letters, and special characters at the same time
+10032=Exception in deleting this data
+10033=Device verification code error
+
+10034=Parameter value cannot be empty
+10035=Parameter type cannot be empty
+10036=Unsupported parameter type
+10037=Parameter value must be a valid number
+10038=Parameter value must be true or false
+10039=Parameter value must be a valid JSON array format
+10040=Parameter value must be a valid JSON format
+
+10041=Device not found
+10042={0}
+10043=Failed to delete data
+10044=User not logged in
+10045=WebSocket connection failed or timeout
+10046=Failed to save voiceprint, please contact administrator
+10047=Daily sending limit reached
+10048=Old password is incorrect
+10049=The set LLM is not openai or ollama
+10050=Failed to generate token
+10051=Resource not found
+10052=Default agent not found
+10053=Agent not found
+10054=Voiceprint interface not configured, please configure voiceprint interface address (server.voice_print) in parameter configuration first
+10055=Failed to send SMS
+10056=Failed to establish SMS connection
+10057=Failed to create voiceprint for agent
+10058=Failed to update voiceprint for agent
+10059=Failed to delete voiceprint for agent
+10060=Sending too frequently, please try again after {0} seconds
+10061=Activation code cannot be empty
+10062=Activation code error
+10063=Device already activated
+10064=This model is the default model, please set another model as default first
+10065=Failed to add data
+10066=Failed to update data
+10067=Graphic verification code error
+10068=Mobile registration is not enabled, SMS verification code function cannot be used
+10069=Username is not a mobile phone number, please re-enter
+10070=This mobile phone number has already been registered
+10071=The entered mobile phone number is not registered
+10072=Ordinary user registration is not allowed currently
+10073=Mobile registration is not enabled, password retrieval function cannot be used
+10074=The entered mobile phone number format is incorrect
+10075=The entered SMS verification code is incorrect
+10076=Dictionary type does not exist
+10077=Dictionary type code is duplicated
+10078=Failed to read resource
+10079=LLM model and Intent recognition, parameter selection does not match
+10080=This voiceprint belongs to {0} who has already registered, please select another voice
+10081=Error occurred while deleting voiceprint
+10082=Modification not allowed, this voice has already been registered as a voiceprint ({0})
+10083=Error modifying voiceprint, please contact administrator
+10084=Voiceprint interface address error, please go to parameter management to modify the voiceprint interface address
+10085=Audio data does not belong to this agent
+10086=Audio data is empty, please check uploaded data
+10087=Voiceprint registration failed, request unsuccessful
+10088=Voiceprint registration failed, request processing failed
+10089=Voiceprint cancellation failed, request unsuccessful
+10090=Voiceprint cancellation failed, request processing failed
+10091=Model provider does not exist
+10092=The configured LLM does not exist
+10093=This model configuration is referenced by agent {0} and cannot be deleted
+10094=This LLM model is referenced by intent recognition configuration and cannot be deleted
+10095=Invalid server operation
+10096=Server WebSocket address not configured
+10097=Target WebSocket address does not exist
+10098=WebSocket address list cannot be empty
+10099=WebSocket address cannot use localhost or 127.0.0.1
+10100=WebSocket address format is incorrect
+10101=WebSocket connection test failed
+10102=OTA address cannot be empty
+10103=OTA address cannot use localhost or 127.0.0.1
+10104=OTA address must start with http or https
+10105=OTA address must end with /ota/
+10106=OTA interface access failed
+10107=OTA interface return content format is incorrect
+10108=OTA interface validation failed
+10109=MCP address cannot be empty
+10110=MCP address cannot use localhost or 127.0.0.1
+10111=Not a valid MCP address
+10112=MCP interface access failed
+10113=MCP interface return content format is incorrect
+10114=MCP interface validation failed
+10115=Voiceprint interface address cannot be empty
+10116=Voiceprint interface address cannot use localhost or 127.0.0.1
+10117=Not a valid voiceprint interface address
+10118=Voiceprint interface address must start with http or https
+10119=Voiceprint interface access failed
+10120=Voiceprint interface return content format is incorrect
+10121=Voiceprint interface validation failed
+10122=MQTT secret cannot be empty
+10123=Your MQTT secret is not secure, it needs to be at least 8 characters and must contain both uppercase and lowercase letters
+10124=Your MQTT secret is not secure, MQTT secret must contain both uppercase and lowercase letters
+10125=Your MQTT secret contains weak password
+10128=Dictionary label is duplicated
+10129=SM2 key not configured
+10130=SM2 decryption failed
+10131=modelType and provideCode cannot be empty
+10132=No permission to view the chat history of this agent
+10133=Session ID cannot be empty
+10134=Agent ID cannot be empty
+10135=Chat history download failed
+10136=Download link expired or invalid
+10137=Download link invalid
+10138=User
+10139=Agent
+10140=Audio file cannot be empty
+10141=Only audio files are supported
+10142=Audio file size cannot exceed 10MB
+10143=Upload failed
+10144=Voice clone record does not exist
+10145=Voice resource information cannot be empty
+10146=TTS platform name cannot be empty
+10147=Voice ID cannot be empty
+10148=Owner account cannot be empty
+10149=Voice resource ID to delete cannot be empty
+10150=You do not have permission to operate this record
+10151=Please upload audio file first
+10152=Model configuration not found
+10153=Model type not found
+10154=Training failed: {0}
+10155=Huoshan Engine configuration missing
+10156=Response format error, missing BaseResp field
+10157=Request failed
+10158=Clone Voice:
+10159=Voice ID already exists
+10160=Huoshan Engine voice ID format error, must start with S_
+10161=Mac address already exists
+10162=Model provider does not exist
+10163=Knowledge base record does not exist
+10164=RAG configuration not found
+10165=RAG configuration type error
+10166=Default RAG configuration not found
+10167=RAG API call failed: {0}
+10168=Upload file failed
+10169=No permission to operate this knowledge base
+10170=Knowledge base name already exists
+10171=RAG configuration base_url cannot be empty
+10172=RAG configuration api_key cannot be empty
+10173=RAG configuration api_key cannot contain placeholder, please replace with actual API key
+10174=RAG configuration base_url format error, must start with http or https
+10175=Mac address cannot be empty
+10176=RAG configuration dataset_id cannot be empty
+10177=RAG configuration model_id cannot be empty
+10178=RAG configuration dataset_id and model_id cannot be empty
+10179=File name cannot be empty
+10180=File content cannot be empty
+10181=Voice clone name cannot be empty
+10182=Voice clone audio not found
+10183=Default agent template not found
+10184=Unsupported adapter type
+10185=RAG configuration validation failed
+10186=Adapter creation failed
+10187=Adapter initialization failed
+10188=Adapter connection test failed
+10189=Adapter operation failed
+10190=Adapter not found
+10191=Adapter cache error
+10192=Adapter type not found
+10193=Device ID cannot be empty
+10194=Device not found or not online
+10195=OTA upload times exceed the limit
+10196=Tag name already exists
+10197=Tag name cannot be empty
+10198=Tag not found
+10199=Parsing in progress, this operation is not supported
+10200=No permission to view the MCP endpoint address of this agent
+10201=Please contact the administrator to configure the MCP endpoint address in parameter management
+10202=No permission to view the MCP tool list of this agent
+10203=File name already exists
+10204=File size exceeds 1MB limit
+
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_pt_BR.properties b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_pt_BR.properties
new file mode 100644
index 0000000..3992667
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_pt_BR.properties
@@ -0,0 +1,213 @@
+#Portugu\u00EAs do Brasil
+500=Erro interno do servidor
+401=N\u00E3o autorizado
+403=Acesso negado, sem permiss\u00E3o
+
+10001={0} n\u00E3o pode ser vazio
+10002=O registro j\u00E1 existe no banco de dados
+10003=Falha ao obter par\u00E2metros
+10004=Conta ou senha incorretos
+10005=Conta desativada
+10006=Identificador \u00FAnico n\u00E3o pode ser vazio
+10007=C\u00F3digo de verifica\u00E7\u00E3o incorreto
+10008=Exclua primeiro o submenu ou bot\u00E3o
+10009=Senha atual incorreta
+10010=Conta ou senha incorretos, voc\u00EA ainda tem {0} tentativas
+
+10011=Erro na sele\u00E7\u00E3o do departamento superior
+10012=O menu superior n\u00E3o pode ser ele mesmo
+10013=Interface de permiss\u00E3o de dados, apenas par\u00E2metros do tipo Map
+10014=Exclua primeiro o departamento inferior
+10015=Exclua primeiro os usu\u00E1rios do departamento
+10016=Falha na implanta\u00E7\u00E3o, sem fluxo
+10017=Diagrama incorreto, verifique
+10018=Falha na exporta\u00E7\u00E3o, ID do modelo 锟� {0}
+
+10019=Por favor, fa\u00E7a upload do arquivo
+10020=Token n\u00E3o pode ser vazio
+10021=Token expirado, fa\u00E7a login novamente
+10022=Conta bloqueada
+10023=Por favor, fa\u00E7a upload de arquivos nos formatos zip, bar, bpmn, bpmn20.xml
+
+10024=Falha no upload do arquivo {0}
+10025=Falha ao enviar SMS {0}
+10026=Modelo de e-mail n\u00E3o existe
+
+10027=Erro no servi\u00E7o Redis
+10028=Falha na tarefa agendada
+10029=N\u00E3o pode conter caracteres ilegais
+10030=Comprimento da senha insuficiente, m\u00EDnimo de {0} caracteres
+10031=A senha deve conter n\u00FAmeros, letras mai\u00FAsculas e min\u00FAsculas e caracteres especiais
+10032=Erro ao excluir dados
+10033=C\u00F3digo de verifica\u00E7\u00E3o do dispositivo incorreto
+
+10034=Valor do par\u00E2metro n\u00E3o pode ser vazio
+10035=Tipo do par\u00E2metro n\u00E3o pode ser vazio
+10036=Tipo de par\u00E2metro n\u00E3o suportado
+10037=Valor do par\u00E2metro deve ser um n\u00FAmero v\u00E1lido
+10038=Valor do par\u00E2metro deve ser true ou false
+10039=Valor do par\u00E2metro deve ser um formato de array JSON v\u00E1lido
+10040=Valor do par\u00E2metro deve ser um formato JSON v\u00E1lido
+
+10041=Dispositivo n\u00E3o encontrado
+10042={0}
+10043=Falha ao excluir dados
+10044=Usu\u00E1rio n\u00E3o logged in
+10045=Falha na conex\u00E3o WebSocket ou conex\u00E3o expirada
+10046=Erro ao salvar impress\u00E3o vocal, entre em contato com o administrador
+10047=Limite di\u00E1rio de envio atingido
+10048=Erro na inser\u00E7\u00E3o da senha antiga
+10049=LLM configurado n\u00E3o 锟� openai ou Collama
+10050=Falha na gera\u00E7\u00E3o do token
+10051=Recurso n\u00E3o existe
+10052=Agente padr\u00E3o n\u00E3o encontrado
+10053=Agente n\u00E3o encontrado
+10054=Interface de impress\u00E3o vocal n\u00E3o configurada, configure primeiro o endere\u00E7o da interface no par\u00E2metro (server.voice_print)
+10055=Falha no envio do SMS
+10056=Falha ao estabelecer conex\u00E3o SMS
+10057=Falha ao criar impress\u00E3o vocal do agente
+10058=Falha ao atualizar a impress\u00E3o vocal correspondente do agente
+10059=Falha ao excluir a impress\u00E3o vocal correspondente do agente
+10060=Envio muito frequente, tente novamente ap\u00F3s {0} segundos
+10061=C\u00F3digo de ativa\u00E7\u00E3o n\u00E3o pode ser vazio
+10062=C\u00F3digo de ativa\u00E7\u00E3o incorreto
+10063=Dispositivo j\u00E1 ativado
+10064=Este modelo 锟� o modelo padr\u00E3o, configure primeiro outro modelo como padr\u00E3o
+10065=Falha ao adicionar dados
+10066=Falha ao modificar dados
+10067=C\u00F3digo de verifica\u00E7\u00E3o gr\u00E1fico incorreto
+10068=Registro por telefone n\u00E3o habilitado, fun\u00E7\u00E3o de c\u00F3digo de verifica\u00E7\u00E3o por SMS indispon\u00EDvel
+10069=Nome de usu\u00E1rio n\u00E3o 锟� n\u00FAmero de telefone, por favor insira novamente
+10070=Este n\u00FAmero de telefone j\u00E1 est\u00E1 registrado
+10071=N\u00FAmero de telefone inserido n\u00E3o est\u00E1 registrado
+10072=Registro de usu\u00E1rio comum n\u00E3o permitido atualmente
+10073=Registro por telefone n\u00E3o habilitado, fun\u00E7\u00E3o de recupera\u00E7\u00E3o de senha indispon\u00EDvel
+10074=Formato de n\u00FAmero de telefone incorreto
+10075=C\u00F3digo de verifica\u00E7\u00E3o do telefone incorreto
+10076=Tipo de dicion\u00E1rio n\u00E3o existe
+10077=C\u00F3digo de tipo de dicion\u00E1rio duplicado
+10078=Falha ao carregar recurso
+10079=Par\u00E2metros de sele\u00E7\u00E3o incompat\u00EDveis entre LLM e reconhecimento de inten\u00E7\u00E3o Intent
+10080=A pessoa ({0}) correspondente a esta impress\u00E3o vocal j\u00E1 est\u00E1 registrada, escolha outra voz para registrar
+10081=Erro ao excluir impress\u00E3o vocal
+10082=Esta modifica\u00E7\u00E3o n\u00E3o 锟� permitida, esta voz j\u00E1 est\u00E1 registrada como impress\u00E3o vocal ({0})
+10083=Erro ao modificar impress\u00E3o vocal, entre em contato com o administrador
+10084=Erro no endere\u00E7o da interface de impress\u00E3o vocal, acesse o gerenciamento de par\u00E2metros para modificar o endere\u00E7o
+10085=Dados de \u00E1udio n\u00E3o pertencem a este agente
+10086=Dados de \u00E1udio est\u00E3o vazios, verifique os dados enviados
+10087=Falha ao salvar impress\u00E3o vocal, falha na requisi\u00E7\u00E3o
+10088=Falha ao salvar impress\u00E3o vocal, falha no processamento
+10089=Falha ao cancelar impress\u00E3o vocal, falha na requisi\u00E7\u00E3o
+10090=Falha ao cancelar impress\u00E3o vocal, falha no processamento
+10091=Provedor n\u00E3o existe
+10092=LLM configurado n\u00E3o existe
+10093=Esta configura\u00E7\u00E3o de modelo foi referenciada pelo agente {0}, n\u00E3o pode ser exclu\u00EDda
+10094=Este modelo LLM foi referenciado pela configura\u00E7\u00E3o de reconhecimento de inten\u00E7\u00E3o, n\u00E3o pode ser exclu\u00EDdo
+10095=Opera\u00E7\u00E3o de servidor inv\u00E1lida
+10096=Endere\u00E7o WebSocket do servidor n\u00E3o configurado
+10097=Endere\u00E7o WebSocket de destino n\u00E3o existe
+10098=Lista de endere\u00E7os WebSocket n\u00E3o pode ser vazia
+10099=Endere\u00E7o WebSocket n\u00E3o pode usar localhost ou 127.0.0.1
+10100=Formato de endere\u00E7o WebSocket incorreto
+10101=Falha no teste de conex\u00E3o WebSocket
+10102=Endere\u00E7o OTA n\u00E3o pode ser vazio
+10103=Endere\u00E7o OTA n\u00E3o pode usar localhost ou 127.0.0.1
+10104=Endere\u00E7o OTA deve come\u00E7ar com http ou https
+10105=Endere\u00E7o OTA deve terminar com /ota/
+10106=Falha ao acessar interface OTA
+10107=Formato de conte\u00FAdo retornado pela interface OTA incorreto
+10108=Falha na valida\u00E7\u00E3o da interface OTA
+10109=Endere\u00E7o MCP n\u00E3o pode ser vazio
+10110=Endere\u00E7o MCP n\u00E3o pode usar localhost ou 127.0.0.1
+10111=Endere\u00E7o MCP incorreto
+10112=Falha ao acessar interface MCP
+10113=Formato de conte\u00FAdo retornado pela interface MCP incorreto
+10114=Falha na valida\u00E7\u00E3o da interface MCP
+10115=Endere\u00E7o da interface de impress\u00E3o vocal n\u00E3o pode ser vazio
+10116=Endere\u00E7o da interface de impress\u00E3o vocal n\u00E3o pode usar localhost ou 127.0.0.1
+10117=Endere\u00E7o da interface de impress\u00E3o vocal incorreto
+10118=Endere\u00E7o da interface de impress\u00E3o vocal deve come\u00E7ar com http ou https
+10119=Falha ao acessar interface de impress\u00E3o vocal
+10120=Formato de conte\u00FAdo retornado pela interface de impress\u00E3o vocal incorreto
+10121=Falha na valida\u00E7\u00E3o da interface de impress\u00E3o vocal
+10122=Chave mqtt n\u00E3o pode ser vazia
+10123=Comprimento da chave mqtt inseguro, deve ter pelo menos 8 caracteres e conter letras mai\u00FAsculas e min\u00FAsculas
+10124=Comprimento da chave mqtt inseguro, deve conter letras mai\u00FAsculas e min\u00FAsculas
+10125=Sua chave mqtt cont\u00E9m senha fraca
+10128=Etiqueta de dicion\u00E1rio duplicada
+10129=Chave SM2 n\u00E3o configurada
+10130=Falha na descriptografia SM2
+10131=modelType e provideCode n\u00E3o podem ser vazios
+10132=Sem permiss\u00E3o para visualizar registros de chat deste agente
+10133=ID da sess\u00E3o n\u00E3o pode ser vazio
+10134=ID do agente n\u00E3o pode ser vazio
+10135=Falha ao baixar registros de chat
+10136=Link de download expirado ou inv\u00E1lido
+10137=Link de download inv\u00E1lido
+10138=Usu\u00E1rio
+10139=Agente
+10140=Arquivo de \u00E1udio n\u00E3o pode ser vazio
+10141=Apenas arquivos de \u00E1udio s\u00E3o suportados
+10142=Tamanho do arquivo de \u00E1udio n\u00E3o pode exceder 10MB
+10143=Falha no upload
+10144=Registro de clonagem de voz n\u00E3o existe
+10145=Informa\u00E7\u00F5es de recurso de tom de voz n\u00E3o podem ser vazias
+10146=Nome da plataforma n\u00E3o pode ser vazio
+10147=ID do tom de voz n\u00E3o pode ser vazio
+10148=N\u00FAmero da conta atribu\u00EDda n\u00E3o pode ser vazio
+10149=IDs de recursos de tom de voz a serem exclu\u00EDdos n\u00E3o podem ser vazios
+10150=Voc\u00EA n\u00E3o tem permiss\u00E3o para operar este registro
+10151=Por favor, fa\u00E7a upload do arquivo de \u00E1udio primeiro
+10152=Configura\u00E7\u00E3o do modelo n\u00E3o encontrada
+10153=Tipo de modelo n\u00E3o encontrado
+10154=Falha no treinamento: {0}
+10155=Configura\u00E7\u00E3o ausente do mecanismo Huoshan
+10156=Erro de formato de resposta, campo BaseResp ausente
+10157=Falha na requisi\u00E7\u00E3o
+10158=Clonagem de tom de voz:
+10159=ID do tom de voz j\u00E1 existe
+10160=Formato de ID do tom de voz do mecanismo Huoshan incorreto, deve come\u00E7ar com S_
+10161=Endere\u00E7o Mac j\u00E1 existe
+10162=Provedor de modelos n\u00E3o existe
+10163=Registro da base de conhecimento n\u00E3o existe
+10164=Configura\u00E7\u00E3o RAG n\u00E3o encontrada
+10165=Erro de tipo de configura\u00E7\u00E3o RAG
+10166=Configura\u00E7\u00E3o RAG padr\u00E3o n\u00E3o encontrada
+10167=Falha na chamada RAG, {0}
+10168=Falha no upload do arquivo
+10169=Voc\u00EA n\u00E3o tem permiss\u00E3o para operar este registro
+10170=Nome da base de conhecimento duplicado
+10171=base_url da configura\u00E7\u00E3o RAG n\u00E3o pode ser vazio
+10172=api_key da configura\u00E7\u00E3o RAG n\u00E3o pode ser vazio
+10173=api_key da configura\u00E7\u00E3o RAG n\u00E3o pode ser vazio, substitua pelo par\u00E2metro API obtido
+10174=Formato de base_url da configura\u00E7\u00E3o RAG incorreto, deve come\u00E7ar com http ou https
+10175=Endere\u00E7o mac n\u00E3o pode ser vazio
+10176=dataset_id da configura\u00E7\u00E3o RAG n\u00E3o pode ser vazio
+10177=model_id da configura\u00E7\u00E3o RAG n\u00E3o pode ser vazio
+10178=dataset_id e model_id da configura\u00E7\u00E3o RAG n\u00E3o podem ser vazios
+10179=Nome do arquivo n\u00E3o pode ser vazio
+10180=Conte\u00FAdo do arquivo n\u00E3o pode ser vazio
+10181=Nome da clonagem de tom de voz n\u00E3o pode ser vazio
+10182=\u00C1udio de clonagem de tom de voz n\u00E3o existe
+10183=Agente padr\u00E3o n\u00E3o encontrado
+10184=Tipo de adaptador n\u00E3o suportado
+10185=Falha na valida\u00E7\u00E3o da configura\u00E7\u00E3o RAG
+10186=Falha ao criar adaptador
+10187=Falha ao inicializar adaptador
+10188=Falha no teste de conex\u00E3o do adaptador
+10189=Falha na opera\u00E7\u00E3o do adaptador
+10190=Adaptador n\u00E3o encontrado
+10191=Erro de cache do adaptador
+10192=Tipo de adaptador n\u00E3o encontrado
+10193=ID do dispositivo n\u00E3o pode ser vazio
+10194=Dispositivo n\u00E3o existe ou est\u00E1 offline
+10195=Limite de uploads OTA excedido
+10196=Nome da etiqueta j\u00E1 existe
+10197=Nome da etiqueta n\u00E3o pode ser vazio
+10198=Etiqueta n\u00E3o existe
+10199=Esta opera\u00E7\u00E3o n\u00E3o 锟� suportada na an\u00E1lise de arquivos chineses
+10200=Sem permiss\u00e3o para visualizar o endere\u00e7o do endpoint MCP deste agente
+10201=Por favor, contate o administrador para configurar o endere\u00e7o do endpoint MCP no gerenciamento de par\u00e2metros
+10202=Sem permiss\u00e3o para visualizar a lista de ferramentas MCP deste agente
+10203=Nome do arquivo j\u00E1 existe
+10204=O tamanho do arquivo excede o limite de 1MB
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_vi_VN.properties b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_vi_VN.properties
new file mode 100644
index 0000000..228b92b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_vi_VN.properties
@@ -0,0 +1,213 @@
+#Ti\u1EBFng Vi\u1EC7t
+500=Ngo\u1EA1i l\u1EC7 n\u1ED9i b\u1ED9 m\u00E1y ch\u1EE7
+401=Kh\u00F4ng \u0111\u01B0\u1EE3c \u1EE7y quy\u1EC1n
+403=Truy c\u1EADp b\u1ECB t\u1EEB ch\u1ED1i, kh\u00F4ng c\u00F3 quy\u1EC1n
+10001={0} kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10002=B\u1EA3n ghi \u0111\u00E3 t\u1ED3n t\u1EA1i trong c\u01A1 s\u1EDF d\u1EEF li\u1EC7u
+10003=Kh\u00F4ng th\u1EC3 l\u1EA5y tham s\u1ED1
+10004=S\u1ED1 t\u00E0i kho\u1EA3n ho\u1EB7c m\u1EADt kh\u1EA9u kh\u00F4ng ch\u00EDnh x\u00E1c
+10005=T\u00E0i kho\u1EA3n \u0111\u00E3 b\u1ECB v\u00F4 hi\u1EC7u h\u00F3a
+10006=ID duy nh\u1EA5t kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10007=M\u00E3 x\u00E1c minh kh\u00F4ng ch\u00EDnh x\u00E1c
+10008=Tr\u01B0\u1EDBc ti\u00EAn h\u00E3y x\u00F3a menu con ho\u1EB7c n\u00FAt
+10009=M\u1EADt kh\u1EA9u g\u1ED1c kh\u00F4ng ch\u00EDnh x\u00E1c
+10010=T\u00E0i kho\u1EA3n ho\u1EB7c m\u1EADt kh\u1EA9u kh\u00F4ng ch\u00EDnh x\u00E1c, b\u1EA1n c\u00F3 th\u1EC3 th\u1EED th\u00EAm {0} l\u1EA7n n\u1EEFa
+
+10011=Ph\u00F2ng ban c\u1EA5p tr\u00EAn \u0111\u00E3 l\u1EF1a ch\u1ECDn sai
+10012=Menu tr\u00EAn kh\u00F4ng th\u1EC3 l\u00E0 ch\u00EDnh n\u00F3
+10013=Giao di\u1EC7n quy\u1EC1n d\u1EEF li\u1EC7u, ch\u1EC9 c\u00F3 th\u1EC3 l\u00E0 tham s\u1ED1 ki\u1EC3u Map
+10014=Vui l\u00F2ng x\u00F3a ph\u00F2ng ban c\u1EA5p d\u01B0\u1EDBi tr\u01B0\u1EDBc
+10015=Vui l\u00F2ng x\u00F3a ng\u01B0\u1EDDi d\u00F9ng thu\u1ED9c ph\u00F2ng ban tr\u01B0\u1EDBc
+10016=Tri\u1EC3n khai th\u1EA5t b\u1EA1i, kh\u00F4ng c\u00F3 quy tr\u00ECnh
+10017=Bi\u1EC3u \u0111\u1ED3 m\u00F4 h\u00ECnh kh\u00F4ng ch\u00EDnh x\u00E1c, vui l\u00F2ng ki\u1EC3m tra
+10018=Xu\u1EA5t th\u1EA5t b\u1EA1i, ID m\u00F4 h\u00ECnh l\u00E0 {0}
+
+10019=Vui l\u00F2ng t\u1EA3i l\u00EAn t\u1EC7p
+10020=Token kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10021=Token kh\u00F4ng h\u1EE3p l\u1EC7, vui l\u00F2ng \u0111\u0103ng nh\u1EADp l\u1EA1i
+10022=T\u00E0i kho\u1EA3n \u0111\u00E3 b\u1ECB kh\u00F3a
+10023=Vui l\u00F2ng t\u1EA3i l\u00EAn t\u1EC7p \u1EDF \u0111\u1ECBnh d\u1EA1ng zip, bar, bpmn, bpmn20.xml
+
+10024=T\u1EA3i l\u00EAn t\u1EC7p {0} th\u1EA5t b\u1EA1i
+10025=G\u1EEDi SMS {0} th\u1EA5t b\u1EA1i
+10026=M\u1EABu email kh\u00F4ng t\u1ED3n t\u1EA1i
+
+10027=Ngo\u1EA1i l\u1EC7 d\u1ECBch v\u1EE5 Redis
+10028=T\u00E1c v\u1EE5 h\u1EB9n gi\u1EDD th\u1EA5t b\u1EA1i
+10029=Kh\u00F4ng th\u1EC3 ch\u1EE9a k\u00FD t\u1EF1 kh\u00F4ng h\u1EE3p l\u1EC7
+
+10030=M\u1EADt kh\u1EA9u \u00EDt h\u01A1n {0} ch\u1EEF s\u1ED1
+10031=M\u1EADt kh\u1EA9u ph\u1EA3i bao g\u1ED3m s\u1ED1, ch\u1EEF hoa, ch\u1EEF th\u01B0\u1EDDng v\u00E0 k\u00FD t\u1EF1 \u0111\u1EB7c bi\u1EC7t c\u00F9ng m\u1ED9t l\u00FAc
+10032=Ngo\u1EA1i l\u1EC7 khi x\u00F3a d\u1EEF li\u1EC7u n\u00E0y
+10033=L\u1ED7i m\u00E3 x\u00E1c minh thi\u1EBFt b\u1ECB
+
+10034=Gi\u00E1 tr\u1ECB tham s\u1ED1 kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10035=Lo\u1EA1i tham s\u1ED1 kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10036=Lo\u1EA1i tham s\u1ED1 kh\u00F4ng \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3
+10037=Gi\u00E1 tr\u1ECB tham s\u1ED1 ph\u1EA3i l\u00E0 s\u1ED1 h\u1EE3p l\u1EC7
+10038=Gi\u00E1 tr\u1ECB tham s\u1ED1 ph\u1EA3i l\u00E0 true ho\u1EB7c false
+10039=Gi\u00E1 tr\u1ECB tham s\u1ED1 ph\u1EA3i l\u00E0 \u0111\u1ECBnh d\u1EA1ng m\u1EA3ng JSON h\u1EE3p l\u1EC7
+10040=Gi\u00E1 tr\u1ECB tham s\u1ED1 ph\u1EA3i l\u00E0 \u0111\u1ECBnh d\u1EA1ng JSON h\u1EE3p l\u1EC7
+
+10041=Kh\u00F4ng t\u00ECm th\u1EA5y thi\u1EBFt b\u1ECB
+10042={0}
+10043=X\u00F3a d\u1EEF li\u1EC7u th\u1EA5t b\u1EA1i
+10044=Ng\u01B0\u1EDDi d\u00F9ng ch\u01B0a \u0111\u0103ng nh\u1EADp
+10045=K\u1EBFt n\u1ED1i WebSocket th\u1EA5t b\u1EA1i ho\u1EB7c h\u1EBFt th\u1EDDi gian ch\u1EDD
+10046=Kh\u00F4ng th\u1EC3 l\u01B0u d\u1EA5u gi\u1ECDng n\u00F3i, vui l\u00F2ng li\u00EAn h\u1EC7 qu\u1EA3n tr\u1ECB vi\u00EAn
+10047=\u0110\u1EA1t \u0111\u1EBFn gi\u1EDBi h\u1EA1n g\u1EEDi h\u00E0ng ng\u00E0y
+10048=M\u1EADt kh\u1EA9u c\u0169 kh\u00F4ng ch\u00EDnh x\u00E1c
+10049=LLM \u0111\u01B0\u1EE3c \u0111\u1EB7t kh\u00F4ng ph\u1EA3i l\u00E0 openai ho\u1EB7c ollama
+10050=Kh\u00F4ng th\u1EC3 t\u1EA1o token
+10051=Kh\u00F4ng t\u00ECm th\u1EA5y t\u00E0i nguy\u00EAn
+10052=Kh\u00F4ng t\u00ECm th\u1EA5y agent m\u1EB7c \u0111\u1ECBnh
+10053=Kh\u00F4ng t\u00ECm th\u1EA5y agent
+10054=Giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i ch\u01B0a \u0111\u01B0\u1EE3c c\u1EA5u h\u00ECnh, vui l\u00F2ng c\u1EA5u h\u00ECnh \u0111\u1ECBa ch\u1EC9 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i (server.voice_print) trong c\u1EA5u h\u00ECnh tham s\u1ED1 tr\u01B0\u1EDBc
+10055=G\u1EEDi SMS th\u1EA5t b\u1EA1i
+10056=Kh\u00F4ng th\u1EC3 thi\u1EBFt l\u1EADp k\u1EBFt n\u1ED1i SMS
+10057=Kh\u00F4ng th\u1EC3 t\u1EA1o d\u1EA5u gi\u1ECDng n\u00F3i cho agent
+10058=Kh\u00F4ng th\u1EC3 c\u1EADp nh\u1EADt d\u1EA5u gi\u1ECDng n\u00F3i cho agent
+10059=Kh\u00F4ng th\u1EC3 x\u00F3a d\u1EA5u gi\u1ECDng n\u00F3i cho agent
+10060=G\u1EEDi qu\u00E1 th\u01B0\u1EDDng xuy\u00EAn, vui l\u00F2ng th\u1EED l\u1EA1i sau {0} gi\u00E2y
+10061=M\u00E3 k\u00EDch ho\u1EA1t kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10062=L\u1ED7i m\u00E3 k\u00EDch ho\u1EA1t
+10063=Thi\u1EBFt b\u1ECB \u0111\u00E3 \u0111\u01B0\u1EE3c k\u00EDch ho\u1EA1t
+10064=M\u00F4 h\u00ECnh n\u00E0y l\u00E0 m\u00F4 h\u00ECnh m\u1EB7c \u0111\u1ECBnh, vui l\u00F2ng \u0111\u1EB7t m\u00F4 h\u00ECnh kh\u00E1c l\u00E0m m\u1EB7c \u0111\u1ECBnh tr\u01B0\u1EDBc
+10065=Th\u00EAm d\u1EEF li\u1EC7u th\u1EA5t b\u1EA1i
+10066=C\u1EADp nh\u1EADt d\u1EEF li\u1EC7u th\u1EA5t b\u1EA1i
+10067=L\u1ED7i m\u00E3 x\u00E1c minh \u0111\u1ED3 h\u1ECDa
+10068=\u0110\u0103ng k\u00FD di \u0111\u1ED9ng kh\u00F4ng \u0111\u01B0\u1EE3c b\u1EADt, kh\u00F4ng th\u1EC3 s\u1EED d\u1EE5ng ch\u1EE9c n\u0103ng m\u00E3 x\u00E1c minh SMS
+10069=T\u00EAn ng\u01B0\u1EDDi d\u00F9ng kh\u00F4ng ph\u1EA3i l\u00E0 s\u1ED1 \u0111i\u1EC7n tho\u1EA1i di \u0111\u1ED9ng, vui l\u00F2ng nh\u1EADp l\u1EA1i
+10070=S\u1ED1 \u0111i\u1EC7n tho\u1EA1i di \u0111\u1ED9ng n\u00E0y \u0111\u00E3 \u0111\u01B0\u1EE3c \u0111\u0103ng k\u00FD
+10071=S\u1ED1 \u0111i\u1EC7n tho\u1EA1i di \u0111\u1ED9ng \u0111\u00E3 nh\u1EADp kh\u00F4ng \u0111\u01B0\u1EE3c \u0111\u0103ng k\u00FD
+10072=Hi\u1EC7n kh\u00F4ng cho ph\u00E9p \u0111\u0103ng k\u00FD ng\u01B0\u1EDDi d\u00F9ng th\u00F4ng th\u01B0\u1EDDng
+10073=\u0110\u0103ng k\u00FD di \u0111\u1ED9ng kh\u00F4ng \u0111\u01B0\u1EE3c b\u1EADt, kh\u00F4ng th\u1EC3 s\u1EED d\u1EE5ng ch\u1EE9c n\u0103ng truy xu\u1EA5t m\u1EADt kh\u1EA9u
+10074=\u0110\u1ECBnh d\u1EA1ng s\u1ED1 \u0111i\u1EC7n tho\u1EA1i di \u0111\u1ED9ng \u0111\u00E3 nh\u1EADp kh\u00F4ng ch\u00EDnh x\u00E1c
+10075=M\u00E3 x\u00E1c minh SMS \u0111\u00E3 nh\u1EADp kh\u00F4ng ch\u00EDnh x\u00E1c
+10076=Lo\u1EA1i t\u1EEB \u0111i\u1EC3n kh\u00F4ng t\u1ED3n t\u1EA1i
+10077=M\u00E3 lo\u1EA1i t\u1EEB \u0111i\u1EC3n b\u1ECB tr\u00F9ng l\u1EB7p
+10078=\u0110\u1ECDc t\u00E0i nguy\u00EAn th\u1EA5t b\u1EA1i
+10079=M\u00F4 h\u00ECnh LLM v\u00E0 nh\u1EADn d\u1EA1ng \u00FD \u0111\u1ECBnh, l\u1EF1a ch\u1ECDn tham s\u1ED1 kh\u00F4ng kh\u1EDBp
+10080=D\u1EA5u gi\u1ECDng n\u00F3i n\u00E0y thu\u1ED9c v\u1EC1 {0} \u0111\u00E3 \u0111\u0103ng k\u00FD, vui l\u00F2ng ch\u1ECDn gi\u1ECDng n\u00F3i kh\u00E1c
+10081=\u0110\u00E3 x\u1EA3y ra l\u1ED7i khi x\u00F3a d\u1EA5u gi\u1ECDng n\u00F3i
+10082=Kh\u00F4ng cho ph\u00E9p s\u1EEDa \u0111\u1ED5i, gi\u1ECDng n\u00F3i n\u00E0y \u0111\u00E3 \u0111\u01B0\u1EE3c \u0111\u0103ng k\u00FD l\u00E0m d\u1EA5u gi\u1ECDng n\u00F3i ({0})
+10083=L\u1ED7i s\u1EEDa \u0111\u1ED5i d\u1EA5u gi\u1ECDng n\u00F3i, vui l\u00F2ng li\u00EAn h\u1EC7 qu\u1EA3n tr\u1ECB vi\u00EAn
+10084=L\u1ED7i \u0111\u1ECBa ch\u1EC9 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i, vui l\u00F2ng v\u00E0o qu\u1EA3n l\u00FD tham s\u1ED1 \u0111\u1EC3 s\u1EEDa \u0111\u1ED5i \u0111\u1ECBa ch\u1EC9 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i
+10085=D\u1EEF li\u1EC7u \u00E2m thanh kh\u00F4ng thu\u1ED9c v\u1EC1 agent n\u00E0y
+10086=D\u1EEF li\u1EC7u \u00E2m thanh tr\u1ED1ng, vui l\u00F2ng ki\u1EC3m tra d\u1EEF li\u1EC7u \u0111\u00E3 t\u1EA3i l\u00EAn
+10087=\u0110\u0103ng k\u00FD d\u1EA5u gi\u1ECDng n\u00F3i th\u1EA5t b\u1EA1i, y\u00EAu c\u1EA7u kh\u00F4ng th\u00E0nh c\u00F4ng
+10088=\u0110\u0103ng k\u00FD d\u1EA5u gi\u1ECDng n\u00F3i th\u1EA5t b\u1EA1i, x\u1EED l\u00FD y\u00EAu c\u1EA7u th\u1EA5t b\u1EA1i
+10089=H\u1EE7y d\u1EA5u gi\u1ECDng n\u00F3i th\u1EA5t b\u1EA1i, y\u00EAu c\u1EA7u kh\u00F4ng th\u00E0nh c\u00F4ng
+10090=H\u1EE7y d\u1EA5u gi\u1ECDng n\u00F3i th\u1EA5t b\u1EA1i, x\u1EED l\u00FD y\u00EAu c\u1EA7u th\u1EA5t b\u1EA1i
+10091=Nh\u00E0 cung c\u1EA5p m\u00F4 h\u00ECnh kh\u00F4ng t\u1ED3n t\u1EA1i
+10092=LLM \u0111\u01B0\u1EE3c c\u1EA5u h\u00ECnh kh\u00F4ng t\u1ED3n t\u1EA1i
+10093=C\u1EA5u h\u00ECnh m\u00F4 h\u00ECnh n\u00E0y \u0111\u01B0\u1EE3c tham chi\u1EBFu b\u1EDFi agent {0} v\u00E0 kh\u00F4ng th\u1EC3 x\u00F3a
+10094=M\u00F4 h\u00ECnh LLM n\u00E0y \u0111\u01B0\u1EE3c tham chi\u1EBFu b\u1EDFi c\u1EA5u h\u00ECnh nh\u1EADn d\u1EA1ng \u00FD \u0111\u1ECBnh v\u00E0 kh\u00F4ng th\u1EC3 x\u00F3a
+10095=Thao t\u00E1c m\u00E1y ch\u1EE7 kh\u00F4ng h\u1EE3p l\u1EC7
+10096=\u0110\u1ECBa ch\u1EC9 WebSocket m\u00E1y ch\u1EE7 ch\u01B0a \u0111\u01B0\u1EE3c c\u1EA5u h\u00ECnh
+10097=\u0110\u1ECBa ch\u1EC9 WebSocket m\u1EE5c ti\u00EAu kh\u00F4ng t\u1ED3n t\u1EA1i
+10098=Danh s\u00E1ch \u0111\u1ECBa ch\u1EC9 WebSocket kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10099=\u0110\u1ECBa ch\u1EC9 WebSocket kh\u00F4ng th\u1EC3 s\u1EED d\u1EE5ng localhost ho\u1EB7c 127.0.0.1
+10100=\u0110\u1ECBnh d\u1EA1ng \u0111\u1ECBa ch\u1EC9 WebSocket kh\u00F4ng ch\u00EDnh x\u00E1c
+10101=Ki\u1EC3m tra k\u1EBFt n\u1ED1i WebSocket th\u1EA5t b\u1EA1i
+10102=\u0110\u1ECBa ch\u1EC9 OTA kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10103=\u0110\u1ECBa ch\u1EC9 OTA kh\u00F4ng th\u1EC3 s\u1EED d\u1EE5ng localhost ho\u1EB7c 127.0.0.1
+10104=\u0110\u1ECBa ch\u1EC9 OTA ph\u1EA3i b\u1EAFt \u0111\u1EA7u b\u1EB1ng http ho\u1EB7c https
+10105=\u0110\u1ECBa ch\u1EC9 OTA ph\u1EA3i k\u1EBFt th\u00FAc b\u1EB1ng /ota/
+10106=Truy c\u1EADp giao di\u1EC7n OTA th\u1EA5t b\u1EA1i
+10107=\u0110\u1ECBnh d\u1EA1ng n\u1ED9i dung tr\u1EA3 v\u1EC1 giao di\u1EC7n OTA kh\u00F4ng ch\u00EDnh x\u00E1c
+10108=X\u00E1c th\u1EF1c giao di\u1EC7n OTA th\u1EA5t b\u1EA1i
+10109=\u0110\u1ECBa ch\u1EC9 MCP kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10110=\u0110\u1ECBa ch\u1EC9 MCP kh\u00F4ng th\u1EC3 s\u1EED d\u1EE5ng localhost ho\u1EB7c 127.0.0.1
+10111=Kh\u00F4ng ph\u1EA3i \u0111\u1ECBa ch\u1EC9 MCP h\u1EE3p l\u1EC7
+10112=Truy c\u1EADp giao di\u1EC7n MCP th\u1EA5t b\u1EA1i
+10113=\u0110\u1ECBnh d\u1EA1ng n\u1ED9i dung tr\u1EA3 v\u1EC1 giao di\u1EC7n MCP kh\u00F4ng ch\u00EDnh x\u00E1c
+10114=X\u00E1c th\u1EF1c giao di\u1EC7n MCP th\u1EA5t b\u1EA1i
+10115=\u0110\u1ECBa ch\u1EC9 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10116=\u0110\u1ECBa ch\u1EC9 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i kh\u00F4ng th\u1EC3 s\u1EED d\u1EE5ng localhost ho\u1EB7c 127.0.0.1
+10117=Kh\u00F4ng ph\u1EA3i \u0111\u1ECBa ch\u1EC9 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i h\u1EE3p l\u1EC7
+10118=\u0110\u1ECBa ch\u1EC9 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i ph\u1EA3i b\u1EAFt \u0111\u1EA7u b\u1EB1ng http ho\u1EB7c https
+10119=Truy c\u1EADp giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i th\u1EA5t b\u1EA1i
+10120=\u0110\u1ECBnh d\u1EA1ng n\u1ED9i dung tr\u1EA3 v\u1EC1 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i kh\u00F4ng ch\u00EDnh x\u00E1c
+10121=X\u00E1c th\u1EF1c giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i th\u1EA5t b\u1EA1i
+10122=B\u00ED m\u1EADt MQTT kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10123=B\u00ED m\u1EADt MQTT c\u1EE7a b\u1EA1n kh\u00F4ng an to\u00E0n, c\u1EA7n \u00EDt nh\u1EA5t 8 k\u00FD t\u1EF1 v\u00E0 ph\u1EA3i ch\u1EE9a c\u1EA3 ch\u1EEF hoa v\u00E0 ch\u1EEF th\u01B0\u1EDDng
+10124=B\u00ED m\u1EADt MQTT c\u1EE7a b\u1EA1n kh\u00F4ng an to\u00E0n, b\u00ED m\u1EADt MQTT ph\u1EA3i ch\u1EE9a c\u1EA3 ch\u1EEF hoa v\u00E0 ch\u1EEF th\u01B0\u1EDDng
+10125=B\u00ED m\u1EADt MQTT c\u1EE7a b\u1EA1n ch\u1EE9a m\u1EADt kh\u1EA9u y\u1EBFu
+10128=Nh\u00E3n t\u1EEB \u0111i\u1EC3n b\u1ECB tr\u00F9ng l\u1EB7p
+10129=Kh\u00F3a SM2 ch\u01B0a \u0111\u01B0\u1EE3c c\u1EA5u h\u00ECnh
+10130=Gi\u1EA3i m\u00E3 SM2 th\u1EA5t b\u1EA1i
+10131=modelType v\u00E0 provideCode kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10132=Kh\u00F4ng c\u00F3 quy\u1EC1n xem l\u1ECBch s\u1EED tr\u00F2 chuy\u1EC7n c\u1EE7a agent n\u00E0y
+10133=ID phi\u00EAn kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10134=ID agent kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10135=T\u1EA3i xu\u1ED1ng l\u1ECBch s\u1EED tr\u00F2 chuy\u1EC7n th\u1EA5t b\u1EA1i
+10136=Li\u00EAn k\u1EBFt t\u1EA3i xu\u1ED1ng \u0111\u00E3 h\u1EBFt h\u1EA1n ho\u1EB7c kh\u00F4ng h\u1EE3p l\u1EC7
+10137=Li\u00EAn k\u1EBFt t\u1EA3i xu\u1ED1ng kh\u00F4ng h\u1EE3p l\u1EC7
+10138=Ng\u01B0\u1EDDi d\u00F9ng
+10139=Agent
+10140=T\u1EC7p \u00E2m thanh kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10141=Ch\u1EC9 h\u1ED7 tr\u1EE3 t\u1EC7p \u00E2m thanh
+10142=K\u00EDch th\u01B0\u1EDBc t\u1EC7p \u00E2m thanh kh\u00F4ng th\u1EC3 v\u01B0\u1EE3t qu\u00E1 10MB
+10143=T\u1EA3i l\u00EAn th\u1EA5t b\u1EA1i
+10144=B\u1EA3n ghi nh\u00E2n b\u1EA3n gi\u1ECDng n\u00F3i kh\u00F4ng t\u1ED3n t\u1EA1i
+10145=Th\u00F4ng tin t\u00E0i nguy\u00EAn gi\u1ECDng n\u00F3i kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10146=T\u00EAn n\u1EC1n t\u1EA3ng TTS kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10147=ID gi\u1ECDng n\u00F3i kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10148=T\u00E0i kho\u1EA3n ch\u1EE7 s\u1EDF h\u1EEFu kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10149=ID t\u00E0i nguy\u00EAn gi\u1ECDng n\u00F3i c\u1EA7n x\u00F3a kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10150=B\u1EA1n kh\u00F4ng c\u00F3 quy\u1EC1n thao t\u00E1c b\u1EA3n ghi n\u00E0y
+10151=Vui l\u00F2ng t\u1EA3i l\u00EAn t\u1EC7p \u00E2m thanh tr\u01B0\u1EDBc
+10152=Kh\u00F4ng t\u00ECm th\u1EA5y c\u1EA5u h\u00ECnh m\u00F4 h\u00ECnh
+10153=Kh\u00F4ng t\u00ECm th\u1EA5y lo\u1EA1i m\u00F4 h\u00ECnh
+10154=\u0110\u00E0o t\u1EA1o th\u1EA5t b\u1EA1i: {0}
+10155=Thi\u1EBFu c\u1EA5u h\u00ECnh Huoshan Engine
+10156=L\u1ED7i \u0111\u1ECBnh d\u1EA1ng ph\u1EA3n h\u1ED3i, thi\u1EBFu tr\u01B0\u1EDDng BaseResp
+10157=Y\u00EAu c\u1EA7u th\u1EA5t b\u1EA1i
+10158=Nh\u00E2n b\u1EA3n \u00E2m s\u1EAFc:
+10159=ID gi\u1ECDng n\u00F3i \u0111\u00E3 t\u1ED3n t\u1EA1i
+10160=L\u1ED7i \u0111\u1ECBnh d\u1EA1ng ID gi\u1ECDng n\u00F3i Huoshan Engine, ph\u1EA3i b\u1EAFt \u0111\u1EA7u b\u1EB1ng S_
+10161=\u0110\u1ECBa ch\u1EC9 Mac \u0111\u00E3 t\u1ED3n t\u1EA1i
+10162=Nh\u00E0 cung c\u1EA5p m\u00F4 h\u00ECnh kh\u00F4ng t\u1ED3n t\u1EA1i
+10163=B\u1EA3n ghi c\u01A1 s\u1EDF ki\u1EBFn th\u1EE9c kh\u00F4ng t\u1ED3n t\u1EA1i
+10164=Kh\u00F4ng t\u00ECm th\u1EA5y c\u1EA5u h\u00ECnh RAG
+10165=L\u1ED7i lo\u1EA1i c\u1EA5u h\u00ECnh RAG
+10166=Kh\u00F4ng t\u00ECm th\u1EA5y c\u1EA5u h\u00ECnh RAG m\u1EB7c \u0111\u1ECBnh
+10167=G\u1ECDi API RAG th\u1EA5t b\u1EA1i: {0}
+10168=T\u1EA3i l\u00EAn t\u1EC7p th\u1EA5t b\u1EA1i
+10169=Kh\u00F4ng c\u00F3 quy\u1EC1n thao t\u00E1c c\u01A1 s\u1EDF ki\u1EBFn th\u1EE9c n\u00E0y
+10170=T\u00EAn c\u01A1 s\u1EDF ki\u1EBFn th\u1EE9c \u0111\u00E3 t\u1ED3n t\u1EA1i
+10171=C\u1EA5u h\u00ECnh RAG base_url kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10172=C\u1EA5u h\u00ECnh RAG api_key kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10173=C\u1EA5u h\u00ECnh RAG api_key kh\u00F4ng th\u1EC3 ch\u1EE9a tr\u00ECnh gi\u1EEF ch\u1ED7, vui l\u00F2ng thay th\u1EBF b\u1EB1ng kh\u00F3a API th\u1EF1c t\u1EBF
+10174=L\u1ED7i \u0111\u1ECBnh d\u1EA1ng c\u1EA5u h\u00ECnh RAG base_url, ph\u1EA3i b\u1EAFt \u0111\u1EA7u b\u1EB1ng http ho\u1EB7c https
+10175=\u0110\u1ECBa ch\u1EC9 Mac kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10176=C\u1EA5u h\u00ECnh RAG dataset_id kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10177=C\u1EA5u h\u00ECnh RAG model_id kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10178=C\u1EA5u h\u00ECnh RAG dataset_id v\u00E0 model_id kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10179=T\u00EAn t\u1EC7p kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10180=N\u1ED9i dung t\u1EC7p kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10181=T\u00EAn nh\u00E2n b\u1EA3n gi\u1ECDng n\u00F3i kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10182=Kh\u00F4ng t\u00ECm th\u1EA5y \u00E2m thanh nh\u00E2n b\u1EA3n gi\u1ECDng n\u00F3i
+10183=Kh\u00F4ng t\u00ECm th\u1EA5y m\u1EABu agent m\u1EB7c \u0111\u1ECBnh
+10184=Lo\u1EA1i b\u1ED9 chuy\u1EC3n \u0111\u1ED5i kh\u00F4ng \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3
+10185=Ki\u1EC3m tra c\u1EA5u h\u00ECnh RAG th\u1EA5t b\u1EA1i
+10186=T\u1EA1o b\u1ED9 chuy\u1EC3n \u0111\u1ED5i th\u1EA5t b\u1EA1i
+10187=Kh\u1EDFi t\u1EA1o b\u1ED9 chuy\u1EC3n \u0111\u1ED5i th\u1EA5t b\u1EA1i
+10188=Ki\u1EC3m tra k\u1EBFt n\u1ED1i b\u1ED9 chuy\u1EC3n \u0111\u1ED5i th\u1EA5t b\u1EA1i
+10189=Thao t\u00E1c b\u1ED9 chuy\u1EC3n \u0111\u1ED5i th\u1EA5t b\u1EA1i
+10190=Kh\u00F4ng t\u00ECm th\u1EA5y b\u1ED9 chuy\u1EC3n \u0111\u1ED5i
+10191=L\u1ED7i b\u1ED9 nh\u1EDB \u0111\u1EC7m b\u1ED9 chuy\u1EC3n \u0111\u1ED5i
+10192=Kh\u00F4ng t\u00ECm th\u1EA5y lo\u1EA1i b\u1ED9 chuy\u1EC3n \u0111\u1ED5i
+10193=ID thi\u1EBFt b\u1ECB kh\u00F4ng th\u1EC3 tr\u1ED1ng
+10194=Kh\u00F4ng t\u00ECm th\u1EA5y thi\u1EBFt b\u1ECB ho\u1EB7c thi\u1EBFt b\u1ECB kh\u00F4ng tr\u1EF1c tuy\u1EBFn
+10195=S\u1ED1 l\u1EA7n t\u1EA3i l\u00EAn OTA v\u01B0\u1EE3t qu\u00E1 gi\u1EDBi h\u1EA1n
+10196=T\u00EAn th\u1EB9\uFFFD \u0111\u00E3 t\u1ED3n t\u1EA1i
+10197=T\u00EAn th\u1EB9\uFFFD kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
+10198=Kh\u00F4ng t\u00ECm th\u1EA5y th\u1EB9\uFFFD
+10199=T\u1EC7p \u0111ang \u0111\u01B0\u1EE3c ph\u00E2n t\u00EDch, thao t\u00E1c n\u00E0y kh\u00F4ng \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3
+10200=Kh\u00f4ng c\u00f3 quy\u1ec1n xem \u0111\u1ecba ch\u1ec9 \u0111i\u1ec3m cu\u1ed1i MCP c\u1ee7a \u0111\u1ea1i l\u00fd n\u00e0y
+10201=Vui l\u00f2ng li\u00ean h\u1ec7 qu\u1ea3n tr\u1ecb vi\u00ean \u0111\u1ec3 c\u1ea5u h\u00ecnh \u0111\u1ecba ch\u1ec9 \u0111i\u1ec3m cu\u1ed1i MCP trong qu\u1ea3n l\u00fd tham s\u1ed1
+10202=Kh\u00f4ng c\u00f3 quy\u1ec1n xem danh s\u00e1ch c\u00f4ng c\u1ee5 MCP c\u1ee7a \u0111\u1ea1i l\u00fd n\u00e0y
+10203=T\u00EAn t\u1EC7p \u0111\u00E3 t\u1ED3n t\u1EA1i
+10204=K\u00EDch th\u01b0\u1edbc t\u1ec7p v\u01b0\u1ee3t qu\u00e1 1MB
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_zh_CN.properties b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_zh_CN.properties
new file mode 100644
index 0000000..860bd12
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_zh_CN.properties
@@ -0,0 +1,213 @@
+#\u7B80\u4F53\u4E2D\u6587
+500=\u670D\u52A1\u5668\u5185\u90E8\u5F02\u5E38
+401=\u672A\u6388\u6743
+403=\u62D2\u7EDD\u8BBF\u95EE\uFF0C\u6CA1\u6709\u6743\u9650
+
+10001={0}\u4E0D\u80FD\u4E3A\u7A7A
+10002=\u6570\u636E\u5E93\u4E2D\u5DF2\u5B58\u5728\u8BE5\u8BB0\u5F55
+10003=\u83B7\u53D6\u53C2\u6570\u5931\u8D25
+10004=\u8D26\u53F7\u6216\u5BC6\u7801\u9519\u8BEF
+10005=\u8D26\u53F7\u5DF2\u88AB\u505C\u7528
+10006=\u552F\u4E00\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A
+10007=\u9A8C\u8BC1\u7801\u4E0D\u6B63\u786E
+10008=\u5148\u5220\u9664\u5B50\u83DC\u5355\u6216\u6309\u94AE
+10009=\u539F\u5BC6\u7801\u4E0D\u6B63\u786E
+10010=\u8D26\u53F7\u6216\u5BC6\u7801\u4E0D\u6B63\u786E,\u60A8\u8FD8\u6709\u53EF\u4EE5\u5C1D\u8BD5{0}\u6B21
+
+10011=\u4E0A\u7EA7\u90E8\u95E8\u9009\u62E9\u9519\u8BEF
+10012=\u4E0A\u7EA7\u83DC\u5355\u4E0D\u80FD\u4E3A\u81EA\u8EAB
+10013=\u6570\u636E\u6743\u9650\u63A5\u53E3\uFF0C\u53EA\u80FD\u662FMap\u7C7B\u578B\u53C2\u6570
+10014=\u8BF7\u5148\u5220\u9664\u4E0B\u7EA7\u90E8\u95E8
+10015=\u8BF7\u5148\u5220\u9664\u90E8\u95E8\u4E0B\u7684\u7528\u6237
+10016=\u90E8\u7F72\u5931\u8D25\uFF0C\u6CA1\u6709\u6D41\u7A0B
+10017=\u6A21\u578B\u56FE\u4E0D\u6B63\u786E\uFF0C\u8BF7\u68C0\u67E5
+10018=\u5BFC\u51FA\u5931\u8D25\uFF0C\u6A21\u578BID\u4E3A{0}
+
+10019=\u8BF7\u4E0A\u4F20\u6587\u4EF6
+10020=token\u4E0D\u80FD\u4E3A\u7A7A
+10021=token\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55
+10022=\u8D26\u53F7\u5DF2\u88AB\u9501\u5B9A
+10023=\u8BF7\u4E0A\u4F20zip\u3001bar\u3001bpmn\u3001bpmn20.xml\u683C\u5F0F\u6587\u4EF6
+
+10024=\u4E0A\u4F20\u6587\u4EF6\u5931\u8D25{0}
+10025=\u53D1\u9001\u77ED\u4FE1\u5931\u8D25{0}
+10026=\u90AE\u4EF6\u6A21\u677F\u4E0D\u5B58\u5728
+
+10027=Redis\u670D\u52A1\u5F02\u5E38
+10028=\u5B9A\u65F6\u4EFB\u52A1\u5931\u8D25
+10029=\u4E0D\u80FD\u5305\u542B\u975E\u6CD5\u5B57\u7B26
+10030=\u5BC6\u7801\u957F\u5EA6\u4E0D\u8DB3{0}\u4F4D
+10031=\u5BC6\u7801\u5FC5\u987B\u540C\u65F6\u5305\u542B\u6570\u5B57\u3001\u5927\u5C0F\u5199\u5B57\u6BCD\u548C\u7279\u6B8A\u5B57\u7B26\u7EC4\u6210
+10032=\u5220\u9664\u672C\u6570\u636E\u5F02\u5E38
+10033=\u8BBE\u5907\u9A8C\u8BC1\u7801\u9519\u8BEF
+
+10034=\u53C2\u6570\u503C\u4E0D\u80FD\u4E3A\u7A7A
+10035=\u53C2\u6570\u7C7B\u578B\u4E0D\u80FD\u4E3A\u7A7A
+10036=\u4E0D\u652F\u6301\u7684\u53C2\u6570\u7C7B\u578B
+10037=\u53C2\u6570\u503C\u5FC5\u987B\u662F\u6709\u6548\u7684\u6570\u5B57
+10038=\u53C2\u6570\u503C\u5FC5\u987B\u662Ftrue\u6216false
+10039=\u53C2\u6570\u503C\u5FC5\u987B\u662F\u6709\u6548\u7684JSON\u6570\u7EC4\u683C\u5F0F
+10040=\u53C2\u6570\u503C\u5FC5\u987B\u662F\u6709\u6548\u7684JSON\u683C\u5F0F
+
+10041=\u8BBE\u5907\u672A\u627E\u5230
+10042={0}
+10043=\u5220\u9664\u6570\u636E\u5931\u8D25
+10044=\u7528\u6237\u672A\u767B\u5F55
+10045=WebSocket\u8FDE\u63A5\u5931\u8D25\u6216\u8FDE\u63A5\u8D85\u65F6
+10046=\u4FDD\u5B58\u58F0\u7EB9\u9519\u8BEF\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
+10047=\u6BCF\u65E5\u53D1\u9001\u6B21\u6570\u5DF2\u8FBE\u4E0A\u9650
+10048=\u65E7\u5BC6\u7801\u8F93\u5165\u9519\u8BEF
+10049=\u8BBE\u7F6E\u7684LLM\u4E0D\u662Fopenai\u548Collama
+10050=token\u751F\u6210\u5931\u8D25
+10051=\u8D44\u6E90\u4E0D\u5B58\u5728
+10052=\u9ED8\u8BA4\u667A\u80FD\u4F53\u672A\u627E\u5230
+10053=\u667A\u80FD\u4F53\u672A\u627E\u5230
+10054=\u58F0\u7EB9\u63A5\u53E3\u672A\u914D\u7F6E\uFF0C\u8BF7\u5148\u5728\u53C2\u6570\u914D\u7F6E\u4E2D\u914D\u7F6E\u58F0\u7EB9\u63A5\u53E3\u5730\u5740(server.voice_print)
+10055=\u77ED\u4FE1\u53D1\u9001\u5931\u8D25
+10056=\u77ED\u4FE1\u8FDE\u63A5\u5EFA\u7ACB\u5931\u8D25
+10057=\u667A\u80FD\u4F53\u7684\u58F0\u7EB9\u521B\u5EFA\u5931\u8D25
+10058=\u667A\u80FD\u4F53\u7684\u5BF9\u5E94\u58F0\u7EB9\u66F4\u65B0\u5931\u8D25
+10059=\u667A\u80FD\u4F53\u7684\u5BF9\u5E94\u58F0\u7EB9\u5220\u9664\u5931\u8D25
+10060=\u53D1\u9001\u592A\u9891\u7E41\uFF0C\u8BF7{0}\u79D2\u540E\u518D\u8BD5
+10061=\u6FC0\u6D3B\u7801\u4E0D\u80FD\u4E3A\u7A7A
+10062=\u6FC0\u6D3B\u7801\u9519\u8BEF
+10063=\u8BBE\u5907\u5DF2\u6FC0\u6D3B
+10064=\u8BE5\u6A21\u578B\u4E3A\u9ED8\u8BA4\u6A21\u578B\uFF0C\u8BF7\u5148\u8BBE\u7F6E\u5176\u4ED6\u6A21\u578B\u4E3A\u9ED8\u8BA4\u6A21\u578B
+10065=\u65B0\u589E\u6570\u636E\u5931\u8D25
+10066=\u4FEE\u6539\u6570\u636E\u5931\u8D25
+10067=\u56FE\u5F62\u9A8C\u8BC1\u7801\u9519\u8BEF
+10068=\u6CA1\u6709\u5F00\u542F\u624B\u673A\u6CE8\u518C\uFF0C\u6CA1\u6CD5\u4F7F\u7528\u77ED\u4FE1\u9A8C\u8BC1\u7801\u529F\u80FD
+10069=\u7528\u6237\u540D\u4E0D\u662F\u624B\u673A\u53F7\u7801\uFF0C\u8BF7\u91CD\u65B0\u8F93\u5165
+10070=\u6B64\u624B\u673A\u53F7\u7801\u5DF2\u7ECF\u6CE8\u518C\u8FC7
+10071=\u8F93\u5165\u7684\u624B\u673A\u53F7\u7801\u672A\u6CE8\u518C
+10072=\u5F53\u524D\u4E0D\u5141\u8BB8\u666E\u901A\u7528\u6237\u6CE8\u518C
+10073=\u6CA1\u6709\u5F00\u542F\u624B\u673A\u6CE8\u518C\uFF0C\u6CA1\u6CD5\u4F7F\u7528\u627E\u56DE\u5BC6\u7801\u529F\u80FD
+10074=\u8F93\u5165\u7684\u624B\u673A\u53F7\u7801\u683C\u5F0F\u4E0D\u6B63\u786E
+10075=\u8F93\u5165\u7684\u624B\u673A\u9A8C\u8BC1\u7801\u9519\u8BEF
+10076=\u5B57\u5178\u7C7B\u578B\u4E0D\u5B58\u5728
+10077=\u5B57\u5178\u7C7B\u578B\u7F16\u7801\u91CD\u590D
+10078=\u8BFB\u53D6\u8D44\u6E90\u5931\u8D25
+10079=LLM\u5927\u6A21\u578B\u548CIntent\u610F\u56FE\u8BC6\u522B\uFF0C\u9009\u62E9\u53C2\u6570\u4E0D\u5339\u914D
+10080=\u6B64\u58F0\u97F3\u58F0\u7EB9\u5BF9\u5E94\u7684\u4EBA\uFF08{0}\uFF09\u5DF2\u7ECF\u6CE8\u518C\uFF0C\u8BF7\u9009\u62E9\u5176\u4ED6\u58F0\u97F3\u6CE8\u518C
+10081=\u5220\u9664\u58F0\u7EB9\u51FA\u73B0\u9519\u8BEF
+10082=\u6B64\u6B21\u4FEE\u6539\u4E0D\u5141\u8BB8\uFF0C\u6B64\u58F0\u97F3\u5DF2\u7ECF\u6CE8\u518C\u4E3A\u58F0\u7EB9\u4E86\uFF08{0}\uFF09
+10083=\u4FEE\u6539\u58F0\u7EB9\u9519\u8BEF\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
+10084=\u58F0\u7EB9\u63A5\u53E3\u5730\u5740\u5B58\u5728\u9519\u8BEF\uFF0C\u8BF7\u8FDB\u5165\u53C2\u6570\u7BA1\u7406\u4FEE\u6539\u58F0\u7EB9\u63A5\u53E3\u5730\u5740
+10085=\u97F3\u9891\u6570\u636E\u4E0D\u5C5E\u4E8E\u8FD9\u4E2A\u667A\u80FD\u4F53
+10086=\u97F3\u9891\u6570\u636E\u662F\u7A7A\u7684\u8BF7\u68C0\u67E5\u4E0A\u4F20\u6570\u636E
+10087=\u58F0\u7EB9\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u6C42\u4E0D\u6210\u529F
+10088=\u58F0\u7EB9\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u6C42\u5904\u7406\u5931\u8D25
+10089=\u58F0\u7EB9\u6CE8\u9500\u5931\u8D25\uFF0C\u8BF7\u6C42\u4E0D\u6210\u529F
+10090=\u58F0\u7EB9\u6CE8\u9500\u5931\u8D25\uFF0C\u8BF7\u6C42\u5904\u7406\u5931\u8D25
+10091=\u4F9B\u5E94\u5668\u4E0D\u5B58\u5728
+10092=\u8BBE\u7F6E\u7684LLM\u4E0D\u5B58\u5728
+10093=\u8BE5\u6A21\u578B\u914D\u7F6E\u5DF2\u88AB\u667A\u80FD\u4F53{0}\u5F15\u7528\uFF0C\u65E0\u6CD5\u5220\u9664
+10094=\u8BE5LLM\u6A21\u578B\u5DF2\u88AB\u610F\u56FE\u8BC6\u522B\u914D\u7F6E\u5F15\u7528\uFF0C\u65E0\u6CD5\u5220\u9664
+10095=\u65E0\u6548\u670D\u52A1\u7AEF\u64CD\u4F5C
+10096=\u672A\u914D\u7F6E\u670D\u52A1\u7AEFWebSocket\u5730\u5740
+10097=\u76EE\u6807WebSocket\u5730\u5740\u4E0D\u5B58\u5728
+10098=WebSocket\u5730\u5740\u5217\u8868\u4E0D\u80FD\u4E3A\u7A7A
+10099=WebSocket\u5730\u5740\u4E0D\u80FD\u4F7F\u7528localhost\u6216127.0.0.1
+10100=WebSocket\u5730\u5740\u683C\u5F0F\u4E0D\u6B63\u786E
+10101=WebSocket\u8FDE\u63A5\u6D4B\u8BD5\u5931\u8D25
+10102=OTA\u5730\u5740\u4E0D\u80FD\u4E3A\u7A7A
+10103=OTA\u5730\u5740\u4E0D\u80FD\u4F7F\u7528localhost\u6216127.0.0.1
+10104=OTA\u5730\u5740\u5FC5\u987B\u4EE5http\u6216https\u5F00\u5934
+10105=OTA\u5730\u5740\u5FC5\u987B\u4EE5/ota/\u7ED3\u5C3E
+10106=OTA\u63A5\u53E3\u8BBF\u95EE\u5931\u8D25
+10107=OTA\u63A5\u53E3\u8FD4\u56DE\u5185\u5BB9\u683C\u5F0F\u4E0D\u6B63\u786E
+10108=OTA\u63A5\u53E3\u9A8C\u8BC1\u5931\u8D25
+10109=MCP\u5730\u5740\u4E0D\u80FD\u4E3A\u7A7A
+10110=MCP\u5730\u5740\u4E0D\u80FD\u4F7F\u7528localhost\u6216127.0.0.1
+10111=\u4E0D\u662F\u6B63\u786E\u7684MCP\u5730\u5740
+10112=MCP\u63A5\u53E3\u8BBF\u95EE\u5931\u8D25
+10113=MCP\u63A5\u53E3\u8FD4\u56DE\u5185\u5BB9\u683C\u5F0F\u4E0D\u6B63\u786E
+10114=MCP\u63A5\u53E3\u9A8C\u8BC1\u5931\u8D25
+10115=\u58F0\u7EB9\u63A5\u53E3\u5730\u5740\u4E0D\u80FD\u4E3A\u7A7A
+10116=\u58F0\u7EB9\u63A5\u53E3\u5730\u5740\u4E0D\u80FD\u4F7F\u7528localhost\u6216127.0.0.1
+10117=\u4E0D\u662F\u6B63\u786E\u7684\u58F0\u7EB9\u63A5\u53E3\u5730\u5740
+10118=\u58F0\u7EB9\u63A5\u53E3\u5730\u5740\u5FC5\u987B\u4EE5http\u6216https\u5F00\u5934
+10119=\u58F0\u7EB9\u63A5\u53E3\u8BBF\u95EE\u5931\u8D25
+10120=\u58F0\u7EB9\u63A5\u53E3\u8FD4\u56DE\u5185\u5BB9\u683C\u5F0F\u4E0D\u6B63\u786E
+10121=\u58F0\u7EB9\u63A5\u53E3\u9A8C\u8BC1\u5931\u8D25
+10122=mqtt\u5BC6\u94A5\u4E0D\u80FD\u4E3A\u7A7A
+10123=\u60A8\u7684mqtt\u5BC6\u94A5\u957F\u5EA6\u4E0D\u5B89\u5168\uFF0C\u5B57\u7B26\u6570\u9700\u8981\u81F3\u5C118\u4E2A\u5B57\u7B26\u4E14\u5FC5\u987B\u540C\u65F6\u5305\u542B\u5927\u5C0F\u5199\u5B57\u6BCD
+10124=\u60A8\u7684mqtt\u5BC6\u94A5\u957F\u5EA6\u4E0D\u5B89\u5168\uFF0Cmqtt\u5BC6\u94A5\u5FC5\u987B\u540C\u65F6\u5305\u542B\u5927\u5C0F\u5199\u5B57\u6BCD
+10125=\u60A8\u7684mqtt\u5BC6\u94A5\u5305\u542B\u5F31\u5BC6\u7801
+10128=\u5B57\u5178\u6807\u7B7E\u91CD\u590D
+10129=SM2\u5BC6\u94A5\u672A\u914D\u7F6E
+10130=SM2\u89E3\u5BC6\u5931\u8D25
+10131=modelType\u548CprovideCode\u4E0D\u80FD\u4E3A\u7A7A
+10132=\u6CA1\u6709\u6743\u9650\u67E5\u770B\u8BE5\u667A\u80FD\u4F53\u7684\u804A\u5929\u8BB0\u5F55
+10133=\u4F1A\u8BDDID\u4E0D\u80FD\u4E3A\u7A7A
+10134=\u667A\u80FD\u4F53ID\u4E0D\u80FD\u4E3A\u7A7A
+10135=\u804A\u5929\u8BB0\u5F55\u4E0B\u8F7D\u5931\u8D25
+10136=\u4E0B\u8F7D\u94FE\u63A5\u5DF2\u8FC7\u671F\u6216\u65E0\u6548
+10137=\u4E0B\u8F7D\u94FE\u63A5\u65E0\u6548
+10138=\u7528\u6237
+10139=\u667A\u4F53
+10140=\u97F3\u9891\u6587\u4EF6\u4E0D\u80FD\u4E3A\u7A7A
+10141=\u53EA\u652F\u6301\u97F3\u9891\u6587\u4EF6
+10142=\u97F3\u9891\u6587\u4EF6\u5927\u5C0F\u4E0D\u80FD\u8D85\u8FC710MB
+10143=\u4E0A\u4F20\u5931\u8D25
+10144=\u58F0\u97F3\u514B\u9686\u8BB0\u5F55\u4E0D\u5B58\u5728
+10145=\u97F3\u8272\u8D44\u6E90\u4FE1\u606F\u4E0D\u80FD\u4E3A\u7A7A
+10146=\u5E73\u53F0\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+10147=\u97F3\u8272ID\u4E0D\u80FD\u4E3A\u7A7A
+10148=\u5F52\u5C5E\u8D26\u53F7\u4E0D\u80FD\u4E3A\u7A7A
+10149=\u5220\u9664\u7684\u97F3\u8272\u8D44\u6E90ID\u4E0D\u80FD\u4E3A\u7A7A
+10150=\u60A8\u6CA1\u6709\u6743\u9650\u64CD\u4F5C\u8BE5\u8BB0\u5F55
+10151=\u8BF7\u5148\u4E0A\u4F20\u97F3\u9891\u6587\u4EF6
+10152=\u6A21\u578B\u914D\u7F6E\u672A\u627E\u5230
+10153=\u6A21\u578B\u7C7B\u578B\u672A\u627E\u5230
+10154=\u8BAD\u7EC3\u5931\u8D25: {0}
+10155=\u706B\u5C71\u5F15\u64CE\u7F3A\u5C11\u914D\u7F6E
+10156=\u54CD\u5E94\u683C\u5F0F\u9519\u8BEF\uFF0C\u7F3A\u5C11BaseResp\u5B57\u6BB5
+10157=\u8BF7\u6C42\u5931\u8D25
+10158=\u514B\u9686\u97F3\u8272:
+10159=\u97F3\u8272ID\u5DF2\u5B58\u5728
+10160=\u706B\u5C71\u5F15\u64CE\u97F3\u8272ID\u683C\u5F0F\u9519\u8BEF\uFF0C\u5FC5\u987B\u4EE5S_\u5F00\u5934
+10161=Mac\u5730\u5740\u5DF2\u5B58\u5728
+10162=\u6A21\u578B\u4F9B\u5E94\u5668\u4E0D\u5B58\u5728
+10163=\u77E5\u8BC6\u5E93\u8BB0\u5F55\u4E0D\u5B58\u5728
+10164=RAG\u914D\u7F6E\u672A\u627E\u5230
+10165=RAG\u914D\u7F6E\u7C7B\u578B\u9519\u8BEF
+10166=\u9ED8\u8BA4RAG\u914D\u7F6E\u672A\u627E\u5230
+10167=RAG\u8C03\u7528\u5931\u8D25\uFF0C{0}
+10168=\u4E0A\u4F20\u6587\u4EF6\u5931\u8D25
+10169=\u60A8\u6CA1\u6709\u6743\u9650\u64CD\u4F5C\u8BE5\u8BB0\u5F55
+10170=\u77E5\u8BC6\u5E93\u540D\u79F0\u91CD\u590D
+10171=RAG\u914D\u7F6E\u4F53\u7684base_url\u4E0D\u80FD\u4E3A\u7A7A
+10172=RAG\u914D\u7F6E\u4F53\u7684api_key\u4E0D\u80FD\u4E3A\u7A7A
+10173=RAG\u914D\u7F6E\u4F53\u7684api_key\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u8BF7\u66F4\u6362\u4E3A\u5728\u53D6\u7684API\u53C2\u6570
+10174=RAG\u914D\u7F6E\u4F53\u7684base_url\u683C\u5F0F\u9519\u8BEF\uFF0C\u5FC5\u987B\u4EE5http\u6216https\u5F00\u5934
+10175=mac\u5730\u5740\u4E0D\u80FD\u4E3A\u7A7A
+10176=RAG\u914D\u7F6E\u4F53\u7684dataset_id\u4E0D\u80FD\u4E3A\u7A7A
+10177=RAG\u914D\u7F6E\u4F53\u7684model_id\u4E0D\u80FD\u4E3A\u7A7A
+10178=RAG\u914D\u7F6E\u4F53\u7684dataset_id\u548Cmodel_id\u4E0D\u80FD\u4E3A\u7A7A
+10179=\u6587\u4EF6\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+10180=\u6587\u4EF6\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A
+10181=\u97F3\u8272\u514B\u9686\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+10182=\u97F3\u8272\u514B\u9686\u97F3\u9891\u4E0D\u5B58\u5728
+10183=\u9ED8\u8BA4\u667A\u80FD\u4F53\u672A\u627E\u5230
+10184=\u4E0D\u652F\u6301\u7684\u9002\u914D\u5668\u7C7B\u578B
+10185=RAG\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25
+10186=\u9002\u914D\u5668\u521B\u5EFA\u5931\u8D25
+10187=\u9002\u914D\u5668\u521D\u59CB\u5316\u5931\u8D25
+10188=\u9002\u914D\u5668\u8FDE\u63A5\u6D4B\u8BD5\u5931\u8D25
+10189=\u9002\u914D\u5668\u64CD\u4F5C\u5931\u8D25
+10190=\u9002\u914D\u5668\u672A\u627E\u5230
+10191=\u9002\u914D\u5668\u7F13\u5B58\u9519\u8BEF
+10192=\u9002\u914D\u5668\u7C7B\u578B\u672A\u627E\u5230
+10193=\u8BBE\u5907ID\u4E0D\u80FD\u4E3A\u7A7A
+10194=\u8BBE\u5907\u4E0D\u5B58\u5728\u6216\u4E0D\u5728\u7EBF
+10195=OTA\u4E0A\u4F20\u6B21\u6570\u8D85\u8FC7\u9650\u5236
+10196=\u6807\u7B7E\u540D\u79F0\u5DF2\u5B58\u5728
+10197=\u6807\u7B7E\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+10198=\u6807\u7B7E\u4E0D\u5B58\u5728
+10199=\u89E3\u6790\u4E2D\u6587\u4EF6\u6682\u4E0D\u652F\u6301\u6B64\u64CD\u4F5C
+10200=\u6CA1\u6709\u6743\u9650\u67E5\u770B\u8BE5\u667A\u80FD\u4F53\u7684MCP\u63A5\u5165\u70B9\u5730\u5740
+10201=\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u8FDB\u5165\u53C2\u6570\u7BA1\u7406\u914D\u7F6Emcp\u63A5\u5165\u70B9\u5730\u5740
+10202=\u6CA1\u6709\u6743\u9650\u67E5\u770B\u8BE5\u667A\u80FD\u4F53\u7684MCP\u5DE5\u5177\u5217\u8868
+10203=\u6587\u4EF6\u540D\u79F0\u5DF2\u5B58\u5728
+10204=\u6587\u4EF6\u5927\u5C0F\u8D85\u8FC71MB\u9650\u5236
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_zh_TW.properties b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_zh_TW.properties
new file mode 100644
index 0000000..15b6135
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/messages_zh_TW.properties
@@ -0,0 +1,213 @@
+#\u7E41\u9AD4\u4E2D\u6587
+500=\u670D\u52D9\u5668\u5167\u90E8\u7570\u5E38
+401=\u672A\u6388\u6B0A
+403=\u62D2\u7D55\u8A2A\u554F\uFF0C\u6C92\u6709\u6743\u9650
+
+10001={0}\u4E0D\u80FD\u70BA\u7A7A
+10002=\u6578\u64DA\u5EAB\u4E2D\u5DF2\u5B58\u5728\u8A72\u8A18\u9304
+10003=\u7372\u53D6\u53C3\u6578\u5931\u6557
+10004=\u8CEC\u865F\u6216\u5BC6\u78BC\u932F\u8AA4
+10005=\u8CEC\u865F\u5DF2\u88AB\u505C\u7528
+10006=\u552F\u4E00\u6A19\u8B58\u4E0D\u80FD\u70BA\u7A7A
+10007=\u9A57\u8B49\u78BC\u4E0D\u6B63\u78BA
+10008=\u5148\u522A\u9664\u5B50\u83DC\u55AE\u6216\u6309\u9215
+10009=\u539F\u5BC6\u78BC\u4E0D\u6B63\u78BA
+10010=\u5E33\u865F\u6216\u5BC6\u78BC\u4E0D\u6B63\u78BA,\u60A8\u9084\u6709\u53EF\u4EE5\u5617\u8A66{0}\u6B21
+
+10011=\u4E0A\u7D1A\u90E8\u9580\u9078\u64C7\u932F\u8AA4
+10012=\u4E0A\u7D1A\u83DC\u55AE\u4E0D\u80FD\u70BA\u81EA\u8EAB
+10013=\u6578\u64DA\u6B0A\u9650\u63A5\u53E3\uFF0C\u53EA\u80FD\u662FMap\u985E\u578B\u53C3\u6578
+10014=\u8ACB\u5148\u522A\u9664\u4E0B\u7D1A\u90E8\u9580
+10015=\u8ACB\u5148\u522A\u9664\u90E8\u9580\u4E0B\u7684\u7528\u6236
+10016=\u90E8\u7F72\u5931\u6557\uFF0C\u6C92\u6709\u6D41\u7A0B
+10017=\u6A21\u578B\u5716\u4E0D\u6B63\u78BA\uFF0C\u8ACB\u6AA2\u67E5
+10018=\u5C0E\u51FA\u5931\u6557\uFF0C\u6A21\u578BID\u70BA{0}
+
+10019=\u8ACB\u4E0A\u50B3\u6587\u4EF6
+10020=token\u4E0D\u80FD\u70BA\u7A7A
+10021=token\u5931\u6548\uFF0C\u8ACB\u91CD\u65B0\u767B\u9304
+10022=\u8CEC\u865F\u5DF2\u88AB\u9396\u5B9A
+10023=\u8ACB\u4E0A\u50B3zip\u3001bar\u3001bpmn\u3001bpmn20.xml\u683C\u5F0F\u6587\u4EF6
+
+10024=\u4E0A\u50B3\u6587\u4EF6\u5931\u6557{0}
+10025=\u767C\u9001\u77ED\u4FE1\u5931\u6557{0}
+10026=\u90F5\u4EF6\u6A21\u677F\u4E0D\u5B58\u5728
+
+10027=Redis\u670D\u52D9\u7570\u5E38
+10028=\u5B9A\u6642\u4EFB\u52D9\u5931\u6557
+10029=\u4E0D\u80FD\u5305\u542B\u975E\u6CD5\u5B57\u7B26
+10030=\u5BC6\u78BC\u9577\u5EA6\u4E0D\u8DB3{0}\u4F4D
+10031=\u5BC6\u78BC\u5FC5\u9808\u540C\u6642\u5305\u542B\u6578\u5B57\u3001\u5927\u5C0F\u5BEB\u5B57\u6BCD\u548C\u7279\u6B8A\u5B57\u7B26\u7D44\u6210
+10032=\u522A\u9664\u672C\u6578\u64DA\u7570\u5E38
+10033=\u8A2D\u5099\u9A57\u8B49\u78BC\u932F\u8AA4
+
+10034=\u53C3\u6578\u503C\u4E0D\u80FD\u70BA\u7A7A
+10035=\u53C3\u6578\u985E\u578B\u4E0D\u80FD\u70BA\u7A7A
+10036=\u4E0D\u652F\u6301\u7684\u53C3\u6578\u985E\u578B
+10037=\u53C3\u6578\u503C\u5FC5\u9808\u662F\u6709\u6548\u7684\u6578\u5B57
+10038=\u53C3\u6578\u503C\u5FC5\u9808\u662Ftrue\u6216false
+10039=\u53C3\u6578\u503C\u5FC5\u9808\u662F\u6709\u6548\u7684JSON\u6578\u7D44\u683C\u5F0F
+10040=\u53C3\u6578\u503C\u5FC5\u9808\u662F\u6709\u6548\u7684JSON\u683C\u5F0F
+
+10041=\u8A2D\u5099\u672A\u627E\u5230
+10042={0}
+10043=\u522A\u9664\u6578\u64DA\u5931\u6557
+10044=\u7528\u6236\u672A\u767B\u9304
+10045=WebSocket\u9023\u63A5\u5931\u6557\u6216\u9023\u63A5\u8D85\u6642
+10046=\u4FDD\u5B58\u8072\u7D0B\u932F\u8AA4\uFF0C\u8ACB\u806F\u7E6B\u7BA1\u7406\u54E1
+10047=\u6BCF\u65E5\u767C\u9001\u6B21\u6578\u5DF2\u9054\u4E0A\u9650
+10048=\u820A\u5BC6\u78BC\u8F38\u5165\u932F\u8AA4
+10049=\u8A2D\u7F6E\u7684LLM\u4E0D\u662Fopenai\u548Collama
+10050=token\u751F\u6210\u5931\u6557
+10051=\u8CC7\u6E90\u4E0D\u5B58\u5728
+10052=\u9ED8\u8A8D\u667A\u80FD\u9AD4\u672A\u627E\u5230
+10053=\u667A\u80FD\u9AD4\u672A\u627E\u5230
+10054=\u8072\u7D0B\u63A5\u53E3\u672A\u914D\u7F6E\uFF0C\u8ACB\u5148\u5728\u53C3\u6578\u914D\u7F6E\u4E2D\u914D\u7F6E\u8072\u7D0B\u63A5\u53E3\u5730\u5740(server.voice_print)
+10055=\u77ED\u4FE1\u767C\u9001\u5931\u6557
+10056=\u77ED\u4FE1\u9023\u63A5\u5EFA\u7ACB\u5931\u6557
+10057=\u667A\u80FD\u9AD4\u7684\u8072\u7D0B\u5275\u5EFA\u5931\u6557
+10058=\u667A\u80FD\u9AD4\u7684\u5C0D\u61C9\u8072\u7D0B\u66F4\u65B0\u5931\u6557
+10059=\u667A\u80FD\u9AD4\u7684\u5C0D\u61C9\u8072\u7D0B\u522A\u9664\u5931\u6557
+10060=\u767C\u9001\u592A\u983B\u7E41\uFF0C\u8ACB{0}\u79D2\u5F8C\u518D\u8A66
+10061=\u6FC0\u6D3B\u78BC\u4E0D\u80FD\u70BA\u7A7A
+10062=\u6FC0\u6D3B\u78BC\u932F\u8AA4
+10063=\u8A2D\u5099\u5DF2\u6FC0\u6D3B
+10064=\u8A72\u6A21\u578B\u70BA\u9810\u8A2D\u6A21\u578B\uFF0C\u8ACB\u5148\u8A2D\u7F6E\u5176\u4ED6\u6A21\u578B\u70BA\u9810\u8A2D\u6A21\u578B
+10065=\u65B0\u589E\u6578\u64DA\u5931\u6557
+10066=\u66F4\u65B0\u6578\u64DA\u5931\u6557
+10067=\u5716\u5F62\u9A57\u8B49\u78BC\u932F\u8AA4
+10068=\u672A\u958B\u555F\u624B\u6A5F\u8A3B\u518A\uFF0C\u7121\u6CD5\u4F7F\u7528\u77ED\u4FE1\u9A57\u8B49\u78BC\u529F\u80FD
+10069=\u7528\u6236\u540D\u4E0D\u662F\u624B\u6A5F\u865F\u78BC\uFF0C\u8ACB\u91CD\u65B0\u8F38\u5165
+10070=\u8A72\u624B\u6A5F\u865F\u78BC\u5DF2\u8A3B\u518A
+10071=\u8F38\u5165\u7684\u624B\u6A5F\u865F\u78BC\u672A\u8A3B\u518A
+10072=\u76EE\u524D\u4E0D\u5141\u8A31\u666E\u901A\u7528\u6236\u8A3B\u518A
+10073=\u672A\u958B\u555F\u624B\u6A5F\u8A3B\u518A\uFF0C\u7121\u6CD5\u4F7F\u7528\u5BC6\u78BC\u627E\u56DE\u529F\u80FD
+10074=\u8F38\u5165\u7684\u624B\u6A5F\u865F\u78BC\u683C\u5F0F\u4E0D\u6B63\u78BA
+10075=\u8F38\u5165\u7684\u77ED\u4FE1\u9A57\u8B49\u78BC\u4E0D\u6B63\u78BA
+10076=\u5B57\u5178\u985E\u578B\u4E0D\u5B58\u5728
+10077=\u5B57\u5178\u985E\u578B\u7DE8\u78BC\u91CD\u8907
+10078=\u8B80\u53D6\u8CC7\u6E90\u5931\u6557
+10079=LLM\u6A21\u578B\u8207\u610F\u5716\u8B58\u5225\u3001\u53C3\u6578\u9078\u64C7\u4E0D\u5339\u914D
+10080=\u6B64\u8072\u7D0B\u5C6C\u65BC\u5DF2\u8A3B\u518A\u7684{0}\uFF0C\u8ACB\u9078\u64C7\u5176\u4ED6\u8072\u97F3
+10081=\u522A\u9664\u8072\u7D0B\u6642\u767C\u751F\u932F\u8AA4
+10082=\u4E0D\u5141\u8A31\u4FEE\u6539\uFF0C\u6B64\u8072\u97F3\u5DF2\u8A3B\u518A\u70BA\u8072\u7D0B({0})
+10083=\u4FEE\u6539\u8072\u7D0B\u932F\u8AA4\uFF0C\u8ACB\u806F\u7E6B\u7BA1\u7406\u54E1
+10084=\u8072\u7D0B\u63A5\u53E3\u5730\u5740\u932F\u8AA4\uFF0C\u8ACB\u524D\u5F80\u53C3\u6578\u7BA1\u7406\u4FEE\u6539\u8072\u7D0B\u63A5\u53E3\u5730\u5740
+10085=\u97F3\u983B\u6578\u64DA\u4E0D\u5C6C\u65BC\u6B64\u667A\u80FD\u9AD4
+10086=\u97F3\u983B\u6578\u64DA\u70BA\u7A7A\uFF0C\u8ACB\u6AA2\u67E5\u4E0A\u50B3\u6578\u64DA
+10087=\u8072\u7D0B\u8A3B\u518A\u5931\u6557\uFF0C\u8ACB\u6C42\u672A\u6210\u529F
+10088=\u8072\u7D0B\u8A3B\u518A\u5931\u6557\uFF0C\u8ACB\u6C42\u8655\u7406\u5931\u6557
+10089=\u8072\u7D0B\u8A3B\u92B7\u5931\u6557\uFF0C\u8ACB\u6C42\u672A\u6210\u529F
+10090=\u8072\u7D0B\u8A3B\u92B7\u5931\u6557\uFF0C\u8ACB\u6C42\u8655\u7406\u5931\u6557
+10091=\u6A21\u578B\u63D0\u4F9B\u5546\u4E0D\u5B58\u5728
+10092=\u914D\u7F6E\u7684LLM\u4E0D\u5B58\u5728
+10093=\u6B64\u6A21\u578B\u914D\u7F6E\u88AB\u667A\u80FD\u9AD4{0}\u5F15\u7528\uFF0C\u7121\u6CD5\u522A\u9664
+10094=\u6B64LLM\u6A21\u578B\u88AB\u610F\u5716\u8B58\u5225\u914D\u7F6E\u5F15\u7528\uFF0C\u7121\u6CD5\u522A\u9664
+10095=\u7121\u6548\u7684\u670D\u52D9\u5668\u64CD\u4F5C
+10096=\u672A\u914D\u7F6E\u670D\u52D9\u5668WebSocket\u5730\u5740
+10097=\u76EE\u6A19WebSocket\u5730\u5740\u4E0D\u5B58\u5728
+10098=WebSocket\u5730\u5740\u5217\u8868\u4E0D\u80FD\u70BA\u7A7A
+10099=WebSocket\u5730\u5740\u4E0D\u80FD\u4F7F\u7528localhost\u6216127.0.0.1
+10100=WebSocket\u5730\u5740\u683C\u5F0F\u4E0D\u6B63\u78BA
+10101=WebSocket\u9023\u63A5\u6E2C\u8A66\u5931\u6557
+10102=OTA\u5730\u5740\u4E0D\u80FD\u70BA\u7A7A
+10103=OTA\u5730\u5740\u4E0D\u80FD\u4F7F\u7528localhost\u6216127.0.0.1
+10104=OTA\u5730\u5740\u5FC5\u9808\u4EE5http\u6216https\u958B\u982D
+10105=OTA\u5730\u5740\u5FC5\u9808\u4EE5/ota/\u7D50\u5C3E
+10106=OTA\u63A5\u53E3\u8A2A\u554F\u5931\u6557
+10107=OTA\u63A5\u53E3\u8FD4\u56DE\u5167\u5BB9\u683C\u5F0F\u4E0D\u6B63\u78BA
+10108=OTA\u63A5\u53E3\u9A57\u8B49\u5931\u6557
+10109=MCP\u5730\u5740\u4E0D\u80FD\u70BA\u7A7A
+10110=MCP\u5730\u5740\u4E0D\u80FD\u4F7F\u7528localhost\u6216127.0.0.1
+10111=\u4E0D\u662F\u6709\u6548\u7684MCP\u5730\u5740
+10112=MCP\u63A5\u53E3\u8A2A\u554F\u5931\u6557
+10113=MCP\u63A5\u53E3\u8FD4\u56DE\u5167\u5BB9\u683C\u5F0F\u4E0D\u6B63\u78BA
+10114=MCP\u63A5\u53E3\u9A57\u8B49\u5931\u6557
+10115=\u8072\u7D0B\u63A5\u53E3\u5730\u5740\u4E0D\u80FD\u70BA\u7A7A
+10116=\u8072\u7D0B\u63A5\u53E3\u5730\u5740\u4E0D\u80FD\u4F7F\u7528localhost\u6216127.0.0.1
+10117=\u4E0D\u662F\u6709\u6548\u7684\u8072\u7D0B\u63A5\u53E3\u5730\u5740
+10118=\u8072\u7D0B\u63A5\u53E3\u5730\u5740\u5FC5\u9808\u4EE5http\u6216https\u958B\u982D
+10119=\u8072\u7D0B\u63A5\u53E3\u8A2A\u554F\u5931\u6557
+10120=\u8072\u7D0B\u63A5\u53E3\u8FD4\u56DE\u5167\u5BB9\u683C\u5F0F\u4E0D\u6B63\u78BA
+10121=\u8072\u7D0B\u63A5\u53E3\u9A57\u8B49\u5931\u6557
+10122=MQTT\u5BC6\u9470\u4E0D\u80FD\u70BA\u7A7A
+10123=\u60A8\u7684MQTT\u5BC6\u9470\u4E0D\u5B89\u5168\uFF0C\u9700\u81F3\u5C118\u500B\u5B57\u7B26\u4E14\u5FC5\u9808\u540C\u6642\u5305\u542B\u5927\u5C0F\u5BEB\u5B57\u6BCD
+10124=\u60A8\u7684MQTT\u5BC6\u9470\u4E0D\u5B89\u5168\uFF0CMQTT\u5BC6\u9470\u5FC5\u9808\u540C\u6642\u5305\u542B\u5927\u5C0F\u5BEB\u5B57\u6BCD
+10125=\u60A8\u7684MQTT\u5BC6\u9470\u5305\u542B\u5F31\u5BC6\u78BC
+10128=\u5B57\u5178\u6A19\u7C64\u91CD\u8907
+10129=SM2\u5BC6\u9470\u672A\u914D\u7F6E
+10130=SM2\u89E3\u5BC6\u5931\u6557
+10131=modelType\u548CprovideCode\u4E0D\u80FD\u70BA\u7A7A
+10132=\u6C92\u6709\u6B0A\u9650\u67E5\u770B\u8A72\u667A\u80FD\u9AD4\u7684\u804A\u5929\u8A18\u9304
+10133=\u6703\u8A71ID\u4E0D\u80FD\u70BA\u7A7A
+10134=\u667A\u80FD\u9AD4ID\u4E0D\u80FD\u70BA\u7A7A
+10135=\u804A\u5929\u8A18\u9304\u4E0B\u8F09\u5931\u6557
+10136=\u4E0B\u8F09\u93C8\u63A5\u5DF2\u904E\u671F\u6216\u7121\u6548
+10137=\u4E0B\u8F09\u93C8\u63A5\u7121\u6548
+10138=\u7528\u6236
+10139=\u667A\u9AD4
+10140=\u97F3\u983B\u6587\u4EF6\u4E0D\u80FD\u70BA\u7A7A
+10141=\u53EA\u652F\u6301\u97F3\u983B\u6587\u4EF6
+10142=\u97F3\u983B\u6587\u4EF6\u5927\u5C0F\u4E0D\u80FD\u8D85\u904E10MB
+10143=\u4E0A\u50B3\u5931\u6557
+10144=\u8072\u97F3\u514B\u9686\u8A18\u9304\u4E0D\u5B58\u5728
+10145=\u97F3\u8272\u8CC7\u6E90\u4FE1\u606F\u4E0D\u80FD\u70BA\u7A7A
+10146=\u5E73\u53F0\u540D\u7A31\u4E0D\u80FD\u70BA\u7A7A
+10147=\u97F3\u8272ID\u4E0D\u80FD\u70BA\u7A7A
+10148=\u6B78\u5C6C\u5E33\u865F\u4E0D\u80FD\u70BA\u7A7A
+10149=\u522A\u9664\u7684\u97F3\u8272\u8CC7\u6E90ID\u4E0D\u80FD\u70BA\u7A7A
+10150=\u60A8\u6C92\u6709\u6B0A\u9650\u64CD\u4F5C\u8A72\u8A18\u9304
+10151=\u8ACB\u5148\u4E0A\u50B3\u97F3\u983B\u6587\u4EF6
+10152=\u6A21\u578B\u914D\u7F6E\u672A\u627E\u5230
+10153=\u6A21\u578B\u985E\u578B\u672A\u627E\u5230
+10154=\u6A21\u578B\u8AB2\u7DF4\u5931\u6557: {0}
+10155=\u706B\u5C71\u5F15\u64CE\u7F3A\u5C11appid\u6216access_token
+10156=\u97FF\u61C9\u683C\u5F0F\u932F\u8AA4\uFF0C\u7F3A\u5C11BaseResp\u5B57\u6BB5
+10157=\u8ACB\u6C42\u5931\u6557
+10158=\u514B\u9686\u97F3\u8272:
+10159=\u97F3\u8272ID\u5DF2\u5B58\u5728
+10160=\u706B\u5C71\u5F15\u64CE\u97F3\u8272ID\u683C\u5F0F\u932F\u8AA4\uFF0C\u5FC5\u9808\u4EE5S_\u958B\u982D
+10161=Mac\u5730\u5740\u5DF2\u5B58\u5728
+10162=\u6A21\u578B\u63D0\u4F9B\u5546\u4E0D\u5B58\u5728
+10163=\u77E5\u8B58\u5EAB\u8A18\u9304\u4E0D\u5B58\u5728
+10164=RAG\u914D\u7F6E\u672A\u627E\u5230
+10165=RAG\u914D\u7F6E\u985E\u578B\u932F\u8AA4
+10166=\u9810\u8A2DRAG\u914D\u7F6E\u672A\u627E\u5230
+10167=RAG\u8ABF\u7528\u5931\u6557\uFF0C{0}
+10168=\u4E0A\u50B3\u6587\u4EF6\u5931\u6557
+10169=\u60A8\u6C92\u6709\u6B0A\u9650\u64CD\u4F5C\u8A72\u8A18\u9304
+10170=\u77E5\u8B58\u5EAB\u540D\u7A31\u91CD\u8907
+10171=RAG\u914D\u7F6E\u4F53\u7684base_url\u4E0D\u80FD\u4E3A\u7A7A
+10172=RAG\u914D\u7F6E\u4F53\u7684api_key\u4E0D\u80FD\u4E3A\u7A7A
+10173=RAG\u914D\u7F6E\u4F53\u7684api_key\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u8BF7\u66F4\u6362\u4E3A\u5728\u53D6\u7684API\u53C2\u6570
+10174=RAG\u914D\u7F6E\u4F53\u7684base_url\u683C\u5F0F\u9519\u8BEF\uFF0C\u5FC5\u987B\u4EE5http\u6216https\u5F00\u5934
+10175=mac\u5730\u5740\u4E0D\u80FD\u4E3A\u7A7A
+10176=RAG\u914D\u7F6E\u4F53\u7684dataset_id\u4E0D\u80FD\u4E3A\u7A7A
+10177=RAG\u914D\u7F6E\u4F53\u7684model_id\u4E0D\u80FD\u4E3A\u7A7A
+10178=RAG\u914D\u7F6E\u4F53\u7684dataset_id\u548Cmodel_id\u4E0D\u80FD\u4E3A\u7A7A
+10179=\u6587\u4EF6\u540D\u7A31\u4E0D\u80FD\u70BA\u7A7A
+10180=\u6587\u4EF6\u5185\u5BB9\u4E0D\u80FD\u70BA\u7A7A
+10181=\u97F3\u8272\u514B\u9686\u540D\u7A31\u4E0D\u80FD\u70BA\u7A7A
+10182=\u97F3\u8272\u514B\u9686\u97F3\u983B\u4E0D\u5B58\u5728
+10183=\u9ED8\u8BA4\u667A\u80FD\u4F53\u672A\u627E\u5230
+10184=\u4E0D\u652F\u6301\u7684\u9002\u914D\u5668\u985E\u578B
+10185=RAG\u914D\u7F6E\u9A57\u8B49\u5931\u6557
+10186=\u9002\u914D\u5668\u5275\u5EFA\u5931\u6557
+10187=\u9002\u914D\u5668\u521D\u59CB\u5316\u5931\u6557
+10188=\u9002\u914D\u5668\u9023\u63A5\u6E2C\u8A66\u5931\u6557
+10189=\u9002\u914D\u5668\u64CD\u4F5C\u5931\u6557
+10190=\u9002\u914D\u5668\u672A\u627E\u5230
+10191=\u9002\u914D\u5668\u7F13\u5B58\u932F\u8AA4
+10192=\u9002\u914D\u5668\u985E\u578B\u672A\u627E\u5230
+10193=\u8A2D\u5099ID\u4E0D\u80FD\u4E3A\u7A7A
+10194=\u8A2D\u5099\u4E0D\u5B58\u5728\u6216\u672A\u5728\u7DDA
+10195=OTA\u4E0A\u4F20\u6B21\u6578\u8D85\u904E\u9650\u5236
+10196=\u6A19\u7C64\u540D\u7A31\u5DF2\u5B58\u5728
+10197=\u6A19\u7C64\u540D\u7A31\u4E0D\u80FD\u4E3A\u7A7A
+10198=\u6A19\u7C64\u4E0D\u5B58\u5728
+10199=\u89E3\u6790\u4E2D\u6587\u4EF6\u66AB\u4E0D\u652F\u6301\u6B64\u64CD\u4F5C
+10200=\u6C92\u6709\u6B0A\u9650\u67E5\u770B\u8A72\u667A\u80FD\u9AD4\u7684MCP\u63A5\u5165\u9EDE\u5730\u5740
+10201=\u8ACB\u806F\u7E6B\u7BA1\u7406\u54E1\u9032\u5165\u53C3\u6578\u7BA1\u7406\u914D\u7F6Emcp\u63A5\u5165\u9EDE\u5730\u5740
+10202=\u6C92\u6709\u6B0A\u9650\u67E5\u770B\u8A72\u667A\u80FD\u9AD4\u7684MCP\u5DE5\u5177\u5217\u8868
+10203=\u6A94\u6848\u540D\u7A31\u5DF2\u5B58\u5728
+10204=\u6A94\u6848\u5927\u5C0F\u8D85\u904E1MB\u9650\u5236
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation.properties b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation.properties
new file mode 100644
index 0000000..3a10bf2
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation.properties
@@ -0,0 +1,35 @@
+#\u7B80\u4F53\u4E2D\u6587
+id.require=ID\u4E0D\u80FD\u4E3A\u7A7A
+id.null=ID\u5FC5\u987B\u4E3A\u7A7A
+
+sort.number=\u6392\u5E8F\u503C\u4E0D\u80FD\u5C0F\u4E8E0
+page.number=\u9875\u6570\u4E0D\u80FD\u5C0F\u4E8E0
+limit.number=\u5217\u6570\u4E0D\u80FD\u5C0F\u4E8E0
+
+sysdict.type.require=\u5B57\u5178\u7C7B\u578B\u4E0D\u80FD\u4E3A\u7A7A
+sysdict.name.require=\u5B57\u5178\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+sysdict.label.require=\u5B57\u5178\u6807\u7B7E\u4E0D\u80FD\u4E3A\u7A7A
+
+sysparams.paramcode.require=\u53C2\u6570\u7F16\u7801\u4E0D\u80FD\u4E3A\u7A7A
+sysparams.paramvalue.require=\u53C2\u6570\u503C\u4E0D\u80FD\u4E3A\u7A7A
+sysparams.valuetype.require=\u503C\u7C7B\u578B\u4E0D\u80FD\u4E3A\u7A7A
+sysparams.valuetype.pattern=\u503C\u7C7B\u578B\u5FC5\u987B\u662F string\u3001number\u3001boolean \u6216 array
+
+sysuser.username.require=\u7528\u6237\u540D\u4E0D\u80FD\u4E3A\u7A7A
+sysuser.password.require=\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A
+sysuser.realname.require=\u59D3\u540D\u4E0D\u80FD\u4E3A\u7A7A
+sysuser.gender.range=\u6027\u522B\u53D6\u503C\u8303\u56F40~2
+sysuser.email.error=\u90AE\u7BB1\u683C\u5F0F\u4E0D\u6B63\u786E
+sysuser.deptId.require=\u90E8\u95E8\u4E0D\u80FD\u4E3A\u7A7A
+sysuser.status.range=\u72B6\u6001\u53D6\u503C\u8303\u56F40~1
+sysuser.captcha.require=\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A
+sysuser.uuid.require=\u552F\u4E00\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A
+
+
+timbre.languages.require=\u97F3\u8272\u7684\u8BED\u8A00\u4E0D\u53EF\u4EE5\u4E3A\u7A7A
+timbre.name.require=\u97F3\u8272\u7684\u540D\u79F0\u4E0D\u53EF\u4EE5\u4E3A\u7A7A
+timbre.ttsModelId.require=\u97F3\u8272\u7684tts\u4E3B\u952E\u4E0D\u53EF\u4EE5\u4E3A\u7A7A
+timbre.ttsVoice.require=\u97F3\u8272\u7684\u7F16\u7801\u4E0D\u53EF\u4EE5\u4E3A\u7A7A
+
+ota.device.not.found=\u8BBE\u5907\u672A\u627E\u5230
+ota.device.need.bind={0}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_de_DE.properties b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_de_DE.properties
new file mode 100644
index 0000000..d3b4ce5
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_de_DE.properties
@@ -0,0 +1,34 @@
+#Deutsch
+id.require=ID darf nicht leer sein
+id.null=ID muss leer sein
+
+sort.number=Der Sortierwert darf nicht kleiner als 0 sein
+page.number=Der Seitenwert darf nicht kleiner als 0 sein
+limit.number=Der Limit-Wert darf nicht kleiner als 0 sein
+
+sysdict.type.require=Der W枚rterbuchtyp darf nicht leer sein
+sysdict.name.require=Der W枚rterbuchname darf nicht leer sein
+sysdict.label.require=W枚rterbuch-Label darf nicht leer sein
+
+sysparams.paramcode.require=Parameter-Code darf nicht leer sein
+sysparams.paramvalue.require=Parameter-Werte d眉rfen nicht leer sein
+sysparams.valuetype.require=Werttyp darf nicht leer sein
+sysparams.valuetype.pattern=Werttyp muss string, number, boolean oder array sein
+
+sysuser.username.require=Der Benutzername darf nicht leer sein
+sysuser.password.require=Das Passwort darf nicht leer sein
+sysuser.realname.require=Der echte Name darf nicht leer sein
+sysuser.gender.range=Geschlecht liegt im Bereich von 0 bis 2
+sysuser.email.error=Falsches E-Mail-Format
+sysuser.deptId.require=Abteilungen d眉rfen nicht leer sein
+sysuser.status.range=Status liegt im Bereich von 0 bis 1
+sysuser.captcha.require=Das Captcha darf nicht leer sein
+sysuser.uuid.require=Der eindeutige Identifikator darf nicht leer sein
+
+timbre.languages.require=Die Sprache des Klangs darf nicht leer sein
+timbre.name.require=Der Name des Klangs darf nicht leer sein
+timbre.ttsModelId.require=Die TTS-Modell-ID des Klangs darf nicht leer sein
+timbre.ttsVoice.require=Die TTS-Stimme des Klangs darf nicht leer sein
+
+ota.device.not.found=Ger盲t nicht gefunden
+ota.device.need.bind={0}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_en_US.properties b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_en_US.properties
new file mode 100644
index 0000000..342cada
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_en_US.properties
@@ -0,0 +1,34 @@
+#English
+id.require=ID can not be empty
+id.null=ID has to be empty
+
+sort.number=The sort value cannot be less than 0
+page.number=The page value cannot be less than 0
+limit.number=The limit value cannot be less than 0
+
+sysdict.type.require=The dictionary type cannot be empty
+sysdict.name.require=The dictionary name cannot be empty
+sysdict.label.require=Dictionary tag cannot be empty
+
+sysparams.paramcode.require=Parameter encoding cannot be empty
+sysparams.paramvalue.require=Parameter values cannot be empty
+sysparams.valuetype.require=Value type cannot be empty
+sysparams.valuetype.pattern=Value type must be string, number, boolean or array
+
+sysuser.username.require=The username cannot be empty
+sysuser.password.require=The password cannot be empty
+sysuser.realname.require=The realname cannot be empty
+sysuser.gender.range=Gender ranges from 0 to 2
+sysuser.email.error=Incorrect email format
+sysuser.deptId.require=Departments cannot be empty
+sysuser.status.range=State ranges from 0 to 1
+sysuser.captcha.require=The captcha cannot be empty
+sysuser.uuid.require=The unique identifier cannot be empty
+
+timbre.languages.require=The language of the timbre cannot be empty
+timbre.name.require=The name of the timbre cannot be empty
+timbre.ttsModelId.require=The TTS model ID of the timbre cannot be empty
+timbre.ttsVoice.require=The TTS voice of the timbre cannot be empty
+
+ota.device.not.found=Device not found
+ota.device.need.bind={0}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_pt_BR.properties b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_pt_BR.properties
new file mode 100644
index 0000000..fccb04b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_pt_BR.properties
@@ -0,0 +1,34 @@
+#Portugu\u00eas
+id.require=O ID n\u00e3o pode estar vazio
+id.null=O ID deve estar vazio
+
+sort.number=O valor de classifica\u00e7\u00e3o n\u00e3o pode ser menor que 0
+page.number=O n\u00famero da p\u00e1gina n\u00e3o pode ser menor que 0
+limit.number=O n\u00famero de registros n\u00e3o pode ser menor que 0
+
+sysdict.type.require=O tipo de dicion\u00e1rio n\u00e3o pode estar vazio
+sysdict.name.require=O nome do dicion\u00e1rio n\u00e3o pode estar vazio
+sysdict.label.require=A etiqueta do dicion\u00e1rio n\u00e3o pode estar vazio
+
+sysparams.paramcode.require=A codifica\u00e7\u00e3o do par\u00e2metro n\u00e3o pode estar vazia
+sysparams.paramvalue.require=O valor do par\u00e2metro n\u00e3o pode estar vazio
+sysparams.valuetype.require=O tipo de valor n\u00e3o pode estar vazio
+sysparams.valuetype.pattern=O tipo de valor deve ser string, number, boolean ou array
+
+sysuser.username.require=O nome de usu\u00e1rio n\u00e3o pode estar vazio
+sysuser.password.require=A senha n\u00e3o pode estar vazio
+sysuser.realname.require=O nome completo n\u00e3o pode estar vazio
+sysuser.gender.range=G\u00eanero varia de 0 a 2
+sysuser.email.error=Formato de e-mail incorreto
+sysuser.deptId.require=O departamento n\u00e3o pode estar vazio
+sysuser.status.range=Status varia de 0 a 1
+sysuser.captcha.require=O c\u00f3digo de verifica\u00e7\u00e3o n\u00e3o pode estar vazio
+sysuser.uuid.require=O identificador \u00fanico n\u00e3o pode estar vazio
+
+timbre.languages.require=O idioma do timbre n\u00e3o pode estar vazio
+timbre.name.require=O nome do timbre n\u00e3o pode estar vazio
+timbre.ttsModelId.require=O ID do modelo TTS do timbre n\u00e3o pode estar vazio
+timbre.ttsVoice.require=O c\u00f3digo do timbre n\u00e3o pode estar vazio
+
+ota.device.not.found=Dispositivo n\u00e3o encontrado
+ota.device.need.bind={0}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_vi_VN.properties b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_vi_VN.properties
new file mode 100644
index 0000000..d143691
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_vi_VN.properties
@@ -0,0 +1,34 @@
+# Ti岷縩g Vi峄噒
+id.require=ID kh么ng 膽瓢峄 膽峄� tr峄憂g
+id.null=ID ph岷 膽峄� tr峄憂g
+
+sort.number=Gi谩 tr峄� s岷痯 x岷縫 kh么ng th峄� nh峄� h啤n 0
+page.number=Gi谩 tr峄� trang kh么ng th峄� nh峄� h啤n 0
+limit.number=Gi谩 tr峄� gi峄沬 h岷 kh么ng th峄� nh峄� h啤n 0
+
+sysdict.type.require=Lo岷 t峄� 膽i峄僴 kh么ng 膽瓢峄 膽峄� tr峄憂g
+sysdict.name.require=T锚n t峄� 膽i峄僴 kh么ng 膽瓢峄 膽峄� tr峄憂g
+sysdict.label.require=Th岷� t峄� 膽i峄僴 kh么ng 膽瓢峄 膽峄� tr峄憂g
+
+sysparams.paramcode.require=M茫 tham s峄� kh么ng 膽瓢峄 膽峄� tr峄憂g
+sysparams.paramvalue.require=Gi谩 tr峄� tham s峄� kh么ng 膽瓢峄 膽峄� tr峄憂g
+sysparams.valuetype.require=Lo岷 gi谩 tr峄� kh么ng 膽瓢峄 膽峄� tr峄憂g
+sysparams.valuetype.pattern=Lo岷 gi谩 tr峄� ph岷 l脿 chu峄梚, s峄�, boolean ho岷穋 m岷g
+
+sysuser.username.require=T锚n ng瓢峄漣 d霉ng kh么ng 膽瓢峄 膽峄� tr峄憂g
+sysuser.password.require=M岷璽 kh岷﹗ kh么ng 膽瓢峄 膽峄� tr峄憂g
+sysuser.realname.require=T锚n th岷璽 kh么ng 膽瓢峄 膽峄� tr峄憂g
+sysuser.gender.range=Gi峄沬 t铆nh ph岷 t峄� 0 膽岷縩 2
+sysuser.email.error=膼峄媙h d岷g email kh么ng ch铆nh x谩c
+sysuser.deptId.require=Ph貌ng ban kh么ng 膽瓢峄 膽峄� tr峄憂g
+sysuser.status.range=Tr岷g th谩i ph岷 t峄� 0 膽岷縩 1
+sysuser.captcha.require=M茫 x谩c th峄眂 kh么ng 膽瓢峄 膽峄� tr峄憂g
+sysuser.uuid.require=膼峄媙h danh duy nh岷 kh么ng 膽瓢峄 膽峄� tr峄憂g
+
+timbre.languages.require=Ng么n ng峄� c峄 芒m s岷痗 kh么ng 膽瓢峄 膽峄� tr峄憂g
+timbre.name.require=T锚n 芒m s岷痗 kh么ng 膽瓢峄 膽峄� tr峄憂g
+timbre.ttsModelId.require=ID m么 h矛nh TTS c峄 芒m s岷痗 kh么ng 膽瓢峄 膽峄� tr峄憂g
+timbre.ttsVoice.require=Gi峄峮g TTS c峄 芒m s岷痗 kh么ng 膽瓢峄 膽峄� tr峄憂g
+
+ota.device.not.found=Kh么ng t矛m th岷 thi岷縯 b峄�
+ota.device.need.bind={0}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_zh_CN.properties b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_zh_CN.properties
new file mode 100644
index 0000000..7860042
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_zh_CN.properties
@@ -0,0 +1,34 @@
+#\u7B80\u4F53\u4E2D\u6587
+id.require=ID\u4E0D\u80FD\u4E3A\u7A7A
+id.null=ID\u5FC5\u987B\u4E3A\u7A7A
+
+sort.number=\u6392\u5E8F\u503C\u4E0D\u80FD\u5C0F\u4E8E0
+page.number=\u9875\u6570\u4E0D\u80FD\u5C0F\u4E8E0
+limit.number=\u5217\u6570\u4E0D\u80FD\u5C0F\u4E8E0
+
+sysdict.type.require=\u5B57\u5178\u7C7B\u578B\u4E0D\u80FD\u4E3A\u7A7A
+sysdict.name.require=\u5B57\u5178\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+sysdict.label.require=\u5B57\u5178\u6807\u7B7E\u4E0D\u80FD\u4E3A\u7A7A
+
+sysparams.paramcode.require=\u53C2\u6570\u7F16\u7801\u4E0D\u80FD\u4E3A\u7A7A
+sysparams.paramvalue.require=\u53C2\u6570\u503C\u4E0D\u80FD\u4E3A\u7A7A
+sysparams.valuetype.require=\u503C\u7C7B\u578B\u4E0D\u80FD\u4E3A\u7A7A
+sysparams.valuetype.pattern=\u503C\u7C7B\u578B\u5FC5\u987B\u662F string\u3001number\u3001boolean \u6216 array
+
+sysuser.username.require=\u7528\u6237\u540D\u4E0D\u80FD\u4E3A\u7A7A
+sysuser.password.require=\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A
+sysuser.realname.require=\u59D3\u540D\u4E0D\u80FD\u4E3A\u7A7A
+sysuser.gender.range=\u6027\u522B\u53D6\u503C\u8303\u56F40~2
+sysuser.email.error=\u90AE\u7BB1\u683C\u5F0F\u4E0D\u6B63\u786E
+sysuser.deptId.require=\u90E8\u95E8\u4E0D\u80FD\u4E3A\u7A7A
+sysuser.status.range=\u72B6\u6001\u53D6\u503C\u8303\u56F40~1
+sysuser.captcha.require=\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A
+sysuser.uuid.require=\u552F\u4E00\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A
+
+timbre.languages.require=\u97F3\u8272\u7684\u8BED\u8A00\u4E0D\u80FD\u4E3A\u7A7A
+timbre.name.require=\u97F3\u8272\u7684\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+timbre.ttsModelId.require=\u97F3\u8272\u7684tts\u4E3B\u952E\u4E0D\u80FD\u4E3A\u7A7A
+timbre.ttsVoice.require=\u97F3\u8272\u7684\u7F16\u7801\u4E0D\u80FD\u4E3A\u7A7A
+
+ota.device.not.found=\u8BBE\u5907\u672A\u627E\u5230
+ota.device.need.bind={0}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_zh_TW.properties b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_zh_TW.properties
new file mode 100644
index 0000000..4e3a271
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/i18n/validation_zh_TW.properties
@@ -0,0 +1,34 @@
+#\u7B80\u4F53\u4E2D\u6587
+id.require=ID\u4E0D\u80FD\u4E3A\u7A7A
+id.null=ID\u5FC5\u987B\u4E3A\u7A7A
+
+sort.number=\u6392\u5E8F\u503C\u4E0D\u80FD\u5C0F\u4E8E0
+page.number=\u9875\u6570\u4E0D\u80FD\u5C0F\u4E8E0
+limit.number=\u5217\u6570\u4E0D\u80FD\u5C0F\u4E8E0
+
+sysdict.type.require=\u5B57\u5178\u7C7B\u578B\u4E0D\u80FD\u4E3A\u7A7A
+sysdict.name.require=\u5B57\u5178\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+sysdict.label.require=\u5B57\u5178\u6807\u7B7E\u4E0D\u80FD\u4E3A\u7A7A
+
+sysparams.paramcode.require=\u53C3\u6578\u7DE8\u78BC\u4E0D\u80FD\u70BA\u7A7A
+sysparams.paramvalue.require=\u53C3\u6578\u503C\u4E0D\u80FD\u70BA\u7A7A
+sysparams.valuetype.require=\u503C\u985E\u578B\u4E0D\u80FD\u70BA\u7A7A
+sysparams.valuetype.pattern=\u503C\u985E\u578B\u5FC5\u9808\u662F string\u3001number\u3001boolean \u6216 array
+
+sysuser.username.require=\u7528\u6237\u540D\u4E0D\u80FD\u4E3A\u7A7A
+sysuser.password.require=\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A
+sysuser.realname.require=\u59D3\u540D\u4E0D\u80FD\u4E3A\u7A7A
+sysuser.gender.range=\u6027\u522B\u53D6\u503C\u8303\u56F40~2
+sysuser.email.error=\u90AE\u7BB1\u683C\u5F0F\u4E0D\u6B63\u786E
+sysuser.deptId.require=\u90E8\u95E8\u4E0D\u80FD\u4E3A\u7A7A
+sysuser.status.range=\u72B6\u6001\u53D6\u503C\u8303\u56F40~1
+sysuser.captcha.require=\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A
+sysuser.uuid.require=\u552F\u4E00\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A
+
+timbre.languages.require=\u97F3\u8272\u7684\u8BED\u8A00\u4E0D\u53EF\u4EE5\u4E3A\u7A7A
+timbre.name.require=\u97F3\u8272\u7684\u540D\u79F0\u4E0D\u53EF\u4EE5\u4E3A\u7A7A
+timbre.ttsModelId.require=\u97F3\u8272\u7684tts\u4E3B\u952E\u4E0D\u53EF\u4EE5\u4E3A\u7A7A
+timbre.ttsVoice.require=\u97F3\u8272\u7684\u7F16\u7801\u4E0D\u53EF\u4EE5\u4E3A\u7A7A
+
+ota.device.not.found=\u8A2D\u5099\u672A\u627E\u5230
+ota.device.need.bind={0}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/logback-spring.xml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/logback-spring.xml
new file mode 100644
index 0000000..42bf076
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/logback-spring.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+ <!-- 瀹氫箟鏃ュ織鏂囦欢瀛樺偍浣嶇疆 -->
+ <property name="LOG_HOME" value="./logs" />
+
+ <!-- 寮曞叆Spring Boot榛樿閰嶇疆 -->
+ <include resource="org/springframework/boot/logging/logback/defaults.xml" />
+
+ <!-- 鎺у埗鍙拌緭鍑洪厤缃� -->
+ <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}) - %msg%n</pattern>
+ <charset>UTF-8</charset>
+ </encoder>
+ </appender>
+
+ <!-- 鏂囦欢杈撳嚭閰嶇疆 - 鎵�鏈夋棩蹇� -->
+ <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${LOG_HOME}/xiaozhi-esp32-api.log</file>
+ <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+ <fileNamePattern>${LOG_HOME}/xiaozhi-esp32-api.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+ <maxFileSize>10MB</maxFileSize>
+ <maxHistory>30</maxHistory>
+ <totalSizeCap>2GB</totalSizeCap>
+ </rollingPolicy>
+ <encoder>
+ <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+ <charset>UTF-8</charset>
+ </encoder>
+ </appender>
+
+ <!-- 鏂囦欢杈撳嚭閰嶇疆 - 浠呴敊璇棩蹇� -->
+ <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${LOG_HOME}/error.log</file>
+ <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+ <level>ERROR</level>
+ </filter>
+ <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+ <fileNamePattern>${LOG_HOME}/error.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+ <maxFileSize>10MB</maxFileSize>
+ <maxHistory>30</maxHistory>
+ <totalSizeCap>1GB</totalSizeCap>
+ </rollingPolicy>
+ <encoder>
+ <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+ <charset>UTF-8</charset>
+ </encoder>
+ </appender>
+
+ <!-- 寮�鍙戠幆澧冮厤缃� -->
+ <springProfile name="dev">
+ <root level="INFO">
+ <appender-ref ref="CONSOLE" />
+ <!-- 寮�鍙戠幆澧冧篃璁板綍鏃ュ織鏂囦欢 -->
+ <appender-ref ref="FILE" />
+ <appender-ref ref="ERROR_FILE" />
+ </root>
+ <logger name="xiaozhi" level="DEBUG" />
+ <logger name="org.springframework.web" level="INFO" />
+ <logger name="org.springboot.sample" level="INFO" />
+ </springProfile>
+
+ <!-- 娴嬭瘯鐜閰嶇疆 -->
+ <springProfile name="test">
+ <root level="INFO">
+ <appender-ref ref="CONSOLE" />
+ <appender-ref ref="FILE" />
+ <appender-ref ref="ERROR_FILE" />
+ </root>
+ <logger name="xiaozhi" level="INFO" />
+ <logger name="org.springframework.web" level="INFO" />
+ <logger name="org.springboot.sample" level="INFO" />
+ </springProfile>
+
+ <!-- 鐢熶骇鐜閰嶇疆 - 鍚堝苟涓や釜鐗堟湰鐨勬渶浣冲疄璺� -->
+ <springProfile name="prod">
+ <!-- 浣跨敤鏈湴鏃ュ織璺緞 -->
+ <appender name="PROD_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${LOG_HOME}/xiaozhi-esp32-api.log</file>
+ <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+ <fileNamePattern>${LOG_HOME}/xiaozhi-esp32-api.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+ <maxFileSize>10MB</maxFileSize>
+ <maxHistory>30</maxHistory>
+ <totalSizeCap>2GB</totalSizeCap>
+ </rollingPolicy>
+ <encoder>
+ <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+ <charset>UTF-8</charset>
+ </encoder>
+ </appender>
+
+ <!-- 鐢熶骇鐜閿欒鏃ュ織 -->
+ <appender name="PROD_ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${LOG_HOME}/error.log</file>
+ <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+ <level>ERROR</level>
+ </filter>
+ <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+ <fileNamePattern>${LOG_HOME}/error.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+ <maxFileSize>10MB</maxFileSize>
+ <maxHistory>30</maxHistory>
+ <totalSizeCap>1GB</totalSizeCap>
+ </rollingPolicy>
+ <encoder>
+ <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+ <charset>UTF-8</charset>
+ </encoder>
+ </appender>
+
+ <!-- 鐢熶骇鐜鏍规棩蹇楅厤缃� -->
+ <root level="INFO">
+ <appender-ref ref="PROD_FILE" />
+ <appender-ref ref="PROD_ERROR_FILE" />
+ <appender-ref ref="CONSOLE" />
+ </root>
+
+ <!-- 鐗瑰畾鍖呯殑鏃ュ織绾у埆 -->
+ <logger name="xiaozhi" level="INFO" />
+ <logger name="org.springframework.web" level="ERROR" />
+ <logger name="org.springboot.sample" level="ERROR" />
+ </springProfile>
+
+ <!-- 纭繚鏃ュ織鐩綍瀛樺湪 -->
+ <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
+ <resetJUL>true</resetJUL>
+ </contextListener>
+</configuration>
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/lua/emptyAll.lua b/xiaozhi-esp32-server/main/manager-api/src/main/resources/lua/emptyAll.lua
new file mode 100644
index 0000000..2626a89
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/lua/emptyAll.lua
@@ -0,0 +1 @@
+redis.call('FLUSHDB')
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/lua/getKeyOrCreate.lua b/xiaozhi-esp32-server/main/manager-api/src/main/resources/lua/getKeyOrCreate.lua
new file mode 100644
index 0000000..bc375b1
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/lua/getKeyOrCreate.lua
@@ -0,0 +1,11 @@
+local value = redis.call('GET', KEYS[1])
+-- value 濡傛灉涓虹┖鐫�璁剧疆鍊�
+if not value then
+ local result = redis.call('SET', KEYS[1], ARGV[1])
+ -- 妫�鏌� ARGV[2] 鏄惁瀛樺湪涓斿ぇ浜� 0
+ local expireTime = tonumber(ARGV[2])
+ if expireTime and expireTime > 0 then
+ redis.call('EXPIRE', KEYS[1], expireTime)
+ end
+end
+return value
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentCorrectWordMappingDao.xml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentCorrectWordMappingDao.xml
new file mode 100644
index 0000000..5cc131a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentCorrectWordMappingDao.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xiaozhi.modules.agent.dao.AgentCorrectWordMappingDao">
+
+ <delete id="deleteByAgentId">
+ DELETE FROM ai_agent_correct_word_mapping WHERE agent_id = #{agentId}
+ </delete>
+
+ <delete id="deleteByFileId">
+ DELETE FROM ai_agent_correct_word_mapping WHERE file_id = #{fileId}
+ </delete>
+
+ <insert id="batchInsertMapping" parameterType="java.util.List">
+ INSERT INTO ai_agent_correct_word_mapping (id, agent_id, file_id, creator, created_at, updater, updated_at)
+ VALUES
+ <foreach collection="list" item="item" separator=",">
+ (#{item.id}, #{item.agentId}, #{item.fileId}, #{item.creator}, #{item.createdAt}, #{item.updater}, #{item.updatedAt})
+ </foreach>
+ </insert>
+
+ <select id="selectByAgentId" resultType="xiaozhi.modules.agent.entity.AgentCorrectWordMappingEntity">
+ SELECT id, agent_id, file_id, creator, created_at, updater, updated_at
+ FROM ai_agent_correct_word_mapping
+ WHERE agent_id = #{agentId}
+ </select>
+
+</mapper>
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentDao.xml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentDao.xml
new file mode 100644
index 0000000..54a575f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentDao.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xiaozhi.modules.agent.dao.AgentDao">
+ <!-- 鑾峰彇鏅鸿兘浣撶殑璁惧鏁伴噺 -->
+ <select id="getDeviceCountByAgentId" resultType="java.lang.Integer">
+ SELECT COUNT(*) FROM ai_device WHERE agent_id = #{agentId}
+ </select>
+
+ <resultMap id="AgentInfoMap" type="xiaozhi.modules.agent.vo.AgentInfoVO">
+ <id column="id" property="id"/>
+ <result column="userId" property="userId"/>
+ <result column="agentCode" property="agentCode"/>
+ <result column="agentName" property="agentName"/>
+ <result column="asrModelId" property="asrModelId"/>
+ <result column="vadModelId" property="vadModelId"/>
+ <result column="llmModelId" property="llmModelId"/>
+ <result column="slmModelId" property="slmModelId"/>
+ <result column="vllmModelId" property="vllmModelId"/>
+ <result column="ttsModelId" property="ttsModelId"/>
+ <result column="ttsVoiceId" property="ttsVoiceId"/>
+ <result column="ttsLanguage" property="ttsLanguage"/>
+ <result column="ttsVolume" property="ttsVolume"/>
+ <result column="ttsRate" property="ttsRate"/>
+ <result column="ttsPitch" property="ttsPitch"/>
+ <result column="memModelId" property="memModelId"/>
+ <result column="intentModelId" property="intentModelId"/>
+
+ <result column="chatHistoryConf" property="chatHistoryConf"/>
+ <result column="systemPrompt" property="systemPrompt"/>
+ <result column="summaryMemory" property="summaryMemory"/>
+ <result column="langCode" property="langCode"/>
+ <result column="language" property="language"/>
+ <result column="sort" property="sort"/>
+ <result column="creator" property="creator"/>
+ <result column="createdAt" property="createdAt"/>
+ <result column="updater" property="updater"/>
+ <result column="updatedAt" property="updatedAt"/>
+ <collection property="functions"
+ ofType="xiaozhi.modules.agent.entity.AgentPluginMapping">
+ <id column="functionId" property="id"/>
+ <result column="functionAgentId" property="agentId"/>
+ <result column="pluginId" property="pluginId"/>
+ <result column="paramInfo" property="paramInfo"/>
+ </collection>
+ </resultMap>
+
+ <select id="selectAgentInfoById" resultMap="AgentInfoMap">
+ SELECT a.id,
+ a.user_id AS userId,
+ a.agent_code AS agentCode,
+ a.agent_name AS agentName,
+ a.asr_model_id AS asrModelId,
+ a.vad_model_id AS vadModelId,
+ a.llm_model_id AS llmModelId,
+ a.slm_model_id AS slmModelId,
+ a.vllm_model_id AS vllmModelId,
+ a.tts_model_id AS ttsModelId,
+ a.tts_voice_id AS ttsVoiceId,
+ a.tts_language AS ttsLanguage,
+ a.tts_volume AS ttsVolume,
+ a.tts_rate AS ttsRate,
+ a.tts_pitch AS ttsPitch,
+ a.mem_model_id AS memModelId,
+ a.intent_model_id AS intentModelId,
+ a.chat_history_conf AS chatHistoryConf,
+ a.system_prompt AS systemPrompt,
+ a.summary_memory AS summaryMemory,
+ a.lang_code AS langCode,
+ a.language AS language,
+ a.sort,
+ a.creator,
+ a.created_at AS createdAt,
+ a.updater,
+ a.updated_at AS updatedAt,
+ f.id AS functionId,
+ f.agent_id AS functionAgentId,
+ f.plugin_id AS pluginId,
+ f.param_info AS paramInfo
+ FROM ai_agent a
+ LEFT JOIN ai_agent_plugin_mapping f ON f.agent_id = a.id
+ WHERE a.id = #{agentId}
+ ORDER BY f.id ASC
+ </select>
+
+ <select id="selectByIdForUpdate" resultType="xiaozhi.modules.agent.entity.AgentEntity">
+ SELECT *
+ FROM ai_agent
+ WHERE id = #{agentId}
+ FOR UPDATE
+ </select>
+
+ <update id="updateSnapshotFields">
+ UPDATE ai_agent
+ SET agent_code = #{agent.agentCode,jdbcType=VARCHAR},
+ agent_name = #{agent.agentName,jdbcType=VARCHAR},
+ asr_model_id = #{agent.asrModelId,jdbcType=VARCHAR},
+ vad_model_id = #{agent.vadModelId,jdbcType=VARCHAR},
+ llm_model_id = #{agent.llmModelId,jdbcType=VARCHAR},
+ slm_model_id = #{agent.slmModelId,jdbcType=VARCHAR},
+ vllm_model_id = #{agent.vllmModelId,jdbcType=VARCHAR},
+ tts_model_id = #{agent.ttsModelId,jdbcType=VARCHAR},
+ tts_voice_id = #{agent.ttsVoiceId,jdbcType=VARCHAR},
+ tts_language = #{agent.ttsLanguage,jdbcType=VARCHAR},
+ tts_volume = #{agent.ttsVolume,jdbcType=INTEGER},
+ tts_rate = #{agent.ttsRate,jdbcType=INTEGER},
+ tts_pitch = #{agent.ttsPitch,jdbcType=INTEGER},
+ mem_model_id = #{agent.memModelId,jdbcType=VARCHAR},
+ intent_model_id = #{agent.intentModelId,jdbcType=VARCHAR},
+ chat_history_conf = #{agent.chatHistoryConf,jdbcType=INTEGER},
+ system_prompt = #{agent.systemPrompt,jdbcType=LONGVARCHAR},
+ summary_memory = #{agent.summaryMemory,jdbcType=LONGVARCHAR},
+ lang_code = #{agent.langCode,jdbcType=VARCHAR},
+ language = #{agent.language,jdbcType=VARCHAR},
+ sort = #{agent.sort,jdbcType=INTEGER},
+ updater = #{agent.updater,jdbcType=BIGINT},
+ updated_at = #{agent.updatedAt,jdbcType=TIMESTAMP}
+ WHERE id = #{agent.id}
+ </update>
+
+</mapper>
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentPluginMappingMapper.xml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentPluginMappingMapper.xml
new file mode 100644
index 0000000..659fcd6
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentPluginMappingMapper.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper
+ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+ "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xiaozhi.modules.agent.dao.AgentPluginMappingMapper">
+
+ <resultMap id="BaseResultMap" type="xiaozhi.modules.agent.entity.AgentPluginMapping">
+ <id property="id" column="id" jdbcType="BIGINT"/>
+ <result property="agentId" column="agent_id" jdbcType="VARCHAR"/>
+ <result property="pluginId" column="plugin_id" jdbcType="VARCHAR"/>
+ <result property="paramInfo" column="param_info" jdbcType="VARCHAR"/>
+ </resultMap>
+
+ <!-- 鐢ㄤ簬鏄犲皠鏍规嵁agentId鏌ヨ瀹屾暣鎻掍欢淇℃伅 -->
+ <resultMap id="AgentPluginWithCodeMap" type="xiaozhi.modules.agent.entity.AgentPluginMapping">
+ <id column="id" property="id" jdbcType="BIGINT"/>
+ <result column="agentId" property="agentId" jdbcType="VARCHAR"/>
+ <result column="pluginId" property="pluginId" jdbcType="VARCHAR"/>
+ <result column="paramInfo" property="paramInfo" jdbcType="VARCHAR"/>
+ <result column="providerCode" property="providerCode" jdbcType="VARCHAR"/>
+ </resultMap>
+
+ <sql id="Base_Column_List">
+ id,agent_id,plugin_id
+ </sql>
+
+
+ <select id="selectPluginsByAgentId" resultMap="AgentPluginWithCodeMap">
+ SELECT m.id AS id,
+ m.agent_id AS agentId,
+ m.plugin_id AS pluginId,
+ m.param_info AS paramInfo,
+ (
+ SELECT
+ p.provider_code
+ FROM
+ ai_model_provider p
+ WHERE
+ p.id = m.plugin_id
+ LIMIT
+ 1
+ ) AS providerCode
+ FROM ai_agent_plugin_mapping m
+ WHERE m.agent_id = #{agentId}
+ </select>
+
+
+</mapper>
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentSnapshotDao.xml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentSnapshotDao.xml
new file mode 100644
index 0000000..39e3211
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentSnapshotDao.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xiaozhi.modules.agent.dao.AgentSnapshotDao">
+
+ <select id="selectMaxVersionNo" resultType="java.lang.Integer">
+ SELECT COALESCE(MAX(version_no), 0)
+ FROM ai_agent_snapshot
+ WHERE agent_id = #{agentId}
+ </select>
+
+ <select id="selectLatestSnapshot" resultType="xiaozhi.modules.agent.entity.AgentSnapshotEntity">
+ SELECT id,
+ agent_id AS agentId,
+ user_id AS userId,
+ version_no AS versionNo,
+ snapshot_data AS snapshotData,
+ changed_fields AS changedFields,
+ source,
+ restore_from_snapshot_id AS restoreFromSnapshotId,
+ restore_from_version_no AS restoreFromVersionNo,
+ creator,
+ created_at AS createdAt,
+ redaction_version AS redactionVersion
+ FROM ai_agent_snapshot
+ WHERE agent_id = #{agentId}
+ ORDER BY version_no DESC
+ LIMIT 1
+ </select>
+
+ <select id="selectNextSnapshot" resultType="xiaozhi.modules.agent.entity.AgentSnapshotEntity">
+ SELECT id,
+ agent_id AS agentId,
+ user_id AS userId,
+ version_no AS versionNo,
+ snapshot_data AS snapshotData,
+ changed_fields AS changedFields,
+ source,
+ restore_from_snapshot_id AS restoreFromSnapshotId,
+ restore_from_version_no AS restoreFromVersionNo,
+ creator,
+ created_at AS createdAt,
+ redaction_version AS redactionVersion
+ FROM ai_agent_snapshot
+ WHERE agent_id = #{agentId}
+ AND version_no > #{versionNo}
+ ORDER BY version_no ASC
+ LIMIT 1
+ </select>
+
+ <insert id="insertWithNextVersion">
+ INSERT INTO ai_agent_snapshot (
+ id,
+ agent_id,
+ user_id,
+ version_no,
+ snapshot_data,
+ changed_fields,
+ source,
+ restore_from_snapshot_id,
+ restore_from_version_no,
+ creator,
+ created_at,
+ redaction_version
+ )
+ SELECT #{snapshot.id},
+ #{snapshot.agentId},
+ #{snapshot.userId},
+ COALESCE(MAX(version_no), 0) + 1,
+ #{snapshot.snapshotData},
+ #{snapshot.changedFields},
+ #{snapshot.source},
+ #{snapshot.restoreFromSnapshotId},
+ #{snapshot.restoreFromVersionNo},
+ #{snapshot.creator},
+ #{snapshot.createdAt},
+ #{snapshot.redactionVersion}
+ FROM ai_agent_snapshot
+ WHERE agent_id = #{snapshot.agentId}
+ </insert>
+
+ <delete id="deleteOlderThanKeepLimit">
+ DELETE FROM ai_agent_snapshot
+ WHERE agent_id = #{agentId}
+ AND id NOT IN (
+ SELECT id
+ FROM (
+ SELECT id
+ FROM ai_agent_snapshot
+ WHERE agent_id = #{agentId}
+ ORDER BY version_no DESC
+ LIMIT #{keepLimit}
+ ) retained_snapshots
+ )
+ </delete>
+
+ <select id="selectLegacyRedactionBatch" resultType="xiaozhi.modules.agent.entity.AgentSnapshotEntity">
+ SELECT id,
+ snapshot_data AS snapshotData,
+ redaction_version AS redactionVersion
+ FROM ai_agent_snapshot
+ WHERE redaction_version < #{targetRedactionVersion}
+ AND (#{afterId} IS NULL OR id > #{afterId})
+ ORDER BY id ASC
+ LIMIT #{limit}
+ </select>
+
+ <update id="updateRedactedSnapshots">
+ UPDATE ai_agent_snapshot
+ SET snapshot_data = CASE id
+ <foreach collection="snapshots" item="snapshot">
+ WHEN #{snapshot.id} THEN #{snapshot.snapshotData}
+ </foreach>
+ ELSE snapshot_data
+ END,
+ redaction_version = #{redactionVersion}
+ WHERE redaction_version < #{redactionVersion}
+ AND id IN
+ <foreach collection="snapshots" item="snapshot" open="(" separator="," close=")">
+ #{snapshot.id}
+ </foreach>
+ </update>
+
+</mapper>
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentTagDao.xml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentTagDao.xml
new file mode 100644
index 0000000..02f0fa0
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentTagDao.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xiaozhi.modules.agent.dao.AgentTagDao">
+
+ <select id="selectByAgentId" resultType="xiaozhi.modules.agent.entity.AgentTagEntity">
+ SELECT t.id, t.tag_name AS tagName, t.sort
+ FROM ai_agent_tag t
+ INNER JOIN ai_agent_tag_relation r ON t.id = r.tag_id
+ WHERE r.agent_id = #{agentId} AND t.deleted = 0
+ ORDER BY r.sort ASC, r.created_at ASC
+ </select>
+
+ <select id="selectByAgentIds" resultType="xiaozhi.modules.agent.entity.AgentTagEntity">
+ SELECT t.id, t.tag_name AS tagName, t.sort, r.agent_id AS agentId
+ FROM ai_agent_tag t
+ INNER JOIN ai_agent_tag_relation r ON t.id = r.tag_id
+ WHERE r.agent_id IN
+ <foreach collection="agentIds" item="id" open="(" separator="," close=")">
+ #{id}
+ </foreach>
+ AND t.deleted = 0
+ ORDER BY r.sort ASC, r.created_at ASC
+ </select>
+
+ <select id="selectAll" resultType="xiaozhi.modules.agent.entity.AgentTagEntity">
+ SELECT id, tag_name AS tagName, sort
+ FROM ai_agent_tag
+ WHERE deleted = 0
+ ORDER BY sort ASC
+ </select>
+
+ <select id="selectAgentIdsByTagName" resultType="java.lang.String">
+ SELECT r.agent_id
+ FROM ai_agent_tag_relation r
+ INNER JOIN ai_agent_tag t ON r.tag_id = t.id
+ WHERE t.tag_name LIKE CONCAT('%', #{tagName}, '%') AND t.deleted = 0
+ </select>
+
+ <select id="selectByTagNames" resultType="xiaozhi.modules.agent.entity.AgentTagEntity">
+ SELECT id, tag_name AS tagName, sort
+ FROM ai_agent_tag
+ WHERE deleted = 0 AND tag_name IN
+ <foreach collection="tagNames" item="name" open="(" separator="," close=")">
+ #{name}
+ </foreach>
+ </select>
+
+ <insert id="batchInsert" parameterType="java.util.List">
+ INSERT INTO ai_agent_tag (id, tag_name, sort, deleted, creator, created_at, updater, updated_at)
+ VALUES
+ <foreach collection="list" item="item" separator=",">
+ (#{item.id}, #{item.tagName}, #{item.sort}, #{item.deleted}, #{item.creator}, #{item.createdAt}, #{item.updater}, #{item.updatedAt})
+ </foreach>
+ </insert>
+
+</mapper>
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentTagRelationDao.xml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentTagRelationDao.xml
new file mode 100644
index 0000000..a13d640
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentTagRelationDao.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xiaozhi.modules.agent.dao.AgentTagRelationDao">
+
+ <delete id="deleteByAgentId">
+ DELETE FROM ai_agent_tag_relation WHERE agent_id = #{agentId}
+ </delete>
+
+ <insert id="insertRelation" parameterType="xiaozhi.modules.agent.entity.AgentTagRelationEntity">
+ INSERT INTO ai_agent_tag_relation (id, agent_id, tag_id, sort, creator, created_at, updater, updated_at)
+ VALUES (#{id}, #{agentId}, #{tagId}, #{sort}, #{creator}, #{createdAt}, #{updater}, #{updatedAt})
+ </insert>
+
+ <insert id="batchInsertRelation" parameterType="java.util.List">
+ INSERT INTO ai_agent_tag_relation (id, agent_id, tag_id, sort, creator, created_at, updater, updated_at)
+ VALUES
+ <foreach collection="list" item="item" separator=",">
+ (#{item.id}, #{item.agentId}, #{item.tagId}, #{item.sort}, #{item.creator}, #{item.createdAt}, #{item.updater}, #{item.updatedAt})
+ </foreach>
+ </insert>
+
+</mapper>
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentTemplateMapper.xml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentTemplateMapper.xml
new file mode 100644
index 0000000..e0caea5
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AgentTemplateMapper.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper
+ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+ "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xiaozhi.modules.agent.dao.AgentTemplateDao">
+</mapper>
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AiAgentChatHistoryDao.xml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AiAgentChatHistoryDao.xml
new file mode 100644
index 0000000..84e22fd
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/agent/AiAgentChatHistoryDao.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xiaozhi.modules.agent.dao.AiAgentChatHistoryDao">
+ <resultMap id="BaseResultMap" type="xiaozhi.modules.agent.entity.AgentChatHistoryEntity">
+ <!--@mbg.generated-->
+ <!--@Table ai_agent_chat_history-->
+ <id column="id" jdbcType="BIGINT" property="id" />
+ <result column="mac_address" jdbcType="VARCHAR" property="macAddress" />
+ <result column="agent_id" jdbcType="VARCHAR" property="agentId" />
+ <result column="session_id" jdbcType="VARCHAR" property="sessionId" />
+ <result column="sort" jdbcType="BIGINT" property="sort" />
+ <result column="chat_type" jdbcType="TINYINT" property="chatType" />
+ <result column="content" jdbcType="VARCHAR" property="content" />
+ <result column="audio" jdbcType="LONGVARCHAR" property="audio" />
+ <result column="audio_url" jdbcType="VARCHAR" property="audioUrl" />
+ <result column="created_at" jdbcType="TIMESTAMP" property="createdAt" />
+ <result column="updated_at" jdbcType="TIMESTAMP" property="updatedAt" />
+ </resultMap>
+ <sql id="Base_Column_List">
+ <!--@mbg.generated-->
+ id, mac_address, agent_id, session_id, sort, chat_type, content, audio, audio_url,
+ created_at, updated_at
+ </sql>
+
+ <select id="getAudioIdsByAgentId" resultType="java.lang.String">
+ SELECT DISTINCT audio_id
+ FROM ai_agent_chat_history
+ WHERE agent_id = #{agentId} AND audio_id IS NOT NULL
+ </select>
+
+ <delete id="deleteAudioByIds">
+ DELETE FROM ai_agent_chat_audio
+ WHERE id IN
+ <foreach collection="audioIds" item="id" open="(" separator="," close=")">
+ #{id}
+ </foreach>
+ </delete>
+
+ <update id="deleteAudioIdByAgentId">
+ UPDATE ai_agent_chat_history
+ SET audio_id = NULL
+ WHERE agent_id = #{agentId}
+ </update>
+
+ <delete id="deleteHistoryByAgentId">
+ DELETE FROM ai_agent_chat_history
+ WHERE agent_id = #{agentId}
+ </delete>
+</mapper>
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/correctword/CorrectWordItemDao.xml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/correctword/CorrectWordItemDao.xml
new file mode 100644
index 0000000..27ea5f4
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/correctword/CorrectWordItemDao.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xiaozhi.modules.correctword.dao.CorrectWordItemDao">
+
+ <insert id="batchInsert" parameterType="java.util.List">
+ INSERT INTO ai_agent_correct_word_item (id, file_id, source_word, target_word)
+ VALUES
+ <foreach collection="list" item="item" separator=",">
+ (#{item.id}, #{item.fileId}, #{item.sourceWord}, #{item.targetWord})
+ </foreach>
+ </insert>
+
+</mapper>
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/device/DeviceAddressBookDao.xml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/device/DeviceAddressBookDao.xml
new file mode 100644
index 0000000..2601f60
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/device/DeviceAddressBookDao.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xiaozhi.modules.device.dao.DeviceAddressBookDao">
+
+ <select id="getAddressBookList" resultType="xiaozhi.modules.device.entity.DeviceAddressBookEntity">
+ SELECT mac_address, target_mac, alias, has_permission, creator, create_date, updater, update_date
+ FROM ai_device_address_book
+ WHERE mac_address = #{macAddress}
+ ORDER BY update_date DESC
+ </select>
+
+ <update id="updateAlias">
+ UPDATE ai_device_address_book
+ SET alias = #{alias}, update_date = NOW()
+ WHERE mac_address = #{macAddress} AND target_mac = #{targetMac}
+ </update>
+
+ <update id="updatePermission">
+ UPDATE ai_device_address_book
+ SET has_permission = #{hasPermission}, update_date = NOW()
+ WHERE mac_address = #{macAddress} AND target_mac = #{targetMac}
+ </update>
+
+ <insert id="insertAddressBook">
+ INSERT INTO ai_device_address_book (mac_address, target_mac, alias, has_permission, creator, create_date, updater, update_date)
+ VALUES (#{macAddress}, #{targetMac}, #{alias}, #{hasPermission}, #{creator}, NOW(), #{updater}, NOW())
+ </insert>
+
+ <delete id="deleteByMacAddresses">
+ DELETE FROM ai_device_address_book WHERE mac_address IN
+ <foreach collection="macAddresses" item="mac" open="(" separator="," close=")">
+ #{mac}
+ </foreach>
+ OR target_mac IN
+ <foreach collection="macAddresses" item="mac" open="(" separator="," close=")">
+ #{mac}
+ </foreach>
+ </delete>
+</mapper>
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/device/DeviceDao.xml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/device/DeviceDao.xml
new file mode 100644
index 0000000..54de47c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/device/DeviceDao.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xiaozhi.modules.device.dao.DeviceDao">
+ <!-- 鑾峰彇姝ゆ櫤鑳戒綋鍏ㄩ儴璁惧鐨勬渶鍚庤繛鎺ユ椂闂� -->
+ <select id="getAllLastConnectedAtByAgentId" resultType="java.util.Date">
+ SELECT last_connected_at FROM ai_device
+ WHERE
+ agent_id = #{agentId}
+ order by
+ last_connected_at desc limit 0,1
+ </select>
+</mapper>
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/knowledge/KnowledgeBaseDao.xml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/knowledge/KnowledgeBaseDao.xml
new file mode 100644
index 0000000..4da660a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/knowledge/KnowledgeBaseDao.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper
+ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+ "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xiaozhi.modules.knowledge.dao.KnowledgeBaseDao">
+
+ <resultMap id="BaseResultMap" type="xiaozhi.modules.knowledge.entity.KnowledgeBaseEntity">
+ <id property="id" column="id" jdbcType="VARCHAR"/>
+ <result property="datasetId" column="dataset_id" jdbcType="VARCHAR"/>
+ <result property="ragModelId" column="rag_model_id" jdbcType="VARCHAR"/>
+ <result property="name" column="name" jdbcType="VARCHAR"/>
+ <result property="description" column="description" jdbcType="VARCHAR"/>
+ <result property="status" column="status" jdbcType="TINYINT"/>
+ <result property="creator" column="creator" jdbcType="BIGINT"/>
+ <result property="createdAt" column="created_at" jdbcType="TIMESTAMP"/>
+ <result property="updater" column="updater" jdbcType="BIGINT"/>
+ <result property="updatedAt" column="updated_at" jdbcType="TIMESTAMP"/>
+ </resultMap>
+
+ <!-- 鏍规嵁鐭ヨ瘑搴揑D鍒犻櫎鐩稿叧鐨勬彃浠舵槧灏勮褰� -->
+ <delete id="deletePluginMappingByKnowledgeBaseId">
+ DELETE FROM ai_agent_plugin_mapping
+ WHERE plugin_id = #{knowledgeBaseId}
+ </delete>
+
+ <!-- 閫氱敤缁村害鍘熷瓙鏇存柊鐭ヨ瘑搴撶粺璁′俊鎭� -->
+ <update id="updateStatsAfterChange">
+ UPDATE ai_rag_dataset
+ SET document_count = document_count + #{docDelta},
+ chunk_count = chunk_count + #{chunkDelta},
+ token_num = token_num + #{tokenDelta},
+ updated_at = NOW()
+ WHERE dataset_id = #{datasetId}
+ </update>
+
+</mapper>
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/model/ModelConfigDao.xml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/model/ModelConfigDao.xml
new file mode 100644
index 0000000..f6f8b3e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/model/ModelConfigDao.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="xiaozhi.modules.model.dao.ModelConfigDao">
+ <resultMap id="ModelConfigResultMap" type="xiaozhi.modules.model.entity.ModelConfigEntity">
+ <id column="id" property="id"/>
+ <result column="model_type" property="modelType"/>
+ <result column="model_code" property="modelCode"/>
+ <result column="model_name" property="modelName"/>
+ <result column="is_default" property="isDefault"/>
+ <result column="is_enabled" property="isEnabled"/>
+ <result column="config_json" property="configJson" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/>
+ <result column="doc_link" property="docLink"/>
+ <result column="remark" property="remark"/>
+ <result column="sort" property="sort"/>
+ <result column="updater" property="updater"/>
+ <result column="update_date" property="updateDate"/>
+ <result column="creator" property="creator"/>
+ <result column="create_date" property="createDate"/>
+ </resultMap>
+ <!-- 鑾峰彇妯″瀷渚涘簲鍣ㄥ瓧娈� -->
+ <select id="getModelCodeList" resultType="String">
+ select model_name from ai_model_config where model_type = #{modelType}
+ <if test="modelName != null">and model_name = #{modelName}</if>
+ </select>
+
+ <!-- 鑾峰彇绗﹀悎鏉′欢鐨凾TS骞冲彴鍒楄〃 -->
+ <select id="getTtsPlatformList" resultType="map">
+ select id, model_name as modelName from ai_model_config
+ where model_type = 'TTS'
+ and JSON_EXTRACT(config_json, '$.type') = 'huoshan_double_stream'
+ </select>
+</mapper>
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/model/ModelProviderDao.xml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/model/ModelProviderDao.xml
new file mode 100644
index 0000000..b17e24f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/model/ModelProviderDao.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="xiaozhi.modules.model.dao.ModelProviderDao">
+ <!-- 鑾峰彇妯″瀷渚涘簲鍣ㄥ瓧娈� -->
+ <select id="getFieldList" resultType="string">
+ select fields from ai_model_provider where model_type = #{modelType} and provider_code = #{providerCode};
+ </select>
+</mapper>
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/security/SysUserTokenDao.xml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/security/SysUserTokenDao.xml
new file mode 100644
index 0000000..4f49414
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/security/SysUserTokenDao.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="xiaozhi.modules.security.dao.SysUserTokenDao">
+
+ <select id="getByToken" resultType="xiaozhi.modules.security.entity.SysUserTokenEntity">
+ select * from sys_user_token where token = #{token}
+ </select>
+
+ <select id="getByUserId" resultType="xiaozhi.modules.security.entity.SysUserTokenEntity">
+ select * from sys_user_token where user_id = #{userId}
+ </select>
+
+ <update id="logout">
+ update sys_user_token set expire_date = #{expireDate} where user_id = #{userId}
+ </update>
+</mapper>
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/sys/SysDictDataDao.xml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/sys/SysDictDataDao.xml
new file mode 100644
index 0000000..7d685c1
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/sys/SysDictDataDao.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="xiaozhi.modules.sys.dao.SysDictDataDao">
+ <select id="getDictDataByType" resultType="xiaozhi.modules.sys.vo.SysDictDataItem">
+ SELECT d.dict_label AS `name`, d.dict_value AS `key`
+ FROM sys_dict_data d
+ LEFT JOIN sys_dict_type t ON d.dict_type_id = t.id
+ WHERE t.dict_type = #{dictType}
+ ORDER BY d.sort ASC
+ </select>
+
+ <select id="getTypeByTypeId" resultType="java.lang.String">
+ SELECT dict_type
+ FROM sys_dict_type
+ WHERE id = #{dictTypeId}
+ </select>
+
+ <select id="getDictTypesByIdList" resultType="java.lang.String">
+ SELECT
+ dict_type
+ FROM
+ sys_dict_type
+ WHERE
+ id IN (
+ SELECT
+ dict_type_id
+ FROM
+ sys_dict_data
+ WHERE
+ id IN
+ <foreach collection="dictDataIdList" item="id" open="(" separator="," close=")">
+ #{id}
+ </foreach>
+ )
+ </select>
+</mapper>
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/sys/SysDictTypeDao.xml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/sys/SysDictTypeDao.xml
new file mode 100644
index 0000000..825db02
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/sys/SysDictTypeDao.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="xiaozhi.modules.sys.dao.SysDictTypeDao">
+
+</mapper>
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/sys/SysParamsDao.xml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/sys/SysParamsDao.xml
new file mode 100644
index 0000000..8faf431
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/sys/SysParamsDao.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="xiaozhi.modules.sys.dao.SysParamsDao">
+
+ <!-- 鏍规嵁鍙傛暟缂栫爜锛屾煡璇alue -->
+ <select id="getValueByCode" resultType="String">
+ select param_value from sys_params where param_code = #{paramCode}
+ </select>
+
+ <!-- 鑾峰彇鍙傛暟缂栫爜鍒楄〃 -->
+ <select id="getParamCodeList" resultType="String">
+ select param_code from sys_params where id in
+ <foreach item="id" collection="array" open="(" separator="," close=")">
+ #{id}
+ </foreach>
+ </select>
+
+ <!-- 鏍规嵁鍙傛暟缂栫爜锛屾洿鏂皏alue -->
+ <update id="updateValueByCode">
+ update sys_params set param_value = #{paramValue} where param_code = #{paramCode}
+ </update>
+</mapper>
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/sys/SysUserDao.xml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/sys/SysUserDao.xml
new file mode 100644
index 0000000..007e91c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/sys/SysUserDao.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="xiaozhi.modules.sys.dao.SysUserDao">
+
+</mapper>
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/voiceclone/VoiceCloneDao.xml b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/voiceclone/VoiceCloneDao.xml
new file mode 100644
index 0000000..50ff485
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/main/resources/mapper/voiceclone/VoiceCloneDao.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="xiaozhi.modules.voiceclone.dao.VoiceCloneDao">
+ <select id="getTrainSuccess" resultType="xiaozhi.modules.model.dto.VoiceDTO">
+ select id, name, voice_id as voiceDemo, languages
+ from ai_voice_clone
+ where model_id = #{modelId} and user_id = #{userId} and train_status = 2
+ </select>
+</mapper>
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/common/redis/RedisSerializationTest.java b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/common/redis/RedisSerializationTest.java
new file mode 100644
index 0000000..26610ae
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/common/redis/RedisSerializationTest.java
@@ -0,0 +1,57 @@
+package xiaozhi.common.redis;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.data.redis.serializer.RedisSerializer;
+
+import xiaozhi.common.utils.JsonUtils;
+import xiaozhi.modules.sys.vo.SysDictDataItem;
+
+class RedisSerializationTest {
+ private final RedisSerializer<Object> serializer = RedisSerializer.json();
+
+ @Test
+ void serverConfigRoundTripRestoresStringKeyedMaps() {
+ Map<String, Object> features = new HashMap<>();
+ features.put("addressBook", Map.of("enabled", true));
+ Map<String, Object> config = new HashMap<>();
+ config.put("features", features);
+
+ Object restored = roundTrip(config);
+ Map<String, Object> restoredConfig = JsonUtils.toStringObjectMap(restored);
+ Map<String, Object> restoredFeatures = JsonUtils.toStringObjectMap(restoredConfig.get("features"));
+ Map<String, Object> addressBook = JsonUtils.toStringObjectMap(restoredFeatures.get("addressBook"));
+
+ assertEquals(true, addressBook.get("enabled"));
+ }
+
+ @Test
+ void dictionaryListRoundTripRestoresDtoElements() {
+ SysDictDataItem item = new SysDictDataItem();
+ item.setName("enabled");
+ item.setKey("1");
+
+ Object restored = roundTrip(new ArrayList<>(List.of(item)));
+ List<SysDictDataItem> restoredItems = JsonUtils.toList(restored, SysDictDataItem.class);
+
+ assertInstanceOf(SysDictDataItem.class, restoredItems.get(0));
+ assertEquals("enabled", restoredItems.get(0).getName());
+ assertEquals("1", restoredItems.get(0).getKey());
+ }
+
+ private Object roundTrip(Object value) {
+ byte[] bytes = serializer.serialize(value);
+ assertNotNull(bytes);
+ Object restored = serializer.deserialize(bytes);
+ assertNotNull(restored);
+ return restored;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/common/service/impl/BaseServiceImplTest.java b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/common/service/impl/BaseServiceImplTest.java
new file mode 100644
index 0000000..50c2065
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/common/service/impl/BaseServiceImplTest.java
@@ -0,0 +1,219 @@
+package xiaozhi.common.service.impl;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.CALLS_REAL_METHODS;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiFunction;
+
+import org.apache.ibatis.binding.MapperMethod;
+import org.apache.ibatis.executor.BatchResult;
+import org.apache.ibatis.logging.nologging.NoLoggingImpl;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.session.ExecutorType;
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.MockedStatic;
+import org.springframework.transaction.support.TransactionSynchronizationManager;
+
+import com.baomidou.mybatisplus.core.enums.SqlMethod;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
+
+class BaseServiceImplTest {
+ private static final String INSERT_STATEMENT = TestMapper.class.getName() + ".insert";
+ private static final String UPDATE_STATEMENT = TestMapper.class.getName() + ".updateById";
+
+ private final TestService service = new TestService();
+
+ @AfterEach
+ void clearTransactionSynchronization() {
+ if (TransactionSynchronizationManager.isSynchronizationActive()) {
+ TransactionSynchronizationManager.clearSynchronization();
+ }
+ }
+
+ @Test
+ void insertBatchFlushesAtTheRequestedBatchSizeAndCommitsWithoutATransaction() {
+ SqlSessionFactory sqlSessionFactory = mock(SqlSessionFactory.class);
+ SqlSession sqlSession = mock(SqlSession.class);
+ when(sqlSessionFactory.openSession(ExecutorType.BATCH)).thenReturn(sqlSession);
+ when(sqlSession.insert(eq(INSERT_STATEMENT), any(TestEntity.class))).thenReturn(1);
+ when(sqlSession.flushStatements())
+ .thenReturn(List.of(batchResult(1, 1)))
+ .thenReturn(List.of(batchResult(1)));
+
+ TestEntity first = new TestEntity(1L);
+ TestEntity second = new TestEntity(2L);
+ TestEntity third = new TestEntity(3L);
+
+ try (MockedStatic<SqlHelper> sqlHelper = sqlHelperUsing(sqlSessionFactory)) {
+ assertTrue(service.insertBatch(List.of(first, second, third), 2));
+ }
+
+ verify(sqlSession).insert(INSERT_STATEMENT, first);
+ verify(sqlSession).insert(INSERT_STATEMENT, second);
+ verify(sqlSession).insert(INSERT_STATEMENT, third);
+ verify(sqlSession, times(2)).flushStatements();
+ verify(sqlSession).commit(true);
+ verify(sqlSession).close();
+ }
+
+ @Test
+ void updateBatchByIdPassesEachEntityAndReturnsSuccessfulFlushResult() {
+ SqlSessionFactory sqlSessionFactory = mock(SqlSessionFactory.class);
+ SqlSession sqlSession = mock(SqlSession.class);
+ when(sqlSessionFactory.openSession(ExecutorType.BATCH)).thenReturn(sqlSession);
+ when(sqlSession.update(eq(UPDATE_STATEMENT), any())).thenReturn(1);
+ when(sqlSession.flushStatements()).thenReturn(List.of(batchResult(1, 1)));
+
+ TestEntity first = new TestEntity(1L);
+ TestEntity second = new TestEntity(2L);
+
+ try (MockedStatic<SqlHelper> sqlHelper = sqlHelperUsing(sqlSessionFactory)) {
+ assertTrue(service.updateBatchById(List.of(first, second), 10));
+ }
+
+ ArgumentCaptor<Object> params = ArgumentCaptor.forClass(Object.class);
+ verify(sqlSession, times(2)).update(eq(UPDATE_STATEMENT), params.capture());
+ assertSame(first, entityFrom(params.getAllValues().get(0)));
+ assertSame(second, entityFrom(params.getAllValues().get(1)));
+ verify(sqlSession).flushStatements();
+ }
+
+ @Test
+ void batchCallbacksReturnActualAffectedRowCounts() {
+ SqlSession sqlSession = mock(SqlSession.class);
+ when(sqlSession.insert(eq(INSERT_STATEMENT), any(TestEntity.class))).thenReturn(3);
+ when(sqlSession.update(eq(UPDATE_STATEMENT), any())).thenReturn(2);
+ CallbackCapturingTestService capturingService = new CallbackCapturingTestService(sqlSession);
+ TestEntity entity = new TestEntity(1L);
+
+ assertTrue(capturingService.insertBatch(List.of(entity), 7));
+ assertEquals(7, capturingService.batchSize);
+ assertEquals(List.of(3), capturingService.affectedRows);
+
+ assertTrue(capturingService.updateBatchById(List.of(entity), 9));
+ assertEquals(9, capturingService.batchSize);
+ assertEquals(List.of(2), capturingService.affectedRows);
+ }
+
+ @Test
+ void emptyBatchesReturnFalseWithoutOpeningABatchSession() {
+ SqlSessionFactory sqlSessionFactory = mock(SqlSessionFactory.class);
+
+ try (MockedStatic<SqlHelper> sqlHelper = sqlHelperUsing(sqlSessionFactory)) {
+ assertFalse(service.insertBatch(List.of(), 10));
+ assertFalse(service.updateBatchById(List.of(), 10));
+ }
+
+ verifyNoInteractions(sqlSessionFactory);
+ }
+
+ @Test
+ void deleteBatchIdsDelegatesToCompatibleApiAndPreservesAffectedRowResult() {
+ TestMapper mapper = mock(TestMapper.class);
+ List<Long> ids = List.of(1L, 2L);
+ service.baseDao = mapper;
+ when(mapper.deleteByIds(ids)).thenReturn(2).thenReturn(0);
+
+ assertTrue(service.deleteBatchIds(ids));
+ assertFalse(service.deleteBatchIds(ids));
+
+ verify(mapper, times(2)).deleteByIds(ids);
+ }
+
+ @Test
+ void activeTransactionSynchronizationUsesTransactionAwareCommitAndLifecycle() {
+ SqlSessionFactory sqlSessionFactory = mock(SqlSessionFactory.class);
+ SqlSession sqlSession = mock(SqlSession.class);
+ when(sqlSessionFactory.openSession(ExecutorType.BATCH)).thenReturn(sqlSession);
+ when(sqlSession.insert(eq(INSERT_STATEMENT), any(TestEntity.class))).thenReturn(1);
+ when(sqlSession.flushStatements()).thenReturn(List.of(batchResult(1)));
+ TransactionSynchronizationManager.initSynchronization();
+
+ try (MockedStatic<SqlHelper> sqlHelper = sqlHelperUsing(sqlSessionFactory)) {
+ assertTrue(service.insertBatch(List.of(new TestEntity(1L)), 1));
+ }
+
+ verify(sqlSession).flushStatements();
+ verify(sqlSession).commit(false);
+ verify(sqlSession, never()).commit(true);
+ verify(sqlSession, never()).close();
+ }
+
+ @SuppressWarnings("deprecation")
+ private static MockedStatic<SqlHelper> sqlHelperUsing(SqlSessionFactory sqlSessionFactory) {
+ MockedStatic<SqlHelper> sqlHelper = mockStatic(SqlHelper.class, CALLS_REAL_METHODS);
+ sqlHelper.when(() -> SqlHelper.sqlSessionFactory(TestEntity.class)).thenReturn(sqlSessionFactory);
+ return sqlHelper;
+ }
+
+ private static BatchResult batchResult(int... updateCounts) {
+ BatchResult result = new BatchResult(mock(MappedStatement.class), "batch");
+ result.setUpdateCounts(updateCounts);
+ return result;
+ }
+
+ private static Object entityFrom(Object parameter) {
+ assertTrue(parameter instanceof MapperMethod.ParamMap<?>);
+ return ((Map<?, ?>) parameter).get(Constants.ENTITY);
+ }
+
+ private interface TestMapper extends BaseMapper<TestEntity> {
+ }
+
+ private record TestEntity(Long id) {
+ }
+
+ private static class TestService extends BaseServiceImpl<TestMapper, TestEntity> {
+ private TestService() {
+ log = new NoLoggingImpl(TestService.class.getName());
+ }
+
+ @Override
+ protected String getSqlStatement(SqlMethod sqlMethod) {
+ return switch (sqlMethod) {
+ case INSERT_ONE -> INSERT_STATEMENT;
+ case UPDATE_BY_ID -> UPDATE_STATEMENT;
+ default -> throw new IllegalArgumentException("Unexpected SQL method: " + sqlMethod);
+ };
+ }
+ }
+
+ private static class CallbackCapturingTestService extends TestService {
+ private final SqlSession sqlSession;
+ private int batchSize;
+ private List<Integer> affectedRows;
+
+ private CallbackCapturingTestService(SqlSession sqlSession) {
+ this.sqlSession = sqlSession;
+ }
+
+ @Override
+ protected <E> boolean executeBatch(Collection<E> list, int batchSize,
+ BiFunction<SqlSession, E, Integer> operation) {
+ this.batchSize = batchSize;
+ this.affectedRows = list.stream().map(entity -> operation.apply(sqlSession, entity)).toList();
+ return true;
+ }
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/common/utils/AESUtilsTest.java b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/common/utils/AESUtilsTest.java
new file mode 100644
index 0000000..c4f3711
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/common/utils/AESUtilsTest.java
@@ -0,0 +1,85 @@
+package xiaozhi.common.utils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+public class AESUtilsTest {
+
+ @Test
+ public void testEncryptAndDecrypt() {
+ String key = "xiaozhi1234567890";
+ String plainText = "Hello, 灏忔櫤!";
+
+ System.out.println("鍘熷鏂囨湰: " + plainText);
+ System.out.println("瀵嗛挜: " + key);
+
+ // 鍔犲瘑
+ String encrypted = AESUtils.encrypt(key, plainText);
+ System.out.println("鍔犲瘑缁撴灉: " + encrypted);
+
+ // 瑙e瘑
+ String decrypted = AESUtils.decrypt(key, encrypted);
+ System.out.println("瑙e瘑缁撴灉: " + decrypted);
+
+ // 楠岃瘉
+ assertEquals(plainText, decrypted, "鍔犺В瀵嗙粨鏋滃簲璇ヤ竴鑷�");
+ System.out.println("鍔犺В瀵嗕竴鑷存��: " + plainText.equals(decrypted));
+ }
+
+ @Test
+ public void testDifferentKeyLengths() {
+ String[] keys = {
+ "1234567890123456", // 16浣�
+ "123456789012345678901234", // 24浣�
+ "12345678901234567890123456789012", // 32浣�
+ "short", // 鐭瘑閽�
+ "verylongkeythatwillbetruncatedto32bytes" // 闀垮瘑閽�
+ };
+
+ String plainText = "娴嬭瘯鏂囨湰";
+
+ for (String key : keys) {
+ String encrypted = AESUtils.encrypt(key, plainText);
+ String decrypted = AESUtils.decrypt(key, encrypted);
+ assertEquals(plainText, decrypted, "瀵嗛挜闀垮害: " + key.length());
+ }
+ }
+
+ @Test
+ public void testSpecialCharacters() {
+ String key = "xiaozhi1234567890";
+ String[] testTexts = {
+ "Hello World",
+ "浣犲ソ涓栫晫",
+ "Hello, 灏忔櫤!",
+ "鐗规畩瀛楃: !@#$%^&*()",
+ "鏁板瓧123鍜屼腑鏂囨贩鍚�",
+ "Emoji: 馃榾馃帀馃殌",
+ "绌哄瓧绗︿覆娴嬭瘯",
+ ""
+ };
+
+ for (String text : testTexts) {
+ String encrypted = AESUtils.encrypt(key, text);
+ String decrypted = AESUtils.decrypt(key, encrypted);
+ assertEquals(text, decrypted, "娴嬭瘯鏂囨湰: " + text);
+ }
+ }
+
+ @Test
+ public void testCrossLanguageCompatibility() {
+ // 杩欎簺鏄疨ython鐗堟湰鐢熸垚鐨勫姞瀵嗙粨鏋滐紝鐢ㄤ簬娴嬭瘯璺ㄨ瑷�鍏煎鎬�
+ String key = "xiaozhi1234567890";
+ String plainText = "Hello, 灏忔櫤!";
+
+ // Python鐗堟湰鐢熸垚鐨勫姞瀵嗙粨鏋滐紙闇�瑕佽繍琛孭ython娴嬭瘯鍚庤幏鍙栵級
+ // String pythonEncrypted = "浠嶱ython娴嬭瘯涓幏鍙栫殑鍔犲瘑缁撴灉";
+ // String decrypted = AESUtils.decrypt(key, pythonEncrypted);
+ // assertEquals(plainText, decrypted, "Java搴旇鑳借В瀵哖ython鍔犲瘑鐨勭粨鏋�");
+
+ // 鐢熸垚Java鍔犲瘑缁撴灉渚汸ython娴嬭瘯
+ String javaEncrypted = AESUtils.encrypt(key, plainText);
+ System.out.println("Java鍔犲瘑缁撴灉渚汸ython娴嬭瘯: " + javaEncrypted);
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/common/utils/JsonUtilsTest.java b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/common/utils/JsonUtilsTest.java
new file mode 100644
index 0000000..c740ad6
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/common/utils/JsonUtilsTest.java
@@ -0,0 +1,77 @@
+package xiaozhi.common.utils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.jupiter.api.Test;
+
+class JsonUtilsTest {
+
+ @Test
+ void parsesTypedMapsAndPreservesNestedCollectionShapes() {
+ Map<String, Object> map = JsonUtils.parseMap(
+ "{\"enabled\":true,\"nested\":{\"items\":[{\"name\":\"tool\"}]}}");
+ Map<?, ?> nested = assertInstanceOf(Map.class, map.get("nested"));
+ List<?> items = assertInstanceOf(List.class, nested.get("items"));
+ Map<?, ?> item = assertInstanceOf(Map.class, items.get(0));
+
+ assertEquals(true, map.get("enabled"));
+ assertEquals("tool", item.get("name"));
+
+ List<Map<String, Object>> maps = JsonUtils.parseMapList(
+ "[{\"name\":\"first\",\"values\":[1,2]},{\"name\":\"second\"}]");
+ assertEquals("first", maps.get(0).get("name"));
+ assertEquals(List.of(1, 2), maps.get(0).get("values"));
+ assertEquals("second", maps.get(1).get("name"));
+ }
+
+ @Test
+ void checkedConvertersAreShallowMutableCopies() {
+ List<Object> nested = new ArrayList<>(List.of(Map.of("name", "tool")));
+ Map<String, Object> source = new LinkedHashMap<>();
+ source.put("nested", nested);
+
+ Map<String, Object> map = JsonUtils.toStringObjectMap(source);
+ List<Map<String, Object>> maps = JsonUtils.toStringObjectMapList(List.of(source));
+ List<String> strings = JsonUtils.toList(Arrays.asList("a", null), String.class);
+
+ assertSame(nested, map.get("nested"));
+ assertSame(nested, maps.get(0).get("nested"));
+ map.put("enabled", true);
+ maps.add(Map.of("name", "second"));
+ strings.add("b");
+ assertEquals(true, map.get("enabled"));
+ assertEquals(2, maps.size());
+ assertEquals(Arrays.asList("a", null, "b"), strings);
+ }
+
+ @Test
+ void preservesNullInputs() {
+ assertNull(JsonUtils.parseMap(""));
+ assertNull(JsonUtils.parseMapList(null));
+ assertNull(JsonUtils.toStringObjectMap(null));
+ assertNull(JsonUtils.toStringObjectMapList(null));
+ assertNull(JsonUtils.toList(null, String.class));
+ }
+
+ @Test
+ void rejectsUnexpectedJsonShapesAndRuntimeTypes() {
+ assertThrows(RuntimeException.class, () -> JsonUtils.parseMap("[]"));
+ assertThrows(RuntimeException.class, () -> JsonUtils.parseMapList("{}"));
+ assertThrows(ClassCastException.class, () -> JsonUtils.toStringObjectMap(List.of()));
+ assertThrows(ClassCastException.class, () -> JsonUtils.toStringObjectMap(Map.of(1, "value")));
+ assertThrows(ClassCastException.class, () -> JsonUtils.toStringObjectMapList(List.of("value")));
+ assertThrows(ClassCastException.class, () -> JsonUtils.toStringObjectMapList(Map.of()));
+ assertThrows(ClassCastException.class, () -> JsonUtils.toList(List.of(1), String.class));
+ assertThrows(ClassCastException.class, () -> JsonUtils.toList(Map.of(), String.class));
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/agent/dto/AgentUpdateDTOTest.java b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/agent/dto/AgentUpdateDTOTest.java
new file mode 100644
index 0000000..8ffe01c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/agent/dto/AgentUpdateDTOTest.java
@@ -0,0 +1,44 @@
+package xiaozhi.modules.agent.dto;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.junit.jupiter.api.Test;
+
+class AgentUpdateDTOTest {
+
+ @Test
+ void functionInfoAcceptsJsonStringParamInfo() {
+ AgentUpdateDTO.FunctionInfo info = new AgentUpdateDTO.FunctionInfo();
+
+ info.setParamInfo("{\"api_key\":\"abc\",\"max_results\":5}");
+
+ assertEquals("abc", info.getParamInfo().get("api_key"));
+ assertEquals(5, info.getParamInfo().get("max_results"));
+ }
+
+ @Test
+ void functionInfoNormalizesMapKeysToStrings() {
+ AgentUpdateDTO.FunctionInfo info = new AgentUpdateDTO.FunctionInfo();
+ Map<Object, Object> params = new LinkedHashMap<>();
+ params.put("api_key", "abc");
+ params.put(42, true);
+
+ info.setParamInfo(params);
+
+ assertEquals("abc", info.getParamInfo().get("api_key"));
+ assertEquals(true, info.getParamInfo().get("42"));
+ }
+
+ @Test
+ void functionInfoFallsBackToEmptyMapForBlankParamInfo() {
+ AgentUpdateDTO.FunctionInfo info = new AgentUpdateDTO.FunctionInfo();
+
+ info.setParamInfo(" ");
+
+ assertTrue(info.getParamInfo().isEmpty());
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/agent/service/impl/AgentSnapshotRedactionRunnerTest.java b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/agent/service/impl/AgentSnapshotRedactionRunnerTest.java
new file mode 100644
index 0000000..42dd244
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/agent/service/impl/AgentSnapshotRedactionRunnerTest.java
@@ -0,0 +1,77 @@
+package xiaozhi.modules.agent.service.impl;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.lang.reflect.Method;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.SmartInitializingSingleton;
+import org.springframework.boot.test.system.CapturedOutput;
+import org.springframework.boot.test.system.OutputCaptureExtension;
+import org.springframework.scheduling.annotation.Scheduled;
+
+import xiaozhi.modules.agent.service.AgentSnapshotService;
+
+@ExtendWith(OutputCaptureExtension.class)
+class AgentSnapshotRedactionRunnerTest {
+
+ @Test
+ void startupRedactionRunsSynchronouslyAndReportsCompensationWindow(CapturedOutput output) {
+ assertTrue(SmartInitializingSingleton.class.isAssignableFrom(AgentSnapshotRedactionRunner.class));
+ AgentSnapshotService service = mock(AgentSnapshotService.class);
+ AgentSnapshotRedactionRunner runner = new AgentSnapshotRedactionRunner(service);
+ when(service.redactLegacySnapshots()).thenReturn(0L);
+
+ runner.afterSingletonsInstantiated();
+
+ verify(service).redactLegacySnapshots();
+ assertTrue(output.getAll().contains("startup pass completed"));
+ assertTrue(output.getAll().contains("starts after 5000 ms and repeats every 15000 ms"));
+ }
+
+ @Test
+ void startupRedactionFailureIsLoggedAndPropagatedToKeepStartupFailClosed(CapturedOutput output) {
+ AgentSnapshotService service = mock(AgentSnapshotService.class);
+ AgentSnapshotRedactionRunner runner = new AgentSnapshotRedactionRunner(service);
+ IllegalStateException failure = new IllegalStateException("database unavailable");
+ when(service.redactLegacySnapshots()).thenThrow(failure);
+
+ IllegalStateException thrown = assertThrows(IllegalStateException.class,
+ runner::afterSingletonsInstantiated);
+
+ assertSame(failure, thrown);
+ assertTrue(output.getAll().contains("blocking application startup before it can accept traffic"));
+ }
+
+ @Test
+ void rollingDeploymentRedactionReportsTriggerCountAndCredentialRotation(CapturedOutput output) {
+ AgentSnapshotService service = mock(AgentSnapshotService.class);
+ AgentSnapshotRedactionRunner runner = new AgentSnapshotRedactionRunner(service);
+ when(service.redactLegacySnapshots()).thenReturn(3L);
+
+ runner.redactLateRollingDeploymentWrites();
+
+ assertTrue(output.getAll().contains("trigger=rolling-deployment migrated=3"));
+ assertTrue(output.getAll().contains("Rotate credentials"));
+ }
+
+ @Test
+ void rollingDeploymentScheduleKeepsLegacyWriteExposureWindowShort() throws Exception {
+ Method method = AgentSnapshotRedactionRunner.class.getMethod("redactLateRollingDeploymentWrites");
+ Scheduled scheduled = method.getAnnotation(Scheduled.class);
+
+ assertNotNull(scheduled);
+ assertEquals(5_000, scheduled.initialDelay());
+ assertEquals(15_000, scheduled.fixedDelay());
+ assertTrue(scheduled.initialDelay() <= 10_000);
+ assertTrue(scheduled.fixedDelay() <= 30_000);
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/agent/service/impl/AgentSnapshotServiceImplTest.java b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/agent/service/impl/AgentSnapshotServiceImplTest.java
new file mode 100644
index 0000000..8a3473e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/agent/service/impl/AgentSnapshotServiceImplTest.java
@@ -0,0 +1,1962 @@
+package xiaozhi.modules.agent.service.impl;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.repository.IRepository;
+import com.fasterxml.jackson.core.type.TypeReference;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.springframework.test.util.ReflectionTestUtils;
+import org.springframework.transaction.annotation.Transactional;
+
+import xiaozhi.common.utils.JsonUtils;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.modules.agent.Enums.AgentSnapshotField;
+import xiaozhi.modules.agent.dao.AgentDao;
+import xiaozhi.modules.agent.dao.AgentSnapshotDao;
+import xiaozhi.modules.agent.dao.AgentTagDao;
+import xiaozhi.modules.agent.dao.AgentTagRelationDao;
+import xiaozhi.modules.agent.dto.AgentCreateDTO;
+import xiaozhi.modules.agent.dto.AgentSnapshotDataDTO;
+import xiaozhi.modules.agent.dto.AgentSnapshotPageDTO;
+import xiaozhi.modules.agent.dto.AgentSnapshotTagDTO;
+import xiaozhi.modules.agent.dto.AgentUpdateDTO;
+import xiaozhi.modules.agent.dto.ContextProviderDTO;
+import xiaozhi.modules.agent.entity.AgentEntity;
+import xiaozhi.modules.agent.entity.AgentPluginMapping;
+import xiaozhi.modules.agent.entity.AgentTemplateEntity;
+import xiaozhi.modules.agent.entity.AgentSnapshotEntity;
+import xiaozhi.modules.agent.entity.AgentTagEntity;
+import xiaozhi.modules.agent.service.AgentChatHistoryService;
+import xiaozhi.modules.agent.service.AgentPluginMappingService;
+import xiaozhi.modules.agent.service.AgentContextProviderService;
+import xiaozhi.modules.agent.service.AgentSnapshotService;
+import xiaozhi.modules.agent.service.AgentTagService;
+import xiaozhi.modules.agent.service.AgentTemplateService;
+import xiaozhi.modules.agent.vo.AgentSnapshotVO;
+import xiaozhi.modules.agent.vo.AgentInfoVO;
+import xiaozhi.modules.correctword.service.CorrectWordFileService;
+import xiaozhi.modules.model.service.ModelProviderService;
+import xiaozhi.modules.timbre.service.TimbreService;
+
+class AgentSnapshotServiceImplTest {
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void normalizeJsonListTreatsDtoAndMapShapesEqually() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method method = AgentSnapshotServiceImpl.class.getDeclaredMethod("normalizeJsonList", List.class);
+ method.setAccessible(true);
+
+ ContextProviderDTO dto = new ContextProviderDTO();
+ dto.setUrl("https://example.com/context");
+ dto.setHeaders(Map.of("Authorization", "Bearer token"));
+
+ Map<String, Object> map = new LinkedHashMap<>();
+ map.put("headers", Map.of("Authorization", "Bearer token"));
+ map.put("url", "https://example.com/context");
+
+ assertEquals(method.invoke(service, List.of(dto)), method.invoke(service, List.of(map)));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void contextProviderOrderIsARealSnapshotChange() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method method = AgentSnapshotServiceImpl.class.getDeclaredMethod("getChangedFields",
+ AgentSnapshotDataDTO.class, AgentSnapshotDataDTO.class);
+ method.setAccessible(true);
+
+ ContextProviderDTO first = new ContextProviderDTO();
+ first.setUrl("https://example.com/first");
+ first.setHeaders(Map.of());
+ ContextProviderDTO second = new ContextProviderDTO();
+ second.setUrl("https://example.com/second");
+ second.setHeaders(Map.of());
+ AgentSnapshotDataDTO current = new AgentSnapshotDataDTO();
+ current.setContextProviders(List.of(first, second));
+ AgentSnapshotDataDTO reordered = new AgentSnapshotDataDTO();
+ reordered.setContextProviders(List.of(second, first));
+
+ List<String> changedFields = (List<String>) method.invoke(service, current, reordered);
+
+ assertEquals(List.of(AgentSnapshotField.CONTEXT_PROVIDERS.getFieldName()), changedFields);
+ }
+
+ @Test
+ void redactSnapshotDataMasksSensitiveFunctionAndHeaderValues() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method method = AgentSnapshotServiceImpl.class.getDeclaredMethod("redactSnapshotData",
+ AgentSnapshotDataDTO.class);
+ method.setAccessible(true);
+
+ AgentSnapshotDataDTO data = buildExtendedSensitiveSnapshot();
+
+ AgentSnapshotDataDTO redacted = (AgentSnapshotDataDTO) method.invoke(service, data);
+ String json = JsonUtils.toJsonString(redacted);
+
+ assertExtendedSecretsAbsent(json);
+ assertTrue(json.contains("__SNAPSHOT_SECRET_REDACTED__"));
+ assertTrue(json.contains("max_tokens"));
+ assertTrue(json.contains("application/json"));
+ }
+
+ @Test
+ void snapshotDetailMasksCookieProxyAuthorizationAndSessionHeaders() throws Exception {
+ AgentSnapshotDao snapshotDao = mock(AgentSnapshotDao.class);
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(snapshotDao, null, null, null, null, null, null,
+ null, null, null);
+ Method method = AgentSnapshotServiceImpl.class.getDeclaredMethod("toVO",
+ AgentSnapshotEntity.class, boolean.class);
+ method.setAccessible(true);
+
+ AgentSnapshotEntity entity = snapshotEntity("snapshot-id", "agent-id", 4, buildExtendedSensitiveSnapshot());
+ when(snapshotDao.selectNextSnapshot("agent-id", 4)).thenReturn(null);
+
+ AgentSnapshotVO detail = (AgentSnapshotVO) method.invoke(service, entity, true);
+ String json = JsonUtils.toJsonString(detail);
+
+ assertExtendedSecretsAbsent(json);
+ assertTrue(json.contains("__SNAPSHOT_SECRET_REDACTED__"));
+ assertTrue(json.contains("application/json"));
+ }
+
+ @Test
+ void snapshotDetailReturnsCurrentPreviewDataAndTokenFromTheSameServerRead() {
+ AgentSnapshotDao snapshotDao = mock(AgentSnapshotDao.class);
+ AgentDao agentDao = mock(AgentDao.class);
+ AgentTagDao tagDao = mock(AgentTagDao.class);
+ AgentContextProviderService contextProviderService = mock(AgentContextProviderService.class);
+ CorrectWordFileService correctWordFileService = mock(CorrectWordFileService.class);
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(snapshotDao, agentDao, tagDao, null, null,
+ contextProviderService, null, null, null, correctWordFileService);
+ ReflectionTestUtils.setField(service, "baseDao", snapshotDao);
+
+ String agentId = "agent-id";
+ String snapshotId = "snapshot-id";
+ AgentInfoVO current = snapshotAgentInfo(agentId, 7L, "current", "current-summary");
+ AgentPluginMapping mapping = new AgentPluginMapping();
+ mapping.setPluginId("plugin");
+ mapping.setParamInfo("{\"api_key\":\"live-secret\",\"visible\":\"kept\","
+ + "\"webhookUrl\":\"https://hooks.slack.com/services/T111/B222/live-path-secret\"}");
+ current.setFunctions(List.of(mapping));
+ AgentEntity lockedAgent = new AgentEntity();
+ lockedAgent.setId(agentId);
+ when(agentDao.selectByIdForUpdate(agentId)).thenReturn(lockedAgent);
+ when(snapshotDao.selectById(snapshotId))
+ .thenReturn(snapshotEntity(snapshotId, agentId, 2, snapshotData("target", "target-summary")));
+ when(snapshotDao.selectNextSnapshot(agentId, 2)).thenReturn(null);
+ when(agentDao.selectAgentInfoById(agentId)).thenReturn(current);
+ when(contextProviderService.getByAgentId(agentId)).thenReturn(null);
+ when(correctWordFileService.getAgentCorrectWordFileIds(agentId)).thenReturn(List.of());
+ when(tagDao.selectByAgentId(agentId)).thenReturn(List.of());
+
+ AgentSnapshotVO detail = service.getSnapshot(agentId, snapshotId);
+
+ assertNotNull(detail.getCurrentSnapshotData());
+ assertEquals(64, detail.getCurrentStateToken().length());
+ String currentJson = JsonUtils.toJsonString(detail.getCurrentSnapshotData());
+ assertFalse(currentJson.contains("live-secret"));
+ assertFalse(currentJson.contains("live-path-secret"));
+ assertTrue(currentJson.contains("__SNAPSHOT_SECRET_REDACTED__"));
+ assertTrue(currentJson.contains("kept"));
+ }
+
+ @Test
+ void legacyRedactionMigrationCleansStructuredAndUrlSecretsWithoutDroppingUnknownFields() {
+ AgentSnapshotDao snapshotDao = mock(AgentSnapshotDao.class);
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(snapshotDao, null, null, null, null, null,
+ null, null, null, null);
+ AgentSnapshotEntity legacy = new AgentSnapshotEntity();
+ legacy.setId("legacy-id");
+ legacy.setRedactionVersion(1);
+ legacy.setSnapshotData(JsonUtils.toJsonString(Map.of(
+ "futureField", Map.of(
+ "kept", true,
+ "primaryEndpoint", "https://api.example.com/hooks/events/status",
+ "secondaryEndpoint", "https://api.example.com/webhooks/events/status",
+ "singleHookEndpoint", "https://api.example.com/hooks/events",
+ "statusEndpoint", "https://api.example.com/webhooks/status",
+ "webhook", "incoming/legacy-token",
+ "description", "incoming/token",
+ "outgoingWebhookUrl", "https://example.com/incoming/legacy-path-secret"),
+ "functions", List.of(Map.of("paramInfo", Map.of(
+ "headers", List.of(Map.of("NAME", "Authorization", "VALUE", "Bearer legacy-secret")),
+ "Cookie", "legacy-cookie",
+ "protocolRelativeEndpoint",
+ "//legacy-relative-user:legacy-relative-pass@example.com/public/path",
+ "endpoint", "https://user:pass@example.com/hook?subscription-key=legacy-key"))))));
+ when(snapshotDao.selectLegacyRedactionBatch(null, 100, 2)).thenReturn(List.of(legacy));
+ when(snapshotDao.selectLegacyRedactionBatch("legacy-id", 100, 2)).thenReturn(List.of());
+ when(snapshotDao.updateRedactedSnapshots(any(), eq(2))).thenReturn(1);
+
+ long migrated = service.redactLegacySnapshots();
+
+ verify(snapshotDao).updateRedactedSnapshots(
+ argThat(snapshots -> snapshots.size() == 1 && "legacy-id".equals(snapshots.get(0).getId())), eq(2));
+ assertEquals(1, migrated);
+ assertFalse(legacy.getSnapshotData().contains("legacy-secret"));
+ assertFalse(legacy.getSnapshotData().contains("legacy-cookie"));
+ assertFalse(legacy.getSnapshotData().contains("legacy-key"));
+ assertFalse(legacy.getSnapshotData().contains("user:pass"));
+ assertFalse(legacy.getSnapshotData().contains("legacy-path-secret"));
+ assertFalse(legacy.getSnapshotData().contains("legacy-relative-user"));
+ assertFalse(legacy.getSnapshotData().contains("legacy-relative-pass"));
+ assertFalse(legacy.getSnapshotData().contains("incoming/legacy-token"));
+ assertTrue(legacy.getSnapshotData().contains("futureField"));
+ assertTrue(legacy.getSnapshotData().contains("\"kept\":true"));
+ assertTrue(legacy.getSnapshotData().contains("https://api.example.com/hooks/events/status"));
+ assertTrue(legacy.getSnapshotData().contains("https://api.example.com/webhooks/events/status"));
+ assertTrue(legacy.getSnapshotData().contains("https://api.example.com/hooks/events"));
+ assertTrue(legacy.getSnapshotData().contains("https://api.example.com/webhooks/status"));
+ assertTrue(legacy.getSnapshotData().contains("//__SNAPSHOT_SECRET_REDACTED__@example.com/public/path"));
+ assertTrue(legacy.getSnapshotData().contains("incoming/__SNAPSHOT_SECRET_REDACTED__"));
+ assertTrue(legacy.getSnapshotData().contains("incoming/token"));
+ assertTrue(legacy.getSnapshotData().contains("__SNAPSHOT_SECRET_REDACTED__"));
+ }
+
+ @Test
+ void preserveCurrentSensitiveValuesKeepsCurrentSecretsWhenRestoringSnapshot() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method method = AgentSnapshotServiceImpl.class.getDeclaredMethod("preserveCurrentSensitiveValues",
+ AgentSnapshotDataDTO.class, AgentSnapshotDataDTO.class);
+ method.setAccessible(true);
+
+ AgentSnapshotDataDTO snapshot = buildSnapshot("old-key", "old-token", "old-value");
+ AgentSnapshotDataDTO current = buildSnapshot("current-key", "current-token", "current-value");
+
+ AgentSnapshotDataDTO restored = (AgentSnapshotDataDTO) method.invoke(service, snapshot, current);
+
+ assertEquals("current-key", restored.getFunctions().get(0).getParamInfo().get("api_key"));
+ assertEquals("old-value", restored.getFunctions().get(0).getParamInfo().get("description"));
+ assertEquals("current-token", restored.getContextProviders().get(0).getHeaders().get("Authorization"));
+ }
+
+ @Test
+ void redactSnapshotDataMasksStructuredHeadersAndUrlCredentials() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method method = AgentSnapshotServiceImpl.class.getDeclaredMethod("redactSnapshotData",
+ AgentSnapshotDataDTO.class);
+ method.setAccessible(true);
+
+ Map<String, Object> params = new HashMap<>();
+ params.put("headers", List.of(
+ Map.of("key", "Authorization", "value", "Bearer structured-secret"),
+ Map.of("name", "Cookie", "value", "cookie=structured-cookie")));
+ params.put("callbackUrl",
+ "https://function-user:function-pass@example.com/hook?access_token=function-token#function-fragment");
+ params.put("endpoint",
+ "https://example.com/signed?locale=zh-CN&X-Amz-Signature=endpoint-signature");
+ params.put("transport",
+ "https://example.com/transport?mode=push&key=transport-key");
+ params.put("mirrors", List.of(
+ "https://example.com/nested?locale=en&token=nested-token"));
+ AgentSnapshotDataDTO data = new AgentSnapshotDataDTO();
+ data.setFunctions(List.of(functionInfo("plugin", params)));
+ ContextProviderDTO provider = new ContextProviderDTO();
+ provider.setUrl("https://provider-user:provider-pass@example.com/context?token=provider-token#provider-fragment");
+ provider.setHeaders(Map.of());
+ data.setContextProviders(List.of(provider));
+
+ AgentSnapshotDataDTO redacted = (AgentSnapshotDataDTO) method.invoke(service, data);
+ String json = JsonUtils.toJsonString(redacted);
+
+ for (String secret : List.of("structured-secret", "structured-cookie", "function-user", "function-pass",
+ "function-token", "function-fragment", "provider-user", "provider-pass", "provider-token",
+ "provider-fragment", "endpoint-signature", "transport-key", "nested-token")) {
+ assertFalse(json.contains(secret), () -> "Sensitive value leaked: " + secret);
+ }
+ assertEquals("https://__SNAPSHOT_SECRET_REDACTED__@example.com/context"
+ + "?token=__SNAPSHOT_SECRET_REDACTED__#__SNAPSHOT_SECRET_REDACTED__",
+ redacted.getContextProviders().get(0).getUrl());
+ assertEquals("https://example.com/signed?locale=zh-CN&X-Amz-Signature=__SNAPSHOT_SECRET_REDACTED__",
+ redacted.getFunctions().get(0).getParamInfo().get("endpoint"));
+ assertEquals("https://example.com/transport?mode=push&key=__SNAPSHOT_SECRET_REDACTED__",
+ redacted.getFunctions().get(0).getParamInfo().get("transport"));
+ assertTrue(json.contains("__SNAPSHOT_SECRET_REDACTED__"));
+ }
+
+ @Test
+ void capabilityUrlPathsAreRedactedPreciselyAndIdempotently() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method method = AgentSnapshotServiceImpl.class.getDeclaredMethod("redactSensitiveUrl",
+ String.class, String.class);
+ method.setAccessible(true);
+
+ Map<String, String> cases = new LinkedHashMap<>();
+ cases.put("https://hooks.slack.com/services/T111/B222/slack-secret",
+ "https://hooks.slack.com/services/T111/B222/__SNAPSHOT_SECRET_REDACTED__");
+ cases.put("https://discord.com/api/v10/webhooks/123456/discord-secret",
+ "https://discord.com/api/v10/webhooks/123456/__SNAPSHOT_SECRET_REDACTED__");
+ cases.put("https://api.telegram.org/bot123456:telegram-secret/sendMessage",
+ "https://api.telegram.org/bot123456:__SNAPSHOT_SECRET_REDACTED__/sendMessage");
+ cases.put("https://hooks.zapier.com/hooks/catch/987/generic-secret",
+ "https://hooks.zapier.com/hooks/__SNAPSHOT_SECRET_REDACTED__");
+ cases.put("/api/webhooks/relative-secret/continuation",
+ "/api/webhooks/__SNAPSHOT_SECRET_REDACTED__");
+
+ for (Map.Entry<String, String> entry : cases.entrySet()) {
+ String redacted = (String) method.invoke(service, entry.getKey(), "endpoint");
+ assertEquals(entry.getValue(), redacted);
+ assertEquals(redacted, method.invoke(service, redacted, "endpoint"));
+ }
+
+ assertEquals("https://example.com/incoming/__SNAPSHOT_SECRET_REDACTED__",
+ method.invoke(service, "https://example.com/incoming/key-secret", "outgoingWebhookUrl"));
+ assertEquals("https://api.example.com/v1/users/123/preferences",
+ method.invoke(service, "https://api.example.com/v1/users/123/preferences", "endpoint"));
+ assertEquals("https://api.example.com/hooks/events/status",
+ method.invoke(service, "https://api.example.com/hooks/events/status", "endpoint"));
+ assertEquals("https://api.example.com/webhooks/events/status",
+ method.invoke(service, "https://api.example.com/webhooks/events/status", "endpoint"));
+ assertEquals("https://api.example.com/hooks/events",
+ method.invoke(service, "https://api.example.com/hooks/events", "endpoint"));
+ assertEquals("https://api.example.com/webhooks/status",
+ method.invoke(service, "https://api.example.com/webhooks/status", "endpoint"));
+ assertEquals("https://api.example.com/hooks/__SNAPSHOT_SECRET_REDACTED__",
+ method.invoke(service, "https://api.example.com/hooks/access-token-value", "endpoint"));
+ assertEquals("https://api.example.com/webhooks/__SNAPSHOT_SECRET_REDACTED__",
+ method.invoke(service, "https://api.example.com/webhooks/aB3dE5fG7hI9jK1mN3pQ5rS7", "endpoint"));
+ assertEquals("/api/v1/agents/42",
+ method.invoke(service, "/api/v1/agents/42", "endpoint"));
+ assertEquals("incoming/__SNAPSHOT_SECRET_REDACTED__",
+ method.invoke(service, "incoming/token", "webhook"));
+ assertEquals("incoming/token",
+ method.invoke(service, "incoming/token", "description"));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void pathCapabilityRotationDoesNotChangeSnapshotStateButPublicIdentityDoes() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method changedMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("getChangedFields",
+ AgentSnapshotDataDTO.class, AgentSnapshotDataDTO.class);
+ changedMethod.setAccessible(true);
+
+ AgentSnapshotDataDTO current = new AgentSnapshotDataDTO();
+ current.setFunctions(List.of(functionInfo("plugin", Map.of(
+ "webhookUrl", "https://hooks.slack.com/services/T111/B222/first-secret",
+ "endpoint", "/api/webhooks/first-relative-secret/continuation"))));
+ AgentSnapshotDataDTO rotated = new AgentSnapshotDataDTO();
+ rotated.setFunctions(List.of(functionInfo("plugin", Map.of(
+ "webhookUrl", "https://hooks.slack.com/services/T111/B222/second-secret",
+ "endpoint", "/api/webhooks/second-relative-secret/continuation"))));
+ AgentSnapshotDataDTO differentPublicIdentity = new AgentSnapshotDataDTO();
+ differentPublicIdentity.setFunctions(List.of(functionInfo("plugin", Map.of(
+ "webhookUrl", "https://hooks.slack.com/services/T111/B999/second-secret",
+ "endpoint", "/api/webhooks/second-relative-secret/continuation"))));
+
+ assertTrue(((List<String>) changedMethod.invoke(service, current, rotated)).isEmpty());
+ assertEquals(currentStateToken(service, current), currentStateToken(service, rotated));
+ assertEquals(List.of(AgentSnapshotField.FUNCTIONS.getFieldName()),
+ changedMethod.invoke(service, current, differentPublicIdentity));
+ assertFalse(currentStateToken(service, current).equals(currentStateToken(service, differentPublicIdentity)));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void nestedWebhookSemanticKeysPropagateAcrossRedactionComparisonAndRestore() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method redactMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("redactSnapshotData",
+ AgentSnapshotDataDTO.class);
+ redactMethod.setAccessible(true);
+ Method changedMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("getChangedFields",
+ AgentSnapshotDataDTO.class, AgentSnapshotDataDTO.class);
+ changedMethod.setAccessible(true);
+ Method preserveMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("preserveCurrentSensitiveValues",
+ AgentSnapshotDataDTO.class, AgentSnapshotDataDTO.class);
+ preserveMethod.setAccessible(true);
+ Method containsMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("containsSensitiveMaterial",
+ Object.class);
+ containsMethod.setAccessible(true);
+
+ List<Map<String, Object>> nestedCases = List.of(
+ Map.of("webhookConfig", Map.of("value",
+ "https://capability.example.com/public/nested-secret-one")),
+ Map.of("deliveryWebhook", Map.of("options", Map.of("target",
+ "https://capability.example.com/public/nested-secret-two"))),
+ Map.of("config", Map.of("webhookUrl",
+ "https://capability.example.com/public/nested-secret-three")));
+ for (Map<String, Object> params : nestedCases) {
+ AgentSnapshotDataDTO candidate = new AgentSnapshotDataDTO();
+ candidate.setFunctions(List.of(functionInfo("plugin", params)));
+ AgentSnapshotDataDTO redacted = (AgentSnapshotDataDTO) redactMethod.invoke(service, candidate);
+ String json = JsonUtils.toJsonString(redacted);
+ for (String secret : List.of("nested-secret-one", "nested-secret-two", "nested-secret-three")) {
+ assertFalse(json.contains(secret), () -> "Nested webhook capability leaked: " + secret);
+ }
+ assertTrue(json.contains("__SNAPSHOT_SECRET_REDACTED__"));
+ }
+
+ AgentSnapshotDataDTO first = nestedWebhookSnapshot("first-current-secret");
+ AgentSnapshotDataDTO rotated = nestedWebhookSnapshot("rotated-current-secret");
+ assertTrue(((List<String>) changedMethod.invoke(service, first, rotated)).isEmpty());
+ assertEquals(currentStateToken(service, first), currentStateToken(service, rotated));
+
+ AgentSnapshotDataDTO target = (AgentSnapshotDataDTO) redactMethod.invoke(service,
+ nestedWebhookSnapshot("historical-secret"));
+ AgentSnapshotDataDTO restored = (AgentSnapshotDataDTO) preserveMethod.invoke(service, target, rotated);
+ Map<String, Object> webhookConfig = (Map<String, Object>) restored.getFunctions().get(0)
+ .getParamInfo().get("webhookConfig");
+ assertEquals("https://capability.example.com/public/rotated-current-secret", webhookConfig.get("value"));
+ assertTrue((Boolean) containsMethod.invoke(service,
+ target.getFunctions().get(0).getParamInfo()));
+ }
+
+ @Test
+ void restoringPathCapabilitiesCopiesOnlyCurrentSecretAndRejectsUnsafeIdentity() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method redactMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("redactSensitiveUrl",
+ String.class, String.class);
+ redactMethod.setAccessible(true);
+ Method preserveMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("preserveCurrentSensitiveUrl",
+ String.class, String.class, String.class);
+ preserveMethod.setAccessible(true);
+
+ String target = (String) redactMethod.invoke(service,
+ "https://hooks.slack.com/services/T111/B222/historical-secret", "webhookUrl");
+ assertEquals("https://hooks.slack.com/services/T111/B222/current-secret",
+ preserveMethod.invoke(service, target,
+ "https://hooks.slack.com/services/T111/B222/current-secret", "webhookUrl"));
+ String relativeTarget = (String) redactMethod.invoke(service,
+ "/api/webhooks/historical-secret/continuation", "endpoint");
+ assertEquals("/api/webhooks/current-relative/callback",
+ preserveMethod.invoke(service, relativeTarget,
+ "/api/webhooks/current-relative/callback", "endpoint"));
+ String bareRelativeTarget = (String) redactMethod.invoke(service,
+ "incoming/historical-token", "webhook");
+ assertEquals("incoming/current-token",
+ preserveMethod.invoke(service, bareRelativeTarget,
+ "incoming/current-token", "webhook"));
+
+ InvocationTargetException mismatch = assertThrows(InvocationTargetException.class,
+ () -> preserveMethod.invoke(service, target,
+ "https://hooks.slack.com/services/T111/B999/current-secret", "webhookUrl"));
+ assertTrue(mismatch.getCause() instanceof RenException);
+ InvocationTargetException missing = assertThrows(InvocationTargetException.class,
+ () -> preserveMethod.invoke(service, target, null, "webhookUrl"));
+ assertTrue(missing.getCause() instanceof RenException);
+ InvocationTargetException placeholder = assertThrows(InvocationTargetException.class,
+ () -> preserveMethod.invoke(service, target,
+ "https://hooks.slack.com/services/T111/B222/__SNAPSHOT_SECRET_REDACTED__", "webhookUrl"));
+ assertTrue(placeholder.getCause() instanceof RenException);
+ InvocationTargetException relativeMismatch = assertThrows(InvocationTargetException.class,
+ () -> preserveMethod.invoke(service, relativeTarget,
+ "/different/webhooks/current-relative/callback", "endpoint"));
+ assertTrue(relativeMismatch.getCause() instanceof RenException);
+ InvocationTargetException bareRelativeMismatch = assertThrows(InvocationTargetException.class,
+ () -> preserveMethod.invoke(service, bareRelativeTarget,
+ "outgoing/current-token", "webhook"));
+ assertTrue(bareRelativeMismatch.getCause() instanceof RenException);
+ }
+
+ @Test
+ void duplicatePublicPathIdentitiesAreRejectedInsteadOfGuessingASecret() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method redactMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("redactSnapshotData",
+ AgentSnapshotDataDTO.class);
+ redactMethod.setAccessible(true);
+ Method preserveMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("preserveCurrentSensitiveValues",
+ AgentSnapshotDataDTO.class, AgentSnapshotDataDTO.class);
+ preserveMethod.setAccessible(true);
+
+ AgentSnapshotDataDTO target = new AgentSnapshotDataDTO();
+ target.setFunctions(List.of(functionInfo("plugin", Map.of("webhooks", List.of(
+ "https://hooks.slack.com/services/T111/B222/historical-secret")))));
+ target = (AgentSnapshotDataDTO) redactMethod.invoke(service, target);
+ AgentSnapshotDataDTO current = new AgentSnapshotDataDTO();
+ current.setFunctions(List.of(functionInfo("plugin", Map.of("webhooks", List.of(
+ "https://hooks.slack.com/services/T111/B222/current-one",
+ "https://hooks.slack.com/services/T111/B222/current-two")))));
+
+ AgentSnapshotDataDTO redactedTarget = target;
+ InvocationTargetException ambiguous = assertThrows(InvocationTargetException.class,
+ () -> preserveMethod.invoke(service, redactedTarget, current));
+ assertTrue(ambiguous.getCause() instanceof RenException);
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void sensitiveListItemsWithoutNamedIdsUseUniquePublicFingerprint() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method redactMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("redactSnapshotData",
+ AgentSnapshotDataDTO.class);
+ redactMethod.setAccessible(true);
+ Method preserveMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("preserveCurrentSensitiveValues",
+ AgentSnapshotDataDTO.class, AgentSnapshotDataDTO.class);
+ preserveMethod.setAccessible(true);
+
+ AgentSnapshotDataDTO target = new AgentSnapshotDataDTO();
+ target.setFunctions(List.of(functionInfo("plugin", Map.of("destinations", List.of(
+ Map.of("host", "alpha.example.com", "api_key", "historical-secret"))))));
+ target = (AgentSnapshotDataDTO) redactMethod.invoke(service, target);
+
+ AgentSnapshotDataDTO current = new AgentSnapshotDataDTO();
+ current.setFunctions(List.of(functionInfo("plugin", Map.of("destinations", List.of(
+ Map.of("host", "alpha.example.com", "api_key", "current-secret"))))));
+ AgentSnapshotDataDTO restored = (AgentSnapshotDataDTO) preserveMethod.invoke(service, target, current);
+ List<Map<String, Object>> destinations = (List<Map<String, Object>>) restored.getFunctions().get(0)
+ .getParamInfo().get("destinations");
+ assertEquals("current-secret", destinations.get(0).get("api_key"));
+
+ AgentSnapshotDataDTO ambiguousCurrent = new AgentSnapshotDataDTO();
+ ambiguousCurrent.setFunctions(List.of(functionInfo("plugin", Map.of("destinations", List.of(
+ Map.of("host", "alpha.example.com", "api_key", "first-current-secret"),
+ Map.of("host", "alpha.example.com", "api_key", "second-current-secret"))))));
+ AgentSnapshotDataDTO redactedTarget = target;
+ InvocationTargetException ambiguous = assertThrows(InvocationTargetException.class,
+ () -> preserveMethod.invoke(service, redactedTarget, ambiguousCurrent));
+ assertTrue(ambiguous.getCause() instanceof RenException);
+ }
+
+ @Test
+ void contextProvidersMatchPathCapabilitiesByUniquePublicIdentity() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method redactMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("redactSnapshotData",
+ AgentSnapshotDataDTO.class);
+ redactMethod.setAccessible(true);
+ Method preserveMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("preserveCurrentSensitiveValues",
+ AgentSnapshotDataDTO.class, AgentSnapshotDataDTO.class);
+ preserveMethod.setAccessible(true);
+
+ AgentSnapshotDataDTO target = new AgentSnapshotDataDTO();
+ ContextProviderDTO targetProvider = new ContextProviderDTO();
+ targetProvider.setUrl("https://discord.com/api/webhooks/123456/historical-secret");
+ targetProvider.setHeaders(Map.of());
+ target.setContextProviders(List.of(targetProvider));
+ target = (AgentSnapshotDataDTO) redactMethod.invoke(service, target);
+
+ AgentSnapshotDataDTO current = new AgentSnapshotDataDTO();
+ ContextProviderDTO currentProvider = new ContextProviderDTO();
+ currentProvider.setUrl("https://discord.com/api/webhooks/123456/current-secret");
+ currentProvider.setHeaders(Map.of());
+ current.setContextProviders(List.of(currentProvider));
+ AgentSnapshotDataDTO restored = (AgentSnapshotDataDTO) preserveMethod.invoke(service, target, current);
+ assertEquals("https://discord.com/api/webhooks/123456/current-secret",
+ restored.getContextProviders().get(0).getUrl());
+
+ currentProvider.setUrl("https://discord.com/api/webhooks/999999/current-secret");
+ AgentSnapshotDataDTO redactedTarget = target;
+ InvocationTargetException mismatch = assertThrows(InvocationTargetException.class,
+ () -> preserveMethod.invoke(service, redactedTarget, current));
+ assertTrue(mismatch.getCause() instanceof RenException);
+ }
+
+ @Test
+ void snapshotPayloadReaderIgnoresFutureFieldsWithoutChangingGlobalJsonValidation() {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ String payload = "{\"agentName\":\"future-safe\",\"futureField\":{\"enabled\":true}}";
+
+ AgentSnapshotDataDTO parsed = ReflectionTestUtils.invokeMethod(service, "parseSnapshotData", payload);
+
+ assertNotNull(parsed);
+ assertEquals("future-safe", parsed.getAgentName());
+ assertThrows(RuntimeException.class, () -> JsonUtils.parseObject(payload, AgentSnapshotDataDTO.class));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void preserveSensitiveUrlListsByUrlIdentityInsteadOfIndex() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method redactMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("redactSnapshotData",
+ AgentSnapshotDataDTO.class);
+ redactMethod.setAccessible(true);
+ Method preserveMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("preserveCurrentSensitiveValues",
+ AgentSnapshotDataDTO.class, AgentSnapshotDataDTO.class);
+ preserveMethod.setAccessible(true);
+
+ AgentSnapshotDataDTO target = new AgentSnapshotDataDTO();
+ target.setFunctions(List.of(functionInfo("plugin", Map.of("mirrors", List.of(
+ "https://a.example.com/hook?token=old-a&locale=en",
+ "https://b.example.com/hook?token=old-b&locale=en")))));
+ target = (AgentSnapshotDataDTO) redactMethod.invoke(service, target);
+ AgentSnapshotDataDTO current = new AgentSnapshotDataDTO();
+ current.setFunctions(List.of(functionInfo("plugin", Map.of("mirrors", List.of(
+ "https://b.example.com/hook?token=current-b&locale=de",
+ "https://a.example.com/hook?token=current-a&locale=de")))));
+
+ AgentSnapshotDataDTO restored = (AgentSnapshotDataDTO) preserveMethod.invoke(service, target, current);
+ List<String> mirrors = (List<String>) restored.getFunctions().get(0).getParamInfo().get("mirrors");
+
+ assertEquals(List.of(
+ "https://a.example.com/hook?token=current-a&locale=en",
+ "https://b.example.com/hook?token=current-b&locale=en"), mirrors);
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void urlComparisonIgnoresOnlySensitiveParameterValues() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method method = AgentSnapshotServiceImpl.class.getDeclaredMethod("getChangedFields",
+ AgentSnapshotDataDTO.class, AgentSnapshotDataDTO.class);
+ method.setAccessible(true);
+
+ AgentSnapshotDataDTO current = new AgentSnapshotDataDTO();
+ current.setFunctions(List.of(functionInfo("plugin", Map.of("endpoint",
+ "https://api.example.com/hook?locale=en&api_key=first&X-Amz-Signature=first-signature"))));
+ AgentSnapshotDataDTO secretOnlyChange = new AgentSnapshotDataDTO();
+ secretOnlyChange.setFunctions(List.of(functionInfo("plugin", Map.of("endpoint",
+ "https://api.example.com/hook?locale=en&api_key=second&X-Amz-Signature=second-signature"))));
+ AgentSnapshotDataDTO publicParameterChange = new AgentSnapshotDataDTO();
+ publicParameterChange.setFunctions(List.of(functionInfo("plugin", Map.of("endpoint",
+ "https://api.example.com/hook?locale=de&api_key=second&X-Amz-Signature=second-signature"))));
+
+ assertTrue(((List<String>) method.invoke(service, current, secretOnlyChange)).isEmpty());
+ assertEquals(List.of(AgentSnapshotField.FUNCTIONS.getFieldName()),
+ method.invoke(service, current, publicParameterChange));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void preserveSensitiveHeaderListsByStableIdentityInsteadOfIndex() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method redactMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("redactSnapshotData",
+ AgentSnapshotDataDTO.class);
+ redactMethod.setAccessible(true);
+ Method preserveMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("preserveCurrentSensitiveValues",
+ AgentSnapshotDataDTO.class, AgentSnapshotDataDTO.class);
+ preserveMethod.setAccessible(true);
+
+ AgentSnapshotDataDTO target = new AgentSnapshotDataDTO();
+ target.setFunctions(List.of(functionInfo("plugin", Map.of("headers", List.of(
+ Map.of("key", "Authorization", "value", "old-auth"),
+ Map.of("name", "Cookie", "value", "old-cookie"))))));
+ target = (AgentSnapshotDataDTO) redactMethod.invoke(service, target);
+ AgentSnapshotDataDTO current = new AgentSnapshotDataDTO();
+ current.setFunctions(List.of(functionInfo("plugin", Map.of("headers", List.of(
+ Map.of("name", "Cookie", "value", "current-cookie"),
+ Map.of("key", "Authorization", "value", "current-auth"))))));
+
+ AgentSnapshotDataDTO restored = (AgentSnapshotDataDTO) preserveMethod.invoke(service, target, current);
+ List<Map<String, Object>> headers = (List<Map<String, Object>>) restored.getFunctions().get(0)
+ .getParamInfo().get("headers");
+
+ assertEquals("Authorization", headers.get(0).get("key"));
+ assertEquals("current-auth", headers.get(0).get("value"));
+ assertEquals("Cookie", headers.get(1).get("name"));
+ assertEquals("current-cookie", headers.get(1).get("value"));
+ assertFalse(JsonUtils.toJsonString(restored).contains("__SNAPSHOT_SECRET_REDACTED__"));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void structuredSensitiveValuesRequireOneEquivalentDiscriminator() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method redactMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("redactSensitiveMap", Map.class);
+ redactMethod.setAccessible(true);
+ Method preserveMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("preserveCurrentSensitiveMap",
+ Map.class, Map.class);
+ preserveMethod.setAccessible(true);
+
+ Map<String, Object> target = (Map<String, Object>) redactMethod.invoke(service,
+ Map.of("key", "Authorization", "value", "historical-secret"));
+ Map<String, Object> restored = (Map<String, Object>) preserveMethod.invoke(service, target,
+ Map.of("key", "authorization", "value", "current-secret"));
+ assertEquals("current-secret", restored.get("value"));
+
+ InvocationTargetException mismatchedType = assertThrows(InvocationTargetException.class,
+ () -> preserveMethod.invoke(service, target,
+ Map.of("key", "Cookie", "value", "cookie-secret")));
+ assertTrue(mismatchedType.getCause() instanceof RenException);
+
+ Map<String, Object> ambiguousTarget = (Map<String, Object>) redactMethod.invoke(service,
+ Map.of("key", "Authorization", "name", "Cookie", "value", "historical-secret"));
+ InvocationTargetException ambiguous = assertThrows(InvocationTargetException.class,
+ () -> preserveMethod.invoke(service, ambiguousTarget,
+ Map.of("key", "Authorization", "name", "Cookie", "value", "current-secret")));
+ assertTrue(ambiguous.getCause() instanceof RenException);
+ }
+
+ @Test
+ void restoringRedactedUrlsUsesTargetLocationAndCurrentCredentials() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method redactMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("redactSnapshotData",
+ AgentSnapshotDataDTO.class);
+ redactMethod.setAccessible(true);
+ Method preserveMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("preserveCurrentSensitiveValues",
+ AgentSnapshotDataDTO.class, AgentSnapshotDataDTO.class);
+ preserveMethod.setAccessible(true);
+
+ AgentSnapshotDataDTO target = new AgentSnapshotDataDTO();
+ target.setFunctions(List.of(functionInfo("plugin", Map.of("callbackUrl",
+ "https://old-user:old-pass@old.example.com/hook"
+ + "?api%5Fkey=old-token&locale=en#token=old-fragment§ion=target"))));
+ ContextProviderDTO targetProvider = new ContextProviderDTO();
+ targetProvider.setUrl("https://old-user:old-pass@api.example.com/context"
+ + "?api_key=old-token&locale=en#sig=old-fragment§ion=target");
+ targetProvider.setHeaders(Map.of());
+ target.setContextProviders(List.of(targetProvider));
+ target = (AgentSnapshotDataDTO) redactMethod.invoke(service, target);
+
+ AgentSnapshotDataDTO current = new AgentSnapshotDataDTO();
+ current.setFunctions(List.of(functionInfo("plugin", Map.of("callbackUrl",
+ "https://current-user:current-pass@old.example.com/hook"
+ + "?api_key=current-token&locale=de#token=current-fragment§ion=current"))));
+ ContextProviderDTO currentProvider = new ContextProviderDTO();
+ currentProvider.setUrl(
+ "https://current-user:current-pass@api.example.com/context"
+ + "?api_key=current-token&locale=de#sig=current-fragment§ion=current");
+ currentProvider.setHeaders(Map.of());
+ current.setContextProviders(List.of(currentProvider));
+
+ AgentSnapshotDataDTO restored = (AgentSnapshotDataDTO) preserveMethod.invoke(service, target, current);
+
+ assertEquals("https://current-user:current-pass@old.example.com/hook"
+ + "?api%5Fkey=current-token&locale=en#token=current-fragment§ion=target",
+ restored.getFunctions().get(0).getParamInfo().get("callbackUrl"));
+ assertEquals("https://current-user:current-pass@api.example.com/context"
+ + "?api_key=current-token&locale=en#sig=current-fragment§ion=target",
+ restored.getContextProviders().get(0).getUrl());
+ assertFalse(JsonUtils.toJsonString(restored).contains("__SNAPSHOT_SECRET_REDACTED__"));
+ }
+
+ @Test
+ void urlSecretRestoreRequiresTheSamePublicSchemeAuthorityAndPath() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method redactMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("redactSensitiveUrl",
+ String.class, String.class);
+ redactMethod.setAccessible(true);
+ Method preserveMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("preserveCurrentSensitiveUrl",
+ String.class, String.class, String.class);
+ preserveMethod.setAccessible(true);
+
+ String target = (String) redactMethod.invoke(service,
+ "https://old-user:old-pass@example.com/callback?token=old-token#old-fragment", "callbackUrl");
+ assertEquals("https://new-user:new-pass@example.com/callback?token=current-token#current-fragment",
+ preserveMethod.invoke(service, target,
+ "https://new-user:new-pass@example.com/callback?token=current-token#current-fragment",
+ "callbackUrl"));
+
+ for (String mismatch : List.of(
+ "https://new-user:new-pass@other.example.com/callback?token=current-token#current-fragment",
+ "https://new-user:new-pass@example.com/other?token=current-token#current-fragment",
+ "http://new-user:new-pass@example.com/callback?token=current-token#current-fragment")) {
+ InvocationTargetException error = assertThrows(InvocationTargetException.class,
+ () -> preserveMethod.invoke(service, target, mismatch, "callbackUrl"));
+ assertTrue(error.getCause() instanceof RenException);
+ }
+ }
+
+ @Test
+ void protocolRelativeUrlsRedactUserInfoAndRequireTheSamePublicBaseOnRestore() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method redactMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("redactSensitiveUrl",
+ String.class, String.class);
+ redactMethod.setAccessible(true);
+ Method preserveMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod("preserveCurrentSensitiveUrl",
+ String.class, String.class, String.class);
+ preserveMethod.setAccessible(true);
+
+ String target = (String) redactMethod.invoke(service,
+ "//old-user:old-pass@example.com/path?token=old-token", "endpoint");
+ assertEquals("//__SNAPSHOT_SECRET_REDACTED__@example.com/path"
+ + "?token=__SNAPSHOT_SECRET_REDACTED__", target);
+ assertEquals("//current-user:current-pass@example.com/path?token=current-token",
+ preserveMethod.invoke(service, target,
+ "//current-user:current-pass@example.com/path?token=current-token", "endpoint"));
+
+ for (String mismatch : List.of(
+ "//current-user:current-pass@other.example.com/path?token=current-token",
+ "//current-user:current-pass@example.com/other?token=current-token",
+ "https://current-user:current-pass@example.com/path?token=current-token")) {
+ InvocationTargetException error = assertThrows(InvocationTargetException.class,
+ () -> preserveMethod.invoke(service, target, mismatch, "endpoint"));
+ assertTrue(error.getCause() instanceof RenException);
+ }
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void restoringOlderStructureDetectsIrreversibleSensitiveRemovals() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method method = AgentSnapshotServiceImpl.class.getDeclaredMethod("preserveCurrentSensitiveValues",
+ AgentSnapshotDataDTO.class, AgentSnapshotDataDTO.class);
+ method.setAccessible(true);
+ Method validateMethod = AgentSnapshotServiceImpl.class.getDeclaredMethod(
+ "validateSensitiveRestoreIsReversible", AgentSnapshotDataDTO.class, AgentSnapshotDataDTO.class);
+ validateMethod.setAccessible(true);
+
+ AgentSnapshotDataDTO noPluginsTarget = new AgentSnapshotDataDTO();
+ noPluginsTarget.setFunctions(List.of());
+ AgentSnapshotDataDTO currentWithPlugin = new AgentSnapshotDataDTO();
+ currentWithPlugin.setFunctions(List.of(functionInfo("new-plugin", Map.of("api_key", "current-secret"))));
+
+ AgentSnapshotDataDTO removedPlugin = (AgentSnapshotDataDTO) method.invoke(service, noPluginsTarget,
+ currentWithPlugin);
+ assertTrue(removedPlugin.getFunctions().isEmpty());
+ InvocationTargetException removedPluginError = assertThrows(InvocationTargetException.class,
+ () -> validateMethod.invoke(service, currentWithPlugin, removedPlugin));
+ assertTrue(removedPluginError.getCause() instanceof RenException);
+
+ AgentSnapshotDataDTO headerTarget = new AgentSnapshotDataDTO();
+ headerTarget.setFunctions(List.of(functionInfo("plugin", Map.of("headers", List.of(
+ Map.of("key", "Content-Type", "value", "application/json"))))));
+ AgentSnapshotDataDTO headerCurrent = new AgentSnapshotDataDTO();
+ headerCurrent.setFunctions(List.of(functionInfo("plugin", Map.of("headers", List.of(
+ Map.of("key", "Authorization", "value", "current-secret"),
+ Map.of("key", "Content-Type", "value", "text/plain"))))));
+
+ AgentSnapshotDataDTO removedHeader = (AgentSnapshotDataDTO) method.invoke(service, headerTarget,
+ headerCurrent);
+ List<Map<String, Object>> headers = (List<Map<String, Object>>) removedHeader.getFunctions().get(0)
+ .getParamInfo().get("headers");
+ assertEquals(1, headers.size());
+ assertEquals("Content-Type", headers.get(0).get("key"));
+ assertEquals("application/json", headers.get(0).get("value"));
+ InvocationTargetException removedHeaderError = assertThrows(InvocationTargetException.class,
+ () -> validateMethod.invoke(service, headerCurrent, removedHeader));
+ assertTrue(removedHeaderError.getCause() instanceof RenException);
+
+ AgentSnapshotDataDTO urlTarget = new AgentSnapshotDataDTO();
+ urlTarget.setFunctions(List.of(functionInfo("plugin", Map.of("endpoint",
+ "https://example.com/hook?locale=en"))));
+ AgentSnapshotDataDTO urlCurrent = new AgentSnapshotDataDTO();
+ urlCurrent.setFunctions(List.of(functionInfo("plugin", Map.of("endpoint",
+ "https://example.com/hook?api_key=current-secret&locale=de"))));
+ AgentSnapshotDataDTO removedUrlSecret = (AgentSnapshotDataDTO) method.invoke(service, urlTarget, urlCurrent);
+ assertEquals("https://example.com/hook?locale=en",
+ removedUrlSecret.getFunctions().get(0).getParamInfo().get("endpoint"));
+ InvocationTargetException removedUrlError = assertThrows(InvocationTargetException.class,
+ () -> validateMethod.invoke(service, urlCurrent, removedUrlSecret));
+ assertTrue(removedUrlError.getCause() instanceof RenException);
+ }
+
+ @Test
+ void ambiguousOrHistoricalRawSensitiveTargetsAreRejectedInsteadOfGuessed() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method method = AgentSnapshotServiceImpl.class.getDeclaredMethod("preserveCurrentSensitiveValues",
+ AgentSnapshotDataDTO.class, AgentSnapshotDataDTO.class);
+ method.setAccessible(true);
+
+ AgentSnapshotDataDTO ambiguous = new AgentSnapshotDataDTO();
+ ambiguous.setFunctions(List.of(functionInfo("plugin", Map.of("headers", List.of(
+ Map.of("value", "__SNAPSHOT_SECRET_REDACTED__"))))));
+ InvocationTargetException ambiguousError = assertThrows(InvocationTargetException.class,
+ () -> method.invoke(service, ambiguous, new AgentSnapshotDataDTO()));
+ assertTrue(ambiguousError.getCause() instanceof RenException);
+
+ AgentSnapshotDataDTO conflictingIdentities = new AgentSnapshotDataDTO();
+ conflictingIdentities.setFunctions(List.of(functionInfo("plugin", Map.of("headers", List.of(
+ Map.of("name", "Shared", "key", "Authorization", "value",
+ "__SNAPSHOT_SECRET_REDACTED__"))))));
+ AgentSnapshotDataDTO conflictingCurrent = new AgentSnapshotDataDTO();
+ conflictingCurrent.setFunctions(List.of(functionInfo("plugin", Map.of("headers", List.of(
+ Map.of("name", "Shared", "key", "Cookie", "value", "cookie-secret"),
+ Map.of("name", "Different", "key", "Authorization", "value", "auth-secret"))))));
+ InvocationTargetException conflictingError = assertThrows(InvocationTargetException.class,
+ () -> method.invoke(service, conflictingIdentities, conflictingCurrent));
+ assertTrue(conflictingError.getCause() instanceof RenException);
+
+ AgentSnapshotDataDTO historicalRaw = new AgentSnapshotDataDTO();
+ historicalRaw.setFunctions(List.of(functionInfo("plugin", Map.of("Authorization", "old-raw-secret"))));
+ AgentSnapshotDataDTO current = new AgentSnapshotDataDTO();
+ current.setFunctions(List.of(functionInfo("plugin", Map.of())));
+ InvocationTargetException rawSecretError = assertThrows(InvocationTargetException.class,
+ () -> method.invoke(service, historicalRaw, current));
+ assertTrue(rawSecretError.getCause() instanceof RenException);
+
+ AgentSnapshotDataDTO historicalRawUrl = new AgentSnapshotDataDTO();
+ historicalRawUrl.setFunctions(List.of(functionInfo("plugin", Map.of("endpoint",
+ "https://example.com/hook?api_key=historical-secret&locale=en"))));
+ AgentSnapshotDataDTO currentUrlWithoutSecret = new AgentSnapshotDataDTO();
+ currentUrlWithoutSecret.setFunctions(List.of(functionInfo("plugin", Map.of("endpoint",
+ "https://example.com/hook?locale=de"))));
+ InvocationTargetException rawUrlSecretError = assertThrows(InvocationTargetException.class,
+ () -> method.invoke(service, historicalRawUrl, currentUrlWithoutSecret));
+ assertTrue(rawUrlSecretError.getCause() instanceof RenException);
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void getChangedFieldsFollowsCentralFieldOrder() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method method = AgentSnapshotServiceImpl.class.getDeclaredMethod("getChangedFields",
+ AgentSnapshotDataDTO.class, AgentUpdateDTO.class);
+ method.setAccessible(true);
+
+ AgentSnapshotDataDTO current = new AgentSnapshotDataDTO();
+ current.setAgentName("old-name");
+ current.setCorrectWordFileIds(List.of("b", "a"));
+
+ AgentUpdateDTO pendingUpdate = new AgentUpdateDTO();
+ pendingUpdate.setAgentName("new-name");
+ pendingUpdate.setCorrectWordFileIds(List.of("a", "b"));
+ AgentUpdateDTO.FunctionInfo function = new AgentUpdateDTO.FunctionInfo();
+ function.setPluginId("rag");
+ function.setParamInfo(Map.of("api_key", "secret-key"));
+ pendingUpdate.setFunctions(List.of(function));
+
+ List<String> changedFields = (List<String>) method.invoke(service, current, pendingUpdate);
+
+ assertEquals(List.of(AgentSnapshotField.AGENT_NAME.getFieldName(),
+ AgentSnapshotField.FUNCTIONS.getFieldName()), changedFields);
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void tagIdsAreNotExposedAsIndependentSnapshotFields() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method method = AgentSnapshotServiceImpl.class.getDeclaredMethod("getChangedFields",
+ AgentSnapshotDataDTO.class, AgentUpdateDTO.class);
+ method.setAccessible(true);
+
+ AgentSnapshotDataDTO current = new AgentSnapshotDataDTO();
+ current.setTagNames(List.of("alpha", "beta"));
+
+ AgentUpdateDTO pendingUpdate = new AgentUpdateDTO();
+ pendingUpdate.setTagNames(List.of("beta", "alpha"));
+ pendingUpdate.setTagIds(List.of("new-id"));
+
+ List<String> changedFields = (List<String>) method.invoke(service, current, pendingUpdate);
+
+ assertFalse(AgentSnapshotField.names().contains("tagIds"));
+ assertFalse(changedFields.contains("tagIds"));
+ assertTrue(changedFields.isEmpty());
+
+ pendingUpdate.setTagNames(List.of("alpha", "gamma"));
+ changedFields = (List<String>) method.invoke(service, current, pendingUpdate);
+
+ assertEquals(List.of(AgentSnapshotField.TAG_NAMES.getFieldName()), changedFields);
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void blankSummaryMemoryValuesAreNotSnapshotChanges() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method method = AgentSnapshotServiceImpl.class.getDeclaredMethod("getChangedFields",
+ AgentSnapshotDataDTO.class, AgentSnapshotDataDTO.class);
+ method.setAccessible(true);
+
+ AgentSnapshotDataDTO current = new AgentSnapshotDataDTO();
+ current.setSummaryMemory(null);
+ AgentSnapshotDataDTO next = new AgentSnapshotDataDTO();
+ next.setSummaryMemory("");
+
+ List<String> changedFields = (List<String>) method.invoke(service, current, next);
+
+ assertFalse(changedFields.contains(AgentSnapshotField.SUMMARY_MEMORY.getFieldName()));
+ assertTrue(changedFields.isEmpty());
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void nullTtsAdvancedValuesDifferFromExplicitZero() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method method = AgentSnapshotServiceImpl.class.getDeclaredMethod("getChangedFields",
+ AgentSnapshotDataDTO.class, AgentSnapshotDataDTO.class);
+ method.setAccessible(true);
+
+ AgentSnapshotDataDTO current = new AgentSnapshotDataDTO();
+ current.setTtsVolume(null);
+ current.setTtsRate(null);
+ current.setTtsPitch(null);
+ AgentSnapshotDataDTO next = new AgentSnapshotDataDTO();
+ next.setTtsVolume(0);
+ next.setTtsRate(0);
+ next.setTtsPitch(0);
+
+ List<String> changedFields = (List<String>) method.invoke(service, current, next);
+
+ assertEquals(List.of(
+ AgentSnapshotField.TTS_VOLUME.getFieldName(),
+ AgentSnapshotField.TTS_RATE.getFieldName(),
+ AgentSnapshotField.TTS_PITCH.getFieldName()), changedFields);
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void redactedSecretsDoNotCreateFalseSnapshotChangeButNonSensitiveValuesDo() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method method = AgentSnapshotServiceImpl.class.getDeclaredMethod("getChangedFields",
+ AgentSnapshotDataDTO.class, AgentSnapshotDataDTO.class);
+ method.setAccessible(true);
+
+ AgentSnapshotDataDTO stored = buildSnapshot(
+ "__SNAPSHOT_SECRET_REDACTED__", "__SNAPSHOT_SECRET_REDACTED__", "same-value");
+ AgentSnapshotDataDTO current = buildSnapshot("current-key", "current-token", "same-value");
+
+ List<String> changedFields = (List<String>) method.invoke(service, stored, current);
+
+ assertTrue(changedFields.isEmpty());
+
+ current.getFunctions().get(0).getParamInfo().put("description", "changed-value");
+ changedFields = (List<String>) method.invoke(service, stored, current);
+
+ assertEquals(List.of(AgentSnapshotField.FUNCTIONS.getFieldName()), changedFields);
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void explicitTtsLanguageIsStoredAsSnapshotChangeEvenWhenItMatchesVoiceDefault() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method method = AgentSnapshotServiceImpl.class.getDeclaredMethod("getChangedFields",
+ AgentSnapshotDataDTO.class, AgentSnapshotDataDTO.class);
+ method.setAccessible(true);
+
+ AgentSnapshotDataDTO current = new AgentSnapshotDataDTO();
+ current.setTtsVoiceId("voice-id");
+ current.setTtsLanguage(null);
+ AgentSnapshotDataDTO next = new AgentSnapshotDataDTO();
+ next.setTtsVoiceId("voice-id");
+ next.setTtsLanguage("鏅�氳瘽");
+
+ List<String> changedFields = (List<String>) method.invoke(service, current, next);
+
+ assertEquals(List.of(AgentSnapshotField.TTS_LANGUAGE.getFieldName()), changedFields);
+ }
+
+ @Test
+ void tagOnlySavePathCreatesSnapshot() throws Exception {
+ String controller = Files.readString(Path.of("src/main/java/xiaozhi/modules/agent/controller/AgentController.java"));
+ String roleConfig = Files.readString(Path.of("../manager-web/src/views/roleConfig.vue"));
+
+ assertTrue(controller.contains("agentService.updateAgentById(id, dto);"));
+ assertFalse(controller.contains("agentService.updateAgentById(id, dto, false);"));
+ assertTrue(roleConfig.contains("configData.tagNames = tagNames;"));
+ assertFalse(roleConfig.contains("this.handleSaveAgentTags(agentId, tagNames)"));
+ }
+
+ @Test
+ void firstAgentSaveKeepsInitialBaselineBeforePersistingNewState() {
+ AgentDao agentDao = mock(AgentDao.class);
+ AgentContextProviderService contextProviderService = mock(AgentContextProviderService.class);
+ CorrectWordFileService correctWordFileService = mock(CorrectWordFileService.class);
+ AgentSnapshotService snapshotService = mock(AgentSnapshotService.class);
+ AgentServiceImpl service = new AgentServiceImpl(agentDao, null, null, null, null, null, null, null,
+ null, null, contextProviderService, null, correctWordFileService, snapshotService);
+ ReflectionTestUtils.setField(service, "baseDao", agentDao);
+
+ String agentId = "agent-id";
+ AgentEntity lockedAgent = new AgentEntity();
+ lockedAgent.setId(agentId);
+ AgentInfoVO currentAgent = new AgentInfoVO();
+ currentAgent.setId(agentId);
+ currentAgent.setAgentName("old-name");
+ AgentUpdateDTO update = new AgentUpdateDTO();
+ update.setAgentName("new-name");
+
+ when(agentDao.selectByIdForUpdate(agentId)).thenReturn(lockedAgent);
+ when(agentDao.selectAgentInfoById(agentId)).thenReturn(currentAgent);
+ when(snapshotService.getCurrentVersionNo(agentId)).thenReturn(0);
+ when(contextProviderService.getByAgentId(agentId)).thenReturn(null);
+ when(correctWordFileService.getAgentCorrectWordFileIds(agentId)).thenReturn(List.of());
+ when(agentDao.updateById(any(AgentEntity.class))).thenReturn(1);
+
+ service.updateAgentById(agentId, update);
+
+ InOrder inOrder = inOrder(agentDao, snapshotService);
+ inOrder.verify(snapshotService).createSnapshot(agentId, "initial");
+ inOrder.verify(agentDao).updateById(argThat((AgentEntity agent) -> "new-name".equals(agent.getAgentName())));
+ inOrder.verify(snapshotService).createSnapshot(agentId, "config");
+ }
+
+ @Test
+ void subsequentAgentSaveCapturesUnversionedCurrentStateBeforePersistingNewState() {
+ AgentDao agentDao = mock(AgentDao.class);
+ AgentContextProviderService contextProviderService = mock(AgentContextProviderService.class);
+ CorrectWordFileService correctWordFileService = mock(CorrectWordFileService.class);
+ AgentSnapshotService snapshotService = mock(AgentSnapshotService.class);
+ AgentServiceImpl service = new AgentServiceImpl(agentDao, null, null, null, null, null, null, null,
+ null, null, contextProviderService, null, correctWordFileService, snapshotService);
+ ReflectionTestUtils.setField(service, "baseDao", agentDao);
+
+ String agentId = "agent-id";
+ AgentEntity lockedAgent = new AgentEntity();
+ lockedAgent.setId(agentId);
+ AgentInfoVO currentAgent = new AgentInfoVO();
+ currentAgent.setId(agentId);
+ currentAgent.setAgentName("old-name");
+ AgentUpdateDTO update = new AgentUpdateDTO();
+ update.setAgentName("new-name");
+
+ when(agentDao.selectByIdForUpdate(agentId)).thenReturn(lockedAgent);
+ when(agentDao.selectAgentInfoById(agentId)).thenReturn(currentAgent);
+ when(snapshotService.getCurrentVersionNo(agentId)).thenReturn(4);
+ when(contextProviderService.getByAgentId(agentId)).thenReturn(null);
+ when(correctWordFileService.getAgentCorrectWordFileIds(agentId)).thenReturn(List.of());
+ when(agentDao.updateById(any(AgentEntity.class))).thenReturn(1);
+
+ service.updateAgentById(agentId, update);
+
+ InOrder inOrder = inOrder(agentDao, snapshotService);
+ inOrder.verify(snapshotService).createSnapshot(agentId, "current");
+ inOrder.verify(agentDao).updateById(argThat((AgentEntity agent) -> "new-name".equals(agent.getAgentName())));
+ inOrder.verify(snapshotService).createSnapshot(agentId, "config");
+ }
+
+ @Test
+ void createAgentPersistsDisplayDefaultsBeforeInitialSnapshot() {
+ AgentDao agentDao = mock(AgentDao.class);
+ TimbreService timbreService = mock(TimbreService.class);
+ AgentPluginMappingService pluginMappingService = mock(AgentPluginMappingService.class);
+ AgentTemplateService templateService = mock(AgentTemplateService.class);
+ ModelProviderService providerService = mock(ModelProviderService.class);
+ AgentSnapshotService snapshotService = mock(AgentSnapshotService.class);
+ AgentServiceImpl service = new AgentServiceImpl(agentDao, null, timbreService, null, null, null,
+ pluginMappingService, null, templateService, providerService, null, null, null, snapshotService);
+ ReflectionTestUtils.setField(service, "baseDao", agentDao);
+
+ AgentTemplateEntity template = new AgentTemplateEntity();
+ template.setTtsModelId("TTS_EdgeTTS");
+ template.setTtsVoiceId("TTS_EdgeTTS0001");
+ template.setMemModelId("Memory_nomem");
+ template.setSummaryMemory(null);
+ when(templateService.getDefaultTemplate()).thenReturn(template);
+
+ when(timbreService.getDefaultLanguageById("TTS_EdgeTTS0001")).thenReturn("鏅�氳瘽");
+ when(agentDao.insert(any(AgentEntity.class))).thenReturn(1);
+
+ AgentCreateDTO dto = new AgentCreateDTO();
+ dto.setAgentName("test123");
+
+ String agentId = service.createAgent(dto);
+
+ InOrder inOrder = inOrder(agentDao, pluginMappingService, snapshotService);
+ inOrder.verify(agentDao).insert(argThat((AgentEntity agent) -> "test123".equals(agent.getAgentName())
+ && "鏅�氳瘽".equals(agent.getTtsLanguage())
+ && "".equals(agent.getSummaryMemory())
+ && Integer.valueOf(0).equals(agent.getChatHistoryConf())));
+ inOrder.verify(pluginMappingService).saveBatch(any(), eq(IRepository.DEFAULT_BATCH_SIZE));
+ inOrder.verify(snapshotService).createSnapshot(agentId, "initial");
+ }
+
+ @Test
+ void agentSnapshotFieldAppliesRestorableAgentFields() {
+ AgentSnapshotDataDTO data = new AgentSnapshotDataDTO();
+ data.setAgentName("restored-name");
+ data.setLlmModelId("llm-new");
+ data.setSystemPrompt("restored prompt");
+ data.setSort(7);
+
+ AgentEntity agent = new AgentEntity();
+ for (AgentSnapshotField field : AgentSnapshotField.values()) {
+ field.applyTo(agent, data);
+ }
+
+ assertEquals("restored-name", agent.getAgentName());
+ assertEquals("llm-new", agent.getLlmModelId());
+ assertEquals("restored prompt", agent.getSystemPrompt());
+ assertEquals(7, agent.getSort());
+ }
+
+ @Test
+ void restoreSnapshotRollsBackForAnyException() throws Exception {
+ Method method = AgentSnapshotServiceImpl.class.getMethod("restoreSnapshot", String.class, String.class,
+ String.class);
+
+ Transactional transactional = method.getAnnotation(Transactional.class);
+
+ assertNotNull(transactional);
+ assertTrue(List.of(transactional.rollbackFor()).contains(Exception.class));
+ }
+
+ @Test
+ void restoreSnapshotRejectsStalePreviewTokenBeforeAnyMutation() {
+ AgentSnapshotDao snapshotDao = mock(AgentSnapshotDao.class);
+ AgentDao agentDao = mock(AgentDao.class);
+ AgentTagDao tagDao = mock(AgentTagDao.class);
+ AgentPluginMappingService pluginMappingService = mock(AgentPluginMappingService.class);
+ AgentContextProviderService contextProviderService = mock(AgentContextProviderService.class);
+ CorrectWordFileService correctWordFileService = mock(CorrectWordFileService.class);
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(snapshotDao, agentDao, tagDao, null,
+ pluginMappingService, contextProviderService, null, null, null, correctWordFileService);
+ ReflectionTestUtils.setField(service, "baseDao", snapshotDao);
+
+ String agentId = "agent-id";
+ String targetId = "target-id";
+ AgentEntity lockedAgent = new AgentEntity();
+ lockedAgent.setId(agentId);
+ when(agentDao.selectByIdForUpdate(agentId)).thenReturn(lockedAgent);
+ when(snapshotDao.selectById(targetId))
+ .thenReturn(snapshotEntity(targetId, agentId, 2, snapshotData("target", "target-summary")));
+ when(agentDao.selectAgentInfoById(agentId))
+ .thenReturn(snapshotAgentInfo(agentId, 7L, "changed-after-preview", "live-summary"));
+ when(contextProviderService.getByAgentId(agentId)).thenReturn(null);
+ when(correctWordFileService.getAgentCorrectWordFileIds(agentId)).thenReturn(List.of());
+ when(tagDao.selectByAgentId(agentId)).thenReturn(List.of());
+
+ RenException error = assertThrows(RenException.class,
+ () -> service.restoreSnapshot(agentId, targetId, "stale-token"));
+
+ assertTrue(error.getMessage().contains("閲嶆柊鎵撳紑鎭㈠棰勮"));
+ verify(snapshotDao, never()).selectLatestSnapshot(agentId);
+ verify(snapshotDao, never()).insertWithNextVersion(any());
+ verify(agentDao, never()).updateSnapshotFields(any());
+ verify(pluginMappingService, never()).deleteByAgentId(any());
+ }
+
+ @Test
+ void restoreSnapshotBacksUpUnversionedMemoryThenStoresAppliedStateAsLatest() {
+ AgentSnapshotDao snapshotDao = mock(AgentSnapshotDao.class);
+ AgentDao agentDao = mock(AgentDao.class);
+ AgentTagDao tagDao = mock(AgentTagDao.class);
+ AgentTagRelationDao tagRelationDao = mock(AgentTagRelationDao.class);
+ AgentPluginMappingService pluginMappingService = mock(AgentPluginMappingService.class);
+ AgentContextProviderService contextProviderService = mock(AgentContextProviderService.class);
+ AgentTagService tagService = mock(AgentTagService.class);
+ AgentChatHistoryService chatHistoryService = mock(AgentChatHistoryService.class);
+ CorrectWordFileService correctWordFileService = mock(CorrectWordFileService.class);
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(snapshotDao, agentDao, tagDao, tagRelationDao,
+ pluginMappingService, contextProviderService, tagService, chatHistoryService, null,
+ correctWordFileService);
+ ReflectionTestUtils.setField(service, "baseDao", snapshotDao);
+
+ String agentId = "agent-id";
+ String targetId = "target-id";
+ AgentEntity lockedAgent = new AgentEntity();
+ lockedAgent.setId(agentId);
+ lockedAgent.setUserId(7L);
+ AgentInfoVO currentInfo = snapshotAgentInfo(agentId, 7L, "current-name", "live-summary");
+ AgentInfoVO restoredInfo = snapshotAgentInfo(agentId, 7L, "target-name", "target-summary");
+ AgentSnapshotDataDTO currentData = snapshotData("current-name", "live-summary");
+ AgentSnapshotDataDTO latestData = snapshotData("current-name", "snapshot-summary");
+ AgentSnapshotDataDTO targetData = snapshotData("target-name", "target-summary");
+ AgentSnapshotEntity target = snapshotEntity(targetId, agentId, 2, targetData);
+ AgentSnapshotEntity latest = snapshotEntity("latest-id", agentId, 5, latestData);
+
+ when(agentDao.selectByIdForUpdate(agentId)).thenReturn(lockedAgent);
+ when(snapshotDao.selectById(targetId)).thenReturn(target);
+ when(agentDao.selectAgentInfoById(agentId)).thenReturn(currentInfo, restoredInfo);
+ when(snapshotDao.selectLatestSnapshot(agentId)).thenReturn(latest);
+ when(contextProviderService.getByAgentId(agentId)).thenReturn(null);
+ when(correctWordFileService.getAgentCorrectWordFileIds(agentId)).thenReturn(List.of());
+ when(tagDao.selectByAgentId(agentId)).thenReturn(List.of());
+ when(agentDao.updateSnapshotFields(any())).thenReturn(1);
+ when(snapshotDao.insertWithNextVersion(any())).thenReturn(1);
+
+ service.restoreSnapshot(agentId, targetId, currentStateToken(service, currentData));
+
+ ArgumentCaptor<AgentSnapshotEntity> snapshotCaptor = ArgumentCaptor.forClass(AgentSnapshotEntity.class);
+ verify(snapshotDao, org.mockito.Mockito.times(2)).insertWithNextVersion(snapshotCaptor.capture());
+ List<AgentSnapshotEntity> inserted = snapshotCaptor.getAllValues();
+ AgentSnapshotEntity backup = inserted.get(0);
+ AgentSnapshotEntity restored = inserted.get(1);
+
+ assertEquals("current", backup.getSource());
+ assertNull(backup.getRestoreFromSnapshotId());
+ assertNull(backup.getRestoreFromVersionNo());
+ assertEquals(List.of(AgentSnapshotField.SUMMARY_MEMORY.getFieldName()),
+ JsonUtils.parseObject(backup.getChangedFields(), new TypeReference<List<String>>() {
+ }));
+ assertEquals("live-summary",
+ JsonUtils.parseObject(backup.getSnapshotData(), AgentSnapshotDataDTO.class).getSummaryMemory());
+
+ assertEquals("restore", restored.getSource());
+ assertEquals(targetId, restored.getRestoreFromSnapshotId());
+ assertEquals(2, restored.getRestoreFromVersionNo());
+ assertEquals("target-name",
+ JsonUtils.parseObject(restored.getSnapshotData(), AgentSnapshotDataDTO.class).getAgentName());
+ assertEquals("target-summary",
+ JsonUtils.parseObject(restored.getSnapshotData(), AgentSnapshotDataDTO.class).getSummaryMemory());
+
+ InOrder order = inOrder(snapshotDao, agentDao);
+ order.verify(snapshotDao).insertWithNextVersion(backup);
+ order.verify(agentDao).updateSnapshotFields(lockedAgent);
+ order.verify(snapshotDao).insertWithNextVersion(restored);
+ verify(snapshotDao).deleteOlderThanKeepLimit(agentId, 100);
+ }
+
+ @Test
+ void restoreSnapshotSkipsBackupWhenLatestAlreadyRepresentsCurrentState() {
+ AgentSnapshotDao snapshotDao = mock(AgentSnapshotDao.class);
+ AgentDao agentDao = mock(AgentDao.class);
+ AgentTagDao tagDao = mock(AgentTagDao.class);
+ AgentPluginMappingService pluginMappingService = mock(AgentPluginMappingService.class);
+ AgentContextProviderService contextProviderService = mock(AgentContextProviderService.class);
+ AgentTagService tagService = mock(AgentTagService.class);
+ CorrectWordFileService correctWordFileService = mock(CorrectWordFileService.class);
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(snapshotDao, agentDao, tagDao, null,
+ pluginMappingService, contextProviderService, tagService, null, null, correctWordFileService);
+ ReflectionTestUtils.setField(service, "baseDao", snapshotDao);
+
+ String agentId = "agent-id";
+ String targetId = "target-id";
+ AgentEntity lockedAgent = new AgentEntity();
+ lockedAgent.setId(agentId);
+ AgentInfoVO currentInfo = snapshotAgentInfo(agentId, 7L, "current-name", "current-summary");
+ AgentInfoVO restoredInfo = snapshotAgentInfo(agentId, 7L, "target-name", "target-summary");
+ AgentSnapshotDataDTO currentData = snapshotData("current-name", "current-summary");
+ AgentSnapshotEntity target = snapshotEntity(targetId, agentId, 2,
+ snapshotData("target-name", "target-summary"));
+
+ when(agentDao.selectByIdForUpdate(agentId)).thenReturn(lockedAgent);
+ when(snapshotDao.selectById(targetId)).thenReturn(target);
+ when(agentDao.selectAgentInfoById(agentId)).thenReturn(currentInfo, restoredInfo);
+ when(snapshotDao.selectLatestSnapshot(agentId)).thenReturn(snapshotEntity("latest-id", agentId, 5, currentData));
+ when(contextProviderService.getByAgentId(agentId)).thenReturn(null);
+ when(correctWordFileService.getAgentCorrectWordFileIds(agentId)).thenReturn(List.of());
+ when(tagDao.selectByAgentId(agentId)).thenReturn(List.of());
+ when(agentDao.updateSnapshotFields(any())).thenReturn(1);
+ when(snapshotDao.insertWithNextVersion(any())).thenReturn(1);
+
+ service.restoreSnapshot(agentId, targetId, currentStateToken(service, currentData));
+
+ ArgumentCaptor<AgentSnapshotEntity> snapshotCaptor = ArgumentCaptor.forClass(AgentSnapshotEntity.class);
+ verify(snapshotDao).insertWithNextVersion(snapshotCaptor.capture());
+ assertEquals("restore", snapshotCaptor.getValue().getSource());
+ assertEquals(targetId, snapshotCaptor.getValue().getRestoreFromSnapshotId());
+ verify(snapshotDao).deleteOlderThanKeepLimit(agentId, 100);
+ }
+
+ @Test
+ void restoreSnapshotContinuesWhenRelationOnlyChangeReportsZeroUpdatedAgentRows() {
+ AgentSnapshotDao snapshotDao = mock(AgentSnapshotDao.class);
+ AgentDao agentDao = mock(AgentDao.class);
+ AgentTagDao tagDao = mock(AgentTagDao.class);
+ AgentPluginMappingService pluginMappingService = mock(AgentPluginMappingService.class);
+ AgentContextProviderService contextProviderService = mock(AgentContextProviderService.class);
+ AgentTagService tagService = mock(AgentTagService.class);
+ CorrectWordFileService correctWordFileService = mock(CorrectWordFileService.class);
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(snapshotDao, agentDao, tagDao, null,
+ pluginMappingService, contextProviderService, tagService, null, null, correctWordFileService);
+ ReflectionTestUtils.setField(service, "baseDao", snapshotDao);
+
+ String agentId = "agent-id";
+ String targetId = "target-id";
+ AgentEntity lockedAgent = new AgentEntity();
+ lockedAgent.setId(agentId);
+ lockedAgent.setUserId(7L);
+ AgentInfoVO currentInfo = snapshotAgentInfo(agentId, 7L, "same-name", "same-summary");
+ AgentInfoVO restoredInfo = snapshotAgentInfo(agentId, 7L, "same-name", "same-summary");
+ AgentPluginMapping currentMapping = new AgentPluginMapping();
+ currentMapping.setPluginId("plugin");
+ currentMapping.setParamInfo("{\"description\":\"current\"}");
+ currentInfo.setFunctions(List.of(currentMapping));
+ AgentPluginMapping restoredMapping = new AgentPluginMapping();
+ restoredMapping.setPluginId("plugin");
+ restoredMapping.setParamInfo("{\"description\":\"target\"}");
+ restoredInfo.setFunctions(List.of(restoredMapping));
+
+ AgentSnapshotDataDTO currentData = snapshotData("same-name", "same-summary");
+ currentData.setFunctions(List.of(functionInfo("plugin", Map.of("description", "current"))));
+ AgentSnapshotDataDTO targetData = snapshotData("same-name", "same-summary");
+ targetData.setFunctions(List.of(functionInfo("plugin", Map.of("description", "target"))));
+
+ when(agentDao.selectByIdForUpdate(agentId)).thenReturn(lockedAgent);
+ when(snapshotDao.selectById(targetId)).thenReturn(snapshotEntity(targetId, agentId, 2, targetData));
+ when(agentDao.selectAgentInfoById(agentId)).thenReturn(currentInfo, restoredInfo);
+ when(snapshotDao.selectLatestSnapshot(agentId))
+ .thenReturn(snapshotEntity("latest-id", agentId, 5, currentData));
+ when(contextProviderService.getByAgentId(agentId)).thenReturn(null);
+ when(correctWordFileService.getAgentCorrectWordFileIds(agentId)).thenReturn(List.of());
+ when(tagDao.selectByAgentId(agentId)).thenReturn(List.of());
+ when(agentDao.updateSnapshotFields(any())).thenReturn(0);
+ when(snapshotDao.insertWithNextVersion(any())).thenReturn(1);
+
+ service.restoreSnapshot(agentId, targetId, currentStateToken(service, currentData));
+
+ verify(pluginMappingService).deleteByAgentId(agentId);
+ verify(pluginMappingService).saveBatch(argThat(mappings -> {
+ AgentPluginMapping mapping = mappings.size() == 1 ? mappings.iterator().next() : null;
+ return mapping != null
+ && "plugin".equals(mapping.getPluginId())
+ && mapping.getParamInfo().contains("target");
+ }), eq(IRepository.DEFAULT_BATCH_SIZE));
+ verify(snapshotDao).insertWithNextVersion(argThat(snapshot -> "restore".equals(snapshot.getSource())));
+ }
+
+ @Test
+ void restoreSnapshotExplicitlyPersistsNullableFieldsAsNull() {
+ AgentSnapshotDao snapshotDao = mock(AgentSnapshotDao.class);
+ AgentDao agentDao = mock(AgentDao.class);
+ AgentTagDao tagDao = mock(AgentTagDao.class);
+ AgentPluginMappingService pluginMappingService = mock(AgentPluginMappingService.class);
+ AgentContextProviderService contextProviderService = mock(AgentContextProviderService.class);
+ AgentTagService tagService = mock(AgentTagService.class);
+ CorrectWordFileService correctWordFileService = mock(CorrectWordFileService.class);
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(snapshotDao, agentDao, tagDao, null,
+ pluginMappingService, contextProviderService, tagService, null, null, correctWordFileService);
+ ReflectionTestUtils.setField(service, "baseDao", snapshotDao);
+
+ String agentId = "agent-id";
+ String targetId = "target-id";
+ AgentEntity lockedAgent = new AgentEntity();
+ lockedAgent.setId(agentId);
+ lockedAgent.setUserId(7L);
+ lockedAgent.setTtsLanguage("鏅�氳瘽");
+ lockedAgent.setTtsVolume(0);
+ lockedAgent.setTtsRate(0);
+ lockedAgent.setTtsPitch(0);
+ lockedAgent.setSlmModelId("slm-id");
+ lockedAgent.setVllmModelId("vllm-id");
+ lockedAgent.setIntentModelId("intent-id");
+ lockedAgent.setSystemPrompt("old prompt");
+
+ AgentInfoVO currentInfo = snapshotAgentInfo(agentId, 7L, "same-name", "same-summary");
+ currentInfo.setTtsLanguage("鏅�氳瘽");
+ currentInfo.setTtsVolume(0);
+ currentInfo.setTtsRate(0);
+ currentInfo.setTtsPitch(0);
+ currentInfo.setSlmModelId("slm-id");
+ currentInfo.setVllmModelId("vllm-id");
+ currentInfo.setIntentModelId("intent-id");
+ currentInfo.setSystemPrompt("old prompt");
+ AgentInfoVO restoredInfo = snapshotAgentInfo(agentId, 7L, "same-name", "same-summary");
+
+ AgentSnapshotDataDTO currentData = snapshotData("same-name", "same-summary");
+ currentData.setTtsLanguage("鏅�氳瘽");
+ currentData.setTtsVolume(0);
+ currentData.setTtsRate(0);
+ currentData.setTtsPitch(0);
+ currentData.setSlmModelId("slm-id");
+ currentData.setVllmModelId("vllm-id");
+ currentData.setIntentModelId("intent-id");
+ currentData.setSystemPrompt("old prompt");
+ AgentSnapshotDataDTO targetData = snapshotData("same-name", "same-summary");
+
+ when(agentDao.selectByIdForUpdate(agentId)).thenReturn(lockedAgent);
+ when(snapshotDao.selectById(targetId)).thenReturn(snapshotEntity(targetId, agentId, 2, targetData));
+ when(agentDao.selectAgentInfoById(agentId)).thenReturn(currentInfo, restoredInfo);
+ when(snapshotDao.selectLatestSnapshot(agentId))
+ .thenReturn(snapshotEntity("latest-id", agentId, 5, currentData));
+ when(contextProviderService.getByAgentId(agentId)).thenReturn(null);
+ when(correctWordFileService.getAgentCorrectWordFileIds(agentId)).thenReturn(List.of());
+ when(tagDao.selectByAgentId(agentId)).thenReturn(List.of());
+ when(agentDao.updateSnapshotFields(any())).thenReturn(1);
+ when(snapshotDao.insertWithNextVersion(any())).thenReturn(1);
+
+ service.restoreSnapshot(agentId, targetId, currentStateToken(service, currentData));
+
+ verify(agentDao).updateSnapshotFields(argThat(agent -> agent.getTtsLanguage() == null
+ && agent.getTtsVolume() == null
+ && agent.getTtsRate() == null
+ && agent.getTtsPitch() == null
+ && agent.getSlmModelId() == null
+ && agent.getVllmModelId() == null
+ && agent.getIntentModelId() == null
+ && agent.getSystemPrompt() == null
+ && Long.valueOf(7L).equals(agent.getUserId())));
+ ArgumentCaptor<AgentSnapshotEntity> snapshotCaptor = ArgumentCaptor.forClass(AgentSnapshotEntity.class);
+ verify(snapshotDao).insertWithNextVersion(snapshotCaptor.capture());
+ AgentSnapshotDataDTO latestData = JsonUtils.parseObject(snapshotCaptor.getValue().getSnapshotData(),
+ AgentSnapshotDataDTO.class);
+ assertEquals("restore", snapshotCaptor.getValue().getSource());
+ assertNull(latestData.getTtsLanguage());
+ assertNull(latestData.getTtsVolume());
+ assertNull(latestData.getTtsRate());
+ assertNull(latestData.getTtsPitch());
+ assertNull(latestData.getSlmModelId());
+ assertNull(latestData.getVllmModelId());
+ assertNull(latestData.getIntentModelId());
+ assertNull(latestData.getSystemPrompt());
+ verify(agentDao, org.mockito.Mockito.times(2)).selectAgentInfoById(agentId);
+ }
+
+ @Test
+ void restoreSnapshotDoesNotMutateOrWriteHistoryWhenTargetMatchesCurrentState() {
+ AgentSnapshotDao snapshotDao = mock(AgentSnapshotDao.class);
+ AgentDao agentDao = mock(AgentDao.class);
+ AgentTagDao tagDao = mock(AgentTagDao.class);
+ AgentPluginMappingService pluginMappingService = mock(AgentPluginMappingService.class);
+ AgentContextProviderService contextProviderService = mock(AgentContextProviderService.class);
+ AgentTagService tagService = mock(AgentTagService.class);
+ CorrectWordFileService correctWordFileService = mock(CorrectWordFileService.class);
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(snapshotDao, agentDao, tagDao, null,
+ pluginMappingService, contextProviderService, tagService, null, null, correctWordFileService);
+ ReflectionTestUtils.setField(service, "baseDao", snapshotDao);
+
+ String agentId = "agent-id";
+ String targetId = "target-id";
+ AgentEntity lockedAgent = new AgentEntity();
+ lockedAgent.setId(agentId);
+ AgentInfoVO currentInfo = snapshotAgentInfo(agentId, 7L, "same-name", "same-summary");
+ AgentSnapshotDataDTO currentData = snapshotData("same-name", "same-summary");
+ AgentSnapshotEntity target = snapshotEntity(targetId, agentId, 3, currentData);
+
+ when(agentDao.selectByIdForUpdate(agentId)).thenReturn(lockedAgent);
+ when(snapshotDao.selectById(targetId)).thenReturn(target);
+ when(agentDao.selectAgentInfoById(agentId)).thenReturn(currentInfo);
+ when(contextProviderService.getByAgentId(agentId)).thenReturn(null);
+ when(correctWordFileService.getAgentCorrectWordFileIds(agentId)).thenReturn(List.of());
+ when(tagDao.selectByAgentId(agentId)).thenReturn(List.of());
+
+ service.restoreSnapshot(agentId, targetId, currentStateToken(service, currentData));
+
+ verify(snapshotDao, never()).selectLatestSnapshot(agentId);
+ verify(snapshotDao, never()).insertWithNextVersion(any());
+ verify(snapshotDao, never()).deleteOlderThanKeepLimit(any(), anyInt());
+ verify(agentDao, never()).updateSnapshotFields(any());
+ verify(pluginMappingService, never()).deleteByAgentId(any());
+ verify(contextProviderService, never()).saveOrUpdateByAgentId(any());
+ verify(correctWordFileService, never()).saveAgentCorrectWords(any(), any());
+ verify(tagService, never()).saveAgentTags(any(), any(), any());
+ }
+
+ @Test
+ void deleteSnapshotRollsBackForAnyException() throws Exception {
+ Method method = AgentSnapshotServiceImpl.class.getMethod("deleteSnapshot", String.class, String.class);
+
+ Transactional transactional = method.getAnnotation(Transactional.class);
+
+ assertNotNull(transactional);
+ assertTrue(List.of(transactional.rollbackFor()).contains(Exception.class));
+ }
+
+ @Test
+ void deleteSnapshotLocksAgentBeforeReadingAndDeletingSnapshot() {
+ AgentSnapshotDao snapshotDao = mock(AgentSnapshotDao.class);
+ AgentDao agentDao = mock(AgentDao.class);
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(snapshotDao, agentDao, null, null, null, null,
+ null, null, null, null);
+ ReflectionTestUtils.setField(service, "baseDao", snapshotDao);
+
+ String agentId = "agent-id";
+ String snapshotId = "snapshot-id";
+ AgentEntity agent = new AgentEntity();
+ agent.setId(agentId);
+ AgentSnapshotEntity snapshot = new AgentSnapshotEntity();
+ snapshot.setId(snapshotId);
+ snapshot.setAgentId(agentId);
+ snapshot.setVersionNo(2);
+
+ when(agentDao.selectByIdForUpdate(agentId)).thenReturn(agent);
+ when(snapshotDao.selectById(snapshotId)).thenReturn(snapshot);
+ when(snapshotDao.selectMaxVersionNo(agentId)).thenReturn(3);
+ when(snapshotDao.deleteById(snapshotId)).thenReturn(1);
+
+ service.deleteSnapshot(agentId, snapshotId);
+
+ InOrder order = inOrder(agentDao, snapshotDao);
+ order.verify(agentDao).selectByIdForUpdate(agentId);
+ order.verify(snapshotDao).selectById(snapshotId);
+ order.verify(snapshotDao).selectMaxVersionNo(agentId);
+ order.verify(snapshotDao).deleteById(snapshotId);
+ }
+
+ @Test
+ void snapshotInsertAllocatesVersionInSingleSqlStatement() throws Exception {
+ String xml = normalizeWhitespace(Files.readString(Path.of("src/main/resources/mapper/agent/AgentSnapshotDao.xml")));
+ String dao = Files.readString(Path.of("src/main/java/xiaozhi/modules/agent/dao/AgentSnapshotDao.java"));
+
+ assertTrue(dao.contains("insertWithNextVersion"));
+ assertTrue(xml.contains("<insert id=\"insertWithNextVersion\">"));
+ assertTrue(xml.contains("INSERT INTO ai_agent_snapshot"));
+ assertTrue(xml.contains("COALESCE(MAX(version_no), 0) + 1"));
+ assertFalse(xml.contains("LAST_INSERT_ID"));
+ assertFalse(xml.contains("ai_agent_snapshot_sequence"));
+ }
+
+ @Test
+ void restoreUpdateSqlWritesNullableSnapshotFieldsWithoutOwnershipColumns() throws Exception {
+ String xml = Files.readString(Path.of("src/main/resources/mapper/agent/AgentDao.xml"));
+ int updateStart = xml.indexOf("<update id=\"updateSnapshotFields\">");
+ int updateEnd = xml.indexOf("</update>", updateStart);
+ assertTrue(updateStart >= 0);
+ assertTrue(updateEnd > updateStart);
+ String updateSql = normalizeWhitespace(xml.substring(updateStart, updateEnd));
+
+ for (AgentSnapshotField field : AgentSnapshotField.values()) {
+ if (!field.isRestorableAgentField()) {
+ continue;
+ }
+ String column = field.getFieldName().replaceAll("([a-z0-9])([A-Z])", "$1_$2")
+ .toLowerCase(Locale.ROOT);
+ assertTrue(updateSql.contains(column + " = #{agent." + field.getFieldName() + ","),
+ () -> "Missing explicit restore assignment for " + field.getFieldName());
+ }
+ assertTrue(updateSql.contains("tts_language = #{agent.ttsLanguage,jdbcType=VARCHAR}"));
+ assertTrue(updateSql.contains("tts_volume = #{agent.ttsVolume,jdbcType=INTEGER}"));
+ assertTrue(updateSql.contains("slm_model_id = #{agent.slmModelId,jdbcType=VARCHAR}"));
+ assertTrue(updateSql.contains("vllm_model_id = #{agent.vllmModelId,jdbcType=VARCHAR}"));
+ assertTrue(updateSql.contains("intent_model_id = #{agent.intentModelId,jdbcType=VARCHAR}"));
+ assertTrue(updateSql.contains("system_prompt = #{agent.systemPrompt,jdbcType=LONGVARCHAR}"));
+ assertTrue(updateSql.contains("summary_memory = #{agent.summaryMemory,jdbcType=LONGVARCHAR}"));
+ assertFalse(updateSql.contains("user_id ="));
+ assertFalse(updateSql.contains("creator ="));
+ assertFalse(updateSql.contains("created_at ="));
+ }
+
+ @Test
+ void snapshotMigrationIsMergedIntoSingleChangeLog() throws Exception {
+ String sql = Files.readString(Path.of("src/main/resources/db/changelog/202607071530.sql"));
+ String master = Files.readString(Path.of("src/main/resources/db/changelog/db.changelog-master.yaml"));
+
+ assertTrue(sql.contains("restore_from_snapshot_id"));
+ assertTrue(sql.contains("restore_from_version_no"));
+ assertTrue(sql.contains("idx_snapshot_user_created_at"));
+ assertTrue(sql.contains("DEFAULT (JSON_OBJECT())"));
+ assertTrue(master.contains("202607071530.sql"));
+ assertFalse(master.contains("202607081150.sql"));
+ assertFalse(master.contains("202607081230.sql"));
+ }
+
+ @Test
+ void legacySnapshotRedactionHasItsOwnResumableSchemaVersionChangeLog() throws Exception {
+ String sql = Files.readString(Path.of("src/main/resources/db/changelog/202607101200.sql"));
+ String master = Files.readString(Path.of("src/main/resources/db/changelog/db.changelog-master.yaml"));
+ String mapper = Files.readString(Path.of("src/main/resources/mapper/agent/AgentSnapshotDao.xml"));
+
+ assertTrue(sql.contains("redaction_version"));
+ assertTrue(sql.contains("idx_snapshot_redaction_version_id"));
+ assertTrue(sql.contains("-- rollback"));
+ assertTrue(master.contains("202607101200.sql"));
+ assertTrue(mapper.contains("selectLegacyRedactionBatch"));
+ assertTrue(mapper.contains("redaction_version < #{targetRedactionVersion}"));
+ assertTrue(mapper.contains("<update id=\"updateRedactedSnapshots\">"));
+ }
+
+ @Test
+ void selectNextSnapshotLoadsRestoreTraceColumnsWithoutPreviousVersionQuery() throws Exception {
+ String xml = normalizeWhitespace(Files.readString(Path.of("src/main/resources/mapper/agent/AgentSnapshotDao.xml")));
+ String dao = Files.readString(Path.of("src/main/java/xiaozhi/modules/agent/dao/AgentSnapshotDao.java"));
+
+ assertTrue(xml.contains("restore_from_snapshot_id AS restoreFromSnapshotId"));
+ assertTrue(xml.contains("restore_from_version_no AS restoreFromVersionNo"));
+ assertTrue(xml.contains("version_no > #{versionNo}"));
+ assertFalse(xml.contains("selectPreviousSnapshot"));
+ assertFalse(dao.contains("selectPreviousSnapshot"));
+ }
+
+ @Test
+ void retentionPruneSqlKeepsLatestVersions() throws Exception {
+ String xml = Files.readString(Path.of("src/main/resources/mapper/agent/AgentSnapshotDao.xml"));
+
+ assertTrue(xml.contains("deleteOlderThanKeepLimit"));
+ assertTrue(xml.contains("ORDER BY version_no DESC"));
+ assertTrue(xml.contains("LIMIT #{keepLimit}"));
+ }
+
+ @Test
+ void agentInfoMapperLoadsFunctionsWithJoinInsteadOfNestedSelect() throws Exception {
+ String xml = Files.readString(Path.of("src/main/resources/mapper/agent/AgentDao.xml"));
+
+ assertTrue(xml.contains("LEFT JOIN ai_agent_plugin_mapping f ON f.agent_id = a.id"));
+ assertFalse(xml.contains("selectAgentFunctionsByAgentId"));
+ assertFalse(xml.contains("select=\"selectAgentFunctions"));
+ }
+
+ @Test
+ void toVOIncludesRestoreTraceFields() throws Exception {
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, null, null, null, null, null, null,
+ null, null);
+ Method method = AgentSnapshotServiceImpl.class.getDeclaredMethod("toVO",
+ AgentSnapshotEntity.class, boolean.class);
+ method.setAccessible(true);
+
+ AgentSnapshotEntity entity = new AgentSnapshotEntity();
+ entity.setId("snapshot-id");
+ entity.setAgentId("agent-id");
+ entity.setVersionNo(3);
+ entity.setChangedFields("[]");
+ entity.setSource("restore");
+ entity.setRestoreFromSnapshotId("target-id");
+ entity.setRestoreFromVersionNo(1);
+
+ AgentSnapshotVO vo = (AgentSnapshotVO) method.invoke(service, entity, false);
+
+ assertEquals("target-id", vo.getRestoreFromSnapshotId());
+ assertEquals(1, vo.getRestoreFromVersionNo());
+ }
+
+ @Test
+ void toVOKeepsStoredEventFieldsStableAcrossDeletedVersionGapAndMetadataChanges() throws Exception {
+ AgentSnapshotDao snapshotDao = mock(AgentSnapshotDao.class);
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(snapshotDao, null, null, null, null, null, null,
+ null, null, null);
+ Method method = AgentSnapshotServiceImpl.class.getDeclaredMethod("toVO",
+ AgentSnapshotEntity.class, boolean.class);
+ method.setAccessible(true);
+
+ AgentSnapshotEntity current = new AgentSnapshotEntity();
+ current.setId("snapshot-id");
+ current.setAgentId("agent-id");
+ current.setVersionNo(3);
+ current.setChangedFields(JsonUtils.toJsonString(List.of("ttsLanguage", "ttsVolume")));
+
+ AgentSnapshotVO vo = (AgentSnapshotVO) method.invoke(service, current, false);
+
+ assertEquals(List.of("ttsLanguage", "ttsVolume"), vo.getChangedFields());
+ verifyNoInteractions(snapshotDao);
+ }
+
+ @Test
+ void pageUsesPersistedChangedFieldsWithoutPerRecordQueries() {
+ AgentSnapshotDao snapshotDao = mock(AgentSnapshotDao.class);
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(snapshotDao, null, null, null, null, null, null,
+ null, null, null);
+
+ AgentSnapshotEntity versionFive = new AgentSnapshotEntity();
+ versionFive.setVersionNo(5);
+ versionFive.setChangedFields(JsonUtils.toJsonString(List.of("agentName")));
+ AgentSnapshotEntity versionThree = new AgentSnapshotEntity();
+ versionThree.setVersionNo(3);
+ versionThree.setChangedFields(JsonUtils.toJsonString(List.of("ttsLanguage")));
+ Page<AgentSnapshotEntity> result = new Page<>(1, 10);
+ result.setRecords(List.of(versionFive, versionThree));
+ result.setTotal(2);
+ when(snapshotDao.selectPage(any(), any())).thenReturn(result);
+
+ List<AgentSnapshotVO> records = service.page("agent-id", new AgentSnapshotPageDTO()).getList();
+
+ assertEquals(List.of("agentName"), records.get(0).getChangedFields());
+ assertEquals(List.of("ttsLanguage"), records.get(1).getChangedFields());
+ verify(snapshotDao).selectPage(any(), any());
+ verifyNoMoreInteractions(snapshotDao);
+ }
+
+ @Test
+ void pageDoesNotCreateInitialSnapshotWhenAgentHasNoHistory() {
+ AgentSnapshotDao snapshotDao = mock(AgentSnapshotDao.class);
+ AgentDao agentDao = mock(AgentDao.class);
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(snapshotDao, agentDao, null, null, null,
+ null, null, null, null, null);
+
+ when(snapshotDao.selectPage(any(), any())).thenReturn(new Page<AgentSnapshotEntity>(1, 10));
+
+ service.page("agent-id", new AgentSnapshotPageDTO());
+
+ verify(agentDao, never()).selectByIdForUpdate(any());
+ verify(snapshotDao, never()).selectMaxVersionNo(any());
+ verify(snapshotDao, never()).insertWithNextVersion(any());
+ verify(snapshotDao, never()).deleteOlderThanKeepLimit(any(), anyInt());
+ }
+
+ @Test
+ void getCurrentVersionNoReturnsPersistedMaxVersion() {
+ AgentSnapshotDao snapshotDao = mock(AgentSnapshotDao.class);
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(snapshotDao, null, null, null, null,
+ null, null, null, null, null);
+
+ when(snapshotDao.selectMaxVersionNo("agent-id")).thenReturn(7);
+
+ assertEquals(7, service.getCurrentVersionNo("agent-id"));
+ }
+
+ @Test
+ void createSnapshotStoresCurrentStateAsInitialVersionWhenHistoryIsEmpty() {
+ AgentSnapshotDao snapshotDao = mock(AgentSnapshotDao.class);
+ AgentDao agentDao = mock(AgentDao.class);
+ AgentTagDao tagDao = mock(AgentTagDao.class);
+ AgentContextProviderService contextProviderService = mock(AgentContextProviderService.class);
+ CorrectWordFileService correctWordFileService = mock(CorrectWordFileService.class);
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(snapshotDao, agentDao, tagDao, null, null,
+ contextProviderService, null, null, null, correctWordFileService);
+
+ AgentEntity lockedAgent = new AgentEntity();
+ lockedAgent.setId("agent-id");
+ AgentInfoVO agentInfo = new AgentInfoVO();
+ agentInfo.setId("agent-id");
+ agentInfo.setUserId(7L);
+ agentInfo.setAgentName("agent");
+ when(agentDao.selectByIdForUpdate("agent-id")).thenReturn(lockedAgent);
+ when(agentDao.selectAgentInfoById("agent-id")).thenReturn(agentInfo);
+ when(snapshotDao.selectLatestSnapshot("agent-id")).thenReturn(null);
+ when(snapshotDao.insertWithNextVersion(any())).thenReturn(1);
+ when(correctWordFileService.getAgentCorrectWordFileIds("agent-id")).thenReturn(List.of());
+ when(contextProviderService.getByAgentId("agent-id")).thenReturn(null);
+ when(tagDao.selectByAgentId("agent-id")).thenReturn(List.of());
+
+ service.createSnapshot("agent-id", "config");
+
+ verify(snapshotDao).insertWithNextVersion(argThat(snapshot -> "config".equals(snapshot.getSource())
+ && snapshot.getVersionNo() == null
+ && snapshot.getChangedFields().contains("initial")));
+ verify(snapshotDao).deleteOlderThanKeepLimit("agent-id", 100);
+ }
+
+ @Test
+ void restoreTagRejectsSoftDeletedSnapshotTagWithoutMutatingGlobalTag() throws Exception {
+ AgentTagDao tagDao = mock(AgentTagDao.class);
+ AgentSnapshotServiceImpl service = new AgentSnapshotServiceImpl(null, null, tagDao, null, null, null, null, null,
+ null, null);
+ Method method = AgentSnapshotServiceImpl.class.getDeclaredMethod("restoreTag",
+ AgentSnapshotTagDTO.class, Date.class);
+ method.setAccessible(true);
+
+ AgentTagEntity deletedTag = new AgentTagEntity();
+ deletedTag.setId("deleted-tag-id");
+ deletedTag.setTagName("archived");
+ deletedTag.setDeleted(1);
+ when(tagDao.selectById("deleted-tag-id")).thenReturn(deletedTag);
+
+ AgentSnapshotTagDTO snapshotTag = new AgentSnapshotTagDTO();
+ snapshotTag.setId("deleted-tag-id");
+ snapshotTag.setTagName("archived");
+
+ InvocationTargetException error = assertThrows(InvocationTargetException.class,
+ () -> method.invoke(service, snapshotTag, new Date()));
+
+ assertTrue(error.getCause() instanceof RenException);
+ assertEquals("蹇収寮曠敤鐨勬爣绛惧凡琚垹闄わ紝鏃犳硶鎭㈠锛岃鍏堥噸鏂板垱寤烘垨閫夋嫨鏍囩", error.getCause().getMessage());
+ verify(tagDao, never()).updateById(any(AgentTagEntity.class));
+ verify(tagDao, never()).insert(any(AgentTagEntity.class));
+ }
+
+ private AgentInfoVO snapshotAgentInfo(String agentId, Long userId, String agentName, String summaryMemory) {
+ AgentInfoVO info = new AgentInfoVO();
+ info.setId(agentId);
+ info.setUserId(userId);
+ info.setAgentName(agentName);
+ info.setSummaryMemory(summaryMemory);
+ info.setChatHistoryConf(0);
+ return info;
+ }
+
+ private AgentSnapshotDataDTO snapshotData(String agentName, String summaryMemory) {
+ AgentSnapshotDataDTO data = new AgentSnapshotDataDTO();
+ data.setAgentName(agentName);
+ data.setSummaryMemory(summaryMemory);
+ data.setChatHistoryConf(0);
+ return data;
+ }
+
+ private AgentSnapshotDataDTO buildExtendedSensitiveSnapshot() {
+ AgentSnapshotDataDTO data = new AgentSnapshotDataDTO();
+ AgentUpdateDTO.FunctionInfo function = new AgentUpdateDTO.FunctionInfo();
+ function.setPluginId("rag");
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("api_key", "secret-api-key");
+ params.put("Cookie", "secret-cookie");
+ params.put("Set-Cookie", "secret-set-cookie");
+ params.put("Proxy-Authorization", "secret-proxy-authorization");
+ params.put("X-Session-ID", "secret-session-id");
+ params.put("X-Auth", "secret-auth-header");
+ params.put("slackWebhookUrl",
+ "https://hooks.slack.com/services/T111/B222/secret-slack-path");
+ params.put("max_tokens", 32);
+ function.setParamInfo(params);
+
+ ContextProviderDTO provider = new ContextProviderDTO();
+ provider.setUrl("https://discord.com/api/webhooks/123456/secret-discord-path");
+ Map<String, Object> headers = new LinkedHashMap<>();
+ headers.put("Authorization", "secret-authorization");
+ headers.put("Cookie", "secret-context-cookie");
+ headers.put("PHPSESSID", "secret-php-session");
+ headers.put("X-Session", "secret-x-session");
+ headers.put("Accept", "application/json");
+ provider.setHeaders(headers);
+
+ data.setFunctions(List.of(function));
+ data.setContextProviders(List.of(provider));
+ return data;
+ }
+
+ private AgentUpdateDTO.FunctionInfo functionInfo(String pluginId, Map<String, Object> params) {
+ AgentUpdateDTO.FunctionInfo function = new AgentUpdateDTO.FunctionInfo();
+ function.setPluginId(pluginId);
+ function.setParamInfo(params);
+ return function;
+ }
+
+ private void assertExtendedSecretsAbsent(String json) {
+ for (String secret : List.of(
+ "secret-api-key",
+ "secret-cookie",
+ "secret-set-cookie",
+ "secret-proxy-authorization",
+ "secret-session-id",
+ "secret-auth-header",
+ "secret-slack-path",
+ "secret-discord-path",
+ "secret-authorization",
+ "secret-context-cookie",
+ "secret-php-session",
+ "secret-x-session")) {
+ assertFalse(json.contains(secret), () -> "Sensitive value leaked: " + secret);
+ }
+ }
+
+ private AgentSnapshotEntity snapshotEntity(String snapshotId, String agentId, Integer versionNo,
+ AgentSnapshotDataDTO data) {
+ AgentSnapshotEntity entity = new AgentSnapshotEntity();
+ entity.setId(snapshotId);
+ entity.setAgentId(agentId);
+ entity.setUserId(7L);
+ entity.setVersionNo(versionNo);
+ entity.setSnapshotData(JsonUtils.toJsonString(data));
+ entity.setChangedFields("[]");
+ return entity;
+ }
+
+ private AgentSnapshotDataDTO buildSnapshot(String apiKey, String authToken, String description) {
+ AgentSnapshotDataDTO data = new AgentSnapshotDataDTO();
+
+ AgentUpdateDTO.FunctionInfo function = new AgentUpdateDTO.FunctionInfo();
+ function.setPluginId("rag");
+ HashMap<String, Object> params = new HashMap<>();
+ params.put("api_key", apiKey);
+ params.put("description", description);
+ function.setParamInfo(params);
+
+ ContextProviderDTO provider = new ContextProviderDTO();
+ provider.setUrl("https://example.com/context");
+ provider.setHeaders(Map.of("Authorization", authToken));
+
+ data.setFunctions(List.of(function));
+ data.setContextProviders(List.of(provider));
+ return data;
+ }
+
+ private AgentSnapshotDataDTO nestedWebhookSnapshot(String secret) {
+ AgentSnapshotDataDTO data = new AgentSnapshotDataDTO();
+ data.setFunctions(List.of(functionInfo("plugin", Map.of(
+ "webhookConfig", Map.of(
+ "value", "https://capability.example.com/public/" + secret)))));
+ return data;
+ }
+
+ private String currentStateToken(AgentSnapshotServiceImpl service, AgentSnapshotDataDTO data) {
+ return ReflectionTestUtils.invokeMethod(service, "buildCurrentStateToken", data);
+ }
+
+ private String normalizeWhitespace(String value) {
+ return value.replaceAll("\\s+", " ").trim();
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/agent/typehandler/ContextProviderListTypeHandlerTest.java b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/agent/typehandler/ContextProviderListTypeHandlerTest.java
new file mode 100644
index 0000000..95c9b15
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/agent/typehandler/ContextProviderListTypeHandlerTest.java
@@ -0,0 +1,73 @@
+package xiaozhi.modules.agent.typehandler;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.List;
+
+import org.apache.ibatis.type.TypeHandler;
+import org.apache.ibatis.type.TypeHandlerRegistry;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+
+import xiaozhi.common.utils.JsonUtils;
+import xiaozhi.modules.agent.dto.ContextProviderDTO;
+
+class ContextProviderListTypeHandlerTest {
+
+ private final ContextProviderListTypeHandler handler = new ContextProviderListTypeHandler();
+
+ @Test
+ void myBatisCanInstantiateHandlerForListField() {
+ TypeHandler<?> typeHandler = new TypeHandlerRegistry().getInstance(List.class,
+ ContextProviderListTypeHandler.class);
+
+ assertInstanceOf(ContextProviderListTypeHandler.class, typeHandler);
+ }
+
+ @Test
+ void resultSetDeserializationKeepsContextProviderDtoElementType() throws Exception {
+ ResultSet resultSet = mock(ResultSet.class);
+ when(resultSet.getString("context_providers"))
+ .thenReturn("[{\"url\":\"https://example.com/context\",\"headers\":{\"Authorization\":\"Bearer token\"}}]");
+
+ List<ContextProviderDTO> providers = handler.getNullableResult(resultSet, "context_providers");
+
+ assertEquals(1, providers.size());
+ assertInstanceOf(ContextProviderDTO.class, providers.get(0));
+ assertEquals("https://example.com/context", providers.get(0).getUrl());
+ assertEquals("Bearer token", providers.get(0).getHeaders().get("Authorization"));
+ }
+
+ @Test
+ void sqlNullRemainsNull() throws Exception {
+ ResultSet resultSet = mock(ResultSet.class);
+
+ assertNull(handler.getNullableResult(resultSet, 1));
+ }
+
+ @Test
+ void serializesProviderListAsJson() throws Exception {
+ ContextProviderDTO provider = new ContextProviderDTO();
+ provider.setUrl("https://example.com/context");
+ PreparedStatement statement = mock(PreparedStatement.class);
+
+ handler.setNonNullParameter(statement, 1, List.of(provider), null);
+
+ ArgumentCaptor<String> json = ArgumentCaptor.forClass(String.class);
+ verify(statement).setString(eq(1), json.capture());
+ List<ContextProviderDTO> serialized = JsonUtils.parseObject(json.getValue(), new TypeReference<>() {
+ });
+ assertEquals(1, serialized.size());
+ assertEquals("https://example.com/context", serialized.get(0).getUrl());
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/config/service/impl/ConfigServiceImplTest.java b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/config/service/impl/ConfigServiceImplTest.java
new file mode 100644
index 0000000..1e76d4b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/config/service/impl/ConfigServiceImplTest.java
@@ -0,0 +1,96 @@
+package xiaozhi.modules.config.service.impl;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.mockito.ArgumentMatchers.anyMap;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import xiaozhi.common.redis.RedisKeys;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.modules.agent.dao.AgentVoicePrintDao;
+import xiaozhi.modules.agent.service.AgentContextProviderService;
+import xiaozhi.modules.agent.service.AgentMcpAccessPointService;
+import xiaozhi.modules.agent.service.AgentPluginMappingService;
+import xiaozhi.modules.agent.service.AgentService;
+import xiaozhi.modules.agent.service.AgentTemplateService;
+import xiaozhi.modules.correctword.service.CorrectWordFileService;
+import xiaozhi.modules.device.service.DeviceService;
+import xiaozhi.modules.model.service.ModelConfigService;
+import xiaozhi.modules.sys.dto.SysParamsDTO;
+import xiaozhi.modules.sys.service.SysParamsService;
+import xiaozhi.modules.timbre.service.TimbreService;
+import xiaozhi.modules.voiceclone.service.VoiceCloneService;
+
+class ConfigServiceImplTest {
+
+ @Test
+ void cachedServerConfigIsCheckedAsAStringKeyedMapWithoutChangingNestedValues() {
+ RedisUtils redisUtils = mock(RedisUtils.class);
+ Map<String, Object> nested = new HashMap<>();
+ nested.put("enabled", true);
+ Map<String, Object> cached = new HashMap<>();
+ cached.put("features", nested);
+ when(redisUtils.get(RedisKeys.getServerConfigKey())).thenReturn(cached);
+
+ ConfigServiceImpl service = newService(mock(SysParamsService.class), redisUtils);
+
+ Map<String, Object> result = service.getConfig(true);
+
+ assertNotSame(cached, result);
+ assertSame(nested, result.get("features"));
+ assertEquals(true, ((Map<?, ?>) result.get("features")).get("enabled"));
+ }
+
+ @Test
+ void nestedSystemParametersStillShareAndPopulateTheSameConfigBranch() {
+ SysParamsService sysParamsService = mock(SysParamsService.class);
+ SysParamsDTO enabled = parameter("server.features.enabled", "true", "boolean");
+ SysParamsDTO labels = parameter("server.features.labels", "first;second", "array");
+ when(sysParamsService.list(anyMap())).thenReturn(List.of(enabled, labels));
+ ConfigServiceImpl service = newService(sysParamsService, mock(RedisUtils.class));
+ Map<String, Object> config = new HashMap<>();
+
+ Object returned = ReflectionTestUtils.invokeMethod(service, "buildConfig", config);
+
+ assertSame(config, returned);
+ Map<?, ?> server = assertInstanceOf(Map.class, config.get("server"));
+ Map<?, ?> features = assertInstanceOf(Map.class, server.get("features"));
+ assertEquals(true, features.get("enabled"));
+ assertEquals(List.of("first", "second"), features.get("labels"));
+ }
+
+ private static SysParamsDTO parameter(String code, String value, String type) {
+ SysParamsDTO parameter = new SysParamsDTO();
+ parameter.setParamCode(code);
+ parameter.setParamValue(value);
+ parameter.setValueType(type);
+ return parameter;
+ }
+
+ private static ConfigServiceImpl newService(SysParamsService sysParamsService, RedisUtils redisUtils) {
+ return new ConfigServiceImpl(
+ sysParamsService,
+ mock(DeviceService.class),
+ mock(ModelConfigService.class),
+ mock(AgentService.class),
+ mock(AgentTemplateService.class),
+ redisUtils,
+ mock(TimbreService.class),
+ mock(AgentPluginMappingService.class),
+ mock(AgentMcpAccessPointService.class),
+ mock(AgentContextProviderService.class),
+ mock(VoiceCloneService.class),
+ mock(AgentVoicePrintDao.class),
+ mock(CorrectWordFileService.class));
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/DeviceTest.java b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/DeviceTest.java
new file mode 100644
index 0000000..7688184
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/DeviceTest.java
@@ -0,0 +1,76 @@
+package xiaozhi.modules.device;
+
+import java.util.HashMap;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.modules.sys.dto.SysUserDTO;
+import xiaozhi.modules.sys.service.SysUserService;
+
+@Slf4j
+@SpringBootTest
+@ActiveProfiles("dev")
+@DisplayName("璁惧娴嬭瘯")
+public class DeviceTest {
+
+ @Autowired
+ private RedisUtils redisUtils;
+ @Autowired
+ private SysUserService sysUserService;
+
+ @Test
+ public void testRejectWeakPassword() {
+ SysUserDTO userDTO = new SysUserDTO();
+ userDTO.setUsername("test");
+ userDTO.setPassword("weak-password-123");
+
+ RenException exception = Assertions.assertThrows(RenException.class, () -> sysUserService.save(userDTO));
+ Assertions.assertEquals(ErrorCode.PASSWORD_WEAK_ERROR, exception.getCode());
+ }
+
+ @Test
+ @DisplayName("娴嬭瘯鍐欏叆璁惧淇℃伅")
+ public void testWriteDeviceInfo() {
+ log.info("寮�濮嬫祴璇曞啓鍏ヨ澶囦俊鎭�...");
+ // 妯℃嫙璁惧MAC鍦板潃
+ String macAddress = "00:11:22:33:44:66";
+ // 妯℃嫙璁惧楠岃瘉鐮�
+ String deviceCode = "123456";
+
+ HashMap<String, Object> map = new HashMap<>();
+ map.put("mac_address", macAddress);
+ map.put("activation_code", deviceCode);
+ map.put("board", "纭欢鍨嬪彿");
+ map.put("app_version", "0.3.13");
+
+ String safeDeviceId = macAddress.replace(":", "_").toLowerCase();
+ String cacheDeviceKey = String.format("ota:activation:data:%s", safeDeviceId);
+ redisUtils.set(cacheDeviceKey, map, 300);
+
+ String redisKey = "ota:activation:code:" + deviceCode;
+ log.info("Redis Key: {}", redisKey);
+
+ // 灏嗚澶囦俊鎭啓鍏edis
+ redisUtils.set(redisKey, macAddress, 300);
+ log.info("璁惧淇℃伅宸插啓鍏edis");
+
+ // 楠岃瘉鏄惁鍐欏叆鎴愬姛
+ String savedMacAddress = (String) redisUtils.get(redisKey);
+ log.info("浠嶳edis璇诲彇鐨凪AC鍦板潃: {}", savedMacAddress);
+
+ // 浣跨敤鏂█楠岃瘉
+ Assertions.assertNotNull(savedMacAddress, "浠嶳edis璇诲彇鐨凪AC鍦板潃涓嶅簲涓虹┖");
+ Assertions.assertEquals(macAddress, savedMacAddress, "淇濆瓨鐨凪AC鍦板潃涓庡師濮婱AC鍦板潃涓嶅尮閰�");
+
+ log.info("娴嬭瘯瀹屾垚");
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/controller/DeviceControllerTest.java b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/controller/DeviceControllerTest.java
new file mode 100644
index 0000000..a3fa0ad
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/controller/DeviceControllerTest.java
@@ -0,0 +1,78 @@
+package xiaozhi.modules.device.controller;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.common.user.UserDetail;
+import xiaozhi.common.utils.MessageUtils;
+import xiaozhi.common.utils.Result;
+import xiaozhi.modules.device.dto.DeviceUpdateDTO;
+import xiaozhi.modules.device.entity.DeviceEntity;
+import xiaozhi.modules.device.service.DeviceAddressBookService;
+import xiaozhi.modules.device.service.DeviceService;
+import xiaozhi.modules.security.user.SecurityUser;
+import xiaozhi.modules.sys.service.SysParamsService;
+
+@DisplayName("璁惧鏇存柊鎺ュ彛鍥炲綊娴嬭瘯")
+class DeviceControllerTest {
+
+ private static final String DEVICE_ID = "device-id";
+ private static final long USER_ID = 1L;
+
+ @Test
+ @DisplayName("鏁版嵁搴撴湭鏇存柊鏃朵笉璇姤鑷姩鍗囩骇鐘舵�佷慨鏀规垚鍔�")
+ void updateFailureIsReturnedToCaller() {
+ DeviceService deviceService = mock(DeviceService.class);
+ DeviceEntity entity = ownedDevice();
+ when(deviceService.selectById(DEVICE_ID)).thenReturn(entity);
+ when(deviceService.updateById(entity)).thenReturn(false);
+ DeviceController controller = controller(deviceService);
+
+ DeviceUpdateDTO update = new DeviceUpdateDTO();
+ update.setAutoUpdate(0);
+
+ try (MockedStatic<SecurityUser> securityUser = mockStatic(SecurityUser.class);
+ MockedStatic<MessageUtils> messageUtils = mockStatic(MessageUtils.class)) {
+ securityUser.when(SecurityUser::getUser).thenReturn(currentUser());
+ messageUtils.when(() -> MessageUtils.getMessage(ErrorCode.UPDATE_DATA_FAILED))
+ .thenReturn("Failed to update data");
+
+ Result<Void> result = controller.updateDeviceInfo(DEVICE_ID, update);
+
+ assertEquals(ErrorCode.UPDATE_DATA_FAILED, result.getCode());
+ assertEquals(0, entity.getAutoUpdate());
+ verify(deviceService).updateById(entity);
+ }
+ }
+
+ private DeviceController controller(DeviceService deviceService) {
+ return new DeviceController(
+ deviceService,
+ mock(DeviceAddressBookService.class),
+ mock(RedisUtils.class),
+ mock(SysParamsService.class));
+ }
+
+ private DeviceEntity ownedDevice() {
+ DeviceEntity entity = new DeviceEntity();
+ entity.setId(DEVICE_ID);
+ entity.setUserId(USER_ID);
+ entity.setAutoUpdate(1);
+ return entity;
+ }
+
+ private UserDetail currentUser() {
+ UserDetail user = new UserDetail();
+ user.setId(USER_ID);
+ return user;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/service/impl/DeviceAddressBookServiceImplTest.java b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/service/impl/DeviceAddressBookServiceImplTest.java
new file mode 100644
index 0000000..69ae3b6
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/service/impl/DeviceAddressBookServiceImplTest.java
@@ -0,0 +1,46 @@
+package xiaozhi.modules.device.service.impl;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.modules.device.dao.DeviceAddressBookDao;
+import xiaozhi.modules.device.entity.DeviceAddressBookEntity;
+import xiaozhi.modules.device.service.DeviceService;
+import xiaozhi.modules.sys.service.SysParamsService;
+
+class DeviceAddressBookServiceImplTest {
+
+ @Test
+ void newEntryUsesTheDedicatedAddressBookInsertMapping() {
+ DeviceAddressBookDao addressBookDao = mock(DeviceAddressBookDao.class);
+ RedisUtils redisUtils = mock(RedisUtils.class);
+ DeviceService deviceService = mock(DeviceService.class);
+ SysParamsService sysParamsService = mock(SysParamsService.class);
+ DeviceAddressBookServiceImpl service = new DeviceAddressBookServiceImpl(
+ addressBookDao, redisUtils, deviceService, sysParamsService);
+ when(addressBookDao.selectOne(any())).thenReturn(null);
+ when(addressBookDao.selectList(null)).thenReturn(List.of());
+
+ service.saveOrUpdate("00:11:22:33:44:55", "00:11:22:33:44:66", "living-room", true);
+
+ ArgumentCaptor<DeviceAddressBookEntity> entityCaptor = ArgumentCaptor.forClass(DeviceAddressBookEntity.class);
+ verify(addressBookDao).insertAddressBook(entityCaptor.capture());
+ DeviceAddressBookEntity entity = entityCaptor.getValue();
+ assertEquals("00:11:22:33:44:55", entity.getMacAddress());
+ assertEquals("00:11:22:33:44:66", entity.getTargetMac());
+ assertEquals("living-room", entity.getAlias());
+ assertTrue(entity.getHasPermission());
+ verify(addressBookDao, never()).insert(any(DeviceAddressBookEntity.class));
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/service/impl/DeviceAutoUpdateTest.java b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/service/impl/DeviceAutoUpdateTest.java
new file mode 100644
index 0000000..67a29a6
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/service/impl/DeviceAutoUpdateTest.java
@@ -0,0 +1,104 @@
+package xiaozhi.modules.device.service.impl;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.aop.framework.ProxyFactory;
+
+import xiaozhi.common.constant.Constant;
+import xiaozhi.modules.device.dto.DeviceReportReqDTO;
+import xiaozhi.modules.device.dto.DeviceReportRespDTO;
+import xiaozhi.modules.device.entity.DeviceEntity;
+import xiaozhi.modules.device.service.OtaService;
+import xiaozhi.modules.sys.service.SysParamsService;
+
+@DisplayName("璁惧鑷姩鍗囩骇鍥炲綊娴嬭瘯")
+class DeviceAutoUpdateTest {
+
+ private static final String MAC_ADDRESS = "00:11:22:33:44:55";
+ private static final String BOARD_TYPE = "test-board";
+ private static final String CURRENT_VERSION = "1.0.0";
+
+ @Test
+ @DisplayName("#3299 鑷姩鍗囩骇鍏抽棴鏃� OTA 鍝嶅簲涓嶅寘鍚浐浠�")
+ void disabledAutoUpdateOmitsFirmware() {
+ OtaService otaService = mock(OtaService.class);
+ DeviceServiceImpl service = proxiedService(deviceWithAutoUpdate(0), otaService);
+
+ DeviceReportRespDTO response = service.checkDeviceActive(
+ MAC_ADDRESS, MAC_ADDRESS, deviceReport());
+
+ assertNull(response.getFirmware());
+ verifyNoInteractions(otaService);
+ }
+
+ @Test
+ @DisplayName("鑷姩鍗囩骇寮�鍚椂缁х画鎵ц鍥轰欢鏌ヨ")
+ void enabledAutoUpdateChecksFirmware() {
+ OtaService otaService = mock(OtaService.class);
+ when(otaService.getLatestOta(BOARD_TYPE)).thenReturn(null);
+ DeviceServiceImpl service = proxiedService(deviceWithAutoUpdate(1), otaService);
+
+ DeviceReportRespDTO response = service.checkDeviceActive(
+ MAC_ADDRESS, MAC_ADDRESS, deviceReport());
+
+ assertNotNull(response.getFirmware());
+ assertEquals(CURRENT_VERSION, response.getFirmware().getVersion());
+ assertEquals(Constant.INVALID_FIRMWARE_URL, response.getFirmware().getUrl());
+ verify(otaService).getLatestOta(BOARD_TYPE);
+ }
+
+ private DeviceServiceImpl proxiedService(DeviceEntity device, OtaService otaService) {
+ SysParamsService sysParamsService = mock(SysParamsService.class);
+ when(sysParamsService.getValue(Constant.SERVER_WEBSOCKET, true))
+ .thenReturn("ws://127.0.0.1:8000/xiaozhi/v1/");
+ when(sysParamsService.getValue(Constant.SERVER_AUTH_ENABLED, true)).thenReturn("false");
+ when(sysParamsService.getValue(Constant.SERVER_MQTT_GATEWAY, true)).thenReturn(null);
+
+ DeviceServiceImpl target = new DeviceServiceImpl(
+ null, null, sysParamsService, null, otaService, null) {
+ @Override
+ public DeviceEntity getDeviceByMacAddress(String macAddress) {
+ return device;
+ }
+
+ @Override
+ public void updateDeviceConnectionInfo(String agentId, String deviceId, String appVersion) {
+ // No-op: connection timestamps are outside this OTA decision test.
+ }
+ };
+
+ ProxyFactory proxyFactory = new ProxyFactory(target);
+ proxyFactory.setProxyTargetClass(true);
+ proxyFactory.setExposeProxy(true);
+ return (DeviceServiceImpl) proxyFactory.getProxy();
+ }
+
+ private DeviceEntity deviceWithAutoUpdate(int autoUpdate) {
+ DeviceEntity device = new DeviceEntity();
+ device.setId(MAC_ADDRESS);
+ device.setMacAddress(MAC_ADDRESS);
+ device.setBoard(BOARD_TYPE);
+ device.setAutoUpdate(autoUpdate);
+ return device;
+ }
+
+ private DeviceReportReqDTO deviceReport() {
+ DeviceReportReqDTO.Application application = new DeviceReportReqDTO.Application();
+ application.setVersion(CURRENT_VERSION);
+ DeviceReportReqDTO.BoardInfo board = new DeviceReportReqDTO.BoardInfo();
+ board.setType(BOARD_TYPE);
+
+ DeviceReportReqDTO report = new DeviceReportReqDTO();
+ report.setApplication(application);
+ report.setBoard(board);
+ return report;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/service/impl/DeviceTimeSerializationTest.java b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/service/impl/DeviceTimeSerializationTest.java
new file mode 100644
index 0000000..bf2e148
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/service/impl/DeviceTimeSerializationTest.java
@@ -0,0 +1,106 @@
+package xiaozhi.modules.device.service.impl;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.util.Date;
+import java.util.List;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import xiaozhi.modules.device.entity.DeviceEntity;
+import xiaozhi.modules.device.vo.UserShowDeviceListVO;
+import xiaozhi.modules.security.config.WebMvcConfig;
+
+@DisplayName("璁惧鏃堕棿搴忓垪鍖栧洖褰掓祴璇�")
+class DeviceTimeSerializationTest {
+
+ @ParameterizedTest(name = "娴忚鍣ㄦ椂鍖� {0}")
+ @ValueSource(strings = { "Asia/Shanghai", "America/Sao_Paulo" })
+ @DisplayName("#3280 缁戝畾鏃堕棿鍜屾渶鍚庤繛鎺ユ椂闂村湪浠绘剰娴忚鍣ㄦ椂鍖洪兘琛ㄧず鍚屼竴鏃跺埢")
+ void serializedDeviceTimesDescribeTheSameInstantAcrossBrowserTimeZones(String browserTimeZone) {
+ Instant connectedAt = Instant.parse("2026-07-10T13:21:42Z");
+ DeviceEntity entity = new DeviceEntity();
+ entity.setCreateDate(Date.from(connectedAt));
+ entity.setLastConnectedAt(Date.from(connectedAt));
+
+ DeviceServiceImpl deviceService = serviceReturning(entity);
+ UserShowDeviceListVO device = deviceService.getUserDeviceList(1L, "agent-id").getFirst();
+
+ ObjectMapper objectMapper = new WebMvcConfig().jackson2HttpMessageConverter().getObjectMapper();
+ JsonNode payload = objectMapper.valueToTree(device);
+ Instant createDate = Instant.ofEpochMilli(
+ Long.parseLong(payload.path("createDateTimestamp").asText()));
+ Instant lastConnectedAt = Instant.ofEpochMilli(
+ Long.parseLong(payload.path("lastConnectedAtTimestamp").asText()));
+ ZoneId browserZone = ZoneId.of(browserTimeZone);
+
+ assertAll(
+ () -> assertTrue(payload.path("createDateTimestamp").isTextual(),
+ "Long 鏃堕棿鎴冲繀椤婚伒寰幇鏈� JSON 濂戠害搴忓垪鍖栦负瀛楃涓�"),
+ () -> assertTrue(payload.path("lastConnectedAtTimestamp").isTextual(),
+ "Long 鏃堕棿鎴冲繀椤婚伒寰幇鏈� JSON 濂戠害搴忓垪鍖栦负瀛楃涓�"),
+ () -> assertEquals(connectedAt, createDate,
+ "createDateTimestamp 蹇呴』淇濈暀婧愭椂闂寸偣"),
+ () -> assertEquals(connectedAt, lastConnectedAt,
+ "lastConnectedAtTimestamp 蹇呴』淇濈暀婧愭椂闂寸偣"),
+ () -> assertEquals(lastConnectedAt.atZone(browserZone).toLocalDateTime(),
+ createDate.atZone(browserZone).toLocalDateTime(),
+ "缁戝畾鏃堕棿鍜屾渶鍚庤繛鎺ユ椂闂村湪鍚屼竴娴忚鍣ㄤ腑蹇呴』鏄剧ず涓虹浉鍚岀殑鏈湴鏃堕棿"),
+ () -> assertTrue(payload.path("createDate").isTextual(),
+ "鍏煎瀛楁 createDate 蹇呴』缁х画淇濈暀"));
+ }
+
+ @Test
+ @DisplayName("鏃堕棿涓虹┖鏃舵柊鏃у瓧娈靛潎淇濇寔 null")
+ void nullDeviceTimesRemainNull() {
+ DeviceEntity entity = new DeviceEntity();
+ DeviceServiceImpl deviceService = serviceReturning(entity);
+
+ UserShowDeviceListVO device = deviceService.getUserDeviceList(1L, "agent-id").getFirst();
+ ObjectMapper objectMapper = new WebMvcConfig().jackson2HttpMessageConverter().getObjectMapper();
+ JsonNode payload = objectMapper.valueToTree(device);
+
+ assertAll(
+ () -> assertTrue(payload.path("createDateTimestamp").isNull()),
+ () -> assertTrue(payload.path("lastConnectedAtTimestamp").isNull()),
+ () -> assertTrue(payload.path("createDate").isNull()));
+ }
+
+ @ParameterizedTest(name = "鑷姩鍗囩骇鐘舵�� {0}")
+ @ValueSource(ints = { 0, 1 })
+ @DisplayName("#3299 璁惧鍒楄〃鎸� autoUpdate 濂戠害杩斿洖鐪熷疄寮�鍏崇姸鎬�")
+ void serializedDeviceContainsAutoUpdateState(int autoUpdate) {
+ DeviceEntity entity = new DeviceEntity();
+ entity.setAutoUpdate(autoUpdate);
+ DeviceServiceImpl deviceService = serviceReturning(entity);
+
+ UserShowDeviceListVO device = deviceService.getUserDeviceList(1L, "agent-id").getFirst();
+ ObjectMapper objectMapper = new WebMvcConfig().jackson2HttpMessageConverter().getObjectMapper();
+ JsonNode payload = objectMapper.valueToTree(device);
+
+ assertAll(
+ () -> assertEquals(autoUpdate, device.getAutoUpdate()),
+ () -> assertEquals(autoUpdate, payload.path("autoUpdate").asInt()),
+ () -> assertTrue(payload.path("otaUpgrade").isMissingNode(),
+ "璁惧鍒楄〃涓嶅簲缁х画鏆撮湶鏈槧灏勭殑鏃у瓧娈� otaUpgrade"));
+ }
+
+ private DeviceServiceImpl serviceReturning(DeviceEntity entity) {
+ return new DeviceServiceImpl(null, null, null, null, null, null) {
+ @Override
+ public List<DeviceEntity> getUserDevices(Long userId, String agentId) {
+ return List.of(entity);
+ }
+ };
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/service/impl/MqttGatewayAuthorizationTest.java b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/service/impl/MqttGatewayAuthorizationTest.java
new file mode 100644
index 0000000..c3644b5
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/device/service/impl/MqttGatewayAuthorizationTest.java
@@ -0,0 +1,180 @@
+package xiaozhi.modules.device.service.impl;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.NullAndEmptySource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+
+import cn.hutool.crypto.digest.DigestUtil;
+
+@DisplayName("MQTT Gateway 鏃ユ湡閴存潈鍥炲綊娴嬭瘯")
+class MqttGatewayAuthorizationTest {
+
+ private static final String SIGNATURE_KEY = "test-signature-key";
+ private static final Instant FIXED_INSTANT = Instant.parse("2026-07-14T00:30:00Z");
+
+ private HttpServer server;
+
+ @AfterEach
+ void stopServer() {
+ if (server != null) {
+ server.stop(0);
+ }
+ }
+
+ @Test
+ @DisplayName("浠� UTC 鏃ユ湡鐢熸垚褰撳ぉ銆佸墠涓�澶╁拰鍚庝竴澶╀笁涓�欓�� token")
+ void generatesUtcDateCandidates() {
+ List<String> tokens = MqttGatewayAuthorization.generateDailyTokens(SIGNATURE_KEY, FIXED_INSTANT);
+
+ assertEquals(List.of(
+ tokenFor("2026-07-14"),
+ tokenFor("2026-07-13"),
+ tokenFor("2026-07-15")), tokens);
+ }
+
+ @ParameterizedTest(name = "Gateway 鏃跺尯 {0}")
+ @MethodSource("gatewayTimeZones")
+ @DisplayName("鍊欓�� token 瑕嗙洊涓婃捣鍜屽湥淇濈綏 Gateway 鐨勬湰鍦版棩鏈�")
+ void coversGatewayLocalDate(String gatewayTimeZone, Instant now, int expectedTokenIndex) {
+ String gatewayDate = now.atZone(ZoneId.of(gatewayTimeZone)).toLocalDate().toString();
+ List<String> tokens = MqttGatewayAuthorization.generateDailyTokens(SIGNATURE_KEY, now);
+
+ assertEquals(tokenFor(gatewayDate), tokens.get(expectedTokenIndex));
+ }
+
+ @Test
+ @DisplayName("浠呭湪 401 鏃舵寜鏃ユ湡鍊欓�夐噸璇曞苟淇濈暀璇锋眰浣�")
+ void retriesOnlyAuthenticationFailures() throws IOException {
+ AtomicInteger requestCount = new AtomicInteger();
+ List<String> authorizationHeaders = new ArrayList<>();
+ List<String> requestBodies = new ArrayList<>();
+ List<String> expectedTokens = MqttGatewayAuthorization.generateDailyTokens(SIGNATURE_KEY, FIXED_INSTANT);
+ startServer(exchange -> {
+ int attempt = requestCount.getAndIncrement();
+ authorizationHeaders.add(exchange.getRequestHeaders().getFirst("Authorization"));
+ requestBodies.add(new String(exchange.getRequestBody().readAllBytes(), StandardCharsets.UTF_8));
+ if (attempt == 0) {
+ respond(exchange, 401, "{\"error\":\"unauthorized\"}");
+ } else if (attempt == 1) {
+ respond(exchange, 401, "{\"error\":\"unauthorized\"}");
+ } else {
+ respond(exchange, 200, "{\"online\":true}");
+ }
+ });
+
+ String requestBody = "{\"clientIds\":[\"device-id\"]}";
+ String response = MqttGatewayAuthorization.postJson(
+ serverUrl(), requestBody, SIGNATURE_KEY, FIXED_INSTANT);
+
+ assertEquals("{\"online\":true}", response);
+ assertEquals(3, requestCount.get());
+ assertEquals(expectedTokens.stream().map(token -> "Bearer " + token).toList(), authorizationHeaders);
+ assertEquals(List.of(requestBody, requestBody, requestBody), requestBodies);
+ }
+
+ @ParameterizedTest(name = "HTTP {0}")
+ @ValueSource(ints = { 403, 500 })
+ @DisplayName("闈� 401 閿欒涓嶉噸璇曚笖鍚戜笂鎶涘嚭")
+ void doesNotRetryNonAuthenticationFailure(int statusCode) throws IOException {
+ AtomicInteger requestCount = new AtomicInteger();
+ startServer(exchange -> {
+ requestCount.incrementAndGet();
+ exchange.getRequestBody().readAllBytes();
+ respond(exchange, statusCode, "{\"error\":\"request rejected\"}");
+ });
+
+ MqttGatewayAuthorization.GatewayRequestException exception = assertThrows(
+ MqttGatewayAuthorization.GatewayRequestException.class,
+ () -> MqttGatewayAuthorization.postJson(
+ serverUrl(), "{}", SIGNATURE_KEY, FIXED_INSTANT));
+
+ assertEquals(statusCode, exception.statusCode());
+ assertEquals(1, requestCount.get());
+ }
+
+ @Test
+ @DisplayName("鎵�鏈夋棩鏈熷�欓�夎鎷掔粷鏃朵笉鎶婇敊璇搷搴斿綋浣滆澶囩绾挎暟鎹�")
+ void propagatesAuthenticationFailureAfterAllCandidatesAreRejected() throws IOException {
+ AtomicInteger requestCount = new AtomicInteger();
+ startServer(exchange -> {
+ requestCount.incrementAndGet();
+ exchange.getRequestBody().readAllBytes();
+ respond(exchange, 401, "{\"error\":\"unauthorized\"}");
+ });
+
+ MqttGatewayAuthorization.GatewayRequestException exception = assertThrows(
+ MqttGatewayAuthorization.GatewayRequestException.class,
+ () -> MqttGatewayAuthorization.postJson(
+ serverUrl(), "{}", SIGNATURE_KEY, FIXED_INSTANT));
+
+ assertEquals(401, exception.statusCode());
+ assertEquals(3, requestCount.get());
+ }
+
+ @ParameterizedTest(name = "瀵嗛挜鍊� [{0}]")
+ @NullAndEmptySource
+ @ValueSource(strings = { " ", "null", " NULL " })
+ @DisplayName("缂哄皯鎴栧崰浣嶇鍚嶅瘑閽ユ椂鍦ㄥ彂璧� HTTP 璇锋眰鍓嶅け璐�")
+ void rejectsMissingSignatureKeyBeforeSendingRequest(String signatureKey) {
+ MqttGatewayAuthorization.GatewayRequestException exception = assertThrows(
+ MqttGatewayAuthorization.GatewayRequestException.class,
+ () -> MqttGatewayAuthorization.postJson(
+ "http://127.0.0.1:1", "{}", signatureKey, FIXED_INSTANT));
+
+ assertNull(exception.statusCode());
+ }
+
+ private static Stream<Arguments> gatewayTimeZones() {
+ return Stream.of(
+ Arguments.of("Asia/Shanghai", Instant.parse("2026-07-13T17:30:00Z"), 2),
+ Arguments.of("America/Sao_Paulo", Instant.parse("2026-07-14T00:30:00Z"), 1));
+ }
+
+ private void startServer(HttpHandler handler) throws IOException {
+ server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
+ server.createContext("/mqtt", handler);
+ server.start();
+ }
+
+ private String serverUrl() {
+ assertTrue(server != null, "test server must be started");
+ return "http://127.0.0.1:" + server.getAddress().getPort() + "/mqtt";
+ }
+
+ private static void respond(HttpExchange exchange, int statusCode, String body) throws IOException {
+ byte[] bytes = body.getBytes(StandardCharsets.UTF_8);
+ exchange.getResponseHeaders().set("Content-Type", "application/json");
+ exchange.sendResponseHeaders(statusCode, bytes.length);
+ try (var output = exchange.getResponseBody()) {
+ output.write(bytes);
+ }
+ }
+
+ private static String tokenFor(String date) {
+ return DigestUtil.sha256Hex(date + SIGNATURE_KEY);
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/security/config/ShiroConfigTest.java b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/security/config/ShiroConfigTest.java
new file mode 100644
index 0000000..9b10be1
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/security/config/ShiroConfigTest.java
@@ -0,0 +1,60 @@
+package xiaozhi.modules.security.config;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import org.apache.shiro.session.mgt.SessionManager;
+import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
+import org.apache.shiro.web.mgt.WebSecurityManager;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.context.annotation.Role;
+
+import xiaozhi.modules.security.oauth2.Oauth2Realm;
+import xiaozhi.modules.sys.service.SysParamsService;
+
+class ShiroConfigTest {
+
+ @Test
+ void beanPostProcessorFactoriesDoNotInstantiateShiroConfigOrBusinessDependenciesEarly() throws Exception {
+ Method lifecycleFactory = ShiroConfig.class.getDeclaredMethod("lifecycleBeanPostProcessor");
+ Method filterFactory = ShiroConfig.class.getDeclaredMethod(
+ "shirFilter", WebSecurityManager.class, SysParamsService.class);
+
+ assertTrue(Modifier.isStatic(lifecycleFactory.getModifiers()));
+ assertTrue(BeanPostProcessor.class.isAssignableFrom(ShiroFilterFactoryBean.class));
+ assertTrue(Modifier.isStatic(filterFactory.getModifiers()));
+ Lazy securityManagerLazy = filterFactory.getParameters()[0].getAnnotation(Lazy.class);
+ Lazy sysParamsServiceLazy = filterFactory.getParameters()[1].getAnnotation(Lazy.class);
+ assertNotNull(securityManagerLazy);
+ assertNotNull(sysParamsServiceLazy);
+ assertTrue(securityManagerLazy.value());
+ assertTrue(sysParamsServiceLazy.value());
+ }
+
+ @Test
+ void authorizationAdvisorIsInfrastructureAndDefersItsSecurityManager() throws Exception {
+ Method advisorFactory = ShiroConfig.class.getDeclaredMethod(
+ "authorizationAttributeSourceAdvisor", WebSecurityManager.class);
+
+ assertTrue(Modifier.isStatic(advisorFactory.getModifiers()));
+ Lazy securityManagerLazy = advisorFactory.getParameters()[0].getAnnotation(Lazy.class);
+ assertNotNull(securityManagerLazy);
+ assertTrue(securityManagerLazy.value());
+ assertEquals(BeanDefinition.ROLE_INFRASTRUCTURE, advisorFactory.getAnnotation(Role.class).value());
+ }
+
+ @Test
+ void securityManagerRetainsTheWebSecurityContractRequiredByShiroFilter() throws Exception {
+ Method securityManagerFactory = ShiroConfig.class.getDeclaredMethod(
+ "securityManager", Oauth2Realm.class, SessionManager.class);
+
+ assertEquals(WebSecurityManager.class, securityManagerFactory.getReturnType());
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/sys/loginControllerTest.java b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/sys/loginControllerTest.java
new file mode 100644
index 0000000..4c51288
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/sys/loginControllerTest.java
@@ -0,0 +1,72 @@
+package xiaozhi.modules.sys;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.when;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.bean.override.mockito.MockitoBean;
+
+import lombok.extern.slf4j.Slf4j;
+import xiaozhi.common.exception.ErrorCode;
+import xiaozhi.common.exception.RenException;
+import xiaozhi.modules.security.controller.LoginController;
+import xiaozhi.modules.security.dto.LoginDTO;
+import xiaozhi.modules.security.dto.SmsVerificationDTO;
+import xiaozhi.modules.sys.dto.RetrievePasswordDTO;
+import xiaozhi.modules.sys.service.SysUserService;
+
+@Slf4j
+@SpringBootTest
+@ActiveProfiles("dev")
+class loginControllerTest {
+
+ @Autowired
+ LoginController loginController;
+
+ @MockitoBean
+ SysUserService sysUserService;
+
+ @Test
+ public void testRegister() {
+ when(sysUserService.getAllowUserRegister()).thenReturn(false);
+
+ LoginDTO loginDTO = new LoginDTO();
+ loginDTO.setUsername("鎵嬫満鍙风爜");
+ loginDTO.setPassword("瀵嗙爜");
+
+ RenException exception = assertThrows(RenException.class, () -> loginController.register(loginDTO));
+ assertEquals(ErrorCode.USER_REGISTER_DISABLED, exception.getCode());
+ }
+
+ @Test
+ public void testSmsVerification() {
+ try {
+ SmsVerificationDTO smsVerificationDTO = new SmsVerificationDTO();
+ smsVerificationDTO.setPhone("鎵嬫満鍙风爜");
+ smsVerificationDTO.setCaptchaId("123456");
+ smsVerificationDTO.setCaptcha("123456");
+ loginController.smsVerification(smsVerificationDTO);
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ @Test
+ public void testRetrievePassword() {
+ try {
+ RetrievePasswordDTO retrievePasswordDTO = new RetrievePasswordDTO();
+ retrievePasswordDTO.setCode("123456");
+ retrievePasswordDTO.setPhone("鎵嬫満鍙风爜");
+ retrievePasswordDTO.setPassword("瀵嗙爜");
+ loginController.retrievePassword(retrievePasswordDTO);
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+
+ }
+
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/sys/service/impl/SysDictDataServiceImplTest.java b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/sys/service/impl/SysDictDataServiceImplTest.java
new file mode 100644
index 0000000..1383969
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/sys/service/impl/SysDictDataServiceImplTest.java
@@ -0,0 +1,35 @@
+package xiaozhi.modules.sys.service.impl;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import xiaozhi.common.redis.RedisKeys;
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.modules.sys.dao.SysUserDao;
+import xiaozhi.modules.sys.vo.SysDictDataItem;
+
+class SysDictDataServiceImplTest {
+
+ @Test
+ void cachedDictionaryItemsAreCheckedAndReturnedAsDtos() {
+ RedisUtils redisUtils = mock(RedisUtils.class);
+ SysDictDataItem item = new SysDictDataItem();
+ item.setName("enabled");
+ item.setKey("1");
+ List<SysDictDataItem> cached = new ArrayList<>(List.of(item));
+ when(redisUtils.get(RedisKeys.getDictDataByTypeKey("status"))).thenReturn(cached);
+ SysDictDataServiceImpl service = new SysDictDataServiceImpl(mock(SysUserDao.class), redisUtils);
+
+ List<SysDictDataItem> result = service.getDictDataByType("status");
+
+ assertNotSame(cached, result);
+ assertEquals(List.of(item), result);
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/sys/service/impl/SysParamsServiceImplTest.java b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/sys/service/impl/SysParamsServiceImplTest.java
new file mode 100644
index 0000000..cf12ee0
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/sys/service/impl/SysParamsServiceImplTest.java
@@ -0,0 +1,36 @@
+package xiaozhi.modules.sys.service.impl;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import xiaozhi.common.constant.Constant;
+import xiaozhi.modules.agent.service.AgentPluginMappingService;
+import xiaozhi.modules.sys.dao.SysParamsDao;
+import xiaozhi.modules.sys.redis.SysParamsRedis;
+
+class SysParamsServiceImplTest {
+
+ @Test
+ void disablingAddressBookStillDeletesItsSystemPlugin() {
+ SysParamsRedis sysParamsRedis = mock(SysParamsRedis.class);
+ AgentPluginMappingService pluginMappingService = mock(AgentPluginMappingService.class);
+ SysParamsDao sysParamsDao = mock(SysParamsDao.class);
+ SysParamsServiceImpl service = new SysParamsServiceImpl(sysParamsRedis, pluginMappingService);
+ ReflectionTestUtils.setField(service, "baseDao", sysParamsDao);
+
+ String currentConfig = "{\"features\":{\"addressBook\":{\"enabled\":true}}}";
+ String newConfig = "{\"features\":{\"addressBook\":{\"enabled\":false}}}";
+ when(sysParamsDao.getValueByCode(Constant.SYSTEM_WEB_MENU)).thenReturn(currentConfig);
+ when(sysParamsDao.updateValueByCode(Constant.SYSTEM_WEB_MENU, newConfig)).thenReturn(1);
+
+ service.updateSystemWebMenu(newConfig);
+
+ verify(pluginMappingService).deleteByPluginId("SYSTEM_PLUGIN_CALL_DEVICE");
+ verify(sysParamsDao).updateValueByCode(Constant.SYSTEM_WEB_MENU, newConfig);
+ verify(sysParamsRedis).set(Constant.SYSTEM_WEB_MENU, newConfig);
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/timbre/service/impl/TimbreServiceImplTest.java b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/timbre/service/impl/TimbreServiceImplTest.java
new file mode 100644
index 0000000..985bc88
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/test/java/xiaozhi/modules/timbre/service/impl/TimbreServiceImplTest.java
@@ -0,0 +1,131 @@
+package xiaozhi.modules.timbre.service.impl;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import xiaozhi.common.redis.RedisUtils;
+import xiaozhi.modules.timbre.dao.TimbreDao;
+import xiaozhi.modules.timbre.dto.TimbreDataDTO;
+import xiaozhi.modules.timbre.entity.TimbreEntity;
+import xiaozhi.modules.timbre.vo.TimbreDetailsVO;
+import xiaozhi.modules.voiceclone.dao.VoiceCloneDao;
+import xiaozhi.modules.voiceclone.entity.VoiceCloneEntity;
+
+class TimbreServiceImplTest {
+
+ @Test
+ void defaultLanguageUsesFirstValidRegularTimbreLanguageWithoutCloneQuery() {
+ TimbreDao timbreDao = mock(TimbreDao.class);
+ VoiceCloneDao voiceCloneDao = mock(VoiceCloneDao.class);
+ TimbreServiceImpl service = new TimbreServiceImpl(timbreDao, voiceCloneDao, mock(RedisUtils.class));
+ TimbreEntity timbre = new TimbreEntity();
+ timbre.setLanguages("锛岋紝 ; 鏅�氳瘽锛涚菠璇�");
+ when(timbreDao.selectById("voice-id")).thenReturn(timbre);
+
+ assertEquals("鏅�氳瘽", service.getDefaultLanguageById("voice-id"));
+
+ verify(voiceCloneDao, never()).selectById("voice-id");
+ }
+
+ @Test
+ void defaultLanguageFallsBackToCloneTimbre() {
+ TimbreDao timbreDao = mock(TimbreDao.class);
+ VoiceCloneDao voiceCloneDao = mock(VoiceCloneDao.class);
+ TimbreServiceImpl service = new TimbreServiceImpl(timbreDao, voiceCloneDao, mock(RedisUtils.class));
+ VoiceCloneEntity voiceClone = new VoiceCloneEntity();
+ voiceClone.setLanguages("銆�, English锛屼腑鏂�");
+ when(voiceCloneDao.selectById("clone-id")).thenReturn(voiceClone);
+
+ assertEquals("English", service.getDefaultLanguageById("clone-id"));
+ }
+
+ @Test
+ void delimiterOnlyLanguageConfigurationReturnsNull() {
+ TimbreDao timbreDao = mock(TimbreDao.class);
+ VoiceCloneDao voiceCloneDao = mock(VoiceCloneDao.class);
+ TimbreServiceImpl service = new TimbreServiceImpl(timbreDao, voiceCloneDao, mock(RedisUtils.class));
+ TimbreEntity timbre = new TimbreEntity();
+ timbre.setLanguages(",锛屻�侊紱;;,,");
+ when(timbreDao.selectById("voice-id")).thenReturn(timbre);
+
+ assertNull(service.getDefaultLanguageById("voice-id"));
+ }
+
+ @Test
+ void updateLeavesSortOutOfTheUpdateWhenRequestOmitsIt() {
+ TimbreDao timbreDao = mock(TimbreDao.class);
+ RedisUtils redisUtils = mock(RedisUtils.class);
+ TimbreServiceImpl service = new TimbreServiceImpl(timbreDao, mock(VoiceCloneDao.class), redisUtils);
+ ReflectionTestUtils.setField(service, "baseDao", timbreDao);
+
+ TimbreDataDTO dto = validTimbreData();
+ service.update("voice-id", dto);
+
+ verify(timbreDao, never()).selectById("voice-id");
+ verify(timbreDao).updateById(argThat((TimbreEntity entity) ->
+ "voice-id".equals(entity.getId()) && entity.getSort() == null));
+ verify(redisUtils).delete("timbre:details:voice-id");
+ }
+
+ @Test
+ void updateUsesExplicitSortWithoutLoadingExistingTimbre() {
+ TimbreDao timbreDao = mock(TimbreDao.class);
+ TimbreServiceImpl service = new TimbreServiceImpl(
+ timbreDao, mock(VoiceCloneDao.class), mock(RedisUtils.class));
+ ReflectionTestUtils.setField(service, "baseDao", timbreDao);
+ TimbreDataDTO dto = validTimbreData();
+ dto.setSort(0L);
+
+ service.update("voice-id", dto);
+
+ verify(timbreDao, never()).selectById("voice-id");
+ verify(timbreDao).updateById(argThat((TimbreEntity entity) -> entity.getSort() == 0L));
+ }
+
+ @Test
+ void saveDefaultsOmittedSortToZero() {
+ TimbreDao timbreDao = mock(TimbreDao.class);
+ TimbreServiceImpl service = new TimbreServiceImpl(
+ timbreDao, mock(VoiceCloneDao.class), mock(RedisUtils.class));
+ ReflectionTestUtils.setField(service, "baseDao", timbreDao);
+
+ service.save(validTimbreData());
+
+ verify(timbreDao).insert(argThat((TimbreEntity entity) ->
+ "娴嬭瘯闊宠壊".equals(entity.getName()) && entity.getSort() == 0L));
+ }
+
+ @Test
+ void getSupportsLegacyRowsWithNullSort() {
+ TimbreDao timbreDao = mock(TimbreDao.class);
+ RedisUtils redisUtils = mock(RedisUtils.class);
+ TimbreServiceImpl service = new TimbreServiceImpl(
+ timbreDao, mock(VoiceCloneDao.class), redisUtils);
+ ReflectionTestUtils.setField(service, "baseDao", timbreDao);
+ TimbreEntity entity = new TimbreEntity();
+ entity.setId("voice-id");
+ entity.setSort(null);
+ when(timbreDao.selectById("voice-id")).thenReturn(entity);
+
+ TimbreDetailsVO details = service.get("voice-id");
+
+ assertNull(details.getSort());
+ }
+
+ private TimbreDataDTO validTimbreData() {
+ TimbreDataDTO dto = new TimbreDataDTO();
+ dto.setLanguages("涓枃");
+ dto.setName("娴嬭瘯闊宠壊");
+ dto.setTtsModelId("TTS_Test");
+ dto.setTtsVoice("test-voice");
+ return dto;
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-api/src/test/resources/application.yml b/xiaozhi-esp32-server/main/manager-api/src/test/resources/application.yml
new file mode 100644
index 0000000..c33ed12
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-api/src/test/resources/application.yml
@@ -0,0 +1,25 @@
+spring:
+ messages:
+ encoding: UTF-8
+ basename: i18n/messages
+ data:
+ redis:
+ host: localhost
+ port: 6379
+ database: 0
+ timeout: 10s
+ lettuce:
+ pool:
+ min-idle: 0
+ max-idle: 8
+ max-active: 8
+ max-wait: -1ms
+
+renren:
+ redis:
+ open: true
+
+logging:
+ level:
+ xiaozhi.modules.device: DEBUG
+ org.springframework.data.redis: DEBUG
diff --git a/xiaozhi-esp32-server/main/manager-mobile/.commitlintrc.cjs b/xiaozhi-esp32-server/main/manager-mobile/.commitlintrc.cjs
new file mode 100644
index 0000000..98ee7df
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/.commitlintrc.cjs
@@ -0,0 +1,3 @@
+module.exports = {
+ extends: ['@commitlint/config-conventional'],
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/.editorconfig b/xiaozhi-esp32-server/main/manager-mobile/.editorconfig
new file mode 100644
index 0000000..7f09864
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/.editorconfig
@@ -0,0 +1,13 @@
+root = true
+
+[*] # 琛ㄧず鎵�鏈夋枃浠堕�傜敤
+charset = utf-8 # 璁剧疆鏂囦欢瀛楃闆嗕负 utf-8
+indent_style = space # 缂╄繘椋庢牸锛坱ab | space锛�
+indent_size = 2 # 缂╄繘澶у皬
+end_of_line = lf # 鎺у埗鎹㈣绫诲瀷(lf | cr | crlf)
+trim_trailing_whitespace = true # 鍘婚櫎琛岄鐨勪换鎰忕┖鐧藉瓧绗�
+insert_final_newline = true # 濮嬬粓鍦ㄦ枃浠舵湯灏炬彃鍏ヤ竴涓柊琛�
+
+[*.md] # 琛ㄧず浠� md 鏂囦欢閫傜敤浠ヤ笅瑙勫垯
+max_line_length = off # 鍏抽棴鏈�澶ц闀垮害闄愬埗
+trim_trailing_whitespace = false # 鍏抽棴鏈熬绌烘牸淇壀
diff --git a/xiaozhi-esp32-server/main/manager-mobile/.gitignore b/xiaozhi-esp32-server/main/manager-mobile/.gitignore
new file mode 100644
index 0000000..34ddbdf
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/.gitignore
@@ -0,0 +1,44 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+.DS_Store
+dist
+*.local
+
+# Editor directories and files
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+.hbuilderx
+
+.stylelintcache
+.eslintcache
+
+docs/.vitepress/dist
+docs/.vitepress/cache
+
+src/types
+
+# lock 鏂囦欢杩樻槸涓嶈浜嗭紝鎴戜富瑕佺殑鐗堟湰鍐欐灏卞ソ浜�
+# pnpm-lock.yaml
+# package-lock.json
+
+# TIPS锛氬鏋滄煇浜涙枃浠跺凡缁忓姞鍏ヤ簡鐗堟湰绠$悊锛岀幇鍦ㄩ噸鏂板姞鍏� .gitignore 鏄笉鐢熸晥鐨勶紝闇�瑕佹墽琛屼笅闈㈢殑鎿嶄綔
+# `git rm -r --cached .` 鐒跺悗鎻愪氦 commit 鍗冲彲銆�
+
+# git rm -r --cached file1 file2 ## 閽堝鏌愪簺鏂囦欢
+# git rm -r --cached dir1 dir2 ## 閽堝鏌愪簺鏂囦欢澶�
+# git rm -r --cached . ## 閽堝鎵�鏈夋枃浠�
+
+# 鏇存柊 uni-app 瀹樻柟鐗堟湰
+# npx @dcloudio/uvm@latest
diff --git a/xiaozhi-esp32-server/main/manager-mobile/.npmrc b/xiaozhi-esp32-server/main/manager-mobile/.npmrc
new file mode 100644
index 0000000..10ecfe2
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/.npmrc
@@ -0,0 +1,8 @@
+# registry = https://registry.npmjs.org
+registry = https://registry.npmmirror.com
+
+strict-peer-dependencies=false
+auto-install-peers=true
+shamefully-hoist=true
+ignore-workspace-root-check=true
+install-workspace-root=true
diff --git a/xiaozhi-esp32-server/main/manager-mobile/LICENSE b/xiaozhi-esp32-server/main/manager-mobile/LICENSE
new file mode 100644
index 0000000..8738d3b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2025 Junsen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/xiaozhi-esp32-server/main/manager-mobile/README.md b/xiaozhi-esp32-server/main/manager-mobile/README.md
new file mode 100644
index 0000000..3a4f33a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/README.md
@@ -0,0 +1,170 @@
+## 鏅烘帶鍙扮Щ鍔ㄧ増锛坢anager-mobile锛�
+鍩轰簬 uni-app v3 + Vue 3 + Vite 鐨勮法绔Щ鍔ㄧ鐞嗙锛屾敮鎸� App锛圓ndroid & iOS锛夊拰寰俊灏忕▼搴忋��
+
+### 骞冲彴鍏煎鎬�
+
+| H5 | iOS | Android | 寰俊灏忕▼搴� |
+| -- | --- | ------- | ---------- |
+| 鈭� | 鈭� | 鈭� | 鈭� |
+
+鎻愮ず锛氫笉鍚� UI 缁勪欢鍦ㄤ笉鍚屽钩鍙扮殑閫傞厤搴︾暐鏈夊樊寮傦紝璇蜂互瀵瑰簲缁勪欢搴撴枃妗d负鍑嗐��
+
+### 寮�鍙戠幆澧冭姹�
+- Node >= 18
+- pnpm >= 7.30锛堝缓璁娇鐢ㄩ」鐩腑澹版槑鐨� `pnpm@10.x`锛�
+- 鍙�夛細HBuilderX锛圓pp 璋冭瘯/鎵撳寘锛夈�佸井淇″紑鍙戣�呭伐鍏凤紙寰俊灏忕▼搴忥級
+
+### 蹇�熷紑濮�
+1) 閰嶇疆鐜鍙橀噺
+ - 澶嶅埗 `env/.env.example` 涓� `env/.env.development`
+ - 鏍规嵁瀹為檯鎯呭喌淇敼閰嶇疆椤癸紙鐗瑰埆鏄� `VITE_SERVER_BASEURL`銆乣VITE_UNI_APPID`銆乣VITE_WX_APPID`锛�
+
+2) 瀹夎渚濊禆
+
+```bash
+pnpm i
+```
+
+3) 鏈湴寮�鍙戯紙鐑洿鏂帮級
+- h5: `pnpm dev:h5`锛岀劧鍚庤瀵熷惎鍔ㄦ棩蹇楁樉绀虹殑ip绔彛鍙�
+- 寰俊灏忕▼搴忥細`pnpm dev:mp` 鎴� `pnpm dev:mp-weixin`锛岀劧鍚庣敤寰俊寮�鍙戣�呭伐鍏峰鍏� `dist/dev/mp-weixin`
+- App锛氱敤 HBuilderX 瀵煎叆 `manager-mobile`锛岀劧鍚庡弬鑰冧笅闈㈢殑鏁欑▼灏辫兘杩愯浜�
+
+### 鐜鍙橀噺涓庨厤缃�
+椤圭洰浣跨敤鑷畾涔� `env` 鐩綍瀛樻斁鐜鏂囦欢锛屾寜 Vite 瑙勮寖鍛藉悕锛歚.env.development`銆乣.env.production` 绛夈��
+
+鍏抽敭鍙橀噺锛堥儴鍒嗭級锛�
+- VITE_APP_TITLE锛氬簲鐢ㄥ悕绉帮紙鍐欏叆 `manifest.config.ts`锛�
+- VITE_UNI_APPID锛歶ni-app 搴旂敤 appid锛圓pp锛�
+- VITE_WX_APPID锛氬井淇″皬绋嬪簭 appid锛坢p-weixin锛�
+- VITE_FALLBACK_LOCALE锛氶粯璁よ瑷�锛屽 `zh-Hans`
+- VITE_SERVER_BASEURL锛氭湇鍔$鍩虹鍦板潃锛圚TTP 璇锋眰 baseURL锛�
+- VITE_DELETE_CONSOLE锛氭瀯寤烘椂鏄惁绉婚櫎 console锛坄true`/`false`锛�
+- VITE_SHOW_SOURCEMAP锛氭槸鍚︾敓鎴� sourcemap锛堥粯璁ゅ叧闂級
+- VITE_LOGIN_URL锛氭湭鐧诲綍璺宠浆鐨勭櫥褰曢〉璺緞锛堣矾鐢辨嫤鎴櫒浣跨敤锛�
+
+绀轰緥锛坄env/.env.development`锛夛細
+```env
+VITE_APP_TITLE=灏忔櫤
+VITE_FALLBACK_LOCALE=zh-Hans
+VITE_UNI_APPID=
+VITE_WX_APPID=
+
+VITE_SERVER_BASEURL=http://localhost:8080
+
+VITE_DELETE_CONSOLE=false
+VITE_SHOW_SOURCEMAP=false
+VITE_LOGIN_URL=/pages/login/index
+```
+
+璇存槑锛�
+- `manifest.config.ts` 浼氫粠 `env` 璇诲彇鏍囬銆乤ppid銆佽瑷�绛夐厤缃��
+
+### 閲嶈娉ㄦ剰浜嬮」
+鈿狅笍 **閮ㄧ讲鍓嶅繀椤讳慨鏀圭殑閰嶇疆椤癸細**
+
+1. **搴旂敤 ID 閰嶇疆**
+ - `VITE_UNI_APPID`锛氶渶瑕佸湪 [DCloud 寮�鍙戣�呬腑蹇僝(https://dev.dcloud.net.cn/) 鍒涘缓搴旂敤骞惰幏鍙� AppID
+ - `VITE_WX_APPID`锛氶渶瑕佸湪 [寰俊鍏紬骞冲彴](https://mp.weixin.qq.com/) 娉ㄥ唽灏忕▼搴忓苟鑾峰彇 AppID
+
+2. **鏈嶅姟绔湴鍧�**
+ - `VITE_SERVER_BASEURL`锛氫慨鏀逛负鎮ㄧ殑瀹為檯鏈嶅姟绔湴鍧�
+
+3. **搴旂敤淇℃伅**
+ - `VITE_APP_TITLE`锛氫慨鏀逛负鎮ㄧ殑搴旂敤鍚嶇О
+ - 鏇存柊 `src/static/logo.png` 绛夊浘鏍囪祫婧�
+
+4. **鍏朵粬閰嶇疆**
+ - 妫�鏌� `manifest.config.ts` 涓殑搴旂敤閰嶇疆淇℃伅
+ - 鏍规嵁闇�瑕佷慨鏀� `src/layouts/fg-tabbar/tabbarList.ts` 涓殑 tabbar 閰嶇疆
+
+### 璇︾粏鎿嶄綔鎸囧崡
+
+#### 1. 鑾峰彇 uni-app AppID
+
+- 澶嶅埗鐢熸垚鐨� AppID 鍒扮幆澧冨彉閲� `VITE_UNI_APPID` 涓�
+
+#### 2. 鏈湴杩愯姝ラ
+
+
+**App 鏈湴璋冭瘯锛�**
+1. 鐢� HBuilderX 瀵煎叆 `manager-mobile` 鐩綍
+2. 閲嶆柊璇嗗埆椤圭洰
+3. 杩炴帴鎵嬫満鎴栦娇鐢ㄦā鎷熷櫒杩涜鐪熸満璋冭瘯
+
+**椤圭洰璇嗗埆闂瑙e喅锛�**
+
+
+濡傛灉 HBuilderX 鏃犳硶姝g‘璇嗗埆椤圭洰绫诲瀷锛�
+- 鍦ㄩ」鐩牴鐩綍鍙抽敭閫夋嫨"閲嶆柊璇嗗埆椤圭洰绫诲瀷"
+- 纭繚椤圭洰琚瘑鍒负 "uni-app" 椤圭洰
+
+### 璺敱涓庨壌鏉�
+- 鍦� `src/main.ts` 涓敞鍐屼簡璺敱鎷︽埅鎻掍欢 `routeInterceptor`銆�
+- 榛戝悕鍗曟嫤鎴細浠呭閰嶇疆涓洪渶瑕佺櫥褰曠殑椤甸潰杩涜鏍¢獙锛堟潵婧� `@/utils` 鐨� `getNeedLoginPages`锛夈��
+- 鐧诲綍鍒ゆ柇锛氬熀浜庣敤鎴蜂俊鎭紙`pinia` 鐨� `useUserStore`锛夛紝鏈櫥褰曞皢璺宠浆鍒� `VITE_LOGIN_URL`锛屽苟闄勫甫閲嶅畾鍚戝洖鍘熼〉闈㈢殑鍙傛暟銆�
+
+### 缃戠粶璇锋眰
+- 鍩轰簬 `alova` + `@alova/adapter-uniapp`锛岀粺涓�鍦� `src/http/request/alova.ts` 鍒涘缓瀹炰緥銆�
+- `baseURL` 璇诲彇鐜閰嶇疆锛坄getEnvBaseUrl`锛夛紝鍙�氳繃 `method.config.meta.domain` 鍔ㄦ�佸垏鎹㈠煙鍚嶃��
+- 璁よ瘉锛氶粯璁や粠鏈湴 `token`锛坄uni.getStorageSync('token')`锛夋敞鍏� `Authorization` 澶达紝缂哄け鍒欓噸瀹氬悜鐧诲綍銆�
+- 鍝嶅簲锛氱粺涓�澶勭悊 `statusCode !== 200` 鐨� HTTP 閿欒涓庝笟鍔� `code !== 0` 鐨勯敊璇紱`401` 浼氭竻闄� token 骞惰烦杞櫥褰曘��
+
+### 鏋勫缓涓庡彂甯�
+
+**寰俊灏忕▼搴忥細**
+1. 纭繚宸查厤缃纭殑 `VITE_WX_APPID`
+2. 杩愯 `pnpm build:mp`锛屼骇鐗╁湪 `dist/build/mp-weixin`
+3. 鐢ㄥ井淇″紑鍙戣�呭伐鍏峰鍏ラ」鐩洰褰曪紝骞朵笂浼犱唬鐮�
+4. 鍦ㄥ井淇″叕浼楀钩鍙版彁浜ゅ鏍�
+
+**Android & iOS App锛�**
+
+#### 3. App 鎵撳寘鍙戣姝ラ
+
+**姝ラ涓�锛氬噯澶囨墦鍖�**
+
+
+1. 纭繚宸查厤缃纭殑 `VITE_UNI_APPID`
+2. 杩愯 `pnpm build:app`锛屼骇鐗╁湪 `dist/build/app`
+3. 鐢� HBuilderX 瀵煎叆椤圭洰鐩綍
+4. 鍦� HBuilderX 涓偣鍑�"鍙戣" 鈫� "鍘熺敓App-浜戞墦鍖�"
+
+**姝ラ浜岋細閰嶇疆鎵撳寘鍙傛暟**
+
+
+1. **搴旂敤鍥炬爣鍜屽惎鍔ㄥ浘**锛氫笂浼犲簲鐢ㄥ浘鏍囧拰鍚姩椤甸潰鍥剧墖
+2. **搴旂敤鐗堟湰鍙�**锛氳缃増鏈彿鍜岀増鏈悕绉�
+3. **绛惧悕璇佷功**锛�
+ - Android锛氫笂浼� keystore 璇佷功鏂囦欢
+ - iOS锛氶厤缃紑鍙戣�呰瘉涔﹀拰鎻忚堪鏂囦欢
+4. **鍖呭悕閰嶇疆**锛氳缃簲鐢ㄧ殑鍖呭悕锛圔undle ID锛�
+5. **鎵撳寘绫诲瀷**锛氶�夋嫨娴嬭瘯鍖呮垨姝e紡鍖�
+6. 鐐瑰嚮"鎵撳寘"寮�濮嬩簯鎵撳寘娴佺▼
+
+**鍙戝竷鍒板簲鐢ㄥ晢搴楋細**
+- **Android**锛氬皢鐢熸垚鐨� APK 鏂囦欢涓婁紶鍒板悇澶� Android 搴旂敤甯傚満
+- **iOS**锛氬皢鐢熸垚鐨� IPA 鏂囦欢閫氳繃 App Store Connect 涓婁紶鍒� App Store锛堥渶瑕� Apple 寮�鍙戣�呰处鍙凤級
+
+### 绾﹀畾涓庡伐绋嬪寲
+- 椤甸潰涓庡垎鍖咃細鐢� `@uni-helper/vite-plugin-uni-pages` 涓� `pages.config.ts` 缁熶竴鐢熸垚锛泃abbar 閰嶇疆鍦� `src/layouts/fg-tabbar/tabbarList.ts`銆�
+- 缁勪欢涓� hooks 鑷姩瀵煎叆锛氳 `vite.config.ts` 涓� `unplugin-auto-import` 涓� `@uni-helper/vite-plugin-uni-components`銆�
+- 鏍峰紡锛氫娇鐢� UnoCSS 涓� `src/style/index.scss`銆�
+- 鐘舵�佺鐞嗭細`pinia` + `pinia-plugin-persistedstate`銆�
+- 浠g爜瑙勮寖锛氬唴缃� `eslint`銆乣husky`銆乣lint-staged`锛屾彁浜ゅ墠鑷姩鏍煎紡鍖栵紙`lint-staged`锛夈��
+
+### 甯哥敤鑴氭湰
+```bash
+# 寮�鍙�
+pnpm dev:mp # 绛変环 dev:mp-weixin
+
+# 鏋勫缓
+pnpm build:mp # 绛変环 build:mp-weixin
+
+# 鍏朵粬
+pnpm type-check
+pnpm lint && pnpm lint:fix
+```
+
+### License
+MIT
diff --git a/xiaozhi-esp32-server/main/manager-mobile/env/.env b/xiaozhi-esp32-server/main/manager-mobile/env/.env
new file mode 100644
index 0000000..e0f4eea
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/env/.env
@@ -0,0 +1,19 @@
+VITE_APP_TITLE = '灏忔櫤'
+VITE_APP_PORT = 9000
+
+VITE_UNI_APPID = '__UNI__36A515E'
+VITE_WX_APPID = 'wxa2abb91f64032a2b'
+
+# h5閮ㄧ讲缃戠珯鐨刡ase锛岄厤缃埌 manifest.config.ts 閲岀殑 h5.router.base
+VITE_APP_PUBLIC_BASE=/
+
+# 鐧诲綍椤甸潰
+VITE_LOGIN_URL = '/pages/login/index'
+# 绗竴涓姹傚湴鍧�
+VITE_SERVER_BASEURL = 'https://2662r3426b.vicp.fun/xiaozhi'
+
+VITE_UPLOAD_BASEURL = '/otaMag/upload'
+
+# h5鏄惁闇�瑕侀厤缃唬鐞�
+VITE_APP_PROXY=true
+VITE_APP_PROXY_PREFIX = '/xiaozhi'
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-mobile/env/.env.development b/xiaozhi-esp32-server/main/manager-mobile/env/.env.development
new file mode 100644
index 0000000..04fa273
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/env/.env.development
@@ -0,0 +1,6 @@
+# 鍙橀噺蹇呴』浠� VITE_ 涓哄墠缂�鎵嶈兘鏆撮湶缁欏閮ㄨ鍙�
+NODE_ENV = 'development'
+# 鏄惁鍘婚櫎console 鍜� debugger
+VITE_DELETE_CONSOLE = false
+# 鏄惁寮�鍚痵ourcemap
+VITE_SHOW_SOURCEMAP = true
diff --git a/xiaozhi-esp32-server/main/manager-mobile/env/.env.production b/xiaozhi-esp32-server/main/manager-mobile/env/.env.production
new file mode 100644
index 0000000..8a1b50c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/env/.env.production
@@ -0,0 +1,6 @@
+# 鍙橀噺蹇呴』浠� VITE_ 涓哄墠缂�鎵嶈兘鏆撮湶缁欏閮ㄨ鍙�
+NODE_ENV = 'development'
+# 鏄惁鍘婚櫎console 鍜� debugger
+VITE_DELETE_CONSOLE = true
+# 鏄惁寮�鍚痵ourcemap
+VITE_SHOW_SOURCEMAP = false
diff --git a/xiaozhi-esp32-server/main/manager-mobile/env/.env.test b/xiaozhi-esp32-server/main/manager-mobile/env/.env.test
new file mode 100644
index 0000000..e22f765
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/env/.env.test
@@ -0,0 +1,4 @@
+# 鍙橀噺蹇呴』浠� VITE_ 涓哄墠缂�鎵嶈兘鏆撮湶缁欏閮ㄨ鍙�
+NODE_ENV = 'development'
+# 鏄惁鍘婚櫎console 鍜� debugger
+VITE_DELETE_CONSOLE = false
diff --git a/xiaozhi-esp32-server/main/manager-mobile/eslint.config.mjs b/xiaozhi-esp32-server/main/manager-mobile/eslint.config.mjs
new file mode 100644
index 0000000..54ce246
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/eslint.config.mjs
@@ -0,0 +1,43 @@
+import uniHelper from '@uni-helper/eslint-config'
+
+export default uniHelper({
+ unocss: true,
+ vue: true,
+ markdown: false,
+ ignores: [
+ 'src/uni_modules/',
+ 'dist',
+ // unplugin-auto-import 鐢熸垚鐨勭被鍨嬫枃浠讹紝姣忔鎻愪氦閮芥敼鍙橈紝鎵�浠ュ姞鍏ヨ繖閲屽惂锛屼笌 .gitignore 閰嶅悎浣跨敤
+ 'auto-import.d.ts',
+ // vite-plugin-uni-pages 鐢熸垚鐨勭被鍨嬫枃浠讹紝姣忔鍒囨崲鍒嗘敮閮戒竴鍫嗕笉鍚岀殑锛屾墍浠ョ洿鎺� .gitignore
+ 'uni-pages.d.ts',
+ // 鎻掍欢鐢熸垚鐨勬枃浠�
+ 'src/pages.json',
+ 'src/manifest.json',
+ // 蹇界暐鑷姩鐢熸垚鏂囦欢
+ 'src/service/app/**',
+ ],
+ rules: {
+ 'no-console': 'off',
+ 'no-unused-vars': 'off',
+ 'vue/no-unused-refs': 'off',
+ 'unused-imports/no-unused-vars': 'off',
+ 'eslint-comments/no-unlimited-disable': 'off',
+ 'jsdoc/check-param-names': 'off',
+ 'jsdoc/require-returns-description': 'off',
+ 'ts/no-empty-object-type': 'off',
+ 'no-extend-native': 'off',
+ },
+ formatters: {
+ /**
+ * Format CSS, LESS, SCSS files, also the `<style>` blocks in Vue
+ * By default uses Prettier
+ */
+ css: true,
+ /**
+ * Format HTML files
+ * By default uses Prettier
+ */
+ html: true,
+ },
+})
diff --git a/xiaozhi-esp32-server/main/manager-mobile/favicon.ico b/xiaozhi-esp32-server/main/manager-mobile/favicon.ico
new file mode 100644
index 0000000..d75b65c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/favicon.ico
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/index.html b/xiaozhi-esp32-server/main/manager-mobile/index.html
new file mode 100644
index 0000000..2e368d0
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/index.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<html build-time="%BUILD_TIME%">
+ <head>
+ <meta charset="UTF-8" />
+ <link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
+ <script>
+ var coverSupport =
+ 'CSS' in window &&
+ typeof CSS.supports === 'function' &&
+ (CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
+ document.write(
+ '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
+ (coverSupport ? ', viewport-fit=cover' : '') +
+ '" />',
+ )
+ </script>
+ <title>unibest</title>
+ <!--preload-links-->
+ <!--app-context-->
+ </head>
+
+ <body>
+ <div id="app"><!--app-html--></div>
+ <script type="module" src="/src/main.ts"></script>
+ </body>
+</html>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/manifest.config.ts b/xiaozhi-esp32-server/main/manager-mobile/manifest.config.ts
new file mode 100644
index 0000000..d0429b8
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/manifest.config.ts
@@ -0,0 +1,155 @@
+import path from 'node:path'
+import process from 'node:process'
+// manifest.config.ts
+import { defineManifestConfig } from '@uni-helper/vite-plugin-uni-manifest'
+import { loadEnv } from 'vite'
+
+// 鎵嬪姩瑙f瀽鍛戒护琛屽弬鏁拌幏鍙� mode
+function getMode() {
+ const args = process.argv.slice(2)
+ const modeFlagIndex = args.findIndex(arg => arg === '--mode')
+ return modeFlagIndex !== -1 ? args[modeFlagIndex + 1] : args[0] === 'build' ? 'production' : 'development' // 榛樿 development
+}
+// 鑾峰彇鐜鍙橀噺鐨勮寖渚�
+const env = loadEnv(getMode(), path.resolve(process.cwd(), 'env'))
+const {
+ VITE_APP_TITLE,
+ VITE_UNI_APPID,
+ VITE_WX_APPID,
+ VITE_APP_PUBLIC_BASE,
+ VITE_FALLBACK_LOCALE,
+} = env
+
+export default defineManifestConfig({
+ 'name': VITE_APP_TITLE,
+ 'appid': VITE_UNI_APPID,
+ 'description': '',
+ 'versionName': '1.0.0',
+ 'versionCode': '100',
+ 'transformPx': false,
+ 'locale': VITE_FALLBACK_LOCALE, // 'zh-Hans'
+ 'h5': {
+ router: {
+ // base: VITE_APP_PUBLIC_BASE,
+ },
+ },
+ /* 5+App鐗规湁鐩稿叧 */
+ 'app-plus': {
+ usingComponents: true,
+ nvueStyleCompiler: 'uni-app',
+ compilerVersion: 3,
+ compatible: {
+ ignoreVersion: true,
+ },
+ splashscreen: {
+ alwaysShowBeforeRender: true,
+ waiting: true,
+ autoclose: true,
+ delay: 0,
+ },
+ /* 妯″潡閰嶇疆 */
+ modules: {},
+ /* 搴旂敤鍙戝竷淇℃伅 */
+ distribute: {
+ /* android鎵撳寘閰嶇疆 */
+ android: {
+ minSdkVersion: 30,
+ targetSdkVersion: 30,
+ abiFilters: ['armeabi-v7a', 'arm64-v8a'],
+ permissions: [
+ '<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>',
+ '<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>',
+ '<uses-permission android:name="android.permission.VIBRATE"/>',
+ '<uses-permission android:name="android.permission.READ_LOGS"/>',
+ '<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>',
+ '<uses-feature android:name="android.hardware.camera.autofocus"/>',
+ '<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>',
+ '<uses-permission android:name="android.permission.CAMERA"/>',
+ '<uses-permission android:name="android.permission.GET_ACCOUNTS"/>',
+ '<uses-permission android:name="android.permission.READ_PHONE_STATE"/>',
+ '<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>',
+ '<uses-permission android:name="android.permission.WAKE_LOCK"/>',
+ '<uses-permission android:name="android.permission.FLASHLIGHT"/>',
+ '<uses-feature android:name="android.hardware.camera"/>',
+ '<uses-permission android:name="android.permission.WRITE_SETTINGS"/>',
+ '<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>',
+ '<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>',
+ ],
+ },
+ /* ios鎵撳寘閰嶇疆 */
+ ios: {},
+ /* SDK閰嶇疆 */
+ sdkConfigs: {},
+ /* 鍥炬爣閰嶇疆 */
+ icons: {
+ android: {
+ hdpi: 'unpackage/res/icons/72x72.png',
+ xhdpi: 'unpackage/res/icons/96x96.png',
+ xxhdpi: 'unpackage/res/icons/144x144.png',
+ xxxhdpi: 'unpackage/res/icons/192x192.png',
+ },
+ ios: {
+ appstore: 'unpackage/res/icons/1024x1024.png',
+ ipad: {
+ 'app': 'unpackage/res/icons/76x76.png',
+ 'app@2x': 'unpackage/res/icons/152x152.png',
+ 'notification': 'unpackage/res/icons/20x20.png',
+ 'notification@2x': 'unpackage/res/icons/40x40.png',
+ 'proapp@2x': 'unpackage/res/icons/167x167.png',
+ 'settings': 'unpackage/res/icons/29x29.png',
+ 'settings@2x': 'unpackage/res/icons/58x58.png',
+ 'spotlight': 'unpackage/res/icons/40x40.png',
+ 'spotlight@2x': 'unpackage/res/icons/80x80.png',
+ },
+ iphone: {
+ 'app@2x': 'unpackage/res/icons/120x120.png',
+ 'app@3x': 'unpackage/res/icons/180x180.png',
+ 'notification@2x': 'unpackage/res/icons/40x40.png',
+ 'notification@3x': 'unpackage/res/icons/60x60.png',
+ 'settings@2x': 'unpackage/res/icons/58x58.png',
+ 'settings@3x': 'unpackage/res/icons/87x87.png',
+ 'spotlight@2x': 'unpackage/res/icons/80x80.png',
+ 'spotlight@3x': 'unpackage/res/icons/120x120.png',
+ },
+ },
+ },
+ },
+ },
+ /* 蹇簲鐢ㄧ壒鏈夌浉鍏� */
+ 'quickapp': {},
+ /* 灏忕▼搴忕壒鏈夌浉鍏� */
+ 'mp-weixin': {
+ appid: VITE_WX_APPID,
+ setting: {
+ urlCheck: false,
+ // 鏄惁鍚敤 ES6 杞� ES5
+ es6: true,
+ minified: true,
+ },
+ optimization: {
+ subPackages: true,
+ },
+ usingComponents: true,
+ // __usePrivacyCheck__: true,
+ permission: {
+ 'scope.userLocation': {
+ desc: 'WiFi閰嶇綉鍔熻兘闇�瑕佽幏鍙栦綅缃潈闄�',
+ },
+ },
+ requiredPrivateInfos: ['getLocation'],
+ },
+ 'mp-alipay': {
+ usingComponents: true,
+ styleIsolation: 'shared',
+ },
+ 'mp-baidu': {
+ usingComponents: true,
+ },
+ 'mp-toutiao': {
+ usingComponents: true,
+ },
+ 'uniStatistics': {
+ enable: false,
+ },
+ 'vueVersion': '3',
+})
diff --git a/xiaozhi-esp32-server/main/manager-mobile/package.json b/xiaozhi-esp32-server/main/manager-mobile/package.json
new file mode 100644
index 0000000..7c85f0a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/package.json
@@ -0,0 +1,163 @@
+{
+ "name": "xiaozhi-mobile-admin",
+ "type": "commonjs",
+ "version": "3.4.0",
+ "unibest-version": "3.4.0",
+ "packageManager": "pnpm@10.10.0",
+ "description": "xiaozhi-esp32-server鐨勭Щ鍔ㄧ绠$悊鍚庡彴",
+ "generate-time": "2025-08-04",
+ "author": {
+ "name": "huangjunsen",
+ "zhName": "榛勪繆妫�",
+ "email": "huangjunsen@xiaozhi.com",
+ "github": "https://github.com/huangjunsen0406",
+ "gitee": "https://gitee.com/huang-jun-sen"
+ },
+ "license": "MIT",
+ "homepage": "https://github.com/xinnan-tech/xiaozhi-esp32-server",
+ "repository": "https://github.com/xinnan-tech/xiaozhi-esp32-server",
+ "bugs": {
+ "url": "https://github.com/xinnan-tech/xiaozhi-esp32-server/issues"
+ },
+ "engines": {
+ "node": ">=18",
+ "pnpm": ">=7.30"
+ },
+ "scripts": {
+ "preinstall": "npx only-allow pnpm",
+ "uvm": "npx @dcloudio/uvm@latest",
+ "uvm-rm": "node ./scripts/postupgrade.js",
+ "postuvm": "echo upgrade uni-app success!",
+ "dev:app": "uni -p app",
+ "dev:app-android": "uni -p app-android",
+ "dev:app-ios": "uni -p app-ios",
+ "dev:custom": "uni -p",
+ "dev": "uni",
+ "dev:h5": "uni",
+ "dev:h5:ssr": "uni --ssr",
+ "dev:mp": "uni -p mp-weixin",
+ "dev:mp-alipay": "uni -p mp-alipay",
+ "dev:mp-baidu": "uni -p mp-baidu",
+ "dev:mp-jd": "uni -p mp-jd",
+ "dev:mp-kuaishou": "uni -p mp-kuaishou",
+ "dev:mp-lark": "uni -p mp-lark",
+ "dev:mp-qq": "uni -p mp-qq",
+ "dev:mp-toutiao": "uni -p mp-toutiao",
+ "dev:mp-weixin": "uni -p mp-weixin",
+ "dev:mp-xhs": "uni -p mp-xhs",
+ "dev:quickapp-webview": "uni -p quickapp-webview",
+ "dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
+ "dev:quickapp-webview-union": "uni -p quickapp-webview-union",
+ "build:app": "uni build -p app",
+ "build:app-android": "uni build -p app-android",
+ "build:app-ios": "uni build -p app-ios",
+ "build:custom": "uni build -p",
+ "build:h5": "uni build",
+ "build": "uni build",
+ "build:h5:ssr": "uni build --ssr",
+ "build:mp-alipay": "uni build -p mp-alipay",
+ "build:mp": "uni build -p mp-weixin",
+ "build:mp-baidu": "uni build -p mp-baidu",
+ "build:mp-jd": "uni build -p mp-jd",
+ "build:mp-kuaishou": "uni build -p mp-kuaishou",
+ "build:mp-lark": "uni build -p mp-lark",
+ "build:mp-qq": "uni build -p mp-qq",
+ "build:mp-toutiao": "uni build -p mp-toutiao",
+ "build:mp-weixin": "uni build -p mp-weixin",
+ "build:mp-xhs": "uni build -p mp-xhs",
+ "build:quickapp-webview": "uni build -p quickapp-webview",
+ "build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
+ "build:quickapp-webview-union": "uni build -p quickapp-webview-union",
+ "type-check": "vue-tsc --noEmit",
+ "test:snapshot": "node --test src/pages/agent/components/agentSnapshotUtils.test.mjs src/pages/agent/components/agentSnapshotContracts.test.mjs src/pages/agent/components/voicePreviewUtils.test.mjs src/pages/device/deviceTimeUtils.test.mjs",
+ "openapi-ts-request": "openapi-ts",
+ "prepare": "git init && husky",
+ "lint": "eslint",
+ "lint:fix": "eslint --fix"
+ },
+ "dependencies": {
+ "@alova/adapter-uniapp": "^2.0.14",
+ "@alova/shared": "^1.3.1",
+ "@dcloudio/uni-app": "3.0.0-4060620250520001",
+ "@dcloudio/uni-app-harmony": "3.0.0-4060620250520001",
+ "@dcloudio/uni-app-plus": "3.0.0-4060620250520001",
+ "@dcloudio/uni-components": "3.0.0-4060620250520001",
+ "@dcloudio/uni-h5": "3.0.0-4060620250520001",
+ "@dcloudio/uni-mp-alipay": "3.0.0-4060620250520001",
+ "@dcloudio/uni-mp-baidu": "3.0.0-4060620250520001",
+ "@dcloudio/uni-mp-harmony": "3.0.0-4060620250520001",
+ "@dcloudio/uni-mp-jd": "3.0.0-4060620250520001",
+ "@dcloudio/uni-mp-kuaishou": "3.0.0-4060620250520001",
+ "@dcloudio/uni-mp-lark": "3.0.0-4060620250520001",
+ "@dcloudio/uni-mp-qq": "3.0.0-4060620250520001",
+ "@dcloudio/uni-mp-toutiao": "3.0.0-4060620250520001",
+ "@dcloudio/uni-mp-weixin": "3.0.0-4060620250520001",
+ "@dcloudio/uni-mp-xhs": "3.0.0-4060620250520001",
+ "@dcloudio/uni-quickapp-webview": "3.0.0-4060620250520001",
+ "@tanstack/vue-query": "^5.62.16",
+ "abortcontroller-polyfill": "^1.7.8",
+ "alova": "^3.3.3",
+ "dayjs": "1.11.10",
+ "js-cookie": "^3.0.5",
+ "pinia": "2.0.36",
+ "pinia-plugin-persistedstate": "3.2.1",
+ "sm-crypto": "^0.3.13",
+ "vue": "^3.4.21",
+ "wot-design-uni": "^1.9.1",
+ "z-paging": "2.8.7"
+ },
+ "devDependencies": {
+ "@alova/wormhole": "^1.0.8",
+ "@antfu/eslint-config": "^4.15.0",
+ "@commitlint/cli": "^19.8.1",
+ "@commitlint/config-conventional": "^19.8.1",
+ "@dcloudio/types": "^3.4.8",
+ "@dcloudio/uni-automator": "3.0.0-4060620250520001",
+ "@dcloudio/uni-cli-shared": "3.0.0-4060620250520001",
+ "@dcloudio/uni-stacktracey": "3.0.0-4060620250520001",
+ "@dcloudio/uni-uts-v1": "3.0.0-4060620250520001",
+ "@dcloudio/vite-plugin-uni": "3.0.0-4060620250520001",
+ "@esbuild/darwin-arm64": "0.20.2",
+ "@esbuild/darwin-x64": "0.20.2",
+ "@iconify-json/carbon": "^1.2.4",
+ "@rollup/rollup-darwin-x64": "^4.28.0",
+ "@types/node": "^20.17.9",
+ "@types/wechat-miniprogram": "^3.4.8",
+ "@uni-helper/eslint-config": "^0.4.0",
+ "@uni-helper/uni-types": "1.0.0-alpha.3",
+ "@uni-helper/unocss-preset-uni": "^0.2.11",
+ "@uni-helper/vite-plugin-uni-components": "0.2.0",
+ "@uni-helper/vite-plugin-uni-layouts": "0.1.10",
+ "@uni-helper/vite-plugin-uni-manifest": "0.2.8",
+ "@uni-helper/vite-plugin-uni-pages": "0.2.28",
+ "@uni-helper/vite-plugin-uni-platform": "0.0.4",
+ "@uni-ku/bundle-optimizer": "^1.3.3",
+ "@unocss/eslint-plugin": "^66.2.3",
+ "@unocss/preset-legacy-compat": "^0.59.4",
+ "@vue/runtime-core": "^3.4.21",
+ "@vue/tsconfig": "^0.1.3",
+ "autoprefixer": "^10.4.20",
+ "eslint": "^9.29.0",
+ "eslint-plugin-format": "^1.0.1",
+ "husky": "^9.1.7",
+ "lint-staged": "^15.2.10",
+ "openapi-ts-request": "^1.1.2",
+ "postcss": "^8.4.49",
+ "postcss-html": "^1.7.0",
+ "postcss-scss": "^4.0.9",
+ "rollup-plugin-visualizer": "^5.12.0",
+ "sass": "1.77.8",
+ "typescript": "^5.7.2",
+ "unocss": "65.4.2",
+ "unplugin-auto-import": "^0.17.8",
+ "vite": "5.2.8",
+ "vite-plugin-restart": "^0.4.2",
+ "vue-tsc": "^2.2.10"
+ },
+ "resolutions": {
+ "bin-wrapper": "npm:bin-wrapper-china"
+ },
+ "lint-staged": {
+ "*": "eslint --fix"
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/pages.config.ts b/xiaozhi-esp32-server/main/manager-mobile/pages.config.ts
new file mode 100644
index 0000000..4547919
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/pages.config.ts
@@ -0,0 +1,23 @@
+import { defineUniPages } from '@uni-helper/vite-plugin-uni-pages'
+import { tabBar } from './src/layouts/fg-tabbar/tabbarList'
+
+export default defineUniPages({
+ globalStyle: {
+ navigationStyle: 'default',
+ navigationBarTitleText: '灏忔櫤',
+ navigationBarBackgroundColor: '#f8f8f8',
+ navigationBarTextStyle: 'black',
+ backgroundColor: '#FFFFFF',
+ },
+ easycom: {
+ autoscan: true,
+ custom: {
+ '^fg-(.*)': '@/components/fg-$1/fg-$1.vue',
+ '^wd-(.*)': 'wot-design-uni/components/wd-$1/wd-$1.vue',
+ '^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)':
+ 'z-paging/components/z-paging$1/z-paging$1.vue',
+ },
+ },
+ // tabbar 鐨勯厤缃粺涓�鍦� "./src/layouts/fg-tabbar/tabbarList.ts" 鏂囦欢涓�
+ tabBar: tabBar as any,
+})
diff --git a/xiaozhi-esp32-server/main/manager-mobile/patches/@dcloudio__uni-h5.patch b/xiaozhi-esp32-server/main/manager-mobile/patches/@dcloudio__uni-h5.patch
new file mode 100644
index 0000000..ff1e37c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/patches/@dcloudio__uni-h5.patch
@@ -0,0 +1,13 @@
+diff --git a/dist/uni-h5.es.js b/dist/uni-h5.es.js
+index 7421bad97d94ad34a3d4d94292a9ee9071430662..19c6071ee4036ceb8d1cfa09030e471c002d2cda 100644
+--- a/dist/uni-h5.es.js
++++ b/dist/uni-h5.es.js
+@@ -23410,7 +23410,7 @@ function useShowTabBar(emit2) {
+ const tabBar2 = useTabBar();
+ const showTabBar2 = computed(() => route.meta.isTabBar && tabBar2.shown);
+ updateCssVar({
+- "--tab-bar-height": tabBar2.height
++ "--tab-bar-height": tabBar2?.height || 0
+ });
+ return showTabBar2;
+ }
diff --git a/xiaozhi-esp32-server/main/manager-mobile/pnpm-lock.yaml b/xiaozhi-esp32-server/main/manager-mobile/pnpm-lock.yaml
new file mode 100644
index 0000000..3aa1fc4
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/pnpm-lock.yaml
@@ -0,0 +1,14881 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+overrides:
+ bin-wrapper: npm:bin-wrapper-china
+
+patchedDependencies:
+ '@dcloudio/uni-h5':
+ hash: 5763725268e9a493075be3c82d91b590b9ae30e997a04a418494dd7d6d327b9d
+ path: patches/@dcloudio__uni-h5.patch
+
+importers:
+
+ .:
+ dependencies:
+ '@alova/adapter-uniapp':
+ specifier: ^2.0.14
+ version: 2.0.14(alova@3.3.3)
+ '@alova/shared':
+ specifier: ^1.3.1
+ version: 1.3.1
+ '@dcloudio/uni-app':
+ specifier: 3.0.0-4060620250520001
+ version: 3.0.0-4060620250520001(@dcloudio/types@3.4.14)(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-app-harmony':
+ specifier: 3.0.0-4060620250520001
+ version: 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-app-plus':
+ specifier: 3.0.0-4060620250520001
+ version: 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-components':
+ specifier: 3.0.0-4060620250520001
+ version: 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-h5':
+ specifier: 3.0.0-4060620250520001
+ version: 3.0.0-4060620250520001(patch_hash=5763725268e9a493075be3c82d91b590b9ae30e997a04a418494dd7d6d327b9d)(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-alipay':
+ specifier: 3.0.0-4060620250520001
+ version: 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-baidu':
+ specifier: 3.0.0-4060620250520001
+ version: 3.0.0-4060620250520001(@dcloudio/types@3.4.14)(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-harmony':
+ specifier: 3.0.0-4060620250520001
+ version: 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-jd':
+ specifier: 3.0.0-4060620250520001
+ version: 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-kuaishou':
+ specifier: 3.0.0-4060620250520001
+ version: 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-lark':
+ specifier: 3.0.0-4060620250520001
+ version: 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-qq':
+ specifier: 3.0.0-4060620250520001
+ version: 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-toutiao':
+ specifier: 3.0.0-4060620250520001
+ version: 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-weixin':
+ specifier: 3.0.0-4060620250520001
+ version: 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-xhs':
+ specifier: 3.0.0-4060620250520001
+ version: 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-quickapp-webview':
+ specifier: 3.0.0-4060620250520001
+ version: 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@tanstack/vue-query':
+ specifier: ^5.62.16
+ version: 5.62.16(vue@3.5.15(typescript@5.7.2))
+ abortcontroller-polyfill:
+ specifier: ^1.7.8
+ version: 1.7.8
+ alova:
+ specifier: ^3.3.3
+ version: 3.3.3
+ dayjs:
+ specifier: 1.11.10
+ version: 1.11.10
+ js-cookie:
+ specifier: ^3.0.5
+ version: 3.0.5
+ pinia:
+ specifier: 2.0.36
+ version: 2.0.36(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2))
+ pinia-plugin-persistedstate:
+ specifier: 3.2.1
+ version: 3.2.1(pinia@2.0.36(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2)))
+ sm-crypto:
+ specifier: ^0.3.13
+ version: 0.3.14
+ vue:
+ specifier: ^3.4.21
+ version: 3.5.15(typescript@5.7.2)
+ wot-design-uni:
+ specifier: ^1.9.1
+ version: 1.9.1(vue@3.5.15(typescript@5.7.2))
+ z-paging:
+ specifier: 2.8.7
+ version: 2.8.7
+ devDependencies:
+ '@alova/wormhole':
+ specifier: ^1.0.8
+ version: 1.0.8
+ '@antfu/eslint-config':
+ specifier: ^4.15.0
+ version: 4.15.0(@unocss/eslint-plugin@66.2.3(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2))(@vue/compiler-sfc@3.5.15)(eslint-plugin-format@1.0.1(eslint@9.29.0(jiti@2.4.2)))(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)
+ '@commitlint/cli':
+ specifier: ^19.8.1
+ version: 19.8.1(@types/node@20.19.0)(typescript@5.7.2)
+ '@commitlint/config-conventional':
+ specifier: ^19.8.1
+ version: 19.8.1
+ '@dcloudio/types':
+ specifier: ^3.4.8
+ version: 3.4.14
+ '@dcloudio/uni-automator':
+ specifier: 3.0.0-4060620250520001
+ version: 3.0.0-4060620250520001(jest-environment-node@27.5.1)(jest@27.0.4)(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-cli-shared':
+ specifier: 3.0.0-4060620250520001
+ version: 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-stacktracey':
+ specifier: 3.0.0-4060620250520001
+ version: 3.0.0-4060620250520001
+ '@dcloudio/uni-uts-v1':
+ specifier: 3.0.0-4060620250520001
+ version: 3.0.0-4060620250520001(rollup@4.41.1)
+ '@dcloudio/vite-plugin-uni':
+ specifier: 3.0.0-4060620250520001
+ version: 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))
+ '@esbuild/darwin-arm64':
+ specifier: 0.20.2
+ version: 0.20.2
+ '@esbuild/darwin-x64':
+ specifier: 0.20.2
+ version: 0.20.2
+ '@iconify-json/carbon':
+ specifier: ^1.2.4
+ version: 1.2.4
+ '@rollup/rollup-darwin-x64':
+ specifier: ^4.28.0
+ version: 4.41.1
+ '@types/node':
+ specifier: ^20.17.9
+ version: 20.19.0
+ '@types/wechat-miniprogram':
+ specifier: ^3.4.8
+ version: 3.4.8
+ '@uni-helper/eslint-config':
+ specifier: ^0.4.0
+ version: 0.4.0(@antfu/eslint-config@4.15.0(@unocss/eslint-plugin@66.2.3(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2))(@vue/compiler-sfc@3.5.15)(eslint-plugin-format@1.0.1(eslint@9.29.0(jiti@2.4.2)))(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.29.0(jiti@2.4.2))
+ '@uni-helper/uni-types':
+ specifier: 1.0.0-alpha.3
+ version: 1.0.0-alpha.3(@uni-helper/uni-app-types@1.0.0-alpha.3(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2)))(@uni-helper/uni-cloud-types@1.0.0-alpha.3(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2)))(@uni-helper/uni-ui-types@1.0.0-alpha.3(@uni-helper/uni-app-types@1.0.0-alpha.3(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2)))(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2)))(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2))
+ '@uni-helper/unocss-preset-uni':
+ specifier: ^0.2.11
+ version: 0.2.11(@unocss/preset-legacy-compat@0.59.4)(@unocss/preset-mini@66.1.2)(@unocss/rule-utils@66.2.3)(@unocss/vite@66.1.2(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2)))(unocss-applet@0.10.0(@unocss/core@66.2.3)(@unocss/preset-mini@66.1.2)(@unocss/preset-wind3@66.1.2)(unocss@65.4.2(postcss@8.5.3)(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))))(unocss@65.4.2(postcss@8.5.3)(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2)))
+ '@uni-helper/vite-plugin-uni-components':
+ specifier: 0.2.0
+ version: 0.2.0(rollup@4.41.1)
+ '@uni-helper/vite-plugin-uni-layouts':
+ specifier: 0.1.10
+ version: 0.1.10(rollup@4.41.1)
+ '@uni-helper/vite-plugin-uni-manifest':
+ specifier: 0.2.8
+ version: 0.2.8(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))
+ '@uni-helper/vite-plugin-uni-pages':
+ specifier: 0.2.28
+ version: 0.2.28(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))
+ '@uni-helper/vite-plugin-uni-platform':
+ specifier: 0.0.4
+ version: 0.0.4
+ '@uni-ku/bundle-optimizer':
+ specifier: ^1.3.3
+ version: 1.3.3(postcss@8.5.3)(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))
+ '@unocss/eslint-plugin':
+ specifier: ^66.2.3
+ version: 66.2.3(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)
+ '@unocss/preset-legacy-compat':
+ specifier: ^0.59.4
+ version: 0.59.4
+ '@vue/runtime-core':
+ specifier: ^3.4.21
+ version: 3.5.15
+ '@vue/tsconfig':
+ specifier: ^0.1.3
+ version: 0.1.3(@types/node@20.19.0)
+ autoprefixer:
+ specifier: ^10.4.20
+ version: 10.4.20(postcss@8.5.3)
+ eslint:
+ specifier: ^9.29.0
+ version: 9.29.0(jiti@2.4.2)
+ eslint-plugin-format:
+ specifier: ^1.0.1
+ version: 1.0.1(eslint@9.29.0(jiti@2.4.2))
+ husky:
+ specifier: ^9.1.7
+ version: 9.1.7
+ lint-staged:
+ specifier: ^15.2.10
+ version: 15.2.10
+ openapi-ts-request:
+ specifier: ^1.1.2
+ version: 1.1.2(@types/node@20.19.0)(@vue/compiler-sfc@3.5.15)(chokidar@3.6.0)(typescript@5.7.2)
+ postcss:
+ specifier: ^8.4.49
+ version: 8.5.3
+ postcss-html:
+ specifier: ^1.7.0
+ version: 1.7.0
+ postcss-scss:
+ specifier: ^4.0.9
+ version: 4.0.9(postcss@8.5.3)
+ rollup-plugin-visualizer:
+ specifier: ^5.12.0
+ version: 5.12.0(rollup@4.41.1)
+ sass:
+ specifier: 1.77.8
+ version: 1.77.8
+ typescript:
+ specifier: ^5.7.2
+ version: 5.7.2
+ unocss:
+ specifier: 65.4.2
+ version: 65.4.2(postcss@8.5.3)(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))
+ unplugin-auto-import:
+ specifier: ^0.17.8
+ version: 0.17.8(rollup@4.41.1)
+ vite:
+ specifier: 5.2.8
+ version: 5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0)
+ vite-plugin-restart:
+ specifier: ^0.4.2
+ version: 0.4.2(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))
+ vue-tsc:
+ specifier: ^2.2.10
+ version: 2.2.10(typescript@5.7.2)
+
+packages:
+
+ '@alova/adapter-uniapp@2.0.14':
+ resolution: {integrity: sha512-AHrS/evdhONySkmtYHs6Sh1D+a0+zjBeOltcYY2/az/KEPN9p/l4H9Nvy+ghLhzMzfKfnITxeeMOi5ANyERimw==}
+ peerDependencies:
+ alova: ^3.0.20
+
+ '@alova/shared@1.3.1':
+ resolution: {integrity: sha512-ijSOaFLUFcVzMKSY3avoEE5C03/p9atjMDPBwvNkwnzaCrhv6/m4A121NdadF8YlHCRuifyYfz90IyEdMXTsJg==}
+
+ '@alova/wormhole@1.0.8':
+ resolution: {integrity: sha512-GL5bkxlDyuqNVitKCynbRPY2OADN9q5ow+HNfGuyes2PeGlpgLO8fSVYeThnyO8bLlPpoiDJreiOrUBd0AHJNg==}
+ hasBin: true
+
+ '@ampproject/remapping@2.3.0':
+ resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
+ engines: {node: '>=6.0.0'}
+
+ '@antfu/eslint-config@4.15.0':
+ resolution: {integrity: sha512-wNn8eDUR+L48nGqX0j8uS19+lHjlhjJsTuhOIbp5aUtnNzAqUuzeoGa7S7rfIbK3XnzcRXJ1AW+xWXsMSGu0YA==}
+ hasBin: true
+ peerDependencies:
+ '@eslint-react/eslint-plugin': ^1.38.4
+ '@prettier/plugin-xml': ^3.4.1
+ '@unocss/eslint-plugin': '>=0.50.0'
+ astro-eslint-parser: ^1.0.2
+ eslint: ^9.10.0
+ eslint-plugin-astro: ^1.2.0
+ eslint-plugin-format: '>=0.1.0'
+ eslint-plugin-react-hooks: ^5.2.0
+ eslint-plugin-react-refresh: ^0.4.19
+ eslint-plugin-solid: ^0.14.3
+ eslint-plugin-svelte: '>=2.35.1'
+ eslint-plugin-vuejs-accessibility: ^2.4.1
+ prettier-plugin-astro: ^0.14.0
+ prettier-plugin-slidev: ^1.0.5
+ svelte-eslint-parser: '>=0.37.0'
+ peerDependenciesMeta:
+ '@eslint-react/eslint-plugin':
+ optional: true
+ '@prettier/plugin-xml':
+ optional: true
+ '@unocss/eslint-plugin':
+ optional: true
+ astro-eslint-parser:
+ optional: true
+ eslint-plugin-astro:
+ optional: true
+ eslint-plugin-format:
+ optional: true
+ eslint-plugin-react-hooks:
+ optional: true
+ eslint-plugin-react-refresh:
+ optional: true
+ eslint-plugin-solid:
+ optional: true
+ eslint-plugin-svelte:
+ optional: true
+ eslint-plugin-vuejs-accessibility:
+ optional: true
+ prettier-plugin-astro:
+ optional: true
+ prettier-plugin-slidev:
+ optional: true
+ svelte-eslint-parser:
+ optional: true
+
+ '@antfu/install-pkg@1.1.0':
+ resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==}
+
+ '@antfu/utils@0.7.10':
+ resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==}
+
+ '@antfu/utils@8.1.1':
+ resolution: {integrity: sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==}
+
+ '@babel/code-frame@7.27.1':
+ resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.26.2':
+ resolution: {integrity: sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.26.0':
+ resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.26.2':
+ resolution: {integrity: sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.27.1':
+ resolution: {integrity: sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.27.5':
+ resolution: {integrity: sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-annotate-as-pure@7.25.9':
+ resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-builder-binary-assignment-operator-visitor@7.25.9':
+ resolution: {integrity: sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.25.9':
+ resolution: {integrity: sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-create-class-features-plugin@7.25.9':
+ resolution: {integrity: sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-create-regexp-features-plugin@7.25.9':
+ resolution: {integrity: sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-define-polyfill-provider@0.6.3':
+ resolution: {integrity: sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==}
+ peerDependencies:
+ '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+
+ '@babel/helper-member-expression-to-functions@7.25.9':
+ resolution: {integrity: sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.25.9':
+ resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-transforms@7.26.0':
+ resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-optimise-call-expression@7.25.9':
+ resolution: {integrity: sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-plugin-utils@7.27.1':
+ resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-remap-async-to-generator@7.25.9':
+ resolution: {integrity: sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-replace-supers@7.25.9':
+ resolution: {integrity: sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-simple-access@7.25.9':
+ resolution: {integrity: sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-skip-transparent-expression-wrappers@7.25.9':
+ resolution: {integrity: sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.25.9':
+ resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.27.1':
+ resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.27.1':
+ resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-option@7.25.9':
+ resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-wrap-function@7.25.9':
+ resolution: {integrity: sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.26.0':
+ resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.26.2':
+ resolution: {integrity: sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/parser@7.27.2':
+ resolution: {integrity: sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/parser@7.27.5':
+ resolution: {integrity: sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9':
+ resolution: {integrity: sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9':
+ resolution: {integrity: sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9':
+ resolution: {integrity: sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9':
+ resolution: {integrity: sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.13.0
+
+ '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9':
+ resolution: {integrity: sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2':
+ resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-async-generators@7.8.4':
+ resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-bigint@7.8.3':
+ resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-class-properties@7.12.13':
+ resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-class-static-block@7.14.5':
+ resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-import-assertions@7.26.0':
+ resolution: {integrity: sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-import-attributes@7.27.1':
+ resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-import-meta@7.10.4':
+ resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-json-strings@7.8.3':
+ resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-jsx@7.25.9':
+ resolution: {integrity: sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-logical-assignment-operators@7.10.4':
+ resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3':
+ resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-numeric-separator@7.10.4':
+ resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-object-rest-spread@7.8.3':
+ resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-optional-catch-binding@7.8.3':
+ resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-optional-chaining@7.8.3':
+ resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-private-property-in-object@7.14.5':
+ resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-top-level-await@7.14.5':
+ resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-typescript@7.27.1':
+ resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-unicode-sets-regex@7.18.6':
+ resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/plugin-transform-arrow-functions@7.25.9':
+ resolution: {integrity: sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-async-generator-functions@7.25.9':
+ resolution: {integrity: sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-async-to-generator@7.25.9':
+ resolution: {integrity: sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-block-scoped-functions@7.25.9':
+ resolution: {integrity: sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-block-scoping@7.25.9':
+ resolution: {integrity: sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-class-properties@7.25.9':
+ resolution: {integrity: sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-class-static-block@7.26.0':
+ resolution: {integrity: sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.12.0
+
+ '@babel/plugin-transform-classes@7.25.9':
+ resolution: {integrity: sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-computed-properties@7.25.9':
+ resolution: {integrity: sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-destructuring@7.25.9':
+ resolution: {integrity: sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-dotall-regex@7.25.9':
+ resolution: {integrity: sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-duplicate-keys@7.25.9':
+ resolution: {integrity: sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9':
+ resolution: {integrity: sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/plugin-transform-dynamic-import@7.25.9':
+ resolution: {integrity: sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-exponentiation-operator@7.25.9':
+ resolution: {integrity: sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-export-namespace-from@7.25.9':
+ resolution: {integrity: sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-for-of@7.25.9':
+ resolution: {integrity: sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-function-name@7.25.9':
+ resolution: {integrity: sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-json-strings@7.25.9':
+ resolution: {integrity: sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-literals@7.25.9':
+ resolution: {integrity: sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-logical-assignment-operators@7.25.9':
+ resolution: {integrity: sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-member-expression-literals@7.25.9':
+ resolution: {integrity: sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-modules-amd@7.25.9':
+ resolution: {integrity: sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-modules-commonjs@7.25.9':
+ resolution: {integrity: sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-modules-systemjs@7.25.9':
+ resolution: {integrity: sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-modules-umd@7.25.9':
+ resolution: {integrity: sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-named-capturing-groups-regex@7.25.9':
+ resolution: {integrity: sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/plugin-transform-new-target@7.25.9':
+ resolution: {integrity: sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-nullish-coalescing-operator@7.25.9':
+ resolution: {integrity: sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-numeric-separator@7.25.9':
+ resolution: {integrity: sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-object-rest-spread@7.25.9':
+ resolution: {integrity: sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-object-super@7.25.9':
+ resolution: {integrity: sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-optional-catch-binding@7.25.9':
+ resolution: {integrity: sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-optional-chaining@7.25.9':
+ resolution: {integrity: sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-parameters@7.25.9':
+ resolution: {integrity: sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-private-methods@7.25.9':
+ resolution: {integrity: sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-private-property-in-object@7.25.9':
+ resolution: {integrity: sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-property-literals@7.25.9':
+ resolution: {integrity: sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-regenerator@7.25.9':
+ resolution: {integrity: sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-regexp-modifiers@7.26.0':
+ resolution: {integrity: sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/plugin-transform-reserved-words@7.25.9':
+ resolution: {integrity: sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-shorthand-properties@7.25.9':
+ resolution: {integrity: sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-spread@7.25.9':
+ resolution: {integrity: sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-sticky-regex@7.25.9':
+ resolution: {integrity: sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-template-literals@7.25.9':
+ resolution: {integrity: sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-typeof-symbol@7.25.9':
+ resolution: {integrity: sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-typescript@7.25.9':
+ resolution: {integrity: sha512-7PbZQZP50tzv2KGGnhh82GSyMB01yKY9scIjf1a+GfZCtInOWqUH5+1EBU4t9fyR5Oykkkc9vFTs4OHrhHXljQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-unicode-escapes@7.25.9':
+ resolution: {integrity: sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-unicode-property-regex@7.25.9':
+ resolution: {integrity: sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-unicode-regex@7.25.9':
+ resolution: {integrity: sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-unicode-sets-regex@7.25.9':
+ resolution: {integrity: sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/preset-env@7.26.0':
+ resolution: {integrity: sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/preset-modules@0.1.6-no-external-plugins':
+ resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0
+
+ '@babel/runtime@7.26.0':
+ resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/template@7.25.9':
+ resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/template@7.27.2':
+ resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.25.9':
+ resolution: {integrity: sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.27.1':
+ resolution: {integrity: sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.27.4':
+ resolution: {integrity: sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.26.0':
+ resolution: {integrity: sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.27.1':
+ resolution: {integrity: sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.27.6':
+ resolution: {integrity: sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==}
+ engines: {node: '>=6.9.0'}
+
+ '@bcoe/v8-coverage@0.2.3':
+ resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
+
+ '@clack/core@0.5.0':
+ resolution: {integrity: sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==}
+
+ '@clack/prompts@0.11.0':
+ resolution: {integrity: sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==}
+
+ '@commitlint/cli@19.8.1':
+ resolution: {integrity: sha512-LXUdNIkspyxrlV6VDHWBmCZRtkEVRpBKxi2Gtw3J54cGWhLCTouVD/Q6ZSaSvd2YaDObWK8mDjrz3TIKtaQMAA==}
+ engines: {node: '>=v18'}
+ hasBin: true
+
+ '@commitlint/config-conventional@19.8.1':
+ resolution: {integrity: sha512-/AZHJL6F6B/G959CsMAzrPKKZjeEiAVifRyEwXxcT6qtqbPwGw+iQxmNS+Bu+i09OCtdNRW6pNpBvgPrtMr9EQ==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/config-validator@19.8.1':
+ resolution: {integrity: sha512-0jvJ4u+eqGPBIzzSdqKNX1rvdbSU1lPNYlfQQRIFnBgLy26BtC0cFnr7c/AyuzExMxWsMOte6MkTi9I3SQ3iGQ==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/ensure@19.8.1':
+ resolution: {integrity: sha512-mXDnlJdvDzSObafjYrOSvZBwkD01cqB4gbnnFuVyNpGUM5ijwU/r/6uqUmBXAAOKRfyEjpkGVZxaDsCVnHAgyw==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/execute-rule@19.8.1':
+ resolution: {integrity: sha512-YfJyIqIKWI64Mgvn/sE7FXvVMQER/Cd+s3hZke6cI1xgNT/f6ZAz5heND0QtffH+KbcqAwXDEE1/5niYayYaQA==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/format@19.8.1':
+ resolution: {integrity: sha512-kSJj34Rp10ItP+Eh9oCItiuN/HwGQMXBnIRk69jdOwEW9llW9FlyqcWYbHPSGofmjsqeoxa38UaEA5tsbm2JWw==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/is-ignored@19.8.1':
+ resolution: {integrity: sha512-AceOhEhekBUQ5dzrVhDDsbMaY5LqtN8s1mqSnT2Kz1ERvVZkNihrs3Sfk1Je/rxRNbXYFzKZSHaPsEJJDJV8dg==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/lint@19.8.1':
+ resolution: {integrity: sha512-52PFbsl+1EvMuokZXLRlOsdcLHf10isTPlWwoY1FQIidTsTvjKXVXYb7AvtpWkDzRO2ZsqIgPK7bI98x8LRUEw==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/load@19.8.1':
+ resolution: {integrity: sha512-9V99EKG3u7z+FEoe4ikgq7YGRCSukAcvmKQuTtUyiYPnOd9a2/H9Ak1J9nJA1HChRQp9OA/sIKPugGS+FK/k1A==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/message@19.8.1':
+ resolution: {integrity: sha512-+PMLQvjRXiU+Ae0Wc+p99EoGEutzSXFVwQfa3jRNUZLNW5odZAyseb92OSBTKCu+9gGZiJASt76Cj3dLTtcTdg==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/parse@19.8.1':
+ resolution: {integrity: sha512-mmAHYcMBmAgJDKWdkjIGq50X4yB0pSGpxyOODwYmoexxxiUCy5JJT99t1+PEMK7KtsCtzuWYIAXYAiKR+k+/Jw==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/read@19.8.1':
+ resolution: {integrity: sha512-03Jbjb1MqluaVXKHKRuGhcKWtSgh3Jizqy2lJCRbRrnWpcM06MYm8th59Xcns8EqBYvo0Xqb+2DoZFlga97uXQ==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/resolve-extends@19.8.1':
+ resolution: {integrity: sha512-GM0mAhFk49I+T/5UCYns5ayGStkTt4XFFrjjf0L4S26xoMTSkdCf9ZRO8en1kuopC4isDFuEm7ZOm/WRVeElVg==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/rules@19.8.1':
+ resolution: {integrity: sha512-Hnlhd9DyvGiGwjfjfToMi1dsnw1EXKGJNLTcsuGORHz6SS9swRgkBsou33MQ2n51/boIDrbsg4tIBbRpEWK2kw==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/to-lines@19.8.1':
+ resolution: {integrity: sha512-98Mm5inzbWTKuZQr2aW4SReY6WUukdWXuZhrqf1QdKPZBCCsXuG87c+iP0bwtD6DBnmVVQjgp4whoHRVixyPBg==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/top-level@19.8.1':
+ resolution: {integrity: sha512-Ph8IN1IOHPSDhURCSXBz44+CIu+60duFwRsg6HqaISFHQHbmBtxVw4ZrFNIYUzEP7WwrNPxa2/5qJ//NK1FGcw==}
+ engines: {node: '>=v18'}
+
+ '@commitlint/types@19.8.1':
+ resolution: {integrity: sha512-/yCrWGCoA1SVKOks25EGadP9Pnj0oAIHGpl2wH2M2Y46dPM2ueb8wyCVOD7O3WCTkaJ0IkKvzhl1JY7+uCT2Dw==}
+ engines: {node: '>=v18'}
+
+ '@dcloudio/types@3.4.14':
+ resolution: {integrity: sha512-VolQeZfTh8pQFsr2IlfIVX93blfvGTuBoJuZUc7iWOqtHV8gDrq6fXLhzsVlgZyuhhRZLOxlo33rkEqnY+ucAw==}
+
+ '@dcloudio/uni-app-harmony@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-5RIgJ+f4U/+5C+0LW4vB3z0DaIurUnRrocV4KVsJ1Hy67GFIp2sjQ5telPXgCYow0d3hregYMKP6iSxinXeloA==}
+
+ '@dcloudio/uni-app-plus@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-dvPgDWXUyh9DLb7q9hPCFzFEb3Nc26V5E3xNeAELwsOdAltzsrrz4ecPBYaOqoUREZ4PNmcNuS/I+AuNW+HvVA==}
+
+ '@dcloudio/uni-app-uts@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-kq1JhhBmtmdfqmkeOCj3jhOwIv3J20gqXbMexoc1B998k42Byt4XQ68x/fITeZ6JGUw/ev99eFexyECI4Y1Nuw==}
+
+ '@dcloudio/uni-app-vite@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-Aid9y2L330X0r7v3IwlJFLTrlESNEqgkwxzyuTZqN6Zlhb1ETgHuliV0Bl1/Pq4GPnt3OEgFg2H2BcqAYekNGQ==}
+
+ '@dcloudio/uni-app-vue@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-eBr1gZlpyzW6jv2mMvrU9eTfibfWJu7cSU8P6k8OoKRt9bIRrQxELNsDGsORQFaWkrNcuarhAXuKxxvnV9bVCA==}
+
+ '@dcloudio/uni-app-x@0.7.60':
+ resolution: {integrity: sha512-o5LVukL23NO+K4ToGPzKUcyqy2z4vVO3Y6KQQ/HtnTLehKdlYDIUoVLRjL/LaQZuwHUz//F4PmAawhDr6u2G7A==}
+
+ '@dcloudio/uni-app@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-lF9bNbAyeGNP0uAgpcdby5+Wbf4gdvjtW2J/DIWe5E0zoFfTDJkyiE3Zg90i3uF7aZ4unw/hwBdlGLZ6xuQIAg==}
+ peerDependencies:
+ '@dcloudio/types': ^3.4.14
+
+ '@dcloudio/uni-automator@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-ImOT4lXHyTPUVNs4jIweB1ORtcIsWVPcO0/Yr9jh1Qwfy17D4yC26+R8cx+kDdowYutTV1hA3IqKk/g/NS2x+Q==}
+ peerDependencies:
+ jest: 27.0.4
+ jest-environment-node: 27.5.1
+
+ '@dcloudio/uni-cli-shared@3.0.0-4020820240925001':
+ resolution: {integrity: sha512-zWIMxjyc8OdpZU1/ivEfjQQa5jNk2sThIkBS6hM3rs7E68HE6cAxPZqZh9CY0LuM8XsAgILIoY77yTcCM3vMJQ==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+
+ '@dcloudio/uni-cli-shared@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-6OhYL/3xDq2vqqfD6jka8yfNPEjTFcr6qZ1HQvN6YZbM1+AJrgPZkCyhh3w4ypC911lSMVWeWqunIigLLobXAw==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+
+ '@dcloudio/uni-cloud@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-M/OCsqNwWkj0xjKqECGHy2w/Veo4E52TZJifov+7dOahRvEKuZe2S5gstI7YlfQuNl17B1MsckyUVwdoqthsiw==}
+
+ '@dcloudio/uni-components@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-kRIGLVaTvkoUFgljtxyTbSRgin7l0TVc9biZTViLRFL8W1DbK7/Nxy4XAOgUwPC807c/bKAf/JUfljlCcMwwQg==}
+
+ '@dcloudio/uni-console@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-KdEj12xYogeGKBxrTh/9HKUsTOe3+nzK1mszT7abkeEZ0iLFEVewhQjxqpD7AzLeGL4wem648sVc+lEbLz75vw==}
+
+ '@dcloudio/uni-h5-vite@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-sLHfMkEfUMJTZXZedr2/FGuz+M84teDnt1BrdZjGE1tiC+1qPHa49Bg8oxeuR0MLWLAuWq33cW4b2149/ZBW7Q==}
+
+ '@dcloudio/uni-h5-vue@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-5il9s6XMxSmi1ZT6xBqKr6D7Si71rxxRC50WxMyr/a59rgpLVcvqonX6BMPwAxxgYKrGU6HIUifJs0UnD64JNQ==}
+
+ '@dcloudio/uni-h5@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-lfjD85Pj0rLnsPJQKP6T+iy3H1gW/PWwcRU8b/K6lfE0CL74P9DdgZp9VsFRyaTvH1uvwg+PeUElj6iFsI9cSQ==}
+
+ '@dcloudio/uni-i18n@3.0.0-4020820240925001':
+ resolution: {integrity: sha512-+EfuUC79QIYIbKB8cdH0IoY69DYytG9jeybIATSlMwMZjVJznA8dGfvkfgy29mBjgLYThuXyZ1dSZ33DXr9MBw==}
+
+ '@dcloudio/uni-i18n@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-UqYcjERvmlb8FPhKqg9xAkDPxjMXG9BjezaVm0PTw33Jm5h2Sf9BnCJZYVg2RNS7+3kPSwUBIGdyC4rBNI4COg==}
+
+ '@dcloudio/uni-mp-alipay@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-B+8QhXjSZzU1Z8jG5GofPtPuLCsBiGobqlhVMmM7e+KJqKdEGPTFhYFXBoQU3zoU1ONCWvRW6VhScWIt26+4Wg==}
+
+ '@dcloudio/uni-mp-baidu@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-hHmwVyrU+ubdglHP2W9yirm9MTgdHoIWSvoR+sQsaL3ygQBE67di6vyqJhPmH4rB+I0j7/29yLjbQ58Xzz77yg==}
+
+ '@dcloudio/uni-mp-compiler@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-nMeZO/vX/vOZVCnFj+fwwzyrSwWI/oiBUL3ci1bnPfOR8mgM1TB6TVLt3x0g8Xmpjvc88E9/La+2UmCpcKWUAg==}
+
+ '@dcloudio/uni-mp-harmony@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-vT72FTv5po6tb7iaWb5iJ4P2AHSZBi2SK0FHNGux8wRYaMs1Qh3cgwBKcWZb/hbZbMNT/Zu+ydgq09nUtKm/0g==}
+
+ '@dcloudio/uni-mp-jd@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-4CQEmLeXeiTKTncWCpL6zAa0SEDKLCUzCkbUfNXcvUl/D7N37nkPdIhyZNWGTt7d3SQCOPSiqN1vGS10X82mOA==}
+
+ '@dcloudio/uni-mp-kuaishou@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-aWLBYZ9G+ltRIgIt1n7gr43CNSlwIRKF5NJ7uxj8uAOscEAt+VXIUsPS6bqhkrQ4Nt3zggSOrF0zBRF4YQVSuA==}
+
+ '@dcloudio/uni-mp-lark@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-8NwHxJb1rtIvEemGfbBQRGqojlL6KGObxluEtrFX+bjqzAmHqvd6kBAR7LUBFAnHmigVZ9V6J0tqt3Fs2hZJYg==}
+
+ '@dcloudio/uni-mp-qq@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-4iYm9Mnb0whS68qNiJ+t08PaUjP9Jfxwc81RsaPYMSm4oQvDmG8h7STSqAJS70ldUvvX7qjiU8eC6g2fbu+glQ==}
+
+ '@dcloudio/uni-mp-toutiao@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-ChTXgYQM55pFyoSRhJh8pIykRPPZ7Ijkj2KaHnPkivAdictZrA0Urj8v4/DDDfFamHRnSNNyciPLZKjlJqZVMA==}
+
+ '@dcloudio/uni-mp-vite@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-zk62bZsl6X6U+Ite7p7BONIjnN7XiNdhu8P+xcZGDOJCstUawrdbvqzsix2Uht/UxFp4I4a7lB82nDLCcPBdlA==}
+
+ '@dcloudio/uni-mp-vue@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-7VlkwL8XSPhurdkj3CXVWaNPt5ydsQJqRtXZDsbpiLET3TF3pB23QRdPHWSZadv1BSM0zOxaA3HiHnnSQmnG3Q==}
+
+ '@dcloudio/uni-mp-weixin@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-0vzOMVPsLtJB/LF+HlplZEzYmXguEP8Ph4Gzl1/5o7PSvsZQGz+e6hpCjjQPSWdm6Pga4nvGnrtOQ9F7yZvkfA==}
+
+ '@dcloudio/uni-mp-xhs@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-Q60T5QACcdO2OYi7L0c7rTPHP9K9c8OvWG44XD2pNnxaYYm8+UzHG1oCPLgvqRcTy9ZiRF3xvWilgj/Fuwu7rA==}
+
+ '@dcloudio/uni-nvue-styler@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-XsM2jDrMVMMQvKJLGHcxBV4sGkyiFw0J6SctbYci7I/qf/se9WehEqdnuNdFrDv1Fah92bitQ5RFrNrsh7MKOw==}
+
+ '@dcloudio/uni-push@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-7ly9CIZQOPu/mkej/M/2OI88Qq2O89E71lbW2NM+isHMbeJWXDTIn84Xdlk9EONWcOw03HuUWLb2ZYqoeDYiTw==}
+
+ '@dcloudio/uni-quickapp-webview@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-DN1BEeX4PxOPR5EG4zHx/QI7DLoBtqIshw3DPOvtaLPMOd9LKMFgiHRm37WkIjUSDzD1n/DgRB+nXgxH20lCvQ==}
+
+ '@dcloudio/uni-shared@3.0.0-4020820240925001':
+ resolution: {integrity: sha512-CSzMyxotDk/O8Yc2h1B0Bfm/wiDumexLNRJ0EJxXBX2eCyHpLl9SMrYIDqC0Y7aSHNPYA2UKfoaAujTMGqFmtg==}
+
+ '@dcloudio/uni-shared@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-U3VhBYC2+kPDFm7Ap9oZeJTqmys774bcli6kJpNmDYFkgkqOoYUtT+0SueOliREL98ogaqoW42zV5T4XjPXj4w==}
+
+ '@dcloudio/uni-stacktracey@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-Sd3Zvps3TWqMCiSVUtb0Ro30GhA1ic7e92kv5PzCP9REHg7ecNJl7ACo6cY2NioAgITEvmMu6jeZKE2Z+FX4sg==}
+
+ '@dcloudio/uni-stat@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-l2XREt2On5uf33PVrAdarncMmGqBLkgZdLsafqag8R2Kdw2Gg/JQQ66RsrLlfBHN2m9eXpfFW6MITqF2vYOoeA==}
+
+ '@dcloudio/uni-uts-v1@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-cab/nSCI3RbjbuVgoe02JqSyTzmc/9ORAYFXSXxNP30pIQ7P3Dw/73Bx9XVsYdccpsNru/eVXuO2CD5PtKojsg==}
+
+ '@dcloudio/uts-darwin-arm64@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-wUCXWtfYJHguH5XUcGGFJV1CNU6tTGc9JtV0sojJ3cAznImJOhCwN2IKyi0cp6TNX9Lh+L/vUprGQuyO459TAw==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@dcloudio/uts-darwin-x64@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-z1gr5/5puyCXflMe0p4gKXsGrJqe4rF2SYpeyBtfqniimN8kWwbn5jKRND39f0BlCGBT96Qopejcafp/bPPYXg==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@dcloudio/uts-linux-x64-gnu@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-5GOU82ss1BU5SiswytgQycnGt1FN1eTVPGrGoe/IOEk9VInMhLqafBg+HT5/ET17yVL60GMF0RAFPrj2JijKBg==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [gnu]
+
+ '@dcloudio/uts-linux-x64-musl@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-/ehQmzfNxFkd6s07JdGbtX6Dxh+BmFST7vo9qcIphgTQwle5QMsmhTRPicPUK1QOcs/YA9I46TPsKIi/jfxLXA==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@dcloudio/uts-win32-ia32-msvc@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-0mVF1vjD44e0/3WJoeJWmM7OfE7GKimFUaCi6ZvvkC2K83E9GCNht9REucoZBinK5USJh/WdTcsdLscFmjpIEw==}
+ engines: {node: '>=10'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@dcloudio/uts-win32-x64-msvc@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-z9gXMtLcc/4Nh6JQwhaJTM5XqpGur+JTrp7cm8J2w72al8RCEt1ixgUkz1XgIK+yHO8mRbitN6AWU0IrQK2Uhg==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@dcloudio/uts@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-aVqRlEhC/0PvDCrUKXBTNQomApt5Bc+KCI3Ojrxtx0EyOqLPIb9PnHw7oVcxM8pwyzOfQ5lwu+pfyweClB6n4w==}
+
+ '@dcloudio/vite-plugin-uni@3.0.0-4060620250520001':
+ resolution: {integrity: sha512-U2UDgnEF6A2xQ2cqGnwaCZMCOk6hMQaJ/lmJoUAlst9ldy8c70T4oPTfzaJzBUQJioF0T+BRhF/HbKnrxICh4g==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ hasBin: true
+ peerDependencies:
+ vite: ^5.2.8
+
+ '@dprint/formatter@0.3.0':
+ resolution: {integrity: sha512-N9fxCxbaBOrDkteSOzaCqwWjso5iAe+WJPsHC021JfHNj2ThInPNEF13ORDKta3llq5D1TlclODCvOvipH7bWQ==}
+
+ '@dprint/markdown@0.17.8':
+ resolution: {integrity: sha512-ukHFOg+RpG284aPdIg7iPrCYmMs3Dqy43S1ejybnwlJoFiW02b+6Bbr5cfZKFRYNP3dKGM86BqHEnMzBOyLvvA==}
+
+ '@dprint/toml@0.6.4':
+ resolution: {integrity: sha512-bZXIUjxr0LIuHWshZr/5mtUkOrnh0NKVZEF6ACojW5z7zkJu7s9sV2mMXm8XQDqN4cJzdHYUYzUyEGdfciaLJA==}
+
+ '@emnapi/core@1.4.3':
+ resolution: {integrity: sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==}
+
+ '@emnapi/runtime@1.4.3':
+ resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==}
+
+ '@emnapi/wasi-threads@1.0.2':
+ resolution: {integrity: sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==}
+
+ '@es-joy/jsdoccomment@0.50.2':
+ resolution: {integrity: sha512-YAdE/IJSpwbOTiaURNCKECdAwqrJuFiZhylmesBcIRawtYKnBR2wxPhoIewMg+Yu+QuYvHfJNReWpoxGBKOChA==}
+ engines: {node: '>=18'}
+
+ '@es-joy/jsdoccomment@0.51.1':
+ resolution: {integrity: sha512-fPn7AW/unCg3JRLt8Wg44HLRVkAEfkFIXiGRJbeOIrd7Hgl4iOFwVHpvVR8I5hJYpn5mNFWwIXR6A16ZRA/M9w==}
+ engines: {node: '>=18'}
+
+ '@esbuild/aix-ppc64@0.20.2':
+ resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/aix-ppc64@0.24.2':
+ resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/aix-ppc64@0.25.5':
+ resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.20.2':
+ resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm64@0.24.2':
+ resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm64@0.25.5':
+ resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.20.2':
+ resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-arm@0.24.2':
+ resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-arm@0.25.5':
+ resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.20.2':
+ resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/android-x64@0.24.2':
+ resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/android-x64@0.25.5':
+ resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.20.2':
+ resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==}
+ engines: {node: '>=12'}
+ os: [darwin]
+
+ '@esbuild/darwin-arm64@0.24.2':
+ resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-arm64@0.25.5':
+ resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.20.2':
+ resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==}
+ engines: {node: '>=12'}
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.24.2':
+ resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.25.5':
+ resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.20.2':
+ resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-arm64@0.24.2':
+ resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-arm64@0.25.5':
+ resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.20.2':
+ resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.24.2':
+ resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.25.5':
+ resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.20.2':
+ resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm64@0.24.2':
+ resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm64@0.25.5':
+ resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.20.2':
+ resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.24.2':
+ resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.25.5':
+ resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.20.2':
+ resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.24.2':
+ resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.25.5':
+ resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.20.2':
+ resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==}
+ engines: {node: '>=12'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.24.2':
+ resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.25.5':
+ resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.20.2':
+ resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==}
+ engines: {node: '>=12'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.24.2':
+ resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.25.5':
+ resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.20.2':
+ resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.24.2':
+ resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.25.5':
+ resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.20.2':
+ resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==}
+ engines: {node: '>=12'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.24.2':
+ resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.25.5':
+ resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.20.2':
+ resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==}
+ engines: {node: '>=12'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.24.2':
+ resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.25.5':
+ resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.20.2':
+ resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.24.2':
+ resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.25.5':
+ resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.24.2':
+ resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-arm64@0.25.5':
+ resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.20.2':
+ resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.24.2':
+ resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.25.5':
+ resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.24.2':
+ resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-arm64@0.25.5':
+ resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.20.2':
+ resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.24.2':
+ resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.25.5':
+ resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/sunos-x64@0.20.2':
+ resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/sunos-x64@0.24.2':
+ resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/sunos-x64@0.25.5':
+ resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.20.2':
+ resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-arm64@0.24.2':
+ resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-arm64@0.25.5':
+ resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.20.2':
+ resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.24.2':
+ resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.25.5':
+ resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.20.2':
+ resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.24.2':
+ resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.25.5':
+ resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@eslint-community/eslint-plugin-eslint-comments@4.5.0':
+ resolution: {integrity: sha512-MAhuTKlr4y/CE3WYX26raZjy+I/kS2PLKSzvfmDCGrBLTFHOYwqROZdr4XwPgXwX3K9rjzMr4pSmUWGnzsUyMg==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0
+
+ '@eslint-community/eslint-utils@4.7.0':
+ resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+ '@eslint-community/regexpp@4.12.1':
+ resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ '@eslint/compat@1.3.0':
+ resolution: {integrity: sha512-ZBygRBqpDYiIHsN+d1WyHn3TYgzgpzLEcgJUxTATyiInQbKZz6wZb6+ljwdg8xeeOe4v03z6Uh6lELiw0/mVhQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^9.10.0
+ peerDependenciesMeta:
+ eslint:
+ optional: true
+
+ '@eslint/config-array@0.20.1':
+ resolution: {integrity: sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/config-helpers@0.2.3':
+ resolution: {integrity: sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/core@0.13.0':
+ resolution: {integrity: sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/core@0.14.0':
+ resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/core@0.15.0':
+ resolution: {integrity: sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/eslintrc@3.3.1':
+ resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/js@9.29.0':
+ resolution: {integrity: sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/markdown@6.6.0':
+ resolution: {integrity: sha512-IsWPy2jU3gaQDlioDC4sT4I4kG1hX1OMWs/q2sWwJrPoMASHW/Z4SDw+6Aql6EsHejGbagYuJbFq9Zvx+Y1b1Q==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/object-schema@2.1.6':
+ resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/plugin-kit@0.2.8':
+ resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/plugin-kit@0.3.2':
+ resolution: {integrity: sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@exodus/schemasafe@1.3.0':
+ resolution: {integrity: sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==}
+
+ '@humanfs/core@0.19.1':
+ resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanfs/node@0.16.6':
+ resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanwhocodes/module-importer@1.0.1':
+ resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+ engines: {node: '>=12.22'}
+
+ '@humanwhocodes/retry@0.3.1':
+ resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==}
+ engines: {node: '>=18.18'}
+
+ '@humanwhocodes/retry@0.4.3':
+ resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
+ engines: {node: '>=18.18'}
+
+ '@iconify-json/carbon@1.2.4':
+ resolution: {integrity: sha512-DhW2jjMVGwV0DLHc0cmDYohdtGxMra8UuwgjHrryPy+rQX4gXhJwCBBVP2h2UG/92AoRCTn7zUJve4WvY5MLYg==}
+
+ '@iconify/types@2.0.0':
+ resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
+
+ '@iconify/utils@2.3.0':
+ resolution: {integrity: sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==}
+
+ '@intlify/core-base@9.1.9':
+ resolution: {integrity: sha512-x5T0p/Ja0S8hs5xs+ImKyYckVkL4CzcEXykVYYV6rcbXxJTe2o58IquSqX9bdncVKbRZP7GlBU1EcRaQEEJ+vw==}
+ engines: {node: '>= 10'}
+
+ '@intlify/devtools-if@9.1.9':
+ resolution: {integrity: sha512-oKSMKjttG3Ut/1UGEZjSdghuP3fwA15zpDPcjkf/1FjlOIm6uIBGMNS5jXzsZy593u+P/YcnrZD6cD3IVFz9vQ==}
+ engines: {node: '>= 10'}
+
+ '@intlify/message-compiler@9.1.9':
+ resolution: {integrity: sha512-6YgCMF46Xd0IH2hMRLCssZI3gFG4aywidoWQ3QP4RGYQXQYYfFC54DxhSgfIPpVoPLQ+4AD29eoYmhiHZ+qLFQ==}
+ engines: {node: '>= 10'}
+
+ '@intlify/message-resolver@9.1.9':
+ resolution: {integrity: sha512-Lx/DBpigeK0sz2BBbzv5mu9/dAlt98HxwbG7xLawC3O2xMF9MNWU5FtOziwYG6TDIjNq0O/3ZbOJAxwITIWXEA==}
+ engines: {node: '>= 10'}
+
+ '@intlify/runtime@9.1.9':
+ resolution: {integrity: sha512-XgPw8+UlHCiie3fI41HPVa/VDJb3/aSH7bLhY1hJvlvNV713PFtb4p4Jo+rlE0gAoMsMCGcsiT982fImolSltg==}
+ engines: {node: '>= 10'}
+
+ '@intlify/shared@9.1.9':
+ resolution: {integrity: sha512-xKGM1d0EAxdDFCWedcYXOm6V5Pfw/TMudd6/qCdEb4tv0hk9EKeg7lwQF1azE0dP2phvx0yXxrt7UQK+IZjNdw==}
+ engines: {node: '>= 10'}
+
+ '@intlify/vue-devtools@9.1.9':
+ resolution: {integrity: sha512-YPehH9uL4vZcGXky4Ev5qQIITnHKIvsD2GKGXgqf+05osMUI6WSEQHaN9USRa318Rs8RyyPCiDfmA0hRu3k7og==}
+ engines: {node: '>= 10'}
+
+ '@isaacs/cliui@8.0.2':
+ resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
+ engines: {node: '>=12'}
+
+ '@istanbuljs/load-nyc-config@1.1.0':
+ resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
+ engines: {node: '>=8'}
+
+ '@istanbuljs/schema@0.1.3':
+ resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
+ engines: {node: '>=8'}
+
+ '@jest/console@27.5.1':
+ resolution: {integrity: sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ '@jest/core@27.5.1':
+ resolution: {integrity: sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+ peerDependencies:
+ node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+ peerDependenciesMeta:
+ node-notifier:
+ optional: true
+
+ '@jest/environment@27.5.1':
+ resolution: {integrity: sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ '@jest/fake-timers@27.5.1':
+ resolution: {integrity: sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ '@jest/globals@27.5.1':
+ resolution: {integrity: sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ '@jest/reporters@27.5.1':
+ resolution: {integrity: sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+ peerDependencies:
+ node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+ peerDependenciesMeta:
+ node-notifier:
+ optional: true
+
+ '@jest/source-map@27.5.1':
+ resolution: {integrity: sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ '@jest/test-result@27.5.1':
+ resolution: {integrity: sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ '@jest/test-sequencer@27.5.1':
+ resolution: {integrity: sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ '@jest/transform@27.5.1':
+ resolution: {integrity: sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ '@jest/types@27.5.1':
+ resolution: {integrity: sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ '@jimp/bmp@0.10.3':
+ resolution: {integrity: sha512-keMOc5woiDmONXsB/6aXLR4Z5Q+v8lFq3EY2rcj2FmstbDMhRuGbmcBxlEgOqfRjwvtf/wOtJ3Of37oAWtVfLg==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+
+ '@jimp/core@0.10.3':
+ resolution: {integrity: sha512-Gd5IpL3U2bFIO57Fh/OA3HCpWm4uW/pU01E75rI03BXfTdz3T+J7TwvyG1XaqsQ7/DSlS99GXtLQPlfFIe28UA==}
+
+ '@jimp/custom@0.10.3':
+ resolution: {integrity: sha512-nZmSI+jwTi5IRyNLbKSXQovoeqsw+D0Jn0SxW08wYQvdkiWA8bTlDQFgQ7HVwCAKBm8oKkDB/ZEo9qvHJ+1gAQ==}
+
+ '@jimp/gif@0.10.3':
+ resolution: {integrity: sha512-vjlRodSfz1CrUvvrnUuD/DsLK1GHB/yDZXHthVdZu23zYJIW7/WrIiD1IgQ5wOMV7NocfrvPn2iqUfBP81/WWA==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+
+ '@jimp/jpeg@0.10.3':
+ resolution: {integrity: sha512-AAANwgUZOt6f6P7LZxY9lyJ9xclqutYJlsxt3JbriXUGJgrrFAIkcKcqv1nObgmQASSAQKYaMV9KdHjMlWFKlQ==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+
+ '@jimp/plugin-blit@0.10.3':
+ resolution: {integrity: sha512-5zlKlCfx4JWw9qUVC7GI4DzXyxDWyFvgZLaoGFoT00mlXlN75SarlDwc9iZ/2e2kp4bJWxz3cGgG4G/WXrbg3Q==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+
+ '@jimp/plugin-blur@0.10.3':
+ resolution: {integrity: sha512-cTOK3rjh1Yjh23jSfA6EHCHjsPJDEGLC8K2y9gM7dnTUK1y9NNmkFS23uHpyjgsWFIoH9oRh2SpEs3INjCpZhQ==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+
+ '@jimp/plugin-circle@0.10.3':
+ resolution: {integrity: sha512-51GAPIVelqAcfuUpaM5JWJ0iWl4vEjNXB7p4P7SX5udugK5bxXUjO6KA2qgWmdpHuCKtoNgkzWU9fNSuYp7tCA==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+
+ '@jimp/plugin-color@0.10.3':
+ resolution: {integrity: sha512-RgeHUElmlTH7vpI4WyQrz6u59spiKfVQbsG/XUzfWGamFSixa24ZDwX/yV/Ts+eNaz7pZeIuv533qmKPvw2ujg==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+
+ '@jimp/plugin-contain@0.10.3':
+ resolution: {integrity: sha512-bYJKW9dqzcB0Ihc6u7jSyKa3juStzbLs2LFr6fu8TzA2WkMS/R8h+ddkiO36+F9ILTWHP0CIA3HFe5OdOGcigw==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+ '@jimp/plugin-blit': '>=0.3.5'
+ '@jimp/plugin-resize': '>=0.3.5'
+ '@jimp/plugin-scale': '>=0.3.5'
+
+ '@jimp/plugin-cover@0.10.3':
+ resolution: {integrity: sha512-pOxu0cM0BRPzdV468n4dMocJXoMbTnARDY/EpC3ZW15SpMuc/dr1KhWQHgoQX5kVW1Wt8zgqREAJJCQ5KuPKDA==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+ '@jimp/plugin-crop': '>=0.3.5'
+ '@jimp/plugin-resize': '>=0.3.5'
+ '@jimp/plugin-scale': '>=0.3.5'
+
+ '@jimp/plugin-crop@0.10.3':
+ resolution: {integrity: sha512-nB7HgOjjl9PgdHr076xZ3Sr6qHYzeBYBs9qvs3tfEEUeYMNnvzgCCGtUl6eMakazZFCMk3mhKmcB9zQuHFOvkg==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+
+ '@jimp/plugin-displace@0.10.3':
+ resolution: {integrity: sha512-8t3fVKCH5IVqI4lewe4lFFjpxxr69SQCz5/tlpDLQZsrNScNJivHdQ09zljTrVTCSgeCqQJIKgH2Q7Sk/pAZ0w==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+
+ '@jimp/plugin-dither@0.10.3':
+ resolution: {integrity: sha512-JCX/oNSnEg1kGQ8ffZ66bEgQOLCY3Rn+lrd6v1jjLy/mn9YVZTMsxLtGCXpiCDC2wG/KTmi4862ysmP9do9dAQ==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+
+ '@jimp/plugin-fisheye@0.10.3':
+ resolution: {integrity: sha512-RRZb1wqe+xdocGcFtj2xHU7sF7xmEZmIa6BmrfSchjyA2b32TGPWKnP3qyj7p6LWEsXn+19hRYbjfyzyebPElQ==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+
+ '@jimp/plugin-flip@0.10.3':
+ resolution: {integrity: sha512-0epbi8XEzp0wmSjoW9IB0iMu0yNF17aZOxLdURCN3Zr+8nWPs5VNIMqSVa1Y62GSyiMDpVpKF/ITiXre+EqrPg==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+ '@jimp/plugin-rotate': '>=0.3.5'
+
+ '@jimp/plugin-gaussian@0.10.3':
+ resolution: {integrity: sha512-25eHlFbHUDnMMGpgRBBeQ2AMI4wsqCg46sue0KklI+c2BaZ+dGXmJA5uT8RTOrt64/K9Wz5E+2n7eBnny4dfpQ==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+
+ '@jimp/plugin-invert@0.10.3':
+ resolution: {integrity: sha512-effYSApWY/FbtlzqsKXlTLkgloKUiHBKjkQnqh5RL4oQxh/33j6aX+HFdDyQKtsXb8CMd4xd7wyiD2YYabTa0g==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+
+ '@jimp/plugin-mask@0.10.3':
+ resolution: {integrity: sha512-twrg8q8TIhM9Z6Jcu9/5f+OCAPaECb0eKrrbbIajJqJ3bCUlj5zbfgIhiQIzjPJ6KjpnFPSqHQfHkU1Vvk/nVw==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+
+ '@jimp/plugin-normalize@0.10.3':
+ resolution: {integrity: sha512-xkb5eZI/mMlbwKkDN79+1/t/+DBo8bBXZUMsT4gkFgMRKNRZ6NQPxlv1d3QpRzlocsl6UMxrHnhgnXdLAcgrXw==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+
+ '@jimp/plugin-print@0.10.3':
+ resolution: {integrity: sha512-wjRiI6yjXsAgMe6kVjizP+RgleUCLkH256dskjoNvJzmzbEfO7xQw9g6M02VET+emnbY0CO83IkrGm2q43VRyg==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+ '@jimp/plugin-blit': '>=0.3.5'
+
+ '@jimp/plugin-resize@0.10.3':
+ resolution: {integrity: sha512-rf8YmEB1d7Sg+g4LpqF0Mp+dfXfb6JFJkwlAIWPUOR7lGsPWALavEwTW91c0etEdnp0+JB9AFpy6zqq7Lwkq6w==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+
+ '@jimp/plugin-rotate@0.10.3':
+ resolution: {integrity: sha512-YXLlRjm18fkW9MOHUaVAxWjvgZM851ofOipytz5FyKp4KZWDLk+dZK1JNmVmK7MyVmAzZ5jsgSLhIgj+GgN0Eg==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+ '@jimp/plugin-blit': '>=0.3.5'
+ '@jimp/plugin-crop': '>=0.3.5'
+ '@jimp/plugin-resize': '>=0.3.5'
+
+ '@jimp/plugin-scale@0.10.3':
+ resolution: {integrity: sha512-5DXD7x7WVcX1gUgnlFXQa8F+Q3ThRYwJm+aesgrYvDOY+xzRoRSdQvhmdd4JEEue3lyX44DvBSgCIHPtGcEPaw==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+ '@jimp/plugin-resize': '>=0.3.5'
+
+ '@jimp/plugin-shadow@0.10.3':
+ resolution: {integrity: sha512-/nkFXpt2zVcdP4ETdkAUL0fSzyrC5ZFxdcphbYBodqD7fXNqChS/Un1eD4xCXWEpW8cnG9dixZgQgStjywH0Mg==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+ '@jimp/plugin-blur': '>=0.3.5'
+ '@jimp/plugin-resize': '>=0.3.5'
+
+ '@jimp/plugin-threshold@0.10.3':
+ resolution: {integrity: sha512-Dzh0Yq2wXP2SOnxcbbiyA4LJ2luwrdf1MghNIt9H+NX7B+IWw/N8qA2GuSm9n4BPGSLluuhdAWJqHcTiREriVA==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+ '@jimp/plugin-color': '>=0.8.0'
+ '@jimp/plugin-resize': '>=0.8.0'
+
+ '@jimp/plugins@0.10.3':
+ resolution: {integrity: sha512-jTT3/7hOScf0EIKiAXmxwayHhryhc1wWuIe3FrchjDjr9wgIGNN2a7XwCgPl3fML17DXK1x8EzDneCdh261bkw==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+
+ '@jimp/png@0.10.3':
+ resolution: {integrity: sha512-YKqk/dkl+nGZxSYIDQrqhmaP8tC3IK8H7dFPnnzFVvbhDnyYunqBZZO3SaZUKTichClRw8k/CjBhbc+hifSGWg==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+
+ '@jimp/tiff@0.10.3':
+ resolution: {integrity: sha512-7EsJzZ5Y/EtinkBGuwX3Bi4S+zgbKouxjt9c82VJTRJOQgLWsE/RHqcyRCOQBhHAZ9QexYmDz34medfLKdoX0g==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+
+ '@jimp/types@0.10.3':
+ resolution: {integrity: sha512-XGmBakiHZqseSWr/puGN+CHzx0IKBSpsKlmEmsNV96HKDiP6eu8NSnwdGCEq2mmIHe0JNcg1hqg59hpwtQ7Tiw==}
+ peerDependencies:
+ '@jimp/custom': '>=0.3.5'
+
+ '@jimp/utils@0.10.3':
+ resolution: {integrity: sha512-VcSlQhkil4ReYmg1KkN+WqHyYfZ2XfZxDsKAHSfST1GEz/RQHxKZbX+KhFKtKflnL0F4e6DlNQj3vznMNXCR2w==}
+
+ '@jridgewell/gen-mapping@0.3.5':
+ resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/gen-mapping@0.3.8':
+ resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/set-array@1.2.1':
+ resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/source-map@0.3.6':
+ resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==}
+
+ '@jridgewell/sourcemap-codec@1.5.0':
+ resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
+
+ '@jridgewell/trace-mapping@0.3.25':
+ resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
+
+ '@napi-rs/wasm-runtime@0.2.10':
+ resolution: {integrity: sha512-bCsCyeZEwVErsGmyPNSzwfwFn4OdxBj0mmv6hOFucB/k81Ojdu68RbZdxYsRQUPc9l6SU5F/cG+bXgWs3oUgsQ==}
+
+ '@node-rs/xxhash-android-arm-eabi@1.7.6':
+ resolution: {integrity: sha512-ptmfpFZ8SgTef58Us+0HsZ9BKhyX/gZYbhLkuzPt7qUoMqMSJK85NC7LEgzDgjUiG+S5GahEEQ9/tfh9BVvKhw==}
+ engines: {node: '>= 12'}
+ cpu: [arm]
+ os: [android]
+
+ '@node-rs/xxhash-android-arm64@1.7.6':
+ resolution: {integrity: sha512-n4MyZvqifuoARfBvrZ2IBqmsGzwlVI3kb2mB0gVvoHtMsPbl/q94zoDBZ7WgeP3t4Wtli+QS3zgeTCOWUbqqUQ==}
+ engines: {node: '>= 12'}
+ cpu: [arm64]
+ os: [android]
+
+ '@node-rs/xxhash-darwin-arm64@1.7.6':
+ resolution: {integrity: sha512-6xGuE07CiCIry/KT3IiwQd/kykTOmjKzO/ZnHlE5ibGMx64NFE0qDuwJbxQ4rGyUzgJ0KuN9ZdOhUDJmepnpcw==}
+ engines: {node: '>= 12'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@node-rs/xxhash-darwin-x64@1.7.6':
+ resolution: {integrity: sha512-Z4oNnhyznDvHhxv+s0ka+5KG8mdfLVucZMZMejj9BL+CPmamClygPiHIRiifRcPAoX9uPZykaCsULngIfLeF3Q==}
+ engines: {node: '>= 12'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@node-rs/xxhash-freebsd-x64@1.7.6':
+ resolution: {integrity: sha512-arCDOf3xZ5NfBL5fk5J52sNPjXL2cVWN6nXNB3nrtRFFdPBLsr6YXtshAc6wMVxnIW4VGaEv/5K6IpTA8AFyWw==}
+ engines: {node: '>= 12'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@node-rs/xxhash-linux-arm-gnueabihf@1.7.6':
+ resolution: {integrity: sha512-ndLLEW+MwLH3lFS0ahlHCcmkf2ykOv/pbP8OBBeAOlz/Xc3jKztg5IJ9HpkjKOkHk470yYxgHVaw1QMoMzU00A==}
+ engines: {node: '>= 12'}
+ cpu: [arm]
+ os: [linux]
+
+ '@node-rs/xxhash-linux-arm64-gnu@1.7.6':
+ resolution: {integrity: sha512-VX7VkTG87mAdrF2vw4aroiRpFIIN8Lj6NgtGHF+IUVbzQxPudl4kG+FPEjsNH8y04yQxRbPE7naQNgHcTKMrNw==}
+ engines: {node: '>= 12'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@node-rs/xxhash-linux-arm64-musl@1.7.6':
+ resolution: {integrity: sha512-AB5m6crGYSllM9F/xZNOQSPImotR5lOa9e4arW99Bv82S+gcpphI8fGMDOVTTCXY/RLRhvvhwzLDxmLB2O8VDg==}
+ engines: {node: '>= 12'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@node-rs/xxhash-linux-x64-gnu@1.7.6':
+ resolution: {integrity: sha512-a2A6M+5tc0PVlJlE/nl0XsLEzMpKkwg7Y1lR5urFUbW9uVQnKjJYQDrUojhlXk0Uv3VnYQPa6ThmwlacZA5mvQ==}
+ engines: {node: '>= 12'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@node-rs/xxhash-linux-x64-musl@1.7.6':
+ resolution: {integrity: sha512-WioGJSC1GoxQpmdQrG5l/uddSBAS4XCWczHNwXe895J5xadGQzyvmr0r17BNfihvbBUDH1H9jwouNYzDDeA6+A==}
+ engines: {node: '>= 12'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@node-rs/xxhash-wasm32-wasi@1.7.6':
+ resolution: {integrity: sha512-WDXXKMMFMrez+esm2DzMPHFNPFYf+wQUtaXrXwtxXeQMFEzleOLwEaqV0+bbXGJTwhPouL3zY1Qo2xmIH4kkTg==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+
+ '@node-rs/xxhash-win32-arm64-msvc@1.7.6':
+ resolution: {integrity: sha512-qjDFUZJT/Zq0yFS+0TApkD86p0NBdPXlOoHur9yNeO9YX2/9/b1sC2P7N27PgOu13h61TUOvTUC00e/82jAZRQ==}
+ engines: {node: '>= 12'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@node-rs/xxhash-win32-ia32-msvc@1.7.6':
+ resolution: {integrity: sha512-s7a+mQWOTnU4NiiypRq/vbNGot/il0HheXuy9oxJ0SW2q/e4BJ8j0pnP6UBlAjsk+005A76vOwsEj01qbQw8+A==}
+ engines: {node: '>= 12'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@node-rs/xxhash-win32-x64-msvc@1.7.6':
+ resolution: {integrity: sha512-zHOHm2UaIahRhgRPJll+4Xy4Z18aAT/7KNeQW+QJupGvFz+GzOFXMGs3R/3B1Ktob/F5ui3i1MrW9GEob3CWTg==}
+ engines: {node: '>= 12'}
+ cpu: [x64]
+ os: [win32]
+
+ '@node-rs/xxhash@1.7.6':
+ resolution: {integrity: sha512-XMisO+aQHsVpxRp/85EszTtOQTOlhPbd149P/Xa9F55wafA6UM3h2UhOgOs7aAzItnHU/Aw1WQ1FVTEg7WB43Q==}
+ engines: {node: '>= 12'}
+
+ '@nodelib/fs.scandir@2.1.5':
+ resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.stat@2.0.5':
+ resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.walk@1.2.8':
+ resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+ engines: {node: '>= 8'}
+
+ '@pkgr/core@0.1.2':
+ resolution: {integrity: sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==}
+ engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+
+ '@pkgr/core@0.2.7':
+ resolution: {integrity: sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==}
+ engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+
+ '@polka/url@1.0.0-next.29':
+ resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
+
+ '@prettier/sync@0.5.2':
+ resolution: {integrity: sha512-Yb569su456XNx5BsH/Vyem7xD6g/y9iLmLUzRKM1a/dhU/D7HqqvkAG72znulXlMXztbV0iiu9O5AL8K98TzZQ==}
+ peerDependencies:
+ prettier: '*'
+
+ '@quansync/fs@0.1.3':
+ resolution: {integrity: sha512-G0OnZbMWEs5LhDyqy2UL17vGhSVHkQIfVojMtEWVenvj0V5S84VBgy86kJIuNsGDp2p7sTKlpSIpBUWdC35OKg==}
+ engines: {node: '>=20.0.0'}
+
+ '@rollup/pluginutils@5.1.3':
+ resolution: {integrity: sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+
+ '@rollup/pluginutils@5.1.4':
+ resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+
+ '@rollup/rollup-android-arm-eabi@4.41.1':
+ resolution: {integrity: sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.41.1':
+ resolution: {integrity: sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==}
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.41.1':
+ resolution: {integrity: sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.41.1':
+ resolution: {integrity: sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==}
+ os: [darwin]
+
+ '@rollup/rollup-freebsd-arm64@4.41.1':
+ resolution: {integrity: sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@rollup/rollup-freebsd-x64@4.41.1':
+ resolution: {integrity: sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.41.1':
+ resolution: {integrity: sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==}
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.41.1':
+ resolution: {integrity: sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==}
+ cpu: [arm]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-arm64-gnu@4.41.1':
+ resolution: {integrity: sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-arm64-musl@4.41.1':
+ resolution: {integrity: sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-loongarch64-gnu@4.41.1':
+ resolution: {integrity: sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==}
+ cpu: [loong64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-powerpc64le-gnu@4.41.1':
+ resolution: {integrity: sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.41.1':
+ resolution: {integrity: sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-riscv64-musl@4.41.1':
+ resolution: {integrity: sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-s390x-gnu@4.41.1':
+ resolution: {integrity: sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-x64-gnu@4.41.1':
+ resolution: {integrity: sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-x64-musl@4.41.1':
+ resolution: {integrity: sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-win32-arm64-msvc@4.41.1':
+ resolution: {integrity: sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.41.1':
+ resolution: {integrity: sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.41.1':
+ resolution: {integrity: sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==}
+ cpu: [x64]
+ os: [win32]
+
+ '@sindresorhus/is@4.6.0':
+ resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
+ engines: {node: '>=10'}
+
+ '@sinonjs/commons@1.8.6':
+ resolution: {integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==}
+
+ '@sinonjs/fake-timers@8.1.0':
+ resolution: {integrity: sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==}
+
+ '@stylistic/eslint-plugin@5.0.0-beta.5':
+ resolution: {integrity: sha512-hYoTE1AuPKkF8gN6nwJVnBovUEkt3UGiE3zu8nNmFi3yw7F36l1/tU/VRg16CZC804rnAhUwsYTHqKnAPqICxg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: '>=9.0.0'
+
+ '@szmarczak/http-timer@4.0.6':
+ resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==}
+ engines: {node: '>=10'}
+
+ '@tanstack/match-sorter-utils@8.19.4':
+ resolution: {integrity: sha512-Wo1iKt2b9OT7d+YGhvEPD3DXvPv2etTusIMhMUoG7fbhmxcXCtIjJDEygy91Y2JFlwGyjqiBPRozme7UD8hoqg==}
+ engines: {node: '>=12'}
+
+ '@tanstack/query-core@5.62.16':
+ resolution: {integrity: sha512-9Sgft7Qavcd+sN0V25xVyo0nfmcZXBuODy3FVG7BMWTg1HMLm8wwG5tNlLlmSic1u7l1v786oavn+STiFaPH2g==}
+
+ '@tanstack/vue-query@5.62.16':
+ resolution: {integrity: sha512-QVx/tAexhAN+q0IbwyhVXu3f3hujfvhqLlZMNAWKTrDSUcp2BxJNjPyiti5gcaDhQwBUK9BdSXIFFYLcjYWvvA==}
+ peerDependencies:
+ '@vue/composition-api': ^1.1.2
+ vue: ^2.6.0 || ^3.3.0
+ peerDependenciesMeta:
+ '@vue/composition-api':
+ optional: true
+
+ '@tootallnate/once@1.1.2':
+ resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==}
+ engines: {node: '>= 6'}
+
+ '@trivago/prettier-plugin-sort-imports@5.2.1':
+ resolution: {integrity: sha512-NDZndt0fmVThIx/8cExuJHLZagUVzfGCoVrwH9x6aZvwfBdkrDFTYujecek6X2WpG4uUFsVaPg5+aNQPSyjcmw==}
+ engines: {node: '>18.12'}
+ peerDependencies:
+ '@vue/compiler-sfc': 3.x
+ prettier: 2.x - 3.x
+ prettier-plugin-svelte: 3.x
+ svelte: 4.x || 5.x
+ peerDependenciesMeta:
+ '@vue/compiler-sfc':
+ optional: true
+ prettier-plugin-svelte:
+ optional: true
+ svelte:
+ optional: true
+
+ '@tybys/wasm-util@0.9.0':
+ resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==}
+
+ '@types/babel__core@7.20.5':
+ resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
+
+ '@types/babel__generator@7.27.0':
+ resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==}
+
+ '@types/babel__template@7.4.4':
+ resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
+
+ '@types/babel__traverse@7.20.7':
+ resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==}
+
+ '@types/cacheable-request@6.0.3':
+ resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==}
+
+ '@types/conventional-commits-parser@5.0.1':
+ resolution: {integrity: sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==}
+
+ '@types/debug@4.1.12':
+ resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
+
+ '@types/estree@1.0.6':
+ resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
+
+ '@types/estree@1.0.7':
+ resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
+
+ '@types/estree@1.0.8':
+ resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
+ '@types/graceful-fs@4.1.9':
+ resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==}
+
+ '@types/http-cache-semantics@4.0.4':
+ resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==}
+
+ '@types/istanbul-lib-coverage@2.0.6':
+ resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
+
+ '@types/istanbul-lib-report@3.0.3':
+ resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==}
+
+ '@types/istanbul-reports@3.0.4':
+ resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==}
+
+ '@types/json-schema@7.0.15':
+ resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
+ '@types/keyv@3.1.4':
+ resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
+
+ '@types/mdast@4.0.4':
+ resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
+
+ '@types/ms@2.1.0':
+ resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
+
+ '@types/node@20.19.0':
+ resolution: {integrity: sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q==}
+
+ '@types/prettier@2.7.3':
+ resolution: {integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==}
+
+ '@types/responselike@1.0.3':
+ resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==}
+
+ '@types/stack-utils@2.0.3':
+ resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==}
+
+ '@types/unist@3.0.3':
+ resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
+
+ '@types/wechat-miniprogram@3.4.8':
+ resolution: {integrity: sha512-lOqddA707X3SZxVhlZNBZPzYzj7lG/ED2fF30k+aE2oT6R4wfw0Wiup2k2hGrmzYUFyJXfGs01sDCuJMhQMAdg==}
+
+ '@types/yargs-parser@21.0.3':
+ resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==}
+
+ '@types/yargs@16.0.9':
+ resolution: {integrity: sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==}
+
+ '@typescript-eslint/eslint-plugin@8.34.1':
+ resolution: {integrity: sha512-STXcN6ebF6li4PxwNeFnqF8/2BNDvBupf2OPx2yWNzr6mKNGF7q49VM00Pz5FaomJyqvbXpY6PhO+T9w139YEQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ '@typescript-eslint/parser': ^8.34.1
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <5.9.0'
+
+ '@typescript-eslint/parser@8.34.1':
+ resolution: {integrity: sha512-4O3idHxhyzjClSMJ0a29AcoK0+YwnEqzI6oz3vlRf3xw0zbzt15MzXwItOlnr5nIth6zlY2RENLsOPvhyrKAQA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <5.9.0'
+
+ '@typescript-eslint/project-service@8.34.1':
+ resolution: {integrity: sha512-nuHlOmFZfuRwLJKDGQOVc0xnQrAmuq1Mj/ISou5044y1ajGNp2BNliIqp7F2LPQ5sForz8lempMFCovfeS1XoA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <5.9.0'
+
+ '@typescript-eslint/scope-manager@8.34.1':
+ resolution: {integrity: sha512-beu6o6QY4hJAgL1E8RaXNC071G4Kso2MGmJskCFQhRhg8VOH/FDbC8soP8NHN7e/Hdphwp8G8cE6OBzC8o41ZA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/tsconfig-utils@8.34.1':
+ resolution: {integrity: sha512-K4Sjdo4/xF9NEeA2khOb7Y5nY6NSXBnod87uniVYW9kHP+hNlDV8trUSFeynA2uxWam4gIWgWoygPrv9VMWrYg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <5.9.0'
+
+ '@typescript-eslint/type-utils@8.34.1':
+ resolution: {integrity: sha512-Tv7tCCr6e5m8hP4+xFugcrwTOucB8lshffJ6zf1mF1TbU67R+ntCc6DzLNKM+s/uzDyv8gLq7tufaAhIBYeV8g==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <5.9.0'
+
+ '@typescript-eslint/types@8.34.1':
+ resolution: {integrity: sha512-rjLVbmE7HR18kDsjNIZQHxmv9RZwlgzavryL5Lnj2ujIRTeXlKtILHgRNmQ3j4daw7zd+mQgy+uyt6Zo6I0IGA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/typescript-estree@8.34.1':
+ resolution: {integrity: sha512-rjCNqqYPuMUF5ODD+hWBNmOitjBWghkGKJg6hiCHzUvXRy6rK22Jd3rwbP2Xi+R7oYVvIKhokHVhH41BxPV5mA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <5.9.0'
+
+ '@typescript-eslint/utils@8.34.1':
+ resolution: {integrity: sha512-mqOwUdZ3KjtGk7xJJnLbHxTuWVn3GO2WZZuM+Slhkun4+qthLdXx32C8xIXbO1kfCECb3jIs3eoxK3eryk7aoQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <5.9.0'
+
+ '@typescript-eslint/visitor-keys@8.34.1':
+ resolution: {integrity: sha512-xoh5rJ+tgsRKoXnkBPFRLZ7rjKM0AfVbC68UZ/ECXoDbfggb9RbEySN359acY1vS3qZ0jVTVWzbtfapwm5ztxw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@uni-helper/eslint-config@0.4.0':
+ resolution: {integrity: sha512-8Cbe4Ortvxs9A708LWN5gyZoUx4TpjPQwusL7tPmRLeSgKvwMppeRq3pDckhXiFXXT2RTdn3LKE8SH5gMPD8fg==}
+ peerDependencies:
+ '@antfu/eslint-config': ^4.0.1
+ eslint: ^9.10.0
+
+ '@uni-helper/uni-app-types@1.0.0-alpha.3':
+ resolution: {integrity: sha512-lhSpVroEUxzre1vzKAckg85CFLUpn6vw9L2FDO1d8cd9JtMstlXkYmIn/Oz8tqzXxQ1sOgd3YcwoQmkXsw+Oxw==}
+ engines: {node: '>=14.18'}
+ peerDependencies:
+ typescript: ^5.5.4
+ vue: ^3.4.21
+
+ '@uni-helper/uni-cloud-types@1.0.0-alpha.3':
+ resolution: {integrity: sha512-rFyWXR2C3XQ59MzLvhSFBV0jFzY6NvZ7KKxTsOB1eVkt9VBFXiCHGr3tE8X5yG9ZslUdye1W+whZ25d8RhmQTQ==}
+ engines: {node: '>=14.18'}
+ peerDependencies:
+ typescript: ^5.5.4
+ vue: ^3.4.21
+
+ '@uni-helper/uni-env@0.0.3':
+ resolution: {integrity: sha512-K6MEnmN7Dg+NnEkfUUhjaKc/rPVY7tcGdsVUKOlC1/z2E6H6zjSSTdJg8z+sVJtZ03Ff1G/MHz2PYDyAS6gjQQ==}
+
+ '@uni-helper/uni-env@0.1.7':
+ resolution: {integrity: sha512-vxWnscbm1VgmK4ZzqN721mY0mIIVCeJutA32fH9P29itaYewVfL9VAWZWZi7nHJEssTkzhfdTTfydcnJKxUQzg==}
+
+ '@uni-helper/uni-types@1.0.0-alpha.3':
+ resolution: {integrity: sha512-wFGaFTY9coLZ9us1SlUPxPhOcQd3bvPmvkpi3CvQANLMVVnaqp3WY/vlrr4rZBQgYeePnkdm7rRxnDLPmJS24A==}
+ engines: {node: '>=14.18'}
+ peerDependencies:
+ '@uni-helper/uni-app-types': 1.0.0-alpha.3
+ '@uni-helper/uni-cloud-types': 1.0.0-alpha.3
+ '@uni-helper/uni-ui-types': 1.0.0-alpha.3
+ typescript: ^5.5.4
+ vue: ^3.4.21
+
+ '@uni-helper/uni-ui-types@1.0.0-alpha.3':
+ resolution: {integrity: sha512-4laNtsQOJYFszvFcxMJfLwcruh/Qx2tsKMyG7+9J2ORCUMJ3HnLsfyCkMkqP8Jj3jwHEgN0XzKzueJaCgIAUiA==}
+ engines: {node: '>=14.18'}
+ peerDependencies:
+ '@uni-helper/uni-app-types': 1.0.0-alpha.3
+ typescript: ^5.5.4
+ vue: ^3.4.21
+
+ '@uni-helper/unocss-preset-uni@0.2.11':
+ resolution: {integrity: sha512-/863vV/9y89j0ZzEGSrz+dCsOjxxUpGTNHL7+i/yokhyscmvcH8KPEpcOTfNtbkE5ALyJdNnfN70g2jbpxC3mw==}
+ peerDependencies:
+ '@unocss/preset-legacy-compat': '>=0.58'
+ '@unocss/preset-mini': '>=0.58'
+ '@unocss/rule-utils': '>=0.58'
+ '@unocss/vite': '>=0.58'
+ unocss: '>=0.58'
+ unocss-applet: '>=0.7'
+ peerDependenciesMeta:
+ '@unocss/preset-mini':
+ optional: true
+ '@unocss/vite':
+ optional: true
+
+ '@uni-helper/vite-plugin-uni-components@0.2.0':
+ resolution: {integrity: sha512-h/rV8Z3N+wus/ZviYzkdRePNSUlkndn5H+wVC17ZXmG+2mqmUfLJdGskzrCbgE7Y1TT7u8E9yMz8Ah/RwMf0GQ==}
+
+ '@uni-helper/vite-plugin-uni-layouts@0.1.10':
+ resolution: {integrity: sha512-RJdGmJjZtpKNVfShiKYZrualMxdi+i8uh7zpPG+X3lzf6wyKSJgWwVAj3GUdqeE/QUEncNPmj2sqwuyeLXPxbA==}
+
+ '@uni-helper/vite-plugin-uni-manifest@0.2.8':
+ resolution: {integrity: sha512-6QtUcCTkvpR5GAPUxyKlEtm+KYxOk1RSVfdrxlWYW2q7Iws0D7UeNirA4GMuc/SaayEjURlU7cUrS7NceoPCfQ==}
+ peerDependencies:
+ vite: ^4.0.0 || ^5.0.0 || ^6.0.0
+
+ '@uni-helper/vite-plugin-uni-pages@0.2.28':
+ resolution: {integrity: sha512-WSunH3tWjuw9tVAVOqo/RmkKPsaF97MY5I9FXuTuovs4j5oNgPiot9Ysb6T5RJN2KU2zlBlQi1dO/ZMj9b4BYA==}
+ peerDependencies:
+ vite: ^5.0.0
+
+ '@uni-helper/vite-plugin-uni-platform@0.0.4':
+ resolution: {integrity: sha512-aQnhpiLpxlxr6C2i6FsEbY6aFJH1igZUC5DsqoDscD/dBnpCPnovW9bFL/ig8qHsq87iAnqwhZtEpsLrzgrDaw==}
+
+ '@uni-ku/bundle-optimizer@1.3.3':
+ resolution: {integrity: sha512-/E7bxVH3Iu/ul8GOxNW18mvQ9ccuQ/HotfoXkHfNZhP346o6cHIn4lbrGTDw10sOfg47KR9zks6hjf5R6lywpw==}
+ peerDependencies:
+ vite: ^4.0.0 || ^5.0.0
+
+ '@unocss-applet/preset-applet@0.10.0':
+ resolution: {integrity: sha512-bN6jaZo0WMQRvKVDisUXAF4Ig2CVDMe8rk6rZDxzWNFZiReMYK97SxS/JTEFAknWSu4C+GYtut+57NPD2PqHrg==}
+ peerDependencies:
+ '@unocss/core': '>=66.0.0'
+ '@unocss/preset-mini': '>=66.0.0'
+ '@unocss/preset-wind3': '>=66.0.0'
+
+ '@unocss-applet/preset-rem-rpx@0.10.0':
+ resolution: {integrity: sha512-l4bLD6RxOqpX+5YJi8a0ntgWjUWWMYZh9gNeMCX337YBi/1qZHjnn96D+K6gny0ECBFq1pa9jvlWY0MismvXGQ==}
+ peerDependencies:
+ '@unocss/core': '>=66.0.0'
+
+ '@unocss-applet/transformer-attributify@0.10.0':
+ resolution: {integrity: sha512-T1h3Zu/KHbMpXl5qSqeAuaGovmtmxX4YsDrFM173N3Y8VxDOQhrA2IzNYotftSkLLZeqKmnqr3g2M24ansdnjw==}
+ peerDependencies:
+ '@unocss/core': '>=66.0.0'
+
+ '@unocss/astro@65.4.2':
+ resolution: {integrity: sha512-5UR8KmonbpeeSG5pxWtBYlwr9XNbcsrVTfXKzWZAic5kRUWQREFpmjMcDL/+Co+OYWe4z5WWayjwfQUmLLPG5w==}
+ peerDependencies:
+ vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0
+ peerDependenciesMeta:
+ vite:
+ optional: true
+
+ '@unocss/cli@65.4.2':
+ resolution: {integrity: sha512-eQmzBhph67CDe+MNz8k/3rMwR1g1xCKZIWfrFoSQv7CqlJxgjRgFOxmnIw7pGhUEC0wenrvauGkVjFkIUHFqeg==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ '@unocss/config@65.4.2':
+ resolution: {integrity: sha512-2xyWDt6t879rsdxJ0TiRbk9ENkGuLjAWjl3aAph5HHxwgGaBN1c9S5G9607j/WXP+tO6c37B4Q1iG/XYx35HBA==}
+ engines: {node: '>=14'}
+
+ '@unocss/config@66.1.2':
+ resolution: {integrity: sha512-2sQXj+Qaq4RVDELVTPoXMggZ30g1WKHeCuur396I12Ab0HgAR6bTc/DIrNtqKVHFI3mmlvP1oM1ynhKWSKPsTg==}
+ engines: {node: '>=14'}
+
+ '@unocss/config@66.2.3':
+ resolution: {integrity: sha512-Xd2P1NgD7poYKDMI176yPvmMojBbc1HH26dgG8OYpNiBuzdhdLuyfSLPbpAOXPSyoK8Fu2BikS+NLTWQ8MmgFQ==}
+ engines: {node: '>=14'}
+
+ '@unocss/core@0.59.4':
+ resolution: {integrity: sha512-bBZ1sgcAtezQVZ1BST9IS3jqcsTLyqKNjiIf7FTnX3DHpfpYuMDFzSOtmkZDzBleOLO/CtcRWjT0HwTSQAmV0A==}
+
+ '@unocss/core@65.4.2':
+ resolution: {integrity: sha512-VmXy5D25por+pt9LBlKZ3gk4rOE5ldm80MyVOEnLcpaFb9LqB0g/8qUU9/Dk3TSA+ZPeoGm53Juo0p8LMFIigA==}
+
+ '@unocss/core@66.1.2':
+ resolution: {integrity: sha512-mN9h1hHEuhDcdbI4z74o7UnxlBZYVsJpYcdC1YLWBKROcLYTkuyZ7hgBzpo1FBNox2Bt3JnrSinVDmc44Bxjow==}
+
+ '@unocss/core@66.2.0':
+ resolution: {integrity: sha512-jq+UPvmf271MjY/RoREBmjSCzTYdjzdlgBcjmtymYjBRg7a6a0GiSuhdL0D20cwQ4MoBvlO1tIzgCqnqImACBg==}
+
+ '@unocss/core@66.2.3':
+ resolution: {integrity: sha512-mRpwm3HUAqCtQrBB7+vkCp0qDZT+G1s2EAVJIF6zzrnoqb2BmIlHgz7DXVDufRFMRJtQUCp2jVMtEQZCTXEVjQ==}
+
+ '@unocss/eslint-plugin@66.2.3':
+ resolution: {integrity: sha512-HULjzcEsfbcHWzTMOkJbsZ90Y/gCyUW3QGepj4p4IMv8iYRvpeoBq0vYknu08rOBZjSH+ONVyIGIRbp/J9kD2Q==}
+ engines: {node: '>=14'}
+
+ '@unocss/extractor-arbitrary-variants@65.4.2':
+ resolution: {integrity: sha512-qm5JXfjbxgXqhQAeOfV1jFT1ThBTi1bP1m+Nu2p6tB9EUbAUp+AKY4sODueqDXoriUtOc7h0QzyW3Lm+s3fTGw==}
+
+ '@unocss/extractor-arbitrary-variants@66.1.2':
+ resolution: {integrity: sha512-F570wH9VYeFTb4r8qgcbN5QpEVIAvFC1zOnrAPUr6B6kbU2YChMXxHP7PHK0AzLHnEr458Pwpzl6hmP6bzxZ8g==}
+
+ '@unocss/extractor-arbitrary-variants@66.2.0':
+ resolution: {integrity: sha512-tRvWLbLLZweCv+eujbkvjflNclbkrJHhW2asuBUcaHXCzXIHCYgbBdF3lV5ww1lBBfEUWekgyFC/9fbB4rh1fg==}
+
+ '@unocss/inspector@65.4.2':
+ resolution: {integrity: sha512-64m6SpjbeTQNqgWLUQpVwu1WQpuv4RshA1KIs4cc6WdDivckWb4woA+4Zdm+DjYjm0aLqX7oUE0kWwjK+pIZdA==}
+
+ '@unocss/inspector@66.1.2':
+ resolution: {integrity: sha512-ftdZzFP5DAKDzgBI078xDDZbNNVq1RV/yhpNkviBvWCUsgRWc6o3G8swqJPIvFaphmUms0RIYH9shmXilVXFtA==}
+
+ '@unocss/postcss@65.4.2':
+ resolution: {integrity: sha512-LKb8K9B1zTFN6zaQYIYSiFRz4a+HpNHKSIGjo/dtN0p0R1ME7VaGOgwg3+cEsNguNhTeXoaX5y/ADp8KP/HD6A==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ postcss: ^8.4.21
+
+ '@unocss/preset-attributify@65.4.2':
+ resolution: {integrity: sha512-DHFHU+tvknLMk01cWQjLjEdJkRyPIdVYVoCoR9yzmwwVT6n7JKK1R/Ailwofm2229GjplY5c9qu1PjA5K1K7Vw==}
+
+ '@unocss/preset-icons@65.4.2':
+ resolution: {integrity: sha512-XytFiMSbIqPshiVtsiFoq1nqyFYNTGnxMe6g3w+Zj5B+vNwfwHPR6CD8pTSkXpbW6zo+Ed/CBYe0pRwCrYjcgw==}
+
+ '@unocss/preset-legacy-compat@0.59.4':
+ resolution: {integrity: sha512-vUggTaG8qXNzaLn5tGzQ+7VzYTjH3sjaN+wVV5I9XojgoiBGmyuyw0xuSVBmiQmxvdGV3KPUBZ5td///hprW8w==}
+
+ '@unocss/preset-mini@65.4.2':
+ resolution: {integrity: sha512-4ZZK9KwDHjI8wFUKeB+30GHekPmy1OzXncjlXhqm+vNQ7FO3xCee7VY00E5bgz5Tt0pXALcKFlrEspjpSaeCoQ==}
+
+ '@unocss/preset-mini@66.1.2':
+ resolution: {integrity: sha512-oiDe+VhwZ8B5Z0UGfggtOwgpRZMLtH1RTDFvmJmJEXYYX5BPWknS6wYcQzxy0i/y9ym0xp2QnEaTpGmR7LKdkg==}
+
+ '@unocss/preset-tagify@65.4.2':
+ resolution: {integrity: sha512-Ldk2QU8Zmy9irQQ/ZlFLBTPfPTHDBXcbxsxCFFCjI/MiTMh1Wac6cpryoYlju5SJ+yQuys4sIKzjkhRUD2d7Ug==}
+
+ '@unocss/preset-typography@65.4.2':
+ resolution: {integrity: sha512-Mp3GSS24qP8Fdf3wH2UeM4PRqGOuJGvKMNuZsIPX09Y/HcpleyjolBDNiagaM2rdp5cs2jcvWDAJ2fz9OXR3HQ==}
+
+ '@unocss/preset-uno@65.4.2':
+ resolution: {integrity: sha512-rJcGx/+EWA3wXGOAZdYQFSEn8knsiqiST/Ji1adN+9dTq4BVYMZ9n3zYRF6GZ8p61aZomhU4jmzpLk12RMdxpg==}
+
+ '@unocss/preset-web-fonts@65.4.2':
+ resolution: {integrity: sha512-YVgjB3igldU5uKgwkDXNvxe9cISiLBnaKwW2bjJJyyTZ3QbjbYklgI4LyakxtnY4hf6U1vO+1W83/GIhl2RAdw==}
+
+ '@unocss/preset-wind3@66.1.2':
+ resolution: {integrity: sha512-S09imGOngAAOXCBCHb3JAtxD1/L7nDWrgEeX6NT0ElDp3X1T6XxUXYJlpjCfcqV/klMoXyYouKvp0YuG9QSgVg==}
+
+ '@unocss/preset-wind4@66.2.0':
+ resolution: {integrity: sha512-+qllvO142kajxD9EFag1WmiCJOjIxoMTVh1sW30SFXftB6rvVCaA8GbDo/Jnk9gXtmRQ2L9ZnuxPIx8W65pBjA==}
+
+ '@unocss/preset-wind@65.4.2':
+ resolution: {integrity: sha512-TQm9P2UHpqfn92APfZJtbK2brkXQ+GInFL2evup/ZChU1fqdbH9mL0ef6ZNQbCH4gjY6mEzwPXt4lhGod6CajA==}
+
+ '@unocss/reset@65.4.2':
+ resolution: {integrity: sha512-Sas0lTGEgzdWKafSiT+dyhhrUOkOpPbhJYbPMgjW6Ol/tB5JXhdlCNm90Xue1wt453P8O3J4v+dQcyrdRSq0Ig==}
+
+ '@unocss/rule-utils@65.4.2':
+ resolution: {integrity: sha512-OdMSJZiZUr8XmLo3Bz3Wrw1nZLT1nTPnPOV8gdi4vZ+2RgCChua9o8Dz4IyeQ7mMhLXoqHIUpJ7jE5Nv+Uz1Fw==}
+ engines: {node: '>=14'}
+
+ '@unocss/rule-utils@66.1.2':
+ resolution: {integrity: sha512-nn0ehvDh7yyWq2mcBDLVpmMAivjRATUroZ8ETinyN1rmfsGesm71R0d1gV3K+Z6YC7a3+dMLc+/qzI7VK3AG/Q==}
+ engines: {node: '>=14'}
+
+ '@unocss/rule-utils@66.2.0':
+ resolution: {integrity: sha512-T2Gg8WwLeCi7QaOss6EKC5Ypmbls+dqet5sMRsiIE5Mi04j3ndS0+lTwV/x7X6NnN6kCoh0IlwCdBUU4tkJbkQ==}
+ engines: {node: '>=14'}
+
+ '@unocss/rule-utils@66.2.3':
+ resolution: {integrity: sha512-OL4s4pTb/YD66OK2b7NavwSqGTn5cDBaafkLphqnXKe9/DUfGoWNmeUAHfSzvM1QelprRXtPNLK/GIfUjcsNMg==}
+ engines: {node: '>=14'}
+
+ '@unocss/transformer-attributify-jsx@65.4.2':
+ resolution: {integrity: sha512-3jANN8pnOd3xX8PhkUMhRYEwT97HOYNLMiACpRb2x3MRxYjmpOPR/We31r/tYz23hMsbGkR0C5xvpuCkDj2QAA==}
+
+ '@unocss/transformer-compile-class@65.4.2':
+ resolution: {integrity: sha512-9kRV7W6LA7D+OBfbA2ayKJyZl9RnOb1mv+XW0KcFdD8KP0r8DfxT5rOj6QpEt88d6KwDo09iOaExsy0rHU7ZTg==}
+
+ '@unocss/transformer-directives@65.4.2':
+ resolution: {integrity: sha512-u/hbpRe/mEasRdzznGQnKmJqDHcoJ2MJJBLpDc2RisiAEokz73dno3JtT70HZVA+DN7Y9ddAKioxlvSU+iJxFA==}
+
+ '@unocss/transformer-variant-group@65.4.2':
+ resolution: {integrity: sha512-a5xjR9mPUo7n6wD3nO5tcEcH7j0ks25E3d100XdNUeVUJeszzMAeLZ/uYrkd6Z3amyLLxwVOkAdYcczGhKdsbA==}
+
+ '@unocss/vite@65.4.2':
+ resolution: {integrity: sha512-pEIU/egxec0CErgUwo/Nuyfi+ZZPIBD+XQBi2Pa51VKeuD91BBnXc1JGu9yzRT2WbrGP3hwsDgYqhj2G6wGXyA==}
+ peerDependencies:
+ vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0
+
+ '@unocss/vite@66.1.2':
+ resolution: {integrity: sha512-ZJHN8+HKSrclVjT/+S7Vh2t59DK8J44d5nLZPG1Goua7uNK8yYJeOLK2sCGX7aackRer1ZynmglFFzxNFVt+IA==}
+ peerDependencies:
+ vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0
+
+ '@vitejs/plugin-legacy@5.3.2':
+ resolution: {integrity: sha512-8moCOrIMaZ/Rjln0Q6GsH6s8fAt1JOI3k8nmfX4tXUxE5KAExVctSyOBk+A25GClsdSWqIk2yaUthH3KJ2X4tg==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ peerDependencies:
+ terser: ^5.4.0
+ vite: ^5.0.0
+
+ '@vitejs/plugin-vue-jsx@3.1.0':
+ resolution: {integrity: sha512-w9M6F3LSEU5kszVb9An2/MmXNxocAnUb3WhRr8bHlimhDrXNt6n6D2nJQR3UXpGlZHh/EsgouOHCsM8V3Ln+WA==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ vite: ^4.0.0 || ^5.0.0
+ vue: ^3.0.0
+
+ '@vitejs/plugin-vue@5.1.0':
+ resolution: {integrity: sha512-QMRxARyrdiwi1mj3AW4fLByoHTavreXq0itdEW696EihXglf1MB3D4C2gBvE0jMPH29ZjC3iK8aIaUMLf4EOGA==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ peerDependencies:
+ vite: ^5.0.0
+ vue: ^3.2.25
+
+ '@vitest/eslint-plugin@1.2.7':
+ resolution: {integrity: sha512-7WHcGZo6uXsE4SsSnpGDqKyGrd6NfOMM52WKoHSpTRZLbjMuDyHfA5P7m8yrr73tpqYjsiAdSjSerOnx8uEhpA==}
+ peerDependencies:
+ eslint: '>= 8.57.0'
+ typescript: '>= 5.0.0'
+ vitest: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ vitest:
+ optional: true
+
+ '@volar/language-core@2.4.14':
+ resolution: {integrity: sha512-X6beusV0DvuVseaOEy7GoagS4rYHgDHnTrdOj5jeUb49fW5ceQyP9Ej5rBhqgz2wJggl+2fDbbojq1XKaxDi6w==}
+
+ '@volar/source-map@2.4.14':
+ resolution: {integrity: sha512-5TeKKMh7Sfxo8021cJfmBzcjfY1SsXsPMMjMvjY7ivesdnybqqS+GxGAoXHAOUawQTwtdUxgP65Im+dEmvWtYQ==}
+
+ '@volar/typescript@2.4.14':
+ resolution: {integrity: sha512-p8Z6f/bZM3/HyCdRNFZOEEzts51uV8WHeN8Tnfnm2EBv6FDB2TQLzfVx7aJvnl8ofKAOnS64B2O8bImBFaauRw==}
+
+ '@vue/babel-helper-vue-transform-on@1.2.5':
+ resolution: {integrity: sha512-lOz4t39ZdmU4DJAa2hwPYmKc8EsuGa2U0L9KaZaOJUt0UwQNjNA3AZTq6uEivhOKhhG1Wvy96SvYBoFmCg3uuw==}
+
+ '@vue/babel-plugin-jsx@1.2.5':
+ resolution: {integrity: sha512-zTrNmOd4939H9KsRIGmmzn3q2zvv1mjxkYZHgqHZgDrXz5B1Q3WyGEjO2f+JrmKghvl1JIRcvo63LgM1kH5zFg==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ peerDependenciesMeta:
+ '@babel/core':
+ optional: true
+
+ '@vue/babel-plugin-resolve-type@1.2.5':
+ resolution: {integrity: sha512-U/ibkQrf5sx0XXRnUZD1mo5F7PkpKyTbfXM3a3rC4YnUz6crHEz9Jg09jzzL6QYlXNto/9CePdOg/c87O4Nlfg==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@vue/compiler-core@3.4.21':
+ resolution: {integrity: sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==}
+
+ '@vue/compiler-core@3.5.15':
+ resolution: {integrity: sha512-nGRc6YJg/kxNqbv/7Tg4juirPnjHvuVdhcmDvQWVZXlLHjouq7VsKmV1hIxM/8yKM0VUfwT/Uzc0lO510ltZqw==}
+
+ '@vue/compiler-dom@3.4.21':
+ resolution: {integrity: sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==}
+
+ '@vue/compiler-dom@3.5.15':
+ resolution: {integrity: sha512-ZelQd9n+O/UCBdL00rlwCrsArSak+YLZpBVuNDio1hN3+wrCshYZEDUO3khSLAzPbF1oQS2duEoMDUHScUlYjA==}
+
+ '@vue/compiler-sfc@3.4.21':
+ resolution: {integrity: sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==}
+
+ '@vue/compiler-sfc@3.5.15':
+ resolution: {integrity: sha512-3zndKbxMsOU6afQWer75Zot/aydjtxNj0T2KLg033rAFaQUn2PGuE32ZRe4iMhflbTcAxL0yEYsRWFxtPro8RQ==}
+
+ '@vue/compiler-ssr@3.4.21':
+ resolution: {integrity: sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==}
+
+ '@vue/compiler-ssr@3.5.15':
+ resolution: {integrity: sha512-gShn8zRREZbrXqTtmLSCffgZXDWv8nHc/GhsW+mbwBfNZL5pI96e7IWcIq8XGQe1TLtVbu7EV9gFIVSmfyarPg==}
+
+ '@vue/compiler-vue2@2.7.16':
+ resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
+
+ '@vue/consolidate@1.0.0':
+ resolution: {integrity: sha512-oTyUE+QHIzLw2PpV14GD/c7EohDyP64xCniWTcqcEmTd699eFqTIwOmtDYjcO1j3QgdXoJEoWv1/cCdLrRoOfg==}
+ engines: {node: '>= 0.12.0'}
+
+ '@vue/devtools-api@6.6.4':
+ resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
+
+ '@vue/language-core@2.2.10':
+ resolution: {integrity: sha512-+yNoYx6XIKuAO8Mqh1vGytu8jkFEOH5C8iOv3i8Z/65A7x9iAOXA97Q+PqZ3nlm2lxf5rOJuIGI/wDtx/riNYw==}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@vue/reactivity@3.5.15':
+ resolution: {integrity: sha512-GaA5VUm30YWobCwpvcs9nvFKf27EdSLKDo2jA0IXzGS344oNpFNbEQ9z+Pp5ESDaxyS8FcH0vFN/XSe95BZtHQ==}
+
+ '@vue/runtime-core@3.5.15':
+ resolution: {integrity: sha512-CZAlIOQ93nj0OPpWWOx4+QDLCMzBNY85IQR4Voe6vIID149yF8g9WQaWnw042f/6JfvLttK7dnyWlC1EVCRK8Q==}
+
+ '@vue/runtime-dom@3.5.15':
+ resolution: {integrity: sha512-wFplHKzKO/v998up2iCW3RN9TNUeDMhdBcNYZgs5LOokHntrB48dyuZHspcahKZczKKh3v6i164gapMPxBTKNw==}
+
+ '@vue/server-renderer@3.4.21':
+ resolution: {integrity: sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==}
+ peerDependencies:
+ vue: 3.4.21
+
+ '@vue/server-renderer@3.5.15':
+ resolution: {integrity: sha512-Gehc693kVTYkLt6QSYEjGvqvdK2zZ/gf/D5zkgmvBdeB30dNnVZS8yY7+IlBmHRd1rR/zwaqeu06Ij04ZxBscg==}
+ peerDependencies:
+ vue: 3.5.15
+
+ '@vue/shared@3.4.21':
+ resolution: {integrity: sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==}
+
+ '@vue/shared@3.5.15':
+ resolution: {integrity: sha512-bKvgFJJL1ZX9KxMCTQY6xD9Dhe3nusd1OhyOb1cJYGqvAr0Vg8FIjHPMOEVbJ9GDT9HG+Bjdn4oS8ohKP8EvoA==}
+
+ '@vue/tsconfig@0.1.3':
+ resolution: {integrity: sha512-kQVsh8yyWPvHpb8gIc9l/HIDiiVUy1amynLNpCy8p+FoCiZXCo6fQos5/097MmnNZc9AtseDsCrfkhqCrJ8Olg==}
+ peerDependencies:
+ '@types/node': '*'
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+
+ JSONStream@1.3.5:
+ resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==}
+ hasBin: true
+
+ a-sync-waterfall@1.0.1:
+ resolution: {integrity: sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==}
+
+ abab@2.0.6:
+ resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
+ deprecated: Use your platform's native atob() and btoa() methods instead
+
+ abortcontroller-polyfill@1.7.8:
+ resolution: {integrity: sha512-9f1iZ2uWh92VcrU9Y8x+LdM4DLj75VE0MJB8zuF1iUnroEptStw+DQ8EQPMUdfe5k+PkB1uUfDQfWbhstH8LrQ==}
+
+ accepts@1.3.8:
+ resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
+ engines: {node: '>= 0.6'}
+
+ acorn-globals@6.0.0:
+ resolution: {integrity: sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==}
+
+ acorn-jsx@5.3.2:
+ resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+ peerDependencies:
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+ acorn-walk@7.2.0:
+ resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==}
+ engines: {node: '>=0.4.0'}
+
+ acorn@7.4.1:
+ resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ acorn@8.14.0:
+ resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ acorn@8.14.1:
+ resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ acorn@8.15.0:
+ resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ address@1.2.2:
+ resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==}
+ engines: {node: '>= 10.0.0'}
+
+ adm-zip@0.5.16:
+ resolution: {integrity: sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==}
+ engines: {node: '>=12.0'}
+
+ agent-base@6.0.2:
+ resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
+ engines: {node: '>= 6.0.0'}
+
+ ajv@6.12.6:
+ resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+
+ ajv@8.17.1:
+ resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
+
+ alien-signals@1.0.13:
+ resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==}
+
+ alova@3.3.3:
+ resolution: {integrity: sha512-DB1t2qpQUERVaymz+u9bS71kcN5PzZ+G5LawHm9grabv++tfHOeYsKaW+LCpgMIJ/V7lQqFyqwOvTHGDph43Jw==}
+ engines: {node: '>= 18.0.0'}
+
+ android-versions@1.9.0:
+ resolution: {integrity: sha512-13O2B6PQMEM4ej9n13ePRQeckrCoKbZrvuzlLvK+9s2QmncpHDbYzZxhgapN32sJNoifN6VAHexLnd/6CYrs7Q==}
+
+ ansi-escapes@4.3.2:
+ resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
+ engines: {node: '>=8'}
+
+ ansi-escapes@7.0.0:
+ resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==}
+ engines: {node: '>=18'}
+
+ ansi-regex@5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+
+ ansi-regex@6.1.0:
+ resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
+ engines: {node: '>=12'}
+
+ ansi-styles@4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+
+ ansi-styles@5.2.0:
+ resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
+ engines: {node: '>=10'}
+
+ ansi-styles@6.2.1:
+ resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
+ engines: {node: '>=12'}
+
+ ansis@4.1.0:
+ resolution: {integrity: sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==}
+ engines: {node: '>=14'}
+
+ any-base@1.1.0:
+ resolution: {integrity: sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==}
+
+ anymatch@3.1.3:
+ resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+ engines: {node: '>= 8'}
+
+ are-docs-informative@0.0.2:
+ resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==}
+ engines: {node: '>=14'}
+
+ argparse@1.0.10:
+ resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
+
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+ array-flatten@1.1.1:
+ resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==}
+
+ array-ify@1.0.0:
+ resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==}
+
+ asap@2.0.6:
+ resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
+
+ ast-kit@0.11.3:
+ resolution: {integrity: sha512-qdwwKEhckRk0XE22/xDdmU3v/60E8Edu4qFhgTLIhGGDs/PAJwLw9pQn8Rj99PitlbBZbYpx0k/lbir4kg0SuA==}
+ engines: {node: '>=16.14.0'}
+
+ asynckit@0.4.0:
+ resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+
+ autoprefixer@10.4.20:
+ resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==}
+ engines: {node: ^10 || ^12 || >=14}
+ hasBin: true
+ peerDependencies:
+ postcss: ^8.1.0
+
+ axios@1.7.9:
+ resolution: {integrity: sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==}
+
+ babel-jest@27.5.1:
+ resolution: {integrity: sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+ peerDependencies:
+ '@babel/core': ^7.8.0
+
+ babel-plugin-istanbul@6.1.1:
+ resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==}
+ engines: {node: '>=8'}
+
+ babel-plugin-jest-hoist@27.5.1:
+ resolution: {integrity: sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ babel-plugin-polyfill-corejs2@0.4.12:
+ resolution: {integrity: sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==}
+ peerDependencies:
+ '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+
+ babel-plugin-polyfill-corejs3@0.10.6:
+ resolution: {integrity: sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==}
+ peerDependencies:
+ '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+
+ babel-plugin-polyfill-regenerator@0.6.3:
+ resolution: {integrity: sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==}
+ peerDependencies:
+ '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+
+ babel-preset-current-node-syntax@1.1.0:
+ resolution: {integrity: sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ babel-preset-jest@27.5.1:
+ resolution: {integrity: sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ base64-js@1.5.1:
+ resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+
+ base64url@3.0.1:
+ resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==}
+ engines: {node: '>=6.0.0'}
+
+ binary-extensions@2.3.0:
+ resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
+ engines: {node: '>=8'}
+
+ bing-translate-api@4.0.2:
+ resolution: {integrity: sha512-JJ8XUehnxzOhHU91oy86xEtp8OOMjVEjCZJX042fKxoO19NNvxJ5omeCcxQNFoPbDqVpBJwqiGVquL0oPdQm1Q==}
+
+ bl@4.1.0:
+ resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
+
+ bmp-js@0.1.0:
+ resolution: {integrity: sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==}
+
+ body-parser@1.20.3:
+ resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==}
+ engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+
+ boolbase@1.0.0:
+ resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+
+ brace-expansion@1.1.12:
+ resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
+
+ brace-expansion@2.0.1:
+ resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
+
+ braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+ engines: {node: '>=8'}
+
+ browser-process-hrtime@1.0.0:
+ resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==}
+
+ browserslist-to-esbuild@2.1.1:
+ resolution: {integrity: sha512-KN+mty6C3e9AN8Z5dI1xeN15ExcRNeISoC3g7V0Kax/MMF9MSoYA2G7lkTTcVUFntiEjkpI0HNgqJC1NjdyNUw==}
+ engines: {node: '>=18'}
+ hasBin: true
+ peerDependencies:
+ browserslist: '*'
+
+ browserslist@4.24.2:
+ resolution: {integrity: sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ browserslist@4.25.0:
+ resolution: {integrity: sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ bser@2.1.1:
+ resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
+
+ buffer-equal@0.0.1:
+ resolution: {integrity: sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==}
+ engines: {node: '>=0.4.0'}
+
+ buffer-from@1.1.2:
+ resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+
+ buffer@5.7.1:
+ resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
+
+ builtin-modules@5.0.0:
+ resolution: {integrity: sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==}
+ engines: {node: '>=18.20'}
+
+ bundle-require@5.1.0:
+ resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ peerDependencies:
+ esbuild: '>=0.18'
+
+ bytes@3.1.2:
+ resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
+ engines: {node: '>= 0.8'}
+
+ c12@1.11.2:
+ resolution: {integrity: sha512-oBs8a4uvSDO9dm8b7OCFW7+dgtVrwmwnrVXYzLm43ta7ep2jCn/0MhoUFygIWtxhyy6+/MG7/agvpY0U1Iemew==}
+ peerDependencies:
+ magicast: ^0.3.4
+ peerDependenciesMeta:
+ magicast:
+ optional: true
+
+ c12@2.0.4:
+ resolution: {integrity: sha512-3DbbhnFt0fKJHxU4tEUPmD1ahWE4PWPMomqfYsTJdrhpmEnRKJi3qSC4rO5U6E6zN1+pjBY7+z8fUmNRMaVKLw==}
+ peerDependencies:
+ magicast: ^0.3.5
+ peerDependenciesMeta:
+ magicast:
+ optional: true
+
+ cac@6.7.14:
+ resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
+ engines: {node: '>=8'}
+
+ cac@6.7.9:
+ resolution: {integrity: sha512-XN5qEpfNQCJ8jRaZgitSkkukjMRCGio+X3Ks5KUbGGlPbV+pSem1l9VuzooCBXOiMFshUZgyYqg6rgN8rjkb/w==}
+ engines: {node: '>=8'}
+
+ cacheable-lookup@5.0.4:
+ resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==}
+ engines: {node: '>=10.6.0'}
+
+ cacheable-request@7.0.4:
+ resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==}
+ engines: {node: '>=8'}
+
+ call-bind-apply-helpers@1.0.2:
+ resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
+ engines: {node: '>= 0.4'}
+
+ call-bind@1.0.7:
+ resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
+ engines: {node: '>= 0.4'}
+
+ call-me-maybe@1.0.2:
+ resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==}
+
+ callsites@3.1.0:
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+ engines: {node: '>=6'}
+
+ camelcase@5.3.1:
+ resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
+ engines: {node: '>=6'}
+
+ camelcase@6.3.0:
+ resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
+ engines: {node: '>=10'}
+
+ caniuse-lite@1.0.30001686:
+ resolution: {integrity: sha512-Y7deg0Aergpa24M3qLC5xjNklnKnhsmSyR/V89dLZ1n0ucJIFNs7PgR2Yfa/Zf6W79SbBicgtGxZr2juHkEUIA==}
+
+ caniuse-lite@1.0.30001724:
+ resolution: {integrity: sha512-WqJo7p0TbHDOythNTqYujmaJTvtYRZrjpP8TCvH6Vb9CYJerJNKamKzIWOM4BkQatWj9H2lYulpdAQNBe7QhNA==}
+
+ ccount@2.0.1:
+ resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
+
+ centra@2.7.0:
+ resolution: {integrity: sha512-PbFMgMSrmgx6uxCdm57RUos9Tc3fclMvhLSATYN39XsDV29B89zZ3KA89jmY0vwSGazyU+uerqwa6t+KaodPcg==}
+
+ chalk@4.1.2:
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+ engines: {node: '>=10'}
+
+ chalk@5.3.0:
+ resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==}
+ engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
+
+ char-regex@1.0.2:
+ resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
+ engines: {node: '>=10'}
+
+ character-entities@2.0.2:
+ resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
+
+ chokidar@3.6.0:
+ resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
+ engines: {node: '>= 8.10.0'}
+
+ chokidar@4.0.3:
+ resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
+ engines: {node: '>= 14.16.0'}
+
+ chownr@2.0.0:
+ resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
+ engines: {node: '>=10'}
+
+ ci-info@3.9.0:
+ resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==}
+ engines: {node: '>=8'}
+
+ ci-info@4.2.0:
+ resolution: {integrity: sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==}
+ engines: {node: '>=8'}
+
+ citty@0.1.6:
+ resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==}
+
+ cjs-module-lexer@1.4.3:
+ resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==}
+
+ clean-regexp@1.0.0:
+ resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==}
+ engines: {node: '>=4'}
+
+ cli-cursor@3.1.0:
+ resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
+ engines: {node: '>=8'}
+
+ cli-cursor@5.0.0:
+ resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==}
+ engines: {node: '>=18'}
+
+ cli-spinners@2.9.2:
+ resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==}
+ engines: {node: '>=6'}
+
+ cli-truncate@4.0.0:
+ resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==}
+ engines: {node: '>=18'}
+
+ cliui@7.0.4:
+ resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
+
+ cliui@8.0.1:
+ resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
+ engines: {node: '>=12'}
+
+ clone-response@1.0.3:
+ resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==}
+
+ clone@1.0.4:
+ resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
+ engines: {node: '>=0.8'}
+
+ co@4.6.0:
+ resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
+ engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
+
+ collect-v8-coverage@1.0.2:
+ resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==}
+
+ color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+
+ color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+ colorette@2.0.20:
+ resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
+
+ colors@1.4.0:
+ resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==}
+ engines: {node: '>=0.1.90'}
+
+ combined-stream@1.0.8:
+ resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+ engines: {node: '>= 0.8'}
+
+ commander@12.1.0:
+ resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==}
+ engines: {node: '>=18'}
+
+ commander@2.20.3:
+ resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
+
+ commander@5.1.0:
+ resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==}
+ engines: {node: '>= 6'}
+
+ comment-parser@1.4.1:
+ resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==}
+ engines: {node: '>= 12.0.0'}
+
+ commondir@1.0.1:
+ resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
+
+ compare-func@2.0.0:
+ resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==}
+
+ compare-versions@3.6.0:
+ resolution: {integrity: sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==}
+
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ confbox@0.1.8:
+ resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
+
+ confbox@0.2.2:
+ resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==}
+
+ consola@3.4.2:
+ resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==}
+ engines: {node: ^14.18.0 || >=16.10.0}
+
+ content-disposition@0.5.4:
+ resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
+ engines: {node: '>= 0.6'}
+
+ content-type@1.0.5:
+ resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
+ engines: {node: '>= 0.6'}
+
+ conventional-changelog-angular@7.0.0:
+ resolution: {integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==}
+ engines: {node: '>=16'}
+
+ conventional-changelog-conventionalcommits@7.0.2:
+ resolution: {integrity: sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==}
+ engines: {node: '>=16'}
+
+ conventional-commits-parser@5.0.0:
+ resolution: {integrity: sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==}
+ engines: {node: '>=16'}
+ hasBin: true
+
+ convert-source-map@1.9.0:
+ resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==}
+
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+ cookie-signature@1.0.6:
+ resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
+
+ cookie@0.7.1:
+ resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==}
+ engines: {node: '>= 0.6'}
+
+ core-js-compat@3.39.0:
+ resolution: {integrity: sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==}
+
+ core-js-compat@3.43.0:
+ resolution: {integrity: sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==}
+
+ core-js@3.39.0:
+ resolution: {integrity: sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==}
+
+ cosmiconfig-typescript-loader@6.1.0:
+ resolution: {integrity: sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g==}
+ engines: {node: '>=v18'}
+ peerDependencies:
+ '@types/node': '*'
+ cosmiconfig: '>=9'
+ typescript: '>=5'
+
+ cosmiconfig@9.0.0:
+ resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ typescript: '>=4.9.5'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ cross-env@7.0.3:
+ resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
+ engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
+ hasBin: true
+
+ cross-spawn@7.0.6:
+ resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+ engines: {node: '>= 8'}
+
+ css-font-size-keywords@1.0.0:
+ resolution: {integrity: sha512-Q+svMDbMlelgCfH/RVDKtTDaf5021O486ZThQPIpahnIjUkMUslC+WuOQSWTgGSrNCH08Y7tYNEmmy0hkfMI8Q==}
+
+ css-font-stretch-keywords@1.0.1:
+ resolution: {integrity: sha512-KmugPO2BNqoyp9zmBIUGwt58UQSfyk1X5DbOlkb2pckDXFSAfjsD5wenb88fNrD6fvS+vu90a/tsPpb9vb0SLg==}
+
+ css-font-style-keywords@1.0.1:
+ resolution: {integrity: sha512-0Fn0aTpcDktnR1RzaBYorIxQily85M2KXRpzmxQPgh8pxUN9Fcn00I8u9I3grNr1QXVgCl9T5Imx0ZwKU973Vg==}
+
+ css-font-weight-keywords@1.0.0:
+ resolution: {integrity: sha512-5So8/NH+oDD+EzsnF4iaG4ZFHQ3vaViePkL1ZbZ5iC/KrsCY+WHq/lvOgrtmuOQ9pBBZ1ADGpaf+A4lj1Z9eYA==}
+
+ css-list-helpers@2.0.0:
+ resolution: {integrity: sha512-9Bj8tZ0jWbAM3u/U6m/boAzAwLPwtjzFvwivr2piSvyVa3K3rChJzQy4RIHkNkKiZCHrEMWDJWtTR8UyVhdDnQ==}
+
+ css-system-font-keywords@1.0.0:
+ resolution: {integrity: sha512-1umTtVd/fXS25ftfjB71eASCrYhilmEsvDEI6wG/QplnmlfmVM5HkZ/ZX46DT5K3eblFPgLUHt5BRCb0YXkSFA==}
+
+ css-tree@3.1.0:
+ resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==}
+ engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
+
+ cssesc@3.0.0:
+ resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ cssom@0.3.8:
+ resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==}
+
+ cssom@0.4.4:
+ resolution: {integrity: sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==}
+
+ cssstyle@2.3.0:
+ resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==}
+ engines: {node: '>=8'}
+
+ csstype@3.1.3:
+ resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+ d@1.0.2:
+ resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==}
+ engines: {node: '>=0.12'}
+
+ dargs@8.1.0:
+ resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==}
+ engines: {node: '>=12'}
+
+ data-urls@2.0.0:
+ resolution: {integrity: sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==}
+ engines: {node: '>=10'}
+
+ dayjs@1.11.10:
+ resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
+
+ de-indent@1.0.2:
+ resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
+
+ debug@2.6.9:
+ resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ debug@4.3.7:
+ resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ debug@4.4.1:
+ resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ decimal.js@10.5.0:
+ resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==}
+
+ decode-named-character-reference@1.2.0:
+ resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==}
+
+ decompress-response@6.0.0:
+ resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
+ engines: {node: '>=10'}
+
+ dedent@0.7.0:
+ resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==}
+
+ deep-is@0.1.4:
+ resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+ deepmerge@4.3.1:
+ resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
+ engines: {node: '>=0.10.0'}
+
+ default-gateway@6.0.3:
+ resolution: {integrity: sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==}
+ engines: {node: '>= 10'}
+
+ defaults@1.0.4:
+ resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==}
+
+ defer-to-connect@2.0.1:
+ resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==}
+ engines: {node: '>=10'}
+
+ define-data-property@1.1.4:
+ resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
+ engines: {node: '>= 0.4'}
+
+ define-lazy-prop@2.0.0:
+ resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==}
+ engines: {node: '>=8'}
+
+ defu@6.1.4:
+ resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
+
+ delayed-stream@1.0.0:
+ resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+ engines: {node: '>=0.4.0'}
+
+ depd@2.0.0:
+ resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
+ engines: {node: '>= 0.8'}
+
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
+ destr@2.0.5:
+ resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==}
+
+ destroy@1.2.0:
+ resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
+ engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+
+ detect-indent@6.1.0:
+ resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==}
+ engines: {node: '>=8'}
+
+ detect-newline@3.1.0:
+ resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
+ engines: {node: '>=8'}
+
+ devlop@1.1.0:
+ resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+
+ diff-sequences@27.5.1:
+ resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ dom-serializer@2.0.0:
+ resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
+
+ dom-walk@0.1.2:
+ resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==}
+
+ domelementtype@2.3.0:
+ resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
+
+ domexception@2.0.1:
+ resolution: {integrity: sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==}
+ engines: {node: '>=8'}
+ deprecated: Use your platform's native DOMException instead
+
+ domhandler@5.0.3:
+ resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
+ engines: {node: '>= 4'}
+
+ domutils@3.1.0:
+ resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==}
+
+ dot-prop@5.3.0:
+ resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==}
+ engines: {node: '>=8'}
+
+ dotenv@16.5.0:
+ resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==}
+ engines: {node: '>=12'}
+
+ dunder-proto@1.0.1:
+ resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
+ engines: {node: '>= 0.4'}
+
+ duplexer@0.1.2:
+ resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
+
+ eastasianwidth@0.2.0:
+ resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+
+ ee-first@1.1.1:
+ resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
+
+ electron-to-chromium@1.5.171:
+ resolution: {integrity: sha512-scWpzXEJEMrGJa4Y6m/tVotb0WuvNmasv3wWVzUAeCgKU0ToFOhUW6Z+xWnRQANMYGxN4ngJXIThgBJOqzVPCQ==}
+
+ electron-to-chromium@1.5.68:
+ resolution: {integrity: sha512-FgMdJlma0OzUYlbrtZ4AeXjKxKPk6KT8WOP8BjcqxWtlg8qyJQjRzPJzUtUn5GBg1oQ26hFs7HOOHJMYiJRnvQ==}
+
+ emittery@0.8.1:
+ resolution: {integrity: sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==}
+ engines: {node: '>=10'}
+
+ emoji-regex@10.4.0:
+ resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==}
+
+ emoji-regex@8.0.0:
+ resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+
+ emoji-regex@9.2.2:
+ resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+
+ encodeurl@1.0.2:
+ resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
+ engines: {node: '>= 0.8'}
+
+ encodeurl@2.0.0:
+ resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
+ engines: {node: '>= 0.8'}
+
+ end-of-stream@1.4.4:
+ resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
+
+ enhanced-resolve@5.18.1:
+ resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==}
+ engines: {node: '>=10.13.0'}
+
+ entities@4.5.0:
+ resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+ engines: {node: '>=0.12'}
+
+ env-paths@2.2.1:
+ resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
+ engines: {node: '>=6'}
+
+ environment@1.1.0:
+ resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
+ engines: {node: '>=18'}
+
+ error-ex@1.3.2:
+ resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
+
+ es-define-property@1.0.1:
+ resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
+ engines: {node: '>= 0.4'}
+
+ es-errors@1.3.0:
+ resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+ engines: {node: '>= 0.4'}
+
+ es-module-lexer@1.5.4:
+ resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==}
+
+ es-object-atoms@1.1.1:
+ resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
+ engines: {node: '>= 0.4'}
+
+ es-set-tostringtag@2.1.0:
+ resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
+ engines: {node: '>= 0.4'}
+
+ es5-ext@0.10.64:
+ resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==}
+ engines: {node: '>=0.10'}
+
+ es6-iterator@2.0.3:
+ resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==}
+
+ es6-promise@3.3.1:
+ resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==}
+
+ es6-symbol@3.1.4:
+ resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==}
+ engines: {node: '>=0.12'}
+
+ es6-weak-map@2.0.3:
+ resolution: {integrity: sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==}
+
+ esbuild@0.20.2:
+ resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==}
+ engines: {node: '>=12'}
+ hasBin: true
+
+ esbuild@0.24.2:
+ resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ esbuild@0.25.5:
+ resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ escape-html@1.0.3:
+ resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
+
+ escape-string-regexp@1.0.5:
+ resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
+ engines: {node: '>=0.8.0'}
+
+ escape-string-regexp@2.0.0:
+ resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
+ engines: {node: '>=8'}
+
+ escape-string-regexp@4.0.0:
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+ engines: {node: '>=10'}
+
+ escape-string-regexp@5.0.0:
+ resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
+ engines: {node: '>=12'}
+
+ escodegen@2.1.0:
+ resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==}
+ engines: {node: '>=6.0'}
+ hasBin: true
+
+ eslint-compat-utils@0.5.1:
+ resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ eslint: '>=6.0.0'
+
+ eslint-compat-utils@0.6.5:
+ resolution: {integrity: sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ eslint: '>=6.0.0'
+
+ eslint-config-flat-gitignore@2.1.0:
+ resolution: {integrity: sha512-cJzNJ7L+psWp5mXM7jBX+fjHtBvvh06RBlcweMhKD8jWqQw0G78hOW5tpVALGHGFPsBV+ot2H+pdDGJy6CV8pA==}
+ peerDependencies:
+ eslint: ^9.5.0
+
+ eslint-flat-config-utils@2.1.0:
+ resolution: {integrity: sha512-6fjOJ9tS0k28ketkUcQ+kKptB4dBZY2VijMZ9rGn8Cwnn1SH0cZBoPXT8AHBFHxmHcLFQK9zbELDinZ2Mr1rng==}
+
+ eslint-formatting-reporter@0.0.0:
+ resolution: {integrity: sha512-k9RdyTqxqN/wNYVaTk/ds5B5rA8lgoAmvceYN7bcZMBwU7TuXx5ntewJv81eF3pIL/CiJE+pJZm36llG8yhyyw==}
+ peerDependencies:
+ eslint: '>=8.40.0'
+
+ eslint-json-compat-utils@0.2.1:
+ resolution: {integrity: sha512-YzEodbDyW8DX8bImKhAcCeu/L31Dd/70Bidx2Qex9OFUtgzXLqtfWL4Hr5fM/aCCB8QUZLuJur0S9k6UfgFkfg==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ '@eslint/json': '*'
+ eslint: '*'
+ jsonc-eslint-parser: ^2.4.0
+ peerDependenciesMeta:
+ '@eslint/json':
+ optional: true
+
+ eslint-merge-processors@2.0.0:
+ resolution: {integrity: sha512-sUuhSf3IrJdGooquEUB5TNpGNpBoQccbnaLHsb1XkBLUPPqCNivCpY05ZcpCOiV9uHwO2yxXEWVczVclzMxYlA==}
+ peerDependencies:
+ eslint: '*'
+
+ eslint-parser-plain@0.1.1:
+ resolution: {integrity: sha512-KRgd6wuxH4U8kczqPp+Oyk4irThIhHWxgFgLDtpgjUGVIS3wGrJntvZW/p6hHq1T4FOwnOtCNkvAI4Kr+mQ/Hw==}
+
+ eslint-plugin-antfu@3.1.1:
+ resolution: {integrity: sha512-7Q+NhwLfHJFvopI2HBZbSxWXngTwBLKxW1AGXLr2lEGxcEIK/AsDs8pn8fvIizl5aZjBbVbVK5ujmMpBe4Tvdg==}
+ peerDependencies:
+ eslint: '*'
+
+ eslint-plugin-command@3.3.0:
+ resolution: {integrity: sha512-hDy283ajilnXykdJZQB5SuIPPZ8He4rzltirXiCgT7/O05DV40IeS6RzRNqnNcNUFviW1ZHAY2DgzGjs1MIC6w==}
+ peerDependencies:
+ eslint: '*'
+
+ eslint-plugin-es-x@7.8.0:
+ resolution: {integrity: sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ eslint: '>=8'
+
+ eslint-plugin-format@1.0.1:
+ resolution: {integrity: sha512-Tdns+CDjS+m7QrM85wwRi2yLae88XiWVdIOXjp9mDII0pmTBQlczPCmjpKnjiUIY3yPZNLqb5Ms/A/JXcBF2Dw==}
+ peerDependencies:
+ eslint: ^8.40.0 || ^9.0.0
+
+ eslint-plugin-import-lite@0.3.0:
+ resolution: {integrity: sha512-dkNBAL6jcoCsXZsQ/Tt2yXmMDoNt5NaBh/U7yvccjiK8cai6Ay+MK77bMykmqQA2bTF6lngaLCDij6MTO3KkvA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: '>=9.0.0'
+ typescript: '>=4.5'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ eslint-plugin-jsdoc@51.1.0:
+ resolution: {integrity: sha512-dWyjRvQcAuFHZUTUaJEw7OQ6XziNlbz2wZNt6QuLUHwYMnOWzs6vGSikR/ZSeh+IFHjdH+91AKf+CwYjlJ4kVA==}
+ engines: {node: '>=20.11.0'}
+ peerDependencies:
+ eslint: ^7.0.0 || ^8.0.0 || ^9.0.0
+
+ eslint-plugin-jsonc@2.20.1:
+ resolution: {integrity: sha512-gUzIwQHXx7ZPypUoadcyRi4WbHW2TPixDr0kqQ4miuJBU0emJmyGTlnaT3Og9X2a8R1CDayN9BFSq5weGWbTng==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: '>=6.0.0'
+
+ eslint-plugin-n@17.20.0:
+ resolution: {integrity: sha512-IRSoatgB/NQJZG5EeTbv/iAx1byOGdbbyhQrNvWdCfTnmPxUT0ao9/eGOeG7ljD8wJBsxwE8f6tES5Db0FRKEw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: '>=8.23.0'
+
+ eslint-plugin-no-only-tests@3.3.0:
+ resolution: {integrity: sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q==}
+ engines: {node: '>=5.0.0'}
+
+ eslint-plugin-perfectionist@4.15.0:
+ resolution: {integrity: sha512-pC7PgoXyDnEXe14xvRUhBII8A3zRgggKqJFx2a82fjrItDs1BSI7zdZnQtM2yQvcyod6/ujmzb7ejKPx8lZTnw==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ peerDependencies:
+ eslint: '>=8.45.0'
+
+ eslint-plugin-pnpm@0.3.1:
+ resolution: {integrity: sha512-vi5iHoELIAlBbX4AW8ZGzU3tUnfxuXhC/NKo3qRcI5o9igbz6zJUqSlQ03bPeMqWIGTPatZnbWsNR1RnlNERNQ==}
+ peerDependencies:
+ eslint: ^9.0.0
+
+ eslint-plugin-regexp@2.9.0:
+ resolution: {integrity: sha512-9WqJMnOq8VlE/cK+YAo9C9YHhkOtcEtEk9d12a+H7OSZFwlpI6stiHmYPGa2VE0QhTzodJyhlyprUaXDZLgHBw==}
+ engines: {node: ^18 || >=20}
+ peerDependencies:
+ eslint: '>=8.44.0'
+
+ eslint-plugin-toml@0.12.0:
+ resolution: {integrity: sha512-+/wVObA9DVhwZB1nG83D2OAQRrcQZXy+drqUnFJKymqnmbnbfg/UPmEMCKrJNcEboUGxUjYrJlgy+/Y930mURQ==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: '>=6.0.0'
+
+ eslint-plugin-unicorn@59.0.1:
+ resolution: {integrity: sha512-EtNXYuWPUmkgSU2E7Ttn57LbRREQesIP1BiLn7OZLKodopKfDXfBUkC/0j6mpw2JExwf43Uf3qLSvrSvppgy8Q==}
+ engines: {node: ^18.20.0 || ^20.10.0 || >=21.0.0}
+ peerDependencies:
+ eslint: '>=9.22.0'
+
+ eslint-plugin-unused-imports@4.1.4:
+ resolution: {integrity: sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==}
+ peerDependencies:
+ '@typescript-eslint/eslint-plugin': ^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0
+ eslint: ^9.0.0 || ^8.0.0
+ peerDependenciesMeta:
+ '@typescript-eslint/eslint-plugin':
+ optional: true
+
+ eslint-plugin-vue@10.2.0:
+ resolution: {integrity: sha512-tl9s+KN3z0hN2b8fV2xSs5ytGl7Esk1oSCxULLwFcdaElhZ8btYYZFrWxvh4En+czrSDtuLCeCOGa8HhEZuBdQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ vue-eslint-parser: ^10.0.0
+
+ eslint-plugin-yml@1.18.0:
+ resolution: {integrity: sha512-9NtbhHRN2NJa/s3uHchO3qVVZw0vyOIvWlXWGaKCr/6l3Go62wsvJK5byiI6ZoYztDsow4GnS69BZD3GnqH3hA==}
+ engines: {node: ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: '>=6.0.0'
+
+ eslint-processor-vue-blocks@2.0.0:
+ resolution: {integrity: sha512-u4W0CJwGoWY3bjXAuFpc/b6eK3NQEI8MoeW7ritKj3G3z/WtHrKjkqf+wk8mPEy5rlMGS+k6AZYOw2XBoN/02Q==}
+ peerDependencies:
+ '@vue/compiler-sfc': ^3.3.0
+ eslint: '>=9.0.0'
+
+ eslint-scope@8.4.0:
+ resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint-visitor-keys@3.4.3:
+ resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ eslint-visitor-keys@4.2.1:
+ resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint@9.29.0:
+ resolution: {integrity: sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ hasBin: true
+ peerDependencies:
+ jiti: '*'
+ peerDependenciesMeta:
+ jiti:
+ optional: true
+
+ esniff@2.0.1:
+ resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==}
+ engines: {node: '>=0.10'}
+
+ espree@10.4.0:
+ resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ espree@9.6.1:
+ resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ esprima@4.0.1:
+ resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ esquery@1.6.0:
+ resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
+ engines: {node: '>=0.10'}
+
+ esrecurse@4.3.0:
+ resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+ engines: {node: '>=4.0'}
+
+ estraverse@5.3.0:
+ resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+ engines: {node: '>=4.0'}
+
+ estree-walker@2.0.2:
+ resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
+ estree-walker@3.0.3:
+ resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+
+ esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+
+ etag@1.8.1:
+ resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
+ engines: {node: '>= 0.6'}
+
+ event-emitter@0.3.5:
+ resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==}
+
+ eventemitter3@5.0.1:
+ resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
+
+ execa@5.1.1:
+ resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
+ engines: {node: '>=10'}
+
+ execa@8.0.1:
+ resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
+ engines: {node: '>=16.17'}
+
+ exif-parser@0.1.12:
+ resolution: {integrity: sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==}
+
+ exit@0.1.2:
+ resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==}
+ engines: {node: '>= 0.8.0'}
+
+ expect@27.5.1:
+ resolution: {integrity: sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ express@4.21.1:
+ resolution: {integrity: sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==}
+ engines: {node: '>= 0.10.0'}
+
+ exsolve@1.0.5:
+ resolution: {integrity: sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==}
+
+ ext@1.7.0:
+ resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==}
+
+ fast-deep-equal@3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+ fast-diff@1.3.0:
+ resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
+
+ fast-glob@3.3.2:
+ resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
+ engines: {node: '>=8.6.0'}
+
+ fast-glob@3.3.3:
+ resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
+ engines: {node: '>=8.6.0'}
+
+ fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+ fast-levenshtein@2.0.6:
+ resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+ fast-safe-stringify@2.1.1:
+ resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
+
+ fast-uri@3.0.6:
+ resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==}
+
+ fastq@1.17.1:
+ resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
+
+ fault@2.0.1:
+ resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==}
+
+ fb-watchman@2.0.2:
+ resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==}
+
+ fdir@6.4.2:
+ resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ fdir@6.4.4:
+ resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ file-entry-cache@8.0.0:
+ resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
+ engines: {node: '>=16.0.0'}
+
+ file-type@9.0.0:
+ resolution: {integrity: sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw==}
+ engines: {node: '>=6'}
+
+ fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+ engines: {node: '>=8'}
+
+ finalhandler@1.3.1:
+ resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==}
+ engines: {node: '>= 0.8'}
+
+ find-cache-dir@3.3.2:
+ resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==}
+ engines: {node: '>=8'}
+
+ find-up-simple@1.0.1:
+ resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==}
+ engines: {node: '>=18'}
+
+ find-up@4.1.0:
+ resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
+ engines: {node: '>=8'}
+
+ find-up@5.0.0:
+ resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+ engines: {node: '>=10'}
+
+ find-up@7.0.0:
+ resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==}
+ engines: {node: '>=18'}
+
+ flat-cache@4.0.1:
+ resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
+ engines: {node: '>=16'}
+
+ flatted@3.3.3:
+ resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
+
+ follow-redirects@1.15.9:
+ resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
+ engines: {node: '>=4.0'}
+ peerDependencies:
+ debug: '*'
+ peerDependenciesMeta:
+ debug:
+ optional: true
+
+ foreground-child@3.3.0:
+ resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
+ engines: {node: '>=14'}
+
+ form-data@3.0.3:
+ resolution: {integrity: sha512-q5YBMeWy6E2Un0nMGWMgI65MAKtaylxfNJGJxpGh45YDciZB4epbWpaAfImil6CPAPTYB4sh0URQNDRIZG5F2w==}
+ engines: {node: '>= 6'}
+
+ form-data@4.0.1:
+ resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==}
+ engines: {node: '>= 6'}
+
+ format@0.2.2:
+ resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==}
+ engines: {node: '>=0.4.x'}
+
+ forwarded@0.2.0:
+ resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
+ engines: {node: '>= 0.6'}
+
+ fraction.js@4.3.7:
+ resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
+
+ fresh@0.5.2:
+ resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
+ engines: {node: '>= 0.6'}
+
+ fs-extra@10.1.0:
+ resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
+ engines: {node: '>=12'}
+
+ fs-minipass@2.1.0:
+ resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
+ engines: {node: '>= 8'}
+
+ fs.realpath@1.0.0:
+ resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ function-bind@1.1.2:
+ resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+ generic-names@4.0.0:
+ resolution: {integrity: sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A==}
+
+ gensync@1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+
+ get-caller-file@2.0.5:
+ resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+ engines: {node: 6.* || 8.* || >= 10.*}
+
+ get-east-asian-width@1.3.0:
+ resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==}
+ engines: {node: '>=18'}
+
+ get-intrinsic@1.3.0:
+ resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
+ engines: {node: '>= 0.4'}
+
+ get-package-type@0.1.0:
+ resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==}
+ engines: {node: '>=8.0.0'}
+
+ get-proto@1.0.1:
+ resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
+ engines: {node: '>= 0.4'}
+
+ get-stream@5.2.0:
+ resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
+ engines: {node: '>=8'}
+
+ get-stream@6.0.1:
+ resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
+ engines: {node: '>=10'}
+
+ get-stream@8.0.1:
+ resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==}
+ engines: {node: '>=16'}
+
+ get-tsconfig@4.10.1:
+ resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==}
+
+ giget@1.2.5:
+ resolution: {integrity: sha512-r1ekGw/Bgpi3HLV3h1MRBIlSAdHoIMklpaQ3OQLFcRw9PwAj2rqigvIbg+dBUI51OxVI2jsEtDywDBjSiuf7Ug==}
+ hasBin: true
+
+ git-raw-commits@4.0.0:
+ resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==}
+ engines: {node: '>=16'}
+ hasBin: true
+
+ github-slugger@2.0.0:
+ resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==}
+
+ glob-parent@5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+
+ glob-parent@6.0.2:
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+ engines: {node: '>=10.13.0'}
+
+ glob@11.0.0:
+ resolution: {integrity: sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==}
+ engines: {node: 20 || >=22}
+ hasBin: true
+
+ glob@7.2.3:
+ resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+ deprecated: Glob versions prior to v9 are no longer supported
+
+ global-directory@4.0.1:
+ resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==}
+ engines: {node: '>=18'}
+
+ global@4.4.0:
+ resolution: {integrity: sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==}
+
+ globals@11.12.0:
+ resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
+ engines: {node: '>=4'}
+
+ globals@14.0.0:
+ resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
+ engines: {node: '>=18'}
+
+ globals@15.15.0:
+ resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==}
+ engines: {node: '>=18'}
+
+ globals@16.2.0:
+ resolution: {integrity: sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg==}
+ engines: {node: '>=18'}
+
+ gopd@1.2.0:
+ resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
+ engines: {node: '>= 0.4'}
+
+ got@11.8.6:
+ resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==}
+ engines: {node: '>=10.19.0'}
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ graphemer@1.4.0:
+ resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
+
+ graphlib@2.1.8:
+ resolution: {integrity: sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==}
+
+ gzip-size@6.0.0:
+ resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==}
+ engines: {node: '>=10'}
+
+ handlebars@4.7.8:
+ resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==}
+ engines: {node: '>=0.4.7'}
+ hasBin: true
+
+ has-flag@4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+
+ has-property-descriptors@1.0.2:
+ resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
+
+ has-symbols@1.1.0:
+ resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
+ engines: {node: '>= 0.4'}
+
+ has-tostringtag@1.0.2:
+ resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
+ engines: {node: '>= 0.4'}
+
+ hash-sum@2.0.0:
+ resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==}
+
+ hasown@2.0.2:
+ resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+ engines: {node: '>= 0.4'}
+
+ he@1.2.0:
+ resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
+ hasBin: true
+
+ html-encoding-sniffer@2.0.1:
+ resolution: {integrity: sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==}
+ engines: {node: '>=10'}
+
+ html-escaper@2.0.2:
+ resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
+
+ html-tags@3.3.1:
+ resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==}
+ engines: {node: '>=8'}
+
+ htmlparser2@8.0.2:
+ resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
+
+ http-cache-semantics@4.1.1:
+ resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==}
+
+ http-errors@2.0.0:
+ resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
+ engines: {node: '>= 0.8'}
+
+ http-proxy-agent@4.0.1:
+ resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==}
+ engines: {node: '>= 6'}
+
+ http2-client@1.3.5:
+ resolution: {integrity: sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==}
+
+ http2-wrapper@1.0.3:
+ resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==}
+ engines: {node: '>=10.19.0'}
+
+ https-proxy-agent@5.0.1:
+ resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
+ engines: {node: '>= 6'}
+
+ human-signals@2.1.0:
+ resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
+ engines: {node: '>=10.17.0'}
+
+ human-signals@5.0.0:
+ resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
+ engines: {node: '>=16.17.0'}
+
+ husky@9.1.7:
+ resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ iconv-lite@0.4.24:
+ resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
+ engines: {node: '>=0.10.0'}
+
+ icss-replace-symbols@1.1.0:
+ resolution: {integrity: sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==}
+
+ icss-utils@5.1.0:
+ resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==}
+ engines: {node: ^10 || ^12 || >= 14}
+ peerDependencies:
+ postcss: ^8.1.0
+
+ ieee754@1.2.1:
+ resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+
+ ignore@5.3.2:
+ resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
+ engines: {node: '>= 4'}
+
+ ignore@7.0.5:
+ resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
+ engines: {node: '>= 4'}
+
+ immutable@4.3.7:
+ resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==}
+
+ import-fresh@3.3.0:
+ resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
+ engines: {node: '>=6'}
+
+ import-local@3.2.0:
+ resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==}
+ engines: {node: '>=8'}
+ hasBin: true
+
+ import-meta-resolve@4.1.0:
+ resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==}
+
+ importx@0.4.4:
+ resolution: {integrity: sha512-Lo1pukzAREqrBnnHC+tj+lreMTAvyxtkKsMxLY8H15M/bvLl54p3YuoTI70Tz7Il0AsgSlD7Lrk/FaApRcBL7w==}
+
+ importx@0.5.2:
+ resolution: {integrity: sha512-YEwlK86Ml5WiTxN/ECUYC5U7jd1CisAVw7ya4i9ZppBoHfFkT2+hChhr3PE2fYxUKLkNyivxEQpa5Ruil1LJBQ==}
+
+ imurmurhash@0.1.4:
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+ engines: {node: '>=0.8.19'}
+
+ indent-string@5.0.0:
+ resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==}
+ engines: {node: '>=12'}
+
+ inflight@1.0.6:
+ resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+ deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
+
+ inherits@2.0.4:
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+
+ ini@4.1.1:
+ resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
+ invert-kv@3.0.1:
+ resolution: {integrity: sha512-CYdFeFexxhv/Bcny+Q0BfOV+ltRlJcd4BBZBYFX/O0u4npJrgZtIcjokegtiSMAvlMTJ+Koq0GBCc//3bueQxw==}
+ engines: {node: '>=8'}
+
+ ipaddr.js@1.9.1:
+ resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
+ engines: {node: '>= 0.10'}
+
+ is-arrayish@0.2.1:
+ resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
+
+ is-binary-path@2.1.0:
+ resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+ engines: {node: '>=8'}
+
+ is-builtin-module@5.0.0:
+ resolution: {integrity: sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==}
+ engines: {node: '>=18.20'}
+
+ is-core-module@2.16.1:
+ resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
+ engines: {node: '>= 0.4'}
+
+ is-docker@2.2.1:
+ resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
+ engines: {node: '>=8'}
+ hasBin: true
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-fullwidth-code-point@3.0.0:
+ resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+ engines: {node: '>=8'}
+
+ is-fullwidth-code-point@4.0.0:
+ resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==}
+ engines: {node: '>=12'}
+
+ is-fullwidth-code-point@5.0.0:
+ resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==}
+ engines: {node: '>=18'}
+
+ is-function@1.0.2:
+ resolution: {integrity: sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==}
+
+ is-generator-fn@2.1.0:
+ resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==}
+ engines: {node: '>=6'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-interactive@1.0.0:
+ resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==}
+ engines: {node: '>=8'}
+
+ is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
+ is-obj@2.0.0:
+ resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==}
+ engines: {node: '>=8'}
+
+ is-potential-custom-element-name@1.0.1:
+ resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+
+ is-promise@2.2.2:
+ resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==}
+
+ is-stream@2.0.1:
+ resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
+ engines: {node: '>=8'}
+
+ is-stream@3.0.0:
+ resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+ is-text-path@2.0.0:
+ resolution: {integrity: sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==}
+ engines: {node: '>=8'}
+
+ is-typedarray@1.0.0:
+ resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==}
+
+ is-unicode-supported@0.1.0:
+ resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
+ engines: {node: '>=10'}
+
+ is-wsl@2.2.0:
+ resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
+ engines: {node: '>=8'}
+
+ isbinaryfile@5.0.4:
+ resolution: {integrity: sha512-YKBKVkKhty7s8rxddb40oOkuP0NbaeXrQvLin6QMHL7Ypiy2RW9LwOVrVgZRyOrhQlayMd9t+D8yDy8MKFTSDQ==}
+ engines: {node: '>= 18.0.0'}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ istanbul-lib-coverage@3.2.2:
+ resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
+ engines: {node: '>=8'}
+
+ istanbul-lib-instrument@5.2.1:
+ resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==}
+ engines: {node: '>=8'}
+
+ istanbul-lib-report@3.0.1:
+ resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==}
+ engines: {node: '>=10'}
+
+ istanbul-lib-source-maps@4.0.1:
+ resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==}
+ engines: {node: '>=10'}
+
+ istanbul-reports@3.1.7:
+ resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==}
+ engines: {node: '>=8'}
+
+ jackspeak@4.0.2:
+ resolution: {integrity: sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==}
+ engines: {node: 20 || >=22}
+
+ javascript-natural-sort@0.7.1:
+ resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==}
+
+ jest-changed-files@27.5.1:
+ resolution: {integrity: sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-circus@27.5.1:
+ resolution: {integrity: sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-cli@27.5.1:
+ resolution: {integrity: sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+ hasBin: true
+ peerDependencies:
+ node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+ peerDependenciesMeta:
+ node-notifier:
+ optional: true
+
+ jest-config@27.5.1:
+ resolution: {integrity: sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+ peerDependencies:
+ ts-node: '>=9.0.0'
+ peerDependenciesMeta:
+ ts-node:
+ optional: true
+
+ jest-diff@27.5.1:
+ resolution: {integrity: sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-docblock@27.5.1:
+ resolution: {integrity: sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-each@27.5.1:
+ resolution: {integrity: sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-environment-jsdom@27.5.1:
+ resolution: {integrity: sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-environment-node@27.5.1:
+ resolution: {integrity: sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-get-type@27.5.1:
+ resolution: {integrity: sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-haste-map@27.5.1:
+ resolution: {integrity: sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-jasmine2@27.5.1:
+ resolution: {integrity: sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-leak-detector@27.5.1:
+ resolution: {integrity: sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-matcher-utils@27.5.1:
+ resolution: {integrity: sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-message-util@27.5.1:
+ resolution: {integrity: sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-mock@27.5.1:
+ resolution: {integrity: sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-pnp-resolver@1.2.3:
+ resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==}
+ engines: {node: '>=6'}
+ peerDependencies:
+ jest-resolve: '*'
+ peerDependenciesMeta:
+ jest-resolve:
+ optional: true
+
+ jest-regex-util@27.5.1:
+ resolution: {integrity: sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-resolve-dependencies@27.5.1:
+ resolution: {integrity: sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-resolve@27.5.1:
+ resolution: {integrity: sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-runner@27.5.1:
+ resolution: {integrity: sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-runtime@27.5.1:
+ resolution: {integrity: sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-serializer@27.5.1:
+ resolution: {integrity: sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-snapshot@27.5.1:
+ resolution: {integrity: sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-util@27.5.1:
+ resolution: {integrity: sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-validate@27.5.1:
+ resolution: {integrity: sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-watcher@27.5.1:
+ resolution: {integrity: sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ jest-worker@27.5.1:
+ resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
+ engines: {node: '>= 10.13.0'}
+
+ jest@27.0.4:
+ resolution: {integrity: sha512-Px1iKFooXgGSkk1H8dJxxBIrM3tsc5SIuI4kfKYK2J+4rvCvPGr/cXktxh0e9zIPQ5g09kOMNfHQEmusBUf/ZA==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+ hasBin: true
+ peerDependencies:
+ node-notifier: ^8.0.1 || ^9.0.0
+ peerDependenciesMeta:
+ node-notifier:
+ optional: true
+
+ jimp@0.10.3:
+ resolution: {integrity: sha512-meVWmDMtyUG5uYjFkmzu0zBgnCvvxwWNi27c4cg55vWNVC9ES4Lcwb+ogx+uBBQE3Q+dLKjXaLl0JVW+nUNwbQ==}
+
+ jiti@1.21.6:
+ resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==}
+ hasBin: true
+
+ jiti@2.0.0-beta.3:
+ resolution: {integrity: sha512-pmfRbVRs/7khFrSAYnSiJ8C0D5GvzkE4Ey2pAvUcJsw1ly/p+7ut27jbJrjY79BpAJQJ4gXYFtK6d1Aub+9baQ==}
+ hasBin: true
+
+ jiti@2.4.2:
+ resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==}
+ hasBin: true
+
+ jpeg-js@0.3.7:
+ resolution: {integrity: sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ==}
+
+ js-cookie@3.0.5:
+ resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
+ engines: {node: '>=14'}
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ js-tokens@9.0.1:
+ resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
+
+ js-yaml@3.14.1:
+ resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
+ hasBin: true
+
+ js-yaml@4.1.0:
+ resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+ hasBin: true
+
+ jsbn@1.1.0:
+ resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==}
+
+ jsdoc-type-pratt-parser@4.1.0:
+ resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==}
+ engines: {node: '>=12.0.0'}
+
+ jsdom@16.7.0:
+ resolution: {integrity: sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ canvas: ^2.5.0
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+
+ jsesc@3.0.2:
+ resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ jsesc@3.1.0:
+ resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ json-buffer@3.0.1:
+ resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
+ json-parse-even-better-errors@2.3.1:
+ resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
+
+ json-schema-traverse@0.4.1:
+ resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
+ json-schema-traverse@1.0.0:
+ resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
+
+ json-stable-stringify-without-jsonify@1.0.1:
+ resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ jsonc-eslint-parser@2.4.0:
+ resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ jsonc-parser@3.3.1:
+ resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==}
+
+ jsonfile@6.1.0:
+ resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
+
+ jsonparse@1.3.1:
+ resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==}
+ engines: {'0': node >= 0.2.0}
+
+ keyv@4.5.4:
+ resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+ kleur@3.0.3:
+ resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
+ engines: {node: '>=6'}
+
+ kolorist@1.8.0:
+ resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
+
+ lcid@3.1.1:
+ resolution: {integrity: sha512-M6T051+5QCGLBQb8id3hdvIW8+zeFV2FyBGFS9IEK5H9Wt4MueD4bW1eWikpHgZp+5xR3l5c8pZUkQsIA0BFZg==}
+ engines: {node: '>=8'}
+
+ leven@3.1.0:
+ resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
+ engines: {node: '>=6'}
+
+ levn@0.4.1:
+ resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+ engines: {node: '>= 0.8.0'}
+
+ licia@1.46.0:
+ resolution: {integrity: sha512-Zms2AjJB+KdqUKFF87J5J/w9DwXnGN/lKlbjpRgvaPf0BIQ0mOZ/2lX4E79zwNafHGMUq5RtN54FN6Af5G92cA==}
+
+ lilconfig@2.1.0:
+ resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
+ engines: {node: '>=10'}
+
+ lilconfig@3.1.3:
+ resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
+ engines: {node: '>=14'}
+
+ lines-and-columns@1.2.4:
+ resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+
+ lines-and-columns@2.0.4:
+ resolution: {integrity: sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+ lint-staged@15.2.10:
+ resolution: {integrity: sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==}
+ engines: {node: '>=18.12.0'}
+ hasBin: true
+
+ listr2@8.2.5:
+ resolution: {integrity: sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==}
+ engines: {node: '>=18.0.0'}
+
+ load-bmfont@1.4.2:
+ resolution: {integrity: sha512-qElWkmjW9Oq1F9EI5Gt7aD9zcdHb9spJCW1L/dmPf7KzCCEJxq8nhHz5eCgI9aMf7vrG/wyaCqdsI+Iy9ZTlog==}
+
+ load-tsconfig@0.2.5:
+ resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+ loader-utils@3.3.1:
+ resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==}
+ engines: {node: '>= 12.13.0'}
+
+ local-pkg@0.4.3:
+ resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==}
+ engines: {node: '>=14'}
+
+ local-pkg@0.5.1:
+ resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==}
+ engines: {node: '>=14'}
+
+ local-pkg@1.1.1:
+ resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==}
+ engines: {node: '>=14'}
+
+ localstorage-polyfill@1.0.1:
+ resolution: {integrity: sha512-m4iHVZxFH5734oQcPKU08025gIz2+4bjWR9lulP8ZYxEJR0BpA0w32oJmkzh8y3UI9ci7xCBehQDc3oA1X+VHw==}
+ engines: {node: '>=6'}
+
+ locate-path@5.0.0:
+ resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
+ engines: {node: '>=8'}
+
+ locate-path@6.0.0:
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+ engines: {node: '>=10'}
+
+ locate-path@7.2.0:
+ resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+ lodash.camelcase@4.3.0:
+ resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
+
+ lodash.debounce@4.0.8:
+ resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
+
+ lodash.groupby@4.6.0:
+ resolution: {integrity: sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==}
+
+ lodash.isplainobject@4.0.6:
+ resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
+
+ lodash.kebabcase@4.1.1:
+ resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==}
+
+ lodash.merge@4.6.2:
+ resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+
+ lodash.mergewith@4.6.2:
+ resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==}
+
+ lodash.snakecase@4.1.1:
+ resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==}
+
+ lodash.startcase@4.4.0:
+ resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==}
+
+ lodash.uniq@4.5.0:
+ resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==}
+
+ lodash.upperfirst@4.3.1:
+ resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==}
+
+ lodash@4.17.21:
+ resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
+ log-symbols@4.1.0:
+ resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
+ engines: {node: '>=10'}
+
+ log-update@6.1.0:
+ resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==}
+ engines: {node: '>=18'}
+
+ longest-streak@3.1.0:
+ resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
+
+ lowercase-keys@2.0.0:
+ resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==}
+ engines: {node: '>=8'}
+
+ lru-cache@11.0.2:
+ resolution: {integrity: sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==}
+ engines: {node: 20 || >=22}
+
+ lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+ lru-queue@0.1.0:
+ resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==}
+
+ magic-string@0.30.14:
+ resolution: {integrity: sha512-5c99P1WKTed11ZC0HMJOj6CDIue6F8ySu+bJL+85q1zBEIY8IklrJ1eiKC2NDRh3Ct3FcvmJPyQHb9erXMTJNw==}
+
+ magic-string@0.30.17:
+ resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
+
+ make-dir@3.1.0:
+ resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
+ engines: {node: '>=8'}
+
+ make-dir@4.0.0:
+ resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
+ engines: {node: '>=10'}
+
+ make-synchronized@0.2.9:
+ resolution: {integrity: sha512-4wczOs8SLuEdpEvp3vGo83wh8rjJ78UsIk7DIX5fxdfmfMJGog4bQzxfvOwq7Q3yCHLC4jp1urPHIxRS/A93gA==}
+
+ makeerror@1.0.12:
+ resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
+
+ markdown-table@3.0.4:
+ resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
+
+ math-intrinsics@1.1.0:
+ resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
+ engines: {node: '>= 0.4'}
+
+ md5-file@5.0.0:
+ resolution: {integrity: sha512-xbEFXCYVWrSx/gEKS1VPlg84h/4L20znVIulKw6kMfmBUAZNAnF00eczz9ICMl+/hjQGo5KSXRxbL/47X3rmMw==}
+ engines: {node: '>=10.13.0'}
+ hasBin: true
+
+ mdast-util-find-and-replace@3.0.2:
+ resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==}
+
+ mdast-util-from-markdown@2.0.2:
+ resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==}
+
+ mdast-util-frontmatter@2.0.1:
+ resolution: {integrity: sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==}
+
+ mdast-util-gfm-autolink-literal@2.0.1:
+ resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==}
+
+ mdast-util-gfm-footnote@2.1.0:
+ resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==}
+
+ mdast-util-gfm-strikethrough@2.0.0:
+ resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==}
+
+ mdast-util-gfm-table@2.0.0:
+ resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==}
+
+ mdast-util-gfm-task-list-item@2.0.0:
+ resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==}
+
+ mdast-util-gfm@3.1.0:
+ resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==}
+
+ mdast-util-phrasing@4.1.0:
+ resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==}
+
+ mdast-util-to-markdown@2.1.2:
+ resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==}
+
+ mdast-util-to-string@4.0.0:
+ resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
+
+ mdn-data@2.12.2:
+ resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==}
+
+ media-typer@0.3.0:
+ resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
+ engines: {node: '>= 0.6'}
+
+ memoizee@0.4.17:
+ resolution: {integrity: sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==}
+ engines: {node: '>=0.12'}
+
+ meow@12.1.1:
+ resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==}
+ engines: {node: '>=16.10'}
+
+ meow@13.2.0:
+ resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==}
+ engines: {node: '>=18'}
+
+ merge-descriptors@1.0.3:
+ resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==}
+
+ merge-stream@2.0.0:
+ resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
+
+ merge2@1.4.1:
+ resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+ engines: {node: '>= 8'}
+
+ merge@2.1.1:
+ resolution: {integrity: sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==}
+
+ methods@1.1.2:
+ resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
+ engines: {node: '>= 0.6'}
+
+ micromark-core-commonmark@2.0.3:
+ resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==}
+
+ micromark-extension-frontmatter@2.0.0:
+ resolution: {integrity: sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==}
+
+ micromark-extension-gfm-autolink-literal@2.1.0:
+ resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==}
+
+ micromark-extension-gfm-footnote@2.1.0:
+ resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==}
+
+ micromark-extension-gfm-strikethrough@2.1.0:
+ resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==}
+
+ micromark-extension-gfm-table@2.1.1:
+ resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==}
+
+ micromark-extension-gfm-tagfilter@2.0.0:
+ resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==}
+
+ micromark-extension-gfm-task-list-item@2.1.0:
+ resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==}
+
+ micromark-extension-gfm@3.0.0:
+ resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==}
+
+ micromark-factory-destination@2.0.1:
+ resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==}
+
+ micromark-factory-label@2.0.1:
+ resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==}
+
+ micromark-factory-space@2.0.1:
+ resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==}
+
+ micromark-factory-title@2.0.1:
+ resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==}
+
+ micromark-factory-whitespace@2.0.1:
+ resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==}
+
+ micromark-util-character@2.1.1:
+ resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==}
+
+ micromark-util-chunked@2.0.1:
+ resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==}
+
+ micromark-util-classify-character@2.0.1:
+ resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==}
+
+ micromark-util-combine-extensions@2.0.1:
+ resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==}
+
+ micromark-util-decode-numeric-character-reference@2.0.2:
+ resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==}
+
+ micromark-util-decode-string@2.0.1:
+ resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==}
+
+ micromark-util-encode@2.0.1:
+ resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==}
+
+ micromark-util-html-tag-name@2.0.1:
+ resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==}
+
+ micromark-util-normalize-identifier@2.0.1:
+ resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==}
+
+ micromark-util-resolve-all@2.0.1:
+ resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==}
+
+ micromark-util-sanitize-uri@2.0.1:
+ resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==}
+
+ micromark-util-subtokenize@2.1.0:
+ resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==}
+
+ micromark-util-symbol@2.0.1:
+ resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==}
+
+ micromark-util-types@2.0.2:
+ resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==}
+
+ micromark@4.0.2:
+ resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==}
+
+ micromatch@4.0.8:
+ resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+ engines: {node: '>=8.6'}
+
+ mime-db@1.52.0:
+ resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+ engines: {node: '>= 0.6'}
+
+ mime-types@2.1.35:
+ resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+ engines: {node: '>= 0.6'}
+
+ mime@1.6.0:
+ resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ mime@3.0.0:
+ resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==}
+ engines: {node: '>=10.0.0'}
+ hasBin: true
+
+ mimic-fn@2.1.0:
+ resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
+ engines: {node: '>=6'}
+
+ mimic-fn@4.0.0:
+ resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
+ engines: {node: '>=12'}
+
+ mimic-function@5.0.1:
+ resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
+ engines: {node: '>=18'}
+
+ mimic-response@1.0.1:
+ resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==}
+ engines: {node: '>=4'}
+
+ mimic-response@3.1.0:
+ resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
+ engines: {node: '>=10'}
+
+ min-document@2.19.0:
+ resolution: {integrity: sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==}
+
+ min-indent@1.0.1:
+ resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
+ engines: {node: '>=4'}
+
+ minimatch@10.0.1:
+ resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==}
+ engines: {node: 20 || >=22}
+
+ minimatch@3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+ minimatch@8.0.4:
+ resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minimatch@9.0.5:
+ resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minimist@1.2.8:
+ resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+
+ minipass@3.3.6:
+ resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
+ engines: {node: '>=8'}
+
+ minipass@5.0.0:
+ resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
+ engines: {node: '>=8'}
+
+ minipass@7.1.2:
+ resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minizlib@2.1.2:
+ resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
+ engines: {node: '>= 8'}
+
+ mkdirp@0.5.6:
+ resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
+ hasBin: true
+
+ mkdirp@1.0.4:
+ resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ mlly@1.7.3:
+ resolution: {integrity: sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==}
+
+ mlly@1.7.4:
+ resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==}
+
+ mockjs@1.1.0:
+ resolution: {integrity: sha512-eQsKcWzIaZzEZ07NuEyO4Nw65g0hdWAyurVol1IPl1gahRwY+svqzfgfey8U8dahLwG44d6/RwEzuK52rSa/JQ==}
+ hasBin: true
+
+ module-alias@2.2.3:
+ resolution: {integrity: sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==}
+
+ mrmime@2.0.1:
+ resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
+ engines: {node: '>=10'}
+
+ ms@2.0.0:
+ resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ muggle-string@0.4.1:
+ resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
+
+ nanoid@3.3.8:
+ resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ natural-compare@1.4.0:
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+ natural-orderby@5.0.0:
+ resolution: {integrity: sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==}
+ engines: {node: '>=18'}
+
+ negotiator@0.6.3:
+ resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
+ engines: {node: '>= 0.6'}
+
+ neo-async@2.6.2:
+ resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
+
+ next-tick@1.1.0:
+ resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
+
+ node-fetch-h2@2.3.0:
+ resolution: {integrity: sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg==}
+ engines: {node: 4.x || >=6.0.0}
+
+ node-fetch-native@1.6.6:
+ resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==}
+
+ node-fetch@2.7.0:
+ resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
+ engines: {node: 4.x || >=6.0.0}
+ peerDependencies:
+ encoding: ^0.1.0
+ peerDependenciesMeta:
+ encoding:
+ optional: true
+
+ node-int64@0.4.0:
+ resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
+
+ node-readfiles@0.2.0:
+ resolution: {integrity: sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==}
+
+ node-releases@2.0.18:
+ resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==}
+
+ node-releases@2.0.19:
+ resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
+
+ normalize-path@3.0.0:
+ resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+ engines: {node: '>=0.10.0'}
+
+ normalize-range@0.1.2:
+ resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
+ engines: {node: '>=0.10.0'}
+
+ normalize-url@6.1.0:
+ resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==}
+ engines: {node: '>=10'}
+
+ npm-run-path@4.0.1:
+ resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
+ engines: {node: '>=8'}
+
+ npm-run-path@5.3.0:
+ resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+ nth-check@2.1.1:
+ resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+
+ nunjucks@3.2.4:
+ resolution: {integrity: sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==}
+ engines: {node: '>= 6.9.0'}
+ hasBin: true
+ peerDependencies:
+ chokidar: ^3.3.0
+ peerDependenciesMeta:
+ chokidar:
+ optional: true
+
+ nwsapi@2.2.20:
+ resolution: {integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==}
+
+ nypm@0.5.4:
+ resolution: {integrity: sha512-X0SNNrZiGU8/e/zAB7sCTtdxWTMSIO73q+xuKgglm2Yvzwlo8UoC5FNySQFCvl84uPaeADkqHUZUkWy4aH4xOA==}
+ engines: {node: ^14.16.0 || >=16.10.0}
+ hasBin: true
+
+ oas-kit-common@1.0.8:
+ resolution: {integrity: sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ==}
+
+ oas-linter@3.2.2:
+ resolution: {integrity: sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==}
+
+ oas-resolver@2.5.6:
+ resolution: {integrity: sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==}
+ hasBin: true
+
+ oas-schema-walker@1.1.5:
+ resolution: {integrity: sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ==}
+
+ oas-validator@5.0.8:
+ resolution: {integrity: sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw==}
+
+ object-hash@3.0.0:
+ resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
+ engines: {node: '>= 6'}
+
+ object-inspect@1.13.3:
+ resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==}
+ engines: {node: '>= 0.4'}
+
+ ofetch@1.4.1:
+ resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==}
+
+ ohash@1.1.4:
+ resolution: {integrity: sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g==}
+
+ ohash@2.0.11:
+ resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==}
+
+ omggif@1.0.10:
+ resolution: {integrity: sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==}
+
+ on-finished@2.4.1:
+ resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
+ engines: {node: '>= 0.8'}
+
+ once@1.4.0:
+ resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+
+ onetime@5.1.2:
+ resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
+ engines: {node: '>=6'}
+
+ onetime@6.0.0:
+ resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
+ engines: {node: '>=12'}
+
+ onetime@7.0.0:
+ resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
+ engines: {node: '>=18'}
+
+ open@8.4.2:
+ resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
+ engines: {node: '>=12'}
+
+ openapi-ts-request@1.1.2:
+ resolution: {integrity: sha512-Hzqm3JzxgzyGGrTv1THo21HqDea215IQq4I3Ic/WMh/Y+SbmeLb7VQmdeIORPP3Wx836qlTm3X0H10ignkPqfQ==}
+ engines: {node: '>=18.0.0', pnpm: '>=9'}
+ hasBin: true
+
+ openapi-types@12.1.3:
+ resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
+
+ optionator@0.9.4:
+ resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+ engines: {node: '>= 0.8.0'}
+
+ ora@5.4.1:
+ resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==}
+ engines: {node: '>=10'}
+
+ os-locale-s-fix@1.0.8-fix-1:
+ resolution: {integrity: sha512-Sv0OvhPiMutICiwORAUefv02DCPb62IelBmo8ZsSrRHyI3FStqIWZvjqDkvtjU+lcujo7UNir+dCwKSqlEQ/5w==}
+ engines: {node: '>=10', yarn: ^1.22.4}
+
+ p-cancelable@2.1.1:
+ resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==}
+ engines: {node: '>=8'}
+
+ p-limit@2.3.0:
+ resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
+ engines: {node: '>=6'}
+
+ p-limit@3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+
+ p-limit@4.0.0:
+ resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+ p-locate@4.1.0:
+ resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
+ engines: {node: '>=8'}
+
+ p-locate@5.0.0:
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+ engines: {node: '>=10'}
+
+ p-locate@6.0.0:
+ resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+ p-try@2.2.0:
+ resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
+ engines: {node: '>=6'}
+
+ package-json-from-dist@1.0.1:
+ resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
+
+ package-manager-detector@1.3.0:
+ resolution: {integrity: sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==}
+
+ pako@1.0.11:
+ resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
+
+ parent-module@1.0.1:
+ resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+ engines: {node: '>=6'}
+
+ parse-bmfont-ascii@1.0.6:
+ resolution: {integrity: sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==}
+
+ parse-bmfont-binary@1.0.6:
+ resolution: {integrity: sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==}
+
+ parse-bmfont-xml@1.1.6:
+ resolution: {integrity: sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==}
+
+ parse-css-font@4.0.0:
+ resolution: {integrity: sha512-lnY7dTUfjRXsSo5G5C639L8RaBBaVSgL+5hacIFKsNHzeCJQ5SFSZv1DZmc7+wZv/22PFGOq2YbaEHLdaCS/mQ==}
+
+ parse-gitignore@2.0.0:
+ resolution: {integrity: sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog==}
+ engines: {node: '>=14'}
+
+ parse-headers@2.0.5:
+ resolution: {integrity: sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==}
+
+ parse-imports-exports@0.2.4:
+ resolution: {integrity: sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==}
+
+ parse-json@5.2.0:
+ resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
+ engines: {node: '>=8'}
+
+ parse-statements@1.0.11:
+ resolution: {integrity: sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==}
+
+ parse5@6.0.1:
+ resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
+
+ parseurl@1.3.3:
+ resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
+ engines: {node: '>= 0.8'}
+
+ path-browserify@1.0.1:
+ resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
+
+ path-exists@4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
+
+ path-exists@5.0.0:
+ resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+ path-is-absolute@1.0.1:
+ resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+ engines: {node: '>=0.10.0'}
+
+ path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+
+ path-key@4.0.0:
+ resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
+ engines: {node: '>=12'}
+
+ path-parse@1.0.7:
+ resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
+ path-scurry@2.0.0:
+ resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==}
+ engines: {node: 20 || >=22}
+
+ path-to-regexp@0.1.10:
+ resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==}
+
+ pathe@1.1.2:
+ resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
+
+ pathe@2.0.3:
+ resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
+
+ perfect-debounce@1.0.0:
+ resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
+
+ phin@2.9.3:
+ resolution: {integrity: sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==}
+ deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
+
+ phin@3.7.1:
+ resolution: {integrity: sha512-GEazpTWwTZaEQ9RhL7Nyz0WwqilbqgLahDM3D0hxWwmVDI52nXEybHqiN6/elwpkJBhcuj+WbBu+QfT0uhPGfQ==}
+ engines: {node: '>= 8'}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+
+ picomatch@4.0.2:
+ resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
+ engines: {node: '>=12'}
+
+ pidtree@0.6.0:
+ resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==}
+ engines: {node: '>=0.10'}
+ hasBin: true
+
+ pify@2.3.0:
+ resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
+ engines: {node: '>=0.10.0'}
+
+ pinia-plugin-persistedstate@3.2.1:
+ resolution: {integrity: sha512-MK++8LRUsGF7r45PjBFES82ISnPzyO6IZx3CH5vyPseFLZCk1g2kgx6l/nW8pEBKxxd4do0P6bJw+mUSZIEZUQ==}
+ peerDependencies:
+ pinia: ^2.0.0
+
+ pinia@2.0.36:
+ resolution: {integrity: sha512-4UKApwjlmJH+VuHKgA+zQMddcCb3ezYnyewQ9NVrsDqZ/j9dMv5+rh+1r48whKNdpFkZAWVxhBp5ewYaYX9JcQ==}
+ peerDependencies:
+ '@vue/composition-api': ^1.4.0
+ typescript: '>=4.4.4'
+ vue: ^2.6.14 || ^3.2.0
+ peerDependenciesMeta:
+ '@vue/composition-api':
+ optional: true
+ typescript:
+ optional: true
+
+ pirates@4.0.7:
+ resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
+ engines: {node: '>= 6'}
+
+ pixelmatch@4.0.2:
+ resolution: {integrity: sha512-J8B6xqiO37sU/gkcMglv6h5Jbd9xNER7aHzpfRdNmV4IbQBzBpe4l9XmbG+xPF/znacgu2jfEw+wHffaq/YkXA==}
+ hasBin: true
+
+ pkg-dir@4.2.0:
+ resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
+ engines: {node: '>=8'}
+
+ pkg-types@1.2.1:
+ resolution: {integrity: sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==}
+
+ pkg-types@1.3.1:
+ resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
+
+ pkg-types@2.1.0:
+ resolution: {integrity: sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==}
+
+ pluralize@8.0.0:
+ resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
+ engines: {node: '>=4'}
+
+ pngjs@3.4.0:
+ resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==}
+ engines: {node: '>=4.0.0'}
+
+ pnpm-workspace-yaml@0.3.1:
+ resolution: {integrity: sha512-3nW5RLmREmZ8Pm8MbPsO2RM+99RRjYd25ynj3NV0cFsN7CcEl4sDFzgoFmSyduFwxFQ2Qbu3y2UdCh6HlyUOeA==}
+
+ postcss-html@1.7.0:
+ resolution: {integrity: sha512-MfcMpSUIaR/nNgeVS8AyvyDugXlADjN9AcV7e5rDfrF1wduIAGSkL4q2+wgrZgA3sHVAHLDO9FuauHhZYW2nBw==}
+ engines: {node: ^12 || >=14}
+
+ postcss-import@14.1.0:
+ resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ postcss: ^8.0.0
+
+ postcss-load-config@3.1.4:
+ resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==}
+ engines: {node: '>= 10'}
+ peerDependencies:
+ postcss: '>=8.0.9'
+ ts-node: '>=9.0.0'
+ peerDependenciesMeta:
+ postcss:
+ optional: true
+ ts-node:
+ optional: true
+
+ postcss-modules-extract-imports@3.1.0:
+ resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==}
+ engines: {node: ^10 || ^12 || >= 14}
+ peerDependencies:
+ postcss: ^8.1.0
+
+ postcss-modules-local-by-default@4.1.0:
+ resolution: {integrity: sha512-rm0bdSv4jC3BDma3s9H19ZddW0aHX6EoqwDYU2IfZhRN+53QrufTRo2IdkAbRqLx4R2IYbZnbjKKxg4VN5oU9Q==}
+ engines: {node: ^10 || ^12 || >= 14}
+ peerDependencies:
+ postcss: ^8.1.0
+
+ postcss-modules-scope@3.2.1:
+ resolution: {integrity: sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==}
+ engines: {node: ^10 || ^12 || >= 14}
+ peerDependencies:
+ postcss: ^8.1.0
+
+ postcss-modules-values@4.0.0:
+ resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==}
+ engines: {node: ^10 || ^12 || >= 14}
+ peerDependencies:
+ postcss: ^8.1.0
+
+ postcss-modules@4.3.1:
+ resolution: {integrity: sha512-ItUhSUxBBdNamkT3KzIZwYNNRFKmkJrofvC2nWab3CPKhYBQ1f27XXh1PAPE27Psx58jeelPsxWB/+og+KEH0Q==}
+ peerDependencies:
+ postcss: ^8.0.0
+
+ postcss-safe-parser@6.0.0:
+ resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==}
+ engines: {node: '>=12.0'}
+ peerDependencies:
+ postcss: ^8.3.3
+
+ postcss-scss@4.0.9:
+ resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==}
+ engines: {node: '>=12.0'}
+ peerDependencies:
+ postcss: ^8.4.29
+
+ postcss-selector-parser@6.1.2:
+ resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
+ engines: {node: '>=4'}
+
+ postcss-selector-parser@7.0.0:
+ resolution: {integrity: sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==}
+ engines: {node: '>=4'}
+
+ postcss-value-parser@4.2.0:
+ resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
+
+ postcss@8.5.3:
+ resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ prelude-ls@1.2.1:
+ resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+ engines: {node: '>= 0.8.0'}
+
+ prettier-linter-helpers@1.0.0:
+ resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
+ engines: {node: '>=6.0.0'}
+
+ prettier@3.5.3:
+ resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ pretty-format@27.5.1:
+ resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ process@0.11.10:
+ resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
+ engines: {node: '>= 0.6.0'}
+
+ prompts@2.4.2:
+ resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
+ engines: {node: '>= 6'}
+
+ proxy-addr@2.0.7:
+ resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
+ engines: {node: '>= 0.10'}
+
+ proxy-from-env@1.1.0:
+ resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
+
+ psl@1.15.0:
+ resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==}
+
+ pump@3.0.2:
+ resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==}
+
+ punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
+ qrcode-reader@1.0.4:
+ resolution: {integrity: sha512-rRjALGNh9zVqvweg1j5OKIQKNsw3bLC+7qwlnead5K/9cb1cEIAGkwikt/09U0K+2IDWGD9CC6SP7tHAjUeqvQ==}
+
+ qrcode-terminal@0.12.0:
+ resolution: {integrity: sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==}
+ hasBin: true
+
+ qs@6.13.0:
+ resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
+ engines: {node: '>=0.6'}
+
+ quansync@0.2.10:
+ resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==}
+
+ querystringify@2.2.0:
+ resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
+
+ queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+ quick-lru@5.1.1:
+ resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==}
+ engines: {node: '>=10'}
+
+ range-parser@1.2.1:
+ resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
+ engines: {node: '>= 0.6'}
+
+ rate-limiter-flexible@5.0.5:
+ resolution: {integrity: sha512-+/dSQfo+3FYwYygUs/V2BBdwGa9nFtakDwKt4l0bnvNB53TNT++QSFewwHX9qXrZJuMe9j+TUaU21lm5ARgqdQ==}
+
+ raw-body@2.5.2:
+ resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
+ engines: {node: '>= 0.8'}
+
+ rc9@2.1.2:
+ resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==}
+
+ react-is@17.0.2:
+ resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
+
+ read-cache@1.0.0:
+ resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
+
+ readable-stream@3.6.2:
+ resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
+ engines: {node: '>= 6'}
+
+ readdirp@3.6.0:
+ resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+ engines: {node: '>=8.10.0'}
+
+ readdirp@4.1.2:
+ resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
+ engines: {node: '>= 14.18.0'}
+
+ refa@0.12.1:
+ resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ reftools@1.1.9:
+ resolution: {integrity: sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==}
+
+ regenerate-unicode-properties@10.2.0:
+ resolution: {integrity: sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==}
+ engines: {node: '>=4'}
+
+ regenerate@1.4.2:
+ resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==}
+
+ regenerator-runtime@0.13.11:
+ resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
+
+ regenerator-runtime@0.14.1:
+ resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
+
+ regenerator-transform@0.15.2:
+ resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==}
+
+ regexp-ast-analysis@0.7.1:
+ resolution: {integrity: sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ regexp-tree@0.1.27:
+ resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==}
+ hasBin: true
+
+ regexpu-core@6.2.0:
+ resolution: {integrity: sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==}
+ engines: {node: '>=4'}
+
+ regjsgen@0.8.0:
+ resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==}
+
+ regjsparser@0.12.0:
+ resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==}
+ hasBin: true
+
+ remove-accents@0.5.0:
+ resolution: {integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==}
+
+ require-directory@2.1.1:
+ resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
+ engines: {node: '>=0.10.0'}
+
+ require-from-string@2.0.2:
+ resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+ engines: {node: '>=0.10.0'}
+
+ requires-port@1.0.0:
+ resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
+
+ reserved-words@0.1.2:
+ resolution: {integrity: sha512-0S5SrIUJ9LfpbVl4Yzij6VipUdafHrOTzvmfazSw/jeZrZtQK303OPZW+obtkaw7jQlTQppy0UvZWm9872PbRw==}
+
+ resolve-alpn@1.2.1:
+ resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}
+
+ resolve-cwd@3.0.0:
+ resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==}
+ engines: {node: '>=8'}
+
+ resolve-from@4.0.0:
+ resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+ engines: {node: '>=4'}
+
+ resolve-from@5.0.0:
+ resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
+ engines: {node: '>=8'}
+
+ resolve-pkg-maps@1.0.0:
+ resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
+
+ resolve.exports@1.1.1:
+ resolution: {integrity: sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==}
+ engines: {node: '>=10'}
+
+ resolve@1.22.10:
+ resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
+ engines: {node: '>= 0.4'}
+ hasBin: true
+
+ responselike@2.0.1:
+ resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==}
+
+ restore-cursor@3.1.0:
+ resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
+ engines: {node: '>=8'}
+
+ restore-cursor@5.1.0:
+ resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==}
+ engines: {node: '>=18'}
+
+ reusify@1.0.4:
+ resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
+ engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+ rfdc@1.4.1:
+ resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
+
+ rimraf@3.0.2:
+ resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
+ deprecated: Rimraf versions prior to v4 are no longer supported
+ hasBin: true
+
+ rimraf@6.0.1:
+ resolution: {integrity: sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==}
+ engines: {node: 20 || >=22}
+ hasBin: true
+
+ rollup-plugin-visualizer@5.12.0:
+ resolution: {integrity: sha512-8/NU9jXcHRs7Nnj07PF2o4gjxmm9lXIrZ8r175bT9dK8qoLlvKTwRMArRCMgpMGlq8CTLugRvEmyMeMXIU2pNQ==}
+ engines: {node: '>=14'}
+ hasBin: true
+ peerDependencies:
+ rollup: 2.x || 3.x || 4.x
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+
+ rollup@4.41.1:
+ resolution: {integrity: sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
+ run-parallel@1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+ safe-area-insets@1.4.1:
+ resolution: {integrity: sha512-r/nRWTjFGhhm3w1Z6Kd/jY11srN+lHt2mNl1E/emQGW8ic7n3Avu4noibklfSM+Y34peNphHD/BSZecav0sXYQ==}
+
+ safe-buffer@5.2.1:
+ resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+
+ safer-buffer@2.1.2:
+ resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+ sass@1.77.8:
+ resolution: {integrity: sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==}
+ engines: {node: '>=14.0.0'}
+ hasBin: true
+
+ sax@1.4.1:
+ resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
+
+ saxes@5.0.1:
+ resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==}
+ engines: {node: '>=10'}
+
+ scslre@0.3.0:
+ resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==}
+ engines: {node: ^14.0.0 || >=16.0.0}
+
+ scule@1.3.0:
+ resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==}
+
+ semver@6.3.1:
+ resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+ hasBin: true
+
+ semver@7.7.2:
+ resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ send@0.19.0:
+ resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==}
+ engines: {node: '>= 0.8.0'}
+
+ serve-static@1.16.2:
+ resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==}
+ engines: {node: '>= 0.8.0'}
+
+ set-function-length@1.2.2:
+ resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
+ engines: {node: '>= 0.4'}
+
+ setprototypeof@1.2.0:
+ resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
+
+ shebang-command@2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+
+ shebang-regex@3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+
+ should-equal@2.0.0:
+ resolution: {integrity: sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==}
+
+ should-format@3.0.3:
+ resolution: {integrity: sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==}
+
+ should-type-adaptors@1.1.0:
+ resolution: {integrity: sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==}
+
+ should-type@1.4.0:
+ resolution: {integrity: sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==}
+
+ should-util@1.0.1:
+ resolution: {integrity: sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==}
+
+ should@13.2.3:
+ resolution: {integrity: sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==}
+
+ side-channel@1.0.6:
+ resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==}
+ engines: {node: '>= 0.4'}
+
+ signal-exit@3.0.7:
+ resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
+
+ signal-exit@4.1.0:
+ resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+ engines: {node: '>=14'}
+
+ sirv@3.0.1:
+ resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==}
+ engines: {node: '>=18'}
+
+ sisteransi@1.0.5:
+ resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
+
+ slash@3.0.0:
+ resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
+ engines: {node: '>=8'}
+
+ slice-ansi@5.0.0:
+ resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==}
+ engines: {node: '>=12'}
+
+ slice-ansi@7.1.0:
+ resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==}
+ engines: {node: '>=18'}
+
+ sm-crypto@0.3.14:
+ resolution: {integrity: sha512-sR7NuGAJH93Om8keAF2/rj1EqFzEiUey4aKGyDo3nQ2BiVqhgq8UkiIoIp5qhV9w4pG7qArhfqyewcG8AbhXbg==}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ source-map-support@0.5.21:
+ resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
+
+ source-map@0.6.1:
+ resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+ engines: {node: '>=0.10.0'}
+
+ source-map@0.7.4:
+ resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
+ engines: {node: '>= 8'}
+
+ spdx-exceptions@2.5.0:
+ resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==}
+
+ spdx-expression-parse@4.0.0:
+ resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==}
+
+ spdx-license-ids@3.0.21:
+ resolution: {integrity: sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==}
+
+ split2@4.2.0:
+ resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
+ engines: {node: '>= 10.x'}
+
+ sprintf-js@1.0.3:
+ resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
+
+ stack-utils@2.0.6:
+ resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
+ engines: {node: '>=10'}
+
+ statuses@2.0.1:
+ resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
+ engines: {node: '>= 0.8'}
+
+ std-env@3.8.0:
+ resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==}
+
+ string-argv@0.3.2:
+ resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
+ engines: {node: '>=0.6.19'}
+
+ string-hash@1.1.3:
+ resolution: {integrity: sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==}
+
+ string-length@4.0.2:
+ resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==}
+ engines: {node: '>=10'}
+
+ string-width@4.2.3:
+ resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+ engines: {node: '>=8'}
+
+ string-width@5.1.2:
+ resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
+ engines: {node: '>=12'}
+
+ string-width@7.2.0:
+ resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
+ engines: {node: '>=18'}
+
+ string_decoder@1.3.0:
+ resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
+
+ strip-ansi@6.0.1:
+ resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+ engines: {node: '>=8'}
+
+ strip-ansi@7.1.0:
+ resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
+ engines: {node: '>=12'}
+
+ strip-bom@4.0.0:
+ resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==}
+ engines: {node: '>=8'}
+
+ strip-final-newline@2.0.0:
+ resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
+ engines: {node: '>=6'}
+
+ strip-final-newline@3.0.0:
+ resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
+ engines: {node: '>=12'}
+
+ strip-indent@4.0.0:
+ resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==}
+ engines: {node: '>=12'}
+
+ strip-json-comments@3.1.1:
+ resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+ engines: {node: '>=8'}
+
+ strip-literal@2.1.1:
+ resolution: {integrity: sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==}
+
+ strip-literal@3.0.0:
+ resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==}
+
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
+ supports-color@8.1.1:
+ resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
+ engines: {node: '>=10'}
+
+ supports-hyperlinks@2.3.0:
+ resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==}
+ engines: {node: '>=8'}
+
+ supports-preserve-symlinks-flag@1.0.0:
+ resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+ engines: {node: '>= 0.4'}
+
+ svg-tags@1.0.0:
+ resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==}
+
+ swagger2openapi@7.0.8:
+ resolution: {integrity: sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==}
+ hasBin: true
+
+ symbol-tree@3.2.4:
+ resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+
+ synckit@0.11.8:
+ resolution: {integrity: sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+
+ synckit@0.9.3:
+ resolution: {integrity: sha512-JJoOEKTfL1urb1mDoEblhD9NhEbWmq9jHEMEnxoC4ujUaZ4itA8vKgwkFAyNClgxplLi9tsUKX+EduK0p/l7sg==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+
+ systemjs@6.15.1:
+ resolution: {integrity: sha512-Nk8c4lXvMB98MtbmjX7JwJRgJOL8fluecYCfCeYBznwmpOs8Bf15hLM6z4z71EDAhQVrQrI+wt1aLWSXZq+hXA==}
+
+ tapable@2.2.1:
+ resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
+ engines: {node: '>=6'}
+
+ tar@6.2.1:
+ resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
+ engines: {node: '>=10'}
+
+ terminal-link@2.1.1:
+ resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==}
+ engines: {node: '>=8'}
+
+ terser@5.36.0:
+ resolution: {integrity: sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ test-exclude@6.0.0:
+ resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
+ engines: {node: '>=8'}
+
+ text-extensions@2.4.0:
+ resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==}
+ engines: {node: '>=8'}
+
+ throat@6.0.2:
+ resolution: {integrity: sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==}
+
+ through@2.3.8:
+ resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
+
+ timers-ext@0.1.8:
+ resolution: {integrity: sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==}
+ engines: {node: '>=0.12'}
+
+ timm@1.7.1:
+ resolution: {integrity: sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw==}
+
+ tiny-pinyin@1.3.2:
+ resolution: {integrity: sha512-uHNGu4evFt/8eNLldazeAM1M8JrMc1jshhJJfVRARTN3yT8HEEibofeQ7QETWQ5ISBjd6fKtTVBCC/+mGS6FpA==}
+
+ tinycolor2@1.6.0:
+ resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==}
+
+ tinyexec@0.3.2:
+ resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
+
+ tinyexec@1.0.1:
+ resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==}
+
+ tinyglobby@0.2.10:
+ resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==}
+ engines: {node: '>=12.0.0'}
+
+ tinyglobby@0.2.14:
+ resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
+ engines: {node: '>=12.0.0'}
+
+ tmpl@1.0.5:
+ resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
+
+ to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+
+ toidentifier@1.0.1:
+ resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
+ engines: {node: '>=0.6'}
+
+ toml-eslint-parser@0.10.0:
+ resolution: {integrity: sha512-khrZo4buq4qVmsGzS5yQjKe/WsFvV8fGfOjDQN0q4iy9FjRfPWRgTFrU8u1R2iu/SfWLhY9WnCi4Jhdrcbtg+g==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ totalist@3.0.1:
+ resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
+ engines: {node: '>=6'}
+
+ tough-cookie@4.1.4:
+ resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==}
+ engines: {node: '>=6'}
+
+ tr46@0.0.3:
+ resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
+
+ tr46@2.1.0:
+ resolution: {integrity: sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==}
+ engines: {node: '>=8'}
+
+ ts-api-utils@2.1.0:
+ resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==}
+ engines: {node: '>=18.12'}
+ peerDependencies:
+ typescript: '>=4.8.4'
+
+ ts-declaration-location@1.0.7:
+ resolution: {integrity: sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==}
+ peerDependencies:
+ typescript: '>=4.0.0'
+
+ tslib@2.8.1:
+ resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+ tsx@4.19.4:
+ resolution: {integrity: sha512-gK5GVzDkJK1SI1zwHf32Mqxf2tSJkNx+eYcNly5+nHvWqXUJYUkWBQtKauoESz3ymezAI++ZwT855x5p5eop+Q==}
+ engines: {node: '>=18.0.0'}
+ hasBin: true
+
+ type-check@0.4.0:
+ resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+ engines: {node: '>= 0.8.0'}
+
+ type-detect@4.0.8:
+ resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
+ engines: {node: '>=4'}
+
+ type-fest@0.21.3:
+ resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
+ engines: {node: '>=10'}
+
+ type-is@1.6.18:
+ resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
+ engines: {node: '>= 0.6'}
+
+ type@2.7.3:
+ resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==}
+
+ typedarray-to-buffer@3.1.5:
+ resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==}
+
+ typescript@5.7.2:
+ resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ ufo@1.5.4:
+ resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==}
+
+ ufo@1.6.1:
+ resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==}
+
+ uglify-js@3.19.3:
+ resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==}
+ engines: {node: '>=0.8.0'}
+ hasBin: true
+
+ unconfig@0.5.5:
+ resolution: {integrity: sha512-VQZ5PT9HDX+qag0XdgQi8tJepPhXiR/yVOkn707gJDKo31lGjRilPREiQJ9Z6zd/Ugpv6ZvO5VxVIcatldYcNQ==}
+
+ unconfig@0.6.1:
+ resolution: {integrity: sha512-cVU+/sPloZqOyJEAfNwnQSFCzFrZm85vcVkryH7lnlB/PiTycUkAjt5Ds79cfIshGOZ+M5v3PBDnKgpmlE5DtA==}
+
+ unconfig@7.3.2:
+ resolution: {integrity: sha512-nqG5NNL2wFVGZ0NA/aCFw0oJ2pxSf1lwg4Z5ill8wd7K4KX/rQbHlwbh+bjctXL5Ly1xtzHenHGOK0b+lG6JVg==}
+
+ undici-types@6.21.0:
+ resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
+
+ unicode-canonical-property-names-ecmascript@2.0.1:
+ resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==}
+ engines: {node: '>=4'}
+
+ unicode-match-property-ecmascript@2.0.0:
+ resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==}
+ engines: {node: '>=4'}
+
+ unicode-match-property-value-ecmascript@2.2.0:
+ resolution: {integrity: sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==}
+ engines: {node: '>=4'}
+
+ unicode-property-aliases-ecmascript@2.1.0:
+ resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==}
+ engines: {node: '>=4'}
+
+ unicorn-magic@0.1.0:
+ resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==}
+ engines: {node: '>=18'}
+
+ unimport@3.14.3:
+ resolution: {integrity: sha512-yEJps4GW7jBdoQlxEV0ElBCJsJmH8FdZtk4oog0y++8hgLh0dGnDpE4oaTc0Lfx4N5rRJiGFUWHrBqC8CyUBmQ==}
+
+ unimport@4.1.1:
+ resolution: {integrity: sha512-j9+fijH6aDd05yv1fXlyt7HSxtOWtGtrZeYTVBsSUg57Iuf+Ps2itIZjeyu7bEQ4k0WOgYhHrdW8m/pJgOpl5g==}
+ engines: {node: '>=18.12.0'}
+
+ unist-util-is@6.0.0:
+ resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==}
+
+ unist-util-stringify-position@4.0.0:
+ resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
+
+ unist-util-visit-parents@6.0.1:
+ resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==}
+
+ unist-util-visit@5.0.0:
+ resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
+
+ universalify@0.2.0:
+ resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
+ engines: {node: '>= 4.0.0'}
+
+ universalify@2.0.1:
+ resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
+ engines: {node: '>= 10.0.0'}
+
+ unocss-applet@0.10.0:
+ resolution: {integrity: sha512-heFDDR2ETuxrzNagAr6K5rrLChQwj4RD0rXdzjF6DD30DafdAqJXaPJ2ZGzXOZK6HW4F0yUc1cJsaVM5N6vroA==}
+ peerDependencies:
+ unocss: '>=66.0.0'
+
+ unocss@65.4.2:
+ resolution: {integrity: sha512-fAmolcpWyU9TlYw04cXu1ba4+lxh/PKjT5xKEAobWCTmwkS+yQDJ3LrEkqfGvry2EJr2os+/qhQm1lAx/0o7Ww==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@unocss/webpack': 65.4.2
+ vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0
+ peerDependenciesMeta:
+ '@unocss/webpack':
+ optional: true
+ vite:
+ optional: true
+
+ unpipe@1.0.0:
+ resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
+ engines: {node: '>= 0.8'}
+
+ unplugin-auto-import@0.16.7:
+ resolution: {integrity: sha512-w7XmnRlchq6YUFJVFGSvG1T/6j8GrdYN6Em9Wf0Ye+HXgD/22kont+WnuCAA0UaUoxtuvRR1u/mXKy63g/hfqQ==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@nuxt/kit': ^3.2.2
+ '@vueuse/core': '*'
+ peerDependenciesMeta:
+ '@nuxt/kit':
+ optional: true
+ '@vueuse/core':
+ optional: true
+
+ unplugin-auto-import@0.17.8:
+ resolution: {integrity: sha512-CHryj6HzJ+n4ASjzwHruD8arhbdl+UXvhuAIlHDs15Y/IMecG3wrf7FVg4pVH/DIysbq/n0phIjNHAjl7TG7Iw==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@nuxt/kit': ^3.2.2
+ '@vueuse/core': '*'
+ peerDependenciesMeta:
+ '@nuxt/kit':
+ optional: true
+ '@vueuse/core':
+ optional: true
+
+ unplugin-auto-import@19.1.0:
+ resolution: {integrity: sha512-B+TGBEBHqY9aR+7YfShfLujETOHstzpV+yaqgy5PkfV0QG7Py+TYMX7vJ9W4SrysHR+UzR+gzcx/nuZjmPeclA==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@nuxt/kit': ^3.2.2
+ '@vueuse/core': '*'
+ peerDependenciesMeta:
+ '@nuxt/kit':
+ optional: true
+ '@vueuse/core':
+ optional: true
+
+ unplugin-utils@0.2.4:
+ resolution: {integrity: sha512-8U/MtpkPkkk3Atewj1+RcKIjb5WBimZ/WSLhhR3w6SsIj8XJuKTacSP8g+2JhfSGw0Cb125Y+2zA/IzJZDVbhA==}
+ engines: {node: '>=18.12.0'}
+
+ unplugin@1.16.0:
+ resolution: {integrity: sha512-5liCNPuJW8dqh3+DM6uNM2EI3MLLpCKp/KY+9pB5M2S2SR2qvvDHhKgBOaTWEbZTAws3CXfB0rKTIolWKL05VQ==}
+ engines: {node: '>=14.0.0'}
+
+ unplugin@2.3.4:
+ resolution: {integrity: sha512-m4PjxTurwpWfpMomp8AptjD5yj8qEZN5uQjjGM3TAs9MWWD2tXSSNNj6jGR2FoVGod4293ytyV6SwBbertfyJg==}
+ engines: {node: '>=18.12.0'}
+
+ unquote@1.1.1:
+ resolution: {integrity: sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==}
+
+ update-browserslist-db@1.1.1:
+ resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
+ update-browserslist-db@1.1.3:
+ resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
+ uri-js@4.4.1:
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+ url-parse@1.5.10:
+ resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
+
+ utif@2.0.1:
+ resolution: {integrity: sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg==}
+
+ util-deprecate@1.0.2:
+ resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+ utils-merge@1.0.1:
+ resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
+ engines: {node: '>= 0.4.0'}
+
+ v8-to-istanbul@8.1.1:
+ resolution: {integrity: sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==}
+ engines: {node: '>=10.12.0'}
+
+ vary@1.1.2:
+ resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
+ engines: {node: '>= 0.8'}
+
+ vite-plugin-restart@0.4.2:
+ resolution: {integrity: sha512-9aWN2ScJ8hbT7aC8SDeZnsbWapnslz1vhNq6Vgf2GU9WdN4NExlrWhtnu7pmtOUG3Guj8y6lPcUZ+ls7SVP33w==}
+ peerDependencies:
+ vite: ^2.9.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0
+
+ vite@5.2.8:
+ resolution: {integrity: sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^18.0.0 || >=20.0.0
+ less: '*'
+ lightningcss: ^1.21.0
+ sass: '*'
+ stylus: '*'
+ sugarss: '*'
+ terser: ^5.4.0
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+
+ vscode-uri@3.1.0:
+ resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
+
+ vue-demi@0.14.10:
+ resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
+ engines: {node: '>=12'}
+ hasBin: true
+ peerDependencies:
+ '@vue/composition-api': ^1.0.0-rc.1
+ vue: ^3.0.0-0 || ^2.6.0
+ peerDependenciesMeta:
+ '@vue/composition-api':
+ optional: true
+
+ vue-eslint-parser@10.1.3:
+ resolution: {integrity: sha512-dbCBnd2e02dYWsXoqX5yKUZlOt+ExIpq7hmHKPb5ZqKcjf++Eo0hMseFTZMLKThrUk61m+Uv6A2YSBve6ZvuDQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+
+ vue-flow-layout@0.1.1:
+ resolution: {integrity: sha512-JdgRRUVrN0Y2GosA0M68DEbKlXMqJ7FQgsK8CjQD2vxvNSqAU6PZEpi4cfcTVtfM2GVOMjHo7GKKLbXxOBqDqA==}
+ peerDependencies:
+ vue: ^3.4.37
+
+ vue-router@4.5.0:
+ resolution: {integrity: sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==}
+ peerDependencies:
+ vue: ^3.2.0
+
+ vue-tsc@2.2.10:
+ resolution: {integrity: sha512-jWZ1xSaNbabEV3whpIDMbjVSVawjAyW+x1n3JeGQo7S0uv2n9F/JMgWW90tGWNFRKya4YwKMZgCtr0vRAM7DeQ==}
+ hasBin: true
+ peerDependencies:
+ typescript: '>=5.0.0'
+
+ vue@3.5.15:
+ resolution: {integrity: sha512-aD9zK4rB43JAMK/5BmS4LdPiEp8Fdh8P1Ve/XNuMF5YRf78fCyPE6FUbQwcaWQ5oZ1R2CD9NKE0FFOVpMR7gEQ==}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ w3c-hr-time@1.0.2:
+ resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==}
+ deprecated: Use your platform's native performance.now() and performance.timeOrigin.
+
+ w3c-xmlserializer@2.0.0:
+ resolution: {integrity: sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==}
+ engines: {node: '>=10'}
+
+ walker@1.0.8:
+ resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
+
+ wcwidth@1.0.1:
+ resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
+
+ webidl-conversions@3.0.1:
+ resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
+
+ webidl-conversions@5.0.0:
+ resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==}
+ engines: {node: '>=8'}
+
+ webidl-conversions@6.1.0:
+ resolution: {integrity: sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==}
+ engines: {node: '>=10.4'}
+
+ webpack-virtual-modules@0.6.2:
+ resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
+
+ whatwg-encoding@1.0.5:
+ resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==}
+
+ whatwg-mimetype@2.3.0:
+ resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==}
+
+ whatwg-url@5.0.0:
+ resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
+
+ whatwg-url@8.7.0:
+ resolution: {integrity: sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==}
+ engines: {node: '>=10'}
+
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
+ word-wrap@1.2.5:
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+ engines: {node: '>=0.10.0'}
+
+ wordwrap@1.0.0:
+ resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==}
+
+ wot-design-uni@1.9.1:
+ resolution: {integrity: sha512-bZBt6xt5HfvmHBm76BZWBrvthHKeK7cbR//bbmv2ICjPRLXvD9KKHGZDZrq6C/j+DXHzA4UbcGer3iE8XcuVkw==}
+ engines: {HBuilderX: ^3.8.7}
+ peerDependencies:
+ vue: '>=3.2.47'
+
+ wrap-ansi@7.0.0:
+ resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+ engines: {node: '>=10'}
+
+ wrap-ansi@8.1.0:
+ resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
+ engines: {node: '>=12'}
+
+ wrap-ansi@9.0.0:
+ resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==}
+ engines: {node: '>=18'}
+
+ wrappy@1.0.2:
+ resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+
+ write-file-atomic@3.0.3:
+ resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==}
+
+ ws@7.5.10:
+ resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==}
+ engines: {node: '>=8.3.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: ^5.0.2
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ ws@8.18.0:
+ resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ xhr@2.6.0:
+ resolution: {integrity: sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==}
+
+ xml-name-validator@3.0.0:
+ resolution: {integrity: sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==}
+
+ xml-name-validator@4.0.0:
+ resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
+ engines: {node: '>=12'}
+
+ xml-parse-from-string@1.0.1:
+ resolution: {integrity: sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==}
+
+ xml2js@0.5.0:
+ resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==}
+ engines: {node: '>=4.0.0'}
+
+ xmlbuilder@11.0.1:
+ resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==}
+ engines: {node: '>=4.0'}
+
+ xmlchars@2.2.0:
+ resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+
+ xmlhttprequest@1.8.0:
+ resolution: {integrity: sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==}
+ engines: {node: '>=0.4.0'}
+
+ xregexp@3.1.0:
+ resolution: {integrity: sha512-4Y1x6DyB8xRoxosooa6PlGWqmmSKatbzhrftZ7Purmm4B8R4qIEJG1A2hZsdz5DhmIqS0msC0I7KEq93GphEVg==}
+
+ xtend@4.0.2:
+ resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
+ engines: {node: '>=0.4'}
+
+ y18n@5.0.8:
+ resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
+ engines: {node: '>=10'}
+
+ yallist@3.1.1:
+ resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+ yallist@4.0.0:
+ resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+
+ yaml-eslint-parser@1.3.0:
+ resolution: {integrity: sha512-E/+VitOorXSLiAqtTd7Yqax0/pAS3xaYMP+AUUJGOK1OZG3rhcj9fcJOM5HJ2VrP1FrStVCWr1muTfQCdj4tAA==}
+ engines: {node: ^14.17.0 || >=16.0.0}
+
+ yaml@1.10.2:
+ resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
+ engines: {node: '>= 6'}
+
+ yaml@2.5.1:
+ resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==}
+ engines: {node: '>= 14'}
+ hasBin: true
+
+ yaml@2.6.1:
+ resolution: {integrity: sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==}
+ engines: {node: '>= 14'}
+ hasBin: true
+
+ yaml@2.8.0:
+ resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==}
+ engines: {node: '>= 14.6'}
+ hasBin: true
+
+ yargs-parser@20.2.9:
+ resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
+ engines: {node: '>=10'}
+
+ yargs-parser@21.1.1:
+ resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
+ engines: {node: '>=12'}
+
+ yargs@16.2.0:
+ resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
+ engines: {node: '>=10'}
+
+ yargs@17.7.2:
+ resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
+ engines: {node: '>=12'}
+
+ yocto-queue@0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+
+ yocto-queue@1.2.1:
+ resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==}
+ engines: {node: '>=12.20'}
+
+ z-paging@2.8.7:
+ resolution: {integrity: sha512-RcDeKFoCQB51dmrrTb1PMIazjTqGuAbFmjPS0/N5hdUNTCRvxGOOBTBFolvIxUcsWhrocI9C0mYDgUwXT6Dwcg==}
+ engines: {HBuilderX: ^3.0.7}
+
+ zwitch@2.0.4:
+ resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
+
+snapshots:
+
+ '@alova/adapter-uniapp@2.0.14(alova@3.3.3)':
+ dependencies:
+ '@alova/shared': 1.3.1
+ alova: 3.3.3
+
+ '@alova/shared@1.3.1': {}
+
+ '@alova/wormhole@1.0.8':
+ dependencies:
+ alova: 3.3.3
+ commander: 12.1.0
+ esbuild: 0.24.2
+ glob: 11.0.0
+ handlebars: 4.7.8
+ import-fresh: 3.3.0
+ js-yaml: 4.1.0
+ lodash: 4.17.21
+ openapi-types: 12.1.3
+ ora: 5.4.1
+ prettier: 3.5.3
+ swagger2openapi: 7.0.8
+ transitivePeerDependencies:
+ - encoding
+
+ '@ampproject/remapping@2.3.0':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.5
+ '@jridgewell/trace-mapping': 0.3.25
+
+ '@antfu/eslint-config@4.15.0(@unocss/eslint-plugin@66.2.3(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2))(@vue/compiler-sfc@3.5.15)(eslint-plugin-format@1.0.1(eslint@9.29.0(jiti@2.4.2)))(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)':
+ dependencies:
+ '@antfu/install-pkg': 1.1.0
+ '@clack/prompts': 0.11.0
+ '@eslint-community/eslint-plugin-eslint-comments': 4.5.0(eslint@9.29.0(jiti@2.4.2))
+ '@eslint/markdown': 6.6.0
+ '@stylistic/eslint-plugin': 5.0.0-beta.5(eslint@9.29.0(jiti@2.4.2))
+ '@typescript-eslint/eslint-plugin': 8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)
+ '@typescript-eslint/parser': 8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)
+ '@vitest/eslint-plugin': 1.2.7(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)
+ ansis: 4.1.0
+ cac: 6.7.14
+ eslint: 9.29.0(jiti@2.4.2)
+ eslint-config-flat-gitignore: 2.1.0(eslint@9.29.0(jiti@2.4.2))
+ eslint-flat-config-utils: 2.1.0
+ eslint-merge-processors: 2.0.0(eslint@9.29.0(jiti@2.4.2))
+ eslint-plugin-antfu: 3.1.1(eslint@9.29.0(jiti@2.4.2))
+ eslint-plugin-command: 3.3.0(eslint@9.29.0(jiti@2.4.2))
+ eslint-plugin-import-lite: 0.3.0(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)
+ eslint-plugin-jsdoc: 51.1.0(eslint@9.29.0(jiti@2.4.2))
+ eslint-plugin-jsonc: 2.20.1(eslint@9.29.0(jiti@2.4.2))
+ eslint-plugin-n: 17.20.0(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)
+ eslint-plugin-no-only-tests: 3.3.0
+ eslint-plugin-perfectionist: 4.15.0(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)
+ eslint-plugin-pnpm: 0.3.1(eslint@9.29.0(jiti@2.4.2))
+ eslint-plugin-regexp: 2.9.0(eslint@9.29.0(jiti@2.4.2))
+ eslint-plugin-toml: 0.12.0(eslint@9.29.0(jiti@2.4.2))
+ eslint-plugin-unicorn: 59.0.1(eslint@9.29.0(jiti@2.4.2))
+ eslint-plugin-unused-imports: 4.1.4(@typescript-eslint/eslint-plugin@8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.29.0(jiti@2.4.2))
+ eslint-plugin-vue: 10.2.0(eslint@9.29.0(jiti@2.4.2))(vue-eslint-parser@10.1.3(eslint@9.29.0(jiti@2.4.2)))
+ eslint-plugin-yml: 1.18.0(eslint@9.29.0(jiti@2.4.2))
+ eslint-processor-vue-blocks: 2.0.0(@vue/compiler-sfc@3.5.15)(eslint@9.29.0(jiti@2.4.2))
+ globals: 16.2.0
+ jsonc-eslint-parser: 2.4.0
+ local-pkg: 1.1.1
+ parse-gitignore: 2.0.0
+ toml-eslint-parser: 0.10.0
+ vue-eslint-parser: 10.1.3(eslint@9.29.0(jiti@2.4.2))
+ yaml-eslint-parser: 1.3.0
+ optionalDependencies:
+ '@unocss/eslint-plugin': 66.2.3(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)
+ eslint-plugin-format: 1.0.1(eslint@9.29.0(jiti@2.4.2))
+ transitivePeerDependencies:
+ - '@eslint/json'
+ - '@vue/compiler-sfc'
+ - supports-color
+ - typescript
+ - vitest
+
+ '@antfu/install-pkg@1.1.0':
+ dependencies:
+ package-manager-detector: 1.3.0
+ tinyexec: 1.0.1
+
+ '@antfu/utils@0.7.10': {}
+
+ '@antfu/utils@8.1.1': {}
+
+ '@babel/code-frame@7.27.1':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.27.1
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/compat-data@7.26.2': {}
+
+ '@babel/core@7.26.0':
+ dependencies:
+ '@ampproject/remapping': 2.3.0
+ '@babel/code-frame': 7.27.1
+ '@babel/generator': 7.27.1
+ '@babel/helper-compilation-targets': 7.25.9
+ '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0)
+ '@babel/helpers': 7.26.0
+ '@babel/parser': 7.27.2
+ '@babel/template': 7.27.2
+ '@babel/traverse': 7.27.1
+ '@babel/types': 7.27.1
+ convert-source-map: 2.0.0
+ debug: 4.4.1
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/generator@7.26.2':
+ dependencies:
+ '@babel/parser': 7.26.2
+ '@babel/types': 7.26.0
+ '@jridgewell/gen-mapping': 0.3.5
+ '@jridgewell/trace-mapping': 0.3.25
+ jsesc: 3.0.2
+
+ '@babel/generator@7.27.1':
+ dependencies:
+ '@babel/parser': 7.27.2
+ '@babel/types': 7.27.1
+ '@jridgewell/gen-mapping': 0.3.8
+ '@jridgewell/trace-mapping': 0.3.25
+ jsesc: 3.1.0
+
+ '@babel/generator@7.27.5':
+ dependencies:
+ '@babel/parser': 7.27.5
+ '@babel/types': 7.27.6
+ '@jridgewell/gen-mapping': 0.3.8
+ '@jridgewell/trace-mapping': 0.3.25
+ jsesc: 3.1.0
+
+ '@babel/helper-annotate-as-pure@7.25.9':
+ dependencies:
+ '@babel/types': 7.27.1
+
+ '@babel/helper-builder-binary-assignment-operator-visitor@7.25.9':
+ dependencies:
+ '@babel/traverse': 7.27.1
+ '@babel/types': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-compilation-targets@7.25.9':
+ dependencies:
+ '@babel/compat-data': 7.26.2
+ '@babel/helper-validator-option': 7.25.9
+ browserslist: 4.24.2
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-create-class-features-plugin@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-annotate-as-pure': 7.25.9
+ '@babel/helper-member-expression-to-functions': 7.25.9
+ '@babel/helper-optimise-call-expression': 7.25.9
+ '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0)
+ '@babel/helper-skip-transparent-expression-wrappers': 7.25.9
+ '@babel/traverse': 7.27.1
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-create-regexp-features-plugin@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-annotate-as-pure': 7.25.9
+ regexpu-core: 6.2.0
+ semver: 6.3.1
+
+ '@babel/helper-define-polyfill-provider@0.6.3(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-compilation-targets': 7.25.9
+ '@babel/helper-plugin-utils': 7.27.1
+ debug: 4.4.1
+ lodash.debounce: 4.0.8
+ resolve: 1.22.10
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-member-expression-to-functions@7.25.9':
+ dependencies:
+ '@babel/traverse': 7.27.1
+ '@babel/types': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-imports@7.25.9':
+ dependencies:
+ '@babel/traverse': 7.27.1
+ '@babel/types': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-module-imports': 7.25.9
+ '@babel/helper-validator-identifier': 7.27.1
+ '@babel/traverse': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-optimise-call-expression@7.25.9':
+ dependencies:
+ '@babel/types': 7.27.1
+
+ '@babel/helper-plugin-utils@7.27.1': {}
+
+ '@babel/helper-remap-async-to-generator@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-annotate-as-pure': 7.25.9
+ '@babel/helper-wrap-function': 7.25.9
+ '@babel/traverse': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-replace-supers@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-member-expression-to-functions': 7.25.9
+ '@babel/helper-optimise-call-expression': 7.25.9
+ '@babel/traverse': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-simple-access@7.25.9':
+ dependencies:
+ '@babel/traverse': 7.27.1
+ '@babel/types': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-skip-transparent-expression-wrappers@7.25.9':
+ dependencies:
+ '@babel/traverse': 7.27.1
+ '@babel/types': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-string-parser@7.25.9': {}
+
+ '@babel/helper-string-parser@7.27.1': {}
+
+ '@babel/helper-validator-identifier@7.27.1': {}
+
+ '@babel/helper-validator-option@7.25.9': {}
+
+ '@babel/helper-wrap-function@7.25.9':
+ dependencies:
+ '@babel/template': 7.27.2
+ '@babel/traverse': 7.27.1
+ '@babel/types': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helpers@7.26.0':
+ dependencies:
+ '@babel/template': 7.27.2
+ '@babel/types': 7.27.1
+
+ '@babel/parser@7.26.2':
+ dependencies:
+ '@babel/types': 7.26.0
+
+ '@babel/parser@7.27.2':
+ dependencies:
+ '@babel/types': 7.27.1
+
+ '@babel/parser@7.27.5':
+ dependencies:
+ '@babel/types': 7.27.6
+
+ '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/traverse': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-skip-transparent-expression-wrappers': 7.25.9
+ '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.26.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/traverse': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+
+ '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-import-assertions@7.26.0(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0)
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-arrow-functions@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-async-generator-functions@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.0)
+ '@babel/traverse': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-async-to-generator@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-module-imports': 7.25.9
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-block-scoped-functions@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-block-scoping@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-class-properties@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0)
+ '@babel/helper-plugin-utils': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-class-static-block@7.26.0(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0)
+ '@babel/helper-plugin-utils': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-classes@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-annotate-as-pure': 7.25.9
+ '@babel/helper-compilation-targets': 7.25.9
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0)
+ '@babel/traverse': 7.27.1
+ globals: 11.12.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-computed-properties@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/template': 7.27.2
+
+ '@babel/plugin-transform-destructuring@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-dotall-regex@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0)
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-duplicate-keys@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0)
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-dynamic-import@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-exponentiation-operator@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-builder-binary-assignment-operator-visitor': 7.25.9
+ '@babel/helper-plugin-utils': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-export-namespace-from@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-for-of@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-skip-transparent-expression-wrappers': 7.25.9
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-function-name@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-compilation-targets': 7.25.9
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/traverse': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-json-strings@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-literals@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-logical-assignment-operators@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-member-expression-literals@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-modules-amd@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0)
+ '@babel/helper-plugin-utils': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-modules-commonjs@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0)
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-simple-access': 7.25.9
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-modules-systemjs@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0)
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-validator-identifier': 7.27.1
+ '@babel/traverse': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-modules-umd@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0)
+ '@babel/helper-plugin-utils': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-named-capturing-groups-regex@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0)
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-new-target@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-nullish-coalescing-operator@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-numeric-separator@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-object-rest-spread@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-compilation-targets': 7.25.9
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.0)
+
+ '@babel/plugin-transform-object-super@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-optional-catch-binding@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-optional-chaining@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-skip-transparent-expression-wrappers': 7.25.9
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-parameters@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-private-methods@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0)
+ '@babel/helper-plugin-utils': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-private-property-in-object@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-annotate-as-pure': 7.25.9
+ '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0)
+ '@babel/helper-plugin-utils': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-property-literals@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-regenerator@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+ regenerator-transform: 0.15.2
+
+ '@babel/plugin-transform-regexp-modifiers@7.26.0(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0)
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-reserved-words@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-shorthand-properties@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-spread@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-skip-transparent-expression-wrappers': 7.25.9
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-sticky-regex@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-template-literals@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-typeof-symbol@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-typescript@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-annotate-as-pure': 7.25.9
+ '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0)
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-skip-transparent-expression-wrappers': 7.25.9
+ '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.26.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-unicode-escapes@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-unicode-property-regex@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0)
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-unicode-regex@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0)
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-unicode-sets-regex@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0)
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/preset-env@7.26.0(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/compat-data': 7.26.2
+ '@babel/core': 7.26.0
+ '@babel/helper-compilation-targets': 7.25.9
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-validator-option': 7.25.9
+ '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.26.0)
+ '@babel/plugin-syntax-import-assertions': 7.26.0(@babel/core@7.26.0)
+ '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.26.0)
+ '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.26.0)
+ '@babel/plugin-transform-arrow-functions': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-async-generator-functions': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-async-to-generator': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-block-scoped-functions': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-block-scoping': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-class-properties': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-class-static-block': 7.26.0(@babel/core@7.26.0)
+ '@babel/plugin-transform-classes': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-computed-properties': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-destructuring': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-dotall-regex': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-duplicate-keys': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-dynamic-import': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-exponentiation-operator': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-export-namespace-from': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-for-of': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-function-name': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-json-strings': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-literals': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-logical-assignment-operators': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-member-expression-literals': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-modules-amd': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-modules-commonjs': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-modules-systemjs': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-modules-umd': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-named-capturing-groups-regex': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-new-target': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-nullish-coalescing-operator': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-numeric-separator': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-object-rest-spread': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-object-super': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-optional-catch-binding': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-private-methods': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-private-property-in-object': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-property-literals': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-regenerator': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-regexp-modifiers': 7.26.0(@babel/core@7.26.0)
+ '@babel/plugin-transform-reserved-words': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-shorthand-properties': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-spread': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-sticky-regex': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-template-literals': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-typeof-symbol': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-unicode-escapes': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-unicode-property-regex': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-unicode-regex': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-unicode-sets-regex': 7.25.9(@babel/core@7.26.0)
+ '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.26.0)
+ babel-plugin-polyfill-corejs2: 0.4.12(@babel/core@7.26.0)
+ babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.26.0)
+ babel-plugin-polyfill-regenerator: 0.6.3(@babel/core@7.26.0)
+ core-js-compat: 3.39.0
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/types': 7.27.1
+ esutils: 2.0.3
+
+ '@babel/runtime@7.26.0':
+ dependencies:
+ regenerator-runtime: 0.14.1
+
+ '@babel/template@7.25.9':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/parser': 7.26.2
+ '@babel/types': 7.26.0
+
+ '@babel/template@7.27.2':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/parser': 7.27.2
+ '@babel/types': 7.27.1
+
+ '@babel/traverse@7.25.9':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/generator': 7.26.2
+ '@babel/parser': 7.26.2
+ '@babel/template': 7.25.9
+ '@babel/types': 7.26.0
+ debug: 4.4.1
+ globals: 11.12.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/traverse@7.27.1':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/generator': 7.27.1
+ '@babel/parser': 7.27.2
+ '@babel/template': 7.27.2
+ '@babel/types': 7.27.1
+ debug: 4.4.1
+ globals: 11.12.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/traverse@7.27.4':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/generator': 7.27.5
+ '@babel/parser': 7.27.5
+ '@babel/template': 7.27.2
+ '@babel/types': 7.27.6
+ debug: 4.4.1
+ globals: 11.12.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/types@7.26.0':
+ dependencies:
+ '@babel/helper-string-parser': 7.25.9
+ '@babel/helper-validator-identifier': 7.27.1
+
+ '@babel/types@7.27.1':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.27.1
+
+ '@babel/types@7.27.6':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.27.1
+
+ '@bcoe/v8-coverage@0.2.3': {}
+
+ '@clack/core@0.5.0':
+ dependencies:
+ picocolors: 1.1.1
+ sisteransi: 1.0.5
+
+ '@clack/prompts@0.11.0':
+ dependencies:
+ '@clack/core': 0.5.0
+ picocolors: 1.1.1
+ sisteransi: 1.0.5
+
+ '@commitlint/cli@19.8.1(@types/node@20.19.0)(typescript@5.7.2)':
+ dependencies:
+ '@commitlint/format': 19.8.1
+ '@commitlint/lint': 19.8.1
+ '@commitlint/load': 19.8.1(@types/node@20.19.0)(typescript@5.7.2)
+ '@commitlint/read': 19.8.1
+ '@commitlint/types': 19.8.1
+ tinyexec: 1.0.1
+ yargs: 17.7.2
+ transitivePeerDependencies:
+ - '@types/node'
+ - typescript
+
+ '@commitlint/config-conventional@19.8.1':
+ dependencies:
+ '@commitlint/types': 19.8.1
+ conventional-changelog-conventionalcommits: 7.0.2
+
+ '@commitlint/config-validator@19.8.1':
+ dependencies:
+ '@commitlint/types': 19.8.1
+ ajv: 8.17.1
+
+ '@commitlint/ensure@19.8.1':
+ dependencies:
+ '@commitlint/types': 19.8.1
+ lodash.camelcase: 4.3.0
+ lodash.kebabcase: 4.1.1
+ lodash.snakecase: 4.1.1
+ lodash.startcase: 4.4.0
+ lodash.upperfirst: 4.3.1
+
+ '@commitlint/execute-rule@19.8.1': {}
+
+ '@commitlint/format@19.8.1':
+ dependencies:
+ '@commitlint/types': 19.8.1
+ chalk: 5.3.0
+
+ '@commitlint/is-ignored@19.8.1':
+ dependencies:
+ '@commitlint/types': 19.8.1
+ semver: 7.7.2
+
+ '@commitlint/lint@19.8.1':
+ dependencies:
+ '@commitlint/is-ignored': 19.8.1
+ '@commitlint/parse': 19.8.1
+ '@commitlint/rules': 19.8.1
+ '@commitlint/types': 19.8.1
+
+ '@commitlint/load@19.8.1(@types/node@20.19.0)(typescript@5.7.2)':
+ dependencies:
+ '@commitlint/config-validator': 19.8.1
+ '@commitlint/execute-rule': 19.8.1
+ '@commitlint/resolve-extends': 19.8.1
+ '@commitlint/types': 19.8.1
+ chalk: 5.3.0
+ cosmiconfig: 9.0.0(typescript@5.7.2)
+ cosmiconfig-typescript-loader: 6.1.0(@types/node@20.19.0)(cosmiconfig@9.0.0(typescript@5.7.2))(typescript@5.7.2)
+ lodash.isplainobject: 4.0.6
+ lodash.merge: 4.6.2
+ lodash.uniq: 4.5.0
+ transitivePeerDependencies:
+ - '@types/node'
+ - typescript
+
+ '@commitlint/message@19.8.1': {}
+
+ '@commitlint/parse@19.8.1':
+ dependencies:
+ '@commitlint/types': 19.8.1
+ conventional-changelog-angular: 7.0.0
+ conventional-commits-parser: 5.0.0
+
+ '@commitlint/read@19.8.1':
+ dependencies:
+ '@commitlint/top-level': 19.8.1
+ '@commitlint/types': 19.8.1
+ git-raw-commits: 4.0.0
+ minimist: 1.2.8
+ tinyexec: 1.0.1
+
+ '@commitlint/resolve-extends@19.8.1':
+ dependencies:
+ '@commitlint/config-validator': 19.8.1
+ '@commitlint/types': 19.8.1
+ global-directory: 4.0.1
+ import-meta-resolve: 4.1.0
+ lodash.mergewith: 4.6.2
+ resolve-from: 5.0.0
+
+ '@commitlint/rules@19.8.1':
+ dependencies:
+ '@commitlint/ensure': 19.8.1
+ '@commitlint/message': 19.8.1
+ '@commitlint/to-lines': 19.8.1
+ '@commitlint/types': 19.8.1
+
+ '@commitlint/to-lines@19.8.1': {}
+
+ '@commitlint/top-level@19.8.1':
+ dependencies:
+ find-up: 7.0.0
+
+ '@commitlint/types@19.8.1':
+ dependencies:
+ '@types/conventional-commits-parser': 5.0.1
+ chalk: 5.3.0
+
+ '@dcloudio/types@3.4.14': {}
+
+ '@dcloudio/uni-app-harmony@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-app-uts': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-app-vite': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))
+ debug: 4.4.1
+ fs-extra: 10.1.0
+ licia: 1.46.0
+ postcss-selector-parser: 6.1.2
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vite
+ - vue
+
+ '@dcloudio/uni-app-plus@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-app-uts': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-app-vite': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-app-vue': 3.0.0-4060620250520001
+ debug: 4.4.1
+ fs-extra: 10.1.0
+ licia: 1.46.0
+ postcss-selector-parser: 6.1.2
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vite
+ - vue
+
+ '@dcloudio/uni-app-uts@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@babel/parser': 7.27.2
+ '@babel/types': 7.27.1
+ '@dcloudio/uni-cli-shared': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-console': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-i18n': 3.0.0-4060620250520001
+ '@dcloudio/uni-nvue-styler': 3.0.0-4060620250520001
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@jridgewell/gen-mapping': 0.3.8
+ '@jridgewell/trace-mapping': 0.3.25
+ '@rollup/pluginutils': 5.1.4(rollup@4.41.1)
+ '@vue/compiler-core': 3.4.21
+ '@vue/compiler-dom': 3.4.21
+ '@vue/compiler-sfc': 3.4.21
+ '@vue/consolidate': 1.0.0
+ '@vue/shared': 3.4.21
+ debug: 4.4.1
+ es-module-lexer: 1.5.4
+ estree-walker: 2.0.2
+ fs-extra: 10.1.0
+ magic-string: 0.30.17
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+ unimport: 4.1.1
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@dcloudio/uni-app-vite@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-cli-shared': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-i18n': 3.0.0-4060620250520001
+ '@dcloudio/uni-nvue-styler': 3.0.0-4060620250520001
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@rollup/pluginutils': 5.1.4(rollup@4.41.1)
+ '@vitejs/plugin-vue': 5.1.0(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))
+ '@vue/compiler-dom': 3.4.21
+ '@vue/compiler-sfc': 3.4.21
+ debug: 4.4.1
+ fs-extra: 10.1.0
+ picocolors: 1.1.1
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vite
+ - vue
+
+ '@dcloudio/uni-app-vue@3.0.0-4060620250520001': {}
+
+ '@dcloudio/uni-app-x@0.7.60': {}
+
+ '@dcloudio/uni-app@3.0.0-4060620250520001(@dcloudio/types@3.4.14)(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/types': 3.4.14
+ '@dcloudio/uni-cloud': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-components': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-console': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-i18n': 3.0.0-4060620250520001
+ '@dcloudio/uni-push': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@dcloudio/uni-stat': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@vue/shared': 3.4.21
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@dcloudio/uni-automator@3.0.0-4060620250520001(jest-environment-node@27.5.1)(jest@27.0.4)(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-cli-shared': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ address: 1.2.2
+ cross-env: 7.0.3
+ debug: 4.4.1
+ default-gateway: 6.0.3
+ fs-extra: 10.1.0
+ jest: 27.0.4
+ jest-environment-node: 27.5.1
+ jsonc-parser: 3.3.1
+ licia: 1.46.0
+ merge: 2.1.1
+ qrcode-reader: 1.0.4
+ qrcode-terminal: 0.12.0
+ ws: 8.18.0
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - bufferutil
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - utf-8-validate
+ - vue
+
+ '@dcloudio/uni-cli-shared@3.0.0-4020820240925001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@ampproject/remapping': 2.3.0
+ '@babel/code-frame': 7.27.1
+ '@babel/core': 7.26.0
+ '@babel/parser': 7.27.2
+ '@babel/types': 7.27.1
+ '@dcloudio/uni-i18n': 3.0.0-4020820240925001
+ '@dcloudio/uni-shared': 3.0.0-4020820240925001
+ '@intlify/core-base': 9.1.9
+ '@intlify/shared': 9.1.9
+ '@intlify/vue-devtools': 9.1.9
+ '@rollup/pluginutils': 5.1.4(rollup@4.41.1)
+ '@vue/compiler-core': 3.4.21
+ '@vue/compiler-dom': 3.4.21
+ '@vue/compiler-sfc': 3.4.21
+ '@vue/compiler-ssr': 3.4.21
+ '@vue/server-renderer': 3.4.21(vue@3.5.15(typescript@5.7.2))
+ '@vue/shared': 3.4.21
+ adm-zip: 0.5.16
+ autoprefixer: 10.4.20(postcss@8.5.3)
+ base64url: 3.0.1
+ chokidar: 3.6.0
+ compare-versions: 3.6.0
+ debug: 4.4.1
+ es-module-lexer: 1.5.4
+ esbuild: 0.20.2
+ estree-walker: 2.0.2
+ fast-glob: 3.3.3
+ fs-extra: 10.1.0
+ hash-sum: 2.0.0
+ isbinaryfile: 5.0.4
+ jsonc-parser: 3.3.1
+ lines-and-columns: 2.0.4
+ magic-string: 0.30.17
+ merge: 2.1.1
+ mime: 3.0.0
+ module-alias: 2.2.3
+ os-locale-s-fix: 1.0.8-fix-1
+ picocolors: 1.1.1
+ postcss-import: 14.1.0(postcss@8.5.3)
+ postcss-load-config: 3.1.4(postcss@8.5.3)
+ postcss-modules: 4.3.1(postcss@8.5.3)
+ postcss-selector-parser: 6.1.2
+ resolve: 1.22.10
+ source-map-js: 1.2.1
+ tapable: 2.2.1
+ unplugin-auto-import: 0.16.7(rollup@4.41.1)
+ xregexp: 3.1.0
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@dcloudio/uni-cli-shared@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@ampproject/remapping': 2.3.0
+ '@babel/code-frame': 7.27.1
+ '@babel/core': 7.26.0
+ '@babel/parser': 7.27.2
+ '@babel/types': 7.27.1
+ '@dcloudio/uni-i18n': 3.0.0-4060620250520001
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@intlify/core-base': 9.1.9
+ '@intlify/shared': 9.1.9
+ '@intlify/vue-devtools': 9.1.9
+ '@rollup/pluginutils': 5.1.3(rollup@4.41.1)
+ '@vue/compiler-core': 3.4.21
+ '@vue/compiler-dom': 3.4.21
+ '@vue/compiler-sfc': 3.4.21
+ '@vue/compiler-ssr': 3.4.21
+ '@vue/server-renderer': 3.4.21(vue@3.5.15(typescript@5.7.2))
+ '@vue/shared': 3.4.21
+ adm-zip: 0.5.16
+ autoprefixer: 10.4.20(postcss@8.5.3)
+ base64url: 3.0.1
+ chokidar: 3.6.0
+ compare-versions: 3.6.0
+ debug: 4.4.1
+ es-module-lexer: 1.5.4
+ esbuild: 0.20.2
+ estree-walker: 2.0.2
+ fast-glob: 3.3.3
+ fs-extra: 10.1.0
+ hash-sum: 2.0.0
+ isbinaryfile: 5.0.4
+ jsonc-parser: 3.3.1
+ lines-and-columns: 2.0.4
+ magic-string: 0.30.17
+ merge: 2.1.1
+ mime: 3.0.0
+ module-alias: 2.2.3
+ os-locale-s-fix: 1.0.8-fix-1
+ picocolors: 1.1.1
+ postcss-import: 14.1.0(postcss@8.5.3)
+ postcss-load-config: 3.1.4(postcss@8.5.3)
+ postcss-modules: 4.3.1(postcss@8.5.3)
+ postcss-selector-parser: 6.1.2
+ resolve: 1.22.10
+ source-map-js: 1.2.1
+ tapable: 2.2.1
+ unimport: 4.1.1
+ unplugin-auto-import: 19.1.0
+ xregexp: 3.1.0
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@dcloudio/uni-cloud@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-cli-shared': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-i18n': 3.0.0-4060620250520001
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@vue/shared': 3.4.21
+ fast-glob: 3.3.3
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@dcloudio/uni-components@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-cloud': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-h5': 3.0.0-4060620250520001(patch_hash=5763725268e9a493075be3c82d91b590b9ae30e997a04a418494dd7d6d327b9d)(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-i18n': 3.0.0-4060620250520001
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@dcloudio/uni-console@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-cli-shared': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ fs-extra: 10.1.0
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@dcloudio/uni-h5-vite@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-cli-shared': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@rollup/pluginutils': 5.1.4(rollup@4.41.1)
+ '@vue/compiler-dom': 3.4.21
+ '@vue/compiler-sfc': 3.4.21
+ '@vue/server-renderer': 3.4.21(vue@3.5.15(typescript@5.7.2))
+ '@vue/shared': 3.4.21
+ debug: 4.4.1
+ fs-extra: 10.1.0
+ mime: 3.0.0
+ module-alias: 2.2.3
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@dcloudio/uni-h5-vue@3.0.0-4060620250520001(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@vue/server-renderer': 3.4.21(vue@3.5.15(typescript@5.7.2))
+ transitivePeerDependencies:
+ - vue
+
+ '@dcloudio/uni-h5@3.0.0-4060620250520001(patch_hash=5763725268e9a493075be3c82d91b590b9ae30e997a04a418494dd7d6d327b9d)(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-h5-vite': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-h5-vue': 3.0.0-4060620250520001(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-i18n': 3.0.0-4060620250520001
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@vue/server-renderer': 3.4.21(vue@3.5.15(typescript@5.7.2))
+ '@vue/shared': 3.4.21
+ debug: 4.4.1
+ localstorage-polyfill: 1.0.1
+ postcss-selector-parser: 6.1.2
+ safe-area-insets: 1.4.1
+ vue-router: 4.5.0(vue@3.5.15(typescript@5.7.2))
+ xmlhttprequest: 1.8.0
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@dcloudio/uni-i18n@3.0.0-4020820240925001': {}
+
+ '@dcloudio/uni-i18n@3.0.0-4060620250520001': {}
+
+ '@dcloudio/uni-mp-alipay@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-cli-shared': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vite': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vue': 3.0.0-4060620250520001
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@vue/compiler-core': 3.4.21
+ '@vue/shared': 3.4.21
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@dcloudio/uni-mp-baidu@3.0.0-4060620250520001(@dcloudio/types@3.4.14)(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-app': 3.0.0-4060620250520001(@dcloudio/types@3.4.14)(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-cli-shared': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-compiler': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vite': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vue': 3.0.0-4060620250520001
+ '@dcloudio/uni-mp-weixin': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@vue/compiler-core': 3.4.21
+ '@vue/shared': 3.4.21
+ jimp: 0.10.3
+ licia: 1.46.0
+ qrcode-reader: 1.0.4
+ qrcode-terminal: 0.12.0
+ ws: 8.18.0
+ transitivePeerDependencies:
+ - '@dcloudio/types'
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - bufferutil
+ - debug
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - utf-8-validate
+ - vue
+
+ '@dcloudio/uni-mp-compiler@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@babel/generator': 7.27.1
+ '@babel/parser': 7.27.2
+ '@babel/types': 7.27.1
+ '@dcloudio/uni-cli-shared': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@vue/compiler-core': 3.4.21
+ '@vue/compiler-dom': 3.4.21
+ '@vue/shared': 3.4.21
+ estree-walker: 2.0.2
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@dcloudio/uni-mp-harmony@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-cli-shared': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-toutiao': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vite': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vue': 3.0.0-4060620250520001
+ '@dcloudio/uni-quickapp-webview': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@vue/shared': 3.4.21
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@dcloudio/uni-mp-jd@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-cli-shared': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-compiler': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vite': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vue': 3.0.0-4060620250520001
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@vue/shared': 3.4.21
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@dcloudio/uni-mp-kuaishou@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-cli-shared': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-compiler': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vite': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vue': 3.0.0-4060620250520001
+ '@dcloudio/uni-mp-weixin': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@vue/compiler-core': 3.4.21
+ '@vue/shared': 3.4.21
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - bufferutil
+ - debug
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - utf-8-validate
+ - vue
+
+ '@dcloudio/uni-mp-lark@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-cli-shared': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-compiler': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-toutiao': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vite': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vue': 3.0.0-4060620250520001
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@vue/compiler-core': 3.4.21
+ '@vue/shared': 3.4.21
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@dcloudio/uni-mp-qq@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-cli-shared': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vite': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vue': 3.0.0-4060620250520001
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@vue/shared': 3.4.21
+ fs-extra: 10.1.0
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@dcloudio/uni-mp-toutiao@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-cli-shared': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-compiler': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vite': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vue': 3.0.0-4060620250520001
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@vue/compiler-core': 3.4.21
+ '@vue/shared': 3.4.21
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@dcloudio/uni-mp-vite@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-cli-shared': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-i18n': 3.0.0-4060620250520001
+ '@dcloudio/uni-mp-compiler': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vue': 3.0.0-4060620250520001
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@vue/compiler-dom': 3.4.21
+ '@vue/compiler-sfc': 3.4.21
+ '@vue/shared': 3.4.21
+ debug: 4.4.1
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@dcloudio/uni-mp-vue@3.0.0-4060620250520001':
+ dependencies:
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@vue/shared': 3.4.21
+
+ '@dcloudio/uni-mp-weixin@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-cli-shared': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vite': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vue': 3.0.0-4060620250520001
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@vue/shared': 3.4.21
+ jimp: 0.10.3
+ licia: 1.46.0
+ qrcode-reader: 1.0.4
+ qrcode-terminal: 0.12.0
+ ws: 8.18.0
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - bufferutil
+ - debug
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - utf-8-validate
+ - vue
+
+ '@dcloudio/uni-mp-xhs@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-cli-shared': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-compiler': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vite': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vue': 3.0.0-4060620250520001
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@vue/shared': 3.4.21
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@dcloudio/uni-nvue-styler@3.0.0-4060620250520001':
+ dependencies:
+ parse-css-font: 4.0.0
+ postcss: 8.5.3
+
+ '@dcloudio/uni-push@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-cli-shared': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@dcloudio/uni-quickapp-webview@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-cli-shared': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vite': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-mp-vue': 3.0.0-4060620250520001
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@vue/shared': 3.4.21
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@dcloudio/uni-shared@3.0.0-4020820240925001':
+ dependencies:
+ '@vue/shared': 3.4.21
+
+ '@dcloudio/uni-shared@3.0.0-4060620250520001':
+ dependencies:
+ '@vue/shared': 3.4.21
+
+ '@dcloudio/uni-stacktracey@3.0.0-4060620250520001': {}
+
+ '@dcloudio/uni-stat@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-cli-shared': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ debug: 4.4.1
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@dcloudio/uni-uts-v1@3.0.0-4060620250520001(rollup@4.41.1)':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@dcloudio/uni-app-x': 0.7.60
+ '@dcloudio/uts': 3.0.0-4060620250520001
+ '@rollup/pluginutils': 5.1.4(rollup@4.41.1)
+ '@vue/shared': 3.4.21
+ adm-zip: 0.5.16
+ android-versions: 1.9.0
+ colors: 1.4.0
+ debug: 4.4.1
+ fast-glob: 3.3.3
+ find-cache-dir: 3.3.2
+ fs-extra: 10.1.0
+ graphlib: 2.1.8
+ jsonc-parser: 3.3.1
+ lodash: 4.17.21
+ md5-file: 5.0.0
+ object-hash: 3.0.0
+ semver: 7.7.2
+ source-map: 0.7.4
+ source-map-js: 1.2.1
+ transitivePeerDependencies:
+ - rollup
+ - supports-color
+
+ '@dcloudio/uts-darwin-arm64@3.0.0-4060620250520001':
+ optional: true
+
+ '@dcloudio/uts-darwin-x64@3.0.0-4060620250520001':
+ optional: true
+
+ '@dcloudio/uts-linux-x64-gnu@3.0.0-4060620250520001':
+ optional: true
+
+ '@dcloudio/uts-linux-x64-musl@3.0.0-4060620250520001':
+ optional: true
+
+ '@dcloudio/uts-win32-ia32-msvc@3.0.0-4060620250520001':
+ optional: true
+
+ '@dcloudio/uts-win32-x64-msvc@3.0.0-4060620250520001':
+ optional: true
+
+ '@dcloudio/uts@3.0.0-4060620250520001':
+ optionalDependencies:
+ '@dcloudio/uts-darwin-arm64': 3.0.0-4060620250520001
+ '@dcloudio/uts-darwin-x64': 3.0.0-4060620250520001
+ '@dcloudio/uts-linux-x64-gnu': 3.0.0-4060620250520001
+ '@dcloudio/uts-linux-x64-musl': 3.0.0-4060620250520001
+ '@dcloudio/uts-win32-ia32-msvc': 3.0.0-4060620250520001
+ '@dcloudio/uts-win32-x64-msvc': 3.0.0-4060620250520001
+
+ '@dcloudio/vite-plugin-uni@3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.26.0)
+ '@babel/plugin-transform-typescript': 7.25.9(@babel/core@7.26.0)
+ '@dcloudio/uni-cli-shared': 3.0.0-4060620250520001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@dcloudio/uni-shared': 3.0.0-4060620250520001
+ '@rollup/pluginutils': 5.1.3(rollup@4.41.1)
+ '@vitejs/plugin-legacy': 5.3.2(terser@5.36.0)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))
+ '@vitejs/plugin-vue': 5.1.0(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))
+ '@vitejs/plugin-vue-jsx': 3.1.0(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))
+ '@vue/compiler-core': 3.4.21
+ '@vue/compiler-dom': 3.4.21
+ '@vue/compiler-sfc': 3.4.21
+ '@vue/shared': 3.4.21
+ cac: 6.7.9
+ debug: 4.4.1
+ estree-walker: 2.0.2
+ express: 4.21.1
+ fast-glob: 3.3.3
+ fs-extra: 10.1.0
+ hash-sum: 2.0.0
+ jsonc-parser: 3.3.1
+ magic-string: 0.30.17
+ picocolors: 1.1.1
+ terser: 5.36.0
+ unplugin-auto-import: 19.1.0
+ vite: 5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0)
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@dprint/formatter@0.3.0': {}
+
+ '@dprint/markdown@0.17.8': {}
+
+ '@dprint/toml@0.6.4': {}
+
+ '@emnapi/core@1.4.3':
+ dependencies:
+ '@emnapi/wasi-threads': 1.0.2
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/runtime@1.4.3':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/wasi-threads@1.0.2':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@es-joy/jsdoccomment@0.50.2':
+ dependencies:
+ '@types/estree': 1.0.8
+ '@typescript-eslint/types': 8.34.1
+ comment-parser: 1.4.1
+ esquery: 1.6.0
+ jsdoc-type-pratt-parser: 4.1.0
+
+ '@es-joy/jsdoccomment@0.51.1':
+ dependencies:
+ '@types/estree': 1.0.8
+ '@typescript-eslint/types': 8.34.1
+ comment-parser: 1.4.1
+ esquery: 1.6.0
+ jsdoc-type-pratt-parser: 4.1.0
+
+ '@esbuild/aix-ppc64@0.20.2':
+ optional: true
+
+ '@esbuild/aix-ppc64@0.24.2':
+ optional: true
+
+ '@esbuild/aix-ppc64@0.25.5':
+ optional: true
+
+ '@esbuild/android-arm64@0.20.2':
+ optional: true
+
+ '@esbuild/android-arm64@0.24.2':
+ optional: true
+
+ '@esbuild/android-arm64@0.25.5':
+ optional: true
+
+ '@esbuild/android-arm@0.20.2':
+ optional: true
+
+ '@esbuild/android-arm@0.24.2':
+ optional: true
+
+ '@esbuild/android-arm@0.25.5':
+ optional: true
+
+ '@esbuild/android-x64@0.20.2':
+ optional: true
+
+ '@esbuild/android-x64@0.24.2':
+ optional: true
+
+ '@esbuild/android-x64@0.25.5':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.20.2': {}
+
+ '@esbuild/darwin-arm64@0.24.2':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.25.5':
+ optional: true
+
+ '@esbuild/darwin-x64@0.20.2': {}
+
+ '@esbuild/darwin-x64@0.24.2':
+ optional: true
+
+ '@esbuild/darwin-x64@0.25.5':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.20.2':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.24.2':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.25.5':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.20.2':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.24.2':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.25.5':
+ optional: true
+
+ '@esbuild/linux-arm64@0.20.2':
+ optional: true
+
+ '@esbuild/linux-arm64@0.24.2':
+ optional: true
+
+ '@esbuild/linux-arm64@0.25.5':
+ optional: true
+
+ '@esbuild/linux-arm@0.20.2':
+ optional: true
+
+ '@esbuild/linux-arm@0.24.2':
+ optional: true
+
+ '@esbuild/linux-arm@0.25.5':
+ optional: true
+
+ '@esbuild/linux-ia32@0.20.2':
+ optional: true
+
+ '@esbuild/linux-ia32@0.24.2':
+ optional: true
+
+ '@esbuild/linux-ia32@0.25.5':
+ optional: true
+
+ '@esbuild/linux-loong64@0.20.2':
+ optional: true
+
+ '@esbuild/linux-loong64@0.24.2':
+ optional: true
+
+ '@esbuild/linux-loong64@0.25.5':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.20.2':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.24.2':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.25.5':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.20.2':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.24.2':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.25.5':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.20.2':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.24.2':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.25.5':
+ optional: true
+
+ '@esbuild/linux-s390x@0.20.2':
+ optional: true
+
+ '@esbuild/linux-s390x@0.24.2':
+ optional: true
+
+ '@esbuild/linux-s390x@0.25.5':
+ optional: true
+
+ '@esbuild/linux-x64@0.20.2':
+ optional: true
+
+ '@esbuild/linux-x64@0.24.2':
+ optional: true
+
+ '@esbuild/linux-x64@0.25.5':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.24.2':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.25.5':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.20.2':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.24.2':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.25.5':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.24.2':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.25.5':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.20.2':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.24.2':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.25.5':
+ optional: true
+
+ '@esbuild/sunos-x64@0.20.2':
+ optional: true
+
+ '@esbuild/sunos-x64@0.24.2':
+ optional: true
+
+ '@esbuild/sunos-x64@0.25.5':
+ optional: true
+
+ '@esbuild/win32-arm64@0.20.2':
+ optional: true
+
+ '@esbuild/win32-arm64@0.24.2':
+ optional: true
+
+ '@esbuild/win32-arm64@0.25.5':
+ optional: true
+
+ '@esbuild/win32-ia32@0.20.2':
+ optional: true
+
+ '@esbuild/win32-ia32@0.24.2':
+ optional: true
+
+ '@esbuild/win32-ia32@0.25.5':
+ optional: true
+
+ '@esbuild/win32-x64@0.20.2':
+ optional: true
+
+ '@esbuild/win32-x64@0.24.2':
+ optional: true
+
+ '@esbuild/win32-x64@0.25.5':
+ optional: true
+
+ '@eslint-community/eslint-plugin-eslint-comments@4.5.0(eslint@9.29.0(jiti@2.4.2))':
+ dependencies:
+ escape-string-regexp: 4.0.0
+ eslint: 9.29.0(jiti@2.4.2)
+ ignore: 5.3.2
+
+ '@eslint-community/eslint-utils@4.7.0(eslint@9.29.0(jiti@2.4.2))':
+ dependencies:
+ eslint: 9.29.0(jiti@2.4.2)
+ eslint-visitor-keys: 3.4.3
+
+ '@eslint-community/regexpp@4.12.1': {}
+
+ '@eslint/compat@1.3.0(eslint@9.29.0(jiti@2.4.2))':
+ optionalDependencies:
+ eslint: 9.29.0(jiti@2.4.2)
+
+ '@eslint/config-array@0.20.1':
+ dependencies:
+ '@eslint/object-schema': 2.1.6
+ debug: 4.4.1
+ minimatch: 3.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/config-helpers@0.2.3': {}
+
+ '@eslint/core@0.13.0':
+ dependencies:
+ '@types/json-schema': 7.0.15
+
+ '@eslint/core@0.14.0':
+ dependencies:
+ '@types/json-schema': 7.0.15
+
+ '@eslint/core@0.15.0':
+ dependencies:
+ '@types/json-schema': 7.0.15
+
+ '@eslint/eslintrc@3.3.1':
+ dependencies:
+ ajv: 6.12.6
+ debug: 4.4.1
+ espree: 10.4.0
+ globals: 14.0.0
+ ignore: 5.3.2
+ import-fresh: 3.3.0
+ js-yaml: 4.1.0
+ minimatch: 3.1.2
+ strip-json-comments: 3.1.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/js@9.29.0': {}
+
+ '@eslint/markdown@6.6.0':
+ dependencies:
+ '@eslint/core': 0.14.0
+ '@eslint/plugin-kit': 0.3.2
+ github-slugger: 2.0.0
+ mdast-util-from-markdown: 2.0.2
+ mdast-util-frontmatter: 2.0.1
+ mdast-util-gfm: 3.1.0
+ micromark-extension-frontmatter: 2.0.0
+ micromark-extension-gfm: 3.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/object-schema@2.1.6': {}
+
+ '@eslint/plugin-kit@0.2.8':
+ dependencies:
+ '@eslint/core': 0.13.0
+ levn: 0.4.1
+
+ '@eslint/plugin-kit@0.3.2':
+ dependencies:
+ '@eslint/core': 0.15.0
+ levn: 0.4.1
+
+ '@exodus/schemasafe@1.3.0': {}
+
+ '@humanfs/core@0.19.1': {}
+
+ '@humanfs/node@0.16.6':
+ dependencies:
+ '@humanfs/core': 0.19.1
+ '@humanwhocodes/retry': 0.3.1
+
+ '@humanwhocodes/module-importer@1.0.1': {}
+
+ '@humanwhocodes/retry@0.3.1': {}
+
+ '@humanwhocodes/retry@0.4.3': {}
+
+ '@iconify-json/carbon@1.2.4':
+ dependencies:
+ '@iconify/types': 2.0.0
+
+ '@iconify/types@2.0.0': {}
+
+ '@iconify/utils@2.3.0':
+ dependencies:
+ '@antfu/install-pkg': 1.1.0
+ '@antfu/utils': 8.1.1
+ '@iconify/types': 2.0.0
+ debug: 4.4.1
+ globals: 15.15.0
+ kolorist: 1.8.0
+ local-pkg: 1.1.1
+ mlly: 1.7.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@intlify/core-base@9.1.9':
+ dependencies:
+ '@intlify/devtools-if': 9.1.9
+ '@intlify/message-compiler': 9.1.9
+ '@intlify/message-resolver': 9.1.9
+ '@intlify/runtime': 9.1.9
+ '@intlify/shared': 9.1.9
+ '@intlify/vue-devtools': 9.1.9
+
+ '@intlify/devtools-if@9.1.9':
+ dependencies:
+ '@intlify/shared': 9.1.9
+
+ '@intlify/message-compiler@9.1.9':
+ dependencies:
+ '@intlify/message-resolver': 9.1.9
+ '@intlify/shared': 9.1.9
+ source-map: 0.6.1
+
+ '@intlify/message-resolver@9.1.9': {}
+
+ '@intlify/runtime@9.1.9':
+ dependencies:
+ '@intlify/message-compiler': 9.1.9
+ '@intlify/message-resolver': 9.1.9
+ '@intlify/shared': 9.1.9
+
+ '@intlify/shared@9.1.9': {}
+
+ '@intlify/vue-devtools@9.1.9':
+ dependencies:
+ '@intlify/message-resolver': 9.1.9
+ '@intlify/runtime': 9.1.9
+ '@intlify/shared': 9.1.9
+
+ '@isaacs/cliui@8.0.2':
+ dependencies:
+ string-width: 5.1.2
+ string-width-cjs: string-width@4.2.3
+ strip-ansi: 7.1.0
+ strip-ansi-cjs: strip-ansi@6.0.1
+ wrap-ansi: 8.1.0
+ wrap-ansi-cjs: wrap-ansi@7.0.0
+
+ '@istanbuljs/load-nyc-config@1.1.0':
+ dependencies:
+ camelcase: 5.3.1
+ find-up: 4.1.0
+ get-package-type: 0.1.0
+ js-yaml: 3.14.1
+ resolve-from: 5.0.0
+
+ '@istanbuljs/schema@0.1.3': {}
+
+ '@jest/console@27.5.1':
+ dependencies:
+ '@jest/types': 27.5.1
+ '@types/node': 20.19.0
+ chalk: 4.1.2
+ jest-message-util: 27.5.1
+ jest-util: 27.5.1
+ slash: 3.0.0
+
+ '@jest/core@27.5.1':
+ dependencies:
+ '@jest/console': 27.5.1
+ '@jest/reporters': 27.5.1
+ '@jest/test-result': 27.5.1
+ '@jest/transform': 27.5.1
+ '@jest/types': 27.5.1
+ '@types/node': 20.19.0
+ ansi-escapes: 4.3.2
+ chalk: 4.1.2
+ emittery: 0.8.1
+ exit: 0.1.2
+ graceful-fs: 4.2.11
+ jest-changed-files: 27.5.1
+ jest-config: 27.5.1
+ jest-haste-map: 27.5.1
+ jest-message-util: 27.5.1
+ jest-regex-util: 27.5.1
+ jest-resolve: 27.5.1
+ jest-resolve-dependencies: 27.5.1
+ jest-runner: 27.5.1
+ jest-runtime: 27.5.1
+ jest-snapshot: 27.5.1
+ jest-util: 27.5.1
+ jest-validate: 27.5.1
+ jest-watcher: 27.5.1
+ micromatch: 4.0.8
+ rimraf: 3.0.2
+ slash: 3.0.0
+ strip-ansi: 6.0.1
+ transitivePeerDependencies:
+ - bufferutil
+ - canvas
+ - supports-color
+ - ts-node
+ - utf-8-validate
+
+ '@jest/environment@27.5.1':
+ dependencies:
+ '@jest/fake-timers': 27.5.1
+ '@jest/types': 27.5.1
+ '@types/node': 20.19.0
+ jest-mock: 27.5.1
+
+ '@jest/fake-timers@27.5.1':
+ dependencies:
+ '@jest/types': 27.5.1
+ '@sinonjs/fake-timers': 8.1.0
+ '@types/node': 20.19.0
+ jest-message-util: 27.5.1
+ jest-mock: 27.5.1
+ jest-util: 27.5.1
+
+ '@jest/globals@27.5.1':
+ dependencies:
+ '@jest/environment': 27.5.1
+ '@jest/types': 27.5.1
+ expect: 27.5.1
+
+ '@jest/reporters@27.5.1':
+ dependencies:
+ '@bcoe/v8-coverage': 0.2.3
+ '@jest/console': 27.5.1
+ '@jest/test-result': 27.5.1
+ '@jest/transform': 27.5.1
+ '@jest/types': 27.5.1
+ '@types/node': 20.19.0
+ chalk: 4.1.2
+ collect-v8-coverage: 1.0.2
+ exit: 0.1.2
+ glob: 7.2.3
+ graceful-fs: 4.2.11
+ istanbul-lib-coverage: 3.2.2
+ istanbul-lib-instrument: 5.2.1
+ istanbul-lib-report: 3.0.1
+ istanbul-lib-source-maps: 4.0.1
+ istanbul-reports: 3.1.7
+ jest-haste-map: 27.5.1
+ jest-resolve: 27.5.1
+ jest-util: 27.5.1
+ jest-worker: 27.5.1
+ slash: 3.0.0
+ source-map: 0.6.1
+ string-length: 4.0.2
+ terminal-link: 2.1.1
+ v8-to-istanbul: 8.1.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@jest/source-map@27.5.1':
+ dependencies:
+ callsites: 3.1.0
+ graceful-fs: 4.2.11
+ source-map: 0.6.1
+
+ '@jest/test-result@27.5.1':
+ dependencies:
+ '@jest/console': 27.5.1
+ '@jest/types': 27.5.1
+ '@types/istanbul-lib-coverage': 2.0.6
+ collect-v8-coverage: 1.0.2
+
+ '@jest/test-sequencer@27.5.1':
+ dependencies:
+ '@jest/test-result': 27.5.1
+ graceful-fs: 4.2.11
+ jest-haste-map: 27.5.1
+ jest-runtime: 27.5.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@jest/transform@27.5.1':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@jest/types': 27.5.1
+ babel-plugin-istanbul: 6.1.1
+ chalk: 4.1.2
+ convert-source-map: 1.9.0
+ fast-json-stable-stringify: 2.1.0
+ graceful-fs: 4.2.11
+ jest-haste-map: 27.5.1
+ jest-regex-util: 27.5.1
+ jest-util: 27.5.1
+ micromatch: 4.0.8
+ pirates: 4.0.7
+ slash: 3.0.0
+ source-map: 0.6.1
+ write-file-atomic: 3.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@jest/types@27.5.1':
+ dependencies:
+ '@types/istanbul-lib-coverage': 2.0.6
+ '@types/istanbul-reports': 3.0.4
+ '@types/node': 20.19.0
+ '@types/yargs': 16.0.9
+ chalk: 4.1.2
+
+ '@jimp/bmp@0.10.3(@jimp/custom@0.10.3)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/utils': 0.10.3
+ bmp-js: 0.1.0
+ core-js: 3.39.0
+
+ '@jimp/core@0.10.3':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/utils': 0.10.3
+ any-base: 1.1.0
+ buffer: 5.7.1
+ core-js: 3.39.0
+ exif-parser: 0.1.12
+ file-type: 9.0.0
+ load-bmfont: 1.4.2
+ mkdirp: 0.5.6
+ phin: 2.9.3
+ pixelmatch: 4.0.2
+ tinycolor2: 1.6.0
+ transitivePeerDependencies:
+ - debug
+
+ '@jimp/custom@0.10.3':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/core': 0.10.3
+ core-js: 3.39.0
+ transitivePeerDependencies:
+ - debug
+
+ '@jimp/gif@0.10.3(@jimp/custom@0.10.3)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+ omggif: 1.0.10
+
+ '@jimp/jpeg@0.10.3(@jimp/custom@0.10.3)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+ jpeg-js: 0.3.7
+
+ '@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+
+ '@jimp/plugin-blur@0.10.3(@jimp/custom@0.10.3)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+
+ '@jimp/plugin-circle@0.10.3(@jimp/custom@0.10.3)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+
+ '@jimp/plugin-color@0.10.3(@jimp/custom@0.10.3)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+ tinycolor2: 1.6.0
+
+ '@jimp/plugin-contain@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-scale@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3)))':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/plugin-blit': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/plugin-scale': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+
+ '@jimp/plugin-cover@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-crop@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-scale@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3)))':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/plugin-crop': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/plugin-scale': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+
+ '@jimp/plugin-crop@0.10.3(@jimp/custom@0.10.3)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+
+ '@jimp/plugin-displace@0.10.3(@jimp/custom@0.10.3)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+
+ '@jimp/plugin-dither@0.10.3(@jimp/custom@0.10.3)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+
+ '@jimp/plugin-fisheye@0.10.3(@jimp/custom@0.10.3)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+
+ '@jimp/plugin-flip@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-rotate@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-crop@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3)))':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/plugin-rotate': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-crop@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+
+ '@jimp/plugin-gaussian@0.10.3(@jimp/custom@0.10.3)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+
+ '@jimp/plugin-invert@0.10.3(@jimp/custom@0.10.3)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+
+ '@jimp/plugin-mask@0.10.3(@jimp/custom@0.10.3)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+
+ '@jimp/plugin-normalize@0.10.3(@jimp/custom@0.10.3)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+
+ '@jimp/plugin-print@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/plugin-blit': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+ load-bmfont: 1.4.2
+ transitivePeerDependencies:
+ - debug
+
+ '@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+
+ '@jimp/plugin-rotate@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-crop@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/plugin-blit': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/plugin-crop': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+
+ '@jimp/plugin-scale@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+
+ '@jimp/plugin-shadow@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blur@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/plugin-blur': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+
+ '@jimp/plugin-threshold@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-color@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/plugin-color': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+
+ '@jimp/plugins@0.10.3(@jimp/custom@0.10.3)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/plugin-blit': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/plugin-blur': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/plugin-circle': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/plugin-color': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/plugin-contain': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-scale@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3)))
+ '@jimp/plugin-cover': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-crop@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-scale@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3)))
+ '@jimp/plugin-crop': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/plugin-displace': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/plugin-dither': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/plugin-fisheye': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/plugin-flip': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-rotate@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-crop@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3)))
+ '@jimp/plugin-gaussian': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/plugin-invert': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/plugin-mask': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/plugin-normalize': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/plugin-print': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))
+ '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/plugin-rotate': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-crop@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))
+ '@jimp/plugin-scale': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))
+ '@jimp/plugin-shadow': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blur@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))
+ '@jimp/plugin-threshold': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-color@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))
+ core-js: 3.39.0
+ timm: 1.7.1
+ transitivePeerDependencies:
+ - debug
+
+ '@jimp/png@0.10.3(@jimp/custom@0.10.3)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/utils': 0.10.3
+ core-js: 3.39.0
+ pngjs: 3.4.0
+
+ '@jimp/tiff@0.10.3(@jimp/custom@0.10.3)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ core-js: 3.39.0
+ utif: 2.0.1
+
+ '@jimp/types@0.10.3(@jimp/custom@0.10.3)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/bmp': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/custom': 0.10.3
+ '@jimp/gif': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/jpeg': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/png': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/tiff': 0.10.3(@jimp/custom@0.10.3)
+ core-js: 3.39.0
+ timm: 1.7.1
+
+ '@jimp/utils@0.10.3':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ core-js: 3.39.0
+ regenerator-runtime: 0.13.11
+
+ '@jridgewell/gen-mapping@0.3.5':
+ dependencies:
+ '@jridgewell/set-array': 1.2.1
+ '@jridgewell/sourcemap-codec': 1.5.0
+ '@jridgewell/trace-mapping': 0.3.25
+
+ '@jridgewell/gen-mapping@0.3.8':
+ dependencies:
+ '@jridgewell/set-array': 1.2.1
+ '@jridgewell/sourcemap-codec': 1.5.0
+ '@jridgewell/trace-mapping': 0.3.25
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/set-array@1.2.1': {}
+
+ '@jridgewell/source-map@0.3.6':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.5
+ '@jridgewell/trace-mapping': 0.3.25
+
+ '@jridgewell/sourcemap-codec@1.5.0': {}
+
+ '@jridgewell/trace-mapping@0.3.25':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.0
+
+ '@napi-rs/wasm-runtime@0.2.10':
+ dependencies:
+ '@emnapi/core': 1.4.3
+ '@emnapi/runtime': 1.4.3
+ '@tybys/wasm-util': 0.9.0
+ optional: true
+
+ '@node-rs/xxhash-android-arm-eabi@1.7.6':
+ optional: true
+
+ '@node-rs/xxhash-android-arm64@1.7.6':
+ optional: true
+
+ '@node-rs/xxhash-darwin-arm64@1.7.6':
+ optional: true
+
+ '@node-rs/xxhash-darwin-x64@1.7.6':
+ optional: true
+
+ '@node-rs/xxhash-freebsd-x64@1.7.6':
+ optional: true
+
+ '@node-rs/xxhash-linux-arm-gnueabihf@1.7.6':
+ optional: true
+
+ '@node-rs/xxhash-linux-arm64-gnu@1.7.6':
+ optional: true
+
+ '@node-rs/xxhash-linux-arm64-musl@1.7.6':
+ optional: true
+
+ '@node-rs/xxhash-linux-x64-gnu@1.7.6':
+ optional: true
+
+ '@node-rs/xxhash-linux-x64-musl@1.7.6':
+ optional: true
+
+ '@node-rs/xxhash-wasm32-wasi@1.7.6':
+ dependencies:
+ '@napi-rs/wasm-runtime': 0.2.10
+ optional: true
+
+ '@node-rs/xxhash-win32-arm64-msvc@1.7.6':
+ optional: true
+
+ '@node-rs/xxhash-win32-ia32-msvc@1.7.6':
+ optional: true
+
+ '@node-rs/xxhash-win32-x64-msvc@1.7.6':
+ optional: true
+
+ '@node-rs/xxhash@1.7.6':
+ optionalDependencies:
+ '@node-rs/xxhash-android-arm-eabi': 1.7.6
+ '@node-rs/xxhash-android-arm64': 1.7.6
+ '@node-rs/xxhash-darwin-arm64': 1.7.6
+ '@node-rs/xxhash-darwin-x64': 1.7.6
+ '@node-rs/xxhash-freebsd-x64': 1.7.6
+ '@node-rs/xxhash-linux-arm-gnueabihf': 1.7.6
+ '@node-rs/xxhash-linux-arm64-gnu': 1.7.6
+ '@node-rs/xxhash-linux-arm64-musl': 1.7.6
+ '@node-rs/xxhash-linux-x64-gnu': 1.7.6
+ '@node-rs/xxhash-linux-x64-musl': 1.7.6
+ '@node-rs/xxhash-wasm32-wasi': 1.7.6
+ '@node-rs/xxhash-win32-arm64-msvc': 1.7.6
+ '@node-rs/xxhash-win32-ia32-msvc': 1.7.6
+ '@node-rs/xxhash-win32-x64-msvc': 1.7.6
+
+ '@nodelib/fs.scandir@2.1.5':
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+
+ '@nodelib/fs.stat@2.0.5': {}
+
+ '@nodelib/fs.walk@1.2.8':
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.17.1
+
+ '@pkgr/core@0.1.2': {}
+
+ '@pkgr/core@0.2.7': {}
+
+ '@polka/url@1.0.0-next.29': {}
+
+ '@prettier/sync@0.5.2(prettier@3.5.3)':
+ dependencies:
+ make-synchronized: 0.2.9
+ prettier: 3.5.3
+
+ '@quansync/fs@0.1.3':
+ dependencies:
+ quansync: 0.2.10
+
+ '@rollup/pluginutils@5.1.3(rollup@4.41.1)':
+ dependencies:
+ '@types/estree': 1.0.6
+ estree-walker: 2.0.2
+ picomatch: 4.0.2
+ optionalDependencies:
+ rollup: 4.41.1
+
+ '@rollup/pluginutils@5.1.4(rollup@4.41.1)':
+ dependencies:
+ '@types/estree': 1.0.8
+ estree-walker: 2.0.2
+ picomatch: 4.0.2
+ optionalDependencies:
+ rollup: 4.41.1
+
+ '@rollup/rollup-android-arm-eabi@4.41.1':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.41.1':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.41.1':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.41.1': {}
+
+ '@rollup/rollup-freebsd-arm64@4.41.1':
+ optional: true
+
+ '@rollup/rollup-freebsd-x64@4.41.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.41.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.41.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.41.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.41.1':
+ optional: true
+
+ '@rollup/rollup-linux-loongarch64-gnu@4.41.1':
+ optional: true
+
+ '@rollup/rollup-linux-powerpc64le-gnu@4.41.1':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.41.1':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-musl@4.41.1':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.41.1':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.41.1':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.41.1':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.41.1':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.41.1':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.41.1':
+ optional: true
+
+ '@sindresorhus/is@4.6.0': {}
+
+ '@sinonjs/commons@1.8.6':
+ dependencies:
+ type-detect: 4.0.8
+
+ '@sinonjs/fake-timers@8.1.0':
+ dependencies:
+ '@sinonjs/commons': 1.8.6
+
+ '@stylistic/eslint-plugin@5.0.0-beta.5(eslint@9.29.0(jiti@2.4.2))':
+ dependencies:
+ '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0(jiti@2.4.2))
+ '@typescript-eslint/types': 8.34.1
+ eslint: 9.29.0(jiti@2.4.2)
+ eslint-visitor-keys: 4.2.1
+ espree: 10.4.0
+ estraverse: 5.3.0
+ picomatch: 4.0.2
+
+ '@szmarczak/http-timer@4.0.6':
+ dependencies:
+ defer-to-connect: 2.0.1
+
+ '@tanstack/match-sorter-utils@8.19.4':
+ dependencies:
+ remove-accents: 0.5.0
+
+ '@tanstack/query-core@5.62.16': {}
+
+ '@tanstack/vue-query@5.62.16(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@tanstack/match-sorter-utils': 8.19.4
+ '@tanstack/query-core': 5.62.16
+ '@vue/devtools-api': 6.6.4
+ vue: 3.5.15(typescript@5.7.2)
+ vue-demi: 0.14.10(vue@3.5.15(typescript@5.7.2))
+
+ '@tootallnate/once@1.1.2': {}
+
+ '@trivago/prettier-plugin-sort-imports@5.2.1(@vue/compiler-sfc@3.5.15)(prettier@3.5.3)':
+ dependencies:
+ '@babel/generator': 7.26.2
+ '@babel/parser': 7.26.2
+ '@babel/traverse': 7.25.9
+ '@babel/types': 7.26.0
+ javascript-natural-sort: 0.7.1
+ lodash: 4.17.21
+ prettier: 3.5.3
+ optionalDependencies:
+ '@vue/compiler-sfc': 3.5.15
+ transitivePeerDependencies:
+ - supports-color
+
+ '@tybys/wasm-util@0.9.0':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@types/babel__core@7.20.5':
+ dependencies:
+ '@babel/parser': 7.27.5
+ '@babel/types': 7.27.6
+ '@types/babel__generator': 7.27.0
+ '@types/babel__template': 7.4.4
+ '@types/babel__traverse': 7.20.7
+
+ '@types/babel__generator@7.27.0':
+ dependencies:
+ '@babel/types': 7.27.6
+
+ '@types/babel__template@7.4.4':
+ dependencies:
+ '@babel/parser': 7.27.5
+ '@babel/types': 7.27.6
+
+ '@types/babel__traverse@7.20.7':
+ dependencies:
+ '@babel/types': 7.27.6
+
+ '@types/cacheable-request@6.0.3':
+ dependencies:
+ '@types/http-cache-semantics': 4.0.4
+ '@types/keyv': 3.1.4
+ '@types/node': 20.19.0
+ '@types/responselike': 1.0.3
+
+ '@types/conventional-commits-parser@5.0.1':
+ dependencies:
+ '@types/node': 20.19.0
+
+ '@types/debug@4.1.12':
+ dependencies:
+ '@types/ms': 2.1.0
+
+ '@types/estree@1.0.6': {}
+
+ '@types/estree@1.0.7': {}
+
+ '@types/estree@1.0.8': {}
+
+ '@types/graceful-fs@4.1.9':
+ dependencies:
+ '@types/node': 20.19.0
+
+ '@types/http-cache-semantics@4.0.4': {}
+
+ '@types/istanbul-lib-coverage@2.0.6': {}
+
+ '@types/istanbul-lib-report@3.0.3':
+ dependencies:
+ '@types/istanbul-lib-coverage': 2.0.6
+
+ '@types/istanbul-reports@3.0.4':
+ dependencies:
+ '@types/istanbul-lib-report': 3.0.3
+
+ '@types/json-schema@7.0.15': {}
+
+ '@types/keyv@3.1.4':
+ dependencies:
+ '@types/node': 20.19.0
+
+ '@types/mdast@4.0.4':
+ dependencies:
+ '@types/unist': 3.0.3
+
+ '@types/ms@2.1.0': {}
+
+ '@types/node@20.19.0':
+ dependencies:
+ undici-types: 6.21.0
+
+ '@types/prettier@2.7.3': {}
+
+ '@types/responselike@1.0.3':
+ dependencies:
+ '@types/node': 20.19.0
+
+ '@types/stack-utils@2.0.3': {}
+
+ '@types/unist@3.0.3': {}
+
+ '@types/wechat-miniprogram@3.4.8': {}
+
+ '@types/yargs-parser@21.0.3': {}
+
+ '@types/yargs@16.0.9':
+ dependencies:
+ '@types/yargs-parser': 21.0.3
+
+ '@typescript-eslint/eslint-plugin@8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)':
+ dependencies:
+ '@eslint-community/regexpp': 4.12.1
+ '@typescript-eslint/parser': 8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)
+ '@typescript-eslint/scope-manager': 8.34.1
+ '@typescript-eslint/type-utils': 8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)
+ '@typescript-eslint/utils': 8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)
+ '@typescript-eslint/visitor-keys': 8.34.1
+ eslint: 9.29.0(jiti@2.4.2)
+ graphemer: 1.4.0
+ ignore: 7.0.5
+ natural-compare: 1.4.0
+ ts-api-utils: 2.1.0(typescript@5.7.2)
+ typescript: 5.7.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)':
+ dependencies:
+ '@typescript-eslint/scope-manager': 8.34.1
+ '@typescript-eslint/types': 8.34.1
+ '@typescript-eslint/typescript-estree': 8.34.1(typescript@5.7.2)
+ '@typescript-eslint/visitor-keys': 8.34.1
+ debug: 4.4.1
+ eslint: 9.29.0(jiti@2.4.2)
+ typescript: 5.7.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/project-service@8.34.1(typescript@5.7.2)':
+ dependencies:
+ '@typescript-eslint/tsconfig-utils': 8.34.1(typescript@5.7.2)
+ '@typescript-eslint/types': 8.34.1
+ debug: 4.4.1
+ typescript: 5.7.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/scope-manager@8.34.1':
+ dependencies:
+ '@typescript-eslint/types': 8.34.1
+ '@typescript-eslint/visitor-keys': 8.34.1
+
+ '@typescript-eslint/tsconfig-utils@8.34.1(typescript@5.7.2)':
+ dependencies:
+ typescript: 5.7.2
+
+ '@typescript-eslint/type-utils@8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)':
+ dependencies:
+ '@typescript-eslint/typescript-estree': 8.34.1(typescript@5.7.2)
+ '@typescript-eslint/utils': 8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)
+ debug: 4.4.1
+ eslint: 9.29.0(jiti@2.4.2)
+ ts-api-utils: 2.1.0(typescript@5.7.2)
+ typescript: 5.7.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/types@8.34.1': {}
+
+ '@typescript-eslint/typescript-estree@8.34.1(typescript@5.7.2)':
+ dependencies:
+ '@typescript-eslint/project-service': 8.34.1(typescript@5.7.2)
+ '@typescript-eslint/tsconfig-utils': 8.34.1(typescript@5.7.2)
+ '@typescript-eslint/types': 8.34.1
+ '@typescript-eslint/visitor-keys': 8.34.1
+ debug: 4.4.1
+ fast-glob: 3.3.3
+ is-glob: 4.0.3
+ minimatch: 9.0.5
+ semver: 7.7.2
+ ts-api-utils: 2.1.0(typescript@5.7.2)
+ typescript: 5.7.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/utils@8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)':
+ dependencies:
+ '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0(jiti@2.4.2))
+ '@typescript-eslint/scope-manager': 8.34.1
+ '@typescript-eslint/types': 8.34.1
+ '@typescript-eslint/typescript-estree': 8.34.1(typescript@5.7.2)
+ eslint: 9.29.0(jiti@2.4.2)
+ typescript: 5.7.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/visitor-keys@8.34.1':
+ dependencies:
+ '@typescript-eslint/types': 8.34.1
+ eslint-visitor-keys: 4.2.1
+
+ '@uni-helper/eslint-config@0.4.0(@antfu/eslint-config@4.15.0(@unocss/eslint-plugin@66.2.3(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2))(@vue/compiler-sfc@3.5.15)(eslint-plugin-format@1.0.1(eslint@9.29.0(jiti@2.4.2)))(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.29.0(jiti@2.4.2))':
+ dependencies:
+ '@antfu/eslint-config': 4.15.0(@unocss/eslint-plugin@66.2.3(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2))(@vue/compiler-sfc@3.5.15)(eslint-plugin-format@1.0.1(eslint@9.29.0(jiti@2.4.2)))(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)
+ '@eslint/eslintrc': 3.3.1
+ eslint: 9.29.0(jiti@2.4.2)
+ eslint-flat-config-utils: 2.1.0
+ local-pkg: 1.1.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@uni-helper/uni-app-types@1.0.0-alpha.3(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ typescript: 5.7.2
+ vue: 3.5.15(typescript@5.7.2)
+
+ '@uni-helper/uni-cloud-types@1.0.0-alpha.3(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ typescript: 5.7.2
+ vue: 3.5.15(typescript@5.7.2)
+
+ '@uni-helper/uni-env@0.0.3': {}
+
+ '@uni-helper/uni-env@0.1.7':
+ dependencies:
+ std-env: 3.8.0
+
+ '@uni-helper/uni-types@1.0.0-alpha.3(@uni-helper/uni-app-types@1.0.0-alpha.3(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2)))(@uni-helper/uni-cloud-types@1.0.0-alpha.3(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2)))(@uni-helper/uni-ui-types@1.0.0-alpha.3(@uni-helper/uni-app-types@1.0.0-alpha.3(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2)))(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2)))(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@uni-helper/uni-app-types': 1.0.0-alpha.3(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2))
+ '@uni-helper/uni-cloud-types': 1.0.0-alpha.3(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2))
+ '@uni-helper/uni-ui-types': 1.0.0-alpha.3(@uni-helper/uni-app-types@1.0.0-alpha.3(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2)))(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2))
+ typescript: 5.7.2
+ vue: 3.5.15(typescript@5.7.2)
+
+ '@uni-helper/uni-ui-types@1.0.0-alpha.3(@uni-helper/uni-app-types@1.0.0-alpha.3(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2)))(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@uni-helper/uni-app-types': 1.0.0-alpha.3(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2))
+ typescript: 5.7.2
+ vue: 3.5.15(typescript@5.7.2)
+
+ '@uni-helper/unocss-preset-uni@0.2.11(@unocss/preset-legacy-compat@0.59.4)(@unocss/preset-mini@66.1.2)(@unocss/rule-utils@66.2.3)(@unocss/vite@66.1.2(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2)))(unocss-applet@0.10.0(@unocss/core@66.2.3)(@unocss/preset-mini@66.1.2)(@unocss/preset-wind3@66.1.2)(unocss@65.4.2(postcss@8.5.3)(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))))(unocss@65.4.2(postcss@8.5.3)(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2)))':
+ dependencies:
+ '@uni-helper/uni-env': 0.1.7
+ '@unocss/preset-legacy-compat': 0.59.4
+ '@unocss/rule-utils': 66.2.3
+ unocss: 65.4.2(postcss@8.5.3)(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))
+ unocss-applet: 0.10.0(@unocss/core@66.2.3)(@unocss/preset-mini@66.1.2)(@unocss/preset-wind3@66.1.2)(unocss@65.4.2(postcss@8.5.3)(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2)))
+ optionalDependencies:
+ '@unocss/preset-mini': 66.1.2
+ '@unocss/vite': 66.1.2(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))
+
+ '@uni-helper/vite-plugin-uni-components@0.2.0(rollup@4.41.1)':
+ dependencies:
+ '@antfu/utils': 0.7.10
+ '@rollup/pluginutils': 5.1.4(rollup@4.41.1)
+ chokidar: 3.6.0
+ debug: 4.4.1
+ fast-glob: 3.3.3
+ local-pkg: 0.4.3
+ magic-string: 0.30.17
+ minimatch: 8.0.4
+ resolve: 1.22.10
+ transitivePeerDependencies:
+ - rollup
+ - supports-color
+
+ '@uni-helper/vite-plugin-uni-layouts@0.1.10(rollup@4.41.1)':
+ dependencies:
+ '@babel/types': 7.27.6
+ '@uni-helper/uni-env': 0.1.7
+ '@vue/compiler-core': 3.5.15
+ '@vue/compiler-sfc': 3.5.15
+ ast-kit: 0.11.3(rollup@4.41.1)
+ c12: 1.11.2
+ chokidar: 3.6.0
+ fast-glob: 3.3.3
+ jsonc-parser: 3.3.1
+ magic-string: 0.30.17
+ scule: 1.3.0
+ transitivePeerDependencies:
+ - magicast
+ - rollup
+
+ '@uni-helper/vite-plugin-uni-manifest@0.2.8(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))':
+ dependencies:
+ c12: 2.0.4
+ vite: 5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0)
+ transitivePeerDependencies:
+ - magicast
+
+ '@uni-helper/vite-plugin-uni-pages@0.2.28(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))':
+ dependencies:
+ '@uni-helper/uni-env': 0.1.7
+ '@vue/compiler-sfc': 3.5.15
+ chokidar: 3.6.0
+ debug: 4.4.1
+ detect-indent: 6.1.0
+ detect-newline: 3.1.0
+ fast-glob: 3.3.3
+ json5: 2.2.3
+ lodash.groupby: 4.6.0
+ magic-string: 0.30.17
+ unconfig: 0.5.5
+ vite: 5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0)
+ yaml: 2.6.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@uni-helper/vite-plugin-uni-platform@0.0.4':
+ dependencies:
+ '@uni-helper/uni-env': 0.0.3
+
+ '@uni-ku/bundle-optimizer@1.3.3(postcss@8.5.3)(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@dcloudio/uni-cli-shared': 3.0.0-4020820240925001(postcss@8.5.3)(rollup@4.41.1)(vue@3.5.15(typescript@5.7.2))
+ '@node-rs/xxhash': 1.7.6
+ chalk: 4.1.2
+ magic-string: 0.30.17
+ minimatch: 9.0.5
+ vite: 5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0)
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - '@vueuse/core'
+ - postcss
+ - rollup
+ - supports-color
+ - ts-node
+ - vue
+
+ '@unocss-applet/preset-applet@0.10.0(@unocss/core@66.2.3)(@unocss/preset-mini@66.1.2)(@unocss/preset-wind3@66.1.2)':
+ dependencies:
+ '@unocss/core': 66.2.3
+ '@unocss/preset-mini': 66.1.2
+ '@unocss/preset-wind3': 66.1.2
+ '@unocss/preset-wind4': 66.2.0
+
+ '@unocss-applet/preset-rem-rpx@0.10.0(@unocss/core@66.2.3)':
+ dependencies:
+ '@unocss/core': 66.2.3
+
+ '@unocss-applet/transformer-attributify@0.10.0(@unocss/core@66.2.3)':
+ dependencies:
+ '@unocss/core': 66.2.3
+ magic-string: 0.30.17
+
+ '@unocss/astro@65.4.2(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@unocss/core': 65.4.2
+ '@unocss/reset': 65.4.2
+ '@unocss/vite': 65.4.2(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))
+ optionalDependencies:
+ vite: 5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0)
+ transitivePeerDependencies:
+ - rollup
+ - supports-color
+ - vue
+
+ '@unocss/cli@65.4.2(rollup@4.41.1)':
+ dependencies:
+ '@ampproject/remapping': 2.3.0
+ '@rollup/pluginutils': 5.1.4(rollup@4.41.1)
+ '@unocss/config': 65.4.2
+ '@unocss/core': 65.4.2
+ '@unocss/preset-uno': 65.4.2
+ cac: 6.7.14
+ chokidar: 3.6.0
+ colorette: 2.0.20
+ consola: 3.4.2
+ magic-string: 0.30.17
+ pathe: 1.1.2
+ perfect-debounce: 1.0.0
+ tinyglobby: 0.2.14
+ transitivePeerDependencies:
+ - rollup
+ - supports-color
+
+ '@unocss/config@65.4.2':
+ dependencies:
+ '@unocss/core': 65.4.2
+ unconfig: 0.6.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@unocss/config@66.1.2':
+ dependencies:
+ '@unocss/core': 66.1.2
+ unconfig: 7.3.2
+ optional: true
+
+ '@unocss/config@66.2.3':
+ dependencies:
+ '@unocss/core': 66.2.3
+ unconfig: 7.3.2
+
+ '@unocss/core@0.59.4': {}
+
+ '@unocss/core@65.4.2': {}
+
+ '@unocss/core@66.1.2': {}
+
+ '@unocss/core@66.2.0': {}
+
+ '@unocss/core@66.2.3': {}
+
+ '@unocss/eslint-plugin@66.2.3(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)':
+ dependencies:
+ '@typescript-eslint/utils': 8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)
+ '@unocss/config': 66.2.3
+ '@unocss/core': 66.2.3
+ '@unocss/rule-utils': 66.2.3
+ magic-string: 0.30.17
+ synckit: 0.11.8
+ transitivePeerDependencies:
+ - eslint
+ - supports-color
+ - typescript
+
+ '@unocss/extractor-arbitrary-variants@65.4.2':
+ dependencies:
+ '@unocss/core': 65.4.2
+
+ '@unocss/extractor-arbitrary-variants@66.1.2':
+ dependencies:
+ '@unocss/core': 66.1.2
+
+ '@unocss/extractor-arbitrary-variants@66.2.0':
+ dependencies:
+ '@unocss/core': 66.2.0
+
+ '@unocss/inspector@65.4.2(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@unocss/core': 65.4.2
+ '@unocss/rule-utils': 65.4.2
+ colorette: 2.0.20
+ gzip-size: 6.0.0
+ sirv: 3.0.1
+ vue-flow-layout: 0.1.1(vue@3.5.15(typescript@5.7.2))
+ transitivePeerDependencies:
+ - vue
+
+ '@unocss/inspector@66.1.2(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@unocss/core': 66.1.2
+ '@unocss/rule-utils': 66.1.2
+ colorette: 2.0.20
+ gzip-size: 6.0.0
+ sirv: 3.0.1
+ vue-flow-layout: 0.1.1(vue@3.5.15(typescript@5.7.2))
+ transitivePeerDependencies:
+ - vue
+ optional: true
+
+ '@unocss/postcss@65.4.2(postcss@8.5.3)':
+ dependencies:
+ '@unocss/config': 65.4.2
+ '@unocss/core': 65.4.2
+ '@unocss/rule-utils': 65.4.2
+ css-tree: 3.1.0
+ postcss: 8.5.3
+ tinyglobby: 0.2.14
+ transitivePeerDependencies:
+ - supports-color
+
+ '@unocss/preset-attributify@65.4.2':
+ dependencies:
+ '@unocss/core': 65.4.2
+
+ '@unocss/preset-icons@65.4.2':
+ dependencies:
+ '@iconify/utils': 2.3.0
+ '@unocss/core': 65.4.2
+ ofetch: 1.4.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@unocss/preset-legacy-compat@0.59.4':
+ dependencies:
+ '@unocss/core': 0.59.4
+
+ '@unocss/preset-mini@65.4.2':
+ dependencies:
+ '@unocss/core': 65.4.2
+ '@unocss/extractor-arbitrary-variants': 65.4.2
+ '@unocss/rule-utils': 65.4.2
+
+ '@unocss/preset-mini@66.1.2':
+ dependencies:
+ '@unocss/core': 66.1.2
+ '@unocss/extractor-arbitrary-variants': 66.1.2
+ '@unocss/rule-utils': 66.1.2
+
+ '@unocss/preset-tagify@65.4.2':
+ dependencies:
+ '@unocss/core': 65.4.2
+
+ '@unocss/preset-typography@65.4.2':
+ dependencies:
+ '@unocss/core': 65.4.2
+ '@unocss/preset-mini': 65.4.2
+
+ '@unocss/preset-uno@65.4.2':
+ dependencies:
+ '@unocss/core': 65.4.2
+ '@unocss/preset-mini': 65.4.2
+ '@unocss/preset-wind': 65.4.2
+ '@unocss/rule-utils': 65.4.2
+
+ '@unocss/preset-web-fonts@65.4.2':
+ dependencies:
+ '@unocss/core': 65.4.2
+ ofetch: 1.4.1
+
+ '@unocss/preset-wind3@66.1.2':
+ dependencies:
+ '@unocss/core': 66.1.2
+ '@unocss/preset-mini': 66.1.2
+ '@unocss/rule-utils': 66.1.2
+
+ '@unocss/preset-wind4@66.2.0':
+ dependencies:
+ '@unocss/core': 66.2.0
+ '@unocss/extractor-arbitrary-variants': 66.2.0
+ '@unocss/rule-utils': 66.2.0
+
+ '@unocss/preset-wind@65.4.2':
+ dependencies:
+ '@unocss/core': 65.4.2
+ '@unocss/preset-mini': 65.4.2
+ '@unocss/rule-utils': 65.4.2
+
+ '@unocss/reset@65.4.2': {}
+
+ '@unocss/rule-utils@65.4.2':
+ dependencies:
+ '@unocss/core': 65.4.2
+ magic-string: 0.30.17
+
+ '@unocss/rule-utils@66.1.2':
+ dependencies:
+ '@unocss/core': 66.2.3
+ magic-string: 0.30.17
+
+ '@unocss/rule-utils@66.2.0':
+ dependencies:
+ '@unocss/core': 66.2.3
+ magic-string: 0.30.17
+
+ '@unocss/rule-utils@66.2.3':
+ dependencies:
+ '@unocss/core': 66.2.3
+ magic-string: 0.30.17
+
+ '@unocss/transformer-attributify-jsx@65.4.2':
+ dependencies:
+ '@unocss/core': 65.4.2
+
+ '@unocss/transformer-compile-class@65.4.2':
+ dependencies:
+ '@unocss/core': 65.4.2
+
+ '@unocss/transformer-directives@65.4.2':
+ dependencies:
+ '@unocss/core': 65.4.2
+ '@unocss/rule-utils': 65.4.2
+ css-tree: 3.1.0
+
+ '@unocss/transformer-variant-group@65.4.2':
+ dependencies:
+ '@unocss/core': 65.4.2
+
+ '@unocss/vite@65.4.2(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@ampproject/remapping': 2.3.0
+ '@rollup/pluginutils': 5.1.4(rollup@4.41.1)
+ '@unocss/config': 65.4.2
+ '@unocss/core': 65.4.2
+ '@unocss/inspector': 65.4.2(vue@3.5.15(typescript@5.7.2))
+ chokidar: 3.6.0
+ magic-string: 0.30.17
+ tinyglobby: 0.2.14
+ vite: 5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0)
+ transitivePeerDependencies:
+ - rollup
+ - supports-color
+ - vue
+
+ '@unocss/vite@66.1.2(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@ampproject/remapping': 2.3.0
+ '@unocss/config': 66.1.2
+ '@unocss/core': 66.1.2
+ '@unocss/inspector': 66.1.2(vue@3.5.15(typescript@5.7.2))
+ chokidar: 3.6.0
+ magic-string: 0.30.17
+ pathe: 2.0.3
+ tinyglobby: 0.2.14
+ unplugin-utils: 0.2.4
+ vite: 5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0)
+ transitivePeerDependencies:
+ - vue
+ optional: true
+
+ '@vitejs/plugin-legacy@5.3.2(terser@5.36.0)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/preset-env': 7.26.0(@babel/core@7.26.0)
+ browserslist: 4.24.2
+ browserslist-to-esbuild: 2.1.1(browserslist@4.24.2)
+ core-js: 3.39.0
+ magic-string: 0.30.17
+ regenerator-runtime: 0.14.1
+ systemjs: 6.15.1
+ terser: 5.36.0
+ vite: 5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vitejs/plugin-vue-jsx@3.1.0(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/plugin-transform-typescript': 7.25.9(@babel/core@7.26.0)
+ '@vue/babel-plugin-jsx': 1.2.5(@babel/core@7.26.0)
+ vite: 5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0)
+ vue: 3.5.15(typescript@5.7.2)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vitejs/plugin-vue@5.1.0(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ vite: 5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0)
+ vue: 3.5.15(typescript@5.7.2)
+
+ '@vitest/eslint-plugin@1.2.7(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)':
+ dependencies:
+ '@typescript-eslint/utils': 8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)
+ eslint: 9.29.0(jiti@2.4.2)
+ optionalDependencies:
+ typescript: 5.7.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@volar/language-core@2.4.14':
+ dependencies:
+ '@volar/source-map': 2.4.14
+
+ '@volar/source-map@2.4.14': {}
+
+ '@volar/typescript@2.4.14':
+ dependencies:
+ '@volar/language-core': 2.4.14
+ path-browserify: 1.0.1
+ vscode-uri: 3.1.0
+
+ '@vue/babel-helper-vue-transform-on@1.2.5': {}
+
+ '@vue/babel-plugin-jsx@1.2.5(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/helper-module-imports': 7.25.9
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0)
+ '@babel/template': 7.27.2
+ '@babel/traverse': 7.27.1
+ '@babel/types': 7.27.1
+ '@vue/babel-helper-vue-transform-on': 1.2.5
+ '@vue/babel-plugin-resolve-type': 1.2.5(@babel/core@7.26.0)
+ html-tags: 3.3.1
+ svg-tags: 1.0.0
+ optionalDependencies:
+ '@babel/core': 7.26.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vue/babel-plugin-resolve-type@1.2.5(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/core': 7.26.0
+ '@babel/helper-module-imports': 7.25.9
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/parser': 7.27.2
+ '@vue/compiler-sfc': 3.5.15
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vue/compiler-core@3.4.21':
+ dependencies:
+ '@babel/parser': 7.27.2
+ '@vue/shared': 3.4.21
+ entities: 4.5.0
+ estree-walker: 2.0.2
+ source-map-js: 1.2.1
+
+ '@vue/compiler-core@3.5.15':
+ dependencies:
+ '@babel/parser': 7.27.2
+ '@vue/shared': 3.5.15
+ entities: 4.5.0
+ estree-walker: 2.0.2
+ source-map-js: 1.2.1
+
+ '@vue/compiler-dom@3.4.21':
+ dependencies:
+ '@vue/compiler-core': 3.4.21
+ '@vue/shared': 3.4.21
+
+ '@vue/compiler-dom@3.5.15':
+ dependencies:
+ '@vue/compiler-core': 3.5.15
+ '@vue/shared': 3.5.15
+
+ '@vue/compiler-sfc@3.4.21':
+ dependencies:
+ '@babel/parser': 7.26.2
+ '@vue/compiler-core': 3.4.21
+ '@vue/compiler-dom': 3.4.21
+ '@vue/compiler-ssr': 3.4.21
+ '@vue/shared': 3.4.21
+ estree-walker: 2.0.2
+ magic-string: 0.30.14
+ postcss: 8.5.3
+ source-map-js: 1.2.1
+
+ '@vue/compiler-sfc@3.5.15':
+ dependencies:
+ '@babel/parser': 7.27.2
+ '@vue/compiler-core': 3.5.15
+ '@vue/compiler-dom': 3.5.15
+ '@vue/compiler-ssr': 3.5.15
+ '@vue/shared': 3.5.15
+ estree-walker: 2.0.2
+ magic-string: 0.30.17
+ postcss: 8.5.3
+ source-map-js: 1.2.1
+
+ '@vue/compiler-ssr@3.4.21':
+ dependencies:
+ '@vue/compiler-dom': 3.4.21
+ '@vue/shared': 3.4.21
+
+ '@vue/compiler-ssr@3.5.15':
+ dependencies:
+ '@vue/compiler-dom': 3.5.15
+ '@vue/shared': 3.5.15
+
+ '@vue/compiler-vue2@2.7.16':
+ dependencies:
+ de-indent: 1.0.2
+ he: 1.2.0
+
+ '@vue/consolidate@1.0.0': {}
+
+ '@vue/devtools-api@6.6.4': {}
+
+ '@vue/language-core@2.2.10(typescript@5.7.2)':
+ dependencies:
+ '@volar/language-core': 2.4.14
+ '@vue/compiler-dom': 3.5.15
+ '@vue/compiler-vue2': 2.7.16
+ '@vue/shared': 3.5.15
+ alien-signals: 1.0.13
+ minimatch: 9.0.5
+ muggle-string: 0.4.1
+ path-browserify: 1.0.1
+ optionalDependencies:
+ typescript: 5.7.2
+
+ '@vue/reactivity@3.5.15':
+ dependencies:
+ '@vue/shared': 3.5.15
+
+ '@vue/runtime-core@3.5.15':
+ dependencies:
+ '@vue/reactivity': 3.5.15
+ '@vue/shared': 3.5.15
+
+ '@vue/runtime-dom@3.5.15':
+ dependencies:
+ '@vue/reactivity': 3.5.15
+ '@vue/runtime-core': 3.5.15
+ '@vue/shared': 3.5.15
+ csstype: 3.1.3
+
+ '@vue/server-renderer@3.4.21(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@vue/compiler-ssr': 3.4.21
+ '@vue/shared': 3.4.21
+ vue: 3.5.15(typescript@5.7.2)
+
+ '@vue/server-renderer@3.5.15(vue@3.5.15(typescript@5.7.2))':
+ dependencies:
+ '@vue/compiler-ssr': 3.5.15
+ '@vue/shared': 3.5.15
+ vue: 3.5.15(typescript@5.7.2)
+
+ '@vue/shared@3.4.21': {}
+
+ '@vue/shared@3.5.15': {}
+
+ '@vue/tsconfig@0.1.3(@types/node@20.19.0)':
+ optionalDependencies:
+ '@types/node': 20.19.0
+
+ JSONStream@1.3.5:
+ dependencies:
+ jsonparse: 1.3.1
+ through: 2.3.8
+
+ a-sync-waterfall@1.0.1: {}
+
+ abab@2.0.6: {}
+
+ abortcontroller-polyfill@1.7.8: {}
+
+ accepts@1.3.8:
+ dependencies:
+ mime-types: 2.1.35
+ negotiator: 0.6.3
+
+ acorn-globals@6.0.0:
+ dependencies:
+ acorn: 7.4.1
+ acorn-walk: 7.2.0
+
+ acorn-jsx@5.3.2(acorn@8.15.0):
+ dependencies:
+ acorn: 8.15.0
+
+ acorn-walk@7.2.0: {}
+
+ acorn@7.4.1: {}
+
+ acorn@8.14.0: {}
+
+ acorn@8.14.1: {}
+
+ acorn@8.15.0: {}
+
+ address@1.2.2: {}
+
+ adm-zip@0.5.16: {}
+
+ agent-base@6.0.2:
+ dependencies:
+ debug: 4.4.1
+ transitivePeerDependencies:
+ - supports-color
+
+ ajv@6.12.6:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-json-stable-stringify: 2.1.0
+ json-schema-traverse: 0.4.1
+ uri-js: 4.4.1
+
+ ajv@8.17.1:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-uri: 3.0.6
+ json-schema-traverse: 1.0.0
+ require-from-string: 2.0.2
+
+ alien-signals@1.0.13: {}
+
+ alova@3.3.3:
+ dependencies:
+ '@alova/shared': 1.3.1
+ rate-limiter-flexible: 5.0.5
+
+ android-versions@1.9.0:
+ dependencies:
+ semver: 7.7.2
+
+ ansi-escapes@4.3.2:
+ dependencies:
+ type-fest: 0.21.3
+
+ ansi-escapes@7.0.0:
+ dependencies:
+ environment: 1.1.0
+
+ ansi-regex@5.0.1: {}
+
+ ansi-regex@6.1.0: {}
+
+ ansi-styles@4.3.0:
+ dependencies:
+ color-convert: 2.0.1
+
+ ansi-styles@5.2.0: {}
+
+ ansi-styles@6.2.1: {}
+
+ ansis@4.1.0: {}
+
+ any-base@1.1.0: {}
+
+ anymatch@3.1.3:
+ dependencies:
+ normalize-path: 3.0.0
+ picomatch: 2.3.1
+
+ are-docs-informative@0.0.2: {}
+
+ argparse@1.0.10:
+ dependencies:
+ sprintf-js: 1.0.3
+
+ argparse@2.0.1: {}
+
+ array-flatten@1.1.1: {}
+
+ array-ify@1.0.0: {}
+
+ asap@2.0.6: {}
+
+ ast-kit@0.11.3(rollup@4.41.1):
+ dependencies:
+ '@babel/parser': 7.27.5
+ '@rollup/pluginutils': 5.1.4(rollup@4.41.1)
+ pathe: 1.1.2
+ transitivePeerDependencies:
+ - rollup
+
+ asynckit@0.4.0: {}
+
+ autoprefixer@10.4.20(postcss@8.5.3):
+ dependencies:
+ browserslist: 4.24.2
+ caniuse-lite: 1.0.30001686
+ fraction.js: 4.3.7
+ normalize-range: 0.1.2
+ picocolors: 1.1.1
+ postcss: 8.5.3
+ postcss-value-parser: 4.2.0
+
+ axios@1.7.9:
+ dependencies:
+ follow-redirects: 1.15.9
+ form-data: 4.0.1
+ proxy-from-env: 1.1.0
+ transitivePeerDependencies:
+ - debug
+
+ babel-jest@27.5.1(@babel/core@7.26.0):
+ dependencies:
+ '@babel/core': 7.26.0
+ '@jest/transform': 27.5.1
+ '@jest/types': 27.5.1
+ '@types/babel__core': 7.20.5
+ babel-plugin-istanbul: 6.1.1
+ babel-preset-jest: 27.5.1(@babel/core@7.26.0)
+ chalk: 4.1.2
+ graceful-fs: 4.2.11
+ slash: 3.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ babel-plugin-istanbul@6.1.1:
+ dependencies:
+ '@babel/helper-plugin-utils': 7.27.1
+ '@istanbuljs/load-nyc-config': 1.1.0
+ '@istanbuljs/schema': 0.1.3
+ istanbul-lib-instrument: 5.2.1
+ test-exclude: 6.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ babel-plugin-jest-hoist@27.5.1:
+ dependencies:
+ '@babel/template': 7.27.2
+ '@babel/types': 7.27.6
+ '@types/babel__core': 7.20.5
+ '@types/babel__traverse': 7.20.7
+
+ babel-plugin-polyfill-corejs2@0.4.12(@babel/core@7.26.0):
+ dependencies:
+ '@babel/compat-data': 7.26.2
+ '@babel/core': 7.26.0
+ '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0)
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ babel-plugin-polyfill-corejs3@0.10.6(@babel/core@7.26.0):
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0)
+ core-js-compat: 3.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ babel-plugin-polyfill-regenerator@0.6.3(@babel/core@7.26.0):
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ babel-preset-current-node-syntax@1.1.0(@babel/core@7.26.0):
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.26.0)
+ '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.26.0)
+ '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.26.0)
+ '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.26.0)
+ '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.26.0)
+ '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.26.0)
+ '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.26.0)
+ '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.26.0)
+ '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.26.0)
+ '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.26.0)
+ '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.26.0)
+ '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.26.0)
+ '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.26.0)
+ '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.26.0)
+ '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.26.0)
+
+ babel-preset-jest@27.5.1(@babel/core@7.26.0):
+ dependencies:
+ '@babel/core': 7.26.0
+ babel-plugin-jest-hoist: 27.5.1
+ babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.0)
+
+ balanced-match@1.0.2: {}
+
+ base64-js@1.5.1: {}
+
+ base64url@3.0.1: {}
+
+ binary-extensions@2.3.0: {}
+
+ bing-translate-api@4.0.2:
+ dependencies:
+ got: 11.8.6
+
+ bl@4.1.0:
+ dependencies:
+ buffer: 5.7.1
+ inherits: 2.0.4
+ readable-stream: 3.6.2
+
+ bmp-js@0.1.0: {}
+
+ body-parser@1.20.3:
+ dependencies:
+ bytes: 3.1.2
+ content-type: 1.0.5
+ debug: 2.6.9
+ depd: 2.0.0
+ destroy: 1.2.0
+ http-errors: 2.0.0
+ iconv-lite: 0.4.24
+ on-finished: 2.4.1
+ qs: 6.13.0
+ raw-body: 2.5.2
+ type-is: 1.6.18
+ unpipe: 1.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ boolbase@1.0.0: {}
+
+ brace-expansion@1.1.12:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ brace-expansion@2.0.1:
+ dependencies:
+ balanced-match: 1.0.2
+
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+
+ browser-process-hrtime@1.0.0: {}
+
+ browserslist-to-esbuild@2.1.1(browserslist@4.24.2):
+ dependencies:
+ browserslist: 4.24.2
+ meow: 13.2.0
+
+ browserslist@4.24.2:
+ dependencies:
+ caniuse-lite: 1.0.30001686
+ electron-to-chromium: 1.5.68
+ node-releases: 2.0.18
+ update-browserslist-db: 1.1.1(browserslist@4.24.2)
+
+ browserslist@4.25.0:
+ dependencies:
+ caniuse-lite: 1.0.30001724
+ electron-to-chromium: 1.5.171
+ node-releases: 2.0.19
+ update-browserslist-db: 1.1.3(browserslist@4.25.0)
+
+ bser@2.1.1:
+ dependencies:
+ node-int64: 0.4.0
+
+ buffer-equal@0.0.1: {}
+
+ buffer-from@1.1.2: {}
+
+ buffer@5.7.1:
+ dependencies:
+ base64-js: 1.5.1
+ ieee754: 1.2.1
+
+ builtin-modules@5.0.0: {}
+
+ bundle-require@5.1.0(esbuild@0.20.2):
+ dependencies:
+ esbuild: 0.20.2
+ load-tsconfig: 0.2.5
+
+ bundle-require@5.1.0(esbuild@0.25.5):
+ dependencies:
+ esbuild: 0.25.5
+ load-tsconfig: 0.2.5
+
+ bytes@3.1.2: {}
+
+ c12@1.11.2:
+ dependencies:
+ chokidar: 3.6.0
+ confbox: 0.1.8
+ defu: 6.1.4
+ dotenv: 16.5.0
+ giget: 1.2.5
+ jiti: 1.21.6
+ mlly: 1.7.4
+ ohash: 1.1.4
+ pathe: 1.1.2
+ perfect-debounce: 1.0.0
+ pkg-types: 1.3.1
+ rc9: 2.1.2
+
+ c12@2.0.4:
+ dependencies:
+ chokidar: 4.0.3
+ confbox: 0.1.8
+ defu: 6.1.4
+ dotenv: 16.5.0
+ giget: 1.2.5
+ jiti: 2.4.2
+ mlly: 1.7.4
+ ohash: 2.0.11
+ pathe: 2.0.3
+ perfect-debounce: 1.0.0
+ pkg-types: 1.3.1
+ rc9: 2.1.2
+
+ cac@6.7.14: {}
+
+ cac@6.7.9: {}
+
+ cacheable-lookup@5.0.4: {}
+
+ cacheable-request@7.0.4:
+ dependencies:
+ clone-response: 1.0.3
+ get-stream: 5.2.0
+ http-cache-semantics: 4.1.1
+ keyv: 4.5.4
+ lowercase-keys: 2.0.0
+ normalize-url: 6.1.0
+ responselike: 2.0.1
+
+ call-bind-apply-helpers@1.0.2:
+ dependencies:
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+
+ call-bind@1.0.7:
+ dependencies:
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+ get-intrinsic: 1.3.0
+ set-function-length: 1.2.2
+
+ call-me-maybe@1.0.2: {}
+
+ callsites@3.1.0: {}
+
+ camelcase@5.3.1: {}
+
+ camelcase@6.3.0: {}
+
+ caniuse-lite@1.0.30001686: {}
+
+ caniuse-lite@1.0.30001724: {}
+
+ ccount@2.0.1: {}
+
+ centra@2.7.0:
+ dependencies:
+ follow-redirects: 1.15.9
+ transitivePeerDependencies:
+ - debug
+
+ chalk@4.1.2:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
+ chalk@5.3.0: {}
+
+ char-regex@1.0.2: {}
+
+ character-entities@2.0.2: {}
+
+ chokidar@3.6.0:
+ dependencies:
+ anymatch: 3.1.3
+ braces: 3.0.3
+ glob-parent: 5.1.2
+ is-binary-path: 2.1.0
+ is-glob: 4.0.3
+ normalize-path: 3.0.0
+ readdirp: 3.6.0
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ chokidar@4.0.3:
+ dependencies:
+ readdirp: 4.1.2
+
+ chownr@2.0.0: {}
+
+ ci-info@3.9.0: {}
+
+ ci-info@4.2.0: {}
+
+ citty@0.1.6:
+ dependencies:
+ consola: 3.4.2
+
+ cjs-module-lexer@1.4.3: {}
+
+ clean-regexp@1.0.0:
+ dependencies:
+ escape-string-regexp: 1.0.5
+
+ cli-cursor@3.1.0:
+ dependencies:
+ restore-cursor: 3.1.0
+
+ cli-cursor@5.0.0:
+ dependencies:
+ restore-cursor: 5.1.0
+
+ cli-spinners@2.9.2: {}
+
+ cli-truncate@4.0.0:
+ dependencies:
+ slice-ansi: 5.0.0
+ string-width: 7.2.0
+
+ cliui@7.0.4:
+ dependencies:
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ wrap-ansi: 7.0.0
+
+ cliui@8.0.1:
+ dependencies:
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ wrap-ansi: 7.0.0
+
+ clone-response@1.0.3:
+ dependencies:
+ mimic-response: 1.0.1
+
+ clone@1.0.4: {}
+
+ co@4.6.0: {}
+
+ collect-v8-coverage@1.0.2: {}
+
+ color-convert@2.0.1:
+ dependencies:
+ color-name: 1.1.4
+
+ color-name@1.1.4: {}
+
+ colorette@2.0.20: {}
+
+ colors@1.4.0: {}
+
+ combined-stream@1.0.8:
+ dependencies:
+ delayed-stream: 1.0.0
+
+ commander@12.1.0: {}
+
+ commander@2.20.3: {}
+
+ commander@5.1.0: {}
+
+ comment-parser@1.4.1: {}
+
+ commondir@1.0.1: {}
+
+ compare-func@2.0.0:
+ dependencies:
+ array-ify: 1.0.0
+ dot-prop: 5.3.0
+
+ compare-versions@3.6.0: {}
+
+ concat-map@0.0.1: {}
+
+ confbox@0.1.8: {}
+
+ confbox@0.2.2: {}
+
+ consola@3.4.2: {}
+
+ content-disposition@0.5.4:
+ dependencies:
+ safe-buffer: 5.2.1
+
+ content-type@1.0.5: {}
+
+ conventional-changelog-angular@7.0.0:
+ dependencies:
+ compare-func: 2.0.0
+
+ conventional-changelog-conventionalcommits@7.0.2:
+ dependencies:
+ compare-func: 2.0.0
+
+ conventional-commits-parser@5.0.0:
+ dependencies:
+ JSONStream: 1.3.5
+ is-text-path: 2.0.0
+ meow: 12.1.1
+ split2: 4.2.0
+
+ convert-source-map@1.9.0: {}
+
+ convert-source-map@2.0.0: {}
+
+ cookie-signature@1.0.6: {}
+
+ cookie@0.7.1: {}
+
+ core-js-compat@3.39.0:
+ dependencies:
+ browserslist: 4.24.2
+
+ core-js-compat@3.43.0:
+ dependencies:
+ browserslist: 4.25.0
+
+ core-js@3.39.0: {}
+
+ cosmiconfig-typescript-loader@6.1.0(@types/node@20.19.0)(cosmiconfig@9.0.0(typescript@5.7.2))(typescript@5.7.2):
+ dependencies:
+ '@types/node': 20.19.0
+ cosmiconfig: 9.0.0(typescript@5.7.2)
+ jiti: 2.4.2
+ typescript: 5.7.2
+
+ cosmiconfig@9.0.0(typescript@5.7.2):
+ dependencies:
+ env-paths: 2.2.1
+ import-fresh: 3.3.0
+ js-yaml: 4.1.0
+ parse-json: 5.2.0
+ optionalDependencies:
+ typescript: 5.7.2
+
+ cross-env@7.0.3:
+ dependencies:
+ cross-spawn: 7.0.6
+
+ cross-spawn@7.0.6:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ css-font-size-keywords@1.0.0: {}
+
+ css-font-stretch-keywords@1.0.1: {}
+
+ css-font-style-keywords@1.0.1: {}
+
+ css-font-weight-keywords@1.0.0: {}
+
+ css-list-helpers@2.0.0: {}
+
+ css-system-font-keywords@1.0.0: {}
+
+ css-tree@3.1.0:
+ dependencies:
+ mdn-data: 2.12.2
+ source-map-js: 1.2.1
+
+ cssesc@3.0.0: {}
+
+ cssom@0.3.8: {}
+
+ cssom@0.4.4: {}
+
+ cssstyle@2.3.0:
+ dependencies:
+ cssom: 0.3.8
+
+ csstype@3.1.3: {}
+
+ d@1.0.2:
+ dependencies:
+ es5-ext: 0.10.64
+ type: 2.7.3
+
+ dargs@8.1.0: {}
+
+ data-urls@2.0.0:
+ dependencies:
+ abab: 2.0.6
+ whatwg-mimetype: 2.3.0
+ whatwg-url: 8.7.0
+
+ dayjs@1.11.10: {}
+
+ de-indent@1.0.2: {}
+
+ debug@2.6.9:
+ dependencies:
+ ms: 2.0.0
+
+ debug@4.3.7:
+ dependencies:
+ ms: 2.1.3
+
+ debug@4.4.1:
+ dependencies:
+ ms: 2.1.3
+
+ decimal.js@10.5.0: {}
+
+ decode-named-character-reference@1.2.0:
+ dependencies:
+ character-entities: 2.0.2
+
+ decompress-response@6.0.0:
+ dependencies:
+ mimic-response: 3.1.0
+
+ dedent@0.7.0: {}
+
+ deep-is@0.1.4: {}
+
+ deepmerge@4.3.1: {}
+
+ default-gateway@6.0.3:
+ dependencies:
+ execa: 5.1.1
+
+ defaults@1.0.4:
+ dependencies:
+ clone: 1.0.4
+
+ defer-to-connect@2.0.1: {}
+
+ define-data-property@1.1.4:
+ dependencies:
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
+ define-lazy-prop@2.0.0: {}
+
+ defu@6.1.4: {}
+
+ delayed-stream@1.0.0: {}
+
+ depd@2.0.0: {}
+
+ dequal@2.0.3: {}
+
+ destr@2.0.5: {}
+
+ destroy@1.2.0: {}
+
+ detect-indent@6.1.0: {}
+
+ detect-newline@3.1.0: {}
+
+ devlop@1.1.0:
+ dependencies:
+ dequal: 2.0.3
+
+ diff-sequences@27.5.1: {}
+
+ dom-serializer@2.0.0:
+ dependencies:
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ entities: 4.5.0
+
+ dom-walk@0.1.2: {}
+
+ domelementtype@2.3.0: {}
+
+ domexception@2.0.1:
+ dependencies:
+ webidl-conversions: 5.0.0
+
+ domhandler@5.0.3:
+ dependencies:
+ domelementtype: 2.3.0
+
+ domutils@3.1.0:
+ dependencies:
+ dom-serializer: 2.0.0
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+
+ dot-prop@5.3.0:
+ dependencies:
+ is-obj: 2.0.0
+
+ dotenv@16.5.0: {}
+
+ dunder-proto@1.0.1:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
+ duplexer@0.1.2: {}
+
+ eastasianwidth@0.2.0: {}
+
+ ee-first@1.1.1: {}
+
+ electron-to-chromium@1.5.171: {}
+
+ electron-to-chromium@1.5.68: {}
+
+ emittery@0.8.1: {}
+
+ emoji-regex@10.4.0: {}
+
+ emoji-regex@8.0.0: {}
+
+ emoji-regex@9.2.2: {}
+
+ encodeurl@1.0.2: {}
+
+ encodeurl@2.0.0: {}
+
+ end-of-stream@1.4.4:
+ dependencies:
+ once: 1.4.0
+
+ enhanced-resolve@5.18.1:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.2.1
+
+ entities@4.5.0: {}
+
+ env-paths@2.2.1: {}
+
+ environment@1.1.0: {}
+
+ error-ex@1.3.2:
+ dependencies:
+ is-arrayish: 0.2.1
+
+ es-define-property@1.0.1: {}
+
+ es-errors@1.3.0: {}
+
+ es-module-lexer@1.5.4: {}
+
+ es-object-atoms@1.1.1:
+ dependencies:
+ es-errors: 1.3.0
+
+ es-set-tostringtag@2.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ has-tostringtag: 1.0.2
+ hasown: 2.0.2
+
+ es5-ext@0.10.64:
+ dependencies:
+ es6-iterator: 2.0.3
+ es6-symbol: 3.1.4
+ esniff: 2.0.1
+ next-tick: 1.1.0
+
+ es6-iterator@2.0.3:
+ dependencies:
+ d: 1.0.2
+ es5-ext: 0.10.64
+ es6-symbol: 3.1.4
+
+ es6-promise@3.3.1: {}
+
+ es6-symbol@3.1.4:
+ dependencies:
+ d: 1.0.2
+ ext: 1.7.0
+
+ es6-weak-map@2.0.3:
+ dependencies:
+ d: 1.0.2
+ es5-ext: 0.10.64
+ es6-iterator: 2.0.3
+ es6-symbol: 3.1.4
+
+ esbuild@0.20.2:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.20.2
+ '@esbuild/android-arm': 0.20.2
+ '@esbuild/android-arm64': 0.20.2
+ '@esbuild/android-x64': 0.20.2
+ '@esbuild/darwin-arm64': 0.20.2
+ '@esbuild/darwin-x64': 0.20.2
+ '@esbuild/freebsd-arm64': 0.20.2
+ '@esbuild/freebsd-x64': 0.20.2
+ '@esbuild/linux-arm': 0.20.2
+ '@esbuild/linux-arm64': 0.20.2
+ '@esbuild/linux-ia32': 0.20.2
+ '@esbuild/linux-loong64': 0.20.2
+ '@esbuild/linux-mips64el': 0.20.2
+ '@esbuild/linux-ppc64': 0.20.2
+ '@esbuild/linux-riscv64': 0.20.2
+ '@esbuild/linux-s390x': 0.20.2
+ '@esbuild/linux-x64': 0.20.2
+ '@esbuild/netbsd-x64': 0.20.2
+ '@esbuild/openbsd-x64': 0.20.2
+ '@esbuild/sunos-x64': 0.20.2
+ '@esbuild/win32-arm64': 0.20.2
+ '@esbuild/win32-ia32': 0.20.2
+ '@esbuild/win32-x64': 0.20.2
+
+ esbuild@0.24.2:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.24.2
+ '@esbuild/android-arm': 0.24.2
+ '@esbuild/android-arm64': 0.24.2
+ '@esbuild/android-x64': 0.24.2
+ '@esbuild/darwin-arm64': 0.24.2
+ '@esbuild/darwin-x64': 0.24.2
+ '@esbuild/freebsd-arm64': 0.24.2
+ '@esbuild/freebsd-x64': 0.24.2
+ '@esbuild/linux-arm': 0.24.2
+ '@esbuild/linux-arm64': 0.24.2
+ '@esbuild/linux-ia32': 0.24.2
+ '@esbuild/linux-loong64': 0.24.2
+ '@esbuild/linux-mips64el': 0.24.2
+ '@esbuild/linux-ppc64': 0.24.2
+ '@esbuild/linux-riscv64': 0.24.2
+ '@esbuild/linux-s390x': 0.24.2
+ '@esbuild/linux-x64': 0.24.2
+ '@esbuild/netbsd-arm64': 0.24.2
+ '@esbuild/netbsd-x64': 0.24.2
+ '@esbuild/openbsd-arm64': 0.24.2
+ '@esbuild/openbsd-x64': 0.24.2
+ '@esbuild/sunos-x64': 0.24.2
+ '@esbuild/win32-arm64': 0.24.2
+ '@esbuild/win32-ia32': 0.24.2
+ '@esbuild/win32-x64': 0.24.2
+
+ esbuild@0.25.5:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.25.5
+ '@esbuild/android-arm': 0.25.5
+ '@esbuild/android-arm64': 0.25.5
+ '@esbuild/android-x64': 0.25.5
+ '@esbuild/darwin-arm64': 0.25.5
+ '@esbuild/darwin-x64': 0.25.5
+ '@esbuild/freebsd-arm64': 0.25.5
+ '@esbuild/freebsd-x64': 0.25.5
+ '@esbuild/linux-arm': 0.25.5
+ '@esbuild/linux-arm64': 0.25.5
+ '@esbuild/linux-ia32': 0.25.5
+ '@esbuild/linux-loong64': 0.25.5
+ '@esbuild/linux-mips64el': 0.25.5
+ '@esbuild/linux-ppc64': 0.25.5
+ '@esbuild/linux-riscv64': 0.25.5
+ '@esbuild/linux-s390x': 0.25.5
+ '@esbuild/linux-x64': 0.25.5
+ '@esbuild/netbsd-arm64': 0.25.5
+ '@esbuild/netbsd-x64': 0.25.5
+ '@esbuild/openbsd-arm64': 0.25.5
+ '@esbuild/openbsd-x64': 0.25.5
+ '@esbuild/sunos-x64': 0.25.5
+ '@esbuild/win32-arm64': 0.25.5
+ '@esbuild/win32-ia32': 0.25.5
+ '@esbuild/win32-x64': 0.25.5
+
+ escalade@3.2.0: {}
+
+ escape-html@1.0.3: {}
+
+ escape-string-regexp@1.0.5: {}
+
+ escape-string-regexp@2.0.0: {}
+
+ escape-string-regexp@4.0.0: {}
+
+ escape-string-regexp@5.0.0: {}
+
+ escodegen@2.1.0:
+ dependencies:
+ esprima: 4.0.1
+ estraverse: 5.3.0
+ esutils: 2.0.3
+ optionalDependencies:
+ source-map: 0.6.1
+
+ eslint-compat-utils@0.5.1(eslint@9.29.0(jiti@2.4.2)):
+ dependencies:
+ eslint: 9.29.0(jiti@2.4.2)
+ semver: 7.7.2
+
+ eslint-compat-utils@0.6.5(eslint@9.29.0(jiti@2.4.2)):
+ dependencies:
+ eslint: 9.29.0(jiti@2.4.2)
+ semver: 7.7.2
+
+ eslint-config-flat-gitignore@2.1.0(eslint@9.29.0(jiti@2.4.2)):
+ dependencies:
+ '@eslint/compat': 1.3.0(eslint@9.29.0(jiti@2.4.2))
+ eslint: 9.29.0(jiti@2.4.2)
+
+ eslint-flat-config-utils@2.1.0:
+ dependencies:
+ pathe: 2.0.3
+
+ eslint-formatting-reporter@0.0.0(eslint@9.29.0(jiti@2.4.2)):
+ dependencies:
+ eslint: 9.29.0(jiti@2.4.2)
+ prettier-linter-helpers: 1.0.0
+
+ eslint-json-compat-utils@0.2.1(eslint@9.29.0(jiti@2.4.2))(jsonc-eslint-parser@2.4.0):
+ dependencies:
+ eslint: 9.29.0(jiti@2.4.2)
+ esquery: 1.6.0
+ jsonc-eslint-parser: 2.4.0
+
+ eslint-merge-processors@2.0.0(eslint@9.29.0(jiti@2.4.2)):
+ dependencies:
+ eslint: 9.29.0(jiti@2.4.2)
+
+ eslint-parser-plain@0.1.1: {}
+
+ eslint-plugin-antfu@3.1.1(eslint@9.29.0(jiti@2.4.2)):
+ dependencies:
+ eslint: 9.29.0(jiti@2.4.2)
+
+ eslint-plugin-command@3.3.0(eslint@9.29.0(jiti@2.4.2)):
+ dependencies:
+ '@es-joy/jsdoccomment': 0.50.2
+ eslint: 9.29.0(jiti@2.4.2)
+
+ eslint-plugin-es-x@7.8.0(eslint@9.29.0(jiti@2.4.2)):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0(jiti@2.4.2))
+ '@eslint-community/regexpp': 4.12.1
+ eslint: 9.29.0(jiti@2.4.2)
+ eslint-compat-utils: 0.5.1(eslint@9.29.0(jiti@2.4.2))
+
+ eslint-plugin-format@1.0.1(eslint@9.29.0(jiti@2.4.2)):
+ dependencies:
+ '@dprint/formatter': 0.3.0
+ '@dprint/markdown': 0.17.8
+ '@dprint/toml': 0.6.4
+ eslint: 9.29.0(jiti@2.4.2)
+ eslint-formatting-reporter: 0.0.0(eslint@9.29.0(jiti@2.4.2))
+ eslint-parser-plain: 0.1.1
+ prettier: 3.5.3
+ synckit: 0.9.3
+
+ eslint-plugin-import-lite@0.3.0(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0(jiti@2.4.2))
+ '@typescript-eslint/types': 8.34.1
+ eslint: 9.29.0(jiti@2.4.2)
+ optionalDependencies:
+ typescript: 5.7.2
+
+ eslint-plugin-jsdoc@51.1.0(eslint@9.29.0(jiti@2.4.2)):
+ dependencies:
+ '@es-joy/jsdoccomment': 0.51.1
+ are-docs-informative: 0.0.2
+ comment-parser: 1.4.1
+ debug: 4.4.1
+ escape-string-regexp: 4.0.0
+ eslint: 9.29.0(jiti@2.4.2)
+ espree: 10.4.0
+ esquery: 1.6.0
+ parse-imports-exports: 0.2.4
+ semver: 7.7.2
+ spdx-expression-parse: 4.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-plugin-jsonc@2.20.1(eslint@9.29.0(jiti@2.4.2)):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0(jiti@2.4.2))
+ eslint: 9.29.0(jiti@2.4.2)
+ eslint-compat-utils: 0.6.5(eslint@9.29.0(jiti@2.4.2))
+ eslint-json-compat-utils: 0.2.1(eslint@9.29.0(jiti@2.4.2))(jsonc-eslint-parser@2.4.0)
+ espree: 10.4.0
+ graphemer: 1.4.0
+ jsonc-eslint-parser: 2.4.0
+ natural-compare: 1.4.0
+ synckit: 0.11.8
+ transitivePeerDependencies:
+ - '@eslint/json'
+
+ eslint-plugin-n@17.20.0(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0(jiti@2.4.2))
+ '@typescript-eslint/utils': 8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)
+ enhanced-resolve: 5.18.1
+ eslint: 9.29.0(jiti@2.4.2)
+ eslint-plugin-es-x: 7.8.0(eslint@9.29.0(jiti@2.4.2))
+ get-tsconfig: 4.10.1
+ globals: 15.15.0
+ ignore: 5.3.2
+ minimatch: 9.0.5
+ semver: 7.7.2
+ ts-declaration-location: 1.0.7(typescript@5.7.2)
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ eslint-plugin-no-only-tests@3.3.0: {}
+
+ eslint-plugin-perfectionist@4.15.0(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2):
+ dependencies:
+ '@typescript-eslint/types': 8.34.1
+ '@typescript-eslint/utils': 8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)
+ eslint: 9.29.0(jiti@2.4.2)
+ natural-orderby: 5.0.0
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ eslint-plugin-pnpm@0.3.1(eslint@9.29.0(jiti@2.4.2)):
+ dependencies:
+ eslint: 9.29.0(jiti@2.4.2)
+ find-up-simple: 1.0.1
+ jsonc-eslint-parser: 2.4.0
+ pathe: 2.0.3
+ pnpm-workspace-yaml: 0.3.1
+ tinyglobby: 0.2.14
+ yaml-eslint-parser: 1.3.0
+
+ eslint-plugin-regexp@2.9.0(eslint@9.29.0(jiti@2.4.2)):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0(jiti@2.4.2))
+ '@eslint-community/regexpp': 4.12.1
+ comment-parser: 1.4.1
+ eslint: 9.29.0(jiti@2.4.2)
+ jsdoc-type-pratt-parser: 4.1.0
+ refa: 0.12.1
+ regexp-ast-analysis: 0.7.1
+ scslre: 0.3.0
+
+ eslint-plugin-toml@0.12.0(eslint@9.29.0(jiti@2.4.2)):
+ dependencies:
+ debug: 4.4.1
+ eslint: 9.29.0(jiti@2.4.2)
+ eslint-compat-utils: 0.6.5(eslint@9.29.0(jiti@2.4.2))
+ lodash: 4.17.21
+ toml-eslint-parser: 0.10.0
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-plugin-unicorn@59.0.1(eslint@9.29.0(jiti@2.4.2)):
+ dependencies:
+ '@babel/helper-validator-identifier': 7.27.1
+ '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0(jiti@2.4.2))
+ '@eslint/plugin-kit': 0.2.8
+ ci-info: 4.2.0
+ clean-regexp: 1.0.0
+ core-js-compat: 3.43.0
+ eslint: 9.29.0(jiti@2.4.2)
+ esquery: 1.6.0
+ find-up-simple: 1.0.1
+ globals: 16.2.0
+ indent-string: 5.0.0
+ is-builtin-module: 5.0.0
+ jsesc: 3.1.0
+ pluralize: 8.0.0
+ regexp-tree: 0.1.27
+ regjsparser: 0.12.0
+ semver: 7.7.2
+ strip-indent: 4.0.0
+
+ eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.29.0(jiti@2.4.2)):
+ dependencies:
+ eslint: 9.29.0(jiti@2.4.2)
+ optionalDependencies:
+ '@typescript-eslint/eslint-plugin': 8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.29.0(jiti@2.4.2))(typescript@5.7.2)
+
+ eslint-plugin-vue@10.2.0(eslint@9.29.0(jiti@2.4.2))(vue-eslint-parser@10.1.3(eslint@9.29.0(jiti@2.4.2))):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0(jiti@2.4.2))
+ eslint: 9.29.0(jiti@2.4.2)
+ natural-compare: 1.4.0
+ nth-check: 2.1.1
+ postcss-selector-parser: 6.1.2
+ semver: 7.7.2
+ vue-eslint-parser: 10.1.3(eslint@9.29.0(jiti@2.4.2))
+ xml-name-validator: 4.0.0
+
+ eslint-plugin-yml@1.18.0(eslint@9.29.0(jiti@2.4.2)):
+ dependencies:
+ debug: 4.4.1
+ escape-string-regexp: 4.0.0
+ eslint: 9.29.0(jiti@2.4.2)
+ eslint-compat-utils: 0.6.5(eslint@9.29.0(jiti@2.4.2))
+ natural-compare: 1.4.0
+ yaml-eslint-parser: 1.3.0
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-processor-vue-blocks@2.0.0(@vue/compiler-sfc@3.5.15)(eslint@9.29.0(jiti@2.4.2)):
+ dependencies:
+ '@vue/compiler-sfc': 3.5.15
+ eslint: 9.29.0(jiti@2.4.2)
+
+ eslint-scope@8.4.0:
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+
+ eslint-visitor-keys@3.4.3: {}
+
+ eslint-visitor-keys@4.2.1: {}
+
+ eslint@9.29.0(jiti@2.4.2):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0(jiti@2.4.2))
+ '@eslint-community/regexpp': 4.12.1
+ '@eslint/config-array': 0.20.1
+ '@eslint/config-helpers': 0.2.3
+ '@eslint/core': 0.14.0
+ '@eslint/eslintrc': 3.3.1
+ '@eslint/js': 9.29.0
+ '@eslint/plugin-kit': 0.3.2
+ '@humanfs/node': 0.16.6
+ '@humanwhocodes/module-importer': 1.0.1
+ '@humanwhocodes/retry': 0.4.3
+ '@types/estree': 1.0.7
+ '@types/json-schema': 7.0.15
+ ajv: 6.12.6
+ chalk: 4.1.2
+ cross-spawn: 7.0.6
+ debug: 4.4.1
+ escape-string-regexp: 4.0.0
+ eslint-scope: 8.4.0
+ eslint-visitor-keys: 4.2.1
+ espree: 10.4.0
+ esquery: 1.6.0
+ esutils: 2.0.3
+ fast-deep-equal: 3.1.3
+ file-entry-cache: 8.0.0
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ ignore: 5.3.2
+ imurmurhash: 0.1.4
+ is-glob: 4.0.3
+ json-stable-stringify-without-jsonify: 1.0.1
+ lodash.merge: 4.6.2
+ minimatch: 3.1.2
+ natural-compare: 1.4.0
+ optionator: 0.9.4
+ optionalDependencies:
+ jiti: 2.4.2
+ transitivePeerDependencies:
+ - supports-color
+
+ esniff@2.0.1:
+ dependencies:
+ d: 1.0.2
+ es5-ext: 0.10.64
+ event-emitter: 0.3.5
+ type: 2.7.3
+
+ espree@10.4.0:
+ dependencies:
+ acorn: 8.15.0
+ acorn-jsx: 5.3.2(acorn@8.15.0)
+ eslint-visitor-keys: 4.2.1
+
+ espree@9.6.1:
+ dependencies:
+ acorn: 8.15.0
+ acorn-jsx: 5.3.2(acorn@8.15.0)
+ eslint-visitor-keys: 3.4.3
+
+ esprima@4.0.1: {}
+
+ esquery@1.6.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ esrecurse@4.3.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ estraverse@5.3.0: {}
+
+ estree-walker@2.0.2: {}
+
+ estree-walker@3.0.3:
+ dependencies:
+ '@types/estree': 1.0.8
+
+ esutils@2.0.3: {}
+
+ etag@1.8.1: {}
+
+ event-emitter@0.3.5:
+ dependencies:
+ d: 1.0.2
+ es5-ext: 0.10.64
+
+ eventemitter3@5.0.1: {}
+
+ execa@5.1.1:
+ dependencies:
+ cross-spawn: 7.0.6
+ get-stream: 6.0.1
+ human-signals: 2.1.0
+ is-stream: 2.0.1
+ merge-stream: 2.0.0
+ npm-run-path: 4.0.1
+ onetime: 5.1.2
+ signal-exit: 3.0.7
+ strip-final-newline: 2.0.0
+
+ execa@8.0.1:
+ dependencies:
+ cross-spawn: 7.0.6
+ get-stream: 8.0.1
+ human-signals: 5.0.0
+ is-stream: 3.0.0
+ merge-stream: 2.0.0
+ npm-run-path: 5.3.0
+ onetime: 6.0.0
+ signal-exit: 4.1.0
+ strip-final-newline: 3.0.0
+
+ exif-parser@0.1.12: {}
+
+ exit@0.1.2: {}
+
+ expect@27.5.1:
+ dependencies:
+ '@jest/types': 27.5.1
+ jest-get-type: 27.5.1
+ jest-matcher-utils: 27.5.1
+ jest-message-util: 27.5.1
+
+ express@4.21.1:
+ dependencies:
+ accepts: 1.3.8
+ array-flatten: 1.1.1
+ body-parser: 1.20.3
+ content-disposition: 0.5.4
+ content-type: 1.0.5
+ cookie: 0.7.1
+ cookie-signature: 1.0.6
+ debug: 2.6.9
+ depd: 2.0.0
+ encodeurl: 2.0.0
+ escape-html: 1.0.3
+ etag: 1.8.1
+ finalhandler: 1.3.1
+ fresh: 0.5.2
+ http-errors: 2.0.0
+ merge-descriptors: 1.0.3
+ methods: 1.1.2
+ on-finished: 2.4.1
+ parseurl: 1.3.3
+ path-to-regexp: 0.1.10
+ proxy-addr: 2.0.7
+ qs: 6.13.0
+ range-parser: 1.2.1
+ safe-buffer: 5.2.1
+ send: 0.19.0
+ serve-static: 1.16.2
+ setprototypeof: 1.2.0
+ statuses: 2.0.1
+ type-is: 1.6.18
+ utils-merge: 1.0.1
+ vary: 1.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ exsolve@1.0.5: {}
+
+ ext@1.7.0:
+ dependencies:
+ type: 2.7.3
+
+ fast-deep-equal@3.1.3: {}
+
+ fast-diff@1.3.0: {}
+
+ fast-glob@3.3.2:
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
+ fast-glob@3.3.3:
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
+ fast-json-stable-stringify@2.1.0: {}
+
+ fast-levenshtein@2.0.6: {}
+
+ fast-safe-stringify@2.1.1: {}
+
+ fast-uri@3.0.6: {}
+
+ fastq@1.17.1:
+ dependencies:
+ reusify: 1.0.4
+
+ fault@2.0.1:
+ dependencies:
+ format: 0.2.2
+
+ fb-watchman@2.0.2:
+ dependencies:
+ bser: 2.1.1
+
+ fdir@6.4.2(picomatch@4.0.2):
+ optionalDependencies:
+ picomatch: 4.0.2
+
+ fdir@6.4.4(picomatch@4.0.2):
+ optionalDependencies:
+ picomatch: 4.0.2
+
+ file-entry-cache@8.0.0:
+ dependencies:
+ flat-cache: 4.0.1
+
+ file-type@9.0.0: {}
+
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+
+ finalhandler@1.3.1:
+ dependencies:
+ debug: 2.6.9
+ encodeurl: 2.0.0
+ escape-html: 1.0.3
+ on-finished: 2.4.1
+ parseurl: 1.3.3
+ statuses: 2.0.1
+ unpipe: 1.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ find-cache-dir@3.3.2:
+ dependencies:
+ commondir: 1.0.1
+ make-dir: 3.1.0
+ pkg-dir: 4.2.0
+
+ find-up-simple@1.0.1: {}
+
+ find-up@4.1.0:
+ dependencies:
+ locate-path: 5.0.0
+ path-exists: 4.0.0
+
+ find-up@5.0.0:
+ dependencies:
+ locate-path: 6.0.0
+ path-exists: 4.0.0
+
+ find-up@7.0.0:
+ dependencies:
+ locate-path: 7.2.0
+ path-exists: 5.0.0
+ unicorn-magic: 0.1.0
+
+ flat-cache@4.0.1:
+ dependencies:
+ flatted: 3.3.3
+ keyv: 4.5.4
+
+ flatted@3.3.3: {}
+
+ follow-redirects@1.15.9: {}
+
+ foreground-child@3.3.0:
+ dependencies:
+ cross-spawn: 7.0.6
+ signal-exit: 4.1.0
+
+ form-data@3.0.3:
+ dependencies:
+ asynckit: 0.4.0
+ combined-stream: 1.0.8
+ es-set-tostringtag: 2.1.0
+ mime-types: 2.1.35
+
+ form-data@4.0.1:
+ dependencies:
+ asynckit: 0.4.0
+ combined-stream: 1.0.8
+ mime-types: 2.1.35
+
+ format@0.2.2: {}
+
+ forwarded@0.2.0: {}
+
+ fraction.js@4.3.7: {}
+
+ fresh@0.5.2: {}
+
+ fs-extra@10.1.0:
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 6.1.0
+ universalify: 2.0.1
+
+ fs-minipass@2.1.0:
+ dependencies:
+ minipass: 3.3.6
+
+ fs.realpath@1.0.0: {}
+
+ fsevents@2.3.3:
+ optional: true
+
+ function-bind@1.1.2: {}
+
+ generic-names@4.0.0:
+ dependencies:
+ loader-utils: 3.3.1
+
+ gensync@1.0.0-beta.2: {}
+
+ get-caller-file@2.0.5: {}
+
+ get-east-asian-width@1.3.0: {}
+
+ get-intrinsic@1.3.0:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ function-bind: 1.1.2
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-symbols: 1.1.0
+ hasown: 2.0.2
+ math-intrinsics: 1.1.0
+
+ get-package-type@0.1.0: {}
+
+ get-proto@1.0.1:
+ dependencies:
+ dunder-proto: 1.0.1
+ es-object-atoms: 1.1.1
+
+ get-stream@5.2.0:
+ dependencies:
+ pump: 3.0.2
+
+ get-stream@6.0.1: {}
+
+ get-stream@8.0.1: {}
+
+ get-tsconfig@4.10.1:
+ dependencies:
+ resolve-pkg-maps: 1.0.0
+
+ giget@1.2.5:
+ dependencies:
+ citty: 0.1.6
+ consola: 3.4.2
+ defu: 6.1.4
+ node-fetch-native: 1.6.6
+ nypm: 0.5.4
+ pathe: 2.0.3
+ tar: 6.2.1
+
+ git-raw-commits@4.0.0:
+ dependencies:
+ dargs: 8.1.0
+ meow: 12.1.1
+ split2: 4.2.0
+
+ github-slugger@2.0.0: {}
+
+ glob-parent@5.1.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob-parent@6.0.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob@11.0.0:
+ dependencies:
+ foreground-child: 3.3.0
+ jackspeak: 4.0.2
+ minimatch: 10.0.1
+ minipass: 7.1.2
+ package-json-from-dist: 1.0.1
+ path-scurry: 2.0.0
+
+ glob@7.2.3:
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 3.1.2
+ once: 1.4.0
+ path-is-absolute: 1.0.1
+
+ global-directory@4.0.1:
+ dependencies:
+ ini: 4.1.1
+
+ global@4.4.0:
+ dependencies:
+ min-document: 2.19.0
+ process: 0.11.10
+
+ globals@11.12.0: {}
+
+ globals@14.0.0: {}
+
+ globals@15.15.0: {}
+
+ globals@16.2.0: {}
+
+ gopd@1.2.0: {}
+
+ got@11.8.6:
+ dependencies:
+ '@sindresorhus/is': 4.6.0
+ '@szmarczak/http-timer': 4.0.6
+ '@types/cacheable-request': 6.0.3
+ '@types/responselike': 1.0.3
+ cacheable-lookup: 5.0.4
+ cacheable-request: 7.0.4
+ decompress-response: 6.0.0
+ http2-wrapper: 1.0.3
+ lowercase-keys: 2.0.0
+ p-cancelable: 2.1.1
+ responselike: 2.0.1
+
+ graceful-fs@4.2.11: {}
+
+ graphemer@1.4.0: {}
+
+ graphlib@2.1.8:
+ dependencies:
+ lodash: 4.17.21
+
+ gzip-size@6.0.0:
+ dependencies:
+ duplexer: 0.1.2
+
+ handlebars@4.7.8:
+ dependencies:
+ minimist: 1.2.8
+ neo-async: 2.6.2
+ source-map: 0.6.1
+ wordwrap: 1.0.0
+ optionalDependencies:
+ uglify-js: 3.19.3
+
+ has-flag@4.0.0: {}
+
+ has-property-descriptors@1.0.2:
+ dependencies:
+ es-define-property: 1.0.1
+
+ has-symbols@1.1.0: {}
+
+ has-tostringtag@1.0.2:
+ dependencies:
+ has-symbols: 1.1.0
+
+ hash-sum@2.0.0: {}
+
+ hasown@2.0.2:
+ dependencies:
+ function-bind: 1.1.2
+
+ he@1.2.0: {}
+
+ html-encoding-sniffer@2.0.1:
+ dependencies:
+ whatwg-encoding: 1.0.5
+
+ html-escaper@2.0.2: {}
+
+ html-tags@3.3.1: {}
+
+ htmlparser2@8.0.2:
+ dependencies:
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ domutils: 3.1.0
+ entities: 4.5.0
+
+ http-cache-semantics@4.1.1: {}
+
+ http-errors@2.0.0:
+ dependencies:
+ depd: 2.0.0
+ inherits: 2.0.4
+ setprototypeof: 1.2.0
+ statuses: 2.0.1
+ toidentifier: 1.0.1
+
+ http-proxy-agent@4.0.1:
+ dependencies:
+ '@tootallnate/once': 1.1.2
+ agent-base: 6.0.2
+ debug: 4.4.1
+ transitivePeerDependencies:
+ - supports-color
+
+ http2-client@1.3.5: {}
+
+ http2-wrapper@1.0.3:
+ dependencies:
+ quick-lru: 5.1.1
+ resolve-alpn: 1.2.1
+
+ https-proxy-agent@5.0.1:
+ dependencies:
+ agent-base: 6.0.2
+ debug: 4.4.1
+ transitivePeerDependencies:
+ - supports-color
+
+ human-signals@2.1.0: {}
+
+ human-signals@5.0.0: {}
+
+ husky@9.1.7: {}
+
+ iconv-lite@0.4.24:
+ dependencies:
+ safer-buffer: 2.1.2
+
+ icss-replace-symbols@1.1.0: {}
+
+ icss-utils@5.1.0(postcss@8.5.3):
+ dependencies:
+ postcss: 8.5.3
+
+ ieee754@1.2.1: {}
+
+ ignore@5.3.2: {}
+
+ ignore@7.0.5: {}
+
+ immutable@4.3.7: {}
+
+ import-fresh@3.3.0:
+ dependencies:
+ parent-module: 1.0.1
+ resolve-from: 4.0.0
+
+ import-local@3.2.0:
+ dependencies:
+ pkg-dir: 4.2.0
+ resolve-cwd: 3.0.0
+
+ import-meta-resolve@4.1.0: {}
+
+ importx@0.4.4:
+ dependencies:
+ bundle-require: 5.1.0(esbuild@0.20.2)
+ debug: 4.4.1
+ esbuild: 0.20.2
+ jiti: 2.0.0-beta.3
+ jiti-v1: jiti@1.21.6
+ pathe: 1.1.2
+ tsx: 4.19.4
+ transitivePeerDependencies:
+ - supports-color
+
+ importx@0.5.2:
+ dependencies:
+ bundle-require: 5.1.0(esbuild@0.25.5)
+ debug: 4.4.1
+ esbuild: 0.25.5
+ jiti: 2.4.2
+ pathe: 2.0.3
+ tsx: 4.19.4
+ transitivePeerDependencies:
+ - supports-color
+
+ imurmurhash@0.1.4: {}
+
+ indent-string@5.0.0: {}
+
+ inflight@1.0.6:
+ dependencies:
+ once: 1.4.0
+ wrappy: 1.0.2
+
+ inherits@2.0.4: {}
+
+ ini@4.1.1: {}
+
+ invert-kv@3.0.1: {}
+
+ ipaddr.js@1.9.1: {}
+
+ is-arrayish@0.2.1: {}
+
+ is-binary-path@2.1.0:
+ dependencies:
+ binary-extensions: 2.3.0
+
+ is-builtin-module@5.0.0:
+ dependencies:
+ builtin-modules: 5.0.0
+
+ is-core-module@2.16.1:
+ dependencies:
+ hasown: 2.0.2
+
+ is-docker@2.2.1: {}
+
+ is-extglob@2.1.1: {}
+
+ is-fullwidth-code-point@3.0.0: {}
+
+ is-fullwidth-code-point@4.0.0: {}
+
+ is-fullwidth-code-point@5.0.0:
+ dependencies:
+ get-east-asian-width: 1.3.0
+
+ is-function@1.0.2: {}
+
+ is-generator-fn@2.1.0: {}
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-interactive@1.0.0: {}
+
+ is-number@7.0.0: {}
+
+ is-obj@2.0.0: {}
+
+ is-potential-custom-element-name@1.0.1: {}
+
+ is-promise@2.2.2: {}
+
+ is-stream@2.0.1: {}
+
+ is-stream@3.0.0: {}
+
+ is-text-path@2.0.0:
+ dependencies:
+ text-extensions: 2.4.0
+
+ is-typedarray@1.0.0: {}
+
+ is-unicode-supported@0.1.0: {}
+
+ is-wsl@2.2.0:
+ dependencies:
+ is-docker: 2.2.1
+
+ isbinaryfile@5.0.4: {}
+
+ isexe@2.0.0: {}
+
+ istanbul-lib-coverage@3.2.2: {}
+
+ istanbul-lib-instrument@5.2.1:
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/parser': 7.27.5
+ '@istanbuljs/schema': 0.1.3
+ istanbul-lib-coverage: 3.2.2
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ istanbul-lib-report@3.0.1:
+ dependencies:
+ istanbul-lib-coverage: 3.2.2
+ make-dir: 4.0.0
+ supports-color: 7.2.0
+
+ istanbul-lib-source-maps@4.0.1:
+ dependencies:
+ debug: 4.4.1
+ istanbul-lib-coverage: 3.2.2
+ source-map: 0.6.1
+ transitivePeerDependencies:
+ - supports-color
+
+ istanbul-reports@3.1.7:
+ dependencies:
+ html-escaper: 2.0.2
+ istanbul-lib-report: 3.0.1
+
+ jackspeak@4.0.2:
+ dependencies:
+ '@isaacs/cliui': 8.0.2
+
+ javascript-natural-sort@0.7.1: {}
+
+ jest-changed-files@27.5.1:
+ dependencies:
+ '@jest/types': 27.5.1
+ execa: 5.1.1
+ throat: 6.0.2
+
+ jest-circus@27.5.1:
+ dependencies:
+ '@jest/environment': 27.5.1
+ '@jest/test-result': 27.5.1
+ '@jest/types': 27.5.1
+ '@types/node': 20.19.0
+ chalk: 4.1.2
+ co: 4.6.0
+ dedent: 0.7.0
+ expect: 27.5.1
+ is-generator-fn: 2.1.0
+ jest-each: 27.5.1
+ jest-matcher-utils: 27.5.1
+ jest-message-util: 27.5.1
+ jest-runtime: 27.5.1
+ jest-snapshot: 27.5.1
+ jest-util: 27.5.1
+ pretty-format: 27.5.1
+ slash: 3.0.0
+ stack-utils: 2.0.6
+ throat: 6.0.2
+ transitivePeerDependencies:
+ - supports-color
+
+ jest-cli@27.5.1:
+ dependencies:
+ '@jest/core': 27.5.1
+ '@jest/test-result': 27.5.1
+ '@jest/types': 27.5.1
+ chalk: 4.1.2
+ exit: 0.1.2
+ graceful-fs: 4.2.11
+ import-local: 3.2.0
+ jest-config: 27.5.1
+ jest-util: 27.5.1
+ jest-validate: 27.5.1
+ prompts: 2.4.2
+ yargs: 16.2.0
+ transitivePeerDependencies:
+ - bufferutil
+ - canvas
+ - supports-color
+ - ts-node
+ - utf-8-validate
+
+ jest-config@27.5.1:
+ dependencies:
+ '@babel/core': 7.26.0
+ '@jest/test-sequencer': 27.5.1
+ '@jest/types': 27.5.1
+ babel-jest: 27.5.1(@babel/core@7.26.0)
+ chalk: 4.1.2
+ ci-info: 3.9.0
+ deepmerge: 4.3.1
+ glob: 7.2.3
+ graceful-fs: 4.2.11
+ jest-circus: 27.5.1
+ jest-environment-jsdom: 27.5.1
+ jest-environment-node: 27.5.1
+ jest-get-type: 27.5.1
+ jest-jasmine2: 27.5.1
+ jest-regex-util: 27.5.1
+ jest-resolve: 27.5.1
+ jest-runner: 27.5.1
+ jest-util: 27.5.1
+ jest-validate: 27.5.1
+ micromatch: 4.0.8
+ parse-json: 5.2.0
+ pretty-format: 27.5.1
+ slash: 3.0.0
+ strip-json-comments: 3.1.1
+ transitivePeerDependencies:
+ - bufferutil
+ - canvas
+ - supports-color
+ - utf-8-validate
+
+ jest-diff@27.5.1:
+ dependencies:
+ chalk: 4.1.2
+ diff-sequences: 27.5.1
+ jest-get-type: 27.5.1
+ pretty-format: 27.5.1
+
+ jest-docblock@27.5.1:
+ dependencies:
+ detect-newline: 3.1.0
+
+ jest-each@27.5.1:
+ dependencies:
+ '@jest/types': 27.5.1
+ chalk: 4.1.2
+ jest-get-type: 27.5.1
+ jest-util: 27.5.1
+ pretty-format: 27.5.1
+
+ jest-environment-jsdom@27.5.1:
+ dependencies:
+ '@jest/environment': 27.5.1
+ '@jest/fake-timers': 27.5.1
+ '@jest/types': 27.5.1
+ '@types/node': 20.19.0
+ jest-mock: 27.5.1
+ jest-util: 27.5.1
+ jsdom: 16.7.0
+ transitivePeerDependencies:
+ - bufferutil
+ - canvas
+ - supports-color
+ - utf-8-validate
+
+ jest-environment-node@27.5.1:
+ dependencies:
+ '@jest/environment': 27.5.1
+ '@jest/fake-timers': 27.5.1
+ '@jest/types': 27.5.1
+ '@types/node': 20.19.0
+ jest-mock: 27.5.1
+ jest-util: 27.5.1
+
+ jest-get-type@27.5.1: {}
+
+ jest-haste-map@27.5.1:
+ dependencies:
+ '@jest/types': 27.5.1
+ '@types/graceful-fs': 4.1.9
+ '@types/node': 20.19.0
+ anymatch: 3.1.3
+ fb-watchman: 2.0.2
+ graceful-fs: 4.2.11
+ jest-regex-util: 27.5.1
+ jest-serializer: 27.5.1
+ jest-util: 27.5.1
+ jest-worker: 27.5.1
+ micromatch: 4.0.8
+ walker: 1.0.8
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ jest-jasmine2@27.5.1:
+ dependencies:
+ '@jest/environment': 27.5.1
+ '@jest/source-map': 27.5.1
+ '@jest/test-result': 27.5.1
+ '@jest/types': 27.5.1
+ '@types/node': 20.19.0
+ chalk: 4.1.2
+ co: 4.6.0
+ expect: 27.5.1
+ is-generator-fn: 2.1.0
+ jest-each: 27.5.1
+ jest-matcher-utils: 27.5.1
+ jest-message-util: 27.5.1
+ jest-runtime: 27.5.1
+ jest-snapshot: 27.5.1
+ jest-util: 27.5.1
+ pretty-format: 27.5.1
+ throat: 6.0.2
+ transitivePeerDependencies:
+ - supports-color
+
+ jest-leak-detector@27.5.1:
+ dependencies:
+ jest-get-type: 27.5.1
+ pretty-format: 27.5.1
+
+ jest-matcher-utils@27.5.1:
+ dependencies:
+ chalk: 4.1.2
+ jest-diff: 27.5.1
+ jest-get-type: 27.5.1
+ pretty-format: 27.5.1
+
+ jest-message-util@27.5.1:
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@jest/types': 27.5.1
+ '@types/stack-utils': 2.0.3
+ chalk: 4.1.2
+ graceful-fs: 4.2.11
+ micromatch: 4.0.8
+ pretty-format: 27.5.1
+ slash: 3.0.0
+ stack-utils: 2.0.6
+
+ jest-mock@27.5.1:
+ dependencies:
+ '@jest/types': 27.5.1
+ '@types/node': 20.19.0
+
+ jest-pnp-resolver@1.2.3(jest-resolve@27.5.1):
+ optionalDependencies:
+ jest-resolve: 27.5.1
+
+ jest-regex-util@27.5.1: {}
+
+ jest-resolve-dependencies@27.5.1:
+ dependencies:
+ '@jest/types': 27.5.1
+ jest-regex-util: 27.5.1
+ jest-snapshot: 27.5.1
+ transitivePeerDependencies:
+ - supports-color
+
+ jest-resolve@27.5.1:
+ dependencies:
+ '@jest/types': 27.5.1
+ chalk: 4.1.2
+ graceful-fs: 4.2.11
+ jest-haste-map: 27.5.1
+ jest-pnp-resolver: 1.2.3(jest-resolve@27.5.1)
+ jest-util: 27.5.1
+ jest-validate: 27.5.1
+ resolve: 1.22.10
+ resolve.exports: 1.1.1
+ slash: 3.0.0
+
+ jest-runner@27.5.1:
+ dependencies:
+ '@jest/console': 27.5.1
+ '@jest/environment': 27.5.1
+ '@jest/test-result': 27.5.1
+ '@jest/transform': 27.5.1
+ '@jest/types': 27.5.1
+ '@types/node': 20.19.0
+ chalk: 4.1.2
+ emittery: 0.8.1
+ graceful-fs: 4.2.11
+ jest-docblock: 27.5.1
+ jest-environment-jsdom: 27.5.1
+ jest-environment-node: 27.5.1
+ jest-haste-map: 27.5.1
+ jest-leak-detector: 27.5.1
+ jest-message-util: 27.5.1
+ jest-resolve: 27.5.1
+ jest-runtime: 27.5.1
+ jest-util: 27.5.1
+ jest-worker: 27.5.1
+ source-map-support: 0.5.21
+ throat: 6.0.2
+ transitivePeerDependencies:
+ - bufferutil
+ - canvas
+ - supports-color
+ - utf-8-validate
+
+ jest-runtime@27.5.1:
+ dependencies:
+ '@jest/environment': 27.5.1
+ '@jest/fake-timers': 27.5.1
+ '@jest/globals': 27.5.1
+ '@jest/source-map': 27.5.1
+ '@jest/test-result': 27.5.1
+ '@jest/transform': 27.5.1
+ '@jest/types': 27.5.1
+ chalk: 4.1.2
+ cjs-module-lexer: 1.4.3
+ collect-v8-coverage: 1.0.2
+ execa: 5.1.1
+ glob: 7.2.3
+ graceful-fs: 4.2.11
+ jest-haste-map: 27.5.1
+ jest-message-util: 27.5.1
+ jest-mock: 27.5.1
+ jest-regex-util: 27.5.1
+ jest-resolve: 27.5.1
+ jest-snapshot: 27.5.1
+ jest-util: 27.5.1
+ slash: 3.0.0
+ strip-bom: 4.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ jest-serializer@27.5.1:
+ dependencies:
+ '@types/node': 20.19.0
+ graceful-fs: 4.2.11
+
+ jest-snapshot@27.5.1:
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/generator': 7.27.5
+ '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.26.0)
+ '@babel/traverse': 7.27.4
+ '@babel/types': 7.27.6
+ '@jest/transform': 27.5.1
+ '@jest/types': 27.5.1
+ '@types/babel__traverse': 7.20.7
+ '@types/prettier': 2.7.3
+ babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.0)
+ chalk: 4.1.2
+ expect: 27.5.1
+ graceful-fs: 4.2.11
+ jest-diff: 27.5.1
+ jest-get-type: 27.5.1
+ jest-haste-map: 27.5.1
+ jest-matcher-utils: 27.5.1
+ jest-message-util: 27.5.1
+ jest-util: 27.5.1
+ natural-compare: 1.4.0
+ pretty-format: 27.5.1
+ semver: 7.7.2
+ transitivePeerDependencies:
+ - supports-color
+
+ jest-util@27.5.1:
+ dependencies:
+ '@jest/types': 27.5.1
+ '@types/node': 20.19.0
+ chalk: 4.1.2
+ ci-info: 3.9.0
+ graceful-fs: 4.2.11
+ picomatch: 2.3.1
+
+ jest-validate@27.5.1:
+ dependencies:
+ '@jest/types': 27.5.1
+ camelcase: 6.3.0
+ chalk: 4.1.2
+ jest-get-type: 27.5.1
+ leven: 3.1.0
+ pretty-format: 27.5.1
+
+ jest-watcher@27.5.1:
+ dependencies:
+ '@jest/test-result': 27.5.1
+ '@jest/types': 27.5.1
+ '@types/node': 20.19.0
+ ansi-escapes: 4.3.2
+ chalk: 4.1.2
+ jest-util: 27.5.1
+ string-length: 4.0.2
+
+ jest-worker@27.5.1:
+ dependencies:
+ '@types/node': 20.19.0
+ merge-stream: 2.0.0
+ supports-color: 8.1.1
+
+ jest@27.0.4:
+ dependencies:
+ '@jest/core': 27.5.1
+ import-local: 3.2.0
+ jest-cli: 27.5.1
+ transitivePeerDependencies:
+ - bufferutil
+ - canvas
+ - supports-color
+ - ts-node
+ - utf-8-validate
+
+ jimp@0.10.3:
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@jimp/custom': 0.10.3
+ '@jimp/plugins': 0.10.3(@jimp/custom@0.10.3)
+ '@jimp/types': 0.10.3(@jimp/custom@0.10.3)
+ core-js: 3.39.0
+ regenerator-runtime: 0.13.11
+ transitivePeerDependencies:
+ - debug
+
+ jiti@1.21.6: {}
+
+ jiti@2.0.0-beta.3: {}
+
+ jiti@2.4.2: {}
+
+ jpeg-js@0.3.7: {}
+
+ js-cookie@3.0.5: {}
+
+ js-tokens@4.0.0: {}
+
+ js-tokens@9.0.1: {}
+
+ js-yaml@3.14.1:
+ dependencies:
+ argparse: 1.0.10
+ esprima: 4.0.1
+
+ js-yaml@4.1.0:
+ dependencies:
+ argparse: 2.0.1
+
+ jsbn@1.1.0: {}
+
+ jsdoc-type-pratt-parser@4.1.0: {}
+
+ jsdom@16.7.0:
+ dependencies:
+ abab: 2.0.6
+ acorn: 8.15.0
+ acorn-globals: 6.0.0
+ cssom: 0.4.4
+ cssstyle: 2.3.0
+ data-urls: 2.0.0
+ decimal.js: 10.5.0
+ domexception: 2.0.1
+ escodegen: 2.1.0
+ form-data: 3.0.3
+ html-encoding-sniffer: 2.0.1
+ http-proxy-agent: 4.0.1
+ https-proxy-agent: 5.0.1
+ is-potential-custom-element-name: 1.0.1
+ nwsapi: 2.2.20
+ parse5: 6.0.1
+ saxes: 5.0.1
+ symbol-tree: 3.2.4
+ tough-cookie: 4.1.4
+ w3c-hr-time: 1.0.2
+ w3c-xmlserializer: 2.0.0
+ webidl-conversions: 6.1.0
+ whatwg-encoding: 1.0.5
+ whatwg-mimetype: 2.3.0
+ whatwg-url: 8.7.0
+ ws: 7.5.10
+ xml-name-validator: 3.0.0
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ jsesc@3.0.2: {}
+
+ jsesc@3.1.0: {}
+
+ json-buffer@3.0.1: {}
+
+ json-parse-even-better-errors@2.3.1: {}
+
+ json-schema-traverse@0.4.1: {}
+
+ json-schema-traverse@1.0.0: {}
+
+ json-stable-stringify-without-jsonify@1.0.1: {}
+
+ json5@2.2.3: {}
+
+ jsonc-eslint-parser@2.4.0:
+ dependencies:
+ acorn: 8.15.0
+ eslint-visitor-keys: 3.4.3
+ espree: 9.6.1
+ semver: 7.7.2
+
+ jsonc-parser@3.3.1: {}
+
+ jsonfile@6.1.0:
+ dependencies:
+ universalify: 2.0.1
+ optionalDependencies:
+ graceful-fs: 4.2.11
+
+ jsonparse@1.3.1: {}
+
+ keyv@4.5.4:
+ dependencies:
+ json-buffer: 3.0.1
+
+ kleur@3.0.3: {}
+
+ kolorist@1.8.0: {}
+
+ lcid@3.1.1:
+ dependencies:
+ invert-kv: 3.0.1
+
+ leven@3.1.0: {}
+
+ levn@0.4.1:
+ dependencies:
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+
+ licia@1.46.0: {}
+
+ lilconfig@2.1.0: {}
+
+ lilconfig@3.1.3: {}
+
+ lines-and-columns@1.2.4: {}
+
+ lines-and-columns@2.0.4: {}
+
+ lint-staged@15.2.10:
+ dependencies:
+ chalk: 5.3.0
+ commander: 12.1.0
+ debug: 4.3.7
+ execa: 8.0.1
+ lilconfig: 3.1.3
+ listr2: 8.2.5
+ micromatch: 4.0.8
+ pidtree: 0.6.0
+ string-argv: 0.3.2
+ yaml: 2.5.1
+ transitivePeerDependencies:
+ - supports-color
+
+ listr2@8.2.5:
+ dependencies:
+ cli-truncate: 4.0.0
+ colorette: 2.0.20
+ eventemitter3: 5.0.1
+ log-update: 6.1.0
+ rfdc: 1.4.1
+ wrap-ansi: 9.0.0
+
+ load-bmfont@1.4.2:
+ dependencies:
+ buffer-equal: 0.0.1
+ mime: 1.6.0
+ parse-bmfont-ascii: 1.0.6
+ parse-bmfont-binary: 1.0.6
+ parse-bmfont-xml: 1.1.6
+ phin: 3.7.1
+ xhr: 2.6.0
+ xtend: 4.0.2
+ transitivePeerDependencies:
+ - debug
+
+ load-tsconfig@0.2.5: {}
+
+ loader-utils@3.3.1: {}
+
+ local-pkg@0.4.3: {}
+
+ local-pkg@0.5.1:
+ dependencies:
+ mlly: 1.7.3
+ pkg-types: 1.2.1
+
+ local-pkg@1.1.1:
+ dependencies:
+ mlly: 1.7.4
+ pkg-types: 2.1.0
+ quansync: 0.2.10
+
+ localstorage-polyfill@1.0.1: {}
+
+ locate-path@5.0.0:
+ dependencies:
+ p-locate: 4.1.0
+
+ locate-path@6.0.0:
+ dependencies:
+ p-locate: 5.0.0
+
+ locate-path@7.2.0:
+ dependencies:
+ p-locate: 6.0.0
+
+ lodash.camelcase@4.3.0: {}
+
+ lodash.debounce@4.0.8: {}
+
+ lodash.groupby@4.6.0: {}
+
+ lodash.isplainobject@4.0.6: {}
+
+ lodash.kebabcase@4.1.1: {}
+
+ lodash.merge@4.6.2: {}
+
+ lodash.mergewith@4.6.2: {}
+
+ lodash.snakecase@4.1.1: {}
+
+ lodash.startcase@4.4.0: {}
+
+ lodash.uniq@4.5.0: {}
+
+ lodash.upperfirst@4.3.1: {}
+
+ lodash@4.17.21: {}
+
+ log-symbols@4.1.0:
+ dependencies:
+ chalk: 4.1.2
+ is-unicode-supported: 0.1.0
+
+ log-update@6.1.0:
+ dependencies:
+ ansi-escapes: 7.0.0
+ cli-cursor: 5.0.0
+ slice-ansi: 7.1.0
+ strip-ansi: 7.1.0
+ wrap-ansi: 9.0.0
+
+ longest-streak@3.1.0: {}
+
+ lowercase-keys@2.0.0: {}
+
+ lru-cache@11.0.2: {}
+
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
+ lru-queue@0.1.0:
+ dependencies:
+ es5-ext: 0.10.64
+
+ magic-string@0.30.14:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.0
+
+ magic-string@0.30.17:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.0
+
+ make-dir@3.1.0:
+ dependencies:
+ semver: 6.3.1
+
+ make-dir@4.0.0:
+ dependencies:
+ semver: 7.7.2
+
+ make-synchronized@0.2.9: {}
+
+ makeerror@1.0.12:
+ dependencies:
+ tmpl: 1.0.5
+
+ markdown-table@3.0.4: {}
+
+ math-intrinsics@1.1.0: {}
+
+ md5-file@5.0.0: {}
+
+ mdast-util-find-and-replace@3.0.2:
+ dependencies:
+ '@types/mdast': 4.0.4
+ escape-string-regexp: 5.0.0
+ unist-util-is: 6.0.0
+ unist-util-visit-parents: 6.0.1
+
+ mdast-util-from-markdown@2.0.2:
+ dependencies:
+ '@types/mdast': 4.0.4
+ '@types/unist': 3.0.3
+ decode-named-character-reference: 1.2.0
+ devlop: 1.1.0
+ mdast-util-to-string: 4.0.0
+ micromark: 4.0.2
+ micromark-util-decode-numeric-character-reference: 2.0.2
+ micromark-util-decode-string: 2.0.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+ unist-util-stringify-position: 4.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-frontmatter@2.0.1:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ escape-string-regexp: 5.0.0
+ mdast-util-from-markdown: 2.0.2
+ mdast-util-to-markdown: 2.1.2
+ micromark-extension-frontmatter: 2.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-autolink-literal@2.0.1:
+ dependencies:
+ '@types/mdast': 4.0.4
+ ccount: 2.0.1
+ devlop: 1.1.0
+ mdast-util-find-and-replace: 3.0.2
+ micromark-util-character: 2.1.1
+
+ mdast-util-gfm-footnote@2.1.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.0.2
+ mdast-util-to-markdown: 2.1.2
+ micromark-util-normalize-identifier: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-strikethrough@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-from-markdown: 2.0.2
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-table@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ markdown-table: 3.0.4
+ mdast-util-from-markdown: 2.0.2
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-task-list-item@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.0.2
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm@3.1.0:
+ dependencies:
+ mdast-util-from-markdown: 2.0.2
+ mdast-util-gfm-autolink-literal: 2.0.1
+ mdast-util-gfm-footnote: 2.1.0
+ mdast-util-gfm-strikethrough: 2.0.0
+ mdast-util-gfm-table: 2.0.0
+ mdast-util-gfm-task-list-item: 2.0.0
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-phrasing@4.1.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ unist-util-is: 6.0.0
+
+ mdast-util-to-markdown@2.1.2:
+ dependencies:
+ '@types/mdast': 4.0.4
+ '@types/unist': 3.0.3
+ longest-streak: 3.1.0
+ mdast-util-phrasing: 4.1.0
+ mdast-util-to-string: 4.0.0
+ micromark-util-classify-character: 2.0.1
+ micromark-util-decode-string: 2.0.1
+ unist-util-visit: 5.0.0
+ zwitch: 2.0.4
+
+ mdast-util-to-string@4.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+
+ mdn-data@2.12.2: {}
+
+ media-typer@0.3.0: {}
+
+ memoizee@0.4.17:
+ dependencies:
+ d: 1.0.2
+ es5-ext: 0.10.64
+ es6-weak-map: 2.0.3
+ event-emitter: 0.3.5
+ is-promise: 2.2.2
+ lru-queue: 0.1.0
+ next-tick: 1.1.0
+ timers-ext: 0.1.8
+
+ meow@12.1.1: {}
+
+ meow@13.2.0: {}
+
+ merge-descriptors@1.0.3: {}
+
+ merge-stream@2.0.0: {}
+
+ merge2@1.4.1: {}
+
+ merge@2.1.1: {}
+
+ methods@1.1.2: {}
+
+ micromark-core-commonmark@2.0.3:
+ dependencies:
+ decode-named-character-reference: 1.2.0
+ devlop: 1.1.0
+ micromark-factory-destination: 2.0.1
+ micromark-factory-label: 2.0.1
+ micromark-factory-space: 2.0.1
+ micromark-factory-title: 2.0.1
+ micromark-factory-whitespace: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-chunked: 2.0.1
+ micromark-util-classify-character: 2.0.1
+ micromark-util-html-tag-name: 2.0.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-resolve-all: 2.0.1
+ micromark-util-subtokenize: 2.1.0
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-frontmatter@2.0.0:
+ dependencies:
+ fault: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-autolink-literal@2.1.0:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-footnote@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-core-commonmark: 2.0.3
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-strikethrough@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-chunked: 2.0.1
+ micromark-util-classify-character: 2.0.1
+ micromark-util-resolve-all: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-table@2.1.1:
+ dependencies:
+ devlop: 1.1.0
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-tagfilter@2.0.0:
+ dependencies:
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-task-list-item@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm@3.0.0:
+ dependencies:
+ micromark-extension-gfm-autolink-literal: 2.1.0
+ micromark-extension-gfm-footnote: 2.1.0
+ micromark-extension-gfm-strikethrough: 2.1.0
+ micromark-extension-gfm-table: 2.1.1
+ micromark-extension-gfm-tagfilter: 2.0.0
+ micromark-extension-gfm-task-list-item: 2.1.0
+ micromark-util-combine-extensions: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-destination@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-label@2.0.1:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-space@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-title@2.0.1:
+ dependencies:
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-whitespace@2.0.1:
+ dependencies:
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-character@2.1.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-chunked@2.0.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-classify-character@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-combine-extensions@2.0.1:
+ dependencies:
+ micromark-util-chunked: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-decode-numeric-character-reference@2.0.2:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-decode-string@2.0.1:
+ dependencies:
+ decode-named-character-reference: 1.2.0
+ micromark-util-character: 2.1.1
+ micromark-util-decode-numeric-character-reference: 2.0.2
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-encode@2.0.1: {}
+
+ micromark-util-html-tag-name@2.0.1: {}
+
+ micromark-util-normalize-identifier@2.0.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-resolve-all@2.0.1:
+ dependencies:
+ micromark-util-types: 2.0.2
+
+ micromark-util-sanitize-uri@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-encode: 2.0.1
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-subtokenize@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-chunked: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-symbol@2.0.1: {}
+
+ micromark-util-types@2.0.2: {}
+
+ micromark@4.0.2:
+ dependencies:
+ '@types/debug': 4.1.12
+ debug: 4.4.1
+ decode-named-character-reference: 1.2.0
+ devlop: 1.1.0
+ micromark-core-commonmark: 2.0.3
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-chunked: 2.0.1
+ micromark-util-combine-extensions: 2.0.1
+ micromark-util-decode-numeric-character-reference: 2.0.2
+ micromark-util-encode: 2.0.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-resolve-all: 2.0.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-subtokenize: 2.1.0
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+ transitivePeerDependencies:
+ - supports-color
+
+ micromatch@4.0.8:
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.1
+
+ mime-db@1.52.0: {}
+
+ mime-types@2.1.35:
+ dependencies:
+ mime-db: 1.52.0
+
+ mime@1.6.0: {}
+
+ mime@3.0.0: {}
+
+ mimic-fn@2.1.0: {}
+
+ mimic-fn@4.0.0: {}
+
+ mimic-function@5.0.1: {}
+
+ mimic-response@1.0.1: {}
+
+ mimic-response@3.1.0: {}
+
+ min-document@2.19.0:
+ dependencies:
+ dom-walk: 0.1.2
+
+ min-indent@1.0.1: {}
+
+ minimatch@10.0.1:
+ dependencies:
+ brace-expansion: 2.0.1
+
+ minimatch@3.1.2:
+ dependencies:
+ brace-expansion: 1.1.12
+
+ minimatch@8.0.4:
+ dependencies:
+ brace-expansion: 2.0.1
+
+ minimatch@9.0.5:
+ dependencies:
+ brace-expansion: 2.0.1
+
+ minimist@1.2.8: {}
+
+ minipass@3.3.6:
+ dependencies:
+ yallist: 4.0.0
+
+ minipass@5.0.0: {}
+
+ minipass@7.1.2: {}
+
+ minizlib@2.1.2:
+ dependencies:
+ minipass: 3.3.6
+ yallist: 4.0.0
+
+ mkdirp@0.5.6:
+ dependencies:
+ minimist: 1.2.8
+
+ mkdirp@1.0.4: {}
+
+ mlly@1.7.3:
+ dependencies:
+ acorn: 8.14.0
+ pathe: 1.1.2
+ pkg-types: 1.2.1
+ ufo: 1.5.4
+
+ mlly@1.7.4:
+ dependencies:
+ acorn: 8.14.1
+ pathe: 2.0.3
+ pkg-types: 1.3.1
+ ufo: 1.5.4
+
+ mockjs@1.1.0:
+ dependencies:
+ commander: 12.1.0
+
+ module-alias@2.2.3: {}
+
+ mrmime@2.0.1: {}
+
+ ms@2.0.0: {}
+
+ ms@2.1.3: {}
+
+ muggle-string@0.4.1: {}
+
+ nanoid@3.3.8: {}
+
+ natural-compare@1.4.0: {}
+
+ natural-orderby@5.0.0: {}
+
+ negotiator@0.6.3: {}
+
+ neo-async@2.6.2: {}
+
+ next-tick@1.1.0: {}
+
+ node-fetch-h2@2.3.0:
+ dependencies:
+ http2-client: 1.3.5
+
+ node-fetch-native@1.6.6: {}
+
+ node-fetch@2.7.0:
+ dependencies:
+ whatwg-url: 5.0.0
+
+ node-int64@0.4.0: {}
+
+ node-readfiles@0.2.0:
+ dependencies:
+ es6-promise: 3.3.1
+
+ node-releases@2.0.18: {}
+
+ node-releases@2.0.19: {}
+
+ normalize-path@3.0.0: {}
+
+ normalize-range@0.1.2: {}
+
+ normalize-url@6.1.0: {}
+
+ npm-run-path@4.0.1:
+ dependencies:
+ path-key: 3.1.1
+
+ npm-run-path@5.3.0:
+ dependencies:
+ path-key: 4.0.0
+
+ nth-check@2.1.1:
+ dependencies:
+ boolbase: 1.0.0
+
+ nunjucks@3.2.4(chokidar@3.6.0):
+ dependencies:
+ a-sync-waterfall: 1.0.1
+ asap: 2.0.6
+ commander: 5.1.0
+ optionalDependencies:
+ chokidar: 3.6.0
+
+ nwsapi@2.2.20: {}
+
+ nypm@0.5.4:
+ dependencies:
+ citty: 0.1.6
+ consola: 3.4.2
+ pathe: 2.0.3
+ pkg-types: 1.3.1
+ tinyexec: 0.3.2
+ ufo: 1.6.1
+
+ oas-kit-common@1.0.8:
+ dependencies:
+ fast-safe-stringify: 2.1.1
+
+ oas-linter@3.2.2:
+ dependencies:
+ '@exodus/schemasafe': 1.3.0
+ should: 13.2.3
+ yaml: 1.10.2
+
+ oas-resolver@2.5.6:
+ dependencies:
+ node-fetch-h2: 2.3.0
+ oas-kit-common: 1.0.8
+ reftools: 1.1.9
+ yaml: 1.10.2
+ yargs: 17.7.2
+
+ oas-schema-walker@1.1.5: {}
+
+ oas-validator@5.0.8:
+ dependencies:
+ call-me-maybe: 1.0.2
+ oas-kit-common: 1.0.8
+ oas-linter: 3.2.2
+ oas-resolver: 2.5.6
+ oas-schema-walker: 1.1.5
+ reftools: 1.1.9
+ should: 13.2.3
+ yaml: 1.10.2
+
+ object-hash@3.0.0: {}
+
+ object-inspect@1.13.3: {}
+
+ ofetch@1.4.1:
+ dependencies:
+ destr: 2.0.5
+ node-fetch-native: 1.6.6
+ ufo: 1.6.1
+
+ ohash@1.1.4: {}
+
+ ohash@2.0.11: {}
+
+ omggif@1.0.10: {}
+
+ on-finished@2.4.1:
+ dependencies:
+ ee-first: 1.1.1
+
+ once@1.4.0:
+ dependencies:
+ wrappy: 1.0.2
+
+ onetime@5.1.2:
+ dependencies:
+ mimic-fn: 2.1.0
+
+ onetime@6.0.0:
+ dependencies:
+ mimic-fn: 4.0.0
+
+ onetime@7.0.0:
+ dependencies:
+ mimic-function: 5.0.1
+
+ open@8.4.2:
+ dependencies:
+ define-lazy-prop: 2.0.0
+ is-docker: 2.2.1
+ is-wsl: 2.2.0
+
+ openapi-ts-request@1.1.2(@types/node@20.19.0)(@vue/compiler-sfc@3.5.15)(chokidar@3.6.0)(typescript@5.7.2):
+ dependencies:
+ '@prettier/sync': 0.5.2(prettier@3.5.3)
+ '@trivago/prettier-plugin-sort-imports': 5.2.1(@vue/compiler-sfc@3.5.15)(prettier@3.5.3)
+ axios: 1.7.9
+ bing-translate-api: 4.0.2
+ chalk: 4.1.2
+ commander: 12.1.0
+ cosmiconfig: 9.0.0(typescript@5.7.2)
+ cosmiconfig-typescript-loader: 6.1.0(@types/node@20.19.0)(cosmiconfig@9.0.0(typescript@5.7.2))(typescript@5.7.2)
+ glob: 11.0.0
+ js-yaml: 4.1.0
+ lodash: 4.17.21
+ memoizee: 0.4.17
+ minimatch: 10.0.1
+ mockjs: 1.1.0
+ nunjucks: 3.2.4(chokidar@3.6.0)
+ prettier: 3.5.3
+ reserved-words: 0.1.2
+ rimraf: 6.0.1
+ swagger2openapi: 7.0.8
+ tiny-pinyin: 1.3.2
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - '@types/node'
+ - '@vue/compiler-sfc'
+ - chokidar
+ - debug
+ - encoding
+ - prettier-plugin-svelte
+ - supports-color
+ - svelte
+ - typescript
+
+ openapi-types@12.1.3: {}
+
+ optionator@0.9.4:
+ dependencies:
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.4.1
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ word-wrap: 1.2.5
+
+ ora@5.4.1:
+ dependencies:
+ bl: 4.1.0
+ chalk: 4.1.2
+ cli-cursor: 3.1.0
+ cli-spinners: 2.9.2
+ is-interactive: 1.0.0
+ is-unicode-supported: 0.1.0
+ log-symbols: 4.1.0
+ strip-ansi: 6.0.1
+ wcwidth: 1.0.1
+
+ os-locale-s-fix@1.0.8-fix-1:
+ dependencies:
+ lcid: 3.1.1
+
+ p-cancelable@2.1.1: {}
+
+ p-limit@2.3.0:
+ dependencies:
+ p-try: 2.2.0
+
+ p-limit@3.1.0:
+ dependencies:
+ yocto-queue: 0.1.0
+
+ p-limit@4.0.0:
+ dependencies:
+ yocto-queue: 1.2.1
+
+ p-locate@4.1.0:
+ dependencies:
+ p-limit: 2.3.0
+
+ p-locate@5.0.0:
+ dependencies:
+ p-limit: 3.1.0
+
+ p-locate@6.0.0:
+ dependencies:
+ p-limit: 4.0.0
+
+ p-try@2.2.0: {}
+
+ package-json-from-dist@1.0.1: {}
+
+ package-manager-detector@1.3.0: {}
+
+ pako@1.0.11: {}
+
+ parent-module@1.0.1:
+ dependencies:
+ callsites: 3.1.0
+
+ parse-bmfont-ascii@1.0.6: {}
+
+ parse-bmfont-binary@1.0.6: {}
+
+ parse-bmfont-xml@1.1.6:
+ dependencies:
+ xml-parse-from-string: 1.0.1
+ xml2js: 0.5.0
+
+ parse-css-font@4.0.0:
+ dependencies:
+ css-font-size-keywords: 1.0.0
+ css-font-stretch-keywords: 1.0.1
+ css-font-style-keywords: 1.0.1
+ css-font-weight-keywords: 1.0.0
+ css-list-helpers: 2.0.0
+ css-system-font-keywords: 1.0.0
+ unquote: 1.1.1
+
+ parse-gitignore@2.0.0: {}
+
+ parse-headers@2.0.5: {}
+
+ parse-imports-exports@0.2.4:
+ dependencies:
+ parse-statements: 1.0.11
+
+ parse-json@5.2.0:
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ error-ex: 1.3.2
+ json-parse-even-better-errors: 2.3.1
+ lines-and-columns: 1.2.4
+
+ parse-statements@1.0.11: {}
+
+ parse5@6.0.1: {}
+
+ parseurl@1.3.3: {}
+
+ path-browserify@1.0.1: {}
+
+ path-exists@4.0.0: {}
+
+ path-exists@5.0.0: {}
+
+ path-is-absolute@1.0.1: {}
+
+ path-key@3.1.1: {}
+
+ path-key@4.0.0: {}
+
+ path-parse@1.0.7: {}
+
+ path-scurry@2.0.0:
+ dependencies:
+ lru-cache: 11.0.2
+ minipass: 7.1.2
+
+ path-to-regexp@0.1.10: {}
+
+ pathe@1.1.2: {}
+
+ pathe@2.0.3: {}
+
+ perfect-debounce@1.0.0: {}
+
+ phin@2.9.3: {}
+
+ phin@3.7.1:
+ dependencies:
+ centra: 2.7.0
+ transitivePeerDependencies:
+ - debug
+
+ picocolors@1.1.1: {}
+
+ picomatch@2.3.1: {}
+
+ picomatch@4.0.2: {}
+
+ pidtree@0.6.0: {}
+
+ pify@2.3.0: {}
+
+ pinia-plugin-persistedstate@3.2.1(pinia@2.0.36(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2))):
+ dependencies:
+ pinia: 2.0.36(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2))
+
+ pinia@2.0.36(typescript@5.7.2)(vue@3.5.15(typescript@5.7.2)):
+ dependencies:
+ '@vue/devtools-api': 6.6.4
+ vue: 3.5.15(typescript@5.7.2)
+ vue-demi: 0.14.10(vue@3.5.15(typescript@5.7.2))
+ optionalDependencies:
+ typescript: 5.7.2
+
+ pirates@4.0.7: {}
+
+ pixelmatch@4.0.2:
+ dependencies:
+ pngjs: 3.4.0
+
+ pkg-dir@4.2.0:
+ dependencies:
+ find-up: 4.1.0
+
+ pkg-types@1.2.1:
+ dependencies:
+ confbox: 0.1.8
+ mlly: 1.7.3
+ pathe: 1.1.2
+
+ pkg-types@1.3.1:
+ dependencies:
+ confbox: 0.1.8
+ mlly: 1.7.4
+ pathe: 2.0.3
+
+ pkg-types@2.1.0:
+ dependencies:
+ confbox: 0.2.2
+ exsolve: 1.0.5
+ pathe: 2.0.3
+
+ pluralize@8.0.0: {}
+
+ pngjs@3.4.0: {}
+
+ pnpm-workspace-yaml@0.3.1:
+ dependencies:
+ yaml: 2.8.0
+
+ postcss-html@1.7.0:
+ dependencies:
+ htmlparser2: 8.0.2
+ js-tokens: 9.0.1
+ postcss: 8.5.3
+ postcss-safe-parser: 6.0.0(postcss@8.5.3)
+
+ postcss-import@14.1.0(postcss@8.5.3):
+ dependencies:
+ postcss: 8.5.3
+ postcss-value-parser: 4.2.0
+ read-cache: 1.0.0
+ resolve: 1.22.10
+
+ postcss-load-config@3.1.4(postcss@8.5.3):
+ dependencies:
+ lilconfig: 2.1.0
+ yaml: 1.10.2
+ optionalDependencies:
+ postcss: 8.5.3
+
+ postcss-modules-extract-imports@3.1.0(postcss@8.5.3):
+ dependencies:
+ postcss: 8.5.3
+
+ postcss-modules-local-by-default@4.1.0(postcss@8.5.3):
+ dependencies:
+ icss-utils: 5.1.0(postcss@8.5.3)
+ postcss: 8.5.3
+ postcss-selector-parser: 7.0.0
+ postcss-value-parser: 4.2.0
+
+ postcss-modules-scope@3.2.1(postcss@8.5.3):
+ dependencies:
+ postcss: 8.5.3
+ postcss-selector-parser: 7.0.0
+
+ postcss-modules-values@4.0.0(postcss@8.5.3):
+ dependencies:
+ icss-utils: 5.1.0(postcss@8.5.3)
+ postcss: 8.5.3
+
+ postcss-modules@4.3.1(postcss@8.5.3):
+ dependencies:
+ generic-names: 4.0.0
+ icss-replace-symbols: 1.1.0
+ lodash.camelcase: 4.3.0
+ postcss: 8.5.3
+ postcss-modules-extract-imports: 3.1.0(postcss@8.5.3)
+ postcss-modules-local-by-default: 4.1.0(postcss@8.5.3)
+ postcss-modules-scope: 3.2.1(postcss@8.5.3)
+ postcss-modules-values: 4.0.0(postcss@8.5.3)
+ string-hash: 1.1.3
+
+ postcss-safe-parser@6.0.0(postcss@8.5.3):
+ dependencies:
+ postcss: 8.5.3
+
+ postcss-scss@4.0.9(postcss@8.5.3):
+ dependencies:
+ postcss: 8.5.3
+
+ postcss-selector-parser@6.1.2:
+ dependencies:
+ cssesc: 3.0.0
+ util-deprecate: 1.0.2
+
+ postcss-selector-parser@7.0.0:
+ dependencies:
+ cssesc: 3.0.0
+ util-deprecate: 1.0.2
+
+ postcss-value-parser@4.2.0: {}
+
+ postcss@8.5.3:
+ dependencies:
+ nanoid: 3.3.8
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ prelude-ls@1.2.1: {}
+
+ prettier-linter-helpers@1.0.0:
+ dependencies:
+ fast-diff: 1.3.0
+
+ prettier@3.5.3: {}
+
+ pretty-format@27.5.1:
+ dependencies:
+ ansi-regex: 5.0.1
+ ansi-styles: 5.2.0
+ react-is: 17.0.2
+
+ process@0.11.10: {}
+
+ prompts@2.4.2:
+ dependencies:
+ kleur: 3.0.3
+ sisteransi: 1.0.5
+
+ proxy-addr@2.0.7:
+ dependencies:
+ forwarded: 0.2.0
+ ipaddr.js: 1.9.1
+
+ proxy-from-env@1.1.0: {}
+
+ psl@1.15.0:
+ dependencies:
+ punycode: 2.3.1
+
+ pump@3.0.2:
+ dependencies:
+ end-of-stream: 1.4.4
+ once: 1.4.0
+
+ punycode@2.3.1: {}
+
+ qrcode-reader@1.0.4: {}
+
+ qrcode-terminal@0.12.0: {}
+
+ qs@6.13.0:
+ dependencies:
+ side-channel: 1.0.6
+
+ quansync@0.2.10: {}
+
+ querystringify@2.2.0: {}
+
+ queue-microtask@1.2.3: {}
+
+ quick-lru@5.1.1: {}
+
+ range-parser@1.2.1: {}
+
+ rate-limiter-flexible@5.0.5: {}
+
+ raw-body@2.5.2:
+ dependencies:
+ bytes: 3.1.2
+ http-errors: 2.0.0
+ iconv-lite: 0.4.24
+ unpipe: 1.0.0
+
+ rc9@2.1.2:
+ dependencies:
+ defu: 6.1.4
+ destr: 2.0.5
+
+ react-is@17.0.2: {}
+
+ read-cache@1.0.0:
+ dependencies:
+ pify: 2.3.0
+
+ readable-stream@3.6.2:
+ dependencies:
+ inherits: 2.0.4
+ string_decoder: 1.3.0
+ util-deprecate: 1.0.2
+
+ readdirp@3.6.0:
+ dependencies:
+ picomatch: 2.3.1
+
+ readdirp@4.1.2: {}
+
+ refa@0.12.1:
+ dependencies:
+ '@eslint-community/regexpp': 4.12.1
+
+ reftools@1.1.9: {}
+
+ regenerate-unicode-properties@10.2.0:
+ dependencies:
+ regenerate: 1.4.2
+
+ regenerate@1.4.2: {}
+
+ regenerator-runtime@0.13.11: {}
+
+ regenerator-runtime@0.14.1: {}
+
+ regenerator-transform@0.15.2:
+ dependencies:
+ '@babel/runtime': 7.26.0
+
+ regexp-ast-analysis@0.7.1:
+ dependencies:
+ '@eslint-community/regexpp': 4.12.1
+ refa: 0.12.1
+
+ regexp-tree@0.1.27: {}
+
+ regexpu-core@6.2.0:
+ dependencies:
+ regenerate: 1.4.2
+ regenerate-unicode-properties: 10.2.0
+ regjsgen: 0.8.0
+ regjsparser: 0.12.0
+ unicode-match-property-ecmascript: 2.0.0
+ unicode-match-property-value-ecmascript: 2.2.0
+
+ regjsgen@0.8.0: {}
+
+ regjsparser@0.12.0:
+ dependencies:
+ jsesc: 3.0.2
+
+ remove-accents@0.5.0: {}
+
+ require-directory@2.1.1: {}
+
+ require-from-string@2.0.2: {}
+
+ requires-port@1.0.0: {}
+
+ reserved-words@0.1.2: {}
+
+ resolve-alpn@1.2.1: {}
+
+ resolve-cwd@3.0.0:
+ dependencies:
+ resolve-from: 5.0.0
+
+ resolve-from@4.0.0: {}
+
+ resolve-from@5.0.0: {}
+
+ resolve-pkg-maps@1.0.0: {}
+
+ resolve.exports@1.1.1: {}
+
+ resolve@1.22.10:
+ dependencies:
+ is-core-module: 2.16.1
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+
+ responselike@2.0.1:
+ dependencies:
+ lowercase-keys: 2.0.0
+
+ restore-cursor@3.1.0:
+ dependencies:
+ onetime: 5.1.2
+ signal-exit: 3.0.7
+
+ restore-cursor@5.1.0:
+ dependencies:
+ onetime: 7.0.0
+ signal-exit: 4.1.0
+
+ reusify@1.0.4: {}
+
+ rfdc@1.4.1: {}
+
+ rimraf@3.0.2:
+ dependencies:
+ glob: 7.2.3
+
+ rimraf@6.0.1:
+ dependencies:
+ glob: 11.0.0
+ package-json-from-dist: 1.0.1
+
+ rollup-plugin-visualizer@5.12.0(rollup@4.41.1):
+ dependencies:
+ open: 8.4.2
+ picomatch: 2.3.1
+ source-map: 0.7.4
+ yargs: 17.7.2
+ optionalDependencies:
+ rollup: 4.41.1
+
+ rollup@4.41.1:
+ dependencies:
+ '@types/estree': 1.0.7
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.41.1
+ '@rollup/rollup-android-arm64': 4.41.1
+ '@rollup/rollup-darwin-arm64': 4.41.1
+ '@rollup/rollup-darwin-x64': 4.41.1
+ '@rollup/rollup-freebsd-arm64': 4.41.1
+ '@rollup/rollup-freebsd-x64': 4.41.1
+ '@rollup/rollup-linux-arm-gnueabihf': 4.41.1
+ '@rollup/rollup-linux-arm-musleabihf': 4.41.1
+ '@rollup/rollup-linux-arm64-gnu': 4.41.1
+ '@rollup/rollup-linux-arm64-musl': 4.41.1
+ '@rollup/rollup-linux-loongarch64-gnu': 4.41.1
+ '@rollup/rollup-linux-powerpc64le-gnu': 4.41.1
+ '@rollup/rollup-linux-riscv64-gnu': 4.41.1
+ '@rollup/rollup-linux-riscv64-musl': 4.41.1
+ '@rollup/rollup-linux-s390x-gnu': 4.41.1
+ '@rollup/rollup-linux-x64-gnu': 4.41.1
+ '@rollup/rollup-linux-x64-musl': 4.41.1
+ '@rollup/rollup-win32-arm64-msvc': 4.41.1
+ '@rollup/rollup-win32-ia32-msvc': 4.41.1
+ '@rollup/rollup-win32-x64-msvc': 4.41.1
+ fsevents: 2.3.3
+
+ run-parallel@1.2.0:
+ dependencies:
+ queue-microtask: 1.2.3
+
+ safe-area-insets@1.4.1: {}
+
+ safe-buffer@5.2.1: {}
+
+ safer-buffer@2.1.2: {}
+
+ sass@1.77.8:
+ dependencies:
+ chokidar: 3.6.0
+ immutable: 4.3.7
+ source-map-js: 1.2.1
+
+ sax@1.4.1: {}
+
+ saxes@5.0.1:
+ dependencies:
+ xmlchars: 2.2.0
+
+ scslre@0.3.0:
+ dependencies:
+ '@eslint-community/regexpp': 4.12.1
+ refa: 0.12.1
+ regexp-ast-analysis: 0.7.1
+
+ scule@1.3.0: {}
+
+ semver@6.3.1: {}
+
+ semver@7.7.2: {}
+
+ send@0.19.0:
+ dependencies:
+ debug: 2.6.9
+ depd: 2.0.0
+ destroy: 1.2.0
+ encodeurl: 1.0.2
+ escape-html: 1.0.3
+ etag: 1.8.1
+ fresh: 0.5.2
+ http-errors: 2.0.0
+ mime: 1.6.0
+ ms: 2.1.3
+ on-finished: 2.4.1
+ range-parser: 1.2.1
+ statuses: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ serve-static@1.16.2:
+ dependencies:
+ encodeurl: 2.0.0
+ escape-html: 1.0.3
+ parseurl: 1.3.3
+ send: 0.19.0
+ transitivePeerDependencies:
+ - supports-color
+
+ set-function-length@1.2.2:
+ dependencies:
+ define-data-property: 1.1.4
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+ get-intrinsic: 1.3.0
+ gopd: 1.2.0
+ has-property-descriptors: 1.0.2
+
+ setprototypeof@1.2.0: {}
+
+ shebang-command@2.0.0:
+ dependencies:
+ shebang-regex: 3.0.0
+
+ shebang-regex@3.0.0: {}
+
+ should-equal@2.0.0:
+ dependencies:
+ should-type: 1.4.0
+
+ should-format@3.0.3:
+ dependencies:
+ should-type: 1.4.0
+ should-type-adaptors: 1.1.0
+
+ should-type-adaptors@1.1.0:
+ dependencies:
+ should-type: 1.4.0
+ should-util: 1.0.1
+
+ should-type@1.4.0: {}
+
+ should-util@1.0.1: {}
+
+ should@13.2.3:
+ dependencies:
+ should-equal: 2.0.0
+ should-format: 3.0.3
+ should-type: 1.4.0
+ should-type-adaptors: 1.1.0
+ should-util: 1.0.1
+
+ side-channel@1.0.6:
+ dependencies:
+ call-bind: 1.0.7
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ object-inspect: 1.13.3
+
+ signal-exit@3.0.7: {}
+
+ signal-exit@4.1.0: {}
+
+ sirv@3.0.1:
+ dependencies:
+ '@polka/url': 1.0.0-next.29
+ mrmime: 2.0.1
+ totalist: 3.0.1
+
+ sisteransi@1.0.5: {}
+
+ slash@3.0.0: {}
+
+ slice-ansi@5.0.0:
+ dependencies:
+ ansi-styles: 6.2.1
+ is-fullwidth-code-point: 4.0.0
+
+ slice-ansi@7.1.0:
+ dependencies:
+ ansi-styles: 6.2.1
+ is-fullwidth-code-point: 5.0.0
+
+ sm-crypto@0.3.14:
+ dependencies:
+ jsbn: 1.1.0
+
+ source-map-js@1.2.1: {}
+
+ source-map-support@0.5.21:
+ dependencies:
+ buffer-from: 1.1.2
+ source-map: 0.6.1
+
+ source-map@0.6.1: {}
+
+ source-map@0.7.4: {}
+
+ spdx-exceptions@2.5.0: {}
+
+ spdx-expression-parse@4.0.0:
+ dependencies:
+ spdx-exceptions: 2.5.0
+ spdx-license-ids: 3.0.21
+
+ spdx-license-ids@3.0.21: {}
+
+ split2@4.2.0: {}
+
+ sprintf-js@1.0.3: {}
+
+ stack-utils@2.0.6:
+ dependencies:
+ escape-string-regexp: 2.0.0
+
+ statuses@2.0.1: {}
+
+ std-env@3.8.0: {}
+
+ string-argv@0.3.2: {}
+
+ string-hash@1.1.3: {}
+
+ string-length@4.0.2:
+ dependencies:
+ char-regex: 1.0.2
+ strip-ansi: 6.0.1
+
+ string-width@4.2.3:
+ dependencies:
+ emoji-regex: 8.0.0
+ is-fullwidth-code-point: 3.0.0
+ strip-ansi: 6.0.1
+
+ string-width@5.1.2:
+ dependencies:
+ eastasianwidth: 0.2.0
+ emoji-regex: 9.2.2
+ strip-ansi: 7.1.0
+
+ string-width@7.2.0:
+ dependencies:
+ emoji-regex: 10.4.0
+ get-east-asian-width: 1.3.0
+ strip-ansi: 7.1.0
+
+ string_decoder@1.3.0:
+ dependencies:
+ safe-buffer: 5.2.1
+
+ strip-ansi@6.0.1:
+ dependencies:
+ ansi-regex: 5.0.1
+
+ strip-ansi@7.1.0:
+ dependencies:
+ ansi-regex: 6.1.0
+
+ strip-bom@4.0.0: {}
+
+ strip-final-newline@2.0.0: {}
+
+ strip-final-newline@3.0.0: {}
+
+ strip-indent@4.0.0:
+ dependencies:
+ min-indent: 1.0.1
+
+ strip-json-comments@3.1.1: {}
+
+ strip-literal@2.1.1:
+ dependencies:
+ js-tokens: 9.0.1
+
+ strip-literal@3.0.0:
+ dependencies:
+ js-tokens: 9.0.1
+
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
+ supports-color@8.1.1:
+ dependencies:
+ has-flag: 4.0.0
+
+ supports-hyperlinks@2.3.0:
+ dependencies:
+ has-flag: 4.0.0
+ supports-color: 7.2.0
+
+ supports-preserve-symlinks-flag@1.0.0: {}
+
+ svg-tags@1.0.0: {}
+
+ swagger2openapi@7.0.8:
+ dependencies:
+ call-me-maybe: 1.0.2
+ node-fetch: 2.7.0
+ node-fetch-h2: 2.3.0
+ node-readfiles: 0.2.0
+ oas-kit-common: 1.0.8
+ oas-resolver: 2.5.6
+ oas-schema-walker: 1.1.5
+ oas-validator: 5.0.8
+ reftools: 1.1.9
+ yaml: 1.10.2
+ yargs: 17.7.2
+ transitivePeerDependencies:
+ - encoding
+
+ symbol-tree@3.2.4: {}
+
+ synckit@0.11.8:
+ dependencies:
+ '@pkgr/core': 0.2.7
+
+ synckit@0.9.3:
+ dependencies:
+ '@pkgr/core': 0.1.2
+ tslib: 2.8.1
+
+ systemjs@6.15.1: {}
+
+ tapable@2.2.1: {}
+
+ tar@6.2.1:
+ dependencies:
+ chownr: 2.0.0
+ fs-minipass: 2.1.0
+ minipass: 5.0.0
+ minizlib: 2.1.2
+ mkdirp: 1.0.4
+ yallist: 4.0.0
+
+ terminal-link@2.1.1:
+ dependencies:
+ ansi-escapes: 4.3.2
+ supports-hyperlinks: 2.3.0
+
+ terser@5.36.0:
+ dependencies:
+ '@jridgewell/source-map': 0.3.6
+ acorn: 8.14.0
+ commander: 2.20.3
+ source-map-support: 0.5.21
+
+ test-exclude@6.0.0:
+ dependencies:
+ '@istanbuljs/schema': 0.1.3
+ glob: 7.2.3
+ minimatch: 3.1.2
+
+ text-extensions@2.4.0: {}
+
+ throat@6.0.2: {}
+
+ through@2.3.8: {}
+
+ timers-ext@0.1.8:
+ dependencies:
+ es5-ext: 0.10.64
+ next-tick: 1.1.0
+
+ timm@1.7.1: {}
+
+ tiny-pinyin@1.3.2: {}
+
+ tinycolor2@1.6.0: {}
+
+ tinyexec@0.3.2: {}
+
+ tinyexec@1.0.1: {}
+
+ tinyglobby@0.2.10:
+ dependencies:
+ fdir: 6.4.2(picomatch@4.0.2)
+ picomatch: 4.0.2
+
+ tinyglobby@0.2.14:
+ dependencies:
+ fdir: 6.4.4(picomatch@4.0.2)
+ picomatch: 4.0.2
+
+ tmpl@1.0.5: {}
+
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
+
+ toidentifier@1.0.1: {}
+
+ toml-eslint-parser@0.10.0:
+ dependencies:
+ eslint-visitor-keys: 3.4.3
+
+ totalist@3.0.1: {}
+
+ tough-cookie@4.1.4:
+ dependencies:
+ psl: 1.15.0
+ punycode: 2.3.1
+ universalify: 0.2.0
+ url-parse: 1.5.10
+
+ tr46@0.0.3: {}
+
+ tr46@2.1.0:
+ dependencies:
+ punycode: 2.3.1
+
+ ts-api-utils@2.1.0(typescript@5.7.2):
+ dependencies:
+ typescript: 5.7.2
+
+ ts-declaration-location@1.0.7(typescript@5.7.2):
+ dependencies:
+ picomatch: 4.0.2
+ typescript: 5.7.2
+
+ tslib@2.8.1: {}
+
+ tsx@4.19.4:
+ dependencies:
+ esbuild: 0.25.5
+ get-tsconfig: 4.10.1
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ type-check@0.4.0:
+ dependencies:
+ prelude-ls: 1.2.1
+
+ type-detect@4.0.8: {}
+
+ type-fest@0.21.3: {}
+
+ type-is@1.6.18:
+ dependencies:
+ media-typer: 0.3.0
+ mime-types: 2.1.35
+
+ type@2.7.3: {}
+
+ typedarray-to-buffer@3.1.5:
+ dependencies:
+ is-typedarray: 1.0.0
+
+ typescript@5.7.2: {}
+
+ ufo@1.5.4: {}
+
+ ufo@1.6.1: {}
+
+ uglify-js@3.19.3:
+ optional: true
+
+ unconfig@0.5.5:
+ dependencies:
+ '@antfu/utils': 0.7.10
+ defu: 6.1.4
+ importx: 0.4.4
+ transitivePeerDependencies:
+ - supports-color
+
+ unconfig@0.6.1:
+ dependencies:
+ '@antfu/utils': 8.1.1
+ defu: 6.1.4
+ importx: 0.5.2
+ transitivePeerDependencies:
+ - supports-color
+
+ unconfig@7.3.2:
+ dependencies:
+ '@quansync/fs': 0.1.3
+ defu: 6.1.4
+ jiti: 2.4.2
+ quansync: 0.2.10
+
+ undici-types@6.21.0: {}
+
+ unicode-canonical-property-names-ecmascript@2.0.1: {}
+
+ unicode-match-property-ecmascript@2.0.0:
+ dependencies:
+ unicode-canonical-property-names-ecmascript: 2.0.1
+ unicode-property-aliases-ecmascript: 2.1.0
+
+ unicode-match-property-value-ecmascript@2.2.0: {}
+
+ unicode-property-aliases-ecmascript@2.1.0: {}
+
+ unicorn-magic@0.1.0: {}
+
+ unimport@3.14.3(rollup@4.41.1):
+ dependencies:
+ '@rollup/pluginutils': 5.1.4(rollup@4.41.1)
+ acorn: 8.14.0
+ escape-string-regexp: 5.0.0
+ estree-walker: 3.0.3
+ local-pkg: 0.5.1
+ magic-string: 0.30.17
+ mlly: 1.7.3
+ pathe: 1.1.2
+ picomatch: 4.0.2
+ pkg-types: 1.2.1
+ scule: 1.3.0
+ strip-literal: 2.1.1
+ tinyglobby: 0.2.10
+ unplugin: 1.16.0
+ transitivePeerDependencies:
+ - rollup
+
+ unimport@4.1.1:
+ dependencies:
+ acorn: 8.14.1
+ escape-string-regexp: 5.0.0
+ estree-walker: 3.0.3
+ fast-glob: 3.3.3
+ local-pkg: 1.1.1
+ magic-string: 0.30.17
+ mlly: 1.7.4
+ pathe: 2.0.3
+ picomatch: 4.0.2
+ pkg-types: 1.3.1
+ scule: 1.3.0
+ strip-literal: 3.0.0
+ unplugin: 2.3.4
+ unplugin-utils: 0.2.4
+
+ unist-util-is@6.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-stringify-position@4.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-visit-parents@6.0.1:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-is: 6.0.0
+
+ unist-util-visit@5.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-is: 6.0.0
+ unist-util-visit-parents: 6.0.1
+
+ universalify@0.2.0: {}
+
+ universalify@2.0.1: {}
+
+ unocss-applet@0.10.0(@unocss/core@66.2.3)(@unocss/preset-mini@66.1.2)(@unocss/preset-wind3@66.1.2)(unocss@65.4.2(postcss@8.5.3)(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))):
+ dependencies:
+ '@unocss-applet/preset-applet': 0.10.0(@unocss/core@66.2.3)(@unocss/preset-mini@66.1.2)(@unocss/preset-wind3@66.1.2)
+ '@unocss-applet/preset-rem-rpx': 0.10.0(@unocss/core@66.2.3)
+ '@unocss-applet/transformer-attributify': 0.10.0(@unocss/core@66.2.3)
+ unocss: 65.4.2(postcss@8.5.3)(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))
+ transitivePeerDependencies:
+ - '@unocss/core'
+ - '@unocss/preset-mini'
+ - '@unocss/preset-wind3'
+
+ unocss@65.4.2(postcss@8.5.3)(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2)):
+ dependencies:
+ '@unocss/astro': 65.4.2(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))
+ '@unocss/cli': 65.4.2(rollup@4.41.1)
+ '@unocss/core': 65.4.2
+ '@unocss/postcss': 65.4.2(postcss@8.5.3)
+ '@unocss/preset-attributify': 65.4.2
+ '@unocss/preset-icons': 65.4.2
+ '@unocss/preset-mini': 65.4.2
+ '@unocss/preset-tagify': 65.4.2
+ '@unocss/preset-typography': 65.4.2
+ '@unocss/preset-uno': 65.4.2
+ '@unocss/preset-web-fonts': 65.4.2
+ '@unocss/preset-wind': 65.4.2
+ '@unocss/transformer-attributify-jsx': 65.4.2
+ '@unocss/transformer-compile-class': 65.4.2
+ '@unocss/transformer-directives': 65.4.2
+ '@unocss/transformer-variant-group': 65.4.2
+ '@unocss/vite': 65.4.2(rollup@4.41.1)(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0))(vue@3.5.15(typescript@5.7.2))
+ optionalDependencies:
+ vite: 5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0)
+ transitivePeerDependencies:
+ - postcss
+ - rollup
+ - supports-color
+ - vue
+
+ unpipe@1.0.0: {}
+
+ unplugin-auto-import@0.16.7(rollup@4.41.1):
+ dependencies:
+ '@antfu/utils': 0.7.10
+ '@rollup/pluginutils': 5.1.4(rollup@4.41.1)
+ fast-glob: 3.3.3
+ local-pkg: 0.5.1
+ magic-string: 0.30.17
+ minimatch: 9.0.5
+ unimport: 3.14.3(rollup@4.41.1)
+ unplugin: 1.16.0
+ transitivePeerDependencies:
+ - rollup
+
+ unplugin-auto-import@0.17.8(rollup@4.41.1):
+ dependencies:
+ '@antfu/utils': 0.7.10
+ '@rollup/pluginutils': 5.1.3(rollup@4.41.1)
+ fast-glob: 3.3.2
+ local-pkg: 0.5.1
+ magic-string: 0.30.14
+ minimatch: 9.0.5
+ unimport: 3.14.3(rollup@4.41.1)
+ unplugin: 1.16.0
+ transitivePeerDependencies:
+ - rollup
+
+ unplugin-auto-import@19.1.0:
+ dependencies:
+ local-pkg: 1.1.1
+ magic-string: 0.30.17
+ picomatch: 4.0.2
+ unimport: 4.1.1
+ unplugin: 2.3.4
+ unplugin-utils: 0.2.4
+
+ unplugin-utils@0.2.4:
+ dependencies:
+ pathe: 2.0.3
+ picomatch: 4.0.2
+
+ unplugin@1.16.0:
+ dependencies:
+ acorn: 8.14.0
+ webpack-virtual-modules: 0.6.2
+
+ unplugin@2.3.4:
+ dependencies:
+ acorn: 8.14.1
+ picomatch: 4.0.2
+ webpack-virtual-modules: 0.6.2
+
+ unquote@1.1.1: {}
+
+ update-browserslist-db@1.1.1(browserslist@4.24.2):
+ dependencies:
+ browserslist: 4.24.2
+ escalade: 3.2.0
+ picocolors: 1.1.1
+
+ update-browserslist-db@1.1.3(browserslist@4.25.0):
+ dependencies:
+ browserslist: 4.25.0
+ escalade: 3.2.0
+ picocolors: 1.1.1
+
+ uri-js@4.4.1:
+ dependencies:
+ punycode: 2.3.1
+
+ url-parse@1.5.10:
+ dependencies:
+ querystringify: 2.2.0
+ requires-port: 1.0.0
+
+ utif@2.0.1:
+ dependencies:
+ pako: 1.0.11
+
+ util-deprecate@1.0.2: {}
+
+ utils-merge@1.0.1: {}
+
+ v8-to-istanbul@8.1.1:
+ dependencies:
+ '@types/istanbul-lib-coverage': 2.0.6
+ convert-source-map: 1.9.0
+ source-map: 0.7.4
+
+ vary@1.1.2: {}
+
+ vite-plugin-restart@0.4.2(vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0)):
+ dependencies:
+ micromatch: 4.0.8
+ vite: 5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0)
+
+ vite@5.2.8(@types/node@20.19.0)(sass@1.77.8)(terser@5.36.0):
+ dependencies:
+ esbuild: 0.20.2
+ postcss: 8.5.3
+ rollup: 4.41.1
+ optionalDependencies:
+ '@types/node': 20.19.0
+ fsevents: 2.3.3
+ sass: 1.77.8
+ terser: 5.36.0
+
+ vscode-uri@3.1.0: {}
+
+ vue-demi@0.14.10(vue@3.5.15(typescript@5.7.2)):
+ dependencies:
+ vue: 3.5.15(typescript@5.7.2)
+
+ vue-eslint-parser@10.1.3(eslint@9.29.0(jiti@2.4.2)):
+ dependencies:
+ debug: 4.4.1
+ eslint: 9.29.0(jiti@2.4.2)
+ eslint-scope: 8.4.0
+ eslint-visitor-keys: 4.2.1
+ espree: 10.4.0
+ esquery: 1.6.0
+ lodash: 4.17.21
+ semver: 7.7.2
+ transitivePeerDependencies:
+ - supports-color
+
+ vue-flow-layout@0.1.1(vue@3.5.15(typescript@5.7.2)):
+ dependencies:
+ vue: 3.5.15(typescript@5.7.2)
+
+ vue-router@4.5.0(vue@3.5.15(typescript@5.7.2)):
+ dependencies:
+ '@vue/devtools-api': 6.6.4
+ vue: 3.5.15(typescript@5.7.2)
+
+ vue-tsc@2.2.10(typescript@5.7.2):
+ dependencies:
+ '@volar/typescript': 2.4.14
+ '@vue/language-core': 2.2.10(typescript@5.7.2)
+ typescript: 5.7.2
+
+ vue@3.5.15(typescript@5.7.2):
+ dependencies:
+ '@vue/compiler-dom': 3.5.15
+ '@vue/compiler-sfc': 3.5.15
+ '@vue/runtime-dom': 3.5.15
+ '@vue/server-renderer': 3.5.15(vue@3.5.15(typescript@5.7.2))
+ '@vue/shared': 3.5.15
+ optionalDependencies:
+ typescript: 5.7.2
+
+ w3c-hr-time@1.0.2:
+ dependencies:
+ browser-process-hrtime: 1.0.0
+
+ w3c-xmlserializer@2.0.0:
+ dependencies:
+ xml-name-validator: 3.0.0
+
+ walker@1.0.8:
+ dependencies:
+ makeerror: 1.0.12
+
+ wcwidth@1.0.1:
+ dependencies:
+ defaults: 1.0.4
+
+ webidl-conversions@3.0.1: {}
+
+ webidl-conversions@5.0.0: {}
+
+ webidl-conversions@6.1.0: {}
+
+ webpack-virtual-modules@0.6.2: {}
+
+ whatwg-encoding@1.0.5:
+ dependencies:
+ iconv-lite: 0.4.24
+
+ whatwg-mimetype@2.3.0: {}
+
+ whatwg-url@5.0.0:
+ dependencies:
+ tr46: 0.0.3
+ webidl-conversions: 3.0.1
+
+ whatwg-url@8.7.0:
+ dependencies:
+ lodash: 4.17.21
+ tr46: 2.1.0
+ webidl-conversions: 6.1.0
+
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ word-wrap@1.2.5: {}
+
+ wordwrap@1.0.0: {}
+
+ wot-design-uni@1.9.1(vue@3.5.15(typescript@5.7.2)):
+ dependencies:
+ vue: 3.5.15(typescript@5.7.2)
+
+ wrap-ansi@7.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+
+ wrap-ansi@8.1.0:
+ dependencies:
+ ansi-styles: 6.2.1
+ string-width: 5.1.2
+ strip-ansi: 7.1.0
+
+ wrap-ansi@9.0.0:
+ dependencies:
+ ansi-styles: 6.2.1
+ string-width: 7.2.0
+ strip-ansi: 7.1.0
+
+ wrappy@1.0.2: {}
+
+ write-file-atomic@3.0.3:
+ dependencies:
+ imurmurhash: 0.1.4
+ is-typedarray: 1.0.0
+ signal-exit: 3.0.7
+ typedarray-to-buffer: 3.1.5
+
+ ws@7.5.10: {}
+
+ ws@8.18.0: {}
+
+ xhr@2.6.0:
+ dependencies:
+ global: 4.4.0
+ is-function: 1.0.2
+ parse-headers: 2.0.5
+ xtend: 4.0.2
+
+ xml-name-validator@3.0.0: {}
+
+ xml-name-validator@4.0.0: {}
+
+ xml-parse-from-string@1.0.1: {}
+
+ xml2js@0.5.0:
+ dependencies:
+ sax: 1.4.1
+ xmlbuilder: 11.0.1
+
+ xmlbuilder@11.0.1: {}
+
+ xmlchars@2.2.0: {}
+
+ xmlhttprequest@1.8.0: {}
+
+ xregexp@3.1.0: {}
+
+ xtend@4.0.2: {}
+
+ y18n@5.0.8: {}
+
+ yallist@3.1.1: {}
+
+ yallist@4.0.0: {}
+
+ yaml-eslint-parser@1.3.0:
+ dependencies:
+ eslint-visitor-keys: 3.4.3
+ yaml: 2.6.1
+
+ yaml@1.10.2: {}
+
+ yaml@2.5.1: {}
+
+ yaml@2.6.1: {}
+
+ yaml@2.8.0: {}
+
+ yargs-parser@20.2.9: {}
+
+ yargs-parser@21.1.1: {}
+
+ yargs@16.2.0:
+ dependencies:
+ cliui: 7.0.4
+ escalade: 3.2.0
+ get-caller-file: 2.0.5
+ require-directory: 2.1.1
+ string-width: 4.2.3
+ y18n: 5.0.8
+ yargs-parser: 20.2.9
+
+ yargs@17.7.2:
+ dependencies:
+ cliui: 8.0.1
+ escalade: 3.2.0
+ get-caller-file: 2.0.5
+ require-directory: 2.1.1
+ string-width: 4.2.3
+ y18n: 5.0.8
+ yargs-parser: 21.1.1
+
+ yocto-queue@0.1.0: {}
+
+ yocto-queue@1.2.1: {}
+
+ z-paging@2.8.7: {}
+
+ zwitch@2.0.4: {}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/pnpm-workspace.yaml b/xiaozhi-esp32-server/main/manager-mobile/pnpm-workspace.yaml
new file mode 100644
index 0000000..82e3c6c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/pnpm-workspace.yaml
@@ -0,0 +1,6 @@
+packages:
+ - '**'
+ - '!node_modules'
+
+patchedDependencies:
+ '@dcloudio/uni-h5': patches/@dcloudio__uni-h5.patch
diff --git a/xiaozhi-esp32-server/main/manager-mobile/scripts/postupgrade.js b/xiaozhi-esp32-server/main/manager-mobile/scripts/postupgrade.js
new file mode 100644
index 0000000..65e4e60
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/scripts/postupgrade.js
@@ -0,0 +1,35 @@
+// # 鎵ц `pnpm upgrade` 鍚庝細鍗囩骇 `uniapp` 鐩稿叧渚濊禆
+// # 鍦ㄥ崌绾у畬鍚庯紝浼氳嚜鍔ㄦ坊鍔犲緢澶氭棤鐢ㄤ緷璧栵紝杩欓渶瑕佸垹闄や互鍑忓皬渚濊禆鍖呬綋绉�
+// # 鍙渶瑕佹墽琛屼笅闈㈢殑鍛戒护鍗冲彲
+
+const { exec } = require('node:child_process')
+
+// 瀹氫箟瑕佹墽琛岀殑鍛戒护
+const dependencies = [
+ '@dcloudio/uni-app-harmony',
+ // TODO: 濡傛灉涓嶉渶瑕佹煇涓钩鍙扮殑灏忕▼搴忥紝璇锋墜鍔ㄥ垹闄ゆ垨娉ㄩ噴鎺�
+ '@dcloudio/uni-mp-alipay',
+ '@dcloudio/uni-mp-baidu',
+ '@dcloudio/uni-mp-jd',
+ '@dcloudio/uni-mp-kuaishou',
+ '@dcloudio/uni-mp-lark',
+ '@dcloudio/uni-mp-qq',
+ '@dcloudio/uni-mp-toutiao',
+ '@dcloudio/uni-mp-xhs',
+ '@dcloudio/uni-quickapp-webview',
+ // i18n妯℃澘瑕佹敞閲婃帀涓嬮潰鐨�
+ 'vue-i18n',
+]
+
+// 浣跨敤exec鎵ц鍛戒护
+exec(`pnpm un ${dependencies.join(' ')}`, (error, stdout, stderr) => {
+ if (error) {
+ // 濡傛灉鏈夐敊璇紝鎵撳嵃閿欒淇℃伅
+ console.error(`鎵ц鍑洪敊: ${error}`)
+ return
+ }
+ // 鎵撳嵃姝e父杈撳嚭
+ console.log(`stdout: ${stdout}`)
+ // 濡傛灉鏈夐敊璇緭鍑猴紝涔熸墦鍗板嚭鏉�
+ console.error(`stderr: ${stderr}`)
+})
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/App.vue b/xiaozhi-esp32-server/main/manager-mobile/src/App.vue
new file mode 100644
index 0000000..a62d43f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/App.vue
@@ -0,0 +1,95 @@
+<script setup lang="ts">
+import { onHide, onLaunch, onShow } from '@dcloudio/uni-app'
+import { onMounted, watch } from 'vue'
+import { usePageAuth } from '@/hooks/usePageAuth'
+import { t } from '@/i18n'
+import { useConfigStore } from '@/store'
+import { useLangStore } from '@/store/lang'
+import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'
+
+usePageAuth()
+
+const configStore = useConfigStore()
+const langStore = useLangStore()
+
+onLaunch(() => {
+ console.log('App Launch')
+ // 鑾峰彇鍏叡閰嶇疆
+ configStore.fetchPublicConfig().catch((error) => {
+ console.error('鑾峰彇鍏叡閰嶇疆澶辫触:', error)
+ })
+})
+onShow(() => {
+ console.log('App Show')
+ // 浣跨敤setTimeout寤惰繜鎵ц锛岀‘淇漷abBar宸茬粡鍒濆鍖�
+ setTimeout(() => {
+ updateTabBarText()
+ }, 100)
+})
+
+// 鍔ㄦ�佹洿鏂皌abBar鏂囨湰
+function updateTabBarText() {
+ try {
+ // 璁剧疆棣栭〉tabBar鏂囨湰
+ uni.setTabBarItem({
+ index: 0,
+ text: t('tabBar.home'),
+ success: () => {},
+ fail: (err) => {
+ console.log('璁剧疆棣栭〉tabBar鏂囨湰澶辫触:', err)
+ },
+ })
+
+ // 璁剧疆閰嶇綉tabBar鏂囨湰
+ uni.setTabBarItem({
+ index: 1,
+ text: t('tabBar.deviceConfig'),
+ success: () => {},
+ fail: (err) => {
+ console.log('璁剧疆閰嶇綉tabBar鏂囨湰澶辫触:', err)
+ },
+ })
+
+ // 璁剧疆绯荤粺tabBar鏂囨湰
+ uni.setTabBarItem({
+ index: 2,
+ text: t('tabBar.settings'),
+ success: () => {},
+ fail: (err) => {
+ console.log('璁剧疆绯荤粺tabBar鏂囨湰澶辫触:', err)
+ },
+ })
+ }
+ catch (error) {
+ console.log('鏇存柊tabBar鏂囨湰鏃跺嚭閿�:', error)
+ }
+}
+// 鐩戝惉璇█鍒囨崲浜嬩欢
+onMounted(() => {
+ // 鐩戝惉璇█鍙樺寲锛屽綋璇█鏀瑰彉鏃惰嚜鍔ㄦ洿鏂皌abBar鏂囨湰
+ watch(() => langStore.currentLang, () => {
+ console.log('璇█宸插垏鎹紝鏇存柊tabBar鏂囨湰')
+ // 璇█鍒囨崲鍚庣珛鍗虫洿鏂皌abBar鏂囨湰
+ updateTabBarText()
+ })
+})
+
+onHide(() => {
+ console.log('App Hide')
+})
+</script>
+
+<style lang="scss">
+swiper,
+scroll-view {
+ flex: 1;
+ height: 100%;
+ overflow: hidden;
+}
+
+image {
+ width: 100%;
+ height: 100%;
+ vertical-align: middle;
+}
+</style>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/api/agent/agent.ts b/xiaozhi-esp32-server/main/manager-mobile/src/api/agent/agent.ts
new file mode 100644
index 0000000..bfaca35
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/api/agent/agent.ts
@@ -0,0 +1,300 @@
+import type {
+ Agent,
+ AgentCreateData,
+ AgentDetail,
+ AgentSnapshot,
+ AgentSnapshotPageParams,
+ CorrectWordFile,
+ ModelOption,
+ PageData,
+ RoleTemplate,
+ TtsVoice,
+} from './types'
+import { http } from '@/http/request/alova'
+
+// 鑾峰彇鏅鸿兘浣撹鎯�
+export function getAgentDetail(id: string) {
+ return http.Get<AgentDetail>(`/agent/${id}`, {
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ cacheFor: {
+ expire: 0,
+ },
+ })
+}
+
+// 鑾峰彇瑙掕壊妯℃澘鍒楄〃
+export function getRoleTemplates() {
+ return http.Get<RoleTemplate[]>('/agent/template', {
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ cacheFor: {
+ expire: 0,
+ },
+ })
+}
+
+// 鑾峰彇妯″瀷閫夐」
+export function getModelOptions(modelType: string, modelName: string = '') {
+ return http.Get<ModelOption[]>('/models/names', {
+ params: {
+ modelType,
+ modelName,
+ },
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ cacheFor: {
+ expire: 0,
+ },
+ })
+}
+
+// 鑾峰彇鏅鸿兘浣撳垪琛�
+export function getAgentList() {
+ return http.Get<Agent[]>('/agent/list', {
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ cacheFor: {
+ expire: 0,
+ },
+ })
+}
+
+// 鍒涘缓鏅鸿兘浣�
+export function createAgent(data: AgentCreateData) {
+ return http.Post<string>('/agent', data, {
+ meta: {
+ ignoreAuth: false,
+ toast: true,
+ },
+ })
+}
+
+// 鍒犻櫎鏅鸿兘浣�
+export function deleteAgent(id: string) {
+ return http.Delete(`/agent/${id}`, {
+ meta: {
+ ignoreAuth: false,
+ toast: true,
+ },
+ })
+}
+
+// 鑾峰彇TTS闊宠壊鍒楄〃
+export function getTTSVoices(ttsModelId: string, voiceName: string = '') {
+ return http.Get<TtsVoice[]>(`/models/${ttsModelId}/voices`, {
+ params: {
+ voiceName,
+ },
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ cacheFor: {
+ expire: 0,
+ },
+ })
+}
+
+// 鏇存柊鏅鸿兘浣�
+export function updateAgent(id: string, data: Partial<AgentDetail> & { tagNames?: string[] }) {
+ return http.Put(`/agent/${id}`, data, {
+ meta: {
+ ignoreAuth: false,
+ toast: true,
+ },
+ cacheFor: {
+ expire: 0,
+ },
+ })
+}
+
+// 鑾峰彇鎻掍欢鍒楄〃
+export function getPluginFunctions() {
+ return http.Get<any[]>(`/models/provider/plugin/names`, {
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ cacheFor: {
+ expire: 0,
+ },
+ })
+}
+
+// 鑾峰彇mcp鎺ュ叆鐐�
+export function getMcpAddress(agentId: string) {
+ return http.Get<string>(`/agent/mcp/address/${agentId}`, {
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ isExposeError: true,
+ },
+ })
+}
+
+// 鑾峰彇mcp宸ュ叿
+export function getMcpTools(agentId: string) {
+ return http.Get<string[]>(`/agent/mcp/tools/${agentId}`, {
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ cacheFor: {
+ expire: 0,
+ },
+ })
+}
+
+// 鑾峰彇澹扮汗鍒楄〃
+export function getVoicePrintList(agentId: string) {
+ return http.Get<any[]>(`/agent/voice-print/list/${agentId}`, {
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ cacheFor: {
+ expire: 0,
+ },
+ })
+}
+
+// 鑾峰彇璇煶瀵硅瘽璁板綍
+export function getChatHistoryUser(agentId: string) {
+ return http.Get<any[]>(`/agent/${agentId}/chat-history/user`, {
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ cacheFor: {
+ expire: 0,
+ },
+ })
+}
+
+// 鏂板澹扮汗璇磋瘽浜�
+export function createVoicePrint(data: { agentId: string, audioId: string, sourceName: string, introduce: string }) {
+ return http.Post('/agent/voice-print', data, {
+ meta: {
+ ignoreAuth: false,
+ toast: true,
+ },
+ })
+}
+
+// 鑾峰彇鏅鸿兘浣撴爣绛�
+export function getAgentTags(agentId: string) {
+ return http.Get<any[]>(`/agent/${agentId}/tags`, {
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ cacheFor: {
+ expire: 0,
+ },
+ })
+}
+
+// 鏇存柊鏅鸿兘浣撴爣绛�
+export function updateAgentTags(agentId: string, data) {
+ return http.Put(`/agent/${agentId}/tags`, data, {
+ meta: {
+ ignoreAuth: false,
+ isExposeError: true,
+ },
+ })
+}
+
+// 鑾峰彇鎵�鏈夎瑷�
+export function getAllLanguage(modelId: string) {
+ return http.Get<TtsVoice[]>(`/models/${modelId}/voices`, {
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ cacheFor: {
+ expire: 0,
+ },
+ })
+}
+
+/**
+ * 鑾峰彇鍏嬮殕闊宠壊鐨勪复鏃舵挱鏀綢D
+ * @param cloneId 鍏嬮殕闊宠壊璁板綍ID
+ */
+export function getVoiceCloneAudioId(cloneId: string) {
+ return http.Post<string>(`/voiceClone/audio/${cloneId}`, {}, {
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ })
+}
+
+// 鑾峰彇鏅鸿兘浣撳巻鍙茬増鏈垪琛�
+export function getAgentSnapshots(agentId: string, params: AgentSnapshotPageParams) {
+ return http.Get<PageData<AgentSnapshot>>(`/agent/${agentId}/snapshots`, {
+ params,
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ cacheFor: {
+ expire: 0,
+ },
+ })
+}
+
+// 鑾峰彇鏅鸿兘浣撳巻鍙茬増鏈鎯�
+export function getAgentSnapshot(agentId: string, snapshotId: string) {
+ return http.Get<AgentSnapshot>(`/agent/${agentId}/snapshots/${snapshotId}`, {
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ cacheFor: {
+ expire: 0,
+ },
+ })
+}
+
+// 鎭㈠鏅鸿兘浣撳巻鍙茬増鏈�
+export function restoreAgentSnapshot(agentId: string, snapshotId: string, currentStateToken: string) {
+ return http.Post(`/agent/${agentId}/snapshots/${snapshotId}/restore`, { currentStateToken }, {
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ })
+}
+
+// 鍒犻櫎鏅鸿兘浣撳巻鍙茬増鏈�
+export function deleteAgentSnapshot(agentId: string, snapshotId: string) {
+ return http.Delete(`/agent/${agentId}/snapshots/${snapshotId}`, {
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ })
+}
+
+// 鑾峰彇鎵�鏈夋浛鎹㈣瘝鏂囦欢
+export function getCorrectWordFiles() {
+ return http.Get<CorrectWordFile[]>('/correct-word/file/select', {
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ cacheFor: {
+ expire: 0,
+ },
+ })
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/api/agent/types.ts b/xiaozhi-esp32-server/main/manager-mobile/src/api/agent/types.ts
new file mode 100644
index 0000000..440f507
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/api/agent/types.ts
@@ -0,0 +1,178 @@
+// 鏅鸿兘浣撳垪琛ㄦ暟鎹被鍨�
+export interface Agent {
+ id: string
+ agentName: string
+ ttsModelName: string
+ ttsVoiceName: string
+ llmModelName: string
+ vllmModelName: string
+ memModelId: string
+ systemPrompt: string
+ summaryMemory: string | null
+ lastConnectedAt: string | null
+ deviceCount: number
+ tags: Record<string, string>[]
+}
+
+// 鏅鸿兘浣撳垱寤烘暟鎹被鍨�
+export interface AgentCreateData {
+ agentName: string
+}
+
+// 鏅鸿兘浣撹鎯呮暟鎹被鍨�
+export interface AgentDetail {
+ id: string
+ userId: string
+ agentCode: string
+ agentName: string
+ asrModelId: string
+ vadModelId: string
+ llmModelId: string
+ slmModelId: string
+ vllmModelId: string
+ ttsModelId: string
+ ttsVoiceId: string
+ memModelId: string
+ intentModelId: string
+ chatHistoryConf: number
+ systemPrompt: string
+ summaryMemory: string
+ langCode: string
+ language: string
+ sort: number
+ creator: string
+ createdAt: string
+ updater: string
+ updatedAt: string
+ ttsLanguage: string | null
+ ttsVolume: number | null
+ ttsRate: number | null
+ ttsPitch: number | null
+ currentVersionNo?: number | null
+ tagNames?: string[]
+ functions: AgentFunction[]
+ contextProviders: Providers[]
+}
+
+export interface Providers {
+ url: string
+ headers: Array<{
+ key: string
+ value: string
+ }>
+}
+
+export interface AgentFunction {
+ id?: string
+ agentId?: string
+ pluginId: string
+ paramInfo: Record<string, string | number | boolean> | null
+}
+
+export interface PageData<T> {
+ list: T[]
+ total: number
+}
+
+export interface AgentSnapshotData extends Partial<AgentDetail> {
+ correctWordFileIds?: string[]
+ tagNames?: string[]
+ tags?: Array<{
+ tagName?: string
+ [key: string]: any
+ }>
+ [key: string]: any
+}
+
+export interface AgentSnapshot {
+ id: string
+ agentId: string
+ userId?: string
+ versionNo: number
+ changedFields?: string[]
+ fieldOrder?: string[]
+ source?: string
+ restoreFromSnapshotId?: string | null
+ restoreFromVersionNo?: number | null
+ currentStateToken?: string
+ currentSnapshotData?: AgentSnapshotData
+ creator?: string
+ createdAt?: string
+ snapshotData?: AgentSnapshotData
+ afterSnapshotData?: AgentSnapshotData
+}
+
+export interface AgentSnapshotPageParams {
+ page?: number
+ limit?: number
+ maxVersionNo?: number
+}
+
+export interface CorrectWordFile {
+ id: string
+ fileName: string
+ wordCount?: number
+}
+
+export interface TtsVoice {
+ id: string
+ name: string
+ voiceDemo?: string | null
+ languages?: string | null
+ isClone?: boolean | null
+}
+
+// 瑙掕壊妯℃澘鏁版嵁绫诲瀷
+export interface RoleTemplate {
+ id: string
+ agentCode: string
+ agentName: string
+ asrModelId: string
+ vadModelId: string
+ llmModelId: string
+ vllmModelId: string
+ ttsModelId: string
+ ttsVoiceId: string
+ ttsLanguage?: string | null
+ memModelId: string
+ intentModelId: string
+ chatHistoryConf: number
+ systemPrompt: string
+ summaryMemory: string
+ langCode: string
+ language: string
+ sort: number
+ creator: string
+ createdAt: string
+ updater: string
+ updatedAt: string
+}
+
+// 妯″瀷閫夐」鏁版嵁绫诲瀷
+export interface ModelOption {
+ id: string
+ modelName: string
+}
+
+export interface PluginField {
+ key: string
+ type: string
+ label: string
+ default: string
+ selected?: boolean
+ editing?: boolean
+}
+
+export interface PluginDefinition {
+ id: string
+ modelType: string
+ providerCode: string
+ name: string
+ fields: PluginField[] // 娉ㄦ剰锛氬師濮嬫槸瀛楃涓诧紝闇�瑕佸厛 JSON.parse
+ sort: number
+ updater: string
+ updateDate: string
+ creator: string
+ createDate: string
+ [key: string]: any
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/api/auth.ts b/xiaozhi-esp32-server/main/manager-mobile/src/api/auth.ts
new file mode 100644
index 0000000..f9750a0
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/api/auth.ts
@@ -0,0 +1,143 @@
+import { http } from '@/http/request/alova'
+
+// 鐧诲綍鎺ュ彛鏁版嵁绫诲瀷
+export interface LoginData {
+ username: string
+ password: string
+ captchaId: string
+ areaCode?: string
+ mobile?: string
+}
+
+// 鐧诲綍鍝嶅簲鏁版嵁绫诲瀷
+export interface LoginResponse {
+ token: string
+ expire: number
+ clientHash: string
+}
+
+// 楠岃瘉鐮佸搷搴旀暟鎹被鍨�
+export interface CaptchaResponse {
+ captchaId: string
+ captchaImage: string
+}
+
+// 鑾峰彇楠岃瘉鐮�
+export function getCaptcha(uuid: string) {
+ return http.Get<string>('/user/captcha', {
+ params: { uuid },
+ meta: {
+ ignoreAuth: true,
+ toast: false,
+ },
+ })
+}
+
+// 鐢ㄦ埛鐧诲綍
+export function login(data: LoginData) {
+ return http.Post<LoginResponse>('/user/login', data, {
+ meta: {
+ ignoreAuth: true,
+ toast: true,
+ },
+ })
+}
+
+// 鐢ㄦ埛淇℃伅鍝嶅簲鏁版嵁绫诲瀷
+export interface UserInfo {
+ id: number
+ username: string
+ realName: string
+ email: string
+ mobile: string
+ status: number
+ superAdmin: number
+}
+
+// 鍏叡閰嶇疆鍝嶅簲鏁版嵁绫诲瀷
+export interface PublicConfig {
+ enableMobileRegister: boolean
+ version: string
+ year: string
+ allowUserRegister: boolean
+ mobileAreaList: Array<{
+ name: string
+ key: string
+ }>
+ beianIcpNum: string
+ beianGaNum: string
+ name: string
+ sm2PublicKey: string
+}
+
+// 鑾峰彇鐢ㄦ埛淇℃伅
+export function getUserInfo() {
+ return http.Get<UserInfo>('/user/info', {
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ })
+}
+
+// 鑾峰彇鍏叡閰嶇疆
+export function getPublicConfig() {
+ return http.Get<PublicConfig>('/user/pub-config', {
+ meta: {
+ ignoreAuth: true,
+ toast: false,
+ },
+ })
+}
+
+// 娉ㄥ唽鏁版嵁绫诲瀷
+export interface RegisterData {
+ username: string
+ password: string
+ captchaId: string
+ areaCode: string
+ mobile: string
+ mobileCaptcha: string
+}
+
+// 鍙戦�佺煭淇¢獙璇佺爜
+export function sendSmsCode(data: {
+ phone: string
+ captcha: string
+ captchaId: string
+}) {
+ return http.Post('/user/smsVerification', data, {
+ meta: {
+ ignoreAuth: true,
+ toast: false,
+ },
+ })
+}
+
+// 鐢ㄦ埛娉ㄥ唽
+export function register(data: RegisterData) {
+ return http.Post('/user/register', data, {
+ meta: {
+ ignoreAuth: true,
+ toast: true,
+ },
+ })
+}
+
+// 蹇樿瀵嗙爜鏁版嵁绫诲瀷
+export interface ForgotPasswordData {
+ phone: string
+ code: string
+ password: string
+ captchaId: string
+}
+
+// 蹇樿瀵嗙爜锛堟壘鍥炲瘑鐮侊級
+export function retrievePassword(data: ForgotPasswordData) {
+ return http.Put('/user/retrieve-password', data, {
+ meta: {
+ ignoreAuth: true,
+ toast: true,
+ },
+ })
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/api/chat-history/chat-history.ts b/xiaozhi-esp32-server/main/manager-mobile/src/api/chat-history/chat-history.ts
new file mode 100644
index 0000000..dc7000e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/api/chat-history/chat-history.ts
@@ -0,0 +1,63 @@
+import type {
+ ChatMessage,
+ ChatSessionsResponse,
+ GetSessionsParams,
+} from './types'
+import { http } from '@/http/request/alova'
+
+/**
+ * 鑾峰彇鑱婂ぉ浼氳瘽鍒楄〃
+ * @param agentId 鏅鸿兘浣揑D
+ * @param params 鍒嗛〉鍙傛暟
+ */
+export function getChatSessions(agentId: string, params: GetSessionsParams) {
+ return http.Get<ChatSessionsResponse>(`/agent/${agentId}/sessions`, {
+ params,
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ cacheFor: {
+ expire: 0,
+ },
+ })
+}
+
+/**
+ * 鑾峰彇鑱婂ぉ璁板綍璇︽儏
+ * @param agentId 鏅鸿兘浣揑D
+ * @param sessionId 浼氳瘽ID
+ */
+export function getChatHistory(agentId: string, sessionId: string) {
+ return http.Get<ChatMessage[]>(`/agent/${agentId}/chat-history/${sessionId}`, {
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ cacheFor: {
+ expire: -1,
+ },
+ })
+}
+
+/**
+ * 鑾峰彇闊抽涓嬭浇ID
+ * @param audioId 闊抽ID
+ */
+export function getAudioId(audioId: string) {
+ return http.Post<string>(`/agent/audio/${audioId}`, {}, {
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ })
+}
+
+/**
+ * 鑾峰彇闊抽鎾斁鍦板潃
+ * @param downloadId 涓嬭浇ID
+ */
+export function getAudioPlayUrl(downloadId: string) {
+ // 鏍规嵁闇�姹傛枃妗o紝杩欎釜鏄洿鎺ヨ繑鍥炰簩杩涘埗鐨勶紝鎵�浠ユ垜浠洿鎺ユ瀯閫燯RL
+ return `/agent/play/${downloadId}`
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/api/chat-history/index.ts b/xiaozhi-esp32-server/main/manager-mobile/src/api/chat-history/index.ts
new file mode 100644
index 0000000..ecf782a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/api/chat-history/index.ts
@@ -0,0 +1,2 @@
+export * from './chat-history'
+export * from './types'
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/api/chat-history/types.ts b/xiaozhi-esp32-server/main/manager-mobile/src/api/chat-history/types.ts
new file mode 100644
index 0000000..9652db6
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/api/chat-history/types.ts
@@ -0,0 +1,39 @@
+// 鑱婂ぉ浼氳瘽鍒楄〃椤�
+export interface ChatSession {
+ sessionId: string
+ createdAt: string
+ chatCount: number
+ title: string
+}
+
+// 鑱婂ぉ浼氳瘽鍒楄〃鍝嶅簲
+export interface ChatSessionsResponse {
+ total: number
+ list: ChatSession[]
+}
+
+// 鑱婂ぉ娑堟伅
+export interface ChatMessage {
+ createdAt: string
+ chatType: 1 | 2 | 3 // 1鏄敤鎴凤紝2鏄疉I锛�3鏄弬鏁拌鏄�
+ content: string
+ audioId: string | null
+ macAddress: string
+}
+
+// 鐢ㄦ埛娑堟伅鍐呭锛堥渶瑕佽В鏋怞SON锛�
+export interface UserMessageContent {
+ speaker: string
+ content: string
+}
+
+// 鑾峰彇鑱婂ぉ浼氳瘽鍒楄〃鍙傛暟
+export interface GetSessionsParams {
+ page: number
+ limit: number
+}
+
+// 闊抽鎾斁鐩稿叧
+export interface AudioResponse {
+ data: string // 闊抽涓嬭浇ID
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/api/device/device.ts b/xiaozhi-esp32-server/main/manager-mobile/src/api/device/device.ts
new file mode 100644
index 0000000..e5a2f9c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/api/device/device.ts
@@ -0,0 +1,71 @@
+import type { Device, FirmwareType } from './types'
+import { http } from '@/http/request/alova'
+
+/**
+ * 鑾峰彇璁惧绫诲瀷鍒楄〃
+ */
+export function getFirmwareTypes() {
+ return http.Get<FirmwareType[]>('/admin/dict/data/type/FIRMWARE_TYPE')
+}
+
+/**
+ * 鑾峰彇缁戝畾璁惧鍒楄〃
+ * @param agentId 鏅鸿兘浣揑D
+ */
+export function getBindDevices(agentId: string) {
+ return http.Get<Device[]>(`/device/bind/${agentId}`, {
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ cacheFor: {
+ expire: 0,
+ },
+ })
+}
+
+/**
+ * 娣诲姞璁惧
+ * @param agentId 鏅鸿兘浣揑D
+ * @param code 楠岃瘉鐮�
+ */
+export function bindDevice(agentId: string, code: string) {
+ return http.Post(`/device/bind/${agentId}/${code}`, null)
+}
+
+/**
+ * 鎵嬪姩娣诲姞璁惧
+ * @param agentId 鏅鸿兘浣揑D
+ * @param board 璁惧绫诲瀷
+ * @param appVersion 鍥轰欢鐗堟湰
+ * @param macAddress MAC鍦板潃
+ */
+export function bindDeviceManual(data: {
+ agentId: string
+ board: string
+ appVersion: string
+ macAddress: string
+}) {
+ return http.Post('/device/manual-add', data)
+}
+
+/**
+ * 璁剧疆璁惧OTA鍗囩骇寮�鍏�
+ * @param deviceId 璁惧ID (MAC鍦板潃)
+ * @param autoUpdate 鏄惁鑷姩鍗囩骇 0|1
+ */
+export function updateDeviceAutoUpdate(deviceId: string, autoUpdate: number) {
+ return http.Put(`/device/update/${deviceId}`, {
+ autoUpdate,
+ })
+}
+
+/**
+ * 瑙g粦璁惧
+ * @param deviceId 璁惧ID (MAC鍦板潃)
+ */
+export function unbindDevice(deviceId: string) {
+ return http.Post('/device/unbind', {
+ deviceId,
+ })
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/api/device/index.ts b/xiaozhi-esp32-server/main/manager-mobile/src/api/device/index.ts
new file mode 100644
index 0000000..aa5f617
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/api/device/index.ts
@@ -0,0 +1,2 @@
+export * from './device'
+export * from './types'
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/api/device/types.ts b/xiaozhi-esp32-server/main/manager-mobile/src/api/device/types.ts
new file mode 100644
index 0000000..8b0de4e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/api/device/types.ts
@@ -0,0 +1,21 @@
+export interface FirmwareType {
+ name: string
+ key: string
+}
+
+export interface Device {
+ id: string
+ userId: string
+ macAddress: string
+ lastConnectedAtTimestamp: string | null
+ autoUpdate: number
+ board: string
+ alias?: string
+ agentId: string
+ appVersion: string
+ sort: number
+ updater?: string
+ updateDate: string
+ creator: string
+ createDate: string
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/api/voiceprint/index.ts b/xiaozhi-esp32-server/main/manager-mobile/src/api/voiceprint/index.ts
new file mode 100644
index 0000000..af56e69
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/api/voiceprint/index.ts
@@ -0,0 +1,2 @@
+export * from './types'
+export * from './voiceprint'
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/api/voiceprint/types.ts b/xiaozhi-esp32-server/main/manager-mobile/src/api/voiceprint/types.ts
new file mode 100644
index 0000000..726b178
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/api/voiceprint/types.ts
@@ -0,0 +1,29 @@
+// 澹扮汗淇℃伅鍝嶅簲绫诲瀷
+export interface VoicePrint {
+ id: string
+ audioId: string
+ sourceName: string
+ introduce: string
+ createDate: string
+}
+
+// 璇煶瀵硅瘽璁板綍绫诲瀷
+export interface ChatHistory {
+ content: string
+ audioId: string
+}
+
+// 鍒涘缓璇磋瘽浜烘暟鎹被鍨�
+export interface CreateSpeakerData {
+ agentId: string
+ audioId: string
+ sourceName: string
+ introduce: string
+}
+
+// 閫氱敤鍝嶅簲绫诲瀷
+export interface ApiResponse<T = any> {
+ code: number
+ msg: string
+ data: T
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/api/voiceprint/voiceprint.ts b/xiaozhi-esp32-server/main/manager-mobile/src/api/voiceprint/voiceprint.ts
new file mode 100644
index 0000000..0726c53
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/api/voiceprint/voiceprint.ts
@@ -0,0 +1,72 @@
+import type {
+ ChatHistory,
+ CreateSpeakerData,
+ VoicePrint,
+} from './types'
+import { http } from '@/http/request/alova'
+
+// 鑾峰彇澹扮汗鍒楄〃
+export function getVoicePrintList(agentId: string) {
+ return http.Get<VoicePrint[]>(`/agent/voice-print/list/${agentId}`, {
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ cacheFor: {
+ expire: 0,
+ },
+ })
+}
+
+// 鑾峰彇璇煶瀵硅瘽璁板綍锛堢敤浜庨�夋嫨澹扮汗鍚戦噺锛�
+export function getChatHistory(agentId: string) {
+ return http.Get<ChatHistory[]>(`/agent/${agentId}/chat-history/user`, {
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ cacheFor: {
+ expire: 0,
+ },
+ })
+}
+
+// 鏂板璇磋瘽浜�
+export function createVoicePrint(data: CreateSpeakerData) {
+ return http.Post<null>('/agent/voice-print', data, {
+ meta: {
+ ignoreAuth: false,
+ toast: true,
+ },
+ })
+}
+
+// 鍒犻櫎澹扮汗
+export function deleteVoicePrint(id: string) {
+ return http.Delete<null>(`/agent/voice-print/${id}`, {
+ meta: {
+ ignoreAuth: false,
+ toast: true,
+ },
+ })
+}
+
+// 鏇存柊澹扮汗淇℃伅
+export function updateVoicePrint(data: VoicePrint) {
+ return http.Put<null>('/agent/voice-print', data, {
+ meta: {
+ ignoreAuth: false,
+ toast: true,
+ },
+ })
+}
+
+// 鑾峰彇闊抽涓嬭浇ID
+export function getAudioDownloadId(audioId: string) {
+ return http.Post<string>(`/agent/audio/${audioId}`, {}, {
+ meta: {
+ ignoreAuth: false,
+ toast: false,
+ },
+ })
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/components/.gitkeep b/xiaozhi-esp32-server/main/manager-mobile/src/components/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/components/.gitkeep
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/components/custom-tabs/index.vue b/xiaozhi-esp32-server/main/manager-mobile/src/components/custom-tabs/index.vue
new file mode 100644
index 0000000..f5ef070
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/components/custom-tabs/index.vue
@@ -0,0 +1,131 @@
+<script setup lang="ts">
+interface TabItem {
+ label: string
+ value: string | number
+ icon: string
+ activeIcon: string
+}
+
+interface Props {
+ tabList: TabItem[]
+ modelValue?: string | number
+}
+
+interface Emits {
+ (e: 'update:modelValue', value: string | number): void
+ (e: 'change', item: TabItem, index: number): void
+}
+
+const props = withDefaults(defineProps<Props>(), {
+ modelValue: '',
+})
+
+const emit = defineEmits<Emits>()
+
+const activeValue = computed(() => {
+ return props.modelValue || (props.tabList[0]?.value ?? '')
+})
+
+function handleTabClick(item: TabItem, index: number) {
+ emit('update:modelValue', item.value)
+ emit('change', item, index)
+}
+</script>
+
+<template>
+ <view class="custom-tabs">
+ <view
+ v-for="(item, index) in tabList"
+ :key="item.value"
+ class="tab-item"
+ :class="{ active: activeValue === item.value }"
+ @click="handleTabClick(item, index)"
+ >
+ <view class="tab-icon">
+ <image
+ :src="activeValue === item.value ? item.activeIcon : item.icon"
+ class="icon-img"
+ mode="aspectFit"
+ />
+ </view>
+ <view class="tab-text">
+ {{ item.label }}
+ </view>
+ <view v-if="activeValue === item.value" class="tab-indicator" />
+ </view>
+ </view>
+</template>
+
+<style scoped>
+.custom-tabs {
+ display: flex;
+ align-items: center;
+ justify-content: space-around;
+ background-color: #ffffff;
+ padding: 0 16rpx;
+ border-top: 1rpx solid #eeeeee;
+ box-sizing: border-box;
+}
+
+.tab-item {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 16rpx 12rpx 12rpx;
+ flex: 1;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.tab-icon {
+ width: 48rpx;
+ height: 48rpx;
+ margin-bottom: 8rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.icon-img {
+ width: 100%;
+ height: 100%;
+}
+
+.tab-text {
+ font-size: 24rpx;
+ color: #9d9ea3;
+ line-height: 1.2;
+ text-align: center;
+ transition: color 0.2s ease;
+}
+
+.tab-item.active .tab-text {
+ color: #336cff;
+ font-weight: 500;
+}
+
+.tab-indicator {
+ position: absolute;
+ bottom: 0;
+ left: 50%;
+ transform: translateX(-50%);
+ width: 48rpx;
+ height: 4rpx;
+ background-color: #336cff;
+ border-radius: 2rpx;
+}
+
+/* 閫傞厤涓嶅悓灏哄灞忓箷 */
+@media (max-width: 375px) {
+ .tab-text {
+ font-size: 22rpx;
+ }
+
+ .tab-icon {
+ width: 44rpx;
+ height: 44rpx;
+ }
+}
+</style>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/env.d.ts b/xiaozhi-esp32-server/main/manager-mobile/src/env.d.ts
new file mode 100644
index 0000000..b4a2c97
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/env.d.ts
@@ -0,0 +1,34 @@
+/// <reference types="vite/client" />
+/// <reference types="vite-svg-loader" />
+
+declare module '*.vue' {
+ import type { DefineComponent } from 'vue'
+
+ const component: DefineComponent<{}, {}, any>
+ export default component
+}
+
+interface ImportMetaEnv {
+ /** 缃戠珯鏍囬锛屽簲鐢ㄥ悕绉� */
+ readonly VITE_APP_TITLE: string
+ /** 鏈嶅姟绔彛鍙� */
+ readonly VITE_SERVER_PORT: string
+ /** 鍚庡彴鎺ュ彛鍦板潃 */
+ readonly VITE_SERVER_BASEURL: string
+ /** H5鏄惁闇�瑕佷唬鐞� */
+ readonly VITE_APP_PROXY: 'true' | 'false'
+ /** H5鏄惁闇�瑕佷唬鐞嗭紝闇�瑕佺殑璇濇湁涓墠缂� */
+ readonly VITE_APP_PROXY_PREFIX: string // 涓�鑸槸/api
+ /** 涓婁紶鍥剧墖鍦板潃 */
+ readonly VITE_UPLOAD_BASEURL: string
+ /** 鏄惁娓呴櫎console */
+ readonly VITE_DELETE_CONSOLE: string
+ // 鏇村鐜鍙橀噺...
+}
+
+interface ImportMeta {
+ readonly env: ImportMetaEnv
+}
+
+declare const __VITE_APP_PROXY__: 'true' | 'false'
+declare const __UNI_PLATFORM__: 'app' | 'h5' | 'mp-alipay' | 'mp-baidu' | 'mp-kuaishou' | 'mp-lark' | 'mp-qq' | 'mp-tiktok' | 'mp-weixin' | 'mp-xiaochengxu'
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/hooks/.gitkeep b/xiaozhi-esp32-server/main/manager-mobile/src/hooks/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/hooks/.gitkeep
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/hooks/usePageAuth.ts b/xiaozhi-esp32-server/main/manager-mobile/src/hooks/usePageAuth.ts
new file mode 100644
index 0000000..fd006c8
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/hooks/usePageAuth.ts
@@ -0,0 +1,50 @@
+import { onLoad } from '@dcloudio/uni-app'
+import { useUserStore } from '@/store'
+import { needLoginPages as _needLoginPages, getNeedLoginPages } from '@/utils'
+
+const loginRoute = import.meta.env.VITE_LOGIN_URL
+const isDev = import.meta.env.DEV
+function isLogined() {
+ const userStore = useUserStore()
+ return !!userStore.userInfo.username
+}
+// 妫�鏌ュ綋鍓嶉〉闈㈡槸鍚﹂渶瑕佺櫥褰�
+export function usePageAuth() {
+ onLoad((options) => {
+ // 鑾峰彇褰撳墠椤甸潰璺緞
+ const pages = getCurrentPages()
+ const currentPage = pages[pages.length - 1]
+ const currentPath = `/${currentPage.route}`
+
+ // 鑾峰彇闇�瑕佺櫥褰曠殑椤甸潰鍒楄〃
+ let needLoginPages: string[] = []
+ if (isDev) {
+ needLoginPages = getNeedLoginPages()
+ }
+ else {
+ needLoginPages = _needLoginPages
+ }
+
+ // 妫�鏌ュ綋鍓嶉〉闈㈡槸鍚﹂渶瑕佺櫥褰�
+ const isNeedLogin = needLoginPages.includes(currentPath)
+ if (!isNeedLogin) {
+ return
+ }
+
+ const hasLogin = isLogined()
+ if (hasLogin) {
+ return true
+ }
+
+ // 鏋勫缓閲嶅畾鍚慤RL
+ const queryString = Object.entries(options || {})
+ .map(([key, value]) => `${key}=${encodeURIComponent(String(value))}`)
+ .join('&')
+
+ const currentFullPath = queryString ? `${currentPath}?${queryString}` : currentPath
+ const redirectRoute = `${loginRoute}?redirect=${encodeURIComponent(currentFullPath)}`
+
+ // 閲嶅畾鍚戝埌鐧诲綍椤�
+ uni.redirectTo({ url: redirectRoute })
+ })
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/hooks/useRequest.ts b/xiaozhi-esp32-server/main/manager-mobile/src/hooks/useRequest.ts
new file mode 100644
index 0000000..017a710
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/hooks/useRequest.ts
@@ -0,0 +1,51 @@
+import type { Ref } from 'vue'
+
+interface IUseRequestOptions<T> {
+ /** 鏄惁绔嬪嵆鎵ц */
+ immediate?: boolean
+ /** 鍒濆鍖栨暟鎹� */
+ initialData?: T
+}
+
+interface IUseRequestReturn<T> {
+ loading: Ref<boolean>
+ error: Ref<boolean | Error>
+ data: Ref<T | undefined>
+ run: () => Promise<T | undefined>
+}
+
+/**
+ * useRequest鏄竴涓畾鍒跺寲鐨勮姹傞挬瀛愶紝鐢ㄤ簬澶勭悊寮傛璇锋眰鍜屽搷搴斻��
+ * @param func 涓�涓墽琛屽紓姝ヨ姹傜殑鍑芥暟锛岃繑鍥炰竴涓寘鍚搷搴旀暟鎹殑Promise銆�
+ * @param options 鍖呭惈璇锋眰閫夐」鐨勫璞� {immediate, initialData}銆�
+ * @param options.immediate 鏄惁绔嬪嵆鎵ц璇锋眰锛岄粯璁や负false銆�
+ * @param options.initialData 鍒濆鍖栨暟鎹紝榛樿涓簎ndefined銆�
+ * @returns 杩斿洖涓�涓璞loading, error, data, run}锛屽寘鍚姹傜殑鍔犺浇鐘舵�併�侀敊璇俊鎭�佸搷搴旀暟鎹拰鎵嬪姩瑙﹀彂璇锋眰鐨勫嚱鏁般��
+ */
+export default function useRequest<T>(
+ func: () => Promise<IResData<T>>,
+ options: IUseRequestOptions<T> = { immediate: false },
+): IUseRequestReturn<T> {
+ const loading = ref(false)
+ const error = ref(false)
+ const data = ref<T | undefined>(options.initialData) as Ref<T | undefined>
+ const run = async () => {
+ loading.value = true
+ return func()
+ .then((res) => {
+ data.value = res.data
+ error.value = false
+ return data.value
+ })
+ .catch((err) => {
+ error.value = err
+ throw err
+ })
+ .finally(() => {
+ loading.value = false
+ })
+ }
+
+ options.immediate && run()
+ return { loading, error, data, run }
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/hooks/useUpload.ts b/xiaozhi-esp32-server/main/manager-mobile/src/hooks/useUpload.ts
new file mode 100644
index 0000000..3080d5a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/hooks/useUpload.ts
@@ -0,0 +1,160 @@
+import { ref } from 'vue'
+import { getEnvBaseUploadUrl } from '@/utils'
+
+const VITE_UPLOAD_BASEURL = `${getEnvBaseUploadUrl()}`
+
+type TfileType = 'image' | 'file'
+type TImage = 'png' | 'jpg' | 'jpeg' | 'webp' | '*'
+type TFile = 'doc' | 'docx' | 'ppt' | 'zip' | 'xls' | 'xlsx' | 'txt' | TImage
+
+interface TOptions<T extends TfileType> {
+ formData?: Record<string, any>
+ maxSize?: number
+ accept?: T extends 'image' ? TImage[] : TFile[]
+ fileType?: T
+ success?: (params: any) => void
+ error?: (err: any) => void
+}
+
+export default function useUpload<T extends TfileType>(options: TOptions<T> = {} as TOptions<T>) {
+ const {
+ formData = {},
+ maxSize = 5 * 1024 * 1024,
+ accept = ['*'],
+ fileType = 'image',
+ success,
+ error: onError,
+ } = options
+
+ const loading = ref(false)
+ const error = ref<Error | null>(null)
+ const data = ref<any>(null)
+
+ const handleFileChoose = ({ tempFilePath, size }: { tempFilePath: string, size: number }) => {
+ if (size > maxSize) {
+ uni.showToast({
+ title: `鏂囦欢澶у皬涓嶈兘瓒呰繃 ${maxSize / 1024 / 1024}MB`,
+ icon: 'none',
+ })
+ return
+ }
+
+ // const fileExtension = file?.tempFiles?.name?.split('.').pop()?.toLowerCase()
+ // const isTypeValid = accept.some((type) => type === '*' || type.toLowerCase() === fileExtension)
+
+ // if (!isTypeValid) {
+ // uni.showToast({
+ // title: `浠呮敮鎸� ${accept.join(', ')} 鏍煎紡鐨勬枃浠禶,
+ // icon: 'none',
+ // })
+ // return
+ // }
+
+ loading.value = true
+ uploadFile({
+ tempFilePath,
+ formData,
+ onSuccess: (res) => {
+ const { data: _data } = JSON.parse(res)
+ data.value = _data
+ // console.log('涓婁紶鎴愬姛', res)
+ success?.(_data)
+ },
+ onError: (err) => {
+ error.value = err
+ onError?.(err)
+ },
+ onComplete: () => {
+ loading.value = false
+ },
+ })
+ }
+
+ const run = () => {
+ // 寰俊灏忕▼搴忎粠鍩虹搴� 2.21.0 寮�濮嬶紝 wx.chooseImage 鍋滄缁存姢锛岃浣跨敤 uni.chooseMedia 浠f浛銆�
+ // 寰俊灏忕▼搴忓湪2023骞�10鏈�17鏃ヤ箣鍚庯紝浣跨敤鏈珹PI闇�瑕侀厤缃殣绉佸崗璁�
+ const chooseFileOptions = {
+ count: 1,
+ success: (res: any) => {
+ console.log('File selected successfully:', res)
+ // 灏忕▼搴忎腑res:{errMsg: "chooseImage:ok", tempFiles: [{fileType: "image", size: 48976, tempFilePath: "http://tmp/5iG1WpIxTaJf3ece38692a337dc06df7eb69ecb49c6b.jpeg"}]}
+ // h5涓璻es:{errMsg: "chooseImage:ok", tempFilePaths: "blob:http://localhost:9000/f74ab6b8-a14d-4cb6-a10d-fcf4511a0de5", tempFiles: [File]}
+ // h5鐨凢ile鏈変互涓嬪瓧娈碉細{name: "girl.jpeg", size: 48976, type: "image/jpeg"}
+ // App涓璻es:{errMsg: "chooseImage:ok", tempFilePaths: "file:///Users/feige/xxx/gallery/1522437259-compressed-IMG_0006.jpg", tempFiles: [File]}
+ // App鐨凢ile鏈変互涓嬪瓧娈碉細{path: "file:///Users/feige/xxx/gallery/1522437259-compressed-IMG_0006.jpg", size: 48976}
+ let tempFilePath = ''
+ let size = 0
+ // #ifdef MP-WEIXIN
+ tempFilePath = res.tempFiles[0].tempFilePath
+ size = res.tempFiles[0].size
+ // #endif
+ // #ifndef MP-WEIXIN
+ tempFilePath = res.tempFilePaths[0]
+ size = res.tempFiles[0].size
+ // #endif
+ handleFileChoose({ tempFilePath, size })
+ },
+ fail: (err: any) => {
+ console.error('File selection failed:', err)
+ error.value = err
+ onError?.(err)
+ },
+ }
+
+ if (fileType === 'image') {
+ // #ifdef MP-WEIXIN
+ uni.chooseMedia({
+ ...chooseFileOptions,
+ mediaType: ['image'],
+ })
+ // #endif
+
+ // #ifndef MP-WEIXIN
+ uni.chooseImage(chooseFileOptions)
+ // #endif
+ }
+ else {
+ uni.chooseFile({
+ ...chooseFileOptions,
+ type: 'all',
+ })
+ }
+ }
+
+ return { loading, error, data, run }
+}
+
+async function uploadFile({
+ tempFilePath,
+ formData,
+ onSuccess,
+ onError,
+ onComplete,
+}: {
+ tempFilePath: string
+ formData: Record<string, any>
+ onSuccess: (data: any) => void
+ onError: (err: any) => void
+ onComplete: () => void
+}) {
+ uni.uploadFile({
+ url: VITE_UPLOAD_BASEURL,
+ filePath: tempFilePath,
+ name: 'file',
+ formData,
+ success: (uploadFileRes) => {
+ try {
+ const data = uploadFileRes.data
+ onSuccess(data)
+ }
+ catch (err) {
+ onError(err)
+ }
+ },
+ fail: (err) => {
+ console.error('Upload failed:', err)
+ onError(err)
+ },
+ complete: onComplete,
+ })
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/http/README.md b/xiaozhi-esp32-server/main/manager-mobile/src/http/README.md
new file mode 100644
index 0000000..fd34fd5
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/http/README.md
@@ -0,0 +1,36 @@
+# 璇锋眰搴�
+
+褰撳墠椤圭洰浣跨敤 Alova 浣滀负鍞竴鐨� HTTP 璇锋眰搴擄細
+
+## 浣跨敤鏂瑰紡
+
+- **Alova HTTP**锛氳矾寰勶紙src/http/request/alova.ts锛�
+- **绀轰緥浠g爜**锛歴rc/api/foo-alova.ts 鍜� src/api/foo.ts
+- **API鏂囨。**锛歨ttps://alova.js.org/
+
+## 閰嶇疆璇存槑
+
+Alova 瀹炰緥宸查厤缃細
+- 鑷姩 Token 璁よ瘉鍜屽埛鏂�
+- 缁熶竴閿欒澶勭悊鍜屾彁绀�
+- 鏀寔鍔ㄦ�佸煙鍚嶅垏鎹�
+- 鍐呯疆璇锋眰/鍝嶅簲鎷︽埅鍣�
+
+## 浣跨敤绀轰緥
+
+```typescript
+import { http } from '@/http/request/alova'
+
+// GET 璇锋眰
+http.Get<ResponseType>('/api/path', {
+ params: { id: 1 },
+ headers: { 'Custom-Header': 'value' },
+ meta: { toast: false } // 鍏抽棴閿欒鎻愮ず
+})
+
+// POST 璇锋眰
+http.Post<ResponseType>('/api/path', data, {
+ params: { query: 'param' },
+ headers: { 'Content-Type': 'application/json' }
+})
+```
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/http/request/alova.ts b/xiaozhi-esp32-server/main/manager-mobile/src/http/request/alova.ts
new file mode 100644
index 0000000..42243bc
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/http/request/alova.ts
@@ -0,0 +1,151 @@
+import type { uniappRequestAdapter } from '@alova/adapter-uniapp'
+import type { IResponse } from './types'
+import type { Language } from '@/store/lang'
+import AdapterUniapp from '@alova/adapter-uniapp'
+import { createAlova } from 'alova'
+import { createServerTokenAuthentication } from 'alova/client'
+import VueHook from 'alova/vue'
+import { getEnvBaseUrl } from '@/utils'
+import { toast } from '@/utils/toast'
+import { ContentTypeEnum, ResultEnum, ShowMessage } from './enum'
+
+// 璇█鏄犲皠, 鐢ㄤ簬璁剧疆 Accept-language 澶�
+const langMap: Record<Language, string> = {
+ zh_CN: 'zh-CN',
+ en: 'en-US',
+ zh_TW: 'zh-TW',
+ de: 'de',
+ vi: 'vi',
+ pt_BR: 'pt-BR',
+}
+
+/**
+ * 鍒涘缓璇锋眰瀹炰緥
+ */
+const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication<
+ typeof VueHook,
+ typeof uniappRequestAdapter
+>({
+ refreshTokenOnError: {
+ isExpired: (error) => {
+ return error.response?.status === ResultEnum.Unauthorized
+ },
+ handler: async () => {
+ try {
+ // await authLogin();
+ }
+ catch (error) {
+ // 鍒囨崲鍒扮櫥褰曢〉
+ await uni.reLaunch({ url: '/pages/login/index' })
+ throw error
+ }
+ },
+ },
+})
+
+/**
+ * alova 璇锋眰瀹炰緥
+ */
+const alovaInstance = createAlova({
+ baseURL: getEnvBaseUrl(),
+ ...AdapterUniapp(),
+ timeout: 5000,
+ statesHook: VueHook,
+
+ beforeRequest: onAuthRequired((method) => {
+ // h5鍔ㄦ�佽幏鍙栨渶鏂扮殑 baseURL锛岀‘淇濅娇鐢ㄧ敤鎴疯缃殑鏈嶅姟鍣ㄥ湴鍧�
+ const currentBaseUrl = getEnvBaseUrl()
+ if (currentBaseUrl !== method.baseURL) {
+ method.baseURL = currentBaseUrl
+ }
+
+ // 妫�鏌ユ贩鍚堝唴瀹归敊璇紙HTTPS椤甸潰璇锋眰HTTP鎺ュ彛锛�
+ const currentProtocol = typeof window !== 'undefined' && window.location.protocol
+ const requestProtocol = method.baseURL?.split(':')[0]
+ const currentLang = langMap[uni.getStorageSync('app_language') as Language || 'zh_CN']
+ if (currentProtocol === 'https:' && requestProtocol === 'http') {
+ const errorMessage = '鏃犳硶閰嶇疆http鍗忚鍦板潃,璇锋鏌ユ帴鍙e湴鍧�'
+ throw new Error(errorMessage)
+ }
+
+ // 璁剧疆榛樿 Content-Type
+ method.config.headers = {
+ 'Content-Type': ContentTypeEnum.JSON,
+ 'Accept': 'application/json, text/plain, */*',
+ 'Accept-language': currentLang,
+ ...method.config.headers,
+ }
+
+ const { config } = method
+ const ignoreAuth = config.meta?.ignoreAuth
+ console.log('ignoreAuth===>', ignoreAuth)
+
+ // 澶勭悊璁よ瘉淇℃伅
+ if (!ignoreAuth) {
+ const authInfo = JSON.parse(uni.getStorageSync('token') || '{}')
+ if (!authInfo.token) {
+ // 璺宠浆鍒扮櫥褰曢〉
+ uni.reLaunch({ url: '/pages/login/index' })
+ throw new Error('[璇锋眰閿欒]锛氭湭鐧诲綍')
+ }
+ // 娣诲姞 Authorization 澶�
+ method.config.headers.Authorization = `Bearer ${authInfo.token}`
+ }
+
+ // 澶勭悊鍔ㄦ�佸煙鍚�
+ if (config.meta?.domain) {
+ method.baseURL = config.meta.domain
+ console.log('褰撳墠鍩熷悕', method.baseURL)
+ }
+ }),
+
+ responded: onResponseRefreshToken((response, method) => {
+ const { config } = method
+ const { requestType } = config
+ const {
+ statusCode,
+ data: rawData,
+ errMsg,
+ } = response as UniNamespace.RequestSuccessCallbackResult
+
+ console.log(response)
+
+ // 澶勭悊鐗规畩璇锋眰绫诲瀷锛堜笂浼�/涓嬭浇锛�
+ if (requestType === 'upload' || requestType === 'download') {
+ return response
+ }
+
+ // 澶勭悊 HTTP 鐘舵�佺爜閿欒
+ if (statusCode !== 200) {
+ const errorMessage = ShowMessage(statusCode) || `HTTP璇锋眰閿欒[${statusCode}]`
+ console.error('errorMessage===>', errorMessage)
+ toast.error(errorMessage)
+ throw new Error(`${errorMessage}锛�${errMsg}`)
+ }
+
+ // 澶勭悊涓氬姟閫昏緫閿欒
+ const { code, msg, data } = rawData as IResponse
+ if (code !== ResultEnum.Success) {
+ // 妫�鏌ユ槸鍚︿负token澶辨晥
+ if (code === ResultEnum.Unauthorized) {
+ // 娓呴櫎token骞惰烦杞埌鐧诲綍椤�
+ uni.removeStorageSync('token')
+ uni.reLaunch({ url: '/pages/login/index' })
+ throw new Error(`璇锋眰閿欒[${code}]锛�${msg}`)
+ }
+
+ if (config.meta?.isExposeError) {
+ return Promise.reject(msg)
+ }
+
+ if (config.meta?.toast !== false) {
+ toast.warning(msg)
+ }
+ throw new Error(`璇锋眰閿欒[${code}]锛�${msg}`)
+ }
+ // 澶勭悊鎴愬姛鍝嶅簲锛岃繑鍥炰笟鍔℃暟鎹�
+ return data
+ }),
+})
+
+export const http = alovaInstance
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/http/request/enum.ts b/xiaozhi-esp32-server/main/manager-mobile/src/http/request/enum.ts
new file mode 100644
index 0000000..048b561
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/http/request/enum.ts
@@ -0,0 +1,70 @@
+export enum ResultEnum {
+ Success = 0, // 鎴愬姛
+ Error = 400, // 閿欒
+ Unauthorized = 401, // 鏈巿鏉�
+ Forbidden = 403, // 绂佹璁块棶锛堝師涓篺orbidden锛�
+ NotFound = 404, // 鏈壘鍒帮紙鍘熶负notFound锛�
+ MethodNotAllowed = 405, // 鏂规硶涓嶅厑璁革紙鍘熶负methodNotAllowed锛�
+ RequestTimeout = 408, // 璇锋眰瓒呮椂锛堝師涓簉equestTimeout锛�
+ InternalServerError = 500, // 鏈嶅姟鍣ㄩ敊璇紙鍘熶负internalServerError锛�
+ NotImplemented = 501, // 鏈疄鐜帮紙鍘熶负notImplemented锛�
+ BadGateway = 502, // 缃戝叧閿欒锛堝師涓篵adGateway锛�
+ ServiceUnavailable = 503, // 鏈嶅姟涓嶅彲鐢紙鍘熶负serviceUnavailable锛�
+ GatewayTimeout = 504, // 缃戝叧瓒呮椂锛堝師涓篻atewayTimeout锛�
+ HttpVersionNotSupported = 505, // HTTP鐗堟湰涓嶆敮鎸侊紙鍘熶负httpVersionNotSupported锛�
+ MixedContent = 600, // 娣峰悎鍐呭閿欒锛圚TTPS椤甸潰璇锋眰HTTP鎺ュ彛锛�
+}
+export enum ContentTypeEnum {
+ JSON = 'application/json;charset=UTF-8',
+ FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8',
+ FORM_DATA = 'multipart/form-data;charset=UTF-8',
+}
+/**
+ * 鏍规嵁鐘舵�佺爜锛岀敓鎴愬搴旂殑閿欒淇℃伅
+ * @param {number|string} status 鐘舵�佺爜
+ * @returns {string} 閿欒淇℃伅
+ */
+export function ShowMessage(status: number | string): string {
+ let message: string
+ switch (status) {
+ case 400:
+ message = '璇锋眰閿欒(400)'
+ break
+ case 401:
+ message = '鏈巿鏉冿紝璇烽噸鏂扮櫥褰�(401)'
+ break
+ case 403:
+ message = '鎷掔粷璁块棶(403)'
+ break
+ case 404:
+ message = '璇锋眰鍑洪敊(404)'
+ break
+ case 408:
+ message = '璇锋眰瓒呮椂(408)'
+ break
+ case 500:
+ message = '鏈嶅姟鍣ㄩ敊璇�(500)'
+ break
+ case 501:
+ message = '鏈嶅姟鏈疄鐜�(501)'
+ break
+ case 502:
+ message = '缃戠粶閿欒(502)'
+ break
+ case 503:
+ message = '鏈嶅姟涓嶅彲鐢�(503)'
+ break
+ case 504:
+ message = '缃戠粶瓒呮椂(504)'
+ break
+ case 505:
+ message = 'HTTP鐗堟湰涓嶅彈鏀寔(505)'
+ break
+ case 600:
+ message = '娣峰悎鍐呭閿欒(600)'
+ break
+ default:
+ message = `杩炴帴鍑洪敊(${status})!`
+ }
+ return `${message}锛岃妫�鏌ョ綉缁滄垨鑱旂郴绠$悊鍛橈紒`
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/http/request/types.ts b/xiaozhi-esp32-server/main/manager-mobile/src/http/request/types.ts
new file mode 100644
index 0000000..824aa4f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/http/request/types.ts
@@ -0,0 +1,22 @@
+// 閫氱敤鍝嶅簲鏍煎紡
+export interface IResponse<T = any> {
+ code: number | string
+ data: T
+ msg: string
+ status: string | number
+}
+
+// 鍒嗛〉璇锋眰鍙傛暟
+export interface PageParams {
+ page: number
+ pageSize: number
+ [key: string]: any
+}
+
+// 鍒嗛〉鍝嶅簲鏁版嵁
+export interface PageResult<T> {
+ list: T[]
+ total: number
+ page: number
+ pageSize: number
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/i18n/de.ts b/xiaozhi-esp32-server/main/manager-mobile/src/i18n/de.ts
new file mode 100644
index 0000000..3a95557
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/i18n/de.ts
@@ -0,0 +1,597 @@
+// Deutsch Sprachpaket
+export default {
+ // TabBar
+ 'tabBar.home': 'Startseite',
+ 'tabBar.deviceConfig': 'Netzwerkkonfig',
+ 'tabBar.settings': 'System',
+ // Einstellungsseitentitel
+ 'settings.title': 'Einstellungen',
+ // Anmeldeseite
+ 'login.pageTitle': 'Anmelden',
+ 'login.navigationTitle': 'Anmelden',
+ 'login.fetchConfigError': 'Konfiguration konnte nicht abgerufen werden:',
+ 'login.selectLanguage': 'Sprache ausw盲hlen',
+ 'login.selectLanguageTip': 'De',
+ 'login.welcomeBack': 'Willkommen zur眉ck',
+ 'login.pleaseLogin': 'Bitte melden Sie sich an',
+ 'login.enterUsername': 'Bitte Benutzernamen eingeben',
+ 'login.enterPassword': 'Bitte Passwort eingeben',
+ 'login.enterCaptcha': 'Bitte Best盲tigungscode eingeben',
+ 'login.loginButton': 'Anmelden',
+ 'login.loggingIn': 'Wird angemeldet...',
+ 'login.noAccount': 'Registrieren',
+ 'login.enterPhone': 'Bitte Handynummer eingeben',
+ 'login.selectCountry': 'Land/Region ausw盲hlen',
+ 'login.confirm': 'Best盲tigen',
+ 'login.serverSetting': 'Server-Einstellungen',
+ 'login.requiredUsername': 'Benutzername darf nicht leer sein',
+ 'login.requiredPassword': 'Passwort darf nicht leer sein',
+ 'login.requiredCaptcha': 'Best盲tigungscode darf nicht leer sein',
+ 'login.requiredMobile': 'Bitte g眉ltige Handynummer eingeben',
+ 'login.captchaError': 'Grafischer Best盲tigungscode Fehler',
+ 'login.forgotPassword': 'Passwort vergessen',
+ 'login.userAgreement': 'Nutzungsbedingungen',
+ 'login.privacyPolicy': 'Datenschutzrichtlinie',
+
+ // Registrierungsseite
+ 'register.pageTitle': 'Registrieren',
+ 'register.createAccount': 'Konto erstellen',
+ 'register.enterUsername': 'Bitte Benutzernamen eingeben',
+ 'register.enterPassword': 'Bitte Passwort eingeben',
+ 'register.confirmPassword': 'Bitte Passwort best盲tigen',
+ 'register.enterPhone': 'Bitte Handynummer eingeben',
+ 'register.enterCode': 'Bitte Best盲tigungscode eingeben',
+ 'register.getCode': 'Code erhalten',
+ 'register.agreeTerms': 'Ich habe gelesen und stimme zu',
+ 'register.terms': 'Nutzungsbedingungen',
+ 'register.privacy': 'Datenschutzrichtlinie',
+ 'register.registerButton': 'Registrieren',
+ 'register.registering': 'Wird registriert...',
+ 'register.haveAccount': 'Bereits ein Konto?',
+ 'register.loginNow': 'Jetzt anmelden',
+ 'register.selectCountry': 'Land/Region ausw盲hlen',
+ 'register.confirm': 'Best盲tigen',
+ 'register.captchaSendSuccess': 'Best盲tigungscode erfolgreich gesendet',
+
+ // Startseite
+ 'home.pageTitle': 'Startseite',
+ 'home.createAgent': 'Agent erstellen',
+ 'home.agentName': 'Agent',
+ 'home.modelInfo': 'Modell-Info',
+ 'home.lastActive': 'Zuletzt aktiv',
+ 'home.greeting': 'Hallo Jarvis',
+ 'home.subtitle': 'Lassen Sie uns',
+ 'home.wonderfulDay': 'einen wunderbaren Tag haben!',
+ 'home.emptyState': 'Keine Agenten verf眉gbar',
+ 'home.deviceManagement': 'Ger盲teverwaltung',
+ 'home.lastConversation': 'Letzte Konversation:',
+ 'home.delete': 'L枚schen',
+ 'home.createFirstAgent': 'Klicken Sie auf die + Schaltfl盲che unten rechts, um Ihren ersten Agenten zu erstellen',
+ 'home.dialogTitle': 'Agent erstellen',
+ 'home.inputPlaceholder': 'z.B. Kundenservice-Assistent, Sprachassistent, Wissens-F&A',
+ 'home.createError': 'Der Name muss zwischen 1 und 64 Zeichen lang sein.',
+ 'home.createNow': 'Jetzt erstellen',
+ 'home.deleteConfirmTitle': 'Agent l枚schen',
+ 'home.confirmDeleteAgent': 'Das L枚schen kann nicht r眉ckg盲ngig gemacht werden. Geben Sie den Agentennamen "{agentName}" ein, um zu best盲tigen.',
+ 'home.deleteAgentNamePlaceholder': 'Agentennamen eingeben',
+ 'home.deleteAgentNameMismatch': 'Der Agentenname stimmt nicht 眉berein',
+ 'home.deleteAgentPasteForbidden': 'Bitte geben Sie den Agentennamen manuell ein. Einf眉gen ist nicht erlaubt.',
+ 'home.deleteAgentMissingInfo': 'Agenteninformationen konnten nicht best盲tigt werden. Bitte aktualisieren und erneut versuchen.',
+ 'home.deleteAgentSuccess': 'Agent erfolgreich gel枚scht',
+ 'home.justNow': 'Gerade eben',
+ 'home.minutesAgo': 'Minuten her',
+ 'home.hoursAgo': 'Stunden her',
+ 'home.daysAgo': 'Tage her',
+ 'home.languageModel': 'LLM',
+ 'home.voiceModel': 'TTS',
+
+ // Agentenseite
+ 'agent.pageTitle': 'Agent',
+ 'agent.roleConfig': 'Rollenkonfiguration',
+ 'agent.deviceManagement': 'Ger盲teverwaltung',
+ 'agent.chatHistory': 'Chat-Verlauf',
+ 'agent.voiceprintManagement': 'Stimmabdruckverwaltung',
+ 'agent.editTitle': 'Agent bearbeiten',
+ 'agent.toolsTitle': 'Bearbeiten',
+ 'agent.voiceActivityDetection': 'Sprachaktivit盲tserkennung',
+ 'agent.speechRecognition': 'Spracherkennung',
+ 'agent.largeLanguageModel': 'Gro脽es Sprachmodell',
+ 'agent.save': 'Speichern',
+ 'agent.cancel': 'Abbrechen',
+ // Agenten-Bearbeitungsseite
+ 'agent.basicInfo': 'Grundinformationen',
+ 'agent.agentName': 'Agentenname',
+ 'agent.inputAgentName': 'Bitte Agenten-Namen eingeben',
+ 'agent.roleMode': 'Rollenmodus',
+ 'agent.agentTag': 'Smart Body-Etiketten',
+ 'agent.addAgentTag': 'Tags hinzuf眉gen',
+ 'agent.inputAgentTag': 'Bitte Smart Body-Etiketten eingeben',
+ 'agent.contextProvider': 'Kontext',
+ 'agent.contextProviderSuccess': '{count} Quellen erfolgreich hinzugef眉gt.',
+ 'agent.contextProviderDocLink': 'Wie man Kontextquellen bereitstellt',
+ 'agent.editContextProvider': 'Quelle bearbeiten',
+ 'agent.roleDescription': 'Rollenbeschreibung',
+ 'agent.inputRoleDescription': 'Bitte Rollenbeschreibung eingeben',
+ 'agent.modelConfig': 'Modellkonfiguration',
+ 'agent.vad': 'Sprachaktivit盲tserkennung',
+ 'agent.asr': 'Spracherkennung',
+ 'agent.llm': 'Hauptsprachenmodell',
+ 'agent.slm': 'Kleine Parametermodelle',
+ 'agent.vllm': 'Vision-Sprachmodell',
+ 'agent.intent': 'Absichtserkennung',
+ 'agent.memory': 'Speicher',
+ 'agent.voiceSettings': 'Stimmeinstellungen',
+ 'agent.tts': 'Text-zu-Sprache',
+ 'agent.voiceprint': 'Agenten-Stimme',
+ 'agent.plugins': 'Plugins',
+ 'agent.editFunctions': 'Bearbeiten',
+ 'agent.historyMemory': 'Verlaufsspeicher',
+ 'agent.memoryContent': 'Speicherinhalt',
+ 'agent.saving': 'Wird gespeichert...',
+ 'agent.saveSuccess': 'Erfolgreich gespeichert',
+ 'agent.saveFail': 'Speichern fehlgeschlagen',
+ 'agent.ttsOptionsLoadFailed': 'Die Sprachoptionen konnten nicht geladen werden. Die vorherigen Einstellungen wurden beibehalten.',
+ 'agent.loadFail': 'Laden fehlgeschlagen',
+ 'agent.pleaseInputAgentName': 'Bitte Agenten-Namen eingeben',
+ 'agent.pleaseInputRoleDescription': 'Bitte Rollenbeschreibung eingeben',
+ 'agent.pleaseSelect': 'Bitte ausw盲hlen',
+ 'agent.reportText': 'Text melden',
+ 'agent.reportTextVoice': 'Text + Sprache melden',
+ 'agent.reportMode': 'Meldemodus',
+ 'agent.language': 'Unterhaltungssprache',
+ 'agent.languageConfig': 'Sprachgeschwindigkeit & Tonh枚he',
+ 'agent.ttsVolume': 'Lautst盲rke',
+ 'agent.ttsRate': 'Sprechtempo',
+ 'agent.ttsPitch': 'Tonh枚he',
+ 'agent.volumeHint': '-100=min, 0=Standard, 100=max',
+ 'agent.speedHint': '-100=langsamst, 0=Standard, 100=schnellst',
+ 'agent.pitchHint': '-100=tiefst, 0=Standard, 100=h枚chst',
+
+ // Agent-Versionsverlauf
+ 'agentSnapshot.title': 'Versionsverlauf',
+ 'agentSnapshot.empty': 'Noch keine Versionen',
+ 'agentSnapshot.emptyTip': 'Gespeicherte Konfigurationen erscheinen hier',
+ 'agentSnapshot.version': 'Version',
+ 'agentSnapshot.createdAt': 'Gespeichert am',
+ 'agentSnapshot.source': 'Quelle',
+ 'agentSnapshot.changedFields': '脛nderungen',
+ 'agentSnapshot.view': 'Ansehen',
+ 'agentSnapshot.restore': 'Wiederherstellen',
+ 'agentSnapshot.delete': 'L枚schen',
+ 'agentSnapshot.loadMore': 'Mehr laden',
+ 'agentSnapshot.detailTitle': '脛nderungsdetails',
+ 'agentSnapshot.restorePreviewTitle': 'Wiederherstellungsvorschau',
+ 'agentSnapshot.confirmRestore': 'Wiederherstellung best盲tigen',
+ 'agentSnapshot.currentVersion': 'Neuester Snapshot',
+ 'agentSnapshot.beforeChange': 'Vorher',
+ 'agentSnapshot.afterChange': 'Nachher',
+ 'agentSnapshot.beforeRestore': 'Vor der Wiederherstellung',
+ 'agentSnapshot.afterRestore': 'Nach der Wiederherstellung',
+ 'agentSnapshot.configValue': 'Konfigurationswert',
+ 'agentSnapshot.emptyValue': 'Keine',
+ 'agentSnapshot.secretRedacted': 'Geheimnis verborgen',
+ 'agentSnapshot.redactedValueChanged': 'Der Wert ist ausgeblendet, wurde aber ge盲ndert',
+ 'agentSnapshot.noChangedContent': 'Keine anzeigbaren 脛nderungen',
+ 'agentSnapshot.recordedChange': 'Bei der Aufzeichnung ge盲ndert',
+ 'agentSnapshot.noRestoreNeeded': 'Die aktuelle Konfiguration entspricht bereits dieser Version',
+ 'agentSnapshot.unsavedChangesTitle': 'Nicht gespeicherte 脛nderungen',
+ 'agentSnapshot.unsavedChangesWarning': 'Beim Fortfahren werden die nicht gespeicherten 脛nderungen auf dieser Seite verworfen.',
+ 'agentSnapshot.discardAndRestore': 'Verwerfen und wiederherstellen',
+ 'agentSnapshot.restoreConfirm': 'Version #{version} wiederherstellen? Die aktuelle Konfiguration bleibt im Verlauf erhalten.',
+ 'agentSnapshot.restoreMemoryWarning': 'Beim Wiederherstellen einer Version ohne Speicher wird der Chatverlauf dieses Agenten gel枚scht. Bitte Risiko best盲tigen.',
+ 'agentSnapshot.restoreChatHistoryDestructiveWarning': 'Diese Wiederherstellung l枚scht den vorhandenen Chatverlauf dieses Agenten dauerhaft. Der Chatverlauf ist nicht in Konfigurations-Snapshots enthalten und kann nicht 眉ber den Versionsverlauf wiederhergestellt werden.',
+ 'agentSnapshot.restoreSuccess': 'Version wurde wiederhergestellt',
+ 'agentSnapshot.restoreFailed': 'Version konnte nicht wiederhergestellt werden',
+ 'agentSnapshot.reloadAfterRestorePending': 'Die wiederhergestellte Konfiguration und die Tags werden neu geladen. Speichern ist bis zum Abschluss deaktiviert.',
+ 'agentSnapshot.reloadAfterRestoreFailed': 'Die wiederhergestellte Konfiguration oder die Tags konnten nicht neu geladen werden. Speichern bleibt deaktiviert, damit veraltete Daten die Wiederherstellung nicht 眉berschreiben. Bitte erneut versuchen.',
+ 'agentSnapshot.retryReload': 'Neu laden',
+ 'agentSnapshot.mutationBusy': 'Die Konfiguration wird gespeichert oder neu geladen. Bitte warten Sie, bevor Sie den Versionsverlauf 枚ffnen oder eine Wiederherstellung starten.',
+ 'agentSnapshot.deleteConfirm': 'Version #{version} l枚schen? Dies kann nicht r眉ckg盲ngig gemacht werden.',
+ 'agentSnapshot.deleteSuccess': 'Version gel枚scht',
+ 'agentSnapshot.deleteFailed': 'Version konnte nicht gel枚scht werden',
+ 'agentSnapshot.fetchFailed': 'Versionsverlauf konnte nicht abgerufen werden',
+ 'agentSnapshot.detailFailed': 'Versionsdetails konnten nicht abgerufen werden',
+ 'agentSnapshot.correctWordCount': '{count} Ersatzw枚rter',
+ 'agentSnapshot.source.config': 'Konfiguration gespeichert',
+ 'agentSnapshot.source.current': 'Aktuelle Konfiguration',
+ 'agentSnapshot.source.restore': 'Wiederhergestellt',
+ 'agentSnapshot.source.initial': 'Initialversion',
+ 'agentSnapshot.field.initial': 'Initialer Snapshot',
+ 'agentSnapshot.field.agentCode': 'Agent-Code',
+ 'agentSnapshot.field.agentName': 'Spitzname',
+ 'agentSnapshot.field.asrModelId': 'Spracherkennung',
+ 'agentSnapshot.field.vadModelId': 'Sprachaktivit盲tserkennung',
+ 'agentSnapshot.field.llmModelId': 'Hauptsprachmodell',
+ 'agentSnapshot.field.slmModelId': 'Kleines Sprachmodell',
+ 'agentSnapshot.field.vllmModelId': 'Vision-Modell',
+ 'agentSnapshot.field.ttsModelId': 'Text-zu-Sprache',
+ 'agentSnapshot.field.ttsVoiceId': 'Stimme',
+ 'agentSnapshot.field.ttsLanguage': 'Sprache',
+ 'agentSnapshot.field.ttsVolume': 'Lautst盲rke',
+ 'agentSnapshot.field.ttsRate': 'Geschwindigkeit',
+ 'agentSnapshot.field.ttsPitch': 'Tonh枚he',
+ 'agentSnapshot.field.memModelId': 'Speichermodus',
+ 'agentSnapshot.field.intentModelId': 'Absichtserkennung',
+ 'agentSnapshot.field.chatHistoryConf': 'Chatverlauf-Konfiguration',
+ 'agentSnapshot.field.systemPrompt': 'Rollenbeschreibung',
+ 'agentSnapshot.field.summaryMemory': 'Speicher',
+ 'agentSnapshot.field.langCode': 'Sprachcode',
+ 'agentSnapshot.field.language': 'Interaktionssprache',
+ 'agentSnapshot.field.sort': 'Sortierung',
+ 'agentSnapshot.field.functions': 'Plugins',
+ 'agentSnapshot.field.contextProviders': 'Kontextquellen',
+ 'agentSnapshot.field.correctWordFileIds': 'Ersatzw枚rter',
+ 'agentSnapshot.field.tagNames': 'Agent-Labels',
+ 'agentSnapshot.chatHistoryConf.none': 'Chatverlauf nicht aufzeichnen',
+ 'agentSnapshot.chatHistoryConf.text': 'Text melden',
+ 'agentSnapshot.chatHistoryConf.textVoice': 'Text und Sprache melden',
+ 'agentSnapshot.model.Memory_nomem': 'Kein Speicher',
+ 'agentSnapshot.model.Memory_mem_local_short': 'Lokaler Kurzzeitspeicher',
+ 'agentSnapshot.model.Memory_mem0ai': 'Mem0AI-Speicher',
+ 'agentSnapshot.model.Memory_mem_report_only': 'Nur melden',
+ 'agentSnapshot.model.Intent_nointent': 'Keine Absichtserkennung',
+ 'agentSnapshot.model.Intent_intent_llm': 'Externe LLM-Absichtserkennung',
+ 'agentSnapshot.model.Intent_function_call': 'LLM-Funktionsaufruf',
+
+ // Context provider dialog related
+ 'contextProviderDialog.title': 'Quelle bearbeiten',
+ 'contextProviderDialog.noContextApi': 'Keine Kontext-API',
+ 'contextProviderDialog.add': 'Hinzuf眉gen',
+ 'contextProviderDialog.apiUrl': 'API-URL',
+ 'contextProviderDialog.apiUrlPlaceholder': 'http://api.example.com/data',
+ 'contextProviderDialog.requestHeaders': 'Anfrage-Header',
+ 'contextProviderDialog.headerKeyPlaceholder': 'Schl眉ssel',
+ 'contextProviderDialog.headerValuePlaceholder': 'Wert',
+ 'contextProviderDialog.noHeaders': 'Keine Headers',
+ 'contextProviderDialog.addHeader': 'Header hinzuf眉gen',
+ 'contextProviderDialog.cancel': 'Abbrechen',
+ 'contextProviderDialog.confirm': 'Best盲tigen',
+
+ // Manual add device dialog related
+ 'manualAddDeviceDialog.title': 'Manuell Ger盲t hinzuf眉gen',
+ 'manualAddDeviceDialog.deviceType': 'Ger盲tetyp',
+ 'manualAddDeviceDialog.deviceTypePlaceholder': 'Bitte Ger盲tetyp ausw盲hlen',
+ 'manualAddDeviceDialog.firmwareVersion': 'Firmware-Version',
+ 'manualAddDeviceDialog.firmwareVersionPlaceholder': 'Bitte Firmware-Version eingeben',
+ 'manualAddDeviceDialog.macAddress': 'Mac-Adresse',
+ 'manualAddDeviceDialog.macAddressPlaceholder': 'Bitte Mac-Adresse eingeben',
+ 'manualAddDeviceDialog.confirm': 'Best盲tigen',
+ 'manualAddDeviceDialog.cancel': 'Abbrechen',
+ 'manualAddDeviceDialog.requiredMacAddress': 'Bitte Mac-Adresse eingeben',
+ 'manualAddDeviceDialog.invalidMacAddress': 'Bitte korrektes Mac-Adressformat eingeben, z.B.: 00:1A:2B:3C:4D:5E',
+ 'manualAddDeviceDialog.requiredDeviceType': 'Bitte Ger盲tetyp ausw盲hlen',
+ 'manualAddDeviceDialog.requiredFirmwareVersion': 'Bitte Firmware-Version eingeben',
+ 'manualAddDeviceDialog.getFirmwareTypeFailed': 'Firmware-Typ konnte nicht abgerufen werden',
+ 'manualAddDeviceDialog.addSuccess': 'Ger盲t erfolgreich hinzugef眉gt',
+ 'manualAddDeviceDialog.addFailed': 'Hinzuf眉gen fehlgeschlagen',
+ 'manualAddDeviceDialog.bindWithCode': 'Mit 6-stelligem Code binden',
+
+ // Chat-Verlauf Seite
+ 'chatHistory.getChatSessions': 'Chat-Sitzungsliste abrufen',
+ 'chatHistory.noSelectedAgent': 'Kein Agent ausgew盲hlt',
+ 'chatHistory.getChatSessionsFailed': 'Chat-Sitzungsliste konnte nicht abgerufen werden:',
+ 'chatHistory.unknownTime': 'Unbekannte Zeit',
+ 'chatHistory.justNow': 'Gerade eben',
+ 'chatHistory.minutesAgo': 'Vor {minutes} Minuten',
+ 'chatHistory.hoursAgo': 'Vor {hours} Stunden',
+ 'chatHistory.daysAgo': 'Vor {days} Tagen',
+ 'chatHistory.conversationRecord': 'Konversationsaufzeichnung',
+ 'chatHistory.totalChats': 'Insgesamt {count} Chats',
+ 'chatHistory.loading': 'L盲dt...',
+ 'chatHistory.noMoreData': 'Keine weiteren Daten',
+ 'chatHistory.noChatRecords': 'Keine Chat-Aufzeichnungen',
+ 'chatHistory.chatRecordsDescription': 'Konversationsaufzeichnungen mit Agenten werden hier angezeigt',
+ // Chat-Verlauf Detailseite
+ 'chatHistory.pageTitle': 'Chat-Details',
+ 'chatHistory.assistantName': 'Intelligenter Assistent',
+ 'chatHistory.userName': 'Benutzer',
+ 'chatHistory.aiAssistantName': 'KI-Assistent',
+ 'chatHistory.loadFailed': 'Chat-Verlauf konnte nicht geladen werden',
+ 'chatHistory.parameterError': 'Seitenparameter Fehler',
+ 'chatHistory.invalidAudioId': 'Ung眉ltige Audio-ID',
+ 'chatHistory.audioPlayFailed': 'Audio-Wiedergabe fehlgeschlagen',
+ 'chatHistory.playAudioFailed': 'Audio konnte nicht abgespielt werden',
+
+ // Ger盲teverwaltungsseite
+ 'device.pageTitle': 'Ger盲teverwaltung',
+ 'device.noDevices': 'Keine Ger盲te verf眉gbar',
+ 'device.macAddress': 'MAC-Adresse',
+ 'device.firmwareVersion': 'Firmware-Version',
+ 'device.lastConnected': 'Letzte Konversation',
+ 'device.otaUpdate': 'OTA-Update',
+ 'device.unbind': 'Entbinden',
+ 'device.confirmUnbind': 'Best盲tigen',
+ 'device.bindDevice': 'Neues Ger盲t binden',
+ 'device.deviceType': 'Ger盲tetyp',
+ 'device.loading': 'L盲dt...',
+ 'device.neverConnected': 'Nie verbunden',
+ 'device.justNow': 'Gerade eben',
+ 'device.minutesAgo': 'Vor {minutes} Minuten',
+ 'device.hoursAgo': 'Vor {hours} Stunden',
+ 'device.daysAgo': 'Vor {days} Tagen',
+ 'device.otaAutoUpdateEnabled': 'OTA Auto-Update aktiviert',
+ 'device.otaAutoUpdateDisabled': 'OTA Auto-Update deaktiviert',
+ 'device.operationFailed': 'Operation fehlgeschlagen, bitte erneut versuchen',
+ 'device.deviceUnbound': 'Ger盲t entbunden',
+ 'device.unbindFailed': 'Entbinden fehlgeschlagen, bitte erneut versuchen',
+ 'device.unbindDevice': 'Ger盲t entbinden',
+ 'device.confirmUnbindDevice': 'Sind Sie sicher, dass Sie Ger盲t "{macAddress}" entbinden m枚chten?',
+ 'device.cancel': 'Abbrechen',
+ 'device.noDevice': 'Kein Ger盲t',
+ 'device.pleaseSelectAgent': 'Bitte w盲hlen Sie zuerst einen Agenten aus',
+ 'device.deviceBindSuccess': 'Ger盲t erfolgreich gebunden!',
+ 'device.bindFailed': 'Binden fehlgeschlagen, bitte pr眉fen Sie ob der Best盲tigungscode korrekt ist',
+ 'device.enterDeviceCode': 'Bitte Ger盲te-Best盲tigungscode eingeben',
+ 'device.bindNow': 'Jetzt binden',
+ 'device.lastConnection': 'Letzte Verbindung',
+ 'device.clickToBindFirstDevice': 'Klicken Sie auf die + Schaltfl盲che unten rechts, um Ihr erstes Ger盲t zu binden',
+
+ // Allgemein
+ 'common.success': 'Erfolg',
+ 'common.fail': 'Fehlgeschlagen',
+ 'common.loading': 'L盲dt...',
+ 'common.confirm': 'Best盲tigen',
+ 'common.cancel': 'Abbrechen',
+ 'common.delete': 'L枚schen',
+ 'common.edit': 'Bearbeiten',
+ 'common.add': 'Hinzuf眉gen',
+ 'common.pleaseSelect': 'Bitte ausw盲hlen',
+ 'common.unknownError': 'Unbekannter Fehler',
+ 'common.networkError': 'Netzwerkfehler',
+
+ // Passwort-Wiederherstellungsseite
+ 'retrievePassword.title': 'Passwort zur眉cksetzen',
+ 'retrievePassword.subtitle': 'Stellen Sie Ihr Kontopasswort 眉ber Handynummer wieder her',
+ 'retrievePassword.mobileRequired': 'Bitte Handynummer eingeben',
+ 'retrievePassword.inputCorrectMobile': 'Bitte g眉ltige Handynummer eingeben',
+ 'retrievePassword.captchaRequired': 'Bitte grafischen Best盲tigungscode eingeben',
+ 'retrievePassword.mobileCaptchaRequired': 'Bitte SMS-Best盲tigungscode eingeben',
+ 'retrievePassword.newPasswordRequired': 'Bitte neues Passwort eingeben',
+ 'retrievePassword.confirmNewPasswordRequired': 'Bitte neues Passwort best盲tigen',
+ 'retrievePassword.passwordsNotMatch': 'Passw枚rter stimmen nicht 眉berein',
+ 'retrievePassword.mobilePlaceholder': 'Bitte Handynummer eingeben',
+ 'retrievePassword.captchaPlaceholder': 'Bitte grafischen Best盲tigungscode eingeben',
+ 'retrievePassword.mobileCaptchaPlaceholder': 'Bitte SMS-Best盲tigungscode eingeben',
+ 'retrievePassword.newPasswordPlaceholder': 'Bitte neues Passwort eingeben',
+ 'retrievePassword.confirmNewPasswordPlaceholder': 'Bitte neues Passwort best盲tigen',
+ 'retrievePassword.getMobileCaptcha': 'Code erhalten',
+ 'retrievePassword.captchaSendSuccess': 'Best盲tigungscode erfolgreich gesendet',
+ 'retrievePassword.passwordUpdateSuccess': 'Passwort erfolgreich zur眉ckgesetzt',
+ 'retrievePassword.resetButton': 'Passwort zur眉cksetzen',
+ 'retrievePassword.goToLogin': 'Zur眉ck zur Anmeldung',
+
+ // SM2-Verschl眉sselungsbezogene Fehlermeldungen
+ 'sm2.publicKeyNotConfigured': 'SM2 枚ffentlicher Schl眉ssel nicht konfiguriert, bitte Administrator kontaktieren',
+ 'sm2.encryptionFailed': 'Passwortverschl眉sselung fehlgeschlagen',
+ 'sm2.keyGenerationFailed': 'Schl眉sselpaar-Generierung fehlgeschlagen',
+ 'sm2.invalidPublicKey': 'Ung眉ltiges 枚ffentliches Schl眉sselformat',
+ 'sm2.encryptionError': 'Fehler bei der Verschl眉sselung aufgetreten',
+ 'sm2.publicKeyRetry': '脰ffentlichen Schl眉ssel erneut abrufen...',
+ 'sm2.publicKeyRetryFailed': 'Wiederholter Abruf des 枚ffentlichen Schl眉ssels fehlgeschlagen',
+
+ // Stimmabdruckseite
+ 'voiceprint.noSelectedAgent': 'Kein Agent ausgew盲hlt',
+ 'voiceprint.pleaseSelectAgent': 'Bitte w盲hlen Sie zuerst einen Agenten aus',
+ 'voiceprint.fetchHistoryFailed': 'Chat-Verlauf konnte nicht abgerufen werden',
+ 'voiceprint.clickToSelectVector': 'Klicken zum Ausw盲hlen des Stimmabdruck-Vektors',
+ 'voiceprint.pleaseInputName': 'Bitte Namen eingeben',
+ 'voiceprint.pleaseSelectVector': 'Bitte Stimmabdruck-Vektor ausw盲hlen',
+ 'voiceprint.addSuccess': 'Erfolgreich hinzugef眉gt',
+ 'voiceprint.addFailed': 'Hinzuf眉gen des Sprechers fehlgeschlagen',
+ 'voiceprint.editSuccess': 'Erfolgreich bearbeitet',
+ 'voiceprint.editFailed': 'Bearbeiten des Sprechers fehlgeschlagen',
+ 'voiceprint.deleteConfirmMsg': 'Sind Sie sicher, dass Sie diesen Sprecher l枚schen m枚chten?',
+ 'voiceprint.deleteConfirmTitle': 'L枚schen best盲tigen',
+ 'voiceprint.deleteSuccess': 'Erfolgreich gel枚scht',
+ 'voiceprint.loading': 'L盲dt...',
+ 'voiceprint.delete': 'L枚schen',
+ 'voiceprint.emptyTitle': 'Keine Stimmabdruck-Daten',
+ 'voiceprint.emptyDesc': 'Klicken Sie auf die + Schaltfl盲che unten rechts, um Ihren ersten Sprecher hinzuzuf眉gen',
+ 'voiceprint.addSpeaker': 'Sprecher hinzuf眉gen',
+ 'voiceprint.voiceVector': 'Stimmabdruck-Vektor',
+ 'voiceprint.name': 'Name',
+ 'voiceprint.description': 'Beschreibung',
+ 'voiceprint.pleaseInputDescription': 'Bitte Beschreibung eingeben',
+ 'voiceprint.cancel': 'Abbrechen',
+ 'voiceprint.save': 'Speichern',
+ 'voiceprint.editSpeaker': 'Sprecher bearbeiten',
+ 'voiceprint.selectVector': 'Stimmabdruck-Vektor ausw盲hlen',
+ 'voiceprint.voiceprintInterfaceNotConfigured': 'Stimmabdruck-Schnittstelle nicht konfiguriert',
+
+ // Einstellungsseite
+ 'settings.pageTitle': 'Einstellungen',
+ 'settings.navigationTitle': 'Einstellungen',
+ 'settings.networkSettings': 'Netzwerkeinstellungen',
+ 'settings.serverApiUrl': 'Server-API-URL',
+ 'settings.validServerUrl': 'Bitte g眉ltige Serveradresse eingeben (beginnt mit http oder https und endet mit /xiaozhi)',
+ 'settings.saveSettings': 'Einstellungen speichern',
+ 'settings.resetDefault': 'Auf Standard zur眉cksetzen',
+ 'settings.restartApp': 'App neu starten',
+ 'settings.restartNow': 'Jetzt neu starten',
+ 'settings.restartLater': 'Sp盲ter',
+ // 脺ber uns
+ 'settings.aboutApp': '脺ber XiaoZhi Konsole',
+ 'settings.aboutContent': 'XiaoZhi Konsole\n\nEine plattform眉bergreifende mobile Management-App, erstellt mit Vue.js 3 + uni-app, bietet Ger盲teverwaltung, Agentenkonfiguration und andere Funktionen f眉r xiaozhi ESP32 Smart Hardware.\n\n漏 2025 xiaozhi-esp32-server {version}',
+ 'settings.restartSuccess': 'Gespeichert, Sie k枚nnen die App sp盲ter manuell neu starten',
+ 'settings.serverUrlSavedAndCacheCleared': 'Server-URL gespeichert und Cache geleert',
+ 'settings.resetToDefaultAndCacheCleared': 'Auf Standard zur眉ckgesetzt und Cache geleert',
+ 'settings.resetSuccess': 'Zur眉cksetzen erfolgreich',
+ 'settings.enterServerUrl': 'Bitte Server-URL eingeben',
+ 'settings.clearCacheFailed': 'Cache konnte nicht geleert werden',
+ 'settings.cacheManagement': 'Cache-Verwaltung',
+ 'settings.totalCacheSize': 'Gesamte Cache-Gr枚脽e',
+ 'settings.appDataSize': 'Gesamte App-Datengr枚脽e',
+ 'settings.cacheClear': 'Cache leeren',
+ 'settings.clearAllCache': 'Alle Cache-Daten l枚schen',
+ 'settings.clearCache': 'Cache leeren',
+ 'settings.modifyWillClearCache': '脛nderungen werden Cache leeren',
+ 'settings.appInfo': 'App-Info',
+ 'settings.aboutUs': '脺ber uns',
+ 'settings.appVersion': 'App-Version & Team-Info',
+ 'settings.confirmClear': 'L枚schen best盲tigen',
+ 'settings.confirmClearMessage': 'Sind Sie sicher, dass Sie den gesamten Cache l枚schen m枚chten? Dies l枚scht alle Daten einschlie脽lich Anmeldestatus und erfordert erneute Anmeldung.',
+ 'settings.cacheCleared': 'Cache erfolgreich geleert, leite zur Anmeldeseite weiter',
+ 'settings.languageSettings': 'Spracheinstellungen',
+ 'settings.language': 'Sprache',
+ 'settings.selectLanguage': 'Sprache ausw盲hlen',
+ 'settings.languageChanged': 'Sprache erfolgreich ge盲ndert',
+
+ // Nachrichten
+ 'message.loginSuccess': 'Anmeldung erfolgreich!',
+ 'message.loginFail': 'Anmeldung fehlgeschlagen',
+ 'message.registerSuccess': 'Registrierung erfolgreich',
+ 'message.registerFail': 'Registrierung fehlgeschlagen',
+ 'message.saveSuccess': 'Erfolgreich gespeichert',
+ 'message.saveFail': 'Speichern fehlgeschlagen',
+ 'message.deleteSuccess': 'Erfolgreich gel枚scht',
+ 'message.deleteFail': 'L枚schen fehlgeschlagen',
+ 'message.bindSuccess': 'Binden erfolgreich',
+ 'message.bindFail': 'Binden fehlgeschlagen',
+ 'message.unbindSuccess': 'Entbinden erfolgreich',
+ 'message.unbindFail': 'Entbinden fehlgeschlagen',
+ 'message.networkError': 'Netzwerkfehler, bitte Verbindung pr眉fen',
+ 'message.serverError': 'Serverfehler, bitte sp盲ter erneut versuchen',
+ 'message.invalidAddress': 'Die Adresse kann ung眉ltig sein. Bitte 眉berpr眉fen Sie, ob der Server gestartet ist oder ob die Netzwerkverbindung funktioniert. Es kann auch sein, dass die Anfrage aufgrund eines Problems mit dem HTTPS-Protokoll nicht gesendet werden kann.',
+ 'message.languageChanged': 'Sprache ge盲ndert',
+ 'message.passwordError': 'Konto oder Passwort Fehler',
+ 'message.phoneRegistered': 'Diese Handynummer wurde bereits registriert',
+
+ // Agenten-Werkzeuge Seite
+ 'agent.tools.pageTitle': 'Agenten-Werkzeuge',
+ 'agent.tools.unselected': 'Nicht ausgew盲hlt',
+ 'agent.tools.selected': 'Ausgew盲hlt',
+ 'agent.tools.noMorePlugins': 'Keine weiteren Plugins',
+ 'agent.tools.pleaseSelectPlugin': 'Bitte Plugin-Funktion ausw盲hlen',
+ 'agent.tools.builtInPlugins': 'Eingebaute Plugins',
+ 'agent.tools.mcpAccessPoint': 'MCP-Zugangspunkt',
+ 'agent.tools.copy': 'Kopieren',
+ 'agent.tools.noTools': 'Keine Werkzeuge verf眉gbar',
+ 'agent.tools.parameterConfig': 'Konfig',
+ 'agent.tools.noParamsNeeded': 'Keine Parameter ben枚tigt',
+ 'agent.tools.pleaseInput': 'Bitte eingeben',
+ 'agent.tools.inputOneItemPerLine': 'Ein Element pro Zeile eingeben',
+ 'agent.tools.pleaseInputValidJson': 'Bitte g眉ltiges JSON-Format eingeben',
+ 'agent.tools.enableFunction': 'Funktion aktivieren',
+ 'agent.tools.toggleFunction': 'Diese Funktion ein- oder ausschalten',
+ 'agent.tools.jsonFormatError': 'JSON-Format Fehler',
+ 'agent.tools.noMcpAddressToCopy': 'Keine MCP-Adresse zum Kopieren',
+ 'agent.tools.mcpAddressCopied': 'MCP-Adresse in Zwischenablage kopiert',
+ 'agent.tools.copyFailed': 'Kopieren fehlgeschlagen, bitte erneut versuchen',
+ 'agent.tools.defaultValue': 'Standardwert',
+ 'agent.tools.notSelected': 'Nicht ausgew盲hlt',
+ 'agent.tools.clickToConfigure': 'Klicken zum Konfigurieren',
+ 'agent.tools.mcpEndpoint': 'MCP-Endpunkt',
+ 'agent.tools.eachLineOneItem': 'Ein Element pro Zeile eingeben',
+
+ // Ger盲tekonfigurationsseite
+ 'deviceConfig.pageTitle': 'Ger盲tekonfiguration',
+ 'deviceConfig.wifiConfig': 'WiFi-Konfiguration',
+ 'deviceConfig.ultrasonicConfig': 'Ultraschall-Konfiguration',
+ 'deviceConfig.selectConfigMethod': 'Konfigurationsmethode ausw盲hlen',
+ 'deviceConfig.networkConfig': 'Netzwerkkonfiguration',
+ 'deviceConfig.selectedNetwork': 'Ausgew盲hltes Netzwerk',
+ 'deviceConfig.signal': 'Signal',
+ 'deviceConfig.openNetwork': 'Offenes Netzwerk',
+ 'deviceConfig.encryptedNetwork': 'Verschl眉sseltes Netzwerk',
+ 'deviceConfig.password': 'Passwort',
+ 'deviceConfig.pleaseEnterPassword': 'Bitte WiFi-Passwort eingeben',
+ 'deviceConfig.startConfig': 'Konfiguration starten',
+ 'deviceConfig.connectToXiaozhiHotspot': 'Bitte verbinden Sie sich zuerst mit dem xiaozhi Hotspot',
+ 'deviceConfig.detecting': 'Erkennen...',
+ 'deviceConfig.reDetect': 'Erneut erkennen',
+ 'deviceConfig.alreadyConnected': 'Mit xiaozhi Hotspot verbunden',
+ 'deviceConfig.refreshStatus': 'Status aktualisieren',
+ 'deviceConfig.wifiNetworks': 'WiFi-Netzwerke',
+ 'deviceConfig.selectWifiNetwork': 'WiFi-Netzwerk ausw盲hlen',
+ 'deviceConfig.refreshScan': 'Scan aktualisieren',
+ 'deviceConfig.noWifiNetworks': 'Keine WiFi-Netzwerke verf眉gbar',
+ 'deviceConfig.clickToRefreshScan': 'Bitte Scan aktualisieren klicken',
+ 'deviceConfig.signalStrong': 'Starkes Signal',
+ 'deviceConfig.signalGood': 'Gutes Signal',
+ 'deviceConfig.signalFair': 'Mittleres Signal',
+ 'deviceConfig.signalWeak': 'Schwaches Signal',
+ 'deviceConfig.channel': 'Kanal',
+ 'deviceConfig.about': 'ungef盲hr',
+ 'deviceConfig.seconds': 'Sekunden',
+ 'deviceConfig.generating': 'Wird generiert...',
+ 'deviceConfig.playing': 'Wird abgespielt...',
+ 'deviceConfig.generateAndPlaySoundWave': 'Schallwelle generieren und abspielen',
+ 'deviceConfig.playSoundWave': 'Schallwelle abspielen',
+ 'deviceConfig.stopPlaying': 'Abspielen stoppen',
+ 'deviceConfig.autoLoopPlaySoundWave': 'Schallwelle automatisch wiederholt abspielen',
+ 'deviceConfig.configAudioFile': 'Konfigurations-Audio-Datei',
+ 'deviceConfig.duration': 'Dauer',
+ 'deviceConfig.ultrasonicConfigInstructions': 'Ultraschall-Konfigurationsanleitung',
+ 'deviceConfig.ensureWifiNetworkSelectedAndPasswordEntered': 'Stellen Sie sicher, dass WiFi-Netzwerk ausgew盲hlt und Passwort eingegeben ist',
+ 'deviceConfig.clickGenerateAndPlaySoundWave': 'Klicken Sie auf Schallwelle generieren und abspielen, das System kodiert Konfigurationsinformationen in Audio',
+ 'deviceConfig.bringPhoneCloseToXiaozhiDevice': 'Bringen Sie das Telefon in die N盲he des xiaozhi Ger盲ts (1-2 Meter Entfernung)',
+ 'deviceConfig.duringAudioPlaybackXiaozhiWillReceive': 'W盲hrend der Audio-Wiedergabe empf盲ngt xiaozhi und dekodiert Konfigurationsinformationen',
+ 'deviceConfig.afterConfigSuccessDeviceWillConnect': 'Nach erfolgreicher Konfiguration verbindet sich das Ger盲t automatisch mit dem WiFi-Netzwerk',
+ 'deviceConfig.usesAfskModulation': 'Verwendet AFSK-Modulationstechnologie, 眉bertr盲gt Daten durch 1800Hz und 1500Hz Frequenzen',
+ 'deviceConfig.ensureModeratePhoneVolume': 'Bitte stellen Sie sicher, dass die Telefonlautst盲rke moderat ist, um Umgebungsger盲uschst枚rungen zu vermeiden',
+ 'deviceConfig.generatingUltrasonicConfigAudio': 'Ultraschall-Konfigurationsaudio wird generiert',
+ 'deviceConfig.configData': 'Konfigurationsdaten',
+ 'deviceConfig.dataBytesLength': 'Datenbytes-L盲nge',
+ 'deviceConfig.bitStreamLength': 'Bitstrom-L盲nge',
+ 'deviceConfig.base64Length': 'Base64-L盲nge',
+ 'deviceConfig.audioFileTooLarge': 'Audio-Datei zu gro脽, bitte SSID oder Passwortl盲nge k眉rzen',
+ 'deviceConfig.audioGenerationSuccess': 'Audio-Generierung erfolgreich',
+ 'deviceConfig.samplePoints': 'Abtastpunkte',
+ 'deviceConfig.soundWaveGenerationSuccess': 'Schallwellen-Generierung erfolgreich',
+ 'deviceConfig.audioGenerationFailed': 'Audio-Generierung fehlgeschlagen',
+ 'deviceConfig.soundWaveGenerationFailed': 'Schallwellen-Generierung fehlgeschlagen',
+ 'deviceConfig.pleaseGenerateAudioFirst': 'Bitte generieren Sie zuerst Audio',
+ 'deviceConfig.startPlayingUltrasonicConfigAudio': 'Ultraschall-Konfigurationsaudio wird abgespielt',
+ 'deviceConfig.ultrasonicAudioStartedPlaying': 'Ultraschall-Audio wurde gestartet',
+ 'deviceConfig.startPlayingConfigSoundWave': 'Konfigurations-Schallwelle wurde gestartet',
+ 'deviceConfig.ultrasonicAudioPlaybackEnded': 'Ultraschall-Audio-Wiedergabe beendet',
+ 'deviceConfig.audioPlaybackFailed': 'Audio-Wiedergabe fehlgeschlagen',
+ 'deviceConfig.audioResourceBusy': 'Audio-Ressource besch盲ftigt, bitte sp盲ter erneut versuchen',
+ 'deviceConfig.audioFormatNotSupported': 'Audio-Format nicht unterst眉tzt, m枚glicherweise ein Data-URI-Problem',
+ 'deviceConfig.audioFileError': 'Audio-Datei-Fehler',
+ 'deviceConfig.cleaningUpAudioContext': 'Audio-Kontext wird bereinigt',
+ 'deviceConfig.cleaningUpAudioContextFailed': 'Audio-Kontext-Bereinigung fehlgeschlagen',
+ 'deviceConfig.stoppedPlayingUltrasonicAudio': 'Ultraschall-Audio-Wiedergabe gestoppt',
+ 'deviceConfig.stoppedPlaying': 'Wiedergabe gestoppt',
+ 'deviceConfig.configMethod': 'Konfigurationsmethode',
+ 'deviceConfig.enterWifiPassword': 'Bitte WiFi-Passwort eingeben',
+ 'deviceConfig.xiaozhi': 'xiaozhi',
+ 'deviceConfig.connectXiaozhiHotspot': 'Bitte verbinden Sie sich mit dem xiaozhi Hotspot',
+ 'deviceConfig.wifiScanResponse': 'WiFi-Scan-Antwort',
+ 'deviceConfig.scanSuccess': 'Scan erfolgreich',
+ 'deviceConfig.networks': 'Netzwerke',
+ 'deviceConfig.wifiScanFailed': 'WiFi-Scan fehlgeschlagen',
+ 'deviceConfig.scanFailedCheckConnection': 'Scan fehlgeschlagen, bitte Verbindung pr眉fen',
+ 'deviceConfig.checking': 'Wird gepr眉ft',
+ 'deviceConfig.reCheck': 'Erneut pr眉fen',
+ 'deviceConfig.connectedXiaozhiHotspot': 'Mit xiaozhi Hotspot verbunden',
+ 'deviceConfig.wifiNetwork': 'WiFi-Netzwerk',
+ 'deviceConfig.wifiPassword': 'WiFi-Passwort',
+ 'deviceConfig.scanning': 'Wird gescannt',
+ 'deviceConfig.cancel': 'Abbrechen',
+ 'deviceConfig.clickRefreshScan': 'Bitte Scan aktualisieren klicken',
+ 'deviceConfig.esp32ConnectionCheckFailed': 'ESP32-Verbindungspr眉fung fehlgeschlagen',
+ 'deviceConfig.startWifiConfig': 'WiFi-Konfiguration wird gestartet',
+ 'deviceConfig.configSuccess': 'Konfiguration erfolgreich',
+ 'deviceConfig.deviceWillConnectTo': 'Ger盲t wird sich verbinden mit',
+ 'deviceConfig.deviceWillRestart': 'Ger盲t wird neu starten',
+ 'deviceConfig.pleaseDisconnectXiaozhiHotspot': 'Bitte trennen Sie sich vom xiaozhi Hotspot',
+ 'deviceConfig.configFailed': 'Konfiguration fehlgeschlagen',
+ 'deviceConfig.wifiConfigFailed': 'WiFi-Konfiguration fehlgeschlagen',
+ 'deviceConfig.pleaseCheckNetworkConnection': 'Bitte Netzwerkverbindung pr眉fen',
+ 'deviceConfig.startWifiConfigButton': 'Konfiguration starten',
+ 'deviceConfig.configuring': 'Wird konfiguriert...',
+ 'deviceConfig.wifiConfigInstructions': 'WiFi-Konfigurationsanleitung',
+ 'deviceConfig.phoneConnectXiaozhiHotspot': 'Telefon mit xiaozhi Hotspot verbinden',
+ 'deviceConfig.selectTargetWifiNetwork': 'Ziel-WiFi-Netzwerk ausw盲hlen',
+ 'deviceConfig.enterWifiPasswordIfNeeded': 'WiFi-Passwort eingeben falls ben枚tigt',
+ 'deviceConfig.clickStartConfigAndWait': 'Konfiguration starten klicken und warten',
+ 'deviceConfig.afterConfigSuccessDeviceWillRestart': 'Nach erfolgreicher Konfiguration startet Ger盲t automatisch neu',
+ 'deviceConfig.audioPlaybackError': 'Audio-Wiedergabe-Fehler',
+ 'deviceConfig.playbackFailed': 'Wiedergabe fehlgeschlagen',
+
+ // Voiceprint page
+ 'voiceprint.audioNotExist': 'Audio existiert nicht',
+ 'voiceprint.getAudioFailed': 'Audio konnte nicht abgerufen werden',
+ 'voiceprint.audioPlayFailed': 'Audio-Wiedergabe fehlgeschlagen',
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/i18n/en.ts b/xiaozhi-esp32-server/main/manager-mobile/src/i18n/en.ts
new file mode 100644
index 0000000..14fbe86
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/i18n/en.ts
@@ -0,0 +1,597 @@
+// English language pack
+export default {
+ // TabBar
+ 'tabBar.home': 'Home',
+ 'tabBar.deviceConfig': 'Network Config',
+ 'tabBar.settings': 'System',
+ // Settings page title
+ 'settings.title': 'Settings',
+ // Login page
+ 'login.pageTitle': 'Login',
+ 'login.navigationTitle': 'Login',
+ 'login.fetchConfigError': 'Failed to fetch configuration:',
+ 'login.selectLanguage': 'Select Language',
+ 'login.selectLanguageTip': 'En',
+ 'login.welcomeBack': 'Welcome Back',
+ 'login.pleaseLogin': 'Please log in to your account',
+ 'login.enterUsername': 'Please enter username',
+ 'login.enterPassword': 'Please enter password',
+ 'login.enterCaptcha': 'Please enter verification code',
+ 'login.loginButton': 'Login',
+ 'login.loggingIn': 'Logging in...',
+ 'login.noAccount': 'Sign Up',
+ 'login.enterPhone': 'Please enter phone number',
+ 'login.selectCountry': 'Select Country/Region',
+ 'login.confirm': 'Confirm',
+ 'login.serverSetting': 'Server Settings',
+ 'login.requiredUsername': 'Username cannot be empty',
+ 'login.requiredPassword': 'Password cannot be empty',
+ 'login.requiredCaptcha': 'Verification code cannot be empty',
+ 'login.requiredMobile': 'Please enter a valid phone number',
+ 'login.captchaError': 'Graphic verification code error',
+ 'login.forgotPassword': 'Forgot Password',
+ 'login.userAgreement': 'User Agreement',
+ 'login.privacyPolicy': 'Privacy Policy',
+
+ // Register page
+ 'register.pageTitle': 'Register',
+ 'register.createAccount': 'Create Account',
+ 'register.enterUsername': 'Please enter username',
+ 'register.enterPassword': 'Please enter password',
+ 'register.confirmPassword': 'Please confirm password',
+ 'register.enterPhone': 'Please enter phone number',
+ 'register.enterCode': 'Please enter verification code',
+ 'register.getCode': 'Get Code',
+ 'register.agreeTerms': 'I have read and agree to the',
+ 'register.terms': 'User Agreement',
+ 'register.privacy': 'Privacy Policy',
+ 'register.registerButton': 'Register',
+ 'register.registering': 'Registering...',
+ 'register.haveAccount': 'Already have an account?',
+ 'register.loginNow': 'Login Now',
+ 'register.selectCountry': 'Select Country/Region',
+ 'register.confirm': 'Confirm',
+ 'register.captchaSendSuccess': 'Verification code sent successfully',
+
+ // Home page
+ 'home.pageTitle': 'Home',
+ 'home.createAgent': 'Create Agent',
+ 'home.agentName': 'Agent',
+ 'home.modelInfo': 'Model Info',
+ 'home.lastActive': 'Last Active',
+ 'home.greeting': 'Hi Jarvis',
+ 'home.subtitle': 'Let\'s have',
+ 'home.wonderfulDay': 'a wonderful day!',
+ 'home.emptyState': 'No agents available',
+ 'home.deviceManagement': 'Device Management',
+ 'home.lastConversation': 'Last Conversation:',
+ 'home.delete': 'Delete',
+ 'home.createFirstAgent': 'Click the + button in the lower right corner to create your first agent',
+ 'home.dialogTitle': 'Create Agent',
+ 'home.inputPlaceholder': 'e.g. Customer Service Assistant, Voice Assistant, Knowledge Q&A',
+ 'home.createError': 'The name length must be between 1 and 64 characters',
+ 'home.createNow': 'Create Now',
+ 'home.deleteConfirmTitle': 'Delete Agent',
+ 'home.confirmDeleteAgent': 'Deletion cannot be undone. Type the agent name "{agentName}" to confirm.',
+ 'home.deleteAgentNamePlaceholder': 'Enter agent name',
+ 'home.deleteAgentNameMismatch': 'The agent name does not match',
+ 'home.deleteAgentPasteForbidden': 'Please type the agent name manually. Pasting is not allowed.',
+ 'home.deleteAgentMissingInfo': 'Unable to confirm agent information. Please refresh and try again.',
+ 'home.deleteAgentSuccess': 'Agent deleted successfully',
+ 'home.justNow': 'Just now',
+ 'home.minutesAgo': 'minutes ago',
+ 'home.hoursAgo': 'hours ago',
+ 'home.daysAgo': 'days ago',
+ 'home.languageModel': 'LLM',
+ 'home.voiceModel': 'TTS',
+
+ // Agent page
+ 'agent.pageTitle': 'Agent',
+ 'agent.roleConfig': 'Role Configuration',
+ 'agent.deviceManagement': 'Device Management',
+ 'agent.chatHistory': 'Chat History',
+ 'agent.voiceprintManagement': 'Voiceprint Management',
+ 'agent.editTitle': 'Edit Agent',
+ 'agent.toolsTitle': 'Edit Features',
+ 'agent.voiceActivityDetection': 'Voice Activity Detection',
+ 'agent.speechRecognition': 'Speech Recognition',
+ 'agent.largeLanguageModel': 'Large Language Model',
+ 'agent.save': 'Save',
+ 'agent.cancel': 'Cancel',
+ // Agent Edit Page
+ 'agent.basicInfo': 'Basic Information',
+ 'agent.agentName': 'Agent Name',
+ 'agent.inputAgentName': 'Please input agent name',
+ 'agent.roleMode': 'Role Mode',
+ 'agent.agentTag': 'Intelligent agent label',
+ 'agent.addAgentTag': 'Add label',
+ 'agent.inputAgentTag': 'Please input agent label',
+ 'agent.contextProvider': 'Context',
+ 'agent.contextProviderSuccess': 'Successfully added {count} sources.',
+ 'agent.contextProviderDocLink': 'How to deploy context provider',
+ 'agent.editContextProvider': 'Edit Source',
+ 'agent.roleDescription': 'Role Description',
+ 'agent.inputRoleDescription': 'Please input role description',
+ 'agent.modelConfig': 'Model Configuration',
+ 'agent.vad': 'Voice Activity Detection',
+ 'agent.asr': 'Speech Recognition',
+ 'agent.llm': 'Main language model',
+ 'agent.slm': 'Small parameter model',
+ 'agent.vllm': 'Vision Language Model',
+ 'agent.intent': 'Intent Recognition',
+ 'agent.memory': 'Memory',
+ 'agent.voiceSettings': 'Voice Settings',
+ 'agent.tts': 'Text-to-Speech',
+ 'agent.voiceprint': 'Agent Voice',
+ 'agent.plugins': 'Plugins',
+ 'agent.editFunctions': 'Edit Functions',
+ 'agent.historyMemory': 'History Memory',
+ 'agent.memoryContent': 'Memory content',
+ 'agent.saving': 'Saving...',
+ 'agent.saveSuccess': 'Save successful',
+ 'agent.saveFail': 'Save failed',
+ 'agent.ttsOptionsLoadFailed': 'Voice options could not be loaded. The previous voice settings were kept.',
+ 'agent.loadFail': 'Load failed',
+ 'agent.pleaseInputAgentName': 'Please input agent name',
+ 'agent.pleaseInputRoleDescription': 'Please input role description',
+ 'agent.pleaseSelect': 'Please select',
+ 'agent.reportText': 'Report Text',
+ 'agent.reportTextVoice': 'Report Text+Voice',
+ 'agent.reportMode': 'Report Mode',
+ 'agent.language': 'Conversation Language',
+ 'agent.languageConfig': 'Speech Rate&Tone',
+ 'agent.ttsVolume': 'Volume',
+ 'agent.ttsRate': 'Speech Rate',
+ 'agent.ttsPitch': 'Tone',
+ 'agent.volumeHint': '-100=Minimum, 0=Standard, 100=Maximum',
+ 'agent.speedHint': '-100=Slower, 0=Standard, 100=Faster',
+ 'agent.pitchHint': '-100=Lowest, 0=Standard, 100=Highest',
+
+ // Agent snapshots
+ 'agentSnapshot.title': 'Version History',
+ 'agentSnapshot.empty': 'No versions yet',
+ 'agentSnapshot.emptyTip': 'Saved configs will appear here',
+ 'agentSnapshot.version': 'Version',
+ 'agentSnapshot.createdAt': 'Saved At',
+ 'agentSnapshot.source': 'Source',
+ 'agentSnapshot.changedFields': 'Changes',
+ 'agentSnapshot.view': 'View',
+ 'agentSnapshot.restore': 'Restore',
+ 'agentSnapshot.delete': 'Delete',
+ 'agentSnapshot.loadMore': 'Load More',
+ 'agentSnapshot.detailTitle': 'Change Details',
+ 'agentSnapshot.restorePreviewTitle': 'Restore Preview',
+ 'agentSnapshot.confirmRestore': 'Confirm Restore',
+ 'agentSnapshot.currentVersion': 'Latest Snapshot',
+ 'agentSnapshot.beforeChange': 'Before',
+ 'agentSnapshot.afterChange': 'After',
+ 'agentSnapshot.beforeRestore': 'Before Restore',
+ 'agentSnapshot.afterRestore': 'After Restore',
+ 'agentSnapshot.configValue': 'Config Value',
+ 'agentSnapshot.emptyValue': 'None',
+ 'agentSnapshot.secretRedacted': 'Secret hidden',
+ 'agentSnapshot.redactedValueChanged': 'The value is hidden, but it did change',
+ 'agentSnapshot.noChangedContent': 'No displayable changes',
+ 'agentSnapshot.recordedChange': 'Changed when recorded',
+ 'agentSnapshot.noRestoreNeeded': 'The current configuration already matches this version',
+ 'agentSnapshot.unsavedChangesTitle': 'Unsaved changes',
+ 'agentSnapshot.unsavedChangesWarning': 'Continuing will discard the unsaved changes on this page.',
+ 'agentSnapshot.discardAndRestore': 'Discard and restore',
+ 'agentSnapshot.restoreConfirm': 'Restore to version #{version}? The current configuration will remain available in history.',
+ 'agentSnapshot.restoreMemoryWarning': 'Restoring to a no-memory version will clear this agent\'s chat history. Please confirm the risk.',
+ 'agentSnapshot.restoreChatHistoryDestructiveWarning': 'This restore will permanently delete this agent\'s existing chat history. Chat history is not included in configuration snapshots and cannot be recovered from version history.',
+ 'agentSnapshot.restoreSuccess': 'Version restored',
+ 'agentSnapshot.restoreFailed': 'Failed to restore version',
+ 'agentSnapshot.reloadAfterRestorePending': 'Reloading the restored configuration and tags. Saving is disabled until this completes.',
+ 'agentSnapshot.reloadAfterRestoreFailed': 'The restored configuration or tags could not be reloaded. Saving remains disabled to prevent stale data from overwriting the restore. Please retry.',
+ 'agentSnapshot.retryReload': 'Reload',
+ 'agentSnapshot.mutationBusy': 'The configuration is being saved or reloaded. Wait for it to finish before opening version history or restoring.',
+ 'agentSnapshot.deleteConfirm': 'Delete version #{version}? This cannot be undone.',
+ 'agentSnapshot.deleteSuccess': 'Version deleted',
+ 'agentSnapshot.deleteFailed': 'Failed to delete version',
+ 'agentSnapshot.fetchFailed': 'Failed to fetch versions',
+ 'agentSnapshot.detailFailed': 'Failed to fetch version details',
+ 'agentSnapshot.correctWordCount': '{count} replacement words',
+ 'agentSnapshot.source.config': 'Config Save',
+ 'agentSnapshot.source.current': 'Current Config',
+ 'agentSnapshot.source.restore': 'Restored',
+ 'agentSnapshot.source.initial': 'Initial Version',
+ 'agentSnapshot.field.initial': 'Initial Snapshot',
+ 'agentSnapshot.field.agentCode': 'Agent Code',
+ 'agentSnapshot.field.agentName': 'Nickname',
+ 'agentSnapshot.field.asrModelId': 'Speech Recognition',
+ 'agentSnapshot.field.vadModelId': 'Voice Activity Detection',
+ 'agentSnapshot.field.llmModelId': 'Main Language Model',
+ 'agentSnapshot.field.slmModelId': 'Small Language Model',
+ 'agentSnapshot.field.vllmModelId': 'Vision Model',
+ 'agentSnapshot.field.ttsModelId': 'Text-to-Speech',
+ 'agentSnapshot.field.ttsVoiceId': 'Voice',
+ 'agentSnapshot.field.ttsLanguage': 'Language',
+ 'agentSnapshot.field.ttsVolume': 'Volume',
+ 'agentSnapshot.field.ttsRate': 'Speed',
+ 'agentSnapshot.field.ttsPitch': 'Pitch',
+ 'agentSnapshot.field.memModelId': 'Memory Mode',
+ 'agentSnapshot.field.intentModelId': 'Intent Recognition',
+ 'agentSnapshot.field.chatHistoryConf': 'Chat History Config',
+ 'agentSnapshot.field.systemPrompt': 'Role Description',
+ 'agentSnapshot.field.summaryMemory': 'Memory',
+ 'agentSnapshot.field.langCode': 'Language Code',
+ 'agentSnapshot.field.language': 'Interaction Language',
+ 'agentSnapshot.field.sort': 'Sort',
+ 'agentSnapshot.field.functions': 'Plugins',
+ 'agentSnapshot.field.contextProviders': 'Context Sources',
+ 'agentSnapshot.field.correctWordFileIds': 'Replacement Words',
+ 'agentSnapshot.field.tagNames': 'Agent Tags',
+ 'agentSnapshot.chatHistoryConf.none': 'Do not record chat history',
+ 'agentSnapshot.chatHistoryConf.text': 'Report text',
+ 'agentSnapshot.chatHistoryConf.textVoice': 'Report text and voice',
+ 'agentSnapshot.model.Memory_nomem': 'No memory',
+ 'agentSnapshot.model.Memory_mem_local_short': 'Local short memory',
+ 'agentSnapshot.model.Memory_mem0ai': 'Mem0AI memory',
+ 'agentSnapshot.model.Memory_mem_report_only': 'Report only',
+ 'agentSnapshot.model.Intent_nointent': 'No intent recognition',
+ 'agentSnapshot.model.Intent_intent_llm': 'External LLM intent recognition',
+ 'agentSnapshot.model.Intent_function_call': 'LLM function calling',
+
+ // Context provider dialog related
+ 'contextProviderDialog.title': 'Edit Source',
+ 'contextProviderDialog.noContextApi': 'No Context API',
+ 'contextProviderDialog.add': 'Add',
+ 'contextProviderDialog.apiUrl': 'API URL',
+ 'contextProviderDialog.apiUrlPlaceholder': 'http://api.example.com/data',
+ 'contextProviderDialog.requestHeaders': 'Request Headers',
+ 'contextProviderDialog.headerKeyPlaceholder': 'Key',
+ 'contextProviderDialog.headerValuePlaceholder': 'Value',
+ 'contextProviderDialog.noHeaders': 'No Headers',
+ 'contextProviderDialog.addHeader': 'Add Header',
+ 'contextProviderDialog.cancel': 'Cancel',
+ 'contextProviderDialog.confirm': 'Confirm',
+
+ // Manual add device dialog related
+ 'manualAddDeviceDialog.title': 'Manual Add Device',
+ 'manualAddDeviceDialog.deviceType': 'Device Type',
+ 'manualAddDeviceDialog.deviceTypePlaceholder': 'Please select device type',
+ 'manualAddDeviceDialog.firmwareVersion': 'Firmware Version',
+ 'manualAddDeviceDialog.firmwareVersionPlaceholder': 'Please enter firmware version',
+ 'manualAddDeviceDialog.macAddress': 'Mac Address',
+ 'manualAddDeviceDialog.macAddressPlaceholder': 'Please enter Mac address',
+ 'manualAddDeviceDialog.confirm': 'Confirm',
+ 'manualAddDeviceDialog.cancel': 'Cancel',
+ 'manualAddDeviceDialog.requiredMacAddress': 'Please enter Mac address',
+ 'manualAddDeviceDialog.invalidMacAddress': 'Please enter correct Mac address format, e.g.: 00:1A:2B:3C:4D:5E',
+ 'manualAddDeviceDialog.requiredDeviceType': 'Please select device type',
+ 'manualAddDeviceDialog.requiredFirmwareVersion': 'Please enter firmware version',
+ 'manualAddDeviceDialog.getFirmwareTypeFailed': 'Failed to get firmware type',
+ 'manualAddDeviceDialog.addSuccess': 'Device added successfully',
+ 'manualAddDeviceDialog.addFailed': 'Failed to add',
+ 'manualAddDeviceDialog.bindWithCode': 'Bind with 6-digit code',
+
+ // Chat History Page
+ 'chatHistory.getChatSessions': 'Get chat session list',
+ 'chatHistory.noSelectedAgent': 'No agent selected',
+ 'chatHistory.getChatSessionsFailed': 'Failed to get chat session list:',
+ 'chatHistory.unknownTime': 'Unknown time',
+ 'chatHistory.justNow': 'Just now',
+ 'chatHistory.minutesAgo': '{minutes} minutes ago',
+ 'chatHistory.hoursAgo': '{hours} hours ago',
+ 'chatHistory.daysAgo': '{days} days ago',
+ 'chatHistory.conversationRecord': 'Conversation Record',
+ 'chatHistory.totalChats': '{count} chats in total',
+ 'chatHistory.loading': 'Loading...',
+ 'chatHistory.noMoreData': 'No more data',
+ 'chatHistory.noChatRecords': 'No chat records',
+ 'chatHistory.chatRecordsDescription': 'Conversation records with agents will be displayed here',
+ // Chat History Detail Page
+ 'chatHistory.pageTitle': 'Chat Detail',
+ 'chatHistory.assistantName': 'Intelligent Assistant',
+ 'chatHistory.userName': 'User',
+ 'chatHistory.aiAssistantName': 'AI Assistant',
+ 'chatHistory.loadFailed': 'Failed to load chat history',
+ 'chatHistory.parameterError': 'Page parameter error',
+ 'chatHistory.invalidAudioId': 'Invalid audio ID',
+ 'chatHistory.audioPlayFailed': 'Audio playback failed',
+ 'chatHistory.playAudioFailed': 'Failed to play audio',
+
+ // Device management page
+ 'device.pageTitle': 'Device Management',
+ 'device.noDevices': 'No devices available',
+ 'device.macAddress': 'MAC Address',
+ 'device.firmwareVersion': 'Firmware Version',
+ 'device.lastConnected': 'Last Conversation',
+ 'device.otaUpdate': 'OTA Update',
+ 'device.unbind': 'Unbind',
+ 'device.confirmUnbind': 'Sure',
+ 'device.bindDevice': 'Bind New Device',
+ 'device.deviceType': 'Device Type',
+ 'device.loading': 'Loading...',
+ 'device.neverConnected': 'Never connected',
+ 'device.justNow': 'Just now',
+ 'device.minutesAgo': '{minutes} minutes ago',
+ 'device.hoursAgo': '{hours} hours ago',
+ 'device.daysAgo': '{days} days ago',
+ 'device.otaAutoUpdateEnabled': 'OTA auto update enabled',
+ 'device.otaAutoUpdateDisabled': 'OTA auto update disabled',
+ 'device.operationFailed': 'Operation failed, please retry',
+ 'device.deviceUnbound': 'Device unbound',
+ 'device.unbindFailed': 'Unbind failed, please retry',
+ 'device.unbindDevice': 'Unbind Device',
+ 'device.confirmUnbindDevice': 'Are you sure you want to unbind device "{macAddress}"?',
+ 'device.cancel': 'Cancel',
+ 'device.noDevice': 'No Device',
+ 'device.pleaseSelectAgent': 'Please select an agent first',
+ 'device.deviceBindSuccess': 'Device bound successfully!',
+ 'device.bindFailed': 'Bind failed, please check if the verification code is correct',
+ 'device.enterDeviceCode': 'Please enter device verification code',
+ 'device.bindNow': 'Bind Now',
+ 'device.lastConnection': 'Last Connection',
+ 'device.clickToBindFirstDevice': 'Click the + button in the lower right corner to bind your first device',
+
+ // Common
+ 'common.success': 'Success',
+ 'common.fail': 'Failed',
+ 'common.loading': 'Loading...',
+ 'common.confirm': 'Confirm',
+ 'common.cancel': 'Cancel',
+ 'common.delete': 'Delete',
+ 'common.edit': 'Edit',
+ 'common.add': 'Add',
+ 'common.pleaseSelect': 'Please select',
+ 'common.unknownError': 'Unknown error',
+ 'common.networkError': 'Network error',
+
+ // Retrieve Password page
+ 'retrievePassword.title': 'Reset Password',
+ 'retrievePassword.subtitle': 'Recover your account password via mobile number',
+ 'retrievePassword.mobileRequired': 'Please enter mobile number',
+ 'retrievePassword.inputCorrectMobile': 'Please enter a valid mobile number',
+ 'retrievePassword.captchaRequired': 'Please enter graphic verification code',
+ 'retrievePassword.mobileCaptchaRequired': 'Please enter SMS verification code',
+ 'retrievePassword.newPasswordRequired': 'Please enter new password',
+ 'retrievePassword.confirmNewPasswordRequired': 'Please confirm new password',
+ 'retrievePassword.passwordsNotMatch': 'Passwords do not match',
+ 'retrievePassword.mobilePlaceholder': 'Please enter mobile number',
+ 'retrievePassword.captchaPlaceholder': 'Please enter graphic verification code',
+ 'retrievePassword.mobileCaptchaPlaceholder': 'Please enter SMS verification code',
+ 'retrievePassword.newPasswordPlaceholder': 'Please enter new password',
+ 'retrievePassword.confirmNewPasswordPlaceholder': 'Please confirm new password',
+ 'retrievePassword.getMobileCaptcha': 'Get Code',
+ 'retrievePassword.captchaSendSuccess': 'Verification code sent successfully',
+ 'retrievePassword.passwordUpdateSuccess': 'Password reset successfully',
+ 'retrievePassword.resetButton': 'Reset Password',
+ 'retrievePassword.goToLogin': 'Back to Login',
+
+ // SM2 encryption related error messages
+ 'sm2.publicKeyNotConfigured': 'SM2 public key not configured, please contact administrator',
+ 'sm2.encryptionFailed': 'Password encryption failed',
+ 'sm2.keyGenerationFailed': 'Key pair generation failed',
+ 'sm2.invalidPublicKey': 'Invalid public key format',
+ 'sm2.encryptionError': 'Error occurred during encryption',
+ 'sm2.publicKeyRetry': 'Retrying to get public key...',
+ 'sm2.publicKeyRetryFailed': 'Public key retrieval retry failed',
+
+ // Voiceprint page
+ 'voiceprint.noSelectedAgent': 'No agent selected',
+ 'voiceprint.pleaseSelectAgent': 'Please select an agent first',
+ 'voiceprint.fetchHistoryFailed': 'Failed to fetch chat history',
+ 'voiceprint.clickToSelectVector': 'Click to select voiceprint vector',
+ 'voiceprint.pleaseInputName': 'Please input name',
+ 'voiceprint.pleaseSelectVector': 'Please select voiceprint vector',
+ 'voiceprint.addSuccess': 'Add success',
+ 'voiceprint.addFailed': 'Failed to add speaker',
+ 'voiceprint.editSuccess': 'Edit success',
+ 'voiceprint.editFailed': 'Failed to edit speaker',
+ 'voiceprint.deleteConfirmMsg': 'Are you sure to delete this speaker?',
+ 'voiceprint.deleteConfirmTitle': 'Confirm Delete',
+ 'voiceprint.deleteSuccess': 'Delete success',
+ 'voiceprint.loading': 'Loading...',
+ 'voiceprint.delete': 'Delete',
+ 'voiceprint.emptyTitle': 'No voiceprint data',
+ 'voiceprint.emptyDesc': 'Click the + button at the bottom right to add your first speaker',
+ 'voiceprint.addSpeaker': 'Add Speaker',
+ 'voiceprint.voiceVector': 'Voiceprint Vector',
+ 'voiceprint.name': 'Name',
+ 'voiceprint.description': 'Description',
+ 'voiceprint.pleaseInputDescription': 'Please input description',
+ 'voiceprint.cancel': 'Cancel',
+ 'voiceprint.save': 'Save',
+ 'voiceprint.editSpeaker': 'Edit Speaker',
+ 'voiceprint.selectVector': 'Select Voiceprint Vector',
+ 'voiceprint.voiceprintInterfaceNotConfigured': 'Voiceprint interface not configured',
+
+ // Settings page
+ 'settings.pageTitle': 'Settings',
+ 'settings.navigationTitle': 'Settings',
+ 'settings.networkSettings': 'Network Settings',
+ 'settings.serverApiUrl': 'Server API URL',
+ 'settings.validServerUrl': 'Please enter a valid server address (starting with http or https and ending with /xiaozhi)',
+ 'settings.saveSettings': 'Save Settings',
+ 'settings.resetDefault': 'Reset Default',
+ 'settings.restartApp': 'Restart App',
+ 'settings.restartNow': 'Restart Now',
+ 'settings.restartLater': 'Later',
+ // About us
+ 'settings.aboutApp': 'About XiaoZhi Console',
+ 'settings.aboutContent': 'XiaoZhi Console\n\nA cross-platform mobile management app built with Vue.js 3 + uni-app, providing device management, agent configuration and other functions for xiaozhi ESP32 smart hardware.\n\n漏 2025 xiaozhi-esp32-server {version}',
+ 'settings.restartSuccess': 'Saved, you can manually restart the app later',
+ 'settings.serverUrlSavedAndCacheCleared': 'Server URL saved and cache cleared',
+ 'settings.resetToDefaultAndCacheCleared': 'Reset to default and cache cleared',
+ 'settings.resetSuccess': 'Reset successful',
+ 'settings.enterServerUrl': 'Please enter server URL',
+ 'settings.clearCacheFailed': 'Failed to clear cache',
+ 'settings.cacheManagement': 'Cache Management',
+ 'settings.totalCacheSize': 'Total Cache Size',
+ 'settings.appDataSize': 'Total App Data Size',
+ 'settings.cacheClear': 'Cache Clear',
+ 'settings.clearAllCache': 'Clear all cache data',
+ 'settings.clearCache': 'Clear Cache',
+ 'settings.modifyWillClearCache': 'Modifications will clear cache',
+ 'settings.appInfo': 'App Info',
+ 'settings.aboutUs': 'About Us',
+ 'settings.appVersion': 'App Version & Team Info',
+ 'settings.confirmClear': 'Confirm Clear',
+ 'settings.confirmClearMessage': 'Are you sure you want to clear all cache? This will delete all data including login status and require re-login.',
+ 'settings.cacheCleared': 'Cache cleared successfully, redirecting to login page',
+ 'settings.languageSettings': 'Language Settings',
+ 'settings.language': 'Language',
+ 'settings.selectLanguage': 'Select Language',
+ 'settings.languageChanged': 'Language changed successfully',
+
+ // Messages
+ 'message.loginSuccess': 'Login successful!',
+ 'message.loginFail': 'Login failed',
+ 'message.registerSuccess': 'Registration successful',
+ 'message.registerFail': 'Registration failed',
+ 'message.saveSuccess': 'Save successful',
+ 'message.saveFail': 'Save failed',
+ 'message.deleteSuccess': 'Delete successful',
+ 'message.deleteFail': 'Delete failed',
+ 'message.bindSuccess': 'Binding successful',
+ 'message.bindFail': 'Binding failed',
+ 'message.unbindSuccess': 'Unbinding successful',
+ 'message.unbindFail': 'Unbinding failed',
+ 'message.networkError': 'Network error, please check your connection',
+ 'message.serverError': 'Server error, please try again later',
+ 'message.invalidAddress': 'The address may be invalid. Please check if the server is started or if the network connection is normal; It is also possible that requests cannot be sent due to HTTPS protocol issues',
+ 'message.languageChanged': 'Language changed',
+ 'message.passwordError': 'Account or password error',
+ 'message.phoneRegistered': 'This phone number has been registered',
+
+ // Agent Tools Page
+ 'agent.tools.pageTitle': 'Agent Tools',
+ 'agent.tools.unselected': 'Unselected',
+ 'agent.tools.selected': 'Selected',
+ 'agent.tools.noMorePlugins': 'No more plugins',
+ 'agent.tools.pleaseSelectPlugin': 'Please select plugin function',
+ 'agent.tools.builtInPlugins': 'Built-in Plugins',
+ 'agent.tools.mcpAccessPoint': 'MCP Access Point',
+ 'agent.tools.copy': 'Copy',
+ 'agent.tools.noTools': 'No tools available',
+ 'agent.tools.parameterConfig': 'Param Config',
+ 'agent.tools.noParamsNeeded': 'No parameters needed',
+ 'agent.tools.pleaseInput': 'Please input',
+ 'agent.tools.inputOneItemPerLine': 'Input one item per line',
+ 'agent.tools.pleaseInputValidJson': 'Please input valid JSON format',
+ 'agent.tools.enableFunction': 'Enable Function',
+ 'agent.tools.toggleFunction': 'Turn on or off this function',
+ 'agent.tools.jsonFormatError': 'JSON format error',
+ 'agent.tools.noMcpAddressToCopy': 'No MCP address to copy',
+ 'agent.tools.mcpAddressCopied': 'MCP address copied to clipboard',
+ 'agent.tools.copyFailed': 'Copy failed, please try again',
+ 'agent.tools.defaultValue': 'Default value',
+ 'agent.tools.notSelected': 'Unselected',
+ 'agent.tools.clickToConfigure': 'Click to configure',
+ 'agent.tools.mcpEndpoint': 'MCP Endpoint',
+ 'agent.tools.eachLineOneItem': 'Input one item per line',
+
+ // Device Config page
+ 'deviceConfig.pageTitle': 'Device Configuration',
+ 'deviceConfig.wifiConfig': 'WiFi Configuration',
+ 'deviceConfig.ultrasonicConfig': 'Ultrasonic Configuration',
+ 'deviceConfig.selectConfigMethod': 'Select Configuration Method',
+ 'deviceConfig.networkConfig': 'Network Configuration',
+ 'deviceConfig.selectedNetwork': 'Selected Network',
+ 'deviceConfig.signal': 'Signal',
+ 'deviceConfig.openNetwork': 'Open Network',
+ 'deviceConfig.encryptedNetwork': 'Encrypted Network',
+ 'deviceConfig.password': 'Password',
+ 'deviceConfig.pleaseEnterPassword': 'Please enter WiFi password',
+ 'deviceConfig.startConfig': 'Start Configuration',
+ 'deviceConfig.connectToXiaozhiHotspot': 'Please connect to xiaozhi hotspot first',
+ 'deviceConfig.detecting': 'Detecting...',
+ 'deviceConfig.reDetect': 'Re-detect',
+ 'deviceConfig.alreadyConnected': 'Connected to xiaozhi hotspot',
+ 'deviceConfig.refreshStatus': 'Refresh Status',
+ 'deviceConfig.wifiNetworks': 'WiFi Networks',
+ 'deviceConfig.selectWifiNetwork': 'Select WiFi Network',
+ 'deviceConfig.refreshScan': 'Refresh Scan',
+ 'deviceConfig.noWifiNetworks': 'No WiFi networks available',
+ 'deviceConfig.clickToRefreshScan': 'Please click Refresh Scan',
+ 'deviceConfig.signalStrong': 'Strong Signal',
+ 'deviceConfig.signalGood': 'Good Signal',
+ 'deviceConfig.signalFair': 'Fair Signal',
+ 'deviceConfig.signalWeak': 'Weak Signal',
+ 'deviceConfig.channel': 'Channel',
+ 'deviceConfig.about': 'about',
+ 'deviceConfig.seconds': 'seconds',
+ 'deviceConfig.generating': 'Generating...',
+ 'deviceConfig.playing': 'Playing...',
+ 'deviceConfig.generateAndPlaySoundWave': 'Generate and Play Sound Wave',
+ 'deviceConfig.playSoundWave': 'Play Sound Wave',
+ 'deviceConfig.stopPlaying': 'Stop Playing',
+ 'deviceConfig.autoLoopPlaySoundWave': 'Auto Loop Play Sound Wave',
+ 'deviceConfig.configAudioFile': 'Configuration Audio File',
+ 'deviceConfig.duration': 'Duration',
+ 'deviceConfig.ultrasonicConfigInstructions': 'Ultrasonic Configuration Instructions',
+ 'deviceConfig.ensureWifiNetworkSelectedAndPasswordEntered': 'Ensure WiFi network is selected and password is entered',
+ 'deviceConfig.clickGenerateAndPlaySoundWave': 'Click Generate and Play Sound Wave, the system will encode configuration information into audio',
+ 'deviceConfig.bringPhoneCloseToXiaozhiDevice': 'Bring phone close to xiaozhi device (1-2 meters distance)',
+ 'deviceConfig.duringAudioPlaybackXiaozhiWillReceive': 'During audio playback, xiaozhi will receive and decode configuration information',
+ 'deviceConfig.afterConfigSuccessDeviceWillConnect': 'After successful configuration, the device will automatically connect to WiFi network',
+ 'deviceConfig.usesAfskModulation': 'Uses AFSK modulation technology, transmitting data through 1800Hz and 1500Hz frequencies',
+ 'deviceConfig.ensureModeratePhoneVolume': 'Please ensure phone volume is moderate to avoid environmental noise interference',
+ 'deviceConfig.generatingUltrasonicConfigAudio': 'Generating ultrasonic configuration audio',
+ 'deviceConfig.configData': 'Configuration data',
+ 'deviceConfig.dataBytesLength': 'Data bytes length',
+ 'deviceConfig.bitStreamLength': 'Bit stream length',
+ 'deviceConfig.base64Length': 'Base64 length',
+ 'deviceConfig.audioFileTooLarge': 'Audio file too large, please shorten SSID or password length',
+ 'deviceConfig.audioGenerationSuccess': 'Audio generation successful',
+ 'deviceConfig.samplePoints': 'Sample points',
+ 'deviceConfig.soundWaveGenerationSuccess': 'Sound wave generation successful',
+ 'deviceConfig.audioGenerationFailed': 'Audio generation failed',
+ 'deviceConfig.soundWaveGenerationFailed': 'Sound wave generation failed',
+ 'deviceConfig.pleaseGenerateAudioFirst': 'Please generate audio first',
+ 'deviceConfig.startPlayingUltrasonicConfigAudio': 'Starting to play ultrasonic configuration audio',
+ 'deviceConfig.ultrasonicAudioStartedPlaying': 'Ultrasonic audio started playing',
+ 'deviceConfig.startPlayingConfigSoundWave': 'Started playing configuration sound wave',
+ 'deviceConfig.ultrasonicAudioPlaybackEnded': 'Ultrasonic audio playback ended',
+ 'deviceConfig.audioPlaybackFailed': 'Audio playback failed',
+ 'deviceConfig.audioResourceBusy': 'Audio resource busy, please try again later',
+ 'deviceConfig.audioFormatNotSupported': 'Audio format not supported, may be a data URI issue',
+ 'deviceConfig.audioFileError': 'Audio file error',
+ 'deviceConfig.cleaningUpAudioContext': 'Cleaning up audio context',
+ 'deviceConfig.cleaningUpAudioContextFailed': 'Failed to clean up audio context',
+ 'deviceConfig.stoppedPlayingUltrasonicAudio': 'Stopped playing ultrasonic audio',
+ 'deviceConfig.stoppedPlaying': 'Stopped playing',
+ 'deviceConfig.configMethod': 'Configuration Method',
+ 'deviceConfig.enterWifiPassword': 'Please enter WiFi password',
+ 'deviceConfig.xiaozhi': 'xiaozhi',
+ 'deviceConfig.connectXiaozhiHotspot': 'Please connect to xiaozhi hotspot',
+ 'deviceConfig.wifiScanResponse': 'WiFi scan response',
+ 'deviceConfig.scanSuccess': 'Scan successful',
+ 'deviceConfig.networks': 'networks',
+ 'deviceConfig.wifiScanFailed': 'WiFi scan failed',
+ 'deviceConfig.scanFailedCheckConnection': 'Scan failed, please check connection',
+ 'deviceConfig.checking': 'Checking',
+ 'deviceConfig.reCheck': 'Re-check',
+ 'deviceConfig.connectedXiaozhiHotspot': 'Connected to xiaozhi hotspot',
+ 'deviceConfig.wifiNetwork': 'WiFi Network',
+ 'deviceConfig.wifiPassword': 'WiFi Password',
+ 'deviceConfig.scanning': 'Scanning',
+ 'deviceConfig.cancel': 'Cancel',
+ 'deviceConfig.clickRefreshScan': 'Please click Refresh Scan',
+ 'deviceConfig.esp32ConnectionCheckFailed': 'ESP32 connection check failed',
+ 'deviceConfig.startWifiConfig': 'Starting WiFi configuration',
+ 'deviceConfig.configSuccess': 'Configuration successful',
+ 'deviceConfig.deviceWillConnectTo': 'Device will connect to',
+ 'deviceConfig.deviceWillRestart': 'Device will restart',
+ 'deviceConfig.pleaseDisconnectXiaozhiHotspot': 'Please disconnect from xiaozhi hotspot',
+ 'deviceConfig.configFailed': 'Configuration failed',
+ 'deviceConfig.wifiConfigFailed': 'WiFi configuration failed',
+ 'deviceConfig.pleaseCheckNetworkConnection': 'Please check network connection',
+ 'deviceConfig.startWifiConfigButton': 'Start Configuration',
+ 'deviceConfig.configuring': 'Configuring...',
+ 'deviceConfig.wifiConfigInstructions': 'WiFi Configuration Instructions',
+ 'deviceConfig.phoneConnectXiaozhiHotspot': 'Phone connect to xiaozhi hotspot',
+ 'deviceConfig.selectTargetWifiNetwork': 'Select target WiFi network',
+ 'deviceConfig.enterWifiPasswordIfNeeded': 'Enter WiFi password if needed',
+ 'deviceConfig.clickStartConfigAndWait': 'Click Start Configuration and wait',
+ 'deviceConfig.afterConfigSuccessDeviceWillRestart': 'After successful configuration, device will automatically restart',
+ 'deviceConfig.audioPlaybackError': 'Audio playback error',
+ 'deviceConfig.playbackFailed': 'Playback failed',
+
+ // Voiceprint page
+ 'voiceprint.audioNotExist': 'Audio does not exist',
+ 'voiceprint.getAudioFailed': 'Failed to get audio',
+ 'voiceprint.audioPlayFailed': 'Audio playback failed',
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/i18n/index.ts b/xiaozhi-esp32-server/main/manager-mobile/src/i18n/index.ts
new file mode 100644
index 0000000..046b6b5
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/i18n/index.ts
@@ -0,0 +1,79 @@
+import type { Language } from '@/store/lang'
+import { ref } from 'vue'
+import { useLangStore } from '@/store/lang'
+
+import de from './de'
+import en from './en'
+import pt_BR from './pt_BR'
+import vi from './vi'
+// 瀵煎叆鍚勪釜璇█鐨勭炕璇戞枃浠�
+import zh_CN from './zh_CN'
+import zh_TW from './zh_TW'
+
+// 璇█鍖呮槧灏�
+const messages = {
+ zh_CN,
+ en,
+ zh_TW,
+ de,
+ vi,
+ pt_BR,
+}
+
+// 褰撳墠浣跨敤鐨勮瑷�
+const currentLang = ref<Language>('zh_CN')
+
+// 鍒濆鍖栬瑷�
+export function initI18n() {
+ const langStore = useLangStore()
+ currentLang.value = langStore.currentLang
+}
+
+// 鍒囨崲璇█
+export function changeLanguage(lang: Language) {
+ currentLang.value = lang
+ const langStore = useLangStore()
+ langStore.changeLang(lang)
+}
+
+// 鑾峰彇缈昏瘧鏂囨湰
+export function t(key: string, params?: Record<string, string | number>): string {
+ const langMessages = messages[currentLang.value]
+
+ // 鐩存帴鏌ユ壘鎵佸钩閿悕
+ if (langMessages && typeof langMessages === 'object' && key in langMessages) {
+ const value = langMessages[key]
+ if (typeof value === 'string') {
+ // 澶勭悊鍙傛暟鏇挎崲
+ if (params) {
+ let result = value
+ Object.entries(params).forEach(([paramKey, paramValue]) => {
+ const regex = new RegExp(`\{${paramKey}\}`, 'g')
+ result = result.replace(regex, String(paramValue))
+ })
+ return result
+ }
+ return value
+ }
+ return key
+ }
+
+ return key // 濡傛灉鎵句笉鍒板搴旂殑缈昏瘧锛岃繑鍥瀔ey鏈韩
+}
+
+// 鑾峰彇褰撳墠璇█
+export function getCurrentLanguage(): Language {
+ return currentLang.value
+}
+
+// 鑾峰彇鏀寔鐨勮瑷�鍒楄〃
+export function getSupportedLanguages(): { code: Language, name: string }[] {
+ return [
+ { code: 'zh_CN', name: '绠�浣撲腑鏂�' },
+ { code: 'en', name: 'English' },
+ { code: 'zh_TW', name: '绻侀珨涓枃' },
+ { code: 'de', name: 'Deutsch' },
+ { code: 'vi', name: 'Ti岷縩g Vi峄噒' },
+ { code: 'pt_BR', name: 'Portugu锚s (Brasil)' },
+ ]
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/i18n/pt_BR.ts b/xiaozhi-esp32-server/main/manager-mobile/src/i18n/pt_BR.ts
new file mode 100644
index 0000000..928ffc6
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/i18n/pt_BR.ts
@@ -0,0 +1,597 @@
+// Pacote de idioma Portugu锚s Brasileiro
+export default {
+ // TabBar
+ 'tabBar.home': 'In铆cio',
+ 'tabBar.deviceConfig': 'Config. de Rede',
+ 'tabBar.settings': 'Sistema',
+ // Settings page title
+ 'settings.title': 'Configura莽玫es',
+ // Login page
+ 'login.pageTitle': 'Entrar',
+ 'login.navigationTitle': 'Entrar',
+ 'login.fetchConfigError': 'Falha ao buscar configura莽茫o:',
+ 'login.selectLanguage': 'Selecionar Idioma',
+ 'login.selectLanguageTip': 'Pt',
+ 'login.welcomeBack': 'Bem-vindo de Volta',
+ 'login.pleaseLogin': 'Por favor, entre na sua conta',
+ 'login.enterUsername': 'Por favor, insira o nome de usu谩rio',
+ 'login.enterPassword': 'Por favor, insira a senha',
+ 'login.enterCaptcha': 'Por favor, insira o c贸digo de verifica莽茫o',
+ 'login.loginButton': 'Entrar',
+ 'login.loggingIn': 'Entrando...',
+ 'login.noAccount': 'Cadastrar',
+ 'login.enterPhone': 'Por favor, insira o n煤mero de telefone',
+ 'login.selectCountry': 'Selecionar Pa铆s/Regi茫o',
+ 'login.confirm': 'Confirmar',
+ 'login.serverSetting': 'Configura莽玫es do Servidor',
+ 'login.requiredUsername': 'O nome de usu谩rio n茫o pode estar vazio',
+ 'login.requiredPassword': 'A senha n茫o pode estar vazia',
+ 'login.requiredCaptcha': 'O c贸digo de verifica莽茫o n茫o pode estar vazio',
+ 'login.requiredMobile': 'Por favor, insira um n煤mero de telefone v谩lido',
+ 'login.captchaError': 'Erro no c贸digo de verifica莽茫o gr谩fico',
+ 'login.forgotPassword': 'Esqueceu a Senha',
+ 'login.userAgreement': 'Termos de Uso',
+ 'login.privacyPolicy': 'Pol铆tica de Privacidade',
+
+ // Register page
+ 'register.pageTitle': 'Cadastro',
+ 'register.createAccount': 'Criar Conta',
+ 'register.enterUsername': 'Por favor, insira o nome de usu谩rio',
+ 'register.enterPassword': 'Por favor, insira a senha',
+ 'register.confirmPassword': 'Por favor, confirme a senha',
+ 'register.enterPhone': 'Por favor, insira o n煤mero de telefone',
+ 'register.enterCode': 'Por favor, insira o c贸digo de verifica莽茫o',
+ 'register.getCode': 'Obter C贸digo',
+ 'register.agreeTerms': 'Li e concordo com os',
+ 'register.terms': 'Termos de Uso',
+ 'register.privacy': 'Pol铆tica de Privacidade',
+ 'register.registerButton': 'Cadastrar',
+ 'register.registering': 'Cadastrando...',
+ 'register.haveAccount': 'J谩 tem uma conta?',
+ 'register.loginNow': 'Entrar Agora',
+ 'register.selectCountry': 'Selecionar Pa铆s/Regi茫o',
+ 'register.confirm': 'Confirmar',
+ 'register.captchaSendSuccess': 'C贸digo de verifica莽茫o enviado com sucesso',
+
+ // Home page
+ 'home.pageTitle': 'In铆cio',
+ 'home.createAgent': 'Criar Agente',
+ 'home.agentName': 'Agente',
+ 'home.modelInfo': 'Info do Modelo',
+ 'home.lastActive': '脷ltima Atividade',
+ 'home.greeting': 'Ol谩 Jarvis',
+ 'home.subtitle': 'Vamos ter',
+ 'home.wonderfulDay': 'um dia maravilhoso!',
+ 'home.emptyState': 'Nenhum agente dispon铆vel',
+ 'home.deviceManagement': 'Gerenciamento de Dispositivos',
+ 'home.lastConversation': '脷ltima Conversa:',
+ 'home.delete': 'Excluir',
+ 'home.createFirstAgent': 'Clique no bot茫o + no canto inferior direito para criar seu primeiro agente',
+ 'home.dialogTitle': 'Criar Agente',
+ 'home.inputPlaceholder': 'ex: Assistente de Atendimento, Assistente de Voz, Perguntas e Respostas',
+ 'home.createError': 'O comprimento do nome deve estar entre 1 e 64 caracteres',
+ 'home.createNow': 'Criar Agora',
+ 'home.deleteConfirmTitle': 'Excluir Agente',
+ 'home.confirmDeleteAgent': 'A exclus茫o n茫o pode ser desfeita. Digite o nome do agente "{agentName}" para confirmar.',
+ 'home.deleteAgentNamePlaceholder': 'Digite o nome do agente',
+ 'home.deleteAgentNameMismatch': 'O nome do agente n茫o corresponde',
+ 'home.deleteAgentPasteForbidden': 'Digite o nome do agente manualmente. Colar n茫o 茅 permitido.',
+ 'home.deleteAgentMissingInfo': 'N茫o foi poss铆vel confirmar as informa莽玫es do agente. Atualize a p谩gina e tente novamente.',
+ 'home.deleteAgentSuccess': 'Agente exclu铆do com sucesso',
+ 'home.justNow': 'Agora mesmo',
+ 'home.minutesAgo': 'minutos atr谩s',
+ 'home.hoursAgo': 'horas atr谩s',
+ 'home.daysAgo': 'dias atr谩s',
+ 'home.languageModel': 'LLM',
+ 'home.voiceModel': 'TTS',
+
+ // Agent page
+ 'agent.pageTitle': 'Agente',
+ 'agent.roleConfig': 'Configura莽茫o de Papel',
+ 'agent.deviceManagement': 'Gerenciamento de Dispositivos',
+ 'agent.chatHistory': 'Hist贸rico de Conversas',
+ 'agent.voiceprintManagement': 'Gerenciamento de Impress茫o Vocal',
+ 'agent.editTitle': 'Editar Agente',
+ 'agent.toolsTitle': 'Editar Funcionalidades',
+ 'agent.voiceActivityDetection': 'Detec莽茫o de Atividade de Voz',
+ 'agent.speechRecognition': 'Reconhecimento de Fala',
+ 'agent.largeLanguageModel': 'Modelo de Linguagem Grande',
+ 'agent.save': 'Salvar',
+ 'agent.cancel': 'Cancelar',
+ // Agent Edit Page
+ 'agent.basicInfo': 'Informa莽玫es B谩sicas',
+ 'agent.agentName': 'Nome do Agente',
+ 'agent.inputAgentName': 'Por favor, insira o nome do agente',
+ 'agent.roleMode': 'Modo de Papel',
+ 'agent.agentTag': 'Etiquetas de corpo inteligente',
+ 'agent.addAgentTag': 'Adicionar etiqueta',
+ 'agent.inputAgentTag': 'Por favor, insira o r贸tulo do corpo inteligente',
+ 'agent.contextProvider': 'Contexto',
+ 'agent.contextProviderSuccess': '{count} fontes adicionadas com sucesso.',
+ 'agent.contextProviderDocLink': 'Como implantar provedor de contexto',
+ 'agent.editContextProvider': 'Editar Fonte',
+ 'agent.roleDescription': 'Descri莽茫o do Papel',
+ 'agent.inputRoleDescription': 'Por favor, insira a descri莽茫o do papel',
+ 'agent.modelConfig': 'Configura莽茫o do Modelo',
+ 'agent.vad': 'Detec莽茫o de Atividade de Voz',
+ 'agent.asr': 'Reconhecimento de Fala',
+ 'agent.llm': 'Modelo de Linguagem Principal',
+ 'agent.slm': 'Modelo de pequenos par芒metros',
+ 'agent.vllm': 'Modelo de Linguagem Visual',
+ 'agent.intent': 'Reconhecimento de Inten莽茫o',
+ 'agent.memory': 'Mem贸ria',
+ 'agent.voiceSettings': 'Configura莽玫es de Voz',
+ 'agent.tts': 'Texto para Fala',
+ 'agent.voiceprint': 'Voz do Agente',
+ 'agent.plugins': 'Plugins',
+ 'agent.editFunctions': 'Editar Fun莽玫es',
+ 'agent.historyMemory': 'Mem贸ria de Hist贸rico',
+ 'agent.memoryContent': 'Conte煤do da mem贸ria',
+ 'agent.saving': 'Salvando...',
+ 'agent.saveSuccess': 'Salvo com sucesso',
+ 'agent.saveFail': 'Falha ao salvar',
+ 'agent.ttsOptionsLoadFailed': 'N茫o foi poss铆vel carregar as op莽玫es de voz. As configura莽玫es anteriores foram mantidas.',
+ 'agent.loadFail': 'Falha ao carregar',
+ 'agent.pleaseInputAgentName': 'Por favor, insira o nome do agente',
+ 'agent.pleaseInputRoleDescription': 'Por favor, insira a descri莽茫o do papel',
+ 'agent.pleaseSelect': 'Por favor, selecione',
+ 'agent.reportText': 'Enviar Texto',
+ 'agent.reportTextVoice': 'Enviar Texto+Voz',
+ 'agent.reportMode': 'Modo de Envio',
+ 'agent.language': 'Idioma de Conversa',
+ 'agent.languageConfig': 'Configura莽茫o de Voz',
+ 'agent.ttsVolume': 'Volume',
+ 'agent.ttsRate': 'Taxa de Fala',
+ 'agent.ttsPitch': 'Tonalidade',
+ 'agent.volumeHint': '-100=M铆nimo, 0=Padr茫o, 100=M谩ximo',
+ 'agent.speedHint': '-100=Slower, 0=Standard, 100=Faster',
+ 'agent.pitchHint': '-100=Lowest, 0=Standard, 100=Highest',
+
+ // Hist贸rico de vers玫es do agente
+ 'agentSnapshot.title': 'Hist贸rico de Vers玫es',
+ 'agentSnapshot.empty': 'Ainda sem vers玫es',
+ 'agentSnapshot.emptyTip': 'Configura莽玫es salvas aparecer茫o aqui',
+ 'agentSnapshot.version': 'Vers茫o',
+ 'agentSnapshot.createdAt': 'Salvo em',
+ 'agentSnapshot.source': 'Origem',
+ 'agentSnapshot.changedFields': 'Altera莽玫es',
+ 'agentSnapshot.view': 'Ver',
+ 'agentSnapshot.restore': 'Restaurar',
+ 'agentSnapshot.delete': 'Excluir',
+ 'agentSnapshot.loadMore': 'Carregar Mais',
+ 'agentSnapshot.detailTitle': 'Detalhes da Altera莽茫o',
+ 'agentSnapshot.restorePreviewTitle': 'Pr茅via da Restaura莽茫o',
+ 'agentSnapshot.confirmRestore': 'Confirmar Restaura莽茫o',
+ 'agentSnapshot.currentVersion': 'Snapshot mais recente',
+ 'agentSnapshot.beforeChange': 'Antes',
+ 'agentSnapshot.afterChange': 'Depois',
+ 'agentSnapshot.beforeRestore': 'Antes da Restaura莽茫o',
+ 'agentSnapshot.afterRestore': 'Depois da Restaura莽茫o',
+ 'agentSnapshot.configValue': 'Valor da Configura莽茫o',
+ 'agentSnapshot.emptyValue': 'Nenhum',
+ 'agentSnapshot.secretRedacted': 'Segredo oculto',
+ 'agentSnapshot.redactedValueChanged': 'O valor est谩 oculto, mas foi alterado',
+ 'agentSnapshot.noChangedContent': 'Nenhuma altera莽茫o exib铆vel',
+ 'agentSnapshot.recordedChange': 'Alterado quando registrado',
+ 'agentSnapshot.noRestoreNeeded': 'A configura莽茫o atual j谩 corresponde a esta vers茫o',
+ 'agentSnapshot.unsavedChangesTitle': 'Altera莽玫es n茫o salvas',
+ 'agentSnapshot.unsavedChangesWarning': 'Continuar descartar谩 as altera莽玫es n茫o salvas desta p谩gina.',
+ 'agentSnapshot.discardAndRestore': 'Descartar e restaurar',
+ 'agentSnapshot.restoreConfirm': 'Restaurar para a vers茫o #{version}? A configura莽茫o atual continuar谩 dispon铆vel no hist贸rico.',
+ 'agentSnapshot.restoreMemoryWarning': 'Restaurar para uma vers茫o sem mem贸ria limpar谩 o hist贸rico de chat deste agente. Confirme o risco.',
+ 'agentSnapshot.restoreChatHistoryDestructiveWarning': 'Esta restaura莽茫o excluir谩 permanentemente o hist贸rico de chat existente deste agente. O hist贸rico de chat n茫o faz parte dos snapshots de configura莽茫o e n茫o pode ser recuperado pelo hist贸rico de vers玫es.',
+ 'agentSnapshot.restoreSuccess': 'Vers茫o restaurada',
+ 'agentSnapshot.restoreFailed': 'Falha ao restaurar vers茫o',
+ 'agentSnapshot.reloadAfterRestorePending': 'Recarregando a configura莽茫o e as tags restauradas. N茫o 茅 poss铆vel salvar at茅 a conclus茫o.',
+ 'agentSnapshot.reloadAfterRestoreFailed': 'N茫o foi poss铆vel recarregar a configura莽茫o ou as tags restauradas. O salvamento permanece desativado para impedir que dados antigos sobrescrevam a restaura莽茫o. Tente novamente.',
+ 'agentSnapshot.retryReload': 'Recarregar',
+ 'agentSnapshot.mutationBusy': 'A configura莽茫o est谩 sendo salva ou recarregada. Aguarde a conclus茫o antes de abrir o hist贸rico de vers玫es ou restaurar.',
+ 'agentSnapshot.deleteConfirm': 'Excluir vers茫o #{version}? Esta a莽茫o n茫o pode ser desfeita.',
+ 'agentSnapshot.deleteSuccess': 'Vers茫o exclu铆da',
+ 'agentSnapshot.deleteFailed': 'Falha ao excluir vers茫o',
+ 'agentSnapshot.fetchFailed': 'Falha ao buscar vers玫es',
+ 'agentSnapshot.detailFailed': 'Falha ao buscar detalhes da vers茫o',
+ 'agentSnapshot.correctWordCount': '{count} palavras de substitui莽茫o',
+ 'agentSnapshot.source.config': 'Configura莽茫o Salva',
+ 'agentSnapshot.source.current': 'Configura莽茫o Atual',
+ 'agentSnapshot.source.restore': 'Restaurado',
+ 'agentSnapshot.source.initial': 'Vers茫o Inicial',
+ 'agentSnapshot.field.initial': 'Snapshot Inicial',
+ 'agentSnapshot.field.agentCode': 'C贸digo do Agente',
+ 'agentSnapshot.field.agentName': 'Apelido',
+ 'agentSnapshot.field.asrModelId': 'Reconhecimento de Fala',
+ 'agentSnapshot.field.vadModelId': 'Detec莽茫o de Voz',
+ 'agentSnapshot.field.llmModelId': 'Modelo Principal',
+ 'agentSnapshot.field.slmModelId': 'Modelo Pequeno',
+ 'agentSnapshot.field.vllmModelId': 'Modelo Visual',
+ 'agentSnapshot.field.ttsModelId': 'Texto para Fala',
+ 'agentSnapshot.field.ttsVoiceId': 'Voz',
+ 'agentSnapshot.field.ttsLanguage': 'Idioma',
+ 'agentSnapshot.field.ttsVolume': 'Volume',
+ 'agentSnapshot.field.ttsRate': 'Velocidade',
+ 'agentSnapshot.field.ttsPitch': 'Tonalidade',
+ 'agentSnapshot.field.memModelId': 'Modo de Mem贸ria',
+ 'agentSnapshot.field.intentModelId': 'Reconhecimento de Inten莽茫o',
+ 'agentSnapshot.field.chatHistoryConf': 'Configura莽茫o do Hist贸rico',
+ 'agentSnapshot.field.systemPrompt': 'Descri莽茫o do Papel',
+ 'agentSnapshot.field.summaryMemory': 'Mem贸ria',
+ 'agentSnapshot.field.langCode': 'C贸digo do Idioma',
+ 'agentSnapshot.field.language': 'Idioma de Intera莽茫o',
+ 'agentSnapshot.field.sort': 'Ordena莽茫o',
+ 'agentSnapshot.field.functions': 'Plugins',
+ 'agentSnapshot.field.contextProviders': 'Fontes de Contexto',
+ 'agentSnapshot.field.correctWordFileIds': 'Palavras de Substitui莽茫o',
+ 'agentSnapshot.field.tagNames': 'Tags do Agente',
+ 'agentSnapshot.chatHistoryConf.none': 'N茫o registrar hist贸rico de chat',
+ 'agentSnapshot.chatHistoryConf.text': 'Reportar texto',
+ 'agentSnapshot.chatHistoryConf.textVoice': 'Reportar texto e voz',
+ 'agentSnapshot.model.Memory_nomem': 'Sem mem贸ria',
+ 'agentSnapshot.model.Memory_mem_local_short': 'Mem贸ria curta local',
+ 'agentSnapshot.model.Memory_mem0ai': 'Mem贸ria Mem0AI',
+ 'agentSnapshot.model.Memory_mem_report_only': 'Apenas reportar',
+ 'agentSnapshot.model.Intent_nointent': 'Sem reconhecimento de inten莽茫o',
+ 'agentSnapshot.model.Intent_intent_llm': 'Reconhecimento por LLM externo',
+ 'agentSnapshot.model.Intent_function_call': 'Chamada de fun莽茫o por LLM',
+
+ // Di谩logo de provedor de contexto
+ 'contextProviderDialog.title': 'Editar Fonte',
+ 'contextProviderDialog.noContextApi': 'Sem API de Contexto',
+ 'contextProviderDialog.add': 'Adicionar',
+ 'contextProviderDialog.apiUrl': 'URL da API',
+ 'contextProviderDialog.apiUrlPlaceholder': 'http://api.example.com/data',
+ 'contextProviderDialog.requestHeaders': 'Cabe莽alhos da Requisi莽茫o',
+ 'contextProviderDialog.headerKeyPlaceholder': 'Chave',
+ 'contextProviderDialog.headerValuePlaceholder': 'Valor',
+ 'contextProviderDialog.noHeaders': 'Sem Cabe莽alhos',
+ 'contextProviderDialog.addHeader': 'Adicionar Cabe莽alho',
+ 'contextProviderDialog.cancel': 'Cancelar',
+ 'contextProviderDialog.confirm': 'Confirmar',
+
+ // Di谩logo de adi莽茫o manual de dispositivo
+ 'manualAddDeviceDialog.title': 'Adicionar Dispositivo Manualmente',
+ 'manualAddDeviceDialog.deviceType': 'Tipo de Dispositivo',
+ 'manualAddDeviceDialog.deviceTypePlaceholder': 'Por favor, selecione o tipo de dispositivo',
+ 'manualAddDeviceDialog.firmwareVersion': 'Vers茫o do Firmware',
+ 'manualAddDeviceDialog.firmwareVersionPlaceholder': 'Por favor, insira a vers茫o do firmware',
+ 'manualAddDeviceDialog.macAddress': 'Endere莽o MAC',
+ 'manualAddDeviceDialog.macAddressPlaceholder': 'Por favor, insira o endere莽o MAC',
+ 'manualAddDeviceDialog.confirm': 'Confirmar',
+ 'manualAddDeviceDialog.cancel': 'Cancelar',
+ 'manualAddDeviceDialog.requiredMacAddress': 'Por favor, insira o endere莽o MAC',
+ 'manualAddDeviceDialog.invalidMacAddress': 'Por favor, insira o formato correto de endere莽o MAC, ex.: 00:1A:2B:3C:4D:5E',
+ 'manualAddDeviceDialog.requiredDeviceType': 'Por favor, selecione o tipo de dispositivo',
+ 'manualAddDeviceDialog.requiredFirmwareVersion': 'Por favor, insira a vers茫o do firmware',
+ 'manualAddDeviceDialog.getFirmwareTypeFailed': 'Falha ao obter tipo de firmware',
+ 'manualAddDeviceDialog.addSuccess': 'Dispositivo adicionado com sucesso',
+ 'manualAddDeviceDialog.addFailed': 'Falha ao adicionar',
+ 'manualAddDeviceDialog.bindWithCode': 'Vincular com C贸digo de 6 D铆gitos',
+
+ // Chat History Page
+ 'chatHistory.getChatSessions': 'Obter lista de sess玫es de conversa',
+ 'chatHistory.noSelectedAgent': 'Nenhum agente selecionado',
+ 'chatHistory.getChatSessionsFailed': 'Falha ao obter lista de sess玫es de conversa:',
+ 'chatHistory.unknownTime': 'Hora desconhecida',
+ 'chatHistory.justNow': 'Agora mesmo',
+ 'chatHistory.minutesAgo': '{minutes} minutos atr谩s',
+ 'chatHistory.hoursAgo': '{hours} horas atr谩s',
+ 'chatHistory.daysAgo': '{days} dias atr谩s',
+ 'chatHistory.conversationRecord': 'Registro de Conversas',
+ 'chatHistory.totalChats': '{count} conversas no total',
+ 'chatHistory.loading': 'Carregando...',
+ 'chatHistory.noMoreData': 'N茫o h谩 mais dados',
+ 'chatHistory.noChatRecords': 'Nenhum registro de conversa',
+ 'chatHistory.chatRecordsDescription': 'Os registros de conversas com agentes ser茫o exibidos aqui',
+ // Chat History Detail Page
+ 'chatHistory.pageTitle': 'Detalhe da Conversa',
+ 'chatHistory.assistantName': 'Assistente Inteligente',
+ 'chatHistory.userName': 'Usu谩rio',
+ 'chatHistory.aiAssistantName': 'Assistente IA',
+ 'chatHistory.loadFailed': 'Falha ao carregar hist贸rico de conversas',
+ 'chatHistory.parameterError': 'Erro de par芒metro da p谩gina',
+ 'chatHistory.invalidAudioId': 'ID de 谩udio inv谩lido',
+ 'chatHistory.audioPlayFailed': 'Falha na reprodu莽茫o de 谩udio',
+ 'chatHistory.playAudioFailed': 'Falha ao reproduzir 谩udio',
+
+ // Device management page
+ 'device.pageTitle': 'Gerenciamento de Dispositivos',
+ 'device.noDevices': 'Nenhum dispositivo dispon铆vel',
+ 'device.macAddress': 'Endere莽o MAC',
+ 'device.firmwareVersion': 'Vers茫o do Firmware',
+ 'device.lastConnected': '脷ltima Conversa',
+ 'device.otaUpdate': 'Atualiza莽茫o OTA',
+ 'device.unbind': 'Desvincular',
+ 'device.confirmUnbind': 'Confirmar',
+ 'device.bindDevice': 'Vincular Novo Dispositivo',
+ 'device.deviceType': 'Tipo de Dispositivo',
+ 'device.loading': 'Carregando...',
+ 'device.neverConnected': 'Nunca conectado',
+ 'device.justNow': 'Agora mesmo',
+ 'device.minutesAgo': '{minutes} minutos atr谩s',
+ 'device.hoursAgo': '{hours} horas atr谩s',
+ 'device.daysAgo': '{days} dias atr谩s',
+ 'device.otaAutoUpdateEnabled': 'Atualiza莽茫o autom谩tica OTA ativada',
+ 'device.otaAutoUpdateDisabled': 'Atualiza莽茫o autom谩tica OTA desativada',
+ 'device.operationFailed': 'Opera莽茫o falhou, por favor tente novamente',
+ 'device.deviceUnbound': 'Dispositivo desvinculado',
+ 'device.unbindFailed': 'Falha ao desvincular, por favor tente novamente',
+ 'device.unbindDevice': 'Desvincular Dispositivo',
+ 'device.confirmUnbindDevice': 'Tem certeza que deseja desvincular o dispositivo "{macAddress}"?',
+ 'device.cancel': 'Cancelar',
+ 'device.noDevice': 'Nenhum Dispositivo',
+ 'device.pleaseSelectAgent': 'Por favor, selecione um agente primeiro',
+ 'device.deviceBindSuccess': 'Dispositivo vinculado com sucesso!',
+ 'device.bindFailed': 'Falha ao vincular, por favor verifique se o c贸digo de verifica莽茫o est谩 correto',
+ 'device.enterDeviceCode': 'Por favor, insira o c贸digo de verifica莽茫o do dispositivo',
+ 'device.bindNow': 'Vincular Agora',
+ 'device.lastConnection': '脷ltima Conex茫o',
+ 'device.clickToBindFirstDevice': 'Clique no bot茫o + no canto inferior direito para vincular seu primeiro dispositivo',
+
+ // Common
+ 'common.success': 'Sucesso',
+ 'common.fail': 'Falhou',
+ 'common.loading': 'Carregando...',
+ 'common.confirm': 'Confirmar',
+ 'common.cancel': 'Cancelar',
+ 'common.delete': 'Excluir',
+ 'common.edit': 'Editar',
+ 'common.add': 'Adicionar',
+ 'common.pleaseSelect': 'Por favor, selecione',
+ 'common.unknownError': 'Erro desconhecido',
+ 'common.networkError': 'Erro de rede',
+
+ // Retrieve Password page
+ 'retrievePassword.title': 'Redefinir Senha',
+ 'retrievePassword.subtitle': 'Recupere a senha da sua conta pelo n煤mero de celular',
+ 'retrievePassword.mobileRequired': 'Por favor, insira o n煤mero de celular',
+ 'retrievePassword.inputCorrectMobile': 'Por favor, insira um n煤mero de celular v谩lido',
+ 'retrievePassword.captchaRequired': 'Por favor, insira o c贸digo de verifica莽茫o gr谩fico',
+ 'retrievePassword.mobileCaptchaRequired': 'Por favor, insira o c贸digo de verifica莽茫o por SMS',
+ 'retrievePassword.newPasswordRequired': 'Por favor, insira a nova senha',
+ 'retrievePassword.confirmNewPasswordRequired': 'Por favor, confirme a nova senha',
+ 'retrievePassword.passwordsNotMatch': 'As senhas n茫o coincidem',
+ 'retrievePassword.mobilePlaceholder': 'Por favor, insira o n煤mero de celular',
+ 'retrievePassword.captchaPlaceholder': 'Por favor, insira o c贸digo de verifica莽茫o gr谩fico',
+ 'retrievePassword.mobileCaptchaPlaceholder': 'Por favor, insira o c贸digo de verifica莽茫o por SMS',
+ 'retrievePassword.newPasswordPlaceholder': 'Por favor, insira a nova senha',
+ 'retrievePassword.confirmNewPasswordPlaceholder': 'Por favor, confirme a nova senha',
+ 'retrievePassword.getMobileCaptcha': 'Obter C贸digo',
+ 'retrievePassword.captchaSendSuccess': 'C贸digo de verifica莽茫o enviado com sucesso',
+ 'retrievePassword.passwordUpdateSuccess': 'Senha redefinida com sucesso',
+ 'retrievePassword.resetButton': 'Redefinir Senha',
+ 'retrievePassword.goToLogin': 'Voltar ao Login',
+
+ // SM2 encryption related error messages
+ 'sm2.publicKeyNotConfigured': 'Chave p煤blica SM2 n茫o configurada, por favor entre em contato com o administrador',
+ 'sm2.encryptionFailed': 'Falha na criptografia da senha',
+ 'sm2.keyGenerationFailed': 'Falha na gera莽茫o do par de chaves',
+ 'sm2.invalidPublicKey': 'Formato de chave p煤blica inv谩lido',
+ 'sm2.encryptionError': 'Ocorreu um erro durante a criptografia',
+ 'sm2.publicKeyRetry': 'Tentando obter a chave p煤blica novamente...',
+ 'sm2.publicKeyRetryFailed': 'Falha na nova tentativa de obter a chave p煤blica',
+
+ // Voiceprint page
+ 'voiceprint.noSelectedAgent': 'Nenhum agente selecionado',
+ 'voiceprint.pleaseSelectAgent': 'Por favor, selecione um agente primeiro',
+ 'voiceprint.fetchHistoryFailed': 'Falha ao buscar hist贸rico de conversas',
+ 'voiceprint.clickToSelectVector': 'Clique para selecionar vetor de impress茫o vocal',
+ 'voiceprint.pleaseInputName': 'Por favor, insira o nome',
+ 'voiceprint.pleaseSelectVector': 'Por favor, selecione o vetor de impress茫o vocal',
+ 'voiceprint.addSuccess': 'Adicionado com sucesso',
+ 'voiceprint.addFailed': 'Falha ao adicionar locutor',
+ 'voiceprint.editSuccess': 'Editado com sucesso',
+ 'voiceprint.editFailed': 'Falha ao editar locutor',
+ 'voiceprint.deleteConfirmMsg': 'Tem certeza que deseja excluir este locutor?',
+ 'voiceprint.deleteConfirmTitle': 'Confirmar Exclus茫o',
+ 'voiceprint.deleteSuccess': 'Exclu铆do com sucesso',
+ 'voiceprint.loading': 'Carregando...',
+ 'voiceprint.delete': 'Excluir',
+ 'voiceprint.emptyTitle': 'Nenhum dado de impress茫o vocal',
+ 'voiceprint.emptyDesc': 'Clique no bot茫o + no canto inferior direito para adicionar seu primeiro locutor',
+ 'voiceprint.addSpeaker': 'Adicionar Locutor',
+ 'voiceprint.voiceVector': 'Vetor de Impress茫o Vocal',
+ 'voiceprint.name': 'Nome',
+ 'voiceprint.description': 'Descri莽茫o',
+ 'voiceprint.pleaseInputDescription': 'Por favor, insira a descri莽茫o',
+ 'voiceprint.cancel': 'Cancelar',
+ 'voiceprint.save': 'Salvar',
+ 'voiceprint.editSpeaker': 'Editar Locutor',
+ 'voiceprint.selectVector': 'Selecionar Vetor de Impress茫o Vocal',
+ 'voiceprint.voiceprintInterfaceNotConfigured': 'Interface de impress茫o vocal n茫o configurada',
+
+ // Settings page
+ 'settings.pageTitle': 'Configura莽玫es',
+ 'settings.navigationTitle': 'Configura莽玫es',
+ 'settings.networkSettings': 'Configura莽玫es de Rede',
+ 'settings.serverApiUrl': 'URL da API do Servidor',
+ 'settings.validServerUrl': 'Por favor, insira um endere莽o de servidor v谩lido (come莽ando com http ou https e terminando com /xiaozhi)',
+ 'settings.saveSettings': 'Salvar Configura莽玫es',
+ 'settings.resetDefault': 'Restaurar Padr茫o',
+ 'settings.restartApp': 'Reiniciar App',
+ 'settings.restartNow': 'Reiniciar Agora',
+ 'settings.restartLater': 'Depois',
+ // About us
+ 'settings.aboutApp': 'Sobre o Console XiaoZhi',
+ 'settings.aboutContent': 'Console XiaoZhi\n\nUm aplicativo m贸vel de gerenciamento multiplataforma constru铆do com Vue.js 3 + uni-app, oferecendo gerenciamento de dispositivos, configura莽茫o de agentes e outras funcionalidades para o hardware inteligente xiaozhi ESP32.\n\n漏 2025 xiaozhi-esp32-server {version}',
+ 'settings.restartSuccess': 'Salvo, voc锚 pode reiniciar o app manualmente depois',
+ 'settings.serverUrlSavedAndCacheCleared': 'URL do servidor salva e cache limpo',
+ 'settings.resetToDefaultAndCacheCleared': 'Restaurado ao padr茫o e cache limpo',
+ 'settings.resetSuccess': 'Restaura莽茫o bem-sucedida',
+ 'settings.enterServerUrl': 'Por favor, insira a URL do servidor',
+ 'settings.clearCacheFailed': 'Falha ao limpar o cache',
+ 'settings.cacheManagement': 'Gerenciamento de Cache',
+ 'settings.totalCacheSize': 'Tamanho Total do Cache',
+ 'settings.appDataSize': 'Tamanho Total dos Dados do App',
+ 'settings.cacheClear': 'Limpar Cache',
+ 'settings.clearAllCache': 'Limpar todos os dados de cache',
+ 'settings.clearCache': 'Limpar Cache',
+ 'settings.modifyWillClearCache': 'Modifica莽玫es ir茫o limpar o cache',
+ 'settings.appInfo': 'Informa莽玫es do App',
+ 'settings.aboutUs': 'Sobre N贸s',
+ 'settings.appVersion': 'Vers茫o do App e Info da Equipe',
+ 'settings.confirmClear': 'Confirmar Limpeza',
+ 'settings.confirmClearMessage': 'Tem certeza que deseja limpar todo o cache? Isso excluir谩 todos os dados, incluindo o status de login, e exigir谩 novo login.',
+ 'settings.cacheCleared': 'Cache limpo com sucesso, redirecionando para a p谩gina de login',
+ 'settings.languageSettings': 'Configura莽玫es de Idioma',
+ 'settings.language': 'Idioma',
+ 'settings.selectLanguage': 'Selecionar Idioma',
+ 'settings.languageChanged': 'Idioma alterado com sucesso',
+
+ // Messages
+ 'message.loginSuccess': 'Login realizado com sucesso!',
+ 'message.loginFail': 'Falha no login',
+ 'message.registerSuccess': 'Cadastro realizado com sucesso',
+ 'message.registerFail': 'Falha no cadastro',
+ 'message.saveSuccess': 'Salvo com sucesso',
+ 'message.saveFail': 'Falha ao salvar',
+ 'message.deleteSuccess': 'Exclu铆do com sucesso',
+ 'message.deleteFail': 'Falha ao excluir',
+ 'message.bindSuccess': 'Vincula莽茫o realizada com sucesso',
+ 'message.bindFail': 'Falha na vincula莽茫o',
+ 'message.unbindSuccess': 'Desvincula莽茫o realizada com sucesso',
+ 'message.unbindFail': 'Falha na desvincula莽茫o',
+ 'message.networkError': 'Erro de rede, por favor verifique sua conex茫o',
+ 'message.serverError': 'Erro no servidor, por favor tente novamente mais tarde',
+ 'message.invalidAddress': 'O endere莽o pode ser inv谩lido, verifique se o servidor est谩 iniciado ou se a conex茫o de rede est谩 correta; Tamb茅m pode ser imposs铆vel enviar solicita莽玫es devido a problemas com o protocolo HTTPS',
+ 'message.languageChanged': 'Idioma alterado',
+ 'message.passwordError': 'Conta ou senha incorretos',
+ 'message.phoneRegistered': 'Este n煤mero de telefone j谩 est谩 cadastrado',
+
+ // Agent Tools Page
+ 'agent.tools.pageTitle': 'Ferramentas do Agente',
+ 'agent.tools.unselected': 'N茫o selecionado',
+ 'agent.tools.selected': 'Selecionado',
+ 'agent.tools.noMorePlugins': 'N茫o h谩 mais plugins',
+ 'agent.tools.pleaseSelectPlugin': 'Por favor, selecione a fun莽茫o do plugin',
+ 'agent.tools.builtInPlugins': 'Plugins Integrados',
+ 'agent.tools.mcpAccessPoint': 'Ponto de Acesso MCP',
+ 'agent.tools.copy': 'Copiar',
+ 'agent.tools.noTools': 'Nenhuma ferramenta dispon铆vel',
+ 'agent.tools.parameterConfig': 'Configura莽茫o',
+ 'agent.tools.noParamsNeeded': 'Nenhum par芒metro necess谩rio',
+ 'agent.tools.pleaseInput': 'Por favor, insira',
+ 'agent.tools.inputOneItemPerLine': 'Insira um item por linha',
+ 'agent.tools.pleaseInputValidJson': 'Por favor, insira um formato JSON v谩lido',
+ 'agent.tools.enableFunction': 'Ativar Fun莽茫o',
+ 'agent.tools.toggleFunction': 'Ativar ou desativar esta fun莽茫o',
+ 'agent.tools.jsonFormatError': 'Erro no formato JSON',
+ 'agent.tools.noMcpAddressToCopy': 'Nenhum endere莽o MCP para copiar',
+ 'agent.tools.mcpAddressCopied': 'Endere莽o MCP copiado para a 谩rea de transfer锚ncia',
+ 'agent.tools.copyFailed': 'Falha ao copiar, por favor tente novamente',
+ 'agent.tools.defaultValue': 'Valor padr茫o',
+ 'agent.tools.notSelected': 'N茫o selecionado',
+ 'agent.tools.clickToConfigure': 'Clique para configurar',
+ 'agent.tools.mcpEndpoint': 'Endpoint MCP',
+ 'agent.tools.eachLineOneItem': 'Insira um item por linha',
+
+ // Device Config page
+ 'deviceConfig.pageTitle': 'Configura莽茫o do Dispositivo',
+ 'deviceConfig.wifiConfig': 'Configura莽茫o WiFi',
+ 'deviceConfig.ultrasonicConfig': 'Configura莽茫o Ultrass么nica',
+ 'deviceConfig.selectConfigMethod': 'Selecionar M茅todo de Configura莽茫o',
+ 'deviceConfig.networkConfig': 'Configura莽茫o de Rede',
+ 'deviceConfig.selectedNetwork': 'Rede Selecionada',
+ 'deviceConfig.signal': 'Sinal',
+ 'deviceConfig.openNetwork': 'Rede Aberta',
+ 'deviceConfig.encryptedNetwork': 'Rede Criptografada',
+ 'deviceConfig.password': 'Senha',
+ 'deviceConfig.pleaseEnterPassword': 'Por favor, insira a senha do WiFi',
+ 'deviceConfig.startConfig': 'Iniciar Configura莽茫o',
+ 'deviceConfig.connectToXiaozhiHotspot': 'Por favor, conecte-se ao hotspot xiaozhi primeiro',
+ 'deviceConfig.detecting': 'Detectando...',
+ 'deviceConfig.reDetect': 'Detectar Novamente',
+ 'deviceConfig.alreadyConnected': 'Conectado ao hotspot xiaozhi',
+ 'deviceConfig.refreshStatus': 'Atualizar Status',
+ 'deviceConfig.wifiNetworks': 'Redes WiFi',
+ 'deviceConfig.selectWifiNetwork': 'Selecionar Rede WiFi',
+ 'deviceConfig.refreshScan': 'Atualizar Busca',
+ 'deviceConfig.noWifiNetworks': 'Nenhuma rede WiFi dispon铆vel',
+ 'deviceConfig.clickToRefreshScan': 'Por favor, clique em Atualizar Busca',
+ 'deviceConfig.signalStrong': 'Sinal Forte',
+ 'deviceConfig.signalGood': 'Sinal Bom',
+ 'deviceConfig.signalFair': 'Sinal Regular',
+ 'deviceConfig.signalWeak': 'Sinal Fraco',
+ 'deviceConfig.channel': 'Canal',
+ 'deviceConfig.about': 'aproximadamente',
+ 'deviceConfig.seconds': 'segundos',
+ 'deviceConfig.generating': 'Gerando...',
+ 'deviceConfig.playing': 'Reproduzindo...',
+ 'deviceConfig.generateAndPlaySoundWave': 'Gerar e Reproduzir Onda Sonora',
+ 'deviceConfig.playSoundWave': 'Reproduzir Onda Sonora',
+ 'deviceConfig.stopPlaying': 'Parar Reprodu莽茫o',
+ 'deviceConfig.autoLoopPlaySoundWave': 'Reproduzir Onda Sonora em Loop Autom谩tico',
+ 'deviceConfig.configAudioFile': 'Arquivo de 脕udio de Configura莽茫o',
+ 'deviceConfig.duration': 'Dura莽茫o',
+ 'deviceConfig.ultrasonicConfigInstructions': 'Instru莽玫es de Configura莽茫o Ultrass么nica',
+ 'deviceConfig.ensureWifiNetworkSelectedAndPasswordEntered': 'Certifique-se de que a rede WiFi foi selecionada e a senha foi inserida',
+ 'deviceConfig.clickGenerateAndPlaySoundWave': 'Clique em Gerar e Reproduzir Onda Sonora, o sistema codificar谩 as informa莽玫es de configura莽茫o em 谩udio',
+ 'deviceConfig.bringPhoneCloseToXiaozhiDevice': 'Aproxime o celular do dispositivo xiaozhi (dist芒ncia de 1-2 metros)',
+ 'deviceConfig.duringAudioPlaybackXiaozhiWillReceive': 'Durante a reprodu莽茫o do 谩udio, o xiaozhi receber谩 e decodificar谩 as informa莽玫es de configura莽茫o',
+ 'deviceConfig.afterConfigSuccessDeviceWillConnect': 'Ap贸s a configura莽茫o bem-sucedida, o dispositivo se conectar谩 automaticamente 脿 rede WiFi',
+ 'deviceConfig.usesAfskModulation': 'Utiliza tecnologia de modula莽茫o AFSK, transmitindo dados pelas frequ锚ncias de 1800Hz e 1500Hz',
+ 'deviceConfig.ensureModeratePhoneVolume': 'Por favor, certifique-se de que o volume do celular est谩 moderado para evitar interfer锚ncia de ru铆dos ambientais',
+ 'deviceConfig.generatingUltrasonicConfigAudio': 'Gerando 谩udio de configura莽茫o ultrass么nica',
+ 'deviceConfig.configData': 'Dados de configura莽茫o',
+ 'deviceConfig.dataBytesLength': 'Comprimento dos bytes de dados',
+ 'deviceConfig.bitStreamLength': 'Comprimento do fluxo de bits',
+ 'deviceConfig.base64Length': 'Comprimento Base64',
+ 'deviceConfig.audioFileTooLarge': 'Arquivo de 谩udio muito grande, por favor reduza o tamanho do SSID ou da senha',
+ 'deviceConfig.audioGenerationSuccess': '脕udio gerado com sucesso',
+ 'deviceConfig.samplePoints': 'Pontos de amostra',
+ 'deviceConfig.soundWaveGenerationSuccess': 'Onda sonora gerada com sucesso',
+ 'deviceConfig.audioGenerationFailed': 'Falha na gera莽茫o do 谩udio',
+ 'deviceConfig.soundWaveGenerationFailed': 'Falha na gera莽茫o da onda sonora',
+ 'deviceConfig.pleaseGenerateAudioFirst': 'Por favor, gere o 谩udio primeiro',
+ 'deviceConfig.startPlayingUltrasonicConfigAudio': 'Iniciando reprodu莽茫o do 谩udio de configura莽茫o ultrass么nica',
+ 'deviceConfig.ultrasonicAudioStartedPlaying': 'Reprodu莽茫o do 谩udio ultrass么nico iniciada',
+ 'deviceConfig.startPlayingConfigSoundWave': 'Reprodu莽茫o da onda sonora de configura莽茫o iniciada',
+ 'deviceConfig.ultrasonicAudioPlaybackEnded': 'Reprodu莽茫o do 谩udio ultrass么nico finalizada',
+ 'deviceConfig.audioPlaybackFailed': 'Falha na reprodu莽茫o do 谩udio',
+ 'deviceConfig.audioResourceBusy': 'Recurso de 谩udio ocupado, por favor tente novamente mais tarde',
+ 'deviceConfig.audioFormatNotSupported': 'Formato de 谩udio n茫o suportado, pode ser um problema de URI de dados',
+ 'deviceConfig.audioFileError': 'Erro no arquivo de 谩udio',
+ 'deviceConfig.cleaningUpAudioContext': 'Limpando contexto de 谩udio',
+ 'deviceConfig.cleaningUpAudioContextFailed': 'Falha ao limpar contexto de 谩udio',
+ 'deviceConfig.stoppedPlayingUltrasonicAudio': 'Reprodu莽茫o do 谩udio ultrass么nico interrompida',
+ 'deviceConfig.stoppedPlaying': 'Reprodu莽茫o interrompida',
+ 'deviceConfig.configMethod': 'M茅todo de Configura莽茫o',
+ 'deviceConfig.enterWifiPassword': 'Por favor, insira a senha do WiFi',
+ 'deviceConfig.xiaozhi': 'xiaozhi',
+ 'deviceConfig.connectXiaozhiHotspot': 'Por favor, conecte-se ao hotspot xiaozhi',
+ 'deviceConfig.wifiScanResponse': 'Resposta da busca WiFi',
+ 'deviceConfig.scanSuccess': 'Busca bem-sucedida',
+ 'deviceConfig.networks': 'redes',
+ 'deviceConfig.wifiScanFailed': 'Falha na busca WiFi',
+ 'deviceConfig.scanFailedCheckConnection': 'Falha na busca, por favor verifique a conex茫o',
+ 'deviceConfig.checking': 'Verificando',
+ 'deviceConfig.reCheck': 'Verificar Novamente',
+ 'deviceConfig.connectedXiaozhiHotspot': 'Conectado ao hotspot xiaozhi',
+ 'deviceConfig.wifiNetwork': 'Rede WiFi',
+ 'deviceConfig.wifiPassword': 'Senha do WiFi',
+ 'deviceConfig.scanning': 'Buscando',
+ 'deviceConfig.cancel': 'Cancelar',
+ 'deviceConfig.clickRefreshScan': 'Por favor, clique em Atualizar Busca',
+ 'deviceConfig.esp32ConnectionCheckFailed': 'Falha na verifica莽茫o de conex茫o ESP32',
+ 'deviceConfig.startWifiConfig': 'Iniciando configura莽茫o WiFi',
+ 'deviceConfig.configSuccess': 'Configura莽茫o bem-sucedida',
+ 'deviceConfig.deviceWillConnectTo': 'O dispositivo se conectar谩 a',
+ 'deviceConfig.deviceWillRestart': 'O dispositivo ser谩 reiniciado',
+ 'deviceConfig.pleaseDisconnectXiaozhiHotspot': 'Por favor, desconecte-se do hotspot xiaozhi',
+ 'deviceConfig.configFailed': 'Falha na configura莽茫o',
+ 'deviceConfig.wifiConfigFailed': 'Falha na configura莽茫o WiFi',
+ 'deviceConfig.pleaseCheckNetworkConnection': 'Por favor, verifique a conex茫o de rede',
+ 'deviceConfig.startWifiConfigButton': 'Iniciar Configura莽茫o',
+ 'deviceConfig.configuring': 'Configurando...',
+ 'deviceConfig.wifiConfigInstructions': 'Instru莽玫es de Configura莽茫o WiFi',
+ 'deviceConfig.phoneConnectXiaozhiHotspot': 'Conecte o celular ao hotspot xiaozhi',
+ 'deviceConfig.selectTargetWifiNetwork': 'Selecione a rede WiFi de destino',
+ 'deviceConfig.enterWifiPasswordIfNeeded': 'Insira a senha do WiFi se necess谩rio',
+ 'deviceConfig.clickStartConfigAndWait': 'Clique em Iniciar Configura莽茫o e aguarde',
+ 'deviceConfig.afterConfigSuccessDeviceWillRestart': 'Ap贸s a configura莽茫o bem-sucedida, o dispositivo reiniciar谩 automaticamente',
+ 'deviceConfig.audioPlaybackError': 'Erro na reprodu莽茫o de 谩udio',
+ 'deviceConfig.playbackFailed': 'Falha na reprodu莽茫o',
+
+ // Voiceprint page
+ 'voiceprint.audioNotExist': 'O 谩udio n茫o existe',
+ 'voiceprint.getAudioFailed': 'Falha ao obter 谩udio',
+ 'voiceprint.audioPlayFailed': 'Falha na reprodu莽茫o de 谩udio',
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/i18n/vi.ts b/xiaozhi-esp32-server/main/manager-mobile/src/i18n/vi.ts
new file mode 100644
index 0000000..5c89966
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/i18n/vi.ts
@@ -0,0 +1,597 @@
+// G贸i ng么n ng峄� ti岷縩g Vi峄噒
+export default {
+ // TabBar
+ 'tabBar.home': 'Trang ch峄�',
+ 'tabBar.deviceConfig': 'C岷 h矛nh m岷g',
+ 'tabBar.settings': 'H峄� th峄憂g',
+ // Ti锚u 膽峄� trang c脿i 膽岷穞
+ 'settings.title': 'C脿i 膽岷穞',
+ // Trang 膽膬ng nh岷璸
+ 'login.pageTitle': '膼膬ng nh岷璸',
+ 'login.navigationTitle': '膼膬ng nh岷璸',
+ 'login.fetchConfigError': 'Kh么ng th峄� t岷 c岷 h矛nh:',
+ 'login.selectLanguage': 'Ch峄峮 ng么n ng峄�',
+ 'login.selectLanguageTip': 'Vi',
+ 'login.welcomeBack': 'Ch脿o m峄玭g tr峄� l岷',
+ 'login.pleaseLogin': 'Vui l貌ng 膽膬ng nh岷璸 v脿o t脿i kho岷 c峄 b岷',
+ 'login.enterUsername': 'Vui l貌ng nh岷璸 t锚n 膽膬ng nh岷璸',
+ 'login.enterPassword': 'Vui l貌ng nh岷璸 m岷璽 kh岷﹗',
+ 'login.enterCaptcha': 'Vui l貌ng nh岷璸 m茫 x谩c minh',
+ 'login.loginButton': '膼膬ng nh岷璸',
+ 'login.loggingIn': '膼ang 膽膬ng nh岷璸...',
+ 'login.noAccount': '膼膬ng k媒',
+ 'login.enterPhone': 'Vui l貌ng nh岷璸 s峄� 膽i峄噉 tho岷',
+ 'login.selectCountry': 'Ch峄峮 qu峄慶 gia/v霉ng',
+ 'login.confirm': 'X谩c nh岷璶',
+ 'login.serverSetting': 'C脿i 膽岷穞 m谩y ch峄�',
+ 'login.requiredUsername': 'T锚n 膽膬ng nh岷璸 kh么ng th峄� 膽峄� tr峄憂g',
+ 'login.requiredPassword': 'M岷璽 kh岷﹗ kh么ng th峄� 膽峄� tr峄憂g',
+ 'login.requiredCaptcha': 'M茫 x谩c minh kh么ng th峄� 膽峄� tr峄憂g',
+ 'login.requiredMobile': 'Vui l貌ng nh岷璸 s峄� 膽i峄噉 tho岷 h峄 l峄�',
+ 'login.captchaError': 'L峄梚 m茫 x谩c minh 膽峄� h峄峚',
+ 'login.forgotPassword': 'Qu锚n m岷璽 kh岷﹗',
+ 'login.userAgreement': 'Th峄廰 thu岷璶 ng瓢峄漣 d霉ng',
+ 'login.privacyPolicy': 'Ch铆nh s谩ch b岷 m岷璽',
+
+ // Trang 膽膬ng k媒
+ 'register.pageTitle': '膼膬ng k媒',
+ 'register.createAccount': 'T岷 t脿i kho岷',
+ 'register.enterUsername': 'Vui l貌ng nh岷璸 t锚n 膽膬ng nh岷璸',
+ 'register.enterPassword': 'Vui l貌ng nh岷璸 m岷璽 kh岷﹗',
+ 'register.confirmPassword': 'Vui l貌ng x谩c nh岷璶 m岷璽 kh岷﹗',
+ 'register.enterPhone': 'Vui l貌ng nh岷璸 s峄� 膽i峄噉 tho岷',
+ 'register.enterCode': 'Vui l貌ng nh岷璸 m茫 x谩c minh',
+ 'register.getCode': 'L岷 m茫',
+ 'register.agreeTerms': 'T么i 膽茫 膽峄峜 v脿 膽峄搉g 媒 v峄沬',
+ 'register.terms': 'Th峄廰 thu岷璶 ng瓢峄漣 d霉ng',
+ 'register.privacy': 'Ch铆nh s谩ch b岷 m岷璽',
+ 'register.registerButton': '膼膬ng k媒',
+ 'register.registering': '膼ang 膽膬ng k媒...',
+ 'register.haveAccount': '膼茫 c贸 t脿i kho岷?',
+ 'register.loginNow': '膼膬ng nh岷璸 ngay',
+ 'register.selectCountry': 'Ch峄峮 qu峄慶 gia/v霉ng',
+ 'register.confirm': 'X谩c nh岷璶',
+ 'register.captchaSendSuccess': '膼茫 g峄璱 m茫 x谩c minh th脿nh c么ng',
+
+ // Trang ch峄�
+ 'home.pageTitle': 'Trang ch峄�',
+ 'home.createAgent': 'T岷 膽岷 l媒',
+ 'home.agentName': '膼岷 l媒',
+ 'home.modelInfo': 'Th么ng tin m么 h矛nh',
+ 'home.lastActive': 'Ho岷 膽峄檔g l岷 cu峄慽',
+ 'home.greeting': 'Xin ch脿o Jarvis',
+ 'home.subtitle': 'H茫y c霉ng c贸',
+ 'home.wonderfulDay': 'm峄檛 ng脿y tuy峄噒 v峄漣!',
+ 'home.emptyState': 'Kh么ng c贸 膽岷 l媒 n脿o',
+ 'home.deviceManagement': 'Qu岷 l媒 thi岷縯 b峄�',
+ 'home.lastConversation': 'Cu峄檆 tr貌 chuy峄噉 cu峄慽:',
+ 'home.delete': 'X贸a',
+ 'home.createFirstAgent': 'Nh岷 v脿o n煤t + 峄� g贸c d瓢峄沬 b锚n ph岷 膽峄� t岷 膽岷 l媒 膽岷 ti锚n c峄 b岷',
+ 'home.dialogTitle': 'T岷 膽岷 l媒',
+ 'home.inputPlaceholder': 'v铆 d峄�: Tr峄� l媒 ch膬m s贸c kh谩ch h脿ng, Tr峄� l媒 gi峄峮g n贸i, H峄廼 膽谩p ki岷縩 th峄ヽ',
+ 'home.createError': '膼峄� d脿i t锚n ph岷 t峄� 1 膽岷縩 64 k媒 t峄�',
+ 'home.createNow': 'T岷 ngay',
+ 'home.deleteConfirmTitle': 'X贸a 膽岷 l媒',
+ 'home.confirmDeleteAgent': 'Kh么ng th峄� ho脿n t谩c sau khi x贸a. Nh岷璸 t锚n 膽岷 l媒 "{agentName}" 膽峄� x谩c nh岷璶.',
+ 'home.deleteAgentNamePlaceholder': 'Nh岷璸 t锚n 膽岷 l媒',
+ 'home.deleteAgentNameMismatch': 'T锚n 膽岷 l媒 kh么ng kh峄沺',
+ 'home.deleteAgentPasteForbidden': 'Vui l貌ng nh岷璸 th峄� c么ng t锚n 膽岷 l媒. Kh么ng 膽瓢峄 d谩n.',
+ 'home.deleteAgentMissingInfo': 'Kh么ng th峄� x谩c nh岷璶 th么ng tin 膽岷 l媒. Vui l貌ng t岷 l岷 r峄搃 th峄� l岷.',
+ 'home.deleteAgentSuccess': '膼茫 x贸a 膽岷 l媒 th脿nh c么ng',
+ 'home.justNow': 'V峄玜 xong',
+ 'home.minutesAgo': 'ph煤t tr瓢峄沜',
+ 'home.hoursAgo': 'gi峄� tr瓢峄沜',
+ 'home.daysAgo': 'ng脿y tr瓢峄沜',
+ 'home.languageModel': 'LLM',
+ 'home.voiceModel': 'TTS',
+
+ // Trang 膽岷 l媒
+ 'agent.pageTitle': '膼岷 l媒',
+ 'agent.roleConfig': 'C岷 h矛nh vai tr貌',
+ 'agent.deviceManagement': 'Qu岷 l媒 thi岷縯 b峄�',
+ 'agent.chatHistory': 'L峄媍h s峄� tr貌 chuy峄噉',
+ 'agent.voiceprintManagement': 'Qu岷 l媒 d岷 gi峄峮g n贸i',
+ 'agent.editTitle': 'Ch峄塶h s峄璦 膽岷 l媒',
+ 'agent.toolsTitle': 'Ch峄塶h s峄璦 t铆nh n膬ng',
+ 'agent.voiceActivityDetection': 'Ph谩t hi峄噉 ho岷 膽峄檔g gi峄峮g n贸i',
+ 'agent.speechRecognition': 'Nh岷璶 d岷g gi峄峮g n贸i',
+ 'agent.largeLanguageModel': 'M么 h矛nh ng么n ng峄� l峄沶',
+ 'agent.save': 'L瓢u',
+ 'agent.cancel': 'H峄',
+ // Trang ch峄塶h s峄璦 膽岷 l媒
+ 'agent.basicInfo': 'Th么ng tin c啤 b岷',
+ 'agent.agentName': 'T锚n 膽岷 l媒',
+ 'agent.inputAgentName': 'Vui l貌ng nh岷璸 t锚n 膽岷 l媒',
+ 'agent.roleMode': 'Ch岷� 膽峄� vai tr貌',
+ 'agent.agentTag': 'Nh茫n c啤 th峄� th么ng minh',
+ 'agent.addAgentTag': 'Th锚m th岷�',
+ 'agent.inputAgentTag': 'Vui l貌ng nh岷璸 th岷� c啤 th峄� th么ng minh',
+ 'agent.contextProvider': 'B峄慽 c岷h',
+ 'agent.contextProviderSuccess': '膼茫 th锚m th脿nh c么ng {count} ngu峄搉.',
+ 'agent.contextProviderDocLink': 'C谩ch tri峄僴 khai ngu峄搉 ng峄� c岷h',
+ 'agent.editContextProvider': 'Ch峄塶h s峄璦 ngu峄搉',
+ 'agent.roleDescription': 'M么 t岷� vai tr貌',
+ 'agent.inputRoleDescription': 'Vui l貌ng nh岷璸 m么 t岷� vai tr貌',
+ 'agent.modelConfig': 'C岷 h矛nh m么 h矛nh',
+ 'agent.vad': 'Ph谩t hi峄噉 ho岷 膽峄檔g gi峄峮g n贸i',
+ 'agent.asr': 'Nh岷璶 d岷g gi峄峮g n贸i',
+ 'agent.llm': 'M么 h矛nh ng么n ng峄� ch铆nh',
+ 'agent.slm': 'M么 h矛nh tham s峄� nh峄�',
+ 'agent.vllm': 'M么 h矛nh ng么n ng峄� th峄� gi谩c',
+ 'agent.intent': 'Nh岷璶 d岷g 媒 膽峄媙h',
+ 'agent.memory': 'B峄� nh峄�',
+ 'agent.voiceSettings': 'C脿i 膽岷穞 gi峄峮g n贸i',
+ 'agent.tts': 'V膬n b岷 th脿nh gi峄峮g n贸i',
+ 'agent.voiceprint': 'Gi峄峮g n贸i 膽岷 l媒',
+ 'agent.plugins': 'Plugin',
+ 'agent.editFunctions': 'Ch峄塶h s峄璦 t铆nh n膬ng',
+ 'agent.historyMemory': 'B峄� nh峄� l峄媍h s峄�',
+ 'agent.memoryContent': 'N峄檌 dung b峄� nh峄�',
+ 'agent.saving': '膼ang l瓢u...',
+ 'agent.saveSuccess': 'L瓢u th脿nh c么ng',
+ 'agent.saveFail': 'L瓢u th岷 b岷',
+ 'agent.ttsOptionsLoadFailed': 'Kh么ng th峄� t岷 t霉y ch峄峮 gi峄峮g n贸i. C岷 h矛nh gi峄峮g n贸i tr瓢峄沜 膽贸 膽茫 膽瓢峄 gi峄� l岷.',
+ 'agent.loadFail': 'T岷 th岷 b岷',
+ 'agent.pleaseInputAgentName': 'Vui l貌ng nh岷璸 t锚n 膽岷 l媒',
+ 'agent.pleaseInputRoleDescription': 'Vui l貌ng nh岷璸 m么 t岷� vai tr貌',
+ 'agent.pleaseSelect': 'Vui l貌ng ch峄峮',
+ 'agent.reportText': 'G峄璱 v膬n b岷',
+ 'agent.reportTextVoice': 'G峄璱 v膬n b岷+gi峄峮g n贸i',
+ 'agent.reportMode': 'Ch岷� 膽峄� g峄璱',
+ 'agent.language': 'Ng么n ng峄�',
+ 'agent.languageConfig': 'Gi峄峮g n贸i',
+ 'agent.ttsVolume': '脗m l瓢峄g',
+ 'agent.ttsRate': 'T峄慶 膽峄�',
+ 'agent.ttsPitch': 'Tone',
+ 'agent.volumeHint': '-100=Minimum, 0=Standard, 100=Maximum',
+ 'agent.speedHint': '-100=Slowest, 0=Standard, 100=Fastest',
+ 'agent.pitchHint': '-100=Lowest, 0=Standard, 100=Highest',
+
+ // L峄媍h s峄� phi锚n b岷 膽岷 l媒
+ 'agentSnapshot.title': 'L峄媍h s峄� phi锚n b岷',
+ 'agentSnapshot.empty': 'Ch瓢a c贸 phi锚n b岷',
+ 'agentSnapshot.emptyTip': 'C岷 h矛nh 膽茫 l瓢u s岷� xu岷 hi峄噉 峄� 膽芒y',
+ 'agentSnapshot.version': 'Phi锚n b岷',
+ 'agentSnapshot.createdAt': 'Th峄漣 gian l瓢u',
+ 'agentSnapshot.source': 'Ngu峄搉',
+ 'agentSnapshot.changedFields': 'Thay 膽峄昳',
+ 'agentSnapshot.view': 'Xem',
+ 'agentSnapshot.restore': 'Kh么i ph峄',
+ 'agentSnapshot.delete': 'X贸a',
+ 'agentSnapshot.loadMore': 'T岷 th锚m',
+ 'agentSnapshot.detailTitle': 'Chi ti岷縯 thay 膽峄昳',
+ 'agentSnapshot.restorePreviewTitle': 'Xem tr瓢峄沜 kh么i ph峄',
+ 'agentSnapshot.confirmRestore': 'X谩c nh岷璶 kh么i ph峄',
+ 'agentSnapshot.currentVersion': '岷h ch峄 m峄沬 nh岷',
+ 'agentSnapshot.beforeChange': 'Tr瓢峄沜',
+ 'agentSnapshot.afterChange': 'Sau',
+ 'agentSnapshot.beforeRestore': 'Tr瓢峄沜 kh么i ph峄',
+ 'agentSnapshot.afterRestore': 'Sau kh么i ph峄',
+ 'agentSnapshot.configValue': 'Gi谩 tr峄� c岷 h矛nh',
+ 'agentSnapshot.emptyValue': 'Kh么ng c贸',
+ 'agentSnapshot.secretRedacted': '膼茫 岷﹏ kh贸a b铆 m岷璽',
+ 'agentSnapshot.redactedValueChanged': 'Gi谩 tr峄� 膽茫 膽瓢峄 岷﹏ nh瓢ng th峄眂 s峄� 膽茫 thay 膽峄昳',
+ 'agentSnapshot.noChangedContent': 'Kh么ng c贸 thay 膽峄昳 膽峄� hi峄僴 th峄�',
+ 'agentSnapshot.recordedChange': '膼茫 thay 膽峄昳 khi ghi nh岷璶',
+ 'agentSnapshot.noRestoreNeeded': 'C岷 h矛nh hi峄噉 t岷 膽茫 kh峄沺 v峄沬 phi锚n b岷 n脿y',
+ 'agentSnapshot.unsavedChangesTitle': 'C贸 thay 膽峄昳 ch瓢a l瓢u',
+ 'agentSnapshot.unsavedChangesWarning': 'Ti岷縫 t峄 s岷� h峄 c谩c thay 膽峄昳 ch瓢a l瓢u tr锚n trang n脿y.',
+ 'agentSnapshot.discardAndRestore': 'H峄 thay 膽峄昳 v脿 kh么i ph峄',
+ 'agentSnapshot.restoreConfirm': 'Kh么i ph峄 v峄� phi锚n b岷 #{version}? C岷 h矛nh hi峄噉 t岷 s岷� v岷玭 c贸 trong l峄媍h s峄�.',
+ 'agentSnapshot.restoreMemoryWarning': 'Kh么i ph峄 v峄� phi锚n b岷 kh么ng c贸 b峄� nh峄� s岷� x贸a l峄媍h s峄� tr貌 chuy峄噉 c峄 膽岷 l媒 n脿y. Vui l貌ng x谩c nh岷璶 r峄 ro.',
+ 'agentSnapshot.restoreChatHistoryDestructiveWarning': 'Thao t谩c kh么i ph峄 n脿y s岷� x贸a v末nh vi峄卬 l峄媍h s峄� tr貌 chuy峄噉 hi峄噉 c贸 c峄 t谩c nh芒n. L峄媍h s峄� tr貌 chuy峄噉 kh么ng n岷眒 trong b岷 ch峄 c岷 h矛nh v脿 kh么ng th峄� kh么i ph峄 t峄� l峄媍h s峄� phi锚n b岷.',
+ 'agentSnapshot.restoreSuccess': '膼茫 kh么i ph峄 phi锚n b岷',
+ 'agentSnapshot.restoreFailed': 'Kh么i ph峄 phi锚n b岷 th岷 b岷',
+ 'agentSnapshot.reloadAfterRestorePending': '膼ang t岷 l岷 c岷 h矛nh v脿 th岷� 膽茫 kh么i ph峄. Kh么ng th峄� l瓢u cho 膽岷縩 khi ho脿n t岷.',
+ 'agentSnapshot.reloadAfterRestoreFailed': 'Kh么ng th峄� t岷 l岷 c岷 h矛nh ho岷穋 th岷� 膽茫 kh么i ph峄. Ch峄ヽ n膬ng l瓢u v岷玭 b峄� t岷痶 膽峄� tr谩nh d峄� li峄噓 c农 ghi 膽猫 k岷縯 qu岷� kh么i ph峄. Vui l貌ng th峄� l岷.',
+ 'agentSnapshot.retryReload': 'T岷 l岷',
+ 'agentSnapshot.mutationBusy': 'C岷 h矛nh 膽ang 膽瓢峄 l瓢u ho岷穋 t岷 l岷. H茫y 膽峄 ho脿n t岷 tr瓢峄沜 khi m峄� l峄媍h s峄� phi锚n b岷 ho岷穋 kh么i ph峄.',
+ 'agentSnapshot.deleteConfirm': 'X贸a phi锚n b岷 #{version}? Kh么ng th峄� ho脿n t谩c thao t谩c n脿y.',
+ 'agentSnapshot.deleteSuccess': '膼茫 x贸a phi锚n b岷',
+ 'agentSnapshot.deleteFailed': 'X贸a phi锚n b岷 th岷 b岷',
+ 'agentSnapshot.fetchFailed': 'L岷 l峄媍h s峄� phi锚n b岷 th岷 b岷',
+ 'agentSnapshot.detailFailed': 'L岷 chi ti岷縯 phi锚n b岷 th岷 b岷',
+ 'agentSnapshot.correctWordCount': '{count} t峄� thay th岷�',
+ 'agentSnapshot.source.config': 'L瓢u c岷 h矛nh',
+ 'agentSnapshot.source.current': 'C岷 h矛nh hi峄噉 t岷',
+ 'agentSnapshot.source.restore': '膼茫 kh么i ph峄',
+ 'agentSnapshot.source.initial': 'Phi锚n b岷 kh峄焛 t岷',
+ 'agentSnapshot.field.initial': '岷h ch峄 ban 膽岷',
+ 'agentSnapshot.field.agentCode': 'M茫 膽岷 l媒',
+ 'agentSnapshot.field.agentName': 'Bi峄噒 danh',
+ 'agentSnapshot.field.asrModelId': 'Nh岷璶 d岷g gi峄峮g n贸i',
+ 'agentSnapshot.field.vadModelId': 'Ph谩t hi峄噉 ho岷 膽峄檔g gi峄峮g n贸i',
+ 'agentSnapshot.field.llmModelId': 'M么 h矛nh ng么n ng峄� ch铆nh',
+ 'agentSnapshot.field.slmModelId': 'M么 h矛nh ng么n ng峄� nh峄�',
+ 'agentSnapshot.field.vllmModelId': 'M么 h矛nh th峄� gi谩c',
+ 'agentSnapshot.field.ttsModelId': 'T峄昻g h峄 gi峄峮g n贸i',
+ 'agentSnapshot.field.ttsVoiceId': 'Gi峄峮g',
+ 'agentSnapshot.field.ttsLanguage': 'Ng么n ng峄�',
+ 'agentSnapshot.field.ttsVolume': '脗m l瓢峄g',
+ 'agentSnapshot.field.ttsRate': 'T峄慶 膽峄�',
+ 'agentSnapshot.field.ttsPitch': 'Tone',
+ 'agentSnapshot.field.memModelId': 'Ch岷� 膽峄� b峄� nh峄�',
+ 'agentSnapshot.field.intentModelId': 'Nh岷璶 d岷g 媒 膽峄媙h',
+ 'agentSnapshot.field.chatHistoryConf': 'C岷 h矛nh l峄媍h s峄� chat',
+ 'agentSnapshot.field.systemPrompt': 'M么 t岷� vai tr貌',
+ 'agentSnapshot.field.summaryMemory': 'B峄� nh峄�',
+ 'agentSnapshot.field.langCode': 'M茫 ng么n ng峄�',
+ 'agentSnapshot.field.language': 'Ng么n ng峄� t瓢啤ng t谩c',
+ 'agentSnapshot.field.sort': 'S岷痯 x岷縫',
+ 'agentSnapshot.field.functions': 'Plugin',
+ 'agentSnapshot.field.contextProviders': 'Ngu峄搉 ng峄� c岷h',
+ 'agentSnapshot.field.correctWordFileIds': 'T峄� thay th岷�',
+ 'agentSnapshot.field.tagNames': 'Nh茫n 膽岷 l媒',
+ 'agentSnapshot.chatHistoryConf.none': 'Kh么ng ghi l峄媍h s峄� chat',
+ 'agentSnapshot.chatHistoryConf.text': 'G峄璱 v膬n b岷',
+ 'agentSnapshot.chatHistoryConf.textVoice': 'G峄璱 v膬n b岷 v脿 gi峄峮g n贸i',
+ 'agentSnapshot.model.Memory_nomem': 'Kh么ng c贸 b峄� nh峄�',
+ 'agentSnapshot.model.Memory_mem_local_short': 'B峄� nh峄� ng岷痭 h岷 c峄 b峄�',
+ 'agentSnapshot.model.Memory_mem0ai': 'B峄� nh峄� Mem0AI',
+ 'agentSnapshot.model.Memory_mem_report_only': 'Ch峄� g峄璱 b谩o c谩o',
+ 'agentSnapshot.model.Intent_nointent': 'Kh么ng nh岷璶 d岷g 媒 膽峄媙h',
+ 'agentSnapshot.model.Intent_intent_llm': 'Nh岷璶 d岷g 媒 膽峄媙h LLM ngo脿i',
+ 'agentSnapshot.model.Intent_function_call': 'G峄峣 h脿m b岷眓g LLM',
+
+ // Context provider dialog related
+ 'contextProviderDialog.title': 'Ch峄塶h s峄璦 ngu峄搉',
+ 'contextProviderDialog.noContextApi': 'Kh么ng c贸 API ng峄� c岷h',
+ 'contextProviderDialog.add': 'Th锚m',
+ 'contextProviderDialog.apiUrl': '膼峄媋 ch峄� API',
+ 'contextProviderDialog.apiUrlPlaceholder': 'http://api.example.com/data',
+ 'contextProviderDialog.requestHeaders': 'Header y锚u c岷',
+ 'contextProviderDialog.headerKeyPlaceholder': 'Kh贸a',
+ 'contextProviderDialog.headerValuePlaceholder': 'Gi谩 tr峄�',
+ 'contextProviderDialog.noHeaders': 'Kh么ng c贸 Headers',
+ 'contextProviderDialog.addHeader': 'Th锚m Header',
+ 'contextProviderDialog.cancel': 'H峄 b峄�',
+ 'contextProviderDialog.confirm': 'X谩c nh岷璶',
+
+ // Manual add device dialog related
+ 'manualAddDeviceDialog.title': 'Th锚m thi岷縯 b峄� th峄� c么ng',
+ 'manualAddDeviceDialog.deviceType': 'Lo岷 thi岷縯 b峄�',
+ 'manualAddDeviceDialog.deviceTypePlaceholder': 'Vui l貌ng ch峄峮 lo岷 thi岷縯 b峄�',
+ 'manualAddDeviceDialog.firmwareVersion': 'Phi锚n b岷 firmware',
+ 'manualAddDeviceDialog.firmwareVersionPlaceholder': 'Vui l貌ng nh岷璸 phi锚n b岷 firmware',
+ 'manualAddDeviceDialog.macAddress': '膼峄媋 ch峄� Mac',
+ 'manualAddDeviceDialog.macAddressPlaceholder': 'Vui l貌ng nh岷璸 膽峄媋 ch峄� Mac',
+ 'manualAddDeviceDialog.confirm': 'X谩c nh岷璶',
+ 'manualAddDeviceDialog.cancel': 'H峄 b峄�',
+ 'manualAddDeviceDialog.requiredMacAddress': 'Vui l貌ng nh岷璸 膽峄媋 ch峄� Mac',
+ 'manualAddDeviceDialog.invalidMacAddress': 'Vui l貌ng nh岷璸 膽煤ng 膽峄媙h d岷g 膽峄媋 ch峄� Mac, v铆 d峄�: 00:1A:2B:3C:4D:5E',
+ 'manualAddDeviceDialog.requiredDeviceType': 'Vui l貌ng ch峄峮 lo岷 thi岷縯 b峄�',
+ 'manualAddDeviceDialog.requiredFirmwareVersion': 'Vui l貌ng nh岷璸 phi锚n b岷 firmware',
+ 'manualAddDeviceDialog.getFirmwareTypeFailed': 'Kh么ng th峄� l岷 lo岷 firmware',
+ 'manualAddDeviceDialog.addSuccess': '膼茫 th锚m thi岷縯 b峄� th脿nh c么ng',
+ 'manualAddDeviceDialog.addFailed': 'Th锚m th岷 b岷',
+ 'manualAddDeviceDialog.bindWithCode': 'Li锚n k岷縯 b岷眓g m茫 x谩c minh 6 ch峄� s峄�',
+
+ // Trang l峄媍h s峄� tr貌 chuy峄噉
+ 'chatHistory.getChatSessions': 'L岷 danh s谩ch phi锚n tr貌 chuy峄噉',
+ 'chatHistory.noSelectedAgent': 'Ch瓢a ch峄峮 膽岷 l媒',
+ 'chatHistory.getChatSessionsFailed': 'Kh么ng th峄� l岷 danh s谩ch phi锚n tr貌 chuy峄噉:',
+ 'chatHistory.unknownTime': 'Th峄漣 gian kh么ng x谩c 膽峄媙h',
+ 'chatHistory.justNow': 'V峄玜 xong',
+ 'chatHistory.minutesAgo': '{minutes} ph煤t tr瓢峄沜',
+ 'chatHistory.hoursAgo': '{hours} gi峄� tr瓢峄沜',
+ 'chatHistory.daysAgo': '{days} ng脿y tr瓢峄沜',
+ 'chatHistory.conversationRecord': 'B岷 ghi tr貌 chuy峄噉',
+ 'chatHistory.totalChats': 'T峄昻g c峄檔g {count} cu峄檆 tr貌 chuy峄噉',
+ 'chatHistory.loading': '膼ang t岷...',
+ 'chatHistory.noMoreData': 'Kh么ng c貌n d峄� li峄噓',
+ 'chatHistory.noChatRecords': 'Kh么ng c贸 b岷 ghi tr貌 chuy峄噉',
+ 'chatHistory.chatRecordsDescription': 'B岷 ghi tr貌 chuy峄噉 v峄沬 膽岷 l媒 s岷� hi峄僴 th峄� 峄� 膽芒y',
+ // Trang chi ti岷縯 l峄媍h s峄� tr貌 chuy峄噉
+ 'chatHistory.pageTitle': 'Chi ti岷縯 tr貌 chuy峄噉',
+ 'chatHistory.assistantName': 'Tr峄� l媒 th么ng minh',
+ 'chatHistory.userName': 'Ng瓢峄漣 d霉ng',
+ 'chatHistory.aiAssistantName': 'Tr峄� l媒 AI',
+ 'chatHistory.loadFailed': 'Kh么ng th峄� t岷 l峄媍h s峄� tr貌 chuy峄噉',
+ 'chatHistory.parameterError': 'L峄梚 tham s峄� trang',
+ 'chatHistory.invalidAudioId': 'ID 芒m thanh kh么ng h峄 l峄�',
+ 'chatHistory.audioPlayFailed': 'Ph谩t 芒m thanh th岷 b岷',
+ 'chatHistory.playAudioFailed': 'Kh么ng th峄� ph谩t 芒m thanh',
+
+ // Trang qu岷 l媒 thi岷縯 b峄�
+ 'device.pageTitle': 'Qu岷 l媒 thi岷縯 b峄�',
+ 'device.noDevices': 'Kh么ng c贸 thi岷縯 b峄� n脿o',
+ 'device.macAddress': '膼峄媋 ch峄� MAC',
+ 'device.firmwareVersion': 'Phi锚n b岷 firmware',
+ 'device.lastConnected': 'Cu峄檆 tr貌 chuy峄噉 cu峄慽',
+ 'device.otaUpdate': 'C岷璸 nh岷璽 OTA',
+ 'device.unbind': 'H峄 li锚n k岷縯',
+ 'device.confirmUnbind': 'X谩c nh岷璶',
+ 'device.bindDevice': 'Li锚n k岷縯 thi岷縯 b峄� m峄沬',
+ 'device.deviceType': 'Lo岷 thi岷縯 b峄�',
+ 'device.loading': '膼ang t岷...',
+ 'device.neverConnected': 'Ch瓢a k岷縯 n峄慽',
+ 'device.justNow': 'V峄玜 xong',
+ 'device.minutesAgo': '{minutes} ph煤t tr瓢峄沜',
+ 'device.hoursAgo': '{hours} gi峄� tr瓢峄沜',
+ 'device.daysAgo': '{days} ng脿y tr瓢峄沜',
+ 'device.otaAutoUpdateEnabled': '膼茫 b岷璽 c岷璸 nh岷璽 OTA t峄� 膽峄檔g',
+ 'device.otaAutoUpdateDisabled': '膼茫 t岷痶 c岷璸 nh岷璽 OTA t峄� 膽峄檔g',
+ 'device.operationFailed': 'Thao t谩c th岷 b岷, vui l貌ng th峄� l岷',
+ 'device.deviceUnbound': '膼茫 h峄 li锚n k岷縯 thi岷縯 b峄�',
+ 'device.unbindFailed': 'H峄 li锚n k岷縯 th岷 b岷, vui l貌ng th峄� l岷',
+ 'device.unbindDevice': 'H峄 li锚n k岷縯 thi岷縯 b峄�',
+ 'device.confirmUnbindDevice': 'B岷 c贸 ch岷痗 ch岷痭 mu峄憂 h峄 li锚n k岷縯 thi岷縯 b峄� "{macAddress}"?',
+ 'device.cancel': 'H峄',
+ 'device.noDevice': 'Kh么ng c贸 thi岷縯 b峄�',
+ 'device.pleaseSelectAgent': 'Vui l貌ng ch峄峮 m峄檛 膽岷 l媒 tr瓢峄沜',
+ 'device.deviceBindSuccess': 'Li锚n k岷縯 thi岷縯 b峄� th脿nh c么ng!',
+ 'device.bindFailed': 'Li锚n k岷縯 th岷 b岷, vui l貌ng ki峄僲 tra m茫 x谩c minh c贸 膽煤ng kh么ng',
+ 'device.enterDeviceCode': 'Vui l貌ng nh岷璸 m茫 x谩c minh thi岷縯 b峄�',
+ 'device.bindNow': 'Li锚n k岷縯 ngay',
+ 'device.lastConnection': 'K岷縯 n峄慽 cu峄慽',
+ 'device.clickToBindFirstDevice': 'Nh岷 v脿o n煤t + 峄� g贸c d瓢峄沬 b锚n ph岷 膽峄� li锚n k岷縯 thi岷縯 b峄� 膽岷 ti锚n c峄 b岷',
+
+ // Chung
+ 'common.success': 'Th脿nh c么ng',
+ 'common.fail': 'Th岷 b岷',
+ 'common.loading': '膼ang t岷...',
+ 'common.confirm': 'X谩c nh岷璶',
+ 'common.cancel': 'H峄',
+ 'common.delete': 'X贸a',
+ 'common.edit': 'Ch峄塶h s峄璦',
+ 'common.add': 'Th锚m',
+ 'common.pleaseSelect': 'Vui l貌ng ch峄峮',
+ 'common.unknownError': 'L峄梚 kh么ng x谩c 膽峄媙h',
+ 'common.networkError': 'L峄梚 m岷g',
+
+ // Trang kh么i ph峄 m岷璽 kh岷﹗
+ 'retrievePassword.title': '膼岷穞 l岷 m岷璽 kh岷﹗',
+ 'retrievePassword.subtitle': 'Kh么i ph峄 m岷璽 kh岷﹗ t脿i kho岷 qua s峄� 膽i峄噉 tho岷',
+ 'retrievePassword.mobileRequired': 'Vui l貌ng nh岷璸 s峄� 膽i峄噉 tho岷',
+ 'retrievePassword.inputCorrectMobile': 'Vui l貌ng nh岷璸 s峄� 膽i峄噉 tho岷 h峄 l峄�',
+ 'retrievePassword.captchaRequired': 'Vui l貌ng nh岷璸 m茫 x谩c minh 膽峄� h峄峚',
+ 'retrievePassword.mobileCaptchaRequired': 'Vui l貌ng nh岷璸 m茫 x谩c minh SMS',
+ 'retrievePassword.newPasswordRequired': 'Vui l貌ng nh岷璸 m岷璽 kh岷﹗ m峄沬',
+ 'retrievePassword.confirmNewPasswordRequired': 'Vui l貌ng x谩c nh岷璶 m岷璽 kh岷﹗ m峄沬',
+ 'retrievePassword.passwordsNotMatch': 'M岷璽 kh岷﹗ kh么ng kh峄沺',
+ 'retrievePassword.mobilePlaceholder': 'Vui l貌ng nh岷璸 s峄� 膽i峄噉 tho岷',
+ 'retrievePassword.captchaPlaceholder': 'Vui l貌ng nh岷璸 m茫 x谩c minh 膽峄� h峄峚',
+ 'retrievePassword.mobileCaptchaPlaceholder': 'Vui l貌ng nh岷璸 m茫 x谩c minh SMS',
+ 'retrievePassword.newPasswordPlaceholder': 'Vui l貌ng nh岷璸 m岷璽 kh岷﹗ m峄沬',
+ 'retrievePassword.confirmNewPasswordPlaceholder': 'Vui l貌ng x谩c nh岷璶 m岷璽 kh岷﹗ m峄沬',
+ 'retrievePassword.getMobileCaptcha': 'L岷 m茫',
+ 'retrievePassword.captchaSendSuccess': '膼茫 g峄璱 m茫 x谩c minh th脿nh c么ng',
+ 'retrievePassword.passwordUpdateSuccess': '膼岷穞 l岷 m岷璽 kh岷﹗ th脿nh c么ng',
+ 'retrievePassword.resetButton': '膼岷穞 l岷 m岷璽 kh岷﹗',
+ 'retrievePassword.goToLogin': 'Quay l岷 膽膬ng nh岷璸',
+
+ // Th么ng b谩o l峄梚 li锚n quan 膽岷縩 m茫 h贸a SM2
+ 'sm2.publicKeyNotConfigured': 'Kh贸a c么ng khai SM2 ch瓢a 膽瓢峄 c岷 h矛nh, vui l貌ng li锚n h峄� qu岷 tr峄� vi锚n',
+ 'sm2.encryptionFailed': 'M茫 h贸a m岷璽 kh岷﹗ th岷 b岷',
+ 'sm2.keyGenerationFailed': 'T岷 c岷穚 kh贸a th岷 b岷',
+ 'sm2.invalidPublicKey': '膼峄媙h d岷g kh贸a c么ng khai kh么ng h峄 l峄�',
+ 'sm2.encryptionError': '膼茫 x岷 ra l峄梚 khi m茫 h贸a',
+ 'sm2.publicKeyRetry': '膼ang th峄� l岷 l岷 kh贸a c么ng khai...',
+ 'sm2.publicKeyRetryFailed': 'Th峄� l岷 l岷 kh贸a c么ng khai th岷 b岷',
+
+ // Trang d岷 gi峄峮g n贸i
+ 'voiceprint.noSelectedAgent': 'Ch瓢a ch峄峮 膽岷 l媒',
+ 'voiceprint.pleaseSelectAgent': 'Vui l貌ng ch峄峮 m峄檛 膽岷 l媒 tr瓢峄沜',
+ 'voiceprint.fetchHistoryFailed': 'Kh么ng th峄� t岷 l峄媍h s峄� tr貌 chuy峄噉',
+ 'voiceprint.clickToSelectVector': 'Nh岷 膽峄� ch峄峮 vector d岷 gi峄峮g n贸i',
+ 'voiceprint.pleaseInputName': 'Vui l貌ng nh岷璸 t锚n',
+ 'voiceprint.pleaseSelectVector': 'Vui l貌ng ch峄峮 vector d岷 gi峄峮g n贸i',
+ 'voiceprint.addSuccess': 'Th锚m th脿nh c么ng',
+ 'voiceprint.addFailed': 'Th锚m ng瓢峄漣 n贸i th岷 b岷',
+ 'voiceprint.editSuccess': 'Ch峄塶h s峄璦 th脿nh c么ng',
+ 'voiceprint.editFailed': 'Ch峄塶h s峄璦 ng瓢峄漣 n贸i th岷 b岷',
+ 'voiceprint.deleteConfirmMsg': 'B岷 c贸 ch岷痗 ch岷痭 mu峄憂 x贸a ng瓢峄漣 n贸i n脿y?',
+ 'voiceprint.deleteConfirmTitle': 'X谩c nh岷璶 x贸a',
+ 'voiceprint.deleteSuccess': 'X贸a th脿nh c么ng',
+ 'voiceprint.loading': '膼ang t岷...',
+ 'voiceprint.delete': 'X贸a',
+ 'voiceprint.emptyTitle': 'Kh么ng c贸 d峄� li峄噓 d岷 gi峄峮g n贸i',
+ 'voiceprint.emptyDesc': 'Nh岷 v脿o n煤t + 峄� g贸c d瓢峄沬 b锚n ph岷 膽峄� th锚m ng瓢峄漣 n贸i 膽岷 ti锚n c峄 b岷',
+ 'voiceprint.addSpeaker': 'Th锚m ng瓢峄漣 n贸i',
+ 'voiceprint.voiceVector': 'Vector d岷 gi峄峮g n贸i',
+ 'voiceprint.name': 'T锚n',
+ 'voiceprint.description': 'M么 t岷�',
+ 'voiceprint.pleaseInputDescription': 'Vui l貌ng nh岷璸 m么 t岷�',
+ 'voiceprint.cancel': 'H峄',
+ 'voiceprint.save': 'L瓢u',
+ 'voiceprint.editSpeaker': 'Ch峄塶h s峄璦 ng瓢峄漣 n贸i',
+ 'voiceprint.selectVector': 'Ch峄峮 vector d岷 gi峄峮g n贸i',
+ 'voiceprint.voiceprintInterfaceNotConfigured': 'Giao di峄噉 d岷 gi峄峮g n贸i ch瓢a 膽瓢峄 c岷 h矛nh',
+
+ // Trang c脿i 膽岷穞
+ 'settings.pageTitle': 'C脿i 膽岷穞',
+ 'settings.navigationTitle': 'C脿i 膽岷穞',
+ 'settings.networkSettings': 'C脿i 膽岷穞 m岷g',
+ 'settings.serverApiUrl': 'URL API m谩y ch峄�',
+ 'settings.validServerUrl': 'Vui l貌ng nh岷璸 膽峄媋 ch峄� m谩y ch峄� h峄 l峄� (b岷痶 膽岷 b岷眓g http ho岷穋 https v脿 k岷縯 th煤c b岷眓g /xiaozhi)',
+ 'settings.saveSettings': 'L瓢u c脿i 膽岷穞',
+ 'settings.resetDefault': '膼岷穞 l岷 m岷穋 膽峄媙h',
+ 'settings.restartApp': 'Kh峄焛 膽峄檔g l岷 峄﹏g d峄g',
+ 'settings.restartNow': 'Kh峄焛 膽峄檔g l岷 ngay',
+ 'settings.restartLater': '膼峄� sau',
+ // V峄� ch煤ng t么i
+ 'settings.aboutApp': 'V峄� B岷g 膽i峄乽 khi峄僴 XiaoZhi',
+ 'settings.aboutContent': 'B岷g 膽i峄乽 khi峄僴 XiaoZhi\n\n峄╪g d峄g qu岷 l媒 di 膽峄檔g 膽a n峄乶 t岷g 膽瓢峄 x芒y d峄眓g v峄沬 Vue.js 3 + uni-app, cung c岷 qu岷 l媒 thi岷縯 b峄�, c岷 h矛nh 膽岷 l媒 v脿 c谩c ch峄ヽ n膬ng kh谩c cho ph岷 c峄﹏g th么ng minh xiaozhi ESP32.\n\n漏 2025 xiaozhi-esp32-server {version}',
+ 'settings.restartSuccess': '膼茫 l瓢u, b岷 c贸 th峄� t峄� kh峄焛 膽峄檔g l岷 峄﹏g d峄g sau',
+ 'settings.serverUrlSavedAndCacheCleared': '膼茫 l瓢u URL m谩y ch峄� v脿 x贸a b峄� nh峄� cache',
+ 'settings.resetToDefaultAndCacheCleared': '膼茫 膽岷穞 l岷 m岷穋 膽峄媙h v脿 x贸a b峄� nh峄� cache',
+ 'settings.resetSuccess': '膼岷穞 l岷 th脿nh c么ng',
+ 'settings.enterServerUrl': 'Vui l貌ng nh岷璸 URL m谩y ch峄�',
+ 'settings.clearCacheFailed': 'Kh么ng th峄� x贸a b峄� nh峄� cache',
+ 'settings.cacheManagement': 'Qu岷 l媒 b峄� nh峄� cache',
+ 'settings.totalCacheSize': 'T峄昻g k铆ch th瓢峄沜 b峄� nh峄� cache',
+ 'settings.appDataSize': 'T峄昻g k铆ch th瓢峄沜 d峄� li峄噓 峄﹏g d峄g',
+ 'settings.cacheClear': 'X贸a b峄� nh峄� cache',
+ 'settings.clearAllCache': 'X贸a t岷 c岷� d峄� li峄噓 b峄� nh峄� cache',
+ 'settings.clearCache': 'X贸a b峄� nh峄� cache',
+ 'settings.modifyWillClearCache': 'Thay 膽峄昳 s岷� x贸a b峄� nh峄� cache',
+ 'settings.appInfo': 'Th么ng tin 峄﹏g d峄g',
+ 'settings.aboutUs': 'V峄� ch煤ng t么i',
+ 'settings.appVersion': 'Phi锚n b岷 峄﹏g d峄g & Th么ng tin nh贸m',
+ 'settings.confirmClear': 'X谩c nh岷璶 x贸a',
+ 'settings.confirmClearMessage': 'B岷 c贸 ch岷痗 ch岷痭 mu峄憂 x贸a to脿n b峄� b峄� nh峄� cache? 膼i峄乽 n脿y s岷� x贸a t岷 c岷� d峄� li峄噓 bao g峄搈 tr岷g th谩i 膽膬ng nh岷璸 v脿 y锚u c岷 膽膬ng nh岷璸 l岷.',
+ 'settings.cacheCleared': '膼茫 x贸a b峄� nh峄� cache th脿nh c么ng, 膽ang chuy峄僴 h瓢峄沶g 膽岷縩 trang 膽膬ng nh岷璸',
+ 'settings.languageSettings': 'C脿i 膽岷穞 ng么n ng峄�',
+ 'settings.language': 'Ng么n ng峄�',
+ 'settings.selectLanguage': 'Ch峄峮 ng么n ng峄�',
+ 'settings.languageChanged': '膼茫 thay 膽峄昳 ng么n ng峄� th脿nh c么ng',
+
+ // Th么ng b谩o
+ 'message.loginSuccess': '膼膬ng nh岷璸 th脿nh c么ng!',
+ 'message.loginFail': '膼膬ng nh岷璸 th岷 b岷',
+ 'message.registerSuccess': '膼膬ng k媒 th脿nh c么ng',
+ 'message.registerFail': '膼膬ng k媒 th岷 b岷',
+ 'message.saveSuccess': 'L瓢u th脿nh c么ng',
+ 'message.saveFail': 'L瓢u th岷 b岷',
+ 'message.deleteSuccess': 'X贸a th脿nh c么ng',
+ 'message.deleteFail': 'X贸a th岷 b岷',
+ 'message.bindSuccess': 'Li锚n k岷縯 th脿nh c么ng',
+ 'message.bindFail': 'Li锚n k岷縯 th岷 b岷',
+ 'message.unbindSuccess': 'H峄 li锚n k岷縯 th脿nh c么ng',
+ 'message.unbindFail': 'H峄 li锚n k岷縯 th岷 b岷',
+ 'message.networkError': 'L峄梚 m岷g, vui l貌ng ki峄僲 tra k岷縯 n峄慽',
+ 'message.serverError': 'L峄梚 m谩y ch峄�, vui l貌ng th峄� l岷 sau',
+ 'message.invalidAddress': '膼峄媋 ch峄� c贸 th峄� kh么ng h峄 l峄�. Vui l貌ng ki峄僲 tra xem m谩y ch峄� 膽茫 kh峄焛 膽峄檔g hay k岷縯 n峄慽 m岷g ch瓢a; C贸 th峄� kh么ng g峄璱 膽瓢峄 y锚u c岷 do l峄梚 giao th峄ヽ HTTPS',
+ 'message.languageChanged': '膼茫 thay 膽峄昳 ng么n ng峄�',
+ 'message.passwordError': 'L峄梚 t脿i kho岷 ho岷穋 m岷璽 kh岷﹗',
+ 'message.phoneRegistered': 'S峄� 膽i峄噉 tho岷 n脿y 膽茫 膽瓢峄 膽膬ng k媒',
+
+ // Trang c么ng c峄� 膽岷 l媒
+ 'agent.tools.pageTitle': 'C么ng c峄� 膽岷 l媒',
+ 'agent.tools.unselected': 'Ch瓢a ch峄峮',
+ 'agent.tools.selected': '膼茫 ch峄峮',
+ 'agent.tools.noMorePlugins': 'Kh么ng c貌n plugin',
+ 'agent.tools.pleaseSelectPlugin': 'Vui l貌ng ch峄峮 ch峄ヽ n膬ng plugin',
+ 'agent.tools.builtInPlugins': 'Plugin t铆ch h峄',
+ 'agent.tools.mcpAccessPoint': '膼i峄僲 truy c岷璸 MCP',
+ 'agent.tools.copy': 'Sao ch茅p',
+ 'agent.tools.noTools': 'Kh么ng c贸 c么ng c峄� n脿o',
+ 'agent.tools.parameterConfig': 'C岷 h矛nh',
+ 'agent.tools.noParamsNeeded': 'Kh么ng c岷 tham s峄�',
+ 'agent.tools.pleaseInput': 'Vui l貌ng nh岷璸',
+ 'agent.tools.inputOneItemPerLine': 'Nh岷璸 m峄檛 m峄 m峄梚 d貌ng',
+ 'agent.tools.pleaseInputValidJson': 'Vui l貌ng nh岷璸 膽峄媙h d岷g JSON h峄 l峄�',
+ 'agent.tools.enableFunction': 'B岷璽 ch峄ヽ n膬ng',
+ 'agent.tools.toggleFunction': 'B岷璽 ho岷穋 t岷痶 ch峄ヽ n膬ng n脿y',
+ 'agent.tools.jsonFormatError': 'L峄梚 膽峄媙h d岷g JSON',
+ 'agent.tools.noMcpAddressToCopy': 'Kh么ng c贸 膽峄媋 ch峄� MCP 膽峄� sao ch茅p',
+ 'agent.tools.mcpAddressCopied': '膼茫 sao ch茅p 膽峄媋 ch峄� MCP v脿o b峄� nh峄� t岷',
+ 'agent.tools.copyFailed': 'Sao ch茅p th岷 b岷, vui l貌ng th峄� l岷',
+ 'agent.tools.defaultValue': 'Gi谩 tr峄� m岷穋 膽峄媙h',
+ 'agent.tools.notSelected': 'Ch瓢a ch峄峮',
+ 'agent.tools.clickToConfigure': 'Nh岷 膽峄� c岷 h矛nh',
+ 'agent.tools.mcpEndpoint': '膼i峄僲 cu峄慽 MCP',
+ 'agent.tools.eachLineOneItem': 'Nh岷璸 m峄檛 m峄 m峄梚 d貌ng',
+
+ // Trang c岷 h矛nh thi岷縯 b峄�
+ 'deviceConfig.pageTitle': 'C岷 h矛nh thi岷縯 b峄�',
+ 'deviceConfig.wifiConfig': 'C岷 h矛nh WiFi',
+ 'deviceConfig.ultrasonicConfig': 'C岷 h矛nh si锚u 芒m',
+ 'deviceConfig.selectConfigMethod': 'Ch峄峮 ph瓢啤ng ph谩p c岷 h矛nh',
+ 'deviceConfig.networkConfig': 'C岷 h矛nh m岷g',
+ 'deviceConfig.selectedNetwork': 'M岷g 膽茫 ch峄峮',
+ 'deviceConfig.signal': 'T铆n hi峄噓',
+ 'deviceConfig.openNetwork': 'M岷g m峄�',
+ 'deviceConfig.encryptedNetwork': 'M岷g m茫 h贸a',
+ 'deviceConfig.password': 'M岷璽 kh岷﹗',
+ 'deviceConfig.pleaseEnterPassword': 'Vui l貌ng nh岷璸 m岷璽 kh岷﹗ WiFi',
+ 'deviceConfig.startConfig': 'B岷痶 膽岷 c岷 h矛nh',
+ 'deviceConfig.connectToXiaozhiHotspot': 'Vui l貌ng k岷縯 n峄慽 v峄沬 膽i峄僲 ph谩t s贸ng xiaozhi tr瓢峄沜',
+ 'deviceConfig.detecting': '膼ang ph谩t hi峄噉...',
+ 'deviceConfig.reDetect': 'Ph谩t hi峄噉 l岷',
+ 'deviceConfig.alreadyConnected': '膼茫 k岷縯 n峄慽 v峄沬 膽i峄僲 ph谩t s贸ng xiaozhi',
+ 'deviceConfig.refreshStatus': 'L脿m m峄沬 tr岷g th谩i',
+ 'deviceConfig.wifiNetworks': 'M岷g WiFi',
+ 'deviceConfig.selectWifiNetwork': 'Ch峄峮 m岷g WiFi',
+ 'deviceConfig.refreshScan': 'L脿m m峄沬 qu茅t',
+ 'deviceConfig.noWifiNetworks': 'Kh么ng c贸 m岷g WiFi n脿o',
+ 'deviceConfig.clickToRefreshScan': 'Vui l貌ng nh岷 L脿m m峄沬 qu茅t',
+ 'deviceConfig.signalStrong': 'T铆n hi峄噓 m岷h',
+ 'deviceConfig.signalGood': 'T铆n hi峄噓 t峄憈',
+ 'deviceConfig.signalFair': 'T铆n hi峄噓 trung b矛nh',
+ 'deviceConfig.signalWeak': 'T铆n hi峄噓 y岷縰',
+ 'deviceConfig.channel': 'K锚nh',
+ 'deviceConfig.about': 'kho岷g',
+ 'deviceConfig.seconds': 'gi芒y',
+ 'deviceConfig.generating': '膼ang t岷...',
+ 'deviceConfig.playing': '膼ang ph谩t...',
+ 'deviceConfig.generateAndPlaySoundWave': 'T岷 v脿 ph谩t s贸ng 芒m',
+ 'deviceConfig.playSoundWave': 'Ph谩t s贸ng 芒m',
+ 'deviceConfig.stopPlaying': 'D峄玭g ph谩t',
+ 'deviceConfig.autoLoopPlaySoundWave': 'T峄� 膽峄檔g l岷穚 ph谩t s贸ng 芒m',
+ 'deviceConfig.configAudioFile': 'T峄噋 芒m thanh c岷 h矛nh',
+ 'deviceConfig.duration': 'Th峄漣 l瓢峄g',
+ 'deviceConfig.ultrasonicConfigInstructions': 'H瓢峄沶g d岷玭 c岷 h矛nh si锚u 芒m',
+ 'deviceConfig.ensureWifiNetworkSelectedAndPasswordEntered': '膼岷 b岷 m岷g WiFi 膽瓢峄 ch峄峮 v脿 m岷璽 kh岷﹗ 膽茫 nh岷璸',
+ 'deviceConfig.clickGenerateAndPlaySoundWave': 'Nh岷 T岷 v脿 ph谩t s贸ng 芒m, h峄� th峄憂g s岷� m茫 h贸a th么ng tin c岷 h矛nh th脿nh 芒m thanh',
+ 'deviceConfig.bringPhoneCloseToXiaozhiDevice': '膼瓢a 膽i峄噉 tho岷 l岷 g岷 thi岷縯 b峄� xiaozhi (kho岷g c谩ch 1-2 m茅t)',
+ 'deviceConfig.duringAudioPlaybackXiaozhiWillReceive': 'Trong khi ph谩t 芒m thanh, xiaozhi s岷� nh岷璶 v脿 gi岷 m茫 th么ng tin c岷 h矛nh',
+ 'deviceConfig.afterConfigSuccessDeviceWillConnect': 'Sau khi c岷 h矛nh th脿nh c么ng, thi岷縯 b峄� s岷� t峄� 膽峄檔g k岷縯 n峄慽 v峄沬 m岷g WiFi',
+ 'deviceConfig.usesAfskModulation': 'S峄� d峄g c么ng ngh峄� 膽i峄乽 ch岷� AFSK, truy峄乶 d峄� li峄噓 qua t岷 s峄� 1800Hz v脿 1500Hz',
+ 'deviceConfig.ensureModeratePhoneVolume': 'Vui l貌ng 膽岷 b岷 芒m l瓢峄g 膽i峄噉 tho岷 v峄玜 ph岷 膽峄� tr谩nh nhi峄卽 ti岷縩g 峄搉 m么i tr瓢峄漬g',
+ 'deviceConfig.generatingUltrasonicConfigAudio': '膼ang t岷 芒m thanh c岷 h矛nh si锚u 芒m',
+ 'deviceConfig.configData': 'D峄� li峄噓 c岷 h矛nh',
+ 'deviceConfig.dataBytesLength': '膼峄� d脿i byte d峄� li峄噓',
+ 'deviceConfig.bitStreamLength': '膼峄� d脿i lu峄搉g bit',
+ 'deviceConfig.base64Length': '膼峄� d脿i base64',
+ 'deviceConfig.audioFileTooLarge': 'T峄噋 芒m thanh qu谩 l峄沶, vui l貌ng r煤t ng岷痭 SSID ho岷穋 膽峄� d脿i m岷璽 kh岷﹗',
+ 'deviceConfig.audioGenerationSuccess': 'T岷 芒m thanh th脿nh c么ng',
+ 'deviceConfig.samplePoints': '膼i峄僲 m岷玼',
+ 'deviceConfig.soundWaveGenerationSuccess': 'T岷 s贸ng 芒m th脿nh c么ng',
+ 'deviceConfig.audioGenerationFailed': 'T岷 芒m thanh th岷 b岷',
+ 'deviceConfig.soundWaveGenerationFailed': 'T岷 s贸ng 芒m th岷 b岷',
+ 'deviceConfig.pleaseGenerateAudioFirst': 'Vui l貌ng t岷 芒m thanh tr瓢峄沜',
+ 'deviceConfig.startPlayingUltrasonicConfigAudio': 'B岷痶 膽岷 ph谩t 芒m thanh c岷 h矛nh si锚u 芒m',
+ 'deviceConfig.ultrasonicAudioStartedPlaying': '膼茫 b岷痶 膽岷 ph谩t 芒m thanh si锚u 芒m',
+ 'deviceConfig.startPlayingConfigSoundWave': '膼茫 b岷痶 膽岷 ph谩t s贸ng 芒m c岷 h矛nh',
+ 'deviceConfig.ultrasonicAudioPlaybackEnded': 'K岷縯 th煤c ph谩t 芒m thanh si锚u 芒m',
+ 'deviceConfig.audioPlaybackFailed': 'Ph谩t 芒m thanh th岷 b岷',
+ 'deviceConfig.audioResourceBusy': 'T脿i nguy锚n 芒m thanh b岷璶, vui l貌ng th峄� l岷 sau',
+ 'deviceConfig.audioFormatNotSupported': '膼峄媙h d岷g 芒m thanh kh么ng 膽瓢峄 h峄� tr峄�, c贸 th峄� l脿 v岷 膽峄� URI d峄� li峄噓',
+ 'deviceConfig.audioFileError': 'L峄梚 t峄噋 芒m thanh',
+ 'deviceConfig.cleaningUpAudioContext': '膼ang d峄峮 d岷筽 ng峄� c岷h 芒m thanh',
+ 'deviceConfig.cleaningUpAudioContextFailed': 'Kh么ng th峄� d峄峮 d岷筽 ng峄� c岷h 芒m thanh',
+ 'deviceConfig.stoppedPlayingUltrasonicAudio': '膼茫 d峄玭g ph谩t 芒m thanh si锚u 芒m',
+ 'deviceConfig.stoppedPlaying': '膼茫 d峄玭g ph谩t',
+ 'deviceConfig.configMethod': 'Ph瓢啤ng ph谩p c岷 h矛nh',
+ 'deviceConfig.enterWifiPassword': 'Vui l貌ng nh岷璸 m岷璽 kh岷﹗ WiFi',
+ 'deviceConfig.xiaozhi': 'xiaozhi',
+ 'deviceConfig.connectXiaozhiHotspot': 'Vui l貌ng k岷縯 n峄慽 v峄沬 膽i峄僲 ph谩t s贸ng xiaozhi',
+ 'deviceConfig.wifiScanResponse': 'Ph岷 h峄搃 qu茅t WiFi',
+ 'deviceConfig.scanSuccess': 'Qu茅t th脿nh c么ng',
+ 'deviceConfig.networks': 'm岷g',
+ 'deviceConfig.wifiScanFailed': 'Qu茅t WiFi th岷 b岷',
+ 'deviceConfig.scanFailedCheckConnection': 'Qu茅t th岷 b岷, vui l貌ng ki峄僲 tra k岷縯 n峄慽',
+ 'deviceConfig.checking': '膼ang ki峄僲 tra',
+ 'deviceConfig.reCheck': 'Ki峄僲 tra l岷',
+ 'deviceConfig.connectedXiaozhiHotspot': '膼茫 k岷縯 n峄慽 v峄沬 膽i峄僲 ph谩t s贸ng xiaozhi',
+ 'deviceConfig.wifiNetwork': 'M岷g WiFi',
+ 'deviceConfig.wifiPassword': 'M岷璽 kh岷﹗ WiFi',
+ 'deviceConfig.scanning': '膼ang qu茅t',
+ 'deviceConfig.cancel': 'H峄',
+ 'deviceConfig.clickRefreshScan': 'Vui l貌ng nh岷 L脿m m峄沬 qu茅t',
+ 'deviceConfig.esp32ConnectionCheckFailed': 'Ki峄僲 tra k岷縯 n峄慽 ESP32 th岷 b岷',
+ 'deviceConfig.startWifiConfig': '膼ang b岷痶 膽岷 c岷 h矛nh WiFi',
+ 'deviceConfig.configSuccess': 'C岷 h矛nh th脿nh c么ng',
+ 'deviceConfig.deviceWillConnectTo': 'Thi岷縯 b峄� s岷� k岷縯 n峄慽 v峄沬',
+ 'deviceConfig.deviceWillRestart': 'Thi岷縯 b峄� s岷� kh峄焛 膽峄檔g l岷',
+ 'deviceConfig.pleaseDisconnectXiaozhiHotspot': 'Vui l貌ng ng岷痶 k岷縯 n峄慽 kh峄廼 膽i峄僲 ph谩t s贸ng xiaozhi',
+ 'deviceConfig.configFailed': 'C岷 h矛nh th岷 b岷',
+ 'deviceConfig.wifiConfigFailed': 'C岷 h矛nh WiFi th岷 b岷',
+ 'deviceConfig.pleaseCheckNetworkConnection': 'Vui l貌ng ki峄僲 tra k岷縯 n峄慽 m岷g',
+ 'deviceConfig.startWifiConfigButton': 'B岷痶 膽岷 c岷 h矛nh',
+ 'deviceConfig.configuring': '膼ang c岷 h矛nh...',
+ 'deviceConfig.wifiConfigInstructions': 'H瓢峄沶g d岷玭 c岷 h矛nh WiFi',
+ 'deviceConfig.phoneConnectXiaozhiHotspot': '膼i峄噉 tho岷 k岷縯 n峄慽 v峄沬 膽i峄僲 ph谩t s贸ng xiaozhi',
+ 'deviceConfig.selectTargetWifiNetwork': 'Ch峄峮 m岷g WiFi m峄 ti锚u',
+ 'deviceConfig.enterWifiPasswordIfNeeded': 'Nh岷璸 m岷璽 kh岷﹗ WiFi n岷縰 c岷',
+ 'deviceConfig.clickStartConfigAndWait': 'Nh岷 B岷痶 膽岷 c岷 h矛nh v脿 ch峄�',
+ 'deviceConfig.afterConfigSuccessDeviceWillRestart': 'Sau khi c岷 h矛nh th脿nh c么ng, thi岷縯 b峄� s岷� t峄� 膽峄檔g kh峄焛 膽峄檔g l岷',
+ 'deviceConfig.audioPlaybackError': 'L峄梚 ph谩t 芒m thanh',
+ 'deviceConfig.playbackFailed': 'Ph谩t th岷 b岷',
+
+ // Voiceprint page
+ 'voiceprint.audioNotExist': '脗m thanh kh么ng t峄搉 t岷',
+ 'voiceprint.getAudioFailed': 'Kh么ng th峄� l岷 芒m thanh',
+ 'voiceprint.audioPlayFailed': 'Ph谩t 芒m thanh th岷 b岷',
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/i18n/zh_CN.ts b/xiaozhi-esp32-server/main/manager-mobile/src/i18n/zh_CN.ts
new file mode 100644
index 0000000..824bf4c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/i18n/zh_CN.ts
@@ -0,0 +1,597 @@
+// 绠�浣撲腑鏂囪瑷�鍖�
+export default {
+ // TabBar
+ 'tabBar.home': '棣栭〉',
+ 'tabBar.deviceConfig': '閰嶇綉',
+ 'tabBar.settings': '绯荤粺',
+ // 璁剧疆椤甸潰鏍囬
+ 'settings.title': '璁剧疆',
+ // 鐧诲綍椤甸潰
+ 'login.pageTitle': '鐧诲綍',
+ 'login.navigationTitle': '鐧诲綍',
+ 'login.fetchConfigError': '鑾峰彇閰嶇疆澶辫触:',
+ 'login.selectLanguage': '閫夋嫨璇█',
+ 'login.selectLanguageTip': '涓枃',
+ 'login.welcomeBack': '娆㈣繋鍥炴潵',
+ 'login.pleaseLogin': '璇风櫥褰曟偍鐨勮处鎴�',
+ 'login.enterUsername': '璇疯緭鍏ョ敤鎴峰悕',
+ 'login.enterPassword': '璇疯緭鍏ュ瘑鐮�',
+ 'login.enterCaptcha': '璇疯緭鍏ラ獙璇佺爜',
+ 'login.loginButton': '鐧诲綍',
+ 'login.loggingIn': '鐧诲綍涓�...',
+ 'login.noAccount': '鏂扮敤鎴锋敞鍐�',
+ 'login.enterPhone': '璇疯緭鍏ユ墜鏈哄彿鐮�',
+ 'login.selectCountry': '閫夋嫨鍥藉/鍦板尯',
+ 'login.confirm': '纭',
+ 'login.serverSetting': '鏈嶅姟绔缃�',
+ 'login.requiredUsername': '鐢ㄦ埛鍚嶄笉鑳戒负绌�',
+ 'login.requiredPassword': '瀵嗙爜涓嶈兘涓虹┖',
+ 'login.requiredCaptcha': '楠岃瘉鐮佷笉鑳戒负绌�',
+ 'login.requiredMobile': '璇疯緭鍏ユ纭殑鎵嬫満鍙风爜',
+ 'login.captchaError': '鍥惧舰楠岃瘉鐮侀敊璇�',
+ 'login.forgotPassword': '蹇樿瀵嗙爜',
+ 'login.userAgreement': '鐢ㄦ埛鍗忚',
+ 'login.privacyPolicy': '闅愮鏀跨瓥',
+
+ // 娉ㄥ唽椤甸潰
+ 'register.pageTitle': '娉ㄥ唽',
+ 'register.createAccount': '鍒涘缓璐︽埛',
+ 'register.enterUsername': '璇疯緭鍏ョ敤鎴峰悕',
+ 'register.enterPassword': '璇疯緭鍏ュ瘑鐮�',
+ 'register.confirmPassword': '璇风‘璁ゅ瘑鐮�',
+ 'register.enterPhone': '璇疯緭鍏ユ墜鏈哄彿鐮�',
+ 'register.enterCode': '璇疯緭鍏ラ獙璇佺爜',
+ 'register.getCode': '鑾峰彇楠岃瘉鐮�',
+ 'register.agreeTerms': '鎴戝凡闃呰骞跺悓鎰�',
+ 'register.terms': '銆婄敤鎴峰崗璁��',
+ 'register.privacy': '銆婇殣绉佹斂绛栥��',
+ 'register.registerButton': '娉ㄥ唽',
+ 'register.registering': '娉ㄥ唽涓�...',
+ 'register.haveAccount': '宸叉湁璐︽埛锛�',
+ 'register.loginNow': '绔嬪嵆鐧诲綍',
+ 'register.selectCountry': '閫夋嫨鍥藉/鍦板尯',
+ 'register.confirm': '纭',
+ 'register.captchaSendSuccess': '楠岃瘉鐮佸彂閫佹垚鍔�',
+
+ // 棣栭〉
+ 'home.pageTitle': '棣栭〉',
+ 'home.createAgent': '鍒涘缓鏅鸿兘浣�',
+ 'home.agentName': '鏅鸿兘浣�',
+ 'home.modelInfo': '妯″瀷淇℃伅',
+ 'home.lastActive': '鏈�杩戞椿璺�',
+ 'home.greeting': '浣犲ソ锛屽皬鏅�',
+ 'home.subtitle': '璁╂垜浠害杩�',
+ 'home.wonderfulDay': '缇庡ソ鐨勪竴澶╋紒',
+ 'home.emptyState': '鏆傛棤鏅鸿兘浣�',
+ 'home.deviceManagement': '璁惧绠$悊',
+ 'home.lastConversation': '鏈�杩戝璇濓細',
+ 'home.delete': '鍒犻櫎',
+ 'home.createFirstAgent': '鐐瑰嚮鍙充笅瑙� + 鍙峰垱寤烘偍鐨勭涓�涓櫤鑳戒綋',
+ 'home.dialogTitle': '鍒涘缓鏅鸿兘浣�',
+ 'home.inputPlaceholder': '渚嬪锛氬鏈嶅姪鎵嬨�佽闊冲姪鐞嗐�佺煡璇嗛棶绛�',
+ 'home.createError': '鍚嶇О闀垮害蹇呴』鍦� 1 鍒� 64 涓瓧绗︿箣闂�',
+ 'home.createNow': '绔嬪嵆鍒涘缓',
+ 'home.deleteConfirmTitle': '鍒犻櫎鏅鸿兘浣�',
+ 'home.confirmDeleteAgent': '鍒犻櫎鍚庢棤娉曟仮澶嶃�傝杈撳叆鏅鸿兘浣撳悕绉扳�渰agentName}鈥濈‘璁ゅ垹闄ゃ��',
+ 'home.deleteAgentNamePlaceholder': '璇疯緭鍏ユ櫤鑳戒綋鍚嶇О',
+ 'home.deleteAgentNameMismatch': '杈撳叆鐨勬櫤鑳戒綋鍚嶇О涓嶅尮閰�',
+ 'home.deleteAgentPasteForbidden': '璇锋墜鍔ㄨ緭鍏ユ櫤鑳戒綋鍚嶇О锛屼笉鍏佽绮樿创',
+ 'home.deleteAgentMissingInfo': '鏃犳硶纭鏅鸿兘浣撲俊鎭紝璇峰埛鏂板悗閲嶈瘯',
+ 'home.deleteAgentSuccess': '鏅鸿兘浣撳垹闄ゆ垚鍔�',
+ 'home.justNow': '鍒氬垰',
+ 'home.minutesAgo': '鍒嗛挓鍓�',
+ 'home.hoursAgo': '灏忔椂鍓�',
+ 'home.daysAgo': '澶╁墠',
+ 'home.languageModel': '璇█妯″瀷',
+ 'home.voiceModel': '闊宠壊妯″瀷',
+
+ // Agent椤甸潰
+ 'agent.pageTitle': '鏅鸿兘浣�',
+ 'agent.roleConfig': '瑙掕壊閰嶇疆',
+ 'agent.deviceManagement': '璁惧绠$悊',
+ 'agent.chatHistory': '鑱婂ぉ璁板綍',
+ 'agent.voiceprintManagement': '澹扮汗绠$悊',
+ 'agent.editTitle': '缂栬緫鏅鸿兘浣�',
+ 'agent.toolsTitle': '缂栬緫鍔熻兘',
+ 'agent.voiceActivityDetection': '璇煶娲诲姩妫�娴�',
+ 'agent.speechRecognition': '璇煶璇嗗埆',
+ 'agent.largeLanguageModel': '澶ц瑷�妯″瀷',
+ 'agent.save': '淇濆瓨',
+ 'agent.cancel': '鍙栨秷',
+ // Agent缂栬緫椤甸潰
+ 'agent.basicInfo': '鍩虹淇℃伅',
+ 'agent.agentName': '鍔╂墜鏄电О',
+ 'agent.inputAgentName': '璇疯緭鍏ュ姪鎵嬫樀绉�',
+ 'agent.roleMode': '瑙掕壊妯″紡',
+ 'agent.agentTag': '鏅鸿兘浣撴爣绛�',
+ 'agent.addAgentTag': '娣诲姞鏍囩',
+ 'agent.inputAgentTag': '璇疯緭鍏ユ櫤鑳戒綋鏍囩',
+ 'agent.contextProvider': '涓婁笅鏂囨簮',
+ 'agent.contextProviderSuccess': '宸叉垚鍔熸坊鍔� {count} 涓簮銆�',
+ 'agent.contextProviderDocLink': '濡備綍閮ㄧ讲涓婁笅鏂囨簮',
+ 'agent.editContextProvider': '缂栬緫婧�',
+ 'agent.roleDescription': '瑙掕壊浠嬬粛',
+ 'agent.inputRoleDescription': '璇疯緭鍏ヨ鑹蹭粙缁�',
+ 'agent.modelConfig': '妯″瀷閰嶇疆',
+ 'agent.vad': '璇煶娲诲姩妫�娴�',
+ 'agent.asr': '璇煶璇嗗埆',
+ 'agent.llm': '涓昏瑷�妯″瀷',
+ 'agent.slm': '灏忓弬鏁版ā鍨�',
+ 'agent.vllm': '瑙嗚澶фā鍨�',
+ 'agent.intent': '鎰忓浘璇嗗埆',
+ 'agent.memory': '璁板繂',
+ 'agent.voiceSettings': '璇煶璁剧疆',
+ 'agent.tts': '璇煶鍚堟垚',
+ 'agent.voiceprint': '瑙掕壊闊宠壊',
+ 'agent.plugins': '鎻掍欢',
+ 'agent.editFunctions': '缂栬緫鍔熻兘',
+ 'agent.historyMemory': '鍘嗗彶璁板繂',
+ 'agent.memoryContent': '璁板繂鍐呭',
+ 'agent.saving': '淇濆瓨涓�...',
+ 'agent.saveSuccess': '淇濆瓨鎴愬姛',
+ 'agent.saveFail': '淇濆瓨澶辫触',
+ 'agent.ttsOptionsLoadFailed': '璇煶閫夐」鍔犺浇澶辫触锛屽凡淇濈暀鍘熸潵鐨勮闊抽厤缃�',
+ 'agent.loadFail': '鍔犺浇澶辫触',
+ 'agent.pleaseInputAgentName': '璇疯緭鍏ユ櫤鑳戒綋鍚嶇О',
+ 'agent.pleaseInputRoleDescription': '璇疯緭鍏ヨ鑹蹭粙缁�',
+ 'agent.pleaseSelect': '璇烽�夋嫨',
+ 'agent.reportText': '涓婃姤鏂囧瓧',
+ 'agent.reportTextVoice': '涓婃姤鏂囧瓧+璇煶',
+ 'agent.reportMode': '涓婃姤妯″紡',
+ 'agent.language': '瀵硅瘽璇█',
+ 'agent.languageConfig': '璇�熼煶璋�',
+ 'agent.ttsVolume': '闊抽噺',
+ 'agent.ttsRate': '璇��',
+ 'agent.ttsPitch': '闊宠皟',
+ 'agent.volumeHint': '-100=鏈�灏�, 0=鏍囧噯, 100=鏈�澶�',
+ 'agent.speedHint': '-100=鏈�鎱�, 0=鏍囧噯, 100=鏈�蹇�',
+ 'agent.pitchHint': '-100=鏈�浣�, 0=鏍囧噯, 100=鏈�楂�',
+
+ // 鏅鸿兘浣撳巻鍙茬増鏈�
+ 'agentSnapshot.title': '鍘嗗彶鐗堟湰',
+ 'agentSnapshot.empty': '鏆傛棤鍘嗗彶鐗堟湰',
+ 'agentSnapshot.emptyTip': '淇濆瓨閰嶇疆鍚庝細鐢熸垚鍘嗗彶鐗堟湰',
+ 'agentSnapshot.version': '鐗堟湰',
+ 'agentSnapshot.createdAt': '淇濆瓨鏃堕棿',
+ 'agentSnapshot.source': '鏉ユ簮',
+ 'agentSnapshot.changedFields': '鍙樻洿鍐呭',
+ 'agentSnapshot.view': '鏌ョ湅',
+ 'agentSnapshot.restore': '鎭㈠',
+ 'agentSnapshot.delete': '鍒犻櫎',
+ 'agentSnapshot.loadMore': '鍔犺浇鏇村',
+ 'agentSnapshot.detailTitle': '鍙樻洿璇︽儏',
+ 'agentSnapshot.restorePreviewTitle': '鎭㈠棰勮',
+ 'agentSnapshot.confirmRestore': '纭鎭㈠',
+ 'agentSnapshot.currentVersion': '鏈�鏂板揩鐓�',
+ 'agentSnapshot.beforeChange': '鍙樺寲鍓�',
+ 'agentSnapshot.afterChange': '鍙樺寲鍚�',
+ 'agentSnapshot.beforeRestore': '鎭㈠鍓�',
+ 'agentSnapshot.afterRestore': '鎭㈠鍚�',
+ 'agentSnapshot.configValue': '閰嶇疆鍊�',
+ 'agentSnapshot.emptyValue': '鏃�',
+ 'agentSnapshot.secretRedacted': '瀵嗛挜宸查殣钘�',
+ 'agentSnapshot.redactedValueChanged': '鍊煎凡鑴辨晱锛屼絾纭湁鍙樺寲',
+ 'agentSnapshot.noChangedContent': '鏃犲彲鏄剧ず鍙樻洿',
+ 'agentSnapshot.recordedChange': '璁板綍鏃跺彂鐢熷彉鏇�',
+ 'agentSnapshot.noRestoreNeeded': '褰撳墠閰嶇疆涓庣洰鏍囩増鏈浉鍚岋紝鏃犻渶鎭㈠',
+ 'agentSnapshot.unsavedChangesTitle': '瀛樺湪鏈繚瀛樹慨鏀�',
+ 'agentSnapshot.unsavedChangesWarning': '缁х画鎭㈠浼氭斁寮冨綋鍓嶉〉闈腑灏氭湭淇濆瓨鐨勪慨鏀广��',
+ 'agentSnapshot.discardAndRestore': '鏀惧純淇敼骞舵仮澶�',
+ 'agentSnapshot.restoreConfirm': '纭畾鎭㈠鍒扮増鏈� #{version}锛熷綋鍓嶉厤缃細淇濈暀鍦ㄥ巻鍙蹭腑銆�',
+ 'agentSnapshot.restoreMemoryWarning': '鎭㈠鍒版棤璁板繂鐗堟湰浼氭竻绌鸿鏅鸿兘浣撹亰澶╄褰曪紝璇风‘璁ら闄┿��',
+ 'agentSnapshot.restoreChatHistoryDestructiveWarning': '姝ゆ仮澶嶄細姘镐箙鍒犻櫎璇ユ櫤鑳戒綋鐜版湁鐨勮亰澶╄褰曘�傝亰澶╄褰曚笉鍖呭惈鍦ㄩ厤缃揩鐓т腑锛屽垹闄ゅ悗鏃犳硶閫氳繃鍘嗗彶鐗堟湰鎭㈠銆�',
+ 'agentSnapshot.restoreSuccess': '鐗堟湰宸叉仮澶�',
+ 'agentSnapshot.restoreFailed': '鐗堟湰鎭㈠澶辫触',
+ 'agentSnapshot.reloadAfterRestorePending': '姝e湪閲嶆柊鍔犺浇鎭㈠鍚庣殑閰嶇疆鍜屾爣绛撅紝瀹屾垚鍓嶆棤娉曚繚瀛樸��',
+ 'agentSnapshot.reloadAfterRestoreFailed': '鏃犳硶閲嶆柊鍔犺浇鎭㈠鍚庣殑閰嶇疆鎴栨爣绛俱�備负闃叉鏃ф暟鎹鐩栨仮澶嶇粨鏋滐紝淇濆瓨宸茬鐢紝璇烽噸璇曘��',
+ 'agentSnapshot.retryReload': '閲嶆柊鍔犺浇',
+ 'agentSnapshot.mutationBusy': '閰嶇疆姝e湪淇濆瓨鎴栭噸鏂板姞杞斤紝璇峰畬鎴愬悗鍐嶆墦寮�鍘嗗彶鐗堟湰鎴栨仮澶嶃��',
+ 'agentSnapshot.deleteConfirm': '纭畾鍒犻櫎鐗堟湰 #{version}锛熸鎿嶄綔涓嶅彲鎾ら攢銆�',
+ 'agentSnapshot.deleteSuccess': '鍘嗗彶鐗堟湰宸插垹闄�',
+ 'agentSnapshot.deleteFailed': '鍘嗗彶鐗堟湰鍒犻櫎澶辫触',
+ 'agentSnapshot.fetchFailed': '鑾峰彇鍘嗗彶鐗堟湰澶辫触',
+ 'agentSnapshot.detailFailed': '鑾峰彇鐗堟湰璇︽儏澶辫触',
+ 'agentSnapshot.correctWordCount': '鍏� {count} 涓浛鎹㈣瘝',
+ 'agentSnapshot.source.config': '閰嶇疆淇濆瓨',
+ 'agentSnapshot.source.current': '褰撳墠閰嶇疆',
+ 'agentSnapshot.source.restore': '鎭㈠缁撴灉',
+ 'agentSnapshot.source.initial': '鍒濆鐗堟湰',
+ 'agentSnapshot.field.initial': '鍒濆蹇収',
+ 'agentSnapshot.field.agentCode': '鏅鸿兘浣撶紪鐮�',
+ 'agentSnapshot.field.agentName': '鍔╂墜鏄电О',
+ 'agentSnapshot.field.asrModelId': '璇煶璇嗗埆',
+ 'agentSnapshot.field.vadModelId': '璇煶娲诲姩妫�娴�',
+ 'agentSnapshot.field.llmModelId': '涓昏瑷�妯″瀷',
+ 'agentSnapshot.field.slmModelId': '灏忓弬鏁版ā鍨�',
+ 'agentSnapshot.field.vllmModelId': '瑙嗚澶фā鍨�',
+ 'agentSnapshot.field.ttsModelId': '璇煶鍚堟垚',
+ 'agentSnapshot.field.ttsVoiceId': '澹伴煶闊宠壊',
+ 'agentSnapshot.field.ttsLanguage': '瀵硅瘽璇█',
+ 'agentSnapshot.field.ttsVolume': '闊抽噺',
+ 'agentSnapshot.field.ttsRate': '璇��',
+ 'agentSnapshot.field.ttsPitch': '闊宠皟',
+ 'agentSnapshot.field.memModelId': '璁板繂妯″紡',
+ 'agentSnapshot.field.intentModelId': '鎰忓浘璇嗗埆',
+ 'agentSnapshot.field.chatHistoryConf': '鑱婂ぉ璁板綍閰嶇疆',
+ 'agentSnapshot.field.systemPrompt': '瑙掕壊浠嬬粛',
+ 'agentSnapshot.field.summaryMemory': '璁板繂',
+ 'agentSnapshot.field.langCode': '璇█缂栫爜',
+ 'agentSnapshot.field.language': '浜や簰璇',
+ 'agentSnapshot.field.sort': '鎺掑簭',
+ 'agentSnapshot.field.functions': '鎻掍欢',
+ 'agentSnapshot.field.contextProviders': '涓婁笅鏂囨簮',
+ 'agentSnapshot.field.correctWordFileIds': '鏇挎崲璇�',
+ 'agentSnapshot.field.tagNames': '鏅鸿兘浣撴爣绛�',
+ 'agentSnapshot.chatHistoryConf.none': '涓嶈褰曡亰澶╄褰�',
+ 'agentSnapshot.chatHistoryConf.text': '涓婃姤鏂囧瓧',
+ 'agentSnapshot.chatHistoryConf.textVoice': '涓婃姤鏂囧瓧+璇煶',
+ 'agentSnapshot.model.Memory_nomem': '鏃犺蹇�',
+ 'agentSnapshot.model.Memory_mem_local_short': '鏈湴鐭湡璁板繂',
+ 'agentSnapshot.model.Memory_mem0ai': 'Mem0AI 璁板繂',
+ 'agentSnapshot.model.Memory_mem_report_only': '浠呬笂鎶�',
+ 'agentSnapshot.model.Intent_nointent': '鏃犳剰鍥捐瘑鍒�',
+ 'agentSnapshot.model.Intent_intent_llm': '澶栭儴澶фā鍨嬫剰鍥捐瘑鍒�',
+ 'agentSnapshot.model.Intent_function_call': '澶фā鍨嬪嚱鏁拌皟鐢�',
+
+ // 涓婁笅鏂囨簮瀵硅瘽妗嗙浉鍏�
+ 'contextProviderDialog.title': '缂栬緫婧�',
+ 'contextProviderDialog.noContextApi': '鏆傛棤涓婁笅鏂嘇PI',
+ 'contextProviderDialog.add': '娣诲姞',
+ 'contextProviderDialog.apiUrl': '鎺ュ彛鍦板潃',
+ 'contextProviderDialog.apiUrlPlaceholder': 'http://api.example.com/data',
+ 'contextProviderDialog.requestHeaders': '璇锋眰澶�',
+ 'contextProviderDialog.headerKeyPlaceholder': 'Key',
+ 'contextProviderDialog.headerValuePlaceholder': 'Value',
+ 'contextProviderDialog.noHeaders': '鏆傛棤 Headers',
+ 'contextProviderDialog.addHeader': '娣诲姞 Header',
+ 'contextProviderDialog.cancel': '鍙栨秷',
+ 'contextProviderDialog.confirm': '纭畾',
+
+ // 鎵嬪嫊娣诲姞瑷倷灏嶈┍妗嗙浉闂�
+ 'manualAddDeviceDialog.title': '鎵嬪姩娣诲姞璁惧',
+ 'manualAddDeviceDialog.deviceType': '璁惧鍨嬪彿',
+ 'manualAddDeviceDialog.deviceTypePlaceholder': '璇烽�夋嫨璁惧鍨嬪彿',
+ 'manualAddDeviceDialog.firmwareVersion': '鍥轰欢鐗堟湰',
+ 'manualAddDeviceDialog.firmwareVersionPlaceholder': '璇疯緭鍏ュ浐浠剁増鏈�',
+ 'manualAddDeviceDialog.macAddress': 'Mac鍦板潃',
+ 'manualAddDeviceDialog.macAddressPlaceholder': '璇疯緭鍏ac鍦板潃',
+ 'manualAddDeviceDialog.confirm': '纭畾',
+ 'manualAddDeviceDialog.cancel': '鍙栨秷',
+ 'manualAddDeviceDialog.requiredMacAddress': '璇疯緭鍏ac鍦板潃',
+ 'manualAddDeviceDialog.invalidMacAddress': '璇疯緭鍏ユ纭殑Mac鍦板潃鏍煎紡锛屼緥濡傦細00:1A:2B:3C:4D:5E',
+ 'manualAddDeviceDialog.requiredDeviceType': '璇烽�夋嫨璁惧鍨嬪彿',
+ 'manualAddDeviceDialog.requiredFirmwareVersion': '璇疯緭鍏ュ浐浠剁増鏈�',
+ 'manualAddDeviceDialog.getFirmwareTypeFailed': '鑾峰彇鍥轰欢绫诲瀷澶辫触',
+ 'manualAddDeviceDialog.addSuccess': '璁惧娣诲姞鎴愬姛',
+ 'manualAddDeviceDialog.addFailed': '娣诲姞澶辫触',
+ 'manualAddDeviceDialog.bindWithCode': '6浣嶉獙璇佺爜缁戝畾',
+
+ // 鑱婂ぉ鍘嗗彶椤甸潰
+ 'chatHistory.getChatSessions': '鑾峰彇鑱婂ぉ浼氳瘽鍒楄〃',
+ 'chatHistory.noSelectedAgent': '娌℃湁閫変腑鐨勬櫤鑳戒綋',
+ 'chatHistory.getChatSessionsFailed': '鑾峰彇鑱婂ぉ浼氳瘽鍒楄〃澶辫触:',
+ 'chatHistory.unknownTime': '鏈煡鏃堕棿',
+ 'chatHistory.justNow': '鍒氬垰',
+ 'chatHistory.minutesAgo': '{minutes}鍒嗛挓鍓�',
+ 'chatHistory.hoursAgo': '{hours}灏忔椂鍓�',
+ 'chatHistory.daysAgo': '{days}澶╁墠',
+ 'chatHistory.conversationRecord': '瀵硅瘽璁板綍',
+ 'chatHistory.totalChats': '鍏� {count} 鏉″璇�',
+ 'chatHistory.loading': '鍔犺浇涓�...',
+ 'chatHistory.noMoreData': '娌℃湁鏇村鏁版嵁浜�',
+ 'chatHistory.noChatRecords': '鏆傛棤鑱婂ぉ璁板綍',
+ 'chatHistory.chatRecordsDescription': '涓庢櫤鑳戒綋鐨勫璇濊褰曚細鏄剧ず鍦ㄨ繖閲�',
+ // 鑱婂ぉ璇︽儏椤甸潰
+ 'chatHistory.pageTitle': '鑱婂ぉ璇︽儏',
+ 'chatHistory.assistantName': '鏅鸿兘鍔╂墜',
+ 'chatHistory.userName': '鐢ㄦ埛',
+ 'chatHistory.aiAssistantName': 'AI鍔╂墜',
+ 'chatHistory.loadFailed': '鑾峰彇鑱婂ぉ璁板綍澶辫触',
+ 'chatHistory.parameterError': '椤甸潰鍙傛暟閿欒',
+ 'chatHistory.invalidAudioId': '闊抽ID鏃犳晥',
+ 'chatHistory.audioPlayFailed': '闊抽鎾斁澶辫触',
+ 'chatHistory.playAudioFailed': '鎾斁闊抽澶辫触',
+
+ // 璁惧绠$悊椤甸潰
+ 'device.pageTitle': '璁惧绠$悊',
+ 'device.noDevices': '鏆傛棤璁惧',
+ 'device.macAddress': 'MAC鍦板潃',
+ 'device.firmwareVersion': '鍥轰欢鐗堟湰',
+ 'device.lastConnected': '鏈�杩戝璇�',
+ 'device.otaUpdate': 'OTA鍗囩骇',
+ 'device.unbind': '瑙g粦',
+ 'device.confirmUnbind': '纭畾瑕佽В缁戣澶�',
+ 'device.bindDevice': '缁戝畾鏂拌澶�',
+ 'device.deviceType': '璁惧绫诲瀷',
+ 'device.loading': '鍔犺浇涓�...',
+ 'device.neverConnected': '浠庢湭杩炴帴',
+ 'device.justNow': '鍒氬垰',
+ 'device.minutesAgo': '{minutes}鍒嗛挓鍓�',
+ 'device.hoursAgo': '{hours}灏忔椂鍓�',
+ 'device.daysAgo': '{days}澶╁墠',
+ 'device.otaAutoUpdateEnabled': 'OTA鑷姩鍗囩骇宸插紑鍚�',
+ 'device.otaAutoUpdateDisabled': 'OTA鑷姩鍗囩骇宸插叧闂�',
+ 'device.operationFailed': '鎿嶄綔澶辫触锛岃閲嶈瘯',
+ 'device.deviceUnbound': '璁惧宸茶В缁�',
+ 'device.unbindFailed': '瑙g粦澶辫触锛岃閲嶈瘯',
+ 'device.unbindDevice': '瑙g粦璁惧',
+ 'device.confirmUnbindDevice': '纭畾瑕佽В缁戣澶� "{macAddress}" 鍚楋紵',
+ 'device.cancel': '鍙栨秷',
+ 'device.noDevice': '鏆傛棤璁惧',
+ 'device.pleaseSelectAgent': '璇峰厛閫夋嫨鏅鸿兘浣�',
+ 'device.deviceBindSuccess': '璁惧缁戝畾鎴愬姛锛�',
+ 'device.bindFailed': '缁戝畾澶辫触锛岃妫�鏌ラ獙璇佺爜鏄惁姝g‘',
+ 'device.enterDeviceCode': '璇疯緭鍏ヨ澶囬獙璇佺爜',
+ 'device.bindNow': '绔嬪嵆缁戝畾',
+ 'device.lastConnection': '鏈�杩戝璇�',
+ 'device.clickToBindFirstDevice': '鐐瑰嚮鍙充笅瑙� + 鍙风粦瀹氭偍鐨勭涓�涓澶�',
+
+ // 閫氱敤
+ 'common.success': '鎴愬姛',
+ 'common.fail': '澶辫触',
+ 'common.loading': '鍔犺浇涓�...',
+ 'common.confirm': '纭',
+ 'common.cancel': '鍙栨秷',
+ 'common.delete': '鍒犻櫎',
+ 'common.edit': '缂栬緫',
+ 'common.add': '娣诲姞',
+ 'common.pleaseSelect': '璇烽�夋嫨',
+ 'common.unknownError': '鏈煡閿欒',
+ 'common.networkError': '缃戠粶閿欒',
+
+ // SM2鍔犲瘑鐩稿叧閿欒娑堟伅
+ 'sm2.publicKeyNotConfigured': 'SM2鍏挜鏈厤缃紝璇疯仈绯荤鐞嗗憳',
+ 'sm2.encryptionFailed': '瀵嗙爜鍔犲瘑澶辫触',
+ 'sm2.keyGenerationFailed': '瀵嗛挜瀵圭敓鎴愬け璐�',
+ 'sm2.invalidPublicKey': '鏃犳晥鐨勫叕閽ユ牸寮�',
+ 'sm2.encryptionError': '鍔犲瘑杩囩▼涓彂鐢熼敊璇�',
+ 'sm2.publicKeyRetry': '姝e湪閲嶈瘯鑾峰彇鍏挜...',
+ 'sm2.publicKeyRetryFailed': '鍏挜鑾峰彇閲嶈瘯澶辫触',
+
+ // Voiceprint page
+ 'voiceprint.noSelectedAgent': '娌℃湁閫変腑鐨勬櫤鑳戒綋',
+ 'voiceprint.pleaseSelectAgent': '璇峰厛閫夋嫨鏅鸿兘浣�',
+ 'voiceprint.fetchHistoryFailed': '鑾峰彇瀵硅瘽璁板綍澶辫触',
+ 'voiceprint.clickToSelectVector': '鐐瑰嚮閫夋嫨澹扮汗鍚戦噺',
+ 'voiceprint.pleaseInputName': '璇疯緭鍏ュ鍚�',
+ 'voiceprint.pleaseSelectVector': '璇烽�夋嫨澹扮汗鍚戦噺',
+ 'voiceprint.addSuccess': '娣诲姞鎴愬姛',
+ 'voiceprint.addFailed': '娣诲姞璇磋瘽浜哄け璐�',
+ 'voiceprint.editSuccess': '缂栬緫鎴愬姛',
+ 'voiceprint.editFailed': '缂栬緫璇磋瘽浜哄け璐�',
+ 'voiceprint.deleteConfirmMsg': '纭畾瑕佸垹闄よ繖涓璇濅汉鍚楋紵',
+ 'voiceprint.deleteConfirmTitle': '纭鍒犻櫎',
+ 'voiceprint.deleteSuccess': '鍒犻櫎鎴愬姛',
+ 'voiceprint.loading': '鍔犺浇涓�...',
+ 'voiceprint.delete': '鍒犻櫎',
+ 'voiceprint.emptyTitle': '鏆傛棤澹扮汗鏁版嵁',
+ 'voiceprint.emptyDesc': '鐐瑰嚮鍙充笅瑙� + 鍙锋坊鍔犳偍鐨勭涓�涓璇濅汉',
+ 'voiceprint.addSpeaker': '娣诲姞璇磋瘽浜�',
+ 'voiceprint.voiceVector': '澹扮汗鍚戦噺',
+ 'voiceprint.name': '濮撳悕',
+ 'voiceprint.description': '鎻忚堪',
+ 'voiceprint.pleaseInputDescription': '璇疯緭鍏ユ弿杩�',
+ 'voiceprint.cancel': '鍙栨秷',
+ 'voiceprint.save': '淇濆瓨',
+ 'voiceprint.editSpeaker': '缂栬緫璇磋瘽浜�',
+ 'voiceprint.selectVector': '閫夋嫨澹扮汗鍚戦噺',
+ 'voiceprint.voiceprintInterfaceNotConfigured': '澹扮汗鎺ュ彛鏈厤缃�',
+
+ // 璁剧疆椤甸潰
+ 'settings.pageTitle': '璁剧疆',
+ 'settings.navigationTitle': '璁剧疆',
+ 'settings.networkSettings': '缃戠粶璁剧疆',
+ 'settings.serverApiUrl': '鏈嶅姟绔帴鍙e湴鍧�',
+ 'settings.validServerUrl': '璇疯緭鍏ユ湁鏁堢殑鏈嶅姟绔湴鍧�锛堜互 http 鎴� https 寮�澶达紝骞朵互 /xiaozhi 缁撳熬锛�',
+ 'settings.saveSettings': '淇濆瓨璁剧疆',
+ 'settings.resetDefault': '鎭㈠榛樿',
+ 'settings.restartApp': '閲嶅惎搴旂敤',
+ 'settings.restartNow': '绔嬪嵆閲嶅惎',
+ 'settings.restartLater': '绋嶅悗',
+ // 鍏充簬鎴戜滑
+ 'settings.aboutApp': '鍏充簬灏忔櫤鏅烘帶鍙�',
+ 'settings.aboutContent': '灏忔櫤鏅烘帶鍙癨n\n鍩轰簬 Vue.js 3 + uni-app 鏋勫缓鐨勮法骞冲彴绉诲姩绔鐞嗗簲鐢紝涓哄皬鏅篍SP32鏅鸿兘纭欢鎻愪緵璁惧绠$悊銆佹櫤鑳戒綋閰嶇疆绛夊姛鑳姐�俓n\n漏 2025 xiaozhi-esp32-server {version}',
+ 'settings.restartSuccess': '宸蹭繚瀛橈紝鍙◢鍚庢墜鍔ㄩ噸鍚簲鐢�',
+ 'settings.serverUrlSavedAndCacheCleared': '鏈嶅姟绔湴鍧�宸蹭繚瀛橈紝缂撳瓨宸叉竻闄�',
+ 'settings.resetToDefaultAndCacheCleared': '宸叉仮澶嶉粯璁よ缃紝缂撳瓨宸叉竻闄�',
+ 'settings.resetSuccess': '閲嶇疆鎴愬姛',
+ 'settings.enterServerUrl': '璇疯緭鍏ユ湇鍔$鍦板潃',
+ 'settings.clearCacheFailed': '娓呴櫎缂撳瓨澶辫触',
+ 'settings.cacheManagement': '缂撳瓨绠$悊',
+ 'settings.totalCacheSize': '鎬荤紦瀛樺ぇ灏�',
+ 'settings.appDataSize': '搴旂敤鏁版嵁鎬诲ぇ灏�',
+ 'settings.cacheClear': '缂撳瓨娓呯悊',
+ 'settings.clearAllCache': '娓呯┖鎵�鏈夌紦瀛樻暟鎹�',
+ 'settings.clearCache': '娓呴櫎缂撳瓨',
+ 'settings.modifyWillClearCache': '淇敼灏嗘竻闄ょ紦瀛�',
+ 'settings.appInfo': '搴旂敤淇℃伅',
+ 'settings.aboutUs': '鍏充簬鎴戜滑',
+ 'settings.appVersion': '搴旂敤鐗堟湰涓庡洟闃熶俊鎭�',
+ 'settings.confirmClear': '纭娓呴櫎',
+ 'settings.confirmClearMessage': '纭畾瑕佹竻闄ゆ墍鏈夌紦瀛樺悧锛熻繖灏嗗垹闄ゆ墍鏈夋暟鎹寘鎷櫥褰曠姸鎬侊紝闇�瑕侀噸鏂扮櫥褰曘��',
+ 'settings.cacheCleared': '缂撳瓨娓呴櫎鎴愬姛锛屽嵆灏嗚烦杞埌鐧诲綍椤�',
+ 'settings.languageSettings': '璇█璁剧疆',
+ 'settings.language': '璇█',
+ 'settings.selectLanguage': '閫夋嫨璇█',
+ 'settings.languageChanged': '璇█鍒囨崲鎴愬姛',
+
+ // 閲嶇疆瀵嗙爜椤甸潰
+ 'retrievePassword.title': '閲嶇疆瀵嗙爜',
+ 'retrievePassword.subtitle': '閫氳繃鎵嬫満鍙锋壘鍥炴偍鐨勮处鎴峰瘑鐮�',
+ 'retrievePassword.mobileRequired': '璇疯緭鍏ユ墜鏈哄彿鐮�',
+ 'retrievePassword.inputCorrectMobile': '璇疯緭鍏ユ纭殑鎵嬫満鍙风爜',
+ 'retrievePassword.captchaRequired': '璇疯緭鍏ュ浘褰㈤獙璇佺爜',
+ 'retrievePassword.mobileCaptchaRequired': '璇疯緭鍏ョ煭淇¢獙璇佺爜',
+ 'retrievePassword.newPasswordRequired': '璇疯緭鍏ユ柊瀵嗙爜',
+ 'retrievePassword.confirmNewPasswordRequired': '璇风‘璁ゆ柊瀵嗙爜',
+ 'retrievePassword.passwordsNotMatch': '涓ゆ杈撳叆鐨勫瘑鐮佷笉涓�鑷�',
+ 'retrievePassword.mobilePlaceholder': '璇疯緭鍏ユ墜鏈哄彿鐮�',
+ 'retrievePassword.captchaPlaceholder': '璇疯緭鍏ュ浘褰㈤獙璇佺爜',
+ 'retrievePassword.mobileCaptchaPlaceholder': '璇疯緭鍏ョ煭淇¢獙璇佺爜',
+ 'retrievePassword.newPasswordPlaceholder': '璇疯緭鍏ユ柊瀵嗙爜',
+ 'retrievePassword.confirmNewPasswordPlaceholder': '璇风‘璁ゆ柊瀵嗙爜',
+ 'retrievePassword.getMobileCaptcha': '鑾峰彇楠岃瘉鐮�',
+ 'retrievePassword.captchaSendSuccess': '楠岃瘉鐮佸彂閫佹垚鍔�',
+ 'retrievePassword.passwordUpdateSuccess': '瀵嗙爜閲嶇疆鎴愬姛',
+ 'retrievePassword.resetButton': '閲嶇疆瀵嗙爜',
+ 'retrievePassword.goToLogin': '杩斿洖鐧诲綍',
+
+ // 娑堟伅鎻愮ず
+ 'message.loginSuccess': '鐧诲綍鎴愬姛锛�',
+ 'message.loginFail': '鐧诲綍澶辫触',
+ 'message.registerSuccess': '娉ㄥ唽鎴愬姛',
+ 'message.registerFail': '娉ㄥ唽澶辫触',
+ 'message.saveSuccess': '淇濆瓨鎴愬姛',
+ 'message.saveFail': '淇濆瓨澶辫触',
+ 'message.deleteSuccess': '鍒犻櫎鎴愬姛',
+ 'message.deleteFail': '鍒犻櫎澶辫触',
+ 'message.bindSuccess': '缁戝畾鎴愬姛',
+ 'message.bindFail': '缁戝畾澶辫触',
+ 'message.unbindSuccess': '瑙g粦鎴愬姛',
+ 'message.unbindFail': '瑙g粦澶辫触',
+ 'message.networkError': '缃戠粶閿欒锛岃妫�鏌ョ綉缁滆繛鎺�',
+ 'message.serverError': '鏈嶅姟鍣ㄩ敊璇紝璇风◢鍚庡啀璇�',
+ 'message.invalidAddress': '鍦板潃鍙兘鏃犳晥锛岃妫�鏌ユ湇鍔$鏄惁鍚姩鎴栫綉缁滆繛鎺ユ槸鍚︽甯革紱涔熷彲鑳藉洜 HTTPS 鍗忚闂瀵艰嚧鏃犳硶鍙戦�佽姹�',
+ 'message.languageChanged': '璇█宸插垏鎹�',
+ 'message.passwordError': '璐﹀彿鎴栧瘑鐮侀敊璇�',
+ 'message.phoneRegistered': '姝ゆ墜鏈哄彿鐮佸凡缁忔敞鍐岃繃',
+
+ // Agent宸ュ叿椤甸潰
+ 'agent.tools.pageTitle': 'Agent宸ュ叿',
+ 'agent.tools.unselected': '鏈��',
+ 'agent.tools.selected': '宸查��',
+ 'agent.tools.noMorePlugins': '鏆傛棤鏇村鎻掍欢',
+ 'agent.tools.pleaseSelectPlugin': '璇烽�夋嫨鎻掍欢鍔熻兘',
+ 'agent.tools.builtInPlugins': '鍐呯疆鎻掍欢',
+ 'agent.tools.mcpAccessPoint': 'mcp鎺ュ叆鐐�',
+ 'agent.tools.copy': '澶嶅埗',
+ 'agent.tools.noTools': '鏆傛棤宸ュ叿',
+ 'agent.tools.parameterConfig': '鍙傛暟閰嶇疆',
+ 'agent.tools.noParamsNeeded': '鏃犻渶閰嶇疆鍙傛暟',
+ 'agent.tools.pleaseInput': '璇疯緭鍏�',
+ 'agent.tools.inputOneItemPerLine': '姣忚杈撳叆涓�涓」鐩�',
+ 'agent.tools.pleaseInputValidJson': '璇疯緭鍏ユ湁鏁堢殑JSON鏍煎紡',
+ 'agent.tools.enableFunction': '鍚敤鍔熻兘',
+ 'agent.tools.toggleFunction': '寮�鍚垨鍏抽棴姝ゅ姛鑳�',
+ 'agent.tools.jsonFormatError': 'JSON鏍煎紡閿欒',
+ 'agent.tools.noMcpAddressToCopy': '鏆傛棤MCP鍦板潃鍙鍒�',
+ 'agent.tools.mcpAddressCopied': 'MCP鍦板潃宸插鍒跺埌鍓创鏉�',
+ 'agent.tools.copyFailed': '澶嶅埗澶辫触锛岃閲嶈瘯',
+ 'agent.tools.defaultValue': '榛樿鍊�',
+ 'agent.tools.notSelected': '鏈��',
+ 'agent.tools.clickToConfigure': '鐐瑰嚮閰嶇疆',
+ 'agent.tools.mcpEndpoint': 'MCP鎺ュ叆鐐�',
+ 'agent.tools.eachLineOneItem': '姣忚杈撳叆涓�涓」鐩�',
+
+ // 璁惧閰嶇疆椤甸潰
+ 'deviceConfig.pageTitle': '璁惧閰嶇疆',
+ 'deviceConfig.wifiConfig': 'WiFi閰嶇綉',
+ 'deviceConfig.ultrasonicConfig': '瓒呭0娉㈤厤缃�',
+ 'deviceConfig.selectConfigMethod': '閫夋嫨閰嶇綉鏂瑰紡',
+ 'deviceConfig.networkConfig': '缃戠粶閰嶇疆',
+ 'deviceConfig.selectedNetwork': '閫変腑缃戠粶',
+ 'deviceConfig.signal': '淇″彿',
+ 'deviceConfig.openNetwork': '寮�鏀剧綉缁�',
+ 'deviceConfig.encryptedNetwork': '鍔犲瘑缃戠粶',
+ 'deviceConfig.password': '瀵嗙爜',
+ 'deviceConfig.pleaseEnterPassword': '璇疯緭鍏iFi瀵嗙爜',
+ 'deviceConfig.startConfig': '寮�濮嬮厤缃�',
+ 'deviceConfig.connectToXiaozhiHotspot': '璇峰厛杩炴帴xiaozhi鐑偣 (xiaozhi-XXXXXX)',
+ 'deviceConfig.detecting': '妫�娴嬩腑...',
+ 'deviceConfig.reDetect': '閲嶆柊妫�娴�',
+ 'deviceConfig.alreadyConnected': '宸茶繛鎺iaozhi鐑偣',
+ 'deviceConfig.refreshStatus': '鍒锋柊鐘舵��',
+ 'deviceConfig.wifiNetworks': 'WiFi缃戠粶',
+ 'deviceConfig.selectWifiNetwork': '閫夋嫨WiFi缃戠粶',
+ 'deviceConfig.refreshScan': '鍒锋柊鎵弿',
+ 'deviceConfig.noWifiNetworks': '鏆傛棤WiFi缃戠粶',
+ 'deviceConfig.clickToRefreshScan': '璇风偣鍑诲埛鏂版壂鎻�',
+ 'deviceConfig.signalStrong': '淇″彿寮�',
+ 'deviceConfig.signalGood': '淇″彿鑹ソ',
+ 'deviceConfig.signalFair': '淇″彿涓�鑸�',
+ 'deviceConfig.signalWeak': '淇″彿寮�',
+ 'deviceConfig.channel': '棰戦亾',
+ 'deviceConfig.about': '绾�',
+ 'deviceConfig.seconds': '绉�',
+ 'deviceConfig.generating': '鐢熸垚涓�...',
+ 'deviceConfig.playing': '鎾斁涓�...',
+ 'deviceConfig.generateAndPlaySoundWave': '鐢熸垚骞舵挱鏀惧0娉�',
+ 'deviceConfig.playSoundWave': '鎾斁澹版尝',
+ 'deviceConfig.stopPlaying': '鍋滄鎾斁',
+ 'deviceConfig.autoLoopPlaySoundWave': '鑷姩寰幆鎾斁澹版尝',
+ 'deviceConfig.configAudioFile': '閰嶇綉闊抽鏂囦欢',
+ 'deviceConfig.duration': '鏃堕暱',
+ 'deviceConfig.ultrasonicConfigInstructions': '瓒呭0娉㈤厤缃戣鏄�',
+ 'deviceConfig.ensureWifiNetworkSelectedAndPasswordEntered': '纭繚宸查�夋嫨WiFi缃戠粶骞惰緭鍏ュ瘑鐮�',
+ 'deviceConfig.clickGenerateAndPlaySoundWave': '鐐瑰嚮鐢熸垚骞舵挱鏀惧0娉紝绯荤粺浼氬皢閰嶇綉淇℃伅缂栫爜涓洪煶棰�',
+ 'deviceConfig.bringPhoneCloseToXiaozhiDevice': '灏嗘墜鏈洪潬杩憍iaozhi璁惧锛堣窛绂�1-2绫筹級',
+ 'deviceConfig.duringAudioPlaybackXiaozhiWillReceive': '闊抽鎾斁鏃讹紝xiaozhi浼氭帴鏀跺苟瑙g爜閰嶇綉淇℃伅',
+ 'deviceConfig.afterConfigSuccessDeviceWillConnect': '閰嶇綉鎴愬姛鍚庤澶囦細鑷姩杩炴帴WiFi缃戠粶',
+ 'deviceConfig.usesAfskModulation': '浣跨敤AFSK璋冨埗鎶�鏈紝閫氳繃1800Hz鍜�1500Hz棰戠巼浼犺緭鏁版嵁',
+ 'deviceConfig.ensureModeratePhoneVolume': '璇风‘淇濇墜鏈洪煶閲忛�備腑锛岄伩鍏嶇幆澧冨櫔闊冲共鎵�',
+ 'deviceConfig.generatingUltrasonicConfigAudio': '鐢熸垚瓒呭0娉㈤厤缃戦煶棰�',
+ 'deviceConfig.configData': '閰嶇綉鏁版嵁',
+ 'deviceConfig.dataBytesLength': '鏁版嵁瀛楄妭闀垮害',
+ 'deviceConfig.bitStreamLength': '姣旂壒娴侀暱搴�',
+ 'deviceConfig.base64Length': 'base64闀垮害',
+ 'deviceConfig.audioFileTooLarge': '闊抽鏂囦欢杩囧ぇ锛岃缂╃煭SSID鎴栧瘑鐮侀暱搴�',
+ 'deviceConfig.audioGenerationSuccess': '闊抽鐢熸垚鎴愬姛',
+ 'deviceConfig.samplePoints': '閲囨牱鐐规暟',
+ 'deviceConfig.soundWaveGenerationSuccess': '澹版尝鐢熸垚鎴愬姛',
+ 'deviceConfig.audioGenerationFailed': '闊抽鐢熸垚澶辫触',
+ 'deviceConfig.soundWaveGenerationFailed': '澹版尝鐢熸垚澶辫触',
+ 'deviceConfig.pleaseGenerateAudioFirst': '璇峰厛鐢熸垚闊抽',
+ 'deviceConfig.startPlayingUltrasonicConfigAudio': '寮�濮嬫挱鏀捐秴澹版尝閰嶇綉闊抽',
+ 'deviceConfig.ultrasonicAudioStartedPlaying': '瓒呭0娉㈤煶棰戝紑濮嬫挱鏀�',
+ 'deviceConfig.startPlayingConfigSoundWave': '寮�濮嬫挱鏀鹃厤缃戝0娉�',
+ 'deviceConfig.ultrasonicAudioPlaybackEnded': '瓒呭0娉㈤煶棰戞挱鏀剧粨鏉�',
+ 'deviceConfig.audioPlaybackFailed': '闊抽鎾斁澶辫触',
+ 'deviceConfig.audioResourceBusy': '闊抽璧勬簮绻佸繖锛岃绋嶅悗閲嶈瘯',
+ 'deviceConfig.audioFormatNotSupported': '闊抽鏍煎紡涓嶆敮鎸侊紝鍙兘鏄痙ata URI闂',
+ 'deviceConfig.audioFileError': '闊抽鏂囦欢閿欒',
+ 'deviceConfig.cleaningUpAudioContext': '娓呯悊闊抽涓婁笅鏂�',
+ 'deviceConfig.cleaningUpAudioContextFailed': '娓呯悊闊抽涓婁笅鏂囧け璐�',
+ 'deviceConfig.stoppedPlayingUltrasonicAudio': '鍋滄鎾斁瓒呭0娉㈤煶棰�',
+ 'deviceConfig.stoppedPlaying': '宸插仠姝㈡挱鏀�',
+ 'deviceConfig.configMethod': '閰嶇綉鏂瑰紡',
+ 'deviceConfig.enterWifiPassword': '璇疯緭鍏iFi瀵嗙爜',
+ 'deviceConfig.xiaozhi': 'xiaozhi',
+ 'deviceConfig.connectXiaozhiHotspot': '璇疯繛鎺iaozhi鐑偣',
+ 'deviceConfig.wifiScanResponse': 'WiFi鎵弿鍝嶅簲',
+ 'deviceConfig.scanSuccess': '鎵弿鎴愬姛',
+ 'deviceConfig.networks': '涓綉缁�',
+ 'deviceConfig.wifiScanFailed': 'WiFi鎵弿澶辫触',
+ 'deviceConfig.scanFailedCheckConnection': '鎵弿澶辫触锛岃妫�鏌ヨ繛鎺�',
+ 'deviceConfig.checking': '妫�鏌ヤ腑',
+ 'deviceConfig.reCheck': '閲嶆柊妫�鏌�',
+ 'deviceConfig.connectedXiaozhiHotspot': '宸茶繛鎺iaozhi鐑偣',
+ 'deviceConfig.wifiNetwork': 'WiFi缃戠粶',
+ 'deviceConfig.wifiPassword': 'WiFi瀵嗙爜',
+ 'deviceConfig.scanning': '鎵弿涓�',
+ 'deviceConfig.cancel': '鍙栨秷',
+ 'deviceConfig.clickRefreshScan': '璇风偣鍑诲埛鏂版壂鎻�',
+ 'deviceConfig.esp32ConnectionCheckFailed': 'ESP32杩炴帴妫�鏌ュけ璐�',
+ 'deviceConfig.startWifiConfig': '寮�濮媁iFi閰嶇綉',
+ 'deviceConfig.configSuccess': '閰嶇綉鎴愬姛',
+ 'deviceConfig.deviceWillConnectTo': '璁惧灏嗚繛鎺ュ埌',
+ 'deviceConfig.deviceWillRestart': '璁惧灏嗛噸鍚�',
+ 'deviceConfig.pleaseDisconnectXiaozhiHotspot': '璇锋柇寮�xiaozhi鐑偣杩炴帴',
+ 'deviceConfig.configFailed': '閰嶇綉澶辫触',
+ 'deviceConfig.wifiConfigFailed': 'WiFi閰嶇綉澶辫触',
+ 'deviceConfig.pleaseCheckNetworkConnection': '璇锋鏌ョ綉缁滆繛鎺�',
+ 'deviceConfig.startWifiConfigButton': '寮�濮嬮厤缃�',
+ 'deviceConfig.configuring': '閰嶇綉涓�...',
+ 'deviceConfig.wifiConfigInstructions': 'WiFi閰嶇綉璇存槑',
+ 'deviceConfig.phoneConnectXiaozhiHotspot': '鎵嬫満杩炴帴xiaozhi鐑偣',
+ 'deviceConfig.selectTargetWifiNetwork': '閫夋嫨鐩爣WiFi缃戠粶',
+ 'deviceConfig.enterWifiPasswordIfNeeded': '濡傛湁闇�瑕佽杈撳叆WiFi瀵嗙爜',
+ 'deviceConfig.clickStartConfigAndWait': '鐐瑰嚮寮�濮嬮厤缃戝苟绛夊緟',
+ 'deviceConfig.afterConfigSuccessDeviceWillRestart': '閰嶇綉鎴愬姛鍚庤澶囧皢鑷姩閲嶅惎',
+ 'deviceConfig.audioPlaybackError': '闊抽鎾斁閿欒',
+ 'deviceConfig.playbackFailed': '鎾斁澶辫触',
+
+ // Voiceprint page
+ 'voiceprint.audioNotExist': '璇ラ煶棰戜笉瀛樺湪',
+ 'voiceprint.getAudioFailed': '鑾峰彇闊抽澶辫触',
+ 'voiceprint.audioPlayFailed': '闊抽鎾斁澶辫触',
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/i18n/zh_TW.ts b/xiaozhi-esp32-server/main/manager-mobile/src/i18n/zh_TW.ts
new file mode 100644
index 0000000..7a21692
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/i18n/zh_TW.ts
@@ -0,0 +1,597 @@
+// 绻侀珨涓枃瑾炶█鍖�
+export default {
+ // TabBar
+ 'tabBar.home': '棣栭爜',
+ 'tabBar.deviceConfig': '閰嶇恫',
+ 'tabBar.settings': '绯荤当',
+ // 瑷疆闋侀潰妯欓
+ 'settings.title': '瑷疆',
+ // 鐧婚寗闋侀潰
+ 'login.pageTitle': '鐧婚寗',
+ 'login.navigationTitle': '鐧婚寗',
+ 'login.fetchConfigError': '鐛插彇閰嶇疆澶辨晽:',
+ 'login.selectLanguage': '閬告搰瑾炶█',
+ 'login.selectLanguageTip': '绻侀珨',
+ 'login.welcomeBack': '姝¤繋鍥炰締',
+ 'login.pleaseLogin': '璜嬬櫥閷勬偍鐨勮超鎴�',
+ 'login.enterUsername': '璜嬭几鍏ョ敤鎴跺悕',
+ 'login.enterPassword': '璜嬭几鍏ュ瘑纰�',
+ 'login.enterCaptcha': '璜嬭几鍏ラ璀夌⒓',
+ 'login.loginButton': '鐧婚寗',
+ 'login.loggingIn': '鐧婚寗涓�...',
+ 'login.noAccount': '鏂扮敤鎴惰ɑ鍐�',
+ 'login.enterPhone': '璜嬭几鍏ユ墜姗熻櫉纰�',
+ 'login.selectCountry': '閬告搰鍦嬪/鍦板崁',
+ 'login.confirm': '纰鸿獚',
+ 'login.serverSetting': '鏈嶅嫏绔ō缃�',
+ 'login.requiredUsername': '鐢ㄦ埗鍚嶄笉鑳界偤绌�',
+ 'login.requiredPassword': '瀵嗙⒓涓嶈兘鐐虹┖',
+ 'login.requiredCaptcha': '椹楄瓑纰间笉鑳界偤绌�',
+ 'login.requiredMobile': '璜嬭几鍏ユ纰虹殑鎵嬫铏熺⒓',
+ 'login.captchaError': '鍦栧舰椹楄瓑纰奸尟瑾�',
+ 'login.forgotPassword': '蹇樿瀵嗙⒓',
+ 'login.userAgreement': '鐢ㄦ埗鍗旇',
+ 'login.privacyPolicy': '闅辩鏀跨瓥',
+
+ // 蹇樿瀵嗙⒓闋侀潰
+ 'retrievePassword.title': '閲嶇疆瀵嗙⒓',
+ 'retrievePassword.subtitle': '璜嬭几鍏ユ偍鐨勬墜姗熻櫉纰间互閲嶇疆瀵嗙⒓',
+ 'retrievePassword.mobileRequired': '璜嬭几鍏ユ墜姗熻櫉纰�',
+ 'retrievePassword.inputCorrectMobile': '璜嬭几鍏ユ纰虹殑鎵嬫铏熺⒓',
+ 'retrievePassword.captchaRequired': '璜嬭几鍏ュ湒褰㈤璀夌⒓',
+ 'retrievePassword.mobileCaptchaRequired': '璜嬭几鍏ユ墜姗熼璀夌⒓',
+ 'retrievePassword.newPasswordRequired': '璜嬭几鍏ユ柊瀵嗙⒓',
+ 'retrievePassword.confirmNewPasswordRequired': '璜嬬⒑瑾嶆柊瀵嗙⒓',
+ 'retrievePassword.passwordsNotMatch': '鍏╂杓稿叆鐨勫瘑纰间笉涓�鑷�',
+ 'retrievePassword.mobilePlaceholder': '璜嬭几鍏ユ墜姗熻櫉纰�',
+ 'retrievePassword.captchaPlaceholder': '璜嬭几鍏ュ湒褰㈤璀夌⒓',
+ 'retrievePassword.mobileCaptchaPlaceholder': '璜嬭几鍏ユ墜姗熼璀夌⒓',
+ 'retrievePassword.newPasswordPlaceholder': '璜嬭几鍏ユ柊瀵嗙⒓',
+ 'retrievePassword.confirmNewPasswordPlaceholder': '璜嬬⒑瑾嶆柊瀵嗙⒓',
+ 'retrievePassword.getMobileCaptcha': '鐛插彇椹楄瓑纰�',
+ 'retrievePassword.captchaSendSuccess': '椹楄瓑纰肩櫦閫佹垚鍔�',
+ 'retrievePassword.passwordUpdateSuccess': '瀵嗙⒓閲嶇疆鎴愬姛',
+ 'retrievePassword.resetButton': '閲嶇疆瀵嗙⒓',
+ 'retrievePassword.goToLogin': '杩斿洖鐧婚寗',
+
+ // 瑷诲唺闋侀潰
+ 'register.pageTitle': '瑷诲唺',
+ 'register.createAccount': '鍓靛缓璩埗',
+ 'register.enterUsername': '璜嬭几鍏ョ敤鎴跺悕',
+ 'register.enterPassword': '璜嬭几鍏ュ瘑纰�',
+ 'register.confirmPassword': '璜嬬⒑瑾嶅瘑纰�',
+ 'register.enterPhone': '璜嬭几鍏ユ墜姗熻櫉纰�',
+ 'register.enterCode': '璜嬭几鍏ラ璀夌⒓',
+ 'register.getCode': '鐛插彇椹楄瓑纰�',
+ 'register.agreeTerms': '鎴戝凡闁辫畝涓﹀悓鎰�',
+ 'register.terms': '銆婄敤鎴跺崝璀般��',
+ 'register.privacy': '銆婇毐绉佹斂绛栥��',
+ 'register.registerButton': '瑷诲唺',
+ 'register.registering': '瑷诲唺涓�...',
+ 'register.haveAccount': '宸叉湁璩埗锛�',
+ 'register.loginNow': '绔嬪嵆鐧婚寗',
+ 'register.selectCountry': '閬告搰鍦嬪/鍦板崁',
+ 'register.confirm': '纰鸿獚',
+ 'register.captchaSendSuccess': '椹楄瓑纰肩櫦閫佹垚鍔�',
+
+ // 棣栭爜
+ 'home.pageTitle': '棣栭爜',
+ 'home.createAgent': '鍓靛缓鏅鸿兘楂�',
+ 'home.agentName': '鏅鸿兘楂�',
+ 'home.modelInfo': '妯″瀷淇℃伅',
+ 'home.lastActive': '鏈�杩戞椿韬�',
+ 'home.greeting': '浣犲ソ锛屽皬鏅�',
+ 'home.subtitle': '璁撴垜鍊戝害閬�',
+ 'home.wonderfulDay': '缇庡ソ鐨勪竴澶╋紒',
+ 'home.emptyState': '鏆劇鏅鸿兘楂�',
+ 'home.deviceManagement': '瑷倷绠$悊',
+ 'home.lastConversation': '鏈�杩戝皪瑭憋細',
+ 'home.delete': '鍒櫎',
+ 'home.createFirstAgent': '榛炴搳鍙充笅瑙� + 铏熷壍寤烘偍鐨勭涓�鍊嬫櫤鑳介珨',
+ 'home.dialogTitle': '鍓靛缓鏅鸿兘楂�',
+ 'home.inputPlaceholder': '渚嬪锛氬鏈嶅姪鎵嬨�佽獮闊冲姪鐞嗐�佺煡璀樺晱绛�',
+ 'home.createError': '鏆辩ū闀峰害蹇呴爤鍦� 1 鍒� 64 鍊嬪瓧鍏冧箣闁撱��',
+ 'home.createNow': '绔嬪嵆鍓靛缓',
+ 'home.deleteConfirmTitle': '鍒櫎鏅鸿兘楂�',
+ 'home.confirmDeleteAgent': '鍒櫎寰岀劇娉曞京鍘熴�傝珛杓稿叆鏅鸿兘楂斿悕绋便�寋agentName}銆嶇⒑瑾嶅埅闄ゃ��',
+ 'home.deleteAgentNamePlaceholder': '璜嬭几鍏ユ櫤鑳介珨鍚嶇ū',
+ 'home.deleteAgentNameMismatch': '杓稿叆鐨勬櫤鑳介珨鍚嶇ū涓嶅尮閰�',
+ 'home.deleteAgentPasteForbidden': '璜嬫墜鍕曡几鍏ユ櫤鑳介珨鍚嶇ū锛屼笉鍏佽ū璨间笂',
+ 'home.deleteAgentMissingInfo': '鐒℃硶纰鸿獚鏅鸿兘楂旇硣瑷婏紝璜嬮噸鏂版暣鐞嗗緦鍐嶈│',
+ 'home.deleteAgentSuccess': '鏅鸿兘楂斿埅闄ゆ垚鍔�',
+ 'home.justNow': '鍓涘墰',
+ 'home.minutesAgo': '鍒嗛悩鍓�',
+ 'home.hoursAgo': '灏忔檪鍓�',
+ 'home.daysAgo': '澶╁墠',
+ 'home.languageModel': '瑾炶█妯″瀷',
+ 'home.voiceModel': '闊宠壊妯″瀷',
+
+ // Agent闋侀潰
+ 'agent.pageTitle': '鏅鸿兘楂�',
+ 'agent.roleConfig': '瑙掕壊閰嶇疆',
+ 'agent.deviceManagement': '瑷倷绠$悊',
+ 'agent.chatHistory': '鑱婂ぉ瑷橀寗',
+ 'agent.voiceprintManagement': '鑱茬磱绠$悊',
+ 'agent.editTitle': '绶ㄨ集鏅鸿兘楂�',
+ 'agent.toolsTitle': '绶ㄨ集鍔熻兘',
+ 'agent.voiceActivityDetection': '瑾為煶娲诲嫊妾㈡脯',
+ 'agent.speechRecognition': '瑾為煶璀樺垾',
+ 'agent.largeLanguageModel': '澶ц獮瑷�妯″瀷',
+ 'agent.save': '淇濆瓨',
+ 'agent.cancel': '鍙栨秷',
+ // Agent绶ㄨ集闋侀潰
+ 'agent.basicInfo': '鍩虹璩囪▕',
+ 'agent.agentName': '鍔╂墜鏆辩ū',
+ 'agent.inputAgentName': '璜嬭几鍏ュ姪鎵嬫毐绋�',
+ 'agent.roleMode': '瑙掕壊妯″紡',
+ 'agent.agentTag': '鏅烘収楂旀绫�',
+ 'agent.addAgentTag': '娣诲姞妯欑堡',
+ 'agent.inputAgentTag': '璜嬭几鍏ユ櫤鎱ч珨妯欑堡',
+ 'agent.contextProvider': '涓婁笅鏂囨簮',
+ 'agent.contextProviderSuccess': '宸叉垚鍔熸坊鍔� {count} 鍊嬫簮銆�',
+ 'agent.contextProviderDocLink': '濡備綍閮ㄧ讲涓婁笅鏂囨簮',
+ 'agent.editContextProvider': '绶ㄨ集婧�',
+ 'agent.roleDescription': '瑙掕壊浠嬬垂',
+ 'agent.inputRoleDescription': '璜嬭几鍏ヨ鑹蹭粙绱�',
+ 'agent.modelConfig': '妯″瀷閰嶇疆',
+ 'agent.vad': '瑾為煶娲诲嫊妾㈡脯',
+ 'agent.asr': '瑾為煶璀樺垾',
+ 'agent.llm': '涓昏獮瑷�妯″瀷',
+ 'agent.slm': '灏忓弮鏁告ā鍨�',
+ 'agent.vllm': '瑕栬澶фā鍨�',
+ 'agent.intent': '鎰忓湒璀樺垾',
+ 'agent.memory': '瑷樻喍',
+ 'agent.voiceSettings': '瑾為煶瑷疆',
+ 'agent.tts': '瑾為煶鍚堟垚',
+ 'agent.voiceprint': '瑙掕壊闊宠壊',
+ 'agent.plugins': '澶栨帥',
+ 'agent.editFunctions': '绶ㄨ集鍔熻兘',
+ 'agent.historyMemory': '姝峰彶瑷樻喍',
+ 'agent.memoryContent': '瑷樻喍鍏у',
+ 'agent.saving': '鍎插瓨涓�...',
+ 'agent.saveSuccess': '鍎插瓨鎴愬姛',
+ 'agent.saveFail': '鍎插瓨澶辨晽',
+ 'agent.ttsOptionsLoadFailed': '瑾為煶閬搁爡杓夊叆澶辨晽锛屽凡淇濈暀鍘熶締鐨勮獮闊宠ō瀹�',
+ 'agent.loadFail': '鍔犺級澶辨晽',
+ 'agent.pleaseInputAgentName': '璜嬭几鍏ュ姪鎵嬫毐绋�',
+ 'agent.pleaseInputRoleDescription': '璜嬭几鍏ヨ鑹蹭粙绱�',
+ 'agent.pleaseSelect': '璜嬮伕鎿�',
+ 'agent.reportText': '涓婃姤鏂囧瓧',
+ 'agent.reportTextVoice': '涓婃姤鏂囧瓧+璇煶',
+ 'agent.reportMode': '涓婂牨妯″紡',
+ 'agent.language': '灏嶈┍瑾炶█',
+ 'agent.languageConfig': '瑾為�熼煶瑾�',
+ 'agent.ttsVolume': '闊抽噺',
+ 'agent.ttsRate': '瑾為��',
+ 'agent.ttsPitch': '闊宠',
+ 'agent.volumeHint': '-100=鏈�灏�, 0=妯欐簴, 100=鏈�澶�',
+ 'agent.speedHint': '-100=鏈�鎱�, 0=妯欐簴, 100=鏈�蹇�',
+ 'agent.pitchHint': '-100=鏈�浣�, 0=妯欐簴, 100=鏈�楂�',
+
+ // 鏅鸿兘楂旀鍙茬増鏈�
+ 'agentSnapshot.title': '姝峰彶鐗堟湰',
+ 'agentSnapshot.empty': '鏆劇姝峰彶鐗堟湰',
+ 'agentSnapshot.emptyTip': '淇濆瓨閰嶇疆寰屾渻鐢熸垚姝峰彶鐗堟湰',
+ 'agentSnapshot.version': '鐗堟湰',
+ 'agentSnapshot.createdAt': '淇濆瓨鏅傞枔',
+ 'agentSnapshot.source': '渚嗘簮',
+ 'agentSnapshot.changedFields': '璁婃洿鍏у',
+ 'agentSnapshot.view': '鏌ョ湅',
+ 'agentSnapshot.restore': '鎭㈠京',
+ 'agentSnapshot.delete': '鍒櫎',
+ 'agentSnapshot.loadMore': '杓夊叆鏇村',
+ 'agentSnapshot.detailTitle': '璁婃洿瑭虫儏',
+ 'agentSnapshot.restorePreviewTitle': '鎭㈠京闋愯',
+ 'agentSnapshot.confirmRestore': '纰鸿獚鎭㈠京',
+ 'agentSnapshot.currentVersion': '鏈�鏂板揩鐓�',
+ 'agentSnapshot.beforeChange': '璁婂寲鍓�',
+ 'agentSnapshot.afterChange': '璁婂寲寰�',
+ 'agentSnapshot.beforeRestore': '鎭㈠京鍓�',
+ 'agentSnapshot.afterRestore': '鎭㈠京寰�',
+ 'agentSnapshot.configValue': '閰嶇疆鍊�',
+ 'agentSnapshot.emptyValue': '鐒�',
+ 'agentSnapshot.secretRedacted': '瀵嗛懓宸查毐钘�',
+ 'agentSnapshot.redactedValueChanged': '鍊煎凡鑴晱锛屼絾纰烘湁璁婂寲',
+ 'agentSnapshot.noChangedContent': '鐒″彲椤ず璁婃洿',
+ 'agentSnapshot.recordedChange': '瑷橀寗鏅傜櫦鐢熻畩鏇�',
+ 'agentSnapshot.noRestoreNeeded': '鐩墠瑷畾鑸囩洰妯欑増鏈浉鍚岋紝鐒¢渶鎭㈠京',
+ 'agentSnapshot.unsavedChangesTitle': '瀛樺湪鏈劜瀛樹慨鏀�',
+ 'agentSnapshot.unsavedChangesWarning': '绻肩簩鎭㈠京鏈冩斁妫勭洰鍓嶉爜闈腑灏氭湭鍎插瓨鐨勪慨鏀广��',
+ 'agentSnapshot.discardAndRestore': '鏀炬淇敼涓︽仮寰�',
+ 'agentSnapshot.restoreConfirm': '纰哄畾鎭㈠京鍒扮増鏈� #{version}锛熺洰鍓嶈ō瀹氭渻淇濈暀鍦ㄦ鍙蹭腑銆�',
+ 'agentSnapshot.restoreMemoryWarning': '鎭㈠京鍒扮劇瑷樻喍鐗堟湰鏈冩竻绌鸿┎鏅鸿兘楂旇亰澶╄閷勶紝璜嬬⒑瑾嶉ⅷ闅��',
+ 'agentSnapshot.restoreChatHistoryDestructiveWarning': '姝ゆ仮寰╂渻姘镐箙鍒櫎瑭叉櫤鑳介珨鐝炬湁鐨勮亰澶╄閷勩�傝亰澶╄閷勪笉鍖呭惈鍦ㄩ厤缃揩鐓т腑锛屽埅闄ゅ緦鐒℃硶閫忛亷姝峰彶鐗堟湰鎭㈠京銆�',
+ 'agentSnapshot.restoreSuccess': '鐗堟湰宸叉仮寰�',
+ 'agentSnapshot.restoreFailed': '鐗堟湰鎭㈠京澶辨晽',
+ 'agentSnapshot.reloadAfterRestorePending': '姝e湪閲嶆柊杓夊叆鎭㈠京寰岀殑閰嶇疆鍜屾绫わ紝瀹屾垚鍓嶇劇娉曞劜瀛樸��',
+ 'agentSnapshot.reloadAfterRestoreFailed': '鐒℃硶閲嶆柊杓夊叆鎭㈠京寰岀殑閰嶇疆鎴栨绫ゃ�傜偤闃叉鑸婅硣鏂欒钃嬫仮寰╃祼鏋滐紝鍎插瓨宸插仠鐢紝璜嬮噸瑭︺��',
+ 'agentSnapshot.retryReload': '閲嶆柊杓夊叆',
+ 'agentSnapshot.mutationBusy': '閰嶇疆姝e湪鍎插瓨鎴栭噸鏂拌級鍏ワ紝璜嬪畬鎴愬緦鍐嶉枊鍟熸鍙茬増鏈垨鎭㈠京銆�',
+ 'agentSnapshot.deleteConfirm': '纰哄畾鍒櫎鐗堟湰 #{version}锛熸鎿嶄綔涓嶅彲寰╁師銆�',
+ 'agentSnapshot.deleteSuccess': '姝峰彶鐗堟湰宸插埅闄�',
+ 'agentSnapshot.deleteFailed': '姝峰彶鐗堟湰鍒櫎澶辨晽',
+ 'agentSnapshot.fetchFailed': '鐛插彇姝峰彶鐗堟湰澶辨晽',
+ 'agentSnapshot.detailFailed': '鐛插彇鐗堟湰瑭虫儏澶辨晽',
+ 'agentSnapshot.correctWordCount': '鍏� {count} 鍊嬫浛鎻涜',
+ 'agentSnapshot.source.config': '閰嶇疆淇濆瓨',
+ 'agentSnapshot.source.current': '鐣跺墠閰嶇疆',
+ 'agentSnapshot.source.restore': '鎭㈠京绲愭灉',
+ 'agentSnapshot.source.initial': '鍒濆鐗堟湰',
+ 'agentSnapshot.field.initial': '鍒濆蹇収',
+ 'agentSnapshot.field.agentCode': '鏅鸿兘楂旂法纰�',
+ 'agentSnapshot.field.agentName': '鍔╂墜鏆辩ū',
+ 'agentSnapshot.field.asrModelId': '瑾為煶璀樺垾',
+ 'agentSnapshot.field.vadModelId': '瑾為煶娲诲嫊妾㈡脯',
+ 'agentSnapshot.field.llmModelId': '涓昏獮瑷�妯″瀷',
+ 'agentSnapshot.field.slmModelId': '灏忓弮鏁告ā鍨�',
+ 'agentSnapshot.field.vllmModelId': '瑕栬澶фā鍨�',
+ 'agentSnapshot.field.ttsModelId': '瑾為煶鍚堟垚',
+ 'agentSnapshot.field.ttsVoiceId': '鑱查煶闊宠壊',
+ 'agentSnapshot.field.ttsLanguage': '灏嶈┍瑾炶█',
+ 'agentSnapshot.field.ttsVolume': '闊抽噺',
+ 'agentSnapshot.field.ttsRate': '瑾為��',
+ 'agentSnapshot.field.ttsPitch': '闊宠',
+ 'agentSnapshot.field.memModelId': '瑷樻喍妯″紡',
+ 'agentSnapshot.field.intentModelId': '鎰忓湒璀樺垾',
+ 'agentSnapshot.field.chatHistoryConf': '鑱婂ぉ瑷橀寗閰嶇疆',
+ 'agentSnapshot.field.systemPrompt': '瑙掕壊浠嬬垂',
+ 'agentSnapshot.field.summaryMemory': '瑷樻喍',
+ 'agentSnapshot.field.langCode': '瑾炶█绶ㄧ⒓',
+ 'agentSnapshot.field.language': '浜や簰瑾炵ó',
+ 'agentSnapshot.field.sort': '鎺掑簭',
+ 'agentSnapshot.field.functions': '鎻掍欢',
+ 'agentSnapshot.field.contextProviders': '涓婁笅鏂囨簮',
+ 'agentSnapshot.field.correctWordFileIds': '鏇挎彌瑭�',
+ 'agentSnapshot.field.tagNames': '鏅鸿兘楂旀绫�',
+ 'agentSnapshot.chatHistoryConf.none': '涓嶈閷勮亰澶╄閷�',
+ 'agentSnapshot.chatHistoryConf.text': '涓婂牨鏂囧瓧',
+ 'agentSnapshot.chatHistoryConf.textVoice': '涓婂牨鏂囧瓧+瑾為煶',
+ 'agentSnapshot.model.Memory_nomem': '鐒¤鎲�',
+ 'agentSnapshot.model.Memory_mem_local_short': '鏈湴鐭湡瑷樻喍',
+ 'agentSnapshot.model.Memory_mem0ai': 'Mem0AI 瑷樻喍',
+ 'agentSnapshot.model.Memory_mem_report_only': '鍍呬笂鍫�',
+ 'agentSnapshot.model.Intent_nointent': '鐒℃剰鍦栬瓨鍒�',
+ 'agentSnapshot.model.Intent_intent_llm': '澶栭儴澶фā鍨嬫剰鍦栬瓨鍒�',
+ 'agentSnapshot.model.Intent_function_call': '澶фā鍨嬪嚱鏁歌鐢�',
+
+ // 涓婁笅鏂囨簮瀵硅瘽妗嗙浉鍏�
+ 'contextProviderDialog.title': '绶ㄨ集婧�',
+ 'contextProviderDialog.noContextApi': '鏆劇涓婁笅鏂嘇PI',
+ 'contextProviderDialog.add': '娣诲姞',
+ 'contextProviderDialog.apiUrl': '浠嬮潰鍦板潃',
+ 'contextProviderDialog.apiUrlPlaceholder': 'http://api.example.com/data',
+ 'contextProviderDialog.requestHeaders': '璜嬫眰闋�',
+ 'contextProviderDialog.headerKeyPlaceholder': 'Key',
+ 'contextProviderDialog.headerValuePlaceholder': 'Value',
+ 'contextProviderDialog.noHeaders': '鏆劇 Headers',
+ 'contextProviderDialog.addHeader': '娣诲姞 Header',
+ 'contextProviderDialog.cancel': '鍙栨秷',
+ 'contextProviderDialog.confirm': '纰哄畾',
+
+ // 鎵嬪嫊娣诲姞瑷倷灏嶈┍妗嗙浉闂�
+ 'manualAddDeviceDialog.title': '鎵嬪嫊娣诲姞瑷倷',
+ 'manualAddDeviceDialog.deviceType': '瑷倷鍨嬭櫉',
+ 'manualAddDeviceDialog.deviceTypePlaceholder': '璜嬮伕鎿囪ō鍌欏瀷铏�',
+ 'manualAddDeviceDialog.firmwareVersion': '鍥轰欢鐗堟湰',
+ 'manualAddDeviceDialog.firmwareVersionPlaceholder': '璜嬭几鍏ュ浐浠剁増鏈�',
+ 'manualAddDeviceDialog.macAddress': 'Mac鍦板潃',
+ 'manualAddDeviceDialog.macAddressPlaceholder': '璜嬭几鍏ac鍦板潃',
+ 'manualAddDeviceDialog.confirm': '纰哄畾',
+ 'manualAddDeviceDialog.cancel': '鍙栨秷',
+ 'manualAddDeviceDialog.requiredMacAddress': '璜嬭几鍏ac鍦板潃',
+ 'manualAddDeviceDialog.invalidMacAddress': '璜嬭几鍏ユ纰虹殑Mac鍦板潃鏍煎紡锛屼緥濡傦細00:1A:2B:3C:4D:5E',
+ 'manualAddDeviceDialog.requiredDeviceType': '璜嬮伕鎿囪ō鍌欏瀷铏�',
+ 'manualAddDeviceDialog.requiredFirmwareVersion': '璜嬭几鍏ュ浐浠剁増鏈�',
+ 'manualAddDeviceDialog.getFirmwareTypeFailed': '鐛插彇鍥轰欢椤炲瀷澶辨晽',
+ 'manualAddDeviceDialog.addSuccess': '瑷倷娣诲姞鎴愬姛',
+ 'manualAddDeviceDialog.addFailed': '娣诲姞澶辨晽',
+ 'manualAddDeviceDialog.bindWithCode': '6浣嶉璀夌⒓缍佸畾',
+
+ // 鑱婂ぉ姝峰彶闋侀潰
+ 'chatHistory.getChatSessions': '鐛插彇鑱婂ぉ鏈冭┍鍒楄〃',
+ 'chatHistory.noSelectedAgent': '娌掓湁閬镐腑鐨勬櫤鑳介珨',
+ 'chatHistory.getChatSessionsFailed': '鐛插彇鑱婂ぉ鏈冭┍鍒楄〃澶辨晽:',
+ 'chatHistory.unknownTime': '鏈煡鏅傞枔',
+ 'chatHistory.justNow': '鍓涘墰',
+ 'chatHistory.minutesAgo': '{minutes}鍒嗛悩鍓�',
+ 'chatHistory.hoursAgo': '{hours}灏忔檪鍓�',
+ 'chatHistory.daysAgo': '{days}澶╁墠',
+ 'chatHistory.conversationRecord': '灏嶈┍瑷橀寗',
+ 'chatHistory.totalChats': '鍏� {count} 姊濆皪瑭�',
+ 'chatHistory.loading': '鍔犺級涓�...',
+ 'chatHistory.noMoreData': '娌掓湁鏇村鏁告摎浜�',
+ 'chatHistory.noChatRecords': '鏆劇鑱婂ぉ瑷橀寗',
+ 'chatHistory.chatRecordsDescription': '鑸囨櫤鑳介珨鐨勫皪瑭辫閷勬渻椤ず鍦ㄩ�欒!',
+ // 鑱婂ぉ瑭虫儏闋侀潰
+ 'chatHistory.pageTitle': '鑱婂ぉ瑭虫儏',
+ 'chatHistory.assistantName': '鏅鸿兘鍔╂墜',
+ 'chatHistory.userName': '鐢ㄦ埗',
+ 'chatHistory.aiAssistantName': 'AI鍔╂墜',
+ 'chatHistory.loadFailed': '鐛插彇鑱婂ぉ瑷橀寗澶辨晽',
+ 'chatHistory.parameterError': '闋侀潰鍙冩暩閷',
+ 'chatHistory.invalidAudioId': '闊抽牷ID鐒℃晥',
+ 'chatHistory.audioPlayFailed': '闊抽牷鎾斁澶辨晽',
+ 'chatHistory.playAudioFailed': '鎾斁闊抽牷澶辨晽',
+
+ // 瑷倷绠$悊闋侀潰
+ 'device.pageTitle': '瑷倷绠$悊',
+ 'device.noDevices': '鏆劇瑷倷',
+ 'device.macAddress': 'MAC鍦板潃',
+ 'device.firmwareVersion': '鍥轰欢鐗堟湰',
+ 'device.lastConnected': '鏈�杩戝皪瑭�',
+ 'device.otaUpdate': 'OTA鍗囩礆',
+ 'device.unbind': '瑙i櫎缍佸畾',
+ 'device.confirmUnbind': '纰哄畾瑕佽В缍佽ō鍌�',
+ 'device.bindDevice': '缍佸畾鏂拌ō鍌�',
+ 'device.deviceType': '瑷倷椤炲瀷',
+ 'device.loading': '鍔犺級涓�...',
+ 'device.neverConnected': '寰炴湭閫f帴',
+ 'device.justNow': '鍓涘墰',
+ 'device.minutesAgo': '{minutes}鍒嗛悩鍓�',
+ 'device.hoursAgo': '{hours}灏忔檪鍓�',
+ 'device.daysAgo': '{days}澶╁墠',
+ 'device.otaAutoUpdateEnabled': 'OTA鑷嫊鍗囩礆宸查枊鍟�',
+ 'device.otaAutoUpdateDisabled': 'OTA鑷嫊鍗囩礆宸查棞闁�',
+ 'device.operationFailed': '鎿嶄綔澶辨晽锛岃珛閲嶈│',
+ 'device.deviceUnbound': '瑷倷宸茶В闄ょ秮瀹�',
+ 'device.unbindFailed': '瑙i櫎缍佸畾澶辨晽锛岃珛閲嶈│',
+ 'device.unbindDevice': '瑙i櫎缍佸畾瑷倷',
+ 'device.confirmUnbindDevice': '纰哄畾瑕佽В闄ょ秮瀹氳ō鍌� "{macAddress}" 鍡庯紵',
+ 'device.cancel': '鍙栨秷',
+ 'device.noDevice': '鏆劇瑷倷',
+ 'device.pleaseSelectAgent': '璜嬪厛閬告搰鏅鸿兘楂�',
+ 'device.deviceBindSuccess': '瑷倷缍佸畾鎴愬姛锛�',
+ 'device.bindFailed': '缍佸畾澶辨晽锛岃珛妾㈡煡椹楄瓑纰兼槸鍚︽纰�',
+ 'device.enterDeviceCode': '璜嬭几鍏ヨō鍌欓璀夌⒓',
+ 'device.bindNow': '绔嬪嵆缍佸畾',
+ 'device.lastConnection': '鏈�杩戝皪瑭�',
+ 'device.clickToBindFirstDevice': '榛炴搳鍙充笅瑙� + 铏熺秮瀹氭偍鐨勭涓�鍊嬭ō鍌�',
+
+ // 閫氱敤
+ 'common.success': '鎴愬姛',
+ 'common.fail': '澶辨晽',
+ 'common.loading': '鍔犺級涓�...',
+ 'common.confirm': '纰鸿獚',
+ 'common.cancel': '鍙栨秷',
+ 'common.delete': '鍒櫎',
+ 'common.edit': '绶ㄨ集',
+ 'common.add': '娣诲姞',
+ 'common.pleaseSelect': '璜嬮伕鎿�',
+ 'common.unknownError': '鏈煡閷',
+ 'common.networkError': '缍茶矾閷',
+
+ // SM2鍔犲瘑鐩搁棞閷娑堟伅
+ 'sm2.publicKeyNotConfigured': 'SM2鍏懓鏈厤缃紝璜嬭伅绻鐞嗗摗',
+ 'sm2.encryptionFailed': '瀵嗙⒓鍔犲瘑澶辨晽',
+ 'sm2.keyGenerationFailed': '閲戦懓灏嶇敓鎴愬け鏁�',
+ 'sm2.invalidPublicKey': '鐒℃晥鐨勫叕閼版牸寮�',
+ 'sm2.encryptionError': '鍔犲瘑閬庣▼涓櫦鐢熼尟瑾�',
+ 'sm2.publicKeyRetry': '姝e湪閲嶈│鐛插彇鍏懓...',
+ 'sm2.publicKeyRetryFailed': '鍏懓鐛插彇閲嶈│澶辨晽',
+
+ // Voiceprint page
+ 'voiceprint.noSelectedAgent': '娌掓湁閬镐腑鐨勬櫤鑳介珨',
+ 'voiceprint.pleaseSelectAgent': '璜嬪厛閬告搰鏅鸿兘楂�',
+ 'voiceprint.fetchHistoryFailed': '鐛插彇灏嶈┍瑷橀寗澶辨晽',
+ 'voiceprint.clickToSelectVector': '榛炴搳閬告搰鑱茬磱鍚戦噺',
+ 'voiceprint.pleaseInputName': '璜嬭几鍏ュ鍚�',
+ 'voiceprint.pleaseSelectVector': '璜嬮伕鎿囪伈绱嬪悜閲�',
+ 'voiceprint.addSuccess': '娣诲姞鎴愬姛',
+ 'voiceprint.addFailed': '娣诲姞瑾┍浜哄け鏁�',
+ 'voiceprint.editSuccess': '绶ㄨ集鎴愬姛',
+ 'voiceprint.editFailed': '绶ㄨ集瑾┍浜哄け鏁�',
+ 'voiceprint.deleteConfirmMsg': '纰哄畾瑕佸埅闄ら�欏�嬭瑭变汉鍡庯紵',
+ 'voiceprint.deleteConfirmTitle': '纰鸿獚鍒櫎',
+ 'voiceprint.deleteSuccess': '鍒櫎鎴愬姛',
+ 'voiceprint.loading': '鍔犺級涓�...',
+ 'voiceprint.delete': '鍒櫎',
+ 'voiceprint.emptyTitle': '鏆劇鑱茬磱鏁告摎',
+ 'voiceprint.emptyDesc': '榛炴搳鍙充笅瑙� + 铏熸坊鍔犳偍鐨勭涓�鍊嬭瑭变汉',
+ 'voiceprint.addSpeaker': '娣诲姞瑾┍浜�',
+ 'voiceprint.voiceVector': '鑱茬磱鍚戦噺',
+ 'voiceprint.name': '濮撳悕',
+ 'voiceprint.description': '鎻忚堪',
+ 'voiceprint.pleaseInputDescription': '璜嬭几鍏ユ弿杩�',
+ 'voiceprint.cancel': '鍙栨秷',
+ 'voiceprint.save': '淇濆瓨',
+ 'voiceprint.editSpeaker': '绶ㄨ集瑾┍浜�',
+ 'voiceprint.selectVector': '閬告搰鑱茬磱鍚戦噺',
+ 'voiceprint.voiceprintInterfaceNotConfigured': '鑱茬磱鎺ュ彛鏈厤缃�',
+
+ // 瑷疆闋侀潰
+ 'settings.pageTitle': '瑷疆',
+ 'settings.navigationTitle': '瑷疆',
+ 'settings.networkSettings': '缍茬怠瑷疆',
+ 'settings.serverApiUrl': '鏈嶅嫏绔帴鍙e湴鍧�',
+ 'settings.validServerUrl': '璜嬭几鍏ユ湁鏁堢殑鏈嶅嫏绔湴鍧�锛堜互 http 鎴� https 闁嬮牠锛屼甫浠� /xiaozhi 绲愬熬锛�',
+ 'settings.saveSettings': '淇濆瓨瑷疆',
+ 'settings.resetDefault': '鎭㈠京榛樿獚',
+ 'settings.restartApp': '閲嶅暉鎳夌敤',
+ 'settings.restartNow': '绔嬪嵆閲嶅暉',
+ 'settings.restartLater': '绋嶅緦',
+ // 闂滄柤鎴戝��
+ 'settings.aboutApp': '闂滄柤灏忔櫤鏅烘帶鍙�',
+ 'settings.aboutContent': '灏忔櫤鏅烘帶鍙癨n\n鍩烘柤 Vue.js 3 + uni-app 妲嬪缓鐨勮法骞冲彴绉诲嫊绔鐞嗘噳鐢紝鐐哄皬鏅篍SP32鏅鸿兘纭珨鎻愪緵瑷倷绠$悊銆佹櫤鑳介珨閰嶇疆绛夊姛鑳姐�俓n\n漏 2025 xiaozhi-esp32-server {version}',
+ 'settings.restartSuccess': '宸蹭繚瀛橈紝鍙◢寰屾墜鍕曢噸鍟熸噳鐢�',
+ 'settings.serverUrlSavedAndCacheCleared': '鏈嶅嫏绔湴鍧�宸蹭繚瀛橈紝绶╁瓨宸叉竻闄�',
+ 'settings.resetToDefaultAndCacheCleared': '宸叉仮寰╅粯瑾嶈ō缃紝绶╁瓨宸叉竻闄�',
+ 'settings.resetSuccess': '閲嶇疆鎴愬姛',
+ 'settings.enterServerUrl': '璜嬭几鍏ユ湇鍕欑鍦板潃',
+ 'settings.clearCacheFailed': '娓呴櫎绶╁瓨澶辨晽',
+ 'settings.cacheManagement': '绶╁瓨绠$悊',
+ 'settings.totalCacheSize': '绺界珐瀛樺ぇ灏�',
+ 'settings.appDataSize': '鎳夌敤鏁告摎绺藉ぇ灏�',
+ 'settings.cacheClear': '绶╁瓨娓呯悊',
+ 'settings.clearAllCache': '娓呯┖鎵�鏈夌珐瀛樻暩鎿�',
+ 'settings.clearCache': '娓呴櫎绶╁瓨',
+ 'settings.modifyWillClearCache': '淇敼灏囨竻闄ょ珐瀛�',
+ 'settings.appInfo': '鎳夌敤淇℃伅',
+ 'settings.aboutUs': '闂滄柤鎴戝��',
+ 'settings.appVersion': '鎳夌敤鐗堟湰鑸囧湗闅婁俊鎭�',
+ 'settings.confirmClear': '纰鸿獚娓呴櫎',
+ 'settings.confirmClearMessage': '纰哄畾瑕佹竻闄ゆ墍鏈夌珐瀛樺棊锛熼�欏皣鍒櫎鎵�鏈夋暩鎿氬寘鎷櫥閷勭媭鎱嬶紝闇�瑕侀噸鏂扮櫥閷勩��',
+ 'settings.cacheCleared': '绶╁瓨娓呴櫎鎴愬姛锛屽嵆灏囪烦杞夊埌鐧婚寗闋�',
+ 'settings.languageSettings': '瑾炶█瑷疆',
+ 'settings.language': '瑾炶█',
+ 'settings.selectLanguage': '閬告搰瑾炶█',
+ 'settings.languageChanged': '瑾炶█鍒囨彌鎴愬姛',
+
+ // 娑堟伅鎻愮ず
+ 'message.loginSuccess': '鐧婚寗鎴愬姛锛�',
+ 'message.loginFail': '鐧婚寗澶辨晽',
+ 'message.registerSuccess': '瑷诲唺鎴愬姛',
+ 'message.registerFail': '瑷诲唺澶辨晽',
+ 'message.saveSuccess': '淇濆瓨鎴愬姛',
+ 'message.saveFail': '淇濆瓨澶辨晽',
+ 'message.deleteSuccess': '鍒櫎鎴愬姛',
+ 'message.deleteFail': '鍒櫎澶辨晽',
+ 'message.bindSuccess': '缍佸畾鎴愬姛',
+ 'message.bindFail': '缍佸畾澶辨晽',
+ 'message.unbindSuccess': '瑙i櫎缍佸畾鎴愬姛',
+ 'message.unbindFail': '瑙i櫎缍佸畾澶辨晽',
+ 'message.networkError': '缍茬怠閷锛岃珛妾㈡煡缍茬怠閫f帴',
+ 'message.serverError': '鏈嶅嫏鍣ㄩ尟瑾わ紝璜嬬◢寰屽啀瑭�',
+ 'message.invalidAddress': '鍦板潃鍙兘鐒℃晥锛岃珛妾㈡煡鏈嶅嫏绔槸鍚﹀暉鍕曟垨缍茬怠閫f帴鏄惁姝e父锛� 涔熷彲鑳藉洜HTTPS鍗斿畾鍟忛灏庤嚧鐒℃硶鐧奸�佽珛姹傚父',
+ 'message.languageChanged': '瑾炶█宸插垏鎻�',
+ 'message.passwordError': '甯宠櫉鎴栧瘑纰奸尟瑾�',
+ 'message.phoneRegistered': '姝ゆ墜姗熻櫉宸茬稉瑷诲唺閬�',
+
+ // Agent宸ュ叿闋侀潰
+ 'agent.tools.pageTitle': 'Agent宸ュ叿',
+ 'agent.tools.unselected': '鏈伕',
+ 'agent.tools.selected': '宸查伕',
+ 'agent.tools.noMorePlugins': '鏆劇鏇村鎻掍欢',
+ 'agent.tools.pleaseSelectPlugin': '璜嬮伕鎿囨彃浠跺姛鑳�',
+ 'agent.tools.builtInPlugins': '鍏х疆鎻掍欢',
+ 'agent.tools.mcpAccessPoint': 'mcp鎺ュ叆榛�',
+ 'agent.tools.copy': '瑜囪=',
+ 'agent.tools.noTools': '鏆劇宸ュ叿',
+ 'agent.tools.parameterConfig': '鍙冩暩閰嶇疆',
+ 'agent.tools.noParamsNeeded': '鐒¢渶閰嶇疆鍙冩暩',
+ 'agent.tools.pleaseInput': '璜嬭几鍏�',
+ 'agent.tools.inputOneItemPerLine': '姣忚杓稿叆涓�鍊嬮爡鐩�',
+ 'agent.tools.pleaseInputValidJson': '璜嬭几鍏ユ湁鏁堢殑JSON鏍煎紡',
+ 'agent.tools.enableFunction': '鍟熺敤鍔熻兘',
+ 'agent.tools.toggleFunction': '闁嬪暉鎴栭棞闁夋鍔熻兘',
+ 'agent.tools.jsonFormatError': 'JSON鏍煎紡閷',
+ 'agent.tools.noMcpAddressToCopy': '鏆劇MCP鍦板潃鍙瑁�',
+ 'agent.tools.mcpAddressCopied': 'MCP鍦板潃宸茶瑁藉埌鍓布绨�',
+ 'agent.tools.copyFailed': '瑜囪=澶辨晽锛岃珛閲嶈│',
+ 'agent.tools.defaultValue': '榛樿獚鍊�',
+ 'agent.tools.notSelected': '鏈伕',
+ 'agent.tools.clickToConfigure': '榛炴搳閰嶇疆',
+ 'agent.tools.mcpEndpoint': 'MCP鎺ュ叆榛�',
+ 'agent.tools.eachLineOneItem': '姣忚杓稿叆涓�鍊嬮爡鐩�',
+
+ // 瑷倷閰嶇疆闋侀潰
+ 'deviceConfig.pageTitle': '瑷倷閰嶇疆',
+ 'deviceConfig.wifiConfig': 'WiFi閰嶇恫',
+ 'deviceConfig.ultrasonicConfig': '瓒呰伈娉㈤厤缍�',
+ 'deviceConfig.selectConfigMethod': '閬告搰閰嶇恫鏂瑰紡',
+ 'deviceConfig.networkConfig': '缍茬怠閰嶇疆',
+ 'deviceConfig.selectedNetwork': '閬镐腑缍茬怠',
+ 'deviceConfig.signal': '淇¤櫉',
+ 'deviceConfig.openNetwork': '闁嬫斁缍茬怠',
+ 'deviceConfig.encryptedNetwork': '鍔犲瘑缍茬怠',
+ 'deviceConfig.password': '瀵嗙⒓',
+ 'deviceConfig.pleaseEnterPassword': '璜嬭几鍏iFi瀵嗙⒓',
+ 'deviceConfig.startConfig': '闁嬪閰嶇恫',
+ 'deviceConfig.connectToXiaozhiHotspot': '璜嬪厛閫f帴xiaozhi鐔遍粸 (xiaozhi-XXXXXX)',
+ 'deviceConfig.detecting': '妾㈡脯涓�...',
+ 'deviceConfig.reDetect': '閲嶆柊妾㈡脯',
+ 'deviceConfig.alreadyConnected': '宸查�f帴xiaozhi鐔遍粸',
+ 'deviceConfig.refreshStatus': '鍒锋柊鐙�鎱�',
+ 'deviceConfig.wifiNetworks': 'WiFi缍茬怠',
+ 'deviceConfig.selectWifiNetwork': '閬告搰WiFi缍茬怠',
+ 'deviceConfig.refreshScan': '鍒锋柊鎺冩弿',
+ 'deviceConfig.noWifiNetworks': '鏆劇WiFi缍茬怠',
+ 'deviceConfig.clickToRefreshScan': '璜嬮粸鎿婂埛鏂版巸鎻�',
+ 'deviceConfig.signalStrong': '淇¤櫉寮�',
+ 'deviceConfig.signalGood': '淇¤櫉鑹ソ',
+ 'deviceConfig.signalFair': '淇¤櫉涓�鑸�',
+ 'deviceConfig.signalWeak': '淇¤櫉寮�',
+ 'deviceConfig.channel': '闋婚亾',
+ 'deviceConfig.about': '绱�',
+ 'deviceConfig.seconds': '绉�',
+ 'deviceConfig.generating': '鐢熸垚涓�...',
+ 'deviceConfig.playing': '鎾斁涓�...',
+ 'deviceConfig.generateAndPlaySoundWave': '鐢熸垚涓︽挱鏀捐伈娉�',
+ 'deviceConfig.playSoundWave': '鎾斁鑱叉尝',
+ 'deviceConfig.stopPlaying': '鍋滄鎾斁',
+ 'deviceConfig.autoLoopPlaySoundWave': '鑷嫊寰挵鎾斁鑱叉尝',
+ 'deviceConfig.configAudioFile': '閰嶇恫闊抽牷鏂囦欢',
+ 'deviceConfig.duration': '鏅傞暦',
+ 'deviceConfig.ultrasonicConfigInstructions': '瓒呰伈娉㈤厤缍茶鏄�',
+ 'deviceConfig.ensureWifiNetworkSelectedAndPasswordEntered': '纰轰繚宸查伕鎿嘩iFi缍茬怠涓﹁几鍏ュ瘑纰�',
+ 'deviceConfig.clickGenerateAndPlaySoundWave': '榛炴搳鐢熸垚涓︽挱鏀捐伈娉紝绯荤当鏈冨皣閰嶇恫淇℃伅绶ㄧ⒓鐐洪煶闋�',
+ 'deviceConfig.bringPhoneCloseToXiaozhiDevice': '灏囨墜姗熼潬杩憍iaozhi瑷倷锛堣窛闆�1-2绫筹級',
+ 'deviceConfig.duringAudioPlaybackXiaozhiWillReceive': '闊抽牷鎾斁鏅傦紝xiaozhi鏈冩帴鏀朵甫瑙g⒓閰嶇恫淇℃伅',
+ 'deviceConfig.afterConfigSuccessDeviceWillConnect': '閰嶇恫鎴愬姛寰岃ō鍌欐渻鑷嫊閫f帴WiFi缍茬怠',
+ 'deviceConfig.usesAfskModulation': '浣跨敤AFSK瑾垮埗鎶�琛擄紝閫氶亷1800Hz鍜�1500Hz闋荤巼鍌宠几鏁告摎',
+ 'deviceConfig.ensureModeratePhoneVolume': '璜嬬⒑淇濇墜姗熼煶閲忛仼涓紝閬垮厤鐠板鍣煶骞叉摼',
+ 'deviceConfig.generatingUltrasonicConfigAudio': '鐢熸垚瓒呰伈娉㈤厤缍查煶闋�',
+ 'deviceConfig.configData': '閰嶇恫鏁告摎',
+ 'deviceConfig.dataBytesLength': '鏁告摎瀛楃瘈闀峰害',
+ 'deviceConfig.bitStreamLength': '姣旂壒娴侀暦搴�',
+ 'deviceConfig.base64Length': 'base64闀峰害',
+ 'deviceConfig.audioFileTooLarge': '闊抽牷鏂囦欢閬庡ぇ锛岃珛绺煭SSID鎴栧瘑纰奸暦搴�',
+ 'deviceConfig.audioGenerationSuccess': '闊抽牷鐢熸垚鎴愬姛',
+ 'deviceConfig.samplePoints': '鎺℃ǎ榛炴暩',
+ 'deviceConfig.soundWaveGenerationSuccess': '鑱叉尝鐢熸垚鎴愬姛',
+ 'deviceConfig.audioGenerationFailed': '闊抽牷鐢熸垚澶辨晽',
+ 'deviceConfig.soundWaveGenerationFailed': '鑱叉尝鐢熸垚澶辨晽',
+ 'deviceConfig.pleaseGenerateAudioFirst': '璜嬪厛鐢熸垚闊抽牷',
+ 'deviceConfig.startPlayingUltrasonicConfigAudio': '闁嬪鎾斁瓒呰伈娉㈤厤缍查煶闋�',
+ 'deviceConfig.ultrasonicAudioStartedPlaying': '瓒呰伈娉㈤煶闋婚枊濮嬫挱鏀�',
+ 'deviceConfig.startPlayingConfigSoundWave': '闁嬪鎾斁閰嶇恫鑱叉尝',
+ 'deviceConfig.ultrasonicAudioPlaybackEnded': '瓒呰伈娉㈤煶闋绘挱鏀剧祼鏉�',
+ 'deviceConfig.audioPlaybackFailed': '闊抽牷鎾斁澶辨晽',
+ 'deviceConfig.audioResourceBusy': '闊抽牷璩囨簮绻佸繖锛岃珛绋嶅緦閲嶈│',
+ 'deviceConfig.audioFormatNotSupported': '闊抽牷鏍煎紡涓嶆敮鎻达紝鍙兘鏄痙ata URI鍟忛',
+ 'deviceConfig.audioFileError': '闊抽牷鏂囦欢閷',
+ 'deviceConfig.cleaningUpAudioContext': '娓呯悊闊抽牷涓婁笅鏂�',
+ 'deviceConfig.cleaningUpAudioContextFailed': '娓呯悊闊抽牷涓婁笅鏂囧け鏁�',
+ 'deviceConfig.stoppedPlayingUltrasonicAudio': '鍋滄鎾斁瓒呰伈娉㈤煶闋�',
+ 'deviceConfig.stoppedPlaying': '宸插仠姝㈡挱鏀�',
+ 'deviceConfig.configMethod': '閰嶇恫鏂瑰紡',
+ 'deviceConfig.enterWifiPassword': '璜嬭几鍏iFi瀵嗙⒓',
+ 'deviceConfig.xiaozhi': 'xiaozhi',
+ 'deviceConfig.connectXiaozhiHotspot': '璜嬮�f帴xiaozhi鐔遍粸',
+ 'deviceConfig.wifiScanResponse': 'WiFi鎺冩弿闊挎噳',
+ 'deviceConfig.scanSuccess': '鎺冩弿鎴愬姛',
+ 'deviceConfig.networks': '鍊嬬恫绲�',
+ 'deviceConfig.wifiScanFailed': 'WiFi鎺冩弿澶辨晽',
+ 'deviceConfig.scanFailedCheckConnection': '鎺冩弿澶辨晽锛岃珛妾㈡煡閫f帴',
+ 'deviceConfig.checking': '妾㈡煡涓�',
+ 'deviceConfig.reCheck': '閲嶆柊妾㈡煡',
+ 'deviceConfig.connectedXiaozhiHotspot': '宸查�f帴xiaozhi鐔遍粸',
+ 'deviceConfig.wifiNetwork': 'WiFi缍茬怠',
+ 'deviceConfig.wifiPassword': 'WiFi瀵嗙⒓',
+ 'deviceConfig.scanning': '鎺冩弿涓�',
+ 'deviceConfig.cancel': '鍙栨秷',
+ 'deviceConfig.clickRefreshScan': '璜嬮粸鎿婂埛鏂版巸鎻�',
+ 'deviceConfig.esp32ConnectionCheckFailed': 'ESP32閫f帴妾㈡煡澶辨晽',
+ 'deviceConfig.startWifiConfig': '闁嬪WiFi閰嶇恫',
+ 'deviceConfig.configSuccess': '閰嶇恫鎴愬姛',
+ 'deviceConfig.deviceWillConnectTo': '瑷倷灏囬�f帴鍒�',
+ 'deviceConfig.deviceWillRestart': '瑷倷灏囬噸鍟�',
+ 'deviceConfig.pleaseDisconnectXiaozhiHotspot': '璜嬫柗闁媥iaozhi鐔遍粸閫f帴',
+ 'deviceConfig.configFailed': '閰嶇恫澶辨晽',
+ 'deviceConfig.wifiConfigFailed': 'WiFi閰嶇恫澶辨晽',
+ 'deviceConfig.pleaseCheckNetworkConnection': '璜嬫鏌ョ恫绲¢�f帴',
+ 'deviceConfig.startWifiConfigButton': '闁嬪閰嶇恫',
+ 'deviceConfig.configuring': '閰嶇恫涓�...',
+ 'deviceConfig.wifiConfigInstructions': 'WiFi閰嶇恫瑾槑',
+ 'deviceConfig.phoneConnectXiaozhiHotspot': '鎵嬫閫f帴xiaozhi鐔遍粸',
+ 'deviceConfig.selectTargetWifiNetwork': '閬告搰鐩WiFi缍茬怠',
+ 'deviceConfig.enterWifiPasswordIfNeeded': '濡傛湁闇�瑕佽珛杓稿叆WiFi瀵嗙⒓',
+ 'deviceConfig.clickStartConfigAndWait': '榛炴搳闁嬪閰嶇恫涓︾瓑寰�',
+ 'deviceConfig.afterConfigSuccessDeviceWillRestart': '閰嶇恫鎴愬姛寰岃ō鍌欏皣鑷嫊閲嶅暉',
+ 'deviceConfig.audioPlaybackError': '闊抽牷鎾斁閷',
+ 'deviceConfig.playbackFailed': '鎾斁澶辨晽',
+
+ // Voiceprint page
+ 'voiceprint.audioNotExist': '瑭查煶闋讳笉瀛樺湪',
+ 'voiceprint.getAudioFailed': '鐛插彇闊抽牷澶辨晽',
+ 'voiceprint.audioPlayFailed': '闊抽牷鎾斁澶辨晽',
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/layouts/default.vue b/xiaozhi-esp32-server/main/manager-mobile/src/layouts/default.vue
new file mode 100644
index 0000000..ffde284
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/layouts/default.vue
@@ -0,0 +1,17 @@
+<script lang="ts" setup>
+import type { ConfigProviderThemeVars } from 'wot-design-uni/components/wd-config-provider/types'
+
+const themeVars: ConfigProviderThemeVars = {
+ // colorTheme: 'red',
+ // buttonPrimaryBgColor: '#07c160',
+ // buttonPrimaryColor: '#07c160',
+}
+</script>
+
+<template>
+ <wd-config-provider :theme-vars="themeVars">
+ <slot />
+ <wd-toast />
+ <wd-message-box />
+ </wd-config-provider>
+</template>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/layouts/fg-tabbar/fg-tabbar.vue b/xiaozhi-esp32-server/main/manager-mobile/src/layouts/fg-tabbar/fg-tabbar.vue
new file mode 100644
index 0000000..dd91890
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/layouts/fg-tabbar/fg-tabbar.vue
@@ -0,0 +1,66 @@
+<script setup lang="ts">
+import { tabbarStore } from './tabbar'
+// 'i-carbon-code',
+import { tabbarList as _tabBarList, cacheTabbarEnable, selectedTabbarStrategy, TABBAR_MAP } from './tabbarList'
+
+const customTabbarEnable
+= selectedTabbarStrategy === TABBAR_MAP.CUSTOM_TABBAR_WITH_CACHE
+ || selectedTabbarStrategy === TABBAR_MAP.CUSTOM_TABBAR_WITHOUT_CACHE
+
+/** tabbarList 閲岄潰鐨� path 浠� pages.config.ts 寰楀埌 */
+const tabbarList = _tabBarList.map(item => ({ ...item, path: `/${item.pagePath}` }))
+function selectTabBar({ value: index }: { value: number }) {
+ const url = tabbarList[index].path
+ tabbarStore.setCurIdx(index)
+ if (cacheTabbarEnable) {
+ uni.switchTab({ url })
+ }
+ else {
+ uni.navigateTo({ url })
+ }
+}
+onLoad(() => {
+ // 瑙e喅鍘熺敓 tabBar 鏈殣钘忓鑷存湁2涓� tabBar 鐨勯棶棰�
+ const hideRedundantTabbarEnable = selectedTabbarStrategy === TABBAR_MAP.CUSTOM_TABBAR_WITH_CACHE
+ hideRedundantTabbarEnable
+ && uni.hideTabBar({
+ fail(err) {
+ console.log('hideTabBar fail: ', err)
+ },
+ success(res) {
+ console.log('hideTabBar success: ', res)
+ },
+ })
+})
+</script>
+
+<template>
+ <wd-tabbar
+ v-if="customTabbarEnable"
+ v-model="tabbarStore.curIdx"
+
+ safe-area-inset-bottom bordered placeholder fixed
+ @change="selectTabBar"
+ >
+ <block v-for="(item, idx) in tabbarList" :key="item.path">
+ <wd-tabbar-item v-if="item.iconType === 'uiLib'" :title="item.text" :icon="item.icon" />
+ <wd-tabbar-item
+ v-else-if="item.iconType === 'unocss' || item.iconType === 'iconfont'"
+ :title="item.text"
+ >
+ <template #icon>
+ <view
+ h-40rpx
+ w-40rpx
+ :class="[item.icon, idx === tabbarStore.curIdx ? 'is-active' : 'is-inactive']"
+ />
+ </template>
+ </wd-tabbar-item>
+ <wd-tabbar-item v-else-if="item.iconType === 'local'" :title="item.text">
+ <template #icon>
+ <image :src="item.icon" h-40rpx w-40rpx />
+ </template>
+ </wd-tabbar-item>
+ </block>
+ </wd-tabbar>
+</template>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/layouts/fg-tabbar/tabbar.md b/xiaozhi-esp32-server/main/manager-mobile/src/layouts/fg-tabbar/tabbar.md
new file mode 100644
index 0000000..2485b06
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/layouts/fg-tabbar/tabbar.md
@@ -0,0 +1,17 @@
+# tabbar 璇存槑
+
+`tabbar` 鍒嗕负 `4 绉峘 鎯呭喌锛�
+
+- 0 `鏃� tabbar`锛屽彧鏈変竴涓〉闈㈠叆鍙o紝搴曢儴鏃� `tabbar` 鏄剧ず锛涘父鐢ㄨ涓存椂娲诲姩椤点��
+- 1 `鍘熺敓 tabbar`锛屼娇鐢� `switchTab` 鍒囨崲 tabbar锛宍tabbar` 椤甸潰鏈夌紦瀛樸��
+ - 浼樺娍锛氬師鐢熻嚜甯︾殑 tabbar锛屾渶鍏堟覆鏌擄紝鏈夌紦瀛樸��
+ - 鍔e娍锛氬彧鑳戒娇鐢� 2 缁勫浘鐗囨潵鍒囨崲閫変腑鍜岄潪閫変腑鐘舵�侊紝淇敼棰滆壊鍙兘閲嶆柊鎹㈠浘鐗囷紙鎴栬�呯敤 iconfont锛夈��
+- 2 `鏈夌紦瀛樿嚜瀹氫箟 tabbar`锛屼娇鐢� `switchTab` 鍒囨崲 tabbar锛宍tabbar` 椤甸潰鏈夌紦瀛樸�備娇鐢ㄤ簡绗笁鏂� UI 搴撶殑 `tabbar` 缁勪欢锛屽苟闅愯棌浜嗗師鐢� `tabbar` 鐨勬樉绀恒��
+ - 浼樺娍锛氬彲浠ラ殢鎰忛厤缃嚜宸辨兂瑕佺殑 `svg icon`锛屽垏鎹㈠瓧浣撻鑹叉柟渚裤�傛湁缂撳瓨銆傚彲浠ュ疄鐜板悇绉嶈姳閲岃儭鍝ㄧ殑鍔ㄦ晥绛夈��
+ - 鍔e娍锛氶娆$偣鍑� tababr 浼氶棯鐑併��
+- 3 `鏃犵紦瀛樿嚜瀹氫箟 tabbar`锛屼娇鐢� `navigateTo` 鍒囨崲 `tabbar`锛宍tabbar` 椤甸潰鏃犵紦瀛樸�備娇鐢ㄤ簡绗笁鏂� UI 搴撶殑 `tabbar` 缁勪欢銆�
+ - 浼樺娍锛氬彲浠ラ殢鎰忛厤缃嚜宸辨兂瑕佺殑 svg icon锛屽垏鎹㈠瓧浣撻鑹叉柟渚裤�傚彲浠ュ疄鐜板悇绉嶈姳閲岃儭鍝ㄧ殑鍔ㄦ晥绛夈��
+ - 鍔e娍锛氶娆$偣鍑� `tababr` 浼氶棯鐑侊紝鏃犵紦瀛樸��
+
+
+> 娉ㄦ剰锛氳姳閲岃儭鍝ㄧ殑鏁堟灉闇�瑕佽嚜宸卞疄鐜帮紝鏈ā鐗堜笉鎻愪緵銆�
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/layouts/fg-tabbar/tabbar.ts b/xiaozhi-esp32-server/main/manager-mobile/src/layouts/fg-tabbar/tabbar.ts
new file mode 100644
index 0000000..03be03f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/layouts/fg-tabbar/tabbar.ts
@@ -0,0 +1,11 @@
+/**
+ * tabbar 鐘舵�侊紝澧炲姞 storageSync 淇濊瘉鍒锋柊娴忚鍣ㄦ椂鍦ㄦ纭殑 tabbar 椤甸潰
+ * 浣跨敤reactive绠�鍗曠姸鎬侊紝鑰屼笉鏄� pinia 鍏ㄥ眬鐘舵��
+ */
+export const tabbarStore = reactive({
+ curIdx: uni.getStorageSync('app-tabbar-index') || 0,
+ setCurIdx(idx: number) {
+ this.curIdx = idx
+ uni.setStorageSync('app-tabbar-index', idx)
+ },
+})
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/layouts/fg-tabbar/tabbarList.ts b/xiaozhi-esp32-server/main/manager-mobile/src/layouts/fg-tabbar/tabbarList.ts
new file mode 100644
index 0000000..5273136
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/layouts/fg-tabbar/tabbarList.ts
@@ -0,0 +1,76 @@
+import type { TabBar } from '@uni-helper/vite-plugin-uni-pages'
+
+type FgTabBarItem = TabBar['list'][0] & {
+ icon: string
+ iconType: 'uiLib' | 'unocss' | 'iconfont'
+}
+
+/**
+ * tabbar 閫夋嫨鐨勭瓥鐣ワ紝鏇磋缁嗙殑浠嬬粛瑙� tabbar.md 鏂囦欢
+ * 0: 'NO_TABBAR' `鏃� tabbar`
+ * 1: 'NATIVE_TABBAR' `瀹屽叏鍘熺敓 tabbar`
+ * 2: 'CUSTOM_TABBAR_WITH_CACHE' `鏈夌紦瀛樿嚜瀹氫箟 tabbar`
+ * 3: 'CUSTOM_TABBAR_WITHOUT_CACHE' `鏃犵紦瀛樿嚜瀹氫箟 tabbar`
+ *
+ * 娓╅Θ鎻愮ず锛氭湰鏂囦欢鐨勪换浣曚唬鐮佹洿鏀逛簡涔嬪悗锛岄兘闇�瑕侀噸鏂拌繍琛岋紝鍚﹀垯 pages.json 涓嶄細鏇存柊瀵艰嚧閿欒
+ */
+export const TABBAR_MAP = {
+ NO_TABBAR: 0,
+ NATIVE_TABBAR: 1,
+ CUSTOM_TABBAR_WITH_CACHE: 2,
+ CUSTOM_TABBAR_WITHOUT_CACHE: 3,
+}
+// TODO锛氶�氳繃杩欓噷鍒囨崲浣跨敤tabbar鐨勭瓥鐣�
+export const selectedTabbarStrategy = TABBAR_MAP.NATIVE_TABBAR
+
+// selectedTabbarStrategy==NATIVE_TABBAR(1) 鏃讹紝闇�瑕佸~ iconPath 鍜� selectedIconPath
+// selectedTabbarStrategy==CUSTOM_TABBAR(2,3) 鏃讹紝闇�瑕佸~ icon 鍜� iconType
+// selectedTabbarStrategy==NO_TABBAR(0) 鏃讹紝tabbarList 涓嶇敓鏁�
+export const tabbarList: FgTabBarItem[] = [
+ {
+ iconPath: 'static/tabbar/robot.png',
+ selectedIconPath: 'static/tabbar/robot_activate.png',
+ pagePath: 'pages/index/index',
+ text: '棣栭〉',
+ icon: 'home',
+ // 閫夌敤 UI 妗嗘灦鑷甫鐨� icon 鏃讹紝iconType 涓� uiLib
+ iconType: 'uiLib',
+ },
+ {
+ iconPath: 'static/tabbar/network.png',
+ selectedIconPath: 'static/tabbar/network_activate.png',
+ pagePath: 'pages/device-config/index',
+ text: '閰嶇綉',
+ icon: 'i-carbon-network-3',
+ iconType: 'uiLib',
+ },
+ {
+ iconPath: 'static/tabbar/system.png',
+ selectedIconPath: 'static/tabbar/system_activate.png',
+ pagePath: 'pages/settings/index',
+ text: '绯荤粺',
+ icon: 'i-carbon-settings',
+ iconType: 'uiLib',
+ },
+]
+
+// NATIVE_TABBAR(1) 鍜� CUSTOM_TABBAR_WITH_CACHE(2) 鏃讹紝闇�瑕乼abbar缂撳瓨
+export const cacheTabbarEnable = selectedTabbarStrategy === TABBAR_MAP.NATIVE_TABBAR
+ || selectedTabbarStrategy === TABBAR_MAP.CUSTOM_TABBAR_WITH_CACHE
+
+const _tabbar: TabBar = {
+ // 鍙湁寰俊灏忕▼搴忔敮鎸� custom銆侫pp 鍜� H5 涓嶇敓鏁�
+ custom: selectedTabbarStrategy === TABBAR_MAP.CUSTOM_TABBAR_WITH_CACHE,
+ color: '#e6e6e6',
+ selectedColor: '#667dea',
+ backgroundColor: '#fff',
+ borderStyle: 'black',
+ height: '50px',
+ fontSize: '10px',
+ iconWidth: '24px',
+ spacing: '3px',
+ list: tabbarList as unknown as TabBar['list'],
+}
+
+// 0鍜�1 闇�瑕佹樉绀哄簳閮ㄧ殑tabbar鐨勫悇绉嶉厤缃紝浠ュ埄鐢ㄧ紦瀛�
+export const tabBar = cacheTabbarEnable ? _tabbar : undefined
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/layouts/tabbar.vue b/xiaozhi-esp32-server/main/manager-mobile/src/layouts/tabbar.vue
new file mode 100644
index 0000000..21851db
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/layouts/tabbar.vue
@@ -0,0 +1,19 @@
+<script lang="ts" setup>
+import type { ConfigProviderThemeVars } from 'wot-design-uni/components/wd-config-provider/types'
+import FgTabbar from './fg-tabbar/fg-tabbar.vue'
+
+const themeVars: ConfigProviderThemeVars = {
+ // colorTheme: 'red',
+ // buttonPrimaryBgColor: '#07c160',
+ // buttonPrimaryColor: '#07c160',
+}
+</script>
+
+<template>
+ <wd-config-provider :theme-vars="themeVars">
+ <slot />
+ <FgTabbar />
+ <wd-toast />
+ <wd-message-box />
+ </wd-config-provider>
+</template>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/main.ts b/xiaozhi-esp32-server/main/manager-mobile/src/main.ts
new file mode 100644
index 0000000..c0f723f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/main.ts
@@ -0,0 +1,25 @@
+import { VueQueryPlugin } from '@tanstack/vue-query'
+import { createSSRApp } from 'vue'
+import App from './App.vue'
+// 瀵煎叆鍥介檯鍖栫浉鍏冲姛鑳�
+import { initI18n } from './i18n'
+
+import { routeInterceptor } from './router/interceptor'
+import store from './store'
+
+import '@/style/index.scss'
+import 'virtual:uno.css'
+
+export function createApp() {
+ const app = createSSRApp(App)
+ app.use(store)
+ app.use(routeInterceptor)
+ app.use(VueQueryPlugin)
+
+ // 鍒濆鍖栧浗闄呭寲
+ initI18n()
+
+ return {
+ app,
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/manifest.json b/xiaozhi-esp32-server/main/manager-mobile/src/manifest.json
new file mode 100644
index 0000000..67225dc
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/manifest.json
@@ -0,0 +1,124 @@
+{
+ "name": "灏忔櫤",
+ "appid": "__UNI__36A515E",
+ "description": "",
+ "versionName": "1.0.0",
+ "versionCode": "100",
+ "transformPx": false,
+ "app-plus": {
+ "usingComponents": true,
+ "nvueStyleCompiler": "uni-app",
+ "compilerVersion": 3,
+ "splashscreen": {
+ "alwaysShowBeforeRender": true,
+ "waiting": true,
+ "autoclose": true,
+ "delay": 0
+ },
+ "modules": {},
+ "distribute": {
+ "android": {
+ "permissions": [
+ "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+ "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+ "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+ "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+ "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+ "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+ "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+ "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+ "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+ "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+ "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+ "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+ "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+ "<uses-feature android:name=\"android.hardware.camera\"/>",
+ "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>",
+ "<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
+ "<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>"
+ ],
+ "minSdkVersion": 30,
+ "targetSdkVersion": 30,
+ "abiFilters": [
+ "armeabi-v7a",
+ "arm64-v8a"
+ ]
+ },
+ "ios": {},
+ "sdkConfigs": {},
+ "icons": {
+ "android": {
+ "hdpi": "unpackage/res/icons/72x72.png",
+ "xhdpi": "unpackage/res/icons/96x96.png",
+ "xxhdpi": "unpackage/res/icons/144x144.png",
+ "xxxhdpi": "unpackage/res/icons/192x192.png"
+ },
+ "ios": {
+ "appstore": "unpackage/res/icons/1024x1024.png",
+ "ipad": {
+ "app": "unpackage/res/icons/76x76.png",
+ "app@2x": "unpackage/res/icons/152x152.png",
+ "notification": "unpackage/res/icons/20x20.png",
+ "notification@2x": "unpackage/res/icons/40x40.png",
+ "proapp@2x": "unpackage/res/icons/167x167.png",
+ "settings": "unpackage/res/icons/29x29.png",
+ "settings@2x": "unpackage/res/icons/58x58.png",
+ "spotlight": "unpackage/res/icons/40x40.png",
+ "spotlight@2x": "unpackage/res/icons/80x80.png"
+ },
+ "iphone": {
+ "app@2x": "unpackage/res/icons/120x120.png",
+ "app@3x": "unpackage/res/icons/180x180.png",
+ "notification@2x": "unpackage/res/icons/40x40.png",
+ "notification@3x": "unpackage/res/icons/60x60.png",
+ "settings@2x": "unpackage/res/icons/58x58.png",
+ "settings@3x": "unpackage/res/icons/87x87.png",
+ "spotlight@2x": "unpackage/res/icons/80x80.png",
+ "spotlight@3x": "unpackage/res/icons/120x120.png"
+ }
+ }
+ }
+ },
+ "compatible": {
+ "ignoreVersion": true
+ }
+ },
+ "quickapp": {},
+ "mp-weixin": {
+ "appid": "wxa2abb91f64032a2b",
+ "setting": {
+ "urlCheck": false,
+ "es6": true,
+ "minified": true
+ },
+ "usingComponents": true,
+ "optimization": {
+ "subPackages": true
+ },
+ "permission": {
+ "scope.userLocation": {
+ "desc": "WiFi閰嶇綉鍔熻兘闇�瑕佽幏鍙栦綅缃潈闄�"
+ }
+ },
+ "requiredPrivateInfos": [
+ "getLocation"
+ ]
+ },
+ "mp-alipay": {
+ "usingComponents": true,
+ "styleIsolation": "shared"
+ },
+ "mp-baidu": {
+ "usingComponents": true
+ },
+ "mp-toutiao": {
+ "usingComponents": true
+ },
+ "uniStatistics": {
+ "enable": false
+ },
+ "vueVersion": "3",
+ "h5": {
+ "router": {}
+ }
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages-sub/demo/index.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages-sub/demo/index.vue
new file mode 100644
index 0000000..4231cff
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages-sub/demo/index.vue
@@ -0,0 +1,27 @@
+<route lang="jsonc" type="page">
+{
+ "layout": "default",
+ "style": {
+ "navigationBarTitleText": "鍒嗗寘椤甸潰"
+ }
+}
+</route>
+
+<script lang="ts" setup>
+// code here
+</script>
+
+<template>
+ <view class="text-center">
+ <view class="m-8">
+ http://localhost:9000/#/pages-sub/demo/index
+ </view>
+ <view class="text-green-500">
+ 鍒嗗寘椤甸潰demo
+ </view>
+ </view>
+</template>
+
+<style lang="scss" scoped>
+//
+</style>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages.json b/xiaozhi-esp32-server/main/manager-mobile/src/pages.json
new file mode 100644
index 0000000..9799c40
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages.json
@@ -0,0 +1,221 @@
+{
+ "globalStyle": {
+ "navigationStyle": "default",
+ "navigationBarTitleText": "灏忔櫤",
+ "navigationBarBackgroundColor": "#f8f8f8",
+ "navigationBarTextStyle": "black",
+ "backgroundColor": "#FFFFFF"
+ },
+ "easycom": {
+ "autoscan": true,
+ "custom": {
+ "^fg-(.*)": "@/components/fg-$1/fg-$1.vue",
+ "^wd-(.*)": "wot-design-uni/components/wd-$1/wd-$1.vue",
+ "^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)": "z-paging/components/z-paging$1/z-paging$1.vue"
+ }
+ },
+ "tabBar": {
+ "custom": false,
+ "color": "#e6e6e6",
+ "selectedColor": "#667dea",
+ "backgroundColor": "#fff",
+ "borderStyle": "black",
+ "height": "50px",
+ "fontSize": "10px",
+ "iconWidth": "24px",
+ "spacing": "3px",
+ "list": [
+ {
+ "iconPath": "static/tabbar/robot.png",
+ "selectedIconPath": "static/tabbar/robot_activate.png",
+ "pagePath": "pages/index/index",
+ "text": "棣栭〉",
+ "icon": "home",
+ "iconType": "uiLib"
+ },
+ {
+ "iconPath": "static/tabbar/network.png",
+ "selectedIconPath": "static/tabbar/network_activate.png",
+ "pagePath": "pages/device-config/index",
+ "text": "閰嶇綉",
+ "icon": "i-carbon-network-3",
+ "iconType": "uiLib"
+ },
+ {
+ "iconPath": "static/tabbar/system.png",
+ "selectedIconPath": "static/tabbar/system_activate.png",
+ "pagePath": "pages/settings/index",
+ "text": "绯荤粺",
+ "icon": "i-carbon-settings",
+ "iconType": "uiLib"
+ }
+ ]
+ },
+ "pages": [
+ {
+ "path": "pages/index/index",
+ "type": "home",
+ "layout": "tabbar",
+ "style": {
+ "navigationStyle": "custom",
+ "navigationBarTitleText": "棣栭〉"
+ }
+ },
+ {
+ "path": "pages/agent/edit",
+ "type": "page"
+ },
+ {
+ "path": "pages/agent/index",
+ "type": "page",
+ "layout": "default",
+ "style": {
+ "navigationBarTitleText": "鏅鸿兘浣�",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/agent/provider",
+ "type": "page",
+ "layout": "default",
+ "style": {
+ "navigationBarTitleText": "缂栬緫婧�",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/agent/speedPitch",
+ "type": "page",
+ "layout": "default",
+ "style": {
+ "navigationBarTitleText": "璇煶璁剧疆",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/agent/tools",
+ "type": "page",
+ "layout": "default",
+ "style": {
+ "navigationBarTitleText": "缂栬緫鍔熻兘",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/chat-history/detail",
+ "type": "page",
+ "layout": "default",
+ "style": {
+ "navigationStyle": "custom",
+ "navigationBarTitleText": "鑱婂ぉ璇︽儏"
+ }
+ },
+ {
+ "path": "pages/chat-history/index",
+ "type": "page"
+ },
+ {
+ "path": "pages/device/index",
+ "type": "page"
+ },
+ {
+ "path": "pages/device-config/index",
+ "type": "page",
+ "style": {
+ "navigationBarTitleText": "璁惧閰嶇疆",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/forgot-password/index",
+ "type": "page",
+ "layout": "default",
+ "style": {
+ "navigationStyle": "custom",
+ "navigationBarTitleText": "ForgotPassword"
+ }
+ },
+ {
+ "path": "pages/login/index",
+ "type": "page",
+ "layout": "default",
+ "style": {
+ "navigationStyle": "custom",
+ "navigationBarTitleText": "Login"
+ }
+ },
+ {
+ "path": "pages/login/privacy-policy-en",
+ "type": "page",
+ "layout": "default",
+ "style": {
+ "navigationStyle": "custom",
+ "navigationBarTitleText": "Privacy Policy"
+ }
+ },
+ {
+ "path": "pages/login/privacy-policy-zh",
+ "type": "page",
+ "layout": "default",
+ "style": {
+ "navigationStyle": "custom",
+ "navigationBarTitleText": "闅愮鏀跨瓥"
+ }
+ },
+ {
+ "path": "pages/login/user-agreement-en",
+ "type": "page",
+ "layout": "default",
+ "style": {
+ "navigationStyle": "custom",
+ "navigationBarTitleText": "User Agreement"
+ }
+ },
+ {
+ "path": "pages/login/user-agreement-zh",
+ "type": "page",
+ "layout": "default",
+ "style": {
+ "navigationStyle": "custom",
+ "navigationBarTitleText": "鐢ㄦ埛鍗忚"
+ }
+ },
+ {
+ "path": "pages/register/index",
+ "type": "page",
+ "layout": "default",
+ "style": {
+ "navigationStyle": "custom",
+ "navigationBarTitleText": "娉ㄥ唽"
+ }
+ },
+ {
+ "path": "pages/settings/index",
+ "type": "page",
+ "layout": "default",
+ "style": {
+ "navigationBarTitleText": "璁剧疆",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/voiceprint/index",
+ "type": "page"
+ }
+ ],
+ "subPackages": [
+ {
+ "root": "pages-sub",
+ "pages": [
+ {
+ "path": "demo/index",
+ "type": "page",
+ "layout": "default",
+ "style": {
+ "navigationBarTitleText": "鍒嗗寘椤甸潰"
+ }
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/AgentSnapshotPanel.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/AgentSnapshotPanel.vue
new file mode 100644
index 0000000..c4d095a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/AgentSnapshotPanel.vue
@@ -0,0 +1,1451 @@
+<script lang="ts" setup>
+import type { AgentSnapshot, AgentSnapshotData } from '@/api/agent/types'
+import { computed, ref, watch } from 'vue'
+import { useMessage } from 'wot-design-uni/components/wd-message-box'
+import {
+ deleteAgentSnapshot,
+ getAgentSnapshot,
+ getAgentSnapshots,
+ getCorrectWordFiles,
+ getModelOptions,
+ getPluginFunctions,
+ getTTSVoices,
+ restoreAgentSnapshot,
+} from '@/api/agent/agent'
+import { getCurrentLanguage, t } from '@/i18n'
+import { toast } from '@/utils/toast'
+import {
+ isSensitiveKey,
+ normalizeSnapshotTtsNumber as normalizeDefaultTtsNumber,
+ normalizeSnapshotOrderedList,
+ redactSnapshotDisplayValue,
+ SNAPSHOT_SECRET_REDACTED,
+ stablePrettyStringify,
+ toIntlLocale,
+ willRestorePermanentlyDeleteChatHistory,
+} from './agentSnapshotUtils.mjs'
+
+defineOptions({
+ name: 'AgentSnapshotPanel',
+})
+
+const props = withDefaults(defineProps<Props>(), {
+ visible: false,
+ currentVersionNo: null,
+ hasUnsavedChanges: false,
+ mutationBusy: false,
+})
+
+const emit = defineEmits<{
+ (event: 'update:visible', value: boolean): void
+ (event: 'restored', context: { agentId: string, actionSequence: number }): void
+}>()
+
+interface Props {
+ visible?: boolean
+ agentId: string
+ currentVersionNo?: number | null
+ hasUnsavedChanges?: boolean
+ mutationBusy?: boolean
+}
+
+interface SnapshotRow extends AgentSnapshot {
+ isLatestSnapshot?: boolean
+ previousSnapshotId?: string | null
+ previousVersionNo?: number | null
+}
+
+interface VersionDetail {
+ mode: 'view' | 'restore'
+ single: boolean
+ row: SnapshotRow
+ beforeData: AgentSnapshotData
+ afterData: AgentSnapshotData
+ changedFields: string[]
+ fieldOrder: string[]
+ forceCompare?: boolean
+ beforeVersionNo?: number | null
+ afterVersionNo?: number | null
+ currentStateToken?: string
+}
+
+interface DetailItem {
+ field: string
+ label: string
+ beforeText: string
+ afterText: string
+ single: boolean
+ recordedOnly?: boolean
+ redactedDifference?: boolean
+}
+
+interface RestoreActionContext {
+ actionSequence: number
+ agentId: string
+ snapshotId: string
+ currentStateToken: string
+ detailRequestSequence: number
+ willClearChatHistory: boolean
+}
+
+const message = useMessage()
+const restoring = ref(false)
+
+const panelVisible = computed({
+ get: () => props.visible,
+ set: (value) => {
+ if (!value && restoring.value) {
+ return
+ }
+ emit('update:visible', value)
+ },
+})
+
+const loading = ref(false)
+const detailLoading = ref(false)
+const deletingSnapshotId = ref('')
+const snapshots = ref<SnapshotRow[]>([])
+const page = ref(1)
+const limit = 10
+const total = ref(0)
+const historyAnchorVersionNo = ref<number | null>(null)
+const detailVisible = ref(false)
+const detailPopupVisible = computed({
+ get: () => detailVisible.value,
+ set: (value) => {
+ if (!value && restoring.value) {
+ return
+ }
+ detailVisible.value = value
+ },
+})
+const currentDetail = ref<VersionDetail | null>(null)
+const pluginNameMap = ref<Record<string, string>>({})
+const modelNameMap = ref<Record<string, string>>({})
+const voiceNameMap = ref<Record<string, string>>({})
+const correctWordNameMap = ref<Record<string, string>>({})
+const loadedVoiceModelIds = ref<Record<string, boolean>>({})
+const metadataLoaded = ref(false)
+const correctWordMetadataLoaded = ref(false)
+let detailRequestSequence = 0
+let snapshotRequestSequence = 0
+let restoreActionSequence = 0
+let restorePostActionSequence: number | null = null
+let restorePreviewRequestSequence: number | null = null
+
+const MODEL_TYPES = ['ASR', 'VAD', 'LLM', 'VLLM', 'TTS', 'Memory', 'Intent']
+const MODEL_FIELD_TYPES: Record<string, string> = {
+ asrModelId: 'ASR',
+ vadModelId: 'VAD',
+ llmModelId: 'LLM',
+ slmModelId: 'LLM',
+ vllmModelId: 'VLLM',
+ ttsModelId: 'TTS',
+ memModelId: 'Memory',
+ intentModelId: 'Intent',
+}
+
+const CHAT_HISTORY_CONF_LABEL_KEYS: Record<string, string> = {
+ 0: 'agentSnapshot.chatHistoryConf.none',
+ 1: 'agentSnapshot.chatHistoryConf.text',
+ 2: 'agentSnapshot.chatHistoryConf.textVoice',
+}
+
+const SNAPSHOT_FIELD_ORDER = [
+ 'agentName',
+ 'agentCode',
+ 'asrModelId',
+ 'vadModelId',
+ 'llmModelId',
+ 'slmModelId',
+ 'vllmModelId',
+ 'ttsModelId',
+ 'ttsLanguage',
+ 'ttsVoiceId',
+ 'ttsVolume',
+ 'ttsRate',
+ 'ttsPitch',
+ 'memModelId',
+ 'intentModelId',
+ 'chatHistoryConf',
+ 'systemPrompt',
+ 'summaryMemory',
+ 'langCode',
+ 'language',
+ 'sort',
+ 'functions',
+ 'contextProviders',
+ 'correctWordFileIds',
+ 'tagNames',
+]
+
+const hasMore = computed(() => snapshots.value.length < total.value)
+const detailItems = computed(() => {
+ if (!currentDetail.value) {
+ return []
+ }
+ return buildDetailItems(currentDetail.value)
+})
+const detailTitle = computed(() => {
+ if (!currentDetail.value) {
+ return t('agentSnapshot.detailTitle')
+ }
+ const label = currentDetail.value.mode === 'restore'
+ ? t('agentSnapshot.restorePreviewTitle')
+ : t('agentSnapshot.detailTitle')
+ return formatVersionRangeTitle(label, currentDetail.value.beforeVersionNo, currentDetail.value.afterVersionNo)
+})
+const restoreWillClearChatHistory = computed(() => {
+ if (!currentDetail.value || currentDetail.value.mode !== 'restore') {
+ return false
+ }
+ return willRestorePermanentlyDeleteChatHistory(
+ currentDetail.value.beforeData?.memModelId,
+ currentDetail.value.afterData?.memModelId,
+ )
+})
+const displayedCurrentVersionNo = computed(() => historyAnchorVersionNo.value || props.currentVersionNo)
+const canConfirmRestore = computed(() => {
+ return !restoring.value
+ && !props.mutationBusy
+ && currentDetail.value?.mode === 'restore'
+ && detailItems.value.length > 0
+ && Boolean(currentDetail.value.currentStateToken?.trim())
+})
+
+watch(() => props.visible, (visible) => {
+ if (visible) {
+ openPanel()
+ }
+ else {
+ invalidateRestoreAction()
+ invalidateSnapshotRequest()
+ closeDetail(true)
+ }
+})
+
+watch(detailVisible, (visible) => {
+ if (!visible) {
+ invalidateDetailRequest()
+ }
+}, { flush: 'sync' })
+
+watch(() => props.currentVersionNo, () => {
+ if (props.visible) {
+ openPanel()
+ }
+})
+
+watch(() => props.agentId, (currentAgentId, previousAgentId) => {
+ if (currentAgentId === previousAgentId) {
+ return
+ }
+ invalidateRestoreAction()
+ invalidateSnapshotRequest()
+ closeDetail(true)
+ if (props.visible) {
+ openPanel()
+ }
+}, { flush: 'sync' })
+
+watch(() => props.mutationBusy, (mutationBusy) => {
+ if (!mutationBusy) {
+ return
+ }
+ const restorePostInFlight = restoring.value
+ && restorePostActionSequence === restoreActionSequence
+ if (restorePostInFlight) {
+ return
+ }
+ const pendingRestorePreview = restorePreviewRequestSequence !== null
+ || currentDetail.value?.mode === 'restore'
+ const pendingRestoreConfirmation = restoring.value
+ && restorePostActionSequence !== restoreActionSequence
+ if (!pendingRestorePreview && !pendingRestoreConfirmation) {
+ return
+ }
+ if (pendingRestoreConfirmation) {
+ invalidateRestoreAction()
+ }
+ restorePreviewRequestSequence = null
+ closeDetail(true)
+ toast.warning(t('agentSnapshot.mutationBusy'))
+}, { flush: 'sync' })
+
+async function openPanel() {
+ const requestId = ++snapshotRequestSequence
+ // The first page must be unanchored so that a stale parent page cannot hide
+ // versions created by another client. The response establishes a stable
+ // anchor for all subsequent pages in this panel session.
+ historyAnchorVersionNo.value = null
+ page.value = 1
+ snapshots.value = []
+ await loadSnapshots(true, requestId)
+}
+
+async function loadSnapshots(reset = false, requestId = snapshotRequestSequence) {
+ if (!props.agentId) {
+ snapshots.value = []
+ total.value = 0
+ return
+ }
+
+ try {
+ loading.value = true
+ const nextPage = reset ? 1 : page.value
+ const params: Record<string, number> = {
+ page: nextPage,
+ limit,
+ }
+ if (!reset && historyAnchorVersionNo.value) {
+ params.maxVersionNo = historyAnchorVersionNo.value
+ }
+ const result = await getAgentSnapshots(props.agentId, params)
+ if (!isActiveSnapshotRequest(requestId)) {
+ return
+ }
+ const responseRows = result?.list || []
+ if (reset) {
+ historyAnchorVersionNo.value = responseRows[0]?.versionNo || null
+ }
+ const rows = decorateSnapshotRows(responseRows)
+ const mergedRows = reset ? rows : [...snapshots.value, ...rows]
+ snapshots.value = attachPreviousRows(mergedRows)
+ total.value = result?.total || 0
+ page.value = nextPage + 1
+ }
+ catch (error) {
+ if (!isActiveSnapshotRequest(requestId)) {
+ return
+ }
+ console.error('Failed to load snapshots:', error)
+ toast.error(t('agentSnapshot.fetchFailed'))
+ }
+ finally {
+ if (isActiveSnapshotRequest(requestId)) {
+ loading.value = false
+ }
+ }
+}
+
+function isActiveSnapshotRequest(requestId: number) {
+ return props.visible && requestId === snapshotRequestSequence
+}
+
+function invalidateSnapshotRequest() {
+ snapshotRequestSequence += 1
+ loading.value = false
+}
+
+function decorateSnapshotRows(rows: AgentSnapshot[]): SnapshotRow[] {
+ return rows.map(row => ({
+ ...row,
+ isLatestSnapshot: Boolean(historyAnchorVersionNo.value && row.versionNo === historyAnchorVersionNo.value),
+ }))
+}
+
+function attachPreviousRows(rows: SnapshotRow[]) {
+ return rows.map((row) => {
+ const previousRow = findPreviousSnapshotRow(row, rows)
+ return {
+ ...row,
+ previousSnapshotId: previousRow?.id || null,
+ previousVersionNo: previousRow?.versionNo || null,
+ }
+ })
+}
+
+function findPreviousSnapshotRow(row: SnapshotRow, rows: SnapshotRow[]) {
+ const versionNo = Number(row.versionNo)
+ if (!Number.isFinite(versionNo)) {
+ return null
+ }
+ return rows.find(item => Number(item.versionNo) < versionNo) || null
+}
+
+async function viewSnapshot(row: SnapshotRow) {
+ detailVisible.value = true
+ const requestId = ++detailRequestSequence
+ detailLoading.value = true
+ currentDetail.value = null
+ try {
+ const metadataRequest = ensureMetadata()
+ const detail = await buildSavedVersionDetail(row)
+ if (!isActiveDetailRequest(requestId)) {
+ return
+ }
+ currentDetail.value = detail
+ // Names are enhancement metadata. Render the snapshot immediately with
+ // stable IDs, then update names reactively as metadata arrives.
+ void Promise.allSettled([
+ metadataRequest,
+ ensureDisplayMetadata(detail.beforeData, detail.afterData),
+ ])
+ }
+ catch (error) {
+ if (!isActiveDetailRequest(requestId)) {
+ return
+ }
+ console.error('Failed to load snapshot detail:', error)
+ toast.error(t('agentSnapshot.detailFailed'))
+ detailVisible.value = false
+ }
+ finally {
+ if (isActiveDetailRequest(requestId)) {
+ detailLoading.value = false
+ }
+ }
+}
+
+async function previewRestoreSnapshot(row: SnapshotRow) {
+ if (!canRestoreSnapshot(row)) {
+ return
+ }
+ if (props.mutationBusy) {
+ toast.warning(t('agentSnapshot.mutationBusy'))
+ return
+ }
+ detailVisible.value = true
+ const requestId = ++detailRequestSequence
+ restorePreviewRequestSequence = requestId
+ detailLoading.value = true
+ currentDetail.value = null
+ try {
+ const metadataRequest = ensureMetadata()
+ const targetSnapshot = await getAgentSnapshot(props.agentId, row.id)
+ if (!isActiveDetailRequest(requestId)) {
+ return
+ }
+ if (!isPlainObject(targetSnapshot.currentSnapshotData)) {
+ throw new Error('Snapshot detail is missing the atomic current-state data')
+ }
+ const detail: VersionDetail = {
+ mode: 'restore',
+ single: false,
+ row: {
+ ...row,
+ id: targetSnapshot.id || row.id,
+ },
+ beforeData: targetSnapshot.currentSnapshotData,
+ afterData: targetSnapshot.snapshotData || {},
+ changedFields: [],
+ fieldOrder: targetSnapshot.fieldOrder || [],
+ forceCompare: true,
+ beforeVersionNo: historyAnchorVersionNo.value || props.currentVersionNo,
+ afterVersionNo: targetSnapshot.versionNo,
+ currentStateToken: targetSnapshot.currentStateToken,
+ }
+ currentDetail.value = detail
+ void Promise.allSettled([
+ metadataRequest,
+ ensureDisplayMetadata(detail.beforeData, detail.afterData),
+ ])
+ }
+ catch (error) {
+ if (!isActiveDetailRequest(requestId)) {
+ return
+ }
+ console.error('Failed to load restore preview:', error)
+ toast.error(t('agentSnapshot.detailFailed'))
+ detailVisible.value = false
+ }
+ finally {
+ if (restorePreviewRequestSequence === requestId) {
+ restorePreviewRequestSequence = null
+ }
+ if (isActiveDetailRequest(requestId)) {
+ detailLoading.value = false
+ }
+ }
+}
+
+function isActiveDetailRequest(requestId: number) {
+ return detailVisible.value && requestId === detailRequestSequence
+}
+
+function invalidateDetailRequest() {
+ detailRequestSequence += 1
+ detailLoading.value = false
+ currentDetail.value = null
+}
+
+function closeDetail(force = false) {
+ if (restoring.value && !force) {
+ return
+ }
+ if (detailVisible.value) {
+ detailVisible.value = false
+ return
+ }
+ invalidateDetailRequest()
+}
+
+async function buildSavedVersionDetail(row: SnapshotRow): Promise<VersionDetail> {
+ const selectedSnapshot = await getAgentSnapshot(props.agentId, row.id)
+ const previousSnapshot = await fetchPreviousSnapshot(row)
+ if (previousSnapshot?.snapshotData) {
+ const adjacentVersion = Number(selectedSnapshot.versionNo) - Number(previousSnapshot.versionNo) === 1
+ return {
+ mode: 'view',
+ single: false,
+ row,
+ beforeData: previousSnapshot.snapshotData || {},
+ afterData: selectedSnapshot.snapshotData || {},
+ changedFields: adjacentVersion ? (selectedSnapshot.changedFields || row.changedFields || []) : [],
+ fieldOrder: selectedSnapshot.fieldOrder || previousSnapshot.fieldOrder || row.fieldOrder || [],
+ forceCompare: !adjacentVersion,
+ beforeVersionNo: previousSnapshot.versionNo,
+ afterVersionNo: selectedSnapshot.versionNo,
+ }
+ }
+
+ return {
+ mode: 'view',
+ single: true,
+ row,
+ beforeData: {},
+ afterData: selectedSnapshot.snapshotData || {},
+ changedFields: selectedSnapshot.changedFields || row.changedFields || [],
+ fieldOrder: selectedSnapshot.fieldOrder || row.fieldOrder || [],
+ afterVersionNo: selectedSnapshot.versionNo,
+ }
+}
+
+async function fetchPreviousSnapshot(row: SnapshotRow) {
+ if (row.previousSnapshotId) {
+ return getAgentSnapshot(props.agentId, row.previousSnapshotId)
+ }
+ if (!row.versionNo || row.versionNo <= 1) {
+ return null
+ }
+ const result = await getAgentSnapshots(props.agentId, {
+ page: 1,
+ limit: 1,
+ maxVersionNo: row.versionNo - 1,
+ })
+ const previousRow = result?.list?.[0]
+ if (!previousRow?.id) {
+ return null
+ }
+ return getAgentSnapshot(props.agentId, previousRow.id)
+}
+
+async function confirmRestoreSnapshot() {
+ const detail = currentDetail.value
+ if (restoring.value || !detail || detail.mode !== 'restore' || !detail.row?.id) {
+ return
+ }
+ if (props.mutationBusy) {
+ toast.warning(t('agentSnapshot.mutationBusy'))
+ return
+ }
+ if (!detailItems.value.length) {
+ toast.info(t('agentSnapshot.noRestoreNeeded'))
+ return
+ }
+ const currentStateToken = detail.currentStateToken?.trim()
+ if (!currentStateToken) {
+ console.error('Cannot restore snapshot without a current-state token')
+ toast.error(t('agentSnapshot.restoreFailed'))
+ return
+ }
+ const context: RestoreActionContext = {
+ actionSequence: ++restoreActionSequence,
+ agentId: props.agentId,
+ snapshotId: detail.row.id,
+ currentStateToken,
+ detailRequestSequence,
+ willClearChatHistory: restoreWillClearChatHistory.value,
+ }
+ // Enter the busy state before either confirmation can yield. This prevents
+ // rapid taps from opening duplicate dialogs or issuing duplicate restores.
+ restoring.value = true
+ try {
+ if (props.hasUnsavedChanges) {
+ const confirmed = await requestRestoreConfirmation({
+ title: t('agentSnapshot.unsavedChangesTitle'),
+ msg: t('agentSnapshot.unsavedChangesWarning'),
+ confirmButtonText: t('agentSnapshot.discardAndRestore'),
+ cancelButtonText: t('common.cancel'),
+ })
+ if (!confirmed || !isActiveRestoreAction(context)) {
+ return
+ }
+ }
+
+ // This confirmation must be the last user interaction before the POST.
+ // It is intentionally separate from the unsaved-form confirmation because
+ // clearing chat history is irreversible and outside snapshot recovery.
+ if (context.willClearChatHistory) {
+ const confirmed = await requestRestoreConfirmation({
+ title: t('agentSnapshot.confirmRestore'),
+ msg: t('agentSnapshot.restoreChatHistoryDestructiveWarning'),
+ confirmButtonText: t('agentSnapshot.confirmRestore'),
+ cancelButtonText: t('common.cancel'),
+ })
+ if (!confirmed || !isActiveRestoreAction(context)) {
+ return
+ }
+ }
+
+ if (!isActiveRestoreAction(context)) {
+ return
+ }
+ // Mark the irreversible request boundary only after the final mutation
+ // guard. A later parent busy transition must not pretend this POST was
+ // canceled; its real response still owns the terminal UI state.
+ restorePostActionSequence = context.actionSequence
+ await restoreAgentSnapshot(context.agentId, context.snapshotId, context.currentStateToken)
+ if (!isActiveRestoreAction(context)) {
+ return
+ }
+ toast.success(t('agentSnapshot.restoreSuccess'))
+ closeDetail(true)
+ emit('restored', {
+ agentId: context.agentId,
+ actionSequence: context.actionSequence,
+ })
+ if (props.visible && props.agentId === context.agentId) {
+ await openPanel()
+ }
+ }
+ catch (error) {
+ if (isActiveRestoreAction(context)) {
+ console.error('Failed to restore snapshot:', error)
+ toast.error(t('agentSnapshot.restoreFailed'))
+ }
+ }
+ finally {
+ if (restorePostActionSequence === context.actionSequence) {
+ restorePostActionSequence = null
+ }
+ if (context.actionSequence === restoreActionSequence) {
+ restoring.value = false
+ }
+ }
+}
+
+async function requestRestoreConfirmation(options: Parameters<typeof message.confirm>[0]) {
+ try {
+ await message.confirm(options)
+ return true
+ }
+ catch {
+ return false
+ }
+}
+
+function isActiveRestoreAction(context: RestoreActionContext) {
+ const detail = currentDetail.value
+ return restoring.value
+ && context.actionSequence === restoreActionSequence
+ && (!props.mutationBusy || restorePostActionSequence === context.actionSequence)
+ && context.agentId === props.agentId
+ && props.visible
+ && detailVisible.value
+ && context.detailRequestSequence === detailRequestSequence
+ && detail?.mode === 'restore'
+ && detail.row?.id === context.snapshotId
+ && detail.currentStateToken?.trim() === context.currentStateToken
+}
+
+function invalidateRestoreAction() {
+ restoreActionSequence += 1
+ restoring.value = false
+}
+
+function deleteSnapshot(row: SnapshotRow) {
+ message.confirm({
+ title: t('agentSnapshot.delete'),
+ msg: t('agentSnapshot.deleteConfirm', { version: row.versionNo }),
+ }).then(async () => {
+ deletingSnapshotId.value = row.id
+ try {
+ await deleteAgentSnapshot(props.agentId, row.id)
+ toast.success(t('agentSnapshot.deleteSuccess'))
+ await openPanel()
+ }
+ catch (error) {
+ console.error('Failed to delete snapshot:', error)
+ toast.error(t('agentSnapshot.deleteFailed'))
+ }
+ finally {
+ deletingSnapshotId.value = ''
+ }
+ }).catch(() => {})
+}
+
+function canRestoreSnapshot(row: SnapshotRow) {
+ return !!row?.id && !row.isLatestSnapshot
+}
+
+function canDeleteSnapshot(row: SnapshotRow) {
+ return !!row?.id && !row.isLatestSnapshot
+}
+
+function buildDetailItems(detail: VersionDetail): DetailItem[] {
+ if (detail.single) {
+ return snapshotFieldOrder(detail.fieldOrder, {}, detail.afterData).map((field) => {
+ const afterValue = getFieldValue(detail.afterData, field)
+ return {
+ field,
+ label: fieldLabel(field),
+ beforeText: '',
+ afterText: formatDisplayValue(field, afterValue, detail.afterData),
+ single: true,
+ recordedOnly: false,
+ redactedDifference: false,
+ }
+ })
+ }
+
+ return resolveDiffFields(
+ detail.beforeData,
+ detail.afterData,
+ detail.changedFields,
+ detail.fieldOrder,
+ detail.forceCompare,
+ ).map((field) => {
+ const beforeValue = getFieldValue(detail.beforeData, field)
+ const afterValue = getFieldValue(detail.afterData, field)
+ const beforeText = formatDisplayValue(field, beforeValue, detail.beforeData)
+ const afterText = formatDisplayValue(field, afterValue, detail.afterData)
+ return {
+ field,
+ label: fieldLabel(field),
+ beforeText,
+ afterText,
+ single: false,
+ recordedOnly: !detail.forceCompare && isSameFieldValue(field, beforeValue, afterValue),
+ redactedDifference: !isSameFieldValue(field, beforeValue, afterValue)
+ && beforeText === afterText
+ && beforeText.includes(t('agentSnapshot.secretRedacted')),
+ }
+ })
+}
+
+function resolveDiffFields(
+ beforeData: AgentSnapshotData,
+ afterData: AgentSnapshotData,
+ changedFields: string[] = [],
+ fieldOrder: string[] = [],
+ forceCompare = false,
+) {
+ const orderedFields = snapshotFieldOrder(fieldOrder, beforeData, afterData)
+ const storedFields = Array.isArray(changedFields) ? changedFields : []
+ const directFields = storedFields
+ .filter(field => field && field !== 'initial' && field !== 'restore')
+ .map(canonicalField)
+ const candidates = forceCompare ? orderedFields : directFields
+
+ const uniqueCandidates = Array.from(new Set(candidates))
+ if (!forceCompare) {
+ // changedFields is an immutable fact about the saved event. Re-running
+ // today's comparison rules would make old events disappear from detail.
+ return uniqueCandidates
+ }
+ return uniqueCandidates.filter((field) => {
+ return !isSameFieldValue(
+ field,
+ getFieldValue(beforeData, field),
+ getFieldValue(afterData, field),
+ )
+ })
+}
+
+function snapshotFieldOrder(fieldOrder: string[] = [], beforeData: AgentSnapshotData = {}, afterData: AgentSnapshotData = {}) {
+ const fields = [
+ ...fieldOrder,
+ ...SNAPSHOT_FIELD_ORDER,
+ ].map(canonicalField)
+ return Array.from(new Set(fields)).filter((field) => {
+ const beforeValue = getFieldValue(beforeData, field)
+ const afterValue = getFieldValue(afterData, field)
+ return beforeValue !== undefined || afterValue !== undefined
+ })
+}
+
+function canonicalField(field: string) {
+ return field === 'tags' || field === 'tagIds' ? 'tagNames' : field
+}
+
+function getFieldValue(data: AgentSnapshotData, field: string) {
+ const canonicalName = canonicalField(field)
+ if (canonicalName === 'tagNames') {
+ return data?.tagNames ?? data?.tags?.map(tag => tag.tagName).filter(Boolean)
+ }
+ return data?.[canonicalName]
+}
+
+function formatDisplayValue(field: string, value: any, rowData: AgentSnapshotData = {}) {
+ if (value === SNAPSHOT_SECRET_REDACTED) {
+ return t('agentSnapshot.secretRedacted')
+ }
+ if (value === null || value === undefined || value === '') {
+ return t('agentSnapshot.emptyValue')
+ }
+ if (MODEL_FIELD_TYPES[field]) {
+ return modelNameMap.value[String(value)] || translatedFallback(`agentSnapshot.model.${value}`, String(value))
+ }
+ if (field === 'ttsVoiceId') {
+ return voiceNameMap.value[String(value)] || translatedFallback(`agentSnapshot.voice.${value}`, String(value))
+ }
+ if (field === 'chatHistoryConf') {
+ return t(CHAT_HISTORY_CONF_LABEL_KEYS[String(value)] || 'agentSnapshot.emptyValue')
+ }
+ if (field === 'functions') {
+ return formatFunctions(value)
+ }
+ if (field === 'contextProviders') {
+ return formatContextProviders(value)
+ }
+ if (field === 'correctWordFileIds') {
+ return formatCorrectWordFileIds(value)
+ }
+ if (field === 'tagNames') {
+ return formatStringList(value)
+ }
+ if (Array.isArray(value)) {
+ return formatArrayValue(value)
+ }
+ if (typeof value === 'object') {
+ return stablePrettyStringify(redactDisplayValue(value))
+ }
+ if (field === 'ttsLanguage' && !value && rowData.ttsVoiceId) {
+ return t('agentSnapshot.emptyValue')
+ }
+ return String(value)
+}
+
+function formatFunctions(value: any) {
+ if (!Array.isArray(value) || value.length === 0) {
+ return t('agentSnapshot.emptyValue')
+ }
+ return value.map((item) => {
+ const pluginId = item?.pluginId || item?.id || ''
+ const name = pluginNameMap.value[pluginId] || translatedFallback(`agentSnapshot.plugin.${pluginId}`, pluginId || t('agentSnapshot.emptyValue'))
+ const params = parseObjectValue(item?.paramInfo ?? item?.params)
+ const safeParams = redactDisplayValue(params)
+ return Object.keys(safeParams).length
+ ? `${name}\n${stablePrettyStringify(safeParams)}`
+ : name
+ }).join('\n')
+}
+
+function formatContextProviders(value: any) {
+ if (!Array.isArray(value) || value.length === 0) {
+ return t('agentSnapshot.emptyValue')
+ }
+ return value.map((item, index) => {
+ return `${index + 1}. ${stablePrettyStringify(redactDisplayValue(item))}`
+ }).join('\n')
+}
+
+function formatCorrectWordFileIds(value: any) {
+ if (!Array.isArray(value) || value.length === 0) {
+ return t('agentSnapshot.emptyValue')
+ }
+ return formatLocalizedList(value.map(id => correctWordNameMap.value[id] || id))
+}
+
+function formatStringList(value: any) {
+ if (!Array.isArray(value) || value.length === 0) {
+ return t('agentSnapshot.emptyValue')
+ }
+ return formatLocalizedList(value)
+}
+
+function formatArrayValue(value: any[]) {
+ if (!value.length) {
+ return t('agentSnapshot.emptyValue')
+ }
+ if (value.every(item => item === null || ['string', 'number', 'boolean'].includes(typeof item))) {
+ return formatLocalizedList(value)
+ }
+ return stablePrettyStringify(redactDisplayValue(value))
+}
+
+async function ensureMetadata() {
+ if (metadataLoaded.value) {
+ return
+ }
+ const modelResults = await Promise.allSettled(
+ MODEL_TYPES.map(async (modelType) => {
+ const options = await getModelOptions(modelType)
+ options.forEach((option) => {
+ modelNameMap.value[option.id] = option.modelName
+ })
+ }),
+ )
+ modelResults.forEach((result) => {
+ if (result.status === 'rejected') {
+ console.warn('Failed to load model metadata:', result.reason)
+ }
+ })
+
+ try {
+ const plugins = await getPluginFunctions()
+ plugins.forEach((plugin) => {
+ pluginNameMap.value[plugin.id] = plugin.name
+ })
+ }
+ catch (error) {
+ console.warn('Failed to load plugin metadata:', error)
+ }
+ metadataLoaded.value = true
+}
+
+async function ensureVoiceMetadata(...snapshots: AgentSnapshotData[]) {
+ const ttsModelIds = snapshots
+ .map(snapshot => snapshot?.ttsModelId)
+ .filter((modelId): modelId is string => Boolean(modelId && !loadedVoiceModelIds.value[modelId]))
+ const uniqueModelIds = Array.from(new Set(ttsModelIds))
+ await Promise.allSettled(uniqueModelIds.map(async (modelId) => {
+ const voices = await getTTSVoices(modelId)
+ voices.forEach((voice) => {
+ voiceNameMap.value[voice.id] = voice.name
+ })
+ loadedVoiceModelIds.value[modelId] = true
+ }))
+}
+
+async function ensureDisplayMetadata(...snapshots: AgentSnapshotData[]) {
+ await Promise.all([
+ ensureVoiceMetadata(...snapshots),
+ ensureCorrectWordMetadata(...snapshots),
+ ])
+}
+
+async function ensureCorrectWordMetadata(...snapshots: AgentSnapshotData[]) {
+ const hasCorrectWordIds = snapshots.some((snapshot) => {
+ return Array.isArray(snapshot?.correctWordFileIds) && snapshot.correctWordFileIds.length > 0
+ })
+ if (!hasCorrectWordIds || correctWordMetadataLoaded.value) {
+ return
+ }
+ try {
+ const files = await getCorrectWordFiles()
+ files.forEach((file) => {
+ if (file.id) {
+ correctWordNameMap.value[file.id] = file.fileName || file.id
+ }
+ })
+ correctWordMetadataLoaded.value = true
+ }
+ catch (error) {
+ console.warn('Failed to load correct word metadata:', error)
+ }
+}
+
+function sourceLabel(source?: string) {
+ return translatedFallback(`agentSnapshot.source.${source || 'config'}`, source || '')
+}
+
+function fieldLabel(field: string) {
+ return translatedFallback(`agentSnapshot.field.${field}`, field)
+}
+
+function translatedFallback(key: string, fallback: string) {
+ const value = t(key)
+ return value === key ? fallback : value
+}
+
+function formatVersion(versionNo?: number | null) {
+ return versionNo ? `#${versionNo}` : ''
+}
+
+function formatVersionRangeTitle(label: string, beforeVersionNo?: number | null, afterVersionNo?: number | null) {
+ if (beforeVersionNo && afterVersionNo) {
+ return `${label} #${beforeVersionNo} -> #${afterVersionNo}`
+ }
+ if (beforeVersionNo) {
+ return `${label} #${beforeVersionNo}`
+ }
+ if (afterVersionNo) {
+ return `${label} #${afterVersionNo}`
+ }
+ return label
+}
+
+function formatTime(time?: string) {
+ if (!time) {
+ return '-'
+ }
+ const date = new Date(String(time).replace(' ', 'T'))
+ if (Number.isNaN(date.getTime())) {
+ return String(time)
+ }
+ try {
+ return new Intl.DateTimeFormat(toIntlLocale(getCurrentLanguage()), {
+ year: 'numeric',
+ month: '2-digit',
+ day: '2-digit',
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit',
+ hour12: false,
+ }).format(date)
+ }
+ catch {
+ const pad = (value: number) => String(value).padStart(2, '0')
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
+ }
+}
+
+function formatLocalizedList(values: any[]) {
+ const items = values.map(value => String(value))
+ try {
+ return new Intl.ListFormat(toIntlLocale(getCurrentLanguage()), {
+ style: 'short',
+ type: 'conjunction',
+ }).format(items)
+ }
+ catch {
+ return items.join(getCurrentLanguage().startsWith('zh_') ? '銆�' : ', ')
+ }
+}
+
+function isSameFieldValue(
+ field: string,
+ left: any,
+ right: any,
+) {
+ return isEquivalentValue(
+ normalizeValueForField(field, left),
+ normalizeValueForField(field, right),
+ )
+}
+
+function normalizeValueForField(field: string, value: any): any {
+ if (field === 'ttsVolume' || field === 'ttsRate' || field === 'ttsPitch') {
+ return normalizeDefaultTtsNumber(value)
+ }
+ if (field === 'summaryMemory') {
+ return value === undefined || value === null || String(value).trim() === '' ? '' : String(value)
+ }
+ if (field === 'correctWordFileIds' || field === 'tagNames') {
+ return normalizeStringList(value)
+ }
+ if (field === 'functions') {
+ return normalizeFunctions(value)
+ }
+ if (field === 'contextProviders') {
+ return normalizeSnapshotOrderedList(value)
+ }
+ return normalizeValue(value)
+}
+
+function normalizeStringList(value: any) {
+ if (!Array.isArray(value)) {
+ return []
+ }
+ return value
+ .filter(item => item !== undefined && item !== null && String(item).trim() !== '')
+ .map(item => String(item))
+ .sort()
+}
+
+function normalizeFunctions(value: any) {
+ if (!Array.isArray(value)) {
+ return {}
+ }
+ const functions = value.reduce<Record<string, any>>((result, item) => {
+ const pluginId = item?.pluginId
+ if (pluginId) {
+ result[String(pluginId)] = normalizeValue(parseObjectValue(item?.paramInfo ?? item?.params))
+ }
+ return result
+ }, {})
+ return normalizeValue(functions)
+}
+
+function parseObjectValue(value: any) {
+ if (!value) {
+ return {}
+ }
+ if (typeof value !== 'string') {
+ return value
+ }
+ try {
+ const parsed = JSON.parse(value)
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {}
+ }
+ catch {
+ return {}
+ }
+}
+
+function isEquivalentValue(left: any, right: any): boolean {
+ if (left === SNAPSHOT_SECRET_REDACTED || right === SNAPSHOT_SECRET_REDACTED) {
+ return true
+ }
+ if (Array.isArray(left) || Array.isArray(right)) {
+ if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) {
+ return false
+ }
+ return left.every((item, index) => isEquivalentValue(item, right[index]))
+ }
+ if (isPlainObject(left) || isPlainObject(right)) {
+ if (!isPlainObject(left) || !isPlainObject(right)) {
+ return false
+ }
+ const keys = Array.from(new Set([...Object.keys(left), ...Object.keys(right)]))
+ return keys.every(key => isEquivalentValue(left[key], right[key]))
+ }
+ return left === right
+}
+
+function normalizeValue(value: any): any {
+ if (Array.isArray(value)) {
+ return value.map(normalizeValue)
+ }
+ if (isPlainObject(value)) {
+ return Object.keys(value).sort().reduce<Record<string, any>>((result, key) => {
+ result[key] = isSensitiveKey(key) ? SNAPSHOT_SECRET_REDACTED : normalizeValue(value[key])
+ return result
+ }, {})
+ }
+ return value === undefined ? null : value
+}
+
+function isPlainObject(value: any): value is Record<string, any> {
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
+}
+
+function redactDisplayValue(value: any, parentKey = ''): any {
+ return redactSnapshotDisplayValue(value, t('agentSnapshot.secretRedacted'), parentKey)
+}
+</script>
+
+<template>
+ <wd-popup
+ v-model="panelVisible"
+ position="bottom"
+ custom-class="agent-snapshot-popup"
+ custom-style="height: 86vh; max-height: 900px;"
+ :close-on-click-modal="!restoring"
+ safe-area-inset-bottom
+ >
+ <view class="agent-snapshot-shell">
+ <view class="agent-snapshot-header">
+ <view class="agent-snapshot-title-block">
+ <text class="block text-[34rpx] text-[#232338] font-bold">
+ {{ t('agentSnapshot.title') }}
+ </text>
+ <text v-if="displayedCurrentVersionNo" class="mt-[6rpx] block text-[24rpx] text-[#65686f]">
+ {{ t('agentSnapshot.currentVersion') }} {{ formatVersion(displayedCurrentVersionNo) }}
+ </text>
+ </view>
+ <wd-icon name="close" size="22px" custom-class="agent-snapshot-close-icon text-[#65686f]" @click="panelVisible = false" />
+ </view>
+
+ <scroll-view scroll-y class="agent-snapshot-body">
+ <view v-if="loading && !snapshots.length" class="py-[80rpx] text-center text-[26rpx] text-[#65686f]">
+ {{ t('common.loading') }}
+ </view>
+
+ <view v-else-if="!snapshots.length" class="py-[120rpx] text-center">
+ <text class="block text-[30rpx] text-[#232338] font-semibold">
+ {{ t('agentSnapshot.empty') }}
+ </text>
+ <text class="mt-[10rpx] block text-[24rpx] text-[#8b8f9a]">
+ {{ t('agentSnapshot.emptyTip') }}
+ </text>
+ </view>
+
+ <view v-else class="flex flex-col gap-[20rpx]">
+ <view
+ v-for="snapshot in snapshots"
+ :key="snapshot.id"
+ class="border border-[#e8eaf0] rounded-[20rpx] bg-white p-[24rpx]"
+ >
+ <view class="flex items-start justify-between gap-[20rpx]">
+ <view>
+ <view class="flex items-center gap-[12rpx]">
+ <text class="text-[32rpx] text-[#232338] font-bold">
+ {{ formatVersion(snapshot.versionNo) }}
+ </text>
+ <text
+ v-if="snapshot.isLatestSnapshot"
+ class="rounded-full bg-[rgba(51,108,255,0.1)] px-[16rpx] py-[4rpx] text-[22rpx] text-[#336cff]"
+ >
+ {{ t('agentSnapshot.currentVersion') }}
+ </text>
+ </view>
+ <text class="mt-[8rpx] block text-[24rpx] text-[#8b8f9a]">
+ {{ formatTime(snapshot.createdAt) }} 路 {{ sourceLabel(snapshot.source) }}
+ </text>
+ </view>
+ </view>
+
+ <view class="mt-[18rpx] flex flex-wrap gap-[10rpx]">
+ <text
+ v-for="field in snapshot.changedFields || []"
+ :key="field"
+ class="rounded-full bg-[#f5f7fb] px-[16rpx] py-[6rpx] text-[22rpx] text-[#4d5566]"
+ >
+ {{ fieldLabel(field) }}
+ </text>
+ <text v-if="!(snapshot.changedFields || []).length" class="text-[24rpx] text-[#9d9ea3]">
+ {{ t('agentSnapshot.noChangedContent') }}
+ </text>
+ </view>
+
+ <view class="agent-snapshot-actions">
+ <wd-button size="small" type="info" custom-class="agent-snapshot-action-button flex-1 !h-[64rpx]" @click="viewSnapshot(snapshot)">
+ {{ t('agentSnapshot.view') }}
+ </wd-button>
+ <wd-button
+ v-if="canRestoreSnapshot(snapshot)"
+ size="small"
+ type="primary"
+ :disabled="mutationBusy"
+ custom-class="agent-snapshot-action-button flex-1 !h-[64rpx] !bg-[#336cff]"
+ @click="previewRestoreSnapshot(snapshot)"
+ >
+ {{ t('agentSnapshot.restore') }}
+ </wd-button>
+ <wd-button
+ v-if="canDeleteSnapshot(snapshot)"
+ size="small"
+ type="error"
+ :loading="deletingSnapshotId === snapshot.id"
+ custom-class="agent-snapshot-action-button flex-1 !h-[64rpx]"
+ @click="deleteSnapshot(snapshot)"
+ >
+ {{ t('agentSnapshot.delete') }}
+ </wd-button>
+ </view>
+ </view>
+
+ <wd-button
+ v-if="hasMore"
+ type="info"
+ plain
+ :loading="loading"
+ custom-class="mt-[8rpx] !h-[72rpx]"
+ @click="loadSnapshots(false)"
+ >
+ {{ t('agentSnapshot.loadMore') }}
+ </wd-button>
+ </view>
+ </scroll-view>
+ </view>
+ </wd-popup>
+
+ <wd-popup
+ v-model="detailPopupVisible"
+ position="bottom"
+ custom-class="agent-snapshot-popup"
+ custom-style="height: 88vh; max-height: 940px;"
+ :close-on-click-modal="!restoring"
+ safe-area-inset-bottom
+ >
+ <view class="agent-snapshot-shell">
+ <view class="agent-snapshot-header">
+ <text class="agent-snapshot-detail-title">
+ {{ detailTitle }}
+ </text>
+ <wd-icon name="close" size="22px" custom-class="agent-snapshot-close-icon text-[#65686f]" @click="closeDetail()" />
+ </view>
+
+ <scroll-view scroll-y class="agent-snapshot-body">
+ <view v-if="detailLoading" class="py-[80rpx] text-center text-[26rpx] text-[#65686f]">
+ {{ t('common.loading') }}
+ </view>
+
+ <view v-else-if="!detailItems.length" class="py-[120rpx] text-center text-[26rpx] text-[#8b8f9a]">
+ {{ t('agentSnapshot.noChangedContent') }}
+ </view>
+
+ <view v-else class="flex flex-col gap-[18rpx]">
+ <view
+ v-for="item in detailItems"
+ :key="item.field"
+ class="border border-[#e8eaf0] rounded-[20rpx] bg-white"
+ >
+ <view class="border-b border-[#f0f1f5] px-[22rpx] py-[18rpx]">
+ <text class="text-[28rpx] text-[#232338] font-semibold">
+ {{ item.label }}
+ </text>
+ <text v-if="item.recordedOnly" class="ml-[10rpx] text-[22rpx] text-[#8b8f9a]">
+ {{ t('agentSnapshot.recordedChange') }}
+ </text>
+ <text v-if="item.redactedDifference" class="ml-[10rpx] text-[22rpx] text-[#a06a1f]">
+ {{ t('agentSnapshot.redactedValueChanged') }}
+ </text>
+ </view>
+
+ <view v-if="item.single" class="p-[22rpx]">
+ <text class="mb-[10rpx] block text-[23rpx] text-[#8b8f9a]">
+ {{ t('agentSnapshot.configValue') }}
+ </text>
+ <text class="block whitespace-pre-wrap break-all rounded-[14rpx] bg-[#f5f9f4] p-[18rpx] text-[25rpx] text-[#27653a] leading-[1.6]">
+ {{ item.afterText }}
+ </text>
+ </view>
+
+ <view v-else class="grid grid-cols-1 gap-[14rpx] p-[22rpx]">
+ <view>
+ <text class="mb-[10rpx] block text-[23rpx] text-[#8b8f9a]">
+ {{ currentDetail?.mode === 'restore' ? t('agentSnapshot.beforeRestore') : t('agentSnapshot.beforeChange') }}
+ </text>
+ <text class="block whitespace-pre-wrap break-all rounded-[14rpx] bg-[#fff7f7] p-[18rpx] text-[25rpx] text-[#7a3b3b] leading-[1.6]">
+ {{ item.beforeText }}
+ </text>
+ </view>
+ <view>
+ <text class="mb-[10rpx] block text-[23rpx] text-[#8b8f9a]">
+ {{ currentDetail?.mode === 'restore' ? t('agentSnapshot.afterRestore') : t('agentSnapshot.afterChange') }}
+ </text>
+ <text class="block whitespace-pre-wrap break-all rounded-[14rpx] bg-[#f5f9f4] p-[18rpx] text-[25rpx] text-[#27653a] leading-[1.6]">
+ {{ item.afterText }}
+ </text>
+ </view>
+ </view>
+ </view>
+
+ <view
+ v-if="currentDetail?.mode === 'restore'"
+ class="rounded-[18rpx] bg-[rgba(51,108,255,0.08)] p-[22rpx] text-[24rpx] text-[#3557a8] leading-[1.6]"
+ >
+ {{ t('agentSnapshot.restoreConfirm', { version: currentDetail.afterVersionNo || '' }) }}
+ </view>
+ <view
+ v-if="currentDetail?.mode === 'restore' && hasUnsavedChanges"
+ class="rounded-[18rpx] bg-[rgba(245,108,108,0.1)] p-[22rpx] text-[24rpx] text-[#a34848] leading-[1.6]"
+ >
+ {{ t('agentSnapshot.unsavedChangesWarning') }}
+ </view>
+ <view
+ v-if="restoreWillClearChatHistory"
+ class="rounded-[18rpx] bg-[rgba(245,108,108,0.1)] p-[22rpx] text-[24rpx] text-[#a34848] leading-[1.6]"
+ >
+ {{ t('agentSnapshot.restoreChatHistoryDestructiveWarning') }}
+ </view>
+ </view>
+ </scroll-view>
+
+ <view
+ v-if="currentDetail?.mode === 'restore'"
+ class="agent-snapshot-footer"
+ >
+ <wd-button type="info" :disabled="restoring" custom-class="agent-snapshot-footer-button flex-1 !h-[78rpx]" @click="closeDetail()">
+ {{ t('common.cancel') }}
+ </wd-button>
+ <wd-button
+ type="primary"
+ :loading="restoring"
+ :disabled="restoring || !canConfirmRestore"
+ custom-class="agent-snapshot-footer-button flex-1 !h-[78rpx] !bg-[#336cff]"
+ @click="confirmRestoreSnapshot"
+ >
+ {{ t('agentSnapshot.confirmRestore') }}
+ </wd-button>
+ </view>
+ </view>
+ </wd-popup>
+</template>
+
+<style lang="scss" scoped>
+:deep(.agent-snapshot-popup) {
+ left: 0 !important;
+ right: 0 !important;
+ width: 100vw !important;
+ max-width: none !important;
+ margin: 0 !important;
+ box-sizing: border-box !important;
+ overflow: hidden !important;
+ border-radius: 28rpx 28rpx 0 0 !important;
+ background: #f5f7fb !important;
+}
+
+@media (min-width: 901px) {
+ :deep(.agent-snapshot-popup) {
+ width: calc(100vw - 32px) !important;
+ max-width: 760px !important;
+ margin: 0 auto !important;
+ }
+}
+
+.agent-snapshot-shell {
+ box-sizing: border-box;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ background: #f5f7fb;
+}
+
+.agent-snapshot-header {
+ box-sizing: border-box;
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 32rpx 32rpx 24rpx;
+ background: #fff;
+ border-bottom: 1rpx solid #e8eaf0;
+}
+
+.agent-snapshot-title-block {
+ min-width: 0;
+}
+
+.agent-snapshot-detail-title {
+ min-width: 0;
+ flex: 1;
+ padding-right: 20rpx;
+ color: #232338;
+ font-size: 32rpx;
+ font-weight: 700;
+ line-height: 1.35;
+ word-break: break-all;
+}
+
+.agent-snapshot-body {
+ box-sizing: border-box;
+ min-height: 0;
+ flex: 1;
+ padding: 22rpx 24rpx 28rpx;
+}
+
+.agent-snapshot-actions {
+ display: flex;
+ gap: 14rpx;
+ margin-top: 22rpx;
+}
+
+.agent-snapshot-footer {
+ box-sizing: border-box;
+ flex-shrink: 0;
+ display: flex;
+ gap: 16rpx;
+ padding: 24rpx;
+ background: #fff;
+ border-top: 1rpx solid #e8eaf0;
+}
+
+:deep(.agent-snapshot-close-icon) {
+ flex-shrink: 0;
+}
+
+:deep(.agent-snapshot-action-button),
+:deep(.agent-snapshot-footer-button) {
+ min-width: 0 !important;
+}
+
+:deep(.agent-snapshot-action-button .wd-button__content),
+:deep(.agent-snapshot-footer-button .wd-button__content) {
+ min-width: 0;
+}
+
+:deep(.agent-snapshot-action-button .wd-button__text),
+:deep(.agent-snapshot-footer-button .wd-button__text) {
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+</style>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/agentSnapshotContracts.test.mjs b/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/agentSnapshotContracts.test.mjs
new file mode 100644
index 0000000..a217a99
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/agentSnapshotContracts.test.mjs
@@ -0,0 +1,103 @@
+/* eslint-disable test/no-import-node-test -- zero-dependency source contract gate */
+import assert from 'node:assert/strict'
+import { readFile } from 'node:fs/promises'
+import test from 'node:test'
+
+const panelSource = await readFile(new URL('./AgentSnapshotPanel.vue', import.meta.url), 'utf8')
+const editSource = await readFile(new URL('../edit.vue', import.meta.url), 'utf8')
+
+function sourceBetween(source, startMarker, endMarker) {
+ const start = source.indexOf(startMarker)
+ const end = source.indexOf(endMarker, start + startMarker.length)
+ assert.notEqual(start, -1, `missing start marker: ${startMarker}`)
+ assert.notEqual(end, -1, `missing end marker: ${endMarker}`)
+ return source.slice(start, end)
+}
+
+test('restore enters busy state before confirmations and requires a final destructive confirmation', () => {
+ const restore = sourceBetween(
+ panelSource,
+ 'async function confirmRestoreSnapshot()',
+ 'async function requestRestoreConfirmation',
+ )
+ const busyIndex = restore.indexOf('restoring.value = true')
+ const unsavedConfirmIndex = restore.indexOf('title: t(\'agentSnapshot.unsavedChangesTitle\')')
+ const destructiveConfirmIndex = restore.indexOf('msg: t(\'agentSnapshot.restoreChatHistoryDestructiveWarning\')')
+ const postIndex = restore.indexOf('await restoreAgentSnapshot(')
+
+ assert.ok(busyIndex >= 0 && busyIndex < unsavedConfirmIndex)
+ assert.ok(destructiveConfirmIndex > unsavedConfirmIndex && destructiveConfirmIndex < postIndex)
+ assert.match(restore, /if \(context\.willClearChatHistory\)/)
+ assert.match(restore.slice(destructiveConfirmIndex, postIndex), /isActiveRestoreAction\(context\)/)
+ const finalMutationGuardIndex = restore.lastIndexOf('if (!isActiveRestoreAction(context))', postIndex)
+ const postBoundaryIndex = restore.indexOf('restorePostActionSequence = context.actionSequence')
+ assert.ok(finalMutationGuardIndex > destructiveConfirmIndex)
+ assert.ok(finalMutationGuardIndex < postBoundaryIndex && postBoundaryIndex < postIndex)
+})
+test('restore guards popup exits and stale action completion', () => {
+ assert.match(panelSource, /if \(!value && restoring\.value\) \{\s*return\s*\}/)
+ assert.equal((panelSource.match(/:close-on-click-modal="!restoring"/g) || []).length, 2)
+ assert.match(panelSource, /function closeDetail\(force = false\) \{\s*if \(restoring\.value && !force\)/)
+ assert.match(panelSource, /context\.agentId === props\.agentId/)
+ assert.match(panelSource, /context\.detailRequestSequence === detailRequestSequence/)
+ assert.match(panelSource, /if \(!isActiveRestoreAction\(context\)\) \{\s*return\s*\}\s*toast\.success/)
+ assert.match(panelSource, /mutationBusy\?: boolean/)
+ assert.match(panelSource, /&& !props\.mutationBusy[\s\S]*currentDetail\.value\?\.mode === 'restore'/)
+ assert.match(panelSource, /&& \(!props\.mutationBusy \|\| restorePostActionSequence === context\.actionSequence\)/)
+ assert.match(panelSource, /if \(restorePostInFlight\) \{\s*return\s*\}/)
+})
+
+test('post-restore detail and tag reload fail closed before save', () => {
+ const save = sourceBetween(editSource, 'async function saveAgent()', 'function loadPluginFunctions()')
+ const reload = sourceBetween(
+ editSource,
+ 'async function reloadAgentAfterSnapshotRestore',
+ 'function isActiveSnapshotReload',
+ )
+
+ assert.ok(save.indexOf('if (snapshotReloadBlocked.value)') < save.indexOf('await updateAgent('))
+ assert.match(reload, /snapshotReloadBlocked\.value = true/)
+ assert.match(reload, /Promise\.all\(\[\s*getAgentDetail\(targetAgentId\),\s*getAgentTags\(targetAgentId\)/)
+ assert.ok(reload.indexOf('const [detail, tags] = await Promise.all') < reload.indexOf('applyPersistedAgentDetail('))
+ assert.match(reload, /snapshotReloadFailed\.value = true/)
+ assert.match(editSource, /:disabled="saving \|\| ttsOptionsLoading \|\| snapshotReloadBlocked"/)
+ assert.match(editSource, /v-if="snapshotReloadFailed"[\s\S]*@click="retrySnapshotReload"/)
+})
+
+test('parent mutations guard history opening and propagate into every restore gate', () => {
+ const opener = sourceBetween(editSource, 'function openSnapshotPanel()', 'function isSameStringList')
+ const restore = sourceBetween(
+ panelSource,
+ 'async function confirmRestoreSnapshot()',
+ 'async function requestRestoreConfirmation',
+ )
+
+ assert.match(opener, /if \(saving\.value \|\| snapshotReloadBlocked\.value\) \{[\s\S]*return/)
+ assert.match(editSource, /:disabled="saving \|\| snapshotReloadBlocked"[\s\S]*@click="openSnapshotPanel"/)
+ assert.match(editSource, /:mutation-busy="saving \|\| snapshotReloadBlocked"/)
+ assert.match(restore, /if \(props\.mutationBusy\) \{[\s\S]*return/)
+ const postIndex = restore.indexOf('await restoreAgentSnapshot(')
+ const finalGate = restore.lastIndexOf('if (!isActiveRestoreAction(context))', postIndex)
+ assert.ok(finalGate >= 0 && finalGate < postIndex)
+})
+
+test('the latest snapshot hides restore and delete actions', () => {
+ const restoreGate = sourceBetween(
+ panelSource,
+ 'function canRestoreSnapshot(row: SnapshotRow)',
+ 'function canDeleteSnapshot(row: SnapshotRow)',
+ )
+ const deleteGate = sourceBetween(
+ panelSource,
+ 'function canDeleteSnapshot(row: SnapshotRow)',
+ 'function buildDetailItems',
+ )
+
+ assert.match(restoreGate, /!!row\?\.id && !row\.isLatestSnapshot/)
+ assert.match(deleteGate, /!!row\?\.id && !row\.isLatestSnapshot/)
+ assert.match(panelSource, /v-if="canRestoreSnapshot\(snapshot\)"/)
+ assert.match(
+ panelSource,
+ /async function previewRestoreSnapshot\(row: SnapshotRow\) \{\s*if \(!canRestoreSnapshot\(row\)\) \{\s*return/,
+ )
+})
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/agentSnapshotUtils.mjs b/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/agentSnapshotUtils.mjs
new file mode 100644
index 0000000..c94a468
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/agentSnapshotUtils.mjs
@@ -0,0 +1,314 @@
+export const SNAPSHOT_SECRET_REDACTED = '__SNAPSHOT_SECRET_REDACTED__'
+
+/** @param {unknown} voices */
+export function hasUsableTtsVoiceMetadata(voices) {
+ return Array.isArray(voices) && voices.length > 0
+}
+
+/**
+ * @param {unknown} currentMemModelId
+ * @param {unknown} targetMemModelId
+ */
+export function willRestorePermanentlyDeleteChatHistory(currentMemModelId, targetMemModelId) {
+ return currentMemModelId !== 'Memory_nomem' && targetMemModelId === 'Memory_nomem'
+}
+
+/**
+ * @param {Array<Record<string, any>>} voices
+ * @param {string} language
+ */
+export function filterTtsVoicesByLanguage(voices, language) {
+ if (!language) {
+ return voices
+ }
+ return voices.filter((voice) => {
+ if (typeof voice.languages !== 'string' || !voice.languages.trim()) {
+ return false
+ }
+ return voice.languages
+ .split(/[銆侊紱;,锛宂/)
+ .map(item => item.trim())
+ .filter(Boolean)
+ .includes(language)
+ })
+}
+
+/** @param {unknown} value */
+export function normalizeSnapshotTtsNumber(value) {
+ if (value === undefined || value === null || String(value).trim() === '') {
+ return null
+ }
+ if (typeof value === 'number') {
+ return Math.trunc(value)
+ }
+ const text = String(value).trim()
+ return /^[+-]?\d+$/.test(text) ? Number.parseInt(text, 10) : text
+}
+
+/**
+ * Normalize object-key order without changing array order. Context providers
+ * are consumed sequentially by the runtime, so their list order is semantic.
+ * @param {unknown} value
+ */
+export function normalizeSnapshotOrderedList(value) {
+ if (!Array.isArray(value)) {
+ return []
+ }
+ return value
+ .filter(item => item !== undefined && item !== null)
+ .map(normalizeDisplayObject)
+}
+
+/**
+ * @param {unknown} value
+ * @param {string} redactedLabel
+ * @param {string} [parentKey]
+ * @returns {unknown}
+ */
+export function redactSnapshotDisplayValue(value, redactedLabel, parentKey = '') {
+ if (value === SNAPSHOT_SECRET_REDACTED || isSensitiveKey(parentKey)) {
+ return redactedLabel
+ }
+ if (Array.isArray(value)) {
+ return value.map(item => redactSnapshotDisplayValue(item, redactedLabel, parentKey))
+ }
+ if (isPlainObject(value)) {
+ const entryNameKey = Object.keys(value).find((key) => {
+ return ['key', 'name'].includes(key.toLowerCase())
+ && typeof value[key] === 'string'
+ && isSensitiveKey(value[key])
+ })
+ const entryValueKey = Object.keys(value).find(key => key.toLowerCase() === 'value')
+ return Object.keys(value).sort().reduce((result, key) => {
+ const semanticKey = resolveUrlSemanticKey(parentKey, key)
+ if (entryNameKey && key === entryValueKey) {
+ result[key] = redactedLabel
+ }
+ else {
+ result[key] = redactSnapshotDisplayValue(value[key], redactedLabel, semanticKey)
+ }
+ return result
+ }, {})
+ }
+ if (typeof value === 'string' && isSnapshotUrlValue(parentKey, value)) {
+ return redactUrl(value, redactedLabel, parentKey)
+ }
+ return value
+}
+
+/** @param {unknown} value */
+export function stablePrettyStringify(value) {
+ return JSON.stringify(normalizeDisplayObject(value), null, 2)
+}
+
+/** @param {string} language */
+export function toIntlLocale(language) {
+ return language.replace('_', '-')
+}
+
+/** @param {string} key */
+export function isSensitiveKey(key) {
+ const normalized = String(key).toLowerCase().replace(/[^a-z0-9]/g, '')
+ return normalized === 'authorization'
+ || normalized.includes('authorization')
+ || normalized.includes('authentication')
+ || normalized === 'auth'
+ || normalized.endsWith('auth')
+ || normalized === 'cookie'
+ || normalized === 'cookie2'
+ || normalized === 'setcookie'
+ || normalized === 'setcookie2'
+ || normalized.endsWith('cookie')
+ || normalized === 'session'
+ || normalized.endsWith('session')
+ || normalized.includes('sessionid')
+ || normalized.includes('sessionkey')
+ || normalized.includes('sessiontoken')
+ || normalized.includes('sessioncookie')
+ || normalized.endsWith('sessid')
+ || normalized === 'token'
+ || normalized.endsWith('token')
+ || normalized.includes('apikey')
+ || normalized.includes('appkey')
+ || normalized.includes('accesskey')
+ || normalized.includes('subscriptionkey')
+ || normalized.includes('privatekey')
+ || normalized.includes('password')
+ || normalized.includes('passwd')
+ || normalized.includes('secret')
+ || normalized.includes('credential')
+}
+
+/**
+ * @param {string} value
+ * @param {string} redactedLabel
+ * @param {string} parentKey
+ */
+function redactUrl(value, redactedLabel, parentKey) {
+ const withoutCredentials = value.replace(
+ /^((?:[a-z][a-z0-9+.-]*:)*\/\/)[^/?#\s]*@/i,
+ (match, prefix) => `${prefix}${redactedLabel}@`,
+ )
+ const schemeMatch = /^((?:[a-z][a-z0-9+.-]*:)*\/\/)/i.exec(withoutCredentials)
+ let pathRedacted = withoutCredentials
+ if (schemeMatch) {
+ const scheme = schemeMatch[1]
+ const remainder = withoutCredentials.slice(scheme.length)
+ const suffixIndex = remainder.search(/[?#]/)
+ const authorityAndPath = suffixIndex < 0 ? remainder : remainder.slice(0, suffixIndex)
+ const suffix = suffixIndex < 0 ? '' : remainder.slice(suffixIndex)
+ const pathIndex = authorityAndPath.indexOf('/')
+ const authority = pathIndex < 0 ? authorityAndPath : authorityAndPath.slice(0, pathIndex)
+ const path = pathIndex < 0 ? '' : authorityAndPath.slice(pathIndex)
+ let host = authority.slice(authority.lastIndexOf('@') + 1)
+ if (host.startsWith('[')) {
+ const closingBracket = host.indexOf(']')
+ host = closingBracket > 0 ? host.slice(1, closingBracket) : host
+ }
+ else {
+ host = host.replace(/:\d+$/, '')
+ }
+ host = host.toLowerCase()
+ pathRedacted = `${scheme}${authority}${redactCapabilityPath(path, host, parentKey, redactedLabel)}${suffix}`
+ }
+ else {
+ const suffixIndex = withoutCredentials.search(/[?#]/)
+ const path = suffixIndex < 0 ? withoutCredentials : withoutCredentials.slice(0, suffixIndex)
+ const suffix = suffixIndex < 0 ? '' : withoutCredentials.slice(suffixIndex)
+ pathRedacted = `${redactCapabilityPath(path, '', parentKey, redactedLabel)}${suffix}`
+ }
+ const queryIndex = pathRedacted.search(/[?#]/)
+ if (queryIndex < 0) {
+ return pathRedacted
+ }
+ return `${pathRedacted.slice(0, queryIndex)}${pathRedacted[queryIndex]}${redactedLabel}`
+}
+
+/**
+ * Capability URLs often carry credentials in path segments rather than in
+ * query parameters. Preserve public provider IDs where their formats are
+ * known, and fail closed after generic hook markers.
+ * @param {string} path
+ * @param {string} host
+ * @param {string} parentKey
+ * @param {string} redactedLabel
+ */
+function redactCapabilityPath(path, host, parentKey, redactedLabel) {
+ if (!path) {
+ return path
+ }
+ const segments = path.split('/')
+ const normalizedSegments = segments.map(segment => segment.toLowerCase())
+
+ if (host === 'hooks.slack.com' || host === 'hooks.slack-gov.com') {
+ const servicesIndex = normalizedSegments.indexOf('services')
+ const capabilityIndex = servicesIndex >= 0 ? servicesIndex + 3 : -1
+ if (capabilityIndex > 0 && capabilityIndex < segments.length) {
+ segments[capabilityIndex] = redactedLabel
+ return segments.join('/')
+ }
+ }
+
+ if (host === 'discord.com'
+ || host.endsWith('.discord.com')
+ || host === 'discordapp.com'
+ || host.endsWith('.discordapp.com')) {
+ const webhooksIndex = normalizedSegments.indexOf('webhooks')
+ const capabilityIndex = webhooksIndex >= 0 ? webhooksIndex + 2 : -1
+ if (capabilityIndex > 0 && capabilityIndex < segments.length) {
+ segments[capabilityIndex] = redactedLabel
+ return segments.join('/')
+ }
+ }
+
+ if (host === 'api.telegram.org') {
+ const botIndex = segments.findIndex(segment => /^bot[^:]+:.+$/i.test(segment))
+ if (botIndex >= 0) {
+ segments[botIndex] = segments[botIndex].replace(/^bot([^:]+):.+$/i, `bot$1:${redactedLabel}`)
+ return segments.join('/')
+ }
+ }
+
+ const markerIndex = normalizedSegments.findIndex(segment => /^(?:webhooks?|hooks?)$/.test(segment))
+ if (markerIndex >= 0 && markerIndex + 1 < segments.length) {
+ return [...segments.slice(0, markerIndex + 1), redactedLabel].join('/')
+ }
+
+ const normalizedKey = String(parentKey).toLowerCase().replace(/[^a-z0-9]/g, '')
+ if (normalizedKey.includes('webhook')) {
+ let lastSegmentIndex = -1
+ for (let index = segments.length - 1; index >= 0; index -= 1) {
+ if (segments[index].length > 0) {
+ lastSegmentIndex = index
+ break
+ }
+ }
+ if (lastSegmentIndex >= 0) {
+ segments[lastSegmentIndex] = redactedLabel
+ return segments.join('/')
+ }
+ }
+
+ return path
+}
+
+/**
+ * Carry webhook capability semantics through wrapper objects such as
+ * `{ deliveryWebhook: { options: { target: "https://.../secret" } } }`.
+ * @param {string} parentKey
+ * @param {string} childKey
+ */
+function resolveUrlSemanticKey(parentKey, childKey) {
+ if (isWebhookSemanticKey(childKey)) {
+ return childKey
+ }
+ if (isWebhookSemanticKey(parentKey)) {
+ return childKey ? `${parentKey}.${childKey}` : parentKey
+ }
+ return childKey
+}
+
+/** @param {string} value */
+function isWebhookSemanticKey(value) {
+ const normalized = String(value || '').toLowerCase().replace(/[^a-z0-9]/g, '')
+ return normalized.includes('webhook')
+ || normalized === 'hook'
+ || normalized === 'hooks'
+ || normalized.endsWith('hook')
+ || normalized.endsWith('hooks')
+}
+
+/**
+ * @param {string} parentKey
+ * @param {string} value
+ */
+function isSnapshotUrlValue(parentKey, value) {
+ const normalizedKey = String(parentKey).toLowerCase().replace(/[^a-z0-9]/g, '')
+ return normalizedKey.includes('url')
+ || normalizedKey.endsWith('uri')
+ || normalizedKey.includes('endpoint')
+ || isWebhookSemanticKey(parentKey)
+ || /^(?:[a-z][a-z0-9+.-]*:)*\/\//i.test(value)
+}
+
+/** @param {unknown} value */
+function normalizeDisplayObject(value) {
+ if (Array.isArray(value)) {
+ return value.map(normalizeDisplayObject)
+ }
+ if (isPlainObject(value)) {
+ return Object.keys(value).sort().reduce((result, key) => {
+ result[key] = normalizeDisplayObject(value[key])
+ return result
+ }, {})
+ }
+ return value
+}
+
+/**
+ * @param {unknown} value
+ * @returns {value is Record<string, unknown>}
+ */
+function isPlainObject(value) {
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/agentSnapshotUtils.test.mjs b/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/agentSnapshotUtils.test.mjs
new file mode 100644
index 0000000..a75e25d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/agentSnapshotUtils.test.mjs
@@ -0,0 +1,183 @@
+/* eslint-disable test/no-import-node-test -- this zero-dependency gate intentionally uses Node's built-in runner */
+import assert from 'node:assert/strict'
+import test from 'node:test'
+import {
+ filterTtsVoicesByLanguage,
+ hasUsableTtsVoiceMetadata,
+ isSensitiveKey,
+ normalizeSnapshotOrderedList,
+ normalizeSnapshotTtsNumber,
+ redactSnapshotDisplayValue,
+ SNAPSHOT_SECRET_REDACTED,
+ stablePrettyStringify,
+ toIntlLocale,
+ willRestorePermanentlyDeleteChatHistory,
+} from './agentSnapshotUtils.mjs'
+
+test('rejects empty TTS metadata while keeping language-less voices selectable without a filter', () => {
+ const voices = [{ id: 'voice-without-language', languages: '' }]
+ assert.equal(hasUsableTtsVoiceMetadata([]), false)
+ assert.equal(hasUsableTtsVoiceMetadata(voices), true)
+ assert.deepEqual(filterTtsVoicesByLanguage(voices, ''), voices)
+ assert.deepEqual(filterTtsVoicesByLanguage(voices, 'zh-CN'), [])
+})
+
+test('keeps inherited TTS values distinct from explicit zero', () => {
+ assert.equal(normalizeSnapshotTtsNumber(null), null)
+ assert.equal(normalizeSnapshotTtsNumber(''), null)
+ assert.equal(normalizeSnapshotTtsNumber(0), 0)
+ assert.equal(normalizeSnapshotTtsNumber('0'), 0)
+})
+
+test('warns only when restoring from a memory mode to no-memory mode', () => {
+ assert.equal(willRestorePermanentlyDeleteChatHistory('Memory_mem_local_short', 'Memory_nomem'), true)
+ assert.equal(willRestorePermanentlyDeleteChatHistory(undefined, 'Memory_nomem'), true)
+ assert.equal(willRestorePermanentlyDeleteChatHistory('Memory_nomem', 'Memory_nomem'), false)
+ assert.equal(willRestorePermanentlyDeleteChatHistory('Memory_mem_local_short', 'Memory_mem0ai'), false)
+})
+
+test('keeps context-provider order while stabilizing object keys', () => {
+ const first = normalizeSnapshotOrderedList([
+ { url: 'https://one.example', headers: { z: 'last', a: 'first' } },
+ { url: 'https://two.example', headers: {} },
+ ])
+ const reversed = normalizeSnapshotOrderedList([
+ { url: 'https://two.example', headers: {} },
+ { headers: { a: 'first', z: 'last' }, url: 'https://one.example' },
+ ])
+
+ assert.deepEqual(first[0], {
+ headers: { a: 'first', z: 'last' },
+ url: 'https://one.example',
+ })
+ assert.notDeepEqual(first, reversed)
+})
+
+test('redacts nested credentials, header entries, URL credentials and query values', () => {
+ const redacted = redactSnapshotDisplayValue({
+ 'apiKey': 'api-secret',
+ 'Authentication': 'authentication-secret',
+ 'X-Auth': 'auth-secret',
+ 'PHPSESSID': 'php-session-secret',
+ 'Session-Key': 'session-key-secret',
+ 'headers': [
+ { key: 'Authorization', value: 'Bearer secret' },
+ { key: 'Proxy-Authorization', value: 'Basic secret' },
+ { key: 'Cookie', value: 'sid=secret' },
+ { key: 'Set-Cookie', value: 'session=secret' },
+ { key: 'Content-Type', value: 'application/json' },
+ ],
+ 'marker': SNAPSHOT_SECRET_REDACTED,
+ 'session_id': 'session-secret',
+ 'url': 'https://user:pass@example.com/context?access_token=secret#fragment',
+ 'endpoint': 'https://example.com/callback?signature=secret',
+ }, '[hidden]')
+
+ assert.deepEqual(redacted, {
+ 'apiKey': '[hidden]',
+ 'Authentication': '[hidden]',
+ 'X-Auth': '[hidden]',
+ 'PHPSESSID': '[hidden]',
+ 'Session-Key': '[hidden]',
+ 'headers': [
+ { key: 'Authorization', value: '[hidden]' },
+ { key: 'Proxy-Authorization', value: '[hidden]' },
+ { key: 'Cookie', value: '[hidden]' },
+ { key: 'Set-Cookie', value: '[hidden]' },
+ { key: 'Content-Type', value: 'application/json' },
+ ],
+ 'marker': '[hidden]',
+ 'session_id': '[hidden]',
+ 'url': 'https://[hidden]@example.com/context?[hidden]',
+ 'endpoint': 'https://example.com/callback?[hidden]',
+ })
+})
+
+test('redacts structured key/name entries without relying on property casing', () => {
+ const entries = [
+ { KEY: 'Authentication', VALUE: 'key secret' },
+ { Name: 'X-Auth', Value: 'name secret' },
+ { key: 'public-label', NAME: 'Authorization', value: 'secret selected from either marker' },
+ { NAME: 'Content-Type', VALUE: 'application/json; charset=utf-8' },
+ ]
+
+ assert.deepEqual(redactSnapshotDisplayValue(entries, '[hidden]'), [
+ { KEY: 'Authentication', VALUE: '[hidden]' },
+ { Name: 'X-Auth', Value: '[hidden]' },
+ { key: 'public-label', NAME: 'Authorization', value: '[hidden]' },
+ { NAME: 'Content-Type', VALUE: 'application/json; charset=utf-8' },
+ ])
+})
+
+test('redacts capability path values without hiding ordinary REST paths', () => {
+ const redacted = redactSnapshotDisplayValue({
+ slack: 'https://hooks.slack.com/services/T111/B222/capability-value',
+ slackGov: 'https://hooks.slack-gov.com/services/T111/B222/gov-capability-value',
+ discord: 'https://discord.com/api/webhooks/123456/capability-value',
+ telegram: 'https://api.telegram.org/bot123456:capability-value/sendMessage',
+ genericHook: 'https://example.com/api/hook/capability/continuation',
+ genericHooks: 'https://example.com/api/hooks/capability/continuation',
+ genericWebhook: 'https://example.com/api/webhook/capability/continuation',
+ genericWebhooks: 'https://example.com/api/webhooks/capability/continuation',
+ relativeUrl: '/api/webhooks/capability/continuation',
+ callbackWebhookUrl: 'https://example.com/incoming/capability-value',
+ nested: {
+ deliveryWebhook: {
+ options: {
+ target: 'https://example.com/incoming/nested-capability-value',
+ relativeTarget: '/incoming/nested-relative-capability-value',
+ },
+ },
+ },
+ protocolRelativeUrl: '//protocol-user:protocol-pass@example.com/context',
+ jdbcUrl: 'jdbc:mysql://jdbc-user:jdbc-pass@example.com/database',
+ ordinary: 'https://example.com/api/v1/agents/42',
+ }, '[hidden]')
+
+ assert.deepEqual(redacted, {
+ slack: 'https://hooks.slack.com/services/T111/B222/[hidden]',
+ slackGov: 'https://hooks.slack-gov.com/services/T111/B222/[hidden]',
+ discord: 'https://discord.com/api/webhooks/123456/[hidden]',
+ telegram: 'https://api.telegram.org/bot123456:[hidden]/sendMessage',
+ genericHook: 'https://example.com/api/hook/[hidden]',
+ genericHooks: 'https://example.com/api/hooks/[hidden]',
+ genericWebhook: 'https://example.com/api/webhook/[hidden]',
+ genericWebhooks: 'https://example.com/api/webhooks/[hidden]',
+ relativeUrl: '/api/webhooks/[hidden]',
+ callbackWebhookUrl: 'https://example.com/incoming/[hidden]',
+ nested: {
+ deliveryWebhook: {
+ options: {
+ target: 'https://example.com/incoming/[hidden]',
+ relativeTarget: '/incoming/[hidden]',
+ },
+ },
+ },
+ protocolRelativeUrl: '//[hidden]@example.com/context',
+ jdbcUrl: 'jdbc:mysql://[hidden]@example.com/database',
+ ordinary: 'https://example.com/api/v1/agents/42',
+ })
+})
+
+test('matches the backend sensitive key variants', () => {
+ for (const key of [
+ 'Authentication',
+ 'X-Auth',
+ 'Proxy-Authorization',
+ 'Cookie',
+ 'Set-Cookie',
+ 'PHPSESSID',
+ 'Session-Key',
+ 'session_token',
+ 'Ocp-Apim-Subscription-Key',
+ ]) {
+ assert.equal(isSensitiveKey(key), true, `${key} should be redacted`)
+ }
+ assert.equal(isSensitiveKey('Content-Type'), false)
+ assert.equal(isSensitiveKey('publicKey'), false)
+})
+
+test('formats objects stably and maps app locale names to Intl locales', () => {
+ assert.equal(stablePrettyStringify({ z: 1, a: { d: 2, c: 1 } }), '{\n "a": {\n "c": 1,\n "d": 2\n },\n "z": 1\n}')
+ assert.equal(toIntlLocale('pt_BR'), 'pt-BR')
+})
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/voicePreviewUtils.mjs b/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/voicePreviewUtils.mjs
new file mode 100644
index 0000000..94f3cdd
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/voicePreviewUtils.mjs
@@ -0,0 +1,42 @@
+/** @param {Record<string, any>} voice */
+export function hasVoicePreview(voice) {
+ return Boolean(voice?.isClone || voice?.voiceDemo || voice?.voice_demo)
+}
+
+export function createVoicePreviewRequestGate() {
+ let sequence = 0
+
+ return {
+ begin() {
+ sequence += 1
+ return sequence
+ },
+ invalidate() {
+ sequence += 1
+ },
+ isCurrent(requestId) {
+ return requestId === sequence
+ },
+ }
+}
+
+/**
+ * @param {{ id: string, isClone?: boolean, voiceDemo?: string | null }} voice
+ * @param {(cloneId: string) => Promise<string>} getCloneAudioId
+ * @param {string} baseUrl
+ */
+export async function resolveVoicePreviewUrl(voice, getCloneAudioId, baseUrl) {
+ if (!voice?.isClone) {
+ return typeof voice?.voiceDemo === 'string' ? voice.voiceDemo : ''
+ }
+ if (!voice.id) {
+ return ''
+ }
+
+ const uuid = await getCloneAudioId(voice.id)
+ if (!uuid) {
+ return ''
+ }
+
+ return `${baseUrl.replace(/\/+$/, '')}/voiceClone/play/${encodeURIComponent(uuid)}`
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/voicePreviewUtils.test.mjs b/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/voicePreviewUtils.test.mjs
new file mode 100644
index 0000000..d0284d5
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/components/voicePreviewUtils.test.mjs
@@ -0,0 +1,50 @@
+/* eslint-disable test/no-import-node-test -- this zero-dependency gate intentionally uses Node's built-in runner */
+import assert from 'node:assert/strict'
+import test from 'node:test'
+import { createVoicePreviewRequestGate, hasVoicePreview, resolveVoicePreviewUrl } from './voicePreviewUtils.mjs'
+
+test('keeps normal voice previews on their direct URL', async () => {
+ let cloneRequestCount = 0
+ const url = await resolveVoicePreviewUrl({
+ id: 'normal-voice',
+ isClone: false,
+ voiceDemo: 'https://cdn.example.test/normal.wav',
+ }, async () => {
+ cloneRequestCount += 1
+ return 'unused'
+ }, 'https://api.example.test')
+
+ assert.equal(url, 'https://cdn.example.test/normal.wav')
+ assert.equal(cloneRequestCount, 0)
+})
+
+test('uses the clone record id to obtain and construct a temporary play URL', async () => {
+ let requestedCloneId = ''
+ const url = await resolveVoicePreviewUrl({
+ id: 'clone-record-id',
+ isClone: true,
+ voiceDemo: 'provider-speaker-id-must-not-be-played',
+ }, async (cloneId) => {
+ requestedCloneId = cloneId
+ return 'temporary uuid'
+ }, 'https://api.example.test/')
+
+ assert.equal(requestedCloneId, 'clone-record-id')
+ assert.equal(url, 'https://api.example.test/voiceClone/play/temporary%20uuid')
+})
+
+test('shows a preview control for cloned voices even without voiceDemo', () => {
+ assert.equal(hasVoicePreview({ id: 'clone-record-id', isClone: true }), true)
+ assert.equal(hasVoicePreview({ id: 'normal-voice', isClone: false, voiceDemo: '' }), false)
+})
+
+test('invalidates an older request when the same voice is cancelled and retried', () => {
+ const gate = createVoicePreviewRequestGate()
+ const firstRequest = gate.begin()
+
+ gate.invalidate()
+ const retryRequest = gate.begin()
+
+ assert.equal(gate.isCurrent(firstRequest), false)
+ assert.equal(gate.isCurrent(retryRequest), true)
+})
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/edit.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/edit.vue
new file mode 100644
index 0000000..8e4f65b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/edit.vue
@@ -0,0 +1,1705 @@
+<script lang="ts" setup>
+import type { AgentDetail, ModelOption, PluginDefinition, RoleTemplate, TtsVoice } from '@/api/agent/types'
+import { computed, nextTick, onMounted, ref, watch } from 'vue'
+import { getAgentDetail, getAgentTags, getAllLanguage, getModelOptions, getPluginFunctions, getRoleTemplates, getVoiceCloneAudioId, updateAgent } from '@/api/agent/agent'
+import { t } from '@/i18n'
+import { usePluginStore, useProvider, useSpeedPitch } from '@/store'
+import { getEnvBaseUrl } from '@/utils'
+import { toast } from '@/utils/toast'
+import AgentSnapshotPanel from './components/AgentSnapshotPanel.vue'
+import { filterTtsVoicesByLanguage, hasUsableTtsVoiceMetadata } from './components/agentSnapshotUtils.mjs'
+import { createVoicePreviewRequestGate, hasVoicePreview, resolveVoicePreviewUrl } from './components/voicePreviewUtils.mjs'
+
+defineOptions({
+ name: 'AgentEdit',
+})
+
+const props = withDefaults(defineProps<Props>(), {
+ agentId: '',
+})
+
+// 缁勪欢鍙傛暟
+interface Props {
+ agentId?: string
+}
+
+const agentId = computed(() => props.agentId)
+
+// 琛ㄥ崟鏁版嵁
+const formData = ref<Partial<AgentDetail>>({
+ agentName: '',
+ systemPrompt: '',
+ summaryMemory: '',
+ vadModelId: '',
+ asrModelId: '',
+ llmModelId: '',
+ slmModelId: '',
+ vllmModelId: '',
+ intentModelId: '',
+ memModelId: '',
+ ttsModelId: '',
+ ttsVoiceId: '',
+ ttsLanguage: '',
+ ttsVolume: 0,
+ ttsRate: 0,
+ ttsPitch: 0,
+})
+
+// 鏄剧ず鍚嶇О鏁版嵁
+const displayNames = ref({
+// 鏄剧ず鍚嶇О鏁版嵁
+ vad: t('agent.pleaseSelect'),
+ asr: t('agent.pleaseSelect'),
+ llm: t('agent.pleaseSelect'),
+ slm: t('agent.pleaseSelect'),
+ vllm: t('agent.pleaseSelect'),
+ intent: t('agent.pleaseSelect'),
+ memory: t('agent.pleaseSelect'),
+ tts: t('agent.pleaseSelect'),
+ voiceprint: t('agent.pleaseSelect'),
+ report: t('agent.pleaseSelect'),
+ language: t('agent.pleaseSelect'),
+})
+
+// 瑙掕壊妯℃澘鏁版嵁
+const roleTemplates = ref<RoleTemplate[]>([])
+const selectedTemplateId = ref('')
+
+// 鍔犺浇鐘舵��
+const loading = ref(false)
+const saving = ref(false)
+const showSnapshotPanel = ref(false)
+const currentVersionNo = ref<number | null>(null)
+const snapshotReloadBlocked = ref(false)
+const snapshotReloadFailed = ref(false)
+
+// 妯″瀷閫夐」鏁版嵁
+const modelOptions = ref<{
+ [key: string]: ModelOption[]
+}>({
+ VAD: [],
+ ASR: [],
+ LLM: [],
+ VLLM: [],
+ Intent: [],
+ Memory: [],
+ TTS: [],
+})
+
+interface VoiceOption {
+ id?: string
+ value: string
+ name: string
+ voiceDemo?: string | null
+ voice_demo?: string | null
+ isClone: boolean
+ train_status?: number
+}
+
+// 闊宠壊閫夐」鏁版嵁
+const voiceOptions = ref<VoiceOption[]>([])
+// 淇濆瓨瀹屾暣鐨勯煶鑹蹭俊鎭�
+const voiceDetails = ref<Record<string, TtsVoice>>({})
+
+// 涓婃姤妯″紡閫夐」鏁版嵁
+const reportOptions = [
+ { name: t('agent.reportText'), value: 1 },
+ { name: t('agent.reportTextVoice'), value: 2 },
+]
+
+// 閫夋嫨鍣ㄦ樉绀虹姸鎬�
+const pickerShow = ref<{
+ [key: string]: boolean
+}>({
+ vad: false,
+ asr: false,
+ llm: false,
+ slm: false,
+ vllm: false,
+ intent: false,
+ memory: false,
+ tts: false,
+ voiceprint: false,
+ language: false,
+ report: false,
+})
+
+const allFunctions = ref<PluginDefinition[]>([])
+const dynamicTags = ref([])
+const inputValue = ref('')
+const inputVisible = ref(false)
+const languageOptions = ref<any[]>([])
+const isVisibleReport = ref(false)
+const tempSummaryMemory = ref('')
+const selectedTtsLanguage = ref('')
+const ttsLanguageTouched = ref(false)
+const ttsVoiceTouched = ref(false)
+const ttsOptionsLoading = ref(false)
+const ttsOptionsModelId = ref('')
+const originalTagNames = ref<string[]>([])
+const originalAgentDetail = ref<AgentDetail | null>(null)
+let ttsOptionsRequestSequence = 0
+let agentDetailRequestSequence = 0
+let agentTagRequestSequence = 0
+let snapshotReloadSequence = 0
+
+interface SnapshotRestoreContext {
+ agentId: string
+ actionSequence: number
+}
+
+// 闊抽鎾斁鐩稿叧
+const audioRef = ref<UniApp.InnerAudioContext | null>(null)
+const playingVoiceId = ref<string>('')
+const voicePreviewRequestGate = createVoicePreviewRequestGate()
+
+// 浣跨敤鎻掍欢store
+const pluginStore = usePluginStore()
+const speedPitchStore = useSpeedPitch()
+const providerStore = useProvider()
+
+const EDITABLE_AGENT_FIELDS: Array<keyof AgentDetail> = [
+ 'agentName',
+ 'systemPrompt',
+ 'summaryMemory',
+ 'vadModelId',
+ 'asrModelId',
+ 'llmModelId',
+ 'slmModelId',
+ 'vllmModelId',
+ 'intentModelId',
+ 'memModelId',
+ 'ttsModelId',
+ 'chatHistoryConf',
+ 'langCode',
+ 'language',
+ 'sort',
+]
+
+const hasUnsavedChanges = computed(() => {
+ if (loading.value || !originalAgentDetail.value) {
+ return false
+ }
+ return stableSerialize(buildCurrentEditableState()) !== stableSerialize(buildOriginalEditableState())
+})
+
+function buildCurrentEditableState() {
+ const original = originalAgentDetail.value as AgentDetail
+ const current = formData.value as Record<string, any>
+ const changedTtsFields = new Set(speedPitchStore.changedFields)
+ return {
+ ...pickEditableFields(current),
+ ttsLanguage: ttsLanguageTouched.value ? selectedTtsLanguage.value : original.ttsLanguage,
+ ttsVoiceId: ttsVoiceTouched.value ? current.ttsVoiceId : original.ttsVoiceId,
+ ttsVolume: changedTtsFields.has('ttsVolume') ? speedPitchStore.speedPitch.ttsVolume : original.ttsVolume,
+ ttsRate: changedTtsFields.has('ttsRate') ? speedPitchStore.speedPitch.ttsRate : original.ttsRate,
+ ttsPitch: changedTtsFields.has('ttsPitch') ? speedPitchStore.speedPitch.ttsPitch : original.ttsPitch,
+ functions: normalizeAgentFunctions(current.functions || []),
+ contextProviders: providerStore.providers,
+ tagNames: dynamicTags.value.map((tag: any) => tag.tagName).filter(Boolean).sort(),
+ }
+}
+
+function buildOriginalEditableState() {
+ const original = originalAgentDetail.value as AgentDetail
+ return {
+ ...pickEditableFields(original as unknown as Record<string, any>),
+ ttsLanguage: original.ttsLanguage,
+ ttsVoiceId: original.ttsVoiceId,
+ ttsVolume: original.ttsVolume,
+ ttsRate: original.ttsRate,
+ ttsPitch: original.ttsPitch,
+ functions: normalizeAgentFunctions(original.functions || []),
+ contextProviders: original.contextProviders || [],
+ tagNames: [...originalTagNames.value].sort(),
+ }
+}
+
+function pickEditableFields(data: Record<string, any>) {
+ return EDITABLE_AGENT_FIELDS.reduce<Record<string, any>>((result, field) => {
+ result[field] = data[field]
+ return result
+ }, {})
+}
+
+function stableSerialize(value: any) {
+ return JSON.stringify(sortObjectKeys(value))
+}
+
+function sortObjectKeys(value: any): any {
+ if (Array.isArray(value)) {
+ return value.map(sortObjectKeys)
+ }
+ if (value && typeof value === 'object') {
+ return Object.keys(value).sort().reduce<Record<string, any>>((result, key) => {
+ result[key] = sortObjectKeys(value[key])
+ return result
+ }, {})
+ }
+ return value
+}
+
+function cloneSerializable<T>(value: T): T {
+ return JSON.parse(JSON.stringify(value)) as T
+}
+
+// tabs
+const tabList = [
+ {
+ label: '瑙掕壊閰嶇疆',
+ value: 'home',
+ icon: '/static/tabbar/robot.png',
+ activeIcon: '/static/tabbar/robot_activate.png',
+ },
+ {
+ label: '璁惧绠$悊',
+ value: 'category',
+ icon: '/static/tabbar/device.png',
+ activeIcon: '/static/tabbar/device_activate.png',
+ },
+ {
+ label: '鑱婂ぉ璁板綍',
+ value: 'settings',
+ icon: '/static/tabbar/chat.png',
+ activeIcon: '/static/tabbar/chat_activate.png',
+ },
+ {
+ label: '澹扮汗绠$悊',
+ value: 'profile',
+ icon: '/static/tabbar/voiceprint.png',
+ activeIcon: '/static/tabbar/voiceprint_activate.png',
+ },
+]
+function handleCloseTag(id: string) {
+ dynamicTags.value = dynamicTags.value.filter(tag => tag.id !== id)
+}
+
+function showInput() {
+ inputVisible.value = true
+}
+
+function handleInputConfirm() {
+ if (inputValue.value) {
+ dynamicTags.value.push({ id: new Date().getTime(), tagName: inputValue.value.trim() })
+ inputValue.value = ''
+ }
+ inputVisible.value = false
+}
+
+// 鏄惁绂佺敤鍘嗗彶璁板繂杈撳叆妗�
+const isMemoryDisabled = computed(() => formData.value.memModelId !== 'Memory_mem_local_short')
+
+function normalizeFunctionParams(paramInfo: any) {
+ if (!paramInfo)
+ return {}
+ if (typeof paramInfo === 'string') {
+ try {
+ const parsed = JSON.parse(paramInfo)
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {}
+ }
+ catch {
+ return {}
+ }
+ }
+ return typeof paramInfo === 'object' && !Array.isArray(paramInfo) ? { ...paramInfo } : {}
+}
+
+function normalizeAgentFunctions(functions: any[] = []) {
+ return functions.map(item => ({
+ ...item,
+ paramInfo: normalizeFunctionParams(item.paramInfo),
+ }))
+}
+
+// 鎵撳紑涓婁笅鏂囨簮缂栬緫寮圭獥
+function openContextProviderDialog() {
+ uni.navigateTo({
+ url: '/pages/agent/provider',
+ })
+}
+
+function handleRegulate() {
+ uni.navigateTo({
+ url: '/pages/agent/speedPitch',
+ })
+}
+
+// 鍔犺浇鏅鸿兘浣撹鎯�
+async function loadAgentDetail(targetAgentId = agentId.value) {
+ if (!targetAgentId)
+ return false
+
+ const requestId = ++agentDetailRequestSequence
+ invalidateTtsMetadataRequest()
+ try {
+ loading.value = true
+ const detail = await getAgentDetail(targetAgentId)
+ if (!isActiveAgentDetailRequest(targetAgentId, requestId)) {
+ return false
+ }
+ applyPersistedAgentDetail(detail, targetAgentId)
+ await enhanceAgentDetailMetadata(detail, targetAgentId, requestId)
+ return isActiveAgentDetailRequest(targetAgentId, requestId)
+ }
+ catch (error) {
+ if (isActiveAgentDetailRequest(targetAgentId, requestId)) {
+ console.error('鍔犺浇鏅鸿兘浣撹鎯呭け璐�:', error)
+ toast.error(t('agent.loadFail'))
+ }
+ return false
+ }
+ finally {
+ if (isActiveAgentDetailRequest(targetAgentId, requestId)) {
+ loading.value = false
+ }
+ }
+}
+
+function applyPersistedAgentDetail(detail: AgentDetail, targetAgentId: string) {
+ const normalizedFunctions = normalizeAgentFunctions(detail.functions || [])
+ tempSummaryMemory.value = ''
+ ttsLanguageTouched.value = false
+ ttsVoiceTouched.value = false
+ ttsOptionsModelId.value = ''
+ voiceOptions.value = []
+ voiceDetails.value = {}
+ languageOptions.value = []
+ formData.value = { ...detail, functions: normalizedFunctions }
+ originalAgentDetail.value = cloneSerializable({ ...detail, functions: normalizedFunctions })
+ currentVersionNo.value = detail.currentVersionNo || null
+ selectedTtsLanguage.value = detail.ttsLanguage || ''
+
+ pluginStore.setCurrentAgentId(targetAgentId)
+ pluginStore.setCurrentFunctions(normalizedFunctions)
+ speedPitchStore.updateSpeedPitch({
+ ttsVolume: detail.ttsVolume ?? 0,
+ ttsRate: detail.ttsRate ?? 0,
+ ttsPitch: detail.ttsPitch ?? 0,
+ })
+ speedPitchStore.resetChangedFields()
+ providerStore.updateProviders(detail.contextProviders || [])
+}
+
+async function enhanceAgentDetailMetadata(detail: AgentDetail, targetAgentId: string, requestId: number) {
+ try {
+ if (detail.ttsModelId) {
+ await fetchAllLanguag(detail.ttsModelId, {
+ preferredLanguage: detail.ttsLanguage,
+ preferredVoiceId: detail.ttsVoiceId,
+ })
+ }
+ else {
+ voiceOptions.value = []
+ voiceDetails.value = {}
+ languageOptions.value = []
+ selectedTtsLanguage.value = ''
+ }
+ await nextTick()
+ if (isActiveAgentDetailRequest(targetAgentId, requestId)) {
+ updateDisplayNames()
+ }
+ }
+ catch (error) {
+ // Persisted agent detail has already loaded successfully. Voice metadata is
+ // display enhancement only and must not keep the post-restore save barrier.
+ console.warn('Failed to enhance agent detail metadata:', error)
+ }
+}
+
+function isActiveAgentDetailRequest(targetAgentId: string, requestId: number) {
+ return targetAgentId === agentId.value && requestId === agentDetailRequestSequence
+}
+
+function invalidateTtsMetadataRequest() {
+ ttsOptionsRequestSequence += 1
+ ttsOptionsLoading.value = false
+ ttsOptionsModelId.value = ''
+}
+
+// 鑾峰彇闊宠壊鏄剧ず鍚嶇О
+function getVoiceDisplayName(ttsVoiceId: string) {
+ if (!ttsVoiceId)
+ return '璇烽�夋嫨'
+
+ console.log('=== 闊宠壊鏄犲皠璋冭瘯 ===')
+ console.log('褰撳墠闊宠壊ID:', ttsVoiceId)
+ console.log('褰撳墠TTS妯″瀷:', formData.value.ttsModelId)
+ console.log('鍙敤闊宠壊閫夐」:', voiceOptions.value)
+
+ // 棣栧厛灏濊瘯鐩存帴浠庨煶鑹查�夐」涓尮閰岻D
+ const voice = voiceOptions.value.find(v => v.id === ttsVoiceId)
+ if (voice) {
+ console.log('鐩存帴鍖归厤鎴愬姛:', voice)
+ return voice.name
+ }
+
+ // 濡傛灉娌℃壘鍒帮紝灏濊瘯鍏煎鎬ф槧灏�
+ if (voiceOptions.value.length > 0) {
+ console.log('鐩存帴鍖归厤澶辫触锛屽皾璇曞吋瀹规�ф槧灏�')
+
+ // 鍒涘缓绱㈠紩鏄犲皠锛歷oice1 鈫� 绗�1涓煶鑹诧紝voice2 鈫� 绗�2涓煶鑹�
+ const indexMap = {
+ voice1: 0,
+ voice2: 1,
+ voice3: 2,
+ voice4: 3,
+ voice5: 4,
+ }
+
+ const index = indexMap[ttsVoiceId]
+ if (index !== undefined && voiceOptions.value[index]) {
+ const mappedVoice = voiceOptions.value[index]
+ console.log(`绱㈠紩鏄犲皠: ${ttsVoiceId} 鈫� index ${index} 鈫� ${mappedVoice.name}`)
+ return mappedVoice.name
+ }
+ }
+
+ console.log('鎵�鏈夋槧灏勬柟寮忛兘澶辫触锛岃繑鍥炲師濮婭D:', ttsVoiceId)
+ return ttsVoiceId
+}
+
+// 鏇存柊鏄剧ず鍚嶇О
+function updateDisplayNames() {
+ if (!formData.value)
+ return
+
+ displayNames.value.vad = getModelDisplayName('VAD', formData.value.vadModelId)
+ displayNames.value.asr = getModelDisplayName('ASR', formData.value.asrModelId)
+ displayNames.value.llm = getModelDisplayName('LLM', formData.value.llmModelId)
+ displayNames.value.slm = getModelDisplayName('LLM', formData.value.slmModelId)
+ displayNames.value.vllm = getModelDisplayName('VLLM', formData.value.vllmModelId)
+ displayNames.value.intent = getModelDisplayName('Intent', formData.value.intentModelId)
+ displayNames.value.memory = getModelDisplayName('Memory', formData.value.memModelId)
+ displayNames.value.tts = getModelDisplayName('TTS', formData.value.ttsModelId)
+
+ // 瑙掕壊闊宠壊鐗规畩澶勭悊
+ displayNames.value.report = reportOptions.find(item => item.value === formData.value.chatHistoryConf)?.name
+
+ isVisibleReport.value = formData.value.memModelId !== 'Memory_nomem'
+
+ console.log('鏈�缁堥煶鑹叉樉绀哄悕绉�:', displayNames.value.voiceprint)
+}
+
+// 鍔犺浇瑙掕壊妯℃澘
+async function loadRoleTemplates() {
+ try {
+ const templates = await getRoleTemplates()
+ roleTemplates.value = templates
+ }
+ catch (error) {
+ console.error('鍔犺浇瑙掕壊妯℃澘澶辫触:', error)
+ }
+}
+
+// 鍔犺浇妯″瀷閫夐」
+async function loadModelOptions() {
+ const modelTypes = ['VAD', 'ASR', 'LLM', 'VLLM', 'Intent', 'Memory', 'TTS']
+
+ try {
+ await Promise.all(
+ modelTypes?.map(async (type) => {
+ console.log(`鍔犺浇妯″瀷绫诲瀷: ${type}`)
+ const options = await getModelOptions(type)
+ modelOptions.value[type] = options
+ console.log(`${type} 閫夐」:`, options)
+ }) || [],
+ )
+ console.log('鎵�鏈夋ā鍨嬮�夐」鍔犺浇瀹屾垚:', modelOptions.value)
+ }
+ catch (error) {
+ console.error('鍔犺浇妯″瀷閫夐」澶辫触:', error)
+ }
+}
+
+// 鏍规嵁璇█绛涢�夐煶鑹�
+interface VoiceSelectionOptions {
+ autoSelectVoice?: boolean
+ preferredLanguage?: string | null
+ preferredVoiceId?: string | null
+}
+
+interface TtsSelectionState {
+ modelId: string
+ voiceId: string
+ language: string
+ selectedLanguage: string
+ languageTouched: boolean
+ voiceTouched: boolean
+ optionsModelId: string
+ voiceOptions: VoiceOption[]
+ voiceDetails: Record<string, TtsVoice>
+ languageOptions: any[]
+ displayNames: {
+ tts: string
+ voiceprint: string
+ language: string
+ }
+}
+
+function captureTtsSelectionState(): TtsSelectionState {
+ return {
+ modelId: formData.value.ttsModelId || '',
+ voiceId: formData.value.ttsVoiceId || '',
+ language: formData.value.ttsLanguage || '',
+ selectedLanguage: selectedTtsLanguage.value,
+ languageTouched: ttsLanguageTouched.value,
+ voiceTouched: ttsVoiceTouched.value,
+ optionsModelId: ttsOptionsModelId.value,
+ voiceOptions: voiceOptions.value,
+ voiceDetails: voiceDetails.value,
+ languageOptions: languageOptions.value,
+ displayNames: {
+ tts: displayNames.value.tts,
+ voiceprint: displayNames.value.voiceprint,
+ language: displayNames.value.language,
+ },
+ }
+}
+
+function restoreTtsSelectionState(state: TtsSelectionState) {
+ formData.value.ttsModelId = state.modelId
+ formData.value.ttsVoiceId = state.voiceId
+ formData.value.ttsLanguage = state.language
+ selectedTtsLanguage.value = state.selectedLanguage
+ ttsLanguageTouched.value = state.languageTouched
+ ttsVoiceTouched.value = state.voiceTouched
+ ttsOptionsModelId.value = state.optionsModelId
+ voiceOptions.value = state.voiceOptions
+ voiceDetails.value = state.voiceDetails
+ languageOptions.value = state.languageOptions
+ displayNames.value.tts = state.displayNames.tts
+ displayNames.value.voiceprint = state.displayNames.voiceprint
+ displayNames.value.language = state.displayNames.language
+}
+
+function filterVoicesByLanguage(options: VoiceSelectionOptions = {}) {
+ if (!voiceDetails.value || Object.keys(voiceDetails.value).length === 0) {
+ voiceOptions.value = []
+ return
+ }
+
+ const allVoices = Object.values(voiceDetails.value)
+
+ // 鏍规嵁閫変腑鐨勮瑷�绛涢�夐煶鑹�
+ const filteredVoices = filterTtsVoicesByLanguage(allVoices, selectedTtsLanguage.value)
+
+ voiceOptions.value = filteredVoices.map(voice => ({
+ value: voice.id,
+ name: voice.name,
+ voiceDemo: voice.voiceDemo,
+ voice_demo: voice.voice_demo,
+ isClone: Boolean(voice.isClone),
+ train_status: voice.trainStatus,
+ }))
+
+ // 妫�鏌ュ綋鍓嶉�変腑鐨勯煶鑹叉槸鍚︽敮鎸佸綋鍓嶈瑷�锛屽鏋滀笉鏀寔鍒欓�夋嫨绗竴涓�
+ const currentVoiceSupportsLanguage = formData.value.ttsVoiceId
+ && filteredVoices.some(voice => voice.id === formData.value.ttsVoiceId)
+
+ if (!currentVoiceSupportsLanguage && options.autoSelectVoice) {
+ formData.value.ttsVoiceId = filteredVoices.length > 0 ? filteredVoices[0].id : ''
+ displayNames.value.voiceprint = filteredVoices.length > 0 ? filteredVoices[0].name : ''
+ ttsVoiceTouched.value = true
+ }
+ else {
+ displayNames.value.voiceprint = filteredVoices.find(item => item.id === formData.value.ttsVoiceId)?.name
+ || getVoiceDisplayName(formData.value.ttsVoiceId)
+ }
+}
+
+function getVoiceDefaultLanguage(ttsVoiceId: string) {
+ if (!ttsVoiceId || !voiceDetails.value?.[ttsVoiceId]?.languages) {
+ return ''
+ }
+ const languages = voiceDetails.value[ttsVoiceId].languages
+ .split(/[銆侊紱;,锛宂/)
+ .map(lang => lang.trim())
+ .filter(Boolean)
+ return languages[0] || ''
+}
+
+// 鏍规嵁璇煶鍚堟垚妯″瀷鍔犺浇璇█
+async function fetchAllLanguag(ttsModelId: string, options: VoiceSelectionOptions = {}): Promise<'loaded' | 'failed' | 'stale'> {
+ const requestId = ++ttsOptionsRequestSequence
+ ttsOptionsLoading.value = true
+ try {
+ const res = await getAllLanguage(ttsModelId)
+ if (requestId !== ttsOptionsRequestSequence) {
+ return 'stale'
+ }
+ if (!Array.isArray(res)) {
+ throw new TypeError('Invalid TTS voice metadata')
+ }
+ // An empty response cannot prove that the newly selected model accepts an
+ // empty voice. Until the API exposes an explicit "voice optional"
+ // capability, keep the previous tuple instead of persisting a guess.
+ if (!hasUsableTtsVoiceMetadata(res)) {
+ throw new Error('No TTS voice metadata is available')
+ }
+ // 淇濆瓨瀹屾暣鐨勯煶鑹蹭俊鎭�
+ voiceDetails.value = res.reduce<Record<string, TtsVoice>>((acc, voice) => {
+ acc[voice.id] = voice
+ return acc
+ }, {})
+ // 鎻愬彇鎵�鏈夎瑷�閫夐」骞跺幓閲�
+ const allLanguages = new Set()
+ res.forEach((voice) => {
+ if (voice.languages) {
+ const languagesArray = voice.languages.split(/[銆侊紱;,锛宂/).map(lang => lang.trim()).filter(lang => lang)
+ languagesArray.forEach(lang => allLanguages.add(lang))
+ }
+ })
+ languageOptions.value = Array.from(allLanguages).map(lang => ({
+ value: lang,
+ name: lang,
+ }))
+
+ const requestedLanguage = options.preferredLanguage
+ const preferredVoiceLanguage = options.preferredVoiceId
+ ? getVoiceDefaultLanguage(options.preferredVoiceId)
+ : ''
+ // Do not carry a language from the previous model into a provider which
+ // exposes no language dimension.
+ selectedTtsLanguage.value = ''
+ displayNames.value.language = ''
+ // 浼樺厛浣跨敤璋冪敤鏂规寚瀹氱殑璇█鎴栭煶鑹查粯璁よ瑷�锛屽啀鍥為��鍒版櫤鑳戒綋褰撳墠閰嶇疆
+ if (requestedLanguage && languageOptions.value.some(option => option.value === requestedLanguage)) {
+ selectedTtsLanguage.value = requestedLanguage
+ displayNames.value.language = requestedLanguage
+ }
+ else if (preferredVoiceLanguage && languageOptions.value.some(option => option.value === preferredVoiceLanguage)) {
+ selectedTtsLanguage.value = preferredVoiceLanguage
+ displayNames.value.language = preferredVoiceLanguage
+ }
+ else if (formData.value.ttsLanguage && languageOptions.value.some(option => option.value === formData.value.ttsLanguage)) {
+ selectedTtsLanguage.value = formData.value.ttsLanguage
+ displayNames.value.language = formData.value.ttsLanguage
+ }
+ else if (getVoiceDefaultLanguage(formData.value.ttsVoiceId)) {
+ selectedTtsLanguage.value = getVoiceDefaultLanguage(formData.value.ttsVoiceId)
+ displayNames.value.language = selectedTtsLanguage.value
+ }
+ else if (languageOptions.value.length > 0) {
+ selectedTtsLanguage.value = languageOptions.value[0].value
+ displayNames.value.language = languageOptions.value[0].value
+ }
+
+ // 鏍规嵁閫変腑鐨勮瑷�绛涢�夐煶鑹�
+ filterVoicesByLanguage(options)
+ ttsOptionsModelId.value = ttsModelId
+ return 'loaded'
+ }
+ catch (error) {
+ if (requestId === ttsOptionsRequestSequence) {
+ console.error('Failed to load TTS options:', error)
+ ttsOptionsModelId.value = ''
+ }
+ return requestId === ttsOptionsRequestSequence ? 'failed' : 'stale'
+ }
+ finally {
+ if (requestId === ttsOptionsRequestSequence) {
+ ttsOptionsLoading.value = false
+ }
+ }
+}
+
+// 鍔犺浇TTS闊宠壊閫夐」
+// async function loadVoiceOptions(ttsModelId?: string) {
+// if (!ttsModelId)
+// return
+
+// try {
+// console.log(`鍔犺浇闊宠壊閫夐」: ${ttsModelId}`)
+// const voices = await getTTSVoices(ttsModelId)
+// voiceOptions.value = voices
+// console.log('闊宠壊閫夐」:', voices)
+// }
+// catch (error) {
+// console.error('鍔犺浇闊宠壊閫夐」澶辫触:', error)
+// voiceOptions.value = []
+// }
+// }
+
+// 閫夋嫨瑙掕壊妯℃澘
+async function selectRoleTemplate(templateId: string) {
+ if (ttsOptionsLoading.value) {
+ return
+ }
+ if (selectedTemplateId.value === templateId) {
+ selectedTemplateId.value = ''
+ return
+ }
+
+ selectedTemplateId.value = templateId
+ const template = roleTemplates.value.find(t => t.id === templateId)
+ if (template) {
+ const previousTtsState = captureTtsSelectionState()
+ const templateTtsLanguage = template.ttsLanguage?.trim() || ''
+ const hasTemplateTtsLanguage = Boolean(templateTtsLanguage)
+ formData.value = {
+ ...formData.value,
+ systemPrompt: template.systemPrompt || formData.value.systemPrompt,
+ vadModelId: template.vadModelId || formData.value.vadModelId,
+ asrModelId: template.asrModelId || formData.value.asrModelId,
+ llmModelId: template.llmModelId || formData.value.llmModelId,
+ slmModelId: template.llmModelId || formData.value.slmModelId,
+ vllmModelId: template.vllmModelId || formData.value.vllmModelId,
+ intentModelId: template.intentModelId || formData.value.intentModelId,
+ memModelId: template.memModelId || formData.value.memModelId,
+ ttsModelId: template.ttsModelId || formData.value.ttsModelId,
+ ttsVoiceId: template.ttsVoiceId || formData.value.ttsVoiceId,
+ ttsLanguage: hasTemplateTtsLanguage ? templateTtsLanguage : formData.value.ttsLanguage,
+ agentName: template.agentName || formData.value.agentName,
+ chatHistoryConf: template.chatHistoryConf || formData.value.chatHistoryConf,
+ summaryMemory: template.summaryMemory || formData.value.summaryMemory,
+ langCode: template.langCode || formData.value.langCode,
+ }
+ if (hasTemplateTtsLanguage) {
+ selectedTtsLanguage.value = templateTtsLanguage
+ displayNames.value.language = templateTtsLanguage
+ }
+ if (template.ttsModelId || template.ttsVoiceId || hasTemplateTtsLanguage) {
+ const result = await fetchAllLanguag(template.ttsModelId || formData.value.ttsModelId, {
+ autoSelectVoice: true,
+ preferredLanguage: hasTemplateTtsLanguage ? templateTtsLanguage : '',
+ preferredVoiceId: template.ttsVoiceId,
+ })
+ if (result === 'failed') {
+ restoreTtsSelectionState(previousTtsState)
+ toast.warning(t('agent.ttsOptionsLoadFailed'))
+ }
+ else if (result === 'loaded') {
+ ttsLanguageTouched.value = true
+ ttsVoiceTouched.value = true
+ }
+ }
+ updateDisplayNames()
+ }
+}
+
+// 鎵撳紑閫夋嫨鍣�
+function openPicker(type: string) {
+ if (ttsOptionsLoading.value && (type === 'tts' || type === 'language' || type === 'voiceprint')) {
+ return
+ }
+ pickerShow.value[type] = true
+}
+
+// 閫夋嫨鍣ㄧ‘璁�
+async function onPickerConfirm(type: string, value: any, name: string) {
+ console.log('閫夋嫨鍣ㄧ‘璁�:', type, value, name)
+
+ const previousTtsState = type === 'tts' ? captureTtsSelectionState() : null
+ // 淇濆瓨鏄剧ず鍚嶇О
+ displayNames.value[type] = name
+
+ switch (type) {
+ case 'vad':
+ formData.value.vadModelId = value
+ break
+ case 'asr':
+ formData.value.asrModelId = value
+ break
+ case 'llm':
+ formData.value.llmModelId = value
+ break
+ case 'slm':
+ formData.value.slmModelId = value
+ break
+ case 'vllm':
+ formData.value.vllmModelId = value
+ break
+ case 'intent':
+ formData.value.intentModelId = value
+ displayNames.value.intent = name // 纭繚鏄剧ず鍚嶇О姝g‘鏇存柊
+ break
+ case 'memory':
+ formData.value.memModelId = value
+ formData.value.chatHistoryConf = value === 'Memory_nomem' ? 0 : 2
+ displayNames.value.memory = name // 纭繚鏄剧ず鍚嶇О姝g‘鏇存柊
+ displayNames.value.report = reportOptions[1].name
+ isVisibleReport.value = value !== 'Memory_nomem'
+ if (value === 'Memory_nomem' || value === 'Memory_mem_report_only') {
+ tempSummaryMemory.value = formData.value.summaryMemory
+ formData.value.summaryMemory = ''
+ }
+ else if (tempSummaryMemory.value !== '' && formData.value.summaryMemory === '') {
+ formData.value.summaryMemory = tempSummaryMemory.value
+ tempSummaryMemory.value = ''
+ }
+ break
+ case 'tts': {
+ const preferredLanguage = selectedTtsLanguage.value
+ formData.value.ttsModelId = value
+ formData.value.ttsVoiceId = ''
+ const result = await fetchAllLanguag(value, { autoSelectVoice: true, preferredLanguage })
+ if (result === 'failed' && previousTtsState) {
+ restoreTtsSelectionState(previousTtsState)
+ toast.warning(t('agent.ttsOptionsLoadFailed'))
+ }
+ else if (result === 'loaded') {
+ ttsLanguageTouched.value = true
+ ttsVoiceTouched.value = true
+ }
+ break
+ }
+ case 'language':
+ selectedTtsLanguage.value = value
+ formData.value.ttsLanguage = value
+ ttsLanguageTouched.value = true
+ filterVoicesByLanguage({ autoSelectVoice: true })
+ break
+ case 'voiceprint':
+ formData.value.ttsVoiceId = value
+ ttsVoiceTouched.value = true
+ if (selectedTtsLanguage.value) {
+ formData.value.ttsLanguage = selectedTtsLanguage.value
+ ttsLanguageTouched.value = true
+ }
+ displayNames.value.voiceprint = name // 纭繚鏄剧ず鍚嶇О姝g‘鏇存柊
+ break
+ case 'report':
+ formData.value.chatHistoryConf = value
+ break
+ }
+
+ pickerShow.value[type] = false
+}
+
+// 閫夋嫨鍣ㄥ彇娑�
+function onPickerCancel(type: string) {
+ pickerShow.value[type] = false
+ // 鍏抽棴鏃跺仠姝㈡挱鏀�
+ if (type === 'voiceprint') {
+ stopAudio()
+ }
+}
+
+// 鎾斁闊抽
+async function playAudio(voice: VoiceOption, event: Event) {
+ event.stopPropagation() // 闃绘浜嬩欢鍐掓场锛岄槻姝㈠叧闂笅鎷夋
+
+ if (!hasVoicePreview(voice)) {
+ return
+ }
+
+ // 濡傛灉姝e湪鎾斁鍚屼竴涓煶棰戯紝鍒欏仠姝�
+ if (playingVoiceId.value === voice.value) {
+ stopAudio()
+ return
+ }
+
+ // 鍋滄涔嬪墠鐨勯煶棰�
+ stopAudio()
+ const requestId = voicePreviewRequestGate.begin()
+ playingVoiceId.value = voice.value
+
+ try {
+ const audioUrl = await resolveVoicePreviewUrl({
+ id: voice.value,
+ isClone: voice.isClone,
+ voiceDemo: voice.voiceDemo || voice.voice_demo,
+ }, getVoiceCloneAudioId, getEnvBaseUrl())
+
+ // 鐢ㄦ埛鍙兘鍦ㄧ瓑寰呭厠闅嗛煶鑹蹭复鏃跺湴鍧�鏃跺彇娑堟垨鍒囨崲浜嗛煶鑹层��
+ if (!voicePreviewRequestGate.isCurrent(requestId) || playingVoiceId.value !== voice.value) {
+ return
+ }
+ if (!audioUrl) {
+ toast.error(t('voiceprint.getAudioFailed'))
+ playingVoiceId.value = ''
+ return
+ }
+
+ // 鍒涘缓鏂扮殑闊抽瀹炰緥
+ const audio = uni.createInnerAudioContext()
+ audioRef.value = audio
+ audio.src = audioUrl
+
+ // 鐩戝惉鎾斁缁撴潫
+ audio.onEnded(() => {
+ if (
+ voicePreviewRequestGate.isCurrent(requestId)
+ && audioRef.value === audio
+ && playingVoiceId.value === voice.value
+ ) {
+ playingVoiceId.value = ''
+ }
+ })
+
+ // 鐩戝惉鎾斁閿欒
+ audio.onError(() => {
+ if (
+ voicePreviewRequestGate.isCurrent(requestId)
+ && audioRef.value === audio
+ && playingVoiceId.value === voice.value
+ ) {
+ toast.error(t('voiceprint.audioPlayFailed'))
+ playingVoiceId.value = ''
+ }
+ })
+
+ // 鎾斁闊抽
+ audio.play()
+ }
+ catch (error) {
+ if (!voicePreviewRequestGate.isCurrent(requestId) || playingVoiceId.value !== voice.value) {
+ return
+ }
+ console.error('鑾峰彇鍏嬮殕闊宠壊璇曞惉鍦板潃澶辫触:', error)
+ toast.error(t('voiceprint.getAudioFailed'))
+ playingVoiceId.value = ''
+ }
+}
+
+// 鍋滄闊抽
+function stopAudio() {
+ voicePreviewRequestGate.invalidate()
+ if (audioRef.value) {
+ audioRef.value.stop()
+ audioRef.value.destroy()
+ audioRef.value = null
+ }
+ playingVoiceId.value = ''
+}
+
+// 鑾峰彇妯″瀷鏄剧ず鍚嶇О
+function getModelDisplayName(modelType: string, modelId: string) {
+ if (!modelId)
+ return '璇烽�夋嫨'
+
+ // 鐩存帴浠嶢PI閰嶇疆鏁版嵁涓煡鎵惧尮閰嶇殑ID
+ const options = modelOptions.value[modelType]
+
+ if (!options || options.length === 0) {
+ return modelId
+ }
+
+ const option = options.find(opt => opt.id === modelId)
+ if (option) {
+ return option.modelName
+ }
+ return modelId
+}
+
+// 淇濆瓨鏅鸿兘浣�
+async function saveAgent() {
+ if (saving.value) {
+ return
+ }
+ if (snapshotReloadBlocked.value) {
+ toast.error(t(snapshotReloadFailed.value
+ ? 'agentSnapshot.reloadAfterRestoreFailed'
+ : 'agentSnapshot.reloadAfterRestorePending'))
+ return
+ }
+ if (ttsOptionsLoading.value) {
+ return
+ }
+ const ttsSelectionTouched = ttsLanguageTouched.value || ttsVoiceTouched.value
+ const hasLanguageOptions = languageOptions.value.length > 0
+ const hasVoiceOptions = Object.keys(voiceDetails.value).length > 0
+ if (ttsSelectionTouched
+ && (ttsOptionsModelId.value !== formData.value.ttsModelId
+ || (hasLanguageOptions && !selectedTtsLanguage.value)
+ || (hasVoiceOptions && !formData.value.ttsVoiceId))) {
+ toast.warning(t('agent.ttsOptionsLoadFailed'))
+ return
+ }
+ if (!formData.value.agentName?.trim()) {
+ toast.warning(t('agent.pleaseInputAgentName'))
+ return
+ }
+
+ if (!formData.value.systemPrompt?.trim()) {
+ toast.warning(t('agent.pleaseInputRoleDescription'))
+ return
+ }
+
+ try {
+ saving.value = true
+ const tagNames = dynamicTags.value.map(tag => tag.tagName)
+ const tagsChanged = !isSameStringList(tagNames, originalTagNames.value)
+ // 鏋勫缓淇濆瓨鏁版嵁锛屽寘鍚笂涓嬫枃閰嶇疆鍜岃闊宠缃�
+ const saveData: Record<string, any> = {
+ ...formData.value,
+ contextProviders: providerStore.providers,
+ functions: normalizeAgentFunctions(formData.value.functions || []),
+ }
+ delete saveData.ttsVolume
+ delete saveData.ttsRate
+ delete saveData.ttsPitch
+ delete saveData.ttsLanguage
+ delete saveData.ttsVoiceId
+ if (ttsLanguageTouched.value) {
+ saveData.ttsLanguage = selectedTtsLanguage.value
+ }
+ if (ttsVoiceTouched.value) {
+ saveData.ttsVoiceId = formData.value.ttsVoiceId
+ }
+ const changedTtsFields = new Set(speedPitchStore.changedFields)
+ if (changedTtsFields.has('ttsVolume')) {
+ saveData.ttsVolume = speedPitchStore.speedPitch.ttsVolume
+ }
+ if (changedTtsFields.has('ttsRate')) {
+ saveData.ttsRate = speedPitchStore.speedPitch.ttsRate
+ }
+ if (changedTtsFields.has('ttsPitch')) {
+ saveData.ttsPitch = speedPitchStore.speedPitch.ttsPitch
+ }
+ if (tagsChanged) {
+ saveData.tagNames = tagNames
+ }
+ await updateAgent(agentId.value, saveData)
+ if (tagsChanged) {
+ originalTagNames.value = [...tagNames]
+ }
+ speedPitchStore.resetChangedFields()
+ await loadAgentDetail()
+
+ toast.success(t('agent.saveSuccess'))
+ }
+ catch (error) {
+ console.error('淇濆瓨澶辫触:', error)
+ toast.error(t('agent.saveFail'))
+ }
+ finally {
+ saving.value = false
+ }
+}
+
+function loadPluginFunctions() {
+ getPluginFunctions().then((res) => {
+ const processedFunctions = res?.map((item) => {
+ const meta = JSON.parse(item.fields || '[]')
+ const params = meta.reduce((m: any, f: any) => {
+ m[f.key] = f.default
+ return m
+ }, {})
+ return { ...item, fieldsMeta: meta, params }
+ }) || []
+
+ allFunctions.value = processedFunctions
+ // 鍚屾椂鏇存柊鍒皊tore
+ pluginStore.setAllFunctions(processedFunctions)
+ })
+}
+
+function handleTools() {
+ console.log('褰撳墠鎻掍欢閰嶇疆:', formData.value.functions)
+
+ // 纭繚store涓湁鏈�鏂版暟鎹�
+ pluginStore.setCurrentAgentId(agentId.value)
+ pluginStore.setCurrentFunctions(normalizeAgentFunctions(formData.value.functions || []))
+ pluginStore.setAllFunctions(allFunctions.value)
+
+ uni.navigateTo({
+ url: '/pages/agent/tools',
+ })
+}
+
+// 鑾峰彇鏅鸿兘浣撴爣绛�
+async function loadAgentTags(targetAgentId = agentId.value) {
+ if (!targetAgentId) {
+ return false
+ }
+ const requestId = ++agentTagRequestSequence
+ try {
+ const res = await getAgentTags(targetAgentId)
+ if (!isActiveAgentTagRequest(targetAgentId, requestId)) {
+ return false
+ }
+ dynamicTags.value = res || []
+ originalTagNames.value = dynamicTags.value.map(tag => tag.tagName)
+ return true
+ }
+ catch (error) {
+ if (isActiveAgentTagRequest(targetAgentId, requestId)) {
+ console.error('鍔犺浇鏅鸿兘浣撴爣绛惧け璐�:', error)
+ }
+ return false
+ }
+}
+
+function isActiveAgentTagRequest(targetAgentId: string, requestId: number) {
+ return targetAgentId === agentId.value && requestId === agentTagRequestSequence
+}
+
+async function handleSnapshotRestored(context: SnapshotRestoreContext) {
+ if (!context || context.agentId !== agentId.value) {
+ return
+ }
+ await reloadAgentAfterSnapshotRestore(context.agentId)
+}
+
+async function reloadAgentAfterSnapshotRestore(targetAgentId = agentId.value) {
+ if (!targetAgentId || targetAgentId !== agentId.value) {
+ return false
+ }
+ const reloadId = ++snapshotReloadSequence
+ const detailRequestId = ++agentDetailRequestSequence
+ const tagRequestId = ++agentTagRequestSequence
+ snapshotReloadBlocked.value = true
+ snapshotReloadFailed.value = false
+ loading.value = true
+ invalidateTtsMetadataRequest()
+
+ try {
+ // Apply detail and tags only after both persisted reads succeed. If either
+ // fails, the old form may remain visible but cannot be saved until retry.
+ const [detail, tags] = await Promise.all([
+ getAgentDetail(targetAgentId),
+ getAgentTags(targetAgentId),
+ ])
+ if (!isActiveSnapshotReload(targetAgentId, reloadId, detailRequestId, tagRequestId)) {
+ return false
+ }
+
+ applyPersistedAgentDetail(detail, targetAgentId)
+ dynamicTags.value = tags || []
+ originalTagNames.value = dynamicTags.value.map(tag => tag.tagName)
+
+ // Voice metadata is an optional display enhancement. Its own failure must
+ // not turn a successful persisted detail+tag reload into a blocked form.
+ await enhanceAgentDetailMetadata(detail, targetAgentId, detailRequestId)
+ if (!isActiveSnapshotReload(targetAgentId, reloadId, detailRequestId, tagRequestId)) {
+ return false
+ }
+ snapshotReloadBlocked.value = false
+ snapshotReloadFailed.value = false
+ return true
+ }
+ catch (error) {
+ if (isActiveSnapshotReload(targetAgentId, reloadId, detailRequestId, tagRequestId)) {
+ console.error('鎭㈠鍚庨噸鏂板姞杞芥櫤鑳戒綋澶辫触:', error)
+ snapshotReloadFailed.value = true
+ toast.error(t('agentSnapshot.reloadAfterRestoreFailed'))
+ }
+ return false
+ }
+ finally {
+ if (isActiveSnapshotReload(targetAgentId, reloadId, detailRequestId, tagRequestId)) {
+ loading.value = false
+ }
+ }
+}
+
+function isActiveSnapshotReload(
+ targetAgentId: string,
+ reloadId: number,
+ detailRequestId: number,
+ tagRequestId: number,
+) {
+ return targetAgentId === agentId.value
+ && reloadId === snapshotReloadSequence
+ && detailRequestId === agentDetailRequestSequence
+ && tagRequestId === agentTagRequestSequence
+}
+
+function retrySnapshotReload() {
+ if (!snapshotReloadFailed.value || !agentId.value) {
+ return
+ }
+ void reloadAgentAfterSnapshotRestore(agentId.value)
+}
+
+function openSnapshotPanel() {
+ if (saving.value || snapshotReloadBlocked.value) {
+ toast.warning(t('agentSnapshot.mutationBusy'))
+ return
+ }
+ showSnapshotPanel.value = true
+}
+
+function isSameStringList(left: string[], right: string[]) {
+ if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) {
+ return false
+ }
+ return left.every((value, index) => value === right[index])
+}
+
+// 鐩戝惉store涓殑鎻掍欢閰嶇疆鍙樺寲
+watch(() => pluginStore.currentFunctions, (newFunctions) => {
+ formData.value.functions = normalizeAgentFunctions(newFunctions || [])
+}, { deep: true })
+
+watch(agentId, (currentAgentId, previousAgentId) => {
+ if (currentAgentId === previousAgentId) {
+ return
+ }
+ agentDetailRequestSequence += 1
+ agentTagRequestSequence += 1
+ snapshotReloadSequence += 1
+ invalidateTtsMetadataRequest()
+ loading.value = false
+ snapshotReloadFailed.value = false
+ snapshotReloadBlocked.value = Boolean(currentAgentId)
+ originalAgentDetail.value = null
+ originalTagNames.value = []
+ if (currentAgentId) {
+ void reloadAgentAfterSnapshotRestore(currentAgentId)
+ }
+}, { flush: 'sync' })
+
+onMounted(async () => {
+ loadAgentTags()
+
+ // 鍏堝姞杞芥ā鍨嬮�夐」鍜岃鑹叉ā鏉�
+ await Promise.all([
+ loadRoleTemplates(),
+ loadModelOptions(),
+ loadPluginFunctions(),
+ ])
+
+ // 鐒跺悗鍔犺浇鏅鸿兘浣撹鎯咃紝杩欐牱鍙互姝g‘鏄犲皠鏄剧ず鍚嶇О
+ if (agentId.value && !snapshotReloadBlocked.value) {
+ await loadAgentDetail()
+ }
+})
+</script>
+
+<template>
+ <view class="bg-[#f5f7fb] px-[20rpx]">
+ <view class="mb-[24rpx] flex items-center justify-between border border-[#eeeeee] rounded-[20rpx] bg-white p-[24rpx]" style="box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);">
+ <view>
+ <text class="block text-[32rpx] text-[#232338] font-bold">
+ {{ t('agent.editTitle') }}
+ </text>
+ <text v-if="currentVersionNo" class="mt-[8rpx] block text-[24rpx] text-[#65686f]">
+ {{ t('agentSnapshot.currentVersion') }} #{{ currentVersionNo }}
+ </text>
+ </view>
+ <wd-button
+ size="small"
+ type="primary"
+ :disabled="saving || snapshotReloadBlocked"
+ custom-class="!bg-[#336cff] !h-[64rpx] !rounded-[32rpx]"
+ @click="openSnapshotPanel"
+ >
+ {{ t('agentSnapshot.title') }}
+ </wd-button>
+ </view>
+
+ <!-- 鍩虹淇℃伅鏍囬
+ <view class="pb-[20rpx] first:pt-[20rpx]">
+ <text class="text-[32rpx] text-[#232338] font-bold">
+ {{ t('agent.basicInfo') }}
+ </text>
+ </view -->
+
+ <!-- 鍩虹淇℃伅鍗$墖 -->
+ <view class="mb-[24rpx] border border-[#eeeeee] rounded-[20rpx] bg-[#fbfbfb] p-[24rpx]" style="box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);">
+ <view class="mb-[24rpx] last:mb-0">
+ <text class="mb-[12rpx] block text-[28rpx] text-[#232338] font-medium">
+ {{ t('agent.agentName') }}
+ </text>
+ <input
+ v-model="formData.agentName"
+ class="box-border h-[80rpx] w-full border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[16rpx_20rpx] text-[28rpx] text-[#232338] leading-[1.4] outline-none focus:border-[#336cff] focus:bg-white placeholder:text-[#9d9ea3]"
+ type="text"
+ :placeholder="t('agent.inputAgentName')"
+ >
+ </view>
+
+ <view class="mb-[24rpx] last:mb-0">
+ <text class="mb-[12rpx] block text-[28rpx] text-[#232338] font-medium">
+ {{ t('agent.agentTag') }}
+ </text>
+ <input
+ v-if="inputVisible"
+ v-model="inputValue"
+ class="mb-[10rpx] box-border h-[80rpx] w-full border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[16rpx_20rpx] text-[28rpx] text-[#232338] leading-[1.4] outline-none focus:border-[#336cff] focus:bg-white placeholder:text-[#9d9ea3]"
+ type="text"
+ :maxlength="20"
+ :placeholder="t('agent.inputAgentTag')"
+ @keyup.enter="handleInputConfirm"
+ @blur="handleInputConfirm"
+ >
+ <view class="flex flex-wrap gap-[10rpx_10rpx]">
+ <wd-tag v-for="tag in dynamicTags" :key="tag.id" class="items-center border !flex !border-[rgba(51,108,255,0.2)] !bg-[rgba(51,108,255,0.1)] !text-[#336cff]" round closable @close="handleCloseTag(tag.id)">
+ {{ tag.tagName }}
+ </wd-tag>
+ <wd-button v-if="!inputVisible" class="!bg-[rgba(51,108,255,0.1)] !text-[#336cff]" size="small" icon="add" @click="showInput">
+ {{ t('agent.addAgentTag') }}
+ </wd-button>
+ </view>
+ </view>
+
+ <view class="mb-[24rpx] last:mb-0">
+ <text class="mb-[12rpx] block text-[28rpx] text-[#232338] font-medium">
+ {{ t('agent.roleMode') }}
+ </text>
+ <view class="mt-0 flex flex-wrap gap-[12rpx]">
+ <view
+ v-for="template in roleTemplates"
+ :key="template.id"
+ class="cursor-pointer rounded-[20rpx] px-[24rpx] py-[12rpx] text-[24rpx] transition-all duration-300"
+ :class="selectedTemplateId === template.id
+ ? 'bg-[#336cff] text-white border border-[#336cff]'
+ : 'bg-[rgba(51,108,255,0.1)] text-[#336cff] border border-[rgba(51,108,255,0.2)]'"
+ @click="selectRoleTemplate(template.id)"
+ >
+ {{ template.agentName }}
+ </view>
+ </view>
+ </view>
+
+ <view class="mb-[24rpx] last:mb-0">
+ <text class="mb-[12rpx] block text-[28rpx] text-[#232338] font-medium">
+ {{ t('agent.contextProvider') }}
+ </text>
+ <view class="mt-0 flex flex-wrap items-center gap-[12rpx]">
+ <text class="text-[26rpx] text-[#65686f]">
+ {{ t('agent.contextProviderSuccess', { count: providerStore.providers.length }) }}
+ </text>
+ <a class="text-[26rpx] text-[#5778ff] no-underline" href="https://github.com/xinnan-tech/xiaozhi-esp32-server/blob/main/docs/context-provider-integration.md" target="_blank">
+ {{ t('agent.contextProviderDocLink') }}
+ </a>
+ <wd-button class="!bg-[rgba(51,108,255,0.1)] !text-[#336cff]" size="small" @click="openContextProviderDialog">
+ {{ t('agent.editContextProvider') }}
+ </wd-button>
+ </view>
+ </view>
+
+ <view class="mb-[24rpx] last:mb-0">
+ <text class="mb-[12rpx] block text-[28rpx] text-[#232338] font-medium">
+ {{ t('agent.roleDescription') }}
+ </text>
+ <textarea
+ v-model="formData.systemPrompt"
+ :maxlength="2000"
+ :placeholder="t('agent.inputRoleDescription')"
+ class="box-border h-[500rpx] w-full resize-none break-words break-all border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] text-[26rpx] text-[#232338] leading-[1.6] outline-none focus:border-[#336cff] focus:bg-white placeholder:text-[#9d9ea3]"
+ />
+ <view class="mt-[8rpx] text-right text-[22rpx] text-[#9d9ea3]">
+ {{ (formData.systemPrompt || '').length }}/2000
+ </view>
+ </view>
+ </view>
+
+ <!-- 妯″瀷閰嶇疆鏍囬 -->
+ <view class="pb-[20rpx]">
+ <text class="text-[32rpx] text-[#232338] font-bold">
+ {{ t('agent.modelConfig') }}
+ </text>
+ </view>
+
+ <!-- 妯″瀷閰嶇疆鍗$墖 -->
+ <view class="mb-[24rpx] border border-[#eeeeee] rounded-[20rpx] bg-[#fbfbfb] p-[24rpx]" style="box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);">
+ <view class="flex flex-col gap-[16rpx]">
+ <view class="flex cursor-pointer items-center justify-between border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] transition-all duration-300 active:bg-[#eef3ff]" @click="openPicker('vad')">
+ <text class="text-[28rpx] text-[#232338] font-medium">
+ {{ t('agent.vad') }}
+ </text>
+ <text class="mx-[16rpx] flex-1 text-right text-[26rpx] text-[#65686f]">
+ {{ displayNames.vad }}
+ </text>
+ <wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
+ </view>
+
+ <view class="flex cursor-pointer items-center justify-between border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] transition-all duration-300 active:bg-[#eef3ff]" @click="openPicker('asr')">
+ <text class="text-[28rpx] text-[#232338] font-medium">
+ {{ t('agent.asr') }}
+ </text>
+ <text class="mx-[16rpx] flex-1 text-right text-[26rpx] text-[#65686f]">
+ {{ displayNames.asr }}
+ </text>
+ <wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
+ </view>
+
+ <view class="flex cursor-pointer items-center justify-between border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] transition-all duration-300 active:bg-[#eef3ff]" @click="openPicker('llm')">
+ <text class="text-[28rpx] text-[#232338] font-medium">
+ {{ t('agent.llm') }}
+ </text>
+ <text class="mx-[16rpx] flex-1 text-right text-[26rpx] text-[#65686f]">
+ {{ displayNames.llm }}
+ </text>
+ <wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
+ </view>
+
+ <view class="flex cursor-pointer items-center justify-between border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] transition-all duration-300 active:bg-[#eef3ff]" @click="openPicker('slm')">
+ <text class="text-[28rpx] text-[#232338] font-medium">
+ {{ t('agent.slm') }}
+ </text>
+ <text class="mx-[16rpx] flex-1 text-right text-[26rpx] text-[#65686f]">
+ {{ displayNames.slm }}
+ </text>
+ <wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
+ </view>
+
+ <view class="flex cursor-pointer items-center justify-between border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] transition-all duration-300 active:bg-[#eef3ff]" @click="openPicker('vllm')">
+ <text class="text-[28rpx] text-[#232338] font-medium">
+ {{ t('agent.vllm') }}
+ </text>
+ <text class="mx-[16rpx] flex-1 text-right text-[26rpx] text-[#65686f]">
+ {{ displayNames.vllm }}
+ </text>
+ <wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
+ </view>
+
+ <view class="flex cursor-pointer items-center justify-between border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] transition-all duration-300 active:bg-[#eef3ff]" @click="openPicker('intent')">
+ <text class="text-[28rpx] text-[#232338] font-medium">
+ {{ t('agent.intent') }}
+ </text>
+ <text class="mx-[16rpx] flex-1 text-right text-[26rpx] text-[#65686f]">
+ {{ displayNames.intent }}
+ </text>
+ <wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
+ </view>
+
+ <view class="flex cursor-pointer items-center justify-between border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] transition-all duration-300 active:bg-[#eef3ff]" @click="openPicker('memory')">
+ <text class="text-[28rpx] text-[#232338] font-medium">
+ {{ t('agent.memory') }}
+ </text>
+ <text class="mx-[16rpx] flex-1 text-right text-[26rpx] text-[#65686f]">
+ {{ displayNames.memory }}
+ </text>
+ <wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
+ </view>
+
+ <view v-show="isVisibleReport" class="flex cursor-pointer items-center justify-between border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] transition-all duration-300 active:bg-[#eef3ff]" @click="openPicker('report')">
+ <text class="text-[28rpx] text-[#232338] font-medium">
+ {{ t('agent.reportMode') }}
+ </text>
+ <text class="mx-[16rpx] flex-1 text-right text-[26rpx] text-[#65686f]">
+ {{ displayNames.report }}
+ </text>
+ <wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
+ </view>
+ </view>
+ </view>
+
+ <!-- 璇煶璁剧疆鏍囬 -->
+ <view class="pb-[20rpx]">
+ <text class="text-[32rpx] text-[#232338] font-bold">
+ {{ t('agent.voiceSettings') }}
+ </text>
+ </view>
+
+ <!-- 璇煶璁剧疆鍗$墖 -->
+ <view class="mb-[24rpx] border border-[#eeeeee] rounded-[20rpx] bg-[#fbfbfb] p-[24rpx]" style="box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);">
+ <view class="flex flex-col gap-[16rpx]">
+ <view class="flex cursor-pointer items-center justify-between border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] transition-all duration-300 active:bg-[#eef3ff]" @click="openPicker('tts')">
+ <text class="text-[28rpx] text-[#232338] font-medium">
+ {{ t('agent.tts') }}
+ </text>
+ <text class="mx-[16rpx] flex-1 text-right text-[26rpx] text-[#65686f]">
+ {{ displayNames.tts }}
+ </text>
+ <wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
+ </view>
+
+ <view class="flex cursor-pointer items-center justify-between border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] transition-all duration-300 active:bg-[#eef3ff]" @click="openPicker('language')">
+ <text class="text-[28rpx] text-[#232338] font-medium">
+ {{ t('agent.language') }}
+ </text>
+ <text class="mx-[16rpx] flex-1 text-right text-[26rpx] text-[#65686f]">
+ {{ displayNames.language }}
+ </text>
+ <wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
+ </view>
+
+ <view class="flex cursor-pointer items-center justify-between border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] transition-all duration-300 active:bg-[#eef3ff]" @click="openPicker('voiceprint')">
+ <text class="text-[28rpx] text-[#232338] font-medium">
+ {{ t('agent.voiceprint') }}
+ </text>
+ <text class="mx-[16rpx] flex-1 text-right text-[26rpx] text-[#65686f]">
+ {{ displayNames.voiceprint }}
+ </text>
+ <wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
+ </view>
+
+ <view class="flex items-center justify-between border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx]">
+ <view class="text-[28rpx] text-[#232338] font-medium">
+ {{ t('agent.languageConfig') }}
+ </view>
+ <view class="cursor-pointer rounded-[20rpx] bg-[rgba(51,108,255,0.1)] px-[24rpx] py-[12rpx] text-[24rpx] text-[#336cff] transition-all duration-300 active:bg-[#336cff] active:text-white" @click="handleRegulate">
+ <text>{{ t('agent.editFunctions') }}</text>
+ </view>
+ </view>
+
+ <view class="flex items-center justify-between border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx]">
+ <view class="text-[28rpx] text-[#232338] font-medium">
+ {{ t('agent.plugins') }}
+ </view>
+ <view class="cursor-pointer rounded-[20rpx] bg-[rgba(51,108,255,0.1)] px-[24rpx] py-[12rpx] text-[24rpx] text-[#336cff] transition-all duration-300 active:bg-[#336cff] active:text-white" @click="handleTools">
+ <text>{{ t('agent.editFunctions') }}</text>
+ </view>
+ </view>
+ </view>
+ </view>
+
+ <!-- 璁板繂鍘嗗彶鏍囬 -->
+ <view class="pb-[20rpx]">
+ <text class="text-[32rpx] text-[#232338] font-bold">
+ {{ t('agent.historyMemory') }}
+ </text>
+ </view>
+
+ <!-- 璁板繂鍘嗗彶鍗$墖 -->
+ <view class="mb-[24rpx] border border-[#eeeeee] rounded-[20rpx] bg-[#fbfbfb] p-[24rpx]" style="box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);">
+ <view class="mb-[24rpx] last:mb-0">
+ <textarea
+ v-model="formData.summaryMemory"
+ :placeholder="t('agent.memoryContent')"
+ :disabled="isMemoryDisabled"
+ :style="isMemoryDisabled ? 'background: #f0f0f0' : ''"
+ class="box-border h-[500rpx] w-full resize-none break-words break-all border border-[#eeeeee] rounded-[12rpx] p-[20rpx] text-[26rpx] leading-[1.6] opacity-80 outline-none"
+ />
+ </view>
+ </view>
+
+ <!-- 淇濆瓨鎸夐挳 -->
+ <view class="mt-[40rpx] p-0">
+ <view
+ v-if="snapshotReloadBlocked"
+ class="mb-[18rpx] rounded-[16rpx] bg-[rgba(245,108,108,0.1)] p-[20rpx] text-[24rpx] text-[#a34848] leading-[1.6]"
+ >
+ <text class="block">
+ {{ t(snapshotReloadFailed ? 'agentSnapshot.reloadAfterRestoreFailed' : 'agentSnapshot.reloadAfterRestorePending') }}
+ </text>
+ <wd-button
+ v-if="snapshotReloadFailed"
+ size="small"
+ type="info"
+ custom-class="mt-[14rpx] !h-[60rpx]"
+ @click="retrySnapshotReload"
+ >
+ {{ t('agentSnapshot.retryReload') }}
+ </wd-button>
+ </view>
+ <wd-button
+ type="primary"
+ :loading="saving"
+ :disabled="saving || ttsOptionsLoading || snapshotReloadBlocked"
+ custom-class="w-full h-[80rpx] rounded-[16rpx] text-[30rpx] font-semibold bg-[#336cff] active:bg-[#2d5bd1]"
+ @click="saveAgent"
+ >
+ {{ saving ? t('agent.saving') : t('agent.save') }}
+ </wd-button>
+ </view>
+ <!-- 妯″瀷閫夋嫨鍣� -->
+ <wd-action-sheet
+ v-model="pickerShow.vad"
+ :actions="modelOptions.VAD && modelOptions.VAD.map(item => ({ name: item.modelName, value: item.id }))"
+ @close="onPickerCancel('vad')"
+ @select="({ item }) => onPickerConfirm('vad', item.value, item.name)"
+ />
+
+ <wd-action-sheet
+ v-model="pickerShow.asr"
+ :actions="modelOptions.ASR && modelOptions.ASR.map(item => ({ name: item.modelName, value: item.id }))"
+ @close="onPickerCancel('asr')"
+ @select="({ item }) => onPickerConfirm('asr', item.value, item.name)"
+ />
+
+ <wd-action-sheet
+ v-model="pickerShow.llm"
+ :actions="modelOptions.LLM && modelOptions.LLM.map(item => ({ name: item.modelName, value: item.id }))"
+ @close="onPickerCancel('llm')"
+ @select="({ item }) => onPickerConfirm('llm', item.value, item.name)"
+ />
+
+ <wd-action-sheet
+ v-model="pickerShow.slm"
+ :actions="modelOptions.LLM && modelOptions.LLM.map(item => ({ name: item.modelName, value: item.id }))"
+ @close="onPickerCancel('slm')"
+ @select="({ item }) => onPickerConfirm('slm', item.value, item.name)"
+ />
+
+ <wd-action-sheet
+ v-model="pickerShow.vllm"
+ :actions="modelOptions.VLLM && modelOptions.VLLM.map(item => ({ name: item.modelName, value: item.id }))"
+ @close="onPickerCancel('vllm')"
+ @select="({ item }) => onPickerConfirm('vllm', item.value, item.name)"
+ />
+
+ <wd-action-sheet
+ v-model="pickerShow.intent"
+ :actions="modelOptions.Intent && modelOptions.Intent.map(item => ({ name: item.modelName, value: item.id }))"
+ @close="onPickerCancel('intent')"
+ @select="({ item }) => onPickerConfirm('intent', item.value, item.name)"
+ />
+
+ <wd-action-sheet
+ v-model="pickerShow.memory"
+ :actions="modelOptions.Memory && modelOptions.Memory.map(item => ({ name: item.modelName, value: item.id }))"
+ @close="onPickerCancel('memory')"
+ @select="({ item }) => onPickerConfirm('memory', item.value, item.name)"
+ />
+
+ <wd-action-sheet
+ v-model="pickerShow.tts"
+ :actions="modelOptions.TTS && modelOptions.TTS.map(item => ({ name: item.modelName, value: item.id }))"
+ class="custom-sheet-tts"
+ @close="onPickerCancel('tts')"
+ @select="({ item }) => onPickerConfirm('tts', item.value, item.name)"
+ />
+
+ <!-- 鑷畾涔夎闊抽�夋嫨寮瑰嚭灞� -->
+ <wd-popup v-model="pickerShow.voiceprint" class="custom-popup" position="bottom" @close="onPickerCancel('voiceprint')">
+ <view class="overflow-hidden rounded-[20rpx] bg-white pb-[20rpx] pt-[20rpx]">
+ <view class="max-h-[600rpx] overflow-y-auto">
+ <view
+ v-for="voice in voiceOptions"
+ :key="voice.value"
+ class="flex items-center justify-between border-b border-[#f5f5f5] p-[32rpx] transition-all active:bg-[#f5f7fb]"
+ @click="onPickerConfirm('voiceprint', voice.value, voice.name)"
+ >
+ <text :class="`flex-1 text-[28rpx] text-[#232338] ${hasVoicePreview(voice) ? '' : 'text-center'}`">
+ {{ voice.name }}
+ </text>
+ <view v-if="hasVoicePreview(voice)" class="ml-[20rpx]" @click.stop="playAudio(voice, $event)">
+ <wd-icon
+ :name="playingVoiceId === voice.value ? 'pause-circle' : 'play-circle'"
+ size="24px"
+ :custom-class="playingVoiceId === voice.value ? 'text-[#336cff]' : 'text-[#9d9ea3]'"
+ />
+ </view>
+ </view>
+ </view>
+ </view>
+ </wd-popup>
+ <wd-action-sheet
+ v-model="pickerShow.language"
+ :actions="languageOptions"
+ @close="onPickerCancel('language')"
+ @select="({ item }) => onPickerConfirm('language', item.value, item.name)"
+ />
+ <wd-action-sheet
+ v-model="pickerShow.report"
+ :actions="reportOptions"
+ @close="onPickerCancel('report')"
+ @select="({ item }) => onPickerConfirm('report', item.value, item.name)"
+ />
+ <AgentSnapshotPanel
+ v-model:visible="showSnapshotPanel"
+ :agent-id="agentId"
+ :current-version-no="currentVersionNo"
+ :has-unsaved-changes="hasUnsavedChanges"
+ :mutation-busy="saving || snapshotReloadBlocked"
+ @restored="handleSnapshotRestored"
+ />
+ </view>
+</template>
+
+<style lang="scss" scoped>
+::v-deep .wd-tag__close {
+ color: #336cff !important;
+}
+::v-deep .custom-popup {
+ .wd-popup {
+ padding: 20rpx !important;
+ background: transparent !important;
+ }
+}
+::v-deep .custom-sheet-tts {
+ .wd-action-sheet {
+ padding: 8px 0 !important;
+ overflow: hidden;
+ }
+ .wd-action-sheet__actions {
+ padding: 0 !important;
+ }
+}
+</style>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/index.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/index.vue
new file mode 100644
index 0000000..104aced
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/index.vue
@@ -0,0 +1,220 @@
+<route lang="jsonc" type="page">
+{
+ "layout": "default",
+ "style": {
+ "navigationBarTitleText": "鏅鸿兘浣�",
+ "navigationStyle": "custom"
+ }
+}
+</route>
+
+<script lang="ts" setup>
+import { onLoad } from '@dcloudio/uni-app'
+import { onMounted, ref } from 'vue'
+import CustomTabs from '@/components/custom-tabs/index.vue'
+import { t } from '@/i18n'
+import ChatHistory from '@/pages/chat-history/index.vue'
+import DeviceManagement from '@/pages/device/index.vue'
+import VoiceprintManagement from '@/pages/voiceprint/index.vue'
+import AgentEdit from './edit.vue'
+
+defineOptions({
+ name: 'AgentIndex',
+})
+
+// 鑾峰彇灞忓箷杈圭晫鍒板畨鍏ㄥ尯鍩熻窛绂�
+let safeAreaInsets: any
+let systemInfo: any
+
+// #ifdef MP-WEIXIN
+systemInfo = uni.getWindowInfo()
+safeAreaInsets = systemInfo.safeArea
+ ? {
+ top: systemInfo.safeArea.top,
+ right: systemInfo.windowWidth - systemInfo.safeArea.right,
+ bottom: systemInfo.windowHeight - systemInfo.safeArea.bottom,
+ left: systemInfo.safeArea.left,
+ }
+ : null
+// #endif
+
+// #ifndef MP-WEIXIN
+systemInfo = uni.getSystemInfoSync()
+safeAreaInsets = systemInfo.safeAreaInsets
+// #endif
+
+// 鏅鸿兘浣揑D
+const currentAgentId = ref('default')
+
+// 褰撳墠 tab
+const currentTab = ref('agent-config')
+
+// 鍒锋柊鍜屽姞杞界姸鎬�
+const refreshing = ref(false)
+const refresherEnabled = ref(false)
+
+// 瀛愮粍浠跺紩鐢�
+const deviceRef = ref()
+const chatRef = ref()
+const voiceprintRef = ref()
+
+// 鏇存柊鍒锋柊鍣ㄧ姸鎬�
+function updateRefresherEnabled(value: boolean) {
+ refresherEnabled.value = value
+}
+
+// Tab 閰嶇疆
+const tabList = [
+ {
+ label: t('agent.roleConfig'),
+ value: 'agent-config',
+ icon: '/static/tabbar/robot.png',
+ activeIcon: '/static/tabbar/robot_activate.png',
+ },
+ {
+ label: t('agent.deviceManagement'),
+ value: 'device-management',
+ icon: '/static/tabbar/device.png',
+ activeIcon: '/static/tabbar/device_activate.png',
+ },
+ {
+ label: t('agent.chatHistory'),
+ value: 'chat-history',
+ icon: '/static/tabbar/chat.png',
+ activeIcon: '/static/tabbar/chat_activate.png',
+ },
+ {
+ label: t('agent.voiceprintManagement'),
+ value: 'voiceprint-management',
+ icon: '/static/tabbar/microphone.png',
+ activeIcon: '/static/tabbar/microphone_activate.png',
+ },
+]
+
+// 杩斿洖涓婁竴椤�
+function goBack() {
+ uni.navigateBack()
+}
+
+// 澶勭悊 tab 鍒囨崲
+function handleTabChange(item: any) {
+ console.log('Tab changed:', item)
+}
+
+// 涓嬫媺鍒锋柊
+async function onRefresh() {
+ // 瑙掕壊缂栬緫椤甸潰涓嶉渶瑕佸埛鏂�
+ if (currentTab.value === 'agent-config') {
+ return
+ }
+
+ refreshing.value = true
+
+ try {
+ switch (currentTab.value) {
+ case 'device-management':
+ if (deviceRef.value?.refresh) {
+ await deviceRef.value.refresh()
+ }
+ break
+ case 'chat-history':
+ if (chatRef.value?.refresh) {
+ await chatRef.value.refresh()
+ }
+ break
+ case 'voiceprint-management':
+ if (voiceprintRef.value?.refresh) {
+ await voiceprintRef.value.refresh()
+ }
+ break
+ }
+ }
+ catch (error) {
+ console.error('鍒锋柊澶辫触:', error)
+ }
+ finally {
+ refreshing.value = false
+ }
+}
+
+// 瑙﹀簳鍔犺浇鏇村
+async function onLoadMore() {
+ // 鍙湁鑱婂ぉ璁板綍闇�瑕佸姞杞芥洿澶�
+ if (currentTab.value === 'chat-history' && chatRef.value?.loadMore) {
+ await chatRef.value.loadMore()
+ }
+}
+
+watch(() => currentTab.value, (newTab) => {
+ updateRefresherEnabled(newTab !== 'agent-config')
+})
+
+// 鎺ユ敹椤甸潰鍙傛暟
+onLoad((options) => {
+ if (options?.agentId) {
+ currentAgentId.value = options.agentId
+ console.log('鎺ユ敹鍒版櫤鑳戒綋ID:', options.agentId)
+ }
+})
+
+onMounted(async () => {
+ // 椤甸潰鍒濆鍖�
+})
+</script>
+
+<template>
+ <view class="h-screen flex flex-col bg-[#f5f7fb]">
+ <!-- 瀵艰埅鏍� -->
+ <wd-navbar :title="t('agent.pageTitle')" safe-area-inset-top>
+ <template #left>
+ <wd-icon name="arrow-left" size="18" @click="goBack" />
+ </template>
+ </wd-navbar>
+
+ <!-- 鑷畾涔� Tabs -->
+ <CustomTabs
+ v-model="currentTab"
+ :tab-list="tabList"
+ @change="handleTabChange"
+ />
+
+ <!-- 涓诲唴瀹规粴鍔ㄥ尯鍩� -->
+ <scroll-view
+ scroll-y
+ :style="{ height: `calc(100vh - ${safeAreaInsets?.top || 0}px - 180rpx)` }"
+ class="box-border flex-1 bg-[#f5f7fb]"
+ enable-back-to-top
+ :refresher-enabled="refresherEnabled"
+ :refresher-triggered="refreshing"
+ @refresherrefresh="onRefresh"
+ @scrolltolower="onLoadMore"
+ >
+ <!-- Tab 鍐呭 -->
+ <view class="flex-1">
+ <AgentEdit
+ v-if="currentTab === 'agent-config'"
+ :agent-id="currentAgentId"
+ />
+ <DeviceManagement
+ v-else-if="currentTab === 'device-management'"
+ ref="deviceRef"
+ :agent-id="currentAgentId"
+ />
+ <ChatHistory
+ v-else-if="currentTab === 'chat-history'"
+ ref="chatRef"
+ :agent-id="currentAgentId"
+ />
+ <VoiceprintManagement
+ v-else-if="currentTab === 'voiceprint-management'"
+ ref="voiceprintRef"
+ :agent-id="currentAgentId"
+ @update-refresher-enabled="updateRefresherEnabled"
+ />
+ </view>
+ </scroll-view>
+ </view>
+</template>
+
+<style scoped>
+</style>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/provider.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/provider.vue
new file mode 100644
index 0000000..47bd56e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/provider.vue
@@ -0,0 +1,208 @@
+<route lang="jsonc" type="page">
+{
+ "layout": "default",
+ "style": {
+ "navigationBarTitleText": "缂栬緫婧�",
+ "navigationStyle": "custom"
+ }
+}
+</route>
+
+<script lang="ts" setup>
+import type { Providers } from '@/api/agent/types'
+import { onMounted, ref } from 'vue'
+import { t } from '@/i18n'
+import { useProvider } from '@/store/provider'
+
+defineOptions({
+ name: 'Provider',
+})
+
+const providerStore = useProvider()
+
+const localProviders = ref<Providers[]>([])
+
+function initLocalData() {
+ localProviders.value = providerStore.providers.map((p) => {
+ const headers = p.headers || {}
+ return {
+ url: p.url,
+ headers: Object.entries(headers).map(([key, value]: [string, string]) => ({ key, value })),
+ }
+ })
+
+ if (localProviders.value.length === 0) {
+ localProviders.value.push({ url: '', headers: [{ key: '', value: '' }] })
+ }
+}
+
+function addProvider(index: number) {
+ localProviders.value.splice(index, 0, {
+ url: '',
+ headers: [{ key: '', value: '' }],
+ })
+}
+
+function removeProvider(index: number) {
+ localProviders.value.splice(index, 1)
+}
+
+function addHeader(pIndex: number, hIndex: number) {
+ localProviders.value[pIndex].headers.splice(hIndex, 0, { key: '', value: '' })
+}
+
+function removeHeader(pIndex: number, hIndex: number) {
+ localProviders.value[pIndex].headers.splice(hIndex, 1)
+}
+
+function handleConfirm() {
+ const result = localProviders.value
+ .filter(p => p.url.trim() !== '')
+ .map((p) => {
+ const headersObj = {}
+ p.headers.forEach((h) => {
+ if (h.key.trim()) {
+ headersObj[h.key.trim()] = h.value
+ }
+ })
+ return {
+ url: p.url.trim(),
+ headers: headersObj,
+ }
+ })
+
+ providerStore.updateProviders(result as Providers[])
+ goBack()
+}
+
+function goBack() {
+ uni.navigateBack()
+}
+
+onMounted(() => {
+ initLocalData()
+})
+</script>
+
+<template>
+ <view class="h-screen flex flex-col bg-[#f5f7fb]">
+ <!-- 澶撮儴瀵艰埅 -->
+ <wd-navbar
+ :title="t('contextProviderDialog.title')"
+ safe-area-inset-top
+ left-arrow
+ :bordered="false"
+ @click-left="goBack"
+ >
+ <template #left>
+ <wd-icon name="arrow-left" size="18" />
+ </template>
+ </wd-navbar>
+ <view class="flex-1 overflow-y-auto px-[20rpx] pt-[20rpx]">
+ <view v-if="localProviders.length === 0" class="flex flex-col items-center justify-center gap-[30rpx] py-[100rpx]">
+ <text class="text-[28rpx] text-[#9d9ea3]">
+ {{ t('contextProviderDialog.noContextApi') }}
+ </text>
+ <wd-button type="primary" size="small" @click="addProvider(0)">
+ {{ t('contextProviderDialog.add') }}
+ </wd-button>
+ </view>
+
+ <view v-else class="flex flex-col">
+ <view v-for="(provider, pIndex) in localProviders" :key="pIndex" class="mb-[30rpx]">
+ <view class="flex-1 border border-l-[6rpx] border-[#eee] border-l-[#336cff] rounded-[16rpx] bg-[#fff] p-[20rpx] shadow-[0_4rpx_16rpx_rgba(0,0,0,0.05)]">
+ <view class="mb-[30rpx] flex items-center justify-between">
+ <view class="flex gap-[16rpx]">
+ <wd-button
+ type="icon"
+ icon="add"
+ size="small"
+ class="!h-[60rpx] !w-[60rpx] !bg-[#66b1ff] !text-[#fff]"
+ @click="addProvider(pIndex + 1)"
+ />
+ <wd-button
+ type="icon"
+ icon="decrease"
+ size="small"
+ class="!h-[60rpx] !w-[60rpx] !bg-[#F56C6C] !text-[#fff]"
+ @click="removeProvider(pIndex)"
+ />
+ </view>
+ </view>
+ <view class="mb-[40rpx] flex items-center gap-[20rpx]">
+ <text class="w-[140rpx] text-[28rpx] text-[#606266] font-semibold">
+ {{ t('contextProviderDialog.apiUrl') }}
+ </text>
+ <wd-input
+ v-model="provider.url"
+ class="flex-1"
+ :placeholder="t('contextProviderDialog.apiUrlPlaceholder')"
+ />
+ </view>
+
+ <view class="flex items-start gap-[20rpx]">
+ <view class="mt-[6rpx] w-[140rpx] text-[28rpx] text-[#606266] font-semibold">
+ {{ t('contextProviderDialog.requestHeaders') }}
+ </view>
+ <view class="flex flex-1 flex-col gap-[20rpx] border border-[#dcdfe6] rounded-[12rpx] border-dashed bg-[#fcfcfc] p-[4rpx]">
+ <view
+ v-for="(header, hIndex) in provider.headers"
+ :key="hIndex"
+ class="flex flex-col gap-[16rpx] rounded-[12rpx] bg-[#fff]"
+ >
+ <view class="flex items-center gap-[8rpx]">
+ <wd-input
+ v-model="header.key"
+ placeholder="key"
+ class="w-full"
+ />
+ </view>
+ <view class="flex items-center gap-[8rpx]">
+ <wd-input
+ v-model="header.value"
+ placeholder="value"
+ class="w-full"
+ />
+ </view>
+ <view class="flex self-start gap-[12rpx]">
+ <wd-button
+ type="icon"
+ icon="add"
+ size="small"
+ class="!h-[50rpx] !w-[50rpx] !border-[1rpx] !border-solid !bg-[#ecf5ff] !text-[#b3d8ff]"
+ @click="addHeader(pIndex, hIndex + 1)"
+ />
+ <wd-button
+ type="icon"
+ icon="decrease"
+ size="small"
+ class="!h-[50rpx] !w-[50rpx] !border-[1rpx] !border-solid !bg-[#fef0f0] !text-[#F56C6C]"
+ @click="removeHeader(pIndex, hIndex)"
+ />
+ </view>
+ </view>
+ <view v-if="provider.headers.length === 0" class="flex items-center justify-center py-[20rpx] text-[26rpx] text-[#909399]">
+ <text class="mr-[16rpx]">
+ {{ t('contextProviderDialog.noHeaders') }}
+ </text>
+ <wd-button type="text" size="small" @click="addHeader(pIndex, 0)">
+ {{ t('contextProviderDialog.addHeader') }}
+ </wd-button>
+ </view>
+ </view>
+ </view>
+ </view>
+ </view>
+ </view>
+ </view>
+
+ <view class="flex gap-[20rpx] border-t border-[#eee] px-[30rpx] py-[30rpx]">
+ <wd-button type="primary" class="h-[80rpx] flex-1 rounded-[12rpx] text-[28rpx]" @click="handleConfirm">
+ {{ t('contextProviderDialog.confirm') }}
+ </wd-button>
+ </view>
+ </view>
+</template>
+
+<style scoped lang="scss">
+</style>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/speedPitch.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/speedPitch.vue
new file mode 100644
index 0000000..ac12fa6
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/speedPitch.vue
@@ -0,0 +1,157 @@
+<route lang="jsonc" type="page">
+{
+ "layout": "default",
+ "style": {
+ "navigationBarTitleText": "璇煶璁剧疆",
+ "navigationStyle": "custom"
+ }
+}
+</route>
+
+<script lang="ts" setup>
+import { t } from '@/i18n'
+import { useSpeedPitch } from '@/store'
+
+defineOptions({
+ name: 'SpeedPitch',
+})
+
+const speedPitchStore = useSpeedPitch()
+const SPEED_PITCH_FIELDS = ['ttsVolume', 'ttsRate', 'ttsPitch'] as const
+
+const localSettings = ref({
+ ttsVolume: 0,
+ ttsRate: 0,
+ ttsPitch: 0,
+})
+
+function handleConfirm() {
+ const changedFields = SPEED_PITCH_FIELDS.filter((field) => {
+ return localSettings.value[field] !== speedPitchStore.speedPitch[field]
+ })
+ speedPitchStore.updateSpeedPitch(localSettings.value, { changedFields })
+ goBack()
+}
+
+// 杩斿洖涓婁竴椤靛苟鏇存柊閰嶇疆
+function goBack() {
+ uni.navigateBack()
+}
+
+onMounted(() => {
+ localSettings.value = {
+ ttsVolume: speedPitchStore.speedPitch.ttsVolume,
+ ttsRate: speedPitchStore.speedPitch.ttsRate,
+ ttsPitch: speedPitchStore.speedPitch.ttsPitch,
+ }
+})
+</script>
+
+<template>
+ <view class="h-screen flex flex-col bg-[#f5f7fb]">
+ <!-- 澶撮儴瀵艰埅 -->
+ <wd-navbar
+ :title="t('agent.languageConfig')"
+ safe-area-inset-top
+ left-arrow
+ :bordered="false"
+ @click-left="goBack"
+ >
+ <template #left>
+ <wd-icon name="arrow-left" size="18" />
+ </template>
+ </wd-navbar>
+ <view class="flex flex-1 flex-col overflow-hidden">
+ <view class="flex flex-1 flex-col gap-[50rpx] overflow-y-auto px-[40rpx] py-[50rpx]">
+ <!-- 闊抽噺璋冭妭 -->
+ <view class="flex flex-col gap-[20rpx]">
+ <text class="text-[30rpx] text-[#232338] font-semibold">
+ {{ t('agent.ttsVolume') }}
+ </text>
+ <view class="flex items-center gap-[20rpx]">
+ <wd-slider
+ v-model="localSettings.ttsVolume"
+ :min="-100"
+ :max="100"
+ :step="1"
+ :show-value="false"
+ custom-class="voice-slider"
+ class="flex-1"
+ />
+ <text class="min-w-[80rpx] text-right text-[28rpx] text-[#336cff] font-medium">
+ {{ localSettings.ttsVolume }}
+ </text>
+ </view>
+ <text class="mt-[10rpx] text-[24rpx] text-[#9d9ea3]">
+ {{ t('agent.volumeHint') }}
+ </text>
+ </view>
+
+ <!-- 璇�熻皟鑺� -->
+ <view class="flex flex-col gap-[20rpx]">
+ <text class="text-[30rpx] text-[#232338] font-semibold">
+ {{ t('agent.ttsRate') }}
+ </text>
+ <view class="flex items-center gap-[20rpx]">
+ <wd-slider
+ v-model="localSettings.ttsRate"
+ :min="-100"
+ :max="100"
+ :step="1"
+ :show-value="false"
+ custom-class="voice-slider"
+ class="flex-1"
+ />
+ <text class="min-w-[80rpx] text-right text-[28rpx] text-[#336cff] font-medium">
+ {{ localSettings.ttsRate }}
+ </text>
+ </view>
+ <text class="mt-[10rpx] text-[24rpx] text-[#9d9ea3]">
+ {{ t('agent.speedHint') }}
+ </text>
+ </view>
+
+ <!-- 闊宠皟璋冭妭 -->
+ <view class="flex flex-col gap-[20rpx]">
+ <text class="text-[30rpx] text-[#232338] font-semibold">
+ {{ t('agent.ttsPitch') }}
+ </text>
+ <view class="flex items-center gap-[20rpx]">
+ <wd-slider
+ v-model="localSettings.ttsPitch"
+ :min="-100"
+ :max="100"
+ :step="1"
+ :show-value="false"
+ custom-class="voice-slider"
+ class="flex-1"
+ />
+ <text class="min-w-[80rpx] text-right text-[28rpx] text-[#336cff] font-medium">
+ {{ localSettings.ttsPitch }}
+ </text>
+ </view>
+ <text class="mt-[10rpx] text-[24rpx] text-[#9d9ea3]">
+ {{ t('agent.pitchHint') }}
+ </text>
+ </view>
+ </view>
+
+ <view class="flex gap-[20rpx] border-t border-[#eee] px-[30rpx] py-[30rpx]">
+ <wd-button type="primary" class="h-[80rpx] flex-1 rounded-[12rpx] text-[28rpx] !bg-[#336cff]" @click="handleConfirm">
+ {{ t('agent.save') }}
+ </wd-button>
+ </view>
+ </view>
+ </view>
+</template>
+
+<style scoped lang="scss">
+/* 鑷畾涔夋粦鍧楁牱寮� */
+:deep(.wd-slider) {
+ --wd-slider-bar-background: #e6ebff;
+ --wd-slider-bar-active-background: #336cff;
+ --wd-slider-thumb-border-color: #336cff;
+ --wd-slider-thumb-background: #336cff;
+ --wd-slider-thumb-size: 32rpx;
+}
+</style>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/tools.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/tools.vue
new file mode 100644
index 0000000..0793a39
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/agent/tools.vue
@@ -0,0 +1,609 @@
+<route lang="jsonc" type="page">
+{
+ "layout": "default",
+ "style": {
+ "navigationBarTitleText": "缂栬緫鍔熻兘",
+ "navigationStyle": "custom"
+ }
+}
+</route>
+
+<script lang="ts" setup>
+import { useMessage } from 'wot-design-uni/components/wd-message-box'
+import { getMcpAddress, getMcpTools } from '@/api/agent/agent'
+import { t } from '@/i18n'
+import { usePluginStore } from '@/store'
+
+const message = useMessage()
+const pluginStore = usePluginStore()
+
+const segmentedList = ref<string[]>([t('agent.tools.notSelected'), t('agent.tools.selected')])
+const currentSegmented = ref(t('agent.tools.notSelected'))
+const notSelectedList = ref<any[]>([])
+const selectedList = ref<any[]>([])
+
+// 浣跨敤璁$畻灞炴�т粠store鑾峰彇鏁版嵁
+const allFunctions = computed(() => pluginStore.allFunctions)
+const functions = computed(() => pluginStore.currentFunctions)
+const agentId = computed(() => pluginStore.currentAgentId)
+const mcpAddress = ref('')
+const mcpTools = ref<string[]>([])
+
+// 鍒濆鍖栨椂浠庢湰鍦板瓨鍌ㄥ姞杞組CP鍦板潃
+if (uni.getStorageSync(`cachedMcpAddress_${agentId.value}`)) {
+ mcpAddress.value = uni.getStorageSync(`cachedMcpAddress_${agentId.value}`)
+}
+
+// 鍙傛暟缂栬緫鐩稿叧
+const showParamDialog = ref(false)
+const currentFunction = ref<any>(null)
+const tempParams = ref<Record<string, any>>({})
+const arrayTextCache = ref<Record<string, string>>({})
+const jsonTextCache = ref<Record<string, string>>({})
+
+function normalizeFunctionParams(paramInfo: any, fallback: Record<string, any> = {}) {
+ if (!paramInfo)
+ return { ...fallback }
+ if (typeof paramInfo === 'string') {
+ try {
+ const parsed = JSON.parse(paramInfo)
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : { ...fallback }
+ }
+ catch {
+ return { ...fallback }
+ }
+ }
+ return typeof paramInfo === 'object' && !Array.isArray(paramInfo) ? { ...fallback, ...paramInfo } : { ...fallback }
+}
+
+async function mergeFunctions() {
+ selectedList.value = functions.value.map((mapping) => {
+ const meta = allFunctions.value.find(f => f.id === mapping.pluginId)
+ if (!meta) {
+ return { id: mapping.pluginId, name: mapping.pluginId, params: normalizeFunctionParams(mapping.paramInfo) }
+ }
+
+ return {
+ id: mapping.pluginId,
+ name: meta.name,
+ params: normalizeFunctionParams(mapping.paramInfo, meta.params),
+ fieldsMeta: meta.fieldsMeta,
+ }
+ })
+
+ // 鏈�夌殑鎻掍欢
+ notSelectedList.value = allFunctions.value.filter(
+ item => !selectedList.value.some(f => f.id === item.id),
+ )
+
+ if (agentId.value) {
+ // 浼樺厛鑾峰彇骞舵樉绀篗CP鍦板潃
+ try {
+ const address = await getMcpAddress(agentId.value)
+ mcpAddress.value = address
+ // 缂撳瓨鍒版湰鍦板瓨鍌紝涓嬫鎵撳紑椤甸潰鍙互绔嬪嵆鏄剧ず
+ uni.setStorageSync(`cachedMcpAddress_${agentId.value}`, address)
+ }
+ catch (error) {
+ mcpAddress.value = error
+ console.error('鑾峰彇MCP鍦板潃澶辫触:', error)
+ }
+
+ // 寮傛鑾峰彇MCP宸ュ叿鍒楄〃锛屼笉闃诲UI鏄剧ず
+ try {
+ const tools = await getMcpTools(agentId.value)
+ mcpTools.value = tools || []
+ }
+ catch (error) {
+ console.error('鑾峰彇MCP宸ュ叿鍒楄〃澶辫触:', error)
+ }
+ }
+}
+
+// 娣诲姞鎻掍欢鍒板凡閫�
+function selectFunction(func: any) {
+ // 娣诲姞鍒板凡閫夊垪琛�
+ selectedList.value = [...selectedList.value, {
+ id: func.id,
+ name: func.name,
+ params: normalizeFunctionParams(func.params),
+ fieldsMeta: func.fieldsMeta,
+ }]
+
+ // 浠庢湭閫夊垪琛ㄤ腑绉婚櫎
+ notSelectedList.value = notSelectedList.value.filter(
+ item => item.id !== func.id,
+ )
+}
+
+// 浠庡凡閫変腑绉婚櫎鎻掍欢
+function removeFunction(func: any) {
+ // 浠庡凡閫夊垪琛ㄤ腑绉婚櫎
+ selectedList.value = selectedList.value.filter(item => item.id !== func.id)
+
+ // 娣诲姞鍥炴湭閫夊垪琛�
+ const originalFunc = allFunctions.value.find(f => f.id === func.id)
+ if (originalFunc) {
+ notSelectedList.value.push(originalFunc)
+ }
+}
+
+// 缂栬緫鎻掍欢鍙傛暟
+function editFunction(func: any) {
+ currentFunction.value = func
+
+ // 鐩存帴浣跨敤褰撳墠鍑芥暟鐨勫弬鏁�
+ tempParams.value = normalizeFunctionParams(func.params)
+
+ // 鍒濆鍖栨枃鏈紦瀛�
+ if (func.fieldsMeta) {
+ func.fieldsMeta.forEach((field: any) => {
+ if (field.type === 'array') {
+ const value = tempParams.value[field.key]
+ arrayTextCache.value[field.key] = Array.isArray(value)
+ ? value.join('\n')
+ : value || ''
+ }
+ else if (field.type === 'json') {
+ const value = tempParams.value[field.key]
+ try {
+ jsonTextCache.value[field.key] = JSON.stringify(value || {}, null, 2)
+ }
+ catch {
+ jsonTextCache.value[field.key] = '{}'
+ }
+ }
+ })
+ }
+
+ showParamDialog.value = true
+}
+
+// 澶勭悊鍙傛暟鍙樺寲 - 瀹炴椂淇濆瓨
+function handleParamChange(key: string, value: any, field: any) {
+ tempParams.value[key] = value
+
+ // 瀹炴椂鏇存柊鍒� selectedList
+ if (currentFunction.value) {
+ const index = selectedList.value.findIndex(
+ f => f.id === currentFunction.value.id,
+ )
+ if (index >= 0) {
+ selectedList.value[index].params = { ...tempParams.value }
+ }
+ }
+}
+
+// 澶勭悊鏁扮粍绫诲瀷鍙傛暟鍙樺寲 - 瀹炴椂淇濆瓨
+function handleArrayChange(key: string, value: string, field: any) {
+ arrayTextCache.value[key] = value
+ // 杞崲涓烘暟缁勫瓨鍌�
+ const arrayValue = value.split('\n').filter(Boolean)
+ tempParams.value[key] = arrayValue
+
+ // 瀹炴椂鏇存柊鍒� selectedList
+ if (currentFunction.value) {
+ const index = selectedList.value.findIndex(
+ f => f.id === currentFunction.value.id,
+ )
+ if (index >= 0) {
+ selectedList.value[index].params = { ...tempParams.value }
+ }
+ }
+}
+
+// 澶勭悊JSON绫诲瀷鍙傛暟鍙樺寲 - 瀹炴椂淇濆瓨
+function handleJsonChange(key: string, value: string, field: any) {
+ jsonTextCache.value[key] = value
+ try {
+ const jsonValue = JSON.parse(value)
+ tempParams.value[key] = jsonValue
+
+ // 瀹炴椂鏇存柊鍒� selectedList
+ if (currentFunction.value) {
+ const index = selectedList.value.findIndex(
+ f => f.id === currentFunction.value.id,
+ )
+ if (index >= 0) {
+ selectedList.value[index].params = { ...tempParams.value }
+ }
+ }
+ }
+ catch {
+ message.alert(t('agent.tools.jsonFormatError'))
+ }
+}
+
+// 鍏抽棴鍙傛暟缂栬緫寮圭獥
+function closeParamEdit() {
+ showParamDialog.value = false
+ tempParams.value = {}
+ arrayTextCache.value = {}
+ jsonTextCache.value = {}
+}
+
+// 杩斿洖涓婁竴椤靛苟鏇存柊閰嶇疆
+function goBack() {
+ uni.navigateBack()
+}
+
+// 澶嶅埗MCP鍦板潃
+function copyMcpAddress() {
+ if (!mcpAddress.value) {
+ message.alert(t('agent.tools.noMcpAddressToCopy'))
+ return
+ }
+
+ uni.setClipboardData({
+ data: mcpAddress.value,
+ showToast: false,
+ success: () => {
+ message.alert(t('agent.tools.mcpAddressCopied'))
+ },
+ fail: () => {
+ message.alert(t('agent.tools.copyFailed'))
+ },
+ })
+}
+
+// 娓叉煋鍙傛暟瀛楁鐨勮緟鍔╁嚱鏁�
+function getFieldDisplayValue(field: any, value: any) {
+ if (field.type === 'array') {
+ return Array.isArray(value) ? value.join('\n') : value || ''
+ }
+ return value || ''
+}
+
+// 瀛楁璇存槑
+function getFieldRemark(field: any) {
+ let description = field.label || ''
+ if (field.default) {
+ description += `锛�${t('agent.tools.defaultValue')}锛�${field.default}锛塦
+ }
+ return description
+}
+
+// 鐩戝惉宸查�夊垪琛ㄥ彉鍖栵紝瀹炴椂鏇存柊鎻掍欢閰嶇疆锛岄伩鍏嶇敤鎴蜂笉鐐瑰嚮杩斿洖鎸夐挳瀵艰嚧閰嶇疆涓㈠け
+watch(() => selectedList.value, (newSelectedList) => {
+ const finalFunctions = newSelectedList.map(f => ({
+ pluginId: f.id,
+ paramInfo: normalizeFunctionParams(f.params),
+ }))
+ pluginStore.updateFunctions(finalFunctions)
+})
+
+onMounted(async () => {
+ // 鐩存帴浠巗tore鑾峰彇鏁版嵁骞跺悎骞�
+ await mergeFunctions()
+})
+</script>
+
+<template>
+ <view class="h-screen flex flex-col bg-[#f5f7fb]">
+ <!-- 澶撮儴瀵艰埅 -->
+ <wd-navbar
+ title=""
+ safe-area-inset-top
+ left-arrow
+ :bordered="false"
+ @click-left="goBack"
+ >
+ <template #left>
+ <wd-icon name="arrow-left" size="18" />
+ </template>
+ </wd-navbar>
+
+ <!-- 鍐呭鍖哄煙 -->
+ <scroll-view
+ scroll-y
+ class="box-border flex-1 bg-transparent px-[20rpx]"
+ :style="{ height: 'calc(100vh - 120rpx)' }"
+ :scroll-with-animation="true"
+ >
+ <!-- 鍐呯疆鎻掍欢鍖哄煙 -->
+ <view class="mt-[20rpx] flex flex-1 flex-col">
+ <view class="text-[32rpx] text-[#333] font-medium">
+ {{ t('agent.tools.builtInPlugins') }}
+ </view>
+ <view
+ class="mt-[20rpx] box-border flex flex-1 flex-col rounded-[10rpx] bg-white p-[20rpx]"
+ >
+ <!-- 鍒嗘鎺у埗鍣� -->
+ <wd-segmented
+ v-model:value="currentSegmented"
+ :options="segmentedList"
+ />
+
+ <!-- 鎻掍欢鍒楄〃 -->
+ <view class="mt-[20rpx] flex-1 overflow-hidden">
+ <!-- 鏈�夋彃浠� -->
+ <scroll-view
+ v-if="currentSegmented === t('agent.tools.notSelected')"
+ class="max-h-[600rpx] bg-transparent"
+ scroll-y
+ >
+ <view
+ v-if="notSelectedList.length === 0"
+ class="h-[400rpx] flex items-center justify-center"
+ >
+ <wd-status-tip image="content" :tip="t('agent.tools.noMorePlugins')" />
+ </view>
+ <view v-else class="p-[20rpx] space-y-[20rpx]">
+ <view
+ v-for="func in notSelectedList"
+ :key="func.id"
+ class="flex items-center justify-between border border-[#e9ecef] rounded-[10rpx] bg-[#f8f9fa] p-[20rpx]"
+ @click="selectFunction(func)"
+ >
+ <view class="flex-1">
+ <view
+ class="mb-[10rpx] text-[30rpx] text-[#333] font-medium"
+ >
+ {{ func.name }}
+ </view>
+ <view class="text-[24rpx] text-[#666]">
+ {{ func.providerCode }}
+ </view>
+ </view>
+ <view
+ class="h-[60rpx] w-[60rpx] flex items-center justify-center rounded-full bg-[#1677ff]"
+ >
+ <text class="text-[36rpx] text-white">
+ +
+ </text>
+ </view>
+ </view>
+ </view>
+ </scroll-view>
+
+ <!-- 宸查�夋彃浠� -->
+ <scroll-view v-else class="max-h-[600rpx] bg-transparent" scroll-y>
+ <view
+ v-if="selectedList.length === 0"
+ class="h-[400rpx] flex items-center justify-center"
+ >
+ <wd-status-tip image="content" :tip="t('agent.tools.pleaseSelectPlugin')" />
+ </view>
+ <view v-else class="p-[20rpx] space-y-[20rpx]">
+ <view
+ v-for="func in selectedList"
+ :key="func.id"
+ class="border border-[#d4edff] rounded-[10rpx] bg-[#f0f7ff] p-[20rpx]"
+ >
+ <view class="flex items-center justify-between">
+ <view class="flex-1" @click="editFunction(func)">
+ <view
+ class="mb-[10rpx] text-[30rpx] text-[#333] font-medium"
+ >
+ {{ func.name }}
+ </view>
+ <view class="text-[24rpx] text-[#1677ff]">
+ {{ t('agent.tools.clickToConfigure') }}
+ </view>
+ </view>
+ <view class="flex space-x-[20rpx]">
+ <!-- 閰嶇疆鎸夐挳 -->
+ <view
+ class="h-[60rpx] w-[60rpx] flex items-center justify-center rounded-full bg-[#1677ff]"
+ @click="editFunction(func)"
+ >
+ <text class="text-[24rpx] text-white">
+ 鈿�
+ </text>
+ </view>
+ <!-- 绉婚櫎鎸夐挳 -->
+ <view
+ class="h-[60rpx] w-[60rpx] flex items-center justify-center rounded-full bg-[#ff4757]"
+ @click="removeFunction(func)"
+ >
+ <text class="text-[32rpx] text-white">
+ 脳
+ </text>
+ </view>
+ </view>
+ </view>
+ </view>
+ </view>
+ </scroll-view>
+ </view>
+ </view>
+ </view>
+
+ <!-- MCP鎺ュ叆鐐瑰尯鍩� -->
+ <view class="mt-[20rpx] flex flex-1 flex-col">
+ <view class="text-[32rpx] text-[#333] font-medium">
+ {{ t('agent.tools.mcpEndpoint') }}
+ </view>
+ <view
+ class="mt-[20rpx] box-border flex flex-1 flex-col rounded-[10rpx] bg-white p-[20rpx]"
+ >
+ <view class="flex items-center justify-between text-[24rpx]">
+ <input
+ v-model="mcpAddress"
+ type="text"
+ disabled
+ class="flex-1 rounded-[10rpx] bg-[#f5f7fb] p-[20rpx]"
+ >
+ <view
+ class="ml-[20rpx] h-[70rpx] flex items-center justify-center rounded-[10rpx] bg-[#1677ff] px-[20rpx] text-[24rpx] text-white"
+ @click="copyMcpAddress"
+ >
+ {{ t('agent.tools.copy') }}
+ </view>
+ </view>
+ <!-- 宸ュ叿鍒楄〃 -->
+ <view class="mt-[20rpx] flex-1 overflow-hidden">
+ <scroll-view class="max-h-[600rpx] bg-transparent" scroll-y>
+ <view
+ v-if="mcpTools && mcpTools.length === 0"
+ class="h-[400rpx] flex items-center justify-center"
+ >
+ <wd-status-tip image="content" :tip="t('agent.tools.noTools')" />
+ </view>
+ <view v-else class="p-[20rpx]">
+ <view class="flex flex-wrap">
+ <view
+ v-for="tool in mcpTools"
+ :key="tool"
+ class="mb-[20rpx] mr-[20rpx] rounded-[10rpx] bg-[#f5f7fb] p-[20rpx]"
+ >
+ {{ tool }}
+ </view>
+ </view>
+ </view>
+ </scroll-view>
+ </view>
+ </view>
+ </view>
+ </scroll-view>
+
+ <!-- 鍙傛暟缂栬緫寮圭獥 -->
+ <wd-action-sheet
+ v-model="showParamDialog"
+ :title="`${t('agent.tools.parameterConfig')} - ${currentFunction?.name || ''}`"
+ custom-header-class="h-[75vh]"
+ @close="closeParamEdit"
+ >
+ <scroll-view
+ scroll-y
+ class="bg-[#f5f7fb]"
+ :style="{ height: 'calc(75vh - 60rpx)' }"
+ >
+ <view class="p-[30rpx] pb-[40rpx]">
+ <!-- 鏃犲弬鏁版彁绀� -->
+ <view
+ v-if="
+ !currentFunction?.fieldsMeta
+ || currentFunction.fieldsMeta.length === 0
+ "
+ class="h-[400rpx] flex items-center justify-center"
+ >
+ <text class="text-[28rpx] text-[#999]">
+ {{ currentFunction?.name }} {{ t('agent.tools.noParamsNeeded') }}
+ </text>
+ </view>
+
+ <!-- 鍙傛暟琛ㄥ崟 - 鍗$墖寮忓竷灞� -->
+ <view v-else class="flex flex-col gap-[24rpx]">
+ <view
+ v-for="field in currentFunction.fieldsMeta"
+ :key="field.key"
+ class="border border-[#eeeeee] rounded-[20rpx] bg-white p-[30rpx]"
+ style="box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);"
+ >
+ <!-- 瀛楁淇℃伅 -->
+ <view class="mb-[24rpx]">
+ <text class="mb-[8rpx] block text-[32rpx] text-[#232338] font-medium">
+ {{ field.label }}
+ </text>
+ <text v-if="getFieldRemark(field)" class="block text-[24rpx] text-[#65686f] leading-[1.5]">
+ {{ getFieldRemark(field) }}
+ </text>
+ </view>
+
+ <!-- 杈撳叆鎺т欢 -->
+ <view>
+ <!-- 瀛楃涓茬被鍨� -->
+ <input
+ v-if="field.type === 'string'"
+ v-model="tempParams[field.key]"
+ class="box-border h-[80rpx] w-full border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[16rpx_20rpx] text-[28rpx] text-[#232338] focus:border-[#336cff] focus:bg-white placeholder:text-[#9d9ea3]"
+ type="text"
+ :placeholder="`${t('agent.tools.pleaseInput')}${field.label}`"
+ @input="
+ handleParamChange(field.key, $event.detail.value, field)
+ "
+ >
+
+ <!-- 鏁扮粍绫诲瀷 -->
+ <view v-else-if="field.type === 'array'">
+ <text class="mb-[16rpx] block text-[24rpx] text-[#65686f]">
+ {{ t('agent.tools.eachLineOneItem') }}
+ </text>
+ <textarea
+ v-model="arrayTextCache[field.key]"
+ class="box-border min-h-[200rpx] w-full border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] text-[26rpx] text-[#232338] leading-[1.6] focus:border-[#336cff] focus:bg-white placeholder:text-[#9d9ea3]"
+ :placeholder="`${t('agent.tools.pleaseInput')}${field.label}锛�${t('agent.tools.eachLineOneItem')}`"
+ @input="
+ handleArrayChange(field.key, $event.detail.value, field)
+ "
+ />
+ </view>
+
+ <!-- JSON绫诲瀷 -->
+ <view v-else-if="field.type === 'json'">
+ <text class="mb-[16rpx] block text-[24rpx] text-[#65686f]">
+ {{ t('agent.tools.pleaseInputValidJson') }}
+ </text>
+ <textarea
+ v-model="jsonTextCache[field.key]"
+ class="box-border min-h-[300rpx] w-full border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] text-[26rpx] text-[#232338] leading-[1.6] font-mono focus:border-[#336cff] focus:bg-white placeholder:text-[#9d9ea3]"
+ :placeholder="t('agent.tools.pleaseInputValidJson')"
+ @blur="
+ handleJsonChange(field.key, $event.detail.value, field)
+ "
+ />
+ </view>
+
+ <!-- 鏁板瓧绫诲瀷 -->
+ <input
+ v-else-if="field.type === 'number'"
+ v-model="tempParams[field.key]"
+ class="box-border h-[80rpx] w-full border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[16rpx_20rpx] text-[28rpx] text-[#232338] focus:border-[#336cff] focus:bg-white placeholder:text-[#9d9ea3]"
+ type="number"
+ :placeholder="`${t('agent.tools.pleaseInput')}${field.label}`"
+ @input="
+ handleParamChange(
+ field.key,
+ Number($event.detail.value),
+ field,
+ )
+ "
+ >
+
+ <!-- 甯冨皵绫诲瀷 -->
+ <view
+ v-else-if="field.type === 'boolean' || field.type === 'bool'"
+ class="flex items-center justify-between py-[20rpx]"
+ >
+ <view class="flex-1">
+ <text class="mb-[8rpx] block text-[28rpx] text-[#232338]">
+ {{ t('agent.tools.enableFunction') }}
+ </text>
+ <text class="block text-[24rpx] text-[#65686f]">
+ {{ t('agent.tools.toggleFunction') }}
+ </text>
+ </view>
+ <switch
+ :checked="tempParams[field.key]"
+ @change="
+ handleParamChange(field.key, $event.detail.value, field)
+ "
+ />
+ </view>
+
+ <!-- 榛樿瀛楃涓茬被鍨� -->
+ <input
+ v-else
+ v-model="tempParams[field.key]"
+ class="box-border h-[80rpx] w-full border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[16rpx_20rpx] text-[28rpx] text-[#232338] focus:border-[#336cff] focus:bg-white placeholder:text-[#9d9ea3]"
+ type="text"
+ :placeholder="`${t('agent.tools.pleaseInput')}${field.label}`"
+ @input="
+ handleParamChange(field.key, $event.detail.value, field)
+ "
+ >
+ </view>
+ </view>
+ </view>
+ </view>
+ </scroll-view>
+ </wd-action-sheet>
+ </view>
+</template>
+
+<style scoped lang="scss">
+::v-deep .wd-action-sheet__header {
+ padding-right: 30rpx;
+}
+</style>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/chat-history/detail.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/chat-history/detail.vue
new file mode 100644
index 0000000..ae2d3d4
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/chat-history/detail.vue
@@ -0,0 +1,500 @@
+<route lang="jsonc" type="page">
+{
+ "layout": "default",
+ "style": {
+ "navigationStyle": "custom",
+ "navigationBarTitleText": "鑱婂ぉ璇︽儏"
+ }
+}
+</route>
+
+<script lang="ts" setup>
+import type { ChatMessage, UserMessageContent } from '@/api/chat-history/types'
+import { onLoad, onUnload } from '@dcloudio/uni-app'
+import { computed, ref } from 'vue'
+import { getAudioId, getChatHistory } from '@/api/chat-history/chat-history'
+import { t } from '@/i18n'
+import { debounce, getEnvBaseUrl } from '@/utils'
+import { toast } from '@/utils/toast'
+
+defineOptions({
+ name: 'ChatDetail',
+})
+
+// 鑾峰彇灞忓箷杈圭晫鍒板畨鍏ㄥ尯鍩熻窛绂�
+let safeAreaInsets: any
+let systemInfo: any
+
+// #ifdef MP-WEIXIN
+systemInfo = uni.getWindowInfo()
+safeAreaInsets = systemInfo.safeArea
+ ? {
+ top: systemInfo.safeArea.top,
+ right: systemInfo.windowWidth - systemInfo.safeArea.right,
+ bottom: systemInfo.windowHeight - systemInfo.safeArea.bottom,
+ left: systemInfo.safeArea.left,
+ }
+ : null
+// #endif
+
+// #ifndef MP-WEIXIN
+systemInfo = uni.getSystemInfoSync()
+safeAreaInsets = systemInfo.safeAreaInsets
+// #endif
+
+// 椤甸潰鍙傛暟
+const sessionId = ref('')
+const agentId = ref('')
+
+// 鏅鸿兘浣撲俊鎭紙绠�鍖栵級
+const currentAgent = computed(() => {
+ return {
+ id: agentId.value,
+ agentName: t('chatHistory.assistantName'),
+ }
+})
+
+// 鑱婂ぉ鏁版嵁
+const messageList = ref<ChatMessage[]>([])
+const loading = ref(false)
+
+// 闊抽鎾斁鐩稿叧
+const audioContext = ref<UniApp.InnerAudioContext | null>(null)
+const playingAudioId = ref<string | null>(null)
+const expandedToolResults = ref({})
+
+// 杩斿洖涓婁竴椤�
+function goBack() {
+ uni.navigateBack()
+}
+
+// 鍔犺浇鑱婂ぉ璁板綍
+async function loadChatHistory() {
+ if (!sessionId.value || !agentId.value) {
+ console.error('缂哄皯蹇呰鍙傛暟')
+ return
+ }
+
+ try {
+ loading.value = true
+ const response = await getChatHistory(agentId.value, sessionId.value)
+ messageList.value = response
+ }
+ catch (error) {
+ console.error('鑾峰彇鑱婂ぉ璁板綍澶辫触:', error)
+ toast.error(t('chatHistory.loadFailed'))
+ }
+ finally {
+ loading.value = false
+ }
+}
+
+// 瑙f瀽鐢ㄦ埛娑堟伅鍐呭
+function parseUserMessage(content: string): UserMessageContent | null {
+ try {
+ return JSON.parse(content)
+ }
+ catch {
+ return null
+ }
+}
+
+// 鑾峰彇娑堟伅鏄剧ず鍐呭
+function getMessageContent(message: ChatMessage): string {
+ if (message.chatType === 1) {
+ // 鐢ㄦ埛娑堟伅锛岄渶瑕佽В鏋怞SON
+ const parsed = parseUserMessage(message.content)
+ return parsed ? parsed.content : message.content
+ }
+ else {
+ // AI娑堟伅锛岀洿鎺ユ樉绀�
+ return message.content
+ }
+}
+
+// 鑾峰彇璇磋瘽浜哄悕绉�
+function getSpeakerName(message: ChatMessage): string {
+ if (message.chatType === 1) {
+ const parsed = parseUserMessage(message.content)
+ return parsed ? parsed.speaker : t('chatHistory.userName')
+ }
+ else {
+ return currentAgent.value?.agentName || t('chatHistory.aiAssistantName')
+ }
+}
+
+// 鏍煎紡鍖栨椂闂�
+function formatTime(timeStr: string) {
+ if (!timeStr)
+ return t('chatHistory.unknownTime')
+
+ // 澶勭悊鏃堕棿瀛楃涓诧紝纭繚鏍煎紡姝g‘
+ const date = new Date(timeStr.replace(' ', 'T')) // 杞崲涓篒SO鏍煎紡
+ const now = new Date()
+
+ // 妫�鏌ユ棩鏈熸槸鍚︽湁鏁�
+ if (Number.isNaN(date.getTime())) {
+ return timeStr // 濡傛灉瑙f瀽澶辫触锛岀洿鎺ヨ繑鍥炲師瀛楃涓�
+ }
+
+ const diff = now.getTime() - date.getTime()
+
+ // 灏忎簬1鍒嗛挓
+ if (diff < 60000)
+ return t('chatHistory.justNow')
+
+ // 灏忎簬1灏忔椂
+ if (diff < 3600000)
+ return t('chatHistory.minutesAgo', { minutes: Math.floor(diff / 60000) })
+
+ // 灏忎簬1澶╋紙24灏忔椂锛�
+ if (diff < 86400000)
+ return t('chatHistory.hoursAgo', { hours: Math.floor(diff / 3600000) })
+
+ // 灏忎簬7澶�
+ if (diff < 604800000) {
+ const days = Math.floor(diff / 86400000)
+ return t('chatHistory.daysAgo', { days })
+ }
+
+ // 瓒呰繃7澶╋紝鏄剧ず鍏蜂綋鏃ユ湡
+ const year = date.getFullYear()
+ const month = String(date.getMonth() + 1).padStart(2, '0')
+ const day = String(date.getDate()).padStart(2, '0')
+ const currentYear = now.getFullYear()
+
+ // 濡傛灉鏄綋鍓嶅勾浠斤紝涓嶆樉绀哄勾浠�
+ if (year === currentYear) {
+ return `${month}-${day}`
+ }
+
+ return `${year}-${month}-${day}`
+}
+
+// 鎾斁闊抽
+const playAudio = debounce(async (audioId: string) => {
+ if (!audioId) {
+ toast.error(t('chatHistory.invalidAudioId'))
+ return
+ }
+
+ try {
+ // 濡傛灉姝e湪鎾斁鍏朵粬闊抽锛屽厛鍋滄
+ if (audioContext.value) {
+ audioContext.value.stop()
+ }
+ // 濡傛灉褰撳墠闊抽ID涓庤姹侷D鐩稿悓鏆傚仠鎾斁
+ if (playingAudioId.value === audioId) {
+ playingAudioId.value = null
+ return
+ }
+
+ // 鑾峰彇闊抽涓嬭浇ID
+ const downloadId = await getAudioId(audioId)
+
+ // 鏋勯�犻煶棰戞挱鏀惧湴鍧�
+ const baseUrl = getEnvBaseUrl()
+ const audioUrl = `${baseUrl}/agent/play/${downloadId}`
+
+ // 鍒涘缓闊抽涓婁笅鏂�
+ if (!audioContext.value) {
+ audioContext.value = uni.createInnerAudioContext()
+ }
+ audioContext.value.src = audioUrl
+
+ // 璁剧疆鎾斁鐘舵��
+ playingAudioId.value = audioId
+
+ // 鐩戝惉鎾斁瀹屾垚
+ audioContext.value.onEnded(() => {
+ playingAudioId.value = null
+ if (audioContext.value) {
+ audioContext.value.destroy()
+ audioContext.value = null
+ }
+ })
+
+ // 鐩戝惉鎾斁閿欒
+ audioContext.value.onError((error) => {
+ console.error('闊抽鎾斁澶辫触:', error)
+ toast.error(t('chatHistory.audioPlayFailed'))
+ playingAudioId.value = null
+ if (audioContext.value) {
+ audioContext.value.destroy()
+ audioContext.value = null
+ }
+ })
+
+ // 寮�濮嬫挱鏀�
+ audioContext.value.play()
+ }
+ catch (error) {
+ console.error('鎾斁闊抽澶辫触:', error)
+ toast.error(t('chatHistory.playAudioFailed'))
+ playingAudioId.value = null
+ }
+}, 400)
+
+function extractContentFromString(content: string) {
+ if (!content || content.trim() === '') {
+ return content
+ }
+
+ // 灏濊瘯瑙f瀽涓� JSON
+ try {
+ const jsonObj = JSON.parse(content)
+
+ // 濡傛灉鏄暟缁勬牸寮忥紙鍖呭惈 text 鍜� tool锛�
+ if (Array.isArray(jsonObj)) {
+ return jsonObj
+ }
+
+ // 濡傛灉鏄璞′笖鏈� content 瀛楁
+ if (jsonObj && typeof jsonObj === 'object' && jsonObj.content) {
+ return jsonObj.content
+ }
+ }
+ catch (e) {
+ // 濡傛灉涓嶆槸鏈夋晥鐨� JSON锛岀洿鎺ヨ繑鍥炲師鍐呭
+ }
+
+ // 濡傛灉涓嶆槸 JSON 鏍煎紡鎴栨病鏈� content 瀛楁锛岀洿鎺ヨ繑鍥炲師鍐呭
+ return content
+}
+
+function toggleToolResult(messageIndex, itemIndex) {
+ const key = `${messageIndex}-${itemIndex}`
+ expandedToolResults.value[key] = !expandedToolResults.value[key]
+}
+
+function isToolResultCollapsed(messageIndex, itemIndex) {
+ const key = `${messageIndex}-${itemIndex}`
+ // 榛樿鎶樺彔锛坱rue琛ㄧず鎶樺彔锛�
+ return !expandedToolResults.value[key]
+}
+
+function getFirstLineText(text: string) {
+ if (!text) {
+ return ''
+ }
+ const firstLine = text.split('\n')[0]
+ return firstLine.length < text.length ? `${firstLine}...` : text
+}
+
+onLoad((options) => {
+ if (options?.sessionId && options?.agentId) {
+ sessionId.value = options.sessionId
+ agentId.value = options.agentId
+ }
+ else {
+ console.error('缂哄皯蹇呰鍙傛暟')
+ toast.error(t('chatHistory.parameterError'))
+ }
+})
+
+onShow(() => {
+ loadChatHistory()
+})
+
+// 椤甸潰閿�姣佹椂娓呯悊闊抽璧勬簮
+onUnload(() => {
+ if (audioContext.value) {
+ audioContext.value.stop()
+ audioContext.value.destroy()
+ audioContext.value = null
+ }
+})
+</script>
+
+<template>
+ <view class="h-screen flex flex-col bg-[#f5f7fb]">
+ <!-- 鐘舵�佹爮鑳屾櫙 -->
+ <view class="w-full bg-white" :style="{ height: `${safeAreaInsets?.top}px` }" />
+
+ <!-- 瀵艰埅鏍� -->
+ <wd-navbar :title="t('chatHistory.pageTitle')">
+ <template #left>
+ <wd-icon name="arrow-left" size="18" @click="goBack" />
+ </template>
+ </wd-navbar>
+
+ <!-- 鑱婂ぉ娑堟伅鍒楄〃 -->
+ <scroll-view
+ scroll-y
+ :style="{ height: `calc(100vh - ${safeAreaInsets?.top || 0}px - 120rpx)` }"
+ class="box-border flex-1 bg-[#f5f7fb] p-[20rpx]"
+ :scroll-into-view="`message-${messageList.length - 1}`"
+ >
+ <view v-if="loading" class="flex flex-col items-center justify-center gap-[20rpx] p-[100rpx_0]">
+ <wd-loading />
+ <text class="text-[28rpx] text-[#65686f]">
+ {{ t('chatHistory.loading') }}
+ </text>
+ </view>
+
+ <view v-else class="flex flex-col gap-[20rpx]">
+ <view
+ v-for="(message, index) in messageList"
+ :id="`message-${index}`"
+ :key="index"
+ class="w-full flex"
+ :class="{
+ 'justify-end': message.chatType === 1,
+ 'justify-start': message.chatType === 2,
+ }"
+ >
+ <view
+ class="max-w-[80%] flex flex-col gap-[8rpx]"
+ :class="{
+ 'items-end': message.chatType === 1,
+ 'items-start': message.chatType === 2,
+ 'tool-message': message.chatType === 3,
+ }"
+ >
+ <!-- 娑堟伅姘旀场 -->
+ <view
+ class="shadow-message break-words rounded-[20rpx] p-[24rpx] leading-[1.4]"
+ :class="{
+ 'bg-[#336cff] text-white': message.chatType === 1,
+ 'bg-white text-[#232338] border border-[#eeeeee]': [2, 3].includes(message.chatType),
+ }"
+ >
+ <template v-if="Array.isArray(extractContentFromString(message.content))">
+ <div class="content-wrapper">
+ <div v-for="(item, idx) in extractContentFromString(message.content)" :key="idx">
+ <div v-if="item.type === 'text'" class="text-content">
+ {{ item.text }}
+ </div>
+ <div v-else-if="item.type === 'tool'" class="tool-call-text">
+ {{ item.text }}
+ </div>
+ <div v-else-if="item.type === 'tool_result'" class="tool-call-text">
+ <div v-if="item.text && item.text.length > 80" class="tool-result-wrapper">
+ <div v-if="isToolResultCollapsed(index, idx)" class="tool-result-collapsed">
+ {{ getFirstLineText(item.text) }}
+ </div>
+ <div v-else class="tool-result-expanded">
+ {{ item.text }}
+ </div>
+ <span class="tool-toggle-btn" @click="toggleToolResult(index, idx)">
+ <wd-icon :name="isToolResultCollapsed(index, idx) ? 'arrow-down' : 'arrow-up'" size="12" />
+ </span>
+ </div>
+ <div v-else>
+ {{ item.text }}
+ </div>
+ </div>
+ </div>
+ </div>
+ </template>
+ <!-- 鍐呭鍖哄煙 - 浣跨敤flex甯冨眬璁╁浘鏍囧拰鏂囨湰瀵归綈 -->
+ <view v-else class="flex items-center gap-[12rpx]">
+ <!-- 闊抽鎾斁鍥炬爣 -->
+ <view
+ v-if="message.audioId"
+ class="flex-shrink-0 cursor-pointer transition-transform duration-200 active:scale-90"
+ :class="{
+ 'text-white animate-pulse-audio': message.chatType === 1 && playingAudioId === message.audioId,
+ 'text-[#ffd700]': message.chatType === 1 && playingAudioId === message.audioId && playingAudioId,
+ 'text-[#336cff] animate-pulse-audio': message.chatType === 2 && playingAudioId === message.audioId,
+ 'text-[#ff6b35]': message.chatType === 2 && playingAudioId === message.audioId && playingAudioId,
+ 'text-white': message.chatType === 1 && playingAudioId !== message.audioId,
+ 'text-[#336cff]': message.chatType === 2 && playingAudioId !== message.audioId,
+ }"
+ @click="playAudio(message.audioId)"
+ >
+ <wd-icon
+ :name="playingAudioId === message.audioId ? 'pause-circle-filled' : 'play-circle-filled'"
+ size="20"
+ />
+ </view>
+
+ <!-- 娑堟伅鍐呭瀹瑰櫒 -->
+ <view class="min-w-0 flex-1">
+ <!-- 娑堟伅鍐呭 -->
+ <text class="block text-[28rpx]">
+ {{ getMessageContent(message) }}
+ </text>
+ </view>
+ </view>
+ </view>
+
+ <!-- 璇磋瘽浜轰俊鎭� -->
+ <text
+ class="mx-[12rpx] text-[22rpx] text-[#9d9ea3]"
+ :class="{
+ 'text-right': message.chatType === 1,
+ 'text-left': message.chatType === 2,
+ }"
+ >
+ {{ formatTime(message.createdAt) }}
+ </text>
+ </view>
+ </view>
+ </view>
+ </scroll-view>
+ </view>
+</template>
+
+<style>
+/* 鑷畾涔夐槾褰卞拰鍔ㄧ敾鏁堟灉锛屾棤娉曠敤UnoCSS琛ㄧず鐨勬牱寮� */
+.shadow-message {
+ box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
+}
+
+@keyframes pulse-audio {
+ 0%,
+ 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0.6;
+ }
+}
+
+.animate-pulse-audio {
+ animation: pulse-audio 1.5s infinite;
+}
+
+.text-content {
+ display: block;
+ margin-bottom: 8rpx;
+}
+
+.tool-call-text {
+ color: #1890ff;
+ font-family: 'Courier New', monospace;
+ font-weight: 500;
+ font-size: 24rpx;
+ display: block;
+ margin-top: 8rpx;
+}
+
+.user-message .tool-call-text {
+ color: #e6f7ff;
+}
+
+.tool-message .message-content {
+ background-color: #f0f0f0;
+}
+
+.tool-result-wrapper {
+ position: relative;
+ padding-right: 40rpx;
+}
+
+.tool-result-collapsed {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.tool-toggle-btn {
+ position: absolute;
+ right: 0;
+ top: 0;
+ cursor: pointer;
+ color: #1890ff;
+ font-size: 24rpx;
+}
+</style>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/chat-history/index.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/chat-history/index.vue
new file mode 100644
index 0000000..b4cf6d9
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/chat-history/index.vue
@@ -0,0 +1,410 @@
+<script lang="ts" setup>
+import type { ChatSession } from '@/api/chat-history/types'
+import { computed, onMounted, ref } from 'vue'
+import { getChatSessions } from '@/api/chat-history/chat-history'
+import { t } from '@/i18n'
+import { deepClone } from '@/utils'
+
+defineOptions({
+ name: 'ChatHistory',
+})
+
+const props = withDefaults(defineProps<Props>(), {
+ agentId: 'default',
+})
+
+// 鎺ユ敹props
+interface Props {
+ agentId?: string
+}
+
+// 鑾峰彇灞忓箷杈圭晫鍒板畨鍏ㄥ尯鍩熻窛绂�
+let safeAreaInsets: any
+let systemInfo: any
+
+// #ifdef MP-WEIXIN
+systemInfo = uni.getWindowInfo()
+safeAreaInsets = systemInfo.safeArea
+ ? {
+ top: systemInfo.safeArea.top,
+ right: systemInfo.windowWidth - systemInfo.safeArea.right,
+ bottom: systemInfo.windowHeight - systemInfo.safeArea.bottom,
+ left: systemInfo.safeArea.left,
+ }
+ : null
+// #endif
+
+// #ifndef MP-WEIXIN
+systemInfo = uni.getSystemInfoSync()
+safeAreaInsets = systemInfo.safeAreaInsets
+// #endif
+
+// 鑱婂ぉ浼氳瘽鏁版嵁
+const sessionList = ref<ChatSession[]>([])
+const loading = ref(false)
+const loadingMore = ref(false)
+const hasMore = ref(true)
+const currentPage = ref(0)
+const pageSize = 10
+
+// 浣跨敤浼犲叆鐨勬櫤鑳戒綋ID
+const currentAgentId = computed(() => {
+ return props.agentId
+})
+
+// 鍔犺浇鑱婂ぉ浼氳瘽鍒楄〃
+async function loadChatSessions(page = 1, isUpdate = false) {
+ try {
+ // 妫�鏌ユ槸鍚︽湁褰撳墠閫変腑鐨勬櫤鑳戒綋
+ if (!currentAgentId.value) {
+ console.warn(t('chatHistory.noSelectedAgent'))
+ sessionList.value = []
+ return
+ }
+
+ if (page === 1) {
+ loading.value = true
+ }
+ else {
+ loadingMore.value = true
+ }
+
+ const response = await getChatSessions(currentAgentId.value, {
+ page,
+ limit: pageSize,
+ })
+
+ if (page === 1) {
+ const oldSessionList = deepClone(sessionList.value)
+ oldSessionList.splice(0, 10)
+ oldSessionList.unshift(...(response.list || []))
+ sessionList.value = isUpdate ? oldSessionList : response.list || []
+ }
+ else {
+ sessionList.value.push(...(response.list || []))
+ }
+
+ // 鏇存柊鍒嗛〉淇℃伅
+ hasMore.value = (response.list?.length || 0) === pageSize
+ currentPage.value = page
+ }
+ catch (error) {
+ console.error(t('chatHistory.getChatSessionsFailed'), error)
+ if (page === 1) {
+ sessionList.value = []
+ }
+ }
+ finally {
+ loading.value = false
+ loadingMore.value = false
+ }
+}
+
+// 鏆撮湶缁欑埗缁勪欢鐨勫埛鏂版柟娉�
+async function refresh() {
+ currentPage.value = 1
+ hasMore.value = true
+ await loadChatSessions(1, true)
+}
+
+// 鏆撮湶缁欑埗缁勪欢鐨勫姞杞芥洿澶氭柟娉�
+async function loadMore() {
+ if (!hasMore.value || loadingMore.value) {
+ return
+ }
+ await loadChatSessions(currentPage.value + 1)
+}
+
+// 鏍煎紡鍖栨椂闂�
+function formatTime(timeStr: string) {
+ if (!timeStr)
+ return t('chatHistory.unknownTime')
+
+ // 澶勭悊鏃堕棿瀛楃涓诧紝纭繚鏍煎紡姝g‘
+ const date = new Date(timeStr.replace(' ', 'T')) // 杞崲涓篒SO鏍煎紡
+ const now = new Date()
+
+ // 妫�鏌ユ棩鏈熸槸鍚︽湁鏁�
+ if (Number.isNaN(date.getTime())) {
+ return timeStr // 濡傛灉瑙f瀽澶辫触锛岀洿鎺ヨ繑鍥炲師瀛楃涓�
+ }
+
+ const diff = now.getTime() - date.getTime()
+
+ // 灏忎簬1鍒嗛挓
+ if (diff < 60000)
+ return t('chatHistory.justNow')
+
+ // 灏忎簬1灏忔椂
+ if (diff < 3600000)
+ return t('chatHistory.minutesAgo', { minutes: Math.floor(diff / 60000) })
+
+ // 灏忎簬1澶╋紙24灏忔椂锛�
+ if (diff < 86400000)
+ return t('chatHistory.hoursAgo', { hours: Math.floor(diff / 3600000) })
+
+ // 灏忎簬7澶�
+ if (diff < 604800000) {
+ const days = Math.floor(diff / 86400000)
+ return t('chatHistory.daysAgo', { days })
+ }
+
+ // 瓒呰繃7澶╋紝鏄剧ず鍏蜂綋鏃ユ湡
+ const year = date.getFullYear()
+ const month = String(date.getMonth() + 1).padStart(2, '0')
+ const day = String(date.getDate()).padStart(2, '0')
+ const currentYear = now.getFullYear()
+
+ // 濡傛灉鏄綋鍓嶅勾浠斤紝涓嶆樉绀哄勾浠�
+ if (year === currentYear) {
+ return `${month}-${day}`
+ }
+
+ return `${year}-${month}-${day}`
+}
+
+// 杩涘叆鑱婂ぉ璇︽儏
+function goToChatDetail(session: ChatSession) {
+ uni.navigateTo({
+ url: `/pages/chat-history/detail?sessionId=${session.sessionId}&agentId=${currentAgentId.value}`,
+ })
+}
+
+onMounted(async () => {
+ // 鏅鸿兘浣撳凡绠�鍖栦负榛樿
+ loadChatSessions(1)
+})
+
+onShow(() => {
+ if (currentPage.value !== 0) {
+ loadChatSessions(1, true)
+ }
+})
+
+// 鏆撮湶鏂规硶缁欑埗缁勪欢
+defineExpose({
+ refresh,
+ loadMore,
+})
+</script>
+
+<template>
+ <view class="chat-history-container" style="background: #f5f7fb; min-height: 100%;">
+ <!-- 鍔犺浇鐘舵�� -->
+ <view v-if="loading && sessionList.length === 0" class="loading-container">
+ <wd-loading color="#336cff" />
+ <text class="loading-text">
+ {{ t('chatHistory.loading') }}
+ </text>
+ </view>
+
+ <!-- 浼氳瘽鍒楄〃 -->
+ <view v-else-if="sessionList.length > 0" class="session-container">
+ <!-- 鑱婂ぉ浼氳瘽鍒楄〃 -->
+ <view class="session-list">
+ <view
+ v-for="session in sessionList"
+ :key="session.sessionId"
+ class="session-item"
+ @click="goToChatDetail(session)"
+ >
+ <view class="session-card">
+ <view class="session-info">
+ <view class="session-header">
+ <text class="session-title">
+ {{ session.title || `${t('chatHistory.conversationRecord')} ${session.sessionId.substring(0, 8)}...` }}
+ </text>
+ <text class="session-time">
+ {{ formatTime(session.createdAt) }}
+ </text>
+ </view>
+ <view class="session-meta">
+ <text class="chat-count">
+ {{ t('chatHistory.totalChats', { count: session.chatCount }) }}
+ </text>
+ </view>
+ </view>
+ <wd-icon name="arrow-right" custom-class="arrow-icon" />
+ </view>
+ </view>
+ </view>
+
+ <!-- 鍔犺浇鏇村鐘舵�� -->
+ <view v-if="loadingMore" class="loading-more">
+ <wd-loading color="#336cff" size="24" />
+ <text class="loading-more-text">
+ {{ t('chatHistory.loading') }}
+ </text>
+ </view>
+
+ <!-- 娌℃湁鏇村鏁版嵁 -->
+ <view v-else-if="!hasMore && sessionList.length > 0" class="no-more">
+ <text class="no-more-text">
+ {{ t('chatHistory.noMoreData') }}
+ </text>
+ </view>
+ </view>
+
+ <!-- 绌虹姸鎬� -->
+ <view v-else-if="!loading" class="empty-state">
+ <wd-icon name="chat" custom-class="empty-icon" />
+ <text class="empty-text">
+ {{ t('chatHistory.noChatRecords') }}
+ </text>
+ <text class="empty-desc">
+ {{ t('chatHistory.chatRecordsDescription') }}
+ </text>
+ </view>
+ </view>
+</template>
+
+<style lang="scss" scoped>
+.chat-history-container {
+ position: relative;
+}
+
+.loading-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 100rpx 40rpx;
+}
+
+.loading-text {
+ margin-top: 20rpx;
+ font-size: 28rpx;
+ color: #666666;
+}
+
+.loading-more {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 30rpx;
+ gap: 16rpx;
+
+ .loading-more-text {
+ font-size: 26rpx;
+ color: #666666;
+ }
+}
+
+.no-more {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 30rpx;
+
+ .no-more-text {
+ font-size: 26rpx;
+ color: #999999;
+ }
+}
+
+.navbar-section {
+ background: #ffffff;
+}
+
+.status-bar {
+ background: #ffffff;
+ width: 100%;
+}
+
+.session-list {
+ display: flex;
+ flex-direction: column;
+ gap: 24rpx;
+ padding: 20rpx;
+ box-sizing: border-box;
+}
+
+.session-item {
+ background: #fbfbfb;
+ border-radius: 20rpx;
+ box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
+ border: 1rpx solid #eeeeee;
+ overflow: hidden;
+ cursor: pointer;
+ transition: all 0.2s ease;
+
+ &:active {
+ background: #f8f9fa;
+ }
+}
+
+.session-card {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 32rpx;
+
+ .session-info {
+ width: 94%;
+
+ .session-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 12rpx;
+
+ .session-title {
+ font-size: 32rpx;
+ font-weight: 600;
+ color: #232338;
+ width: 70%;
+ word-break: break-all;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ .session-time {
+ font-size: 24rpx;
+ color: #9d9ea3;
+ }
+ }
+
+ .session-meta {
+ .chat-count {
+ font-size: 28rpx;
+ color: #65686f;
+ }
+ }
+ }
+
+ :deep(.arrow-icon) {
+ font-size: 24rpx;
+ color: #c7c7cc;
+ margin-left: 16rpx;
+ }
+}
+
+.empty-state {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 100rpx 40rpx;
+ text-align: center;
+
+ :deep(.empty-icon) {
+ font-size: 120rpx;
+ color: #d9d9d9;
+ margin-bottom: 32rpx;
+ }
+
+ .empty-text {
+ font-size: 32rpx;
+ color: #666666;
+ margin-bottom: 16rpx;
+ font-weight: 500;
+ }
+
+ .empty-desc {
+ font-size: 26rpx;
+ color: #999999;
+ line-height: 1.5;
+ }
+}
+</style>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/device-config/components/ultrasonic-config.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/device-config/components/ultrasonic-config.vue
new file mode 100644
index 0000000..ef5f4f7
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/device-config/components/ultrasonic-config.vue
@@ -0,0 +1,685 @@
+<script setup lang="ts">
+import { computed, ref } from 'vue'
+import { useToast } from 'wot-design-uni/components/wd-toast'
+import { t } from '@/i18n'
+
+// 绫诲瀷瀹氫箟
+interface WiFiNetwork {
+ ssid: string
+ rssi: number
+ authmode: number
+ channel: number
+}
+
+// Props
+interface Props {
+ selectedNetwork: WiFiNetwork | null
+ password: string
+}
+
+const props = defineProps<Props>()
+
+// Toast 瀹炰緥
+const toast = useToast()
+
+// 鍝嶅簲寮忔暟鎹�
+const generating = ref(false)
+const playing = ref(false)
+const audioGenerated = ref(false)
+const autoLoop = ref(true)
+const audioFilePath = ref('')
+const audioContext = ref<any>(null)
+
+// AFSK璋冨埗鍙傛暟 - 鍙傝�僅TML鏂囦欢
+const MARK = 1800 // 浜岃繘鍒�1鐨勯鐜� (Hz)
+const SPACE = 1500 // 浜岃繘鍒�0鐨勯鐜� (Hz)
+const SAMPLE_RATE = 44100 // 閲囨牱鐜�
+const BIT_RATE = 100 // 姣旂壒鐜� (bps)
+const START_BYTES = [0x01, 0x02] // 璧峰鏍囪
+const END_BYTES = [0x03, 0x04] // 缁撴潫鏍囪
+
+// 璁$畻灞炴��
+const canGenerate = computed(() => {
+ if (!props.selectedNetwork)
+ return false
+ if (props.selectedNetwork.authmode > 0 && !props.password)
+ return false
+ return true
+})
+
+const audioLengthText = computed(() => {
+ if (!props.selectedNetwork)
+ return '0绉�'
+ const dataStr = `${props.selectedNetwork.ssid}\n${props.password}`
+ const textBytes = stringToBytes(dataStr)
+ const totalBits = (START_BYTES.length + textBytes.length + 1 + END_BYTES.length) * 8
+ const duration = Math.ceil(totalBits / BIT_RATE)
+ return `${t('deviceConfig.about')}${duration}${t('deviceConfig.seconds')}`
+})
+
+// 瀛楃涓茶浆瀛楄妭鏁扮粍 - uniapp鍏煎鐗堟湰
+function stringToBytes(str: string): number[] {
+ const bytes: number[] = []
+ for (let i = 0; i < str.length; i++) {
+ const code = str.charCodeAt(i)
+ if (code < 0x80) {
+ bytes.push(code)
+ }
+ else if (code < 0x800) {
+ bytes.push(0xC0 | (code >> 6))
+ bytes.push(0x80 | (code & 0x3F))
+ }
+ else if (code < 0xD800 || code >= 0xE000) {
+ bytes.push(0xE0 | (code >> 12))
+ bytes.push(0x80 | ((code >> 6) & 0x3F))
+ bytes.push(0x80 | (code & 0x3F))
+ }
+ else {
+ // 浠g悊瀵瑰鐞�
+ i++
+ const hi = code
+ const lo = str.charCodeAt(i)
+ const codePoint = 0x10000 + (((hi & 0x3FF) << 10) | (lo & 0x3FF))
+ bytes.push(0xF0 | (codePoint >> 18))
+ bytes.push(0x80 | ((codePoint >> 12) & 0x3F))
+ bytes.push(0x80 | ((codePoint >> 6) & 0x3F))
+ bytes.push(0x80 | (codePoint & 0x3F))
+ }
+ }
+ return bytes
+}
+
+// 鏍¢獙鍜岃绠� - 鍙傝�僅TML鏂囦欢
+function checksum(data: number[]): number {
+ return data.reduce((sum, b) => (sum + b) & 0xFF, 0)
+}
+
+// 瀛楄妭杞瘮鐗逛綅 - 鍙傝�僅TML鏂囦欢
+function toBits(byte: number): number[] {
+ const bits: number[] = []
+ for (let i = 7; i >= 0; i--) {
+ bits.push((byte >> i) & 1)
+ }
+ return bits
+}
+
+// AFSK璋冨埗 - 鍙傝�僅TML鏂囦欢绠楁硶
+function afskModulate(bits: number[]): Float32Array {
+ const samplesPerBit = SAMPLE_RATE / BIT_RATE
+ const totalSamples = Math.floor(bits.length * samplesPerBit)
+ const buffer = new Float32Array(totalSamples)
+
+ for (let i = 0; i < bits.length; i++) {
+ const freq = bits[i] ? MARK : SPACE
+ for (let j = 0; j < samplesPerBit; j++) {
+ const t = (i * samplesPerBit + j) / SAMPLE_RATE
+ buffer[i * samplesPerBit + j] = Math.sin(2 * Math.PI * freq * t)
+ }
+ }
+
+ return buffer
+}
+
+// 娴偣杞�16浣峆CM - 鍙傝�僅TML鏂囦欢
+function floatTo16BitPCM(floatSamples: Float32Array): Uint8Array {
+ const buffer = new Uint8Array(floatSamples.length * 2)
+ for (let i = 0; i < floatSamples.length; i++) {
+ const s = Math.max(-1, Math.min(1, floatSamples[i]))
+ const val = s < 0 ? s * 0x8000 : s * 0x7FFF
+ buffer[i * 2] = val & 0xFF
+ buffer[i * 2 + 1] = (val >> 8) & 0xFF
+ }
+ return buffer
+}
+
+// base64缂栫爜琛�
+const base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+
+// 鍏煎鐨刡ase64缂栫爜瀹炵幇
+function base64Encode(bytes: Uint8Array): string {
+ let result = ''
+ let i = 0
+
+ while (i < bytes.length) {
+ const a = bytes[i++]
+ const b = i < bytes.length ? bytes[i++] : 0
+ const c = i < bytes.length ? bytes[i++] : 0
+
+ const bitmap = (a << 16) | (b << 8) | c
+
+ result += base64Chars.charAt((bitmap >> 18) & 63)
+ result += base64Chars.charAt((bitmap >> 12) & 63)
+ result += i - 2 < bytes.length ? base64Chars.charAt((bitmap >> 6) & 63) : '='
+ result += i - 1 < bytes.length ? base64Chars.charAt(bitmap & 63) : '='
+ }
+
+ return result
+}
+
+// 鏁扮粍杞琤ase64缂栫爜 - 鍏煎鐗堟湰
+function arrayBufferToBase64(buffer: ArrayBuffer): string {
+ const bytes = new Uint8Array(buffer)
+
+ // 灏濊瘯浣跨敤鍘熺敓btoa锛屽鏋滀笉瀛樺湪鍒欎娇鐢ㄨ嚜瀹氫箟瀹炵幇
+ if (typeof btoa !== 'undefined') {
+ let binary = ''
+ for (let i = 0; i < bytes.byteLength; i++) {
+ binary += String.fromCharCode(bytes[i])
+ }
+ return btoa(binary)
+ }
+ else {
+ return base64Encode(bytes)
+ }
+}
+
+// 鏋勫缓WAV鏂囦欢 - 杩斿洖ArrayBuffer鑰屼笉鏄疊lob
+function buildWav(pcm: Uint8Array): ArrayBuffer {
+ const wavHeader = new Uint8Array(44)
+ const dataLen = pcm.length
+ const fileLen = 36 + dataLen
+
+ const writeStr = (offset: number, str: string) => {
+ for (let i = 0; i < str.length; i++) {
+ wavHeader[offset + i] = str.charCodeAt(i)
+ }
+ }
+
+ const write32 = (offset: number, value: number) => {
+ wavHeader[offset] = value & 0xFF
+ wavHeader[offset + 1] = (value >> 8) & 0xFF
+ wavHeader[offset + 2] = (value >> 16) & 0xFF
+ wavHeader[offset + 3] = (value >> 24) & 0xFF
+ }
+
+ const write16 = (offset: number, value: number) => {
+ wavHeader[offset] = value & 0xFF
+ wavHeader[offset + 1] = (value >> 8) & 0xFF
+ }
+
+ writeStr(0, 'RIFF')
+ write32(4, fileLen)
+ writeStr(8, 'WAVE')
+ writeStr(12, 'fmt ')
+ write32(16, 16)
+ write16(20, 1)
+ write16(22, 1)
+ write32(24, SAMPLE_RATE)
+ write32(28, SAMPLE_RATE * 2)
+ write16(32, 2)
+ write16(34, 16)
+ writeStr(36, 'data')
+ write32(40, dataLen)
+
+ // 鍚堝苟header鍜屾暟鎹�
+ const result = new ArrayBuffer(44 + dataLen)
+ const resultView = new Uint8Array(result)
+ resultView.set(wavHeader)
+ resultView.set(pcm, 44)
+
+ return result
+}
+
+// 鐢熸垚骞舵挱鏀惧0娉� - 涓昏鍔熻兘鍑芥暟
+async function generateAndPlay() {
+ if (!canGenerate.value || !props.selectedNetwork)
+ return
+
+ generating.value = true
+
+ try {
+ console.log(`${t('deviceConfig.generatingUltrasonicConfigAudio')}...`)
+
+ // 鍑嗗閰嶇綉鏁版嵁 - 鍙傝�僅TML鏂囦欢鏍煎紡
+ const dataStr = `${props.selectedNetwork.ssid}\n${props.password}`
+ const textBytes = stringToBytes(dataStr)
+ const fullBytes = [...START_BYTES, ...textBytes, checksum(textBytes), ...END_BYTES]
+
+ console.log(`${t('deviceConfig.configData')}:`, { ssid: props.selectedNetwork.ssid, password: props.password })
+ console.log(`${t('deviceConfig.dataBytesLength')}:`, textBytes.length)
+
+ // 杞崲涓烘瘮鐗规祦
+ let bits: number[] = []
+ fullBytes.forEach((b) => {
+ bits = bits.concat(toBits(b))
+ })
+
+ console.log(`${t('deviceConfig.bitStreamLength')}:`, bits.length)
+
+ // AFSK璋冨埗 - 鍑忓皯閲囨牱鐜囬檷浣庢枃浠跺ぇ灏�
+ const reducedSampleRate = 22050 // 闄嶄綆閲囨牱鐜�
+ const samplesPerBit = reducedSampleRate / BIT_RATE
+ const totalSamples = Math.floor(bits.length * samplesPerBit)
+ const floatBuf = new Float32Array(totalSamples)
+
+ for (let i = 0; i < bits.length; i++) {
+ const freq = bits[i] ? MARK : SPACE
+ for (let j = 0; j < samplesPerBit; j++) {
+ const t = (i * samplesPerBit + j) / reducedSampleRate
+ floatBuf[i * samplesPerBit + j] = Math.sin(2 * Math.PI * freq * t) * 0.5 // 闄嶄綆闊抽噺
+ }
+ }
+
+ const pcmBuf = floatTo16BitPCM(floatBuf)
+
+ // 鐢熸垚WAV鏂囦欢 - 浣跨敤闄嶄綆鐨勯噰鏍风巼
+ const wavBuffer = buildWavOptimized(pcmBuf, reducedSampleRate)
+ const base64 = arrayBufferToBase64(wavBuffer)
+ const dataUri = `data:audio/wav;base64,${base64}`
+
+ console.log(`${t('deviceConfig.base64Length')}:`, base64.length, t('deviceConfig.about'), Math.round(base64.length / 1024), 'KB')
+
+ // 妫�鏌ユ暟鎹ぇ灏�
+ if (base64.length > 1024 * 1024) { // 瓒呰繃1MB
+ throw new Error(t('deviceConfig.audioFileTooLarge'))
+ }
+
+ audioFilePath.value = dataUri
+ audioGenerated.value = true
+
+ console.log(`${t('deviceConfig.audioGenerationSuccess')}锛屾瘮鐗规祦闀垮害:`, bits.length, `${t('deviceConfig.samplePoints')}:`, floatBuf.length)
+
+ toast.success(t('deviceConfig.soundWaveGenerationSuccess'))
+
+ // 寤惰繜鎾斁
+ setTimeout(async () => {
+ await playAudio()
+ }, 800) // 澧炲姞寤惰繜鏃堕棿
+ }
+ catch (error) {
+ console.error(`${t('deviceConfig.audioGenerationFailed')}:`, error)
+ toast.error(`${t('deviceConfig.soundWaveGenerationFailed')}: ${error.message || error}`)
+ }
+ finally {
+ generating.value = false
+ }
+}
+
+// 浼樺寲鐨刉AV鏋勫缓鍑芥暟
+function buildWavOptimized(pcm: Uint8Array, sampleRate: number): ArrayBuffer {
+ const wavHeader = new Uint8Array(44)
+ const dataLen = pcm.length
+ const fileLen = 36 + dataLen
+
+ const writeStr = (offset: number, str: string) => {
+ for (let i = 0; i < str.length; i++) {
+ wavHeader[offset + i] = str.charCodeAt(i)
+ }
+ }
+
+ const write32 = (offset: number, value: number) => {
+ wavHeader[offset] = value & 0xFF
+ wavHeader[offset + 1] = (value >> 8) & 0xFF
+ wavHeader[offset + 2] = (value >> 16) & 0xFF
+ wavHeader[offset + 3] = (value >> 24) & 0xFF
+ }
+
+ const write16 = (offset: number, value: number) => {
+ wavHeader[offset] = value & 0xFF
+ wavHeader[offset + 1] = (value >> 8) & 0xFF
+ }
+
+ writeStr(0, 'RIFF')
+ write32(4, fileLen)
+ writeStr(8, 'WAVE')
+ writeStr(12, 'fmt ')
+ write32(16, 16)
+ write16(20, 1)
+ write16(22, 1)
+ write32(24, sampleRate) // 浣跨敤浼犲叆鐨勯噰鏍风巼
+ write32(28, sampleRate * 2)
+ write16(32, 2)
+ write16(34, 16)
+ writeStr(36, 'data')
+ write32(40, dataLen)
+
+ // 鍚堝苟header鍜屾暟鎹�
+ const result = new ArrayBuffer(44 + dataLen)
+ const resultView = new Uint8Array(result)
+ resultView.set(wavHeader)
+ resultView.set(pcm, 44)
+
+ return result
+}
+
+// 鎾斁闊抽
+async function playAudio() {
+ if (!audioFilePath.value) {
+ toast.error(t('deviceConfig.pleaseGenerateAudioFirst'))
+ return
+ }
+
+ try {
+ // 寮哄埗娓呯悊鎵�鏈夋棫鐨勯煶棰戝疄渚�
+ await cleanupAudio()
+
+ // 绛夊緟涓�涓嬬‘淇濇竻鐞嗗畬鎴�
+ await new Promise(resolve => setTimeout(resolve, 200))
+
+ playing.value = true
+ console.log(t('deviceConfig.startPlayingUltrasonicConfigAudio'))
+
+ // 鍒涘缓鏂扮殑闊抽涓婁笅鏂�
+ const innerAudioContext = uni.createInnerAudioContext()
+ audioContext.value = innerAudioContext
+
+ // 鏈�绠�鍖栫殑闊抽璁剧疆
+ innerAudioContext.src = audioFilePath.value
+ innerAudioContext.loop = autoLoop.value
+ innerAudioContext.volume = 0.8
+ innerAudioContext.autoplay = false
+
+ // 绠�鍖栫殑浜嬩欢鐩戝惉
+ innerAudioContext.onPlay(() => {
+ console.log(t('deviceConfig.ultrasonicAudioStartedPlaying'))
+ toast.success(t('deviceConfig.startPlayingConfigSoundWave'))
+ })
+
+ innerAudioContext.onEnded(() => {
+ console.log(t('deviceConfig.ultrasonicAudioPlaybackEnded'))
+ if (!autoLoop.value) {
+ playing.value = false
+ cleanupAudio()
+ }
+ })
+
+ innerAudioContext.onError((error) => {
+ console.error(`${t('deviceConfig.audioPlaybackFailed')}:`, error)
+ playing.value = false
+
+ let errorMsg = t('deviceConfig.audioPlaybackFailed')
+ if (error.errCode === -99) {
+ errorMsg = t('deviceConfig.audioResourceBusy')
+ }
+ else if (error.errCode === 10004) {
+ errorMsg = t('deviceConfig.audioFormatNotSupported')
+ }
+ else if (error.errCode === 10003) {
+ errorMsg = t('deviceConfig.audioFileError')
+ }
+
+ toast.error(errorMsg)
+
+ cleanupAudio()
+ })
+
+ innerAudioContext.onStop(() => {
+ console.log('闊抽鎾斁鍋滄')
+ playing.value = false
+ })
+
+ // 寤惰繜鎾斁
+ setTimeout(() => {
+ if (audioContext.value) {
+ console.log('灏濊瘯鎾斁闊抽锛宻rc闀垮害:', audioFilePath.value.length)
+ audioContext.value.play()
+ }
+ }, 300)
+ }
+ catch (error) {
+ console.error(`${t('deviceConfig.audioPlaybackError')}:`, error)
+ playing.value = false
+ await cleanupAudio()
+ toast.error(`${t('deviceConfig.playbackFailed')}: ${error.message}`)
+ }
+}
+
+// 娓呯悊闊抽璧勬簮
+async function cleanupAudio() {
+ if (audioContext.value) {
+ try {
+ audioContext.value.pause()
+ audioContext.value.destroy()
+ console.log(t('deviceConfig.cleaningUpAudioContext'))
+ }
+ catch (e) {
+ console.log(`${t('deviceConfig.cleaningUpAudioContextFailed')}:`, e)
+ }
+ finally {
+ audioContext.value = null
+ }
+ }
+}
+
+// 鍋滄鎾斁
+async function stopAudio() {
+ playing.value = false
+ await cleanupAudio()
+
+ console.log(t('deviceConfig.stoppedPlayingUltrasonicAudio'))
+ toast.success(t('deviceConfig.stoppedPlaying'))
+}
+</script>
+
+<template>
+ <view class="ultrasonic-config">
+ <!-- 閫変腑鐨勭綉缁滀俊鎭� -->
+ <view v-if="props.selectedNetwork" class="selected-network">
+ <view class="network-info">
+ <view class="network-name">
+ {{ t('deviceConfig.selectedNetwork') }}: {{ props.selectedNetwork.ssid }}
+ </view>
+ <view class="network-details">
+ <text class="network-signal">
+ {{ t('deviceConfig.signal') }}: {{ props.selectedNetwork.rssi }}dBm
+ </text>
+ <text class="network-security">
+ {{ props.selectedNetwork.authmode === 0 ? t('deviceConfig.openNetwork') : t('deviceConfig.encryptedNetwork') }}
+ </text>
+ </view>
+ <view v-if="props.password" class="network-password">
+ {{ t('deviceConfig.password') }}: {{ '*'.repeat(props.password.length) }}
+ </view>
+ </view>
+ </view>
+
+ <!-- 瓒呭0娉㈤厤缃戞搷浣� -->
+ <view class="submit-section">
+ <wd-button
+ type="primary"
+ size="large"
+ block
+ :loading="generating"
+ :disabled="!canGenerate"
+ @click="generateAndPlay"
+ >
+ {{ generating ? t('deviceConfig.generating') : `馃幍 ${t('deviceConfig.generateAndPlaySoundWave')}` }}
+ </wd-button>
+
+ <wd-button
+ v-if="audioGenerated"
+ type="success"
+ size="large"
+ block
+ :loading="playing"
+ @click="playAudio"
+ >
+ {{ playing ? t('deviceConfig.playing') : `馃攰 ${t('deviceConfig.playSoundWave')}` }}
+ </wd-button>
+
+ <wd-button
+ v-if="playing"
+ type="warning"
+ size="large"
+ block
+ @click="stopAudio"
+ >
+ 鈴癸笍 {{ t('deviceConfig.stopPlaying') }}
+ </wd-button>
+ </view>
+
+ <!-- 闊抽鎺у埗閫夐」 -->
+ <view v-if="audioGenerated" class="audio-options">
+ <view class="option-item">
+ <wd-checkbox v-model="autoLoop">
+ {{ t('deviceConfig.autoLoopPlaySoundWave') }}
+ </wd-checkbox>
+ </view>
+ </view>
+
+ <!-- 闊抽鎾斁鍣� -->
+ <view v-if="audioGenerated" class="audio-player">
+ <view class="player-info">
+ <text class="audio-title">
+ {{ t('deviceConfig.configAudioFile') }}
+ </text>
+ <text class="audio-duration">
+ {{ t('deviceConfig.duration') }}: {{ audioLengthText }}
+ </text>
+ </view>
+ </view>
+
+ <!-- 浣跨敤璇存槑 -->
+ <view class="help-section">
+ <view class="help-title">
+ {{ t('deviceConfig.ultrasonicConfigInstructions') }}
+ </view>
+ <view class="help-content">
+ <text class="help-item">
+ 1. {{ t('deviceConfig.ensureWifiNetworkSelectedAndPasswordEntered') }}
+ </text>
+ <text class="help-item">
+ 2. {{ t('deviceConfig.clickGenerateAndPlaySoundWave') }}
+ </text>
+ <text class="help-item">
+ 3. {{ t('deviceConfig.bringPhoneCloseToXiaozhiDevice') }}
+ </text>
+ <text class="help-item">
+ 4. {{ t('deviceConfig.duringAudioPlaybackXiaozhiWillReceive') }}
+ </text>
+ <text class="help-item">
+ 5. {{ t('deviceConfig.afterConfigSuccessDeviceWillConnect') }}
+ </text>
+ <text class="help-tip">
+ {{ t('deviceConfig.usesAfskModulation') }}
+ </text>
+ <text class="help-tip">
+ {{ t('deviceConfig.ensureModeratePhoneVolume') }}
+ </text>
+ </view>
+ </view>
+ </view>
+</template>
+
+<style scoped>
+.ultrasonic-config {
+ padding: 20rpx 0;
+}
+
+.selected-network {
+ margin-bottom: 32rpx;
+}
+
+.network-info {
+ padding: 24rpx;
+ background-color: #f0f6ff;
+ border: 1rpx solid #336cff;
+ border-radius: 16rpx;
+}
+
+.network-name {
+ font-size: 28rpx;
+ font-weight: 600;
+ color: #232338;
+ margin-bottom: 8rpx;
+}
+
+.network-details {
+ display: flex;
+ gap: 24rpx;
+ margin-bottom: 8rpx;
+}
+
+.network-signal,
+.network-security {
+ font-size: 24rpx;
+ color: #65686f;
+}
+
+.network-password {
+ font-size: 24rpx;
+ color: #65686f;
+}
+
+.submit-section {
+ margin-bottom: 32rpx;
+}
+
+.submit-section .wd-button {
+ margin-bottom: 16rpx;
+}
+
+.submit-section .wd-button:last-child {
+ margin-bottom: 0;
+}
+
+.audio-options {
+ margin-bottom: 32rpx;
+ padding: 24rpx;
+ background-color: #fbfbfb;
+ border-radius: 16rpx;
+ border: 1rpx solid #eeeeee;
+}
+
+.option-item {
+ font-size: 28rpx;
+}
+
+.audio-player {
+ margin-bottom: 32rpx;
+ padding: 24rpx;
+ background-color: #f0f6ff;
+ border: 1rpx solid #336cff;
+ border-radius: 16rpx;
+}
+
+.player-info {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.audio-title {
+ font-size: 28rpx;
+ font-weight: 600;
+ color: #232338;
+}
+
+.audio-duration {
+ font-size: 24rpx;
+ color: #65686f;
+}
+
+.help-section {
+ padding: 32rpx 24rpx;
+ background-color: #fbfbfb;
+ border-radius: 16rpx;
+ border: 1rpx solid #eeeeee;
+}
+
+.help-title {
+ font-size: 28rpx;
+ font-weight: 600;
+ color: #232338;
+ margin-bottom: 20rpx;
+}
+
+.help-content {
+ display: flex;
+ flex-direction: column;
+ gap: 12rpx;
+}
+
+.help-item {
+ font-size: 24rpx;
+ color: #65686f;
+ line-height: 1.5;
+}
+
+.help-tip {
+ font-size: 24rpx;
+ color: #336cff;
+ font-weight: 500;
+ margin-top: 8rpx;
+}
+</style>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/device-config/components/wifi-config.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/device-config/components/wifi-config.vue
new file mode 100644
index 0000000..32792be
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/device-config/components/wifi-config.vue
@@ -0,0 +1,236 @@
+<script setup lang="ts">
+import { computed, ref } from 'vue'
+import { t } from '@/i18n'
+import { toast } from '@/utils/toast'
+
+// 绫诲瀷瀹氫箟
+interface WiFiNetwork {
+ ssid: string
+ rssi: number
+ authmode: number
+ channel: number
+}
+
+// Props
+interface Props {
+ selectedNetwork: WiFiNetwork | null
+ password: string
+}
+
+const props = defineProps<Props>()
+
+// 鍝嶅簲寮忔暟鎹�
+const configuring = ref(false)
+
+// 璁$畻灞炴��
+const canSubmit = computed(() => {
+ if (!props.selectedNetwork)
+ return false
+ if (props.selectedNetwork.authmode > 0 && !props.password)
+ return false
+ return true
+})
+
+// ESP32杩炴帴妫�鏌�
+async function checkESP32Connection() {
+ try {
+ const response = await uni.request({
+ url: 'http://192.168.4.1/scan',
+ method: 'GET',
+ timeout: 3000,
+ })
+ return response.statusCode === 200
+ }
+ catch (error) {
+ console.log(`${t('deviceConfig.esp32ConnectionCheckFailed')}:`, error)
+ return false
+ }
+}
+
+// 鎻愪氦閰嶇綉
+async function submitConfig() {
+ if (!props.selectedNetwork)
+ return
+
+ // 妫�鏌SP32杩炴帴
+ const connected = await checkESP32Connection()
+ if (!connected) {
+ toast.error(t('deviceConfig.connectXiaozhiHotspot'))
+ return
+ }
+
+ configuring.value = true
+ console.log(`${t('deviceConfig.startWifiConfig')}:`, props.selectedNetwork.ssid)
+
+ try {
+ const response = await uni.request({
+ url: 'http://192.168.4.1/submit',
+ method: 'POST',
+ header: {
+ 'Content-Type': 'application/json',
+ },
+ data: {
+ ssid: props.selectedNetwork.ssid,
+ password: props.selectedNetwork.authmode > 0 ? props.password : '',
+ },
+ timeout: 15000,
+ })
+
+ console.log('WiFi閰嶇綉鍝嶅簲:', response)
+
+ if (response.statusCode === 200 && (response.data as any)?.success) {
+ toast.success(`${t('deviceConfig.configSuccess')}锛�${t('deviceConfig.deviceWillConnectTo')} ${props.selectedNetwork.ssid}锛�${t('deviceConfig.deviceWillRestart')}銆�${t('deviceConfig.pleaseDisconnectXiaozhiHotspot')}`)
+ // 璁惧閫�鍑洪厤缃戞ā寮�
+ setTimeout(() => {
+ uni.request({
+ url: 'http://192.168.4.1/exit',
+ method: 'POST',
+ timeout: 15000,
+ })
+ }, 1500)
+ }
+ else {
+ const errorMsg = (response.data as any)?.error || t('deviceConfig.configFailed')
+ toast.error(errorMsg)
+ }
+ }
+ catch (error) {
+ console.error(`${t('deviceConfig.wifiConfigFailed')}:`, error)
+ toast.error(`${t('deviceConfig.configFailed')}锛�${t('deviceConfig.pleaseCheckNetworkConnection')}`)
+ }
+ finally {
+ configuring.value = false
+ }
+}
+</script>
+
+<template>
+ <view class="wifi-config">
+ <!-- 閫変腑鐨勭綉缁滀俊鎭� -->
+ <view v-if="props.selectedNetwork" class="selected-network">
+ <view class="network-info">
+ <view class="network-name">
+ {{ t('deviceConfig.selectedNetwork') }}: {{ props.selectedNetwork.ssid }}
+ </view>
+ <view class="network-details">
+ <text class="network-signal">
+ {{ t('deviceConfig.signal') }}: {{ props.selectedNetwork.rssi }}dBm
+ </text>
+ <text class="network-security">
+ {{ props.selectedNetwork.authmode === 0 ? t('deviceConfig.openNetwork') : t('deviceConfig.encryptedNetwork') }}
+ </text>
+ </view>
+ </view>
+ </view>
+
+ <!-- 閰嶇綉鎸夐挳 -->
+ <view class="submit-section">
+ <wd-button
+ type="primary"
+ size="large"
+ block
+ :loading="configuring"
+ :disabled="!canSubmit"
+ @click="submitConfig"
+ >
+ {{ configuring ? t('deviceConfig.configuring') : t('deviceConfig.startWifiConfigButton') }}
+ </wd-button>
+ </view>
+
+ <!-- 浣跨敤璇存槑 -->
+ <view class="help-section">
+ <view class="help-title">
+ {{ t('deviceConfig.wifiConfigInstructions') }}
+ </view>
+ <view class="help-content">
+ <text class="help-item">
+ 1. {{ t('deviceConfig.phoneConnectXiaozhiHotspot') }} (xiaozhi-XXXXXX)
+ </text>
+ <text class="help-item">
+ 2. {{ t('deviceConfig.selectTargetWifiNetwork') }}
+ </text>
+ <text class="help-item">
+ 3. {{ t('deviceConfig.enterWifiPasswordIfNeeded') }}
+ </text>
+ <text class="help-item">
+ 4. {{ t('deviceConfig.clickStartConfigAndWait') }}
+ </text>
+ <text class="help-tip">
+ {{ t('deviceConfig.afterConfigSuccessDeviceWillRestart') }}
+ </text>
+ </view>
+ </view>
+ </view>
+</template>
+
+<style scoped>
+.wifi-config {
+ padding: 20rpx 0;
+}
+
+.selected-network {
+ margin-bottom: 32rpx;
+}
+
+.network-info {
+ padding: 24rpx;
+ background-color: #f0f6ff;
+ border: 1rpx solid #336cff;
+ border-radius: 16rpx;
+}
+
+.network-name {
+ font-size: 28rpx;
+ font-weight: 600;
+ color: #232338;
+ margin-bottom: 8rpx;
+}
+
+.network-details {
+ display: flex;
+ gap: 24rpx;
+}
+
+.network-signal,
+.network-security {
+ font-size: 24rpx;
+ color: #65686f;
+}
+
+.submit-section {
+ margin-bottom: 32rpx;
+}
+
+.help-section {
+ padding: 32rpx 24rpx;
+ background-color: #fbfbfb;
+ border-radius: 16rpx;
+ border: 1rpx solid #eeeeee;
+}
+
+.help-title {
+ font-size: 28rpx;
+ font-weight: 600;
+ color: #232338;
+ margin-bottom: 20rpx;
+}
+
+.help-content {
+ display: flex;
+ flex-direction: column;
+ gap: 12rpx;
+}
+
+.help-item {
+ font-size: 24rpx;
+ color: #65686f;
+ line-height: 1.5;
+}
+
+.help-tip {
+ font-size: 24rpx;
+ color: #336cff;
+ font-weight: 500;
+ margin-top: 8rpx;
+}
+</style>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/device-config/components/wifi-selector.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/device-config/components/wifi-selector.vue
new file mode 100644
index 0000000..e6e0435
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/device-config/components/wifi-selector.vue
@@ -0,0 +1,567 @@
+<script setup lang="ts">
+import { computed, onMounted, ref } from 'vue'
+import { useToast } from 'wot-design-uni/components/wd-toast'
+import { t } from '@/i18n'
+
+// 绫诲瀷瀹氫箟
+interface WiFiNetwork {
+ ssid: string
+ rssi: number
+ authmode: number
+ channel: number
+}
+
+// Props
+interface Props {
+ autoConnect?: boolean // 鏄惁鑷姩妫�娴婨SP32杩炴帴
+}
+
+const props = withDefaults(defineProps<Props>(), {
+ autoConnect: true,
+})
+
+// Emits
+const emit = defineEmits<{
+ 'network-selected': [network: WiFiNetwork | null, password: string]
+ 'connection-status': [connected: boolean]
+}>()
+
+// Toast 瀹炰緥
+const toast = useToast()
+
+// 鍝嶅簲寮忔暟鎹�
+const isConnectedToESP32 = ref(false)
+const checkingConnection = ref(false)
+const scanning = ref(false)
+const wifiNetworks = ref<WiFiNetwork[]>([])
+const selectedNetwork = ref<WiFiNetwork | null>(null)
+const password = ref('')
+const selectorExpanded = ref(false)
+
+// 璁$畻灞炴��
+const networkDisplayText = computed(() => {
+ if (!selectedNetwork.value)
+ return t('deviceConfig.selectWifiNetwork')
+ return selectedNetwork.value.ssid
+})
+
+// 妫�鏌iaozhi杩炴帴鐘舵��
+async function checkESP32Connection() {
+ checkingConnection.value = true
+ try {
+ const response = await uni.request({
+ url: 'http://192.168.4.1/scan',
+ method: 'GET',
+ timeout: 3000,
+ })
+ isConnectedToESP32.value = response.statusCode === 200
+ emit('connection-status', isConnectedToESP32.value)
+ console.log(`${t('deviceConfig.xiaozhi')}杩炴帴鐘舵��:`, isConnectedToESP32.value)
+ }
+ catch (error) {
+ isConnectedToESP32.value = false
+ emit('connection-status', false)
+ console.log('xiaozhi杩炴帴妫�鏌ュけ璐�:', error)
+ }
+ finally {
+ checkingConnection.value = false
+ }
+}
+
+// 鎵弿WiFi缃戠粶
+async function scanWifi() {
+ if (!isConnectedToESP32.value) {
+ toast.error(t('deviceConfig.connectXiaozhiHotspot'))
+ return
+ }
+
+ scanning.value = true
+ console.log('寮�濮嬫壂鎻廤iFi缃戠粶')
+
+ try {
+ const response = await uni.request({
+ url: 'http://192.168.4.1/scan',
+ method: 'GET',
+ timeout: 10000,
+ })
+
+ console.log(`${t('deviceConfig.wifiScanResponse')}:`, response)
+
+ if (response.statusCode === 200 && response.data) {
+ const data = response.data as any
+ if (data.success && Array.isArray(data.networks)) {
+ wifiNetworks.value = data.networks
+ console.log(`${t('deviceConfig.scanSuccess')}锛屽彂鐜� ${data.networks.length} ${t('deviceConfig.networks')}`)
+ }
+ else if (data.aps && Array.isArray(data.aps)) {
+ // 鍏煎 { data: { support_5g, aps } } 鏍煎紡
+ wifiNetworks.value = data.aps.map((item: any) => ({
+ ssid: item.ssid,
+ rssi: item.rssi,
+ authmode: item.authmode,
+ channel: item.channel || 0,
+ }))
+ console.log(`${t('deviceConfig.scanSuccess')}锛屽彂鐜� ${data.aps.length} ${t('deviceConfig.networks')}`)
+ }
+ else if (Array.isArray(response.data)) {
+ // 鍏煎鏃ф牸寮�
+ wifiNetworks.value = response.data.map((item: any) => ({
+ ssid: item.ssid,
+ rssi: item.rssi,
+ authmode: item.authmode,
+ channel: item.channel || 0,
+ }))
+ }
+ else {
+ throw new TypeError('鎵弿鎺ュ彛杩斿洖鏍煎紡寮傚父')
+ }
+ }
+ else {
+ throw new Error(`HTTP ${response.statusCode}`)
+ }
+ }
+ catch (error) {
+ console.error(`${t('deviceConfig.wifiScanFailed')}:`, error)
+ toast.error(t('deviceConfig.scanFailedCheckConnection'))
+ }
+ finally {
+ scanning.value = false
+ }
+}
+
+// 鏄剧ず缃戠粶閫夋嫨鍣�
+async function showNetworkSelector() {
+ // 瀹炴椂妫�娴媥iaozhi杩炴帴鐘舵��
+ await checkESP32Connection()
+
+ if (!isConnectedToESP32.value) {
+ toast.error(t('deviceConfig.connectXiaozhiHotspot'))
+ return
+ }
+
+ selectorExpanded.value = true
+
+ // 濡傛灉杩樻病鏈夌綉缁滃垪琛紝鑷姩鎵弿
+ if (wifiNetworks.value.length === 0) {
+ scanWifi()
+ }
+}
+
+// 閫夋嫨缃戠粶
+function selectNetwork(network: WiFiNetwork) {
+ selectedNetwork.value = network
+ password.value = ''
+ selectorExpanded.value = false
+ console.log('閫夋嫨缃戠粶:', network.ssid)
+
+ // 閫氱煡鐖剁粍浠�
+ emit('network-selected', network, '')
+}
+
+// 瀵嗙爜鍙樺寲鏃堕�氱煡鐖剁粍浠�
+function onPasswordChange() {
+ emit('network-selected', selectedNetwork.value, password.value)
+}
+
+// 鑾峰彇褰撳墠閫夋嫨鐨勭綉缁滃拰瀵嗙爜
+function getSelectedNetworkInfo() {
+ return {
+ network: selectedNetwork.value,
+ password: password.value,
+ }
+}
+
+// 閲嶇疆閫夋嫨
+function reset() {
+ selectedNetwork.value = null
+ password.value = ''
+ wifiNetworks.value = []
+ selectorExpanded.value = false
+ emit('network-selected', null, '')
+}
+
+// 鑾峰彇淇″彿寮哄害鎻忚堪
+function getSignalStrength(rssi: number): string {
+ if (rssi >= -50)
+ return t('deviceConfig.signalStrong')
+ if (rssi >= -60)
+ return t('deviceConfig.signalGood')
+ if (rssi >= -70)
+ return t('deviceConfig.signalFair')
+ return t('deviceConfig.signalWeak')
+}
+
+// 鑾峰彇淇″彿寮哄害棰滆壊
+function getSignalColor(rssi: number): string {
+ if (rssi >= -50)
+ return '#52c41a'
+ if (rssi >= -60)
+ return '#73d13d'
+ if (rssi >= -70)
+ return '#faad14'
+ return '#ff4d4f'
+}
+
+// 鏆撮湶鏂规硶缁欑埗缁勪欢
+defineExpose({
+ checkESP32Connection,
+ scanWifi,
+ getSelectedNetworkInfo,
+ reset,
+})
+
+// 鐢熷懡鍛ㄦ湡
+onMounted(() => {
+ if (props.autoConnect) {
+ checkESP32Connection()
+ }
+})
+</script>
+
+<template>
+ <view class="wifi-selector">
+ <!-- Xiaozhi杩炴帴鐘舵�� -->
+ <view v-if="props.autoConnect" class="connection-status">
+ <view v-if="!isConnectedToESP32" class="status-warning">
+ <view class="status-content">
+ <text class="warning-text">
+ {{ t('deviceConfig.connectXiaozhiHotspot') }} (xiaozhi-XXXXXX)
+ </text>
+ <wd-button
+ size="small"
+ type="primary"
+ :loading="checkingConnection"
+ @click="checkESP32Connection"
+ >
+ {{ checkingConnection ? t('deviceConfig.checking') : t('deviceConfig.reCheck') }}
+ </wd-button>
+ </view>
+ </view>
+ <view v-else class="status-success">
+ <view class="status-content">
+ <text class="success-text">
+ {{ t('deviceConfig.connectedXiaozhiHotspot') }}
+ </text>
+ <wd-button
+ size="small"
+ :loading="checkingConnection"
+ @click="checkESP32Connection"
+ >
+ {{ checkingConnection ? t('deviceConfig.checking') : t('deviceConfig.refreshStatus') }}
+ </wd-button>
+ </view>
+ </view>
+ </view>
+
+ <!-- WiFi缃戠粶閫夋嫨鍣� -->
+ <view class="network-selector">
+ <view class="selector-item" @click="showNetworkSelector">
+ <text class="selector-label">
+ {{ t('deviceConfig.wifiNetwork') }}
+ </text>
+ <text class="selector-value">
+ {{ networkDisplayText }}
+ </text>
+ <wd-icon name="arrow-right" custom-class="arrow-icon" />
+ </view>
+ </view>
+
+ <!-- 灞曞紑鐨勭綉缁滃垪琛� -->
+ <view v-if="selectorExpanded" class="network-list-overlay">
+ <view class="network-list-container">
+ <view class="list-header">
+ <text class="list-title">
+ {{ t('deviceConfig.selectWifiNetwork') }}
+ </text>
+ <view class="list-actions">
+ <wd-button
+ type="primary"
+ size="small"
+ :loading="scanning"
+ @click="scanWifi"
+ >
+ {{ scanning ? t('deviceConfig.scanning') : t('deviceConfig.refreshScan') }}
+ </wd-button>
+ <wd-button
+ size="small"
+ @click="selectorExpanded = false"
+ >
+ {{ t('deviceConfig.cancel') }}
+ </wd-button>
+ </view>
+ </view>
+
+ <view class="network-list">
+ <view v-if="wifiNetworks.length === 0 && !scanning" class="empty-state">
+ <text class="empty-text">
+ {{ t('deviceConfig.noWifiNetworks') }}
+ </text>
+ <text class="empty-tip">
+ {{ t('deviceConfig.clickRefreshScan') }}
+ </text>
+ </view>
+
+ <view v-else class="wifi-list">
+ <view
+ v-for="network in wifiNetworks"
+ :key="network.ssid"
+ class="wifi-item"
+ @click="selectNetwork(network)"
+ >
+ <view class="wifi-info">
+ <view class="wifi-name">
+ {{ network.ssid }}
+ </view>
+ <view class="wifi-details">
+ <text class="wifi-signal">
+ {{ t('deviceConfig.signal') }}: {{ network.rssi }}dBm
+ </text>
+ <text class="wifi-channel">
+ {{ t('deviceConfig.channel') }}: {{ network.channel }}
+ </text>
+ </view>
+ </view>
+ <view class="wifi-security">
+ <text class="security-icon">
+ {{ network.authmode === 0 ? t('deviceConfig.open') : t('deviceConfig.encryptedNetwork') }}
+ </text>
+ </view>
+ </view>
+ </view>
+ </view>
+ </view>
+ </view>
+
+ <!-- 瀵嗙爜杈撳叆 -->
+ <view v-if="selectedNetwork && selectedNetwork.authmode > 0" class="password-section">
+ <view class="password-item">
+ <text class="password-label">
+ {{ t('deviceConfig.wifiPassword') }}
+ </text>
+ <wd-input
+ v-model="password"
+ :placeholder="t('deviceConfig.enterWifiPassword')"
+ show-password
+ clearable
+ @input="onPasswordChange"
+ />
+ </view>
+ </view>
+ </view>
+</template>
+
+<style scoped>
+.wifi-selector {
+ width: 100%;
+}
+
+.connection-status {
+ margin-bottom: 24rpx;
+}
+
+.status-warning {
+ padding: 24rpx;
+ background-color: #fff3cd;
+ border: 1rpx solid #ffeaa7;
+ border-radius: 16rpx;
+}
+
+.status-success {
+ padding: 24rpx;
+ background-color: #d4edda;
+ border: 1rpx solid #c3e6cb;
+ border-radius: 16rpx;
+}
+
+.status-content {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+}
+
+.warning-text {
+ color: #856404;
+ font-size: 28rpx;
+}
+
+.success-text {
+ color: #155724;
+ font-size: 28rpx;
+}
+
+.network-selector {
+ margin-bottom: 24rpx;
+}
+
+.selector-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 20rpx;
+ background: #f5f7fb;
+ border-radius: 12rpx;
+ border: 1rpx solid #eeeeee;
+ cursor: pointer;
+ transition: all 0.3s ease;
+}
+
+.selector-item:active {
+ background: #eef3ff;
+ border-color: #336cff;
+}
+
+.selector-label {
+ font-size: 28rpx;
+ color: #232338;
+ font-weight: 500;
+}
+
+.selector-value {
+ flex: 1;
+ text-align: right;
+ font-size: 26rpx;
+ color: #65686f;
+ margin: 0 16rpx;
+}
+
+:deep(.arrow-icon) {
+ font-size: 20rpx;
+ color: #9d9ea3;
+}
+
+.network-list-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ background-color: rgba(0, 0, 0, 0.5);
+ z-index: 1000;
+ display: flex;
+ align-items: flex-end;
+}
+
+.network-list-container {
+ width: 100%;
+ max-height: 70vh;
+ background-color: #ffffff;
+ border-radius: 20rpx 20rpx 0 0;
+ padding: 32rpx;
+ box-sizing: border-box;
+}
+
+.list-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 24rpx;
+ padding-bottom: 16rpx;
+ border-bottom: 1rpx solid #eeeeee;
+}
+
+.list-title {
+ font-size: 32rpx;
+ font-weight: 600;
+ color: #232338;
+}
+
+.list-actions {
+ display: flex;
+ gap: 16rpx;
+}
+
+.network-list {
+ max-height: 50vh;
+ overflow-y: auto;
+}
+
+.empty-state {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 80rpx 20rpx;
+ background-color: #fbfbfb;
+ border-radius: 16rpx;
+ border: 1rpx solid #eeeeee;
+}
+
+.empty-text {
+ font-size: 32rpx;
+ color: #65686f;
+ margin-bottom: 16rpx;
+}
+
+.empty-tip {
+ font-size: 24rpx;
+ color: #9d9ea3;
+}
+
+.wifi-list {
+ display: flex;
+ flex-direction: column;
+ gap: 16rpx;
+}
+
+.wifi-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 32rpx 24rpx;
+ background-color: #fbfbfb;
+ border: 2rpx solid #eeeeee;
+ border-radius: 16rpx;
+ transition: all 0.3s ease;
+ cursor: pointer;
+}
+
+.wifi-item:active {
+ transform: scale(0.98);
+ background-color: #f0f6ff;
+ border-color: #336cff;
+}
+
+.wifi-info {
+ flex: 1;
+}
+
+.wifi-name {
+ font-size: 32rpx;
+ font-weight: 600;
+ color: #232338;
+ margin-bottom: 8rpx;
+}
+
+.wifi-details {
+ display: flex;
+ gap: 24rpx;
+}
+
+.wifi-signal,
+.wifi-channel {
+ font-size: 24rpx;
+ color: #65686f;
+}
+
+.security-icon {
+ font-size: 24rpx;
+ color: #65686f;
+ margin-left: 20rpx;
+}
+
+.password-section {
+ margin-top: 24rpx;
+}
+
+.password-item {
+ display: flex;
+ flex-direction: column;
+ gap: 12rpx;
+}
+
+.password-label {
+ font-size: 28rpx;
+ color: #232338;
+ font-weight: 500;
+}
+</style>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/device-config/index.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/device-config/index.vue
new file mode 100644
index 0000000..23ce529
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/device-config/index.vue
@@ -0,0 +1,153 @@
+<script setup lang="ts">
+import { onMounted, ref } from 'vue'
+import { t } from '@/i18n'
+import UltrasonicConfig from './components/ultrasonic-config.vue'
+import WifiConfig from './components/wifi-config.vue'
+import WifiSelector from './components/wifi-selector.vue'
+
+// 绫诲瀷瀹氫箟
+interface WiFiNetwork {
+ ssid: string
+ rssi: number
+ authmode: number
+ channel: number
+}
+
+// 閰嶇綉绫诲瀷
+const configType = ref<'wifi' | 'ultrasonic'>('wifi')
+
+// 閰嶇綉妯″紡閫夋嫨鍣ㄧ姸鎬�
+const configTypeSelectorShow = ref(false)
+
+// WiFi閫夋嫨鍣ㄥ紩鐢�
+const wifiSelectorRef = ref<InstanceType<typeof WifiSelector>>()
+
+// 閫夋嫨鐨刉iFi缃戠粶淇℃伅
+const selectedWifiInfo = ref<{
+ network: WiFiNetwork | null
+ password: string
+}>({
+ network: null,
+ password: '',
+})
+
+// 閰嶇綉妯″紡閫夐」
+const configTypeOptions = [
+ {
+ name: t('deviceConfig.wifiConfig'),
+ value: 'wifi' as const,
+ },
+ // {
+ // name: t('deviceConfig.ultrasonicConfig'),
+ // value: 'ultrasonic' as const,
+ // },
+]
+
+// 鏄剧ず閰嶇綉妯″紡閫夋嫨鍣�
+function showConfigTypeSelector() {
+ configTypeSelectorShow.value = true
+}
+
+// 閰嶇綉妯″紡閫夋嫨鍣ㄧ‘璁�
+function onConfigTypeConfirm(item: { name: string, value: 'wifi' | 'ultrasonic' }) {
+ configType.value = item.value
+ configTypeSelectorShow.value = false
+}
+
+// 閰嶇綉妯″紡閫夋嫨鍣ㄥ彇娑�
+function onConfigTypeCancel() {
+ configTypeSelectorShow.value = false
+}
+
+// WiFi缃戠粶閫夋嫨浜嬩欢
+function onNetworkSelected(network: WiFiNetwork | null, password: string) {
+ selectedWifiInfo.value = { network, password }
+}
+
+// ESP32杩炴帴鐘舵�佸彉鍖栦簨浠�
+function onConnectionStatusChange(connected: boolean) {
+ console.log('ESP32杩炴帴鐘舵��:', connected)
+}
+// 鍦ㄧ粍浠舵寕杞藉悗璁剧疆瀵艰埅鏍忔爣棰�
+onMounted(() => {
+ uni.setNavigationBarTitle({
+ title: t('deviceConfig.pageTitle'),
+ })
+})
+</script>
+
+<template>
+ <view class="min-h-screen bg-[#f5f7fb]">
+ <wd-navbar :title="t('deviceConfig.pageTitle')" safe-area-inset-top />
+
+ <view class="box-border px-[20rpx]">
+ <!-- 閰嶇綉鏂瑰紡閫夋嫨 -->
+ <view class="pb-[20rpx] first:pt-[20rpx]">
+ <text class="text-[32rpx] text-[#232338] font-bold">
+ {{ t('deviceConfig.configMethod') }}
+ </text>
+ </view>
+
+ <view class="mb-[24rpx] border border-[#eeeeee] rounded-[20rpx] bg-[#fbfbfb] p-[24rpx]" style="box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);">
+ <view class="flex cursor-pointer items-center justify-between border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] transition-all duration-300 active:border-[#336cff] active:bg-[#eef3ff]" @click="showConfigTypeSelector">
+ <text class="text-[28rpx] text-[#232338] font-medium">
+ {{ t('deviceConfig.configMethod') }}
+ </text>
+ <text class="mx-[16rpx] flex-1 text-right text-[26rpx] text-[#65686f]">
+ {{ configType === 'wifi' ? t('deviceConfig.wifiConfig') : t('deviceConfig.ultrasonicConfig') }}
+ </text>
+ <wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
+ </view>
+ </view>
+
+ <!-- WiFi缃戠粶閫夋嫨 -->
+ <view class="pb-[20rpx]">
+ <text class="text-[32rpx] text-[#232338] font-bold">
+ {{ t('deviceConfig.networkConfig') }}
+ </text>
+ </view>
+
+ <view class="mb-[24rpx] border border-[#eeeeee] rounded-[20rpx] bg-[#fbfbfb] p-[24rpx]" style="box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);">
+ <wifi-selector
+ ref="wifiSelectorRef"
+ @network-selected="onNetworkSelected"
+ @connection-status="onConnectionStatusChange"
+ />
+ </view>
+
+ <!-- 閰嶇綉鎿嶄綔 -->
+ <view v-if="selectedWifiInfo.network" class="flex-1">
+ <!-- WiFi閰嶇綉缁勪欢 -->
+ <wifi-config
+ v-if="configType === 'wifi'"
+ :selected-network="selectedWifiInfo.network"
+ :password="selectedWifiInfo.password"
+ />
+
+ <!-- 瓒呭0娉㈤厤缃戠粍浠� -->
+ <ultrasonic-config
+ v-else-if="configType === 'ultrasonic'"
+ :selected-network="selectedWifiInfo.network"
+ :password="selectedWifiInfo.password"
+ />
+ </view>
+ </view>
+
+ <!-- 閰嶇綉妯″紡閫夋嫨鍣� -->
+ <wd-action-sheet
+ v-model="configTypeSelectorShow"
+ :actions="configTypeOptions.map(item => ({ name: item.name, value: item.value }))"
+ @close="onConfigTypeCancel"
+ @select="({ item }) => onConfigTypeConfirm(item)"
+ />
+ </view>
+</template>
+
+<route lang="jsonc" type="page">
+{
+ "style": {
+ "navigationBarTitleText": "璁惧閰嶇疆",
+ "navigationStyle": "custom"
+ }
+}
+</route>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/device/deviceTimeUtils.mjs b/xiaozhi-esp32-server/main/manager-mobile/src/pages/device/deviceTimeUtils.mjs
new file mode 100644
index 0000000..5a975d5
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/device/deviceTimeUtils.mjs
@@ -0,0 +1,14 @@
+/** @param {unknown} timestamp */
+export function parseDeviceLastConnectedAtTimestamp(timestamp) {
+ if (typeof timestamp !== 'string' || !timestamp.trim()) {
+ return null
+ }
+
+ const milliseconds = Number(timestamp)
+ if (!Number.isFinite(milliseconds)) {
+ return null
+ }
+
+ const date = new Date(milliseconds)
+ return Number.isNaN(date.getTime()) ? null : date
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/device/deviceTimeUtils.test.mjs b/xiaozhi-esp32-server/main/manager-mobile/src/pages/device/deviceTimeUtils.test.mjs
new file mode 100644
index 0000000..60da60d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/device/deviceTimeUtils.test.mjs
@@ -0,0 +1,15 @@
+/* eslint-disable test/no-import-node-test -- this zero-dependency gate intentionally uses Node's built-in runner */
+import assert from 'node:assert/strict'
+import test from 'node:test'
+import { parseDeviceLastConnectedAtTimestamp } from './deviceTimeUtils.mjs'
+
+test('parses the backend Long timestamp serialized as a string', () => {
+ const timestamp = '1783689702000'
+ assert.equal(parseDeviceLastConnectedAtTimestamp(timestamp)?.getTime(), Number(timestamp))
+})
+
+test('rejects missing and malformed device timestamps', () => {
+ assert.equal(parseDeviceLastConnectedAtTimestamp(null), null)
+ assert.equal(parseDeviceLastConnectedAtTimestamp(''), null)
+ assert.equal(parseDeviceLastConnectedAtTimestamp('not-a-timestamp'), null)
+})
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/device/index.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/device/index.vue
new file mode 100644
index 0000000..ce08cab
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/device/index.vue
@@ -0,0 +1,632 @@
+<script lang="ts" setup>
+import type { Device, FirmwareType } from '@/api/device'
+import { computed, onMounted, ref } from 'vue'
+import { useMessage } from 'wot-design-uni/components/wd-message-box'
+import { bindDevice, bindDeviceManual, getBindDevices, getFirmwareTypes, unbindDevice, updateDeviceAutoUpdate } from '@/api/device'
+import { t } from '@/i18n'
+import { toast } from '@/utils/toast'
+import { parseDeviceLastConnectedAtTimestamp } from './deviceTimeUtils.mjs'
+
+defineOptions({
+ name: 'DeviceManage',
+})
+
+const props = withDefaults(defineProps<Props>(), {
+ agentId: 'default',
+})
+
+const actions = [
+ { key: 'code', name: t('manualAddDeviceDialog.bindWithCode') },
+ { key: 'manual', name: t('manualAddDeviceDialog.title') },
+]
+
+// 鎺ユ敹props
+interface Props {
+ agentId?: string
+}
+
+// 鑾峰彇灞忓箷杈圭晫鍒板畨鍏ㄥ尯鍩熻窛绂�
+let safeAreaInsets: any
+let systemInfo: any
+
+// #ifdef MP-WEIXIN
+systemInfo = uni.getWindowInfo()
+safeAreaInsets = systemInfo.safeArea
+ ? {
+ top: systemInfo.safeArea.top,
+ right: systemInfo.windowWidth - systemInfo.safeArea.right,
+ bottom: systemInfo.windowHeight - systemInfo.safeArea.bottom,
+ left: systemInfo.safeArea.left,
+ }
+ : null
+// #endif
+
+// #ifndef MP-WEIXIN
+systemInfo = uni.getSystemInfoSync()
+safeAreaInsets = systemInfo.safeAreaInsets
+// #endif
+
+// 璁惧鏁版嵁
+const deviceList = ref<Device[]>([])
+const firmwareTypes = ref<FirmwareType[]>([])
+const loading = ref(false)
+const isBindDevice = ref(false)
+
+// 鎵嬪姩缁戝畾寮圭獥
+const isManualBindDialog = ref(false)
+const manualBindForm = ref({
+ board: '',
+ appVersion: '',
+ macAddress: '',
+})
+
+// 琛ㄥ崟鏍¢獙閿欒鎻愮ず
+const formErrors = ref({
+ board: '',
+ appVersion: '',
+ macAddress: '',
+})
+
+// MAC鍦板潃姝e垯鏍¢獙
+const macRegex = /^(?:[0-9A-F]{2}[:-]){5}[0-9A-F]{2}$/i
+
+function selectBindMode(row) {
+ if (row.item.key === 'code') {
+ openBindDialog()
+ }
+ else if (row.item.key === 'manual') {
+ // 鎵撳紑寮圭獥鍓嶉噸缃〃鍗曞拰閿欒鎻愮ず
+ manualBindForm.value = {
+ board: '',
+ appVersion: '',
+ macAddress: '',
+ }
+ formErrors.value = {
+ board: '',
+ appVersion: '',
+ macAddress: '',
+ }
+ isManualBindDialog.value = true
+ }
+}
+
+// 娑堟伅缁勪欢
+const message = useMessage()
+
+// 浣跨敤浼犲叆鐨勬櫤鑳戒綋ID
+const currentAgentId = computed(() => {
+ return props.agentId
+})
+
+// 鑾峰彇璁惧鍒楄〃
+async function loadDeviceList() {
+ try {
+ // 妫�鏌ユ槸鍚︽湁褰撳墠閫変腑鐨勬櫤鑳戒綋
+ if (!currentAgentId.value) {
+ deviceList.value = []
+ return
+ }
+
+ loading.value = true
+ const response = await getBindDevices(currentAgentId.value)
+ deviceList.value = response || []
+ }
+ catch (error) {
+ console.error('鑾峰彇璁惧鍒楄〃澶辫触:', error)
+ deviceList.value = []
+ }
+ finally {
+ loading.value = false
+ }
+}
+
+// 鏆撮湶缁欑埗缁勪欢鐨勫埛鏂版柟娉�
+async function refresh() {
+ await loadDeviceList()
+}
+
+// 鑾峰彇璁惧绫诲瀷鍚嶇О
+function getDeviceTypeName(boardKey: string): string {
+ const firmwareType = firmwareTypes.value.find(type => type.key === boardKey)
+ return firmwareType?.name || boardKey
+}
+
+// 鏍煎紡鍖栨椂闂�
+function formatTime(timestamp: string | null) {
+ const date = parseDeviceLastConnectedAtTimestamp(timestamp)
+ if (!date)
+ return t('device.neverConnected')
+ const now = new Date()
+ const diff = now.getTime() - date.getTime()
+
+ if (diff < 60000)
+ return t('device.justNow')
+ if (diff < 3600000)
+ return t('device.minutesAgo', { minutes: Math.floor(diff / 60000) })
+ if (diff < 86400000)
+ return t('device.hoursAgo', { hours: Math.floor(diff / 3600000) })
+ if (diff < 604800000)
+ return t('device.daysAgo', { days: Math.floor(diff / 86400000) })
+
+ return date.toLocaleDateString()
+}
+
+// 鍒囨崲OTA鑷姩鏇存柊
+async function toggleAutoUpdate(device: Device) {
+ try {
+ const newStatus = device.autoUpdate === 1 ? 0 : 1
+ await updateDeviceAutoUpdate(device.id, newStatus)
+ device.autoUpdate = newStatus
+ toast.success(newStatus === 1 ? t('device.otaAutoUpdateEnabled') : t('device.otaAutoUpdateDisabled'))
+ }
+ catch (error: any) {
+ console.error('鏇存柊璁惧OTA鐘舵�佸け璐�:', error)
+ toast.error(t('device.operationFailed'))
+ }
+}
+
+// 瑙g粦璁惧
+async function handleUnbindDevice(device: Device) {
+ try {
+ await unbindDevice(device.id)
+ await loadDeviceList()
+ toast.success(t('device.deviceUnbound'))
+ }
+ catch (error: any) {
+ console.error('瑙g粦璁惧澶辫触:', error)
+ toast.error(t('device.unbindFailed'))
+ }
+}
+
+// 纭瑙g粦璁惧
+function confirmUnbindDevice(device: Device) {
+ message.confirm({
+ title: t('device.unbindDevice'),
+ msg: t('device.confirmUnbindDevice', { macAddress: device.macAddress }),
+ confirmButtonText: t('device.confirmUnbind'),
+ cancelButtonText: t('device.cancel'),
+ }).then(() => {
+ handleUnbindDevice(device)
+ }).catch(() => {
+ // 鐢ㄦ埛鍙栨秷
+ })
+}
+
+// 缁戝畾鏂拌澶�
+async function handleBindDevice(code: string) {
+ try {
+ if (!currentAgentId.value) {
+ toast.error(t('device.pleaseSelectAgent'))
+ return
+ }
+
+ await bindDevice(currentAgentId.value, code.trim())
+ await loadDeviceList()
+ toast.success(t('device.deviceBindSuccess'))
+ }
+ catch (error: any) {
+ console.error('缁戝畾璁惧澶辫触:', error)
+ const errorMessage = error?.message || t('device.bindFailed')
+ toast.error(errorMessage)
+ }
+}
+
+// 鎵撳紑缁戝畾璁惧瀵硅瘽妗�
+function openBindDialog() {
+ message
+ .prompt({
+ title: t('device.bindDevice'),
+ inputPlaceholder: t('device.enterDeviceCode'),
+ inputValue: '',
+ inputPattern: /^\d{6}$/,
+ confirmButtonText: t('device.bindNow'),
+ cancelButtonText: t('device.cancel'),
+ })
+ .then(async (result: any) => {
+ if (result.value && String(result.value).trim()) {
+ await handleBindDevice(String(result.value).trim())
+ }
+ })
+ .catch(() => {
+ // 鐢ㄦ埛鍙栨秷鎿嶄綔
+ })
+}
+
+// 鎵嬪姩缁戝畾璁惧
+async function handleManualBind() {
+ try {
+ // 鍏堟牎楠屾暣涓〃鍗�
+ const isValid = validateForm()
+ if (!isValid) {
+ return
+ }
+
+ if (!currentAgentId.value) {
+ toast.error(t('device.pleaseSelectAgent'))
+ return
+ }
+
+ await bindDeviceManual({
+ agentId: currentAgentId.value,
+ board: manualBindForm.value.board,
+ appVersion: manualBindForm.value.appVersion,
+ macAddress: manualBindForm.value.macAddress,
+ })
+ await loadDeviceList()
+ toast.success(t('manualAddDeviceDialog.addSuccess'))
+ isManualBindDialog.value = false
+ // 閲嶇疆琛ㄥ崟鍜岄敊璇彁绀�
+ manualBindForm.value = {
+ board: '',
+ appVersion: '',
+ macAddress: '',
+ }
+ formErrors.value = {
+ board: '',
+ appVersion: '',
+ macAddress: '',
+ }
+ }
+ catch (error: any) {
+ const errorMessage = error?.message || t('manualAddDeviceDialog.addFailed')
+ toast.error(errorMessage)
+ }
+}
+
+// 鏍¢獙鍗曚釜瀛楁
+function validateField(field: string) {
+ switch (field) {
+ case 'board':
+ if (!manualBindForm.value.board) {
+ formErrors.value.board = t('manualAddDeviceDialog.deviceTypePlaceholder')
+ }
+ else {
+ formErrors.value.board = ''
+ }
+ break
+ case 'appVersion':
+ if (!manualBindForm.value.appVersion) {
+ formErrors.value.appVersion = t('manualAddDeviceDialog.firmwareVersionPlaceholder')
+ }
+ else {
+ formErrors.value.appVersion = ''
+ }
+ break
+ case 'macAddress':
+ if (!manualBindForm.value.macAddress) {
+ formErrors.value.macAddress = t('manualAddDeviceDialog.macAddressPlaceholder')
+ }
+ else if (!macRegex.test(manualBindForm.value.macAddress)) {
+ formErrors.value.macAddress = t('manualAddDeviceDialog.invalidMacAddress')
+ }
+ else {
+ formErrors.value.macAddress = ''
+ }
+ break
+ }
+}
+
+// 娓呴櫎瀛楁閿欒鎻愮ず
+function clearFieldError(field: string) {
+ formErrors.value[field] = ''
+}
+
+// 澶勭悊閫夋嫨鍣ㄥ彉鍖�
+function handlePickerChange() {
+ clearFieldError('board')
+}
+
+// 鏍¢獙鏁翠釜琛ㄥ崟
+function validateForm(): boolean {
+ let isValid = true
+
+ // 鏍¢獙璁惧绫诲瀷
+ if (!manualBindForm.value.board) {
+ formErrors.value.board = t('manualAddDeviceDialog.deviceTypePlaceholder')
+ isValid = false
+ }
+ else {
+ formErrors.value.board = ''
+ }
+
+ // 鏍¢獙鍥轰欢鐗堟湰
+ if (!manualBindForm.value.appVersion) {
+ formErrors.value.appVersion = t('manualAddDeviceDialog.firmwareVersionPlaceholder')
+ isValid = false
+ }
+ else {
+ formErrors.value.appVersion = ''
+ }
+
+ // 鏍¢獙MAC鍦板潃
+ if (!manualBindForm.value.macAddress) {
+ formErrors.value.macAddress = t('manualAddDeviceDialog.macAddressPlaceholder')
+ isValid = false
+ }
+ else if (!macRegex.test(manualBindForm.value.macAddress)) {
+ formErrors.value.macAddress = t('manualAddDeviceDialog.invalidMacAddress')
+ isValid = false
+ }
+ else {
+ formErrors.value.macAddress = ''
+ }
+
+ return isValid
+}
+
+// 鑾峰彇璁惧绫诲瀷鍒楄〃
+async function loadFirmwareTypes() {
+ try {
+ const response = await getFirmwareTypes()
+ firmwareTypes.value = response
+ }
+ catch (error) {
+ console.error('鑾峰彇璁惧绫诲瀷澶辫触:', error)
+ }
+}
+
+onMounted(async () => {
+ // 鏅鸿兘浣撳凡绠�鍖栦负榛樿
+
+ loadFirmwareTypes()
+ loadDeviceList()
+})
+
+// 鏆撮湶鏂规硶缁欑埗缁勪欢
+defineExpose({
+ refresh,
+})
+</script>
+
+<template>
+ <view class="device-container" style="background: #f5f7fb; min-height: 100%;">
+ <!-- 鍔犺浇鐘舵�� -->
+ <view v-if="loading && deviceList.length === 0" class="loading-container">
+ <wd-loading color="#336cff" />
+ <text class="loading-text">
+ {{ t('device.loading') }}
+ </text>
+ </view>
+
+ <!-- 璁惧鍒楄〃 -->
+ <view v-else-if="deviceList.length > 0" class="device-list">
+ <!-- 璁惧鍗$墖鍒楄〃 -->
+ <view class="box-border flex flex-col gap-[24rpx] p-[20rpx]">
+ <view v-for="device in deviceList" :key="device.id">
+ <wd-swipe-action>
+ <view class="cursor-pointer bg-[#fbfbfb] p-[32rpx] transition-all duration-200 active:bg-[#f8f9fa]">
+ <view class="flex items-start justify-between">
+ <view class="flex-1">
+ <view class="mb-[16rpx] flex items-center justify-between">
+ <text class="max-w-[60%] break-all text-[32rpx] text-[#232338] font-semibold">
+ {{ getDeviceTypeName(device.board) }}
+ </text>
+ </view>
+
+ <view class="mb-[20rpx]">
+ <text class="mb-[12rpx] block text-[28rpx] text-[#65686f] leading-[1.4]">
+ {{ t('device.macAddress') }}锛歿{ device.macAddress }}
+ </text>
+ <text class="mb-[12rpx] block text-[28rpx] text-[#65686f] leading-[1.4]">
+ {{ t('device.firmwareVersion') }}锛歿{ device.appVersion }}
+ </text>
+ <text class="block text-[28rpx] text-[#65686f] leading-[1.4]">
+ {{ t('device.lastConnection') }}锛歿{ formatTime(device.lastConnectedAtTimestamp) }}
+ </text>
+ </view>
+
+ <view class="flex items-center justify-between border-[1rpx] border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[16rpx_20rpx]">
+ <text class="text-[28rpx] text-[#232338] font-medium">
+ {{ t('device.otaUpdate') }}
+ </text>
+ <wd-switch
+ :model-value="device.autoUpdate === 1"
+ size="24"
+ @change="toggleAutoUpdate(device)"
+ />
+ </view>
+ </view>
+ </view>
+ </view>
+
+ <template #right>
+ <view class="h-full flex">
+ <view
+ class="h-full min-w-[120rpx] flex items-center justify-center bg-[#ff4d4f] p-x-[32rpx] text-[28rpx] text-white font-medium"
+ @click.stop="confirmUnbindDevice(device)"
+ >
+ <wd-icon name="delete" />
+ <text>{{ t('device.unbind') }}</text>
+ </view>
+ </view>
+ </template>
+ </wd-swipe-action>
+ </view>
+ </view>
+ </view>
+
+ <!-- 绌虹姸鎬� -->
+ <view v-else-if="!loading" class="empty-container">
+ <view class="flex flex-col items-center justify-center p-[100rpx_40rpx] text-center">
+ <wd-icon name="phone" custom-class="text-[120rpx] text-[#d9d9d9] mb-[32rpx]" />
+ <text class="mb-[16rpx] text-[32rpx] text-[#666666] font-medium">
+ {{ t('device.noDevice') }}
+ </text>
+ <text class="text-[26rpx] text-[#999999] leading-[1.5]">
+ {{ t('device.clickToBindFirstDevice') }}
+ </text>
+ </view>
+ </view>
+
+ <!-- FAB 缁戝畾璁惧鎸夐挳 -->
+ <wd-fab type="primary" size="small" icon="add" :draggable="true" :expandable="false" @click="isBindDevice = true" />
+
+ <!-- MessageBox 缁勪欢 -->
+ <wd-message-box />
+ <wd-action-sheet v-model="isBindDevice" :actions="actions" @close="isBindDevice = false" @select="selectBindMode" />
+
+ <!-- 鎵嬪姩缁戝畾璁惧寮圭獥 -->
+ <wd-popup v-model="isManualBindDialog" position="bottom" :close-on-click-modal="false" custom-style="border-radius: 24rpx 24rpx 0 0;">
+ <view class="manual-bind-dialog">
+ <view class="dialog-header">
+ <text class="dialog-title">
+ {{ t('manualAddDeviceDialog.title') }}
+ </text>
+ <wd-icon name="close" size="20" @click="isManualBindDialog = false" />
+ </view>
+
+ <view class="dialog-content">
+ <view class="form-item">
+ <text class="form-label">
+ {{ t('manualAddDeviceDialog.deviceType') }}
+ <text class="required">
+ *
+ </text>
+ </text>
+ <wd-picker
+ v-model="manualBindForm.board"
+ class="custom-wd-picker"
+ :columns="firmwareTypes.map(item => ({ value: item.key, label: item.name }))"
+ :placeholder="t('manualAddDeviceDialog.deviceTypePlaceholder')"
+ :cancel-button-text="t('common.cancel')"
+ :confirm-button-text="t('common.confirm')"
+ @confirm="handlePickerChange"
+ />
+ <text v-if="formErrors.board" class="error-text">
+ {{ formErrors.board }}
+ </text>
+ </view>
+
+ <view class="form-item">
+ <text class="form-label">
+ {{ t('manualAddDeviceDialog.firmwareVersion') }}
+ <text class="required">
+ *
+ </text>
+ </text>
+ <wd-input
+ v-model="manualBindForm.appVersion"
+ :placeholder="t('manualAddDeviceDialog.firmwareVersionPlaceholder')"
+ @input="clearFieldError('appVersion')"
+ @blur="validateField('appVersion')"
+ />
+ <text v-if="formErrors.appVersion" class="error-text">
+ {{ formErrors.appVersion }}
+ </text>
+ </view>
+
+ <view class="form-item">
+ <text class="form-label">
+ {{ t('manualAddDeviceDialog.macAddress') }}
+ <text class="required">
+ *
+ </text>
+ </text>
+ <wd-input
+ v-model="manualBindForm.macAddress"
+ :placeholder="t('manualAddDeviceDialog.macAddressPlaceholder')"
+ @input="validateField('macAddress')"
+ @blur="validateField('macAddress')"
+ />
+ <text v-if="formErrors.macAddress" class="error-text">
+ {{ formErrors.macAddress }}
+ </text>
+ </view>
+ </view>
+
+ <view class="dialog-footer">
+ <wd-button block type="primary" @click="handleManualBind">
+ {{ t('manualAddDeviceDialog.confirm') }}
+ </wd-button>
+ </view>
+ </view>
+ </wd-popup>
+ </view>
+</template>
+
+<style scoped>
+.device-container {
+ position: relative;
+}
+
+.loading-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 100rpx 40rpx;
+}
+
+.loading-text {
+ margin-top: 20rpx;
+ font-size: 28rpx;
+ color: #666666;
+}
+
+:deep(.wd-swipe-action) {
+ border-radius: 20rpx;
+ overflow: hidden;
+ box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
+ border: 1rpx solid #eeeeee;
+}
+::v-deep .wd-action-sheet__popup,
+::v-deep .wd-popup {
+ z-index: 100 !important;
+}
+.custom-wd-picker ::v-deep .wd-picker__cell {
+ padding-left: 0 !important;
+}
+
+:deep(.wd-icon) {
+ font-size: 32rpx;
+}
+
+.manual-bind-dialog {
+ padding: 32rpx;
+ background: #ffffff;
+}
+
+.dialog-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 32rpx;
+}
+
+.dialog-title {
+ font-size: 36rpx;
+ font-weight: 600;
+ color: #232338;
+}
+
+.dialog-content {
+ margin-bottom: 32rpx;
+}
+
+.form-item {
+ margin-bottom: 24rpx;
+}
+
+.form-label {
+ display: block;
+ font-size: 28rpx;
+ color: #65686f;
+ margin-bottom: 12rpx;
+}
+
+.required {
+ color: #ff4d4f;
+ margin-left: 4rpx;
+}
+
+.error-text {
+ display: block;
+ font-size: 24rpx;
+ color: #ff4d4f;
+ margin-top: 8rpx;
+}
+
+.dialog-footer {
+ padding-top: 16rpx;
+}
+</style>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/forgot-password/index.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/forgot-password/index.vue
new file mode 100644
index 0000000..9bdd303
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/forgot-password/index.vue
@@ -0,0 +1,850 @@
+<route lang="jsonc" type="page">
+{
+ "layout": "default",
+ "style": {
+ "navigationStyle": "custom",
+ "navigationBarTitleText": "ForgotPassword"
+ }
+}
+</route>
+
+<script lang="ts" setup>
+import { onLoad } from '@dcloudio/uni-app'
+import { computed, onMounted, ref } from 'vue'
+// 瀵煎叆API鎺ュ彛
+import { retrievePassword, sendSmsCode } from '@/api/auth'
+// 瀵煎叆鍥介檯鍖栫浉鍏冲姛鑳�
+import { initI18n, t } from '@/i18n'
+import { useConfigStore } from '@/store'
+import { getEnvBaseUrl, sm2Encrypt } from '@/utils'
+import { toast } from '@/utils/toast'
+
+// 鑾峰彇灞忓箷杈圭晫鍒板畨鍏ㄥ尯鍩熻窛绂�
+let safeAreaInsets
+let systemInfo
+
+// #ifdef MP-WEIXIN
+// 寰俊灏忕▼搴忎娇鐢ㄦ柊鐨凙PI
+systemInfo = uni.getWindowInfo()
+safeAreaInsets = systemInfo.safeArea
+ ? {
+ top: systemInfo.safeArea.top,
+ right: systemInfo.windowWidth - systemInfo.safeArea.right,
+ bottom: systemInfo.windowHeight - systemInfo.safeArea.bottom,
+ left: systemInfo.safeArea.left,
+ }
+ : null
+// #endif
+
+// #ifndef MP-WEIXIN
+// 鍏朵粬骞冲彴缁х画浣跨敤uni API
+systemInfo = uni.getSystemInfoSync()
+safeAreaInsets = systemInfo.safeAreaInsets
+// #endif
+
+// 琛ㄥ崟鏁版嵁
+interface ForgotPasswordData {
+ mobile: string
+ captcha: string
+ captchaId: string
+ mobileCaptcha: string
+ newPassword: string
+ confirmPassword: string
+ areaCode: string
+}
+
+const formData = ref<ForgotPasswordData>({
+ mobile: '',
+ captcha: '',
+ captchaId: '',
+ mobileCaptcha: '',
+ newPassword: '',
+ confirmPassword: '',
+ areaCode: '+86',
+})
+
+// 楠岃瘉鐮佸浘鐗�
+const captchaImage = ref('')
+const loading = ref(false)
+
+// 鑾峰彇閰嶇疆store
+const configStore = useConfigStore()
+
+// State for area code action sheet
+const showAreaCodeSheet = ref(false)
+const selectedAreaCode = ref('+86')
+// 鐭俊楠岃瘉鐮佸�掕鏃�
+const smsCountdown = ref(0)
+const smsLoading = ref(false)
+const areaCodeList = computed(() =>
+ (configStore.config.mobileAreaList || []).map((item) => {
+ return {
+ value: item.key,
+ label: `${item.name} (${item.key})`,
+ }
+ }),
+)
+
+const canSendMobileCaptcha = computed(() => {
+ const mobile = formData.value.mobile
+ const phoneRegex = /^1[3-9]\d{9}$/
+ return phoneRegex.test(mobile) && smsCountdown.value === 0
+})
+
+// SM2鍏挜
+const sm2PublicKey = computed(() => {
+ return configStore.config.sm2PublicKey
+})
+
+// 鎵撳紑鍖哄彿閫夋嫨寮圭獥
+function openAreaCodeSheet() {
+ showAreaCodeSheet.value = true
+}
+
+// 閫夋嫨鍖哄彿
+function selectAreaCode(item: { value: string, label: string }) {
+ selectedAreaCode.value = item.value
+ formData.value.areaCode = item.value
+ showAreaCodeSheet.value = false
+}
+
+// 鍏抽棴鍖哄彿閫夋嫨寮圭獥
+function closeAreaCodeSheet() {
+ showAreaCodeSheet.value = false
+}
+
+// 鐢熸垚UUID
+function generateUUID() {
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
+ const r = (Math.random() * 16) | 0
+ const v = c === 'x' ? r : (r & 0x3) | 0x8
+ return v.toString(16)
+ })
+}
+
+// 鑾峰彇鍥惧舰楠岃瘉鐮�
+async function refreshCaptcha() {
+ const uuid = generateUUID()
+ formData.value.captchaId = uuid
+ captchaImage.value = `${getEnvBaseUrl()}/user/captcha?uuid=${uuid}&t=${Date.now()}`
+}
+
+// 鍙戦�佺煭淇¢獙璇佺爜
+async function handleSendSmsCode() {
+ // 鎵嬫満鍙锋牸寮忛獙璇�
+ const phoneRegex = /^1[3-9]\d{9}$/
+ if (!phoneRegex.test(formData.value.mobile)) {
+ toast.warning(t('retrievePassword.inputCorrectMobile'))
+ return
+ }
+
+ if (!formData.value.captcha) {
+ toast.warning(t('retrievePassword.captchaRequired'))
+ return
+ }
+
+ try {
+ smsLoading.value = true
+ // 灏嗘墜鏈哄彿杞崲涓哄浗闄呮牸寮�
+ const internationalPhone = formData.value.areaCode + formData.value.mobile
+ // 璋冪敤鍙戦�佺煭淇¢獙璇佺爜API
+ await sendSmsCode({
+ phone: internationalPhone,
+ captcha: formData.value.captcha,
+ captchaId: formData.value.captchaId,
+ })
+
+ toast.success(t('retrievePassword.captchaSendSuccess'))
+
+ // 寮�濮嬪�掕鏃�
+ smsCountdown.value = 60
+ const timer = setInterval(() => {
+ smsCountdown.value--
+ if (smsCountdown.value <= 0) {
+ clearInterval(timer)
+ }
+ }, 1000)
+ }
+ catch (error: any) {
+ // 澶勭悊楠岃瘉鐮侀敊璇�
+ if (error.message.includes('璇锋眰閿欒[10067]')) {
+ toast.warning(t('login.captchaError'))
+ }
+ // 鍙戦�佸け璐ラ噸鏂拌幏鍙栧浘褰㈤獙璇佺爜
+ refreshCaptcha()
+ }
+ finally {
+ smsLoading.value = false
+ }
+}
+
+// 閲嶇疆瀵嗙爜
+async function handleResetPassword() {
+ // 琛ㄥ崟楠岃瘉
+ if (!formData.value.mobile) {
+ toast.warning(t('retrievePassword.mobileRequired'))
+ return
+ }
+
+ // 鎵嬫満鍙锋牸寮忛獙璇�
+ const phoneRegex = /^1[3-9]\d{9}$/
+ if (!phoneRegex.test(formData.value.mobile)) {
+ toast.warning(t('retrievePassword.inputCorrectMobile'))
+ return
+ }
+
+ // 灏嗘墜鏈哄彿杞崲涓哄浗闄呮牸寮�
+ const internationalPhone = formData.value.areaCode + formData.value.mobile
+
+ if (!formData.value.captcha) {
+ toast.warning(t('retrievePassword.captchaRequired'))
+ return
+ }
+
+ if (!formData.value.mobileCaptcha) {
+ toast.warning(t('retrievePassword.mobileCaptchaRequired'))
+ return
+ }
+
+ if (!formData.value.newPassword) {
+ toast.warning(t('retrievePassword.newPasswordRequired'))
+ return
+ }
+
+ if (!formData.value.confirmPassword) {
+ toast.warning(t('retrievePassword.confirmNewPasswordRequired'))
+ return
+ }
+
+ if (formData.value.newPassword !== formData.value.confirmPassword) {
+ toast.warning(t('retrievePassword.passwordsNotMatch'))
+ return
+ }
+
+ try {
+ loading.value = true
+
+ // 妫�鏌M2鍏挜鏄惁閰嶇疆
+ if (!sm2PublicKey.value) {
+ toast.warning(t('sm2.publicKeyNotConfigured'))
+ return
+ }
+
+ // 鍔犲瘑瀵嗙爜
+ let encryptedPassword
+ try {
+ // 鎷兼帴鍥惧舰楠岃瘉鐮佸拰鏂板瘑鐮佽繘琛屽姞瀵�
+ const captchaAndPassword = formData.value.captcha + formData.value.newPassword
+ encryptedPassword = sm2Encrypt(sm2PublicKey.value, captchaAndPassword)
+ }
+ catch (error) {
+ console.error('瀵嗙爜鍔犲瘑澶辫触:', error)
+ toast.warning(t('sm2.encryptionFailed'))
+ return
+ }
+
+ // 璋冪敤閲嶇疆瀵嗙爜API
+ await retrievePassword({
+ phone: internationalPhone,
+ code: formData.value.mobileCaptcha,
+ password: encryptedPassword,
+ captchaId: formData.value.captchaId,
+ })
+
+ toast.success(t('retrievePassword.passwordUpdateSuccess'))
+
+ // 璺宠浆鍒扮櫥褰曢〉
+ setTimeout(() => {
+ uni.redirectTo({
+ url: '/pages/login/index',
+ })
+ }, 1000)
+ }
+ catch (error: any) {
+ // 澶勭悊楠岃瘉鐮侀敊璇�
+ if (error.message.includes('璇锋眰閿欒[10067]')) {
+ toast.warning(t('login.captchaError'))
+ }
+ // 閲嶇疆澶辫触閲嶆柊鑾峰彇楠岃瘉鐮�
+ refreshCaptcha()
+ }
+ finally {
+ loading.value = false
+ }
+}
+
+// 杩斿洖鐧诲綍
+function goBack() {
+ uni.redirectTo({
+ url: '/pages/login/index',
+ })
+}
+
+// 椤甸潰鍔犺浇鏃惰幏鍙栭獙璇佺爜
+onLoad(() => {
+ refreshCaptcha()
+})
+
+// 缁勪欢鎸傝浇鏃剁‘淇濋厤缃凡鍔犺浇
+onMounted(async () => {
+ if (!configStore.config.name) {
+ try {
+ await configStore.fetchPublicConfig()
+ }
+ catch (error) {
+ console.error('鑾峰彇閰嶇疆澶辫触:', error)
+ }
+ }
+ // 鍒濆鍖栧浗闄呭寲
+ initI18n()
+})
+</script>
+
+<template>
+ <view
+ class="app-container box-border h-screen w-full"
+ :style="{ paddingTop: `${safeAreaInsets?.top}px` }"
+ >
+ <view class="header">
+ <view class="back-button" @click="goBack">
+ <wd-icon name="arrow-left" custom-class="back-icon" />
+ </view>
+ <view class="logo-section">
+ <wd-img :width="80" :height="80" round src="/static/logo.png" class="logo" />
+ <text class="welcome-text">
+ {{ t("retrievePassword.title") }}
+ </text>
+ <text class="subtitle">
+ {{ t("retrievePassword.subtitle") }}
+ </text>
+ </view>
+ </view>
+
+ <view class="form-container">
+ <view class="form">
+ <!-- 鎵嬫満鍙疯緭鍏� -->
+ <view class="input-group">
+ <view class="input-wrapper mobile-wrapper">
+ <view class="area-code-selector" @click="openAreaCodeSheet">
+ <text class="area-code-text">
+ {{ selectedAreaCode }}
+ </text>
+ <wd-icon name="arrow-down" custom-class="area-code-arrow" />
+ </view>
+ <view class="mobile-input-wrapper">
+ <wd-input
+ v-model="formData.mobile"
+ custom-class="styled-input"
+ no-border
+ :placeholder="t('retrievePassword.mobilePlaceholder')"
+ type="number"
+ :maxlength="11"
+ />
+ </view>
+ </view>
+ </view>
+
+ <!-- 鍥惧舰楠岃瘉鐮� -->
+ <view class="input-group">
+ <view class="input-wrapper captcha-wrapper">
+ <wd-input
+ v-model="formData.captcha"
+ custom-class="styled-input"
+ no-border
+ :placeholder="t('retrievePassword.captchaPlaceholder')"
+ :maxlength="6"
+ />
+ <view class="captcha-image" @click="refreshCaptcha">
+ <image :src="captchaImage" class="captcha-img" />
+ </view>
+ </view>
+ </view>
+
+ <!-- 鐭俊楠岃瘉鐮� -->
+ <view class="input-group">
+ <view class="input-wrapper sms-wrapper">
+ <wd-input
+ v-model="formData.mobileCaptcha"
+ custom-class="styled-input"
+ no-border
+ :placeholder="t('retrievePassword.mobileCaptchaPlaceholder')"
+ :maxlength="6"
+ />
+ <view class="sms-button" @click="handleSendSmsCode">
+ <text class="sms-text">
+ {{
+ smsCountdown > 0
+ ? `${smsCountdown}s`
+ : t("retrievePassword.getMobileCaptcha")
+ }}
+ </text>
+ </view>
+ </view>
+ </view>
+
+ <!-- 鏂板瘑鐮� -->
+ <view class="input-group">
+ <view class="input-wrapper">
+ <wd-input
+ v-model="formData.newPassword"
+ custom-class="styled-input"
+ no-border
+ :placeholder="t('retrievePassword.newPasswordPlaceholder')"
+ show-password
+ :maxlength="20"
+ />
+ </view>
+ </view>
+
+ <!-- 纭鏂板瘑鐮� -->
+ <view class="input-group">
+ <view class="input-wrapper">
+ <wd-input
+ v-model="formData.confirmPassword"
+ custom-class="styled-input"
+ no-border
+ :placeholder="t('retrievePassword.confirmNewPasswordPlaceholder')"
+ show-password
+ :maxlength="20"
+ />
+ </view>
+ </view>
+
+ <!-- 閲嶇疆瀵嗙爜鎸夐挳 -->
+ <view class="reset-btn" @click="handleResetPassword">
+ {{ loading ? t("common.loading") : t("retrievePassword.resetButton") }}
+ </view>
+
+ <!-- 杩斿洖鐧诲綍閾炬帴 -->
+ <view class="back-login-hint" @click="goBack">
+ <text class="hint-text">
+ {{ t("retrievePassword.goToLogin") }}
+ </text>
+ </view>
+ </view>
+ </view>
+
+ <!-- 鍖哄彿閫夋嫨寮圭獥 -->
+ <wd-action-sheet
+ v-model="showAreaCodeSheet"
+ :title="t('login.selectCountry')"
+ :close-on-click-modal="true"
+ @close="closeAreaCodeSheet"
+ >
+ <view class="area-code-sheet">
+ <scroll-view scroll-y class="area-code-list">
+ <view
+ v-for="item in areaCodeList"
+ :key="item.value"
+ class="area-code-item"
+ :class="{ selected: selectedAreaCode === item.value }"
+ @click="selectAreaCode(item)"
+ >
+ <view class="area-info">
+ <text class="area-name">
+ {{ item.label }}
+ </text>
+ </view>
+ <wd-icon
+ v-if="selectedAreaCode === item.value"
+ name="check"
+ custom-class="check-icon"
+ />
+ </view>
+ </scroll-view>
+ <view class="sheet-footer">
+ <wd-button
+ type="primary"
+ custom-class="confirm-btn"
+ @click="closeAreaCodeSheet"
+ >
+ {{ t("login.confirm") }}
+ </wd-button>
+ </view>
+ </view>
+ </wd-action-sheet>
+ </view>
+</template>
+
+<style lang="scss" scoped>
+.app-container {
+ background: linear-gradient(145deg, #f5f8fd, #6baaff, #9ebbfc, #f5f8fd);
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ overflow: hidden;
+ min-height: 100vh;
+
+ &::before {
+ content: '';
+ position: absolute;
+ top: -50%;
+ left: -50%;
+ width: 200%;
+ height: 200%;
+ background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
+ animation: float 6s ease-in-out infinite;
+ }
+}
+
+@keyframes float {
+ 0%,
+ 100% {
+ transform: translateY(0px) rotate(0deg);
+ }
+ 50% {
+ transform: translateY(-20px) rotate(180deg);
+ }
+}
+
+.header {
+ flex: 0 0 auto;
+ min-height: 280rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 15% 0 40rpx 0;
+ position: relative;
+
+ .back-button {
+ position: absolute;
+ left: 30rpx;
+ top: 50rpx;
+ width: 60rpx;
+ height: 60rpx;
+ border-radius: 50%;
+ background: rgba(255, 255, 255, 0.2);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ backdrop-filter: blur(10rpx);
+ border: 1rpx solid rgba(255, 255, 255, 0.3);
+
+ .back-icon {
+ font-size: 32rpx;
+ color: #ffffff;
+ }
+ }
+
+ .logo-section {
+ text-align: center;
+
+ .logo {
+ margin-bottom: 20rpx;
+ box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.1);
+ }
+
+ .welcome-text {
+ display: block;
+ color: #ffffff;
+ font-size: 40rpx;
+ font-weight: 600;
+ margin-bottom: 12rpx;
+ text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
+ }
+
+ .subtitle {
+ display: block;
+ color: rgba(255, 255, 255, 0.8);
+ font-size: 26rpx;
+ font-weight: 400;
+ }
+ }
+}
+
+.form-container {
+ padding: 0 40rpx 40rpx 40rpx;
+ flex: 1;
+ display: flex;
+ align-items: flex-start;
+
+ .form {
+ width: 100%;
+ background: rgba(255, 255, 255, 0.95);
+ border-radius: 24rpx;
+ padding: 40rpx 30rpx 30rpx 30rpx;
+ backdrop-filter: blur(10rpx);
+ box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.1);
+ border: 1rpx solid rgba(255, 255, 255, 0.2);
+ max-height: calc(100vh - 350rpx);
+ overflow-y: auto;
+
+ .input-group {
+ margin-bottom: 24rpx;
+
+ .input-wrapper {
+ position: relative;
+ background: #f8f9fa;
+ border-radius: 16rpx;
+ padding: 20rpx 16rpx;
+ border: 2rpx solid #e9ecef;
+ transition: all 0.3s ease;
+ display: flex;
+ align-items: center;
+
+ &:focus-within {
+ border-color: #667eea;
+ background: #ffffff;
+ box-shadow: 0 0 0 6rpx rgba(102, 126, 234, 0.1);
+ }
+
+ &.mobile-wrapper {
+ padding: 0;
+ background: transparent;
+ border: none;
+ display: flex;
+ gap: 20rpx;
+
+ .area-code-selector {
+ flex: 0 0 160rpx;
+ background: #f8f9fa;
+ border-radius: 16rpx;
+ padding: 20rpx 16rpx;
+ border: 2rpx solid #e9ecef;
+ height: 45rpx;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ cursor: pointer;
+ transition: all 0.3s ease;
+
+ &:hover {
+ border-color: #667eea;
+ background: #ffffff;
+ box-shadow: 0 0 0 6rpx rgba(102, 126, 234, 0.1);
+ }
+
+ .area-code-text {
+ font-size: 28rpx;
+ color: #333333;
+ font-weight: 500;
+ }
+
+ :deep(.area-code-arrow) {
+ font-size: 24rpx;
+ color: #999999;
+ transition: transform 0.3s ease;
+ }
+ }
+
+ .mobile-input-wrapper {
+ flex: 1;
+ background: #f8f9fa;
+ border-radius: 16rpx;
+ padding: 20rpx 16rpx;
+ border: 2rpx solid #e9ecef;
+ transition: all 0.3s ease;
+
+ &:focus-within {
+ border-color: #667eea;
+ background: #ffffff;
+ box-shadow: 0 0 0 6rpx rgba(102, 126, 234, 0.1);
+ }
+ }
+ }
+
+ &.captcha-wrapper {
+ .captcha-image {
+ margin-left: 20rpx;
+ width: 150rpx;
+ height: 60rpx;
+ border-radius: 8rpx;
+ overflow: hidden;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: #e9ecef;
+ border: 1rpx solid #ddd;
+
+ .captcha-img {
+ width: 100%;
+ height: 100%;
+ }
+ }
+ }
+
+ &.sms-wrapper {
+ .sms-button {
+ margin-left: 20rpx;
+ width: 180rpx;
+ height: 60rpx;
+ border-radius: 12rpx;
+ background: #667eea;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: all 0.3s ease;
+
+ &:active {
+ background: #5a6fd8;
+ transform: scale(0.98);
+ }
+
+ .sms-text {
+ font-size: 24rpx;
+ color: #ffffff;
+ font-weight: 500;
+ }
+ }
+ }
+
+ :deep(.styled-input) {
+ background: transparent !important;
+ border: none !important;
+ outline: none !important;
+ font-size: 32rpx;
+ color: #333333;
+ flex: 1;
+
+ &::placeholder {
+ color: #999999;
+ font-size: 28rpx;
+ }
+ }
+ }
+ }
+
+ .reset-btn {
+ width: 100%;
+ height: 80rpx;
+ border: none;
+ border-radius: 16rpx;
+ font-size: 30rpx;
+ font-weight: 600;
+ color: #ffffff;
+ margin-bottom: 30rpx;
+ box-shadow: 0 8rpx 24rpx rgba(102, 126, 234, 0.3);
+ transition: all 0.3s ease;
+ background-color: var(--wot-button-primary-bg-color, var(--wot-color-theme, #4d80f0));
+ text-align: center;
+ line-height: 80rpx;
+
+ &:active {
+ transform: translateY(2rpx);
+ box-shadow: 0 4rpx 12rpx rgba(102, 126, 234, 0.3);
+ }
+
+ &:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ }
+ }
+
+ .back-login-hint {
+ text-align: center;
+
+ .hint-text {
+ color: #667eea;
+ font-size: 26rpx;
+ font-weight: 500;
+ cursor: pointer;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+ }
+}
+
+// 鍖哄彿閫夋嫨寮圭獥鏍峰紡
+.area-code-sheet {
+ background: #ffffff;
+ border-radius: 24rpx 24rpx 0 0;
+ overflow: hidden;
+
+ .sheet-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 40rpx 40rpx 20rpx 40rpx;
+ border-bottom: 1rpx solid #f0f0f0;
+
+ .sheet-title {
+ font-size: 36rpx;
+ font-weight: 600;
+ color: #333333;
+ }
+
+ :deep(.close-icon) {
+ font-size: 32rpx;
+ color: #999999;
+ cursor: pointer;
+ padding: 10rpx;
+
+ &:hover {
+ color: #333333;
+ }
+ }
+ }
+
+ .area-code-list {
+ max-height: 60vh;
+ padding: 0 40rpx;
+
+ .area-code-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 32rpx 0;
+ border-bottom: 1rpx solid #f8f9fa;
+ cursor: pointer;
+ transition: background-color 0.3s ease;
+
+ &:hover {
+ background-color: #f8f9fa;
+ }
+
+ &.selected {
+ background-color: rgba(102, 126, 234, 0.05);
+
+ .area-name {
+ color: #667eea;
+ font-weight: 500;
+ }
+
+ .area-code {
+ color: #667eea;
+ }
+ }
+
+ .area-info {
+ display: flex;
+ flex-direction: column;
+ gap: 8rpx;
+
+ .area-name {
+ font-size: 32rpx;
+ color: #333333;
+ }
+
+ .area-code {
+ font-size: 28rpx;
+ color: #666666;
+ }
+ }
+
+ :deep(.check-icon) {
+ font-size: 32rpx;
+ color: #667eea;
+ }
+ }
+ }
+
+ .sheet-footer {
+ padding: 30rpx 40rpx 40rpx 40rpx;
+ border-top: 1rpx solid #f0f0f0;
+
+ :deep(.confirm-btn) {
+ width: 100%;
+ height: 88rpx;
+ border-radius: 16rpx;
+ font-size: 32rpx;
+ font-weight: 600;
+ }
+ }
+}
+</style>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/index/index.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/index/index.vue
new file mode 100644
index 0000000..4d73a15
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/index/index.vue
@@ -0,0 +1,804 @@
+<!-- 浣跨敤 type="home" 灞炴�ц缃椤碉紝鍏朵粬椤甸潰涓嶉渶瑕佽缃紝榛樿涓簆age -->
+<route lang="jsonc" type="home">
+{
+ "layout": "tabbar",
+ "style": {
+ // 'custom' 琛ㄧず寮�鍚嚜瀹氫箟瀵艰埅鏍忥紝榛樿 'default'
+ "navigationStyle": "custom",
+ "navigationBarTitleText": "棣栭〉"
+ }
+}
+</route>
+
+<script lang="ts" setup>
+import type { Agent } from '@/api/agent/types'
+import { computed, onMounted, ref } from 'vue'
+// 鍦ㄧ粍浠舵寕杞藉悗璁剧疆瀵艰埅鏍忔爣棰�
+import { useMessage } from 'wot-design-uni/components/wd-message-box'
+import useZPaging from 'z-paging/components/z-paging/js/hooks/useZPaging.js'
+import { createAgent, deleteAgent, getAgentList } from '@/api/agent/agent'
+import { t } from '@/i18n'
+import { toast } from '@/utils/toast'
+
+defineOptions({
+ name: 'Home',
+})
+
+// 鑾峰彇灞忓箷杈圭晫鍒板畨鍏ㄥ尯鍩熻窛绂�
+let safeAreaInsets: any
+let systemInfo: any
+
+// #ifdef MP-WEIXIN
+// 寰俊灏忕▼搴忎娇鐢ㄦ柊鐨凙PI
+systemInfo = uni.getWindowInfo()
+safeAreaInsets = systemInfo.safeArea
+ ? {
+ top: systemInfo.safeArea.top,
+ right: systemInfo.windowWidth - systemInfo.safeArea.right,
+ bottom: systemInfo.windowHeight - systemInfo.safeArea.bottom,
+ left: systemInfo.safeArea.left,
+ }
+ : null
+// #endif
+
+// #ifndef MP-WEIXIN
+// 鍏朵粬骞冲彴缁х画浣跨敤uni API
+systemInfo = uni.getSystemInfoSync()
+safeAreaInsets = systemInfo.safeAreaInsets
+// #endif
+
+// 鏅鸿兘浣撴暟鎹�
+const agentList = ref<Agent[]>([])
+const pagingRef = ref()
+useZPaging(pagingRef)
+// 娑堟伅缁勪欢
+const message = useMessage()
+const deleteDialogVisible = ref(false)
+const deleteTargetAgent = ref<Agent | null>(null)
+const deleteConfirmText = ref('')
+const deleteAgentLoading = ref(false)
+
+const isDeleteAgentNameMatched = computed(() => {
+ return !!deleteTargetAgent.value?.agentName && deleteConfirmText.value === deleteTargetAgent.value.agentName
+})
+
+// z-paging鏌ヨ鍒楄〃鏁版嵁
+async function queryList(pageNo: number, pageSize: number) {
+ try {
+ console.log('z-paging鑾峰彇鏅鸿兘浣撳垪琛�')
+
+ const response = await getAgentList()
+
+ // 鏇存柊鏈湴鍒楄〃
+ agentList.value = response
+
+ // 鐩存帴杩斿洖鍏ㄩ儴鏁版嵁锛屼笉闇�瑕佸垎椤靛鐞�
+ pagingRef.value.complete(response)
+ }
+ catch (error) {
+ console.error('鑾峰彇鏅鸿兘浣撳垪琛ㄥけ璐�:', error)
+ // 鍛婄煡z-paging鏁版嵁鍔犺浇澶辫触
+ pagingRef.value.complete(false)
+ }
+}
+
+// 鍒涘缓鏅鸿兘浣�
+async function handleCreateAgent(agentName: string) {
+ try {
+ await createAgent({ agentName: agentName.trim() })
+ // 鍒涘缓鎴愬姛鍚庡埛鏂板垪琛�
+ pagingRef.value.reload()
+ toast.success(`${t('home.agentName')}"${agentName}"${t('message.saveSuccess')}`)
+ }
+ catch (error: any) {
+ console.error('鍒涘缓鏅鸿兘浣撳け璐�:', error)
+ const errorMessage = error?.message || t('message.saveFail')
+ toast.error(errorMessage)
+ }
+}
+
+function openDeleteAgentDialog(agent: Agent) {
+ if (!agent.id || !agent.agentName) {
+ toast.error(t('home.deleteAgentMissingInfo'))
+ return
+ }
+
+ deleteTargetAgent.value = agent
+ deleteConfirmText.value = ''
+ deleteDialogVisible.value = true
+}
+
+function closeDeleteAgentDialog() {
+ if (deleteAgentLoading.value)
+ return
+
+ deleteDialogVisible.value = false
+ deleteTargetAgent.value = null
+ deleteConfirmText.value = ''
+}
+
+function handleDeleteAgentPaste() {
+ toast.warning(t('home.deleteAgentPasteForbidden'))
+}
+
+// 鍒犻櫎鏅鸿兘浣�
+async function handleDeleteAgent() {
+ if (!deleteTargetAgent.value || !isDeleteAgentNameMatched.value || deleteAgentLoading.value)
+ return
+
+ try {
+ deleteAgentLoading.value = true
+ await deleteAgent(deleteTargetAgent.value.id)
+ // 鍒犻櫎鎴愬姛鍚庡埛鏂板垪琛�
+ pagingRef.value.reload()
+ toast.success(t('home.deleteAgentSuccess'))
+ deleteDialogVisible.value = false
+ deleteTargetAgent.value = null
+ deleteConfirmText.value = ''
+ }
+ catch (error: any) {
+ console.error('鍒犻櫎鏅鸿兘浣撳け璐�:', error)
+ const errorMessage = error?.message || t('message.deleteFail')
+ toast.error(errorMessage)
+ }
+ finally {
+ deleteAgentLoading.value = false
+ }
+}
+
+// 杩涘叆缂栬緫椤甸潰
+function goToEditAgent(agent: Agent) {
+ // 浼犻�掓櫤鑳戒綋ID鍒扮紪杈戦〉闈�
+ uni.navigateTo({
+ url: `/pages/agent/index?agentId=${agent.id}`,
+ })
+}
+
+// 鐐瑰嚮鍗$墖杩涘叆缂栬緫
+function handleCardClick(agent: Agent) {
+ goToEditAgent(agent)
+}
+
+// 鎵撳紑鍒涘缓瀵硅瘽妗�
+function openCreateDialog() {
+ message
+ .prompt({
+ title: t('home.dialogTitle'),
+ msg: '',
+ inputPlaceholder: t('home.inputPlaceholder'),
+ inputValue: '',
+ inputPattern: /^.{1,64}$/,
+ inputError: t('home.createError'),
+ confirmButtonText: t('home.createNow'),
+ cancelButtonText: t('common.cancel'),
+ })
+ .then(async (result: any) => {
+ if (result.value && String(result.value).trim()) {
+ await handleCreateAgent(String(result.value).trim())
+ }
+ })
+ .catch(() => {
+ // 鐢ㄦ埛鍙栨秷鎿嶄綔
+ })
+}
+
+// 鏍煎紡鍖栨椂闂�
+function formatTime(timeStr: string) {
+ const date = new Date(timeStr)
+ const now = new Date()
+ const diff = now.getTime() - date.getTime()
+
+ if (diff < 60000)
+ return t('home.justNow')
+ if (diff < 3600000)
+ return `${Math.floor(diff / 60000)}${t('home.minutesAgo')}`
+ if (diff < 86400000)
+ return `${Math.floor(diff / 3600000)}${t('home.hoursAgo')}`
+ return `${Math.floor(diff / 86400000)}${t('home.daysAgo')}`
+}
+
+// 椤甸潰鏄剧ず鏃跺埛鏂板垪琛�
+onShow(() => {
+ console.log('棣栭〉 onShow锛屽埛鏂版櫤鑳戒綋鍒楄〃')
+ if (pagingRef.value) {
+ pagingRef.value.refresh()
+ }
+})
+
+onMounted(() => {
+ uni.setNavigationBarTitle({
+ title: t('home.pageTitle'),
+ })
+})
+</script>
+
+<template>
+ <z-paging
+ ref="pagingRef" v-model="agentList" :refresher-enabled="true" :auto-show-back-to-top="true"
+ :loading-more-enabled="false" :show-loading-more="false" :hide-empty-view="false" :empty-view-text="t('home.emptyState')"
+ empty-view-img="" :refresher-threshold="80" :back-to-top-style="{
+ backgroundColor: '#fff',
+ borderRadius: '50%',
+ width: '56px',
+ height: '56px',
+ }" @query="queryList"
+ >
+ <!-- 鍥哄畾鍦ㄩ《閮ㄧ殑妯箙鍖哄煙 -->
+ <template #top>
+ <view class="banner-section" :style="{ paddingTop: `${safeAreaInsets?.top + 100}rpx` }">
+ <view class="banner-content">
+ <view class="welcome-info">
+ <text class="greeting">
+ {{ t('home.greeting') }}
+ </text>
+ <text class="subtitle">
+ {{ t('home.subtitle') }} <text class="highlight">
+ {{ t('home.wonderfulDay') }}
+ </text>
+ </text>
+ </view>
+ <view class="wave-decoration">
+ <!-- 娣诲姞娉㈡氮瑁呴グ -->
+ <view class="wave" />
+ <view class="wave wave-2" />
+ </view>
+ </view>
+ </view>
+
+ <!-- 鍐呭鍖哄煙寮�濮嬫爣璇� -->
+ <view class="content-section-header" />
+ </template>
+
+ <!-- 鏅鸿兘浣撳崱鐗囧垪琛� -->
+ <view class="agent-list">
+ <view v-for="agent in agentList" :key="agent.id" class="agent-item">
+ <wd-swipe-action>
+ <view class="simple-card" @click="handleCardClick(agent)">
+ <view class="card-content">
+ <view class="card-main">
+ <view class="agent-title">
+ <text class="agent-name">
+ {{ agent.agentName }}
+ </text>
+ </view>
+
+ <view class="model-info">
+ <text class="model-text">
+ {{ t('home.languageModel') }}锛� {{ agent.llmModelName }}
+ </text>
+ <text class="model-text">
+ {{ t('home.voiceModel') }}锛� {{ agent.ttsModelName }} ({{ agent.ttsVoiceName }})
+ </text>
+ </view>
+
+ <view class="stats-row">
+ <view class="stat-chip">
+ <wd-icon name="phone" custom-class="chip-icon" />
+ <text class="chip-text">
+ {{ t('home.deviceManagement') }}({{ agent.deviceCount }})
+ </text>
+ </view>
+ <view v-if="agent.lastConnectedAt" class="stat-chip">
+ <wd-icon name="time" custom-class="chip-icon" />
+ <text class="chip-text">
+ {{ t('home.lastConversation') }}{{ formatTime(agent.lastConnectedAt) }}
+ </text>
+ </view>
+ <text v-if="agent.tags" class="flex-1 truncate text-right text-[22rpx] text-[#666]">
+ {{ agent.tags.map(tag => tag.tagName).join(',') }}
+ </text>
+ </view>
+ </view>
+
+ <wd-icon name="arrow-right" custom-class="arrow-icon" />
+ </view>
+ </view>
+
+ <template #right>
+ <view class="swipe-actions">
+ <view class="action-btn delete-btn" @click.stop="openDeleteAgentDialog(agent)">
+ <wd-icon name="delete" />
+ <text>{{ t('home.delete') }}</text>
+ </view>
+ </view>
+ </template>
+ </wd-swipe-action>
+ </view>
+ </view>
+
+ <!-- 鑷畾涔夌┖鐘舵�� -->
+ <template #empty>
+ <view class="empty-state">
+ <wd-icon name="robot" custom-class="empty-icon" />
+ <text class="empty-text">
+ {{ t('home.emptyState') }}
+ </text>
+ <text class="empty-desc">
+ {{ t('home.createFirstAgent') }}
+ </text>
+ </view>
+ </template>
+
+ <!-- FAB 鏂板鎸夐挳 -->
+ <wd-fab type="primary" icon="add" :draggable="true" :expandable="false" @click="openCreateDialog" />
+
+ <!-- MessageBox 缁勪欢 -->
+ <wd-message-box />
+ </z-paging>
+
+ <wd-popup
+ v-model="deleteDialogVisible"
+ position="center"
+ custom-style="width: 88%; max-width: 420px; border-radius: 16px;"
+ :close-on-click-modal="!deleteAgentLoading"
+ safe-area-inset-bottom
+ @close="closeDeleteAgentDialog"
+ >
+ <view class="delete-agent-dialog">
+ <view class="delete-agent-header">
+ <text class="delete-agent-title">
+ {{ t('home.deleteConfirmTitle') }}
+ </text>
+ </view>
+
+ <view class="delete-agent-body">
+ <view class="delete-agent-content">
+ <wd-icon name="warning" custom-class="delete-agent-warning" />
+ <view class="delete-agent-message">
+ <text
+ class="delete-agent-copy-guard delete-agent-tip"
+ @copy.stop.prevent
+ @cut.stop.prevent
+ @contextmenu.stop.prevent
+ @longpress.stop.prevent
+ >
+ {{ t('home.confirmDeleteAgent', { agentName: deleteTargetAgent?.agentName || '' }) }}
+ </text>
+ <view
+ class="delete-agent-copy-guard delete-agent-target"
+ @copy.stop.prevent
+ @cut.stop.prevent
+ @contextmenu.stop.prevent
+ @longpress.stop.prevent
+ >
+ <text>{{ deleteTargetAgent?.agentName || '' }}</text>
+ </view>
+
+ <input
+ v-model="deleteConfirmText"
+ class="delete-agent-input"
+ type="text"
+ :maxlength="deleteTargetAgent?.agentName?.length || 64"
+ :placeholder="t('home.deleteAgentNamePlaceholder')"
+ :disabled="deleteAgentLoading"
+ @paste.stop.prevent="handleDeleteAgentPaste"
+ @drop.stop.prevent="handleDeleteAgentPaste"
+ @contextmenu.stop.prevent
+ >
+ <text v-if="deleteConfirmText && !isDeleteAgentNameMatched" class="delete-agent-helper">
+ {{ t('home.deleteAgentNameMismatch') }}
+ </text>
+ </view>
+ </view>
+ </view>
+
+ <view class="delete-agent-footer">
+ <wd-button type="info" custom-class="delete-agent-button" :disabled="deleteAgentLoading" @click="closeDeleteAgentDialog">
+ {{ t('common.cancel') }}
+ </wd-button>
+ <wd-button
+ type="primary"
+ custom-class="delete-agent-button"
+ :loading="deleteAgentLoading"
+ :disabled="!isDeleteAgentNameMatched"
+ @click="handleDeleteAgent"
+ >
+ {{ t('common.confirm') }}
+ </wd-button>
+ </view>
+ </view>
+ </wd-popup>
+</template>
+
+<style lang="scss" scoped>
+.banner-section {
+ background: linear-gradient(145deg, #9ebbfc, #6baaff, #9ebbfc, #f5f8fd);
+ position: relative;
+ padding: 40rpx 40rpx 80rpx 40rpx;
+ overflow: hidden;
+
+ .banner-content {
+ position: relative;
+ z-index: 2;
+ }
+
+ .header-actions {
+ position: absolute;
+ top: -50rpx;
+ right: 0;
+ display: flex;
+ gap: 32rpx;
+
+ .filter-icon,
+ .setting-icon {
+ color: white;
+ cursor: pointer;
+ transition: opacity 0.2s ease;
+
+ &:active {
+ opacity: 0.7;
+ }
+ }
+ }
+
+ .welcome-info {
+ .greeting {
+ display: block;
+ font-size: 48rpx;
+ font-weight: 700;
+ color: #ffffff;
+ margin-bottom: 16rpx;
+ text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
+ }
+
+ .subtitle {
+ display: block;
+ font-size: 32rpx;
+ color: rgba(255, 255, 255, 0.9);
+ margin-bottom: 12rpx;
+ font-weight: 500;
+
+ .highlight {
+ color: #ffd700;
+ font-weight: 600;
+ }
+ }
+
+ .english-subtitle {
+ display: block;
+ font-size: 24rpx;
+ color: rgba(255, 255, 255, 0.7);
+ font-style: italic;
+ }
+ }
+
+ .wave-decoration {
+ position: absolute;
+ top: 0;
+ right: -100rpx;
+ width: 400rpx;
+ height: 100%;
+ opacity: 0.1;
+ pointer-events: none;
+
+ .wave {
+ position: absolute;
+ width: 200%;
+ height: 200%;
+ background: radial-gradient(circle, rgba(255, 255, 255, 0.3) 0%, transparent 70%);
+ border-radius: 50%;
+ animation: float 6s ease-in-out infinite;
+
+ &.wave-2 {
+ top: 20%;
+ right: 20%;
+ animation-delay: -3s;
+ opacity: 0.5;
+ }
+ }
+ }
+}
+
+@keyframes float {
+ 0%,
+ 100% {
+ transform: translateY(0px) rotate(0deg);
+ }
+
+ 50% {
+ transform: translateY(-30rpx) rotate(180deg);
+ }
+}
+
+// 鍐呭鍖哄煙寮�濮嬫爣璇嗭紝鍒涘缓鐧借壊鑳屾櫙杩囨浮
+.content-section-header {
+ background: #ffffff;
+ border-radius: 32rpx 32rpx 0 0;
+ margin-top: -32rpx;
+ height: 32rpx;
+ position: relative;
+ z-index: 1;
+}
+
+// z-paging鍐呭鍖哄煙鏍峰紡
+:deep(.z-paging-content) {
+ background: #ffffff;
+ padding: 0 0 40rpx 0;
+}
+
+.agent-list {
+ display: flex;
+ flex-direction: column;
+ gap: 16rpx;
+ padding: 0 20rpx;
+}
+
+.agent-item {
+ :deep(.wd-swipe-action) {
+ border-radius: 16rpx;
+ overflow: hidden;
+ box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
+ border: 1rpx solid #f0f0f0;
+ }
+}
+
+.simple-card {
+ background: #ffffff;
+ padding: 24rpx;
+ cursor: pointer;
+ transition: all 0.2s ease;
+
+ &:active {
+ background: #f8f9fa;
+ }
+
+ .card-content {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ }
+
+ .card-main {
+ flex: 1;
+ width: 100%;
+ }
+
+ .agent-title {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 12rpx;
+
+ .agent-name {
+ font-size: 32rpx;
+ font-weight: 600;
+ color: #1a1a1a;
+ }
+ }
+
+ .model-info {
+ margin-bottom: 16rpx;
+
+ .model-text {
+ display: block;
+ font-size: 24rpx;
+ color: #666666;
+ line-height: 1.5;
+ margin-bottom: 4rpx;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+ }
+ }
+
+ .stats-row {
+ width: 100%;
+ display: flex;
+ align-items: center;
+ gap: 12rpx;
+ flex-wrap: wrap;
+
+ .stat-chip {
+ display: flex;
+ align-items: center;
+ padding: 6rpx 12rpx;
+ background: #f8f9fa;
+ border-radius: 20rpx;
+ border: 1rpx solid #eaeaea;
+
+ :deep(.chip-icon) {
+ font-size: 20rpx;
+ color: #666666;
+ margin-right: 6rpx;
+ }
+
+ .chip-text {
+ font-size: 22rpx;
+ color: #666666;
+ }
+ }
+ }
+
+ :deep(.arrow-icon) {
+ font-size: 24rpx;
+ color: #c7c7cc;
+ margin-left: 16rpx;
+ }
+}
+
+.swipe-actions {
+ display: flex;
+ height: 100%;
+
+ .action-btn {
+ width: 120rpx;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 8rpx;
+ color: #ffffff;
+ font-size: 24rpx;
+ font-weight: 500;
+ transition: all 0.3s ease;
+
+ &.edit-btn {
+ background: #1890ff;
+
+ &:active {
+ background: #096dd9;
+ }
+ }
+
+ &.delete-btn {
+ background: #ff4d4f;
+
+ &:active {
+ background: #d9363e;
+ }
+ }
+
+ :deep(.wd-icon) {
+ font-size: 32rpx;
+ }
+ }
+}
+
+.empty-state {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 100rpx 40rpx;
+ text-align: center;
+
+ :deep(.empty-icon) {
+ font-size: 120rpx;
+ color: #d9d9d9;
+ margin-bottom: 32rpx;
+ }
+
+ .empty-text {
+ font-size: 32rpx;
+ color: #666666;
+ margin-bottom: 16rpx;
+ font-weight: 500;
+ }
+
+ .empty-desc {
+ font-size: 26rpx;
+ color: #999999;
+ line-height: 1.5;
+ }
+}
+
+@keyframes pulse {
+ 0% {
+ opacity: 1;
+ }
+
+ 50% {
+ opacity: 0.5;
+ }
+
+ 100% {
+ opacity: 1;
+ }
+}
+
+.filter-actions {
+ padding: 32rpx;
+ text-align: center;
+ border-top: 1rpx solid #eeeeee;
+}
+
+.delete-agent-dialog {
+ overflow: hidden;
+ background: #ffffff;
+}
+
+.delete-agent-header {
+ padding: 32rpx 32rpx 20rpx;
+ border-bottom: 1rpx solid #f0f0f0;
+ text-align: center;
+}
+
+.delete-agent-title {
+ font-size: 32rpx;
+ font-weight: 600;
+ color: #232338;
+}
+
+.delete-agent-body {
+ padding: 32rpx;
+}
+
+.delete-agent-content {
+ display: flex;
+ align-items: flex-start;
+ gap: 16rpx;
+}
+
+:deep(.delete-agent-warning) {
+ margin-top: 4rpx;
+ color: #e6a23c;
+ font-size: 40rpx;
+}
+
+.delete-agent-message {
+ flex: 1;
+ min-width: 0;
+}
+
+.delete-agent-tip {
+ display: block;
+ color: #606266;
+ font-size: 28rpx;
+ line-height: 1.6;
+}
+
+.delete-agent-target {
+ box-sizing: border-box;
+ width: 100%;
+ margin-top: 20rpx;
+ padding: 18rpx 20rpx;
+ border: 1rpx solid #ebeef5;
+ border-radius: 12rpx;
+ background: #f5f7fa;
+ color: #303133;
+ font-size: 30rpx;
+ font-weight: 600;
+ line-height: 1.5;
+ word-break: break-all;
+}
+
+.delete-agent-copy-guard {
+ user-select: none;
+ -webkit-user-select: none;
+}
+
+.delete-agent-input {
+ box-sizing: border-box;
+ width: 100%;
+ height: 80rpx;
+ margin-top: 28rpx;
+ padding: 0 20rpx;
+ border: 1rpx solid #eeeeee;
+ border-radius: 12rpx;
+ background: #f5f7fb;
+ color: #232338;
+ font-size: 28rpx;
+ outline: none;
+}
+
+.delete-agent-helper {
+ display: block;
+ min-height: 32rpx;
+ margin-top: 10rpx;
+ color: #f56c6c;
+ font-size: 24rpx;
+ line-height: 1.4;
+}
+
+.delete-agent-footer {
+ display: flex;
+ gap: 16rpx;
+ padding: 24rpx 32rpx 32rpx;
+ border-top: 1rpx solid #eeeeee;
+}
+
+:deep(.delete-agent-button) {
+ flex: 1;
+}
+</style>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/login/index.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/login/index.vue
new file mode 100644
index 0000000..f8fd6c0
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/login/index.vue
@@ -0,0 +1,1012 @@
+<route lang="jsonc" type="page">
+{
+ "layout": "default",
+ "style": {
+ "navigationStyle": "custom",
+ "navigationBarTitleText": "Login"
+ }
+}
+</route>
+
+<script lang="ts" setup>
+import type { LoginData } from '@/api/auth'
+import type { Language } from '@/store/lang'
+import { computed, onMounted, ref } from 'vue'
+import { login } from '@/api/auth'
+// 瀵煎叆鍥介檯鍖栫浉鍏冲姛鑳�
+import { changeLanguage, getCurrentLanguage, getSupportedLanguages, initI18n, t } from '@/i18n'
+import { useConfigStore, useUserStore } from '@/store'
+// 瀵煎叆SM2鍔犲瘑宸ュ叿
+import { getEnvBaseUrl, sm2Encrypt } from '@/utils'
+import { toast } from '@/utils/toast'
+
+// 鑾峰彇灞忓箷杈圭晫鍒板畨鍏ㄥ尯鍩熻窛绂�
+let safeAreaInsets
+let systemInfo
+
+// #ifdef MP-WEIXIN
+// 寰俊灏忕▼搴忎娇鐢ㄦ柊鐨凙PI
+systemInfo = uni.getWindowInfo()
+safeAreaInsets = systemInfo.safeArea
+ ? {
+ top: systemInfo.safeArea.top,
+ right: systemInfo.windowWidth - systemInfo.safeArea.right,
+ bottom: systemInfo.windowHeight - systemInfo.safeArea.bottom,
+ left: systemInfo.safeArea.left,
+ }
+ : null
+// #endif
+
+// #ifndef MP-WEIXIN
+// 鍏朵粬骞冲彴缁х画浣跨敤uni API
+systemInfo = uni.getSystemInfoSync()
+safeAreaInsets = systemInfo.safeAreaInsets
+// #endif
+// 琛ㄥ崟鏁版嵁
+const formData = ref({
+ username: '',
+ password: '',
+ captcha: '',
+ captchaId: '',
+ areaCode: '+86',
+ mobile: '',
+})
+
+// 楠岃瘉鐮佸浘鐗�
+const captchaImage = ref('')
+const loading = ref(false)
+
+// 鐧诲綍鏂瑰紡锛�'username' | 'mobile'
+const loginType = ref<'username' | 'mobile'>('username')
+
+// 鑾峰彇閰嶇疆store
+const configStore = useConfigStore()
+const userStore = useUserStore()
+
+// 鍖哄彿閫夋嫨鐩稿叧
+const showAreaCodeSheet = ref(false)
+const selectedAreaCode = ref('+86')
+const selectedAreaName = ref('涓浗澶ч檰')
+
+// 璁$畻灞炴�э細鏄惁鍚敤鎵嬫満鍙风櫥褰�
+const enableMobileLogin = computed(() => {
+ return configStore.config.enableMobileRegister
+})
+
+// 璁$畻灞炴�э細鍖哄彿鍒楄〃
+const areaCodeList = computed(() => {
+ return configStore.config.mobileAreaList || [{ name: '涓浗澶ч檰', key: '+86' }]
+})
+
+// 鍒囨崲鐧诲綍鏂瑰紡
+function toggleLoginType() {
+ loginType.value = loginType.value === 'username' ? 'mobile' : 'username'
+ // 娓呯┖杈撳叆妗�
+ formData.value.username = ''
+ formData.value.mobile = ''
+}
+
+// 鎵撳紑鍖哄彿閫夋嫨寮圭獥
+function openAreaCodeSheet() {
+ showAreaCodeSheet.value = true
+}
+
+// 閫夋嫨鍖哄彿
+function selectAreaCode(item: { name: string, key: string }) {
+ selectedAreaCode.value = item.key
+ selectedAreaName.value = item.name
+ formData.value.areaCode = item.key
+ showAreaCodeSheet.value = false
+}
+
+// 鍏抽棴鍖哄彿閫夋嫨寮圭獥
+function closeAreaCodeSheet() {
+ showAreaCodeSheet.value = false
+}
+
+// 璺宠浆鍒版敞鍐岄〉闈�
+function goToRegister() {
+ uni.navigateTo({
+ url: '/pages/register/index',
+ })
+}
+
+// 璺宠浆鍒板繕璁板瘑鐮侀〉闈�
+function goToForgotPassword() {
+ uni.navigateTo({
+ url: '/pages/forgot-password/index',
+ })
+}
+
+// 璺宠浆鍒扮敤鎴峰崗璁�
+function goToUserAgreement() {
+ const lang = getCurrentLanguage() === 'zh_CN' ? 'zh' : 'en'
+ uni.navigateTo({
+ url: `/pages/login/user-agreement-${lang}`,
+ })
+}
+
+// 璺宠浆鍒伴殣绉佹斂绛�
+function goToPrivacyPolicy() {
+ const lang = getCurrentLanguage() === 'zh_CN' ? 'zh' : 'en'
+ uni.navigateTo({
+ url: `/pages/login/privacy-policy-${lang}`,
+ })
+}
+
+// 鐢熸垚UUID
+function generateUUID() {
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
+ const r = Math.random() * 16 | 0
+ const v = c === 'x' ? r : (r & 0x3 | 0x8)
+ return v.toString(16)
+ })
+}
+
+// 璺宠浆鑷虫湇鍔$璁剧疆椤甸潰
+function goToServerSetting() {
+ uni.switchTab({
+ url: '/pages/settings/index',
+ })
+}
+
+// 鑾峰彇楠岃瘉鐮�
+async function refreshCaptcha() {
+ const uuid = generateUUID()
+ formData.value.captchaId = uuid
+ captchaImage.value = `${getEnvBaseUrl()}/user/captcha?uuid=${uuid}&t=${Date.now()}`
+}
+
+// 鐧诲綍
+async function handleLogin() {
+ // 琛ㄥ崟楠岃瘉
+ if (loginType.value === 'username') {
+ if (!formData.value.username) {
+ toast.warning(t('login.enterUsername'))
+ return
+ }
+ }
+ else {
+ if (!formData.value.mobile) {
+ toast.warning(t('login.enterPhone'))
+ return
+ }
+ // 鎵嬫満鍙锋牸寮忛獙璇�
+ const phoneRegex = /^1[3-9]\d{9}$/
+ if (!phoneRegex.test(formData.value.mobile)) {
+ toast.warning(t('login.enterPhone'))
+ return
+ }
+ }
+ if (!formData.value.password) {
+ toast.warning(t('login.enterPassword'))
+ return
+ }
+ if (!formData.value.captcha) {
+ toast.warning(t('login.enterCaptcha'))
+ return
+ }
+
+ // 妫�鏌M2鍏挜鏄惁閰嶇疆
+ const sm2PublicKey = configStore.config.sm2PublicKey
+ if (!sm2PublicKey) {
+ toast.warning(t('sm2.publicKeyNotConfigured'))
+ return
+ }
+
+ try {
+ loading.value = true
+
+ // 鍔犲瘑瀵嗙爜
+ let encryptedPassword
+ try {
+ // 鎷兼帴楠岃瘉鐮佸拰瀵嗙爜
+ const captchaAndPassword = formData.value.captcha + formData.value.password
+ encryptedPassword = sm2Encrypt(sm2PublicKey, captchaAndPassword)
+ }
+ catch (error) {
+ console.error('瀵嗙爜鍔犲瘑澶辫触:', error)
+ toast.warning(t('sm2.encryptionFailed'))
+ return
+ }
+
+ // 鏋勫缓鐧诲綍鏁版嵁
+ const loginData: LoginData = {
+ username: '',
+ password: encryptedPassword,
+ captchaId: formData.value.captchaId,
+ }
+
+ // 濡傛灉鏄墜鏈哄彿鐧诲綍锛屽皢鍖哄彿+鎵嬫満鍙锋嫾鎺ュ埌username瀛楁
+ if (loginType.value === 'mobile') {
+ loginData.username = `${selectedAreaCode.value}${formData.value.mobile}`
+ }
+ else {
+ loginData.username = formData.value.username
+ }
+
+ const response = await login(loginData)
+ // 瀛樺偍token
+ uni.setStorageSync('token', JSON.stringify(response))
+ await userStore.getUserInfo()
+
+ toast.success(t('message.loginSuccess'))
+
+ // 璺宠浆鍒颁富椤�
+ setTimeout(() => {
+ uni.reLaunch({
+ url: '/pages/index/index',
+ })
+ }, 1000)
+ }
+ catch (error: any) {
+ // 鐧诲綍澶辫触閲嶆柊鑾峰彇楠岃瘉鐮�
+ refreshCaptcha()
+ }
+ finally {
+ loading.value = false
+ }
+}
+
+// 椤甸潰鍔犺浇鏃惰幏鍙栭獙璇佺爜
+onLoad(() => {
+ refreshCaptcha()
+})
+
+// 璇█鍒囨崲鐩稿叧
+const showLanguageSheet = ref(false)
+const supportedLanguages = getSupportedLanguages()
+
+// 鍒濆鍖栧浗闄呭寲
+initI18n()
+
+// 鍒囨崲璇█
+function handleLanguageChange(lang: Language) {
+ changeLanguage(lang)
+ showLanguageSheet.value = false
+}
+
+// 缁勪欢鎸傝浇鏃剁‘淇濋厤缃凡鍔犺浇
+onMounted(async () => {
+ if (!configStore.config.name) {
+ try {
+ await configStore.fetchPublicConfig()
+ }
+ catch (error) {
+ console.error(t('login.fetchConfigError'), error)
+ }
+ }
+})
+</script>
+
+<template>
+ <view class="app-container box-border h-screen w-full" :style="{ paddingTop: `${safeAreaInsets?.top}px` }">
+ <view class="header">
+ <view class="logo-section">
+ <wd-img :width="80" :height="80" round src="/static/logo.png" class="logo" />
+ <text class="welcome-text">
+ {{ t('login.welcomeBack') }}
+ </text>
+ <text class="subtitle">
+ {{ t('login.pleaseLogin') }}
+ </text>
+ </view>
+ </view>
+
+ <!-- 鍙充笂瑙掓寜閽粍 -->
+ <view class="top-right-buttons" :style="{ top: `${safeAreaInsets?.top + 10}px` }">
+ <!-- 璇█鍒囨崲鎸夐挳 -->
+ <view class="lang-btn" @click="showLanguageSheet = true">
+ <text class="lang-text-icon">
+ {{ t('login.selectLanguageTip') }}
+ </text>
+ </view>
+
+ <!-- 鏈嶅姟绔缃寜閽� -->
+ <view class="server-btn" @click="goToServerSetting">
+ <wd-icon name="setting" custom-class="server-icon" />
+ </view>
+ </view>
+
+ <view class="form-container">
+ <view class="form">
+ <!-- 鎵嬫満鍙风櫥褰� -->
+ <template v-if="loginType === 'mobile'">
+ <view class="input-group">
+ <view class="input-wrapper mobile-wrapper">
+ <view class="area-code-selector" @click="openAreaCodeSheet">
+ <text class="area-code-text">
+ {{ selectedAreaCode }}
+ </text>
+ <wd-icon name="arrow-down" custom-class="area-code-arrow" />
+ </view>
+ <view class="mobile-input-wrapper">
+ <wd-input
+ v-model="formData.mobile"
+ custom-class="styled-input"
+ no-border
+ :placeholder="t('login.enterPhone')"
+ type="number"
+ :maxlength="11"
+ />
+ </view>
+ </view>
+ </view>
+ </template>
+
+ <!-- 鐢ㄦ埛鍚嶇櫥褰� -->
+ <template v-else>
+ <view class="input-group">
+ <view class="input-wrapper">
+ <wd-input
+ v-model="formData.username"
+ custom-class="styled-input"
+ no-border
+ :placeholder="t('login.enterUsername')"
+ />
+ </view>
+ </view>
+ </template>
+
+ <view class="input-group">
+ <view class="input-wrapper">
+ <wd-input
+ v-model="formData.password"
+ custom-class="styled-input"
+ no-border
+ :placeholder="t('login.enterPassword')"
+ clearable
+ show-password
+ :maxlength="20"
+ />
+ </view>
+ </view>
+
+ <view class="input-group">
+ <view class="input-wrapper captcha-wrapper">
+ <wd-input
+ v-model="formData.captcha"
+ custom-class="styled-input"
+ no-border
+ :placeholder="t('login.enterCaptcha')"
+ :maxlength="6"
+ />
+ <view class="captcha-image" @click="refreshCaptcha">
+ <image :src="captchaImage" class="captcha-img" />
+ </view>
+ </view>
+ </view>
+ <view
+ class="login-btn"
+ @click="handleLogin"
+ >
+ {{ loading ? t('login.loggingIn') : t('login.loginButton') }}
+ </view>
+
+ <view class="hint-row">
+ <view class="register-hint">
+ <text class="register-link" @click="goToRegister">
+ {{ t('login.noAccount') }}
+ </text>
+ </view>
+
+ <view class="forgot-password">
+ <text class="forgot-text" @click="goToForgotPassword">
+ {{ t('login.forgotPassword') }}
+ </text>
+ </view>
+ </view>
+
+ <view class="policy-links">
+ <text class="policy-link" @click="goToUserAgreement">
+ {{ t('login.userAgreement') }}
+ </text>
+ <text class="policy-divider">
+ {{ '|' }}
+ </text>
+ <text class="policy-link" @click="goToPrivacyPolicy">
+ {{ t('login.privacyPolicy') }}
+ </text>
+ </view>
+
+ <!-- 鐧诲綍鏂瑰紡鍒囨崲 -->
+ <view v-if="enableMobileLogin" class="login-type-switch">
+ <view class="switch-tabs">
+ <view
+ class="switch-tab"
+ :class="{ active: loginType === 'username' }"
+ @click="toggleLoginType"
+ >
+ <wd-icon name="user" />
+ </view>
+ <view
+ class="switch-tab"
+ :class="{ active: loginType === 'mobile' }"
+ @click="toggleLoginType"
+ >
+ <wd-icon name="phone" />
+ </view>
+ </view>
+ </view>
+ </view>
+ </view>
+
+ <!-- 鍖哄彿閫夋嫨寮圭獥 -->
+ <wd-action-sheet
+ v-model="showAreaCodeSheet"
+ :title="t('login.selectCountry')"
+ :close-on-click-modal="true"
+ @close="closeAreaCodeSheet"
+ >
+ <view class="area-code-sheet">
+ <scroll-view scroll-y class="area-code-list">
+ <view
+ v-for="item in areaCodeList"
+ :key="item.key"
+ class="area-code-item"
+ :class="{ selected: selectedAreaCode === item.key }"
+ @click="selectAreaCode(item)"
+ >
+ <view class="area-info">
+ <text class="area-name">
+ {{ item.name }}
+ </text>
+ <text class="area-code">
+ {{ item.key }}
+ </text>
+ </view>
+ <wd-icon
+ v-if="selectedAreaCode === item.key"
+ name="check"
+ custom-class="check-icon"
+ />
+ </view>
+ </scroll-view>
+ <view class="sheet-footer">
+ <wd-button
+ type="primary"
+ custom-class="confirm-btn"
+ @click="closeAreaCodeSheet"
+ >
+ {{ t('login.confirm') }}
+ </wd-button>
+ </view>
+ </view>
+ </wd-action-sheet>
+
+ <!-- 璇█閫夋嫨寮圭獥 -->
+ <wd-action-sheet
+ v-model="showLanguageSheet"
+ :title="t('login.selectLanguage')"
+ :close-on-click-modal="true"
+ >
+ <view class="language-sheet">
+ <scroll-view scroll-y class="language-list">
+ <view
+ v-for="lang in supportedLanguages"
+ :key="lang.code"
+ class="language-item"
+ @click="handleLanguageChange(lang.code)"
+ >
+ <text class="language-name">
+ {{ lang.name }}
+ </text>
+ </view>
+ </scroll-view>
+ </view>
+ </wd-action-sheet>
+ </view>
+</template>
+
+<style lang="scss" scoped>
+.app-container {
+ background: linear-gradient(145deg, #f5f8fd, #6baaff, #9ebbfc, #f5f8fd);
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ overflow: hidden;
+ min-height: 100vh;
+
+ &::before {
+ content: '';
+ position: absolute;
+ top: -50%;
+ left: -50%;
+ width: 200%;
+ height: 200%;
+ background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
+ animation: float 6s ease-in-out infinite;
+ }
+}
+
+@keyframes float {
+ 0%,
+ 100% {
+ transform: translateY(0px) rotate(0deg);
+ }
+ 50% {
+ transform: translateY(-20px) rotate(180deg);
+ }
+}
+
+.header {
+ flex: 0 0 auto;
+ min-height: 280rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 15% 0 40rpx 0;
+
+ .logo-section {
+ text-align: center;
+
+ .logo {
+ margin-bottom: 20rpx;
+ box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.1);
+ }
+
+ .welcome-text {
+ display: block;
+ color: #ffffff;
+ font-size: 40rpx;
+ font-weight: 600;
+ margin-bottom: 12rpx;
+ text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
+ }
+
+ .subtitle {
+ display: block;
+ color: rgba(255, 255, 255, 0.8);
+ font-size: 26rpx;
+ font-weight: 400;
+ }
+ }
+}
+
+.form-container {
+ padding: 0 40rpx 40rpx 40rpx;
+ flex: 1;
+ display: flex;
+ align-items: flex-start;
+
+ .form {
+ width: 100%;
+ background: rgba(255, 255, 255, 0.95);
+ border-radius: 24rpx;
+ padding: 40rpx 30rpx 30rpx 30rpx;
+ backdrop-filter: blur(10rpx);
+ box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.1);
+ border: 1rpx solid rgba(255, 255, 255, 0.2);
+ max-height: calc(100vh - 350rpx);
+ overflow-y: auto;
+
+ .input-group {
+ margin-bottom: 24rpx;
+
+ .input-wrapper {
+ position: relative;
+ border-radius: 16rpx;
+ padding: 20rpx 16rpx;
+ border: 2rpx solid #e9ecef;
+ transition: all 0.3s ease;
+ display: flex;
+ align-items: center;
+
+ &:focus-within {
+ border-color: #667eea;
+ background: #ffffff;
+ box-shadow: 0 0 0 6rpx rgba(102, 126, 234, 0.1);
+ }
+
+ &.captcha-wrapper {
+ .captcha-image {
+ margin-left: 20rpx;
+ width: 150rpx;
+ height: 60rpx;
+ border-radius: 8rpx;
+ overflow: hidden;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: #e9ecef;
+ border: 1rpx solid #ddd;
+
+ .captcha-img {
+ width: 100%;
+ height: 100%;
+ }
+
+ .captcha-loading {
+ font-size: 20rpx;
+ color: #999;
+ }
+ }
+ }
+
+ &.mobile-wrapper {
+ padding: 0;
+ background: transparent;
+ border: none;
+ display: flex;
+ gap: 20rpx;
+
+ .area-code-selector {
+ flex: 0 0 160rpx;
+ background: #f8f9fa;
+ border-radius: 16rpx;
+ padding: 20rpx 16rpx;
+ border: 2rpx solid #e9ecef;
+ height: 45rpx;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ cursor: pointer;
+ transition: all 0.3s ease;
+
+ &:hover {
+ border-color: #667eea;
+ background: #ffffff;
+ box-shadow: 0 0 0 6rpx rgba(102, 126, 234, 0.1);
+ }
+
+ .area-code-text {
+ font-size: 28rpx;
+ color: #333333;
+ font-weight: 500;
+ }
+
+ :deep(.area-code-arrow) {
+ font-size: 24rpx;
+ color: #999999;
+ transition: transform 0.3s ease;
+ }
+ }
+
+ .mobile-input-wrapper {
+ flex: 1;
+ background: #f8f9fa;
+ border-radius: 16rpx;
+ padding: 20rpx 16rpx;
+ border: 2rpx solid #e9ecef;
+ transition: all 0.3s ease;
+
+ &:focus-within {
+ border-color: #667eea;
+ background: #ffffff;
+ box-shadow: 0 0 0 6rpx rgba(102, 126, 234, 0.1);
+ }
+ }
+ }
+
+ :deep(.styled-input) {
+ background: transparent !important;
+ border: none !important;
+ outline: none !important;
+ font-size: 32rpx;
+ color: #333333;
+ flex: 1;
+
+ &::placeholder {
+ color: #999999;
+ font-size: 28rpx;
+ }
+ }
+ }
+ }
+
+ .hint-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 30rpx;
+ }
+
+ .policy-links {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 20rpx;
+ margin-bottom: 30rpx;
+
+ .policy-link {
+ color: #667eea;
+ font-size: 26rpx;
+ cursor: pointer;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+
+ .policy-divider {
+ color: #999999;
+ font-size: 26rpx;
+ }
+ }
+
+ .forgot-password {
+ .forgot-text {
+ color: #667eea;
+ font-size: 26rpx;
+ cursor: pointer;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+
+ .login-btn {
+ width: 100%;
+ height: 80rpx;
+ border: none;
+ border-radius: 16rpx;
+ font-size: 30rpx;
+ font-weight: 600;
+ color: #ffffff;
+ margin-bottom: 30rpx;
+ box-shadow: 0 8rpx 24rpx rgba(102, 126, 234, 0.3);
+ transition: all 0.3s ease;
+ background-color: var(--wot-button-primary-bg-color, var(--wot-color-theme, #4d80f0));
+ text-align: center;
+ line-height: 80rpx;
+ &:active {
+ transform: translateY(2rpx);
+ box-shadow: 0 4rpx 12rpx rgba(102, 126, 234, 0.3);
+ }
+
+ &:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ }
+ }
+
+ .register-hint {
+ .register-link {
+ color: #667eea;
+ font-size: 26rpx;
+ font-weight: 500;
+ cursor: pointer;
+ // text-decoration: underline;
+ // &:hover {
+ // text-decoration: underline;
+ // }
+ }
+ }
+
+ .login-type-switch {
+ margin-top: 20rpx;
+ text-align: center;
+
+ .switch-tabs {
+ display: flex;
+ justify-content: center;
+ gap: 60rpx;
+ margin-bottom: 20rpx;
+
+ .switch-tab {
+ width: 60rpx;
+ height: 60rpx;
+ border-radius: 50%;
+ background: #f0f0f0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ border: 2rpx solid transparent;
+
+ &.active {
+ background: #667eea;
+ color: #ffffff;
+ border-color: #667eea;
+ box-shadow: 0 4rpx 12rpx rgba(102, 126, 234, 0.3);
+ }
+
+ :deep(.wd-icon) {
+ font-size: 24rpx;
+ }
+ }
+ }
+
+ .switch-hint {
+ font-size: 24rpx;
+ color: #666666;
+ }
+ }
+ }
+}
+
+// 鍖哄彿閫夋嫨寮圭獥鏍峰紡
+.area-code-sheet {
+ background: #ffffff;
+ border-radius: 24rpx 24rpx 0 0;
+ overflow: hidden;
+
+ .sheet-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 40rpx 40rpx 20rpx 40rpx;
+ border-bottom: 1rpx solid #f0f0f0;
+
+ .sheet-title {
+ font-size: 36rpx;
+ font-weight: 600;
+ color: #333333;
+ }
+
+ :deep(.close-icon) {
+ font-size: 32rpx;
+ color: #999999;
+ cursor: pointer;
+ padding: 10rpx;
+
+ &:hover {
+ color: #333333;
+ }
+ }
+ }
+
+ .area-code-list {
+ max-height: 60vh;
+ padding: 0 40rpx;
+
+ .area-code-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 32rpx 0;
+ border-bottom: 1rpx solid #f8f9fa;
+ cursor: pointer;
+ transition: background-color 0.3s ease;
+
+ &:hover {
+ background-color: #f8f9fa;
+ }
+
+ &.selected {
+ background-color: rgba(102, 126, 234, 0.05);
+
+ .area-name {
+ color: #667eea;
+ font-weight: 500;
+ }
+
+ .area-code {
+ color: #667eea;
+ }
+ }
+
+ .area-info {
+ display: flex;
+ flex-direction: column;
+ gap: 8rpx;
+
+ .area-name {
+ font-size: 32rpx;
+ color: #333333;
+ }
+
+ .area-code {
+ font-size: 28rpx;
+ color: #666666;
+ }
+ }
+
+ :deep(.check-icon) {
+ font-size: 32rpx;
+ color: #667eea;
+ }
+ }
+ }
+
+ .sheet-footer {
+ padding: 30rpx 40rpx 40rpx 40rpx;
+ border-top: 1rpx solid #f0f0f0;
+
+ :deep(.confirm-btn) {
+ width: 100%;
+ height: 88rpx;
+ border-radius: 16rpx;
+ font-size: 32rpx;
+ font-weight: 600;
+ }
+ }
+}
+// 鍙充笂瑙掓寜閽粍
+.top-right-buttons {
+ position: absolute;
+ right: 20rpx;
+ display: flex;
+ gap: 20rpx;
+ z-index: 999;
+}
+
+// 璇█鍒囨崲鎸夐挳
+.lang-btn {
+ width: 48rpx;
+ height: 48rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ background: rgba(255, 255, 255, 0.15);
+ border-radius: 24rpx;
+ box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.2);
+
+ &:active {
+ transform: scale(0.95);
+ }
+
+ .lang-text-icon {
+ font-size: 18rpx;
+ color: #ffffff;
+ }
+
+ &:hover {
+ background: rgba(255, 255, 255, 0.25);
+ }
+}
+
+// 鏈嶅姟绔缃寜閽�
+.server-btn {
+ width: 48rpx;
+ height: 48rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ background: rgba(255, 255, 255, 0.15);
+ border-radius: 24rpx;
+ box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.2);
+
+ &:active {
+ transform: scale(0.95);
+ }
+
+ .server-icon {
+ font-size: 28rpx;
+ color: #ffffff;
+ }
+
+ &:hover {
+ background: rgba(255, 255, 255, 0.25);
+ }
+}
+
+// 璇█閫夋嫨寮圭獥鏍峰紡
+.language-sheet {
+ background: #ffffff;
+ border-radius: 24rpx 24rpx 0 0;
+ overflow: hidden;
+
+ .language-list {
+ max-height: 60vh;
+ padding: 0 40rpx;
+
+ .language-item {
+ display: flex;
+ align-items: center;
+ padding: 32rpx 0;
+ border-bottom: 1rpx solid #f8f9fa;
+ cursor: pointer;
+ transition: background-color 0.3s ease;
+
+ &:hover {
+ background-color: #f8f9fa;
+ }
+
+ &:last-child {
+ border-bottom: none;
+ }
+
+ .language-name {
+ font-size: 32rpx;
+ color: #333333;
+ }
+ }
+ }
+}
+</style>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/login/privacy-policy-en.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/login/privacy-policy-en.vue
new file mode 100644
index 0000000..53aa021
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/login/privacy-policy-en.vue
@@ -0,0 +1,402 @@
+<route lang="jsonc" type="page">
+{
+ "layout": "default",
+ "style": {
+ "navigationStyle": "custom",
+ "navigationBarTitleText": "Privacy Policy"
+ }
+}
+</route>
+
+<script lang="ts" setup>
+import { t } from '@/i18n'
+
+function goBack() {
+ uni.navigateBack()
+}
+</script>
+
+<template>
+ <view class="h-screen flex flex-col bg-[#f5f8fd]">
+ <wd-navbar
+ :title="t('login.privacyPolicy')"
+ safe-area-inset-top
+ left-arrow
+ :bordered="false"
+ @click-left="goBack"
+ >
+ <template #left>
+ <wd-icon name="arrow-left" size="18" />
+ </template>
+ </wd-navbar>
+ <scroll-view scroll-y class="box-border flex-1 px-[30rpx] py-[30rpx]">
+ <view class="rounded-[16rpx] bg-white px-[30rpx] py-[40rpx] shadow-[0_4rpx_20rpx_rgba(0,0,0,0.05)]">
+ <view class="mb-[40rpx] text-center">
+ <text class="text-[36rpx] text-[#1a1a1a] font-bold">
+ Privacy Policy
+ </text>
+ </view>
+ <view class="mb-[40rpx] text-center">
+ <text class="text-[24rpx] text-[#666666]">
+ Last Updated: March 10, 2026
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ Introduction
+ </text>
+ </view>
+ <view class="mb-[24rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ Welcome to use the XiaoZhi Backend Service (hereinafter referred to as "the Service"). The operator of this Service is the actual deployer and administrator of the Service (hereinafter referred to as "Operator" or "we"). We fully understand the importance of your personal information and will do our best to protect your personal information security.
+ </text>
+ </view>
+ <view class="mb-[24rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ Please carefully read and fully understand all contents of this Privacy Policy before using the Service. Once you start using the Service, it signifies that you have read and agreed to this Privacy Policy.
+ </text>
+ </view>
+ <view class="mb-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ This Privacy Policy applies to our collection, storage, use, sharing, and protection of your personal information when you use the Service through the Admin Console (management backend), API interfaces, and other means.
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[30rpx] text-[#1a1a1a] font-bold">
+ I. Information We Collect
+ </text>
+ </view>
+ <view class="mb-[24rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ To provide services to you, we may need to collect the following information:
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.1 Account Registration Information: When you register an account, we collect your phone number or username, password, and other information to create and verify your account.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.2 Device Information: When you bind hardware devices, we collect device identification information (such as device code, MAC address), device model, firmware version, etc., for device management and service provision.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.3 Agent Configuration Information: When you create and configure Agents, we collect role templates, language model selections, voice parameter configurations, etc., to provide personalized AI interaction services.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.4 Voice Interaction Data: When you use the voice interaction function, the Service uses Voice Activity Detection (VAD) to determine the start and end of your voice, processes your voice input data, and transmits it to third-party Automatic Speech Recognition (ASR) and Large Language Model (LLM) services for processing to achieve voice interaction capabilities.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.5 Image Data: When you use the vision model function, we may process image data captured through the device camera and transmit it to third-party vision model services for analysis and understanding to achieve image recognition, scene understanding, and other functions.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.6 Conversation Memory Data: When you enable the memory function, the Service stores summaries of your interaction history with the Agent to provide more coherent and personalized interaction experiences in subsequent conversations.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.7 Knowledge Base Data: When you use the knowledge base function, we collect and store documents, texts, and other knowledge base content you upload for Agents to perform knowledge retrieval and Q&A during conversations.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.8 Voice Print Data: When you use the voice print recognition function, we collect and store your voice print characteristic samples for speaker identity verification and personalized services.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.9 Chat History Data: We store conversation history records between you and the Agents, including text conversation content, conversation time, interaction context, etc., to provide continuous conversation experience and history query functions.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.10 Conversation Audio Data: When you use the voice interaction function, we may store audio data of your interactions with the Agents to improve voice interaction quality and enable retrieval queries.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.11 Agent Configuration Data: When you configure Agents, we collect tags, plugin configurations, context provider settings, etc., to provide Agent customization services.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.12 Device Firmware Data: When you use the OTA upgrade function, we record device firmware version, upgrade history, etc., for device management and firmware traceability.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.13 Log Information: When you use the Service, we automatically collect service log information, including but not limited to access time, IP address, browser type, operation records, etc., for service operation and security assurance.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.14 Verification Code Information: When you use phone number login, we send verification codes via SMS service for identity verification.
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[30rpx] text-[#1a1a1a] font-bold">
+ II. How We Use the Collected Information
+ </text>
+ </view>
+ <view class="mb-[24rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ The information we collect will be used for the following purposes:
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (1) Providing, maintaining, and improving the Service, including core functions such as account management, device management, and Agent configuration.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (2) Processing your voice interaction requests, using Voice Activity Detection (VAD) to identify voice input, calling third-party AI model services to complete speech recognition, intent recognition, semantic understanding, and speech synthesis.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (3) Processing your image data, calling third-party vision model services to complete image recognition and scene understanding.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (4) Storing and managing your conversation memory data to provide more coherent service experiences in subsequent interactions.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (5) Storing and retrieving knowledge base content you upload so that Agents can provide more accurate knowledge Q&A during conversations.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (6) Ensuring service security and stability, including identity verification, security protection, troubleshooting, etc.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (7) Complying with applicable laws, regulations, and regulatory requirements.
+ </text>
+ </view>
+ <view class="mb-[24rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ We will not use your personal information for purposes unrelated to the above. If we need to use information for other purposes not stated in this Privacy Policy, we will obtain your consent in advance.
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[30rpx] text-[#1a1a1a] font-bold">
+ III. Sharing and Disclosure of Information
+ </text>
+ </view>
+ <view class="mb-[24rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ We will not sell your personal information to third parties. Only in the following circumstances may we share or disclose your information:
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 3.1 Third-party Service Calls: To achieve voice interaction, visual understanding, and other functions, your voice data, image data, and text content may be transmitted to third-party AI service providers for processing.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 3.2 Legal Requirements: According to applicable laws, regulations, legal procedures, or requirements from government authorities, we may need to disclose your personal information.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 3.3 Security Assurance: To protect the Operator, other users, or the public from damage, we may use or disclose personal information within a reasonably necessary scope.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 3.4 With Consent: We may share your personal information with third parties if we obtain your explicit consent.
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[30rpx] text-[#1a1a1a] font-bold">
+ IV. Storage and Protection of Information
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.1 Storage Location: Your personal information will be stored on the server where the Operator deploys the Service, including but not limited to databases (MySQL/PostgreSQL) and cache services (Redis).
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.2 Storage Period: We will retain your personal information for the period necessary to provide you with services. After you cancel your account, we will delete or anonymize your personal information within a reasonable time.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.3 Security Measures: We take reasonable technical and management measures to protect the security of your personal information, including but not limited to data encryption, access control, security auditing, etc.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.4 Security Incident Handling: If a personal information security incident occurs, we will promptly inform you of the basic situation of the security incident, possible impacts, and measures taken or to be taken.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.5 Special Note for Open-source Projects: The Service is an open-source project with two operation modes:
+ </text>
+ </view>
+ <view class="mb-[8rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (1) Self-deployment: If you self-deploy the Service, the Operator is only the code provider, and actual data storage and processing are your responsibility.
+ </text>
+ </view>
+ <view class="mb-[8rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (2) Test Platform: If you use the test platform deployed by the Operator, your data will be managed and protected by the Operator.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.6 Cross-border Data Transfer: When providing intelligent interaction functions, the Service may need to call interfaces from third-party AI service providers outside mainland China.
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[30rpx] text-[#1a1a1a] font-bold">
+ V. Your Rights
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 5.1 Query and Access: You can view and manage your personal information such as account information, device information, and Agent configuration through the Admin Console.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 5.2 Correction and Modification: When you find that your personal information is incorrect, you can correct it through the Admin Console or contact the Operator for assistance.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 5.3 Deletion: Under certain circumstances, you may request us to delete your personal information.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 5.4 Account Cancellation: You can cancel your account through the account settings in the Admin Console or contact the Operator for processing.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 5.5 Withdrawal of Consent: You may withdraw your consent granted to us at any time.
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[30rpx] text-[#1a1a1a] font-bold">
+ VI. Protection of Minor Information
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 6.1 We attach great importance to the protection of minors' personal information. If you are a minor under 18 years of age, please use the Service under the guidance and consent of your guardian.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 6.2 If a guardian discovers that a minor has provided personal information to us without consent, please contact the Operator. We will delete the relevant information as soon as possible.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 6.3 For minors who use the Service with guardian consent, we will provide stricter protection for their personal information in accordance with laws and regulations.
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[30rpx] text-[#1a1a1a] font-bold">
+ VII. Third-party Services Description
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 7.1 When providing intelligent interaction functions, the Service needs to call third-party services, including but not limited to: Voice Activity Detection Service (VAD), Automatic Speech Recognition Service (ASR), Large Language Model Service (LLM), Text-to-Speech Service (TTS), Vision Model Service, SMS Verification Code Service, MQTT Message Broker Service, Database Service.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 7.2 The above third-party service providers will process data according to their respective privacy policies. We recommend that you understand the privacy policies of relevant third-party service providers before using the Service.
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[30rpx] text-[#1a1a1a] font-bold">
+ VIII. Use of Cookies and Similar Technologies
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 8.1 The Service may use Cookies and similar technologies to save your login status, record your preference settings, etc., to provide you with a better user experience.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 8.2 You can manage Cookies through browser settings. However, please note that disabling Cookies may affect some functions of the Service.
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[30rpx] text-[#1a1a1a] font-bold">
+ IX. Changes to the Privacy Policy
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 9.1 We may revise this Privacy Policy based on business adjustments, changes in laws and regulations, and other reasons. The revised Privacy Policy will be published through the Service's announcement mechanisms.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 9.2 If you continue to use the Service after the Privacy Policy revision, it shall be deemed that you have accepted the revised Privacy Policy.
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[30rpx] text-[#1a1a1a] font-bold">
+ X. Information Security Warning
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 10.1 Please do not disclose sensitive personal information such as your property account numbers, bank card numbers, credit card numbers, passwords, ID card numbers, etc., in voice or text interactions with AI. Any losses resulting from this shall be borne by you.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 10.2 Please properly keep your account and password secure, and do not share your account information with others or share accounts with others.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 10.3 If you discover that personal information may have been leaked, please contact the Operator promptly to take measures.
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[30rpx] text-[#1a1a1a] font-bold">
+ XI. Applicable Law
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 11.1 The formulation, interpretation, execution, and dispute resolution of this Privacy Policy shall be governed by the laws of the People's Republic of China (excluding the laws of Hong Kong, Macau, and Taiwan for the purposes of this Privacy Policy).
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 11.2 If any provision of this Privacy Policy conflicts with the current laws and regulations of the People's Republic of China, the provisions of laws and regulations shall prevail, and the remaining provisions shall remain valid.
+ </text>
+ </view>
+ </view>
+ </scroll-view>
+ </view>
+</template>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/login/privacy-policy-zh.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/login/privacy-policy-zh.vue
new file mode 100644
index 0000000..3fffa07
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/login/privacy-policy-zh.vue
@@ -0,0 +1,402 @@
+<route lang="jsonc" type="page">
+{
+ "layout": "default",
+ "style": {
+ "navigationStyle": "custom",
+ "navigationBarTitleText": "闅愮鏀跨瓥"
+ }
+}
+</route>
+
+<script lang="ts" setup>
+import { t } from '@/i18n'
+
+function goBack() {
+ uni.navigateBack()
+}
+</script>
+
+<template>
+ <view class="h-screen flex flex-col bg-[#f5f8fd]">
+ <wd-navbar
+ :title="t('login.privacyPolicy')"
+ safe-area-inset-top
+ left-arrow
+ :bordered="false"
+ @click-left="goBack"
+ >
+ <template #left>
+ <wd-icon name="arrow-left" size="18" />
+ </template>
+ </wd-navbar>
+ <scroll-view scroll-y class="box-border flex-1 px-[20rpx] py-[30rpx]" :scroll-with-animation="true">
+ <view class="rounded-[16rpx] bg-white px-[30rpx] py-[40rpx] shadow-[0_4rpx_20rpx_rgba(0,0,0,0.05)]">
+ <view class="mb-[40rpx] text-center">
+ <text class="text-[36rpx] text-[#1a1a1a] font-bold">
+ {{ t('login.privacyPolicy') }}
+ </text>
+ </view>
+ <view class="mb-[40rpx] text-center">
+ <text class="text-[24rpx] text-[#666666]">
+ 鏇存柊鏃ユ湡锛�2026骞�3鏈�10鏃�
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 寮曡█
+ </text>
+ </view>
+ <view class="mb-[24rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 娆㈣繋鎮ㄤ娇鐢ㄥ皬鏅哄悗绔湇鍔★紙浠ヤ笅绠�绉�"鏈湇鍔�"锛夈�傛湰鏈嶅姟鐨勮繍钀ユ柟涓烘湰鏈嶅姟鐨勫疄闄呴儴缃茶�呭拰绠$悊鑰咃紙浠ヤ笅绠�绉�"杩愯惀鑰�"鎴�"鎴戜滑"锛夈�傛垜浠繁鐭ヤ釜浜轰俊鎭鎮ㄧ殑閲嶈鎬э紝灏嗗敖鍏ㄥ姏淇濇姢鎮ㄧ殑涓汉淇℃伅瀹夊叏銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 璇锋偍鍦ㄤ娇鐢ㄦ湰鏈嶅姟鍓嶏紝浠旂粏闃呰骞跺厖鍒嗙悊瑙f湰闅愮鏀跨瓥鐨勫叏閮ㄥ唴瀹广�備竴鏃︽偍寮�濮嬩娇鐢ㄦ湰鏈嶅姟锛屽嵆琛ㄧず鎮ㄥ凡闃呰骞跺悓鎰忔湰闅愮鏀跨瓥銆�
+ </text>
+ </view>
+ <view class="mb-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 鏈殣绉佹斂绛栭�傜敤浜庢偍閫氳繃鏅烘帶鍙帮紙绠$悊鍚庡彴锛夈�丄PI鎺ュ彛鍙婂叾浠栨柟寮忎娇鐢ㄦ湰鏈嶅姟鏃讹紝鎴戜滑瀵规偍涓汉淇℃伅鐨勬敹闆嗐�佸瓨鍌ㄣ�佷娇鐢ㄣ�佸叡浜�佷繚鎶ょ瓑琛屼负銆�
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[30rpx] text-[#1a1a1a] font-bold">
+ 涓�銆佹垜浠敹闆嗙殑淇℃伅
+ </text>
+ </view>
+ <view class="mb-[24rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 涓哄悜鎮ㄦ彁渚涙湇鍔★紝鎴戜滑鍙兘闇�瑕佹敹闆嗕互涓嬩俊鎭細
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.1 璐﹀彿娉ㄥ唽淇℃伅锛氬綋鎮ㄦ敞鍐岃处鍙锋椂锛屾垜浠細鏀堕泦鎮ㄧ殑鎵嬫満鍙风爜鎴栫敤鎴峰悕銆佸瘑鐮佺瓑淇℃伅锛岀敤浜庡垱寤哄拰楠岃瘉鎮ㄧ殑璐﹀彿銆�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.2 璁惧淇℃伅锛氬綋鎮ㄧ粦瀹氱‖浠惰澶囨椂锛屾垜浠細鏀堕泦璁惧鐨勬爣璇嗕俊鎭紙濡傝澶囩紪鐮併�丮AC鍦板潃绛夛級銆佽澶囧瀷鍙枫�佸浐浠剁増鏈瓑锛岀敤浜庤澶囩鐞嗗拰鏈嶅姟鎻愪緵銆�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.3 鏅鸿兘浣撻厤缃俊鎭細褰撴偍鍒涘缓鍜岄厤缃櫤鑳戒綋鏃讹紝鎴戜滑浼氭敹闆嗘偍璁惧畾鐨勮鑹叉ā鏉裤�佽瑷�妯″瀷閫夋嫨銆佽闊冲弬鏁伴厤缃瓑淇℃伅锛岀敤浜庢彁渚涗釜鎬у寲鐨凙I浜や簰鏈嶅姟銆�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.4 璇煶浜や簰鏁版嵁锛氬湪鎮ㄤ娇鐢ㄨ闊充氦浜掑姛鑳芥椂锛屾湰鏈嶅姟浼氶�氳繃璇煶娲诲姩妫�娴嬶紙VAD锛夊垽鏂偍鐨勮闊宠捣姝㈢姸鎬侊紝骞跺鐞嗘偍鐨勮闊宠緭鍏ユ暟鎹紝灏嗗叾浼犺緭鑷崇涓夋柟璇煶璇嗗埆锛圓SR锛夊拰璇█妯″瀷锛圠LM锛夋湇鍔¤繘琛屽鐞嗭紝浠ュ疄鐜拌闊充氦浜掑姛鑳姐��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.5 鍥惧儚鏁版嵁锛氬綋鎮ㄤ娇鐢ㄨ瑙夋ā鍨嬪姛鑳芥椂锛屾垜浠彲鑳戒細澶勭悊鎮ㄩ�氳繃璁惧鎽勫儚澶撮噰闆嗙殑鍥惧儚鏁版嵁锛屽苟灏嗗叾浼犺緭鑷崇涓夋柟瑙嗚妯″瀷鏈嶅姟杩涜鍒嗘瀽鍜岀悊瑙o紝鐢ㄤ簬瀹炵幇鍥惧儚璇嗗埆銆佸満鏅悊瑙g瓑鍔熻兘銆�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.6 瀵硅瘽璁板繂鏁版嵁锛氬綋鎮ㄥ紑鍚蹇嗗姛鑳芥椂锛屾湰鏈嶅姟浼氬瓨鍌ㄦ偍涓庢櫤鑳戒綋鐨勪氦浜掑巻鍙叉憳瑕侊紝鐢ㄤ簬鍦ㄥ悗缁璇濅腑鎻愪緵鏇磋繛璐�佷釜鎬у寲鐨勪氦浜掍綋楠屻��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.7 鐭ヨ瘑搴撴暟鎹細褰撴偍浣跨敤鐭ヨ瘑搴撳姛鑳芥椂锛屾垜浠細鏀堕泦鍜屽瓨鍌ㄦ偍涓婁紶鐨勬枃妗c�佹枃鏈瓑鐭ヨ瘑搴撳唴瀹癸紝鐢ㄤ簬鏅鸿兘浣撳湪瀵硅瘽涓繘琛岀煡璇嗘绱㈠拰闂瓟銆�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.8 澹扮汗鏁版嵁锛氬綋鎮ㄤ娇鐢ㄥ0绾硅瘑鍒姛鑳芥椂锛屾垜浠細鏀堕泦鍜屽瓨鍌ㄦ偍鐨勫0绾圭壒寰佹牱鏈紝鐢ㄤ簬璇磋瘽浜鸿韩浠介獙璇佸拰涓�у寲鏈嶅姟銆�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.9 鑱婂ぉ鍘嗗彶鏁版嵁锛氭垜浠細瀛樺偍鎮ㄤ笌鏅鸿兘浣撶殑瀵硅瘽鍘嗗彶璁板綍锛屽寘鎷枃鏈璇濆唴瀹广�佸璇濇椂闂淬�佷氦浜掍笂涓嬫枃绛変俊鎭紝鐢ㄤ簬鎻愪緵杩炵画鎬у璇濅綋楠屽拰鍘嗗彶鏌ヨ鍔熻兘銆�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.10 瀵硅瘽闊抽鏁版嵁锛氬湪鎮ㄤ娇鐢ㄨ闊充氦浜掑姛鑳芥椂锛屾垜浠彲鑳戒細瀛樺偍鎮ㄤ笌鏅鸿兘浣撲氦浜掔殑闊抽鏁版嵁锛岀敤浜庝紭鍖栬闊充氦浜掕川閲忓拰鍥炴函鏌ヨ銆�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.11 鏅鸿兘浣撻厤缃暟鎹細褰撴偍閰嶇疆鏅鸿兘浣撴椂锛屾垜浠細鏀堕泦鎮ㄨ瀹氱殑鏍囩銆佹彃浠堕厤缃�佷笂涓嬫枃鎻愪緵鑰呰缃瓑淇℃伅锛岀敤浜庢彁渚涙櫤鑳戒綋瀹氬埗鍖栨湇鍔°��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.12 璁惧鍥轰欢鏁版嵁锛氬綋鎮ㄤ娇鐢∣TA鍗囩骇鍔熻兘鏃讹紝鎴戜滑浼氳褰曡澶囧浐浠剁増鏈�佸崌绾у巻鍙茬瓑淇℃伅锛岀敤浜庤澶囩鐞嗗拰鍥轰欢杩芥函銆�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.13 鏃ュ織淇℃伅锛氬綋鎮ㄤ娇鐢ㄦ湰鏈嶅姟鏃讹紝鎴戜滑浼氳嚜鍔ㄦ敹闆嗘湇鍔℃棩蹇椾俊鎭紝鍖呮嫭浣嗕笉闄愪簬璁块棶鏃堕棿銆両P鍦板潃銆佹祻瑙堝櫒绫诲瀷銆佹搷浣滆褰曠瓑锛岀敤浜庢湇鍔¤繍缁村拰瀹夊叏淇濋殰銆�
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 1.14 楠岃瘉鐮佷俊鎭細褰撴偍浣跨敤鎵嬫満鍙风櫥褰曟椂锛屾垜浠細閫氳繃鐭俊鏈嶅姟鍚戞偍鍙戦�侀獙璇佺爜锛岀敤浜庤韩浠介獙璇併��
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[30rpx] text-[#1a1a1a] font-bold">
+ 浜屻�佹垜浠浣曚娇鐢ㄦ敹闆嗙殑淇℃伅
+ </text>
+ </view>
+ <view class="mb-[24rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 鎴戜滑鏀堕泦鐨勪俊鎭皢鐢ㄤ簬浠ヤ笅鐩殑锛�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�1锛夋彁渚涖�佺淮鎶ゅ拰鏀硅繘鏈湇鍔★紝鍖呮嫭璐﹀彿绠$悊銆佽澶囩鐞嗐�佹櫤鑳戒綋閰嶇疆绛夋牳蹇冨姛鑳姐��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�2锛夊鐞嗘偍鐨勮闊充氦浜掕姹傦紝閫氳繃璇煶娲诲姩妫�娴嬶紙VAD锛夎瘑鍒闊宠緭鍏ワ紝璋冪敤绗笁鏂笰I妯″瀷鏈嶅姟瀹屾垚璇煶璇嗗埆銆佹剰鍥捐瘑鍒�佽涔夌悊瑙e拰璇煶鍚堟垚銆�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�3锛夊鐞嗘偍鐨勫浘鍍忔暟鎹紝璋冪敤绗笁鏂硅瑙夋ā鍨嬫湇鍔″畬鎴愬浘鍍忚瘑鍒拰鍦烘櫙鐞嗚В銆�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�4锛夊瓨鍌ㄥ拰绠$悊鎮ㄧ殑瀵硅瘽璁板繂鏁版嵁锛屼互渚垮湪鍚庣画浜や簰涓彁渚涙洿杩炶疮鐨勬湇鍔′綋楠屻��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�5锛夊瓨鍌ㄥ拰妫�绱㈡偍涓婁紶鐨勭煡璇嗗簱鍐呭锛屼互渚挎櫤鑳戒綋鍦ㄥ璇濅腑涓烘偍鎻愪緵鏇村噯纭殑鐭ヨ瘑闂瓟銆�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�6锛変繚闅滄湇鍔$殑瀹夊叏鎬у拰绋冲畾鎬э紝鍖呮嫭韬唤楠岃瘉銆佸畨鍏ㄩ槻鎶ゃ�佹晠闅滄帓鏌ョ瓑銆�
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�7锛夐伒瀹堥�傜敤鐨勬硶寰嬫硶瑙勫拰鐩戠瑕佹眰銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 鎴戜滑涓嶄細灏嗘偍鐨勪釜浜轰俊鎭敤浜庝笌涓婅堪鐩殑鏃犲叧鐨勫叾浠栫敤閫斻�傚闇�灏嗕俊鎭敤浜庢湰闅愮鏀跨瓥鏈浇鏄庣殑鍏朵粬鐩殑锛屾垜浠細浜嬪厛寰佸緱鎮ㄧ殑鍚屾剰銆�
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[30rpx] text-[#1a1a1a] font-bold">
+ 涓夈�佷俊鎭殑鍏变韩涓庢姭闇�
+ </text>
+ </view>
+ <view class="mb-[24rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 鎴戜滑涓嶄細鍚戠涓夋柟鍑哄敭鎮ㄧ殑涓汉淇℃伅銆備粎鍦ㄤ互涓嬫儏褰笅锛屾垜浠彲鑳戒細鍏变韩鎴栨姭闇叉偍鐨勪俊鎭細
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 3.1 绗笁鏂规湇鍔¤皟鐢細涓哄疄鐜拌闊充氦浜掋�佽瑙夌悊瑙g瓑鍔熻兘锛屾偍鐨勮闊虫暟鎹�佸浘鍍忔暟鎹拰鏂囨湰鍐呭鍙兘浼氳浼犺緭鑷崇涓夋柟AI鏈嶅姟鎻愪緵鍟嗭紙濡傝瑷�妯″瀷銆佽闊宠瘑鍒�佽闊冲悎鎴愩�佽瑙夋ā鍨嬫湇鍔″晢锛夎繘琛屽鐞嗐�傛垜浠細閫夋嫨鍏峰鍚堢悊瀹夊叏淇濋殰鑳藉姏鐨勬湇鍔″晢锛屼絾璇锋偍鐭ユ倝绗笁鏂规湇鍔″晢灏嗘寜鐓у叾鑷韩鐨勯殣绉佹斂绛栧鐞嗙浉鍏虫暟鎹��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 3.2 娉曞緥瑕佹眰锛氭牴鎹�傜敤鐨勬硶寰嬫硶瑙勩�佹硶寰嬬▼搴忔垨鏀垮簻涓荤閮ㄩ棬鐨勮姹傦紝鎴戜滑鍙兘闇�瑕佹姭闇叉偍鐨勪釜浜轰俊鎭��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 3.3 瀹夊叏淇濋殰锛氫负淇濇姢杩愯惀鑰呫�佸叾浠栫敤鎴锋垨鍏紬鐨勬潈鐩娿�佽储浜ф垨瀹夊叏鍏嶉伃鎹熷锛屽湪鍚堢悊蹇呰鐨勮寖鍥村唴浣跨敤鎴栨姭闇蹭釜浜轰俊鎭��
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 3.4 寰佸緱鍚屾剰锛氬湪鑾峰緱鎮ㄦ槑纭悓鎰忕殑鍓嶆彁涓嬶紝鎴戜滑鍙兘浼氫笌绗笁鏂瑰叡浜偍鐨勪釜浜轰俊鎭��
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[30rpx] text-[#1a1a1a] font-bold">
+ 鍥涖�佷俊鎭殑瀛樺偍涓庝繚鎶�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.1 瀛樺偍鍦扮偣锛氭偍鐨勪釜浜轰俊鎭皢瀛樺偍鍦ㄨ繍钀ヨ�呴儴缃叉湰鏈嶅姟鐨勬湇鍔″櫒涓婏紝鍖呮嫭浣嗕笉闄愪簬鏁版嵁搴擄紙MySQL/PostgreSQL锛夊拰缂撳瓨鏈嶅姟锛圧edis锛夈��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.2 瀛樺偍鏈熼檺锛氭垜浠皢鍦ㄤ负鎮ㄦ彁渚涙湇鍔℃墍蹇呴渶鐨勬湡闄愬唴淇濈暀鎮ㄧ殑涓汉淇℃伅銆傚綋鎮ㄦ敞閿�璐﹀彿鍚庯紝鎴戜滑灏嗗湪鍚堢悊鏃堕棿鍐呭垹闄ゆ垨鍖垮悕鍖栧鐞嗘偍鐨勪釜浜轰俊鎭紝娉曞緥娉曡鍙︽湁瑙勫畾鐨勯櫎澶栥��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.3 瀹夊叏鎺柦锛氭垜浠噰鍙栧悎鐞嗙殑鎶�鏈拰绠$悊鎺柦淇濇姢鎮ㄧ殑涓汉淇℃伅瀹夊叏锛屽寘鎷絾涓嶉檺浜庢暟鎹姞瀵嗐�佽闂帶鍒躲�佸畨鍏ㄥ璁$瓑銆備絾璇锋偍鐞嗚В锛屼簰鑱旂綉骞堕潪缁濆瀹夊叏鐨勭幆澧冿紝鎴戜滑鏃犳硶淇濊瘉淇℃伅浼犺緭鍜屽瓨鍌ㄧ殑缁濆瀹夊叏鎬с��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.4 瀹夊叏浜嬩欢澶勭悊锛氬鍙戠敓涓汉淇℃伅瀹夊叏浜嬩欢锛屾垜浠皢鎸夌収娉曞緥娉曡鐨勮姹傦紝鍙婃椂鍚戞偍鍛婄煡瀹夊叏浜嬩欢鐨勫熀鏈儏鍐点�佸彲鑳界殑褰卞搷銆佸凡閲囧彇鎴栧皢瑕侀噰鍙栫殑澶勭疆鎺柦绛夈��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.5 寮�婧愰」鐩壒鍒鏄庯細鏈湇鍔′负寮�婧愰」鐩紝瀛樺湪涓ょ杩愯惀妯″紡锛�
+ </text>
+ </view>
+ <view class="mb-[8rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�1锛夎嚜琛岄儴缃诧細濡傛偍鑷閮ㄧ讲鏈湇鍔★紝杩愯惀鑰呬粎涓轰唬鐮佹彁渚涙柟锛屽疄闄呯殑鏁版嵁瀛樺偍鍜屽鐞嗙敱鎮ㄨ嚜琛岃礋璐c��
+ </text>
+ </view>
+ <view class="mb-[8rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�2锛夋祴璇曞钩鍙帮細濡傛偍浣跨敤杩愯惀鑰呴儴缃茬殑娴嬭瘯骞冲彴锛屾偍鐨勬暟鎹皢鐢辫繍钀ヤ汉鍛樿礋璐g鐞嗗拰淇濇姢銆�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.6 鏁版嵁璺ㄥ浼犺緭锛氭湰鏈嶅姟鍦ㄦ彁渚涙櫤鑳戒氦浜掑姛鑳芥椂锛屽彲鑳介渶瑕佽皟鐢ㄥ澶栫涓夋柟AI鏈嶅姟鎻愪緵鍟嗙殑鎺ュ彛銆�
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[30rpx] text-[#1a1a1a] font-bold">
+ 浜斻�佹偍鐨勬潈鍒�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 5.1 鏌ヨ涓庤闂細鎮ㄥ彲閫氳繃鏅烘帶鍙版煡鐪嬪拰绠$悊鎮ㄧ殑璐﹀彿淇℃伅銆佽澶囦俊鎭�佹櫤鑳戒綋閰嶇疆绛変釜浜轰俊鎭��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 5.2 鏇存涓庝慨鏀癸細褰撴偍鍙戠幇涓汉淇℃伅鏈夎鏃讹紝鎮ㄥ彲閫氳繃鏅烘帶鍙拌嚜琛屾洿姝o紝鎴栬仈绯昏繍钀ヨ�呭崗鍔╁鐞嗐��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 5.3 鍒犻櫎锛氬湪浠ヤ笅鎯呭舰涓嬶紝鎮ㄥ彲璇锋眰鎴戜滑鍒犻櫎鎮ㄧ殑涓汉淇℃伅锛氾紙1锛夊鐞嗙洰鐨勫凡瀹炵幇銆佹棤娉曞疄鐜版垨涓嶅啀蹇呰锛涳紙2锛夋垜浠繚鍙嶆硶寰嬫硶瑙勬垨涓庢偍鐨勭害瀹氭敹闆嗐�佷娇鐢ㄤ釜浜轰俊鎭紱锛�3锛夋敞閿�璐﹀彿銆�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 5.4 璐﹀彿娉ㄩ攢锛氭偍鍙�氳繃鏅烘帶鍙扮殑璐﹀彿璁剧疆鍔熻兘娉ㄩ攢璐﹀彿锛屾垨鑱旂郴杩愯惀鑰呰繘琛屽鐞嗐��
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 5.5 鎾ゅ洖鍚屾剰锛氭偍鍙殢鏃舵挙鍥炴偍姝ゅ墠鍚戞垜浠綔鍑虹殑鍚屾剰鎺堟潈銆�
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[30rpx] text-[#1a1a1a] font-bold">
+ 鍏�佹湭鎴愬勾浜轰俊鎭繚鎶�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 6.1 鎴戜滑楂樺害閲嶈鏈垚骞翠汉涓汉淇℃伅鐨勪繚鎶ゃ�傝嫢鎮ㄦ槸鏈弧18鍛ㄥ瞾鐨勬湭鎴愬勾浜猴紝璇峰湪鐩戞姢浜虹殑鎸囧鍜屽悓鎰忎笅浣跨敤鏈湇鍔°��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 6.2 濡傛灉鐩戞姢浜哄彂鐜版湭鎴愬勾浜烘湭缁忓叾鍚屾剰鍚戞垜浠彁渚涗簡涓汉淇℃伅锛岃鑱旂郴杩愯惀鑰咃紝鎴戜滑灏嗗敖蹇垹闄ょ浉鍏充俊鎭��
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 6.3 瀵逛簬缁忕洃鎶や汉鍚屾剰浣跨敤鏈湇鍔$殑鏈垚骞翠汉锛屾垜浠皢鎸夌収娉曞緥娉曡鐨勮瀹氾紝缁欎簣鍏朵釜浜轰俊鎭洿涓ユ牸鐨勪繚鎶ゃ��
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[30rpx] text-[#1a1a1a] font-bold">
+ 涓冦�佺涓夋柟鏈嶅姟璇存槑
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 7.1 鏈湇鍔″湪鎻愪緵鏅鸿兘浜や簰鍔熻兘鏃讹紝闇�瑕佽皟鐢ㄧ涓夋柟鏈嶅姟锛屽寘鎷絾涓嶉檺浜庯細璇煶娲诲姩妫�娴嬫湇鍔★紙VAD锛夈�佽闊宠瘑鍒湇鍔★紙ASR锛夈�佸ぇ璇█妯″瀷鏈嶅姟锛圠LM锛夈�佽闊冲悎鎴愭湇鍔★紙TTS锛夈�佽瑙夋ā鍨嬫湇鍔°�佺煭淇¢獙璇佺爜鏈嶅姟銆丮QTT娑堟伅浠g悊鏈嶅姟銆佹暟鎹簱鏈嶅姟銆�
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 7.2 涓婅堪绗笁鏂规湇鍔℃彁渚涘晢灏嗘寜鐓у叾鍚勮嚜鐨勯殣绉佹斂绛栧鏁版嵁杩涜澶勭悊銆傛垜浠缓璁偍鍦ㄤ娇鐢ㄦ湰鏈嶅姟鍓嶏紝浜嗚В鐩稿叧绗笁鏂规湇鍔″晢鐨勯殣绉佹斂绛栥��
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[30rpx] text-[#1a1a1a] font-bold">
+ 鍏�丆ookie鍙婄被浼兼妧鏈殑浣跨敤
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 8.1 鏈湇鍔″彲鑳戒娇鐢–ookie鍙婄被浼兼妧鏈潵淇濆瓨鎮ㄧ殑鐧诲綍鐘舵�併�佽褰曟偍鐨勫亸濂借缃瓑锛屼互渚夸负鎮ㄦ彁渚涙洿濂界殑浣跨敤浣撻獙銆�
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 8.2 鎮ㄥ彲浠ラ�氳繃娴忚鍣ㄨ缃鐞咰ookie銆備絾璇锋敞鎰忥紝濡傛灉绂佺敤Cookie锛屽彲鑳戒細褰卞搷鏈湇鍔$殑閮ㄥ垎鍔熻兘銆�
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[30rpx] text-[#1a1a1a] font-bold">
+ 涔濄�侀殣绉佹斂绛栫殑鍙樻洿
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 9.1 鎴戜滑鍙兘浼氭牴鎹笟鍔¤皟鏁淬�佹硶寰嬫硶瑙勫彉鍖栫瓑鍘熷洜瀵规湰闅愮鏀跨瓥杩涜淇銆備慨璁㈠悗鐨勯殣绉佹斂绛栧皢閫氳繃鏈钩鍙板叕鍛婄瓑鏂瑰紡浜堜互鍙戝竷銆�
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 9.2 濡傛偍鍦ㄩ殣绉佹斂绛栦慨璁㈠悗缁х画浣跨敤鏈湇鍔★紝鍒欒涓烘偍宸叉帴鍙椾慨璁㈠悗鐨勯殣绉佹斂绛栥��
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[30rpx] text-[#1a1a1a] font-bold">
+ 鍗併�佷俊鎭畨鍏ㄦ彁绀�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 10.1 璇峰嬁鍦ㄤ笌AI鐨勮闊虫垨鏂囧瓧浜や簰涓�忛湶鎮ㄧ殑璐骇璐︽埛銆侀摱琛屽崱鍙枫�佷俊鐢ㄥ崱鍙枫�佸瘑鐮併�佽韩浠借瘉鍙风瓑鏁忔劅涓汉淇℃伅锛屽惁鍒欑敱姝ゅ甫鏉ョ殑鎹熷け鐢辨偍鑷鎵挎媴銆�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 10.2 璇峰Ε鍠勪繚绠℃偍鐨勮处鍙峰拰瀵嗙爜锛屼笉瑕佸皢璐﹀彿淇℃伅鍛婄煡浠栦汉鎴栦笌浠栦汉鍏变韩璐﹀彿銆�
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 10.3 濡傛偍鍙戠幇涓汉淇℃伅鍙兘琚硠闇诧紝璇峰強鏃惰仈绯昏繍钀ヨ�呴噰鍙栨帾鏂姐��
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[30rpx] text-[#1a1a1a] font-bold">
+ 鍗佷竴銆侀�傜敤娉曞緥
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 11.1 鏈殣绉佹斂绛栫殑鍒跺畾銆佽В閲娿�佹墽琛屽強浜夎瑙e喅鍧囬�傜敤涓崕浜烘皯鍏卞拰鍥芥硶寰嬫硶瑙勶紙涓烘湰闅愮鏀跨瓥涔嬬洰鐨勶紝涓嶅寘鎷腐婢冲彴鍦板尯娉曞緥锛夈��
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 11.2 濡傛湰闅愮鏀跨瓥鐨勪换浣曟潯娆句笌涓崕浜烘皯鍏卞拰鍥界幇琛屾硶寰嬫硶瑙勭浉鎶佃Е锛屼互娉曞緥娉曡鐨勮瀹氫负鍑嗭紝鍏朵綑鏉℃浠嶇劧鏈夋晥銆�
+ </text>
+ </view>
+ </view>
+ </scroll-view>
+ </view>
+</template>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/login/user-agreement-en.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/login/user-agreement-en.vue
new file mode 100644
index 0000000..be9369a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/login/user-agreement-en.vue
@@ -0,0 +1,539 @@
+<route lang="jsonc" type="page">
+{
+ "layout": "default",
+ "style": {
+ "navigationStyle": "custom",
+ "navigationBarTitleText": "User Agreement"
+ }
+}
+</route>
+
+<script lang="ts" setup>
+import { t } from '@/i18n'
+
+function goBack() {
+ uni.navigateBack()
+}
+</script>
+
+<template>
+ <view class="h-screen flex flex-col bg-[#f5f8fd]">
+ <wd-navbar
+ :title="t('login.userAgreement')"
+ safe-area-inset-top
+ left-arrow
+ :bordered="false"
+ @click-left="goBack"
+ >
+ <template #left>
+ <wd-icon name="arrow-left" size="18" />
+ </template>
+ </wd-navbar>
+ <scroll-view scroll-y class="box-border flex-1 px-[30rpx] py-[30rpx]">
+ <view class="rounded-[16rpx] bg-white px-[30rpx] py-[40rpx] shadow-[0_4rpx_20rpx_rgba(0,0,0,0.05)]">
+ <view class="mb-[40rpx] text-center">
+ <text class="text-[36rpx] text-[#1a1a1a] font-bold">
+ User Agreement
+ </text>
+ </view>
+ <view class="mb-[40rpx] text-center">
+ <text class="text-[24rpx] text-[#666666]">
+ Last Updated: March 10, 2026
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ Important Notice
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ Welcome to use the XiaoZhi Backend Service (hereinafter referred to as "the Service"). The Service is designed to provide backend service support for XiaoZhi AI hardware devices, including but not limited to intelligent voice interaction, visual understanding, intent recognition, conversation memory, knowledge base Q&A, device management, and agent management. The operator of this Service is the actual deployer and administrator of the Service (hereinafter referred to as "Operator" or "we").
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ Before registering, logging in, or using the Service, please carefully read and fully understand all terms of this Agreement, especially the terms that are highlighted in bold and may have significant impact on your interests, including but not limited to disclaimers and liability limitations.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ When you complete registration, login, or otherwise use the Service following the on-screen instructions, it signifies that you have fully read, understood, and accepted all contents of this Agreement. If you disagree with this Agreement or any of its terms, please stop using the Service immediately.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ This Agreement may be updated and revised based on actual circumstances. The revised Agreement will be published through the Service's announcement mechanisms. The revised Agreement shall take effect from the date of publication. If you continue to use the Service after the Agreement revision, it shall be deemed that you have accepted the revised Agreement.
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ I. Definitions
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ XiaoZhi Backend Service: refers to the XiaoZhi Backend Service and its backend service system, including but not limited to the Admin Console (management backend), API interfaces, WebSocket communication services, etc.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ Admin Console: refers to the web management interface provided by the Service for device management, agent configuration, user management, and other functions.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ User: refers to natural persons, legal persons, or other organizations who register or otherwise use the Service, hereinafter referred to as "you".
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ Intelligent Agent (Agent): refers to an AI character created and configured within the Service, containing a collection of capabilities such as language models, speech recognition, speech synthesis, visual understanding, intent recognition, conversation memory, and knowledge base.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ Device: refers to ESP32 and other hardware devices connected and managed through the Service.
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ II. Scope of Agreement
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ This Agreement is a legal agreement between you and the Operator regarding your use of the Service. This Agreement applies to all your actions when using the Service through the Admin Console or other means.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ The source code of the Service follows the corresponding open-source license. This Agreement governs your use of the Service and does not affect the rights granted to you by the open-source license itself.
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ III. Account Registration and Use
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 3.1 User Eligibility: You confirm that before using the Service, you should have the capacity for civil conduct as required by the laws of the People's Republic of China. If you are a minor, you should use the Service under the company and guidance of your legal guardian.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 3.2 Account Registration: You may register and log in to the Service through mobile phone verification code, username/password, or other methods. You shall provide true, accurate, and complete registration information and update it promptly.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 3.3 Account Security: You shall properly keep your account and password safe. All consequences arising from account leakage or unauthorized use by others due to your reasons shall be borne by you. If you discover any security risks with your account, please contact the Operator immediately.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 3.4 Account Usage Restrictions: Your account is for your personal use only. Without the Operator's consent, you shall not transfer, rent, or lend your account to any third party in any way.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 3.5 Account Cancellation: If you need to cancel your account, you may do so through the account settings in the Admin Console or contact the Operator for processing. After account cancellation, related data will be deleted and cannot be recovered. Please proceed with caution.
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ IV. Service Content
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.1 Service Overview: Relying on artificial intelligence technology, the Service provides intelligent interaction services for connected hardware devices through API calls to third-party generative AI models, including but not limited to Voice Activity Detection (VAD), Automatic Speech Recognition (ASR), Large Language Model (LLM), Text-to-Speech (TTS), Vision Large Language Model (VLLM), Intent Recognition, Conversation Memory, and Retrieval-Augmented Generation (RAG).
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.2 Agent Management: You can create and configure Agents through the Admin Console, including setting role templates, selecting language models, configuring voice parameters, setting intent recognition rules, managing knowledge bases, enabling conversation memory, configuring Agent plugins, etc. The Agent settings shall not violate national laws and regulations.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.3 Device Management: You can bind and manage your hardware devices through the Admin Console, perform device configuration, firmware updates, OTA remote upgrades, etc. You shall ensure that the devices you manage are lawfully owned or authorized by you.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.4 Visual Understanding: The Service supports image capture through device cameras and uses third-party vision models for image recognition and scene understanding. You shall ensure that image capture complies with relevant laws and regulations and does not infringe upon others' privacy.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.5 Intent Recognition: The Service can understand the intent of user commands through intent recognition and trigger corresponding operations or services, such as controlling smart devices, querying information, etc.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.6 Conversation Memory: The Service can record and store summaries of your interaction history with the Agent to provide more coherent and personalized interaction experiences in subsequent conversations. You can manage or clear memory data in the Admin Console.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.7 Knowledge Base: The Service supports uploading documents, texts, and other content to build knowledge bases. Agents can retrieve knowledge base content during conversations to provide Q&A services. You shall ensure that uploaded knowledge base content does not infringe upon third-party intellectual property rights and does not contain content that violates laws and regulations.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.8 Voice Print Recognition: The Service supports voice print recognition. You can register voice print samples to achieve speaker identity verification and personalized services. Voice print data will be used for identity verification purposes.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.9 Voice Cloning: The Service supports voice cloning. You can clone custom voice timbres by providing audio samples. Cloned voices are for your personal use only and shall not be used to impersonate others or engage in illegal activities.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.10 MCP Endpoints: the Service supports MCP (Model Context Protocol) and can serve as an MCP Server to provide tool calling capabilities to external systems, or as an MCP Client to call external MCP services, achieving standardized integration with external systems.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.11 Context Providers: The Service supports Context Provider functionality, which can obtain real-time information from external data sources, such as weather, news, stocks, etc., providing Agents with richer knowledge sources.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.12 MQTT Protocol: The Service supports MQTT (Message Queuing Telemetry Transport) protocol for message communication between IoT devices. You can use the MQTT protocol to send device control commands, monitor device status, etc.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.13 Service Disclaimer: The conversations and responses generated by third-party AI models that the Service depends on are for reference only and do not constitute professional advice in any field. You shall not use the output content as professional advice in medical, legal, financial, or other professional fields. Any judgments or decisions you make based on the output content shall be entirely at your own responsibility.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.14 Service Fees: The Service is a free open-source project and does not charge any usage fees from users. It only provides platform capabilities for calling third-party services and does not involve any paid functions or value-added services.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ The Service supports multiple AI service providers with different billing models as follows:
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[60rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (1) Free Services: Some service providers offer free quotas or completely free services, such as Zhipu AI (free models like glm-4-flash), Microsoft EdgeTTS voice synthesis, etc.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[60rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (2) Pay-as-you-go: Some service providers charge based on API usage, such as OpenAI, Alibaba Cloud Intelligent Speech, Baidu Wenxin, iFlytek, etc. You need to pay the corresponding fees to third-party service providers yourself. Such fees are unrelated to this Service.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[60rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (3) Local Deployment: The Service supports fully local AI deployment solutions, such as FunASR speech recognition, FishSpeech voice synthesis, Ollama local large models, etc. Local deployment does not require network fees or API usage fees but requires sufficient local computing resources.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.15 Service Changes: The Operator reserves the right to adjust, upgrade, or terminate the Service content as actual circumstances require, and will provide notice as far in advance as possible.
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ V. User Conduct Standards
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 5.1 Legal Use: When using the Service, you shall comply with the laws and regulations of the People's Republic of China and relevant international treaties, and shall not use the Service for any illegal activities.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 5.2 Prohibited Conduct: You promise not to engage in the following behaviors when using the Service:
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (1) Publishing, transmitting, or storing content that endangers national security or social stability, including but not limited to content involving subverting state power, damaging national honor and interests, or inciting ethnic hatred.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (2) Publishing, transmitting, or storing content that infringes upon the legitimate rights and interests of others, including but not limited to infringing upon intellectual property rights, privacy rights, reputation rights, etc.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (3) Publishing, transmitting, or storing obscene, pornographic, violent, terrorist, or crime-inciting content.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (4) Publishing false information, spreading rumors, or engaging in fraudulent activities.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (5) Interfering with the normal operation of the Service in any way, including but not limited to attacking servers, spreading malicious programs, or malicious consuming system resources.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (6) Unauthorized reverse engineering, decompiling, or attempting to obtain system source code of the Service (this clause does not limit your lawful use of open-source code according to the open-source license).
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (7) Using the Service for automated batch operations, malicious consuming API call quotas or other system resources.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (8) Entering content that violates laws, regulations, or public order and good customs in Agent settings.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (9) Using the Service to harm or attempt to harm minors.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (10) Other behaviors that violate laws, regulations, this Agreement, or may harm the legitimate rights and interests of the Operator or third parties.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 5.3 Handling of Violations: If you violate the above conduct standards, the Operator has the right to take measures such as warnings, feature restrictions, service suspension, or account banning based on the severity of the situation, and reserves the right to pursue legal liability.
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ VI. Intellectual Property
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 6.1 Open-source License: The Service is an open-source project, and its source code follows the corresponding open-source license. You may use the relevant source code in compliance with the license terms.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 6.2 Service Content Ownership: Except for open-source code, the intellectual property rights of interface designs, icons, copy, and other independently created content in the Service belong to the Operator.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 6.3 User Content: The intellectual property rights of content you input, upload, or generate through the Service shall be determined according to relevant laws and regulations. You grant the Operator the right to store and process your input content for the purpose of maintaining and improving the Service.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 6.4 Third-party Rights: When using the Service, you should respect the intellectual property rights and other legitimate rights of third parties. You shall bear all responsibilities for any disputes arising from your infringement of third-party rights.
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ VII. Privacy Protection
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 7.1 Information Collection: To provide the Service, the Operator may collect your registration information (such as phone number, username), device information, usage logs, etc. For specific personal information collection and usage rules, please refer to the Privacy Policy.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 7.2 Information Protection: The Operator will take reasonable technical and management measures to protect the security of your personal information. However, due to the openness of the Internet, the Operator cannot absolutely guarantee information security. Please pay attention to protecting your personal sensitive information.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 7.3 Information Security Warning: Please do not disclose your sensitive personal information such as property accounts, bank card numbers, passwords, etc. to AI during your use of the Service. Any losses resulting from this shall be borne by you.
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ VIII. Disclaimer
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 8.1 AI-Generated Content: The Service relies on third-party AI models to provide intelligent interaction capabilities. The content generated by AI may be inaccurate, incomplete, or inappropriate. The Operator does not make any guarantees regarding the authenticity, accuracy, or completeness of AI-generated content.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 8.2 Service Interruption: The Operator shall not be responsible for service interruptions or abnormalities caused by the following reasons:
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (1) Force majeure factors, such as natural disasters, pandemics, wars, etc.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (2) Public service factors such as power supply failures or communication network failures.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (3) Service failures or policy adjustments of third-party AI model service providers.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (4) Temporary service interruptions caused by system maintenance or upgrades.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (5) Network security incidents such as hacker attacks or computer viruses.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (6) Service adjustments due to laws, regulations, or government controls.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (7) Other situations not caused by the Operator's fault.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 8.3 Third-party Services: The Service may involve third-party provided language models, speech recognition, speech synthesis, vision models, intent recognition, and other services. You should also comply with the terms of service of third parties when using them. For issues arising from third-party services, please contact the third party directly.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 8.4 Open-source Statement: The Service is an open-source project provided "as is". To the maximum extent permitted by law, the Operator does not make any express or implied guarantees regarding the merchantability, fitness for a particular purpose, or non-infringement of the Service.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 8.5 Special Open-source Software Statement: This project is open-source software. This Service has no commercial cooperation relationship with any third-party API service providers it connects to (including but not limited to platforms such as speech recognition, large language models, speech synthesis, etc.) and does not provide any form of guarantee for their service quality and capital security. This software does not host any account keys, does not participate in capital flow, and does not bear the risk of recharge capital losses.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 8.6 Security Warning: This project functions are not fully developed and have not passed network security assessment. Please do not use it in production environments. If you deploy this project for learning in a public network environment, you must take necessary protective measures, including but not limited to setting strong passwords, restricting access permissions, enabling HTTPS encrypted transmission, etc.
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ IX. Protection of Minors
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 9.1 If you are a minor under 18 years of age, you should use the Service under the guidance and consent of your guardian.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 9.2 Guardians should strengthen supervision of minors using the Service, guide minors to use it reasonably, and avoid excessive reliance.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 9.3 Minors should pay attention to personal information protection when using the Service and avoid uploading or disclosing personal sensitive information.
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ X. Agreement Termination
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 10.1 User Termination: You have the right to stop using the Service and cancel your account at any time.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 10.2 Operator Termination: The Operator has the right to terminate providing services to you under the following circumstances:
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (1) You violate the terms of this Agreement.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (2) You use the Service for illegal activities.
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (3) As required by laws, regulations, or policies.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ (4) The Operator decides to stop operating the Service.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 10.3 Post-termination Handling: After the Agreement is terminated, the Operator is not obligated to retain your account information and related data. The Operator still has the right to pursue your breach of contract liability during the validity period of the Agreement.
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ XI. Governing Law and Dispute Resolution
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 11.1 The formation, effectiveness, interpretation, revision, termination, and dispute resolution of this Agreement are governed by the laws of the People's Republic of China.
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 11.2 Any disputes arising from this Agreement or the Service shall be resolved through friendly negotiation between both parties; if negotiation fails, either party has the right to bring a lawsuit to the people's court with jurisdiction at the Operator's location.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 11.3 If any provision of this Agreement is determined to be invalid or unenforceable, it does not affect the validity of the remaining provisions.
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ XII. Miscellaneous
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 12.1 This Agreement constitutes the complete agreement between you and the Operator regarding your use of the Service.
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 12.2 The Operator's failure to exercise or delay in exercising any rights under this Agreement does not constitute a waiver of such rights.
+ </text>
+ </view>
+ </view>
+ </scroll-view>
+ </view>
+</template>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/login/user-agreement-zh.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/login/user-agreement-zh.vue
new file mode 100644
index 0000000..e612a18
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/login/user-agreement-zh.vue
@@ -0,0 +1,542 @@
+<route lang="jsonc" type="page">
+{
+ "layout": "default",
+ "style": {
+ "navigationStyle": "custom",
+ "navigationBarTitleText": "鐢ㄦ埛鍗忚"
+ }
+}
+</route>
+
+<script lang="ts" setup>
+import { t } from '@/i18n'
+
+function goBack() {
+ uni.navigateBack()
+}
+</script>
+
+<template>
+ <view class="h-screen flex flex-col bg-[#f5f8fd]">
+ <wd-navbar
+ :title="t('login.userAgreement')"
+ safe-area-inset-top
+ left-arrow
+ :bordered="false"
+ @click-left="goBack"
+ >
+ <template #left>
+ <wd-icon name="arrow-left" size="18" />
+ </template>
+ </wd-navbar>
+ <scroll-view scroll-y class="box-border flex-1 px-[30rpx] py-[30rpx]">
+ <view class="rounded-[16rpx] bg-white px-[30rpx] py-[40rpx] shadow-[0_4rpx_20rpx_rgba(0,0,0,0.05)]">
+ <view class="mb-[40rpx] text-center">
+ <text class="text-[36rpx] text-[#1a1a1a] font-bold">
+ {{ t('login.userAgreement') }}
+ </text>
+ </view>
+ <view class="mb-[40rpx] text-center">
+ <text class="text-[24rpx] text-[#666666]">
+ 鏇存柊鏃ユ湡锛�2026骞�3鏈�10鏃�
+ </text>
+ </view>
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ 鎻愮ず鏉℃
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 娆㈣繋鎮ㄤ娇鐢ㄥ皬鏅哄悗绔湇鍔★紙浠ヤ笅绠�绉�"鏈湇鍔�"锛夈�傛湰鏈嶅姟鏃ㄥ湪涓哄皬鏅篈I纭欢璁惧鎻愪緵鍚庣鏈嶅姟鏀寔锛屽寘鎷絾涓嶉檺浜庢櫤鑳借闊充氦浜掋�佽瑙夌悊瑙c�佹剰鍥捐瘑鍒�佸璇濊蹇嗐�佺煡璇嗗簱闂瓟銆佽澶囩鐞嗐�佹櫤鑳戒綋绠$悊绛夊姛鑳姐�傛湰鏈嶅姟鐨勮繍钀ユ柟涓烘湰鏈嶅姟鐨勫疄闄呴儴缃茶�呭拰绠$悊鑰咃紙浠ヤ笅绠�绉�"杩愯惀鑰�"鎴�"鎴戜滑"锛夈��
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 鍦ㄦ偍娉ㄥ唽銆佺櫥褰曟垨浣跨敤鏈湇鍔′箣鍓嶏紝璇锋偍鍔″繀瀹℃厧闃呰銆佸厖鍒嗙悊瑙f湰鍗忚鍚勬潯娆惧唴瀹癸紝鐗瑰埆鏄互鍔犵矖褰㈠紡鎻愮ず鎮ㄦ敞鎰忕殑鍙兘涓庢偍鍒╃泭鏈夐噸澶у叧绯荤殑鏉℃锛屽寘鎷絾涓嶉檺浜庡厤璐e0鏄庛�佽矗浠婚檺鍒剁瓑鏉℃銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 褰撴偍鎸夌収椤甸潰鎻愮ず瀹屾垚娉ㄥ唽銆佺櫥褰曟垨浠ュ叾浠栨柟寮忎娇鐢ㄦ湰鏈嶅姟鏃讹紝鍗宠〃绀烘偍宸插厖鍒嗛槄璇汇�佺悊瑙e苟鎺ュ彈鏈崗璁殑鍏ㄩ儴鍐呭銆傚鏋滄偍涓嶅悓鎰忔湰鍗忚鎴栧叾涓换浣曟潯娆撅紝璇风珛鍗冲仠姝娇鐢ㄦ湰鏈嶅姟銆�
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 鏈崗璁彲鑳芥牴鎹疄闄呮儏鍐佃繘琛屾洿鏂板拰淇锛屼慨璁㈠悗鐨勫崗璁皢閫氳繃鏈钩鍙板叕鍛婄瓑鏂瑰紡浜堜互鍏ず銆備慨璁㈠悗鐨勫崗璁嚜鍏ず涔嬫棩璧风敓鏁堬紝濡傛偍鍦ㄥ崗璁慨璁㈠悗缁х画浣跨敤鏈湇鍔★紝鍒欒涓烘偍宸叉帴鍙椾慨璁㈠悗鐨勫崗璁��
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ 涓�銆佸畾涔�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 灏忔櫤鍚庣鏈嶅姟锛氭寚灏忔櫤鍚庣鏈嶅姟鍙婂叾閮ㄧ讲杩愯鐨勫悗绔湇鍔$郴缁燂紝鍖呮嫭浣嗕笉闄愪簬鏅烘帶鍙帮紙绠$悊鍚庡彴锛夈�丄PI鎺ュ彛銆乄ebSocket閫氫俊鏈嶅姟绛夈��
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 鏅烘帶鍙帮細鎸囨湰鏈嶅姟鎻愪緵鐨刉eb绠$悊鐣岄潰锛岀敤浜庤澶囩鐞嗐�佹櫤鑳戒綋閰嶇疆銆佺敤鎴风鐞嗙瓑鍔熻兘銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 鐢ㄦ埛锛氭寚閫氳繃娉ㄥ唽鎴栧叾浠栨柟寮忎娇鐢ㄦ湰鏈嶅姟鐨勮嚜鐒朵汉銆佹硶浜烘垨鍏朵粬缁勭粐锛屼互涓嬬畝绉�"鎮�"銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 鏅鸿兘浣擄細鎸囧湪鏈湇鍔′腑鍒涘缓鍜岄厤缃殑AI瑙掕壊锛屽寘鍚瑷�妯″瀷銆佽闊宠瘑鍒�佽闊冲悎鎴愩�佽瑙夌悊瑙c�佹剰鍥捐瘑鍒�佸璇濊蹇嗐�佺煡璇嗗簱绛夎兘鍔涚殑闆嗗悎銆�
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 璁惧锛氭寚閫氳繃鏈湇鍔¤繘琛岃繛鎺ュ拰绠$悊鐨凟SP32绛夌‖浠惰澶囥��
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ 浜屻�佸崗璁寖鍥�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 鏈崗璁槸鎮ㄤ笌杩愯惀鑰呬箣闂村叧浜庝娇鐢ㄦ湰鏈嶅姟鐨勬硶寰嬪崗璁�傛湰鍗忚閫傜敤浜庢偍閫氳繃鏅烘帶鍙版垨鍏朵粬鏂瑰紡浣跨敤鏈湇鍔$殑鍏ㄩ儴琛屼负銆�
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 鏈湇鍔$殑婧愪唬鐮侀伒寰浉搴旂殑寮�婧愯鍙瘉銆傛湰鍗忚绾︽潫鎮ㄥ鏈湇鍔$殑浣跨敤琛屼负锛屼笉褰卞搷寮�婧愯鍙瘉鏈韩璧嬩簣鎮ㄧ殑鏉冨埄銆�
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ 涓夈�佽处鍙锋敞鍐屼笌浣跨敤
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 3.1 鐢ㄦ埛璧勬牸锛氭偍纭锛屽湪浣跨敤鏈湇鍔″墠锛屾偍搴斿綋鍏峰涓崕浜烘皯鍏卞拰鍥芥硶寰嬭瀹氱殑涓庢偍琛屼负鐩搁�傚簲鐨勬皯浜嬭涓鸿兘鍔涖�傝嫢鎮ㄤ负鏈垚骞翠汉锛屽簲鍦ㄦ硶瀹氱洃鎶や汉鐨勯櫔鍚屽拰鎸囧涓嬩娇鐢ㄦ湰鏈嶅姟銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 3.2 璐﹀彿娉ㄥ唽锛氭偍鍙�氳繃鎵嬫満鍙烽獙璇佺爜鎴栫敤鎴峰悕瀵嗙爜绛夋柟寮忔敞鍐屽拰鐧诲綍鏈湇鍔°�傛偍搴斿綋鎻愪緵鐪熷疄銆佸噯纭�佸畬鏁寸殑娉ㄥ唽淇℃伅锛屽苟鍙婃椂鏇存柊銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 3.3 璐﹀彿瀹夊叏锛氭偍搴斿Ε鍠勪繚绠℃偍鐨勮处鍙峰拰瀵嗙爜锛屽洜鎮ㄧ殑鍘熷洜瀵艰嚧璐﹀彿娉勯湶鎴栬浠栦汉鍐掔敤鎵�浜х敓鐨勪竴鍒囧悗鏋滐紝鐢辨偍鑷鎵挎媴銆傚鍙戠幇璐﹀彿瀛樺湪瀹夊叏闅愭偅锛岃绔嬪嵆鑱旂郴杩愯惀鑰呫��
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 3.4 璐﹀彿浣跨敤闄愬埗锛氭偍鐨勮处鍙蜂粎闄愭偍鏈汉浣跨敤锛屾湭缁忚繍钀ヨ�呭悓鎰忥紝涓嶅緱浠ヤ换浣曟柟寮忓皢璐﹀彿杞銆佸嚭绉熸垨鍊熺粰绗笁鏂逛娇鐢ㄣ��
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 3.5 璐﹀彿娉ㄩ攢锛氬鎮ㄩ渶瑕佹敞閿�璐﹀彿锛屽彲閫氳繃鏅烘帶鍙扮殑璐﹀彿璁剧疆鍔熻兘杩涜鎿嶄綔锛屾垨鑱旂郴杩愯惀鑰呰繘琛屽鐞嗐�傝处鍙锋敞閿�鍚庯紝鐩稿叧鏁版嵁灏嗚鍒犻櫎涓旀棤娉曟仮澶嶏紝璇疯皑鎱庢搷浣溿��
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ 鍥涖�佹湇鍔″唴瀹�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.1 鏈嶅姟姒傝堪锛氭湰鏈嶅姟渚濇墭浜哄伐鏅鸿兘鎶�鏈紝閫氳繃API鎺ュ彛璋冪敤绗笁鏂圭敓鎴愬紡浜哄伐鏅鸿兘妯″瀷锛屼负杩炴帴鐨勭‖浠惰澶囨彁渚涙櫤鑳戒氦浜掓湇鍔★紝鍖呮嫭浣嗕笉闄愪簬璇煶娲诲姩妫�娴嬶紙VAD锛夈�佽闊宠瘑鍒紙ASR锛夈�佽涔夌悊瑙o紙LLM锛夈�佽闊冲悎鎴愶紙TTS锛夈�佽瑙夌悊瑙o紙VLLM锛夈�佹剰鍥捐瘑鍒紙Intent锛夈�佸璇濊蹇嗭紙Memory锛夈�佺煡璇嗗簱锛圧AG锛夌瓑銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.2 鏅鸿兘浣撶鐞嗭細鎮ㄥ彲閫氳繃鏅烘帶鍙板垱寤哄拰閰嶇疆鏅鸿兘浣擄紝鍖呮嫭璁惧畾瑙掕壊妯℃澘銆侀�夋嫨璇█妯″瀷銆侀厤缃闊冲弬鏁般�佽缃剰鍥捐瘑鍒鍒欍�佺鐞嗙煡璇嗗簱銆佸紑鍚璇濊蹇嗐�侀厤缃櫤鑳戒綋鎻掍欢绛夈�傛櫤鑳戒綋鐨勮瀹氬唴瀹逛笉寰楄繚鍙嶅浗瀹舵硶寰嬫硶瑙勩��
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.3 璁惧绠$悊锛氭偍鍙�氳繃鏅烘帶鍙扮粦瀹氬拰绠$悊鎮ㄧ殑纭欢璁惧锛岃繘琛岃澶囬厤缃�佸浐浠舵洿鏂般�丱TA杩滅▼鍗囩骇绛夋搷浣溿�傛偍搴旂‘淇濇墍绠$悊鐨勮澶囦负鎮ㄥ悎娉曟嫢鏈夋垨宸茶幏寰楁巿鏉冦��
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.4 瑙嗚鐞嗚В锛氭湰鏈嶅姟鏀寔閫氳繃璁惧鎽勫儚澶撮噰闆嗗浘鍍忥紝璋冪敤绗笁鏂硅瑙夋ā鍨嬭繘琛屽浘鍍忚瘑鍒拰鍦烘櫙鐞嗚В銆傛偍搴旂‘淇濋噰闆嗗浘鍍忕殑琛屼负绗﹀悎鐩稿叧娉曞緥娉曡锛屼笉寰椾镜鐘粬浜洪殣绉併��
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.5 鎰忓浘璇嗗埆锛氭湰鏈嶅姟鍙�氳繃鎰忓浘璇嗗埆鍔熻兘鐞嗚В鐢ㄦ埛鎸囦护鐨勬剰鍥撅紝骞舵嵁姝よЕ鍙戠浉搴旂殑鎿嶄綔鎴栨湇鍔★紝濡傛帶鍒舵櫤鑳借澶囥�佹煡璇俊鎭瓑銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.6 瀵硅瘽璁板繂锛氭湰鏈嶅姟鍙褰曞拰瀛樺偍鎮ㄤ笌鏅鸿兘浣撶殑浜や簰鍘嗗彶鎽樿锛屼互渚垮湪鍚庣画瀵硅瘽涓彁渚涙洿杩炶疮銆佷釜鎬у寲鐨勪氦浜掍綋楠屻�傛偍鍙湪鏅烘帶鍙扮鐞嗘垨娓呴櫎璁板繂鏁版嵁銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.7 鐭ヨ瘑搴擄細鏈湇鍔℃敮鎸佹偍涓婁紶鏂囨。銆佹枃鏈瓑鍐呭鏋勫缓鐭ヨ瘑搴擄紝鏅鸿兘浣撳彲鍦ㄥ璇濅腑妫�绱㈢煡璇嗗簱鍐呭涓烘偍鎻愪緵闂瓟鏈嶅姟銆傛偍搴旂‘淇濅笂浼犵殑鐭ヨ瘑搴撳唴瀹逛笉渚电姱绗笁鏂圭煡璇嗕骇鏉冿紝涓斾笉鍖呭惈杩濆弽娉曞緥娉曡鐨勫唴瀹广��
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.8 澹扮汗璇嗗埆锛氭湰鏈嶅姟鏀寔澹扮汗璇嗗埆鍔熻兘锛屾偍鍙敞鍐屽0绾规牱鏈互瀹炵幇璇磋瘽浜鸿韩浠介獙璇佸拰涓�у寲鏈嶅姟銆傚0绾规暟鎹皢鐢ㄤ簬韬唤楠岃瘉鐩殑銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.9 璇煶鍏嬮殕锛氭湰鏈嶅姟鏀寔璇煶鍏嬮殕鍔熻兘锛屾偍鍙�氳繃鎻愪緵闊抽鏍锋湰鍏嬮殕鑷畾涔夐煶鑹层�傚厠闅嗛煶鑹蹭粎渚涙偍鏈汉浣跨敤锛屼笉寰楃敤浜庝豢鍐掍粬浜烘垨浠庝簨杩濇硶杩濊娲诲姩銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.10 MCP鎺ュ叆鐐癸細鏈湇鍔℃敮鎸丮CP锛圡odel Context Protocol锛夊崗璁紝鍙綔涓篗CP Server鍚戝閮ㄦ彁渚涘伐鍏疯皟鐢ㄨ兘鍔涳紝涔熷彲浣滀负MCP Client璋冪敤澶栭儴MCP鏈嶅姟锛屽疄鐜颁笌澶栭儴绯荤粺鐨勬爣鍑嗗寲闆嗘垚銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.11 涓婁笅鏂囨彁渚涜�咃細鏈湇鍔℃敮鎸佷笂涓嬫枃鎻愪緵鑰咃紙Context Provider锛夊姛鑳斤紝鍙粠澶栭儴鏁版嵁婧愯幏鍙栧疄鏃朵俊鎭紝濡傚ぉ姘斻�佹柊闂汇�佽偂绁ㄧ瓑锛屼负鏅鸿兘浣撴彁渚涙洿涓板瘜鐨勭煡璇嗘潵婧愩��
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.12 MQTT鍗忚锛氭湰鏈嶅姟鏀寔MQTT锛圡essage Queuing Telemetry Transport锛夊崗璁紝鐢ㄤ簬涓庣墿鑱旂綉璁惧涔嬮棿鐨勬秷鎭�氫俊銆傛偍鍙�氳繃MQTT鍗忚瀹炵幇璁惧鎺у埗鎸囦护涓嬪彂銆佽澶囩姸鎬佺洃鎺х瓑鍔熻兘銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.13 鏈嶅姟璇存槑锛氭湰鏈嶅姟鎵�渚濊禆鐨勭涓夋柟AI妯″瀷鐢熸垚鐨勫璇濄�佺瓟澶嶅唴瀹逛粎渚涘弬鑰冿紝涓嶆瀯鎴愪换浣曚笓涓氬缓璁�傛偍涓嶅緱灏嗚緭鍑哄唴瀹逛綔涓哄尰鐤椼�佹硶寰嬨�侀噾铻嶇瓑棰嗗煙鐨勪笓涓氬缓璁�傛偍鏍规嵁杈撳嚭鍐呭鎵�浣滅殑浠讳綍鍒ゆ柇鎴栧喅绛栵紝鐢辨偍鑷鎵挎媴鍏ㄩ儴璐d换銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.14 鏈嶅姟璐圭敤锛氭湰鏈嶅姟涓哄厤璐瑰紑婧愰」鐩紝涓嶅悜鐢ㄦ埛鏀跺彇浠讳綍浣跨敤璐圭敤锛屼粎鎻愪緵璋冪敤绗笁鏂规湇鍔$殑骞冲彴鑳藉姏锛屼笉娑夊強浠讳綍浠樿垂鍔熻兘鎴栧鍊兼湇鍔°��
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 鏈湇鍔℃敮鎸佸绉岮I鏈嶅姟鎻愪緵鍟嗭紝涓嶅悓鎻愪緵鍟嗙殑鏀惰垂妯″紡濡備笅锛�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[60rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�1锛夊厤璐规湇鍔★細閮ㄥ垎鏈嶅姟鍟嗘彁渚涘厤璐归搴︽垨瀹屽叏鍏嶈垂鐨勬湇鍔★紝濡傛櫤璋盇I锛坓lm-4-flash绛夊厤璐规ā鍨嬶級銆佸井杞疎dgeTTS璇煶鍚堟垚绛夈��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[60rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�2锛夋寜閲忎粯璐规湇鍔★細閮ㄥ垎鏈嶅姟鍟嗘寜API璋冪敤閲忚璐癸紝濡侽penAI銆侀樋閲屼簯鏅鸿兘璇煶銆佺櫨搴︽枃蹇冦�佽椋炵瓑銆傛偍闇�瑕佽嚜琛屽悜绗笁鏂规湇鍔″晢鏀粯鐩稿簲璐圭敤锛岃绛夎垂鐢ㄤ笌鏈湇鍔℃棤鍏炽��
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[60rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�3锛夋湰鍦伴儴缃叉柟妗堬細鏈湇鍔℃敮鎸佸畬鍏ㄦ湰鍦伴儴缃茬殑AI鏂规锛屽FunASR璇煶璇嗗埆銆丗ishSpeech璇煶鍚堟垚銆丱llama鏈湴澶фā鍨嬬瓑銆傛湰鍦伴儴缃叉棤闇�缃戠粶璐圭敤鍜孉PI璋冪敤璐圭敤锛屼絾闇�瑕佹湰鍦板叿澶囪冻澶熺殑璁$畻璧勬簮銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 4.15 鏈嶅姟鍙樻洿锛氳繍钀ヨ�呬繚鐣欐牴鎹疄闄呮儏鍐靛鏈嶅姟鍐呭杩涜璋冩暣銆佸崌绾ф垨缁堟鐨勬潈鍒╋紝骞跺皢灏藉彲鑳芥彁鍓嶄簣浠ラ�氱煡銆�
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ 浜斻�佺敤鎴疯涓鸿鑼�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 5.1 鍚堟硶浣跨敤锛氭偍鍦ㄤ娇鐢ㄦ湰鏈嶅姟鏃讹紝搴旈伒瀹堜腑鍗庝汉姘戝叡鍜屽浗鐨勬硶寰嬫硶瑙勫強鐩稿叧鍥介檯鏉$害锛屼笉寰楀埄鐢ㄦ湰鏈嶅姟浠庝簨浠讳綍杩濇硶杩濊娲诲姩銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 5.2 绂佹琛屼负锛氭偍鎵胯鍦ㄤ娇鐢ㄦ湰鏈嶅姟鏃朵笉寰楀疄鏂戒互涓嬭涓猴細
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�1锛夊彂甯冦�佷紶杈撱�佸瓨鍌ㄥ嵄瀹冲浗瀹跺畨鍏ㄣ�佺ぞ浼氱ǔ瀹氱殑鍐呭锛屽寘鎷絾涓嶉檺浜庢秹鍙婇瑕嗗浗瀹舵斂鏉冦�佹崯瀹冲浗瀹惰崳瑾夊拰鍒╃泭銆佺吔鍔ㄦ皯鏃忎粐鎭ㄧ瓑鍐呭銆�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�2锛夊彂甯冦�佷紶杈撱�佸瓨鍌ㄤ镜鐘粬浜哄悎娉曟潈鐩婄殑鍐呭锛屽寘鎷絾涓嶉檺浜庝镜鐘煡璇嗕骇鏉冦�侀殣绉佹潈銆佸悕瑾夋潈绛夈��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�3锛夊彂甯冦�佷紶杈撱�佸瓨鍌ㄦ帆绉姐�佽壊鎯呫�佹毚鍔涖�佹亹鎬栨垨鏁欏攩鐘姜鐨勫唴瀹广��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�4锛夊彂甯冭櫄鍋囦俊鎭�佹暎甯冭埃瑷�鎴栦粠浜嬫璇堣涓恒��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�5锛変互浠讳綍鏂瑰紡骞叉壈鏈湇鍔$殑姝e父杩愯锛屽寘鎷絾涓嶉檺浜庢敾鍑绘湇鍔″櫒銆佷紶鎾伓鎰忕▼搴忋�佹伓鎰忔秷鑰楃郴缁熻祫婧愮瓑銆�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�6锛夋湭缁忔巿鏉冨鏈湇鍔¤繘琛屽弽鍚戝伐绋嬨�佸弽缂栬瘧鎴栧叾浠栬瘯鍥捐幏鍙栫郴缁熸簮浠g爜鐨勮涓猴紙鏈潯涓嶉檺鍒舵偍渚濇嵁寮�婧愯鍙瘉瀵瑰紑婧愪唬鐮佺殑鍚堟硶浣跨敤锛夈��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�7锛夊埄鐢ㄦ湰鏈嶅姟杩涜鑷姩鍖栨壒閲忔搷浣滐紝鎭舵剰娑堣�桝PI璋冪敤閰嶉鎴栧叾浠栫郴缁熻祫婧愩��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�8锛夊湪鏅鸿兘浣撹瀹氫腑杈撳叆杩濆弽娉曞緥娉曡鎴栫ぞ浼氬叕搴忚壇淇楃殑鍐呭銆�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�9锛夊埄鐢ㄦ湰鏈嶅姟浼ゅ鎴栦紒鍥句激瀹虫湭鎴愬勾浜恒��
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�10锛夊叾浠栬繚鍙嶆硶寰嬫硶瑙勩�佹湰鍗忚绾﹀畾鎴栧彲鑳芥崯瀹宠繍钀ヨ�呭強绗笁鏂瑰悎娉曟潈鐩婄殑琛屼负銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 5.3 杩濊澶勭悊锛氬鎮ㄨ繚鍙嶄笂杩拌涓鸿鑼冿紝杩愯惀鑰呮湁鏉冭鎯呰妭涓ラ噸绋嬪害閲囧彇璀﹀憡銆侀檺鍒跺姛鑳姐�佹殏鍋滄湇鍔°�佸皝绂佽处鍙风瓑鎺柦锛屽苟淇濈暀杩界┒娉曞緥璐d换鐨勬潈鍒┿��
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ 鍏�佺煡璇嗕骇鏉�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 6.1 寮�婧愯鍙細鏈湇鍔′负寮�婧愰」鐩紝鍏舵簮浠g爜閬靛惊鐩稿簲鐨勫紑婧愯鍙瘉銆傛偍鍙互鍦ㄩ伒瀹堣璁稿彲璇佹潯娆剧殑鍓嶆彁涓嬩娇鐢ㄧ浉鍏虫簮浠g爜銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 6.2 鏈嶅姟鍐呭鏉冨睘锛氶櫎寮�婧愪唬鐮佸锛屾湰鏈嶅姟涓殑鐣岄潰璁捐銆佸浘鏍囥�佹枃妗堝強鍏朵粬杩愯惀鑰呯嫭绔嬪垱浣滅殑鍐呭锛屽叾鐭ヨ瘑浜ф潈褰掕繍钀ヨ�呮墍鏈夈��
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 6.3 鐢ㄦ埛鍐呭锛氭偍閫氳繃鏈湇鍔¤緭鍏ャ�佷笂浼犳垨鐢熸垚鐨勫唴瀹癸紝鍏剁煡璇嗕骇鏉冨綊灞炰緷鐓х浉鍏虫硶寰嬫硶瑙勭‘瀹氥�傛偍鎺堜簣杩愯惀鑰呬负缁存姢鍜屾敼杩涙湇鍔′箣鐩殑锛屽鎮ㄨ緭鍏ュ唴瀹硅繘琛屽瓨鍌ㄥ拰蹇呰澶勭悊鐨勬潈鍒┿��
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 6.4 绗笁鏂规潈鐩婏細鎮ㄥ湪浣跨敤鏈湇鍔℃椂锛屽簲灏婇噸绗笁鏂圭殑鐭ヨ瘑浜ф潈鍙婂叾浠栧悎娉曟潈鐩娿�傚洜鎮ㄤ镜鐘涓夋柟鏉冪泭鑰屽紩鍙戠殑绾犵悍锛岀敱鎮ㄨ嚜琛屾壙鎷呭叏閮ㄨ矗浠汇��
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ 涓冦�侀殣绉佷繚鎶�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 7.1 淇℃伅鏀堕泦锛氫负鎻愪緵鏈湇鍔★紝杩愯惀鑰呭彲鑳戒細鏀堕泦鎮ㄧ殑娉ㄥ唽淇℃伅锛堝鎵嬫満鍙枫�佺敤鎴峰悕锛夈�佽澶囦俊鎭�佷娇鐢ㄦ棩蹇楃瓑銆傚叿浣撶殑涓汉淇℃伅鏀堕泦鍜屼娇鐢ㄨ鍒欒鍙傞槄銆婇殣绉佹斂绛栥�嬨��
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 7.2 淇℃伅淇濇姢锛氳繍钀ヨ�呭皢閲囧彇鍚堢悊鐨勬妧鏈拰绠$悊鎺柦淇濇姢鎮ㄧ殑涓汉淇℃伅瀹夊叏銆備絾鐢变簬浜掕仈缃戠殑寮�鏀炬�э紝杩愯惀鑰呬笉鑳界粷瀵逛繚璇佷俊鎭畨鍏紝璇锋偍娉ㄦ剰淇濇姢涓汉鏁忔劅淇℃伅銆�
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 7.3 淇℃伅瀹夊叏鎻愮ず锛氳鍕垮湪浣跨敤鏈湇鍔¤繃绋嬩腑鍚慉I閫忛湶鎮ㄧ殑璐骇璐︽埛銆侀摱琛屽崱銆佸瘑鐮佺瓑鏁忔劅涓汉淇℃伅锛屽惁鍒欑敱姝ゅ甫鏉ョ殑鎹熷け鐢辨偍鑷鎵挎媴銆�
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ 鍏�佸厤璐e0鏄�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 8.1 AI鐢熸垚鍐呭锛氭湰鏈嶅姟渚濊禆绗笁鏂笰I妯″瀷鎻愪緵鏅鸿兘浜や簰鑳藉姏锛孉I鐢熸垚鐨勫唴瀹瑰彲鑳藉瓨鍦ㄤ笉鍑嗙‘銆佷笉瀹屾暣鎴栦笉鎭板綋涔嬪銆傝繍钀ヨ�呬笉瀵笰I鐢熸垚鍐呭鐨勭湡瀹炴�с�佸噯纭�с�佸畬鏁存�т綔浠讳綍淇濊瘉銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 8.2 鏈嶅姟涓柇锛氬洜浠ヤ笅鍘熷洜瀵艰嚧鏈嶅姟涓柇鎴栧紓甯革紝杩愯惀鑰呬笉鎵挎媴璐d换锛�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�1锛変笉鍙姉鍔涘洜绱狅紝濡傝嚜鐒剁伨瀹炽�佺柅鎯呫�佹垬浜夌瓑銆�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�2锛夌數鍔涗緵搴旀晠闅溿�侀�氫俊缃戠粶鏁呴殰绛夊叕鍏辨湇鍔″洜绱犮��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�3锛夌涓夋柟AI妯″瀷鏈嶅姟鍟嗙殑鏈嶅姟鏁呴殰鎴栨斂绛栬皟鏁淬��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�4锛夌郴缁熺淮鎶ゃ�佸崌绾у鑷寸殑涓存椂鎬ф湇鍔′腑鏂��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�5锛夐粦瀹㈡敾鍑汇�佽绠楁満鐥呮瘨绛夌綉缁滃畨鍏ㄤ簨浠躲��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�6锛夋硶寰嬫硶瑙勬垨鏀垮簻绠″埗瀵艰嚧鐨勬湇鍔¤皟鏁淬��
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�7锛夊叾浠栭潪杩愯惀鑰呰繃閿欏鑷寸殑鎯呭舰銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 8.3 绗笁鏂规湇鍔★細鏈湇鍔″彲鑳芥秹鍙婄涓夋柟鎻愪緵鐨勮瑷�妯″瀷銆佽闊宠瘑鍒�佽闊冲悎鎴愩�佽瑙夋ā鍨嬨�佹剰鍥捐瘑鍒瓑鏈嶅姟銆傛偍鍦ㄤ娇鐢ㄦ椂搴斿悓鏃堕伒瀹堢涓夋柟鐨勬湇鍔℃潯娆俱�傚洜绗笁鏂规湇鍔″紩鍙戠殑闂锛岃鐩存帴涓庣涓夋柟鑱旂郴銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 8.4 寮�婧愬0鏄庯細鏈湇鍔′负寮�婧愰」鐩紝鎸�"鐜扮姸"鎻愪緵銆傚湪娉曞緥鍏佽鐨勬渶澶ц寖鍥村唴锛岃繍钀ヨ�呬笉灏辨湰鏈嶅姟鐨勯�傞攢鎬с�佺壒瀹氱敤閫旈�傜敤鎬ф垨涓嶄镜鏉冩�т綔浠讳綍鏄庣ず鎴栨殫绀虹殑淇濊瘉銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 8.5 寮�婧愯蒋浠剁壒鍒0鏄庯細鏈」鐩负寮�婧愯蒋浠讹紝鏈湇鍔′笌瀵规帴鐨勪换浣曠涓夋柟API鏈嶅姟鍟嗭紙鍖呮嫭浣嗕笉闄愪簬璇煶璇嗗埆銆佸ぇ妯″瀷銆佽闊冲悎鎴愮瓑骞冲彴锛夊潎涓嶅瓨鍦ㄥ晢涓氬悎浣滃叧绯伙紝涓嶄负鍏舵湇鍔¤川閲忓強璧勯噾瀹夊叏鎻愪緵浠讳綍褰㈠紡鐨勬媴淇濄�傛湰杞欢涓嶆墭绠′换浣曡处鎴峰瘑閽ャ�佷笉鍙備笌璧勯噾娴佽浆銆佷笉鎵挎媴鍏呭�艰祫閲戞崯澶遍闄┿��
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 8.6 瀹夊叏璀﹀憡锛氭湰椤圭洰鍔熻兘鏈畬鍠勶紝涓旀湭閫氳繃缃戠粶瀹夊叏娴嬭瘎锛岃鍕垮湪鐢熶骇鐜涓娇鐢ㄣ�傚鏋滄偍鍦ㄥ叕缃戠幆澧冧腑閮ㄧ讲瀛︿範鏈」鐩紝璇峰姟蹇呭仛濂藉繀瑕佺殑闃叉姢鎺柦锛屽寘鎷絾涓嶉檺浜庤缃己瀵嗙爜銆侀檺鍒惰闂潈闄愩�佸惎鐢℉TTPS鍔犲瘑浼犺緭绛夈��
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ 涔濄�佹湭鎴愬勾浜轰繚鎶�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 9.1 鑻ユ偍鏄湭婊�18鍛ㄥ瞾鐨勬湭鎴愬勾浜猴紝搴斿湪鐩戞姢浜虹殑鎸囧鍜屽悓鎰忎笅浣跨敤鏈湇鍔°��
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 9.2 鐩戞姢浜哄簲鍔犲己瀵规湭鎴愬勾浜轰娇鐢ㄦ湰鏈嶅姟鐨勭洃鐫o紝寮曞鏈垚骞翠汉鍚堢悊浣跨敤锛岄伩鍏嶈繃搴︿緷璧栥��
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 9.3 鏈垚骞翠汉鍦ㄤ娇鐢ㄦ湰鏈嶅姟鏃跺簲娉ㄦ剰涓汉淇℃伅淇濇姢锛岄伩鍏嶄笂浼犳垨閫忛湶涓汉鏁忔劅淇℃伅銆�
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ 鍗併�佸崗璁粓姝�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 10.1 鐢ㄦ埛缁堟锛氭偍鏈夋潈闅忔椂鍋滄浣跨敤鏈湇鍔″苟娉ㄩ攢璐﹀彿銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 10.2 杩愯惀鑰呯粓姝細鍑虹幇浠ヤ笅鎯呭舰鏃讹紝杩愯惀鑰呮湁鏉冪粓姝㈠悜鎮ㄦ彁渚涙湇鍔★細
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�1锛夋偍杩濆弽鏈崗璁殑绾﹀畾銆�
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�2锛夋偍鍒╃敤鏈湇鍔′粠浜嬭繚娉曡繚瑙勬椿鍔ㄣ��
+ </text>
+ </view>
+ <view class="mb-[16rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�3锛夋牴鎹硶寰嬫硶瑙勬垨鏀跨瓥瑕佹眰銆�
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[40rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 锛�4锛夎繍钀ヨ�呭喅瀹氬仠姝㈣繍钀ユ湰鏈嶅姟銆�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 10.3 缁堟鍚庡鐞嗭細鍗忚缁堟鍚庯紝杩愯惀鑰呮棤涔夊姟淇濈暀鎮ㄧ殑璐﹀彿淇℃伅鍙婄浉鍏虫暟鎹�傝繍钀ヨ�呬粛鏈夋潈渚濇嵁鏈崗璁拷绌舵偍鍦ㄥ崗璁湁鏁堟湡鍐呯殑杩濈害璐d换銆�
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ 鍗佷竴銆佹硶寰嬮�傜敤涓庝簤璁В鍐�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 11.1 鏈崗璁殑璁㈢珛銆佺敓鏁堛�佽В閲娿�佷慨璁€�佺粓姝㈠強浜夎瑙e喅鍧囬�傜敤涓崕浜烘皯鍏卞拰鍥芥硶寰嬨��
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 11.2 鍥犳湰鍗忚鎴栨湰鏈嶅姟寮曞彂鐨勪簤璁紝鍙屾柟搴斿弸濂藉崗鍟嗚В鍐筹紱鍗忓晢涓嶆垚鐨勶紝浠讳綍涓�鏂规湁鏉冨悜杩愯惀鑰呮墍鍦ㄥ湴鏈夌杈栨潈鐨勪汉姘戞硶闄㈡彁璧疯瘔璁笺��
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 11.3 鏈崗璁殑浠讳綍鏉℃琚瀹氫负鏃犳晥鎴栦笉鍙墽琛岋紝涓嶅奖鍝嶅叾浣欐潯娆剧殑鏁堝姏銆�
+ </text>
+ </view>
+
+ <view class="mb-[32rpx]">
+ <text class="text-[28rpx] text-[#333333] font-semibold">
+ 鍗佷簩銆佸叾浠�
+ </text>
+ </view>
+ <view class="mb-[24rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 12.1 鏈崗璁瀯鎴愭偍涓庤繍钀ヨ�呬箣闂村叧浜庝娇鐢ㄦ湰鏈嶅姟鐨勫畬鏁村崗璁��
+ </text>
+ </view>
+ <view class="mb-[40rpx] ml-[20rpx]">
+ <text class="text-[28rpx] text-[#333333] leading-[1.8]">
+ 12.2 杩愯惀鑰呮湭琛屼娇鎴栧欢杩熻浣挎湰鍗忚椤逛笅鐨勪换浣曟潈鍒╋紝涓嶆瀯鎴愬璇ユ潈鍒╃殑鏀惧純銆�
+ </text>
+ </view>
+ </view>
+ </scroll-view>
+ </view>
+</template>
+
+<style lang="scss" scoped>
+</style>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/register/index.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/register/index.vue
new file mode 100644
index 0000000..0484b13
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/register/index.vue
@@ -0,0 +1,903 @@
+<route lang="jsonc" type="page">
+{
+ "layout": "default",
+ "style": {
+ "navigationStyle": "custom",
+ "navigationBarTitleText": "娉ㄥ唽"
+ }
+}
+</route>
+
+<script lang="ts" setup>
+import { computed, onMounted, ref } from 'vue'
+import { register, sendSmsCode } from '@/api/auth'
+// 瀵煎叆鍥介檯鍖栫浉鍏冲姛鑳�
+import { initI18n, t } from '@/i18n'
+import { useConfigStore } from '@/store'
+// 瀵煎叆SM2鍔犲瘑宸ュ叿
+import { getEnvBaseUrl, sm2Encrypt } from '@/utils'
+import { toast } from '@/utils/toast'
+
+// 鑾峰彇灞忓箷杈圭晫鍒板畨鍏ㄥ尯鍩熻窛绂�
+let safeAreaInsets
+let systemInfo
+
+// #ifdef MP-WEIXIN
+// 寰俊灏忕▼搴忎娇鐢ㄦ柊鐨凙PI
+systemInfo = uni.getWindowInfo()
+safeAreaInsets = systemInfo.safeArea
+ ? {
+ top: systemInfo.safeArea.top,
+ right: systemInfo.windowWidth - systemInfo.safeArea.right,
+ bottom: systemInfo.windowHeight - systemInfo.safeArea.bottom,
+ left: systemInfo.safeArea.left,
+ }
+ : null
+// #endif
+
+// #ifndef MP-WEIXIN
+// 鍏朵粬骞冲彴缁х画浣跨敤uni API
+systemInfo = uni.getSystemInfoSync()
+safeAreaInsets = systemInfo.safeAreaInsets
+// #endif
+
+// 娉ㄥ唽琛ㄥ崟鏁版嵁
+interface RegisterData {
+ username: string
+ password: string
+ confirmPassword: string
+ captcha: string
+ captchaId: string
+ areaCode: string
+ mobile: string
+ mobileCaptcha: string
+}
+
+const formData = ref<RegisterData>({
+ username: '',
+ password: '',
+ confirmPassword: '',
+ captcha: '',
+ captchaId: '',
+ areaCode: '+86',
+ mobile: '',
+ mobileCaptcha: '',
+})
+
+// 楠岃瘉鐮佸浘鐗�
+const captchaImage = ref('')
+const loading = ref(false)
+const smsLoading = ref(false)
+const smsCountdown = ref(0)
+
+// 娉ㄥ唽鏂瑰紡锛�'username' | 'mobile'
+const registerType = ref<'username' | 'mobile'>('username')
+
+// 鑾峰彇閰嶇疆store
+const configStore = useConfigStore()
+
+// 鍖哄彿閫夋嫨鐩稿叧
+const showAreaCodeSheet = ref(false)
+const selectedAreaCode = ref('+86')
+const selectedAreaName = ref('涓浗澶ч檰')
+
+// 璁$畻灞炴�э細鏄惁鍚敤鎵嬫満鍙锋敞鍐�
+const enableMobileRegister = computed(() => {
+ return configStore.config.enableMobileRegister
+})
+
+// 璁$畻灞炴�э細鍖哄彿鍒楄〃
+const areaCodeList = computed(() => {
+ return configStore.config.mobileAreaList || [{ name: '涓浗澶ч檰', key: '+86' }]
+})
+
+// SM2鍏挜
+const sm2PublicKey = computed(() => {
+ return configStore.config.sm2PublicKey
+})
+
+// 鍒囨崲娉ㄥ唽鏂瑰紡
+function toggleRegisterType() {
+ registerType.value = registerType.value === 'username' ? 'mobile' : 'username'
+ // 娓呯┖杈撳叆妗�
+ formData.value.username = ''
+ formData.value.mobile = ''
+ formData.value.mobileCaptcha = ''
+}
+
+// 鎵撳紑鍖哄彿閫夋嫨寮圭獥
+function openAreaCodeSheet() {
+ showAreaCodeSheet.value = true
+}
+
+// 閫夋嫨鍖哄彿
+function selectAreaCode(item: { name: string, key: string }) {
+ selectedAreaCode.value = item.key
+ selectedAreaName.value = item.name
+ formData.value.areaCode = item.key
+ showAreaCodeSheet.value = false
+}
+
+// 鍏抽棴鍖哄彿閫夋嫨寮圭獥
+function closeAreaCodeSheet() {
+ showAreaCodeSheet.value = false
+}
+
+// 鐢熸垚UUID
+function generateUUID() {
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
+ const r = (Math.random() * 16) | 0
+ const v = c === 'x' ? r : (r & 0x3) | 0x8
+ return v.toString(16)
+ })
+}
+
+// 鑾峰彇楠岃瘉鐮�
+async function refreshCaptcha() {
+ const uuid = generateUUID()
+ formData.value.captchaId = uuid
+ captchaImage.value = `${getEnvBaseUrl()}/user/captcha?uuid=${uuid}&t=${Date.now()}`
+}
+
+// 鍙戦�佺煭淇¢獙璇佺爜
+async function sendSmsVerification() {
+ if (!formData.value.mobile) {
+ toast.warning(t('register.enterPhone'))
+ return
+ }
+ if (!formData.value.captcha) {
+ toast.warning(t('register.enterCode'))
+ return
+ }
+
+ // 鎵嬫満鍙锋牸寮忛獙璇�
+ const phoneRegex = /^1[3-9]\d{9}$/
+ if (!phoneRegex.test(formData.value.mobile)) {
+ toast.warning(t('register.enterPhone'))
+ return
+ }
+
+ try {
+ smsLoading.value = true
+ await sendSmsCode({
+ phone: `${selectedAreaCode.value}${formData.value.mobile}`,
+ captcha: formData.value.captcha,
+ captchaId: formData.value.captchaId,
+ })
+
+ toast.success(t('register.captchaSendSuccess'))
+
+ // 寮�濮嬪�掕鏃�
+ smsCountdown.value = 60
+ const timer = setInterval(() => {
+ smsCountdown.value--
+ if (smsCountdown.value <= 0) {
+ clearInterval(timer)
+ }
+ }, 1000)
+ }
+ catch (error: any) {
+ // 澶勭悊楠岃瘉鐮侀敊璇� - 浠巈rror.message涓В鏋愰敊璇爜
+ if (error.message.includes('璇锋眰閿欒[10067]')) {
+ toast.warning(t('login.captchaError'))
+ }
+ // 鍙戦�佸け璐ラ噸鏂拌幏鍙栧浘褰㈤獙璇佺爜
+ refreshCaptcha()
+ }
+ finally {
+ smsLoading.value = false
+ }
+}
+
+// 娉ㄥ唽
+async function handleRegister() {
+ // 琛ㄥ崟楠岃瘉
+ if (enableMobileRegister.value) {
+ // 鎵嬫満鍙锋敞鍐岄獙璇�
+ if (!formData.value.mobile) {
+ toast.warning(t('register.enterPhone'))
+ return
+ }
+ // 鎵嬫満鍙锋牸寮忛獙璇�
+ const phoneRegex = /^1[3-9]\d{9}$/
+ if (!phoneRegex.test(formData.value.mobile)) {
+ toast.warning(t('register.enterPhone'))
+ return
+ }
+ if (!formData.value.mobileCaptcha) {
+ toast.warning(t('register.enterCode'))
+ return
+ }
+ }
+ else {
+ // 鐢ㄦ埛鍚嶆敞鍐岄獙璇�
+ if (!formData.value.username) {
+ toast.warning(t('register.enterUsername'))
+ return
+ }
+ }
+
+ if (!formData.value.password) {
+ toast.warning(t('register.enterPassword'))
+ return
+ }
+
+ if (!formData.value.confirmPassword) {
+ toast.warning(t('register.confirmPassword'))
+ return
+ }
+
+ if (formData.value.password !== formData.value.confirmPassword) {
+ toast.warning(t('register.confirmPassword'))
+ return
+ }
+
+ if (!formData.value.captcha) {
+ toast.warning(t('register.enterCode'))
+ return
+ }
+
+ // 妫�鏌M2鍏挜鏄惁閰嶇疆
+ if (!sm2PublicKey.value) {
+ toast.warning(t('sm2.publicKeyNotConfigured'))
+ return
+ }
+
+ try {
+ loading.value = true
+
+ // 鍔犲瘑瀵嗙爜
+ let encryptedPassword
+ try {
+ // 鎷兼帴楠岃瘉鐮佸拰瀵嗙爜
+ const captchaAndPassword = formData.value.captcha + formData.value.password
+ encryptedPassword = sm2Encrypt(sm2PublicKey.value, captchaAndPassword)
+ }
+ catch (error) {
+ console.error('瀵嗙爜鍔犲瘑澶辫触:', error)
+ toast.warning(t('sm2.encryptionFailed'))
+ return
+ }
+
+ // 鏋勫缓娉ㄥ唽鏁版嵁
+ const registerData = {
+ username: enableMobileRegister.value ? `${selectedAreaCode.value}${formData.value.mobile}` : formData.value.username,
+ password: encryptedPassword,
+ captchaId: formData.value.captchaId,
+ areaCode: formData.value.areaCode,
+ mobile: formData.value.mobile,
+ mobileCaptcha: formData.value.mobileCaptcha,
+ }
+
+ await register(registerData)
+ toast.success(t('message.registerSuccess'))
+
+ // 璺宠浆鍒扮櫥褰曢〉
+ setTimeout(() => {
+ uni.redirectTo({
+ url: '/pages/login/index',
+ })
+ }, 1000)
+ }
+ catch (error: any) {
+ // 澶勭悊楠岃瘉鐮侀敊璇� - 浠巈rror.message涓В鏋愰敊璇爜
+ if (error.message.includes('璇锋眰閿欒[10067]')) {
+ toast.warning(t('login.captchaError'))
+ }
+ // 澶勭悊鎵嬫満鍙风爜宸叉敞鍐岄敊璇�
+ else if (error.message.includes('璇锋眰閿欒[10070]')) {
+ toast.warning(t('message.phoneRegistered'))
+ }
+ // 娉ㄥ唽澶辫触閲嶆柊鑾峰彇楠岃瘉鐮�
+ refreshCaptcha()
+ }
+ finally {
+ loading.value = false
+ }
+}
+
+// 杩斿洖鐧诲綍
+function goBack() {
+ uni.redirectTo({
+ url: '/pages/login/index',
+ })
+}
+
+// 椤甸潰鍔犺浇鏃惰幏鍙栭獙璇佺爜
+onLoad(() => {
+ refreshCaptcha()
+})
+
+// 缁勪欢鎸傝浇鏃剁‘淇濋厤缃凡鍔犺浇
+onMounted(async () => {
+ if (!configStore.config.name) {
+ try {
+ await configStore.fetchPublicConfig()
+ }
+ catch (error) {
+ console.error('鑾峰彇閰嶇疆澶辫触:', error)
+ }
+ }
+ // 鍒濆鍖栧浗闄呭寲
+ initI18n()
+})
+</script>
+
+<template>
+ <view class="app-container box-border h-screen w-full" :style="{ paddingTop: `${safeAreaInsets?.top}px` }">
+ <view class="header">
+ <view class="back-button" @click="goBack">
+ <wd-icon name="arrow-left" custom-class="back-icon" />
+ </view>
+ <view class="logo-section">
+ <wd-img :width="80" :height="80" round src="/static/logo.png" class="logo" />
+ <text class="welcome-text">
+ {{ t('register.pageTitle') }}
+ </text>
+ <text class="subtitle">
+ {{ t('register.createAccount') }}
+ </text>
+ </view>
+ </view>
+
+ <view class="form-container">
+ <view class="form">
+ <!-- 鎵嬫満鍙锋敞鍐� -->
+ <template v-if="enableMobileRegister">
+ <view class="input-group">
+ <view class="input-wrapper mobile-wrapper">
+ <view class="area-code-selector" @click="openAreaCodeSheet">
+ <text class="area-code-text">
+ {{ selectedAreaCode }}
+ </text>
+ <wd-icon name="arrow-down" custom-class="area-code-arrow" />
+ </view>
+ <view class="mobile-input-wrapper">
+ <wd-input
+ v-model="formData.mobile"
+ custom-class="styled-input"
+ no-border
+ :placeholder="t('register.enterPhone')"
+ type="number"
+ :maxlength="11"
+ />
+ </view>
+ </view>
+ </view>
+ </template>
+
+ <!-- 鐢ㄦ埛鍚嶆敞鍐� -->
+ <template v-else>
+ <view class="input-group">
+ <view class="input-wrapper">
+ <wd-input
+ v-model="formData.username"
+ custom-class="styled-input"
+ no-border
+ :placeholder="t('register.enterUsername')"
+ />
+ </view>
+ </view>
+ </template>
+
+ <view class="input-group">
+ <view class="input-wrapper">
+ <wd-input
+ v-model="formData.password"
+ custom-class="styled-input"
+ no-border
+ :placeholder="t('register.enterPassword')"
+ show-password
+ :maxlength="20"
+ />
+ </view>
+ </view>
+
+ <view class="input-group">
+ <view class="input-wrapper">
+ <wd-input
+ v-model="formData.confirmPassword"
+ custom-class="styled-input"
+ no-border
+ :placeholder="t('register.confirmPassword')"
+ show-password
+ :maxlength="20"
+ />
+ </view>
+ </view>
+
+ <view class="input-group">
+ <view class="input-wrapper captcha-wrapper">
+ <wd-input
+ v-model="formData.captcha"
+ custom-class="styled-input"
+ no-border
+ :placeholder="t('register.enterCode')"
+ :maxlength="6"
+ />
+ <view class="captcha-image" @click="refreshCaptcha">
+ <image :src="captchaImage" class="captcha-img" />
+ </view>
+ </view>
+ </view>
+
+ <!-- 鎵嬫満楠岃瘉鐮佽緭鍏ユ -->
+ <view v-if="enableMobileRegister" class="input-group">
+ <view class="input-wrapper sms-wrapper">
+ <wd-input
+ v-model="formData.mobileCaptcha"
+ custom-class="styled-input"
+ no-border
+ :placeholder="t('register.enterCode')"
+ type="number"
+ :maxlength="6"
+ />
+ <wd-button
+ :loading="smsLoading"
+ :disabled="smsCountdown > 0"
+ custom-class="sms-btn"
+ @click="sendSmsVerification"
+ >
+ {{ smsCountdown > 0 ? `${smsCountdown}s` : t('register.getCode') }}
+ </wd-button>
+ </view>
+ </view>
+
+ <view
+ class="register-btn"
+ type="primary"
+ :loading="loading"
+ @click="handleRegister"
+ >
+ {{ loading ? t('register.registering') : t('register.registerButton') }}
+ </view>
+
+ <view class="login-hint">
+ <text class="hint-text">
+ {{ t('register.haveAccount') }}
+ </text>
+ <text class="login-link" @click="goBack">
+ {{ t('register.loginNow') }}
+ </text>
+ </view>
+ </view>
+ </view>
+
+ <!-- 鍖哄彿閫夋嫨寮圭獥 -->
+ <wd-action-sheet
+ v-model="showAreaCodeSheet"
+ :title="t('register.selectCountry')"
+ :close-on-click-modal="true"
+ @close="closeAreaCodeSheet"
+ >
+ <view class="area-code-sheet">
+ <scroll-view scroll-y class="area-code-list">
+ <view
+ v-for="item in areaCodeList"
+ :key="item.key"
+ class="area-code-item"
+ :class="{ selected: selectedAreaCode === item.key }"
+ @click="selectAreaCode(item)"
+ >
+ <view class="area-info">
+ <text class="area-name">
+ {{ item.name }}
+ </text>
+ <text class="area-code">
+ {{ item.key }}
+ </text>
+ </view>
+ <wd-icon
+ v-if="selectedAreaCode === item.key"
+ name="check"
+ custom-class="check-icon"
+ />
+ </view>
+ </scroll-view>
+ <view class="sheet-footer">
+ <wd-button
+ type="primary"
+ custom-class="confirm-btn"
+ @click="closeAreaCodeSheet"
+ >
+ {{ t('register.confirm') }}
+ </wd-button>
+ </view>
+ </view>
+ </wd-action-sheet>
+ </view>
+</template>
+
+<style lang="scss" scoped>
+.app-container {
+ background: linear-gradient(145deg, #f5f8fd, #6baaff, #9ebbfc, #f5f8fd);
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ overflow: hidden;
+ min-height: 100vh;
+
+ &::before {
+ content: '';
+ position: absolute;
+ top: -50%;
+ left: -50%;
+ width: 200%;
+ height: 200%;
+ background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
+ animation: float 6s ease-in-out infinite;
+ }
+}
+
+@keyframes float {
+ 0%,
+ 100% {
+ transform: translateY(0px) rotate(0deg);
+ }
+ 50% {
+ transform: translateY(-20px) rotate(180deg);
+ }
+}
+
+.header {
+ flex: 0 0 auto;
+ min-height: 280rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 15% 0 40rpx 0;
+ position: relative;
+
+ .back-button {
+ position: absolute;
+ left: 40rpx;
+ top: 60rpx;
+ width: 60rpx;
+ height: 60rpx;
+ background: rgba(255, 255, 255, 0.2);
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: all 0.3s ease;
+
+ &:hover {
+ background: rgba(255, 255, 255, 0.3);
+ }
+
+ :deep(.back-icon) {
+ font-size: 32rpx;
+ color: #ffffff;
+ }
+ }
+
+ .logo-section {
+ text-align: center;
+
+ .logo {
+ margin-bottom: 20rpx;
+ box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.1);
+ }
+
+ .welcome-text {
+ display: block;
+ color: #ffffff;
+ font-size: 40rpx;
+ font-weight: 600;
+ margin-bottom: 12rpx;
+ text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
+ }
+
+ .subtitle {
+ display: block;
+ color: rgba(255, 255, 255, 0.8);
+ font-size: 26rpx;
+ font-weight: 400;
+ }
+ }
+}
+
+.form-container {
+ padding: 0 40rpx 40rpx 40rpx;
+ flex: 1;
+ display: flex;
+ align-items: flex-start;
+
+ .form {
+ width: 100%;
+ background: rgba(255, 255, 255, 0.95);
+ border-radius: 24rpx;
+ padding: 40rpx 30rpx 30rpx 30rpx;
+ backdrop-filter: blur(10rpx);
+ box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.1);
+ border: 1rpx solid rgba(255, 255, 255, 0.2);
+ max-height: calc(100vh - 350rpx);
+ overflow-y: auto;
+
+ .input-group {
+ margin-bottom: 24rpx;
+
+ .input-wrapper {
+ position: relative;
+ background: #f8f9fa;
+ border-radius: 16rpx;
+ padding: 20rpx 16rpx;
+ border: 2rpx solid #e9ecef;
+ transition: all 0.3s ease;
+ display: flex;
+ align-items: center;
+
+ &:focus-within {
+ border-color: #667eea;
+ background: #ffffff;
+ box-shadow: 0 0 0 6rpx rgba(102, 126, 234, 0.1);
+ }
+
+ &.captcha-wrapper {
+ .captcha-image {
+ margin-left: 20rpx;
+ width: 150rpx;
+ height: 60rpx;
+ border-radius: 8rpx;
+ overflow: hidden;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: #e9ecef;
+ border: 1rpx solid #ddd;
+
+ .captcha-img {
+ width: 100%;
+ height: 100%;
+ }
+ }
+ }
+
+ &.sms-wrapper {
+ :deep(.sms-btn) {
+ margin-left: 20rpx;
+ padding: 0 20rpx;
+ height: 60rpx;
+ border-radius: 8rpx;
+ font-size: 24rpx;
+ white-space: nowrap;
+ min-width: 140rpx;
+ }
+ }
+
+ &.mobile-wrapper {
+ padding: 0;
+ background: transparent;
+ border: none;
+ display: flex;
+ gap: 20rpx;
+
+ .area-code-selector {
+ flex: 0 0 160rpx;
+ background: #f8f9fa;
+ border-radius: 16rpx;
+ padding: 20rpx 16rpx;
+ border: 2rpx solid #e9ecef;
+ height: 45rpx;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ cursor: pointer;
+ transition: all 0.3s ease;
+
+ &:hover {
+ border-color: #667eea;
+ background: #ffffff;
+ box-shadow: 0 0 0 6rpx rgba(102, 126, 234, 0.1);
+ }
+
+ .area-code-text {
+ font-size: 28rpx;
+ color: #333333;
+ font-weight: 500;
+ }
+
+ :deep(.area-code-arrow) {
+ font-size: 24rpx;
+ color: #999999;
+ transition: transform 0.3s ease;
+ }
+ }
+
+ .mobile-input-wrapper {
+ flex: 1;
+ background: #f8f9fa;
+ border-radius: 16rpx;
+ padding: 20rpx 16rpx;
+ border: 2rpx solid #e9ecef;
+ transition: all 0.3s ease;
+
+ &:focus-within {
+ border-color: #667eea;
+ background: #ffffff;
+ box-shadow: 0 0 0 6rpx rgba(102, 126, 234, 0.1);
+ }
+ }
+ }
+
+ :deep(.styled-input) {
+ background: transparent !important;
+ border: none !important;
+ outline: none !important;
+ font-size: 32rpx;
+ color: #333333;
+ flex: 1;
+
+ &::placeholder {
+ color: #999999;
+ font-size: 28rpx;
+ }
+ }
+ }
+ }
+
+ :deep(.register-btn) {
+ width: 100%;
+ height: 80rpx;
+ border: none;
+ border-radius: 16rpx;
+ font-size: 30rpx;
+ font-weight: 600;
+ color: #ffffff;
+ margin-bottom: 30rpx;
+ box-shadow: 0 8rpx 24rpx rgba(102, 126, 234, 0.3);
+ transition: all 0.3s ease;
+ background-color: var(--wot-button-primary-bg-color, var(--wot-color-theme, #4d80f0));
+ text-align: center;
+ line-height: 80rpx;
+ &:active {
+ transform: translateY(2rpx);
+ box-shadow: 0 4rpx 12rpx rgba(102, 126, 234, 0.3);
+ }
+
+ &:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ }
+ }
+
+ .login-hint {
+ text-align: center;
+ margin-bottom: 30rpx;
+
+ .hint-text {
+ color: #666666;
+ font-size: 26rpx;
+ margin-right: 8rpx;
+ }
+
+ .login-link {
+ color: #667eea;
+ font-size: 26rpx;
+ font-weight: 500;
+ cursor: pointer;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+
+ .register-type-switch {
+ margin-top: 20rpx;
+ text-align: center;
+
+ .switch-tabs {
+ display: flex;
+ justify-content: center;
+ gap: 60rpx;
+ margin-bottom: 20rpx;
+
+ .switch-tab {
+ width: 60rpx;
+ height: 60rpx;
+ border-radius: 50%;
+ background: #f0f0f0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ border: 2rpx solid transparent;
+
+ &.active {
+ background: #667eea;
+ color: #ffffff;
+ border-color: #667eea;
+ box-shadow: 0 4rpx 12rpx rgba(102, 126, 234, 0.3);
+ }
+
+ :deep(.wd-icon) {
+ font-size: 24rpx;
+ }
+ }
+ }
+
+ .switch-hint {
+ font-size: 24rpx;
+ color: #666666;
+ }
+ }
+ }
+}
+
+// 鍖哄彿閫夋嫨寮圭獥鏍峰紡
+.area-code-sheet {
+ background: #ffffff;
+ border-radius: 24rpx 24rpx 0 0;
+ overflow: hidden;
+
+ .area-code-list {
+ max-height: 60vh;
+ padding: 0 40rpx;
+
+ .area-code-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 32rpx 0;
+ border-bottom: 1rpx solid #f8f9fa;
+ cursor: pointer;
+ transition: background-color 0.3s ease;
+
+ &:hover {
+ background-color: #f8f9fa;
+ }
+
+ &.selected {
+ background-color: rgba(102, 126, 234, 0.05);
+
+ .area-name {
+ color: #667eea;
+ font-weight: 500;
+ }
+
+ .area-code {
+ color: #667eea;
+ }
+ }
+
+ .area-info {
+ display: flex;
+ flex-direction: column;
+ gap: 8rpx;
+
+ .area-name {
+ font-size: 32rpx;
+ color: #333333;
+ }
+
+ .area-code {
+ font-size: 28rpx;
+ color: #666666;
+ }
+ }
+
+ :deep(.check-icon) {
+ font-size: 32rpx;
+ color: #667eea;
+ }
+ }
+ }
+
+ .sheet-footer {
+ padding: 30rpx 40rpx 40rpx 40rpx;
+ border-top: 1rpx solid #f0f0f0;
+
+ :deep(.confirm-btn) {
+ width: 100%;
+ height: 88rpx;
+ border-radius: 16rpx;
+ font-size: 32rpx;
+ font-weight: 600;
+ }
+ }
+}
+</style>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/settings/index.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/settings/index.vue
new file mode 100644
index 0000000..9e7a451
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/settings/index.vue
@@ -0,0 +1,515 @@
+<route lang="jsonc" type="page">
+{
+ "layout": "default",
+ "style": {
+ "navigationBarTitleText": "璁剧疆",
+ "navigationStyle": "custom"
+ }
+}
+</route>
+
+<script lang="ts" setup>
+import type { Language } from '@/store/lang'
+import { computed, onMounted, reactive, ref } from 'vue'
+import { useToast } from 'wot-design-uni/components/wd-toast'
+import { changeLanguage, getCurrentLanguage, getSupportedLanguages, t } from '@/i18n'
+import { useConfigStore } from '@/store'
+import { clearServerBaseUrlOverride, getEnvBaseUrl, getServerBaseUrlOverride, setServerBaseUrlOverride } from '@/utils'
+import { isMp } from '@/utils/platform'
+
+defineOptions({
+ name: 'SettingsPage',
+})
+
+const toast = useToast()
+
+// 缂撳瓨淇℃伅
+const cacheInfo = reactive({
+ storageSize: '0MB',
+ imageCache: '0MB',
+ dataCache: '0MB',
+})
+
+const configStore = useConfigStore()
+
+// 鏈嶅姟绔湴鍧�璁剧疆
+const baseUrlInput = ref('')
+const urlError = ref('')
+
+// 绯荤粺淇℃伅锛堜繚鐣欙級
+const systemInfo = computed(() => {
+ const info = uni.getSystemInfoSync()
+ return `${info.platform} ${info.system}`
+})
+
+// 璇诲彇鏈湴瑕嗙洊鍦板潃
+function loadServerBaseUrl() {
+ const override = getServerBaseUrlOverride()
+ baseUrlInput.value = override || getEnvBaseUrl()
+}
+
+// 鑾峰彇缂撳瓨淇℃伅
+function getCacheInfo() {
+ try {
+ const info = uni.getStorageInfoSync()
+ const totalSize = (info.currentSize || 0) / 1024 // KB to MB
+ cacheInfo.storageSize = `${totalSize.toFixed(2)}MB`
+ }
+ catch (error) {
+ console.error('鑾峰彇缂撳瓨淇℃伅澶辫触:', error)
+ }
+}
+
+// 楠岃瘉URL鏍煎紡
+function validateUrl() {
+ urlError.value = ''
+
+ if (!baseUrlInput.value) {
+ return
+ }
+
+ if (!/^https?:\/\/.+\/xiaozhi$/.test(baseUrlInput.value)) {
+ urlError.value = t('settings.validServerUrl')
+ }
+}
+
+// 娴嬭瘯鏈嶅姟绔湴鍧�
+async function testServerBaseUrl() {
+ // 鍏堟竻闄ら敊璇俊鎭�
+ urlError.value = ''
+
+ if (!baseUrlInput.value || !/^https?:\/\/.+\/xiaozhi$/.test(baseUrlInput.value)) {
+ return false
+ }
+
+ try {
+ const response = await uni.request({
+ url: `${baseUrlInput.value}/api/ping`,
+ method: 'GET',
+ timeout: 3000,
+ })
+
+ if (response.statusCode === 200) {
+ return true
+ }
+ else {
+ toast.error({
+ msg: t('message.invalidAddress'),
+ duration: 3000,
+ })
+ return false
+ }
+ }
+ catch (error) {
+ console.error('娴嬭瘯鏈嶅姟绔湴鍧�澶辫触:', error)
+ toast.error({
+ msg: t('message.invalidAddress'),
+ duration: 3000,
+ })
+ return false
+ }
+}
+
+// 淇濆瓨鏈嶅姟绔湴鍧�
+async function saveServerBaseUrl() {
+ if (!baseUrlInput.value || !/^https?:\/\/.+\/xiaozhi$/.test(baseUrlInput.value)) {
+ toast.warning(t('settings.validServerUrl'))
+ return
+ }
+
+ // 娴嬭瘯鍦板潃鏈夋晥鎬�
+ const isServerValid = await testServerBaseUrl()
+ if (!isServerValid) {
+ return
+ }
+ setServerBaseUrlOverride(baseUrlInput.value)
+ // 澶勭悊config缂撳瓨鏃犳硶鏇存柊鐨勯棶棰�
+ uni.request({
+ url: `${getEnvBaseUrl()}/user/pub-config`,
+ method: 'GET',
+ success: (res: any) => {
+ if (res.statusCode === 200) {
+ configStore.setConfig(res.data.data)
+ uni.setStorageSync('config', res.data.data)
+ }
+ },
+ fail: (err) => {
+ console.error('鑾峰彇SM2鍏挜澶辫触:', err)
+ },
+ })
+
+ // 鍒囨崲璇锋眰鍦板潃鍚庢竻绌烘墍鏈夌紦瀛�
+ clearAllCacheAfterUrlChange()
+
+ uni.showModal({
+ title: t('settings.restartApp'),
+ content: t('settings.serverUrlSavedAndCacheCleared'),
+ confirmText: t('settings.restartNow'),
+ cancelText: t('settings.restartLater'),
+ success: (res) => {
+ if (res.confirm) {
+ restartApp()
+ }
+ else {
+ toast.success(t('settings.restartSuccess'))
+ }
+ },
+ })
+}
+
+// 璇█鍒囨崲
+const supportedLanguages = getSupportedLanguages()
+const currentLanguage = ref<Language>(getCurrentLanguage())
+const showLanguageSheet = ref(false)
+
+function handleLanguageChange(lang: Language) {
+ changeLanguage(lang)
+ showLanguageSheet.value = false
+ currentLanguage.value = lang
+ toast.success(t('settings.languageChanged'))
+}
+
+// 閲嶇疆涓� env 榛樿
+function resetServerBaseUrl() {
+ clearServerBaseUrlOverride()
+ baseUrlInput.value = getEnvBaseUrl()
+
+ // 鍒囨崲璇锋眰鍦板潃鍚庢竻绌烘墍鏈夌紦瀛�
+ clearAllCacheAfterUrlChange()
+
+ uni.showModal({
+ title: t('settings.restartApp'),
+ content: t('settings.resetToDefaultAndCacheCleared'),
+ confirmText: t('settings.restartNow'),
+ cancelText: t('settings.restartLater'),
+ success: (res) => {
+ if (res.confirm) {
+ restartApp()
+ }
+ else {
+ toast.success(t('settings.resetSuccess'))
+ }
+ },
+ })
+}
+
+// 閲嶅惎搴旂敤锛圓pp 鍘熺敓閲嶅惎锛涘叾浠栫鍥炲埌棣栭〉锛�
+function restartApp() {
+ // #ifdef APP-PLUS
+ plus.runtime.restart()
+ // #endif
+ // #ifndef APP-PLUS
+ uni.reLaunch({ url: '/pages/index/index' })
+ // #endif
+}
+
+// 鍒囨崲鍦板潃鍚庤嚜鍔ㄦ竻绌烘墍鏈夌紦瀛�
+function clearAllCacheAfterUrlChange() {
+ try {
+ // 澶囦唤杩愯鏃惰鐩栧湴鍧�锛岀‘淇濇竻鐞嗗悗鎭㈠
+ const preservedOverride = getServerBaseUrlOverride()
+
+ // 瀹屽叏娓呯┖鎵�鏈夌紦瀛橈紝鍖呮嫭token
+ uni.clearStorageSync()
+
+ // 娓呯┖localStorage锛圚5鐜锛�
+ // #ifdef H5
+ if (typeof localStorage !== 'undefined') {
+ localStorage.clear()
+ }
+ // #endif
+
+ // 鎭㈠杩愯鏃惰鐩栧湴鍧�锛堝鏈夛級锛岄渶瑕佸湪娓呯悊瀹屾垚鍚庡啀鍐欏叆
+ if (preservedOverride) {
+ setServerBaseUrlOverride(preservedOverride)
+ }
+
+ // 閲嶆柊鑾峰彇缂撳瓨淇℃伅
+ getCacheInfo()
+ }
+ catch (error) {
+ console.error('娓呴櫎缂撳瓨澶辫触:', error)
+ }
+}
+
+// 娓呴櫎缂撳瓨
+async function clearCache() {
+ try {
+ uni.showModal({
+ title: t('settings.confirmClear'),
+ content: t('settings.confirmClearMessage'),
+ confirmText: t('common.confirm'),
+ cancelText: t('common.cancel'),
+ success: (res) => {
+ if (res.confirm) {
+ clearAllCacheAfterUrlChange()
+ toast.success(t('settings.cacheCleared'))
+
+ // 寤惰繜璺宠浆鍒扮櫥褰曢〉
+ setTimeout(() => {
+ uni.reLaunch({ url: '/pages/login/index' })
+ }, 1500)
+ }
+ },
+ })
+ }
+ catch (error) {
+ console.error('娓呴櫎缂撳瓨澶辫触:', error)
+ toast.error(t('settings.clearCacheFailed'))
+ }
+}
+
+// 鍏充簬鎴戜滑
+function showAbout() {
+ uni.showModal({
+ title: t('settings.aboutApp', { appName: import.meta.env.VITE_APP_TITLE }),
+ content: t('settings.aboutContent', {
+ appName: import.meta.env.VITE_APP_TITLE,
+ version: '0.9.6',
+ }),
+ showCancel: false,
+ confirmText: t('common.confirm'),
+ })
+}
+
+onMounted(async () => {
+ // 浠呭湪闈炲皬绋嬪簭鐜鍔犺浇鏈嶅姟绔湴鍧�璁剧疆
+ if (!isMp) {
+ loadServerBaseUrl()
+ }
+ getCacheInfo()
+
+ // 鍔ㄦ�佽缃鑸爮鏍囬涓哄浗闄呭寲鏂囨湰
+ uni.setNavigationBarTitle({
+ title: t('settings.title'),
+ })
+})
+</script>
+
+<template>
+ <view class="min-h-screen bg-[#f5f7fb]">
+ <wd-navbar :title="t('settings.title')" placeholder safe-area-inset-top fixed />
+
+ <view class="p-[24rpx]">
+ <!-- 缃戠粶璁剧疆 - 浠呭湪闈炲皬绋嬪簭鐜鏄剧ず -->
+ <view v-if="!isMp" class="mb-[32rpx]">
+ <view class="mb-[24rpx] flex items-center">
+ <text class="text-[32rpx] text-[#232338] font-bold">
+ {{ t('settings.networkSettings') }}
+ </text>
+ </view>
+
+ <view
+ class="overflow-hidden border border-[#eeeeee] rounded-[24rpx] bg-[#fbfbfb] p-[32rpx]"
+ style="box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);"
+ >
+ <view class="mb-[24rpx]">
+ <text class="text-[28rpx] text-[#232338] font-semibold">
+ {{ t('settings.serverApiUrl') }}
+ </text>
+ <text class="mt-[8rpx] block text-[24rpx] text-[#9d9ea3]">
+ {{ t('settings.modifyWillClearCache') }}
+ </text>
+ </view>
+
+ <view class="mb-[24rpx]">
+ <view class="w-full overflow-hidden border border-[#eeeeee] rounded-[16rpx] bg-[#f5f7fb]">
+ <wd-input
+ v-model="baseUrlInput" type="text" clearable :maxlength="200"
+ :placeholder="t('settings.enterServerUrl')"
+ custom-class="!border-none !bg-transparent h-[64rpx] px-[24rpx] items-center"
+ input-class="text-[28rpx] text-[#232338]" @input="validateUrl" @blur="validateUrl"
+ />
+ </view>
+ <text v-if="urlError" class="mt-[8rpx] block text-[24rpx] text-[#ff4d4f]">
+ {{ urlError }}
+ </text>
+ </view>
+
+ <view class="flex gap-[16rpx]">
+ <wd-button
+ type="primary"
+ custom-class="flex-1 h-[88rpx] rounded-[20rpx] text-[28rpx] font-semibold bg-[#336cff] border-none shadow-[0_4rpx_16rpx_rgba(51,108,255,0.3)] active:shadow-[0_2rpx_8rpx_rgba(51,108,255,0.4)] active:scale-98"
+ @click="saveServerBaseUrl"
+ >
+ {{ t('settings.saveSettings') }}
+ </wd-button>
+ <wd-button
+ type="default"
+ custom-class="flex-1 h-[88rpx] rounded-[20rpx] text-[28rpx] font-semibold bg-white border-[#eeeeee] text-[#65686f] active:bg-[#f5f7fb]"
+ @click="resetServerBaseUrl"
+ >
+ {{ t('settings.resetDefault') }}
+ </wd-button>
+ </view>
+ </view>
+ </view>
+
+ <!-- 缂撳瓨绠$悊 -->
+ <view class="mb-[32rpx]">
+ <view class="mb-[24rpx] flex items-center">
+ <text class="text-[32rpx] text-[#232338] font-bold">
+ {{ t('settings.cacheManagement') }}
+ </text>
+ </view>
+
+ <view
+ class="border border-[#eeeeee] rounded-[24rpx] bg-[#fbfbfb] p-[32rpx]"
+ style="box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);"
+ >
+ <view class="space-y-[16rpx]">
+ <!-- 缂撳瓨淇℃伅灞曠ず锛屽弬鑰冩彃浠舵牱寮� -->
+ <view
+ class="flex items-center justify-between border border-[#eeeeee] rounded-[16rpx] bg-[#f5f7fb] p-[24rpx] transition-all active:bg-[#eef3ff]"
+ >
+ <view>
+ <text class="text-[28rpx] text-[#232338] font-medium">
+ {{ t('settings.totalCacheSize') }}
+ </text>
+ <text class="mt-[4rpx] block text-[24rpx] text-[#9d9ea3]">
+ {{ t('settings.appDataSize') }}
+ </text>
+ </view>
+ <text class="text-[28rpx] text-[#65686f] font-semibold">
+ {{ cacheInfo.storageSize }}
+ </text>
+ </view>
+
+ <!-- 娓呴櫎缂撳瓨鎸夐挳锛屽弬鑰冩彃浠剁紪杈戞寜閽牱寮� -->
+ <view
+ class="flex items-center justify-between border border-[#eeeeee] rounded-[16rpx] bg-[#f5f7fb] p-[24rpx]"
+ >
+ <view>
+ <text class="text-[28rpx] text-[#232338] font-medium">
+ {{ t('settings.cacheClear') }}
+ </text>
+ <text class="mt-[4rpx] block text-[24rpx] text-[#9d9ea3]">
+ {{ t('settings.clearAllCache') }}
+ </text>
+ </view>
+ <view
+ class="cursor-pointer rounded-[24rpx] bg-[rgba(255,107,107,0.1)] px-[28rpx] py-[16rpx] text-[24rpx] text-[#ff6b6b] font-semibold transition-all duration-300 active:scale-95 active:bg-[#ff6b6b] active:text-white"
+ @click="clearCache"
+ >
+ {{ t('settings.clearCache') }}
+ </view>
+ </view>
+ </view>
+ </view>
+ </view>
+
+ <!-- 搴旂敤淇℃伅 -->
+ <view class="mb-[32rpx]">
+ <view class="mb-[24rpx] flex items-center">
+ <text class="text-[32rpx] text-[#232338] font-bold">
+ {{ t('settings.appInfo') }}
+ </text>
+ </view>
+
+ <view
+ class="border border-[#eeeeee] rounded-[24rpx] bg-[#fbfbfb] p-[32rpx]"
+ style="box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);"
+ >
+ <view
+ class="flex cursor-pointer items-center justify-between border border-[#eeeeee] rounded-[16rpx] bg-[#f5f7fb] p-[24rpx] transition-all active:bg-[#eef3ff]"
+ @click="showAbout"
+ >
+ <view>
+ <text class="text-[28rpx] text-[#232338] font-medium">
+ {{ t('settings.aboutUs') }}
+ </text>
+ <text class="mt-[4rpx] block text-[24rpx] text-[#9d9ea3]">
+ {{ t('settings.appVersion') }}
+ </text>
+ </view>
+ <wd-icon name="arrow-right" custom-class="text-[32rpx] text-[#9d9ea3]" />
+ </view>
+ </view>
+ </view>
+
+ <!-- 璇█璁剧疆 -->
+ <view class="mb-[32rpx]">
+ <view class="mb-[24rpx] flex items-center">
+ <text class="text-[32rpx] text-[#232338] font-bold">
+ {{ t('settings.languageSettings') }}
+ </text>
+ </view>
+
+ <view
+ class="border border-[#eeeeee] rounded-[24rpx] bg-[#fbfbfb] p-[32rpx]"
+ style="box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);"
+ >
+ <view
+ class="flex cursor-pointer items-center justify-between border border-[#eeeeee] rounded-[16rpx] bg-[#f5f7fb] p-[24rpx] transition-all active:bg-[#eef3ff]"
+ @click="showLanguageSheet = true"
+ >
+ <view>
+ <text class="text-[32rpx] text-[#232338] font-medium">
+ {{ t('settings.language') }}
+ </text>
+ <text class="mt-[4rpx] block text-[24rpx] text-[#9d9ea3]">
+ {{ t('settings.selectLanguage') }}
+ </text>
+ </view>
+ <view class="flex items-center">
+ <text class="mr-[16rpx] text-[32rpx] text-[#9d9ea3] font-semibold">
+ {{ supportedLanguages.find(lang => lang.code === currentLanguage)?.name }}
+ </text>
+ <wd-icon name="arrow-right" custom-class="text-[32rpx] text-[#9d9ea3]" />
+ </view>
+ </view>
+ </view>
+ </view>
+
+ <!-- 璇█閫夋嫨寮圭獥 -->
+ <wd-action-sheet v-model="showLanguageSheet" :title="t('settings.selectLanguage')" :close-on-click-modal="true">
+ <view class="language-sheet">
+ <scroll-view scroll-y class="language-list">
+ <view
+ v-for="lang in supportedLanguages" :key="lang.code" class="language-item"
+ @click="handleLanguageChange(lang.code)"
+ >
+ <text class="language-name">
+ {{ lang.name }}
+ </text>
+ </view>
+ </scroll-view>
+ </view>
+ </wd-action-sheet>
+
+ <!-- 搴曢儴瀹夊叏璺濈 -->
+ <!-- 搴曢儴瀹夊叏璺濈 -->
+ <view style="height: env(safe-area-inset-bottom);" />
+ </view>
+ </view>
+</template>
+
+<style lang="scss" scoped>
+// 淇濇寔涓� edit.vue 涓�鑷寸殑椋庢牸锛屾牱寮忎富瑕侀�氳繃绫诲悕鎺у埗
+
+// 璇█閫夋嫨寮圭獥鏍峰紡
+.language-sheet {
+ .language-list {
+ max-height: 50vh;
+
+ .language-item {
+ padding: 30rpx 0;
+ text-align: center;
+ border-bottom: 1rpx solid #f0f0f0;
+
+ .language-name {
+ font-size: 28rpx;
+ color: #333;
+ }
+
+ &:last-child {
+ border-bottom: none;
+ }
+
+ &:active {
+ background-color: #f5f7fb;
+ }
+ }
+ }
+}
+</style>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/pages/voiceprint/index.vue b/xiaozhi-esp32-server/main/manager-mobile/src/pages/voiceprint/index.vue
new file mode 100644
index 0000000..8a11052
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/pages/voiceprint/index.vue
@@ -0,0 +1,657 @@
+<script lang="ts" setup>
+import type { ChatHistory, CreateSpeakerData, VoicePrint } from '@/api/voiceprint'
+import { computed, onMounted, ref } from 'vue'
+import { useMessage } from 'wot-design-uni/components/wd-message-box'
+import { useToast } from 'wot-design-uni/components/wd-toast'
+import { createVoicePrint, deleteVoicePrint, getAudioDownloadId, getChatHistory, getVoicePrintList, updateVoicePrint } from '@/api/voiceprint'
+import { t } from '@/i18n'
+import { getEnvBaseUrl } from '@/utils'
+
+defineOptions({
+ name: 'VoicePrintManage',
+})
+
+const props = withDefaults(defineProps<Props>(), {
+ agentId: 'default',
+})
+
+const emits = defineEmits(['update-refresher-enabled'])
+
+// 鎺ユ敹props
+interface Props {
+ agentId?: string
+}
+
+// 鑾峰彇灞忓箷杈圭晫鍒板畨鍏ㄥ尯鍩熻窛绂�
+let safeAreaInsets: any
+let systemInfo: any
+
+// #ifdef MP-WEIXIN
+systemInfo = uni.getWindowInfo()
+safeAreaInsets = systemInfo.safeArea
+ ? {
+ top: systemInfo.safeArea.top,
+ right: systemInfo.windowWidth - systemInfo.safeArea.right,
+ bottom: systemInfo.windowHeight - systemInfo.safeArea.bottom,
+ left: systemInfo.safeArea.left,
+ }
+ : null
+// #endif
+
+// #ifndef MP-WEIXIN
+systemInfo = uni.getSystemInfoSync()
+safeAreaInsets = systemInfo.safeAreaInsets
+// #endif
+
+const message = useMessage()
+const toast = useToast()
+
+// 椤甸潰鏁版嵁
+const voicePrintList = ref<VoicePrint[]>([])
+const chatHistoryList = ref<ChatHistory[]>([])
+const chatHistoryActions = ref<any[]>([])
+const swipeStates = ref<Record<string, 'left' | 'close' | 'right'>>({})
+const loading = ref(false)
+
+// 闊抽鎾斁鐩稿叧
+const audioRef = ref<UniApp.InnerAudioContext | null>(null)
+const playingAudioId = ref<string>('')
+
+// 浣跨敤浼犲叆鐨勬櫤鑳戒綋ID
+const currentAgentId = computed(() => {
+ return props.agentId
+})
+
+// 鏅鸿兘浣撻�夋嫨鐩稿叧鍔熻兘宸茬Щ闄�
+
+// 寮圭獥鐩稿叧
+const showAddDialog = ref(false)
+const showEditDialog = ref(false)
+const showChatHistoryDialog = ref(false)
+const addForm = ref<CreateSpeakerData>({
+ agentId: '',
+ audioId: '',
+ sourceName: '',
+ introduce: '',
+})
+const editForm = ref<VoicePrint>({
+ id: '',
+ audioId: '',
+ sourceName: '',
+ introduce: '',
+ createDate: '',
+})
+
+// 鑾峰彇澹扮汗鍒楄〃
+async function loadVoicePrintList() {
+ try {
+ console.log('鑾峰彇澹扮汗鍒楄〃')
+
+ // 妫�鏌ユ槸鍚︽湁褰撳墠閫変腑鐨勬櫤鑳戒綋
+ if (!currentAgentId.value) {
+ console.warn(t('voiceprint.noSelectedAgent'))
+ voicePrintList.value = []
+ return
+ }
+
+ loading.value = true
+ const data = await getVoicePrintList(currentAgentId.value)
+
+ // 鍒濆鍖栨粦鍔ㄧ姸鎬�
+ const list = data || []
+ list.forEach((item) => {
+ if (!swipeStates.value[item.id]) {
+ swipeStates.value[item.id] = 'close'
+ }
+ })
+
+ voicePrintList.value = list
+ }
+ catch (error) {
+ console.error('鑾峰彇澹扮汗鍒楄〃澶辫触:', error)
+ voicePrintList.value = []
+ }
+ finally {
+ loading.value = false
+ }
+}
+
+// 鏆撮湶缁欑埗缁勪欢鐨勫埛鏂版柟娉�
+async function refresh() {
+ await loadVoicePrintList()
+}
+
+// 鑾峰彇璇煶瀵硅瘽璁板綍
+async function loadChatHistory() {
+ try {
+ if (!currentAgentId.value) {
+ toast.error(t('voiceprint.pleaseSelectAgent'))
+ return
+ }
+
+ const data = await getChatHistory(currentAgentId.value)
+ chatHistoryList.value = data || []
+ // 杞崲涓篈ctionSheet鏍煎紡
+ chatHistoryActions.value = chatHistoryList.value.map((item, index) => ({
+ name: item.content,
+ audioId: item.audioId,
+ index,
+ }))
+ }
+ catch (error) {
+ console.error('鑾峰彇瀵硅瘽璁板綍澶辫触:', error)
+ toast.error(t('voiceprint.fetchHistoryFailed'))
+ }
+}
+
+// 鎵撳紑娣诲姞寮圭獥
+function openAddDialog() {
+ if (!currentAgentId.value) {
+ toast.error(t('voiceprint.pleaseSelectAgent'))
+ return
+ }
+
+ // 妫�鏌ュ0绾规帴鍙f槸鍚﹂厤缃紙閫氳繃灏濊瘯鑾峰彇澹扮汗鍒楄〃鏉ユ娴嬶級
+ const checkVoicePrintConfig = async () => {
+ try {
+ await getVoicePrintList(currentAgentId.value)
+ // 鎺ュ彛姝e父锛岀户缁墦寮�娣诲姞寮圭獥
+ addForm.value = {
+ agentId: currentAgentId.value,
+ audioId: '',
+ sourceName: '',
+ introduce: '',
+ }
+ showAddDialog.value = true
+ }
+ catch (error: any) {
+ // 鎹曟崏澹扮汗鎺ュ彛鏈厤缃敊璇�
+ if (error.message && error.message.includes('璇锋眰閿欒[10054]')) {
+ toast.error(t('voiceprint.voiceprintInterfaceNotConfigured'))
+ }
+ else {
+ // 鍏朵粬閿欒锛岀户缁墦寮�寮圭獥
+ addForm.value = {
+ agentId: currentAgentId.value,
+ audioId: '',
+ sourceName: '',
+ introduce: '',
+ }
+ showAddDialog.value = true
+ }
+ }
+ }
+
+ checkVoicePrintConfig()
+}
+
+// 鎵撳紑缂栬緫寮圭獥
+function openEditDialog(item: VoicePrint) {
+ editForm.value = { ...item }
+ showEditDialog.value = true
+}
+
+// 鑾峰彇閫変腑闊抽鐨勬樉绀哄唴瀹�
+function getSelectedAudioContent(audioId: string) {
+ if (!audioId)
+ return t('voiceprint.clickToSelectVector')
+ const chatItem = chatHistoryList.value.find(item => item.audioId === audioId)
+ return chatItem ? chatItem.content : `宸查�夋嫨: ${audioId.substring(0, 8)}...`
+}
+
+// 閫夋嫨澹扮汗鍚戦噺
+function selectAudioId({ item }: { item: any }) {
+ if (showAddDialog.value) {
+ addForm.value.audioId = item.audioId
+ }
+ else if (showEditDialog.value) {
+ editForm.value.audioId = item.audioId
+ }
+ showChatHistoryDialog.value = false
+}
+
+// 鐐瑰嚮閫夋嫨
+function handleItemClick(item: any) {
+ selectAudioId({ item })
+}
+
+// 鎻愪氦娣诲姞璇磋瘽浜�
+async function submitAdd() {
+ if (!addForm.value.sourceName.trim()) {
+ toast.error(t('voiceprint.pleaseInputName'))
+ return
+ }
+ if (!addForm.value.audioId) {
+ toast.error(t('voiceprint.pleaseSelectVector'))
+ return
+ }
+
+ try {
+ await createVoicePrint(addForm.value)
+ toast.success(t('voiceprint.addSuccess'))
+ showAddDialog.value = false
+ await loadVoicePrintList()
+ }
+ catch (error) {
+ console.error('娣诲姞璇磋瘽浜哄け璐�:', error)
+ toast.error(t('voiceprint.addFailed'))
+ }
+}
+
+// 鎻愪氦缂栬緫璇磋瘽浜�
+async function submitEdit() {
+ if (!editForm.value.sourceName.trim()) {
+ toast.error(t('voiceprint.pleaseInputName'))
+ return
+ }
+ if (!editForm.value.audioId) {
+ toast.error(t('voiceprint.pleaseSelectVector'))
+ return
+ }
+
+ try {
+ await updateVoicePrint({
+ id: editForm.value.id,
+ audioId: editForm.value.audioId,
+ sourceName: editForm.value.sourceName,
+ introduce: editForm.value.introduce,
+ createDate: editForm.value.createDate,
+ })
+ toast.success(t('voiceprint.editSuccess'))
+ showEditDialog.value = false
+ await loadVoicePrintList()
+ }
+ catch (error) {
+ console.error('缂栬緫璇磋瘽浜哄け璐�:', error)
+ toast.error(t('voiceprint.editFailed'))
+ }
+}
+
+// 澶勭悊缂栬緫鎿嶄綔
+function handleEdit(item: VoicePrint) {
+ openEditDialog(item)
+ swipeStates.value[item.id] = 'close'
+}
+
+// 鍒犻櫎澹扮汗
+async function handleDelete(id: string) {
+ message.confirm({
+ msg: t('voiceprint.deleteConfirmMsg'),
+ title: t('voiceprint.deleteConfirmTitle'),
+ }).then(async () => {
+ await deleteVoicePrint(id)
+ toast.success(t('voiceprint.deleteSuccess'))
+ await loadVoicePrintList()
+ }).catch(() => {
+ console.log('鐐瑰嚮浜嗗彇娑堟寜閽�')
+ })
+}
+
+// 鎾斁闊抽
+async function playAudio(audioId: string, event: Event) {
+ event.stopPropagation() // 闃绘浜嬩欢鍐掓场锛岄槻姝㈠叧闂笅鎷夋
+
+ if (!audioId) {
+ toast.warning(t('voiceprint.audioNotExist'))
+ return
+ }
+
+ // 濡傛灉姝e湪鎾斁鍚屼竴涓煶棰戯紝鍒欏仠姝�
+ if (playingAudioId.value === audioId) {
+ stopAudio()
+ return
+ }
+
+ // 鍋滄涔嬪墠鐨勯煶棰�
+ stopAudio()
+
+ try {
+ // 鍏堣幏鍙栭煶棰戜笅杞絀D
+ playingAudioId.value = audioId
+ const downloadId = await getAudioDownloadId(audioId)
+
+ if (!downloadId) {
+ toast.error(t('voiceprint.getAudioFailed'))
+ playingAudioId.value = ''
+ return
+ }
+
+ // 鑾峰彇baseURL
+ const baseURL = getEnvBaseUrl()
+ const audioUrl = `${baseURL}/agent/play/${downloadId}`
+
+ // 鍒涘缓鏂扮殑闊抽瀹炰緥
+ audioRef.value = uni.createInnerAudioContext()
+ audioRef.value.src = audioUrl
+ audioRef.value.autoplay = true
+
+ // 鐩戝惉鎾斁缁撴潫
+ audioRef.value.onEnded(() => {
+ playingAudioId.value = ''
+ })
+
+ // 鐩戝惉鎾斁閿欒
+ audioRef.value.onError((error) => {
+ console.error('闊抽鎾斁閿欒:', error)
+ toast.error(t('voiceprint.audioPlayFailed'))
+ playingAudioId.value = ''
+ })
+ }
+ catch (error) {
+ console.error('鎾斁闊抽澶辫触:', error)
+ toast.error(t('voiceprint.audioPlayFailed'))
+ playingAudioId.value = ''
+ }
+}
+
+// 鍋滄闊抽
+function stopAudio() {
+ if (audioRef.value) {
+ audioRef.value.stop()
+ audioRef.value.destroy()
+ audioRef.value = null
+ }
+ playingAudioId.value = ''
+}
+
+watch(() => [showAddDialog.value, showEditDialog.value], (newValues) => {
+ if (newValues.some((value: boolean) => value)) {
+ emits('update-refresher-enabled', false)
+ }
+ else {
+ emits('update-refresher-enabled', true)
+ }
+})
+
+onMounted(async () => {
+ // 鏅鸿兘浣撳凡绠�鍖栦负榛樿
+
+ loadVoicePrintList()
+ loadChatHistory()
+})
+
+// 鏆撮湶鏂规硶缁欑埗缁勪欢
+defineExpose({
+ showAddDialog,
+ showEditDialog,
+ refresh,
+})
+</script>
+
+<template>
+ <view class="voiceprint-container" style="background: #f5f7fb; min-height: 100%;">
+ <!-- 鍔犺浇鐘舵�� -->
+ <view v-if="loading && voicePrintList.length === 0" class="loading-container">
+ <wd-loading color="#336cff" />
+ <text class="loading-text">
+ {{ t('voiceprint.loading') }}
+ </text>
+ </view>
+
+ <!-- 澹扮汗鍒楄〃 -->
+ <view v-else-if="voicePrintList.length > 0" class="voiceprint-list">
+ <!-- 澹扮汗鍗$墖鍒楄〃 -->
+ <view class="box-border flex flex-col gap-[24rpx] p-[20rpx]">
+ <view v-for="item in voicePrintList" :key="item.id">
+ <wd-swipe-action
+ :model-value="swipeStates[item.id] || 'close'"
+ @update:model-value="swipeStates[item.id] = $event"
+ >
+ <view class="bg-[#fbfbfb] p-[32rpx]" @click="handleEdit(item)">
+ <view>
+ <text class="mb-[12rpx] block text-[32rpx] text-[#232338] font-semibold">
+ {{ item.sourceName }}
+ </text>
+ <text class="mb-[12rpx] block text-[28rpx] text-[#65686f] leading-[1.4]">
+ {{ item.introduce || '鏆傛棤鎻忚堪' }}
+ </text>
+ <text class="block text-[24rpx] text-[#9d9ea3]">
+ {{ item.createDate }}
+ </text>
+ </view>
+ </view>
+
+ <template #right>
+ <view class="h-full flex">
+ <view
+ class="h-full min-w-[120rpx] flex items-center justify-center bg-[#ff4d4f] p-x-[32rpx] text-[28rpx] text-white font-medium"
+ @click="handleDelete(item.id)"
+ >
+ <wd-icon name="delete" />
+ {{ t('voiceprint.delete') }}
+ </view>
+ </view>
+ </template>
+ </wd-swipe-action>
+ </view>
+ </view>
+ </view>
+
+ <!-- 绌虹姸鎬� -->
+ <view v-else-if="!loading" class="empty-container">
+ <view class="flex flex-col items-center justify-center p-[100rpx_40rpx] text-center">
+ <wd-icon name="voice" custom-class="text-[120rpx] text-[#d9d9d9] mb-[32rpx]" />
+ <text class="mb-[32rpx] text-[32rpx] text-[#666666] font-medium">
+ {{ t('voiceprint.emptyTitle') }}
+ </text>
+ <text class="text-[26rpx] text-[#999999] leading-[1.5]">
+ {{ t('voiceprint.emptyDesc') }}
+ </text>
+ </view>
+ </view>
+
+ <!-- 娴姩鎿嶄綔鎸夐挳 -->
+ <wd-fab custom-style="z-index:10" type="primary" size="small" :draggable="true" :expandable="false" @click="openAddDialog">
+ <wd-icon name="add" />
+ </wd-fab>
+
+ <!-- MessageBox 缁勪欢 -->
+ <wd-message-box />
+ </view>
+
+ <!-- 娣诲姞璇磋瘽浜哄脊绐� -->
+ <wd-popup
+ v-model="showAddDialog"
+ position="center"
+ custom-style="width: 90%; max-width: 400px; border-radius: 16px;"
+ safe-area-inset-bottom
+ >
+ <view>
+ <view class="p-[32rpx]">
+ <!-- 澹扮汗鍚戦噺閫夋嫨 -->
+ <view class="mb-[32rpx]">
+ <text class="mb-[16rpx] block text-[28rpx] text-[#232338] font-medium">
+ <text class="text-red">
+ *
+ </text>
+ {{ t('voiceprint.voiceVector') }}
+ </text>
+ <view
+ class="flex cursor-pointer items-center justify-between border-[1rpx] border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] transition-all duration-300 active:bg-[#eef3ff]"
+ @click="showChatHistoryDialog = true"
+ >
+ <text
+ class="m-r-[16rpx] flex-1 text-left text-[26rpx] text-[#232338]"
+ :class="{ 'text-[#9d9ea3]': !addForm.audioId }"
+ >
+ {{ getSelectedAudioContent(addForm.audioId) }}
+ </text>
+ <wd-icon name="arrow-down" custom-class="text-[20rpx] text-[#9d9ea3]" />
+ </view>
+ </view>
+
+ <!-- 濮撳悕 -->
+ <view class="mb-[32rpx]">
+ <text class="mb-[16rpx] block text-[28rpx] text-[#232338] font-medium">
+ <text class="text-red">
+ *
+ </text>
+ {{ t('voiceprint.name') }}
+ </text>
+ <input
+ v-model="addForm.sourceName"
+ class="box-border h-[80rpx] w-full border-[1rpx] border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[16rpx_20rpx] text-[28rpx] text-[#232338] leading-[1.4] outline-none focus:border-[#336cff] focus:bg-white placeholder:text-[#9d9ea3]"
+ type="text" :placeholder="t('voiceprint.pleaseInputName')"
+ >
+ </view>
+
+ <!-- 鎻忚堪 -->
+ <view>
+ <text class="mb-[16rpx] block text-[28rpx] text-[#232338] font-medium">
+ <text class="text-red">
+ *
+ </text>
+ {{ t('voiceprint.description') }}
+ </text>
+ <textarea
+ v-model="addForm.introduce" :maxlength="100" :placeholder="t('voiceprint.pleaseInputDescription')"
+ class="box-border h-[200rpx] w-full resize-none border-[1rpx] border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] text-[26rpx] text-[#232338] leading-[1.6] outline-none focus:border-[#336cff] focus:bg-white placeholder:text-[#9d9ea3]"
+ />
+ <view class="mt-[8rpx] text-right text-[22rpx] text-[#9d9ea3]">
+ {{ (addForm.introduce || '').length }}/100
+ </view>
+ </view>
+ </view>
+
+ <view class="flex gap-[16rpx] border-t-[2rpx] border-[#eeeeee] p-[24rpx_32rpx_32rpx]">
+ <wd-button type="info" custom-class="flex-1" @click="showAddDialog = false">
+ {{ t('voiceprint.cancel') }}
+ </wd-button>
+ <wd-button type="primary" custom-class="flex-1" @click="submitAdd">
+ {{ t('voiceprint.save') }}
+ </wd-button>
+ </view>
+ </view>
+ </wd-popup>
+
+ <!-- 缂栬緫璇磋瘽浜哄脊绐� -->
+ <wd-popup
+ v-model="showEditDialog" position="center" custom-style="width: 90%; max-width: 400px; border-radius: 16px;"
+ safe-area-inset-bottom
+ >
+ <view>
+ <view class="box-border w-full flex items-center justify-between border-b-[2rpx] border-[#eeeeee] p-[32rpx_32rpx_24rpx]">
+ <text class="w-full text-center text-[32rpx] text-[#232338] font-semibold">
+ {{ t('voiceprint.editSpeaker') }}
+ </text>
+ </view>
+
+ <view class="p-[32rpx]">
+ <!-- 澹扮汗鍚戦噺閫夋嫨 -->
+ <view class="mb-[32rpx]">
+ <text class="mb-[16rpx] block text-[28rpx] text-[#232338] font-medium">
+ <text class="text-red">
+ *
+ </text>
+ {{ t('voiceprint.voiceVector') }}
+ </text>
+ <view
+ class="flex cursor-pointer items-center justify-between border-[1rpx] border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] transition-all duration-300 active:bg-[#eef3ff]"
+ @click="showChatHistoryDialog = true"
+ >
+ <text
+ class="m-r-[16rpx] flex-1 text-left text-[26rpx] text-[#232338]"
+ :class="{ 'text-[#9d9ea3]': !editForm.audioId }"
+ >
+ {{ getSelectedAudioContent(editForm.audioId) }}
+ </text>
+ <wd-icon name="arrow-down" custom-class="text-[20rpx] text-[#9d9ea3]" />
+ </view>
+ </view>
+
+ <!-- 濮撳悕 -->
+ <view class="mb-[32rpx]">
+ <text class="mb-[16rpx] block text-[28rpx] text-[#232338] font-medium">
+ <text class="text-red">
+ *
+ </text>
+ {{ t('voiceprint.name') }}
+ </text>
+ <input
+ v-model="editForm.sourceName"
+ class="box-border h-[80rpx] w-full border-[1rpx] border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[16rpx_20rpx] text-[28rpx] text-[#232338] leading-[1.4] outline-none focus:border-[#336cff] focus:bg-white placeholder:text-[#9d9ea3]"
+ type="text" :placeholder="t('voiceprint.pleaseInputName')"
+ >
+ </view>
+
+ <!-- 鎻忚堪 -->
+ <view>
+ <text class="mb-[16rpx] block text-[28rpx] text-[#232338] font-medium">
+ <text class="text-red">
+ *
+ </text>
+ {{ t('voiceprint.description') }}
+ </text>
+ <textarea
+ v-model="editForm.introduce" :maxlength="100" :placeholder="t('voiceprint.pleaseInputDescription')"
+ class="box-border h-[200rpx] w-full resize-none border-[1rpx] border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] text-[26rpx] text-[#232338] leading-[1.6] outline-none focus:border-[#336cff] focus:bg-white placeholder:text-[#9d9ea3]"
+ />
+ <view class="mt-[8rpx] text-right text-[22rpx] text-[#9d9ea3]">
+ {{ (editForm.introduce || '').length }}/100
+ </view>
+ </view>
+ </view>
+
+ <view class="flex gap-[16rpx] border-t-[2rpx] border-[#eeeeee] p-[24rpx_32rpx_32rpx]">
+ <wd-button type="info" custom-class="flex-1" @click="showEditDialog = false">
+ {{ t('voiceprint.cancel') }}
+ </wd-button>
+ <wd-button type="primary" custom-class="flex-1" @click="submitEdit">
+ {{ t('voiceprint.save') }}
+ </wd-button>
+ </view>
+ </view>
+ </wd-popup>
+
+ <!-- 鑷畾涔夎闊冲璇濊褰曢�夋嫨寮瑰嚭灞� -->
+ <wd-popup v-model="showChatHistoryDialog" class="custom-popup" position="bottom" @close="stopAudio">
+ <view class="rounded-[20rpx] bg-white pb-[20rpx] pt-[20rpx]">
+ <view class="max-h-[600rpx] overflow-y-auto rounded-[20rpx]">
+ <view
+ v-for="item in chatHistoryActions"
+ :key="item.audioId"
+ class="flex items-center justify-between border-b border-[#f5f5f5] p-[32rpx] transition-all active:bg-[#f5f7fb]"
+ @click="handleItemClick(item)"
+ >
+ <text class="flex-1 text-[28rpx] text-[#232338]">
+ {{ item.name }}
+ </text>
+ <view class="ml-[20rpx]" @click.stop="playAudio(item.audioId, $event)">
+ <wd-icon
+ :name="playingAudioId === item.audioId ? 'pause-circle' : 'play-circle'"
+ size="24px"
+ :custom-class="playingAudioId === item.audioId ? 'text-[#336cff]' : 'text-[#9d9ea3]'"
+ />
+ </view>
+ </view>
+ </view>
+ </view>
+ </wd-popup>
+</template>
+
+<style lang="scss" scoped>
+.voiceprint-container {
+ position: relative;
+}
+
+.loading-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 100rpx 40rpx;
+}
+
+.loading-text {
+ margin-top: 20rpx;
+ font-size: 28rpx;
+ color: #666666;
+}
+
+::v-deep .custom-popup {
+ .wd-popup {
+ padding: 20rpx !important;
+ background: transparent;
+ }
+}
+</style>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/router/interceptor.ts b/xiaozhi-esp32-server/main/manager-mobile/src/router/interceptor.ts
new file mode 100644
index 0000000..74905a6
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/router/interceptor.ts
@@ -0,0 +1,66 @@
+/**
+ * by 鑿查附 on 2024-03-06
+ * 璺敱鎷︽埅锛岄�氬父涔熸槸鐧诲綍鎷︽埅
+ * 鍙互璁剧疆璺敱鐧藉悕鍗曪紝鎴栬�呴粦鍚嶅崟锛岀湅涓氬姟闇�瑕侀�夊摢涓�涓�
+ * 鎴戣繖閲屽簲涓哄ぇ閮ㄥ垎閮藉彲浠ラ殢渚胯繘鍏ワ紝鎵�浠ヤ娇鐢ㄩ粦鍚嶅崟
+ */
+import { useUserStore } from '@/store'
+import { needLoginPages as _needLoginPages, getLastPage, getNeedLoginPages } from '@/utils'
+
+// TODO Check
+const loginRoute = import.meta.env.VITE_LOGIN_URL
+
+function isLogined() {
+ const userStore = useUserStore()
+ return !!userStore.userInfo.username
+}
+
+const isDev = import.meta.env.DEV
+
+// 榛戝悕鍗曠櫥褰曟嫤鎴櫒 - 锛堥�傜敤浜庡ぇ閮ㄥ垎椤甸潰涓嶉渶瑕佺櫥褰曪紝灏戦儴鍒嗛〉闈㈤渶瑕佺櫥褰曪級
+const navigateToInterceptor = {
+ // 娉ㄦ剰锛岃繖閲岀殑url鏄� '/' 寮�澶寸殑锛屽 '/pages/index/index'锛岃窡 'pages.json' 閲岄潰鐨� path 涓嶅悓
+ // 澧炲姞瀵圭浉瀵硅矾寰勭殑澶勭悊锛孊Y 缃戝弸 @ideal
+ invoke({ url }: { url: string }) {
+ // console.log(url) // /pages/route-interceptor/index?name=feige&age=30
+ let path = url.split('?')[0]
+ console.log('椤甸潰鍙樺姩')
+
+ // 澶勭悊鐩稿璺緞
+ if (!path.startsWith('/')) {
+ const currentPath = getLastPage().route
+ const normalizedCurrentPath = currentPath.startsWith('/') ? currentPath : `/${currentPath}`
+ const baseDir = normalizedCurrentPath.substring(0, normalizedCurrentPath.lastIndexOf('/'))
+ path = `${baseDir}/${path}`
+ }
+
+ let needLoginPages: string[] = []
+ // 涓轰簡闃叉寮�鍙戞椂鍑虹幇BUG锛岃繖閲屾瘡娆¢兘鑾峰彇涓�涓嬨�傜敓浜х幆澧冨彲浠ョЩ鍒板嚱鏁板锛屾�ц兘鏇村ソ
+ if (isDev) {
+ needLoginPages = getNeedLoginPages()
+ }
+ else {
+ needLoginPages = _needLoginPages
+ }
+ const isNeedLogin = needLoginPages.includes(path)
+ if (!isNeedLogin) {
+ return true
+ }
+ const hasLogin = isLogined()
+ if (hasLogin) {
+ return true
+ }
+ const redirectRoute = `${loginRoute}?redirect=${encodeURIComponent(url)}`
+ uni.navigateTo({ url: redirectRoute })
+ return false
+ },
+}
+
+export const routeInterceptor = {
+ install() {
+ uni.addInterceptor('navigateTo', navigateToInterceptor)
+ uni.addInterceptor('reLaunch', navigateToInterceptor)
+ uni.addInterceptor('redirectTo', navigateToInterceptor)
+ uni.addInterceptor('switchTab', navigateToInterceptor)
+ },
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/1024x1024.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/1024x1024.png
new file mode 100644
index 0000000..2ceae9c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/1024x1024.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/120x120.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/120x120.png
new file mode 100644
index 0000000..3eb78f6
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/120x120.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/144x144.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/144x144.png
new file mode 100644
index 0000000..a103673
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/144x144.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/152x152.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/152x152.png
new file mode 100644
index 0000000..6829c93
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/152x152.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/167x167.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/167x167.png
new file mode 100644
index 0000000..5e9289b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/167x167.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/180x180.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/180x180.png
new file mode 100644
index 0000000..607fc4a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/180x180.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/192x192.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/192x192.png
new file mode 100644
index 0000000..4a91273
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/192x192.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/20x20.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/20x20.png
new file mode 100644
index 0000000..f4b0e59
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/20x20.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/29x29.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/29x29.png
new file mode 100644
index 0000000..403fda9
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/29x29.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/40x40.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/40x40.png
new file mode 100644
index 0000000..88ae47c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/40x40.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/58x58.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/58x58.png
new file mode 100644
index 0000000..889b9ff
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/58x58.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/60x60.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/60x60.png
new file mode 100644
index 0000000..6d0b41a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/60x60.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/72x72.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/72x72.png
new file mode 100644
index 0000000..6cf11e5
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/72x72.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/76x76.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/76x76.png
new file mode 100644
index 0000000..a1c11df
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/76x76.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/80x80.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/80x80.png
new file mode 100644
index 0000000..68b1891
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/80x80.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/87x87.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/87x87.png
new file mode 100644
index 0000000..6ac4e62
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/87x87.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/96x96.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/96x96.png
new file mode 100644
index 0000000..2644d32
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/app/icons/96x96.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/images/.gitkeep b/xiaozhi-esp32-server/main/manager-mobile/src/static/images/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/images/.gitkeep
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/images/default-avatar.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/images/default-avatar.png
new file mode 100644
index 0000000..4eb5879
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/images/default-avatar.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/images/home/main-top-bg.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/images/home/main-top-bg.png
new file mode 100644
index 0000000..e56ba94
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/images/home/main-top-bg.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/logo.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/logo.png
new file mode 100644
index 0000000..9e66497
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/logo.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/logo.svg b/xiaozhi-esp32-server/main/manager-mobile/src/static/logo.svg
new file mode 100644
index 0000000..eaee669
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/logo.svg
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg id="_鍥惧眰_2" data-name="鍥惧眰 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 113.39 113.39">
+ <defs>
+ <style>
+ .cls-1 {
+ fill: none;
+ }
+
+ .cls-2 {
+ fill: #d14328;
+ }
+
+ .cls-3 {
+ fill: #2c8d3a;
+ }
+ </style>
+ </defs>
+ <g id="_鍥惧眰_1-2" data-name="鍥惧眰 1">
+ <g>
+ <rect class="cls-1" width="113.39" height="113.39" />
+ <g>
+ <path class="cls-3"
+ d="M86.31,11.34H25.08c-8.14,0-14.74,6.6-14.74,14.74v61.23c0,8.14,6.6,14.74,14.74,14.74h61.23c.12,0,.24-.02,.37-.02-9.76-.2-17.64-8.18-17.64-17.99,0-.56,.03-1.12,.08-1.67H34.1c-1.57,0-2.83-1.27-2.83-2.83V32.43c0-.78,.63-1.42,1.42-1.42h9.17c.78,0,1.42,.63,1.42,1.42v36.52c0,.78,.63,1.42,1.42,1.42h22.02c.78,0,1.42-.63,1.42-1.42V32.43c0-.78,.63-1.42,1.42-1.42h9.17c.78,0,1.42,.63,1.42,1.42v34.99c2.13-.89,4.47-1.39,6.92-1.39,5.66,0,10.7,2.63,14.01,6.72V26.08c0-8.14-6.6-14.74-14.74-14.74Z" />
+ <g>
+ <path class="cls-2"
+ d="M87.04,68.03c-8.83,0-16.01,7.18-16.01,16.01s7.18,16.01,16.01,16.01,16.01-7.18,16.01-16.01-7.18-16.01-16.01-16.01Zm-.27,24.84h-7.2v-3h1.18v-10.48h4.58v2.81h1.42c.84,0,1.46-.16,1.88-.48s.62-.87,.62-1.64c0-.69-.25-1.17-.74-1.45s-1.19-.42-2.09-.42h-6.84v-3h7.2c2.38,0,4.15,.38,5.31,1.15,1.16,.77,1.74,1.93,1.74,3.48,0,1.71-.83,2.93-2.5,3.64,1.07,.4,1.87,.95,2.39,1.65s.79,1.56,.79,2.58c0,3.44-2.58,5.16-7.73,5.16Z" />
+ <path class="cls-2"
+ d="M86.49,85.17h-1.16v4.7h1.8c.81,0,1.46-.18,1.94-.55s.72-.95,.72-1.73c0-.86-.25-1.48-.74-1.85s-1.35-.56-2.56-.56Z" />
+ </g>
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/chat.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/chat.png
new file mode 100644
index 0000000..90c4338
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/chat.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/chat_activate.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/chat_activate.png
new file mode 100644
index 0000000..78d38e7
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/chat_activate.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/device.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/device.png
new file mode 100644
index 0000000..2d624f4
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/device.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/device_activate.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/device_activate.png
new file mode 100644
index 0000000..1b8f96f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/device_activate.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/example.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/example.png
new file mode 100644
index 0000000..fd1e942
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/example.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/exampleHL.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/exampleHL.png
new file mode 100644
index 0000000..7501011
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/exampleHL.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/home.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/home.png
new file mode 100644
index 0000000..8f82e21
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/home.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/homeHL.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/homeHL.png
new file mode 100644
index 0000000..26d3761
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/homeHL.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/microphone.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/microphone.png
new file mode 100644
index 0000000..bde9d13
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/microphone.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/microphone_activate.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/microphone_activate.png
new file mode 100644
index 0000000..c6b6c41
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/microphone_activate.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/network.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/network.png
new file mode 100644
index 0000000..d06283f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/network.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/network_activate.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/network_activate.png
new file mode 100644
index 0000000..753a3f1
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/network_activate.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/personal.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/personal.png
new file mode 100644
index 0000000..0a569a2
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/personal.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/personalHL.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/personalHL.png
new file mode 100644
index 0000000..8c3e66e
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/personalHL.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/robot.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/robot.png
new file mode 100644
index 0000000..ffe1db2
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/robot.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/robot_activate.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/robot_activate.png
new file mode 100644
index 0000000..aa5c305
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/robot_activate.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/system.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/system.png
new file mode 100644
index 0000000..a84c9aa
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/system.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/system_activate.png b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/system_activate.png
new file mode 100644
index 0000000..b596f3c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/static/tabbar/system_activate.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/store/config.ts b/xiaozhi-esp32-server/main/manager-mobile/src/store/config.ts
new file mode 100644
index 0000000..2be6e5f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/store/config.ts
@@ -0,0 +1,66 @@
+import type { PublicConfig } from '@/api/auth'
+import { defineStore } from 'pinia'
+import { ref } from 'vue'
+import { getPublicConfig } from '@/api/auth'
+
+// 鍒濆鍖栫姸鎬�
+const initialConfigState: PublicConfig = {
+ enableMobileRegister: false,
+ version: '',
+ year: '',
+ allowUserRegister: false,
+ mobileAreaList: [],
+ beianIcpNum: '',
+ beianGaNum: '',
+ sm2PublicKey: '',
+ name: import.meta.env.VITE_APP_TITLE,
+}
+
+export const useConfigStore = defineStore(
+ 'config',
+ () => {
+ // 瀹氫箟鍏ㄥ眬閰嶇疆
+ const config = ref<PublicConfig>({ ...initialConfigState })
+
+ // 璁剧疆閰嶇疆淇℃伅
+ const setConfig = (val: PublicConfig) => {
+ config.value = val
+ }
+
+ // 鑾峰彇鍏叡閰嶇疆
+ const fetchPublicConfig = async () => {
+ try {
+ const configData = await getPublicConfig()
+ console.log(configData)
+
+ setConfig(configData)
+ return configData
+ }
+ catch (error) {
+ console.error('鑾峰彇鍏叡閰嶇疆澶辫触:', error)
+ throw error
+ }
+ }
+
+ // 閲嶇疆閰嶇疆
+ const resetConfig = () => {
+ config.value = { ...initialConfigState }
+ }
+
+ return {
+ config,
+ setConfig,
+ fetchPublicConfig,
+ resetConfig,
+ }
+ },
+ {
+ persist: {
+ key: 'config',
+ serializer: {
+ serialize: state => JSON.stringify(state.config),
+ deserialize: value => ({ config: JSON.parse(value) }),
+ },
+ },
+ },
+)
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/store/index.ts b/xiaozhi-esp32-server/main/manager-mobile/src/store/index.ts
new file mode 100644
index 0000000..51d14e0
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/store/index.ts
@@ -0,0 +1,21 @@
+import { createPinia } from 'pinia'
+import { createPersistedState } from 'pinia-plugin-persistedstate' // 鏁版嵁鎸佷箙鍖�
+
+const store = createPinia()
+store.use(
+ createPersistedState({
+ storage: {
+ getItem: uni.getStorageSync,
+ setItem: uni.setStorageSync,
+ },
+ }),
+)
+
+export default store
+
+export * from './config'
+export * from './plugin'
+export * from './provider'
+export * from './speedPitch'
+// 妯″潡缁熶竴瀵煎嚭
+export * from './user'
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/store/lang.ts b/xiaozhi-esp32-server/main/manager-mobile/src/store/lang.ts
new file mode 100644
index 0000000..9828d82
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/store/lang.ts
@@ -0,0 +1,41 @@
+import type { Ref } from 'vue'
+import { defineStore } from 'pinia'
+import { ref } from 'vue'
+
+// 鏀寔鐨勮瑷�绫诲瀷
+export type Language = 'zh_CN' | 'en' | 'zh_TW' | 'de' | 'vi' | 'pt_BR'
+
+export interface LangStore {
+ currentLang: Ref<Language>
+ changeLang: (lang: Language) => void
+}
+
+export const useLangStore = defineStore(
+ 'lang',
+ (): LangStore => {
+ // 浠庢湰鍦板瓨鍌ㄨ幏鍙栬瑷�璁剧疆锛屽鏋滄病鏈夊垯浣跨敤榛樿鍊�
+ const savedLang = uni.getStorageSync('app_language') as Language | null
+ const currentLang = ref<Language>(savedLang || 'zh_CN')
+
+ // 鍒囨崲璇█
+ const changeLang = (lang: Language) => {
+ currentLang.value = lang
+ // 灏嗚瑷�璁剧疆淇濆瓨鍒版湰鍦板瓨鍌�
+ uni.setStorageSync('app_language', lang)
+ }
+
+ return {
+ currentLang,
+ changeLang,
+ }
+ },
+ {
+ persist: {
+ key: 'lang',
+ serializer: {
+ serialize: state => JSON.stringify(state.currentLang),
+ deserialize: value => ({ currentLang: JSON.parse(value) }),
+ },
+ },
+ },
+)
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/store/plugin.ts b/xiaozhi-esp32-server/main/manager-mobile/src/store/plugin.ts
new file mode 100644
index 0000000..200b085
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/store/plugin.ts
@@ -0,0 +1,58 @@
+import type { AgentFunction, PluginDefinition } from '@/api/agent/types'
+import { defineStore } from 'pinia'
+import { ref } from 'vue'
+
+export const usePluginStore = defineStore(
+ 'plugin',
+ () => {
+ // 鎵�鏈夊彲鐢ㄦ彃浠�
+ const allFunctions = ref<PluginDefinition[]>([])
+
+ // 褰撳墠鏅鸿兘浣撶殑鎻掍欢閰嶇疆
+ const currentFunctions = ref<AgentFunction[]>([])
+
+ // 褰撳墠缂栬緫鐨勬櫤鑳戒綋ID
+ const currentAgentId = ref('')
+
+ // 璁剧疆鎵�鏈夊彲鐢ㄦ彃浠�
+ const setAllFunctions = (functions: PluginDefinition[]) => {
+ allFunctions.value = functions
+ }
+
+ // 璁剧疆褰撳墠鏅鸿兘浣撶殑鎻掍欢閰嶇疆
+ const setCurrentFunctions = (functions: AgentFunction[]) => {
+ currentFunctions.value = functions
+ }
+
+ // 璁剧疆褰撳墠鏅鸿兘浣揑D
+ const setCurrentAgentId = (agentId: string) => {
+ currentAgentId.value = agentId
+ }
+
+ // 鏇存柊鎻掍欢閰嶇疆锛堢敤浜庝繚瀛樻椂璋冪敤锛�
+ const updateFunctions = (functions: AgentFunction[]) => {
+ currentFunctions.value = functions
+ }
+
+ // 娓呯┖鏁版嵁
+ const clear = () => {
+ allFunctions.value = []
+ currentFunctions.value = []
+ currentAgentId.value = ''
+ }
+
+ return {
+ allFunctions,
+ currentFunctions,
+ currentAgentId,
+ setAllFunctions,
+ setCurrentFunctions,
+ setCurrentAgentId,
+ updateFunctions,
+ clear,
+ }
+ },
+ {
+ persist: false, // 涓嶆寔涔呭寲锛屾瘡娆¤繘鍏ラ〉闈㈤噸鏂板姞杞�
+ },
+)
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/store/provider.ts b/xiaozhi-esp32-server/main/manager-mobile/src/store/provider.ts
new file mode 100644
index 0000000..76a0171
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/store/provider.ts
@@ -0,0 +1,23 @@
+import type { Providers } from '@/api/agent/types'
+import { defineStore } from 'pinia'
+
+export const useProvider = defineStore('provider', () => {
+ const providers = ref<Providers[]>([])
+
+ const updateProviders = (val: Providers[]) => {
+ providers.value = val
+ }
+
+ return {
+ providers,
+ updateProviders,
+ }
+}, {
+ persist: {
+ key: 'providers',
+ serializer: {
+ serialize: state => JSON.stringify(state.providers),
+ deserialize: value => ({ providers: JSON.parse(value) }),
+ },
+ },
+})
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/store/speedPitch.ts b/xiaozhi-esp32-server/main/manager-mobile/src/store/speedPitch.ts
new file mode 100644
index 0000000..61f4bb6
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/store/speedPitch.ts
@@ -0,0 +1,44 @@
+import { defineStore } from 'pinia'
+
+const SPEED_PITCH_FIELDS = ['ttsVolume', 'ttsRate', 'ttsPitch'] as const
+
+type SpeedPitchField = typeof SPEED_PITCH_FIELDS[number]
+type SpeedPitchSettings = Record<SpeedPitchField, number>
+
+export const useSpeedPitch = defineStore('speedPitch', () => {
+ const speedPitch = ref<SpeedPitchSettings>({
+ ttsVolume: 0,
+ ttsRate: 0,
+ ttsPitch: 0,
+ })
+ const changedFields = ref<SpeedPitchField[]>([])
+
+ const updateSpeedPitch = (val: SpeedPitchSettings, options: { changedFields?: SpeedPitchField[] } = {}) => {
+ speedPitch.value = val
+ if (options.changedFields) {
+ changedFields.value = Array.from(new Set([
+ ...changedFields.value,
+ ...options.changedFields,
+ ]))
+ }
+ }
+
+ const resetChangedFields = () => {
+ changedFields.value = []
+ }
+
+ return {
+ speedPitch,
+ changedFields,
+ updateSpeedPitch,
+ resetChangedFields,
+ }
+}, {
+ persist: {
+ key: 'speedPitch',
+ serializer: {
+ serialize: state => JSON.stringify(state.speedPitch),
+ deserialize: value => ({ speedPitch: JSON.parse(value) }),
+ },
+ },
+})
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/store/user.ts b/xiaozhi-esp32-server/main/manager-mobile/src/store/user.ts
new file mode 100644
index 0000000..a062fcc
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/store/user.ts
@@ -0,0 +1,82 @@
+import type { UserInfo } from '@/api/auth'
+import { defineStore } from 'pinia'
+import { ref } from 'vue'
+import {
+ getUserInfo as _getUserInfo,
+} from '@/api/auth'
+
+// 鍒濆鍖栫姸鎬�
+const userInfoState: UserInfo & { avatar?: string, token?: string } = {
+ id: 0,
+ username: '',
+ realName: '',
+ email: '',
+ mobile: '',
+ status: 0,
+ superAdmin: 0,
+ avatar: '/static/images/default-avatar.png',
+ token: '',
+}
+
+export const useUserStore = defineStore(
+ 'userInfo',
+ () => {
+ // 瀹氫箟鐢ㄦ埛淇℃伅
+ const userInfo = ref<UserInfo & { avatar?: string, token?: string }>({ ...userInfoState })
+ // 璁剧疆鐢ㄦ埛淇℃伅
+ const setUserInfo = (val: UserInfo & { avatar?: string, token?: string }) => {
+ console.log('璁剧疆鐢ㄦ埛淇℃伅', val)
+ // 鑻ュご鍍忎负绌� 鍒欎娇鐢ㄩ粯璁ゅご鍍�
+ if (!val.avatar) {
+ val.avatar = userInfoState.avatar
+ }
+ else {
+ val.avatar = 'https://oss.laf.run/ukw0y1-site/avatar.jpg?feige'
+ }
+ userInfo.value = val
+ }
+ const setUserAvatar = (avatar: string) => {
+ userInfo.value.avatar = avatar
+ console.log('璁剧疆鐢ㄦ埛澶村儚', avatar)
+ console.log('userInfo', userInfo.value)
+ }
+ // 鍒犻櫎鐢ㄦ埛淇℃伅
+ const removeUserInfo = () => {
+ userInfo.value = { ...userInfoState }
+ uni.removeStorageSync('userInfo')
+ uni.removeStorageSync('token')
+ }
+ /**
+ * 鑾峰彇鐢ㄦ埛淇℃伅
+ */
+ const getUserInfo = async () => {
+ const userData = await _getUserInfo()
+ setUserInfo(userData)
+ return userData
+ }
+ /**
+ * 閫�鍑虹櫥褰� 骞� 鍒犻櫎鐢ㄦ埛淇℃伅
+ */
+ const logout = async () => {
+ removeUserInfo()
+ }
+
+ return {
+ userInfo,
+ getUserInfo,
+ setUserInfo,
+ setUserAvatar,
+ logout,
+ removeUserInfo,
+ }
+ },
+ {
+ persist: {
+ key: 'userInfo',
+ serializer: {
+ serialize: state => JSON.stringify(state.userInfo),
+ deserialize: value => ({ userInfo: JSON.parse(value) }),
+ },
+ },
+ },
+)
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/style/iconfont.css b/xiaozhi-esp32-server/main/manager-mobile/src/style/iconfont.css
new file mode 100644
index 0000000..35da86c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/style/iconfont.css
@@ -0,0 +1,28 @@
+@font-face {
+ font-family: 'iconfont'; /* Project id 4543091 */
+ src:
+ url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAOwAAsAAAAAB9AAAANjAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDHAqDBIJqATYCJAMQCwoABCAFhGcHPRvnBsgusG3kMyE15/44PsBX09waBHv0REDt97oHAQDFrOIyPirRiULQ+TJcXV0hCYTuVFcBC915/2vX/32Q80hkZ5PZGZ9snvwruVLloidKqYN6iKC53bOtbKwVLSIi3W6zCWZbs3VbER3j9JpGX3ySYcc94IQRTK5s4epS/jSqIgvg37qlY2/jwQN7D9ADpfRCmIknQByTscVZPTBr+hnnCKg2o4bjakvXEPjuY65DJGeJNtBUhn1JxOBuB2UZmUpBOXdsFp4oxOv4GHgs3h/+wRDcicqSZJG1q9kK1z/Af9NpqxjpC2QaAdpHlCFh4spcYXs5sMWpSk5wUj31G2dLQKVKkZ/w7f/8/i/A3JVUSZK9f7xIKJeU14IFpBI/Qfkkz46GT/CuaGREfCtKJUougWeQWHvVC5Lcz2BGS+SePR99vj3yjJx7h574tp7uWcOh4yfaTjS/245TT/vkQrN+a7RLkK8+Vd+bz+FSGh+9srDQKPeJ2s29z7ah4+efdoxefRbbGwfy7ht+SuIWukzsu1b6ePP+6kN1aamb47qsPim1Ia3xdEpDcl1dckPKGYnneI23+57r2W1Mmkqs6ajrChRCs5qyQ66rTVWhgZaG7toOeHm5cxn0sSQuNDEgcUTdNTSupKI1JRZih/JssAUKezPeOJJzbNozF6zWJuuVavVU5Tgtkop/SDzHa7ytvnCTq0PhkEfi4xLLtb0PuwyOAYqmrYQApFJyoJjTnfz+ve94vvv2f/yWgxl8Jd8Di2DRDPuob59mU/+VfDCROQyR8xSnmP9fXm7liagmN39OlmbvjqG0sMsJKrU0EFXogaRSH5bNY1CmxhyUq7QC1cY1T67RwuQk5CoM2RUQNLoEUb03kDS6h2XzcyjT7iOUa/QXqq1Hn6/GUBAaGcGcWJFlGUmCoVOp8kLvABHnVczGYiOE2SVEUH5OXj/TSnTCDjHAviAWcE4RZYaGWszNiKoayGSGTASeY+PcrMjNpVMvyREMDRoxBMYRVojFMkQiMOhohubdzxtAiOapMMbERpKMnQT9SL4ceQysVdJZVa9kEbsFogIcRyEUE2kN0mL7CDVIGhBzupWMEHA5bDvipgq5hKJcKef8ivbx1kC15KgcYkghhzLxYNntxoKCReJ82jAHAAA=')
+ format('woff2'),
+ url('//at.alicdn.com/t/c/font_4543091_njpo5b95nl.woff?t=1715485842402') format('woff'),
+ url('//at.alicdn.com/t/c/font_4543091_njpo5b95nl.ttf?t=1715485842402') format('truetype');
+}
+
+.iconfont {
+ font-family: 'iconfont' !important;
+ font-size: 16px;
+ font-style: normal;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-my:before {
+ content: '\e78c';
+}
+
+.icon-package:before {
+ content: '\e9c2';
+}
+
+.icon-chat:before {
+ content: '\e600';
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/style/index.scss b/xiaozhi-esp32-server/main/manager-mobile/src/style/index.scss
new file mode 100644
index 0000000..e1d33c5
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/style/index.scss
@@ -0,0 +1,19 @@
+// 娴嬭瘯鐢ㄧ殑 iconfont锛屽彲鐢熸晥
+// @import './iconfont.css';
+
+.test {
+ // 鍙互閫氳繃 @apply 澶氫釜鏍峰紡灏佽鏁翠綋鏍峰紡
+ @apply mt-4 ml-4;
+
+ padding-top: 4px;
+ color: red;
+}
+
+:root,
+page {
+ // 淇敼鎸変富棰樿壊
+ // --wot-color-theme: #37c2bc;
+
+ // 淇敼鎸夐挳鑳屾櫙鑹�
+ // --wot-button-primary-bg-color: green;
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/typings.d.ts b/xiaozhi-esp32-server/main/manager-mobile/src/typings.d.ts
new file mode 100644
index 0000000..9ead3fb
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/typings.d.ts
@@ -0,0 +1,28 @@
+// 鍏ㄥ眬瑕佺敤鐨勭被鍨嬫斁鍒拌繖閲�
+
+declare global {
+ interface IResData<T> {
+ code: number
+ msg: string
+ data: T
+ }
+
+ // uni.uploadFile鏂囦欢涓婁紶鍙傛暟
+ interface IUniUploadFileOptions {
+ file?: File
+ files?: UniApp.UploadFileOptionFiles[]
+ filePath?: string
+ name?: string
+ formData?: any
+ }
+
+ interface IUserInfo {
+ nickname?: string
+ avatar?: string
+ /** 寰俊鐨� openid锛岄潪寰俊娌℃湁杩欎釜瀛楁 */
+ openid?: string
+ token?: string
+ }
+}
+
+export {} // 闃叉妯″潡姹℃煋
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/typings.ts b/xiaozhi-esp32-server/main/manager-mobile/src/typings.ts
new file mode 100644
index 0000000..b48b630
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/typings.ts
@@ -0,0 +1,15 @@
+// 鏋氫妇瀹氫箟
+
+export enum TestEnum {
+ A = '1',
+ B = '2',
+}
+
+// uni.uploadFile鏂囦欢涓婁紶鍙傛暟
+export interface IUniUploadFileOptions {
+ file?: File
+ files?: UniApp.UploadFileOptionFiles[]
+ filePath?: string
+ name?: string
+ formData?: any
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/uni.scss b/xiaozhi-esp32-server/main/manager-mobile/src/uni.scss
new file mode 100644
index 0000000..21b9e5f
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/uni.scss
@@ -0,0 +1,77 @@
+/* stylelint-disable comment-empty-line-before */
+/**
+ * 杩欓噷鏄痷ni-app鍐呯疆鐨勫父鐢ㄦ牱寮忓彉閲�
+ *
+ * uni-app 瀹樻柟鎵╁睍鎻掍欢鍙婃彃浠跺競鍦猴紙https://ext.dcloud.net.cn锛変笂寰堝涓夋柟鎻掍欢鍧囦娇鐢ㄤ簡杩欎簺鏍峰紡鍙橀噺
+ * 濡傛灉浣犳槸鎻掍欢寮�鍙戣�咃紝寤鸿浣犱娇鐢╯css棰勫鐞嗭紝骞跺湪鎻掍欢浠g爜涓洿鎺ヤ娇鐢ㄨ繖浜涘彉閲忥紙鏃犻渶 import 杩欎釜鏂囦欢锛夛紝鏂逛究鐢ㄦ埛閫氳繃鎼Н鏈ㄧ殑鏂瑰紡寮�鍙戞暣浣撻鏍间竴鑷寸殑App
+ *
+ */
+
+/**
+ * 濡傛灉浣犳槸App寮�鍙戣�咃紙鎻掍欢浣跨敤鑰咃級锛屼綘鍙互閫氳繃淇敼杩欎簺鍙橀噺鏉ュ畾鍒惰嚜宸辩殑鎻掍欢涓婚锛屽疄鐜拌嚜瀹氫箟涓婚鍔熻兘
+ *
+ * 濡傛灉浣犵殑椤圭洰鍚屾牱浣跨敤浜唖css棰勫鐞嗭紝浣犱篃鍙互鐩存帴鍦ㄤ綘鐨� scss 浠g爜涓娇鐢ㄥ涓嬪彉閲忥紝鍚屾椂鏃犻渶 import 杩欎釜鏂囦欢
+ */
+
+/* 棰滆壊鍙橀噺 */
+
+/* 琛屼负鐩稿叧棰滆壊 */
+$uni-color-primary: #007aff;
+$uni-color-success: #4cd964;
+$uni-color-warning: #f0ad4e;
+$uni-color-error: #dd524d;
+
+/* 鏂囧瓧鍩烘湰棰滆壊 */
+$uni-text-color: #333; // 鍩烘湰鑹�
+$uni-text-color-inverse: #fff; // 鍙嶈壊
+$uni-text-color-grey: #999; // 杈呭姪鐏拌壊锛屽鍔犺浇鏇村鐨勬彁绀轰俊鎭�
+$uni-text-color-placeholder: #808080;
+$uni-text-color-disable: #c0c0c0;
+
+/* 鑳屾櫙棰滆壊 */
+$uni-bg-color: #fff;
+$uni-bg-color-grey: #f8f8f8;
+$uni-bg-color-hover: #f1f1f1; // 鐐瑰嚮鐘舵�侀鑹�
+$uni-bg-color-mask: rgb(0 0 0 / 40%); // 閬僵棰滆壊
+
+/* 杈规棰滆壊 */
+$uni-border-color: #c8c7cc;
+
+/* 灏哄鍙橀噺 */
+
+/* 鏂囧瓧灏哄 */
+$uni-font-size-sm: 12px;
+$uni-font-size-base: 14px;
+$uni-font-size-lg: 16;
+
+/* 鍥剧墖灏哄 */
+$uni-img-size-sm: 20px;
+$uni-img-size-base: 26px;
+$uni-img-size-lg: 40px;
+
+/* Border Radius */
+$uni-border-radius-sm: 2px;
+$uni-border-radius-base: 3px;
+$uni-border-radius-lg: 6px;
+$uni-border-radius-circle: 50%;
+
+/* 姘村钩闂磋窛 */
+$uni-spacing-row-sm: 5px;
+$uni-spacing-row-base: 10px;
+$uni-spacing-row-lg: 15px;
+
+/* 鍨傜洿闂磋窛 */
+$uni-spacing-col-sm: 4px;
+$uni-spacing-col-base: 8px;
+$uni-spacing-col-lg: 12px;
+
+/* 閫忔槑搴� */
+$uni-opacity-disabled: 0.3; // 缁勪欢绂佺敤鎬佺殑閫忔槑搴�
+
+/* 鏂囩珷鍦烘櫙鐩稿叧 */
+$uni-color-title: #2c405a; // 鏂囩珷鏍囬棰滆壊
+$uni-font-size-title: 20px;
+$uni-color-subtitle: #555; // 浜岀骇鏍囬棰滆壊
+$uni-font-size-subtitle: 18px;
+$uni-color-paragraph: #3f536e; // 鏂囩珷娈佃惤棰滆壊
+$uni-font-size-paragraph: 15px;
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/utils/index.ts b/xiaozhi-esp32-server/main/manager-mobile/src/utils/index.ts
new file mode 100644
index 0000000..dbc3f8d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/utils/index.ts
@@ -0,0 +1,335 @@
+import smCrypto from 'sm-crypto'
+import { pages, subPackages } from '@/pages.json'
+
+import { isMpWeixin } from './platform'
+
+/**
+ * 杩愯鏃舵湇鍔$鍦板潃瑕嗙洊瀛樺偍閿�
+ */
+export const SERVER_BASE_URL_OVERRIDE_KEY = 'server_base_url_override'
+
+/**
+ * 璁剧疆/娓呴櫎/鑾峰彇 杩愯鏃惰鐩栫殑鏈嶅姟绔湴鍧�
+ */
+export function setServerBaseUrlOverride(url: string) {
+ uni.setStorageSync(SERVER_BASE_URL_OVERRIDE_KEY, url)
+}
+
+export function clearServerBaseUrlOverride() {
+ uni.removeStorageSync(SERVER_BASE_URL_OVERRIDE_KEY)
+}
+
+export function getServerBaseUrlOverride(): string | null {
+ const value = uni.getStorageSync(SERVER_BASE_URL_OVERRIDE_KEY)
+ return value || null
+}
+
+export function getLastPage() {
+ // getCurrentPages() 鑷冲皯鏈�1涓厓绱狅紝鎵�浠ヤ笉鍐嶉澶栧垽鏂�
+ // const lastPage = getCurrentPages().at(-1)
+ // 涓婇潰閭d釜鍦ㄤ綆鐗堟湰瀹夊崜涓墦鍖呬細鎶ラ敊锛屾墍浠ユ敼鐢ㄤ笅闈㈣繖涓�愯櫧鐒舵垜鍔犱簡 src/interceptions/prototype.ts锛屼絾渚濈劧鎶ラ敊銆�
+ const pages = getCurrentPages()
+ return pages[pages.length - 1]
+}
+
+/**
+ * 鑾峰彇褰撳墠椤甸潰璺敱鐨� path 璺緞鍜� redirectPath 璺緞
+ * path 濡� '/pages/login/index'
+ * redirectPath 濡� '/pages/demo/base/route-interceptor'
+ */
+export function currRoute() {
+ const lastPage = getLastPage()
+ const currRoute = (lastPage as any).$page
+ // console.log('lastPage.$page:', currRoute)
+ // console.log('lastPage.$page.fullpath:', currRoute.fullPath)
+ // console.log('lastPage.$page.options:', currRoute.options)
+ // console.log('lastPage.options:', (lastPage as any).options)
+ // 缁忚繃澶氱娴嬭瘯锛屽彧鏈� fullPath 闈犺氨锛屽叾浠栭兘涓嶉潬璋�
+ const { fullPath } = currRoute as { fullPath: string }
+ // console.log(fullPath)
+ // eg: /pages/login/index?redirect=%2Fpages%2Fdemo%2Fbase%2Froute-interceptor (灏忕▼搴�)
+ // eg: /pages/login/index?redirect=%2Fpages%2Froute-interceptor%2Findex%3Fname%3Dfeige%26age%3D30(h5)
+ return getUrlObj(fullPath)
+}
+
+function ensureDecodeURIComponent(url: string) {
+ if (url.startsWith('%')) {
+ return ensureDecodeURIComponent(decodeURIComponent(url))
+ }
+ return url
+}
+/**
+ * 瑙f瀽 url 寰楀埌 path 鍜� query
+ * 姣斿杈撳叆url: /pages/login/index?redirect=%2Fpages%2Fdemo%2Fbase%2Froute-interceptor
+ * 杈撳嚭: {path: /pages/login/index, query: {redirect: /pages/demo/base/route-interceptor}}
+ */
+export function getUrlObj(url: string) {
+ const [path, queryStr] = url.split('?')
+ // console.log(path, queryStr)
+
+ if (!queryStr) {
+ return {
+ path,
+ query: {},
+ }
+ }
+ const query: Record<string, string> = {}
+ queryStr.split('&').forEach((item) => {
+ const [key, value] = item.split('=')
+ // console.log(key, value)
+ query[key] = ensureDecodeURIComponent(value) // 杩欓噷闇�瑕佺粺涓� decodeURIComponent 涓�涓嬶紝鍙互鍏煎h5鍜屽井淇
+ })
+ return { path, query }
+}
+/**
+ * 寰楀埌鎵�鏈夌殑闇�瑕佺櫥褰曠殑 pages锛屽寘鎷富鍖呭拰鍒嗗寘鐨�
+ * 杩欓噷璁捐寰楅�氱敤涓�鐐癸紝鍙互浼犻�� key 浣滀负鍒ゆ柇渚濇嵁锛岄粯璁ゆ槸 needLogin, 涓� route-block 閰嶅浣跨敤
+ * 濡傛灉娌℃湁浼� key锛屽垯琛ㄧず鎵�鏈夌殑 pages锛屽鏋滀紶閫掍簡 key, 鍒欒〃绀洪�氳繃 key 杩囨护
+ */
+export function getAllPages(key = 'needLogin') {
+ // 杩欓噷澶勭悊涓诲寘
+ const mainPages = pages
+ .filter(page => !key || page[key])
+ .map(page => ({
+ ...page,
+ path: `/${page.path}`,
+ }))
+
+ // 杩欓噷澶勭悊鍒嗗寘
+ const subPages: any[] = []
+ subPackages.forEach((subPageObj) => {
+ // console.log(subPageObj)
+ const { root } = subPageObj
+
+ subPageObj.pages
+ .filter(page => !key || page[key])
+ .forEach((page: { path: string } & Record<string, any>) => {
+ subPages.push({
+ ...page,
+ path: `/${root}/${page.path}`,
+ })
+ })
+ })
+ const result = [...mainPages, ...subPages]
+ // console.log(`getAllPages by ${key} result: `, result)
+ return result
+}
+
+/**
+ * 寰楀埌鎵�鏈夌殑闇�瑕佺櫥褰曠殑 pages锛屽寘鎷富鍖呭拰鍒嗗寘鐨�
+ * 鍙緱鍒� path 鏁扮粍
+ */
+export const getNeedLoginPages = (): string[] => getAllPages('needLogin').map(page => page.path)
+
+/**
+ * 寰楀埌鎵�鏈夌殑闇�瑕佺櫥褰曠殑 pages锛屽寘鎷富鍖呭拰鍒嗗寘鐨�
+ * 鍙緱鍒� path 鏁扮粍
+ */
+export const needLoginPages: string[] = getAllPages('needLogin').map(page => page.path)
+
+/**
+ * 鏍规嵁寰俊灏忕▼搴忓綋鍓嶇幆澧冿紝鍒ゆ柇搴旇鑾峰彇鐨� baseUrl
+ */
+export function getEnvBaseUrl() {
+ // 鑻ュ瓨鍦ㄧ敤鎴疯缃殑瑕嗙洊鍦板潃锛屼紭鍏堣繑鍥�
+ const override = getServerBaseUrlOverride()
+ if (override)
+ return override
+
+ // 璇锋眰鍩哄噯鍦板潃锛堥粯璁ゆ潵婧愪簬 env锛�
+ let baseUrl = import.meta.env.VITE_SERVER_BASEURL
+
+ // # 鏈変簺鍚屽鍙兘闇�瑕佸湪寰俊灏忕▼搴忛噷闈㈡牴鎹� develop銆乼rial銆乺elease 鍒嗗埆璁剧疆涓婁紶鍦板潃锛屽弬鑰冧唬鐮佸涓嬨��
+ const VITE_SERVER_BASEURL__WEIXIN_DEVELOP = 'https://ukw0y1.laf.run'
+ const VITE_SERVER_BASEURL__WEIXIN_TRIAL = 'https://ukw0y1.laf.run'
+ const VITE_SERVER_BASEURL__WEIXIN_RELEASE = 'https://ukw0y1.laf.run'
+
+ // 寰俊灏忕▼搴忕鐜鍖哄垎
+ if (isMpWeixin) {
+ const {
+ miniProgram: { envVersion },
+ } = uni.getAccountInfoSync()
+
+ switch (envVersion) {
+ case 'develop':
+ baseUrl = VITE_SERVER_BASEURL__WEIXIN_DEVELOP || baseUrl
+ break
+ case 'trial':
+ baseUrl = VITE_SERVER_BASEURL__WEIXIN_TRIAL || baseUrl
+ break
+ case 'release':
+ baseUrl = VITE_SERVER_BASEURL__WEIXIN_RELEASE || baseUrl
+ break
+ }
+ }
+
+ return baseUrl
+}
+
+/**
+ * 鏍规嵁寰俊灏忕▼搴忓綋鍓嶇幆澧冿紝鍒ゆ柇搴旇鑾峰彇鐨� UPLOAD_BASEURL
+ */
+export function getEnvBaseUploadUrl() {
+ // 璇锋眰鍩哄噯鍦板潃
+ let baseUploadUrl = import.meta.env.VITE_UPLOAD_BASEURL
+
+ const VITE_UPLOAD_BASEURL__WEIXIN_DEVELOP = 'https://ukw0y1.laf.run/upload'
+ const VITE_UPLOAD_BASEURL__WEIXIN_TRIAL = 'https://ukw0y1.laf.run/upload'
+ const VITE_UPLOAD_BASEURL__WEIXIN_RELEASE = 'https://ukw0y1.laf.run/upload'
+
+ // 寰俊灏忕▼搴忕鐜鍖哄垎
+ if (isMpWeixin) {
+ const {
+ miniProgram: { envVersion },
+ } = uni.getAccountInfoSync()
+
+ switch (envVersion) {
+ case 'develop':
+ baseUploadUrl = VITE_UPLOAD_BASEURL__WEIXIN_DEVELOP || baseUploadUrl
+ break
+ case 'trial':
+ baseUploadUrl = VITE_UPLOAD_BASEURL__WEIXIN_TRIAL || baseUploadUrl
+ break
+ case 'release':
+ baseUploadUrl = VITE_UPLOAD_BASEURL__WEIXIN_RELEASE || baseUploadUrl
+ break
+ }
+ }
+
+ return baseUploadUrl
+}
+
+/**
+ * 鐢熸垚SM2瀵嗛挜瀵癸紙鍗佸叚杩涘埗鏍煎紡锛�
+ * @returns {object} 鍖呭惈鍏挜鍜岀閽ョ殑瀵硅薄
+ */
+export function generateSm2KeyPairHex() {
+ // 浣跨敤sm-crypto搴撶敓鎴怱M2瀵嗛挜瀵�
+ const sm2 = smCrypto.sm2
+ const keypair = sm2.generateKeyPairHex()
+
+ return {
+ publicKey: keypair.publicKey,
+ privateKey: keypair.privateKey,
+ clientPublicKey: keypair.publicKey, // 瀹㈡埛绔叕閽�
+ clientPrivateKey: keypair.privateKey, // 瀹㈡埛绔閽�
+ }
+}
+
+/**
+ * SM2鍏挜鍔犲瘑
+ * @param {string} publicKey 鍏挜锛堝崄鍏繘鍒舵牸寮忥級
+ * @param {string} plainText 鏄庢枃
+ * @returns {string} 鍔犲瘑鍚庣殑瀵嗘枃锛堝崄鍏繘鍒舵牸寮忥級
+ */
+export function sm2Encrypt(publicKey: string, plainText: string): string {
+ if (!publicKey) {
+ throw new Error('鍏挜涓嶈兘涓簄ull鎴杣ndefined')
+ }
+
+ if (!plainText) {
+ throw new Error('鏄庢枃涓嶈兘涓虹┖')
+ }
+
+ const sm2 = smCrypto.sm2
+ // SM2鍔犲瘑锛屾坊鍔�04鍓嶇紑琛ㄧず鏈帇缂╁叕閽�
+ const encrypted = sm2.doEncrypt(plainText, publicKey, 1)
+ // 杞崲涓哄崄鍏繘鍒舵牸寮忥紙涓庡悗绔繚鎸佷竴鑷达紝娣诲姞04鍓嶇紑锛�
+ const result = `04${encrypted}`
+
+ return result
+}
+
+/**
+ * SM2绉侀挜瑙e瘑
+ * @param {string} privateKey 绉侀挜锛堝崄鍏繘鍒舵牸寮忥級
+ * @param {string} cipherText 瀵嗘枃锛堝崄鍏繘鍒舵牸寮忥級
+ * @returns {string} 瑙e瘑鍚庣殑鏄庢枃
+ */
+export function sm2Decrypt(privateKey: string, cipherText: string): string {
+ const sm2 = smCrypto.sm2
+ // 绉婚櫎04鍓嶇紑锛堜笌鍚庣淇濇寔涓�鑷达級
+ const dataWithoutPrefix = cipherText.startsWith('04') ? cipherText.substring(2) : cipherText
+ // SM2瑙e瘑
+ return sm2.doDecrypt(dataWithoutPrefix, privateKey, 1)
+}
+
+type AnyFunction = (...args: any[]) => any
+
+interface DebouncedFunction extends AnyFunction {
+ cancel: () => void
+}
+
+/**
+ * 闃叉姈鍑芥暟
+ * @param fn 瑕侀槻鎶栫殑鍑芥暟
+ * @param delay 寤惰繜鏃堕棿锛堟绉掞級锛岄粯璁�500ms
+ * @param immediate 鏄惁绔嬪嵆鎵ц锛岄粯璁alse
+ * @returns 闃叉姈澶勭悊鍚庣殑鍑芥暟
+ */
+export function debounce<T extends AnyFunction>(
+ fn: T,
+ delay = 500,
+ immediate = false,
+): DebouncedFunction {
+ let timer: ReturnType<typeof setTimeout> | null = null
+
+ const debounced = function (this: any, ...args: Parameters<T>) {
+ if (timer) {
+ clearTimeout(timer)
+ }
+
+ if (immediate && !timer) {
+ fn.apply(this, args)
+ }
+
+ timer = setTimeout(() => {
+ if (!immediate) {
+ fn.apply(this, args)
+ }
+ timer = null
+ }, delay)
+ } as DebouncedFunction
+
+ debounced.cancel = () => {
+ if (timer) {
+ clearTimeout(timer)
+ timer = null
+ }
+ }
+
+ return debounced
+}
+
+type DeepCloneTarget = string | number | boolean | null | undefined | object
+
+/**
+ * 娣辨嫹璐濇柟娉�
+ * @param target 瑕佹嫹璐濈殑鐩爣
+ * @returns 鎷疯礉鍚庣殑鏂板璞�
+ */
+export function deepClone<T extends DeepCloneTarget>(target: T): T {
+ if (target === null || typeof target !== 'object') {
+ return target
+ }
+
+ if (target instanceof Date) {
+ return new Date(target.getTime()) as any
+ }
+
+ if (Array.isArray(target)) {
+ return target.map(item => deepClone(item)) as any
+ }
+
+ if (target instanceof Object) {
+ const clonedObj = {} as T
+ for (const key in target) {
+ if (Object.prototype.hasOwnProperty.call(target, key)) {
+ (clonedObj as any)[key] = deepClone((target as any)[key])
+ }
+ }
+ return clonedObj
+ }
+
+ return target
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/utils/platform.ts b/xiaozhi-esp32-server/main/manager-mobile/src/utils/platform.ts
new file mode 100644
index 0000000..c714199
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/utils/platform.ts
@@ -0,0 +1,24 @@
+/*
+ * @Author: 鑿查附
+ * @Date: 2024-03-28 19:13:55
+ * @Last Modified by: 鑿查附
+ * @Last Modified time: 2024-03-28 19:24:55
+ */
+export const platform = __UNI_PLATFORM__
+export const isH5 = __UNI_PLATFORM__ === 'h5'
+export const isApp = __UNI_PLATFORM__ === 'app'
+export const isMp = __UNI_PLATFORM__.startsWith('mp-')
+export const isMpWeixin = __UNI_PLATFORM__.startsWith('mp-weixin')
+export const isMpAplipay = __UNI_PLATFORM__.startsWith('mp-alipay')
+export const isMpToutiao = __UNI_PLATFORM__.startsWith('mp-toutiao')
+
+const PLATFORM = {
+ platform,
+ isH5,
+ isApp,
+ isMp,
+ isMpWeixin,
+ isMpAplipay,
+ isMpToutiao,
+}
+export default PLATFORM
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/utils/toast.ts b/xiaozhi-esp32-server/main/manager-mobile/src/utils/toast.ts
new file mode 100644
index 0000000..e524b00
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/utils/toast.ts
@@ -0,0 +1,65 @@
+/**
+ * toast 寮圭獥缁勪欢
+ * 鏀寔 success/error/warning/info 鍥涚鐘舵��
+ * 鍙厤缃� duration, position 绛夊弬鏁�
+ */
+
+type ToastType = 'success' | 'error' | 'warning' | 'info'
+
+interface ToastOptions {
+ type?: ToastType
+ duration?: number
+ position?: 'top' | 'middle' | 'bottom'
+ icon?: 'success' | 'error' | 'none' | 'loading' | 'fail' | 'exception'
+ message: string
+}
+
+export function showToast(options: ToastOptions | string) {
+ const defaultOptions: ToastOptions = {
+ type: 'info',
+ duration: 2000,
+ position: 'middle',
+ message: '',
+ }
+ const mergedOptions
+ = typeof options === 'string'
+ ? { ...defaultOptions, message: options }
+ : { ...defaultOptions, ...options }
+ // 鏄犲皠position鍒皍niapp鏀寔鐨勬牸寮�
+ const positionMap: Record<ToastOptions['position'], 'top' | 'bottom' | 'center'> = {
+ top: 'top',
+ middle: 'center',
+ bottom: 'bottom',
+ }
+
+ // 鏄犲皠鍥炬爣绫诲瀷
+ const iconMap: Record<
+ ToastType,
+ 'success' | 'error' | 'none' | 'loading' | 'fail' | 'exception'
+ > = {
+ success: 'success',
+ error: 'error',
+ warning: 'fail',
+ info: 'none',
+ }
+
+ // 璋冪敤uni.showToast鏄剧ず鎻愮ず
+ uni.showToast({
+ title: mergedOptions.message,
+ duration: mergedOptions.duration,
+ position: positionMap[mergedOptions.position],
+ icon: mergedOptions.icon || iconMap[mergedOptions.type],
+ mask: true,
+ })
+}
+
+export const toast = {
+ success: (message: string, options?: Omit<ToastOptions, 'type'>) =>
+ showToast({ ...options, type: 'success', message }),
+ error: (message: string, options?: Omit<ToastOptions, 'type'>) =>
+ showToast({ ...options, type: 'error', message }),
+ warning: (message: string, options?: Omit<ToastOptions, 'type'>) =>
+ showToast({ ...options, type: 'warning', message }),
+ info: (message: string, options?: Omit<ToastOptions, 'type'>) =>
+ showToast({ ...options, type: 'info', message }),
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/utils/uploadFile.ts b/xiaozhi-esp32-server/main/manager-mobile/src/utils/uploadFile.ts
new file mode 100644
index 0000000..18cf5e9
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/utils/uploadFile.ts
@@ -0,0 +1,327 @@
+import { getEnvBaseUrl } from './index'
+import { toast } from './toast'
+
+/**
+ * 鏂囦欢涓婁紶閽╁瓙鍑芥暟浣跨敤绀轰緥
+ * @example
+ * const { loading, error, data, progress, run } = useUpload<IUploadResult>(
+ * uploadUrl,
+ * {},
+ * {
+ * maxSize: 5, // 鏈�澶�5MB
+ * sourceType: ['album'], // 浠呮敮鎸佷粠鐩稿唽閫夋嫨
+ * onProgress: (p) => console.log(`涓婁紶杩涘害锛�${p}%`),
+ * onSuccess: (res) => console.log('涓婁紶鎴愬姛', res),
+ * onError: (err) => console.error('涓婁紶澶辫触', err),
+ * },
+ * )
+ */
+
+/**
+ * 涓婁紶鏂囦欢鐨刄RL閰嶇疆
+ */
+export const uploadFileUrl = {
+ /** 鐢ㄦ埛澶村儚涓婁紶鍦板潃锛堝姩鎬佽鍙栧綋鍓嶇敓鏁堢殑 BaseURL锛� */
+ get USER_AVATAR() {
+ return `${getEnvBaseUrl()}/user/avatar`
+ },
+}
+
+/**
+ * 閫氱敤鏂囦欢涓婁紶鍑芥暟锛堟敮鎸佺洿鎺ヤ紶鍏ユ枃浠惰矾寰勶級
+ * @param url 涓婁紶鍦板潃
+ * @param filePath 鏈湴鏂囦欢璺緞
+ * @param formData 棰濆琛ㄥ崟鏁版嵁
+ * @param options 涓婁紶閫夐」
+ */
+export function useFileUpload<T = string>(url: string, filePath: string, formData: Record<string, any> = {}, options: Omit<UploadOptions, 'sourceType' | 'sizeType' | 'count'> = {}) {
+ return useUpload<T>(
+ url,
+ formData,
+ {
+ ...options,
+ sourceType: ['album'],
+ sizeType: ['original'],
+ },
+ filePath,
+ )
+}
+
+export interface UploadOptions {
+ /** 鏈�澶у彲閫夋嫨鐨勫浘鐗囨暟閲忥紝榛樿涓�1 */
+ count?: number
+ /** 鎵�閫夌殑鍥剧墖鐨勫昂瀵革紝original-鍘熷浘锛宑ompressed-鍘嬬缉鍥� */
+ sizeType?: Array<'original' | 'compressed'>
+ /** 閫夋嫨鍥剧墖鐨勬潵婧愶紝album-鐩稿唽锛宑amera-鐩告満 */
+ sourceType?: Array<'album' | 'camera'>
+ /** 鏂囦欢澶у皬闄愬埗锛屽崟浣嶏細MB */
+ maxSize?: number //
+ /** 涓婁紶杩涘害鍥炶皟鍑芥暟 */
+ onProgress?: (progress: number) => void
+ /** 涓婁紶鎴愬姛鍥炶皟鍑芥暟 */
+ onSuccess?: (res: Record<string, any>) => void
+ /** 涓婁紶澶辫触鍥炶皟鍑芥暟 */
+ onError?: (err: Error | UniApp.GeneralCallbackResult) => void
+ /** 涓婁紶瀹屾垚鍥炶皟鍑芥暟锛堟棤璁烘垚鍔熷け璐ワ級 */
+ onComplete?: () => void
+}
+
+/**
+ * 鏂囦欢涓婁紶閽╁瓙鍑芥暟
+ * @template T 涓婁紶鎴愬姛鍚庤繑鍥炵殑鏁版嵁绫诲瀷
+ * @param url 涓婁紶鍦板潃
+ * @param formData 棰濆鐨勮〃鍗曟暟鎹�
+ * @param options 涓婁紶閫夐」
+ * @returns 涓婁紶鐘舵�佸拰鎺у埗瀵硅薄
+ */
+export function useUpload<T = string>(url: string, formData: Record<string, any> = {}, options: UploadOptions = {},
+ /** 鐩存帴浼犲叆鏂囦欢璺緞锛岃烦杩囬�夋嫨鍣� */
+ directFilePath?: string) {
+ /** 涓婁紶涓姸鎬� */
+ const loading = ref(false)
+ /** 涓婁紶閿欒鐘舵�� */
+ const error = ref(false)
+ /** 涓婁紶鎴愬姛鍚庣殑鍝嶅簲鏁版嵁 */
+ const data = ref<T>()
+ /** 涓婁紶杩涘害锛�0-100锛� */
+ const progress = ref(0)
+
+ /** 瑙f瀯涓婁紶閫夐」锛岃缃粯璁ゅ�� */
+ const {
+ /** 鏈�澶у彲閫夋嫨鐨勫浘鐗囨暟閲� */
+ count = 1,
+ /** 鎵�閫夌殑鍥剧墖鐨勫昂瀵� */
+ sizeType = ['original', 'compressed'],
+ /** 閫夋嫨鍥剧墖鐨勬潵婧� */
+ sourceType = ['album', 'camera'],
+ /** 鏂囦欢澶у皬闄愬埗锛圡B锛� */
+ maxSize = 10,
+ /** 杩涘害鍥炶皟 */
+ onProgress,
+ /** 鎴愬姛鍥炶皟 */
+ onSuccess,
+ /** 澶辫触鍥炶皟 */
+ onError,
+ /** 瀹屾垚鍥炶皟 */
+ onComplete,
+ } = options
+
+ /**
+ * 妫�鏌ユ枃浠跺ぇ灏忔槸鍚﹁秴杩囬檺鍒�
+ * @param size 鏂囦欢澶у皬锛堝瓧鑺傦級
+ * @returns 鏄惁閫氳繃妫�鏌�
+ */
+ const checkFileSize = (size: number) => {
+ const sizeInMB = size / 1024 / 1024
+ if (sizeInMB > maxSize) {
+ toast.warning(`鏂囦欢澶у皬涓嶈兘瓒呰繃${maxSize}MB`)
+ return false
+ }
+ return true
+ }
+ /**
+ * 瑙﹀彂鏂囦欢閫夋嫨鍜屼笂浼�
+ * 鏍规嵁骞冲彴浣跨敤涓嶅悓鐨勯�夋嫨鍣細
+ * - 寰俊灏忕▼搴忎娇鐢� chooseMedia
+ * - 鍏朵粬骞冲彴浣跨敤 chooseImage
+ */
+ const run = () => {
+ if (directFilePath) {
+ // 鐩存帴浣跨敤浼犲叆鐨勬枃浠惰矾寰�
+ loading.value = true
+ progress.value = 0
+ uploadFile<T>({
+ url,
+ tempFilePath: directFilePath,
+ formData,
+ data,
+ error,
+ loading,
+ progress,
+ onProgress,
+ onSuccess,
+ onError,
+ onComplete,
+ })
+ return
+ }
+
+ // #ifdef MP-WEIXIN
+ // 寰俊灏忕▼搴忕幆澧冧笅浣跨敤 chooseMedia API
+ uni.chooseMedia({
+ count,
+ mediaType: ['image'], // 浠呮敮鎸佸浘鐗囩被鍨�
+ sourceType,
+ success: (res) => {
+ const file = res.tempFiles[0]
+ // 妫�鏌ユ枃浠跺ぇ灏忔槸鍚︾鍚堥檺鍒�
+ if (!checkFileSize(file.size))
+ return
+
+ // 寮�濮嬩笂浼�
+ loading.value = true
+ progress.value = 0
+ uploadFile<T>({
+ url,
+ tempFilePath: file.tempFilePath,
+ formData,
+ data,
+ error,
+ loading,
+ progress,
+ onProgress,
+ onSuccess,
+ onError,
+ onComplete,
+ })
+ },
+ fail: (err) => {
+ console.error('閫夋嫨濯掍綋鏂囦欢澶辫触:', err)
+ error.value = true
+ onError?.(err)
+ },
+ })
+ // #endif
+
+ // #ifndef MP-WEIXIN
+ // 闈炲井淇″皬绋嬪簭鐜涓嬩娇鐢� chooseImage API
+ uni.chooseImage({
+ count,
+ sizeType,
+ sourceType,
+ success: (res) => {
+ console.log('閫夋嫨鍥剧墖鎴愬姛:', res)
+
+ // 寮�濮嬩笂浼�
+ loading.value = true
+ progress.value = 0
+ uploadFile<T>({
+ url,
+ tempFilePath: res.tempFilePaths[0],
+ formData,
+ data,
+ error,
+ loading,
+ progress,
+ onProgress,
+ onSuccess,
+ onError,
+ onComplete,
+ })
+ },
+ fail: (err) => {
+ console.error('閫夋嫨鍥剧墖澶辫触:', err)
+ error.value = true
+ onError?.(err)
+ },
+ })
+ // #endif
+ }
+
+ return { loading, error, data, progress, run }
+}
+
+/**
+ * 鏂囦欢涓婁紶閫夐」鎺ュ彛
+ * @template T 涓婁紶鎴愬姛鍚庤繑鍥炵殑鏁版嵁绫诲瀷
+ */
+interface UploadFileOptions<T> {
+ /** 涓婁紶鍦板潃 */
+ url: string
+ /** 涓存椂鏂囦欢璺緞 */
+ tempFilePath: string
+ /** 棰濆鐨勮〃鍗曟暟鎹� */
+ formData: Record<string, any>
+ /** 涓婁紶鎴愬姛鍚庣殑鍝嶅簲鏁版嵁 */
+ data: Ref<T | undefined>
+ /** 涓婁紶閿欒鐘舵�� */
+ error: Ref<boolean>
+ /** 涓婁紶涓姸鎬� */
+ loading: Ref<boolean>
+ /** 涓婁紶杩涘害锛�0-100锛� */
+ progress: Ref<number>
+ /** 涓婁紶杩涘害鍥炶皟 */
+ onProgress?: (progress: number) => void
+ /** 涓婁紶鎴愬姛鍥炶皟 */
+ onSuccess?: (res: Record<string, any>) => void
+ /** 涓婁紶澶辫触鍥炶皟 */
+ onError?: (err: Error | UniApp.GeneralCallbackResult) => void
+ /** 涓婁紶瀹屾垚鍥炶皟 */
+ onComplete?: () => void
+}
+
+/**
+ * 鎵ц鏂囦欢涓婁紶
+ * @template T 涓婁紶鎴愬姛鍚庤繑鍥炵殑鏁版嵁绫诲瀷
+ * @param options 涓婁紶閫夐」
+ */
+function uploadFile<T>({
+ url,
+ tempFilePath,
+ formData,
+ data,
+ error,
+ loading,
+ progress,
+ onProgress,
+ onSuccess,
+ onError,
+ onComplete,
+}: UploadFileOptions<T>) {
+ try {
+ // 鍒涘缓涓婁紶浠诲姟
+ const uploadTask = uni.uploadFile({
+ url,
+ filePath: tempFilePath,
+ name: 'file', // 鏂囦欢瀵瑰簲鐨� key
+ formData,
+ header: {
+ // H5鐜涓嬩笉闇�瑕佹墜鍔ㄨ缃瓹ontent-Type锛岃娴忚鍣ㄨ嚜鍔ㄥ鐞唌ultipart鏍煎紡
+ // #ifndef H5
+ 'Content-Type': 'multipart/form-data',
+ // #endif
+ },
+ // 纭繚鏂囦欢鍚嶇О鍚堟硶
+ success: (uploadFileRes) => {
+ console.log('涓婁紶鏂囦欢鎴愬姛:', uploadFileRes)
+ try {
+ // 瑙f瀽鍝嶅簲鏁版嵁
+ const { data: _data } = JSON.parse(uploadFileRes.data)
+ // 涓婁紶鎴愬姛
+ data.value = _data as T
+ onSuccess?.(_data)
+ }
+ catch (err) {
+ // 鍝嶅簲瑙f瀽閿欒
+ console.error('瑙f瀽涓婁紶鍝嶅簲澶辫触:', err)
+ error.value = true
+ onError?.(new Error('涓婁紶鍝嶅簲瑙f瀽澶辫触'))
+ }
+ },
+ fail: (err) => {
+ // 涓婁紶璇锋眰澶辫触
+ console.error('涓婁紶鏂囦欢澶辫触:', err)
+ error.value = true
+ onError?.(err)
+ },
+ complete: () => {
+ // 鏃犺鎴愬姛澶辫触閮芥墽琛�
+ loading.value = false
+ onComplete?.()
+ },
+ })
+
+ // 鐩戝惉涓婁紶杩涘害
+ uploadTask.onProgressUpdate((res) => {
+ progress.value = res.progress
+ onProgress?.(res.progress)
+ })
+ }
+ catch (err) {
+ // 鍒涘缓涓婁紶浠诲姟澶辫触
+ console.error('鍒涘缓涓婁紶浠诲姟澶辫触:', err)
+ error.value = true
+ loading.value = false
+ onError?.(new Error('鍒涘缓涓婁紶浠诲姟澶辫触'))
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/src/wot-design-uni.d.ts b/xiaozhi-esp32-server/main/manager-mobile/src/wot-design-uni.d.ts
new file mode 100644
index 0000000..c2c0072
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/src/wot-design-uni.d.ts
@@ -0,0 +1,56 @@
+import type { ExtractPropTypes } from 'vue'
+import type { actionSheetProps } from 'wot-design-uni/components/wd-action-sheet/types'
+import type { buttonProps } from 'wot-design-uni/components/wd-button/types'
+import type { checkboxProps } from 'wot-design-uni/components/wd-checkbox/types'
+import type { configProviderProps } from 'wot-design-uni/components/wd-config-provider/types'
+import type { fabProps } from 'wot-design-uni/components/wd-fab/types'
+import type { iconProps } from 'wot-design-uni/components/wd-icon/types'
+import type { imgProps } from 'wot-design-uni/components/wd-img/types'
+import type { inputProps } from 'wot-design-uni/components/wd-input/types'
+import type { loadingProps } from 'wot-design-uni/components/wd-loading/types'
+import type { messageBoxProps } from 'wot-design-uni/components/wd-message-box/types'
+import type { navbarProps } from 'wot-design-uni/components/wd-navbar/types'
+import type { pickerProps } from 'wot-design-uni/components/wd-picker/types'
+import type { popupProps } from 'wot-design-uni/components/wd-popup/types'
+import type { segmentedProps } from 'wot-design-uni/components/wd-segmented/types'
+import type { sliderProps } from 'wot-design-uni/components/wd-slider/types'
+import type { statusTipProps } from 'wot-design-uni/components/wd-status-tip/types'
+import type { swipeActionProps } from 'wot-design-uni/components/wd-swipe-action/types'
+import type { switchProps } from 'wot-design-uni/components/wd-switch/types'
+import type { tabbarItemProps } from 'wot-design-uni/components/wd-tabbar-item/types'
+import type { tabbarProps } from 'wot-design-uni/components/wd-tabbar/types'
+import type { tagProps } from 'wot-design-uni/components/wd-tag/types'
+import type { toastProps } from 'wot-design-uni/components/wd-toast/types'
+
+type TypedGlobalComponent<Props> = new () => { $props: Partial<Props> }
+
+// wot-design-uni 1.9.1 exposes raw Vue source through its global declarations.
+// Rebuild lightweight global components from the published prop objects without
+// pulling the package's raw Vue source into vue-tsc. Partial preserves Vue's
+// runtime defaults while retaining prop names and value types.
+declare module 'vue' {
+ export interface GlobalComponents {
+ WdActionSheet: TypedGlobalComponent<ExtractPropTypes<typeof actionSheetProps>>
+ WdButton: TypedGlobalComponent<ExtractPropTypes<typeof buttonProps>>
+ WdCheckbox: TypedGlobalComponent<ExtractPropTypes<typeof checkboxProps>>
+ WdConfigProvider: TypedGlobalComponent<ExtractPropTypes<typeof configProviderProps>>
+ WdFab: TypedGlobalComponent<ExtractPropTypes<typeof fabProps>>
+ WdIcon: TypedGlobalComponent<ExtractPropTypes<typeof iconProps>>
+ WdImg: TypedGlobalComponent<ExtractPropTypes<typeof imgProps>>
+ WdInput: TypedGlobalComponent<ExtractPropTypes<typeof inputProps>>
+ WdLoading: TypedGlobalComponent<ExtractPropTypes<typeof loadingProps>>
+ WdMessageBox: TypedGlobalComponent<ExtractPropTypes<typeof messageBoxProps>>
+ WdNavbar: TypedGlobalComponent<ExtractPropTypes<typeof navbarProps>>
+ WdPicker: TypedGlobalComponent<ExtractPropTypes<typeof pickerProps>>
+ WdPopup: TypedGlobalComponent<ExtractPropTypes<typeof popupProps>>
+ WdSegmented: TypedGlobalComponent<ExtractPropTypes<typeof segmentedProps>>
+ WdSlider: TypedGlobalComponent<ExtractPropTypes<typeof sliderProps>>
+ WdStatusTip: TypedGlobalComponent<ExtractPropTypes<typeof statusTipProps>>
+ WdSwipeAction: TypedGlobalComponent<ExtractPropTypes<typeof swipeActionProps>>
+ WdSwitch: TypedGlobalComponent<ExtractPropTypes<typeof switchProps>>
+ WdTabbar: TypedGlobalComponent<ExtractPropTypes<typeof tabbarProps>>
+ WdTabbarItem: TypedGlobalComponent<ExtractPropTypes<typeof tabbarItemProps>>
+ WdTag: TypedGlobalComponent<ExtractPropTypes<typeof tagProps>>
+ WdToast: TypedGlobalComponent<ExtractPropTypes<typeof toastProps>>
+ }
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/tsconfig.json b/xiaozhi-esp32-server/main/manager-mobile/tsconfig.json
new file mode 100644
index 0000000..2de63b9
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/tsconfig.json
@@ -0,0 +1,41 @@
+{
+ "compilerOptions": {
+ "composite": true,
+ "lib": ["esnext", "dom"],
+ "baseUrl": ".",
+ "module": "ESNext",
+ "moduleResolution": "Node",
+ "paths": {
+ "@/*": ["./src/*"],
+ "@img/*": ["./src/static/*"]
+ },
+ "resolveJsonModule": true,
+ "types": [
+ "@dcloudio/types",
+ "@uni-helper/uni-types",
+ "@types/wechat-miniprogram",
+ "z-paging/types",
+ "./src/typings.d.ts"
+ ],
+ "allowJs": true,
+ "noImplicitThis": true,
+ "outDir": "dist",
+ "sourceMap": true,
+ "allowSyntheticDefaultImports": true,
+ "skipLibCheck": true
+ },
+ "vueCompilerOptions": {
+ "plugins": ["@uni-helper/uni-types/volar-plugin"]
+ },
+ "include": [
+ "src/**/*.ts",
+ "src/**/*.js",
+ "src/**/*.mjs",
+ "src/**/*.d.ts",
+ "src/**/*.tsx",
+ "src/**/*.jsx",
+ "src/**/*.vue",
+ "src/**/*.json"
+ ],
+ "exclude": ["node_modules"]
+}
diff --git a/xiaozhi-esp32-server/main/manager-mobile/uno.config.ts b/xiaozhi-esp32-server/main/manager-mobile/uno.config.ts
new file mode 100644
index 0000000..2ddbd83
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/uno.config.ts
@@ -0,0 +1,65 @@
+// https://www.npmjs.com/package/@uni-helper/unocss-preset-uni
+import { presetUni } from '@uni-helper/unocss-preset-uni'
+import {
+ defineConfig,
+ presetAttributify,
+ presetIcons,
+ transformerDirectives,
+ transformerVariantGroup,
+} from 'unocss'
+
+export default defineConfig({
+ presets: [
+ presetUni({
+ attributify: {
+ // prefix: 'fg-', // 濡傛灉鍔犲墠缂�锛屽垯闇�瑕佸湪浠g爜閲岄潰浣跨敤 `fg-` 鍓嶇紑锛屽锛�<div fg-border="1px solid #000"></div>
+ prefixedOnly: true,
+ },
+ }),
+ presetIcons({
+ scale: 1.2,
+ warn: true,
+ extraProperties: {
+ 'display': 'inline-block',
+ 'vertical-align': 'middle',
+ },
+ }),
+ // 鏀寔css class灞炴�у寲
+ presetAttributify(),
+ ],
+ transformers: [
+ // 鍚敤鎸囦护鍔熻兘锛氫富瑕佺敤浜庢敮鎸� @apply銆丂screen 鍜� theme() 绛� CSS 鎸囦护
+ transformerDirectives(),
+ // 鍚敤 () 鍒嗙粍鍔熻兘
+ // 鏀寔css class缁勫悎锛宔g: `<div class="hover:(bg-gray-400 font-medium) font-(light mono)">娴嬭瘯 unocss</div>`
+ transformerVariantGroup(),
+ ],
+ shortcuts: [
+ {
+ center: 'flex justify-center items-center',
+ },
+ ],
+ safelist: [],
+ rules: [
+ [
+ 'p-safe',
+ {
+ padding:
+ 'env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left)',
+ },
+ ],
+ ['pt-safe', { 'padding-top': 'env(safe-area-inset-top)' }],
+ ['pb-safe', { 'padding-bottom': 'env(safe-area-inset-bottom)' }],
+ ],
+ theme: {
+ colors: {
+ /** 涓婚鑹诧紝鐢ㄦ硶濡�: text-primary */
+ primary: 'var(--wot-color-theme,#0957DE)',
+ },
+ fontSize: {
+ /** 鎻愪緵鏇村皬鍙风殑瀛椾綋锛岀敤娉曞锛歵ext-2xs */
+ '2xs': ['20rpx', '28rpx'],
+ '3xs': ['18rpx', '26rpx'],
+ },
+ },
+})
diff --git a/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/1024x1024.png b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/1024x1024.png
new file mode 100644
index 0000000..2ceae9c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/1024x1024.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/120x120.png b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/120x120.png
new file mode 100644
index 0000000..3eb78f6
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/120x120.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/144x144.png b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/144x144.png
new file mode 100644
index 0000000..a103673
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/144x144.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/152x152.png b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/152x152.png
new file mode 100644
index 0000000..6829c93
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/152x152.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/167x167.png b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/167x167.png
new file mode 100644
index 0000000..5e9289b
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/167x167.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/180x180.png b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/180x180.png
new file mode 100644
index 0000000..607fc4a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/180x180.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/192x192.png b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/192x192.png
new file mode 100644
index 0000000..4a91273
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/192x192.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/20x20.png b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/20x20.png
new file mode 100644
index 0000000..f4b0e59
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/20x20.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/29x29.png b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/29x29.png
new file mode 100644
index 0000000..403fda9
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/29x29.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/40x40.png b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/40x40.png
new file mode 100644
index 0000000..88ae47c
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/40x40.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/58x58.png b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/58x58.png
new file mode 100644
index 0000000..889b9ff
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/58x58.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/60x60.png b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/60x60.png
new file mode 100644
index 0000000..6d0b41a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/60x60.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/72x72.png b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/72x72.png
new file mode 100644
index 0000000..6cf11e5
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/72x72.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/76x76.png b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/76x76.png
new file mode 100644
index 0000000..a1c11df
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/76x76.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/80x80.png b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/80x80.png
new file mode 100644
index 0000000..68b1891
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/80x80.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/87x87.png b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/87x87.png
new file mode 100644
index 0000000..6ac4e62
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/87x87.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/96x96.png b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/96x96.png
new file mode 100644
index 0000000..2644d32
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/unpackage/res/icons/96x96.png
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-mobile/vite.config.ts b/xiaozhi-esp32-server/main/manager-mobile/vite.config.ts
new file mode 100644
index 0000000..5175427
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-mobile/vite.config.ts
@@ -0,0 +1,182 @@
+import path from 'node:path'
+import process from 'node:process'
+import Uni from '@dcloudio/vite-plugin-uni'
+import Components from '@uni-helper/vite-plugin-uni-components'
+// @see https://uni-helper.js.org/vite-plugin-uni-layouts
+import UniLayouts from '@uni-helper/vite-plugin-uni-layouts'
+// @see https://github.com/uni-helper/vite-plugin-uni-manifest
+import UniManifest from '@uni-helper/vite-plugin-uni-manifest'
+// @see https://uni-helper.js.org/vite-plugin-uni-pages
+import UniPages from '@uni-helper/vite-plugin-uni-pages'
+// @see https://github.com/uni-helper/vite-plugin-uni-platform
+// 闇�瑕佷笌 @uni-helper/vite-plugin-uni-pages 鎻掍欢涓�璧蜂娇鐢�
+import UniPlatform from '@uni-helper/vite-plugin-uni-platform'
+/**
+ * 鍒嗗寘浼樺寲銆佹ā鍧楀紓姝ヨ法鍖呰皟鐢ㄣ�佺粍浠跺紓姝ヨ法鍖呭紩鐢�
+ * @see https://github.com/uni-ku/bundle-optimizer
+ */
+import Optimization from '@uni-ku/bundle-optimizer'
+import dayjs from 'dayjs'
+import { visualizer } from 'rollup-plugin-visualizer'
+import AutoImport from 'unplugin-auto-import/vite'
+import { defineConfig, loadEnv } from 'vite'
+import ViteRestart from 'vite-plugin-restart'
+
+// https://vitejs.dev/config/
+export default async ({ command, mode }) => {
+ // @see https://unocss.dev/
+ const UnoCSS = (await import('unocss/vite')).default
+ // console.log(mode === process.env.NODE_ENV) // true
+
+ // mode: 鍖哄垎鐢熶骇鐜杩樻槸寮�鍙戠幆澧�
+ console.log('command, mode -> ', command, mode)
+ // pnpm dev:h5 鏃跺緱鍒� => serve development
+ // pnpm build:h5 鏃跺緱鍒� => build production
+ // pnpm dev:mp-weixin 鏃跺緱鍒� => build development (娉ㄦ剰鍖哄埆锛宑ommand涓篵uild)
+ // pnpm build:mp-weixin 鏃跺緱鍒� => build production
+ // pnpm dev:app 鏃跺緱鍒� => build development (娉ㄦ剰鍖哄埆锛宑ommand涓篵uild)
+ // pnpm build:app 鏃跺緱鍒� => build production
+ // dev 鍜� build 鍛戒护鍙互鍒嗗埆浣跨敤 .env.development 鍜� .env.production 鐨勭幆澧冨彉閲�
+
+ const { UNI_PLATFORM } = process.env
+ console.log('UNI_PLATFORM -> ', UNI_PLATFORM) // 寰楀埌 mp-weixin, h5, app 绛�
+
+ const env = loadEnv(mode, path.resolve(process.cwd(), 'env'))
+ const {
+ VITE_APP_PORT,
+ VITE_SERVER_BASEURL,
+ VITE_DELETE_CONSOLE,
+ VITE_SHOW_SOURCEMAP,
+ VITE_APP_PUBLIC_BASE,
+ VITE_APP_PROXY,
+ VITE_APP_PROXY_PREFIX,
+ } = env
+ console.log('鐜鍙橀噺 env -> ', env)
+
+ return defineConfig({
+ envDir: './env', // 鑷畾涔塭nv鐩綍
+ base: VITE_APP_PUBLIC_BASE,
+ plugins: [
+ UniPages({
+ exclude: ['**/components/**/**.*'],
+ // homePage 閫氳繃 vue 鏂囦欢鐨� route-block 鐨則ype="home"鏉ヨ瀹�
+ // pages 鐩綍涓� src/pages锛屽垎鍖呯洰褰曚笉鑳介厤缃湪pages鐩綍涓�
+ subPackages: ['src/pages-sub'], // 鏄釜鏁扮粍锛屽彲浠ラ厤缃涓紝浣嗘槸涓嶈兘涓簆ages閲岄潰鐨勭洰褰�
+ dts: 'src/types/uni-pages.d.ts',
+ }),
+ UniLayouts(),
+ UniPlatform(),
+ UniManifest(),
+ // UniXXX 闇�瑕佸湪 Uni 涔嬪墠寮曞叆
+ {
+ // 涓存椂瑙e喅 dcloudio 瀹樻柟鐨� @dcloudio/uni-mp-compiler 鍑虹幇鐨勭紪璇� BUG
+ // 鍙傝�� github issue: https://github.com/dcloudio/uni-app/issues/4952
+ // 鑷畾涔夋彃浠剁鐢� vite:vue 鎻掍欢鐨� devToolsEnabled锛屽己鍒剁紪璇� vue 妯℃澘鏃� inline 涓� true
+ name: 'fix-vite-plugin-vue',
+ configResolved(config) {
+ const plugin = config.plugins.find(p => p.name === 'vite:vue')
+ if (plugin && plugin.api && plugin.api.options) {
+ plugin.api.options.devToolsEnabled = false
+ }
+ },
+ },
+ UnoCSS(),
+ AutoImport({
+ imports: ['vue', 'uni-app'],
+ dts: 'src/types/auto-import.d.ts',
+ dirs: ['src/hooks'], // 鑷姩瀵煎叆 hooks
+ vueTemplate: true, // default false
+ }),
+ // Optimization 鎻掍欢闇�瑕� page.json 鏂囦欢锛屾晠搴斿湪 UniPages 鎻掍欢涔嬪悗鎵ц
+ Optimization({
+ enable: {
+ 'optimization': true,
+ 'async-import': true,
+ 'async-component': true,
+ },
+ dts: {
+ base: 'src/types',
+ },
+ logger: false,
+ }),
+
+ ViteRestart({
+ // 閫氳繃杩欎釜鎻掍欢锛屽湪淇敼vite.config.js鏂囦欢鍒欎笉闇�瑕侀噸鏂拌繍琛屼篃鐢熸晥閰嶇疆
+ restart: ['vite.config.js'],
+ }),
+ // h5鐜澧炲姞 BUILD_TIME 鍜� BUILD_BRANCH
+ UNI_PLATFORM === 'h5' && {
+ name: 'html-transform',
+ transformIndexHtml(html) {
+ return html.replace('%BUILD_TIME%', dayjs().format('YYYY-MM-DD HH:mm:ss'))
+ },
+ },
+ // 鎵撳寘鍒嗘瀽鎻掍欢锛宧5 + 鐢熶骇鐜鎵嶅脊鍑�
+ UNI_PLATFORM === 'h5'
+ && mode === 'production'
+ && visualizer({
+ filename: './node_modules/.cache/visualizer/stats.html',
+ open: false,
+ gzipSize: true,
+ brotliSize: true,
+ }),
+ // 鍙湁鍦� app 骞冲彴鏃舵墠鍚敤 copyNativeRes 鎻掍欢
+ // UNI_PLATFORM === 'app' && copyNativeRes(),
+ Components({
+ extensions: ['vue'],
+ deep: true, // 鏄惁閫掑綊鎵弿瀛愮洰褰曪紝
+ directoryAsNamespace: false, // 鏄惁鎶婄洰褰曞悕浣滀负鍛藉悕绌洪棿鍓嶇紑锛宼rue 鏃剁粍浠跺悕涓� 鐩綍鍚�+缁勪欢鍚嶏紝
+ dts: 'src/types/components.d.ts', // 鑷姩鐢熸垚鐨勭粍浠剁被鍨嬪0鏄庢枃浠惰矾寰勶紙鐢ㄤ簬 TypeScript 鏀寔锛�
+ }),
+ Uni(),
+ ],
+ define: {
+ __UNI_PLATFORM__: JSON.stringify(UNI_PLATFORM),
+ __VITE_APP_PROXY__: JSON.stringify(VITE_APP_PROXY),
+ },
+ css: {
+ postcss: {
+ plugins: [
+ // autoprefixer({
+ // // 鎸囧畾鐩爣娴忚鍣�
+ // overrideBrowserslist: ['> 1%', 'last 2 versions'],
+ // }),
+ ],
+ },
+ },
+
+ resolve: {
+ alias: {
+ '@': path.join(process.cwd(), './src'),
+ '@img': path.join(process.cwd(), './src/static/images'),
+ },
+ },
+ server: {
+ host: '0.0.0.0',
+ hmr: true,
+ port: Number.parseInt(VITE_APP_PORT, 10),
+ // 浠� H5 绔敓鏁堬紝鍏朵粬绔笉鐢熸晥锛堝叾浠栫璧癰uild锛屼笉璧癲evServer)
+ proxy: JSON.parse(VITE_APP_PROXY)
+ ? {
+ [VITE_APP_PROXY_PREFIX]: {
+ target: VITE_SERVER_BASEURL,
+ changeOrigin: true,
+ rewrite: path => path.replace(new RegExp(`^${VITE_APP_PROXY_PREFIX}`), ''),
+ },
+ }
+ : undefined,
+ },
+ esbuild: {
+ drop: VITE_DELETE_CONSOLE === 'true' ? ['console', 'debugger'] : ['debugger'],
+ },
+ build: {
+ sourcemap: false,
+ // 鏂逛究闈瀐5绔皟璇�
+ // sourcemap: VITE_SHOW_SOURCEMAP === 'true', // 榛樿鏄痜alse
+ target: 'es6',
+ // 寮�鍙戠幆澧冧笉鐢ㄥ帇缂�
+ minify: mode === 'development' ? false : 'esbuild',
+
+ },
+ })
+}
diff --git a/xiaozhi-esp32-server/main/manager-web/.env.production b/xiaozhi-esp32-server/main/manager-web/.env.production
new file mode 100644
index 0000000..f10c154
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-web/.env.production
@@ -0,0 +1,4 @@
+VUE_APP_API_BASE_URL=/xiaozhi
+VUE_APP_PUBLIC_PATH=/
+# 鏄惁寮�鍚疌DN
+VUE_APP_USE_CDN=false
diff --git a/xiaozhi-esp32-server/main/manager-web/.npmrc b/xiaozhi-esp32-server/main/manager-web/.npmrc
new file mode 100644
index 0000000..5e4086a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-web/.npmrc
@@ -0,0 +1 @@
+registry=https://registry.npmmirror.com/
diff --git a/xiaozhi-esp32-server/main/manager-web/README.md b/xiaozhi-esp32-server/main/manager-web/README.md
new file mode 100644
index 0000000..d0a34ad
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-web/README.md
@@ -0,0 +1,27 @@
+鏈枃妗f槸寮�鍙戠被鏂囨。锛屽闇�閮ㄧ讲灏忔櫤鏈嶅姟绔紝[鐐瑰嚮杩欓噷鏌ョ湅閮ㄧ讲鏁欑▼](../../README.md#%E9%83%A8%E7%BD%B2%E6%96%87%E6%A1%A3)
+
+# xiaozhi
+
+## Project setup
+
+寮�鍙戜娇鐢ㄤ唬鐮佺紪杈戝櫒锛屽鍏ラ」鐩椂锛岄�夋嫨`manager-web`鏂囦欢澶逛綔涓洪」鐩洰褰�
+
+```
+npm install
+```
+
+### Compiles and hot-reloads for development
+
+```
+npm run serve
+```
+
+### Compiles and minifies for production
+
+```
+npm run build
+```
+
+### Customize configuration
+
+See [Configuration Reference](https://cli.vuejs.org/config/).
diff --git a/xiaozhi-esp32-server/main/manager-web/babel.config.js b/xiaozhi-esp32-server/main/manager-web/babel.config.js
new file mode 100644
index 0000000..b3469a7
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-web/babel.config.js
@@ -0,0 +1,12 @@
+module.exports = {
+ presets: [
+ ['@vue/cli-plugin-babel/preset', {
+ useBuiltIns: 'usage',
+ corejs: 3
+ }]
+ ],
+ plugins: [
+ '@babel/plugin-syntax-dynamic-import', // 纭繚鏀寔鍔ㄦ�佸鍏� (Lazy Loading)
+ '@babel/plugin-transform-runtime'
+ ]
+}
diff --git a/xiaozhi-esp32-server/main/manager-web/package.json b/xiaozhi-esp32-server/main/manager-web/package.json
new file mode 100644
index 0000000..0d0804d
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-web/package.json
@@ -0,0 +1,53 @@
+{
+ "name": "xiaozhi",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "serve": "vue-cli-service serve",
+ "check:i18n": "node scripts/check-i18n.js",
+ "test:snapshot": "node --test src/components/agentSnapshotDisplayUtils.test.mjs src/apis/module/agentSnapshotApi.test.mjs",
+ "build": "vue-cli-service build",
+ "test:unit": "node --test tests/*.test.mjs",
+ "analyze": "cross-env ANALYZE=true vue-cli-service build"
+ },
+ "dependencies": {
+ "core-js": "^3.41.0",
+ "cross-env": "^7.0.3",
+ "dotenv": "^16.5.0",
+ "element-ui": "^2.15.14",
+ "flyio": "^0.6.14",
+ "normalize.css": "^8.0.1",
+ "opus-decoder": "^0.7.7",
+ "opus-recorder": "^8.0.5",
+ "sm-crypto": "^0.3.13",
+ "vue": "^2.6.14",
+ "vue-axios": "^3.5.2",
+ "vue-i18n": "^8.28.2",
+ "vue-router": "^3.6.5",
+ "vuex": "^3.6.2",
+ "xiaozhi": "file:"
+ },
+ "devDependencies": {
+ "@babel/plugin-syntax-dynamic-import": "^7.8.3",
+ "@babel/plugin-transform-runtime": "^7.26.10",
+ "@vue/cli-plugin-router": "~5.0.0",
+ "@vue/cli-plugin-vuex": "~5.0.0",
+ "@vue/cli-service": "~5.0.0",
+ "compression-webpack-plugin": "^11.1.0",
+ "sass": "^1.32.7",
+ "sass-loader": "^12.0.0",
+ "vue-template-compiler": "^2.6.14",
+ "webpack-bundle-analyzer": "^4.10.2",
+ "workbox-webpack-plugin": "^7.3.0"
+ },
+ "browserslist": [
+ "> 1%",
+ "last 2 versions",
+ "not dead"
+ ],
+ "sideEffects": [
+ "*.css",
+ "*.scss",
+ "*.vue"
+ ]
+}
diff --git a/xiaozhi-esp32-server/main/manager-web/public/favicon.ico b/xiaozhi-esp32-server/main/manager-web/public/favicon.ico
new file mode 100644
index 0000000..5f285a5
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-web/public/favicon.ico
Binary files differ
diff --git a/xiaozhi-esp32-server/main/manager-web/public/generator/README.md b/xiaozhi-esp32-server/main/manager-web/public/generator/README.md
new file mode 100644
index 0000000..c4bc72a
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-web/public/generator/README.md
@@ -0,0 +1,89 @@
+# 璇煶鐩掑瓙涓婚鑷畾涔�
+
+## 椤圭洰姒傝堪
+
+鏈洰褰曞寘鍚粠 [xiaozhi-assets-generator](https://github.com/xinnan-tech/xiaozhi-assets-generator) 椤圭洰鎵撳寘鐨勯潤鎬佹枃浠讹紝鐢ㄤ簬璇煶鐩掑瓙涓婚鐨勫湪绾胯嚜瀹氫箟涓庣敓鎴愩�傜敤鎴峰彲浠ラ�氳繃姝ゅ伐鍏烽厤缃敜閱掕瘝銆佸瓧浣撱�佽〃鎯呭拰鑱婂ぉ鑳屾櫙绛夊厓绱狅紝骞跺鍑轰负 `assets.bin` 鏂囦欢銆�
+
+## 鐩綍缁撴瀯
+
+```
+generator/
+鈹溾攢鈹� assets/ # 鏋勫缓鐢熸垚鐨勮祫婧愭枃浠�
+鈹� 鈹溾攢鈹� ft_render-ByO_jG18.js
+鈹� 鈹溾攢鈹� index-CYcyz9xb.js
+鈹� 鈹斺攢鈹� index-NXxBVrod.css
+鈹溾攢鈹� static/ # 闈欐�佽祫婧愮洰褰�
+鈹� 鈹溾攢鈹� charsets/ # 瀛楃闆嗘枃浠�
+鈹� 鈹� 鈹溾攢鈹� deepseek.txt
+鈹� 鈹� 鈹溾攢鈹� gb2312.txt
+鈹� 鈹� 鈹溾攢鈹� latin1.txt
+鈹� 鈹� 鈹斺攢鈹� qwen18409.txt
+鈹� 鈹溾攢鈹� fonts/ # 瀛椾綋璧勬簮
+鈹� 鈹� 鈹溾攢鈹� font_noto_qwen_14_1.bin
+鈹� 鈹� 鈹溾攢鈹� font_noto_qwen_16_4.bin
+鈹� 鈹� 鈹溾攢鈹� font_noto_qwen_20_4.bin
+鈹� 鈹� 鈹溾攢鈹� font_noto_qwen_30_4.bin
+鈹� 鈹� 鈹溾攢鈹� font_puhui_deepseek_14_1.bin
+鈹� 鈹� 鈹溾攢鈹� font_puhui_deepseek_16_4.bin
+鈹� 鈹� 鈹溾攢鈹� font_puhui_deepseek_20_4.bin
+鈹� 鈹� 鈹溾攢鈹� font_puhui_deepseek_30_4.bin
+鈹� 鈹� 鈹溾攢鈹� noto_qwen.ttf
+鈹� 鈹� 鈹斺攢鈹� puhui_deepseek.ttf
+鈹� 鈹溾攢鈹� multinet_model/ # 鑷畾涔夊敜閱掕瘝妯″瀷
+鈹� 鈹� 鈹溾攢鈹� fst/
+鈹� 鈹� 鈹溾攢鈹� mn6_cn/
+鈹� 鈹� 鈹溾攢鈹� mn6_en/
+鈹� 鈹� 鈹溾攢鈹� mn7_cn/
+鈹� 鈹� 鈹斺攢鈹� mn7_en/
+鈹� 鈹溾攢鈹� twemoji32/ # 32x32 琛ㄦ儏鍥剧墖
+鈹� 鈹溾攢鈹� twemoji64/ # 64x64 琛ㄦ儏鍥剧墖
+鈹� 鈹溾攢鈹� wakenet_model/ # 棰勮鍞ら啋璇嶆ā鍨�
+鈹� 鈹斺攢鈹� README.md # 闈欐�佽祫婧愯鏄�
+鈹溾攢鈹� index.html # 涓婚〉闈�
+鈹斺攢鈹� README.md # 椤圭洰璇存槑鏂囨。
+```
+
+## 涓昏鍔熻兘
+
+### 1. 鑺墖涓庡睆骞曢厤缃�
+- 鏀寔澶氱鑺墖鍨嬪彿锛欵SP32-S3銆丒SP32-C3銆丒SP32-P4銆丒SP32-C6
+- 鐏垫椿鐨勫睆骞曞垎杈ㄧ巼璁剧疆
+- 鏀寔 RGB565 棰滆壊鏍煎紡
+
+### 2. 鍞ら啋璇嶉厤缃�
+- **棰勮鍞ら啋璇�**锛氬熀浜庝笉鍚岃姱鐗囨敮鎸佺殑 WakeNet 妯″瀷
+- **鑷畾涔夊敜閱掕瘝**锛氭敮鎸佷腑鏂囧拰鑻辨枃鍛戒护璇嶏紝鍙厤缃槇鍊煎拰瓒呮椂鏃堕棿
+
+### 3. 瀛椾綋閰嶇疆
+- 棰勮澶氱瀛椾綋锛氶樋閲屽反宸存櫘鎯犱綋銆丯oto Qwen 绛�
+- 鏀寔涓婁紶鑷畾涔� TTF/WOFF 瀛椾綋鏂囦欢
+- 鍙厤缃瓧鍙峰拰棰滆壊娣卞害锛坆pp锛�
+
+### 4. 琛ㄦ儏闆嗗悎
+- 鎻愪緵 21 绉嶅熀纭�琛ㄦ儏鐨勯璁炬柟妗堬紙32x32 鍜� 64x64 涓ょ灏哄锛�
+- 鏀寔鑷畾涔夎〃鎯呬笂浼�
+
+### 5. 鑱婂ぉ鑳屾櫙
+- 鏀寔娴呰壊/娣辫壊妯″紡鍒囨崲
+- 鍙厤缃函鑹茶儗鏅垨鍥剧墖鑳屾櫙
+- 鑷姩閫傞厤灞忓箷鍒嗚鲸鐜�
+
+## 浣跨敤鏂规硶
+
+1. 浠ユ湇鍔℃柟寮忓惎鍔� `index.html` 鏂囦欢
+2. 閫夋嫨鑺墖鍨嬪彿鍜屽睆骞曢厤缃�
+3. 閫氳繃涓嶅悓鏍囩椤甸厤缃富棰樺厓绱�
+4. 鐐瑰嚮鐢熸垚鎸夐挳鏌ョ湅璧勬簮娓呭崟
+5. 纭鍚庣敓鎴愬苟涓嬭浇 `assets.bin` 鏂囦欢
+
+## 鎶�鏈鏄�
+
+- 鏋勫缓鍚庣殑闈欐�佽祫婧愪綅浜� `assets/` 鐩綍
+- 鍘熷妯″瀷鍜岃祫婧愭枃浠朵綅浜� `static/` 鐩綍
+- 鏀寔绂荤嚎浣跨敤锛屾棤闇�棰濆渚濊禆
+
+## 娉ㄦ剰浜嬮」
+
+- 鏈伐鍏蜂负绂荤嚎浣跨敤璁捐锛屾墍鏈夎祫婧愬凡鍖呭惈鍦ㄧ洰褰曚腑
+- 鐢熸垚鐨� `assets.bin` 鏂囦欢闇�瑕佷笌璇煶鐩掑瓙纭欢閰嶅悎浣跨敤
+- 鑷畾涔夎祫婧愰渶娉ㄦ剰鏂囦欢鏍煎紡鍜屽ぇ灏忛檺鍒讹紝浠ョ‘淇濆吋瀹�
\ No newline at end of file
diff --git a/xiaozhi-esp32-server/main/manager-web/public/generator/assets/ft_render-ByO_jG18.js b/xiaozhi-esp32-server/main/manager-web/public/generator/assets/ft_render-ByO_jG18.js
new file mode 100644
index 0000000..f803bf8
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-web/public/generator/assets/ft_render-ByO_jG18.js
@@ -0,0 +1,5 @@
+var Module=(()=>{var _scriptDir=typeof document<"u"&&document.currentScript?document.currentScript.src:void 0;return typeof __filename<"u"&&(_scriptDir=_scriptDir||__filename),function(Module){Module=Module||{};var Module=typeof Module<"u"?Module:{},objAssign=Object.assign,readyPromiseResolve,readyPromiseReject;Module.ready=new Promise(function(A,I){readyPromiseResolve=A,readyPromiseReject=I});var moduleOverrides=objAssign({},Module),thisProgram="./this.program",ENVIRONMENT_IS_WEB=typeof window=="object",ENVIRONMENT_IS_WORKER=typeof importScripts=="function",ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string",scriptDirectory="";function locateFile(A){return Module.locateFile?Module.locateFile(A,scriptDirectory):scriptDirectory+A}var read_,readAsync,readBinary,fs,nodePath,requireNodeFS;ENVIRONMENT_IS_NODE?(ENVIRONMENT_IS_WORKER?scriptDirectory=require("path").dirname(scriptDirectory)+"/":scriptDirectory=__dirname+"/",requireNodeFS=function(){nodePath||(fs=require("fs"),nodePath=require("path"))},read_=function(I,g){var C=tryParseAsDataURI(I);return C?g?C:C.toString():(requireNodeFS(),I=nodePath.normalize(I),fs.readFileSync(I,g?null:"utf8"))},readBinary=function(I){var g=read_(I,!0);return g.buffer||(g=new Uint8Array(g)),g},readAsync=function(I,g,C){var Q=tryParseAsDataURI(I);Q&&g(Q),requireNodeFS(),I=nodePath.normalize(I),fs.readFile(I,function(E,B){E?C(E):g(B.buffer)})},process.argv.length>1&&(thisProgram=process.argv[1].replace(/\\/g,"/")),process.argv.slice(2),Module.inspect=function(){return"[Emscripten Module object]"}):(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&(ENVIRONMENT_IS_WORKER?scriptDirectory=self.location.href:typeof document<"u"&&document.currentScript&&(scriptDirectory=document.currentScript.src),_scriptDir&&(scriptDirectory=_scriptDir),scriptDirectory.indexOf("blob:")!==0?scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1):scriptDirectory="",read_=function(A){try{var I=new XMLHttpRequest;return I.open("GET",A,!1),I.send(null),I.responseText}catch(C){var g=tryParseAsDataURI(A);if(g)return intArrayToString(g);throw C}},ENVIRONMENT_IS_WORKER&&(readBinary=function(A){try{var I=new XMLHttpRequest;return I.open("GET",A,!1),I.responseType="arraybuffer",I.send(null),new Uint8Array(I.response)}catch(C){var g=tryParseAsDataURI(A);if(g)return g;throw C}}),readAsync=function(A,I,g){var C=new XMLHttpRequest;C.open("GET",A,!0),C.responseType="arraybuffer",C.onload=function(){if(C.status==200||C.status==0&&C.response){I(C.response);return}var Q=tryParseAsDataURI(A);if(Q){I(Q.buffer);return}g()},C.onerror=g,C.send(null)}),Module.print||console.log.bind(console);var err=Module.printErr||console.warn.bind(console);objAssign(Module,moduleOverrides),moduleOverrides=null,Module.arguments&&Module.arguments,Module.thisProgram&&(thisProgram=Module.thisProgram),Module.quit&&Module.quit;var tempRet0=0,setTempRet0=function(A){tempRet0=A},getTempRet0=function(){return tempRet0},wasmBinary;Module.wasmBinary&&(wasmBinary=Module.wasmBinary),Module.noExitRuntime,typeof WebAssembly!="object"&&abort("no native wasm support detected");function getValue(A,I,g){switch(I=I||"i8",I.charAt(I.length-1)==="*"&&(I="i32"),I){case"i1":return HEAP8[A>>0];case"i8":return HEAP8[A>>0];case"i16":return HEAP16[A>>1];case"i32":return HEAP32[A>>2];case"i64":return HEAP32[A>>2];case"float":return HEAPF32[A>>2];case"double":return Number(HEAPF64[A>>3]);default:abort("invalid type for getValue: "+I)}return null}var wasmMemory,ABORT=!1;function getCFunc(A){var I=Module["_"+A];return I}function ccall(A,I,g,C,Q){var E={string:function(h){var G=0;if(h!=null&&h!==0){var s=(h.length<<2)+1;G=stackAlloc(s),stringToUTF8(h,G,s)}return G},array:function(h){var G=stackAlloc(h.length);return writeArrayToMemory(h,G),G}};function B(h){return I==="string"?UTF8ToString(h):I==="boolean"?!!h:h}var D=getCFunc(A),i=[],o=0;if(C)for(var w=0;w<C.length;w++){var F=E[g[w]];F?(o===0&&(o=stackSave()),i[w]=F(C[w])):i[w]=C[w]}var y=D.apply(null,i);function N(h){return o!==0&&stackRestore(o),B(h)}return y=N(y),y}function cwrap(A,I,g,C){g=g||[];var Q=g.every(function(B){return B==="number"}),E=I!=="string";return E&&Q&&!C?getCFunc(A):function(){return ccall(A,I,g,arguments)}}var UTF8Decoder=typeof TextDecoder<"u"?new TextDecoder("utf8"):void 0;function UTF8ArrayToString(A,I,g){for(var C=I+g,Q=I;A[Q]&&!(Q>=C);)++Q;if(Q-I>16&&A.subarray&&UTF8Decoder)return UTF8Decoder.decode(A.subarray(I,Q));for(var E="";I<Q;){var B=A[I++];if(!(B&128)){E+=String.fromCharCode(B);continue}var D=A[I++]&63;if((B&224)==192){E+=String.fromCharCode((B&31)<<6|D);continue}var i=A[I++]&63;if((B&240)==224?B=(B&15)<<12|D<<6|i:B=(B&7)<<18|D<<12|i<<6|A[I++]&63,B<65536)E+=String.fromCharCode(B);else{var o=B-65536;E+=String.fromCharCode(55296|o>>10,56320|o&1023)}}return E}function UTF8ToString(A,I){return A?UTF8ArrayToString(HEAPU8,A,I):""}function stringToUTF8Array(A,I,g,C){if(!(C>0))return 0;for(var Q=g,E=g+C-1,B=0;B<A.length;++B){var D=A.charCodeAt(B);if(D>=55296&&D<=57343){var i=A.charCodeAt(++B);D=65536+((D&1023)<<10)|i&1023}if(D<=127){if(g>=E)break;I[g++]=D}else if(D<=2047){if(g+1>=E)break;I[g++]=192|D>>6,I[g++]=128|D&63}else if(D<=65535){if(g+2>=E)break;I[g++]=224|D>>12,I[g++]=128|D>>6&63,I[g++]=128|D&63}else{if(g+3>=E)break;I[g++]=240|D>>18,I[g++]=128|D>>12&63,I[g++]=128|D>>6&63,I[g++]=128|D&63}}return I[g]=0,g-Q}function stringToUTF8(A,I,g){return stringToUTF8Array(A,HEAPU8,I,g)}function lengthBytesUTF8(A){for(var I=0,g=0;g<A.length;++g){var C=A.charCodeAt(g);C>=55296&&C<=57343&&(C=65536+((C&1023)<<10)|A.charCodeAt(++g)&1023),C<=127?++I:C<=2047?I+=2:C<=65535?I+=3:I+=4}return I}var UTF16Decoder=typeof TextDecoder<"u"?new TextDecoder("utf-16le"):void 0;function UTF16ToString(A,I){for(var g=A,C=g>>1,Q=C+I/2;!(C>=Q)&&HEAPU16[C];)++C;if(g=C<<1,g-A>32&&UTF16Decoder)return UTF16Decoder.decode(HEAPU8.subarray(A,g));for(var E="",B=0;!(B>=I/2);++B){var D=HEAP16[A+B*2>>1];if(D==0)break;E+=String.fromCharCode(D)}return E}function stringToUTF16(A,I,g){if(g===void 0&&(g=2147483647),g<2)return 0;g-=2;for(var C=I,Q=g<A.length*2?g/2:A.length,E=0;E<Q;++E){var B=A.charCodeAt(E);HEAP16[I>>1]=B,I+=2}return HEAP16[I>>1]=0,I-C}function lengthBytesUTF16(A){return A.length*2}function UTF32ToString(A,I){for(var g=0,C="";!(g>=I/4);){var Q=HEAP32[A+g*4>>2];if(Q==0)break;if(++g,Q>=65536){var E=Q-65536;C+=String.fromCharCode(55296|E>>10,56320|E&1023)}else C+=String.fromCharCode(Q)}return C}function stringToUTF32(A,I,g){if(g===void 0&&(g=2147483647),g<4)return 0;for(var C=I,Q=C+g-4,E=0;E<A.length;++E){var B=A.charCodeAt(E);if(B>=55296&&B<=57343){var D=A.charCodeAt(++E);B=65536+((B&1023)<<10)|D&1023}if(HEAP32[I>>2]=B,I+=4,I+4>Q)break}return HEAP32[I>>2]=0,I-C}function lengthBytesUTF32(A){for(var I=0,g=0;g<A.length;++g){var C=A.charCodeAt(g);C>=55296&&C<=57343&&++g,I+=4}return I}function writeArrayToMemory(A,I){HEAP8.set(A,I)}function writeAsciiToMemory(A,I,g){for(var C=0;C<A.length;++C)HEAP8[I++>>0]=A.charCodeAt(C);HEAP8[I>>0]=0}function alignUp(A,I){return A%I>0&&(A+=I-A%I),A}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(A){buffer=A,Module.HEAP8=HEAP8=new Int8Array(A),Module.HEAP16=HEAP16=new Int16Array(A),Module.HEAP32=HEAP32=new Int32Array(A),Module.HEAPU8=HEAPU8=new Uint8Array(A),Module.HEAPU16=HEAPU16=new Uint16Array(A),Module.HEAPU32=HEAPU32=new Uint32Array(A),Module.HEAPF32=HEAPF32=new Float32Array(A),Module.HEAPF64=HEAPF64=new Float64Array(A)}Module.INITIAL_MEMORY;var wasmTable,__ATPRERUN__=[],__ATINIT__=[],__ATPOSTRUN__=[];function preRun(){if(Module.preRun)for(typeof Module.preRun=="function"&&(Module.preRun=[Module.preRun]);Module.preRun.length;)addOnPreRun(Module.preRun.shift());callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){callRuntimeCallbacks(__ATINIT__)}function postRun(){if(Module.postRun)for(typeof Module.postRun=="function"&&(Module.postRun=[Module.postRun]);Module.postRun.length;)addOnPostRun(Module.postRun.shift());callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(A){__ATPRERUN__.unshift(A)}function addOnInit(A){__ATINIT__.unshift(A)}function addOnPostRun(A){__ATPOSTRUN__.unshift(A)}var runDependencies=0,dependenciesFulfilled=null;function addRunDependency(A){runDependencies++,Module.monitorRunDependencies&&Module.monitorRunDependencies(runDependencies)}function removeRunDependency(A){if(runDependencies--,Module.monitorRunDependencies&&Module.monitorRunDependencies(runDependencies),runDependencies==0&&dependenciesFulfilled){var I=dependenciesFulfilled;dependenciesFulfilled=null,I()}}Module.preloadedImages={},Module.preloadedAudios={};function abort(A){Module.onAbort&&Module.onAbort(A),A="Aborted("+A+")",err(A),ABORT=!0,A+=". Build with -s ASSERTIONS=1 for more info.";var I=new WebAssembly.RuntimeError(A);throw readyPromiseReject(I),I}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(A){return A.startsWith(dataURIPrefix)}function isFileURI(A){return A.startsWith("file://")}var wasmBinaryFile;wasmBinaryFile="data:application/octet-stream;base64,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KAKQWyAAKAKUWyAAKAKYWyAAKAKcW0EAEGoLIAAgAjYCyFsgACABNgLEWwsgA0EwaiQAC4ABACAAEMgBIABB2NsAaiACNgIAIAAgATYCxFsgACABNgLUWyAAQcjbAGogAjYCACAAQQE6AI9bIAAoAphbIQECQCAAQQhqIgItAAwEQCABLQAFRQ0BCyACIAAoApBbIAAoApRbIAEgACgCnFtBABBqCyAAQaQeaiACQZwe/AoAAAseACAAQgA3AgQgACABNgIAIABCADcCFCAAQgA3AgwLjQEBBX8gAC0AACEEAkADQCAEIAJBAnRBsNUCaigCACIDLQAARgRAIAJByQAgAkHJAEobIQYDQCADIAAgARBWRQRAIAIPC0HLACEFIAIgBkYNAyACQQFqIgJBAnRBsNUCaigCACIDLQAAIARGDQALDAILIAJByQBJIQMgAkEBaiECIAMNAAtBywAhBQsgBQtRAQR/IABFIAFFckUEQCABKAIIIQIgASgCDCEDIAAgACgCACIEIAEoAgAQFSAAKAIEIgUgASgCBBAVajYCACAAIAQgAhAVIAUgAxAVajYCBAsLqwMBBH8jAEEQayIDJAAgAUIANwIAIAFBADYCCCAAEEQgAyAAKAIAIgI2AgwgACgCCCIFIAJLBEACQAJAAkACQAJAAkACQCACLQAAIgRB+wBHBEAgBEHbAEYNASAEQShHDQIgASACNgIAIAFBAjYCCCADQQxqIAUQzgENAwwECyABIAI2AgAgAUEDNgIIIANBDGogBRDRAkUNAwwCCyABIAI2AgAgAUEDNgIIQQEhBCAAIAJBAWo2AgAgABBEAkAgACgCACICIAVPDQADQCAAKAIMDQECQAJAAkAgAi0AAEHbAGsOAwECAAILIARBAUoEQCAEQQFrIQQMAgsgAyACQQFqIgI2AgwgASACNgIEDAkLIARBAWohBAsgABDPASAAEEQgACgCACICIAVJDQALCyADIAI2AgwMAQsgASACNgIAIAFBBEEBIAItAABBL0YbNgIIIAAQzwEgAyAAKAIAIgI2AgwgACgCDEUNAgsgASgCBCECDAILIAMoAgwhAgsgASACNgIECyACDQAgAUEANgIIIAFBADYCAAsgACADKAIMNgIACyADQRBqJAALhAEBBX8jAEEQayICJAAgAiAAKAIAIgQ2AgwgAkEMaiAB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QYGChAhrcUGAgYKEeHENASAEIAU2AgAgBEEEaiEEIAFBBGohASADQQRrIgNBA0sNAAsLIANFDQADQCAEIAEtAAAiBToAACAFRQ0CIARBAWohBCABQQFqIQEgA0EBayIDDQALC0EAIQMLIARBACADEHwgACAIakEAOgAAIAdBAWohAQwBCyAAQQA6AAALIAAgAhDZAiABENkCIQMLIAZBEGokACADCxkAIAAgARA5IgBFBEBBAA8LIAAoAgAoAhQLNgECfwJAIABFDQAgACgCXCIDRQ0AIAMgASADKAIMKAIMEQAAIgFBACABIAAoAhBJGyECCyACC3UBBX8gAARAIAAgAC4BOCICIAAvARRqOwEUIAAgAC8BFiIDIAAvATpqOwEWIAAgACgCMCAAKAJUajYCMCACQQBKBEAgACgCRCEEA0AgBCABQQF0aiIFIAUvAQAgA2o7AQAgAUEBaiIBIAJHDQALCyAAEKECCwsKACAAKAIAQQBHCw0AIAAtAABBCnFBAEcLtQIBBH8gAEIANwIAIABBADYCECAAQgA3AggCfwJAIAEgAhBbIggoAggiASAIKAIEIglrIgpBgICwf0cEQCAKQYCArH9HDQEgBkUEQEEBDAMLIAAgATYCCEEBIQdBAAwCC0EBIAYNARogACAJNgIIQQIhB0EADAELAkACQCAKQQBIBEAgBgRAIAAgATYCCAwDCyAAIAk2AggMAQsgBgRAIAAgCTYCCAwCCyAAIAE2AggLQQghB0EADAELQQQhB0EACyEBIAAgBzYCACAAKAIIIQcgABCGAQRAIAMoAugBQQF0IAdqIQcLIAAgBTYCECAAIAI2AgQgACAEIAdqIgI2AggCQCABDQAgCC0AAEUNACAAIAhBEEEMIAAQhgEbaigCADYCDCAAEMMBDwsgACACIAUQFTYCDAsjACAAQQA2AhQgAEIANwIMIAAoAgAgACgCGBAUIABBADYCGAs5AQF/IABBAEGcHvwLACABLQC4ASEFIAAgBDYCECAAIAU6AA0gACADNgIIIAAgAjYCBCAAIAE2AgALNgAgABAoIAFJBEAgACgCBEGCARAxDwsgAUEDdCIBIAAoAghqIAI2AgAgACgCCCABakEANgIEC70DAQd/IwBBQGoiByQAIAcgASgCACIJNgIAIAcgAigCADYCBEEKQQkgBC0ACSIKGyELA0AgCEECdCAHaiIMQQhqIAk2AgAgBCAIai0AAARAIAwgACAGEBsgCWo2AgggBkEBaiEGCyALIAhBAWoiCEcEQCAHIAhBAnRqKAIAIQkMAQsLIApFBEAgByACKAIANgIsCyAHAn8CQCAFBEAgAigCACEJIAcoAiwhBCABKAIAIQogBygCKCEFIAAgBhAbIQggBSAKayIGIAZBH3UiBmogBnMgBCAJayIGIAZBH3UiBmogBnNKBEAgByAFIAhqIgU2AjAMAgsgByABKAIAIgU2AjAgBCAIagwCCyAHAn8gBC0ACgRAIAZBAWohCCAHKAIoIAAgBhAbagwBCyAGIQggASgCAAsiBTYCMCAELQALRQ0AIAcoAiwgACAIEBtqDAELIAIoAgALIgY2AjRBACEIQQEhCQNAIAMgCEECdCAHaiIEKAIIIAQoAgwgBCgCECAEKAIUIAQoAhggBCgCHBBQIAlBAXEhBEEGIQhBACEJIAQNAAsgABBcIAEgBTYCACACIAY2AgAgB0FAayQAC7gBAQN/IwBBIGsiBiQAIAEQKCIIQQFxIQcCQCAALQAIIAdFcg0AIAQtAAANACADIAFBABAbIAAoArABELgCajYCAAsgACgCsAEtAKQERQRAIAhBAk8EQANAIAYgASAHEBsgBWoiADYCDCABIAdBAWoQGyEDIAZCADcCFCAGQQA6AAggBiAAIANqIgU2AhAgAiAGQQhqEK4CIAdBAmoiByAISQ0ACwsgARBcCyAEQQE6AAAgBkEgaiQACyUAIABCADcCDCAAIAM2AgggACACNgIEIAAgATYCACAAQgA3AhQLWgEDfyAAKAIUIQQgAC0ALQRAIAQoAgghACAEKAIEIAQuAQIiBUEDdGoiBiACQQp1NgIEIAYgAUEKdTYCACAAIAVqQQFBAiADGzoAAAsgBCAELwECQQFqOwECCysBAX8gACgCDCIALgEWIAFqIAAuATpqIAAoAgRLBH8gACABQQAQRQUgAgsLGgAgAEENdCAAcyIAQRF2IABzIgBBBXQgAHML6wcBDH8jAEEQayIEJABBASEDIAAoAkhB7OjV+wZGBEAgAEHsAGohAyACBH8gAigCBCEFIAIoAgAFQQALIQIgAyAEEGYgBCAFQT9xIgMgBCgCDCIGQT9xaiIINgIMIAQgAkE/cSIJIAQoAggiB0E/cWoiDDYCCCAEIAMgBCgCBCIKQT9xaiINNgIEIAQgBCgCACIDQT9xIAlqIg42AgAgBUEGdSIFIAZBBnVqIQkgAkEGdSICIAdBBnVqIQsgCkEGdSAFaiEHIANBBnUgAmohCkECIQNBBSECAkACfwJAAkACQAJAAkAgAUECaw4DAAIBAwsCfyAOQR9qIgJBBnYgCmoiASAMQSBqIgNBBnYgC2oiBkYEQCACQT9xIANBP3FqQR9rQR9MBEAgASEGIAFBAWsMAgsgAUEBaiEGCyABCyEBIAcgDUEfaiIFQQZ2aiIDIAkgCEEgaiIHQQZ2aiICRw0DIAVBP3EgB0E/cWpBH2tBH0wEQCADIgJBAWsMBQsgA0EBaiECDAMLQQQhAUEGIQILIAAoAgAhAwJAAkACQCABQQNrDgIAAQILIAQgBCgCACADKAK0ASIBIAMoArwBIgUgASAFShsiCCADKALEASIGIAYgCEgbazYCACAEIAQoAgggASAFIAEgBUgbIgEgBiABIAZIG2s2AgggBCAEKAIEIAMoArgBIgEgAygCwAEiBSABIAVKGyIGIAMoAsgBIgMgAyAGSBtrNgIEIAQgBCgCDCABIAUgASAFSBsiASADIAEgA0gbazYCDAwBCyAEIAQoAgAgAygCuAEiASADKALAASIFIAEgBUobIgggAygCyAEiBiAGIAhIG2s2AgAgBCAEKAIIIAEgBSABIAVIGyIBIAYgASAGSBtrNgIIIAQgBCgCBCADKAK0ASIBIAMoArwBIgUgASAFSBsiBiADKALEASIDIAMgBkobajYCBCAEIAQoAgwgASAFIAEgBUobIgEgAyABIANKG2o2AgwLIAIhAwsgBCgCDEE/akEGdSAJaiICIAQoAgRBBnUgB2oiCGshByAEKAIIQT9qQQZ1IAtqIgYgBCgCAEEGdSAKaiIBayIFIQkCQAJAIANBBWsOAgABBAsgBUEDbCIFQQNqQXxxIQlBBSEDDAMLIAdBA2whB0EGIQMgBSEJDAILIAMLIQggAiAIayEHIAYgAWsiBUEPakEDdUF+cSEJQQEhAwsgACACNgJoIAAgATYCZCAAIAc2AkwgACADOgBeIABBgAI7AVwgACAFNgJQIAAgCTYCVCABQYCAfkggBkH//wFKciAIQYCAfkhyIAJB//8BSnIhAwsgBEEQaiQAIAMLQQECfyMAQRBrIgIkACACQQM2AghBoAEhAyAAIAJBCGpBARBRQQFGBEAgASACKAIMNgIAQQAhAwsgAkEQaiQAIAML9wEBBX8CQAJAIAAoAhQiAEUNAAJAAkAgAC8BACIBwSIDQQJOBEAgACgCDCABQQF0akEEay4BAEEBaiECDAELIAMNACAALgECIQEMAQsgAiAALgECIgFHDQAMAgsCQCABQQJIDQAgACgCBCIEIAJBA3RqIgUoAgAgBCABQQN0akEIayIEKAIARw0AIAUoAgQgBCgCBEcNACAAKAIIIAFqQQFrLQAAQQFHDQAgACABQQFrIgE7AQILIANBAEwNACACIAHBQQFrIgJGBEAgACABQQFrOwECDAILIAAoAgwgA0EBdGpBAmsgAjsBAAsPCyAAIANBAWs7AQALMwEBfwJAIAFFDQAgACgCDCIALgEWIAFqIAAuATpqIAAoAgRNDQAgACABQQAQRSECCyACCzEBAX8gACgCCCIBBEAgASAAKAIQIgApAgA3AmwgASAAKAIQNgJ8IAEgACkCCDcCdAsLvAEBBH8CQCAAKAIAIgFFDQADQCABIAEoAggiAygCADYCACABIAEoAhBBAWs2AhAgASADQQRBfCABLwEMQQhxG2o2AgggASgCBCIBDQALIAAoAgAiAUUNACABKAIEIgJFDQAgAUEEaiEEIAAhAwNAAn8gASgCACACKAIATARAIAIhASAEDAELIAMgAjYCACAEIAIoAgQ2AgAgAiABNgIEIAAoAgAhASAACyEDIAFBBGohBCABKAIEIgINAAsLCwsAIAAoAgAgABAUCwQAQQALPAEDfyMAQRBrIgIkACAAQQQgAkEMahAiIQMgAigCDCIERQRAIAMgADYCAAsgASADNgIAIAJBEGokACAEC94BAQN/IAAgBCAFEOEBIgQEfyAEBSMAQRBrIgQkAAJAIABBDGogBSAEQQxqEOYCIgYNACAEKAIMIgAgAyAFEOABIgYNACAAIAM2AgBBACEGIANFDQAgASACQQN2aiEFQYABIAJBB3F2IQEgACgCCCECQYABIQADQCACIAAgAi0AACIHciAHIABBf3NxIAEgBS0AAHEbOgAAQYABIABBAXUgAEECSSIHGyEAQYABIAFBAXUgAUECSSIIGyEBIAIgB2ohAiAFIAhqIQUgA0EBayIDDQALCyAEQRBqJAAgBgsLUAEB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IAAoAgBBAWoiAjYCDAJAIAEgAk0NAANAIANBDGogARBSIAMoAgwiAiABTw0BIAItAAAiBEEwa0H/AXFBCU0gBEHBAGtBBklyRQRAIARB4QBrQQZPDQILIAMgAkEBaiICNgIMIAEgAksNAAsLAn8gASACSwRAQQMgAi0AAEE+Rw0BGgsgAkEBaiECQQALIQEgACACNgIAIANBEGokACABC+IBAQV/IwBBEGsiAiQAIAIgACgCACIDNgIMAn8CQAJAIAEgA00NAANAQQAhBQJAAkACQAJAIAMtAAAiBkElaw4EAgMDAQALAkACQAJAIAZB+wBrDgMBBQIACyAGQTxHDQQgAkEMaiABENACIQUMBAsgBEEBaiEEDAMLIARBAWsiBA0CIANBAWohAwwECyACQQxqIAEQzgEhBQwBCyACQQxqIAEQvAILIAIgAigCDEEBaiIDNgIMQQAgASADSyAFGw0AC0EDIAQNAhoMAQsLIAULIQEgACADNgIAIAJBEGokACABC00BAn8gACgCECICQQBKBEAgACgCACABayEBIAAoAhgiACACQQJ0aiECA0AgACgCACIDBEAgACABIANqNgIACyAAQQRqIgAgAkkNAAsLC1wBAn8jAEEQayICJAAgAkEANgIMQQYhAwJAIABFIAFFcg0AIABBECACQQxqECQhACACKAIMIgMNACAAQgA3AgAgAEIANwIIIAEgADYCAEEAIQMLIAJBEGokACADC4QBAQR/IwBBEGsiBCQAAkAgAEUgAUVyDQAgASgCACICRQ0AIAAgAigCCCIDBH8DQCAEIAM2AgwgAygCJCEDAkAgAEUgBEEMaiICRXINACACKAIAIgVFDQAgACAFEBQgAkEANgIACyADDQALIAEoAgAFIAILEBQgAUEANgIACyAEQRBqJAALTAEEf0GAgICABCEBA0AgAiABIAJqIgMgAWogACADSSIEGyECIABBACADIAQba0EBdCEAIAFBgQFLIQMgAUEBdiEBIAMNAAsgAkEIdgtfAQF/QQBBgAFB/wAgACABEBYiACAAQR91IgFqIAFzIgFBCXYiAiABQf//A0sbIAIgAEEAShsiAUH/AXEiAiABQYABSxsgAiAAQQBIIgAbIgFrIAEgABtBgAFzQf8BcQuWGwIvfwF+IwBBEGsiFyQAIwBBMGsiBCQAQQYhBwJAIAFF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ACECDAELIAAgAygCCEEBdiIENgK8BSAAIAJBBEEAIARBACADQQxqEBw2AsAFIAMoAgwiAg0AIAMgASAAKAK8BUEBdBAlIgI2AgwgAg0AIAAoArwFIgRBAEoEQCAAKALABSICIARBAnRqIQQDQCACIAEQIcFBBnQ2AgAgAkEEaiICIARJDQALCyABECMgAC0A3QVFBEAgAygCDCECDAELIAAgARDNAyECCyADQRBqJAAgAgvwAQEEfyMAQRBrIgMkACAAIANBDGoQJyEFAkAgAygCDCIEDQBBAiEEIAEgBUcNACAAIANBDGoQJxogAygCDCIEDQAgAyAAQRAQMCIENgIMIAQNACAAIANBDGoQHyEFIAMoAgwiBA0AQQIhBCAFRQ0AQQAhAQNAIAAgA0EMahAnIQYgAygCDCIEDQECQCAGQQJGBEAgACADQQxqECchBCADKAIMDQEgACADQQxqECcaIAMoAgwNASACIAQ2AgBBACEEDAMLIAMgAEEIEDAiBDYCDCAEDQILQQIhBCABQQFqIgEgBUcNAAsLIANBEGokACAECx4AIANBADYCACABRQRAQdEADwsgAUGHrBQgBBDbAwscACAAKAIcIAAoAgAQFCAAQgA3AgAgAEEANgIYCz4BAn8CQCAARSABRXINACAAKAIAIgBFBEAPCwNAIAAoAgQhAiAAQQAgAREAACIDDQEgACACIAMbIgANAAsLC0kBAX8CQCACQQJJDQADQCABLQAAIgNFDQEgACADOgAAIABBAWohACABQQFqIQEgAkEBayICQQFLDQALCyAAQQA6AAAgAS0AABoLrAQBB38jAEEQayIIJAAgACgCACEMIAggASACEBkiADYCDAJAIAANACABIAhBDGoQHyEJIAgoAgwiAA0AQQghACAJwSIKQe4fSg0AQQAhCSAKQQBIBEBBASEADAELA0AgASAIQQxqECchDSAIKAIMIgANASABIAhBDGoQHyELIAgoAgwiAA0BIAEgCEEMahAfIQ4gCCgCDCIADQEgBCANRwRAQQEhACAJIApGIQsgCUEBaiEJIAsNAgwBCwsgByALwUEBajYCAEEIIQAgC0GmFUsNACAIIAEgDsEgAmoQGSIANgIMIAANAEEAIQIgDEEIQQAgBygCAEEAIAhBDGoQHCEEIAgoAgwiAA0AAkAgBygCACIAQQBKBEAD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QEEAIQEMAQsgAiABEQQAIQELIARBEGokACABBEAgAhDwAyAIIAIQFAwCCyACIAAoAlQ2AgggACACNgJUQQAhAQsLIAVBEGokACABC7wCAAJAAkACQAJAAkACQAJAAkACQAJAAkAgAUEJaw4SAAgJCggJAQIDBAoJCgoICQUGBwsgAiACKAIAIgFBBGo2AgAgACABKAIANgIADwsgAiACKAIAIgFBBGo2AgAgACABMgEANwMADwsgAiACKAIAIgFBBGo2AgAgACABMwEANwMADwsgAiACKAIAIgFBBGo2AgAgACABMAAANwMADwsgAiACKAIAIgFBBGo2AgAgACABMQAANwMADwsgAiACKAIAQQdqQXhxIgFBCGo2AgAgACABKwMAOQMADwsgACACQQARBQALDwsgAiACKAIAIgFBBGo2AgAgACABNAIANwMADwsgAiACKAIAIgFBBGo2AgAgACABNQIANwMADwsgAiACKAIAQQdqQXhxIgFBCGo2AgAgACABKQMANwMAC2kBBH8jAEEQayICJAAgACgCBCgCZCEDAkAgACgCnAEiBCgCBCIFQQFxBEAgAyAAKAJYEBQgAEEANgJYDAELIAQgBUEBcjYCBAsgACADIAEgAkEMahAiNgJYIAIoAgwhACACQRBqJAAgAAsOACAAEJ0CIAAgATYCWAsiACAAIAE2ApwBIAAgAjYCoAEgACADNgKkASAAQQA2AqgBC2sBBH8gACgCACwAABB1RQRAQQAPCwNAIAAoAgAhA0F/IQEgAkHMmbPmAE0EQEF/IAMsAABBMGsiBCACQQpsIgFqIARB/////wcgAWtKGyEBCyAAIANBAWo2AgAgASECIAMsAAEQdQ0ACyABC5UCAQh/IABFBEBBIQ8LIAAoAgAhBiAABEAgACAAKALMASIBQQFrNgLMASABQQFMBEAgACgCACEHIAAoAhAiAiEBA0ACQCABRQRAQQAhAQwBCyADQQJ0QfA7aiEIQQAhBQNAIAAgBUECdGooAhQhBAJAAkAgA0EBRgRAIAQoAgAhAQwBCyAEKAIAIgEoAgggCCgCABAgDQELIAEtAABBAXFFDQAgBCgCECIBRQ0AA0AgASgCCBC8ARogBCgCECIBDQALIAAoAhAhAgsgAiIBIAVBAWoiBUsNAAsLIANBAWoiA0ECRw0ACyACBEADQCAAIAJB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IAAgAzYCAA8LIAIgASgCCCABKAIMQQAQjgEgAiABKAIQIAEoAhRBABCOASACIAEoAhggASgCHEEBEI4BCwsTACAAKAIcIgAQkwEgAEEAOgAsC2UBAn8CQAJAIAAoAhwiAi0ALA0AIAIgASgCACABKAIEELoCIgNFDQAgACgCGCIAKAIADQEgACADNgIADwsgAiABKAIIIAEoAgwQuQIiAUUNACAAKAIYIgAoAgANACAAIAE2AgALC+4EAQN/IwBBEGsiBSQAIAUgAjYCCCAFIAA2AgwgAkEAQYwF/AsAIAIgAS0ACCIEOgC8ASAEBEADQCACIANBAnRqIAEgA0EBdGouAQw2AsABIANBAWoiAyAERw0ACwsgAiABLQAJIgQ6AL0BIAQEQEEAIQMDQCACIANBAnRqIAEgA0EBdGouASg2AvgBIANBAWoiAyAERw0ACwsgAiABLQAKIgQ6AL4BIAQEQEEAIQMDQCACIANBAnRqIAEgA0EBdGouATw2AqACIANBAWoiAyAERw0ACwsgAiABLQALIgQ6AL8BIAQEQEEAIQMDQCACIANBAnRqIAEgA0EBdGouAVg2AtgCIANBAWoiAyAERw0ACwsgAiABKAJsNgKAAyACIAEoAnA2AoQDIAIgASgCdDYCiAMgAiABLwF4NgKMAyACIAEvAXo2ApADIAIgAS0AfCIEOgCUAyAEBEBBACEDA0AgAiADQQJ0aiABIANBAXRqLgGAATYCmAMgA0EBaiIDIARHDQALCyACIAEtAH0iBDoAlQMgBARAQQAhAwNAIAIgA0ECdGogASADQQF0ai4BmgE2AswDIANBAWoiAyAERw0ACwsgAiABLQB+OgCABCACIAEoAgQ2AogEIAIgASgCuAE2AowEIAEoArQBIQEgAiACNgKoBCACIAE2ApAEAkACQCAAKAKAASIAKAI8IgFBf0YNACACIAE2AogFIAFFDQADQCABEJABIgFBAEgNAAsgACABNgI8DAELIAIgBUEEaiAFQQhqIAVBDGpzcyIAQQp2IABzIABBFHZzIgBBhOcBIAAbNgKIBQsgBUEQaiQAC/ECACAAQQBBkAX8CwACQCACBEAgACABIAIQzAEgACABQeQLajYCnAQgACABKALECjYC3AQgACABKALICjYCyAQgACABKALMCjYCxAQgASgCwAshAiAAIAEoArwL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NwIIIABCADcCACAAQfzRAikCADcCJCAAQYTSAikCADcCLEEADAELIAIgACgCGBAUIABBADYCGCADKAIMCyECIANBEGokACACC58BAQN/IwBBEGsiBCQAIARBADYCDEEGIQYCQCABRSAARXIgAkUgA0Vycg0AIAMoAgQhBSADKAIAIARBDGoQSCIGDQAgBCgCDCIDQQM2AiAgAyAFKQIANwIAIAMgACkCADcCECADIAEpAgA3AhggAyACKQIANwIIIAMgBSgCCDYCJCAFIAM2AgggBSACKQIANwIAQQAhBgsgBEEQaiQAIAYLkQEBA38jAEEQayIDJAAgA0EANgIMQQYhBQJAIAJFIABFIAFFcnINACACKAIEIQQgAigCACADQQxqEEgiBQ0AIAMoAgwiAkECNgIgIAIgBCkCADcCACACIAApAgA3AhAgAiABKQIANwIIIAIgBCgCCDYCJCAEIAI2AgggBCABKQIANwIAQQAhBQsgA0EQaiQAIAULpAEBA38jAEEQayIDJAAgA0EANgIMQQYhAgJAIABFIAFFcg0AIAEoAgAhBCABKAIEIgEoAgAgACgCAEYEQEEAIQIgASgCBCAAKAIERg0BCyAEIANBDGoQSCICDQAgAygCDCICQQE2AiAgAiABKQIANwIAIAIgACkCADcCCCACIAEoAgg2AiQgASACNgIIIAEgACkCADcCAEEAIQILIANBEGokACACC2IBAn8jAEEQayIDJAAgA0EANgIMQQYhAgJAIABFIAFFcg0AIAEoAgAgA0EMahDTAiICDQAgAygCDCICIAApAgA3AgAgAiABKAIENgIMIAEgAjYCBEEAIQILIANBEGokACACC2gAAn8CQCABQckYECBFBEAgAiAAQUBrKAIANgIADAELIAFBjhMQIEUEQCACIAAtAEQ2AgAMAQsgAUG9CxAgRQRAIAIgAC0ARTYCAAwBC0EMIAFBlw8QIA0BGiACIAAtAEY2AgALQQALC38AAn8CQCABQckYECBFBEBBBiACKAIAIgFBIWtBYUkNAhogAEFAayABNgIADAELIAFBjhMQIEUEQCAAIAIoAgBBAEc6AERBAA8LIAFBvQsQIEUEQCAAIAIoAgBBAEc6AEVBAA8LQQwgAUGXDxAgDQEaIAAgAi0AADoARgtBAAsL2QwBFn8jAEEwayIEJAAgBEEANgIs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CyACKALECgsNACAAKAIIQQl2QQFxC58CAQV/IwBBEGsiBSQAIAAoAsgFIQYgBUEANgIMAkAgBkUNACAGKAK8GCIDRQRAIAAoAmRBAiAFQQxqECQhAyAFKAIMIgINASADQQA7AQACQCAGIAZB3AtqKAIAEDUiAEUNACAAQbgWEKUBIgBFDQAgAEEHaiICQdgVEKUBIgBFIAAgAkZyDQADQAJAAkAgAi0AACIEQTBrQf8BcUEJTQRAIAMvAQAiBEGXM0sNAiADIARBCmwiBDsBACADIAQgAiwAAGpBMGs7AQAMAQsCQCAEQQprDgQBAgIBAAsgBEEgRw0BCyACQQFqIgIgAEcNAQwCCwsgA0EAOwEACyAGIAM2ArwYIAUoAgwhAgsgASADLwEAOwEACyAFQRBqJAAgAguaAgEEfyMAQRBrIgQkACAAKALIBSECIARBADYCDAJAIAJFDQAgAigClBgiAwR/QQAFIAAoAmRBICAEQQxqECQhAyAEKAIMIgUNASADIAIgAigC3AoQNTYCACADIAIgAkHgCmooAgAQNTYCBCADIAIgAkHoCmooAgAQNTYCCCADIAIgAkHsCmooAgAQNTYCDCADIAIgAkHwCmooAgAQNTYCECADIAJB+ApqKAIANgIUIAMgAkH0CmotAAA6ABggAyACQfwKaigCADsBGiADIAJBgAtqKAIAOwEcIAIgAzYClBggBCgCDAshBSABIAMpAgA3AgAgASADKQIYNwIYIAEgAykCEDcCECABIAMpAgg3AggLIARBEGokACAFC5IBACAAIAEoAgggBBAzBEAgASACIAMQgAIPCwJAIAAgASgCACAEEDNFDQACQCACIAEoAhBHBEAgASgCFCACRw0BCyADQQFHDQEgAUEBNgIgDwsgASACNgIUIAEgAzYCICABIAEoAihBAWo2AigCQCABKAIkQQFHDQAgASgCGEECRw0AIAFBAToANgsgAUEENgIsCwsQACAAIAAoAqgEKAIcEQMACxQAIAAgASACIAAoAqgEKAIAEQEACxgAIAAgASACIAMgBCAAKAKkBCgCKBEIAAsUACAAIAEgAiAAKAKkBCgCJBEBAAsUACAAIAEgAiAAKAKkBCgCIBEBAAsSACAAIAEgACgCpAQoAhwRAAALFAAgACABIAIgACgCpAQoAhgRAQALFAAgACABIAIgACgC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AnT8CwALIAUL0AEBA38CQCAAKALgBSIERQRAIABBABBkIgUNASAAKALgBSEECyAEKAIERQRAIABBAEEAQQEQrwEiBQ0BCyAEKAIAIgMgASABIANLGyEDAkACQAJAIAAtAN0FBEAgAw0BDAILIANFDQEgAkEAIANBAnT8CwAMAgsgBCgCCCEEQQAhAANAIAIgAEECdCIFaiAEIAVqKAIANgIAIABBAWoiACADRw0ACwwBC0EAIQMLQQAhBSABIANNDQAgAiADQQJ0akEAIAEgA2tBAnT8CwALIAULLQAgACABIAJBARCvASICRQRAIAAgACgCCEH//31xIAFBAEdBD3RyNgIICyACCz4AIAAgAkECdGooAowCIQAgAUEATgRAIAAgAWpBQHEiAEEAIABBAEobDwtBACAAIAFrQUBxayIAQR91IABxC0QAIAAgAkECdGooAowCIQAgAUEATgRAIAAgAWpBP2pBQHEiAEEAIABBAEobDwtBACAAIAFrQT9qQUBxayIAQR91IABxC3YBAn8gACACQQJ0aigCjAIhAyAAKAKABCECIAAoAoQEIQQgAUEATgRAIAIgASADaiAEaiACayIBIAEgACgC/ANvayACaiIAIABBAEgbDwtBACACayADIARqIAEgAmprIgEgACgC/ANvIAEgAmprIgAgAEEAShsLdQECfyAAIAJBAnRqKAKMAiEDIAAoAoAEIQIgACgChAQhBCABQQBOBEAgAiABIANqIARqIAJrQQAgACgC/ANrcSACaiIAIABBAEgbDwtBACACa0EAIAIgAyAEaiABIAJqa0EAIAAoAvwDa3FqayIAIABBAEobCzIAIAAgASgCCEEAEDMEQCABIAIgAxCPAg8LIAAoAggiACABIAIgAyAAKAIAKAIcEQYAC0QAIAAgAkECdGooAowCIQAgAUEATgRAIAAgAWpBEGpBYHEiAEEAIABBAEobDwtBACAAIAFrQRBqQWBxayIAQR91IABxC0MAIAAgAkECdGooAowCIQAgAUEATgRAQSAgACABakFAcUEgciIAIABBAEgbDwtBYEFgIAAgAWtBQHEiAGsgAEFgSBsLRAAgACACQQJ0aigCjAIhACABQQBOBEAgACABakEgakFAcSIAQQAgAEEAShsPC0EAIAAgAWtBIGpBQHFrIgBBH3UgAHELGQAgACABKAII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EQEAIQQgACAAKAJYIAJqIgIoAgAgACgCNCAALwGgAkEDdGoiBSgCAGsgAigCBCAFKAIEayAAKALcBBEBACEFQQAgAWsgASABIARzQQBIGyABIAAtAMACGyECAkAgAC0A+AIiAUEEcQRAIAAgAC8B3AIgAC8B3gJGBH8gBCACIAAoAsQCIAIgBGsiAiACQR91IgJqIAJzSBsFIAILIAFBA3EgACgC2AQRAQAhAiAALQD4AiEBDAELIAAgAiABQQNxELABIQILIABByABqIQYCQCABQQhxRQ0AIAAoArgCIQEgBEEATgRAIAEgAiABIAJKGyECDAELQQAgAWsiASACIAEgAkgbIQILIAAgBiADIAIgBWsgACgC6AQRBgALIAAgAC8BoAI7AaICIAAtAPgCQRBxBEAgACADOwGgAgsgACADOwGkAgwCCyADQcABTwRAAkACQCABLwEAIgMgAC8BUEkEQCAALwGgAiIBIAAvASxJDQELIAAtANEERQ0BIABBhgE2AgwMAQsCfwJAIAAvAdwCBEAgAC8B3gINAQsgACAAKAJUIANBA3RqIgIoAgAgACgCMCABQQN0aiIBKAIAayACKAIEIAEoAgRrIAAoAuAEEQEADAELIAAoAjggAUEDdGohASAAKAJcIANBA3RqIQIgACgC4AEiBCAAKALkAUYEQCAAIAIoAgAgASgCAGsgAigCBCABKAIEayAAKALgBBEBACAAKALgARAVDAELIAAgAigCACABKAIAayAEEBUgAigCBCABKAIEayAAKALkARAVIAAoAuAEEQEACyECAkAgACgCyAIiBEEATA0AIAIgACgCzAIiASAEak4gAiABIARrTHINACABQQAgAWsgAkEAThshAgsCQCAALQD4AiIEQQRxBEAgACACIARBA3EgACgC2AQRAQAhASAALQD4AiEEDAELIAAgAiAEQQNxELABIQELIABByABqIQUCQCAEQQhxRQ0AIAAoArgCIQQgAkEATgRAIAQgASABIARIGyEBDAELQQAgBGsiAiABIAEgAkobIQELIAAgBSADIAEgACAAKAJYIANBA3RqIgEoAgAgACgCNCAALwGgAkEDdGoiAigCAGsgASgCBCACKAIEayAAKALcBBEBAGsgACgC6AQRBgALIAAgAzsBpAIgACAALwGgAjsBogIgAC0A+AJBEHEEQCAA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hCDYrSDZgADqpI0g6qSAAOKCkiDigoAAZmxpcF95AHBzYXV4AEZvbnRCQm94AC0rICAgMFgweAB1bnNpZ25lZCBzaG9ydABkZWZhdWx0LXNjcmlwdABmYWxsYmFjay1zY3JpcHQASXNCYXNlRm9udABJc0NJREZvbnQAdW5zaWduZWQgaW50AHNmbnQAU2xhbnQAaW5jcmVhc2UteC1oZWlnaHQAV2VpZ2h0AENhcEhlaWdodABYSGVpZ2h0AGludmFsaWQgbGl0ZXJhbC9sZW5ndGhzIHNldABpbnZhbGlkIGNvZGUgbGVuZ3RocyBzZXQAdW5rbm93biBoZWFkZXIgZmxhZ3Mgc2V0AGludmFsaWQgZGlzdGFuY2VzIHNldABDaGFyYWN0ZXJTZXQAZ2x5cGgtZGljdABmbG9hdABmb250LWZvcm1hdABpbnZhbGlkIGJpdCBsZW5ndGggcmVwZWF0AHVpbnQ2NF90AE1ldHJpY3NTZXRzAFVuZGVybGluZVRoaWNrbmVzcwBTdGFydEtlcm5QYWlycwBFbmRLZXJuUGFpcnMAbXVsdGktbWFzdGVycwBkYXJrZW5pbmctcGFyYW1ldGVycwBDaGFyYWN0ZXJzAHR0LWNtYXBzAHBvc3RzY3JpcHQtY21hcHMAb3ZlcmxhcHMAQmxlbmREZXNpZ25Qb3NpdGlvbnMAbWV0cmljcy12YXJpYXRpb25zAHRvbyBtYW55IGxlbmd0aCBvciBkaXN0YW5jZSBzeW1ib2xzAFN0YXJ0QXhpcwBFbmRBeGlzAGludmFsaWQgc3RvcmVkIGJsb2NrIGxlbmd0aHMAU3RhcnRDb21wb3NpdGVzAEVuZENvbXBvc2l0ZXMAQmxlbmRBeGlzVHlwZXMAcHNuYW1lcwBwcm9wZXJ0aWVzAFN0YXJ0Rm9udE1ldHJpY3MARW5kRm9udE1ldHJpY3MAU3RhcnRDaGFyTWV0cmljcwBFbmRDaGFyTWV0cmljcwBXZWlnaHRWZWN0b3IAVlZlY3RvcgBhdXRvZml0dGVyAHBzaGludGVyAERlc2NlbmRlcgBBc2NlbmRlcgBSZWd1bGFyAHVuc2lnbmVkIGNoYXIARXNjQ2hhcgBnbHlwaC10by1zY3JpcHQtbWFwAEJsZW5kRGVzaWduTWFwAHBvc3RzY3JpcHQtaW5mbwBTdGFydFRyYWNrS2VybgBFbmRUcmFja0tlcm4AVW5kZXJsaW5lUG9zaXRpb24AU3RhcnREaXJlY3Rpb24ARW5kRGlyZWN0aW9uAGludGVycHJldGVyLXZlcnNpb24AVmVyc2lvbgBmbGlwX3NpZ24AYm9vbABBeGlzTGFiZWwAZW1zY3JpcHRlbjo6dmFsAGludmFsaWQgY29kZSAtLSBtaXNzaW5nIGVuZC1vZi1ibG9jawBpbmNvcnJlY3QgaGVhZGVyIGNoZWNrAGluY29ycmVjdCBsZW5ndGggY2hlY2sAaW5jb3JyZWN0IGRhdGEgY2hlY2sAQmxhY2sAaW52YWxpZCBkaXN0YW5jZSB0b28gZmFyIGJhY2sAc21vb3RoAENoYXJXaWR0aABJc0ZpeGVkUGl0Y2gAaGVhZGVyIGNyYyBtaXNtYXRjaAB1bnNpZ25lZCBsb25nAHN0ZDo6d3N0cmluZwBzdGQ6OnN0cmluZwBzdGQ6OnUxNnN0cmluZwBzdGQ6OnUzMnN0cmluZwBuby1zdGVtLWRhcmtlbmluZwB0dC1nbHlmAGNmZgBkZWYAYnNkZgBiZGYAaW52YWxpZCB3aW5kb3cgc2l6ZQBPcHRpY2FsU2l6ZQB0cnVlAHRydWV0eXBlAGludmFsaWQgYmxvY2sgdHlwZQBBeGlzVHlwZQBUcnVlVHlwZQAvRlNUeXBlAGhpbnRpbmctZW5naW5lAHRydWV0eXBlLWVuZ2luZQBNYXBwaW5nU2NoZW1lAEVuY29kaW5nU2NoZW1lAHBvc3RzY3JpcHQtZm9udC1uYW1lAEZhbWlseU5hbWUARm9udE5hbWUARnVsbE5hbWUASXRhbGljQW5nbGUAZG91YmxlAHNmbnQtdGFibGUAaW52YWxpZCBsaXRlcmFsL2xlbmd0aCBjb2RlAGludmFsaWQgZGlzdGFuY2UgY29kZQBOb3RpY2UAYWRvYmUAdW5rbm93biBjb21wcmVzc2lvbiBtZXRob2QAQm9sZAB2b2lkAGNpZAByYW5kb20tc2VlZABjZmYtbG9hZABzcHJlYWQALy4ubmFtZWRmb3JrL3JzcmMAU3RhcnRLZXJuRGF0YQBFbmRLZXJuRGF0YQAuXwBPRkZTRVRfR0xZUEhfTUVUUklDU19WRVJUX0JFQVJJTkdfWQBPRkZTRVRfR0xZUEhfTUVUUklDU19IT1JJX0JFQVJJTkdfWQBPRkZTRVRfR0xZUEhfQURWQU5DRV9ZAFdZAENIQVJTRVRfUkVHSVNUUlkAS1BZAFcxWQBXMFkAT0ZGU0VUX0dMWVBIX01FVFJJQ1NfVkVSVF9CRUFSSU5HX1gAT0ZGU0VUX0dMWVBIX01FVFJJQ1NfSE9SSV9CRUFSSU5HX1gAT0ZGU0VUX0dMWVBIX0FEVkFOQ0VfWABXWABLUFgAT0ZGU0VUX0dMWVBIX0lOREVYAFcxWABXMFgAU3RkVlcAU3RkSFcASXNGaXhlZFYARlRfTE9BRF9UQVJHRVRfTENEX1YARlRfUkVOREVSX01PREVfTENEX1YAVlYARlRfTE9BRF9OT19BVVRPSElOVABGVF9MT0FEX0ZPUkNFX0FVVE9ISU5UAEZUX0tFUk5JTkdfREVGQVVMVABGVF9MT0FEX0RFRkFVTFQARlRfTE9BRF9UQVJHRVRfTElHSFQAT0ZGU0VUX0dMWVBIX01FVFJJQ1NfSEVJR0hUAE9GRlNFVF9GQUNFX0hFSUdIVABPRkZTRVRfR0xZUEhfQklUTUFQX0xFRlQAT0ZGU0VUX0dMWVBIX0JJVE1BUF9OVU1fR1JBWVMAT0ZGU0VUX0dMWVBIX0JJVE1BUF9ST1dTAE9GRlNFVF9GQUNFX0ZBQ0VfRkxBR1MARlJFRVRZUEVfUFJPUEVSVElFUwBGVF9GQUNFX0ZMQUdfQ09MT1IARlRfTE9BRF9DT0xPUgBPRkZTRVRfR0xZUEhfQklUTUFQX0JVRkZFUgBGVF9MT0FEX1JFTkRFUgBPRkZTRVRfRkFDRV9ERVNDRU5ERVIAT0ZGU0VUX1RUX09TMl9ERVNDRU5ERVIAT0ZGU0VUX0ZBQ0VfQVNDRU5ERVIAT0ZGU0VUX1RUX09TMl9BU0NFTkRFUgBPRkZTRVRfR0xZUEhfQklUTUFQX1RPUABLUABPRkZTRVRfVFRfT1MyX0xJTkVHQVAARlRfTE9BRF9UQVJHRVRfTU9OTwBGVF9SRU5ERVJfTU9ERV9NT05PAEZUX1BJWEVMX01PREVfTU9OTwBOAE9GRlNFVF9GQUNFX1VOSVRTX1BFUl9FTQBGVF9MT0FEX1RBUkdFVF9OT1JNQUwARlRfUkVOREVSX01PREVfTk9STUFMAE9GRlNFVF9HTFlQSF9NRVRSSUNTX1dJRFRIAE9GRlNFVF9HTFlQSF9CSVRNQVBfV0lEVEgAT0ZGU0VUX0ZBQ0VfR0xZUEgAS1BIAE9GRlNFVF9HTFlQSF9CSVRNQVBfUElUQ0gARlRfTE9BRF9OT19ISU5USU5HAENIQVJTRVRfRU5DT0RJTkcAQ0ZGAEZUX0xPQURfTU9OT0NIUk9NRQBPRkZTRVRfR0xZUEhfQklUTUFQX1BJWEVMX01PREUAT0ZGU0VUX0dMWVBIX0JJVE1BUF9QQUxFVFRFX01PREUAT0ZGU0VUX0dMWVBIX01FVFJJQ1NfVkVSVF9BRFZBTkNFAE9GRlNFVF9HTFlQSF9MSU5FQVJfVkVSVF9BRFZBTkNFAE9GRlNFVF9HTFlQSF9NRVRSSUNTX0hPUklfQURWQU5DRQBPRkZTRVRfR0xZUEhfTElORUFSX0hPUklfQURWQU5DRQBDSUQARlRfS0VSTklOR19VTkZJVFRFRABGVF9LRVJOSU5HX1VOU0NBTEVEAEZUX0xPQURfVEFSR0VUX0xDRABGVF9SRU5ERVJfTU9ERV9MQ0QARlRfTE9BRF9QRURBTlRJQwBQQ0MAQgBPRkZTRVRfR0xZUEhfUlNCX0RFTFRBAE9GRlNFVF9HTFlQSF9MU0JfREVMVEEARlRfUElYRUxfTU9ERV9CR1JBAGVtc2NyaXB0ZW46Om1lbW9yeV92aWV3PHNob3J0PgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzx1bnNpZ25lZCBzaG9ydD4AZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8aW50PgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzx1bnNpZ25lZCBpbnQ+AGVtc2NyaXB0ZW46Om1lbW9yeV92aWV3PGZsb2F0PgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzx1aW50OF90PgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzxpbnQ4X3Q+AGVtc2NyaXB0ZW46Om1lbW9yeV92aWV3PHVpbnQxNl90PgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzxpbnQxNl90PgBlbXNjcmlwdGVuOjptZW1vcnlfdmlldzx1aW50MzJfdD4AZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8aW50MzJfdD4AZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8Y2hhcj4AZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8dW5zaWduZWQgY2hhcj4Ac3RkOjpiYXNpY19zdHJpbmc8dW5zaWduZWQgY2hhcj4AZW1zY3JpcHRlbjo6bWVtb3J5X3ZpZXc8c2lnbmVkIGNoYXI+AGVtc2NyaXB0ZW46Om1lbW9yeV92aWV3PGxvbmc+AGVtc2NyaXB0ZW46Om1lbW9yeV92aWV3PHVuc2lnbmVkIGxvbmc+AGVtc2NyaXB0ZW46Om1lbW9yeV92aWV3PGRvdWJsZT4ATW9kdWxlLiVzID0gJWQ7AEZUX1NGTlRfT1MyAHR5cGU0MgBTdGFydEtlcm5QYWlyczEAcmFzdGVyMQB0eXBlMQBXMQAxLjIuMTEAVHlwZSAxAFN0YXJ0S2VyblBhaXJzMABXMABvIE8gMAByZXNvdXJjZS5mcmsvAC5BcHBsZURvdWJsZS8ALnJlc291cmNlLwAobnVsbCkAJQBOU3QzX18yMTJiYXNpY19zdHJpbmdJY05TXzExY2hhcl90cmFpdHNJY0VFTlNfOWFsbG9jYXRvckljRUVFRQBOU3QzX18yMjFfX2Jhc2ljX3N0cmluZ19jb21tb25JTGIxRUVFAAAA3BwAAHAVAABgHQAAMRUAAAAAAAABAAAAmBUAAAAAAABOU3QzX18yMTJiYXNpY19zdHJpbmdJaE5TXzExY2hhcl90cmFpdHNJaEVFTlNfOWFsbG9jYXRvckloRUVFRQAAYB0AALgVAAAAAAAAAQAAAJgVAAAAAAAATlN0M19fMjEyYmFzaWNfc3RyaW5nSXdOU18xMWNoYXJfdHJhaXRzSXdFRU5TXzlhbGxvY2F0b3JJd0VFRUUAAGAdAAAQFgAAAAAAAAEAAACYFQAAAAAAAE5TdDNfXzIxMmJhc2ljX3N0cmluZ0lEc05TXzExY2hhcl90cmFpdHNJRHNFRU5TXzlhbGxvY2F0b3JJRHNFRUVFAAAAYB0AAGgWAAAAAAAAAQAAAJgVAAAAAAAATlN0M19fMjEyYmFzaWNfc3RyaW5nSURpTlNfMTFjaGFyX3RyYWl0c0lEaUVFTlNfOWFsbG9jYXRvcklEaUVFRUUAAABgHQAAxBYAAAAAAAABAAAAmBUAAAAAAABOMTBlbXNjcmlwdGVuM3ZhbEUAANwcAAAg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AAAAAAAAAAAAAAAAAAAABAAAAAQQAAAUAAAABAAAAAAAAAAAAAAAAAAAAAUAAAABEQAAGAAAAAEAAAAAAAAAAAAAAAAAAAACAAAAAhEAABwAAAAEAAAAAAAAAAAAAAAAAAAAAgAAAAMRAAAgAAAABAAAAAAAAAAAAAAAAAAAAAIAAAAEEQAAJAAAAAQAAAAAAAAAAAAAAAAAAAABAAAABREAACgAAAAEAAAAAAAAAAAAAAAAAAAAAQAAAAYRAAAsAAAABAAAAAAAAAAAAAAAAAAAAAcAAAAHEQAAAAAAAAAAAACXAAAAAAAAAAAAAAABAAAADRAAAFAAAAAEAAAAAAAAAAAAAAAAAAAABwAAAAUQAAAAAAAAAAAAAJgAAAAAAAAAAAAAAAEAAAAIEQAAZAAAAAQAAAAAAAAAAAAAAAAAAAABAAAADxAAAGgAAAAEAAAAAAAAAAAAAAAAAAAAAQAAABAQAABsAAAABAAAAAAAAAAAAAAAAAAAAAEAAAAREAAAcAAAAAQAAAAAAAAAAAAAAAAAAAAHAAAAEhAAAAAAAAAAAAAAmQAAAAAAAAAAAAAAAQAAABQRAAB8AAAABAAAAAAAAAAAAAAAAAAAAAQAAAAVEQAAgAAAAAQAAAAAAAAAAAAAAAAAAAAHAAAAGBEAAAAAAAAAAAAAmgAAAAAAAAAAAAAABwAAAB4RAAAAAAAAAAAAAJsAAAAAAAAAAAAAAAEAAAAfEQAAkAAAAAQAAAAAAAAAAAAAAAAAAAABAAAAIBEAAJQAAAAEAAAAAAAAAAAAAAAAAAAAAQAAACERAACYAAAABAAAAAAAAAAAAAAAAAAAAAEAAAAiEQAAnAAAAAQAAAAAAAAAAAAAAAAAAAABAAAAIxEAAKAAAAAEAAAAAAAAAAAAAAAAAAAAAQAAACQRAACkAAAABAAAAAAAAAAAAAAAAAAAAAEAAAAlEQAAqAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAJhEAAKwAAAAEAAAAAAAAAAAAAAAAAAAABgAAAAYgAAAEAAAABAAAAAAAAAAOAAAAAAAAAAYAAAAHIAAAPAAAAAQAAAAAAAAACgAAAAEAAAAGAAAACCAAAGQAAAAEAAAAAAAAAA4AAAACAAAABgAAAAkgAACcAAAABAAAAAAAAAAKAAAAAwAAAAMAAAAJIQAAxAAAAAQAAAAA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CF4AAAgeAAAJnAAUB2MAAAh+AAAIPgAACdwAEgcbAAAIbgAACC4AAAm8AAAIDgAACI4AAAhOAAAJ/ABgBwAAAAhRAAAIEQAVCIMAEgcfAAAIcQAACDEAAAnCABAHCgAACGEAAAghAAAJogAACAEAAAiBAAAIQQAACeIAEAcGAAAIWQAACBkAAAmSABMHOwAACHkAAAg5AAAJ0gARBxEAAAhpAAAIKQAACbIAAAgJAAAIiQAACEkAAAnyABAHBAAACFUAAAgVABAIAgETBysAAAh1AAAINQAACcoAEQcNAAAIZQAACCUAAAmqAAAIBQAACIUAAAhFAAAJ6gAQBwgAAAhdAAAIHQAACZoAFAdTAAAIfQAACD0AAAnaABIHFwAACG0AAAgtAAAJugAACA0AAAiNAAAITQAACfoAEAcDAAAIUwAACBMAFQjDABMHIwAACHMAAAgzAAAJxgARBwsAAAhjAAAIIwAACaYAAAgDAAAIgwAACEMAAAnmABAHBwAACFsAAAgbAAAJlgAUB0MAAAh7AAAIOwAACdYAEgcTAAAIawAACCsAAAm2AAAICwAACIsAAAhLAAAJ9gAQBwUAAAhXAAAIFwBACAAAEwczAAAIdwAACDcAAAnOABEHDwAACGcAAAgnAAAJrgAACAcAAAiHAAAIRwAACe4AEAcJAAAIXwAACB8AAAmeABQHYwAACH8AAAg/AAAJ3gASBxsAAAhvAAAILwAACb4AAAgPAAAIjwAACE8AAAn+AGAHAAAACFAAAAgQABQIcwASBx8AAAhwAAAIMAAACcEAEAcKAAAIYAAACCAAAAmhAAAIAAAACIAAAAhAAAAJ4QAQBwYAAAhYAAAIGAAACZEAEwc7AAAIeAAACDgAAAnRABEHEQAACGgAAAgoAAAJsQAACAgAAAiIAAAISAAACfEAEAcEAAAIVAAACBQAFQjjABMHKwAACHQAAAg0AAAJyQARBw0AAAhkAAAIJAAACakAAAgEAAAIhAAACEQAAAnpABAHCAAACFwAAAgcAAAJmQAUB1MAAAh8AAAIPAAACdkAEgcXAAAIbAAACCwAAAm5AAAIDAAACIwAAAhMAAAJ+QAQBwMAAAhSAAAIEgAVCKMAEwcjAAAIcgAACDIAAAnFABEHCwAACGIAAAgiAAAJpQAACAIAAAiCAAAIQgAACeUAEAcHAAAIWgAACBoAAAmVABQHQwAACHoAAAg6AAAJ1QASBxMAAAhqAAAIKgAACbUAAAgKAAAIigAACEoAAAn1ABAHBQAACFYAAAgWAEAIAAATBzMAAAh2AAAINgAACc0AEQcPAAAIZgAACCYAAAmtAAAIBgAACIYAAAhGAAAJ7QAQBwkAAAheAAAIHgAACZ0AFAdjAAAIfgAACD4AAAndABIHGwAACG4AAAguAAAJvQAACA4AAAiOAAAITgAACf0AYAcAAAAIUQAACBEAFQiDABIHHwAACHEAAAgxAAAJwwAQBwoAAAhhAAAIIQAACaMAAAgBAAAIgQAACEEAAAnjABAHBgAACFkAAAgZAAAJkwATBzsAAAh5AAAIOQAACdMAEQcRAAAIaQAACCkAAAmzAAAICQAACIkAAAhJAAAJ8wAQBwQAAAhVAAAIFQAQCAIBEwcrAAAIdQAACDUAAAnLABEHDQAACGUAAAglAAAJqwAACAUAAAiFAAAIRQAACesAEAcIAAAIXQAACB0AAAmbABQHUwAACH0AAAg9AAAJ2wASBxcAAAhtAAAILQAACbsAAAgNAAAIjQAACE0AAAn7ABAHAwAACFMAAAgTABUIwwATByMAAAhzAAAIMwAACccAEQcLAAAIYwAACCMAAAmnAAAIAwAACIMAAAhDAAAJ5wAQBwcAAAhbAAAIGwAACZcAFAdDAAAIewAACDsAAAnXABIHEwAACGsAAAgrAAAJtwAACAsAAAiLAAAISwAACfcAEAcFAAAIVwAACBcAQAgAABMHMwAACHcAAAg3AAAJzwARBw8AAAhnAAAIJwAACa8AAAgHAAAIhwAACEcAAAnvABAHCQAACF8AAAgfAAAJnwAUB2MAAAh/AAAIPwAACd8AEgcbAAAIbwAACC8AAAm/AAAIDwAACI8AAAhPAAAJ/wAQBQEAFwUBARMFEQAbBQEQEQUFABkFAQQVBUEAHQUBQBAFAwAYBQECFAUhABwFASASBQkAGgUBCBYFgQBABQAAEAUCABcFgQETBRkAGwUBGBEFBwAZBQEGFQVhAB0FAWAQBQQAGAUBAxQFMQAcBQEwEgUNABoFAQwWBcEAQAUAAAoAAABAAAAACAAAAK8AAACwAAAAsQAAALIAAACzAAAAtAAAALUAAAC2AAAAtwAAALgAAAC5AAAAugAAALsAAAC8AAAAvQAAAL4AAAC/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AAAAAAAAABwAAACrAQAArAEAAK0BAACuAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAArwEAALABAACxAQAAsgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKkAAKypAADUqQAA/KkAALMBAAC0AQAAtQEAALYBAAC3AQAAuAEAALkBAAC6AQAAuwEAALwBAACiAQAA/KgAAAypAABIqQAAaKkAAL0BAAC+AQAAvwEAAMABAAAkqgAAeKkAAFSqAAAAAAAADAAAAMQFAAAAAAIAAAACAGCqAAAAAAAAAAAAAAAAAADOCAAAnQkAACYLAADzEQAAOQgAAAkJAACgBwAA8REAAPARAACIEAAAUQYAAEoKAADHBgAAcgcAAMQIAABtCwAA9gcAAIwIAAArCAAAZQkAAGwIAABwDAAAQgcAADcJAADtCAAAkQsAAMoFAACcCwAApQsAAAsGAAAWBgAAVAoAAIsNAACuCwAAiw8AAG0QAABgDQAA8gwAACQQAABfCwAAFAcAAN8PAAD+CwAA7xEAAOwHAAB7CAAAGwgAAFYJAABbCAAAYgwAADMHAADoFAAAuRQAACgJAACFDQAAfw0AADoJAABECQAAIAcAAL4NAACoCAAAhgkAAIkNAAD4FAAAew0AAPoMAADXFAAAdw0AAPYMAABdDQAA3gwAAEoGAACbCAAAWwYAAAAAAAAAAAAA////////////////////////////////////////////////////////////////AAECAwQFBgcICf////////8KCwwNDg8QERITFBUWFxgZGhscHR4fICEiI////////woLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIj//////8AAAAAAAAAAAIAAAAFAAAABAAAAAAAAAABAAAAAQAAAAQAAAACAAAAAgAAAAYAAAAEAAAAAQAAAAEAAAAAAAAAAgAAAAYAAAACAAAABgAAAAIAAAD/////AQAAAAAAAAAAAAAAAgAAAAIAAAABAAEBAQABAAEAAQABAQEBAQEBAQEBAQEBAQEBAQABAAEBAQABAQEBAQEBAQEBAAAAAAAALm51bGwAbm9ubWFya2luZ3JldHVybgBub3RlcXVhbABpbmZpbml0eQBsZXNzZXF1YWwAZ3JlYXRlcmVxdWFsAHBhcnRpYWxkaWZmAHN1bW1hdGlvbgBwcm9kdWN0AHBpAGludGVncmFsAE9tZWdhAHJhZGljYWwAYXBwcm94ZXF1YWwARGVsdGEAbm9uYnJlYWtpbmdzcGFjZQBsb3plbmdlAGFwcGxlAGZyYW5jAEdicmV2ZQBnYnJldmUASWRvdGFjY2VudABTY2VkaWxsYQBzY2VkaWxsYQBDYWN1dGUAY2FjdXRlAENjYXJvbgBjY2Fyb24AZGNyb2F0AC5ub3RkZWYAc3BhY2UAZXhjbGFtAHF1b3RlZGJsAG51bWJlcnNpZ24AZG9sbGFyAHBlcmNlbnQAYW1wZXJzYW5kAHF1b3RlcmlnaHQAcGFyZW5sZWZ0AHBhcmVucmlnaHQAYXN0ZXJpc2sAcGx1cwBjb21tYQBoeXBoZW4AcGVyaW9kAHNsYXNoAHplcm8Ab25lAHR3bwB0aHJlZQBmb3VyAGZpdmUAc2l4AHNldmVuAGVpZ2h0AG5pbmUAY29sb24Ac2VtaWNvbG9uAGxlc3MAZXF1YWwAZ3JlYXRlcgBxdWVzdGlvbgBhdABBAEIAQwBEAEUARgBHAEgASQBKAEsATABNAE4ATwBQAFEAUgBTAFQAVQBWAFcAWABZAFoAYnJhY2tldGxlZnQAYmFja3NsYXNoAGJyYWNrZXRyaWdodABhc2NpaWNpcmN1bQB1bmRlcnNjb3JlAHF1b3RlbGVmdABhAGIAYwBkAGUAZgBnAGgAaQBqAGsAbABtAG4AbwBwAHEAcgBzAHQAdQB2AHcAeAB5AHoAYnJhY2VsZWZ0AGJhcgBicmFjZXJpZ2h0AGFzY2lpdGlsZGUAZXhjbGFtZG93bgBjZW50AHN0ZXJsaW5nAGZyYWN0aW9uAHllbgBmbG9yaW4Ac2VjdGlvbgBjdXJyZW5jeQBxdW90ZXNpbmdsZQBxdW90ZWRibGxlZnQAZ3VpbGxlbW90bGVmdABndWlsc2luZ2xsZWZ0AGd1aWxzaW5nbHJpZ2h0AGZpAGZsAGVuZGFzaABkYWdnZXIAZGFnZ2VyZGJsAHBlcmlvZGNlbnRlcmVkAHBhcmFncmFwaABidWxsZXQAcXVvdGVzaW5nbGJhc2UAcXVvdGVkYmxiYXNlAHF1b3RlZGJscmlnaHQAZ3VpbGxlbW90cmlnaHQAZWxsaXBzaXMAcGVydGhvdXNhbmQAcXVlc3Rpb25kb3duAGdyYXZlAGFjdXRlAGNpcmN1bWZsZXgAdGlsZGUAbWFjcm9uAGJyZXZlAGRvdGFjY2VudABkaWVyZXNpcwByaW5nAGNlZGlsbGEAaHVuZ2FydW1sYXV0AG9nb25lawBjYXJvbgBlbWRhc2gAQUUAb3JkZmVtaW5pbmUATHNsYXNoAE9zbGFzaABPRQBvcmRtYXNjdWxpbmUAYWUAZG90bGVzc2kAbHNsYXNoAG9zbGFzaABvZQBnZXJtYW5kYmxzAG9uZXN1cGVyaW9yAGxvZ2ljYWxub3QAbXUAdHJhZGVtYXJrAEV0aABvbmVoYWxmAHBsdXNtaW51cwBUaG9ybgBvbmVxdWFydGVyAGRpdmlkZQBicm9rZW5iYXIAZGVncmVlAHRob3JuAHRocmVlcXVhcnRlcnMAdHdvc3VwZXJpb3IAcmVnaXN0ZXJlZABtaW51cwBldGgAbXVsdGlwbHkAdGhyZWVzdXBlcmlvcgBjb3B5cmlnaHQAQWFjdXRlAEFjaXJjdW1mbGV4AEFkaWVyZXNpcwBBZ3JhdmUAQXJpbmcAQXRpbGRlAENjZWRpbGxhAEVhY3V0ZQBFY2lyY3VtZmxleABFZGllcmVzaXMARWdyYXZlAElhY3V0ZQBJY2lyY3VtZmxleABJZGllcmVzaXMASWdyYXZlAE50aWxkZQBPYWN1dGUAT2NpcmN1bWZsZXgAT2RpZXJlc2lzAE9ncmF2ZQBPdGlsZGUAU2Nhcm9uAFVhY3V0ZQBVY2lyY3VtZmxleABVZGllcmVzaXMAVWdyYXZlAFlhY3V0ZQBZZGllcmVzaXMAWmNhcm9uAGFhY3V0ZQBhY2lyY3VtZmxleABhZGllcmVzaXMAYWdyYXZlAGFyaW5nAGF0aWxkZQBjY2VkaWxsYQBlYWN1dGUAZWNpcmN1bWZsZXgAZWRpZXJlc2lzAGVncmF2ZQBpYWN1dGUAaWNpcmN1bWZsZXgAaWRpZXJlc2lzAGlncmF2ZQBudGlsZGUAb2FjdXRlAG9jaXJjdW1mbGV4AG9kaWVyZXNpcwBvZ3JhdmUAb3RpbGRlAHNjYXJvbgB1YWN1dGUAdWNpcmN1bWZsZXgAdWRpZXJlc2lzAHVncmF2ZQB5YWN1dGUAeWRpZXJlc2lzAHpjYXJvbgBleGNsYW1zbWFsbABIdW5nYXJ1bWxhdXRzbWFsbABkb2xsYXJvbGRzdHlsZQBkb2xsYXJzdXBlcmlvcgBhbXBlcnNhbmRzbWFsbABBY3V0ZXNtYWxsAHBhcmVubGVmdHN1cGVyaW9yAHBhcmVucmlnaHRzdXBlcmlvcgB0d29kb3RlbmxlYWRlcgBvbmVkb3RlbmxlYWRlcgB6ZXJvb2xkc3R5bGUAb25lb2xkc3R5bGUAdHdvb2xkc3R5bGUAdGhyZWVvbGRzdHlsZQBmb3Vyb2xkc3R5bGUAZml2ZW9sZHN0eWxlAHNpeG9sZHN0eWxlAHNldmVub2xkc3R5bGUAZWlnaHRvbGRzdHlsZQBuaW5lb2xkc3R5bGUAY29tbWFzdXBlcmlvcgB0aHJlZXF1YXJ0ZXJzZW1kYXNoAHBlcmlvZHN1cGVyaW9yAHF1ZXN0aW9uc21hbGwAYXN1cGVyaW9yAGJzdXBlcmlvcgBjZW50c3VwZXJpb3IAZHN1cGVyaW9yAGVzdXBlcmlvcgBpc3VwZXJpb3IAbHN1cGVyaW9yAG1zdXBlcmlvcgBuc3VwZXJpb3IAb3N1cGVyaW9yAHJzdXBlcmlvcgBzc3VwZXJpb3IAdHN1cGVyaW9yAGZmAGZmaQBmZmwAcGFyZW5sZWZ0aW5mZXJpb3IAcGFyZW5yaWdodGluZmVyaW9yAENpcmN1bWZsZXhzbWFsbABoeXBoZW5zdXBlcmlvcgBHcmF2ZXNtYWxsAEFzbWFsbABCc21hbGwAQ3NtYWxsAERzbWFsbABFc21hbGwARnNtYWxsAEdzbWFsbABIc21hbGwASXNtYWxsAEpzbWFsbABLc21hbGwATHNtYWxsAE1zbWFsbABOc21hbGwAT3NtYWxsAFBzbWFsbABRc21hbGwAUnNtYWxsAFNzbWFsbABUc21hbGwAVXNtYWxsAFZzbWFsbABXc21hbGwAWHNtYWxsAFlzbWFsbABac21hbGwAY29sb25tb25ldGFyeQBvbmVmaXR0ZWQAcnVwaWFoAFRpbGRlc21hbGwAZXhjbGFtZG93bnNtYWxsAGNlbnRvbGRzdHlsZQBMc2xhc2hzbWFsbABTY2Fyb25zbWFsbABaY2Fyb25zbWFsbABEaWVyZXNpc3NtYWxsAEJyZXZlc21hbGwAQ2Fyb25zbWFsbABEb3RhY2NlbnRzbWFsbABNYWNyb25zbWFsbABmaWd1cmVkYXNoAGh5cGhlbmluZmVyaW9yAE9nb25la3NtYWxsAFJpbmdzbWFsbABDZWRpbGxhc21hbGwAcXVlc3Rpb25kb3duc21hbGwAb25lZWlnaHRoAHRocmVlZWlnaHRocwBmaXZlZWlnaHRocwBzZXZlbmVpZ2h0aHMAb25ldGhpcmQAdHdvdGhpcmRzAHplcm9zdXBlcmlvcgBmb3Vyc3VwZXJpb3IAZml2ZXN1cGVyaW9yAHNpeHN1cGVyaW9yAHNldmVuc3VwZXJpb3IAZWlnaHRzdXBlcmlvcgBuaW5lc3VwZXJpb3IAemVyb2luZmVyaW9yAG9uZWluZmVyaW9yAHR3b2luZmVyaW9yAHRocmVlaW5mZXJpb3IAZm91cmluZmVyaW9yAGZpdmVpbmZlcmlvcgBzaXhpbmZlcmlvcgBzZXZlbmluZmVyaW9yAGVpZ2h0aW5mZXJpb3IAbmluZWluZmVyaW9yAGNlbnRpbmZlcmlvcgBkb2xsYXJpbmZlcmlvcgBwZXJpb2RpbmZlcmlvcgBjb21tYWluZmVyaW9yAEFncmF2ZXNtYWxsAEFhY3V0ZXNtYWxsAEFjaXJjdW1mbGV4c21hbGwAQXRpbGRlc21hbGwAQWRpZXJlc2lzc21hbGwAQXJpbmdzbWFsbABBRXNtYWxsAENjZWRpbGxhc21hbGwARWdyYXZlc21hbGwARWFjdXRlc21hbGwARWNpcmN1bWZsZXhzbWFsbABFZGllcmVzaXNzbWFsbABJZ3JhdmVzbWFsbABJYWN1dGVzbWFsbABJY2lyY3VtZmxleHNtYWxsAElkaWVyZXNpc3NtYWxsAEV0aHNtYWxsAE50aWxkZXNtYWxsAE9ncmF2ZXNtYWxsAE9hY3V0ZXNtYWxsAE9jaXJjdW1mbGV4c21hbGwAT3RpbGRlc21hbGwAT2RpZXJlc2lzc21hbGwAT0VzbWFsbABPc2xhc2hzbWFsbABVZ3JhdmVzbWFsbABVYWN1dGVzbWFsbABVY2lyY3VtZmxleHNtYWxsAFVkaWVyZXNpc3NtYWxsAFlhY3V0ZXNtYWxsAFRob3Juc21hbGwAWWRpZXJlc2lzc21hbGwAMDAxLjAwMAAwMDEuMDAxADAwMS4wMDIAMDAxLjAwMwBCbGFjawBCb2xkAEJvb2sATGlnaHQATWVkaXVtAFJlZ3VsYXIAUm9tYW4AU2VtaWJvbGQA/QAAAAYABQELARIBGwEmAS0BNQH2AkoBVAFfAWgBbQFzAXoBgQGHAYwBkAGUAZoBnwGkAagBrgG0AbkBvwHJAc4B1AHcAeUB6AHqAewB7gHwAfIB9AH2AfgB+gH8Af4BAAICAgQCBgIIAgoCDAIOAhACEgIUAhYCGAIaAhwCKAIyAj8CSwLTA2ACYgJkAmYCaAJqAmwCbgJwAnICdAJ2AngCegJ8An4CgAKCAoQChgKIAooCjAKOApACkgKUAp4CogKtAl8FcAV9BYYFzgXoBRoGQwZgBkoGVgZtBmcGdAZ9BpoGhAaQBqEGvgaoBrQGxQbMBukG0wbfBvAG/gYbBwUHEQdHA+8EwwLIAuUCcQNnA4gEFgVCBa0E2QMHBBcAPgRUBCAAwwQpADMA2gKqBEAATABWAF4AYQBBBF4EagBrBH4ExgO4Ap8EcADeAngAhAAPA6IDsQOKAGkFdgX5BVsEhQRAAzcEAgOUA1YCPwHeBJsAKQcyBtEC7QIdAysDOgM9A04DWAN4A4cDugNTBY0FTAWZBaMFqgWxBb0FxwXVBdwFowDyBQcGDgYkBm4E3wPqA/AD9wP9AxAEFQQdBCoEMQRNBHcEAAb3BjwGMwflBLcEJwUrBiIHzQT2BCEFKwWTBAoFNAW7BNME/ASpAK8AtgC9AMgA0QDaAOEA6ADvAPYAAAAAAAAAAAAAAAAA/QAFAQsBEgEbASYBLQE1AT8BSgFUAV8BaAFtAXMBegGBAYcBjAGQAZQBmgGfAaQBqAGuAbQBuQG/AckBzgHUAdwB5QHoAeoB7AHuAfAB8gH0AfYB+AH6AfwB/gEAAgICBAIGAggCCgIMAg4CEAISAhQCFgIYAhoCHAIoAjICPwJLAlYCYAJiAmQCZgJoAmoCbAJuAnACcgJ0AnYCeAJ6AnwCfgKAAoIChAKGAogCigKMAo4CkAKSApQCngKiAq0CuALDAsgC0QLaAt4C5QLtAvYCAgMPAx0DKwM6Az0DQANHA04DWANnA3EDeAOHA5QDogOxA7oDxgPTA9kD3wPqA/AD9wP9AwcEEAQVBB0EKgQxBDcEPgRBBE0EVARbBF4EawRuBHcEfgSFBIgEkwSfBKoErQS3BLsEwwTNBNME3gTlBO8E9gT8BAoFFgUhBScFKwU0BUIFTAVTBV8FaQVwBXYFfQWG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ZTGAJmYygCZjN4AnCTiAJwg5gCcHMoomHiSyJLYkuiS+JMIkxiTKJM4k0iTWMIAkYDGAJGEygCRiM4AkYzSAJGQ1gCRlNoAkZjeAJGc4gCRoOYAkaTOKJwwk8iT2JPok/iUCJQYlCiUOJRIlFjCAJ3YxgCd3MoAneDOAJ3k0gCd6NYAnezaAJ3w3gCd9OIAnfjmAJ380iicNJTIlNiU6JT4lQiVGJUolTiVSJVYwgCeAMYAngTKAJ4IzgCeDNIAnhDWAJ4U2gCeGN4AnhziAJ4g5gCeJNYonDiVyJXYleiV+JYIlhiWKJY4lkiWWMIAnijGAJ4sygCeMM4AnjTSAJ441gCePNoAnkDeAJ5E4gCeSOYAnkzaKJw8lsiW2JbolviXCJcYlyiXOJdIl1jCAJ5QxgCGSMoAnozOAIZQ0gCGVNYAnmTaAJ5s3gCecOIAnnTmAJ543iicRJfIl9iX6Jf4mAiYGJgomDiYSJhYwgCefMYAnoDKAJ6EzgCeiNIAnpDWAJ6U2gCemN4AnpziAJ6g5gCepOIonEiYyJjYmOiY+JkImRiZKJk4mUiZWMIAnqzGAJ60ygCevM4AnsjSAJ7M1gCe1NoAnuDeAJ7o4gCe7OYAnvDmKJxMmciZ2JnomfiaCJoYmiiaOJpImljCAJ70xgCe+MoAnmjOAJ6o0gCe2NYAnuTaAJ5g3gCe0OIAntzmAJ6wyiicCJrIm4CbkJugm7CbwJvQm+Cb8JwAwhycUJsQmyCbMJtAm1CbYJtwwgCeuMYAnsTKAJwMzgCdQNIAnUjWAJ242gCdwMYAnFTKAJxYzgCcXNIAnGDWAJxk2gCcaN4AnGziAJxw5gCciM4onBCccJyAnJCcoJywnMCc0JzgnPCdAMIAnIzGAJyQygCclM4AnJjSAJyc1gCYFNoAnKTeAJyo4gCcrOYAnLDSKJg4nXCdgJ2QnaCdsJ3AndCd4J3wngDCAJy0xgCcuMoAnLzOAJzA0gCcxNYAnMjaAJzM3gCc0OIAnNTmAJzY1iicGJ5wnoCekJ6gnrCewJ7QnuCe8J8AwgCc3MYAnODKAJzkzgCc6NIAnOzWAJzw2gCc9N4AnPjiAJz85gCdANoonHSfcJ+An5CfoJ+wn8Cf0J/gn/CgAMIAnQTGAJ0IygCdDM4AnRDSAJ0U1gCdGNoAnRzeAJ0g4gCdJOYAnSjeKJx4o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",isDataURI(wasmBinaryFile)||(wasmBinaryFile=locateFile(wasmBinaryFile));function getBinary(A){try{if(A==wasmBinaryFile&&wasmBinary)return new Uint8Array(wasmBinary);var I=tryParseAsDataURI(A);if(I)return I;if(readBinary)return readBinary(A);throw"both async and sync fetching of the wasm failed"}catch(g){abort(g)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)){if(typeof fetch=="function"&&!isFileURI(wasmBinaryFile))return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(A){if(!A.ok)throw"failed to load wasm binary file at '"+wasmBinaryFile+"'";return A.arrayBuffer()}).catch(function(){return getBinary(wasmBinaryFile)});if(readAsync)return new Promise(function(A,I){readAsync(wasmBinaryFile,function(g){A(new Uint8Array(g))},I)})}return Promise.resolve().then(function(){return getBinary(wasmBinaryFile)})}function createWasm(){var A={a:asmLibraryArg};function I(B,D){var i=B.exports;Module.asm=i,wasmMemory=Module.asm.u,updateGlobalBufferAndViews(wasmMemory.buffer),wasmTable=Module.asm.K,addOnInit(Module.asm.v),removeRunDependency()}addRunDependency();function g(B){I(B.instance)}function C(B){return getBinaryPromise().then(function(D){return WebAssembly.instantiate(D,A)}).then(function(D){return D}).then(B,function(D){err("failed to asynchronously prepare wasm: "+D),abort(D)})}function Q(){return!wasmBinary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch=="function"?fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(B){var D=WebAssembly.instantiateStreaming(B,A);return D.then(g,function(i){return err("wasm streaming compile failed: "+i),err("falling back to ArrayBuffer instantiation"),C(g)})}):C(g)}if(Module.instantiateWasm)try{var E=Module.instantiateWasm(A,I);return E}catch(B){return err("Module.instantiateWasm callback failed with error: "+B),!1}return Q().catch(readyPromiseReject),{}}function callRuntimeCallbacks(A){for(;A.length>0;){var I=A.shift();if(typeof I=="function"){I(Module);continue}var g=I.func;typeof g=="number"?I.arg===void 0?getWasmTableEntry(g)():getWasmTableEntry(g)(I.arg):g(I.arg===void 0?null:I.arg)}}function getWasmTableEntry(A){return wasmTable.get(A)}function __embind_register_bigint(A,I,g,C,Q){}function getShiftFromSize(A){switch(A){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw new TypeError("Unknown type size: "+A)}}function embind_init_charCodes(){for(var A=new Array(256),I=0;I<256;++I)A[I]=String.fromCharCode(I);embind_charCodes=A}var embind_charCodes=void 0;function readLatin1String(A){for(var I="",g=A;HEAPU8[g];)I+=embind_charCodes[HEAPU8[g++]];return I}var awaitingDependencies={},registeredTypes={},char_0=48,char_9=57;function makeLegalFunctionName(A){if(A===void 0)return"_unknown";A=A.replace(/[^a-zA-Z0-9_]/g,"$");var I=A.charCodeAt(0);return I>=char_0&&I<=char_9?"_"+A:A}function createNamedFunction(A,I){return A=makeLegalFunctionName(A),new Function("body","return function "+A+`() {
+ "use strict"; return body.apply(this, arguments);
+};
+`)(I)}function extendError(A,I){var g=createNamedFunction(I,function(C){this.name=I,this.message=C;var Q=new Error(C).stack;Q!==void 0&&(this.stack=this.toString()+`
+`+Q.replace(/^Error(:[^\n]*)?\n/,""))});return g.prototype=Object.create(A.prototype),g.prototype.constructor=g,g.prototype.toString=function(){return this.message===void 0?this.name:this.name+": "+this.message},g}var BindingError=void 0;function throwBindingError(A){throw new BindingError(A)}function registerType(A,I,g){if(g=g||{},!("argPackAdvance"in I))throw new TypeError("registerType registeredInstance requires argPackAdvance");var C=I.name;if(A||throwBindingError('type "'+C+'" must have a positive integer typeid pointer'),registeredTypes.hasOwnProperty(A)){if(g.ignoreDuplicateRegistrations)return;throwBindingError("Cannot register type '"+C+"' twice")}if(registeredTypes[A]=I,awaitingDependencies.hasOwnProperty(A)){var Q=awaitingDependencies[A];delete awaitingDependencies[A],Q.forEach(function(E){E()})}}function __embind_register_bool(A,I,g,C,Q){var E=getShiftFromSize(g);I=readLatin1String(I),registerType(A,{name:I,fromWireType:function(B){return!!B},toWireType:function(B,D){return D?C:Q},argPackAdvance:8,readValueFromPointer:function(B){var D;if(g===1)D=HEAP8;else if(g===2)D=HEAP16;else if(g===4)D=HEAP32;else throw new TypeError("Unknown boolean type size: "+I);return this.fromWireType(D[B>>E])},destructorFunction:null})}var emval_free_list=[],emval_handle_array=[{},{value:void 0},{value:null},{value:!0},{value:!1}];function __emval_decref(A){A>4&&--emval_handle_array[A].refcount===0&&(emval_handle_array[A]=void 0,emval_free_list.push(A))}function count_emval_handles(){for(var A=0,I=5;I<emval_handle_array.length;++I)emval_handle_array[I]!==void 0&&++A;return A}function get_first_emval(){for(var A=5;A<emval_handle_array.length;++A)if(emval_handle_array[A]!==void 0)return emval_handle_array[A];return null}function init_emval(){Module.count_emval_handles=count_emval_handles,Module.get_first_emval=get_first_emval}var Emval={toValue:function(A){return A||throwBindingError("Cannot use deleted val. handle = "+A),emval_handle_array[A].value},toHandle:function(A){switch(A){case void 0:return 1;case null:return 2;case!0:return 3;case!1:return 4;default:{var I=emval_free_list.length?emval_free_list.pop():emval_handle_array.length;return emval_handle_array[I]={refcount:1,value:A},I}}}};function simpleReadValueFromPointer(A){return this.fromWireType(HEAPU32[A>>2])}function __embind_register_emval(A,I){I=readLatin1String(I),registerType(A,{name:I,fromWireType:function(g){var C=Emval.toValue(g);return __emval_decref(g),C},toWireType:function(g,C){return Emval.toHandle(C)},argPackAdvance:8,readValueFromPointer:simpleReadValueFromPointer,destructorFunction:null})}function floatReadValueFromPointer(A,I){switch(I){case 2:return function(g){return this.fromWireType(HEAPF32[g>>2])};case 3:return function(g){return this.fromWireType(HEAPF64[g>>3])};default:throw new TypeError("Unknown float type: "+A)}}function __embind_register_float(A,I,g){var C=getShiftFromSize(g);I=readLatin1String(I),registerType(A,{name:I,fromWireType:function(Q){return Q},toWireType:function(Q,E){return E},argPackAdvance:8,readValueFromPointer:floatReadValueFromPointer(I,C),destructorFunction:null})}function integerReadValueFromPointer(A,I,g){switch(I){case 0:return g?function(Q){return HEAP8[Q]}:function(Q){return HEAPU8[Q]};case 1:return g?function(Q){return HEAP16[Q>>1]}:function(Q){return HEAPU16[Q>>1]};case 2:return g?function(Q){return HEAP32[Q>>2]}:function(Q){return HEAPU32[Q>>2]};default:throw new TypeError("Unknown integer type: "+A)}}function __embind_register_integer(A,I,g,C,Q){I=readLatin1String(I);var E=getShiftFromSize(g),B=function(F){return F};if(C===0){var D=32-8*g;B=function(F){return F<<D>>>D}}var i=I.includes("unsigned"),o=function(F,y){},w;i?w=function(F,y){return o(y,this.name),y>>>0}:w=function(F,y){return o(y,this.name),y},registerType(A,{name:I,fromWireType:B,toWireType:w,argPackAdvance:8,readValueFromPointer:integerReadValueFromPointer(I,E,C!==0),destructorFunction:null})}function __embind_register_memory_view(A,I,g){var C=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array],Q=C[I];function E(B){B=B>>2;var D=HEAPU32,i=D[B],o=D[B+1];return new Q(buffer,o,i)}g=readLatin1String(g),registerType(A,{name:g,fromWireType:E,argPackAdvance:8,readValueFromPointer:E},{ignoreDuplicateRegistrations:!0})}function __embind_register_std_string(A,I){I=readLatin1String(I);var g=I==="std::string";registerType(A,{name:I,fromWireType:function(C){var Q=HEAPU32[C>>2],E;if(g)for(var B=C+4,D=0;D<=Q;++D){var i=C+4+D;if(D==Q||HEAPU8[i]==0){var o=i-B,w=UTF8ToString(B,o);E===void 0?E=w:(E+="\0",E+=w),B=i+1}}else{for(var F=new Array(Q),D=0;D<Q;++D)F[D]=String.fromCharCode(HEAPU8[C+4+D]);E=F.join("")}return _free(C),E},toWireType:function(C,Q){Q instanceof ArrayBuffer&&(Q=new Uint8Array(Q));var E,B=typeof Q=="string";B||Q instanceof Uint8Array||Q instanceof Uint8ClampedArray||Q instanceof Int8Array||throwBindingError("Cannot pass non-string to std::string"),g&&B?E=function(){return lengthBytesUTF8(Q)}:E=function(){return Q.length};var D=E(),i=_malloc(4+D+1);if(HEAPU32[i>>2]=D,g&&B)stringToUTF8(Q,i+4,D+1);else if(B)for(var o=0;o<D;++o){var w=Q.charCodeAt(o);w>255&&(_free(i),throwBindingError("String has UTF-16 code units that do not fit in 8 bits")),HEAPU8[i+4+o]=w}else for(var o=0;o<D;++o)HEAPU8[i+4+o]=Q[o];return C!==null&&C.push(_free,i),i},argPackAdvance:8,readValueFromPointer:simpleReadValueFromPointer,destructorFunction:function(C){_free(C)}})}function __embind_register_std_wstring(A,I,g){g=readLatin1String(g);var C,Q,E,B,D;I===2?(C=UTF16ToString,Q=stringToUTF16,B=lengthBytesUTF16,E=function(){return HEAPU16},D=1):I===4&&(C=UTF32ToString,Q=stringToUTF32,B=lengthBytesUTF32,E=function(){return HEAPU32},D=2),registerType(A,{name:g,fromWireType:function(i){for(var o=HEAPU32[i>>2],w=E(),F,y=i+4,N=0;N<=o;++N){var h=i+4+N*I;if(N==o||w[h>>D]==0){var G=h-y,s=C(y,G);F===void 0?F=s:(F+="\0",F+=s),y=h+I}}return _free(i),F},toWireType:function(i,o){typeof o!="string"&&throwBindingError("Cannot pass non-string to C++ string type "+g);var w=B(o),F=_malloc(4+w+I);return HEAPU32[F>>2]=w>>D,Q(o,F+4,w+I),i!==null&&i.push(_free,F),F},argPackAdvance:8,readValueFromPointer:simpleReadValueFromPointer,destructorFunction:function(i){_free(i)}})}function __embind_register_void(A,I){I=readLatin1String(I),registerType(A,{isVoid:!0,name:I,argPackAdvance:0,fromWireType:function(){},toWireType:function(g,C){}})}function __emscripten_throw_longjmp(){throw"longjmp"}function _emscripten_memcpy_big(A,I,g){HEAPU8.copyWithin(A,I,I+g)}function emscripten_realloc_buffer(A){try{return wasmMemory.grow(A-buffer.byteLength+65535>>>16),updateGlobalBufferAndViews(wasmMemory.buffer),1}catch{}}function _emscripten_resize_heap(A){var I=HEAPU8.length;A=A>>>0;var g=2147483648;if(A>g)return!1;for(var C=1;C<=4;C*=2){var Q=I*(1+.2/C);Q=Math.min(Q,A+100663296);var E=Math.min(g,alignUp(Math.max(A,Q),65536)),B=emscripten_realloc_buffer(E);if(B)return!0}return!1}function _emscripten_run_script(ptr){eval(UTF8ToString(ptr))}var ENV={};function getExecutableName(){return thisProgram||"./this.program"}function getEnvStrings(){if(!getEnvStrings.strings){var A=(typeof navigator=="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",I={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:A,_:getExecutableName()};for(var g in ENV)ENV[g]===void 0?delete I[g]:I[g]=ENV[g];var C=[];for(var g in I)C.push(g+"="+I[g]);getEnvStrings.strings=C}return getEnvStrings.strings}function _environ_get(A,I){var g=0;return getEnvStrings().forEach(function(C,Q){var E=I+g;HEAP32[A+Q*4>>2]=E,writeAsciiToMemory(C,E),g+=C.length+1}),0}function _environ_sizes_get(A,I){var g=getEnvStrings();HEAP32[A>>2]=g.length;var C=0;return g.forEach(function(Q){C+=Q.length+1}),HEAP32[I>>2]=C,0}function _getTempRet0(){return getTempRet0()}function _setTempRet0(A){setTempRet0(A)}embind_init_charCodes(),BindingError=Module.BindingError=extendError(Error,"BindingError"),Module.InternalError=extendError(Error,"InternalError"),init_emval();function intArrayToString(A){for(var I=[],g=0;g<A.length;g++){var C=A[g];C>255&&(C&=255),I.push(String.fromCharCode(C))}return I.join("")}var decodeBase64=typeof atob=="function"?atob:function(A){var I="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",g="",C,Q,E,B,D,i,o,w=0;A=A.replace(/[^A-Za-z0-9\+\/\=]/g,"");do B=I.indexOf(A.charAt(w++)),D=I.indexOf(A.charAt(w++)),i=I.indexOf(A.charAt(w++)),o=I.indexOf(A.charAt(w++)),C=B<<2|D>>4,Q=(D&15)<<4|i>>2,E=(i&3)<<6|o,g=g+String.fromCharCode(C),i!==64&&(g=g+String.fromCharCode(Q)),o!==64&&(g=g+String.fromCharCode(E));while(w<A.length);return g};function intArrayFromBase64(A){if(typeof ENVIRONMENT_IS_NODE=="boolean"&&ENVIRONMENT_IS_NODE){var I=Buffer.from(A,"base64");return new Uint8Array(I.buffer,I.byteOffset,I.byteLength)}try{for(var g=decodeBase64(A),C=new Uint8Array(g.length),Q=0;Q<g.length;++Q)C[Q]=g.charCodeAt(Q);return C}catch{throw new Error("Converting base64 string to bytes failed.")}}function tryParseAsDataURI(A){if(isDataURI(A))return intArrayFromBase64(A.slice(dataURIPrefix.length))}var asmLibraryArg={l:__embind_register_bigint,k:__embind_register_bool,j:__embind_register_emval,g:__embind_register_float,b:__embind_register_integer,a:__embind_register_memory_view,f:__embind_register_std_string,e:__embind_register_std_wstring,s:__embind_register_void,q:__emscripten_throw_longjmp,r:_emscripten_memcpy_big,i:_emscripten_resize_heap,t:_emscripten_run_script,o:_environ_get,p:_environ_sizes_get,c:_getTempRet0,n:invoke_iii,h:invoke_iiii,m:invoke_iiiii,d:_setTempRet0};createWasm(),Module.___wasm_call_ctors=function(){return(Module.___wasm_call_ctors=Module.asm.v).apply(null,arguments)},Module._init_constants=function(){return(Module._init_constants=Module.asm.w).apply(null,arguments)},Module._FT_Init_FreeType=function(){return(Module._FT_Init_FreeType=Module.asm.x).apply(null,arguments)},Module._FT_Done_FreeType=function(){return(Module._FT_Done_FreeType=Module.asm.y).apply(null,arguments)},Module._FT_Load_Glyph=function(){return(Module._FT_Load_Glyph=Module.asm.z).apply(null,arguments)},Module._FT_Render_Glyph=function(){return(Module._FT_Render_Glyph=Module.asm.A).apply(null,arguments)},Module._FT_Get_Char_Index=function(){return(Module._FT_Get_Char_Index=Module.asm.B).apply(null,arguments)},Module._FT_Done_Face=function(){return(Module._FT_Done_Face=Module.asm.C).apply(null,arguments)},Module._FT_New_Memory_Face=function(){return(Module._FT_New_Memory_Face=Module.asm.D).apply(null,arguments)},Module._FT_Set_Char_Size=function(){return(Module._FT_Set_Char_Size=Module.asm.E).apply(null,arguments)},Module._FT_Set_Pixel_Sizes=function(){return(Module._FT_Set_Pixel_Sizes=Module.asm.F).apply(null,arguments)},Module._FT_Get_Kerning=function(){return(Module._FT_Get_Kerning=Module.asm.G).apply(null,arguments)},Module._FT_Get_Sfnt_Table=function(){return(Module._FT_Get_Sfnt_Table=Module.asm.H).apply(null,arguments)},Module.___getTypeName=function(){return(Module.___getTypeName=Module.asm.I).apply(null,arguments)},Module.___embind_register_native_and_builtin_types=function(){return(Module.___embind_register_native_and_builtin_types=Module.asm.J).apply(null,arguments)};var _malloc=Module._malloc=function(){return(_malloc=Module._malloc=Module.asm.L).apply(null,arguments)},_setThrew=Module._setThrew=function(){return(_setThrew=Module._setThrew=Module.asm.M).apply(null,arguments)},stackSave=Module.stackSave=function(){return(stackSave=Module.stackSave=Module.asm.N).apply(null,arguments)},stackRestore=Module.stackRestore=function(){return(stackRestore=Module.stackRestore=Module.asm.O).apply(null,arguments)},stackAlloc=Module.stackAlloc=function(){return(stackAlloc=Module.stackAlloc=Module.asm.P).apply(null,arguments)},_free=Module._free=function(){return(_free=Module._free=Module.asm.Q).apply(null,arguments)};function invoke_iii(A,I,g){var C=stackSave();try{return getWasmTableEntry(A)(I,g)}catch(Q){if(stackRestore(C),Q!==Q+0&&Q!=="longjmp")throw Q;_setThrew(1,0)}}function invoke_iiiii(A,I,g,C,Q){var E=stackSave();try{return getWasmTableEntry(A)(I,g,C,Q)}catch(B){if(stackRestore(E),B!==B+0&&B!=="longjmp")throw B;_setThrew(1,0)}}function invoke_iiii(A,I,g,C){var Q=stackSave();try{return getWasmTableEntry(A)(I,g,C)}catch(E){if(stackRestore(Q),E!==E+0&&E!=="longjmp")throw E;_setThrew(1,0)}}Module.ccall=ccall,Module.cwrap=cwrap,Module.getValue=getValue,Module.writeArrayToMemory=writeArrayToMemory;var calledRun;dependenciesFulfilled=function A(){calledRun||run(),calledRun||(dependenciesFulfilled=A)};function run(A){if(runDependencies>0||(preRun(),runDependencies>0))return;function I(){calledRun||(calledRun=!0,Module.calledRun=!0,!ABORT&&(initRuntime(),readyPromiseResolve(Module),Module.onRuntimeInitialized&&Module.onRuntimeInitialized(),postRun()))}Module.setStatus?(Module.setStatus("Running..."),setTimeout(function(){setTimeout(function(){Module.setStatus("")},1),I()},1)):I()}if(Module.run=run,Module.preInit)for(typeof Module.preInit=="function"&&(Module.preInit=[Module.preInit]);Module.preInit.length>0;)Module.preInit.pop()();return run(),Module.ready}})();export{Module as default};
diff --git a/xiaozhi-esp32-server/main/manager-web/public/generator/assets/index-B3ns2K1c.css b/xiaozhi-esp32-server/main/manager-web/public/generator/assets/index-B3ns2K1c.css
new file mode 100644
index 0000000..fe32124
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-web/public/generator/assets/index-B3ns2K1c.css
@@ -0,0 +1 @@
+*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.-right-1{right:-.25rem}.-top-1{top:-.25rem}.bottom-4{bottom:1rem}.right-0{right:0}.right-4{right:1rem}.z-10{z-index:10}.z-40{z-index:40}.z-50{z-index:50}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.-mb-px{margin-bottom:-1px}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.ml-auto{margin-left:auto}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.aspect-square{aspect-ratio:1 / 1}.h-0\.5{height:.125rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-20{height:5rem}.h-3{height:.75rem}.h-32{height:8rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-full{height:100%}.max-h-32{max-height:8rem}.max-h-64{max-height:16rem}.max-h-\[90vh\]{max-height:90vh}.min-h-96{min-height:24rem}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-11{width:2.75rem}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-20{width:5rem}.w-24{width:6rem}.w-3{width:.75rem}.w-4{width:1rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-8{width:2rem}.w-full{width:100%}.min-w-0{min-width:0px}.min-w-\[300px\]{min-width:300px}.max-w-2xl{max-width:42rem}.max-w-32{max-width:8rem}.max-w-7xl{max-width:80rem}.max-w-full{max-width:100%}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.grow{flex-grow:1}.rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.resize{resize:both}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-items-center{justify-items:center}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-2\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.25rem * var(--tw-space-x-reverse));margin-left:calc(.25rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.75rem * var(--tw-space-x-reverse));margin-left:calc(.75rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1rem * var(--tw-space-x-reverse));margin-left:calc(1rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.break-words{overflow-wrap:break-word}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-blue-200{--tw-border-opacity: 1;border-color:rgb(191 219 254 / var(--tw-border-opacity, 1))}.border-blue-500{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.border-gray-100{--tw-border-opacity: 1;border-color:rgb(243 244 246 / var(--tw-border-opacity, 1))}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1))}.border-gray-300{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1))}.border-gray-700{--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity, 1))}.border-green-200{--tw-border-opacity: 1;border-color:rgb(187 247 208 / var(--tw-border-opacity, 1))}.border-green-300{--tw-border-opacity: 1;border-color:rgb(134 239 172 / var(--tw-border-opacity, 1))}.border-orange-200{--tw-border-opacity: 1;border-color:rgb(254 215 170 / var(--tw-border-opacity, 1))}.border-primary-500{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.border-red-300{--tw-border-opacity: 1;border-color:rgb(252 165 165 / var(--tw-border-opacity, 1))}.border-red-500{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity, 1))}.border-transparent{border-color:transparent}.border-yellow-200{--tw-border-opacity: 1;border-color:rgb(254 240 138 / var(--tw-border-opacity, 1))}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-blue-50{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.bg-gray-200{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}.bg-gray-300{--tw-bg-opacity: 1;background-color:rgb(209 213 219 / var(--tw-bg-opacity, 1))}.bg-gray-400{--tw-bg-opacity: 1;background-color:rgb(156 163 175 / var(--tw-bg-opacity, 1))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.bg-gray-500{--tw-bg-opacity: 1;background-color:rgb(107 114 128 / var(--tw-bg-opacity, 1))}.bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity, 1))}.bg-green-50{--tw-bg-opacity: 1;background-color:rgb(240 253 244 / var(--tw-bg-opacity, 1))}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-orange-50{--tw-bg-opacity: 1;background-color:rgb(255 247 237 / var(--tw-bg-opacity, 1))}.bg-pink-100{--tw-bg-opacity: 1;background-color:rgb(252 231 243 / var(--tw-bg-opacity, 1))}.bg-primary-50{--tw-bg-opacity: 1;background-color:rgb(240 249 255 / var(--tw-bg-opacity, 1))}.bg-primary-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-red-400{--tw-bg-opacity: 1;background-color:rgb(248 113 113 / var(--tw-bg-opacity, 1))}.bg-red-50{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity, 1))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-sky-100{--tw-bg-opacity: 1;background-color:rgb(224 242 254 / var(--tw-bg-opacity, 1))}.bg-stone-100{--tw-bg-opacity: 1;background-color:rgb(245 245 244 / var(--tw-bg-opacity, 1))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-yellow-100{--tw-bg-opacity: 1;background-color:rgb(254 249 195 / var(--tw-bg-opacity, 1))}.bg-yellow-50{--tw-bg-opacity: 1;background-color:rgb(254 252 232 / var(--tw-bg-opacity, 1))}.bg-opacity-10{--tw-bg-opacity: .1}.bg-opacity-50{--tw-bg-opacity: .5}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pr-10{padding-right:2.5rem}.pt-4{padding-top:1rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.leading-tight{line-height:1.25}.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity, 1))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-blue-700{--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity, 1))}.text-blue-800{--tw-text-opacity: 1;color:rgb(30 64 175 / var(--tw-text-opacity, 1))}.text-blue-900{--tw-text-opacity: 1;color:rgb(30 58 138 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.text-gray-800{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity, 1))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-green-600{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity, 1))}.text-green-700{--tw-text-opacity: 1;color:rgb(21 128 61 / var(--tw-text-opacity, 1))}.text-green-800{--tw-text-opacity: 1;color:rgb(22 101 52 / var(--tw-text-opacity, 1))}.text-green-900{--tw-text-opacity: 1;color:rgb(20 83 45 / var(--tw-text-opacity, 1))}.text-indigo-500{--tw-text-opacity: 1;color:rgb(99 102 241 / var(--tw-text-opacity, 1))}.text-orange-600{--tw-text-opacity: 1;color:rgb(234 88 12 / var(--tw-text-opacity, 1))}.text-orange-800{--tw-text-opacity: 1;color:rgb(154 52 18 / var(--tw-text-opacity, 1))}.text-pink-500{--tw-text-opacity: 1;color:rgb(236 72 153 / var(--tw-text-opacity, 1))}.text-primary-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-primary-700{--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity, 1))}.text-purple-500{--tw-text-opacity: 1;color:rgb(168 85 247 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity, 1))}.text-yellow-600{--tw-text-opacity: 1;color:rgb(202 138 4 / var(--tw-text-opacity, 1))}.text-yellow-700{--tw-text-opacity: 1;color:rgb(161 98 7 / var(--tw-text-opacity, 1))}.text-yellow-900{--tw-text-opacity: 1;color:rgb(113 63 18 / var(--tw-text-opacity, 1))}.opacity-0{opacity:0}.opacity-25{opacity:.25}.opacity-60{opacity:.6}.opacity-75{opacity:.75}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-inner{--tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / .05);--tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline{outline-style:solid}.grayscale{--tw-grayscale: grayscale(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}.step-indicator{display:flex;height:2rem;width:2rem;align-items:center;justify-content:center;border-radius:9999px;font-size:.875rem;line-height:1.25rem;font-weight:500}.step-indicator.active{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.step-indicator.completed{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.step-indicator.inactive{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.tab-button{border-top-left-radius:.5rem;border-top-right-radius:.5rem;border-bottom-width:2px;padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem;font-weight:500;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.tab-button.active{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.tab-button.inactive{border-color:transparent;--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.tab-button.inactive:hover{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1));--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:left-\[2px\]:after{content:var(--tw-content);left:2px}.after\:top-\[2px\]:after{content:var(--tw-content);top:2px}.after\:h-5:after{content:var(--tw-content);height:1.25rem}.after\:w-5:after{content:var(--tw-content);width:1.25rem}.after\:rounded-full:after{content:var(--tw-content);border-radius:9999px}.after\:border:after{content:var(--tw-content);border-width:1px}.after\:border-gray-300:after{content:var(--tw-content);--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1))}.after\:bg-white:after{content:var(--tw-content);--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.after\:transition-all:after{content:var(--tw-content);transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.after\:content-\[\'\'\]:after{--tw-content: "";content:var(--tw-content)}.hover\:border-gray-300:hover{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1))}.hover\:border-gray-400:hover{--tw-border-opacity: 1;border-color:rgb(156 163 175 / var(--tw-border-opacity, 1))}.hover\:bg-blue-600:hover{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-200:hover{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-50:hover{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-600:hover{--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity, 1))}.hover\:bg-green-600:hover{--tw-bg-opacity: 1;background-color:rgb(22 163 74 / var(--tw-bg-opacity, 1))}.hover\:bg-pink-200:hover{--tw-bg-opacity: 1;background-color:rgb(251 207 232 / var(--tw-bg-opacity, 1))}.hover\:bg-primary-600:hover{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.hover\:bg-red-600:hover{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity, 1))}.hover\:bg-sky-200:hover{--tw-bg-opacity: 1;background-color:rgb(186 230 253 / var(--tw-bg-opacity, 1))}.hover\:bg-stone-200:hover{--tw-bg-opacity: 1;background-color:rgb(231 229 228 / var(--tw-bg-opacity, 1))}.hover\:bg-yellow-200:hover{--tw-bg-opacity: 1;background-color:rgb(254 240 138 / var(--tw-bg-opacity, 1))}.hover\:text-gray-500:hover{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.hover\:text-gray-700:hover{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.hover\:text-gray-900:hover{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.hover\:text-primary-500:hover{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity, 1))}.hover\:text-red-500:hover{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.hover\:text-red-800:hover{--tw-text-opacity: 1;color:rgb(153 27 27 / var(--tw-text-opacity, 1))}.focus\:border-primary-500:focus{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.focus\:bg-gray-100:focus{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-blue-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.focus\:ring-primary-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}.disabled\:bg-gray-300:disabled{--tw-bg-opacity: 1;background-color:rgb(209 213 219 / var(--tw-bg-opacity, 1))}.peer:checked~.peer-checked\:bg-primary-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.peer:checked~.peer-checked\:after\:translate-x-full:after{content:var(--tw-content);--tw-translate-x: 100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.peer:checked~.peer-checked\:after\:border-white:after{content:var(--tw-content);--tw-border-opacity: 1;border-color:rgb(255 255 255 / var(--tw-border-opacity, 1))}.peer:focus~.peer-focus\:outline-none{outline:2px solid transparent;outline-offset:2px}.peer:focus~.peer-focus\:ring-4{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}@media (min-width: 640px){.sm\:mr-2{margin-right:.5rem}.sm\:inline{display:inline}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:gap-0{gap:0px}.sm\:space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(2rem * var(--tw-space-x-reverse));margin-left:calc(2rem * calc(1 - var(--tw-space-x-reverse)))}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}}@media (min-width: 768px){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (min-width: 1024px){.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:w-80{width:20rem}.lg\:grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:px-8{padding-left:2rem;padding-right:2rem}}.emoji-container[data-v-0726c332]{display:flex;align-items:center;justify-content:center}.emoji-image[data-v-0726c332]{border-radius:8px;-o-object-fit:contain;object-fit:contain}.emoji-fallback[data-v-0726c332]{display:flex;align-items:center;justify-content:center}.text-message[data-v-0726c332]{line-height:1;word-wrap:break-word}
diff --git a/xiaozhi-esp32-server/main/manager-web/public/generator/assets/index-Guo1hQ-y.js b/xiaozhi-esp32-server/main/manager-web/public/generator/assets/index-Guo1hQ-y.js
new file mode 100644
index 0000000..45dcae7
--- /dev/null
+++ b/xiaozhi-esp32-server/main/manager-web/public/generator/assets/index-Guo1hQ-y.js
@@ -0,0 +1,81 @@
+(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))i(o);new MutationObserver(o=>{for(const n of o)if(n.type==="childList")for(const r of n.addedNodes)r.tagName==="LINK"&&r.rel==="modulepreload"&&i(r)}).observe(document,{childList:!0,subtree:!0});function t(o){const n={};return o.integrity&&(n.integrity=o.integrity),o.referrerPolicy&&(n.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?n.credentials="include":o.crossOrigin==="anonymous"?n.credentials="omit":n.credentials="same-origin",n}function i(o){if(o.ep)return;o.ep=!0;const n=t(o);fetch(o.href,n)}})();/**
+* @vue/shared v3.5.26
+* (c) 2018-present Yuxi (Evan) You and Vue contributors
+* @license MIT
+**/function Vr(A){const e=Object.create(null);for(const t of A.split(","))e[t]=1;return t=>t in e}const _A={},fi=[],nt=()=>{},iI=()=>!1,Bn=A=>A.charCodeAt(0)===111&&A.charCodeAt(1)===110&&(A.charCodeAt(2)>122||A.charCodeAt(2)<97),Wr=A=>A.startsWith("onUpdate:"),ue=Object.assign,qr=(A,e)=>{const t=A.indexOf(e);t>-1&&A.splice(t,1)},oB=Object.prototype.hasOwnProperty,JA=(A,e)=>oB.call(A,e),yA=Array.isArray,hi=A=>co(A)==="[object Map]",Qn=A=>co(A)==="[object Set]",Lg=A=>co(A)==="[object Date]",FA=A=>typeof A=="function",ee=A=>typeof A=="string",It=A=>typeof A=="symbol",KA=A=>A!==null&&typeof A=="object",oI=A=>(KA(A)||FA(A))&&FA(A.then)&&FA(A.catch),nI=Object.prototype.toString,co=A=>nI.call(A),nB=A=>co(A).slice(8,-1),rI=A=>co(A)==="[object Object]",jr=A=>ee(A)&&A!=="NaN"&&A[0]!=="-"&&""+parseInt(A,10)===A,Oi=Vr(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),cn=A=>{const e=Object.create(null);return t=>e[t]||(e[t]=A(t))},rB=/-\w/g,Je=cn(A=>A.replace(rB,e=>e.slice(1).toUpperCase())),gB=/\B([A-Z])/g,Ii=cn(A=>A.replace(gB,"-$1").toLowerCase()),En=cn(A=>A.charAt(0).toUpperCase()+A.slice(1)),xn=cn(A=>A?`on${En(A)}`:""),Yt=(A,e)=>!Object.is(A,e),To=(A,...e)=>{for(let t=0;t<A.length;t++)A[t](...e)},gI=(A,e,t,i=!1)=>{Object.defineProperty(A,e,{configurable:!0,enumerable:!1,writable:i,value:t})},un=A=>{const e=parseFloat(A);return isNaN(e)?A:e};let Ug;const fn=()=>Ug||(Ug=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function VA(A){if(yA(A)){const e={};for(let t=0;t<A.length;t++){const i=A[t],o=ee(i)?CB(i):VA(i);if(o)for(const n in o)e[n]=o[n]}return e}else if(ee(A)||KA(A))return A}const sB=/;(?![^(]*\))/g,aB=/:([^]+)/,IB=/\/\*[^]*?\*\//g;function CB(A){const e={};return A.replace(IB,"").split(sB).forEach(t=>{if(t){const i=t.split(aB);i.length>1&&(e[i[0].trim()]=i[1].trim())}}),e}function kA(A){let e="";if(ee(A))e=A;else if(yA(A))for(let t=0;t<A.length;t++){const i=kA(A[t]);i&&(e+=i+" ")}else if(KA(A))for(const t in A)A[t]&&(e+=t+" ");return e.trim()}const lB="itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly",BB=Vr(lB);function sI(A){return!!A||A===""}function QB(A,e){if(A.length!==e.length)return!1;let t=!0;for(let i=0;t&&i<A.length;i++)t=hn(A[i],e[i]);return t}function hn(A,e){if(A===e)return!0;let t=Lg(A),i=Lg(e);if(t||i)return t&&i?A.getTime()===e.getTime():!1;if(t=It(A),i=It(e),t||i)return A===e;if(t=yA(A),i=yA(e),t||i)return t&&i?QB(A,e):!1;if(t=KA(A),i=KA(e),t||i){if(!t||!i)return!1;const o=Object.keys(A).length,n=Object.keys(e).length;if(o!==n)return!1;for(const r in A){const g=A.hasOwnProperty(r),s=e.hasOwnProperty(r);if(g&&!s||!g&&s||!hn(A[r],e[r]))return!1}}return String(A)===String(e)}function cB(A,e){return A.findIndex(t=>hn(t,e))}const aI=A=>!!(A&&A.__v_isRef===!0),F=A=>ee(A)?A:A==null?"":yA(A)||KA(A)&&(A.toString===nI||!FA(A.toString))?aI(A)?F(A.value):JSON.stringify(A,II,2):String(A),II=(A,e)=>aI(e)?II(A,e.value):hi(e)?{[`Map(${e.size})`]:[...e.entries()].reduce((t,[i,o],n)=>(t[Jn(i,n)+" =>"]=o,t),{})}:Qn(e)?{[`Set(${e.size})`]:[...e.values()].map(t=>Jn(t))}:It(e)?Jn(e):KA(e)&&!yA(e)&&!rI(e)?String(e):e,Jn=(A,e="")=>{var t;return It(A)?`Symbol(${(t=A.description)!=null?t:e})`:A};/**
+* @vue/reactivity v3.5.26
+* (c) 2018-present Yuxi (Evan) You and Vue contributors
+* @license MIT
+**/let ve;class CI{constructor(e=!1){this.detached=e,this._active=!0,this._on=0,this.effects=[],this.cleanups=[],this._isPaused=!1,this.parent=ve,!e&&ve&&(this.index=(ve.scopes||(ve.scopes=[])).push(this)-1)}get active(){return this._active}pause(){if(this._active){this._isPaused=!0;let e,t;if(this.scopes)for(e=0,t=this.scopes.length;e<t;e++)this.scopes[e].pause();for(e=0,t=this.effects.length;e<t;e++)this.effects[e].pause()}}resume(){if(this._active&&this._isPaused){this._isPaused=!1;let e,t;if(this.scopes)for(e=0,t=this.scopes.length;e<t;e++)this.scopes[e].resume();for(e=0,t=this.effects.length;e<t;e++)this.effects[e].resume()}}run(e){if(this._active){const t=ve;try{return ve=this,e()}finally{ve=t}}}on(){++this._on===1&&(this.prevScope=ve,ve=this)}off(){this._on>0&&--this._on===0&&(ve=this.prevScope,this.prevScope=void 0)}stop(e){if(this._active){this._active=!1;let t,i;for(t=0,i=this.effects.length;t<i;t++)this.effects[t].stop();for(this.effects.length=0,t=0,i=this.cleanups.length;t<i;t++)this.cleanups[t]();if(this.cleanups.length=0,this.scopes){for(t=0,i=this.scopes.length;t<i;t++)this.scopes[t].stop(!0);this.scopes.length=0}if(!this.detached&&this.parent&&!e){const o=this.parent.scopes.pop();o&&o!==this&&(this.parent.scopes[this.index]=o,o.index=this.index)}this.parent=void 0}}}function EB(A){return new CI(A)}function uB(){return ve}let OA;const Tn=new WeakSet;class lI{constructor(e){this.fn=e,this.deps=void 0,this.depsTail=void 0,this.flags=5,this.next=void 0,this.cleanup=void 0,this.scheduler=void 0,ve&&ve.active&&ve.effects.push(this)}pause(){this.flags|=64}resume(){this.flags&64&&(this.flags&=-65,Tn.has(this)&&(Tn.delete(this),this.trigger()))}notify(){this.flags&2&&!(this.flags&32)||this.flags&8||QI(this)}run(){if(!(this.flags&1))return this.fn();this.flags|=2,Mg(this),cI(this);const e=OA,t=_e;OA=this,_e=!0;try{return this.fn()}finally{EI(this),OA=e,_e=t,this.flags&=-3}}stop(){if(this.flags&1){for(let e=this.deps;e;e=e.nextDep)zr(e);this.deps=this.depsTail=void 0,Mg(this),this.onStop&&this.onStop(),this.flags&=-2}}trigger(){this.flags&64?Tn.add(this):this.scheduler?this.scheduler():this.runIfDirty()}runIfDirty(){Ir(this)&&this.run()}get dirty(){return Ir(this)}}let BI=0,Pi,Vi;function QI(A,e=!1){if(A.flags|=8,e){A.next=Vi,Vi=A;return}A.next=Pi,Pi=A}function Zr(){BI++}function Xr(){if(--BI>0)return;if(Vi){let e=Vi;for(Vi=void 0;e;){const t=e.next;e.next=void 0,e.flags&=-9,e=t}}let A;for(;Pi;){let e=Pi;for(Pi=void 0;e;){const t=e.next;if(e.next=void 0,e.flags&=-9,e.flags&1)try{e.trigger()}catch(i){A||(A=i)}e=t}}if(A)throw A}function cI(A){for(let e=A.deps;e;e=e.nextDep)e.version=-1,e.prevActiveLink=e.dep.activeLink,e.dep.activeLink=e}function EI(A){let e,t=A.depsTail,i=t;for(;i;){const o=i.prevDep;i.version===-1?(i===t&&(t=o),zr(i),fB(i)):e=i,i.dep.activeLink=i.prevActiveLink,i.prevActiveLink=void 0,i=o}A.deps=e,A.depsTail=t}function Ir(A){for(let e=A.deps;e;e=e.nextDep)if(e.dep.version!==e.version||e.dep.computed&&(uI(e.dep.computed)||e.dep.version!==e.version))return!0;return!!A._dirty}function uI(A){if(A.flags&4&&!(A.flags&16)||(A.flags&=-17,A.globalVersion===$i)||(A.globalVersion=$i,!A.isSSR&&A.flags&128&&(!A.deps&&!A._dirty||!Ir(A))))return;A.flags|=2;const e=A.dep,t=OA,i=_e;OA=A,_e=!0;try{cI(A);const o=A.fn(A._value);(e.version===0||Yt(o,A._value))&&(A.flags|=128,A._value=o,e.version++)}catch(o){throw e.version++,o}finally{OA=t,_e=i,EI(A),A.flags&=-3}}function zr(A,e=!1){const{dep:t,prevSub:i,nextSub:o}=A;if(i&&(i.nextSub=o,A.prevSub=void 0),o&&(o.prevSub=i,A.nextSub=void 0),t.subs===A&&(t.subs=i,!i&&t.computed)){t.computed.flags&=-5;for(let n=t.computed.deps;n;n=n.nextDep)zr(n,!0)}!e&&!--t.sc&&t.map&&t.map.delete(t.key)}function fB(A){const{prevDep:e,nextDep:t}=A;e&&(e.nextDep=t,A.prevDep=void 0),t&&(t.prevDep=e,A.nextDep=void 0)}let _e=!0;const fI=[];function wt(){fI.push(_e),_e=!1}function Dt(){const A=fI.pop();_e=A===void 0?!0:A}function Mg(A){const{cleanup:e}=A;if(A.cleanup=void 0,e){const t=OA;OA=void 0;try{e()}finally{OA=t}}}let $i=0;class hB{constructor(e,t){this.sub=e,this.dep=t,this.version=t.version,this.nextDep=this.prevDep=this.nextSub=this.prevSub=this.prevActiveLink=void 0}}class $r{constructor(e){this.computed=e,this.version=0,this.activeLink=void 0,this.subs=void 0,this.map=void 0,this.key=void 0,this.sc=0,this.__v_skip=!0}track(e){if(!OA||!_e||OA===this.computed)return;let t=this.activeLink;if(t===void 0||t.sub!==OA)t=this.activeLink=new hB(OA,this),OA.deps?(t.prevDep=OA.depsTail,OA.depsTail.nextDep=t,OA.depsTail=t):OA.deps=OA.depsTail=t,hI(t);else if(t.version===-1&&(t.version=this.version,t.nextDep)){const i=t.nextDep;i.prevDep=t.prevDep,t.prevDep&&(t.prevDep.nextDep=i),t.prevDep=OA.depsTail,t.nextDep=void 0,OA.depsTail.nextDep=t,OA.depsTail=t,OA.deps===t&&(OA.deps=i)}return t}trigger(e){this.version++,$i++,this.notify(e)}notify(e){Zr();try{for(let t=this.subs;t;t=t.prevSub)t.sub.notify()&&t.sub.dep.notify()}finally{Xr()}}}function hI(A){if(A.dep.sc++,A.sub.flags&4){const e=A.dep.computed;if(e&&!A.dep.subs){e.flags|=20;for(let i=e.deps;i;i=i.nextDep)hI(i)}const t=A.dep.subs;t!==A&&(A.prevSub=t,t&&(t.nextSub=A)),A.dep.subs=A}}const Cr=new WeakMap,ri=Symbol(""),lr=Symbol(""),Ao=Symbol("");function ce(A,e,t){if(_e&&OA){let i=Cr.get(A);i||Cr.set(A,i=new Map);let o=i.get(t);o||(i.set(t,o=new $r),o.map=i,o.key=t),o.track()}}function ut(A,e,t,i,o,n){const r=Cr.get(A);if(!r){$i++;return}const g=s=>{s&&s.trigger()};if(Zr(),e==="clear")r.forEach(g);else{const s=yA(A),a=s&&jr(t);if(s&&t==="length"){const I=Number(i);r.forEach((l,C)=>{(C==="length"||C===Ao||!It(C)&&C>=I)&&g(l)})}else switch((t!==void 0||r.has(void 0))&&g(r.get(t)),a&&g(r.get(Ao)),e){case"add":s?a&&g(r.get("length")):(g(r.get(ri)),hi(A)&&g(r.get(lr)));break;case"delete":s||(g(r.get(ri)),hi(A)&&g(r.get(lr)));break;case"set":hi(A)&&g(r.get(ri));break}}Xr()}function li(A){const e=xA(A);return e===A?e:(ce(e,"iterate",Ao),xe(A)?e:e.map(Oe))}function dn(A){return ce(A=xA(A),"iterate",Ao),A}function Ut(A,e){return kt(A)?gi(A)?Di(Oe(e)):Di(e):Oe(e)}const dB={__proto__:null,[Symbol.iterator](){return Hn(this,Symbol.iterator,A=>Ut(this,A))},concat(...A){return li(this).concat(...A.map(e=>yA(e)?li(e):e))},entries(){return Hn(this,"entries",A=>(A[1]=Ut(this,A[1]),A))},every(A,e){return lt(this,"every",A,e,void 0,arguments)},filter(A,e){return lt(this,"filter",A,e,t=>t.map(i=>Ut(this,i)),arguments)},find(A,e){return lt(this,"find",A,e,t=>Ut(this,t),arguments)},findIndex(A,e){return lt(this,"findIndex",A,e,void 0,arguments)},findLast(A,e){return lt(this,"findLast",A,e,t=>Ut(this,t),arguments)},findLastIndex(A,e){return lt(this,"findLastIndex",A,e,void 0,arguments)},forEach(A,e){return lt(this,"forEach",A,e,void 0,arguments)},includes(...A){return Yn(this,"includes",A)},indexOf(...A){return Yn(this,"indexOf",A)},join(A){return li(this).join(A)},lastIndexOf(...A){return Yn(this,"lastIndexOf",A)},map(A,e){return lt(this,"map",A,e,void 0,arguments)},pop(){return Mi(this,"pop")},push(...A){return Mi(this,"push",A)},reduce(A,...e){return xg(this,"reduce",A,e)},reduceRight(A,...e){return xg(this,"reduceRight",A,e)},shift(){return Mi(this,"shift")},some(A,e){return lt(this,"some",A,e,void 0,arguments)},splice(...A){return Mi(this,"splice",A)},toReversed(){return li(this).toReversed()},toSorted(A){return li(this).toSorted(A)},toSpliced(...A){return li(this).toSpliced(...A)},unshift(...A){return Mi(this,"unshift",A)},values(){return Hn(this,"values",A=>Ut(this,A))}};function Hn(A,e,t){const i=dn(A),o=i[e]();return i!==A&&!xe(A)&&(o._next=o.next,o.next=()=>{const n=o._next();return n.done||(n.value=t(n.value)),n}),o}const pB=Array.prototype;function lt(A,e,t,i,o,n){const r=dn(A),g=r!==A&&!xe(A),s=r[e];if(s!==pB[e]){const l=s.apply(A,n);return g?Oe(l):l}let a=t;r!==A&&(g?a=function(l,C){return t.call(this,Ut(A,l),C,A)}:t.length>2&&(a=function(l,C){return t.call(this,l,C,A)}));const I=s.call(r,a,i);return g&&o?o(I):I}function xg(A,e,t,i){const o=dn(A);let n=t;return o!==A&&(xe(A)?t.length>3&&(n=function(r,g,s){return t.call(this,r,g,s,A)}):n=function(r,g,s){return t.call(this,r,Ut(A,g),s,A)}),o[e](n,...i)}function Yn(A,e,t){const i=xA(A);ce(i,"iterate",Ao);const o=i[e](...t);return(o===-1||o===!1)&&tg(t[0])?(t[0]=xA(t[0]),i[e](...t)):o}function Mi(A,e,t=[]){wt(),Zr();const i=xA(A)[e].apply(A,t);return Xr(),Dt(),i}const mB=Vr("__proto__,__v_isRef,__isVue"),dI=new Set(Object.getOwnPropertyNames(Symbol).filter(A=>A!=="arguments"&&A!=="caller").map(A=>Symbol[A]).filter(It));function yB(A){It(A)||(A=String(A));const e=xA(this);return ce(e,"has",A),e.hasOwnProperty(A)}class pI{constructor(e=!1,t=!1){this._isReadonly=e,this._isShallow=t}get(e,t,i){if(t==="__v_skip")return e.__v_skip;const o=this._isReadonly,n=this._isShallow;if(t==="__v_isReactive")return!o;if(t==="__v_isReadonly")return o;if(t==="__v_isShallow")return n;if(t==="__v_raw")return i===(o?n?GB:DI:n?wI:yI).get(e)||Object.getPrototypeOf(e)===Object.getPrototypeOf(i)?e:void 0;const r=yA(e);if(!o){let s;if(r&&(s=dB[t]))return s;if(t==="hasOwnProperty")return yB}const g=Reflect.get(e,t,le(e)?e:i);if((It(t)?dI.has(t):mB(t))||(o||ce(e,"get",t),n))return g;if(le(g)){const s=r&&jr(t)?g:g.value;return o&&KA(s)?Qr(s):s}return KA(g)?o?Qr(g):pn(g):g}}class mI extends pI{constructor(e=!1){super(!1,e)}set(e,t,i,o){let n=e[t];const r=yA(e)&&jr(t);if(!this._isShallow){const a=kt(n);if(!xe(i)&&!kt(i)&&(n=xA(n),i=xA(i)),!r&&le(n)&&!le(i))return a||(n.value=i),!0}const g=r?Number(t)<e.length:JA(e,t),s=Reflect.set(e,t,i,le(e)?e:o);return e===xA(o)&&(g?Yt(i,n)&&ut(e,"set",t,i):ut(e,"add",t,i)),s}deleteProperty(e,t){const i=JA(e,t);e[t];const o=Reflect.deleteProperty(e,t);return o&&i&&ut(e,"delete",t,void 0),o}has(e,t){const i=Reflect.has(e,t);return(!It(t)||!dI.has(t))&&ce(e,"has",t),i}ownKeys(e){return ce(e,"iterate",yA(e)?"length":ri),Reflect.ownKeys(e)}}class wB extends pI{constructor(e=!1){super(!0,e)}set(e,t){return!0}deleteProperty(e,t){return!0}}const DB=new mI,kB=new wB,vB=new mI(!0);const Br=A=>A,vo=A=>Reflect.getPrototypeOf(A);function FB(A,e,t){return function(...i){const o=this.__v_raw,n=xA(o),r=hi(n),g=A==="entries"||A===Symbol.iterator&&r,s=A==="keys"&&r,a=o[A](...i),I=t?Br:e?Di:Oe;return!e&&ce(n,"iterate",s?lr:ri),{next(){const{value:l,done:C}=a.next();return C?{value:l,done:C}:{value:g?[I(l[0]),I(l[1])]:I(l),done:C}},[Symbol.iterator](){return this}}}}function Fo(A){return function(...e){return A==="delete"?!1:A==="clear"?void 0:this}}function SB(A,e){const t={get(o){const n=this.__v_raw,r=xA(n),g=xA(o);A||(Yt(o,g)&&ce(r,"get",o),ce(r,"get",g));const{has:s}=vo(r),a=e?Br:A?Di:Oe;if(s.call(r,o))return a(n.get(o));if(s.call(r,g))return a(n.get(g));n!==r&&n.get(o)},get size(){const o=this.__v_raw;return!A&&ce(xA(o),"iterate",ri),o.size},has(o){const n=this.__v_raw,r=xA(n),g=xA(o);return A||(Yt(o,g)&&ce(r,"has",o),ce(r,"has",g)),o===g?n.has(o):n.has(o)||n.has(g)},forEach(o,n){const r=this,g=r.__v_raw,s=xA(g),a=e?Br:A?Di:Oe;return!A&&ce(s,"iterate",ri),g.forEach((I,l)=>o.call(n,a(I),a(l),r))}};return ue(t,A?{add:Fo("add"),set:Fo("set"),delete:Fo("delete"),clear:Fo("clear")}:{add(o){!e&&!xe(o)&&!kt(o)&&(o=xA(o));const n=xA(this);return vo(n).has.call(n,o)||(n.add(o),ut(n,"add",o,o)),this},set(o,n){!e&&!xe(n)&&!kt(n)&&(n=xA(n));const r=xA(this),{has:g,get:s}=vo(r);let a=g.call(r,o);a||(o=xA(o),a=g.call(r,o));const I=s.call(r,o);return r.set(o,n),a?Yt(n,I)&&ut(r,"set",o,n):ut(r,"add",o,n),this},delete(o){const n=xA(this),{has:r,get:g}=vo(n);let s=r.call(n,o);s||(o=xA(o),s=r.call(n,o)),g&&g.call(n,o);const a=n.delete(o);return s&&ut(n,"delete",o,void 0),a},clear(){const o=xA(this),n=o.size!==0,r=o.clear();return n&&ut(o,"clear",void 0,void 0),r}}),["keys","values","entries",Symbol.iterator].forEach(o=>{t[o]=FB(o,A,e)}),t}function Ag(A,e){const t=SB(A,e);return(i,o,n)=>o==="__v_isReactive"?!A:o==="__v_isReadonly"?A:o==="__v_raw"?i:Reflect.get(JA(t,o)&&o in i?t:i,o,n)}const RB={get:Ag(!1,!1)},bB={get:Ag(!1,!0)},NB={get:Ag(!0,!1)};const yI=new WeakMap,wI=new WeakMap,DI=new WeakMap,GB=new WeakMap;function LB(A){switch(A){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function UB(A){return A.__v_skip||!Object.isExtensible(A)?0:LB(nB(A))}function pn(A){return kt(A)?A:eg(A,!1,DB,RB,yI)}function kI(A){return eg(A,!1,vB,bB,wI)}function Qr(A){return eg(A,!0,kB,NB,DI)}function eg(A,e,t,i,o){if(!KA(A)||A.__v_raw&&!(e&&A.__v_isReactive))return A;const n=UB(A);if(n===0)return A;const r=o.get(A);if(r)return r;const g=new Proxy(A,n===2?i:t);return o.set(A,g),g}function gi(A){return kt(A)?gi(A.__v_raw):!!(A&&A.__v_isReactive)}function kt(A){return!!(A&&A.__v_isReadonly)}function xe(A){return!!(A&&A.__v_isShallow)}function tg(A){return A?!!A.__v_raw:!1}function xA(A){const e=A&&A.__v_raw;return e?xA(e):A}function _i(A){return!JA(A,"__v_skip")&&Object.isExtensible(A)&&gI(A,"__v_skip",!0),A}const Oe=A=>KA(A)?pn(A):A,Di=A=>KA(A)?Qr(A):A;function le(A){return A?A.__v_isRef===!0:!1}function uA(A){return vI(A,!1)}function ig(A){return vI(A,!0)}function vI(A,e){return le(A)?A:new MB(A,e)}class MB{constructor(e,t){this.dep=new $r,this.__v_isRef=!0,this.__v_isShallow=!1,this._rawValue=t?e:xA(e),this._value=t?e:Oe(e),this.__v_isShallow=t}get value(){return this.dep.track(),this._value}set value(e){const t=this._rawValue,i=this.__v_isShallow||xe(e)||kt(e);e=i?e:xA(e),Yt(e,t)&&(this._rawValue=e,this._value=i?e:Oe(e),this.dep.trigger())}}function cA(A){return le(A)?A.value:A}const xB={get:(A,e,t)=>e==="__v_raw"?A:cA(Reflect.get(A,e,t)),set:(A,e,t,i)=>{const o=A[e];return le(o)&&!le(t)?(o.value=t,!0):Reflect.set(A,e,t,i)}};function FI(A){return gi(A)?A:new Proxy(A,xB)}class JB{constructor(e,t,i){this.fn=e,this.setter=t,this._value=void 0,this.dep=new $r(this),this.__v_isRef=!0,this.deps=void 0,this.depsTail=void 0,this.flags=16,this.globalVersion=$i-1,this.next=void 0,this.effect=this,this.__v_isReadonly=!t,this.isSSR=i}notify(){if(this.flags|=16,!(this.flags&8)&&OA!==this)return QI(this,!0),!0}get value(){const e=this.dep.track();return uI(this),e&&(e.version=this.dep.version),this._value}set value(e){this.setter&&this.setter(e)}}function TB(A,e,t=!1){let i,o;return FA(A)?i=A:(i=A.get,o=A.set),new JB(i,o,t)}const So={},qo=new WeakMap;let ei;function HB(A,e=!1,t=ei){if(t){let i=qo.get(t);i||qo.set(t,i=[]),i.push(A)}}function YB(A,e,t=_A){const{immediate:i,deep:o,once:n,scheduler:r,augmentJob:g,call:s}=t,a=p=>o?p:xe(p)||o===!1||o===0?ft(p,1):ft(p);let I,l,C,Q,f=!1,h=!1;if(le(A)?(l=()=>A.value,f=xe(A)):gi(A)?(l=()=>a(A),f=!0):yA(A)?(h=!0,f=A.some(p=>gi(p)||xe(p)),l=()=>A.map(p=>{if(le(p))return p.value;if(gi(p))return a(p);if(FA(p))return s?s(p,2):p()})):FA(A)?e?l=s?()=>s(A,2):A:l=()=>{if(C){wt();try{C()}finally{Dt()}}const p=ei;ei=I;try{return s?s(A,3,[Q]):A(Q)}finally{ei=p}}:l=nt,e&&o){const p=l,d=o===!0?1/0:o;l=()=>ft(p(),d)}const u=uB(),E=()=>{I.stop(),u&&u.active&&qr(u.effects,I)};if(n&&e){const p=e;e=(...d)=>{p(...d),E()}}let D=h?new Array(A.length).fill(So):So;const c=p=>{if(!(!(I.flags&1)||!I.dirty&&!p))if(e){const d=I.run();if(o||f||(h?d.some((v,b)=>Yt(v,D[b])):Yt(d,D))){C&&C();const v=ei;ei=I;try{const b=[d,D===So?void 0:h&&D[0]===So?[]:D,Q];D=d,s?s(e,3,b):e(...b)}finally{ei=v}}}else I.run()};return g&&g(c),I=new lI(l),I.scheduler=r?()=>r(c,!1):c,Q=p=>HB(p,!1,I),C=I.onStop=()=>{const p=qo.get(I);if(p){if(s)s(p,4);else for(const d of p)d();qo.delete(I)}},e?i?c(!0):D=I.run():r?r(c.bind(null,!0),!0):I.run(),E.pause=I.pause.bind(I),E.resume=I.resume.bind(I),E.stop=E,E}function ft(A,e=1/0,t){if(e<=0||!KA(A)||A.__v_skip||(t=t||new Map,(t.get(A)||0)>=e))return A;if(t.set(A,e),e--,le(A))ft(A.value,e,t);else if(yA(A))for(let i=0;i<A.length;i++)ft(A[i],e,t);else if(Qn(A)||hi(A))A.forEach(i=>{ft(i,e,t)});else if(rI(A)){for(const i in A)ft(A[i],e,t);for(const i of Object.getOwnPropertySymbols(A))Object.prototype.propertyIsEnumerable.call(A,i)&&ft(A[i],e,t)}return A}/**
+* @vue/runtime-core v3.5.26
+* (c) 2018-present Yuxi (Evan) You and Vue contributors
+* @license MIT
+**/function Eo(A,e,t,i){try{return i?A(...i):A()}catch(o){mn(o,e,t)}}function Ct(A,e,t,i){if(FA(A)){const o=Eo(A,e,t,i);return o&&oI(o)&&o.catch(n=>{mn(n,e,t)}),o}if(yA(A)){const o=[];for(let n=0;n<A.length;n++)o.push(Ct(A[n],e,t,i));return o}}function mn(A,e,t,i=!0){const o=e?e.vnode:null,{errorHandler:n,throwUnhandledErrorInProduction:r}=e&&e.appContext.config||_A;if(e){let g=e.parent;const s=e.proxy,a=`https://vuejs.org/error-reference/#runtime-${t}`;for(;g;){const I=g.ec;if(I){for(let l=0;l<I.length;l++)if(I[l](A,s,a)===!1)return}g=g.parent}if(n){wt(),Eo(n,null,10,[A,s,a]),Dt();return}}_B(A,t,o,i,r)}function _B(A,e,t,i=!0,o=!1){if(o)throw A;console.error(A)}const me=[];let At=-1;const di=[];let Mt=null,Qi=0;const SI=Promise.resolve();let jo=null;function uo(A){const e=jo||SI;return A?e.then(this?A.bind(this):A):e}function KB(A){let e=At+1,t=me.length;for(;e<t;){const i=e+t>>>1,o=me[i],n=eo(o);n<A||n===A&&o.flags&2?e=i+1:t=i}return e}function og(A){if(!(A.flags&1)){const e=eo(A),t=me[me.length-1];!t||!(A.flags&2)&&e>=eo(t)?me.push(A):me.splice(KB(e),0,A),A.flags|=1,RI()}}function RI(){jo||(jo=SI.then(NI))}function OB(A){yA(A)?di.push(...A):Mt&&A.id===-1?Mt.splice(Qi+1,0,A):A.flags&1||(di.push(A),A.flags|=1),RI()}function Jg(A,e,t=At+1){for(;t<me.length;t++){const i=me[t];if(i&&i.flags&2){if(A&&i.id!==A.uid)continue;me.splice(t,1),t--,i.flags&4&&(i.flags&=-2),i(),i.flags&4||(i.flags&=-2)}}}function bI(A){if(di.length){const e=[...new Set(di)].sort((t,i)=>eo(t)-eo(i));if(di.length=0,Mt){Mt.push(...e);return}for(Mt=e,Qi=0;Qi<Mt.length;Qi++){const t=Mt[Qi];t.flags&4&&(t.flags&=-2),t.flags&8||t(),t.flags&=-2}Mt=null,Qi=0}}const eo=A=>A.id==null?A.flags&2?-1:1/0:A.id;function NI(A){try{for(At=0;At<me.length;At++){const e=me[At];e&&!(e.flags&8)&&(e.flags&4&&(e.flags&=-2),Eo(e,e.i,e.i?15:14),e.flags&4||(e.flags&=-2))}}finally{for(;At<me.length;At++){const e=me[At];e&&(e.flags&=-2)}At=-1,me.length=0,bI(),jo=null,(me.length||di.length)&&NI()}}let Re=null,GI=null;function Zo(A){const e=Re;return Re=A,GI=A&&A.type.__scopeId||null,e}function PB(A,e=Re,t){if(!e||A._n)return A;const i=(...o)=>{i._d&&$o(-1);const n=Zo(e);let r;try{r=A(...o)}finally{Zo(n),i._d&&$o(1)}return r};return i._n=!0,i._c=!0,i._d=!0,i}function ZA(A,e){if(Re===null)return A;const t=kn(Re),i=A.dirs||(A.dirs=[]);for(let o=0;o<e.length;o++){let[n,r,g,s=_A]=e[o];n&&(FA(n)&&(n={mounted:n,updated:n}),n.deep&&ft(r),i.push({dir:n,instance:t,value:r,oldValue:void 0,arg:g,modifiers:s}))}return A}function Zt(A,e,t,i){const o=A.dirs,n=e&&e.dirs;for(let r=0;r<o.length;r++){const g=o[r];n&&(g.oldValue=n[r].value);let s=g.dir[i];s&&(wt(),Ct(s,t,8,[A.el,g,A,e]),Dt())}}function Ho(A,e){if(Ee){let t=Ee.provides;const i=Ee.parent&&Ee.parent.provides;i===t&&(t=Ee.provides=Object.create(i)),t[A]=e}}function rt(A,e,t=!1){const i=vi();if(i||pi){let o=pi?pi._context.provides:i?i.parent==null||i.ce?i.vnode.appContext&&i.vnode.appContext.provides:i.parent.provides:void 0;if(o&&A in o)return o[A];if(arguments.length>1)return t&&FA(e)?e.call(i&&i.proxy):e}}const VB=Symbol.for("v-scx"),WB=()=>rt(VB);function ae(A,e,t){return LI(A,e,t)}function LI(A,e,t=_A){const{immediate:i,deep:o,flush:n,once:r}=t,g=ue({},t),s=e&&i||!e&&n!=="post";let a;if(io){if(n==="sync"){const Q=WB();a=Q.__watcherHandles||(Q.__watcherHandles=[])}else if(!s){const Q=()=>{};return Q.stop=nt,Q.resume=nt,Q.pause=nt,Q}}const I=Ee;g.call=(Q,f,h)=>Ct(Q,I,f,h);let l=!1;n==="post"?g.scheduler=Q=>{Se(Q,I&&I.suspense)}:n!=="sync"&&(l=!0,g.scheduler=(Q,f)=>{f?Q():og(Q)}),g.augmentJob=Q=>{e&&(Q.flags|=4),l&&(Q.flags|=2,I&&(Q.id=I.uid,Q.i=I))};const C=YB(A,e,g);return io&&(a?a.push(C):s&&C()),C}function qB(A,e,t){const i=this.proxy,o=ee(A)?A.includes(".")?UI(i,A):()=>i[A]:A.bind(i,i);let n;FA(e)?n=e:(n=e.handler,t=e);const r=po(this),g=LI(o,n.bind(i),t);return r(),g}function UI(A,e){const t=e.split(".");return()=>{let i=A;for(let o=0;o<t.length&&i;o++)i=i[t[o]];return i}}const jB=Symbol("_vte"),ZB=A=>A.__isTeleport,XB=Symbol("_leaveCb");function ng(A,e){A.shapeFlag&6&&A.component?(A.transition=e,ng(A.component.subTree,e)):A.shapeFlag&128?(A.ssContent.transition=e.clone(A.ssContent),A.ssFallback.transition=e.clone(A.ssFallback)):A.transition=e}function fo(A,e){return FA(A)?ue({name:A.name},e,{setup:A}):A}function MI(A){A.ids=[A.ids[0]+A.ids[2]+++"-",0,0]}const Xo=new WeakMap;function Wi(A,e,t,i,o=!1){if(yA(A)){A.forEach((f,h)=>Wi(f,e&&(yA(e)?e[h]:e),t,i,o));return}if(qi(i)&&!o){i.shapeFlag&512&&i.type.__asyncResolved&&i.component.subTree.component&&Wi(A,e,t,i.component.subTree);return}const n=i.shapeFlag&4?kn(i.component):i.el,r=o?null:n,{i:g,r:s}=A,a=e&&e.r,I=g.refs===_A?g.refs={}:g.refs,l=g.setupState,C=xA(l),Q=l===_A?iI:f=>JA(C,f);if(a!=null&&a!==s){if(Tg(e),ee(a))I[a]=null,Q(a)&&(l[a]=null);else if(le(a)){a.value=null;const f=e;f.k&&(I[f.k]=null)}}if(FA(s))Eo(s,g,12,[r,I]);else{const f=ee(s),h=le(s);if(f||h){const u=()=>{if(A.f){const E=f?Q(s)?l[s]:I[s]:s.value;if(o)yA(E)&&qr(E,n);else if(yA(E))E.includes(n)||E.push(n);else if(f)I[s]=[n],Q(s)&&(l[s]=I[s]);else{const D=[n];s.value=D,A.k&&(I[A.k]=D)}}else f?(I[s]=r,Q(s)&&(l[s]=r)):h&&(s.value=r,A.k&&(I[A.k]=r))};if(r){const E=()=>{u(),Xo.delete(A)};E.id=-1,Xo.set(A,E),Se(E,t)}else Tg(A),u()}}}function Tg(A){const e=Xo.get(A);e&&(e.flags|=8,Xo.delete(A))}fn().requestIdleCallback;fn().cancelIdleCallback;const qi=A=>!!A.type.__asyncLoader,xI=A=>A.type.__isKeepAlive;function zB(A,e){JI(A,"a",e)}function $B(A,e){JI(A,"da",e)}function JI(A,e,t=Ee){const i=A.__wdc||(A.__wdc=()=>{let o=t;for(;o;){if(o.isDeactivated)return;o=o.parent}return A()});if(yn(e,i,t),t){let o=t.parent;for(;o&&o.parent;)xI(o.parent.vnode)&&AQ(i,e,t,o),o=o.parent}}function AQ(A,e,t,i){const o=yn(e,A,i,!0);Ci(()=>{qr(i[e],o)},t)}function yn(A,e,t=Ee,i=!1){if(t){const o=t[A]||(t[A]=[]),n=e.__weh||(e.__weh=(...r)=>{wt();const g=po(t),s=Ct(e,t,A,r);return g(),Dt(),s});return i?o.unshift(n):o.push(n),n}}const vt=A=>(e,t=Ee)=>{(!io||A==="sp")&&yn(A,(...i)=>e(...i),t)},TI=vt("bm"),Ft=vt("m"),eQ=vt("bu"),tQ=vt("u"),iQ=vt("bum"),Ci=vt("um"),oQ=vt("sp"),nQ=vt("rtg"),rQ=vt("rtc");function gQ(A,e=Ee){yn("ec",A,e)}const sQ="components",HI=Symbol.for("v-ndc");function YI(A){return ee(A)?aQ(sQ,A,!1)||A:A||HI}function aQ(A,e,t=!0,i=!1){const o=Re||Ee;if(o){const n=o.type;{const g=WQ(n,!1);if(g&&(g===e||g===Je(e)||g===En(Je(e))))return n}const r=Hg(o[A]||n[A],e)||Hg(o.appContext[A],e);return!r&&i?n:r}}function Hg(A,e){return A&&(A[e]||A[Je(e)]||A[En(Je(e))])}function Ke(A,e,t,i){let o;const n=t,r=yA(A);if(r||ee(A)){const g=r&&gi(A);let s=!1,a=!1;g&&(s=!xe(A),a=kt(A),A=dn(A)),o=new Array(A.length);for(let I=0,l=A.length;I<l;I++)o[I]=e(s?a?Di(Oe(A[I])):Oe(A[I]):A[I],I,void 0,n)}else if(typeof A=="number"){o=new Array(A);for(let g=0;g<A;g++)o[g]=e(g+1,g,void 0,n)}else if(KA(A))if(A[Symbol.iterator])o=Array.from(A,(g,s)=>e(g,s,void 0,n));else{const g=Object.keys(A);o=new Array(g.length);for(let s=0,a=g.length;s<a;s++){const I=g[s];o[s]=e(A[I],I,s,n)}}else o=[];return o}const cr=A=>A?rC(A)?kn(A):cr(A.parent):null,ji=ue(Object.create(null),{$:A=>A,$el:A=>A.vnode.el,$data:A=>A.data,$props:A=>A.props,$attrs:A=>A.attrs,$slots:A=>A.slots,$refs:A=>A.refs,$parent:A=>cr(A.parent),$root:A=>cr(A.root),$host:A=>A.ce,$emit:A=>A.emit,$options:A=>KI(A),$forceUpdate:A=>A.f||(A.f=()=>{og(A.update)}),$nextTick:A=>A.n||(A.n=uo.bind(A.proxy)),$watch:A=>qB.bind(A)}),_n=(A,e)=>A!==_A&&!A.__isScriptSetup&&JA(A,e),IQ={get({_:A},e){if(e==="__v_skip")return!0;const{ctx:t,setupState:i,data:o,props:n,accessCache:r,type:g,appContext:s}=A;if(e[0]!=="$"){const C=r[e];if(C!==void 0)switch(C){case 1:return i[e];case 2:return o[e];case 4:return t[e];case 3:return n[e]}else{if(_n(i,e))return r[e]=1,i[e];if(o!==_A&&JA(o,e))return r[e]=2,o[e];if(JA(n,e))return r[e]=3,n[e];if(t!==_A&&JA(t,e))return r[e]=4,t[e];Er&&(r[e]=0)}}const a=ji[e];let I,l;if(a)return e==="$attrs"&&ce(A.attrs,"get",""),a(A);if((I=g.__cssModules)&&(I=I[e]))return I;if(t!==_A&&JA(t,e))return r[e]=4,t[e];if(l=s.config.globalProperties,JA(l,e))return l[e]},set({_:A},e,t){const{data:i,setupState:o,ctx:n}=A;return _n(o,e)?(o[e]=t,!0):i!==_A&&JA(i,e)?(i[e]=t,!0):JA(A.props,e)||e[0]==="$"&&e.slice(1)in A?!1:(n[e]=t,!0)},has({_:{data:A,setupState:e,accessCache:t,ctx:i,appContext:o,props:n,type:r}},g){let s;return!!(t[g]||A!==_A&&g[0]!=="$"&&JA(A,g)||_n(e,g)||JA(n,g)||JA(i,g)||JA(ji,g)||JA(o.config.globalProperties,g)||(s=r.__cssModules)&&s[g])},defineProperty(A,e,t){return t.get!=null?A._.accessCache[e]=0:JA(t,"value")&&this.set(A,e,t.value,null),Reflect.defineProperty(A,e,t)}};function Yg(A){return yA(A)?A.reduce((e,t)=>(e[t]=null,e),{}):A}let Er=!0;function CQ(A){const e=KI(A),t=A.proxy,i=A.ctx;Er=!1,e.beforeCreate&&_g(e.beforeCreate,A,"bc");const{data:o,computed:n,methods:r,watch:g,provide:s,inject:a,created:I,beforeMount:l,mounted:C,beforeUpdate:Q,updated:f,activated:h,deactivated:u,beforeDestroy:E,beforeUnmount:D,destroyed:c,unmounted:p,render:d,renderTracked:v,renderTriggered:b,errorCaptured:N,serverPrefetch:H,expose:M,inheritAttrs:U,components:R,directives:j,filters:rA}=e;if(a&&lQ(a,i,null),r)for(const z in r){const oA=r[z];FA(oA)&&(i[z]=oA.bind(t))}if(o){const z=o.call(t,t);KA(z)&&(A.data=pn(z))}if(Er=!0,n)for(const z in n){const oA=n[z],dA=FA(oA)?oA.bind(t,t):FA(oA.get)?oA.get.bind(t,t):nt,Y=!FA(oA)&&FA(oA.set)?oA.set.bind(t):nt,W=hA({get:dA,set:Y});Object.defineProperty(i,z,{enumerable:!0,configurable:!0,get:()=>W.value,set:$=>W.value=$})}if(g)for(const z in g)_I(g[z],i,t,z);if(s){const z=FA(s)?s.call(t):s;Reflect.ownKeys(z).forEach(oA=>{Ho(oA,z[oA])})}I&&_g(I,A,"c");function AA(z,oA){yA(oA)?oA.forEach(dA=>z(dA.bind(t))):oA&&z(oA.bind(t))}if(AA(TI,l),AA(Ft,C),AA(eQ,Q),AA(tQ,f),AA(zB,h),AA($B,u),AA(gQ,N),AA(rQ,v),AA(nQ,b),AA(iQ,D),AA(Ci,p),AA(oQ,H),yA(M))if(M.length){const z=A.exposed||(A.exposed={});M.forEach(oA=>{Object.defineProperty(z,oA,{get:()=>t[oA],set:dA=>t[oA]=dA,enumerable:!0})})}else A.exposed||(A.exposed={});d&&A.render===nt&&(A.render=d),U!=null&&(A.inheritAttrs=U),R&&(A.components=R),j&&(A.directives=j),H&&MI(A)}function lQ(A,e,t=nt){yA(A)&&(A=ur(A));for(const i in A){const o=A[i];let n;KA(o)?"default"in o?n=rt(o.from||i,o.default,!0):n=rt(o.from||i):n=rt(o),le(n)?Object.defineProperty(e,i,{enumerable:!0,configurable:!0,get:()=>n.value,set:r=>n.value=r}):e[i]=n}}function _g(A,e,t){Ct(yA(A)?A.map(i=>i.bind(e.proxy)):A.bind(e.proxy),e,t)}function _I(A,e,t,i){let o=i.includes(".")?UI(t,i):()=>t[i];if(ee(A)){const n=e[A];FA(n)&&ae(o,n)}else if(FA(A))ae(o,A.bind(t));else if(KA(A))if(yA(A))A.forEach(n=>_I(n,e,t,i));else{const n=FA(A.handler)?A.handler.bind(t):e[A.handler];FA(n)&&ae(o,n,A)}}function KI(A){const e=A.type,{mixins:t,extends:i}=e,{mixins:o,optionsCache:n,config:{optionMergeStrategies:r}}=A.appContext,g=n.get(e);let s;return g?s=g:!o.length&&!t&&!i?s=e:(s={},o.length&&o.forEach(a=>zo(s,a,r,!0)),zo(s,e,r)),KA(e)&&n.set(e,s),s}function zo(A,e,t,i=!1){const{mixins:o,extends:n}=e;n&&zo(A,n,t,!0),o&&o.forEach(r=>zo(A,r,t,!0));for(const r in e)if(!(i&&r==="expose")){const g=BQ[r]||t&&t[r];A[r]=g?g(A[r],e[r]):e[r]}return A}const BQ={data:Kg,props:Og,emits:Og,methods:Ki,computed:Ki,beforeCreate:he,created:he,beforeMount:he,mounted:he,beforeUpdate:he,updated:he,beforeDestroy:he,beforeUnmount:he,destroyed:he,unmounted:he,activated:he,deactivated:he,errorCaptured:he,serverPrefetch:he,components:Ki,directives:Ki,watch:cQ,provide:Kg,inject:QQ};function Kg(A,e){return e?A?function(){return ue(FA(A)?A.call(this,this):A,FA(e)?e.call(this,this):e)}:e:A}function QQ(A,e){return Ki(ur(A),ur(e))}function ur(A){if(yA(A)){const e={};for(let t=0;t<A.length;t++)e[A[t]]=A[t];return e}return A}function he(A,e){return A?[...new Set([].concat(A,e))]:e}function Ki(A,e){return A?ue(Object.create(null),A,e):e}function Og(A,e){return A?yA(A)&&yA(e)?[...new Set([...A,...e])]:ue(Object.create(null),Yg(A),Yg(e??{})):e}function cQ(A,e){if(!A)return e;if(!e)return A;const t=ue(Object.create(null),A);for(const i in e)t[i]=he(A[i],e[i]);return t}function OI(){return{app:null,config:{isNativeTag:iI,performance:!1,globalProperties:{},optionMergeStrategies:{},errorHandler:void 0,warnHandler:void 0,compilerOptions:{}},mixins:[],components:{},directives:{},provides:Object.create(null),optionsCache:new WeakMap,propsCache:new WeakMap,emitsCache:new WeakMap}}let EQ=0;function uQ(A,e){return function(i,o=null){FA(i)||(i=ue({},i)),o!=null&&!KA(o)&&(o=null);const n=OI(),r=new WeakSet,g=[];let s=!1;const a=n.app={_uid:EQ++,_component:i,_props:o,_container:null,_context:n,_instance:null,version:jQ,get config(){return n.config},set config(I){},use(I,...l){return r.has(I)||(I&&FA(I.install)?(r.add(I),I.install(a,...l)):FA(I)&&(r.add(I),I(a,...l))),a},mixin(I){return n.mixins.includes(I)||n.mixins.push(I),a},component(I,l){return l?(n.components[I]=l,a):n.components[I]},directive(I,l){return l?(n.directives[I]=l,a):n.directives[I]},mount(I,l,C){if(!s){const Q=a._ceVNode||TA(i,o);return Q.appContext=n,C===!0?C="svg":C===!1&&(C=void 0),A(Q,I,C),s=!0,a._container=I,I.__vue_app__=a,kn(Q.component)}},onUnmount(I){g.push(I)},unmount(){s&&(Ct(g,a._instance,16),A(null,a._container),delete a._container.__vue_app__)},provide(I,l){return n.provides[I]=l,a},runWithContext(I){const l=pi;pi=a;try{return I()}finally{pi=l}}};return a}}let pi=null;const fQ=(A,e)=>e==="modelValue"||e==="model-value"?A.modelModifiers:A[`${e}Modifiers`]||A[`${Je(e)}Modifiers`]||A[`${Ii(e)}Modifiers`];function hQ(A,e,...t){if(A.isUnmounted)return;const i=A.vnode.props||_A;let o=t;const n=e.startsWith("update:"),r=n&&fQ(i,e.slice(7));r&&(r.trim&&(o=t.map(I=>ee(I)?I.trim():I)),r.number&&(o=t.map(un)));let g,s=i[g=xn(e)]||i[g=xn(Je(e))];!s&&n&&(s=i[g=xn(Ii(e))]),s&&Ct(s,A,6,o);const a=i[g+"Once"];if(a){if(!A.emitted)A.emitted={};else if(A.emitted[g])return;A.emitted[g]=!0,Ct(a,A,6,o)}}const dQ=new WeakMap;function PI(A,e,t=!1){const i=t?dQ:e.emitsCache,o=i.get(A);if(o!==void 0)return o;const n=A.emits;let r={},g=!1;if(!FA(A)){const s=a=>{const I=PI(a,e,!0);I&&(g=!0,ue(r,I))};!t&&e.mixins.length&&e.mixins.forEach(s),A.extends&&s(A.extends),A.mixins&&A.mixins.forEach(s)}return!n&&!g?(KA(A)&&i.set(A,null),null):(yA(n)?n.forEach(s=>r[s]=null):ue(r,n),KA(A)&&i.set(A,r),r)}function wn(A,e){return!A||!Bn(e)?!1:(e=e.slice(2).replace(/Once$/,""),JA(A,e[0].toLowerCase()+e.slice(1))||JA(A,Ii(e))||JA(A,e))}function Pg(A){const{type:e,vnode:t,proxy:i,withProxy:o,propsOptions:[n],slots:r,attrs:g,emit:s,render:a,renderCache:I,props:l,data:C,setupState:Q,ctx:f,inheritAttrs:h}=A,u=Zo(A);let E,D;try{if(t.shapeFlag&4){const p=o||i,d=p;E=tt(a.call(d,p,I,l,Q,C,f)),D=g}else{const p=e;E=tt(p.length>1?p(l,{attrs:g,slots:r,emit:s}):p(l,null)),D=e.props?g:pQ(g)}}catch(p){Zi.length=0,mn(p,A,1),E=TA(Kt)}let c=E;if(D&&h!==!1){const p=Object.keys(D),{shapeFlag:d}=c;p.length&&d&7&&(n&&p.some(Wr)&&(D=mQ(D,n)),c=ki(c,D,!1,!0))}return t.dirs&&(c=ki(c,null,!1,!0),c.dirs=c.dirs?c.dirs.concat(t.dirs):t.dirs),t.transition&&ng(c,t.transition),E=c,Zo(u),E}const pQ=A=>{let e;for(const t in A)(t==="class"||t==="style"||Bn(t))&&((e||(e={}))[t]=A[t]);return e},mQ=(A,e)=>{const t={};for(const i in A)(!Wr(i)||!(i.slice(9)in e))&&(t[i]=A[i]);return t};function yQ(A,e,t){const{props:i,children:o,component:n}=A,{props:r,children:g,patchFlag:s}=e,a=n.emitsOptions;if(e.dirs||e.transition)return!0;if(t&&s>=0){if(s&1024)return!0;if(s&16)return i?Vg(i,r,a):!!r;if(s&8){const I=e.dynamicProps;for(let l=0;l<I.length;l++){const C=I[l];if(r[C]!==i[C]&&!wn(a,C))return!0}}}else return(o||g)&&(!g||!g.$stable)?!0:i===r?!1:i?r?Vg(i,r,a):!0:!!r;return!1}function Vg(A,e,t){const i=Object.keys(e);if(i.length!==Object.keys(A).length)return!0;for(let o=0;o<i.length;o++){const n=i[o];if(e[n]!==A[n]&&!wn(t,n))return!0}return!1}function wQ({vnode:A,parent:e},t){for(;e;){const i=e.subTree;if(i.suspense&&i.suspense.activeBranch===A&&(i.el=A.el),i===A)(A=e.vnode).el=t,e=e.parent;else break}}const VI={},WI=()=>Object.create(VI),qI=A=>Object.getPrototypeOf(A)===VI;function DQ(A,e,t,i=!1){const o={},n=WI();A.propsDefaults=Object.create(null),jI(A,e,o,n);for(const r in A.propsOptions[0])r in o||(o[r]=void 0);t?A.props=i?o:kI(o):A.type.props?A.props=o:A.props=n,A.attrs=n}function kQ(A,e,t,i){const{props:o,attrs:n,vnode:{patchFlag:r}}=A,g=xA(o),[s]=A.propsOptions;let a=!1;if((i||r>0)&&!(r&16)){if(r&8){const I=A.vnode.dynamicProps;for(let l=0;l<I.length;l++){let C=I[l];if(wn(A.emitsOptions,C))continue;const Q=e[C];if(s)if(JA(n,C))Q!==n[C]&&(n[C]=Q,a=!0);else{const f=Je(C);o[f]=fr(s,g,f,Q,A,!1)}else Q!==n[C]&&(n[C]=Q,a=!0)}}}else{jI(A,e,o,n)&&(a=!0);let I;for(const l in g)(!e||!JA(e,l)&&((I=Ii(l))===l||!JA(e,I)))&&(s?t&&(t[l]!==void 0||t[I]!==void 0)&&(o[l]=fr(s,g,l,void 0,A,!0)):delete o[l]);if(n!==g)for(const l in n)(!e||!JA(e,l))&&(delete n[l],a=!0)}a&&ut(A.attrs,"set","")}function jI(A,e,t,i){const[o,n]=A.propsOptions;let r=!1,g;if(e)for(let s in e){if(Oi(s))continue;const a=e[s];let I;o&&JA(o,I=Je(s))?!n||!n.includes(I)?t[I]=a:(g||(g={}))[I]=a:wn(A.emitsOptions,s)||(!(s in i)||a!==i[s])&&(i[s]=a,r=!0)}if(n){const s=xA(t),a=g||_A;for(let I=0;I<n.length;I++){const l=n[I];t[l]=fr(o,s,l,a[l],A,!JA(a,l))}}return r}function fr(A,e,t,i,o,n){const r=A[t];if(r!=null){const g=JA(r,"default");if(g&&i===void 0){const s=r.default;if(r.type!==Function&&!r.skipFactory&&FA(s)){const{propsDefaults:a}=o;if(t in a)i=a[t];else{const I=po(o);i=a[t]=s.call(null,e),I()}}else i=s;o.ce&&o.ce._setProp(t,i)}r[0]&&(n&&!g?i=!1:r[1]&&(i===""||i===Ii(t))&&(i=!0))}return i}const vQ=new WeakMap;function ZI(A,e,t=!1){const i=t?vQ:e.propsCache,o=i.get(A);if(o)return o;const n=A.props,r={},g=[];let s=!1;if(!FA(A)){const I=l=>{s=!0;const[C,Q]=ZI(l,e,!0);ue(r,C),Q&&g.push(...Q)};!t&&e.mixins.length&&e.mixins.forEach(I),A.extends&&I(A.extends),A.mixins&&A.mixins.forEach(I)}if(!n&&!s)return KA(A)&&i.set(A,fi),fi;if(yA(n))for(let I=0;I<n.length;I++){const l=Je(n[I]);Wg(l)&&(r[l]=_A)}else if(n)for(const I in n){const l=Je(I);if(Wg(l)){const C=n[I],Q=r[l]=yA(C)||FA(C)?{type:C}:ue({},C),f=Q.type;let h=!1,u=!0;if(yA(f))for(let E=0;E<f.length;++E){const D=f[E],c=FA(D)&&D.name;if(c==="Boolean"){h=!0;break}else c==="String"&&(u=!1)}else h=FA(f)&&f.name==="Boolean";Q[0]=h,Q[1]=u,(h||JA(Q,"default"))&&g.push(l)}}const a=[r,g];return KA(A)&&i.set(A,a),a}function Wg(A){return A[0]!=="$"&&!Oi(A)}const rg=A=>A==="_"||A==="_ctx"||A==="$stable",gg=A=>yA(A)?A.map(tt):[tt(A)],FQ=(A,e,t)=>{if(e._n)return e;const i=PB((...o)=>gg(e(...o)),t);return i._c=!1,i},XI=(A,e,t)=>{const i=A._ctx;for(const o in A){if(rg(o))continue;const n=A[o];if(FA(n))e[o]=FQ(o,n,i);else if(n!=null){const r=gg(n);e[o]=()=>r}}},zI=(A,e)=>{const t=gg(e);A.slots.default=()=>t},$I=(A,e,t)=>{for(const i in e)(t||!rg(i))&&(A[i]=e[i])},SQ=(A,e,t)=>{const i=A.slots=WI();if(A.vnode.shapeFlag&32){const o=e._;o?($I(i,e,t),t&&gI(i,"_",o,!0)):XI(e,i)}else e&&zI(A,e)},RQ=(A,e,t)=>{const{vnode:i,slots:o}=A;let n=!0,r=_A;if(i.shapeFlag&32){const g=e._;g?t&&g===1?n=!1:$I(o,e,t):(n=!e.$stable,XI(e,o)),r=e}else e&&(zI(A,e),r={default:1});if(n)for(const g in o)!rg(g)&&r[g]==null&&delete o[g]},Se=UQ;function bQ(A){return NQ(A)}function NQ(A,e){const t=fn();t.__VUE__=!0;const{insert:i,remove:o,patchProp:n,createElement:r,createText:g,createComment:s,setText:a,setElementText:I,parentNode:l,nextSibling:C,setScopeId:Q=nt,insertStaticContent:f}=A,h=(w,S,k,x=null,q=null,V=null,nA=void 0,iA=null,m=!!S.dynamicChildren)=>{if(w===S)return;w&&!xi(w,S)&&(x=_(w),$(w,q,V,!0),w=null),S.patchFlag===-2&&(m=!1,S.dynamicChildren=null);const{type:y,ref:L,shapeFlag:O}=S;switch(y){case ho:u(w,S,k,x);break;case Kt:E(w,S,k,x);break;case Yo:w==null&&D(S,k,x,nA);break;case qA:R(w,S,k,x,q,V,nA,iA,m);break;default:O&1?d(w,S,k,x,q,V,nA,iA,m):O&6?j(w,S,k,x,q,V,nA,iA,m):(O&64||O&128)&&y.process(w,S,k,x,q,V,nA,iA,m,EA)}L!=null&&q?Wi(L,w&&w.ref,V,S||w,!S):L==null&&w&&w.ref!=null&&Wi(w.ref,null,V,w,!0)},u=(w,S,k,x)=>{if(w==null)i(S.el=g(S.children),k,x);else{const q=S.el=w.el;S.children!==w.children&&a(q,S.children)}},E=(w,S,k,x)=>{w==null?i(S.el=s(S.children||""),k,x):S.el=w.el},D=(w,S,k,x)=>{[w.el,w.anchor]=f(w.children,S,k,x,w.el,w.anchor)},c=({el:w,anchor:S},k,x)=>{let q;for(;w&&w!==S;)q=C(w),i(w,k,x),w=q;i(S,k,x)},p=({el:w,anchor:S})=>{let k;for(;w&&w!==S;)k=C(w),o(w),w=k;o(S)},d=(w,S,k,x,q,V,nA,iA,m)=>{if(S.type==="svg"?nA="svg":S.type==="math"&&(nA="mathml"),w==null)v(S,k,x,q,V,nA,iA,m);else{const y=w.el&&w.el._isVueCE?w.el:null;try{y&&y._beginPatch(),H(w,S,q,V,nA,iA,m)}finally{y&&y._endPatch()}}},v=(w,S,k,x,q,V,nA,iA)=>{let m,y;const{props:L,shapeFlag:O,transition:gA,dirs:X}=w;if(m=w.el=r(w.type,V,L&&L.is,L),O&8?I(m,w.children):O&16&&N(w.children,m,null,x,q,Kn(w,V),nA,iA),X&&Zt(w,null,x,"created"),b(m,w,w.scopeId,nA,x),L){for(const P in L)P!=="value"&&!Oi(P)&&n(m,P,null,L[P],V,x);"value"in L&&n(m,"value",null,L.value,V),(y=L.onVnodeBeforeMount)&&ze(y,x,w)}X&&Zt(w,null,x,"beforeMount");const G=GQ(q,gA);G&&gA.beforeEnter(m),i(m,S,k),((y=L&&L.onVnodeMounted)||G||X)&&Se(()=>{y&&ze(y,x,w),G&&gA.enter(m),X&&Zt(w,null,x,"mounted")},q)},b=(w,S,k,x,q)=>{if(k&&Q(w,k),x)for(let V=0;V<x.length;V++)Q(w,x[V]);if(q){let V=q.subTree;if(S===V||iC(V.type)&&(V.ssContent===S||V.ssFallback===S)){const nA=q.vnode;b(w,nA,nA.scopeId,nA.slotScopeIds,q.parent)}}},N=(w,S,k,x,q,V,nA,iA,m=0)=>{for(let y=m;y<w.length;y++){const L=w[y]=iA?xt(w[y]):tt(w[y]);h(null,L,S,k,x,q,V,nA,iA)}},H=(w,S,k,x,q,V,nA)=>{const iA=S.el=w.el;let{patchFlag:m,dynamicChildren:y,dirs:L}=S;m|=w.patchFlag&16;const O=w.props||_A,gA=S.props||_A;let X;if(k&&Xt(k,!1),(X=gA.onVnodeBeforeUpdate)&&ze(X,k,S,w),L&&Zt(S,w,k,"beforeUpdate"),k&&Xt(k,!0),(O.innerHTML&&gA.innerHTML==null||O.textContent&&gA.textContent==null)&&I(iA,""),y?M(w.dynamicChildren,y,iA,k,x,Kn(S,q),V):nA||oA(w,S,iA,null,k,x,Kn(S,q),V,!1),m>0){if(m&16)U(iA,O,gA,k,q);else if(m&2&&O.class!==gA.class&&n(iA,"class",null,gA.class,q),m&4&&n(iA,"style",O.style,gA.style,q),m&8){const G=S.dynamicProps;for(let P=0;P<G.length;P++){const fA=G[P],bA=O[fA],jA=gA[fA];(jA!==bA||fA==="value")&&n(iA,fA,bA,jA,q,k)}}m&1&&w.children!==S.children&&I(iA,S.children)}else!nA&&y==null&&U(iA,O,gA,k,q);((X=gA.onVnodeUpdated)||L)&&Se(()=>{X&&ze(X,k,S,w),L&&Zt(S,w,k,"updated")},x)},M=(w,S,k,x,q,V,nA)=>{for(let iA=0;iA<S.length;iA++){const m=w[iA],y=S[iA],L=m.el&&(m.type===qA||!xi(m,y)||m.shapeFlag&198)?l(m.el):k;h(m,y,L,null,x,q,V,nA,!0)}},U=(w,S,k,x,q)=>{if(S!==k){if(S!==_A)for(const V in S)!Oi(V)&&!(V in k)&&n(w,V,S[V],null,q,x);for(const V in k){if(Oi(V))continue;const nA=k[V],iA=S[V];nA!==iA&&V!=="value"&&n(w,V,iA,nA,q,x)}"value"in k&&n(w,"value",S.value,k.value,q)}},R=(w,S,k,x,q,V,nA,iA,m)=>{const y=S.el=w?w.el:g(""),L=S.anchor=w?w.anchor:g("");let{patchFlag:O,dynamicChildren:gA,slotScopeIds:X}=S;X&&(iA=iA?iA.concat(X):X),w==null?(i(y,k,x),i(L,k,x),N(S.children||[],k,L,q,V,nA,iA,m)):O>0&&O&64&&gA&&w.dynamicChildren&&w.dynamicChildren.length===gA.length?(M(w.dynamicChildren,gA,k,q,V,nA,iA),(S.key!=null||q&&S===q.subTree)&&AC(w,S,!0)):oA(w,S,k,L,q,V,nA,iA,m)},j=(w,S,k,x,q,V,nA,iA,m)=>{S.slotScopeIds=iA,w==null?S.shapeFlag&512?q.ctx.activate(S,k,x,nA,m):rA(S,k,x,q,V,nA,m):IA(w,S,m)},rA=(w,S,k,x,q,V,nA)=>{const iA=w.component=_Q(w,x,q);if(xI(w)&&(iA.ctx.renderer=EA),KQ(iA,!1,nA),iA.asyncDep){if(q&&q.registerDep(iA,AA,nA),!w.el){const m=iA.subTree=TA(Kt);E(null,m,S,k),w.placeholder=m.el}}else AA(iA,w,S,k,q,V,nA)},IA=(w,S,k)=>{const x=S.component=w.component;if(yQ(w,S,k))if(x.asyncDep&&!x.asyncResolved){z(x,S,k);return}else x.next=S,x.update();else S.el=w.el,x.vnode=S},AA=(w,S,k,x,q,V,nA)=>{const iA=()=>{if(w.isMounted){let{next:O,bu:gA,u:X,parent:G,vnode:P}=w;{const Ce=eC(w);if(Ce){O&&(O.el=P.el,z(w,O,nA)),Ce.asyncDep.then(()=>{w.isUnmounted||iA()});return}}let fA=O,bA;Xt(w,!1),O?(O.el=P.el,z(w,O,nA)):O=P,gA&&To(gA),(bA=O.props&&O.props.onVnodeBeforeUpdate)&&ze(bA,G,O,P),Xt(w,!0);const jA=Pg(w),fe=w.subTree;w.subTree=jA,h(fe,jA,l(fe.el),_(fe),w,q,V),O.el=jA.el,fA===null&&wQ(w,jA.el),X&&Se(X,q),(bA=O.props&&O.props.onVnodeUpdated)&&Se(()=>ze(bA,G,O,P),q)}else{let O;const{el:gA,props:X}=S,{bm:G,m:P,parent:fA,root:bA,type:jA}=w,fe=qi(S);Xt(w,!1),G&&To(G),!fe&&(O=X&&X.onVnodeBeforeMount)&&ze(O,fA,S),Xt(w,!0);{bA.ce&&bA.ce._def.shadowRoot!==!1&&bA.ce._injectChildStyle(jA);const Ce=w.subTree=Pg(w);h(null,Ce,k,x,w,q,V),S.el=Ce.el}if(P&&Se(P,q),!fe&&(O=X&&X.onVnodeMounted)){const Ce=S;Se(()=>ze(O,fA,Ce),q)}(S.shapeFlag&256||fA&&qi(fA.vnode)&&fA.vnode.shapeFlag&256)&&w.a&&Se(w.a,q),w.isMounted=!0,S=k=x=null}};w.scope.on();const m=w.effect=new lI(iA);w.scope.off();const y=w.update=m.run.bind(m),L=w.job=m.runIfDirty.bind(m);L.i=w,L.id=w.uid,m.scheduler=()=>og(L),Xt(w,!0),y()},z=(w,S,k)=>{S.component=w;const x=w.vnode.props;w.vnode=S,w.next=null,kQ(w,S.props,x,k),RQ(w,S.children,k),wt(),Jg(w),Dt()},oA=(w,S,k,x,q,V,nA,iA,m=!1)=>{const y=w&&w.children,L=w?w.shapeFlag:0,O=S.children,{patchFlag:gA,shapeFlag:X}=S;if(gA>0){if(gA&128){Y(y,O,k,x,q,V,nA,iA,m);return}else if(gA&256){dA(y,O,k,x,q,V,nA,iA,m);return}}X&8?(L&16&&LA(y,q,V),O!==y&&I(k,O)):L&16?X&16?Y(y,O,k,x,q,V,nA,iA,m):LA(y,q,V,!0):(L&8&&I(k,""),X&16&&N(O,k,x,q,V,nA,iA,m))},dA=(w,S,k,x,q,V,nA,iA,m)=>{w=w||fi,S=S||fi;const y=w.length,L=S.length,O=Math.min(y,L);let gA;for(gA=0;gA<O;gA++){const X=S[gA]=m?xt(S[gA]):tt(S[gA]);h(w[gA],X,k,null,q,V,nA,iA,m)}y>L?LA(w,q,V,!0,!1,O):N(S,k,x,q,V,nA,iA,m,O)},Y=(w,S,k,x,q,V,nA,iA,m)=>{let y=0;const L=S.length;let O=w.length-1,gA=L-1;for(;y<=O&&y<=gA;){const X=w[y],G=S[y]=m?xt(S[y]):tt(S[y]);if(xi(X,G))h(X,G,k,null,q,V,nA,iA,m);else break;y++}for(;y<=O&&y<=gA;){const X=w[O],G=S[gA]=m?xt(S[gA]):tt(S[gA]);if(xi(X,G))h(X,G,k,null,q,V,nA,iA,m);else break;O--,gA--}if(y>O){if(y<=gA){const X=gA+1,G=X<L?S[X].el:x;for(;y<=gA;)h(null,S[y]=m?xt(S[y]):tt(S[y]),k,G,q,V,nA,iA,m),y++}}else if(y>gA)for(;y<=O;)$(w[y],q,V,!0),y++;else{const X=y,G=y,P=new Map;for(y=G;y<=gA;y++){const Fe=S[y]=m?xt(S[y]):tt(S[y]);Fe.key!=null&&P.set(Fe.key,y)}let fA,bA=0;const jA=gA-G+1;let fe=!1,Ce=0;const jt=new Array(jA);for(y=0;y<jA;y++)jt[y]=0;for(y=X;y<=O;y++){const Fe=w[y];if(bA>=jA){$(Fe,q,V,!0);continue}let Xe;if(Fe.key!=null)Xe=P.get(Fe.key);else for(fA=G;fA<=gA;fA++)if(jt[fA-G]===0&&xi(Fe,S[fA])){Xe=fA;break}Xe===void 0?$(Fe,q,V,!0):(jt[Xe-G]=y+1,Xe>=Ce?Ce=Xe:fe=!0,h(Fe,S[Xe],k,null,q,V,nA,iA,m),bA++)}const Mn=fe?LQ(jt):fi;for(fA=Mn.length-1,y=jA-1;y>=0;y--){const Fe=G+y,Xe=S[Fe],Ng=S[Fe+1],Gg=Fe+1<L?Ng.el||tC(Ng):x;jt[y]===0?h(null,Xe,k,Gg,q,V,nA,iA,m):fe&&(fA<0||y!==Mn[fA]?W(Xe,k,Gg,2):fA--)}}},W=(w,S,k,x,q=null)=>{const{el:V,type:nA,transition:iA,children:m,shapeFlag:y}=w;if(y&6){W(w.component.subTree,S,k,x);return}if(y&128){w.suspense.move(S,k,x);return}if(y&64){nA.move(w,S,k,EA);return}if(nA===qA){i(V,S,k);for(let O=0;O<m.length;O++)W(m[O],S,k,x);i(w.anchor,S,k);return}if(nA===Yo){c(w,S,k);return}if(x!==2&&y&1&&iA)if(x===0)iA.beforeEnter(V),i(V,S,k),Se(()=>iA.enter(V),q);else{const{leave:O,delayLeave:gA,afterLeave:X}=iA,G=()=>{w.ctx.isUnmounted?o(V):i(V,S,k)},P=()=>{V._isLeaving&&V[XB](!0),O(V,()=>{G(),X&&X()})};gA?gA(V,G,P):P()}else i(V,S,k)},$=(w,S,k,x=!1,q=!1)=>{const{type:V,props:nA,ref:iA,children:m,dynamicChildren:y,shapeFlag:L,patchFlag:O,dirs:gA,cacheIndex:X}=w;if(O===-2&&(q=!1),iA!=null&&(wt(),Wi(iA,null,k,w,!0),Dt()),X!=null&&(S.renderCache[X]=void 0),L&256){S.ctx.deactivate(w);return}const G=L&1&&gA,P=!qi(w);let fA;if(P&&(fA=nA&&nA.onVnodeBeforeUnmount)&&ze(fA,S,w),L&6)oe(w.component,k,x);else{if(L&128){w.suspense.unmount(k,x);return}G&&Zt(w,null,S,"beforeUnmount"),L&64?w.type.remove(w,S,k,EA,x):y&&!y.hasOnce&&(V!==qA||O>0&&O&64)?LA(y,S,k,!1,!0):(V===qA&&O&384||!q&&L&16)&&LA(m,S,k),x&&DA(w)}(P&&(fA=nA&&nA.onVnodeUnmounted)||G)&&Se(()=>{fA&&ze(fA,S,w),G&&Zt(w,null,S,"unmounted")},k)},DA=w=>{const{type:S,el:k,anchor:x,transition:q}=w;if(S===qA){NA(k,x);return}if(S===Yo){p(w);return}const V=()=>{o(k),q&&!q.persisted&&q.afterLeave&&q.afterLeave()};if(w.shapeFlag&1&&q&&!q.persisted){const{leave:nA,delayLeave:iA}=q,m=()=>nA(k,V);iA?iA(w.el,V,m):m()}else V()},NA=(w,S)=>{let k;for(;w!==S;)k=C(w),o(w),w=k;o(S)},oe=(w,S,k)=>{const{bum:x,scope:q,job:V,subTree:nA,um:iA,m,a:y}=w;qg(m),qg(y),x&&To(x),q.stop(),V&&(V.flags|=8,$(nA,w,S,k)),iA&&Se(iA,S),Se(()=>{w.isUnmounted=!0},S)},LA=(w,S,k,x=!1,q=!1,V=0)=>{for(let nA=V;nA<w.length;nA++)$(w[nA],S,k,x,q)},_=w=>{if(w.shapeFlag&6)return _(w.component.subTree);if(w.shapeFlag&128)return w.suspense.next();const S=C(w.anchor||w.el),k=S&&S[jB];return k?C(k):S};let eA=!1;const tA=(w,S,k)=>{let x;w==null?S._vnode&&($(S._vnode,null,null,!0),x=S._vnode.component):h(S._vnode||null,w,S,null,null,null,k),S._vnode=w,eA||(eA=!0,Jg(x),bI(),eA=!1)},EA={p:h,um:$,m:W,r:DA,mt:rA,mc:N,pc:oA,pbc:M,n:_,o:A};return{render:tA,hydrate:void 0,createApp:uQ(tA)}}function Kn({type:A,props:e},t){return t==="svg"&&A==="foreignObject"||t==="mathml"&&A==="annotation-xml"&&e&&e.encoding&&e.encoding.includes("html")?void 0:t}function Xt({effect:A,job:e},t){t?(A.flags|=32,e.flags|=4):(A.flags&=-33,e.flags&=-5)}function GQ(A,e){return(!A||A&&!A.pendingBranch)&&e&&!e.persisted}function AC(A,e,t=!1){const i=A.children,o=e.children;if(yA(i)&&yA(o))for(let n=0;n<i.length;n++){const r=i[n];let g=o[n];g.shapeFlag&1&&!g.dynamicChildren&&((g.patchFlag<=0||g.patchFlag===32)&&(g=o[n]=xt(o[n]),g.el=r.el),!t&&g.patchFlag!==-2&&AC(r,g)),g.type===ho&&(g.patchFlag!==-1?g.el=r.el:g.__elIndex=n+(A.type===qA?1:0)),g.type===Kt&&!g.el&&(g.el=r.el)}}function LQ(A){const e=A.slice(),t=[0];let i,o,n,r,g;const s=A.length;for(i=0;i<s;i++){const a=A[i];if(a!==0){if(o=t[t.length-1],A[o]<a){e[i]=o,t.push(i);continue}for(n=0,r=t.length-1;n<r;)g=n+r>>1,A[t[g]]<a?n=g+1:r=g;a<A[t[n]]&&(n>0&&(e[i]=t[n-1]),t[n]=i)}}for(n=t.length,r=t[n-1];n-- >0;)t[n]=r,r=e[r];return t}function eC(A){const e=A.subTree.component;if(e)return e.asyncDep&&!e.asyncResolved?e:eC(e)}function qg(A){if(A)for(let e=0;e<A.length;e++)A[e].flags|=8}function tC(A){if(A.placeholder)return A.placeholder;const e=A.component;return e?tC(e.subTree):null}const iC=A=>A.__isSuspense;function UQ(A,e){e&&e.pendingBranch?yA(A)?e.effects.push(...A):e.effects.push(A):OB(A)}const qA=Symbol.for("v-fgt"),ho=Symbol.for("v-txt"),Kt=Symbol.for("v-cmt"),Yo=Symbol.for("v-stc"),Zi=[];let be=null;function T(A=!1){Zi.push(be=A?null:[])}function MQ(){Zi.pop(),be=Zi[Zi.length-1]||null}let to=1;function $o(A,e=!1){to+=A,A<0&&be&&e&&(be.hasOnce=!0)}function oC(A){return A.dynamicChildren=to>0?be||fi:null,MQ(),to>0&&be&&be.push(A),A}function K(A,e,t,i,o,n){return oC(B(A,e,t,i,o,n,!0))}function ye(A,e,t,i,o){return oC(TA(A,e,t,i,o,!0))}function An(A){return A?A.__v_isVNode===!0:!1}function xi(A,e){return A.type===e.type&&A.key===e.key}const nC=({key:A})=>A??null,_o=({ref:A,ref_key:e,ref_for:t})=>(typeof A=="number"&&(A=""+A),A!=null?ee(A)||le(A)||FA(A)?{i:Re,r:A,k:e,f:!!t}:A:null);function B(A,e=null,t=null,i=0,o=null,n=A===qA?0:1,r=!1,g=!1){const s={__v_isVNode:!0,__v_skip:!0,type:A,props:e,key:e&&nC(e),ref:e&&_o(e),scopeId:GI,slotScopeIds:null,children:t,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetStart:null,targetAnchor:null,staticCount:0,shapeFlag:n,patchFlag:i,dynamicProps:o,dynamicChildren:null,appContext:null,ctx:Re};return g?(sg(s,t),n&128&&A.normalize(s)):t&&(s.shapeFlag|=ee(t)?8:16),to>0&&!r&&be&&(s.patchFlag>0||n&6)&&s.patchFlag!==32&&be.push(s),s}const TA=xQ;function xQ(A,e=null,t=null,i=0,o=null,n=!1){if((!A||A===HI)&&(A=Kt),An(A)){const g=ki(A,e,!0);return t&&sg(g,t),to>0&&!n&&be&&(g.shapeFlag&6?be[be.indexOf(A)]=g:be.push(g)),g.patchFlag=-2,g}if(qQ(A)&&(A=A.__vccOpts),e){e=JQ(e);let{class:g,style:s}=e;g&&!ee(g)&&(e.class=kA(g)),KA(s)&&(tg(s)&&!yA(s)&&(s=ue({},s)),e.style=VA(s))}const r=ee(A)?1:iC(A)?128:ZB(A)?64:KA(A)?4:FA(A)?2:0;return B(A,e,t,i,o,r,n,!0)}function JQ(A){return A?tg(A)||qI(A)?ue({},A):A:null}function ki(A,e,t=!1,i=!1){const{props:o,ref:n,patchFlag:r,children:g,transition:s}=A,a=e?TQ(o||{},e):o,I={__v_isVNode:!0,__v_skip:!0,type:A.type,props:a,key:a&&nC(a),ref:e&&e.ref?t&&n?yA(n)?n.concat(_o(e)):[n,_o(e)]:_o(e):n,scopeId:A.scopeId,slotScopeIds:A.slotScopeIds,children:g,target:A.target,targetStart:A.targetStart,targetAnchor:A.targetAnchor,staticCount:A.staticCount,shapeFlag:A.shapeFlag,patchFlag:e&&A.type!==qA?r===-1?16:r|16:r,dynamicProps:A.dynamicProps,dynamicChildren:A.dynamicChildren,appContext:A.appContext,dirs:A.dirs,transition:s,component:A.component,suspense:A.suspense,ssContent:A.ssContent&&ki(A.ssContent),ssFallback:A.ssFallback&&ki(A.ssFallback),placeholder:A.placeholder,el:A.el,anchor:A.anchor,ctx:A.ctx,ce:A.ce};return s&&i&&ng(I,s.clone(I)),I}function gt(A=" ",e=0){return TA(ho,null,A,e)}function Dn(A,e){const t=TA(Yo,null,A);return t.staticCount=e,t}function CA(A="",e=!1){return e?(T(),ye(Kt,null,A)):TA(Kt,null,A)}function tt(A){return A==null||typeof A=="boolean"?TA(Kt):yA(A)?TA(qA,null,A.slice()):An(A)?xt(A):TA(ho,null,String(A))}function xt(A){return A.el===null&&A.patchFlag!==-1||A.memo?A:ki(A)}function sg(A,e){let t=0;const{shapeFlag:i}=A;if(e==null)e=null;else if(yA(e))t=16;else if(typeof e=="object")if(i&65){const o=e.default;o&&(o._c&&(o._d=!1),sg(A,o()),o._c&&(o._d=!0));return}else{t=32;const o=e._;!o&&!qI(e)?e._ctx=Re:o===3&&Re&&(Re.slots._===1?e._=1:(e._=2,A.patchFlag|=1024))}else FA(e)?(e={default:e,_ctx:Re},t=32):(e=String(e),i&64?(t=16,e=[gt(e)]):t=8);A.children=e,A.shapeFlag|=t}function TQ(...A){const e={};for(let t=0;t<A.length;t++){const i=A[t];for(const o in i)if(o==="class")e.class!==i.class&&(e.class=kA([e.class,i.class]));else if(o==="style")e.style=VA([e.style,i.style]);else if(Bn(o)){const n=e[o],r=i[o];r&&n!==r&&!(yA(n)&&n.includes(r))&&(e[o]=n?[].concat(n,r):r)}else o!==""&&(e[o]=i[o])}return e}function ze(A,e,t,i=null){Ct(A,e,7,[t,i])}const HQ=OI();let YQ=0;function _Q(A,e,t){const i=A.type,o=(e?e.appContext:A.appContext)||HQ,n={uid:YQ++,vnode:A,type:i,parent:e,appContext:o,root:null,next:null,subTree:null,effect:null,update:null,job:null,scope:new CI(!0),render:null,proxy:null,exposed:null,exposeProxy:null,withProxy:null,provides:e?e.provides:Object.create(o.provides),ids:e?e.ids:["",0,0],accessCache:null,renderCache:[],components:null,directives:null,propsOptions:ZI(i,o),emitsOptions:PI(i,o),emit:null,emitted:null,propsDefaults:_A,inheritAttrs:i.inheritAttrs,ctx:_A,data:_A,props:_A,attrs:_A,slots:_A,refs:_A,setupState:_A,setupContext:null,suspense:t,suspenseId:t?t.pendingId:0,asyncDep:null,asyncResolved:!1,isMounted:!1,isUnmounted:!1,isDeactivated:!1,bc:null,c:null,bm:null,m:null,bu:null,u:null,um:null,bum:null,da:null,a:null,rtg:null,rtc:null,ec:null,sp:null};return n.ctx={_:n},n.root=e?e.root:n,n.emit=hQ.bind(null,n),A.ce&&A.ce(n),n}let Ee=null;const vi=()=>Ee||Re;let en,hr;{const A=fn(),e=(t,i)=>{let o;return(o=A[t])||(o=A[t]=[]),o.push(i),n=>{o.length>1?o.forEach(r=>r(n)):o[0](n)}};en=e("__VUE_INSTANCE_SETTERS__",t=>Ee=t),hr=e("__VUE_SSR_SETTERS__",t=>io=t)}const po=A=>{const e=Ee;return en(A),A.scope.on(),()=>{A.scope.off(),en(e)}},jg=()=>{Ee&&Ee.scope.off(),en(null)};function rC(A){return A.vnode.shapeFlag&4}let io=!1;function KQ(A,e=!1,t=!1){e&&hr(e);const{props:i,children:o}=A.vnode,n=rC(A);DQ(A,i,n,e),SQ(A,o,t||e);const r=n?OQ(A,e):void 0;return e&&hr(!1),r}function OQ(A,e){const t=A.type;A.accessCache=Object.create(null),A.proxy=new Proxy(A.ctx,IQ);const{setup:i}=t;if(i){wt();const o=A.setupContext=i.length>1?VQ(A):null,n=po(A),r=Eo(i,A,0,[A.props,o]),g=oI(r);if(Dt(),n(),(g||A.sp)&&!qi(A)&&MI(A),g){if(r.then(jg,jg),e)return r.then(s=>{Zg(A,s)}).catch(s=>{mn(s,A,0)});A.asyncDep=r}else Zg(A,r)}else gC(A)}function Zg(A,e,t){FA(e)?A.type.__ssrInlineRender?A.ssrRender=e:A.render=e:KA(e)&&(A.setupState=FI(e)),gC(A)}function gC(A,e,t){const i=A.type;A.render||(A.render=i.render||nt);{const o=po(A);wt();try{CQ(A)}finally{Dt(),o()}}}const PQ={get(A,e){return ce(A,"get",""),A[e]}};function VQ(A){const e=t=>{A.exposed=t||{}};return{attrs:new Proxy(A.attrs,PQ),slots:A.slots,emit:A.emit,expose:e}}function kn(A){return A.exposed?A.exposeProxy||(A.exposeProxy=new Proxy(FI(_i(A.exposed)),{get(e,t){if(t in e)return e[t];if(t in ji)return ji[t](A)},has(e,t){return t in e||t in ji}})):A.proxy}function WQ(A,e=!0){return FA(A)?A.displayName||A.name:A.name||e&&A.__name}function qQ(A){return FA(A)&&"__vccOpts"in A}const hA=(A,e)=>TB(A,e,io);function GA(A,e,t){try{$o(-1);const i=arguments.length;return i===2?KA(e)&&!yA(e)?An(e)?TA(A,null,[e]):TA(A,e):TA(A,null,e):(i>3?t=Array.prototype.slice.call(arguments,2):i===3&&An(t)&&(t=[t]),TA(A,e,t))}finally{$o(1)}}const jQ="3.5.26";/**
+* @vue/runtime-dom v3.5.26
+* (c) 2018-present Yuxi (Evan) You and Vue contributors
+* @license MIT
+**/let dr;const Xg=typeof window<"u"&&window.trustedTypes;if(Xg)try{dr=Xg.createPolicy("vue",{createHTML:A=>A})}catch{}const sC=dr?A=>dr.createHTML(A):A=>A,ZQ="http://www.w3.org/2000/svg",XQ="http://www.w3.org/1998/Math/MathML",Et=typeof document<"u"?document:null,zg=Et&&Et.createElement("template"),zQ={insert:(A,e,t)=>{e.insertBefore(A,t||null)},remove:A=>{const e=A.parentNode;e&&e.removeChild(A)},createElement:(A,e,t,i)=>{const o=e==="svg"?Et.createElementNS(ZQ,A):e==="mathml"?Et.createElementNS(XQ,A):t?Et.createElement(A,{is:t}):Et.createElement(A);return A==="select"&&i&&i.multiple!=null&&o.setAttribute("multiple",i.multiple),o},createText:A=>Et.createTextNode(A),createComment:A=>Et.createComment(A),setText:(A,e)=>{A.nodeValue=e},setElementText:(A,e)=>{A.textContent=e},parentNode:A=>A.parentNode,nextSibling:A=>A.nextSibling,querySelector:A=>Et.querySelector(A),setScopeId(A,e){A.setAttribute(e,"")},insertStaticContent(A,e,t,i,o,n){const r=t?t.previousSibling:e.lastChild;if(o&&(o===n||o.nextSibling))for(;e.insertBefore(o.cloneNode(!0),t),!(o===n||!(o=o.nextSibling)););else{zg.innerHTML=sC(i==="svg"?`<svg>${A}</svg>`:i==="mathml"?`<math>${A}</math>`:A);const g=zg.content;if(i==="svg"||i==="mathml"){const s=g.firstChild;for(;s.firstChild;)g.appendChild(s.firstChild);g.removeChild(s)}e.insertBefore(g,t)}return[r?r.nextSibling:e.firstChild,t?t.previousSibling:e.lastChild]}},$Q=Symbol("_vtc");function Ac(A,e,t){const i=A[$Q];i&&(e=(e?[e,...i]:[...i]).join(" ")),e==null?A.removeAttribute("class"):t?A.setAttribute("class",e):A.className=e}const $g=Symbol("_vod"),ec=Symbol("_vsh"),tc=Symbol(""),ic=/(?:^|;)\s*display\s*:/;function oc(A,e,t){const i=A.style,o=ee(t);let n=!1;if(t&&!o){if(e)if(ee(e))for(const r of e.split(";")){const g=r.slice(0,r.indexOf(":")).trim();t[g]==null&&Ko(i,g,"")}else for(const r in e)t[r]==null&&Ko(i,r,"");for(const r in t)r==="display"&&(n=!0),Ko(i,r,t[r])}else if(o){if(e!==t){const r=i[tc];r&&(t+=";"+r),i.cssText=t,n=ic.test(t)}}else e&&A.removeAttribute("style");$g in A&&(A[$g]=n?i.display:"",A[ec]&&(i.display="none"))}const As=/\s*!important$/;function Ko(A,e,t){if(yA(t))t.forEach(i=>Ko(A,e,i));else if(t==null&&(t=""),e.startsWith("--"))A.setProperty(e,t);else{const i=nc(A,e);As.test(t)?A.setProperty(Ii(i),t.replace(As,""),"important"):A[i]=t}}const es=["Webkit","Moz","ms"],On={};function nc(A,e){const t=On[e];if(t)return t;let i=Je(e);if(i!=="filter"&&i in A)return On[e]=i;i=En(i);for(let o=0;o<es.length;o++){const n=es[o]+i;if(n in A)return On[e]=n}return e}const ts="http://www.w3.org/1999/xlink";function is(A,e,t,i,o,n=BB(e)){i&&e.startsWith("xlink:")?t==null?A.removeAttributeNS(ts,e.slice(6,e.length)):A.setAttributeNS(ts,e,t):t==null||n&&!sI(t)?A.removeAttribute(e):A.setAttribute(e,n?"":It(t)?String(t):t)}function os(A,e,t,i,o){if(e==="innerHTML"||e==="textContent"){t!=null&&(A[e]=e==="innerHTML"?sC(t):t);return}const n=A.tagName;if(e==="value"&&n!=="PROGRESS"&&!n.includes("-")){const g=n==="OPTION"?A.getAttribute("value")||"":A.value,s=t==null?A.type==="checkbox"?"on":"":String(t);(g!==s||!("_value"in A))&&(A.value=s),t==null&&A.removeAttribute(e),A._value=t;return}let r=!1;if(t===""||t==null){const g=typeof A[e];g==="boolean"?t=sI(t):t==null&&g==="string"?(t="",r=!0):g==="number"&&(t=0,r=!0)}try{A[e]=t}catch{}r&&A.removeAttribute(o||e)}function ti(A,e,t,i){A.addEventListener(e,t,i)}function rc(A,e,t,i){A.removeEventListener(e,t,i)}const ns=Symbol("_vei");function gc(A,e,t,i,o=null){const n=A[ns]||(A[ns]={}),r=n[e];if(i&&r)r.value=i;else{const[g,s]=sc(e);if(i){const a=n[e]=Cc(i,o);ti(A,g,a,s)}else r&&(rc(A,g,r,s),n[e]=void 0)}}const rs=/(?:Once|Passive|Capture)$/;function sc(A){let e;if(rs.test(A)){e={};let i;for(;i=A.match(rs);)A=A.slice(0,A.length-i[0].length),e[i[0].toLowerCase()]=!0}return[A[2]===":"?A.slice(3):Ii(A.slice(2)),e]}let Pn=0;const ac=Promise.resolve(),Ic=()=>Pn||(ac.then(()=>Pn=0),Pn=Date.now());function Cc(A,e){const t=i=>{if(!i._vts)i._vts=Date.now();else if(i._vts<=t.attached)return;Ct(lc(i,t.value),e,5,[i])};return t.value=A,t.attached=Ic(),t}function lc(A,e){if(yA(e)){const t=A.stopImmediatePropagation;return A.stopImmediatePropagation=()=>{t.call(A),A._stopped=!0},e.map(i=>o=>!o._stopped&&i&&i(o))}else return e}const gs=A=>A.charCodeAt(0)===111&&A.charCodeAt(1)===110&&A.charCodeAt(2)>96&&A.charCodeAt(2)<123,Bc=(A,e,t,i,o,n)=>{const r=o==="svg";e==="class"?Ac(A,i,r):e==="style"?oc(A,t,i):Bn(e)?Wr(e)||gc(A,e,t,i,n):(e[0]==="."?(e=e.slice(1),!0):e[0]==="^"?(e=e.slice(1),!1):Qc(A,e,i,r))?(os(A,e,i),!A.tagName.includes("-")&&(e==="value"||e==="checked"||e==="selected")&&is(A,e,i,r,n,e!=="value")):A._isVueCE&&(/[A-Z]/.test(e)||!ee(i))?os(A,Je(e),i,n,e):(e==="true-value"?A._trueValue=i:e==="false-value"&&(A._falseValue=i),is(A,e,i,r))};function Qc(A,e,t,i){if(i)return!!(e==="innerHTML"||e==="textContent"||e in A&&gs(e)&&FA(t));if(e==="spellcheck"||e==="draggable"||e==="translate"||e==="autocorrect"||e==="sandbox"&&A.tagName==="IFRAME"||e==="form"||e==="list"&&A.tagName==="INPUT"||e==="type"&&A.tagName==="TEXTAREA")return!1;if(e==="width"||e==="height"){const o=A.tagName;if(o==="IMG"||o==="VIDEO"||o==="CANVAS"||o==="SOURCE")return!1}return gs(e)&&ee(t)?!1:e in A}const tn=A=>{const e=A.props["onUpdate:modelValue"]||!1;return yA(e)?t=>To(e,t):e};function cc(A){A.target.composing=!0}function ss(A){const e=A.target;e.composing&&(e.composing=!1,e.dispatchEvent(new Event("input")))}const mi=Symbol("_assign");function as(A,e,t){return e&&(A=A.trim()),t&&(A=un(A)),A}const te={created(A,{modifiers:{lazy:e,trim:t,number:i}},o){A[mi]=tn(o);const n=i||o.props&&o.props.type==="number";ti(A,e?"change":"input",r=>{r.target.composing||A[mi](as(A.value,t,n))}),(t||n)&&ti(A,"change",()=>{A.value=as(A.value,t,n)}),e||(ti(A,"compositionstart",cc),ti(A,"compositionend",ss),ti(A,"change",ss))},mounted(A,{value:e}){A.value=e??""},beforeUpdate(A,{value:e,oldValue:t,modifiers:{lazy:i,trim:o,number:n}},r){if(A[mi]=tn(r),A.composing)return;const g=(n||A.type==="number")&&!/^0\d/.test(A.value)?un(A.value):A.value,s=e??"";g!==s&&(document.activeElement===A&&A.type!=="range"&&(i&&e===t||o&&A.value.trim()===s)||(A.value=s))}},pr={deep:!0,created(A,{value:e,modifiers:{number:t}},i){const o=Qn(e);ti(A,"change",()=>{const n=Array.prototype.filter.call(A.options,r=>r.selected).map(r=>t?un(on(r)):on(r));A[mi](A.multiple?o?new Set(n):n:n[0]),A._assigning=!0,uo(()=>{A._assigning=!1})}),A[mi]=tn(i)},mounted(A,{value:e}){Is(A,e)},beforeUpdate(A,e,t){A[mi]=tn(t)},updated(A,{value:e}){A._assigning||Is(A,e)}};function Is(A,e){const t=A.multiple,i=yA(e);if(!(t&&!i&&!Qn(e))){for(let o=0,n=A.options.length;o<n;o++){const r=A.options[o],g=on(r);if(t)if(i){const s=typeof g;s==="string"||s==="number"?r.selected=e.some(a=>String(a)===String(g)):r.selected=cB(e,g)>-1}else r.selected=e.has(g);else if(hn(on(r),e)){A.selectedIndex!==o&&(A.selectedIndex=o);return}}!t&&A.selectedIndex!==-1&&(A.selectedIndex=-1)}}function on(A){return"_value"in A?A._value:A.value}const Ec=["ctrl","shift","alt","meta"],uc={stop:A=>A.stopPropagation(),prevent:A=>A.preventDefault(),self:A=>A.target!==A.currentTarget,ctrl:A=>!A.ctrlKey,shift:A=>!A.shiftKey,alt:A=>!A.altKey,meta:A=>!A.metaKey,left:A=>"button"in A&&A.button!==0,middle:A=>"button"in A&&A.button!==1,right:A=>"button"in A&&A.button!==2,exact:(A,e)=>Ec.some(t=>A[`${t}Key`]&&!e.includes(t))},dt=(A,e)=>{const t=A._withMods||(A._withMods={}),i=e.join(".");return t[i]||(t[i]=(o,...n)=>{for(let r=0;r<e.length;r++){const g=uc[e[r]];if(g&&g(o,e))return}return A(o,...n)})},fc=ue({patchProp:Bc},zQ);let Cs;function hc(){return Cs||(Cs=bQ(fc))}const dc=(...A)=>{const e=hc().createApp(...A),{mount:t}=e;return e.mount=i=>{const o=mc(i);if(!o)return;const n=e._component;!FA(n)&&!n.render&&!n.template&&(n.template=o.innerHTML),o.nodeType===1&&(o.textContent="");const r=t(o,!1,pc(o));return o instanceof Element&&(o.removeAttribute("v-cloak"),o.setAttribute("data-v-app","")),r},e};function pc(A){if(A instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&A instanceof MathMLElement)return"mathml"}function mc(A){return ee(A)?document.querySelector(A):A}/*!
+ * shared v9.14.5
+ * (c) 2025 kazuya kawaguchi
+ * Released under the MIT License.
+ */function yc(A,e){typeof console<"u"&&(console.warn("[intlify] "+A),e&&console.warn(e.stack))}const nn=typeof window<"u",Vt=(A,e=!1)=>e?Symbol.for(A):Symbol(A),wc=(A,e,t)=>Dc({l:A,k:e,s:t}),Dc=A=>JSON.stringify(A).replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029").replace(/\u0027/g,"\\u0027"),Ae=A=>typeof A=="number"&&isFinite(A),kc=A=>IC(A)==="[object Date]",Ot=A=>IC(A)==="[object RegExp]",vn=A=>vA(A)&&Object.keys(A).length===0,Be=Object.assign,vc=Object.create,HA=(A=null)=>vc(A);let ls;const ht=()=>ls||(ls=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:HA());function Bs(A){return A.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/").replace(/=/g,"=")}function Qs(A){return A.replace(/&(?![a-zA-Z0-9#]{2,6};)/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(/</g,"<").replace(/>/g,">")}function Fc(A){return A=A.replace(/(\w+)\s*=\s*"([^"]*)"/g,(i,o,n)=>`${o}="${Qs(n)}"`),A=A.replace(/(\w+)\s*=\s*'([^']*)'/g,(i,o,n)=>`${o}='${Qs(n)}'`),/\s*on\w+\s*=\s*["']?[^"'>]+["']?/gi.test(A)&&(A=A.replace(/(\s+)(on)(\w+\s*=)/gi,"$1on$3")),[/(\s+(?:href|src|action|formaction)\s*=\s*["']?)\s*javascript:/gi,/(style\s*=\s*["'][^"']*url\s*\(\s*)javascript:/gi].forEach(i=>{A=A.replace(i,"$1javascript:")}),A}const Sc=Object.prototype.hasOwnProperty;function He(A,e){return Sc.call(A,e)}const WA=Array.isArray,PA=A=>typeof A=="function",aA=A=>typeof A=="string",RA=A=>typeof A=="boolean",UA=A=>A!==null&&typeof A=="object",Rc=A=>UA(A)&&PA(A.then)&&PA(A.catch),aC=Object.prototype.toString,IC=A=>aC.call(A),vA=A=>{if(!UA(A))return!1;const e=Object.getPrototypeOf(A);return e===null||e.constructor===Object},bc=A=>A==null?"":WA(A)||vA(A)&&A.toString===aC?JSON.stringify(A,null,2):String(A);function Nc(A,e=""){return A.reduce((t,i,o)=>o===0?t+i:t+e+i,"")}function Fn(A){let e=A;return()=>++e}const Ro=A=>!UA(A)||WA(A);function Oo(A,e){if(Ro(A)||Ro(e))throw new Error("Invalid value");const t=[{src:A,des:e}];for(;t.length;){const{src:i,des:o}=t.pop();Object.keys(i).forEach(n=>{n!=="__proto__"&&(UA(i[n])&&!UA(o[n])&&(o[n]=Array.isArray(i[n])?[]:HA()),Ro(o[n])||Ro(i[n])?o[n]=i[n]:t.push({src:i[n],des:o[n]}))})}}/*!
+ * message-compiler v9.14.5
+ * (c) 2025 kazuya kawaguchi
+ * Released under the MIT License.
+ */function Gc(A,e,t){return{line:A,column:e,offset:t}}function rn(A,e,t){return{start:A,end:e}}const Lc=/\{([0-9a-zA-Z]+)\}/g;function CC(A,...e){return e.length===1&&Uc(e[0])&&(e=e[0]),(!e||!e.hasOwnProperty)&&(e={}),A.replace(Lc,(t,i)=>e.hasOwnProperty(i)?e[i]:"")}const lC=Object.assign,cs=A=>typeof A=="string",Uc=A=>A!==null&&typeof A=="object";function BC(A,e=""){return A.reduce((t,i,o)=>o===0?t+i:t+e+i,"")}const ag={USE_MODULO_SYNTAX:1,__EXTEND_POINT__:2},Mc={[ag.USE_MODULO_SYNTAX]:"Use modulo before '{{0}}'."};function xc(A,e,...t){const i=CC(Mc[A],...t||[]),o={message:String(i),code:A};return e&&(o.location=e),o}const pA={EXPECTED_TOKEN:1,INVALID_TOKEN_IN_PLACEHOLDER:2,UNTERMINATED_SINGLE_QUOTE_IN_PLACEHOLDER:3,UNKNOWN_ESCAPE_SEQUENCE:4,INVALID_UNICODE_ESCAPE_SEQUENCE:5,UNBALANCED_CLOSING_BRACE:6,UNTERMINATED_CLOSING_BRACE:7,EMPTY_PLACEHOLDER:8,NOT_ALLOW_NEST_PLACEHOLDER:9,INVALID_LINKED_FORMAT:10,MUST_HAVE_MESSAGES_IN_PLURAL:11,UNEXPECTED_EMPTY_LINKED_MODIFIER:12,UNEXPECTED_EMPTY_LINKED_KEY:13,UNEXPECTED_LEXICAL_ANALYSIS:14,UNHANDLED_CODEGEN_NODE_TYPE:15,UNHANDLED_MINIFIER_NODE_TYPE:16,__EXTEND_POINT__:17},Jc={[pA.EXPECTED_TOKEN]:"Expected token: '{0}'",[pA.INVALID_TOKEN_IN_PLACEHOLDER]:"Invalid token in placeholder: '{0}'",[pA.UNTERMINATED_SINGLE_QUOTE_IN_PLACEHOLDER]:"Unterminated single quote in placeholder",[pA.UNKNOWN_ESCAPE_SEQUENCE]:"Unknown escape sequence: \\{0}",[pA.INVALID_UNICODE_ESCAPE_SEQUENCE]:"Invalid unicode escape sequence: {0}",[pA.UNBALANCED_CLOSING_BRACE]:"Unbalanced closing brace",[pA.UNTERMINATED_CLOSING_BRACE]:"Unterminated closing brace",[pA.EMPTY_PLACEHOLDER]:"Empty placeholder",[pA.NOT_ALLOW_NEST_PLACEHOLDER]:"Not allowed nest placeholder",[pA.INVALID_LINKED_FORMAT]:"Invalid linked format",[pA.MUST_HAVE_MESSAGES_IN_PLURAL]:"Plural must have messages",[pA.UNEXPECTED_EMPTY_LINKED_MODIFIER]:"Unexpected empty linked modifier",[pA.UNEXPECTED_EMPTY_LINKED_KEY]:"Unexpected empty linked key",[pA.UNEXPECTED_LEXICAL_ANALYSIS]:"Unexpected lexical analysis in token: '{0}'",[pA.UNHANDLED_CODEGEN_NODE_TYPE]:"unhandled codegen node type: '{0}'",[pA.UNHANDLED_MINIFIER_NODE_TYPE]:"unhandled mimifier node type: '{0}'"};function Gi(A,e,t={}){const{domain:i,messages:o,args:n}=t,r=CC((o||Jc)[A]||"",...n||[]),g=new SyntaxError(String(r));return g.code=A,e&&(g.location=e),g.domain=i,g}function Tc(A){throw A}const Bt=" ",Hc="\r",de=`
+`,Yc="\u2028",_c="\u2029";function Kc(A){const e=A;let t=0,i=1,o=1,n=0;const r=b=>e[b]===Hc&&e[b+1]===de,g=b=>e[b]===de,s=b=>e[b]===_c,a=b=>e[b]===Yc,I=b=>r(b)||g(b)||s(b)||a(b),l=()=>t,C=()=>i,Q=()=>o,f=()=>n,h=b=>r(b)||s(b)||a(b)?de:e[b],u=()=>h(t),E=()=>h(t+n);function D(){return n=0,I(t)&&(i++,o=0),r(t)&&t++,t++,o++,e[t]}function c(){return r(t+n)&&n++,n++,e[t+n]}function p(){t=0,i=1,o=1,n=0}function d(b=0){n=b}function v(){const b=t+n;for(;b!==t;)D();n=0}return{index:l,line:C,column:Q,peekOffset:f,charAt:h,currentChar:u,currentPeek:E,next:D,peek:c,reset:p,resetPeek:d,skipToPeek:v}}const Rt=void 0,Oc=".",Es="'",Pc="tokenizer";function Vc(A,e={}){const t=e.location!==!1,i=Kc(A),o=()=>i.index(),n=()=>Gc(i.line(),i.column(),i.index()),r=n(),g=o(),s={currentType:14,offset:g,startLoc:r,endLoc:r,lastType:14,lastOffset:g,lastStartLoc:r,lastEndLoc:r,braceNest:0,inLinked:!1,text:""},a=()=>s,{onError:I}=e;function l(m,y,L,...O){const gA=a();if(y.column+=L,y.offset+=L,I){const X=t?rn(gA.startLoc,y):null,G=Gi(m,X,{domain:Pc,args:O});I(G)}}function C(m,y,L){m.endLoc=n(),m.currentType=y;const O={type:y};return t&&(O.loc=rn(m.startLoc,m.endLoc)),L!=null&&(O.value=L),O}const Q=m=>C(m,14);function f(m,y){return m.currentChar()===y?(m.next(),y):(l(pA.EXPECTED_TOKEN,n(),0,y),"")}function h(m){let y="";for(;m.currentPeek()===Bt||m.currentPeek()===de;)y+=m.currentPeek(),m.peek();return y}function u(m){const y=h(m);return m.skipToPeek(),y}function E(m){if(m===Rt)return!1;const y=m.charCodeAt(0);return y>=97&&y<=122||y>=65&&y<=90||y===95}function D(m){if(m===Rt)return!1;const y=m.charCodeAt(0);return y>=48&&y<=57}function c(m,y){const{currentType:L}=y;if(L!==2)return!1;h(m);const O=E(m.currentPeek());return m.resetPeek(),O}function p(m,y){const{currentType:L}=y;if(L!==2)return!1;h(m);const O=m.currentPeek()==="-"?m.peek():m.currentPeek(),gA=D(O);return m.resetPeek(),gA}function d(m,y){const{currentType:L}=y;if(L!==2)return!1;h(m);const O=m.currentPeek()===Es;return m.resetPeek(),O}function v(m,y){const{currentType:L}=y;if(L!==8)return!1;h(m);const O=m.currentPeek()===".";return m.resetPeek(),O}function b(m,y){const{currentType:L}=y;if(L!==9)return!1;h(m);const O=E(m.currentPeek());return m.resetPeek(),O}function N(m,y){const{currentType:L}=y;if(!(L===8||L===12))return!1;h(m);const O=m.currentPeek()===":";return m.resetPeek(),O}function H(m,y){const{currentType:L}=y;if(L!==10)return!1;const O=()=>{const X=m.currentPeek();return X==="{"?E(m.peek()):X==="@"||X==="%"||X==="|"||X===":"||X==="."||X===Bt||!X?!1:X===de?(m.peek(),O()):R(m,!1)},gA=O();return m.resetPeek(),gA}function M(m){h(m);const y=m.currentPeek()==="|";return m.resetPeek(),y}function U(m){const y=h(m),L=m.currentPeek()==="%"&&m.peek()==="{";return m.resetPeek(),{isModulo:L,hasSpace:y.length>0}}function R(m,y=!0){const L=(gA=!1,X="",G=!1)=>{const P=m.currentPeek();return P==="{"?X==="%"?!1:gA:P==="@"||!P?X==="%"?!0:gA:P==="%"?(m.peek(),L(gA,"%",!0)):P==="|"?X==="%"||G?!0:!(X===Bt||X===de):P===Bt?(m.peek(),L(!0,Bt,G)):P===de?(m.peek(),L(!0,de,G)):!0},O=L();return y&&m.resetPeek(),O}function j(m,y){const L=m.currentChar();return L===Rt?Rt:y(L)?(m.next(),L):null}function rA(m){const y=m.charCodeAt(0);return y>=97&&y<=122||y>=65&&y<=90||y>=48&&y<=57||y===95||y===36}function IA(m){return j(m,rA)}function AA(m){const y=m.charCodeAt(0);return y>=97&&y<=122||y>=65&&y<=90||y>=48&&y<=57||y===95||y===36||y===45}function z(m){return j(m,AA)}function oA(m){const y=m.charCodeAt(0);return y>=48&&y<=57}function dA(m){return j(m,oA)}function Y(m){const y=m.charCodeAt(0);return y>=48&&y<=57||y>=65&&y<=70||y>=97&&y<=102}function W(m){return j(m,Y)}function $(m){let y="",L="";for(;y=dA(m);)L+=y;return L}function DA(m){u(m);const y=m.currentChar();return y!=="%"&&l(pA.EXPECTED_TOKEN,n(),0,y),m.next(),"%"}function NA(m){let y="";for(;;){const L=m.currentChar();if(L==="{"||L==="}"||L==="@"||L==="|"||!L)break;if(L==="%")if(R(m))y+=L,m.next();else break;else if(L===Bt||L===de)if(R(m))y+=L,m.next();else{if(M(m))break;y+=L,m.next()}else y+=L,m.next()}return y}function oe(m){u(m);let y="",L="";for(;y=z(m);)L+=y;return m.currentChar()===Rt&&l(pA.UNTERMINATED_CLOSING_BRACE,n(),0),L}function LA(m){u(m);let y="";return m.currentChar()==="-"?(m.next(),y+=`-${$(m)}`):y+=$(m),m.currentChar()===Rt&&l(pA.UNTERMINATED_CLOSING_BRACE,n(),0),y}function _(m){return m!==Es&&m!==de}function eA(m){u(m),f(m,"'");let y="",L="";for(;y=j(m,_);)y==="\\"?L+=tA(m):L+=y;const O=m.currentChar();return O===de||O===Rt?(l(pA.UNTERMINATED_SINGLE_QUOTE_IN_PLACEHOLDER,n(),0),O===de&&(m.next(),f(m,"'")),L):(f(m,"'"),L)}function tA(m){const y=m.currentChar();switch(y){case"\\":case"'":return m.next(),`\\${y}`;case"u":return EA(m,y,4);case"U":return EA(m,y,6);default:return l(pA.UNKNOWN_ESCAPE_SEQUENCE,n(),0,y),""}}function EA(m,y,L){f(m,y);let O="";for(let gA=0;gA<L;gA++){const X=W(m);if(!X){l(pA.INVALID_UNICODE_ESCAPE_SEQUENCE,n(),0,`\\${y}${O}${m.currentChar()}`);break}O+=X}return`\\${y}${O}`}function SA(m){return m!=="{"&&m!=="}"&&m!==Bt&&m!==de}function w(m){u(m);let y="",L="";for(;y=j(m,SA);)L+=y;return L}function S(m){let y="",L="";for(;y=IA(m);)L+=y;return L}function k(m){const y=L=>{const O=m.currentChar();return O==="{"||O==="%"||O==="@"||O==="|"||O==="("||O===")"||!O||O===Bt?L:(L+=O,m.next(),y(L))};return y("")}function x(m){u(m);const y=f(m,"|");return u(m),y}function q(m,y){let L=null;switch(m.currentChar()){case"{":return y.braceNest>=1&&l(pA.NOT_ALLOW_NEST_PLACEHOLDER,n(),0),m.next(),L=C(y,2,"{"),u(m),y.braceNest++,L;case"}":return y.braceNest>0&&y.currentType===2&&l(pA.EMPTY_PLACEHOLDER,n(),0),m.next(),L=C(y,3,"}"),y.braceNest--,y.braceNest>0&&u(m),y.inLinked&&y.braceNest===0&&(y.inLinked=!1),L;case"@":return y.braceNest>0&&l(pA.UNTERMINATED_CLOSING_BRACE,n(),0),L=V(m,y)||Q(y),y.braceNest=0,L;default:{let gA=!0,X=!0,G=!0;if(M(m))return y.braceNest>0&&l(pA.UNTERMINATED_CLOSING_BRACE,n(),0),L=C(y,1,x(m)),y.braceNest=0,y.inLinked=!1,L;if(y.braceNest>0&&(y.currentType===5||y.currentType===6||y.currentType===7))return l(pA.UNTERMINATED_CLOSING_BRACE,n(),0),y.braceNest=0,nA(m,y);if(gA=c(m,y))return L=C(y,5,oe(m)),u(m),L;if(X=p(m,y))return L=C(y,6,LA(m)),u(m),L;if(G=d(m,y))return L=C(y,7,eA(m)),u(m),L;if(!gA&&!X&&!G)return L=C(y,13,w(m)),l(pA.INVALID_TOKEN_IN_PLACEHOLDER,n(),0,L.value),u(m),L;break}}return L}function V(m,y){const{currentType:L}=y;let O=null;const gA=m.currentChar();switch((L===8||L===9||L===12||L===10)&&(gA===de||gA===Bt)&&l(pA.INVALID_LINKED_FORMAT,n(),0),gA){case"@":return m.next(),O=C(y,8,"@"),y.inLinked=!0,O;case".":return u(m),m.next(),C(y,9,".");case":":return u(m),m.next(),C(y,10,":");default:return M(m)?(O=C(y,1,x(m)),y.braceNest=0,y.inLinked=!1,O):v(m,y)||N(m,y)?(u(m),V(m,y)):b(m,y)?(u(m),C(y,12,S(m))):H(m,y)?(u(m),gA==="{"?q(m,y)||O:C(y,11,k(m))):(L===8&&l(pA.INVALID_LINKED_FORMAT,n(),0),y.braceNest=0,y.inLinked=!1,nA(m,y))}}function nA(m,y){let L={type:14};if(y.braceNest>0)return q(m,y)||Q(y);if(y.inLinked)return V(m,y)||Q(y);switch(m.currentChar()){case"{":return q(m,y)||Q(y);case"}":return l(pA.UNBALANCED_CLOSING_BRACE,n(),0),m.next(),C(y,3,"}");case"@":return V(m,y)||Q(y);default:{if(M(m))return L=C(y,1,x(m)),y.braceNest=0,y.inLinked=!1,L;const{isModulo:gA,hasSpace:X}=U(m);if(gA)return X?C(y,0,NA(m)):C(y,4,DA(m));if(R(m))return C(y,0,NA(m));break}}return L}function iA(){const{currentType:m,offset:y,startLoc:L,endLoc:O}=s;return s.lastType=m,s.lastOffset=y,s.lastStartLoc=L,s.lastEndLoc=O,s.offset=o(),s.startLoc=n(),i.currentChar()===Rt?C(s,14):nA(i,s)}return{nextToken:iA,currentOffset:o,currentPosition:n,context:a}}const Wc="parser",qc=/(?:\\\\|\\'|\\u([0-9a-fA-F]{4})|\\U([0-9a-fA-F]{6}))/g;function jc(A,e,t){switch(A){case"\\\\":return"\\";case"\\'":return"'";default:{const i=parseInt(e||t,16);return i<=55295||i>=57344?String.fromCodePoint(i):"锟�"}}}function Zc(A={}){const e=A.location!==!1,{onError:t,onWarn:i}=A;function o(c,p,d,v,...b){const N=c.currentPosition();if(N.offset+=v,N.column+=v,t){const H=e?rn(d,N):null,M=Gi(p,H,{domain:Wc,args:b});t(M)}}function n(c,p,d,v,...b){const N=c.currentPosition();if(N.offset+=v,N.column+=v,i){const H=e?rn(d,N):null;i(xc(p,H,b))}}function r(c,p,d){const v={type:c};return e&&(v.start=p,v.end=p,v.loc={start:d,end:d}),v}function g(c,p,d,v){e&&(c.end=p,c.loc&&(c.loc.end=d))}function s(c,p){const d=c.context(),v=r(3,d.offset,d.startLoc);return v.value=p,g(v,c.currentOffset(),c.currentPosition()),v}function a(c,p){const d=c.context(),{lastOffset:v,lastStartLoc:b}=d,N=r(5,v,b);return N.index=parseInt(p,10),c.nextToken(),g(N,c.currentOffset(),c.currentPosition()),N}function I(c,p,d){const v=c.context(),{lastOffset:b,lastStartLoc:N}=v,H=r(4,b,N);return H.key=p,d===!0&&(H.modulo=!0),c.nextToken(),g(H,c.currentOffset(),c.currentPosition()),H}function l(c,p){const d=c.context(),{lastOffset:v,lastStartLoc:b}=d,N=r(9,v,b);return N.value=p.replace(qc,jc),c.nextToken(),g(N,c.currentOffset(),c.currentPosition()),N}function C(c){const p=c.nextToken(),d=c.context(),{lastOffset:v,lastStartLoc:b}=d,N=r(8,v,b);return p.type!==12?(o(c,pA.UNEXPECTED_EMPTY_LINKED_MODIFIER,d.lastStartLoc,0),N.value="",g(N,v,b),{nextConsumeToken:p,node:N}):(p.value==null&&o(c,pA.UNEXPECTED_LEXICAL_ANALYSIS,d.lastStartLoc,0,Te(p)),N.value=p.value||"",g(N,c.currentOffset(),c.currentPosition()),{node:N})}function Q(c,p){const d=c.context(),v=r(7,d.offset,d.startLoc);return v.value=p,g(v,c.currentOffset(),c.currentPosition()),v}function f(c){const p=c.context(),d=r(6,p.offset,p.startLoc);let v=c.nextToken();if(v.type===9){const b=C(c);d.modifier=b.node,v=b.nextConsumeToken||c.nextToken()}switch(v.type!==10&&o(c,pA.UNEXPECTED_LEXICAL_ANALYSIS,p.lastStartLoc,0,Te(v)),v=c.nextToken(),v.type===2&&(v=c.nextToken()),v.type){case 11:v.value==null&&o(c,pA.UNEXPECTED_LEXICAL_ANALYSIS,p.lastStartLoc,0,Te(v)),d.key=Q(c,v.value||"");break;case 5:v.value==null&&o(c,pA.UNEXPECTED_LEXICAL_ANALYSIS,p.lastStartLoc,0,Te(v)),d.key=I(c,v.value||"");break;case 6:v.value==null&&o(c,pA.UNEXPECTED_LEXICAL_ANALYSIS,p.lastStartLoc,0,Te(v)),d.key=a(c,v.value||"");break;case 7:v.value==null&&o(c,pA.UNEXPECTED_LEXICAL_ANALYSIS,p.lastStartLoc,0,Te(v)),d.key=l(c,v.value||"");break;default:{o(c,pA.UNEXPECTED_EMPTY_LINKED_KEY,p.lastStartLoc,0);const b=c.context(),N=r(7,b.offset,b.startLoc);return N.value="",g(N,b.offset,b.startLoc),d.key=N,g(d,b.offset,b.startLoc),{nextConsumeToken:v,node:d}}}return g(d,c.currentOffset(),c.currentPosition()),{node:d}}function h(c){const p=c.context(),d=p.currentType===1?c.currentOffset():p.offset,v=p.currentType===1?p.endLoc:p.startLoc,b=r(2,d,v);b.items=[];let N=null,H=null;do{const R=N||c.nextToken();switch(N=null,R.type){case 0:R.value==null&&o(c,pA.UNEXPECTED_LEXICAL_ANALYSIS,p.lastStartLoc,0,Te(R)),b.items.push(s(c,R.value||""));break;case 6:R.value==null&&o(c,pA.UNEXPECTED_LEXICAL_ANALYSIS,p.lastStartLoc,0,Te(R)),b.items.push(a(c,R.value||""));break;case 4:H=!0;break;case 5:R.value==null&&o(c,pA.UNEXPECTED_LEXICAL_ANALYSIS,p.lastStartLoc,0,Te(R)),b.items.push(I(c,R.value||"",!!H)),H&&(n(c,ag.USE_MODULO_SYNTAX,p.lastStartLoc,0,Te(R)),H=null);break;case 7:R.value==null&&o(c,pA.UNEXPECTED_LEXICAL_ANALYSIS,p.lastStartLoc,0,Te(R)),b.items.push(l(c,R.value||""));break;case 8:{const j=f(c);b.items.push(j.node),N=j.nextConsumeToken||null;break}}}while(p.currentType!==14&&p.currentType!==1);const M=p.currentType===1?p.lastOffset:c.currentOffset(),U=p.currentType===1?p.lastEndLoc:c.currentPosition();return g(b,M,U),b}function u(c,p,d,v){const b=c.context();let N=v.items.length===0;const H=r(1,p,d);H.cases=[],H.cases.push(v);do{const M=h(c);N||(N=M.items.length===0),H.cases.push(M)}while(b.currentType!==14);return N&&o(c,pA.MUST_HAVE_MESSAGES_IN_PLURAL,d,0),g(H,c.currentOffset(),c.currentPosition()),H}function E(c){const p=c.context(),{offset:d,startLoc:v}=p,b=h(c);return p.currentType===14?b:u(c,d,v,b)}function D(c){const p=Vc(c,lC({},A)),d=p.context(),v=r(0,d.offset,d.startLoc);return e&&v.loc&&(v.loc.source=c),v.body=E(p),A.onCacheKey&&(v.cacheKey=A.onCacheKey(c)),d.currentType!==14&&o(p,pA.UNEXPECTED_LEXICAL_ANALYSIS,d.lastStartLoc,0,c[d.offset]||""),g(v,p.currentOffset(),p.currentPosition()),v}return{parse:D}}function Te(A){if(A.type===14)return"EOF";const e=(A.value||"").replace(/\r?\n/gu,"\\n");return e.length>10?e.slice(0,9)+"鈥�":e}function Xc(A,e={}){const t={ast:A,helpers:new Set};return{context:()=>t,helper:n=>(t.helpers.add(n),n)}}function us(A,e){for(let t=0;t<A.length;t++)Ig(A[t],e)}function Ig(A,e){switch(A.type){case 1:us(A.cases,e),e.helper("plural");break;case 2:us(A.items,e);break;case 6:{Ig(A.key,e),e.helper("linked"),e.helper("type");break}case 5:e.helper("interpolate"),e.helper("list");break;case 4:e.helper("interpolate"),e.helper("named");break}}function zc(A,e={}){const t=Xc(A);t.helper("normalize"),A.body&&Ig(A.body,t);const i=t.context();A.helpers=Array.from(i.helpers)}function $c(A){const e=A.body;return e.type===2?fs(e):e.cases.forEach(t=>fs(t)),A}function fs(A){if(A.items.length===1){const e=A.items[0];(e.type===3||e.type===9)&&(A.static=e.value,delete e.value)}else{const e=[];for(let t=0;t<A.items.length;t++){const i=A.items[t];if(!(i.type===3||i.type===9)||i.value==null)break;e.push(i.value)}if(e.length===A.items.length){A.static=BC(e);for(let t=0;t<A.items.length;t++){const i=A.items[t];(i.type===3||i.type===9)&&delete i.value}}}}const AE="minifier";function ci(A){switch(A.t=A.type,A.type){case 0:{const e=A;ci(e.body),e.b=e.body,delete e.body;break}case 1:{const e=A,t=e.cases;for(let i=0;i<t.length;i++)ci(t[i]);e.c=t,delete e.cases;break}case 2:{const e=A,t=e.items;for(let i=0;i<t.length;i++)ci(t[i]);e.i=t,delete e.items,e.static&&(e.s=e.static,delete e.static);break}case 3:case 9:case 8:case 7:{const e=A;e.value&&(e.v=e.value,delete e.value);break}case 6:{const e=A;ci(e.key),e.k=e.key,delete e.key,e.modifier&&(ci(e.modifier),e.m=e.modifier,delete e.modifier);break}case 5:{const e=A;e.i=e.index,delete e.index;break}case 4:{const e=A;e.k=e.key,delete e.key;break}default:throw Gi(pA.UNHANDLED_MINIFIER_NODE_TYPE,null,{domain:AE,args:[A.type]})}delete A.type}const eE="parser";function tE(A,e){const{filename:t,breakLineCode:i,needIndent:o}=e,n=e.location!==!1,r={filename:t,code:"",column:1,line:1,offset:0,map:void 0,breakLineCode:i,needIndent:o,indentLevel:0};n&&A.loc&&(r.source=A.loc.source);const g=()=>r;function s(h,u){r.code+=h}function a(h,u=!0){const E=u?i:"";s(o?E+" ".repeat(h):E)}function I(h=!0){const u=++r.indentLevel;h&&a(u)}function l(h=!0){const u=--r.indentLevel;h&&a(u)}function C(){a(r.indentLevel)}return{context:g,push:s,indent:I,deindent:l,newline:C,helper:h=>`_${h}`,needIndent:()=>r.needIndent}}function iE(A,e){const{helper:t}=A;A.push(`${t("linked")}(`),Fi(A,e.key),e.modifier?(A.push(", "),Fi(A,e.modifier),A.push(", _type")):A.push(", undefined, _type"),A.push(")")}function oE(A,e){const{helper:t,needIndent:i}=A;A.push(`${t("normalize")}([`),A.indent(i());const o=e.items.length;for(let n=0;n<o&&(Fi(A,e.items[n]),n!==o-1);n++)A.push(", ");A.deindent(i()),A.push("])")}function nE(A,e){const{helper:t,needIndent:i}=A;if(e.cases.length>1){A.push(`${t("plural")}([`),A.indent(i());const o=e.cases.length;for(let n=0;n<o&&(Fi(A,e.cases[n]),n!==o-1);n++)A.push(", ");A.deindent(i()),A.push("])")}}function rE(A,e){e.body?Fi(A,e.body):A.push("null")}function Fi(A,e){const{helper:t}=A;switch(e.type){case 0:rE(A,e);break;case 1:nE(A,e);break;case 2:oE(A,e);break;case 6:iE(A,e);break;case 8:A.push(JSON.stringify(e.value),e);break;case 7:A.push(JSON.stringify(e.value),e);break;case 5:A.push(`${t("interpolate")}(${t("list")}(${e.index}))`,e);break;case 4:A.push(`${t("interpolate")}(${t("named")}(${JSON.stringify(e.key)}))`,e);break;case 9:A.push(JSON.stringify(e.value),e);break;case 3:A.push(JSON.stringify(e.value),e);break;default:throw Gi(pA.UNHANDLED_CODEGEN_NODE_TYPE,null,{domain:eE,args:[e.type]})}}const gE=(A,e={})=>{const t=cs(e.mode)?e.mode:"normal",i=cs(e.filename)?e.filename:"message.intl";e.sourceMap;const o=e.breakLineCode!=null?e.breakLineCode:t==="arrow"?";":`
+`,n=e.needIndent?e.needIndent:t!=="arrow",r=A.helpers||[],g=tE(A,{filename:i,breakLineCode:o,needIndent:n});g.push(t==="normal"?"function __msg__ (ctx) {":"(ctx) => {"),g.indent(n),r.length>0&&(g.push(`const { ${BC(r.map(I=>`${I}: _${I}`),", ")} } = ctx`),g.newline()),g.push("return "),Fi(g,A),g.deindent(n),g.push("}"),delete A.helpers;const{code:s,map:a}=g.context();return{ast:A,code:s,map:a?a.toJSON():void 0}};function sE(A,e={}){const t=lC({},e),i=!!t.jit,o=!!t.minify,n=t.optimize==null?!0:t.optimize,g=Zc(t).parse(A);return i?(n&&$c(g),o&&ci(g),{ast:g,code:""}):(zc(g,t),gE(g,t))}/*!
+ * core-base v9.14.5
+ * (c) 2025 kazuya kawaguchi
+ * Released under the MIT License.
+ */function aE(){typeof __INTLIFY_PROD_DEVTOOLS__!="boolean"&&(ht().__INTLIFY_PROD_DEVTOOLS__=!1),typeof __INTLIFY_JIT_COMPILATION__!="boolean"&&(ht().__INTLIFY_JIT_COMPILATION__=!1),typeof __INTLIFY_DROP_MESSAGE_COMPILER__!="boolean"&&(ht().__INTLIFY_DROP_MESSAGE_COMPILER__=!1)}function st(A){return UA(A)&&Cg(A)===0&&(He(A,"b")||He(A,"body"))}const QC=["b","body"];function IE(A){return Wt(A,QC)}const cC=["c","cases"];function CE(A){return Wt(A,cC,[])}const EC=["s","static"];function lE(A){return Wt(A,EC)}const uC=["i","items"];function BE(A){return Wt(A,uC,[])}const fC=["t","type"];function Cg(A){return Wt(A,fC)}const hC=["v","value"];function bo(A,e){const t=Wt(A,hC);if(t!=null)return t;throw oo(e)}const dC=["m","modifier"];function QE(A){return Wt(A,dC)}const pC=["k","key"];function cE(A){const e=Wt(A,pC);if(e)return e;throw oo(6)}function Wt(A,e,t){for(let i=0;i<e.length;i++){const o=e[i];if(He(A,o)&&A[o]!=null)return A[o]}return t}const mC=[...QC,...cC,...EC,...uC,...pC,...dC,...hC,...fC];function oo(A){return new Error(`unhandled node type: ${A}`)}const qt=[];qt[0]={w:[0],i:[3,0],"[":[4],o:[7]};qt[1]={w:[1],".":[2],"[":[4],o:[7]};qt[2]={w:[2],i:[3,0],0:[3,0]};qt[3]={i:[3,0],0:[3,0],w:[1,1],".":[2,1],"[":[4,1],o:[7,1]};qt[4]={"'":[5,0],'"':[6,0],"[":[4,2],"]":[1,3],o:8,l:[4,0]};qt[5]={"'":[4,0],o:8,l:[5,0]};qt[6]={'"':[4,0],o:8,l:[6,0]};const EE=/^\s?(?:true|false|-?[\d.]+|'[^']*'|"[^"]*")\s?$/;function uE(A){return EE.test(A)}function fE(A){const e=A.charCodeAt(0),t=A.charCodeAt(A.length-1);return e===t&&(e===34||e===39)?A.slice(1,-1):A}function hE(A){if(A==null)return"o";switch(A.charCodeAt(0)){case 91:case 93:case 46:case 34:case 39:return A;case 95:case 36:case 45:return"i";case 9:case 10:case 13:case 160:case 65279:case 8232:case 8233:return"w"}return"i"}function dE(A){const e=A.trim();return A.charAt(0)==="0"&&isNaN(parseInt(A))?!1:uE(e)?fE(e):"*"+e}function pE(A){const e=[];let t=-1,i=0,o=0,n,r,g,s,a,I,l;const C=[];C[0]=()=>{r===void 0?r=g:r+=g},C[1]=()=>{r!==void 0&&(e.push(r),r=void 0)},C[2]=()=>{C[0](),o++},C[3]=()=>{if(o>0)o--,i=4,C[0]();else{if(o=0,r===void 0||(r=dE(r),r===!1))return!1;C[1]()}};function Q(){const f=A[t+1];if(i===5&&f==="'"||i===6&&f==='"')return t++,g="\\"+f,C[0](),!0}for(;i!==null;)if(t++,n=A[t],!(n==="\\"&&Q())){if(s=hE(n),l=qt[i],a=l[s]||l.l||8,a===8||(i=a[0],a[1]!==void 0&&(I=C[a[1]],I&&(g=n,I()===!1))))return;if(i===7)return e}}const hs=new Map;function mE(A,e){return UA(A)?A[e]:null}function yE(A,e){if(!UA(A))return null;let t=hs.get(e);if(t||(t=pE(e),t&&hs.set(e,t)),!t)return null;const i=t.length;let o=A,n=0;for(;n<i;){const r=t[n];if(mC.includes(r)&&st(o))return null;const g=o[r];if(g===void 0||PA(o))return null;o=g,n++}return o}const wE=A=>A,DE=A=>"",kE="text",vE=A=>A.length===0?"":Nc(A),FE=bc;function ds(A,e){return A=Math.abs(A),e===2?A?A>1?1:0:1:A?Math.min(A,2):0}function SE(A){const e=Ae(A.pluralIndex)?A.pluralIndex:-1;return A.named&&(Ae(A.named.count)||Ae(A.named.n))?Ae(A.named.count)?A.named.count:Ae(A.named.n)?A.named.n:e:e}function RE(A,e){e.count||(e.count=A),e.n||(e.n=A)}function bE(A={}){const e=A.locale,t=SE(A),i=UA(A.pluralRules)&&aA(e)&&PA(A.pluralRules[e])?A.pluralRules[e]:ds,o=UA(A.pluralRules)&&aA(e)&&PA(A.pluralRules[e])?ds:void 0,n=E=>E[i(t,E.length,o)],r=A.list||[],g=E=>r[E],s=A.named||HA();Ae(A.pluralIndex)&&RE(t,s);const a=E=>s[E];function I(E){const D=PA(A.messages)?A.messages(E):UA(A.messages)?A.messages[E]:!1;return D||(A.parent?A.parent.message(E):DE)}const l=E=>A.modifiers?A.modifiers[E]:wE,C=vA(A.processor)&&PA(A.processor.normalize)?A.processor.normalize:vE,Q=vA(A.processor)&&PA(A.processor.interpolate)?A.processor.interpolate:FE,f=vA(A.processor)&&aA(A.processor.type)?A.processor.type:kE,u={list:g,named:a,plural:n,linked:(E,...D)=>{const[c,p]=D;let d="text",v="";D.length===1?UA(c)?(v=c.modifier||v,d=c.type||d):aA(c)&&(v=c||v):D.length===2&&(aA(c)&&(v=c||v),aA(p)&&(d=p||d));const b=I(E)(u),N=d==="vnode"&&WA(b)&&v?b[0]:b;return v?l(v)(N,d):N},message:I,type:f,interpolate:Q,normalize:C,values:Be(HA(),r,s)};return u}let no=null;function NE(A){no=A}function GE(A,e,t){no&&no.emit("i18n:init",{timestamp:Date.now(),i18n:A,version:e,meta:t})}const LE=UE("function:translate");function UE(A){return e=>no&&no.emit(A,e)}const ME=ag.__EXTEND_POINT__,zt=Fn(ME),xE={FALLBACK_TO_TRANSLATE:zt(),CANNOT_FORMAT_NUMBER:zt(),FALLBACK_TO_NUMBER_FORMAT:zt(),CANNOT_FORMAT_DATE:zt(),FALLBACK_TO_DATE_FORMAT:zt(),EXPERIMENTAL_CUSTOM_MESSAGE_COMPILER:zt(),__EXTEND_POINT__:zt()},yC=pA.__EXTEND_POINT__,$t=Fn(yC),Ye={INVALID_ARGUMENT:yC,INVALID_DATE_ARGUMENT:$t(),INVALID_ISO_DATE_ARGUMENT:$t(),NOT_SUPPORT_NON_STRING_MESSAGE:$t(),NOT_SUPPORT_LOCALE_PROMISE_VALUE:$t(),NOT_SUPPORT_LOCALE_ASYNC_FUNCTION:$t(),NOT_SUPPORT_LOCALE_TYPE:$t(),__EXTEND_POINT__:$t()};function it(A){return Gi(A,null,void 0)}function lg(A,e){return e.locale!=null?ps(e.locale):ps(A.locale)}let Vn;function ps(A){if(aA(A))return A;if(PA(A)){if(A.resolvedOnce&&Vn!=null)return Vn;if(A.constructor.name==="Function"){const e=A();if(Rc(e))throw it(Ye.NOT_SUPPORT_LOCALE_PROMISE_VALUE);return Vn=e}else throw it(Ye.NOT_SUPPORT_LOCALE_ASYNC_FUNCTION)}else throw it(Ye.NOT_SUPPORT_LOCALE_TYPE)}function JE(A,e,t){return[...new Set([t,...WA(e)?e:UA(e)?Object.keys(e):aA(e)?[e]:[t]])]}function wC(A,e,t){const i=aA(t)?t:Si,o=A;o.__localeChainCache||(o.__localeChainCache=new Map);let n=o.__localeChainCache.get(i);if(!n){n=[];let r=[t];for(;WA(r);)r=ms(n,r,e);const g=WA(e)||!vA(e)?e:e.default?e.default:null;r=aA(g)?[g]:g,WA(r)&&ms(n,r,!1),o.__localeChainCache.set(i,n)}return n}function ms(A,e,t){let i=!0;for(let o=0;o<e.length&&RA(i);o++){const n=e[o];aA(n)&&(i=TE(A,e[o],t))}return i}function TE(A,e,t){let i;const o=e.split("-");do{const n=o.join("-");i=HE(A,n,t),o.splice(-1,1)}while(o.length&&i===!0);return i}function HE(A,e,t){let i=!1;if(!A.includes(e)&&(i=!0,e)){i=e[e.length-1]!=="!";const o=e.replace(/!/g,"");A.push(o),(WA(t)||vA(t))&&t[o]&&(i=t[o])}return i}const YE="9.14.5",Sn=-1,Si="en-US",ys="",ws=A=>`${A.charAt(0).toLocaleUpperCase()}${A.substr(1)}`;function _E(){return{upper:(A,e)=>e==="text"&&aA(A)?A.toUpperCase():e==="vnode"&&UA(A)&&"__v_isVNode"in A?A.children.toUpperCase():A,lower:(A,e)=>e==="text"&&aA(A)?A.toLowerCase():e==="vnode"&&UA(A)&&"__v_isVNode"in A?A.children.toLowerCase():A,capitalize:(A,e)=>e==="text"&&aA(A)?ws(A):e==="vnode"&&UA(A)&&"__v_isVNode"in A?ws(A.children):A}}let DC;function Ds(A){DC=A}let kC;function KE(A){kC=A}let vC;function OE(A){vC=A}let FC=null;const PE=A=>{FC=A},VE=()=>FC;let SC=null;const ks=A=>{SC=A},WE=()=>SC;let vs=0;function qE(A={}){const e=PA(A.onWarn)?A.onWarn:yc,t=aA(A.version)?A.version:YE,i=aA(A.locale)||PA(A.locale)?A.locale:Si,o=PA(i)?Si:i,n=WA(A.fallbackLocale)||vA(A.fallbackLocale)||aA(A.fallbackLocale)||A.fallbackLocale===!1?A.fallbackLocale:o,r=vA(A.messages)?A.messages:Wn(o),g=vA(A.datetimeFormats)?A.datetimeFormats:Wn(o),s=vA(A.numberFormats)?A.numberFormats:Wn(o),a=Be(HA(),A.modifiers,_E()),I=A.pluralRules||HA(),l=PA(A.missing)?A.missing:null,C=RA(A.missingWarn)||Ot(A.missingWarn)?A.missingWarn:!0,Q=RA(A.fallbackWarn)||Ot(A.fallbackWarn)?A.fallbackWarn:!0,f=!!A.fallbackFormat,h=!!A.unresolving,u=PA(A.postTranslation)?A.postTranslation:null,E=vA(A.processor)?A.processor:null,D=RA(A.warnHtmlMessage)?A.warnHtmlMessage:!0,c=!!A.escapeParameter,p=PA(A.messageCompiler)?A.messageCompiler:DC,d=PA(A.messageResolver)?A.messageResolver:kC||mE,v=PA(A.localeFallbacker)?A.localeFallbacker:vC||JE,b=UA(A.fallbackContext)?A.fallbackContext:void 0,N=A,H=UA(N.__datetimeFormatters)?N.__datetimeFormatters:new Map,M=UA(N.__numberFormatters)?N.__numberFormatters:new Map,U=UA(N.__meta)?N.__meta:{};vs++;const R={version:t,cid:vs,locale:i,fallbackLocale:n,messages:r,modifiers:a,pluralRules:I,missing:l,missingWarn:C,fallbackWarn:Q,fallbackFormat:f,unresolving:h,postTranslation:u,processor:E,warnHtmlMessage:D,escapeParameter:c,messageCompiler:p,messageResolver:d,localeFallbacker:v,fallbackContext:b,onWarn:e,__meta:U};return R.datetimeFormats=g,R.numberFormats=s,R.__datetimeFormatters=H,R.__numberFormatters=M,__INTLIFY_PROD_DEVTOOLS__&&GE(R,t,U),R}const Wn=A=>({[A]:HA()});function Bg(A,e,t,i,o){const{missing:n,onWarn:r}=A;if(n!==null){const g=n(A,t,e,o);return aA(g)?g:e}else return e}function Ji(A,e,t){const i=A;i.__localeChainCache=new Map,A.localeFallbacker(A,t,e)}function jE(A,e){return A===e?!1:A.split("-")[0]===e.split("-")[0]}function ZE(A,e){const t=e.indexOf(A);if(t===-1)return!1;for(let i=t+1;i<e.length;i++)if(jE(A,e[i]))return!0;return!1}function qn(A){return t=>XE(t,A)}function XE(A,e){const t=IE(e);if(t==null)throw oo(0);if(Cg(t)===1){const n=CE(t);return A.plural(n.reduce((r,g)=>[...r,Fs(A,g)],[]))}else return Fs(A,t)}function Fs(A,e){const t=lE(e);if(t!=null)return A.type==="text"?t:A.normalize([t]);{const i=BE(e).reduce((o,n)=>[...o,mr(A,n)],[]);return A.normalize(i)}}function mr(A,e){const t=Cg(e);switch(t){case 3:return bo(e,t);case 9:return bo(e,t);case 4:{const i=e;if(He(i,"k")&&i.k)return A.interpolate(A.named(i.k));if(He(i,"key")&&i.key)return A.interpolate(A.named(i.key));throw oo(t)}case 5:{const i=e;if(He(i,"i")&&Ae(i.i))return A.interpolate(A.list(i.i));if(He(i,"index")&&Ae(i.index))return A.interpolate(A.list(i.index));throw oo(t)}case 6:{const i=e,o=QE(i),n=cE(i);return A.linked(mr(A,n),o?mr(A,o):void 0,A.type)}case 7:return bo(e,t);case 8:return bo(e,t);default:throw new Error(`unhandled node on format message part: ${t}`)}}const RC=A=>A;let ui=HA();function bC(A,e={}){let t=!1;const i=e.onError||Tc;return e.onError=o=>{t=!0,i(o)},{...sE(A,e),detectError:t}}const zE=(A,e)=>{if(!aA(A))throw it(Ye.NOT_SUPPORT_NON_STRING_MESSAGE);{RA(e.warnHtmlMessage)&&e.warnHtmlMessage;const i=(e.onCacheKey||RC)(A),o=ui[i];if(o)return o;const{code:n,detectError:r}=bC(A,e),g=new Function(`return ${n}`)();return r?g:ui[i]=g}};function $E(A,e){if(__INTLIFY_JIT_COMPILATION__&&!__INTLIFY_DROP_MESSAGE_COMPILER__&&aA(A)){RA(e.warnHtmlMessage)&&e.warnHtmlMessage;const i=(e.onCacheKey||RC)(A),o=ui[i];if(o)return o;const{ast:n,detectError:r}=bC(A,{...e,location:!1,jit:!0}),g=qn(n);return r?g:ui[i]=g}else{const t=A.cacheKey;if(t){const i=ui[t];return i||(ui[t]=qn(A))}else return qn(A)}}const Ss=()=>"",Me=A=>PA(A);function Rs(A,...e){const{fallbackFormat:t,postTranslation:i,unresolving:o,messageCompiler:n,fallbackLocale:r,messages:g}=A,[s,a]=yr(...e),I=RA(a.missingWarn)?a.missingWarn:A.missingWarn,l=RA(a.fallbackWarn)?a.fallbackWarn:A.fallbackWarn,C=RA(a.escapeParameter)?a.escapeParameter:A.escapeParameter,Q=!!a.resolvedMessage,f=aA(a.default)||RA(a.default)?RA(a.default)?n?s:()=>s:a.default:t?n?s:()=>s:"",h=t||f!=="",u=lg(A,a);C&&Au(a);let[E,D,c]=Q?[s,u,g[u]||HA()]:NC(A,s,u,r,l,I),p=E,d=s;if(!Q&&!(aA(p)||st(p)||Me(p))&&h&&(p=f,d=p),!Q&&(!(aA(p)||st(p)||Me(p))||!aA(D)))return o?Sn:s;let v=!1;const b=()=>{v=!0},N=Me(p)?p:GC(A,s,D,p,d,b);if(v)return p;const H=iu(A,D,c,a),M=bE(H),U=eu(A,N,M);let R=i?i(U,s):U;if(C&&aA(R)&&(R=Fc(R)),__INTLIFY_PROD_DEVTOOLS__){const j={timestamp:Date.now(),key:aA(s)?s:Me(p)?p.key:"",locale:D||(Me(p)?p.locale:""),format:aA(p)?p:Me(p)?p.source:"",message:R};j.meta=Be({},A.__meta,VE()||{}),LE(j)}return R}function Au(A){WA(A.list)?A.list=A.list.map(e=>aA(e)?Bs(e):e):UA(A.named)&&Object.keys(A.named).forEach(e=>{aA(A.named[e])&&(A.named[e]=Bs(A.named[e]))})}function NC(A,e,t,i,o,n){const{messages:r,onWarn:g,messageResolver:s,localeFallbacker:a}=A,I=a(A,i,t);let l=HA(),C,Q=null;const f="translate";for(let h=0;h<I.length&&(C=I[h],l=r[C]||HA(),(Q=s(l,e))===null&&(Q=l[e]),!(aA(Q)||st(Q)||Me(Q)));h++)if(!ZE(C,I)){const u=Bg(A,e,C,n,f);u!==e&&(Q=u)}return[Q,C,l]}function GC(A,e,t,i,o,n){const{messageCompiler:r,warnHtmlMessage:g}=A;if(Me(i)){const a=i;return a.locale=a.locale||t,a.key=a.key||e,a}if(r==null){const a=()=>i;return a.locale=t,a.key=e,a}const s=r(i,tu(A,t,o,i,g,n));return s.locale=t,s.key=e,s.source=i,s}function eu(A,e,t){return e(t)}function yr(...A){const[e,t,i]=A,o=HA();if(!aA(e)&&!Ae(e)&&!Me(e)&&!st(e))throw it(Ye.INVALID_ARGUMENT);const n=Ae(e)?String(e):(Me(e),e);return Ae(t)?o.plural=t:aA(t)?o.default=t:vA(t)&&!vn(t)?o.named=t:WA(t)&&(o.list=t),Ae(i)?o.plural=i:aA(i)?o.default=i:vA(i)&&Be(o,i),[n,o]}function tu(A,e,t,i,o,n){return{locale:e,key:t,warnHtmlMessage:o,onError:r=>{throw n&&n(r),r},onCacheKey:r=>wc(e,t,r)}}function iu(A,e,t,i){const{modifiers:o,pluralRules:n,messageResolver:r,fallbackLocale:g,fallbackWarn:s,missingWarn:a,fallbackContext:I}=A,C={locale:e,modifiers:o,pluralRules:n,messages:Q=>{let f=r(t,Q);if(f==null&&I){const[,,h]=NC(I,Q,e,g,s,a);f=r(h,Q)}if(aA(f)||st(f)){let h=!1;const E=GC(A,Q,e,f,Q,()=>{h=!0});return h?Ss:E}else return Me(f)?f:Ss}};return A.processor&&(C.processor=A.processor),i.list&&(C.list=i.list),i.named&&(C.named=i.named),Ae(i.plural)&&(C.pluralIndex=i.plural),C}function bs(A,...e){const{datetimeFormats:t,unresolving:i,fallbackLocale:o,onWarn:n,localeFallbacker:r}=A,{__datetimeFormatters:g}=A,[s,a,I,l]=wr(...e),C=RA(I.missingWarn)?I.missingWarn:A.missingWarn;RA(I.fallbackWarn)?I.fallbackWarn:A.fallbackWarn;const Q=!!I.part,f=lg(A,I),h=r(A,o,f);if(!aA(s)||s==="")return new Intl.DateTimeFormat(f,l).format(a);let u={},E,D=null;const c="datetime format";for(let v=0;v<h.length&&(E=h[v],u=t[E]||{},D=u[s],!vA(D));v++)Bg(A,s,E,C,c);if(!vA(D)||!aA(E))return i?Sn:s;let p=`${E}__${s}`;vn(l)||(p=`${p}__${JSON.stringify(l)}`);let d=g.get(p);return d||(d=new Intl.DateTimeFormat(E,Be({},D,l)),g.set(p,d)),Q?d.formatToParts(a):d.format(a)}const LC=["localeMatcher","weekday","era","year","month","day","hour","minute","second","timeZoneName","formatMatcher","hour12","timeZone","dateStyle","timeStyle","calendar","dayPeriod","numberingSystem","hourCycle","fractionalSecondDigits"];function wr(...A){const[e,t,i,o]=A,n=HA();let r=HA(),g;if(aA(e)){const s=e.match(/(\d{4}-\d{2}-\d{2})(T|\s)?(.*)/);if(!s)throw it(Ye.INVALID_ISO_DATE_ARGUMENT);const a=s[3]?s[3].trim().startsWith("T")?`${s[1].trim()}${s[3].trim()}`:`${s[1].trim()}T${s[3].trim()}`:s[1].trim();g=new Date(a);try{g.toISOString()}catch{throw it(Ye.INVALID_ISO_DATE_ARGUMENT)}}else if(kc(e)){if(isNaN(e.getTime()))throw it(Ye.INVALID_DATE_ARGUMENT);g=e}else if(Ae(e))g=e;else throw it(Ye.INVALID_ARGUMENT);return aA(t)?n.key=t:vA(t)&&Object.keys(t).forEach(s=>{LC.includes(s)?r[s]=t[s]:n[s]=t[s]}),aA(i)?n.locale=i:vA(i)&&(r=i),vA(o)&&(r=o),[n.key||"",g,n,r]}function Ns(A,e,t){const i=A;for(const o in t){const n=`${e}__${o}`;i.__datetimeFormatters.has(n)&&i.__datetimeFormatters.delete(n)}}function Gs(A,...e){const{numberFormats:t,unresolving:i,fallbackLocale:o,onWarn:n,localeFallbacker:r}=A,{__numberFormatters:g}=A,[s,a,I,l]=Dr(...e),C=RA(I.missingWarn)?I.missingWarn:A.missingWarn;RA(I.fallbackWarn)?I.fallbackWarn:A.fallbackWarn;const Q=!!I.part,f=lg(A,I),h=r(A,o,f);if(!aA(s)||s==="")return new Intl.NumberFormat(f,l).format(a);let u={},E,D=null;const c="number format";for(let v=0;v<h.length&&(E=h[v],u=t[E]||{},D=u[s],!vA(D));v++)Bg(A,s,E,C,c);if(!vA(D)||!aA(E))return i?Sn:s;let p=`${E}__${s}`;vn(l)||(p=`${p}__${JSON.stringify(l)}`);let d=g.get(p);return d||(d=new Intl.NumberFormat(E,Be({},D,l)),g.set(p,d)),Q?d.formatToParts(a):d.format(a)}const UC=["localeMatcher","style","currency","currencyDisplay","currencySign","useGrouping","minimumIntegerDigits","minimumFractionDigits","maximumFractionDigits","minimumSignificantDigits","maximumSignificantDigits","compactDisplay","notation","signDisplay","unit","unitDisplay","roundingMode","roundingPriority","roundingIncrement","trailingZeroDisplay"];function Dr(...A){const[e,t,i,o]=A,n=HA();let r=HA();if(!Ae(e))throw it(Ye.INVALID_ARGUMENT);const g=e;return aA(t)?n.key=t:vA(t)&&Object.keys(t).forEach(s=>{UC.includes(s)?r[s]=t[s]:n[s]=t[s]}),aA(i)?n.locale=i:vA(i)&&(r=i),vA(o)&&(r=o),[n.key||"",g,n,r]}function Ls(A,e,t){const i=A;for(const o in t){const n=`${e}__${o}`;i.__numberFormatters.has(n)&&i.__numberFormatters.delete(n)}}aE();/*!
+ * vue-i18n v9.14.5
+ * (c) 2025 kazuya kawaguchi
+ * Released under the MIT License.
+ */const ou="9.14.5";function nu(){typeof __VUE_I18N_FULL_INSTALL__!="boolean"&&(ht().__VUE_I18N_FULL_INSTALL__=!0),typeof __VUE_I18N_LEGACY_API__!="boolean"&&(ht().__VUE_I18N_LEGACY_API__=!0),typeof __INTLIFY_JIT_COMPILATION__!="boolean"&&(ht().__INTLIFY_JIT_COMPILATION__=!1),typeof __INTLIFY_DROP_MESSAGE_COMPILER__!="boolean"&&(ht().__INTLIFY_DROP_MESSAGE_COMPILER__=!1),typeof __INTLIFY_PROD_DEVTOOLS__!="boolean"&&(ht().__INTLIFY_PROD_DEVTOOLS__=!1)}const ru=xE.__EXTEND_POINT__,Qt=Fn(ru);Qt(),Qt(),Qt(),Qt(),Qt(),Qt(),Qt(),Qt(),Qt();const MC=Ye.__EXTEND_POINT__,De=Fn(MC),re={UNEXPECTED_RETURN_TYPE:MC,INVALID_ARGUMENT:De(),MUST_BE_CALL_SETUP_TOP:De(),NOT_INSTALLED:De(),NOT_AVAILABLE_IN_LEGACY_MODE:De(),REQUIRED_VALUE:De(),INVALID_VALUE:De(),CANNOT_SETUP_VUE_DEVTOOLS_PLUGIN:De(),NOT_INSTALLED_WITH_PROVIDE:De(),UNEXPECTED_ERROR:De(),NOT_COMPATIBLE_LEGACY_VUE_I18N:De(),BRIDGE_SUPPORT_VUE_2_ONLY:De(),MUST_DEFINE_I18N_OPTION_IN_ALLOW_COMPOSITION:De(),NOT_AVAILABLE_COMPOSITION_IN_LEGACY:De(),__EXTEND_POINT__:De()};function Ie(A,...e){return Gi(A,null,void 0)}const kr=Vt("__translateVNode"),vr=Vt("__datetimeParts"),Fr=Vt("__numberParts"),xC=Vt("__setPluralRules"),JC=Vt("__injectWithOption"),Sr=Vt("__dispose");function ro(A){if(!UA(A)||st(A))return A;for(const e in A)if(He(A,e))if(!e.includes("."))UA(A[e])&&ro(A[e]);else{const t=e.split("."),i=t.length-1;let o=A,n=!1;for(let r=0;r<i;r++){if(t[r]==="__proto__")throw new Error(`unsafe key: ${t[r]}`);if(t[r]in o||(o[t[r]]=HA()),!UA(o[t[r]])){n=!0;break}o=o[t[r]]}if(n||(st(o)?mC.includes(t[i])||delete A[e]:(o[t[i]]=A[e],delete A[e])),!st(o)){const r=o[t[i]];UA(r)&&ro(r)}}return A}function Rn(A,e){const{messages:t,__i18n:i,messageResolver:o,flatJson:n}=e,r=vA(t)?t:WA(i)?HA():{[A]:HA()};if(WA(i)&&i.forEach(g=>{if("locale"in g&&"resource"in g){const{locale:s,resource:a}=g;s?(r[s]=r[s]||HA(),Oo(a,r[s])):Oo(a,r)}else aA(g)&&Oo(JSON.parse(g),r)}),o==null&&n)for(const g in r)He(r,g)&&ro(r[g]);return r}function TC(A){return A.type}function HC(A,e,t){let i=UA(e.messages)?e.messages:HA();"__i18nGlobal"in t&&(i=Rn(A.locale.value,{messages:i,__i18n:t.__i18nGlobal}));const o=Object.keys(i);o.length&&o.forEach(n=>{A.mergeLocaleMessage(n,i[n])});{if(UA(e.datetimeFormats)){const n=Object.keys(e.datetimeFormats);n.length&&n.forEach(r=>{A.mergeDateTimeFormat(r,e.datetimeFormats[r])})}if(UA(e.numberFormats)){const n=Object.keys(e.numberFormats);n.length&&n.forEach(r=>{A.mergeNumberFormat(r,e.numberFormats[r])})}}}function Us(A){return TA(ho,null,A,0)}const Ms="__INTLIFY_META__",xs=()=>[],gu=()=>!1;let Js=0;function Ts(A){return(e,t,i,o)=>A(t,i,vi()||void 0,o)}const su=()=>{const A=vi();let e=null;return A&&(e=TC(A)[Ms])?{[Ms]:e}:null};function Qg(A={},e){const{__root:t,__injectWithOption:i}=A,o=t===void 0,n=A.flatJson,r=nn?uA:ig,g=!!A.translateExistCompatible;let s=RA(A.inheritLocale)?A.inheritLocale:!0;const a=r(t&&s?t.locale.value:aA(A.locale)?A.locale:Si),I=r(t&&s?t.fallbackLocale.value:aA(A.fallbackLocale)||WA(A.fallbackLocale)||vA(A.fallbackLocale)||A.fallbackLocale===!1?A.fallbackLocale:a.value),l=r(Rn(a.value,A)),C=r(vA(A.datetimeFormats)?A.datetimeFormats:{[a.value]:{}}),Q=r(vA(A.numberFormats)?A.numberFormats:{[a.value]:{}});let f=t?t.missingWarn:RA(A.missingWarn)||Ot(A.missingWarn)?A.missingWarn:!0,h=t?t.fallbackWarn:RA(A.fallbackWarn)||Ot(A.fallbackWarn)?A.fallbackWarn:!0,u=t?t.fallbackRoot:RA(A.fallbackRoot)?A.fallbackRoot:!0,E=!!A.fallbackFormat,D=PA(A.missing)?A.missing:null,c=PA(A.missing)?Ts(A.missing):null,p=PA(A.postTranslation)?A.postTranslation:null,d=t?t.warnHtmlMessage:RA(A.warnHtmlMessage)?A.warnHtmlMessage:!0,v=!!A.escapeParameter;const b=t?t.modifiers:vA(A.modifiers)?A.modifiers:{};let N=A.pluralRules||t&&t.pluralRules,H;H=(()=>{o&&ks(null);const G={version:ou,locale:a.value,fallbackLocale:I.value,messages:l.value,modifiers:b,pluralRules:N,missing:c===null?void 0:c,missingWarn:f,fallbackWarn:h,fallbackFormat:E,unresolving:!0,postTranslation:p===null?void 0:p,warnHtmlMessage:d,escapeParameter:v,messageResolver:A.messageResolver,messageCompiler:A.messageCompiler,__meta:{framework:"vue"}};G.datetimeFormats=C.value,G.numberFormats=Q.value,G.__datetimeFormatters=vA(H)?H.__datetimeFormatters:void 0,G.__numberFormatters=vA(H)?H.__numberFormatters:void 0;const P=qE(G);return o&&ks(P),P})(),Ji(H,a.value,I.value);function U(){return[a.value,I.value,l.value,C.value,Q.value]}const R=hA({get:()=>a.value,set:G=>{a.value=G,H.locale=a.value}}),j=hA({get:()=>I.value,set:G=>{I.value=G,H.fallbackLocale=I.value,Ji(H,a.value,G)}}),rA=hA(()=>l.value),IA=hA(()=>C.value),AA=hA(()=>Q.value);function z(){return PA(p)?p:null}function oA(G){p=G,H.postTranslation=G}function dA(){return D}function Y(G){G!==null&&(c=Ts(G)),D=G,H.missing=c}const W=(G,P,fA,bA,jA,fe)=>{U();let Ce;try{__INTLIFY_PROD_DEVTOOLS__,o||(H.fallbackContext=t?WE():void 0),Ce=G(H)}finally{__INTLIFY_PROD_DEVTOOLS__,o||(H.fallbackContext=void 0)}if(fA!=="translate exists"&&Ae(Ce)&&Ce===Sn||fA==="translate exists"&&!Ce){const[jt,Mn]=P();return t&&u?bA(t):jA(jt)}else{if(fe(Ce))return Ce;throw Ie(re.UNEXPECTED_RETURN_TYPE)}};function $(...G){return W(P=>Reflect.apply(Rs,null,[P,...G]),()=>yr(...G),"translate",P=>Reflect.apply(P.t,P,[...G]),P=>P,P=>aA(P))}function DA(...G){const[P,fA,bA]=G;if(bA&&!UA(bA))throw Ie(re.INVALID_ARGUMENT);return $(P,fA,Be({resolvedMessage:!0},bA||{}))}function NA(...G){return W(P=>Reflect.apply(bs,null,[P,...G]),()=>wr(...G),"datetime format",P=>Reflect.apply(P.d,P,[...G]),()=>ys,P=>aA(P))}function oe(...G){return W(P=>Reflect.apply(Gs,null,[P,...G]),()=>Dr(...G),"number format",P=>Reflect.apply(P.n,P,[...G]),()=>ys,P=>aA(P))}function LA(G){return G.map(P=>aA(P)||Ae(P)||RA(P)?Us(String(P)):P)}const eA={normalize:LA,interpolate:G=>G,type:"vnode"};function tA(...G){return W(P=>{let fA;const bA=P;try{bA.processor=eA,fA=Reflect.apply(Rs,null,[bA,...G])}finally{bA.processor=null}return fA},()=>yr(...G),"translate",P=>P[kr](...G),P=>[Us(P)],P=>WA(P))}function EA(...G){return W(P=>Reflect.apply(Gs,null,[P,...G]),()=>Dr(...G),"number format",P=>P[Fr](...G),xs,P=>aA(P)||WA(P))}function SA(...G){return W(P=>Reflect.apply(bs,null,[P,...G]),()=>wr(...G),"datetime format",P=>P[vr](...G),xs,P=>aA(P)||WA(P))}function w(G){N=G,H.pluralRules=N}function S(G,P){return W(()=>{if(!G)return!1;const fA=aA(P)?P:a.value,bA=q(fA),jA=H.messageResolver(bA,G);return g?jA!=null:st(jA)||Me(jA)||aA(jA)},()=>[G],"translate exists",fA=>Reflect.apply(fA.te,fA,[G,P]),gu,fA=>RA(fA))}function k(G){let P=null;const fA=wC(H,I.value,a.value);for(let bA=0;bA<fA.length;bA++){const jA=l.value[fA[bA]]||{},fe=H.messageResolver(jA,G);if(fe!=null){P=fe;break}}return P}function x(G){const P=k(G);return P??(t?t.tm(G)||{}:{})}function q(G){return l.value[G]||{}}function V(G,P){if(n){const fA={[G]:P};for(const bA in fA)He(fA,bA)&&ro(fA[bA]);P=fA[G]}l.value[G]=P,H.messages=l.value}function nA(G,P){l.value[G]=l.value[G]||{};const fA={[G]:P};if(n)for(const bA in fA)He(fA,bA)&&ro(fA[bA]);P=fA[G],Oo(P,l.value[G]),H.messages=l.value}function iA(G){return C.value[G]||{}}function m(G,P){C.value[G]=P,H.datetimeFormats=C.value,Ns(H,G,P)}function y(G,P){C.value[G]=Be(C.value[G]||{},P),H.datetimeFormats=C.value,Ns(H,G,P)}function L(G){return Q.value[G]||{}}function O(G,P){Q.value[G]=P,H.numberFormats=Q.value,Ls(H,G,P)}function gA(G,P){Q.value[G]=Be(Q.value[G]||{},P),H.numberFormats=Q.value,Ls(H,G,P)}Js++,t&&nn&&(ae(t.locale,G=>{s&&(a.value=G,H.locale=G,Ji(H,a.value,I.value))}),ae(t.fallbackLocale,G=>{s&&(I.value=G,H.fallbackLocale=G,Ji(H,a.value,I.value))}));const X={id:Js,locale:R,fallbackLocale:j,get inheritLocale(){return s},set inheritLocale(G){s=G,G&&t&&(a.value=t.locale.value,I.value=t.fallbackLocale.value,Ji(H,a.value,I.value))},get availableLocales(){return Object.keys(l.value).sort()},messages:rA,get modifiers(){return b},get pluralRules(){return N||{}},get isGlobal(){return o},get missingWarn(){return f},set missingWarn(G){f=G,H.missingWarn=f},get fallbackWarn(){return h},set fallbackWarn(G){h=G,H.fallbackWarn=h},get fallbackRoot(){return u},set fallbackRoot(G){u=G},get fallbackFormat(){return E},set fallbackFormat(G){E=G,H.fallbackFormat=E},get warnHtmlMessage(){return d},set warnHtmlMessage(G){d=G,H.warnHtmlMessage=G},get escapeParameter(){return v},set escapeParameter(G){v=G,H.escapeParameter=G},t:$,getLocaleMessage:q,setLocaleMessage:V,mergeLocaleMessage:nA,getPostTranslationHandler:z,setPostTranslationHandler:oA,getMissingHandler:dA,setMissingHandler:Y,[xC]:w};return X.datetimeFormats=IA,X.numberFormats=AA,X.rt=DA,X.te=S,X.tm=x,X.d=NA,X.n=oe,X.getDateTimeFormat=iA,X.setDateTimeFormat=m,X.mergeDateTimeFormat=y,X.getNumberFormat=L,X.setNumberFormat=O,X.mergeNumberFormat=gA,X[JC]=i,X[kr]=tA,X[vr]=SA,X[Fr]=EA,X}function au(A){const e=aA(A.locale)?A.locale:Si,t=aA(A.fallbackLocale)||WA(A.fallbackLocale)||vA(A.fallbackLocale)||A.fallbackLocale===!1?A.fallbackLocale:e,i=PA(A.missing)?A.missing:void 0,o=RA(A.silentTranslationWarn)||Ot(A.silentTranslationWarn)?!A.silentTranslationWarn:!0,n=RA(A.silentFallbackWarn)||Ot(A.silentFallbackWarn)?!A.silentFallbackWarn:!0,r=RA(A.fallbackRoot)?A.fallbackRoot:!0,g=!!A.formatFallbackMessages,s=vA(A.modifiers)?A.modifiers:{},a=A.pluralizationRules,I=PA(A.postTranslation)?A.postTranslation:void 0,l=aA(A.warnHtmlInMessage)?A.warnHtmlInMessage!=="off":!0,C=!!A.escapeParameterHtml,Q=RA(A.sync)?A.sync:!0;let f=A.messages;if(vA(A.sharedMessages)){const v=A.sharedMessages;f=Object.keys(v).reduce((N,H)=>{const M=N[H]||(N[H]={});return Be(M,v[H]),N},f||{})}const{__i18n:h,__root:u,__injectWithOption:E}=A,D=A.datetimeFormats,c=A.numberFormats,p=A.flatJson,d=A.translateExistCompatible;return{locale:e,fallbackLocale:t,messages:f,flatJson:p,datetimeFormats:D,numberFormats:c,missing:i,missingWarn:o,fallbackWarn:n,fallbackRoot:r,fallbackFormat:g,modifiers:s,pluralRules:a,postTranslation:I,warnHtmlMessage:l,escapeParameter:C,messageResolver:A.messageResolver,inheritLocale:Q,translateExistCompatible:d,__i18n:h,__root:u,__injectWithOption:E}}function Rr(A={},e){{const t=Qg(au(A)),{__extender:i}=A,o={id:t.id,get locale(){return t.locale.value},set locale(n){t.locale.value=n},get fallbackLocale(){return t.fallbackLocale.value},set fallbackLocale(n){t.fallbackLocale.value=n},get messages(){return t.messages.value},get datetimeFormats(){return t.datetimeFormats.value},get numberFormats(){return t.numberFormats.value},get availableLocales(){return t.availableLocales},get formatter(){return{interpolate(){return[]}}},set formatter(n){},get missing(){return t.getMissingHandler()},set missing(n){t.setMissingHandler(n)},get silentTranslationWarn(){return RA(t.missingWarn)?!t.missingWarn:t.missingWarn},set silentTranslationWarn(n){t.missingWarn=RA(n)?!n:n},get silentFallbackWarn(){return RA(t.fallbackWarn)?!t.fallbackWarn:t.fallbackWarn},set silentFallbackWarn(n){t.fallbackWarn=RA(n)?!n:n},get modifiers(){return t.modifiers},get formatFallbackMessages(){return t.fallbackFormat},set formatFallbackMessages(n){t.fallbackFormat=n},get postTranslation(){return t.getPostTranslationHandler()},set postTranslation(n){t.setPostTranslationHandler(n)},get sync(){return t.inheritLocale},set sync(n){t.inheritLocale=n},get warnHtmlInMessage(){return t.warnHtmlMessage?"warn":"off"},set warnHtmlInMessage(n){t.warnHtmlMessage=n!=="off"},get escapeParameterHtml(){return t.escapeParameter},set escapeParameterHtml(n){t.escapeParameter=n},get preserveDirectiveContent(){return!0},set preserveDirectiveContent(n){},get pluralizationRules(){return t.pluralRules||{}},__composer:t,t(...n){const[r,g,s]=n,a={};let I=null,l=null;if(!aA(r))throw Ie(re.INVALID_ARGUMENT);const C=r;return aA(g)?a.locale=g:WA(g)?I=g:vA(g)&&(l=g),WA(s)?I=s:vA(s)&&(l=s),Reflect.apply(t.t,t,[C,I||l||{},a])},rt(...n){return Reflect.apply(t.rt,t,[...n])},tc(...n){const[r,g,s]=n,a={plural:1};let I=null,l=null;if(!aA(r))throw Ie(re.INVALID_ARGUMENT);const C=r;return aA(g)?a.locale=g:Ae(g)?a.plural=g:WA(g)?I=g:vA(g)&&(l=g),aA(s)?a.locale=s:WA(s)?I=s:vA(s)&&(l=s),Reflect.apply(t.t,t,[C,I||l||{},a])},te(n,r){return t.te(n,r)},tm(n){return t.tm(n)},getLocaleMessage(n){return t.getLocaleMessage(n)},setLocaleMessage(n,r){t.setLocaleMessage(n,r)},mergeLocaleMessage(n,r){t.mergeLocaleMessage(n,r)},d(...n){return Reflect.apply(t.d,t,[...n])},getDateTimeFormat(n){return t.getDateTimeFormat(n)},setDateTimeFormat(n,r){t.setDateTimeFormat(n,r)},mergeDateTimeFormat(n,r){t.mergeDateTimeFormat(n,r)},n(...n){return Reflect.apply(t.n,t,[...n])},getNumberFormat(n){return t.getNumberFormat(n)},setNumberFormat(n,r){t.setNumberFormat(n,r)},mergeNumberFormat(n,r){t.mergeNumberFormat(n,r)},getChoiceIndex(n,r){return-1}};return o.__extender=i,o}}const cg={tag:{type:[String,Object]},locale:{type:String},scope:{type:String,validator:A=>A==="parent"||A==="global",default:"parent"},i18n:{type:Object}};function Iu({slots:A},e){return e.length===1&&e[0]==="default"?(A.default?A.default():[]).reduce((i,o)=>[...i,...o.type===qA?o.children:[o]],[]):e.reduce((t,i)=>{const o=A[i];return o&&(t[i]=o()),t},HA())}function YC(A){return qA}const Cu=fo({name:"i18n-t",props:Be({keypath:{type:String,required:!0},plural:{type:[Number,String],validator:A=>Ae(A)||!isNaN(A)}},cg),setup(A,e){const{slots:t,attrs:i}=e,o=A.i18n||we({useScope:A.scope,__useComponent:!0});return()=>{const n=Object.keys(t).filter(l=>l!=="_"),r=HA();A.locale&&(r.locale=A.locale),A.plural!==void 0&&(r.plural=aA(A.plural)?+A.plural:A.plural);const g=Iu(e,n),s=o[kr](A.keypath,g,r),a=Be(HA(),i),I=aA(A.tag)||UA(A.tag)?A.tag:YC();return GA(I,a,s)}}}),Hs=Cu;function lu(A){return WA(A)&&!aA(A[0])}function _C(A,e,t,i){const{slots:o,attrs:n}=e;return()=>{const r={part:!0};let g=HA();A.locale&&(r.locale=A.locale),aA(A.format)?r.key=A.format:UA(A.format)&&(aA(A.format.key)&&(r.key=A.format.key),g=Object.keys(A.format).reduce((C,Q)=>t.includes(Q)?Be(HA(),C,{[Q]:A.format[Q]}):C,HA()));const s=i(A.value,r,g);let a=[r.key];WA(s)?a=s.map((C,Q)=>{const f=o[C.type],h=f?f({[C.type]:C.value,index:Q,parts:s}):[C.value];return lu(h)&&(h[0].key=`${C.type}-${Q}`),h}):aA(s)&&(a=[s]);const I=Be(HA(),n),l=aA(A.tag)||UA(A.tag)?A.tag:YC();return GA(l,I,a)}}const Bu=fo({name:"i18n-n",props:Be({value:{type:Number,required:!0},format:{type:[String,Object]}},cg),setup(A,e){const t=A.i18n||we({useScope:A.scope,__useComponent:!0});return _C(A,e,UC,(...i)=>t[Fr](...i))}}),Ys=Bu,Qu=fo({name:"i18n-d",props:Be({value:{type:[Number,Date],required:!0},format:{type:[String,Object]}},cg),setup(A,e){const t=A.i18n||we({useScope:A.scope,__useComponent:!0});return _C(A,e,LC,(...i)=>t[vr](...i))}}),_s=Qu;function cu(A,e){const t=A;if(A.mode==="composition")return t.__getInstance(e)||A.global;{const i=t.__getInstance(e);return i!=null?i.__composer:A.global.__composer}}function Eu(A){const e=r=>{const{instance:g,modifiers:s,value:a}=r;if(!g||!g.$)throw Ie(re.UNEXPECTED_ERROR);const I=cu(A,g.$),l=Ks(a);return[Reflect.apply(I.t,I,[...Os(l)]),I]};return{created:(r,g)=>{const[s,a]=e(g);nn&&A.global===a&&(r.__i18nWatcher=ae(a.locale,()=>{g.instance&&g.instance.$forceUpdate()})),r.__composer=a,r.textContent=s},unmounted:r=>{nn&&r.__i18nWatcher&&(r.__i18nWatcher(),r.__i18nWatcher=void 0,delete r.__i18nWatcher),r.__composer&&(r.__composer=void 0,delete r.__composer)},beforeUpdate:(r,{value:g})=>{if(r.__composer){const s=r.__composer,a=Ks(g);r.textContent=Reflect.apply(s.t,s,[...Os(a)])}},getSSRProps:r=>{const[g]=e(r);return{textContent:g}}}}function Ks(A){if(aA(A))return{path:A};if(vA(A)){if(!("path"in A))throw Ie(re.REQUIRED_VALUE,"path");return A}else throw Ie(re.INVALID_VALUE)}function Os(A){const{path:e,locale:t,args:i,choice:o,plural:n}=A,r={},g=i||{};return aA(t)&&(r.locale=t),Ae(o)&&(r.plural=o),Ae(n)&&(r.plural=n),[e,g,r]}function uu(A,e,...t){const i=vA(t[0])?t[0]:{},o=!!i.useI18nComponentName;(RA(i.globalInstall)?i.globalInstall:!0)&&([o?"i18n":Hs.name,"I18nT"].forEach(r=>A.component(r,Hs)),[Ys.name,"I18nN"].forEach(r=>A.component(r,Ys)),[_s.name,"I18nD"].forEach(r=>A.component(r,_s))),A.directive("t",Eu(e))}function fu(A,e,t){return{beforeCreate(){const i=vi();if(!i)throw Ie(re.UNEXPECTED_ERROR);const o=this.$options;if(o.i18n){const n=o.i18n;if(o.__i18n&&(n.__i18n=o.__i18n),n.__root=e,this===this.$root)this.$i18n=Ps(A,n);else{n.__injectWithOption=!0,n.__extender=t.__vueI18nExtend,this.$i18n=Rr(n);const r=this.$i18n;r.__extender&&(r.__disposer=r.__extender(this.$i18n))}}else if(o.__i18n)if(this===this.$root)this.$i18n=Ps(A,o);else{this.$i18n=Rr({__i18n:o.__i18n,__injectWithOption:!0,__extender:t.__vueI18nExtend,__root:e});const n=this.$i18n;n.__extender&&(n.__disposer=n.__extender(this.$i18n))}else this.$i18n=A;o.__i18nGlobal&&HC(e,o,o),this.$t=(...n)=>this.$i18n.t(...n),this.$rt=(...n)=>this.$i18n.rt(...n),this.$tc=(...n)=>this.$i18n.tc(...n),this.$te=(n,r)=>this.$i18n.te(n,r),this.$d=(...n)=>this.$i18n.d(...n),this.$n=(...n)=>this.$i18n.n(...n),this.$tm=n=>this.$i18n.tm(n),t.__setInstance(i,this.$i18n)},mounted(){},unmounted(){const i=vi();if(!i)throw Ie(re.UNEXPECTED_ERROR);const o=this.$i18n;delete this.$t,delete this.$rt,delete this.$tc,delete this.$te,delete this.$d,delete this.$n,delete this.$tm,o.__disposer&&(o.__disposer(),delete o.__disposer,delete o.__extender),t.__deleteInstance(i),delete this.$i18n}}}function Ps(A,e){A.locale=e.locale||A.locale,A.fallbackLocale=e.fallbackLocale||A.fallbackLocale,A.missing=e.missing||A.missing,A.silentTranslationWarn=e.silentTranslationWarn||A.silentFallbackWarn,A.silentFallbackWarn=e.silentFallbackWarn||A.silentFallbackWarn,A.formatFallbackMessages=e.formatFallbackMessages||A.formatFallbackMessages,A.postTranslation=e.postTranslation||A.postTranslation,A.warnHtmlInMessage=e.warnHtmlInMessage||A.warnHtmlInMessage,A.escapeParameterHtml=e.escapeParameterHtml||A.escapeParameterHtml,A.sync=e.sync||A.sync,A.__composer[xC](e.pluralizationRules||A.pluralizationRules);const t=Rn(A.locale,{messages:e.messages,__i18n:e.__i18n});return Object.keys(t).forEach(i=>A.mergeLocaleMessage(i,t[i])),e.datetimeFormats&&Object.keys(e.datetimeFormats).forEach(i=>A.mergeDateTimeFormat(i,e.datetimeFormats[i])),e.numberFormats&&Object.keys(e.numberFormats).forEach(i=>A.mergeNumberFormat(i,e.numberFormats[i])),A}const hu=Vt("global-vue-i18n");function du(A={},e){const t=__VUE_I18N_LEGACY_API__&&RA(A.legacy)?A.legacy:__VUE_I18N_LEGACY_API__,i=RA(A.globalInjection)?A.globalInjection:!0,o=__VUE_I18N_LEGACY_API__&&t?!!A.allowComposition:!0,n=new Map,[r,g]=pu(A,t),s=Vt("");function a(C){return n.get(C)||null}function I(C,Q){n.set(C,Q)}function l(C){n.delete(C)}{const C={get mode(){return __VUE_I18N_LEGACY_API__&&t?"legacy":"composition"},get allowComposition(){return o},async install(Q,...f){if(Q.__VUE_I18N_SYMBOL__=s,Q.provide(Q.__VUE_I18N_SYMBOL__,C),vA(f[0])){const E=f[0];C.__composerExtend=E.__composerExtend,C.__vueI18nExtend=E.__vueI18nExtend}let h=null;!t&&i&&(h=Ru(Q,C.global)),__VUE_I18N_FULL_INSTALL__&&uu(Q,C,...f),__VUE_I18N_LEGACY_API__&&t&&Q.mixin(fu(g,g.__composer,C));const u=Q.unmount;Q.unmount=()=>{h&&h(),C.dispose(),u()}},get global(){return g},dispose(){r.stop()},__instances:n,__getInstance:a,__setInstance:I,__deleteInstance:l};return C}}function we(A={}){const e=vi();if(e==null)throw Ie(re.MUST_BE_CALL_SETUP_TOP);if(!e.isCE&&e.appContext.app!=null&&!e.appContext.app.__VUE_I18N_SYMBOL__)throw Ie(re.NOT_INSTALLED);const t=mu(e),i=wu(t),o=TC(e),n=yu(A,o);if(__VUE_I18N_LEGACY_API__&&t.mode==="legacy"&&!A.__useComponent){if(!t.allowComposition)throw Ie(re.NOT_AVAILABLE_IN_LEGACY_MODE);return Fu(e,n,i,A)}if(n==="global")return HC(i,A,o),i;if(n==="parent"){let s=Du(t,e,A.__useComponent);return s==null&&(s=i),s}const r=t;let g=r.__getInstance(e);if(g==null){const s=Be({},A);"__i18n"in o&&(s.__i18n=o.__i18n),i&&(s.__root=i),g=Qg(s),r.__composerExtend&&(g[Sr]=r.__composerExtend(g)),vu(r,e,g),r.__setInstance(e,g)}return g}function pu(A,e,t){const i=EB();{const o=__VUE_I18N_LEGACY_API__&&e?i.run(()=>Rr(A)):i.run(()=>Qg(A));if(o==null)throw Ie(re.UNEXPECTED_ERROR);return[i,o]}}function mu(A){{const e=rt(A.isCE?hu:A.appContext.app.__VUE_I18N_SYMBOL__);if(!e)throw Ie(A.isCE?re.NOT_INSTALLED_WITH_PROVIDE:re.UNEXPECTED_ERROR);return e}}function yu(A,e){return vn(A)?"__i18n"in e?"local":"global":A.useScope?A.useScope:"local"}function wu(A){return A.mode==="composition"?A.global:A.global.__composer}function Du(A,e,t=!1){let i=null;const o=e.root;let n=ku(e,t);for(;n!=null;){const r=A;if(A.mode==="composition")i=r.__getInstance(n);else if(__VUE_I18N_LEGACY_API__){const g=r.__getInstance(n);g!=null&&(i=g.__composer,t&&i&&!i[JC]&&(i=null))}if(i!=null||o===n)break;n=n.parent}return i}function ku(A,e=!1){return A==null?null:e&&A.vnode.ctx||A.parent}function vu(A,e,t){Ft(()=>{},e),Ci(()=>{const i=t;A.__deleteInstance(e);const o=i[Sr];o&&(o(),delete i[Sr])},e)}function Fu(A,e,t,i={}){const o=e==="local",n=ig(null);if(o&&A.proxy&&!(A.proxy.$options.i18n||A.proxy.$options.__i18n))throw Ie(re.MUST_DEFINE_I18N_OPTION_IN_ALLOW_COMPOSITION);const r=RA(i.inheritLocale)?i.inheritLocale:!aA(i.locale),g=uA(!o||r?t.locale.value:aA(i.locale)?i.locale:Si),s=uA(!o||r?t.fallbackLocale.value:aA(i.fallbackLocale)||WA(i.fallbackLocale)||vA(i.fallbackLocale)||i.fallbackLocale===!1?i.fallbackLocale:g.value),a=uA(Rn(g.value,i)),I=uA(vA(i.datetimeFormats)?i.datetimeFormats:{[g.value]:{}}),l=uA(vA(i.numberFormats)?i.numberFormats:{[g.value]:{}}),C=o?t.missingWarn:RA(i.missingWarn)||Ot(i.missingWarn)?i.missingWarn:!0,Q=o?t.fallbackWarn:RA(i.fallbackWarn)||Ot(i.fallbackWarn)?i.fallbackWarn:!0,f=o?t.fallbackRoot:RA(i.fallbackRoot)?i.fallbackRoot:!0,h=!!i.fallbackFormat,u=PA(i.missing)?i.missing:null,E=PA(i.postTranslation)?i.postTranslation:null,D=o?t.warnHtmlMessage:RA(i.warnHtmlMessage)?i.warnHtmlMessage:!0,c=!!i.escapeParameter,p=o?t.modifiers:vA(i.modifiers)?i.modifiers:{},d=i.pluralRules||o&&t.pluralRules;function v(){return[g.value,s.value,a.value,I.value,l.value]}const b=hA({get:()=>n.value?n.value.locale.value:g.value,set:k=>{n.value&&(n.value.locale.value=k),g.value=k}}),N=hA({get:()=>n.value?n.value.fallbackLocale.value:s.value,set:k=>{n.value&&(n.value.fallbackLocale.value=k),s.value=k}}),H=hA(()=>n.value?n.value.messages.value:a.value),M=hA(()=>I.value),U=hA(()=>l.value);function R(){return n.value?n.value.getPostTranslationHandler():E}function j(k){n.value&&n.value.setPostTranslationHandler(k)}function rA(){return n.value?n.value.getMissingHandler():u}function IA(k){n.value&&n.value.setMissingHandler(k)}function AA(k){return v(),k()}function z(...k){return n.value?AA(()=>Reflect.apply(n.value.t,null,[...k])):AA(()=>"")}function oA(...k){return n.value?Reflect.apply(n.value.rt,null,[...k]):""}function dA(...k){return n.value?AA(()=>Reflect.apply(n.value.d,null,[...k])):AA(()=>"")}function Y(...k){return n.value?AA(()=>Reflect.apply(n.value.n,null,[...k])):AA(()=>"")}function W(k){return n.value?n.value.tm(k):{}}function $(k,x){return n.value?n.value.te(k,x):!1}function DA(k){return n.value?n.value.getLocaleMessage(k):{}}function NA(k,x){n.value&&(n.value.setLocaleMessage(k,x),a.value[k]=x)}function oe(k,x){n.value&&n.value.mergeLocaleMessage(k,x)}function LA(k){return n.value?n.value.getDateTimeFormat(k):{}}function _(k,x){n.value&&(n.value.setDateTimeFormat(k,x),I.value[k]=x)}function eA(k,x){n.value&&n.value.mergeDateTimeFormat(k,x)}function tA(k){return n.value?n.value.getNumberFormat(k):{}}function EA(k,x){n.value&&(n.value.setNumberFormat(k,x),l.value[k]=x)}function SA(k,x){n.value&&n.value.mergeNumberFormat(k,x)}const w={get id(){return n.value?n.value.id:-1},locale:b,fallbackLocale:N,messages:H,datetimeFormats:M,numberFormats:U,get inheritLocale(){return n.value?n.value.inheritLocale:r},set inheritLocale(k){n.value&&(n.value.inheritLocale=k)},get availableLocales(){return n.value?n.value.availableLocales:Object.keys(a.value)},get modifiers(){return n.value?n.value.modifiers:p},get pluralRules(){return n.value?n.value.pluralRules:d},get isGlobal(){return n.value?n.value.isGlobal:!1},get missingWarn(){return n.value?n.value.missingWarn:C},set missingWarn(k){n.value&&(n.value.missingWarn=k)},get fallbackWarn(){return n.value?n.value.fallbackWarn:Q},set fallbackWarn(k){n.value&&(n.value.missingWarn=k)},get fallbackRoot(){return n.value?n.value.fallbackRoot:f},set fallbackRoot(k){n.value&&(n.value.fallbackRoot=k)},get fallbackFormat(){return n.value?n.value.fallbackFormat:h},set fallbackFormat(k){n.value&&(n.value.fallbackFormat=k)},get warnHtmlMessage(){return n.value?n.value.warnHtmlMessage:D},set warnHtmlMessage(k){n.value&&(n.value.warnHtmlMessage=k)},get escapeParameter(){return n.value?n.value.escapeParameter:c},set escapeParameter(k){n.value&&(n.value.escapeParameter=k)},t:z,getPostTranslationHandler:R,setPostTranslationHandler:j,getMissingHandler:rA,setMissingHandler:IA,rt:oA,d:dA,n:Y,tm:W,te:$,getLocaleMessage:DA,setLocaleMessage:NA,mergeLocaleMessage:oe,getDateTimeFormat:LA,setDateTimeFormat:_,mergeDateTimeFormat:eA,getNumberFormat:tA,setNumberFormat:EA,mergeNumberFormat:SA};function S(k){k.locale.value=g.value,k.fallbackLocale.value=s.value,Object.keys(a.value).forEach(x=>{k.mergeLocaleMessage(x,a.value[x])}),Object.keys(I.value).forEach(x=>{k.mergeDateTimeFormat(x,I.value[x])}),Object.keys(l.value).forEach(x=>{k.mergeNumberFormat(x,l.value[x])}),k.escapeParameter=c,k.fallbackFormat=h,k.fallbackRoot=f,k.fallbackWarn=Q,k.missingWarn=C,k.warnHtmlMessage=D}return TI(()=>{if(A.proxy==null||A.proxy.$i18n==null)throw Ie(re.NOT_AVAILABLE_COMPOSITION_IN_LEGACY);const k=n.value=A.proxy.$i18n.__composer;e==="global"?(g.value=k.locale.value,s.value=k.fallbackLocale.value,a.value=k.messages.value,I.value=k.datetimeFormats.value,l.value=k.numberFormats.value):o&&S(k)}),w}const Su=["locale","fallbackLocale","availableLocales"],Vs=["t","rt","d","n","tm","te"];function Ru(A,e){const t=Object.create(null);return Su.forEach(o=>{const n=Object.getOwnPropertyDescriptor(e,o);if(!n)throw Ie(re.UNEXPECTED_ERROR);const r=le(n.value)?{get(){return n.value.value},set(g){n.value.value=g}}:{get(){return n.get&&n.get()}};Object.defineProperty(t,o,r)}),A.config.globalProperties.$i18n=t,Vs.forEach(o=>{const n=Object.getOwnPropertyDescriptor(e,o);if(!n||!n.value)throw Ie(re.UNEXPECTED_ERROR);Object.defineProperty(A.config.globalProperties,`$${o}`,n)}),()=>{delete A.config.globalProperties.$i18n,Vs.forEach(o=>{delete A.config.globalProperties[`$${o}`]})}}nu();__INTLIFY_JIT_COMPILATION__?Ds($E):Ds(zE);KE(yE);OE(wC);if(__INTLIFY_PROD_DEVTOOLS__){const A=ht();A.__INTLIFY__=!0,NE(A.__INTLIFY_DEVTOOLS_GLOBAL_HOOK__)}const Jt=uA({isOnline:!1,error:"",lastCheck:null}),ge=uA({chip:null,board:null,firmware:null,flash:null,assetsPartition:null,network:null,screen:null}),go=uA(""),so=uA(""),br=uA(!1),yi=uA(null),bu=A=>new URLSearchParams(window.location.search).get(A),Po=async(A,e={})=>{const t=await fetch(`/xiaozhi/device/tools/call/${so.value}`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${go.value}`},body:JSON.stringify({name:A,arguments:e})});if(t.ok)return await t.json();{const i=await t.text();console.error(`MCP tool ${A} failed:`,t.status,i);let o=`Failed to call ${A}`;try{const n=JSON.parse(i);n.message&&(o=n.message)}catch{o=`${o}: HTTP ${t.status}`}throw new Error(o)}},Nu=async()=>{var A,e,t,i;try{const[o,n,r]=await Promise.allSettled([Po("self.get_system_info"),Po("self.get_device_status"),Po("self.screen.get_info")]);if(o.status==="fulfilled"&&o.value){const g=o.value.data||o.value;if(ge.value.chip={model:g.chip_model_name||"Unknown"},ge.value.board={model:((A=g.board)==null?void 0:A.name)||"Unknown"},ge.value.firmware={version:((e=g.application)==null?void 0:e.version)||"Unknown"},g.flash_size){const s=Math.round(g.flash_size/1024/1024);ge.value.flash={size:`${s}MB`}}else ge.value.flash={size:"Unknown"};if(g.partition_table){const s=g.partition_table.find(a=>a.label==="assets");s?ge.value.assetsPartition={size:s.size,sizeFormatted:`${Math.round(s.size/1024/1024)}MB`}:ge.value.assetsPartition=null}else ge.value.assetsPartition=null}else console.warn("绯荤粺淇℃伅鑾峰彇澶辫触:",o.reason||o.value),ge.value.chip={model:"Unknown"},ge.value.board={model:"Unknown"},ge.value.firmware={version:"Unknown"},ge.value.flash={size:"Unknown"},ge.value.assetsPartition=null;if(n.status==="fulfilled"&&n.value){const g=n.value.data||n.value;ge.value.network={type:((t=g.network)==null?void 0:t.type)||"unknown",signal:((i=g.network)==null?void 0:i.signal)||"Unknown"}}else console.warn("璁惧鐘舵�佽幏鍙栧け璐�:",n.reason||n.value),ge.value.network={type:"unknown",signal:"Unknown"};if(r.status==="fulfilled"&&r.value){const g=r.value.data||r.value;ge.value.screen={resolution:`${g.width||0}x${g.height||0}`}}else console.warn("灞忓箷淇℃伅鑾峰彇澶辫触:",r.reason||r.value),ge.value.screen={resolution:"Unknown"}}catch(o){console.error("鑾峰彇璁惧淇℃伅鏃跺彂鐢熼敊璇�:",o)}},bn=async(A=!0)=>{br.value=!0;try{const e=await fetch(`/xiaozhi/device/tools/list/${so.value}`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${go.value}`}});if((await e.json()).code===0)Jt.value.isOnline=!0,Jt.value.error="",Jt.value.lastCheck=new Date,A&&await Nu();else throw new Error(`HTTP ${e.status}: ${e.statusText}`)}catch{Jt.value.isOnline=!1,Jt.value.error="",Jt.value.lastCheck=new Date,yi.value&&clearTimeout(yi.value),yi.value=setTimeout(bn,3e4)}finally{br.value=!1}},Gu=(A,e)=>{if(!A)return e("device.signal.unknown");switch(A.toLowerCase()){case"strong":return e("device.signal.strong");case"medium":return e("device.signal.medium");case"weak":return e("device.signal.weak");case"none":return e("device.signal.none");default:return A}},Lu=()=>{const A=localStorage.getItem("token");so.value=bu("deviceId"),A&&so.value&&(go.value=JSON.parse(A).token,bn())},Uu=()=>{yi.value&&(clearTimeout(yi.value),yi.value=null)},Mu=async()=>{await bn()};function Nn(){const A=hA(()=>!!go.value),e=hA(()=>Jt.value.isOnline);return{token:go,deviceId:so,deviceStatus:Jt,deviceInfo:ge,isChecking:br,hasToken:A,isDeviceOnline:e,initializeDeviceStatus:Lu,cleanupDeviceStatus:Uu,refreshDeviceStatus:Mu,checkDeviceStatus:bn,callMcpTool:Po,getSignalDisplayText:Gu}}const Ws={__name:"WifiIcon",props:{size:{type:String,default:"w-4 h-4"},color:{type:String,default:"text-blue-500"}},setup(A){const e=A,t=hA(()=>`${e.size} ${e.color}`);return(i,o)=>(T(),K("svg",{class:kA(t.value),fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[...o[0]||(o[0]=[B("path",{d:"M3 9c4.97-4.97 13.03-4.97 18 0","stroke-width":"2","stroke-linecap":"round"},null,-1),B("path",{d:"M6 12c3.31-3.31 8.69-3.31 12 0","stroke-width":"2","stroke-linecap":"round"},null,-1),B("path",{d:"M9 15c1.66-1.66 4.34-1.66 6 0","stroke-width":"2","stroke-linecap":"round"},null,-1),B("circle",{cx:"12",cy:"18",r:"1.5",fill:"currentColor"},null,-1)])],2))}},qs={__name:"Signal4GIcon",props:{size:{type:String,default:"w-4 h-4"},color:{type:String,default:"text-green-500"}},setup(A){const e=A,t=hA(()=>`${e.size} ${e.color}`);return(i,o)=>(T(),K("svg",{class:kA(t.value),fill:"currentColor",viewBox:"0 0 24 24"},[...o[0]||(o[0]=[Dn('<rect x="2" y="16" width="3" height="6" rx="0.5"></rect><rect x="7" y="12" width="3" height="10" rx="0.5" opacity="0.8"></rect><rect x="12" y="8" width="3" height="14" rx="0.5" opacity="0.6"></rect><rect x="17" y="4" width="3" height="18" rx="0.5" opacity="0.4"></rect><text x="21" y="8" font-size="6" font-weight="bold" fill="currentColor">4G</text>',5)])],2))}},js={__name:"ChipIcon",props:{size:{type:String,default:"w-4 h-4"},color:{type:String,default:"text-gray-400"}},setup(A){const e=A,t=hA(()=>`${e.size} ${e.color}`);return(i,o)=>(T(),K("svg",{class:kA(t.value),fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[...o[0]||(o[0]=[Dn('<rect x="7" y="7" width="10" height="10" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></rect><rect x="9" y="9" width="6" height="6" stroke-width="1.5" fill="currentColor" opacity="0.1"></rect><line x1="7" y1="9" x2="4" y2="9" stroke-width="1.5" stroke-linecap="round"></line><line x1="7" y1="12" x2="4" y2="12" stroke-width="1.5" stroke-linecap="round"></line><line x1="7" y1="15" x2="4" y2="15" stroke-width="1.5" stroke-linecap="round"></line><line x1="17" y1="9" x2="20" y2="9" stroke-width="1.5" stroke-linecap="round"></line><line x1="17" y1="12" x2="20" y2="12" stroke-width="1.5" stroke-linecap="round"></line><line x1="17" y1="15" x2="20" y2="15" stroke-width="1.5" stroke-linecap="round"></line><line x1="9" y1="7" x2="9" y2="4" stroke-width="1.5" stroke-linecap="round"></line><line x1="12" y1="7" x2="12" y2="4" stroke-width="1.5" stroke-linecap="round"></line><line x1="15" y1="7" x2="15" y2="4" stroke-width="1.5" stroke-linecap="round"></line><line x1="9" y1="17" x2="9" y2="20" stroke-width="1.5" stroke-linecap="round"></line><line x1="12" y1="17" x2="12" y2="20" stroke-width="1.5" stroke-linecap="round"></line><line x1="15" y1="17" x2="15" y2="20" stroke-width="1.5" stroke-linecap="round"></line>',14)])],2))}},Zs={__name:"FlashIcon",props:{size:{type:String,default:"w-4 h-4"},color:{type:String,default:"text-gray-400"}},setup(A){const e=A,t=hA(()=>`${e.size} ${e.color}`);return(i,o)=>(T(),K("svg",{class:kA(t.value),fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[...o[0]||(o[0]=[Dn('<rect x="6" y="5" width="12" height="14" rx="1" stroke-width="2"></rect><line x1="6" y1="8" x2="4" y2="8" stroke-width="1.5"></line><line x1="6" y1="11" x2="4" y2="11" stroke-width="1.5"></line><line x1="6" y1="14" x2="4" y2="14" stroke-width="1.5"></line><line x1="6" y1="17" x2="4" y2="17" stroke-width="1.5"></line><line x1="18" y1="8" x2="20" y2="8" stroke-width="1.5"></line><line x1="18" y1="11" x2="20" y2="11" stroke-width="1.5"></line><line x1="18" y1="14" x2="20" y2="14" stroke-width="1.5"></line><line x1="18" y1="17" x2="20" y2="17" stroke-width="1.5"></line><path d="M13 7l-3 5h2l-1 4 3-5h-2l1-4z" fill="currentColor" opacity="0.5"></path>',10)])],2))}},Xs={__name:"BoardIcon",props:{size:{type:String,default:"w-4 h-4"},color:{type:String,default:"text-gray-400"}},setup(A){const e=A,t=hA(()=>`${e.size} ${e.color}`);return(i,o)=>(T(),K("svg",{class:kA(t.value),fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[...o[0]||(o[0]=[Dn('<rect x="4" y="4" width="16" height="16" rx="1" stroke-width="2"></rect><circle cx="6.5" cy="6.5" r="1" stroke-width="1.5"></circle><circle cx="17.5" cy="6.5" r="1" stroke-width="1.5"></circle><circle cx="6.5" cy="17.5" r="1" stroke-width="1.5"></circle><circle cx="17.5" cy="17.5" r="1" stroke-width="1.5"></circle><rect x="9" y="9" width="6" height="6" rx="0.5" stroke-width="1.5" fill="currentColor" opacity="0.1"></rect><line x1="9" y1="11" x2="15" y2="11" stroke-width="1" opacity="0.5"></line><line x1="9" y1="13" x2="15" y2="13" stroke-width="1" opacity="0.5"></line><line x1="9" y1="12" x2="6" y2="12" stroke-width="1" opacity="0.4"></line><line x1="15" y1="12" x2="18" y2="12" stroke-width="1" opacity="0.4"></line>',10)])],2))}},zs={__name:"ScreenIcon",props:{size:{type:String,default:"w-4 h-4"},color:{type:String,default:"text-gray-400"}},setup(A){const e=A,t=hA(()=>`${e.size} ${e.color}`);return(i,o)=>(T(),K("svg",{class:kA(t.value),fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[...o[0]||(o[0]=[B("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"},null,-1)])],2))}},xu={class:"flex items-center space-x-2"},Ju={class:"flex items-center space-x-1"},Tu={key:0,class:"flex items-center space-x-1"},Hu={class:"text-xs text-gray-500"},Yu={key:0,class:"flex items-center space-x-4 text-sm text-gray-600"},_u={key:0,class:"flex items-center space-x-1"},Ku={key:1,class:"flex items-center space-x-1"},Ou={key:2,class:"flex items-center space-x-1"},Pu={key:3,class:"flex items-center space-x-1"},Vu={key:1,class:"lg:hidden flex flex-col bg-white rounded-lg border border-gray-200 shadow-sm overflow-hidden"},Wu={class:"flex items-center justify-between px-4 py-3 bg-gray-50 border-b border-gray-200"},qu={class:"flex items-center space-x-2"},ju={key:0,class:"flex items-center space-x-1"},Zu={class:"text-xs font-medium text-gray-600"},Xu={key:0,class:"px-4 py-3"},zu={class:"grid grid-cols-1 gap-2.5"},$u={class:"flex justify-between items-center py-1.5 border-b border-gray-100"},Af={key:0,class:"flex items-center space-x-2 flex-1"},ef={class:"min-w-0 flex-1"},tf={class:"text-xs text-gray-500 leading-tight"},of={class:"text-sm text-gray-800 font-medium truncate"},nf={key:1,class:"flex items-center space-x-2 flex-1 ml-3"},rf={class:"min-w-0 flex-1"},gf={class:"text-xs text-gray-500 leading-tight"},sf={class:"text-sm text-gray-800 font-medium truncate"},af={class:"flex justify-between items-center py-1.5"},If={key:0,class:"flex items-center space-x-2 flex-1"},Cf={class:"min-w-0 flex-1"},lf={class:"text-xs text-gray-500 leading-tight"},Bf={class:"text-sm text-gray-800 font-medium"},Qf={key:1,class:"flex items-center space-x-2 flex-1 ml-3"},cf={class:"min-w-0 flex-1"},Ef={class:"text-xs text-gray-500 leading-tight"},uf={class:"text-sm text-gray-800 font-medium"},ff={__name:"DeviceStatus",setup(A){const{t:e}=we(),{deviceStatus:t,deviceInfo:i,hasToken:o,initializeDeviceStatus:n,cleanupDeviceStatus:r,getSignalDisplayText:g}=Nn(),s=hA(()=>o.value);return Ft(()=>{n()}),Ci(()=>{r()}),(a,I)=>(T(),K(qA,null,[s.value?(T(),K("div",{key:0,class:kA(["hidden lg:flex items-center space-x-4",cA(t).isOnline?"":"opacity-60"])},[B("div",xu,[B("div",Ju,[B("div",{class:kA(["w-2 h-2 rounded-full",cA(t).isOnline?"bg-green-500":"bg-gray-400"])},null,2),B("span",{class:kA(["text-sm font-medium",cA(t).isOnline?"text-gray-700":"text-gray-500"])},F(cA(t).isOnline?a.$t("device.online"):a.$t("device.offline")),3)]),cA(t).isOnline&&cA(i).network?(T(),K("div",Tu,[cA(i).network.type==="wifi"?(T(),ye(cA(Ws),{key:0,color:"text-blue-500"})):cA(i).network.type==="4g"?(T(),ye(cA(qs),{key:1})):CA("",!0),B("span",Hu,F(cA(g)(cA(i).network.signal,cA(e))),1)])):CA("",!0)]),cA(t).isOnline?(T(),K("div",Yu,[cA(i).chip?(T(),K("div",_u,[TA(cA(js)),B("span",null,F(cA(i).chip.model),1)])):CA("",!0),cA(i).flash?(T(),K("div",Ku,[TA(cA(Zs)),B("span",null,F(cA(i).flash.size),1)])):CA("",!0),cA(i).board?(T(),K("div",Ou,[TA(cA(Xs)),B("span",null,F(cA(i).board.model),1)])):CA("",!0),cA(i).screen?(T(),K("div",Pu,[TA(cA(zs)),B("span",null,F(cA(i).screen.resolution),1)])):CA("",!0)])):CA("",!0)],2)):CA("",!0),s.value?(T(),K("div",Vu,[B("div",Wu,[B("div",qu,[B("div",{class:kA(["w-2.5 h-2.5 rounded-full",cA(t).isOnline?"bg-green-500":"bg-red-400"])},null,2),B("span",{class:kA(["text-sm font-medium",cA(t).isOnline?"text-gray-800":"text-gray-600"])},F(cA(t).isOnline?a.$t("device.online"):a.$t("device.offline")),3)]),cA(t).isOnline&&cA(i).network?(T(),K("div",ju,[cA(i).network.type==="wifi"?(T(),ye(cA(Ws),{key:0,color:"text-blue-500"})):cA(i).network.type==="4g"?(T(),ye(cA(qs),{key:1})):CA("",!0),B("span",Zu,F(cA(g)(cA(i).network.signal,cA(e))),1)])):CA("",!0)]),cA(t).isOnline?(T(),K("div",Xu,[B("div",zu,[B("div",$u,[cA(i).chip?(T(),K("div",Af,[TA(cA(js),{class:"flex-shrink-0"}),B("div",ef,[B("div",tf,F(a.$t("device.chip")),1),B("div",of,F(cA(i).chip.model),1)])])):CA("",!0),cA(i).board?(T(),K("div",nf,[TA(cA(Xs),{class:"flex-shrink-0"}),B("div",rf,[B("div",gf,F(a.$t("device.board")),1),B("div",sf,F(cA(i).board.model),1)])])):CA("",!0)]),B("div",af,[cA(i).flash?(T(),K("div",If,[TA(cA(Zs),{class:"flex-shrink-0"}),B("div",Cf,[B("div",lf,F(a.$t("device.flash")),1),B("div",Bf,F(cA(i).flash.size),1)])])):CA("",!0),cA(i).screen?(T(),K("div",Qf,[TA(cA(zs),{class:"flex-shrink-0"}),B("div",cf,[B("div",Ef,F(a.$t("device.screen")),1),B("div",uf,F(cA(i).screen.resolution),1)])])):CA("",!0)])])])):CA("",!0)])):CA("",!0)],64))}},hf={title:"Xiaozhi AI Customization"},df={cancel:"Cancel",confirm:"Confirm",close:"Close",start:"Start",download:"Download",generate:"Generate",reset:"Reset",yes:"Yes",no:"No",loading:"Loading...",error:"Error",success:"Success",warning:"Warning",info:"Information",next:"Next",previous:"Previous",finish:"Finish",retry:"Retry",ok:"OK",other:"Other",back:"Go Back"},pf={title:"Xiaozhi AI Customization"},mf={title:"Saved Configuration Detected",message:"Configuration has been automatically restored. You can continue from where you left off or start over.",restart:"Start Over"},yf={chip:"Chip Configuration",theme:"Theme Design",generate:"Preview & Generate"},wf={title:"Generate assets.bin",confirmConfig:"Please confirm your configuration",chipModel:"Chip Model:",resolution:"Resolution:",wakeword:"Wake Word:",font:"Font:",emoji:"Emoji Package:",fileList:"Included Files List",estimated:"Estimated",compressed:"Before compression",generating:"Generating assets.bin...",progress:"{progress}% Complete",completed:"Your assets.bin file is ready",filename:"Filename: assets.bin",fileSize:"File Size: {size}",generationTime:"Generation Time: {time}",downloadAssets:"Download assets.bin",flashToDevice:"Flash to Device Online",flashing:"Flashing to device...",flashingCancel:"Cancel Flash",flashingError:"Flashing Error",flashingInProgress:"Flashing in progress, please do not close the window",customFont:"Custom Font",customEmoji:"Custom Emoji Package",notConfigured:"Not Configured",wakewordNames:{wn9s_hilexin:"Hi, Espressif",wn9s_hiesp:"Hi, ESP",wn9s_nihaoxiaozhi:"Hello Xiaozhi",wn9_nihaoxiaozhi_tts:"Hello Xiaozhi",wn9_alexa:"Alexa"}},Df={init:"Initialize Generator",font:"Process Font Files",wakeword:"Package Wake Word Model",emoji:"Process Emoji Images",background:"Process Background Images",index:"Generate Index File",spiffs:"Build SPIFFS Mapping",package:"Complete Packaging"},kf={title:"Wake Word Configuration",description:"Select a preset wake word model or customize it using a command recognition model.",supportWakeNet9:"Your chip supports WakeNet9 models.",supportWakeNet9s:"Your chip supports WakeNet9s models.",notSupported:"Your chip does not support wake word functionality.",notice:"Note:",unsupportedMessage:"{chipModel} chip does not support wake word functionality. If you need to use wake words, please select ESP32-S3, ESP32-P4, or ESP32-C3/C5/C6 chips.",noWakeword:"No Wake Word",presetWakeword:"Preset Wake Word",customWakeword:"Custom Wake Word",selectWakeword:"Select Wake Word",placeholder:"Select wake word...",customSettings:"Custom Wake Word Settings",wakewordName:"Wake Word Name",wakewordNamePlaceholder:"e.g., Xiaozhi / Potato",wakewordCommand:"Wake Command",wakewordCommandPlaceholderCN:"Pinyin separated by spaces, e.g., ni hao xiao zhi",wakewordCommandPlaceholderEN:"English words, no punctuation, e.g., HELLO POTATO",threshold:"Sensitivity Threshold (0-100)",thresholdDesc:"Lower is more sensitive, default 20",duration:"Recognition Duration",durationDesc:"Maximum duration for command recognition",selectModel:"Select Recognition Model",mn6cn:"MultiNet6 (Chinese)",mn6en:"MultiNet6 (English)",mn7cn:"MultiNet7 (Chinese)",mn7en:"MultiNet7 (English)",errors:{nameRequired:"Please enter wake word name",commandRequired:"Please enter wake command",noPunctuation:"Command cannot contain punctuation",durationRange:"Duration must be between 500-10000ms"},tips:{tipLabel:"Tip:",optional:"Wake words are optional. If disabled, the device cannot be voice-activated.",wakeNet9sOnly:"C3/C5/C6 chips only support WakeNet9s lightweight models.",wakeNet9Full:"S3/P4 chips support complete WakeNet9 models with more choices.",customSupport:"Custom wake words use MultiNet models, currently only supported on ESP32-S3."}},vf={resetFailed:"Reset failed, please refresh the page and try again",generationFailed:"Generation failed: {error}",noFileToDownload:"No downloadable file available",deviceOffline:"Device is offline and cannot be flashed",fileTooLarge:"Flash failed: File size {fileSize}MB exceeds assets partition size {partitionSize}MB. Please reduce configuration content or use a device with larger capacity.",flashCancelConfirm:"Are you sure you want to cancel flashing?",oldDataCleared:`Old version emoji data structure has been detected and cleared.
+
+The new version uses file deduplication technology to save storage space.
+
+Please re-upload custom emoji images.`,incompatibleEmojiData:"Incompatible emoji data detected, please reconfigure"},Ff={authTokenMissing:"Authentication token not found",checkingDeviceStatus:"Checking device status...",deviceOfflineOrUnresponsive:"The device does not exist or is not online",unableToGetDeviceStatus:"Unable to get device status",initializingTransferService:"Initializing transfer service...",initializeTransferFailed:"Failed to initialize transfer service: {error}",uploadingFile:"Upload the file ...",uploadFailed:"Upload file failed",settingDeviceDownloadUrl:"Setting device download URL...",setDownloadUrlFailed:"Failed to set download URL: {error}",rebootingDevice:"Rebooting device...",waitingForDeviceReboot:"Waiting for device to reboot...",deviceRebootTimeout:"Device reboot timeout (60 seconds)",deviceReady:"Device ready, please wait for program burning",startingFileTransfer:"Starting file transfer...",flashCompleted:"During program burning, please pay attention to the firmware burning status!",onlineFlashFailed:"Online flashing failed: {error}"},Sf={online:"Online",offline:"Offline",checking:"Checking...",flashing:"Flashing...",ready:"Ready",chip:"Chip",board:"Board",flash:"Flash",screen:"Screen",signal:{unknown:"Unknown",strong:"Strong",medium:"Medium",weak:"Weak",none:"No Signal"}},Rf={title:"Step 1: Chip Model and Screen Configuration",loadingFromDevice:"Loading hardware configuration from device...",loadedFromDevice:"Hardware configuration automatically loaded from device",manualConfig:"Please configure hardware parameters manually",loadingTitle:"Loading Device Configuration...",loadingDesc:"Reading chip model and screen resolution from device",autoLoadedTitle:"Device Configuration Auto-loaded",chipModel:"Chip Model",screenWidth:"Screen Width",screenHeight:"Screen Height",colorFormat:"Color Format:",manualEdit:"Manual Configuration",collapseManual:"Collapse Manual Configuration",manualConfigTitle:"Manual Hardware Configuration",next:"Next",selectChip:"Select Chip",chipRequired:"Chip Model",widthRequired:"Screen Width",heightRequired:"Screen Height",loadingError:"Loading configuration error",deviceOffline:"Device not connected or offline",chipModelError:"Unable to get chip model, please enter manually",resolutionError:"Unable to get screen resolution, please enter manually",timeoutError:"Configuration loading timeout, please enter manually",manualConfigDesc:"Unable to auto-read device configuration",manualConfigHint:"Device not connected or offline, please manually input hardware configuration",manualConfigRequired:"Please manually input hardware configuration",resolutionFormatError:"Screen resolution format error"},bf={title:"Step 2: Theme Design",description:"Configure wake word, font, emoji and background to customize your Xiaozhi AI theme.",previous:"Previous",next:"Next",tabs:{wakeword:"Wake Word Config",font:"Font Config",emoji:"Emoji Collection",background:"Chat Background"}},Nf={title:"Step 3: Preview",description:"Preview the effect of your custom configuration on the actual device.",devicePreview:"Device Preview (1:1 Pixel Ratio)",previewSettings:"Preview Settings",previewText:"Preview Text",defaultPreviewText:"Hi, I'm your friend Xiaozhi!",currentEmotion:"Current Emotion",themeMode:"Theme Mode",lightMode:"Light",darkMode:"Dark",configSummary:"Configuration Summary",wakeword:"Wake Word:",font:"Font:",loading:"Loading...",emotion:"Emotion:",skin:"Skin:",previous:"Previous",generate:"Generate assets.bin",emotions:{neutral:"Neutral",happy:"Happy",laughing:"Laughing",funny:"Funny",sad:"Sad",angry:"Angry",crying:"Crying",loving:"Loving",embarrassed:"Embarrassed",surprised:"Surprised",shocked:"Shocked",thinking:"Thinking",winking:"Winking",cool:"Cool",relaxed:"Relaxed",delicious:"Delicious",kissy:"Kiss",confident:"Confident",sleepy:"Sleepy",silly:"Silly",confused:"Confused"},noEmotionConfigured:"No emotion configured",fontLoading:"Font loading...",configureEmojiFirst:"Please configure emoji package in theme design first",customFont:"Custom Font {size}px",customEmoji:"Custom Emoji {count} images",notConfigured:"Not configured",skinLight:"Light {type}",skinDark:"Dark {type}",hideSubtitle:"Hide Subtitle:",image:"Image",color:"Color"},Gf={title:"Chat Background Configuration",description:"Configure chat backgrounds for light and dark modes, supporting solid colors or custom images.",lightMode:"Light Mode Background",darkMode:"Dark Mode Background",solidBackground:"Solid Background",imageBackground:"Image Background",backgroundColor:"Background Color:",textColor:"Text Color:",backgroundImage:"Background Image:",clickOrDragToUpload:"Click or drag to upload background image",removeImage:"Remove Image",backgroundPreview:"Background Preview",lightModePreview:"Light Mode",darkModePreview:"Dark Mode",chatArea:"Chat Content Area",quickConfig:"Quick Configuration",defaultColors:"Default Colors",stoneTexture:"Stone Texture",sunnyColors:"Sunny Colors",skyBlue:"Sky Blue",romanticPink:"Romantic Pink",selectValidImage:"Please select a valid image file"},Lf={title:"Emoji Collection Configuration",description:"Choose preset emoji package or custom emoji images. Each emoji package contains 21 different emotion expressions.",noEmojiPack:"No Emoji Pack",noEmojiPackDescription:"Don't configure emoji pack, only font and background resources will be included in the package.",presetEmojiPack:"Preset Emoji Pack",customEmojiPack:"Custom Emoji Pack",selectPresetEmojiPack:"Select Preset Emoji Pack",twemoji64:"Twemoji 64脳64",twemoji32:"Twemoji 32脳32",twemojiDescription:"High-quality emoji set from Twitter, perfect for clear display",twitterEmojiName:"Twemoji {size}x{size}",twitterEmojiDescription:"Twitter emoji pack, {size}脳{size} pixels",notoEmojiName:"Noto Emoji {size}x{size}",notoEmojiDescription:"Google Noto animated emoji pack, {size}脳{size} pixels (requires PSRAM)",size:"Size",preview:"Preview",select:"Select",customEmojiTitle:"Custom Emoji Pack",customEmojiDescription:"Upload your own emoji images. Each emotion needs one image.",uploadInstructions:"Click or drag to upload emoji images (PNG/JPG/WEBP)",supportedFormats:"Supported formats: PNG, JPG, WEBP. Recommended size: 64脳64 pixels",emotionMapping:"Emotion Mapping",uploadAllEmotions:"Please upload emoji images for all emotions",maxImageWidth:"Max Image Width (px)",maxImageHeight:"Max Image Height (px)",clickToUpload:"Click to upload",orDragDrop:"or drag and drop",clickToUploadOrDrag:"Click to upload or drag here",remove:"Remove",replace:"Replace",selectValidEmojiImage:"Please select a valid emoji image file",selectValidFormat:"Please select a valid PNG or GIF format image",maxFileSize:"File size cannot exceed 1MB",invalidFormat:"Unsupported image format. Please use PNG, JPG, or WEBP.",required:"Required",uploadEmojiImages:"Upload Emoji Images (GIF requires PSRAM)",customEmojiPackConfig:"Custom Emoji Pack Configuration",neutralRequiredNotice:"* Must upload neutral default emotion, others are optional. If not uploaded, default emotion will be used instead.",sharedFileMessage:"Emotion {emotionKey} uses the same image file as {existingEmotions} (shared storage)",fileDeleted:"File {fileHash} has been deleted (no other emotion references)",fileRetained:"File {fileHash} is still used by other emotions, retained",imageLoadFailed:"Failed to load emoji image",emotions:{neutral:"Neutral",happy:"Happy",laughing:"Laughing",funny:"Funny",sad:"Sad",angry:"Angry",crying:"Crying",loving:"Loving",embarrassed:"Embarrassed",surprised:"Surprised",shocked:"Shocked",thinking:"Thinking",winking:"Winking",cool:"Cool",relaxed:"Relaxed",delicious:"Delicious",kissy:"Kiss",confident:"Confident",sleepy:"Sleepy",silly:"Silly",confused:"Confused"}},Uf={title:"Font Configuration",description:"Choose preset fonts or upload custom font files.",noFonts:"No Fonts",noFontsDescription:"Do not use font configuration, the device will use the default font",presetFonts:"Preset Fonts",customFonts:"Custom Fonts",selectPresetFont:"Select Preset Font",fontSize:"Font Size:",bitDepth:"Bit Depth:",charset:"Character Set:",fileSize:"File Size:",customFontConfig:"Custom Font Configuration",fontFile:"Font File",clickSelectFontFile:"Click to select font file",orDragFileHere:"or drag file here",supportedFormats:"Supports TTF, WOFF, WOFF2 formats",removeFile:"Remove File",fontSizePx:"Font Size (px)",range:"Range:",bitDepthBpp:"Bit Depth (BPP)",monochrome:"1 bpp (Monochrome)",fourColors:"2 bpp (4 colors)",sixteenColors:"4 bpp (16 colors)",characterSet:"Character Set",deepseekR1:"DeepSeek R1 (7405 characters)",gb2312:"GB2312 (7445 characters)",qwen18409:"Qwen (18409 characters)",latin1:"Latin1 (190 characters)",fullCharset:"Full Character Set (All characters in font file)",hideSubtitle:"Hide Subtitle",hideSubtitleDescription:"Do not show real-time text subtitles during conversation",selectValidFontFile:"Please select a valid font file (TTF, WOFF, WOFF2)",presetFontNames:{font_puhui_deepseek_14_1:"Alibaba PuHuiTi 14px",font_puhui_deepseek_16_4:"Alibaba PuHuiTi 16px",font_puhui_deepseek_20_4:"Alibaba PuHuiTi 20px",font_puhui_deepseek_30_4:"Alibaba PuHuiTi 30px",font_noto_qwen_14_1:"Noto Sans 14px",font_noto_qwen_16_4:"Noto Sans 16px",font_noto_qwen_20_4:"Noto Sans 20px",font_noto_qwen_30_4:"Noto Sans 30px"},charsetDeepseek:"DeepSeek 7405",charsetQwen:"Qwen 18409"},Mf={app:hf,common:df,header:pf,configNotice:mf,steps:yf,generateModal:wf,progressSteps:Df,wakewordConfig:kf,errors:vf,flashProgress:Ff,device:Sf,chipConfig:Rf,themeDesign:bf,generateSummary:Nf,backgroundConfig:Gf,emojiConfig:Lf,fontConfig:Uf},xf={title:"灏忔櫤 AI 鑷畾涔�"},Jf={cancel:"鍙栨秷",confirm:"纭",close:"鍏抽棴",start:"寮�濮�",download:"涓嬭浇",generate:"鐢熸垚",reset:"閲嶇疆",yes:"鏄�",no:"鍚�",loading:"鍔犺浇涓�...",error:"閿欒",success:"鎴愬姛",warning:"璀﹀憡",info:"淇℃伅",next:"涓嬩竴姝�",previous:"涓婁竴姝�",finish:"瀹屾垚",retry:"閲嶈瘯",ok:"纭畾",other:"鍏朵粬",back:"杩斿洖"},Tf={title:"灏忔櫤 AI 鑷畾涔�"},Hf={title:"妫�娴嬪埌宸蹭繚瀛樼殑閰嶇疆",message:"閰嶇疆宸茶嚜鍔ㄦ仮澶嶏紝鎮ㄥ彲浠ョ户缁箣鍓嶇殑杩涘害鎴栭噸鏂板紑濮嬨��",restart:"閲嶆柊寮�濮�"},Yf={chip:"鑺墖閰嶇疆",theme:"涓婚璁捐",generate:"鏁堟灉棰勮"},_f={title:"鐢熸垚 assets.bin",confirmConfig:"璇风‘璁ゆ偍鐨勯厤缃�",chipModel:"鑺墖鍨嬪彿:",resolution:"鍒嗚鲸鐜�:",wakeword:"鍞ら啋璇�:",font:"瀛椾綋:",emoji:"琛ㄦ儏鍖�:",fileList:"鍖呭惈鐨勬枃浠跺垪琛�",estimated:"棰勪及",compressed:"鍘嬬缉鍓�",generating:"姝e湪鐢熸垚 assets.bin...",progress:"{progress}% 瀹屾垚",completed:"鎮ㄧ殑 assets.bin 鏂囦欢宸插噯澶囧氨缁�",filename:"鏂囦欢鍚�: assets.bin",fileSize:"鏂囦欢澶у皬: {size}",generationTime:"鐢熸垚鏃堕棿: {time}",downloadAssets:"涓嬭浇 assets.bin",flashToDevice:"鍦ㄧ嚎鐑у綍鍒拌澶�",flashing:"姝e湪鐑у綍鍒拌澶�...",flashingCancel:"鍙栨秷鐑у綍",flashingError:"鐑у綍閿欒",flashingInProgress:"鐑у綍涓紝璇峰嬁鍏抽棴绐楀彛",customFont:"鑷畾涔夊瓧浣�",customEmoji:"鑷畾涔夎〃鎯呭寘",notConfigured:"鏈厤缃�",wakewordNames:{wn9s_hilexin:"Hi,涔愰懌",wn9s_hiesp:"Hi,ESP",wn9s_nihaoxiaozhi:"浣犲ソ灏忔櫤",wn9_nihaoxiaozhi_tts:"浣犲ソ灏忔櫤",wn9_alexa:"Alexa"}},Kf={init:"鍒濆鍖栫敓鎴愬櫒",font:"澶勭悊瀛椾綋鏂囦欢",wakeword:"鎵撳寘鍞ら啋璇嶆ā鍨�",emoji:"澶勭悊琛ㄦ儏鍥剧墖",background:"澶勭悊鑳屾櫙鍥剧墖",index:"鐢熸垚绱㈠紩鏂囦欢",spiffs:"鏋勫缓SPIFFS鏄犲皠",package:"瀹屾垚鎵撳寘"},Of={resetFailed:"閲嶇疆澶辫触锛岃鍒锋柊椤甸潰閲嶈瘯",generationFailed:"鐢熸垚澶辫触: {error}",noFileToDownload:"娌℃湁鍙笅杞界殑鏂囦欢",deviceOffline:"璁惧绂荤嚎锛屾棤娉曡繘琛岀儳褰�",fileTooLarge:"鐑у綍澶辫触锛氭枃浠跺ぇ灏� {fileSize}MB 瓒呰繃 assets 鍒嗗尯澶у皬 {partitionSize}MB锛岃鍑忓皯閰嶇疆鍐呭鎴栦娇鐢ㄦ洿澶у閲忕殑璁惧銆�",flashCancelConfirm:"纭畾瑕佸彇娑堢儳褰曞悧锛�",oldDataCleared:`妫�娴嬪埌鏃х増鏈殑琛ㄦ儏鏁版嵁缁撴瀯宸茶娓呴櫎銆�
+
+鏂扮増鏈娇鐢ㄦ枃浠跺幓閲嶆妧鏈紝鍙互鑺傜渷瀛樺偍绌洪棿銆�
+
+璇烽噸鏂颁笂浼犺嚜瀹氫箟琛ㄦ儏鍥剧墖銆俙,incompatibleEmojiData:"妫�娴嬪埌涓嶅吋瀹圭殑琛ㄦ儏鏁版嵁锛岃閲嶆柊閰嶇疆"},Pf={authTokenMissing:"鏈壘鍒拌璇佷护鐗�",checkingDeviceStatus:"妫�鏌ヨ澶囩姸鎬�...",deviceOfflineOrUnresponsive:"璁惧涓嶅瓨鍦ㄦ垨涓嶅湪绾�",unableToGetDeviceStatus:"鏃犳硶鑾峰彇璁惧鐘舵��",initializingTransferService:"鍒濆鍖栦紶杈撴湇鍔�...",initializeTransferFailed:"鍒濆鍖栦紶杈撴湇鍔″け璐�: {error}",uploadingFile:"涓婁紶鏂囦欢涓�...",uploadFailed:"涓婁紶鏂囦欢澶辫触",settingDeviceDownloadUrl:"璁剧疆璁惧涓嬭浇URL...",setDownloadUrlFailed:"璁剧疆涓嬭浇URL澶辫触: {error}",rebootingDevice:"閲嶅惎璁惧...",waitingForDeviceReboot:"绛夊緟璁惧閲嶅惎...",deviceRebootTimeout:"绛夊緟璁惧閲嶅惎瓒呮椂锛�60绉掞級",deviceReady:"璁惧灏辩华锛岃绛夊緟绋嬪簭鐑у綍",startingFileTransfer:"寮�濮嬩紶杈撴枃浠�...",flashCompleted:"绋嬪簭鐑у綍涓紝璇风暀鎰忓浐浠剁殑鐑у綍鐘舵�侊紒",onlineFlashFailed:"鍦ㄧ嚎鐑у綍澶辫触: {error}"},Vf={online:"鍦ㄧ嚎",offline:"绂荤嚎",checking:"妫�鏌ヤ腑...",flashing:"鐑у綍涓�...",ready:"灏辩华",chip:"鑺墖",board:"寮�鍙戞澘",flash:"Flash",screen:"灞忓箷",signal:{unknown:"鏈煡",strong:"寮�",medium:"涓瓑",weak:"寮�",none:"鏃犱俊鍙�"}},Wf={title:"姝ラ 1: 鑺墖鍨嬪彿鍜屽睆骞曢厤缃�",loadingFromDevice:"姝e湪浠庤澶囪鍙栫‖浠堕厤缃�...",loadedFromDevice:"宸蹭粠璁惧鑷姩璇诲彇纭欢閰嶇疆",manualConfig:"璇锋墜鍔ㄩ厤缃‖浠跺弬鏁�",loadingTitle:"姝e湪鍔犺浇璁惧閰嶇疆...",loadingDesc:"浠庤澶囪鍙栬姱鐗囧瀷鍙峰拰灞忓箷鍒嗚鲸鐜�",autoLoadedTitle:"璁惧閰嶇疆宸茶嚜鍔ㄥ姞杞�",chipModel:"鑺墖鍨嬪彿",screenWidth:"灞忓箷瀹藉害",screenHeight:"灞忓箷楂樺害",colorFormat:"棰滆壊鏍煎紡:",manualEdit:"鎵嬪姩淇敼閰嶇疆",collapseManual:"鏀惰捣鎵嬪姩閰嶇疆",manualConfigTitle:"鎵嬪姩閰嶇疆纭欢鍙傛暟",next:"涓嬩竴姝�",selectChip:"璇烽�夋嫨鑺墖",chipRequired:"鑺墖鍨嬪彿",widthRequired:"灞忓箷瀹藉害",heightRequired:"灞忓箷楂樺害",loadingError:"鍔犺浇閰嶇疆鏃跺彂鐢熼敊璇�",deviceOffline:"璁惧鏈繛鎺ユ垨绂荤嚎",chipModelError:"鏃犳硶鑾峰彇鑺墖鍨嬪彿锛岃鎵嬪姩杈撳叆",resolutionError:"鏃犳硶鑾峰彇灞忓箷鍒嗚鲸鐜囷紝璇锋墜鍔ㄨ緭鍏�",timeoutError:"鍔犺浇璁惧閰嶇疆瓒呮椂锛岃鎵嬪姩杈撳叆",manualConfigDesc:"鏃犳硶鑷姩璇诲彇璁惧閰嶇疆",manualConfigHint:"璁惧鏈繛鎺ユ垨绂荤嚎锛岃鎵嬪姩杈撳叆纭欢閰嶇疆",manualConfigRequired:"璇锋墜鍔ㄨ緭鍏ョ‖浠堕厤缃�",resolutionFormatError:"灞忓箷鍒嗚鲸鐜囨牸寮忛敊璇�"},qf={title:"姝ラ 2: 涓婚璁捐",description:"閰嶇疆鍞ら啋璇嶃�佸瓧浣撱�佽〃鎯呭拰鑳屾櫙鏉ヨ嚜瀹氫箟鎮ㄧ殑灏忔櫤AI涓婚銆�",previous:"涓婁竴姝�",next:"涓嬩竴姝�",tabs:{wakeword:"鍞ら啋璇嶉厤缃�",font:"瀛椾綋閰嶇疆",emoji:"琛ㄦ儏闆嗗悎",background:"鑱婂ぉ鑳屾櫙"}},jf={title:"姝ラ 3: 鏁堟灉棰勮",description:"棰勮鎮ㄧ殑鑷畾涔夐厤缃湪瀹為檯璁惧涓婄殑鏁堟灉銆�",devicePreview:"璁惧棰勮 (1:1 鍍忕礌姣斾緥)",previewSettings:"棰勮璁剧疆",previewText:"棰勮鏂囧瓧",defaultPreviewText:"浣犲ソ锛屾垜鏄綘鐨勬湅鍙嬪皬鏅猴紒",currentEmotion:"褰撳墠琛ㄦ儏",themeMode:"涓婚妯″紡",lightMode:"娴呰壊",darkMode:"娣辫壊",configSummary:"閰嶇疆鎽樿",wakeword:"鍞ら啋璇�:",font:"瀛椾綋:",loading:"鍔犺浇涓�...",emotion:"琛ㄦ儏:",skin:"鐨偆:",previous:"涓婁竴姝�",generate:"鐢熸垚 assets.bin",emotions:{neutral:"榛樿",happy:"寮�蹇�",laughing:"澶х瑧",funny:"鎼炵瑧",sad:"浼ゅ績",angry:"鐢熸皵",crying:"鍝常",loving:"鍠滅埍",embarrassed:"灏村艾",surprised:"鎯婅",shocked:"闇囨儕",thinking:"鎬濊��",winking:"鐪ㄧ溂",cool:"閰风偒",relaxed:"鏀炬澗",delicious:"缇庡懗",kissy:"椋炲惢",confident:"鑷俊",sleepy:"鍥板��",silly:"璋冪毊",confused:"鍥版儜"},noEmotionConfigured:"鏈厤缃〃鎯�",fontLoading:"瀛椾綋鍔犺浇涓�...",configureEmojiFirst:"璇峰厛鍦ㄤ富棰樿璁′腑閰嶇疆琛ㄦ儏鍖�",customFont:"鑷畾涔夊瓧浣� {size}px",customEmoji:"鑷畾涔夎〃鎯� {count}寮�",notConfigured:"鏈厤缃�",skinLight:"娴呰壊{type}",skinDark:"娣辫壊{type}",hideSubtitle:"闅愯棌瀛楀箷:",image:"鍥剧墖",color:"棰滆壊"},Zf={title:"鑱婂ぉ鑳屾櫙閰嶇疆",description:"閰嶇疆娴呰壊妯″紡鍜屾繁鑹叉ā寮忕殑鑱婂ぉ鑳屾櫙锛屾敮鎸佺函鑹叉垨鑷畾涔夊浘鐗囥��",lightMode:"娴呰壊妯″紡鑳屾櫙",darkMode:"娣辫壊妯″紡鑳屾櫙",solidBackground:"绾壊鑳屾櫙",imageBackground:"鍥剧墖鑳屾櫙",backgroundColor:"鑳屾櫙棰滆壊:",textColor:"鏂囧瓧棰滆壊:",backgroundImage:"鑳屾櫙鍥剧墖:",clickOrDragToUpload:"鐐瑰嚮鎴栨嫋鎷戒笂浼犺儗鏅浘鐗�",removeImage:"绉婚櫎鍥剧墖",backgroundPreview:"鑳屾櫙棰勮",lightModePreview:"娴呰壊妯″紡",darkModePreview:"娣辫壊妯″紡",chatArea:"鑱婂ぉ鍐呭鍖哄煙",quickConfig:"蹇嵎閰嶇疆",defaultColors:"榛樿閰嶈壊",stoneTexture:"鐭冲ⅷ璐ㄦ劅",sunnyColors:"鏆栭槼閰嶈壊",skyBlue:"澶╃┖钃濊皟",romanticPink:"娴极绮夎壊",selectValidImage:"璇烽�夋嫨鏈夋晥鐨勫浘鐗囨枃浠�"},Xf={title:"琛ㄦ儏闆嗗悎閰嶇疆",description:"閫夋嫨棰勮琛ㄦ儏鍖呮垨鑷畾涔夎〃鎯呭浘鐗囥�傛瘡涓〃鎯呭寘鍖呭惈21绉嶄笉鍚屾儏缁殑琛ㄦ儏銆�",noEmojiPack:"鏃犺〃鎯呭寘",noEmojiPackDescription:"涓嶉厤缃〃鎯呭寘锛屾墦鍖呮椂鍙寘鍚瓧浣撳拰鑳屾櫙璧勬簮銆�",presetEmojiPack:"棰勮琛ㄦ儏鍖�",customEmojiPack:"鑷畾涔夎〃鎯呭寘",selectPresetEmojiPack:"閫夋嫨棰勮琛ㄦ儏鍖�",twemoji64:"Twemoji 64脳64",twemoji32:"Twemoji 32脳32",twemojiDescription:"鏉ヨ嚜Twitter鐨勯珮璐ㄩ噺琛ㄦ儏绗﹀彿闆嗭紝閫傚悎娓呮櫚鏄剧ず",twitterEmojiName:"Twemoji {size}x{size}",twitterEmojiDescription:"Twitter琛ㄦ儏鍖咃紝{size}脳{size}鍍忕礌",notoEmojiName:"Noto Emoji {size}x{size}",notoEmojiDescription:"Google Noto 鍔ㄧ敾琛ㄦ儏鍖咃紝{size}脳{size}鍍忕礌锛堥渶瑕� PSRAM锛�",size:"灏哄",preview:"棰勮",select:"閫夋嫨",customEmojiTitle:"鑷畾涔夎〃鎯呭寘",customEmojiDescription:"涓婁紶鎮ㄨ嚜宸辩殑琛ㄦ儏鍥剧墖銆傛瘡绉嶆儏缁渶瑕佷竴寮犲浘鐗囥��",uploadInstructions:"鐐瑰嚮鎴栨嫋鎷戒笂浼犺〃鎯呭浘鐗� (PNG/JPG/WEBP)",supportedFormats:"鏀寔鏍煎紡: PNG, JPG, WEBP銆傛帹鑽愬昂瀵�: 64脳64鍍忕礌",emotionMapping:"鎯呯华鏄犲皠",uploadAllEmotions:"璇蜂负鎵�鏈夋儏缁笂浼犺〃鎯呭浘鐗�",maxImageWidth:"鏈�澶у浘鐗囧搴� (px)",maxImageHeight:"鏈�澶у浘鐗囬珮搴� (px)",clickToUpload:"鐐瑰嚮涓婁紶",orDragDrop:"鎴栨嫋鎷戒笂浼�",clickToUploadOrDrag:"鐐瑰嚮涓婁紶鎴栨嫋鎷藉埌姝ゅ",remove:"绉婚櫎",replace:"鏇挎崲",selectValidEmojiImage:"璇烽�夋嫨鏈夋晥鐨勮〃鎯呭浘鐗囨枃浠�",selectValidFormat:"璇烽�夋嫨鏈夋晥鐨凱NG鎴朑IF鏍煎紡鍥剧墖",maxFileSize:"鏂囦欢澶у皬涓嶈兘瓒呰繃1MB",invalidFormat:"涓嶆敮鎸佺殑鍥剧墖鏍煎紡銆傝浣跨敤PNG銆丣PG鎴朩EBP銆�",required:"蹇呴渶",uploadEmojiImages:"涓婁紶琛ㄦ儏鍥剧墖锛圙IF 闇�瑕� PSRAM锛�",customEmojiPackConfig:"鑷畾涔夎〃鎯呭寘閰嶇疆",neutralRequiredNotice:"* 蹇呴』涓婁紶 neutral 榛樿琛ㄦ儏锛屽叾浠栬〃鎯呭彲閫夈�傚鏋滀笉涓婁紶鍏朵粬琛ㄦ儏锛屽皢浣跨敤榛樿琛ㄦ儏浠f浛銆�",sharedFileMessage:"琛ㄦ儏 {emotionKey} 浣跨敤浜嗕笌 {existingEmotions} 鐩稿悓鐨勫浘鐗囨枃浠讹紙鍏变韩瀛樺偍锛�",fileDeleted:"鏂囦欢 {fileHash} 宸插垹闄わ紙鏃犲叾浠栬〃鎯呭紩鐢級",fileRetained:"鏂囦欢 {fileHash} 浠嶈鍏朵粬琛ㄦ儏浣跨敤锛屼繚鐣欐枃浠�",imageLoadFailed:"琛ㄦ儏鍥剧墖鍔犺浇澶辫触",emotions:{neutral:"榛樿",happy:"寮�蹇�",laughing:"澶х瑧",funny:"鎼炵瑧",sad:"浼ゅ績",angry:"鐢熸皵",crying:"鍝常",loving:"鍠滅埍",embarrassed:"灏村艾",surprised:"鎯婅",shocked:"闇囨儕",thinking:"鎬濊��",winking:"鐪ㄧ溂",cool:"閰风偒",relaxed:"鏀炬澗",delicious:"缇庡懗",kissy:"椋炲惢",confident:"鑷俊",sleepy:"鍥板��",silly:"璋冪毊",confused:"鍥版儜"}},zf={title:"瀛椾綋閰嶇疆",description:"閫夋嫨棰勮瀛椾綋鎴栦笂浼犺嚜瀹氫箟瀛椾綋鏂囦欢銆�",noFonts:"鏃犲瓧浣�",noFontsDescription:"涓嶄娇鐢ㄥ瓧浣撻厤缃紝璁惧灏嗕娇鐢ㄩ粯璁ゅ瓧浣�",presetFonts:"棰勮瀛椾綋",customFonts:"鑷畾涔夊瓧浣�",selectPresetFont:"閫夋嫨棰勮瀛椾綋",fontSize:"瀛楀彿:",bitDepth:"浣嶆繁:",charset:"瀛楃闆�:",fileSize:"鏂囦欢澶у皬:",customFontConfig:"鑷畾涔夊瓧浣撻厤缃�",fontFile:"瀛椾綋鏂囦欢",clickSelectFontFile:"鐐瑰嚮閫夋嫨瀛椾綋鏂囦欢",orDragFileHere:"鎴栨嫋鎷芥枃浠跺埌姝ゅ",supportedFormats:"鏀寔 TTF, WOFF, WOFF2 鏍煎紡",removeFile:"绉婚櫎鏂囦欢",fontSizePx:"瀛楀彿 (px)",range:"鑼冨洿:",bitDepthBpp:"浣嶆繁 (BPP)",monochrome:"1 bpp (鍗曡壊)",fourColors:"2 bpp (4鑹�)",sixteenColors:"4 bpp (16鑹�)",characterSet:"瀛楃闆�",deepseekR1:"DeepSeek R1 (7405瀛楃)",gb2312:"GB2312 (7445瀛楃)",qwen18409:"Qwen (18409瀛楃)",latin1:"Latin1 (190瀛楃)",fullCharset:"瀹屾暣瀛楃闆� (瀛椾綋鏂囦欢鐨勬墍鏈夊瓧绗�)",hideSubtitle:"闅愯棌瀛楀箷",hideSubtitleDescription:"瀵硅瘽鏃朵笉鏄剧ず瀹炴椂鏂囧瓧瀛楀箷",selectValidFontFile:"璇烽�夋嫨鏈夋晥鐨勫瓧浣撴枃浠� (TTF, WOFF, WOFF2)",presetFontNames:{font_puhui_deepseek_14_1:"闃块噷宸村反鏅儬浣� 14px",font_puhui_deepseek_16_4:"闃块噷宸村反鏅儬浣� 16px",font_puhui_deepseek_20_4:"闃块噷宸村反鏅儬浣� 20px",font_puhui_deepseek_30_4:"闃块噷宸村反鏅儬浣� 30px",font_noto_qwen_14_1:"鎬濇簮榛戜綋 14px",font_noto_qwen_16_4:"鎬濇簮榛戜綋 16px",font_noto_qwen_20_4:"鎬濇簮榛戜綋 20px",font_noto_qwen_30_4:"鎬濇簮榛戜綋 30px"},charsetDeepseek:"DeepSeek 7405",charsetQwen:"Qwen 18409"},$f={title:"鍞ら啋璇嶉厤缃�",description:"閫夋嫨棰勮鍞ら啋璇嶆ā鍨嬫垨閫氳繃鍛戒护璇嶆ā鍨嬭嚜瀹氫箟鍞ら啋璇嶃��",supportWakeNet9:"鎮ㄧ殑鑺墖鏀寔 WakeNet9 妯″瀷銆�",supportWakeNet9s:"鎮ㄧ殑鑺墖鏀寔 WakeNet9s 妯″瀷銆�",notSupported:"鎮ㄧ殑鑺墖涓嶆敮鎸佸敜閱掕瘝鍔熻兘銆�",notice:"娉ㄦ剰锛�",unsupportedMessage:"{chipModel} 鑺墖涓嶆敮鎸佸敜閱掕瘝鍔熻兘銆傚闇�浣跨敤鍞ら啋璇嶏紝璇烽�夋嫨 ESP32-S3銆丒SP32-P4 鎴� ESP32-C3/C5/C6 鑺墖銆�",noWakeword:"鍏抽棴鍞ら啋",presetWakeword:"棰勮鍞ら啋璇�",customWakeword:"鑷畾涔夊敜閱掕瘝",selectWakeword:"閫夋嫨鍞ら啋璇�",placeholder:"閫夋嫨鍞ら啋璇�...",customSettings:"鑷畾涔夊敜閱掕瘝璁剧疆",wakewordName:"鍞ら啋璇嶅悕绉�",wakewordNamePlaceholder:"渚嬪锛氬皬鏅� / Potato",wakewordCommand:"鍞ら啋鍛戒护",wakewordCommandPlaceholderCN:"鎷奸煶绌烘牸闅斿紑锛屼緥濡傦細ni hao xiao zhi",wakewordCommandPlaceholderEN:"鑻辨枃鍗曡瘝锛屼笉鑳藉寘鍚爣鐐癸紝渚嬪锛欻ELLO POTATO",threshold:"鐏垫晱搴﹂槇鍊� (0-100)",thresholdDesc:"瓒婂皬瓒婄伒鏁忥紝榛樿 20",duration:"璇嗗埆鏃堕暱",durationDesc:"鍛戒护璇嶆渶闀挎寔缁椂闂�",selectModel:"閫夋嫨璇嗗埆妯″瀷",mn6cn:"MultiNet6 (涓枃)",mn6en:"MultiNet6 (鑻辨枃)",mn7cn:"MultiNet7 (涓枃)",mn7en:"MultiNet7 (鑻辨枃)",errors:{nameRequired:"璇疯緭鍏ュ敜閱掕瘝鍚嶇О",commandRequired:"璇疯緭鍏ュ敜閱掑懡浠�",noPunctuation:"鍛戒护涓嶈兘鍖呭惈鏍囩偣绗﹀彿",durationRange:"鏃堕暱闇�鍦� 500-10000ms 涔嬮棿"},tips:{tipLabel:"鎻愮ず锛�",optional:"鍞ら啋璇嶆槸鍙�夌殑锛屽叧闂悗璁惧灏嗘棤娉曢�氳繃璇煶鍞ら啋銆�",wakeNet9sOnly:"C3/C5/C6 鑺墖浠呮敮鎸� WakeNet9s 杞婚噺绾фā鍨嬨��",wakeNet9Full:"S3/P4 鑺墖鏀寔瀹屾暣鐨� WakeNet9 妯″瀷锛岄�夋嫨鏇村銆�",customSupport:"鑷畾涔夊敜閱掕瘝浣跨敤 MultiNet 妯″瀷瀹炵幇锛岀洰鍓嶄粎鏀寔 ESP32-S3 鑺墖銆�"}},Ah={app:xf,common:Jf,header:Tf,configNotice:Hf,steps:Yf,generateModal:_f,progressSteps:Kf,errors:Of,flashProgress:Pf,device:Vf,chipConfig:Wf,themeDesign:qf,generateSummary:jf,backgroundConfig:Zf,emojiConfig:Xf,fontConfig:zf,wakewordConfig:$f},eh={title:"灏忔櫤 AI 鑷▊"},th={cancel:"鍙栨秷",confirm:"纰鸿獚",close:"闂滈枆",start:"闁嬪",download:"涓嬭級",generate:"鐢熸垚",reset:"閲嶇疆",yes:"鏄�",no:"鍚�",loading:"杓夊叆涓�...",error:"閷",success:"鎴愬姛",warning:"璀﹀憡",info:"璩囪▕",next:"涓嬩竴姝�",previous:"涓婁竴姝�",finish:"瀹屾垚",retry:"閲嶈│",ok:"纰哄畾",other:"鍏朵粬",back:"杩斿洖"},ih={title:"灏忔櫤 AI 鑷▊"},oh={title:"妾㈡脯鍒板凡鍎插瓨鐨勯厤缃�",message:"閰嶇疆宸茶嚜鍕曟仮寰╋紝鎮ㄥ彲浠ョ辜绾屼箣鍓嶇殑閫插害鎴栭噸鏂伴枊濮嬨��",restart:"閲嶆柊闁嬪"},nh={chip:"鏅剁墖閰嶇疆",theme:"涓婚瑷▓",generate:"鏁堟灉闋愯"},rh={title:"鐢熸垚 assets.bin",confirmConfig:"璜嬬⒑瑾嶆偍鐨勯厤缃�",chipModel:"鏅剁墖鍨嬭櫉:",resolution:"瑙f瀽搴�:",wakeword:"鍠氶啋瑭�:",font:"瀛楅珨:",emoji:"琛ㄦ儏鍖�:",fileList:"鍖呭惈鐨勬獢妗堝垪琛�",estimated:"闋愪及",compressed:"澹撶府鍓�",generating:"姝e湪鐢熸垚 assets.bin...",progress:"{progress}% 瀹屾垚",completed:"鎮ㄧ殑 assets.bin 妾旀宸叉簴鍌欏氨绶�",filename:"妾旀鍚�: assets.bin",fileSize:"妾旀澶у皬: {size}",generationTime:"鐢熸垚鏅傞枔: {time}",downloadAssets:"涓嬭級 assets.bin",flashToDevice:"绶氫笂鐕掗寗鍒拌ō鍌�",flashing:"姝e湪鐕掗寗鍒拌ō鍌�...",flashingCancel:"鍙栨秷鐕掗寗",flashingError:"鐕掗寗閷",flashingInProgress:"鐕掗寗涓紝璜嬪嬁闂滈枆瑕栫獥",customFont:"鑷▊瀛楅珨",customEmoji:"鑷▊琛ㄦ儏濂椾欢",notConfigured:"鏈ō瀹�",wakewordNames:{wn9s_hilexin:"Hi,妯傞懌",wn9s_hiesp:"Hi,ESP",wn9s_nihaoxiaozhi:"浣犲ソ灏忔櫤",wn9_nihaoxiaozhi_tts:"浣犲ソ灏忔櫤",wn9_alexa:"Alexa"}},gh={init:"鍒濆鍖栫敓鎴愬櫒",font:"铏曠悊瀛楅珨妾旀",wakeword:"鎵撳寘鍠氶啋瑭炴ā鍨�",emoji:"铏曠悊琛ㄦ儏鍦栫墖",background:"铏曠悊鑳屾櫙鍦栫墖",index:"鐢熸垚绱㈠紩妾旀",spiffs:"寤烘SPIFFS鏄犲皠",package:"瀹屾垚鎵撳寘"},sh={title:"鍠氶啋瑭為厤缃�",description:"閬告搰闋愯ō鍠氶啋瑭炴ā鍨嬫垨閫忛亷鍛戒护瑭炴ā鍨嬭嚜瑷傚枤閱掕銆�",supportWakeNet9:"鎮ㄧ殑鏅剁墖鏀彺 WakeNet9 妯″瀷銆�",supportWakeNet9s:"鎮ㄧ殑鏅剁墖鏀彺 WakeNet9s 妯″瀷銆�",notSupported:"鎮ㄧ殑鏅剁墖涓嶆敮鎻村枤閱掕鍔熻兘銆�",notice:"娉ㄦ剰锛�",unsupportedMessage:"{chipModel} 鏅剁墖涓嶆敮鎻村枤閱掕鍔熻兘銆傚闇�浣跨敤鍠氶啋瑭烇紝璜嬮伕鎿� ESP32-S3銆丒SP32-P4 鎴� ESP32-C3/C5/C6 鏅剁墖銆�",noWakeword:"闂滈枆鍠氶啋",presetWakeword:"闋愯ō鍠氶啋瑭�",customWakeword:"鑷▊鍠氶啋瑭�",selectWakeword:"閬告搰鍠氶啋瑭�",placeholder:"閬告搰鍠氶啋瑭�...",customSettings:"鑷▊鍠氶啋瑭炶ō瀹�",wakewordName:"鍠氶啋瑭炲悕绋�",wakewordNamePlaceholder:"渚嬪锛氬皬鏅� / Potato",wakewordCommand:"鍠氶啋鍛戒护",wakewordCommandPlaceholderCN:"鎷奸煶绌烘牸闅旈枊锛屼緥濡傦細ni hao xiao zhi",wakewordCommandPlaceholderEN:"鑻辨枃鍠锛屼笉鑳藉寘鍚榛烇紝渚嬪锛欻ELLO POTATO",threshold:"闈堟晱搴﹂柧鍊� (0-100)",thresholdDesc:"瓒婂皬瓒婇潏鏁忥紝闋愯ō 20",duration:"璀樺垾鏅傞暦",durationDesc:"鍛戒护瑭炴渶闀锋寔绾屾檪闁�",selectModel:"閬告搰璀樺垾妯″瀷",mn6cn:"MultiNet6 (涓枃)",mn6en:"MultiNet6 (鑻辨枃)",mn7cn:"MultiNet7 (涓枃)",mn7en:"MultiNet7 (鑻辨枃)",errors:{nameRequired:"璜嬭几鍏ュ枤閱掕鍚嶇ū",commandRequired:"璜嬭几鍏ュ枤閱掑懡浠�",noPunctuation:"鍛戒护涓嶈兘鍖呭惈妯欓粸绗﹁櫉",durationRange:"鏅傞暦闇�鍦� 500-10000ms 涔嬮枔"},tips:{tipLabel:"鎻愮ず锛�",optional:"鍠氶啋瑭炴槸鍙伕鐨勶紝闂滈枆寰岃缃皣鐒℃硶閫忛亷瑾為煶鍠氶啋銆�",wakeNet9sOnly:"C3/C5/C6 鏅剁墖鍍呮敮鎻� WakeNet9s 杓曢噺绱氭ā鍨嬨��",wakeNet9Full:"S3/P4 鏅剁墖鏀彺瀹屾暣鐨� WakeNet9 妯″瀷锛岄伕鎿囨洿澶氥��",customSupport:"鑷▊鍠氶啋瑭炰娇鐢� MultiNet 妯″瀷瀵︾従锛岀洰鍓嶅儏鏀彺 ESP32-S3 鏅剁墖銆�"}},ah={resetFailed:"閲嶇疆澶辨晽锛岃珛閲嶆柊鏁寸悊闋侀潰閲嶈│",generationFailed:"鐢熸垚澶辨晽: {error}",noFileToDownload:"娌掓湁鍙笅杓夌殑妾旀",deviceOffline:"瑷倷闆㈢窔锛岀劇娉曢�茶鐕掗寗",fileTooLarge:"鐕掗寗澶辨晽锛氭獢妗堝ぇ灏� {fileSize}MB 瓒呴亷 assets 鍒嗗崁澶у皬 {partitionSize}MB锛岃珛娓涘皯閰嶇疆鍏у鎴栦娇鐢ㄦ洿澶у閲忕殑瑷倷銆�",flashCancelConfirm:"纰哄畾瑕佸彇娑堢噿閷勫棊锛�",oldDataCleared:`妾㈡脯鍒拌垔鐗堟湰鐨勮〃鎯呰硣鏂欑祼妲嬪凡琚竻闄ゃ��
+
+鏂扮増鏈娇鐢ㄦ獢妗堝幓閲嶆妧琛擄紝鍙互绡�鐪佸劜瀛樼┖闁撱��
+
+璜嬮噸鏂颁笂鍌宠嚜瑷傝〃鎯呭湒鐗囥�俙,incompatibleEmojiData:"妾㈡脯鍒颁笉鐩稿鐨勮〃鎯呰硣鏂欙紝璜嬮噸鏂伴厤缃�"},Ih={authTokenMissing:"鏈壘鍒拌獚璀変护鐗�",checkingDeviceStatus:"妾㈡煡瑷倷鐙�鎱�...",deviceOfflineOrUnresponsive:"瑷倷涓嶅瓨鍦ㄦ垨涓嶇窔涓�",unableToGetDeviceStatus:"鐒℃硶鐛插彇瑷倷鐙�鎱�",initializingTransferService:"鍒濆鍖栧偝杓告湇鍕�...",initializeTransferFailed:"鍒濆鍖栧偝杓告湇鍕欏け鏁�: {error}",uploadingFile:"涓婂偝鏂囦欢涓�...",uploadFailed:"涓婂偝鏂囦欢澶辨晽",settingDeviceDownloadUrl:"瑷疆瑷倷涓嬭級URL...",setDownloadUrlFailed:"瑷疆涓嬭級URL澶辨晽: {error}",rebootingDevice:"閲嶅暉瑷倷...",waitingForDeviceReboot:"绛夊緟瑷倷閲嶅暉...",deviceRebootTimeout:"绛夊緟瑷倷閲嶅暉瓒呮檪锛�60绉掞級",deviceReady:"璁惧灏辩华锛岃绛夊緟绋嬪簭鐑у綍",startingFileTransfer:"闁嬪妾旀鍌宠几...",flashCompleted:"绋嬪紡鐕掗寗涓紝璜嬬暀鎰忓浐浠剁殑鐕掗寗鐙�鎱嬶紒",onlineFlashFailed:"绶氫笂鐕掗寗澶辨晽: {error}"},Ch={online:"绶氫笂",offline:"闆㈢窔",checking:"妾㈡煡涓�...",flashing:"鐕掗寗涓�...",ready:"灏辩窉",chip:"鏅剁墖",board:"闁嬬櫦鏉�",flash:"Flash",screen:"铻㈠箷",signal:{unknown:"鏈煡",strong:"寮�",medium:"涓瓑",weak:"寮�",none:"鐒¤▕铏�"}},lh={title:"姝ラ 1: 鏅剁墖鍨嬭櫉鍜岃灑骞曢厤缃�",loadingFromDevice:"姝e湪寰炶ō鍌欒畝鍙栫‖楂旈厤缃�...",loadedFromDevice:"宸插緸瑷倷鑷嫊璁�鍙栫‖楂旈厤缃�",manualConfig:"璜嬫墜鍕曢厤缃‖楂斿弮鏁�",loadingTitle:"姝e湪杓夊叆瑷倷閰嶇疆...",loadingDesc:"寰炶ō鍌欒畝鍙栨櫠鐗囧瀷铏熷拰铻㈠箷瑙f瀽搴�",autoLoadedTitle:"瑷倷閰嶇疆宸茶嚜鍕曡級鍏�",chipModel:"鏅剁墖鍨嬭櫉",screenWidth:"铻㈠箷瀵害",screenHeight:"铻㈠箷楂樺害",colorFormat:"椤忚壊鏍煎紡:",manualEdit:"鎵嬪嫊淇敼閰嶇疆",collapseManual:"鏀惰捣鎵嬪嫊閰嶇疆",manualConfigTitle:"鎵嬪嫊閰嶇疆纭珨鍙冩暩",next:"涓嬩竴姝�",selectChip:"璜嬮伕鎿囨櫠鐗�",chipRequired:"鏅剁墖鍨嬭櫉",widthRequired:"铻㈠箷瀵害",heightRequired:"铻㈠箷楂樺害",loadingError:"杓夊叆閰嶇疆鏅傜櫦鐢熼尟瑾�",deviceOffline:"瑷倷鏈�f帴鎴栭洟绶�",chipModelError:"鐒℃硶鐛插彇鏅剁墖鍨嬭櫉锛岃珛鎵嬪嫊杓稿叆",resolutionError:"鐒℃硶鐛插彇铻㈠箷瑙f瀽搴︼紝璜嬫墜鍕曡几鍏�",timeoutError:"杓夊叆瑷倷閰嶇疆瓒呮檪锛岃珛鎵嬪嫊杓稿叆",manualConfigDesc:"鐒℃硶鑷嫊璁�鍙栬ō鍌欓厤缃�",manualConfigHint:"瑷倷鏈�f帴鎴栭洟绶氾紝璜嬫墜鍕曡几鍏ョ‖楂旈厤缃�",manualConfigRequired:"璜嬫墜鍕曡几鍏ョ‖楂旈厤缃�",resolutionFormatError:"铻㈠箷瑙f瀽搴︽牸寮忛尟瑾�"},Bh={title:"姝ラ 2: 涓婚瑷▓",description:"閰嶇疆鍠氶啋瑭炪�佸瓧楂斻�佽〃鎯呭拰鑳屾櫙渚嗚嚜瑷傛偍鐨勫皬鏅篈I涓婚銆�",previous:"涓婁竴姝�",next:"涓嬩竴姝�",tabs:{wakeword:"鍠氶啋瑭為厤缃�",font:"瀛楅珨閰嶇疆",emoji:"琛ㄦ儏闆嗗悎",background:"鑱婂ぉ鑳屾櫙"}},Qh={title:"姝ラ 3: 鏁堟灉闋愯",description:"闋愯鎮ㄧ殑鑷▊閰嶇疆鍦ㄥ闅涜ō鍌欎笂鐨勬晥鏋溿��",devicePreview:"瑷倷闋愯 (1:1 鍍忕礌姣斾緥)",previewSettings:"闋愯瑷畾",previewText:"闋愯鏂囧瓧",defaultPreviewText:"浣犲ソ锛屾垜鏄綘鐨勬湅鍙嬪皬鏅猴紒",currentEmotion:"鐣跺墠琛ㄦ儏",themeMode:"涓婚妯″紡",lightMode:"娣鸿壊",darkMode:"娣辫壊",configSummary:"閰嶇疆鎽樿",wakeword:"鍠氶啋瑭�:",font:"瀛楅珨:",loading:"杓夊叆涓�...",emotion:"琛ㄦ儏:",skin:"鐨啔:",previous:"涓婁竴姝�",generate:"鐢熸垚 assets.bin",emotions:{neutral:"闋愯ō",happy:"闁嬪績",laughing:"澶х瑧",funny:"鎼炵瑧",sad:"鍌峰績",angry:"鐢熸埃",crying:"鍝常",loving:"鍠滄剾",embarrassed:"灏峰艾",surprised:"椹氳",shocked:"闇囬",thinking:"鎬濊��",winking:"鐪ㄧ溂",cool:"閰风偒",relaxed:"鏀鹃瑔",delicious:"缇庡懗",kissy:"椋涘惢",confident:"鑷俊",sleepy:"鐫忓��",silly:"瑾跨毊",confused:"鍥版儜"},noEmotionConfigured:"鏈厤缃〃鎯�",fontLoading:"瀛楅珨杓夊叆涓�...",configureEmojiFirst:"璜嬪厛鍦ㄤ富椤岃ō瑷堜腑閰嶇疆琛ㄦ儏鍖�",customFont:"鑷▊瀛楅珨 {size}px",customEmoji:"鑷▊琛ㄦ儏 {count}寮�",notConfigured:"鏈厤缃�",skinLight:"娣鸿壊{type}",skinDark:"娣辫壊{type}",hideSubtitle:"闅辫棌瀛楀箷:",image:"鍦栫墖",color:"椤忚壊"},ch={title:"鑱婂ぉ鑳屾櫙閰嶇疆",description:"閰嶇疆娣鸿壊妯″紡鍜屾繁鑹叉ā寮忕殑鑱婂ぉ鑳屾櫙锛屾敮鎻寸磾鑹叉垨鑷▊鍦栫墖銆�",lightMode:"娣鸿壊妯″紡鑳屾櫙",darkMode:"娣辫壊妯″紡鑳屾櫙",solidBackground:"绱旇壊鑳屾櫙",imageBackground:"鍦栫墖鑳屾櫙",backgroundColor:"鑳屾櫙椤忚壊:",textColor:"鏂囧瓧椤忚壊:",backgroundImage:"鑳屾櫙鍦栫墖:",clickOrDragToUpload:"榛炴搳鎴栨嫋鎷戒笂鍌宠儗鏅湒鐗�",removeImage:"绉婚櫎鍦栫墖",backgroundPreview:"鑳屾櫙闋愯",lightModePreview:"娣鸿壊妯″紡",darkModePreview:"娣辫壊妯″紡",chatArea:"鑱婂ぉ鍏у鍗�鍩�",quickConfig:"蹇嵎閰嶇疆",defaultColors:"闋愯ō閰嶈壊",stoneTexture:"鐭冲ⅷ璩劅",sunnyColors:"鏆栭櫧閰嶈壊",skyBlue:"澶╃┖钘嶈",romanticPink:"娴极绮夎壊",selectValidImage:"璜嬮伕鎿囨湁鏁堢殑鍦栫墖妾旀"},Eh={title:"琛ㄦ儏闆嗗悎閰嶇疆",description:"閬告搰闋愯ō琛ㄦ儏鍖呮垨鑷畾缇╄〃鎯呭湒鐗囥�傛瘡鍊嬭〃鎯呭寘鍖呭惈21绋笉鍚屾儏绶掔殑琛ㄦ儏銆�",noEmojiPack:"鐒¤〃鎯呭寘",noEmojiPackDescription:"涓嶉厤缃〃鎯呭寘锛屾墦鍖呮檪鍙寘鍚瓧楂斿拰鑳屾櫙璩囨簮銆�",presetEmojiPack:"闋愯ō琛ㄦ儏鍖�",customEmojiPack:"鑷畾缇╄〃鎯呭寘",selectPresetEmojiPack:"閬告搰闋愯ō琛ㄦ儏鍖�",twemoji64:"Twemoji 64脳64",twemoji32:"Twemoji 32脳32",twemojiDescription:"渚嗚嚜Twitter鐨勯珮鍝佽唱琛ㄦ儏绗﹁櫉闆嗭紝閬╁悎娓呮櫚椤ず",twitterEmojiName:"Twemoji {size}x{size}",twitterEmojiDescription:"Twitter琛ㄦ儏鍖咃紝{size}脳{size}鍍忕礌",notoEmojiName:"Noto Emoji {size}x{size}",notoEmojiDescription:"Google Noto 鍕曠暙琛ㄦ儏鍖咃紝{size}脳{size}鍍忕礌锛堥渶瑕� PSRAM锛�",size:"灏哄",preview:"闋愯",select:"閬告搰",customEmojiTitle:"鑷畾缇╄〃鎯呭寘",customEmojiDescription:"涓婂偝鎮ㄨ嚜宸辩殑琛ㄦ儏鍦栫墖銆傛瘡绋儏绶掗渶瑕佷竴寮靛湒鐗囥��",uploadInstructions:"榛炴搳鎴栨嫋鎷戒笂鍌宠〃鎯呭湒鐗� (PNG/JPG/WEBP)",supportedFormats:"鏀彺鏍煎紡: PNG, JPG, WEBP銆傛帹钖﹀昂瀵�: 64脳64鍍忕礌",emotionMapping:"鎯呯窉鏄犲皠",uploadAllEmotions:"璜嬬偤鎵�鏈夋儏绶掍笂鍌宠〃鎯呭湒鐗�",maxImageWidth:"鏈�澶у湒鐗囧搴� (px)",maxImageHeight:"鏈�澶у湒鐗囬珮搴� (px)",clickToUpload:"榛炴搳涓婂偝",orDragDrop:"鎴栨嫋鎷戒笂鍌�",clickToUploadOrDrag:"榛炴搳涓婂偝鎴栨嫋鎷藉埌姝よ檿",remove:"绉婚櫎",replace:"鏇挎彌",selectValidEmojiImage:"璜嬮伕鎿囨湁鏁堢殑琛ㄦ儏鍦栫墖妾旀",selectValidFormat:"璜嬮伕鎿囨湁鏁堢殑PNG鎴朑IF鏍煎紡鍦栫墖",maxFileSize:"妾旀澶у皬涓嶈兘瓒呴亷1MB",invalidFormat:"涓嶆敮鎻寸殑鍦栫墖鏍煎紡銆傝珛浣跨敤PNG銆丣PG鎴朩EBP銆�",required:"蹇呴渶",uploadEmojiImages:"涓婂偝琛ㄦ儏鍦栫墖锛圙IF 闇�瑕� PSRAM锛�",customEmojiPackConfig:"鑷畾缇╄〃鎯呭寘閰嶇疆",neutralRequiredNotice:"* 蹇呴爤涓婂偝 neutral 闋愯ō琛ㄦ儏锛屽叾浠栬〃鎯呭彲閬搞�傚鏋滀笉涓婂偝鍏朵粬琛ㄦ儏锛屽皣浣跨敤闋愯ō琛ㄦ儏浠f浛銆�",sharedFileMessage:"琛ㄦ儏 {emotionKey} 浣跨敤浜嗚垏 {existingEmotions} 鐩稿悓鐨勫湒鐗囨獢妗堬紙鍏变韩鍎插瓨锛�",fileDeleted:"妾旀 {fileHash} 宸插埅闄わ紙鐒″叾浠栬〃鎯呭紩鐢級",fileRetained:"妾旀 {fileHash} 浠嶈鍏朵粬琛ㄦ儏浣跨敤锛屼繚鐣欐獢妗�",imageLoadFailed:"琛ㄦ儏鍦栫墖杓夊叆澶辨晽",emotions:{neutral:"闋愯ō",happy:"闁嬪績",laughing:"澶х瑧",funny:"鎼炵瑧",sad:"鍌峰績",angry:"鐢熸埃",crying:"鍝常",loving:"鍠滄剾",embarrassed:"灏峰艾",surprised:"椹氳",shocked:"闇囬",thinking:"鎬濊��",winking:"鐪ㄧ溂",cool:"閰风偒",relaxed:"鏀鹃瑔",delicious:"缇庡懗",kissy:"椋涘惢",confident:"鑷俊",sleepy:"鐫忓��",silly:"瑾跨毊",confused:"鍥版儜"}},uh={title:"瀛楅珨閰嶇疆",description:"閬告搰闋愯ō瀛楅珨鎴栦笂鍌宠嚜瑷傚瓧楂旀獢妗堛��",noFonts:"鐒″瓧楂�",noFontsDescription:"涓嶄娇鐢ㄥ瓧楂旈厤缃紝瑁濈疆灏囦娇鐢ㄩ爯瑷瓧楂�",presetFonts:"闋愯ō瀛楅珨",customFonts:"鑷▊瀛楅珨",selectPresetFont:"閬告搰闋愯ō瀛楅珨",fontSize:"瀛楄櫉:",bitDepth:"浣嶅厓娣卞害:",charset:"瀛楀厓闆�:",fileSize:"妾旀澶у皬:",customFontConfig:"鑷▊瀛楅珨閰嶇疆",fontFile:"瀛楅珨妾旀",clickSelectFontFile:"榛炴搳閬告搰瀛楅珨妾旀",orDragFileHere:"鎴栨嫋鎷芥獢妗堝埌姝よ檿",supportedFormats:"鏀彺 TTF, WOFF, WOFF2 鏍煎紡",removeFile:"绉婚櫎妾旀",fontSizePx:"瀛楄櫉 (px)",range:"绡勫湇:",bitDepthBpp:"浣嶅厓娣卞害 (BPP)",monochrome:"1 bpp (鍠壊)",fourColors:"2 bpp (4鑹�)",sixteenColors:"4 bpp (16鑹�)",characterSet:"瀛楀厓闆�",deepseekR1:"DeepSeek R1 (7405瀛楀厓)",gb2312:"GB2312 (7445瀛楀厓)",qwen18409:"Qwen (18409瀛楀厓)",latin1:"Latin1 (190瀛楀厓)",fullCharset:"瀹屾暣瀛楀厓闆� (瀛楅珨妾旀鐨勬墍鏈夊瓧鍏�)",hideSubtitle:"闅辫棌瀛楀箷",hideSubtitleDescription:"灏嶈┍鏅備笉椤ず鍗虫檪鏂囧瓧瀛楀箷",selectValidFontFile:"璜嬮伕鎿囨湁鏁堢殑瀛楅珨妾旀 (TTF, WOFF, WOFF2)",presetFontNames:{font_puhui_deepseek_14_1:"闃块噷宸村反鏅儬楂� 14px",font_puhui_deepseek_16_4:"闃块噷宸村反鏅儬楂� 16px",font_puhui_deepseek_20_4:"闃块噷宸村反鏅儬楂� 20px",font_puhui_deepseek_30_4:"闃块噷宸村反鏅儬楂� 30px",font_noto_qwen_14_1:"鎬濇簮榛戦珨 14px",font_noto_qwen_16_4:"鎬濇簮榛戦珨 16px",font_noto_qwen_20_4:"鎬濇簮榛戦珨 20px",font_noto_qwen_30_4:"鎬濇簮榛戦珨 30px"},charsetDeepseek:"DeepSeek 7405",charsetQwen:"Qwen 18409"},fh={app:eh,common:th,header:ih,configNotice:oh,steps:nh,generateModal:rh,progressSteps:gh,wakewordConfig:sh,errors:ah,flashProgress:Ih,device:Ch,chipConfig:lh,themeDesign:Bh,generateSummary:Qh,backgroundConfig:ch,emojiConfig:Eh,fontConfig:uh},hh={title:"灏忔櫤 AI 銈偣銈裤優銈ゃ偤"},dh={cancel:"銈儯銉炽偦銉�",confirm:"纰鸿獚",close:"闁夈仒銈�",start:"闁嬪",download:"銉�銈︺兂銉兗銉�",generate:"鐢熸垚",reset:"銉偦銉冦儓",yes:"銇亜",no:"銇勩亜銇�",loading:"瑾伩杈笺伩涓�...",error:"銈ㄣ儵銉�",success:"鎴愬姛",warning:"璀﹀憡",info:"鎯呭牨",next:"娆°伕",previous:"鍓嶃伕",finish:"瀹屼簡",retry:"鍐嶈│琛�",ok:"OK",other:"銇濄伄浠�",back:"鎴汇倠"},ph={title:"灏忔櫤 AI 銈偣銈裤優銈ゃ偤"},mh={title:"淇濆瓨銇曘倢銇熻ō瀹氥倰妞滃嚭",message:"瑷畾銇岃嚜鍕曠殑銇京鍏冦仌銈屻伨銇椼仧銆備腑鏂仐銇熴仺銇撱倣銇嬨倝缍氳銇欍倠銇嬨�佹渶鍒濄亱銈夈倓銈婄洿銇欍亾銇ㄣ亴銇с亶銇俱仚銆�",restart:"鏈�鍒濄亱銈夈倓銈婄洿銇�"},yh={chip:"銉併儍銉楄ō瀹�",theme:"銉嗐兗銉炪儑銈躲偆銉�",generate:"銉椼儸銉撱儱銉硷紗鐢熸垚"},wh={title:"assets.bin銈掔敓鎴�",confirmConfig:"瑷畾銈掔⒑瑾嶃仐銇︺亸銇犮仌銇�",chipModel:"銉併儍銉椼儮銉囥儷:",resolution:"瑙e儚搴�:",wakeword:"銈︺偋銈ゃ偗銉兗銉�:",font:"銉曘偐銉炽儓:",emoji:"绲垫枃瀛椼儜銉冦偗:",fileList:"鍚伨銈屻倠銉曘偂銈ゃ儷涓�瑕�",estimated:"鎺ㄥ畾",compressed:"鍦х府鍓�",generating:"assets.bin銈掔敓鎴愪腑...",progress:"{progress}% 瀹屼簡",completed:"assets.bin銉曘偂銈ゃ儷銇簴鍌欍亴銇с亶銇俱仐銇�",filename:"銉曘偂銈ゃ儷鍚�: assets.bin",fileSize:"銉曘偂銈ゃ儷銈点偆銈�: {size}",generationTime:"鐢熸垚鏅傞枔: {time}",downloadAssets:"assets.bin銈掋儉銈︺兂銉兗銉�",flashToDevice:"銉囥儛銈ゃ偣銇偑銉炽儵銈ゃ兂鏇搞亶杈笺伩",flashing:"銉囥儛銈ゃ偣銇浉銇嶈炯銇夸腑...",flashingCancel:"鏇搞亶杈笺伩銈掋偔銉c兂銈汇儷",flashingError:"鏇搞亶杈笺伩銈ㄣ儵銉�",flashingInProgress:"鏇搞亶杈笺伩涓�併偊銈c兂銉夈偊銈掗枆銇樸仾銇勩仹銇忋仩銇曘亜",customFont:"銈偣銈裤儬銉曘偐銉炽儓",customEmoji:"銈偣銈裤儬绲垫枃瀛椼儜銉冦偗",notConfigured:"鏈ō瀹�",wakewordNames:{wn9s_hilexin:"Hi, Espressif",wn9s_hiesp:"Hi, ESP",wn9s_nihaoxiaozhi:"銇撱倱銇仭銇皬鏅�",wn9_nihaoxiaozhi_tts:"銇撱倱銇仭銇皬鏅�",wn9_alexa:"銈€儸銈偟"}},Dh={init:"銈搞偋銉嶃儸銉笺偪銉笺倰鍒濇湡鍖�",font:"銉曘偐銉炽儓銉曘偂銈ゃ儷銈掑嚘鐞�",wakeword:"銈︺偋銈ゃ偗銉兗銉夈儮銉囥儷銈掋儜銉冦偙銉笺偢鍖�",emoji:"绲垫枃瀛楃敾鍍忋倰鍑︾悊",background:"鑳屾櫙鐢诲儚銈掑嚘鐞�",index:"銈ゃ兂銉囥儍銈偣銉曘偂銈ゃ儷銈掔敓鎴�",spiffs:"SPIFFS銉炪儍銉斻兂銈般倰妲嬬瘔",package:"銉戙儍銈便兗銈稿寲銈掑畬浜�"},kh={title:"銈︺偋銈ゃ偗銉兗銉夎ō瀹�",description:"銉椼儶銈汇儍銉堛伄銈︺偋銈ゃ偗銉兗銉夈倰閬告姙銇欍倠銇嬨�併偝銉炪兂銉夎獚璀樸儮銉囥儷銈掍娇鐢ㄣ仐銇︺偒銈广偪銉炪偆銈恒仐銇俱仚銆�",supportWakeNet9:"銉併儍銉椼伅WakeNet9銉€儑銉倰銈点儩銉笺儓銇椼仸銇勩伨銇欍��",supportWakeNet9s:"銉併儍銉椼伅WakeNet9s銉€儑銉倰銈点儩銉笺儓銇椼仸銇勩伨銇欍��",notSupported:"銉併儍銉椼伅銈︺偋銈ゃ偗銉兗銉夋鑳姐倰銈点儩銉笺儓銇椼仸銇勩伨銇涖倱銆�",notice:"娉ㄦ剰:",unsupportedMessage:"{chipModel}銉併儍銉椼伅銈︺偋銈ゃ偗銉兗銉夋鑳姐倰銈点儩銉笺儓銇椼仸銇勩伨銇涖倱銆傘偊銈с偆銈儻銉笺儔銈掍娇鐢ㄣ仚銈嬪繀瑕併亴銇傘倠鍫村悎銇�丒SP32-S3銆丒SP32-P4銆併伨銇熴伅ESP32-C3/C5/C6銉併儍銉椼倰閬告姙銇椼仸銇忋仩銇曘亜銆�",noWakeword:"璧峰嫊銈掔劇鍔广伀銇欍倠",presetWakeword:"銉椼儶銈汇儍銉�",customWakeword:"銈偣銈裤儬",selectWakeword:"銈︺偋銈ゃ偗銉兗銉夈倰閬告姙",placeholder:"閬告姙銇椼仸銇忋仩銇曘亜...",customSettings:"銈偣銈裤儬銈︺偋銈ゃ偗銉兗銉夎ō瀹�",wakewordName:"銈︺偋銈ゃ偗銉兗銉夊悕",wakewordNamePlaceholder:"渚嬶細灏忔櫤 / Potato",wakewordCommand:"銈︺偋銈ゃ偗銈炽優銉炽儔",wakewordCommandPlaceholderCN:"銉斻兂銈ゃ兂銈掋偣銉氥兗銈广仹鍖哄垏銈嬨�備緥锛歯i hao xiao zhi",wakewordCommandPlaceholderEN:"鑻辫獮銇崢瑾炪�傚彞瑾偣銇惈銈併仾銇勩�備緥锛欻ELLO POTATO",threshold:"鎰熷害銇椼亶銇勫�� (0-100)",thresholdDesc:"鍊ゃ亴灏忋仌銇勩伝銇╂劅搴︺亴楂樸亸銇倞銇俱仚銆傘儑銉曘偐銉儓銇�20",duration:"瑾嶈瓨鏅傞枔",durationDesc:"銈炽優銉炽儔瑾嶈瓨銇渶澶х稒缍氭檪闁�",selectModel:"瑾嶈瓨銉€儑銉倰閬告姙",mn6cn:"MultiNet6 (涓浗瑾�)",mn6en:"MultiNet6 (鑻辫獮)",mn7cn:"MultiNet7 (涓浗瑾�)",mn7en:"MultiNet7 (鑻辫獮)",errors:{nameRequired:"銈︺偋銈ゃ偗銉兗銉夊悕銈掑叆鍔涖仐銇︺亸銇犮仌銇�",commandRequired:"銈︺偋銈ゃ偗銈炽優銉炽儔銈掑叆鍔涖仐銇︺亸銇犮仌銇�",noPunctuation:"銈炽優銉炽儔銇彞瑾偣銈掑惈銈併倠銇撱仺銇仹銇嶃伨銇涖倱",durationRange:"鏅傞枔銇�500銆�10000ms銇枔銇цō瀹氥仐銇︺亸銇犮仌銇�"},tips:{tipLabel:"銉掋兂銉�:",optional:"銈︺偋銈ゃ偗銉兗銉夈伅銈儣銈枫儳銉炽仹銇欍�傜劇鍔广伀銇欍倠銇ㄣ�併儑銉愩偆銈广伅闊冲0銇ц捣鍕曘仹銇嶃仾銇忋仾銈娿伨銇欍��",wakeNet9sOnly:"C3/C5/C6銉併儍銉椼伅WakeNet9s杌介噺銉€儑銉仐銇嬨偟銉濄兗銉堛仐銇︺亜銇俱仜銈撱��",wakeNet9Full:"S3/P4銉併儍銉椼伅瀹屽叏銇猈akeNet9銉€儑銉倰銈点儩銉笺儓銇椼�併倛銈婂銇忋伄閬告姙鑲€亴銇傘倞銇俱仚銆�",customSupport:"銈偣銈裤儬銈︺偋銈ゃ偗銉兗銉夈伅MultiNet銉€儑銉倰浣跨敤銇椼仸銇娿倞銆佺従鍦ㄣ伅ESP32-S3銉併儍銉椼伄銇裤倰銈点儩銉笺儓銇椼仸銇勩伨銇欍��"}},vh={resetFailed:"銉偦銉冦儓銇け鏁椼仐銇俱仐銇熴�併儦銉笺偢銈掓洿鏂般仐銇﹀啀瑭﹁銇椼仸銇忋仩銇曘亜",generationFailed:"鐢熸垚銇け鏁椼仐銇俱仐銇�: {error}",noFileToDownload:"銉�銈︺兂銉兗銉夊彲鑳姐仾銉曘偂銈ゃ儷銇屻亗銈娿伨銇涖倱",deviceOffline:"銉囥儛銈ゃ偣銇屻偑銉曘儵銈ゃ兂銇ф浉銇嶈炯銇裤仹銇嶃伨銇涖倱",fileTooLarge:"鏇搞亶杈笺伩銇け鏁椼仐銇俱仐銇�: 銉曘偂銈ゃ儷銈点偆銈簕fileSize}MB銇宎ssets銉戙兗銉嗐偅銈枫儳銉炽偟銈ゃ偤{partitionSize}MB銈掕秴銇堛仸銇勩伨銇欍�傝ō瀹氬唴瀹广倰娓涖倝銇欍亱銆併倛銈婂ぇ瀹归噺銇儑銉愩偆銈广倰浣跨敤銇椼仸銇忋仩銇曘亜銆�",flashCancelConfirm:"鏇搞亶杈笺伩銈掋偔銉c兂銈汇儷銇椼仸銈傘倛銈嶃仐銇勩仹銇欍亱锛�",oldDataCleared:`鍙ゃ亜銉愩兗銈搞儳銉炽伄绲垫枃瀛椼儑銉笺偪妲嬮�犮亴妞滃嚭銇曘倢銆併偗銉偄銇曘倢銇俱仐銇熴��
+
+鏂般仐銇勩儛銉笺偢銉с兂銇儠銈°偆銉噸瑜囬櫎鍘绘妧琛撱倰浣跨敤銇椼仸銈广儓銉兗銈搞偣銉氥兗銈广倰绡�绱勩仐銇俱仚銆�
+
+銈偣銈裤儬绲垫枃瀛楃敾鍍忋倰鍐嶃偄銉冦儣銉兗銉夈仐銇︺亸銇犮仌銇勩�俙,incompatibleEmojiData:"浜掓彌鎬с伄銇亜绲垫枃瀛椼儑銉笺偪銈掓鍑恒仐銇俱仐銇熴�佸啀瑷畾銇椼仸銇忋仩銇曘亜"},Fh={authTokenMissing:"瑾嶈銉堛兗銈兂銇岃銇ゃ亱銈娿伨銇涖倱",checkingDeviceStatus:"銉囥儛銈ゃ偣銇姸鎱嬨倰纰鸿獚涓�...",deviceOfflineOrUnresponsive:"銉囥儛銈ゃ偣銇屽瓨鍦ㄣ仐銇亜銇嬨�併偑銉炽儵銈ゃ兂銇с伅銇傘倞銇俱仜銈�",unableToGetDeviceStatus:"銉囥儛銈ゃ偣銇姸鎱嬨倰鍙栧緱銇с亶銇俱仜銈�",initializingTransferService:"杌㈤�併偟銉笺儞銈广倰鍒濇湡鍖栦腑...",initializeTransferFailed:"杌㈤�併偟銉笺儞銈广伄鍒濇湡鍖栥伀澶辨晽銇椼伨銇椼仧: {error}",uploadingFile:"銉曘偂銈ゃ儷銈掋偄銉冦儣銉兗銉変腑...",uploadFailed:"銉曘偂銈ゃ儷銇偄銉冦儣銉兗銉夈伀澶辨晽銇椼伨銇椼仧",settingDeviceDownloadUrl:"銉囥儛銈ゃ偣銉�銈︺兂銉兗銉塙RL銈掕ō瀹氫腑...",setDownloadUrlFailed:"銉�銈︺兂銉兗銉塙RL銇ō瀹氥伀澶辨晽銇椼伨銇椼仧: {error}",rebootingDevice:"銉囥儛銈ゃ偣銈掑啀璧峰嫊涓�...",waitingForDeviceReboot:"銉囥儛銈ゃ偣銇啀璧峰嫊銈掑緟姗熶腑...",deviceRebootTimeout:"銉囥儛銈ゃ偣銇啀璧峰嫊銈裤偆銉犮偄銈︺儓锛�60绉掞級",deviceReady:"銉囥儛銈ゃ偣銇屾簴鍌欍仹銇嶃伨銇椼仧銆傘儣銉偘銉┿儬銇浉銇嶈炯銇裤倰銇婂緟銇°亸銇犮仌銇�",startingFileTransfer:"銉曘偂銈ゃ儷杌㈤�併倰闁嬪涓�...",flashCompleted:"銉椼儹銈般儵銉犮伄鏇搞亶杈笺伩涓�併儠銈°兗銉犮偊銈с偄銇浉銇嶈炯銇跨姸鎱嬨伀娉ㄦ剰銇椼仸銇忋仩銇曘亜锛�",onlineFlashFailed:"銈兂銉┿偆銉虫浉銇嶈炯銇裤伀澶辨晽銇椼伨銇椼仧: {error}"},Sh={online:"銈兂銉┿偆銉�",offline:"銈儠銉┿偆銉�",checking:"纰鸿獚涓�...",flashing:"鏇搞亶杈笺伩涓�...",ready:"婧栧倷瀹屼簡",chip:"銉併儍銉�",board:"銉溿兗銉�",flash:"Flash",screen:"鐢婚潰",signal:{unknown:"涓嶆槑",strong:"寮�",medium:"涓�",weak:"寮�",none:"淇″彿銇仐"}},Rh={title:"銈广儐銉冦儣1: 銉併儍銉椼儮銉囥儷銇ㄧ敾闈㈣ō瀹�",loadingFromDevice:"銉囥儛銈ゃ偣銇嬨倝銉忋兗銉夈偊銈с偄瑷畾銈掕銇胯炯銇夸腑...",loadedFromDevice:"銉囥儛銈ゃ偣銇嬨倝銉忋兗銉夈偊銈с偄瑷畾銇岃嚜鍕曡銇胯炯銇裤仌銈屻伨銇椼仧",manualConfig:"銉忋兗銉夈偊銈с偄銉戙儵銉°兗銈裤倰鎵嬪嫊銇цō瀹氥仐銇︺亸銇犮仌銇�",loadingTitle:"銉囥儛銈ゃ偣瑷畾銈掕銇胯炯銇夸腑...",loadingDesc:"銉囥儛銈ゃ偣銇嬨倝銉併儍銉椼儮銉囥儷銇ㄧ敾闈㈣В鍍忓害銈掕銇垮彇銈婁腑",autoLoadedTitle:"銉囥儛銈ゃ偣瑷畾銇岃嚜鍕曡銇胯炯銇裤仌銈屻伨銇椼仧",chipModel:"銉併儍銉椼儮銉囥儷",screenWidth:"鐢婚潰骞�",screenHeight:"鐢婚潰楂�",colorFormat:"鑹插舰寮�:",manualEdit:"鎵嬪嫊瑷畾",collapseManual:"鎵嬪嫊瑷畾銈掓姌銈娿仧銇熴個",manualConfigTitle:"鎵嬪嫊銉忋兗銉夈偊銈с偄瑷畾",next:"娆°伕",selectChip:"銉併儍銉椼倰閬告姙",chipRequired:"銉併儍銉椼儮銉囥儷",widthRequired:"鐢婚潰骞�",heightRequired:"鐢婚潰楂�",loadingError:"瑷畾瑾伩杈笺伩銈ㄣ儵銉�",deviceOffline:"銉囥儛銈ゃ偣銇屾帴缍氥仌銈屻仸銇勩仾銇勩亱銈儠銉┿偆銉炽仹銇�",chipModelError:"銉併儍銉椼儮銉囥儷銈掑彇寰椼仹銇嶃伨銇涖倱銆佹墜鍕曘仹鍏ュ姏銇椼仸銇忋仩銇曘亜",resolutionError:"鐢婚潰瑙e儚搴︺倰鍙栧緱銇с亶銇俱仜銈撱�佹墜鍕曘仹鍏ュ姏銇椼仸銇忋仩銇曘亜",timeoutError:"瑷畾瑾伩杈笺伩銈裤偆銉犮偄銈︺儓銆佹墜鍕曘仹鍏ュ姏銇椼仸銇忋仩銇曘亜",manualConfigDesc:"銉囥儛銈ゃ偣銇ō瀹氥倰鑷嫊瑾伩鍙栥倞銇с亶銇俱仜銈�",manualConfigHint:"銉囥儛銈ゃ偣銇屾帴缍氥仌銈屻仸銇勩仾銇勩亱銈儠銉┿偆銉炽仹銇欍�併儚銉笺儔銈︺偋銈㈣ō瀹氥倰鎵嬪嫊銇у叆鍔涖仐銇︺亸銇犮仌銇�",manualConfigRequired:"銉忋兗銉夈偊銈с偄瑷畾銈掓墜鍕曘仹鍏ュ姏銇椼仸銇忋仩銇曘亜",resolutionFormatError:"鐢婚潰瑙e儚搴﹀舰寮忋偍銉┿兗"},bh={title:"銈广儐銉冦儣2: 銉嗐兗銉炪儑銈躲偆銉�",description:"銈︺偋銈ゃ偗銉兗銉夈�併儠銈┿兂銉堛�佺档鏂囧瓧銆佽儗鏅倰瑷畾銇椼仸銆佸皬鏅篈I銉嗐兗銉炪倰銈偣銈裤優銈ゃ偤銇椼伨銇欍��",previous:"鍓嶃伕",next:"娆°伕",tabs:{wakeword:"銈︺偋銈ゃ偗銉兗銉夎ō瀹�",font:"銉曘偐銉炽儓瑷畾",emoji:"绲垫枃瀛椼偝銉偗銈枫儳銉�",background:"銉併儯銉冦儓鑳屾櫙"}},Nh={title:"銈广儐銉冦儣3: 銉椼儸銉撱儱銉�",description:"瀹熼殯銇儑銉愩偆銈广仹銇偒銈广偪銉犺ō瀹氥伄鍔规灉銈掋儣銉儞銉ャ兗銇椼伨銇欍��",devicePreview:"銉囥儛銈ゃ偣銉椼儸銉撱儱銉硷紙1:1銉斻偗銈汇儷姣旓級",previewSettings:"銉椼儸銉撱儱銉艰ō瀹�",previewText:"銉椼儸銉撱儱銉笺儐銈偣銉�",defaultPreviewText:"銇撱倱銇仭銇�佺銇亗銇仧銇弸浜恒�併偡銉c偑銈恒兗銇с仚锛�",currentEmotion:"鐝惧湪銇劅鎯�",themeMode:"銉嗐兗銉炪儮銉笺儔",lightMode:"銉┿偆銉�",darkMode:"銉�銉笺偗",configSummary:"瑷畾姒傝",wakeword:"銈︺偋銈ゃ偗銉兗銉�:",font:"銉曘偐銉炽儓:",loading:"瑾伩杈笺伩涓�...",emotion:"鎰熸儏:",skin:"銈广偔銉�:",previous:"鍓嶃伕",generate:"assets.bin銈掔敓鎴�",emotions:{neutral:"涓珛",happy:"骞搞仜",laughing:"澶х瑧銇�",funny:"闈㈢櫧銇�",sad:"鎮层仐銇�",angry:"鎬掋倞",crying:"娉c亶",loving:"鎰涙儏",embarrassed:"鎭ャ仛銇嬨仐銇�",surprised:"椹氥亶",shocked:"銈枫儳銉冦偗",thinking:"鑰冦亪涓�",winking:"銈︺偆銉炽偗",cool:"銈兗銉�",relaxed:"銉儵銉冦偗銈�",delicious:"缇庡懗銇椼亜",kissy:"銈偣",confident:"鑷俊",sleepy:"鐪犮亜",silly:"銇般亱銇掋仧",confused:"娣蜂贡"},noEmotionConfigured:"鎰熸儏銇岃ō瀹氥仌銈屻仸銇勩伨銇涖倱",fontLoading:"銉曘偐銉炽儓瑾伩杈笺伩涓�...",configureEmojiFirst:"銉嗐兗銉炪儑銈躲偆銉炽仹绲垫枃瀛椼儜銉冦偗銈掕ō瀹氥仐銇︺亸銇犮仌銇�",customFont:"銈偣銈裤儬銉曘偐銉炽儓 {size}px",customEmoji:"銈偣銈裤儬绲垫枃瀛� {count}鐢诲儚",notConfigured:"鏈ō瀹�",skinLight:"銉┿偆銉坽type}",skinDark:"銉�銉笺偗{type}",hideSubtitle:"瀛楀箷銈掗殸銇�:",image:"鐢诲儚",color:"鑹�"},Gh={title:"銉併儯銉冦儓鑳屾櫙瑷畾",description:"銉┿偆銉堛儮銉笺儔銇ㄣ儉銉笺偗銉€兗銉夈伄銉併儯銉冦儓鑳屾櫙銈掕ō瀹氥仐銆佸崢鑹层伨銇熴伅銈偣銈裤儬鐢诲儚銈掋偟銉濄兗銉堛仐銇俱仚銆�",lightMode:"銉┿偆銉堛儮銉笺儔鑳屾櫙",darkMode:"銉�銉笺偗銉€兗銉夎儗鏅�",solidBackground:"鍗樿壊鑳屾櫙",imageBackground:"鐢诲儚鑳屾櫙",backgroundColor:"鑳屾櫙鑹�:",textColor:"鏂囧瓧鑹�:",backgroundImage:"鑳屾櫙鐢诲儚:",clickOrDragToUpload:"銈儶銉冦偗銇俱仧銇儔銉┿儍銈般仐銇﹁儗鏅敾鍍忋倰銈€儍銉椼儹銉笺儔",removeImage:"鐢诲儚銈掑墛闄�",backgroundPreview:"鑳屾櫙銉椼儸銉撱儱銉�",lightModePreview:"銉┿偆銉堛儮銉笺儔",darkModePreview:"銉�銉笺偗銉€兗銉�",chatArea:"銉併儯銉冦儓銈炽兂銉嗐兂銉勩偍銉偄",quickConfig:"銈偆銉冦偗瑷畾",defaultColors:"銉囥儠銈┿儷銉堣壊",stoneTexture:"鐭宠唱鎰�",sunnyColors:"鏅淬倢鑹�",skyBlue:"绌鸿壊",romanticPink:"銉優銉炽儊銉冦偗銉斻兂銈�",selectValidImage:"鏈夊姽銇敾鍍忋儠銈°偆銉倰閬告姙銇椼仸銇忋仩銇曘亜"},Lh={title:"绲垫枃瀛椼偝銉偗銈枫儳銉宠ō瀹�",description:"銉椼儶銈汇儍銉堢档鏂囧瓧銉戙儍銈倰閬告姙銇欍倠銇嬨�併偒銈广偪銉犵档鏂囧瓧鐢诲儚銈掋偄銉冦儣銉兗銉夈仐銇俱仚銆傚悇绲垫枃瀛椼儜銉冦偗銇伅21绋銇暟銇倠鎰熸儏琛ㄧ従銇屽惈銇俱倢銇俱仚銆�",noEmojiPack:"绲垫枃瀛椼儜銉冦偗銇仐",noEmojiPackDescription:"绲垫枃瀛椼儜銉冦偗銈掕ō瀹氥仜銇氥�併儜銉冦偙銉笺偢銇伅銉曘偐銉炽儓銇ㄨ儗鏅儶銈姐兗銈广伄銇裤倰鍚倎銇俱仚銆�",presetEmojiPack:"銉椼儶銈汇儍銉堢档鏂囧瓧銉戙儍銈�",customEmojiPack:"銈偣銈裤儬绲垫枃瀛椼儜銉冦偗",selectPresetEmojiPack:"銉椼儶銈汇儍銉堢档鏂囧瓧銉戙儍銈倰閬告姙",twemoji64:"Twemoji 64脳64",twemoji32:"Twemoji 32脳32",twemojiDescription:"Twitter銇珮鍝佽唱绲垫枃瀛椼偦銉冦儓銆侀鏄庛仾琛ㄧず銇渶閬�",twitterEmojiName:"Twemoji {size}x{size}",twitterEmojiDescription:"Twitter绲垫枃瀛椼儜銉冦偗銆亄size}脳{size}銉斻偗銈汇儷",size:"銈点偆銈�",preview:"銉椼儸銉撱儱銉�",select:"閬告姙",customEmojiTitle:"銈偣銈裤儬绲垫枃瀛椼儜銉冦偗",customEmojiDescription:"鐙嚜銇档鏂囧瓧鐢诲儚銈掋偄銉冦儣銉兗銉夈仐銇俱仚銆傚悇鎰熸儏銇�1銇ゃ伄鐢诲儚銇屽繀瑕併仹銇欍��",uploadInstructions:"銈儶銉冦偗銇俱仧銇儔銉┿儍銈般仐銇︾档鏂囧瓧鐢诲儚銈掋偄銉冦儣銉兗銉夛紙PNG/JPG/WEBP锛�",supportedFormats:"銈点儩銉笺儓褰㈠紡: PNG, JPG, WEBP銆傛帹濂ㄣ偟銈ゃ偤: 64脳64銉斻偗銈汇儷",emotionMapping:"鎰熸儏銉炪儍銉斻兂銈�",uploadAllEmotions:"銇欍伖銇︺伄鎰熸儏銇档鏂囧瓧鐢诲儚銈掋偄銉冦儣銉兗銉夈仐銇︺亸銇犮仌銇�",maxImageWidth:"鏈�澶х敾鍍忓箙锛坧x锛�",maxImageHeight:"鏈�澶х敾鍍忛珮锛坧x锛�",clickToUpload:"銈儶銉冦偗銇椼仸銈€儍銉椼儹銉笺儔",orDragDrop:"銇俱仧銇儔銉┿儍銈帮紗銉夈儹銉冦儣",clickToUploadOrDrag:"銈儶銉冦偗銇椼仸銈€儍銉椼儹銉笺儔銇俱仧銇亾銇撱伀銉夈儵銉冦偘",remove:"鍓婇櫎",replace:"缃亶鎻涖亪",selectValidEmojiImage:"鏈夊姽銇档鏂囧瓧鐢诲儚銉曘偂銈ゃ儷銈掗伕鎶炪仐銇︺亸銇犮仌銇�",selectValidFormat:"鏈夊姽銇狿NG銇俱仧銇疓IF褰㈠紡銇敾鍍忋倰閬告姙銇椼仸銇忋仩銇曘亜",maxFileSize:"銉曘偂銈ゃ儷銈点偆銈恒伅1MB銈掕秴銇堛仾銇勩仹銇忋仩銇曘亜",invalidFormat:"銈点儩銉笺儓銇曘倢銇︺亜銇亜鐢诲儚褰㈠紡銇с仚銆侾NG銆丣PG銆併伨銇熴伅WEBP銈掍娇鐢ㄣ仐銇︺亸銇犮仌銇勩��",required:"蹇呴爤",uploadEmojiImages:"绲垫枃瀛楃敾鍍忋倰銈€儍銉椼儹銉笺儔锛圙IF銇伅PSRAM銇屽繀瑕侊級",customEmojiPackConfig:"銈偣銈裤儬绲垫枃瀛椼儜銉冦偗瑷畾",neutralRequiredNotice:"* 涓珛銇儑銉曘偐銉儓鎰熸儏銈掋偄銉冦儣銉兗銉夈仚銈嬪繀瑕併亴銇傘倞銇俱仚銆佷粬銇劅鎯呫伅銈儣銈枫儳銉炽仹銇欍�傘偄銉冦儣銉兗銉夈仐銇亜鍫村悎銆併儑銉曘偐銉儓鎰熸儏銇屼唬銈忋倞銇娇鐢ㄣ仌銈屻伨銇欍��",sharedFileMessage:"鎰熸儏{emotionKey}銇瘂existingEmotions}銇ㄥ悓銇樼敾鍍忋儠銈°偆銉倰浣跨敤銇椼仸銇勩伨銇欙紙鍏辨湁銈广儓銉兗銈革級",fileDeleted:"銉曘偂銈ゃ儷{fileHash}銇屽墛闄ゃ仌銈屻伨銇椼仧锛堜粬銇劅鎯呭弬鐓с仾銇楋級",fileRetained:"銉曘偂銈ゃ儷{fileHash}銇粬銇劅鎯呫仹銇俱仩浣跨敤銇曘倢銇︺亜銈嬨仧銈併�佷繚鎸併仌銈屻伨銇�",imageLoadFailed:"绲垫枃瀛楃敾鍍忋伄瑾伩杈笺伩銇け鏁椼仐銇俱仐銇�",emotions:{neutral:"涓珛",happy:"骞搞仜",laughing:"澶х瑧銇�",funny:"闈㈢櫧銇�",sad:"鎮层仐銇�",angry:"鎬掋倞",crying:"娉c亶",loving:"鎰涙儏",embarrassed:"鎭ャ仛銇嬨仐銇�",surprised:"椹氥亶",shocked:"銈枫儳銉冦偗",thinking:"鑰冦亪涓�",winking:"銈︺偆銉炽偗",cool:"銈兗銉�",relaxed:"銉儵銉冦偗銈�",delicious:"缇庡懗銇椼亜",kissy:"銈偣",confident:"鑷俊",sleepy:"鐪犮亜",silly:"銇般亱銇掋仧",confused:"娣蜂贡"}},Uh={title:"銉曘偐銉炽儓瑷畾",description:"銉椼儶銈汇儍銉堛儠銈┿兂銉堛倰閬告姙銇欍倠銇嬨�併偒銈广偪銉犮儠銈┿兂銉堛儠銈°偆銉倰銈€儍銉椼儹銉笺儔銇椼伨銇欍��",noFonts:"銉曘偐銉炽儓銇仐",noFontsDescription:"銉曘偐銉炽儓瑷畾銈掍娇鐢ㄣ仐銇亜銆併儑銉愩偆銈广伅銉囥儠銈┿儷銉堛伄銉曘偐銉炽儓銈掍娇鐢ㄣ仐銇俱仚",presetFonts:"銉椼儶銈汇儍銉堛儠銈┿兂銉�",customFonts:"銈偣銈裤儬銉曘偐銉炽儓",selectPresetFont:"銉椼儶銈汇儍銉堛儠銈┿兂銉堛倰閬告姙",fontSize:"銉曘偐銉炽儓銈点偆銈�:",bitDepth:"銉撱儍銉堟繁搴�:",charset:"鏂囧瓧銈汇儍銉�:",fileSize:"銉曘偂銈ゃ儷銈点偆銈�:",customFontConfig:"銈偣銈裤儬銉曘偐銉炽儓瑷畾",fontFile:"銉曘偐銉炽儓銉曘偂銈ゃ儷",clickSelectFontFile:"銈儶銉冦偗銇椼仸銉曘偐銉炽儓銉曘偂銈ゃ儷銈掗伕鎶�",orDragFileHere:"銇俱仧銇儠銈°偆銉倰銇撱亾銇儔銉┿儍銈�",supportedFormats:"TTF銆乄OFF銆乄OFF2褰㈠紡銈掋偟銉濄兗銉�",removeFile:"銉曘偂銈ゃ儷銈掑墛闄�",fontSizePx:"銉曘偐銉炽儓銈点偆銈猴紙px锛�",range:"绡勫洸:",bitDepthBpp:"銉撱儍銉堟繁搴︼紙BPP锛�",monochrome:"1 bpp锛堛儮銉庛偗銉級",fourColors:"2 bpp锛�4鑹诧級",sixteenColors:"4 bpp锛�16鑹诧級",characterSet:"鏂囧瓧銈汇儍銉�",deepseekR1:"DeepSeek R1锛�7405鏂囧瓧锛�",gb2312:"GB2312锛�7445鏂囧瓧锛�",qwen18409:"Qwen锛�18409鏂囧瓧锛�",latin1:"Latin1锛�190鏂囧瓧锛�",fullCharset:"瀹屽叏銇枃瀛椼偦銉冦儓锛堛儠銈┿兂銉堛儠銈°偆銉伄銇欍伖銇︺伄鏂囧瓧锛�",hideSubtitle:"瀛楀箷銈掗殸銇�",hideSubtitleDescription:"浼氳┍涓伀銉偄銉偪銈ゃ儬瀛楀箷銈掕〃绀恒仐銇亜",selectValidFontFile:"鏈夊姽銇儠銈┿兂銉堛儠銈°偆銉倰閬告姙銇椼仸銇忋仩銇曘亜锛圱TF銆乄OFF銆乄OFF2锛�",presetFontNames:{font_puhui_deepseek_14_1:"Alibaba PuHuiTi 14px",font_puhui_deepseek_16_4:"Alibaba PuHuiTi 16px",font_puhui_deepseek_20_4:"Alibaba PuHuiTi 20px",font_puhui_deepseek_30_4:"Alibaba PuHuiTi 30px",font_noto_qwen_14_1:"Noto Sans 14px",font_noto_qwen_16_4:"Noto Sans 16px",font_noto_qwen_20_4:"Noto Sans 20px",font_noto_qwen_30_4:"Noto Sans 30px"},charsetDeepseek:"DeepSeek 7405",charsetQwen:"Qwen 18409"},Mh={app:hh,common:dh,header:ph,configNotice:mh,steps:yh,generateModal:wh,progressSteps:Dh,wakewordConfig:kh,errors:vh,flashProgress:Fh,device:Sh,chipConfig:Rh,themeDesign:bh,generateSummary:Nh,backgroundConfig:Gh,emojiConfig:Lh,fontConfig:Uh},xh={title:"Xiaozhi AI T霉y ch峄塶h"},Jh={cancel:"H峄",confirm:"X谩c nh岷璶",close:"膼贸ng",start:"B岷痶 膽岷",download:"T岷 xu峄憂g",generate:"T岷",reset:"膼岷穞 l岷",yes:"C贸",no:"Kh么ng",loading:"膼ang t岷...",error:"L峄梚",success:"Th脿nh c么ng",warning:"C岷h b谩o",info:"Th么ng tin",next:"Ti岷縫 theo",previous:"Tr瓢峄沜 膽贸",finish:"Ho脿n th脿nh",retry:"Th峄� l岷",ok:"OK",other:"Kh谩c",back:"tr峄� v峄�"},Th={title:"Xiaozhi AI T霉y ch峄塶h"},Hh={title:"Ph谩t hi峄噉 c岷 h矛nh 膽茫 l瓢u",message:"C岷 h矛nh 膽茫 膽瓢峄 kh么i ph峄 t峄� 膽峄檔g. B岷 c贸 th峄� ti岷縫 t峄 t峄� n啤i 膽茫 d峄玭g ho岷穋 b岷痶 膽岷 l岷.",restart:"B岷痶 膽岷 l岷"},Yh={chip:"C岷 h矛nh chip",theme:"Thi岷縯 k岷� ch峄� 膽峄�",generate:"Xem tr瓢峄沜 & T岷"},_h={title:"T岷 assets.bin",confirmConfig:"Vui l貌ng x谩c nh岷璶 c岷 h矛nh c峄 b岷",chipModel:"M么 h矛nh chip:",resolution:"膼峄� ph芒n gi岷:",wakeword:"T峄� 膽谩nh th峄ヽ:",font:"Ph么ng ch峄�:",emoji:"G贸i bi峄僽 t瓢峄g c岷 x煤c:",fileList:"Danh s谩ch t峄噋 膽瓢峄 bao g峄搈",estimated:"漂峄沜 t铆nh",compressed:"Tr瓢峄沜 khi n茅n",generating:"膼ang t岷 assets.bin...",progress:"{progress}% Ho脿n th脿nh",completed:"T峄噋 assets.bin c峄 b岷 膽茫 s岷祅 s脿ng",filename:"T锚n t峄噋: assets.bin",fileSize:"K铆ch th瓢峄沜 t峄噋: {size}",generationTime:"Th峄漣 gian t岷: {time}",downloadAssets:"T岷 xu峄憂g assets.bin",flashToDevice:"Flash tr峄眂 tuy岷縩 v脿o thi岷縯 b峄�",flashing:"膼ang flash v脿o thi岷縯 b峄�...",flashingCancel:"H峄 flash",flashingError:"L峄梚 flash",flashingInProgress:"膼ang flash, vui l貌ng kh么ng 膽贸ng c峄璦 s峄�",customFont:"Ph么ng ch峄� t霉y ch峄塶h",customEmoji:"G贸i bi峄僽 t瓢峄g c岷 x煤c t霉y ch峄塶h",notConfigured:"Ch瓢a c岷 h矛nh",wakewordNames:{wn9s_hilexin:"Hi, Espressif",wn9s_hiesp:"Hi, ESP",wn9s_nihaoxiaozhi:"Xin ch脿o Xiaozhi",wn9_nihaoxiaozhi_tts:"Xin ch脿o Xiaozhi",wn9_alexa:"Alexa"}},Kh={init:"Kh峄焛 t岷 tr矛nh t岷",font:"X峄� l媒 t峄噋 ph么ng ch峄�",wakeword:"膼贸ng g贸i m么 h矛nh t峄� 膽谩nh th峄ヽ",emoji:"X峄� l媒 h矛nh 岷h bi峄僽 t瓢峄g c岷 x煤c",background:"X峄� l媒 h矛nh 岷h n峄乶",index:"T岷 t峄噋 ch峄� m峄",spiffs:"X芒y d峄眓g 谩nh x岷� SPIFFS",package:"Ho脿n th脿nh 膽贸ng g贸i"},Oh={title:"C岷 h矛nh t峄� 膽谩nh th峄ヽ",description:"Ch峄峮 m么 h矛nh t峄� 膽谩nh th峄ヽ 膽岷穞 s岷祅 ho岷穋 t霉y ch峄塶h th么ng qua m么 h矛nh l峄噉h.",supportWakeNet9:"Chip c峄 b岷 h峄� tr峄� m么 h矛nh WakeNet9.",supportWakeNet9s:"Chip c峄 b岷 h峄� tr峄� m么 h矛nh WakeNet9s.",notSupported:"Chip c峄 b岷 kh么ng h峄� tr峄� ch峄ヽ n膬ng t峄� 膽谩nh th峄ヽ.",notice:"L瓢u 媒:",unsupportedMessage:"Chip {chipModel} kh么ng h峄� tr峄� ch峄ヽ n膬ng t峄� 膽谩nh th峄ヽ. N岷縰 b岷 c岷 s峄� d峄g t峄� 膽谩nh th峄ヽ, vui l貌ng ch峄峮 chip ESP32-S3, ESP32-P4 ho岷穋 ESP32-C3/C5/C6.",noWakeword:"T岷痶 膽谩nh th峄ヽ",presetWakeword:"M岷穋 膽峄媙h",customWakeword:"T霉y ch峄塶h",selectWakeword:"Ch峄峮 t峄� 膽谩nh th峄ヽ",placeholder:"Ch峄峮 m峄檛 t峄�...",customSettings:"C脿i 膽岷穞 t峄� 膽谩nh th峄ヽ t霉y ch峄塶h",wakewordName:"T锚n t峄� 膽谩nh th峄ヽ",wakewordNamePlaceholder:"VD: Xiaozhi / Potato",wakewordCommand:"L峄噉h 膽谩nh th峄ヽ",wakewordCommandPlaceholderCN:"Pinyin c谩ch nhau b岷眓g d岷 c谩ch, VD: ni hao xiao zhi",wakewordCommandPlaceholderEN:"T峄� ti岷縩g Anh, kh么ng d岷 c芒u, VD: HELLO POTATO",threshold:"Ng瓢峄g nh岷 (0-100)",thresholdDesc:"C脿ng nh峄� c脿ng nh岷, m岷穋 膽峄媙h l脿 20",duration:"Th峄漣 gian nh岷璶 di峄噉",durationDesc:"Th峄漣 gian t峄慽 膽a 膽峄� nh岷璶 di峄噉 l峄噉h",selectModel:"Ch峄峮 m么 h矛nh nh岷璶 di峄噉",mn6cn:"MultiNet6 (Ti岷縩g Trung)",mn6en:"MultiNet6 (Ti岷縩g Anh)",mn7cn:"MultiNet7 (Ti岷縩g Trung)",mn7en:"MultiNet7 (Ti岷縩g Anh)",errors:{nameRequired:"Vui l貌ng nh岷璸 t锚n t峄� 膽谩nh th峄ヽ",commandRequired:"Vui l貌ng nh岷璸 l峄噉h 膽谩nh th峄ヽ",noPunctuation:"L峄噉h kh么ng th峄� ch峄゛ d岷 c芒u",durationRange:"Th峄漣 gian ph岷 t峄� 500-10000ms"},tips:{tipLabel:"M岷筼:",optional:"T峄� 膽谩nh th峄ヽ l脿 t霉y ch峄峮. N岷縰 t岷痶, thi岷縯 b峄� s岷� kh么ng th峄� 膽谩nh th峄ヽ b岷眓g gi峄峮g n贸i.",wakeNet9sOnly:"Chip C3/C5/C6 ch峄� h峄� tr峄� m么 h矛nh nh岷� WakeNet9s.",wakeNet9Full:"Chip S3/P4 h峄� tr峄� m么 h矛nh WakeNet9 ho脿n ch峄塶h v峄沬 nhi峄乽 l峄盿 ch峄峮 h啤n.",customSupport:"T峄� 膽谩nh th峄ヽ t霉y ch峄塶h s峄� d峄g m么 h矛nh MultiNet, hi峄噉 ch峄� h峄� tr峄� chip ESP32-S3."}},Ph={resetFailed:"膼岷穞 l岷 th岷 b岷, vui l貌ng l脿m m峄沬 trang v脿 th峄� l岷",generationFailed:"T岷 th岷 b岷: {error}",noFileToDownload:"Kh么ng c贸 t峄噋 n脿o 膽峄� t岷 xu峄憂g",deviceOffline:"Thi岷縯 b峄� ngo岷 tuy岷縩 v脿 kh么ng th峄� flash",fileTooLarge:"Flash th岷 b岷: K铆ch th瓢峄沜 t峄噋 {fileSize}MB v瓢峄 qu谩 k铆ch th瓢峄沜 ph芒n v霉ng assets {partitionSize}MB. Vui l貌ng gi岷 n峄檌 dung c岷 h矛nh ho岷穋 s峄� d峄g thi岷縯 b峄� c贸 dung l瓢峄g l峄沶 h啤n.",flashCancelConfirm:"B岷 c贸 ch岷痗 ch岷痭 mu峄憂 h峄 flash?",oldDataCleared:`膼茫 ph谩t hi峄噉 v脿 x贸a c岷 tr煤c d峄� li峄噓 bi峄僽 t瓢峄g c岷 x煤c phi锚n b岷 c农.
+
+Phi锚n b岷 m峄沬 s峄� d峄g c么ng ngh峄� lo岷 b峄� tr霉ng l岷穚 t峄噋 膽峄� ti岷縯 ki峄噈 kh么ng gian l瓢u tr峄�.
+
+Vui l貌ng t岷 l岷 h矛nh 岷h bi峄僽 t瓢峄g c岷 x煤c t霉y ch峄塶h.`,incompatibleEmojiData:"Ph谩t hi峄噉 d峄� li峄噓 bi峄僽 t瓢峄g c岷 x煤c kh么ng t瓢啤ng th铆ch, vui l貌ng c岷 h矛nh l岷"},Vh={authTokenMissing:"Kh么ng t矛m th岷 m茫 th么ng b谩o x谩c th峄眂",checkingDeviceStatus:"膼ang ki峄僲 tra tr岷g th谩i thi岷縯 b峄�...",deviceOfflineOrUnresponsive:"Thi岷縯 b峄� kh么ng t峄搉 t岷 ho岷穋 kh么ng tr峄眂 tuy岷縩",unableToGetDeviceStatus:"Kh么ng th峄� l岷 tr岷g th谩i thi岷縯 b峄�",initializingTransferService:"膼ang kh峄焛 t岷 d峄媍h v峄� truy峄乶 t岷...",initializeTransferFailed:"Kh峄焛 t岷 d峄媍h v峄� truy峄乶 t岷 th岷 b岷: {error}",uploadingFile:"T岷 l锚n t岷璸 tin. .....",uploadFailed:"T岷 l锚n t岷璸 tin kh么ng th脿nh c么ng",settingDeviceDownloadUrl:"膼ang thi岷縯 l岷璸 URL t岷 xu峄憂g thi岷縯 b峄�...",setDownloadUrlFailed:"Thi岷縯 l岷璸 URL t岷 xu峄憂g th岷 b岷: {error}",rebootingDevice:"膼ang kh峄焛 膽峄檔g l岷 thi岷縯 b峄�...",waitingForDeviceReboot:"膼ang ch峄� thi岷縯 b峄� kh峄焛 膽峄檔g l岷...",deviceRebootTimeout:"H岷縯 th峄漣 gian ch峄� thi岷縯 b峄� kh峄焛 膽峄檔g l岷 (60 gi芒y)",deviceReady:"Thi岷縯 b峄� 膽茫 s岷祅 s脿ng, vui l貌ng ch峄� ch瓢啤ng tr矛nh ghi",startingFileTransfer:"膼ang b岷痶 膽岷 truy峄乶 t岷 t峄噋...",flashCompleted:"Trong qu谩 tr矛nh ghi 芒m ch瓢啤ng tr矛nh, xin l瓢u 媒 tr岷g th谩i ghi 芒m c峄 ph岷 c峄� 膽峄媙h!",onlineFlashFailed:"Flash tr峄眂 tuy岷縩 th岷 b岷: {error}"},Wh={online:"Tr峄眂 tuy岷縩",offline:"Ngo岷 tuy岷縩",checking:"膼ang ki峄僲 tra...",flashing:"膼ang flash...",ready:"S岷祅 s脿ng",chip:"Chip",board:"Bo m岷h",flash:"Flash",screen:"M脿n h矛nh",signal:{unknown:"Kh么ng x谩c 膽峄媙h",strong:"M岷h",medium:"Trung b矛nh",weak:"Y岷縰",none:"Kh么ng c贸 t铆n hi峄噓"}},qh={title:"B瓢峄沜 1: C岷 h矛nh m么 h矛nh chip v脿 m脿n h矛nh",loadingFromDevice:"膼ang t岷 c岷 h矛nh ph岷 c峄﹏g t峄� thi岷縯 b峄�...",loadedFromDevice:"C岷 h矛nh ph岷 c峄﹏g 膽瓢峄 t岷 t峄� 膽峄檔g t峄� thi岷縯 b峄�",manualConfig:"Vui l貌ng c岷 h矛nh c谩c th么ng s峄� ph岷 c峄﹏g theo c谩ch th峄� c么ng",loadingTitle:"膼ang t岷 c岷 h矛nh thi岷縯 b峄�...",loadingDesc:"膼ang 膽峄峜 m么 h矛nh chip v脿 膽峄� ph芒n gi岷 m脿n h矛nh t峄� thi岷縯 b峄�",autoLoadedTitle:"C岷 h矛nh thi岷縯 b峄� 膽瓢峄 t岷 t峄� 膽峄檔g",chipModel:"M么 h矛nh chip",screenWidth:"Chi峄乽 r峄檔g m脿n h矛nh",screenHeight:"Chi峄乽 cao m脿n h矛nh",colorFormat:"膼峄媙h d岷g m脿u:",manualEdit:"C岷 h矛nh th峄� c么ng",collapseManual:"Thu g峄峮 c岷 h矛nh th峄� c么ng",manualConfigTitle:"C岷 h矛nh ph岷 c峄﹏g th峄� c么ng",next:"Ti岷縫 theo",selectChip:"Ch峄峮 chip",chipRequired:"M么 h矛nh chip",widthRequired:"Chi峄乽 r峄檔g m脿n h矛nh",heightRequired:"Chi峄乽 cao m脿n h矛nh",loadingError:"L峄梚 t岷 c岷 h矛nh",deviceOffline:"Thi岷縯 b峄� ch瓢a k岷縯 n峄慽 ho岷穋 ngo岷 tuy岷縩",chipModelError:"Kh么ng th峄� l岷 m么 h矛nh chip, vui l貌ng nh岷璸 th峄� c么ng",resolutionError:"Kh么ng th峄� l岷 膽峄� ph芒n gi岷 m脿n h矛nh, vui l貌ng nh岷璸 th峄� c么ng",timeoutError:"H岷縯 th峄漣 gian t岷 c岷 h矛nh, vui l貌ng nh岷璸 th峄� c么ng",manualConfigDesc:"Kh么ng th峄� t峄� 膽峄檔g 膽峄峜 c岷 h矛nh thi岷縯 b峄�",manualConfigHint:"Thi岷縯 b峄� ch瓢a k岷縯 n峄慽 ho岷穋 ngo岷 tuy岷縩, vui l貌ng nh岷璸 c岷 h矛nh ph岷 c峄﹏g th峄� c么ng",manualConfigRequired:"Vui l貌ng nh岷璸 c岷 h矛nh ph岷 c峄﹏g th峄� c么ng",resolutionFormatError:"L峄梚 膽峄媙h d岷g 膽峄� ph芒n gi岷 m脿n h矛nh"},jh={title:"B瓢峄沜 2: Thi岷縯 k岷� ch峄� 膽峄�",description:"C岷 h矛nh t峄� 膽谩nh th峄ヽ, ph么ng ch峄�, bi峄僽 t瓢峄g c岷 x煤c v脿 n峄乶 膽峄� t霉y ch峄塶h ch峄� 膽峄� Xiaozhi AI c峄 b岷.",previous:"Tr瓢峄沜 膽贸",next:"Ti岷縫 theo",tabs:{wakeword:"C岷 h矛nh t峄� 膽谩nh th峄ヽ",font:"C岷 h矛nh ph么ng ch峄�",emoji:"B峄� s瓢u t岷璸 bi峄僽 t瓢峄g c岷 x煤c",background:"N峄乶 tr貌 chuy峄噉"}},Zh={title:"B瓢峄沜 3: Xem tr瓢峄沜",description:"Xem tr瓢峄沜 hi峄噓 峄﹏g c峄 c岷 h矛nh t霉y ch峄塶h tr锚n thi岷縯 b峄� th峄眂 t岷�.",devicePreview:"Xem tr瓢峄沜 thi岷縯 b峄� (t峄� l峄� pixel 1:1)",previewSettings:"C脿i 膽岷穞 xem tr瓢峄沜",previewText:"V膬n b岷 xem tr瓢峄沜",defaultPreviewText:"Xin ch脿o, t么i l脿 b岷 c峄 b岷 Xiaozhi!",currentEmotion:"C岷 x煤c hi峄噉 t岷",themeMode:"Ch岷� 膽峄� ch峄� 膽峄�",lightMode:"S谩ng",darkMode:"T峄慽",configSummary:"T贸m t岷痶 c岷 h矛nh",wakeword:"T峄� 膽谩nh th峄ヽ:",font:"Ph么ng ch峄�:",loading:"膼ang t岷...",emotion:"C岷 x煤c:",skin:"Da:",previous:"Tr瓢峄沜 膽贸",generate:"T岷 assets.bin",emotions:{neutral:"Trung l岷璸",happy:"Vui v岷�",laughing:"C瓢峄漣 l峄沶",funny:"H脿i h瓢峄沜",sad:"Bu峄搉",angry:"Gi岷璶 d峄�",crying:"Kh贸c",loving:"Y锚u th瓢啤ng",embarrassed:"X岷 h峄�",surprised:"B岷 ng峄�",shocked:"S峄慶",thinking:"Suy ngh末",winking:"Nh谩y m岷痶",cool:"Ng岷",relaxed:"Tho岷 m谩i",delicious:"Ngon mi峄噉g",kissy:"H么n",confident:"T峄� tin",sleepy:"Bu峄搉 ng峄�",silly:"Ng峄� ng岷﹏",confused:"B峄慽 r峄慽"},noEmotionConfigured:"Ch瓢a c岷 h矛nh c岷 x煤c",fontLoading:"膼ang t岷 ph么ng ch峄�...",configureEmojiFirst:"Vui l貌ng c岷 h矛nh g贸i bi峄僽 t瓢峄g c岷 x煤c trong thi岷縯 k岷� ch峄� 膽峄� tr瓢峄沜",customFont:"Ph么ng ch峄� t霉y ch峄塶h {size}px",customEmoji:"Bi峄僽 t瓢峄g c岷 x煤c t霉y ch峄塶h {count} h矛nh 岷h",notConfigured:"Ch瓢a c岷 h矛nh",skinLight:"S谩ng {type}",skinDark:"T峄慽 {type}",hideSubtitle:"岷╪ ph峄� 膽峄�:",image:"H矛nh 岷h",color:"M脿u s岷痗"},Xh={title:"C岷 h矛nh n峄乶 tr貌 chuy峄噉",description:"C岷 h矛nh n峄乶 tr貌 chuy峄噉 cho ch岷� 膽峄� s谩ng v脿 t峄慽, h峄� tr峄� m脿u 膽啤n ho岷穋 h矛nh 岷h t霉y ch峄塶h.",lightMode:"N峄乶 ch岷� 膽峄� s谩ng",darkMode:"N峄乶 ch岷� 膽峄� t峄慽",solidBackground:"N峄乶 m脿u 膽啤n",imageBackground:"N峄乶 h矛nh 岷h",backgroundColor:"M脿u n峄乶:",textColor:"M脿u ch峄�:",backgroundImage:"H矛nh 岷h n峄乶:",clickOrDragToUpload:"Nh岷 ho岷穋 k茅o 膽峄� t岷 l锚n h矛nh 岷h n峄乶",removeImage:"X贸a h矛nh 岷h",backgroundPreview:"Xem tr瓢峄沜 n峄乶",lightModePreview:"Ch岷� 膽峄� s谩ng",darkModePreview:"Ch岷� 膽峄� t峄慽",chatArea:"Khu v峄眂 n峄檌 dung tr貌 chuy峄噉",quickConfig:"C岷 h矛nh nhanh",defaultColors:"M脿u m岷穋 膽峄媙h",stoneTexture:"K岷縯 c岷 膽谩",sunnyColors:"M脿u n岷痭g",skyBlue:"Xanh tr峄漣",romanticPink:"H峄搉g l茫ng m岷",selectValidImage:"Vui l貌ng ch峄峮 t峄噋 h矛nh 岷h h峄 l峄�"},zh={title:"C岷 h矛nh b峄� s瓢u t岷璸 bi峄僽 t瓢峄g c岷 x煤c",description:"Ch峄峮 g贸i bi峄僽 t瓢峄g c岷 x煤c 膽岷穞 s岷祅 ho岷穋 h矛nh 岷h bi峄僽 t瓢峄g c岷 x煤c t霉y ch峄塶h. M峄梚 g贸i bi峄僽 t瓢峄g c岷 x煤c ch峄゛ 21 bi峄僽 c岷 c岷 x煤c kh谩c nhau.",noEmojiPack:"Kh么ng c贸 g贸i bi峄僽 t瓢峄g c岷 x煤c",noEmojiPackDescription:"Kh么ng c岷 h矛nh g贸i bi峄僽 t瓢峄g c岷 x煤c, ch峄� bao g峄搈 ph么ng ch峄� v脿 t脿i nguy锚n n峄乶 trong g贸i.",presetEmojiPack:"G贸i bi峄僽 t瓢峄g c岷 x煤c 膽岷穞 s岷祅",customEmojiPack:"G贸i bi峄僽 t瓢峄g c岷 x煤c t霉y ch峄塶h",selectPresetEmojiPack:"Ch峄峮 g贸i bi峄僽 t瓢峄g c岷 x煤c 膽岷穞 s岷祅",twemoji64:"Twemoji 64脳64",twemoji32:"Twemoji 32脳32",twemojiDescription:"B峄� bi峄僽 t瓢峄g c岷 x煤c ch岷 l瓢峄g cao t峄� Twitter, ho脿n h岷 膽峄� hi峄僴 th峄� r玫 r脿ng",twitterEmojiName:"Twemoji {size}x{size}",twitterEmojiDescription:"G贸i bi峄僽 t瓢峄g c岷 x煤c Twitter, {size}脳{size} pixel",size:"K铆ch th瓢峄沜",preview:"Xem tr瓢峄沜",select:"Ch峄峮",customEmojiTitle:"G贸i bi峄僽 t瓢峄g c岷 x煤c t霉y ch峄塶h",customEmojiDescription:"T岷 l锚n h矛nh 岷h bi峄僽 t瓢峄g c岷 x煤c c峄 ri锚ng b岷. M峄梚 c岷 x煤c c岷 m峄檛 h矛nh 岷h.",uploadInstructions:"Nh岷 ho岷穋 k茅o 膽峄� t岷 l锚n h矛nh 岷h bi峄僽 t瓢峄g c岷 x煤c (PNG/JPG/WEBP)",supportedFormats:"膼峄媙h d岷g 膽瓢峄 h峄� tr峄�: PNG, JPG, WEBP. K铆ch th瓢峄沜 khuy岷縩 ngh峄�: 64脳64 pixel",emotionMapping:"脕nh x岷� c岷 x煤c",uploadAllEmotions:"Vui l貌ng t岷 l锚n h矛nh 岷h bi峄僽 t瓢峄g c岷 x煤c cho t岷 c岷� c岷 x煤c",maxImageWidth:"Chi峄乽 r峄檔g h矛nh 岷h t峄慽 膽a (px)",maxImageHeight:"Chi峄乽 cao h矛nh 岷h t峄慽 膽a (px)",clickToUpload:"Nh岷 膽峄� t岷 l锚n",orDragDrop:"ho岷穋 k茅o v脿 th岷�",clickToUploadOrDrag:"Nh岷 膽峄� t岷 l锚n ho岷穋 k茅o v脿o 膽芒y",remove:"X贸a",replace:"Thay th岷�",selectValidEmojiImage:"Vui l貌ng ch峄峮 t峄噋 h矛nh 岷h bi峄僽 t瓢峄g c岷 x煤c h峄 l峄�",selectValidFormat:"Vui l貌ng ch峄峮 h矛nh 岷h 膽峄媙h d岷g PNG ho岷穋 GIF h峄 l峄�",maxFileSize:"K铆ch th瓢峄沜 t峄噋 kh么ng 膽瓢峄 v瓢峄 qu谩 1MB",invalidFormat:"膼峄媙h d岷g h矛nh 岷h kh么ng 膽瓢峄 h峄� tr峄�. Vui l貌ng s峄� d峄g PNG, JPG ho岷穋 WEBP.",required:"B岷痶 bu峄檆",uploadEmojiImages:"T岷 l锚n h矛nh 岷h bi峄僽 t瓢峄g c岷 x煤c (GIF c岷 PSRAM)",customEmojiPackConfig:"C岷 h矛nh g贸i bi峄僽 t瓢峄g c岷 x煤c t霉y ch峄塶h",neutralRequiredNotice:"* Ph岷 t岷 l锚n c岷 x煤c m岷穋 膽峄媙h trung l岷璸, c谩c c岷 x煤c kh谩c l脿 t霉y ch峄峮. N岷縰 kh么ng t岷 l锚n, c岷 x煤c m岷穋 膽峄媙h s岷� 膽瓢峄 s峄� d峄g thay th岷�.",sharedFileMessage:"C岷 x煤c {emotionKey} s峄� d峄g c霉ng t峄噋 h矛nh 岷h v峄沬 {existingEmotions} (l瓢u tr峄� chia s岷�)",fileDeleted:"T峄噋 {fileHash} 膽茫 b峄� x贸a (kh么ng c贸 tham chi岷縰 c岷 x煤c kh谩c)",fileRetained:"T峄噋 {fileHash} v岷玭 膽瓢峄 s峄� d峄g b峄焛 c谩c c岷 x煤c kh谩c, 膽瓢峄 gi峄� l岷",imageLoadFailed:"Kh么ng t岷 膽瓢峄 h矛nh 岷h bi峄僽 t瓢峄g c岷 x煤c",emotions:{neutral:"Trung l岷璸",happy:"Vui v岷�",laughing:"C瓢峄漣 l峄沶",funny:"H脿i h瓢峄沜",sad:"Bu峄搉",angry:"Gi岷璶 d峄�",crying:"Kh贸c",loving:"Y锚u th瓢啤ng",embarrassed:"X岷 h峄�",surprised:"B岷 ng峄�",shocked:"S峄慶",thinking:"Suy ngh末",winking:"Nh谩y m岷痶",cool:"Ng岷",relaxed:"Tho岷 m谩i",delicious:"Ngon mi峄噉g",kissy:"H么n",confident:"T峄� tin",sleepy:"Bu峄搉 ng峄�",silly:"Ng峄� ng岷﹏",confused:"B峄慽 r峄慽"}},$h={title:"C岷 h矛nh ph么ng ch峄�",description:"Ch峄峮 ph么ng ch峄� 膽岷穞 s岷祅 ho岷穋 t岷 l锚n t峄噋 ph么ng ch峄� t霉y ch峄塶h.",noFonts:"Kh么ng c贸 ph么ng ch峄�",noFontsDescription:"Kh么ng s峄� d峄g c岷 h矛nh ph么ng ch峄�, thi岷縯 b峄� s岷� s峄� d峄g ph么ng ch峄� m岷穋 膽峄媙h",presetFonts:"Ph么ng ch峄� 膽岷穞 s岷祅",customFonts:"Ph么ng ch峄� t霉y ch峄塶h",selectPresetFont:"Ch峄峮 ph么ng ch峄� 膽岷穞 s岷祅",fontSize:"K铆ch th瓢峄沜 ph么ng ch峄�:",bitDepth:"膼峄� s芒u bit:",charset:"B峄� k媒 t峄�:",fileSize:"K铆ch th瓢峄沜 t峄噋:",customFontConfig:"C岷 h矛nh ph么ng ch峄� t霉y ch峄塶h",fontFile:"T峄噋 ph么ng ch峄�",clickSelectFontFile:"Nh岷 膽峄� ch峄峮 t峄噋 ph么ng ch峄�",orDragFileHere:"ho岷穋 k茅o t峄噋 v脿o 膽芒y",supportedFormats:"H峄� tr峄� 膽峄媙h d岷g TTF, WOFF, WOFF2",removeFile:"X贸a t峄噋",fontSizePx:"K铆ch th瓢峄沜 ph么ng ch峄� (px)",range:"Ph岷 vi:",bitDepthBpp:"膼峄� s芒u bit (BPP)",monochrome:"1 bpp (膼啤n s岷痗)",fourColors:"2 bpp (4 m脿u)",sixteenColors:"4 bpp (16 m脿u)",characterSet:"B峄� k媒 t峄�",deepseekR1:"DeepSeek R1 (7405 k媒 t峄�)",gb2312:"GB2312 (7445 k媒 t峄�)",qwen18409:"Qwen (18409 k媒 t峄�)",latin1:"Latin1 (190 k媒 t峄�)",fullCharset:"B峄� k媒 t峄� 膽岷 膽峄� (T岷 c岷� k媒 t峄� trong t峄噋 ph么ng ch峄�)",hideSubtitle:"岷╪ ph峄� 膽峄�",hideSubtitleDescription:"Kh么ng hi峄僴 th峄� ph峄� 膽峄� v膬n b岷 trong khi tr貌 chuy峄噉",selectValidFontFile:"Vui l貌ng ch峄峮 t峄噋 ph么ng ch峄� h峄 l峄� (TTF, WOFF, WOFF2)",presetFontNames:{font_puhui_deepseek_14_1:"Alibaba PuHuiTi 14px",font_puhui_deepseek_16_4:"Alibaba PuHuiTi 16px",font_puhui_deepseek_20_4:"Alibaba PuHuiTi 20px",font_puhui_deepseek_30_4:"Alibaba PuHuiTi 30px",font_noto_qwen_14_1:"Noto Sans 14px",font_noto_qwen_16_4:"Noto Sans 16px",font_noto_qwen_20_4:"Noto Sans 20px",font_noto_qwen_30_4:"Noto Sans 30px"},charsetDeepseek:"DeepSeek 7405",charsetQwen:"Qwen 18409"},Ad={app:xh,common:Jh,header:Th,configNotice:Hh,steps:Yh,generateModal:_h,progressSteps:Kh,wakewordConfig:Oh,errors:Ph,flashProgress:Vh,device:Wh,chipConfig:qh,themeDesign:jh,generateSummary:Zh,backgroundConfig:Xh,emojiConfig:zh,fontConfig:$h},ed={en:Mf,"zh-CN":Ah,"zh-TW":fh,ja:Mh,vi:Ad},No=[{code:"en",name:"English",flag:"馃嚭馃嚫"},{code:"zh-CN",name:"绠�浣撲腑鏂�",flag:"馃嚚馃嚦"},{code:"zh-TW",name:"绻侀珨涓枃",flag:"馃嚬馃嚰"},{code:"ja",name:"鏃ユ湰瑾�",flag:"馃嚡馃嚨"},{code:"vi",name:"Ti岷縩g Vi峄噒",flag:"馃嚮馃嚦"}];function td(){const A=navigator.language||navigator.userLanguage;return{"zh-CN":"zh-CN","zh-TW":"zh-TW","zh-HK":"zh-TW",zh:"zh-CN",ja:"ja",vi:"vi",en:"en","en-US":"en","en-GB":"en"}[A]||"en"}const id={class:"relative"},od={class:"py-1"},nd=["onClick"],rd={class:"mr-3"},gd={key:0,class:"ml-auto w-4 h-4 text-blue-600",fill:"currentColor",viewBox:"0 0 20 20"},sd={__name:"LanguageSelector",setup(A){const{locale:e,t}=we(),i=uA(!1),o=hA(()=>No.find(a=>a.code===e.value)||No[0]),n=()=>{i.value=!i.value},r=()=>{i.value=!1},g=a=>{e.value=a,localStorage.setItem("user-language",a),document.title=t("app.title"),r()},s=a=>{a.target.closest(".relative")||r()};return Ft(()=>{document.addEventListener("click",s);const a=localStorage.getItem("user-language");a&&No.find(I=>I.code===a)&&(e.value=a)}),Ci(()=>{document.removeEventListener("click",s)}),(a,I)=>(T(),K("div",id,[B("button",{onClick:n,class:"flex items-center space-x-2 px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"},[B("span",null,F(o.value.flag),1),B("span",null,F(o.value.name),1),(T(),K("svg",{class:kA(["w-4 h-4 transition-transform duration-200",{"rotate-180":i.value}]),fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[...I[1]||(I[1]=[B("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M19 9l-7 7-7-7"},null,-1)])],2))]),i.value?(T(),K("div",{key:0,class:"absolute right-0 mt-2 w-48 bg-white border border-gray-200 rounded-md shadow-lg z-50",onClick:I[0]||(I[0]=dt(()=>{},["stop"]))},[B("div",od,[(T(!0),K(qA,null,Ke(cA(No),l=>(T(),K("button",{key:l.code,onClick:C=>g(l.code),class:kA(["flex items-center w-full px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100",{"bg-blue-50 text-blue-700":l.code===o.value.code}])},[B("span",rd,F(l.flag),1),B("span",null,F(l.name),1),l.code===o.value.code?(T(),K("svg",gd,[...I[2]||(I[2]=[B("path",{"fill-rule":"evenodd",d:"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z","clip-rule":"evenodd"},null,-1)])])):CA("",!0)],10,nd))),128))])])):CA("",!0),i.value?(T(),K("div",{key:1,class:"fixed inset-0 z-40",onClick:r})):CA("",!0)]))}},ad={class:"space-y-6"},Id={class:"text-xl font-semibold text-gray-900 mb-4"},Cd={class:"text-gray-600 mb-6"},ld={key:0},Bd={key:1},Qd={key:2},cd={key:0,class:"space-y-4"},Ed={class:"bg-blue-50 border-2 border-blue-200 rounded-lg p-6"},ud={class:"flex items-center justify-center space-x-3"},fd={class:"text-lg font-medium text-blue-900"},hd={class:"text-sm text-blue-700 mt-1"},dd={key:1,class:"space-y-4"},pd={class:"bg-green-50 border-2 border-green-200 rounded-lg p-4"},md={class:"flex items-start justify-between mb-3"},yd={class:"flex items-center"},wd={class:"text-lg font-medium text-green-900"},Dd={class:"grid grid-cols-1 md:grid-cols-3 gap-4 mt-4"},kd={class:"bg-white rounded-lg p-3"},vd={class:"block text-sm font-medium text-gray-700 mb-1"},Fd={class:"text-base font-semibold text-gray-900"},Sd={class:"bg-white rounded-lg p-3"},Rd={class:"block text-sm font-medium text-gray-700 mb-1"},bd={class:"text-base font-semibold text-gray-900"},Nd={class:"bg-white rounded-lg p-3"},Gd={class:"block text-sm font-medium text-gray-700 mb-1"},Ld={class:"text-base font-semibold text-gray-900"},Ud={class:"mt-3 text-sm text-green-700"},Md={class:"border border-gray-200 rounded-lg p-4"},xd={key:0,class:"mt-4"},Jd={key:2,class:"space-y-4"},Td={class:"bg-yellow-50 border border-yellow-200 rounded-lg p-4"},Hd={class:"flex items-start"},Yd={class:"font-medium text-yellow-900"},_d={class:"text-sm text-yellow-700 mt-1"},Kd={class:"border-2 border-gray-300 rounded-lg p-4"},Od={class:"text-lg font-medium text-gray-900 mb-4"},Pd={class:"flex justify-end"},Vd=["disabled"],Wd={__name:"ChipConfig",props:{modelValue:{type:Object,default:()=>({model:"",display:{width:320,height:240,color:"RGB565"}})}},emits:["update:modelValue","next"],setup(A,{emit:e}){const{t}=we(),i=e,o=hA(()=>[{value:"",label:t("chipConfig.selectChip")},{value:"esp32s3",label:"ESP32-S3"},{value:"esp32c3",label:"ESP32-C3"},{value:"esp32c5",label:"ESP32-C5"},{value:"esp32c6",label:"ESP32-C6"},{value:"esp32p4",label:"ESP32-P4"},{value:"esp32",label:"ESP32"},{value:"others",label:t("common.other")}]),n={name:"ConfigForm",props:{config:{type:Object,required:!0},showRequired:{type:Boolean,default:!1}},render(){const d=this.$t;return GA("div",{class:"grid grid-cols-1 md:grid-cols-3 gap-4"},[GA("div",[GA("label",{class:"block text-sm font-medium text-gray-700 mb-2"},[d("chipConfig.chipRequired")+" ",this.showRequired&&GA("span",{class:"text-red-500"},"*")]),GA("select",{class:"w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-primary-500",value:this.config.model,onChange:v=>{this.config.model=v.target.value}},cA(o).map(v=>GA("option",{value:v.value,key:v.value},v.label)))]),GA("div",[GA("label",{class:"block text-sm font-medium text-gray-700 mb-2"},[d("chipConfig.widthRequired")+" ",this.showRequired&&GA("span",{class:"text-red-500"},"*")]),GA("input",{type:"number",class:"w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-primary-500",value:this.config.display.width,min:128,max:800,placeholder:"320",onInput:v=>{this.config.display.width=Number(v.target.value)}})]),GA("div",[GA("label",{class:"block text-sm font-medium text-gray-700 mb-2"},[d("chipConfig.heightRequired")+" ",this.showRequired&&GA("span",{class:"text-red-500"},"*")]),GA("input",{type:"number",class:"w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-primary-500",value:this.config.display.height,min:128,max:600,placeholder:"240",onInput:v=>{this.config.display.height=Number(v.target.value)}})])])}},{deviceInfo:r,isDeviceOnline:g,hasToken:s}=Nn(),a=uA(s.value),I=uA(!1),l=uA(""),C=uA(!1),Q=uA({model:"",display:{width:320,height:240,color:"RGB565"}}),f={"ESP32-S3":"esp32s3",ESP32S3:"esp32s3","ESP32-C3":"esp32c3",ESP32C3:"esp32c3","ESP32-C5":"esp32c5",ESP32C5:"esp32c5","ESP32-P4":"esp32p4",ESP32P4:"esp32p4","ESP32-C6":"esp32c6",ESP32C6:"esp32c6",ESP32:"esp32"},h=()=>{var d,v;try{if(!g.value)return a.value||(l.value=t("chipConfig.deviceOffline"),I.value=!1),!1;const b=(d=r.value.chip)==null?void 0:d.model;if(!b||b==="鏈煡")return!1;let N=f[b.toUpperCase()]||f[b]||b.toLowerCase().replace(/-/g,"");["esp32s3","esp32c3","esp32c5","esp32p4","esp32c6","esp32","others"].includes(N)||(N="others");const M=(v=r.value.screen)==null?void 0:v.resolution;if(!M||M==="鏈煡")return!1;const[U,R]=M.split("x").map(Number);return!U||!R||isNaN(U)||isNaN(R)?(l.value=t("chipConfig.resolutionFormatError"),I.value=!1,a.value=!1,!1):(Q.value={model:N,display:{width:U,height:R,color:"RGB565"}},i("update:modelValue",{model:N,display:{width:U,height:R,color:"RGB565"}}),I.value=!0,l.value="",a.value=!1,console.log("鉁� 璁惧閰嶇疆鍔犺浇鎴愬姛:",{chip:N,width:U,height:R}),!0)}catch(b){return console.error("鍔犺浇璁惧閰嶇疆澶辫触:",b),l.value=t("chipConfig.loadingError"),I.value=!1,a.value=!1,!1}},u=hA(()=>Q.value.model&&Q.value.display.width&&Q.value.display.height),E=hA(()=>Q.value.model?Q.value.model.toUpperCase().replace(/ESP32/i,"ESP32-"):""),D=hA(()=>Q.value.display),c=()=>{C.value=!C.value},p=()=>{u.value&&i("next")};return ae(()=>Q.value,d=>{i("update:modelValue",{model:d.model,display:{...d.display}})},{deep:!0}),ae(()=>g.value,d=>{d&&!I.value&&(a.value=!0,l.value="",setTimeout(()=>{h()},100))}),ae(()=>[r.value.chip,r.value.screen],()=>{g.value&&!I.value&&h()},{deep:!0}),Ft(()=>{if(!s.value){a.value=!1,l.value=t("chipConfig.manualConfigRequired"),console.log("鈿� 鏈娴嬪埌璁惧杩炴帴锛屼娇鐢ㄦ墜鍔ㄩ厤缃ā寮�");return}g.value&&r.value.chip&&r.value.chip.model!=="鏈煡"&&r.value.screen&&r.value.screen.resolution!=="鏈煡"?(console.log("鉁� 妫�娴嬪埌宸茬紦瀛樼殑璁惧淇℃伅锛岀珛鍗冲姞杞�"),h()):g.value?(console.log("鈴� 璁惧鍦ㄧ嚎锛岀瓑寰呰澶囦俊鎭姞杞�..."),a.value=!0,setTimeout(()=>{h()},500),setTimeout(()=>{a.value&&!I.value&&g.value&&(a.value=!1,!r.value.chip||r.value.chip.model==="鏈煡"?l.value=t("chipConfig.chipModelError"):!r.value.screen||r.value.screen.resolution==="鏈煡"?l.value=t("chipConfig.resolutionError"):l.value=t("chipConfig.timeoutError"),console.warn("鈿� 璁惧閰嶇疆鍔犺浇瓒呮椂:",l.value))},1e4)):(console.log("鈴� 绛夊緟璁惧杩炴帴..."),a.value=!0,setTimeout(()=>{a.value&&!I.value&&!g.value&&(a.value=!1,l.value=t("chipConfig.manualConfigHint"),console.warn("鈿� 绛夊緟璁惧杩炴帴瓒呮椂"))},1e4))}),(d,v)=>(T(),K("div",ad,[B("div",null,[B("h2",Id,F(d.$t("chipConfig.title")),1),B("p",Cd,[a.value?(T(),K("span",ld,F(d.$t("chipConfig.loadingFromDevice")),1)):I.value?(T(),K("span",Bd,F(d.$t("chipConfig.loadedFromDevice")),1)):(T(),K("span",Qd,F(d.$t("chipConfig.manualConfig")),1))])]),a.value?(T(),K("div",cd,[B("div",Ed,[B("div",ud,[v[0]||(v[0]=B("svg",{class:"animate-spin h-6 w-6 text-blue-600",xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24"},[B("circle",{class:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor","stroke-width":"4"}),B("path",{class:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"})],-1)),B("div",null,[B("h3",fd,F(d.$t("chipConfig.loadingTitle")),1),B("p",hd,F(d.$t("chipConfig.loadingDesc")),1)])])])])):cA(g)&&I.value?(T(),K("div",dd,[B("div",pd,[B("div",md,[B("div",yd,[v[1]||(v[1]=B("div",{class:"w-5 h-5 bg-green-500 rounded-full flex items-center justify-center mr-2"},[B("svg",{class:"w-3 h-3 text-white",fill:"currentColor",viewBox:"0 0 20 20"},[B("path",{"fill-rule":"evenodd",d:"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z","clip-rule":"evenodd"})])],-1)),B("h3",wd,F(d.$t("chipConfig.autoLoadedTitle")),1)])]),B("div",Dd,[B("div",kd,[B("label",vd,F(d.$t("chipConfig.chipModel")),1),B("div",Fd,F(E.value),1)]),B("div",Sd,[B("label",Rd,F(d.$t("chipConfig.screenWidth")),1),B("div",bd,F(D.value.width)+" px",1)]),B("div",Nd,[B("label",Gd,F(d.$t("chipConfig.screenHeight")),1),B("div",Ld,F(D.value.height)+" px",1)])]),B("div",Ud," 鉁� "+F(d.$t("chipConfig.colorFormat"))+" "+F(D.value.color),1)]),B("div",Md,[B("button",{onClick:c,class:"flex items-center text-sm text-gray-600 hover:text-gray-900"},[v[2]||(v[2]=B("svg",{class:"w-4 h-4 mr-2",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[B("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"}),B("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M15 12a3 3 0 11-6 0 3 3 0 016 0z"})],-1)),gt(" "+F(C.value?d.$t("chipConfig.collapseManual"):d.$t("chipConfig.manualEdit")),1)]),C.value?(T(),K("div",xd,[TA(n,{config:Q.value},null,8,["config"])])):CA("",!0)])])):(T(),K("div",Jd,[B("div",Td,[B("div",Hd,[v[3]||(v[3]=B("svg",{class:"w-5 h-5 text-yellow-600 mr-3 flex-shrink-0 mt-0.5",fill:"currentColor",viewBox:"0 0 20 20"},[B("path",{"fill-rule":"evenodd",d:"M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z","clip-rule":"evenodd"})],-1)),B("div",null,[B("h4",Yd,F(d.$t("chipConfig.manualConfigDesc")),1),B("p",_d,F(l.value||d.$t("chipConfig.manualConfigHint")),1)])])]),B("div",Kd,[B("h3",Od,F(d.$t("chipConfig.manualConfigTitle")),1),TA(n,{config:Q.value,"show-required":!0},null,8,["config"])])])),B("div",Pd,[B("button",{onClick:p,disabled:!u.value,class:"bg-primary-500 hover:bg-primary-600 disabled:bg-gray-300 text-white px-6 py-2 rounded-lg font-medium transition-colors"},F(d.$t("chipConfig.next")),9,Vd)])]))}},qd={class:"space-y-6"},jd={class:"text-lg font-medium text-gray-900 mb-2"},Zd={class:"text-gray-600"},Xd={key:0,class:"bg-orange-50 border border-orange-200 rounded-lg p-4"},zd={class:"text-sm text-orange-800"},$d={class:"mt-1"},A0={key:1,class:"space-y-6"},e0={class:"flex space-x-4"},t0={key:0,class:"space-y-4"},i0={class:"block text-sm font-medium text-gray-700"},o0={class:"relative"},n0=["value"],r0=["value"],g0={key:1,class:"space-y-4 bg-gray-50 p-4 rounded-lg border border-gray-200"},s0={class:"font-medium text-gray-900"},a0={class:"grid grid-cols-1 md:grid-cols-2 gap-4"},I0={class:"block text-sm font-medium text-gray-700 mb-1"},C0=["placeholder"],l0={key:0,class:"text-xs text-red-500 mt-1"},B0={class:"block text-sm font-medium text-gray-700 mb-1"},Q0=["placeholder"],c0={key:0,class:"text-xs text-red-500 mt-1"},E0={class:"grid grid-cols-1 md:grid-cols-3 gap-4"},u0={class:"block text-sm font-medium text-gray-700 mb-1"},f0={class:"flex items-center space-x-2"},h0={class:"text-sm text-gray-600 w-8"},d0={class:"text-xs text-gray-500 mt-1"},p0={class:"block text-sm font-medium text-gray-700 mb-1"},m0={key:0,class:"text-xs text-red-500 mt-1"},y0={class:"block text-sm font-medium text-gray-700 mb-1"},w0={value:"mn6_cn"},D0={value:"mn6_en"},k0={class:"bg-blue-50 border border-blue-200 rounded-lg p-3"},v0={class:"text-sm text-blue-800"},F0={class:"mt-1 list-disc list-inside space-y-1"},S0={key:0},R0={key:1},b0={key:2},N0={__name:"WakewordConfig",props:{modelValue:{type:Object,required:!0},chipModel:{type:String,required:!0}},emits:["update:modelValue"],setup(A,{emit:e}){const{t}=we(),i=A,o=e,n=[{id:"wn9s_hilexin",name:"Hi,涔愰懌",model:"WakeNet9s"},{id:"wn9s_hiesp",name:"Hi,ESP",model:"WakeNet9s"},{id:"wn9s_nihaoxiaozhi",name:"浣犲ソ灏忔櫤",model:"WakeNet9s"},{id:"wn9s_hijason",name:"Hi,Jason",model:"WakeNet9s"},{id:"wn9s_alexa",name:"Alexa",model:"WakeNet9s"},{id:"wn9_hilexin",name:"Hi,涔愰懌",model:"WakeNet9"},{id:"wn9_hiesp",name:"Hi,ESP",model:"WakeNet9"},{id:"wn9_nihaoxiaozhi_tts",name:"浣犲ソ灏忔櫤",model:"WakeNet9"},{id:"wn9_hijason_tts2",name:"Hi,Jason",model:"WakeNet9"},{id:"wn9_nihaomiaoban_tts2",name:"浣犲ソ鍠典即",model:"WakeNet9"},{id:"wn9_xiaoaitongxue",name:"灏忕埍鍚屽",model:"WakeNet9"},{id:"wn9_himfive",name:"Hi,M Five",model:"WakeNet9"},{id:"wn9_alexa",name:"Alexa",model:"WakeNet9"},{id:"wn9_jarvis_tts",name:"Jarvis",model:"WakeNet9"},{id:"wn9_computer_tts",name:"Computer",model:"WakeNet9"},{id:"wn9_heywillow_tts",name:"Hey,Willow",model:"WakeNet9"},{id:"wn9_sophia_tts",name:"Sophia",model:"WakeNet9"},{id:"wn9_mycroft_tts",name:"Mycroft",model:"WakeNet9"},{id:"wn9_heyprinter_tts",name:"Hey,Printer",model:"WakeNet9"},{id:"wn9_hijoy_tts",name:"Hi,Joy",model:"WakeNet9"},{id:"wn9_heywanda_tts",name:"Hey,Wand",model:"WakeNet9"},{id:"wn9_astrolabe_tts",name:"Astrolabe",model:"WakeNet9"},{id:"wn9_heyily_tts2",name:"Hey,Ily",model:"WakeNet9"},{id:"wn9_hijolly_tts2",name:"Hi,Jolly",model:"WakeNet9"},{id:"wn9_hifairy_tts2",name:"Hi,Fairy",model:"WakeNet9"},{id:"wn9_bluechip_tts2",name:"Blue Chip",model:"WakeNet9"},{id:"wn9_hiandy_tts2",name:"Hi,Andy",model:"WakeNet9"},{id:"wn9_heyivy_tts2",name:"Hey,Ivy",model:"WakeNet9"},{id:"wn9_histackchan_tts3",name:"Hi,Stack Chan",model:"WakeNet9"},{id:"wn9_hiwalle_tts2",name:"Hi,Wall E",model:"WakeNet9"},{id:"wn9_nihaoxiaoxin_tts",name:"浣犲ソ灏忛懌",model:"WakeNet9"},{id:"wn9_xiaomeitongxue_tts",name:"灏忕編鍚屽",model:"WakeNet9"},{id:"wn9_hixiaoxing_tts",name:"Hi,灏忔槦",model:"WakeNet9"},{id:"wn9_xiaolongxiaolong_tts",name:"灏忛緳灏忛緳",model:"WakeNet9"},{id:"wn9_miaomiaotongxue_tts",name:"鍠靛柕鍚屽",model:"WakeNet9"},{id:"wn9_himiaomiao_tts",name:"Hi,鍠靛柕",model:"WakeNet9"},{id:"wn9_hilili_tts",name:"Hi,Lily",model:"WakeNet9"},{id:"wn9_hitelly_tts",name:"Hi,Telly",model:"WakeNet9"},{id:"wn9_xiaobinxiaobin_tts",name:"灏忔花灏忔花",model:"WakeNet9"},{id:"wn9_haixiaowu_tts",name:"Hi,灏忓帆",model:"WakeNet9"},{id:"wn9_xiaoyaxiaoya_tts2",name:"灏忛腑灏忛腑",model:"WakeNet9"},{id:"wn9_linaiban_tts2",name:"鐠冨鏉�",model:"WakeNet9"},{id:"wn9_xiaosurou_tts2",name:"灏忛叆鑲�",model:"WakeNet9"},{id:"wn9_xiaoyutongxue_tts2",name:"灏忓畤鍚屽",model:"WakeNet9"},{id:"wn9_xiaomingtongxue_tts2",name:"灏忔槑鍚屽",model:"WakeNet9"},{id:"wn9_xiaokangtongxue_tts2",name:"灏忓悍鍚屽",model:"WakeNet9"},{id:"wn9_xiaojianxiaojian_tts2",name:"灏忕灏忕",model:"WakeNet9"},{id:"wn9_xiaotexiaote_tts2",name:"灏忕壒灏忕壒",model:"WakeNet9"},{id:"wn9_nihaoxiaoyi_tts2",name:"浣犲ソ灏忕泭",model:"WakeNet9"},{id:"wn9_nihaobaiying_tts2",name:"浣犲ソ鐧惧簲",model:"WakeNet9"},{id:"wn9_xiaoluxiaolu_tts2",name:"灏忛箍灏忛箍",model:"WakeNet9"},{id:"wn9_nihaodongdong_tts2",name:"浣犲ソ涓滀笢",model:"WakeNet9"},{id:"wn9_nihaoxiaoan_tts2",name:"浣犲ソ灏忓畨",model:"WakeNet9"},{id:"wn9_ni3hao3xiao3mai4_tts2",name:"浣犲ソ灏忚剦",model:"WakeNet9"},{id:"wn9_ni3hao3xiao3rui4_tts3",name:"浣犲ソ灏忕憺",model:"WakeNet9"},{id:"wn9_hai1xiao3ou1_tts3",name:"鍡ㄥ皬娆�",model:"WakeNet9"},{id:"wn9_xiao3jia1xiao3jia1_tts3",name:"灏忕張灏忕張",model:"WakeNet9"},{id:"wn9_xiao3feng1xiao3feng1_tts3",name:"灏忓嘲灏忓嘲",model:"WakeNet9"}],r=hA(()=>{const E=i.chipModel.toLowerCase();return E==="esp32s3"||E==="esp32p4"}),g=hA(()=>{const E=i.chipModel.toLowerCase();return E==="esp32c3"||E==="esp32c5"||E==="esp32c6"}),s=hA(()=>i.chipModel.toLowerCase()==="esp32s3"),a=hA(()=>r.value||g.value),I=hA(()=>r.value?n.filter(E=>E.model==="WakeNet9"):g.value?n.filter(E=>E.model==="WakeNet9s"):[]),l=uA({name:"",command:"",threshold:20,duration:3e3,model:"mn6_cn"}),C=uA({name:"",command:"",duration:""}),Q=()=>{let E=!0;return C.value={name:"",command:"",duration:""},l.value.name.trim()||(C.value.name=t("wakewordConfig.errors.nameRequired"),E=!1),l.value.command.trim()?l.value.model.includes("_en")&&/[!,.?]/.test(l.value.command)&&(C.value.command=t("wakewordConfig.errors.noPunctuation"),E=!1):(C.value.command=t("wakewordConfig.errors.commandRequired"),E=!1),(!l.value.duration||l.value.duration<500||l.value.duration>1e4)&&(C.value.duration=t("wakewordConfig.errors.durationRange"),E=!1),E},f=uA(!1),h=E=>{var D;o("update:modelValue",{...i.modelValue,type:E,preset:E==="preset"&&(i.modelValue.preset||((D=I.value[0])==null?void 0:D.id))||"",custom:{...i.modelValue.custom,...l.value}})},u=E=>{o("update:modelValue",{...i.modelValue,preset:E})};return ae(l,E=>{f.value||i.modelValue.type==="custom"&&(Q(),o("update:modelValue",{...i.modelValue,custom:{...E}}))},{deep:!0}),ae(()=>i.modelValue.custom,E=>{E&&(f.value=!0,l.value={name:E.name||"",command:E.command||"",threshold:E.threshold!==void 0?E.threshold:20,duration:E.duration!==void 0?E.duration:3e3,model:E.model||"mn6_cn"},uo(()=>{f.value=!1}))},{deep:!0,immediate:!0}),(E,D)=>(T(),K("div",qd,[B("div",null,[B("h3",jd,F(E.$t("wakewordConfig.title")),1),B("p",Zd,F(E.$t("wakewordConfig.description")),1)]),a.value?(T(),K("div",A0,[B("div",e0,[B("button",{onClick:D[0]||(D[0]=c=>h("none")),class:kA(["px-4 py-2 border rounded-lg transition-colors",A.modelValue.type==="none"?"border-primary-500 bg-primary-50 text-primary-700":"border-gray-300 hover:border-gray-400"])},F(E.$t("wakewordConfig.noWakeword")),3),B("button",{onClick:D[1]||(D[1]=c=>h("preset")),class:kA(["px-4 py-2 border rounded-lg transition-colors",A.modelValue.type==="preset"?"border-primary-500 bg-primary-50 text-primary-700":"border-gray-300 hover:border-gray-400"])},F(E.$t("wakewordConfig.presetWakeword")),3),s.value?(T(),K("button",{key:0,onClick:D[2]||(D[2]=c=>h("custom")),class:kA(["px-4 py-2 border rounded-lg transition-colors",A.modelValue.type==="custom"?"border-primary-500 bg-primary-50 text-primary-700":"border-gray-300 hover:border-gray-400"])},F(E.$t("wakewordConfig.customWakeword")),3)):CA("",!0)]),A.modelValue.type==="preset"?(T(),K("div",t0,[B("label",i0,F(E.$t("wakewordConfig.selectWakeword")),1),B("div",o0,[B("select",{value:A.modelValue.preset,onChange:D[3]||(D[3]=c=>u(c.target.value)),class:"w-full border border-gray-300 rounded-md px-3 py-2 pr-10 bg-white focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-primary-500"},[(T(!0),K(qA,null,Ke(I.value,c=>(T(),K("option",{key:c.id,value:c.id},F(c.name)+" ("+F(c.model)+") ",9,r0))),128))],40,n0)])])):CA("",!0),A.modelValue.type==="custom"?(T(),K("div",g0,[B("h4",s0,F(E.$t("wakewordConfig.customSettings")),1),B("div",a0,[B("div",null,[B("label",I0,[gt(F(E.$t("wakewordConfig.wakewordName"))+" ",1),D[9]||(D[9]=B("span",{class:"text-red-500"},"*",-1))]),ZA(B("input",{type:"text","onUpdate:modelValue":D[4]||(D[4]=c=>l.value.name=c),placeholder:E.$t("wakewordConfig.wakewordNamePlaceholder"),class:kA(["w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary-500",{"border-red-500":C.value.name}])},null,10,C0),[[te,l.value.name]]),C.value.name?(T(),K("p",l0,F(C.value.name),1)):CA("",!0)]),B("div",null,[B("label",B0,[gt(F(E.$t("wakewordConfig.wakewordCommand"))+" ",1),D[10]||(D[10]=B("span",{class:"text-red-500"},"*",-1))]),ZA(B("input",{type:"text","onUpdate:modelValue":D[5]||(D[5]=c=>l.value.command=c),placeholder:l.value.model.includes("_cn")?E.$t("wakewordConfig.wakewordCommandPlaceholderCN"):E.$t("wakewordConfig.wakewordCommandPlaceholderEN"),class:kA(["w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary-500",{"border-red-500":C.value.command}])},null,10,Q0),[[te,l.value.command]]),C.value.command?(T(),K("p",c0,F(C.value.command),1)):CA("",!0)])]),B("div",E0,[B("div",null,[B("label",u0,F(E.$t("wakewordConfig.threshold")),1),B("div",f0,[ZA(B("input",{type:"range","onUpdate:modelValue":D[6]||(D[6]=c=>l.value.threshold=c),min:"0",max:"100",step:"1",class:"flex-1"},null,512),[[te,l.value.threshold,void 0,{number:!0}]]),B("span",h0,F(l.value.threshold),1)]),B("p",d0,F(E.$t("wakewordConfig.thresholdDesc")),1)]),B("div",null,[B("label",p0,F(E.$t("wakewordConfig.duration"))+" (ms) ",1),ZA(B("input",{type:"number","onUpdate:modelValue":D[7]||(D[7]=c=>l.value.duration=c),min:"500",max:"10000",step:"100",class:kA(["w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary-500",{"border-red-500":C.value.duration}])},null,2),[[te,l.value.duration,void 0,{number:!0}]]),C.value.duration?(T(),K("p",m0,F(C.value.duration),1)):CA("",!0)]),B("div",null,[B("label",y0,F(E.$t("wakewordConfig.selectModel")),1),ZA(B("select",{"onUpdate:modelValue":D[8]||(D[8]=c=>l.value.model=c),class:"w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary-500"},[B("option",w0,F(E.$t("wakewordConfig.mn6cn")),1),B("option",D0,F(E.$t("wakewordConfig.mn6en")),1)],512),[[pr,l.value.model]])])])])):CA("",!0),B("div",k0,[B("div",v0,[B("strong",null,F(E.$t("wakewordConfig.tips.tipLabel")),1),B("ul",F0,[B("li",null,F(E.$t("wakewordConfig.tips.optional")),1),g.value?(T(),K("li",S0,F(E.$t("wakewordConfig.tips.wakeNet9sOnly")),1)):r.value?(T(),K("li",R0,F(E.$t("wakewordConfig.tips.wakeNet9Full")),1)):CA("",!0),s.value?(T(),K("li",b0,F(E.$t("wakewordConfig.tips.customSupport")),1)):CA("",!0)])])])])):(T(),K("div",Xd,[B("div",zd,[B("strong",null,F(E.$t("wakewordConfig.notice")),1),B("p",$d,F(E.$t("wakewordConfig.unsupportedMessage",{chipModel:A.chipModel})),1)])]))]))}};class G0{constructor(){this.dbName="XiaozhiConfigDB",this.version=1,this.db=null,this.initialized=!1}async initialize(){if(!(this.initialized&&this.db))return new Promise((e,t)=>{const i=indexedDB.open(this.dbName,this.version);i.onerror=()=>{console.error("IndexedDB 鍒濆鍖栧け璐�:",i.error),t(i.error)},i.onsuccess=()=>{this.db=i.result,this.initialized=!0,console.log("IndexedDB 鍒濆鍖栨垚鍔�"),e()},i.onupgradeneeded=o=>{const n=o.target.result;if(n.objectStoreNames.contains("configs")||n.createObjectStore("configs",{keyPath:"key"}).createIndex("timestamp","timestamp",{unique:!1}),!n.objectStoreNames.contains("files")){const r=n.createObjectStore("files",{keyPath:"id"});r.createIndex("type","type",{unique:!1}),r.createIndex("timestamp","timestamp",{unique:!1})}if(!n.objectStoreNames.contains("temp_data")){const r=n.createObjectStore("temp_data",{keyPath:"key"});r.createIndex("type","type",{unique:!1}),r.createIndex("timestamp","timestamp",{unique:!1})}console.log("IndexedDB 琛ㄧ粨鏋勫垱寤哄畬鎴�")}})}async saveConfig(e){this.initialized||await this.initialize();const i={key:"current_config",config:this.sanitizeConfigForStorage(e),timestamp:Date.now()};return new Promise((o,n)=>{const s=this.db.transaction(["configs"],"readwrite").objectStore("configs").put(i);s.onerror=()=>{console.error("淇濆瓨閰嶇疆澶辫触:",s.error),n(s.error)},s.onsuccess=()=>{console.log("閰嶇疆宸蹭繚瀛樺埌 IndexedDB"),o()}})}sanitizeConfigForStorage(e){var i,o,n,r,g,s,a,I,l,C;const t=JSON.parse(JSON.stringify(e||{}));try{if((o=(i=t==null?void 0:t.theme)==null?void 0:i.font)!=null&&o.custom&&(t.theme.font.custom.file=null),((r=(n=t==null?void 0:t.theme)==null?void 0:n.emoji)==null?void 0:r.type)==="custom"){t.theme.emoji.custom||(t.theme.emoji.custom={});const Q=((s=(g=t.theme.emoji)==null?void 0:g.custom)==null?void 0:s.images)||{},f={};if(Object.keys(Q).forEach(h=>{f[h]=null}),t.theme.emoji.custom.images=f,t.theme.emoji.custom.emotionMap,t.theme.emoji.custom.fileMap){const h=t.theme.emoji.custom.fileMap,u={};Object.keys(h).forEach(E=>{u[E]=null}),t.theme.emoji.custom.fileMap=u}}(I=(a=t==null?void 0:t.theme)==null?void 0:a.skin)!=null&&I.light&&(t.theme.skin.light.backgroundImage=null),(C=(l=t==null?void 0:t.theme)==null?void 0:l.skin)!=null&&C.dark&&(t.theme.skin.dark.backgroundImage=null)}catch{}return t}async loadConfig(){return this.initialized||await this.initialize(),new Promise((e,t)=>{const n=this.db.transaction(["configs"],"readonly").objectStore("configs").get("current_config");n.onerror=()=>{console.error("鍔犺浇閰嶇疆澶辫触:",n.error),t(n.error)},n.onsuccess=()=>{const r=n.result;r?(console.log("浠� IndexedDB 鎭㈠閰嶇疆鎴愬姛"),e({config:r.config,timestamp:r.timestamp})):e(null)}})}async saveFile(e,t,i,o={}){this.initialized||await this.initialize();const n=await this.fileToArrayBuffer(t);let r={};try{r=o?JSON.parse(JSON.stringify(o)):{}}catch{r={...o}}const g={id:e,type:i,name:t.name,size:t.size,mimeType:t.type,lastModified:t.lastModified,data:n,metadata:r,timestamp:Date.now()};return new Promise((s,a)=>{const C=this.db.transaction(["files"],"readwrite").objectStore("files").put(g);C.onerror=()=>{console.error("淇濆瓨鏂囦欢澶辫触:",C.error),a(C.error)},C.onsuccess=()=>{console.log(`鏂囦欢 ${t.name} 宸蹭繚瀛樺埌 IndexedDB`),s()}})}async loadFile(e){return this.initialized||await this.initialize(),new Promise((t,i)=>{const r=this.db.transaction(["files"],"readonly").objectStore("files").get(e);r.onerror=()=>{console.error("鍔犺浇鏂囦欢澶辫触:",r.error),i(r.error)},r.onsuccess=()=>{const g=r.result;if(g){const s=new Blob([g.data],{type:g.mimeType}),a=new File([s],g.name,{type:g.mimeType,lastModified:g.lastModified});a.storedId=g.id,a.storedType=g.type,a.storedMetadata=g.metadata,a.storedTimestamp=g.timestamp,console.log(`鏂囦欢 ${g.name} 浠� IndexedDB 鎭㈠鎴愬姛`),t(a)}else t(null)}})}async getFilesByType(e){return this.initialized||await this.initialize(),new Promise((t,i)=>{const g=this.db.transaction(["files"],"readonly").objectStore("files").index("type").getAll(e);g.onerror=()=>{console.error("鑾峰彇鏂囦欢鍒楄〃澶辫触:",g.error),i(g.error)},g.onsuccess=()=>{const a=(g.result||[]).map(I=>{const l=new Blob([I.data],{type:I.mimeType}),C=new File([l],I.name,{type:I.mimeType,lastModified:I.lastModified});return C.storedId=I.id,C.storedType=I.type,C.storedMetadata=I.metadata,C.storedTimestamp=I.timestamp,C});t(a)}})}async saveTempData(e,t,i,o={}){this.initialized||await this.initialize();const n={key:e,type:i,data:t,metadata:o,timestamp:Date.now()};return new Promise((r,g)=>{const I=this.db.transaction(["temp_data"],"readwrite").objectStore("temp_data").put(n);I.onerror=()=>{console.error("淇濆瓨涓存椂鏁版嵁澶辫触:",I.error),g(I.error)},I.onsuccess=()=>{console.log(`涓存椂鏁版嵁 ${e} 宸蹭繚瀛榒),r()}})}async loadTempData(e){return this.initialized||await this.initialize(),new Promise((t,i)=>{const r=this.db.transaction(["temp_data"],"readonly").objectStore("temp_data").get(e);r.onerror=()=>{console.error("鍔犺浇涓存椂鏁版嵁澶辫触:",r.error),i(r.error)},r.onsuccess=()=>{const g=r.result;t(g||null)}})}async clearAll(){this.initialized||await this.initialize();const e=["configs","files","temp_data"];return new Promise((t,i)=>{const o=this.db.transaction(e,"readwrite");let n=0,r=!1;const g=()=>{n++,n===e.length&&(r?i(new Error("娓呯┖閮ㄥ垎鏁版嵁鏃跺嚭鐜伴敊璇�")):(console.log("鎵�鏈夊瓨鍌ㄦ暟鎹凡娓呯┖"),t()))};e.forEach(s=>{const I=o.objectStore(s).clear();I.onerror=()=>{console.error(`娓呯┖ ${s} 澶辫触:`,I.error),r=!0,g()},I.onsuccess=()=>{console.log(`${s} 宸叉竻绌篳),g()}})})}async deleteFile(e){return this.initialized||await this.initialize(),new Promise((t,i)=>{const r=this.db.transaction(["files"],"readwrite").objectStore("files").delete(e);r.onerror=()=>{console.error("鍒犻櫎鏂囦欢澶辫触:",r.error),i(r.error)},r.onsuccess=()=>{console.log(`鏂囦欢 ${e} 宸插垹闄),t()}})}async getStorageInfo(){this.initialized||await this.initialize();const e=["configs","files","temp_data"],t={};for(const o of e){const n=await new Promise((r,g)=>{const I=this.db.transaction([o],"readonly").objectStore(o).count();I.onerror=()=>g(I.error),I.onsuccess=()=>r(I.result)});t[o]={count:n}}const i=await this.loadConfig();return t.lastSaved=i?new Date(i.timestamp):null,t}async hasStoredConfig(){try{return await this.loadConfig()!==null}catch(e){return console.error("妫�鏌ュ瓨鍌ㄩ厤缃椂鍑洪敊:",e),!1}}fileToArrayBuffer(e){return new Promise((t,i)=>{const o=new FileReader;o.onload=()=>t(o.result),o.onerror=()=>i(new Error("璇诲彇鏂囦欢澶辫触")),o.readAsArrayBuffer(e)})}close(){this.db&&(this.db.close(),this.db=null,this.initialized=!1,console.log("IndexedDB 杩炴帴宸插叧闂�"))}}const Qe=new G0;class si{static async saveFontFile(e,t={}){if(e){const i="custom_font";try{await Qe.saveFile(i,e,"font",{size:t.size||20,bpp:t.bpp||4,charset:t.charset||"deepseek"}),console.log(`瀛椾綋鏂囦欢宸蹭繚瀛�: ${e.name}`)}catch(o){console.warn(`淇濆瓨瀛椾綋鏂囦欢澶辫触: ${e.name}`,o)}}}static async saveEmojiFile(e,t,i={}){var o,n;if(t&&e){const r=e.startsWith("hash_")?e:`emoji_${e}`;try{const g=((o=i==null?void 0:i.size)==null?void 0:o.width)??64,s=((n=i==null?void 0:i.size)==null?void 0:n.height)??64;await Qe.saveFile(r,t,"emoji",{name:e,size:{width:g,height:s},format:i==null?void 0:i.format,emotions:i==null?void 0:i.emotions}),console.log(`琛ㄦ儏鏂囦欢宸蹭繚瀛�: ${r} - ${t.name}`)}catch(g){console.warn(`淇濆瓨琛ㄦ儏鏂囦欢澶辫触: ${e}`,g)}}}static async saveBackgroundFile(e,t,i={}){if(t&&e){const o=`background_${e}`;try{let n={};try{n=i?JSON.parse(JSON.stringify(i)):{}}catch{n={...i}}await Qe.saveFile(o,t,"background",{mode:e,...n}),console.log(`鑳屾櫙鏂囦欢宸蹭繚瀛�: ${e} - ${t.name}`)}catch(n){console.warn(`淇濆瓨鑳屾櫙鏂囦欢澶辫触: ${e}`,n)}}}static async restoreFontFile(){try{return await Qe.loadFile("custom_font")}catch(e){return console.warn("鎭㈠瀛椾綋鏂囦欢澶辫触:",e),null}}static async restoreEmojiFile(e){if(!e)return null;try{const t=e.startsWith("hash_")?e:`emoji_${e}`;return await Qe.loadFile(t)}catch(t){return console.warn(`鎭㈠琛ㄦ儏鏂囦欢澶辫触: ${e}`,t),null}}static async restoreBackgroundFile(e){if(!e)return null;try{const t=`background_${e}`;return await Qe.loadFile(t)}catch(t){return console.warn(`鎭㈠鑳屾櫙鏂囦欢澶辫触: ${e}`,t),null}}static async deleteFontFile(){try{await Qe.deleteFile("custom_font"),console.log("瀛椾綋鏂囦欢宸插垹闄�")}catch(e){console.warn("鍒犻櫎瀛椾綋鏂囦欢澶辫触:",e)}}static async deleteEmojiFile(e){if(e)try{const t=e.startsWith("hash_")?e:`emoji_${e}`;await Qe.deleteFile(t),console.log(`琛ㄦ儏鏂囦欢宸插垹闄�: ${t}`)}catch(t){console.warn(`鍒犻櫎琛ㄦ儏鏂囦欢澶辫触: ${e}`,t)}}static async deleteBackgroundFile(e){if(e)try{const t=`background_${e}`;await Qe.deleteFile(t),console.log(`鑳屾櫙鏂囦欢宸插垹闄�: ${e}`)}catch(t){console.warn(`鍒犻櫎鑳屾櫙鏂囦欢澶辫触: ${e}`,t)}}static async getStorageInfo(){try{return await Qe.getStorageInfo()}catch(e){return console.warn("鑾峰彇瀛樺偍淇℃伅澶辫触:",e),{configs:{count:0},files:{count:0},temp_data:{count:0},lastSaved:null}}}static async clearAllFiles(){try{await Qe.clearAll(),console.log("鎵�鏈夊瓨鍌ㄦ枃浠跺凡娓呯悊")}catch(e){throw console.warn("娓呯悊瀛樺偍鏂囦欢澶辫触:",e),e}}}const L0={class:"space-y-6"},U0={class:"text-lg font-medium text-gray-900 mb-2"},M0={class:"text-gray-600"},x0={class:"space-y-4"},J0={class:"flex space-x-4"},T0={key:0,class:"space-y-4"},H0={class:"bg-gray-50 border border-gray-200 rounded-lg p-6 text-center"},Y0={class:"mt-2 text-gray-600"},_0={key:1,class:"space-y-4"},K0={class:"font-medium text-gray-900"},O0={class:"grid grid-cols-1 md:grid-cols-4 gap-4"},P0=["onClick"],V0={class:"flex items-start justify-between"},W0={class:"flex-1"},q0={class:"font-medium text-gray-900 mb-1"},j0={class:"text-sm text-gray-600 space-y-1"},Z0={key:0,class:"flex-shrink-0 ml-3"},X0={key:2,class:"space-y-6"},z0={class:"font-medium text-gray-900"},$0={class:"space-y-2"},Ap={class:"block text-sm font-medium text-gray-700"},ep={key:0},tp={class:"mt-2"},ip={class:"text-gray-500"},op={class:"text-xs text-gray-400 mt-1"},np={key:1,class:"text-green-600"},rp={class:"mt-1 font-medium"},gp={key:0,class:"grid grid-cols-1 md:grid-cols-3 gap-4"},sp={class:"block text-sm font-medium text-gray-700 mb-2"},ap={class:"text-xs text-gray-500 mt-1"},Ip={class:"block text-sm font-medium text-gray-700 mb-2"},Cp={value:1},lp={value:2},Bp={value:4},Qp={class:"block text-sm font-medium text-gray-700 mb-2"},cp={value:"qwen"},Ep={value:"deepseek"},up={value:"gb2312"},fp={value:"latin"},hp={value:"full"},dp={class:"flex items-center justify-between p-4 bg-gray-50 rounded-lg border border-gray-200"},pp={class:"flex-1"},mp={class:"font-medium text-gray-900"},yp={class:"text-sm text-gray-500 mt-1"},wp={class:"ml-4"},Dp={class:"relative inline-flex items-center cursor-pointer"},kp=["checked"],vp={__name:"FontConfig",props:{modelValue:{type:Object,required:!0}},emits:["update:modelValue"],setup(A,{emit:e}){const{t}=we(),i=A,o=e,n=uA(null),r=[{id:"font_puhui_deepseek_14_1",size:14,bpp:1,fileSize:"~180KB"},{id:"font_puhui_deepseek_16_4",size:16,bpp:4,fileSize:"~720KB"},{id:"font_puhui_deepseek_20_4",size:20,bpp:4,fileSize:"~1.1MB"},{id:"font_puhui_deepseek_30_4",size:30,bpp:4,fileSize:"~2.5MB"},{id:"font_noto_qwen_14_1",size:14,bpp:1,fileSize:"~626KB"},{id:"font_noto_qwen_16_4",size:16,bpp:4,fileSize:"~2.0MB"},{id:"font_noto_qwen_20_4",size:20,bpp:4,fileSize:"~2.9MB"},{id:"font_noto_qwen_30_4",size:30,bpp:4,fileSize:"~5.8MB"}],g=hA(()=>r.map(u=>{let E;return u.id.startsWith("font_puhui_deepseek_")?E=t("fontConfig.charsetDeepseek"):u.id.startsWith("font_noto_qwen_")&&(E=t("fontConfig.charsetQwen")),{...u,name:t("fontConfig.presetFontNames."+u.id),charset:E}})),s=uA({size:20,bpp:4,charset:"qwen"}),a=u=>{o("update:modelValue",{...i.modelValue,type:u,preset:u==="preset"?i.modelValue.preset||"font_puhui_deepseek_20_4":"",custom:{...i.modelValue.custom,file:i.modelValue.custom.file||null}})},I=u=>{o("update:modelValue",{...i.modelValue,preset:u,custom:{...i.modelValue.custom,file:i.modelValue.custom.file||null}})},l=u=>{const E=u.target.files[0];E&&Q(E)},C=u=>{u.preventDefault();const E=u.dataTransfer.files;E.length>0&&Q(E[0])},Q=async u=>{u&&(u.type.includes("font")||u.name.toLowerCase().match(/\.(ttf|woff|woff2)$/))?(o("update:modelValue",{...i.modelValue,custom:{...i.modelValue.custom,file:u,...s.value}}),await si.saveFontFile(u,s.value)):alert(t("fontConfig.selectValidFontFile"))},f=async()=>{o("update:modelValue",{...i.modelValue,custom:{...i.modelValue.custom,file:null}}),n.value&&(n.value.value=""),await si.deleteFontFile()},h=uA(!1);return ae(()=>s.value,u=>{h.value||i.modelValue.type==="custom"&&i.modelValue.custom.file&&o("update:modelValue",{...i.modelValue,custom:{...i.modelValue.custom,...u}})},{deep:!0}),ae(()=>i.modelValue.custom,u=>{u.size&&u.bpp&&u.charset&&(h.value=!0,s.value={size:u.size,bpp:u.bpp,charset:u.charset},uo(()=>{h.value=!1}))},{deep:!0,immediate:!0}),(u,E)=>{var D;return T(),K("div",L0,[B("div",null,[B("h3",U0,F(u.$t("fontConfig.title")),1),B("p",M0,F(u.$t("fontConfig.description")),1)]),B("div",x0,[B("div",J0,[B("button",{onClick:E[0]||(E[0]=c=>a("none")),class:kA(["px-4 py-2 border rounded-lg transition-colors",A.modelValue.type==="none"?"border-primary-500 bg-primary-50 text-primary-700":"border-gray-300 hover:border-gray-400"])},F(u.$t("fontConfig.noFonts")),3),B("button",{onClick:E[1]||(E[1]=c=>a("preset")),class:kA(["px-4 py-2 border rounded-lg transition-colors",A.modelValue.type==="preset"?"border-primary-500 bg-primary-50 text-primary-700":"border-gray-300 hover:border-gray-400"])},F(u.$t("fontConfig.presetFonts")),3),B("button",{onClick:E[2]||(E[2]=c=>a("custom")),class:kA(["px-4 py-2 border rounded-lg transition-colors",A.modelValue.type==="custom"?"border-primary-500 bg-primary-50 text-primary-700":"border-gray-300 hover:border-gray-400"])},F(u.$t("fontConfig.customFonts")),3)])]),A.modelValue.type==="none"?(T(),K("div",T0,[B("div",H0,[E[10]||(E[10]=B("svg",{class:"mx-auto h-12 w-12 text-gray-400",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor"},[B("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M6 18L18 6M6 6l12 12"})],-1)),B("p",Y0,F(u.$t("fontConfig.noFontsDescription")),1)])])):CA("",!0),A.modelValue.type==="preset"?(T(),K("div",_0,[B("h4",K0,F(u.$t("fontConfig.presetFonts")),1),B("div",O0,[(T(!0),K(qA,null,Ke(g.value,c=>(T(),K("div",{key:c.id,onClick:p=>I(c.id),class:kA(["border-2 rounded-lg p-4 cursor-pointer transition-all",A.modelValue.preset===c.id?"border-primary-500 bg-primary-50":"border-gray-200 hover:border-gray-300"])},[B("div",V0,[B("div",W0,[B("h5",q0,F(c.name),1),B("div",j0,[B("div",null,F(u.$t("fontConfig.fontSize"))+" "+F(c.size)+"px",1),B("div",null,F(u.$t("fontConfig.bitDepth"))+" "+F(c.bpp)+"bpp",1),B("div",null,F(u.$t("fontConfig.charset"))+" "+F(c.charset),1),B("div",null,F(u.$t("fontConfig.fileSize"))+" "+F(c.fileSize),1)])]),A.modelValue.preset===c.id?(T(),K("div",Z0,[...E[11]||(E[11]=[B("div",{class:"w-5 h-5 bg-primary-500 rounded-full flex items-center justify-center"},[B("svg",{class:"w-3 h-3 text-white",fill:"currentColor",viewBox:"0 0 20 20"},[B("path",{"fill-rule":"evenodd",d:"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z","clip-rule":"evenodd"})])],-1)])])):CA("",!0)])],10,P0))),128))])])):CA("",!0),A.modelValue.type==="custom"?(T(),K("div",X0,[B("h4",z0,F(u.$t("fontConfig.customFonts")),1),B("div",$0,[B("label",Ap,F(u.$t("fontConfig.fontFile")),1),B("div",{onDrop:C,onDragover:E[4]||(E[4]=dt(()=>{},["prevent"])),onDragenter:E[5]||(E[5]=dt(()=>{},["prevent"])),class:"border-2 border-dashed border-gray-300 rounded-lg p-6 text-center hover:border-gray-400 transition-colors"},[B("input",{ref_key:"fileInput",ref:n,type:"file",accept:".ttf,.woff,.woff2",onChange:l,class:"hidden"},null,544),A.modelValue.custom.file?(T(),K("div",np,[E[13]||(E[13]=B("svg",{class:"mx-auto h-8 w-8",fill:"currentColor",viewBox:"0 0 20 20"},[B("path",{"fill-rule":"evenodd",d:"M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z","clip-rule":"evenodd"})],-1)),B("p",rp,F((D=A.modelValue.custom.file)==null?void 0:D.name),1),B("button",{onClick:f,class:"text-red-600 hover:text-red-500 text-sm mt-1"},F(u.$t("fontConfig.removeFile")),1)])):(T(),K("div",ep,[E[12]||(E[12]=B("svg",{class:"mx-auto h-12 w-12 text-gray-400",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor"},[B("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"})],-1)),B("div",tp,[B("button",{onClick:E[3]||(E[3]=c=>u.$refs.fileInput.click()),class:"text-primary-600 hover:text-primary-500 font-medium"},F(u.$t("fontConfig.clickSelectFontFile")),1),B("p",ip,F(u.$t("fontConfig.orDragFileHere")),1)]),B("p",op,F(u.$t("fontConfig.supportedFormats")),1)]))],32)]),A.modelValue.custom.file?(T(),K("div",gp,[B("div",null,[B("label",sp,F(u.$t("fontConfig.fontSizePx")),1),ZA(B("input",{type:"number","onUpdate:modelValue":E[6]||(E[6]=c=>s.value.size=c),min:"8",max:"80",class:"w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-primary-500"},null,512),[[te,s.value.size,void 0,{number:!0}]]),B("p",ap,F(u.$t("fontConfig.range"))+" 8-80px",1)]),B("div",null,[B("label",Ip,F(u.$t("fontConfig.bitDepthBpp")),1),ZA(B("select",{"onUpdate:modelValue":E[7]||(E[7]=c=>s.value.bpp=c),class:"w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-primary-500"},[B("option",Cp,F(u.$t("fontConfig.monochrome")),1),B("option",lp,F(u.$t("fontConfig.fourColors")),1),B("option",Bp,F(u.$t("fontConfig.sixteenColors")),1)],512),[[pr,s.value.bpp]])]),B("div",null,[B("label",Qp,F(u.$t("fontConfig.characterSet")),1),ZA(B("select",{"onUpdate:modelValue":E[8]||(E[8]=c=>s.value.charset=c),class:"w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-primary-500"},[B("option",cp,F(u.$t("fontConfig.qwen18409")),1),B("option",Ep,F(u.$t("fontConfig.deepseekR1")),1),B("option",up,F(u.$t("fontConfig.gb2312")),1),B("option",fp,F(u.$t("fontConfig.latin1")),1),B("option",hp,F(u.$t("fontConfig.fullCharset")),1)],512),[[pr,s.value.charset]])])])):CA("",!0)])):CA("",!0),B("div",dp,[B("div",pp,[B("h4",mp,F(u.$t("fontConfig.hideSubtitle")),1),B("p",yp,F(u.$t("fontConfig.hideSubtitleDescription")),1)]),B("div",wp,[B("label",Dp,[B("input",{type:"checkbox",checked:A.modelValue.hide_subtitle,onChange:E[9]||(E[9]=c=>o("update:modelValue",{...A.modelValue,hide_subtitle:c.target.checked})),class:"sr-only peer"},null,40,kp),E[14]||(E[14]=B("div",{class:"w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-primary-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-primary-500"},null,-1))])])])])}}},Fp={class:"space-y-6"},Sp={class:"text-lg font-medium text-gray-900 mb-2"},Rp={class:"text-gray-600"},bp={class:"space-y-4"},Np={class:"flex flex-wrap gap-3"},Gp={key:0,class:"text-sm text-gray-500"},Lp={key:0,class:"space-y-4"},Up={class:"font-medium text-gray-900"},Mp={class:"grid grid-cols-1 md:grid-cols-2 gap-6"},xp=["onClick"],Jp={class:"flex items-start justify-between mb-3"},Tp={class:"font-medium text-gray-900"},Hp={class:"text-sm text-gray-600"},Yp={class:"text-xs text-gray-500 mt-1"},_p={key:0,class:"flex-shrink-0 ml-3"},Kp={class:"grid grid-cols-7 gap-1 justify-items-center overflow-hidden"},Op=["src","alt"],Pp={key:1,class:"space-y-6"},Vp={class:"font-medium text-gray-900"},Wp={class:"grid grid-cols-1 md:grid-cols-2 gap-4"},qp={class:"block text-sm font-medium text-gray-700 mb-2"},jp={class:"block text-sm font-medium text-gray-700 mb-2"},Zp={class:"space-y-4"},Xp={class:"font-medium text-gray-900"},zp={class:"grid grid-cols-2 md:grid-cols-4 lg:grid-cols-7 gap-4"},$p={class:"text-center"},Am={class:"text-lg mb-1"},em={class:"text-xs text-gray-600 flex items-center justify-center gap-1"},tm={key:0,class:"text-red-500"},im=["onDrop"],om=["onChange"],nm=["onClick"],rm={class:"text-xs text-gray-500"},gm={key:1,class:"w-full h-full relative"},sm=["src","alt"],am=["onClick"],Im={class:"text-xs text-gray-500 mt-2"},Cm={__name:"EmojiConfig",props:{modelValue:{type:Object,required:!0}},emits:["update:modelValue"],setup(A,{emit:e}){const{t}=we(),i=A,o=e,n=async c=>{var d;const p=await c.arrayBuffer();if((d=crypto==null?void 0:crypto.subtle)!=null&&d.digest)try{const v=await crypto.subtle.digest("SHA-256",p);return Array.from(new Uint8Array(v)).map(N=>N.toString(16).padStart(2,"0")).join("")}catch(v){console.warn("浣跨敤 crypto.subtle.digest 澶辫触锛屽洖閫�鍒拌嚜瀹氫箟鍝堝笇鍑芥暟:",v)}return r(p)},r=c=>{const p=new Uint8Array(c);let d=2166136261,v=16777619;for(let H=0;H<p.length;H++)d=(d^p[H])*v,d=d&4294967295;let b=0;for(let H=0;H<p.length;H++)b=b*31+p[H]&4294967295;return((d^b)>>>0).toString(16).padStart(8,"0")},g=[{id:"twemoji32",name:t("emojiConfig.twitterEmojiName",{size:32}),description:t("emojiConfig.twitterEmojiDescription",{size:32}),size:32,format:"png",preview:["neutral","happy","laughing","funny","sad","angry","crying"]},{id:"twemoji64",name:t("emojiConfig.twitterEmojiName",{size:64}),description:t("emojiConfig.twitterEmojiDescription",{size:64}),size:64,format:"png",preview:["neutral","happy","laughing","funny","sad","angry","crying"]},{id:"noto-emoji_64",name:t("emojiConfig.notoEmojiName",{size:64}),description:t("emojiConfig.notoEmojiDescription",{size:64}),size:64,format:"gif",preview:["neutral","happy","laughing","funny","sad","angry","crying"]},{id:"noto-emoji_128",name:t("emojiConfig.notoEmojiName",{size:128}),description:t("emojiConfig.notoEmojiDescription",{size:128}),size:128,format:"gif",preview:["neutral","happy","laughing","funny","sad","angry","crying"]}],s=hA(()=>[{key:"neutral",name:t("emojiConfig.emotions.neutral"),emoji:"馃樁"},{key:"happy",name:t("emojiConfig.emotions.happy"),emoji:"馃檪"},{key:"laughing",name:t("emojiConfig.emotions.laughing"),emoji:"馃槅"},{key:"funny",name:t("emojiConfig.emotions.funny"),emoji:"馃槀"},{key:"sad",name:t("emojiConfig.emotions.sad"),emoji:"馃様"},{key:"angry",name:t("emojiConfig.emotions.angry"),emoji:"馃槧"},{key:"crying",name:t("emojiConfig.emotions.crying"),emoji:"馃槶"},{key:"loving",name:t("emojiConfig.emotions.loving"),emoji:"馃槏"},{key:"embarrassed",name:t("emojiConfig.emotions.embarrassed"),emoji:"馃槼"},{key:"surprised",name:t("emojiConfig.emotions.surprised"),emoji:"馃槸"},{key:"shocked",name:t("emojiConfig.emotions.shocked"),emoji:"馃槺"},{key:"thinking",name:t("emojiConfig.emotions.thinking"),emoji:"馃"},{key:"winking",name:t("emojiConfig.emotions.winking"),emoji:"馃槈"},{key:"cool",name:t("emojiConfig.emotions.cool"),emoji:"馃槑"},{key:"relaxed",name:t("emojiConfig.emotions.relaxed"),emoji:"馃槍"},{key:"delicious",name:t("emojiConfig.emotions.delicious"),emoji:"馃い"},{key:"kissy",name:t("emojiConfig.emotions.kissy"),emoji:"馃槝"},{key:"confident",name:t("emojiConfig.emotions.confident"),emoji:"馃槒"},{key:"sleepy",name:t("emojiConfig.emotions.sleepy"),emoji:"馃槾"},{key:"silly",name:t("emojiConfig.emotions.silly"),emoji:"馃槣"},{key:"confused",name:t("emojiConfig.emotions.confused"),emoji:"馃檮"}]),a=uA({size:{width:32,height:32}}),I=c=>{if(i.modelValue.type===c)return;const p={...i.modelValue,type:c};c==="none"?(p.preset="",p.custom={...i.modelValue.custom,images:i.modelValue.custom.images||{}}):c==="preset"?(p.preset=i.modelValue.preset||"twemoji32",p.custom={...i.modelValue.custom,images:i.modelValue.custom.images||{}}):c==="custom"&&(p.preset="",p.custom={...i.modelValue.custom,images:i.modelValue.custom.images||{}}),o("update:modelValue",p)},l=c=>{i.modelValue.preset!==c&&o("update:modelValue",{...i.modelValue,preset:c,custom:{...i.modelValue.custom,images:i.modelValue.custom.images||{}}})},C=(c,p)=>{const d=c.target.files[0];d&&f(p,d)},Q=(c,p)=>{c.preventDefault();const d=c.dataTransfer.files;d.length>0&&f(p,d[0])},f=async(c,p)=>{const d=["png","gif"],v=p.name.split(".").pop().toLowerCase();if(!d.includes(v)){alert(t("emojiConfig.selectValidFormat"));return}const b=await n(p),N=i.modelValue.custom||{},H={...N.fileMap||{}},M={...N.emotionMap||{}},U={...N.images||{}};let R=[];for(const[j,rA]of Object.entries(M))rA===b&&j!==c&&R.push(j);R.length>0&&console.log(t("emojiConfig.sharedFileMessage",{emotionKey:c,existingEmotions:R.join(", ")})),H[b]=p,M[c]=b,U[c]=p,o("update:modelValue",{...i.modelValue,custom:{...N,size:a.value.size,images:U,fileMap:H,emotionMap:M}}),await si.saveEmojiFile(`hash_${b}`,p,{size:a.value.size,format:v,emotions:[...R,c]})},h=async c=>{const p=i.modelValue.custom||{},d={...p.images},v={...p.emotionMap||{}},b={...p.fileMap||{}},N=v[c];delete d[c],delete v[c],Object.values(v).filter(M=>M===N).length===0&&N?(delete b[N],await si.deleteEmojiFile(`hash_${N}`),console.log(t("emojiConfig.fileDeleted",{fileHash:N}))):console.log(t("emojiConfig.fileRetained",{fileHash:N})),o("update:modelValue",{...i.modelValue,custom:{...p,images:d,emotionMap:v,fileMap:b}})},u=(c,p)=>{const d=g.find(b=>b.id===c),v=(d==null?void 0:d.format)||"png";return`./static/emojis/${c}/${p}.${v}`},E=c=>{if(i.modelValue.type==="preset")return u(i.modelValue.preset,c);{const p=i.modelValue.custom.images[c];return p instanceof File||p instanceof Blob?URL.createObjectURL(p):null}},D=c=>{console.warn(t("emojiConfig.imageLoadFailed"),c.target.src),c.target.style.display="none"};return ae(()=>a.value.size,c=>{if(i.modelValue.type==="custom"){const p=i.modelValue.custom;JSON.stringify(p.size)!==JSON.stringify(c)&&o("update:modelValue",{...i.modelValue,custom:{...p,size:c}})}},{deep:!0}),i.modelValue.custom.size&&(a.value={size:{...i.modelValue.custom.size}}),(c,p)=>(T(),K("div",Fp,[B("div",null,[B("h3",Sp,F(c.$t("emojiConfig.title")),1),B("p",Rp,F(c.$t("emojiConfig.description")),1)]),B("div",bp,[B("div",Np,[B("button",{onClick:p[0]||(p[0]=d=>I("none")),class:kA(["px-4 py-2 border rounded-lg transition-colors",A.modelValue.type==="none"?"border-primary-500 bg-primary-50 text-primary-700":"border-gray-300 hover:border-gray-400"])},F(c.$t("emojiConfig.noEmojiPack")),3),B("button",{onClick:p[1]||(p[1]=d=>I("preset")),class:kA(["px-4 py-2 border rounded-lg transition-colors",A.modelValue.type==="preset"?"border-primary-500 bg-primary-50 text-primary-700":"border-gray-300 hover:border-gray-400"])},F(c.$t("emojiConfig.presetEmojiPack")),3),B("button",{onClick:p[2]||(p[2]=d=>I("custom")),class:kA(["px-4 py-2 border rounded-lg transition-colors",A.modelValue.type==="custom"?"border-primary-500 bg-primary-50 text-primary-700":"border-gray-300 hover:border-gray-400"])},F(c.$t("emojiConfig.customEmojiPack")),3)]),A.modelValue.type==="none"?(T(),K("p",Gp,F(c.$t("emojiConfig.noEmojiPackDescription")),1)):CA("",!0)]),A.modelValue.type==="preset"?(T(),K("div",Lp,[B("h4",Up,F(c.$t("emojiConfig.selectPresetEmojiPack")),1),B("div",Mp,[(T(),K(qA,null,Ke(g,d=>B("div",{key:d.id,onClick:v=>l(d.id),class:kA(["border-2 rounded-lg p-4 cursor-pointer transition-all",A.modelValue.preset===d.id?"border-primary-500 bg-primary-50":"border-gray-200 hover:border-gray-300"])},[B("div",Jp,[B("div",null,[B("h5",Tp,F(d.name),1),B("p",Hp,F(d.description),1),B("div",Yp,F(c.$t("emojiConfig.size"))+": "+F(d.size)+"px 脳 "+F(d.size)+"px ",1)]),A.modelValue.preset===d.id?(T(),K("div",_p,[...p[7]||(p[7]=[B("div",{class:"w-5 h-5 bg-primary-500 rounded-full flex items-center justify-center"},[B("svg",{class:"w-3 h-3 text-white",fill:"currentColor",viewBox:"0 0 20 20"},[B("path",{"fill-rule":"evenodd",d:"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z","clip-rule":"evenodd"})])],-1)])])):CA("",!0)]),B("div",Kp,[(T(!0),K(qA,null,Ke(d.preview,v=>(T(),K("div",{key:v,style:VA({width:d.size+"px",height:d.size+"px"}),class:"bg-gray-100 rounded flex items-center justify-center"},[B("img",{src:u(d.id,v),alt:v,style:VA({width:d.size+"px",height:d.size+"px"}),class:"object-contain rounded",onError:D},null,44,Op)],4))),128))])],10,xp)),64))])])):CA("",!0),A.modelValue.type==="custom"?(T(),K("div",Pp,[B("h4",Vp,F(c.$t("emojiConfig.customEmojiPackConfig")),1),B("div",Wp,[B("div",null,[B("label",qp,F(c.$t("emojiConfig.maxImageWidth")),1),ZA(B("input",{type:"number","onUpdate:modelValue":p[3]||(p[3]=d=>a.value.size.width=d),min:"16",max:"200",class:"w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-primary-500"},null,512),[[te,a.value.size.width,void 0,{number:!0}]])]),B("div",null,[B("label",jp,F(c.$t("emojiConfig.maxImageHeight")),1),ZA(B("input",{type:"number","onUpdate:modelValue":p[4]||(p[4]=d=>a.value.size.height=d),min:"16",max:"200",class:"w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-primary-500"},null,512),[[te,a.value.size.height,void 0,{number:!0}]])])]),B("div",Zp,[B("h5",Xp,F(c.$t("emojiConfig.uploadEmojiImages")),1),B("div",zp,[(T(!0),K(qA,null,Ke(s.value,d=>(T(),K("div",{key:d.key,class:"space-y-2"},[B("div",$p,[B("div",Am,F(d.emoji),1),B("div",em,[B("span",null,F(d.name),1),d.key==="neutral"?(T(),K("span",tm,F(c.$t("emojiConfig.required")),1)):CA("",!0)])]),B("div",{onDrop:v=>Q(v,d.key),onDragover:p[5]||(p[5]=dt(()=>{},["prevent"])),onDragenter:p[6]||(p[6]=dt(()=>{},["prevent"])),class:kA(["border-2 border-dashed rounded-lg p-2 text-center cursor-pointer transition-colors aspect-square flex flex-col items-center justify-center",A.modelValue.custom.images[d.key]?"border-green-300 bg-green-50":d.key==="neutral"?"border-red-300 bg-red-50":"border-gray-300 hover:border-gray-400"])},[B("input",{ref_for:!0,ref:d.key+"Input",type:"file",accept:".png,.gif",onChange:v=>C(v,d.key),class:"hidden"},null,40,om),A.modelValue.custom.images[d.key]?(T(),K("div",gm,[E(d.key)?(T(),K("img",{key:0,src:E(d.key),alt:d.name,class:"w-full h-full object-cover rounded",onError:D},null,40,sm)):CA("",!0),B("button",{onClick:v=>h(d.key),class:"absolute -top-1 -right-1 w-4 h-4 bg-red-500 text-white rounded-full flex items-center justify-center text-xs hover:bg-red-600"}," 脳 ",8,am)])):(T(),K("div",{key:0,onClick:v=>{var b;return(b=c.$refs[d.key+"Input"][0])==null?void 0:b.click()}},[p[8]||(p[8]=B("svg",{class:"w-6 h-6 text-gray-400 mx-auto mb-1",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor"},[B("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M12 6v6m0 0v6m0-6h6m-6 0H6"})],-1)),B("div",rm,F(c.$t("emojiConfig.clickToUploadOrDrag")),1)],8,nm))],42,im)]))),128))]),B("div",Im,F(c.$t("emojiConfig.neutralRequiredNotice")),1)])])):CA("",!0)]))}},lm={class:"space-y-6"},Bm={class:"text-lg font-medium text-gray-900 mb-2"},Qm={class:"text-gray-600"},cm={class:"space-y-4"},Em={class:"font-medium text-gray-900 flex items-center"},um={class:"border border-gray-200 rounded-lg p-4 space-y-4"},fm={class:"flex space-x-4"},hm={key:0,class:"space-y-3"},dm={class:"flex items-center space-x-3"},pm={class:"text-sm font-medium text-gray-700"},mm={class:"flex items-center space-x-2"},ym={class:"flex items-center space-x-3"},wm={class:"text-sm font-medium text-gray-700"},Dm={class:"flex items-center space-x-2"},km={key:1,class:"space-y-3"},vm={class:"text-sm font-medium text-gray-700"},Fm={class:"mt-1 text-sm text-gray-600"},Sm={key:1,class:"space-y-2"},Rm=["src"],bm={class:"text-sm text-green-700 font-medium"},Nm={class:"flex items-center space-x-3"},Gm={class:"text-sm font-medium text-gray-700"},Lm={class:"flex items-center space-x-2"},Um={class:"space-y-4"},Mm={class:"font-medium text-gray-900 flex items-center"},xm={class:"border border-gray-200 rounded-lg p-4 space-y-4"},Jm={class:"flex space-x-4"},Tm={key:0,class:"space-y-3"},Hm={class:"flex items-center space-x-3"},Ym={class:"text-sm font-medium text-gray-700"},_m={class:"flex items-center space-x-2"},Km={class:"flex items-center space-x-3"},Om={class:"text-sm font-medium text-gray-700"},Pm={class:"flex items-center space-x-2"},Vm={key:1,class:"space-y-3"},Wm={class:"text-sm font-medium text-gray-700"},qm={class:"mt-1 text-sm text-gray-600"},jm={key:1,class:"space-y-2"},Zm=["src"],Xm={class:"text-sm text-green-700 font-medium"},zm={class:"flex items-center space-x-3"},$m={class:"text-sm font-medium text-gray-700"},Ay={class:"flex items-center space-x-2"},ey={class:"space-y-4"},ty={class:"font-medium text-gray-900"},iy={class:"grid grid-cols-1 md:grid-cols-2 gap-4"},oy={class:"space-y-2"},ny={class:"text-sm font-medium text-gray-700"},ry={class:"absolute inset-0 bg-white bg-opacity-10 flex items-center justify-center rounded-lg"},gy={class:"space-y-2"},sy={class:"text-sm font-medium text-gray-700"},ay={class:"absolute inset-0 bg-black bg-opacity-10 flex items-center justify-center rounded-lg"},Iy={class:"bg-blue-50 border border-blue-200 rounded-lg p-4"},Cy={class:"font-medium text-blue-900 mb-2"},ly={class:"flex flex-wrap gap-2"},By={__name:"BackgroundConfig",props:{modelValue:{type:Object,required:!0}},emits:["update:modelValue"],setup(A,{emit:e}){const{t}=we(),i=A,o=e,n=uA(null),r=uA(null),g=hA({get:()=>i.modelValue.light.backgroundColor,set:M=>Q(M)}),s=hA({get:()=>i.modelValue.dark.backgroundColor,set:M=>f(M)}),a=hA({get:()=>i.modelValue.light.textColor,set:M=>h(M)}),I=hA({get:()=>i.modelValue.dark.textColor,set:M=>u(M)}),l=M=>{o("update:modelValue",{...i.modelValue,light:{...i.modelValue.light,backgroundType:M,backgroundImage:M==="image"?i.modelValue.light.backgroundImage:null}})},C=M=>{o("update:modelValue",{...i.modelValue,dark:{...i.modelValue.dark,backgroundType:M,backgroundImage:M==="image"?i.modelValue.dark.backgroundImage:null}})},Q=M=>{o("update:modelValue",{...i.modelValue,light:{...i.modelValue.light,backgroundColor:M}})},f=M=>{o("update:modelValue",{...i.modelValue,dark:{...i.modelValue.dark,backgroundColor:M}})},h=M=>{o("update:modelValue",{...i.modelValue,light:{...i.modelValue.light,textColor:M}})},u=M=>{o("update:modelValue",{...i.modelValue,dark:{...i.modelValue.dark,textColor:M}})},E=(M,U)=>{const R=M.target.files[0];R&&c(U,R)},D=(M,U)=>{M.preventDefault();const R=M.dataTransfer.files;R.length>0&&c(U,R[0])},c=async(M,U)=>{U&&U.type.startsWith("image/")?(o("update:modelValue",{...i.modelValue,[M]:{...i.modelValue[M],backgroundImage:U}}),await si.saveBackgroundFile(M,U)):alert(t("backgroundConfig.selectValidImage"))},p=async M=>{o("update:modelValue",{...i.modelValue,[M]:{...i.modelValue[M],backgroundImage:null}}),M==="light"&&n.value&&(n.value.value=""),M==="dark"&&r.value&&(r.value.value=""),await si.deleteBackgroundFile(M)},d=M=>{const U=i.modelValue[M].backgroundImage;return U?URL.createObjectURL(U):null},v=()=>i.modelValue.light.backgroundType==="image"&&i.modelValue.light.backgroundImage?{backgroundImage:`url(${d("light")})`,backgroundSize:"cover",backgroundPosition:"center"}:{backgroundColor:i.modelValue.light.backgroundColor},b=()=>i.modelValue.dark.backgroundType==="image"&&i.modelValue.dark.backgroundImage?{backgroundImage:`url(${d("dark")})`,backgroundSize:"cover",backgroundPosition:"center"}:{backgroundColor:i.modelValue.dark.backgroundColor},N=(M,U)=>{const R=H(M)?"#000000":"#ffffff",j=H(U)?"#000000":"#ffffff";o("update:modelValue",{...i.modelValue,light:{...i.modelValue.light,backgroundType:"color",backgroundColor:M,textColor:R,backgroundImage:null},dark:{...i.modelValue.dark,backgroundType:"color",backgroundColor:U,textColor:j,backgroundImage:null}})},H=M=>{const U=M.replace("#",""),R=parseInt(U.substr(0,2),16),j=parseInt(U.substr(2,2),16),rA=parseInt(U.substr(4,2),16);return(R*299+j*587+rA*114)/1e3>128};return(M,U)=>(T(),K("div",lm,[B("div",null,[B("h3",Bm,F(M.$t("backgroundConfig.title")),1),B("p",Qm,F(M.$t("backgroundConfig.description")),1)]),B("div",cm,[B("h4",Em,[U[33]||(U[33]=B("svg",{class:"w-5 h-5 mr-2 text-yellow-500",fill:"currentColor",viewBox:"0 0 20 20"},[B("path",{"fill-rule":"evenodd",d:"M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z","clip-rule":"evenodd"})],-1)),gt(" "+F(M.$t("backgroundConfig.lightMode")),1)]),B("div",um,[B("div",fm,[B("button",{onClick:U[0]||(U[0]=R=>l("color")),class:kA(["px-3 py-2 text-sm border rounded transition-colors",A.modelValue.light.backgroundType==="color"?"border-primary-500 bg-primary-50 text-primary-700":"border-gray-300 hover:border-gray-400"])},F(M.$t("backgroundConfig.solidBackground")),3),B("button",{onClick:U[1]||(U[1]=R=>l("image")),class:kA(["px-3 py-2 text-sm border rounded transition-colors",A.modelValue.light.backgroundType==="image"?"border-primary-500 bg-primary-50 text-primary-700":"border-gray-300 hover:border-gray-400"])},F(M.$t("backgroundConfig.imageBackground")),3)]),A.modelValue.light.backgroundType==="color"?(T(),K("div",hm,[B("div",dm,[B("label",pm,F(M.$t("backgroundConfig.backgroundColor")),1),B("div",mm,[ZA(B("input",{type:"color","onUpdate:modelValue":U[2]||(U[2]=R=>g.value=R),class:"w-10 h-10 border border-gray-300 rounded cursor-pointer"},null,512),[[te,g.value]]),ZA(B("input",{type:"text","onUpdate:modelValue":U[3]||(U[3]=R=>g.value=R),class:"border border-gray-300 rounded px-3 py-2 text-sm font-mono w-24"},null,512),[[te,g.value]])]),B("div",{style:VA({backgroundColor:g.value}),class:"w-16 h-10 border border-gray-300 rounded shadow-inner"},null,4)]),B("div",ym,[B("label",wm,F(M.$t("backgroundConfig.textColor")),1),B("div",Dm,[ZA(B("input",{type:"color","onUpdate:modelValue":U[4]||(U[4]=R=>a.value=R),class:"w-10 h-10 border border-gray-300 rounded cursor-pointer"},null,512),[[te,a.value]]),ZA(B("input",{type:"text","onUpdate:modelValue":U[5]||(U[5]=R=>a.value=R),class:"border border-gray-300 rounded px-3 py-2 text-sm font-mono w-24"},null,512),[[te,a.value]])]),B("div",{style:VA({backgroundColor:a.value}),class:"w-16 h-10 border border-gray-300 rounded shadow-inner"},null,4)])])):CA("",!0),A.modelValue.light.backgroundType==="image"?(T(),K("div",km,[B("label",vm,F(M.$t("backgroundConfig.backgroundImage")),1),B("div",{onDrop:U[9]||(U[9]=R=>D(R,"light")),onDragover:U[10]||(U[10]=dt(()=>{},["prevent"])),onDragenter:U[11]||(U[11]=dt(()=>{},["prevent"])),class:kA(["border-2 border-dashed rounded-lg p-4 text-center cursor-pointer transition-colors",A.modelValue.light.backgroundImage?"border-green-300 bg-green-50":"border-gray-300 hover:border-gray-400"])},[B("input",{ref_key:"lightImageInput",ref:n,type:"file",accept:"image/*",onChange:U[6]||(U[6]=R=>E(R,"light")),class:"hidden"},null,544),A.modelValue.light.backgroundImage?(T(),K("div",Sm,[B("img",{src:d("light"),alt:"{{ $t('backgroundConfig.lightModePreview') }}鑳屾櫙",class:"max-w-32 max-h-32 mx-auto rounded shadow"},null,8,Rm),B("p",bm,F(A.modelValue.light.backgroundImage.name),1),B("button",{onClick:U[8]||(U[8]=R=>p("light")),class:"text-red-600 hover:text-red-500 text-sm"},F(M.$t("backgroundConfig.removeImage")),1)])):(T(),K("div",{key:0,onClick:U[7]||(U[7]=R=>M.$refs.lightImageInput.click())},[U[34]||(U[34]=B("svg",{class:"mx-auto h-8 w-8 text-gray-400",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor"},[B("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"})],-1)),B("p",Fm,F(M.$t("backgroundConfig.clickOrDragToUpload")),1)]))],34),B("div",Nm,[B("label",Gm,F(M.$t("backgroundConfig.textColor")),1),B("div",Lm,[ZA(B("input",{type:"color","onUpdate:modelValue":U[12]||(U[12]=R=>a.value=R),class:"w-10 h-10 border border-gray-300 rounded cursor-pointer"},null,512),[[te,a.value]]),ZA(B("input",{type:"text","onUpdate:modelValue":U[13]||(U[13]=R=>a.value=R),class:"border border-gray-300 rounded px-3 py-2 text-sm font-mono w-24"},null,512),[[te,a.value]])]),B("div",{style:VA({backgroundColor:a.value}),class:"w-16 h-10 border border-gray-300 rounded shadow-inner"},null,4)])])):CA("",!0)])]),B("div",Um,[B("h4",Mm,[U[35]||(U[35]=B("svg",{class:"w-5 h-5 mr-2 text-gray-600",fill:"currentColor",viewBox:"0 0 20 20"},[B("path",{d:"M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"})],-1)),gt(" "+F(M.$t("backgroundConfig.darkMode")),1)]),B("div",xm,[B("div",Jm,[B("button",{onClick:U[14]||(U[14]=R=>C("color")),class:kA(["px-3 py-2 text-sm border rounded transition-colors",A.modelValue.dark.backgroundType==="color"?"border-primary-500 bg-primary-50 text-primary-700":"border-gray-300 hover:border-gray-400"])},F(M.$t("backgroundConfig.solidBackground")),3),B("button",{onClick:U[15]||(U[15]=R=>C("image")),class:kA(["px-3 py-2 text-sm border rounded transition-colors",A.modelValue.dark.backgroundType==="image"?"border-primary-500 bg-primary-50 text-primary-700":"border-gray-300 hover:border-gray-400"])},F(M.$t("backgroundConfig.imageBackground")),3)]),A.modelValue.dark.backgroundType==="color"?(T(),K("div",Tm,[B("div",Hm,[B("label",Ym,F(M.$t("backgroundConfig.backgroundColor")),1),B("div",_m,[ZA(B("input",{type:"color","onUpdate:modelValue":U[16]||(U[16]=R=>s.value=R),class:"w-10 h-10 border border-gray-300 rounded cursor-pointer"},null,512),[[te,s.value]]),ZA(B("input",{type:"text","onUpdate:modelValue":U[17]||(U[17]=R=>s.value=R),class:"border border-gray-300 rounded px-3 py-2 text-sm font-mono w-24"},null,512),[[te,s.value]])]),B("div",{style:VA({backgroundColor:s.value}),class:"w-16 h-10 border border-gray-300 rounded shadow-inner"},null,4)]),B("div",Km,[B("label",Om,F(M.$t("backgroundConfig.textColor")),1),B("div",Pm,[ZA(B("input",{type:"color","onUpdate:modelValue":U[18]||(U[18]=R=>I.value=R),class:"w-10 h-10 border border-gray-300 rounded cursor-pointer"},null,512),[[te,I.value]]),ZA(B("input",{type:"text","onUpdate:modelValue":U[19]||(U[19]=R=>I.value=R),class:"border border-gray-300 rounded px-3 py-2 text-sm font-mono w-24"},null,512),[[te,I.value]])]),B("div",{style:VA({backgroundColor:I.value}),class:"w-16 h-10 border border-gray-300 rounded shadow-inner"},null,4)])])):CA("",!0),A.modelValue.dark.backgroundType==="image"?(T(),K("div",Vm,[B("label",Wm,F(M.$t("backgroundConfig.backgroundImage")),1),B("div",{onDrop:U[23]||(U[23]=R=>D(R,"dark")),onDragover:U[24]||(U[24]=dt(()=>{},["prevent"])),onDragenter:U[25]||(U[25]=dt(()=>{},["prevent"])),class:kA(["border-2 border-dashed rounded-lg p-4 text-center cursor-pointer transition-colors",A.modelValue.dark.backgroundImage?"border-green-300 bg-green-50":"border-gray-300 hover:border-gray-400"])},[B("input",{ref_key:"darkImageInput",ref:r,type:"file",accept:"image/*",onChange:U[20]||(U[20]=R=>E(R,"dark")),class:"hidden"},null,544),A.modelValue.dark.backgroundImage?(T(),K("div",jm,[B("img",{src:d("dark"),alt:"{{ $t('backgroundConfig.darkModePreview') }}鑳屾櫙",class:"max-w-32 max-h-32 mx-auto rounded shadow"},null,8,Zm),B("p",Xm,F(A.modelValue.dark.backgroundImage.name),1),B("button",{onClick:U[22]||(U[22]=R=>p("dark")),class:"text-red-600 hover:text-red-500 text-sm"},F(M.$t("backgroundConfig.removeImage")),1)])):(T(),K("div",{key:0,onClick:U[21]||(U[21]=R=>M.$refs.darkImageInput.click())},[U[36]||(U[36]=B("svg",{class:"mx-auto h-8 w-8 text-gray-400",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor"},[B("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"})],-1)),B("p",qm,F(M.$t("backgroundConfig.clickOrDragToUpload")),1)]))],34),B("div",zm,[B("label",$m,F(M.$t("backgroundConfig.textColor")),1),B("div",Ay,[ZA(B("input",{type:"color","onUpdate:modelValue":U[26]||(U[26]=R=>I.value=R),class:"w-10 h-10 border border-gray-300 rounded cursor-pointer"},null,512),[[te,I.value]]),ZA(B("input",{type:"text","onUpdate:modelValue":U[27]||(U[27]=R=>I.value=R),class:"border border-gray-300 rounded px-3 py-2 text-sm font-mono w-24"},null,512),[[te,I.value]])]),B("div",{style:VA({backgroundColor:I.value}),class:"w-16 h-10 border border-gray-300 rounded shadow-inner"},null,4)])])):CA("",!0)])]),B("div",ey,[B("h4",ty,F(M.$t("backgroundConfig.backgroundPreview")),1),B("div",iy,[B("div",oy,[B("div",ny,F(M.$t("backgroundConfig.lightModePreview")),1),B("div",{style:VA(v()),class:"h-32 border border-gray-300 rounded-lg flex items-center justify-center text-sm relative overflow-hidden"},[B("div",ry,[B("span",{style:VA({color:A.modelValue.light.textColor})},F(M.$t("backgroundConfig.chatArea")),5)])],4)]),B("div",gy,[B("div",sy,F(M.$t("backgroundConfig.darkModePreview")),1),B("div",{style:VA(b()),class:"h-32 border border-gray-300 rounded-lg flex items-center justify-center text-sm relative overflow-hidden"},[B("div",ay,[B("span",{style:VA({color:A.modelValue.dark.textColor})},F(M.$t("backgroundConfig.chatArea")),5)])],4)])])]),B("div",Iy,[B("h5",Cy,F(M.$t("backgroundConfig.quickConfig")),1),B("div",ly,[B("button",{onClick:U[28]||(U[28]=R=>N("#ffffff","#1f2937")),class:"px-3 py-1 text-sm bg-white border border-gray-300 rounded hover:bg-gray-50"},F(M.$t("backgroundConfig.defaultColors")),1),B("button",{onClick:U[29]||(U[29]=R=>N("#f5f5f4","#374151")),class:"px-3 py-1 text-sm bg-stone-100 border border-gray-300 rounded hover:bg-stone-200"},F(M.$t("backgroundConfig.stoneTexture")),1),B("button",{onClick:U[30]||(U[30]=R=>N("#fef7cd","#7c2d12")),class:"px-3 py-1 text-sm bg-yellow-100 border border-gray-300 rounded hover:bg-yellow-200"},F(M.$t("backgroundConfig.sunnyColors")),1),B("button",{onClick:U[31]||(U[31]=R=>N("#e0f2fe","#1e40af")),class:"px-3 py-1 text-sm bg-sky-100 border border-gray-300 rounded hover:bg-sky-200"},F(M.$t("backgroundConfig.skyBlue")),1),B("button",{onClick:U[32]||(U[32]=R=>N("#fdf2f8","#be185d")),class:"px-3 py-1 text-sm bg-pink-100 border border-gray-300 rounded hover:bg-pink-200"},F(M.$t("backgroundConfig.romanticPink")),1)])])]))}},Qy={class:"space-y-6"},cy={class:"text-xl font-semibold text-gray-900 mb-4"},Ey={class:"text-gray-600 mb-6"},uy={class:"border-b border-gray-200"},fy={class:"-mb-px flex space-x-4 sm:space-x-8"},hy=["onClick"],dy={class:"flex items-center"},py={class:"hidden sm:inline"},my={class:"min-h-96"},yy={class:"flex justify-between"},wy={__name:"ThemeDesign",props:{modelValue:{type:Object,required:!0},chipModel:{type:String,required:!0},activeTab:{type:String,default:"wakeword"}},emits:["update:modelValue","next","prev","tabChange"],setup(A,{emit:e}){const{t}=we(),i={render(){return GA("svg",{fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[GA("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z"})])}},o={render(){return GA("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"currentColor",viewBox:"0 0 640 640"},[GA("path",{d:"M320 96C329.5 96 338 101.5 341.9 110.2L515.1 496L536 496C549.3 496 560 506.7 560 520C560 533.3 549.3 544 536 544L440 544C426.7 544 416 533.3 416 520C416 506.7 426.7 496 440 496L462.5 496L426.6 416L213.4 416L177.5 496L200 496C213.3 496 224 506.7 224 520C224 533.3 213.3 544 200 544L104 544C90.7 544 80 533.3 80 520C80 506.7 90.7 496 104 496L124.9 496L298.1 110.2C302 101.5 310.5 96 320 96zM320 178.6L235 368L405 368L320 178.6z"})])}},n={render(){return GA("svg",{fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[GA("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M14.828 14.828a4 4 0 01-5.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"})])}},r={render(){return GA("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"currentColor",viewBox:"0 0 640 640"},[GA("path",{d:"M160 144C151.2 144 144 151.2 144 160L144 480C144 488.8 151.2 496 160 496L480 496C488.8 496 496 488.8 496 480L496 160C496 151.2 488.8 144 480 144L160 144zM96 160C96 124.7 124.7 96 160 96L480 96C515.3 96 544 124.7 544 160L544 480C544 515.3 515.3 544 480 544L160 544C124.7 544 96 515.3 96 480L96 160zM224 192C241.7 192 256 206.3 256 224C256 241.7 241.7 256 224 256C206.3 256 192 241.7 192 224C192 206.3 206.3 192 224 192zM360 264C368.5 264 376.4 268.5 380.7 275.8L460.7 411.8C465.1 419.2 465.1 428.4 460.8 435.9C456.5 443.4 448.6 448 440 448L200 448C191.1 448 182.8 443 178.7 435.1C174.6 427.2 175.2 417.6 180.3 410.3L236.3 330.3C240.8 323.9 248.1 320.1 256 320.1C263.9 320.1 271.2 323.9 275.7 330.3L292.9 354.9L339.4 275.9C343.7 268.6 351.6 264.1 360.1 264.1z"})])}},g=A,s=e,a=uA(g.activeTab),I=hA(()=>[{id:"wakeword",name:t("themeDesign.tabs.wakeword"),icon:i},{id:"font",name:t("themeDesign.tabs.font"),icon:o},{id:"emoji",name:t("themeDesign.tabs.emoji"),icon:n},{id:"background",name:t("themeDesign.tabs.background"),icon:r}]),l=hA({get:()=>g.modelValue,set:f=>s("update:modelValue",f)}),C=()=>{var u,E,D;const f=g.modelValue.wakeword;if(f.type==="custom"){if(!((u=f.custom.name)!=null&&u.trim())){alert(t("wakewordConfig.errors.nameRequired"));return}if(!((E=f.custom.command)!=null&&E.trim())){alert(t("wakewordConfig.errors.commandRequired"));return}if(!f.custom.duration||f.custom.duration<500||f.custom.duration>1e4){alert(t("wakewordConfig.errors.durationRange"));return}}const h=g.modelValue.font;if(h.type==="custom"&&!((D=h.custom)!=null&&D.file)){alert(t("fontConfig.selectValidFontFile"));return}s("next")},Q=f=>{a.value=f,s("tabChange",f)};return ae(()=>g.activeTab,f=>{a.value=f}),(f,h)=>(T(),K("div",Qy,[B("div",null,[B("h2",cy,F(f.$t("themeDesign.title")),1),B("p",Ey,F(f.$t("themeDesign.description")),1)]),B("div",uy,[B("nav",fy,[(T(!0),K(qA,null,Ke(I.value,u=>(T(),K("button",{key:u.id,onClick:E=>Q(u.id),class:kA(["py-2 px-1 border-b-2 font-medium text-sm relative",a.value===u.id?"border-primary-500 text-primary-600":"border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"])},[B("span",dy,[(T(),ye(YI(u.icon),{class:"w-5 h-5 sm:mr-2"})),B("span",py,F(u.name),1)])],10,hy))),128))])]),B("div",my,[a.value==="wakeword"?(T(),ye(N0,{key:0,modelValue:l.value.wakeword,"onUpdate:modelValue":h[0]||(h[0]=u=>l.value.wakeword=u),chipModel:A.chipModel},null,8,["modelValue","chipModel"])):CA("",!0),a.value==="font"?(T(),ye(vp,{key:1,modelValue:l.value.font,"onUpdate:modelValue":h[1]||(h[1]=u=>l.value.font=u)},null,8,["modelValue"])):CA("",!0),a.value==="emoji"?(T(),ye(Cm,{key:2,modelValue:l.value.emoji,"onUpdate:modelValue":h[2]||(h[2]=u=>l.value.emoji=u)},null,8,["modelValue"])):CA("",!0),a.value==="background"?(T(),ye(By,{key:3,modelValue:l.value.skin,"onUpdate:modelValue":h[3]||(h[3]=u=>l.value.skin=u)},null,8,["modelValue"])):CA("",!0)]),B("div",yy,[B("button",{onClick:h[4]||(h[4]=u=>f.$emit("prev")),class:"bg-gray-500 hover:bg-gray-600 text-white px-6 py-2 rounded-lg font-medium transition-colors"},F(f.$t("themeDesign.previous")),1),B("button",{onClick:C,class:"bg-primary-500 hover:bg-primary-600 text-white px-6 py-2 rounded-lg font-medium transition-colors"},F(f.$t("themeDesign.next")),1)])]))}},Dy=(A,e)=>{const t=A.__vccOpts||A;for(const[i,o]of e)t[i]=o;return t},ky={class:"space-y-6"},vy={class:"text-xl font-semibold text-gray-900 mb-4"},Fy={class:"text-gray-600 mb-6"},Sy={class:"flex flex-col lg:flex-row gap-8"},Ry={class:"flex-1"},by={class:"text-lg font-medium text-gray-900 mb-4"},Ny={class:"bg-gray-100 p-4 rounded-lg"},Gy={class:"max-w-full overflow-auto flex justify-center"},Ly={class:"bg-gray-800 p-6 rounded-2xl shadow-2xl inline-block"},Uy={class:"bg-gray-900 p-2 rounded-xl"},My={class:"relative z-10 flex flex-col items-center justify-center p-4 text-center"},xy={class:"mb-4"},Jy={key:0,class:"emoji-container"},Ty=["src","alt"],Hy={key:1,class:"emoji-container"},Yy={class:"text-center"},_y={class:"text-sm"},Ky={class:"text-xs"},Oy={key:0,class:"absolute inset-0 flex items-center justify-center"},Py={class:"animate-pulse text-gray-400 text-xs"},Vy={class:"mt-3 text-center text-xs text-gray-400"},Wy={class:"w-full lg:w-80"},qy={class:"text-lg font-medium text-gray-900 mb-4"},jy={class:"space-y-6 bg-white border border-gray-200 rounded-lg p-4"},Zy={class:"block text-sm font-medium text-gray-700 mb-2"},Xy={class:"block text-sm font-medium text-gray-700 mb-2"},zy={key:0,class:"flex flex-wrap gap-2 max-h-32 overflow-y-auto justify-center"},$y=["onClick","title"],Aw={key:0},ew=["src","alt"],tw={key:1,class:"text-lg"},iw={key:1,class:"text-center py-4 text-gray-500 bg-gray-50 rounded-lg border-2 border-dashed"},ow={class:"text-2xl mb-2"},nw={class:"text-sm"},rw={class:"block text-sm font-medium text-gray-700 mb-2"},gw={class:"flex space-x-2"},sw={class:"border-t pt-4"},aw={class:"font-medium text-gray-900 mb-2"},Iw={class:"text-xs text-gray-600 space-y-1"},Cw={key:0},lw={class:"flex items-center"},Bw={key:0,class:"ml-2 animate-pulse text-blue-500"},Qw={key:1},cw={class:"flex justify-between"},Ew={__name:"GenerateSummary",props:{config:{type:Object,required:!0}},emits:["prev","generate"],setup(A){const{t:e}=we(),t=A,i=uA(e("generateSummary.defaultPreviewText")),o=uA("happy"),n=uA("light"),r=uA(!1),g=uA(""),s=hA(()=>[{key:"neutral",name:e("generateSummary.emotions.neutral"),emoji:"馃樁"},{key:"happy",name:e("generateSummary.emotions.happy"),emoji:"馃檪"},{key:"laughing",name:e("generateSummary.emotions.laughing"),emoji:"馃槅"},{key:"funny",name:e("generateSummary.emotions.funny"),emoji:"馃槀"},{key:"sad",name:e("generateSummary.emotions.sad"),emoji:"馃様"},{key:"angry",name:e("generateSummary.emotions.angry"),emoji:"馃槧"},{key:"crying",name:e("generateSummary.emotions.crying"),emoji:"馃槶"},{key:"loving",name:e("generateSummary.emotions.loving"),emoji:"馃槏"},{key:"surprised",name:e("generateSummary.emotions.surprised"),emoji:"馃槸"},{key:"thinking",name:e("generateSummary.emotions.thinking"),emoji:"馃"},{key:"cool",name:e("generateSummary.emotions.cool"),emoji:"馃槑"},{key:"sleepy",name:e("generateSummary.emotions.sleepy"),emoji:"馃槾"}]),a=hA(()=>{if(t.config.theme.emoji.type==="preset"&&t.config.theme.emoji.preset)return s.value;if(t.config.theme.emoji.type==="custom"){const R=t.config.theme.emoji.custom.images;return s.value.filter(j=>R[j.key])}else return[]}),I=hA(()=>c(o.value)),l=()=>{const{width:R,height:j}=t.config.chip.display;return{width:`${R}px`,height:`${j}px`}},C=()=>{const R=t.config.theme.skin[n.value];if(R.backgroundType==="image"&&R.backgroundImage)try{if(R.backgroundImage&&typeof R.backgroundImage=="object"&&R.backgroundImage.size)return{backgroundImage:`url(${URL.createObjectURL(R.backgroundImage)})`,backgroundSize:"cover",backgroundPosition:"center"}}catch(j){console.warn("鑳屾櫙鍥剧墖棰勮鍔犺浇澶辫触:",j)}return{backgroundColor:R.backgroundColor||"#ffffff"}},Q={twemoji32:32,twemoji64:64,"noto-emoji_64":64,"noto-emoji_128":128},f=()=>{let R=48;return t.config.theme.emoji.type==="preset"?R=Q[t.config.theme.emoji.preset]||32:t.config.theme.emoji.custom.size&&(R=Math.min(t.config.theme.emoji.custom.size.width,t.config.theme.emoji.custom.size.height)),{width:`${R}px`,height:`${R}px`}},h=()=>{let R=14;if(t.config.theme.font.type==="preset"){const rA=t.config.theme.font.preset;rA.includes("_14_")?R=14:rA.includes("_16_")?R=16:rA.includes("_20_")?R=20:rA.includes("_30_")&&(R=30)}else t.config.theme.font.custom.size&&(R=t.config.theme.font.custom.size);const j=n.value==="dark"?t.config.theme.skin.dark.textColor:t.config.theme.skin.light.textColor;return{fontSize:`${R}px`,color:j,fontFamily:E(),textShadow:n.value==="dark"?"1px 1px 2px rgba(0,0,0,0.5)":"1px 1px 2px rgba(255,255,255,0.5)"}},u=async()=>{document.querySelectorAll("style[data-font-preview]").forEach(j=>j.remove()),r.value=!1,g.value="";try{if(t.config.theme.font.type==="preset"){const j=t.config.theme.font.preset;let rA,IA;j&&j.startsWith("font_noto_qwen_")?(rA="NotoPreview",IA="./static/fonts/noto_qwen.ttf"):(rA="PuHuiPreview",IA="./static/fonts/puhui_deepseek.ttf");const AA=document.createElement("style");AA.setAttribute("data-font-preview","true"),AA.textContent=`
+ @font-face {
+ font-family: '${rA}';
+ src: url('${IA}') format('truetype');
+ font-display: swap;
+ }
+ `,document.head.appendChild(AA),await document.fonts.load(`16px "${rA}"`),g.value=rA,r.value=!0}else if(t.config.theme.font.custom.file)try{const j=t.config.theme.font.custom.file;if(!j||typeof j!="object"||!j.size)throw new Error("瀛椾綋鏂囦欢瀵硅薄鏃犳晥");const rA="CustomFontPreview",IA=URL.createObjectURL(j),AA=document.createElement("style");AA.setAttribute("data-font-preview","true"),AA.textContent=`
+ @font-face {
+ font-family: '${rA}';
+ src: url('${IA}');
+ font-display: swap;
+ }
+ `,document.head.appendChild(AA),await document.fonts.load(`16px "${rA}"`),g.value=rA,r.value=!0}catch(j){console.warn("鑷畾涔夊瓧浣撻瑙堝姞杞藉け璐�:",j),g.value="Arial, sans-serif",r.value=!0}else g.value="system-ui",r.value=!0}catch(j){console.warn("Font loading failed:",j),g.value="system-ui",r.value=!0}},E=()=>r.value&&g.value?`"${g.value}", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif`:'"PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif',D={twemoji32:"png",twemoji64:"png","noto-emoji_64":"gif","noto-emoji_128":"gif"},c=R=>{if(t.config.theme.emoji.type==="preset"){const j=t.config.theme.emoji.preset,rA=D[j]||"png";return`./static/emojis/${j}/${R}.${rA}`}else if(t.config.theme.emoji.type==="custom"&&t.config.theme.emoji.custom.images[R])try{const j=t.config.theme.emoji.custom.images[R];if(j&&typeof j=="object"&&j.size)return URL.createObjectURL(j)}catch(j){console.warn(`琛ㄦ儏鍥剧墖棰勮鍔犺浇澶辫触 (${R}):`,j)}return null},p=R=>{const j=s.value.find(rA=>rA.key===R);return j?j.emoji:"馃樁"},d=()=>{if(t.config.theme.emoji.type==="preset"){const R=Q[t.config.theme.emoji.preset]||32;return Math.min(R+16,80)}else if(t.config.theme.emoji.custom.size){const R=Math.min(t.config.theme.emoji.custom.size.width,t.config.theme.emoji.custom.size.height);return Math.min(R+16,80)}return 48},v=()=>{if(t.config.theme.emoji.type==="preset"){const R=Q[t.config.theme.emoji.preset]||32;return Math.min(R,64)}else if(t.config.theme.emoji.custom.size)return Math.min(t.config.theme.emoji.custom.size.width,t.config.theme.emoji.custom.size.height,64);return 32},b=R=>{o.value=R},N=()=>{const R=t.config.theme.wakeword;return!R||R.type==="none"?e("wakewordConfig.noWakeword"):R.type==="preset"?{wn9s_hilexin:"Hi,涔愰懌",wn9s_hiesp:"Hi,ESP",wn9s_nihaoxiaozhi:"浣犲ソ灏忔櫤",wn9_nihaoxiaozhi_tts:"浣犲ソ灏忔櫤",wn9_alexa:"Alexa",wn9_jarvis_tts:"Jarvis"}[R.preset]||R.preset:R.type==="custom"?R.custom.name||e("wakewordConfig.customWakeword"):e("wakewordConfig.noWakeword")},H=()=>{if(t.config.theme.font.type==="preset")return e("fontConfig.presetFontNames."+t.config.theme.font.preset)||t.config.theme.font.preset;{const R=t.config.theme.font.custom;return e("generateSummary.customFont",{size:R.size})}},M=()=>{if(t.config.theme.emoji.type==="preset"&&t.config.theme.emoji.preset)return{twemoji32:"Twemoji 32脳32",twemoji64:"Twemoji 64脳64","noto-emoji_64":"Noto Emoji 64脳64","noto-emoji_128":"Noto Emoji 128脳128"}[t.config.theme.emoji.preset]||t.config.theme.emoji.preset;if(t.config.theme.emoji.type==="custom"){const R=Object.keys(t.config.theme.emoji.custom.images).length;return e("generateSummary.customEmoji",{count:R})}else return t.config.theme.emoji.type==="none"?e("emojiConfig.noEmojiPack"):e("generateSummary.notConfigured")},U=()=>{const R=t.config.theme.skin.light.backgroundType==="image"?e("generateSummary.image"):e("generateSummary.color"),j=t.config.theme.skin.dark.backgroundType==="image"?e("generateSummary.image"):e("generateSummary.color");return e("generateSummary.skinLight",{type:R})+"/"+e("generateSummary.skinDark",{type:j})};return ae(()=>t.config.theme.font,()=>{u()},{deep:!0}),Ft(async()=>{a.value.length>0?o.value=a.value[0].key:o.value="",await u()}),Ci(()=>{document.querySelectorAll("style[data-font-preview]").forEach(j=>j.remove())}),(R,j)=>(T(),K("div",ky,[B("div",null,[B("h2",vy,F(R.$t("generateSummary.title")),1),B("p",Fy,F(R.$t("generateSummary.description")),1)]),B("div",Sy,[B("div",Ry,[B("h3",by,F(R.$t("generateSummary.devicePreview")),1),B("div",Ny,[B("div",Gy,[B("div",Ly,[B("div",Uy,[B("div",{style:VA(l()),class:"relative rounded-lg overflow-hidden border-2 border-gray-700 flex flex-col items-center justify-center"},[B("div",{style:VA(C()),class:"absolute inset-0"},null,4),B("div",My,[B("div",xy,[o.value&&a.value.length>0?(T(),K("div",Jy,[I.value?(T(),K("img",{key:0,src:I.value,alt:o.value,style:VA(f()),class:"emoji-image"},null,12,Ty)):(T(),K("div",{key:1,style:VA(f()),class:"emoji-fallback bg-gray-200 rounded-full flex items-center justify-center text-2xl"},F(p(o.value)),5))])):(T(),K("div",Hy,[B("div",{style:VA(f()),class:"emoji-placeholder flex items-center justify-center text-gray-400 border-2 border-dashed border-gray-300 rounded bg-gray-50"},[B("div",Yy,[B("div",_y,F(A.config.theme.emoji.type==="none"?"馃摝":"馃槙"),1),B("div",Ky,F(A.config.theme.emoji.type==="none"?R.$t("emojiConfig.noEmojiPack"):R.$t("generateSummary.noEmotionConfigured")),1)])],4)]))]),A.config.theme.font.hide_subtitle?CA("",!0):(T(),K("div",{key:0,style:VA(h()),class:"text-message max-w-full break-words relative"},[r.value?CA("",!0):(T(),K("div",Oy,[B("div",Py,F(R.$t("generateSummary.fontLoading")),1)])),B("div",{class:kA({"opacity-0":!r.value})},F(i.value),3)],4))])],4)]),B("div",Vy,F(A.config.chip.display.width)+" 脳 "+F(A.config.chip.display.height)+" "+F(A.config.chip.model.toUpperCase()),1)])])])]),B("div",Wy,[B("h3",qy,F(R.$t("generateSummary.previewSettings")),1),B("div",jy,[B("div",null,[B("label",Zy,F(R.$t("generateSummary.previewText")),1),ZA(B("textarea",{"onUpdate:modelValue":j[0]||(j[0]=rA=>i.value=rA),class:"w-full border border-gray-300 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-primary-500",rows:"3",placeholder:"Hi, I'm your friend Xiaozhi!"},null,512),[[te,i.value]])]),B("div",null,[B("label",Xy,F(R.$t("generateSummary.currentEmotion")),1),a.value.length>0?(T(),K("div",zy,[(T(!0),K(qA,null,Ke(a.value,rA=>(T(),K("button",{key:rA.key,onClick:IA=>b(rA.key),class:kA(["p-2 border rounded transition-colors flex items-center justify-center",o.value===rA.key?"border-primary-500 bg-primary-50":"border-gray-200 hover:border-gray-300"]),title:rA.name,style:VA({width:d()+"px",height:d()+"px"})},[c(rA.key)?(T(),K("div",Aw,[B("img",{src:c(rA.key),alt:rA.name,style:VA({width:v()+"px",height:v()+"px"}),class:"object-contain rounded"},null,12,ew)])):(T(),K("div",tw,F(rA.emoji),1))],14,$y))),128))])):(T(),K("div",iw,[B("div",ow,F(A.config.theme.emoji.type==="none"?"馃摝":"馃槙"),1),B("div",nw,F(A.config.theme.emoji.type==="none"?R.$t("emojiConfig.noEmojiPackDescription"):R.$t("generateSummary.configureEmojiFirst")),1)]))]),B("div",null,[B("label",rw,F(R.$t("generateSummary.themeMode")),1),B("div",gw,[B("button",{onClick:j[1]||(j[1]=rA=>n.value="light"),class:kA(["flex-1 py-2 px-3 text-sm border rounded transition-colors",n.value==="light"?"border-primary-500 bg-primary-50 text-primary-700":"border-gray-300 hover:border-gray-400"])}," 馃尀 "+F(R.$t("generateSummary.lightMode")),3),B("button",{onClick:j[2]||(j[2]=rA=>n.value="dark"),class:kA(["flex-1 py-2 px-3 text-sm border rounded transition-colors",n.value==="dark"?"border-primary-500 bg-primary-50 text-primary-700":"border-gray-300 hover:border-gray-400"])}," 馃寵 "+F(R.$t("generateSummary.darkMode")),3)])]),B("div",sw,[B("h4",aw,F(R.$t("generateSummary.configSummary")),1),B("div",Iw,[A.config.theme.wakeword?(T(),K("div",Cw,F(R.$t("generateSummary.wakeword"))+" "+F(N()),1)):CA("",!0),B("div",lw,[B("span",null,F(R.$t("generateSummary.font"))+" "+F(H()),1),r.value?CA("",!0):(T(),K("span",Bw,F(R.$t("generateSummary.loading")),1))]),B("div",null,F(R.$t("generateSummary.emotion"))+" "+F(M()),1),B("div",null,F(R.$t("generateSummary.skin"))+" "+F(U()),1),A.config.theme.font.hide_subtitle?(T(),K("div",Qw,F(R.$t("generateSummary.hideSubtitle"))+" "+F(R.$t("common.yes")),1)):CA("",!0)])])])])]),B("div",cw,[B("button",{onClick:j[3]||(j[3]=rA=>R.$emit("prev")),class:"bg-gray-500 hover:bg-gray-600 text-white px-6 py-2 rounded-lg font-medium transition-colors"},F(R.$t("generateSummary.previous")),1),B("button",{onClick:j[4]||(j[4]=rA=>R.$emit("generate")),class:"bg-green-500 hover:bg-green-600 text-white px-8 py-2 rounded-lg font-medium transition-colors flex items-center"},[j[5]||(j[5]=B("svg",{class:"w-5 h-5 mr-2",fill:"currentColor",viewBox:"0 0 20 20"},[B("path",{"fill-rule":"evenodd",d:"M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z","clip-rule":"evenodd"})],-1)),gt(" "+F(R.$t("generateSummary.generate")),1)])])]))}},uw=Dy(Ew,[["__scopeId","data-v-0726c332"]]);/*! https://mths.be/codepointat v0.2.0 by @mathias */String.prototype.codePointAt||function(){var A=function(){try{var t={},i=Object.defineProperty,o=i(t,t,t)&&i}catch{}return o}(),e=function(t){if(this==null)throw TypeError();var i=String(this),o=i.length,n=t?Number(t):0;if(n!=n&&(n=0),!(n<0||n>=o)){var r=i.charCodeAt(n),g;return r>=55296&&r<=56319&&o>n+1&&(g=i.charCodeAt(n+1),g>=56320&&g<=57343)?(r-55296)*1024+g-56320+65536:r}};A?A(String.prototype,"codePointAt",{value:e,configurable:!0,writable:!0}):String.prototype.codePointAt=e}();var Eg=0,KC=-3;function ao(){this.table=new Uint16Array(16),this.trans=new Uint16Array(288)}function fw(A,e){this.source=A,this.sourceIndex=0,this.tag=0,this.bitcount=0,this.dest=e,this.destLen=0,this.ltree=new ao,this.dtree=new ao}var OC=new ao,PC=new ao,ug=new Uint8Array(30),fg=new Uint16Array(30),VC=new Uint8Array(30),WC=new Uint16Array(30),hw=new Uint8Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),$s=new ao,$e=new Uint8Array(320);function qC(A,e,t,i){var o,n;for(o=0;o<t;++o)A[o]=0;for(o=0;o<30-t;++o)A[o+t]=o/t|0;for(n=i,o=0;o<30;++o)e[o]=n,n+=1<<A[o]}function dw(A,e){var t;for(t=0;t<7;++t)A.table[t]=0;for(A.table[7]=24,A.table[8]=152,A.table[9]=112,t=0;t<24;++t)A.trans[t]=256+t;for(t=0;t<144;++t)A.trans[24+t]=t;for(t=0;t<8;++t)A.trans[168+t]=280+t;for(t=0;t<112;++t)A.trans[176+t]=144+t;for(t=0;t<5;++t)e.table[t]=0;for(e.table[5]=32,t=0;t<32;++t)e.trans[t]=t}var Aa=new Uint16Array(16);function jn(A,e,t,i){var o,n;for(o=0;o<16;++o)A.table[o]=0;for(o=0;o<i;++o)A.table[e[t+o]]++;for(A.table[0]=0,n=0,o=0;o<16;++o)Aa[o]=n,n+=A.table[o];for(o=0;o<i;++o)e[t+o]&&(A.trans[Aa[e[t+o]]++]=o)}function pw(A){A.bitcount--||(A.tag=A.source[A.sourceIndex++],A.bitcount=7);var e=A.tag&1;return A.tag>>>=1,e}function et(A,e,t){if(!e)return t;for(;A.bitcount<24;)A.tag|=A.source[A.sourceIndex++]<<A.bitcount,A.bitcount+=8;var i=A.tag&65535>>>16-e;return A.tag>>>=e,A.bitcount-=e,i+t}function Nr(A,e){for(;A.bitcount<24;)A.tag|=A.source[A.sourceIndex++]<<A.bitcount,A.bitcount+=8;var t=0,i=0,o=0,n=A.tag;do i=2*i+(n&1),n>>>=1,++o,t+=e.table[o],i-=e.table[o];while(i>=0);return A.tag=n,A.bitcount-=o,e.trans[t+i]}function mw(A,e,t){var i,o,n,r,g,s;for(i=et(A,5,257),o=et(A,5,1),n=et(A,4,4),r=0;r<19;++r)$e[r]=0;for(r=0;r<n;++r){var a=et(A,3,0);$e[hw[r]]=a}for(jn($s,$e,0,19),g=0;g<i+o;){var I=Nr(A,$s);switch(I){case 16:var l=$e[g-1];for(s=et(A,2,3);s;--s)$e[g++]=l;break;case 17:for(s=et(A,3,3);s;--s)$e[g++]=0;break;case 18:for(s=et(A,7,11);s;--s)$e[g++]=0;break;default:$e[g++]=I;break}}jn(e,$e,0,i),jn(t,$e,i,o)}function ea(A,e,t){for(;;){var i=Nr(A,e);if(i===256)return Eg;if(i<256)A.dest[A.destLen++]=i;else{var o,n,r,g;for(i-=257,o=et(A,ug[i],fg[i]),n=Nr(A,t),r=A.destLen-et(A,VC[n],WC[n]),g=r;g<r+o;++g)A.dest[A.destLen++]=A.dest[g]}}}function yw(A){for(var e,t,i;A.bitcount>8;)A.sourceIndex--,A.bitcount-=8;if(e=A.source[A.sourceIndex+1],e=256*e+A.source[A.sourceIndex],t=A.source[A.sourceIndex+3],t=256*t+A.source[A.sourceIndex+2],e!==(~t&65535))return KC;for(A.sourceIndex+=4,i=e;i;--i)A.dest[A.destLen++]=A.source[A.sourceIndex++];return A.bitcount=0,Eg}function ww(A,e){var t=new fw(A,e),i,o,n;do{switch(i=pw(t),o=et(t,2,0),o){case 0:n=yw(t);break;case 1:n=ea(t,OC,PC);break;case 2:mw(t,t.ltree,t.dtree),n=ea(t,t.ltree,t.dtree);break;default:n=KC}if(n!==Eg)throw new Error("Data error")}while(!i);return t.destLen<t.dest.length?typeof t.dest.slice=="function"?t.dest.slice(0,t.destLen):t.dest.subarray(0,t.destLen):t.dest}dw(OC,PC);qC(ug,fg,4,3);qC(VC,WC,2,1);ug[28]=0;fg[28]=258;var Dw=ww;function Bi(A,e,t,i,o){return Math.pow(1-o,3)*A+3*Math.pow(1-o,2)*o*e+3*(1-o)*Math.pow(o,2)*t+Math.pow(o,3)*i}function St(){this.x1=Number.NaN,this.y1=Number.NaN,this.x2=Number.NaN,this.y2=Number.NaN}St.prototype.isEmpty=function(){return isNaN(this.x1)||isNaN(this.y1)||isNaN(this.x2)||isNaN(this.y2)};St.prototype.addPoint=function(A,e){typeof A=="number"&&((isNaN(this.x1)||isNaN(this.x2))&&(this.x1=A,this.x2=A),A<this.x1&&(this.x1=A),A>this.x2&&(this.x2=A)),typeof e=="number"&&((isNaN(this.y1)||isNaN(this.y2))&&(this.y1=e,this.y2=e),e<this.y1&&(this.y1=e),e>this.y2&&(this.y2=e))};St.prototype.addX=function(A){this.addPoint(A,null)};St.prototype.addY=function(A){this.addPoint(null,A)};St.prototype.addBezier=function(A,e,t,i,o,n,r,g){var s=[A,e],a=[t,i],I=[o,n],l=[r,g];this.addPoint(A,e),this.addPoint(r,g);for(var C=0;C<=1;C++){var Q=6*s[C]-12*a[C]+6*I[C],f=-3*s[C]+9*a[C]-9*I[C]+3*l[C],h=3*a[C]-3*s[C];if(f===0){if(Q===0)continue;var u=-h/Q;0<u&&u<1&&(C===0&&this.addX(Bi(s[C],a[C],I[C],l[C],u)),C===1&&this.addY(Bi(s[C],a[C],I[C],l[C],u)));continue}var E=Math.pow(Q,2)-4*h*f;if(!(E<0)){var D=(-Q+Math.sqrt(E))/(2*f);0<D&&D<1&&(C===0&&this.addX(Bi(s[C],a[C],I[C],l[C],D)),C===1&&this.addY(Bi(s[C],a[C],I[C],l[C],D)));var c=(-Q-Math.sqrt(E))/(2*f);0<c&&c<1&&(C===0&&this.addX(Bi(s[C],a[C],I[C],l[C],c)),C===1&&this.addY(Bi(s[C],a[C],I[C],l[C],c)))}}};St.prototype.addQuad=function(A,e,t,i,o,n){var r=A+.6666666666666666*(t-A),g=e+2/3*(i-e),s=r+1/3*(o-A),a=g+1/3*(n-e);this.addBezier(A,e,r,g,s,a,o,n)};function ie(){this.commands=[],this.fill="black",this.stroke=null,this.strokeWidth=1}ie.prototype.moveTo=function(A,e){this.commands.push({type:"M",x:A,y:e})};ie.prototype.lineTo=function(A,e){this.commands.push({type:"L",x:A,y:e})};ie.prototype.curveTo=ie.prototype.bezierCurveTo=function(A,e,t,i,o,n){this.commands.push({type:"C",x1:A,y1:e,x2:t,y2:i,x:o,y:n})};ie.prototype.quadTo=ie.prototype.quadraticCurveTo=function(A,e,t,i){this.commands.push({type:"Q",x1:A,y1:e,x:t,y:i})};ie.prototype.close=ie.prototype.closePath=function(){this.commands.push({type:"Z"})};ie.prototype.extend=function(A){if(A.commands)A=A.commands;else if(A instanceof St){var e=A;this.moveTo(e.x1,e.y1),this.lineTo(e.x2,e.y1),this.lineTo(e.x2,e.y2),this.lineTo(e.x1,e.y2),this.close();return}Array.prototype.push.apply(this.commands,A)};ie.prototype.getBoundingBox=function(){for(var A=new St,e=0,t=0,i=0,o=0,n=0;n<this.commands.length;n++){var r=this.commands[n];switch(r.type){case"M":A.addPoint(r.x,r.y),e=i=r.x,t=o=r.y;break;case"L":A.addPoint(r.x,r.y),i=r.x,o=r.y;break;case"Q":A.addQuad(i,o,r.x1,r.y1,r.x,r.y),i=r.x,o=r.y;break;case"C":A.addBezier(i,o,r.x1,r.y1,r.x2,r.y2,r.x,r.y),i=r.x,o=r.y;break;case"Z":i=e,o=t;break;default:throw new Error("Unexpected path command "+r.type)}}return A.isEmpty()&&A.addPoint(0,0),A};ie.prototype.draw=function(A){A.beginPath();for(var e=0;e<this.commands.length;e+=1){var t=this.commands[e];t.type==="M"?A.moveTo(t.x,t.y):t.type==="L"?A.lineTo(t.x,t.y):t.type==="C"?A.bezierCurveTo(t.x1,t.y1,t.x2,t.y2,t.x,t.y):t.type==="Q"?A.quadraticCurveTo(t.x1,t.y1,t.x,t.y):t.type==="Z"&&A.closePath()}this.fill&&(A.fillStyle=this.fill,A.fill()),this.stroke&&(A.strokeStyle=this.stroke,A.lineWidth=this.strokeWidth,A.stroke())};ie.prototype.toPathData=function(A){A=A!==void 0?A:2;function e(r){return Math.round(r)===r?""+Math.round(r):r.toFixed(A)}function t(){for(var r=arguments,g="",s=0;s<arguments.length;s+=1){var a=r[s];a>=0&&s>0&&(g+=" "),g+=e(a)}return g}for(var i="",o=0;o<this.commands.length;o+=1){var n=this.commands[o];n.type==="M"?i+="M"+t(n.x,n.y):n.type==="L"?i+="L"+t(n.x,n.y):n.type==="C"?i+="C"+t(n.x1,n.y1,n.x2,n.y2,n.x,n.y):n.type==="Q"?i+="Q"+t(n.x1,n.y1,n.x,n.y):n.type==="Z"&&(i+="Z")}return i};ie.prototype.toSVG=function(A){var e='<path d="';return e+=this.toPathData(A),e+='"',this.fill&&this.fill!=="black"&&(this.fill===null?e+=' fill="none"':e+=' fill="'+this.fill+'"'),this.stroke&&(e+=' stroke="'+this.stroke+'" stroke-width="'+this.strokeWidth+'"'),e+="/>",e};ie.prototype.toDOMElement=function(A){var e=this.toPathData(A),t=document.createElementNS("http://www.w3.org/2000/svg","path");return t.setAttribute("d",e),t};function jC(A){throw new Error(A)}function ta(A,e){A||jC(e)}var mA={fail:jC,argument:ta,assert:ta},ia=32768,oa=2147483648,Ri={},sA={},wA={};function je(A){return function(){return A}}sA.BYTE=function(A){return mA.argument(A>=0&&A<=255,"Byte value should be between 0 and 255."),[A]};wA.BYTE=je(1);sA.CHAR=function(A){return[A.charCodeAt(0)]};wA.CHAR=je(1);sA.CHARARRAY=function(A){typeof A>"u"&&(A="",console.warn("Undefined CHARARRAY encountered and treated as an empty string. This is probably caused by a missing glyph name."));for(var e=[],t=0;t<A.length;t+=1)e[t]=A.charCodeAt(t);return e};wA.CHARARRAY=function(A){return typeof A>"u"?0:A.length};sA.USHORT=function(A){return[A>>8&255,A&255]};wA.USHORT=je(2);sA.SHORT=function(A){return A>=ia&&(A=-(2*ia-A)),[A>>8&255,A&255]};wA.SHORT=je(2);sA.UINT24=function(A){return[A>>16&255,A>>8&255,A&255]};wA.UINT24=je(3);sA.ULONG=function(A){return[A>>24&255,A>>16&255,A>>8&255,A&255]};wA.ULONG=je(4);sA.LONG=function(A){return A>=oa&&(A=-(2*oa-A)),[A>>24&255,A>>16&255,A>>8&255,A&255]};wA.LONG=je(4);sA.FIXED=sA.ULONG;wA.FIXED=wA.ULONG;sA.FWORD=sA.SHORT;wA.FWORD=wA.SHORT;sA.UFWORD=sA.USHORT;wA.UFWORD=wA.USHORT;sA.LONGDATETIME=function(A){return[0,0,0,0,A>>24&255,A>>16&255,A>>8&255,A&255]};wA.LONGDATETIME=je(8);sA.TAG=function(A){return mA.argument(A.length===4,"Tag should be exactly 4 ASCII characters."),[A.charCodeAt(0),A.charCodeAt(1),A.charCodeAt(2),A.charCodeAt(3)]};wA.TAG=je(4);sA.Card8=sA.BYTE;wA.Card8=wA.BYTE;sA.Card16=sA.USHORT;wA.Card16=wA.USHORT;sA.OffSize=sA.BYTE;wA.OffSize=wA.BYTE;sA.SID=sA.USHORT;wA.SID=wA.USHORT;sA.NUMBER=function(A){return A>=-107&&A<=107?[A+139]:A>=108&&A<=1131?(A=A-108,[(A>>8)+247,A&255]):A>=-1131&&A<=-108?(A=-A-108,[(A>>8)+251,A&255]):A>=-32768&&A<=32767?sA.NUMBER16(A):sA.NUMBER32(A)};wA.NUMBER=function(A){return sA.NUMBER(A).length};sA.NUMBER16=function(A){return[28,A>>8&255,A&255]};wA.NUMBER16=je(3);sA.NUMBER32=function(A){return[29,A>>24&255,A>>16&255,A>>8&255,A&255]};wA.NUMBER32=je(5);sA.REAL=function(A){var e=A.toString(),t=/\.(\d*?)(?:9{5,20}|0{5,20})\d{0,2}(?:e(.+)|$)/.exec(e);if(t){var i=parseFloat("1e"+((t[2]?+t[2]:0)+t[1].length));e=(Math.round(A*i)/i).toString()}for(var o="",n=0,r=e.length;n<r;n+=1){var g=e[n];g==="e"?o+=e[++n]==="-"?"c":"b":g==="."?o+="a":g==="-"?o+="e":o+=g}o+=o.length&1?"f":"ff";for(var s=[30],a=0,I=o.length;a<I;a+=2)s.push(parseInt(o.substr(a,2),16));return s};wA.REAL=function(A){return sA.REAL(A).length};sA.NAME=sA.CHARARRAY;wA.NAME=wA.CHARARRAY;sA.STRING=sA.CHARARRAY;wA.STRING=wA.CHARARRAY;Ri.UTF8=function(A,e,t){for(var i=[],o=t,n=0;n<o;n++,e+=1)i[n]=A.getUint8(e);return String.fromCharCode.apply(null,i)};Ri.UTF16=function(A,e,t){for(var i=[],o=t/2,n=0;n<o;n++,e+=2)i[n]=A.getUint16(e);return String.fromCharCode.apply(null,i)};sA.UTF16=function(A){for(var e=[],t=0;t<A.length;t+=1){var i=A.charCodeAt(t);e[e.length]=i>>8&255,e[e.length]=i&255}return e};wA.UTF16=function(A){return A.length*2};var Gr={"x-mac-croatian":"脛脜脟脡脩脰脺谩脿芒盲茫氓莽茅猫锚毛铆矛卯茂帽贸貌么枚玫煤霉没眉鈥犅奥⒙Bр�⒙睹熉犫劉麓篓鈮犈矫樷垶卤鈮も墺鈭喡碘垈鈭戔垙拧鈭何┡久嘎柯÷垰茠鈮埬喡屸�β犆�脙脮艗艙膼鈥斺�溾�濃�樷�櫭封棅铮柯┾亜鈧�光�好喡烩�撀封�氣�炩�懊偰嚸伳嵜埫嵜幟徝屆撁斈懨捗毭浢櫮彼喫溌�脣藲赂脢忙藝","x-mac-cyrillic":"袗袘袙袚袛袝袞袟袠袡袣袥袦袧袨袩袪小孝校肖啸笑效楔些歇蝎鞋协挟携鈥犅耙惵Bр�⒙缎喡┾劉袀褣鈮犘冄撯垶卤鈮も墺褨碌覒袌袆褦袊褩袎褭袏褮褬袇卢鈭毱掆増鈭喡烩�β犘嬔浶屟溠曗�撯�斺�溾�濃�樷�櫭封�炐幯炐徰熲剸衼褢褟邪斜胁谐写械卸蟹懈泄泻谢屑薪芯锌褉褋褌褍褎褏褑褔褕褖褗褘褜褝褞","x-mac-gaelic":"脛脜脟脡脩脰脺谩脿芒盲茫氓莽茅猫锚毛铆矛卯茂帽贸貌么枚玫煤霉没眉鈥犅奥⒙Bр�⒙睹熉┾劉麓篓鈮犆喢樶競卤鈮も墺岣兡娔嬦笂岣嬦笧岣熌犇♂箑忙酶峁佱箹峁椛计捙酷範芦禄鈥β犆�脙脮艗艙鈥撯�斺�溾�濃�樷�欋埂岷浢颗羔躬鈧�光�号杜丰公路峄册怀鈦娒偯娒伱嬅埫嵜幟徝屆撁斺櫍脪脷脹脵谋脻媒糯诺岷勧簠岷�岷佱簜岷�","x-mac-greek":"脛鹿虏脡鲁脰脺螀脿芒盲蝿篓莽茅猫锚毛拢鈩⒚�⒙解�懊疵堵︹偓霉没眉鈥犖撐斘樜浳炍犆熉┪Nр墵掳路螒卤鈮も墺楼螔螘螙螚螜螝螠桅潍唯惟维螡卢螣巍鈮埼ぢ烩�β犖ノ單埮撯�撯�曗�溾�濃�樷�櫭肺壩娢屛幬屛徬嵨蔽蚕埼次迪單澄肺刮疚何晃嘉轿肯�蠋蟻蟽蟿胃蠅蟼蠂蠀味蠆蠇螑伟颅","x-mac-icelandic":"脛脜脟脡脩脰脺谩脿芒盲茫氓莽茅猫锚毛铆矛卯茂帽贸貌么枚玫煤霉没眉脻掳垄拢搂鈥⒙睹熉┾劉麓篓鈮犆喢樷垶卤鈮も墺楼碌鈭傗垜鈭徬�鈭何┟γ嘎柯÷垰茠鈮堚垎芦禄鈥β犆�脙脮艗艙鈥撯�斺�溾�濃�樷�櫭封棅每鸥鈦勨偓脨冒脼镁媒路鈥氣�炩�懊偯娒伱嬅埫嵜幟徝屆撁旓?脪脷脹脵谋藛藴炉藰藱藲赂藵藳藝","x-mac-inuit":"釔冡悇釔呩悊釔娽悑釔贬惒釔翅惔釔羔惞釕夅憥釕忈憪釕戓憰釕栣懄釕懏釕懓釕册懗釖冡拫釖屷拲釖庒拹釖懧搬挕釖メ挦鈥⒙夺挧庐漏鈩⑨挩釖挮釖会搨釗冡搫釗呩搰釗堘搻釗摪釗贬摬釗瘁摰釘呩摃釗栣摋釗樶摎釗涐摢釘ㄡ敥釘敨釘�β犪敭釘踞晻釙栣晽鈥撯�斺�溾�濃�樷�欋晿釙欋暁釙濁晢釙囜晥釙夅晪釙屷晲釙酷杸釚佱杺釚冡杽釚呩枏釚愥枒釚掅枔釚斸枙釞贬櫜釞翅櫞釞滇櫠釚栣枲釚♂枹釚a枻釚メ枽釙寂伵�","x-mac-ce":"脛膧膩脡膭脰脺谩膮膶盲膷膯膰茅殴藕膸铆膹膾膿臇贸臈么枚玫煤臍臎眉鈥犅澳樎Bр�⒙睹熉┾劉臋篓鈮犇D墹鈮ツ垛垈鈭懪偰荒寄侥灸鼓号吪喤兟垰艅艊鈭喡烩�β犈埮惷暸懪屸�撯�斺�溾�濃�樷�櫭封棅艒艛艜艠鈥光�号櫯柵椗犫�氣�炁∨毰浢伵づッ嵟脚九撁斉毰芭迸才趁澝侥放慌伵寄⑺�",macintosh:"脛脜脟脡脩脰脺谩脿芒盲茫氓莽茅猫锚毛铆矛卯茂帽贸貌么枚玫煤霉没眉鈥犅奥⒙Bр�⒙睹熉┾劉麓篓鈮犆喢樷垶卤鈮も墺楼碌鈭傗垜鈭徬�鈭何┟γ嘎柯÷垰茠鈮堚垎芦禄鈥β犆�脙脮艗艙鈥撯�斺�溾�濃�樷�櫭封棅每鸥鈦勨偓鈥光�猴瑏铿傗�÷封�氣�炩�懊偯娒伱嬅埫嵜幟徝屆撁旓?脪脷脹脵谋藛藴炉藰藱藲赂藵藳藝","x-mac-romanian":"脛脜脟脡脩脰脺谩脿芒盲茫氓莽茅猫锚毛铆矛卯茂帽贸貌么枚玫煤霉没眉鈥犅奥⒙Bр�⒙睹熉┾劉麓篓鈮犇側樷垶卤鈮も墺楼碌鈭傗垜鈭徬�鈭何┠內櫬柯÷垰茠鈮堚垎芦禄鈥β犆�脙脮艗艙鈥撯�斺�溾�濃�樷�櫭封棅每鸥鈦勨偓鈥光�喝毴涒�÷封�氣�炩�懊偯娒伱嬅埫嵜幟徝屆撁旓?脪脷脹脵谋藛藴炉藰藱藲赂藵藳藝","x-mac-turkish":"脛脜脟脡脩脰脺谩脿芒盲茫氓莽茅猫锚毛铆矛卯茂帽贸貌么枚玫煤霉没眉鈥犅奥⒙Bр�⒙睹熉┾劉麓篓鈮犆喢樷垶卤鈮も墺楼碌鈭傗垜鈭徬�鈭何┟γ嘎柯÷垰茠鈮堚垎芦禄鈥β犆�脙脮艗艙鈥撯�斺�溾�濃�樷�櫭封棅每鸥臑臒陌谋艦艧鈥÷封�氣�炩�懊偯娒伱嬅埫嵜幟徝屆撁旓?脪脷脹脵铫犓喫溌標櫵毬杆澦浰�"};Ri.MACSTRING=function(A,e,t,i){var o=Gr[i];if(o!==void 0){for(var n="",r=0;r<t;r++){var g=A.getUint8(e+r);g<=127?n+=String.fromCharCode(g):n+=o[g&127]}return n}};var Go=typeof WeakMap=="function"&&new WeakMap,Lo,kw=function(A){if(!Lo){Lo={};for(var e in Gr)Lo[e]=new String(e)}var t=Lo[A];if(t!==void 0){if(Go){var i=Go.get(t);if(i!==void 0)return i}var o=Gr[A];if(o!==void 0){for(var n={},r=0;r<o.length;r++)n[o.charCodeAt(r)]=r+128;return Go&&Go.set(t,n),n}}};sA.MACSTRING=function(A,e){var t=kw(e);if(t!==void 0){for(var i=[],o=0;o<A.length;o++){var n=A.charCodeAt(o);if(n>=128&&(n=t[n],n===void 0))return;i[o]=n}return i}};wA.MACSTRING=function(A,e){var t=sA.MACSTRING(A,e);return t!==void 0?t.length:0};function Lr(A){return A>=-128&&A<=127}function vw(A,e,t){for(var i=0,o=A.length;e<o&&i<64&&A[e]===0;)++e,++i;return t.push(128|i-1),e}function Fw(A,e,t){for(var i=0,o=A.length,n=e;n<o&&i<64;){var r=A[n];if(!Lr(r)||r===0&&n+1<o&&A[n+1]===0)break;++n,++i}t.push(i-1);for(var g=e;g<n;++g)t.push(A[g]+256&255);return n}function Sw(A,e,t){for(var i=0,o=A.length,n=e;n<o&&i<64;){var r=A[n];if(r===0||Lr(r)&&n+1<o&&Lr(A[n+1]))break;++n,++i}t.push(64|i-1);for(var g=e;g<n;++g){var s=A[g];t.push(s+65536>>8&255,s+256&255)}return n}sA.VARDELTAS=function(A){for(var e=0,t=[];e<A.length;){var i=A[e];i===0?e=vw(A,e,t):i>=-128&&i<=127?e=Fw(A,e,t):e=Sw(A,e,t)}return t};sA.INDEX=function(A){for(var e=1,t=[e],i=[],o=0;o<A.length;o+=1){var n=sA.OBJECT(A[o]);Array.prototype.push.apply(i,n),e+=n.length,t.push(e)}if(i.length===0)return[0,0];for(var r=[],g=1+Math.floor(Math.log(e)/Math.log(2))/8|0,s=[void 0,sA.BYTE,sA.USHORT,sA.UINT24,sA.ULONG][g],a=0;a<t.length;a+=1){var I=s(t[a]);Array.prototype.push.apply(r,I)}return Array.prototype.concat(sA.Card16(A.length),sA.OffSize(g),r,i)};wA.INDEX=function(A){return sA.INDEX(A).length};sA.DICT=function(A){for(var e=[],t=Object.keys(A),i=t.length,o=0;o<i;o+=1){var n=parseInt(t[o],0),r=A[n];e=e.concat(sA.OPERAND(r.value,r.type)),e=e.concat(sA.OPERATOR(n))}return e};wA.DICT=function(A){return sA.DICT(A).length};sA.OPERATOR=function(A){return A<1200?[A]:[12,A-1200]};sA.OPERAND=function(A,e){var t=[];if(Array.isArray(e))for(var i=0;i<e.length;i+=1)mA.argument(A.length===e.length,"Not enough arguments given for type"+e),t=t.concat(sA.OPERAND(A[i],e[i]));else if(e==="SID")t=t.concat(sA.NUMBER(A));else if(e==="offset")t=t.concat(sA.NUMBER32(A));else if(e==="number")t=t.concat(sA.NUMBER(A));else if(e==="real")t=t.concat(sA.REAL(A));else throw new Error("Unknown operand type "+e);return t};sA.OP=sA.BYTE;wA.OP=wA.BYTE;var Uo=typeof WeakMap=="function"&&new WeakMap;sA.CHARSTRING=function(A){if(Uo){var e=Uo.get(A);if(e!==void 0)return e}for(var t=[],i=A.length,o=0;o<i;o+=1){var n=A[o];t=t.concat(sA[n.type](n.value))}return Uo&&Uo.set(A,t),t};wA.CHARSTRING=function(A){return sA.CHARSTRING(A).length};sA.OBJECT=function(A){var e=sA[A.type];return mA.argument(e!==void 0,"No encoding function for type "+A.type),e(A.value)};wA.OBJECT=function(A){var e=wA[A.type];return mA.argument(e!==void 0,"No sizeOf function for type "+A.type),e(A.value)};sA.TABLE=function(A){for(var e=[],t=A.fields.length,i=[],o=[],n=0;n<t;n+=1){var r=A.fields[n],g=sA[r.type];mA.argument(g!==void 0,"No encoding function for field type "+r.type+" ("+r.name+")");var s=A[r.name];s===void 0&&(s=r.value);var a=g(s);r.type==="TABLE"?(o.push(e.length),e=e.concat([0,0]),i.push(a)):e=e.concat(a)}for(var I=0;I<i.length;I+=1){var l=o[I],C=e.length;mA.argument(C<65536,"Table "+A.tableName+" too big."),e[l]=C>>8,e[l+1]=C&255,e=e.concat(i[I])}return e};wA.TABLE=function(A){for(var e=0,t=A.fields.length,i=0;i<t;i+=1){var o=A.fields[i],n=wA[o.type];mA.argument(n!==void 0,"No sizeOf function for field type "+o.type+" ("+o.name+")");var r=A[o.name];r===void 0&&(r=o.value),e+=n(r),o.type==="TABLE"&&(e+=2)}return e};sA.RECORD=sA.TABLE;wA.RECORD=wA.TABLE;sA.LITERAL=function(A){return A};wA.LITERAL=function(A){return A.length};function se(A,e,t){if(e.length&&(e[0].name!=="coverageFormat"||e[0].value===1))for(var i=0;i<e.length;i+=1){var o=e[i];this[o.name]=o.value}if(this.tableName=A,this.fields=e,t)for(var n=Object.keys(t),r=0;r<n.length;r+=1){var g=n[r],s=t[g];this[g]!==void 0&&(this[g]=s)}}se.prototype.encode=function(){return sA.TABLE(this)};se.prototype.sizeOf=function(){return wA.TABLE(this)};function Io(A,e,t){t===void 0&&(t=e.length);var i=new Array(e.length+1);i[0]={name:A+"Count",type:"USHORT",value:t};for(var o=0;o<e.length;o++)i[o+1]={name:A+o,type:"USHORT",value:e[o]};return i}function Ur(A,e,t){var i=e.length,o=new Array(i+1);o[0]={name:A+"Count",type:"USHORT",value:i};for(var n=0;n<i;n++)o[n+1]={name:A+n,type:"TABLE",value:t(e[n],n)};return o}function Co(A,e,t){var i=e.length,o=[];o[0]={name:A+"Count",type:"USHORT",value:i};for(var n=0;n<i;n++)o=o.concat(t(e[n],n));return o}function gn(A){A.format===1?se.call(this,"coverageTable",[{name:"coverageFormat",type:"USHORT",value:1}].concat(Io("glyph",A.glyphs))):A.format===2?se.call(this,"coverageTable",[{name:"coverageFormat",type:"USHORT",value:2}].concat(Co("rangeRecord",A.ranges,function(e){return[{name:"startGlyphID",type:"USHORT",value:e.start},{name:"endGlyphID",type:"USHORT",value:e.end},{name:"startCoverageIndex",type:"USHORT",value:e.index}]}))):mA.assert(!1,"Coverage format must be 1 or 2.")}gn.prototype=Object.create(se.prototype);gn.prototype.constructor=gn;function sn(A){se.call(this,"scriptListTable",Co("scriptRecord",A,function(e,t){var i=e.script,o=i.defaultLangSys;return mA.assert(!!o,"Unable to write GSUB: script "+e.tag+" has no default language system."),[{name:"scriptTag"+t,type:"TAG",value:e.tag},{name:"script"+t,type:"TABLE",value:new se("scriptTable",[{name:"defaultLangSys",type:"TABLE",value:new se("defaultLangSys",[{name:"lookupOrder",type:"USHORT",value:0},{name:"reqFeatureIndex",type:"USHORT",value:o.reqFeatureIndex}].concat(Io("featureIndex",o.featureIndexes)))}].concat(Co("langSys",i.langSysRecords,function(n,r){var g=n.langSys;return[{name:"langSysTag"+r,type:"TAG",value:n.tag},{name:"langSys"+r,type:"TABLE",value:new se("langSys",[{name:"lookupOrder",type:"USHORT",value:0},{name:"reqFeatureIndex",type:"USHORT",value:g.reqFeatureIndex}].concat(Io("featureIndex",g.featureIndexes)))}]})))}]}))}sn.prototype=Object.create(se.prototype);sn.prototype.constructor=sn;function an(A){se.call(this,"featureListTable",Co("featureRecord",A,function(e,t){var i=e.feature;return[{name:"featureTag"+t,type:"TAG",value:e.tag},{name:"feature"+t,type:"TABLE",value:new se("featureTable",[{name:"featureParams",type:"USHORT",value:i.featureParams}].concat(Io("lookupListIndex",i.lookupListIndexes)))}]}))}an.prototype=Object.create(se.prototype);an.prototype.constructor=an;function In(A,e){se.call(this,"lookupListTable",Ur("lookup",A,function(t){var i=e[t.lookupType];return mA.assert(!!i,"Unable to write GSUB lookup type "+t.lookupType+" tables."),new se("lookupTable",[{name:"lookupType",type:"USHORT",value:t.lookupType},{name:"lookupFlag",type:"USHORT",value:t.lookupFlag}].concat(Ur("subtable",t.subtables,i)))}))}In.prototype=Object.create(se.prototype);In.prototype.constructor=In;var lA={Table:se,Record:se,Coverage:gn,ScriptList:sn,FeatureList:an,LookupList:In,ushortList:Io,tableList:Ur,recordList:Co};function na(A,e){return A.getUint8(e)}function Cn(A,e){return A.getUint16(e,!1)}function Rw(A,e){return A.getInt16(e,!1)}function hg(A,e){return A.getUint32(e,!1)}function ZC(A,e){var t=A.getInt16(e,!1),i=A.getUint16(e+2,!1);return t+i/65535}function bw(A,e){for(var t="",i=e;i<e+4;i+=1)t+=String.fromCharCode(A.getInt8(i));return t}function Nw(A,e,t){for(var i=0,o=0;o<t;o+=1)i<<=8,i+=A.getUint8(e+o);return i}function Gw(A,e,t){for(var i=[],o=e;o<t;o+=1)i.push(A.getUint8(o));return i}function Lw(A){for(var e="",t=0;t<A.length;t+=1)e+=String.fromCharCode(A[t]);return e}var Uw={byte:1,uShort:2,short:2,uLong:4,fixed:4,longDateTime:8,tag:4};function J(A,e){this.data=A,this.offset=e,this.relativeOffset=0}J.prototype.parseByte=function(){var A=this.data.getUint8(this.offset+this.relativeOffset);return this.relativeOffset+=1,A};J.prototype.parseChar=function(){var A=this.data.getInt8(this.offset+this.relativeOffset);return this.relativeOffset+=1,A};J.prototype.parseCard8=J.prototype.parseByte;J.prototype.parseUShort=function(){var A=this.data.getUint16(this.offset+this.relativeOffset);return this.relativeOffset+=2,A};J.prototype.parseCard16=J.prototype.parseUShort;J.prototype.parseSID=J.prototype.parseUShort;J.prototype.parseOffset16=J.prototype.parseUShort;J.prototype.parseShort=function(){var A=this.data.getInt16(this.offset+this.relativeOffset);return this.relativeOffset+=2,A};J.prototype.parseF2Dot14=function(){var A=this.data.getInt16(this.offset+this.relativeOffset)/16384;return this.relativeOffset+=2,A};J.prototype.parseULong=function(){var A=hg(this.data,this.offset+this.relativeOffset);return this.relativeOffset+=4,A};J.prototype.parseOffset32=J.prototype.parseULong;J.prototype.parseFixed=function(){var A=ZC(this.data,this.offset+this.relativeOffset);return this.relativeOffset+=4,A};J.prototype.parseString=function(A){var e=this.data,t=this.offset+this.relativeOffset,i="";this.relativeOffset+=A;for(var o=0;o<A;o++)i+=String.fromCharCode(e.getUint8(t+o));return i};J.prototype.parseTag=function(){return this.parseString(4)};J.prototype.parseLongDateTime=function(){var A=hg(this.data,this.offset+this.relativeOffset+4);return A-=2082844800,this.relativeOffset+=8,A};J.prototype.parseVersion=function(A){var e=Cn(this.data,this.offset+this.relativeOffset),t=Cn(this.data,this.offset+this.relativeOffset+2);return this.relativeOffset+=4,A===void 0&&(A=4096),e+t/A/10};J.prototype.skip=function(A,e){e===void 0&&(e=1),this.relativeOffset+=Uw[A]*e};J.prototype.parseULongList=function(A){A===void 0&&(A=this.parseULong());for(var e=new Array(A),t=this.data,i=this.offset+this.relativeOffset,o=0;o<A;o++)e[o]=t.getUint32(i),i+=4;return this.relativeOffset+=A*4,e};J.prototype.parseOffset16List=J.prototype.parseUShortList=function(A){A===void 0&&(A=this.parseUShort());for(var e=new Array(A),t=this.data,i=this.offset+this.relativeOffset,o=0;o<A;o++)e[o]=t.getUint16(i),i+=2;return this.relativeOffset+=A*2,e};J.prototype.parseShortList=function(A){for(var e=new Array(A),t=this.data,i=this.offset+this.relativeOffset,o=0;o<A;o++)e[o]=t.getInt16(i),i+=2;return this.relativeOffset+=A*2,e};J.prototype.parseByteList=function(A){for(var e=new Array(A),t=this.data,i=this.offset+this.relativeOffset,o=0;o<A;o++)e[o]=t.getUint8(i++);return this.relativeOffset+=A,e};J.prototype.parseList=function(A,e){e||(e=A,A=this.parseUShort());for(var t=new Array(A),i=0;i<A;i++)t[i]=e.call(this);return t};J.prototype.parseList32=function(A,e){e||(e=A,A=this.parseULong());for(var t=new Array(A),i=0;i<A;i++)t[i]=e.call(this);return t};J.prototype.parseRecordList=function(A,e){e||(e=A,A=this.parseUShort());for(var t=new Array(A),i=Object.keys(e),o=0;o<A;o++){for(var n={},r=0;r<i.length;r++){var g=i[r],s=e[g];n[g]=s.call(this)}t[o]=n}return t};J.prototype.parseRecordList32=function(A,e){e||(e=A,A=this.parseULong());for(var t=new Array(A),i=Object.keys(e),o=0;o<A;o++){for(var n={},r=0;r<i.length;r++){var g=i[r],s=e[g];n[g]=s.call(this)}t[o]=n}return t};J.prototype.parseStruct=function(A){if(typeof A=="function")return A.call(this);for(var e=Object.keys(A),t={},i=0;i<e.length;i++){var o=e[i],n=A[o];t[o]=n.call(this)}return t};J.prototype.parseValueRecord=function(A){if(A===void 0&&(A=this.parseUShort()),A!==0){var e={};return A&1&&(e.xPlacement=this.parseShort()),A&2&&(e.yPlacement=this.parseShort()),A&4&&(e.xAdvance=this.parseShort()),A&8&&(e.yAdvance=this.parseShort()),A&16&&(e.xPlaDevice=void 0,this.parseShort()),A&32&&(e.yPlaDevice=void 0,this.parseShort()),A&64&&(e.xAdvDevice=void 0,this.parseShort()),A&128&&(e.yAdvDevice=void 0,this.parseShort()),e}};J.prototype.parseValueRecordList=function(){for(var A=this.parseUShort(),e=this.parseUShort(),t=new Array(e),i=0;i<e;i++)t[i]=this.parseValueRecord(A);return t};J.prototype.parsePointer=function(A){var e=this.parseOffset16();if(e>0)return new J(this.data,this.offset+e).parseStruct(A)};J.prototype.parsePointer32=function(A){var e=this.parseOffset32();if(e>0)return new J(this.data,this.offset+e).parseStruct(A)};J.prototype.parseListOfLists=function(A){for(var e=this.parseOffset16List(),t=e.length,i=this.relativeOffset,o=new Array(t),n=0;n<t;n++){var r=e[n];if(r===0){o[n]=void 0;continue}if(this.relativeOffset=r,A){for(var g=this.parseOffset16List(),s=new Array(g.length),a=0;a<g.length;a++)this.relativeOffset=r+g[a],s[a]=A.call(this);o[n]=s}else o[n]=this.parseUShortList()}return this.relativeOffset=i,o};J.prototype.parseCoverage=function(){var A=this.offset+this.relativeOffset,e=this.parseUShort(),t=this.parseUShort();if(e===1)return{format:1,glyphs:this.parseUShortList(t)};if(e===2){for(var i=new Array(t),o=0;o<t;o++)i[o]={start:this.parseUShort(),end:this.parseUShort(),index:this.parseUShort()};return{format:2,ranges:i}}throw new Error("0x"+A.toString(16)+": Coverage format must be 1 or 2.")};J.prototype.parseClassDef=function(){var A=this.offset+this.relativeOffset,e=this.parseUShort();if(e===1)return{format:1,startGlyph:this.parseUShort(),classes:this.parseUShortList()};if(e===2)return{format:2,ranges:this.parseRecordList({start:J.uShort,end:J.uShort,classId:J.uShort})};throw new Error("0x"+A.toString(16)+": ClassDef format must be 1 or 2.")};J.list=function(A,e){return function(){return this.parseList(A,e)}};J.list32=function(A,e){return function(){return this.parseList32(A,e)}};J.recordList=function(A,e){return function(){return this.parseRecordList(A,e)}};J.recordList32=function(A,e){return function(){return this.parseRecordList32(A,e)}};J.pointer=function(A){return function(){return this.parsePointer(A)}};J.pointer32=function(A){return function(){return this.parsePointer32(A)}};J.tag=J.prototype.parseTag;J.byte=J.prototype.parseByte;J.uShort=J.offset16=J.prototype.parseUShort;J.uShortList=J.prototype.parseUShortList;J.uLong=J.offset32=J.prototype.parseULong;J.uLongList=J.prototype.parseULongList;J.struct=J.prototype.parseStruct;J.coverage=J.prototype.parseCoverage;J.classDef=J.prototype.parseClassDef;var ra={reserved:J.uShort,reqFeatureIndex:J.uShort,featureIndexes:J.uShortList};J.prototype.parseScriptList=function(){return this.parsePointer(J.recordList({tag:J.tag,script:J.pointer({defaultLangSys:J.pointer(ra),langSysRecords:J.recordList({tag:J.tag,langSys:J.pointer(ra)})})}))||[]};J.prototype.parseFeatureList=function(){return this.parsePointer(J.recordList({tag:J.tag,feature:J.pointer({featureParams:J.offset16,lookupListIndexes:J.uShortList})}))||[]};J.prototype.parseLookupList=function(A){return this.parsePointer(J.list(J.pointer(function(){var e=this.parseUShort();mA.argument(1<=e&&e<=9,"GPOS/GSUB lookup type "+e+" unknown.");var t=this.parseUShort(),i=t&16;return{lookupType:e,lookupFlag:t,subtables:this.parseList(J.pointer(A[e])),markFilteringSet:i?this.parseUShort():void 0}})))||[]};J.prototype.parseFeatureVariationsList=function(){return this.parsePointer32(function(){var A=this.parseUShort(),e=this.parseUShort();mA.argument(A===1&&e<1,"GPOS/GSUB feature variations table unknown.");var t=this.parseRecordList32({conditionSetOffset:J.offset32,featureTableSubstitutionOffset:J.offset32});return t})||[]};var QA={getByte:na,getCard8:na,getUShort:Cn,getCard16:Cn,getShort:Rw,getULong:hg,getFixed:ZC,getTag:bw,getOffset:Nw,getBytes:Gw,bytesToString:Lw,Parser:J};function Mw(A,e){e.parseUShort(),A.length=e.parseULong(),A.language=e.parseULong();var t;A.groupCount=t=e.parseULong(),A.glyphIndexMap={};for(var i=0;i<t;i+=1)for(var o=e.parseULong(),n=e.parseULong(),r=e.parseULong(),g=o;g<=n;g+=1)A.glyphIndexMap[g]=r,r++}function xw(A,e,t,i,o){A.length=e.parseUShort(),A.language=e.parseUShort();var n;A.segCount=n=e.parseUShort()>>1,e.skip("uShort",3),A.glyphIndexMap={};for(var r=new QA.Parser(t,i+o+14),g=new QA.Parser(t,i+o+16+n*2),s=new QA.Parser(t,i+o+16+n*4),a=new QA.Parser(t,i+o+16+n*6),I=i+o+16+n*8,l=0;l<n-1;l+=1)for(var C=void 0,Q=r.parseUShort(),f=g.parseUShort(),h=s.parseShort(),u=a.parseUShort(),E=f;E<=Q;E+=1)u!==0?(I=a.offset+a.relativeOffset-2,I+=u,I+=(E-f)*2,C=QA.getUShort(t,I),C!==0&&(C=C+h&65535)):C=E+h&65535,A.glyphIndexMap[E]=C}function Jw(A,e){var t={};t.version=QA.getUShort(A,e),mA.argument(t.version===0,"cmap table version should be 0."),t.numTables=QA.getUShort(A,e+2);for(var i=-1,o=t.numTables-1;o>=0;o-=1){var n=QA.getUShort(A,e+4+o*8),r=QA.getUShort(A,e+4+o*8+2);if(n===3&&(r===0||r===1||r===10)||n===0&&(r===0||r===1||r===2||r===3||r===4)){i=QA.getULong(A,e+4+o*8+4);break}}if(i===-1)throw new Error("No valid cmap sub-tables found.");var g=new QA.Parser(A,e+i);if(t.format=g.parseUShort(),t.format===12)Mw(t,g);else if(t.format===4)xw(t,g,A,e,i);else throw new Error("Only format 4 and 12 cmap tables are supported (found format "+t.format+").");return t}function Tw(A,e,t){A.segments.push({end:e,start:e,delta:-(e-t),offset:0,glyphIndex:t})}function Hw(A){A.segments.push({end:65535,start:65535,delta:1,offset:0})}function Yw(A){var e=!0,t;for(t=A.length-1;t>0;t-=1){var i=A.get(t);if(i.unicode>65535){console.log("Adding CMAP format 12 (needed!)"),e=!1;break}}var o=[{name:"version",type:"USHORT",value:0},{name:"numTables",type:"USHORT",value:e?1:2},{name:"platformID",type:"USHORT",value:3},{name:"encodingID",type:"USHORT",value:1},{name:"offset",type:"ULONG",value:e?12:20}];e||(o=o.concat([{name:"cmap12PlatformID",type:"USHORT",value:3},{name:"cmap12EncodingID",type:"USHORT",value:10},{name:"cmap12Offset",type:"ULONG",value:0}])),o=o.concat([{name:"format",type:"USHORT",value:4},{name:"cmap4Length",type:"USHORT",value:0},{name:"language",type:"USHORT",value:0},{name:"segCountX2",type:"USHORT",value:0},{name:"searchRange",type:"USHORT",value:0},{name:"entrySelector",type:"USHORT",value:0},{name:"rangeShift",type:"USHORT",value:0}]);var n=new lA.Table("cmap",o);for(n.segments=[],t=0;t<A.length;t+=1){for(var r=A.get(t),g=0;g<r.unicodes.length;g+=1)Tw(n,r.unicodes[g],t);n.segments=n.segments.sort(function(D,c){return D.start-c.start})}Hw(n);var s=n.segments.length,a=0,I=[],l=[],C=[],Q=[],f=[],h=[];for(t=0;t<s;t+=1){var u=n.segments[t];u.end<=65535&&u.start<=65535?(I=I.concat({name:"end_"+t,type:"USHORT",value:u.end}),l=l.concat({name:"start_"+t,type:"USHORT",value:u.start}),C=C.concat({name:"idDelta_"+t,type:"SHORT",value:u.delta}),Q=Q.concat({name:"idRangeOffset_"+t,type:"USHORT",value:u.offset}),u.glyphId!==void 0&&(f=f.concat({name:"glyph_"+t,type:"USHORT",value:u.glyphId}))):a+=1,!e&&u.glyphIndex!==void 0&&(h=h.concat({name:"cmap12Start_"+t,type:"ULONG",value:u.start}),h=h.concat({name:"cmap12End_"+t,type:"ULONG",value:u.end}),h=h.concat({name:"cmap12Glyph_"+t,type:"ULONG",value:u.glyphIndex}))}if(n.segCountX2=(s-a)*2,n.searchRange=Math.pow(2,Math.floor(Math.log(s-a)/Math.log(2)))*2,n.entrySelector=Math.log(n.searchRange/2)/Math.log(2),n.rangeShift=n.segCountX2-n.searchRange,n.fields=n.fields.concat(I),n.fields.push({name:"reservedPad",type:"USHORT",value:0}),n.fields=n.fields.concat(l),n.fields=n.fields.concat(C),n.fields=n.fields.concat(Q),n.fields=n.fields.concat(f),n.cmap4Length=14+I.length*2+2+l.length*2+C.length*2+Q.length*2+f.length*2,!e){var E=16+h.length*4;n.cmap12Offset=12+2*2+4+n.cmap4Length,n.fields=n.fields.concat([{name:"cmap12Format",type:"USHORT",value:12},{name:"cmap12Reserved",type:"USHORT",value:0},{name:"cmap12Length",type:"ULONG",value:E},{name:"cmap12Language",type:"ULONG",value:0},{name:"cmap12nGroups",type:"ULONG",value:h.length/3}]),n.fields=n.fields.concat(h)}return n}var XC={parse:Jw,make:Yw},Vo=[".notdef","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","exclamdown","cent","sterling","fraction","yen","florin","section","currency","quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright","fi","fl","endash","dagger","daggerdbl","periodcentered","paragraph","bullet","quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis","perthousand","questiondown","grave","acute","circumflex","tilde","macron","breve","dotaccent","dieresis","ring","cedilla","hungarumlaut","ogonek","caron","emdash","AE","ordfeminine","Lslash","Oslash","OE","ordmasculine","ae","dotlessi","lslash","oslash","oe","germandbls","onesuperior","logicalnot","mu","trademark","Eth","onehalf","plusminus","Thorn","onequarter","divide","brokenbar","degree","thorn","threequarters","twosuperior","registered","minus","eth","multiply","threesuperior","copyright","Aacute","Acircumflex","Adieresis","Agrave","Aring","Atilde","Ccedilla","Eacute","Ecircumflex","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Ntilde","Oacute","Ocircumflex","Odieresis","Ograve","Otilde","Scaron","Uacute","Ucircumflex","Udieresis","Ugrave","Yacute","Ydieresis","Zcaron","aacute","acircumflex","adieresis","agrave","aring","atilde","ccedilla","eacute","ecircumflex","edieresis","egrave","iacute","icircumflex","idieresis","igrave","ntilde","oacute","ocircumflex","odieresis","ograve","otilde","scaron","uacute","ucircumflex","udieresis","ugrave","yacute","ydieresis","zcaron","exclamsmall","Hungarumlautsmall","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","266 ff","onedotenleader","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","commasuperior","threequartersemdash","periodsuperior","questionsmall","asuperior","bsuperior","centsuperior","dsuperior","esuperior","isuperior","lsuperior","msuperior","nsuperior","osuperior","rsuperior","ssuperior","tsuperior","ff","ffi","ffl","parenleftinferior","parenrightinferior","Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","exclamdownsmall","centoldstyle","Lslashsmall","Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall","Dotaccentsmall","Macronsmall","figuredash","hypheninferior","Ogoneksmall","Ringsmall","Cedillasmall","questiondownsmall","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","zerosuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior","Agravesmall","Aacutesmall","Acircumflexsmall","Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall","Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall","Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall","Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall","Thornsmall","Ydieresissmall","001.000","001.001","001.002","001.003","Black","Bold","Book","Light","Medium","Regular","Roman","Semibold"],_w=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","exclamdown","cent","sterling","fraction","yen","florin","section","currency","quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright","fi","fl","","endash","dagger","daggerdbl","periodcentered","","paragraph","bullet","quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis","perthousand","","questiondown","","grave","acute","circumflex","tilde","macron","breve","dotaccent","dieresis","","ring","cedilla","","hungarumlaut","ogonek","caron","emdash","","","","","","","","","","","","","","","","","AE","","ordfeminine","","","","","Lslash","Oslash","OE","ordmasculine","","","","","","ae","","","","dotlessi","","","lslash","oslash","oe","germandbls"],Kw=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclamsmall","Hungarumlautsmall","","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","comma","hyphen","period","fraction","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","colon","semicolon","commasuperior","threequartersemdash","periodsuperior","questionsmall","","asuperior","bsuperior","centsuperior","dsuperior","esuperior","","","isuperior","","","lsuperior","msuperior","nsuperior","osuperior","","","rsuperior","ssuperior","tsuperior","","ff","fi","fl","ffi","ffl","parenleftinferior","","parenrightinferior","Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","exclamdownsmall","centoldstyle","Lslashsmall","","","Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall","","Dotaccentsmall","","","Macronsmall","","","figuredash","hypheninferior","","","Ogoneksmall","Ringsmall","Cedillasmall","","","","onequarter","onehalf","threequarters","questiondownsmall","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","","","zerosuperior","onesuperior","twosuperior","threesuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior","Agravesmall","Aacutesmall","Acircumflexsmall","Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall","Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall","Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall","Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall","Thornsmall","Ydieresissmall"],ii=[".notdef",".null","nonmarkingreturn","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quotesingle","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","grave","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","Adieresis","Aring","Ccedilla","Eacute","Ntilde","Odieresis","Udieresis","aacute","agrave","acircumflex","adieresis","atilde","aring","ccedilla","eacute","egrave","ecircumflex","edieresis","iacute","igrave","icircumflex","idieresis","ntilde","oacute","ograve","ocircumflex","odieresis","otilde","uacute","ugrave","ucircumflex","udieresis","dagger","degree","cent","sterling","section","bullet","paragraph","germandbls","registered","copyright","trademark","acute","dieresis","notequal","AE","Oslash","infinity","plusminus","lessequal","greaterequal","yen","mu","partialdiff","summation","product","pi","integral","ordfeminine","ordmasculine","Omega","ae","oslash","questiondown","exclamdown","logicalnot","radical","florin","approxequal","Delta","guillemotleft","guillemotright","ellipsis","nonbreakingspace","Agrave","Atilde","Otilde","OE","oe","endash","emdash","quotedblleft","quotedblright","quoteleft","quoteright","divide","lozenge","ydieresis","Ydieresis","fraction","currency","guilsinglleft","guilsinglright","fi","fl","daggerdbl","periodcentered","quotesinglbase","quotedblbase","perthousand","Acircumflex","Ecircumflex","Aacute","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Oacute","Ocircumflex","apple","Ograve","Uacute","Ucircumflex","Ugrave","dotlessi","circumflex","tilde","macron","breve","dotaccent","ring","cedilla","hungarumlaut","ogonek","caron","Lslash","lslash","Scaron","scaron","Zcaron","zcaron","brokenbar","Eth","eth","Yacute","yacute","Thorn","thorn","minus","multiply","onesuperior","twosuperior","threesuperior","onehalf","onequarter","threequarters","franc","Gbreve","gbreve","Idotaccent","Scedilla","scedilla","Cacute","cacute","Ccaron","ccaron","dcroat"];function zC(A){this.font=A}zC.prototype.charToGlyphIndex=function(A){var e=A.codePointAt(0),t=this.font.glyphs;if(t){for(var i=0;i<t.length;i+=1)for(var o=t.get(i),n=0;n<o.unicodes.length;n+=1)if(o.unicodes[n]===e)return i}return null};function $C(A){this.cmap=A}$C.prototype.charToGlyphIndex=function(A){return this.cmap.glyphIndexMap[A.codePointAt(0)]||0};function ln(A,e){this.encoding=A,this.charset=e}ln.prototype.charToGlyphIndex=function(A){var e=A.codePointAt(0),t=this.encoding[e];return this.charset.indexOf(t)};function dg(A){switch(A.version){case 1:this.names=ii.slice();break;case 2:this.names=new Array(A.numberOfGlyphs);for(var e=0;e<A.numberOfGlyphs;e++)A.glyphNameIndex[e]<ii.length?this.names[e]=ii[A.glyphNameIndex[e]]:this.names[e]=A.names[A.glyphNameIndex[e]-ii.length];break;case 2.5:this.names=new Array(A.numberOfGlyphs);for(var t=0;t<A.numberOfGlyphs;t++)this.names[t]=ii[t+A.glyphNameIndex[t]];break;case 3:this.names=[];break;default:this.names=[];break}}dg.prototype.nameToGlyphIndex=function(A){return this.names.indexOf(A)};dg.prototype.glyphIndexToName=function(A){return this.names[A]};function Ow(A){for(var e,t=A.tables.cmap.glyphIndexMap,i=Object.keys(t),o=0;o<i.length;o+=1){var n=i[o],r=t[n];e=A.glyphs.get(r),e.addUnicode(parseInt(n))}for(var g=0;g<A.glyphs.length;g+=1)e=A.glyphs.get(g),A.cffEncoding?A.isCIDFont?e.name="gid"+g:e.name=A.cffEncoding.charset[g]:A.glyphNames.names&&(e.name=A.glyphNames.glyphIndexToName(g))}function Pw(A){A._IndexToUnicodeMap={};for(var e=A.tables.cmap.glyphIndexMap,t=Object.keys(e),i=0;i<t.length;i+=1){var o=t[i],n=e[o];A._IndexToUnicodeMap[n]===void 0?A._IndexToUnicodeMap[n]={unicodes:[parseInt(o)]}:A._IndexToUnicodeMap[n].unicodes.push(parseInt(o))}}function Vw(A,e){e.lowMemory?Pw(A):Ow(A)}function Ww(A,e,t,i,o){A.beginPath(),A.moveTo(e,t),A.lineTo(i,o),A.stroke()}var Ai={line:Ww};function qw(A,e){var t=e||new ie;return{configurable:!0,get:function(){return typeof t=="function"&&(t=t()),t},set:function(i){t=i}}}function Ne(A){this.bindConstructorValues(A)}Ne.prototype.bindConstructorValues=function(A){this.index=A.index||0,this.name=A.name||null,this.unicode=A.unicode||void 0,this.unicodes=A.unicodes||A.unicode!==void 0?[A.unicode]:[],"xMin"in A&&(this.xMin=A.xMin),"yMin"in A&&(this.yMin=A.yMin),"xMax"in A&&(this.xMax=A.xMax),"yMax"in A&&(this.yMax=A.yMax),"advanceWidth"in A&&(this.advanceWidth=A.advanceWidth),Object.defineProperty(this,"path",qw(this,A.path))};Ne.prototype.addUnicode=function(A){this.unicodes.length===0&&(this.unicode=A),this.unicodes.push(A)};Ne.prototype.getBoundingBox=function(){return this.path.getBoundingBox()};Ne.prototype.getPath=function(A,e,t,i,o){A=A!==void 0?A:0,e=e!==void 0?e:0,t=t!==void 0?t:72;var n,r;i||(i={});var g=i.xScale,s=i.yScale;if(i.hinting&&o&&o.hinting&&(r=this.path&&o.hinting.exec(this,t)),r)n=o.hinting.getCommands(r),A=Math.round(A),e=Math.round(e),g=s=1;else{n=this.path.commands;var a=1/(this.path.unitsPerEm||1e3)*t;g===void 0&&(g=a),s===void 0&&(s=a)}for(var I=new ie,l=0;l<n.length;l+=1){var C=n[l];C.type==="M"?I.moveTo(A+C.x*g,e+-C.y*s):C.type==="L"?I.lineTo(A+C.x*g,e+-C.y*s):C.type==="Q"?I.quadraticCurveTo(A+C.x1*g,e+-C.y1*s,A+C.x*g,e+-C.y*s):C.type==="C"?I.curveTo(A+C.x1*g,e+-C.y1*s,A+C.x2*g,e+-C.y2*s,A+C.x*g,e+-C.y*s):C.type==="Z"&&I.closePath()}return I};Ne.prototype.getContours=function(){if(this.points===void 0)return[];for(var A=[],e=[],t=0;t<this.points.length;t+=1){var i=this.points[t];e.push(i),i.lastPointOfContour&&(A.push(e),e=[])}return mA.argument(e.length===0,"There are still points left in the current contour."),A};Ne.prototype.getMetrics=function(){for(var A=this.path.commands,e=[],t=[],i=0;i<A.length;i+=1){var o=A[i];o.type!=="Z"&&(e.push(o.x),t.push(o.y)),(o.type==="Q"||o.type==="C")&&(e.push(o.x1),t.push(o.y1)),o.type==="C"&&(e.push(o.x2),t.push(o.y2))}var n={xMin:Math.min.apply(null,e),yMin:Math.min.apply(null,t),xMax:Math.max.apply(null,e),yMax:Math.max.apply(null,t),leftSideBearing:this.leftSideBearing};return isFinite(n.xMin)||(n.xMin=0),isFinite(n.xMax)||(n.xMax=this.advanceWidth),isFinite(n.yMin)||(n.yMin=0),isFinite(n.yMax)||(n.yMax=0),n.rightSideBearing=this.advanceWidth-n.leftSideBearing-(n.xMax-n.xMin),n};Ne.prototype.draw=function(A,e,t,i,o){this.getPath(e,t,i,o).draw(A)};Ne.prototype.drawPoints=function(A,e,t,i){function o(l,C,Q,f){A.beginPath();for(var h=0;h<l.length;h+=1)A.moveTo(C+l[h].x*f,Q+l[h].y*f),A.arc(C+l[h].x*f,Q+l[h].y*f,2,0,Math.PI*2,!1);A.closePath(),A.fill()}e=e!==void 0?e:0,t=t!==void 0?t:0,i=i!==void 0?i:24;for(var n=1/this.path.unitsPerEm*i,r=[],g=[],s=this.path,a=0;a<s.commands.length;a+=1){var I=s.commands[a];I.x!==void 0&&r.push({x:I.x,y:-I.y}),I.x1!==void 0&&g.push({x:I.x1,y:-I.y1}),I.x2!==void 0&&g.push({x:I.x2,y:-I.y2})}A.fillStyle="blue",o(r,e,t,n),A.fillStyle="red",o(g,e,t,n)};Ne.prototype.drawMetrics=function(A,e,t,i){var o;e=e!==void 0?e:0,t=t!==void 0?t:0,i=i!==void 0?i:24,o=1/this.path.unitsPerEm*i,A.lineWidth=1,A.strokeStyle="black",Ai.line(A,e,-1e4,e,1e4),Ai.line(A,-1e4,t,1e4,t);var n=this.xMin||0,r=this.yMin||0,g=this.xMax||0,s=this.yMax||0,a=this.advanceWidth||0;A.strokeStyle="blue",Ai.line(A,e+n*o,-1e4,e+n*o,1e4),Ai.line(A,e+g*o,-1e4,e+g*o,1e4),Ai.line(A,-1e4,t+-r*o,1e4,t+-r*o),Ai.line(A,-1e4,t+-s*o,1e4,t+-s*o),A.strokeStyle="green",Ai.line(A,e+a*o,-1e4,e+a*o,1e4)};function Mo(A,e,t){Object.defineProperty(A,e,{get:function(){return A.path,A[t]},set:function(i){A[t]=i},enumerable:!0,configurable:!0})}function pg(A,e){if(this.font=A,this.glyphs={},Array.isArray(e))for(var t=0;t<e.length;t++){var i=e[t];i.path.unitsPerEm=A.unitsPerEm,this.glyphs[t]=i}this.length=e&&e.length||0}pg.prototype.get=function(A){if(this.glyphs[A]===void 0){this.font._push(A),typeof this.glyphs[A]=="function"&&(this.glyphs[A]=this.glyphs[A]());var e=this.glyphs[A],t=this.font._IndexToUnicodeMap[A];if(t)for(var i=0;i<t.unicodes.length;i++)e.addUnicode(t.unicodes[i]);this.font.cffEncoding?this.font.isCIDFont?e.name="gid"+A:e.name=this.font.cffEncoding.charset[A]:this.font.glyphNames.names&&(e.name=this.font.glyphNames.glyphIndexToName(A)),this.glyphs[A].advanceWidth=this.font._hmtxTableData[A].advanceWidth,this.glyphs[A].leftSideBearing=this.font._hmtxTableData[A].leftSideBearing}else typeof this.glyphs[A]=="function"&&(this.glyphs[A]=this.glyphs[A]());return this.glyphs[A]};pg.prototype.push=function(A,e){this.glyphs[A]=e,this.length++};function jw(A,e){return new Ne({index:e,font:A})}function Zw(A,e,t,i,o,n){return function(){var r=new Ne({index:e,font:A});return r.path=function(){t(r,i,o);var g=n(A.glyphs,r);return g.unitsPerEm=A.unitsPerEm,g},Mo(r,"xMin","_xMin"),Mo(r,"xMax","_xMax"),Mo(r,"yMin","_yMin"),Mo(r,"yMax","_yMax"),r}}function Xw(A,e,t,i){return function(){var o=new Ne({index:e,font:A});return o.path=function(){var n=t(A,o,i);return n.unitsPerEm=A.unitsPerEm,n},o}}var at={GlyphSet:pg,glyphLoader:jw,ttfGlyphLoader:Zw,cffGlyphLoader:Xw};function Al(A,e){if(A===e)return!0;if(Array.isArray(A)&&Array.isArray(e)){if(A.length!==e.length)return!1;for(var t=0;t<A.length;t+=1)if(!Al(A[t],e[t]))return!1;return!0}else return!1}function Mr(A){var e;return A.length<1240?e=107:A.length<33900?e=1131:e=32768,e}function Lt(A,e,t){var i=[],o=[],n=QA.getCard16(A,e),r,g;if(n!==0){var s=QA.getByte(A,e+2);r=e+(n+1)*s+2;for(var a=e+3,I=0;I<n+1;I+=1)i.push(QA.getOffset(A,a,s)),a+=s;g=r+i[n]}else g=e+2;for(var l=0;l<i.length-1;l+=1){var C=QA.getBytes(A,r+i[l],r+i[l+1]);t&&(C=t(C)),o.push(C)}return{objects:o,startOffset:e,endOffset:g}}function zw(A,e){var t=[],i=QA.getCard16(A,e),o,n;if(i!==0){var r=QA.getByte(A,e+2);o=e+(i+1)*r+2;for(var g=e+3,s=0;s<i+1;s+=1)t.push(QA.getOffset(A,g,r)),g+=r;n=o+t[i]}else n=e+2;return{offsets:t,startOffset:e,endOffset:n}}function $w(A,e,t,i,o){var n=QA.getCard16(t,i),r=0;if(n!==0){var g=QA.getByte(t,i+2);r=i+(n+1)*g+2}var s=QA.getBytes(t,r+e[A],r+e[A+1]);return s}function AD(A){for(var e="",t=15,i=["0","1","2","3","4","5","6","7","8","9",".","E","E-",null,"-"];;){var o=A.parseByte(),n=o>>4,r=o&15;if(n===t||(e+=i[n],r===t))break;e+=i[r]}return parseFloat(e)}function eD(A,e){var t,i,o,n;if(e===28)return t=A.parseByte(),i=A.parseByte(),t<<8|i;if(e===29)return t=A.parseByte(),i=A.parseByte(),o=A.parseByte(),n=A.parseByte(),t<<24|i<<16|o<<8|n;if(e===30)return AD(A);if(e>=32&&e<=246)return e-139;if(e>=247&&e<=250)return t=A.parseByte(),(e-247)*256+t+108;if(e>=251&&e<=254)return t=A.parseByte(),-(e-251)*256-t-108;throw new Error("Invalid b0 "+e)}function tD(A){for(var e={},t=0;t<A.length;t+=1){var i=A[t][0],o=A[t][1],n=void 0;if(o.length===1?n=o[0]:n=o,e.hasOwnProperty(i)&&!isNaN(e[i]))throw new Error("Object "+e+" already has key "+i);e[i]=n}return e}function el(A,e,t){e=e!==void 0?e:0;var i=new QA.Parser(A,e),o=[],n=[];for(t=t!==void 0?t:A.length;i.relativeOffset<t;){var r=i.parseByte();r<=21?(r===12&&(r=1200+i.parseByte()),o.push([r,n]),n=[]):n.push(eD(i,r))}return tD(o)}function Xi(A,e){return e<=390?e=Vo[e]:e=A[e-391],e}function tl(A,e,t){for(var i={},o,n=0;n<e.length;n+=1){var r=e[n];if(Array.isArray(r.type)){var g=[];g.length=r.type.length;for(var s=0;s<r.type.length;s++)o=A[r.op]!==void 0?A[r.op][s]:void 0,o===void 0&&(o=r.value!==void 0&&r.value[s]!==void 0?r.value[s]:null),r.type[s]==="SID"&&(o=Xi(t,o)),g[s]=o;i[r.name]=g}else o=A[r.op],o===void 0&&(o=r.value!==void 0?r.value:null),r.type==="SID"&&(o=Xi(t,o)),i[r.name]=o}return i}function iD(A,e){var t={};return t.formatMajor=QA.getCard8(A,e),t.formatMinor=QA.getCard8(A,e+1),t.size=QA.getCard8(A,e+2),t.offsetSize=QA.getCard8(A,e+3),t.startOffset=e,t.endOffset=e+4,t}var il=[{name:"version",op:0,type:"SID"},{name:"notice",op:1,type:"SID"},{name:"copyright",op:1200,type:"SID"},{name:"fullName",op:2,type:"SID"},{name:"familyName",op:3,type:"SID"},{name:"weight",op:4,type:"SID"},{name:"isFixedPitch",op:1201,type:"number",value:0},{name:"italicAngle",op:1202,type:"number",value:0},{name:"underlinePosition",op:1203,type:"number",value:-100},{name:"underlineThickness",op:1204,type:"number",value:50},{name:"paintType",op:1205,type:"number",value:0},{name:"charstringType",op:1206,type:"number",value:2},{name:"fontMatrix",op:1207,type:["real","real","real","real","real","real"],value:[.001,0,0,.001,0,0]},{name:"uniqueId",op:13,type:"number"},{name:"fontBBox",op:5,type:["number","number","number","number"],value:[0,0,0,0]},{name:"strokeWidth",op:1208,type:"number",value:0},{name:"xuid",op:14,type:[],value:null},{name:"charset",op:15,type:"offset",value:0},{name:"encoding",op:16,type:"offset",value:0},{name:"charStrings",op:17,type:"offset",value:0},{name:"private",op:18,type:["number","offset"],value:[0,0]},{name:"ros",op:1230,type:["SID","SID","number"]},{name:"cidFontVersion",op:1231,type:"number",value:0},{name:"cidFontRevision",op:1232,type:"number",value:0},{name:"cidFontType",op:1233,type:"number",value:0},{name:"cidCount",op:1234,type:"number",value:8720},{name:"uidBase",op:1235,type:"number"},{name:"fdArray",op:1236,type:"offset"},{name:"fdSelect",op:1237,type:"offset"},{name:"fontName",op:1238,type:"SID"}],ol=[{name:"subrs",op:19,type:"offset",value:0},{name:"defaultWidthX",op:20,type:"number",value:0},{name:"nominalWidthX",op:21,type:"number",value:0}];function oD(A,e){var t=el(A,0,A.byteLength);return tl(t,il,e)}function nl(A,e,t,i){var o=el(A,e,t);return tl(o,ol,i)}function ga(A,e,t,i){for(var o=[],n=0;n<t.length;n+=1){var r=new DataView(new Uint8Array(t[n]).buffer),g=oD(r,i);g._subrs=[],g._subrsBias=0,g._defaultWidthX=0,g._nominalWidthX=0;var s=g.private[0],a=g.private[1];if(s!==0&&a!==0){var I=nl(A,a+e,s,i);if(g._defaultWidthX=I.defaultWidthX,g._nominalWidthX=I.nominalWidthX,I.subrs!==0){var l=a+I.subrs,C=Lt(A,l+e);g._subrs=C.objects,g._subrsBias=Mr(g._subrs)}g._privateDict=I}o.push(g)}return o}function nD(A,e,t,i){var o,n,r=new QA.Parser(A,e);t-=1;var g=[".notdef"],s=r.parseCard8();if(s===0)for(var a=0;a<t;a+=1)o=r.parseSID(),g.push(Xi(i,o));else if(s===1)for(;g.length<=t;){o=r.parseSID(),n=r.parseCard8();for(var I=0;I<=n;I+=1)g.push(Xi(i,o)),o+=1}else if(s===2)for(;g.length<=t;){o=r.parseSID(),n=r.parseCard16();for(var l=0;l<=n;l+=1)g.push(Xi(i,o)),o+=1}else throw new Error("Unknown charset format "+s);return g}function rD(A,e,t){var i,o={},n=new QA.Parser(A,e),r=n.parseCard8();if(r===0)for(var g=n.parseCard8(),s=0;s<g;s+=1)i=n.parseCard8(),o[i]=s;else if(r===1){var a=n.parseCard8();i=1;for(var I=0;I<a;I+=1)for(var l=n.parseCard8(),C=n.parseCard8(),Q=l;Q<=l+C;Q+=1)o[Q]=i,i+=1}else throw new Error("Unknown encoding format "+r);return new ln(o,t)}function sa(A,e,t){var i,o,n,r,g=new ie,s=[],a=0,I=!1,l=!1,C=0,Q=0,f,h,u,E;if(A.isCIDFont){var D=A.tables.cff.topDict._fdSelect[e.index],c=A.tables.cff.topDict._fdArray[D];f=c._subrs,h=c._subrsBias,u=c._defaultWidthX,E=c._nominalWidthX}else f=A.tables.cff.topDict._subrs,h=A.tables.cff.topDict._subrsBias,u=A.tables.cff.topDict._defaultWidthX,E=A.tables.cff.topDict._nominalWidthX;var p=u;function d(N,H){l&&g.closePath(),g.moveTo(N,H),l=!0}function v(){var N;N=s.length%2!==0,N&&!I&&(p=s.shift()+E),a+=s.length>>1,s.length=0,I=!0}function b(N){for(var H,M,U,R,j,rA,IA,AA,z,oA,dA,Y,W=0;W<N.length;){var $=N[W];switch(W+=1,$){case 1:v();break;case 3:v();break;case 4:s.length>1&&!I&&(p=s.shift()+E,I=!0),Q+=s.pop(),d(C,Q);break;case 5:for(;s.length>0;)C+=s.shift(),Q+=s.shift(),g.lineTo(C,Q);break;case 6:for(;s.length>0&&(C+=s.shift(),g.lineTo(C,Q),s.length!==0);)Q+=s.shift(),g.lineTo(C,Q);break;case 7:for(;s.length>0&&(Q+=s.shift(),g.lineTo(C,Q),s.length!==0);)C+=s.shift(),g.lineTo(C,Q);break;case 8:for(;s.length>0;)i=C+s.shift(),o=Q+s.shift(),n=i+s.shift(),r=o+s.shift(),C=n+s.shift(),Q=r+s.shift(),g.curveTo(i,o,n,r,C,Q);break;case 10:j=s.pop()+h,rA=f[j],rA&&b(rA);break;case 11:return;case 12:switch($=N[W],W+=1,$){case 35:i=C+s.shift(),o=Q+s.shift(),n=i+s.shift(),r=o+s.shift(),IA=n+s.shift(),AA=r+s.shift(),z=IA+s.shift(),oA=AA+s.shift(),dA=z+s.shift(),Y=oA+s.shift(),C=dA+s.shift(),Q=Y+s.shift(),s.shift(),g.curveTo(i,o,n,r,IA,AA),g.curveTo(z,oA,dA,Y,C,Q);break;case 34:i=C+s.shift(),o=Q,n=i+s.shift(),r=o+s.shift(),IA=n+s.shift(),AA=r,z=IA+s.shift(),oA=r,dA=z+s.shift(),Y=Q,C=dA+s.shift(),g.curveTo(i,o,n,r,IA,AA),g.curveTo(z,oA,dA,Y,C,Q);break;case 36:i=C+s.shift(),o=Q+s.shift(),n=i+s.shift(),r=o+s.shift(),IA=n+s.shift(),AA=r,z=IA+s.shift(),oA=r,dA=z+s.shift(),Y=oA+s.shift(),C=dA+s.shift(),g.curveTo(i,o,n,r,IA,AA),g.curveTo(z,oA,dA,Y,C,Q);break;case 37:i=C+s.shift(),o=Q+s.shift(),n=i+s.shift(),r=o+s.shift(),IA=n+s.shift(),AA=r+s.shift(),z=IA+s.shift(),oA=AA+s.shift(),dA=z+s.shift(),Y=oA+s.shift(),Math.abs(dA-C)>Math.abs(Y-Q)?C=dA+s.shift():Q=Y+s.shift(),g.curveTo(i,o,n,r,IA,AA),g.curveTo(z,oA,dA,Y,C,Q);break;default:console.log("Glyph "+e.index+": unknown operator 1200"+$),s.length=0}break;case 14:s.length>0&&!I&&(p=s.shift()+E,I=!0),l&&(g.closePath(),l=!1);break;case 18:v();break;case 19:case 20:v(),W+=a+7>>3;break;case 21:s.length>2&&!I&&(p=s.shift()+E,I=!0),Q+=s.pop(),C+=s.pop(),d(C,Q);break;case 22:s.length>1&&!I&&(p=s.shift()+E,I=!0),C+=s.pop(),d(C,Q);break;case 23:v();break;case 24:for(;s.length>2;)i=C+s.shift(),o=Q+s.shift(),n=i+s.shift(),r=o+s.shift(),C=n+s.shift(),Q=r+s.shift(),g.curveTo(i,o,n,r,C,Q);C+=s.shift(),Q+=s.shift(),g.lineTo(C,Q);break;case 25:for(;s.length>6;)C+=s.shift(),Q+=s.shift(),g.lineTo(C,Q);i=C+s.shift(),o=Q+s.shift(),n=i+s.shift(),r=o+s.shift(),C=n+s.shift(),Q=r+s.shift(),g.curveTo(i,o,n,r,C,Q);break;case 26:for(s.length%2&&(C+=s.shift());s.length>0;)i=C,o=Q+s.shift(),n=i+s.shift(),r=o+s.shift(),C=n,Q=r+s.shift(),g.curveTo(i,o,n,r,C,Q);break;case 27:for(s.length%2&&(Q+=s.shift());s.length>0;)i=C+s.shift(),o=Q,n=i+s.shift(),r=o+s.shift(),C=n+s.shift(),Q=r,g.curveTo(i,o,n,r,C,Q);break;case 28:H=N[W],M=N[W+1],s.push((H<<24|M<<16)>>16),W+=2;break;case 29:j=s.pop()+A.gsubrsBias,rA=A.gsubrs[j],rA&&b(rA);break;case 30:for(;s.length>0&&(i=C,o=Q+s.shift(),n=i+s.shift(),r=o+s.shift(),C=n+s.shift(),Q=r+(s.length===1?s.shift():0),g.curveTo(i,o,n,r,C,Q),s.length!==0);)i=C+s.shift(),o=Q,n=i+s.shift(),r=o+s.shift(),Q=r+s.shift(),C=n+(s.length===1?s.shift():0),g.curveTo(i,o,n,r,C,Q);break;case 31:for(;s.length>0&&(i=C+s.shift(),o=Q,n=i+s.shift(),r=o+s.shift(),Q=r+s.shift(),C=n+(s.length===1?s.shift():0),g.curveTo(i,o,n,r,C,Q),s.length!==0);)i=C,o=Q+s.shift(),n=i+s.shift(),r=o+s.shift(),C=n+s.shift(),Q=r+(s.length===1?s.shift():0),g.curveTo(i,o,n,r,C,Q);break;default:$<32?console.log("Glyph "+e.index+": unknown operator "+$):$<247?s.push($-139):$<251?(H=N[W],W+=1,s.push(($-247)*256+H+108)):$<255?(H=N[W],W+=1,s.push(-($-251)*256-H-108)):(H=N[W],M=N[W+1],U=N[W+2],R=N[W+3],W+=4,s.push((H<<24|M<<16|U<<8|R)/65536))}}}return b(t),e.advanceWidth=p,g}function gD(A,e,t,i){var o=[],n,r=new QA.Parser(A,e),g=r.parseCard8();if(g===0)for(var s=0;s<t;s++){if(n=r.parseCard8(),n>=i)throw new Error("CFF table CID Font FDSelect has bad FD index value "+n+" (FD count "+i+")");o.push(n)}else if(g===3){var a=r.parseCard16(),I=r.parseCard16();if(I!==0)throw new Error("CFF Table CID Font FDSelect format 3 range has bad initial GID "+I);for(var l,C=0;C<a;C++){if(n=r.parseCard8(),l=r.parseCard16(),n>=i)throw new Error("CFF table CID Font FDSelect has bad FD index value "+n+" (FD count "+i+")");if(l>t)throw new Error("CFF Table CID Font FDSelect format 3 range has bad GID "+l);for(;I<l;I++)o.push(n);I=l}if(l!==t)throw new Error("CFF Table CID Font FDSelect format 3 range has bad final GID "+l)}else throw new Error("CFF Table CID Font FDSelect table has unsupported format "+g);return o}function sD(A,e,t,i){t.tables.cff={};var o=iD(A,e),n=Lt(A,o.endOffset,QA.bytesToString),r=Lt(A,n.endOffset),g=Lt(A,r.endOffset,QA.bytesToString),s=Lt(A,g.endOffset);t.gsubrs=s.objects,t.gsubrsBias=Mr(t.gsubrs);var a=ga(A,e,r.objects,g.objects);if(a.length!==1)throw new Error("CFF table has too many fonts in 'FontSet' - count of fonts NameIndex.length = "+a.length);var I=a[0];if(t.tables.cff.topDict=I,I._privateDict&&(t.defaultWidthX=I._privateDict.defaultWidthX,t.nominalWidthX=I._privateDict.nominalWidthX),I.ros[0]!==void 0&&I.ros[1]!==void 0&&(t.isCIDFont=!0),t.isCIDFont){var l=I.fdArray,C=I.fdSelect;if(l===0||C===0)throw new Error("Font is marked as a CID font, but FDArray and/or FDSelect information is missing");l+=e;var Q=Lt(A,l),f=ga(A,e,Q.objects,g.objects);I._fdArray=f,C+=e,I._fdSelect=gD(A,C,t.numGlyphs,f.length)}var h=e+I.private[1],u=nl(A,h,I.private[0],g.objects);if(t.defaultWidthX=u.defaultWidthX,t.nominalWidthX=u.nominalWidthX,u.subrs!==0){var E=h+u.subrs,D=Lt(A,E);t.subrs=D.objects,t.subrsBias=Mr(t.subrs)}else t.subrs=[],t.subrsBias=0;var c;i.lowMemory?(c=zw(A,e+I.charStrings),t.nGlyphs=c.offsets.length):(c=Lt(A,e+I.charStrings),t.nGlyphs=c.objects.length);var p=nD(A,e+I.charset,t.nGlyphs,g.objects);if(I.encoding===0?t.cffEncoding=new ln(_w,p):I.encoding===1?t.cffEncoding=new ln(Kw,p):t.cffEncoding=rD(A,e+I.encoding,p),t.encoding=t.encoding||t.cffEncoding,t.glyphs=new at.GlyphSet(t),i.lowMemory)t._push=function(b){var N=$w(b,c.offsets,A,e+I.charStrings);t.glyphs.push(b,at.cffGlyphLoader(t,b,sa,N))};else for(var d=0;d<t.nGlyphs;d+=1){var v=c.objects[d];t.glyphs.push(d,at.cffGlyphLoader(t,d,sa,v))}}function rl(A,e){var t,i=Vo.indexOf(A);return i>=0&&(t=i),i=e.indexOf(A),i>=0?t=i+Vo.length:(t=Vo.length+e.length,e.push(A)),t}function aD(){return new lA.Record("Header",[{name:"major",type:"Card8",value:1},{name:"minor",type:"Card8",value:0},{name:"hdrSize",type:"Card8",value:4},{name:"major",type:"Card8",value:1}])}function ID(A){var e=new lA.Record("Name INDEX",[{name:"names",type:"INDEX",value:[]}]);e.names=[];for(var t=0;t<A.length;t+=1)e.names.push({name:"name_"+t,type:"NAME",value:A[t]});return e}function gl(A,e,t){for(var i={},o=0;o<A.length;o+=1){var n=A[o],r=e[n.name];r!==void 0&&!Al(r,n.value)&&(n.type==="SID"&&(r=rl(r,t)),i[n.op]={name:n.name,type:n.type,value:r})}return i}function aa(A,e){var t=new lA.Record("Top DICT",[{name:"dict",type:"DICT",value:{}}]);return t.dict=gl(il,A,e),t}function Ia(A){var e=new lA.Record("Top DICT INDEX",[{name:"topDicts",type:"INDEX",value:[]}]);return e.topDicts=[{name:"topDict_0",type:"TABLE",value:A}],e}function CD(A){var e=new lA.Record("String INDEX",[{name:"strings",type:"INDEX",value:[]}]);e.strings=[];for(var t=0;t<A.length;t+=1)e.strings.push({name:"string_"+t,type:"STRING",value:A[t]});return e}function lD(){return new lA.Record("Global Subr INDEX",[{name:"subrs",type:"INDEX",value:[]}])}function BD(A,e){for(var t=new lA.Record("Charsets",[{name:"format",type:"Card8",value:0}]),i=0;i<A.length;i+=1){var o=A[i],n=rl(o,e);t.fields.push({name:"glyph_"+i,type:"SID",value:n})}return t}function QD(A){var e=[],t=A.path;e.push({name:"width",type:"NUMBER",value:A.advanceWidth});for(var i=0,o=0,n=0;n<t.commands.length;n+=1){var r=void 0,g=void 0,s=t.commands[n];if(s.type==="Q"){var a=.3333333333333333,I=2/3;s={type:"C",x:s.x,y:s.y,x1:Math.round(a*i+I*s.x1),y1:Math.round(a*o+I*s.y1),x2:Math.round(a*s.x+I*s.x1),y2:Math.round(a*s.y+I*s.y1)}}if(s.type==="M")r=Math.round(s.x-i),g=Math.round(s.y-o),e.push({name:"dx",type:"NUMBER",value:r}),e.push({name:"dy",type:"NUMBER",value:g}),e.push({name:"rmoveto",type:"OP",value:21}),i=Math.round(s.x),o=Math.round(s.y);else if(s.type==="L")r=Math.round(s.x-i),g=Math.round(s.y-o),e.push({name:"dx",type:"NUMBER",value:r}),e.push({name:"dy",type:"NUMBER",value:g}),e.push({name:"rlineto",type:"OP",value:5}),i=Math.round(s.x),o=Math.round(s.y);else if(s.type==="C"){var l=Math.round(s.x1-i),C=Math.round(s.y1-o),Q=Math.round(s.x2-s.x1),f=Math.round(s.y2-s.y1);r=Math.round(s.x-s.x2),g=Math.round(s.y-s.y2),e.push({name:"dx1",type:"NUMBER",value:l}),e.push({name:"dy1",type:"NUMBER",value:C}),e.push({name:"dx2",type:"NUMBER",value:Q}),e.push({name:"dy2",type:"NUMBER",value:f}),e.push({name:"dx",type:"NUMBER",value:r}),e.push({name:"dy",type:"NUMBER",value:g}),e.push({name:"rrcurveto",type:"OP",value:8}),i=Math.round(s.x),o=Math.round(s.y)}}return e.push({name:"endchar",type:"OP",value:14}),e}function cD(A){for(var e=new lA.Record("CharStrings INDEX",[{name:"charStrings",type:"INDEX",value:[]}]),t=0;t<A.length;t+=1){var i=A.get(t),o=QD(i);e.charStrings.push({name:i.name,type:"CHARSTRING",value:o})}return e}function ED(A,e){var t=new lA.Record("Private DICT",[{name:"dict",type:"DICT",value:{}}]);return t.dict=gl(ol,A,e),t}function uD(A,e){for(var t=new lA.Table("CFF ",[{name:"header",type:"RECORD"},{name:"nameIndex",type:"RECORD"},{name:"topDictIndex",type:"RECORD"},{name:"stringIndex",type:"RECORD"},{name:"globalSubrIndex",type:"RECORD"},{name:"charsets",type:"RECORD"},{name:"charStringsIndex",type:"RECORD"},{name:"privateDict",type:"RECORD"}]),i=1/e.unitsPerEm,o={version:e.version,fullName:e.fullName,familyName:e.familyName,weight:e.weightName,fontBBox:e.fontBBox||[0,0,0,0],fontMatrix:[i,0,0,i,0,0],charset:999,encoding:0,charStrings:999,private:[0,999]},n={},r=[],g,s=1;s<A.length;s+=1)g=A.get(s),r.push(g.name);var a=[];t.header=aD(),t.nameIndex=ID([e.postScriptName]);var I=aa(o,a);t.topDictIndex=Ia(I),t.globalSubrIndex=lD(),t.charsets=BD(r,a),t.charStringsIndex=cD(A),t.privateDict=ED(n,a),t.stringIndex=CD(a);var l=t.header.sizeOf()+t.nameIndex.sizeOf()+t.topDictIndex.sizeOf()+t.stringIndex.sizeOf()+t.globalSubrIndex.sizeOf();return o.charset=l,o.encoding=0,o.charStrings=o.charset+t.charsets.sizeOf(),o.private[1]=o.charStrings+t.charStringsIndex.sizeOf(),I=aa(o,a),t.topDictIndex=Ia(I),t}var sl={parse:sD,make:uD};function fD(A,e){var t={},i=new QA.Parser(A,e);return t.version=i.parseVersion(),t.fontRevision=Math.round(i.parseFixed()*1e3)/1e3,t.checkSumAdjustment=i.parseULong(),t.magicNumber=i.parseULong(),mA.argument(t.magicNumber===1594834165,"Font header has wrong magic number."),t.flags=i.parseUShort(),t.unitsPerEm=i.parseUShort(),t.created=i.parseLongDateTime(),t.modified=i.parseLongDateTime(),t.xMin=i.parseShort(),t.yMin=i.parseShort(),t.xMax=i.parseShort(),t.yMax=i.parseShort(),t.macStyle=i.parseUShort(),t.lowestRecPPEM=i.parseUShort(),t.fontDirectionHint=i.parseShort(),t.indexToLocFormat=i.parseShort(),t.glyphDataFormat=i.parseShort(),t}function hD(A){var e=Math.round(new Date().getTime()/1e3)+2082844800,t=e;return A.createdTimestamp&&(t=A.createdTimestamp+2082844800),new lA.Table("head",[{name:"version",type:"FIXED",value:65536},{name:"fontRevision",type:"FIXED",value:65536},{name:"checkSumAdjustment",type:"ULONG",value:0},{name:"magicNumber",type:"ULONG",value:1594834165},{name:"flags",type:"USHORT",value:0},{name:"unitsPerEm",type:"USHORT",value:1e3},{name:"created",type:"LONGDATETIME",value:t},{name:"modified",type:"LONGDATETIME",value:e},{name:"xMin",type:"SHORT",value:0},{name:"yMin",type:"SHORT",value:0},{name:"xMax",type:"SHORT",value:0},{name:"yMax",type:"SHORT",value:0},{name:"macStyle",type:"USHORT",value:0},{name:"lowestRecPPEM",type:"USHORT",value:0},{name:"fontDirectionHint",type:"SHORT",value:2},{name:"indexToLocFormat",type:"SHORT",value:0},{name:"glyphDataFormat",type:"SHORT",value:0}],A)}var al={parse:fD,make:hD};function dD(A,e){var t={},i=new QA.Parser(A,e);return t.version=i.parseVersion(),t.ascender=i.parseShort(),t.descender=i.parseShort(),t.lineGap=i.parseShort(),t.advanceWidthMax=i.parseUShort(),t.minLeftSideBearing=i.parseShort(),t.minRightSideBearing=i.parseShort(),t.xMaxExtent=i.parseShort(),t.caretSlopeRise=i.parseShort(),t.caretSlopeRun=i.parseShort(),t.caretOffset=i.parseShort(),i.relativeOffset+=8,t.metricDataFormat=i.parseShort(),t.numberOfHMetrics=i.parseUShort(),t}function pD(A){return new lA.Table("hhea",[{name:"version",type:"FIXED",value:65536},{name:"ascender",type:"FWORD",value:0},{name:"descender",type:"FWORD",value:0},{name:"lineGap",type:"FWORD",value:0},{name:"advanceWidthMax",type:"UFWORD",value:0},{name:"minLeftSideBearing",type:"FWORD",value:0},{name:"minRightSideBearing",type:"FWORD",value:0},{name:"xMaxExtent",type:"FWORD",value:0},{name:"caretSlopeRise",type:"SHORT",value:1},{name:"caretSlopeRun",type:"SHORT",value:0},{name:"caretOffset",type:"SHORT",value:0},{name:"reserved1",type:"SHORT",value:0},{name:"reserved2",type:"SHORT",value:0},{name:"reserved3",type:"SHORT",value:0},{name:"reserved4",type:"SHORT",value:0},{name:"metricDataFormat",type:"SHORT",value:0},{name:"numberOfHMetrics",type:"USHORT",value:0}],A)}var Il={parse:dD,make:pD};function mD(A,e,t,i,o){for(var n,r,g=new QA.Parser(A,e),s=0;s<i;s+=1){s<t&&(n=g.parseUShort(),r=g.parseShort());var a=o.get(s);a.advanceWidth=n,a.leftSideBearing=r}}function yD(A,e,t,i,o){A._hmtxTableData={};for(var n,r,g=new QA.Parser(e,t),s=0;s<o;s+=1)s<i&&(n=g.parseUShort(),r=g.parseShort()),A._hmtxTableData[s]={advanceWidth:n,leftSideBearing:r}}function wD(A,e,t,i,o,n,r){r.lowMemory?yD(A,e,t,i,o):mD(e,t,i,o,n)}function DD(A){for(var e=new lA.Table("hmtx",[]),t=0;t<A.length;t+=1){var i=A.get(t),o=i.advanceWidth||0,n=i.leftSideBearing||0;e.fields.push({name:"advanceWidth_"+t,type:"USHORT",value:o}),e.fields.push({name:"leftSideBearing_"+t,type:"SHORT",value:n})}return e}var Cl={parse:wD,make:DD};function kD(A){for(var e=new lA.Table("ltag",[{name:"version",type:"ULONG",value:1},{name:"flags",type:"ULONG",value:0},{name:"numTags",type:"ULONG",value:A.length}]),t="",i=12+A.length*4,o=0;o<A.length;++o){var n=t.indexOf(A[o]);n<0&&(n=t.length,t+=A[o]),e.fields.push({name:"offset "+o,type:"USHORT",value:i+n}),e.fields.push({name:"length "+o,type:"USHORT",value:A[o].length})}return e.fields.push({name:"stringPool",type:"CHARARRAY",value:t}),e}function vD(A,e){var t=new QA.Parser(A,e),i=t.parseULong();mA.argument(i===1,"Unsupported ltag table version."),t.skip("uLong",1);for(var o=t.parseULong(),n=[],r=0;r<o;r++){for(var g="",s=e+t.parseUShort(),a=t.parseUShort(),I=s;I<s+a;++I)g+=String.fromCharCode(A.getInt8(I));n.push(g)}return n}var ll={make:kD,parse:vD};function FD(A,e){var t={},i=new QA.Parser(A,e);return t.version=i.parseVersion(),t.numGlyphs=i.parseUShort(),t.version===1&&(t.maxPoints=i.parseUShort(),t.maxContours=i.parseUShort(),t.maxCompositePoints=i.parseUShort(),t.maxCompositeContours=i.parseUShort(),t.maxZones=i.parseUShort(),t.maxTwilightPoints=i.parseUShort(),t.maxStorage=i.parseUShort(),t.maxFunctionDefs=i.parseUShort(),t.maxInstructionDefs=i.parseUShort(),t.maxStackElements=i.parseUShort(),t.maxSizeOfInstructions=i.parseUShort(),t.maxComponentElements=i.parseUShort(),t.maxComponentDepth=i.parseUShort()),t}function SD(A){return new lA.Table("maxp",[{name:"version",type:"FIXED",value:20480},{name:"numGlyphs",type:"USHORT",value:A}])}var Bl={parse:FD,make:SD},Ql=["copyright","fontFamily","fontSubfamily","uniqueID","fullName","version","postScriptName","trademark","manufacturer","designer","description","manufacturerURL","designerURL","license","licenseURL","reserved","preferredFamily","preferredSubfamily","compatibleFullName","sampleText","postScriptFindFontName","wwsFamily","wwsSubfamily"],cl={0:"en",1:"fr",2:"de",3:"it",4:"nl",5:"sv",6:"es",7:"da",8:"pt",9:"no",10:"he",11:"ja",12:"ar",13:"fi",14:"el",15:"is",16:"mt",17:"tr",18:"hr",19:"zh-Hant",20:"ur",21:"hi",22:"th",23:"ko",24:"lt",25:"pl",26:"hu",27:"es",28:"lv",29:"se",30:"fo",31:"fa",32:"ru",33:"zh",34:"nl-BE",35:"ga",36:"sq",37:"ro",38:"cz",39:"sk",40:"si",41:"yi",42:"sr",43:"mk",44:"bg",45:"uk",46:"be",47:"uz",48:"kk",49:"az-Cyrl",50:"az-Arab",51:"hy",52:"ka",53:"mo",54:"ky",55:"tg",56:"tk",57:"mn-CN",58:"mn",59:"ps",60:"ks",61:"ku",62:"sd",63:"bo",64:"ne",65:"sa",66:"mr",67:"bn",68:"as",69:"gu",70:"pa",71:"or",72:"ml",73:"kn",74:"ta",75:"te",76:"si",77:"my",78:"km",79:"lo",80:"vi",81:"id",82:"tl",83:"ms",84:"ms-Arab",85:"am",86:"ti",87:"om",88:"so",89:"sw",90:"rw",91:"rn",92:"ny",93:"mg",94:"eo",128:"cy",129:"eu",130:"ca",131:"la",132:"qu",133:"gn",134:"ay",135:"tt",136:"ug",137:"dz",138:"jv",139:"su",140:"gl",141:"af",142:"br",143:"iu",144:"gd",145:"gv",146:"ga",147:"to",148:"el-polyton",149:"kl",150:"az",151:"nn"},RD={0:0,1:0,2:0,3:0,4:0,5:0,6:0,7:0,8:0,9:0,10:5,11:1,12:4,13:0,14:6,15:0,16:0,17:0,18:0,19:2,20:4,21:9,22:21,23:3,24:29,25:29,26:29,27:29,28:29,29:0,30:0,31:4,32:7,33:25,34:0,35:0,36:0,37:0,38:29,39:29,40:0,41:5,42:7,43:7,44:7,45:7,46:7,47:7,48:7,49:7,50:4,51:24,52:23,53:7,54:7,55:7,56:7,57:27,58:7,59:4,60:4,61:4,62:4,63:26,64:9,65:9,66:9,67:13,68:13,69:11,70:10,71:12,72:17,73:16,74:14,75:15,76:18,77:19,78:20,79:22,80:30,81:0,82:0,83:0,84:4,85:28,86:28,87:28,88:0,89:0,90:0,91:0,92:0,93:0,94:0,128:0,129:0,130:0,131:0,132:0,133:0,134:0,135:7,136:4,137:26,138:0,139:0,140:0,141:0,142:0,143:28,144:0,145:0,146:0,147:0,148:6,149:0,150:0,151:0},El={1078:"af",1052:"sq",1156:"gsw",1118:"am",5121:"ar-DZ",15361:"ar-BH",3073:"ar",2049:"ar-IQ",11265:"ar-JO",13313:"ar-KW",12289:"ar-LB",4097:"ar-LY",6145:"ary",8193:"ar-OM",16385:"ar-QA",1025:"ar-SA",10241:"ar-SY",7169:"aeb",14337:"ar-AE",9217:"ar-YE",1067:"hy",1101:"as",2092:"az-Cyrl",1068:"az",1133:"ba",1069:"eu",1059:"be",2117:"bn",1093:"bn-IN",8218:"bs-Cyrl",5146:"bs",1150:"br",1026:"bg",1027:"ca",3076:"zh-HK",5124:"zh-MO",2052:"zh",4100:"zh-SG",1028:"zh-TW",1155:"co",1050:"hr",4122:"hr-BA",1029:"cs",1030:"da",1164:"prs",1125:"dv",2067:"nl-BE",1043:"nl",3081:"en-AU",10249:"en-BZ",4105:"en-CA",9225:"en-029",16393:"en-IN",6153:"en-IE",8201:"en-JM",17417:"en-MY",5129:"en-NZ",13321:"en-PH",18441:"en-SG",7177:"en-ZA",11273:"en-TT",2057:"en-GB",1033:"en",12297:"en-ZW",1061:"et",1080:"fo",1124:"fil",1035:"fi",2060:"fr-BE",3084:"fr-CA",1036:"fr",5132:"fr-LU",6156:"fr-MC",4108:"fr-CH",1122:"fy",1110:"gl",1079:"ka",3079:"de-AT",1031:"de",5127:"de-LI",4103:"de-LU",2055:"de-CH",1032:"el",1135:"kl",1095:"gu",1128:"ha",1037:"he",1081:"hi",1038:"hu",1039:"is",1136:"ig",1057:"id",1117:"iu",2141:"iu-Latn",2108:"ga",1076:"xh",1077:"zu",1040:"it",2064:"it-CH",1041:"ja",1099:"kn",1087:"kk",1107:"km",1158:"quc",1159:"rw",1089:"sw",1111:"kok",1042:"ko",1088:"ky",1108:"lo",1062:"lv",1063:"lt",2094:"dsb",1134:"lb",1071:"mk",2110:"ms-BN",1086:"ms",1100:"ml",1082:"mt",1153:"mi",1146:"arn",1102:"mr",1148:"moh",1104:"mn",2128:"mn-CN",1121:"ne",1044:"nb",2068:"nn",1154:"oc",1096:"or",1123:"ps",1045:"pl",1046:"pt",2070:"pt-PT",1094:"pa",1131:"qu-BO",2155:"qu-EC",3179:"qu",1048:"ro",1047:"rm",1049:"ru",9275:"smn",4155:"smj-NO",5179:"smj",3131:"se-FI",1083:"se",2107:"se-SE",8251:"sms",6203:"sma-NO",7227:"sms",1103:"sa",7194:"sr-Cyrl-BA",3098:"sr",6170:"sr-Latn-BA",2074:"sr-Latn",1132:"nso",1074:"tn",1115:"si",1051:"sk",1060:"sl",11274:"es-AR",16394:"es-BO",13322:"es-CL",9226:"es-CO",5130:"es-CR",7178:"es-DO",12298:"es-EC",17418:"es-SV",4106:"es-GT",18442:"es-HN",2058:"es-MX",19466:"es-NI",6154:"es-PA",15370:"es-PY",10250:"es-PE",20490:"es-PR",3082:"es",1034:"es",21514:"es-US",14346:"es-UY",8202:"es-VE",2077:"sv-FI",1053:"sv",1114:"syr",1064:"tg",2143:"tzm",1097:"ta",1092:"tt",1098:"te",1054:"th",1105:"bo",1055:"tr",1090:"tk",1152:"ug",1058:"uk",1070:"hsb",1056:"ur",2115:"uz-Cyrl",1091:"uz",1066:"vi",1106:"cy",1160:"wo",1157:"sah",1144:"ii",1130:"yo"};function bD(A,e,t){switch(A){case 0:if(e===65535)return"und";if(t)return t[e];break;case 1:return cl[e];case 3:return El[e]}}var xr="utf-16",ND={0:"macintosh",1:"x-mac-japanese",2:"x-mac-chinesetrad",3:"x-mac-korean",6:"x-mac-greek",7:"x-mac-cyrillic",9:"x-mac-devanagai",10:"x-mac-gurmukhi",11:"x-mac-gujarati",12:"x-mac-oriya",13:"x-mac-bengali",14:"x-mac-tamil",15:"x-mac-telugu",16:"x-mac-kannada",17:"x-mac-malayalam",18:"x-mac-sinhalese",19:"x-mac-burmese",20:"x-mac-khmer",21:"x-mac-thai",22:"x-mac-lao",23:"x-mac-georgian",24:"x-mac-armenian",25:"x-mac-chinesesimp",26:"x-mac-tibetan",27:"x-mac-mongolian",28:"x-mac-ethiopic",29:"x-mac-ce",30:"x-mac-vietnamese",31:"x-mac-extarabic"},GD={15:"x-mac-icelandic",17:"x-mac-turkish",18:"x-mac-croatian",24:"x-mac-ce",25:"x-mac-ce",26:"x-mac-ce",27:"x-mac-ce",28:"x-mac-ce",30:"x-mac-icelandic",37:"x-mac-romanian",38:"x-mac-ce",39:"x-mac-ce",40:"x-mac-ce",143:"x-mac-inuit",146:"x-mac-gaelic"};function ul(A,e,t){switch(A){case 0:return xr;case 1:return GD[t]||ND[e];case 3:if(e===1||e===10)return xr;break}}function LD(A,e,t){for(var i={},o=new QA.Parser(A,e),n=o.parseUShort(),r=o.parseUShort(),g=o.offset+o.parseUShort(),s=0;s<r;s++){var a=o.parseUShort(),I=o.parseUShort(),l=o.parseUShort(),C=o.parseUShort(),Q=Ql[C]||C,f=o.parseUShort(),h=o.parseUShort(),u=bD(a,l,t),E=ul(a,I,l);if(E!==void 0&&u!==void 0){var D=void 0;if(E===xr?D=Ri.UTF16(A,g+h,f):D=Ri.MACSTRING(A,g+h,f,E),D){var c=i[Q];c===void 0&&(c=i[Q]={}),c[u]=D}}}return n===1&&o.parseUShort(),i}function Zn(A){var e={};for(var t in A)e[A[t]]=parseInt(t);return e}function Ca(A,e,t,i,o,n){return new lA.Record("NameRecord",[{name:"platformID",type:"USHORT",value:A},{name:"encodingID",type:"USHORT",value:e},{name:"languageID",type:"USHORT",value:t},{name:"nameID",type:"USHORT",value:i},{name:"length",type:"USHORT",value:o},{name:"offset",type:"USHORT",value:n}])}function UD(A,e){var t=A.length,i=e.length-t+1;A:for(var o=0;o<i;o++)for(;o<i;o++){for(var n=0;n<t;n++)if(e[o+n]!==A[n])continue A;return o}return-1}function la(A,e){var t=UD(A,e);if(t<0){t=e.length;for(var i=0,o=A.length;i<o;++i)e.push(A[i])}return t}function MD(A,e){var t,i=[],o={},n=Zn(Ql);for(var r in A){var g=n[r];if(g===void 0&&(g=r),t=parseInt(g),isNaN(t))throw new Error('Name table entry "'+r+'" does not exist, see nameTableNames for complete list.');o[t]=A[r],i.push(t)}for(var s=Zn(cl),a=Zn(El),I=[],l=[],C=0;C<i.length;C++){t=i[C];var Q=o[t];for(var f in Q){var h=Q[f],u=1,E=s[f],D=RD[E],c=ul(u,D,E),p=sA.MACSTRING(h,c);p===void 0&&(u=0,E=e.indexOf(f),E<0&&(E=e.length,e.push(f)),D=4,p=sA.UTF16(h));var d=la(p,l);I.push(Ca(u,D,E,t,p.length,d));var v=a[f];if(v!==void 0){var b=sA.UTF16(h),N=la(b,l);I.push(Ca(3,1,v,t,b.length,N))}}}I.sort(function(U,R){return U.platformID-R.platformID||U.encodingID-R.encodingID||U.languageID-R.languageID||U.nameID-R.nameID});for(var H=new lA.Table("name",[{name:"format",type:"USHORT",value:0},{name:"count",type:"USHORT",value:I.length},{name:"stringOffset",type:"USHORT",value:6+I.length*12}]),M=0;M<I.length;M++)H.fields.push({name:"record_"+M,type:"RECORD",value:I[M]});return H.fields.push({name:"strings",type:"LITERAL",value:l}),H}var fl={parse:LD,make:MD},Jr=[{begin:0,end:127},{begin:128,end:255},{begin:256,end:383},{begin:384,end:591},{begin:592,end:687},{begin:688,end:767},{begin:768,end:879},{begin:880,end:1023},{begin:11392,end:11519},{begin:1024,end:1279},{begin:1328,end:1423},{begin:1424,end:1535},{begin:42240,end:42559},{begin:1536,end:1791},{begin:1984,end:2047},{begin:2304,end:2431},{begin:2432,end:2559},{begin:2560,end:2687},{begin:2688,end:2815},{begin:2816,end:2943},{begin:2944,end:3071},{begin:3072,end:3199},{begin:3200,end:3327},{begin:3328,end:3455},{begin:3584,end:3711},{begin:3712,end:3839},{begin:4256,end:4351},{begin:6912,end:7039},{begin:4352,end:4607},{begin:7680,end:7935},{begin:7936,end:8191},{begin:8192,end:8303},{begin:8304,end:8351},{begin:8352,end:8399},{begin:8400,end:8447},{begin:8448,end:8527},{begin:8528,end:8591},{begin:8592,end:8703},{begin:8704,end:8959},{begin:8960,end:9215},{begin:9216,end:9279},{begin:9280,end:9311},{begin:9312,end:9471},{begin:9472,end:9599},{begin:9600,end:9631},{begin:9632,end:9727},{begin:9728,end:9983},{begin:9984,end:10175},{begin:12288,end:12351},{begin:12352,end:12447},{begin:12448,end:12543},{begin:12544,end:12591},{begin:12592,end:12687},{begin:43072,end:43135},{begin:12800,end:13055},{begin:13056,end:13311},{begin:44032,end:55215},{begin:55296,end:57343},{begin:67840,end:67871},{begin:19968,end:40959},{begin:57344,end:63743},{begin:12736,end:12783},{begin:64256,end:64335},{begin:64336,end:65023},{begin:65056,end:65071},{begin:65040,end:65055},{begin:65104,end:65135},{begin:65136,end:65279},{begin:65280,end:65519},{begin:65520,end:65535},{begin:3840,end:4095},{begin:1792,end:1871},{begin:1920,end:1983},{begin:3456,end:3583},{begin:4096,end:4255},{begin:4608,end:4991},{begin:5024,end:5119},{begin:5120,end:5759},{begin:5760,end:5791},{begin:5792,end:5887},{begin:6016,end:6143},{begin:6144,end:6319},{begin:10240,end:10495},{begin:40960,end:42127},{begin:5888,end:5919},{begin:66304,end:66351},{begin:66352,end:66383},{begin:66560,end:66639},{begin:118784,end:119039},{begin:119808,end:120831},{begin:1044480,end:1048573},{begin:65024,end:65039},{begin:917504,end:917631},{begin:6400,end:6479},{begin:6480,end:6527},{begin:6528,end:6623},{begin:6656,end:6687},{begin:11264,end:11359},{begin:11568,end:11647},{begin:19904,end:19967},{begin:43008,end:43055},{begin:65536,end:65663},{begin:65856,end:65935},{begin:66432,end:66463},{begin:66464,end:66527},{begin:66640,end:66687},{begin:66688,end:66735},{begin:67584,end:67647},{begin:68096,end:68191},{begin:119552,end:119647},{begin:73728,end:74751},{begin:119648,end:119679},{begin:7040,end:7103},{begin:7168,end:7247},{begin:7248,end:7295},{begin:43136,end:43231},{begin:43264,end:43311},{begin:43312,end:43359},{begin:43520,end:43615},{begin:65936,end:65999},{begin:66e3,end:66047},{begin:66208,end:66271},{begin:127024,end:127135}];function xD(A){for(var e=0;e<Jr.length;e+=1){var t=Jr[e];if(A>=t.begin&&A<t.end)return e}return-1}function JD(A,e){var t={},i=new QA.Parser(A,e);t.version=i.parseUShort(),t.xAvgCharWidth=i.parseShort(),t.usWeightClass=i.parseUShort(),t.usWidthClass=i.parseUShort(),t.fsType=i.parseUShort(),t.ySubscriptXSize=i.parseShort(),t.ySubscriptYSize=i.parseShort(),t.ySubscriptXOffset=i.parseShort(),t.ySubscriptYOffset=i.parseShort(),t.ySuperscriptXSize=i.parseShort(),t.ySuperscriptYSize=i.parseShort(),t.ySuperscriptXOffset=i.parseShort(),t.ySuperscriptYOffset=i.parseShort(),t.yStrikeoutSize=i.parseShort(),t.yStrikeoutPosition=i.parseShort(),t.sFamilyClass=i.parseShort(),t.panose=[];for(var o=0;o<10;o++)t.panose[o]=i.parseByte();return t.ulUnicodeRange1=i.parseULong(),t.ulUnicodeRange2=i.parseULong(),t.ulUnicodeRange3=i.parseULong(),t.ulUnicodeRange4=i.parseULong(),t.achVendID=String.fromCharCode(i.parseByte(),i.parseByte(),i.parseByte(),i.parseByte()),t.fsSelection=i.parseUShort(),t.usFirstCharIndex=i.parseUShort(),t.usLastCharIndex=i.parseUShort(),t.sTypoAscender=i.parseShort(),t.sTypoDescender=i.parseShort(),t.sTypoLineGap=i.parseShort(),t.usWinAscent=i.parseUShort(),t.usWinDescent=i.parseUShort(),t.version>=1&&(t.ulCodePageRange1=i.parseULong(),t.ulCodePageRange2=i.parseULong()),t.version>=2&&(t.sxHeight=i.parseShort(),t.sCapHeight=i.parseShort(),t.usDefaultChar=i.parseUShort(),t.usBreakChar=i.parseUShort(),t.usMaxContent=i.parseUShort()),t}function TD(A){return new lA.Table("OS/2",[{name:"version",type:"USHORT",value:3},{name:"xAvgCharWidth",type:"SHORT",value:0},{name:"usWeightClass",type:"USHORT",value:0},{name:"usWidthClass",type:"USHORT",value:0},{name:"fsType",type:"USHORT",value:0},{name:"ySubscriptXSize",type:"SHORT",value:650},{name:"ySubscriptYSize",type:"SHORT",value:699},{name:"ySubscriptXOffset",type:"SHORT",value:0},{name:"ySubscriptYOffset",type:"SHORT",value:140},{name:"ySuperscriptXSize",type:"SHORT",value:650},{name:"ySuperscriptYSize",type:"SHORT",value:699},{name:"ySuperscriptXOffset",type:"SHORT",value:0},{name:"ySuperscriptYOffset",type:"SHORT",value:479},{name:"yStrikeoutSize",type:"SHORT",value:49},{name:"yStrikeoutPosition",type:"SHORT",value:258},{name:"sFamilyClass",type:"SHORT",value:0},{name:"bFamilyType",type:"BYTE",value:0},{name:"bSerifStyle",type:"BYTE",value:0},{name:"bWeight",type:"BYTE",value:0},{name:"bProportion",type:"BYTE",value:0},{name:"bContrast",type:"BYTE",value:0},{name:"bStrokeVariation",type:"BYTE",value:0},{name:"bArmStyle",type:"BYTE",value:0},{name:"bLetterform",type:"BYTE",value:0},{name:"bMidline",type:"BYTE",value:0},{name:"bXHeight",type:"BYTE",value:0},{name:"ulUnicodeRange1",type:"ULONG",value:0},{name:"ulUnicodeRange2",type:"ULONG",value:0},{name:"ulUnicodeRange3",type:"ULONG",value:0},{name:"ulUnicodeRange4",type:"ULONG",value:0},{name:"achVendID",type:"CHARARRAY",value:"XXXX"},{name:"fsSelection",type:"USHORT",value:0},{name:"usFirstCharIndex",type:"USHORT",value:0},{name:"usLastCharIndex",type:"USHORT",value:0},{name:"sTypoAscender",type:"SHORT",value:0},{name:"sTypoDescender",type:"SHORT",value:0},{name:"sTypoLineGap",type:"SHORT",value:0},{name:"usWinAscent",type:"USHORT",value:0},{name:"usWinDescent",type:"USHORT",value:0},{name:"ulCodePageRange1",type:"ULONG",value:0},{name:"ulCodePageRange2",type:"ULONG",value:0},{name:"sxHeight",type:"SHORT",value:0},{name:"sCapHeight",type:"SHORT",value:0},{name:"usDefaultChar",type:"USHORT",value:0},{name:"usBreakChar",type:"USHORT",value:0},{name:"usMaxContext",type:"USHORT",value:0}],A)}var Tr={parse:JD,make:TD,unicodeRanges:Jr,getUnicodeRange:xD};function HD(A,e){var t={},i=new QA.Parser(A,e);switch(t.version=i.parseVersion(),t.italicAngle=i.parseFixed(),t.underlinePosition=i.parseShort(),t.underlineThickness=i.parseShort(),t.isFixedPitch=i.parseULong(),t.minMemType42=i.parseULong(),t.maxMemType42=i.parseULong(),t.minMemType1=i.parseULong(),t.maxMemType1=i.parseULong(),t.version){case 1:t.names=ii.slice();break;case 2:t.numberOfGlyphs=i.parseUShort(),t.glyphNameIndex=new Array(t.numberOfGlyphs);for(var o=0;o<t.numberOfGlyphs;o++)t.glyphNameIndex[o]=i.parseUShort();t.names=[];for(var n=0;n<t.numberOfGlyphs;n++)if(t.glyphNameIndex[n]>=ii.length){var r=i.parseChar();t.names.push(i.parseString(r))}break;case 2.5:t.numberOfGlyphs=i.parseUShort(),t.offset=new Array(t.numberOfGlyphs);for(var g=0;g<t.numberOfGlyphs;g++)t.offset[g]=i.parseChar();break}return t}function YD(){return new lA.Table("post",[{name:"version",type:"FIXED",value:196608},{name:"italicAngle",type:"FIXED",value:0},{name:"underlinePosition",type:"FWORD",value:0},{name:"underlineThickness",type:"FWORD",value:0},{name:"isFixedPitch",type:"ULONG",value:0},{name:"minMemType42",type:"ULONG",value:0},{name:"maxMemType42",type:"ULONG",value:0},{name:"minMemType1",type:"ULONG",value:0},{name:"maxMemType1",type:"ULONG",value:0}])}var hl={parse:HD,make:YD},Pe=new Array(9);Pe[1]=function(){var e=this.offset+this.relativeOffset,t=this.parseUShort();if(t===1)return{substFormat:1,coverage:this.parsePointer(J.coverage),deltaGlyphId:this.parseUShort()};if(t===2)return{substFormat:2,coverage:this.parsePointer(J.coverage),substitute:this.parseOffset16List()};mA.assert(!1,"0x"+e.toString(16)+": lookup type 1 format must be 1 or 2.")};Pe[2]=function(){var e=this.parseUShort();return mA.argument(e===1,"GSUB Multiple Substitution Subtable identifier-format must be 1"),{substFormat:e,coverage:this.parsePointer(J.coverage),sequences:this.parseListOfLists()}};Pe[3]=function(){var e=this.parseUShort();return mA.argument(e===1,"GSUB Alternate Substitution Subtable identifier-format must be 1"),{substFormat:e,coverage:this.parsePointer(J.coverage),alternateSets:this.parseListOfLists()}};Pe[4]=function(){var e=this.parseUShort();return mA.argument(e===1,"GSUB ligature table identifier-format must be 1"),{substFormat:e,coverage:this.parsePointer(J.coverage),ligatureSets:this.parseListOfLists(function(){return{ligGlyph:this.parseUShort(),components:this.parseUShortList(this.parseUShort()-1)}})}};var wi={sequenceIndex:J.uShort,lookupListIndex:J.uShort};Pe[5]=function(){var e=this.offset+this.relativeOffset,t=this.parseUShort();if(t===1)return{substFormat:t,coverage:this.parsePointer(J.coverage),ruleSets:this.parseListOfLists(function(){var n=this.parseUShort(),r=this.parseUShort();return{input:this.parseUShortList(n-1),lookupRecords:this.parseRecordList(r,wi)}})};if(t===2)return{substFormat:t,coverage:this.parsePointer(J.coverage),classDef:this.parsePointer(J.classDef),classSets:this.parseListOfLists(function(){var n=this.parseUShort(),r=this.parseUShort();return{classes:this.parseUShortList(n-1),lookupRecords:this.parseRecordList(r,wi)}})};if(t===3){var i=this.parseUShort(),o=this.parseUShort();return{substFormat:t,coverages:this.parseList(i,J.pointer(J.coverage)),lookupRecords:this.parseRecordList(o,wi)}}mA.assert(!1,"0x"+e.toString(16)+": lookup type 5 format must be 1, 2 or 3.")};Pe[6]=function(){var e=this.offset+this.relativeOffset,t=this.parseUShort();if(t===1)return{substFormat:1,coverage:this.parsePointer(J.coverage),chainRuleSets:this.parseListOfLists(function(){return{backtrack:this.parseUShortList(),input:this.parseUShortList(this.parseShort()-1),lookahead:this.parseUShortList(),lookupRecords:this.parseRecordList(wi)}})};if(t===2)return{substFormat:2,coverage:this.parsePointer(J.coverage),backtrackClassDef:this.parsePointer(J.classDef),inputClassDef:this.parsePointer(J.classDef),lookaheadClassDef:this.parsePointer(J.classDef),chainClassSet:this.parseListOfLists(function(){return{backtrack:this.parseUShortList(),input:this.parseUShortList(this.parseShort()-1),lookahead:this.parseUShortList(),lookupRecords:this.parseRecordList(wi)}})};if(t===3)return{substFormat:3,backtrackCoverage:this.parseList(J.pointer(J.coverage)),inputCoverage:this.parseList(J.pointer(J.coverage)),lookaheadCoverage:this.parseList(J.pointer(J.coverage)),lookupRecords:this.parseRecordList(wi)};mA.assert(!1,"0x"+e.toString(16)+": lookup type 6 format must be 1, 2 or 3.")};Pe[7]=function(){var e=this.parseUShort();mA.argument(e===1,"GSUB Extension Substitution subtable identifier-format must be 1");var t=this.parseUShort(),i=new J(this.data,this.offset+this.parseULong());return{substFormat:1,lookupType:t,extension:Pe[t].call(i)}};Pe[8]=function(){var e=this.parseUShort();return mA.argument(e===1,"GSUB Reverse Chaining Contextual Single Substitution Subtable identifier-format must be 1"),{substFormat:e,coverage:this.parsePointer(J.coverage),backtrackCoverage:this.parseList(J.pointer(J.coverage)),lookaheadCoverage:this.parseList(J.pointer(J.coverage)),substitutes:this.parseUShortList()}};function _D(A,e){e=e||0;var t=new J(A,e),i=t.parseVersion(1);return mA.argument(i===1||i===1.1,"Unsupported GSUB table version."),i===1?{version:i,scripts:t.parseScriptList(),features:t.parseFeatureList(),lookups:t.parseLookupList(Pe)}:{version:i,scripts:t.parseScriptList(),features:t.parseFeatureList(),lookups:t.parseLookupList(Pe),variations:t.parseFeatureVariationsList()}}var Li=new Array(9);Li[1]=function(e){return e.substFormat===1?new lA.Table("substitutionTable",[{name:"substFormat",type:"USHORT",value:1},{name:"coverage",type:"TABLE",value:new lA.Coverage(e.coverage)},{name:"deltaGlyphID",type:"USHORT",value:e.deltaGlyphId}]):new lA.Table("substitutionTable",[{name:"substFormat",type:"USHORT",value:2},{name:"coverage",type:"TABLE",value:new lA.Coverage(e.coverage)}].concat(lA.ushortList("substitute",e.substitute)))};Li[2]=function(e){return mA.assert(e.substFormat===1,"Lookup type 2 substFormat must be 1."),new lA.Table("substitutionTable",[{name:"substFormat",type:"USHORT",value:1},{name:"coverage",type:"TABLE",value:new lA.Coverage(e.coverage)}].concat(lA.tableList("seqSet",e.sequences,function(t){return new lA.Table("sequenceSetTable",lA.ushortList("sequence",t))})))};Li[3]=function(e){return mA.assert(e.substFormat===1,"Lookup type 3 substFormat must be 1."),new lA.Table("substitutionTable",[{name:"substFormat",type:"USHORT",value:1},{name:"coverage",type:"TABLE",value:new lA.Coverage(e.coverage)}].concat(lA.tableList("altSet",e.alternateSets,function(t){return new lA.Table("alternateSetTable",lA.ushortList("alternate",t))})))};Li[4]=function(e){return mA.assert(e.substFormat===1,"Lookup type 4 substFormat must be 1."),new lA.Table("substitutionTable",[{name:"substFormat",type:"USHORT",value:1},{name:"coverage",type:"TABLE",value:new lA.Coverage(e.coverage)}].concat(lA.tableList("ligSet",e.ligatureSets,function(t){return new lA.Table("ligatureSetTable",lA.tableList("ligature",t,function(i){return new lA.Table("ligatureTable",[{name:"ligGlyph",type:"USHORT",value:i.ligGlyph}].concat(lA.ushortList("component",i.components,i.components.length+1)))}))})))};Li[6]=function(e){if(e.substFormat===1){var t=new lA.Table("chainContextTable",[{name:"substFormat",type:"USHORT",value:e.substFormat},{name:"coverage",type:"TABLE",value:new lA.Coverage(e.coverage)}].concat(lA.tableList("chainRuleSet",e.chainRuleSets,function(n){return new lA.Table("chainRuleSetTable",lA.tableList("chainRule",n,function(r){var g=lA.ushortList("backtrackGlyph",r.backtrack,r.backtrack.length).concat(lA.ushortList("inputGlyph",r.input,r.input.length+1)).concat(lA.ushortList("lookaheadGlyph",r.lookahead,r.lookahead.length)).concat(lA.ushortList("substitution",[],r.lookupRecords.length));return r.lookupRecords.forEach(function(s,a){g=g.concat({name:"sequenceIndex"+a,type:"USHORT",value:s.sequenceIndex}).concat({name:"lookupListIndex"+a,type:"USHORT",value:s.lookupListIndex})}),new lA.Table("chainRuleTable",g)}))})));return t}else if(e.substFormat===2)mA.assert(!1,"lookup type 6 format 2 is not yet supported.");else if(e.substFormat===3){var i=[{name:"substFormat",type:"USHORT",value:e.substFormat}];i.push({name:"backtrackGlyphCount",type:"USHORT",value:e.backtrackCoverage.length}),e.backtrackCoverage.forEach(function(n,r){i.push({name:"backtrackCoverage"+r,type:"TABLE",value:new lA.Coverage(n)})}),i.push({name:"inputGlyphCount",type:"USHORT",value:e.inputCoverage.length}),e.inputCoverage.forEach(function(n,r){i.push({name:"inputCoverage"+r,type:"TABLE",value:new lA.Coverage(n)})}),i.push({name:"lookaheadGlyphCount",type:"USHORT",value:e.lookaheadCoverage.length}),e.lookaheadCoverage.forEach(function(n,r){i.push({name:"lookaheadCoverage"+r,type:"TABLE",value:new lA.Coverage(n)})}),i.push({name:"substitutionCount",type:"USHORT",value:e.lookupRecords.length}),e.lookupRecords.forEach(function(n,r){i=i.concat({name:"sequenceIndex"+r,type:"USHORT",value:n.sequenceIndex}).concat({name:"lookupListIndex"+r,type:"USHORT",value:n.lookupListIndex})});var o=new lA.Table("chainContextTable",i);return o}mA.assert(!1,"lookup type 6 format must be 1, 2 or 3.")};function KD(A){return new lA.Table("GSUB",[{name:"version",type:"ULONG",value:65536},{name:"scripts",type:"TABLE",value:new lA.ScriptList(A.scripts)},{name:"features",type:"TABLE",value:new lA.FeatureList(A.features)},{name:"lookups",type:"TABLE",value:new lA.LookupList(A.lookups,Li)}])}var dl={parse:_D,make:KD};function OD(A,e){var t=new QA.Parser(A,e),i=t.parseULong();mA.argument(i===1,"Unsupported META table version."),t.parseULong(),t.parseULong();for(var o=t.parseULong(),n={},r=0;r<o;r++){var g=t.parseTag(),s=t.parseULong(),a=t.parseULong(),I=Ri.UTF8(A,e+s,a);n[g]=I}return n}function PD(A){var e=Object.keys(A).length,t="",i=16+e*12,o=new lA.Table("meta",[{name:"version",type:"ULONG",value:1},{name:"flags",type:"ULONG",value:0},{name:"offset",type:"ULONG",value:i},{name:"numTags",type:"ULONG",value:e}]);for(var n in A){var r=t.length;t+=A[n],o.fields.push({name:"tag "+n,type:"TAG",value:n}),o.fields.push({name:"offset "+n,type:"ULONG",value:i+r}),o.fields.push({name:"length "+n,type:"ULONG",value:A[n].length})}return o.fields.push({name:"stringPool",type:"CHARARRAY",value:t}),o}var pl={parse:OD,make:PD};function Ba(A){return Math.log(A)/Math.log(2)|0}function mg(A){for(;A.length%4!==0;)A.push(0);for(var e=0,t=0;t<A.length;t+=4)e+=(A[t]<<24)+(A[t+1]<<16)+(A[t+2]<<8)+A[t+3];return e%=Math.pow(2,32),e}function Qa(A,e,t,i){return new lA.Record("Table Record",[{name:"tag",type:"TAG",value:A!==void 0?A:""},{name:"checkSum",type:"ULONG",value:e!==void 0?e:0},{name:"offset",type:"ULONG",value:t!==void 0?t:0},{name:"length",type:"ULONG",value:i!==void 0?i:0}])}function ml(A){var e=new lA.Table("sfnt",[{name:"version",type:"TAG",value:"OTTO"},{name:"numTables",type:"USHORT",value:0},{name:"searchRange",type:"USHORT",value:0},{name:"entrySelector",type:"USHORT",value:0},{name:"rangeShift",type:"USHORT",value:0}]);e.tables=A,e.numTables=A.length;var t=Math.pow(2,Ba(e.numTables));e.searchRange=16*t,e.entrySelector=Ba(t),e.rangeShift=e.numTables*16-e.searchRange;for(var i=[],o=[],n=e.sizeOf()+Qa().sizeOf()*e.numTables;n%4!==0;)n+=1,o.push({name:"padding",type:"BYTE",value:0});for(var r=0;r<A.length;r+=1){var g=A[r];mA.argument(g.tableName.length===4,"Table name"+g.tableName+" is invalid.");var s=g.sizeOf(),a=Qa(g.tableName,mg(g.encode()),n,s);for(i.push({name:a.tag+" Table Record",type:"RECORD",value:a}),o.push({name:g.tableName+" table",type:"RECORD",value:g}),n+=s,mA.argument(!isNaN(n),"Something went wrong calculating the offset.");n%4!==0;)n+=1,o.push({name:"padding",type:"BYTE",value:0})}return i.sort(function(I,l){return I.value.tag>l.value.tag?1:-1}),e.fields=e.fields.concat(i),e.fields=e.fields.concat(o),e}function ca(A,e,t){for(var i=0;i<e.length;i+=1){var o=A.charToGlyphIndex(e[i]);if(o>0){var n=A.glyphs.get(o);return n.getMetrics()}}return t}function VD(A){for(var e=0,t=0;t<A.length;t+=1)e+=A[t];return e/A.length}function WD(A){for(var e=[],t=[],i=[],o=[],n=[],r=[],g=[],s,a=0,I=0,l=0,C=0,Q=0,f=0;f<A.glyphs.length;f+=1){var h=A.glyphs.get(f),u=h.unicode|0;if(isNaN(h.advanceWidth))throw new Error("Glyph "+h.name+" ("+f+"): advanceWidth is not a number.");(s>u||s===void 0)&&u>0&&(s=u),a<u&&(a=u);var E=Tr.getUnicodeRange(u);if(E<32)I|=1<<E;else if(E<64)l|=1<<E-32;else if(E<96)C|=1<<E-64;else if(E<123)Q|=1<<E-96;else throw new Error("Unicode ranges bits > 123 are reserved for internal usage");if(h.name!==".notdef"){var D=h.getMetrics();e.push(D.xMin),t.push(D.yMin),i.push(D.xMax),o.push(D.yMax),r.push(D.leftSideBearing),g.push(D.rightSideBearing),n.push(h.advanceWidth)}}var c={xMin:Math.min.apply(null,e),yMin:Math.min.apply(null,t),xMax:Math.max.apply(null,i),yMax:Math.max.apply(null,o),advanceWidthMax:Math.max.apply(null,n),advanceWidthAvg:VD(n),minLeftSideBearing:Math.min.apply(null,r),maxLeftSideBearing:Math.max.apply(null,r),minRightSideBearing:Math.min.apply(null,g)};c.ascender=A.ascender,c.descender=A.descender;var p=al.make({flags:3,unitsPerEm:A.unitsPerEm,xMin:c.xMin,yMin:c.yMin,xMax:c.xMax,yMax:c.yMax,lowestRecPPEM:3,createdTimestamp:A.createdTimestamp}),d=Il.make({ascender:c.ascender,descender:c.descender,advanceWidthMax:c.advanceWidthMax,minLeftSideBearing:c.minLeftSideBearing,minRightSideBearing:c.minRightSideBearing,xMaxExtent:c.maxLeftSideBearing+(c.xMax-c.xMin),numberOfHMetrics:A.glyphs.length}),v=Bl.make(A.glyphs.length),b=Tr.make(Object.assign({xAvgCharWidth:Math.round(c.advanceWidthAvg),usFirstCharIndex:s,usLastCharIndex:a,ulUnicodeRange1:I,ulUnicodeRange2:l,ulUnicodeRange3:C,ulUnicodeRange4:Q,sTypoAscender:c.ascender,sTypoDescender:c.descender,sTypoLineGap:0,usWinAscent:c.yMax,usWinDescent:Math.abs(c.yMin),ulCodePageRange1:1,sxHeight:ca(A,"xyvw",{yMax:Math.round(c.ascender/2)}).yMax,sCapHeight:ca(A,"HIKLEFJMNTZBDPRAGOQSUVWXY",c).yMax,usDefaultChar:A.hasChar(" ")?32:0,usBreakChar:A.hasChar(" ")?32:0},A.tables.os2)),N=Cl.make(A.glyphs),H=XC.make(A.glyphs),M=A.getEnglishName("fontFamily"),U=A.getEnglishName("fontSubfamily"),R=M+" "+U,j=A.getEnglishName("postScriptName");j||(j=M.replace(/\s/g,"")+"-"+U);var rA={};for(var IA in A.names)rA[IA]=A.names[IA];rA.uniqueID||(rA.uniqueID={en:A.getEnglishName("manufacturer")+":"+R}),rA.postScriptName||(rA.postScriptName={en:j}),rA.preferredFamily||(rA.preferredFamily=A.names.fontFamily),rA.preferredSubfamily||(rA.preferredSubfamily=A.names.fontSubfamily);var AA=[],z=fl.make(rA,AA),oA=AA.length>0?ll.make(AA):void 0,dA=hl.make(),Y=sl.make(A.glyphs,{version:A.getEnglishName("version"),fullName:R,familyName:M,weightName:U,postScriptName:j,unitsPerEm:A.unitsPerEm,fontBBox:[0,c.yMin,c.ascender,c.advanceWidthMax]}),W=A.metas&&Object.keys(A.metas).length>0?pl.make(A.metas):void 0,$=[p,d,v,b,z,H,dA,Y,N];oA&&$.push(oA),A.tables.gsub&&$.push(dl.make(A.tables.gsub)),W&&$.push(W);for(var DA=ml($),NA=DA.encode(),oe=mg(NA),LA=DA.fields,_=!1,eA=0;eA<LA.length;eA+=1)if(LA[eA].name==="head table"){LA[eA].value.checkSumAdjustment=2981146554-oe,_=!0;break}if(!_)throw new Error("Could not find head table with checkSum to adjust.");return DA}var qD={make:ml,fontToTable:WD,computeCheckSum:mg};function Xn(A,e){for(var t=0,i=A.length-1;t<=i;){var o=t+i>>>1,n=A[o].tag;if(n===e)return o;n<e?t=o+1:i=o-1}return-t-1}function Ea(A,e){for(var t=0,i=A.length-1;t<=i;){var o=t+i>>>1,n=A[o];if(n===e)return o;n<e?t=o+1:i=o-1}return-t-1}function ua(A,e){for(var t,i=0,o=A.length-1;i<=o;){var n=i+o>>>1;t=A[n];var r=t.start;if(r===e)return t;r<e?i=n+1:o=n-1}if(i>0)return t=A[i-1],e>t.end?0:t}function mo(A,e){this.font=A,this.tableName=e}mo.prototype={searchTag:Xn,binSearch:Ea,getTable:function(A){var e=this.font.tables[this.tableName];return!e&&A&&(e=this.font.tables[this.tableName]=this.createDefaultTable()),e},getScriptNames:function(){var A=this.getTable();return A?A.scripts.map(function(e){return e.tag}):[]},getDefaultScriptName:function(){var A=this.getTable();if(A){for(var e=!1,t=0;t<A.scripts.length;t++){var i=A.scripts[t].tag;if(i==="DFLT")return i;i==="latn"&&(e=!0)}if(e)return"latn"}},getScriptTable:function(A,e){var t=this.getTable(e);if(t){A=A||"DFLT";var i=t.scripts,o=Xn(t.scripts,A);if(o>=0)return i[o].script;if(e){var n={tag:A,script:{defaultLangSys:{reserved:0,reqFeatureIndex:65535,featureIndexes:[]},langSysRecords:[]}};return i.splice(-1-o,0,n),n.script}}},getLangSysTable:function(A,e,t){var i=this.getScriptTable(A,t);if(i){if(!e||e==="dflt"||e==="DFLT")return i.defaultLangSys;var o=Xn(i.langSysRecords,e);if(o>=0)return i.langSysRecords[o].langSys;if(t){var n={tag:e,langSys:{reserved:0,reqFeatureIndex:65535,featureIndexes:[]}};return i.langSysRecords.splice(-1-o,0,n),n.langSys}}},getFeatureTable:function(A,e,t,i){var o=this.getLangSysTable(A,e,i);if(o){for(var n,r=o.featureIndexes,g=this.font.tables[this.tableName].features,s=0;s<r.length;s++)if(n=g[r[s]],n.tag===t)return n.feature;if(i){var a=g.length;return mA.assert(a===0||t>=g[a-1].tag,"Features must be added in alphabetical order."),n={tag:t,feature:{params:0,lookupListIndexes:[]}},g.push(n),r.push(a),n.feature}}},getLookupTables:function(A,e,t,i,o){var n=this.getFeatureTable(A,e,t,o),r=[];if(n){for(var g,s=n.lookupListIndexes,a=this.font.tables[this.tableName].lookups,I=0;I<s.length;I++)g=a[s[I]],g.lookupType===i&&r.push(g);if(r.length===0&&o){g={lookupType:i,lookupFlag:0,subtables:[],markFilteringSet:void 0};var l=a.length;return a.push(g),s.push(l),[g]}}return r},getGlyphClass:function(A,e){switch(A.format){case 1:return A.startGlyph<=e&&e<A.startGlyph+A.classes.length?A.classes[e-A.startGlyph]:0;case 2:var t=ua(A.ranges,e);return t?t.classId:0}},getCoverageIndex:function(A,e){switch(A.format){case 1:var t=Ea(A.glyphs,e);return t>=0?t:-1;case 2:var i=ua(A.ranges,e);return i?i.index+e-i.start:-1}},expandCoverage:function(A){if(A.format===1)return A.glyphs;for(var e=[],t=A.ranges,i=0;i<t.length;i++)for(var o=t[i],n=o.start,r=o.end,g=n;g<=r;g++)e.push(g);return e}};function yo(A){mo.call(this,A,"gpos")}yo.prototype=mo.prototype;yo.prototype.init=function(){var A=this.getDefaultScriptName();this.defaultKerningTables=this.getKerningTables(A)};yo.prototype.getKerningValue=function(A,e,t){for(var i=0;i<A.length;i++)for(var o=A[i].subtables,n=0;n<o.length;n++){var r=o[n],g=this.getCoverageIndex(r.coverage,e);if(!(g<0))switch(r.posFormat){case 1:for(var s=r.pairSets[g],a=0;a<s.length;a++){var I=s[a];if(I.secondGlyph===t)return I.value1&&I.value1.xAdvance||0}break;case 2:var l=this.getGlyphClass(r.classDef1,e),C=this.getGlyphClass(r.classDef2,t),Q=r.classRecords[l][C];return Q.value1&&Q.value1.xAdvance||0}}return 0};yo.prototype.getKerningTables=function(A,e){if(this.font.tables.gpos)return this.getLookupTables(A,e,"kern",2)};function Ge(A){mo.call(this,A,"gsub")}function jD(A,e){var t=A.length;if(t!==e.length)return!1;for(var i=0;i<t;i++)if(A[i]!==e[i])return!1;return!0}function yg(A,e,t){for(var i=A.subtables,o=0;o<i.length;o++){var n=i[o];if(n.substFormat===e)return n}if(t)return i.push(t),t}Ge.prototype=mo.prototype;Ge.prototype.createDefaultTable=function(){return{version:1,scripts:[{tag:"DFLT",script:{defaultLangSys:{reserved:0,reqFeatureIndex:65535,featureIndexes:[]},langSysRecords:[]}}],features:[],lookups:[]}};Ge.prototype.getSingle=function(A,e,t){for(var i=[],o=this.getLookupTables(e,t,A,1),n=0;n<o.length;n++)for(var r=o[n].subtables,g=0;g<r.length;g++){var s=r[g],a=this.expandCoverage(s.coverage),I=void 0;if(s.substFormat===1){var l=s.deltaGlyphId;for(I=0;I<a.length;I++){var C=a[I];i.push({sub:C,by:C+l})}}else{var Q=s.substitute;for(I=0;I<a.length;I++)i.push({sub:a[I],by:Q[I]})}}return i};Ge.prototype.getMultiple=function(A,e,t){for(var i=[],o=this.getLookupTables(e,t,A,2),n=0;n<o.length;n++)for(var r=o[n].subtables,g=0;g<r.length;g++){var s=r[g],a=this.expandCoverage(s.coverage),I=void 0;for(I=0;I<a.length;I++){var l=a[I],C=s.sequences[I];i.push({sub:l,by:C})}}return i};Ge.prototype.getAlternates=function(A,e,t){for(var i=[],o=this.getLookupTables(e,t,A,3),n=0;n<o.length;n++)for(var r=o[n].subtables,g=0;g<r.length;g++)for(var s=r[g],a=this.expandCoverage(s.coverage),I=s.alternateSets,l=0;l<a.length;l++)i.push({sub:a[l],by:I[l]});return i};Ge.prototype.getLigatures=function(A,e,t){for(var i=[],o=this.getLookupTables(e,t,A,4),n=0;n<o.length;n++)for(var r=o[n].subtables,g=0;g<r.length;g++)for(var s=r[g],a=this.expandCoverage(s.coverage),I=s.ligatureSets,l=0;l<a.length;l++)for(var C=a[l],Q=I[l],f=0;f<Q.length;f++){var h=Q[f];i.push({sub:[C].concat(h.components),by:h.ligGlyph})}return i};Ge.prototype.addSingle=function(A,e,t,i){var o=this.getLookupTables(t,i,A,1,!0)[0],n=yg(o,2,{substFormat:2,coverage:{format:1,glyphs:[]},substitute:[]});mA.assert(n.coverage.format===1,"Single: unable to modify coverage table format "+n.coverage.format);var r=e.sub,g=this.binSearch(n.coverage.glyphs,r);g<0&&(g=-1-g,n.coverage.glyphs.splice(g,0,r),n.substitute.splice(g,0,0)),n.substitute[g]=e.by};Ge.prototype.addMultiple=function(A,e,t,i){mA.assert(e.by instanceof Array&&e.by.length>1,'Multiple: "by" must be an array of two or more ids');var o=this.getLookupTables(t,i,A,2,!0)[0],n=yg(o,1,{substFormat:1,coverage:{format:1,glyphs:[]},sequences:[]});mA.assert(n.coverage.format===1,"Multiple: unable to modify coverage table format "+n.coverage.format);var r=e.sub,g=this.binSearch(n.coverage.glyphs,r);g<0&&(g=-1-g,n.coverage.glyphs.splice(g,0,r),n.sequences.splice(g,0,0)),n.sequences[g]=e.by};Ge.prototype.addAlternate=function(A,e,t,i){var o=this.getLookupTables(t,i,A,3,!0)[0],n=yg(o,1,{substFormat:1,coverage:{format:1,glyphs:[]},alternateSets:[]});mA.assert(n.coverage.format===1,"Alternate: unable to modify coverage table format "+n.coverage.format);var r=e.sub,g=this.binSearch(n.coverage.glyphs,r);g<0&&(g=-1-g,n.coverage.glyphs.splice(g,0,r),n.alternateSets.splice(g,0,0)),n.alternateSets[g]=e.by};Ge.prototype.addLigature=function(A,e,t,i){var o=this.getLookupTables(t,i,A,4,!0)[0],n=o.subtables[0];n||(n={substFormat:1,coverage:{format:1,glyphs:[]},ligatureSets:[]},o.subtables[0]=n),mA.assert(n.coverage.format===1,"Ligature: unable to modify coverage table format "+n.coverage.format);var r=e.sub[0],g=e.sub.slice(1),s={ligGlyph:e.by,components:g},a=this.binSearch(n.coverage.glyphs,r);if(a>=0){for(var I=n.ligatureSets[a],l=0;l<I.length;l++)if(jD(I[l].components,g))return;I.push(s)}else a=-1-a,n.coverage.glyphs.splice(a,0,r),n.ligatureSets.splice(a,0,[s])};Ge.prototype.getFeature=function(A,e,t){if(/ss\d\d/.test(A))return this.getSingle(A,e,t);switch(A){case"aalt":case"salt":return this.getSingle(A,e,t).concat(this.getAlternates(A,e,t));case"dlig":case"liga":case"rlig":return this.getLigatures(A,e,t);case"ccmp":return this.getMultiple(A,e,t).concat(this.getLigatures(A,e,t));case"stch":return this.getMultiple(A,e,t)}};Ge.prototype.add=function(A,e,t,i){if(/ss\d\d/.test(A))return this.addSingle(A,e,t,i);switch(A){case"aalt":case"salt":return typeof e.by=="number"?this.addSingle(A,e,t,i):this.addAlternate(A,e,t,i);case"dlig":case"liga":case"rlig":return this.addLigature(A,e,t,i);case"ccmp":return e.by instanceof Array?this.addMultiple(A,e,t,i):this.addLigature(A,e,t,i)}};function ZD(){return typeof window<"u"}function yl(A){for(var e=new ArrayBuffer(A.length),t=new Uint8Array(e),i=0;i<A.length;++i)t[i]=A[i];return e}function XD(A){for(var e=new Buffer(A.byteLength),t=new Uint8Array(A),i=0;i<e.length;++i)e[i]=t[i];return e}function Ti(A,e){if(!A)throw e}function fa(A,e,t,i,o){var n;return(e&i)>0?(n=A.parseByte(),e&o||(n=-n),n=t+n):(e&o)>0?n=t:n=t+A.parseShort(),n}function wl(A,e,t){var i=new QA.Parser(e,t);A.numberOfContours=i.parseShort(),A._xMin=i.parseShort(),A._yMin=i.parseShort(),A._xMax=i.parseShort(),A._yMax=i.parseShort();var o,n;if(A.numberOfContours>0){for(var r=A.endPointIndices=[],g=0;g<A.numberOfContours;g+=1)r.push(i.parseUShort());A.instructionLength=i.parseUShort(),A.instructions=[];for(var s=0;s<A.instructionLength;s+=1)A.instructions.push(i.parseByte());var a=r[r.length-1]+1;o=[];for(var I=0;I<a;I+=1)if(n=i.parseByte(),o.push(n),(n&8)>0)for(var l=i.parseByte(),C=0;C<l;C+=1)o.push(n),I+=1;if(mA.argument(o.length===a,"Bad flags."),r.length>0){var Q=[],f;if(a>0){for(var h=0;h<a;h+=1)n=o[h],f={},f.onCurve=!!(n&1),f.lastPointOfContour=r.indexOf(h)>=0,Q.push(f);for(var u=0,E=0;E<a;E+=1)n=o[E],f=Q[E],f.x=fa(i,n,u,2,16),u=f.x;for(var D=0,c=0;c<a;c+=1)n=o[c],f=Q[c],f.y=fa(i,n,D,4,32),D=f.y}A.points=Q}else A.points=[]}else if(A.numberOfContours===0)A.points=[];else{A.isComposite=!0,A.points=[],A.components=[];for(var p=!0;p;){o=i.parseUShort();var d={glyphIndex:i.parseUShort(),xScale:1,scale01:0,scale10:0,yScale:1,dx:0,dy:0};(o&1)>0?(o&2)>0?(d.dx=i.parseShort(),d.dy=i.parseShort()):d.matchedPoints=[i.parseUShort(),i.parseUShort()]:(o&2)>0?(d.dx=i.parseChar(),d.dy=i.parseChar()):d.matchedPoints=[i.parseByte(),i.parseByte()],(o&8)>0?d.xScale=d.yScale=i.parseF2Dot14():(o&64)>0?(d.xScale=i.parseF2Dot14(),d.yScale=i.parseF2Dot14()):(o&128)>0&&(d.xScale=i.parseF2Dot14(),d.scale01=i.parseF2Dot14(),d.scale10=i.parseF2Dot14(),d.yScale=i.parseF2Dot14()),A.components.push(d),p=!!(o&32)}if(o&256){A.instructionLength=i.parseUShort(),A.instructions=[];for(var v=0;v<A.instructionLength;v+=1)A.instructions.push(i.parseByte())}}}function zn(A,e){for(var t=[],i=0;i<A.length;i+=1){var o=A[i],n={x:e.xScale*o.x+e.scale01*o.y+e.dx,y:e.scale10*o.x+e.yScale*o.y+e.dy,onCurve:o.onCurve,lastPointOfContour:o.lastPointOfContour};t.push(n)}return t}function zD(A){for(var e=[],t=[],i=0;i<A.length;i+=1){var o=A[i];t.push(o),o.lastPointOfContour&&(e.push(t),t=[])}return mA.argument(t.length===0,"There are still points left in the current contour."),e}function Dl(A){var e=new ie;if(!A)return e;for(var t=zD(A),i=0;i<t.length;++i){var o=t[i],n=null,r=o[o.length-1],g=o[0];if(r.onCurve)e.moveTo(r.x,r.y);else if(g.onCurve)e.moveTo(g.x,g.y);else{var s={x:(r.x+g.x)*.5,y:(r.y+g.y)*.5};e.moveTo(s.x,s.y)}for(var a=0;a<o.length;++a)if(n=r,r=g,g=o[(a+1)%o.length],r.onCurve)e.lineTo(r.x,r.y);else{var I=g;n.onCurve||((r.x+n.x)*.5,(r.y+n.y)*.5),g.onCurve||(I={x:(r.x+g.x)*.5,y:(r.y+g.y)*.5}),e.quadraticCurveTo(r.x,r.y,I.x,I.y)}e.closePath()}return e}function kl(A,e){if(e.isComposite)for(var t=0;t<e.components.length;t+=1){var i=e.components[t],o=A.get(i.glyphIndex);if(o.getPath(),o.points){var n=void 0;if(i.matchedPoints===void 0)n=zn(o.points,i);else{if(i.matchedPoints[0]>e.points.length-1||i.matchedPoints[1]>o.points.length-1)throw Error("Matched points out of range in "+e.name);var r=e.points[i.matchedPoints[0]],g=o.points[i.matchedPoints[1]],s={xScale:i.xScale,scale01:i.scale01,scale10:i.scale10,yScale:i.yScale,dx:0,dy:0};g=zn([g],s)[0],s.dx=r.x-g.x,s.dy=r.y-g.y,n=zn(o.points,s)}e.points=e.points.concat(n)}}return Dl(e.points)}function $D(A,e,t,i){for(var o=new at.GlyphSet(i),n=0;n<t.length-1;n+=1){var r=t[n],g=t[n+1];r!==g?o.push(n,at.ttfGlyphLoader(i,n,wl,A,e+r,kl)):o.push(n,at.glyphLoader(i,n))}return o}function Ak(A,e,t,i){var o=new at.GlyphSet(i);return i._push=function(n){var r=t[n],g=t[n+1];r!==g?o.push(n,at.ttfGlyphLoader(i,n,wl,A,e+r,kl)):o.push(n,at.glyphLoader(i,n))},o}function ek(A,e,t,i,o){return o.lowMemory?Ak(A,e,t,i):$D(A,e,t,i)}var vl={getPath:Dl,parse:ek},Fl,ai,Sl,Hr;function Rl(A){this.font=A,this.getCommands=function(e){return vl.getPath(e).commands},this._fpgmState=this._prepState=void 0,this._errorState=0}function tk(A){return A}function bl(A){return Math.sign(A)*Math.round(Math.abs(A))}function ik(A){return Math.sign(A)*Math.round(Math.abs(A*2))/2}function ok(A){return Math.sign(A)*(Math.round(Math.abs(A)+.5)-.5)}function nk(A){return Math.sign(A)*Math.ceil(Math.abs(A))}function rk(A){return Math.sign(A)*Math.floor(Math.abs(A))}var Nl=function(A){var e=this.srPeriod,t=this.srPhase,i=this.srThreshold,o=1;return A<0&&(A=-A,o=-1),A+=i-t,A=Math.trunc(A/e)*e,A+=t,A<0?t*o:A*o},ot={x:1,y:0,axis:"x",distance:function(A,e,t,i){return(t?A.xo:A.x)-(i?e.xo:e.x)},interpolate:function(A,e,t,i){var o,n,r,g,s,a,I;if(!i||i===this){if(o=A.xo-e.xo,n=A.xo-t.xo,s=e.x-e.xo,a=t.x-t.xo,r=Math.abs(o),g=Math.abs(n),I=r+g,I===0){A.x=A.xo+(s+a)/2;return}A.x=A.xo+(s*g+a*r)/I;return}if(o=i.distance(A,e,!0,!0),n=i.distance(A,t,!0,!0),s=i.distance(e,e,!1,!0),a=i.distance(t,t,!1,!0),r=Math.abs(o),g=Math.abs(n),I=r+g,I===0){ot.setRelative(A,A,(s+a)/2,i,!0);return}ot.setRelative(A,A,(s*g+a*r)/I,i,!0)},normalSlope:Number.NEGATIVE_INFINITY,setRelative:function(A,e,t,i,o){if(!i||i===this){A.x=(o?e.xo:e.x)+t;return}var n=o?e.xo:e.x,r=o?e.yo:e.y,g=n+t*i.x,s=r+t*i.y;A.x=g+(A.y-s)/i.normalSlope},slope:0,touch:function(A){A.xTouched=!0},touched:function(A){return A.xTouched},untouch:function(A){A.xTouched=!1}},pt={x:0,y:1,axis:"y",distance:function(A,e,t,i){return(t?A.yo:A.y)-(i?e.yo:e.y)},interpolate:function(A,e,t,i){var o,n,r,g,s,a,I;if(!i||i===this){if(o=A.yo-e.yo,n=A.yo-t.yo,s=e.y-e.yo,a=t.y-t.yo,r=Math.abs(o),g=Math.abs(n),I=r+g,I===0){A.y=A.yo+(s+a)/2;return}A.y=A.yo+(s*g+a*r)/I;return}if(o=i.distance(A,e,!0,!0),n=i.distance(A,t,!0,!0),s=i.distance(e,e,!1,!0),a=i.distance(t,t,!1,!0),r=Math.abs(o),g=Math.abs(n),I=r+g,I===0){pt.setRelative(A,A,(s+a)/2,i,!0);return}pt.setRelative(A,A,(s*g+a*r)/I,i,!0)},normalSlope:0,setRelative:function(A,e,t,i,o){if(!i||i===this){A.y=(o?e.yo:e.y)+t;return}var n=o?e.xo:e.x,r=o?e.yo:e.y,g=n+t*i.x,s=r+t*i.y;A.y=s+i.normalSlope*(A.x-g)},slope:Number.POSITIVE_INFINITY,touch:function(A){A.yTouched=!0},touched:function(A){return A.yTouched},untouch:function(A){A.yTouched=!1}};Object.freeze(ot);Object.freeze(pt);function wo(A,e){this.x=A,this.y=e,this.axis=void 0,this.slope=e/A,this.normalSlope=-A/e,Object.freeze(this)}wo.prototype.distance=function(A,e,t,i){return this.x*ot.distance(A,e,t,i)+this.y*pt.distance(A,e,t,i)};wo.prototype.interpolate=function(A,e,t,i){var o,n,r,g,s,a,I;if(r=i.distance(A,e,!0,!0),g=i.distance(A,t,!0,!0),o=i.distance(e,e,!1,!0),n=i.distance(t,t,!1,!0),s=Math.abs(r),a=Math.abs(g),I=s+a,I===0){this.setRelative(A,A,(o+n)/2,i,!0);return}this.setRelative(A,A,(o*a+n*s)/I,i,!0)};wo.prototype.setRelative=function(A,e,t,i,o){i=i||this;var n=o?e.xo:e.x,r=o?e.yo:e.y,g=n+t*i.x,s=r+t*i.y,a=i.normalSlope,I=this.slope,l=A.x,C=A.y;A.x=(I*l-a*g+s-C)/(I-a),A.y=I*(A.x-l)+C};wo.prototype.touch=function(A){A.xTouched=!0,A.yTouched=!0};function Do(A,e){var t=Math.sqrt(A*A+e*e);return A/=t,e/=t,A===1&&e===0?ot:A===0&&e===1?pt:new wo(A,e)}function mt(A,e,t,i){this.x=this.xo=Math.round(A*64)/64,this.y=this.yo=Math.round(e*64)/64,this.lastPointOfContour=t,this.onCurve=i,this.prevPointOnContour=void 0,this.nextPointOnContour=void 0,this.xTouched=!1,this.yTouched=!1,Object.preventExtensions(this)}mt.prototype.nextTouched=function(A){for(var e=this.nextPointOnContour;!A.touched(e)&&e!==this;)e=e.nextPointOnContour;return e};mt.prototype.prevTouched=function(A){for(var e=this.prevPointOnContour;!A.touched(e)&&e!==this;)e=e.prevPointOnContour;return e};var lo=Object.freeze(new mt(0,0)),gk={cvCutIn:17/16,deltaBase:9,deltaShift:.125,loop:1,minDis:1,autoFlip:!0};function Ht(A,e){switch(this.env=A,this.stack=[],this.prog=e,A){case"glyf":this.zp0=this.zp1=this.zp2=1,this.rp0=this.rp1=this.rp2=0;case"prep":this.fv=this.pv=this.dpv=ot,this.round=bl}}Rl.prototype.exec=function(A,e){if(typeof e!="number")throw new Error("Point size is not a number!");if(!(this._errorState>2)){var t=this.font,i=this._prepState;if(!i||i.ppem!==e){var o=this._fpgmState;if(!o){Ht.prototype=gk,o=this._fpgmState=new Ht("fpgm",t.tables.fpgm),o.funcs=[],o.font=t,exports.DEBUG&&(console.log("---EXEC FPGM---"),o.step=-1);try{ai(o)}catch(a){console.log("Hinting error in FPGM:"+a),this._errorState=3;return}}Ht.prototype=o,i=this._prepState=new Ht("prep",t.tables.prep),i.ppem=e;var n=t.tables.cvt;if(n)for(var r=i.cvt=new Array(n.length),g=e/t.unitsPerEm,s=0;s<n.length;s++)r[s]=n[s]*g;else i.cvt=[];exports.DEBUG&&(console.log("---EXEC PREP---"),i.step=-1);try{ai(i)}catch(a){this._errorState<2&&console.log("Hinting error in PREP:"+a),this._errorState=2}}if(!(this._errorState>1))try{return Sl(A,i)}catch(a){this._errorState<1&&(console.log("Hinting error:"+a),console.log("Note: further hinting errors are silenced")),this._errorState=1;return}}};Sl=function(A,e){var t=e.ppem/e.font.unitsPerEm,i=t,o=A.components,n,r,g;if(Ht.prototype=e,!o)g=new Ht("glyf",A.instructions),exports.DEBUG&&(console.log("---EXEC GLYPH---"),g.step=-1),Hr(A,g,t,i),r=g.gZone;else{var s=e.font;r=[],n=[];for(var a=0;a<o.length;a++){var I=o[a],l=s.glyphs.get(I.glyphIndex);g=new Ht("glyf",l.instructions),exports.DEBUG&&(console.log("---EXEC COMP "+a+"---"),g.step=-1),Hr(l,g,t,i);for(var C=Math.round(I.dx*t),Q=Math.round(I.dy*i),f=g.gZone,h=g.contours,u=0;u<f.length;u++){var E=f[u];E.xTouched=E.yTouched=!1,E.xo=E.x=E.x+C,E.yo=E.y=E.y+Q}var D=r.length;r.push.apply(r,f);for(var c=0;c<h.length;c++)n.push(h[c]+D)}A.instructions&&!g.inhibitGridFit&&(g=new Ht("glyf",A.instructions),g.gZone=g.z0=g.z1=g.z2=r,g.contours=n,r.push(new mt(0,0),new mt(Math.round(A.advanceWidth*t),0)),exports.DEBUG&&(console.log("---EXEC COMPOSITE---"),g.step=-1),ai(g),r.length-=2)}return r};Hr=function(A,e,t,i){for(var o=A.points||[],n=o.length,r=e.gZone=e.z0=e.z1=e.z2=[],g=e.contours=[],s,a=0;a<n;a++)s=o[a],r[a]=new mt(s.x*t,s.y*i,s.lastPointOfContour,s.onCurve);for(var I,l,C=0;C<n;C++)s=r[C],I||(I=s,g.push(C)),s.lastPointOfContour?(s.nextPointOnContour=I,I.prevPointOnContour=s,I=void 0):(l=r[C+1],s.nextPointOnContour=l,l.prevPointOnContour=s);if(!e.inhibitGridFit){if(exports.DEBUG){console.log("PROCESSING GLYPH",e.stack);for(var Q=0;Q<n;Q++)console.log(Q,r[Q].x,r[Q].y)}if(r.push(new mt(0,0),new mt(Math.round(A.advanceWidth*t),0)),ai(e),r.length-=2,exports.DEBUG){console.log("FINISHED GLYPH",e.stack);for(var f=0;f<n;f++)console.log(f,r[f].x,r[f].y)}}};ai=function(A){var e=A.prog;if(e){var t=e.length,i;for(A.ip=0;A.ip<t;A.ip++){if(exports.DEBUG&&A.step++,i=Fl[e[A.ip]],!i)throw new Error("unknown instruction: 0x"+Number(e[A.ip]).toString(16));i(A)}}};function Gn(A){for(var e=A.tZone=new Array(A.gZone.length),t=0;t<e.length;t++)e[t]=new mt(0,0)}function Gl(A,e){var t=A.prog,i=A.ip,o=1,n;do if(n=t[++i],n===88)o++;else if(n===89)o--;else if(n===64)i+=t[i+1]+1;else if(n===65)i+=2*t[i+1]+1;else if(n>=176&&n<=183)i+=n-176+1;else if(n>=184&&n<=191)i+=(n-184+1)*2;else if(e&&o===1&&n===27)break;while(o>0);A.ip=i}function ha(A,e){exports.DEBUG&&console.log(e.step,"SVTCA["+A.axis+"]"),e.fv=e.pv=e.dpv=A}function da(A,e){exports.DEBUG&&console.log(e.step,"SPVTCA["+A.axis+"]"),e.pv=e.dpv=A}function pa(A,e){exports.DEBUG&&console.log(e.step,"SFVTCA["+A.axis+"]"),e.fv=A}function ma(A,e){var t=e.stack,i=t.pop(),o=t.pop(),n=e.z2[i],r=e.z1[o];exports.DEBUG&&console.log("SPVTL["+A+"]",i,o);var g,s;A?(g=n.y-r.y,s=r.x-n.x):(g=r.x-n.x,s=r.y-n.y),e.pv=e.dpv=Do(g,s)}function ya(A,e){var t=e.stack,i=t.pop(),o=t.pop(),n=e.z2[i],r=e.z1[o];exports.DEBUG&&console.log("SFVTL["+A+"]",i,o);var g,s;A?(g=n.y-r.y,s=r.x-n.x):(g=r.x-n.x,s=r.y-n.y),e.fv=Do(g,s)}function sk(A){var e=A.stack,t=e.pop(),i=e.pop();exports.DEBUG&&console.log(A.step,"SPVFS[]",t,i),A.pv=A.dpv=Do(i,t)}function ak(A){var e=A.stack,t=e.pop(),i=e.pop();exports.DEBUG&&console.log(A.step,"SPVFS[]",t,i),A.fv=Do(i,t)}function Ik(A){var e=A.stack,t=A.pv;exports.DEBUG&&console.log(A.step,"GPV[]"),e.push(t.x*16384),e.push(t.y*16384)}function Ck(A){var e=A.stack,t=A.fv;exports.DEBUG&&console.log(A.step,"GFV[]"),e.push(t.x*16384),e.push(t.y*16384)}function lk(A){A.fv=A.pv,exports.DEBUG&&console.log(A.step,"SFVTPV[]")}function Bk(A){var e=A.stack,t=e.pop(),i=e.pop(),o=e.pop(),n=e.pop(),r=e.pop(),g=A.z0,s=A.z1,a=g[t],I=g[i],l=s[o],C=s[n],Q=A.z2[r];exports.DEBUG&&console.log("ISECT[], ",t,i,o,n,r);var f=a.x,h=a.y,u=I.x,E=I.y,D=l.x,c=l.y,p=C.x,d=C.y,v=(f-u)*(c-d)-(h-E)*(D-p),b=f*E-h*u,N=D*d-c*p;Q.x=(b*(D-p)-N*(f-u))/v,Q.y=(b*(c-d)-N*(h-E))/v}function Qk(A){A.rp0=A.stack.pop(),exports.DEBUG&&console.log(A.step,"SRP0[]",A.rp0)}function ck(A){A.rp1=A.stack.pop(),exports.DEBUG&&console.log(A.step,"SRP1[]",A.rp1)}function Ek(A){A.rp2=A.stack.pop(),exports.DEBUG&&console.log(A.step,"SRP2[]",A.rp2)}function uk(A){var e=A.stack.pop();switch(exports.DEBUG&&console.log(A.step,"SZP0[]",e),A.zp0=e,e){case 0:A.tZone||Gn(A),A.z0=A.tZone;break;case 1:A.z0=A.gZone;break;default:throw new Error("Invalid zone pointer")}}function fk(A){var e=A.stack.pop();switch(exports.DEBUG&&console.log(A.step,"SZP1[]",e),A.zp1=e,e){case 0:A.tZone||Gn(A),A.z1=A.tZone;break;case 1:A.z1=A.gZone;break;default:throw new Error("Invalid zone pointer")}}function hk(A){var e=A.stack.pop();switch(exports.DEBUG&&console.log(A.step,"SZP2[]",e),A.zp2=e,e){case 0:A.tZone||Gn(A),A.z2=A.tZone;break;case 1:A.z2=A.gZone;break;default:throw new Error("Invalid zone pointer")}}function dk(A){var e=A.stack.pop();switch(exports.DEBUG&&console.log(A.step,"SZPS[]",e),A.zp0=A.zp1=A.zp2=e,e){case 0:A.tZone||Gn(A),A.z0=A.z1=A.z2=A.tZone;break;case 1:A.z0=A.z1=A.z2=A.gZone;break;default:throw new Error("Invalid zone pointer")}}function pk(A){A.loop=A.stack.pop(),exports.DEBUG&&console.log(A.step,"SLOOP[]",A.loop)}function mk(A){exports.DEBUG&&console.log(A.step,"RTG[]"),A.round=bl}function yk(A){exports.DEBUG&&console.log(A.step,"RTHG[]"),A.round=ok}function wk(A){var e=A.stack.pop();exports.DEBUG&&console.log(A.step,"SMD[]",e),A.minDis=e/64}function Dk(A){exports.DEBUG&&console.log(A.step,"ELSE[]"),Gl(A,!1)}function kk(A){var e=A.stack.pop();exports.DEBUG&&console.log(A.step,"JMPR[]",e),A.ip+=e-1}function vk(A){var e=A.stack.pop();exports.DEBUG&&console.log(A.step,"SCVTCI[]",e),A.cvCutIn=e/64}function Fk(A){var e=A.stack;exports.DEBUG&&console.log(A.step,"DUP[]"),e.push(e[e.length-1])}function $n(A){exports.DEBUG&&console.log(A.step,"POP[]"),A.stack.pop()}function Sk(A){exports.DEBUG&&console.log(A.step,"CLEAR[]"),A.stack.length=0}function Rk(A){var e=A.stack,t=e.pop(),i=e.pop();exports.DEBUG&&console.log(A.step,"SWAP[]"),e.push(t),e.push(i)}function bk(A){var e=A.stack;exports.DEBUG&&console.log(A.step,"DEPTH[]"),e.push(e.length)}function Nk(A){var e=A.stack,t=e.pop(),i=e.pop();exports.DEBUG&&console.log(A.step,"LOOPCALL[]",t,i);var o=A.ip,n=A.prog;A.prog=A.funcs[t];for(var r=0;r<i;r++)ai(A),exports.DEBUG&&console.log(++A.step,r+1<i?"next loopcall":"done loopcall",r);A.ip=o,A.prog=n}function Gk(A){var e=A.stack.pop();exports.DEBUG&&console.log(A.step,"CALL[]",e);var t=A.ip,i=A.prog;A.prog=A.funcs[e],ai(A),A.ip=t,A.prog=i,exports.DEBUG&&console.log(++A.step,"returning from",e)}function Lk(A){var e=A.stack,t=e.pop();exports.DEBUG&&console.log(A.step,"CINDEX[]",t),e.push(e[e.length-t])}function Uk(A){var e=A.stack,t=e.pop();exports.DEBUG&&console.log(A.step,"MINDEX[]",t),e.push(e.splice(e.length-t,1)[0])}function Mk(A){if(A.env!=="fpgm")throw new Error("FDEF not allowed here");var e=A.stack,t=A.prog,i=A.ip,o=e.pop(),n=i;for(exports.DEBUG&&console.log(A.step,"FDEF[]",o);t[++i]!==45;);A.ip=i,A.funcs[o]=t.slice(n+1,i)}function wa(A,e){var t=e.stack.pop(),i=e.z0[t],o=e.fv,n=e.pv;exports.DEBUG&&console.log(e.step,"MDAP["+A+"]",t);var r=n.distance(i,lo);A&&(r=e.round(r)),o.setRelative(i,lo,r,n),o.touch(i),e.rp0=e.rp1=t}function Da(A,e){var t=e.z2,i=t.length-2,o,n,r;exports.DEBUG&&console.log(e.step,"IUP["+A.axis+"]");for(var g=0;g<i;g++)o=t[g],!A.touched(o)&&(n=o.prevTouched(A),n!==o&&(r=o.nextTouched(A),n===r&&A.setRelative(o,o,A.distance(n,n,!1,!0),A,!0),A.interpolate(o,n,r,A)))}function ka(A,e){for(var t=e.stack,i=A?e.rp1:e.rp2,o=(A?e.z0:e.z1)[i],n=e.fv,r=e.pv,g=e.loop,s=e.z2;g--;){var a=t.pop(),I=s[a],l=r.distance(o,o,!1,!0);n.setRelative(I,I,l,r),n.touch(I),exports.DEBUG&&console.log(e.step,(e.loop>1?"loop "+(e.loop-g)+": ":"")+"SHP["+(A?"rp1":"rp2")+"]",a)}e.loop=1}function va(A,e){var t=e.stack,i=A?e.rp1:e.rp2,o=(A?e.z0:e.z1)[i],n=e.fv,r=e.pv,g=t.pop(),s=e.z2[e.contours[g]],a=s;exports.DEBUG&&console.log(e.step,"SHC["+A+"]",g);var I=r.distance(o,o,!1,!0);do a!==o&&n.setRelative(a,a,I,r),a=a.nextPointOnContour;while(a!==s)}function Fa(A,e){var t=e.stack,i=A?e.rp1:e.rp2,o=(A?e.z0:e.z1)[i],n=e.fv,r=e.pv,g=t.pop();exports.DEBUG&&console.log(e.step,"SHZ["+A+"]",g);var s;switch(g){case 0:s=e.tZone;break;case 1:s=e.gZone;break;default:throw new Error("Invalid zone")}for(var a,I=r.distance(o,o,!1,!0),l=s.length-2,C=0;C<l;C++)a=s[C],n.setRelative(a,a,I,r)}function xk(A){for(var e=A.stack,t=A.loop,i=A.fv,o=e.pop()/64,n=A.z2;t--;){var r=e.pop(),g=n[r];exports.DEBUG&&console.log(A.step,(A.loop>1?"loop "+(A.loop-t)+": ":"")+"SHPIX[]",r,o),i.setRelative(g,g,o),i.touch(g)}A.loop=1}function Jk(A){for(var e=A.stack,t=A.rp1,i=A.rp2,o=A.loop,n=A.z0[t],r=A.z1[i],g=A.fv,s=A.dpv,a=A.z2;o--;){var I=e.pop(),l=a[I];exports.DEBUG&&console.log(A.step,(A.loop>1?"loop "+(A.loop-o)+": ":"")+"IP[]",I,t,"<->",i),g.interpolate(l,n,r,s),g.touch(l)}A.loop=1}function Sa(A,e){var t=e.stack,i=t.pop()/64,o=t.pop(),n=e.z1[o],r=e.z0[e.rp0],g=e.fv,s=e.pv;g.setRelative(n,r,i,s),g.touch(n),exports.DEBUG&&console.log(e.step,"MSIRP["+A+"]",i,o),e.rp1=e.rp0,e.rp2=o,A&&(e.rp0=o)}function Tk(A){for(var e=A.stack,t=A.rp0,i=A.z0[t],o=A.loop,n=A.fv,r=A.pv,g=A.z1;o--;){var s=e.pop(),a=g[s];exports.DEBUG&&console.log(A.step,(A.loop>1?"loop "+(A.loop-o)+": ":"")+"ALIGNRP[]",s),n.setRelative(a,i,0,r),n.touch(a)}A.loop=1}function Hk(A){exports.DEBUG&&console.log(A.step,"RTDG[]"),A.round=ik}function Ra(A,e){var t=e.stack,i=t.pop(),o=t.pop(),n=e.z0[o],r=e.fv,g=e.pv,s=e.cvt[i];exports.DEBUG&&console.log(e.step,"MIAP["+A+"]",i,"(",s,")",o);var a=g.distance(n,lo);A&&(Math.abs(a-s)<e.cvCutIn&&(a=s),a=e.round(a)),r.setRelative(n,lo,a,g),e.zp0===0&&(n.xo=n.x,n.yo=n.y),r.touch(n),e.rp0=e.rp1=o}function Yk(A){var e=A.prog,t=A.ip,i=A.stack,o=e[++t];exports.DEBUG&&console.log(A.step,"NPUSHB[]",o);for(var n=0;n<o;n++)i.push(e[++t]);A.ip=t}function _k(A){var e=A.ip,t=A.prog,i=A.stack,o=t[++e];exports.DEBUG&&console.log(A.step,"NPUSHW[]",o);for(var n=0;n<o;n++){var r=t[++e]<<8|t[++e];r&32768&&(r=-((r^65535)+1)),i.push(r)}A.ip=e}function Kk(A){var e=A.stack,t=A.store;t||(t=A.store=[]);var i=e.pop(),o=e.pop();exports.DEBUG&&console.log(A.step,"WS",i,o),t[o]=i}function Ok(A){var e=A.stack,t=A.store,i=e.pop();exports.DEBUG&&console.log(A.step,"RS",i);var o=t&&t[i]||0;e.push(o)}function Pk(A){var e=A.stack,t=e.pop(),i=e.pop();exports.DEBUG&&console.log(A.step,"WCVTP",t,i),A.cvt[i]=t/64}function Vk(A){var e=A.stack,t=e.pop();exports.DEBUG&&console.log(A.step,"RCVT",t),e.push(A.cvt[t]*64)}function ba(A,e){var t=e.stack,i=t.pop(),o=e.z2[i];exports.DEBUG&&console.log(e.step,"GC["+A+"]",i),t.push(e.dpv.distance(o,lo,A,!1)*64)}function Na(A,e){var t=e.stack,i=t.pop(),o=t.pop(),n=e.z1[i],r=e.z0[o],g=e.dpv.distance(r,n,A,A);exports.DEBUG&&console.log(e.step,"MD["+A+"]",i,o,"->",g),e.stack.push(Math.round(g*64))}function Wk(A){exports.DEBUG&&console.log(A.step,"MPPEM[]"),A.stack.push(A.ppem)}function qk(A){exports.DEBUG&&console.log(A.step,"FLIPON[]"),A.autoFlip=!0}function jk(A){var e=A.stack,t=e.pop(),i=e.pop();exports.DEBUG&&console.log(A.step,"LT[]",t,i),e.push(i<t?1:0)}function Zk(A){var e=A.stack,t=e.pop(),i=e.pop();exports.DEBUG&&console.log(A.step,"LTEQ[]",t,i),e.push(i<=t?1:0)}function Xk(A){var e=A.stack,t=e.pop(),i=e.pop();exports.DEBUG&&console.log(A.step,"GT[]",t,i),e.push(i>t?1:0)}function zk(A){var e=A.stack,t=e.pop(),i=e.pop();exports.DEBUG&&console.log(A.step,"GTEQ[]",t,i),e.push(i>=t?1:0)}function $k(A){var e=A.stack,t=e.pop(),i=e.pop();exports.DEBUG&&console.log(A.step,"EQ[]",t,i),e.push(t===i?1:0)}function Av(A){var e=A.stack,t=e.pop(),i=e.pop();exports.DEBUG&&console.log(A.step,"NEQ[]",t,i),e.push(t!==i?1:0)}function ev(A){var e=A.stack,t=e.pop();exports.DEBUG&&console.log(A.step,"ODD[]",t),e.push(Math.trunc(t)%2?1:0)}function tv(A){var e=A.stack,t=e.pop();exports.DEBUG&&console.log(A.step,"EVEN[]",t),e.push(Math.trunc(t)%2?0:1)}function iv(A){var e=A.stack.pop();exports.DEBUG&&console.log(A.step,"IF[]",e),e||(Gl(A,!0),exports.DEBUG&&console.log(A.step,"EIF[]"))}function ov(A){exports.DEBUG&&console.log(A.step,"EIF[]")}function nv(A){var e=A.stack,t=e.pop(),i=e.pop();exports.DEBUG&&console.log(A.step,"AND[]",t,i),e.push(t&&i?1:0)}function rv(A){var e=A.stack,t=e.pop(),i=e.pop();exports.DEBUG&&console.log(A.step,"OR[]",t,i),e.push(t||i?1:0)}function gv(A){var e=A.stack,t=e.pop();exports.DEBUG&&console.log(A.step,"NOT[]",t),e.push(t?0:1)}function Ar(A,e){var t=e.stack,i=t.pop(),o=e.fv,n=e.pv,r=e.ppem,g=e.deltaBase+(A-1)*16,s=e.deltaShift,a=e.z0;exports.DEBUG&&console.log(e.step,"DELTAP["+A+"]",i,t);for(var I=0;I<i;I++){var l=t.pop(),C=t.pop(),Q=g+((C&240)>>4);if(Q===r){var f=(C&15)-8;f>=0&&f++,exports.DEBUG&&console.log(e.step,"DELTAPFIX",l,"by",f*s);var h=a[l];o.setRelative(h,h,f*s,n)}}}function sv(A){var e=A.stack,t=e.pop();exports.DEBUG&&console.log(A.step,"SDB[]",t),A.deltaBase=t}function av(A){var e=A.stack,t=e.pop();exports.DEBUG&&console.log(A.step,"SDS[]",t),A.deltaShift=Math.pow(.5,t)}function Iv(A){var e=A.stack,t=e.pop(),i=e.pop();exports.DEBUG&&console.log(A.step,"ADD[]",t,i),e.push(i+t)}function Cv(A){var e=A.stack,t=e.pop(),i=e.pop();exports.DEBUG&&console.log(A.step,"SUB[]",t,i),e.push(i-t)}function lv(A){var e=A.stack,t=e.pop(),i=e.pop();exports.DEBUG&&console.log(A.step,"DIV[]",t,i),e.push(i*64/t)}function Bv(A){var e=A.stack,t=e.pop(),i=e.pop();exports.DEBUG&&console.log(A.step,"MUL[]",t,i),e.push(i*t/64)}function Qv(A){var e=A.stack,t=e.pop();exports.DEBUG&&console.log(A.step,"ABS[]",t),e.push(Math.abs(t))}function cv(A){var e=A.stack,t=e.pop();exports.DEBUG&&console.log(A.step,"NEG[]",t),e.push(-t)}function Ev(A){var e=A.stack,t=e.pop();exports.DEBUG&&console.log(A.step,"FLOOR[]",t),e.push(Math.floor(t/64)*64)}function uv(A){var e=A.stack,t=e.pop();exports.DEBUG&&console.log(A.step,"CEILING[]",t),e.push(Math.ceil(t/64)*64)}function xo(A,e){var t=e.stack,i=t.pop();exports.DEBUG&&console.log(e.step,"ROUND[]"),t.push(e.round(i/64)*64)}function fv(A){var e=A.stack,t=e.pop(),i=e.pop();exports.DEBUG&&console.log(A.step,"WCVTF[]",t,i),A.cvt[i]=t*A.ppem/A.font.unitsPerEm}function er(A,e){var t=e.stack,i=t.pop(),o=e.ppem,n=e.deltaBase+(A-1)*16,r=e.deltaShift;exports.DEBUG&&console.log(e.step,"DELTAC["+A+"]",i,t);for(var g=0;g<i;g++){var s=t.pop(),a=t.pop(),I=n+((a&240)>>4);if(I===o){var l=(a&15)-8;l>=0&&l++;var C=l*r;exports.DEBUG&&console.log(e.step,"DELTACFIX",s,"by",C),e.cvt[s]+=C}}}function hv(A){var e=A.stack.pop();exports.DEBUG&&console.log(A.step,"SROUND[]",e),A.round=Nl;var t;switch(e&192){case 0:t=.5;break;case 64:t=1;break;case 128:t=2;break;default:throw new Error("invalid SROUND value")}switch(A.srPeriod=t,e&48){case 0:A.srPhase=0;break;case 16:A.srPhase=.25*t;break;case 32:A.srPhase=.5*t;break;case 48:A.srPhase=.75*t;break;default:throw new Error("invalid SROUND value")}e&=15,e===0?A.srThreshold=0:A.srThreshold=(e/8-.5)*t}function dv(A){var e=A.stack.pop();exports.DEBUG&&console.log(A.step,"S45ROUND[]",e),A.round=Nl;var t;switch(e&192){case 0:t=Math.sqrt(2)/2;break;case 64:t=Math.sqrt(2);break;case 128:t=2*Math.sqrt(2);break;default:throw new Error("invalid S45ROUND value")}switch(A.srPeriod=t,e&48){case 0:A.srPhase=0;break;case 16:A.srPhase=.25*t;break;case 32:A.srPhase=.5*t;break;case 48:A.srPhase=.75*t;break;default:throw new Error("invalid S45ROUND value")}e&=15,e===0?A.srThreshold=0:A.srThreshold=(e/8-.5)*t}function pv(A){exports.DEBUG&&console.log(A.step,"ROFF[]"),A.round=tk}function mv(A){exports.DEBUG&&console.log(A.step,"RUTG[]"),A.round=nk}function yv(A){exports.DEBUG&&console.log(A.step,"RDTG[]"),A.round=rk}function wv(A){var e=A.stack.pop();exports.DEBUG&&console.log(A.step,"SCANCTRL[]",e)}function Ga(A,e){var t=e.stack,i=t.pop(),o=t.pop(),n=e.z2[i],r=e.z1[o];exports.DEBUG&&console.log(e.step,"SDPVTL["+A+"]",i,o);var g,s;A?(g=n.y-r.y,s=r.x-n.x):(g=r.x-n.x,s=r.y-n.y),e.dpv=Do(g,s)}function Dv(A){var e=A.stack,t=e.pop(),i=0;exports.DEBUG&&console.log(A.step,"GETINFO[]",t),t&1&&(i=35),t&32&&(i|=4096),e.push(i)}function kv(A){var e=A.stack,t=e.pop(),i=e.pop(),o=e.pop();exports.DEBUG&&console.log(A.step,"ROLL[]"),e.push(i),e.push(t),e.push(o)}function vv(A){var e=A.stack,t=e.pop(),i=e.pop();exports.DEBUG&&console.log(A.step,"MAX[]",t,i),e.push(Math.max(i,t))}function Fv(A){var e=A.stack,t=e.pop(),i=e.pop();exports.DEBUG&&console.log(A.step,"MIN[]",t,i),e.push(Math.min(i,t))}function Sv(A){var e=A.stack.pop();exports.DEBUG&&console.log(A.step,"SCANTYPE[]",e)}function Rv(A){var e=A.stack.pop(),t=A.stack.pop();switch(exports.DEBUG&&console.log(A.step,"INSTCTRL[]",e,t),e){case 1:A.inhibitGridFit=!!t;return;case 2:A.ignoreCvt=!!t;return;default:throw new Error("invalid INSTCTRL[] selector")}}function bt(A,e){var t=e.stack,i=e.prog,o=e.ip;exports.DEBUG&&console.log(e.step,"PUSHB["+A+"]");for(var n=0;n<A;n++)t.push(i[++o]);e.ip=o}function Nt(A,e){var t=e.ip,i=e.prog,o=e.stack;exports.DEBUG&&console.log(e.ip,"PUSHW["+A+"]");for(var n=0;n<A;n++){var r=i[++t]<<8|i[++t];r&32768&&(r=-((r^65535)+1)),o.push(r)}e.ip=t}function BA(A,e,t,i,o,n){var r=n.stack,g=A&&r.pop(),s=r.pop(),a=n.rp0,I=n.z0[a],l=n.z1[s],C=n.minDis,Q=n.fv,f=n.dpv,h,u,E,D;u=h=f.distance(l,I,!0,!0),E=u>=0?1:-1,u=Math.abs(u),A&&(D=n.cvt[g],i&&Math.abs(u-D)<n.cvCutIn&&(u=D)),t&&u<C&&(u=C),i&&(u=n.round(u)),Q.setRelative(l,I,E*u,f),Q.touch(l),exports.DEBUG&&console.log(n.step,(A?"MIRP[":"MDRP[")+(e?"M":"m")+(t?">":"_")+(i?"R":"_")+(o===0?"Gr":o===1?"Bl":o===2?"Wh":"")+"]",A?g+"("+n.cvt[g]+","+D+")":"",s,"(d =",h,"->",E*u,")"),n.rp1=n.rp0,n.rp2=s,e&&(n.rp0=s)}Fl=[ha.bind(void 0,pt),ha.bind(void 0,ot),da.bind(void 0,pt),da.bind(void 0,ot),pa.bind(void 0,pt),pa.bind(void 0,ot),ma.bind(void 0,0),ma.bind(void 0,1),ya.bind(void 0,0),ya.bind(void 0,1),sk,ak,Ik,Ck,lk,Bk,Qk,ck,Ek,uk,fk,hk,dk,pk,mk,yk,wk,Dk,kk,vk,void 0,void 0,Fk,$n,Sk,Rk,bk,Lk,Uk,void 0,void 0,void 0,Nk,Gk,Mk,void 0,wa.bind(void 0,0),wa.bind(void 0,1),Da.bind(void 0,pt),Da.bind(void 0,ot),ka.bind(void 0,0),ka.bind(void 0,1),va.bind(void 0,0),va.bind(void 0,1),Fa.bind(void 0,0),Fa.bind(void 0,1),xk,Jk,Sa.bind(void 0,0),Sa.bind(void 0,1),Tk,Hk,Ra.bind(void 0,0),Ra.bind(void 0,1),Yk,_k,Kk,Ok,Pk,Vk,ba.bind(void 0,0),ba.bind(void 0,1),void 0,Na.bind(void 0,0),Na.bind(void 0,1),Wk,void 0,qk,void 0,void 0,jk,Zk,Xk,zk,$k,Av,ev,tv,iv,ov,nv,rv,gv,Ar.bind(void 0,1),sv,av,Iv,Cv,lv,Bv,Qv,cv,Ev,uv,xo.bind(void 0,0),xo.bind(void 0,1),xo.bind(void 0,2),xo.bind(void 0,3),void 0,void 0,void 0,void 0,fv,Ar.bind(void 0,2),Ar.bind(void 0,3),er.bind(void 0,1),er.bind(void 0,2),er.bind(void 0,3),hv,dv,void 0,void 0,pv,void 0,mv,yv,$n,$n,void 0,void 0,void 0,void 0,void 0,wv,Ga.bind(void 0,0),Ga.bind(void 0,1),Dv,void 0,kv,vv,Fv,Sv,Rv,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,void 0,bt.bind(void 0,1),bt.bind(void 0,2),bt.bind(void 0,3),bt.bind(void 0,4),bt.bind(void 0,5),bt.bind(void 0,6),bt.bind(void 0,7),bt.bind(void 0,8),Nt.bind(void 0,1),Nt.bind(void 0,2),Nt.bind(void 0,3),Nt.bind(void 0,4),Nt.bind(void 0,5),Nt.bind(void 0,6),Nt.bind(void 0,7),Nt.bind(void 0,8),BA.bind(void 0,0,0,0,0,0),BA.bind(void 0,0,0,0,0,1),BA.bind(void 0,0,0,0,0,2),BA.bind(void 0,0,0,0,0,3),BA.bind(void 0,0,0,0,1,0),BA.bind(void 0,0,0,0,1,1),BA.bind(void 0,0,0,0,1,2),BA.bind(void 0,0,0,0,1,3),BA.bind(void 0,0,0,1,0,0),BA.bind(void 0,0,0,1,0,1),BA.bind(void 0,0,0,1,0,2),BA.bind(void 0,0,0,1,0,3),BA.bind(void 0,0,0,1,1,0),BA.bind(void 0,0,0,1,1,1),BA.bind(void 0,0,0,1,1,2),BA.bind(void 0,0,0,1,1,3),BA.bind(void 0,0,1,0,0,0),BA.bind(void 0,0,1,0,0,1),BA.bind(void 0,0,1,0,0,2),BA.bind(void 0,0,1,0,0,3),BA.bind(void 0,0,1,0,1,0),BA.bind(void 0,0,1,0,1,1),BA.bind(void 0,0,1,0,1,2),BA.bind(void 0,0,1,0,1,3),BA.bind(void 0,0,1,1,0,0),BA.bind(void 0,0,1,1,0,1),BA.bind(void 0,0,1,1,0,2),BA.bind(void 0,0,1,1,0,3),BA.bind(void 0,0,1,1,1,0),BA.bind(void 0,0,1,1,1,1),BA.bind(void 0,0,1,1,1,2),BA.bind(void 0,0,1,1,1,3),BA.bind(void 0,1,0,0,0,0),BA.bind(void 0,1,0,0,0,1),BA.bind(void 0,1,0,0,0,2),BA.bind(void 0,1,0,0,0,3),BA.bind(void 0,1,0,0,1,0),BA.bind(void 0,1,0,0,1,1),BA.bind(void 0,1,0,0,1,2),BA.bind(void 0,1,0,0,1,3),BA.bind(void 0,1,0,1,0,0),BA.bind(void 0,1,0,1,0,1),BA.bind(void 0,1,0,1,0,2),BA.bind(void 0,1,0,1,0,3),BA.bind(void 0,1,0,1,1,0),BA.bind(void 0,1,0,1,1,1),BA.bind(void 0,1,0,1,1,2),BA.bind(void 0,1,0,1,1,3),BA.bind(void 0,1,1,0,0,0),BA.bind(void 0,1,1,0,0,1),BA.bind(void 0,1,1,0,0,2),BA.bind(void 0,1,1,0,0,3),BA.bind(void 0,1,1,0,1,0),BA.bind(void 0,1,1,0,1,1),BA.bind(void 0,1,1,0,1,2),BA.bind(void 0,1,1,0,1,3),BA.bind(void 0,1,1,1,0,0),BA.bind(void 0,1,1,1,0,1),BA.bind(void 0,1,1,1,0,2),BA.bind(void 0,1,1,1,0,3),BA.bind(void 0,1,1,1,1,0),BA.bind(void 0,1,1,1,1,1),BA.bind(void 0,1,1,1,1,2),BA.bind(void 0,1,1,1,1,3)];function Ui(A){this.char=A,this.state={},this.activeState=null}function wg(A,e,t){this.contextName=t,this.startIndex=A,this.endOffset=e}function bv(A,e,t){this.contextName=A,this.openRange=null,this.ranges=[],this.checkStart=e,this.checkEnd=t}function Ve(A,e){this.context=A,this.index=e,this.length=A.length,this.current=A[e],this.backtrack=A.slice(0,e),this.lookahead=A.slice(e+1)}function Ln(A){this.eventId=A,this.subscribers=[]}function Nv(A){var e=this,t=["start","end","next","newToken","contextStart","contextEnd","insertToken","removeToken","removeRange","replaceToken","replaceRange","composeRUD","updateContextsRanges"];t.forEach(function(o){Object.defineProperty(e.events,o,{value:new Ln(o)})}),A&&t.forEach(function(o){var n=A[o];typeof n=="function"&&e.events[o].subscribe(n)});var i=["insertToken","removeToken","removeRange","replaceToken","replaceRange","composeRUD"];i.forEach(function(o){e.events[o].subscribe(e.updateContextsRanges)})}function zA(A){this.tokens=[],this.registeredContexts={},this.contextCheckers=[],this.events={},this.registeredModifiers=[],Nv.call(this,A)}Ui.prototype.setState=function(A,e){return this.state[A]=e,this.activeState={key:A,value:this.state[A]},this.activeState};Ui.prototype.getState=function(A){return this.state[A]||null};zA.prototype.inboundIndex=function(A){return A>=0&&A<this.tokens.length};zA.prototype.composeRUD=function(A){var e=this,t=!0,i=A.map(function(n){return e[n[0]].apply(e,n.slice(1).concat(t))}),o=function(n){return typeof n=="object"&&n.hasOwnProperty("FAIL")};if(i.every(o))return{FAIL:"composeRUD: one or more operations hasn't completed successfully",report:i.filter(o)};this.dispatch("composeRUD",[i.filter(function(n){return!o(n)})])};zA.prototype.replaceRange=function(A,e,t,i){e=e!==null?e:this.tokens.length;var o=t.every(function(r){return r instanceof Ui});if(!isNaN(A)&&this.inboundIndex(A)&&o){var n=this.tokens.splice.apply(this.tokens,[A,e].concat(t));return i||this.dispatch("replaceToken",[A,e,t]),[n,t]}else return{FAIL:"replaceRange: invalid tokens or startIndex."}};zA.prototype.replaceToken=function(A,e,t){if(!isNaN(A)&&this.inboundIndex(A)&&e instanceof Ui){var i=this.tokens.splice(A,1,e);return t||this.dispatch("replaceToken",[A,e]),[i[0],e]}else return{FAIL:"replaceToken: invalid token or index."}};zA.prototype.removeRange=function(A,e,t){e=isNaN(e)?this.tokens.length:e;var i=this.tokens.splice(A,e);return t||this.dispatch("removeRange",[i,A,e]),i};zA.prototype.removeToken=function(A,e){if(!isNaN(A)&&this.inboundIndex(A)){var t=this.tokens.splice(A,1);return e||this.dispatch("removeToken",[t,A]),t}else return{FAIL:"removeToken: invalid token index."}};zA.prototype.insertToken=function(A,e,t){var i=A.every(function(o){return o instanceof Ui});return i?(this.tokens.splice.apply(this.tokens,[e,0].concat(A)),t||this.dispatch("insertToken",[A,e]),A):{FAIL:"insertToken: invalid token(s)."}};zA.prototype.registerModifier=function(A,e,t){this.events.newToken.subscribe(function(i,o){var n=[i,o],r=e===null||e.apply(this,n)===!0,g=[i,o];if(r){var s=t.apply(this,g);i.setState(A,s)}}),this.registeredModifiers.push(A)};Ln.prototype.subscribe=function(A){return typeof A=="function"?this.subscribers.push(A)-1:{FAIL:"invalid '"+this.eventId+"' event handler"}};Ln.prototype.unsubscribe=function(A){this.subscribers.splice(A,1)};Ve.prototype.setCurrentIndex=function(A){this.index=A,this.current=this.context[A],this.backtrack=this.context.slice(0,A),this.lookahead=this.context.slice(A+1)};Ve.prototype.get=function(A){switch(!0){case A===0:return this.current;case(A<0&&Math.abs(A)<=this.backtrack.length):return this.backtrack.slice(A)[0];case(A>0&&A<=this.lookahead.length):return this.lookahead[A-1];default:return null}};zA.prototype.rangeToText=function(A){if(A instanceof wg)return this.getRangeTokens(A).map(function(e){return e.char}).join("")};zA.prototype.getText=function(){return this.tokens.map(function(A){return A.char}).join("")};zA.prototype.getContext=function(A){var e=this.registeredContexts[A];return e||null};zA.prototype.on=function(A,e){var t=this.events[A];return t?t.subscribe(e):null};zA.prototype.dispatch=function(A,e){var t=this,i=this.events[A];i instanceof Ln&&i.subscribers.forEach(function(o){o.apply(t,e||[])})};zA.prototype.registerContextChecker=function(A,e,t){if(this.getContext(A))return{FAIL:"context name '"+A+"' is already registered."};if(typeof e!="function")return{FAIL:"missing context start check."};if(typeof t!="function")return{FAIL:"missing context end check."};var i=new bv(A,e,t);return this.registeredContexts[A]=i,this.contextCheckers.push(i),i};zA.prototype.getRangeTokens=function(A){var e=A.startIndex+A.endOffset;return[].concat(this.tokens.slice(A.startIndex,e))};zA.prototype.getContextRanges=function(A){var e=this.getContext(A);return e?e.ranges:{FAIL:"context checker '"+A+"' is not registered."}};zA.prototype.resetContextsRanges=function(){var A=this.registeredContexts;for(var e in A)if(A.hasOwnProperty(e)){var t=A[e];t.ranges=[]}};zA.prototype.updateContextsRanges=function(){this.resetContextsRanges();for(var A=this.tokens.map(function(i){return i.char}),e=0;e<A.length;e++){var t=new Ve(A,e);this.runContextCheck(t)}this.dispatch("updateContextsRanges",[this.registeredContexts])};zA.prototype.setEndOffset=function(A,e){var t=this.getContext(e).openRange.startIndex,i=new wg(t,A,e),o=this.getContext(e).ranges;return i.rangeId=e+"."+o.length,o.push(i),this.getContext(e).openRange=null,i};zA.prototype.runContextCheck=function(A){var e=this,t=A.index;this.contextCheckers.forEach(function(i){var o=i.contextName,n=e.getContext(o).openRange;if(!n&&i.checkStart(A)&&(n=new wg(t,null,o),e.getContext(o).openRange=n,e.dispatch("contextStart",[o,t])),n&&i.checkEnd(A)){var r=t-n.startIndex+1,g=e.setEndOffset(r,o);e.dispatch("contextEnd",[o,g])}})};zA.prototype.tokenize=function(A){this.tokens=[],this.resetContextsRanges();var e=Array.from(A);this.dispatch("start");for(var t=0;t<e.length;t++){var i=e[t],o=new Ve(e,t);this.dispatch("next",[o]),this.runContextCheck(o);var n=new Ui(i);this.tokens.push(n),this.dispatch("newToken",[n,o])}return this.dispatch("end",[this.tokens]),this.tokens};function _t(A){return/[\u0600-\u065F\u066A-\u06D2\u06FA-\u06FF]/.test(A)}function Ll(A){return/[\u0630\u0690\u0621\u0631\u0661\u0671\u0622\u0632\u0672\u0692\u06C2\u0623\u0673\u0693\u06C3\u0624\u0694\u06C4\u0625\u0675\u0695\u06C5\u06E5\u0676\u0696\u06C6\u0627\u0677\u0697\u06C7\u0648\u0688\u0698\u06C8\u0689\u0699\u06C9\u068A\u06CA\u066B\u068B\u06CB\u068C\u068D\u06CD\u06FD\u068E\u06EE\u06FE\u062F\u068F\u06CF\u06EF]/.test(A)}function Pt(A){return/[\u0600-\u0605\u060C-\u060E\u0610-\u061B\u061E\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED]/.test(A)}function Wo(A){return/[A-z]/.test(A)}function Gv(A){return/\s/.test(A)}function Le(A){this.font=A,this.features={}}function oi(A){this.id=A.id,this.tag=A.tag,this.substitution=A.substitution}function ko(A,e){if(!A)return-1;switch(e.format){case 1:return e.glyphs.indexOf(A);case 2:for(var t=e.ranges,i=0;i<t.length;i++){var o=t[i];if(A>=o.start&&A<=o.end){var n=A-o.start;return o.index+n}}break;default:return-1}return-1}function Lv(A,e){var t=ko(A,e.coverage);return t===-1?null:A+e.deltaGlyphId}function Uv(A,e){var t=ko(A,e.coverage);return t===-1?null:e.substitute[t]}function tr(A,e){for(var t=[],i=0;i<A.length;i++){var o=A[i],n=e.current;n=Array.isArray(n)?n[0]:n;var r=ko(n,o);r!==-1&&t.push(r)}return t.length!==A.length?-1:t}function Mv(A,e){var t=e.inputCoverage.length+e.lookaheadCoverage.length+e.backtrackCoverage.length;if(A.context.length<t)return[];var i=tr(e.inputCoverage,A);if(i===-1)return[];var o=e.inputCoverage.length-1;if(A.lookahead.length<e.lookaheadCoverage.length)return[];for(var n=A.lookahead.slice(o);n.length&&Pt(n[0].char);)n.shift();var r=new Ve(n,0),g=tr(e.lookaheadCoverage,r),s=[].concat(A.backtrack);for(s.reverse();s.length&&Pt(s[0].char);)s.shift();if(s.length<e.backtrackCoverage.length)return[];var a=new Ve(s,0),I=tr(e.backtrackCoverage,a),l=i.length===e.inputCoverage.length&&g.length===e.lookaheadCoverage.length&&I.length===e.backtrackCoverage.length,C=[];if(l)for(var Q=0;Q<e.lookupRecords.length;Q++)for(var f=e.lookupRecords[Q],h=f.lookupListIndex,u=this.getLookupByIndex(h),E=0;E<u.subtables.length;E++){var D=u.subtables[E],c=this.getLookupMethod(u,D),p=this.getSubstitutionType(u,D);if(p==="12")for(var d=0;d<i.length;d++){var v=A.get(d),b=c(v);b&&C.push(b)}}return C}function xv(A,e){var t=A.current,i=ko(t,e.coverage);if(i===-1)return null;for(var o,n=e.ligatureSets[i],r=0;r<n.length;r++){o=n[r];for(var g=0;g<o.components.length;g++){var s=A.lookahead[g],a=o.components[g];if(s!==a)break;if(g===o.components.length-1)return o}}return null}function Jv(A,e){var t=ko(A,e.coverage);return t===-1?null:e.sequences[t]}Le.prototype.getDefaultScriptFeaturesIndexes=function(){for(var A=this.font.tables.gsub.scripts,e=0;e<A.length;e++){var t=A[e];if(t.tag==="DFLT")return t.script.defaultLangSys.featureIndexes}return[]};Le.prototype.getScriptFeaturesIndexes=function(A){var e=this.font.tables;if(!e.gsub)return[];if(!A)return this.getDefaultScriptFeaturesIndexes();for(var t=this.font.tables.gsub.scripts,i=0;i<t.length;i++){var o=t[i];if(o.tag===A&&o.script.defaultLangSys)return o.script.defaultLangSys.featureIndexes;var n=o.langSysRecords;if(n)for(var r=0;r<n.length;r++){var g=n[r];if(g.tag===A){var s=g.langSys;return s.featureIndexes}}}return this.getDefaultScriptFeaturesIndexes()};Le.prototype.mapTagsToFeatures=function(A,e){for(var t={},i=0;i<A.length;i++){var o=A[i].tag,n=A[i].feature;t[o]=n}this.features[e].tags=t};Le.prototype.getScriptFeatures=function(A){var e=this.features[A];if(this.features.hasOwnProperty(A))return e;var t=this.getScriptFeaturesIndexes(A);if(!t)return null;var i=this.font.tables.gsub;return e=t.map(function(o){return i.features[o]}),this.features[A]=e,this.mapTagsToFeatures(e,A),e};Le.prototype.getSubstitutionType=function(A,e){var t=A.lookupType.toString(),i=e.substFormat.toString();return t+i};Le.prototype.getLookupMethod=function(A,e){var t=this,i=this.getSubstitutionType(A,e);switch(i){case"11":return function(o){return Lv.apply(t,[o,e])};case"12":return function(o){return Uv.apply(t,[o,e])};case"63":return function(o){return Mv.apply(t,[o,e])};case"41":return function(o){return xv.apply(t,[o,e])};case"21":return function(o){return Jv.apply(t,[o,e])};default:throw new Error("lookupType: "+A.lookupType+" - substFormat: "+e.substFormat+" is not yet supported")}};Le.prototype.lookupFeature=function(A){var e=A.contextParams,t=e.index,i=this.getFeature({tag:A.tag,script:A.script});if(!i)return new Error("font '"+this.font.names.fullName.en+"' doesn't support feature '"+A.tag+"' for script '"+A.script+"'.");for(var o=this.getFeatureLookups(i),n=[].concat(e.context),r=0;r<o.length;r++)for(var g=o[r],s=this.getLookupSubtables(g),a=0;a<s.length;a++){var I=s[a],l=this.getSubstitutionType(g,I),C=this.getLookupMethod(g,I),Q=void 0;switch(l){case"11":Q=C(e.current),Q&&n.splice(t,1,new oi({id:11,tag:A.tag,substitution:Q}));break;case"12":Q=C(e.current),Q&&n.splice(t,1,new oi({id:12,tag:A.tag,substitution:Q}));break;case"63":Q=C(e),Array.isArray(Q)&&Q.length&&n.splice(t,1,new oi({id:63,tag:A.tag,substitution:Q}));break;case"41":Q=C(e),Q&&n.splice(t,1,new oi({id:41,tag:A.tag,substitution:Q}));break;case"21":Q=C(e.current),Q&&n.splice(t,1,new oi({id:21,tag:A.tag,substitution:Q}));break}e=new Ve(n,t),!(Array.isArray(Q)&&!Q.length)&&(Q=null)}return n.length?n:null};Le.prototype.supports=function(A){if(!A.script)return!1;this.getScriptFeatures(A.script);var e=this.features.hasOwnProperty(A.script);if(!A.tag)return e;var t=this.features[A.script].some(function(i){return i.tag===A.tag});return e&&t};Le.prototype.getLookupSubtables=function(A){return A.subtables||null};Le.prototype.getLookupByIndex=function(A){var e=this.font.tables.gsub.lookups;return e[A]||null};Le.prototype.getFeatureLookups=function(A){return A.lookupListIndexes.map(this.getLookupByIndex.bind(this))};Le.prototype.getFeature=function(e){if(!this.font)return{FAIL:"No font was found"};this.features.hasOwnProperty(e.script)||this.getScriptFeatures(e.script);var t=this.features[e.script];return t?t.tags[e.tag]?this.features[e.script].tags[e.tag]:null:{FAIL:"No feature for script "+e.script}};function Tv(A){var e=A.current,t=A.get(-1);return t===null&&_t(e)||!_t(t)&&_t(e)}function Hv(A){var e=A.get(1);return e===null||!_t(e)}var Yv={startCheck:Tv,endCheck:Hv};function _v(A){var e=A.current,t=A.get(-1);return(_t(e)||Pt(e))&&!_t(t)}function Kv(A){var e=A.get(1);switch(!0){case e===null:return!0;case(!_t(e)&&!Pt(e)):var t=Gv(e);if(!t)return!0;if(t){var i=!1;if(i=A.lookahead.some(function(o){return _t(o)||Pt(o)}),!i)return!0}break;default:return!1}}var Ov={startCheck:_v,endCheck:Kv};function Pv(A,e,t){e[t].setState(A.tag,A.substitution)}function Vv(A,e,t){e[t].setState(A.tag,A.substitution)}function Wv(A,e,t){A.substitution.forEach(function(i,o){var n=e[t+o];n.setState(A.tag,i)})}function qv(A,e,t){var i=e[t];i.setState(A.tag,A.substitution.ligGlyph);for(var o=A.substitution.components.length,n=0;n<o;n++)i=e[t+n+1],i.setState("deleted",!0)}var La={11:Pv,12:Vv,63:Wv,41:qv};function Dg(A,e,t){A instanceof oi&&La[A.id]&&La[A.id](A,e,t)}function jv(A){for(var e=[].concat(A.backtrack),t=e.length-1;t>=0;t--){var i=e[t],o=Ll(i),n=Pt(i);if(!o&&!n)return!0;if(o)return!1}return!1}function Zv(A){if(Ll(A.current))return!1;for(var e=0;e<A.lookahead.length;e++){var t=A.lookahead[e],i=Pt(t);if(!i)return!0}return!1}function Xv(A){var e=this,t="arab",i=this.featuresTags[t],o=this.tokenizer.getRangeTokens(A);if(o.length!==1){var n=new Ve(o.map(function(g){return g.getState("glyphIndex")}),0),r=new Ve(o.map(function(g){return g.char}),0);o.forEach(function(g,s){if(!Pt(g.char)){n.setCurrentIndex(s),r.setCurrentIndex(s);var a=0;jv(r)&&(a|=1),Zv(r)&&(a|=2);var I;switch(a){case 1:I="fina";break;case 2:I="init";break;case 3:I="medi";break}if(i.indexOf(I)!==-1){var l=e.query.lookupFeature({tag:I,script:t,contextParams:n});if(l instanceof Error)return console.info(l.message);l.forEach(function(C,Q){C instanceof oi&&(Dg(C,o,Q),n.context[Q]=C.substitution)})}}})}}function Ua(A,e){var t=A.map(function(i){return i.activeState.value});return new Ve(t,0)}function zv(A){var e=this,t="arab",i=this.tokenizer.getRangeTokens(A),o=Ua(i);o.context.forEach(function(n,r){o.setCurrentIndex(r);var g=e.query.lookupFeature({tag:"rlig",script:t,contextParams:o});g.length&&(g.forEach(function(s){return Dg(s,i,r)}),o=Ua(i))})}function $v(A){var e=A.current,t=A.get(-1);return t===null&&Wo(e)||!Wo(t)&&Wo(e)}function AF(A){var e=A.get(1);return e===null||!Wo(e)}var eF={startCheck:$v,endCheck:AF};function Ma(A,e){var t=A.map(function(i){return i.activeState.value});return new Ve(t,0)}function tF(A){var e=this,t="latn",i=this.tokenizer.getRangeTokens(A),o=Ma(i);o.context.forEach(function(n,r){o.setCurrentIndex(r);var g=e.query.lookupFeature({tag:"liga",script:t,contextParams:o});g.length&&(g.forEach(function(s){return Dg(s,i,r)}),o=Ma(i))})}function Ze(A){this.baseDir=A||"ltr",this.tokenizer=new zA,this.featuresTags={}}Ze.prototype.setText=function(A){this.text=A};Ze.prototype.contextChecks={latinWordCheck:eF,arabicWordCheck:Yv,arabicSentenceCheck:Ov};function ir(A){var e=this.contextChecks[A+"Check"];return this.tokenizer.registerContextChecker(A,e.startCheck,e.endCheck)}function iF(){return ir.call(this,"latinWord"),ir.call(this,"arabicWord"),ir.call(this,"arabicSentence"),this.tokenizer.tokenize(this.text)}function oF(){var A=this,e=this.tokenizer.getContextRanges("arabicSentence");e.forEach(function(t){var i=A.tokenizer.getRangeTokens(t);A.tokenizer.replaceRange(t.startIndex,t.endOffset,i.reverse())})}Ze.prototype.registerFeatures=function(A,e){var t=this,i=e.filter(function(o){return t.query.supports({script:A,tag:o})});this.featuresTags.hasOwnProperty(A)?this.featuresTags[A]=this.featuresTags[A].concat(i):this.featuresTags[A]=i};Ze.prototype.applyFeatures=function(A,e){if(!A)throw new Error("No valid font was provided to apply features");this.query||(this.query=new Le(A));for(var t=0;t<e.length;t++){var i=e[t];this.query.supports({script:i.script})&&this.registerFeatures(i.script,i.tags)}};Ze.prototype.registerModifier=function(A,e,t){this.tokenizer.registerModifier(A,e,t)};function kg(){if(this.tokenizer.registeredModifiers.indexOf("glyphIndex")===-1)throw new Error("glyphIndex modifier is required to apply arabic presentation features.")}function nF(){var A=this,e="arab";if(this.featuresTags.hasOwnProperty(e)){kg.call(this);var t=this.tokenizer.getContextRanges("arabicWord");t.forEach(function(i){Xv.call(A,i)})}}function rF(){var A=this,e="arab";if(this.featuresTags.hasOwnProperty(e)){var t=this.featuresTags[e];if(t.indexOf("rlig")!==-1){kg.call(this);var i=this.tokenizer.getContextRanges("arabicWord");i.forEach(function(o){zv.call(A,o)})}}}function gF(){var A=this,e="latn";if(this.featuresTags.hasOwnProperty(e)){var t=this.featuresTags[e];if(t.indexOf("liga")!==-1){kg.call(this);var i=this.tokenizer.getContextRanges("latinWord");i.forEach(function(o){tF.call(A,o)})}}}Ze.prototype.checkContextReady=function(A){return!!this.tokenizer.getContext(A)};Ze.prototype.applyFeaturesToContexts=function(){this.checkContextReady("arabicWord")&&(nF.call(this),rF.call(this)),this.checkContextReady("latinWord")&&gF.call(this),this.checkContextReady("arabicSentence")&&oF.call(this)};Ze.prototype.processText=function(A){(!this.text||this.text!==A)&&(this.setText(A),iF.call(this),this.applyFeaturesToContexts())};Ze.prototype.getBidiText=function(A){return this.processText(A),this.tokenizer.getText()};Ze.prototype.getTextGlyphs=function(A){this.processText(A);for(var e=[],t=0;t<this.tokenizer.tokens.length;t++){var i=this.tokenizer.tokens[t];if(!i.state.deleted){var o=i.activeState.value;e.push(Array.isArray(o)?o[0]:o)}}return e};function YA(A){A=A||{},A.tables=A.tables||{},A.empty||(Ti(A.familyName,"When creating a new Font object, familyName is required."),Ti(A.styleName,"When creating a new Font object, styleName is required."),Ti(A.unitsPerEm,"When creating a new Font object, unitsPerEm is required."),Ti(A.ascender,"When creating a new Font object, ascender is required."),Ti(A.descender<=0,"When creating a new Font object, negative descender value is required."),this.names={fontFamily:{en:A.familyName||" "},fontSubfamily:{en:A.styleName||" "},fullName:{en:A.fullName||A.familyName+" "+A.styleName},postScriptName:{en:A.postScriptName||(A.familyName+A.styleName).replace(/\s/g,"")},designer:{en:A.designer||" "},designerURL:{en:A.designerURL||" "},manufacturer:{en:A.manufacturer||" "},manufacturerURL:{en:A.manufacturerURL||" "},license:{en:A.license||" "},licenseURL:{en:A.licenseURL||" "},version:{en:A.version||"Version 0.1"},description:{en:A.description||" "},copyright:{en:A.copyright||" "},trademark:{en:A.trademark||" "}},this.unitsPerEm=A.unitsPerEm||1e3,this.ascender=A.ascender,this.descender=A.descender,this.createdTimestamp=A.createdTimestamp,this.tables=Object.assign(A.tables,{os2:Object.assign({usWeightClass:A.weightClass||this.usWeightClasses.MEDIUM,usWidthClass:A.widthClass||this.usWidthClasses.MEDIUM,fsSelection:A.fsSelection||this.fsSelectionValues.REGULAR},A.tables.os2)})),this.supported=!0,this.glyphs=new at.GlyphSet(this,A.glyphs||[]),this.encoding=new zC(this),this.position=new yo(this),this.substitution=new Ge(this),this.tables=this.tables||{},this._push=null,this._hmtxTableData={},Object.defineProperty(this,"hinting",{get:function(){if(this._hinting)return this._hinting;if(this.outlinesFormat==="truetype")return this._hinting=new Rl(this)}})}YA.prototype.hasChar=function(A){return this.encoding.charToGlyphIndex(A)!==null};YA.prototype.charToGlyphIndex=function(A){return this.encoding.charToGlyphIndex(A)};YA.prototype.charToGlyph=function(A){var e=this.charToGlyphIndex(A),t=this.glyphs.get(e);return t||(t=this.glyphs.get(0)),t};YA.prototype.updateFeatures=function(A){return this.defaultRenderOptions.features.map(function(e){return e.script==="latn"?{script:"latn",tags:e.tags.filter(function(t){return A[t]})}:e})};YA.prototype.stringToGlyphs=function(A,e){var t=this,i=new Ze,o=function(l){return t.charToGlyphIndex(l.char)};i.registerModifier("glyphIndex",null,o);var n=e?this.updateFeatures(e.features):this.defaultRenderOptions.features;i.applyFeatures(this,n);for(var r=i.getTextGlyphs(A),g=r.length,s=new Array(g),a=this.glyphs.get(0),I=0;I<g;I+=1)s[I]=this.glyphs.get(r[I])||a;return s};YA.prototype.nameToGlyphIndex=function(A){return this.glyphNames.nameToGlyphIndex(A)};YA.prototype.nameToGlyph=function(A){var e=this.nameToGlyphIndex(A),t=this.glyphs.get(e);return t||(t=this.glyphs.get(0)),t};YA.prototype.glyphIndexToName=function(A){return this.glyphNames.glyphIndexToName?this.glyphNames.glyphIndexToName(A):""};YA.prototype.getKerningValue=function(A,e){A=A.index||A,e=e.index||e;var t=this.position.defaultKerningTables;return t?this.position.getKerningValue(t,A,e):this.kerningPairs[A+","+e]||0};YA.prototype.defaultRenderOptions={kerning:!0,features:[{script:"arab",tags:["init","medi","fina","rlig"]},{script:"latn",tags:["liga","rlig"]}]};YA.prototype.forEachGlyph=function(A,e,t,i,o,n){e=e!==void 0?e:0,t=t!==void 0?t:0,i=i!==void 0?i:72,o=Object.assign({},this.defaultRenderOptions,o);var r=1/this.unitsPerEm*i,g=this.stringToGlyphs(A,o),s;if(o.kerning){var a=o.script||this.position.getDefaultScriptName();s=this.position.getKerningTables(a,o.language)}for(var I=0;I<g.length;I+=1){var l=g[I];if(n.call(this,l,e,t,i,o),l.advanceWidth&&(e+=l.advanceWidth*r),o.kerning&&I<g.length-1){var C=s?this.position.getKerningValue(s,l.index,g[I+1].index):this.getKerningValue(l,g[I+1]);e+=C*r}o.letterSpacing?e+=o.letterSpacing*i:o.tracking&&(e+=o.tracking/1e3*i)}return e};YA.prototype.getPath=function(A,e,t,i,o){var n=new ie;return this.forEachGlyph(A,e,t,i,o,function(r,g,s,a){var I=r.getPath(g,s,a,o,this);n.extend(I)}),n};YA.prototype.getPaths=function(A,e,t,i,o){var n=[];return this.forEachGlyph(A,e,t,i,o,function(r,g,s,a){var I=r.getPath(g,s,a,o,this);n.push(I)}),n};YA.prototype.getAdvanceWidth=function(A,e,t){return this.forEachGlyph(A,0,0,e,t,function(){})};YA.prototype.draw=function(A,e,t,i,o,n){this.getPath(e,t,i,o,n).draw(A)};YA.prototype.drawPoints=function(A,e,t,i,o,n){this.forEachGlyph(e,t,i,o,n,function(r,g,s,a){r.drawPoints(A,g,s,a)})};YA.prototype.drawMetrics=function(A,e,t,i,o,n){this.forEachGlyph(e,t,i,o,n,function(r,g,s,a){r.drawMetrics(A,g,s,a)})};YA.prototype.getEnglishName=function(A){var e=this.names[A];if(e)return e.en};YA.prototype.validate=function(){var A=this;function e(i,o){}function t(i){var o=A.getEnglishName(i);o&&o.trim().length>0}t("fontFamily"),t("weightName"),t("manufacturer"),t("copyright"),t("version"),this.unitsPerEm>0};YA.prototype.toTables=function(){return qD.fontToTable(this)};YA.prototype.toBuffer=function(){return console.warn("Font.toBuffer is deprecated. Use Font.toArrayBuffer instead."),this.toArrayBuffer()};YA.prototype.toArrayBuffer=function(){for(var A=this.toTables(),e=A.encode(),t=new ArrayBuffer(e.length),i=new Uint8Array(t),o=0;o<e.length;o++)i[o]=e[o];return t};YA.prototype.download=function(A){var e=this.getEnglishName("fontFamily"),t=this.getEnglishName("fontSubfamily");A=A||e.replace(/\s/g,"")+"-"+t+".otf";var i=this.toArrayBuffer();if(ZD())if(window.URL=window.URL||window.webkitURL,window.URL){var o=new DataView(i),n=new Blob([o],{type:"font/opentype"}),r=document.createElement("a");r.href=window.URL.createObjectURL(n),r.download=A;var g=document.createEvent("MouseEvents");g.initEvent("click",!0,!1),r.dispatchEvent(g)}else console.warn("Font file could not be downloaded. Try using a different browser.");else{var s=require("fs"),a=XD(i);s.writeFileSync(A,a)}};YA.prototype.fsSelectionValues={ITALIC:1,UNDERSCORE:2,NEGATIVE:4,OUTLINED:8,STRIKEOUT:16,BOLD:32,REGULAR:64,USER_TYPO_METRICS:128,WWS:256,OBLIQUE:512};YA.prototype.usWidthClasses={ULTRA_CONDENSED:1,EXTRA_CONDENSED:2,CONDENSED:3,SEMI_CONDENSED:4,MEDIUM:5,SEMI_EXPANDED:6,EXPANDED:7,EXTRA_EXPANDED:8,ULTRA_EXPANDED:9};YA.prototype.usWeightClasses={THIN:100,EXTRA_LIGHT:200,LIGHT:300,NORMAL:400,MEDIUM:500,SEMI_BOLD:600,BOLD:700,EXTRA_BOLD:800,BLACK:900};function Ul(A,e){var t=JSON.stringify(A),i=256;for(var o in e){var n=parseInt(o);if(!(!n||n<256)){if(JSON.stringify(e[o])===t)return n;i<=n&&(i=n+1)}}return e[i]=A,i}function sF(A,e,t){var i=Ul(e.name,t);return[{name:"tag_"+A,type:"TAG",value:e.tag},{name:"minValue_"+A,type:"FIXED",value:e.minValue<<16},{name:"defaultValue_"+A,type:"FIXED",value:e.defaultValue<<16},{name:"maxValue_"+A,type:"FIXED",value:e.maxValue<<16},{name:"flags_"+A,type:"USHORT",value:0},{name:"nameID_"+A,type:"USHORT",value:i}]}function aF(A,e,t){var i={},o=new QA.Parser(A,e);return i.tag=o.parseTag(),i.minValue=o.parseFixed(),i.defaultValue=o.parseFixed(),i.maxValue=o.parseFixed(),o.skip("uShort",1),i.name=t[o.parseUShort()]||{},i}function IF(A,e,t,i){for(var o=Ul(e.name,i),n=[{name:"nameID_"+A,type:"USHORT",value:o},{name:"flags_"+A,type:"USHORT",value:0}],r=0;r<t.length;++r){var g=t[r].tag;n.push({name:"axis_"+A+" "+g,type:"FIXED",value:e.coordinates[g]<<16})}return n}function CF(A,e,t,i){var o={},n=new QA.Parser(A,e);o.name=i[n.parseUShort()]||{},n.skip("uShort",1),o.coordinates={};for(var r=0;r<t.length;++r)o.coordinates[t[r].tag]=n.parseFixed();return o}function lF(A,e){var t=new lA.Table("fvar",[{name:"version",type:"ULONG",value:65536},{name:"offsetToData",type:"USHORT",value:0},{name:"countSizePairs",type:"USHORT",value:2},{name:"axisCount",type:"USHORT",value:A.axes.length},{name:"axisSize",type:"USHORT",value:20},{name:"instanceCount",type:"USHORT",value:A.instances.length},{name:"instanceSize",type:"USHORT",value:4+A.axes.length*4}]);t.offsetToData=t.sizeOf();for(var i=0;i<A.axes.length;i++)t.fields=t.fields.concat(sF(i,A.axes[i],e));for(var o=0;o<A.instances.length;o++)t.fields=t.fields.concat(IF(o,A.instances[o],A.axes,e));return t}function BF(A,e,t){var i=new QA.Parser(A,e),o=i.parseULong();mA.argument(o===65536,"Unsupported fvar table version.");var n=i.parseOffset16();i.skip("uShort",1);for(var r=i.parseUShort(),g=i.parseUShort(),s=i.parseUShort(),a=i.parseUShort(),I=[],l=0;l<r;l++)I.push(aF(A,e+n+l*g,t));for(var C=[],Q=e+n+r*g,f=0;f<s;f++)C.push(CF(A,Q+f*a,I,t));return{axes:I,instances:C}}var QF={make:lF,parse:BF},cF=function(){return{coverage:this.parsePointer(J.coverage),attachPoints:this.parseList(J.pointer(J.uShortList))}},EF=function(){var A=this.parseUShort();if(mA.argument(A===1||A===2||A===3,"Unsupported CaretValue table version."),A===1)return{coordinate:this.parseShort()};if(A===2)return{pointindex:this.parseShort()};if(A===3)return{coordinate:this.parseShort()}},uF=function(){return this.parseList(J.pointer(EF))},fF=function(){return{coverage:this.parsePointer(J.coverage),ligGlyphs:this.parseList(J.pointer(uF))}},hF=function(){return this.parseUShort(),this.parseList(J.pointer(J.coverage))};function dF(A,e){e=e||0;var t=new J(A,e),i=t.parseVersion(1);mA.argument(i===1||i===1.2||i===1.3,"Unsupported GDEF table version.");var o={version:i,classDef:t.parsePointer(J.classDef),attachList:t.parsePointer(cF),ligCaretList:t.parsePointer(fF),markAttachClassDef:t.parsePointer(J.classDef)};return i>=1.2&&(o.markGlyphSets=t.parsePointer(hF)),o}var pF={parse:dF},We=new Array(10);We[1]=function(){var e=this.offset+this.relativeOffset,t=this.parseUShort();if(t===1)return{posFormat:1,coverage:this.parsePointer(J.coverage),value:this.parseValueRecord()};if(t===2)return{posFormat:2,coverage:this.parsePointer(J.coverage),values:this.parseValueRecordList()};mA.assert(!1,"0x"+e.toString(16)+": GPOS lookup type 1 format must be 1 or 2.")};We[2]=function(){var e=this.offset+this.relativeOffset,t=this.parseUShort();mA.assert(t===1||t===2,"0x"+e.toString(16)+": GPOS lookup type 2 format must be 1 or 2.");var i=this.parsePointer(J.coverage),o=this.parseUShort(),n=this.parseUShort();if(t===1)return{posFormat:t,coverage:i,valueFormat1:o,valueFormat2:n,pairSets:this.parseList(J.pointer(J.list(function(){return{secondGlyph:this.parseUShort(),value1:this.parseValueRecord(o),value2:this.parseValueRecord(n)}})))};if(t===2){var r=this.parsePointer(J.classDef),g=this.parsePointer(J.classDef),s=this.parseUShort(),a=this.parseUShort();return{posFormat:t,coverage:i,valueFormat1:o,valueFormat2:n,classDef1:r,classDef2:g,class1Count:s,class2Count:a,classRecords:this.parseList(s,J.list(a,function(){return{value1:this.parseValueRecord(o),value2:this.parseValueRecord(n)}}))}}};We[3]=function(){return{error:"GPOS Lookup 3 not supported"}};We[4]=function(){return{error:"GPOS Lookup 4 not supported"}};We[5]=function(){return{error:"GPOS Lookup 5 not supported"}};We[6]=function(){return{error:"GPOS Lookup 6 not supported"}};We[7]=function(){return{error:"GPOS Lookup 7 not supported"}};We[8]=function(){return{error:"GPOS Lookup 8 not supported"}};We[9]=function(){return{error:"GPOS Lookup 9 not supported"}};function mF(A,e){e=e||0;var t=new J(A,e),i=t.parseVersion(1);return mA.argument(i===1||i===1.1,"Unsupported GPOS table version "+i),i===1?{version:i,scripts:t.parseScriptList(),features:t.parseFeatureList(),lookups:t.parseLookupList(We)}:{version:i,scripts:t.parseScriptList(),features:t.parseFeatureList(),lookups:t.parseLookupList(We),variations:t.parseFeatureVariationsList()}}var yF=new Array(10);function wF(A){return new lA.Table("GPOS",[{name:"version",type:"ULONG",value:65536},{name:"scripts",type:"TABLE",value:new lA.ScriptList(A.scripts)},{name:"features",type:"TABLE",value:new lA.FeatureList(A.features)},{name:"lookups",type:"TABLE",value:new lA.LookupList(A.lookups,yF)}])}var DF={parse:mF,make:wF};function kF(A){var e={};A.skip("uShort");var t=A.parseUShort();mA.argument(t===0,"Unsupported kern sub-table version."),A.skip("uShort",2);var i=A.parseUShort();A.skip("uShort",3);for(var o=0;o<i;o+=1){var n=A.parseUShort(),r=A.parseUShort(),g=A.parseShort();e[n+","+r]=g}return e}function vF(A){var e={};A.skip("uShort");var t=A.parseULong();t>1&&console.warn("Only the first kern subtable is supported."),A.skip("uLong");var i=A.parseUShort(),o=i&255;if(A.skip("uShort"),o===0){var n=A.parseUShort();A.skip("uShort",3);for(var r=0;r<n;r+=1){var g=A.parseUShort(),s=A.parseUShort(),a=A.parseShort();e[g+","+s]=a}}return e}function FF(A,e){var t=new QA.Parser(A,e),i=t.parseUShort();if(i===0)return kF(t);if(i===1)return vF(t);throw new Error("Unsupported kern table version ("+i+").")}var SF={parse:FF};function RF(A,e,t,i){for(var o=new QA.Parser(A,e),n=i?o.parseUShort:o.parseULong,r=[],g=0;g<t+1;g+=1){var s=n.call(o);i&&(s*=2),r.push(s)}return r}var bF={parse:RF};function NF(A,e){var t=require("fs");t.readFile(A,function(i,o){if(i)return e(i.message);e(null,yl(o))})}function GF(A,e){var t=new XMLHttpRequest;t.open("get",A,!0),t.responseType="arraybuffer",t.onload=function(){return t.response?e(null,t.response):e("Font could not be loaded: "+t.statusText)},t.onerror=function(){e("Font could not be loaded")},t.send()}function xa(A,e){for(var t=[],i=12,o=0;o<e;o+=1){var n=QA.getTag(A,i),r=QA.getULong(A,i+4),g=QA.getULong(A,i+8),s=QA.getULong(A,i+12);t.push({tag:n,checksum:r,offset:g,length:s,compression:!1}),i+=16}return t}function LF(A,e){for(var t=[],i=44,o=0;o<e;o+=1){var n=QA.getTag(A,i),r=QA.getULong(A,i+4),g=QA.getULong(A,i+8),s=QA.getULong(A,i+12),a=void 0;g<s?a="WOFF":a=!1,t.push({tag:n,offset:r,compression:a,compressedLength:g,length:s}),i+=20}return t}function $A(A,e){if(e.compression==="WOFF"){var t=new Uint8Array(A.buffer,e.offset+2,e.compressedLength-2),i=new Uint8Array(e.length);if(Dw(t,i),i.byteLength!==e.length)throw new Error("Decompression error: "+e.tag+" decompressed length doesn't match recorded length");var o=new DataView(i.buffer,0);return{data:o,offset:0}}else return{data:A,offset:e.offset}}function vg(A,e){e=e??{};var t,i,o=new YA({empty:!0}),n=new DataView(A,0),r,g=[],s=QA.getTag(n,0);if(s==="\0\0\0"||s==="true"||s==="typ1")o.outlinesFormat="truetype",r=QA.getUShort(n,4),g=xa(n,r);else if(s==="OTTO")o.outlinesFormat="cff",r=QA.getUShort(n,4),g=xa(n,r);else if(s==="wOFF"){var a=QA.getTag(n,4);if(a==="\0\0\0")o.outlinesFormat="truetype";else if(a==="OTTO")o.outlinesFormat="cff";else throw new Error("Unsupported OpenType flavor "+s);r=QA.getUShort(n,12),g=LF(n,r)}else throw new Error("Unsupported OpenType signature "+s);for(var I,l,C,Q,f,h,u,E,D,c,p,d,v=0;v<r;v+=1){var b=g[v],N=void 0;switch(b.tag){case"cmap":N=$A(n,b),o.tables.cmap=XC.parse(N.data,N.offset),o.encoding=new $C(o.tables.cmap);break;case"cvt ":N=$A(n,b),d=new QA.Parser(N.data,N.offset),o.tables.cvt=d.parseShortList(b.length/2);break;case"fvar":l=b;break;case"fpgm":N=$A(n,b),d=new QA.Parser(N.data,N.offset),o.tables.fpgm=d.parseByteList(b.length);break;case"head":N=$A(n,b),o.tables.head=al.parse(N.data,N.offset),o.unitsPerEm=o.tables.head.unitsPerEm,t=o.tables.head.indexToLocFormat;break;case"hhea":N=$A(n,b),o.tables.hhea=Il.parse(N.data,N.offset),o.ascender=o.tables.hhea.ascender,o.descender=o.tables.hhea.descender,o.numberOfHMetrics=o.tables.hhea.numberOfHMetrics;break;case"hmtx":u=b;break;case"ltag":N=$A(n,b),i=ll.parse(N.data,N.offset);break;case"maxp":N=$A(n,b),o.tables.maxp=Bl.parse(N.data,N.offset),o.numGlyphs=o.tables.maxp.numGlyphs;break;case"name":c=b;break;case"OS/2":N=$A(n,b),o.tables.os2=Tr.parse(N.data,N.offset);break;case"post":N=$A(n,b),o.tables.post=hl.parse(N.data,N.offset),o.glyphNames=new dg(o.tables.post);break;case"prep":N=$A(n,b),d=new QA.Parser(N.data,N.offset),o.tables.prep=d.parseByteList(b.length);break;case"glyf":C=b;break;case"loca":D=b;break;case"CFF ":I=b;break;case"kern":E=b;break;case"GDEF":Q=b;break;case"GPOS":f=b;break;case"GSUB":h=b;break;case"meta":p=b;break}}var H=$A(n,c);if(o.tables.name=fl.parse(H.data,H.offset,i),o.names=o.tables.name,C&&D){var M=t===0,U=$A(n,D),R=bF.parse(U.data,U.offset,o.numGlyphs,M),j=$A(n,C);o.glyphs=vl.parse(j.data,j.offset,R,o,e)}else if(I){var rA=$A(n,I);sl.parse(rA.data,rA.offset,o,e)}else throw new Error("Font doesn't contain TrueType or CFF outlines.");var IA=$A(n,u);if(Cl.parse(o,IA.data,IA.offset,o.numberOfHMetrics,o.numGlyphs,o.glyphs,e),Vw(o,e),E){var AA=$A(n,E);o.kerningPairs=SF.parse(AA.data,AA.offset)}else o.kerningPairs={};if(Q){var z=$A(n,Q);o.tables.gdef=pF.parse(z.data,z.offset)}if(f){var oA=$A(n,f);o.tables.gpos=DF.parse(oA.data,oA.offset),o.position.init()}if(h){var dA=$A(n,h);o.tables.gsub=dl.parse(dA.data,dA.offset)}if(l){var Y=$A(n,l);o.tables.fvar=QF.parse(Y.data,Y.offset,o.names)}if(p){var W=$A(n,p);o.tables.meta=pl.parse(W.data,W.offset),o.metas=o.tables.meta}return o}function UF(A,e,t){t=t??{};var i=typeof window>"u",o=i&&!t.isUrl?NF:GF;return new Promise(function(n,r){o(A,function(g,s){if(g){if(e)return e(g);r(g)}var a;try{a=vg(s,t)}catch(I){if(e)return e(I,null);r(I)}if(e)return e(null,a);n(a)})})}function MF(A,e){var t=require("fs"),i=t.readFileSync(A);return vg(yl(i),e)}var Yr=Object.freeze({__proto__:null,Font:YA,Glyph:Ne,Path:ie,BoundingBox:St,_parse:QA,parse:vg,load:UF,loadSync:MF});const xF="modulepreload",JF=function(A,e){return new URL(A,e).href},Ja={},Ml=function(e,t,i){let o=Promise.resolve();if(t&&t.length>0){const r=document.getElementsByTagName("link"),g=document.querySelector("meta[property=csp-nonce]"),s=(g==null?void 0:g.nonce)||(g==null?void 0:g.getAttribute("nonce"));o=Promise.allSettled(t.map(a=>{if(a=JF(a,i),a in Ja)return;Ja[a]=!0;const I=a.endsWith(".css"),l=I?'[rel="stylesheet"]':"";if(!!i)for(let f=r.length-1;f>=0;f--){const h=r[f];if(h.href===a&&(!I||h.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${a}"]${l}`))return;const Q=document.createElement("link");if(Q.rel=I?"stylesheet":xF,I||(Q.as="script"),Q.crossOrigin="",Q.href=a,s&&Q.setAttribute("nonce",s),document.head.appendChild(Q),I)return new Promise((f,h)=>{Q.addEventListener("load",f),Q.addEventListener("error",()=>h(new Error(`Unable to preload CSS for ${a}`)))})}))}function n(r){const g=new Event("vite:preloadError",{cancelable:!0});if(g.payload=r,window.dispatchEvent(g),!g.defaultPrevented)throw r}return o.then(r=>{for(const g of r||[])g.status==="rejected"&&n(g.reason);return e().catch(n)})},TF=()=>Ml(()=>import("./ft_render-ByO_jG18.js"),[],import.meta.url);let Z=null,Bo=0;const HF=new Promise(A=>{TF().then(e=>{e.default().then(t=>{Z=t,A()})})});function Ta(A){return A/65536}function Ue(A){return A/64}function Hi(A){return A>=0?A:A+256}let xl,Jl,Tl,Fg,Hl,Yl,_l;async function YF(){if(await HF,Z._init_constants(),xl=Z.cwrap("FT_New_Memory_Face","number",["number","number","number","number","number"]),Jl=Z.cwrap("FT_Set_Char_Size","number",["number","number","number","number","number"]),Tl=Z.cwrap("FT_Set_Pixel_Sizes","number",["number","number","number"]),Fg=Z.cwrap("FT_Get_Char_Index","number",["number","number"]),Hl=Z.cwrap("FT_Load_Glyph","number",["number","number","number"]),Yl=Z.cwrap("FT_Get_Sfnt_Table","number",["number","number"]),Z.cwrap("FT_Get_Kerning","number",["number","number","number","number","number"]),_l=Z.cwrap("FT_Done_Face","number",["number"]),!Bo){let A=Z._malloc(4);try{let e=Z.ccall("FT_Init_FreeType","number",["number"],[A]);if(e)throw new Error(`error in FT_Init_FreeType: ${e}`);Bo=Z.getValue(A,"i32")}finally{Z._free(A)}}}function _F(A,e){let t,i;if(A instanceof ArrayBuffer)i=A.byteLength;else if(A instanceof Uint8Array)i=A.byteLength;else if(A&&typeof A.length=="number")i=A.length;else throw new Error("Invalid font source format");let o={ptr:0,font:Z._malloc(i)};const n=A instanceof ArrayBuffer?new Uint8Array(A):A;Z.writeArrayToMemory(n,o.font);let r=Z._malloc(4);try{if(t=xl(Bo,o.font,i,0,r),t)throw new Error(`error in FT_New_Memory_Face: ${t}`);o.ptr=Z.getValue(r,"i32")}finally{Z._free(r)}if(t=Jl(o.ptr,0,e*64,300,300),t)throw new Error(`error in FT_Set_Char_Size: ${t}`);if(t=Tl(o.ptr,0,e),t)throw new Error(`error in FT_Set_Pixel_Sizes: ${t}`);let g=Z.getValue(o.ptr+Z.OFFSET_FACE_UNITS_PER_EM,"i16"),s=Z.getValue(o.ptr+Z.OFFSET_FACE_ASCENDER,"i16"),a=Z.getValue(o.ptr+Z.OFFSET_FACE_DESCENDER,"i16"),I=Z.getValue(o.ptr+Z.OFFSET_FACE_HEIGHT,"i16");return Object.assign(o,{units_per_em:g,ascender:s,descender:a,height:I})}function KF(A){let e=Yl(A.ptr,Z.FT_SFNT_OS2);if(!e)throw new Error("os/2 table not found for this font");let t=Z.getValue(e+Z.OFFSET_TT_OS2_ASCENDER,"i16"),i=Z.getValue(e+Z.OFFSET_TT_OS2_DESCENDER,"i16"),o=Z.getValue(e+Z.OFFSET_TT_OS2_LINEGAP,"i16");return{typoAscent:t,typoDescent:i,typoLineGap:o}}function or(A,e){return Fg(A.ptr,e)!==0}function OF(A,e,t={}){let i=Fg(A.ptr,e);if(i===0)throw new Error(`glyph does not exist for codepoint ${e}`);let o=Z.FT_LOAD_RENDER;t.mono?o|=Z.FT_LOAD_TARGET_MONO:t.lcd?o|=Z.FT_LOAD_TARGET_LCD:t.lcd_v?o|=Z.FT_LOAD_TARGET_LCD_V:t.autohint_strong?o|=Z.FT_LOAD_TARGET_NORMAL:o|=Z.FT_LOAD_TARGET_LIGHT,t.autohint_off?o|=Z.FT_LOAD_NO_AUTOHINT:o|=Z.FT_LOAD_FORCE_AUTOHINT,t.use_color_info&&(o|=Z.FT_LOAD_COLOR);let n=Hl(A.ptr,i,o);if(n)throw new Error(`error in FT_Load_Glyph: ${n}`);let r=Z.getValue(A.ptr+Z.OFFSET_FACE_GLYPH,"i32"),g={glyph_index:Z.getValue(r+Z.OFFSET_GLYPH_INDEX,"i32"),metrics:{width:Ue(Z.getValue(r+Z.OFFSET_GLYPH_METRICS_WIDTH,"i32")),height:Ue(Z.getValue(r+Z.OFFSET_GLYPH_METRICS_HEIGHT,"i32")),horiBearingX:Ue(Z.getValue(r+Z.OFFSET_GLYPH_METRICS_HORI_BEARING_X,"i32")),horiBearingY:Ue(Z.getValue(r+Z.OFFSET_GLYPH_METRICS_HORI_BEARING_Y,"i32")),horiAdvance:Ue(Z.getValue(r+Z.OFFSET_GLYPH_METRICS_HORI_ADVANCE,"i32")),vertBearingX:Ue(Z.getValue(r+Z.OFFSET_GLYPH_METRICS_VERT_BEARING_X,"i32")),vertBearingY:Ue(Z.getValue(r+Z.OFFSET_GLYPH_METRICS_VERT_BEARING_Y,"i32")),vertAdvance:Ue(Z.getValue(r+Z.OFFSET_GLYPH_METRICS_VERT_ADVANCE,"i32"))},linearHoriAdvance:Ta(Z.getValue(r+Z.OFFSET_GLYPH_LINEAR_HORI_ADVANCE,"i32")),linearVertAdvance:Ta(Z.getValue(r+Z.OFFSET_GLYPH_LINEAR_VERT_ADVANCE,"i32")),advance:{x:Ue(Z.getValue(r+Z.OFFSET_GLYPH_ADVANCE_X,"i32")),y:Ue(Z.getValue(r+Z.OFFSET_GLYPH_ADVANCE_Y,"i32"))},bitmap:{width:Z.getValue(r+Z.OFFSET_GLYPH_BITMAP_WIDTH,"i32"),rows:Z.getValue(r+Z.OFFSET_GLYPH_BITMAP_ROWS,"i32"),pitch:Z.getValue(r+Z.OFFSET_GLYPH_BITMAP_PITCH,"i32"),num_grays:Z.getValue(r+Z.OFFSET_GLYPH_BITMAP_NUM_GRAYS,"i16"),pixel_mode:Z.getValue(r+Z.OFFSET_GLYPH_BITMAP_PIXEL_MODE,"i8"),palette_mode:Z.getValue(r+Z.OFFSET_GLYPH_BITMAP_PALETTE_MODE,"i8")},bitmap_left:Z.getValue(r+Z.OFFSET_GLYPH_BITMAP_LEFT,"i32"),bitmap_top:Z.getValue(r+Z.OFFSET_GLYPH_BITMAP_TOP,"i32"),lsb_delta:Ue(Z.getValue(r+Z.OFFSET_GLYPH_LSB_DELTA,"i32")),rsb_delta:Ue(Z.getValue(r+Z.OFFSET_GLYPH_RSB_DELTA,"i32"))},s=g.bitmap.width,a=g.bitmap.rows,I=g.bitmap_left,l=g.bitmap_top,C=Z.getValue(r+Z.OFFSET_GLYPH_BITMAP_BUFFER,"i32"),Q=Math.abs(g.bitmap.pitch),f=g.linearHoriAdvance,h=g.linearVertAdvance,u=g.bitmap.pixel_mode,E=[];for(let D=0;D<a;D++){let c=C+D*Q,p=[];for(let d=0;d<s;d++)if(u===Z.FT_PIXEL_MODE_MONO){let v=Z.getValue(c+~~(d/8),"i8");p.push(v&1<<7-d%8?255:0)}else if(u===Z.FT_PIXEL_MODE_BGRA){let v=Hi(Z.getValue(c+d*4+0,"i8")),b=Hi(Z.getValue(c+d*4+1,"i8")),N=Hi(Z.getValue(c+d*4+2,"i8")),H=Hi(Z.getValue(c+d*4+3,"i8")),M=Math.round(.299*N+.587*b+.114*v);M>255&&(M=255),H=(255-M)*H/255,p.push(H)}else{let v=Z.getValue(c+d,"i8");p.push(Hi(v))}E.push(p)}return{x:I,y:l,width:s,height:a,advance_x:f,advance_y:h,pixels:E,freetype:g}}function PF(A){let e=_l(A.ptr);if(e)throw new Error(`error in FT_Done_Face: ${e}`);Z._free(A.font),A.ptr=0,A.font=0}function VF(){let A=Z.ccall("FT_Done_FreeType","number",["number"],[Bo]);if(A)throw new Error(`error in FT_Done_FreeType: ${A}`);Bo=0}class yt extends Error{constructor(e){super(e),this.name="AppError",Error.captureStackTrace&&Error.captureStackTrace(this,yt)}}class WF{constructor(){this.data={}}add_range(e,t,i,o){let n=o-t,r=[];for(let g=t;g<=i;g++)this._set_char(e,g,g+n),r.push(g);return r}add_symbols(e,t){let i=[];for(let o of t){let n=o.codePointAt(0);this._set_char(e,n,n),i.push(n)}return i}_set_char(e,t,i){this.data[i]={font:e,code:t}}get(){return this.data}}async function qF(A){await YF();let e={};A.font.forEach(C=>{e[C.source_path]=C});let t={},i={};for(let{source_path:C,source_bin:Q}of A.font)if(!t[C])try{let f=Q;f instanceof File?f=await f.arrayBuffer():f instanceof Uint8Array&&(f=f.buffer.slice(f.byteOffset,f.byteOffset+f.byteLength)),t[C]=Yr.parse(f),i[C]=_F(f,A.size)}catch(f){throw new yt(`Cannot load font "${C}": ${f.message}`)}let o=new WF;for(let{source_path:C,ranges:Q}of A.font){let f=i[C];for(let h of Q){if(h.range)for(let u=0;u<h.range.length;u+=3){let E=h.range.slice(u,u+3),D=o.add_range(C,...E),c=!0;for(let p of D)if(or(f,p)){c=!1;break}if(c){let p="0x"+E[0].toString(16),d="0x"+E[1].toString(16);throw new yt(`Font "${C}" doesn't have any characters included in range ${p}-${d}`)}}if(h.symbols){let u=o.add_symbols(C,h.symbols),E=!0;for(let D of u)if(or(f,D)){E=!1;break}if(E)throw new yt(`Font "${C}" doesn't have any characters included in "${h.symbols}"`)}}}let n=o.get(),r=[],g=Object.keys(n).sort((C,Q)=>C-Q).map(Number);for(let C of g){let Q=n[C].code,f=n[C].font;if(!or(i[f],Q))continue;let h=OF(i[f],Q,{autohint_off:e[f].autohint_off,autohint_strong:e[f].autohint_strong,lcd:A.lcd,lcd_v:A.lcd_v,mono:!A.lcd&&!A.lcd_v&&A.bpp===1,use_color_info:A.use_color_info});r.push({code:C,advanceWidth:h.advance_x,bbox:{x:h.x,y:h.y-h.height,width:h.width,height:h.height},kerning:{},freetype:h.freetype,pixels:h.pixels})}if(!A.no_kerning){let C=r.map(Q=>Q.code);for(let{code:Q,kerning:f}of r){let h=n[Q].code,u=n[Q].font,E=t[u],D=E.charToGlyph(String.fromCodePoint(h));for(let c of C){if(n[c].font!==u)continue;let p=n[c].code,d=E.charToGlyph(String.fromCodePoint(p)),v=E.getKerningValue(D,d);v&&(f[c]=v*A.size/E.unitsPerEm)}}}let s=i[A.font[0].source_path],a=A.size/s.units_per_em,I=KF(s),l=t[A.font[0].source_path].tables.post;for(let C of Object.values(i))PF(C);return VF(),{ascent:Math.max(...r.map(C=>C.bbox.y+C.bbox.height)),descent:Math.min(...r.map(C=>C.bbox.y)),typoAscent:Math.round(I.typoAscent*a),typoDescent:Math.round(I.typoDescent*a),typoLineGap:Math.round(I.typoLineGap*a),size:A.size,glyphs:r,underlinePosition:Math.round(l.underlinePosition*a),underlineThickness:Math.round(l.underlineThickness*a)}}function jF(){return 16}function ZF(A,e){return 16+(e-A+1)}function XF(A){return 16+A*2}function Ha(A){A=A.sort((i,o)=>i-o);let e=[];for(let i=0;i<A.length;i++){let o={dist:1/0};for(let n=0;n<=i;n++){let r=n-1>=0?e[n-1].dist:0,g;A[i]-A[n]<256&&(g=ZF(A[n],A[i]),r+g<o.dist&&(o={dist:r+g,start:n,end:i,format:"format0"})),A[i]-A[n]<256&&A[i]-i===A[n]-n&&(g=jF(A[n],A[i]),r+g<o.dist&&(o={dist:r+g,start:n,end:i,format:"format0_tiny"})),A[i]-A[n]<65536&&(g=XF(i-n+1),r+g<o.dist&&(o={dist:r+g,start:n,end:i,format:"sparse_tiny"}))}e[i]=o}let t=[];for(let i=A.length-1;i>=0;){let o=e[i];t.unshift([o.format,A.slice(o.start,o.end+1)]),i=o.start-1}return t}const ke=4,zF=(A,e,t)=>{A.setUint32(t,e,!0),A.setUint32(t+4,0,!0)};class $F{constructor(e,t){if(this.src=e,this.opts=t,this.font_name=t.lv_font_name,this.font_name||(this.font_name=t.output||"font"),t.bpp===3&&t.no_compress)throw new yt('LVGL supports "--bpp 3" with compression only');this.init_tables()}init_tables(){this.head=new AS(this),this.glyf=new tS(this),this.cmap=new iS(this),this.kern=new oS(this)}toCBin(){const[e,t]=this.glyf.toCBin(),i=this.cmap.toCBin(ke),o=this.kern.toCBin(ke),n=12+ke*6+0,r=8+ke*4,g=new ArrayBuffer(n+r),s=new DataView(g),a=(u,E)=>s.setUint32(E,u,!0);var I=0;a(0,I),I+=ke,a(0,I),I+=ke,a(0,I),I+=ke,s.setUint32(I,this.src.ascent-this.src.descent,!0),I+=4,s.setInt32(I,-this.src.descent,!0),I+=4,s.setUint8(I,this.src.subpixels_mode||0),I+=1,s.setInt8(I,this.src.underlinePosition||0),I+=1,s.setInt8(I,this.src.underlineThickness||0),I+=1,I+=1,a(n,I),I+=ke,a(0,I),I+=ke,a(0,I),I+=ke;const l=this.head.kern_ref();a(r+o.byteLength,I),I+=ke,a(r+o.byteLength+e.byteLength,I),I+=ke,a(r+o.byteLength+e.byteLength+t.byteLength,I),I+=ke,a(l.dsc==="NULL"?0:r,I),I+=ke,s.setUint16(I,l.scale,!0),I+=2;const C=this.cmap.getMapNumber(),Q=this.opts.bpp,f=l.classes,h=this.glyf.getCompressionCode();return s.setUint16(I,C|Q<<9|f<<13|h<<14,!0),I+=2,this.concatArrayBuffers([g,o,e,t,i])}concatArrayBuffers(e){const t=e.reduce((r,g)=>r+g.byteLength,0),i=new ArrayBuffer(t),o=new Uint8Array(i);let n=0;for(const r of e)o.set(new Uint8Array(r),n),n+=r.byteLength;return i}}class AS{constructor(e){this.font=e}kern_ref(){return this.font,{scale:0,dsc:"NULL",classes:0}}}class eS{constructor(e){this.buffer=new Uint8Array(e),this.byteIndex=0,this.bitIndex=0,this.bigEndian=!0}writeBits(e,t){for(;t>0;){const i=Math.min(8-this.bitIndex,t),o=(1<<i)-1,n=e>>t-i&o;this.bigEndian?this.buffer[this.byteIndex]|=n<<8-this.bitIndex-i:this.buffer[this.byteIndex]|=n<<this.bitIndex,this.bitIndex+=i,this.bitIndex>=8&&(this.byteIndex++,this.bitIndex=0),t-=i}}getUsedBytes(){return this.bitIndex>0?this.byteIndex+1:this.byteIndex}}class tS{constructor(e){this.font=e,this.lv_data=[],this.lv_compiled=!1}pixelsToBpp(e){const t=this.font.opts.bpp;return e.map(i=>i.map(o=>o>>>8-t))}storePixels(e,t){this.getCompressionCode()===0?this.storePixelsRaw(e,t):this.storePixelsCompressed(e,t)}storePixelsRaw(e,t){const i=this.font.opts.bpp;for(let o=0;o<t.length;o++){const n=t[o];for(let r=0;r<n.length;r++)e.writeBits(n[r],i)}}storePixelsCompressed(e,t){this.storePixelsRaw(e,t)}lv_bitmap(e){const t=100+e.bbox.width*e.bbox.height*4,i=new ArrayBuffer(t),o=new eS(i);o.bigEndian=!0;const n=this.pixelsToBpp(e.pixels);this.storePixels(o,n);const r=new ArrayBuffer(o.getUsedBytes()),g=new Uint8Array(i);return new Uint8Array(r).set(g.subarray(0,o.getUsedBytes())),r}lv_compile(){if(this.lv_compiled)return;this.lv_compiled=!0;const e=this.font;this.lv_data=[];let t=0;e.src.glyphs.forEach(i=>{const o=e.glyph_id[i.code],n=this.lv_bitmap(i);this.lv_data[o]={bin:n,offset:t,glyph:i},t+=n.byteLength})}toCBin(){this.lv_compile();const e=this.lv_data.slice(1).filter(r=>r).map(r=>r.bin),t=this.balign4(this.concatArrayBuffers(e)),i=new ArrayBuffer(this.lv_data.length*16+16),o=new DataView(i);let n=1;return this.lv_data.forEach(r=>{if(r){const g=Math.round(r.glyph.advanceWidth*16);o.setUint32(n*16,r.offset,!0),o.setUint32(n*16+4,g,!0),o.setUint16(n*16+8,r.glyph.bbox.width,!0),o.setUint16(n*16+10,r.glyph.bbox.height,!0),o.setInt16(n*16+12,r.glyph.bbox.x,!0),o.setInt16(n*16+14,r.glyph.bbox.y,!0)}n++}),[t,i]}balign4(e){const t=e.byteLength%4;if(t===0)return e;const i=4-t,o=new ArrayBuffer(i);return this.concatArrayBuffers([e,o])}concatArrayBuffers(e){const t=e.reduce((r,g)=>r+g.byteLength,0),i=new ArrayBuffer(t),o=new Uint8Array(i);let n=0;for(const r of e)o.set(new Uint8Array(r),n),n+=r.byteLength;return i}getCompressionCode(){return this.font.opts.no_compress||this.font.opts.bpp===1?0:this.font.opts.no_prefilter?2:1}}class iS{constructor(e){this.font=e,this.lv_compiled=!1,this.lv_subtables=[],this.subtables_plan=null,this.buildGlyphIdMap()}buildGlyphIdMap(){const e=this.font;e.glyph_id={},e.src.glyphs&&e.src.glyphs.forEach((t,i)=>{e.glyph_id[t.code]=i+1})}lv_format2enum(e){switch(e){case"format0_tiny":return"LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY";case"format0":return"LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL";case"sparse_tiny":return"LV_FONT_FMT_TXT_CMAP_SPARSE_TINY";case"sparse":return"LV_FONT_FMT_TXT_CMAP_SPARSE_FULL";default:throw new Error("Unknown subtable format")}}lv_format2int(e){return["format0","sparse","format0_tiny","sparse_tiny"].indexOf(e)}getMapNumber(){if(!this.subtables_plan){const e=this.font;if(!e.src.glyphs||e.src.glyphs.length===0)return 0;this.subtables_plan=Ha(e.src.glyphs.map(t=>t.code))}return this.subtables_plan.length}glyphByCode(e){const t=this.font;return t.src.glyphs?t.src.glyphs.find(i=>i.code===e):null}collect_format0_data(e,t,i){const o=this.font,n=[];for(let r=e;r<=t;r++){const g=o.glyph_id[r]||0;n.push(g?g-i:0)}return n}collect_sparse_data(e,t){const i=this.font;let o=[],n=[];for(let r of e){let g=this.glyphByCode(r),s=i.glyph_id[g.code],a=r-e[0],I=s-t;if(a<0||a>65535)throw new Error("Codepoint delta out of range");if(I<0||I>65535)throw new Error("Glyph ID delta out of range");o.push(a),n.push(I)}return{codes:o,ids:n}}toCBin(e){const t=this.font;if(!t.src.glyphs||t.src.glyphs.length===0)return new ArrayBuffer(0);this.subtables_plan||(this.subtables_plan=Ha(t.src.glyphs.map(l=>l.code)));const i=this.subtables_plan;let o=0;const n=12+e*2+(e==4?0:4),r=new ArrayBuffer(i.length*n),g=new DataView(r),s=e==4?(l,C)=>g.setUint32(C,l,!0):(l,C)=>zF(g,l,C),a=[];let I=r.byteLength;for(let[l,C]of i){let Q=this.glyphByCode(C[0]),f=t.glyph_id[Q.code],h=C[0],u=C[C.length-1],E=!1,D=!1,c=0,p=new ArrayBuffer(0),d=new ArrayBuffer(0);if(l!=="format0_tiny")if(l==="format0"){D=!0;let b=this.collect_format0_data(h,u,f);c=b.length,p=this.balign4(this.uint8ArrayToBuffer(new Uint8Array(b)))}else if(l==="sparse_tiny"){E=!0;let b=this.collect_sparse_data(C,f);c=b.codes.length,d=this.balign4(this.uint16ArrayToBuffer(new Uint16Array(b.codes)))}else{E=!0,D=!0;let b=this.collect_sparse_data(C,f);c=b.codes.length,d=this.balign4(this.uint16ArrayToBuffer(new Uint16Array(b.codes))),p=this.align4(this.uint16ArrayToBuffer(new Uint16Array(b.ids)))}let v=o*n;g.setUint32(v,h,!0),v+=4,g.setUint16(v,u-h+1,!0),v+=2,g.setUint16(v,f,!0),v+=2,s(E?I:0,v),v+=e,I+=d.byteLength,s(D?I:0,v),v+=e,I+=p.byteLength,g.setUint16(v,c,!0),v+=2,g.setUint8(v,this.lv_format2int(l)),v+=1,a.push(d),a.push(p),o++}return this.concatArrayBuffers([r,...a])}balign4(e){const t=e.byteLength%4;if(t===0)return e;const i=4-t,o=new ArrayBuffer(i);return this.concatArrayBuffers([e,o])}align4(e){const t=e.byteLength%4;if(t===0)return e;const i=4-t,o=new ArrayBuffer(i);return this.concatArrayBuffers([e,o])}uint8ArrayToBuffer(e){const t=new ArrayBuffer(e.byteLength);return new Uint8Array(t).set(e),t}uint16ArrayToBuffer(e){return e.buffer.slice(0,e.byteLength)}concatArrayBuffers(e){const t=e.reduce((r,g)=>r+g.byteLength,0),i=new ArrayBuffer(t),o=new Uint8Array(i);let n=0;for(const r of e)o.set(new Uint8Array(r),n),n+=r.byteLength;return i}}class oS{constructor(e){this.font=e}toCBin(e){return new ArrayBuffer(0)}}function nS(A,e){if(!A.output)throw new yt('Output is required for "cbin" writer');const i=new $F(e,A).toCBin();return{[A.output]:i}}class rS{constructor(){this.initialized=!1,this.supportedFormats=["ttf","woff","woff2","otf"],this.charsetCache=new Map}async initialize(){if(!this.initialized)try{if(typeof Yr>"u")throw new Error("opentype.js not loaded");this.initialized=!0,console.log("BrowserFontConverter 鍒濆鍖栧畬鎴�")}catch(e){throw console.error("BrowserFontConverter initialization failed:",e),e}}validateFont(e){if(!e)return!1;if(e instanceof File){const t=e.name.toLowerCase(),i=e.type.toLowerCase(),o=this.supportedFormats.some(r=>t.endsWith(`.${r}`)),n=["font/ttf","font/truetype","application/x-font-ttf","font/woff","font/woff2","application/font-woff","font/otf","application/x-font-otf"].some(r=>i.includes(r));return o||n}return e instanceof ArrayBuffer&&e.byteLength>0}async getFontInfo(e){try{let t;if(e instanceof File)t=await e.arrayBuffer();else if(e instanceof ArrayBuffer)t=e;else throw new Error("Unsupported font file type");const i=Yr.parse(t);return{familyName:this.getLocalizedName(i.names.fontFamily)||"Unknown",fullName:this.getLocalizedName(i.names.fullName)||"Unknown",postScriptName:this.getLocalizedName(i.names.postScriptName)||"Unknown",version:this.getLocalizedName(i.names.version)||"Unknown",unitsPerEm:i.unitsPerEm,ascender:i.ascender,descender:i.descender,numGlyphs:i.numGlyphs,supported:!0}}catch(t){return console.error("Failed to get font information:",t),{familyName:"Unknown",supported:!1,error:t.message}}}getLocalizedName(e){return e?e.zh||e["zh-CN"]||e.en||e[Object.keys(e)[0]]:null}async convertToCBIN(e){this.initialized||await this.initialize();const{fontFile:t,fontName:i,fontSize:o=20,bpp:n=4,charset:r="deepseek",symbols:g="",range:s="",compression:a=!1,lcd:I=!1,lcd_v:l=!1,progressCallback:C=null}=e;if(!this.validateFont(t))throw new yt("涓嶆敮鎸佺殑瀛椾綋鏂囦欢鏍煎紡");try{C&&C(0,"Starting font processing...");let Q;t instanceof File?Q=await t.arrayBuffer():Q=t,C&&C(10,"Parsing font structure...");const{ranges:f,charSymbols:h}=await this.parseCharacterInputAsync(r,g,s);C&&C(20,"Preparing conversion parameters...");const u={font:[{source_path:i||"custom_font",source_bin:Q,ranges:[{range:f,symbols:h}],autohint_off:!1,autohint_strong:!1}],size:o,bpp:n,lcd:I,lcd_v:l,no_compress:!a,no_kerning:!1,use_color_info:!1,format:"cbin",output:i||"font"};C&&C(30,"Collecting font data...");const E=await qF(u);C&&C(70,"Generating CBIN format...");const D=nS(u,E),c=u.output;return C&&C(100,"Conversion completed!"),D[c]}catch(Q){throw console.error("Font conversion failed:",Q),new yt(`Font conversion failed: ${Q.message}`)}}async parseCharacterInputAsync(e,t,i){let o=[],n=t||"";return e==="full"?(o=[0,65535,0],{ranges:o,charSymbols:n}):(e&&e!=="custom"&&(n=await this.getCharsetContentAsync(e)+n),i&&(o=this.parseUnicodeRange(i)),{ranges:o,charSymbols:n})}parseCharacterInput(e,t,i){let o=[],n=t||"";return e==="full"?(o=[0,65535,0],{ranges:o,charSymbols:n}):(e&&e!=="custom"&&(n=this.getCharsetContent(e)+n),i&&(o=this.parseUnicodeRange(i)),{ranges:o,charSymbols:n})}async loadCharsetFromFile(e){const i={latin:"./static/charsets/latin1.txt",deepseek:"./static/charsets/deepseek.txt",gb2312:"./static/charsets/gb2312.txt",qwen:"./static/charsets/qwen18409.txt"}[e];if(!i)return null;try{const o=await fetch(i);if(!o.ok)throw new Error(`Failed to load charset file: ${o.status}`);const r=(await o.text()).split(`
+`).join("");return this.charsetCache.set(e,r),r}catch(o){return console.error(`Failed to load charset ${e}:`,o),null}}getCharsetContent(e){const t={};return(e==="latin"||e==="deepseek"||e==="gb2312")&&this.charsetCache.has(e)?this.charsetCache.get(e):e==="basic"?this.getCharsetContent("latin"):t[e]||""}async getCharsetContentAsync(e){if(e==="basic"&&(e="latin"),this.charsetCache.has(e))return this.charsetCache.get(e);if(e==="latin"||e==="deepseek"||e==="gb2312"||e==="qwen"){const t=await this.loadCharsetFromFile(e);if(t)return t}return this.getCharsetContent(e)}parseUnicodeRange(e){const t=[],i=e.split(",");for(const o of i){const n=o.trim();if(n)if(n.includes("-")){const[r,g]=n.split("-"),s=this.parseHexOrDec(r),a=this.parseHexOrDec(g);s!==null&&a!==null&&t.push(s,a,s)}else{const r=this.parseHexOrDec(n);r!==null&&t.push(r,r,r)}}return t}parseHexOrDec(e){const t=e.trim();if(t.startsWith("0x")||t.startsWith("0X")){const o=parseInt(t,16);return isNaN(o)?null:o}const i=parseInt(t,10);return isNaN(i)?null:i}async estimateSizeAsync(e){const{fontSize:t=20,bpp:i=4,charset:o="latin",symbols:n="",range:r=""}=e;let g=n.length;if(o==="full")g=65536;else if(o&&o!=="custom"){const I=await this.getCharsetContentAsync(o);g+=I.length}if(r){const I=this.parseUnicodeRange(r);for(let l=0;l<I.length;l+=3)g+=I[l+1]-I[l]+1}o!=="full"&&(g=Math.min(g,g*.8));const s=Math.ceil(t*t*i/8)+40,a=g*s+2048;return{characterCount:Math.floor(g),avgBytesPerChar:s,estimatedSize:a,formattedSize:this.formatBytes(a)}}estimateSize(e){const{fontSize:t=20,bpp:i=4,charset:o="latin",symbols:n="",range:r=""}=e;let g=n.length;if(o==="full")g=65536;else if(o&&o!=="custom"){const I=this.getCharsetContent(o);g+=I.length}if(r){const I=this.parseUnicodeRange(r);for(let l=0;l<I.length;l+=3)g+=I[l+1]-I[l]+1}o!=="full"&&(g=Math.min(g,g*.8));const s=Math.ceil(t*t*i/8)+40,a=g*s+2048;return{characterCount:Math.floor(g),avgBytesPerChar:s,estimatedSize:a,formattedSize:this.formatBytes(a)}}formatBytes(e){if(e===0)return"0 Bytes";const t=1024,i=["Bytes","KB","MB","GB"],o=Math.floor(Math.log(e)/Math.log(t));return parseFloat((e/Math.pow(t,o)).toFixed(2))+" "+i[o]}cleanup(){this.initialized=!1}}const gS=new rS;class sS{constructor(){this.models=new Map}addModelFile(e,t,i){this.models.has(e)||this.models.set(e,new Map),this.models.get(e).set(t,i)}async loadModelFromShare(e){try{let t=[],i="";if(e.startsWith("wn9"))t=["_MODEL_INFO_","wn9_data","wn9_index"],i=`./static/wakenet_model/${e}/`;else if(e.startsWith("mn6")||e.startsWith("mn7"))t=["_MODEL_INFO_",`${e.substring(0,3)}_data`,`${e.substring(0,3)}_index`,"vocab"],i=`./static/multinet_model/${e}/`,await this.loadFSTModel();else throw new Error(`Unknown model type: ${e}`);let o=0;for(const n of t)try{const r=await fetch(`${i}${n}`);if(r.ok){const g=await r.arrayBuffer();this.addModelFile(e,n,g),o++}else console.warn(`鏃犳硶鍔犺浇鏂囦欢: ${n}, status: ${r.status}`)}catch(r){console.warn(`鍔犺浇鏂囦欢澶辫触: ${n}`,r)}return o===t.length}catch(t){return console.error(`鍔犺浇妯″瀷澶辫触: ${e}`,t),!1}}async loadFSTModel(){try{const e="fst";if(this.models.has(e))return!0;const t=["commands_cn.txt","commands_en.txt"],i="./static/multinet_model/fst/";let o=0;for(const n of t)try{const r=await fetch(`${i}${n}`);if(r.ok){const g=await r.arrayBuffer();this.addModelFile(e,n,g),o++}else console.warn(`鏃犳硶鍔犺浇 FST 鏂囦欢: ${n}, status: ${r.status}`)}catch(r){console.warn(`鍔犺浇 FST 鏂囦欢澶辫触: ${n}`,r)}return o>0}catch(e){return console.error("鍔犺浇 FST 妯″瀷澶辫触",e),!1}}packString(e,t){const i=new Uint8Array(t),o=Math.min(e.length,t);for(let n=0;n<o;n++)i[n]=e.charCodeAt(n)&255;return i}packUint32(e){const t=new Uint8Array(4);return t[0]=e&255,t[1]=e>>8&255,t[2]=e>>16&255,t[3]=e>>24&255,t}packModels(){if(this.models.size===0)throw new Error("娌℃湁妯″瀷鏁版嵁鍙墦鍖�");let e=0;const t=[];for(const[a,I]of Array.from(this.models.entries()).sort((l,C)=>l[0].localeCompare(C[0]))){e+=I.size;const l=Array.from(I.entries()).sort((C,Q)=>C[0].localeCompare(Q[0]));t.push({name:a,files:l})}const i=this.models.size;let o=4;for(const a of t)o+=36,o+=a.files.length*40;const n=o+Array.from(this.models.values()).reduce((a,I)=>a+Array.from(I.values()).reduce((l,C)=>l+C.byteLength,0),0),r=new Uint8Array(n);let g=0;r.set(this.packUint32(i),g),g+=4;let s=o;for(const a of t){r.set(this.packString(a.name,32),g),g+=32,r.set(this.packUint32(a.files.length),g),g+=4;for(const[I,l]of a.files)r.set(this.packString(I,32),g),g+=32,r.set(this.packUint32(s),g),g+=4,r.set(this.packUint32(l.byteLength),g),g+=4,s+=l.byteLength}for(const a of t)for(const[I,l]of a.files)r.set(new Uint8Array(l),g),g+=l.byteLength;return r.buffer}static isValidModel(e,t){return t==="esp32c3"||t==="esp32c6"?e.startsWith("wn9s_"):e.startsWith("wn9_")}clear(){this.models.clear()}getStats(){let e=0,t=0;for(const i of this.models.values()){e+=i.size;for(const o of i.values())t+=o.byteLength}return{modelCount:this.models.size,fileCount:e,totalSize:t,models:Array.from(this.models.keys())}}validatePackingCompatibility(){const e="test_model",t=this.packString(e,32),i=305419896,o=this.packUint32(i);return{stringPacking:{input:e,output:Array.from(t).map(n=>`0x${n.toString(16).padStart(2,"0")}`),isASCII:t.every((n,r)=>r>=e.length||n===e.charCodeAt(r))},intPacking:{input:`0x${i.toString(16)}`,output:Array.from(o).map(n=>`0x${n.toString(16).padStart(2,"0")}`),isLittleEndian:o[0]===120&&o[3]===18}}}}class aS{constructor(){this.files=[],this.textEncoder=new TextEncoder}addFile(e,t,i={}){e.length>32&&console.warn(`Filename "${e}" exceeds 32 bytes and will be truncated`),this.files.push({filename:e,data:t,size:t.byteLength,width:i.width||0,height:i.height||0})}async getImageDimensions(e){return new Promise(t=>{try{const i=new Blob([e]),o=URL.createObjectURL(i),n=new Image;n.onload=()=>{URL.revokeObjectURL(o),t({width:n.width,height:n.height})},n.onerror=()=>{URL.revokeObjectURL(o),t({width:0,height:0})},n.src=o}catch{t({width:0,height:0})}})}parseSpecialImageFormat(e,t){const i=e.toLowerCase().split(".").pop();if([".sjpg",".spng",".sqoi"].includes("."+i))try{const o=new DataView(t),n=o.getUint16(14,!0),r=o.getUint16(16,!0);return{width:n,height:r}}catch(o){console.warn(`Failed to parse special image format: ${e}`,o)}return{width:0,height:0}}packUint32(e){const t=new Uint8Array(4);return t[0]=e&255,t[1]=e>>8&255,t[2]=e>>16&255,t[3]=e>>24&255,t}packUint16(e){const t=new Uint8Array(2);return t[0]=e&255,t[1]=e>>8&255,t}packString(e,t){const i=new Uint8Array(t),o=this.textEncoder.encode(e),n=Math.min(o.length,t);return i.set(o.slice(0,n),0),i}computeChecksum(e){let t=0;for(let i=0;i<e.length;i++)t+=e[i];return t&65535}sortFiles(e){return e.slice().sort((t,i)=>{const o=t.filename.split(".").pop()||"",n=i.filename.split(".").pop()||"";if(o!==n)return o.localeCompare(n);const r=t.filename.replace(/\.[^/.]+$/,""),g=i.filename.replace(/\.[^/.]+$/,"");return r.localeCompare(g)})}async generate(e=null){if(this.files.length===0)throw new Error("No files to package");e&&e(0,"Starting to package files...");const t=this.sortFiles(this.files),i=t.length,o=[];let n=0;for(let D=0;D<t.length;D++){const c=t[D];let p=c.width,d=c.height;if(e&&e(10+D/i*30,`Processing file: ${c.filename}`),p===0&&d===0){const v=this.parseSpecialImageFormat(c.filename,c.data);if(v.width>0||v.height>0)p=v.width,d=v.height;else{const b=c.filename.toLowerCase().split(".").pop();if(["png","jpg","jpeg","gif","bmp","webp"].includes(b)){const N=await this.getImageDimensions(c.data);p=N.width,d=N.height}}}o.push({filename:c.filename,data:c.data,size:c.size,offset:n,width:p,height:d}),n+=2+c.size}e&&e(40,"Building file mapping table...");const r=i*44,g=new Uint8Array(r);let s=0;for(const D of o)g.set(this.packString(D.filename,32),s),s+=32,g.set(this.packUint32(D.size),s),s+=4,g.set(this.packUint32(D.offset),s),s+=4,g.set(this.packUint16(D.width),s),s+=2,g.set(this.packUint16(D.height),s),s+=2;e&&e(60,"Merging file data...");const a=new Uint8Array(n);let I=0;for(let D=0;D<o.length;D++){const c=o[D];e&&e(60+D/i*20,`Merging file: ${c.filename}`),a[I]=90,a[I+1]=90,I+=2,a.set(new Uint8Array(c.data),I),I+=c.size}e&&e(80,"Computing checksum...");const l=new Uint8Array(r+n);l.set(g,0),l.set(a,r);const C=this.computeChecksum(l),Q=l.length;e&&e(90,"Building final file...");const h=12+Q,u=new Uint8Array(h);let E=0;return u.set(this.packUint32(i),E),E+=4,u.set(this.packUint32(C),E),E+=4,u.set(this.packUint32(Q),E),E+=4,u.set(l,E),e&&e(100,"Packaging completed"),u.buffer}getStats(){var i;let e=0;const t=new Map;for(const o of this.files){e+=o.size;const n=((i=o.filename.split(".").pop())==null?void 0:i.toLowerCase())||"unknown";t.set(n,(t.get(n)||0)+1)}return{fileCount:this.files.length,totalSize:e,fileTypes:Object.fromEntries(t),averageFileSize:this.files.length>0?Math.round(e/this.files.length):0}}printFileList(){if(console.log("=== Packaged File List ==="),console.log(`Total files: ${this.files.length}`),this.files.length===0){console.log("No files available");return}this.sortFiles(this.files).forEach((i,o)=>{var s;const n=((s=i.filename.split(".").pop())==null?void 0:s.toLowerCase())||"unknown",r=(i.size/1024).toFixed(2),g=i.width&&i.height?`${i.width}x${i.height}`:"N/A";console.log(`${String(o+1).padStart(3," ")}. ${i.filename}`),console.log(` Type: ${n.toUpperCase()}`),console.log(` Size: ${r} KB (${i.size} bytes)`),console.log(` Dimensions: ${g}`),console.log("")});const t=this.getStats();console.log("=== File Statistics ==="),console.log(`Total size: ${(t.totalSize/1024).toFixed(2)} KB`),console.log(`Average size: ${(t.averageFileSize/1024).toFixed(2)} KB`),console.log("File type distribution:"),Object.entries(t.fileTypes).forEach(([i,o])=>{console.log(` ${i.toUpperCase()}: ${o} files`)})}clear(){this.files=[]}}let Jo={tool:{workerLocalUrl:`var gifsicle=function(){Me="undefined"!=typeof document&&document.currentScript?document.currentScript.src:void 0,"undefined"!=typeof __filename&&(Me=Me||__filename);var Me,c=function(e){var j,r,h=void 0!==(e=e||{})?e:{},t=(h.ready=new Promise(function(e,r){j=e}),h.preRun=h.preRun||[],h.preRun.push(function(){if(h.folder&&h.folder.length)for(let e=0;e<h.folder.length;e++){var r=h.folder[e];T.mkdir(r)}T.mkdir("/tem"),T.mkdir("/out");var t=h.input;for(let e=0;e<t.length;e++){var n=t[e];T.writeFile("/"+n.name,new Uint8Array(n.file))}}),h.postRun=h.postRun||[],h.postRun.push(function(){let r=[],t=T.readdir("/out");t.splice(0,2);for(let e=0;e<t.length;e++){var n=t[e],o=T.readFile("/out/"+n);r.push({file:o,name:n})}h.output(r)}),{});for(r in h)h.hasOwnProperty(r)&&(t[r]=h[r]);var n,o,i,O,a=[],C="./this.program",u=function(e,r){throw r},L=!1,c=!1,L="object"==typeof window,c="function"==typeof importScripts,s="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node,I=!L&&!s&&!c,f="";s?(f=c?require("path").dirname(f)+"/":__dirname+"/",n=function(e,r){return i=i||require("fs"),e=(O=O||require("path")).normalize(e),i.readFileSync(e,r?null:"utf8")},o=function(e){e=n(e,!0);return H((e=e.buffer?e:new Uint8Array(e)).buffer),e},1<process.argv.length&&(C=process.argv[1].replace(/\\\\/g,"/")),a=process.argv.slice(2),process.on("uncaughtException",function(e){if(!(e instanceof Fe))throw e}),process.on("unhandledRejection",D),u=function(e){process.exit(e)},h.inspect=function(){return"[Emscripten Module object]"}):I?("undefined"!=typeof read&&(n=function(e){return read(e)}),o=function(e){return"function"==typeof readbuffer?new Uint8Array(readbuffer(e)):(H("object"==typeof(e=read(e,"binary"))),e)},"undefined"!=typeof scriptArgs?a=scriptArgs:void 0!==arguments&&(a=arguments),"function"==typeof quit&&(u=function(e){quit(e)}),"undefined"!=typeof print&&((console="undefined"==typeof console?{}:console).log=print,console.warn=console.error="undefined"!=typeof printErr?printErr:print)):(L||c)&&(c?f=self.location.href:document.currentScript&&(f=document.currentScript.src),f=0!==(f=Me?Me:f).indexOf("blob:")?f.substr(0,f.lastIndexOf("/")+1):"",n=function(e){var r=new XMLHttpRequest;return r.open("GET",e,!1),r.send(null),r.responseText},c&&(o=function(e){var r=new XMLHttpRequest;return r.open("GET",e,!1),r.responseType="arraybuffer",r.send(null),new Uint8Array(r.response)}));var l,d,p,m=h.print||console.log.bind(console),w=h.printErr||console.warn.bind(console);for(r in t)t.hasOwnProperty(r)&&(h[r]=t[r]);t=null,h.arguments&&(a=h.arguments),h.thisProgram&&(C=h.thisProgram),h.quit&&(u=h.quit),h.wasmBinary&&(l=h.wasmBinary),h.noExitRuntime&&(d=h.noExitRuntime),"object"!=typeof WebAssembly&&w("no native wasm support detected");var I=new WebAssembly.Table({initial:64,maximum:64,element:"anyfunc"}),U=!1;function H(e,r){e||D("Assertion failed: "+r)}var q="undefined"!=typeof TextDecoder?new TextDecoder("utf8"):void 0;function g(e,r,t){for(var n=r+t,o=r;e[o]&&!(n<=o);)++o;if(16<o-r&&e.subarray&&q)return q.decode(e.subarray(r,o));for(var i="";r<o;){var a,s,u=e[r++];128&u?(s=63&e[r++],192==(224&u)?i+=String.fromCharCode((31&u)<<6|s):(a=63&e[r++],(u=224==(240&u)?(15&u)<<12|s<<6|a:(7&u)<<18|s<<12|a<<6|63&e[r++])<65536?i+=String.fromCharCode(u):(s=u-65536,i+=String.fromCharCode(55296|s>>10,56320|1023&s)))):i+=String.fromCharCode(u)}return i}function W(e,r){return e?g(E,e,r):""}function X(e,r,t,n){if(!(0<n))return 0;for(var o=t,i=t+n-1,a=0;a<e.length;++a){var s=e.charCodeAt(a);if((s=55296<=s&&s<=57343?65536+((1023&s)<<10)|1023&e.charCodeAt(++a):s)<=127){if(i<=t)break;r[t++]=s}else if(s<=2047){if(i<=t+1)break;r[t++]=192|s>>6,r[t++]=128|63&s}else if(s<=65535){if(i<=t+2)break;r[t++]=224|s>>12,r[t++]=128|s>>6&63,r[t++]=128|63&s}else{if(i<=t+3)break;r[t++]=240|s>>18,r[t++]=128|s>>12&63,r[t++]=128|s>>6&63,r[t++]=128|63&s}}return r[t]=0,t-o}function G(e){for(var r=0,t=0;t<e.length;++t){var n=e.charCodeAt(t);(n=55296<=n&&n<=57343?65536+((1023&n)<<10)|1023&e.charCodeAt(++t):n)<=127?++r:r+=n<=2047?2:n<=65535?3:4}return r}function V(e){var r=G(e)+1,t=Se(r);return X(e,y,t,r),t}var v,y,E,K,k;function Y(e){v=e,h.HEAP8=y=new Int8Array(e),h.HEAP16=K=new Int16Array(e),h.HEAP32=k=new Int32Array(e),h.HEAPU8=E=new Uint8Array(e),h.HEAPU16=new Uint16Array(e),h.HEAPU32=new Uint32Array(e),h.HEAPF32=new Float32Array(e),h.HEAPF64=new Float64Array(e)}f=h.INITIAL_MEMORY||16777216;function Z(e){for(;0<e.length;){var r,t=e.shift();"function"==typeof t?t(h):"number"==typeof(r=t.func)?void 0===t.arg?h.dynCall_v(r):h.dynCall_vi(r,t.arg):r(void 0===t.arg?null:t.arg)}}p=h.wasmMemory||new WebAssembly.Memory({initial:f/65536,maximum:32768}),f=(v=p?p.buffer:v).byteLength,Y(v),k[9776]=5282144;var $=[],J=[],Q=[],ee=[];function re(){if(h.preRun)for("function"==typeof h.preRun&&(h.preRun=[h.preRun]);h.preRun.length;)e=h.preRun.shift(),$.unshift(e);var e;Z($)}function te(){if(h.postRun)for("function"==typeof h.postRun&&(h.postRun=[h.postRun]);h.postRun.length;)e=h.postRun.shift(),ee.unshift(e);var e;Z(ee)}var ne=Math.abs,oe=Math.ceil,ie=Math.floor,ae=Math.min,_=0,b=null;function se(){_++,h.monitorRunDependencies&&h.monitorRunDependencies(_)}function ue(){var e;_--,h.monitorRunDependencies&&h.monitorRunDependencies(_),0==_&&b&&(e=b,b=null,e())}function D(e){throw h.onAbort&&h.onAbort(e),m(e+=""),w(e),U=!0,e="abort("+e+"). Build with -s ASSERTIONS=1 for more info.",new WebAssembly.RuntimeError(e)}function ce(e,r){return String.prototype.startsWith?e.startsWith(r):0===e.indexOf(r)}h.preloadedImages={},h.preloadedAudios={};var fe="data:application/octet-stream;base64,";var le="file://";function de(e){return ce(e,le)}var S,F,P="data:application/wasm;base64,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ACABOgABIANBfWogAToAACAAIAE6AAIgAkEHSQ0AIANBfGogAToAACAAIAE6AAMgAkEJSQ0AIABBACAAa0EDcSIEaiIDIAFB/wFxQYGChAhsIgE2AgAgAyACIARrQXxxIgRqIgJBfGogATYCACAEQQlJDQAgAyABNgIIIAMgATYCBCACQXhqIAE2AgAgAkF0aiABNgIAIARBGUkNACADIAE2AhggAyABNgIUIAMgATYCECADIAE2AgwgAkFwaiABNgIAIAJBbGogATYCACACQWhqIAE2AgAgAkFkaiABNgIAIAQgA0EEcUEYciIEayICQSBJDQAgAa0iBUIghiAFhCEFIAMgBGohAQNAIAEgBTcDGCABIAU3AxAgASAFNwMIIAEgBTcDACABQSBqIQEgAkFgaiICQR9LDQALCyAAC5ABAQN/IAAhAQJAAkAgAEEDcUUNACAALQAARQRAQQAPCwNAIAFBAWoiAUEDcUUNASABLQAADQALDAELA0AgASICQQRqIQEgAigCACIDQX9zIANB//37d2pxQYCBgoR4cUUNAAsgA0H/AXFFBEAgAiAAaw8LA0AgAi0AASEDIAJBAWoiASECIAMNAAsLIAEgAGsLSgECfwJAIAAtAAAiAkUgAiABLQAAIgNHcg0AA0AgAS0AASEDIAAtAAEiAkUNASABQQFqIQEgAEEBaiEAIAIgA0YNAAsLIAIgA2sLoggBC38gAEUEQCABEBoPCyABQUBPBEBB3JwCQTA2AgBBAA8LIABBfGoiBygCACIIQXhxIQIgCEEDcSIDQQFGQeCtAigCACILIABBeGoiBktyGkEQIAFBC2pBeHEgAUELSRshBAJAAkAgA0UEQCAEQYACSSACIARBBHJJcg0BIAIgBGtBsLECKAIAQQF0TQ0CDAELIAIgBmohBSACIARPBEAgAiAEayIBQRBJDQIgByAIQQFxIARyQQJyNgIAIAQgBmoiAiABQQNyNgIEIAUgBSgCBEEBcjYCBCACIAEQpQEgAA8LIAVB6K0CKAIARgRAQdytAigCACACaiICIARNDQEgByAIQQFxIARyQQJyNgIAIAQgBmoiASACIARrIgJBAXI2AgRB3K0CIAI2AgBB6K0CIAE2AgAgAA8LIAVB5K0CKAIARgRAQditAigCACACaiICIARJDQECQCACIARrIgFBEE8EQCAHIAhBAXEgBHJBAnI2AgAgBCAGaiIDIAFBAXI2AgQgAiAGaiICIAE2AgAgAiACKAIEQX5xNgIEDAELIAcgCEEBcSACckECcjYCACACIAZqIgEgASgCBEEBcjYCBEEAIQFBACEDC0HkrQIgAzYCAEHYrQIgATYCACAADwsgBSgCBCIDQQJxDQAgA0F4cSACaiIJIARJDQAgCSAEayEMAkAgA0H/AU0EQCAFKAIIIgIgA0EDdiIDQQN0QfitAmpHGiACIAUoAgwiAUYEQEHQrQJB0K0CKAIAQX4gA3dxNgIADAILIAIgATYCDCABIAI2AggMAQsgBSgCGCEKAkAgBSAFKAIMIgJHBEAgCyAFKAIIIgFNBEAgASgCDBoLIAEgAjYCDCACIAE2AggMAQsCQCAFQRRqIgEoAgAiAw0AIAVBEGoiASgCACIDDQBBACECDAELA0AgASELIAMiAkEUaiIBKAIAIgMNACACQRBqIQEgAigCECIDDQALIAtBADYCAAsgCkUNAAJAIAUgBSgCHCIBQQJ0QYCwAmoiAygCAEYEQCADIAI2AgAgAg0BQdStAkHUrQIoAgBBfiABd3E2AgAMAgsgCkEQQRQgCigCECAFRhtqIAI2AgAgAkUNAQsgAiAKNgIYIAUoAhAiAQRAIAIgATYCECABIAI2AhgLIAUoAhQiAUUNACACIAE2AhQgASACNgIYCyAMQQ9NBEAgByAIQQFxIAlyQQJyNgIAIAYgCWoiASABKAIEQQFyNgIEIAAPCyAHIAhBAXEgBHJBAnI2AgAgBCAGaiIBIAxBA3I2AgQgBiAJaiICIAIoAgRBAXI2AgQgASAMEKUBIAAPCyABEBoiAkUEQEEADwsgAiAAQXxBeCAHKAIAIgNBA3EbIANBeHFqIgMgASADIAFJGxAYGiAAEBYgAiEACyAAC8EBAgN/AX4CQAJAIAApA3AiBFBFBEAgACkDeCAEWQ0BCyAAEGsiAkF/Sg0BCyAAQQA2AmhBfw8LIAAoAgghAQJAAkAgACkDcCIEUA0AIAQgACkDeEJ/hXwiBCABIAAoAgQiA2usWQ0AIAAgAyAEp2o2AmgMAQsgACABNgJoCwJAIAFFBEAgACgCBCEADAELIAAgACkDeCABIAAoAgQiAGtBAWqsfDcDeAsgAEF/aiIALQAAIAJHBEAgACACOgAACyACC5gLAgV/D34jAEHgAGsiBSQAIAJCIIYgAUIgiIQhDyAEQi+GIANCEYiEIQ0gBEL///////8/gyIOQg+GIANCMYiEIRAgAiAEhUKAgICAgICAgIB/gyEKIAJC////////P4MiC0IgiCERIA5CEYghEiAEQjCIp0H//wFxIQcCQAJ/IAJCMIinQf//AXEiCUF/akH9/wFNBEBBACAHQX9qQf7/AUkNARoLIAFQIAJC////////////AIMiDEKAgICAgIDA//8AVCAMQoCAgICAgMD//wBRG0UEQCACQoCAgICAgCCEIQoMAgsgA1AgBEL///////////8AgyICQoCAgICAgMD//wBUIAJCgICAgICAwP//AFEbRQRAIARCgICAgICAIIQhCiADIQEMAgsgASAMQoCAgICAgMD//wCFhFAEQCACIAOEUARAQoCAgICAgOD//wAhCkIAIQEMAwsgCkKAgICAgIDA//8AhCEKQgAhAQwCCyADIAJCgICAgICAwP//AIWEUARAIAEgDIQhAkIAIQEgAlAEQEKAgICAgIDg//8AIQoMAwsgCkKAgICAgIDA//8AhCEKDAILIAEgDIRQBEBCACEBDAILIAIgA4RQBEBCACEBDAILIAxC////////P1gEQCAFQdAAaiABIAsgASALIAtQIgYbeSAGQQZ0rXynIgZBcWoQKCAFKQNYIgtCIIYgBSkDUCIBQiCIhCEPIAtCIIghEUEQIAZrIQYLIAYgAkL///////8/Vg0AGiAFQUBrIAMgDiADIA4gDlAiCBt5IAhBBnStfKciCEFxahAoIAUpA0giAkIPhiAFKQNAIgNCMYiEIRAgAkIvhiADQhGIhCENIAJCEYghEiAGIAhrQRBqCyEGIA1C/////w+DIgIgAUL/////D4MiAX4iEyADQg+GQoCA/v8PgyIDIA9C/////w+DIgx+fCIEQiCGIg4gASADfnwiDSAOVK0gAiAMfiIVIAMgC0L/////D4MiC358IhQgEEL/////D4MiDiABfnwiECAEIBNUrUIghiAEQiCIhHwiEyACIAt+IhYgAyARQoCABIQiD358IgMgDCAOfnwiESABIBJC/////weDQoCAgIAIhCIBfnwiEkIghnwiF3whBCAHIAlqIAZqQYGAf2ohBgJAIAsgDn4iGCACIA9+fCICIBhUrSACIAEgDH58Igwg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AAsLAkACQCAAIAhHBEAgBigCECIFIABBBHRqIgcoAgAgAUYNAQsCQEEAIAggCEEHcRtFBEAgBigCECEFDAELIAYoAhAgCEEEdEGAAWoQHiIFRQ0DIAYgBTYCECAGKAIUIQgLIAUgAEEEdCIJaiIHQRBqIAcgCCAAa0EEdBDCASAGIAYoAhRBAWo2AhQgBigCECIFIAlqQQA2AgQMAQsgBygCBEEFRw0AIAUgAEEEdGooAgwiBygCABAWIAcoAgQQFiAHEBYgBigCECEFCyAFIABBBHRqIgAgBDYCDCAAIAI2AgggACADNgIEIAAgATYCAEEAIQoLIAoLaQEBfyMAQYACayIFJAAgBEGAwARxIAIgA0xyRQRAIAUgASACIANrIgJBgAIgAkGAAkkiARsQGxogAUUEQANAIAAgBUGAAhAnIAJBgH5qIgJB/wFLDQALCyAAIAUgAhAnCyAFQYACaiQAC2gBAX8gAEEASCABQQFIckVBAEEAQRRBARAXIgIbRQRAIAIQFkEADwsgAiAANgIAIAIgACABIAAgAUobIgA2AgQgAkEAQQggABAXIgE2AhAgAkIANwIIIAIhACABBH8gAAUgABAWQQALCyUBAX8jAEEQayICJAAgAiABNgIMQQBBAEEDIAAgARBdQQEQAgALwQkCBH8EfiMAQfAAayIFJAAgBEL///////////8AgyEKAkACQCABQn98IgtCf1EgAkL///////////8AgyIJIAsgAVStfEJ/fCILQv///////7///wBWIAtC////////v///AFEbRQRAIANCf3wiC0J/UiAKIAsgA1StfEJ/fCILQv///////7///wBUIAtC////////v///AFEbDQELIAFQIAlCgICAgICAwP//AFQgCUKAgICAgIDA//8AURtFBEAgAkKAgICAgIAghCEEIAEhAwwCCyADUCAKQoCAgICAgMD//wBUIApCgICAgICAwP//AFEbRQRAIARCgICAgICAIIQhBAwCCyABIAlCgICAgICAwP//AIWEUARAQoCAgICAgOD//wAgAiABIAOFIAIgBIVCgICAgICAgICAf4WEUCIGGyEEQgAgASAGGyEDDAILIAMgCkKAgICAgIDA//8AhYRQDQEgASAJhFAEQCADIAqEQgBSDQIgASADgyEDIAIgBIMhBAwCCyADIAqEUEUN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BiAEIAFBAnRqKAIARQ0AC0F/QQEgAEEJSRshBwwFCwsgAEEgIAogByAMayIOIAsgCyAOSBsiE2oiByAPIA8gB0gbIgEgByAJECsgACAQIAoQJyAAQTAgASAHIAlBgIAEcxArIABBMCATIA5BABArIAAgDCAOECcgAEEgIAEgByAJQYDAAHMQKwwBCwtBACEHDAELQX8hBwsgCEHQAGokACAHC20BAn9BAEHMAEEBEBciAAR/IABCADcCACAAQgA3AhggAEEANgI8IABB//8DOwEUIABBADYCSCAAQgA3AkAgAEEGNgIoIABCADcCICAAQgA3AiwgAEIANwIIIABBADYCECAAQgA3AjQgAAVBAAsLEAAgACABIAJBAEEAELoBGgt8AQJ/IAAgAC0ASiIBQX9qIAFyOgBKIAAoAhQgACgCHEsEQCAAQQBBACAAKAIkEQMAGgsgAEEANgIcIABCADcDECAAKAIAIgFBBHEEQCAAIAFBIHI2AgBBfw8LIAAgACgCLCAAKAIwaiICNgIIIAAgAjYCBCABQRt0QR91C2kBAn8CQCAAKAIUIAAoAhxNDQAgAEEAQQAgACgCJBEDABogACgCFA0AQX8PCyAAKAIEIgEgACgCCCICSQRAIAAgASACa6xBASAAKAIoEQ8AGgsgAEEANgIcIABCADcDECAAQgA3AgRBAAtAAQF/IwBBEGsiBSQAIAUgASACIAMgBEKAgICAgICAgIB/hRAuIAAgBSkDADcDACAAIAUpAwg3AwggBUEQaiQACykBAX8jAEEQayICJAAgAiABNgIMQYjoASgCACAAIAEQhQEgAkEQaiQAC7cBAQR/AkAgAigCECIDBH8gAwUgAhDBAQ0BIAIoAhALIAIoAhQiBWsgAUkEQCACIAAgASACKAIkEQMADwsCQCACLABLQQBIDQAgASEEA0AgBCIDRQ0BIAAgA0F/aiIEai0AAEEKRw0ACyACIAAgAyACKAIkEQMAIgQgA0kNASABIANrIQEgACADaiEAIAIoAhQhBSADIQYLIAUgACABEBgaIAIgAigCFCABajYCFCABIAZqIQQLIAQLgwEBA38jAEEQayICJAAgAiABOgAPAkAgACgCECIDRQRAIAAQwQENASAAKAIQIQMLAkAgACgCFCIEIANPDQAgACwASyABQf8BcUYNACAAIARBAWo2AhQg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IAAgBiAEoKKhDwsgAUEDdCIBQcCIAmorAwAgACAGIASgoiABQeCIAmorAwChIAChoSIAmiAAIANCAFMbIQALIAAPCyAARBgtRFT7Ifk/IACmIANC////////////AINCgICAgICAgPj/AFYbC5IBAQN8RAAAAAAAAPA/IAAgAKIiAkQAAAAAAADgP6IiA6EiBEQAAAAAAADwPyAEoSADoSACIAIgAiACRJAVyxmgAfo+okR3UcEWbMFWv6CiRExVVVVVVaU/oKIgAiACoiIDIAOiIAIgAkTUOIi+6fqovaJExLG0vZ7uIT6gokStUpyAT36SvqCioKIgACABoqGgoAtGAQF/IwBBIGsiASQAAn8gACABQQhqEBEiAEUEQEE7IQBBASABLQAIQQJGDQEaC0HcnAIgADYCAEEACyEAIAFBIGokACAAC+EBAQN/IwBBIGsiBEIANwMYIARCADcDECAEQgA3AwggBEIANwMAIAEtAAAiAkUEQEEADwsgAS0AAUUEQCAAIQEDQCABIgNBAWohASADLQAAIAJGDQALIAMgAGsPCwNAIAQgAkEDdkEccWoiAyADKAIAQQEgAkEfcXRyNgIAIAEtAAEhAiABQQFqIQEgAg0ACyAAIQMCQCAALQAAIgJFDQAgACEBA0AgBCACQQN2QRxxaigCACACQR9xdkEBcUUEQCABIQMMAgsgAS0AASECIAFBAWoiAyEBIAINAAsLIAMgAGsLewECfwJAIAAoAgQiAyAAKAIIIgJIBEAgACgCACECDAELIAAgAkEBdEECIAIbIgM2AgggACAAKAIAQQQgAxAXIgI2AgAgAkUEQEEADwsgACgCBCEDCyACIANBAnRqIAE2AgAgACADQQFqNgIEIAEgASgCSEEBajYCSEEBCw4AIAAgABAcQQFqEJICCwkAIAAgARCTAgt7AQF/IwBBkAFrIgQkACAEIAA2AiwgBCAANgIEIARBADYCACAEQX82AkwgBEF/IABB/////wdqIABBAEgbNgIIIARCABA0IAQgAkEBIAMQtwEhAyABBEAgASAAIAQoAgQgBCgCeGogBCgCCGtqNgIACyAEQZABaiQAIAMLpQEBBX8jAEGAAmsiBCQAAkAgAkECSA0AIAEgAkECdGoiByAENgIAIABFDQAgBCEDA0AgAyABKAIAIABBgAIgAEGAAkkbIgUQGBpBACEDA0AgASADQQJ0aiIGKAIAIAEgA0EBaiIDQQJ0aigCACAFEBgaIAYgBigCACAFajYCACACIANHDQALIAAgBWsiAEUNASAHKAIAIQMMAAALAAsgBEGAAmokAAsmAQF/IAAoAgBBf2poIgFFBEAgACgCBGgiAEEgakEAIAAbDwsgAQvvAgEFfyAAKAIIIQMgACgCBCEEIAAoAgAhBUEAQShBARAXIgEEfyABIAU2AgACQCAEBEAgAUEAQQEgA0EBahAXIgI2AgQgAkUEQCABEBZBAA8LIAIgBCADEBgaIAEoAgQgA2pBADoAAAwBC0EAIQMgAUEANgIECyABQgA3AhQgAUEANgIMIAEgAzYCCCABQgA3AhwgAUEANgIkAkACQCAAKAIMIgIEQCAAKAIkDQELIAEgAjYCDCABIAAoAhA2AhAMAQsgAUEAQQEgACgCEBAXIgI2AgwgAkUEQCABKAIEEBYCQEEAIAEoAhgiAEUgASgCHCICGw0AIAJBJGogAEEgaiACGyEDA0AgASADIgAoAgAiAkcEQCACQSBqIQMgAg0BCwsgAkUNACAAIAEoAiA2AgALIAEQFkEADwsgAiAAKAIMIAAoAhAQGBogACgCECECIAFBBjYCJCABIAI2AhALIAEgACgCFDYCFCABBUEACwtDAAJAIABFDQACQAJAAkACQCABQQJqDgYAAQICBAMECyAAIAI8AAAPCyAAIAI9AQAPCyAAIAI+AgAPCyAAIAI3AwALC/gDAgN/AX4CQAJAAkACfyAAKAIEIgIgACgCaEkEQCAAIAJBAWo2AgQgAi0AAAwBCyAAEB8LIgNBVWoOAwEAAQALIANBUGohAQwBCyADQS1GIQQCQCABRQJ/IAAoAgQiAiAAKAJoSQRAIAAgAkEBajYCBCACLQAADAELIAAQHwsiAkFQaiIBQQpJcg0AIAAoAmhFDQAgACAAKAIEQX9qNgIECyACIQMLAkAgAUEKSQRAQQAhAQNAIAMgAUEKbGohAQJ/IAAoAgQiAiAAKAJoSQRAIAAgAkEBajYCBCACLQAADAELIAAQHwsiA0FQaiICQQlNQQAgAUFQaiIBQcyZs+YASBsNAAsgAawhBQJAIAJBCk8NAANAIAOtIAVCCn58QlB8IQUCfyAAKAIEIgEgACgCaEkEQCAAIAFBAWo2AgQgAS0AAAwBCyAAEB8LIgNBUGoiAkEJSw0BIAVCro+F18fC66MBUw0ACwsgAkEKSQRAA0ACfyAAKAIEIgEgACgCaEkEQCAAIAFBAWo2AgQgAS0AAAwBCyAAEB8LQVBqQQpJDQALCyAAKAJoBEAgACAAKAIEQX9qNgIEC0IAIAV9IAUgBBshBQwBC0KAgICAgICAgIB/IQUgACgCaEUNACAAIAAoAgRBf2o2AgRCgICAgICAgICAfw8LIAUL7QcCBX8CfiMAQTBrIgUkAAJAIAJBAk0EQCACQQJ0IgJBuPEBaigCACEHIAJBrPEBaigCACEIA0ACfyABKAIEIgIgASgCaEkEQCABIAJBAWo2AgQgAi0AAAwBCyABEB8LIgIQNQ0AC0EBIQYCQAJAIAJBVWoOAwABAAELQX9BASACQS1GGyEGIAEoAgQiAiABKAJoSQRAIAEgAkEBajYCBCACLQAAIQIMAQsgARAfIQILAkACQANAIARBxPEBaiwAACACQSByRgRAAkAgBEEGSw0AIAEoAgQiAiABKAJoSQRAIAEgAkEBajYCBCACLQAAIQIMAQsgARAfIQILIARBAWoiBEEIRw0BDAILCyAEQQNHBEAgBEEIRg0BIANFIARBBElyDQIgBEEIRg0BCyABKAJoIgIEQCABIAEoAgRBf2o2AgQLIANFIARBBElyDQADQCACBEAgASABKAIEQX9qNgIECyAEQX9qIgRBA0sNAAsLIAUgBrJDAACAf5QQtgIgBSkDCCEKIAUpAwAhCQwCCwJAAkACQCAEDQBBACEEA0AgBEHN8QFqLAAAIAJBIHJHDQECQCAEQQFLDQAgASgCBCICIAEoAmhJBEAgASACQQFqNgIEIAItAAAhAgwBCyABEB8hAgsgBEEBaiIEQQNHDQALDAELAkACQCAEDgQAAQECAQsCQCACQTBHDQACfyABKAIEIgQgASgCaEkEQCABIARBAWo2AgQgBC0AAAwBCyABEB8LQV9xQdgARgRAIAVBEGogASAIIAcgBiADEJwCIAUpAxghCiAFKQMQIQkMBgsgASgCaEUNACABIAEoAgRBf2o2AgQLIAVBIGogASACIAggByAGIAMQmwIgBSkDKCEKIAUpAyAhCQwECyABKAJoBEAgASABKAIEQX9qNgIECwwBCwJAAn8gASgCBCICIAEoAmhJBEAgASACQQFqNgIEIAItAAAMAQsgARAfC0EoRgRAQQEhBAwBC0KAgICAgIDg//8AIQogASgCaEUNAyABIAEoAgRBf2o2AgQMAwsDQAJ/IAEoAgQiAiABKAJoSQRAIAEgAkEBajYCBCACLQAADAELIAEQHwsiAkFQakEKSSACQb9/akEaSXIgAkHfAEZyRUEAIAJBn39qQRpPG0UEQCAEQQFqIQQMAQsLQoCAgICAgOD//wAhCiACQSlGDQIgASgCaCICBEAgASABKAIEQX9qNgIECyADBEAgBEUNAwNAIARBf2ohBCACBEAgASABKAIEQX9qNgIECyAEDQALDAMLC0HcnAJBHDYCACABQgAQNAtCACEKCyAAIAk3AwAgACAKNwMIIAVBMGokAAu/AgEBfyMAQdAAayIEJAACQCADQYCAAU4EQCAEQSBqIAEgAkIAQoCAgICAgID//wAQICAEKQMoIQIgBCkDICEBIANB//8BSARAIANBgYB/aiEDDAILIARBEGogASACQgBCgICAgICAgP//ABAgIANB/f8CIANB/f8CSBtBgoB+aiEDIAQpAxghAiAEKQMQIQEMAQsgA0GBgH9KDQAgBEFAayABIAJCAEKAgICAgIDAABAgIAQpA0ghAiAEKQNAIQEgA0GDgH5KBEAgA0H+/wBqIQMMAQsgBEEwaiABIAJCAEKAgICAgIDAABAgIANBhoB9IANBhoB9ShtB/P8BaiEDIAQpAzghAiAEKQMwIQELIAQgASACQgAgA0H//wBqrUIwhhAgIAAgBCkDCDcDCCAAIAQpAwA3AwAgBEHQAGokAAs1ACAAIAE3AwAgACACQv///////z+DIARCMIinQYCAAnEgAkIwiKdB//8BcXKtQjCGhDcDCAvHCgIFfwR+IwBBEGsiByQAAkACQAJAAkACQAJAIAFBJE0EQANAAn8gACgCBCIEIAAoAmhJBEAgACAEQQFqNgIEIAQtAAAMAQsgABAfCyIEEDUNAAsCQAJAIARBVWoOAwABAAELQX9BACAEQS1GGyEGIAAoAgQiBCAAKAJoSQRAIAAgBEEBajYCBCAELQAAIQQMAQsgABAfIQQLAkAgAUFvcSAEQTBHckUEQAJ/IAAoAgQiBCAAKAJoSQRAIAAgBEEBajYCBCAELQAADAELIAAQHwsiBEFfcUHYAEYEQAJ/IAAoAgQiASAAKAJoSQRAIAAgAUEBajYCBCABLQAADAELIAAQHwshBEEQIQEgBEGh7wFqLQAAQRBJDQUgACgCaEUEQEIAIQMgAg0KDAkLIAAgACgCBCIBQX9qNgIEIAJFDQggACABQX5qNgIEQgAhAwwJCyABDQFBCCEBDAQLIAFBCiABGyIBIARBoe8Bai0AAEsNACAAKAJoBEAgACAAKAIEQX9qNgIEC0IAIQMgAEIAEDRB3JwCQRw2AgAMBwsgAUEKRw0CIARBUGoiAkEJTQRAQQAhAQNAIAFBCmwhBQJ/IAAoAgQiASAAKAJoSQRAIAAgAUEBajYCBCABLQAADAELIAAQHwshBCACIAVqIQEgBEFQaiICQQlNQQAgAUGZs+bMAUkbDQALIAGtIQkLIAJBCUsNASAJQgp+IQogAq0hCwNAAn8gACgCBCIBIAAoAmhJBEAgACABQQFqNgIEIAEtAAAMAQsgABAfCyIEQVBqIgJBCUsgCiALfCIJQpqz5syZs+bMGVpyDQIgCUIKfiIKIAKtIgtCf4VYDQALQQohAQwDC0HcnAJBHDYCAEIAIQMMBQtBCiEBIAJBCU0NAQwCCyABIAFBf2pxBEAgASAEQaHvAWotAAAiAksEQANAIAIgASAFbGoiBUHG4/E4TUEAIAECfyAAKAIEIgIgACgCaEkEQCAAIAJBAWo2AgQgAi0AAAwBCyAAEB8LIgRBoe8Bai0AACICSxsNAAsgBa0hCQsgASACTQ0BIAGtIQoDQCAJIAp+IgsgAq1C/wGDIgxCf4VWDQIgCyAMfCEJIAECfyAAKAIEIgIgACgCaEkEQCAAIAJBAWo2AgQgAi0AAAwBCyAAEB8LIgRBoe8Bai0AACICTQ0CIAcgCiAJEDogBykDCFANAAsMAQsgAUEXbEEFdkEHcUGh8QFqLAAAIQggASAEQaHvAWotAAAiAksEQANAIAIgBSAIdHIiBUH///8/TUEAIAECfyAAKAIEIgIgACgCaEkEQCAAIAJBAWo2AgQgAi0AAAwBCyAAEB8LIgRBoe8Bai0AACICSxsNAAsgBa0hCQsgASACTUJ/IAitIgqIIgsgCVRyDQADQCACrUL/AYMgCSAKhoQhCQJ/IAAoAgQiAiAAKAJoSQRAIAAgAkEBajYCBCACLQAADAELIAAQHwshBCAJIAtWDQEgASAEQaHvAWotAAAiAksNAAsLIAEgBEGh7wFqLQAATQ0AA0AgAQJ/IAAoAgQiAiAAKAJoSQRAIAAgAkEBajYCBCACLQAADAELIAAQHwtBoe8Bai0AAEsNAAtB3JwCQcQANgIAIAZBACADQgGDUBshBiADIQkLIAAoAmgEQCAAIAAoAgRBf2o2AgQLAkAgCSADVA0AIAOnQQFxIAZyRQRAQdycAkHEADYCACADQn98IQMMAwsgCSADWA0AQdycAkHEADYCAAwCCyAJIAasIgOFIAN9IQMMAQtCACEDIABCABA0CyAHQRBqJAAgAwuYAgACQAJAIAFBFEsNAAJAAkACQAJAAkACQAJAAkAgAUF3ag4KAAECCQMEBQYJBwgLIAIgAigCACIBQQRqNgIAIAAgASgCADYCAA8LIAIgAigCACIBQQRqNgIAIAAgATQCADcDAA8LIAIgAigCACIBQQRqNgIAIAAgATUCADcDAA8LIAIgAigCACIBQQRqNgIAIAAgATIBADcDAA8LIAIgAigCACIBQQRqNgIAIAAgATMBADcDAA8LIAIgAigCACIBQQRqNgIAIAAgATAAADcDAA8LIAIgAigCACIBQQRqNgIAIAAgATEAADcDAA8LIAAgAiADEQEACw8LIAIgAigCAEEHakF4cSIBQQhqNgIAIAAgASkDADcDAAtEAQR/IAAoAgAiAiwAACIDEEQEQANAIAAgAkEBaiIENgIAIAFBCmwgA2pBUGohASACLAABIQMgBCECIAMQRA0ACwsgAQv7AgEDfyMAQdABayIFJAAgBSACNgLMAUEAIQIgBUGgAWpBAEEoEBsaIAUgBSgCzAE2AsgBAkBBACABIAVByAFqIAVB0ABqIAVBoAFqIAMgBBCDAUEASARAQX8hAQwBCyAAKAJMQQBOBEBBASECCyAAKAIAIQYgACwASkEATARAIAAgBkFfcTYCAAsgBkEgcSEHAn8gACgCMARAIAAgASAFQcgBaiAFQdAAaiAFQaABaiADIAQQgwEMAQsgAEHQADYCMCAAIAVB0ABqNgIQIAAgBTYCHCAAIAU2AhQgACgCLCEGIAAgBTYCLCAAIAEgBUHIAWogBUHQAGogBUGgAWogAyAEEIMBIgEgBkUNABogAEEAQQAgACgCJBEDABogAEEANgIwIAAgBjYCLCAAQQA2AhwgAEEANgIQIAAoAhQhAyAAQQA2AhQgAUF/IAMbCyEBIAAgACgCACIAIAdyNgIAQX8gASAAQSBxGyEBIAJFDQALIAVB0AFqJAAgAQt/AgF/AX4gAL0iA0I0iKdB/w9xIgJB/w9HBHwgAkUEQCABIABEAAAAAAAAAABhBH9BAAUgAEQAAAAAAADwQ6IgARC7ASEAIAEoAgBBQGoLNgIAIAAPCyABIAJBgnhqNgIAIANC/////////4eAf4NCgICAgICAgPA/hL8FIAALCxIAIABFBEBBAA8LIAAgARCkAgtPAQJ/QQBBNEEBEBciAAR/IABCADcCACAAQQA7ARQgAEIANwIcIABBfzYCGCAAQYACNgIQIABCADcCCCAAQgA3AiQgAEIANwIsIAAFQQALCxQAAkAgACgCTEEASA0ACyAAKAI8C4ICAQV/IAEoAkxBAE4Ef0EBBUEACxpB/wchBCAAIQMCQANAAkAgBEUNACADIAEoAgQiAiACQQogASgCCCACayIFEGoiBiACa0EBaiAFIAYbIgIgBCACIARJGyICEBgaIAEgASgCBCACaiIFNgIEIAIgA2ohAyAGDQAgBCACayIERQ0AAkAgBSABKAIISQRAIAEgBUEBajYCBCAFLQAAIQIMAQsgARBrIgJBf0oNAEEAIQIgACADRg0DIAEtAABBEHFFDQMMAQsgAyACOgAAIANBAWohAyAEQX9qIQQgAkH/AXFBCkcNAQsLIABFBEBBACECDAELIANBADoAACAAIQILIAILCAAgABAJEFoLWQEBfyAAIAAtAEoiAUF/aiABcjoASiAAKAIAIgFBCHEEQCAAIAFBIHI2AgBBfw8LIABCADcCBCAAIAAoAiwiATYCHCAAIAE2AhQgACABIAAoAjBqNgIQQQAL1gIBAX8CQCAAIAFGDQAgASAAayACa0EAIAJBAXRrTQRAIAAgASACEBgaDwsgACABc0EDcSEDAkACQCAAIAFJBEAgAw0CIABBA3FFDQEDQCACRQ0EIAAgAS0AADoAACABQQFqIQEgAkF/aiECIABBAWoiAEEDcQ0ACwwBCwJAIAMNACAAIAJqQQNxBEADQCACRQ0FIAAgAkF/aiICaiIDIAEgAmotAAA6AAAgA0EDcQ0ACwsgAkEDTQ0AA0AgACACQXxqIgJqIAEgAmooAgA2AgAgAkEDSw0ACwsgAkUNAgNAIAAgAkF/aiICaiABIAJqLQAAOgAAIAINAAsMAgsgAkEDTQ0AA0AgACABKAIANgIAIAFBBGohASAAQQRqIQAgAkF8aiICQQNLDQALCyACRQ0AA0AgACABLQAAOgAAIABBAWohACABQQFqIQEgAkF/aiICDQALCwuhBgIFfwR+IwBBgAFrIgUkAAJAAkACQCADIARCAEIAEEhFDQAgAyAEELoCIQcgAkIwiKciCUH//wFxIgZB//8BRg0AIAcNAQsgBUEQaiABIAIgAyAEECAgBSAFKQMQIgIgBSkDGCIBIAIgARDEASAFKQMIIQIgBSkDACEEDAELIAEgAkL///////8/gyAGrUIwhoQiCiADIARC////////P4MgBEIwiKdB//8BcSIHrUIwhoQiCxBIQQBMBEAgASAKIAMgCxBIBEAgASEEDAILIAVB8ABqIAEgAkIAQgAQICAFKQN4IQIgBSkDcCEEDAELIAYEfiABBSAFQeAAaiABIApCAEKAgICAgIDAu8AAECAgBSkDaCIKQjCIp0GIf2ohBiAFKQNgCyEEIAdFBEAgBUHQAGogAyALQgBCgICAgICAwLvAABAgIAUpA1giC0IwiKdBiH9qIQcgBSkDUCEDCyAKQv///////z+DQoCAgICAgMAAhCIKIAtC////////P4NCgICAgICAwACEIg19IAQgA1StfSIMQn9VIQggBCADfSELIAYgB0oEQANAAn4gCARAIAsgDIRQBEAgBUEgaiABIAJCAEIAECAgBSkDKCECIAUpAyAhBAwFCyALQj+IIQogDEIBhgwBCyAKQgGGIQogBCELIARCP4gLIQwgCiAMhCIKIA19IAtCAYYiBCADVK19IgxCf1UhCCAEIAN9IQsgBkF/aiIGIAdKDQALIAchBgsCQCAIRQ0AIAsiBCAMIgqEQgBSDQAgBUEwaiABIAJCAEIAECAgBSkDOCECIAUpAzAhBAwBCyAKQv///////z9YBEADQCAEQj+IIQEgBkF/aiEGIARCAYYhBCABIApCAYaEIgpCgICAgICAwABUDQALCyAJQYCAAnEhByAGQQBMBEAgBUFAayAEIApC////////P4MgBkH4AGogB3KtQjCGhEIAQoCAgICAgMDDPxAgIAUpA0ghAiAFKQNAIQQMAQsgCkL///////8/gyAGIAdyrUIwhoQhAgsgACAENwMAIAAgAjcDCCAFQYABaiQAC4IRAgV/DH4jAEHAAWsiBSQAIARC////////P4MhEiACQv///////z+DIQwgAiAEhUKAgICAgICAgIB/gyERIARCMIinQf//AXEhBwJAAkACQCACQjCIp0H//wFxIglBf2pB/f8BTQRAIAdBf2pB/v8BSQ0BCyABUCACQv///////////wCDIgpCgICAgICAwP//AFQgCkKAgICAgIDA//8AURtFBEAgAkKAgICAgIAghCERDAILIANQIARC////////////AIMiAkKAgICAgIDA//8AVCACQoCAgICAgMD//wBRG0UEQCAEQoCAgICAgCCEIREgAyEBDAILIAEgCkKAgICAgIDA//8AhYRQBEAgAyACQoCAgICAgMD//wCFhFAEQEIAIQFCgICAgICA4P//ACERDAMLIBFCgICAgICAwP//AIQhEUIAIQEMAgsgAyACQoCAgICAgMD//wCFhFAEQEIAIQEMAgsgASAKhFANAiACIAOEUARAIBFCgICAgICAwP//AIQhEUIAIQEMAgsgCkL///////8/WARAIAVBsAFqIAEgDCABIAwgDFAiBht5IAZBBnStfKciBkFxahAoQRAgBmshBiAFKQO4ASEMIAUpA7ABIQELIAJC////////P1YNACAFQaABaiADIBIgAyASIBJQIggbeSAIQQZ0rXynIghBcWoQKCAGIAhqQXBqIQYgBSkDqAEhEiAFKQOgASEDCyAFQZABaiASQoCAgICAgMAAhCIUQg+GIANCMYiEIgJChMn5zr/mvIL1ACACfSIEEDogBUGAAWpCACAFKQOYAX0gBBA6IAVB8ABqIAUpA4gBQgGGIAUpA4ABQj+IhCIEIAIQOiAFQeAAaiAEQgAgBSkDeH0QOiAFQdAAaiAFKQNoQgGGIAUpA2BCP4iEIgQgAhA6IAVBQGsgBEIAIAUpA1h9EDogBUEwaiAFKQNIQgGGIAUpA0BCP4iEIgQgAhA6IAVBIGogBEIAIAUpAzh9EDogBUEQaiAFKQMoQgGGIAUpAyBCP4iEIgQgAhA6IAUgBEIAIAUpAxh9EDogBiAJIAdraiEGAn5CACAFKQMIQgGGIAUpAwBCP4iEQn98IgpC/////w+DIgQgAkIgiCIOfiIQIApCIIgiCiACQv////8PgyILfnwiAkIghiINIAQgC358IgsgDVStIAogDn4gAiAQVK1CIIYgAkIgiIR8fCALIAQgA0IRiEL/////D4MiDn4iECAKIANCD4ZCgID+/w+DIg1+fCICQiCGIg8gBCANfnwgD1StIAogDn4gAiAQVK1CIIYgAkIgiIR8fHwiAiALVK18IAJCAFKtfH0iC0L/////D4MiDiAEfiIQIAogDn4iDSAEIAtCIIgiD358IgtCIIZ8Ig4gEFStIAogD34gCyANVK1CIIYgC0IgiIR8fCAOQgAgAn0iAkIgiCILIAR+IhAgAkL/////D4MiDSAKfnwiAkIghiIPIAQgDX58IA9UrSAKIAt+IAIgEFStQiCGIAJCIIiEfHx8IgIgDlStfCACQn58IhAgAlStfEJ/fCILQv////8PgyICIAxCAoYgAUI+iIRC/////w+DIgR+Ig4gAUIeiEL/////D4MiCiALQiCIIgt+fCINIA5UrSANIBBCIIgiDiAMQh6IQv//7/8Pg0KAgBCEIgx+fCIPIA1UrXwgCyAMfnwgAiAMfiITIAQgC358Ig0gE1StQiCGIA1CIIiEfCAPIA1CIIZ8Ig0gD1StfCANIAogDn4iEyAQQv////8PgyIQIAR+fCIPIBNUrSAPIAIgAUIChkL8////D4MiE358IhUgD1StfHwiDyANVK18IA8gCyATfiILIAwgEH58IgwgBCAOfnwiBCACIAp+fCICQiCIIAIgBFStIAwgC1StIAQgDFStfHxCIIaEfCIMIA9UrXwgDCAVIA4gE34iBCAKIBB+fCIKQiCIIAogBFStQiCGhHwiBCAVVK0gBCACQiCGfCAEVK18fCIEIAxUrXwiAkL/////////AFgEQCABQjGGIARC/////w+DIgEgA0L/////D4MiCn4iDEIAUq19QgAgDH0iECAEQiCIIgwgCn4iDSABIANCIIgiC358Ig5CIIYiD1StfSACQv////8PgyAKfiABIBJC/////w+DfnwgCyAMfnwgDiANVK1CIIYgDkIgiIR8IAQgFEIgiH4gAyACQiCIfnwgAiALfnwgDCASfnxCIIZ8fSESIAZBf2ohBiAQIA99DAELIARCIYghCyABQjCGIAJCP4YgBEIBiIQiBEL/////D4MiASADQv////8PgyIKfiIMQgBSrX1CACAMfSIOIAEgA0IgiCIMfiIQIAsgAkIfhoQiDUL/////D4MiDyAKfnwiC0Ig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URtFBEAgAUIZiKdB////AXFBgICA/gdyIQIMAQtBgICA/AchAiAFQv///////7+/wABWDQBBACECIAVCMIinIgRBkf4ASQ0AIANBEGogACABQv///////z+DQoCAgICAgMAAhCIFIARB/4F/ahAoIAMgACAFQYH/ACAEaxBTIAMpAwgiAEIZiKchAiADKQMAIAMpAxAgAykDGIRCAFKthCIFUCAAQv///w+DIgBCgICACFQgAEKAgIAIURtFBEAgAkEBaiECDAELIAUgAEKAgIAIhYRCAFINACACQQFxIAJqIQILIANBIGokACACIAFCIIinQYCAgIB4cXK+C8YBAgN/An4jAEEQayIDJAACfiABvCIEQf////8HcSICQYCAgHxqQf////cHTQRAIAKtQhmGQoCAgICAgIDAP3wMAQsgAkGAgID8B08EQCAErUIZhkKAgICAgIDA//8AhAwBCyACRQRAQgAMAQsgAyACrUIAIAJnIgJB0QBqECggAykDACEFIAMpAwhCgICAgICAwACFQYn/ACACa61CMIaECyEGIAAgBTcDACAAIAYgBEGAgICAeHGtQiCGhDcDCCADQRBqJAALEAAjACAAa0FwcSIAJAAgAAuNBQEFfwJAIAAoAjwiAigCmAEiBEF/TARAQdCQAkGSC0EUIgAQGBoMAQsgAi0AnwEEQEHQkAIgAkGAAWoiAEH/ASAAEBwiASABQf8BShsiABAYIQMgAigCfARAIAAgA2ohASAAQX9zQYACaiIAQQFIQf3/A0H9/wMgAigCACAEQRRsaigCBCICIAJBgHBxQYCwA0YbIAJB///DAEsbIgJB/wBKckUEQCABIAI6AAAgAUEBaiADayEADAMLAkAgAEECSCACQf8PSnIEfwJ/IABBA0ggAkH//wNKckUEQEEBIQQgASEAIAJBDHZBYHIMAQsgAEEESA0CIAEgAkESdkHwAXI6AAAgAUEBaiEAQQIhBCACQQx2QT9xQYB/cgshBSAAIAU6AAAgASAEaiEBIAJBBnZBP3FBgH9yBSACQQZ2QUByCyEAIAEgADoAACABIAJBP3FBgAFyOgABIAFBAmohAQsgASADayEADAILQf8BIAFMDQEgACADaiACKAIAIARBFGxqKAIEOgAAIABBAWohAAwBCyACLQCgASEDQdCQAiACQYABaiIAQf8BIAAQHCIAIABB/wFKGyIAEBghASADBEAgACABakGaCSAAQX9zQYACaiIDQQMgA0EDSBsiAxAYGiABIAAgA2oiAGogAigCACAEQRRsaigCACACKAIEIARBDGxqLQACaiIBIABBf3NBgAJqIgIgARAcIgEgASACShsiARAYGiAAIAFqIQAMAQsgACABaiACKAIAIARBFGxqKAIAIAIoAgQgBEEMbGotAAJqIgEgAEF/c0GAAmoiAiABEBwiASABIAJKGyIBEBgaIAAgAWohAAsgAEGAAkgEQCAAQdCQAmpBADoAAAsLgAEBAn8jAEGQAmsiBCQAIAQgBEGMAmo2AgggBCAEQQxqIgU2AgAgBCAFNgIEIAAgBCACIAMQkwEgASAEKAIAIgAgBCgCBCAAayICQf8HIAJBgAhJGyIBEBggAWpBADoAACAAIAVHBEAgABAWIAQoAgQgBCgCAGsaCyAEQZACaiQAC0QCAX8BfiABQv///////z+DIQMCfyABQjCIp0H//wFxIgJB//8BRwRAQQQgAg0BGkECQQMgACADhFAbDwsgACADhFALC7UCAwJ/AX4CfAJAAnwgAL0iA0IgiKdB/////wdxIgFBgOC/hARPBEAgAUGAgMCEBEkgA0IAU3JFBEAgAEQAAAAAAADgf6IPCyABQYCAwP8HTwRARAAAAAAAAPC/IACjDwsgAEQAAAAAAMyQwGVBAXMNAkQAAAAAAAAAACADQn9XDQEaDAILIAFB//+/5ANLDQEgAEQAAAAAAADwP6ALDwsgAEQAAAAAAAC4QqAiBL2nQYABaiIBQQR0QfAfcSICQZC5AWorAwAiBSAFIAAgBEQAAAAAAAC4wqChIAJBCHJBkLkBaisDAKEiAKIgACAAIAAgAER0XIcDgNhVP6JEAAT3iKuygz+gokSmoATXCGusP6CiRHXFgv+9v84/oKJE7zn6/kIu5j+goqAgAUGAfnFBgAJtEEEL2AECAX8BfkF/IQICQCAAQgBSIAFC////////////AIMiA0KAgICAgIDA//8AViADQoCAgICAgMD//wBRGw0AIAAgA0KAgICAgICA/z+EhFAEQEEADwsgAUKAgICAgICA/z+DQgBZBEAgAEIAVCABQoCAgICAgID/P1MgAUKAgICAgICA/z9RGw0BIAAgAUKAgICAgICA/z+FhEIAUg8LIABCAFYg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IAJBoAJqEBkLQcCXAkJ/NwMAQYCVAkGAlQIoAgBBAnI2AgAMOgtBkJcCKAIAKAIEDQACQEH4lAItAABBgAFxRQ0AQdicAi0AAEUEQCACQdSLATYC2AIgAkHUiwE2AtQCIAJB1IsBNgLQAkEAQfGxASACQdACahAZQdicAkEBOgAADAELIAJB1IsBNgLAAkEAQdeyASACQcACahAZC0H4lAJB+JQCKAIAQYABcjYCAEHIlgIoAgAhAUEAQTBBARAXIQACQCABBEAgACABKQIANwIAIAAgASkCKDcCKCAAIAEpAiA3AiAgACABKQIYNwIYIAAgASkCEDcCECAAIAEpAgg3AggMAQsgAEIANwIAIABCADcCKCAAQgA3AiAgAEIANwIYIABCADcCECAAQgA3AggLIABBADYCACAAQfSTAigCADYCCCAAQfiTAigCADYCDCAAQeyTAigCADYCECAAQfCTAigCADYCFEHIlgIgADYCAAw5C0HIlgJBADYCAAw4C0HIlgIoAgAhAEGQlwIoAgAoAgRFBEBBAEEwQQEQFyEBAkAgAARAIAEgACkCADcCACABIAApAig3AiggASAAKQIgNwIgIAEgACkCGDcCGCABIAApAhA3AhAgASAAKQIINwIIDAELIAFCADcCACABQgA3AiggAUIANwIgIAFCADcCGCABQgA3AhAgAUIANwIICyABQQA2AgBByJYCIAE2AgAgAUEBNgIEDDgLIABFDTcgACgCBEUNN0EAQTBBARAXIgEgACkCADcCACABIAApAig3AiggASAAKQIgNwIgIAEgACkCGDcCGCABIAApAhA3AhAgASAAKQIINwIIIAFBADYCAEHIlgIgATYCACABQQA2AgQMNwtB5JYCQQE2AgAMNgtB6JYCQQE2AgAMNQtB5JYCQQA2AgAMNAtB6JYCQQA2AgAMMwsgAkGQlwIoAgAiASgCDCIANgKIDCABQQEQjAEiBEUEQCABQZq0AUEAECIaDAILQfiUAkH4lAIoAgBBgAJyNgIAAkAgACwAACIDQVBqQQpPBEAgAC0AAUUNAQsgACACQYgMakEAECQiA0H/AUsNLyACKAKIDC0AAA0vCyADQQBBABCCASIAIAQ2AgwgACAEEBw2AhAgAEHslgIoAgA2AiBB7JYCIAA2AgAMMgtBkJcCKAIAIgAoAgwhAyAAQQEQjAEiAQ0BIABBmrQBQQAQIhoLEOICQQEQAgALQfiUAkH4lAIoAgBBgAJyNgIAQf8BIANBCxCCASIAIAE2AgwgACABEBw2AhAgAEHslgIoAgA2AiBB7JYCIAA2AgAMLwsCQEH5lAItAABBAnFFDQBB2JwCLQAARQRAIAJB+LIBNgL4AiACQfiyATYC9AIgAkH4sgE2AvACQQBB8bEBIAJB8AJqEBlB2JwCQQE6AAAMAQsgAkH4sgE2AuACQQBB17IBIAJB4AJqEBkLQfiUAkH4lAIoAgBBgARyNgIAQfCWAkGQlwIoAgAoAgRFQfCWAi0AAEH+AXFyOgAADC4LAkBB+ZQCLQAAQQJxRQ0AQdicAi0AAEUEQCACQfiyATYCmAMgAkH4sgE2ApQDIAJB+LIBNgKQA0EAQfGxASACQZADahAZQdicAkEBOgAADAELIAJB+LIBNgKAA0EAQdeyASACQYADahAZC0H4lAJB+JQCKAIAQYAEcjYCAEHwlgJB8JYCLQAAQf0BcUGQlwIoAgAoAgRFQQF0cjoAAAwtC0HwlgJB8JYCLQAAQfwBcToAAAwsC0H0lgJBADYCAAwrCwJAQfmUAi0AAEEEcUUNAEHYnAItAABFBEAgAkH9sgE2ArgDIAJB/bIBNgK0AyACQf2yATYCsANBAEHxsQEgAkGwA2oQGUHYnAJBAToAAAwBCyACQf2yATYCoANBAEHXsgEgAkGgA2oQGQtB9JYCQQE2AgBB+JQCQfiUAigCAEGACHI2AgA