diff --git a/docs/doc/en/audio/recognize.md b/docs/doc/en/audio/recognize.md index 10df7f61..4e5b6b1c 100644 --- a/docs/doc/en/audio/recognize.md +++ b/docs/doc/en/audio/recognize.md @@ -21,7 +21,7 @@ Speech recognition model support list: | ------- | ------- | ----------- | -------- | | Whisper | ❌ | ❌ | ✅ | | SenseVoice | ❌ | ❌ | ✅ | -| Speech | ✅ | ✅ | ❌ | +| Speech | ✅ | ✅ | ✅ | In addition, we have ported OpenAI's Whisper speech recognition model to the `MaixCAM2`, enabling powerful speech-to-text functionality even on resource-constrained devices. diff --git a/docs/doc/en/projects/README.md b/docs/doc/en/projects/README.md index d5352f17..67d9bf72 100644 --- a/docs/doc/en/projects/README.md +++ b/docs/doc/en/projects/README.md @@ -42,52 +42,57 @@ These are usually complete projects that include source code, documentation, dem Applications pre-installed on platforms such as `MaixCAM`, `MaixCAM Pro`, and `MaixCAM2` -| Built-in Application | Supported Platforms | Description | Documentation | -| --------------- | ------------------------------------ | -------------------------------------- | ------------ | -| Benchmark | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Tests the comprehensive performance of CPU/NPU and other hardware | [Docs]() | -| Local Chat | `MaixCAM2` | Offline chat | [Docs]() | -| Desktop Monitor | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Host performance monitoring widget | [Docs]() | -| Face Emotion | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Emotion recognition | [Docs]() | -| Face Landmarks | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Keypoint recognition | [Docs]() | -| Face Recognizer | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Face recognition | [Docs]() | -| Face tracking | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Face tracking | [Docs]() | -| Gesture Classifier | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Recognizes different gestures | [Docs]() | -| Hand Landmarks | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Hand keypoint detection | [Docs]() | -| HTTP File Browser | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | View and download files via a browser | [Docs]() | -| Human Pose | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Human pose recognition | [Docs]() | -| Pose Classifier | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Recognizes different human poses | [Docs]() | -| Image Generation | `MaixCAM2` | Text-to-image, image-to-image | [Docs]() | -| IMU AHRS | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Calculates IMU data | [Docs]() | -| MaixHub Client | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Interacts with MaixHub | [Docs]() | -| Depth Estimation | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Monocular depth estimation | [Docs]() | -| Mouse Simulator | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Uses the device as a mouse | [Docs]() | -| OCR | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Text recognition | [Docs]() | -| RTMP Live | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | RTMP streaming | [Docs]() | -| RTSP Stream | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | RTSP streaming | [Docs]() | -| Scan QR Code | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Scans and recognizes barcodes, QR codes, Apriltag labels | [Docs]() | -| Self Learn Classifier | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Learns and classifies targets | [Docs]() | -| Self Learn Tracker | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Learns and detects targets | [Docs]() | -| Speech Recognition | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Speech-to-text | [Docs]() | -| Thermal256 Camera | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | External thermal imaging module | [Docs]() | -| Thermal Night Vision | `MaixCAM2` | Fusion of thermal imaging and AI night vision | [Docs]() | -| Tracker Counter | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | People/object counting | [Docs]() | -| USB Hand Contrl | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Controls the mouse via gestures | [Docs]() | -| USB Pose Mario | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Controls the keyboard via human pose | [Docs]() | -| Local VLM | `MaixCAM2` | Image-to-text | [Docs]() | -| WebRTC Stream | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | WebRTC streaming | [Docs]() | -| YOLO11 OBB | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Image detection with rotation angle | [Docs]() | -| YOLO11 Seg | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Image segmentation | [Docs]() | -| YOLO-World | `MaixCAM2` | YOLO-World detection | [Docs]() | -| Camera | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Photo taking, video recording | [Docs]() | -| Photos | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Displays photos | [Docs]() | -| AI Classifier | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | AI classification | [Docs]() | -| AI Detector | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | AI detection | [Docs]() | -| Find blobs | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Finds color blobs | [Docs]() | -| Line tracking | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Find line | [Docs]() | -| Speech Recognition | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Speech-to-text | [Docs]() | -| Thermal Camera | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | External infrared camera module (PMOD_Thermal32) | [Docs]() | -| ToF Camera | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | External ToF module (ToF100) | [Docs]() | -| UVC Camera | `MaixCAM`, `MaixCAM Pro` | Serves as a USB camera | [Docs]() | +Here is the translation of the table into English: + +| Built-in Application | Supported Platforms | Description | Documentation | +| :--- | :--- | :--- | :--- | +| **Benchmark Test** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Tests comprehensive performance of CPU/NPU and other hardware | [Documentation](https://maixhub.com/app/188) | +| **Local Chat** | `MaixCAM2` | Offline voice chat | [Documentation](https://maixhub.com/app/187) | +| **Desktop Monitor** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Host performance monitoring gadget | [Documentation](https://maixhub.com/app/13) | +| **Facial Emotion** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Emotion recognition | [Documentation](https://maixhub.com/app/189) | +| **Facial Key Points** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Key point recognition | [Documentation](https://maixhub.com/app/186) | +| **Face Recognition** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Face recognition | [Documentation](https://maixhub.com/app/190) | +| **Face Tracking** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Face tracking | [Documentation](https://maixhub.com/app/31) | +| **Gesture Classification** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Recognize different gestures | [Documentation](https://maixhub.com/app/192) | +| **Hand Key Points** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Hand key point detection | [Documentation](https://maixhub.com/app/227) | +| **HTTP File Browser** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | View and download files via browser | [Documentation](https://maixhub.com/app/59) | +| **Human Pose** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Human pose recognition | [Documentation](https://maixhub.com/app/191) | +| **Human Pose Classification** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Recognize different human poses | [Documentation](https://maixhub.com/app/193) | +| **Image Generation** | `MaixCAM2` | Text-to-Image, Image-to-Image | [Documentation](https://maixhub.com/app/198) | +| **Pose Calculation** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | IMU data calculation | [Documentation](https://maixhub.com/app/128) | +| **MaixHub Client** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Interact with MaixHub | [Documentation](https://maixhub.com/app/48) | +| **Depth Estimation** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Monocular depth estimation | [Documentation](https://maixhub.com/app/195) | +| **Mouse Simulation** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Use device as a mouse | [Documentation](https://maixhub.com/app/196) | +| **Text Recognition** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Text recognition (OCR) | [Documentation](https://maixhub.com/app/70) | +| **RTMP Live Streaming** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | RTMP streaming | [Documentation](https://maixhub.com/app/35) | +| **RTSP Streaming** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | RTSP streaming | [Documentation](https://maixhub.com/app/197) | +| **QR Code Scanner** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Scan and recognize barcodes, QR codes, Apriltag | [Documentation](https://maixhub.com/app/199) | +| **Self-learning Classification** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Learn targets and classify | [Documentation](https://maixhub.com/app/200) | +| **Self-learning Detector** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Learn targets and detect | [Documentation](https://maixhub.com/app/62) | +| **Speech Recognition** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Speech to text | [Documentation](https://maixhub.com/app/65) | +| **Thermal Camera 256** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | External thermal imaging module | [Documentation](https://maixhub.com/app/208) | +| **Thermal Fusion Night Vision** | `MaixCAM2` | Fusion of thermal camera and AI night vision | [Documentation](https://maixhub.com/app/228) | +| **Tracking Count** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | People flow counting | [Documentation](https://maixhub.com/app/61) | +| **Gesture Control Mouse** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Control mouse via gestures | [Documentation](https://maixhub.com/app/223) | +| **Pose Control Keyboard** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Control keyboard via human pose | [Documentation](https://maixhub.com/app/178) | +| **Local Visual LLM** | `MaixCAM2` | Image-to-text | [Documentation](https://maixhub.com/app/194) | +| **WebRTC Streaming** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | WebRTC streaming | [Documentation](https://maixhub.com/app/202) | +| **OBB Detection** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Image detection with rotation angle | [Documentation](https://maixhub.com/app/203) | +| **YOLO11 Segmentation** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Image segmentation | [Documentation](https://maixhub.com/app/204) | +| **YOLO-World** | `MaixCAM2` | YOLO-World detection | [Documentation](https://maixhub.com/app/229) | +| **Camera** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Take photos, record videos | [Documentation](https://maixhub.com/app/221) | +| **Album** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Display photos | [Documentation](https://maixhub.com/app/222) | +| **AI Classifier** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | AI classification | [Documentation](https://maixhub.com/app/211) | +| **AI Detector** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | AI detection | [Documentation](https://maixhub.com/app/213) | +| **Find Color Blocks** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Find color blocks | [Documentation](https://maixhub.com/app/33) | +| **Line Patrol** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Find lines | [Documentation](https://maixhub.com/app/215) | +| **Speech Recognition (Maix-Speech)** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | Speech to text | [Documentation](https://maixhub.com/app/216) | +| **Speech Recognition (AI LLM)** | `MaixCAM2` | Speech to text | [Documentation](https://maixhub.com/app/217) | +| **Thermal Camera** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | External thermal camera module (PMOD_Thermal32) | [Documentation](https://maixhub.com/app/218) | +| **ToF Camera** | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | External ToF module (ToF100) | [Documentation](https://maixhub.com/app/219) | +| **UVC Camera** | `MaixCAM`, `MaixCAM Pro` | Act as USB camera | [Documentation](https://maixhub.com/app/220) | +| **App Store** | `MaixCAM`, `MaixCAM Pro` | Install other applications | [Documentation](https://maixhub.com/app/225) | +| **Settings** | `MaixCAM`, `MaixCAM Pro` | Modify system settings | [Documentation](https://maixhub.com/app/224) | ### Tools diff --git a/docs/doc/zh/audio/recognize.md b/docs/doc/zh/audio/recognize.md index 1c995263..ccc228b0 100644 --- a/docs/doc/zh/audio/recognize.md +++ b/docs/doc/zh/audio/recognize.md @@ -23,7 +23,7 @@ update: | ------- | ------- | ----------- | -------- | | Whisper | ❌ | ❌ | ✅ | | Sensevoice | ❌ | ❌ | ✅ | -| Speech | ✅ | ✅ | ❌ | +| Speech | ✅ | ✅ | ✅ | ## 使用Whisper做语音转文字 diff --git a/docs/doc/zh/projects/README.md b/docs/doc/zh/projects/README.md index d6921970..de3c9312 100644 --- a/docs/doc/zh/projects/README.md +++ b/docs/doc/zh/projects/README.md @@ -46,51 +46,53 @@ title: MaixCAM MaixPy 项目实战 介绍和汇总 | 内置应用 | 已支持的平台 | 说明 | 文档 | | ------------------------------------------------------------ | ------------ | -------- | -------------- | -| 跑分测试 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 测试CPU/NPU等硬件的综合性能 | [说明文档]() | -| 本地聊天 | `MaixCAM2` | 离线语音聊天 | [说明文档]() | -| 桌面监视器 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 主机性能监控小工具 | [说明文档]() | -| 人脸情绪 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 情绪识别 | [说明文档]() | -| 人脸关键点 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 关键点识别 | [说明文档]() | -| 人脸识别 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 人脸识别 | [说明文档]() | -| 人脸追踪 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 人脸追踪 | [说明文档]() | -| 手势分类 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 识别不同的手势 | [说明文档]() | -| 手关键点 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 手部的关键点检测 | [说明文档]() | -| HTTP 文件浏览器 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 通过浏览器查看和下载文件 | [说明文档]() | -| 人体姿态 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 人体姿态识别 | [说明文档]() | -| 人体姿态分类 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 识别不同的人体姿态 | [说明文档]() | -| 图像生成 | `MaixCAM2` | 文生图, 图生图 | [说明文档]() | -| 姿态解算 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 解算 IMU 数据 | [说明文档]() | -| MaixHub 客户端 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 与MaixHub交互 | [说明文档]() | -| 深度估计 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 单目深度估计 | [说明文档]() | -| 鼠标模拟 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 将设备作为鼠标使用 | [说明文档]() | -| 文字识别 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 文字识别 | [说明文档]() | -| RTMP 直播 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | RTMP 推流 | [说明文档]() | -| RTSP 推流 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | RTSP 推流 | [说明文档]() | -| 扫描二维码 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 扫描和识别条形码, 二维码, Apriltag标签 | [说明文档]() | -| 自学习分类 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 学习目标并分类 | [说明文档]() | -| 自学习检测器 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 学习目标并检测 | [说明文档]() | -| 语音识别 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 语音转文本 | [说明文档]() | -| 热成像仪256 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 外接热成像模组 | [说明文档]() | -| 热融合夜视仪 | `MaixCAM2` | 热成像仪与AI夜视的融合 | [说明文档]() | -| 跟踪计数 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 人流计数 | [说明文档]() | -| 手势控制鼠标 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 通过手势控制鼠标 | [说明文档]() | -| 姿态控制键盘 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 通过人体姿态控制键盘 | [说明文档]() | -| 本地视觉大模型 | `MaixCAM2` | 图生文 | [说明文档]() | -| WebRTC 推流 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | WebRTC推流 | [说明文档]() | -| OBB 检测 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 图像检测,结果带旋转角度 | [说明文档]() | -| YOLO11 分割 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 图像分割 | [说明文档]() | -| YOLO-World | `MaixCAM2` | YOLO-World 检测 | [说明文档]() | -| 相机 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 拍照, 录像 | [说明文档]() | -| 相册 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 显示照片 | [说明文档]() | -| AI 分类器 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | AI分类 | [说明文档]() | -| AI 检测器 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | AI检测 | [说明文档]() | -| 寻找色块 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 找色块 | [说明文档]() | -| 巡线 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 找直线 | [说明文档]() | -| 语音识别 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 语音转文本 | [说明文档]() | -| 热成像仪 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 外接红外摄像头模组(PMOD_Thermal32) | [说明文档]() | -| ToF相机 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 外接ToF模组(ToF100) | [说明文档]() | -| UVC相机 | `MaixCAM`, `MaixCAM Pro` | 作为 usb 摄像头 | [说明文档]() | - +| 跑分测试 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 测试CPU/NPU等硬件的综合性能 | [说明文档](https://maixhub.com/app/188) | +| 本地聊天 | `MaixCAM2` | 离线语音聊天 | [说明文档](https://maixhub.com/app/187) | +| 桌面监视器 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 主机性能监控小工具 | [说明文档](https://maixhub.com/app/13) | +| 人脸情绪 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 情绪识别 | [说明文档](https://maixhub.com/app/189) | +| 人脸关键点 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 关键点识别 | [说明文档](https://maixhub.com/app/186) | +| 人脸识别 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 人脸识别 | [说明文档](https://maixhub.com/app/190) | +| 人脸追踪 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 人脸追踪 | [说明文档](https://maixhub.com/app/31) | +| 手势分类 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 识别不同的手势 | [说明文档](https://maixhub.com/app/192) | +| 手关键点 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 手部的关键点检测 | [说明文档](https://maixhub.com/app/227) | +| HTTP 文件浏览器 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 通过浏览器查看和下载文件 | [说明文档](https://maixhub.com/app/59) | +| 人体姿态 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 人体姿态识别 | [说明文档](https://maixhub.com/app/191) | +| 人体姿态分类 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 识别不同的人体姿态 | [说明文档](https://maixhub.com/app/193) | +| 图像生成 | `MaixCAM2` | 文生图, 图生图 | [说明文档](https://maixhub.com/app/198) | +| 姿态解算 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 解算 IMU 数据 | [说明文档](https://maixhub.com/app/128) | +| MaixHub 客户端 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 与MaixHub交互 | [说明文档](https://maixhub.com/app/48) | +| 深度估计 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 单目深度估计 | [说明文档](https://maixhub.com/app/195) | +| 鼠标模拟 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 将设备作为鼠标使用 | [说明文档](https://maixhub.com/app/196) | +| 文字识别 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 文字识别 | [说明文档](https://maixhub.com/app/70) | +| RTMP 直播 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | RTMP 推流 | [说明文档](https://maixhub.com/app/35) | +| RTSP 推流 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | RTSP 推流 | [说明文档](https://maixhub.com/app/197) | +| 扫描二维码 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 扫描和识别条形码, 二维码, Apriltag标签 | [说明文档](https://maixhub.com/app/199) | +| 自学习分类 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 学习目标并分类 | [说明文档](https://maixhub.com/app/200) | +| 自学习检测器 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 学习目标并检测 | [说明文档](https://maixhub.com/app/62) | +| 语音识别 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 语音转文本 | [说明文档](https://maixhub.com/app/65) | +| 热成像仪256 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 外接热成像模组 | [说明文档](https://maixhub.com/app/208) | +| 热融合夜视仪 | `MaixCAM2` | 热成像仪与AI夜视的融合 | [说明文档](https://maixhub.com/app/228) | +| 跟踪计数 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 人流计数 | [说明文档](https://maixhub.com/app/61) | +| 手势控制鼠标 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 通过手势控制鼠标 | [说明文档](https://maixhub.com/app/223) | +| 姿态控制键盘 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 通过人体姿态控制键盘 | [说明文档](https://maixhub.com/app/178) | +| 本地视觉大模型 | `MaixCAM2` | 图生文 | [说明文档](https://maixhub.com/app/194) | +| WebRTC 推流 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | WebRTC推流 | [说明文档](https://maixhub.com/app/202) | +| OBB 检测 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 图像检测,结果带旋转角度 | [说明文档](https://maixhub.com/app/203) | +| YOLO11 分割 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 图像分割 | [说明文档](https://maixhub.com/app/204) | +| YOLO-World | `MaixCAM2` | YOLO-World 检测 | [说明文档](https://maixhub.com/app/229) | +| 相机 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 拍照, 录像 | [说明文档](https://maixhub.com/app/221) | +| 相册 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 显示照片 | [说明文档](https://maixhub.com/app/222) | +| AI 分类器 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | AI分类 | [说明文档](https://maixhub.com/app/211) | +| AI 检测器 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | AI检测 | [说明文档](https://maixhub.com/app/213) | +| 寻找色块 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 找色块 | [说明文档](https://maixhub.com/app/33) | +| 巡线 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 找直线 | [说明文档](https://maixhub.com/app/215) | +| 语音识别(Maix-Speech) | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 语音转文本 | [说明文档](https://maixhub.com/app/216) | +| 语音识别(AI 大模型) | `MaixCAM2` | 语音转文本 | [说明文档](https://maixhub.com/app/217) | +| 热成像仪 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 外接红外摄像头模组(PMOD_Thermal32) | [说明文档](https://maixhub.com/app/218) | +| ToF相机 | `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` | 外接ToF模组(ToF100) | [说明文档](https://maixhub.com/app/219) | +| UVC相机 | `MaixCAM`, `MaixCAM Pro` | 作为 usb 摄像头 | [说明文档](https://maixhub.com/app/220) | +| 应用商店 | `MaixCAM`, `MaixCAM Pro` | 安装其他应用 | [说明文档](https://maixhub.com/app/225) | +| 设置 | `MaixCAM`, `MaixCAM Pro` | 修改系统设置 | [说明文档](https://maixhub.com/app/224) | diff --git a/projects/app_benchmark/README.md b/projects/app_benchmark/README.md index d0b8d067..0ce9b8ee 100644 --- a/projects/app_benchmark/README.md +++ b/projects/app_benchmark/README.md @@ -1,9 +1,75 @@ -MaixPy benchmark APP -===== +## 简介 +这是一款运行在MaixCam设备上的性能测试应用,能够自动加载预置测试用例,通过可视化界面完成测试切换与执行,并自动保存测试结果。应用适合用于Maix设备性能评估、固件验证、算法基准测试等场景的演示和实际使用。 +支持平台: `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` -MaixPy firmware must > 4.11.10 +## 主要功能 +这性能测试应用可用于评估设备能力,可加载 OpenCV、Display、NPU、Stress 测试,支持按钮点击和屏幕滑动两种便捷方式切换用例,测试完成后能实时预览结果并自动保存至设备本地,展示的结果会自动保存。 +根据你的代码,这是一个基准测试程序框架,通过动态加载 `benchmarks/` 目录下的测试模块来运行不同的性能测试。以下是这四类测试的介绍: +## OpenCV 测试 +![alt text](./assets/opencv.png) +测试设备的图像处理能力,包括: +- 图像滤波(高斯模糊、中值滤波等) +- 边缘检测(Canny、Sobel) +- 形态学操作(腐蚀、膨胀) +- 图像变换(缩放、旋转、仿射变换) +这类测试主要衡量 CPU 在传统计算机视觉算法上的执行效率。 + +## Display 测试 +![alt text](./assets/display.png) + +测试显示子系统的性能,主要评估: +- 帧率(FPS)能力 +- 图像渲染速度 +- 不同分辨率下的刷新效率 +- 图像格式转换开销 + +反映设备的图形输出和显示驱动性能。 + +## NPU 测试 +![alt text](./assets/NPU.png) + +测试神经网络处理单元的推理能力,包括: +- 模型加载时间 +- 单次推理延迟 +- 吞吐量(每秒推理次数) +- 不同模型架构的性能对比 + +这是评估设备 AI 加速能力的核心指标,直接影响目标检测、图像分类等深度学习应用的实际表现。 + +## Stress 测试 +![alt text](./assets/Stress.png) + +压力测试,评估设备在高负载下的稳定性: +- 长时间满载运行的稳定性 +- 温度控制与降频表现 +- 内存压力下的系统响应 +- 多任务并发处理能力 + +用于检验设备的散热设计和系统可靠性,判断能否支撑长时间高强度工作。 + +## 使用说明 + +### 操作说明 +![alt text](./assets/image.png) +1. **退出应用**:点击屏幕左上角的返回按钮(< 图标),或直接按下设备按键,即可退出应用 +2. **切换测试用例**:支持两种切换方式,可按需选择: + - **按钮切换**:点击屏幕左下角的「Mode」显示区域,可在所有预置测试用例间循环切换 + - **滑动切换**:在屏幕上左右滑动(滑动距离需超过屏幕宽度1/5),向右滑动切换上一个测试,向左滑动切换下一个测试 +3. **启动测试**:确认选中目标测试用例后,点击屏幕右下角的「Start」按钮,应用将自动执行该测试用例 +4. **查看测试结果**:测试执行完成后,将自动进入结果预览界面: + - 点击屏幕任意区域(除返回按钮外),可循环切换多组测试结果 + - 点击屏幕左下角的返回按钮(< 图标),可退出结果预览,返回主界面重新选择测试用例 + +## 注意事项 +1. 请确保设备摄像头、触屏功能正常,无遮挡或损坏 +2. 测试效果受设备性能、存储空间、运行环境等因素影响 +3. 测试结果会自动保存到「/root/benchmark/」目录,长期使用请及时清理无用结果文件,避免存储空间不足 + + +## 源码 +- [源码](https://github.com/sipeed/MaixPy/projects/app_benchmark) diff --git a/projects/app_benchmark/README_EN.md b/projects/app_benchmark/README_EN.md new file mode 100644 index 00000000..37b4dafe --- /dev/null +++ b/projects/app_benchmark/README_EN.md @@ -0,0 +1,82 @@ +# MaixCam 性能测试应用翻译(中英对照,专业技术术语精准适配) +## Introduction +This is a performance testing application running on MaixCam devices. It can automatically load preset test cases, complete test switching and execution through a visual interface, and automatically save test results. This application is suitable for demonstration and practical use in scenarios such as Maix device performance evaluation, firmware verification, and algorithm benchmarking. + +Supported Platforms: `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` + +## Key Features +This performance testing application is designed to evaluate device capabilities. It supports loading OpenCV, Display, NPU, and Stress tests, and provides two convenient ways to switch test cases: button click and screen swipe. After the test is completed, the results can be previewed in real time and automatically saved to the local device, with all displayed results being stored persistently. + +Based on the code, this is a benchmark testing framework that runs different performance tests by dynamically loading test modules in the `benchmarks/` directory. Below is an introduction to these four types of tests: + +## OpenCV Test + +![alt text](./assets/opencv.png) + +This test evaluates the device's image processing capabilities, including: +- Image filtering (Gaussian blur, median filtering, etc.) +- Edge detection (Canny, Sobel) +- Morphological operations (Erosion, Dilation) +- Image transformation (Scaling, Rotation, Affine transformation) + +This type of test mainly measures the CPU's execution efficiency in traditional computer vision algorithms. + +## Display Test +![alt text](./assets/display.png) + +This test evaluates the performance of the display subsystem, focusing on: +- Frame rate (FPS) capability +- Image rendering speed +- Refresh efficiency at different resolutions +- Image format conversion overhead + +It reflects the device's graphics output and display driver performance. + +## NPU Test +![alt text](./assets/NPU.png) + +This test evaluates the inference capability of the Neural Processing Unit, including: +- Model loading time +- Single inference latency +- Throughput (number of inferences per second) +- Performance comparison of different model architectures + +This is a core indicator for evaluating the device's AI acceleration capability, which directly affects the actual performance of deep learning applications such as object detection and image classification. + +## Stress Test +![alt text](./assets/Stress.png) + +Stress testing evaluates the device's stability under high load: +- Stability during long-term full-load operation +- Temperature control and frequency throttling performance +- System response under memory pressure +- Multi-task concurrent processing capability + +It is used to verify the device's thermal design and system reliability, and to determine whether it can support long-term high-intensity work. + +## User Guide + +### Operation Instructions +![alt text](./assets/image.png) +1. **Exit the Application**: Click the back button (< icon) in the upper left corner of the screen, or directly press the device button to exit the application. +2. **Switch Test Cases**: Two switching methods are supported for your convenience: + - **Button Switching**: Click the "Mode" display area in the lower left corner of the screen to cycle through all preset test cases. + - **Swipe Switching**: Swipe left or right on the screen (the swipe distance must exceed 1/5 of the screen width). Swipe right to switch to the previous test, and swipe left to switch to the next test. +3. **Start the Test**: After confirming the target test case is selected, click the "Start" button in the lower right corner of the screen. The application will automatically execute the selected test case. +4. **View Test Results**: After the test is completed, the system will automatically enter the result preview interface: + - Click any area of the screen (except the back button) to cycle through multiple sets of test results. + - Click the back button (< icon) in the lower left corner of the screen to exit the result preview and return to the main interface to select a test case again. + +## Notes +1. Please ensure that the device's camera and touch screen functions are normal, with no obstructions or damage. +2. Test results are affected by factors such as device performance, storage space, and operating environment. +3. Test results are automatically saved to the `/root/benchmark/` directory. For long-term use, please clean up unnecessary result files in a timely manner to avoid insufficient storage space. + +## Source Code +- [Source Code](https://github.com/sipeed/MaixPy/projects/app_benchmark) + +### 翻译说明 +1. **技术术语精准性**:针对嵌入式设备、计算机视觉、AI 推理相关术语采用行业通用译法(如 NPU = Neural Processing Unit、FPS = Frame Rate、Affine transformation = 仿射变换、inference latency = 推理延迟)。 +2. **句式适配**:将中文的流水句、主动句式转换为英文技术文档常用的被动句式、定语从句,保证行文严谨、符合技术文档规范。 +3. **操作描述清晰性**:对屏幕操作、路径等内容进行精准还原(如「滑动距离需超过屏幕宽度1/5」译为「the swipe distance must exceed 1/5 of the screen width」,「/root/benchmark/」目录保留原路径格式)。 +4. **一致性**:同类术语、操作描述保持前后统一(如「测试用例」均译为「test case」,「返回按钮」均译为「back button」)。 \ No newline at end of file diff --git a/projects/app_benchmark/assets/NPU.png b/projects/app_benchmark/assets/NPU.png new file mode 100644 index 00000000..717fbfac Binary files /dev/null and b/projects/app_benchmark/assets/NPU.png differ diff --git a/projects/app_benchmark/assets/Stress.png b/projects/app_benchmark/assets/Stress.png new file mode 100644 index 00000000..600a1410 Binary files /dev/null and b/projects/app_benchmark/assets/Stress.png differ diff --git a/projects/app_benchmark/assets/display.png b/projects/app_benchmark/assets/display.png new file mode 100644 index 00000000..55f79358 Binary files /dev/null and b/projects/app_benchmark/assets/display.png differ diff --git a/projects/app_benchmark/assets/image.png b/projects/app_benchmark/assets/image.png new file mode 100644 index 00000000..45b95bb8 Binary files /dev/null and b/projects/app_benchmark/assets/image.png differ diff --git a/projects/app_benchmark/assets/opencv.png b/projects/app_benchmark/assets/opencv.png new file mode 100644 index 00000000..9a42dd57 Binary files /dev/null and b/projects/app_benchmark/assets/opencv.png differ diff --git a/projects/app_chat/README.md b/projects/app_chat/README.md index 93ea7a3d..25705020 100644 --- a/projects/app_chat/README.md +++ b/projects/app_chat/README.md @@ -1,7 +1,33 @@ -AI Chat - -Offline AI Chat - - - - +## 简介 +这是一款运行在MaixCam2设备上的**全离线语音交互应用**,无需连接网络即可完成“语音输入→文字转写→智能回复”的完整流程。应用操作简洁,界面直观,适合现场快速查询、离线语音助手等日常场景使用,无需专业技术背景即可上手。 + +## 主要功能 +1. **离线语音录制**:直接在设备上采集语音信息,无需外接录音设备,录制过程简单便捷。 +2. **语音转文字**:自动将录制的语音转换成清晰可阅读的文字内容,无需手动输入。 +3. **智能文字回复**:针对转写后的文字内容,自动生成贴合语境的自然语言回复,满足日常咨询、信息整理等需求。 +4. **可视化交互**:通过设备屏幕直观查看录制状态、转写结果和智能回复,操作反馈清晰可见。 +5. **安全便捷退出**:支持一键退出应用,自动清理设备运行资源,避免占用设备内存。 + +## 使用说明 +![](./assets/AI_chat.png) +### 录制语音 + - 主界面下方会显示录制按钮,并有“Long press me”(长按我)的提示文字。 + - 用手指**长按录制按钮**(按住不放,持续约1秒即可触发),此时屏幕会提示“Recording..”(正在录制),说明已进入录音状态。 + - 录制完成后,松开手指即可停止录音,屏幕会切换为“Transcribing ...”(正在转写文字)。 +### 查看结果 + - 语音停止录制后,应用会自动完成“语音转文字”和“智能回复”两个步骤,无需额外操作。 + - 屏幕中间的文本区域会依次显示你的语音转写文字、以及应用生成的智能回复内容,直接查看即可。 +### 退出应用 + - 主界面左上角有一个退出按钮(小图标)。 + - 用手指点击该退出按钮,应用会自动清理资源并正常退出,返回设备主界面。 + +## 注意事项 +1. 设备要求:仅支持4GB内存版本的Maix设备,低内存设备无法正常运行该应用。 +2. 前置配置:使用前必须关闭设备的“AI ISP”功能(路径:设备「设置」应用→找到「AI ISP」选项→选择「关闭」),未关闭将无法启动应用。 +3. 录制环境:尽量在安静环境下录制语音,嘈杂环境会影响语音转写的准确率。 +4. 操作规范:录制语音时需长按按钮,短按无法触发录制功能;加载过程中请勿断电或强制关闭应用,避免设备出现异常。 + + +## 更多介绍 +APP源码: [源码](https://github.com/sipeed/MaixPy/tree/main/projects/app_chat) +人脸关键点检测文档: [MaixPy MaixCAM 运行 Qwen 大语言模型](https://wiki.sipeed.com/maixpy/doc/zh/mllm/llm_qwen.html) \ No newline at end of file diff --git a/projects/app_chat/README_EN.md b/projects/app_chat/README_EN.md new file mode 100644 index 00000000..36d94e14 --- /dev/null +++ b/projects/app_chat/README_EN.md @@ -0,0 +1,34 @@ +## Introduction +This is an **all-offline voice interaction application** running on MaixCam2 devices. It completes the full process of "Voice Input → Transcription → Intelligent Response" without requiring an internet connection. The application features simple operation and an intuitive interface, making it suitable for scenarios such as quick on-site queries and offline voice assistants. It is user-friendly and requires no specialized technical background. + +## Main Features +1. **Offline Voice Recording**: Capture voice information directly on the device without needing an external recording device; the recording process is simple and convenient. +2. **Speech-to-Text**: Automatically converts recorded speech into clear, readable text, eliminating the need for manual input. +3. **Intelligent Text Responses**: Automatically generates contextually appropriate natural language replies based on the transcribed text, suitable for daily inquiries and information organization. +4. **Visual Interaction**: Intuitively view recording status, transcription results, and intelligent responses via the device screen, providing clear operational feedback. +5. **Safe and Convenient Exit**: Supports one-click exit, which automatically cleans up device resources to prevent memory bloat. + +## User Guide +![](./assets/AI_chat.png) +### Recording Voice +* The recording button is located at the bottom of the main interface, with the prompt text "Long press me". +* **Long press the recording button** (press and hold for approximately 1 second to trigger). The screen will display "Recording..." to indicate that recording has started. +* To finish recording, simply release your finger. The screen will then switch to display "Transcribing ...". + +### Viewing Results +* After stopping the recording, the application automatically completes the "Speech-to-Text" and "Intelligent Response" steps; no additional action is required. +* The text area in the middle of the screen will sequentially display your transcribed text followed by the application's generated intelligent response. + +### Exiting the Application +* There is an exit button (small icon) in the top-left corner of the main interface. +* Tap this exit button with your finger. The application will automatically clean up resources and exit normally, returning you to the device's home screen. + +## Notes +1. **Device Requirements**: Only supports Maix devices with **4GB of RAM**. The application cannot run normally on low-memory devices. +2. **Prerequisite Configuration**: You **must disable** the "AI ISP" function before use (Path: Device [Settings] App → Find [AI ISP] option → Select [Off]). The application will fail to launch if this is not disabled. +3. **Recording Environment**: Try to record in a quiet environment. Background noise can affect transcription accuracy. +4. **Operation Norms**: You must long press the button to record; a short press will not trigger the function. Do not power off or force close the application during loading to avoid device exceptions. + +## More Information +APP Source Code: [Source Code](https://github.com/sipeed/MaixPy/tree/main/projects/app_chat) +Documentation: [Running Qwen LLM on MaixPy MaixCAM](https://wiki.sipeed.com/maixpy/doc/en/mllm/llm_qwen.html) \ No newline at end of file diff --git a/projects/app_chat/assets/AI_chat.png b/projects/app_chat/assets/AI_chat.png new file mode 100644 index 00000000..fbf93cbf Binary files /dev/null and b/projects/app_chat/assets/AI_chat.png differ diff --git a/projects/app_face_emotion/README.md b/projects/app_face_emotion/README.md index bb7e7264..b7d22320 100644 --- a/projects/app_face_emotion/README.md +++ b/projects/app_face_emotion/README.md @@ -1,7 +1,31 @@ -Face landmarks detection -===== +## 简介 +本程序是基于YOLOv8人脸检测模型与专用人脸情绪分类模型,实现了实时的人脸捕捉与情绪状态判定。无需复杂的额外配置,开箱即可使用,适用于快速的人脸情绪场景验证、简易人机交互辅助等场景。 +## 主要功能 +1. **实时人脸检测**:通过YOLOv8轻量化人脸检测模型,能够从摄像头实时采集的画面中,快速识别并定位人脸区域,支持多个人脸同时检测。 +2. **人脸情绪分类**:对检测到的人脸进行裁剪与预处理后,通过专用情绪分类模型完成情绪判定,输出对应情绪标签及置信度评分。 +3. **详细结果展示**: + - 对首个检测到的人脸,展示全量情绪类别及对应置信度可视化进度条,直观呈现各类情绪的判定概率。 + - 对所有检测到的人脸,标注人脸框及最优匹配情绪标签、置信度,通过颜色区分(绿色为高置信度,红色为低置信度)。 +4. **简易人机交互**:支持触摸屏操作,提供返回退出与模型切换功能,满足不同场景下的使用需求。 +5. **界面辅助元素**:内置返回图标、模型状态显示,界面简洁易懂,无需专业操作经验即可查看结果。 +## 使用说明 +![](./assets/face_emotion_neutral.jpg) +1. **启动程序**:程序启动后将自动初始化摄像头、显示屏与模型,进入实时检测界面。 +2. **查看结果**: + - 摄像头对准人脸区域,程序将自动检测人脸并进行情绪分类,实时在显示屏上刷新结果。 + - 查看首个人脸的全量情绪置信度详情,以及所有人脸的快速标注结果,置信度达到设定阈值将显示绿色标注,未达到则显示红色标注。 +3. **触摸屏操作**: + - **模型切换**:点击显示屏下方显示当前模型名称的矩形区域,程序将提示“switching model ...”,完成模型切换后将刷新检测结果(当前支持7类情绪模型)。 + - **退出程序**:点击显示屏左上角的返回图标,即可触发程序退出流程,关闭应用。 -visit https://wiki.sipeed.com/maixpy/doc/zh/vision/face_landmarks.html +## 注意事项 +1. **使用环境要求**: + - 建议在光线充足、环境稳定的场景下使用,昏暗、强光直射或抖动剧烈的环境会降低人脸检测准确率与情绪分类效果。 + - 人脸需正对摄像头,距离适中,过度遮挡(如口罩、墨镜)将影响检测与分类结果。 +2. **置信度阈值说明**:程序内置检测置信度与情绪置信度阈值,低于阈值的结果将标注为红色,仅作参考,不代表有效情绪判定。 +## 更多介绍 +[源码](https://github.com/sipeed/MaixPy/tree/main/projects/app_face_emotion) +[MaixCAM MaixPy 人脸表情情绪识别、性别、口罩,年龄等识别](https://wiki.sipeed.com/maixpy/doc/zh/vision/face_emotion.html) \ No newline at end of file diff --git a/projects/app_face_emotion/README_EN.md b/projects/app_face_emotion/README_EN.md new file mode 100644 index 00000000..b295dde1 --- /dev/null +++ b/projects/app_face_emotion/README_EN.md @@ -0,0 +1,31 @@ +## Introduction +This program implements real-time face capture and emotional state determination based on the YOLOv8 face detection model and a dedicated facial emotion classification model. It requires no complex additional configuration and is ready to use out of the box, making it suitable for rapid verification of facial emotion scenarios, auxiliary simple human-computer interaction, and other use cases. + +## Key Features +1. **Real-time Face Detection**: Using the lightweight YOLOv8 face detection model, it can quickly identify and locate face regions from real-time images captured by the camera, supporting simultaneous detection of multiple faces. +2. **Facial Emotion Classification**: After cropping and preprocessing the detected faces, the dedicated emotion classification model completes emotion determination and outputs corresponding emotion labels along with confidence scores. +3. **Detailed Result Display**: + - For the first detected face, it displays a visual progress bar of all emotion categories and their corresponding confidence levels, intuitively presenting the judgment probability of each emotion type. + - For all detected faces, it marks the face bounding box, the optimal matching emotion label, and the confidence level, with color differentiation (green for high confidence and red for low confidence). +4. **Simple Human-Computer Interaction**: It supports touch screen operation and provides functions of return/exit and model switching to meet the usage requirements in different scenarios. +5. **Interface Auxiliary Elements**: Built-in return icon and model status display make the interface concise and easy to understand, allowing users to view results without professional operation experience. + +## Usage Instructions +![](./assets/face_emotion_neutral.jpg) +1. **Launch the Program**: After launching, the program will automatically initialize the camera, display screen, and model, and enter the real-time detection interface. +2. **View Results**: + - Align the camera with the face region; the program will automatically detect the face and perform emotion classification, refreshing the results on the display screen in real time. + - View the detailed full emotion confidence of the first face, as well as the quick annotation results of all faces. Results with confidence reaching the set threshold will be displayed with green annotations, while those below the threshold will be displayed with red annotations (for reference only and not representing valid emotion determination). +3. **Touch Screen Operation**: + - **Model Switching**: Click the rectangular area at the bottom of the display screen that shows the current model name. The program will prompt "switching model ...", and refresh the detection results after completing the model switching (7-category emotion model is currently supported). + - **Exit the Program**: Click the return icon in the upper left corner of the display screen to trigger the program exit process and close the application. + +## Notes +1. **Usage Environment Requirements**: + - It is recommended to use the program in well-lit and stable environments. Dim light, direct strong light, or severe jitter will reduce the accuracy of face detection and the effect of emotion classification. + - The face should be facing the camera directly at a moderate distance. Excessive occlusion (such as masks and sunglasses) will affect the detection and classification results. +2. **Confidence Threshold Explanation**: The program has built-in detection confidence and emotion confidence thresholds. Results below the thresholds will be marked in red, which are for reference only and do not represent valid emotion determination. + +## More Information +[Source Code](https://github.com/sipeed/MaixPy/tree/main/projects/app_face_emotion) +[MaixCAM MaixPy Facial Expression Recognition, Gender, Mask, Age, and More](https://wiki.sipeed.com/maixpy/doc/eh/vision/face_emotion.html) \ No newline at end of file diff --git a/projects/app_face_emotion/assets/face_emotion_neutral.jpg b/projects/app_face_emotion/assets/face_emotion_neutral.jpg new file mode 100644 index 00000000..7bf52130 Binary files /dev/null and b/projects/app_face_emotion/assets/face_emotion_neutral.jpg differ diff --git a/projects/app_face_landmarks/README.md b/projects/app_face_landmarks/README.md index bb7e7264..d640106f 100644 --- a/projects/app_face_landmarks/README.md +++ b/projects/app_face_landmarks/README.md @@ -1,7 +1,39 @@ -Face landmarks detection -===== - - - -visit https://wiki.sipeed.com/maixpy/doc/zh/vision/face_landmarks.html - +## 简介 + +这是一款人脸关键点检测应用,能够实时检测摄像头画面中的人脸,并在人脸上标记出不同数量的关键点。应用适合用于人脸分析、表情识别、虚拟试妆等场景的技术演示和测试。 + +支持平台: `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` + +## 主要功能 +**人脸检测**:自动检测画面中的多个人脸(最多4个) + +**关键点标记**:在人脸上标记出关键特征点 + +**关键点模式切换**:支持4种不同密度的关键点模式 + +**实时显示**:在屏幕上实时显示带关键点标记的人脸图像 + +## 使用说明 + +![](./assets/face_landmarks_demo.jpg) + +### 操作说明 +1. **退出应用**:点击屏幕左上角的返回按钮(←图标) +2. **切换关键点模式**:点击屏幕左下角的模式显示区域,可以在4种模式间循环切换: + - **478点模式**:完整的人脸关键点(478个点), 最密集的关键点分布,覆盖人脸所有细节 + - **146点模式**:精选的关键点(146个点), 中等密度的关键点,适合大多数应用场景 + - **68点模式**:常用的人脸关键点(68个点), 标准的人脸关键点数量,常用于面部特征分析 + - **5点模式**:基础的关键点(5个点), 最基本的关键点(如眼睛、鼻子、嘴角等) + +## 注意事项 +1. 请确保摄像头清洁,无遮挡 +2. 检测效果受光线、人脸角度、遮挡物等因素影响 +3. 当画面中出现多个人脸时,最多同时检测4个人脸 +4. 关键点模式切换会立即生效,无需重启应用 +5. 不同关键点模式适用于不同应用场景,请根据需求选择 + +## 更多介绍 + +APP源码: [源码](https://github.com/sipeed/MaixPy/tree/main/projects/app_face_landmarks) +人脸关键点检测文档: [MaixCAM MaixPy 人脸 478 关键点检测](https://wiki.sipeed.com/maixpy/doc/zh/vision/face_landmarks.html) + diff --git a/projects/app_face_landmarks/README_EN.md b/projects/app_face_landmarks/README_EN.md new file mode 100644 index 00000000..83bd2abe --- /dev/null +++ b/projects/app_face_landmarks/README_EN.md @@ -0,0 +1,40 @@ +Here is the translation of the introduction into English: + +## Introduction + +This is a facial landmark detection application capable of real-time detection of human faces in the camera feed and marking them with varying numbers of key points. It is suitable for technical demonstrations and testing in scenarios such as facial analysis, expression recognition, and virtual makeup. + +**Supported Platforms:** `MaixCAM`, `MaixCAM Pro`, `MaixCAM2` + +## Main Features +**Face Detection**: Automatically detects multiple faces in the frame (up to 4). + +**Landmark Marking**: Marks key feature points on detected faces. + +**Landmark Mode Switching**: Supports 4 different density modes for landmarks. + +**Real-time Display**: Displays the camera feed with real-time facial landmark overlays on the screen. + +## User Guide + +![](./assets/face_landmarks_demo.jpg) + +### Operation Instructions +1. **Exit Application**: Click the back button (← icon) in the top-left corner of the screen. +2. **Switch Landmark Modes**: Click the mode display area in the bottom-left corner to cycle through 4 modes: + - **478-point Mode**: Complete facial landmarks (478 points). The densest distribution, covering all facial details. + - **146-point Mode**: Selected landmarks (146 points). Medium density, suitable for most application scenarios. + - **68-point Mode**: Commonly used facial landmarks (68 points). A standard number often used for facial feature analysis. + - **5-point Mode**: Basic landmarks (5 points). The most fundamental points (e.g., eyes, nose, mouth corners). + +## Notes +1. Please ensure the camera lens is clean and unobstructed. +2. Detection effectiveness is influenced by lighting, face angle, and obstructions. +3. When multiple faces appear in the frame, a maximum of 4 will be detected simultaneously. +4. Mode changes take effect immediately; there is no need to restart the application. +5. Different landmark modes are suitable for different scenarios; please choose according to your requirements. + +## More Information + +APP Source Code: [Source Code](https://github.com/sipeed/MaixPy/tree/main/projects/app_face_landmarks) +Facial Landmark Detection Documentation: [MaixCAM MaixPy Face 478-Point Landmark Detection](https://wiki.sipeed.com/maixpy/doc/en/vision/face_landmarks.html) \ No newline at end of file diff --git a/projects/app_face_landmarks/assets/face_landmarks_demo.jpg b/projects/app_face_landmarks/assets/face_landmarks_demo.jpg new file mode 100644 index 00000000..f6ae5c92 Binary files /dev/null and b/projects/app_face_landmarks/assets/face_landmarks_demo.jpg differ diff --git a/projects/app_face_recognizer/.gitignore b/projects/app_face_recognizer/.gitignore index babf76a7..3a99d8b5 100644 --- a/projects/app_face_recognizer/.gitignore +++ b/projects/app_face_recognizer/.gitignore @@ -1,5 +1,3 @@ - -build +data +__pycache__ dist -/CMakeLists.txt - diff --git a/projects/app_face_recognizer/README.md b/projects/app_face_recognizer/README.md new file mode 100644 index 00000000..32aa00c0 --- /dev/null +++ b/projects/app_face_recognizer/README.md @@ -0,0 +1,38 @@ +## 简介 +本应用是运行在Maix设备(支持MaixCam2等)上的人脸识别工具,集成了高性能人脸检测与特征提取模型,能够实现人脸实时识别、人脸信息录入与已录入人脸清空等核心操作,操作简洁直观,无需复杂配置即可快速使用。 + +## 主要功能 +1. **实时人脸检测与识别**:通过设备摄像头实时捕捉画面,自动检测画面中的人脸并进行身份匹配,展示识别结果与匹配置信度。 +2. **人脸信息录入(学习)**:支持将新的未知人脸录入系统,生成唯一身份标识,后续可对该人脸进行精准识别。 +3. **已录入人脸清空**:一键清除所有已录入的人脸信息,恢复应用初始识别状态。 +4. **快捷返回退出**:支持快速退出当前人脸识别功能,返回上一级应用界面。 +5. **录入人脸临时预览**:录入新人脸后,会在画面左上角临时展示录入的人脸图像,方便确认录入效果。 + +## 使用说明 +### 应用启动 +启动应用后,屏幕会先显示“loading ...”加载提示,等待模型与摄像头、触摸屏等硬件初始化完成,加载完成后自动进入实时人脸识别界面,界面会显示摄像头实时画面及三个操作按钮。 + +### 界面按钮说明 +应用界面包含三个核心操作按钮,均支持触摸屏点击操作: +1. **< back 按钮**(左上角):返回按钮,用于退出当前人脸识别界面。 +2. **learn 按钮**(左下角):学习/录入按钮,用于录入新的人脸信息。 +3. **clear 按钮**(右下角):清空按钮,用于清除所有已录入的人脸信息。 +![](./assets/face_recognize.jpg) +### 具体操作步骤 +1. **人脸实时识别**:应用加载完成后,将摄像头对准需要识别的人脸,画面会自动框选检测到的人脸,标注人脸身份(已录入)或未知身份,同时显示匹配置信度(数值越接近1,识别精度越高)。 +2. **新人脸录入**: + - 确保需要录入的人脸出现在摄像头画面中,且被成功检测(画面中出现人脸框选)。 + - 点击触摸屏左下角的“learn”按钮,系统自动提取当前人脸特征并完成录入,生成格式为“id_XXX”的唯一身份标识。 + - 录入成功后,左上角会临时显示录入的人脸预览图(持续5秒),后续该人脸再次出现在画面中时,会被自动识别并标注对应的“id_XXX”身份。 + - 重复上述步骤可录入多人脸信息,系统会自动递增生成新的身份标识。 +3. **已录入人脸清空**:直接点击触摸屏右下角的“clear”按钮,系统会自动清除所有已录入的人脸信息,恢复至仅支持人脸检测、不支持身份匹配的初始状态。 +4. **退出应用功能**:点击触摸屏左上角的“< back”按钮,即可退出当前人脸识别界面,返回上一级应用页面。 + +## 注意事项 +1. 应用启动前请确保设备摄像头无遮挡,触摸屏工作正常,否则可能无法正常完成人脸检测与按钮操作。 +2. 人脸录入时,请保持人脸正对摄像头,光线充足且无强烈逆光,避免人脸被遮挡(如口罩、墨镜等),以提高录入与后续识别的精度。 +3. 录入人脸后,系统会自动保存人脸信息至设备“/root/faces.bin”文件,无需手动备份,再次启动应用时会自动加载该文件中的人脸信息(代码中相关加载逻辑已预留,启用后可实现信息持久化)。 + +## 更多介绍 +[源码](https://github.com/sipeed/MaixPy/tree/main/projects/app_face_recognizer) +[MaixCAM MaixPy 人脸识别](https://wiki.sipeed.com/maixpy/doc/zh/vision/face_recognition.html) \ No newline at end of file diff --git a/projects/app_face_recognizer/README_EN.md b/projects/app_face_recognizer/README_EN.md new file mode 100644 index 00000000..0e1b502f --- /dev/null +++ b/projects/app_face_recognizer/README_EN.md @@ -0,0 +1,39 @@ +## Introduction +This application is a face recognition tool designed for Maix devices (supporting MaixCam2, etc.). It integrates high-performance face detection and feature extraction models to achieve real-time face recognition, face information enrollment, and data clearing. The operation is concise and intuitive, allowing for quick use without complex configuration. + +## Key Features +1. **Real-time Face Detection & Recognition**: Captures real-time video through the device camera, automatically detects faces in the frame, performs identity matching, and displays the recognition results along with the confidence level. +2. **Face Information Enrollment (Learning)**: Supports enrolling new/unknown faces into the system, generating a unique identity identifier for precise recognition in subsequent sessions. +3. **Clear Enrolled Faces**: One-click clearing of all enrolled face information to restore the application to its initial state. +4. **Quick Return/Exit**: Supports quickly exiting the current face recognition function and returning to the previous application interface. +5. **Temporary Preview of Enrolled Faces**: After enrolling a new face, a temporary preview of the enrolled image is displayed in the upper-left corner to facilitate confirmation of the enrollment quality. + +## User Guide +### Application Startup +After launching the application, the screen will first display a "loading ..." prompt while initializing the model, camera, and touchscreen hardware. Once loaded, it automatically enters the real-time face recognition interface, showing the live camera feed and three operation buttons. + +### Interface Button Description +The application interface contains three core operation buttons, all supporting touch input: +1. **< back Button** (Top-left): Return button, used to exit the current face recognition interface. +2. **learn Button** (Bottom-left): Learn/Enroll button, used to register new face information. +3. **clear Button** (Bottom-right): Clear button, used to erase all enrolled face information. +![](./assets/face_recognize.jpg) + +### Specific Operation Steps +1. **Real-time Face Recognition**: After the application loads, point the camera at the face to be recognized. The system will automatically frame the detected face, labeling it as a known identity (enrolled) or "Unknown," along with a matching confidence score (values closer to 1 indicate higher accuracy). +2. **Enrolling a New Face**: + * Ensure the face to be enrolled is in the camera frame and successfully detected (a bounding box appears around the face). + * Click the "learn" button at the bottom-left of the touchscreen. The system automatically extracts the current facial features and completes the enrollment, generating a unique identifier in the format "id_XXX". + * Upon successful enrollment, a temporary preview of the face will appear in the upper-left corner (lasting 5 seconds). Subsequent appearances of this face in the frame will be automatically recognized and labeled with the corresponding "id_XXX". + * Repeat the above steps to enroll multiple faces; the system will automatically increment the identity identifier. +3. **Clearing Enrolled Faces**: Directly click the "clear" button at the bottom-right. The system will automatically clear all enrolled face information, reverting to the initial state where only detection (not identification) is supported. +4. **Exiting the Application**: Click the "< back" button at the top-left to exit the current interface and return to the previous application page. + +## Notes +1. Before starting the application, ensure the device camera is unobstructed and the touchscreen is functioning properly; otherwise, face detection and button operations may fail. +2. When enrolling a face, please keep the face facing the camera directly with sufficient lighting and no strong backlighting. Avoid obstructions (such as masks or sunglasses) to improve the accuracy of enrollment and subsequent recognition. +3. After enrollment, the system automatically saves face information to the device file "/root/faces.bin". Manual backup is not required, and the application will automatically load this data upon restarting (the relevant loading logic is reserved in the code and enables data persistence when activated). + +## More Information +[Source Code](https://github.com/sipeed/MaixPy/tree/main/projects/app_face_recognizer) +[MaixCAM MaixPy Face Recognition](https://wiki.sipeed.com/maixpy/doc/en/vision/face_recognition.html) \ No newline at end of file diff --git a/projects/app_face_recognizer/app.yaml b/projects/app_face_recognizer/app.yaml index 69e4c319..6d5808c0 100644 --- a/projects/app_face_recognizer/app.yaml +++ b/projects/app_face_recognizer/app.yaml @@ -9,3 +9,4 @@ files: - face.png - app.yaml - main.py + - .gitignore \ No newline at end of file diff --git a/projects/app_face_recognizer/assets/face_recognize.jpg b/projects/app_face_recognizer/assets/face_recognize.jpg new file mode 100644 index 00000000..ec5ff7a3 Binary files /dev/null and b/projects/app_face_recognizer/assets/face_recognize.jpg differ diff --git a/projects/app_hand_gesture_classifier/README.md b/projects/app_hand_gesture_classifier/README.md index 5f26edba..4071588a 100644 --- a/projects/app_hand_gesture_classifier/README.md +++ b/projects/app_hand_gesture_classifier/README.md @@ -1,15 +1,58 @@ -The touchscreen is segmented into four sections: +## 1. 简介 +你正在使用的是一套基于嵌入式设备开发的**实时手部姿态识别与分类系统**,该系统通过摄像头采集手部图像,借助预训练的手部关键点检测模型提取手部21个核心关键点信息,再通过线性支持向量机(LinearSVC)对不同手部姿态进行分类识别,能够快速区分多种常见手势(如数字、手势指令等),全程在设备端本地运行,无需依赖云端计算,具备低延迟、高实时性的特点。 -1. The first two are circles located in the upper-right and lower-right corners. +## 2. 主要功能 +1. **实时手部检测与关键点提取**:自动识别摄像头画面中的手部区域,精准提取手部21个核心关键点,并用可视化线条勾勒出手部骨骼轮廓,直观呈现手部姿态。 +2. **多手势分类识别**:支持14种常见手势分类识别,包括数字手势(1、3、4、5、6、9等)、指令手势(点赞、OK、比心、我爱你等),识别结果附带置信度(准确率)与识别耗时。 +3. **双分类模式切换**:提供「精简模式(4类手势)」与「完整模式(14类手势)」两种分类模式,可通过触摸屏快速切换,满足不同场景下的使用需求(精简模式识别更快,完整模式覆盖更广)。 +4. **可视化信息展示**:在屏幕上实时叠加展示手部信息(左手/右手、检测置信度)、手势分类结果(类别名称、类别编号、置信度百分比)、识别耗时(微秒级),同时用不同颜色区分左手(红色)与右手(绿色)。 +5. **训练过程耗时记录**:自动记录模型加载、手势分类、模式切换时的训练/更新耗时,便于了解系统运行性能。 -2. The third section is the area between these two circles. +## 3. 使用说明 +### 3.1 系统启动 -3. The fourth section is the largest, covering the entire left area. +1. 运行该程序后,系统会先显示「Loading... wait up to 10s」加载界面,等待模型、数据集与相关组件初始化完成。 +2. 初始化完成后,摄像头自动启动并实时采集画面,进入正常工作界面。 -Upon pressing them, the display shows the following messages: +![alt text](./assets/image.png) -1. Releasing without moving away will activate them. +### 3.2 基本操作(手势识别) +1. 将手部放置在摄像头可视范围内,保持手部清晰、无遮挡,与摄像头保持适当距离(建议30-50cm)。 +2. 系统会自动检测手部,勾勒出红色/绿色手部骨骼轮廓,并在手部附近显示相关信息: + - 第一行:左手/右手 + 手部检测置信度(0.00-1.00) + - 第二行:识别出的手势名称 + 类别编号 + 识别置信度百分比(越高表示识别越准确) + - 第三行:本次手势识别耗时(微秒级,反映系统实时性) +3. 无需额外操作,系统会持续刷新画面,实现实时动态手势识别。 -2. It indicates the elapsed time since the last training session. +### 3.3 分类模式切换(触摸屏操作) +系统界面提供两个圆形切换按钮,支持4类/14类手势模式切换: +1. **右上角按钮(class 14)**: + - 绿色表示当前处于「14类完整模式」,红色表示当前未处于该模式。 + - 切换操作:长按该圆形按钮,屏幕会显示提示文字「Release to upgrade to class 14 and please wait for Training be done.」,松开手指后,系统会自动完成模型训练更新,完成后提示「success changed to 14」,按钮变为绿色,进入14类手势识别模式。 +2. **右下角按钮(class 4)**: + - 绿色表示当前处于「4类精简模式」,红色表示当前未处于该模式。 + - 切换操作:长按该圆形按钮,屏幕会显示提示文字「Release to retrain to class 4 and please wait for Training be done.」,松开手指后,系统会自动完成模型更新,完成后提示「success changed to 4」,按钮变为绿色,进入4类手势识别模式。 +3. 模式切换过程中请保持设备稳定,等待训练更新完成(耗时参考界面显示的「last_train_time」),不可强制退出程序。 -3. It shows the number of active classes. \ No newline at end of file +### 3.4 系统退出 +直接点击按键退出即可,程序会自动终止循环并释放相关资源。 + +## 4. 注意事项 +1. **环境要求**: + - 建议在光线充足、无强光直射与反光的环境下使用,避免昏暗、逆光环境影响手部检测与识别准确率。 + - 背景尽量简洁,避免复杂背景中有与手部颜色相近的物体,减少误检测。 +2. **手部要求**: + - 手部需保持完整无遮挡,避免手指弯曲过度、手部重叠或被其他物体遮挡,否则会导致关键点提取失败或识别准确率下降。 + - 避免手部快速晃动,保持手势稳定片刻,便于系统精准识别。 + - 左右手可正常识别,系统会自动区分并标注颜色(左手红色、右手绿色)。 +3. **性能提示**: + - 14类完整模式的识别耗时略高于4类精简模式,属于正常现象。 + - 模式切换时的训练更新耗时与设备性能相关,耐心等待即可,不可中途中断。 +5. **触摸屏操作**: + - 点击切换按钮时请确保触摸位置准确(圆形按钮区域内),避免误触导致操作失效。 + - 仅在按钮为红色(未激活对应模式)时长按有效,绿色(已激活)时长按无额外效果。 + +## 5. 更多介绍 +[源码](https://github.com/sipeed/MaixPy/tree/main/projects/app_hand_gesture_classifier) + +[MaixCAM MaixPy 基于手部关键点检测结果进行进行手势分类](https://wiki.sipeed.com/maixpy/doc/zh/vision/hand_gesture_classification.html) \ No newline at end of file diff --git a/projects/app_hand_gesture_classifier/README_EN.md b/projects/app_hand_gesture_classifier/README_EN.md new file mode 100644 index 00000000..4aa7e13b --- /dev/null +++ b/projects/app_hand_gesture_classifier/README_EN.md @@ -0,0 +1,62 @@ +Here is the English translation of the user manual for your hand gesture recognition system. + +*** + +## 1. Introduction +You are using a **Real-time Hand Gesture Recognition and Classification System** developed for embedded devices. The system captures hand images via a camera, extracts 21 core hand key points using a pre-trained hand detection model, and classifies different hand gestures using a Linear Support Vector Machine (LinearSVC). It can quickly distinguish between various common gestures (such as numbers and command gestures). The entire process runs locally on the device without relying on cloud computing, offering low latency and high real-time performance. + +## 2. Main Features +1. **Real-time Hand Detection & Keypoint Extraction**: Automatically identifies hand regions in the camera feed, accurately extracts 21 core hand key points, and visualizes the hand bone structure with lines for an intuitive representation of hand posture. +2. **Multi-gesture Classification**: Supports recognition of 14 common gestures, including number gestures (1, 3, 4, 5, 6, 9, etc.) and command gestures (Thumbs Up, OK, Heart, I Love You, etc.). Recognition results include confidence scores (accuracy) and processing time. +3. **Toggle Between Two Classification Modes**: Offers **"Lite Mode (4 classes)"** and **"Full Mode (14 classes)"**. You can quickly switch via the touchscreen to meet different needs (Lite Mode is faster, while Full Mode offers broader coverage). +4. **Visual Information Display**: Real-time overlay on the screen shows hand information (Left/Right Hand, Detection Confidence), gesture classification results (Class Name, Class ID, Confidence Percentage), and recognition time (in microseconds). Different colors distinguish the Left Hand (Red) from the Right Hand (Green). +5. **Training Time Recording**: Automatically records the time taken for model loading, gesture classification, and training/updating during mode switches, making it easy to monitor system performance. + +## 3. User Guide +### 3.1 System Startup + +1. After running the program, the system will display a loading screen saying *"Loading... wait up to 10s"* while initializing the model, dataset, and related components. +2. Once initialization is complete, the camera starts automatically to capture the feed in real-time, and you will enter the main working interface. + +![alt text](./assets/image.png) + +### 3.2 Basic Operation (Gesture Recognition) +1. Place your hand within the camera's visible range. Keep the hand clear and unobstructed, and maintain an appropriate distance from the camera (recommended 30-50cm). +2. The system will automatically detect the hand, draw the red/green bone structure, and display relevant information near the hand: + * **Line 1**: Left/Right Hand + Hand Detection Confidence (0.00-1.00) + * **Line 2**: Recognized Gesture Name + Class ID + Recognition Confidence Percentage (higher is more accurate) + * **Line 3**: Time taken for the current gesture recognition (in microseconds, reflecting system real-time performance) +3. No extra action is needed; the system continuously refreshes the frame for dynamic gesture recognition. + +### 3.3 Switching Classification Modes (Touchscreen Operation) +The interface provides two circular toggle buttons to switch between 4-class and 14-class gesture modes: +1. **Top-right Button (class 14)**: + * **Green** indicates the current mode is **"14-class Full Mode"**; **Red** indicates it is not. + * **Switching**: Long-press the circular button. The screen will display the prompt *"Release to upgrade to class 14 and please wait for Training be done."* After releasing your finger, the system will automatically complete the model training update. Once finished, it will prompt *"success changed to 14"*, the button will turn green, and the system enters 14-class recognition mode. +2. **Bottom-right Button (class 4)**: + * **Green** indicates the current mode is **"4-class Lite Mode"**; **Red** indicates it is not. + * **Switching**: Long-press the circular button. The screen will display the prompt *"Release to retrain to class 4 and please wait for Training be done."* After releasing your finger, the system will automatically complete the model update. Once finished, it will prompt *"success changed to 4"*, the button will turn green, and the system enters 4-class recognition mode. +3. Please keep the device stable during mode switching and wait for the training update to complete (refer to the "last_train_time" displayed on the interface). Do not force quit the program. + +### 3.4 System Exit +Simply press the exit button to quit. The program will automatically terminate the loop and release related resources. + +## 4. Notes +1. **Environmental Requirements**: + * It is recommended to use the system in a well-lit environment without glare or strong backlighting. Avoid dark environments as they affect detection accuracy. + * Keep the background as simple as possible. Avoid objects with colors similar to skin tones in the background to reduce false detections. +2. **Hand Requirements**: + * Keep the hand fully visible and unobstructed. Avoid excessive finger bending, overlapping hands, or occlusion by other objects, as this will cause keypoint extraction failure or reduced accuracy. + * Avoid shaking the hand rapidly; hold the gesture steady for a moment to facilitate accurate recognition. + * Both left and right hands are supported. The system automatically distinguishes them and labels them with colors (Red for Left, Green for Right). +3. **Performance Tips**: + * The recognition time in 14-class Full Mode is slightly longer than in 4-class Lite Mode, which is normal. + * The training update time during mode switching is related to device performance; please wait patiently and do not interrupt the process. +5. **Touchscreen Operation**: + * Ensure accurate positioning when tapping the toggle buttons (within the circular area) to avoid ineffective touches. + * Long-pressing is only effective when the button is red (inactive mode). Long-pressing a green button (active mode) has no additional effect. + +## 5. More Information +[Source Code](https://github.com/sipeed/MaixPy/tree/main/projects/app_hand_gesture_classifier) + +[MaixCAM MaixPy: Hand Gesture Classification based on Keypoint Detection](https://wiki.sipeed.com/maixpy/doc/eh/vision/hand_gesture_classification.html) \ No newline at end of file diff --git a/projects/app_hand_gesture_classifier/app.yaml b/projects/app_hand_gesture_classifier/app.yaml index 02e21c64..5f98b780 100644 --- a/projects/app_hand_gesture_classifier/app.yaml +++ b/projects/app_hand_gesture_classifier/app.yaml @@ -5,7 +5,7 @@ version: 1.0.0 author: Taorye@Sipeed icon: icon.png desc: Classify the hand gesture. -files: +include: - app.yaml - icon.png - main.py diff --git a/projects/app_hand_gesture_classifier/assets/image.png b/projects/app_hand_gesture_classifier/assets/image.png new file mode 100644 index 00000000..ae27d71d Binary files /dev/null and b/projects/app_hand_gesture_classifier/assets/image.png differ diff --git a/projects/app_hand_landmarks/README.md b/projects/app_hand_landmarks/README.md new file mode 100644 index 00000000..28853009 --- /dev/null +++ b/projects/app_hand_landmarks/README.md @@ -0,0 +1,37 @@ +## 1. 简介 +本应用是基于Maix系列硬件开发的手部关键点检测工具,能够实时识别画面中的手部并定位手部关键点,同时可视化展示手部检测结果、关键点轨迹及手部角度等信息,适用于手部动作分析、简易手势交互等场景。 + +## 2. 主要功能 +1. **实时手部检测**:摄像头实时采集画面,自动识别画面中的手部目标,标注手部类别及置信度; +2. **关键点可视化**:精准绘制手部关键点及骨骼连线,直观展示手部形态; +3. **轨迹追踪**:记录最近10帧的手部关键点位置,绘制手部移动轨迹,支持双手指尖轨迹连线; +4. **角度显示**:在手部区域显示实时检测到的手部角度(角度值以度数为单位); +5. **模型切换**:支持在不同的手部检测模型间快速切换,适配不同硬件性能需求; +6. **快捷退出**:提供一键退出功能,操作便捷。 + +## 3. 使用说明 +### 3.1 启动应用 +将应用部署至MaixCam设备后,直接运行该程序即可启动,启动后摄像头自动开启并进入实时检测界面。 + +### 3.2 界面元素说明 +- **退出应用**:触摸屏幕左上角的返回图标区域,立即退出应用程序 +- **切换检测模型**:触摸屏幕下方左侧的“model: X”矩形区域 可循环切换可用的手部检测模型,切换时屏幕会显示“switching model ...”提示,切换完成后自动恢复检测 +- **手部标注**:画面中识别到手部后,会显示手部类别及置信度(红色/绿色文字区分不同手部): +- **关键点与骨骼**:以线条连接手部关键点,形成手部骨骼轮廓,矩形框标注手部区域; +- **角度数值**:手部区域显示的数字为当前检测到的手部角度; +- **轨迹线条**:红色/绿色线条为单只手的移动轨迹,黄色线条为双手指尖的连线; +- **模型标识**:屏幕下方显示“model: X”,X为当前使用的模型编号。 + +## 4. 注意事项 +2. **检测范围**:请将手部置于摄像头画面中央区域,距离摄像头30-80cm为宜,以保证检测精度; +3. **环境要求**:避免强光直射或光线过暗的环境,否则可能导致检测准确率下降; +4. **轨迹清空**:若某只手超过3秒未被检测到,其对应的轨迹会自动清空; +7. **模型说明**:应用内置的手部检测模型针对MaixCam硬件做了优化,不同模型版本(如bf16格式)在检测速度和精度上有不同侧重,可根据实际需求切换; +8. **参数可调**:代码中可调整的参数(如检测置信度阈值、轨迹记录长度等),可根据使用场景修改,以适配不同的检测需求; +9. **扩展能力**:本应用可作为手部交互的基础模块,可扩展实现手势控制、手部动作识别、隔空操作等进阶功能; +10. **性能优化**:若检测帧率较低,可优先切换至bf16格式的模型(仅MaixCam/Pro支持),或降低摄像头分辨率以提升运行速度。 + +## 5. 更多介绍 +[源码](https://github.com/sipeed/MaixPy/tree/main/projects/app_hand_landmarks) + +[MaixPy MaixCAM 人手部 21 个关键点三维坐标检测](https://wiki.sipeed.com/maixpy/doc/zh/vision/hand_landmarks.html) \ No newline at end of file diff --git a/projects/app_hand_landmarks/README_EN.md b/projects/app_hand_landmarks/README_EN.md new file mode 100644 index 00000000..94806fcb --- /dev/null +++ b/projects/app_hand_landmarks/README_EN.md @@ -0,0 +1,37 @@ +## 1. Introduction +This application is a hand keypoint detection tool developed based on Maix series hardware. It can real-time identify hands in the frame and locate hand keypoints, while visually displaying detection results, keypoint trajectories, and hand angles. It is suitable for scenarios such as hand motion analysis and simple gesture interaction. + +## 2. Main Features +1. **Real-time Hand Detection**: The camera captures frames in real-time, automatically identifies hand targets in the frame, and labels the hand category along with confidence scores. +2. **Keypoint Visualization**: Precisely draws hand keypoints and bone connections to intuitively display hand morphology. +3. **Trajectory Tracking**: Records the positions of hand keypoints from the last 10 frames to draw movement trajectories. Supports trajectory lines between the fingertips of both hands. +4. **Angle Display**: Displays the real-time detected hand angle (in degrees) within the hand region. +5. **Model Switching**: Supports quick switching between different hand detection models to adapt to varying hardware performance requirements. +6. **Quick Exit**: Provides a one-click exit function for convenient operation. + +## 3. User Guide +### 3.1 Starting the Application +After deploying the application to the MaixCam device, simply run the program to start it. Once started, the camera turns on automatically and enters the real-time detection interface. + +### 3.2 Interface Element Description +* **Exit Application**: Touch the back icon area in the upper left corner of the screen to immediately exit the application. +* **Switch Detection Model**: Touch the "model: X" rectangular area on the lower left of the screen to cycle through available hand detection models. A "switching model ..." prompt will appear during the switch, and detection will resume automatically upon completion. +* **Hand Labeling**: When a hand is recognized, the hand category and confidence score are displayed (Red/Green text distinguishes between different hands). +* **Keypoints & Bones**: Lines connect hand keypoints to form a hand bone contour, and a rectangular box labels the hand region. +* **Angle Value**: The number displayed in the hand region is the currently detected hand angle. +* **Trajectory Lines**: Red/Green lines represent the movement trajectory of a single hand; yellow lines represent the connection between the fingertips of both hands. +* **Model Indicator**: "model: X" is displayed at the bottom of the screen, where X is the number of the currently used model. + +## 4. Notes +1. **Detection Range**: Please place your hand in the central area of the camera frame, ideally 30-80cm away from the camera, to ensure detection accuracy. +2. **Environmental Requirements**: Avoid environments with direct strong light or excessive darkness, as this may reduce detection accuracy. +3. **Trajectory Clearing**: If a hand is not detected for more than 3 seconds, its corresponding trajectory will be automatically cleared. +4. **Model Description**: The built-in hand detection models are optimized for MaixCam hardware. Different model versions (such as bf16 format) have different focuses on detection speed and precision and can be switched according to actual needs. +5. **Adjustable Parameters**: Parameters in the code (such as detection confidence threshold, trajectory recording length, etc.) can be modified according to usage scenarios to adapt to different detection requirements. +6. **Expandability**: This application can serve as a basic module for hand interaction and can be extended to implement advanced functions such as gesture control, hand motion recognition, and touchless operation. +7. **Performance Optimization**: If the detection frame rate is low, prioritize switching to a model in bf16 format (MaixCam/Pro only) or lower the camera resolution to improve speed. + +## 5. More Information +[Source Code](https://github.com/sipeed/MaixPy/tree/main/projects/app_hand_landmarks) + +[MaixPy MaixCAM Hand 21 Keypoint 3D Coordinate Detection](https://wiki.sipeed.com/maixpy/doc/en/vision/hand_landmarks.html) \ No newline at end of file diff --git a/projects/app_hand_landmarks/app.yaml b/projects/app_hand_landmarks/app.yaml index eebedc39..bc65ad1b 100644 --- a/projects/app_hand_landmarks/app.yaml +++ b/projects/app_hand_landmarks/app.yaml @@ -5,7 +5,7 @@ version: 1.0.2 author: Neucrack@Sipeed icon: icon.png desc: Hand Landmarks detect hand keypoints -files: +include: - icon.png - app.yaml - main.py diff --git a/projects/app_human_pose/README.md b/projects/app_human_pose/README.md new file mode 100644 index 00000000..71a525b9 --- /dev/null +++ b/projects/app_human_pose/README.md @@ -0,0 +1,33 @@ +## 简介 +本应用是一款运行于MaixPy 实现了基于 YOLOv8-Pose / YOLO11-Pose 的人体姿态检测,可以检测到人体17个关键点。能够实时采集摄像头画面并完成人体姿态关键点识别与展示,操作简洁、运行高效,适用于快速人体姿态捕捉、简易运动分析等场景。 + +## 主要功能 +![](./assets/body_keypoints.jpg) +1. 设备兼容性:本应用针对MaixCam系列设备开发,其中MaixCam2设备使用YOLO11模型,MaixCam/Pro备使用YOLOv8模型 +2. 实时摄像头采集:自动调用设备摄像头,获取高清实时画面,画面分辨率适配姿态检测模型需求。 +3. 人体姿态检测与识别:基于预加载的姿态检测模型,自动识别画面中的人体目标,筛选置信度符合要求的检测结果。 +4. 检测结果可视化:在实时画面上标注人体目标矩形框、目标类别及置信度评分,同时绘制人体关键姿态点连线。 +5. 自动检测人体17个关键点: + ``` + 1. 鼻子(Nose) + 2. 左眼(Left Eye) + 3. 右眼(Right Eye) + 4. 左耳(Left Ear) + 5. 右耳(Right Ear) + 6. 左肩(Left Shoulder) + 7. 右肩(Right Shoulder) + 8. 左肘(Left Elbow) + 9. 右肘(Right Elbow) + 10. 左手腕(Left Wrist) + 11. 右手腕(Right Wrist) + 12. 左髋(Left Hip) + 13. 右髋(Right Hip) + 14. 左膝(Left Knee) + 15. 右膝(Right Knee) + 16. 左脚踝(Left Ankle) + 17. 右脚踝(Right Ankle) + ``` + +## 更多 +[源码](https://github.com/sipeed/MaixPy/tree/main/projects/app_human_pose) +[MaixCAM MaixPy 检测人体关键点姿态检测](https://wiki.sipeed.com/maixpy/doc/zh/vision/body_key_points.html) \ No newline at end of file diff --git a/projects/app_human_pose/README_EN.md b/projects/app_human_pose/README_EN.md new file mode 100644 index 00000000..e2c7c48b --- /dev/null +++ b/projects/app_human_pose/README_EN.md @@ -0,0 +1,33 @@ +## Introduction +This application runs on MaixPy and implements human pose detection based on **YOLOv8-Pose** / **YOLO11-Pose**, capable of detecting 17 human key points. It can capture camera frames in real-time to identify and display human pose key points. With its simple operation and high efficiency, it is suitable for scenarios such as rapid human pose capture and basic motion analysis. + +## Main Features +![](./assets/body_keypoints.jpg) +1. **Device Compatibility:** Developed specifically for the MaixCam series. The **MaixCam2** utilizes the YOLO11 model, while the **MaixCam/Pro** utilizes the YOLOv8 model. +2. **Real-time Camera Capture:** Automatically invokes the device camera to acquire high-definition real-time frames, with resolution automatically adapted to meet the requirements of the pose detection model. +3. **Human Pose Detection & Recognition:** Based on the preloaded pose detection model, it automatically identifies human targets in the frame and filters results based on confidence thresholds. +4. **Visualization of Results:** Annotates the real-time frame with bounding boxes, class labels, confidence scores, and draws connecting lines between key body points. +5. **Automatic Detection of 17 Key Points:** + ``` + 1. Nose + 2. Left Eye + 3. Right Eye + 4. Left Ear + 5. Right Ear + 6. Left Shoulder + 7. Right Shoulder + 8. Left Elbow + 9. Right Elbow + 10. Left Wrist + 11. Right Wrist + 12. Left Hip + 13. Right Hip + 14. Left Knee + 15. Right Knee + 16. Left Ankle + 17. Right Ankle + ``` + +## More +[Source Code](https://github.com/sipeed/MaixPy/tree/main/projects/app_human_pose) +[MaixCAM MaixPy Human Pose Keypoint Detection](https://wiki.sipeed.com/maixpy/doc/en/vision/body_key_points.html) \ No newline at end of file diff --git a/projects/app_human_pose/assets/body_keypoints.jpg b/projects/app_human_pose/assets/body_keypoints.jpg new file mode 100644 index 00000000..b0fd0057 Binary files /dev/null and b/projects/app_human_pose/assets/body_keypoints.jpg differ diff --git a/projects/app_human_pose_classifier/README.md b/projects/app_human_pose_classifier/README.md new file mode 100644 index 00000000..73cb4b10 --- /dev/null +++ b/projects/app_human_pose_classifier/README.md @@ -0,0 +1,45 @@ +## 1. 简介 + +这是一个基于YOLO视觉的实时人体姿态检测与分类系统,能够通过摄像头识别人体关键点,自动分析并分类当前姿态类型。系统采用YOLO11姿态检测模型,可以实时显示检测结果、姿态分类和评分信息。 + +## 2. 主要功能 + +- **实时姿态检测**:通过摄像头实时捕捉画面并识别人体姿态 +- **关键点标注**:在检测到的人体上标记关键点(如头部、肩膀、肘部、手腕、臀部、膝盖、脚踝等) +- **智能姿态分类**:自动识别并分类当前姿态(如站立、坐姿、蹲姿、躺卧等) +- **姿态评估**:对检测到的姿态进行质量评分和规范性分析 +- **检测框显示**:用红色方框标记检测到的人体区域 +- **置信度显示**:实时显示检测结果的准确度 +- **触摸返回**:支持触摸屏操作,点击返回按钮可退出程序 + +## 3. 使用说明 + +### 3.1 启动程序 +- 运行程序后,系统会自动启动摄像头 +- 屏幕将实时显示摄像头画面 + +### 3.2 进行姿态检测与分类 +- 将身体完整置于摄像头视野范围内 +- 保持适当距离,确保全身能被摄像头捕捉 +- 系统会自动检测并在画面上显示: + - **红色矩形框**:标记检测到的人体位置 + - **关键点连线**:显示人体骨架结构 + - **置信度分数**:显示检测准确度(0-1之间的数值) + - **姿态分类结果**:显示当前姿态的类型(如"站立"、"坐姿"、"深蹲"等) + +![](./assets/image.png) + +## 4. 注意事项 + +- **光线条件**:请在光线充足的环境下使用,以获得更好的检测和分类效果 +- **拍摄距离**:建议距离摄像头1.5-3米,确保全身在画面中 +- **背景环境**:尽量选择简洁的背景,避免复杂背景干扰检测 +- **衣着建议**:穿着贴身或对比度较高的衣物有助于提升检测准确度 +- **遮挡问题**:避免身体关键部位被遮挡,以免影响检测和分类效果 +- **姿态保持**:保持姿态稳定1-2秒,有助于系统准确分类 +- **多人检测**:系统可同时检测多个人体并分别分类,但建议单人使用以获得最佳体验 + +## 5. 更多介绍 +[源码](https://github.com/sipeed/MaixPy/tree/main/projects/app_human_pose_classifier) + +[MaixCAM MaixPy 检测人体关键点姿态检测](https://wiki.sipeed.com/maixpy/doc/zh/vision/body_key_points.html) \ No newline at end of file diff --git a/projects/app_human_pose_classifier/README_EN.md b/projects/app_human_pose_classifier/README_EN.md new file mode 100644 index 00000000..f0e12677 --- /dev/null +++ b/projects/app_human_pose_classifier/README_EN.md @@ -0,0 +1,45 @@ +## 1. Introduction + +This is a real-time human pose detection and classification system based on YOLO vision, which can identify human body keypoints through a camera, automatically analyze and classify current posture types. The system uses the YOLO11 pose detection model to display detection results, posture classification, and scoring information in real-time. + +## 2. Main Features + +- **Real-time Pose Detection**: Captures video feed through camera and identifies human postures in real-time +- **Keypoint Annotation**: Marks keypoints on detected human bodies (such as head, shoulders, elbows, wrists, hips, knees, ankles, etc.) +- **Intelligent Pose Classification**: Automatically recognizes and classifies current postures (such as standing, sitting, squatting, lying down, etc.) +- **Pose Evaluation**: Performs quality scoring and normative analysis on detected postures +- **Detection Box Display**: Marks detected human body areas with red rectangular boxes +- **Confidence Display**: Shows detection result accuracy in real-time +- **Touch Return**: Supports touchscreen operation, tap the return button to exit the program + +## 3. User Guide + +### 3.1 Starting the Program +- After running the program, the system will automatically start the camera +- The screen will display the camera feed in real-time + +### 3.2 Pose Detection and Classification +- Position your body completely within the camera's field of view +- Maintain an appropriate distance to ensure your full body can be captured +- The system will automatically detect and display on the screen: + - **Red Rectangular Box**: Marks the detected human body position + - **Keypoint Connections**: Displays the human skeletal structure + - **Confidence Score**: Shows detection accuracy (value between 0-1) + - **Pose Classification Result**: Displays the current posture type (such as "Standing", "Sitting", "Deep Squat", etc.) + +![](./assets/image.png) + +## 4. Precautions + +- **Lighting Conditions**: Please use in well-lit environments for better detection and classification results +- **Camera Distance**: Recommended distance of 1.5-3 meters from the camera to ensure full body is in frame +- **Background Environment**: Choose a simple background to avoid complex backgrounds interfering with detection +- **Clothing Recommendations**: Wearing fitted or high-contrast clothing helps improve detection accuracy +- **Occlusion Issues**: Avoid blocking key body parts to prevent affecting detection and classification results +- **Pose Stability**: Maintain posture for 1-2 seconds to help the system accurately classify +- **Multi-person Detection**: The system can detect and classify multiple people simultaneously, but single-person use is recommended for optimal experience + +## 5. More Information +[Source Code](https://github.com/sipeed/MaixPy/tree/main/projects/app_human_pose_classifier) + +[MaixCAM MaixPy Body Keypoint Pose Detection](https://wiki.sipeed.com/maixpy/doc/eh/vision/body_key_points.html) diff --git a/projects/app_human_pose_classifier/app.yaml b/projects/app_human_pose_classifier/app.yaml index 3dfdbc03..1156f071 100644 --- a/projects/app_human_pose_classifier/app.yaml +++ b/projects/app_human_pose_classifier/app.yaml @@ -5,7 +5,7 @@ version: 1.0.1 author: Taorye@Sipeed icon: icon.json desc: Detect human body 17 keypoints and then classify the pose. -files: +include: - app.yaml - icon.json - main.py diff --git a/projects/app_human_pose_classifier/assets/image.png b/projects/app_human_pose_classifier/assets/image.png new file mode 100644 index 00000000..fcf928ea Binary files /dev/null and b/projects/app_human_pose_classifier/assets/image.png differ diff --git a/projects/app_image_generation/README.md b/projects/app_image_generation/README.md index 8afbead6..f271dbdc 100644 --- a/projects/app_image_generation/README.md +++ b/projects/app_image_generation/README.md @@ -1,7 +1,50 @@ -Image Generation +## 1. 简介 +本APP是一款运行在MaixCam2设备上的智能应用,通过**语音识别(ASR)** 与**文本/图像生成图像(Txt to Img/Img to Img)** 技术结合,实现了语音输入触发图像生成的便捷操作,同时支持相机实时预览与图像抓拍功能,无需手动输入文本即可完成创意图像制作,操作直观,适合快速实现视觉创意落地。 -Text-to-Image / Image-to-Image Large Models +## 2. 主要功能 +1. **语音转录(长按录音)**:支持语音输入并自动转录为文本提示词(prompt),作为图像生成的输入依据。 +2. **文本生成图像(Txt2 to Img)**:基于语音转录的提示词,调用LCM-LoRA SDV1.5模型生成对应的创意图像。 +3. **图像生成图像(Img to Img)**:基于已生成的文本图像,结合语音提示词进行二次优化与重构,生成更贴合需求的图像。 +4. **相机实时预览与抓拍**:支持开启设备相机实时预览画面,并可抓拍当前画面作为Img2Img的基础素材。 +5. **便捷退出**:通过屏幕专属退出区域快速终止应用,保障设备资源正常回收。 +## 3. 使用说明 +### 3.1 前期准备 +1. 确保设备为**4GB版本MaixCam2**,且已正确烧录对应固件。 +2. 系统默认自带模型文件若模型不存在,需要提前下载模型至/root/models目录 +3. 运行应用程序,等待启动加载完成(加载过程中屏幕会显示进度提示,请耐心等待)。 +### 3.2 操作步骤 +![alt text](./assets/image.png) +1. **启动完成**:应用加载完成后,进入主界面,界面包含录音按钮、功能按钮、相机预览区与文本提示区。 +2. **录制语音提示词**: + - 长按界面上的「Record (Long press)」按钮,开始录音(屏幕提示「Recording..」)。 + - 对着设备麦克风说出图像生成需求(如“一片开满樱花的湖边小屋,卡通风格”)。 + - 松开按钮停止录音,应用自动转录语音为文本(屏幕提示「Transcribing ...」,转录完成后文本提示区显示对应内容)。 +3. **文本生成图像**: + - 转录完成后,点击「Text To Image」按钮。 + - 等待模型运行(期间保持设备稳定,勿频繁操作),完成后预览区显示生成的图像,文本提示区显示完成信息。 + - 生成的图像自动保存于设备`/tmp/text2img.jpg`路径下。 +4. **图像生成图像**: + - 确保已完成Txt2Img生成(存在有效基础图像)。 + - 如需二次优化,可重新录制新的语音提示词(或沿用原有提示词)。 + - 点击「Image To Image」按钮,等待模型基于基础图像与提示词完成二次生成,生成结果保存于`/tmp/img2img.jpg`路径下。 +5. **相机操作**: + - 点击「Camera」按钮,开启相机实时预览(再次点击可关闭预览,返回原有图像)。 + - 开启预览后,点击「Capture」按钮,抓拍当前相机画面,作为Img2Img的基础素材(自动覆盖`/tmp/text2img.jpg`)。 +6. **退出应用**: + - 点击屏幕左上角的退出图像区域(「exit.jpg」显示区域)。 + - 等待应用显示「Exit..」提示,完成资源回收后自动退出,无需手动强制关闭。 +## 4. 注意事项 +1. **硬件要求**:仅支持4GB版本MaixCam2设备,2GB版本设备因内存不足无法运行,启动时会自动检测并提示退出。 +2. **模型加载**:首次启动应用时,模型加载与初始化需要较长时间(屏幕提示「It may wait for a long time」),请耐心等待,勿断电或强制退出。 +3. **录音操作**:录音需**长按「Record」按钮**(短按不生效),建议在安静环境下录音,提升语音转录准确率。 +4. **设备稳定性**:图像生成过程中(Txt2Img/Img2Img),模型占用大量计算资源,请勿同时运行其他应用,避免设备卡顿或崩溃。 +5. **存储路径**:生成的图像自动保存于`/tmp`目录下。 +6. **免责声明**: 该模型存在的固有的局限性,可能产生错误的、有害的、冒犯性的或其他不良的输出等内容与 *Sipeed* 无关 +## 5. 更多介绍 +[源码](https://github.com/sipeed/MaixPy/tree/dev/projects/demo_generate_image_and_print) + +[MaixPy MaixCAM 运行 LCM-LoRA-SDv1-5 模型](https://wiki.sipeed.com/maixpy/doc/zh/mllm/dlm_lora_sdv1_5.html) \ No newline at end of file diff --git a/projects/app_image_generation/README_EN.md b/projects/app_image_generation/README_EN.md new file mode 100644 index 00000000..ebff3fca --- /dev/null +++ b/projects/app_image_generation/README_EN.md @@ -0,0 +1,50 @@ +## 1. Introduction +This APP is an intelligent application running on the **MaixCam2** device. By combining **Automatic Speech Recognition (ASR)** with **Text-to-Image / Image-to-Image** technologies, it enables convenient image generation triggered by voice input. It also supports real-time camera preview and image capture, allowing users to create creative images without manual text input. The operation is intuitive, making it suitable for quickly realizing visual creative ideas. + +## 2. Main Features +1. **Voice Transcription (Long Press to Record)**: Supports voice input and automatically transcribes it into text prompts, serving as the basis for image generation. +2. **Text-to-Image (Txt2Img)**: Based on the transcribed voice prompts, the LCM-LoRA SDV1.5 model is called to generate corresponding creative images. +3. **Image-to-Image (Img2Img)**: Based on the already generated text image, combined with voice prompts, the image undergoes secondary optimization and reconstruction to generate images that better fit the requirements. +4. **Camera Real-time Preview & Capture**: Supports turning on the device camera for real-time preview and capturing the current frame as base material for Img2Img. +5. **Convenient Exit**: Quickly terminate the application through a dedicated exit area on the screen to ensure proper resource recovery. + +## 3. User Guide +### 3.1 Preparation +1. Ensure the device is the **4GB version of MaixCam2** with the corresponding firmware correctly flashed. +2. If the model file is not included in the default system, it needs to be downloaded in advance to the `/root/models` directory. +3. Run the application and wait for the startup loading to complete (the screen will display a progress prompt during loading, please wait patiently). + +### 3.2 Operation Steps +![alt text](./assets/image.png) +1. **Startup Complete**: After the application loads, you will enter the main interface, which includes a record button, function buttons, a camera preview area, and a text prompt area. +2. **Recording Voice Prompts**: + * Long press the `Record (Long press)` button on the interface to start recording (the screen prompts `Recording..`). + * Speak your image generation requirements into the device microphone (e.g., "A small house by a lake full of cherry blossoms, cartoon style"). + * Release the button to stop recording. The application will automatically transcribe the voice to text (the screen prompts `Transcribing ...`). Once complete, the corresponding content will be displayed in the text prompt area. +3. **Text-to-Image Generation**: + * After transcription is complete, click the `Text To Image` button. + * Wait for the model to run (keep the device stable and avoid frequent operations during this period). Once completed, the generated image will be displayed in the preview area, and completion information will appear in the text prompt area. + * The generated image is automatically saved to the device path `/tmp/text2img.jpg`. +4. **Image-to-Image Generation**: + * Ensure Txt2Img generation has been completed (a valid base image exists). + * If secondary optimization is needed, you can record a new voice prompt (or use the existing one). + * Click the `Image To Image` button. Wait for the model to complete secondary generation based on the base image and prompt. The result is saved to `/tmp/img2img.jpg`. +5. **Camera Operations**: + * Click the `Camera` button to turn on the real-time camera preview (click again to close the preview and return to the original image). + * Once the preview is active, click the `Capture` button to snap the current camera frame as base material for Img2Img (automatically overwrites `/tmp/text2img.jpg`). +6. **Exiting the Application**: + * Click the exit image area in the upper left corner of the screen (where `exit.jpg` is displayed). + * Wait for the application to display the `Exit..` prompt. It will automatically exit after resource recovery; there is no need to force close it manually. + +## 4. Notes +1. **Hardware Requirements**: Only supports the 4GB version of the MaixCam2 device. The 2GB version cannot run due to insufficient memory and will automatically detect this and prompt an exit on startup. +2. **Model Loading**: Model loading and initialization may take a long time when starting the application for the first time (the screen prompts `It may wait for a long time`). Please be patient and do not power off or force quit. +3. **Recording Operation**: Recording requires a **long press** of the `Record` button (short press is ineffective). It is recommended to record in a quiet environment to improve speech transcription accuracy. +4. **Device Stability**: The image generation process (Txt2Img/Img2Img) occupies significant computing resources. Do not run other applications simultaneously to avoid device lag or crashes. +5. **Storage Path**: Generated images are automatically saved in the `/tmp` directory. +6. **Disclaimer**: The model has inherent limitations and may produce incorrect, harmful, offensive, or other undesirable outputs. This is unrelated to *Sipeed*. + +## 5. More Information +[Source Code](https://github.com/sipeed/MaixPy/tree/dev/projects/demo_generate_image_and_print) + +[Running the LCM-LoRA-SDv1-5 Model on MaixPy MaixCAM](https://wiki.sipeed.com/maixpy/doc/en/mllm/dlm_lora_sdv1_5.html) \ No newline at end of file diff --git a/projects/app_image_generation/app.yaml b/projects/app_image_generation/app.yaml index d2b6c1a8..f6a351fb 100644 --- a/projects/app_image_generation/app.yaml +++ b/projects/app_image_generation/app.yaml @@ -10,7 +10,7 @@ exclude: - dist - build - .gitignore -files: +include: - assets/exit.jpg - assets/icon.json - app.yaml diff --git a/projects/app_image_generation/assets/image.png b/projects/app_image_generation/assets/image.png new file mode 100644 index 00000000..21d57a4c Binary files /dev/null and b/projects/app_image_generation/assets/image.png differ diff --git a/projects/app_mono_depth_estimation/README.md b/projects/app_mono_depth_estimation/README.md index d0b8d067..663a88a4 100644 --- a/projects/app_mono_depth_estimation/README.md +++ b/projects/app_mono_depth_estimation/README.md @@ -1,9 +1,23 @@ -MaixPy benchmark APP -===== - - -MaixPy firmware must > 4.11.10 +## 1. 简介 +本工具是一款基于Maix硬件平台开发的实时深度图像检测应用,能够通过设备摄像头采集实时画面,并借助`DepthAnything`模型快速处理生成对应的深度可视化图像,将场景中的远近信息以色彩编码的形式直观呈现,操作简洁且运行高效,适用于各类需要快速获取场景深度信息的场景。 +## 2. 主要功能 +1. 实时摄像头采集:自动调用设备摄像头,获取高清实时场景画面,无需手动配置摄像头参数。 +2. 深度图像生成:依托`DepthAnything v2`模型对采集画面进行实时处理,生成以TURBO色彩映射的深度可视化图像,色彩差异对应场景中物体的远近差异。 +3. 便捷退出功能:提供可视化返回按钮,支持触摸操作快速退出应用,无需复杂的指令输入。 +4. 异常容错处理:应用内置异常捕获与展示机制,运行过程中出现问题会自动打印错误信息并在设备屏幕上显示,便于排查问题。 +## 3. 使用说明 +1. 应用启动:将预配置好的设备上电后,应用会自动加载运行,无需手动启动额外程序。 +2. 查看深度图像:应用运行后,设备屏幕会实时显示场景对应的深度可视化图像,通过图像色彩可直观区分场景中物体的远近。 +3. 退出应用:设备屏幕左上角会显示返回按钮图标,触摸该图标区域,即可快速退出本应用,回到设备初始界面。 +![alt text](./assets/image.png) +## 4. 注意事项 +1. 硬件环境:本工具仅适用于支持`maix`系列库的专用硬件设备,不支持普通电脑或其他非兼容嵌入式设备运行。 +2. 模型文件:设备`/root/models/`目录下必须存在`depth_anything_v2_vits.mud`模型文件,缺少该文件可以自行下载。 +3. 运行环境:应用运行过程中请避免断电或强行重启设备,以免损坏相关配置文件或模型文件。 +## 5. 更多介绍 +[源码](https://github.com/sipeed/MaixPy/tree/main/projects/app_mono_depth_estimation) +[MaixCAM2 MaixPy 使用 Depth-Anything 单目估计深度距离](https://wiki.sipeed.com/maixpy/doc/zh/vision/depth_anything.html) \ No newline at end of file diff --git a/projects/app_mono_depth_estimation/README_EN.md b/projects/app_mono_depth_estimation/README_EN.md new file mode 100644 index 00000000..737a6e61 --- /dev/null +++ b/projects/app_mono_depth_estimation/README_EN.md @@ -0,0 +1,25 @@ +## 1. Introduction +This tool is a real-time depth image detection application developed based on the Maix hardware platform. It captures real-time frames through the device camera and utilizes the `DepthAnything` model to rapidly process and generate corresponding depth-visualized images. It intuitively presents the distance information within a scene through color coding. With its simple operation and efficient performance, it is suitable for various scenarios requiring quick acquisition of scene depth information. + +## 2. Main Features +1. **Real-time Camera Capture:** Automatically invokes the device camera to acquire high-definition real-time scene frames without requiring manual configuration of camera parameters. +2. **Depth Image Generation:** Relies on the `DepthAnything v2` model to process captured frames in real-time, generating depth-visualized images using a TURBO color map, where color differences correspond to the varying distances of objects in the scene. +3. **Convenient Exit Function:** Provides a visualized back button that supports touch operation for quick application exit, eliminating the need for complex command input. +4. **Exception Fault Tolerance:** The application includes a built-in exception capture and display mechanism. If issues arise during operation, error messages are automatically printed and displayed on the device screen to facilitate troubleshooting. + +## 3. User Guide +1. **Application Startup:** After powering on the pre-configured device, the application loads and runs automatically; no manual startup of additional programs is required. +2. **Viewing Depth Images:** Once running, the device screen displays the depth-visualized image corresponding to the scene in real-time. You can intuitively distinguish the distance of objects by observing the image colors. +3. **Exiting the Application:** A back button icon is displayed in the upper-left corner of the screen. Touching this icon area allows you to quickly exit the application and return to the device's initial interface. +![alt text](./assets/image.png) + +## 4. Notes +1. **Hardware Environment:** This tool is designed exclusively for dedicated hardware devices supporting the `maix` series libraries. It does not support running on regular computers or other non-compatible embedded devices. +2. **Model File:** The model file `depth_anything_v2_vits.mud` must exist in the device's `/root/models/` directory. If missing, it can be downloaded separately. +3. **Runtime Environment:** To avoid damaging configuration or model files, please refrain from powering off or force-restarting the device while the application is running. + +## 5. More Information +[Source Code](https://github.com/sipeed/MaixPy/tree/main/projects/app_mono_depth_estimation) + +[MaixCAM2 MaixPy Using Depth-Anything for Monocular Depth Estimation](https://wiki.sipeed.com/maixpy/doc/en/vision/depth_anything.html) + diff --git a/projects/app_mono_depth_estimation/assets/image.png b/projects/app_mono_depth_estimation/assets/image.png new file mode 100644 index 00000000..97dc21cd Binary files /dev/null and b/projects/app_mono_depth_estimation/assets/image.png differ diff --git a/projects/app_mouse_simulator/README.md b/projects/app_mouse_simulator/README.md new file mode 100644 index 00000000..a15b6c4e --- /dev/null +++ b/projects/app_mouse_simulator/README.md @@ -0,0 +1,39 @@ +## 1. 简介 +本工具通过设备的触摸屏来模拟电脑鼠标的各项操作,无需额外连接物理鼠标,即可实现对电脑等支持HID鼠标协议设备的远程控制,适用于无物理鼠标场景下的简易操作、嵌入式设备配套控制等场景。 + +## 2. 主要功能 +1. **触摸板移动**:通过核心触摸区域实现鼠标指针的精准移动,适配屏幕比例优化移动灵敏度。 +2. **鼠标按键模拟**:支持鼠标左键、右键的按压与释放操作,实现文件选择、菜单调出等基础鼠标功能。 +3. **滚轮模拟**:专属滚轮区域支持上下滑动,对应电脑鼠标滚轮的滚动效果,可用于翻页、缩放等操作。 +4. **快速退出**:设置专属退出区域,一键终止应用运行,操作便捷。 +5. **可视化交互**:操作过程中实时显示触摸点位、按键状态,直观反馈当前操作效果。 + +## 3. 使用说明 +![alt text](./assets/image.png) +1. **前期准备** + - 确保设备已开启HID Mouse功能(路径:设置 -> USB设置 -> HID Mouse,勾选后确认并重启设备)。 + - 将Maix设备通过USB线与电脑(或其他支持HID协议的设备)连接,确保设备被正常识别。 +2. **界面区域识别** + - **触摸板区域(TOUCHPAD)**:占据屏幕大部分区域的白色边框矩形,标注有"TOUCHPAD",是鼠标指针移动的核心操作区。 + - **滚轮区域(WHEEL)**:位于触摸板右侧的白色边框矩形,标注有"WHEEL",用于模拟鼠标滚轮。 + - **退出区域(EXIT)**:位于界面右下角的白色边框矩形,标注有"EXIT",用于退出应用。 + - **左键区域(LEFT KEY)**:位于屏幕底部左侧的白色边框矩形,标注有"LEFT KEY",对应鼠标左键。 + - **右键区域(RIGHT KEY)**:位于屏幕底部右侧的白色边框矩形,标注有"RIGHT KEY",对应鼠标右键。 +3. **具体操作步骤** + - **移动鼠标指针**:在触摸板区域(TOUCHPAD)内轻触并滑动手指,屏幕上的红色圆点会跟随手指移动,同时电脑端的鼠标指针也会同步移动,滑动幅度决定指针移动距离。 + - **鼠标左键操作**:触摸底部左侧的左键区域(LEFT KEY),区域会变为白色填充状态,对应鼠标左键按压;松开手指后,区域恢复边框状态,对应鼠标左键释放;在触摸板区域快速点击(轻触后立即松开,无明显滑动),可模拟鼠标左键单击。 + - **鼠标右键操作**:触摸底部右侧的右键区域(RIGHT KEY),区域会变为白色填充状态,对应鼠标右键按压;松开手指后,区域恢复边框状态,对应鼠标右键释放。 + - **滚轮操作**:在滚轮区域(WHEEL)内上下滑动手指,即可模拟鼠标滚轮的上下滚动,实现电脑端页面的翻页或内容缩放。 + - **退出应用**:触摸右下角的退出区域(EXIT),即可立即终止本工具运行,退出应用界面。 + +## 4. 注意事项 +1. **HID功能必开启**:使用前必须在设备中开启HID Mouse功能并重启,否则应用无法正常工作,会提示功能未启用的相关信息。 +2. **USB连接稳定性**:确保USB连接线接触良好,避免连接松动导致鼠标控制中断、操作无响应等问题。 +3. **操作区域区分**:各功能区域有明确划分,操作时尽量在对应区域内进行,避免跨区域误触导致操作不符合预期(如误触退出区域导致应用关闭)。 +4. **触摸灵敏度说明**:鼠标指针移动、滚轮滚动的灵敏度已做优化适配,若需调整可通过设备相关设置修改,操作时避免过度滑动导致指针偏移过大。 +5. **设备兼容性**:本工具主要适配支持HID鼠标协议的设备(如Windows、Linux系统电脑),部分特殊设备可能存在兼容性问题,无法正常识别或响应操作。 + +## 5. 更多介绍 +[源码](https://github.com/sipeed/MaixPy/tree/main/projects/app_usb_hand_touch) + +[MaixCAM MaixPy 使用 USB HID(作为设备)](https://wiki.sipeed.com/maixpy/doc/zh/peripheral/hid.html) \ No newline at end of file diff --git a/projects/app_mouse_simulator/README_EN.md b/projects/app_mouse_simulator/README_EN.md new file mode 100644 index 00000000..d6ded872 --- /dev/null +++ b/projects/app_mouse_simulator/README_EN.md @@ -0,0 +1,39 @@ +## 1. Introduction +This tool utilizes the device's touchscreen to simulate various computer mouse operations. It enables remote control of computers and other devices supporting the HID mouse protocol without the need for an additional physical mouse. It is suitable for simple operations in scenarios without a physical mouse, embedded device control, and similar applications. + +## 2. Main Features +1. **Touchpad Movement**: Achieve precise mouse cursor movement via the core touch area, with optimized sensitivity adapted to the screen ratio. +2. **Mouse Button Simulation**: Supports the pressing and releasing of the left and right mouse buttons, enabling basic functions such as file selection and context menu invocation. +3. **Scroll Wheel Simulation**: A dedicated wheel area supports vertical sliding to replicate the scrolling effect of a computer mouse wheel, useful for page turning and zooming. +4. **Quick Exit**: Features a dedicated exit area to terminate the application with one click for convenient operation. +5. **Visual Interaction**: Real-time display of touch points and button states during operation provides intuitive feedback on the current actions. + +## 3. User Guide +![alt text](./assets/image.png) +1. **Preparation** + * Ensure the device's HID Mouse function is enabled (Path: Settings -> USB Settings -> HID Mouse, check the box, confirm, and restart the device). + * Connect the Maix device to the computer (or other HID-supporting device) via a USB cable and ensure the device is recognized correctly. +2. **Interface Area Identification** + * **Touchpad Area (TOUCHPAD)**: The large white-bordered rectangle occupying most of the screen, labeled "TOUCHPAD". This is the core area for moving the mouse cursor. + * **Wheel Area (WHEEL)**: The white-bordered rectangle to the right of the touchpad, labeled "WHEEL". Used to simulate the mouse scroll wheel. + * **Exit Area (EXIT)**: The white-bordered rectangle at the bottom right corner of the interface, labeled "EXIT". Used to quit the application. + * **Left Key Area (LEFT KEY)**: The white-bordered rectangle at the bottom left of the screen, labeled "LEFT KEY". Corresponds to the left mouse button. + * **Right Key Area (RIGHT KEY)**: The white-bordered rectangle at the bottom right of the screen, labeled "RIGHT KEY". Corresponds to the right mouse button. +3. **Specific Operation Steps** + * **Moving the Mouse Cursor**: Lightly touch and slide your finger within the Touchpad Area (TOUCHPAD). A red dot on the screen will follow your finger, and the computer's mouse cursor will move synchronously. The sliding distance determines the cursor movement distance. + * **Left Mouse Button Operation**: Touch the Left Key Area (LEFT KEY) at the bottom left; the area will fill with white, corresponding to a left button press. Release your finger, and the area reverts to a border state, corresponding to a left button release. A quick tap (light touch followed by immediate release without significant sliding) within the Touchpad Area simulates a left mouse button click. + * **Right Mouse Button Operation**: Touch the Right Key Area (RIGHT KEY) at the bottom right; the area will fill with white, corresponding to a right button press. Release your finger, and the area reverts to a border state, corresponding to a right button release. + * **Scroll Wheel Operation**: Slide your finger up or down within the Wheel Area (WHEEL) to simulate mouse wheel scrolling, allowing you to turn pages or zoom content on the computer. + * **Exiting the Application**: Touch the Exit Area (EXIT) at the bottom right to immediately terminate the tool and exit the application interface. + +## 4. Notes +1. **HID Function Mandatory**: The HID Mouse function must be enabled in the device settings and the device restarted before use. Otherwise, the application will not work correctly and may display an error indicating the function is not enabled. +2. **USB Connection Stability**: Ensure the USB cable connection is secure. Loose connections can cause interruptions in mouse control or unresponsive operations. +3. **Distinguishing Operation Areas**: Each functional area is clearly defined. Please operate within the corresponding areas as much as possible to avoid accidental touches (e.g., accidentally triggering the exit area and closing the application). +4. **Touch Sensitivity**: The sensitivity for cursor movement and scrolling has been optimized. If adjustments are needed, they can be modified through relevant device settings. Avoid excessive sliding, which may cause the cursor to drift significantly. +5. **Device Compatibility**: This tool is primarily designed for devices supporting the HID mouse protocol (such as Windows and Linux computers). Some specialized devices may have compatibility issues and may not recognize or respond to operations correctly. + +## 5. More Information +[Source Code](https://github.com/sipeed/MaixPy/tree/main/projects/app_usb_hand_touch) + +[Introduction to Using MaixCAM MaixPy USB HID (as device)](https://wiki.sipeed.com/maixpy/doc/en/peripheral/hid.html) \ No newline at end of file diff --git a/projects/app_mouse_simulator/app.yaml b/projects/app_mouse_simulator/app.yaml index a667f95e..8b62968e 100644 --- a/projects/app_mouse_simulator/app.yaml +++ b/projects/app_mouse_simulator/app.yaml @@ -5,7 +5,7 @@ version: 1.0.1 author: Sipeed Ltd icon: icon.png desc: Simulate using a mouse to control the mouse -files: +include: - app.yaml - icon.png - main.py diff --git a/projects/app_mouse_simulator/assets/image.png b/projects/app_mouse_simulator/assets/image.png new file mode 100644 index 00000000..0bb13838 Binary files /dev/null and b/projects/app_mouse_simulator/assets/image.png differ diff --git a/projects/app_rtsp/README.md b/projects/app_rtsp/README.md index 29572cbb..18744cdd 100644 --- a/projects/app_rtsp/README.md +++ b/projects/app_rtsp/README.md @@ -1,6 +1,28 @@ -# RTSP 推流 +## 1. 简介 +本工具是一款运行于Maix设备的简易RTSP音视频推流应用,无需复杂配置,启动后即可实现设备摄像头视频采集与音频录制,并通过RTSP协议进行音视频流推送,支持本地可视化操作界面,方便使用者快速部署和使用音视频推流服务。 -## 使用方法 -1. 点击右上角图标显示RTSP地址 -2. 点击左上角图标退出应用 -3. 可以使用ffplay拉流,命令参考`ffplay rtsp://192.168.0.240:8554/live`, 也可以使用`vlc`拉流,注意版本号必须是`` \ No newline at end of file +## 2. 主要功能 +1. **RTSP音视频推流**:绑定设备摄像头与音频录制模块,启动内置RTSP服务器,对外提供可访问的音视频流地址。 +2. **本地可视化显示**:实时展示摄像头采集的视频画面,提供直观的操作交互界面。 +3. **RTSP地址查看/隐藏切换**:支持手动切换是否在本地界面显示推流地址,方便使用者获取和记录。 +4. **快速退出应用**:提供可视化快捷退出按钮,无需命令行操作即可终止应用。 + +## 3. 使用说明。 + **启动应用**: + - 启动后应用将自动完成摄像头、音频、RTSP服务器的初始化。 + - 应用启动后,终端会自动打印RTSP推流地址列表; + - 也可在本地显示界面点击右侧上方的眼睛图标(切换为睁眼状态),界面将直接展示所有可用推流地址。 + **使用推流地址**:将获取到的RTSP地址输入至支持RTSP协议的音视频播放工具(如VLC播放器),即可远程查看设备采集的视频内容。 + **隐藏推流地址**:再次点击右侧上方的眼睛图标(切换为闭眼状态),即可隐藏界面上的推流地址。 + **退出应用**:点击界面左侧上方的退出图标,应用将自动终止运行,并完成RTSP服务器与音频模块的资源释放,无需额外执行清理操作。 +![alt text](./assets/image.png) +## 4. 注意事项 +2. **网络环境要求**:使用RTSP远程播放时,Maix设备与播放设备需处于同一局域网内,且网络状态稳定,避免出现音视频卡顿、断流现象。 +3. **操作延时说明**:眼睛图标切换(显示/隐藏地址)存在200ms操作延时,避免快速连续点击导致功能异常,单次点击后稍作等待即可完成状态切换。 +4. **资源释放说明**:应用退出时已内置自动资源释放逻辑,请勿在应用运行过程中强行通过终端终止程序(如使用`Ctrl+C`),否则可能导致设备摄像头、音频模块占用无法释放,影响后续使用。 +5. **推流地址有效性**:应用重启后,RTSP推流地址可能发生变化,如需重新使用,需再次获取最新地址。 + +## 5. 更多介绍 +[源码](https://github.com/sipeed/MaixPy/tree/main/projects/app_rtsp) + +[MaixCAM MaixPy 视频流 RTSP 推流](https://wiki.sipeed.com/maixpy/doc/zh/video/rtsp_streaming.html) \ No newline at end of file diff --git a/projects/app_rtsp/README_EN.md b/projects/app_rtsp/README_EN.md new file mode 100644 index 00000000..29902440 --- /dev/null +++ b/projects/app_rtsp/README_EN.md @@ -0,0 +1,36 @@ +## 1. Introduction +This tool is a simple RTSP audio and video streaming application designed to run on Maix devices. It requires no complex configuration; once launched, it enables video capture from the device camera and audio recording, pushing the streams via the RTSP protocol. It features a local visual interface for easy deployment and operation. + +## 2. Key Features +1. **RTSP Audio/Video Streaming**: Binds the device camera and audio recording module to start a built-in RTSP server, providing accessible stream URLs. +2. **Local Visual Display**: Real-time display of the camera feed with an intuitive interactive user interface. +3. **RTSP URL Toggle**: Supports manual switching to show or hide the streaming URL on the local interface for easy access and recording. +4. **Quick Exit**: Provides a visual shortcut button to terminate the application without needing command-line interaction. + +## 3. User Guide +**Launching the Application**: +* Upon startup, the application automatically initializes the camera, audio, and RTSP server. +* The terminal will automatically print the list of RTSP stream URLs. +* You can also click the eye icon at the top right of the local display (switching it to the "open" state) to view all available streaming URLs directly on the screen. + +**Using the Stream URL**: +* Enter the obtained RTSP URL into an RTSP-compatible media player (such as VLC Media Player) to view the device's video feed remotely. + +**Hiding the Stream URL**: +* Click the eye icon at the top right again (switching it to the "closed" state) to hide the URL from the interface. + +**Exiting the Application**: +* Click the exit icon at the top left of the interface. The application will terminate automatically and release resources for the RTSP server and audio module; no additional cleanup is required. + +![Interface Screenshot](./assets/image.png) + +## 4. Notes +1. **Network Requirements**: For remote playback, the Maix device and the viewing device must be on the same local network with a stable connection to avoid lag or stream interruptions. +2. **Operation Delay**: There is a 200ms delay for toggling the eye icon. Avoid rapid consecutive clicks to prevent functional anomalies; wait briefly after a single click for the state to switch. +3. **Resource Release**: The application includes built-in logic for automatic resource release upon exit. Do not force-terminate the program via the terminal (e.g., using `Ctrl+C`) while it is running, as this may leave the camera or audio module occupied, affecting future use. +4. **URL Validity**: The RTSP URL may change after the application restarts. If you need to use it again, please obtain the latest address. + +## 5. More Information +[Source Code](https://github.com/sipeed/MaixPy/tree/main/projects/app_rtsp) + +[MaixCAM MaixPy Video Stream RTSP Streaming](https://wiki.sipeed.com/maixpy/doc/en/video/rtsp_streaming.html) \ No newline at end of file diff --git a/projects/app_rtsp/app.yaml b/projects/app_rtsp/app.yaml index 1ee8849e..26ee63ea 100644 --- a/projects/app_rtsp/app.yaml +++ b/projects/app_rtsp/app.yaml @@ -6,7 +6,7 @@ icon: assets/app.png author: Sipeed Ltd desc: RTSP stream desc[zh]: RTSP 推流 -files: +include: app.yaml: app.yaml assets: assets README.md: README.md diff --git a/projects/app_rtsp/assets/image.png b/projects/app_rtsp/assets/image.png new file mode 100644 index 00000000..28c89351 Binary files /dev/null and b/projects/app_rtsp/assets/image.png differ diff --git a/projects/app_scan_qrcode/README.md b/projects/app_scan_qrcode/README.md index 7dce14e2..c75eb9a5 100644 --- a/projects/app_scan_qrcode/README.md +++ b/projects/app_scan_qrcode/README.md @@ -1,16 +1,52 @@ -Scan the qrcode in camera screen and send the result of the qrcode from the communication module(Default is uart). +## 1. 简介 +本工具是一款运行在MaixCam设备上的视觉识别应用,能够实现条形码、二维码(QR Code)以及AprilTag(TAG36H11)的实时识别与数据上报。应用具备直观的触控操作界面,识别结果会通过设备默认通信模块(默认为UART)进行发送,方便后续数据对接与处理。 -Example: +## 2. 主要功能 +1. 多类型视觉码识别:支持条形码、二维码(QR Code)、AprilTag(TAG36H11)三种格式的目标识别; +2. 实时视觉反馈:识别过程中会在设备屏幕上标注目标位置、显示识别结果详情,同时提供识别区域的动态提示; +3. 触控切换识别模式:通过屏幕下方触控按钮,可快速切换三种识别模式,操作简洁直观; +4. 数据通信上报:识别到有效目标后,会自动将结果通过通信模块(默认UART)上报,支持后续数据解析与应用; +5. 快捷退出功能:提供屏幕上方快捷退出入口,方便终止应用运行。 -1. run the program -```shell -./main.py -``` +## 3. 使用说明 +### 3.1 运行程序 +打开程序即可自动运行扫码 + +![](./assets/image.png) +### 3.2 切换识别模式 +程序启动后默认进入二维码(QR Code)识别模式,可通过屏幕下方三个触控按钮进行模式切换: +1. 左侧按钮:切换至条形码识别模式; +2. 中间按钮:切换至二维码(QR Code)识别模式; +3. 右侧按钮:切换至AprilTag(TAG36H11)识别模式; +4. 按钮被选中时会呈现深色高亮状态,同时屏幕上方会显示对应识别模式的提示文字。 -2. Put the qrcode in the camera, and you will see the qrcode is marked by a rectangle. -3. Check the data sent by the communication module. Use `app.get_sys_config_kv("comm", "method")` to get the protocol used by the current communication module. Default protocol is `uart`, baudrate is `115200`. -```shell +### 3.3 进行目标识别 +1. 切换至所需识别模式后,将待识别的条形码/二维码/AprilTag放置在屏幕中间的蓝色框选识别区域内; +2. 设备会实时采集图像并进行识别,识别成功后,目标会被红色矩形(或角点连线)标注,同时屏幕上会显示识别结果详情(二维码/条形码显示内容、AprilTag显示ID、家族、坐标等); +3. 以二维码识别为例:Put the qrcode in the camera, and you will see the qrcode is marked by a rectangle. + +### 3.4 查看上报数据 +1. 识别成功后,设备会自动将识别结果通过通信模块发送,默认通信协议为UART,波特率115200; +2. 可通过`app.get_sys_config_kv("comm", "method")`获取当前设备使用的通信协议; +3. 查看UART上报数据示例: +``` # uart data AA CA AC BB 0D 00 00 00 E1 05 31 32 33 34 35 36 37 38 39 1A 15 # 31 32 33 34 35 36 37 38 39: means the qrcode is "123456789" ``` +4. 条形码数据上报格式与二维码类似,AprilTag会上报家族、ID、坐标等结构化打包数据。 + +### 3.5 退出程序 +1. 点击屏幕上方的退出图标,图标会呈现高亮状态,此时开始计数; +2. 短暂等待后(计数超过2帧),程序会自动终止,设备屏幕清空并退出应用。 + +## 4. 注意事项 +1. 识别过程中请保持设备稳定,待识别目标与设备保持合适距离,避免过近、过远或角度倾斜过大,影响识别成功率; +2. 条形码识别有专属优化识别区域,切换至条形码模式后,请将条形码放置在对应优化区域内,以获得更好识别效果; +3. 避免在强光直射或光线过暗的环境下使用,光线条件不佳会严重影响视觉识别的准确性和实时性。 +4. AprilTag识别功能默认支持TAG36H11家族,如需支持其他AprilTag家族,可修改代码中`image.ApriltagFamilies`对应的参数并重新部署应用。 + +## 5. 更多介绍 +[源码](https://github.com/sipeed/MaixPy/tree/dev/projects/app_scan_qrcode) + +[MaixCAM MaixPy 二维码识别](https://wiki.sipeed.com/maixpy/doc/zh/vision/qrcode.html) diff --git a/projects/app_scan_qrcode/README_EN.md b/projects/app_scan_qrcode/README_EN.md new file mode 100644 index 00000000..9c594dd9 --- /dev/null +++ b/projects/app_scan_qrcode/README_EN.md @@ -0,0 +1,56 @@ +## 1. Introduction +This tool is a visual recognition application running on the MaixCam device. It enables real-time recognition and data reporting of barcodes, QR codes, and AprilTags (TAG36H11). The application features an intuitive touch interface, and recognition results are sent via the device's default communication module (UART by default), facilitating subsequent data integration and processing. + +## 2. Main Features +1. **Multi-type Visual Code Recognition:** Supports three formats: Barcode, QR Code, and AprilTag (TAG36H11). +2. **Real-time Visual Feedback:** During recognition, the target position is marked on the screen, detailed results are displayed, and dynamic prompts for the recognition area are provided. +3. **Touch-based Mode Switching:** Quickly switch between the three recognition modes using the touch buttons at the bottom of the screen. +4. **Data Communication Reporting:** Upon successful identification of a valid target, the result is automatically reported via the communication module (default UART). +5. **Quick Exit Function:** Provides a quick exit entry at the top of the screen to easily terminate the application. + +## 3. User Guide + +### 3.1 Running the Program +Open the program to start scanning automatically. + +![](./assets/image.png) + +### 3.2 Switching Recognition Modes +The program defaults to QR Code recognition mode. You can switch modes using the three touch buttons at the bottom of the screen: +1. **Left Button:** Switch to Barcode recognition mode. +2. **Middle Button:** Switch to QR Code recognition mode. +3. **Right Button:** Switch to AprilTag (TAG36H11) recognition mode. +4. The selected button will be highlighted in dark color, and a corresponding prompt will appear at the top of the screen. + +### 3.3 Performing Target Recognition +1. After switching to the desired mode, place the Barcode/QR Code/AprilTag within the blue boxed recognition area in the center of the screen. +2. The device captures images in real-time. Upon success, the target is marked with a red rectangle (or corner lines), and details are displayed (content for Barcodes/QR Codes; ID, Family, and Coordinates for AprilTags). +3. *Scan the qrcode in camera screen and send the result of the qrcode from the communication module(Default is uart).* + +### 3.4 Viewing Reported Data +1. After successful recognition, the result is sent via the communication module. The default protocol is UART with a baud rate of 115200. +2. Use `app.get_sys_config_kv("comm", "method")` to check the current communication protocol. +3. **Example:** + 1. Put the qrcode in the camera, and you will see the qrcode is marked by a rectangle. + 2. Check the data sent by the communication module. + ```shell + # uart data + AA CA AC BB 0D 00 00 00 E1 05 31 32 33 34 35 36 37 38 39 1A 15 + # 31 32 33 34 35 36 37 38 39: means the qrcode is "123456789" + ``` +4. Barcode data is reported in a similar format, while AprilTag reports structured data including family, ID, and coordinates. + +### 3.5 Exiting the Program +1. Click the exit icon at the top of the screen; the icon will highlight, and a countdown begins. +2. After a short wait (count exceeds 2 frames), the program automatically terminates, clears the screen, and exits. + +## 4. Notes +1. Keep the device stable during scanning. Maintain an appropriate distance from the target; avoid being too close, too far, or at an extreme angle. +2. Barcode recognition has an optimized ROI (Region of Interest). For best results, place barcodes within this specific area. +3. Avoid strong direct light or excessively dark environments, as poor lighting affects accuracy and speed. +4. The AprilTag function defaults to the TAG36H11 family. To support others, modify the `image.ApriltagFamilies` parameter in the code and redeploy. + +## 5. More Information +[Source Code](https://github.com/sipeed/MaixPy/tree/dev/projects/app_scan_qrcode) + +[MaixCAM MaixPy QR Code Recognition](https://wiki.sipeed.com/maixpy/doc/en/vision/qrcode.html) \ No newline at end of file diff --git a/projects/app_scan_qrcode/app.yaml b/projects/app_scan_qrcode/app.yaml index 19d0d6eb..f8934dec 100644 --- a/projects/app_scan_qrcode/app.yaml +++ b/projects/app_scan_qrcode/app.yaml @@ -6,7 +6,7 @@ icon: assets/scan.json author: Sipeed Ltd desc: Scan the qrcode/apriltag in camera screen and send the result of the qrcode from the communication module(Default is uart). desc[zh]: 扫描摄像头画面中的二维码或者Apriltag,并用通信模块(默认是uart)发送二维码的识别结果 -files: +include: app.yaml: app.yaml assets: assets README.md: README.md diff --git a/projects/app_scan_qrcode/assets/image.png b/projects/app_scan_qrcode/assets/image.png new file mode 100644 index 00000000..0499d19f Binary files /dev/null and b/projects/app_scan_qrcode/assets/image.png differ diff --git a/projects/app_self_learn_classifier/README.md b/projects/app_self_learn_classifier/README.md new file mode 100644 index 00000000..2d24cb84 --- /dev/null +++ b/projects/app_self_learn_classifier/README.md @@ -0,0 +1,47 @@ +## 1. 简介 +本工具是一款运行在MaixCam设备上的视觉自学习分类应用,基于MobileNet V2模型实现自定义类别与样本的采集、训练和实时识别。无需提前编写复杂模型训练代码,通过触控操作即可完成类别创建、样本添加、模型学习和结果验证,识别结果会直观地显示在设备屏幕上,适合快速实现简单的自定义视觉分类需求。 + +## 2. 主要功能 +1. **自定义类别管理**:支持创建新的视觉分类类别,类别会自动命名并持久化保存,可查看当前已创建类别数量。 +2. **样本采集添加**:为已创建的类别添加视觉样本,样本数据会随类别信息一同保存,支持查看当前总样本数量。 +3. **一键模型学习**:对已采集的类别和样本进行自动模型训练,无需手动配置训练参数,训练完成后自动保存模型。 +4. **实时视觉识别**:训练完成后,设备会实时采集图像并进行分类识别,显示当前识别结果及相似度评分。 +5. **数据清空功能**:支持一键清除所有已创建的类别、样本和保存的模型数据,方便重新开始分类学习。 +6. **快捷退出**:提供直观的退出按钮,可快速终止应用运行,返回设备主环境。 + +## 3. 使用说明 +### 3.1 启动应用 +直接运行即可启动,启动后设备会自动初始化相机、显示屏和触控功能,进入应用主界面,屏幕上会显示操作按钮和实时相机画面。 +![alt text](./assets/image.png) +### 3.2 核心操作流程(关键步骤) +1. **创建分类类别(+Class 按钮)** + - 点击屏幕上的「+Class」按钮,应用会自动采集当前相机画面中心区域作为类别初始样本,创建一个新的分类类别(命名格式为「Class X」,X为类别序号)。 + - 可多次点击该按钮创建多个不同的分类类别,屏幕上会实时显示当前已创建的「class」数量。 +2. **添加样本数据(+Sample 按钮)** + - 先确保已创建至少一个分类类别(未创建类别点击会提示「+Class first please」)。 + - 将需要作为该类别样本的物体放置在相机画面中心的白色方框内,保持画面稳定,点击「+Sample」按钮,即可为当前类别添加一份新的视觉样本。 + - 建议为每个类别添加多份不同角度、不同光线条件下的样本,提升后续识别的准确性,屏幕上会实时显示当前总「sample」数量。 +3. **模型自学习(Learn 按钮)** + - 完成类别和样本添加后,点击「Learn」按钮,应用会进入训练状态,屏幕显示「Learning ...」。 + - 训练完成后,会提示「Learn complete」,并更新屏幕上的「learn」次数,训练后的模型会自动持久化保存,即使关闭应用后重新启动,数据也不会丢失。 + - 若已完成过训练且未新增类别/样本,点击会提示「Already learned」。 +4. **实时查看识别结果** + - 模型训练完成后,应用会自动进入实时识别模式,相机实时采集画面并进行分类。 + - 屏幕上会显示当前识别到的类别名称(「Class X」),以及对应的相似度评分(similarity),评分越接近1.0,说明识别匹配度越高。 +5. **清空所有数据(Clear 按钮)** + - 点击「Clear」按钮,会一键删除所有已创建的类别、样本,以及保存在设备中的「my_classes.bin」模型文件,屏幕上的类别、样本数量会重置为0,识别结果也会清空。 +6. **退出应用(< Exit 按钮)** + - 点击「< Exit」按钮,即可快速终止应用运行,退出到Maix设备的主环境。 + + +## 4. 注意事项 +1. 操作顺序注意:必须先点击「+Class」创建类别,才能点击「+Sample」添加样本和「Learn」进行训练,否则会出现相应的提示信息。 +2. 样本采集注意:添加样本时,尽量保持目标物体在相机中心白色方框内,避免强光直射或光线过暗,同时可更换不同角度采集,提升模型识别的鲁棒性。 +3. 数据保存说明:类别、样本和训练后的模型会自动保存为「my_classes.bin」文件,删除该文件(或点击「Clear」按钮)会丢失所有学习数据,且无法恢复,操作前请谨慎。 +4. 设备稳定性:训练过程中(显示「Learning ...」)请保持设备稳定,避免训练中断或数据异常。 +5. 识别限制:本工具基于轻量级MobileNet V2模型,适合简单的视觉分类场景,复杂场景(如目标重叠、背景杂乱)可能会影响识别准确性。 + +## 5. 更多介绍 +[源码](https://github.com/sipeed/MaixPy/tree/dev/projects/app_self_learn_classifier) + +[MaixCAM MaixPy 使用 AI 模型进行物体分类](https://wiki.sipeed.com/maixpy/doc/zh/vision/classify.html) \ No newline at end of file diff --git a/projects/app_self_learn_classifier/README_EN.md b/projects/app_self_learn_classifier/README_EN.md new file mode 100644 index 00000000..7f895904 --- /dev/null +++ b/projects/app_self_learn_classifier/README_EN.md @@ -0,0 +1,53 @@ +## 1. Introduction +This tool is a visual self-learning classification application running on the MaixCam device. Based on the MobileNet V2 model, it enables the collection, training, and real-time recognition of custom categories and samples. Without the need to write complex model training code in advance, you can complete category creation, sample addition, model learning, and result verification through touch operations. Recognition results are displayed intuitively on the device screen, making it suitable for quickly implementing simple custom visual classification needs. + +## 2. Main Features +1. **Custom Category Management:** Supports creating new visual classification categories. Categories are automatically named and persistently saved, and you can view the number of currently created categories. +2. **Sample Collection and Addition:** Add visual samples to created categories. Sample data is saved along with category information, and you can view the total number of current samples. +3. **One-Click Model Learning:** Automatically trains the model on collected categories and samples without manual configuration of training parameters. The model is automatically saved upon completion of training. +4. **Real-Time Visual Recognition:** After training, the device captures images in real-time for classification and displays the current recognition results along with similarity scores. +5. **Data Clear Function:** Supports one-click clearing of all created categories, samples, and saved model data, making it easy to restart classification learning. +6. **Quick Exit:** Provides an intuitive exit button to quickly terminate the application and return to the device's main environment. + +## 3. User Guide + +### 3.1 Launching the Application +Simply run the program to start. Upon startup, the device automatically initializes the camera, display, and touch functions, entering the main application interface where operation buttons and the real-time camera feed are displayed. +![alt text](./assets/image.png) + +### 3.2 Core Operation Workflow (Key Steps) +1. **Creating Classification Categories (+Class Button)** + * Click the 「+Class」 button on the screen. The application automatically captures the central area of the current camera frame as the initial sample for the category and creates a new classification category (named in the format 「Class X」, where X is the category index). + * You can click this button multiple times to create several different classification categories. The screen displays the number of currently created 「class」 in real-time. + +2. **Adding Sample Data (+Sample Button)** + * First, ensure at least one classification category has been created (clicking without creating a category will prompt 「+Class first please」). + * Place the object to be used as a sample for the current category within the white box in the center of the camera frame. Keep the frame stable and click the 「+Sample」 button to add a new visual sample for the current category. + * It is recommended to add multiple samples from different angles and lighting conditions for each category to improve subsequent recognition accuracy. The screen displays the total number of current 「sample」 in real-time. + +3. **Model Self-Learning (Learn Button)** + * After adding categories and samples, click the 「Learn」 button. The application enters training mode, displaying 「Learning ...」 on the screen. + * Upon completion, it prompts 「Learn complete」 and updates the 「learn」 count on the screen. The trained model is automatically saved persistently, so data is not lost even if the application is closed and restarted. + * If training has already been completed and no new categories/samples have been added, clicking will prompt 「Already learned」. + +4. **Viewing Recognition Results in Real-Time** + * After model training is complete, the application automatically enters real-time recognition mode, capturing and classifying images live. + * The screen displays the name of the currently recognized category (「Class X」) and the corresponding similarity score. A score closer to 1.0 indicates a higher recognition match. + +5. **Clearing All Data (Clear Button)** + * Clicking the 「Clear」 button deletes all created categories, samples, and the 「my_classes.bin」 model file saved on the device. The category and sample counts on the screen reset to 0, and recognition results are cleared. + +6. **Exiting the Application (< Exit Button)** + * Click the 「< Exit」 button to quickly terminate the application and exit to the Maix device's main environment. + +## 4. Notes +1. **Operation Sequence:** You must first click 「+Class」 to create a category before clicking 「+Sample」 to add samples or 「Learn」 to train. Otherwise, a corresponding prompt will appear. +2. **Sample Collection:** When adding samples, try to keep the target object within the white box in the center of the camera view. Avoid direct strong light or excessively dark environments. Collecting from different angles improves the robustness of model recognition. +3. **Data Saving:** Categories, samples, and the trained model are automatically saved as the 「my_classes.bin」 file. Deleting this file (or clicking the 「Clear」 button) results in the loss of all learning data, which cannot be recovered. Please proceed with caution. +4. **Device Stability:** Please keep the device stable during training (while 「Learning ...」 is displayed) to avoid training interruptions or data anomalies. +5. **Recognition Limitations:** This tool is based on the lightweight MobileNet V2 model, making it suitable for simple visual classification scenarios. Complex scenarios (such as overlapping targets or cluttered backgrounds) may affect recognition accuracy. + +## 5. More Information +[Source Code](https://github.com/sipeed/MaixPy/tree/dev/projects/app_self_learn_classifier) + +[Using AI Models for Object Classification in MaixCAM MaixPy](https://wiki.sipeed.com/maixpy/doc/en/vision/classify.html) \ No newline at end of file diff --git a/projects/app_self_learn_classifier/app.yaml b/projects/app_self_learn_classifier/app.yaml index a373317f..4edf8f0f 100644 --- a/projects/app_self_learn_classifier/app.yaml +++ b/projects/app_self_learn_classifier/app.yaml @@ -5,7 +5,7 @@ version: 1.0.4 author: Sipeed Ltd icon: app.png desc: Learn anything on device, no PC training needed. -files: +include: - app.png - app.yaml - main.py diff --git a/projects/app_self_learn_classifier/assets/image.png b/projects/app_self_learn_classifier/assets/image.png new file mode 100644 index 00000000..50591c62 Binary files /dev/null and b/projects/app_self_learn_classifier/assets/image.png differ diff --git a/projects/app_self_learn_tracker/app.yaml b/projects/app_self_learn_tracker/app.yaml index 06096b10..da1478df 100644 --- a/projects/app_self_learn_tracker/app.yaml +++ b/projects/app_self_learn_tracker/app.yaml @@ -6,7 +6,7 @@ icon: assets/icon.png author: Sipeed Ltd desc: Learn and detect and track any object desc[zh]: 学习、检测、追踪任意物体 -files: +include: app.yaml: app.yaml assets: assets README.md: README.md diff --git a/projects/app_speech/README.md b/projects/app_speech/README.md index a64a0156..53922d95 100644 --- a/projects/app_speech/README.md +++ b/projects/app_speech/README.md @@ -1,7 +1,39 @@ -MaixPy Speech APP -===== +## 1. 简介 +本应用是基于MaixCAM2硬件开发的语音转文字工具,依托SenseVoice语音识别模型,支持通过长按录音按钮录制语音,并实时将录制的音频转换为文字内容,操作简单且交互直观。 +## 2. 主要功能 +- 语音录制:通过长按指定按钮启动音频录制,松开按钮停止录制。 +- 语音转文字:停止录制后自动调用语音识别模型,将录制的音频内容转换为文字。 +- 交互界面:提供可视化操作界面,清晰展示录制状态、转换结果及退出按钮。 +- 硬件适配检测:启动时自动检测设备内存规格,仅支持4GB版本的MaixCAM2硬件。 +## 3. 使用说明 +### 3.1 启动应用 +将应用部署至MaixCAM2设备后,直接运行程序即可启动,启动过程中界面会依次显示“初始化触摸屏”“加载语音识别模块”“初始化ASR模型”等加载提示,等待加载完成后进入主界面。 +### 3.2 核心操作 +1. **录制语音**:长按界面中间的录音图标(96×96尺寸的record.png图标),长按超过100ms后,界面文字区域显示“Recording..”,表示进入录音状态,此时开始录制语音。 +2. **停止录制与转写**:松开录音图标后,界面文字区域显示“Transcribing ...”,应用自动将录制的音频转换为文字,转换完成后文字区域显示识别结果。 +3. **退出应用**:点击界面左上角的退出图标(40×40尺寸的exit.jpg图标),即可退出应用程序。 +### 3.3 界面说明 +- 左上角:退出按钮,点击可关闭应用。 +- 中间区域:录音按钮+“Long press me”提示文字,长按录音按钮启动录音。 +- 下方文字区域:显示录音状态、转写状态及最终的语音转文字结果,初始显示“No text”。 +## 4. 注意事项 +1. **模型加载**:启动时初始化ASR模型可能耗时较长,界面会显示加载进度(1-8),需耐心等待加载完成,无需中途退出。 +2. **录音操作**:录音需长按按钮超过100ms才会触发,短按不会启动录音,避免误触。 +3. **文件依赖**:应用运行依赖指定路径的模型文件(/root/models/sensevoice-maixcam2/model.mud),缺失会导致运行异常。 +4. **音频参数**:录音采样率固定为16000Hz、单声道,无需手动设置,修改参数可能导致转写失败。 +5. **识别语言**:当前应用默认设置识别语言为英语(en),可通过修改代码中`self.language = 'en'`调整为其他支持的语言(需模型适配)。 +6. **音频缓存**:录音过程中音频数据会临时存储在内存中,转写完成后自动清空,若录制过长语音可能占用较多内存,建议单次录制时长不超过1分钟。 +8. **异常处理**:应用退出时会自动停止ASR模型并释放资源,避免强制退出导致资源泄漏;运行中若触发设备退出信号(app.need_exit()),也会自动清理资源后退出。 +9. **交互优化**:触摸检测支持20px的偏移容错,即使未精准点击图标区域,轻微偏移也可触发对应操作,提升操作便捷性。 + +## 5. 更多介绍 +[源码](https://github.com/sipeed/MaixPy/tree/main/projects/app_speech) + +[MaixPy MaixCAM 运行 SenseVoice 模型](https://wiki.sipeed.com/maixpy/doc/zh/mllm/asr_sensevoice.html) + +[MaixPy MaixCAM 运行 Whisper 模型](https://wiki.sipeed.com/maixpy/doc/zh/mllm/asr_whisper.html) \ No newline at end of file diff --git a/projects/app_speech/README_EN.md b/projects/app_speech/README_EN.md new file mode 100644 index 00000000..371f9a0e --- /dev/null +++ b/projects/app_speech/README_EN.md @@ -0,0 +1,39 @@ +## 1. Introduction +This application is a speech-to-text tool developed for the MaixCAM2 hardware. It utilizes the SenseVoice speech recognition model to support voice recording via a long-press button and converts the recorded audio into text. It features simple operation and an intuitive interface. + +## 2. Main Features +- **Voice Recording**: Start audio recording by long-pressing the designated button and stop by releasing it. +- **Speech-to-Text**: Automatically invokes the speech recognition model after recording stops to convert the audio content into text. +- **Interactive Interface**: Provides a visual interface that clearly displays the recording status, transcription results, and an exit button. +- **Hardware Compatibility Check**: Automatically detects the device memory specifications at startup. Only the 4GB version of the MaixCAM2 is supported. + +## 3. Usage Instructions +### 3.1 Starting the Application +After deploying the application to the MaixCAM2 device, simply run the program to start it. During startup, the screen will sequentially display loading prompts such as "Init touchscreen," "Loading sensevoice module," and "Init asr model." Wait for the loading to complete to enter the main interface. + +### 3.2 Core Operations +1. **Record Voice**: Long-press the recording icon in the center of the screen (the 96x96 `record.png` icon). After holding for more than 100ms, the text area on the screen will display "Recording..", indicating that recording has started. +2. **Stop Recording & Transcribe**: Release the recording icon. The text area will display "Transcribing ..." as the application automatically converts the audio to text. Once complete, the recognized text will appear in the text area. +3. **Exit Application**: Click the exit icon in the upper-left corner of the screen (the 40x40 `exit.jpg` icon) to close the application. + +### 3.3 Interface Description +- **Top-Left Corner**: Exit button; click to close the app. +- **Center Area**: Recording button + "Long press me" prompt text. Long-press to start recording. +- **Bottom Text Area**: Displays the recording status, transcription status, and final speech-to-text results. Initially displays "No text." + +## 4. Notes +1. **Model Loading**: Initializing the ASR model at startup may take a long time. The screen will display a loading progress (1-8). Please be patient and wait for it to finish; there is no need to exit midway. +2. **Recording Operation**: The recording requires a long press of more than 100ms to trigger. A short tap will not start recording, which helps prevent accidental triggers. +3. **File Dependencies**: The application relies on the model file located at a specific path (`/root/models/sensevoice-maixcam2/model.mud`). If this file is missing, the application will not run correctly. +4. **Audio Parameters**: The recording sample rate is fixed at 16000Hz, mono. No manual setup is required. Modifying these parameters may cause transcription failure. +5. **Recognition Language**: The application is set to recognize English (`en`) by default. This can be adjusted by modifying `self.language = 'en'` in the code to other supported languages (model compatibility required). +6. **Audio Caching**: Audio data is temporarily stored in memory during recording and automatically cleared after transcription. Recording very long audio clips may consume significant memory; it is recommended to keep recordings under 1 minute. +7. **Exception Handling**: The application automatically stops the ASR model and releases resources upon exit to prevent resource leaks caused by forced termination. If an exit signal (`app.need_exit()`) is triggered during operation, resources will also be cleaned up automatically. +8. **Interaction Optimization**: Touch detection supports a 20px offset tolerance. Even if you do not click the icon area precisely, a slight offset will still trigger the operation, improving usability. + +## 5. More Information +[Source Code](https://github.com/sipeed/MaixPy/tree/main/projects/app_speech) + +[Running SenseVoice Model on MaixPy MaixCAM](https://wiki.sipeed.com/maixpy/doc/en/mllm/asr_sensevoice.html) + +[Running Whisper Model on MaixPy MaixCAM](https://wiki.sipeed.com/maixpy/doc/en/mllm/asr_whisper.html) \ No newline at end of file diff --git a/projects/app_speech/app.yaml b/projects/app_speech/app.yaml index 6a6186b8..8ab327ed 100644 --- a/projects/app_speech/app.yaml +++ b/projects/app_speech/app.yaml @@ -6,7 +6,7 @@ icon: 'assets/voice.json' author: Sipeed Ltd desc: AI speech recognition desc[zh]: AI 语音识别 -files: +include: - assets - app.yaml - main.py diff --git a/projects/app_thermal256_camera/README.md b/projects/app_thermal256_camera/README.md new file mode 100644 index 00000000..07a195a5 --- /dev/null +++ b/projects/app_thermal256_camera/README.md @@ -0,0 +1,47 @@ +## 1. 简介 +本APP是专为 **MaixCAM2 设备**开发的热成像数据处理应用,能够实现热成像画面的实时预览、高清增强、温度数据提取与可视化,同时支持热成像画面和原始数据的本地保存,方便后续分析与使用。工具内置多种图像增强模型,可切换不同分辨率模式,满足不同场景下的热成像观测需求。 + +## 2. 主要功能 +1. **实时热成像预览**:以热力图形式呈现热成像画面,清晰展示被测物体的温度分布,支持 COLORMAP_MAGMA 伪彩色映射,提升温度差异辨识度。 +2. **温度数据自动提取**:自动识别并标注热成像画面中的中心温度、最高温度(H)、最低温度(L),温度值精确到小数点后1位。 +3. **图像分辨率增强**:支持切换 Lo-Res(原始分辨率)和 Hi-Res(高清增强分辨率)模式,高清模式下调用预训练图像增强模型,提升画面细节清晰度。 +4. **多格式数据保存**:支持捕获并保存热成像画面(JPG格式)、原始灰度数据、增强后灰度数据(NPY格式),方便后续数据复盘与专业分析。 +5. **HUD 界面切换**:支持显示/隐藏操作提示界面(HUD),既可以获取完整热成像画面,也可以通过提示快速上手操作。 +6. **便捷退出机制**:提供安全退出流程,退出时自动恢复设备原始 NPU 配置,避免影响设备后续使用。 + +## 3. 使用说明 +### 3.1 前期准备 +确保设备为 **MaixCAM2**,且已完成相关硬件接线(热成像模块与 MaixCAM2 的 I2C/SPI 引脚对应正确),工具已成功部署到设备中并正常启动。 + +### 3.2 启动与初始界面 +1. 工具启动后会自动完成初始化(包括引脚配置、I2C/SPI 初始化、热成像模块启动、模型加载等),初始界面会显示热力图预览及完整 HUD 操作提示。 +2. 初始化过程中若出现报错,需检查硬件接线和设备型号是否符合要求。 +![alt text](./assets/images.png) +### 3.3 核心操作(触摸屏操作) +本工具所有操作均通过设备触摸屏完成,核心操作如下: +1. **退出工具**:长按左上角(0≤x≤80,0≤y≤40)区域约1-2秒,待提示文字变色后,即可触发退出流程,工具会自动恢复设备配置并关闭。 +2. **捕获保存数据**:点击右上角(屏幕宽度-120≤x≤屏幕宽度,0≤y≤40)区域,即可捕获当前热成像画面及相关数据。 + - 保存路径:`/maixapp/share/picture/thermal/` + - 保存文件:包含 JPG 格式热力图、NPY 格式原始灰度数据、NPY 格式增强后灰度数据(高清模式下),文件名包含时间戳和帧编号,方便区分。 +3. **切换分辨率模式** + - 切换 Lo-Res(原始分辨率):点击左下角(0≤x≤120,屏幕高度-40≤y≤屏幕高度)区域。 + - 切换 Hi-Res(高清增强):点击右下角(屏幕宽度-120≤x≤屏幕宽度,屏幕高度-40≤y≤屏幕高度)区域,切换后会自动加载对应增强模型并更新画面。 +4. **HUD 界面切换** + - 隐藏 HUD:点击顶部中间(屏幕宽度//2-60≤x≤屏幕宽度//2+60,0≤y≤40)区域,隐藏所有操作提示,仅保留热成像画面。 + - 显示 HUD:点击底部中间(屏幕宽度//2-60≤x≤屏幕宽度//2+60,屏幕高度-40≤y≤屏幕高度)区域,恢复所有操作提示界面。 + +### 3.4 画面查看 +1. 热力图中绿色十字+圆圈为**中心温度**标注,白色十字为**最高温度**标注,蓝色十字为**最低温度**标注,对应温度值会显示在十字下方。 +2. 高清模式下画面细节更清晰,适合观测微小温度差异;原始分辨率模式下运行更流畅,适合快速实时观测。 + +## 4. 注意事项 +1. **设备兼容性**:本工具仅支持 **MaixCAM2 设备**,其他型号设备运行会直接报错,请勿在非目标设备上部署使用。 +2. **硬件接线**:使用前需确保热成像模块与 MaixCAM2 的引脚对应正确(A8/I2C7_SCL、A9/I2C7_SDA、B21/SPI2_CS1 等),接线错误会导致初始化失败或数据采集异常。 +3. **模型文件**:高清增强模式依赖预训练模型文件(`espcn_x3.mud`、`sr3_ir_32.mud` 等),需确保模型文件已放置在 `/root/models/` 目录下,缺失模型会导致高清模式无法正常工作。 +4. **存储空间**:捕获的数据文件会占用一定存储空间,长期使用需定期清理 `/maixapp/share/picture/thermal/` 目录下的旧文件,避免存储空间不足。 +5. **退出规范**:请务必通过长按左上角区域的正规流程退出工具,切勿强制断电或强制终止进程,否则可能无法恢复设备原始 NPU 配置,影响后续其他应用的使用。 +6. **运行耗时**:高清增强模式下因涉及模型推理,画面更新耗时会略高于原始分辨率模式,属于正常现象。 +7. **温度换算**:工具显示的温度为经过校准后的实际温度(基于热成像模块原始数据换算:原始值/64 - 273.15),若需更高精度温度测量,需对热成像模块进行单独校准。 + +## 5. 更多介绍 +[源码](https://github.com/sipeed/MaixPy/tree/main/projects/app_thermal256_camera) diff --git a/projects/app_thermal256_camera/README_EN.md b/projects/app_thermal256_camera/README_EN.md new file mode 100644 index 00000000..af5a5964 --- /dev/null +++ b/projects/app_thermal256_camera/README_EN.md @@ -0,0 +1,47 @@ +## 1. Introduction +This APP is a thermal imaging data processing application specifically developed for the **MaixCAM2 device**. It enables real-time preview, high-definition enhancement, temperature data extraction, and visualization of thermal imaging frames. It also supports local saving of thermal images and raw data for subsequent analysis. The tool features multiple built-in image enhancement models and supports resolution switching to meet thermal observation needs across different scenarios. + +## 2. Key Features +1. **Real-Time Thermal Preview:** Displays thermal images in a heatmap format to clearly show temperature distribution. It supports the COLORMAP_MAGMA pseudo-color mapping to enhance the visibility of temperature differences. +2. **Automatic Temperature Extraction:** Automatically identifies and annotates the Center Temperature, Maximum Temperature (H), and Minimum Temperature (L) in the frame, accurate to one decimal place. +3. **Image Resolution Enhancement:** Supports switching between Lo-Res (Original Resolution) and Hi-Res (Enhanced Resolution) modes. The Hi-Res mode utilizes a pre-trained image enhancement model to improve overall detail and clarity. +4. **Multi-Format Data Saving:** Supports capturing and saving thermal images (JPG), raw grayscale data, and enhanced grayscale data (NPY format), facilitating data review and professional analysis. +5. **HUD Toggle:** Allows showing or hiding the Heads-Up Display (HUD) to either view the full thermal frame or use the interface for quick operational guidance. +6. **Safe Exit Mechanism:** Provides a secure exit process that automatically restores the device's original NPU configuration to prevent interference with subsequent usage. + +## 3. User Guide +### 3.1 Prerequisites +Ensure the device is a **MaixCAM2** and that hardware wiring is complete (Thermal module pins correctly mapped to MaixCAM2's I2C/SPI pins). Ensure the tool has been successfully deployed and launched on the device. +![alt text](./assets/images.png) +### 3.2 Startup and Initial Interface +1. Upon startup, the tool automatically initializes (including pin configuration, I2C/SPI setup, thermal module startup, and model loading). The initial screen displays the thermal heatmap preview along with the full HUD. +2. If errors occur during initialization, check that the hardware wiring and device model meet the requirements. + +### 3.3 Core Operations (Touchscreen) +All operations are performed via the touchscreen: +1. **Exit Tool:** Long-press the top-left corner (Area: 0≤x≤80, 0≤y≤40) for approximately 1-2 seconds. When the text color changes, the exit process is triggered, restoring the device configuration and closing the tool. +2. **Capture & Save Data:** Tap the top-right corner (Area: Screen_Width-120≤x≤Screen_Width, 0≤y≤40) to capture the current frame and data. + * **Save Path:** `/maixapp/share/picture/thermal/` + * **Saved Files:** Includes a JPG heatmap, raw grayscale data (NPY), and enhanced grayscale data (NPY, if in Hi-Res mode). Filenames include timestamps and frame numbers for easy differentiation. +3. **Switch Resolution Mode:** + * **Switch to Lo-Res:** Tap the bottom-left corner (Area: 0≤x≤120, Screen_Height-40≤y≤Screen_Height). + * **Switch to Hi-Res:** Tap the bottom-right corner (Area: Screen_Width-120≤x≤Screen_Width, Screen_Height-40≤y≤Screen_Height). The corresponding enhancement model will load automatically. +4. **Toggle HUD:** + * **Hide HUD:** Tap the top-middle area (Area: Screen_Width//2-60≤x≤Screen_Width//2+60, 0≤y≤40) to hide all prompts and view only the thermal image. + * **Show HUD:** Tap the bottom-middle area (Area: Screen_Width//2-60≤x≤Screen_Width//2+60, Screen_Height-40≤y≤Screen_Height) to restore the interface. + +### 3.4 Viewing the Image +1. **Annotations:** The **Center Temperature** is marked by a green cross (+ circle), the **Maximum Temperature** by a white cross, and the **Minimum Temperature** by a blue cross. The corresponding temperature value is displayed below each cross. +2. **Modes:** The Hi-Res mode offers clearer details, suitable for observing minute temperature differences. The Lo-Res mode runs more smoothly, suitable for fast real-time observation. + +## 4. Notes +1. **Compatibility:** This tool supports only the **MaixCAM2**. Running it on other models will result in errors. +2. **Wiring:** Ensure correct pin connections (A8/I2C7_SCL, A9/I2C7_SDA, B21/SPI2_CS1, etc.). Incorrect wiring causes initialization failure or data acquisition errors. +3. **Model Files:** Hi-Res mode relies on pre-trained models (`espcn_x3.mud`, `sr3_ir_32.mud`, etc.). Ensure these files are placed in the `/root/models/` directory; missing files will disable Hi-Res functionality. +4. **Storage:** Captured data consumes storage space. Regularly clear old files in `/maixapp/share/picture/thermal/` to prevent running out of space. +5. **Exit Protocol:** Always exit via the long-press top-left method. Do not force power off or kill the process, as this may fail to restore the NPU configuration. +6. **Performance:** Hi-Res mode involves model inference, resulting in slightly higher latency compared to Lo-Res mode. This is normal. +7. **Temperature Calibration:** Displayed temperatures are calculated based on raw sensor data (Formula: Raw_Value/64 - 273.15). For higher precision measurements, the thermal module requires individual calibration. + +## 5. More Information +[Source Code](https://github.com/sipeed/MaixPy/tree/main/projects/app_thermal256_camera) \ No newline at end of file diff --git a/projects/app_thermal256_camera/app.yaml b/projects/app_thermal256_camera/app.yaml index 6b692b2f..a82cdfed 100644 --- a/projects/app_thermal256_camera/app.yaml +++ b/projects/app_thermal256_camera/app.yaml @@ -6,7 +6,7 @@ icon: assets/thermal.json author: Sipeed Ltd desc: Thermal Camera desc[zh]: 热成像仪 -files: +include: - assets/thermal.json - app.yaml - main.py diff --git a/projects/app_thermal256_camera/assets/images.png b/projects/app_thermal256_camera/assets/images.png new file mode 100644 index 00000000..7bfc932f Binary files /dev/null and b/projects/app_thermal256_camera/assets/images.png differ diff --git a/projects/app_thermal256_nightvision/README.md b/projects/app_thermal256_nightvision/README.md new file mode 100644 index 00000000..9bf6bd10 --- /dev/null +++ b/projects/app_thermal256_nightvision/README.md @@ -0,0 +1,77 @@ +## 1. 简介 +本系统是一款运行在 **MaixCAM2 设备**上的热成像与可见光图像融合应用,能够实现热成像数据采集、温度可视化、图像超分增强以及多种模式下的图像融合展示,支持触屏交互配置参数,可广泛用于设备测温、场景巡检、异常热源排查等场景。 + +## 2. 主要功能 +1. **四种工作模式切换**:支持可见光模式(Vis)、纯热成像模式(Therm)、自适应混合融合模式(Mix)、边缘增强融合模式(Edge),满足不同场景下的观测需求。 +2. **热成像增强**:内置超分模型(SR),可对热成像图像进行清晰度提升,支持手动开启/关闭该功能。 +3. **热成像伪彩切换**:提供Hot、Cool、Magma、Turbo、Night五种伪彩色映射方案,方便用户更清晰地分辨温度差异。 +4. **参数灵活配置**:支持触屏调节图像缩放比例、偏移量(X/Y轴),配置参数自动保存,下次启动无需重新调整。 +5. **温度差异可视化**:实时计算并显示热成像图像的温度差值,边缘增强功能会根据温度差值自动触发,突出高温/低温异常区域。 +6. **HUD界面隐藏/显示**:支持隐藏操作界面,获得纯净的观测画面,再次唤起即可恢复操作功能。 + +## 3. 使用说明 +### 3.1 系统启动 +1. 确保设备为 MaixCAM2,已完成相关驱动和依赖环境部署。 +2. 将热成像模块正确连接至 MaixCAM2 对应的 I2C/SPI 引脚(代码已预设引脚配置,无需额外修改)。 +3. 上传应用程序及相关文件(超分模型 `sr2.5_ir32_npu1.mud`、字体文件、配置文件)至设备指定路径。 +4. 运行应用程序,系统将自动完成初始化(摄像头、热成像模块、模型加载),初始化过程约8秒,屏幕显示"Initializing (Wait 8s)..."提示。 +5. 初始化完成后,默认进入纯热成像模式(Therm),即可开始使用。 +![alt text](./assets/image.png) +### 3.2 触屏操作指南 +#### 3.2.1 基础操作 +- **模式切换**:点击屏幕左上角(0,0 至 150,80 区域),循环切换 Vis/Therm/Mix/Edge 四种工作模式。 +- **HUD界面切换**:点击屏幕左下角(0, h-80 至 120, h 区域,h 为屏幕高度),隐藏/显示操作界面(隐藏后仅保留绿色小圆点提示,再次点击该区域恢复)。 + +#### 3.2.2 纯热成像模式(Therm)专属操作 +- **超分(SR)开关**:点击屏幕左侧中间(0, h/2-40 至 100, h/2+40 区域),开启/关闭热成像图像超分增强功能(开启后图像更清晰,绿色显示表示开启,红色表示关闭)。 +- **伪彩切换**:点击屏幕右侧中间(w-120, h/2-40 至 w, h/2+40 区域,w 为屏幕宽度),循环切换五种伪彩色映射方案,当前伪彩名称显示在屏幕右侧中间。 + +#### 3.2.3 混合/边缘融合模式(Mix/Edge)专属操作 +- 继承纯热成像模式的 **超分开关**、**伪彩切换** 功能(Mix模式超分独立配置,与Therm模式互不影响)。 +- **缩放比例调节**:点击屏幕上方右侧(w-180, 50 至 w-80, 130 区域)减少缩放比例(最小0.1),点击(w-80, 50 至 w, 130 区域)增加缩放比例,当前缩放比例显示在屏幕右上角。 +- **图像偏移调节**:点击屏幕右下角(w-160, h-160 至 w, h 区域),通过滑动调整图像X/Y轴偏移量: + - 左右滑动:调整X轴偏移量(< 向左偏移,> 向右偏移) + - 上下滑动:调整Y轴偏移量(^ 向上偏移,v 向下偏移) + - 偏移量实时显示在屏幕右下角。 + +#### 3.2.4 可见光模式(Vis)专属操作 +仅支持 **模式切换** 和 **HUD界面切换**,无额外参数配置功能。 + +### 3.3 数据保存说明 +应用运行过程中,所有配置参数(缩放比例、X/Y偏移量、伪彩索引、超分开关状态等)会在程序正常退出时自动保存至 `/root/fusion.json` 文件,下次启动应用将自动加载上次的配置,无需重复调整。 + +## 4. 注意事项 +1. **设备兼容性**:本应用仅支持 **MaixCAM2 设备**,运行在其他设备上会直接抛出异常,请勿在非目标设备上部署。 +2. **初始化要求**: + - 初始化过程中请勿断电或操作设备,否则可能导致热成像模块配置失败。 + - 热成像模块初始化需约8秒(包含预览停止、启动、温度预览启动等步骤),耐心等待屏幕切换至工作界面即可。 +3. **硬件连接**:确保热成像模块与 MaixCAM2 的 I2C7(A8/A9)、SPI2(B18/B19/B20/B21)引脚连接牢固,接触不良会导致数据采集失败(屏幕无热成像画面)。 +4. **文件依赖**: + - 超分模型文件 `sr2.5_ir32_npu1.mud` 需放置在 `/root/models/` 目录下,缺失将导致超分功能无法使用。 + - 字体文件 `SourceHanSansCN-Regular.otf` 需放置在 `/maixapp/share/font/` 目录下,缺失将导致界面文字无法正常显示。 +5. **性能说明**:开启超分功能后,系统处理帧率会有所下降,属于正常现象,优先保证图像清晰度。 +6. **温度阈值**:边缘增强功能默认触发阈值为5.0,当热成像图像温度差值小于该阈值时,边缘增强不生效,仅显示融合图像。 +7. **正常退出**:请通过设备正常流程退出应用,避免强制断电,否则配置参数无法自动保存。 + +## 5. 更多介绍 +### 5.1 各工作模式详细说明 +1. **可见光模式(Vis)**:仅显示摄像头采集的可见光画面,无热成像相关信息,适用于正常光线环境下的常规观测。 +2. **纯热成像模式(Therm)**:仅显示热成像模块采集的温度图像,通过伪彩区分温度高低,适用于黑暗、烟雾等可见光无法穿透的环境,或需要快速排查热源的场景。 +3. **自适应混合融合模式(Mix)**:将可见光图像与热成像图像进行自适应融合,根据热成像图像的温度差异自动调整融合权重,既保留场景细节,又突出异常热源,适用于需要同时观察场景环境和温度信息的场景。 +4. **边缘增强融合模式(Edge)**:提取热成像图像的边缘信息并叠加到可见光图像上,突出高温/低温区域的轮廓,适用于需要精准定位异常热源边界的场景。 + +### 5.2 配置文件说明 +配置文件 `/root/fusion.json` 为JSON格式,包含以下核心字段: +- `scale`:图像缩放比例(默认1.0) +- `x`/`y`:图像X/Y轴偏移量(默认0) +- `cmap_idx`:伪彩索引(默认0,对应Hot伪彩) +- `sr_therm`/`sr_mix`/`sr_edge`:各模式超分功能开关状态(默认True/False/True) + +若配置文件损坏或丢失,应用将自动加载默认配置,不影响正常运行。 + +### 5.3 扩展说明 +1. 可修改代码中的 `EDGE_TEMP_THRESHOLD` 变量调整边缘增强触发阈值,适应不同场景的温度检测需求。 +2. 可新增伪彩色映射方案至 `CMAP_LIST` 列表,扩展热成像图像的显示效果。 +3. 支持更换超分模型,只需替换 `/root/models/` 目录下的模型文件,并修改代码中的模型名称和输出层名称即可。 + +[源码](https://github.com/sipeed/MaixPy/tree/main/projects/app_thermal256_nightvision) \ No newline at end of file diff --git a/projects/app_thermal256_nightvision/README_EN.md b/projects/app_thermal256_nightvision/README_EN.md new file mode 100644 index 00000000..8ba58214 --- /dev/null +++ b/projects/app_thermal256_nightvision/README_EN.md @@ -0,0 +1,77 @@ +## 1. Overview +This system is a thermal imaging and visible light image fusion application running on the **MaixCAM2 device**. It enables thermal imaging data acquisition, temperature visualization, image super-resolution enhancement, and image fusion display in multiple modes, with touchscreen interactive parameter configuration. It can be widely used in scenarios such as equipment temperature measurement, scene inspection, and abnormal heat source troubleshooting. + +## 2. Key Features +1. **Four Working Mode Switching**: Supports Visible Light Mode (Vis), Pure Thermal Imaging Mode (Therm), Adaptive Hybrid Fusion Mode (Mix), and Edge-Enhanced Fusion Mode (Edge) to meet observation needs in different scenarios. +2. **Thermal Imaging Enhancement**: Built-in Super-Resolution (SR) model to improve the clarity of thermal imaging images, with manual on/off control for this function. +3. **Thermal Imaging Pseudocolor Switching**: Provides five pseudocolor mapping schemes (Hot, Cool, Magma, Turbo, Night) to help users distinguish temperature differences more clearly. +4. **Flexible Parameter Configuration**: Supports touchscreen adjustment of image scaling ratio and offset (X/Y axes). Configuration parameters are automatically saved, eliminating the need for re-adjustment at the next startup. +5. **Temperature Difference Visualization**: Real-time calculation and display of temperature differences in thermal imaging images. The edge enhancement function is automatically triggered based on temperature differences to highlight abnormal high/low temperature areas. +6. **HUD Interface Hide/Display**: Supports hiding the operation interface to obtain a pure observation screen, which can be restored with a single tap again. + +## 3. User Instructions +### 3.1 System Startup +1. Ensure the device is MaixCAM2 with relevant drivers and dependency environments deployed. +2. Correctly connect the thermal imaging module to the corresponding I2C/SPI pins of MaixCAM2 (pin configuration is preset in the code, no additional modification required). +3. Upload the application program and related files (super-resolution model `sr2.5_ir32_npu1.mud`, font file, configuration file) to the specified path of the device. +4. Run the application program. The system will automatically complete initialization (camera, thermal imaging module, model loading), which takes about 8 seconds. The screen displays the prompt "Initializing (Wait 8s)...". +5. After initialization is completed, the system enters Pure Thermal Imaging Mode (Therm) by default, and you can start using it immediately. +![alt text](./assets/image.png) +### 3.2 Touchscreen Operation Guide +#### 3.2.1 Basic Operations +- **Mode Switching**: Tap the top left corner of the screen (area from (0,0) to (150,80)) to cycle through the four working modes: Vis/Therm/Mix/Edge. +- **HUD Interface Switching**: Tap the bottom left corner of the screen (area from (0, h-80) to (120, h), where h is the screen height) to hide/display the operation interface (only a green dot prompt remains after hiding, and tapping this area again restores the interface). + +#### 3.2.2 Exclusive Operations for Pure Thermal Imaging Mode (Therm) +- **Super-Resolution (SR) Switch**: Tap the middle left of the screen (area from (0, h/2-40) to (100, h/2+40)) to turn on/off the thermal imaging image super-resolution enhancement function (the image becomes clearer when enabled, displayed in green for on and red for off). +- **Pseudocolor Switching**: Tap the middle right of the screen (area from (w-120, h/2-40) to (w, h/2+40), where w is the screen width) to cycle through the five pseudocolor mapping schemes. The current pseudocolor name is displayed in the middle right of the screen. + +#### 3.2.3 Exclusive Operations for Hybrid/Edge Fusion Modes (Mix/Edge) +- Inherits the **Super-Resolution Switch** and **Pseudocolor Switching** functions of the Pure Thermal Imaging Mode (the super-resolution of the Mix mode is independently configured and does not affect the Therm mode). +- **Scaling Ratio Adjustment**: Tap the upper right of the screen (area from (w-180, 50) to (w-80, 130)) to decrease the scaling ratio (minimum 0.1), and tap the area from (w-80, 50) to (w, 130) to increase the scaling ratio. The current scaling ratio is displayed in the top right of the screen. +- **Image Offset Adjustment**: Tap the bottom right of the screen (area from (w-160, h-160) to (w, h)) and adjust the image X/Y axis offset by sliding: + - Left/right sliding: Adjust the X-axis offset (< for left offset, > for right offset) + - Up/down sliding: Adjust the Y-axis offset (^ for upward offset, v for downward offset) + - The offset is displayed in real-time in the bottom right of the screen. + +#### 3.2.4 Exclusive Operations for Visible Light Mode (Vis) +Only supports **Mode Switching** and **HUD Interface Switching**, with no additional parameter configuration functions. + +### 3.3 Data Saving Instructions +During the operation of the application, all configuration parameters (scaling ratio, X/Y offset, pseudocolor index, super-resolution switch status, etc.) are automatically saved to the `/root/fusion.json` file when the program exits normally. The next time the application is started, it will automatically load the last configuration without the need for repeated adjustments. + +## 4. Notes +1. **Device Compatibility**: This application only supports the **MaixCAM2 device**. Running it on other devices will directly throw an exception. Do not deploy it on non-target devices. +2. **Initialization Requirements**: + - Do not power off or operate the device during initialization, otherwise, the thermal imaging module configuration may fail. + - The initialization of the thermal imaging module takes about 8 seconds (including preview stop, start, temperature preview start, etc.). Please wait patiently for the screen to switch to the working interface. +3. **Hardware Connection**: Ensure the thermal imaging module is firmly connected to the I2C7 (A8/A9) and SPI2 (B18/B19/B20/B21) pins of MaixCAM2. Poor contact will lead to data acquisition failure (no thermal imaging image on the screen). +4. **File Dependencies**: + - The super-resolution model file `sr2.5_ir32_npu1.mud` must be placed in the `/root/models/` directory. Its absence will render the super-resolution function unavailable. + - The font file `SourceHanSansCN-Regular.otf` must be placed in the `/maixapp/share/font/` directory. Its absence will result in the failure of normal display of interface text. +5. **Performance Notes**: Enabling the super-resolution function will cause a certain drop in the system processing frame rate, which is a normal phenomenon to prioritize image clarity. +6. **Temperature Threshold**: The default trigger threshold for the edge enhancement function is 5.0. When the temperature difference of the thermal imaging image is less than this threshold, the edge enhancement does not take effect, and only the fused image is displayed. +7. **Normal Exit**: Please exit the application through the normal device process to avoid forced power off, otherwise, the configuration parameters cannot be automatically saved. + +## 5. Further Introduction +### 5.1 Detailed Explanation of Each Working Mode +1. **Visible Light Mode (Vis)**: Only displays the visible light image captured by the camera without thermal imaging-related information. Suitable for regular observation in normal light environments. +2. **Pure Thermal Imaging Mode (Therm)**: Only displays the temperature image collected by the thermal imaging module, distinguishing temperature levels through pseudocolors. Suitable for environments where visible light cannot penetrate (such as darkness and smoke) or scenarios that require rapid troubleshooting of heat sources. +3. **Adaptive Hybrid Fusion Mode (Mix)**: Adaptively fuses visible light images and thermal imaging images, and automatically adjusts the fusion weight according to the temperature difference of thermal imaging images. It not only retains scene details but also highlights abnormal heat sources, suitable for scenarios that require simultaneous observation of scene environment and temperature information. +4. **Edge-Enhanced Fusion Mode (Edge)**: Extracts edge information from thermal imaging images and overlays it on visible light images to highlight the contours of high/low temperature areas. Suitable for scenarios that require accurate positioning of the boundaries of abnormal heat sources. + +### 5.2 Configuration File Explanation +The configuration file `/root/fusion.json` is in JSON format and contains the following core fields: +- `scale`: Image scaling ratio (default 1.0) +- `x`/`y`: Image X/Y axis offset (default 0) +- `cmap_idx`: Pseudocolor index (default 0, corresponding to Hot pseudocolor) +- `sr_therm`/`sr_mix`/`sr_edge`: Super-resolution function switch status of each mode (default True/False/True) + +If the configuration file is damaged or lost, the application will automatically load the default configuration without affecting normal operation. + +### 5.3 Extension Instructions +1. The edge enhancement trigger threshold can be adjusted by modifying the `EDGE_TEMP_THRESHOLD` variable in the code to adapt to the temperature detection needs of different scenarios. +2. New pseudocolor mapping schemes can be added to the `CMAP_LIST` to expand the display effects of thermal imaging images. +3. Supports replacing the super-resolution model. Simply replace the model file in the `/root/models/` directory and modify the model name and output layer name in the code. + +[Source Code](https://github.com/sipeed/MaixPy/tree/main/projects/app_thermal256_nightvision) diff --git a/projects/app_thermal256_nightvision/app.yaml b/projects/app_thermal256_nightvision/app.yaml index 6db9db45..ce7fa572 100644 --- a/projects/app_thermal256_nightvision/app.yaml +++ b/projects/app_thermal256_nightvision/app.yaml @@ -6,7 +6,7 @@ icon: assets/icon.png author: Sipeed Ltd desc: Thermal256 Night Vision desc[zh]: 热融合夜视仪 -files: +include: - assets - app.yaml - main.py diff --git a/projects/app_thermal256_nightvision/assets/image.png b/projects/app_thermal256_nightvision/assets/image.png new file mode 100644 index 00000000..0ac440ae Binary files /dev/null and b/projects/app_thermal256_nightvision/assets/image.png differ diff --git a/projects/app_usb_hand_touch/README.md b/projects/app_usb_hand_touch/README.md index 1b28a872..5327d3a2 100644 --- a/projects/app_usb_hand_touch/README.md +++ b/projects/app_usb_hand_touch/README.md @@ -1,7 +1,41 @@ -Hand simulate USB HID touch input device -======= +## 1. 简介 +本程序适用于Maixcam/Pro/2系列硬件设备,通过调用摄像头采集画面、手部关键点检测模型识别手部动作,将识别到的手部位置和按压动作转化为HID触摸板指令,实现无接触的手势操控电脑/设备触摸板功能。 -MaixCAM as USB input for other host(like PC) via USB HID protocol, so you can use your hand to control the mouse pointer on PC. +## 2. 主要功能 +- **手部位置追踪**:实时识别画面中手部位置,并将其映射为触摸板的绝对坐标,移动手部即可控制触摸板光标移动。 +- **手势按压模拟**:识别拇指与中指的并拢/分开动作,模拟触摸板左键按压/释放操作(拇指和中指靠近时触发按压,分开时释放)。 +- **自动复位**:若3秒内未检测到手部,自动取消按压状态,避免误操作。 +- **可视化反馈**:实时显示摄像头画面及手部关键点检测结果,便于确认识别状态。 +## 3. 使用说明 +### 3.1 前置准备 +1. **USB HID模式设置**: + - 进入设备“设置 -> USB设置 -> HID Touchpad”,点击确认启用; +2. **模型文件确认**:确保设备内`/root/models/`路径下存在`hand_landmarks.mud`(或`hand_landmarks_bf16.mud`)手部关键点检测模型文件。 +### 3.2 运行程序 +1. 将程序文件上传至设备并执行; +2. 程序启动后,摄像头会自动开启,画面实时显示手部检测结果; +3. 操控方式(需要将摄像头对准人): + - 移动手部:在摄像头画面的检测区域(画面10%~90%宽、10%~80%高范围)内移动手部,触摸板光标会同步移动; + - 模拟点击:将拇指与中指并拢(画面中两点距离小于阈值),触发触摸板左键按压;分开手指则释放按压。 +### 3.3 退出程序 +- 按设备物理按键退出,或通过设备管理界面终止程序进程。 + +## 4. 注意事项 +1. **环境要求**:使用时确保光线充足,避免强光/逆光,否则会降低手部识别准确率; +2. **识别范围**:手部需处于摄像头画面的检测区域内(10%~90%宽、10%~80%高),超出范围可能无法识别; +3. **USB连接**:设备需通过USB线连接至电脑/目标设备,且已正确启用HID Touchpad模式,否则程序会提示HID未就绪,若未连接电脑则会报错; +4. **模型兼容性**:Maixcam-pro使用bf16格式模型,普通Maixcam使用默认模型,程序会自动适配,但需确保对应模型文件存在; +5. **误操作避免**:若长时间不使用,建议退出程序,防止手部误进入检测区域触发不必要的触摸板操作。 +6. **参数可调项**: + - `SHOW_IMG`:控制是否显示摄像头画面(默认开启); + - `DEBUG_MODE`:开启后强制显示画面,便于调试; + - `detect_roi`:调整手部检测区域(百分比单位),可根据使用场景优化识别范围; + - `target_screen_roi`:调整触摸板坐标映射的屏幕区域,适配不同显示设备。 +7. **扩展功能**:程序预留了键盘按键发送接口(`send_keys`函数),可基于手部关键点识别扩展更多手势指令(如滑动、滚轮、快捷键等); +8. **画质调节**:`trans_img_quality`参数可调整画面传输画质,数值越低画质越差但传输速度越快,可根据网络/设备性能调整。 + +## 5. 更多介绍 +[源码](https://github.com/sipeed/MaixPy/tree/main/projects/app_usb_hand_touch) \ No newline at end of file diff --git a/projects/app_usb_hand_touch/README_EN.md b/projects/app_usb_hand_touch/README_EN.md new file mode 100644 index 00000000..1153ead0 --- /dev/null +++ b/projects/app_usb_hand_touch/README_EN.md @@ -0,0 +1,41 @@ +## 1. Introduction +This program is applicable to Maix series hardware devices such as Maixcam/Pro/2. It captures images via the camera and identifies hand movements using a hand keypoint detection model, converting the recognized hand position and pressing actions into HID touchpad commands to achieve contactless gesture control of a computer/device touchpad. + +## 2. Main Features +- **Hand Position Tracking**: Real-time recognition of hand position in the frame, mapping it to absolute coordinates of the touchpad. Moving the hand synchronously controls the movement of the touchpad cursor. +- **Gesture Press Simulation**: Recognizes the closing/separation action of the thumb and middle finger to simulate the left-click press/release operation of the touchpad (press is triggered when the thumb and middle finger are close, and release when separated). +- **Automatic Reset**: Automatically cancels the press state if no hand is detected within 3 seconds to avoid misoperation. +- **Visual Feedback**: Displays the camera frame and hand keypoint detection results in real time for easy confirmation of recognition status. + +## 3. Usage Instructions +### 3.1 Preparations +1. **USB HID Mode Settings**: + - Enter the device's "Settings -> USB Settings -> HID Touchpad" and click Confirm to enable it; +2. **Model File Confirmation**: Ensure that the hand keypoint detection model file `hand_landmarks.mud` (or `hand_landmarks_bf16.mud`) exists in the `/root/models/` directory of the device. + +### 3.2 Running the Program +1. Upload the program file to the device and execute it; +2. After the program starts, the camera will turn on automatically, and the frame will display hand detection results in real time; +3. Control Methods (the camera needs to be aimed at the user): + - Moving the Hand: Move the hand within the detection area of the camera frame (10%~90% of the frame width, 10%~80% of the frame height), and the touchpad cursor will move synchronously; + - Simulating a Click: Bring the thumb and middle finger together (the distance between the two points in the frame is less than the threshold) to trigger the left-click press of the touchpad; separate the fingers to release the press. + +### 3.3 Exiting the Program +- Press the physical button of the device to exit, or terminate the program process through the device management interface. + +## 4. Notes +1. **Environmental Requirements**: Ensure sufficient light during use and avoid strong/backlight, otherwise the hand recognition accuracy will be reduced; +2. **Recognition Range**: The hand must be within the detection area of the camera frame (10%~90% of the frame width, 10%~80% of the frame height); recognition may fail if out of this range; +3. **USB Connection**: The device must be connected to a computer/target device via a USB cable, and the HID Touchpad mode must be enabled correctly; otherwise, the program will prompt that HID is not ready, and an error will be reported if not connected to a computer; +4. **Model Compatibility**: Maixcam-pro uses the bf16 format model, while the regular Maixcam uses the default model. The program will adapt automatically, but ensure the corresponding model file exists; +5. **Misoperation Prevention**: If not in use for a long time, it is recommended to exit the program to prevent unnecessary touchpad operations triggered by the hand accidentally entering the detection area; +6. **Adjustable Parameters**: + - `SHOW_IMG`: Controls whether to display the camera frame (enabled by default); + - `DEBUG_MODE`: Forces frame display when enabled, facilitating debugging; + - `detect_roi`: Adjusts the hand detection area (in percentage units), which can be optimized according to usage scenarios; + - `target_screen_roi`: Adjusts the screen area mapped by touchpad coordinates to adapt to different display devices; +7. **Extended Functions**: The program reserves a keyboard key sending interface (`send_keys` function), which can extend more gesture commands (such as sliding, scrolling, shortcut keys, etc.) based on hand keypoint recognition; +8. **Image Quality Adjustment**: The `trans_img_quality` parameter can adjust the frame transmission quality. A lower value results in poorer image quality but faster transmission speed, which can be adjusted according to network/device performance. + +## 5. More Information +[Source Code](https://github.com/sipeed/MaixPy/tree/main/projects/app_usb_hand_touch) \ No newline at end of file diff --git a/projects/app_usb_hand_touch/app.yaml b/projects/app_usb_hand_touch/app.yaml index e5b1f4dc..6cd6e775 100644 --- a/projects/app_usb_hand_touch/app.yaml +++ b/projects/app_usb_hand_touch/app.yaml @@ -5,6 +5,6 @@ version: 1.0.0 author: Neucrack@Sipeed icon: icon.json desc: detect hand status and simulate touchpad input through USB -files: +inclede: - main.py - icon.json diff --git a/projects/app_vlm/README.md b/projects/app_vlm/README.md index 7e317a7f..1c059cd6 100644 --- a/projects/app_vlm/README.md +++ b/projects/app_vlm/README.md @@ -1,7 +1,54 @@ -AI VLM +## 1. 简介 +本工具是一款运行在MaixCam设备上的多模态应用,支持加载多款主流轻量化视觉语言模型(Qwen3-VL、InternVL、SmolVLM),能够通过设备摄像头采集图像,并利用选定的模型对图像进行智能描述,最终将结果展示在设备屏幕上,实现"看图说话"的核心能力,操作简洁,无需复杂的命令行配置,仅通过触摸屏即可完成全部交互。 -Offline AI Vision Language Model +## 2. 主要功能 +1. **模型选择**:支持在Qwen3-VL、InternVL、SmolVLM三款预训练视觉语言模型中自由选择,适配不同的识别需求和设备运行效率。 +2. **图像采集与解析**:通过设备内置摄像头实时采集图像,利用选定模型对图像进行快速分析和自然语言描述。 +3. **双语切换**:支持中文(ZH)和英文(EN)两种输出语言切换,满足不同使用者的语言习惯(SmolVLM模型仅支持英文)。 +4. **便捷退出**:提供屏幕快速退出入口,一键终止应用运行,恢复设备初始状态。 +5. **运行环境校验**:自动校验设备内存配置,确保应用稳定运行,同时提供模型缺失、环境异常等友好提示。 +## 3. 使用说明 +### 3.1 前期准备 +1. 确保MaixCam设备已完成基础系统部署,且设备存储空间充足(预留至少2GB空间用于存放模型文件)。 +2. 默认自带SmolVLM大模型,其余大模型需要自行下载并上传到设备的`/root/models/`目录下 +### 3.2 启动应用 +1. 将包含本工具代码的文件上传至MaixCam设备。 +2. 通过设备终端或配套工具运行该代码,启动应用。 +3. 启动后将依次显示"loading touchscreen"、"loading 模型名称"等加载提示,等待加载完成即可进入交互界面。 +![alt text](./assets/image.png) +### 3.3 核心操作 +1. **模型选择**: + - 加载完成后首先进入模型选择界面,屏幕中央显示三款可选模型,每个模型对应一个白色边框选框。 + - 通过触摸对应模型的选框,即可选定该模型,应用将自动进入该模型的加载流程。 +2. **图像描述查看**: + - 模型加载完成后,应用将自动启动摄像头采集图像,无需手动操作。 + - 模型将对采集到的图像进行解析,解析结果将显示在摄像头画面下方的黑色区域,自动换行展示。 + - 应用将循环采集图像并进行解析,实时更新展示结果。 +3. **语言切换**: + - 屏幕右上角显示当前语言(ZH/EN),若选定模型支持中文(Qwen3-VL、InternVL),触摸右上角语言区域即可切换中英文输出。 + - 若选定SmolVLM模型,右上角将固定显示EN,不支持中文切换。 +4. **退出应用**: + - 屏幕左上角显示退出图标,触摸该图标区域(或图标周边放大识别区域),即可快速退出应用。 + - 若遇到模型缺失等异常提示,触摸屏幕任意区域即可退出应用。 +## 4. 注意事项 +1. **设备硬件要求**:某些大模型仅支持4GB内存版本的MaixCam2设备,2GB内存版本设备无法运行,启动时将自动检测并提示退出。 +2. **模型相关**: + - 模型文件必须完整上传至指定目录,否则应用将提示模型缺失并终止运行。 + - 不同模型对设备性能要求不同,Qwen3-VL模型运行占用资源较高,解析速度相对较慢;SmolVLM模型占用资源较低,解析速度更快。 +3. **AI ISP设置**:应用启动时将自动关闭AI ISP功能,退出时将自动恢复原有设置,无需手动调整,避免影响其他应用运行。 +4. **操作环境**: + - 建议在光线充足的环境下使用,确保摄像头能够清晰采集图像,提升模型解析准确率。 + - 避免在设备运行过程中插拔外设,防止应用异常崩溃。 +5. **异常处理**: + - 若应用出现卡顿,可等待片刻,模型解析完成后将自动恢复正常;若卡顿持续,可强制退出后重新启动。 + - 若屏幕无显示内容,需检查显示屏连接是否正常,以及代码是否正确运行。 +6. **存储空间**:多款模型同时存放将占用大量存储空间,建议按需下载和保留模型文件,无需使用的模型可及时删除释放空间。 + +## 5. 更多介绍 +[源码](https://github.com/sipeed/MaixPy/tree/main/projects/app_vlm) + +[MaixPy MaixCAM 运行 VLM InternVL 视觉语言模型](https://wiki.sipeed.com/maixpy/doc/zh/mllm/vlm_internvl.html) diff --git a/projects/app_vlm/README_EN.md b/projects/app_vlm/README_EN.md new file mode 100644 index 00000000..a25a6e66 --- /dev/null +++ b/projects/app_vlm/README_EN.md @@ -0,0 +1,54 @@ +## 1. Introduction +This tool is a multimodal application running on the **MaixCam** device. It supports loading several mainstream lightweight Vision Language Models (VLMs) such as **Qwen3-VL**, **InternVL**, and **SmolVLM**. The application captures images via the device camera, intelligently describes them using the selected model, and displays the results on the screen, realizing the core capability of "describing what it sees." The operation is concise and does not require complex command-line configurations; all interactions can be completed via the touchscreen. + +## 2. Main Features +1. **Model Selection**: Freely choose between three pre-trained VLMs (Qwen3-VL, InternVL, SmolVLM) to adapt to different recognition needs and device performance levels. +2. **Image Capture & Analysis**: Real-time image capture through the built-in camera, with rapid analysis and natural language description performed by the selected model. +3. **Bilingual Support**: Supports switching between Chinese (ZH) and English (EN) output languages to accommodate different user habits (Note: The SmolVLM model only supports English). +4. **Convenient Exit**: Provides a quick exit entry on the screen to terminate the application with one click and restore the device to its initial state. +5. **Runtime Environment Check**: Automatically verifies device memory configuration to ensure stable operation, and provides user-friendly prompts for missing models or environment exceptions. + +## 3. User Guide +### 3.1 Prerequisites +1. Ensure the MaixCam device has completed basic system deployment and has sufficient storage space (at least **2GB of free space** is recommended for storing model files). +2. The **SmolVLM** model is included by default; other models need to be downloaded and uploaded manually to the `/root/models/` directory on the device. + +### 3.2 Starting the Application +1. Upload the files containing the tool's code to the MaixCam device. +2. Run the code via the device terminal or supporting tools to start the application. +3. Upon startup, loading prompts such as "loading touchscreen" and "loading [model name]" will appear sequentially. Wait for the loading to complete to enter the interactive interface. +![alt text](./assets/image.png) + +### 3.3 Core Operations +1. **Model Selection**: + * After loading, you will first enter the model selection interface. The center of the screen displays the three available models, each within a white bordered selection box. + * Tap the selection box corresponding to a model to select it; the application will automatically proceed to load that specific model. +2. **Viewing Image Descriptions**: + * Once the model is loaded, the application automatically starts the camera to capture images—no manual operation is required. + * The model analyzes the captured images, and the results are displayed in the black area below the camera feed, with automatic line wrapping. + * The application continuously captures and analyzes images, updating the displayed results in real-time. +3. **Language Switching**: + * The current language (ZH/EN) is displayed in the top-right corner. If the selected model supports Chinese (Qwen3-VL, InternVL), tap the language area to switch between Chinese and English output. + * If the SmolVLM model is selected, the top-right corner will be fixed on "EN," and Chinese switching is not supported. +4. **Exiting the Application**: + * An exit icon is displayed in the top-left corner. Tapping this icon area (or the expanded recognition area around it) allows for a quick exit. + * If an error prompt appears (e.g., missing model), tapping anywhere on the screen will exit the application. + +## 4. Notes +1. **Hardware Requirements**: Certain large models only support the **4GB RAM version** of the MaixCam2 device. The 2GB RAM version cannot run these models and will automatically detect this and prompt an exit upon startup. +2. **Model Related**: + * Model files must be completely uploaded to the specified directory; otherwise, the application will prompt that the model is missing and terminate. + * Different models have different performance requirements. The **Qwen3-VL** model occupies more resources and has a relatively slower analysis speed, while the **SmolVLM** model is lighter and faster. +3. **AI ISP Settings**: The application automatically disables the AI ISP feature on startup and restores the original settings upon exit. No manual adjustment is needed, preventing interference with other applications. +4. **Operating Environment**: + * It is recommended to use the device in a well-lit environment to ensure clear image capture and improve analysis accuracy. + * Avoid plugging or unplugging peripherals while the device is running to prevent application crashes. +5. **Troubleshooting**: + * If the application freezes, wait a moment; it should automatically recover after the model analysis is complete. If the freeze persists, force quit and restart. + * If nothing is displayed on the screen, check if the display is connected properly and if the code is running correctly. +6. **Storage Space**: Storing multiple models simultaneously consumes significant storage space. It is recommended to download and keep only the models you need; delete unused models to free up space. + +## 5. More Information +[Source Code](https://github.com/sipeed/MaixPy/tree/main/projects/app_vlm) + +[MaixPy MaixCAM Running VLM InternVL Vision Language Model](https://wiki.sipeed.com/maixpy/doc/en/mllm/vlm_internvl.html) \ No newline at end of file diff --git a/projects/app_vlm/app.yaml b/projects/app_vlm/app.yaml index 6c1c9291..13c48b9d 100644 --- a/projects/app_vlm/app.yaml +++ b/projects/app_vlm/app.yaml @@ -10,7 +10,7 @@ exclude: - dist - build - .gitignore -files: +include: - assets - app.yaml - main.py diff --git a/projects/app_vlm/assets/image.png b/projects/app_vlm/assets/image.png new file mode 100644 index 00000000..2da68f6a Binary files /dev/null and b/projects/app_vlm/assets/image.png differ diff --git a/projects/app_webrtc_stream/README.md b/projects/app_webrtc_stream/README.md new file mode 100644 index 00000000..08fa18e7 --- /dev/null +++ b/projects/app_webrtc_stream/README.md @@ -0,0 +1,42 @@ +## 1. 简介 +本工具是一款运行在MaixCam设备上的可视化流媒体配置与推流应用,提供了简洁的触控操作界面,无需命令行输入即可完成WebRTC流媒体参数配置、推流启动,同时集成了Tailscale网络工具的快速管理功能,方便用户实现跨网络的流媒体访问,操作门槛低。 + +## 2. 主要功能 +1. **流媒体参数可视化配置**:支持对推流核心参数进行触控切换选择,无需手动修改配置文件。 +2. **Tailscale网络管理**:可快速启动、停止Tailscale服务,查看设备在线状态与IP地址,完成登录授权操作,实现跨网络设备互通。 +3. **WebRTC流媒体推流**:根据配置的参数启动推流服务,生成可访问的播放URL,支持实时预览推流画面。 +4. **推流URL查看**:推流过程中可切换显示/隐藏播放URL,方便用户获取并在其他设备上访问流媒体。 + +## 3. 使用说明 +![alt text](./assets/image.png) +### 3.1 配置页面与功能操作 + +设备启动本工具后,直接进入**Stream Settings**(流媒体配置)主页面。该页面包含四大核心配置项(编码器、码率、码控模式、分辨率)以及底部的功能操作区。 + +**1. 参数配置(点击对应区域循环切换)** +* **编码器 (Encoder)**:点击区域可在 **H.264** 和 **H.265** 两种编码格式间循环切换。 +* **码率 (Bitrate)**:点击区域可在 **1 Mbps ~ 64 Mbps** 之间循环切换。数值越大画质越清晰,但对网络带宽要求越高。 +* **码控模式 (RC Type)**:点击区域可在 **CBR(恒定码率)** 和 **VBR(可变码率)** 之间切换。 +* **分辨率 (Resolution)**:点击区域可根据设备摄像头传感器能力,在 **720 P ~ 4 K** 之间循环切换。 + +**2. 底部功能按钮操作** +* **Start Streaming(启动推流)**:点击蓝色按钮,工具将根据当前选中的参数启动WebRTC推流服务,并自动进入推流预览页面。 +* **Tailscale(网络管理,可选)**:若设备已安装Tailscale,将显示绿色按钮。点击进入管理页面,可查看设备状态(ONLINE/OFFLINE)与IP地址,进行 **Start**(启动服务/登录)、**Stop**(停止服务)、**Logout**(退出登录)操作,点击左上角图标可返回配置页面。 +* **Exit(退出工具)**:点击红色按钮,直接退出本工具,返回设备桌面。 +![alt text](./assets/image2.png) +**3. 推流预览与操作** +1. 进入推流页面后,将实时显示摄像头采集的画面,即当前推流的内容。 +2. 左上角退出图标:点击该图标,停止当前推流服务,返回流媒体配置主页面。 +3. 眼睛图标(URL显示/隐藏):点击该图标,切换显示/隐藏推流播放URL,显示状态下将在画面右侧展示可访问的WebRTC播放地址,可复制该地址在其他设备的浏览器或兼容WebRTC的播放器中打开,观看流媒体内容。 +4. 推流过程中保持设备网络通畅,如需停止推流,点击左上角退出图标即可。 + +## 4. 注意事项 +1. 网络要求:推流过程需要稳定的网络环境,选择高码率、高分辨率推流时,需确保设备网络上行带宽满足要求,否则可能出现卡顿、花屏现象。 + +3. 硬件适配:4K分辨率仅对搭载4K摄像头传感器的MaixCam生效。 + + +## 5. 更多介绍 +[MaixCAM MaixPy 视频流 JPEG 推流 / 发送图片到服务器](https://wiki.sipeed.com/maixpy/doc/zh/video/jpeg_streaming.html) + +[源码](https://github.com/sipeed/MaixPy/tree/main/projects/app_webrtc_stream) \ No newline at end of file diff --git a/projects/app_webrtc_stream/README_EN.md b/projects/app_webrtc_stream/README_EN.md new file mode 100644 index 00000000..4230b540 --- /dev/null +++ b/projects/app_webrtc_stream/README_EN.md @@ -0,0 +1,41 @@ +# Streaming Configuration and Broadcasting Tool User Manual + +## 1. Introduction +This tool is a visual streaming configuration and broadcasting application running on the **MaixCam** device. It provides a simple touch-based interface that allows you to configure WebRTC streaming parameters and start broadcasting without requiring command-line input. Additionally, it integrates the **Tailscale** networking tool for quick management, enabling users to access the video stream across different networks with minimal setup. + +## 2. Key Features +1. **Visual Streaming Configuration**: Intuitively switch and select core streaming parameters via touch controls without manually editing configuration files. +2. **Tailscale Network Management**: Quickly start or stop the Tailscale service, check device status (Online/Offline) and IP address, and complete login authorization to achieve cross-network device communication. +3. **WebRTC Streaming**: Start the streaming service based on configured parameters, generate accessible playback URLs, and support real-time preview of the stream. +4. **Stream URL Display**: Toggle the visibility of the playback URL during streaming to easily retrieve it and access the stream from other devices. + +## 3. User Guide + +### 3.1 Configuration Page & Operations +After launching the tool, you will enter the **Stream Settings** main page. This page contains four core configuration items (Encoder, Bitrate, Rate Control, Resolution) and a functional operation area at the bottom. + +**1. Parameter Configuration (Tap the corresponding area to cycle options)** +* **Encoder**: Tap to cycle between **H.264** and **H.265** encoding formats. +* **Bitrate**: Tap to cycle between options from **1 Mbps to 64 Mbps**. Higher values provide clearer image quality but require higher network bandwidth. +* **RC Type (Rate Control)**: Tap to switch between **CBR (Constant Bitrate)** and **VBR (Variable Bitrate)**. +* **Resolution**: Tap to cycle between resolutions from **720 P to 4 K**, depending on the capabilities of the device's camera sensor. + +**2. Bottom Function Buttons** +* **Start Streaming**: Tap the blue button to start the WebRTC streaming service with the currently selected parameters and automatically enter the streaming preview page. +* **Tailscale (Network Management, Optional)**: If Tailscale is installed on the device, a green button will appear. Tap to enter the management page where you can view the device status (ONLINE/OFFLINE) and IP address. You can perform **Start** (launch service/login), **Stop** (stop service), and **Logout** operations. Tap the icon in the top-left corner to return to the configuration page. +* **Exit**: Tap the red button to exit the tool and return to the device desktop. + +### 3.2 Streaming Preview & Operations +1. Once entering the streaming page, the screen will display the real-time camera feed, which is the content currently being streamed. +2. **Exit Icon (Top-left)**: Tap this icon to stop the current stream and return to the Stream Settings main page. +3. **Eye Icon (Show/Hide URL)**: Tap this icon to toggle the visibility of the playback URL. When visible, the accessible WebRTC playback address will be displayed on the right side of the screen. You can copy this address to open in a browser or a WebRTC-compatible player on another device to watch the stream. +4. Ensure the device has a stable network connection during streaming. To stop the stream, simply tap the Exit icon in the top-left corner. + +## 4. Notes +1. **Network Requirements**: Streaming requires a stable network environment. When selecting high bitrates or high resolutions, ensure the device's upload bandwidth meets the requirements to avoid buffering or visual artifacts. +2. **Hardware Compatibility**: 4K resolution is only supported on MaixCam devices equipped with a 4K camera sensor. + +## 5. More Information +[MaixCAM MaixPy Video Stream JPEG Streaming / Sending Images to Server](https://wiki.sipeed.com/maixpy/doc/en/video/jpeg_streaming.html) + +[Source Code](https://github.com/sipeed/MaixPy/tree/main/projects/app_webrtc_stream) \ No newline at end of file diff --git a/projects/app_webrtc_stream/app.yaml b/projects/app_webrtc_stream/app.yaml index ce8539a8..54f451db 100755 --- a/projects/app_webrtc_stream/app.yaml +++ b/projects/app_webrtc_stream/app.yaml @@ -6,7 +6,7 @@ author: Sipeed Ltd icon: assets/app.json desc: WebRTC Stream desc[zh]: WebRTC 推流 -files: +include: - assets - app.yaml - main.py diff --git a/projects/app_webrtc_stream/assets/image.png b/projects/app_webrtc_stream/assets/image.png new file mode 100644 index 00000000..f2fa5422 Binary files /dev/null and b/projects/app_webrtc_stream/assets/image.png differ diff --git a/projects/app_webrtc_stream/assets/image2.png b/projects/app_webrtc_stream/assets/image2.png new file mode 100644 index 00000000..cf76759e Binary files /dev/null and b/projects/app_webrtc_stream/assets/image2.png differ diff --git a/projects/app_yolo_obb/README.md b/projects/app_yolo_obb/README.md new file mode 100644 index 00000000..61a5e048 --- /dev/null +++ b/projects/app_yolo_obb/README.md @@ -0,0 +1,25 @@ +## 1. 简介 +本工具采用YOLO11 OBB(定向边界框)算法模型,能够对拍摄场景中的目标进行精准识别、置信度评估及角度检测,同时提供简洁的操作交互界面,无需复杂配置即可快速启动使用。 + +## 2. 主要功能 +![alt text](./assets/images.png) +1. 实时图像采集:通过设备摄像头自动获取实时场景画面,无需手动触发拍摄。 +2. 定向目标检测:采用YOLO11n_obb模型,可检测目标并输出带角度的定向边界框,相比普通矩形框更贴合不规则或旋转目标。 +3. 检测信息可视化:实时显示目标类别、置信度(保留2位小数)及目标旋转角度(保留1位小数),并以红色线条绘制目标定向边界框。 +4. 一键退出功能:提供可视化返回按钮,支持触摸操作快速退出应用,提升使用便捷性。 +5. 异常处理机制:应用运行过程中若出现异常,会自动捕获并显示详细错误信息,便于问题排查。 + +## 3. 使用说明 +1. 启动应用:直接运行本应用程序,无需额外输入参数,应用会自动初始化摄像头、显示屏、YOLO11检测器及触摸屏。 +2. 查看检测结果:应用启动后,摄像头会自动采集画面,显示屏实时展示原始画面及目标检测结果,包括定向边界框和对应的类别、置信度、角度信息。 +3. 退出应用:在显示屏左上角找到返回图标,点击该图标区域,即可快速退出应用,结束运行。 + +## 4. 注意事项 + + 检测参数说明:应用默认置信度阈值为0.5、IOU阈值为0.45、关键点阈值为0.5,低于该阈值的目标将不会被检测和显示,若需调整需修改应用内部参数。 + + +## 5. 更多介绍 +[源码](https://github.com/sipeed/MaixPy/tree/main/projects/app_yolo_obb) + +[带旋转角度的检测(OBB)](https://wiki.sipeed.com/maixpy/doc/zh/vision/detect_obb.html) \ No newline at end of file diff --git a/projects/app_yolo_obb/README_EN.md b/projects/app_yolo_obb/README_EN.md new file mode 100644 index 00000000..5319efb7 --- /dev/null +++ b/projects/app_yolo_obb/README_EN.md @@ -0,0 +1,23 @@ +## 1. Introduction +This tool utilizes the YOLO11 OBB (Oriented Bounding Box) algorithm model to perform precise recognition, confidence assessment, and angle detection of objects within the camera's view. It features a simple operational interface and can be launched quickly without complex configurations. + +## 2. Main Features +![alt text](./assets/images.png) +1. **Real-time Image Acquisition:** Automatically captures real-time scene frames through the device camera without manual triggering. +2. **Oriented Object Detection:** Uses the YOLO11n_obb model to detect objects and output oriented bounding boxes with angles. Compared to standard rectangular boxes, these fit irregular or rotated objects much more accurately. +3. **Detection Information Visualization:** Real-time display of object class, confidence score (2 decimal places), and object rotation angle (1 decimal place). The oriented bounding box is drawn with red lines. +4. **One-Click Exit:** Provides a visual back button that supports touch operation to quickly exit the application, improving user convenience. +5. **Exception Handling:** If an exception occurs during operation, it is automatically caught and detailed error information is displayed to facilitate troubleshooting. + +## 3. User Guide +1. **Launch Application:** Run the application directly without additional parameters. The app will automatically initialize the camera, display, YOLO11 detector, and touchscreen. +2. **View Detection Results:** After launching, the camera automatically captures frames. The display shows the original feed along with detection results, including the oriented bounding box, class, confidence, and angle information. +3. **Exit Application:** Locate the back icon in the top-left corner of the display and tap the icon area to quickly exit and terminate the application. + +## 4. Notes +**Detection Parameters:** The application defaults to a confidence threshold of 0.5, an IOU threshold of 0.45, and a keypoint threshold of 0.5. Objects falling below these thresholds will not be detected or displayed. Modifying the internal application parameters is required to adjust these values. + +## 5. More Information +[Source Code](https://github.com/sipeed/MaixPy/tree/main/projects/app_yolo_obb) + +[Oriented Detection (OBB)](https://wiki.sipeed.com/maixpy/doc/en/vision/detect_obb.html) \ No newline at end of file diff --git a/projects/app_yolo_obb/app.yaml b/projects/app_yolo_obb/app.yaml index 48fb1155..27cfe45f 100644 --- a/projects/app_yolo_obb/app.yaml +++ b/projects/app_yolo_obb/app.yaml @@ -5,7 +5,7 @@ version: 1.0.2 author: Sipeed Ltd icon: icon.png desc: YOLO11 detect objects with orientation. -files: +include: - icon.png - app.yaml - main.py diff --git a/projects/app_yolo_obb/assets/images.png b/projects/app_yolo_obb/assets/images.png new file mode 100644 index 00000000..5dc0f916 Binary files /dev/null and b/projects/app_yolo_obb/assets/images.png differ diff --git a/projects/app_yolov8_seg/README.md b/projects/app_yolov8_seg/README.md new file mode 100644 index 00000000..4ed1c340 --- /dev/null +++ b/projects/app_yolov8_seg/README.md @@ -0,0 +1,33 @@ +## 1. 简介 +本工具是一款基于YOLO11n_seg模型实现高效的视觉检测,能够在设备端直接完成目标的识别、评分以及分割掩码绘制,无需依赖外部服务器,具备操作简洁、运行流畅、实时性强的特点,适用于各类现场视觉检测、简易物体识别场景。 + +## 2. 主要功能 +1. 实时图像采集:通过设备摄像头自动采集实时画面,无需手动触发拍摄。 +2. 目标识别与评分:精准识别画面中的目标物体,标注物体类别,并输出0-1区间的识别置信度(保留2位小数)。 +3. 图像分割掩码绘制:对识别到的目标进行分割处理,绘制专属分割掩码,清晰区分目标与背景。 +4. 目标框标注:用红色矩形框标注识别到的目标位置,直观易懂。 +5. 快捷退出功能:提供专用返回按钮,支持触摸触发快速退出应用,操作便捷。 + +## 3. 使用说明 +![alt text](./assets/image.png) +1. 启动应用:将设备部署完成后,运行本应用,应用将自动初始化摄像头、显示模块、YOLO11识别模型及触摸屏。 +2. 实时查看结果:应用启动后,摄像头将自动采集画面,设备显示屏将实时展示: + - 原始采集画面 + - 红色矩形框标注的识别目标 + - 目标上方的“类别:置信度”文字说明 + - 目标区域对应的分割掩码 + - 屏幕左上角的返回按钮图标 +3. 退出应用:直接用手指触摸设备显示屏左上角的返回按钮图标,应用将接收到退出指令并自动关闭,返回至设备主界面或初始状态。 +4. 异常查看:若应用运行过程中出现故障,将自动切换至黑色背景界面,显示白色异常详情日志,便于了解故障原因。 + +## 4. 注意事项 +1. 拍摄环境要求: + - 尽量在光线充足、环境明亮的场景下使用,避免逆光、暗光环境,否则会降低目标识别准确率及分割效果。 + - 保持摄像头与目标物体的适当距离,避免目标过近或过远导致无法识别。 + - 减少画面中无关干扰物体,避免多个目标重叠,提升识别效率。 + + +## 5. 更多介绍 +[源码](https://github.com/sipeed/MaixPy/tree/main/projects/app_yolo_obb) + +[MaixCAM MaixPy 图像语义分割](https://wiki.sipeed.com/maixpy/doc/zh/vision/segmentation.html) \ No newline at end of file diff --git a/projects/app_yolov8_seg/README_EN.md b/projects/app_yolov8_seg/README_EN.md new file mode 100644 index 00000000..f6026a2b --- /dev/null +++ b/projects/app_yolov8_seg/README_EN.md @@ -0,0 +1,32 @@ +## 1. Introduction +This tool is an efficient visual detection application based on the YOLO11n_seg model. It is capable of performing object recognition, scoring, and segmentation mask rendering directly on the device without relying on external servers. Featuring a simple interface, smooth operation, and strong real-time performance, it is suitable for various on-site visual inspection and simple object recognition scenarios. + +## 2. Main Features +1. **Real-time Image Acquisition:** Automatically captures real-time frames via the device camera without manual triggering. +2. **Object Recognition & Scoring:** Precisely identifies target objects in the frame, labels their categories, and outputs a confidence score between 0 and 1 (rounded to 2 decimal places). +3. **Segmentation Mask Rendering:** Processes recognized targets with segmentation and renders dedicated masks to clearly distinguish the target from the background. +4. **Bounding Box Annotation:** Marks the position of recognized targets with red rectangular boxes for intuitive understanding. +5. **Quick Exit:** Provides a dedicated back button that supports touch input to quickly exit the application. + +## 3. User Guide +![alt text](./assets/image.png) +1. **Launch the Application:** After deploying the device, run the application. It will automatically initialize the camera, display module, YOLO11 recognition model, and touchscreen. +2. **View Results in Real-time:** Once launched, the camera captures frames automatically, and the device screen displays: + * The original captured frame. + * Recognized targets annotated with red rectangular boxes. + * Text labels above targets in the format "Class: Confidence". + * Segmentation masks corresponding to the target areas. + * A back button icon in the top-left corner of the screen. +3. **Exit the Application:** Directly touch the back button icon in the top-left corner of the screen. The application will receive the exit command and close automatically, returning to the device's main menu or initial state. +4. **View Errors:** If a fault occurs during operation, the screen will automatically switch to a black background displaying white error logs for troubleshooting. + +## 4. Notes +1. **Shooting Environment Requirements:** + * Use in well-lit environments with sufficient light. Avoid backlighting or dark conditions, as this will reduce recognition accuracy and segmentation quality. + * Maintain an appropriate distance between the camera and the target object; avoid being too close or too far to prevent recognition failure. + * Minimize irrelevant background objects and avoid overlapping targets to improve recognition efficiency. + +## 5. More Information +[Source Code](https://github.com/sipeed/MaixPy/tree/main/projects/app_yolo_obb) + +[MaixCAM MaixPy Image Segmentation](https://wiki.sipeed.com/maixpy/doc/en/vision/segmentation.html) \ No newline at end of file diff --git a/projects/app_yolov8_seg/app.yaml b/projects/app_yolov8_seg/app.yaml index a199a5e4..0d65702a 100644 --- a/projects/app_yolov8_seg/app.yaml +++ b/projects/app_yolov8_seg/app.yaml @@ -5,7 +5,7 @@ version: 1.0.2 author: Sipeed Ltd icon: icon.png desc: YOLO11 detect objects and segment object -files: +include: - icon.png - app.yaml - main.py diff --git a/projects/app_yolov8_seg/assets/image.png b/projects/app_yolov8_seg/assets/image.png new file mode 100644 index 00000000..117f8494 Binary files /dev/null and b/projects/app_yolov8_seg/assets/image.png differ diff --git a/projects/app_yoloworld/README.md b/projects/app_yoloworld/README.md index d0b8d067..2f1eeb40 100644 --- a/projects/app_yoloworld/README.md +++ b/projects/app_yoloworld/README.md @@ -1,9 +1,30 @@ -MaixPy benchmark APP -===== +## 1. 简介 +本应用是一款运行在MaixCam2设备上的YOLO-World目标检测演示程序,支持通过语音录入目标类别并自动完成模型特征生成,实现对指定目标的实时视觉检测,无需手动修改配置或编写额外代码,操作简洁高效,适用于快速实现自定义目标的现场检测需求。 +## 2. 主要功能 +1. **实时目标检测**:加载预配置模型后,可对指定目标(默认为人脸)进行实时画面检测,标注目标位置并显示检测置信度。 +2. **语音录入自定义类别**:支持通过3秒语音录入自定义检测目标类别(英文),自动完成语音转文字识别。 +3. **自动生成模型特征**:确认自定义类别后,应用将自动运行脚本生成对应类别的标签文件和文本特征文件,无需手动处理模型相关配置。 +4. **一键切换检测模型**:特征文件生成完成后,将自动加载对应规模的YOLO-World模型,切换为新目标的实时检测模式。 +5. **简易交互操作**:提供返回退出、类别确认/取消、学习新目标等可视化按钮,支持触屏点击操作,界面友好。 -MaixPy firmware must > 4.11.10 - +# 3. 使用说明 +![alt text](./assets/image.png) +1. **启动与基础检测**:确保设备摄像头、麦克风正常且已预装相关模型文件,直接运行应用即可;程序会自动完成初始化(包括加载模型和语音识别工具),随后进入实时预览界面,摄像头将自动对默认目标进行检测,并用红色矩形框标注目标位置及置信度。 +2. **自定义目标学习**:在预览界面点击下方的「Learn」按钮,界面提示后在3秒内清晰说出目标的**英文名称**(如「car」);录音结束后,应用会自动转写语音并显示识别结果,点击「Yes」确认即可自动生成特征文件并切换为该目标的实时检测模式,点击「No」则放弃。 +3. **退出应用**:在非学习/转写的任意界面,点击屏幕左上角的返回图标,即可正常关闭应用并恢复设备配置。 +## 4. 注意事项 +1. **设备硬件要求**:本应用要求设备为MaixCam的4GB内存版本,低于4GB内存的设备无法正常运行,启动时将提示硬件不满足并退出。 +2. **语音录入限制**:仅支持英文目标类别录入,不支持中文及其他语言,且录音时需保持环境安静,避免杂音影响转写准确率,同时需在3秒内完成语音录入。 +3. **操作交互说明**:本应用仅支持触屏点击操作,所有按钮需点击确认生效,长按无额外功能;学习过程中(生成特征文件)请勿关闭应用或重启设备,避免文件损坏。 +4. **资源占用说明**:应用运行过程中占用摄像头、麦克风、NPU等硬件资源,运行期间建议关闭其他无关应用,避免影响检测效果和运行流畅度。 +5. **异常处理**:若运行过程中出现报错,界面将显示错误信息,点击屏幕任意位置即可退出应用,可查看设备日志排查具体问题。 +6. **文件存储路径**:自定义目标的标签文件(`.txt`)和特征文件(`.bin`)将自动保存至`/root/models/my_yolo_world/`目录下,可通过设备文件管理工具查看和备份。 +7. **检测参数说明**:应用默认检测置信度阈值为0.5,IOU阈值为0.45,若需调整检测灵敏度,可修改代码中对应参数(`detector.detect(img, conf_th=0.5, iou_th=0.45)`)后重新运行。 +8. **扩展功能**:若需支持多类别同时检测,可修改语音录入和特征生成相关逻辑,生成包含多个类别的标签文件和特征文件,同时加载对应多类别YOLO-World模型。 +## 5. 更多介绍 +[源码](https://github.com/sipeed/MaixPy/tree/main/projects/app_yoloworld) +[MaixPy MaixCAM2 使用 YOLO World 模型实现无需训练检测任意目标](https://wiki.sipeed.com/maixpy/doc/zh/vision/yolo_world.html) \ No newline at end of file diff --git a/projects/app_yoloworld/README_EN.md b/projects/app_yoloworld/README_EN.md new file mode 100644 index 00000000..c7543a82 --- /dev/null +++ b/projects/app_yoloworld/README_EN.md @@ -0,0 +1,32 @@ +# YOLO-World Detection Application User Guide + +## 1. Introduction +This application is a YOLO-World object detection demo running on the **MaixCam2** device. It supports specifying detection targets via voice input and automatically generates the necessary model features, enabling real-time visual detection of the specified object. No manual configuration changes or additional coding are required. The operation is concise and efficient, making it suitable for quickly implementing on-site detection of custom objects. + +## 2. Main Features +1. **Real-time Object Detection**: After loading the pre-configured model, it performs real-time detection on the specified target (default is "person"), drawing bounding boxes and displaying detection confidence scores. +2. **Voice Input for Custom Classes**: Supports recording custom detection classes via a 3-second voice input (English only), with automatic speech-to-text recognition. +3. **Automatic Model Feature Generation**: After confirming the custom class, the application automatically runs a script to generate the corresponding label file and text feature file, eliminating the need for manual model configuration. +4. **One-click Model Switching**: Once the feature file is generated, the application automatically loads the corresponding scale YOLO-World model and switches to the real-time detection mode for the new target. +5. **Simple Interactive Operation**: Provides visual buttons for back/exit, class confirmation/cancellation, and learning new objects, supporting touchscreen interaction for a user-friendly experience. + +## 3. Usage Instructions +![alt text](./assets/image.png) +1. **Startup and Basic Detection**: Ensure the device camera and microphone are functional and that relevant model files are pre-installed, then run the application directly. The program automatically completes initialization (including loading the model and speech recognition tools) and enters the real-time preview interface. The camera will automatically detect the default target, marking it with a red bounding box and displaying the confidence score. +2. **Custom Target Learning**: Click the "Learn" button at the bottom of the preview interface. When prompted, clearly speak the **English name** of the target (e.g., "car") within 3 seconds. After recording, the application transcribes the speech and displays the result. Click "Yes" to confirm, and the app will automatically generate the feature file and switch to real-time detection mode for that target. Click "No" to discard. +3. **Exit Application**: In any interface except during learning/transcription, click the back icon in the top-left corner of the screen to close the application normally and restore device configurations. + +## 4. Notes +1. **Hardware Requirements**: This application requires the **4GB RAM version** of the MaixCam. Devices with less than 4GB RAM cannot run it properly and will exit with a hardware incompatibility message. +2. **Voice Input Limitations**: Only English target names are supported (Chinese and other languages are not). Record in a quiet environment to avoid noise affecting transcription accuracy, and ensure the name is spoken within 3 seconds. +3. **Interaction Instructions**: This application only supports single touchscreen clicks; long presses have no additional function. Do not close the application or restart the device during the learning process (feature file generation) to avoid file corruption. +4. **Resource Usage**: The application utilizes hardware resources such as the camera, microphone, and NPU. It is recommended to close other unrelated applications during runtime to avoid affecting detection performance and smoothness. +5. **Error Handling**: If an error occurs, an error message will be displayed. Tapping anywhere on the screen will exit the application. You can check the device logs to troubleshoot specific issues. +6. **File Storage Path**: Custom target label files (`.txt`) and feature files (`.bin`) are automatically saved to the `/root/models/my_yolo_world/` directory. These can be viewed and backed up using the device's file management tools. +7. **Detection Parameters**: The default confidence threshold (`conf_th`) is 0.5, and the IOU threshold (`iou_th`) is 0.45. To adjust detection sensitivity, modify the corresponding parameters in the code (`detector.detect(img, conf_th=0.5, iou_th=0.45)`) and rerun the application. +8. **Extended Functionality**: To support detection of multiple classes simultaneously, modify the logic related to voice input and feature generation to create a label file containing multiple classes and load the corresponding multi-class YOLO-World model. + +## 5. More Information +[Source Code](https://github.com/sipeed/MaixPy/tree/main/projects/app_yoloworld) + +[Using YOLO World Model on MaixPy MaixCAM2 for Detection of Any Target Without Training](https://wiki.sipeed.com/maixpy/doc/en/vision/yolo_world.html) \ No newline at end of file diff --git a/projects/app_yoloworld/assets/image.png b/projects/app_yoloworld/assets/image.png new file mode 100644 index 00000000..6c6b521b Binary files /dev/null and b/projects/app_yoloworld/assets/image.png differ diff --git a/tools/maix_module2/.gitignore b/tools/maix_module2/.gitignore new file mode 100644 index 00000000..c6ae5060 --- /dev/null +++ b/tools/maix_module2/.gitignore @@ -0,0 +1,180 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# 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 test / 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/ + +# 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 + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + + +build/ +dist/ +dist_old/ +dl_lib/ +*.pyi +stub/ +projects/MaixPy/docs/api/ +MaixCDK/ +docs/api/* +!docs/api/config.json +docs/out/ +CMakeLists.txt +!*/**/CMakeLists.txt +tools/tmp/ +tools/os/*/tmp/ +tools/os/*/img_root/ +tools/os/*/img_boot/ +projects/apps/ diff --git a/tools/maix_module2/LICENSE b/tools/maix_module2/LICENSE new file mode 100644 index 00000000..3ade4f83 --- /dev/null +++ b/tools/maix_module2/LICENSE @@ -0,0 +1,95 @@ + +MIT License + +Copyright (c) 2023- Sipeed Ltd. + +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. + +------------------------- MaixCDK end ------------------------- + + +------------------------- MaixPy ------------------------- +Apache 2.0 License + +Copyright (c) 2023- Sipeed Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +------------------------- MaixCDK end ------------------------- + + +------------------------- MaixCDK ------------------------- +Apache 2.0 License + +Copyright (c) 2023- Sipeed Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +------------------------- MaixCDK end ------------------------- + +----------------- c_cpp_project_framework ----------------- + +The project framework is based on c_cpp_project_framework(https://github.com/neutree/c_cpp_project_framework) + +MIT License + +Copyright (c) 2019- Neucrack(CZD666666@gmail.com) + +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. + +----------------- c_cpp_project_framework end ----------------- + + +And more submodules see the LICENSE file in each submodule. diff --git a/tools/maix_module2/README.md b/tools/maix_module2/README.md new file mode 100644 index 00000000..a21cb367 --- /dev/null +++ b/tools/maix_module2/README.md @@ -0,0 +1,5 @@ +MaixPy external module template based on MaixCDK +===== + +Usage: refer to [中文文档](https://wiki.sipeed.com/maixpy/doc/zh/source_code/add_c_module.html) or [English documentation](https://wiki.sipeed.com/maixpy/doc/en/source_code/add_c_module.html) + diff --git a/tools/maix_module2/README_CN.md b/tools/maix_module2/README_CN.md new file mode 100644 index 00000000..9bd9a7e1 --- /dev/null +++ b/tools/maix_module2/README_CN.md @@ -0,0 +1,16 @@ +# MaixPy Module: maix_kws + +## Quick Start +1. Compile module: `python setup.py bdist_wheel 'platform'` +2. Install module: `pip install dist/maix_kws*.whl` +3. Test import: `python test/test_import.py` + +## Development Guide +- Python module root directory: `maix_kws/` +- C++ source file: `components/maix/src/maix_kws.cpp` +- C++ header file: `components/maix/include/maix_kws.hpp` +- Version management file: `maix_kws/version.py` + +## TODO +- Add detailed API documentation +- Add unit tests for core functions diff --git a/tools/maix_module2/build_run.sh b/tools/maix_module2/build_run.sh new file mode 100644 index 00000000..9079d227 --- /dev/null +++ b/tools/maix_module2/build_run.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +maixpy_root="." +cd $maixpy_root python project.py build && python -u -c "exec('import sys;sys.path.insert(0, \'$maixpy_root\');'+open('$1', 'r').read())" + + diff --git a/tools/maix_module2/compile/compile_flags.cmake b/tools/maix_module2/compile/compile_flags.cmake new file mode 100644 index 00000000..56b66289 --- /dev/null +++ b/tools/maix_module2/compile/compile_flags.cmake @@ -0,0 +1,40 @@ +########## set C flags ######### +set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -Wall -fPIC -Wl,-rpath=$ORIGIN/dl_lib) +################################ + + +###### set CXX(cpp) flags ###### +set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -Wall -std=c++17 -fPIC -Wl,-rpath=$ORIGIN/dl_lib) +################################ + +# set(LINK_FLAGS -Wl,-EL) # (default little endian) +set(CMAKE_C_LINK_FLAGS ${CMAKE_C_LINK_FLAGS} + ${LINK_FLAGS} + -Wl,-rpath=$ORIGIN/dl_lib + ) +set(CMAKE_CXX_LINK_FLAGS ${CMAKE_CXX_LINK_FLAGS} ${CMAKE_C_LINK_FLAGS} + -std=c++17 + ) +# set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} +# ${LINK_FLAGS} +# ) +# set(CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} +# ${LINK_FLAGS} +# ) +# set(CMAKE_MODULE_LINKER_FLAGS ${CMAKE_MODULE_LINKER_FLAGS} +# ${LINK_FLAGS} +# ) + + +# Convert list to string +string(REPLACE ";" " " CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") +string(REPLACE ";" " " CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") +string(REPLACE ";" " " LINK_FLAGS "${LINK_FLAGS}") +string(REPLACE ";" " " CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS}") +string(REPLACE ";" " " CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS}") +# string(REPLACE ";" " " CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") +# string(REPLACE ";" " " CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") +# string(REPLACE ";" " " CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}") + + + diff --git a/tools/maix_module2/compile/copy_so.py b/tools/maix_module2/compile/copy_so.py new file mode 100644 index 00000000..a92bb774 --- /dev/null +++ b/tools/maix_module2/compile/copy_so.py @@ -0,0 +1,15 @@ +import os +import sys +import shutil + + +so_path = sys.argv[1] +project_path = sys.argv[2] + +with open(os.path.join(project_path, "module_name.txt"), "r") as f: + module_name = f.readline().strip() + +dst_dir = os.path.join(project_path, module_name) +dst_path = os.path.join(dst_dir, f"_{module_name}.so") +os.makedirs(dst_dir, exist_ok=True) +shutil.copyfile(so_path, dst_path) diff --git a/tools/maix_module2/compile/gen_binary.cmake b/tools/maix_module2/compile/gen_binary.cmake new file mode 100644 index 00000000..126f5274 --- /dev/null +++ b/tools/maix_module2/compile/gen_binary.cmake @@ -0,0 +1,48 @@ + +# set(CMAKE_C_LINK_EXECUTABLE " -o .elf ") +# set(CMAKE_CXX_LINK_EXECUTABLE " -o .elf ") + +# add_custom_command(TARGET ${PROJECT_ID} POST_BUILD +# COMMAND ${CMAKE_OBJCOPY} --output-format=binary ${CMAKE_BINARY_DIR}/${PROJECT_ID}.elf ${CMAKE_BINARY_DIR}/${PROJECT_ID}.bin +# DEPENDS ${PROJECT_ID} +# COMMENT "-- Generating binary file ...") + +# variable #{g_dynamic_libs} have dependency dynamic libs and compiled dynamic libs(register component and assigned DYNAMIC or SHARED flag) + +# remove endswith libpython*.so items and libmaix.so from g_dynamic_libs +set(final_dynamic_libs) +set(except_libs "libpython.*\\.so$" + "libmaix\\.so$" + ) +foreach(item ${g_dynamic_libs}) + set(is_except FALSE) + foreach(except ${except_libs}) + if(${item} MATCHES ${except}) + set(is_except TRUE) + break() + endif() + endforeach() + if(NOT is_except) + list(APPEND final_dynamic_libs ${item}) + endif() +endforeach() + +file(STRINGS "${PROJECT_PATH}/module_name.txt" ALL_LINES) +list(GET ALL_LINES 0 MODULE_NAME) + +if(final_dynamic_libs) + set(copy_dynamic_libs_cmd COMMAND mkdir -p ${PROJECT_BINARY_DIR}/dl_lib && cp ${final_dynamic_libs} ${PROJECT_BINARY_DIR}/dl_lib) + set(copy_dynamic_libs_cmd2 COMMAND mkdir -p ${PROJECT_PATH}/${MODULE_NAME}/dl_lib && cp -r ${PROJECT_BINARY_DIR}/dl_lib/* ${PROJECT_PATH}/${MODULE_NAME}/dl_lib) +else() + set(copy_dynamic_libs_cmd) + set(copy_dynamic_libs_cmd2) +endif() + +add_custom_command(TARGET ${PROJECT_ID} POST_BUILD + # COMMAND mkdir -p ${PROJECT_DIST_DIR} + ${copy_dynamic_libs_cmd} + ${copy_dynamic_libs_cmd2} + COMMAND python ${PROJECT_PATH}/compile/copy_so.py ${PROJECT_BINARY_DIR}/maix/libmaix.so ${PROJECT_PATH} + DEPENDS ${PROJECT_ID} + COMMENT "-- copy binary files to dist dir ...") + diff --git a/tools/maix_module2/compile/priority.conf b/tools/maix_module2/compile/priority.conf new file mode 100644 index 00000000..1de2ca16 --- /dev/null +++ b/tools/maix_module2/compile/priority.conf @@ -0,0 +1,8 @@ +# component register priority +# The upper components have higher priority +# comments start with `#` + +component1 +component2 + + diff --git a/tools/maix_module2/components/maix/CMakeLists.txt b/tools/maix_module2/components/maix/CMakeLists.txt new file mode 100644 index 00000000..e96ae485 --- /dev/null +++ b/tools/maix_module2/components/maix/CMakeLists.txt @@ -0,0 +1,93 @@ +############### Add include ################### +list(APPEND ADD_INCLUDE "include" + ) +list(APPEND ADD_PRIVATE_INCLUDE "") +############################################### + +############ Add source files ################# +# list(APPEND ADD_SRCS "src/main.c" +# "src/test.c" +# ) +# append_srcs_dir(ADD_SRCS "src") # append source file in src dir to var ADD_SRCS +# list(REMOVE_ITEM COMPONENT_SRCS "src/test2.c") +# FILE(GLOB_RECURSE EXTRA_SRC "src/*.c") +# FILE(GLOB EXTRA_SRC "src/*.c") +# list(APPEND ADD_SRCS ${EXTRA_SRC}) +# aux_source_directory(src ADD_SRCS) # collect all source file in src dir, will set var ADD_SRCS +append_srcs_dir(ADD_SRCS "src") # append source file in src dir to var ADD_SRCS +# list(REMOVE_ITEM COMPONENT_SRCS "src/test.c") +# set(ADD_ASM_SRCS "src/asm.S") +# list(APPEND ADD_SRCS ${ADD_ASM_SRCS}) +# SET_PROPERTY(SOURCE ${ADD_ASM_SRCS} PROPERTY LANGUAGE C) # set .S ASM file as C language +# SET_SOURCE_FILES_PROPERTIES(${ADD_ASM_SRCS} PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp -D BBBBB") +############################################### + +###### Add required/dependent components ###### +list(APPEND ADD_REQUIREMENTS pybind11 python3 basic nn peripheral vision comm network voice) +############################################### + +###### Add link search path for requirements/libs ###### +# list(APPEND ADD_LINK_SEARCH_PATH "${CONFIG_TOOLCHAIN_PATH}/lib") +# list(APPEND ADD_REQUIREMENTS pthread m) # add system libs, pthread and math lib for example here +# set (OpenCV_DIR opencv/lib/cmake/opencv4) +# find_package(OpenCV REQUIRED) +############################################### + +############ Add static libs ################## +# list(APPEND ADD_STATIC_LIB "lib/libtest.a") +############################################### + +#### Add compile option for this component #### +#### Just for this component, won't affect other +#### modules, including component that depend +#### on this component +# list(APPEND ADD_DEFINITIONS_PRIVATE -DAAAAA=1) + +#### Add compile option for this component +#### and components depend on this component +# list(APPEND ADD_DEFINITIONS -DAAAAA222=1 +# -DAAAAA333=1) +############################################### + +############ Add static libs ################## +#### Update parent's variables like CMAKE_C_LINK_FLAGS +# set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--start-group libmaix/libtest.a -ltest2 -Wl,--end-group" PARENT_SCOPE) +############################################### + +######### Add files need to download ######### +# list(APPEND ADD_FILE_DOWNLOADS "{ +# 'url': 'https://*****/abcde.tar.xz', +# 'urls': [], # backup urls, if url failed, will try urls +# 'sites': [], # download site, user can manually download file and put it into dl_path +# 'sha256sum': '', +# 'filename': 'abcde.tar.xz', +# 'path': 'toolchains/xxxxx', +# 'check_files': [] +# }" +# ) +# +# then extracted file in ${DL_EXTRACTED_PATH}/toolchains/xxxxx, +# you can directly use then, for example use it in add_custom_command +############################################## + +set(maixpy_wrapper_src "${CMAKE_BINARY_DIR}/maixpy_wrapper.cpp") +list(APPEND ADD_SRCS "${maixpy_wrapper_src}") +set_property(SOURCE ${maixpy_wrapper_src} PROPERTY GENERATED 1) +set(cmake_global_vars_json ${CMAKE_BINARY_DIR}/config/cmake_global_vars.json) +# if exists maixpy_wrapper_src then remove it to ensure it will be generated every build time, cause it's not easy to detect all header dependency +if(EXISTS ${maixpy_wrapper_src}) + file(REMOVE ${maixpy_wrapper_src}) +endif() +add_custom_command(OUTPUT ${maixpy_wrapper_src} + COMMAND ${python} -u ${CMAKE_CURRENT_SOURCE_DIR}/gen_api_cpp.py -o ${maixpy_wrapper_src} --sdk_path ${SDK_PATH} + COMMENT "Generating maixpy_wrapper.cpp" + VERBATIM + ) + +# list(APPEND ADD_DEFINITIONS -Wl,-rpath=$ORIGIN/dl_lib) +# FIXME: $ character can not correctly convert to flag \$ORIGIN when build +# so we change it when compile end in gen_binary.cmake + +# register component, DYNAMIC o NATIVE_COMMAND "${flags}")RED flags will make component compiled to dynamic(shared) lib +register_component(DYNAMIC) + diff --git a/tools/maix_module2/components/maix/Kconfig b/tools/maix_module2/components/maix/Kconfig new file mode 100644 index 00000000..aec45d2d --- /dev/null +++ b/tools/maix_module2/components/maix/Kconfig @@ -0,0 +1,5 @@ +config BUILD_WITH_MAIXPY + bool "Used to indicate that maixpy ​​is compiling" + default y + help + application code can use this macro to do special process when compiling MaixPy. diff --git a/tools/maix_module2/components/maix/gen_api.py b/tools/maix_module2/components/maix/gen_api.py new file mode 100644 index 00000000..354c5d58 --- /dev/null +++ b/tools/maix_module2/components/maix/gen_api.py @@ -0,0 +1,254 @@ +''' + @brief Generate C++ code for MaixPy API + @license Apache 2.0 + @author Neucrack@Sipeed + @date 2023.10.23 +''' + + +import sys, os +import argparse +import json +import re +import yaml +import time +try: + from .gen_api_cpp import generate_api_cpp + from .pyi_util import parse_pyi +except Exception: + from gen_api_cpp import generate_api_cpp + from pyi_util import parse_pyi + +curr_dir = os.path.abspath(os.path.dirname(__file__)) +module_name_path = os.path.join(curr_dir, "..", "..", "module_name.txt") +with open(module_name_path, "r") as f: + module_name = f.readline().strip() + + +def sort_headers(headers): + # read headers_priority.txt + headers_priority = [] + priority_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "headers_priority.txt") + with open(priority_file, "r", encoding="utf-8") as f: + for line in f.readlines(): + line = line.strip() + if line.startswith("#"): + continue + headers_priority.append(line) + # sort headers + headers = sorted(headers, key = lambda x: headers_priority.index(os.path.basename(x)) if os.path.basename(x) in headers_priority else len(headers_priority)) + return headers + +def find_func_def(items, name): + for item in items: + # def check_bool_raise(ok: bool, msg: str = '') -> None: + funcname = item.replace("def", "").strip().split("(")[0] + if funcname == name: + return item + return None + +def find_class_func_def(items, mc_k, name, debug): + item = items["class"].get(mc_k, {}) + if debug: + print(items, items["class"], mc_k) + if not item: + return None + # {"name": "", "func": {}} + return find_func_def(item["func"], name) + +def update_py_def_from_stub_files(api_tree, stub): + ''' + parse stub files, add definition to api_tree + ''' + maix_pyi_root = os.path.join(stub, module_name, f"_{module_name}") + for k, v in api_tree["members"][module_name]["members"].items(): + def parse_module(pyi_path, k, v): + if not os.path.exists(pyi_path): + print(f"[WARN] can not find {pyi_path}, you can build for linux platform first to generate this file") + return + items = parse_pyi(pyi_path) + if v["type"] == "func": + name = v["name"] + func_def = find_func_def(items["func"], name) + if func_def: + v["py_def"] = func_def.replace(f"{module_name}._{module_name}", module_name) + elif v["type"] == "class": + for mc_k, mc_v in v["members"].items(): + if mc_v["type"] == "func": + func_def = find_class_func_def(items, k, mc_k, False) + if func_def: + mc_v["py_def"] = func_def.replace(f"{module_name}._{module_name}", module_name) + for m_k, m_v in v.get("members", {}).items(): + if m_v["type"] == "func": + name = m_v["name"] + func_def = find_func_def(items["func"], name) + if func_def: + m_v["py_def"] = func_def.replace(f"{module_name}._{module_name}", module_name) + elif m_v["type"] == "class": + for mc_k, mc_v in m_v["members"].items(): + if mc_v["type"] == "func": + func_def = find_class_func_def(items, m_k, mc_k, False) + if func_def: + mc_v["py_def"] = func_def.replace(f"{module_name}._{module_name}", module_name) + module_dir = os.path.join(maix_pyi_root, k) + if os.path.isdir(module_dir): + for m_k, m_v in v["members"].items(): + path = os.path.join(maix_pyi_root, k, f"{m_k}.pyi") + # TODO: optimize speed + if os.path.exists(path): + parse_module(path, m_k, m_v) + else: + parse_module(os.path.join(maix_pyi_root, k, f"__init__.pyi"), m_k, m_v) + else: + parse_module(os.path.join(maix_pyi_root, f"{k}.pyi"), k, v) + return api_tree + +if __name__ == "__main__": + print("-- Generate MaixPy C/C++ API") + parser = argparse.ArgumentParser(description='Generate MaixPy C/C++ API') + parser.add_argument('--vars', type=str, default="", help="CMake global variables file") + parser.add_argument('--sdk_path', type=str, default="", help="MaixPy SDK path") + parser.add_argument('--doc', type=str, default="", help="API documentation output file") + parser.add_argument('--stub', type=str, default="stub", help="stub dir") + args = parser.parse_args() + + t = time.time() + + sys.path.insert(0, os.path.join(args.sdk_path, "tools")) + from doc_tool.gen_api import get_headers_recursive, parse_api_from_header + from doc_tool.gen_markdown import module_to_md + + # get header files + headers = [] + if args.vars: + with open(args.vars, "r", encoding="utf-8") as f: + vars = json.load(f) + for include_dir in vars["includes"]: + headers += get_headers_recursive(include_dir) + else: # add sdk_path/components all .h and .hpp header files, except 3rd_party components + except_dirs = ["3rd_party"] + curr_dir = os.path.dirname(os.path.abspath(__file__)) + project_components_dir = os.path.abspath(os.path.join(curr_dir, "..")) + componets_dirs = [project_components_dir] + for componets_dir in componets_dirs: + for root, dirs, files in os.walk(componets_dir): + ignored = False + for except_dir in except_dirs: + if os.path.join(componets_dir, except_dir) in root: + ignored = True + break + if ignored: + continue + for name in files: + path = os.path.join(root, name) + if path.endswith(".h") or path.endswith(".hpp"): + headers.append(path) + # check each header file to find MaixPy API + api_tree = {} + rm = [] + all_keys = {} + + headers = sort_headers(headers) + + for header in headers: + api_tree, updated, keys = parse_api_from_header(header, api_tree, sdks = [module_name], module_name = module_name) + if not updated: + rm.append(header) + for h, ks in all_keys.items(): + for k in ks: + if k in keys: + raise Exception("API {} multiple defined in {} and {}".format(k, h, header)) + all_keys[header] = keys + + for r in rm: + headers.remove(r) + + api_tree = update_py_def_from_stub_files(api_tree, args.stub) + + # generate API documenation according to api_tree + print("-- Generating MaixPy API documentation") + doc_out_dir = args.doc + api_json_path = os.path.join(doc_out_dir, "api.json") + side_bar_path = os.path.join(doc_out_dir, "sidebar.yaml") + readme_path = os.path.join(doc_out_dir, "README.md") + sidebar = { + "items": [ + { + "label": "Brief", + "file": "README.md" + } + ] + } + with open(api_json_path, "w", encoding="utf-8") as f: + json.dump(api_tree, f, indent=4) + + doc_maix_sidebar = { + "label": module_name, + "collapsed": False, + "items": [ + # { + # "label": "example", + # "file": "maix/example.md" + # } + ] + } + sidebar["items"].append(doc_maix_sidebar) + start_comment_template = ''' +> You can use `{}` to access this module with MaixPy +> This module is generated from [MaixPy](https://github.com/sipeed/MaixPy) and [MaixCDK](https://github.com/sipeed/MaixCDK) + +''' + top_api_keys = [module_name] + module_members = api_tree["members"][module_name]["members"] + def gen_modules_doc(module_members, parents): + sidebar_items = [] + for m, v in module_members.items(): + if v["type"] == "module": + item = { + "label": m, + "collapsed": False, + "file": "{}.md".format("/".join(parents) + "/" + m) + } + sidebar_items.append(item) + api_file = os.path.join(doc_out_dir, item["file"]) + os.makedirs(os.path.dirname(api_file), exist_ok = True) + module_full_name = ".".join(parents + [m]) + start_comment = start_comment_template.format(module_full_name, module_full_name) + content = module_to_md(parents, m, v, start_comment, module_join_char = ".") + with open(api_file, "w", encoding="utf-8") as f: + f.write(content) + # find submodule + for _k, _v in v["members"].items(): + if _v["type"] == "module": + item["items"] = gen_modules_doc(v["members"], parents + [m]) + return sidebar_items + sidebar_items = gen_modules_doc(module_members, top_api_keys) + doc_maix_sidebar["items"] += sidebar_items + with open(side_bar_path, "w", encoding="utf-8") as f: + yaml.dump(sidebar, f, indent=4) + + readme = ''' +--- +title: MaixPy API -- Maix AI machine vision platform Python API +--- + +**You can read API doc at [MaixPy API on Sipeed Wiki](https://wiki.sipeed.com/maixpy/api/index.html)** + +If you want to preview API doc offline, build MaixPy, and API doc will be generated in `MaixPy/docs/api/` directory. + +> For MaixPy developer: This API documentation is generated from the source code, DO NOT edit this file manually! + +MaixPy API documentation, modules: + +''' + readme += "| module | brief |\n" + readme += "| --- | --- |\n" + for m, v in module_members.items(): + # add link to module api doc + readme += "|[{}.{}](./{}/{}.md) | {} |\n".format(module_name, m, module_name, m, + v["doc"].replace("\n", "
") if type(v["doc"]) == str else v["doc"]["brief"].replace("\n", "
") + ) + with open(readme_path, "w", encoding="utf-8") as f: + f.write(readme) + + print("-- Generate MaixPy API doc complete ({:.2f}s)".format(time.time() - t)) \ No newline at end of file diff --git a/tools/maix_module2/components/maix/gen_api_cpp.py b/tools/maix_module2/components/maix/gen_api_cpp.py new file mode 100644 index 00000000..0104b667 --- /dev/null +++ b/tools/maix_module2/components/maix/gen_api_cpp.py @@ -0,0 +1,210 @@ +''' + @brief Generate C++ code for MaixPy API + @license Apache 2.0 + @author Neucrack@Sipeed + @date 2023.10.23 + 2024.7.17 neucrack add custom module name support +''' + +import os +import argparse +import time +import sys +import json + +curr_dir = os.path.abspath(os.path.dirname(__file__)) +module_name_path = os.path.join(curr_dir, "..", "..", "module_name.txt") +with open(module_name_path, "r") as f: + module_name = f.readline().strip() + +def generate_api_cpp(api_tree, headers, out_path = None): + content = ''' +// This file is generated by MaixPy gen_api.py, +// !! DO NOT edit this file manually + +#include +#include +#include +#include +#include + +#include "maix_basic.hpp" +#include "maix_peripheral.hpp" + +{} + +using namespace maix; +using namespace maix::peripheral; +using namespace {}; + +namespace py = pybind11; + + +PYBIND11_MODULE(_{}, m) {{ + {} +}} +''' + code = [] + maix = api_tree["members"][module_name] + code.append('m.doc() = "{}";'.format(maix["doc"])) + def gen_members(members, _code, parent_var, parent_name, parent_type, parent_names): + for k, v in members.items(): + if type(v["doc"]) == str: + doc = v["doc"] + else: + doc = v.get("doc", {}).get("py_doc", "") + if not doc: + doc = v["doc"].get("brief", "") + doc = doc.replace("\n", "\\n").replace('"', '\\"') + if v["type"] == "module": + sub_m_name = "m_{}".format(k) + _code.append('auto {} = {}.def_submodule("{}", "{}");'.format(sub_m_name, parent_var, k, doc)) + gen_members(v["members"], _code, sub_m_name, k, v["type"], parent_names + [k]) + elif v["type"] == "class": + sub_obj_name = "class_{}".format(k) + v_names = parent_names + [k] + _code.append('auto {} = py::class_<{}>({}, "{}");'.format(sub_obj_name, "::".join(v_names), parent_var, k)) + gen_members(v["members"], _code, sub_obj_name, k, v["type"], parent_names + [k]) + elif v["type"] == "func": + kwargs_str = ", ".join(['py::arg("{}") {}'.format(x[1], '= {}'.format(x[2]) if x[2] is not None else "") for x in v["args"]]) + if kwargs_str: + kwargs_str = ", " + kwargs_str + if k == "__init__": + func_name = parent_name + _code.append('{}.def(py::init<{}>(){});'.format(parent_var, ", ".join([x[0] for x in v["args"]]), kwargs_str)) + elif k == "__iter__": + func_name = parent_name + _code.append('{}.def("__iter__", []({} &c){{return py::make_iterator(c.begin(), c.end());}}, py::keep_alive<0, 1>());'.format(parent_var, "::".join(parent_names))) + elif k == "__del__": + raise Exception("not support __del__ yet") + else: + func_name = v["name"] + # if parent_type == "class": + if v["ret_type"].endswith("&"): + ret_policy = "reference" + else: + ret_policy = "take_ownership" + _code.append('{}.def{}("{}", static_cast<{} ({})({})>({}{}), py::return_value_policy::{}, "{}"{});'.format( + parent_var, "_static" if v["static"] else "", k, + v["ret_type"], + "::".join(parent_names + ["*"]) if (parent_type == "class" and not v["static"]) else "*", + ", ".join([x[0] for x in v["args"]]), + "&{}::".format("::".join(parent_names)) if len(parent_names) > 0 else "", func_name, + ret_policy, + doc, kwargs_str)) + # else: + # _code.append('{}.def{}("{}", {}{}, "{}"{});'.format(parent_var, "_static" if v["static"] else "",k, + # "&{}::".format("::".join(parent_names)) if len(parent_names) > 0 else "", func_name, + # doc, kwargs_str)) + + elif v["type"] == "var": + if parent_type == "class": + v_names = parent_names + [k] + if v["readonly"]: + _code.append('{}.def_readonly{}("{}", &{});'.format(parent_var, "_static" if v["static"] else "", k, "::".join(v_names) )) + else: + _code.append('{}.def_readwrite{}("{}", &{});'.format(parent_var, "_static" if v["static"] else "", k, "::".join(v_names))) + else: + _code.append('{}.attr("{}") = {};'.format(parent_var, k, "::".join(parent_names + [k]))) + elif v["type"] == "enum": + _code.append('py::enum_<{}>({}, "{}")'.format("::".join(parent_names + [k]), parent_var, k)) + for enum_k, v, comment in v["values"]: + v_names = parent_names + [k, enum_k] + _code.append(' .value("{}", {})'.format(enum_k, "::".join(v_names))) + _code.append(';') + + + gen_members(maix["members"], code, parent_var="m", parent_name=module_name, parent_type="module", parent_names=[]) + + code = "\n ".join(code) + headers_final = [] + for h in headers: + headers_final.append('#include "{}"'.format(os.path.basename(h))) + header_str = "\n".join(headers_final) + content = content.format(header_str, module_name, module_name, code) + if out_path: + if os.path.dirname(out_path): + os.makedirs(os.path.dirname(out_path), exist_ok=True) + with open(out_path, "w", encoding="utf-8") as f: + f.write(content) + return content + +def sort_headers(headers): + # read headers_priority.txt + headers_priority = [] + priority_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "headers_priority.txt") + with open(priority_file, "r", encoding="utf-8") as f: + for line in f.readlines(): + line = line.strip() + if line.startswith("#"): + continue + headers_priority.append(line) + # sort headers + headers = sorted(headers, key = lambda x: headers_priority.index(os.path.basename(x)) if os.path.basename(x) in headers_priority else len(headers_priority)) + return headers + +if __name__ == "__main__": + print("-- Generate MaixPy C/C++ API") + parser = argparse.ArgumentParser(description='Generate MaixPy C/C++ API') + parser.add_argument('--vars', type=str, default="", help="CMake global variables file") + parser.add_argument('-o', '--output', type=str, default="", help="API wrapper output file") + parser.add_argument('--sdk_path', type=str, default="", help="MaixPy SDK path") + args = parser.parse_args() + + t = time.time() + + sys.path.insert(0, os.path.join(args.sdk_path, "tools")) + from doc_tool.gen_api import get_headers_recursive, parse_api_from_header + from doc_tool.gen_markdown import module_to_md + + # get header files + headers = [] + if args.vars: + with open(args.vars, "r", encoding="utf-8") as f: + vars = json.load(f) + for include_dir in vars["includes"]: + headers += get_headers_recursive(include_dir) + else: # add sdk_path/components all .h and .hpp header files, except 3rd_party components + except_dirs = ["3rd_party"] + curr_dir = os.path.dirname(os.path.abspath(__file__)) + project_components_dir = os.path.abspath(os.path.join(curr_dir, "..")) + componets_dirs = [project_components_dir] + for componets_dir in componets_dirs: + for root, dirs, files in os.walk(componets_dir): + ignored = False + for except_dir in except_dirs: + if os.path.join(componets_dir, except_dir) in root: + ignored = True + break + if ignored: + continue + for name in files: + path = os.path.join(root, name) + if path.endswith(".h") or path.endswith(".hpp"): + headers.append(path) + # check each header file to find MaixPy API + api_tree = {} + rm = [] + all_keys = {} + + headers = sort_headers(headers) + + for header in headers: + api_tree, updated, keys = parse_api_from_header(header, api_tree, sdks = [module_name], module_name = module_name) + if not updated: + rm.append(header) + for h, ks in all_keys.items(): + for k in ks: + if k in keys: + raise Exception("API {} multiple defined in {} and {}".format(k, h, header)) + all_keys[header] = keys + + for r in rm: + headers.remove(r) + + # generate API cpp file + content = generate_api_cpp(api_tree, headers) + with open(args.output, "w", encoding="utf-8") as f: + f.write(content) + print("-- Generate MaixPy C/C++ API done") + diff --git a/tools/maix_module2/components/maix/headers_priority.txt b/tools/maix_module2/components/maix/headers_priority.txt new file mode 100644 index 00000000..dab13cfb --- /dev/null +++ b/tools/maix_module2/components/maix/headers_priority.txt @@ -0,0 +1,13 @@ +# one line per file +# the former's API will be register first in pybind11 module +# to avoid `ImportError: arg(): could not convert default argument *** into a Python object (type not registered yet?)` error +# comment starts with # + +maix_err.hpp +maix_tensor.hpp +maix_image_def.hpp +maix_image_obj.hpp +maix_image_color.hpp +maix_image.hpp +maix_camera.hpp +maix_display.hpp diff --git a/tools/maix_module2/components/maix/include/decoder_kws.h b/tools/maix_module2/components/maix/include/decoder_kws.h new file mode 100644 index 00000000..777b6ecb --- /dev/null +++ b/tools/maix_module2/components/maix/include/decoder_kws.h @@ -0,0 +1,52 @@ +#ifndef _ASR_DECODE_KWS_H +#define _ASR_DECODE_KWS_H + +#include +#include +#include +#include +#include "ms_asr.h" +#include "ms_asr_cfg.h" + +/*****************************************************************************/ +// Macro definitions +/*****************************************************************************/ +#define KW_LOG_LEN 10 //最多记录的历史拼音格数量 + +/*****************************************************************************/ +// Enums +/*****************************************************************************/ + + +/*****************************************************************************/ +// Types +/*****************************************************************************/ +typedef void (*decoder_cb_t)(void* data, int cnt); + +typedef struct { + uint16_t pny[ASR_KW_MAX_PNY]; //每个唤醒词最多 KW_MAX_PNY 个字 + uint16_t pny_cnt; //该唤醒词的字数 + char* name; //唤醒词的字符表示 + float gate; //门限值 +}asr_kw_t; + + +/*****************************************************************************/ +// Functions +/*****************************************************************************/ + +#ifdef __cplusplus +extern "C"{ +#endif +int decoder_kws_init(decoder_cb_t decoder_cb, size_t* decoder_args, int decoder_argc); +void decoder_kws_deinit(void); +void decoder_kws_run(pnyp_t* pnyp_list); +void decoder_kws_clear(void); +int decoder_kws_reg_similar(char* pny, char** similar_pnys, int similar_cnt); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/tools/maix_module2/components/maix/include/maix_kws.hpp b/tools/maix_module2/components/maix/include/maix_kws.hpp new file mode 100644 index 00000000..ec7ace43 --- /dev/null +++ b/tools/maix_module2/components/maix/include/maix_kws.hpp @@ -0,0 +1,13 @@ + +#include "maix_basic.hpp" + +namespace maix_kws::basic +{ + /** + * hello + * @param name Name to say hello + * @maix_kws maix_kws.basic.hello + */ + void hello(const std::string &name); + +} // namespace maix_kws diff --git a/tools/maix_module2/components/maix/include/ms_asr.h b/tools/maix_module2/components/maix/include/ms_asr.h new file mode 100644 index 00000000..69e2f643 --- /dev/null +++ b/tools/maix_module2/components/maix/include/ms_asr.h @@ -0,0 +1,38 @@ +#ifndef _MS_ASR_H +#define _MS_ASR_H + +#include +#include +#include +#include + +/*****************************************************************************/ +// 基础配置宏 +/*****************************************************************************/ +#define BEAM_CNT 3 // 每帧候选拼音数 +#define ASR_KW_MAX 10 // 最大关键词数量 +#define ASR_KW_MAX_PNY 8 // 每个关键词最大拼音数 +#define MAX_VOCAB_CNT 1250 // 词表大小 + +// 调试标志 +#define DBGT_KWS (1 << 0) + +/*****************************************************************************/ +// 数据结构 +/*****************************************************************************/ + +// 拼音概率结构体(声学模型输出) +typedef struct { + uint16_t idx; // 拼音索引 + float p; // 概率 +} pnyp_t; + +/*****************************************************************************/ +// 外部变量声明 +/*****************************************************************************/ + +// 拼音词表(定义在 ms_asr_tbl.c) +extern const char* am_pnytone_vocab[1250]; +#define am_vocab am_pnytone_vocab + +#endif diff --git a/tools/maix_module2/components/maix/include/ms_asr_cfg.h b/tools/maix_module2/components/maix/include/ms_asr_cfg.h new file mode 100644 index 00000000..dfb314ab --- /dev/null +++ b/tools/maix_module2/components/maix/include/ms_asr_cfg.h @@ -0,0 +1,93 @@ +#ifndef _SP_ASR_CFG_H +#define _SP_ASR_CFG_H + +#include +#include +#include +#include + +typedef struct { + //am config + char* model_name; + int phone_type; + int model_in_len; + int strip_l; + int strip_r; + int agc; + int vocab_cnt; + //计算strip参数 + int strip_out; + int strip_mel; + int strip_p; + //计算model参数 + int model_in_time; + int model_in_p; + int model_out_len; + int model_core_len; + //计算wav参数 + int wav_buf_len; + int wav_core_len; + int wav_rest_len; +}asr_param_t; + +extern asr_param_t asrp; + + +//声学处理的相关缓冲设置 +#define AUDIO_CHANNEL (1) //单声道识别 +#define AUDIO_RATE (16000) //采样率 +#define FFT_N (512) //FFT 点数 +#define FFT_MOVE (128) //STFT 滑窗间隔 + +#define MEL_N (80) //80维mel谱 + +#define KW_LOG_LEN 10 //最多记录的历史拼音格数量 + + +typedef enum { +ASR_DICT_LV=0, ASR_DICT_SHI, ASR_DICT_YANG, ASR_DICT_CHUN, ASR_DICT_YAN, ASR_DICT_JING, ASR_DICT_DA, ASR_DICT_KUAI, ASR_DICT_WEN, ASR_DICT_ZHANG, ASR_DICT_DE, ASR_DICT_DI, ASR_DICT_SE, ASR_DICT_SI, ASR_DICT_YUE, ASR_DICT_LIN, ASR_DICT_LUAN, ASR_DICT_GENG, ASR_DICT_XIAN, ASR_DICT_HUO, ASR_DICT_XIU, ASR_DICT_MEI, ASR_DICT_YI, ASR_DICT_ANG, ASR_DICT_RAN, ASR_DICT_TA, ASR_DICT_JIN, ASR_DICT_PING, ASR_DICT_YAO, ASR_DICT_BU, ASR_DICT_LI, ASR_DICT_LIANG, ASR_DICT_ZAI, ASR_DICT_YONG, ASR_DICT_DAO, ASR_DICT_SHANG, ASR_DICT_XIA, ASR_DICT_FAN, ASR_DICT_TENG, ASR_DICT_DONG, ASR_DICT_SHE, ASR_DICT_XING, ASR_DICT_ZHUANG, ASR_DICT_RU, ASR_DICT_HAI, ASR_DICT_TUN, ASR_DICT_ZHI, ASR_DICT_TOU, ASR_DICT_YOU, ASR_DICT_LING, ASR_DICT_PAO, ASR_DICT_HAO, ASR_DICT_LE, ASR_DICT_ZHA, ASR_DICT_ZEN, ASR_DICT_ME, ASR_DICT_ZHENG, ASR_DICT_CAI, ASR_DICT_YA, ASR_DICT_SHU, ASR_DICT_TUO, ASR_DICT_QU, ASR_DICT_FU, ASR_DICT_GUANG, ASR_DICT_BANG, ASR_DICT_ZI, ASR_DICT_CHONG, ASR_DICT_SHUI, ASR_DICT_CUAN, ASR_DICT_KE, ASR_DICT_SHEI, ASR_DICT_WAN, ASR_DICT_HOU, ASR_DICT_ZHAO, ASR_DICT_JIAN, ASR_DICT_ZUO, ASR_DICT_CU, ASR_DICT_HEI, ASR_DICT_YU, ASR_DICT_CE, ASR_DICT_MING, ASR_DICT_DUI, ASR_DICT_CHENG, ASR_DICT_MEN, ASR_DICT_WO, ASR_DICT_BEI, ASR_DICT_DAI, ASR_DICT_ZHE, ASR_DICT_HU, ASR_DICT_JIAO, ASR_DICT_PANG, ASR_DICT_JI, ASR_DICT_LAO, ASR_DICT_NONG, ASR_DICT_KANG, ASR_DICT_YUAN, ASR_DICT_CHAO, ASR_DICT_HUI, ASR_DICT_XIANG, ASR_DICT_BING, ASR_DICT_QI, ASR_DICT_CHANG, ASR_DICT_NIAN, ASR_DICT_JIA, ASR_DICT_TU, ASR_DICT_BI, ASR_DICT_PIN, ASR_DICT_XI, ASR_DICT_ZOU, ASR_DICT_CHU, ASR_DICT_CUN, ASR_DICT_WANG, ASR_DICT_NA, ASR_DICT_GE, ASR_DICT_AN, ASR_DICT_NING, ASR_DICT_TIAN, ASR_DICT_XIAO, ASR_DICT_ZHONG, ASR_DICT_SHEN, ASR_DICT_NAN, ASR_DICT_ER, ASR_DICT_RI, ASR_DICT_ZHU, ASR_DICT_XIN, ASR_DICT_WAI, ASR_DICT_LUO, ASR_DICT_GANG, ASR_DICT_QING, ASR_DICT_XUN, ASR_DICT_TE, ASR_DICT_CONG, ASR_DICT_GAN, ASR_DICT_LAI, ASR_DICT_HE, ASR_DICT_DAN, ASR_DICT_WEI, ASR_DICT_DIE, ASR_DICT_KAI, ASR_DICT_CI, ASR_DICT_GU, ASR_DICT_NENG, ASR_DICT_BA, ASR_DICT_BAO, ASR_DICT_XUE, ASR_DICT_SHUAI, ASR_DICT_DOU, ASR_DICT_CAO, ASR_DICT_MAO, ASR_DICT_BO, ASR_DICT_ZHOU, ASR_DICT_LIE, ASR_DICT_QIE, ASR_DICT_JU, ASR_DICT_CHUAN, ASR_DICT_GUO, ASR_DICT_LAN, ASR_DICT_NI, ASR_DICT_TANG, ASR_DICT_BAN, ASR_DICT_SU, ASR_DICT_QUAN, ASR_DICT_HUAN, ASR_DICT_YING, ASR_DICT_A, ASR_DICT_MIN, ASR_DICT_MENG, ASR_DICT_WU, ASR_DICT_TAI, ASR_DICT_HUA, ASR_DICT_XIE, ASR_DICT_PAI, ASR_DICT_HUANG, ASR_DICT_GUA, ASR_DICT_JIANG, ASR_DICT_PIAN, ASR_DICT_MA, ASR_DICT_JIE, ASR_DICT_WA, ASR_DICT_SAN, ASR_DICT_KA, ASR_DICT_ZONG, ASR_DICT_NV, ASR_DICT_GAO, ASR_DICT_YE, ASR_DICT_BIAO, ASR_DICT_BIE, ASR_DICT_ZUI, ASR_DICT_REN, ASR_DICT_JUN, ASR_DICT_DUO, ASR_DICT_ZE, ASR_DICT_TAN, ASR_DICT_MU, ASR_DICT_GUI, ASR_DICT_QIU, ASR_DICT_BAI, ASR_DICT_SANG, ASR_DICT_JIU, ASR_DICT_YIN, ASR_DICT_HUAI, ASR_DICT_RANG, ASR_DICT_ZAN, ASR_DICT_SHUO, ASR_DICT_SHA, ASR_DICT_BEN, ASR_DICT_YUN, ASR_DICT_LA, ASR_DICT_CUO, ASR_DICT_HANG, ASR_DICT_HA, ASR_DICT_TUAN, ASR_DICT_GONG, ASR_DICT_SHAN, ASR_DICT_AI, ASR_DICT_KOU, ASR_DICT_ZHEN, ASR_DICT_QIONG, ASR_DICT_DING, ASR_DICT_DANG, ASR_DICT_QUE, ASR_DICT_WENG, ASR_DICT_QIAN, ASR_DICT_FENG, ASR_DICT_JUE, ASR_DICT_ZHUAN, ASR_DICT_CENG, ASR_DICT_ZU, ASR_DICT_BIAN, ASR_DICT_NEI, ASR_DICT_SHENG, ASR_DICT_CHAN, ASR_DICT_ZAO, ASR_DICT_FANG, ASR_DICT_QIN, ASR_DICT_E, ASR_DICT_LIAN, ASR_DICT_FA, ASR_DICT_LU, ASR_DICT_SUN, ASR_DICT_XU, ASR_DICT_DENG, ASR_DICT_GUAN, ASR_DICT_SHOU, ASR_DICT_MO, ASR_DICT_ZHAN, ASR_DICT_PO, ASR_DICT_PI, ASR_DICT_GUN, ASR_DICT_SHUANG, ASR_DICT_QIANG, ASR_DICT_KAO, ASR_DICT_HONG, ASR_DICT_KAN, ASR_DICT_DIAN, ASR_DICT_KONG, ASR_DICT_PEI, ASR_DICT_TONG, ASR_DICT_TING, ASR_DICT_ZANG, ASR_DICT_KUANG, ASR_DICT_RENG, ASR_DICT_TI, ASR_DICT_PAN, ASR_DICT_HENG, ASR_DICT_CHI, ASR_DICT_LUN, ASR_DICT_KUN, ASR_DICT_HAN, ASR_DICT_LEI, ASR_DICT_ZUAN, ASR_DICT_MAN, ASR_DICT_SEN, ASR_DICT_DUAN, ASR_DICT_LENG, ASR_DICT_SUI, ASR_DICT_GAI, ASR_DICT_GA, ASR_DICT_FOU, ASR_DICT_KUO, ASR_DICT_OU, ASR_DICT_SUO, ASR_DICT_SOU, ASR_DICT_NU, ASR_DICT_DU, ASR_DICT_MIAN, ASR_DICT_CHOU, ASR_DICT_HEN, ASR_DICT_KUA, ASR_DICT_SHAO, ASR_DICT_ROU, ASR_DICT_XUAN, ASR_DICT_CAN, ASR_DICT_SAI, ASR_DICT_DUN, ASR_DICT_NIAO, ASR_DICT_CHUI, ASR_DICT_CHEN, ASR_DICT_HUN, ASR_DICT_PENG, ASR_DICT_FEN, ASR_DICT_CANG, ASR_DICT_GEN, ASR_DICT_SHUA, ASR_DICT_CHUO, ASR_DICT_SHUN, ASR_DICT_CHA, ASR_DICT_GOU, ASR_DICT_MAI, ASR_DICT_LIU, ASR_DICT_DIAO, ASR_DICT_TAO, ASR_DICT_NIU, ASR_DICT_MI, ASR_DICT_CHAI, ASR_DICT_LONG, ASR_DICT_GUAI, ASR_DICT_XIONG, ASR_DICT_MOU, ASR_DICT_RONG, ASR_DICT_KU, ASR_DICT_SONG, ASR_DICT_CHE, ASR_DICT_SAO, ASR_DICT_PIAO, ASR_DICT_PU, ASR_DICT_TUI, ASR_DICT_LANG, ASR_DICT_CHUANG, ASR_DICT_KENG, ASR_DICT_LIAO, ASR_DICT_MIAO, ASR_DICT_ZHUI, ASR_DICT_NAI, ASR_DICT_LOU, ASR_DICT_BIN, ASR_DICT_JUAN, ASR_DICT_ZHUA, ASR_DICT_RUN, ASR_DICT_ZENG, ASR_DICT_AO, ASR_DICT_RE, ASR_DICT_PA, ASR_DICT_QUN, ASR_DICT_LIA, ASR_DICT_COU, ASR_DICT_TIE, ASR_DICT_ZHAI, ASR_DICT_KUAN, ASR_DICT_KUI, ASR_DICT_CUI, ASR_DICT_MIE, ASR_DICT_FEI, ASR_DICT_TIAO, ASR_DICT_NUO, ASR_DICT_GEI, ASR_DICT_CA, ASR_DICT_ZHUN, ASR_DICT_NIE, ASR_DICT_MANG, ASR_DICT_ZHUO, ASR_DICT_PEN, ASR_DICT_ZUN, ASR_DICT_NIANG, ASR_DICT_SUAN, ASR_DICT_NAO, ASR_DICT_RUAN, ASR_DICT_QIAO, ASR_DICT_FO, ASR_DICT_RUI, ASR_DICT_RAO, ASR_DICT_RUO, ASR_DICT_ZEI, ASR_DICT_EN, ASR_DICT_ZA, ASR_DICT_DIU, ASR_DICT_NVE, ASR_DICT_SA, ASR_DICT_NIN, ASR_DICT_SHAI, ASR_DICT_NEN, ASR_DICT_KEN, ASR_DICT_CHUAI, ASR_DICT_SHUAN, ASR_DICT_BENG, ASR_DICT_NE, ASR_DICT_LVE, ASR_DICT_QIA, ASR_DICT_JIONG, ASR_DICT_PIE, ASR_DICT_SENG, ASR_DICT_NUAN, ASR_DICT_NANG, ASR_DICT_MIU, ASR_DICT_POU, ASR_DICT_CEN, ASR_DICT_DIA, ASR_DICT_O, ASR_DICT_ZHUAI, ASR_DICT_YO, ASR_DICT_DEI, ASR_DICT_N, ASR_DICT_EI, ASR_DICT_NOU, ASR_DICT_BIA, ASR_DICT_ENG, ASR_DICT_DEN, ASR_DICT_BLANK, ASR_DICT_ERR}asr_dict_t; + +typedef enum +{ + ASR_DIGIT_0 = 0, + ASR_DIGIT_1, + ASR_DIGIT_2, + ASR_DIGIT_3, + ASR_DIGIT_4, + ASR_DIGIT_5, + ASR_DIGIT_6, + ASR_DIGIT_7, + ASR_DIGIT_8, + ASR_DIGIT_9, + ASR_DIGIT_SHI, + ASR_DIGIT_BAI, + ASR_DIGIT_QIAN, + ASR_DIGIT_WAN, + ASR_DIGIT_DOT, + ASR_DIGIT_NONE, +}asr_digits_t; + +/* +#define STRIP_OUT (strip_l+strip_r) +#define STRIP_MEL (STRIP_OUT*8) //回到mel数量要*8 +#define STRIP_P (STRIP_MEL*FFT_MOVE) //2048点,128ms原始数据 + +//#define MODEL_IN_LEN (64) //64高 +#define MODEL_IN_TIME (model_in_len*8)//512ms +#define MODEL_IN_P (model_in_len*FFT_MOVE+FFT_N-FFT_MOVE)//8K点+额外 +#define MODEL_OUT_LEN (model_in_len/8)//8格输出 +#define MODEL_CORE_LEN (MODEL_OUT_LEN-strip_l-strip_r) //输出中心可用的内容 + +#define WAV_BUF_LEN (MODEL_IN_P) //这么多点数计算出来的MEL给模型 +#define WAV_CORE_LEN ((MODEL_OUT_LEN-strip_l-strip_r)*8*FFT_MOVE) //每次新入的buf点数 +#define WAV_REST_LEN ((strip_l+strip_r)*8*FFT_MOVE+FFT_N-FFT_MOVE) //每次滑帧保留的上次末尾的点数 +//WAV_BUF_LEN = WAV_CORE_LEN + WAV_REST_LEN + + +*/ + + +#endif + + + diff --git a/tools/maix_module2/components/maix/include/ms_asr_tbl.h b/tools/maix_module2/components/maix/include/ms_asr_tbl.h new file mode 100644 index 00000000..8295c881 --- /dev/null +++ b/tools/maix_module2/components/maix/include/ms_asr_tbl.h @@ -0,0 +1,18 @@ +#ifndef _MS_ASR_TBL_H +#define _MS_ASR_TBL_H + +#include +#include + +#define MAX_VOCAB_CNT 1250 + +extern uint32_t lm_tbl_cnt; +extern char** am_vocab; +extern char* lm_tbl[MAX_VOCAB_CNT]; +extern uint16_t am2lm[MAX_VOCAB_CNT]; + +extern const char digit_char[16]; +extern const char* am_pny_vocab[408]; +extern const char* am_pnytone_vocab[1250]; + +#endif diff --git a/tools/maix_module2/components/maix/include/ms_asr_utils.h b/tools/maix_module2/components/maix/include/ms_asr_utils.h new file mode 100644 index 00000000..872eb762 --- /dev/null +++ b/tools/maix_module2/components/maix/include/ms_asr_utils.h @@ -0,0 +1,24 @@ +#ifndef __SP_ASR_UTILS_H +#define __SP_ASR_UTILS_H + +#include +#include +#include "stdint.h" + +//CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC +#define DBG_TIME_INIT() struct timespec start,finish;clock_gettime(CLOCK_MONOTONIC,&start); +#define DBG_TIME_START() clock_gettime(CLOCK_MONOTONIC,&start); +#define DBG_TIME(x) {clock_gettime(CLOCK_MONOTONIC,&finish);printf("%s use %.3f ms\n", (x), (double)(utils_cal_dt_us(&start, &finish))/1000.0);clock_gettime(CLOCK_MONOTONIC,&start);} + +#ifdef __cplusplus +extern "C"{ +#endif + +uint32_t utils_cal_dt_us(struct timespec *t0, struct timespec *t1); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/tools/maix_module2/components/maix/pyi_util.py b/tools/maix_module2/components/maix/pyi_util.py new file mode 100644 index 00000000..e6d957d4 --- /dev/null +++ b/tools/maix_module2/components/maix/pyi_util.py @@ -0,0 +1,37 @@ +def parse_pyi(path): + items = { + "class": {}, + "func": [] + } + with open(path) as f: + lines = f.readlines() + class_item = None + for i, line in enumerate(lines): + if class_item: + if line[0] != " ": + items["class"][class_item["name"]] = class_item + class_item = None + else: + line = line.strip() + if line.startswith("def"): + class_item["func"].append(line.rsplit(":", 1)[0]) + continue + + if line.startswith("def"): + items["func"].append(line.rsplit(":", 1)[0]) + if line.startswith("class"): + class_item = { + "name": line.replace("class", "").replace(":", "").strip(), + "func": [] + } + if class_item: + items["class"][class_item["name"]] = class_item + return items + +if __name__ == "__main__": + import sys + items = parse_pyi(sys.argv[1]) + print("Class:") + print(items["class"]) + print("Func:") + print(items["func"]) diff --git a/tools/maix_module2/components/maix/src/decoder_kws.cpp b/tools/maix_module2/components/maix/src/decoder_kws.cpp new file mode 100644 index 00000000..1af58e4b --- /dev/null +++ b/tools/maix_module2/components/maix/src/decoder_kws.cpp @@ -0,0 +1,556 @@ +#include +#include "math.h" +#include +#include +#include +#include +#include "string.h" +#include "decoder_kws.h" +#include "ms_asr.h" +#include "ms_asr_cfg.h" +#include "ms_asr_utils.h" + +#include +#include +using namespace std; + +/*****************************************************************************/ +// Macro +/*****************************************************************************/ +#define DEBUG_LINE() \ + do \ + { \ + printf("[decode] L%d\r\n", __LINE__); \ + } while(0) + +#define TOK_N (5) //toks的剪枝数量 +#define BG_P (1e-6) +#define SIMILAR_CNT (10) + +#define TOK_DBG 0 + +/*****************************************************************************/ +// Types +/*****************************************************************************/ +typedef struct { + int cur_idx; + float cur_p; + int first_t; + int blank_flag; +#if TOK_DBG + vector path; +#endif +}kws_tok_t; + +/*****************************************************************************/ +// Local variable +/*****************************************************************************/ +static decoder_cb_t l_decoder_cb=NULL; +static asr_kw_t l_kw_tbl[ASR_KW_MAX]; +static int l_kw_cnt = 0; +static int l_init_flag = 0; +static int l_log_len = 0; +static pnyp_t l_log[100][BEAM_CNT]; //记录最近的l_log_len格数据,每格64ms +static int l_log_idx = 0; +static int l_tick = 0; +static int l_kw_res_tick[ASR_KW_MAX]; //记录所有关键词上次识别的tick,防止在滑窗时重复识别 + +static uint16_t* l_similar_dict = NULL; + +extern int ms_asr_dbg_flag; + + +/*******************Decoder KWS private **********************/ +static int pny2idx(char* pny) +{ + int idx = -1; + for(int i=0; ipny[pny_i] = idx; + } + + p = p_space+1; + pny_i+=1; + } + kw->pny_cnt = pny_i; + kw->name = kw_str; + kw->gate = pgate; + return 0; +} + +static int is_same_pny(int kw_pny, int rec_pny) +{ + if(rec_pny == kw_pny) return 1; + + int match_flag = 0; + uint16_t* similar_pnys = l_similar_dict+kw_pny*SIMILAR_CNT; + for(int i=0; icur_p; + if (tok->cur_idx > 0){ //至少识别了一个pny才能合并pny + int last_idx = tok->cur_idx-1; + int match_flag = 0; + float psum=0; + for(int i=0; ipny[last_idx], pnyp->idx)){ + if(pnyp->p == 0){ + pp = BG_P; + } else { + pp = pnyp->p; + } + psum += pp; + match_flag = 1; + } + } + if(match_flag==0){ + p *= BG_P; + } else { + p *= psum; + } + } else { + p = 0; + } + return p; +} + +static float cal_blankp(kws_tok_t* tok, pnyp_t* pnyps, asr_kw_t* kw) +{ + int match_flag = 0; + float p = tok->cur_p; + for(int i=0; iidx == asrp.vocab_cnt-1){ + if(pnyp->p == 0){ + pp = BG_P; + } else { + pp = pnyp->p; + } + p *= pp; + match_flag = 1; + } + } + if(match_flag==0){ + p *= BG_P; + } + return p; +} + +static float cal_similarp(kws_tok_t* tok, pnyp_t* pnyps, asr_kw_t* kw) +{ + int match_flag = 0; + int cur_idx = tok->cur_idx; + float p = tok->cur_p; + float psum = 0; + for(int i=0; ipny[cur_idx], pnyp->idx)){ + if(pnyp->p == 0){ + pp = BG_P; + } else { + pp = pnyp->p; + } + psum += pp; + match_flag = 1; + } + } + if(match_flag==0){ + p *= BG_P; + } else { + p *= psum; + } + return p; +} + + +static bool tok_sort_func(kws_tok_t &t1, kws_tok_t &t2) +{ + return t1.cur_p > t2.cur_p; +} + +#if TOK_DBG +static void dump_tok(kws_tok_t &tok) +{ + printf(" cur_idx=%d, cur_p=%.3f, blank_flag=%d: ", tok.cur_idx, tok.cur_p, tok.blank_flag); + for(int j=0; jtoks; +#if TOK_DBG + vector path; + kws_tok_t tok0 = {0,1,-1, 1, path}; +#else + kws_tok_t tok0 = {0,1,-1, 1}; +#endif + //printf("kw pny_idx=%d %d %d %d\n", kw->pny[0], kw->pny[1], kw->pny[2], kw->pny[3]); + toks.push_back(tok0); + for(int t=0; t < frame_t; t++) { + vectornew_toks; + for(int i =0; i < toks.size(); i++){ + if(toks[i].cur_idx >= kw->pny_cnt){ + new_toks.push_back(toks[i]); + } else if(toks[i].cur_p>0){ //未结束的有效tok + //合并相同拼音 + p = cal_multip(&toks[i], pnyp_list+t*BEAM_CNT, kw); + if(p>0){ + #if !TOK_DBG + kws_tok_t tok = {toks[i].cur_idx, p, toks[i].first_t, 0}; + #else + vector new_path = toks[i].path; + uint16_t pny_idx = kw->pny[toks[i].cur_idx-1]; + new_path.push_back(am_vocab[pny_idx]); + kws_tok_t tok = {toks[i].cur_idx, p, toks[i].first_t, 0, new_path}; + #endif + new_toks.push_back(tok); + } + //合并blank + p = cal_blankp(&toks[i], pnyp_list+t*BEAM_CNT, kw); + if(p>0){ + int blank_flag = 0; + if(toks[i].cur_idx == 0){ + p = toks[i].cur_p; + blank_flag = 1; + } + #if !TOK_DBG + kws_tok_t tok = {toks[i].cur_idx, p, toks[i].first_t, blank_flag}; + #else + vector new_path = toks[i].path; + new_path.push_back(am_vocab[asrp.vocab_cnt-1]); + kws_tok_t tok = {toks[i].cur_idx, p, toks[i].first_t, blank_flag, new_path}; + #endif + new_toks.push_back(tok); + } + //合并近音词 + p = cal_similarp(&toks[i], pnyp_list+t*BEAM_CNT, kw); + if(p>0){ + int first_t; + if(toks[i].cur_idx == 0){ + first_t = t; + } else { + first_t = toks[i].first_t; + } + #if !TOK_DBG + kws_tok_t tok = {toks[i].cur_idx+1, p, first_t, 0}; + #else + vector new_path = toks[i].path; + uint16_t pny_idx = kw->pny[toks[i].cur_idx]; + new_path.push_back(am_vocab[pny_idx]); + kws_tok_t tok = {toks[i].cur_idx+1, p, first_t, 0, new_path}; + //dump_tok(tok); + #endif + new_toks.push_back(tok); + } + } + } + //printf("Total %d new toks\n", new_toks.size()); + //去除所有全部都blank的tok + for(int i =0; i < new_toks.size(); ){ + if(new_toks[i].blank_flag == 1){ + new_toks.erase(std::begin(new_toks)+i); + //删除后下一个元素到了i处,所以无需自增i + } else { + i++; + } + } + //对所有有效tok进行剪枝,从最多3*TOK_N+2 剪刀TOK_N个 + sort(new_toks.begin(), new_toks.end(), tok_sort_func); + int topn = new_toks.size()>TOK_N ? TOK_N : new_toks.size(); + int end_cnt = 0; + toks.clear(); + #if TOK_DBG + printf("DBG T=%d:\n", t); + #endif + for(int i = 0; i < topn; i++){ + toks.push_back(new_toks[i]); + if(new_toks[i].cur_idx == kw->pny_cnt){ + end_cnt += 1; + } + #if TOK_DBG + printf("tok%d:", i); + dump_tok(new_toks[i]); + #endif + } + if(end_cnt >= TOK_N){ + //printf("All toks reach end! exit!\n"); + break; + } + //重新加回一个全blank的tok + #if TOK_DBG + vector new_path; + for(int i=0; i sump; + vector first_t; + for(int i =0; i < toks.size(); i++){ + if(toks[i].cur_idx >= kw->pny_cnt){ + int _first_t = toks[i].first_t; + int first_t_idx = -1; + for(int j=0; j < first_t.size(); j++){ + if(_first_t == first_t[j]){ + first_t_idx = j; + break; + } + } + if(first_t_idx >= 0){ + sump[first_t_idx] += toks[i].cur_p; + } else { + first_t.push_back(toks[i].first_t); + sump.push_back(toks[i].cur_p); + } + } + } + //返回概率最大位置 + if(sump.size()>0){ + int max_i = -1; + float max_p = -1; + for(int i=0; i < sump.size(); i++){ + if(sump[i]>max_p){ + max_i = i; + max_p = sump[i]; + } + } + *_p = max_p; + *_tick = first_t[max_i]; + }else{ + *_p = 0; + *_tick = -1; + } + + return; +} + +static void push_pny(pnyp_t* pnyp_list, int cnt) +{ + memmove(l_log, &l_log[cnt], (l_log_len-cnt)*BEAM_CNT*sizeof(pnyp_t)); + memcpy(&l_log[l_log_len-cnt], pnyp_list, cnt*BEAM_CNT*sizeof(pnyp_t)); + return; +} + +static void do_auto_similar(void) +{ + for(int i=0; i='0' && pny[len-1]<='9'){ + pny_strip[len-1] = 0; //去掉最后的音调 + } + len = strlen(pny_strip); + int s_idx = 0; + int idx; + idx = pny2idx(pny_strip); //轻声 + if(idx>=0) {l_similar_dict[i*SIMILAR_CNT+s_idx] = idx; s_idx++;} + pny_strip[len] = '1'; pny_strip[len+1] = 0; + idx = pny2idx(pny_strip); //一声 + if(idx>=0) {l_similar_dict[i*SIMILAR_CNT+s_idx] = idx; s_idx++;} + pny_strip[len] = '2'; pny_strip[len+1] = 0; + idx = pny2idx(pny_strip); //二声 + if(idx>=0) {l_similar_dict[i*SIMILAR_CNT+s_idx] = idx; s_idx++;} + pny_strip[len] = '3'; pny_strip[len+1] = 0; + idx = pny2idx(pny_strip); //三声 + if(idx>=0) {l_similar_dict[i*SIMILAR_CNT+s_idx] = idx; s_idx++;} + pny_strip[len] = '4'; pny_strip[len+1] = 0; + idx = pny2idx(pny_strip); //四声 + if(idx>=0) {l_similar_dict[i*SIMILAR_CNT+s_idx] = idx; s_idx++;} + } + /*for(int i=0; iASR_KW_MAX) { + printf("cnt exceed ASR_KW_MAX!\n"); + return -1; + } + printf("decoder_kws_init get %d kws\n", l_kw_cnt); + for(int i=0; i name); + } + l_similar_dict = (uint16_t*)malloc(sizeof(uint16_t)*asrp.vocab_cnt*SIMILAR_CNT); + if(l_similar_dict == NULL) { + printf("alloc l_similar_dict failed!\n"); + return -1; + } + memset(l_similar_dict, 0xff, sizeof(uint16_t)*asrp.vocab_cnt*SIMILAR_CNT); + if(auto_similar) do_auto_similar(); + l_init_flag = 1; + decoder_kws_clear(); + return 0; +} + +void decoder_kws_deinit(void) +{ + if(l_init_flag == 1){ + decoder_kws_clear(); + free(l_similar_dict); + l_similar_dict = NULL; + l_decoder_cb = NULL; + l_init_flag = 0; + } + return; +} + +void decoder_kws_run(pnyp_t* pnyp_list) +{ + float kw_res[ASR_KW_MAX]; + int kw_res_cnt; + + if(l_decoder_cb) { + DBG_TIME_INIT();DBG_TIME_START(); + push_pny(pnyp_list, asrp.model_core_len); //推入历史拼音列表 + //printf("###l_kw_cnt=%d, l_log_len=%d\n", l_kw_cnt, l_log_len); + for(int i=0; i < l_kw_cnt; i++) { + float p; int tick; + cal_frame_kw((pnyp_t*)l_log, l_log_len, &l_kw_tbl[i], &p, &tick); + //cal_frame_kw(pnyp_t* pnyp_list, int frame_t, asr_kw_t* kw, float*_p, int* _tick) + // 检查重复性,这里的4是随便写的一个数,预留余量 + if((l_kw_res_tick[i] >= 0) && ((l_tick + tick) < l_kw_res_tick[i]+4)){ + kw_res[i] = -1.0*p; //标记是重复的 + } else { + kw_res[i] = p; + if(p >= l_kw_tbl[i].gate) { + l_kw_res_tick[i] = l_tick + tick; + } else { + l_kw_res_tick[i] = 0; //非有效概率,则清零,方便下次更大值唤醒 + } + } + } + if(ms_asr_dbg_flag&DBGT_KWS)DBG_TIME("KWS"); + l_decoder_cb(kw_res, l_kw_cnt);//关键词回调 + } + l_tick += asrp.model_core_len; + return; +} + +void decoder_kws_clear(void) +{ + if(l_init_flag){ + l_log_idx = 0; + for(int t=0; tidx = asrp.vocab_cnt-1; + pnyp->p = 1.0; + for(int i=1; iidx = i; + pnyp->p = 0.0; + } + } + memset(l_log, 0, sizeof(pnyp_t)*BEAM_CNT*KW_LOG_LEN); + l_tick = 0; + for(int i=0; i SIMILAR_CNT) return -1; + for(int i=0; i < similar_cnt; i++){ + int idx = pny2idx(similar_pnys[i]); + if(idx < 0) { //出现非法pny,则清空之前的设置 + memset(l_similar_dict+pny_idx*SIMILAR_CNT, 0xff, sizeof(uint16_t)*SIMILAR_CNT); + return -1; + } + l_similar_dict[pny_idx*SIMILAR_CNT+i] = idx; + } + return 0; +} + + +} + diff --git a/tools/maix_module2/components/maix/src/maix_kws.cpp b/tools/maix_module2/components/maix/src/maix_kws.cpp new file mode 100644 index 00000000..6f60e85a --- /dev/null +++ b/tools/maix_module2/components/maix/src/maix_kws.cpp @@ -0,0 +1,12 @@ +#include "maix_kws.hpp" +#include "decoder_kws.h" +#include "ms_asr.h" +#include "ms_asr_cfg.h" + +namespace maix_kws::basic +{ + void hello(const std::string &name) + { + maix::log::info(("hello: " + name).c_str()); + } +} // namespace maix_kws diff --git a/tools/maix_module2/components/maix/src/ms_asr_tbl.c b/tools/maix_module2/components/maix/src/ms_asr_tbl.c new file mode 100644 index 00000000..280ac8e4 --- /dev/null +++ b/tools/maix_module2/components/maix/src/ms_asr_tbl.c @@ -0,0 +1,184 @@ +#include "ms_asr_tbl.h" + +uint32_t lm_tbl_cnt = 0; +char** am_vocab = NULL; +char* lm_tbl[MAX_VOCAB_CNT]; +uint16_t am2lm[MAX_VOCAB_CNT]; + +const char digit_char[16] = { + '0','1','2','3','4','5','6','7','8','9','S','B','Q','W','.','_' +}; + +//无声调拼音,与声学模型绑定,10个每行 +const char* am_pny_vocab[408]={\ +"lv", "shi", "yang", "chun", "yan", "jing", "da", "kuai", "wen", "zhang", +"de", "di", "se", "si", "yue", "lin", "luan", "geng", "xian", "huo", +"xiu", "mei", "yi", "ang", "ran", "ta", "jin", "ping", "yao", "bu", +"li", "liang", "zai", "yong", "dao", "shang", "xia", "fan", "teng", "dong", +"she", "xing", "zhuang","ru", "hai", "tun", "zhi", "tou", "you", "ling", +"pao", "hao", "le", "zha", "zen", "me", "zheng", "cai", "ya", "shu", +"tuo", "qu", "fu", "guang", "bang", "zi", "chong", "shui", "cuan", "ke", +"shei", "wan", "hou", "zhao", "jian", "zuo", "cu", "hei", "yu", "ce", +"ming", "dui", "cheng", "men", "wo", "bei", "dai", "zhe", "hu", "jiao", +"pang", "ji", "lao", "nong", "kang", "yuan", "chao", "hui", "xiang", "bing", +"qi", "chang", "nian", "jia", "tu", "bi", "pin", "xi", "zou", "chu", +"cun", "wang", "na", "ge", "an", "ning", "tian", "xiao", "zhong", "shen", +"nan", "er", "ri", "zhu", "xin", "wai", "luo", "gang", "qing", "xun", +"te", "cong", "gan", "lai", "he", "dan", "wei", "die", "kai", "ci", +"gu", "neng", "ba", "bao", "xue", "shuai", "dou", "cao", "mao", "bo", +"zhou", "lie", "qie", "ju", "chuan", "guo", "lan", "ni", "tang", "ban", +"su", "quan", "huan", "ying", "a", "min", "meng", "wu", "tai", "hua", +"xie", "pai", "huang", "gua", "jiang", "pian", "ma", "jie", "wa", "san", +"ka", "zong", "nv", "gao", "ye", "biao", "bie", "zui", "ren", "jun", +"duo", "ze", "tan", "mu", "gui", "qiu", "bai", "sang", "jiu", "yin", +"huai", "rang", "zan", "shuo", "sha", "ben", "yun", "la", "cuo", "hang", +"ha", "tuan", "gong", "shan", "ai", "kou", "zhen", "qiong", "ding", "dang", +"que", "weng", "qian", "feng", "jue", "zhuan", "ceng", "zu", "bian", "nei", +"sheng", "chan", "zao", "fang", "qin", "e", "lian", "fa", "lu", "sun", +"xu", "deng", "guan", "shou", "mo", "zhan", "po", "pi", "gun", "shuang", +"qiang", "kao", "hong", "kan", "dian", "kong", "pei", "tong", "ting", "zang", +"kuang", "reng", "ti", "pan", "heng", "chi", "lun", "kun", "han", "lei", +"zuan", "man", "sen", "duan", "leng", "sui", "gai", "ga", "fou", "kuo", +"ou", "suo", "sou", "nu", "du", "mian", "chou", "hen", "kua", "shao", +"rou", "xuan", "can", "sai", "dun", "niao", "chui", "chen", "hun", "peng", +"fen", "cang", "gen", "shua", "chuo", "shun", "cha", "gou", "mai", "liu", +"diao", "tao", "niu", "mi", "chai", "long", "guai", "xiong", "mou", "rong", +"ku", "song", "che", "sao", "piao", "pu", "tui", "lang", "chuang","keng", +"liao", "miao", "zhui", "nai", "lou", "bin", "juan", "zhua", "run", "zeng", +"ao", "re", "pa", "qun", "lia", "cou", "tie", "zhai", "kuan", "kui", +"cui", "mie", "fei", "tiao", "nuo", "gei", "ca", "zhun", "nie", "mang", +"zhuo", "pen", "zun", "niang", "suan", "nao", "ruan", "qiao", "fo", "rui", +"rao", "ruo", "zei", "en", "za", "diu", "nve", "sa", "nin", "shai", +"nen", "ken", "chuai", "shuan", "beng", "ne", "lve", "qia", "jiong", "pie", +"seng", "nuan", "nang", "miu", "pou", "cen", "dia", "o", "zhuai", "yo", +"dei", "n", "ei", "nou", "bia", "eng", "den", "_", }; + +//含声调拼音,与声学模型绑定,10个每行 +//const char* am_tone_vocab[]={ +const char* am_pnytone_vocab[1250]={\ +"de", "wo3", "shi4", "ni3", "me", "le", "bu4", "you3", "yi1", "tian1", +"zai4", "shi2", "ge4", "dao4", "hao3", "shen2", "ma", "lai2", "ge1", "yao4", +"shang4", "gei3", "xia4", "ren2", "hua4", "dian4", "shou3", "mei2", "zen3", "yi4", +"he2", "ming2", "li3", "shuo1", "zhe4", "jiu4", "zhi1", "qu4", "dian3", "yi3", +"fang4", "xin1", "xiang3", "jin1", "jia1", "hai2", "hui4", "zhong1", "a", "da4", +"wei4", "yi2", "xian4", "ji1", "di4", "na4", "hou4", "duo1", "neng2", "bu2", +"wu3", "bo1", "kan4", "gong1", "qi4", "da3", "ting1", "ke3", "sheng1", "ji3", +"zuo4", "xi1", "yue4", "jiao4", "ji2", "cheng2", "qing3", "ba", "er4", "zui4", +"ji4", "kai1", "yu3", "dou1", "ta1", "jian4", "xiao3", "yang4", "san1", "zi4", +"guo4", "men", "er2", "fa1", "hui2", "chu1", "zi", "chi1", "jing1", "ye3", +"nian2", "nan2", "xue2", "ri4", "na3", "jiu3", "yuan2", "qi3", "guo2", "li4", +"zhi4", "yong4", "jie2", "qi1", "yin1", "zhou1", "ai4", "jin4", "ne", "che1", +"lao3", "ye4", "qu3", "qian2", "xin4", "fu4", "dui4", "si1", "hao4", "ban4", +"ling2", "xing2", "wen4", "kuai4", "nv3", "xiao4", "shao3", "ba1", "qing2", "bei4", +"fang1", "yu2", "mei3", "wan2", "wan3", "si4", "ti2", "zhu4", "fu2", "wang3", +"zheng4", "jian1", "zhao3", "ru2", "guan1", "an1", "zhi3", "lu4", "fen1", "hen3", +"bang1", "liu4", "you2", "chang4", "dai4", "ju4", "yu4", "zao3", "zhen1", "ping2", +"du4", "xi3", "dong4", "gao1", "wen2", "fan4", "wu4", "dong1", "ban1", "jie4", +"ying1", "liu2", "cai2", "xiang4", "shui4", "gan3", "xie1", "tai4", "cha2", "de2", +"dan4", "ma3", "suo3", "yang2", "jiang1", "yuan4", "shi1", "su4", "shei2", "qi2", +"shen1", "bao3", "mian4", "bie2", "ding4", "she4", "qu1", "zhang1", "xi4", "wei2", +"jue2", "bai3", "xiang1", "ya", "qing1", "zhi2", "feng1", "xie4", "bao4", "ke4", +"zhan4", "ben3", "wu2", "bi3", "jia4", "lian2", "tong1", "huan1", "shu4", "you4", +"quan2", "ba3", "bei3", "shi3", "xing4", "deng3", "nei3", "hai3", "yan3", "jie1", +"guo3", "bian4", "le4", "chang3", "gang1", "tong2", "zhu3", "mu4", "xing1", "shu1", +"hang2", "jiao1", "qiu2", "chang2", "shang1", "shan1", "jun1", "yan2", "fa3", "huo3", +"fei1", "shui3", "ti3", "zhang3", "gen1", "peng2", "jiang3", "liang4", "piao4", "jie3", +"huang2", "liang3", "ying3", "liao2", "mai3", "mo4", "gan4", "rang4", "zhe", "fei4", +"wang2", "lin2", "cong2", "ren4", "ci4", "gao4", "shou1", "zhao4", "tou2", "huo4", +"zou3", "ma1", "wen1", "xiao1", "wai4", "wang4", "gai1", "ci2", "shou4", "yong3", +"fang2", "ha1", "guan3", "duan3", "nei4", "kao3", "dan1", "men2", "zhuan1", "zhe3", +"jing4", "da2", "ran2", "bi4", "bian1", "tai2", "he1", "hong2", "zhun3", "xing3", +"guang3", "zi1", "hua2", "diao4", "hua1", "zhong4", "yun4", "zhong3", "gu3", "ka3", +"chen2", "la", "xian1", "xu1", "leng3", "xiu1", "chuan1", "yao1", "bai2", "se4", +"hu2", "ge2", "jian3", "qian1", "po2", "tu2", "rong2", "qin1", "huo2", "huan4", +"gu4", "ke1", "te4", "yin2", "dang1", "zhen4", "chuang2","pian4", "xun2", "pai2", +"bing4", "chan3", "wan4", "xi2", "lei4", "kou3", "si3", "pin3", "dao3", "chuan2", +"gou4", "hu4", "biao3", "mo2", "song4", "li2", "lv4", "sheng4", "suan4", "guang1", +"ba4", "tiao2", "kong1", "min2", "zhuang1","pian1", "o2", "shao4", "du2", "yuan3", +"er3", "hei1", "han2", "ying2", "nao3", "zong3", "zhu1", "long2", "lian4", "wei3", +"zuo2", "an4", "wei1", "ban3", "chi2", "fen4", "lan2", "sha1", "mai4", "yan4", +"xu3", "luo4", "ru4", "re4", "cuo4", "jin3", "sheng3", "man4", "pei2", "sha2", +"can1", "fou3", "sai4", "chu3", "ze2", "han4", "yun2", "sou1", "chun1", "xue3", +"liang2", "fan1", "cai3", "tang2", "ting2", "xiong1", "duan4", "jing3", "ying4", "shu3", +"zhuan3", "yue1", "mi4", "xu4", "kuang4", "xun4", "que4", "mang2", "xie3", "qin2", +"huai2", "zhao1", "pu3", "ce4", "sui4", "fan2", "ning2", "gui4", "qun2", "dong3", +"bao1", "gai3", "geng4", "gui1", "chao1", "hun1", "di1", "liao3", "dou4", "chu2", +"mei4", "pin2", "lou2", "qiang2", "mi3", "you1", "tui1", "chong1", "mao4", "xian3", +"fan3", "cai4", "bing1", "gai4", "du1", "la1", "ju2", "jia3", "xiong2", "meng4", +"nin2", "zheng1", "di3", "su1", "biao1", "lun2", "bo2", "gu1", "kuan3", "ya4", +"jiao3", "chong2", "mao2", "xuan3", "er", "n2", "hui1", "hu1", "zu3", "shuang1", +"na2", "tong3", "zu2", "luo2", "qing4", "yang3", "lie4", "dang4", "tuan2", "cun1", +"tan2", "nong2", "bin1", "ci3", "kong4", "zheng3", "gong4", "chu4", "fu1", "yin3", +"zhan3", "chao2", "xuan1", "tao2", "pei4", "huan2", "zi3", "ma2", "jiang4", "tuo1", +"ya1", "wu1", "tian2", "lv3", "ao4", "teng2", "ya2", "ling3", "zhang4", "cun2", +"pi2", "tie3", "kang1", "gang3", "gao3", "fu3", "ruan3", "ceng2", "cheng1", "a1", +"ting3", "sui2", "pa4", "xian2", "deng1", "shun4", "xie2", "liao4", "mu3", "e2", +"qiao2", "bai4", "cha4", "hai4", "di2", "ku4", "yao2", "man3", "zao4", "gan1", +"niu2", "xiang2", "za3", "weng1", "bei1", "song1", "e4", "ben4", "wa2", "qie4", +"xia2", "nian4", "qiu1", "ming4", "nao4", "zeng1", "mian3", "pao3", "huai4", "rou4", +"zhuang4","yan1", "tu3", "tao4", "fei2", "zhe2", "guan4", "shao1", "meng2", "ju1", +"ai1", "lian3", "nai3", "ling4", "lang2", "qie3", "sha3", "zuo3", "guo1", "ya3", +"pai1", "quan1", "ken3", "chang1", "chuang4","tu1", "wan1", "yang1", "sun1", "tui4", +"bu3", "lun4", "ye2", "xiu4", "shuai4", "yun3", "zan2", "po4", "tong4", "zong1", +"xue4", "nong4", "ku3", "yong1", "gou3", "heng2", "feng4", "lang4", "mian2", "ni2", +"gui3", "xu2", "jiu1", "mou3", "wen3", "pan2", "shen3", "tiao4", "kuo4", "ju3", +"gun3", "shan4", "gua1", "cha1", "cao3", "shang3", "ruo4", "bi1", "qian4", "min3", +"pi1", "kai3", "nuan3", "cong1", "yin4", "guai1", "du3", "guai4", "chao3", "pai4", +"que1", "mi2", "bei", "kao4", "qiong2", "ku1", "ou1", "zhao2", "jun4", "niang2", +"tan4", "lan3", "pao4", "xuan2", "fa4", "hu3", "kun1", "?", "gua4", "kong3", +"nuo4", "zou4", "lia3", "tao3", "kou4", "lei2", "bang3", "geng1", "rui4", "zhuo2", +"zha1", "ding1", "juan1", "lao2", "lu2", "pang2", "rao3", "luan4", "cang1", "za2", +"tie1", "lu3", "zu1", "fen3", "miao2", "pan4", "ding3", "shua1", "niao3", "rou2", +"he4", "cang2", "zhui1", "chun2", "duan1", "zhuan4", "tou", "dun4", "la4", "da1", +"quan4", "kun4", "fang3", "cao1", "en1", "cai1", "chui1", "chou2", "zan4", "wo4", +"suan1", "shua3", "zai3", "gun4", "bang4", "dang3", "tou4", "shu2", "kuang2", "chuang1", +"pi4", "qiang1", "shuang3","run4", "pian2", "sui1", "mao1", "hao2", "chi4", "zhu2", +"lei3", "tao1", "pang4", "sun3", "zhou4", "zun1", "cui1", "dao1", "duo3", "kuan1", +"ran3", "die2", "tang4", "xuan4", "zhuo1", "fa2", "zui3", "san3", "shan3", "ti4", +"san4", "nu3", "yuan1", "zhai4", "gou1", "zang4", "pan1", "piao1", "hou2", "miao4", +"qiang3", "deng4", "lve4", "sa4", "wo1", "zhua1", "ma4", "mo1", "ta3", "die1", +"hun2", "ou3", "tui3", "chou1", "chi3", "po1", "pu2", "sen1", "miao3", "kang4", +"reng2", "yun1", "tan1", "shen4", "tang1", "guang4", "bing3", "tan3", "ru3", "pu4", +"juan3", "zhai2", "nai4", "bi2", "sao3", "shai4", "dun1", "o1", "han3", "wang1", +"cu4", "zao1", "can2", "tou1", "pin4", "ni4", "liu3", "qiao3", "ka1", "hun4", +"diu1", "hui3", "men4", "xia1", "man2", "fo2", "tang3", "tu4", "shuai1", "re3", +"juan4", "tai1", "kua4", "ren3", "chai1", "mie4", "yo1", "suo1", "che4", "zha4", +"sai1", "chou3", "cao2", "cui4", "dai1", "hai1", "wa1", "pa2", "feng2", "duo2", +"huo", "sang1", "su2", "she3", "luo3", "lang3", "xue1", "sha4", "lai4", "she2", +"shao2", "zhen3", "huan3", "ni1", "kan1", "peng4", "lan4", "chou4", "niu1", "xun1", +"sa1", "shi", "ti1", "zai1", "wa", "niu3", "ca1", "hen4", "chan2", "qin3", +"heng1", "niao4", "shuai3", "ben1", "wa3", "che3", "lei", "diao1", "an3", "qiao1", +"kui1", "wai1", "kan3", "pin1", "meng3", "lei1", "shuo4", "chong3", "zhan1", "tun2", +"ba2", "ao2", "wa4", "chai2", "lou4", "qiao4", "nu4", "yao3", "rao4", "dian1", +"guai3", "keng1", "huang1", "dan3", "kui2", "ta4", "sao1", "liu1", "bao2", "kui4", +"mi1", "ke2", "huang3", "chuang3","zuan1", "cun4", "kua1", "dui1", "geng3", "qu2", +"mou2", "zhai1", "zeng4", "zong4", "bo", "jiong3", "qia4", "chuan4", "pu1", "tiao1", +"zhui4", "lv2", "ga1", "cu1", "tuo2", "pi3", "tiao3", "ang2", "qian3", "fei3", +"ning4", "ei2", "can4", "chen4", "long3", "chui2", "can3", "zei2", "quan3", "rao2", +"mai2", "pao1", "fen2", "ai2", "pen1", "zang1", "chun3", "nan4", "qie1", "dou3", +"zha2", "piao2", "pen2", "hen2", "cou4", "ye1", "beng4", "gua3", "hong3", "sa3", +"nang2", "bian3", "mu2", "nu2", "tuo4", "e3", "sang3", "po3", "gong3", "kuang1", +"zhe1", "ping1", "han1", "reng1", "hong1", "seng1", "beng1", "sou4", "nie4", "tun1", +"chan4", "sang4", "pang1", "li1", "pa1", "qie2", "tuo3", "lao1", "ge3", "li", +"nen4", "diao3", "peng1", "duo4", "sheng2", "zhai3", "zhou2", "ai3", "zuan4", "lao4", +"bie1", "nve4", "lang1", "leng2", "dai3", "luan2", "lu1", "lie3", "la3", "lin4", +"jia2", "mo3", "nie1", "cheng4", "luan3", "cuo1", "zou1", "rang3", "mie1", "nuo2", +"men1", "dei3", "weng4", "mao3", "fu", "hou3", "peng3", "chuo4", "qin4", "kun3", +"kua3", "shui2", "pao2", "ceng4", "she1", "shuan1", "song3", "yu1", "qia3", "sui3", +"shuan4", "tian3", "suo", "rui3", "ao3", "nian3", "gu2", "zan3", "lou3", "e1", +"pei1", "chen1", "chuan3", "ga4", "pou1", "bin4", "cuan4", "niang4", "chuai1", "ga2", +"di", "ci1", "zhua3", "luo1", "bai1", "cheng3", "gang4", "kang2", "qia1", "miao1", +"xiu3", "chuo1", "zhou3", "feng3", "pie3", "mang3", "po", "ao1", "zhuai1", "pie1", +"bo4", "nang1", "kou1", "na1", "long4", "tui2", "huang4", "leng4", "cen2", "xin2", +"zha3", "miu4", "shang", "bie4", "bo3", "nao2", "nan3", "hao1", "wai3", "qiu3", +"lu", "la2", "hu", "chong4", "ang1", "cha3", "beyond", "heng4", "ha2", "de1", +"chuai4", "jue1", "ne2", "shai3", "en4", "n", "cuo2", "shai1", "ceng1", "za1", +"nian1", "zao2", "zuo1", "liao1", "piao3", "beng2", "mo", "du", "jiao2", "sao4", +"cui3", "huo1", "sa", "ne4", "ze4", "ju", "que2", "ha3", "nan1", "meng1", +"le1", "chan1", "ang4", "qi", "lin1", "zan1", "cuan1", "shun3", "me1", "jia", +"tuan1", "liang", "hou", "qie", "sou3", "bing", "xiao2", "qq", "rui2", "hong4", +"rong1", "zuan3", "gen4", "pin", "sam", "chuai3", "fang", "sheng", "lao", "hang1", +"lin3", "lun1", "zhuai3", "chen3", "da", "rang1", "dun3", "bu1", "e", "tun4", +"tan", "lie1", "gen3", "tie4", "jie", "qiong1", "he", "ga3", "man", "qiang4", +"rang2", "cen1", "kuai3", "rong3", "nou4", "hao", "cuan2", "yong2", "cu2", "_", +}; diff --git a/tools/maix_module2/components/maix/src/ms_asr_utils.c b/tools/maix_module2/components/maix/src/ms_asr_utils.c new file mode 100644 index 00000000..5db1be21 --- /dev/null +++ b/tools/maix_module2/components/maix/src/ms_asr_utils.c @@ -0,0 +1,25 @@ +#include "ms_asr_utils.h" + +#ifdef __cplusplus +extern "C"{ +#endif +//note: max 4295s +uint32_t utils_cal_dt_us(struct timespec *t0, struct timespec *t1) +{ + uint32_t dt; + if(t1->tv_nsec < t0->tv_nsec) { + long tmp = t1->tv_nsec + 1e9; + long d_ns = tmp - t0->tv_nsec; + long d_s = t1->tv_sec-1-t0->tv_sec; + dt = (uint32_t)(d_s*1000000+d_ns/1000); + } else { + long d_ns = t1->tv_nsec - t0->tv_nsec; + long d_s = t1->tv_sec-t0->tv_sec; + dt = (uint32_t)(d_s*1000000+d_ns/1000); + } + return dt; +} + +#ifdef __cplusplus +} +#endif diff --git a/tools/maix_module2/components/maix/test.py b/tools/maix_module2/components/maix/test.py new file mode 100644 index 00000000..a392b5ed --- /dev/null +++ b/tools/maix_module2/components/maix/test.py @@ -0,0 +1,43 @@ +from gen_api import parse_api, parse_api_from_header +from gen_api_cpp import generate_api_cpp +import os, sys + + +code = ''' +namespace maix +{ + /** + * @brief Example class + * this class will be export to MaixPy as maix.example.Example + * @maixpy maix.example.Example + */ + class Example + { + }; +} + +namespace maix +{ + /** + * @brief Example class + * this class will be export to MaixPy as maix.example.Example + * @maixpy maix.example.Example + */ + class Example + { + }; +} + +''' + +test_header = sys.argv[1] + +curr_dir = os.path.abspath(os.path.dirname(__file__)) + +res = parse_api_from_header(os.path.join(curr_dir, test_header)) +import json, os + +with open("test.json", "w") as f: + f.write(json.dumps(res, indent=4)) + +generate_api_cpp(res, [os.path.join(curr_dir, test_header)], "test.cpp") diff --git a/tools/maix_module2/configs/config_platform_linux.mk b/tools/maix_module2/configs/config_platform_linux.mk new file mode 100644 index 00000000..76fd1207 --- /dev/null +++ b/tools/maix_module2/configs/config_platform_linux.mk @@ -0,0 +1,4 @@ + +# set config opencv compile from source to avoid opencv lib not found error or version not match error +# CONFIG_OPENCV_COMPILE_FROM_SOURCE=y + diff --git a/tools/maix_module2/configs/config_platform_maixcam.mk b/tools/maix_module2/configs/config_platform_maixcam.mk new file mode 100644 index 00000000..e69de29b diff --git a/tools/maix_module2/configs/config_platform_maixcam2.mk b/tools/maix_module2/configs/config_platform_maixcam2.mk new file mode 100644 index 00000000..e69de29b diff --git a/tools/maix_module2/docs/README.md b/tools/maix_module2/docs/README.md new file mode 100644 index 00000000..ae506102 --- /dev/null +++ b/tools/maix_module2/docs/README.md @@ -0,0 +1,32 @@ +MaixPy documentation +==== + +Visit online doc: [https://wiki.sipeed.com/maixpy/](https://wiki.sipeed.com/maixpy/) + + +## Preview locally + + +* Install doc build tool `teedoc`: +```shell +pip install teedoc -U +``` + +* Install plugins used by this doc: +```shell +cd MaixPy/docs +teedoc install +``` + +* Start local preview server: +```shell +teedoc serve +``` +Then open `http://127.0.0.1:2333` in your browser. + +* To build a offline html doc: +```shell +teedoc build +``` +Then you will find HTML docs in `out` directory. + diff --git a/tools/maix_module2/docs/api/config.json b/tools/maix_module2/docs/api/config.json new file mode 100644 index 00000000..ff4f116e --- /dev/null +++ b/tools/maix_module2/docs/api/config.json @@ -0,0 +1,4 @@ +{ + "import": "config_en", + "name": "MaixPy API Reference" +} diff --git a/tools/maix_module2/docs/config/config_en.json b/tools/maix_module2/docs/config/config_en.json new file mode 100644 index 00000000..401295db --- /dev/null +++ b/tools/maix_module2/docs/config/config_en.json @@ -0,0 +1,162 @@ +{ + "locale": "en", + "navbar": { + "title": "MaixPy", + "logo": { + "alt": "MaixPy logo", + "url": "" + }, + "home_url": "/en/", + "items": [ + { + "url": "https://wiki.sipeed.com", + "label": "Sipeed Wiki", + "position": "left" + }, + { + "url": "/doc/en/index.html", + "label": "Documentation", + "position": "left" + }, + { + "url": "/api/index.html", + "label": "API", + "position": "left" + }, + { + "url": "/doc/en/faq.html", + "label": "FAQ", + "position": "left" + }, + { + "id": "github", + "label": " ", + "url": "https://github.com/sipeed/maixpy", + "position": "right", + "target": "_blank" + }, + { + "id": "language", + "label": " ", + "position": "right", + "type": "language" + } + ] + }, + "footer":{ + "top":[ + { + "label": "Links", + "items": [ + { + "label": "Sipeed Wiki", + "url": "https://wiki.sipeed.com", + "target": "_blank" + }, + { + "label": "Sipeed Official", + "url": "https://www.sipeed.com", + "target": "_blank" + }, + { + "label": "MaixHub", + "url": "https://maixhub.com/", + "target": "_blank" + }, + { + "label": "Site map", + "url": "/sitemap.xml" + }, + { + "label": "Generated by teedoc", + "url": "https://github.com/neutree/teedoc", + "target": "_blank" + } + ] + }, + { + "label": "Source code", + "items": [ + { + "label": "MaixPy source code", + "url": "https://github.com/sipeed/maixpy", + "target": "_blank" + }, + { + "label": "MaixCDK source code", + "url": "https://github.com/sipeed/MaixCDK", + "target": "_blank" + }, + { + "label": "Wiki source code", + "url": "https://github.com/sipeed/sipeed_wiki", + "target": "_blank" + }, + { + "label": "Open source projects", + "url": "https://github.com/sipeed", + "target": "_blank" + } + ] + }, + { + "label": "Follow us", + "items": [ + { + "label": "twitter", + "url": "https://twitter.com/SipeedIO", + "target":"_blank" + }, + { + "label": "Taobao", + "url": "https://sipeed.taobao.com/", + "target":"_blank" + }, + { + "label": "AliExpress", + "url": "https://www.aliexpress.com/store/911876460", + "target":"_blank" + }, + { + "label": "github", + "url": "https://github.com/sipeed", + "target":"_blank" + }, + { + "label": "Wechat " + } + ] + }, + { + "label": "Contact us", + "items": [ + { + "label": "Tel: +86 0755-27808509" + }, + { + "label": "Business: support@sipeed.com" + }, + { + "label": "Addr: 深圳市宝安区新湖路4008号蘅芳科技办公大厦A座-2101C" + }, + { + "label": "Join us", + "url": "https://wiki.sipeed.com/join_us.html" + } + ] + } + ], + "bottom": [ + { + "label": "©2018-2023 深圳矽速科技有限公司", + "url": "https://www.sipeed.com", + "target": "_blank" + }, + { + "label": "粤ICP备19015433号", + "url": "https://beian.miit.gov.cn/#/Integrated/index", + "target": "_blank" + } + ] + } +} diff --git a/tools/maix_module2/docs/config/config_zh.json b/tools/maix_module2/docs/config/config_zh.json new file mode 100644 index 00000000..1e979691 --- /dev/null +++ b/tools/maix_module2/docs/config/config_zh.json @@ -0,0 +1,162 @@ +{ + "locale": "zh", + "navbar": { + "title": "MaixPy", + "logo": { + "alt": "MaixPy logo", + "url": "" + }, + "home_url": "/", + "items": [ + { + "url": "https://wiki.sipeed.com", + "label": "Sipeed Wiki", + "position": "left" + }, + { + "url": "/doc/zh/index.html", + "label": "文档", + "position": "left" + }, + { + "url": "/api/index.html", + "label": "API", + "position": "left" + }, + { + "url": "/doc/zh/faq.html", + "label": "FAQ", + "position": "left" + }, + { + "id": "github", + "label": " ", + "url": "https://github.com/sipeed/maixpy", + "position": "right", + "target": "_blank" + }, + { + "id": "language", + "label": " ", + "position": "right", + "type": "language" + } + ] + }, + "footer":{ + "top":[ + { + "label": "相关链接", + "items": [ + { + "label": "Sipeed Wiki", + "url": "https://wiki.sipeed.com", + "target": "_blank" + }, + { + "label": "Sipeed 官网", + "url": "https://www.sipeed.com", + "target": "_blank" + }, + { + "label": "MaixHub", + "url": "https://maixhub.com/", + "target": "_blank" + }, + { + "label": "网站地图", + "url": "/sitemap.xml" + }, + { + "label": "网站使用 teedoc 生成", + "url": "https://github.com/neutree/teedoc", + "target": "_blank" + } + ] + }, + { + "label": "源码", + "items": [ + { + "label": "MaixPy 源码", + "url": "https://github.com/sipeed/maixpy", + "target": "_blank" + }, + { + "label": "MaixCDK 源码", + "url": "https://github.com/sipeed/MaixCDK", + "target": "_blank" + }, + { + "label": "Wiki 源码", + "url": "https://github.com/sipeed/sipeed_wiki", + "target": "_blank" + }, + { + "label": "开源项目", + "url": "https://github.com/sipeed", + "target": "_blank" + } + ] + }, + { + "label": "关注我们", + "items": [ + { + "label": "twitter", + "url": "https://twitter.com/SipeedIO", + "target":"_blank" + }, + { + "label": "淘宝", + "url": "https://sipeed.taobao.com/", + "target":"_blank" + }, + { + "label": "AliExpress", + "url": "https://www.aliexpress.com/store/911876460", + "target":"_blank" + }, + { + "label": "github", + "url": "https://github.com/sipeed", + "target":"_blank" + }, + { + "label": "微信公众号" + } + ] + }, + { + "label": "联系我们", + "items": [ + { + "label": "电话: +86 0755-27808509" + }, + { + "label": "商业支持: support@sipeed.com" + }, + { + "label": "地址: 深圳市宝安区新湖路4008号蘅芳科技办公大厦A座-2101C" + }, + { + "label": "加入我们", + "url": "https://wiki.sipeed.com/join_us.html" + } + ] + } + ], + "bottom": [ + { + "label": "©2018-2023 深圳矽速科技有限公司", + "url": "https://www.sipeed.com", + "target": "_blank" + }, + { + "label": "粤ICP备19015433号", + "url": "https://beian.miit.gov.cn/#/Integrated/index", + "target": "_blank" + } + ] + } +} diff --git a/tools/maix_module2/docs/doc/assets/yolov8_seg.jpg b/tools/maix_module2/docs/doc/assets/yolov8_seg.jpg new file mode 100644 index 00000000..316875be Binary files /dev/null and b/tools/maix_module2/docs/doc/assets/yolov8_seg.jpg differ diff --git a/tools/maix_module2/docs/doc/en/README.md b/tools/maix_module2/docs/doc/en/README.md new file mode 100644 index 00000000..e7bd3492 --- /dev/null +++ b/tools/maix_module2/docs/doc/en/README.md @@ -0,0 +1,25 @@ +--- +title: MaixPy moudle maix_xxx Quick Start +--- + +## Introduction + +This module is designed to address the issues of xxxxx xxxxxx. + +![picture](../../assets/yolov8_seg.jpg) + +## First step + +xxxxx + +## Second step + + +xxxx + +## More Information + +This module is built on [MaixCDK](https://github.com/sipeed/MaixCDK) and is recommended to be used in conjunction with [MaixPy](https://github.com/sipeed/MaixPy). + +For MaixPy documentation, visit [wiki.sipeed.com/maixpy](https://wiki.sipeed.com/maixpy). + diff --git a/tools/maix_module2/docs/doc/en/config.json b/tools/maix_module2/docs/doc/en/config.json new file mode 100644 index 00000000..5e4e4fd2 --- /dev/null +++ b/tools/maix_module2/docs/doc/en/config.json @@ -0,0 +1,4 @@ +{ + "import": "config_en", + "name": "MaixPy module English Documentation" +} diff --git a/tools/maix_module2/docs/doc/en/faq.md b/tools/maix_module2/docs/doc/en/faq.md new file mode 100644 index 00000000..6bf04717 --- /dev/null +++ b/tools/maix_module2/docs/doc/en/faq.md @@ -0,0 +1,21 @@ +--- + +title: MaixPy FAQ (Frequently Asked Questions) + +--- + +This page lists common questions and solutions related to MaixPy. If you encounter any issues, please look for answers here first. If you cannot find the answer on this page, you can post detailed steps of your problem in the [MaixHub Discussion Forum](https://maixhub.com/discussion). + +For MaixPy-related questions, refer to [MaixPy FAQ](https://wiki.sipeed.com/maixpy/doc/en/faq.html). If you are using MaixCAM, you can also refer to the [MaixCAM FAQ](https://wiki.sipeed.com/hardware/zh/maixcam/faq.html). + +## Installation + +* Download the precompiled `xxx.whl` file. +* Upload the file to your device, for example, to the `/root` directory. +* Execute the following Python code. +```python +import os + +os.system("pip install /root/xxx.whl") +``` + diff --git a/tools/maix_module2/docs/doc/en/sidebar.yaml b/tools/maix_module2/docs/doc/en/sidebar.yaml new file mode 100644 index 00000000..3e6ed156 --- /dev/null +++ b/tools/maix_module2/docs/doc/en/sidebar.yaml @@ -0,0 +1,5 @@ +items: +- file: README.md + label: Quick Start +- file: faq.md + label: FAQ diff --git a/tools/maix_module2/docs/doc/zh/README.md b/tools/maix_module2/docs/doc/zh/README.md new file mode 100644 index 00000000..f4a54411 --- /dev/null +++ b/tools/maix_module2/docs/doc/zh/README.md @@ -0,0 +1,30 @@ +--- +title: MaixPy 模块 maix_xxx 快速开始 +--- + +## 功能介绍 + +本模块旨在解决 xxxxx 的问题 xxxxxx。 + +![picture](../../assets/yolov8_seg.jpg) + + +## 第一步 + +xxxxx + +## 第二步 + + +xxxx + + +## 更多 + +本模块基于 [MaixCDK](https://github.com/sipeed/MaixCDK) 构建,建议配合使用 [MaixPy](https://github.com/sipeed/MaixPy) 一起使用本模块。 + +MaixPy 文档见 [wiki.sipeed.com/maixpy](https://wiki.sipeed.com/maixpy)。 + + + + diff --git a/tools/maix_module2/docs/doc/zh/config.json b/tools/maix_module2/docs/doc/zh/config.json new file mode 100644 index 00000000..977ebbb3 --- /dev/null +++ b/tools/maix_module2/docs/doc/zh/config.json @@ -0,0 +1,4 @@ +{ + "import": "config_zh", + "name": "MaixPy 模块中文文档" +} diff --git a/tools/maix_module2/docs/doc/zh/faq.md b/tools/maix_module2/docs/doc/zh/faq.md new file mode 100644 index 00000000..6250817a --- /dev/null +++ b/tools/maix_module2/docs/doc/zh/faq.md @@ -0,0 +1,21 @@ +--- +title: MaixPy FAQ(常见问题) +--- + +此页面列出了 MaixPy 相关的常见问题和解决方案,如果你遇到了问题,请先在这里找寻答案。 +如果这个页面找不到答案,可以到 [MaixHub 讨论版块](https://maixhub.com/discussion) 将问题的详细步骤发贴提问。 + +MaixPy 相关问题参考 [MaixPy FAQ](https://wiki.sipeed.com/maixpy/doc/zh/faq.html) +如果你使用的是 MaixCAM, 也可以参考 [MaixCAM FAQ](https://wiki.sipeed.com/hardware/zh/maixcam/faq.html) + +## 如何安装 + +* 下载编译好的 `xxx.whl` 文件。 +* 上传文件到设备,比如上传到`/root`目录下。 +* 执行 Python 代码。 +```python +import os + +os.system("pip install /root/xxx.whl") +``` + diff --git a/tools/maix_module2/docs/doc/zh/sidebar.yaml b/tools/maix_module2/docs/doc/zh/sidebar.yaml new file mode 100644 index 00000000..dcf21a9f --- /dev/null +++ b/tools/maix_module2/docs/doc/zh/sidebar.yaml @@ -0,0 +1,6 @@ +items: +- file: README.md + label: 快速开始 +- file: faq.md + label: FAQ 常见问题 + diff --git a/tools/maix_module2/docs/pages/index/404.md b/tools/maix_module2/docs/pages/index/404.md new file mode 100644 index 00000000..445310ba --- /dev/null +++ b/tools/maix_module2/docs/pages/index/404.md @@ -0,0 +1,4 @@ +--- +layout: 404.html +--- + diff --git a/tools/maix_module2/docs/pages/index/README.md b/tools/maix_module2/docs/pages/index/README.md new file mode 100644 index 00000000..f4f5f56b --- /dev/null +++ b/tools/maix_module2/docs/pages/index/README.md @@ -0,0 +1,123 @@ + +--- +title: MaixPy +id: home_page +--- + +
+ +
+ + + +
+
+ MaixPy Banner +
+

MaixPy (v4) 模块 maix_xxx

+

快速落地 AI 视觉、听觉应用

+
+ + + + +
+ +[English](./en/) | 中文 + +
+ diff --git a/tools/maix_module2/docs/pages/index/config.json b/tools/maix_module2/docs/pages/index/config.json new file mode 100644 index 00000000..7689a260 --- /dev/null +++ b/tools/maix_module2/docs/pages/index/config.json @@ -0,0 +1,5 @@ +{ + "import": "config_zh", + "class": "md_page", + "name": "MaixPy 页面" +} diff --git a/tools/maix_module2/docs/pages/index/favicon.ico b/tools/maix_module2/docs/pages/index/favicon.ico new file mode 100644 index 00000000..3d8e88b1 Binary files /dev/null and b/tools/maix_module2/docs/pages/index/favicon.ico differ diff --git a/tools/maix_module2/docs/pages/index_en/README.md b/tools/maix_module2/docs/pages/index_en/README.md new file mode 100644 index 00000000..f2109c73 --- /dev/null +++ b/tools/maix_module2/docs/pages/index_en/README.md @@ -0,0 +1,123 @@ + +--- +title: MaixPy +id: home_page +--- + +
+ +
+ + + +
+
+ MaixPy Banner +
+

MaixPy (v4) module maix_xxx

+

Easily create AI projects with Python on edge device

+
+ + + + +
+ +English | [中文](/) + +
+ diff --git a/tools/maix_module2/docs/pages/index_en/config.json b/tools/maix_module2/docs/pages/index_en/config.json new file mode 100644 index 00000000..04d94f89 --- /dev/null +++ b/tools/maix_module2/docs/pages/index_en/config.json @@ -0,0 +1,5 @@ +{ + "import": "config_en", + "class": "md_page", + "name": "MaixPy Pages" +} diff --git a/tools/maix_module2/docs/site_config.json b/tools/maix_module2/docs/site_config.json new file mode 100644 index 00000000..012dc32d --- /dev/null +++ b/tools/maix_module2/docs/site_config.json @@ -0,0 +1,97 @@ +{ + "site_name": "MaixPy", + "site_slogon": "", + "site_root_url": "/maixpy/", + "site_domain": "wiki.sipeed.com", + "site_protocol": "https", + "config_template_dir": "config", + "source": "https://github.com/sipeed/MaixPy/blob/main/docs", + "route": { + "docs": { + "/api/": "api", + "/doc/zh/": "doc/zh" + }, + "pages": { + "/": "pages/index" + }, + "assets": { + "/static/": "static", + "/doc/assets/": "doc/assets" + } + }, + "translate": { + "docs": { + "/doc/zh/": [ + { + "url": "/doc/en/", + "src": "doc/en" + } + ] + }, + "pages": { + "/": [ + { + "url": "/en/", + "src": "pages/index_en" + } + ] + } + }, + "plugins": { + "teedoc-plugin-markdown-parser":{ + "from": "pypi", + "config": { + } + }, + "teedoc-plugin-theme-default":{ + "from": "pypi", + "config": { + "dark": true, + "show_print_page": false, + "env":{ + "main_color": "#c33d45" + } + } + }, + "teedoc-plugin-search":{ + "from": "pypi", + "config": { + "search_hint": "Search" + } + }, + "teedoc-plugin-assets": { + "from": "pypi", + "config": { + "header_items": [ + "/static/css/custom.css" + ], + "footer_items": [ + "/static/js/custom.js" + ], + "env":{ + "main_color": "#c33d45", + "second_color": "#922f36" + } + } + }, + "teedoc-plugin-baidu-tongji":{ + "from": "pypi", + "config": { + "code": "9cb07365544a53067c56c346c838181a" + } + }, + "teedoc-plugin-thumbs-up": { + "from": "pypi", + "config": { + "url": "https://thumbs-up.sipeed.com", + "show_up_count": true, + "show_down_count": false + } + } + }, + "rebuild_changes_delay": 1, + "robots":{ + "User-agent": "*" + }, + "layout_root_dir": "layout" +} diff --git a/tools/maix_module2/docs/static/css/custom.css b/tools/maix_module2/docs/static/css/custom.css new file mode 100644 index 00000000..db773d3b --- /dev/null +++ b/tools/maix_module2/docs/static/css/custom.css @@ -0,0 +1,15 @@ + + +#home_page h1 { + color: #c33d45; +} +.dark #home_page h1{ + color: white; +} + +@media screen and (max-width: 900px) { + #home_page h1 { + color: #eb4848; + } +} + diff --git a/tools/maix_module2/docs/static/css/tailwind.css b/tools/maix_module2/docs/static/css/tailwind.css new file mode 100644 index 00000000..d705193e --- /dev/null +++ b/tools/maix_module2/docs/static/css/tailwind.css @@ -0,0 +1,63 @@ +(()=>{var Sb=Object.create;var li=Object.defineProperty;var Cb=Object.getOwnPropertyDescriptor;var Ab=Object.getOwnPropertyNames;var _b=Object.getPrototypeOf,Eb=Object.prototype.hasOwnProperty;var uu=i=>li(i,"__esModule",{value:!0});var fu=i=>{if(typeof require!="undefined")return require(i);throw new Error('Dynamic require of "'+i+'" is not supported')};var C=(i,e)=>()=>(i&&(e=i(i=0)),e);var v=(i,e)=>()=>(e||i((e={exports:{}}).exports,e),e.exports),Ae=(i,e)=>{uu(i);for(var t in e)li(i,t,{get:e[t],enumerable:!0})},Ob=(i,e,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Ab(e))!Eb.call(i,r)&&r!=="default"&&li(i,r,{get:()=>e[r],enumerable:!(t=Cb(e,r))||t.enumerable});return i},K=i=>Ob(uu(li(i!=null?Sb(_b(i)):{},"default",i&&i.__esModule&&"default"in i?{get:()=>i.default,enumerable:!0}:{value:i,enumerable:!0})),i);var m,l=C(()=>{m={platform:"",env:{},versions:{node:"14.17.6"}}});var Tb,te,ze=C(()=>{l();Tb=0,te={readFileSync:i=>self[i]||"",statSync:()=>({mtimeMs:Tb++}),promises:{readFile:i=>Promise.resolve(self[i]||"")}}});var Xn=v((X5,pu)=>{l();"use strict";var cu=class{constructor(e={}){if(!(e.maxSize&&e.maxSize>0))throw new TypeError("`maxSize` must be a number greater than 0");if(typeof e.maxAge=="number"&&e.maxAge===0)throw new TypeError("`maxAge` must be a number greater than 0");this.maxSize=e.maxSize,this.maxAge=e.maxAge||1/0,this.onEviction=e.onEviction,this.cache=new Map,this.oldCache=new Map,this._size=0}_emitEvictions(e){if(typeof this.onEviction=="function")for(let[t,r]of e)this.onEviction(t,r.value)}_deleteIfExpired(e,t){return typeof t.expiry=="number"&&t.expiry<=Date.now()?(typeof this.onEviction=="function"&&this.onEviction(e,t.value),this.delete(e)):!1}_getOrDeleteIfExpired(e,t){if(this._deleteIfExpired(e,t)===!1)return t.value}_getItemValue(e,t){return t.expiry?this._getOrDeleteIfExpired(e,t):t.value}_peek(e,t){let r=t.get(e);return this._getItemValue(e,r)}_set(e,t){this.cache.set(e,t),this._size++,this._size>=this.maxSize&&(this._size=0,this._emitEvictions(this.oldCache),this.oldCache=this.cache,this.cache=new Map)}_moveToRecent(e,t){this.oldCache.delete(e),this._set(e,t)}*_entriesAscending(){for(let e of this.oldCache){let[t,r]=e;this.cache.has(t)||this._deleteIfExpired(t,r)===!1&&(yield e)}for(let e of this.cache){let[t,r]=e;this._deleteIfExpired(t,r)===!1&&(yield e)}}get(e){if(this.cache.has(e)){let t=this.cache.get(e);return this._getItemValue(e,t)}if(this.oldCache.has(e)){let t=this.oldCache.get(e);if(this._deleteIfExpired(e,t)===!1)return this._moveToRecent(e,t),t.value}}set(e,t,{maxAge:r=this.maxAge===1/0?void 0:Date.now()+this.maxAge}={}){this.cache.has(e)?this.cache.set(e,{value:t,maxAge:r}):this._set(e,{value:t,expiry:r})}has(e){return this.cache.has(e)?!this._deleteIfExpired(e,this.cache.get(e)):this.oldCache.has(e)?!this._deleteIfExpired(e,this.oldCache.get(e)):!1}peek(e){if(this.cache.has(e))return this._peek(e,this.cache);if(this.oldCache.has(e))return this._peek(e,this.oldCache)}delete(e){let t=this.cache.delete(e);return t&&this._size--,this.oldCache.delete(e)||t}clear(){this.cache.clear(),this.oldCache.clear(),this._size=0}resize(e){if(!(e&&e>0))throw new TypeError("`maxSize` must be a number greater than 0");let t=[...this._entriesAscending()],r=t.length-e;r<0?(this.cache=new Map(t),this.oldCache=new Map,this._size=t.length):(r>0&&this._emitEvictions(t.slice(0,r)),this.oldCache=new Map(t.slice(r)),this.cache=new Map,this._size=0),this.maxSize=e}*keys(){for(let[e]of this)yield e}*values(){for(let[,e]of this)yield e}*[Symbol.iterator](){for(let e of this.cache){let[t,r]=e;this._deleteIfExpired(t,r)===!1&&(yield[t,r.value])}for(let e of this.oldCache){let[t,r]=e;this.cache.has(t)||this._deleteIfExpired(t,r)===!1&&(yield[t,r.value])}}*entriesDescending(){let e=[...this.cache];for(let t=e.length-1;t>=0;--t){let r=e[t],[n,a]=r;this._deleteIfExpired(n,a)===!1&&(yield[n,a.value])}e=[...this.oldCache];for(let t=e.length-1;t>=0;--t){let r=e[t],[n,a]=r;this.cache.has(n)||this._deleteIfExpired(n,a)===!1&&(yield[n,a.value])}}*entriesAscending(){for(let[e,t]of this._entriesAscending())yield[e,t.value]}get size(){if(!this._size)return this.oldCache.size;let e=0;for(let t of this.oldCache.keys())this.cache.has(t)||e++;return Math.min(this._size+e,this.maxSize)}};pu.exports=cu});var du,hu=C(()=>{l();du=i=>i&&i._hash});function ui(i){return du(i,{ignoreUnknown:!0})}var mu=C(()=>{l();hu()});function Xe(i){if(i=`${i}`,i==="0")return"0";if(/^[+-]?(\d+|\d*\.\d+)(e[+-]?\d+)?(%|\w+)?$/.test(i))return i.replace(/^[+-]?/,t=>t==="-"?"":"-");let e=["var","calc","min","max","clamp"];for(let t of e)if(i.includes(`${t}(`))return`calc(${i} * -1)`}var fi=C(()=>{l()});var gu,yu=C(()=>{l();gu=["preflight","container","accessibility","pointerEvents","visibility","position","inset","isolation","zIndex","order","gridColumn","gridColumnStart","gridColumnEnd","gridRow","gridRowStart","gridRowEnd","float","clear","margin","boxSizing","lineClamp","display","aspectRatio","height","maxHeight","minHeight","width","minWidth","maxWidth","flex","flexShrink","flexGrow","flexBasis","tableLayout","captionSide","borderCollapse","borderSpacing","transformOrigin","translate","rotate","skew","scale","transform","animation","cursor","touchAction","userSelect","resize","scrollSnapType","scrollSnapAlign","scrollSnapStop","scrollMargin","scrollPadding","listStylePosition","listStyleType","listStyleImage","appearance","columns","breakBefore","breakInside","breakAfter","gridAutoColumns","gridAutoFlow","gridAutoRows","gridTemplateColumns","gridTemplateRows","flexDirection","flexWrap","placeContent","placeItems","alignContent","alignItems","justifyContent","justifyItems","gap","space","divideWidth","divideStyle","divideColor","divideOpacity","placeSelf","alignSelf","justifySelf","overflow","overscrollBehavior","scrollBehavior","textOverflow","hyphens","whitespace","wordBreak","borderRadius","borderWidth","borderStyle","borderColor","borderOpacity","backgroundColor","backgroundOpacity","backgroundImage","gradientColorStops","boxDecorationBreak","backgroundSize","backgroundAttachment","backgroundClip","backgroundPosition","backgroundRepeat","backgroundOrigin","fill","stroke","strokeWidth","objectFit","objectPosition","padding","textAlign","textIndent","verticalAlign","fontFamily","fontSize","fontWeight","textTransform","fontStyle","fontVariantNumeric","lineHeight","letterSpacing","textColor","textOpacity","textDecoration","textDecorationColor","textDecorationStyle","textDecorationThickness","textUnderlineOffset","fontSmoothing","placeholderColor","placeholderOpacity","caretColor","accentColor","opacity","backgroundBlendMode","mixBlendMode","boxShadow","boxShadowColor","outlineStyle","outlineWidth","outlineOffset","outlineColor","ringWidth","ringColor","ringOpacity","ringOffsetWidth","ringOffsetColor","blur","brightness","contrast","dropShadow","grayscale","hueRotate","invert","saturate","sepia","filter","backdropBlur","backdropBrightness","backdropContrast","backdropGrayscale","backdropHueRotate","backdropInvert","backdropOpacity","backdropSaturate","backdropSepia","backdropFilter","transitionProperty","transitionDelay","transitionDuration","transitionTimingFunction","willChange","content"]});function wu(i,e){return i===void 0?e:Array.isArray(i)?i:[...new Set(e.filter(r=>i!==!1&&i[r]!==!1).concat(Object.keys(i).filter(r=>i[r]!==!1)))]}var bu=C(()=>{l()});var vu={};Ae(vu,{default:()=>_e});var _e,ci=C(()=>{l();_e=new Proxy({},{get:()=>String})});function Kn(i,e,t){typeof m!="undefined"&&m.env.JEST_WORKER_ID||t&&xu.has(t)||(t&&xu.add(t),console.warn(""),e.forEach(r=>console.warn(i,"-",r)))}function Zn(i){return _e.dim(i)}var xu,F,Ee=C(()=>{l();ci();xu=new Set;F={info(i,e){Kn(_e.bold(_e.cyan("info")),...Array.isArray(i)?[i]:[e,i])},warn(i,e){["content-problems"].includes(i)||Kn(_e.bold(_e.yellow("warn")),...Array.isArray(i)?[i]:[e,i])},risk(i,e){Kn(_e.bold(_e.magenta("risk")),...Array.isArray(i)?[i]:[e,i])}}});var ku={};Ae(ku,{default:()=>es});function nr({version:i,from:e,to:t}){F.warn(`${e}-color-renamed`,[`As of Tailwind CSS ${i}, \`${e}\` has been renamed to \`${t}\`.`,"Update your configuration file to silence this warning."])}var es,ts=C(()=>{l();Ee();es={inherit:"inherit",current:"currentColor",transparent:"transparent",black:"#000",white:"#fff",slate:{50:"#f8fafc",100:"#f1f5f9",200:"#e2e8f0",300:"#cbd5e1",400:"#94a3b8",500:"#64748b",600:"#475569",700:"#334155",800:"#1e293b",900:"#0f172a",950:"#020617"},gray:{50:"#f9fafb",100:"#f3f4f6",200:"#e5e7eb",300:"#d1d5db",400:"#9ca3af",500:"#6b7280",600:"#4b5563",700:"#374151",800:"#1f2937",900:"#111827",950:"#030712"},zinc:{50:"#fafafa",100:"#f4f4f5",200:"#e4e4e7",300:"#d4d4d8",400:"#a1a1aa",500:"#71717a",600:"#52525b",700:"#3f3f46",800:"#27272a",900:"#18181b",950:"#09090b"},neutral:{50:"#fafafa",100:"#f5f5f5",200:"#e5e5e5",300:"#d4d4d4",400:"#a3a3a3",500:"#737373",600:"#525252",700:"#404040",800:"#262626",900:"#171717",950:"#0a0a0a"},stone:{50:"#fafaf9",100:"#f5f5f4",200:"#e7e5e4",300:"#d6d3d1",400:"#a8a29e",500:"#78716c",600:"#57534e",700:"#44403c",800:"#292524",900:"#1c1917",950:"#0c0a09"},red:{50:"#fef2f2",100:"#fee2e2",200:"#fecaca",300:"#fca5a5",400:"#f87171",500:"#ef4444",600:"#dc2626",700:"#b91c1c",800:"#991b1b",900:"#7f1d1d",950:"#450a0a"},orange:{50:"#fff7ed",100:"#ffedd5",200:"#fed7aa",300:"#fdba74",400:"#fb923c",500:"#f97316",600:"#ea580c",700:"#c2410c",800:"#9a3412",900:"#7c2d12",950:"#431407"},amber:{50:"#fffbeb",100:"#fef3c7",200:"#fde68a",300:"#fcd34d",400:"#fbbf24",500:"#f59e0b",600:"#d97706",700:"#b45309",800:"#92400e",900:"#78350f",950:"#451a03"},yellow:{50:"#fefce8",100:"#fef9c3",200:"#fef08a",300:"#fde047",400:"#facc15",500:"#eab308",600:"#ca8a04",700:"#a16207",800:"#854d0e",900:"#713f12",950:"#422006"},lime:{50:"#f7fee7",100:"#ecfccb",200:"#d9f99d",300:"#bef264",400:"#a3e635",500:"#84cc16",600:"#65a30d",700:"#4d7c0f",800:"#3f6212",900:"#365314",950:"#1a2e05"},green:{50:"#f0fdf4",100:"#dcfce7",200:"#bbf7d0",300:"#86efac",400:"#4ade80",500:"#22c55e",600:"#16a34a",700:"#15803d",800:"#166534",900:"#14532d",950:"#052e16"},emerald:{50:"#ecfdf5",100:"#d1fae5",200:"#a7f3d0",300:"#6ee7b7",400:"#34d399",500:"#10b981",600:"#059669",700:"#047857",800:"#065f46",900:"#064e3b",950:"#022c22"},teal:{50:"#f0fdfa",100:"#ccfbf1",200:"#99f6e4",300:"#5eead4",400:"#2dd4bf",500:"#14b8a6",600:"#0d9488",700:"#0f766e",800:"#115e59",900:"#134e4a",950:"#042f2e"},cyan:{50:"#ecfeff",100:"#cffafe",200:"#a5f3fc",300:"#67e8f9",400:"#22d3ee",500:"#06b6d4",600:"#0891b2",700:"#0e7490",800:"#155e75",900:"#164e63",950:"#083344"},sky:{50:"#f0f9ff",100:"#e0f2fe",200:"#bae6fd",300:"#7dd3fc",400:"#38bdf8",500:"#0ea5e9",600:"#0284c7",700:"#0369a1",800:"#075985",900:"#0c4a6e",950:"#082f49"},blue:{50:"#eff6ff",100:"#dbeafe",200:"#bfdbfe",300:"#93c5fd",400:"#60a5fa",500:"#3b82f6",600:"#2563eb",700:"#1d4ed8",800:"#1e40af",900:"#1e3a8a",950:"#172554"},indigo:{50:"#eef2ff",100:"#e0e7ff",200:"#c7d2fe",300:"#a5b4fc",400:"#818cf8",500:"#6366f1",600:"#4f46e5",700:"#4338ca",800:"#3730a3",900:"#312e81",950:"#1e1b4b"},violet:{50:"#f5f3ff",100:"#ede9fe",200:"#ddd6fe",300:"#c4b5fd",400:"#a78bfa",500:"#8b5cf6",600:"#7c3aed",700:"#6d28d9",800:"#5b21b6",900:"#4c1d95",950:"#2e1065"},purple:{50:"#faf5ff",100:"#f3e8ff",200:"#e9d5ff",300:"#d8b4fe",400:"#c084fc",500:"#a855f7",600:"#9333ea",700:"#7e22ce",800:"#6b21a8",900:"#581c87",950:"#3b0764"},fuchsia:{50:"#fdf4ff",100:"#fae8ff",200:"#f5d0fe",300:"#f0abfc",400:"#e879f9",500:"#d946ef",600:"#c026d3",700:"#a21caf",800:"#86198f",900:"#701a75",950:"#4a044e"},pink:{50:"#fdf2f8",100:"#fce7f3",200:"#fbcfe8",300:"#f9a8d4",400:"#f472b6",500:"#ec4899",600:"#db2777",700:"#be185d",800:"#9d174d",900:"#831843",950:"#500724"},rose:{50:"#fff1f2",100:"#ffe4e6",200:"#fecdd3",300:"#fda4af",400:"#fb7185",500:"#f43f5e",600:"#e11d48",700:"#be123c",800:"#9f1239",900:"#881337",950:"#4c0519"},get lightBlue(){return nr({version:"v2.2",from:"lightBlue",to:"sky"}),this.sky},get warmGray(){return nr({version:"v3.0",from:"warmGray",to:"stone"}),this.stone},get trueGray(){return nr({version:"v3.0",from:"trueGray",to:"neutral"}),this.neutral},get coolGray(){return nr({version:"v3.0",from:"coolGray",to:"gray"}),this.gray},get blueGray(){return nr({version:"v3.0",from:"blueGray",to:"slate"}),this.slate}}});function rs(i,...e){for(let t of e){for(let r in t)i?.hasOwnProperty?.(r)||(i[r]=t[r]);for(let r of Object.getOwnPropertySymbols(t))i?.hasOwnProperty?.(r)||(i[r]=t[r])}return i}var Su=C(()=>{l()});function Ke(i){if(Array.isArray(i))return i;let e=i.split("[").length-1,t=i.split("]").length-1;if(e!==t)throw new Error(`Path is invalid. Has unbalanced brackets: ${i}`);return i.split(/\.(?![^\[]*\])|[\[\]]/g).filter(Boolean)}var pi=C(()=>{l()});function J(i,e){return di.future.includes(e)?i.future==="all"||(i?.future?.[e]??Cu[e]??!1):di.experimental.includes(e)?i.experimental==="all"||(i?.experimental?.[e]??Cu[e]??!1):!1}function Au(i){return i.experimental==="all"?di.experimental:Object.keys(i?.experimental??{}).filter(e=>di.experimental.includes(e)&&i.experimental[e])}function _u(i){if(m.env.JEST_WORKER_ID===void 0&&Au(i).length>0){let e=Au(i).map(t=>_e.yellow(t)).join(", ");F.warn("experimental-flags-enabled",[`You have enabled experimental features: ${e}`,"Experimental features in Tailwind CSS are not covered by semver, may introduce breaking changes, and can change at any time."])}}var Cu,di,De=C(()=>{l();ci();Ee();Cu={optimizeUniversalDefaults:!1,generalizedModifiers:!0,get disableColorOpacityUtilitiesByDefault(){return!1},get relativeContentPathsByDefault(){return!1}},di={future:["hoverOnlyWhenSupported","respectDefaultRingColorOpacity","disableColorOpacityUtilitiesByDefault","relativeContentPathsByDefault"],experimental:["optimizeUniversalDefaults","generalizedModifiers"]}});function Eu(i){(()=>{if(i.purge||!i.content||!Array.isArray(i.content)&&!(typeof i.content=="object"&&i.content!==null))return!1;if(Array.isArray(i.content))return i.content.every(t=>typeof t=="string"?!0:!(typeof t?.raw!="string"||t?.extension&&typeof t?.extension!="string"));if(typeof i.content=="object"&&i.content!==null){if(Object.keys(i.content).some(t=>!["files","relative","extract","transform"].includes(t)))return!1;if(Array.isArray(i.content.files)){if(!i.content.files.every(t=>typeof t=="string"?!0:!(typeof t?.raw!="string"||t?.extension&&typeof t?.extension!="string")))return!1;if(typeof i.content.extract=="object"){for(let t of Object.values(i.content.extract))if(typeof t!="function")return!1}else if(!(i.content.extract===void 0||typeof i.content.extract=="function"))return!1;if(typeof i.content.transform=="object"){for(let t of Object.values(i.content.transform))if(typeof t!="function")return!1}else if(!(i.content.transform===void 0||typeof i.content.transform=="function"))return!1;if(typeof i.content.relative!="boolean"&&typeof i.content.relative!="undefined")return!1}return!0}return!1})()||F.warn("purge-deprecation",["The `purge`/`content` options have changed in Tailwind CSS v3.0.","Update your configuration file to eliminate this warning.","https://tailwindcss.com/docs/upgrade-guide#configure-content-sources"]),i.safelist=(()=>{let{content:t,purge:r,safelist:n}=i;return Array.isArray(n)?n:Array.isArray(t?.safelist)?t.safelist:Array.isArray(r?.safelist)?r.safelist:Array.isArray(r?.options?.safelist)?r.options.safelist:[]})(),i.blocklist=(()=>{let{blocklist:t}=i;if(Array.isArray(t)){if(t.every(r=>typeof r=="string"))return t;F.warn("blocklist-invalid",["The `blocklist` option must be an array of strings.","https://tailwindcss.com/docs/content-configuration#discarding-classes"])}return[]})(),typeof i.prefix=="function"?(F.warn("prefix-function",["As of Tailwind CSS v3.0, `prefix` cannot be a function.","Update `prefix` in your configuration to be a string to eliminate this warning.","https://tailwindcss.com/docs/upgrade-guide#prefix-cannot-be-a-function"]),i.prefix=""):i.prefix=i.prefix??"",i.content={relative:(()=>{let{content:t}=i;return t?.relative?t.relative:J(i,"relativeContentPathsByDefault")})(),files:(()=>{let{content:t,purge:r}=i;return Array.isArray(r)?r:Array.isArray(r?.content)?r.content:Array.isArray(t)?t:Array.isArray(t?.content)?t.content:Array.isArray(t?.files)?t.files:[]})(),extract:(()=>{let t=(()=>i.purge?.extract?i.purge.extract:i.content?.extract?i.content.extract:i.purge?.extract?.DEFAULT?i.purge.extract.DEFAULT:i.content?.extract?.DEFAULT?i.content.extract.DEFAULT:i.purge?.options?.extractors?i.purge.options.extractors:i.content?.options?.extractors?i.content.options.extractors:{})(),r={},n=(()=>{if(i.purge?.options?.defaultExtractor)return i.purge.options.defaultExtractor;if(i.content?.options?.defaultExtractor)return i.content.options.defaultExtractor})();if(n!==void 0&&(r.DEFAULT=n),typeof t=="function")r.DEFAULT=t;else if(Array.isArray(t))for(let{extensions:a,extractor:s}of t??[])for(let o of a)r[o]=s;else typeof t=="object"&&t!==null&&Object.assign(r,t);return r})(),transform:(()=>{let t=(()=>i.purge?.transform?i.purge.transform:i.content?.transform?i.content.transform:i.purge?.transform?.DEFAULT?i.purge.transform.DEFAULT:i.content?.transform?.DEFAULT?i.content.transform.DEFAULT:{})(),r={};return typeof t=="function"&&(r.DEFAULT=t),typeof t=="object"&&t!==null&&Object.assign(r,t),r})()};for(let t of i.content.files)if(typeof t=="string"&&/{([^,]*?)}/g.test(t)){F.warn("invalid-glob-braces",[`The glob pattern ${Zn(t)} in your Tailwind CSS configuration is invalid.`,`Update it to ${Zn(t.replace(/{([^,]*?)}/g,"$1"))} to silence this warning.`]);break}return i}var Ou=C(()=>{l();De();Ee()});function ie(i){if(Object.prototype.toString.call(i)!=="[object Object]")return!1;let e=Object.getPrototypeOf(i);return e===null||e===Object.prototype}var xt=C(()=>{l()});function Ze(i){return Array.isArray(i)?i.map(e=>Ze(e)):typeof i=="object"&&i!==null?Object.fromEntries(Object.entries(i).map(([e,t])=>[e,Ze(t)])):i}var hi=C(()=>{l()});function ht(i){return i.replace(/\\,/g,"\\2c ")}var mi=C(()=>{l()});var is,Tu=C(()=>{l();is={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]}});function sr(i,{loose:e=!1}={}){if(typeof i!="string")return null;if(i=i.trim(),i==="transparent")return{mode:"rgb",color:["0","0","0"],alpha:"0"};if(i in is)return{mode:"rgb",color:is[i].map(a=>a.toString())};let t=i.replace(Db,(a,s,o,u,c)=>["#",s,s,o,o,u,u,c?c+c:""].join("")).match(Pb);if(t!==null)return{mode:"rgb",color:[parseInt(t[1],16),parseInt(t[2],16),parseInt(t[3],16)].map(a=>a.toString()),alpha:t[4]?(parseInt(t[4],16)/255).toString():void 0};let r=i.match(Ib)??i.match(qb);if(r===null)return null;let n=[r[2],r[3],r[4]].filter(Boolean).map(a=>a.toString());return n.length===2&&n[0].startsWith("var(")?{mode:r[1],color:[n[0]],alpha:n[1]}:!e&&n.length!==3||n.length<3&&!n.some(a=>/^var\(.*?\)$/.test(a))?null:{mode:r[1],color:n,alpha:r[5]?.toString?.()}}function ns({mode:i,color:e,alpha:t}){let r=t!==void 0;return i==="rgba"||i==="hsla"?`${i}(${e.join(", ")}${r?`, ${t}`:""})`:`${i}(${e.join(" ")}${r?` / ${t}`:""})`}var Pb,Db,et,gi,Pu,tt,Ib,qb,ss=C(()=>{l();Tu();Pb=/^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i,Db=/^#([a-f\d])([a-f\d])([a-f\d])([a-f\d])?$/i,et=/(?:\d+|\d*\.\d+)%?/,gi=/(?:\s*,\s*|\s+)/,Pu=/\s*[,/]\s*/,tt=/var\(--(?:[^ )]*?)\)/,Ib=new RegExp(`^(rgba?)\\(\\s*(${et.source}|${tt.source})(?:${gi.source}(${et.source}|${tt.source}))?(?:${gi.source}(${et.source}|${tt.source}))?(?:${Pu.source}(${et.source}|${tt.source}))?\\s*\\)$`),qb=new RegExp(`^(hsla?)\\(\\s*((?:${et.source})(?:deg|rad|grad|turn)?|${tt.source})(?:${gi.source}(${et.source}|${tt.source}))?(?:${gi.source}(${et.source}|${tt.source}))?(?:${Pu.source}(${et.source}|${tt.source}))?\\s*\\)$`)});function Ie(i,e,t){if(typeof i=="function")return i({opacityValue:e});let r=sr(i,{loose:!0});return r===null?t:ns({...r,alpha:e})}function se({color:i,property:e,variable:t}){let r=[].concat(e);if(typeof i=="function")return{[t]:"1",...Object.fromEntries(r.map(a=>[a,i({opacityVariable:t,opacityValue:`var(${t})`})]))};let n=sr(i);return n===null?Object.fromEntries(r.map(a=>[a,i])):n.alpha!==void 0?Object.fromEntries(r.map(a=>[a,i])):{[t]:"1",...Object.fromEntries(r.map(a=>[a,ns({...n,alpha:`var(${t})`})]))}}var ar=C(()=>{l();ss()});function le(i,e){let t=[],r=[],n=0,a=!1;for(let s=0;s{l()});function yi(i){return le(i,",").map(t=>{let r=t.trim(),n={raw:r},a=r.split(Mb),s=new Set;for(let o of a)Du.lastIndex=0,!s.has("KEYWORD")&&Rb.has(o)?(n.keyword=o,s.add("KEYWORD")):Du.test(o)?s.has("X")?s.has("Y")?s.has("BLUR")?s.has("SPREAD")||(n.spread=o,s.add("SPREAD")):(n.blur=o,s.add("BLUR")):(n.y=o,s.add("Y")):(n.x=o,s.add("X")):n.color?(n.unknown||(n.unknown=[]),n.unknown.push(o)):n.color=o;return n.valid=n.x!==void 0&&n.y!==void 0,n})}function Iu(i){return i.map(e=>e.valid?[e.keyword,e.x,e.y,e.blur,e.spread,e.color].filter(Boolean).join(" "):e.raw).join(", ")}var Rb,Mb,Du,as=C(()=>{l();or();Rb=new Set(["inset","inherit","initial","revert","unset"]),Mb=/\ +(?![^(]*\))/g,Du=/^-?(\d+|\.\d+)(.*?)$/g});function os(i){return Bb.some(e=>new RegExp(`^${e}\\(.*\\)`).test(i))}function U(i,e=!0){return i.startsWith("--")?`var(${i})`:i.includes("url(")?i.split(/(url\(.*?\))/g).filter(Boolean).map(t=>/^url\(.*?\)$/.test(t)?t:U(t,!1)).join(""):(i=i.replace(/([^\\])_+/g,(t,r)=>r+" ".repeat(t.length-1)).replace(/^_/g," ").replace(/\\_/g,"_"),e&&(i=i.trim()),i=Nb(i),i)}function Nb(i){return i.replace(/(calc|min|max|clamp)\(.+\)/g,e=>{let t=[];return e.replace(/var\((--.+?)[,)]/g,(r,n)=>(t.push(n),r.replace(n,qu))).replace(/(-?\d*\.?\d(?!\b-\d.+[,)](?![^+\-/*])\D)(?:%|[a-z]+)?|\))([+\-/*])/g,"$1 $2 ").replace(Fb,()=>t.shift())})}function ls(i){return i.startsWith("url(")}function us(i){return!isNaN(Number(i))||os(i)}function lr(i){return i.endsWith("%")&&us(i.slice(0,-1))||os(i)}function ur(i){return i==="0"||new RegExp(`^[+-]?[0-9]*.?[0-9]+(?:[eE][+-]?[0-9]+)?${$b}$`).test(i)||os(i)}function Ru(i){return jb.has(i)}function Mu(i){let e=yi(U(i));for(let t of e)if(!t.valid)return!1;return!0}function Bu(i){let e=0;return le(i,"_").every(r=>(r=U(r),r.startsWith("var(")?!0:sr(r,{loose:!0})!==null?(e++,!0):!1))?e>0:!1}function Fu(i){let e=0;return le(i,",").every(r=>(r=U(r),r.startsWith("var(")?!0:ls(r)||Vb(r)||["element(","image(","cross-fade(","image-set("].some(n=>r.startsWith(n))?(e++,!0):!1))?e>0:!1}function Vb(i){i=U(i);for(let e of zb)if(i.startsWith(`${e}(`))return!0;return!1}function Nu(i){let e=0;return le(i,"_").every(r=>(r=U(r),r.startsWith("var(")?!0:Ub.has(r)||ur(r)||lr(r)?(e++,!0):!1))?e>0:!1}function Lu(i){let e=0;return le(i,",").every(r=>(r=U(r),r.startsWith("var(")?!0:r.includes(" ")&&!/(['"])([^"']+)\1/g.test(r)||/^\d/g.test(r)?!1:(e++,!0)))?e>0:!1}function $u(i){return Wb.has(i)}function ju(i){return Gb.has(i)}function zu(i){return Hb.has(i)}var Bb,qu,Fb,Lb,$b,jb,zb,Ub,Wb,Gb,Hb,fr=C(()=>{l();ss();as();or();Bb=["min","max","clamp","calc"];qu="--tw-placeholder",Fb=new RegExp(qu,"g");Lb=["cm","mm","Q","in","pc","pt","px","em","ex","ch","rem","lh","rlh","vw","vh","vmin","vmax","vb","vi","svw","svh","lvw","lvh","dvw","dvh","cqw","cqh","cqi","cqb","cqmin","cqmax"],$b=`(?:${Lb.join("|")})`;jb=new Set(["thin","medium","thick"]);zb=new Set(["conic-gradient","linear-gradient","radial-gradient","repeating-conic-gradient","repeating-linear-gradient","repeating-radial-gradient"]);Ub=new Set(["center","top","right","bottom","left"]);Wb=new Set(["serif","sans-serif","monospace","cursive","fantasy","system-ui","ui-serif","ui-sans-serif","ui-monospace","ui-rounded","math","emoji","fangsong"]);Gb=new Set(["xx-small","x-small","small","medium","large","x-large","x-large","xxx-large"]);Hb=new Set(["larger","smaller"])});function Vu(i){let e=["cover","contain"];return le(i,",").every(t=>{let r=le(t,"_").filter(Boolean);return r.length===1&&e.includes(r[0])?!0:r.length!==1&&r.length!==2?!1:r.every(n=>ur(n)||lr(n)||n==="auto")})}var Uu=C(()=>{l();fr();or()});function Wu(i,e){i.walkClasses(t=>{t.value=e(t.value),t.raws&&t.raws.value&&(t.raws.value=ht(t.raws.value))})}function Gu(i,e){if(!rt(i))return;let t=i.slice(1,-1);if(!!e(t))return U(t)}function Yb(i,e={},t){let r=e[i];if(r!==void 0)return Xe(r);if(rt(i)){let n=Gu(i,t);return n===void 0?void 0:Xe(n)}}function wi(i,e={},{validate:t=()=>!0}={}){let r=e.values?.[i];return r!==void 0?r:e.supportsNegativeValues&&i.startsWith("-")?Yb(i.slice(1),e.values,t):Gu(i,t)}function rt(i){return i.startsWith("[")&&i.endsWith("]")}function Hu(i){let e=i.lastIndexOf("/");return e===-1||e===i.length-1?[i,void 0]:rt(i)&&!i.includes("]/[")?[i,void 0]:[i.slice(0,e),i.slice(e+1)]}function kt(i){if(typeof i=="string"&&i.includes("")){let e=i;return({opacityValue:t=1})=>e.replace("",t)}return i}function Yu(i){return U(i.slice(1,-1))}function Qb(i,e={},{tailwindConfig:t={}}={}){if(e.values?.[i]!==void 0)return kt(e.values?.[i]);let[r,n]=Hu(i);if(n!==void 0){let a=e.values?.[r]??(rt(r)?r.slice(1,-1):void 0);return a===void 0?void 0:(a=kt(a),rt(n)?Ie(a,Yu(n)):t.theme?.opacity?.[n]===void 0?void 0:Ie(a,t.theme.opacity[n]))}return wi(i,e,{validate:Bu})}function Jb(i,e={}){return e.values?.[i]}function he(i){return(e,t)=>wi(e,t,{validate:i})}function Xb(i,e){let t=i.indexOf(e);return t===-1?[void 0,i]:[i.slice(0,t),i.slice(t+1)]}function cs(i,e,t,r){if(t.values&&e in t.values)for(let{type:a}of i??[]){let s=fs[a](e,t,{tailwindConfig:r});if(s!==void 0)return[s,a,null]}if(rt(e)){let a=e.slice(1,-1),[s,o]=Xb(a,":");if(!/^[\w-_]+$/g.test(s))o=a;else if(s!==void 0&&!Qu.includes(s))return[];if(o.length>0&&Qu.includes(s))return[wi(`[${o}]`,t),s,null]}let n=ps(i,e,t,r);for(let a of n)return a;return[]}function*ps(i,e,t,r){let n=J(r,"generalizedModifiers"),[a,s]=Hu(e);if(n&&t.modifiers!=null&&(t.modifiers==="any"||typeof t.modifiers=="object"&&(s&&rt(s)||s in t.modifiers))||(a=e,s=void 0),s!==void 0&&a===""&&(a="DEFAULT"),s!==void 0&&typeof t.modifiers=="object"){let u=t.modifiers?.[s]??null;u!==null?s=u:rt(s)&&(s=Yu(s))}for(let{type:u}of i??[]){let c=fs[u](a,t,{tailwindConfig:r});c!==void 0&&(yield[c,u,s??null])}}var fs,Qu,cr=C(()=>{l();mi();ar();fr();fi();Uu();De();fs={any:wi,color:Qb,url:he(ls),image:he(Fu),length:he(ur),percentage:he(lr),position:he(Nu),lookup:Jb,"generic-name":he($u),"family-name":he(Lu),number:he(us),"line-width":he(Ru),"absolute-size":he(ju),"relative-size":he(zu),shadow:he(Mu),size:he(Vu)},Qu=Object.keys(fs)});function N(i){return typeof i=="function"?i({}):i}var ds=C(()=>{l()});function St(i){return typeof i=="function"}function pr(i,...e){let t=e.pop();for(let r of e)for(let n in r){let a=t(i[n],r[n]);a===void 0?ie(i[n])&&ie(r[n])?i[n]=pr({},i[n],r[n],t):i[n]=r[n]:i[n]=a}return i}function Kb(i,...e){return St(i)?i(...e):i}function Zb(i){return i.reduce((e,{extend:t})=>pr(e,t,(r,n)=>r===void 0?[n]:Array.isArray(r)?[n,...r]:[n,r]),{})}function e0(i){return{...i.reduce((e,t)=>rs(e,t),{}),extend:Zb(i)}}function Ju(i,e){if(Array.isArray(i)&&ie(i[0]))return i.concat(e);if(Array.isArray(e)&&ie(e[0])&&ie(i))return[i,...e];if(Array.isArray(e))return e}function t0({extend:i,...e}){return pr(e,i,(t,r)=>!St(t)&&!r.some(St)?pr({},t,...r,Ju):(n,a)=>pr({},...[t,...r].map(s=>Kb(s,n,a)),Ju))}function*r0(i){let e=Ke(i);if(e.length===0||(yield e,Array.isArray(i)))return;let t=/^(.*?)\s*\/\s*([^/]+)$/,r=i.match(t);if(r!==null){let[,n,a]=r,s=Ke(n);s.alpha=a,yield s}}function i0(i){let e=(t,r)=>{for(let n of r0(t)){let a=0,s=i;for(;s!=null&&a(t[r]=St(i[r])?i[r](e,hs):i[r],t),{})}function Xu(i){let e=[];return i.forEach(t=>{e=[...e,t];let r=t?.plugins??[];r.length!==0&&r.forEach(n=>{n.__isOptionsFunction&&(n=n()),e=[...e,...Xu([n?.config??{}])]})}),e}function n0(i){return[...i].reduceRight((t,r)=>St(r)?r({corePlugins:t}):wu(r,t),gu)}function s0(i){return[...i].reduceRight((t,r)=>[...t,...r],[])}function ms(i){let e=[...Xu(i),{prefix:"",important:!1,separator:":"}];return Eu(rs({theme:i0(t0(e0(e.map(t=>t?.theme??{})))),corePlugins:n0(e.map(t=>t.corePlugins)),plugins:s0(i.map(t=>t?.plugins??[]))},...e))}var hs,Ku=C(()=>{l();fi();yu();bu();ts();Su();pi();Ou();xt();hi();cr();ar();ds();hs={colors:es,negative(i){return Object.keys(i).filter(e=>i[e]!=="0").reduce((e,t)=>{let r=Xe(i[t]);return r!==void 0&&(e[`-${t}`]=r),e},{})},breakpoints(i){return Object.keys(i).filter(e=>typeof i[e]=="string").reduce((e,t)=>({...e,[`screen-${t}`]:i[t]}),{})}}});var bi=v((eT,Zu)=>{l();Zu.exports={content:[],presets:[],darkMode:"media",theme:{accentColor:({theme:i})=>({...i("colors"),auto:"auto"}),animation:{none:"none",spin:"spin 1s linear infinite",ping:"ping 1s cubic-bezier(0, 0, 0.2, 1) infinite",pulse:"pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",bounce:"bounce 1s infinite"},aria:{busy:'busy="true"',checked:'checked="true"',disabled:'disabled="true"',expanded:'expanded="true"',hidden:'hidden="true"',pressed:'pressed="true"',readonly:'readonly="true"',required:'required="true"',selected:'selected="true"'},aspectRatio:{auto:"auto",square:"1 / 1",video:"16 / 9"},backdropBlur:({theme:i})=>i("blur"),backdropBrightness:({theme:i})=>i("brightness"),backdropContrast:({theme:i})=>i("contrast"),backdropGrayscale:({theme:i})=>i("grayscale"),backdropHueRotate:({theme:i})=>i("hueRotate"),backdropInvert:({theme:i})=>i("invert"),backdropOpacity:({theme:i})=>i("opacity"),backdropSaturate:({theme:i})=>i("saturate"),backdropSepia:({theme:i})=>i("sepia"),backgroundColor:({theme:i})=>i("colors"),backgroundImage:{none:"none","gradient-to-t":"linear-gradient(to top, var(--tw-gradient-stops))","gradient-to-tr":"linear-gradient(to top right, var(--tw-gradient-stops))","gradient-to-r":"linear-gradient(to right, var(--tw-gradient-stops))","gradient-to-br":"linear-gradient(to bottom right, var(--tw-gradient-stops))","gradient-to-b":"linear-gradient(to bottom, var(--tw-gradient-stops))","gradient-to-bl":"linear-gradient(to bottom left, var(--tw-gradient-stops))","gradient-to-l":"linear-gradient(to left, var(--tw-gradient-stops))","gradient-to-tl":"linear-gradient(to top left, var(--tw-gradient-stops))"},backgroundOpacity:({theme:i})=>i("opacity"),backgroundPosition:{bottom:"bottom",center:"center",left:"left","left-bottom":"left bottom","left-top":"left top",right:"right","right-bottom":"right bottom","right-top":"right top",top:"top"},backgroundSize:{auto:"auto",cover:"cover",contain:"contain"},blur:{0:"0",none:"0",sm:"4px",DEFAULT:"8px",md:"12px",lg:"16px",xl:"24px","2xl":"40px","3xl":"64px"},borderColor:({theme:i})=>({...i("colors"),DEFAULT:i("colors.gray.200","currentColor")}),borderOpacity:({theme:i})=>i("opacity"),borderRadius:{none:"0px",sm:"0.125rem",DEFAULT:"0.25rem",md:"0.375rem",lg:"0.5rem",xl:"0.75rem","2xl":"1rem","3xl":"1.5rem",full:"9999px"},borderSpacing:({theme:i})=>({...i("spacing")}),borderWidth:{DEFAULT:"1px",0:"0px",2:"2px",4:"4px",8:"8px"},boxShadow:{sm:"0 1px 2px 0 rgb(0 0 0 / 0.05)",DEFAULT:"0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)",md:"0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",lg:"0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)",xl:"0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)","2xl":"0 25px 50px -12px rgb(0 0 0 / 0.25)",inner:"inset 0 2px 4px 0 rgb(0 0 0 / 0.05)",none:"none"},boxShadowColor:({theme:i})=>i("colors"),brightness:{0:"0",50:".5",75:".75",90:".9",95:".95",100:"1",105:"1.05",110:"1.1",125:"1.25",150:"1.5",200:"2"},caretColor:({theme:i})=>i("colors"),colors:({colors:i})=>({inherit:i.inherit,current:i.current,transparent:i.transparent,black:i.black,white:i.white,slate:i.slate,gray:i.gray,zinc:i.zinc,neutral:i.neutral,stone:i.stone,red:i.red,orange:i.orange,amber:i.amber,yellow:i.yellow,lime:i.lime,green:i.green,emerald:i.emerald,teal:i.teal,cyan:i.cyan,sky:i.sky,blue:i.blue,indigo:i.indigo,violet:i.violet,purple:i.purple,fuchsia:i.fuchsia,pink:i.pink,rose:i.rose}),columns:{auto:"auto",1:"1",2:"2",3:"3",4:"4",5:"5",6:"6",7:"7",8:"8",9:"9",10:"10",11:"11",12:"12","3xs":"16rem","2xs":"18rem",xs:"20rem",sm:"24rem",md:"28rem",lg:"32rem",xl:"36rem","2xl":"42rem","3xl":"48rem","4xl":"56rem","5xl":"64rem","6xl":"72rem","7xl":"80rem"},container:{},content:{none:"none"},contrast:{0:"0",50:".5",75:".75",100:"1",125:"1.25",150:"1.5",200:"2"},cursor:{auto:"auto",default:"default",pointer:"pointer",wait:"wait",text:"text",move:"move",help:"help","not-allowed":"not-allowed",none:"none","context-menu":"context-menu",progress:"progress",cell:"cell",crosshair:"crosshair","vertical-text":"vertical-text",alias:"alias",copy:"copy","no-drop":"no-drop",grab:"grab",grabbing:"grabbing","all-scroll":"all-scroll","col-resize":"col-resize","row-resize":"row-resize","n-resize":"n-resize","e-resize":"e-resize","s-resize":"s-resize","w-resize":"w-resize","ne-resize":"ne-resize","nw-resize":"nw-resize","se-resize":"se-resize","sw-resize":"sw-resize","ew-resize":"ew-resize","ns-resize":"ns-resize","nesw-resize":"nesw-resize","nwse-resize":"nwse-resize","zoom-in":"zoom-in","zoom-out":"zoom-out"},divideColor:({theme:i})=>i("borderColor"),divideOpacity:({theme:i})=>i("borderOpacity"),divideWidth:({theme:i})=>i("borderWidth"),dropShadow:{sm:"0 1px 1px rgb(0 0 0 / 0.05)",DEFAULT:["0 1px 2px rgb(0 0 0 / 0.1)","0 1px 1px rgb(0 0 0 / 0.06)"],md:["0 4px 3px rgb(0 0 0 / 0.07)","0 2px 2px rgb(0 0 0 / 0.06)"],lg:["0 10px 8px rgb(0 0 0 / 0.04)","0 4px 3px rgb(0 0 0 / 0.1)"],xl:["0 20px 13px rgb(0 0 0 / 0.03)","0 8px 5px rgb(0 0 0 / 0.08)"],"2xl":"0 25px 25px rgb(0 0 0 / 0.15)",none:"0 0 #0000"},fill:({theme:i})=>({none:"none",...i("colors")}),flex:{1:"1 1 0%",auto:"1 1 auto",initial:"0 1 auto",none:"none"},flexBasis:({theme:i})=>({auto:"auto",...i("spacing"),"1/2":"50%","1/3":"33.333333%","2/3":"66.666667%","1/4":"25%","2/4":"50%","3/4":"75%","1/5":"20%","2/5":"40%","3/5":"60%","4/5":"80%","1/6":"16.666667%","2/6":"33.333333%","3/6":"50%","4/6":"66.666667%","5/6":"83.333333%","1/12":"8.333333%","2/12":"16.666667%","3/12":"25%","4/12":"33.333333%","5/12":"41.666667%","6/12":"50%","7/12":"58.333333%","8/12":"66.666667%","9/12":"75%","10/12":"83.333333%","11/12":"91.666667%",full:"100%"}),flexGrow:{0:"0",DEFAULT:"1"},flexShrink:{0:"0",DEFAULT:"1"},fontFamily:{sans:["ui-sans-serif","system-ui","-apple-system","BlinkMacSystemFont",'"Segoe UI"',"Roboto",'"Helvetica Neue"',"Arial",'"Noto Sans"',"sans-serif",'"Apple Color Emoji"','"Segoe UI Emoji"','"Segoe UI Symbol"','"Noto Color Emoji"'],serif:["ui-serif","Georgia","Cambria",'"Times New Roman"',"Times","serif"],mono:["ui-monospace","SFMono-Regular","Menlo","Monaco","Consolas",'"Liberation Mono"','"Courier New"',"monospace"]},fontSize:{xs:["0.75rem",{lineHeight:"1rem"}],sm:["0.875rem",{lineHeight:"1.25rem"}],base:["1rem",{lineHeight:"1.5rem"}],lg:["1.125rem",{lineHeight:"1.75rem"}],xl:["1.25rem",{lineHeight:"1.75rem"}],"2xl":["1.5rem",{lineHeight:"2rem"}],"3xl":["1.875rem",{lineHeight:"2.25rem"}],"4xl":["2.25rem",{lineHeight:"2.5rem"}],"5xl":["3rem",{lineHeight:"1"}],"6xl":["3.75rem",{lineHeight:"1"}],"7xl":["4.5rem",{lineHeight:"1"}],"8xl":["6rem",{lineHeight:"1"}],"9xl":["8rem",{lineHeight:"1"}]},fontWeight:{thin:"100",extralight:"200",light:"300",normal:"400",medium:"500",semibold:"600",bold:"700",extrabold:"800",black:"900"},gap:({theme:i})=>i("spacing"),gradientColorStops:({theme:i})=>i("colors"),gradientColorStopPositions:{"0%":"0%","5%":"5%","10%":"10%","15%":"15%","20%":"20%","25%":"25%","30%":"30%","35%":"35%","40%":"40%","45%":"45%","50%":"50%","55%":"55%","60%":"60%","65%":"65%","70%":"70%","75%":"75%","80%":"80%","85%":"85%","90%":"90%","95%":"95%","100%":"100%"},grayscale:{0:"0",DEFAULT:"100%"},gridAutoColumns:{auto:"auto",min:"min-content",max:"max-content",fr:"minmax(0, 1fr)"},gridAutoRows:{auto:"auto",min:"min-content",max:"max-content",fr:"minmax(0, 1fr)"},gridColumn:{auto:"auto","span-1":"span 1 / span 1","span-2":"span 2 / span 2","span-3":"span 3 / span 3","span-4":"span 4 / span 4","span-5":"span 5 / span 5","span-6":"span 6 / span 6","span-7":"span 7 / span 7","span-8":"span 8 / span 8","span-9":"span 9 / span 9","span-10":"span 10 / span 10","span-11":"span 11 / span 11","span-12":"span 12 / span 12","span-full":"1 / -1"},gridColumnEnd:{auto:"auto",1:"1",2:"2",3:"3",4:"4",5:"5",6:"6",7:"7",8:"8",9:"9",10:"10",11:"11",12:"12",13:"13"},gridColumnStart:{auto:"auto",1:"1",2:"2",3:"3",4:"4",5:"5",6:"6",7:"7",8:"8",9:"9",10:"10",11:"11",12:"12",13:"13"},gridRow:{auto:"auto","span-1":"span 1 / span 1","span-2":"span 2 / span 2","span-3":"span 3 / span 3","span-4":"span 4 / span 4","span-5":"span 5 / span 5","span-6":"span 6 / span 6","span-full":"1 / -1"},gridRowEnd:{auto:"auto",1:"1",2:"2",3:"3",4:"4",5:"5",6:"6",7:"7"},gridRowStart:{auto:"auto",1:"1",2:"2",3:"3",4:"4",5:"5",6:"6",7:"7"},gridTemplateColumns:{none:"none",1:"repeat(1, minmax(0, 1fr))",2:"repeat(2, minmax(0, 1fr))",3:"repeat(3, minmax(0, 1fr))",4:"repeat(4, minmax(0, 1fr))",5:"repeat(5, minmax(0, 1fr))",6:"repeat(6, minmax(0, 1fr))",7:"repeat(7, minmax(0, 1fr))",8:"repeat(8, minmax(0, 1fr))",9:"repeat(9, minmax(0, 1fr))",10:"repeat(10, minmax(0, 1fr))",11:"repeat(11, minmax(0, 1fr))",12:"repeat(12, minmax(0, 1fr))"},gridTemplateRows:{none:"none",1:"repeat(1, minmax(0, 1fr))",2:"repeat(2, minmax(0, 1fr))",3:"repeat(3, minmax(0, 1fr))",4:"repeat(4, minmax(0, 1fr))",5:"repeat(5, minmax(0, 1fr))",6:"repeat(6, minmax(0, 1fr))"},height:({theme:i})=>({auto:"auto",...i("spacing"),"1/2":"50%","1/3":"33.333333%","2/3":"66.666667%","1/4":"25%","2/4":"50%","3/4":"75%","1/5":"20%","2/5":"40%","3/5":"60%","4/5":"80%","1/6":"16.666667%","2/6":"33.333333%","3/6":"50%","4/6":"66.666667%","5/6":"83.333333%",full:"100%",screen:"100vh",min:"min-content",max:"max-content",fit:"fit-content"}),hueRotate:{0:"0deg",15:"15deg",30:"30deg",60:"60deg",90:"90deg",180:"180deg"},inset:({theme:i})=>({auto:"auto",...i("spacing"),"1/2":"50%","1/3":"33.333333%","2/3":"66.666667%","1/4":"25%","2/4":"50%","3/4":"75%",full:"100%"}),invert:{0:"0",DEFAULT:"100%"},keyframes:{spin:{to:{transform:"rotate(360deg)"}},ping:{"75%, 100%":{transform:"scale(2)",opacity:"0"}},pulse:{"50%":{opacity:".5"}},bounce:{"0%, 100%":{transform:"translateY(-25%)",animationTimingFunction:"cubic-bezier(0.8,0,1,1)"},"50%":{transform:"none",animationTimingFunction:"cubic-bezier(0,0,0.2,1)"}}},letterSpacing:{tighter:"-0.05em",tight:"-0.025em",normal:"0em",wide:"0.025em",wider:"0.05em",widest:"0.1em"},lineHeight:{none:"1",tight:"1.25",snug:"1.375",normal:"1.5",relaxed:"1.625",loose:"2",3:".75rem",4:"1rem",5:"1.25rem",6:"1.5rem",7:"1.75rem",8:"2rem",9:"2.25rem",10:"2.5rem"},listStyleType:{none:"none",disc:"disc",decimal:"decimal"},listStyleImage:{none:"none"},margin:({theme:i})=>({auto:"auto",...i("spacing")}),lineClamp:{1:"1",2:"2",3:"3",4:"4",5:"5",6:"6"},maxHeight:({theme:i})=>({...i("spacing"),none:"none",full:"100%",screen:"100vh",min:"min-content",max:"max-content",fit:"fit-content"}),maxWidth:({theme:i,breakpoints:e})=>({none:"none",0:"0rem",xs:"20rem",sm:"24rem",md:"28rem",lg:"32rem",xl:"36rem","2xl":"42rem","3xl":"48rem","4xl":"56rem","5xl":"64rem","6xl":"72rem","7xl":"80rem",full:"100%",min:"min-content",max:"max-content",fit:"fit-content",prose:"65ch",...e(i("screens"))}),minHeight:{0:"0px",full:"100%",screen:"100vh",min:"min-content",max:"max-content",fit:"fit-content"},minWidth:{0:"0px",full:"100%",min:"min-content",max:"max-content",fit:"fit-content"},objectPosition:{bottom:"bottom",center:"center",left:"left","left-bottom":"left bottom","left-top":"left top",right:"right","right-bottom":"right bottom","right-top":"right top",top:"top"},opacity:{0:"0",5:"0.05",10:"0.1",20:"0.2",25:"0.25",30:"0.3",40:"0.4",50:"0.5",60:"0.6",70:"0.7",75:"0.75",80:"0.8",90:"0.9",95:"0.95",100:"1"},order:{first:"-9999",last:"9999",none:"0",1:"1",2:"2",3:"3",4:"4",5:"5",6:"6",7:"7",8:"8",9:"9",10:"10",11:"11",12:"12"},outlineColor:({theme:i})=>i("colors"),outlineOffset:{0:"0px",1:"1px",2:"2px",4:"4px",8:"8px"},outlineWidth:{0:"0px",1:"1px",2:"2px",4:"4px",8:"8px"},padding:({theme:i})=>i("spacing"),placeholderColor:({theme:i})=>i("colors"),placeholderOpacity:({theme:i})=>i("opacity"),ringColor:({theme:i})=>({DEFAULT:i("colors.blue.500","#3b82f6"),...i("colors")}),ringOffsetColor:({theme:i})=>i("colors"),ringOffsetWidth:{0:"0px",1:"1px",2:"2px",4:"4px",8:"8px"},ringOpacity:({theme:i})=>({DEFAULT:"0.5",...i("opacity")}),ringWidth:{DEFAULT:"3px",0:"0px",1:"1px",2:"2px",4:"4px",8:"8px"},rotate:{0:"0deg",1:"1deg",2:"2deg",3:"3deg",6:"6deg",12:"12deg",45:"45deg",90:"90deg",180:"180deg"},saturate:{0:"0",50:".5",100:"1",150:"1.5",200:"2"},scale:{0:"0",50:".5",75:".75",90:".9",95:".95",100:"1",105:"1.05",110:"1.1",125:"1.25",150:"1.5"},screens:{sm:"640px",md:"768px",lg:"1024px",xl:"1280px","2xl":"1536px"},scrollMargin:({theme:i})=>({...i("spacing")}),scrollPadding:({theme:i})=>i("spacing"),sepia:{0:"0",DEFAULT:"100%"},skew:{0:"0deg",1:"1deg",2:"2deg",3:"3deg",6:"6deg",12:"12deg"},space:({theme:i})=>({...i("spacing")}),spacing:{px:"1px",0:"0px",.5:"0.125rem",1:"0.25rem",1.5:"0.375rem",2:"0.5rem",2.5:"0.625rem",3:"0.75rem",3.5:"0.875rem",4:"1rem",5:"1.25rem",6:"1.5rem",7:"1.75rem",8:"2rem",9:"2.25rem",10:"2.5rem",11:"2.75rem",12:"3rem",14:"3.5rem",16:"4rem",20:"5rem",24:"6rem",28:"7rem",32:"8rem",36:"9rem",40:"10rem",44:"11rem",48:"12rem",52:"13rem",56:"14rem",60:"15rem",64:"16rem",72:"18rem",80:"20rem",96:"24rem"},stroke:({theme:i})=>({none:"none",...i("colors")}),strokeWidth:{0:"0",1:"1",2:"2"},supports:{},data:{},textColor:({theme:i})=>i("colors"),textDecorationColor:({theme:i})=>i("colors"),textDecorationThickness:{auto:"auto","from-font":"from-font",0:"0px",1:"1px",2:"2px",4:"4px",8:"8px"},textIndent:({theme:i})=>({...i("spacing")}),textOpacity:({theme:i})=>i("opacity"),textUnderlineOffset:{auto:"auto",0:"0px",1:"1px",2:"2px",4:"4px",8:"8px"},transformOrigin:{center:"center",top:"top","top-right":"top right",right:"right","bottom-right":"bottom right",bottom:"bottom","bottom-left":"bottom left",left:"left","top-left":"top left"},transitionDelay:{0:"0s",75:"75ms",100:"100ms",150:"150ms",200:"200ms",300:"300ms",500:"500ms",700:"700ms",1e3:"1000ms"},transitionDuration:{DEFAULT:"150ms",0:"0s",75:"75ms",100:"100ms",150:"150ms",200:"200ms",300:"300ms",500:"500ms",700:"700ms",1e3:"1000ms"},transitionProperty:{none:"none",all:"all",DEFAULT:"color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter",colors:"color, background-color, border-color, text-decoration-color, fill, stroke",opacity:"opacity",shadow:"box-shadow",transform:"transform"},transitionTimingFunction:{DEFAULT:"cubic-bezier(0.4, 0, 0.2, 1)",linear:"linear",in:"cubic-bezier(0.4, 0, 1, 1)",out:"cubic-bezier(0, 0, 0.2, 1)","in-out":"cubic-bezier(0.4, 0, 0.2, 1)"},translate:({theme:i})=>({...i("spacing"),"1/2":"50%","1/3":"33.333333%","2/3":"66.666667%","1/4":"25%","2/4":"50%","3/4":"75%",full:"100%"}),width:({theme:i})=>({auto:"auto",...i("spacing"),"1/2":"50%","1/3":"33.333333%","2/3":"66.666667%","1/4":"25%","2/4":"50%","3/4":"75%","1/5":"20%","2/5":"40%","3/5":"60%","4/5":"80%","1/6":"16.666667%","2/6":"33.333333%","3/6":"50%","4/6":"66.666667%","5/6":"83.333333%","1/12":"8.333333%","2/12":"16.666667%","3/12":"25%","4/12":"33.333333%","5/12":"41.666667%","6/12":"50%","7/12":"58.333333%","8/12":"66.666667%","9/12":"75%","10/12":"83.333333%","11/12":"91.666667%",full:"100%",screen:"100vw",min:"min-content",max:"max-content",fit:"fit-content"}),willChange:{auto:"auto",scroll:"scroll-position",contents:"contents",transform:"transform"},zIndex:{auto:"auto",0:"0",10:"10",20:"20",30:"30",40:"40",50:"50"}},plugins:[]}});function vi(i){let e=(i?.presets??[ef.default]).slice().reverse().flatMap(n=>vi(n instanceof Function?n():n)),t={respectDefaultRingColorOpacity:{theme:{ringColor:({theme:n})=>({DEFAULT:"#3b82f67f",...n("colors")})}},disableColorOpacityUtilitiesByDefault:{corePlugins:{backgroundOpacity:!1,borderOpacity:!1,divideOpacity:!1,placeholderOpacity:!1,ringOpacity:!1,textOpacity:!1}}},r=Object.keys(t).filter(n=>J(i,n)).map(n=>t[n]);return[i,...r,...e]}var ef,tf=C(()=>{l();ef=K(bi());De()});var rf={};Ae(rf,{default:()=>dr});function dr(...i){let[,...e]=vi(i[0]);return ms([...i,...e])}var gs=C(()=>{l();Ku();tf()});var nf={};Ae(nf,{default:()=>Z});var Z,mt=C(()=>{l();Z={resolve:i=>i,extname:i=>"."+i.split(".").pop()}});function xi(i){return typeof i=="object"&&i!==null}function o0(i){return Object.keys(i).length===0}function sf(i){return typeof i=="string"||i instanceof String}function ys(i){return xi(i)&&i.config===void 0&&!o0(i)?null:xi(i)&&i.config!==void 0&&sf(i.config)?Z.resolve(i.config):xi(i)&&i.config!==void 0&&xi(i.config)?null:sf(i)?Z.resolve(i):l0()}function l0(){for(let i of a0)try{let e=Z.resolve(i);return te.accessSync(e),e}catch(e){}return null}var a0,af=C(()=>{l();ze();mt();a0=["./tailwind.config.js","./tailwind.config.cjs","./tailwind.config.mjs","./tailwind.config.ts"]});var of={};Ae(of,{default:()=>ws});var ws,bs=C(()=>{l();ws={parse:i=>({href:i})}});var vs=v(()=>{l()});var ki=v((fT,ff)=>{l();"use strict";var lf=(ci(),vu),uf=vs(),Ct=class extends Error{constructor(e,t,r,n,a,s){super(e);this.name="CssSyntaxError",this.reason=e,a&&(this.file=a),n&&(this.source=n),s&&(this.plugin=s),typeof t!="undefined"&&typeof r!="undefined"&&(typeof t=="number"?(this.line=t,this.column=r):(this.line=t.line,this.column=t.column,this.endLine=r.line,this.endColumn=r.column)),this.setMessage(),Error.captureStackTrace&&Error.captureStackTrace(this,Ct)}setMessage(){this.message=this.plugin?this.plugin+": ":"",this.message+=this.file?this.file:"",typeof this.line!="undefined"&&(this.message+=":"+this.line+":"+this.column),this.message+=": "+this.reason}showSourceCode(e){if(!this.source)return"";let t=this.source;e==null&&(e=lf.isColorSupported),uf&&e&&(t=uf(t));let r=t.split(/\r?\n/),n=Math.max(this.line-3,0),a=Math.min(this.line+2,r.length),s=String(a).length,o,u;if(e){let{bold:c,red:f,gray:p}=lf.createColors(!0);o=d=>c(f(d)),u=d=>p(d)}else o=u=c=>c;return r.slice(n,a).map((c,f)=>{let p=n+1+f,d=" "+(" "+p).slice(-s)+" | ";if(p===this.line){let h=u(d.replace(/\d/g," "))+c.slice(0,this.column-1).replace(/[^\t]/g," ");return o(">")+u(d)+c+` + `+h+o("^")}return" "+u(d)+c}).join(` +`)}toString(){let e=this.showSourceCode();return e&&(e=` + +`+e+` +`),this.name+": "+this.message+e}};ff.exports=Ct;Ct.default=Ct});var Si=v((cT,xs)=>{l();"use strict";xs.exports.isClean=Symbol("isClean");xs.exports.my=Symbol("my")});var ks=v((pT,pf)=>{l();"use strict";var cf={colon:": ",indent:" ",beforeDecl:` +`,beforeRule:` +`,beforeOpen:" ",beforeClose:` +`,beforeComment:` +`,after:` +`,emptyBody:"",commentLeft:" ",commentRight:" ",semicolon:!1};function u0(i){return i[0].toUpperCase()+i.slice(1)}var Ci=class{constructor(e){this.builder=e}stringify(e,t){if(!this[e.type])throw new Error("Unknown AST node type "+e.type+". Maybe you need to change PostCSS stringifier.");this[e.type](e,t)}document(e){this.body(e)}root(e){this.body(e),e.raws.after&&this.builder(e.raws.after)}comment(e){let t=this.raw(e,"left","commentLeft"),r=this.raw(e,"right","commentRight");this.builder("/*"+t+e.text+r+"*/",e)}decl(e,t){let r=this.raw(e,"between","colon"),n=e.prop+r+this.rawValue(e,"value");e.important&&(n+=e.raws.important||" !important"),t&&(n+=";"),this.builder(n,e)}rule(e){this.block(e,this.rawValue(e,"selector")),e.raws.ownSemicolon&&this.builder(e.raws.ownSemicolon,e,"end")}atrule(e,t){let r="@"+e.name,n=e.params?this.rawValue(e,"params"):"";if(typeof e.raws.afterName!="undefined"?r+=e.raws.afterName:n&&(r+=" "),e.nodes)this.block(e,r+n);else{let a=(e.raws.between||"")+(t?";":"");this.builder(r+n+a,e)}}body(e){let t=e.nodes.length-1;for(;t>0&&e.nodes[t].type==="comment";)t-=1;let r=this.raw(e,"semicolon");for(let n=0;n{if(n=u.raws[t],typeof n!="undefined")return!1})}return typeof n=="undefined"&&(n=cf[r]),s.rawCache[r]=n,n}rawSemicolon(e){let t;return e.walk(r=>{if(r.nodes&&r.nodes.length&&r.last.type==="decl"&&(t=r.raws.semicolon,typeof t!="undefined"))return!1}),t}rawEmptyBody(e){let t;return e.walk(r=>{if(r.nodes&&r.nodes.length===0&&(t=r.raws.after,typeof t!="undefined"))return!1}),t}rawIndent(e){if(e.raws.indent)return e.raws.indent;let t;return e.walk(r=>{let n=r.parent;if(n&&n!==e&&n.parent&&n.parent===e&&typeof r.raws.before!="undefined"){let a=r.raws.before.split(` +`);return t=a[a.length-1],t=t.replace(/\S/g,""),!1}}),t}rawBeforeComment(e,t){let r;return e.walkComments(n=>{if(typeof n.raws.before!="undefined")return r=n.raws.before,r.includes(` +`)&&(r=r.replace(/[^\n]+$/,"")),!1}),typeof r=="undefined"?r=this.raw(t,null,"beforeDecl"):r&&(r=r.replace(/\S/g,"")),r}rawBeforeDecl(e,t){let r;return e.walkDecls(n=>{if(typeof n.raws.before!="undefined")return r=n.raws.before,r.includes(` +`)&&(r=r.replace(/[^\n]+$/,"")),!1}),typeof r=="undefined"?r=this.raw(t,null,"beforeRule"):r&&(r=r.replace(/\S/g,"")),r}rawBeforeRule(e){let t;return e.walk(r=>{if(r.nodes&&(r.parent!==e||e.first!==r)&&typeof r.raws.before!="undefined")return t=r.raws.before,t.includes(` +`)&&(t=t.replace(/[^\n]+$/,"")),!1}),t&&(t=t.replace(/\S/g,"")),t}rawBeforeClose(e){let t;return e.walk(r=>{if(r.nodes&&r.nodes.length>0&&typeof r.raws.after!="undefined")return t=r.raws.after,t.includes(` +`)&&(t=t.replace(/[^\n]+$/,"")),!1}),t&&(t=t.replace(/\S/g,"")),t}rawBeforeOpen(e){let t;return e.walk(r=>{if(r.type!=="decl"&&(t=r.raws.between,typeof t!="undefined"))return!1}),t}rawColon(e){let t;return e.walkDecls(r=>{if(typeof r.raws.between!="undefined")return t=r.raws.between.replace(/[^\s:]/g,""),!1}),t}beforeAfter(e,t){let r;e.type==="decl"?r=this.raw(e,null,"beforeDecl"):e.type==="comment"?r=this.raw(e,null,"beforeComment"):t==="before"?r=this.raw(e,null,"beforeRule"):r=this.raw(e,null,"beforeClose");let n=e.parent,a=0;for(;n&&n.type!=="root";)a+=1,n=n.parent;if(r.includes(` +`)){let s=this.raw(e,null,"indent");if(s.length)for(let o=0;o{l();"use strict";var f0=ks();function Ss(i,e){new f0(e).stringify(i)}df.exports=Ss;Ss.default=Ss});var mr=v((hT,hf)=>{l();"use strict";var{isClean:Ai,my:c0}=Si(),p0=ki(),d0=ks(),h0=hr();function Cs(i,e){let t=new i.constructor;for(let r in i){if(!Object.prototype.hasOwnProperty.call(i,r)||r==="proxyCache")continue;let n=i[r],a=typeof n;r==="parent"&&a==="object"?e&&(t[r]=e):r==="source"?t[r]=n:Array.isArray(n)?t[r]=n.map(s=>Cs(s,t)):(a==="object"&&n!==null&&(n=Cs(n)),t[r]=n)}return t}var _i=class{constructor(e={}){this.raws={},this[Ai]=!1,this[c0]=!0;for(let t in e)if(t==="nodes"){this.nodes=[];for(let r of e[t])typeof r.clone=="function"?this.append(r.clone()):this.append(r)}else this[t]=e[t]}error(e,t={}){if(this.source){let{start:r,end:n}=this.rangeBy(t);return this.source.input.error(e,{line:r.line,column:r.column},{line:n.line,column:n.column},t)}return new p0(e)}warn(e,t,r){let n={node:this};for(let a in r)n[a]=r[a];return e.warn(t,n)}remove(){return this.parent&&this.parent.removeChild(this),this.parent=void 0,this}toString(e=h0){e.stringify&&(e=e.stringify);let t="";return e(this,r=>{t+=r}),t}assign(e={}){for(let t in e)this[t]=e[t];return this}clone(e={}){let t=Cs(this);for(let r in e)t[r]=e[r];return t}cloneBefore(e={}){let t=this.clone(e);return this.parent.insertBefore(this,t),t}cloneAfter(e={}){let t=this.clone(e);return this.parent.insertAfter(this,t),t}replaceWith(...e){if(this.parent){let t=this,r=!1;for(let n of e)n===this?r=!0:r?(this.parent.insertAfter(t,n),t=n):this.parent.insertBefore(t,n);r||this.remove()}return this}next(){if(!this.parent)return;let e=this.parent.index(this);return this.parent.nodes[e+1]}prev(){if(!this.parent)return;let e=this.parent.index(this);return this.parent.nodes[e-1]}before(e){return this.parent.insertBefore(this,e),this}after(e){return this.parent.insertAfter(this,e),this}root(){let e=this;for(;e.parent&&e.parent.type!=="document";)e=e.parent;return e}raw(e,t){return new d0().raw(this,e,t)}cleanRaws(e){delete this.raws.before,delete this.raws.after,e||delete this.raws.between}toJSON(e,t){let r={},n=t==null;t=t||new Map;let a=0;for(let s in this){if(!Object.prototype.hasOwnProperty.call(this,s)||s==="parent"||s==="proxyCache")continue;let o=this[s];if(Array.isArray(o))r[s]=o.map(u=>typeof u=="object"&&u.toJSON?u.toJSON(null,t):u);else if(typeof o=="object"&&o.toJSON)r[s]=o.toJSON(null,t);else if(s==="source"){let u=t.get(o.input);u==null&&(u=a,t.set(o.input,a),a++),r[s]={inputId:u,start:o.start,end:o.end}}else r[s]=o}return n&&(r.inputs=[...t.keys()].map(s=>s.toJSON())),r}positionInside(e){let t=this.toString(),r=this.source.start.column,n=this.source.start.line;for(let a=0;ae.root().toProxy():e[t]}}}toProxy(){return this.proxyCache||(this.proxyCache=new Proxy(this,this.getProxyProcessor())),this.proxyCache}addToError(e){if(e.postcssNode=this,e.stack&&this.source&&/\n\s{4}at /.test(e.stack)){let t=this.source;e.stack=e.stack.replace(/\n\s{4}at /,`$&${t.input.from}:${t.start.line}:${t.start.column}$&`)}return e}markDirty(){if(this[Ai]){this[Ai]=!1;let e=this;for(;e=e.parent;)e[Ai]=!1}}get proxyOf(){return this}};hf.exports=_i;_i.default=_i});var gr=v((mT,mf)=>{l();"use strict";var m0=mr(),Ei=class extends m0{constructor(e){e&&typeof e.value!="undefined"&&typeof e.value!="string"&&(e={...e,value:String(e.value)});super(e);this.type="decl"}get variable(){return this.prop.startsWith("--")||this.prop[0]==="$"}};mf.exports=Ei;Ei.default=Ei});var As=v((gT,gf)=>{l();gf.exports=function(i,e){return{generate:()=>{let t="";return i(e,r=>{t+=r}),[t]}}}});var yr=v((yT,yf)=>{l();"use strict";var g0=mr(),Oi=class extends g0{constructor(e){super(e);this.type="comment"}};yf.exports=Oi;Oi.default=Oi});var it=v((wT,_f)=>{l();"use strict";var{isClean:wf,my:bf}=Si(),vf=gr(),xf=yr(),y0=mr(),kf,_s,Es,Sf;function Cf(i){return i.map(e=>(e.nodes&&(e.nodes=Cf(e.nodes)),delete e.source,e))}function Af(i){if(i[wf]=!1,i.proxyOf.nodes)for(let e of i.proxyOf.nodes)Af(e)}var ye=class extends y0{push(e){return e.parent=this,this.proxyOf.nodes.push(e),this}each(e){if(!this.proxyOf.nodes)return;let t=this.getIterator(),r,n;for(;this.indexes[t]{let n;try{n=e(t,r)}catch(a){throw t.addToError(a)}return n!==!1&&t.walk&&(n=t.walk(e)),n})}walkDecls(e,t){return t?e instanceof RegExp?this.walk((r,n)=>{if(r.type==="decl"&&e.test(r.prop))return t(r,n)}):this.walk((r,n)=>{if(r.type==="decl"&&r.prop===e)return t(r,n)}):(t=e,this.walk((r,n)=>{if(r.type==="decl")return t(r,n)}))}walkRules(e,t){return t?e instanceof RegExp?this.walk((r,n)=>{if(r.type==="rule"&&e.test(r.selector))return t(r,n)}):this.walk((r,n)=>{if(r.type==="rule"&&r.selector===e)return t(r,n)}):(t=e,this.walk((r,n)=>{if(r.type==="rule")return t(r,n)}))}walkAtRules(e,t){return t?e instanceof RegExp?this.walk((r,n)=>{if(r.type==="atrule"&&e.test(r.name))return t(r,n)}):this.walk((r,n)=>{if(r.type==="atrule"&&r.name===e)return t(r,n)}):(t=e,this.walk((r,n)=>{if(r.type==="atrule")return t(r,n)}))}walkComments(e){return this.walk((t,r)=>{if(t.type==="comment")return e(t,r)})}append(...e){for(let t of e){let r=this.normalize(t,this.last);for(let n of r)this.proxyOf.nodes.push(n)}return this.markDirty(),this}prepend(...e){e=e.reverse();for(let t of e){let r=this.normalize(t,this.first,"prepend").reverse();for(let n of r)this.proxyOf.nodes.unshift(n);for(let n in this.indexes)this.indexes[n]=this.indexes[n]+r.length}return this.markDirty(),this}cleanRaws(e){if(super.cleanRaws(e),this.nodes)for(let t of this.nodes)t.cleanRaws(e)}insertBefore(e,t){let r=this.index(e),n=r===0?"prepend":!1,a=this.normalize(t,this.proxyOf.nodes[r],n).reverse();r=this.index(e);for(let o of a)this.proxyOf.nodes.splice(r,0,o);let s;for(let o in this.indexes)s=this.indexes[o],r<=s&&(this.indexes[o]=s+a.length);return this.markDirty(),this}insertAfter(e,t){let r=this.index(e),n=this.normalize(t,this.proxyOf.nodes[r]).reverse();r=this.index(e);for(let s of n)this.proxyOf.nodes.splice(r+1,0,s);let a;for(let s in this.indexes)a=this.indexes[s],r=e&&(this.indexes[r]=t-1);return this.markDirty(),this}removeAll(){for(let e of this.proxyOf.nodes)e.parent=void 0;return this.proxyOf.nodes=[],this.markDirty(),this}replaceValues(e,t,r){return r||(r=t,t={}),this.walkDecls(n=>{t.props&&!t.props.includes(n.prop)||t.fast&&!n.value.includes(t.fast)||(n.value=n.value.replace(e,r))}),this.markDirty(),this}every(e){return this.nodes.every(e)}some(e){return this.nodes.some(e)}index(e){return typeof e=="number"?e:(e.proxyOf&&(e=e.proxyOf),this.proxyOf.nodes.indexOf(e))}get first(){if(!!this.proxyOf.nodes)return this.proxyOf.nodes[0]}get last(){if(!!this.proxyOf.nodes)return this.proxyOf.nodes[this.proxyOf.nodes.length-1]}normalize(e,t){if(typeof e=="string")e=Cf(kf(e).nodes);else if(Array.isArray(e)){e=e.slice(0);for(let n of e)n.parent&&n.parent.removeChild(n,"ignore")}else if(e.type==="root"&&this.type!=="document"){e=e.nodes.slice(0);for(let n of e)n.parent&&n.parent.removeChild(n,"ignore")}else if(e.type)e=[e];else if(e.prop){if(typeof e.value=="undefined")throw new Error("Value field is missed in node creation");typeof e.value!="string"&&(e.value=String(e.value)),e=[new vf(e)]}else if(e.selector)e=[new _s(e)];else if(e.name)e=[new Es(e)];else if(e.text)e=[new xf(e)];else throw new Error("Unknown node type in node creation");return e.map(n=>(n[bf]||ye.rebuild(n),n=n.proxyOf,n.parent&&n.parent.removeChild(n),n[wf]&&Af(n),typeof n.raws.before=="undefined"&&t&&typeof t.raws.before!="undefined"&&(n.raws.before=t.raws.before.replace(/\S/g,"")),n.parent=this.proxyOf,n))}getProxyProcessor(){return{set(e,t,r){return e[t]===r||(e[t]=r,(t==="name"||t==="params"||t==="selector")&&e.markDirty()),!0},get(e,t){return t==="proxyOf"?e:e[t]?t==="each"||typeof t=="string"&&t.startsWith("walk")?(...r)=>e[t](...r.map(n=>typeof n=="function"?(a,s)=>n(a.toProxy(),s):n)):t==="every"||t==="some"?r=>e[t]((n,...a)=>r(n.toProxy(),...a)):t==="root"?()=>e.root().toProxy():t==="nodes"?e.nodes.map(r=>r.toProxy()):t==="first"||t==="last"?e[t].toProxy():e[t]:e[t]}}}getIterator(){this.lastEach||(this.lastEach=0),this.indexes||(this.indexes={}),this.lastEach+=1;let e=this.lastEach;return this.indexes[e]=0,e}};ye.registerParse=i=>{kf=i};ye.registerRule=i=>{_s=i};ye.registerAtRule=i=>{Es=i};ye.registerRoot=i=>{Sf=i};_f.exports=ye;ye.default=ye;ye.rebuild=i=>{i.type==="atrule"?Object.setPrototypeOf(i,Es.prototype):i.type==="rule"?Object.setPrototypeOf(i,_s.prototype):i.type==="decl"?Object.setPrototypeOf(i,vf.prototype):i.type==="comment"?Object.setPrototypeOf(i,xf.prototype):i.type==="root"&&Object.setPrototypeOf(i,Sf.prototype),i[bf]=!0,i.nodes&&i.nodes.forEach(e=>{ye.rebuild(e)})}});var Ti=v((bT,Tf)=>{l();"use strict";var w0=it(),Ef,Of,At=class extends w0{constructor(e){super({type:"document",...e});this.nodes||(this.nodes=[])}toResult(e={}){return new Ef(new Of,this,e).stringify()}};At.registerLazyResult=i=>{Ef=i};At.registerProcessor=i=>{Of=i};Tf.exports=At;At.default=At});var Os=v((vT,Df)=>{l();"use strict";var Pf={};Df.exports=function(e){Pf[e]||(Pf[e]=!0,typeof console!="undefined"&&console.warn&&console.warn(e))}});var Ts=v((xT,If)=>{l();"use strict";var Pi=class{constructor(e,t={}){if(this.type="warning",this.text=e,t.node&&t.node.source){let r=t.node.rangeBy(t);this.line=r.start.line,this.column=r.start.column,this.endLine=r.end.line,this.endColumn=r.end.column}for(let r in t)this[r]=t[r]}toString(){return this.node?this.node.error(this.text,{plugin:this.plugin,index:this.index,word:this.word}).message:this.plugin?this.plugin+": "+this.text:this.text}};If.exports=Pi;Pi.default=Pi});var Ii=v((kT,qf)=>{l();"use strict";var b0=Ts(),Di=class{constructor(e,t,r){this.processor=e,this.messages=[],this.root=t,this.opts=r,this.css=void 0,this.map=void 0}toString(){return this.css}warn(e,t={}){t.plugin||this.lastPlugin&&this.lastPlugin.postcssPlugin&&(t.plugin=this.lastPlugin.postcssPlugin);let r=new b0(e,t);return this.messages.push(r),r}warnings(){return this.messages.filter(e=>e.type==="warning")}get content(){return this.css}};qf.exports=Di;Di.default=Di});var Nf=v((ST,Ff)=>{l();"use strict";var Ps="'".charCodeAt(0),Rf='"'.charCodeAt(0),qi="\\".charCodeAt(0),Mf="/".charCodeAt(0),Ri=` +`.charCodeAt(0),wr=" ".charCodeAt(0),Mi="\f".charCodeAt(0),Bi=" ".charCodeAt(0),Fi="\r".charCodeAt(0),v0="[".charCodeAt(0),x0="]".charCodeAt(0),k0="(".charCodeAt(0),S0=")".charCodeAt(0),C0="{".charCodeAt(0),A0="}".charCodeAt(0),_0=";".charCodeAt(0),E0="*".charCodeAt(0),O0=":".charCodeAt(0),T0="@".charCodeAt(0),Ni=/[\t\n\f\r "#'()/;[\\\]{}]/g,Li=/[\t\n\f\r !"#'():;@[\\\]{}]|\/(?=\*)/g,P0=/.[\n"'(/\\]/,Bf=/[\da-f]/i;Ff.exports=function(e,t={}){let r=e.css.valueOf(),n=t.ignoreErrors,a,s,o,u,c,f,p,d,h,y,x=r.length,w=0,b=[],k=[];function S(){return w}function _(q){throw e.error("Unclosed "+q,w)}function E(){return k.length===0&&w>=x}function I(q){if(k.length)return k.pop();if(w>=x)return;let X=q?q.ignoreUnclosed:!1;switch(a=r.charCodeAt(w),a){case Ri:case wr:case Bi:case Fi:case Mi:{s=w;do s+=1,a=r.charCodeAt(s);while(a===wr||a===Ri||a===Bi||a===Fi||a===Mi);y=["space",r.slice(w,s)],w=s-1;break}case v0:case x0:case C0:case A0:case O0:case _0:case S0:{let ae=String.fromCharCode(a);y=[ae,ae,w];break}case k0:{if(d=b.length?b.pop()[1]:"",h=r.charCodeAt(w+1),d==="url"&&h!==Ps&&h!==Rf&&h!==wr&&h!==Ri&&h!==Bi&&h!==Mi&&h!==Fi){s=w;do{if(f=!1,s=r.indexOf(")",s+1),s===-1)if(n||X){s=w;break}else _("bracket");for(p=s;r.charCodeAt(p-1)===qi;)p-=1,f=!f}while(f);y=["brackets",r.slice(w,s+1),w,s],w=s}else s=r.indexOf(")",w+1),u=r.slice(w,s+1),s===-1||P0.test(u)?y=["(","(",w]:(y=["brackets",u,w,s],w=s);break}case Ps:case Rf:{o=a===Ps?"'":'"',s=w;do{if(f=!1,s=r.indexOf(o,s+1),s===-1)if(n||X){s=w+1;break}else _("string");for(p=s;r.charCodeAt(p-1)===qi;)p-=1,f=!f}while(f);y=["string",r.slice(w,s+1),w,s],w=s;break}case T0:{Ni.lastIndex=w+1,Ni.test(r),Ni.lastIndex===0?s=r.length-1:s=Ni.lastIndex-2,y=["at-word",r.slice(w,s+1),w,s],w=s;break}case qi:{for(s=w,c=!0;r.charCodeAt(s+1)===qi;)s+=1,c=!c;if(a=r.charCodeAt(s+1),c&&a!==Mf&&a!==wr&&a!==Ri&&a!==Bi&&a!==Fi&&a!==Mi&&(s+=1,Bf.test(r.charAt(s)))){for(;Bf.test(r.charAt(s+1));)s+=1;r.charCodeAt(s+1)===wr&&(s+=1)}y=["word",r.slice(w,s+1),w,s],w=s;break}default:{a===Mf&&r.charCodeAt(w+1)===E0?(s=r.indexOf("*/",w+2)+1,s===0&&(n||X?s=r.length:_("comment")),y=["comment",r.slice(w,s+1),w,s],w=s):(Li.lastIndex=w+1,Li.test(r),Li.lastIndex===0?s=r.length-1:s=Li.lastIndex-2,y=["word",r.slice(w,s+1),w,s],b.push(y),w=s);break}}return w++,y}function B(q){k.push(q)}return{back:B,nextToken:I,endOfFile:E,position:S}}});var $i=v((CT,$f)=>{l();"use strict";var Lf=it(),br=class extends Lf{constructor(e){super(e);this.type="atrule"}append(...e){return this.proxyOf.nodes||(this.nodes=[]),super.append(...e)}prepend(...e){return this.proxyOf.nodes||(this.nodes=[]),super.prepend(...e)}};$f.exports=br;br.default=br;Lf.registerAtRule(br)});var _t=v((AT,Uf)=>{l();"use strict";var jf=it(),zf,Vf,gt=class extends jf{constructor(e){super(e);this.type="root",this.nodes||(this.nodes=[])}removeChild(e,t){let r=this.index(e);return!t&&r===0&&this.nodes.length>1&&(this.nodes[1].raws.before=this.nodes[r].raws.before),super.removeChild(e)}normalize(e,t,r){let n=super.normalize(e);if(t){if(r==="prepend")this.nodes.length>1?t.raws.before=this.nodes[1].raws.before:delete t.raws.before;else if(this.first!==t)for(let a of n)a.raws.before=t.raws.before}return n}toResult(e={}){return new zf(new Vf,this,e).stringify()}};gt.registerLazyResult=i=>{zf=i};gt.registerProcessor=i=>{Vf=i};Uf.exports=gt;gt.default=gt;jf.registerRoot(gt)});var Ds=v((_T,Wf)=>{l();"use strict";var vr={split(i,e,t){let r=[],n="",a=!1,s=0,o=!1,u="",c=!1;for(let f of i)c?c=!1:f==="\\"?c=!0:o?f===u&&(o=!1):f==='"'||f==="'"?(o=!0,u=f):f==="("?s+=1:f===")"?s>0&&(s-=1):s===0&&e.includes(f)&&(a=!0),a?(n!==""&&r.push(n.trim()),n="",a=!1):n+=f;return(t||n!=="")&&r.push(n.trim()),r},space(i){let e=[" ",` +`," "];return vr.split(i,e)},comma(i){return vr.split(i,[","],!0)}};Wf.exports=vr;vr.default=vr});var ji=v((ET,Hf)=>{l();"use strict";var Gf=it(),D0=Ds(),xr=class extends Gf{constructor(e){super(e);this.type="rule",this.nodes||(this.nodes=[])}get selectors(){return D0.comma(this.selector)}set selectors(e){let t=this.selector?this.selector.match(/,\s*/):null,r=t?t[0]:","+this.raw("between","beforeOpen");this.selector=e.join(r)}};Hf.exports=xr;xr.default=xr;Gf.registerRule(xr)});var Kf=v((OT,Xf)=>{l();"use strict";var I0=gr(),q0=Nf(),R0=yr(),M0=$i(),B0=_t(),Yf=ji(),Qf={empty:!0,space:!0};function F0(i){for(let e=i.length-1;e>=0;e--){let t=i[e],r=t[3]||t[2];if(r)return r}}var Jf=class{constructor(e){this.input=e,this.root=new B0,this.current=this.root,this.spaces="",this.semicolon=!1,this.customProperty=!1,this.createTokenizer(),this.root.source={input:e,start:{offset:0,line:1,column:1}}}createTokenizer(){this.tokenizer=q0(this.input)}parse(){let e;for(;!this.tokenizer.endOfFile();)switch(e=this.tokenizer.nextToken(),e[0]){case"space":this.spaces+=e[1];break;case";":this.freeSemicolon(e);break;case"}":this.end(e);break;case"comment":this.comment(e);break;case"at-word":this.atrule(e);break;case"{":this.emptyRule(e);break;default:this.other(e);break}this.endFile()}comment(e){let t=new R0;this.init(t,e[2]),t.source.end=this.getPosition(e[3]||e[2]);let r=e[1].slice(2,-2);if(/^\s*$/.test(r))t.text="",t.raws.left=r,t.raws.right="";else{let n=r.match(/^(\s*)([^]*\S)(\s*)$/);t.text=n[2],t.raws.left=n[1],t.raws.right=n[3]}}emptyRule(e){let t=new Yf;this.init(t,e[2]),t.selector="",t.raws.between="",this.current=t}other(e){let t=!1,r=null,n=!1,a=null,s=[],o=e[1].startsWith("--"),u=[],c=e;for(;c;){if(r=c[0],u.push(c),r==="("||r==="[")a||(a=c),s.push(r==="("?")":"]");else if(o&&n&&r==="{")a||(a=c),s.push("}");else if(s.length===0)if(r===";")if(n){this.decl(u,o);return}else break;else if(r==="{"){this.rule(u);return}else if(r==="}"){this.tokenizer.back(u.pop()),t=!0;break}else r===":"&&(n=!0);else r===s[s.length-1]&&(s.pop(),s.length===0&&(a=null));c=this.tokenizer.nextToken()}if(this.tokenizer.endOfFile()&&(t=!0),s.length>0&&this.unclosedBracket(a),t&&n){if(!o)for(;u.length&&(c=u[u.length-1][0],!(c!=="space"&&c!=="comment"));)this.tokenizer.back(u.pop());this.decl(u,o)}else this.unknownWord(u)}rule(e){e.pop();let t=new Yf;this.init(t,e[0][2]),t.raws.between=this.spacesAndCommentsFromEnd(e),this.raw(t,"selector",e),this.current=t}decl(e,t){let r=new I0;this.init(r,e[0][2]);let n=e[e.length-1];for(n[0]===";"&&(this.semicolon=!0,e.pop()),r.source.end=this.getPosition(n[3]||n[2]||F0(e));e[0][0]!=="word";)e.length===1&&this.unknownWord(e),r.raws.before+=e.shift()[1];for(r.source.start=this.getPosition(e[0][2]),r.prop="";e.length;){let c=e[0][0];if(c===":"||c==="space"||c==="comment")break;r.prop+=e.shift()[1]}r.raws.between="";let a;for(;e.length;)if(a=e.shift(),a[0]===":"){r.raws.between+=a[1];break}else a[0]==="word"&&/\w/.test(a[1])&&this.unknownWord([a]),r.raws.between+=a[1];(r.prop[0]==="_"||r.prop[0]==="*")&&(r.raws.before+=r.prop[0],r.prop=r.prop.slice(1));let s=[],o;for(;e.length&&(o=e[0][0],!(o!=="space"&&o!=="comment"));)s.push(e.shift());this.precheckMissedSemicolon(e);for(let c=e.length-1;c>=0;c--){if(a=e[c],a[1].toLowerCase()==="!important"){r.important=!0;let f=this.stringFrom(e,c);f=this.spacesFromEnd(e)+f,f!==" !important"&&(r.raws.important=f);break}else if(a[1].toLowerCase()==="important"){let f=e.slice(0),p="";for(let d=c;d>0;d--){let h=f[d][0];if(p.trim().indexOf("!")===0&&h!=="space")break;p=f.pop()[1]+p}p.trim().indexOf("!")===0&&(r.important=!0,r.raws.important=p,e=f)}if(a[0]!=="space"&&a[0]!=="comment")break}e.some(c=>c[0]!=="space"&&c[0]!=="comment")&&(r.raws.between+=s.map(c=>c[1]).join(""),s=[]),this.raw(r,"value",s.concat(e),t),r.value.includes(":")&&!t&&this.checkMissedSemicolon(e)}atrule(e){let t=new M0;t.name=e[1].slice(1),t.name===""&&this.unnamedAtrule(t,e),this.init(t,e[2]);let r,n,a,s=!1,o=!1,u=[],c=[];for(;!this.tokenizer.endOfFile();){if(e=this.tokenizer.nextToken(),r=e[0],r==="("||r==="["?c.push(r==="("?")":"]"):r==="{"&&c.length>0?c.push("}"):r===c[c.length-1]&&c.pop(),c.length===0)if(r===";"){t.source.end=this.getPosition(e[2]),this.semicolon=!0;break}else if(r==="{"){o=!0;break}else if(r==="}"){if(u.length>0){for(a=u.length-1,n=u[a];n&&n[0]==="space";)n=u[--a];n&&(t.source.end=this.getPosition(n[3]||n[2]))}this.end(e);break}else u.push(e);else u.push(e);if(this.tokenizer.endOfFile()){s=!0;break}}t.raws.between=this.spacesAndCommentsFromEnd(u),u.length?(t.raws.afterName=this.spacesAndCommentsFromStart(u),this.raw(t,"params",u),s&&(e=u[u.length-1],t.source.end=this.getPosition(e[3]||e[2]),this.spaces=t.raws.between,t.raws.between="")):(t.raws.afterName="",t.params=""),o&&(t.nodes=[],this.current=t)}end(e){this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.semicolon=!1,this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.spaces="",this.current.parent?(this.current.source.end=this.getPosition(e[2]),this.current=this.current.parent):this.unexpectedClose(e)}endFile(){this.current.parent&&this.unclosedBlock(),this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.current.raws.after=(this.current.raws.after||"")+this.spaces}freeSemicolon(e){if(this.spaces+=e[1],this.current.nodes){let t=this.current.nodes[this.current.nodes.length-1];t&&t.type==="rule"&&!t.raws.ownSemicolon&&(t.raws.ownSemicolon=this.spaces,this.spaces="")}}getPosition(e){let t=this.input.fromOffset(e);return{offset:e,line:t.line,column:t.col}}init(e,t){this.current.push(e),e.source={start:this.getPosition(t),input:this.input},e.raws.before=this.spaces,this.spaces="",e.type!=="comment"&&(this.semicolon=!1)}raw(e,t,r,n){let a,s,o=r.length,u="",c=!0,f,p;for(let d=0;dh+y[1],"");e.raws[t]={value:u,raw:d}}e[t]=u}spacesAndCommentsFromEnd(e){let t,r="";for(;e.length&&(t=e[e.length-1][0],!(t!=="space"&&t!=="comment"));)r=e.pop()[1]+r;return r}spacesAndCommentsFromStart(e){let t,r="";for(;e.length&&(t=e[0][0],!(t!=="space"&&t!=="comment"));)r+=e.shift()[1];return r}spacesFromEnd(e){let t,r="";for(;e.length&&(t=e[e.length-1][0],t==="space");)r=e.pop()[1]+r;return r}stringFrom(e,t){let r="";for(let n=t;n=0&&(n=e[a],!(n[0]!=="space"&&(r+=1,r===2)));a--);throw this.input.error("Missed semicolon",n[0]==="word"?n[3]+1:n[2])}};Xf.exports=Jf});var Zf=v(()=>{l()});var tc=v((DT,ec)=>{l();var N0="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict",L0=(i,e=21)=>(t=e)=>{let r="",n=t;for(;n--;)r+=i[Math.random()*i.length|0];return r},$0=(i=21)=>{let e="",t=i;for(;t--;)e+=N0[Math.random()*64|0];return e};ec.exports={nanoid:$0,customAlphabet:L0}});var Is=v((IT,rc)=>{l();rc.exports={}});var Vi=v((qT,ac)=>{l();"use strict";var{SourceMapConsumer:j0,SourceMapGenerator:z0}=Zf(),{fileURLToPath:ic,pathToFileURL:zi}=(bs(),of),{resolve:qs,isAbsolute:Rs}=(mt(),nf),{nanoid:V0}=tc(),Ms=vs(),nc=ki(),U0=Is(),Bs=Symbol("fromOffsetCache"),W0=Boolean(j0&&z0),sc=Boolean(qs&&Rs),kr=class{constructor(e,t={}){if(e===null||typeof e=="undefined"||typeof e=="object"&&!e.toString)throw new Error(`PostCSS received ${e} instead of CSS string`);if(this.css=e.toString(),this.css[0]==="\uFEFF"||this.css[0]==="\uFFFE"?(this.hasBOM=!0,this.css=this.css.slice(1)):this.hasBOM=!1,t.from&&(!sc||/^\w+:\/\//.test(t.from)||Rs(t.from)?this.file=t.from:this.file=qs(t.from)),sc&&W0){let r=new U0(this.css,t);if(r.text){this.map=r;let n=r.consumer().file;!this.file&&n&&(this.file=this.mapResolve(n))}}this.file||(this.id=""),this.map&&(this.map.file=this.from)}fromOffset(e){let t,r;if(this[Bs])r=this[Bs];else{let a=this.css.split(` +`);r=new Array(a.length);let s=0;for(let o=0,u=a.length;o=t)n=r.length-1;else{let a=r.length-2,s;for(;n>1),e=r[s+1])n=s+1;else{n=s;break}}return{line:n+1,col:e-r[n]+1}}error(e,t,r,n={}){let a,s,o;if(t&&typeof t=="object"){let c=t,f=r;if(typeof c.offset=="number"){let p=this.fromOffset(c.offset);t=p.line,r=p.col}else t=c.line,r=c.column;if(typeof f.offset=="number"){let p=this.fromOffset(f.offset);s=p.line,o=p.col}else s=f.line,o=f.column}else if(!r){let c=this.fromOffset(t);t=c.line,r=c.col}let u=this.origin(t,r,s,o);return u?a=new nc(e,u.endLine===void 0?u.line:{line:u.line,column:u.column},u.endLine===void 0?u.column:{line:u.endLine,column:u.endColumn},u.source,u.file,n.plugin):a=new nc(e,s===void 0?t:{line:t,column:r},s===void 0?r:{line:s,column:o},this.css,this.file,n.plugin),a.input={line:t,column:r,endLine:s,endColumn:o,source:this.css},this.file&&(zi&&(a.input.url=zi(this.file).toString()),a.input.file=this.file),a}origin(e,t,r,n){if(!this.map)return!1;let a=this.map.consumer(),s=a.originalPositionFor({line:e,column:t});if(!s.source)return!1;let o;typeof r=="number"&&(o=a.originalPositionFor({line:r,column:n}));let u;Rs(s.source)?u=zi(s.source):u=new URL(s.source,this.map.consumer().sourceRoot||zi(this.map.mapFile));let c={url:u.toString(),line:s.line,column:s.column,endLine:o&&o.line,endColumn:o&&o.column};if(u.protocol==="file:")if(ic)c.file=ic(u);else throw new Error("file: protocol is not available in this PostCSS build");let f=a.sourceContentFor(s.source);return f&&(c.source=f),c}mapResolve(e){return/^\w+:\/\//.test(e)?e:qs(this.map.consumer().sourceRoot||this.map.root||".",e)}get from(){return this.file||this.id}toJSON(){let e={};for(let t of["hasBOM","css","file","id"])this[t]!=null&&(e[t]=this[t]);return this.map&&(e.map={...this.map},e.map.consumerCache&&(e.map.consumerCache=void 0)),e}};ac.exports=kr;kr.default=kr;Ms&&Ms.registerInput&&Ms.registerInput(kr)});var Wi=v((RT,oc)=>{l();"use strict";var G0=it(),H0=Kf(),Y0=Vi();function Ui(i,e){let t=new Y0(i,e),r=new H0(t);try{r.parse()}catch(n){throw n}return r.root}oc.exports=Ui;Ui.default=Ui;G0.registerParse(Ui)});var Ls=v((BT,cc)=>{l();"use strict";var{isClean:qe,my:Q0}=Si(),J0=As(),X0=hr(),K0=it(),Z0=Ti(),MT=Os(),lc=Ii(),ev=Wi(),tv=_t(),rv={document:"Document",root:"Root",atrule:"AtRule",rule:"Rule",decl:"Declaration",comment:"Comment"},iv={postcssPlugin:!0,prepare:!0,Once:!0,Document:!0,Root:!0,Declaration:!0,Rule:!0,AtRule:!0,Comment:!0,DeclarationExit:!0,RuleExit:!0,AtRuleExit:!0,CommentExit:!0,RootExit:!0,DocumentExit:!0,OnceExit:!0},nv={postcssPlugin:!0,prepare:!0,Once:!0},Et=0;function Sr(i){return typeof i=="object"&&typeof i.then=="function"}function uc(i){let e=!1,t=rv[i.type];return i.type==="decl"?e=i.prop.toLowerCase():i.type==="atrule"&&(e=i.name.toLowerCase()),e&&i.append?[t,t+"-"+e,Et,t+"Exit",t+"Exit-"+e]:e?[t,t+"-"+e,t+"Exit",t+"Exit-"+e]:i.append?[t,Et,t+"Exit"]:[t,t+"Exit"]}function fc(i){let e;return i.type==="document"?e=["Document",Et,"DocumentExit"]:i.type==="root"?e=["Root",Et,"RootExit"]:e=uc(i),{node:i,events:e,eventIndex:0,visitors:[],visitorIndex:0,iterator:0}}function Fs(i){return i[qe]=!1,i.nodes&&i.nodes.forEach(e=>Fs(e)),i}var Ns={},Ve=class{constructor(e,t,r){this.stringified=!1,this.processed=!1;let n;if(typeof t=="object"&&t!==null&&(t.type==="root"||t.type==="document"))n=Fs(t);else if(t instanceof Ve||t instanceof lc)n=Fs(t.root),t.map&&(typeof r.map=="undefined"&&(r.map={}),r.map.inline||(r.map.inline=!1),r.map.prev=t.map);else{let a=ev;r.syntax&&(a=r.syntax.parse),r.parser&&(a=r.parser),a.parse&&(a=a.parse);try{n=a(t,r)}catch(s){this.processed=!0,this.error=s}n&&!n[Q0]&&K0.rebuild(n)}this.result=new lc(e,n,r),this.helpers={...Ns,result:this.result,postcss:Ns},this.plugins=this.processor.plugins.map(a=>typeof a=="object"&&a.prepare?{...a,...a.prepare(this.result)}:a)}get[Symbol.toStringTag](){return"LazyResult"}get processor(){return this.result.processor}get opts(){return this.result.opts}get css(){return this.stringify().css}get content(){return this.stringify().content}get map(){return this.stringify().map}get root(){return this.sync().root}get messages(){return this.sync().messages}warnings(){return this.sync().warnings()}toString(){return this.css}then(e,t){return this.async().then(e,t)}catch(e){return this.async().catch(e)}finally(e){return this.async().then(e,e)}async(){return this.error?Promise.reject(this.error):this.processed?Promise.resolve(this.result):(this.processing||(this.processing=this.runAsync()),this.processing)}sync(){if(this.error)throw this.error;if(this.processed)return this.result;if(this.processed=!0,this.processing)throw this.getAsyncError();for(let e of this.plugins){let t=this.runOnRoot(e);if(Sr(t))throw this.getAsyncError()}if(this.prepareVisitors(),this.hasListener){let e=this.result.root;for(;!e[qe];)e[qe]=!0,this.walkSync(e);if(this.listeners.OnceExit)if(e.type==="document")for(let t of e.nodes)this.visitSync(this.listeners.OnceExit,t);else this.visitSync(this.listeners.OnceExit,e)}return this.result}stringify(){if(this.error)throw this.error;if(this.stringified)return this.result;this.stringified=!0,this.sync();let e=this.result.opts,t=X0;e.syntax&&(t=e.syntax.stringify),e.stringifier&&(t=e.stringifier),t.stringify&&(t=t.stringify);let n=new J0(t,this.result.root,this.result.opts).generate();return this.result.css=n[0],this.result.map=n[1],this.result}walkSync(e){e[qe]=!0;let t=uc(e);for(let r of t)if(r===Et)e.nodes&&e.each(n=>{n[qe]||this.walkSync(n)});else{let n=this.listeners[r];if(n&&this.visitSync(n,e.toProxy()))return}}visitSync(e,t){for(let[r,n]of e){this.result.lastPlugin=r;let a;try{a=n(t,this.helpers)}catch(s){throw this.handleError(s,t.proxyOf)}if(t.type!=="root"&&t.type!=="document"&&!t.parent)return!0;if(Sr(a))throw this.getAsyncError()}}runOnRoot(e){this.result.lastPlugin=e;try{if(typeof e=="object"&&e.Once){if(this.result.root.type==="document"){let t=this.result.root.nodes.map(r=>e.Once(r,this.helpers));return Sr(t[0])?Promise.all(t):t}return e.Once(this.result.root,this.helpers)}else if(typeof e=="function")return e(this.result.root,this.result)}catch(t){throw this.handleError(t)}}getAsyncError(){throw new Error("Use process(css).then(cb) to work with async plugins")}handleError(e,t){let r=this.result.lastPlugin;try{t&&t.addToError(e),this.error=e,e.name==="CssSyntaxError"&&!e.plugin?(e.plugin=r.postcssPlugin,e.setMessage()):r.postcssVersion}catch(n){console&&console.error&&console.error(n)}return e}async runAsync(){this.plugin=0;for(let e=0;e0;){let r=this.visitTick(t);if(Sr(r))try{await r}catch(n){let a=t[t.length-1].node;throw this.handleError(n,a)}}}if(this.listeners.OnceExit)for(let[t,r]of this.listeners.OnceExit){this.result.lastPlugin=t;try{if(e.type==="document"){let n=e.nodes.map(a=>r(a,this.helpers));await Promise.all(n)}else await r(e,this.helpers)}catch(n){throw this.handleError(n)}}}return this.processed=!0,this.stringify()}prepareVisitors(){this.listeners={};let e=(t,r,n)=>{this.listeners[r]||(this.listeners[r]=[]),this.listeners[r].push([t,n])};for(let t of this.plugins)if(typeof t=="object")for(let r in t){if(!iv[r]&&/^[A-Z]/.test(r))throw new Error(`Unknown event ${r} in ${t.postcssPlugin}. Try to update PostCSS (${this.processor.version} now).`);if(!nv[r])if(typeof t[r]=="object")for(let n in t[r])n==="*"?e(t,r,t[r][n]):e(t,r+"-"+n.toLowerCase(),t[r][n]);else typeof t[r]=="function"&&e(t,r,t[r])}this.hasListener=Object.keys(this.listeners).length>0}visitTick(e){let t=e[e.length-1],{node:r,visitors:n}=t;if(r.type!=="root"&&r.type!=="document"&&!r.parent){e.pop();return}if(n.length>0&&t.visitorIndex{Ns=i};cc.exports=Ve;Ve.default=Ve;tv.registerLazyResult(Ve);Z0.registerLazyResult(Ve)});var dc=v((NT,pc)=>{l();"use strict";var sv=As(),av=hr(),FT=Os(),ov=Wi(),lv=Ii(),Gi=class{constructor(e,t,r){t=t.toString(),this.stringified=!1,this._processor=e,this._css=t,this._opts=r,this._map=void 0;let n,a=av;this.result=new lv(this._processor,n,this._opts),this.result.css=t;let s=this;Object.defineProperty(this.result,"root",{get(){return s.root}});let o=new sv(a,n,this._opts,t);if(o.isMap()){let[u,c]=o.generate();u&&(this.result.css=u),c&&(this.result.map=c)}}get[Symbol.toStringTag](){return"NoWorkResult"}get processor(){return this.result.processor}get opts(){return this.result.opts}get css(){return this.result.css}get content(){return this.result.css}get map(){return this.result.map}get root(){if(this._root)return this._root;let e,t=ov;try{e=t(this._css,this._opts)}catch(r){this.error=r}if(this.error)throw this.error;return this._root=e,e}get messages(){return[]}warnings(){return[]}toString(){return this._css}then(e,t){return this.async().then(e,t)}catch(e){return this.async().catch(e)}finally(e){return this.async().then(e,e)}async(){return this.error?Promise.reject(this.error):Promise.resolve(this.result)}sync(){if(this.error)throw this.error;return this.result}};pc.exports=Gi;Gi.default=Gi});var mc=v((LT,hc)=>{l();"use strict";var uv=dc(),fv=Ls(),cv=Ti(),pv=_t(),Ot=class{constructor(e=[]){this.version="8.4.24",this.plugins=this.normalize(e)}use(e){return this.plugins=this.plugins.concat(this.normalize([e])),this}process(e,t={}){return this.plugins.length===0&&typeof t.parser=="undefined"&&typeof t.stringifier=="undefined"&&typeof t.syntax=="undefined"?new uv(this,e,t):new fv(this,e,t)}normalize(e){let t=[];for(let r of e)if(r.postcss===!0?r=r():r.postcss&&(r=r.postcss),typeof r=="object"&&Array.isArray(r.plugins))t=t.concat(r.plugins);else if(typeof r=="object"&&r.postcssPlugin)t.push(r);else if(typeof r=="function")t.push(r);else if(!(typeof r=="object"&&(r.parse||r.stringify)))throw new Error(r+" is not a PostCSS plugin");return t}};hc.exports=Ot;Ot.default=Ot;pv.registerProcessor(Ot);cv.registerProcessor(Ot)});var yc=v(($T,gc)=>{l();"use strict";var dv=gr(),hv=Is(),mv=yr(),gv=$i(),yv=Vi(),wv=_t(),bv=ji();function Cr(i,e){if(Array.isArray(i))return i.map(n=>Cr(n));let{inputs:t,...r}=i;if(t){e=[];for(let n of t){let a={...n,__proto__:yv.prototype};a.map&&(a.map={...a.map,__proto__:hv.prototype}),e.push(a)}}if(r.nodes&&(r.nodes=i.nodes.map(n=>Cr(n,e))),r.source){let{inputId:n,...a}=r.source;r.source=a,n!=null&&(r.source.input=e[n])}if(r.type==="root")return new wv(r);if(r.type==="decl")return new dv(r);if(r.type==="rule")return new bv(r);if(r.type==="comment")return new mv(r);if(r.type==="atrule")return new gv(r);throw new Error("Unknown node type: "+i.type)}gc.exports=Cr;Cr.default=Cr});var me=v((jT,Cc)=>{l();"use strict";var vv=ki(),wc=gr(),xv=Ls(),kv=it(),$s=mc(),Sv=hr(),Cv=yc(),bc=Ti(),Av=Ts(),vc=yr(),xc=$i(),_v=Ii(),Ev=Vi(),Ov=Wi(),Tv=Ds(),kc=ji(),Sc=_t(),Pv=mr();function j(...i){return i.length===1&&Array.isArray(i[0])&&(i=i[0]),new $s(i)}j.plugin=function(e,t){let r=!1;function n(...s){console&&console.warn&&!r&&(r=!0,console.warn(e+`: postcss.plugin was deprecated. Migration guide: +https://evilmartians.com/chronicles/postcss-8-plugin-migration`),m.env.LANG&&m.env.LANG.startsWith("cn")&&console.warn(e+`: \u91CC\u9762 postcss.plugin \u88AB\u5F03\u7528. \u8FC1\u79FB\u6307\u5357: +https://www.w3ctech.com/topic/2226`));let o=t(...s);return o.postcssPlugin=e,o.postcssVersion=new $s().version,o}let a;return Object.defineProperty(n,"postcss",{get(){return a||(a=n()),a}}),n.process=function(s,o,u){return j([n(u)]).process(s,o)},n};j.stringify=Sv;j.parse=Ov;j.fromJSON=Cv;j.list=Tv;j.comment=i=>new vc(i);j.atRule=i=>new xc(i);j.decl=i=>new wc(i);j.rule=i=>new kc(i);j.root=i=>new Sc(i);j.document=i=>new bc(i);j.CssSyntaxError=vv;j.Declaration=wc;j.Container=kv;j.Processor=$s;j.Document=bc;j.Comment=vc;j.Warning=Av;j.AtRule=xc;j.Result=_v;j.Input=Ev;j.Rule=kc;j.Root=Sc;j.Node=Pv;xv.registerPostcss(j);Cc.exports=j;j.default=j});var W,z,zT,VT,UT,WT,GT,HT,YT,QT,JT,XT,KT,ZT,eP,tP,rP,iP,nP,sP,aP,oP,lP,uP,fP,cP,nt=C(()=>{l();W=K(me()),z=W.default,zT=W.default.stringify,VT=W.default.fromJSON,UT=W.default.plugin,WT=W.default.parse,GT=W.default.list,HT=W.default.document,YT=W.default.comment,QT=W.default.atRule,JT=W.default.rule,XT=W.default.decl,KT=W.default.root,ZT=W.default.CssSyntaxError,eP=W.default.Declaration,tP=W.default.Container,rP=W.default.Processor,iP=W.default.Document,nP=W.default.Comment,sP=W.default.Warning,aP=W.default.AtRule,oP=W.default.Result,lP=W.default.Input,uP=W.default.Rule,fP=W.default.Root,cP=W.default.Node});var js=v((dP,Ac)=>{l();Ac.exports=function(i,e,t,r,n){for(e=e.split?e.split("."):e,r=0;r{l();"use strict";Hi.__esModule=!0;Hi.default=qv;function Dv(i){for(var e=i.toLowerCase(),t="",r=!1,n=0;n<6&&e[n]!==void 0;n++){var a=e.charCodeAt(n),s=a>=97&&a<=102||a>=48&&a<=57;if(r=a===32,!s)break;t+=e[n]}if(t.length!==0){var o=parseInt(t,16),u=o>=55296&&o<=57343;return u||o===0||o>1114111?["\uFFFD",t.length+(r?1:0)]:[String.fromCodePoint(o),t.length+(r?1:0)]}}var Iv=/\\/;function qv(i){var e=Iv.test(i);if(!e)return i;for(var t="",r=0;r{l();"use strict";Qi.__esModule=!0;Qi.default=Rv;function Rv(i){for(var e=arguments.length,t=new Array(e>1?e-1:0),r=1;r0;){var n=t.shift();if(!i[n])return;i=i[n]}return i}Ec.exports=Qi.default});var Pc=v((Ji,Tc)=>{l();"use strict";Ji.__esModule=!0;Ji.default=Mv;function Mv(i){for(var e=arguments.length,t=new Array(e>1?e-1:0),r=1;r0;){var n=t.shift();i[n]||(i[n]={}),i=i[n]}}Tc.exports=Ji.default});var Ic=v((Xi,Dc)=>{l();"use strict";Xi.__esModule=!0;Xi.default=Bv;function Bv(i){for(var e="",t=i.indexOf("/*"),r=0;t>=0;){e=e+i.slice(r,t);var n=i.indexOf("*/",t+2);if(n<0)return e;r=n+2,t=i.indexOf("/*",r)}return e=e+i.slice(r),e}Dc.exports=Xi.default});var Ar=v(Re=>{l();"use strict";Re.__esModule=!0;Re.unesc=Re.stripComments=Re.getProp=Re.ensureObject=void 0;var Fv=Ki(Yi());Re.unesc=Fv.default;var Nv=Ki(Oc());Re.getProp=Nv.default;var Lv=Ki(Pc());Re.ensureObject=Lv.default;var $v=Ki(Ic());Re.stripComments=$v.default;function Ki(i){return i&&i.__esModule?i:{default:i}}});var Ue=v((_r,Mc)=>{l();"use strict";_r.__esModule=!0;_r.default=void 0;var qc=Ar();function Rc(i,e){for(var t=0;tr||this.source.end.linen||this.source.end.line===r&&this.source.end.column{l();"use strict";G.__esModule=!0;G.UNIVERSAL=G.TAG=G.STRING=G.SELECTOR=G.ROOT=G.PSEUDO=G.NESTING=G.ID=G.COMMENT=G.COMBINATOR=G.CLASS=G.ATTRIBUTE=void 0;var Uv="tag";G.TAG=Uv;var Wv="string";G.STRING=Wv;var Gv="selector";G.SELECTOR=Gv;var Hv="root";G.ROOT=Hv;var Yv="pseudo";G.PSEUDO=Yv;var Qv="nesting";G.NESTING=Qv;var Jv="id";G.ID=Jv;var Xv="comment";G.COMMENT=Xv;var Kv="combinator";G.COMBINATOR=Kv;var Zv="class";G.CLASS=Zv;var ex="attribute";G.ATTRIBUTE=ex;var tx="universal";G.UNIVERSAL=tx});var Zi=v((Er,Lc)=>{l();"use strict";Er.__esModule=!0;Er.default=void 0;var rx=nx(Ue()),We=ix(ne());function Bc(i){if(typeof WeakMap!="function")return null;var e=new WeakMap,t=new WeakMap;return(Bc=function(n){return n?t:e})(i)}function ix(i,e){if(!e&&i&&i.__esModule)return i;if(i===null||typeof i!="object"&&typeof i!="function")return{default:i};var t=Bc(e);if(t&&t.has(i))return t.get(i);var r={},n=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in i)if(a!=="default"&&Object.prototype.hasOwnProperty.call(i,a)){var s=n?Object.getOwnPropertyDescriptor(i,a):null;s&&(s.get||s.set)?Object.defineProperty(r,a,s):r[a]=i[a]}return r.default=i,t&&t.set(i,r),r}function nx(i){return i&&i.__esModule?i:{default:i}}function sx(i,e){var t=typeof Symbol!="undefined"&&i[Symbol.iterator]||i["@@iterator"];if(t)return(t=t.call(i)).next.bind(t);if(Array.isArray(i)||(t=ax(i))||e&&i&&typeof i.length=="number"){t&&(i=t);var r=0;return function(){return r>=i.length?{done:!0}:{done:!1,value:i[r++]}}}throw new TypeError(`Invalid attempt to iterate non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function ax(i,e){if(!!i){if(typeof i=="string")return Fc(i,e);var t=Object.prototype.toString.call(i).slice(8,-1);if(t==="Object"&&i.constructor&&(t=i.constructor.name),t==="Map"||t==="Set")return Array.from(i);if(t==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t))return Fc(i,e)}}function Fc(i,e){(e==null||e>i.length)&&(e=i.length);for(var t=0,r=new Array(e);t=n&&(this.indexes[s]=a-1);return this},t.removeAll=function(){for(var n=sx(this.nodes),a;!(a=n()).done;){var s=a.value;s.parent=void 0}return this.nodes=[],this},t.empty=function(){return this.removeAll()},t.insertAfter=function(n,a){a.parent=this;var s=this.index(n);this.nodes.splice(s+1,0,a),a.parent=this;var o;for(var u in this.indexes)o=this.indexes[u],s<=o&&(this.indexes[u]=o+1);return this},t.insertBefore=function(n,a){a.parent=this;var s=this.index(n);this.nodes.splice(s,0,a),a.parent=this;var o;for(var u in this.indexes)o=this.indexes[u],o<=s&&(this.indexes[u]=o+1);return this},t._findChildAtPosition=function(n,a){var s=void 0;return this.each(function(o){if(o.atPosition){var u=o.atPosition(n,a);if(u)return s=u,!1}else if(o.isAtPosition(n,a))return s=o,!1}),s},t.atPosition=function(n,a){if(this.isAtPosition(n,a))return this._findChildAtPosition(n,a)||this},t._inferEndPosition=function(){this.last&&this.last.source&&this.last.source.end&&(this.source=this.source||{},this.source.end=this.source.end||{},Object.assign(this.source.end,this.last.source.end))},t.each=function(n){this.lastEach||(this.lastEach=0),this.indexes||(this.indexes={}),this.lastEach++;var a=this.lastEach;if(this.indexes[a]=0,!!this.length){for(var s,o;this.indexes[a]{l();"use strict";Or.__esModule=!0;Or.default=void 0;var fx=px(Zi()),cx=ne();function px(i){return i&&i.__esModule?i:{default:i}}function $c(i,e){for(var t=0;t{l();"use strict";Tr.__esModule=!0;Tr.default=void 0;var gx=wx(Zi()),yx=ne();function wx(i){return i&&i.__esModule?i:{default:i}}function bx(i,e){i.prototype=Object.create(e.prototype),i.prototype.constructor=i,Ws(i,e)}function Ws(i,e){return Ws=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(r,n){return r.__proto__=n,r},Ws(i,e)}var vx=function(i){bx(e,i);function e(t){var r;return r=i.call(this,t)||this,r.type=yx.SELECTOR,r}return e}(gx.default);Tr.default=vx;zc.exports=Tr.default});var en=v((gP,Vc)=>{l();"use strict";var xx={},kx=xx.hasOwnProperty,Sx=function(e,t){if(!e)return t;var r={};for(var n in t)r[n]=kx.call(e,n)?e[n]:t[n];return r},Cx=/[ -,\.\/:-@\[-\^`\{-~]/,Ax=/[ -,\.\/:-@\[\]\^`\{-~]/,_x=/(^|\\+)?(\\[A-F0-9]{1,6})\x20(?![a-fA-F0-9\x20])/g,Hs=function i(e,t){t=Sx(t,i.options),t.quotes!="single"&&t.quotes!="double"&&(t.quotes="single");for(var r=t.quotes=="double"?'"':"'",n=t.isIdentifier,a=e.charAt(0),s="",o=0,u=e.length;o126){if(f>=55296&&f<=56319&&o{l();"use strict";Pr.__esModule=!0;Pr.default=void 0;var Ex=Uc(en()),Ox=Ar(),Tx=Uc(Ue()),Px=ne();function Uc(i){return i&&i.__esModule?i:{default:i}}function Wc(i,e){for(var t=0;t{l();"use strict";Dr.__esModule=!0;Dr.default=void 0;var Rx=Bx(Ue()),Mx=ne();function Bx(i){return i&&i.__esModule?i:{default:i}}function Fx(i,e){i.prototype=Object.create(e.prototype),i.prototype.constructor=i,Js(i,e)}function Js(i,e){return Js=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(r,n){return r.__proto__=n,r},Js(i,e)}var Nx=function(i){Fx(e,i);function e(t){var r;return r=i.call(this,t)||this,r.type=Mx.COMMENT,r}return e}(Rx.default);Dr.default=Nx;Hc.exports=Dr.default});var Zs=v((Ir,Yc)=>{l();"use strict";Ir.__esModule=!0;Ir.default=void 0;var Lx=jx(Ue()),$x=ne();function jx(i){return i&&i.__esModule?i:{default:i}}function zx(i,e){i.prototype=Object.create(e.prototype),i.prototype.constructor=i,Ks(i,e)}function Ks(i,e){return Ks=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(r,n){return r.__proto__=n,r},Ks(i,e)}var Vx=function(i){zx(e,i);function e(r){var n;return n=i.call(this,r)||this,n.type=$x.ID,n}var t=e.prototype;return t.valueToString=function(){return"#"+i.prototype.valueToString.call(this)},e}(Lx.default);Ir.default=Vx;Yc.exports=Ir.default});var tn=v((qr,Xc)=>{l();"use strict";qr.__esModule=!0;qr.default=void 0;var Ux=Qc(en()),Wx=Ar(),Gx=Qc(Ue());function Qc(i){return i&&i.__esModule?i:{default:i}}function Jc(i,e){for(var t=0;t{l();"use strict";Rr.__esModule=!0;Rr.default=void 0;var Jx=Kx(tn()),Xx=ne();function Kx(i){return i&&i.__esModule?i:{default:i}}function Zx(i,e){i.prototype=Object.create(e.prototype),i.prototype.constructor=i,ta(i,e)}function ta(i,e){return ta=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(r,n){return r.__proto__=n,r},ta(i,e)}var e1=function(i){Zx(e,i);function e(t){var r;return r=i.call(this,t)||this,r.type=Xx.TAG,r}return e}(Jx.default);Rr.default=e1;Kc.exports=Rr.default});var na=v((Mr,Zc)=>{l();"use strict";Mr.__esModule=!0;Mr.default=void 0;var t1=i1(Ue()),r1=ne();function i1(i){return i&&i.__esModule?i:{default:i}}function n1(i,e){i.prototype=Object.create(e.prototype),i.prototype.constructor=i,ia(i,e)}function ia(i,e){return ia=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(r,n){return r.__proto__=n,r},ia(i,e)}var s1=function(i){n1(e,i);function e(t){var r;return r=i.call(this,t)||this,r.type=r1.STRING,r}return e}(t1.default);Mr.default=s1;Zc.exports=Mr.default});var aa=v((Br,ep)=>{l();"use strict";Br.__esModule=!0;Br.default=void 0;var a1=l1(Zi()),o1=ne();function l1(i){return i&&i.__esModule?i:{default:i}}function u1(i,e){i.prototype=Object.create(e.prototype),i.prototype.constructor=i,sa(i,e)}function sa(i,e){return sa=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(r,n){return r.__proto__=n,r},sa(i,e)}var f1=function(i){u1(e,i);function e(r){var n;return n=i.call(this,r)||this,n.type=o1.PSEUDO,n}var t=e.prototype;return t.toString=function(){var n=this.length?"("+this.map(String).join(",")+")":"";return[this.rawSpaceBefore,this.stringifyProperty("value"),n,this.rawSpaceAfter].join("")},e}(a1.default);Br.default=f1;ep.exports=Br.default});var tp={};Ae(tp,{deprecate:()=>c1});function c1(i){return i}var rp=C(()=>{l()});var np=v((yP,ip)=>{l();ip.exports=(rp(),tp).deprecate});var pa=v(Lr=>{l();"use strict";Lr.__esModule=!0;Lr.default=void 0;Lr.unescapeValue=fa;var Fr=la(en()),p1=la(Yi()),d1=la(tn()),h1=ne(),oa;function la(i){return i&&i.__esModule?i:{default:i}}function sp(i,e){for(var t=0;t0&&!n.quoted&&o.before.length===0&&!(n.spaces.value&&n.spaces.value.after)&&(o.before=" "),ap(s,o)}))),a.push("]"),a.push(this.rawSpaceAfter),a.join("")},m1(e,[{key:"quoted",get:function(){var n=this.quoteMark;return n==="'"||n==='"'},set:function(n){b1()}},{key:"quoteMark",get:function(){return this._quoteMark},set:function(n){if(!this._constructed){this._quoteMark=n;return}this._quoteMark!==n&&(this._quoteMark=n,this._syncRawValue())}},{key:"qualifiedAttribute",get:function(){return this.qualifiedName(this.raws.attribute||this.attribute)}},{key:"insensitiveFlag",get:function(){return this.insensitive?"i":""}},{key:"value",get:function(){return this._value},set:function(n){if(this._constructed){var a=fa(n),s=a.deprecatedUsage,o=a.unescaped,u=a.quoteMark;if(s&&w1(),o===this._value&&u===this._quoteMark)return;this._value=o,this._quoteMark=u,this._syncRawValue()}else this._value=n}},{key:"insensitive",get:function(){return this._insensitive},set:function(n){n||(this._insensitive=!1,this.raws&&(this.raws.insensitiveFlag==="I"||this.raws.insensitiveFlag==="i")&&(this.raws.insensitiveFlag=void 0)),this._insensitive=n}},{key:"attribute",get:function(){return this._attribute},set:function(n){this._handleEscapes("attribute",n),this._attribute=n}}]),e}(d1.default);Lr.default=rn;rn.NO_QUOTE=null;rn.SINGLE_QUOTE="'";rn.DOUBLE_QUOTE='"';var ca=(oa={"'":{quotes:"single",wrap:!0},'"':{quotes:"double",wrap:!0}},oa[null]={isIdentifier:!0},oa);function ap(i,e){return""+e.before+i+e.after}});var ha=v(($r,op)=>{l();"use strict";$r.__esModule=!0;$r.default=void 0;var k1=C1(tn()),S1=ne();function C1(i){return i&&i.__esModule?i:{default:i}}function A1(i,e){i.prototype=Object.create(e.prototype),i.prototype.constructor=i,da(i,e)}function da(i,e){return da=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(r,n){return r.__proto__=n,r},da(i,e)}var _1=function(i){A1(e,i);function e(t){var r;return r=i.call(this,t)||this,r.type=S1.UNIVERSAL,r.value="*",r}return e}(k1.default);$r.default=_1;op.exports=$r.default});var ga=v((jr,lp)=>{l();"use strict";jr.__esModule=!0;jr.default=void 0;var E1=T1(Ue()),O1=ne();function T1(i){return i&&i.__esModule?i:{default:i}}function P1(i,e){i.prototype=Object.create(e.prototype),i.prototype.constructor=i,ma(i,e)}function ma(i,e){return ma=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(r,n){return r.__proto__=n,r},ma(i,e)}var D1=function(i){P1(e,i);function e(t){var r;return r=i.call(this,t)||this,r.type=O1.COMBINATOR,r}return e}(E1.default);jr.default=D1;lp.exports=jr.default});var wa=v((zr,up)=>{l();"use strict";zr.__esModule=!0;zr.default=void 0;var I1=R1(Ue()),q1=ne();function R1(i){return i&&i.__esModule?i:{default:i}}function M1(i,e){i.prototype=Object.create(e.prototype),i.prototype.constructor=i,ya(i,e)}function ya(i,e){return ya=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(r,n){return r.__proto__=n,r},ya(i,e)}var B1=function(i){M1(e,i);function e(t){var r;return r=i.call(this,t)||this,r.type=q1.NESTING,r.value="&",r}return e}(I1.default);zr.default=B1;up.exports=zr.default});var cp=v((nn,fp)=>{l();"use strict";nn.__esModule=!0;nn.default=F1;function F1(i){return i.sort(function(e,t){return e-t})}fp.exports=nn.default});var ba=v(D=>{l();"use strict";D.__esModule=!0;D.word=D.tilde=D.tab=D.str=D.space=D.slash=D.singleQuote=D.semicolon=D.plus=D.pipe=D.openSquare=D.openParenthesis=D.newline=D.greaterThan=D.feed=D.equals=D.doubleQuote=D.dollar=D.cr=D.comment=D.comma=D.combinator=D.colon=D.closeSquare=D.closeParenthesis=D.caret=D.bang=D.backslash=D.at=D.asterisk=D.ampersand=void 0;var N1=38;D.ampersand=N1;var L1=42;D.asterisk=L1;var $1=64;D.at=$1;var j1=44;D.comma=j1;var z1=58;D.colon=z1;var V1=59;D.semicolon=V1;var U1=40;D.openParenthesis=U1;var W1=41;D.closeParenthesis=W1;var G1=91;D.openSquare=G1;var H1=93;D.closeSquare=H1;var Y1=36;D.dollar=Y1;var Q1=126;D.tilde=Q1;var J1=94;D.caret=J1;var X1=43;D.plus=X1;var K1=61;D.equals=K1;var Z1=124;D.pipe=Z1;var ek=62;D.greaterThan=ek;var tk=32;D.space=tk;var pp=39;D.singleQuote=pp;var rk=34;D.doubleQuote=rk;var ik=47;D.slash=ik;var nk=33;D.bang=nk;var sk=92;D.backslash=sk;var ak=13;D.cr=ak;var ok=12;D.feed=ok;var lk=10;D.newline=lk;var uk=9;D.tab=uk;var fk=pp;D.str=fk;var ck=-1;D.comment=ck;var pk=-2;D.word=pk;var dk=-3;D.combinator=dk});var mp=v(Vr=>{l();"use strict";Vr.__esModule=!0;Vr.FIELDS=void 0;Vr.default=vk;var O=hk(ba()),Tt,V;function dp(i){if(typeof WeakMap!="function")return null;var e=new WeakMap,t=new WeakMap;return(dp=function(n){return n?t:e})(i)}function hk(i,e){if(!e&&i&&i.__esModule)return i;if(i===null||typeof i!="object"&&typeof i!="function")return{default:i};var t=dp(e);if(t&&t.has(i))return t.get(i);var r={},n=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in i)if(a!=="default"&&Object.prototype.hasOwnProperty.call(i,a)){var s=n?Object.getOwnPropertyDescriptor(i,a):null;s&&(s.get||s.set)?Object.defineProperty(r,a,s):r[a]=i[a]}return r.default=i,t&&t.set(i,r),r}var mk=(Tt={},Tt[O.tab]=!0,Tt[O.newline]=!0,Tt[O.cr]=!0,Tt[O.feed]=!0,Tt),gk=(V={},V[O.space]=!0,V[O.tab]=!0,V[O.newline]=!0,V[O.cr]=!0,V[O.feed]=!0,V[O.ampersand]=!0,V[O.asterisk]=!0,V[O.bang]=!0,V[O.comma]=!0,V[O.colon]=!0,V[O.semicolon]=!0,V[O.openParenthesis]=!0,V[O.closeParenthesis]=!0,V[O.openSquare]=!0,V[O.closeSquare]=!0,V[O.singleQuote]=!0,V[O.doubleQuote]=!0,V[O.plus]=!0,V[O.pipe]=!0,V[O.tilde]=!0,V[O.greaterThan]=!0,V[O.equals]=!0,V[O.dollar]=!0,V[O.caret]=!0,V[O.slash]=!0,V),va={},hp="0123456789abcdefABCDEF";for(sn=0;sn0?(k=s+x,S=b-w[x].length):(k=s,S=a),E=O.comment,s=k,d=k,p=b-S):c===O.slash?(b=o,E=c,d=s,p=o-a,u=b+1):(b=yk(t,o),E=O.word,d=s,p=b-a),u=b+1;break}e.push([E,s,o-a,d,p,o,u]),S&&(a=S,S=null),o=u}return e}});var Sp=v((Ur,kp)=>{l();"use strict";Ur.__esModule=!0;Ur.default=void 0;var xk=we(Us()),xa=we(Gs()),kk=we(Qs()),gp=we(Xs()),Sk=we(Zs()),Ck=we(ra()),ka=we(na()),Ak=we(aa()),yp=an(pa()),_k=we(ha()),Sa=we(ga()),Ek=we(wa()),Ok=we(cp()),A=an(mp()),T=an(ba()),Tk=an(ne()),Y=Ar(),yt,Ca;function wp(i){if(typeof WeakMap!="function")return null;var e=new WeakMap,t=new WeakMap;return(wp=function(n){return n?t:e})(i)}function an(i,e){if(!e&&i&&i.__esModule)return i;if(i===null||typeof i!="object"&&typeof i!="function")return{default:i};var t=wp(e);if(t&&t.has(i))return t.get(i);var r={},n=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in i)if(a!=="default"&&Object.prototype.hasOwnProperty.call(i,a)){var s=n?Object.getOwnPropertyDescriptor(i,a):null;s&&(s.get||s.set)?Object.defineProperty(r,a,s):r[a]=i[a]}return r.default=i,t&&t.set(i,r),r}function we(i){return i&&i.__esModule?i:{default:i}}function bp(i,e){for(var t=0;t0){var s=this.current.last;if(s){var o=this.convertWhitespaceNodesToSpace(a),u=o.space,c=o.rawSpace;c!==void 0&&(s.rawSpaceAfter+=c),s.spaces.after+=u}else a.forEach(function(E){return r.newNode(E)})}return}var f=this.currToken,p=void 0;n>this.position&&(p=this.parseWhitespaceEquivalentTokens(n));var d;if(this.isNamedCombinator()?d=this.namedCombinator():this.currToken[A.FIELDS.TYPE]===T.combinator?(d=new Sa.default({value:this.content(),source:Pt(this.currToken),sourceIndex:this.currToken[A.FIELDS.START_POS]}),this.position++):Aa[this.currToken[A.FIELDS.TYPE]]||p||this.unexpected(),d){if(p){var h=this.convertWhitespaceNodesToSpace(p),y=h.space,x=h.rawSpace;d.spaces.before=y,d.rawSpaceBefore=x}}else{var w=this.convertWhitespaceNodesToSpace(p,!0),b=w.space,k=w.rawSpace;k||(k=b);var S={},_={spaces:{}};b.endsWith(" ")&&k.endsWith(" ")?(S.before=b.slice(0,b.length-1),_.spaces.before=k.slice(0,k.length-1)):b.startsWith(" ")&&k.startsWith(" ")?(S.after=b.slice(1),_.spaces.after=k.slice(1)):_.value=k,d=new Sa.default({value:" ",source:_a(f,this.tokens[this.position-1]),sourceIndex:f[A.FIELDS.START_POS],spaces:S,raws:_})}return this.currToken&&this.currToken[A.FIELDS.TYPE]===T.space&&(d.spaces.after=this.optionalSpace(this.content()),this.position++),this.newNode(d)},e.comma=function(){if(this.position===this.tokens.length-1){this.root.trailingComma=!0,this.position++;return}this.current._inferEndPosition();var r=new xa.default({source:{start:vp(this.tokens[this.position+1])}});this.current.parent.append(r),this.current=r,this.position++},e.comment=function(){var r=this.currToken;this.newNode(new gp.default({value:this.content(),source:Pt(r),sourceIndex:r[A.FIELDS.START_POS]})),this.position++},e.error=function(r,n){throw this.root.error(r,n)},e.missingBackslash=function(){return this.error("Expected a backslash preceding the semicolon.",{index:this.currToken[A.FIELDS.START_POS]})},e.missingParenthesis=function(){return this.expected("opening parenthesis",this.currToken[A.FIELDS.START_POS])},e.missingSquareBracket=function(){return this.expected("opening square bracket",this.currToken[A.FIELDS.START_POS])},e.unexpected=function(){return this.error("Unexpected '"+this.content()+"'. Escaping special characters with \\ may help.",this.currToken[A.FIELDS.START_POS])},e.unexpectedPipe=function(){return this.error("Unexpected '|'.",this.currToken[A.FIELDS.START_POS])},e.namespace=function(){var r=this.prevToken&&this.content(this.prevToken)||!0;if(this.nextToken[A.FIELDS.TYPE]===T.word)return this.position++,this.word(r);if(this.nextToken[A.FIELDS.TYPE]===T.asterisk)return this.position++,this.universal(r);this.unexpectedPipe()},e.nesting=function(){if(this.nextToken){var r=this.content(this.nextToken);if(r==="|"){this.position++;return}}var n=this.currToken;this.newNode(new Ek.default({value:this.content(),source:Pt(n),sourceIndex:n[A.FIELDS.START_POS]})),this.position++},e.parentheses=function(){var r=this.current.last,n=1;if(this.position++,r&&r.type===Tk.PSEUDO){var a=new xa.default({source:{start:vp(this.tokens[this.position-1])}}),s=this.current;for(r.append(a),this.current=a;this.position1&&r.nextToken&&r.nextToken[A.FIELDS.TYPE]===T.openParenthesis&&r.error("Misplaced parenthesis.",{index:r.nextToken[A.FIELDS.START_POS]})});else return this.expected(["pseudo-class","pseudo-element"],this.currToken[A.FIELDS.START_POS])},e.space=function(){var r=this.content();this.position===0||this.prevToken[A.FIELDS.TYPE]===T.comma||this.prevToken[A.FIELDS.TYPE]===T.openParenthesis||this.current.nodes.every(function(n){return n.type==="comment"})?(this.spaces=this.optionalSpace(r),this.position++):this.position===this.tokens.length-1||this.nextToken[A.FIELDS.TYPE]===T.comma||this.nextToken[A.FIELDS.TYPE]===T.closeParenthesis?(this.current.last.spaces.after=this.optionalSpace(r),this.position++):this.combinator()},e.string=function(){var r=this.currToken;this.newNode(new ka.default({value:this.content(),source:Pt(r),sourceIndex:r[A.FIELDS.START_POS]})),this.position++},e.universal=function(r){var n=this.nextToken;if(n&&this.content(n)==="|")return this.position++,this.namespace();var a=this.currToken;this.newNode(new _k.default({value:this.content(),source:Pt(a),sourceIndex:a[A.FIELDS.START_POS]}),r),this.position++},e.splitWord=function(r,n){for(var a=this,s=this.nextToken,o=this.content();s&&~[T.dollar,T.caret,T.equals,T.word].indexOf(s[A.FIELDS.TYPE]);){this.position++;var u=this.content();if(o+=u,u.lastIndexOf("\\")===u.length-1){var c=this.nextToken;c&&c[A.FIELDS.TYPE]===T.space&&(o+=this.requiredSpace(this.content(c)),this.position++)}s=this.nextToken}var f=Ea(o,".").filter(function(y){var x=o[y-1]==="\\",w=/^\d+\.\d+%$/.test(o);return!x&&!w}),p=Ea(o,"#").filter(function(y){return o[y-1]!=="\\"}),d=Ea(o,"#{");d.length&&(p=p.filter(function(y){return!~d.indexOf(y)}));var h=(0,Ok.default)(Ik([0].concat(f,p)));h.forEach(function(y,x){var w=h[x+1]||o.length,b=o.slice(y,w);if(x===0&&n)return n.call(a,b,h.length);var k,S=a.currToken,_=S[A.FIELDS.START_POS]+h[x],E=wt(S[1],S[2]+y,S[3],S[2]+(w-1));if(~f.indexOf(y)){var I={value:b.slice(1),source:E,sourceIndex:_};k=new kk.default(Dt(I,"value"))}else if(~p.indexOf(y)){var B={value:b.slice(1),source:E,sourceIndex:_};k=new Sk.default(Dt(B,"value"))}else{var q={value:b,source:E,sourceIndex:_};Dt(q,"value"),k=new Ck.default(q)}a.newNode(k,r),r=null}),this.position++},e.word=function(r){var n=this.nextToken;return n&&this.content(n)==="|"?(this.position++,this.namespace()):this.splitWord(r)},e.loop=function(){for(;this.position{l();"use strict";Wr.__esModule=!0;Wr.default=void 0;var Rk=Mk(Sp());function Mk(i){return i&&i.__esModule?i:{default:i}}var Bk=function(){function i(t,r){this.func=t||function(){},this.funcRes=null,this.options=r}var e=i.prototype;return e._shouldUpdateSelector=function(r,n){n===void 0&&(n={});var a=Object.assign({},this.options,n);return a.updateSelector===!1?!1:typeof r!="string"},e._isLossy=function(r){r===void 0&&(r={});var n=Object.assign({},this.options,r);return n.lossless===!1},e._root=function(r,n){n===void 0&&(n={});var a=new Rk.default(r,this._parseOptions(n));return a.root},e._parseOptions=function(r){return{lossy:this._isLossy(r)}},e._run=function(r,n){var a=this;return n===void 0&&(n={}),new Promise(function(s,o){try{var u=a._root(r,n);Promise.resolve(a.func(u)).then(function(c){var f=void 0;return a._shouldUpdateSelector(r,n)&&(f=u.toString(),r.selector=f),{transform:c,root:u,string:f}}).then(s,o)}catch(c){o(c);return}})},e._runSync=function(r,n){n===void 0&&(n={});var a=this._root(r,n),s=this.func(a);if(s&&typeof s.then=="function")throw new Error("Selector processor returned a promise to a synchronous call.");var o=void 0;return n.updateSelector&&typeof r!="string"&&(o=a.toString(),r.selector=o),{transform:s,root:a,string:o}},e.ast=function(r,n){return this._run(r,n).then(function(a){return a.root})},e.astSync=function(r,n){return this._runSync(r,n).root},e.transform=function(r,n){return this._run(r,n).then(function(a){return a.transform})},e.transformSync=function(r,n){return this._runSync(r,n).transform},e.process=function(r,n){return this._run(r,n).then(function(a){return a.string||a.root.toString()})},e.processSync=function(r,n){var a=this._runSync(r,n);return a.string||a.root.toString()},i}();Wr.default=Bk;Cp.exports=Wr.default});var _p=v(H=>{l();"use strict";H.__esModule=!0;H.universal=H.tag=H.string=H.selector=H.root=H.pseudo=H.nesting=H.id=H.comment=H.combinator=H.className=H.attribute=void 0;var Fk=be(pa()),Nk=be(Qs()),Lk=be(ga()),$k=be(Xs()),jk=be(Zs()),zk=be(wa()),Vk=be(aa()),Uk=be(Us()),Wk=be(Gs()),Gk=be(na()),Hk=be(ra()),Yk=be(ha());function be(i){return i&&i.__esModule?i:{default:i}}var Qk=function(e){return new Fk.default(e)};H.attribute=Qk;var Jk=function(e){return new Nk.default(e)};H.className=Jk;var Xk=function(e){return new Lk.default(e)};H.combinator=Xk;var Kk=function(e){return new $k.default(e)};H.comment=Kk;var Zk=function(e){return new jk.default(e)};H.id=Zk;var eS=function(e){return new zk.default(e)};H.nesting=eS;var tS=function(e){return new Vk.default(e)};H.pseudo=tS;var rS=function(e){return new Uk.default(e)};H.root=rS;var iS=function(e){return new Wk.default(e)};H.selector=iS;var nS=function(e){return new Gk.default(e)};H.string=nS;var sS=function(e){return new Hk.default(e)};H.tag=sS;var aS=function(e){return new Yk.default(e)};H.universal=aS});var Pp=v(L=>{l();"use strict";L.__esModule=!0;L.isComment=L.isCombinator=L.isClassName=L.isAttribute=void 0;L.isContainer=wS;L.isIdentifier=void 0;L.isNamespace=bS;L.isNesting=void 0;L.isNode=Oa;L.isPseudo=void 0;L.isPseudoClass=yS;L.isPseudoElement=Tp;L.isUniversal=L.isTag=L.isString=L.isSelector=L.isRoot=void 0;var Q=ne(),fe,oS=(fe={},fe[Q.ATTRIBUTE]=!0,fe[Q.CLASS]=!0,fe[Q.COMBINATOR]=!0,fe[Q.COMMENT]=!0,fe[Q.ID]=!0,fe[Q.NESTING]=!0,fe[Q.PSEUDO]=!0,fe[Q.ROOT]=!0,fe[Q.SELECTOR]=!0,fe[Q.STRING]=!0,fe[Q.TAG]=!0,fe[Q.UNIVERSAL]=!0,fe);function Oa(i){return typeof i=="object"&&oS[i.type]}function ve(i,e){return Oa(e)&&e.type===i}var Ep=ve.bind(null,Q.ATTRIBUTE);L.isAttribute=Ep;var lS=ve.bind(null,Q.CLASS);L.isClassName=lS;var uS=ve.bind(null,Q.COMBINATOR);L.isCombinator=uS;var fS=ve.bind(null,Q.COMMENT);L.isComment=fS;var cS=ve.bind(null,Q.ID);L.isIdentifier=cS;var pS=ve.bind(null,Q.NESTING);L.isNesting=pS;var Ta=ve.bind(null,Q.PSEUDO);L.isPseudo=Ta;var dS=ve.bind(null,Q.ROOT);L.isRoot=dS;var hS=ve.bind(null,Q.SELECTOR);L.isSelector=hS;var mS=ve.bind(null,Q.STRING);L.isString=mS;var Op=ve.bind(null,Q.TAG);L.isTag=Op;var gS=ve.bind(null,Q.UNIVERSAL);L.isUniversal=gS;function Tp(i){return Ta(i)&&i.value&&(i.value.startsWith("::")||i.value.toLowerCase()===":before"||i.value.toLowerCase()===":after"||i.value.toLowerCase()===":first-letter"||i.value.toLowerCase()===":first-line")}function yS(i){return Ta(i)&&!Tp(i)}function wS(i){return!!(Oa(i)&&i.walk)}function bS(i){return Ep(i)||Op(i)}});var Dp=v(Oe=>{l();"use strict";Oe.__esModule=!0;var Pa=ne();Object.keys(Pa).forEach(function(i){i==="default"||i==="__esModule"||i in Oe&&Oe[i]===Pa[i]||(Oe[i]=Pa[i])});var Da=_p();Object.keys(Da).forEach(function(i){i==="default"||i==="__esModule"||i in Oe&&Oe[i]===Da[i]||(Oe[i]=Da[i])});var Ia=Pp();Object.keys(Ia).forEach(function(i){i==="default"||i==="__esModule"||i in Oe&&Oe[i]===Ia[i]||(Oe[i]=Ia[i])})});var Me=v((Gr,qp)=>{l();"use strict";Gr.__esModule=!0;Gr.default=void 0;var vS=SS(Ap()),xS=kS(Dp());function Ip(i){if(typeof WeakMap!="function")return null;var e=new WeakMap,t=new WeakMap;return(Ip=function(n){return n?t:e})(i)}function kS(i,e){if(!e&&i&&i.__esModule)return i;if(i===null||typeof i!="object"&&typeof i!="function")return{default:i};var t=Ip(e);if(t&&t.has(i))return t.get(i);var r={},n=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in i)if(a!=="default"&&Object.prototype.hasOwnProperty.call(i,a)){var s=n?Object.getOwnPropertyDescriptor(i,a):null;s&&(s.get||s.set)?Object.defineProperty(r,a,s):r[a]=i[a]}return r.default=i,t&&t.set(i,r),r}function SS(i){return i&&i.__esModule?i:{default:i}}var qa=function(e){return new vS.default(e)};Object.assign(qa,xS);delete qa.__esModule;var CS=qa;Gr.default=CS;qp.exports=Gr.default});function Ge(i){return["fontSize","outline"].includes(i)?e=>(typeof e=="function"&&(e=e({})),Array.isArray(e)&&(e=e[0]),e):i==="fontFamily"?e=>{typeof e=="function"&&(e=e({}));let t=Array.isArray(e)&&ie(e[1])?e[0]:e;return Array.isArray(t)?t.join(", "):t}:["boxShadow","transitionProperty","transitionDuration","transitionDelay","transitionTimingFunction","backgroundImage","backgroundSize","backgroundColor","cursor","animation"].includes(i)?e=>(typeof e=="function"&&(e=e({})),Array.isArray(e)&&(e=e.join(", ")),e):["gridTemplateColumns","gridTemplateRows","objectPosition"].includes(i)?e=>(typeof e=="function"&&(e=e({})),typeof e=="string"&&(e=z.list.comma(e).join(" ")),e):(e,t={})=>(typeof e=="function"&&(e=e(t)),e)}var Hr=C(()=>{l();nt();xt()});var $p=v((EP,Na)=>{l();var{Rule:Rp,AtRule:AS}=me(),Mp=Me();function Ra(i,e){let t;try{Mp(r=>{t=r}).processSync(i)}catch(r){throw i.includes(":")?e?e.error("Missed semicolon"):r:e?e.error(r.message):r}return t.at(0)}function Bp(i,e){let t=!1;return i.each(r=>{if(r.type==="nesting"){let n=e.clone({});r.value!=="&"?r.replaceWith(Ra(r.value.replace("&",n.toString()))):r.replaceWith(n),t=!0}else"nodes"in r&&r.nodes&&Bp(r,e)&&(t=!0)}),t}function Fp(i,e){let t=[];return i.selectors.forEach(r=>{let n=Ra(r,i);e.selectors.forEach(a=>{if(!a)return;let s=Ra(a,e);Bp(s,n)||(s.prepend(Mp.combinator({value:" "})),s.prepend(n.clone({}))),t.push(s.toString())})}),t}function on(i,e){let t=i.prev();for(e.after(i);t&&t.type==="comment";){let r=t.prev();e.after(t),t=r}return i}function _S(i){return function e(t,r,n,a=n){let s=[];if(r.each(o=>{o.type==="rule"&&n?a&&(o.selectors=Fp(t,o)):o.type==="atrule"&&o.nodes?i[o.name]?e(t,o,a):r[Ba]!==!1&&s.push(o):s.push(o)}),n&&s.length){let o=t.clone({nodes:[]});for(let u of s)o.append(u);r.prepend(o)}}}function Ma(i,e,t){let r=new Rp({selector:i,nodes:[]});return r.append(e),t.after(r),r}function Np(i,e){let t={};for(let r of i)t[r]=!0;if(e)for(let r of e)t[r.replace(/^@/,"")]=!0;return t}function ES(i){i=i.trim();let e=i.match(/^\((.*)\)$/);if(!e)return{type:"basic",selector:i};let t=e[1].match(/^(with(?:out)?):(.+)$/);if(t){let r=t[1]==="with",n=Object.fromEntries(t[2].trim().split(/\s+/).map(s=>[s,!0]));if(r&&n.all)return{type:"noop"};let a=s=>!!n[s];return n.all?a=()=>!0:r&&(a=s=>s==="all"?!1:!n[s]),{type:"withrules",escapes:a}}return{type:"unknown"}}function OS(i){let e=[],t=i.parent;for(;t&&t instanceof AS;)e.push(t),t=t.parent;return e}function TS(i){let e=i[Lp];if(!e)i.after(i.nodes);else{let t=i.nodes,r,n=-1,a,s,o,u=OS(i);if(u.forEach((c,f)=>{if(e(c.name))r=c,n=f,s=o;else{let p=o;o=c.clone({nodes:[]}),p&&o.append(p),a=a||o}}),r?s?(a.append(t),r.after(s)):r.after(t):i.after(t),i.next()&&r){let c;u.slice(0,n+1).forEach((f,p,d)=>{let h=c;c=f.clone({nodes:[]}),h&&c.append(h);let y=[],w=(d[p-1]||i).next();for(;w;)y.push(w),w=w.next();c.append(y)}),c&&(s||t[t.length-1]).after(c)}}i.remove()}var Ba=Symbol("rootRuleMergeSel"),Lp=Symbol("rootRuleEscapes");function PS(i){let{params:e}=i,{type:t,selector:r,escapes:n}=ES(e);if(t==="unknown")throw i.error(`Unknown @${i.name} parameter ${JSON.stringify(e)}`);if(t==="basic"&&r){let a=new Rp({selector:r,nodes:i.nodes});i.removeAll(),i.append(a)}i[Lp]=n,i[Ba]=n?!n("all"):t==="noop"}var Fa=Symbol("hasRootRule");Na.exports=(i={})=>{let e=Np(["media","supports","layer","container"],i.bubble),t=_S(e),r=Np(["document","font-face","keyframes","-webkit-keyframes","-moz-keyframes"],i.unwrap),n=(i.rootRuleName||"at-root").replace(/^@/,""),a=i.preserveEmpty;return{postcssPlugin:"postcss-nested",Once(s){s.walkAtRules(n,o=>{PS(o),s[Fa]=!0})},Rule(s){let o=!1,u=s,c=!1,f=[];s.each(p=>{p.type==="rule"?(f.length&&(u=Ma(s.selector,f,u),f=[]),c=!0,o=!0,p.selectors=Fp(s,p),u=on(p,u)):p.type==="atrule"?(f.length&&(u=Ma(s.selector,f,u),f=[]),p.name===n?(o=!0,t(s,p,!0,p[Ba]),u=on(p,u)):e[p.name]?(c=!0,o=!0,t(s,p,!0),u=on(p,u)):r[p.name]?(c=!0,o=!0,t(s,p,!1),u=on(p,u)):c&&f.push(p)):p.type==="decl"&&c&&f.push(p)}),f.length&&(u=Ma(s.selector,f,u)),o&&a!==!0&&(s.raws.semicolon=!0,s.nodes.length===0&&s.remove())},RootExit(s){s[Fa]&&(s.walkAtRules(n,TS),s[Fa]=!1)}}};Na.exports.postcss=!0});var Up=v((OP,Vp)=>{l();"use strict";var jp=/-(\w|$)/g,zp=(i,e)=>e.toUpperCase(),DS=i=>(i=i.toLowerCase(),i==="float"?"cssFloat":i.startsWith("-ms-")?i.substr(1).replace(jp,zp):i.replace(jp,zp));Vp.exports=DS});var ja=v((TP,Wp)=>{l();var IS=Up(),qS={boxFlex:!0,boxFlexGroup:!0,columnCount:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,strokeDashoffset:!0,strokeOpacity:!0,strokeWidth:!0};function La(i){return typeof i.nodes=="undefined"?!0:$a(i)}function $a(i){let e,t={};return i.each(r=>{if(r.type==="atrule")e="@"+r.name,r.params&&(e+=" "+r.params),typeof t[e]=="undefined"?t[e]=La(r):Array.isArray(t[e])?t[e].push(La(r)):t[e]=[t[e],La(r)];else if(r.type==="rule"){let n=$a(r);if(t[r.selector])for(let a in n)t[r.selector][a]=n[a];else t[r.selector]=n}else if(r.type==="decl"){r.prop[0]==="-"&&r.prop[1]==="-"||r.parent&&r.parent.selector===":export"?e=r.prop:e=IS(r.prop);let n=r.value;!isNaN(r.value)&&qS[e]&&(n=parseFloat(r.value)),r.important&&(n+=" !important"),typeof t[e]=="undefined"?t[e]=n:Array.isArray(t[e])?t[e].push(n):t[e]=[t[e],n]}}),t}Wp.exports=$a});var ln=v((PP,Qp)=>{l();var Yr=me(),Gp=/\s*!important\s*$/i,RS={"box-flex":!0,"box-flex-group":!0,"column-count":!0,flex:!0,"flex-grow":!0,"flex-positive":!0,"flex-shrink":!0,"flex-negative":!0,"font-weight":!0,"line-clamp":!0,"line-height":!0,opacity:!0,order:!0,orphans:!0,"tab-size":!0,widows:!0,"z-index":!0,zoom:!0,"fill-opacity":!0,"stroke-dashoffset":!0,"stroke-opacity":!0,"stroke-width":!0};function MS(i){return i.replace(/([A-Z])/g,"-$1").replace(/^ms-/,"-ms-").toLowerCase()}function Hp(i,e,t){t===!1||t===null||(e.startsWith("--")||(e=MS(e)),typeof t=="number"&&(t===0||RS[e]?t=t.toString():t+="px"),e==="css-float"&&(e="float"),Gp.test(t)?(t=t.replace(Gp,""),i.push(Yr.decl({prop:e,value:t,important:!0}))):i.push(Yr.decl({prop:e,value:t})))}function Yp(i,e,t){let r=Yr.atRule({name:e[1],params:e[3]||""});typeof t=="object"&&(r.nodes=[],za(t,r)),i.push(r)}function za(i,e){let t,r,n;for(t in i)if(r=i[t],!(r===null||typeof r=="undefined"))if(t[0]==="@"){let a=t.match(/@(\S+)(\s+([\W\w]*)\s*)?/);if(Array.isArray(r))for(let s of r)Yp(e,a,s);else Yp(e,a,r)}else if(Array.isArray(r))for(let a of r)Hp(e,t,a);else typeof r=="object"?(n=Yr.rule({selector:t}),za(r,n),e.push(n)):Hp(e,t,r)}Qp.exports=function(i){let e=Yr.root();return za(i,e),e}});var Va=v((DP,Jp)=>{l();var BS=ja();Jp.exports=function(e){return console&&console.warn&&e.warnings().forEach(t=>{let r=t.plugin||"PostCSS";console.warn(r+": "+t.text)}),BS(e.root)}});var Kp=v((IP,Xp)=>{l();var FS=me(),NS=Va(),LS=ln();Xp.exports=function(e){let t=FS(e);return async r=>{let n=await t.process(r,{parser:LS,from:void 0});return NS(n)}}});var ed=v((qP,Zp)=>{l();var $S=me(),jS=Va(),zS=ln();Zp.exports=function(i){let e=$S(i);return t=>{let r=e.process(t,{parser:zS,from:void 0});return jS(r)}}});var rd=v((RP,td)=>{l();var VS=ja(),US=ln(),WS=Kp(),GS=ed();td.exports={objectify:VS,parse:US,async:WS,sync:GS}});var It,id,MP,BP,FP,NP,nd=C(()=>{l();It=K(rd()),id=It.default,MP=It.default.objectify,BP=It.default.parse,FP=It.default.async,NP=It.default.sync});function qt(i){return Array.isArray(i)?i.flatMap(e=>z([(0,sd.default)({bubble:["screen"]})]).process(e,{parser:id}).root.nodes):qt([i])}var sd,Ua=C(()=>{l();nt();sd=K($p());nd()});function Rt(i,e,t=!1){if(i==="")return e;let r=typeof e=="string"?(0,ad.default)().astSync(e):e;return r.walkClasses(n=>{let a=n.value,s=t&&a.startsWith("-");n.value=s?`-${i}${a.slice(1)}`:`${i}${a}`}),typeof e=="string"?r.toString():r}var ad,un=C(()=>{l();ad=K(Me())});function ce(i){let e=od.default.className();return e.value=i,ht(e?.raws?.value??e.value)}var od,Mt=C(()=>{l();od=K(Me());mi()});function Wa(i){return ht(`.${ce(i)}`)}function fn(i,e){return Wa(Qr(i,e))}function Qr(i,e){return e==="DEFAULT"?i:e==="-"||e==="-DEFAULT"?`-${i}`:e.startsWith("-")?`-${i}${e}`:e.startsWith("/")?`${i}${e}`:`${i}-${e}`}var Ga=C(()=>{l();Mt();mi()});function P(i,e=[[i,[i]]],{filterDefault:t=!1,...r}={}){let n=Ge(i);return function({matchUtilities:a,theme:s}){for(let o of e){let u=Array.isArray(o[0])?o:[o];a(u.reduce((c,[f,p])=>Object.assign(c,{[f]:d=>p.reduce((h,y)=>Array.isArray(y)?Object.assign(h,{[y[0]]:y[1]}):Object.assign(h,{[y]:n(d)}),{})}),{}),{...r,values:t?Object.fromEntries(Object.entries(s(i)??{}).filter(([c])=>c!=="DEFAULT")):s(i)})}}}var ld=C(()=>{l();Hr()});function st(i){return i=Array.isArray(i)?i:[i],i.map(e=>{let t=e.values.map(r=>r.raw!==void 0?r.raw:[r.min&&`(min-width: ${r.min})`,r.max&&`(max-width: ${r.max})`].filter(Boolean).join(" and "));return e.not?`not all and ${t}`:t}).join(", ")}var cn=C(()=>{l()});function Ha(i){return i.split(ZS).map(t=>{let r=t.trim(),n={value:r},a=r.split(eC),s=new Set;for(let o of a)!s.has("DIRECTIONS")&&HS.has(o)?(n.direction=o,s.add("DIRECTIONS")):!s.has("PLAY_STATES")&&YS.has(o)?(n.playState=o,s.add("PLAY_STATES")):!s.has("FILL_MODES")&&QS.has(o)?(n.fillMode=o,s.add("FILL_MODES")):!s.has("ITERATION_COUNTS")&&(JS.has(o)||tC.test(o))?(n.iterationCount=o,s.add("ITERATION_COUNTS")):!s.has("TIMING_FUNCTION")&&XS.has(o)||!s.has("TIMING_FUNCTION")&&KS.some(u=>o.startsWith(`${u}(`))?(n.timingFunction=o,s.add("TIMING_FUNCTION")):!s.has("DURATION")&&ud.test(o)?(n.duration=o,s.add("DURATION")):!s.has("DELAY")&&ud.test(o)?(n.delay=o,s.add("DELAY")):s.has("NAME")?(n.unknown||(n.unknown=[]),n.unknown.push(o)):(n.name=o,s.add("NAME"));return n})}var HS,YS,QS,JS,XS,KS,ZS,eC,ud,tC,fd=C(()=>{l();HS=new Set(["normal","reverse","alternate","alternate-reverse"]),YS=new Set(["running","paused"]),QS=new Set(["none","forwards","backwards","both"]),JS=new Set(["infinite"]),XS=new Set(["linear","ease","ease-in","ease-out","ease-in-out","step-start","step-end"]),KS=["cubic-bezier","steps"],ZS=/\,(?![^(]*\))/g,eC=/\ +(?![^(]*\))/g,ud=/^(-?[\d.]+m?s)$/,tC=/^(\d+)$/});var cd,re,pd=C(()=>{l();cd=i=>Object.assign({},...Object.entries(i??{}).flatMap(([e,t])=>typeof t=="object"?Object.entries(cd(t)).map(([r,n])=>({[e+(r==="DEFAULT"?"":`-${r}`)]:n})):[{[`${e}`]:t}])),re=cd});var rC,Qa,iC,nC,sC,aC,oC,lC,uC,fC,cC,pC,dC,hC,mC,gC,yC,wC,Ja,Ya=C(()=>{rC="tailwindcss",Qa="3.3.3",iC="A utility-first CSS framework for rapidly building custom user interfaces.",nC="MIT",sC="lib/index.js",aC="types/index.d.ts",oC="https://github.com/tailwindlabs/tailwindcss.git",lC="https://github.com/tailwindlabs/tailwindcss/issues",uC="https://tailwindcss.com",fC={tailwind:"lib/cli.js",tailwindcss:"lib/cli.js"},cC={engine:"stable"},pC={prebuild:"npm run generate && rimraf lib",build:`swc src --out-dir lib --copy-files --config jsc.transform.optimizer.globals.vars.__OXIDE__='"false"'`,postbuild:"esbuild lib/cli-peer-dependencies.js --bundle --platform=node --outfile=peers/index.js --define:process.env.CSS_TRANSFORMER_WASM=false","rebuild-fixtures":"npm run build && node -r @swc/register scripts/rebuildFixtures.js",style:"eslint .",pretest:"npm run generate",test:"jest","test:integrations":"npm run test --prefix ./integrations","install:integrations":"node scripts/install-integrations.js","generate:plugin-list":"node -r @swc/register scripts/create-plugin-list.js","generate:types":"node -r @swc/register scripts/generate-types.js",generate:"npm run generate:plugin-list && npm run generate:types","release-channel":"node ./scripts/release-channel.js","release-notes":"node ./scripts/release-notes.js",prepublishOnly:"npm install --force && npm run build"},dC=["src/*","cli/*","lib/*","peers/*","scripts/*.js","stubs/*","nesting/*","types/**/*","*.d.ts","*.css","*.js"],hC={"@swc/cli":"^0.1.62","@swc/core":"^1.3.55","@swc/jest":"^0.2.26","@swc/register":"^0.1.10",autoprefixer:"^10.4.14",browserslist:"^4.21.5",concurrently:"^8.0.1",cssnano:"^6.0.0",esbuild:"^0.17.18",eslint:"^8.39.0","eslint-config-prettier":"^8.8.0","eslint-plugin-prettier":"^4.2.1",jest:"^29.5.0","jest-diff":"^29.5.0",lightningcss:"1.18.0",prettier:"^2.8.8",rimraf:"^5.0.0","source-map-js":"^1.0.2",turbo:"^1.9.3"},mC={"@alloc/quick-lru":"^5.2.0",arg:"^5.0.2",chokidar:"^3.5.3",didyoumean:"^1.2.2",dlv:"^1.1.3","fast-glob":"^3.2.12","glob-parent":"^6.0.2","is-glob":"^4.0.3",jiti:"^1.18.2",lilconfig:"^2.1.0",micromatch:"^4.0.5","normalize-path":"^3.0.0","object-hash":"^3.0.0",picocolors:"^1.0.0",postcss:"^8.4.23","postcss-import":"^15.1.0","postcss-js":"^4.0.1","postcss-load-config":"^4.0.1","postcss-nested":"^6.0.1","postcss-selector-parser":"^6.0.11",resolve:"^1.22.2",sucrase:"^3.32.0"},gC=["> 1%","not edge <= 18","not ie 11","not op_mini all"],yC={testTimeout:3e4,setupFilesAfterEnv:["/jest/customMatchers.js"],testPathIgnorePatterns:["/node_modules/","/integrations/","/standalone-cli/","\\.test\\.skip\\.js$"],transformIgnorePatterns:["node_modules/(?!lightningcss)"],transform:{"\\.js$":"@swc/jest","\\.ts$":"@swc/jest"}},wC={node:">=14.0.0"},Ja={name:rC,version:Qa,description:iC,license:nC,main:sC,types:aC,repository:oC,bugs:lC,homepage:uC,bin:fC,tailwindcss:cC,scripts:pC,files:dC,devDependencies:hC,dependencies:mC,browserslist:gC,jest:yC,engines:wC}});function at(i,e=!0){return Array.isArray(i)?i.map(t=>{if(e&&Array.isArray(t))throw new Error("The tuple syntax is not supported for `screens`.");if(typeof t=="string")return{name:t.toString(),not:!1,values:[{min:t,max:void 0}]};let[r,n]=t;return r=r.toString(),typeof n=="string"?{name:r,not:!1,values:[{min:n,max:void 0}]}:Array.isArray(n)?{name:r,not:!1,values:n.map(a=>hd(a))}:{name:r,not:!1,values:[hd(n)]}}):at(Object.entries(i??{}),!1)}function pn(i){return i.values.length!==1?{result:!1,reason:"multiple-values"}:i.values[0].raw!==void 0?{result:!1,reason:"raw-values"}:i.values[0].min!==void 0&&i.values[0].max!==void 0?{result:!1,reason:"min-and-max"}:{result:!0,reason:null}}function dd(i,e,t){let r=dn(e,i),n=dn(t,i),a=pn(r),s=pn(n);if(a.reason==="multiple-values"||s.reason==="multiple-values")throw new Error("Attempted to sort a screen with multiple values. This should never happen. Please open a bug report.");if(a.reason==="raw-values"||s.reason==="raw-values")throw new Error("Attempted to sort a screen with raw values. This should never happen. Please open a bug report.");if(a.reason==="min-and-max"||s.reason==="min-and-max")throw new Error("Attempted to sort a screen with both min and max values. This should never happen. Please open a bug report.");let{min:o,max:u}=r.values[0],{min:c,max:f}=n.values[0];e.not&&([o,u]=[u,o]),t.not&&([c,f]=[f,c]),o=o===void 0?o:parseFloat(o),u=u===void 0?u:parseFloat(u),c=c===void 0?c:parseFloat(c),f=f===void 0?f:parseFloat(f);let[p,d]=i==="min"?[o,c]:[f,u];return p-d}function dn(i,e){return typeof i=="object"?i:{name:"arbitrary-screen",values:[{[e]:i}]}}function hd({"min-width":i,min:e=i,max:t,raw:r}={}){return{min:e,max:t,raw:r}}var hn=C(()=>{l()});function mn(i,e){i.walkDecls(t=>{if(e.includes(t.prop)){t.remove();return}for(let r of e)t.value.includes(`/ var(${r})`)&&(t.value=t.value.replace(`/ var(${r})`,""))})}var md=C(()=>{l()});var pe,Te,Be,Fe,gd,yd=C(()=>{l();ze();mt();nt();ld();cn();Mt();fd();pd();ar();ds();xt();Hr();Ya();Ee();hn();as();md();De();fr();Xr();pe={pseudoElementVariants:({addVariant:i})=>{i("first-letter","&::first-letter"),i("first-line","&::first-line"),i("marker",[({container:e})=>(mn(e,["--tw-text-opacity"]),"& *::marker"),({container:e})=>(mn(e,["--tw-text-opacity"]),"&::marker")]),i("selection",["& *::selection","&::selection"]),i("file","&::file-selector-button"),i("placeholder","&::placeholder"),i("backdrop","&::backdrop"),i("before",({container:e})=>(e.walkRules(t=>{let r=!1;t.walkDecls("content",()=>{r=!0}),r||t.prepend(z.decl({prop:"content",value:"var(--tw-content)"}))}),"&::before")),i("after",({container:e})=>(e.walkRules(t=>{let r=!1;t.walkDecls("content",()=>{r=!0}),r||t.prepend(z.decl({prop:"content",value:"var(--tw-content)"}))}),"&::after"))},pseudoClassVariants:({addVariant:i,matchVariant:e,config:t,prefix:r})=>{let n=[["first","&:first-child"],["last","&:last-child"],["only","&:only-child"],["odd","&:nth-child(odd)"],["even","&:nth-child(even)"],"first-of-type","last-of-type","only-of-type",["visited",({container:s})=>(mn(s,["--tw-text-opacity","--tw-border-opacity","--tw-bg-opacity"]),"&:visited")],"target",["open","&[open]"],"default","checked","indeterminate","placeholder-shown","autofill","optional","required","valid","invalid","in-range","out-of-range","read-only","empty","focus-within",["hover",J(t(),"hoverOnlyWhenSupported")?"@media (hover: hover) and (pointer: fine) { &:hover }":"&:hover"],"focus","focus-visible","active","enabled","disabled"].map(s=>Array.isArray(s)?s:[s,`&:${s}`]);for(let[s,o]of n)i(s,u=>typeof o=="function"?o(u):o);let a={group:(s,{modifier:o})=>o?[`:merge(${r(".group")}\\/${ce(o)})`," &"]:[`:merge(${r(".group")})`," &"],peer:(s,{modifier:o})=>o?[`:merge(${r(".peer")}\\/${ce(o)})`," ~ &"]:[`:merge(${r(".peer")})`," ~ &"]};for(let[s,o]of Object.entries(a))e(s,(u="",c)=>{let f=U(typeof u=="function"?u(c):u);f.includes("&")||(f="&"+f);let[p,d]=o("",c),h=null,y=null,x=0;for(let w=0;w{i("ltr",':is([dir="ltr"] &)'),i("rtl",':is([dir="rtl"] &)')},reducedMotionVariants:({addVariant:i})=>{i("motion-safe","@media (prefers-reduced-motion: no-preference)"),i("motion-reduce","@media (prefers-reduced-motion: reduce)")},darkVariants:({config:i,addVariant:e})=>{let[t,r=".dark"]=[].concat(i("darkMode","media"));t===!1&&(t="media",F.warn("darkmode-false",["The `darkMode` option in your Tailwind CSS configuration is set to `false`, which now behaves the same as `media`.","Change `darkMode` to `media` or remove it entirely.","https://tailwindcss.com/docs/upgrade-guide#remove-dark-mode-configuration"])),t==="class"?e("dark",`:is(${r} &)`):t==="media"&&e("dark","@media (prefers-color-scheme: dark)")},printVariant:({addVariant:i})=>{i("print","@media print")},screenVariants:({theme:i,addVariant:e,matchVariant:t})=>{let r=i("screens")??{},n=Object.values(r).every(b=>typeof b=="string"),a=at(i("screens")),s=new Set([]);function o(b){return b.match(/(\D+)$/)?.[1]??"(none)"}function u(b){b!==void 0&&s.add(o(b))}function c(b){return u(b),s.size===1}for(let b of a)for(let k of b.values)u(k.min),u(k.max);let f=s.size<=1;function p(b){return Object.fromEntries(a.filter(k=>pn(k).result).map(k=>{let{min:S,max:_}=k.values[0];if(b==="min"&&S!==void 0)return k;if(b==="min"&&_!==void 0)return{...k,not:!k.not};if(b==="max"&&_!==void 0)return k;if(b==="max"&&S!==void 0)return{...k,not:!k.not}}).map(k=>[k.name,k]))}function d(b){return(k,S)=>dd(b,k.value,S.value)}let h=d("max"),y=d("min");function x(b){return k=>{if(n)if(f){if(typeof k=="string"&&!c(k))return F.warn("minmax-have-mixed-units",["The `min-*` and `max-*` variants are not supported with a `screens` configuration containing mixed units."]),[]}else return F.warn("mixed-screen-units",["The `min-*` and `max-*` variants are not supported with a `screens` configuration containing mixed units."]),[];else return F.warn("complex-screen-config",["The `min-*` and `max-*` variants are not supported with a `screens` configuration containing objects."]),[];return[`@media ${st(dn(k,b))}`]}}t("max",x("max"),{sort:h,values:n?p("max"):{}});let w="min-screens";for(let b of a)e(b.name,`@media ${st(b)}`,{id:w,sort:n&&f?y:void 0,value:b});t("min",x("min"),{id:w,sort:y})},supportsVariants:({matchVariant:i,theme:e})=>{i("supports",(t="")=>{let r=U(t),n=/^\w*\s*\(/.test(r);return r=n?r.replace(/\b(and|or|not)\b/g," $1 "):r,n?`@supports ${r}`:(r.includes(":")||(r=`${r}: var(--tw)`),r.startsWith("(")&&r.endsWith(")")||(r=`(${r})`),`@supports ${r}`)},{values:e("supports")??{}})},ariaVariants:({matchVariant:i,theme:e})=>{i("aria",t=>`&[aria-${U(t)}]`,{values:e("aria")??{}}),i("group-aria",(t,{modifier:r})=>r?`:merge(.group\\/${r})[aria-${U(t)}] &`:`:merge(.group)[aria-${U(t)}] &`,{values:e("aria")??{}}),i("peer-aria",(t,{modifier:r})=>r?`:merge(.peer\\/${r})[aria-${U(t)}] ~ &`:`:merge(.peer)[aria-${U(t)}] ~ &`,{values:e("aria")??{}})},dataVariants:({matchVariant:i,theme:e})=>{i("data",t=>`&[data-${U(t)}]`,{values:e("data")??{}}),i("group-data",(t,{modifier:r})=>r?`:merge(.group\\/${r})[data-${U(t)}] &`:`:merge(.group)[data-${U(t)}] &`,{values:e("data")??{}}),i("peer-data",(t,{modifier:r})=>r?`:merge(.peer\\/${r})[data-${U(t)}] ~ &`:`:merge(.peer)[data-${U(t)}] ~ &`,{values:e("data")??{}})},orientationVariants:({addVariant:i})=>{i("portrait","@media (orientation: portrait)"),i("landscape","@media (orientation: landscape)")},prefersContrastVariants:({addVariant:i})=>{i("contrast-more","@media (prefers-contrast: more)"),i("contrast-less","@media (prefers-contrast: less)")}},Te=["translate(var(--tw-translate-x), var(--tw-translate-y))","rotate(var(--tw-rotate))","skewX(var(--tw-skew-x))","skewY(var(--tw-skew-y))","scaleX(var(--tw-scale-x))","scaleY(var(--tw-scale-y))"].join(" "),Be=["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)"].join(" "),Fe=["var(--tw-backdrop-blur)","var(--tw-backdrop-brightness)","var(--tw-backdrop-contrast)","var(--tw-backdrop-grayscale)","var(--tw-backdrop-hue-rotate)","var(--tw-backdrop-invert)","var(--tw-backdrop-opacity)","var(--tw-backdrop-saturate)","var(--tw-backdrop-sepia)"].join(" "),gd={preflight:({addBase:i})=>{let e=z.parse(`*,::after,::before{box-sizing:border-box;border-width:0;border-style:solid;border-color:theme('borderColor.DEFAULT', currentColor)}::after,::before{--tw-content:''}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;font-family:theme('fontFamily.sans', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:theme('fontFamily.sans[1].fontFeatureSettings', normal);font-variation-settings:theme('fontFamily.sans[1].fontVariationSettings', normal)}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){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,pre,samp{font-family:theme('fontFamily.mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);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;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-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,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::placeholder,textarea::placeholder{opacity:1;color:theme('colors.gray.4', #9ca3af)}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}`);i([z.comment({text:`! tailwindcss v${Qa} | MIT License | https://tailwindcss.com`}),...e.nodes])},container:(()=>{function i(t=[]){return t.flatMap(r=>r.values.map(n=>n.min)).filter(r=>r!==void 0)}function e(t,r,n){if(typeof n=="undefined")return[];if(!(typeof n=="object"&&n!==null))return[{screen:"DEFAULT",minWidth:0,padding:n}];let a=[];n.DEFAULT&&a.push({screen:"DEFAULT",minWidth:0,padding:n.DEFAULT});for(let s of t)for(let o of r)for(let{min:u}of o.values)u===s&&a.push({minWidth:s,padding:n[o.name]});return a}return function({addComponents:t,theme:r}){let n=at(r("container.screens",r("screens"))),a=i(n),s=e(a,n,r("container.padding")),o=c=>{let f=s.find(p=>p.minWidth===c);return f?{paddingRight:f.padding,paddingLeft:f.padding}:{}},u=Array.from(new Set(a.slice().sort((c,f)=>parseInt(c)-parseInt(f)))).map(c=>({[`@media (min-width: ${c})`]:{".container":{"max-width":c,...o(c)}}}));t([{".container":Object.assign({width:"100%"},r("container.center",!1)?{marginRight:"auto",marginLeft:"auto"}:{},o(0))},...u])}})(),accessibility:({addUtilities:i})=>{i({".sr-only":{position:"absolute",width:"1px",height:"1px",padding:"0",margin:"-1px",overflow:"hidden",clip:"rect(0, 0, 0, 0)",whiteSpace:"nowrap",borderWidth:"0"},".not-sr-only":{position:"static",width:"auto",height:"auto",padding:"0",margin:"0",overflow:"visible",clip:"auto",whiteSpace:"normal"}})},pointerEvents:({addUtilities:i})=>{i({".pointer-events-none":{"pointer-events":"none"},".pointer-events-auto":{"pointer-events":"auto"}})},visibility:({addUtilities:i})=>{i({".visible":{visibility:"visible"},".invisible":{visibility:"hidden"},".collapse":{visibility:"collapse"}})},position:({addUtilities:i})=>{i({".static":{position:"static"},".fixed":{position:"fixed"},".absolute":{position:"absolute"},".relative":{position:"relative"},".sticky":{position:"sticky"}})},inset:P("inset",[["inset",["inset"]],[["inset-x",["left","right"]],["inset-y",["top","bottom"]]],[["start",["inset-inline-start"]],["end",["inset-inline-end"]],["top",["top"]],["right",["right"]],["bottom",["bottom"]],["left",["left"]]]],{supportsNegativeValues:!0}),isolation:({addUtilities:i})=>{i({".isolate":{isolation:"isolate"},".isolation-auto":{isolation:"auto"}})},zIndex:P("zIndex",[["z",["zIndex"]]],{supportsNegativeValues:!0}),order:P("order",void 0,{supportsNegativeValues:!0}),gridColumn:P("gridColumn",[["col",["gridColumn"]]]),gridColumnStart:P("gridColumnStart",[["col-start",["gridColumnStart"]]]),gridColumnEnd:P("gridColumnEnd",[["col-end",["gridColumnEnd"]]]),gridRow:P("gridRow",[["row",["gridRow"]]]),gridRowStart:P("gridRowStart",[["row-start",["gridRowStart"]]]),gridRowEnd:P("gridRowEnd",[["row-end",["gridRowEnd"]]]),float:({addUtilities:i})=>{i({".float-right":{float:"right"},".float-left":{float:"left"},".float-none":{float:"none"}})},clear:({addUtilities:i})=>{i({".clear-left":{clear:"left"},".clear-right":{clear:"right"},".clear-both":{clear:"both"},".clear-none":{clear:"none"}})},margin:P("margin",[["m",["margin"]],[["mx",["margin-left","margin-right"]],["my",["margin-top","margin-bottom"]]],[["ms",["margin-inline-start"]],["me",["margin-inline-end"]],["mt",["margin-top"]],["mr",["margin-right"]],["mb",["margin-bottom"]],["ml",["margin-left"]]]],{supportsNegativeValues:!0}),boxSizing:({addUtilities:i})=>{i({".box-border":{"box-sizing":"border-box"},".box-content":{"box-sizing":"content-box"}})},lineClamp:({matchUtilities:i,addUtilities:e,theme:t})=>{i({"line-clamp":r=>({overflow:"hidden",display:"-webkit-box","-webkit-box-orient":"vertical","-webkit-line-clamp":`${r}`})},{values:t("lineClamp")}),e({".line-clamp-none":{overflow:"visible",display:"block","-webkit-box-orient":"horizontal","-webkit-line-clamp":"none"}})},display:({addUtilities:i})=>{i({".block":{display:"block"},".inline-block":{display:"inline-block"},".inline":{display:"inline"},".flex":{display:"flex"},".inline-flex":{display:"inline-flex"},".table":{display:"table"},".inline-table":{display:"inline-table"},".table-caption":{display:"table-caption"},".table-cell":{display:"table-cell"},".table-column":{display:"table-column"},".table-column-group":{display:"table-column-group"},".table-footer-group":{display:"table-footer-group"},".table-header-group":{display:"table-header-group"},".table-row-group":{display:"table-row-group"},".table-row":{display:"table-row"},".flow-root":{display:"flow-root"},".grid":{display:"grid"},".inline-grid":{display:"inline-grid"},".contents":{display:"contents"},".list-item":{display:"list-item"},".hidden":{display:"none"}})},aspectRatio:P("aspectRatio",[["aspect",["aspect-ratio"]]]),height:P("height",[["h",["height"]]]),maxHeight:P("maxHeight",[["max-h",["maxHeight"]]]),minHeight:P("minHeight",[["min-h",["minHeight"]]]),width:P("width",[["w",["width"]]]),minWidth:P("minWidth",[["min-w",["minWidth"]]]),maxWidth:P("maxWidth",[["max-w",["maxWidth"]]]),flex:P("flex"),flexShrink:P("flexShrink",[["flex-shrink",["flex-shrink"]],["shrink",["flex-shrink"]]]),flexGrow:P("flexGrow",[["flex-grow",["flex-grow"]],["grow",["flex-grow"]]]),flexBasis:P("flexBasis",[["basis",["flex-basis"]]]),tableLayout:({addUtilities:i})=>{i({".table-auto":{"table-layout":"auto"},".table-fixed":{"table-layout":"fixed"}})},captionSide:({addUtilities:i})=>{i({".caption-top":{"caption-side":"top"},".caption-bottom":{"caption-side":"bottom"}})},borderCollapse:({addUtilities:i})=>{i({".border-collapse":{"border-collapse":"collapse"},".border-separate":{"border-collapse":"separate"}})},borderSpacing:({addDefaults:i,matchUtilities:e,theme:t})=>{i("border-spacing",{"--tw-border-spacing-x":0,"--tw-border-spacing-y":0}),e({"border-spacing":r=>({"--tw-border-spacing-x":r,"--tw-border-spacing-y":r,"@defaults border-spacing":{},"border-spacing":"var(--tw-border-spacing-x) var(--tw-border-spacing-y)"}),"border-spacing-x":r=>({"--tw-border-spacing-x":r,"@defaults border-spacing":{},"border-spacing":"var(--tw-border-spacing-x) var(--tw-border-spacing-y)"}),"border-spacing-y":r=>({"--tw-border-spacing-y":r,"@defaults border-spacing":{},"border-spacing":"var(--tw-border-spacing-x) var(--tw-border-spacing-y)"})},{values:t("borderSpacing")})},transformOrigin:P("transformOrigin",[["origin",["transformOrigin"]]]),translate:P("translate",[[["translate-x",[["@defaults transform",{}],"--tw-translate-x",["transform",Te]]],["translate-y",[["@defaults transform",{}],"--tw-translate-y",["transform",Te]]]]],{supportsNegativeValues:!0}),rotate:P("rotate",[["rotate",[["@defaults transform",{}],"--tw-rotate",["transform",Te]]]],{supportsNegativeValues:!0}),skew:P("skew",[[["skew-x",[["@defaults transform",{}],"--tw-skew-x",["transform",Te]]],["skew-y",[["@defaults transform",{}],"--tw-skew-y",["transform",Te]]]]],{supportsNegativeValues:!0}),scale:P("scale",[["scale",[["@defaults transform",{}],"--tw-scale-x","--tw-scale-y",["transform",Te]]],[["scale-x",[["@defaults transform",{}],"--tw-scale-x",["transform",Te]]],["scale-y",[["@defaults transform",{}],"--tw-scale-y",["transform",Te]]]]],{supportsNegativeValues:!0}),transform:({addDefaults:i,addUtilities:e})=>{i("transform",{"--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"}),e({".transform":{"@defaults transform":{},transform:Te},".transform-cpu":{transform:Te},".transform-gpu":{transform:Te.replace("translate(var(--tw-translate-x), var(--tw-translate-y))","translate3d(var(--tw-translate-x), var(--tw-translate-y), 0)")},".transform-none":{transform:"none"}})},animation:({matchUtilities:i,theme:e,config:t})=>{let r=a=>ce(t("prefix")+a),n=Object.fromEntries(Object.entries(e("keyframes")??{}).map(([a,s])=>[a,{[`@keyframes ${r(a)}`]:s}]));i({animate:a=>{let s=Ha(a);return[...s.flatMap(o=>n[o.name]),{animation:s.map(({name:o,value:u})=>o===void 0||n[o]===void 0?u:u.replace(o,r(o))).join(", ")}]}},{values:e("animation")})},cursor:P("cursor"),touchAction:({addDefaults:i,addUtilities:e})=>{i("touch-action",{"--tw-pan-x":" ","--tw-pan-y":" ","--tw-pinch-zoom":" "});let t="var(--tw-pan-x) var(--tw-pan-y) var(--tw-pinch-zoom)";e({".touch-auto":{"touch-action":"auto"},".touch-none":{"touch-action":"none"},".touch-pan-x":{"@defaults touch-action":{},"--tw-pan-x":"pan-x","touch-action":t},".touch-pan-left":{"@defaults touch-action":{},"--tw-pan-x":"pan-left","touch-action":t},".touch-pan-right":{"@defaults touch-action":{},"--tw-pan-x":"pan-right","touch-action":t},".touch-pan-y":{"@defaults touch-action":{},"--tw-pan-y":"pan-y","touch-action":t},".touch-pan-up":{"@defaults touch-action":{},"--tw-pan-y":"pan-up","touch-action":t},".touch-pan-down":{"@defaults touch-action":{},"--tw-pan-y":"pan-down","touch-action":t},".touch-pinch-zoom":{"@defaults touch-action":{},"--tw-pinch-zoom":"pinch-zoom","touch-action":t},".touch-manipulation":{"touch-action":"manipulation"}})},userSelect:({addUtilities:i})=>{i({".select-none":{"user-select":"none"},".select-text":{"user-select":"text"},".select-all":{"user-select":"all"},".select-auto":{"user-select":"auto"}})},resize:({addUtilities:i})=>{i({".resize-none":{resize:"none"},".resize-y":{resize:"vertical"},".resize-x":{resize:"horizontal"},".resize":{resize:"both"}})},scrollSnapType:({addDefaults:i,addUtilities:e})=>{i("scroll-snap-type",{"--tw-scroll-snap-strictness":"proximity"}),e({".snap-none":{"scroll-snap-type":"none"},".snap-x":{"@defaults scroll-snap-type":{},"scroll-snap-type":"x var(--tw-scroll-snap-strictness)"},".snap-y":{"@defaults scroll-snap-type":{},"scroll-snap-type":"y var(--tw-scroll-snap-strictness)"},".snap-both":{"@defaults scroll-snap-type":{},"scroll-snap-type":"both var(--tw-scroll-snap-strictness)"},".snap-mandatory":{"--tw-scroll-snap-strictness":"mandatory"},".snap-proximity":{"--tw-scroll-snap-strictness":"proximity"}})},scrollSnapAlign:({addUtilities:i})=>{i({".snap-start":{"scroll-snap-align":"start"},".snap-end":{"scroll-snap-align":"end"},".snap-center":{"scroll-snap-align":"center"},".snap-align-none":{"scroll-snap-align":"none"}})},scrollSnapStop:({addUtilities:i})=>{i({".snap-normal":{"scroll-snap-stop":"normal"},".snap-always":{"scroll-snap-stop":"always"}})},scrollMargin:P("scrollMargin",[["scroll-m",["scroll-margin"]],[["scroll-mx",["scroll-margin-left","scroll-margin-right"]],["scroll-my",["scroll-margin-top","scroll-margin-bottom"]]],[["scroll-ms",["scroll-margin-inline-start"]],["scroll-me",["scroll-margin-inline-end"]],["scroll-mt",["scroll-margin-top"]],["scroll-mr",["scroll-margin-right"]],["scroll-mb",["scroll-margin-bottom"]],["scroll-ml",["scroll-margin-left"]]]],{supportsNegativeValues:!0}),scrollPadding:P("scrollPadding",[["scroll-p",["scroll-padding"]],[["scroll-px",["scroll-padding-left","scroll-padding-right"]],["scroll-py",["scroll-padding-top","scroll-padding-bottom"]]],[["scroll-ps",["scroll-padding-inline-start"]],["scroll-pe",["scroll-padding-inline-end"]],["scroll-pt",["scroll-padding-top"]],["scroll-pr",["scroll-padding-right"]],["scroll-pb",["scroll-padding-bottom"]],["scroll-pl",["scroll-padding-left"]]]]),listStylePosition:({addUtilities:i})=>{i({".list-inside":{"list-style-position":"inside"},".list-outside":{"list-style-position":"outside"}})},listStyleType:P("listStyleType",[["list",["listStyleType"]]]),listStyleImage:P("listStyleImage",[["list-image",["listStyleImage"]]]),appearance:({addUtilities:i})=>{i({".appearance-none":{appearance:"none"}})},columns:P("columns",[["columns",["columns"]]]),breakBefore:({addUtilities:i})=>{i({".break-before-auto":{"break-before":"auto"},".break-before-avoid":{"break-before":"avoid"},".break-before-all":{"break-before":"all"},".break-before-avoid-page":{"break-before":"avoid-page"},".break-before-page":{"break-before":"page"},".break-before-left":{"break-before":"left"},".break-before-right":{"break-before":"right"},".break-before-column":{"break-before":"column"}})},breakInside:({addUtilities:i})=>{i({".break-inside-auto":{"break-inside":"auto"},".break-inside-avoid":{"break-inside":"avoid"},".break-inside-avoid-page":{"break-inside":"avoid-page"},".break-inside-avoid-column":{"break-inside":"avoid-column"}})},breakAfter:({addUtilities:i})=>{i({".break-after-auto":{"break-after":"auto"},".break-after-avoid":{"break-after":"avoid"},".break-after-all":{"break-after":"all"},".break-after-avoid-page":{"break-after":"avoid-page"},".break-after-page":{"break-after":"page"},".break-after-left":{"break-after":"left"},".break-after-right":{"break-after":"right"},".break-after-column":{"break-after":"column"}})},gridAutoColumns:P("gridAutoColumns",[["auto-cols",["gridAutoColumns"]]]),gridAutoFlow:({addUtilities:i})=>{i({".grid-flow-row":{gridAutoFlow:"row"},".grid-flow-col":{gridAutoFlow:"column"},".grid-flow-dense":{gridAutoFlow:"dense"},".grid-flow-row-dense":{gridAutoFlow:"row dense"},".grid-flow-col-dense":{gridAutoFlow:"column dense"}})},gridAutoRows:P("gridAutoRows",[["auto-rows",["gridAutoRows"]]]),gridTemplateColumns:P("gridTemplateColumns",[["grid-cols",["gridTemplateColumns"]]]),gridTemplateRows:P("gridTemplateRows",[["grid-rows",["gridTemplateRows"]]]),flexDirection:({addUtilities:i})=>{i({".flex-row":{"flex-direction":"row"},".flex-row-reverse":{"flex-direction":"row-reverse"},".flex-col":{"flex-direction":"column"},".flex-col-reverse":{"flex-direction":"column-reverse"}})},flexWrap:({addUtilities:i})=>{i({".flex-wrap":{"flex-wrap":"wrap"},".flex-wrap-reverse":{"flex-wrap":"wrap-reverse"},".flex-nowrap":{"flex-wrap":"nowrap"}})},placeContent:({addUtilities:i})=>{i({".place-content-center":{"place-content":"center"},".place-content-start":{"place-content":"start"},".place-content-end":{"place-content":"end"},".place-content-between":{"place-content":"space-between"},".place-content-around":{"place-content":"space-around"},".place-content-evenly":{"place-content":"space-evenly"},".place-content-baseline":{"place-content":"baseline"},".place-content-stretch":{"place-content":"stretch"}})},placeItems:({addUtilities:i})=>{i({".place-items-start":{"place-items":"start"},".place-items-end":{"place-items":"end"},".place-items-center":{"place-items":"center"},".place-items-baseline":{"place-items":"baseline"},".place-items-stretch":{"place-items":"stretch"}})},alignContent:({addUtilities:i})=>{i({".content-normal":{"align-content":"normal"},".content-center":{"align-content":"center"},".content-start":{"align-content":"flex-start"},".content-end":{"align-content":"flex-end"},".content-between":{"align-content":"space-between"},".content-around":{"align-content":"space-around"},".content-evenly":{"align-content":"space-evenly"},".content-baseline":{"align-content":"baseline"},".content-stretch":{"align-content":"stretch"}})},alignItems:({addUtilities:i})=>{i({".items-start":{"align-items":"flex-start"},".items-end":{"align-items":"flex-end"},".items-center":{"align-items":"center"},".items-baseline":{"align-items":"baseline"},".items-stretch":{"align-items":"stretch"}})},justifyContent:({addUtilities:i})=>{i({".justify-normal":{"justify-content":"normal"},".justify-start":{"justify-content":"flex-start"},".justify-end":{"justify-content":"flex-end"},".justify-center":{"justify-content":"center"},".justify-between":{"justify-content":"space-between"},".justify-around":{"justify-content":"space-around"},".justify-evenly":{"justify-content":"space-evenly"},".justify-stretch":{"justify-content":"stretch"}})},justifyItems:({addUtilities:i})=>{i({".justify-items-start":{"justify-items":"start"},".justify-items-end":{"justify-items":"end"},".justify-items-center":{"justify-items":"center"},".justify-items-stretch":{"justify-items":"stretch"}})},gap:P("gap",[["gap",["gap"]],[["gap-x",["columnGap"]],["gap-y",["rowGap"]]]]),space:({matchUtilities:i,addUtilities:e,theme:t})=>{i({"space-x":r=>(r=r==="0"?"0px":r,{"& > :not([hidden]) ~ :not([hidden])":{"--tw-space-x-reverse":"0","margin-right":`calc(${r} * var(--tw-space-x-reverse))`,"margin-left":`calc(${r} * calc(1 - var(--tw-space-x-reverse)))`}}),"space-y":r=>(r=r==="0"?"0px":r,{"& > :not([hidden]) ~ :not([hidden])":{"--tw-space-y-reverse":"0","margin-top":`calc(${r} * calc(1 - var(--tw-space-y-reverse)))`,"margin-bottom":`calc(${r} * var(--tw-space-y-reverse))`}})},{values:t("space"),supportsNegativeValues:!0}),e({".space-y-reverse > :not([hidden]) ~ :not([hidden])":{"--tw-space-y-reverse":"1"},".space-x-reverse > :not([hidden]) ~ :not([hidden])":{"--tw-space-x-reverse":"1"}})},divideWidth:({matchUtilities:i,addUtilities:e,theme:t})=>{i({"divide-x":r=>(r=r==="0"?"0px":r,{"& > :not([hidden]) ~ :not([hidden])":{"@defaults border-width":{},"--tw-divide-x-reverse":"0","border-right-width":`calc(${r} * var(--tw-divide-x-reverse))`,"border-left-width":`calc(${r} * calc(1 - var(--tw-divide-x-reverse)))`}}),"divide-y":r=>(r=r==="0"?"0px":r,{"& > :not([hidden]) ~ :not([hidden])":{"@defaults border-width":{},"--tw-divide-y-reverse":"0","border-top-width":`calc(${r} * calc(1 - var(--tw-divide-y-reverse)))`,"border-bottom-width":`calc(${r} * var(--tw-divide-y-reverse))`}})},{values:t("divideWidth"),type:["line-width","length","any"]}),e({".divide-y-reverse > :not([hidden]) ~ :not([hidden])":{"@defaults border-width":{},"--tw-divide-y-reverse":"1"},".divide-x-reverse > :not([hidden]) ~ :not([hidden])":{"@defaults border-width":{},"--tw-divide-x-reverse":"1"}})},divideStyle:({addUtilities:i})=>{i({".divide-solid > :not([hidden]) ~ :not([hidden])":{"border-style":"solid"},".divide-dashed > :not([hidden]) ~ :not([hidden])":{"border-style":"dashed"},".divide-dotted > :not([hidden]) ~ :not([hidden])":{"border-style":"dotted"},".divide-double > :not([hidden]) ~ :not([hidden])":{"border-style":"double"},".divide-none > :not([hidden]) ~ :not([hidden])":{"border-style":"none"}})},divideColor:({matchUtilities:i,theme:e,corePlugins:t})=>{i({divide:r=>t("divideOpacity")?{["& > :not([hidden]) ~ :not([hidden])"]:se({color:r,property:"border-color",variable:"--tw-divide-opacity"})}:{["& > :not([hidden]) ~ :not([hidden])"]:{"border-color":N(r)}}},{values:(({DEFAULT:r,...n})=>n)(re(e("divideColor"))),type:["color","any"]})},divideOpacity:({matchUtilities:i,theme:e})=>{i({"divide-opacity":t=>({["& > :not([hidden]) ~ :not([hidden])"]:{"--tw-divide-opacity":t}})},{values:e("divideOpacity")})},placeSelf:({addUtilities:i})=>{i({".place-self-auto":{"place-self":"auto"},".place-self-start":{"place-self":"start"},".place-self-end":{"place-self":"end"},".place-self-center":{"place-self":"center"},".place-self-stretch":{"place-self":"stretch"}})},alignSelf:({addUtilities:i})=>{i({".self-auto":{"align-self":"auto"},".self-start":{"align-self":"flex-start"},".self-end":{"align-self":"flex-end"},".self-center":{"align-self":"center"},".self-stretch":{"align-self":"stretch"},".self-baseline":{"align-self":"baseline"}})},justifySelf:({addUtilities:i})=>{i({".justify-self-auto":{"justify-self":"auto"},".justify-self-start":{"justify-self":"start"},".justify-self-end":{"justify-self":"end"},".justify-self-center":{"justify-self":"center"},".justify-self-stretch":{"justify-self":"stretch"}})},overflow:({addUtilities:i})=>{i({".overflow-auto":{overflow:"auto"},".overflow-hidden":{overflow:"hidden"},".overflow-clip":{overflow:"clip"},".overflow-visible":{overflow:"visible"},".overflow-scroll":{overflow:"scroll"},".overflow-x-auto":{"overflow-x":"auto"},".overflow-y-auto":{"overflow-y":"auto"},".overflow-x-hidden":{"overflow-x":"hidden"},".overflow-y-hidden":{"overflow-y":"hidden"},".overflow-x-clip":{"overflow-x":"clip"},".overflow-y-clip":{"overflow-y":"clip"},".overflow-x-visible":{"overflow-x":"visible"},".overflow-y-visible":{"overflow-y":"visible"},".overflow-x-scroll":{"overflow-x":"scroll"},".overflow-y-scroll":{"overflow-y":"scroll"}})},overscrollBehavior:({addUtilities:i})=>{i({".overscroll-auto":{"overscroll-behavior":"auto"},".overscroll-contain":{"overscroll-behavior":"contain"},".overscroll-none":{"overscroll-behavior":"none"},".overscroll-y-auto":{"overscroll-behavior-y":"auto"},".overscroll-y-contain":{"overscroll-behavior-y":"contain"},".overscroll-y-none":{"overscroll-behavior-y":"none"},".overscroll-x-auto":{"overscroll-behavior-x":"auto"},".overscroll-x-contain":{"overscroll-behavior-x":"contain"},".overscroll-x-none":{"overscroll-behavior-x":"none"}})},scrollBehavior:({addUtilities:i})=>{i({".scroll-auto":{"scroll-behavior":"auto"},".scroll-smooth":{"scroll-behavior":"smooth"}})},textOverflow:({addUtilities:i})=>{i({".truncate":{overflow:"hidden","text-overflow":"ellipsis","white-space":"nowrap"},".overflow-ellipsis":{"text-overflow":"ellipsis"},".text-ellipsis":{"text-overflow":"ellipsis"},".text-clip":{"text-overflow":"clip"}})},hyphens:({addUtilities:i})=>{i({".hyphens-none":{hyphens:"none"},".hyphens-manual":{hyphens:"manual"},".hyphens-auto":{hyphens:"auto"}})},whitespace:({addUtilities:i})=>{i({".whitespace-normal":{"white-space":"normal"},".whitespace-nowrap":{"white-space":"nowrap"},".whitespace-pre":{"white-space":"pre"},".whitespace-pre-line":{"white-space":"pre-line"},".whitespace-pre-wrap":{"white-space":"pre-wrap"},".whitespace-break-spaces":{"white-space":"break-spaces"}})},wordBreak:({addUtilities:i})=>{i({".break-normal":{"overflow-wrap":"normal","word-break":"normal"},".break-words":{"overflow-wrap":"break-word"},".break-all":{"word-break":"break-all"},".break-keep":{"word-break":"keep-all"}})},borderRadius:P("borderRadius",[["rounded",["border-radius"]],[["rounded-s",["border-start-start-radius","border-end-start-radius"]],["rounded-e",["border-start-end-radius","border-end-end-radius"]],["rounded-t",["border-top-left-radius","border-top-right-radius"]],["rounded-r",["border-top-right-radius","border-bottom-right-radius"]],["rounded-b",["border-bottom-right-radius","border-bottom-left-radius"]],["rounded-l",["border-top-left-radius","border-bottom-left-radius"]]],[["rounded-ss",["border-start-start-radius"]],["rounded-se",["border-start-end-radius"]],["rounded-ee",["border-end-end-radius"]],["rounded-es",["border-end-start-radius"]],["rounded-tl",["border-top-left-radius"]],["rounded-tr",["border-top-right-radius"]],["rounded-br",["border-bottom-right-radius"]],["rounded-bl",["border-bottom-left-radius"]]]]),borderWidth:P("borderWidth",[["border",[["@defaults border-width",{}],"border-width"]],[["border-x",[["@defaults border-width",{}],"border-left-width","border-right-width"]],["border-y",[["@defaults border-width",{}],"border-top-width","border-bottom-width"]]],[["border-s",[["@defaults border-width",{}],"border-inline-start-width"]],["border-e",[["@defaults border-width",{}],"border-inline-end-width"]],["border-t",[["@defaults border-width",{}],"border-top-width"]],["border-r",[["@defaults border-width",{}],"border-right-width"]],["border-b",[["@defaults border-width",{}],"border-bottom-width"]],["border-l",[["@defaults border-width",{}],"border-left-width"]]]],{type:["line-width","length"]}),borderStyle:({addUtilities:i})=>{i({".border-solid":{"border-style":"solid"},".border-dashed":{"border-style":"dashed"},".border-dotted":{"border-style":"dotted"},".border-double":{"border-style":"double"},".border-hidden":{"border-style":"hidden"},".border-none":{"border-style":"none"}})},borderColor:({matchUtilities:i,theme:e,corePlugins:t})=>{i({border:r=>t("borderOpacity")?se({color:r,property:"border-color",variable:"--tw-border-opacity"}):{"border-color":N(r)}},{values:(({DEFAULT:r,...n})=>n)(re(e("borderColor"))),type:["color","any"]}),i({"border-x":r=>t("borderOpacity")?se({color:r,property:["border-left-color","border-right-color"],variable:"--tw-border-opacity"}):{"border-left-color":N(r),"border-right-color":N(r)},"border-y":r=>t("borderOpacity")?se({color:r,property:["border-top-color","border-bottom-color"],variable:"--tw-border-opacity"}):{"border-top-color":N(r),"border-bottom-color":N(r)}},{values:(({DEFAULT:r,...n})=>n)(re(e("borderColor"))),type:["color","any"]}),i({"border-s":r=>t("borderOpacity")?se({color:r,property:"border-inline-start-color",variable:"--tw-border-opacity"}):{"border-inline-start-color":N(r)},"border-e":r=>t("borderOpacity")?se({color:r,property:"border-inline-end-color",variable:"--tw-border-opacity"}):{"border-inline-end-color":N(r)},"border-t":r=>t("borderOpacity")?se({color:r,property:"border-top-color",variable:"--tw-border-opacity"}):{"border-top-color":N(r)},"border-r":r=>t("borderOpacity")?se({color:r,property:"border-right-color",variable:"--tw-border-opacity"}):{"border-right-color":N(r)},"border-b":r=>t("borderOpacity")?se({color:r,property:"border-bottom-color",variable:"--tw-border-opacity"}):{"border-bottom-color":N(r)},"border-l":r=>t("borderOpacity")?se({color:r,property:"border-left-color",variable:"--tw-border-opacity"}):{"border-left-color":N(r)}},{values:(({DEFAULT:r,...n})=>n)(re(e("borderColor"))),type:["color","any"]})},borderOpacity:P("borderOpacity",[["border-opacity",["--tw-border-opacity"]]]),backgroundColor:({matchUtilities:i,theme:e,corePlugins:t})=>{i({bg:r=>t("backgroundOpacity")?se({color:r,property:"background-color",variable:"--tw-bg-opacity"}):{"background-color":N(r)}},{values:re(e("backgroundColor")),type:["color","any"]})},backgroundOpacity:P("backgroundOpacity",[["bg-opacity",["--tw-bg-opacity"]]]),backgroundImage:P("backgroundImage",[["bg",["background-image"]]],{type:["lookup","image","url"]}),gradientColorStops:(()=>{function i(e){return Ie(e,0,"rgb(255 255 255 / 0)")}return function({matchUtilities:e,theme:t,addDefaults:r}){r("gradient-color-stops",{"--tw-gradient-from-position":" ","--tw-gradient-via-position":" ","--tw-gradient-to-position":" "});let n={values:re(t("gradientColorStops")),type:["color","any"]},a={values:t("gradientColorStopPositions"),type:["length","percentage"]};e({from:s=>{let o=i(s);return{"@defaults gradient-color-stops":{},"--tw-gradient-from":`${N(s)} var(--tw-gradient-from-position)`,"--tw-gradient-to":`${o} var(--tw-gradient-to-position)`,"--tw-gradient-stops":"var(--tw-gradient-from), var(--tw-gradient-to)"}}},n),e({from:s=>({"--tw-gradient-from-position":s})},a),e({via:s=>{let o=i(s);return{"@defaults gradient-color-stops":{},"--tw-gradient-to":`${o} var(--tw-gradient-to-position)`,"--tw-gradient-stops":`var(--tw-gradient-from), ${N(s)} var(--tw-gradient-via-position), var(--tw-gradient-to)`}}},n),e({via:s=>({"--tw-gradient-via-position":s})},a),e({to:s=>({"@defaults gradient-color-stops":{},"--tw-gradient-to":`${N(s)} var(--tw-gradient-to-position)`})},n),e({to:s=>({"--tw-gradient-to-position":s})},a)}})(),boxDecorationBreak:({addUtilities:i})=>{i({".decoration-slice":{"box-decoration-break":"slice"},".decoration-clone":{"box-decoration-break":"clone"},".box-decoration-slice":{"box-decoration-break":"slice"},".box-decoration-clone":{"box-decoration-break":"clone"}})},backgroundSize:P("backgroundSize",[["bg",["background-size"]]],{type:["lookup","length","percentage","size"]}),backgroundAttachment:({addUtilities:i})=>{i({".bg-fixed":{"background-attachment":"fixed"},".bg-local":{"background-attachment":"local"},".bg-scroll":{"background-attachment":"scroll"}})},backgroundClip:({addUtilities:i})=>{i({".bg-clip-border":{"background-clip":"border-box"},".bg-clip-padding":{"background-clip":"padding-box"},".bg-clip-content":{"background-clip":"content-box"},".bg-clip-text":{"background-clip":"text"}})},backgroundPosition:P("backgroundPosition",[["bg",["background-position"]]],{type:["lookup",["position",{preferOnConflict:!0}]]}),backgroundRepeat:({addUtilities:i})=>{i({".bg-repeat":{"background-repeat":"repeat"},".bg-no-repeat":{"background-repeat":"no-repeat"},".bg-repeat-x":{"background-repeat":"repeat-x"},".bg-repeat-y":{"background-repeat":"repeat-y"},".bg-repeat-round":{"background-repeat":"round"},".bg-repeat-space":{"background-repeat":"space"}})},backgroundOrigin:({addUtilities:i})=>{i({".bg-origin-border":{"background-origin":"border-box"},".bg-origin-padding":{"background-origin":"padding-box"},".bg-origin-content":{"background-origin":"content-box"}})},fill:({matchUtilities:i,theme:e})=>{i({fill:t=>({fill:N(t)})},{values:re(e("fill")),type:["color","any"]})},stroke:({matchUtilities:i,theme:e})=>{i({stroke:t=>({stroke:N(t)})},{values:re(e("stroke")),type:["color","url","any"]})},strokeWidth:P("strokeWidth",[["stroke",["stroke-width"]]],{type:["length","number","percentage"]}),objectFit:({addUtilities:i})=>{i({".object-contain":{"object-fit":"contain"},".object-cover":{"object-fit":"cover"},".object-fill":{"object-fit":"fill"},".object-none":{"object-fit":"none"},".object-scale-down":{"object-fit":"scale-down"}})},objectPosition:P("objectPosition",[["object",["object-position"]]]),padding:P("padding",[["p",["padding"]],[["px",["padding-left","padding-right"]],["py",["padding-top","padding-bottom"]]],[["ps",["padding-inline-start"]],["pe",["padding-inline-end"]],["pt",["padding-top"]],["pr",["padding-right"]],["pb",["padding-bottom"]],["pl",["padding-left"]]]]),textAlign:({addUtilities:i})=>{i({".text-left":{"text-align":"left"},".text-center":{"text-align":"center"},".text-right":{"text-align":"right"},".text-justify":{"text-align":"justify"},".text-start":{"text-align":"start"},".text-end":{"text-align":"end"}})},textIndent:P("textIndent",[["indent",["text-indent"]]],{supportsNegativeValues:!0}),verticalAlign:({addUtilities:i,matchUtilities:e})=>{i({".align-baseline":{"vertical-align":"baseline"},".align-top":{"vertical-align":"top"},".align-middle":{"vertical-align":"middle"},".align-bottom":{"vertical-align":"bottom"},".align-text-top":{"vertical-align":"text-top"},".align-text-bottom":{"vertical-align":"text-bottom"},".align-sub":{"vertical-align":"sub"},".align-super":{"vertical-align":"super"}}),e({align:t=>({"vertical-align":t})})},fontFamily:({matchUtilities:i,theme:e})=>{i({font:t=>{let[r,n={}]=Array.isArray(t)&&ie(t[1])?t:[t],{fontFeatureSettings:a,fontVariationSettings:s}=n;return{"font-family":Array.isArray(r)?r.join(", "):r,...a===void 0?{}:{"font-feature-settings":a},...s===void 0?{}:{"font-variation-settings":s}}}},{values:e("fontFamily"),type:["lookup","generic-name","family-name"]})},fontSize:({matchUtilities:i,theme:e})=>{i({text:(t,{modifier:r})=>{let[n,a]=Array.isArray(t)?t:[t];if(r)return{"font-size":n,"line-height":r};let{lineHeight:s,letterSpacing:o,fontWeight:u}=ie(a)?a:{lineHeight:a};return{"font-size":n,...s===void 0?{}:{"line-height":s},...o===void 0?{}:{"letter-spacing":o},...u===void 0?{}:{"font-weight":u}}}},{values:e("fontSize"),modifiers:e("lineHeight"),type:["absolute-size","relative-size","length","percentage"]})},fontWeight:P("fontWeight",[["font",["fontWeight"]]],{type:["lookup","number","any"]}),textTransform:({addUtilities:i})=>{i({".uppercase":{"text-transform":"uppercase"},".lowercase":{"text-transform":"lowercase"},".capitalize":{"text-transform":"capitalize"},".normal-case":{"text-transform":"none"}})},fontStyle:({addUtilities:i})=>{i({".italic":{"font-style":"italic"},".not-italic":{"font-style":"normal"}})},fontVariantNumeric:({addDefaults:i,addUtilities:e})=>{let t="var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)";i("font-variant-numeric",{"--tw-ordinal":" ","--tw-slashed-zero":" ","--tw-numeric-figure":" ","--tw-numeric-spacing":" ","--tw-numeric-fraction":" "}),e({".normal-nums":{"font-variant-numeric":"normal"},".ordinal":{"@defaults font-variant-numeric":{},"--tw-ordinal":"ordinal","font-variant-numeric":t},".slashed-zero":{"@defaults font-variant-numeric":{},"--tw-slashed-zero":"slashed-zero","font-variant-numeric":t},".lining-nums":{"@defaults font-variant-numeric":{},"--tw-numeric-figure":"lining-nums","font-variant-numeric":t},".oldstyle-nums":{"@defaults font-variant-numeric":{},"--tw-numeric-figure":"oldstyle-nums","font-variant-numeric":t},".proportional-nums":{"@defaults font-variant-numeric":{},"--tw-numeric-spacing":"proportional-nums","font-variant-numeric":t},".tabular-nums":{"@defaults font-variant-numeric":{},"--tw-numeric-spacing":"tabular-nums","font-variant-numeric":t},".diagonal-fractions":{"@defaults font-variant-numeric":{},"--tw-numeric-fraction":"diagonal-fractions","font-variant-numeric":t},".stacked-fractions":{"@defaults font-variant-numeric":{},"--tw-numeric-fraction":"stacked-fractions","font-variant-numeric":t}})},lineHeight:P("lineHeight",[["leading",["lineHeight"]]]),letterSpacing:P("letterSpacing",[["tracking",["letterSpacing"]]],{supportsNegativeValues:!0}),textColor:({matchUtilities:i,theme:e,corePlugins:t})=>{i({text:r=>t("textOpacity")?se({color:r,property:"color",variable:"--tw-text-opacity"}):{color:N(r)}},{values:re(e("textColor")),type:["color","any"]})},textOpacity:P("textOpacity",[["text-opacity",["--tw-text-opacity"]]]),textDecoration:({addUtilities:i})=>{i({".underline":{"text-decoration-line":"underline"},".overline":{"text-decoration-line":"overline"},".line-through":{"text-decoration-line":"line-through"},".no-underline":{"text-decoration-line":"none"}})},textDecorationColor:({matchUtilities:i,theme:e})=>{i({decoration:t=>({"text-decoration-color":N(t)})},{values:re(e("textDecorationColor")),type:["color","any"]})},textDecorationStyle:({addUtilities:i})=>{i({".decoration-solid":{"text-decoration-style":"solid"},".decoration-double":{"text-decoration-style":"double"},".decoration-dotted":{"text-decoration-style":"dotted"},".decoration-dashed":{"text-decoration-style":"dashed"},".decoration-wavy":{"text-decoration-style":"wavy"}})},textDecorationThickness:P("textDecorationThickness",[["decoration",["text-decoration-thickness"]]],{type:["length","percentage"]}),textUnderlineOffset:P("textUnderlineOffset",[["underline-offset",["text-underline-offset"]]],{type:["length","percentage","any"]}),fontSmoothing:({addUtilities:i})=>{i({".antialiased":{"-webkit-font-smoothing":"antialiased","-moz-osx-font-smoothing":"grayscale"},".subpixel-antialiased":{"-webkit-font-smoothing":"auto","-moz-osx-font-smoothing":"auto"}})},placeholderColor:({matchUtilities:i,theme:e,corePlugins:t})=>{i({placeholder:r=>t("placeholderOpacity")?{"&::placeholder":se({color:r,property:"color",variable:"--tw-placeholder-opacity"})}:{"&::placeholder":{color:N(r)}}},{values:re(e("placeholderColor")),type:["color","any"]})},placeholderOpacity:({matchUtilities:i,theme:e})=>{i({"placeholder-opacity":t=>({["&::placeholder"]:{"--tw-placeholder-opacity":t}})},{values:e("placeholderOpacity")})},caretColor:({matchUtilities:i,theme:e})=>{i({caret:t=>({"caret-color":N(t)})},{values:re(e("caretColor")),type:["color","any"]})},accentColor:({matchUtilities:i,theme:e})=>{i({accent:t=>({"accent-color":N(t)})},{values:re(e("accentColor")),type:["color","any"]})},opacity:P("opacity",[["opacity",["opacity"]]]),backgroundBlendMode:({addUtilities:i})=>{i({".bg-blend-normal":{"background-blend-mode":"normal"},".bg-blend-multiply":{"background-blend-mode":"multiply"},".bg-blend-screen":{"background-blend-mode":"screen"},".bg-blend-overlay":{"background-blend-mode":"overlay"},".bg-blend-darken":{"background-blend-mode":"darken"},".bg-blend-lighten":{"background-blend-mode":"lighten"},".bg-blend-color-dodge":{"background-blend-mode":"color-dodge"},".bg-blend-color-burn":{"background-blend-mode":"color-burn"},".bg-blend-hard-light":{"background-blend-mode":"hard-light"},".bg-blend-soft-light":{"background-blend-mode":"soft-light"},".bg-blend-difference":{"background-blend-mode":"difference"},".bg-blend-exclusion":{"background-blend-mode":"exclusion"},".bg-blend-hue":{"background-blend-mode":"hue"},".bg-blend-saturation":{"background-blend-mode":"saturation"},".bg-blend-color":{"background-blend-mode":"color"},".bg-blend-luminosity":{"background-blend-mode":"luminosity"}})},mixBlendMode:({addUtilities:i})=>{i({".mix-blend-normal":{"mix-blend-mode":"normal"},".mix-blend-multiply":{"mix-blend-mode":"multiply"},".mix-blend-screen":{"mix-blend-mode":"screen"},".mix-blend-overlay":{"mix-blend-mode":"overlay"},".mix-blend-darken":{"mix-blend-mode":"darken"},".mix-blend-lighten":{"mix-blend-mode":"lighten"},".mix-blend-color-dodge":{"mix-blend-mode":"color-dodge"},".mix-blend-color-burn":{"mix-blend-mode":"color-burn"},".mix-blend-hard-light":{"mix-blend-mode":"hard-light"},".mix-blend-soft-light":{"mix-blend-mode":"soft-light"},".mix-blend-difference":{"mix-blend-mode":"difference"},".mix-blend-exclusion":{"mix-blend-mode":"exclusion"},".mix-blend-hue":{"mix-blend-mode":"hue"},".mix-blend-saturation":{"mix-blend-mode":"saturation"},".mix-blend-color":{"mix-blend-mode":"color"},".mix-blend-luminosity":{"mix-blend-mode":"luminosity"},".mix-blend-plus-lighter":{"mix-blend-mode":"plus-lighter"}})},boxShadow:(()=>{let i=Ge("boxShadow"),e=["var(--tw-ring-offset-shadow, 0 0 #0000)","var(--tw-ring-shadow, 0 0 #0000)","var(--tw-shadow)"].join(", ");return function({matchUtilities:t,addDefaults:r,theme:n}){r(" box-shadow",{"--tw-ring-offset-shadow":"0 0 #0000","--tw-ring-shadow":"0 0 #0000","--tw-shadow":"0 0 #0000","--tw-shadow-colored":"0 0 #0000"}),t({shadow:a=>{a=i(a);let s=yi(a);for(let o of s)!o.valid||(o.color="var(--tw-shadow-color)");return{"@defaults box-shadow":{},"--tw-shadow":a==="none"?"0 0 #0000":a,"--tw-shadow-colored":a==="none"?"0 0 #0000":Iu(s),"box-shadow":e}}},{values:n("boxShadow"),type:["shadow"]})}})(),boxShadowColor:({matchUtilities:i,theme:e})=>{i({shadow:t=>({"--tw-shadow-color":N(t),"--tw-shadow":"var(--tw-shadow-colored)"})},{values:re(e("boxShadowColor")),type:["color","any"]})},outlineStyle:({addUtilities:i})=>{i({".outline-none":{outline:"2px solid transparent","outline-offset":"2px"},".outline":{"outline-style":"solid"},".outline-dashed":{"outline-style":"dashed"},".outline-dotted":{"outline-style":"dotted"},".outline-double":{"outline-style":"double"}})},outlineWidth:P("outlineWidth",[["outline",["outline-width"]]],{type:["length","number","percentage"]}),outlineOffset:P("outlineOffset",[["outline-offset",["outline-offset"]]],{type:["length","number","percentage","any"],supportsNegativeValues:!0}),outlineColor:({matchUtilities:i,theme:e})=>{i({outline:t=>({"outline-color":N(t)})},{values:re(e("outlineColor")),type:["color","any"]})},ringWidth:({matchUtilities:i,addDefaults:e,addUtilities:t,theme:r,config:n})=>{let a=(()=>{if(J(n(),"respectDefaultRingColorOpacity"))return r("ringColor.DEFAULT");let s=r("ringOpacity.DEFAULT","0.5");return r("ringColor")?.DEFAULT?Ie(r("ringColor")?.DEFAULT,s,`rgb(147 197 253 / ${s})`):`rgb(147 197 253 / ${s})`})();e("ring-width",{"--tw-ring-inset":" ","--tw-ring-offset-width":r("ringOffsetWidth.DEFAULT","0px"),"--tw-ring-offset-color":r("ringOffsetColor.DEFAULT","#fff"),"--tw-ring-color":a,"--tw-ring-offset-shadow":"0 0 #0000","--tw-ring-shadow":"0 0 #0000","--tw-shadow":"0 0 #0000","--tw-shadow-colored":"0 0 #0000"}),i({ring:s=>({"@defaults ring-width":{},"--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(${s} + 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)"].join(", ")})},{values:r("ringWidth"),type:"length"}),t({".ring-inset":{"@defaults ring-width":{},"--tw-ring-inset":"inset"}})},ringColor:({matchUtilities:i,theme:e,corePlugins:t})=>{i({ring:r=>t("ringOpacity")?se({color:r,property:"--tw-ring-color",variable:"--tw-ring-opacity"}):{"--tw-ring-color":N(r)}},{values:Object.fromEntries(Object.entries(re(e("ringColor"))).filter(([r])=>r!=="DEFAULT")),type:["color","any"]})},ringOpacity:i=>{let{config:e}=i;return P("ringOpacity",[["ring-opacity",["--tw-ring-opacity"]]],{filterDefault:!J(e(),"respectDefaultRingColorOpacity")})(i)},ringOffsetWidth:P("ringOffsetWidth",[["ring-offset",["--tw-ring-offset-width"]]],{type:"length"}),ringOffsetColor:({matchUtilities:i,theme:e})=>{i({"ring-offset":t=>({"--tw-ring-offset-color":N(t)})},{values:re(e("ringOffsetColor")),type:["color","any"]})},blur:({matchUtilities:i,theme:e})=>{i({blur:t=>({"--tw-blur":`blur(${t})`,"@defaults filter":{},filter:Be})},{values:e("blur")})},brightness:({matchUtilities:i,theme:e})=>{i({brightness:t=>({"--tw-brightness":`brightness(${t})`,"@defaults filter":{},filter:Be})},{values:e("brightness")})},contrast:({matchUtilities:i,theme:e})=>{i({contrast:t=>({"--tw-contrast":`contrast(${t})`,"@defaults filter":{},filter:Be})},{values:e("contrast")})},dropShadow:({matchUtilities:i,theme:e})=>{i({"drop-shadow":t=>({"--tw-drop-shadow":Array.isArray(t)?t.map(r=>`drop-shadow(${r})`).join(" "):`drop-shadow(${t})`,"@defaults filter":{},filter:Be})},{values:e("dropShadow")})},grayscale:({matchUtilities:i,theme:e})=>{i({grayscale:t=>({"--tw-grayscale":`grayscale(${t})`,"@defaults filter":{},filter:Be})},{values:e("grayscale")})},hueRotate:({matchUtilities:i,theme:e})=>{i({"hue-rotate":t=>({"--tw-hue-rotate":`hue-rotate(${t})`,"@defaults filter":{},filter:Be})},{values:e("hueRotate"),supportsNegativeValues:!0})},invert:({matchUtilities:i,theme:e})=>{i({invert:t=>({"--tw-invert":`invert(${t})`,"@defaults filter":{},filter:Be})},{values:e("invert")})},saturate:({matchUtilities:i,theme:e})=>{i({saturate:t=>({"--tw-saturate":`saturate(${t})`,"@defaults filter":{},filter:Be})},{values:e("saturate")})},sepia:({matchUtilities:i,theme:e})=>{i({sepia:t=>({"--tw-sepia":`sepia(${t})`,"@defaults filter":{},filter:Be})},{values:e("sepia")})},filter:({addDefaults:i,addUtilities:e})=>{i("filter",{"--tw-blur":" ","--tw-brightness":" ","--tw-contrast":" ","--tw-grayscale":" ","--tw-hue-rotate":" ","--tw-invert":" ","--tw-saturate":" ","--tw-sepia":" ","--tw-drop-shadow":" "}),e({".filter":{"@defaults filter":{},filter:Be},".filter-none":{filter:"none"}})},backdropBlur:({matchUtilities:i,theme:e})=>{i({"backdrop-blur":t=>({"--tw-backdrop-blur":`blur(${t})`,"@defaults backdrop-filter":{},"backdrop-filter":Fe})},{values:e("backdropBlur")})},backdropBrightness:({matchUtilities:i,theme:e})=>{i({"backdrop-brightness":t=>({"--tw-backdrop-brightness":`brightness(${t})`,"@defaults backdrop-filter":{},"backdrop-filter":Fe})},{values:e("backdropBrightness")})},backdropContrast:({matchUtilities:i,theme:e})=>{i({"backdrop-contrast":t=>({"--tw-backdrop-contrast":`contrast(${t})`,"@defaults backdrop-filter":{},"backdrop-filter":Fe})},{values:e("backdropContrast")})},backdropGrayscale:({matchUtilities:i,theme:e})=>{i({"backdrop-grayscale":t=>({"--tw-backdrop-grayscale":`grayscale(${t})`,"@defaults backdrop-filter":{},"backdrop-filter":Fe})},{values:e("backdropGrayscale")})},backdropHueRotate:({matchUtilities:i,theme:e})=>{i({"backdrop-hue-rotate":t=>({"--tw-backdrop-hue-rotate":`hue-rotate(${t})`,"@defaults backdrop-filter":{},"backdrop-filter":Fe})},{values:e("backdropHueRotate"),supportsNegativeValues:!0})},backdropInvert:({matchUtilities:i,theme:e})=>{i({"backdrop-invert":t=>({"--tw-backdrop-invert":`invert(${t})`,"@defaults backdrop-filter":{},"backdrop-filter":Fe})},{values:e("backdropInvert")})},backdropOpacity:({matchUtilities:i,theme:e})=>{i({"backdrop-opacity":t=>({"--tw-backdrop-opacity":`opacity(${t})`,"@defaults backdrop-filter":{},"backdrop-filter":Fe})},{values:e("backdropOpacity")})},backdropSaturate:({matchUtilities:i,theme:e})=>{i({"backdrop-saturate":t=>({"--tw-backdrop-saturate":`saturate(${t})`,"@defaults backdrop-filter":{},"backdrop-filter":Fe})},{values:e("backdropSaturate")})},backdropSepia:({matchUtilities:i,theme:e})=>{i({"backdrop-sepia":t=>({"--tw-backdrop-sepia":`sepia(${t})`,"@defaults backdrop-filter":{},"backdrop-filter":Fe})},{values:e("backdropSepia")})},backdropFilter:({addDefaults:i,addUtilities:e})=>{i("backdrop-filter",{"--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":" "}),e({".backdrop-filter":{"@defaults backdrop-filter":{},"backdrop-filter":Fe},".backdrop-filter-none":{"backdrop-filter":"none"}})},transitionProperty:({matchUtilities:i,theme:e})=>{let t=e("transitionTimingFunction.DEFAULT"),r=e("transitionDuration.DEFAULT");i({transition:n=>({"transition-property":n,...n==="none"?{}:{"transition-timing-function":t,"transition-duration":r}})},{values:e("transitionProperty")})},transitionDelay:P("transitionDelay",[["delay",["transitionDelay"]]]),transitionDuration:P("transitionDuration",[["duration",["transitionDuration"]]],{filterDefault:!0}),transitionTimingFunction:P("transitionTimingFunction",[["ease",["transitionTimingFunction"]]],{filterDefault:!0}),willChange:P("willChange",[["will-change",["will-change"]]]),content:P("content",[["content",["--tw-content",["content","var(--tw-content)"]]]])}});function bC(i){if(i===void 0)return!1;if(i==="true"||i==="1")return!0;if(i==="false"||i==="0")return!1;if(i==="*")return!0;let e=i.split(",").map(t=>t.split(":")[0]);return e.includes("-tailwindcss")?!1:!!e.includes("tailwindcss")}var Pe,wd,bd,gn,Xa,He,Kr,ot=C(()=>{l();Ya();Pe=typeof m!="undefined"?{NODE_ENV:"production",DEBUG:bC(m.env.DEBUG),ENGINE:Ja.tailwindcss.engine}:{NODE_ENV:"production",DEBUG:!1,ENGINE:Ja.tailwindcss.engine},wd=new Map,bd=new Map,gn=new Map,Xa=new Map,He=new String("*"),Kr=Symbol("__NONE__")});function Bt(i){let e=[],t=!1;for(let r=0;r0)}var vd,xd,vC,Ka=C(()=>{l();vd=new Map([["{","}"],["[","]"],["(",")"]]),xd=new Map(Array.from(vd.entries()).map(([i,e])=>[e,i])),vC=new Set(['"',"'","`"])});function Ft(i){let[e]=kd(i);return e.forEach(([t,r])=>t.removeChild(r)),i.nodes.push(...e.map(([,t])=>t)),i}function kd(i){let e=[],t=null;for(let r of i.nodes)if(r.type==="combinator")e=e.filter(([,n])=>eo(n).includes("jumpable")),t=null;else if(r.type==="pseudo"){xC(r)?(t=r,e.push([i,r,null])):t&&kC(r,t)?e.push([i,r,t]):t=null;for(let n of r.nodes??[]){let[a,s]=kd(n);t=s||t,e.push(...a)}}return[e,t]}function Sd(i){return i.value.startsWith("::")||Za[i.value]!==void 0}function xC(i){return Sd(i)&&eo(i).includes("terminal")}function kC(i,e){return i.type!=="pseudo"||Sd(i)?!1:eo(e).includes("actionable")}function eo(i){return Za[i.value]??Za.__default__}var Za,yn=C(()=>{l();Za={"::after":["terminal","jumpable"],"::backdrop":["terminal","jumpable"],"::before":["terminal","jumpable"],"::cue":["terminal"],"::cue-region":["terminal"],"::first-letter":["terminal","jumpable"],"::first-line":["terminal","jumpable"],"::grammar-error":["terminal"],"::marker":["terminal","jumpable"],"::part":["terminal","actionable"],"::placeholder":["terminal","jumpable"],"::selection":["terminal","jumpable"],"::slotted":["terminal"],"::spelling-error":["terminal"],"::target-text":["terminal"],"::file-selector-button":["terminal","actionable"],"::deep":["actionable"],"::v-deep":["actionable"],"::ng-deep":["actionable"],":after":["terminal","jumpable"],":before":["terminal","jumpable"],":first-letter":["terminal","jumpable"],":first-line":["terminal","jumpable"],__default__:["terminal","actionable"]}});function Nt(i,{context:e,candidate:t}){let r=e?.tailwindConfig.prefix??"",n=i.map(s=>{let o=(0,Ne.default)().astSync(s.format);return{...s,ast:s.respectPrefix?Rt(r,o):o}}),a=Ne.default.root({nodes:[Ne.default.selector({nodes:[Ne.default.className({value:ce(t)})]})]});for(let{ast:s}of n)[a,s]=CC(a,s),s.walkNesting(o=>o.replaceWith(...a.nodes[0].nodes)),a=s;return a}function Ad(i){let e=[];for(;i.prev()&&i.prev().type!=="combinator";)i=i.prev();for(;i&&i.type!=="combinator";)e.push(i),i=i.next();return e}function SC(i){return i.sort((e,t)=>e.type==="tag"&&t.type==="class"?-1:e.type==="class"&&t.type==="tag"?1:e.type==="class"&&t.type==="pseudo"&&t.value.startsWith("::")?-1:e.type==="pseudo"&&e.value.startsWith("::")&&t.type==="class"?1:i.index(e)-i.index(t)),i}function ro(i,e){let t=!1;i.walk(r=>{if(r.type==="class"&&r.value===e)return t=!0,!1}),t||i.remove()}function wn(i,e,{context:t,candidate:r,base:n}){let a=t?.tailwindConfig?.separator??":";n=n??r.split(new RegExp(`\\${a}(?![^[]*\\])`)).pop();let s=(0,Ne.default)().astSync(i);s.walkClasses(f=>{f.raws&&f.value.includes(n)&&(f.raws.value=ce((0,Cd.default)(f.raws.value)))}),s.each(f=>ro(f,n));let o=Array.isArray(e)?Nt(e,{context:t,candidate:r}):e;if(o===null)return s.toString();let u=Ne.default.comment({value:"/*__simple__*/"}),c=Ne.default.comment({value:"/*__simple__*/"});return s.walkClasses(f=>{if(f.value!==n)return;let p=f.parent,d=o.nodes[0].nodes;if(p.nodes.length===1){f.replaceWith(...d);return}let h=Ad(f);p.insertBefore(h[0],u),p.insertAfter(h[h.length-1],c);for(let x of d)p.insertBefore(h[0],x.clone());f.remove(),h=Ad(u);let y=p.index(u);p.nodes.splice(y,h.length,...SC(Ne.default.selector({nodes:h})).nodes),u.remove(),c.remove()}),s.walkPseudos(f=>{f.value===to&&f.replaceWith(f.nodes)}),s.each(f=>Ft(f)),s.toString()}function CC(i,e){let t=[];return i.walkPseudos(r=>{r.value===to&&t.push({pseudo:r,value:r.nodes[0].toString()})}),e.walkPseudos(r=>{if(r.value!==to)return;let n=r.nodes[0].toString(),a=t.find(c=>c.value===n);if(!a)return;let s=[],o=r.next();for(;o&&o.type!=="combinator";)s.push(o),o=o.next();let u=o;a.pseudo.parent.insertAfter(a.pseudo,Ne.default.selector({nodes:s.map(c=>c.clone())})),r.remove(),s.forEach(c=>c.remove()),u&&u.type==="combinator"&&u.remove()}),[i,e]}var Ne,Cd,to,io=C(()=>{l();Ne=K(Me()),Cd=K(Yi());Mt();un();yn();to=":merge"});function bn(i,e){let t=(0,no.default)().astSync(i);return t.each(r=>{r.nodes[0].type==="pseudo"&&r.nodes[0].value===":is"&&r.nodes.every(a=>a.type!=="combinator")||(r.nodes=[no.default.pseudo({value:":is",nodes:[r.clone()]})]),Ft(r)}),`${e} ${t.toString()}`}var no,so=C(()=>{l();no=K(Me());yn()});function ao(i){return AC.transformSync(i)}function*_C(i){let e=1/0;for(;e>=0;){let t,r=!1;if(e===1/0&&i.endsWith("]")){let s=i.indexOf("[");i[s-1]==="-"?t=s-1:i[s-1]==="/"?(t=s-1,r=!0):t=-1}else e===1/0&&i.includes("/")?(t=i.lastIndexOf("/"),r=!0):t=i.lastIndexOf("-",e);if(t<0)break;let n=i.slice(0,t),a=i.slice(r?t:t+1);e=t-1,!(n===""||a==="/")&&(yield[n,a])}}function EC(i,e){if(i.length===0||e.tailwindConfig.prefix==="")return i;for(let t of i){let[r]=t;if(r.options.respectPrefix){let n=z.root({nodes:[t[1].clone()]}),a=t[1].raws.tailwind.classCandidate;n.walkRules(s=>{let o=a.startsWith("-");s.selector=Rt(e.tailwindConfig.prefix,s.selector,o)}),t[1]=n.nodes[0]}}return i}function OC(i,e){if(i.length===0)return i;let t=[];for(let[r,n]of i){let a=z.root({nodes:[n.clone()]});a.walkRules(s=>{let o=(0,vn.default)().astSync(s.selector);o.each(u=>ro(u,e)),Wu(o,u=>u===e?`!${u}`:u),s.selector=o.toString(),s.walkDecls(u=>u.important=!0)}),t.push([{...r,important:!0},a.nodes[0]])}return t}function TC(i,e,t){if(e.length===0)return e;let r={modifier:null,value:Kr};{let[n,...a]=le(i,"/");if(a.length>1&&(n=n+"/"+a.slice(0,-1).join("/"),a=a.slice(-1)),a.length&&!t.variantMap.has(i)&&(i=n,r.modifier=a[0],!J(t.tailwindConfig,"generalizedModifiers")))return[]}if(i.endsWith("]")&&!i.startsWith("[")){let n=/(.)(-?)\[(.*)\]/g.exec(i);if(n){let[,a,s,o]=n;if(a==="@"&&s==="-")return[];if(a!=="@"&&s==="")return[];i=i.replace(`${s}[${o}]`,""),r.value=o}}if(lo(i)&&!t.variantMap.has(i)){let n=t.offsets.recordVariant(i),a=U(i.slice(1,-1)),s=le(a,",");if(s.length>1)return[];if(!s.every(An))return[];let o=s.map((u,c)=>[t.offsets.applyParallelOffset(n,c),Zr(u.trim())]);t.variantMap.set(i,o)}if(t.variantMap.has(i)){let n=lo(i),a=t.variantOptions.get(i)?.[Jr]??{},s=t.variantMap.get(i).slice(),o=[],u=(()=>!(n||a.respectPrefix===!1))();for(let[c,f]of e){if(c.layer==="user")continue;let p=z.root({nodes:[f.clone()]});for(let[d,h,y]of s){let b=function(){x.raws.neededBackup||(x.raws.neededBackup=!0,x.walkRules(E=>E.raws.originalSelector=E.selector))},k=function(E){return b(),x.each(I=>{I.type==="rule"&&(I.selectors=I.selectors.map(B=>E({get className(){return ao(B)},selector:B})))}),x},x=(y??p).clone(),w=[],S=h({get container(){return b(),x},separator:t.tailwindConfig.separator,modifySelectors:k,wrap(E){let I=x.nodes;x.removeAll(),E.append(I),x.append(E)},format(E){w.push({format:E,respectPrefix:u})},args:r});if(Array.isArray(S)){for(let[E,I]of S.entries())s.push([t.offsets.applyParallelOffset(d,E),I,x.clone()]);continue}if(typeof S=="string"&&w.push({format:S,respectPrefix:u}),S===null)continue;x.raws.neededBackup&&(delete x.raws.neededBackup,x.walkRules(E=>{let I=E.raws.originalSelector;if(!I||(delete E.raws.originalSelector,I===E.selector))return;let B=E.selector,q=(0,vn.default)(X=>{X.walkClasses(ae=>{ae.value=`${i}${t.tailwindConfig.separator}${ae.value}`})}).processSync(I);w.push({format:B.replace(q,"&"),respectPrefix:u}),E.selector=I})),x.nodes[0].raws.tailwind={...x.nodes[0].raws.tailwind,parentLayer:c.layer};let _=[{...c,sort:t.offsets.applyVariantOffset(c.sort,d,Object.assign(r,t.variantOptions.get(i))),collectedFormats:(c.collectedFormats??[]).concat(w)},x.nodes[0]];o.push(_)}}return o}return[]}function oo(i,e,t={}){return!ie(i)&&!Array.isArray(i)?[[i],t]:Array.isArray(i)?oo(i[0],e,i[1]):(e.has(i)||e.set(i,qt(i)),[e.get(i),t])}function DC(i){return PC.test(i)}function IC(i){if(!i.includes("://"))return!1;try{let e=new URL(i);return e.scheme!==""&&e.host!==""}catch(e){return!1}}function _d(i){let e=!0;return i.walkDecls(t=>{if(!Ed(t.prop,t.value))return e=!1,!1}),e}function Ed(i,e){if(IC(`${i}:${e}`))return!1;try{return z.parse(`a{${i}:${e}}`).toResult(),!0}catch(t){return!1}}function qC(i,e){let[,t,r]=i.match(/^\[([a-zA-Z0-9-_]+):(\S+)\]$/)??[];if(r===void 0||!DC(t)||!Bt(r))return null;let n=U(r);return Ed(t,n)?[[{sort:e.offsets.arbitraryProperty(),layer:"utilities"},()=>({[Wa(i)]:{[t]:n}})]]:null}function*RC(i,e){e.candidateRuleMap.has(i)&&(yield[e.candidateRuleMap.get(i),"DEFAULT"]),yield*function*(o){o!==null&&(yield[o,"DEFAULT"])}(qC(i,e));let t=i,r=!1,n=e.tailwindConfig.prefix,a=n.length,s=t.startsWith(n)||t.startsWith(`-${n}`);t[a]==="-"&&s&&(r=!0,t=n+t.slice(a+1)),r&&e.candidateRuleMap.has(t)&&(yield[e.candidateRuleMap.get(t),"-DEFAULT"]);for(let[o,u]of _C(t))e.candidateRuleMap.has(o)&&(yield[e.candidateRuleMap.get(o),r?`-${u}`:u])}function MC(i,e){return i===He?[He]:le(i,e)}function*BC(i,e){for(let t of i)t[1].raws.tailwind={...t[1].raws.tailwind,classCandidate:e,preserveSource:t[0].options?.preserveSource??!1},yield t}function*xn(i,e,t=i){let r=e.tailwindConfig.separator,[n,...a]=MC(i,r).reverse(),s=!1;if(n.startsWith("!")&&(s=!0,n=n.slice(1)),J(e.tailwindConfig,"variantGrouping")&&n.startsWith("(")&&n.endsWith(")")){let o=a.slice().reverse().join(r);for(let u of le(n.slice(1,-1),","))yield*xn(o+r+u,e,t)}for(let o of RC(n,e)){let u=[],c=new Map,[f,p]=o,d=f.length===1;for(let[h,y]of f){let x=[];if(typeof y=="function")for(let w of[].concat(y(p,{isOnlyPlugin:d}))){let[b,k]=oo(w,e.postCssNodeCache);for(let S of b)x.push([{...h,options:{...h.options,...k}},S])}else if(p==="DEFAULT"||p==="-DEFAULT"){let w=y,[b,k]=oo(w,e.postCssNodeCache);for(let S of b)x.push([{...h,options:{...h.options,...k}},S])}if(x.length>0){let w=Array.from(ps(h.options?.types??[],p,h.options??{},e.tailwindConfig)).map(([b,k])=>k);w.length>0&&c.set(x,w),u.push(x)}}if(lo(p)){if(u.length>1){let x=function(b){return b.length===1?b[0]:b.find(k=>{let S=c.get(k);return k.some(([{options:_},E])=>_d(E)?_.types.some(({type:I,preferOnConflict:B})=>S.includes(I)&&B):!1)})},[h,y]=u.reduce((b,k)=>(k.some(([{options:_}])=>_.types.some(({type:E})=>E==="any"))?b[0].push(k):b[1].push(k),b),[[],[]]),w=x(y)??x(h);if(w)u=[w];else{let b=u.map(S=>new Set([...c.get(S)??[]]));for(let S of b)for(let _ of S){let E=!1;for(let I of b)S!==I&&I.has(_)&&(I.delete(_),E=!0);E&&S.delete(_)}let k=[];for(let[S,_]of b.entries())for(let E of _){let I=u[S].map(([,B])=>B).flat().map(B=>B.toString().split(` +`).slice(1,-1).map(q=>q.trim()).map(q=>` ${q}`).join(` +`)).join(` + +`);k.push(` Use \`${i.replace("[",`[${E}:`)}\` for \`${I.trim()}\``);break}F.warn([`The class \`${i}\` is ambiguous and matches multiple utilities.`,...k,`If this is content and not a class, replace it with \`${i.replace("[","[").replace("]","]")}\` to silence this warning.`]);continue}}u=u.map(h=>h.filter(y=>_d(y[1])))}u=u.flat(),u=Array.from(BC(u,n)),u=EC(u,e),s&&(u=OC(u,n));for(let h of a)u=TC(h,u,e);for(let h of u)h[1].raws.tailwind={...h[1].raws.tailwind,candidate:i},h=FC(h,{context:e,candidate:i,original:t}),h!==null&&(yield h)}}function FC(i,{context:e,candidate:t,original:r}){if(!i[0].collectedFormats)return i;let n=!0,a;try{a=Nt(i[0].collectedFormats,{context:e,candidate:t})}catch{return null}let s=z.root({nodes:[i[1].clone()]});return s.walkRules(o=>{if(!kn(o))try{o.selector=wn(o.selector,a,{candidate:r,context:e})}catch{return n=!1,!1}}),n?(i[1]=s.nodes[0],i):null}function kn(i){return i.parent&&i.parent.type==="atrule"&&i.parent.name==="keyframes"}function NC(i){if(i===!0)return e=>{kn(e)||e.walkDecls(t=>{t.parent.type==="rule"&&!kn(t.parent)&&(t.important=!0)})};if(typeof i=="string")return e=>{kn(e)||(e.selectors=e.selectors.map(t=>bn(t,i)))}}function Sn(i,e){let t=[],r=NC(e.tailwindConfig.important);for(let n of i){if(e.notClassCache.has(n))continue;if(e.candidateRuleCache.has(n)){t=t.concat(Array.from(e.candidateRuleCache.get(n)));continue}let a=Array.from(xn(n,e));if(a.length===0){e.notClassCache.add(n);continue}e.classCache.set(n,a);let s=e.candidateRuleCache.get(n)??new Set;e.candidateRuleCache.set(n,s);for(let o of a){let[{sort:u,options:c},f]=o;if(c.respectImportant&&r){let d=z.root({nodes:[f.clone()]});d.walkRules(r),f=d.nodes[0]}let p=[u,f];s.add(p),e.ruleCache.add(p),t.push(p)}}return t}function lo(i){return i.startsWith("[")&&i.endsWith("]")}var vn,AC,PC,Cn=C(()=>{l();nt();vn=K(Me());Ua();xt();un();cr();Ee();ot();io();Ga();fr();Xr();Ka();or();De();so();AC=(0,vn.default)(i=>i.first.filter(({type:e})=>e==="class").pop().value);PC=/^[a-z_-]/});var Od,Td=C(()=>{l();Od={}});function LC(i){try{return Od.createHash("md5").update(i,"utf-8").digest("binary")}catch(e){return""}}function Pd(i,e){let t=e.toString();if(!t.includes("@tailwind"))return!1;let r=Xa.get(i),n=LC(t),a=r!==n;return Xa.set(i,n),a}var Dd=C(()=>{l();Td();ot()});function _n(i){return(i>0n)-(i<0n)}var Id=C(()=>{l()});function qd(i,e){let t=0n,r=0n;for(let[n,a]of e)i&n&&(t=t|n,r=r|a);return i&~t|r}var Rd=C(()=>{l()});function Md(i){let e=null;for(let t of i)e=e??t,e=e>t?e:t;return e}function $C(i,e){let t=i.length,r=e.length,n=t{l();Id();Rd();uo=class{constructor(){this.offsets={defaults:0n,base:0n,components:0n,utilities:0n,variants:0n,user:0n},this.layerPositions={defaults:0n,base:1n,components:2n,utilities:3n,user:4n,variants:5n},this.reservedVariantBits=0n,this.variantOffsets=new Map}create(e){return{layer:e,parentLayer:e,arbitrary:0n,variants:0n,parallelIndex:0n,index:this.offsets[e]++,options:[]}}arbitraryProperty(){return{...this.create("utilities"),arbitrary:1n}}forVariant(e,t=0){let r=this.variantOffsets.get(e);if(r===void 0)throw new Error(`Cannot find offset for unknown variant ${e}`);return{...this.create("variants"),variants:r<n.startsWith("[")).sort(([n],[a])=>$C(n,a)),t=e.map(([,n])=>n).sort((n,a)=>_n(n-a));return e.map(([,n],a)=>[n,t[a]]).filter(([n,a])=>n!==a)}remapArbitraryVariantOffsets(e){let t=this.recalculateVariantOffsets();return t.length===0?e:e.map(r=>{let[n,a]=r;return n={...n,variants:qd(n.variants,t)},[n,a]})}sort(e){return e=this.remapArbitraryVariantOffsets(e),e.sort(([t],[r])=>_n(this.compare(t,r)))}}});function ho(i,e){let t=i.tailwindConfig.prefix;return typeof t=="function"?t(e):t+e}function Nd({type:i="any",...e}){let t=[].concat(i);return{...e,types:t.map(r=>Array.isArray(r)?{type:r[0],...r[1]}:{type:r,preferOnConflict:!1})}}function jC(i){let e=[],t="",r=0;for(let n=0;n0&&e.push(t.trim()),e=e.filter(n=>n!==""),e}function zC(i,e,{before:t=[]}={}){if(t=[].concat(t),t.length<=0){i.push(e);return}let r=i.length-1;for(let n of t){let a=i.indexOf(n);a!==-1&&(r=Math.min(r,a))}i.splice(r,0,e)}function Ld(i){return Array.isArray(i)?i.flatMap(e=>!Array.isArray(e)&&!ie(e)?e:qt(e)):Ld([i])}function $d(i,e){return(0,fo.default)(r=>{let n=[];return e&&e(r),r.walkClasses(a=>{n.push(a.value)}),n}).transformSync(i)}function VC(i,e={containsNonOnDemandable:!1},t=0){let r=[];if(i.type==="rule"){let n=function(a){a.walkPseudos(s=>{s.value===":not"&&s.remove()})};for(let a of i.selectors){let s=$d(a,n);s.length===0&&(e.containsNonOnDemandable=!0);for(let o of s)r.push(o)}}else i.type==="atrule"&&i.walkRules(n=>{for(let a of n.selectors.flatMap(s=>$d(s)))r.push(a)});return t===0?[e.containsNonOnDemandable||r.length===0,r]:r}function En(i){return Ld(i).flatMap(e=>{let t=new Map,[r,n]=VC(e);return r&&n.unshift(He),n.map(a=>(t.has(e)||t.set(e,e),[a,t.get(e)]))})}function An(i){return i.startsWith("@")||i.includes("&")}function Zr(i){i=i.replace(/\n+/g,"").replace(/\s{1,}/g," ").trim();let e=jC(i).map(t=>{if(!t.startsWith("@"))return({format:a})=>a(t);let[,r,n]=/@(\S*)( .+|[({].*)?/g.exec(t);return({wrap:a})=>a(z.atRule({name:r,params:n?.trim()??""}))}).reverse();return t=>{for(let r of e)r(t)}}function UC(i,e,{variantList:t,variantMap:r,offsets:n,classList:a}){function s(d,h){return d?(0,Fd.default)(i,d,h):i}function o(d){return Rt(i.prefix,d)}function u(d,h){return d===He?He:h.respectPrefix?e.tailwindConfig.prefix+d:d}function c(d,h,y={}){let x=Ke(d),w=s(["theme",...x],h);return Ge(x[0])(w,y)}let f=0,p={postcss:z,prefix:o,e:ce,config:s,theme:c,corePlugins:d=>Array.isArray(i.corePlugins)?i.corePlugins.includes(d):s(["corePlugins",d],!0),variants:()=>[],addBase(d){for(let[h,y]of En(d)){let x=u(h,{}),w=n.create("base");e.candidateRuleMap.has(x)||e.candidateRuleMap.set(x,[]),e.candidateRuleMap.get(x).push([{sort:w,layer:"base"},y])}},addDefaults(d,h){let y={[`@defaults ${d}`]:h};for(let[x,w]of En(y)){let b=u(x,{});e.candidateRuleMap.has(b)||e.candidateRuleMap.set(b,[]),e.candidateRuleMap.get(b).push([{sort:n.create("defaults"),layer:"defaults"},w])}},addComponents(d,h){h=Object.assign({},{preserveSource:!1,respectPrefix:!0,respectImportant:!1},Array.isArray(h)?{}:h);for(let[x,w]of En(d)){let b=u(x,h);a.add(b),e.candidateRuleMap.has(b)||e.candidateRuleMap.set(b,[]),e.candidateRuleMap.get(b).push([{sort:n.create("components"),layer:"components",options:h},w])}},addUtilities(d,h){h=Object.assign({},{preserveSource:!1,respectPrefix:!0,respectImportant:!0},Array.isArray(h)?{}:h);for(let[x,w]of En(d)){let b=u(x,h);a.add(b),e.candidateRuleMap.has(b)||e.candidateRuleMap.set(b,[]),e.candidateRuleMap.get(b).push([{sort:n.create("utilities"),layer:"utilities",options:h},w])}},matchUtilities:function(d,h){h=Nd({...{respectPrefix:!0,respectImportant:!0,modifiers:!1},...h});let x=n.create("utilities");for(let w in d){let S=function(E,{isOnlyPlugin:I}){let[B,q,X]=cs(h.types,E,h,i);if(B===void 0)return[];if(!h.types.some(({type:$})=>$===q))if(I)F.warn([`Unnecessary typehint \`${q}\` in \`${w}-${E}\`.`,`You can safely update it to \`${w}-${E.replace(q+":","")}\`.`]);else return[];if(!Bt(B))return[];let ae={get modifier(){return h.modifiers||F.warn(`modifier-used-without-options-for-${w}`,["Your plugin must set `modifiers: true` in its options to support modifiers."]),X}},ge=J(i,"generalizedModifiers");return[].concat(ge?k(B,ae):k(B)).filter(Boolean).map($=>({[fn(w,E)]:$}))},b=u(w,h),k=d[w];a.add([b,h]);let _=[{sort:x,layer:"utilities",options:h},S];e.candidateRuleMap.has(b)||e.candidateRuleMap.set(b,[]),e.candidateRuleMap.get(b).push(_)}},matchComponents:function(d,h){h=Nd({...{respectPrefix:!0,respectImportant:!1,modifiers:!1},...h});let x=n.create("components");for(let w in d){let S=function(E,{isOnlyPlugin:I}){let[B,q,X]=cs(h.types,E,h,i);if(B===void 0)return[];if(!h.types.some(({type:$})=>$===q))if(I)F.warn([`Unnecessary typehint \`${q}\` in \`${w}-${E}\`.`,`You can safely update it to \`${w}-${E.replace(q+":","")}\`.`]);else return[];if(!Bt(B))return[];let ae={get modifier(){return h.modifiers||F.warn(`modifier-used-without-options-for-${w}`,["Your plugin must set `modifiers: true` in its options to support modifiers."]),X}},ge=J(i,"generalizedModifiers");return[].concat(ge?k(B,ae):k(B)).filter(Boolean).map($=>({[fn(w,E)]:$}))},b=u(w,h),k=d[w];a.add([b,h]);let _=[{sort:x,layer:"components",options:h},S];e.candidateRuleMap.has(b)||e.candidateRuleMap.set(b,[]),e.candidateRuleMap.get(b).push(_)}},addVariant(d,h,y={}){h=[].concat(h).map(x=>{if(typeof x!="string")return(w={})=>{let{args:b,modifySelectors:k,container:S,separator:_,wrap:E,format:I}=w,B=x(Object.assign({modifySelectors:k,container:S,separator:_},y.type===co.MatchVariant&&{args:b,wrap:E,format:I}));if(typeof B=="string"&&!An(B))throw new Error(`Your custom variant \`${d}\` has an invalid format string. Make sure it's an at-rule or contains a \`&\` placeholder.`);return Array.isArray(B)?B.filter(q=>typeof q=="string").map(q=>Zr(q)):B&&typeof B=="string"&&Zr(B)(w)};if(!An(x))throw new Error(`Your custom variant \`${d}\` has an invalid format string. Make sure it's an at-rule or contains a \`&\` placeholder.`);return Zr(x)}),zC(t,d,y),r.set(d,h),e.variantOptions.set(d,y)},matchVariant(d,h,y){let x=y?.id??++f,w=d==="@",b=J(i,"generalizedModifiers");for(let[S,_]of Object.entries(y?.values??{}))S!=="DEFAULT"&&p.addVariant(w?`${d}${S}`:`${d}-${S}`,({args:E,container:I})=>h(_,b?{modifier:E?.modifier,container:I}:{container:I}),{...y,value:_,id:x,type:co.MatchVariant,variantInfo:po.Base});let k="DEFAULT"in(y?.values??{});p.addVariant(d,({args:S,container:_})=>S?.value===Kr&&!k?null:h(S?.value===Kr?y.values.DEFAULT:S?.value??(typeof S=="string"?S:""),b?{modifier:S?.modifier,container:_}:{container:_}),{...y,id:x,type:co.MatchVariant,variantInfo:po.Dynamic})}};return p}function On(i){return mo.has(i)||mo.set(i,new Map),mo.get(i)}function jd(i,e){let t=!1,r=new Map;for(let n of i){if(!n)continue;let a=ws.parse(n),s=a.hash?a.href.replace(a.hash,""):a.href;s=a.search?s.replace(a.search,""):s;let o=te.statSync(decodeURIComponent(s),{throwIfNoEntry:!1})?.mtimeMs;!o||((!e.has(n)||o>e.get(n))&&(t=!0),r.set(n,o))}return[t,r]}function zd(i){i.walkAtRules(e=>{["responsive","variants"].includes(e.name)&&(zd(e),e.before(e.nodes),e.remove())})}function WC(i){let e=[];return i.each(t=>{t.type==="atrule"&&["responsive","variants"].includes(t.name)&&(t.name="layer",t.params="utilities")}),i.walkAtRules("layer",t=>{if(zd(t),t.params==="base"){for(let r of t.nodes)e.push(function({addBase:n}){n(r,{respectPrefix:!1})});t.remove()}else if(t.params==="components"){for(let r of t.nodes)e.push(function({addComponents:n}){n(r,{respectPrefix:!1,preserveSource:!0})});t.remove()}else if(t.params==="utilities"){for(let r of t.nodes)e.push(function({addUtilities:n}){n(r,{respectPrefix:!1,preserveSource:!0})});t.remove()}}),e}function GC(i,e){let t=Object.entries({...pe,...gd}).map(([o,u])=>i.tailwindConfig.corePlugins.includes(o)?u:null).filter(Boolean),r=i.tailwindConfig.plugins.map(o=>(o.__isOptionsFunction&&(o=o()),typeof o=="function"?o:o.handler)),n=WC(e),a=[pe.pseudoElementVariants,pe.pseudoClassVariants,pe.ariaVariants,pe.dataVariants],s=[pe.supportsVariants,pe.directionVariants,pe.reducedMotionVariants,pe.prefersContrastVariants,pe.darkVariants,pe.printVariant,pe.screenVariants,pe.orientationVariants];return[...t,...a,...r,...s,...n]}function HC(i,e){let t=[],r=new Map;e.variantMap=r;let n=new uo;e.offsets=n;let a=new Set,s=UC(e.tailwindConfig,e,{variantList:t,variantMap:r,offsets:n,classList:a});for(let f of i)if(Array.isArray(f))for(let p of f)p(s);else f?.(s);n.recordVariants(t,f=>r.get(f).length);for(let[f,p]of r.entries())e.variantMap.set(f,p.map((d,h)=>[n.forVariant(f,h),d]));let o=(e.tailwindConfig.safelist??[]).filter(Boolean);if(o.length>0){let f=[];for(let p of o){if(typeof p=="string"){e.changedContent.push({content:p,extension:"html"});continue}if(p instanceof RegExp){F.warn("root-regex",["Regular expressions in `safelist` work differently in Tailwind CSS v3.0.","Update your `safelist` configuration to eliminate this warning.","https://tailwindcss.com/docs/content-configuration#safelisting-classes"]);continue}f.push(p)}if(f.length>0){let p=new Map,d=e.tailwindConfig.prefix.length,h=f.some(y=>y.pattern.source.includes("!"));for(let y of a){let x=Array.isArray(y)?(()=>{let[w,b]=y,S=Object.keys(b?.values??{}).map(_=>Qr(w,_));return b?.supportsNegativeValues&&(S=[...S,...S.map(_=>"-"+_)],S=[...S,...S.map(_=>_.slice(0,d)+"-"+_.slice(d))]),b.types.some(({type:_})=>_==="color")&&(S=[...S,...S.flatMap(_=>Object.keys(e.tailwindConfig.theme.opacity).map(E=>`${_}/${E}`))]),h&&b?.respectImportant&&(S=[...S,...S.map(_=>"!"+_)]),S})():[y];for(let w of x)for(let{pattern:b,variants:k=[]}of f)if(b.lastIndex=0,p.has(b)||p.set(b,0),!!b.test(w)){p.set(b,p.get(b)+1),e.changedContent.push({content:w,extension:"html"});for(let S of k)e.changedContent.push({content:S+e.tailwindConfig.separator+w,extension:"html"})}}for(let[y,x]of p.entries())x===0&&F.warn([`The safelist pattern \`${y}\` doesn't match any Tailwind CSS classes.`,"Fix this pattern or remove it from your `safelist` configuration.","https://tailwindcss.com/docs/content-configuration#safelisting-classes"])}}let u=[].concat(e.tailwindConfig.darkMode??"media")[1]??"dark",c=[ho(e,u),ho(e,"group"),ho(e,"peer")];e.getClassOrder=function(p){let d=[...p].sort((w,b)=>w===b?0:w[w,null])),y=Sn(new Set(d),e);y=e.offsets.sort(y);let x=BigInt(c.length);for(let[,w]of y){let b=w.raws.tailwind.candidate;h.set(b,h.get(b)??x++)}return p.map(w=>{let b=h.get(w)??null,k=c.indexOf(w);return b===null&&k!==-1&&(b=BigInt(k)),[w,b]})},e.getClassList=function(p={}){let d=[];for(let h of a)if(Array.isArray(h)){let[y,x]=h,w=[],b=Object.keys(x?.modifiers??{});x?.types?.some(({type:_})=>_==="color")&&b.push(...Object.keys(e.tailwindConfig.theme.opacity??{}));let k={modifiers:b},S=p.includeMetadata&&b.length>0;for(let[_,E]of Object.entries(x?.values??{})){if(E==null)continue;let I=Qr(y,_);if(d.push(S?[I,k]:I),x?.supportsNegativeValues&&Xe(E)){let B=Qr(y,`-${_}`);w.push(S?[B,k]:B)}}d.push(...w)}else d.push(h);return d},e.getVariants=function(){let p=[];for(let[d,h]of e.variantOptions.entries())h.variantInfo!==po.Base&&p.push({name:d,isArbitrary:h.type===Symbol.for("MATCH_VARIANT"),values:Object.keys(h.values??{}),hasDash:d!=="@",selectors({modifier:y,value:x}={}){let w="__TAILWIND_PLACEHOLDER__",b=z.rule({selector:`.${w}`}),k=z.root({nodes:[b.clone()]}),S=k.toString(),_=(e.variantMap.get(d)??[]).flatMap(([$,oe])=>oe),E=[];for(let $ of _){let oe=[],ai={args:{modifier:y,value:h.values?.[x]??x},separator:e.tailwindConfig.separator,modifySelectors(Ce){return k.each(Jn=>{Jn.type==="rule"&&(Jn.selectors=Jn.selectors.map(lu=>Ce({get className(){return ao(lu)},selector:lu})))}),k},format(Ce){oe.push(Ce)},wrap(Ce){oe.push(`@${Ce.name} ${Ce.params} { & }`)},container:k},oi=$(ai);if(oe.length>0&&E.push(oe),Array.isArray(oi))for(let Ce of oi)oe=[],Ce(ai),E.push(oe)}let I=[],B=k.toString();S!==B&&(k.walkRules($=>{let oe=$.selector,ai=(0,fo.default)(oi=>{oi.walkClasses(Ce=>{Ce.value=`${d}${e.tailwindConfig.separator}${Ce.value}`})}).processSync(oe);I.push(oe.replace(ai,"&").replace(w,"&"))}),k.walkAtRules($=>{I.push(`@${$.name} (${$.params}) { & }`)}));let q=!(x in(h.values??{})),X=h[Jr]??{},ae=(()=>!(q||X.respectPrefix===!1))();E=E.map($=>$.map(oe=>({format:oe,respectPrefix:ae}))),I=I.map($=>({format:$,respectPrefix:ae}));let ge={candidate:w,context:e},je=E.map($=>wn(`.${w}`,Nt($,ge),ge).replace(`.${w}`,"&").replace("{ & }","").trim());return I.length>0&&je.push(Nt(I,ge).toString().replace(`.${w}`,"&")),je}});return p}}function Vd(i,e){!i.classCache.has(e)||(i.notClassCache.add(e),i.classCache.delete(e),i.applyClassCache.delete(e),i.candidateRuleMap.delete(e),i.candidateRuleCache.delete(e),i.stylesheetCache=null)}function YC(i,e){let t=e.raws.tailwind.candidate;if(!!t){for(let r of i.ruleCache)r[1].raws.tailwind.candidate===t&&i.ruleCache.delete(r);Vd(i,t)}}function go(i,e=[],t=z.root()){let r={disposables:[],ruleCache:new Set,candidateRuleCache:new Map,classCache:new Map,applyClassCache:new Map,notClassCache:new Set(i.blocklist??[]),postCssNodeCache:new Map,candidateRuleMap:new Map,tailwindConfig:i,changedContent:e,variantMap:new Map,stylesheetCache:null,variantOptions:new Map,markInvalidUtilityCandidate:a=>Vd(r,a),markInvalidUtilityNode:a=>YC(r,a)},n=GC(r,t);return HC(n,r),r}function Ud(i,e,t,r,n,a){let s=e.opts.from,o=r!==null;Pe.DEBUG&&console.log("Source path:",s);let u;if(o&&Lt.has(s))u=Lt.get(s);else if(ei.has(n)){let d=ei.get(n);lt.get(d).add(s),Lt.set(s,d),u=d}let c=Pd(s,i);if(u){let[d,h]=jd([...a],On(u));if(!d&&!c)return[u,!1,h]}if(Lt.has(s)){let d=Lt.get(s);if(lt.has(d)&&(lt.get(d).delete(s),lt.get(d).size===0)){lt.delete(d);for(let[h,y]of ei)y===d&&ei.delete(h);for(let h of d.disposables.splice(0))h(d)}}Pe.DEBUG&&console.log("Setting up new context...");let f=go(t,[],i);Object.assign(f,{userConfigPath:r});let[,p]=jd([...a],On(f));return ei.set(n,f),Lt.set(s,f),lt.has(f)||lt.set(f,new Set),lt.get(f).add(s),[f,!0,p]}var Fd,fo,Jr,co,po,mo,Lt,ei,lt,Xr=C(()=>{l();ze();bs();nt();Fd=K(js()),fo=K(Me());Hr();Ua();un();xt();Mt();Ga();cr();yd();ot();ot();pi();Ee();fi();Ka();Cn();Dd();Bd();De();io();Jr=Symbol(),co={AddVariant:Symbol.for("ADD_VARIANT"),MatchVariant:Symbol.for("MATCH_VARIANT")},po={Base:1<<0,Dynamic:1<<1};mo=new WeakMap;Lt=wd,ei=bd,lt=gn});function yo(i){return i.ignore?[]:i.glob?m.env.ROLLUP_WATCH==="true"?[{type:"dependency",file:i.base}]:[{type:"dir-dependency",dir:i.base,glob:i.glob}]:[{type:"dependency",file:i.base}]}var Wd=C(()=>{l()});function Gd(i,e){return{handler:i,config:e}}var Hd,Yd=C(()=>{l();Gd.withOptions=function(i,e=()=>({})){let t=function(r){return{__options:r,handler:i(r),config:e(r)}};return t.__isOptionsFunction=!0,t.__pluginFunction=i,t.__configFunction=e,t};Hd=Gd});var wo={};Ae(wo,{default:()=>QC});var QC,bo=C(()=>{l();Yd();QC=Hd});var Jd=v((ID,Qd)=>{l();var JC=(bo(),wo).default,XC={overflow:"hidden",display:"-webkit-box","-webkit-box-orient":"vertical"},KC=JC(function({matchUtilities:i,addUtilities:e,theme:t,variants:r}){let n=t("lineClamp");i({"line-clamp":a=>({...XC,"-webkit-line-clamp":`${a}`})},{values:n}),e([{".line-clamp-none":{"-webkit-line-clamp":"unset"}}],r("lineClamp"))},{theme:{lineClamp:{1:"1",2:"2",3:"3",4:"4",5:"5",6:"6"}},variants:{lineClamp:["responsive"]}});Qd.exports=KC});function vo(i){i.content.files.length===0&&F.warn("content-problems",["The `content` option in your Tailwind CSS configuration is missing or empty.","Configure your content sources or your generated CSS will be missing styles.","https://tailwindcss.com/docs/content-configuration"]);try{let e=Jd();i.plugins.includes(e)&&(F.warn("line-clamp-in-core",["As of Tailwind CSS v3.3, the `@tailwindcss/line-clamp` plugin is now included by default.","Remove it from the `plugins` array in your configuration to eliminate this warning."]),i.plugins=i.plugins.filter(t=>t!==e))}catch{}return i}var Xd=C(()=>{l();Ee()});var Kd,Zd=C(()=>{l();Kd=()=>!1});var Tn,eh=C(()=>{l();Tn={sync:i=>[].concat(i),generateTasks:i=>[{dynamic:!1,base:".",negative:[],positive:[].concat(i),patterns:[].concat(i)}],escapePath:i=>i}});var xo,th=C(()=>{l();xo=i=>i});var rh,ih=C(()=>{l();rh=()=>""});function nh(i){let e=i,t=rh(i);return t!=="."&&(e=i.substr(t.length),e.charAt(0)==="/"&&(e=e.substr(1))),e.substr(0,2)==="./"&&(e=e.substr(2)),e.charAt(0)==="/"&&(e=e.substr(1)),{base:t,glob:e}}var sh=C(()=>{l();ih()});function ah(i,e){let t=e.content.files;t=t.filter(o=>typeof o=="string"),t=t.map(xo);let r=Tn.generateTasks(t),n=[],a=[];for(let o of r)n.push(...o.positive.map(u=>oh(u,!1))),a.push(...o.negative.map(u=>oh(u,!0)));let s=[...n,...a];return s=e2(i,s),s=s.flatMap(t2),s=s.map(ZC),s}function oh(i,e){let t={original:i,base:i,ignore:e,pattern:i,glob:null};return Kd(i)&&Object.assign(t,nh(i)),t}function ZC(i){let e=xo(i.base);return e=Tn.escapePath(e),i.pattern=i.glob?`${e}/${i.glob}`:e,i.pattern=i.ignore?`!${i.pattern}`:i.pattern,i}function e2(i,e){let t=[];return i.userConfigPath&&i.tailwindConfig.content.relative&&(t=[Z.dirname(i.userConfigPath)]),e.map(r=>(r.base=Z.resolve(...t,r.base),r))}function t2(i){let e=[i];try{let t=te.realpathSync(i.base);t!==i.base&&e.push({...i,base:t})}catch{}return e}function lh(i,e,t){let r=i.tailwindConfig.content.files.filter(s=>typeof s.raw=="string").map(({raw:s,extension:o="html"})=>({content:s,extension:o})),[n,a]=r2(e,t);for(let s of n){let o=Z.extname(s).slice(1);r.push({file:s,extension:o})}return[r,a]}function r2(i,e){let t=i.map(s=>s.pattern),r=new Map,n=new Set;Pe.DEBUG&&console.time("Finding changed files");let a=Tn.sync(t,{absolute:!0});for(let s of a){let o=e.get(s)||-1/0,u=te.statSync(s).mtimeMs;u>o&&(n.add(s),r.set(s,u))}return Pe.DEBUG&&console.timeEnd("Finding changed files"),[n,r]}var uh=C(()=>{l();ze();mt();Zd();eh();th();sh();ot()});function fh(){}var ch=C(()=>{l()});function a2(i,e){for(let t of e){let r=`${i}${t}`;if(te.existsSync(r)&&te.statSync(r).isFile())return r}for(let t of e){let r=`${i}/index${t}`;if(te.existsSync(r))return r}return null}function*ph(i,e,t,r=Z.extname(i)){let n=a2(Z.resolve(e,i),i2.includes(r)?n2:s2);if(n===null||t.has(n))return;t.add(n),yield n,e=Z.dirname(n),r=Z.extname(n);let a=te.readFileSync(n,"utf-8");for(let s of[...a.matchAll(/import[\s\S]*?['"](.{3,}?)['"]/gi),...a.matchAll(/import[\s\S]*from[\s\S]*?['"](.{3,}?)['"]/gi),...a.matchAll(/require\(['"`](.+)['"`]\)/gi)])!s[1].startsWith(".")||(yield*ph(s[1],e,t,r))}function ko(i){return i===null?new Set:new Set(ph(i,Z.dirname(i),new Set))}var i2,n2,s2,dh=C(()=>{l();ze();mt();i2=[".js",".cjs",".mjs"],n2=["",".js",".cjs",".mjs",".ts",".cts",".mts",".jsx",".tsx"],s2=["",".ts",".cts",".mts",".tsx",".js",".cjs",".mjs",".jsx"]});function o2(i,e){if(So.has(i))return So.get(i);let t=ah(i,e);return So.set(i,t).get(i)}function l2(i){let e=ys(i);if(e!==null){let[r,n,a,s]=mh.get(e)||[],o=ko(e),u=!1,c=new Map;for(let d of o){let h=te.statSync(d).mtimeMs;c.set(d,h),(!s||!s.has(d)||h>s.get(d))&&(u=!0)}if(!u)return[r,e,n,a];for(let d of o)delete fu.cache[d];let f=vo(dr(fh(e))),p=ui(f);return mh.set(e,[f,p,o,c]),[f,e,p,o]}let t=dr(i?.config??i??{});return t=vo(t),[t,null,ui(t),[]]}function Co(i){return({tailwindDirectives:e,registerDependency:t})=>(r,n)=>{let[a,s,o,u]=l2(i),c=new Set(u);if(e.size>0){c.add(n.opts.from);for(let y of n.messages)y.type==="dependency"&&c.add(y.file)}let[f,,p]=Ud(r,n,a,s,o,c),d=On(f),h=o2(f,a);if(e.size>0){for(let w of h)for(let b of yo(w))t(b);let[y,x]=lh(f,h,d);for(let w of y)f.changedContent.push(w);for(let[w,b]of x.entries())p.set(w,b)}for(let y of u)t({type:"dependency",file:y});for(let[y,x]of p.entries())d.set(y,x);return f}}var hh,mh,So,gh=C(()=>{l();ze();hh=K(Xn());mu();gs();af();Xr();Wd();Xd();uh();ch();dh();mh=new hh.default({maxSize:100}),So=new WeakMap});function Ao(i){let e=new Set,t=new Set,r=new Set;if(i.walkAtRules(n=>{n.name==="apply"&&r.add(n),n.name==="import"&&(n.params==='"tailwindcss/base"'||n.params==="'tailwindcss/base'"?(n.name="tailwind",n.params="base"):n.params==='"tailwindcss/components"'||n.params==="'tailwindcss/components'"?(n.name="tailwind",n.params="components"):n.params==='"tailwindcss/utilities"'||n.params==="'tailwindcss/utilities'"?(n.name="tailwind",n.params="utilities"):(n.params==='"tailwindcss/screens"'||n.params==="'tailwindcss/screens'"||n.params==='"tailwindcss/variants"'||n.params==="'tailwindcss/variants'")&&(n.name="tailwind",n.params="variants")),n.name==="tailwind"&&(n.params==="screens"&&(n.params="variants"),e.add(n.params)),["layer","responsive","variants"].includes(n.name)&&(["responsive","variants"].includes(n.name)&&F.warn(`${n.name}-at-rule-deprecated`,[`The \`@${n.name}\` directive has been deprecated in Tailwind CSS v3.0.`,"Use `@layer utilities` or `@layer components` instead.","https://tailwindcss.com/docs/upgrade-guide#replace-variants-with-layer"]),t.add(n))}),!e.has("base")||!e.has("components")||!e.has("utilities")){for(let n of t)if(n.name==="layer"&&["base","components","utilities"].includes(n.params)){if(!e.has(n.params))throw n.error(`\`@layer ${n.params}\` is used but no matching \`@tailwind ${n.params}\` directive is present.`)}else if(n.name==="responsive"){if(!e.has("utilities"))throw n.error("`@responsive` is used but `@tailwind utilities` is missing.")}else if(n.name==="variants"&&!e.has("utilities"))throw n.error("`@variants` is used but `@tailwind utilities` is missing.")}return{tailwindDirectives:e,applyDirectives:r}}var yh=C(()=>{l();Ee()});function bt(i,e=void 0,t=void 0){return i.map(r=>{let n=r.clone(),a=r.raws.tailwind?.preserveSource!==!0||!n.source;return e!==void 0&&a&&(n.source=e,"walk"in n&&n.walk(s=>{s.source=e})),t!==void 0&&(n.raws.tailwind={...n.raws.tailwind,...t}),n})}var wh=C(()=>{l()});function Pn(i){return i=Array.isArray(i)?i:[i],i=i.map(e=>e instanceof RegExp?e.source:e),i.join("")}function xe(i){return new RegExp(Pn(i),"g")}function $t(i){return`(?:${i.map(Pn).join("|")})`}function _o(i){return`(?:${Pn(i)})?`}function vh(i){return`(?:${Pn(i)})*`}function xh(i){return i&&u2.test(i)?i.replace(bh,"\\$&"):i||""}var bh,u2,kh=C(()=>{l();bh=/[\\^$.*+?()[\]{}|]/g,u2=RegExp(bh.source)});function Sh(i){let e=Array.from(f2(i));return t=>{let r=[];for(let n of e)r=[...r,...t.match(n)??[]];return r.filter(n=>n!==void 0).map(d2)}}function*f2(i){let e=i.tailwindConfig.separator,t=J(i.tailwindConfig,"variantGrouping"),r=i.tailwindConfig.prefix!==""?_o(xe([/-?/,xh(i.tailwindConfig.prefix)])):"",n=$t([/\[[^\s:'"`]+:[^\s\[\]]+\]/,/\[[^\s:'"`]+:[^\s]+?\[[^\s]+\][^\s]+?\]/,xe([/-?(?:\w+)/,_o($t([xe([/-(?:\w+-)*\[[^\s:]+\]/,/(?![{([]])/,/(?:\/[^\s'"`\\><$]*)?/]),xe([/-(?:\w+-)*\[[^\s]+\]/,/(?![{([]])/,/(?:\/[^\s'"`\\$]*)?/]),/[-\/][^\s'"`\\$={><]*/]))])]),a=[$t([xe([/@\[[^\s"'`]+\](\/[^\s"'`]+)?/,e]),xe([/([^\s"'`\[\\]+-)?\[[^\s"'`]+\]/,e]),xe([/[^\s"'`\[\\]+/,e])]),$t([xe([/([^\s"'`\[\\]+-)?\[[^\s`]+\]/,e]),xe([/[^\s`\[\\]+/,e])])];for(let s of a)yield xe(["((?=((",s,")+))\\2)?",/!?/,r,t?$t([xe([/\(/,n,vh([/,/,n]),/\)/]),n]):n]);yield/[^<>"'`\s.(){}[\]#=%$]*[^<>"'`\s.(){}[\]#=%:$]/g}function d2(i){if(!i.includes("-["))return i;let e=0,t=[],r=i.matchAll(c2);r=Array.from(r).flatMap(n=>{let[,...a]=n;return a.map((s,o)=>Object.assign([],n,{index:n.index+o,0:s}))});for(let n of r){let a=n[0],s=t[t.length-1];if(a===s?t.pop():(a==="'"||a==='"'||a==="`")&&t.push(a),!s){if(a==="["){e++;continue}else if(a==="]"){e--;continue}if(e<0)return i.substring(0,n.index-1);if(e===0&&!p2.test(a))return i.substring(0,n.index)}}return i}var c2,p2,Ch=C(()=>{l();De();kh();c2=/([\[\]'"`])([^\[\]'"`])?/g,p2=/[^"'`\s<>\]]+/});function h2(i,e){let t=i.tailwindConfig.content.extract;return t[e]||t.DEFAULT||_h[e]||_h.DEFAULT(i)}function m2(i,e){let t=i.content.transform;return t[e]||t.DEFAULT||Eh[e]||Eh.DEFAULT}function g2(i,e,t,r){ti.has(e)||ti.set(e,new Ah.default({maxSize:25e3}));for(let n of i.split(` +`))if(n=n.trim(),!r.has(n))if(r.add(n),ti.get(e).has(n))for(let a of ti.get(e).get(n))t.add(a);else{let a=e(n).filter(o=>o!=="!*"),s=new Set(a);for(let o of s)t.add(o);ti.get(e).set(n,s)}}function y2(i,e){let t=e.offsets.sort(i),r={base:new Set,defaults:new Set,components:new Set,utilities:new Set,variants:new Set};for(let[n,a]of t)r[n.layer].add(a);return r}function Eo(i){return async e=>{let t={base:null,components:null,utilities:null,variants:null};if(e.walkAtRules(y=>{y.name==="tailwind"&&Object.keys(t).includes(y.params)&&(t[y.params]=y)}),Object.values(t).every(y=>y===null))return e;let r=new Set([...i.candidates??[],He]),n=new Set;Ye.DEBUG&&console.time("Reading changed files"),await Promise.all(i.changedContent.map(async({file:y,content:x,extension:w})=>{let b=m2(i.tailwindConfig,w),k=h2(i,w);x=y?await te.promises.readFile(y,"utf8"):x,g2(b(x),k,r,n)})),Ye.DEBUG&&console.timeEnd("Reading changed files");let a=i.classCache.size;Ye.DEBUG&&console.time("Generate rules"),Ye.DEBUG&&console.time("Sorting candidates");let s=new Set([...r].sort((y,x)=>y===x?0:y{let x=y.raws.tailwind?.parentLayer;return x==="components"?t.components!==null:x==="utilities"?t.utilities!==null:!0});t.variants?(t.variants.before(bt(d,t.variants.source,{layer:"variants"})),t.variants.remove()):d.length>0&&e.append(bt(d,e.source,{layer:"variants"}));let h=d.some(y=>y.raws.tailwind?.parentLayer==="utilities");t.utilities&&f.size===0&&!h&&F.warn("content-problems",["No utility classes were detected in your source files. If this is unexpected, double-check the `content` option in your Tailwind CSS configuration.","https://tailwindcss.com/docs/content-configuration"]),Ye.DEBUG&&(console.log("Potential classes: ",r.size),console.log("Active contexts: ",gn.size)),i.changedContent=[],e.walkAtRules("layer",y=>{Object.keys(t).includes(y.params)&&y.remove()})}}var Ah,Ye,_h,Eh,ti,Oh=C(()=>{l();ze();Ah=K(Xn());ot();Cn();Ee();wh();Ch();Ye=Pe,_h={DEFAULT:Sh},Eh={DEFAULT:i=>i,svelte:i=>i.replace(/(?:^|\s)class:/g," ")};ti=new WeakMap});function In(i){let e=new Map;z.root({nodes:[i.clone()]}).walkRules(a=>{(0,Dn.default)(s=>{s.walkClasses(o=>{let u=o.parent.toString(),c=e.get(u);c||e.set(u,c=new Set),c.add(o.value)})}).processSync(a.selector)});let r=Array.from(e.values(),a=>Array.from(a)),n=r.flat();return Object.assign(n,{groups:r})}function Oo(i){return w2.astSync(i)}function Th(i,e){let t=new Set;for(let r of i)t.add(r.split(e).pop());return Array.from(t)}function Ph(i,e){let t=i.tailwindConfig.prefix;return typeof t=="function"?t(e):t+e}function*Dh(i){for(yield i;i.parent;)yield i.parent,i=i.parent}function b2(i,e={}){let t=i.nodes;i.nodes=[];let r=i.clone(e);return i.nodes=t,r}function v2(i){for(let e of Dh(i))if(i!==e){if(e.type==="root")break;i=b2(e,{nodes:[i]})}return i}function x2(i,e){let t=new Map;return i.walkRules(r=>{for(let s of Dh(r))if(s.raws.tailwind?.layer!==void 0)return;let n=v2(r),a=e.offsets.create("user");for(let s of In(r)){let o=t.get(s)||[];t.set(s,o),o.push([{layer:"user",sort:a,important:!1},n])}}),t}function k2(i,e){for(let t of i){if(e.notClassCache.has(t)||e.applyClassCache.has(t))continue;if(e.classCache.has(t)){e.applyClassCache.set(t,e.classCache.get(t).map(([n,a])=>[n,a.clone()]));continue}let r=Array.from(xn(t,e));if(r.length===0){e.notClassCache.add(t);continue}e.applyClassCache.set(t,r)}return e.applyClassCache}function S2(i){let e=null;return{get:t=>(e=e||i(),e.get(t)),has:t=>(e=e||i(),e.has(t))}}function C2(i){return{get:e=>i.flatMap(t=>t.get(e)||[]),has:e=>i.some(t=>t.has(e))}}function Ih(i){let e=i.split(/[\s\t\n]+/g);return e[e.length-1]==="!important"?[e.slice(0,-1),!0]:[e,!1]}function qh(i,e,t){let r=new Set,n=[];if(i.walkAtRules("apply",u=>{let[c]=Ih(u.params);for(let f of c)r.add(f);n.push(u)}),n.length===0)return;let a=C2([t,k2(r,e)]);function s(u,c,f){let p=Oo(u),d=Oo(c),y=Oo(`.${ce(f)}`).nodes[0].nodes[0];return p.each(x=>{let w=new Set;d.each(b=>{let k=!1;b=b.clone(),b.walkClasses(S=>{S.value===y.value&&(k||(S.replaceWith(...x.nodes.map(_=>_.clone())),w.add(b),k=!0))})});for(let b of w){let k=[[]];for(let S of b.nodes)S.type==="combinator"?(k.push(S),k.push([])):k[k.length-1].push(S);b.nodes=[];for(let S of k)Array.isArray(S)&&S.sort((_,E)=>_.type==="tag"&&E.type==="class"?-1:_.type==="class"&&E.type==="tag"?1:_.type==="class"&&E.type==="pseudo"&&E.value.startsWith("::")?-1:_.type==="pseudo"&&_.value.startsWith("::")&&E.type==="class"?1:0),b.nodes=b.nodes.concat(S)}x.replaceWith(...w)}),p.toString()}let o=new Map;for(let u of n){let[c]=o.get(u.parent)||[[],u.source];o.set(u.parent,[c,u.source]);let[f,p]=Ih(u.params);if(u.parent.type==="atrule"){if(u.parent.name==="screen"){let d=u.parent.params;throw u.error(`@apply is not supported within nested at-rules like @screen. We suggest you write this as @apply ${f.map(h=>`${d}:${h}`).join(" ")} instead.`)}throw u.error(`@apply is not supported within nested at-rules like @${u.parent.name}. You can fix this by un-nesting @${u.parent.name}.`)}for(let d of f){if([Ph(e,"group"),Ph(e,"peer")].includes(d))throw u.error(`@apply should not be used with the '${d}' utility`);if(!a.has(d))throw u.error(`The \`${d}\` class does not exist. If \`${d}\` is a custom class, make sure it is defined within a \`@layer\` directive.`);let h=a.get(d);c.push([d,p,h])}}for(let[u,[c,f]]of o){let p=[];for(let[h,y,x]of c){let w=[h,...Th([h],e.tailwindConfig.separator)];for(let[b,k]of x){let S=In(u),_=In(k);if(_=_.groups.filter(q=>q.some(X=>w.includes(X))).flat(),_=_.concat(Th(_,e.tailwindConfig.separator)),S.some(q=>_.includes(q)))throw k.error(`You cannot \`@apply\` the \`${h}\` utility here because it creates a circular dependency.`);let I=z.root({nodes:[k.clone()]});I.walk(q=>{q.source=f}),(k.type!=="atrule"||k.type==="atrule"&&k.name!=="keyframes")&&I.walkRules(q=>{if(!In(q).some($=>$===h)){q.remove();return}let X=typeof e.tailwindConfig.important=="string"?e.tailwindConfig.important:null,ge=u.raws.tailwind!==void 0&&X&&u.selector.indexOf(X)===0?u.selector.slice(X.length):u.selector;q.selector=s(ge,q.selector,h),X&&ge!==u.selector&&(q.selector=bn(q.selector,X)),q.walkDecls($=>{$.important=b.important||y});let je=(0,Dn.default)().astSync(q.selector);je.each($=>Ft($)),q.selector=je.toString()}),!!I.nodes[0]&&p.push([b.sort,I.nodes[0]])}}let d=e.offsets.sort(p).map(h=>h[1]);u.after(d)}for(let u of n)u.parent.nodes.length>1?u.remove():u.parent.remove();qh(i,e,t)}function To(i){return e=>{let t=S2(()=>x2(e,i));qh(e,i,t)}}var Dn,w2,Rh=C(()=>{l();nt();Dn=K(Me());Cn();Mt();so();yn();w2=(0,Dn.default)()});var Mh=v((PI,qn)=>{l();(function(){"use strict";function i(r,n,a){if(!r)return null;i.caseSensitive||(r=r.toLowerCase());var s=i.threshold===null?null:i.threshold*r.length,o=i.thresholdAbsolute,u;s!==null&&o!==null?u=Math.min(s,o):s!==null?u=s:o!==null?u=o:u=null;var c,f,p,d,h,y=n.length;for(h=0;ha)return a+1;var u=[],c,f,p,d,h;for(c=0;c<=o;c++)u[c]=[c];for(f=0;f<=s;f++)u[0][f]=f;for(c=1;c<=o;c++){for(p=e,d=1,c>a&&(d=c-a),h=o+1,h>a+c&&(h=a+c),f=1;f<=s;f++)fh?u[c][f]=a+1:n.charAt(c-1)===r.charAt(f-1)?u[c][f]=u[c-1][f-1]:u[c][f]=Math.min(u[c-1][f-1]+1,Math.min(u[c][f-1]+1,u[c-1][f]+1)),u[c][f]a)return a+1}return u[o][s]}})()});var Fh=v((DI,Bh)=>{l();var Po="(".charCodeAt(0),Do=")".charCodeAt(0),Rn="'".charCodeAt(0),Io='"'.charCodeAt(0),qo="\\".charCodeAt(0),jt="/".charCodeAt(0),Ro=",".charCodeAt(0),Mo=":".charCodeAt(0),Mn="*".charCodeAt(0),A2="u".charCodeAt(0),_2="U".charCodeAt(0),E2="+".charCodeAt(0),O2=/^[a-f0-9?-]+$/i;Bh.exports=function(i){for(var e=[],t=i,r,n,a,s,o,u,c,f,p=0,d=t.charCodeAt(p),h=t.length,y=[{nodes:e}],x=0,w,b="",k="",S="";p{l();Nh.exports=function i(e,t,r){var n,a,s,o;for(n=0,a=e.length;n{l();function $h(i,e){var t=i.type,r=i.value,n,a;return e&&(a=e(i))!==void 0?a:t==="word"||t==="space"?r:t==="string"?(n=i.quote||"",n+r+(i.unclosed?"":n)):t==="comment"?"/*"+r+(i.unclosed?"":"*/"):t==="div"?(i.before||"")+r+(i.after||""):Array.isArray(i.nodes)?(n=jh(i.nodes,e),t!=="function"?n:r+"("+(i.before||"")+n+(i.after||"")+(i.unclosed?"":")")):r}function jh(i,e){var t,r;if(Array.isArray(i)){for(t="",r=i.length-1;~r;r-=1)t=$h(i[r],e)+t;return t}return $h(i,e)}zh.exports=jh});var Wh=v((RI,Uh)=>{l();var Bn="-".charCodeAt(0),Fn="+".charCodeAt(0),Bo=".".charCodeAt(0),T2="e".charCodeAt(0),P2="E".charCodeAt(0);function D2(i){var e=i.charCodeAt(0),t;if(e===Fn||e===Bn){if(t=i.charCodeAt(1),t>=48&&t<=57)return!0;var r=i.charCodeAt(2);return t===Bo&&r>=48&&r<=57}return e===Bo?(t=i.charCodeAt(1),t>=48&&t<=57):e>=48&&e<=57}Uh.exports=function(i){var e=0,t=i.length,r,n,a;if(t===0||!D2(i))return!1;for(r=i.charCodeAt(e),(r===Fn||r===Bn)&&e++;e57));)e+=1;if(r=i.charCodeAt(e),n=i.charCodeAt(e+1),r===Bo&&n>=48&&n<=57)for(e+=2;e57));)e+=1;if(r=i.charCodeAt(e),n=i.charCodeAt(e+1),a=i.charCodeAt(e+2),(r===T2||r===P2)&&(n>=48&&n<=57||(n===Fn||n===Bn)&&a>=48&&a<=57))for(e+=n===Fn||n===Bn?3:2;e57));)e+=1;return{number:i.slice(0,e),unit:i.slice(e)}}});var Qh=v((MI,Yh)=>{l();var I2=Fh(),Gh=Lh(),Hh=Vh();function ut(i){return this instanceof ut?(this.nodes=I2(i),this):new ut(i)}ut.prototype.toString=function(){return Array.isArray(this.nodes)?Hh(this.nodes):""};ut.prototype.walk=function(i,e){return Gh(this.nodes,i,e),this};ut.unit=Wh();ut.walk=Gh;ut.stringify=Hh;Yh.exports=ut});function No(i){return typeof i=="object"&&i!==null}function q2(i,e){let t=Ke(e);do if(t.pop(),(0,ri.default)(i,t)!==void 0)break;while(t.length);return t.length?t:void 0}function zt(i){return typeof i=="string"?i:i.reduce((e,t,r)=>t.includes(".")?`${e}[${t}]`:r===0?t:`${e}.${t}`,"")}function Xh(i){return i.map(e=>`'${e}'`).join(", ")}function Kh(i){return Xh(Object.keys(i))}function Lo(i,e,t,r={}){let n=Array.isArray(e)?zt(e):e.replace(/^['"]+|['"]+$/g,""),a=Array.isArray(e)?e:Ke(n),s=(0,ri.default)(i.theme,a,t);if(s===void 0){let u=`'${n}' does not exist in your theme config.`,c=a.slice(0,-1),f=(0,ri.default)(i.theme,c);if(No(f)){let p=Object.keys(f).filter(h=>Lo(i,[...c,h]).isValid),d=(0,Jh.default)(a[a.length-1],p);d?u+=` Did you mean '${zt([...c,d])}'?`:p.length>0&&(u+=` '${zt(c)}' has the following valid keys: ${Xh(p)}`)}else{let p=q2(i.theme,n);if(p){let d=(0,ri.default)(i.theme,p);No(d)?u+=` '${zt(p)}' has the following keys: ${Kh(d)}`:u+=` '${zt(p)}' is not an object.`}else u+=` Your theme has the following top-level keys: ${Kh(i.theme)}`}return{isValid:!1,error:u}}if(!(typeof s=="string"||typeof s=="number"||typeof s=="function"||s instanceof String||s instanceof Number||Array.isArray(s))){let u=`'${n}' was found but does not resolve to a string.`;if(No(s)){let c=Object.keys(s).filter(f=>Lo(i,[...a,f]).isValid);c.length&&(u+=` Did you mean something like '${zt([...a,c[0]])}'?`)}return{isValid:!1,error:u}}let[o]=a;return{isValid:!0,value:Ge(o)(s,r)}}function R2(i,e,t){e=e.map(n=>Zh(i,n,t));let r=[""];for(let n of e)n.type==="div"&&n.value===","?r.push(""):r[r.length-1]+=Fo.default.stringify(n);return r}function Zh(i,e,t){if(e.type==="function"&&t[e.value]!==void 0){let r=R2(i,e.nodes,t);e.type="word",e.value=t[e.value](i,...r)}return e}function M2(i,e,t){return Object.keys(t).some(n=>e.includes(`${n}(`))?(0,Fo.default)(e).walk(n=>{Zh(i,n,t)}).toString():e}function*F2(i){i=i.replace(/^['"]+|['"]+$/g,"");let e=i.match(/^([^\s]+)(?![^\[]*\])(?:\s*\/\s*([^\/\s]+))$/),t;yield[i,void 0],e&&(i=e[1],t=e[2],yield[i,t])}function N2(i,e,t){let r=Array.from(F2(e)).map(([n,a])=>Object.assign(Lo(i,n,t,{opacityValue:a}),{resolvedPath:n,alpha:a}));return r.find(n=>n.isValid)??r[0]}function em(i){let e=i.tailwindConfig,t={theme:(r,n,...a)=>{let{isValid:s,value:o,error:u,alpha:c}=N2(e,n,a.length?a:void 0);if(!s){let d=r.parent,h=d?.raws.tailwind?.candidate;if(d&&h!==void 0){i.markInvalidUtilityNode(d),d.remove(),F.warn("invalid-theme-key-in-class",[`The utility \`${h}\` contains an invalid theme value and was not generated.`]);return}throw r.error(u)}let f=kt(o),p=f!==void 0&&typeof f=="function";return(c!==void 0||p)&&(c===void 0&&(c=1),o=Ie(f,c,f)),o},screen:(r,n)=>{n=n.replace(/^['"]+/g,"").replace(/['"]+$/g,"");let s=at(e.theme.screens).find(({name:o})=>o===n);if(!s)throw r.error(`The '${n}' screen does not exist in your theme.`);return st(s)}};return r=>{r.walk(n=>{let a=B2[n.type];a!==void 0&&(n[a]=M2(n,n[a],t))})}}var ri,Jh,Fo,B2,tm=C(()=>{l();ri=K(js()),Jh=K(Mh());Hr();Fo=K(Qh());hn();cn();pi();ar();cr();Ee();B2={atrule:"params",decl:"value"}});function rm({tailwindConfig:{theme:i}}){return function(e){e.walkAtRules("screen",t=>{let r=t.params,a=at(i.screens).find(({name:s})=>s===r);if(!a)throw t.error(`No \`${r}\` screen found.`);t.name="media",t.params=st(a)})}}var im=C(()=>{l();hn();cn()});function L2(i){let e=i.filter(o=>o.type!=="pseudo"||o.nodes.length>0?!0:o.value.startsWith("::")||[":before",":after",":first-line",":first-letter"].includes(o.value)).reverse(),t=new Set(["tag","class","id","attribute"]),r=e.findIndex(o=>t.has(o.type));if(r===-1)return e.reverse().join("").trim();let n=e[r],a=nm[n.type]?nm[n.type](n):n;e=e.slice(0,r);let s=e.findIndex(o=>o.type==="combinator"&&o.value===">");return s!==-1&&(e.splice(0,s),e.unshift(Nn.default.universal())),[a,...e.reverse()].join("").trim()}function j2(i){return $o.has(i)||$o.set(i,$2.transformSync(i)),$o.get(i)}function jo({tailwindConfig:i}){return e=>{let t=new Map,r=new Set;if(e.walkAtRules("defaults",n=>{if(n.nodes&&n.nodes.length>0){r.add(n);return}let a=n.params;t.has(a)||t.set(a,new Set),t.get(a).add(n.parent),n.remove()}),J(i,"optimizeUniversalDefaults"))for(let n of r){let a=new Map,s=t.get(n.params)??[];for(let o of s)for(let u of j2(o.selector)){let c=u.includes(":-")||u.includes("::-")?u:"__DEFAULT__",f=a.get(c)??new Set;a.set(c,f),f.add(u)}if(J(i,"optimizeUniversalDefaults")){if(a.size===0){n.remove();continue}for(let[,o]of a){let u=z.rule({source:n.source});u.selectors=[...o],u.append(n.nodes.map(c=>c.clone())),n.before(u)}}n.remove()}else if(r.size){let n=z.rule({selectors:["*","::before","::after"]});for(let s of r)n.append(s.nodes),n.parent||s.before(n),n.source||(n.source=s.source),s.remove();let a=n.clone({selectors:["::backdrop"]});n.after(a)}}}var Nn,nm,$2,$o,sm=C(()=>{l();nt();Nn=K(Me());De();nm={id(i){return Nn.default.attribute({attribute:"id",operator:"=",value:i.value,quoteMark:'"'})}};$2=(0,Nn.default)(i=>i.map(e=>{let t=e.split(r=>r.type==="combinator"&&r.value===" ").pop();return L2(t)})),$o=new Map});function zo(){function i(e){let t=null;e.each(r=>{if(!z2.has(r.type)){t=null;return}if(t===null){t=r;return}let n=am[r.type];r.type==="atrule"&&r.name==="font-face"?t=r:n.every(a=>(r[a]??"").replace(/\s+/g," ")===(t[a]??"").replace(/\s+/g," "))?(r.nodes&&t.append(r.nodes),r.remove()):t=r}),e.each(r=>{r.type==="atrule"&&i(r)})}return e=>{i(e)}}var am,z2,om=C(()=>{l();am={atrule:["name","params"],rule:["selector"]},z2=new Set(Object.keys(am))});function Vo(){return i=>{i.walkRules(e=>{let t=new Map,r=new Set([]),n=new Map;e.walkDecls(a=>{if(a.parent===e){if(t.has(a.prop)){if(t.get(a.prop).value===a.value){r.add(t.get(a.prop)),t.set(a.prop,a);return}n.has(a.prop)||n.set(a.prop,new Set),n.get(a.prop).add(t.get(a.prop)),n.get(a.prop).add(a)}t.set(a.prop,a)}});for(let a of r)a.remove();for(let a of n.values()){let s=new Map;for(let o of a){let u=U2(o.value);u!==null&&(s.has(u)||s.set(u,new Set),s.get(u).add(o))}for(let o of s.values()){let u=Array.from(o).slice(0,-1);for(let c of u)c.remove()}}})}}function U2(i){let e=/^-?\d*.?\d+([\w%]+)?$/g.exec(i);return e?e[1]??V2:null}var V2,lm=C(()=>{l();V2=Symbol("unitless-number")});function W2(i){if(!i.walkAtRules)return;let e=new Set;if(i.walkAtRules("apply",t=>{e.add(t.parent)}),e.size!==0)for(let t of e){let r=[],n=[];for(let a of t.nodes)a.type==="atrule"&&a.name==="apply"?(n.length>0&&(r.push(n),n=[]),r.push([a])):n.push(a);if(n.length>0&&r.push(n),r.length!==1){for(let a of[...r].reverse()){let s=t.clone({nodes:[]});s.append(a),t.after(s)}t.remove()}}}function Ln(){return i=>{W2(i)}}var um=C(()=>{l()});function G2(i){return i.type==="root"}function H2(i){return i.type==="atrule"&&i.name==="layer"}function fm(i){return(e,t)=>{let r=!1;e.walkAtRules("tailwind",n=>{if(r)return!1;if(n.parent&&!(G2(n.parent)||H2(n.parent)))return r=!0,n.warn(t,["Nested @tailwind rules were detected, but are not supported.","Consider using a prefix to scope Tailwind's classes: https://tailwindcss.com/docs/configuration#prefix","Alternatively, use the important selector strategy: https://tailwindcss.com/docs/configuration#selector-strategy"].join(` +`)),!1}),e.walkRules(n=>{if(r)return!1;n.walkRules(a=>(r=!0,a.warn(t,["Nested CSS was detected, but CSS nesting has not been configured correctly.","Please enable a CSS nesting plugin *before* Tailwind in your configuration.","See how here: https://tailwindcss.com/docs/using-with-preprocessors#nesting"].join(` +`)),!1))})}}var cm=C(()=>{l()});function $n(i){return async function(e,t){let{tailwindDirectives:r,applyDirectives:n}=Ao(e);fm()(e,t),Ln()(e,t);let a=i({tailwindDirectives:r,applyDirectives:n,registerDependency(s){t.messages.push({plugin:"tailwindcss",parent:t.opts.from,...s})},createContext(s,o){return go(s,o,e)}})(e,t);if(a.tailwindConfig.separator==="-")throw new Error("The '-' character cannot be used as a custom separator in JIT mode due to parsing ambiguity. Please use another character like '_' instead.");_u(a.tailwindConfig),await Eo(a)(e,t),Ln()(e,t),To(a)(e,t),em(a)(e,t),rm(a)(e,t),jo(a)(e,t),zo(a)(e,t),Vo(a)(e,t)}}var pm=C(()=>{l();yh();Oh();Rh();tm();im();sm();om();lm();um();cm();Xr();De()});function dm(i,e){let t=null,r=null;return i.walkAtRules("config",n=>{if(r=n.source?.input.file??e.opts.from??null,r===null)throw n.error("The `@config` directive cannot be used without setting `from` in your PostCSS config.");if(t)throw n.error("Only one `@config` directive is allowed per file.");let a=n.params.match(/(['"])(.*?)\1/);if(!a)throw n.error("A path is required when using the `@config` directive.");let s=a[2];if(Z.isAbsolute(s))throw n.error("The `@config` directive cannot be used with an absolute path.");if(t=Z.resolve(Z.dirname(r),s),!te.existsSync(t))throw n.error(`The config file at "${s}" does not exist. Make sure the path is correct and the file exists.`);n.remove()}),t||null}var hm=C(()=>{l();ze();mt()});var mm=v((v4,Uo)=>{l();gh();pm();ot();hm();Uo.exports=function(e){return{postcssPlugin:"tailwindcss",plugins:[Pe.DEBUG&&function(t){return console.log(` +`),console.time("JIT TOTAL"),t},async function(t,r){e=dm(t,r)??e;let n=Co(e);if(t.type==="document"){let a=t.nodes.filter(s=>s.type==="root");for(let s of a)s.type==="root"&&await $n(n)(s,r);return}await $n(n)(t,r)},!1,Pe.DEBUG&&function(t){return console.timeEnd("JIT TOTAL"),console.log(` +`),t}].filter(Boolean)}};Uo.exports.postcss=!0});var ym=v((x4,gm)=>{l();gm.exports=mm()});var Wo=v((k4,wm)=>{l();wm.exports=()=>["and_chr 114","and_uc 15.5","chrome 114","chrome 113","chrome 109","edge 114","firefox 114","ios_saf 16.5","ios_saf 16.4","ios_saf 16.3","ios_saf 16.1","opera 99","safari 16.5","samsung 21"]});var jn={};Ae(jn,{agents:()=>Y2,feature:()=>Q2});function Q2(){return{status:"cr",title:"CSS Feature Queries",stats:{ie:{"6":"n","7":"n","8":"n","9":"n","10":"n","11":"n","5.5":"n"},edge:{"12":"y","13":"y","14":"y","15":"y","16":"y","17":"y","18":"y","79":"y","80":"y","81":"y","83":"y","84":"y","85":"y","86":"y","87":"y","88":"y","89":"y","90":"y","91":"y","92":"y","93":"y","94":"y","95":"y","96":"y","97":"y","98":"y","99":"y","100":"y","101":"y","102":"y","103":"y","104":"y","105":"y","106":"y","107":"y","108":"y","109":"y","110":"y","111":"y","112":"y","113":"y","114":"y"},firefox:{"2":"n","3":"n","4":"n","5":"n","6":"n","7":"n","8":"n","9":"n","10":"n","11":"n","12":"n","13":"n","14":"n","15":"n","16":"n","17":"n","18":"n","19":"n","20":"n","21":"n","22":"y","23":"y","24":"y","25":"y","26":"y","27":"y","28":"y","29":"y","30":"y","31":"y","32":"y","33":"y","34":"y","35":"y","36":"y","37":"y","38":"y","39":"y","40":"y","41":"y","42":"y","43":"y","44":"y","45":"y","46":"y","47":"y","48":"y","49":"y","50":"y","51":"y","52":"y","53":"y","54":"y","55":"y","56":"y","57":"y","58":"y","59":"y","60":"y","61":"y","62":"y","63":"y","64":"y","65":"y","66":"y","67":"y","68":"y","69":"y","70":"y","71":"y","72":"y","73":"y","74":"y","75":"y","76":"y","77":"y","78":"y","79":"y","80":"y","81":"y","82":"y","83":"y","84":"y","85":"y","86":"y","87":"y","88":"y","89":"y","90":"y","91":"y","92":"y","93":"y","94":"y","95":"y","96":"y","97":"y","98":"y","99":"y","100":"y","101":"y","102":"y","103":"y","104":"y","105":"y","106":"y","107":"y","108":"y","109":"y","110":"y","111":"y","112":"y","113":"y","114":"y","115":"y","116":"y","117":"y","3.5":"n","3.6":"n"},chrome:{"4":"n","5":"n","6":"n","7":"n","8":"n","9":"n","10":"n","11":"n","12":"n","13":"n","14":"n","15":"n","16":"n","17":"n","18":"n","19":"n","20":"n","21":"n","22":"n","23":"n","24":"n","25":"n","26":"n","27":"n","28":"y","29":"y","30":"y","31":"y","32":"y","33":"y","34":"y","35":"y","36":"y","37":"y","38":"y","39":"y","40":"y","41":"y","42":"y","43":"y","44":"y","45":"y","46":"y","47":"y","48":"y","49":"y","50":"y","51":"y","52":"y","53":"y","54":"y","55":"y","56":"y","57":"y","58":"y","59":"y","60":"y","61":"y","62":"y","63":"y","64":"y","65":"y","66":"y","67":"y","68":"y","69":"y","70":"y","71":"y","72":"y","73":"y","74":"y","75":"y","76":"y","77":"y","78":"y","79":"y","80":"y","81":"y","83":"y","84":"y","85":"y","86":"y","87":"y","88":"y","89":"y","90":"y","91":"y","92":"y","93":"y","94":"y","95":"y","96":"y","97":"y","98":"y","99":"y","100":"y","101":"y","102":"y","103":"y","104":"y","105":"y","106":"y","107":"y","108":"y","109":"y","110":"y","111":"y","112":"y","113":"y","114":"y","115":"y","116":"y","117":"y"},safari:{"4":"n","5":"n","6":"n","7":"n","8":"n","9":"y","10":"y","11":"y","12":"y","13":"y","14":"y","15":"y","17":"y","9.1":"y","10.1":"y","11.1":"y","12.1":"y","13.1":"y","14.1":"y","15.1":"y","15.2-15.3":"y","15.4":"y","15.5":"y","15.6":"y","16.0":"y","16.1":"y","16.2":"y","16.3":"y","16.4":"y","16.5":"y","16.6":"y",TP:"y","3.1":"n","3.2":"n","5.1":"n","6.1":"n","7.1":"n"},opera:{"9":"n","11":"n","12":"n","15":"y","16":"y","17":"y","18":"y","19":"y","20":"y","21":"y","22":"y","23":"y","24":"y","25":"y","26":"y","27":"y","28":"y","29":"y","30":"y","31":"y","32":"y","33":"y","34":"y","35":"y","36":"y","37":"y","38":"y","39":"y","40":"y","41":"y","42":"y","43":"y","44":"y","45":"y","46":"y","47":"y","48":"y","49":"y","50":"y","51":"y","52":"y","53":"y","54":"y","55":"y","56":"y","57":"y","58":"y","60":"y","62":"y","63":"y","64":"y","65":"y","66":"y","67":"y","68":"y","69":"y","70":"y","71":"y","72":"y","73":"y","74":"y","75":"y","76":"y","77":"y","78":"y","79":"y","80":"y","81":"y","82":"y","83":"y","84":"y","85":"y","86":"y","87":"y","88":"y","89":"y","90":"y","91":"y","92":"y","93":"y","94":"y","95":"y","96":"y","97":"y","98":"y","99":"y","100":"y","12.1":"y","9.5-9.6":"n","10.0-10.1":"n","10.5":"n","10.6":"n","11.1":"n","11.5":"n","11.6":"n"},ios_saf:{"8":"n","17":"y","9.0-9.2":"y","9.3":"y","10.0-10.2":"y","10.3":"y","11.0-11.2":"y","11.3-11.4":"y","12.0-12.1":"y","12.2-12.5":"y","13.0-13.1":"y","13.2":"y","13.3":"y","13.4-13.7":"y","14.0-14.4":"y","14.5-14.8":"y","15.0-15.1":"y","15.2-15.3":"y","15.4":"y","15.5":"y","15.6":"y","16.0":"y","16.1":"y","16.2":"y","16.3":"y","16.4":"y","16.5":"y","16.6":"y","3.2":"n","4.0-4.1":"n","4.2-4.3":"n","5.0-5.1":"n","6.0-6.1":"n","7.0-7.1":"n","8.1-8.4":"n"},op_mini:{all:"y"},android:{"3":"n","4":"n","114":"y","4.4":"y","4.4.3-4.4.4":"y","2.1":"n","2.2":"n","2.3":"n","4.1":"n","4.2-4.3":"n"},bb:{"7":"n","10":"n"},op_mob:{"10":"n","11":"n","12":"n","73":"y","11.1":"n","11.5":"n","12.1":"n"},and_chr:{"114":"y"},and_ff:{"115":"y"},ie_mob:{"10":"n","11":"n"},and_uc:{"15.5":"y"},samsung:{"4":"y","20":"y","21":"y","5.0-5.4":"y","6.2-6.4":"y","7.2-7.4":"y","8.2":"y","9.2":"y","10.1":"y","11.1-11.2":"y","12.0":"y","13.0":"y","14.0":"y","15.0":"y","16.0":"y","17.0":"y","18.0":"y","19.0":"y"},and_qq:{"13.1":"y"},baidu:{"13.18":"y"},kaios:{"2.5":"y","3.0-3.1":"y"}}}}var Y2,zn=C(()=>{l();Y2={ie:{prefix:"ms"},edge:{prefix:"webkit",prefix_exceptions:{"12":"ms","13":"ms","14":"ms","15":"ms","16":"ms","17":"ms","18":"ms"}},firefox:{prefix:"moz"},chrome:{prefix:"webkit"},safari:{prefix:"webkit"},opera:{prefix:"webkit",prefix_exceptions:{"9":"o","11":"o","12":"o","9.5-9.6":"o","10.0-10.1":"o","10.5":"o","10.6":"o","11.1":"o","11.5":"o","11.6":"o","12.1":"o"}},ios_saf:{prefix:"webkit"},op_mini:{prefix:"o"},android:{prefix:"webkit"},bb:{prefix:"webkit"},op_mob:{prefix:"o",prefix_exceptions:{"73":"webkit"}},and_chr:{prefix:"webkit"},and_ff:{prefix:"moz"},ie_mob:{prefix:"ms"},and_uc:{prefix:"webkit",prefix_exceptions:{"15.5":"webkit"}},samsung:{prefix:"webkit"},and_qq:{prefix:"webkit"},baidu:{prefix:"webkit"},kaios:{prefix:"moz"}}});var bm=v(()=>{l()});var ue=v((A4,ft)=>{l();var{list:Go}=me();ft.exports.error=function(i){let e=new Error(i);throw e.autoprefixer=!0,e};ft.exports.uniq=function(i){return[...new Set(i)]};ft.exports.removeNote=function(i){return i.includes(" ")?i.split(" ")[0]:i};ft.exports.escapeRegexp=function(i){return i.replace(/[$()*+-.?[\\\]^{|}]/g,"\\$&")};ft.exports.regexp=function(i,e=!0){return e&&(i=this.escapeRegexp(i)),new RegExp(`(^|[\\s,(])(${i}($|[\\s(,]))`,"gi")};ft.exports.editList=function(i,e){let t=Go.comma(i),r=e(t,[]);if(t===r)return i;let n=i.match(/,\s*/);return n=n?n[0]:", ",r.join(n)};ft.exports.splitSelector=function(i){return Go.comma(i).map(e=>Go.space(e).map(t=>t.split(/(?=\.|#)/g)))}});var ct=v((_4,km)=>{l();var J2=Wo(),vm=(zn(),jn).agents,X2=ue(),xm=class{static prefixes(){if(this.prefixesCache)return this.prefixesCache;this.prefixesCache=[];for(let e in vm)this.prefixesCache.push(`-${vm[e].prefix}-`);return this.prefixesCache=X2.uniq(this.prefixesCache).sort((e,t)=>t.length-e.length),this.prefixesCache}static withPrefix(e){return this.prefixesRegexp||(this.prefixesRegexp=new RegExp(this.prefixes().join("|"))),this.prefixesRegexp.test(e)}constructor(e,t,r,n){this.data=e,this.options=r||{},this.browserslistOpts=n||{},this.selected=this.parse(t)}parse(e){let t={};for(let r in this.browserslistOpts)t[r]=this.browserslistOpts[r];return t.path=this.options.from,J2(e,t)}prefix(e){let[t,r]=e.split(" "),n=this.data[t],a=n.prefix_exceptions&&n.prefix_exceptions[r];return a||(a=n.prefix),`-${a}-`}isSelected(e){return this.selected.includes(e)}};km.exports=xm});var ii=v((E4,Sm)=>{l();Sm.exports={prefix(i){let e=i.match(/^(-\w+-)/);return e?e[0]:""},unprefixed(i){return i.replace(/^-\w+-/,"")}}});var Vt=v((O4,Am)=>{l();var K2=ct(),Cm=ii(),Z2=ue();function Ho(i,e){let t=new i.constructor;for(let r of Object.keys(i||{})){let n=i[r];r==="parent"&&typeof n=="object"?e&&(t[r]=e):r==="source"||r===null?t[r]=n:Array.isArray(n)?t[r]=n.map(a=>Ho(a,t)):r!=="_autoprefixerPrefix"&&r!=="_autoprefixerValues"&&r!=="proxyCache"&&(typeof n=="object"&&n!==null&&(n=Ho(n,t)),t[r]=n)}return t}var Vn=class{static hack(e){return this.hacks||(this.hacks={}),e.names.map(t=>(this.hacks[t]=e,this.hacks[t]))}static load(e,t,r){let n=this.hacks&&this.hacks[e];return n?new n(e,t,r):new this(e,t,r)}static clone(e,t){let r=Ho(e);for(let n in t)r[n]=t[n];return r}constructor(e,t,r){this.prefixes=t,this.name=e,this.all=r}parentPrefix(e){let t;return typeof e._autoprefixerPrefix!="undefined"?t=e._autoprefixerPrefix:e.type==="decl"&&e.prop[0]==="-"?t=Cm.prefix(e.prop):e.type==="root"?t=!1:e.type==="rule"&&e.selector.includes(":-")&&/:(-\w+-)/.test(e.selector)?t=e.selector.match(/:(-\w+-)/)[1]:e.type==="atrule"&&e.name[0]==="-"?t=Cm.prefix(e.name):t=this.parentPrefix(e.parent),K2.prefixes().includes(t)||(t=!1),e._autoprefixerPrefix=t,e._autoprefixerPrefix}process(e,t){if(!this.check(e))return;let r=this.parentPrefix(e),n=this.prefixes.filter(s=>!r||r===Z2.removeNote(s)),a=[];for(let s of n)this.add(e,s,a.concat([s]),t)&&a.push(s);return a}clone(e,t){return Vn.clone(e,t)}};Am.exports=Vn});var R=v((T4,Om)=>{l();var eA=Vt(),tA=ct(),_m=ue(),Em=class extends eA{check(){return!0}prefixed(e,t){return t+e}normalize(e){return e}otherPrefixes(e,t){for(let r of tA.prefixes())if(r!==t&&e.includes(r))return!0;return!1}set(e,t){return e.prop=this.prefixed(e.prop,t),e}needCascade(e){return e._autoprefixerCascade||(e._autoprefixerCascade=this.all.options.cascade!==!1&&e.raw("before").includes(` +`)),e._autoprefixerCascade}maxPrefixed(e,t){if(t._autoprefixerMax)return t._autoprefixerMax;let r=0;for(let n of e)n=_m.removeNote(n),n.length>r&&(r=n.length);return t._autoprefixerMax=r,t._autoprefixerMax}calcBefore(e,t,r=""){let a=this.maxPrefixed(e,t)-_m.removeNote(r).length,s=t.raw("before");return a>0&&(s+=Array(a).fill(" ").join("")),s}restoreBefore(e){let t=e.raw("before").split(` +`),r=t[t.length-1];this.all.group(e).up(n=>{let a=n.raw("before").split(` +`),s=a[a.length-1];s.lengths.prop===n.prop&&s.value===n.value)))return this.needCascade(e)&&(n.raws.before=this.calcBefore(r,e,t)),e.parent.insertBefore(e,n)}isAlready(e,t){let r=this.all.group(e).up(n=>n.prop===t);return r||(r=this.all.group(e).down(n=>n.prop===t)),r}add(e,t,r,n){let a=this.prefixed(e.prop,t);if(!(this.isAlready(e,a)||this.otherPrefixes(e.value,t)))return this.insert(e,t,r,n)}process(e,t){if(!this.needCascade(e)){super.process(e,t);return}let r=super.process(e,t);!r||!r.length||(this.restoreBefore(e),e.raws.before=this.calcBefore(r,e))}old(e,t){return[this.prefixed(e,t)]}};Om.exports=Em});var Pm=v((P4,Tm)=>{l();Tm.exports=function i(e){return{mul:t=>new i(e*t),div:t=>new i(e/t),simplify:()=>new i(e),toString:()=>e.toString()}}});var qm=v((D4,Im)=>{l();var rA=Pm(),iA=Vt(),Yo=ue(),nA=/(min|max)-resolution\s*:\s*\d*\.?\d+(dppx|dpcm|dpi|x)/gi,sA=/(min|max)-resolution(\s*:\s*)(\d*\.?\d+)(dppx|dpcm|dpi|x)/i,Dm=class extends iA{prefixName(e,t){return e==="-moz-"?t+"--moz-device-pixel-ratio":e+t+"-device-pixel-ratio"}prefixQuery(e,t,r,n,a){return n=new rA(n),a==="dpi"?n=n.div(96):a==="dpcm"&&(n=n.mul(2.54).div(96)),n=n.simplify(),e==="-o-"&&(n=n.n+"/"+n.d),this.prefixName(e,t)+r+n}clean(e){if(!this.bad){this.bad=[];for(let t of this.prefixes)this.bad.push(this.prefixName(t,"min")),this.bad.push(this.prefixName(t,"max"))}e.params=Yo.editList(e.params,t=>t.filter(r=>this.bad.every(n=>!r.includes(n))))}process(e){let t=this.parentPrefix(e),r=t?[t]:this.prefixes;e.params=Yo.editList(e.params,(n,a)=>{for(let s of n){if(!s.includes("min-resolution")&&!s.includes("max-resolution")){a.push(s);continue}for(let o of r){let u=s.replace(nA,c=>{let f=c.match(sA);return this.prefixQuery(o,f[1],f[2],f[3],f[4])});a.push(u)}a.push(s)}return Yo.uniq(a)})}};Im.exports=Dm});var Mm=v((I4,Rm)=>{l();var Qo="(".charCodeAt(0),Jo=")".charCodeAt(0),Un="'".charCodeAt(0),Xo='"'.charCodeAt(0),Ko="\\".charCodeAt(0),Ut="/".charCodeAt(0),Zo=",".charCodeAt(0),el=":".charCodeAt(0),Wn="*".charCodeAt(0),aA="u".charCodeAt(0),oA="U".charCodeAt(0),lA="+".charCodeAt(0),uA=/^[a-f0-9?-]+$/i;Rm.exports=function(i){for(var e=[],t=i,r,n,a,s,o,u,c,f,p=0,d=t.charCodeAt(p),h=t.length,y=[{nodes:e}],x=0,w,b="",k="",S="";p{l();Bm.exports=function i(e,t,r){var n,a,s,o;for(n=0,a=e.length;n{l();function Nm(i,e){var t=i.type,r=i.value,n,a;return e&&(a=e(i))!==void 0?a:t==="word"||t==="space"?r:t==="string"?(n=i.quote||"",n+r+(i.unclosed?"":n)):t==="comment"?"/*"+r+(i.unclosed?"":"*/"):t==="div"?(i.before||"")+r+(i.after||""):Array.isArray(i.nodes)?(n=Lm(i.nodes,e),t!=="function"?n:r+"("+(i.before||"")+n+(i.after||"")+(i.unclosed?"":")")):r}function Lm(i,e){var t,r;if(Array.isArray(i)){for(t="",r=i.length-1;~r;r-=1)t=Nm(i[r],e)+t;return t}return Nm(i,e)}$m.exports=Lm});var Vm=v((M4,zm)=>{l();var Gn="-".charCodeAt(0),Hn="+".charCodeAt(0),tl=".".charCodeAt(0),fA="e".charCodeAt(0),cA="E".charCodeAt(0);function pA(i){var e=i.charCodeAt(0),t;if(e===Hn||e===Gn){if(t=i.charCodeAt(1),t>=48&&t<=57)return!0;var r=i.charCodeAt(2);return t===tl&&r>=48&&r<=57}return e===tl?(t=i.charCodeAt(1),t>=48&&t<=57):e>=48&&e<=57}zm.exports=function(i){var e=0,t=i.length,r,n,a;if(t===0||!pA(i))return!1;for(r=i.charCodeAt(e),(r===Hn||r===Gn)&&e++;e57));)e+=1;if(r=i.charCodeAt(e),n=i.charCodeAt(e+1),r===tl&&n>=48&&n<=57)for(e+=2;e57));)e+=1;if(r=i.charCodeAt(e),n=i.charCodeAt(e+1),a=i.charCodeAt(e+2),(r===fA||r===cA)&&(n>=48&&n<=57||(n===Hn||n===Gn)&&a>=48&&a<=57))for(e+=n===Hn||n===Gn?3:2;e57));)e+=1;return{number:i.slice(0,e),unit:i.slice(e)}}});var Yn=v((B4,Gm)=>{l();var dA=Mm(),Um=Fm(),Wm=jm();function pt(i){return this instanceof pt?(this.nodes=dA(i),this):new pt(i)}pt.prototype.toString=function(){return Array.isArray(this.nodes)?Wm(this.nodes):""};pt.prototype.walk=function(i,e){return Um(this.nodes,i,e),this};pt.unit=Vm();pt.walk=Um;pt.stringify=Wm;Gm.exports=pt});var Xm=v((F4,Jm)=>{l();var{list:hA}=me(),Hm=Yn(),mA=ct(),Ym=ii(),Qm=class{constructor(e){this.props=["transition","transition-property"],this.prefixes=e}add(e,t){let r,n,a=this.prefixes.add[e.prop],s=this.ruleVendorPrefixes(e),o=s||a&&a.prefixes||[],u=this.parse(e.value),c=u.map(h=>this.findProp(h)),f=[];if(c.some(h=>h[0]==="-"))return;for(let h of u){if(n=this.findProp(h),n[0]==="-")continue;let y=this.prefixes.add[n];if(!(!y||!y.prefixes))for(r of y.prefixes){if(s&&!s.some(w=>r.includes(w)))continue;let x=this.prefixes.prefixed(n,r);x!=="-ms-transform"&&!c.includes(x)&&(this.disabled(n,r)||f.push(this.clone(n,x,h)))}}u=u.concat(f);let p=this.stringify(u),d=this.stringify(this.cleanFromUnprefixed(u,"-webkit-"));if(o.includes("-webkit-")&&this.cloneBefore(e,`-webkit-${e.prop}`,d),this.cloneBefore(e,e.prop,d),o.includes("-o-")){let h=this.stringify(this.cleanFromUnprefixed(u,"-o-"));this.cloneBefore(e,`-o-${e.prop}`,h)}for(r of o)if(r!=="-webkit-"&&r!=="-o-"){let h=this.stringify(this.cleanOtherPrefixes(u,r));this.cloneBefore(e,r+e.prop,h)}p!==e.value&&!this.already(e,e.prop,p)&&(this.checkForWarning(t,e),e.cloneBefore(),e.value=p)}findProp(e){let t=e[0].value;if(/^\d/.test(t)){for(let[r,n]of e.entries())if(r!==0&&n.type==="word")return n.value}return t}already(e,t,r){return e.parent.some(n=>n.prop===t&&n.value===r)}cloneBefore(e,t,r){this.already(e,t,r)||e.cloneBefore({prop:t,value:r})}checkForWarning(e,t){if(t.prop!=="transition-property")return;let r=!1,n=!1;t.parent.each(a=>{if(a.type!=="decl"||a.prop.indexOf("transition-")!==0)return;let s=hA.comma(a.value);if(a.prop==="transition-property"){s.forEach(o=>{let u=this.prefixes.add[o];u&&u.prefixes&&u.prefixes.length>0&&(r=!0)});return}return n=n||s.length>1,!1}),r&&n&&t.warn(e,"Replace transition-property to transition, because Autoprefixer could not support any cases of transition-property and other transition-*")}remove(e){let t=this.parse(e.value);t=t.filter(s=>{let o=this.prefixes.remove[this.findProp(s)];return!o||!o.remove});let r=this.stringify(t);if(e.value===r)return;if(t.length===0){e.remove();return}let n=e.parent.some(s=>s.prop===e.prop&&s.value===r),a=e.parent.some(s=>s!==e&&s.prop===e.prop&&s.value.length>r.length);if(n||a){e.remove();return}e.value=r}parse(e){let t=Hm(e),r=[],n=[];for(let a of t.nodes)n.push(a),a.type==="div"&&a.value===","&&(r.push(n),n=[]);return r.push(n),r.filter(a=>a.length>0)}stringify(e){if(e.length===0)return"";let t=[];for(let r of e)r[r.length-1].type!=="div"&&r.push(this.div(e)),t=t.concat(r);return t[0].type==="div"&&(t=t.slice(1)),t[t.length-1].type==="div"&&(t=t.slice(0,-2+1||void 0)),Hm.stringify({nodes:t})}clone(e,t,r){let n=[],a=!1;for(let s of r)!a&&s.type==="word"&&s.value===e?(n.push({type:"word",value:t}),a=!0):n.push(s);return n}div(e){for(let t of e)for(let r of t)if(r.type==="div"&&r.value===",")return r;return{type:"div",value:",",after:" "}}cleanOtherPrefixes(e,t){return e.filter(r=>{let n=Ym.prefix(this.findProp(r));return n===""||n===t})}cleanFromUnprefixed(e,t){let r=e.map(a=>this.findProp(a)).filter(a=>a.slice(0,t.length)===t).map(a=>this.prefixes.unprefixed(a)),n=[];for(let a of e){let s=this.findProp(a),o=Ym.prefix(s);!r.includes(s)&&(o===t||o==="")&&n.push(a)}return n}disabled(e,t){let r=["order","justify-content","align-self","align-content"];if(e.includes("flex")||r.includes(e)){if(this.prefixes.options.flexbox===!1)return!0;if(this.prefixes.options.flexbox==="no-2009")return t.includes("2009")}}ruleVendorPrefixes(e){let{parent:t}=e;if(t.type!=="rule")return!1;if(!t.selector.includes(":-"))return!1;let r=mA.prefixes().filter(n=>t.selector.includes(":"+n));return r.length>0?r:!1}};Jm.exports=Qm});var Wt=v((N4,Zm)=>{l();var gA=ue(),Km=class{constructor(e,t,r,n){this.unprefixed=e,this.prefixed=t,this.string=r||t,this.regexp=n||gA.regexp(t)}check(e){return e.includes(this.string)?!!e.match(this.regexp):!1}};Zm.exports=Km});var ke=v((L4,tg)=>{l();var yA=Vt(),wA=Wt(),bA=ii(),vA=ue(),eg=class extends yA{static save(e,t){let r=t.prop,n=[];for(let a in t._autoprefixerValues){let s=t._autoprefixerValues[a];if(s===t.value)continue;let o,u=bA.prefix(r);if(u==="-pie-")continue;if(u===a){o=t.value=s,n.push(o);continue}let c=e.prefixed(r,a),f=t.parent;if(!f.every(y=>y.prop!==c)){n.push(o);continue}let p=s.replace(/\s+/," ");if(f.some(y=>y.prop===t.prop&&y.value.replace(/\s+/," ")===p)){n.push(o);continue}let h=this.clone(t,{value:s});o=t.parent.insertBefore(t,h),n.push(o)}return n}check(e){let t=e.value;return t.includes(this.name)?!!t.match(this.regexp()):!1}regexp(){return this.regexpCache||(this.regexpCache=vA.regexp(this.name))}replace(e,t){return e.replace(this.regexp(),`$1${t}$2`)}value(e){return e.raws.value&&e.raws.value.value===e.value?e.raws.value.raw:e.value}add(e,t){e._autoprefixerValues||(e._autoprefixerValues={});let r=e._autoprefixerValues[t]||this.value(e),n;do if(n=r,r=this.replace(r,t),r===!1)return;while(r!==n);e._autoprefixerValues[t]=r}old(e){return new wA(this.name,e+this.name)}};tg.exports=eg});var dt=v(($4,rg)=>{l();rg.exports={}});var il=v((j4,sg)=>{l();var ig=Yn(),xA=ke(),kA=dt().insertAreas,SA=/(^|[^-])linear-gradient\(\s*(top|left|right|bottom)/i,CA=/(^|[^-])radial-gradient\(\s*\d+(\w*|%)\s+\d+(\w*|%)\s*,/i,AA=/(!\s*)?autoprefixer:\s*ignore\s+next/i,_A=/(!\s*)?autoprefixer\s*grid:\s*(on|off|(no-)?autoplace)/i,EA=["width","height","min-width","max-width","min-height","max-height","inline-size","min-inline-size","max-inline-size","block-size","min-block-size","max-block-size"];function rl(i){return i.parent.some(e=>e.prop==="grid-template"||e.prop==="grid-template-areas")}function OA(i){let e=i.parent.some(r=>r.prop==="grid-template-rows"),t=i.parent.some(r=>r.prop==="grid-template-columns");return e&&t}var ng=class{constructor(e){this.prefixes=e}add(e,t){let r=this.prefixes.add["@resolution"],n=this.prefixes.add["@keyframes"],a=this.prefixes.add["@viewport"],s=this.prefixes.add["@supports"];e.walkAtRules(f=>{if(f.name==="keyframes"){if(!this.disabled(f,t))return n&&n.process(f)}else if(f.name==="viewport"){if(!this.disabled(f,t))return a&&a.process(f)}else if(f.name==="supports"){if(this.prefixes.options.supports!==!1&&!this.disabled(f,t))return s.process(f)}else if(f.name==="media"&&f.params.includes("-resolution")&&!this.disabled(f,t))return r&&r.process(f)}),e.walkRules(f=>{if(!this.disabled(f,t))return this.prefixes.add.selectors.map(p=>p.process(f,t))});function o(f){return f.parent.nodes.some(p=>{if(p.type!=="decl")return!1;let d=p.prop==="display"&&/(inline-)?grid/.test(p.value),h=p.prop.startsWith("grid-template"),y=/^grid-([A-z]+-)?gap/.test(p.prop);return d||h||y})}function u(f){return f.parent.some(p=>p.prop==="display"&&/(inline-)?flex/.test(p.value))}let c=this.gridStatus(e,t)&&this.prefixes.add["grid-area"]&&this.prefixes.add["grid-area"].prefixes;return e.walkDecls(f=>{if(this.disabledDecl(f,t))return;let p=f.parent,d=f.prop,h=f.value;if(d==="grid-row-span"){t.warn("grid-row-span is not part of final Grid Layout. Use grid-row.",{node:f});return}else if(d==="grid-column-span"){t.warn("grid-column-span is not part of final Grid Layout. Use grid-column.",{node:f});return}else if(d==="display"&&h==="box"){t.warn("You should write display: flex by final spec instead of display: box",{node:f});return}else if(d==="text-emphasis-position")(h==="under"||h==="over")&&t.warn("You should use 2 values for text-emphasis-position For example, `under left` instead of just `under`.",{node:f});else if(/^(align|justify|place)-(items|content)$/.test(d)&&u(f))(h==="start"||h==="end")&&t.warn(`${h} value has mixed support, consider using flex-${h} instead`,{node:f});else if(d==="text-decoration-skip"&&h==="ink")t.warn("Replace text-decoration-skip: ink to text-decoration-skip-ink: auto, because spec had been changed",{node:f});else{if(c&&this.gridStatus(f,t))if(f.value==="subgrid"&&t.warn("IE does not support subgrid",{node:f}),/^(align|justify|place)-items$/.test(d)&&o(f)){let x=d.replace("-items","-self");t.warn(`IE does not support ${d} on grid containers. Try using ${x} on child elements instead: ${f.parent.selector} > * { ${x}: ${f.value} }`,{node:f})}else if(/^(align|justify|place)-content$/.test(d)&&o(f))t.warn(`IE does not support ${f.prop} on grid containers`,{node:f});else if(d==="display"&&f.value==="contents"){t.warn("Please do not use display: contents; if you have grid setting enabled",{node:f});return}else if(f.prop==="grid-gap"){let x=this.gridStatus(f,t);x==="autoplace"&&!OA(f)&&!rl(f)?t.warn("grid-gap only works if grid-template(-areas) is being used or both rows and columns have been declared and cells have not been manually placed inside the explicit grid",{node:f}):(x===!0||x==="no-autoplace")&&!rl(f)&&t.warn("grid-gap only works if grid-template(-areas) is being used",{node:f})}else if(d==="grid-auto-columns"){t.warn("grid-auto-columns is not supported by IE",{node:f});return}else if(d==="grid-auto-rows"){t.warn("grid-auto-rows is not supported by IE",{node:f});return}else if(d==="grid-auto-flow"){let x=p.some(b=>b.prop==="grid-template-rows"),w=p.some(b=>b.prop==="grid-template-columns");rl(f)?t.warn("grid-auto-flow is not supported by IE",{node:f}):h.includes("dense")?t.warn("grid-auto-flow: dense is not supported by IE",{node:f}):!x&&!w&&t.warn("grid-auto-flow works only if grid-template-rows and grid-template-columns are present in the same rule",{node:f});return}else if(h.includes("auto-fit")){t.warn("auto-fit value is not supported by IE",{node:f,word:"auto-fit"});return}else if(h.includes("auto-fill")){t.warn("auto-fill value is not supported by IE",{node:f,word:"auto-fill"});return}else d.startsWith("grid-template")&&h.includes("[")&&t.warn("Autoprefixer currently does not support line names. Try using grid-template-areas instead.",{node:f,word:"["});if(h.includes("radial-gradient"))if(CA.test(f.value))t.warn("Gradient has outdated direction syntax. New syntax is like `closest-side at 0 0` instead of `0 0, closest-side`.",{node:f});else{let x=ig(h);for(let w of x.nodes)if(w.type==="function"&&w.value==="radial-gradient")for(let b of w.nodes)b.type==="word"&&(b.value==="cover"?t.warn("Gradient has outdated direction syntax. Replace `cover` to `farthest-corner`.",{node:f}):b.value==="contain"&&t.warn("Gradient has outdated direction syntax. Replace `contain` to `closest-side`.",{node:f}))}h.includes("linear-gradient")&&SA.test(h)&&t.warn("Gradient has outdated direction syntax. New syntax is like `to left` instead of `right`.",{node:f})}EA.includes(f.prop)&&(f.value.includes("-fill-available")||(f.value.includes("fill-available")?t.warn("Replace fill-available to stretch, because spec had been changed",{node:f}):f.value.includes("fill")&&ig(h).nodes.some(w=>w.type==="word"&&w.value==="fill")&&t.warn("Replace fill to stretch, because spec had been changed",{node:f})));let y;if(f.prop==="transition"||f.prop==="transition-property")return this.prefixes.transition.add(f,t);if(f.prop==="align-self"){if(this.displayType(f)!=="grid"&&this.prefixes.options.flexbox!==!1&&(y=this.prefixes.add["align-self"],y&&y.prefixes&&y.process(f)),this.gridStatus(f,t)!==!1&&(y=this.prefixes.add["grid-row-align"],y&&y.prefixes))return y.process(f,t)}else if(f.prop==="justify-self"){if(this.gridStatus(f,t)!==!1&&(y=this.prefixes.add["grid-column-align"],y&&y.prefixes))return y.process(f,t)}else if(f.prop==="place-self"){if(y=this.prefixes.add["place-self"],y&&y.prefixes&&this.gridStatus(f,t)!==!1)return y.process(f,t)}else if(y=this.prefixes.add[f.prop],y&&y.prefixes)return y.process(f,t)}),this.gridStatus(e,t)&&kA(e,this.disabled),e.walkDecls(f=>{if(this.disabledValue(f,t))return;let p=this.prefixes.unprefixed(f.prop),d=this.prefixes.values("add",p);if(Array.isArray(d))for(let h of d)h.process&&h.process(f,t);xA.save(this.prefixes,f)})}remove(e,t){let r=this.prefixes.remove["@resolution"];e.walkAtRules((n,a)=>{this.prefixes.remove[`@${n.name}`]?this.disabled(n,t)||n.parent.removeChild(a):n.name==="media"&&n.params.includes("-resolution")&&r&&r.clean(n)});for(let n of this.prefixes.remove.selectors)e.walkRules((a,s)=>{n.check(a)&&(this.disabled(a,t)||a.parent.removeChild(s))});return e.walkDecls((n,a)=>{if(this.disabled(n,t))return;let s=n.parent,o=this.prefixes.unprefixed(n.prop);if((n.prop==="transition"||n.prop==="transition-property")&&this.prefixes.transition.remove(n),this.prefixes.remove[n.prop]&&this.prefixes.remove[n.prop].remove){let u=this.prefixes.group(n).down(c=>this.prefixes.normalize(c.prop)===o);if(o==="flex-flow"&&(u=!0),n.prop==="-webkit-box-orient"){let c={"flex-direction":!0,"flex-flow":!0};if(!n.parent.some(f=>c[f.prop]))return}if(u&&!this.withHackValue(n)){n.raw("before").includes(` +`)&&this.reduceSpaces(n),s.removeChild(a);return}}for(let u of this.prefixes.values("remove",o)){if(!u.check||!u.check(n.value))continue;if(o=u.unprefixed,this.prefixes.group(n).down(f=>f.value.includes(o))){s.removeChild(a);return}}})}withHackValue(e){return e.prop==="-webkit-background-clip"&&e.value==="text"}disabledValue(e,t){return this.gridStatus(e,t)===!1&&e.type==="decl"&&e.prop==="display"&&e.value.includes("grid")||this.prefixes.options.flexbox===!1&&e.type==="decl"&&e.prop==="display"&&e.value.includes("flex")||e.type==="decl"&&e.prop==="content"?!0:this.disabled(e,t)}disabledDecl(e,t){if(this.gridStatus(e,t)===!1&&e.type==="decl"&&(e.prop.includes("grid")||e.prop==="justify-items"))return!0;if(this.prefixes.options.flexbox===!1&&e.type==="decl"){let r=["order","justify-content","align-items","align-content"];if(e.prop.includes("flex")||r.includes(e.prop))return!0}return this.disabled(e,t)}disabled(e,t){if(!e)return!1;if(e._autoprefixerDisabled!==void 0)return e._autoprefixerDisabled;if(e.parent){let n=e.prev();if(n&&n.type==="comment"&&AA.test(n.text))return e._autoprefixerDisabled=!0,e._autoprefixerSelfDisabled=!0,!0}let r=null;if(e.nodes){let n;e.each(a=>{a.type==="comment"&&/(!\s*)?autoprefixer:\s*(off|on)/i.test(a.text)&&(typeof n!="undefined"?t.warn("Second Autoprefixer control comment was ignored. Autoprefixer applies control comment to whole block, not to next rules.",{node:a}):n=/on/i.test(a.text))}),n!==void 0&&(r=!n)}if(!e.nodes||r===null)if(e.parent){let n=this.disabled(e.parent,t);e.parent._autoprefixerSelfDisabled===!0?r=!1:r=n}else r=!1;return e._autoprefixerDisabled=r,r}reduceSpaces(e){let t=!1;if(this.prefixes.group(e).up(()=>(t=!0,!0)),t)return;let r=e.raw("before").split(` +`),n=r[r.length-1].length,a=!1;this.prefixes.group(e).down(s=>{r=s.raw("before").split(` +`);let o=r.length-1;r[o].length>n&&(a===!1&&(a=r[o].length-n),r[o]=r[o].slice(0,-a),s.raws.before=r.join(` +`))})}displayType(e){for(let t of e.parent.nodes)if(t.prop==="display"){if(t.value.includes("flex"))return"flex";if(t.value.includes("grid"))return"grid"}return!1}gridStatus(e,t){if(!e)return!1;if(e._autoprefixerGridStatus!==void 0)return e._autoprefixerGridStatus;let r=null;if(e.nodes){let n;e.each(a=>{if(a.type==="comment"&&_A.test(a.text)){let s=/:\s*autoplace/i.test(a.text),o=/no-autoplace/i.test(a.text);typeof n!="undefined"?t.warn("Second Autoprefixer grid control comment was ignored. Autoprefixer applies control comments to the whole block, not to the next rules.",{node:a}):s?n="autoplace":o?n=!0:n=/on/i.test(a.text)}}),n!==void 0&&(r=n)}if(e.type==="atrule"&&e.name==="supports"){let n=e.params;n.includes("grid")&&n.includes("auto")&&(r=!1)}if(!e.nodes||r===null)if(e.parent){let n=this.gridStatus(e.parent,t);e.parent._autoprefixerSelfDisabled===!0?r=!1:r=n}else typeof this.prefixes.options.grid!="undefined"?r=this.prefixes.options.grid:typeof m.env.AUTOPREFIXER_GRID!="undefined"?m.env.AUTOPREFIXER_GRID==="autoplace"?r="autoplace":r=!0:r=!1;return e._autoprefixerGridStatus=r,r}};sg.exports=ng});var og=v((z4,ag)=>{l();ag.exports={A:{A:{"2":"K E F G A B JC"},B:{"1":"C L M H N D O P Q R S T U V W X Y Z a b c d e f g h i j n o p q r s t u v w x y z I"},C:{"1":"2 3 4 5 6 7 8 9 AB BB CB DB EB FB GB HB IB JB KB LB MB NB OB PB QB RB SB TB UB VB WB XB YB ZB aB bB cB 0B dB 1B eB fB gB hB iB jB kB lB mB nB oB m pB qB rB sB tB P Q R 2B S T U V W X Y Z a b c d e f g h i j n o p q r s t u v w x y z I uB 3B 4B","2":"0 1 KC zB J K E F G A B C L M H N D O k l LC MC"},D:{"1":"8 9 AB BB CB DB EB FB GB HB IB JB KB LB MB NB OB PB QB RB SB TB UB VB WB XB YB ZB aB bB cB 0B dB 1B eB fB gB hB iB jB kB lB mB nB oB m pB qB rB sB tB P Q R S T U V W X Y Z a b c d e f g h i j n o p q r s t u v w x y z I uB 3B 4B","2":"0 1 2 3 4 5 6 7 J K E F G A B C L M H N D O k l"},E:{"1":"G A B C L M H D RC 6B vB wB 7B SC TC 8B 9B xB AC yB BC CC DC EC FC GC UC","2":"0 J K E F NC 5B OC PC QC"},F:{"1":"1 2 3 4 5 6 7 8 9 H N D O k l AB BB CB DB EB FB GB HB IB JB KB LB MB NB OB PB QB RB SB TB UB VB WB XB YB ZB aB bB cB dB eB fB gB hB iB jB kB lB mB nB oB m pB qB rB sB tB P Q R 2B S T U V W X Y Z a b c d e f g h i j wB","2":"G B C VC WC XC YC vB HC ZC"},G:{"1":"D fC gC hC iC jC kC lC mC nC oC pC qC rC sC tC 8B 9B xB AC yB BC CC DC EC FC GC","2":"F 5B aC IC bC cC dC eC"},H:{"1":"uC"},I:{"1":"I zC 0C","2":"zB J vC wC xC yC IC"},J:{"2":"E A"},K:{"1":"m","2":"A B C vB HC wB"},L:{"1":"I"},M:{"1":"uB"},N:{"2":"A B"},O:{"1":"xB"},P:{"1":"J k l 1C 2C 3C 4C 5C 6B 6C 7C 8C 9C AD yB BD CD DD"},Q:{"1":"7B"},R:{"1":"ED"},S:{"1":"FD GD"}},B:4,C:"CSS Feature Queries"}});var cg=v((V4,fg)=>{l();function lg(i){return i[i.length-1]}var ug={parse(i){let e=[""],t=[e];for(let r of i){if(r==="("){e=[""],lg(t).push(e),t.push(e);continue}if(r===")"){t.pop(),e=lg(t),e.push("");continue}e[e.length-1]+=r}return t[0]},stringify(i){let e="";for(let t of i){if(typeof t=="object"){e+=`(${ug.stringify(t)})`;continue}e+=t}return e}};fg.exports=ug});var gg=v((U4,mg)=>{l();var TA=og(),{feature:PA}=(zn(),jn),{parse:DA}=me(),IA=ct(),nl=cg(),qA=ke(),RA=ue(),pg=PA(TA),dg=[];for(let i in pg.stats){let e=pg.stats[i];for(let t in e){let r=e[t];/y/.test(r)&&dg.push(i+" "+t)}}var hg=class{constructor(e,t){this.Prefixes=e,this.all=t}prefixer(){if(this.prefixerCache)return this.prefixerCache;let e=this.all.browsers.selected.filter(r=>dg.includes(r)),t=new IA(this.all.browsers.data,e,this.all.options);return this.prefixerCache=new this.Prefixes(this.all.data,t,this.all.options),this.prefixerCache}parse(e){let t=e.split(":"),r=t[0],n=t[1];return n||(n=""),[r.trim(),n.trim()]}virtual(e){let[t,r]=this.parse(e),n=DA("a{}").first;return n.append({prop:t,value:r,raws:{before:""}}),n}prefixed(e){let t=this.virtual(e);if(this.disabled(t.first))return t.nodes;let r={warn:()=>null},n=this.prefixer().add[t.first.prop];n&&n.process&&n.process(t.first,r);for(let a of t.nodes){for(let s of this.prefixer().values("add",t.first.prop))s.process(a);qA.save(this.all,a)}return t.nodes}isNot(e){return typeof e=="string"&&/not\s*/i.test(e)}isOr(e){return typeof e=="string"&&/\s*or\s*/i.test(e)}isProp(e){return typeof e=="object"&&e.length===1&&typeof e[0]=="string"}isHack(e,t){return!new RegExp(`(\\(|\\s)${RA.escapeRegexp(t)}:`).test(e)}toRemove(e,t){let[r,n]=this.parse(e),a=this.all.unprefixed(r),s=this.all.cleaner();if(s.remove[r]&&s.remove[r].remove&&!this.isHack(t,a))return!0;for(let o of s.values("remove",a))if(o.check(n))return!0;return!1}remove(e,t){let r=0;for(;rtypeof t!="object"?t:t.length===1&&typeof t[0]=="object"?this.cleanBrackets(t[0]):this.cleanBrackets(t))}convert(e){let t=[""];for(let r of e)t.push([`${r.prop}: ${r.value}`]),t.push(" or ");return t[t.length-1]="",t}normalize(e){if(typeof e!="object")return e;if(e=e.filter(t=>t!==""),typeof e[0]=="string"){let t=e[0].trim();if(t.includes(":")||t==="selector"||t==="not selector")return[nl.stringify(e)]}return e.map(t=>this.normalize(t))}add(e,t){return e.map(r=>{if(this.isProp(r)){let n=this.prefixed(r[0]);return n.length>1?this.convert(n):r}return typeof r=="object"?this.add(r,t):r})}process(e){let t=nl.parse(e.params);t=this.normalize(t),t=this.remove(t,e.params),t=this.add(t,e.params),t=this.cleanBrackets(t),e.params=nl.stringify(t)}disabled(e){if(!this.all.options.grid&&(e.prop==="display"&&e.value.includes("grid")||e.prop.includes("grid")||e.prop==="justify-items"))return!0;if(this.all.options.flexbox===!1){if(e.prop==="display"&&e.value.includes("flex"))return!0;let t=["order","justify-content","align-items","align-content"];if(e.prop.includes("flex")||t.includes(e.prop))return!0}return!1}};mg.exports=hg});var bg=v((W4,wg)=>{l();var yg=class{constructor(e,t){this.prefix=t,this.prefixed=e.prefixed(this.prefix),this.regexp=e.regexp(this.prefix),this.prefixeds=e.possible().map(r=>[e.prefixed(r),e.regexp(r)]),this.unprefixed=e.name,this.nameRegexp=e.regexp()}isHack(e){let t=e.parent.index(e)+1,r=e.parent.nodes;for(;t{l();var{list:MA}=me(),BA=bg(),FA=Vt(),NA=ct(),LA=ue(),vg=class extends FA{constructor(e,t,r){super(e,t,r);this.regexpCache=new Map}check(e){return e.selector.includes(this.name)?!!e.selector.match(this.regexp()):!1}prefixed(e){return this.name.replace(/^(\W*)/,`$1${e}`)}regexp(e){if(!this.regexpCache.has(e)){let t=e?this.prefixed(e):this.name;this.regexpCache.set(e,new RegExp(`(^|[^:"'=])${LA.escapeRegexp(t)}`,"gi"))}return this.regexpCache.get(e)}possible(){return NA.prefixes()}prefixeds(e){if(e._autoprefixerPrefixeds){if(e._autoprefixerPrefixeds[this.name])return e._autoprefixerPrefixeds}else e._autoprefixerPrefixeds={};let t={};if(e.selector.includes(",")){let n=MA.comma(e.selector).filter(a=>a.includes(this.name));for(let a of this.possible())t[a]=n.map(s=>this.replace(s,a)).join(", ")}else for(let r of this.possible())t[r]=this.replace(e.selector,r);return e._autoprefixerPrefixeds[this.name]=t,e._autoprefixerPrefixeds}already(e,t,r){let n=e.parent.index(e)-1;for(;n>=0;){let a=e.parent.nodes[n];if(a.type!=="rule")return!1;let s=!1;for(let o in t[this.name]){let u=t[this.name][o];if(a.selector===u){if(r===o)return!0;s=!0;break}}if(!s)return!1;n-=1}return!1}replace(e,t){return e.replace(this.regexp(),`$1${this.prefixed(t)}`)}add(e,t){let r=this.prefixeds(e);if(this.already(e,r,t))return;let n=this.clone(e,{selector:r[this.name][t]});e.parent.insertBefore(e,n)}old(e){return new BA(this,e)}};xg.exports=vg});var Cg=v((H4,Sg)=>{l();var $A=Vt(),kg=class extends $A{add(e,t){let r=t+e.name;if(e.parent.some(s=>s.name===r&&s.params===e.params))return;let a=this.clone(e,{name:r});return e.parent.insertBefore(e,a)}process(e){let t=this.parentPrefix(e);for(let r of this.prefixes)(!t||t===r)&&this.add(e,r)}};Sg.exports=kg});var _g=v((Y4,Ag)=>{l();var jA=Gt(),sl=class extends jA{prefixed(e){return e==="-webkit-"?":-webkit-full-screen":e==="-moz-"?":-moz-full-screen":`:${e}fullscreen`}};sl.names=[":fullscreen"];Ag.exports=sl});var Og=v((Q4,Eg)=>{l();var zA=Gt(),al=class extends zA{possible(){return super.possible().concat(["-moz- old","-ms- old"])}prefixed(e){return e==="-webkit-"?"::-webkit-input-placeholder":e==="-ms-"?"::-ms-input-placeholder":e==="-ms- old"?":-ms-input-placeholder":e==="-moz- old"?":-moz-placeholder":`::${e}placeholder`}};al.names=["::placeholder"];Eg.exports=al});var Pg=v((J4,Tg)=>{l();var VA=Gt(),ol=class extends VA{prefixed(e){return e==="-ms-"?":-ms-input-placeholder":`:${e}placeholder-shown`}};ol.names=[":placeholder-shown"];Tg.exports=ol});var Ig=v((X4,Dg)=>{l();var UA=Gt(),WA=ue(),ll=class extends UA{constructor(e,t,r){super(e,t,r);this.prefixes&&(this.prefixes=WA.uniq(this.prefixes.map(n=>"-webkit-")))}prefixed(e){return e==="-webkit-"?"::-webkit-file-upload-button":`::${e}file-selector-button`}};ll.names=["::file-selector-button"];Dg.exports=ll});var de=v((K4,qg)=>{l();qg.exports=function(i){let e;return i==="-webkit- 2009"||i==="-moz-"?e=2009:i==="-ms-"?e=2012:i==="-webkit-"&&(e="final"),i==="-webkit- 2009"&&(i="-webkit-"),[e,i]}});var Fg=v((Z4,Bg)=>{l();var Rg=me().list,Mg=de(),GA=R(),Ht=class extends GA{prefixed(e,t){let r;return[r,t]=Mg(t),r===2009?t+"box-flex":super.prefixed(e,t)}normalize(){return"flex"}set(e,t){let r=Mg(t)[0];if(r===2009)return e.value=Rg.space(e.value)[0],e.value=Ht.oldValues[e.value]||e.value,super.set(e,t);if(r===2012){let n=Rg.space(e.value);n.length===3&&n[2]==="0"&&(e.value=n.slice(0,2).concat("0px").join(" "))}return super.set(e,t)}};Ht.names=["flex","box-flex"];Ht.oldValues={auto:"1",none:"0"};Bg.exports=Ht});var $g=v((eq,Lg)=>{l();var Ng=de(),HA=R(),ul=class extends HA{prefixed(e,t){let r;return[r,t]=Ng(t),r===2009?t+"box-ordinal-group":r===2012?t+"flex-order":super.prefixed(e,t)}normalize(){return"order"}set(e,t){return Ng(t)[0]===2009&&/\d/.test(e.value)?(e.value=(parseInt(e.value)+1).toString(),super.set(e,t)):super.set(e,t)}};ul.names=["order","flex-order","box-ordinal-group"];Lg.exports=ul});var zg=v((tq,jg)=>{l();var YA=R(),fl=class extends YA{check(e){let t=e.value;return!t.toLowerCase().includes("alpha(")&&!t.includes("DXImageTransform.Microsoft")&&!t.includes("data:image/svg+xml")}};fl.names=["filter"];jg.exports=fl});var Ug=v((rq,Vg)=>{l();var QA=R(),cl=class extends QA{insert(e,t,r,n){if(t!=="-ms-")return super.insert(e,t,r);let a=this.clone(e),s=e.prop.replace(/end$/,"start"),o=t+e.prop.replace(/end$/,"span");if(!e.parent.some(u=>u.prop===o)){if(a.prop=o,e.value.includes("span"))a.value=e.value.replace(/span\s/i,"");else{let u;if(e.parent.walkDecls(s,c=>{u=c}),u){let c=Number(e.value)-Number(u.value)+"";a.value=c}else e.warn(n,`Can not prefix ${e.prop} (${s} is not found)`)}e.cloneBefore(a)}}};cl.names=["grid-row-end","grid-column-end"];Vg.exports=cl});var Gg=v((iq,Wg)=>{l();var JA=R(),pl=class extends JA{check(e){return!e.value.split(/\s+/).some(t=>{let r=t.toLowerCase();return r==="reverse"||r==="alternate-reverse"})}};pl.names=["animation","animation-direction"];Wg.exports=pl});var Yg=v((nq,Hg)=>{l();var XA=de(),KA=R(),dl=class extends KA{insert(e,t,r){let n;if([n,t]=XA(t),n!==2009)return super.insert(e,t,r);let a=e.value.split(/\s+/).filter(p=>p!=="wrap"&&p!=="nowrap"&&"wrap-reverse");if(a.length===0||e.parent.some(p=>p.prop===t+"box-orient"||p.prop===t+"box-direction"))return;let o=a[0],u=o.includes("row")?"horizontal":"vertical",c=o.includes("reverse")?"reverse":"normal",f=this.clone(e);return f.prop=t+"box-orient",f.value=u,this.needCascade(e)&&(f.raws.before=this.calcBefore(r,e,t)),e.parent.insertBefore(e,f),f=this.clone(e),f.prop=t+"box-direction",f.value=c,this.needCascade(e)&&(f.raws.before=this.calcBefore(r,e,t)),e.parent.insertBefore(e,f)}};dl.names=["flex-flow","box-direction","box-orient"];Hg.exports=dl});var Jg=v((sq,Qg)=>{l();var ZA=de(),e_=R(),hl=class extends e_{normalize(){return"flex"}prefixed(e,t){let r;return[r,t]=ZA(t),r===2009?t+"box-flex":r===2012?t+"flex-positive":super.prefixed(e,t)}};hl.names=["flex-grow","flex-positive"];Qg.exports=hl});var Kg=v((aq,Xg)=>{l();var t_=de(),r_=R(),ml=class extends r_{set(e,t){if(t_(t)[0]!==2009)return super.set(e,t)}};ml.names=["flex-wrap"];Xg.exports=ml});var ey=v((oq,Zg)=>{l();var i_=R(),Yt=dt(),gl=class extends i_{insert(e,t,r,n){if(t!=="-ms-")return super.insert(e,t,r);let a=Yt.parse(e),[s,o]=Yt.translate(a,0,2),[u,c]=Yt.translate(a,1,3);[["grid-row",s],["grid-row-span",o],["grid-column",u],["grid-column-span",c]].forEach(([f,p])=>{Yt.insertDecl(e,f,p)}),Yt.warnTemplateSelectorNotFound(e,n),Yt.warnIfGridRowColumnExists(e,n)}};gl.names=["grid-area"];Zg.exports=gl});var ry=v((lq,ty)=>{l();var n_=R(),ni=dt(),yl=class extends n_{insert(e,t,r){if(t!=="-ms-")return super.insert(e,t,r);if(e.parent.some(s=>s.prop==="-ms-grid-row-align"))return;let[[n,a]]=ni.parse(e);a?(ni.insertDecl(e,"grid-row-align",n),ni.insertDecl(e,"grid-column-align",a)):(ni.insertDecl(e,"grid-row-align",n),ni.insertDecl(e,"grid-column-align",n))}};yl.names=["place-self"];ty.exports=yl});var ny=v((uq,iy)=>{l();var s_=R(),wl=class extends s_{check(e){let t=e.value;return!t.includes("/")||t.includes("span")}normalize(e){return e.replace("-start","")}prefixed(e,t){let r=super.prefixed(e,t);return t==="-ms-"&&(r=r.replace("-start","")),r}};wl.names=["grid-row-start","grid-column-start"];iy.exports=wl});var oy=v((fq,ay)=>{l();var sy=de(),a_=R(),Qt=class extends a_{check(e){return e.parent&&!e.parent.some(t=>t.prop&&t.prop.startsWith("grid-"))}prefixed(e,t){let r;return[r,t]=sy(t),r===2012?t+"flex-item-align":super.prefixed(e,t)}normalize(){return"align-self"}set(e,t){let r=sy(t)[0];if(r===2012)return e.value=Qt.oldValues[e.value]||e.value,super.set(e,t);if(r==="final")return super.set(e,t)}};Qt.names=["align-self","flex-item-align"];Qt.oldValues={"flex-end":"end","flex-start":"start"};ay.exports=Qt});var uy=v((cq,ly)=>{l();var o_=R(),l_=ue(),bl=class extends o_{constructor(e,t,r){super(e,t,r);this.prefixes&&(this.prefixes=l_.uniq(this.prefixes.map(n=>n==="-ms-"?"-webkit-":n)))}};bl.names=["appearance"];ly.exports=bl});var py=v((pq,cy)=>{l();var fy=de(),u_=R(),vl=class extends u_{normalize(){return"flex-basis"}prefixed(e,t){let r;return[r,t]=fy(t),r===2012?t+"flex-preferred-size":super.prefixed(e,t)}set(e,t){let r;if([r,t]=fy(t),r===2012||r==="final")return super.set(e,t)}};vl.names=["flex-basis","flex-preferred-size"];cy.exports=vl});var hy=v((dq,dy)=>{l();var f_=R(),xl=class extends f_{normalize(){return this.name.replace("box-image","border")}prefixed(e,t){let r=super.prefixed(e,t);return t==="-webkit-"&&(r=r.replace("border","box-image")),r}};xl.names=["mask-border","mask-border-source","mask-border-slice","mask-border-width","mask-border-outset","mask-border-repeat","mask-box-image","mask-box-image-source","mask-box-image-slice","mask-box-image-width","mask-box-image-outset","mask-box-image-repeat"];dy.exports=xl});var gy=v((hq,my)=>{l();var c_=R(),Le=class extends c_{insert(e,t,r){let n=e.prop==="mask-composite",a;n?a=e.value.split(","):a=e.value.match(Le.regexp)||[],a=a.map(c=>c.trim()).filter(c=>c);let s=a.length,o;if(s&&(o=this.clone(e),o.value=a.map(c=>Le.oldValues[c]||c).join(", "),a.includes("intersect")&&(o.value+=", xor"),o.prop=t+"mask-composite"),n)return s?(this.needCascade(e)&&(o.raws.before=this.calcBefore(r,e,t)),e.parent.insertBefore(e,o)):void 0;let u=this.clone(e);return u.prop=t+u.prop,s&&(u.value=u.value.replace(Le.regexp,"")),this.needCascade(e)&&(u.raws.before=this.calcBefore(r,e,t)),e.parent.insertBefore(e,u),s?(this.needCascade(e)&&(o.raws.before=this.calcBefore(r,e,t)),e.parent.insertBefore(e,o)):e}};Le.names=["mask","mask-composite"];Le.oldValues={add:"source-over",subtract:"source-out",intersect:"source-in",exclude:"xor"};Le.regexp=new RegExp(`\\s+(${Object.keys(Le.oldValues).join("|")})\\b(?!\\))\\s*(?=[,])`,"ig");my.exports=Le});var by=v((mq,wy)=>{l();var yy=de(),p_=R(),Jt=class extends p_{prefixed(e,t){let r;return[r,t]=yy(t),r===2009?t+"box-align":r===2012?t+"flex-align":super.prefixed(e,t)}normalize(){return"align-items"}set(e,t){let r=yy(t)[0];return(r===2009||r===2012)&&(e.value=Jt.oldValues[e.value]||e.value),super.set(e,t)}};Jt.names=["align-items","flex-align","box-align"];Jt.oldValues={"flex-end":"end","flex-start":"start"};wy.exports=Jt});var xy=v((gq,vy)=>{l();var d_=R(),kl=class extends d_{set(e,t){return t==="-ms-"&&e.value==="contain"&&(e.value="element"),super.set(e,t)}insert(e,t,r){if(!(e.value==="all"&&t==="-ms-"))return super.insert(e,t,r)}};kl.names=["user-select"];vy.exports=kl});var Cy=v((yq,Sy)=>{l();var ky=de(),h_=R(),Sl=class extends h_{normalize(){return"flex-shrink"}prefixed(e,t){let r;return[r,t]=ky(t),r===2012?t+"flex-negative":super.prefixed(e,t)}set(e,t){let r;if([r,t]=ky(t),r===2012||r==="final")return super.set(e,t)}};Sl.names=["flex-shrink","flex-negative"];Sy.exports=Sl});var _y=v((wq,Ay)=>{l();var m_=R(),Cl=class extends m_{prefixed(e,t){return`${t}column-${e}`}normalize(e){return e.includes("inside")?"break-inside":e.includes("before")?"break-before":"break-after"}set(e,t){return(e.prop==="break-inside"&&e.value==="avoid-column"||e.value==="avoid-page")&&(e.value="avoid"),super.set(e,t)}insert(e,t,r){if(e.prop!=="break-inside")return super.insert(e,t,r);if(!(/region/i.test(e.value)||/page/i.test(e.value)))return super.insert(e,t,r)}};Cl.names=["break-inside","page-break-inside","column-break-inside","break-before","page-break-before","column-break-before","break-after","page-break-after","column-break-after"];Ay.exports=Cl});var Oy=v((bq,Ey)=>{l();var g_=R(),Al=class extends g_{prefixed(e,t){return t+"print-color-adjust"}normalize(){return"color-adjust"}};Al.names=["color-adjust","print-color-adjust"];Ey.exports=Al});var Py=v((vq,Ty)=>{l();var y_=R(),Xt=class extends y_{insert(e,t,r){if(t==="-ms-"){let n=this.set(this.clone(e),t);this.needCascade(e)&&(n.raws.before=this.calcBefore(r,e,t));let a="ltr";return e.parent.nodes.forEach(s=>{s.prop==="direction"&&(s.value==="rtl"||s.value==="ltr")&&(a=s.value)}),n.value=Xt.msValues[a][e.value]||e.value,e.parent.insertBefore(e,n)}return super.insert(e,t,r)}};Xt.names=["writing-mode"];Xt.msValues={ltr:{"horizontal-tb":"lr-tb","vertical-rl":"tb-rl","vertical-lr":"tb-lr"},rtl:{"horizontal-tb":"rl-tb","vertical-rl":"bt-rl","vertical-lr":"bt-lr"}};Ty.exports=Xt});var Iy=v((xq,Dy)=>{l();var w_=R(),_l=class extends w_{set(e,t){return e.value=e.value.replace(/\s+fill(\s)/,"$1"),super.set(e,t)}};_l.names=["border-image"];Dy.exports=_l});var My=v((kq,Ry)=>{l();var qy=de(),b_=R(),Kt=class extends b_{prefixed(e,t){let r;return[r,t]=qy(t),r===2012?t+"flex-line-pack":super.prefixed(e,t)}normalize(){return"align-content"}set(e,t){let r=qy(t)[0];if(r===2012)return e.value=Kt.oldValues[e.value]||e.value,super.set(e,t);if(r==="final")return super.set(e,t)}};Kt.names=["align-content","flex-line-pack"];Kt.oldValues={"flex-end":"end","flex-start":"start","space-between":"justify","space-around":"distribute"};Ry.exports=Kt});var Fy=v((Sq,By)=>{l();var v_=R(),Se=class extends v_{prefixed(e,t){return t==="-moz-"?t+(Se.toMozilla[e]||e):super.prefixed(e,t)}normalize(e){return Se.toNormal[e]||e}};Se.names=["border-radius"];Se.toMozilla={};Se.toNormal={};for(let i of["top","bottom"])for(let e of["left","right"]){let t=`border-${i}-${e}-radius`,r=`border-radius-${i}${e}`;Se.names.push(t),Se.names.push(r),Se.toMozilla[t]=r,Se.toNormal[r]=t}By.exports=Se});var Ly=v((Cq,Ny)=>{l();var x_=R(),El=class extends x_{prefixed(e,t){return e.includes("-start")?t+e.replace("-block-start","-before"):t+e.replace("-block-end","-after")}normalize(e){return e.includes("-before")?e.replace("-before","-block-start"):e.replace("-after","-block-end")}};El.names=["border-block-start","border-block-end","margin-block-start","margin-block-end","padding-block-start","padding-block-end","border-before","border-after","margin-before","margin-after","padding-before","padding-after"];Ny.exports=El});var jy=v((Aq,$y)=>{l();var k_=R(),{parseTemplate:S_,warnMissedAreas:C_,getGridGap:A_,warnGridGap:__,inheritGridGap:E_}=dt(),Ol=class extends k_{insert(e,t,r,n){if(t!=="-ms-")return super.insert(e,t,r);if(e.parent.some(h=>h.prop==="-ms-grid-rows"))return;let a=A_(e),s=E_(e,a),{rows:o,columns:u,areas:c}=S_({decl:e,gap:s||a}),f=Object.keys(c).length>0,p=Boolean(o),d=Boolean(u);return __({gap:a,hasColumns:d,decl:e,result:n}),C_(c,e,n),(p&&d||f)&&e.cloneBefore({prop:"-ms-grid-rows",value:o,raws:{}}),d&&e.cloneBefore({prop:"-ms-grid-columns",value:u,raws:{}}),e}};Ol.names=["grid-template"];$y.exports=Ol});var Vy=v((_q,zy)=>{l();var O_=R(),Tl=class extends O_{prefixed(e,t){return t+e.replace("-inline","")}normalize(e){return e.replace(/(margin|padding|border)-(start|end)/,"$1-inline-$2")}};Tl.names=["border-inline-start","border-inline-end","margin-inline-start","margin-inline-end","padding-inline-start","padding-inline-end","border-start","border-end","margin-start","margin-end","padding-start","padding-end"];zy.exports=Tl});var Wy=v((Eq,Uy)=>{l();var T_=R(),Pl=class extends T_{check(e){return!e.value.includes("flex-")&&e.value!=="baseline"}prefixed(e,t){return t+"grid-row-align"}normalize(){return"align-self"}};Pl.names=["grid-row-align"];Uy.exports=Pl});var Hy=v((Oq,Gy)=>{l();var P_=R(),Zt=class extends P_{keyframeParents(e){let{parent:t}=e;for(;t;){if(t.type==="atrule"&&t.name==="keyframes")return!0;({parent:t}=t)}return!1}contain3d(e){if(e.prop==="transform-origin")return!1;for(let t of Zt.functions3d)if(e.value.includes(`${t}(`))return!0;return!1}set(e,t){return e=super.set(e,t),t==="-ms-"&&(e.value=e.value.replace(/rotatez/gi,"rotate")),e}insert(e,t,r){if(t==="-ms-"){if(!this.contain3d(e)&&!this.keyframeParents(e))return super.insert(e,t,r)}else if(t==="-o-"){if(!this.contain3d(e))return super.insert(e,t,r)}else return super.insert(e,t,r)}};Zt.names=["transform","transform-origin"];Zt.functions3d=["matrix3d","translate3d","translateZ","scale3d","scaleZ","rotate3d","rotateX","rotateY","perspective"];Gy.exports=Zt});var Jy=v((Tq,Qy)=>{l();var Yy=de(),D_=R(),Dl=class extends D_{normalize(){return"flex-direction"}insert(e,t,r){let n;if([n,t]=Yy(t),n!==2009)return super.insert(e,t,r);if(e.parent.some(f=>f.prop===t+"box-orient"||f.prop===t+"box-direction"))return;let s=e.value,o,u;s==="inherit"||s==="initial"||s==="unset"?(o=s,u=s):(o=s.includes("row")?"horizontal":"vertical",u=s.includes("reverse")?"reverse":"normal");let c=this.clone(e);return c.prop=t+"box-orient",c.value=o,this.needCascade(e)&&(c.raws.before=this.calcBefore(r,e,t)),e.parent.insertBefore(e,c),c=this.clone(e),c.prop=t+"box-direction",c.value=u,this.needCascade(e)&&(c.raws.before=this.calcBefore(r,e,t)),e.parent.insertBefore(e,c)}old(e,t){let r;return[r,t]=Yy(t),r===2009?[t+"box-orient",t+"box-direction"]:super.old(e,t)}};Dl.names=["flex-direction","box-direction","box-orient"];Qy.exports=Dl});var Ky=v((Pq,Xy)=>{l();var I_=R(),Il=class extends I_{check(e){return e.value==="pixelated"}prefixed(e,t){return t==="-ms-"?"-ms-interpolation-mode":super.prefixed(e,t)}set(e,t){return t!=="-ms-"?super.set(e,t):(e.prop="-ms-interpolation-mode",e.value="nearest-neighbor",e)}normalize(){return"image-rendering"}process(e,t){return super.process(e,t)}};Il.names=["image-rendering","interpolation-mode"];Xy.exports=Il});var ew=v((Dq,Zy)=>{l();var q_=R(),R_=ue(),ql=class extends q_{constructor(e,t,r){super(e,t,r);this.prefixes&&(this.prefixes=R_.uniq(this.prefixes.map(n=>n==="-ms-"?"-webkit-":n)))}};ql.names=["backdrop-filter"];Zy.exports=ql});var rw=v((Iq,tw)=>{l();var M_=R(),B_=ue(),Rl=class extends M_{constructor(e,t,r){super(e,t,r);this.prefixes&&(this.prefixes=B_.uniq(this.prefixes.map(n=>n==="-ms-"?"-webkit-":n)))}check(e){return e.value.toLowerCase()==="text"}};Rl.names=["background-clip"];tw.exports=Rl});var nw=v((qq,iw)=>{l();var F_=R(),N_=["none","underline","overline","line-through","blink","inherit","initial","unset"],Ml=class extends F_{check(e){return e.value.split(/\s+/).some(t=>!N_.includes(t))}};Ml.names=["text-decoration"];iw.exports=Ml});var ow=v((Rq,aw)=>{l();var sw=de(),L_=R(),er=class extends L_{prefixed(e,t){let r;return[r,t]=sw(t),r===2009?t+"box-pack":r===2012?t+"flex-pack":super.prefixed(e,t)}normalize(){return"justify-content"}set(e,t){let r=sw(t)[0];if(r===2009||r===2012){let n=er.oldValues[e.value]||e.value;if(e.value=n,r!==2009||n!=="distribute")return super.set(e,t)}else if(r==="final")return super.set(e,t)}};er.names=["justify-content","flex-pack","box-pack"];er.oldValues={"flex-end":"end","flex-start":"start","space-between":"justify","space-around":"distribute"};aw.exports=er});var uw=v((Mq,lw)=>{l();var $_=R(),Bl=class extends $_{set(e,t){let r=e.value.toLowerCase();return t==="-webkit-"&&!r.includes(" ")&&r!=="contain"&&r!=="cover"&&(e.value=e.value+" "+e.value),super.set(e,t)}};Bl.names=["background-size"];lw.exports=Bl});var cw=v((Bq,fw)=>{l();var j_=R(),Fl=dt(),Nl=class extends j_{insert(e,t,r){if(t!=="-ms-")return super.insert(e,t,r);let n=Fl.parse(e),[a,s]=Fl.translate(n,0,1);n[0]&&n[0].includes("span")&&(s=n[0].join("").replace(/\D/g,"")),[[e.prop,a],[`${e.prop}-span`,s]].forEach(([u,c])=>{Fl.insertDecl(e,u,c)})}};Nl.names=["grid-row","grid-column"];fw.exports=Nl});var hw=v((Fq,dw)=>{l();var z_=R(),{prefixTrackProp:pw,prefixTrackValue:V_,autoplaceGridItems:U_,getGridGap:W_,inheritGridGap:G_}=dt(),H_=il(),Ll=class extends z_{prefixed(e,t){return t==="-ms-"?pw({prop:e,prefix:t}):super.prefixed(e,t)}normalize(e){return e.replace(/^grid-(rows|columns)/,"grid-template-$1")}insert(e,t,r,n){if(t!=="-ms-")return super.insert(e,t,r);let{parent:a,prop:s,value:o}=e,u=s.includes("rows"),c=s.includes("columns"),f=a.some(k=>k.prop==="grid-template"||k.prop==="grid-template-areas");if(f&&u)return!1;let p=new H_({options:{}}),d=p.gridStatus(a,n),h=W_(e);h=G_(e,h)||h;let y=u?h.row:h.column;(d==="no-autoplace"||d===!0)&&!f&&(y=null);let x=V_({value:o,gap:y});e.cloneBefore({prop:pw({prop:s,prefix:t}),value:x});let w=a.nodes.find(k=>k.prop==="grid-auto-flow"),b="row";if(w&&!p.disabled(w,n)&&(b=w.value.trim()),d==="autoplace"){let k=a.nodes.find(_=>_.prop==="grid-template-rows");if(!k&&f)return;if(!k&&!f){e.warn(n,"Autoplacement does not work without grid-template-rows property");return}!a.nodes.find(_=>_.prop==="grid-template-columns")&&!f&&e.warn(n,"Autoplacement does not work without grid-template-columns property"),c&&!f&&U_(e,n,h,b)}}};Ll.names=["grid-template-rows","grid-template-columns","grid-rows","grid-columns"];dw.exports=Ll});var gw=v((Nq,mw)=>{l();var Y_=R(),$l=class extends Y_{check(e){return!e.value.includes("flex-")&&e.value!=="baseline"}prefixed(e,t){return t+"grid-column-align"}normalize(){return"justify-self"}};$l.names=["grid-column-align"];mw.exports=$l});var ww=v((Lq,yw)=>{l();var Q_=R(),jl=class extends Q_{prefixed(e,t){return t+"scroll-chaining"}normalize(){return"overscroll-behavior"}set(e,t){return e.value==="auto"?e.value="chained":(e.value==="none"||e.value==="contain")&&(e.value="none"),super.set(e,t)}};jl.names=["overscroll-behavior","scroll-chaining"];yw.exports=jl});var xw=v(($q,vw)=>{l();var J_=R(),{parseGridAreas:X_,warnMissedAreas:K_,prefixTrackProp:Z_,prefixTrackValue:bw,getGridGap:eE,warnGridGap:tE,inheritGridGap:rE}=dt();function iE(i){return i.trim().slice(1,-1).split(/["']\s*["']?/g)}var zl=class extends J_{insert(e,t,r,n){if(t!=="-ms-")return super.insert(e,t,r);let a=!1,s=!1,o=e.parent,u=eE(e);u=rE(e,u)||u,o.walkDecls(/-ms-grid-rows/,p=>p.remove()),o.walkDecls(/grid-template-(rows|columns)/,p=>{if(p.prop==="grid-template-rows"){s=!0;let{prop:d,value:h}=p;p.cloneBefore({prop:Z_({prop:d,prefix:t}),value:bw({value:h,gap:u.row})})}else a=!0});let c=iE(e.value);a&&!s&&u.row&&c.length>1&&e.cloneBefore({prop:"-ms-grid-rows",value:bw({value:`repeat(${c.length}, auto)`,gap:u.row}),raws:{}}),tE({gap:u,hasColumns:a,decl:e,result:n});let f=X_({rows:c,gap:u});return K_(f,e,n),e}};zl.names=["grid-template-areas"];vw.exports=zl});var Sw=v((jq,kw)=>{l();var nE=R(),Vl=class extends nE{set(e,t){return t==="-webkit-"&&(e.value=e.value.replace(/\s*(right|left)\s*/i,"")),super.set(e,t)}};Vl.names=["text-emphasis-position"];kw.exports=Vl});var Aw=v((zq,Cw)=>{l();var sE=R(),Ul=class extends sE{set(e,t){return e.prop==="text-decoration-skip-ink"&&e.value==="auto"?(e.prop=t+"text-decoration-skip",e.value="ink",e):super.set(e,t)}};Ul.names=["text-decoration-skip-ink","text-decoration-skip"];Cw.exports=Ul});var Dw=v((Vq,Pw)=>{l();"use strict";Pw.exports={wrap:_w,limit:Ew,validate:Ow,test:Wl,curry:aE,name:Tw};function _w(i,e,t){var r=e-i;return((t-i)%r+r)%r+i}function Ew(i,e,t){return Math.max(i,Math.min(e,t))}function Ow(i,e,t,r,n){if(!Wl(i,e,t,r,n))throw new Error(t+" is outside of range ["+i+","+e+")");return t}function Wl(i,e,t,r,n){return!(te||n&&t===e||r&&t===i)}function Tw(i,e,t,r){return(t?"(":"[")+i+","+e+(r?")":"]")}function aE(i,e,t,r){var n=Tw.bind(null,i,e,t,r);return{wrap:_w.bind(null,i,e),limit:Ew.bind(null,i,e),validate:function(a){return Ow(i,e,a,t,r)},test:function(a){return Wl(i,e,a,t,r)},toString:n,name:n}}});var Rw=v((Uq,qw)=>{l();var Gl=Yn(),oE=Dw(),lE=Wt(),uE=ke(),fE=ue(),Iw=/top|left|right|bottom/gi,Qe=class extends uE{replace(e,t){let r=Gl(e);for(let n of r.nodes)if(n.type==="function"&&n.value===this.name)if(n.nodes=this.newDirection(n.nodes),n.nodes=this.normalize(n.nodes),t==="-webkit- old"){if(!this.oldWebkit(n))return!1}else n.nodes=this.convertDirection(n.nodes),n.value=t+n.value;return r.toString()}replaceFirst(e,...t){return t.map(n=>n===" "?{type:"space",value:n}:{type:"word",value:n}).concat(e.slice(1))}normalizeUnit(e,t){return`${parseFloat(e)/t*360}deg`}normalize(e){if(!e[0])return e;if(/-?\d+(.\d+)?grad/.test(e[0].value))e[0].value=this.normalizeUnit(e[0].value,400);else if(/-?\d+(.\d+)?rad/.test(e[0].value))e[0].value=this.normalizeUnit(e[0].value,2*Math.PI);else if(/-?\d+(.\d+)?turn/.test(e[0].value))e[0].value=this.normalizeUnit(e[0].value,1);else if(e[0].value.includes("deg")){let t=parseFloat(e[0].value);t=oE.wrap(0,360,t),e[0].value=`${t}deg`}return e[0].value==="0deg"?e=this.replaceFirst(e,"to"," ","top"):e[0].value==="90deg"?e=this.replaceFirst(e,"to"," ","right"):e[0].value==="180deg"?e=this.replaceFirst(e,"to"," ","bottom"):e[0].value==="270deg"&&(e=this.replaceFirst(e,"to"," ","left")),e}newDirection(e){if(e[0].value==="to"||(Iw.lastIndex=0,!Iw.test(e[0].value)))return e;e.unshift({type:"word",value:"to"},{type:"space",value:" "});for(let t=2;t0&&(e[0].value==="to"?this.fixDirection(e):e[0].value.includes("deg")?this.fixAngle(e):this.isRadial(e)&&this.fixRadial(e)),e}fixDirection(e){e.splice(0,2);for(let t of e){if(t.type==="div")break;t.type==="word"&&(t.value=this.revertDirection(t.value))}}fixAngle(e){let t=e[0].value;t=parseFloat(t),t=Math.abs(450-t)%360,t=this.roundFloat(t,3),e[0].value=`${t}deg`}fixRadial(e){let t=[],r=[],n,a,s,o,u;for(o=0;o{l();var cE=Wt(),pE=ke();function Mw(i){return new RegExp(`(^|[\\s,(])(${i}($|[\\s),]))`,"gi")}var Hl=class extends pE{regexp(){return this.regexpCache||(this.regexpCache=Mw(this.name)),this.regexpCache}isStretch(){return this.name==="stretch"||this.name==="fill"||this.name==="fill-available"}replace(e,t){return t==="-moz-"&&this.isStretch()?e.replace(this.regexp(),"$1-moz-available$3"):t==="-webkit-"&&this.isStretch()?e.replace(this.regexp(),"$1-webkit-fill-available$3"):super.replace(e,t)}old(e){let t=e+this.name;return this.isStretch()&&(e==="-moz-"?t="-moz-available":e==="-webkit-"&&(t="-webkit-fill-available")),new cE(this.name,t,t,Mw(t))}add(e,t){if(!(e.prop.includes("grid")&&t!=="-webkit-"))return super.add(e,t)}};Hl.names=["max-content","min-content","fit-content","fill","fill-available","stretch"];Bw.exports=Hl});var $w=v((Gq,Lw)=>{l();var Nw=Wt(),dE=ke(),Yl=class extends dE{replace(e,t){return t==="-webkit-"?e.replace(this.regexp(),"$1-webkit-optimize-contrast"):t==="-moz-"?e.replace(this.regexp(),"$1-moz-crisp-edges"):super.replace(e,t)}old(e){return e==="-webkit-"?new Nw(this.name,"-webkit-optimize-contrast"):e==="-moz-"?new Nw(this.name,"-moz-crisp-edges"):super.old(e)}};Yl.names=["pixelated"];Lw.exports=Yl});var zw=v((Hq,jw)=>{l();var hE=ke(),Ql=class extends hE{replace(e,t){let r=super.replace(e,t);return t==="-webkit-"&&(r=r.replace(/("[^"]+"|'[^']+')(\s+\d+\w)/gi,"url($1)$2")),r}};Ql.names=["image-set"];jw.exports=Ql});var Uw=v((Yq,Vw)=>{l();var mE=me().list,gE=ke(),Jl=class extends gE{replace(e,t){return mE.space(e).map(r=>{if(r.slice(0,+this.name.length+1)!==this.name+"(")return r;let n=r.lastIndexOf(")"),a=r.slice(n+1),s=r.slice(this.name.length+1,n);if(t==="-webkit-"){let o=s.match(/\d*.?\d+%?/);o?(s=s.slice(o[0].length).trim(),s+=`, ${o[0]}`):s+=", 0.5"}return t+this.name+"("+s+")"+a}).join(" ")}};Jl.names=["cross-fade"];Vw.exports=Jl});var Gw=v((Qq,Ww)=>{l();var yE=de(),wE=Wt(),bE=ke(),Xl=class extends bE{constructor(e,t){super(e,t);e==="display-flex"&&(this.name="flex")}check(e){return e.prop==="display"&&e.value===this.name}prefixed(e){let t,r;return[t,e]=yE(e),t===2009?this.name==="flex"?r="box":r="inline-box":t===2012?this.name==="flex"?r="flexbox":r="inline-flexbox":t==="final"&&(r=this.name),e+r}replace(e,t){return this.prefixed(t)}old(e){let t=this.prefixed(e);if(!!t)return new wE(this.name,t)}};Xl.names=["display-flex","inline-flex"];Ww.exports=Xl});var Yw=v((Jq,Hw)=>{l();var vE=ke(),Kl=class extends vE{constructor(e,t){super(e,t);e==="display-grid"&&(this.name="grid")}check(e){return e.prop==="display"&&e.value===this.name}};Kl.names=["display-grid","inline-grid"];Hw.exports=Kl});var Jw=v((Xq,Qw)=>{l();var xE=ke(),Zl=class extends xE{constructor(e,t){super(e,t);e==="filter-function"&&(this.name="filter")}};Zl.names=["filter","filter-function"];Qw.exports=Zl});var eb=v((Kq,Zw)=>{l();var Xw=ii(),M=R(),Kw=qm(),kE=Xm(),SE=il(),CE=gg(),eu=ct(),tr=Gt(),AE=Cg(),$e=ke(),rr=ue(),_E=_g(),EE=Og(),OE=Pg(),TE=Ig(),PE=Fg(),DE=$g(),IE=zg(),qE=Ug(),RE=Gg(),ME=Yg(),BE=Jg(),FE=Kg(),NE=ey(),LE=ry(),$E=ny(),jE=oy(),zE=uy(),VE=py(),UE=hy(),WE=gy(),GE=by(),HE=xy(),YE=Cy(),QE=_y(),JE=Oy(),XE=Py(),KE=Iy(),ZE=My(),e5=Fy(),t5=Ly(),r5=jy(),i5=Vy(),n5=Wy(),s5=Hy(),a5=Jy(),o5=Ky(),l5=ew(),u5=rw(),f5=nw(),c5=ow(),p5=uw(),d5=cw(),h5=hw(),m5=gw(),g5=ww(),y5=xw(),w5=Sw(),b5=Aw(),v5=Rw(),x5=Fw(),k5=$w(),S5=zw(),C5=Uw(),A5=Gw(),_5=Yw(),E5=Jw();tr.hack(_E);tr.hack(EE);tr.hack(OE);tr.hack(TE);M.hack(PE);M.hack(DE);M.hack(IE);M.hack(qE);M.hack(RE);M.hack(ME);M.hack(BE);M.hack(FE);M.hack(NE);M.hack(LE);M.hack($E);M.hack(jE);M.hack(zE);M.hack(VE);M.hack(UE);M.hack(WE);M.hack(GE);M.hack(HE);M.hack(YE);M.hack(QE);M.hack(JE);M.hack(XE);M.hack(KE);M.hack(ZE);M.hack(e5);M.hack(t5);M.hack(r5);M.hack(i5);M.hack(n5);M.hack(s5);M.hack(a5);M.hack(o5);M.hack(l5);M.hack(u5);M.hack(f5);M.hack(c5);M.hack(p5);M.hack(d5);M.hack(h5);M.hack(m5);M.hack(g5);M.hack(y5);M.hack(w5);M.hack(b5);$e.hack(v5);$e.hack(x5);$e.hack(k5);$e.hack(S5);$e.hack(C5);$e.hack(A5);$e.hack(_5);$e.hack(E5);var tu=new Map,si=class{constructor(e,t,r={}){this.data=e,this.browsers=t,this.options=r,[this.add,this.remove]=this.preprocess(this.select(this.data)),this.transition=new kE(this),this.processor=new SE(this)}cleaner(){if(this.cleanerCache)return this.cleanerCache;if(this.browsers.selected.length){let e=new eu(this.browsers.data,[]);this.cleanerCache=new si(this.data,e,this.options)}else return this;return this.cleanerCache}select(e){let t={add:{},remove:{}};for(let r in e){let n=e[r],a=n.browsers.map(u=>{let c=u.split(" ");return{browser:`${c[0]} ${c[1]}`,note:c[2]}}),s=a.filter(u=>u.note).map(u=>`${this.browsers.prefix(u.browser)} ${u.note}`);s=rr.uniq(s),a=a.filter(u=>this.browsers.isSelected(u.browser)).map(u=>{let c=this.browsers.prefix(u.browser);return u.note?`${c} ${u.note}`:c}),a=this.sort(rr.uniq(a)),this.options.flexbox==="no-2009"&&(a=a.filter(u=>!u.includes("2009")));let o=n.browsers.map(u=>this.browsers.prefix(u));n.mistakes&&(o=o.concat(n.mistakes)),o=o.concat(s),o=rr.uniq(o),a.length?(t.add[r]=a,a.length!a.includes(u)))):t.remove[r]=o}return t}sort(e){return e.sort((t,r)=>{let n=rr.removeNote(t).length,a=rr.removeNote(r).length;return n===a?r.length-t.length:a-n})}preprocess(e){let t={selectors:[],"@supports":new CE(si,this)};for(let n in e.add){let a=e.add[n];if(n==="@keyframes"||n==="@viewport")t[n]=new AE(n,a,this);else if(n==="@resolution")t[n]=new Kw(n,a,this);else if(this.data[n].selector)t.selectors.push(tr.load(n,a,this));else{let s=this.data[n].props;if(s){let o=$e.load(n,a,this);for(let u of s)t[u]||(t[u]={values:[]}),t[u].values.push(o)}else{let o=t[n]&&t[n].values||[];t[n]=M.load(n,a,this),t[n].values=o}}}let r={selectors:[]};for(let n in e.remove){let a=e.remove[n];if(this.data[n].selector){let s=tr.load(n,a);for(let o of a)r.selectors.push(s.old(o))}else if(n==="@keyframes"||n==="@viewport")for(let s of a){let o=`@${s}${n.slice(1)}`;r[o]={remove:!0}}else if(n==="@resolution")r[n]=new Kw(n,a,this);else{let s=this.data[n].props;if(s){let o=$e.load(n,[],this);for(let u of a){let c=o.old(u);if(c)for(let f of s)r[f]||(r[f]={}),r[f].values||(r[f].values=[]),r[f].values.push(c)}}else for(let o of a){let u=this.decl(n).old(n,o);if(n==="align-self"){let c=t[n]&&t[n].prefixes;if(c){if(o==="-webkit- 2009"&&c.includes("-webkit-"))continue;if(o==="-webkit-"&&c.includes("-webkit- 2009"))continue}}for(let c of u)r[c]||(r[c]={}),r[c].remove=!0}}}return[t,r]}decl(e){return tu.has(e)||tu.set(e,M.load(e)),tu.get(e)}unprefixed(e){let t=this.normalize(Xw.unprefixed(e));return t==="flex-direction"&&(t="flex-flow"),t}normalize(e){return this.decl(e).normalize(e)}prefixed(e,t){return e=Xw.unprefixed(e),this.decl(e).prefixed(e,t)}values(e,t){let r=this[e],n=r["*"]&&r["*"].values,a=r[t]&&r[t].values;return n&&a?rr.uniq(n.concat(a)):n||a||[]}group(e){let t=e.parent,r=t.index(e),{length:n}=t.nodes,a=this.unprefixed(e.prop),s=(o,u)=>{for(r+=o;r>=0&&r{l();tb.exports={"backdrop-filter":{feature:"css-backdrop-filter",browsers:["ios_saf 16.1","ios_saf 16.3","ios_saf 16.4","ios_saf 16.5","safari 16.5"]},element:{props:["background","background-image","border-image","mask","list-style","list-style-image","content","mask-image"],feature:"css-element-function",browsers:["firefox 114"]},"user-select":{mistakes:["-khtml-"],feature:"user-select-none",browsers:["ios_saf 16.1","ios_saf 16.3","ios_saf 16.4","ios_saf 16.5","safari 16.5"]},"background-clip":{feature:"background-clip-text",browsers:["and_chr 114","and_uc 15.5","chrome 109","chrome 113","chrome 114","edge 114","opera 99","samsung 21"]},hyphens:{feature:"css-hyphens",browsers:["ios_saf 16.1","ios_saf 16.3","ios_saf 16.4","ios_saf 16.5","safari 16.5"]},fill:{props:["width","min-width","max-width","height","min-height","max-height","inline-size","min-inline-size","max-inline-size","block-size","min-block-size","max-block-size","grid","grid-template","grid-template-rows","grid-template-columns","grid-auto-columns","grid-auto-rows"],feature:"intrinsic-width",browsers:["and_chr 114","and_uc 15.5","chrome 109","chrome 113","chrome 114","edge 114","opera 99","samsung 21"]},"fill-available":{props:["width","min-width","max-width","height","min-height","max-height","inline-size","min-inline-size","max-inline-size","block-size","min-block-size","max-block-size","grid","grid-template","grid-template-rows","grid-template-columns","grid-auto-columns","grid-auto-rows"],feature:"intrinsic-width",browsers:["and_chr 114","and_uc 15.5","chrome 109","chrome 113","chrome 114","edge 114","opera 99","samsung 21"]},stretch:{props:["width","min-width","max-width","height","min-height","max-height","inline-size","min-inline-size","max-inline-size","block-size","min-block-size","max-block-size","grid","grid-template","grid-template-rows","grid-template-columns","grid-auto-columns","grid-auto-rows"],feature:"intrinsic-width",browsers:["firefox 114"]},"fit-content":{props:["width","min-width","max-width","height","min-height","max-height","inline-size","min-inline-size","max-inline-size","block-size","min-block-size","max-block-size","grid","grid-template","grid-template-rows","grid-template-columns","grid-auto-columns","grid-auto-rows"],feature:"intrinsic-width",browsers:["firefox 114"]},"text-decoration-style":{feature:"text-decoration",browsers:["ios_saf 16.1","ios_saf 16.3","ios_saf 16.4","ios_saf 16.5"]},"text-decoration-color":{feature:"text-decoration",browsers:["ios_saf 16.1","ios_saf 16.3","ios_saf 16.4","ios_saf 16.5"]},"text-decoration-line":{feature:"text-decoration",browsers:["ios_saf 16.1","ios_saf 16.3","ios_saf 16.4","ios_saf 16.5"]},"text-decoration":{feature:"text-decoration",browsers:["ios_saf 16.1","ios_saf 16.3","ios_saf 16.4","ios_saf 16.5"]},"text-decoration-skip":{feature:"text-decoration",browsers:["ios_saf 16.1","ios_saf 16.3","ios_saf 16.4","ios_saf 16.5"]},"text-decoration-skip-ink":{feature:"text-decoration",browsers:["ios_saf 16.1","ios_saf 16.3","ios_saf 16.4","ios_saf 16.5"]},"text-size-adjust":{feature:"text-size-adjust",browsers:["ios_saf 16.1","ios_saf 16.3","ios_saf 16.4","ios_saf 16.5"]},"mask-clip":{feature:"css-masks",browsers:["and_chr 114","and_uc 15.5","chrome 109","chrome 113","chrome 114","edge 114","opera 99","samsung 21"]},"mask-composite":{feature:"css-masks",browsers:["and_chr 114","and_uc 15.5","chrome 109","chrome 113","chrome 114","edge 114","opera 99","samsung 21"]},"mask-image":{feature:"css-masks",browsers:["and_chr 114","and_uc 15.5","chrome 109","chrome 113","chrome 114","edge 114","opera 99","samsung 21"]},"mask-origin":{feature:"css-masks",browsers:["and_chr 114","and_uc 15.5","chrome 109","chrome 113","chrome 114","edge 114","opera 99","samsung 21"]},"mask-repeat":{feature:"css-masks",browsers:["and_chr 114","and_uc 15.5","chrome 109","chrome 113","chrome 114","edge 114","opera 99","samsung 21"]},"mask-border-repeat":{feature:"css-masks",browsers:["and_chr 114","and_uc 15.5","chrome 109","chrome 113","chrome 114","edge 114","opera 99","samsung 21"]},"mask-border-source":{feature:"css-masks",browsers:["and_chr 114","and_uc 15.5","chrome 109","chrome 113","chrome 114","edge 114","opera 99","samsung 21"]},mask:{feature:"css-masks",browsers:["and_chr 114","and_uc 15.5","chrome 109","chrome 113","chrome 114","edge 114","opera 99","samsung 21"]},"mask-position":{feature:"css-masks",browsers:["and_chr 114","and_uc 15.5","chrome 109","chrome 113","chrome 114","edge 114","opera 99","samsung 21"]},"mask-size":{feature:"css-masks",browsers:["and_chr 114","and_uc 15.5","chrome 109","chrome 113","chrome 114","edge 114","opera 99","samsung 21"]},"mask-border":{feature:"css-masks",browsers:["and_chr 114","and_uc 15.5","chrome 109","chrome 113","chrome 114","edge 114","opera 99","samsung 21"]},"mask-border-outset":{feature:"css-masks",browsers:["and_chr 114","and_uc 15.5","chrome 109","chrome 113","chrome 114","edge 114","opera 99","samsung 21"]},"mask-border-width":{feature:"css-masks",browsers:["and_chr 114","and_uc 15.5","chrome 109","chrome 113","chrome 114","edge 114","opera 99","samsung 21"]},"mask-border-slice":{feature:"css-masks",browsers:["and_chr 114","and_uc 15.5","chrome 109","chrome 113","chrome 114","edge 114","opera 99","samsung 21"]},"clip-path":{feature:"css-clip-path",browsers:["samsung 21"]},"box-decoration-break":{feature:"css-boxdecorationbreak",browsers:["and_chr 114","and_uc 15.5","chrome 109","chrome 113","chrome 114","edge 114","ios_saf 16.1","ios_saf 16.3","ios_saf 16.4","ios_saf 16.5","opera 99","safari 16.5","samsung 21"]},appearance:{feature:"css-appearance",browsers:["samsung 21"]},"image-set":{props:["background","background-image","border-image","cursor","mask","mask-image","list-style","list-style-image","content"],feature:"css-image-set",browsers:["and_uc 15.5","chrome 109","samsung 21"]},"cross-fade":{props:["background","background-image","border-image","mask","list-style","list-style-image","content","mask-image"],feature:"css-cross-fade",browsers:["and_chr 114","and_uc 15.5","chrome 109","chrome 113","chrome 114","edge 114","opera 99","samsung 21"]},isolate:{props:["unicode-bidi"],feature:"css-unicode-bidi",browsers:["ios_saf 16.1","ios_saf 16.3","ios_saf 16.4","ios_saf 16.5","safari 16.5"]},"color-adjust":{feature:"css-color-adjust",browsers:["chrome 109","chrome 113","chrome 114","edge 114","opera 99"]}}});var nb=v((e6,ib)=>{l();ib.exports={}});var lb=v((t6,ob)=>{l();var O5=Wo(),{agents:T5}=(zn(),jn),ru=bm(),P5=ct(),D5=eb(),I5=rb(),q5=nb(),sb={browsers:T5,prefixes:I5},ab=` + Replace Autoprefixer \`browsers\` option to Browserslist config. + Use \`browserslist\` key in \`package.json\` or \`.browserslistrc\` file. + + Using \`browsers\` option can cause errors. Browserslist config can + be used for Babel, Autoprefixer, postcss-normalize and other tools. + + If you really need to use option, rename it to \`overrideBrowserslist\`. + + Learn more at: + https://github.com/browserslist/browserslist#readme + https://twitter.com/browserslist + +`;function R5(i){return Object.prototype.toString.apply(i)==="[object Object]"}var iu=new Map;function M5(i,e){e.browsers.selected.length!==0&&(e.add.selectors.length>0||Object.keys(e.add).length>2||i.warn(`Autoprefixer target browsers do not need any prefixes.You do not need Autoprefixer anymore. +Check your Browserslist config to be sure that your targets are set up correctly. + + Learn more at: + https://github.com/postcss/autoprefixer#readme + https://github.com/browserslist/browserslist#readme + +`))}ob.exports=ir;function ir(...i){let e;if(i.length===1&&R5(i[0])?(e=i[0],i=void 0):i.length===0||i.length===1&&!i[0]?i=void 0:i.length<=2&&(Array.isArray(i[0])||!i[0])?(e=i[1],i=i[0]):typeof i[i.length-1]=="object"&&(e=i.pop()),e||(e={}),e.browser)throw new Error("Change `browser` option to `overrideBrowserslist` in Autoprefixer");if(e.browserslist)throw new Error("Change `browserslist` option to `overrideBrowserslist` in Autoprefixer");e.overrideBrowserslist?i=e.overrideBrowserslist:e.browsers&&(typeof console!="undefined"&&console.warn&&(ru.red?console.warn(ru.red(ab.replace(/`[^`]+`/g,n=>ru.yellow(n.slice(1,-1))))):console.warn(ab)),i=e.browsers);let t={ignoreUnknownVersions:e.ignoreUnknownVersions,stats:e.stats,env:e.env};function r(n){let a=sb,s=new P5(a.browsers,i,n,t),o=s.selected.join(", ")+JSON.stringify(e);return iu.has(o)||iu.set(o,new D5(a.prefixes,s,e)),iu.get(o)}return{postcssPlugin:"autoprefixer",prepare(n){let a=r({from:n.opts.from,env:e.env});return{OnceExit(s){M5(n,a),e.remove!==!1&&a.processor.remove(s,n),e.add!==!1&&a.processor.add(s,n)}}},info(n){return n=n||{},n.from=n.from||m.cwd(),q5(r(n))},options:e,browsers:i}}ir.postcss=!0;ir.data=sb;ir.defaults=O5.defaults;ir.info=()=>ir().info()});var ub={};Ae(ub,{default:()=>B5});var B5,fb=C(()=>{l();B5=[]});var pb={};Ae(pb,{default:()=>F5});var cb,F5,db=C(()=>{l();hi();cb=K(bi()),F5=Ze(cb.default.theme)});var mb={};Ae(mb,{default:()=>N5});var hb,N5,gb=C(()=>{l();hi();hb=K(bi()),N5=Ze(hb.default)});l();"use strict";var L5=Je(ym()),$5=Je(me()),j5=Je(lb()),z5=Je((fb(),ub)),V5=Je((db(),pb)),U5=Je((gb(),mb)),W5=Je((ts(),ku)),G5=Je((bo(),wo)),H5=Je((gs(),rf));function Je(i){return i&&i.__esModule?i:{default:i}}console.warn("cdn.tailwindcss.com should not be used in production. To use Tailwind CSS in production, install it as a PostCSS plugin or use the Tailwind CLI: https://tailwindcss.com/docs/installation");var Qn="tailwind",nu="text/tailwindcss",yb="/template.html",vt,wb=!0,bb=0,su=new Set,au,vb="",xb=(i=!1)=>({get(e,t){return(!i||t==="config")&&typeof e[t]=="object"&&e[t]!==null?new Proxy(e[t],xb()):e[t]},set(e,t,r){return e[t]=r,(!i||t==="config")&&ou(!0),!0}});window[Qn]=new Proxy({config:{},defaultTheme:V5.default,defaultConfig:U5.default,colors:W5.default,plugin:G5.default,resolveConfig:H5.default},xb(!0));function kb(i){au.observe(i,{attributes:!0,attributeFilter:["type"],characterData:!0,subtree:!0,childList:!0})}new MutationObserver(async i=>{let e=!1;if(!au){au=new MutationObserver(async()=>await ou(!0));for(let t of document.querySelectorAll(`style[type="${nu}"]`))kb(t)}for(let t of i)for(let r of t.addedNodes)r.nodeType===1&&r.tagName==="STYLE"&&r.getAttribute("type")===nu&&(kb(r),e=!0);await ou(e)}).observe(document.documentElement,{attributes:!0,attributeFilter:["class"],childList:!0,subtree:!0});async function ou(i=!1){i&&(bb++,su.clear());let e="";for(let r of document.querySelectorAll(`style[type="${nu}"]`))e+=r.textContent;let t=new Set;for(let r of document.querySelectorAll("[class]"))for(let n of r.classList)su.has(n)||t.add(n);if(document.body&&(wb||t.size>0||e!==vb||!vt||!vt.isConnected)){for(let n of t)su.add(n);wb=!1,vb=e,self[yb]=Array.from(t).join(" ");let{css:r}=await(0,$5.default)([(0,L5.default)({...window[Qn].config,_hash:bb,content:[yb],plugins:[...z5.default,...Array.isArray(window[Qn].config.plugins)?window[Qn].config.plugins:[]]}),(0,j5.default)({remove:!1})]).process(`@tailwind base;@tailwind components;@tailwind utilities;${e}`);(!vt||!vt.isConnected)&&(vt=document.createElement("style"),document.head.append(vt)),vt.textContent=r}}})(); +/*! https://mths.be/cssesc v3.0.0 by @mathias */ \ No newline at end of file diff --git a/tools/maix_module2/docs/static/image/github-fill.svg b/tools/maix_module2/docs/static/image/github-fill.svg new file mode 100644 index 00000000..d3fd1f6d --- /dev/null +++ b/tools/maix_module2/docs/static/image/github-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tools/maix_module2/docs/static/image/language copy.svg b/tools/maix_module2/docs/static/image/language copy.svg new file mode 100644 index 00000000..b1903d01 --- /dev/null +++ b/tools/maix_module2/docs/static/image/language copy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tools/maix_module2/docs/static/image/language.svg b/tools/maix_module2/docs/static/image/language.svg new file mode 100644 index 00000000..b1903d01 --- /dev/null +++ b/tools/maix_module2/docs/static/image/language.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tools/maix_module2/docs/static/image/maixpy_banner.png b/tools/maix_module2/docs/static/image/maixpy_banner.png new file mode 100644 index 00000000..3b290144 Binary files /dev/null and b/tools/maix_module2/docs/static/image/maixpy_banner.png differ diff --git a/tools/maix_module2/docs/static/image/wechat.png b/tools/maix_module2/docs/static/image/wechat.png new file mode 100644 index 00000000..13978a67 Binary files /dev/null and b/tools/maix_module2/docs/static/image/wechat.png differ diff --git a/tools/maix_module2/docs/static/js/custom.js b/tools/maix_module2/docs/static/js/custom.js new file mode 100644 index 00000000..e69de29b diff --git a/tools/maix_module2/init_files.py b/tools/maix_module2/init_files.py new file mode 100644 index 00000000..5a237089 --- /dev/null +++ b/tools/maix_module2/init_files.py @@ -0,0 +1,214 @@ +import os +import sys + +def main(): + # ===================== 1. Read and validate module name ===================== + try: + with open("module_name.txt", "r", encoding="utf-8") as f: + module_name = f.readline().strip() + except FileNotFoundError: + raise Exception("Error: module_name.txt file not found. Please create it first and fill in the module name (e.g., maix_kkk)") + except Exception as e: + raise Exception(f"Failed to read module_name.txt: {e}") + + # Validate module name must start with maix_ + if not module_name.startswith("maix_"): + raise Exception("Error: Module name must start with 'maix_' (e.g., maix_kkk)") + + # Optional: Specify initial version number from command line (Example: python init_files.py 1.0.1) + init_version = sys.argv[1] if len(sys.argv) > 1 else "1.0.0" + try: + version_major, version_minor, version_patch = init_version.split(".") + # Verify version number is numeric + int(version_major), int(version_minor), int(version_patch) + except ValueError: + raise Exception(f"Error: Invalid version number format (Please use format like 1.0.0, current: {init_version})") + + # ===================== 2. Generate test file (test/test_import.py) ===================== + def write_test_import_file(): + test_file = "test/test_import.py" + if os.path.exists(test_file): + print(f"Note: {test_file} already exists, skip generation") + return + try: + os.makedirs("test", exist_ok=True) + content = f"import {module_name}\nprint(f'Successfully imported module: {module_name}')" + with open(test_file, "w", encoding="utf-8") as f: + f.write(content) + print(f"Successfully generated: {test_file}") + except PermissionError: + raise Exception(f"Error: Insufficient permissions to create {test_file}") + except Exception as e: + raise Exception(f"Failed to generate test file: {e}") + + # ===================== 3. Generate core module files (__init__.py / version.py) ===================== + def write_module_dir(): + # Generate __init__.py content (Fix hardcoded extension module name) + init_content = f'''from .version import __version__ +from ._{module_name} import * +''' + # Generate version.py content (Dynamic version number) + version_content = f'''# Versions should comply with PEP440: https://peps.python.org/pep-0440/ + +version_major = {version_major} +version_minor = {version_minor} +version_patch = {version_patch} + +__version__ = "{version_major}.{version_minor}.{version_patch}" +''' + try: + # Create module directory + os.makedirs(module_name, exist_ok=True) + + # Generate __init__.py + init_file = f"{module_name}/__init__.py" + if not os.path.exists(init_file): + with open(init_file, "w", encoding="utf-8") as f: + f.write(init_content) + print(f"Successfully generated: {init_file}") + else: + print(f"Note: {init_file} already exists, skip generation") + + # Generate version.py + version_file = f"{module_name}/version.py" + if not os.path.exists(version_file): + with open(version_file, "w", encoding="utf-8") as f: + f.write(version_content) + print(f"Successfully generated: {version_file}") + else: + print(f"Note: {version_file} already exists, skip generation") + except PermissionError: + raise Exception(f"Error: Insufficient permissions to create {module_name} directory/files") + except Exception as e: + raise Exception(f"Failed to generate core module files: {e}") + + # ===================== 4. Generate C++ extension module files (hpp/cpp) ===================== + def write_component_dir(): + # Generate .hpp header file content + hpp_content = f''' +#include "maix_basic.hpp" + +namespace {module_name}::basic +{{ + /** + * hello + * @param name Name to say hello + * @{module_name} {module_name}.basic.hello + */ + void hello(const std::string &name); + +}} // namespace {module_name} +''' + # Generate .cpp source file content + cpp_content = f'''#include "{module_name}.hpp" + +namespace {module_name}::basic +{{ + void hello(const std::string &name) + {{ + maix::log::info(("hello: " + name).c_str()); + }} +}} // namespace {module_name} +''' + try: + # Create directories + include_dir = "components/maix/include" + src_dir = "components/maix/src" + os.makedirs(include_dir, exist_ok=True) + os.makedirs(src_dir, exist_ok=True) + + # Generate .hpp file + hpp_file = f"{include_dir}/{module_name}.hpp" + if not os.path.exists(hpp_file): + with open(hpp_file, "w", encoding="utf-8") as f: + f.write(hpp_content) + print(f"Successfully generated: {hpp_file}") + else: + print(f"Note: {hpp_file} already exists, skip generation") + + # Generate .cpp file + cpp_file = f"{src_dir}/{module_name}.cpp" + if not os.path.exists(cpp_file): + with open(cpp_file, "w", encoding="utf-8") as f: + f.write(cpp_content) + print(f"Successfully generated: {cpp_file}") + else: + print(f"Note: {cpp_file} already exists, skip generation") + except PermissionError: + raise Exception("Error: Insufficient permissions to create components directory/files") + except Exception as e: + raise Exception(f"Failed to generate C++ extension files: {e}") + + # ===================== 5. Generate README documentation ===================== + def write_readme_files(): + # English README + readme_en = "README.md" + if not os.path.exists(readme_en): + en_content = f"""# MaixPy Module: {module_name} + +## Quick Start +1. Compile module: `python setup.py bdist_wheel maixcam` +2. Install module: `pip install dist/{module_name}*.whl` +3. Test import: `python test/test_import.py` + +## Development Guide +- Python module root: `{module_name}/` +- C++ source code: `components/maix/src/{module_name}.cpp` +- C++ header file: `components/maix/include/{module_name}.hpp` +- Version management: `{module_name}/version.py` + +## TODO +- Add detailed API documentation +- Add unit tests for core functions +""" + with open(readme_en, "w", encoding="utf-8") as f: + f.write(en_content) + print(f"Successfully generated: {readme_en}") + else: + print(f"Note: {readme_en} already exists, skip generation") + + # Chinese README (renamed to README_CN.md for consistency) + readme_cn = "README_CN.md" + if not os.path.exists(readme_cn): + cn_content = f"""# MaixPy Module: {module_name} + +## Quick Start +1. Compile module: `python setup.py bdist_wheel 'platform'` +2. Install module: `pip install dist/{module_name}*.whl` +3. Test import: `python test/test_import.py` + +## Development Guide +- Python module root directory: `{module_name}/` +- C++ source file: `components/maix/src/{module_name}.cpp` +- C++ header file: `components/maix/include/{module_name}.hpp` +- Version management file: `{module_name}/version.py` + +## TODO +- Add detailed API documentation +- Add unit tests for core functions +""" + with open(readme_cn, "w", encoding="utf-8") as f: + f.write(cn_content) + print(f"Successfully generated: {readme_cn}") + else: + print(f"Note: {readme_cn} already exists, skip generation") + + # ===================== Execute all generation logic ===================== + write_test_import_file() + write_module_dir() + write_component_dir() + write_readme_files() + + print(f"\n✅ Module project skeleton generation completed! Module name: {module_name}") + print(f"📌 Next steps:") + print(f" 1. Modify {module_name}/__init__.py to add custom interfaces") + print(f" 2. Improve components/maix/src/{module_name}.cpp to implement business logic") + print(f" 3. Execute python setup.py bdist_wheel 'platform' to compile the module") + print(f" 4. Modify whl package version: in {module_name}/version.py file") + +if __name__ == "__main__": + try: + main() + except Exception as e: + print(f"\n❌ Generation failed: {e}") + sys.exit(1) \ No newline at end of file diff --git a/tools/maix_module2/main/CMakeLists.txt b/tools/maix_module2/main/CMakeLists.txt new file mode 100644 index 00000000..0b184bce --- /dev/null +++ b/tools/maix_module2/main/CMakeLists.txt @@ -0,0 +1,73 @@ +############### Add include ################### +list(APPEND ADD_INCLUDE "include" + ) +list(APPEND ADD_PRIVATE_INCLUDE "") +############################################### + +############ Add source files ################# +# list(APPEND ADD_SRCS "src/main.c" +# "src/test.c" +# ) +append_srcs_dir(ADD_SRCS "src") # append source file in src dir to var ADD_SRCS +# list(REMOVE_ITEM COMPONENT_SRCS "src/test2.c") +# FILE(GLOB_RECURSE EXTRA_SRC "src/*.c") +# FILE(GLOB EXTRA_SRC "src/*.c") +# list(APPEND ADD_SRCS ${EXTRA_SRC}) +# aux_source_directory(src ADD_SRCS) # collect all source file in src dir, will set var ADD_SRCS +# append_srcs_dir(ADD_SRCS "src") # append source file in src dir to var ADD_SRCS +# list(REMOVE_ITEM COMPONENT_SRCS "src/test.c") +# set(ADD_ASM_SRCS "src/asm.S") +# list(APPEND ADD_SRCS ${ADD_ASM_SRCS}) +# SET_PROPERTY(SOURCE ${ADD_ASM_SRCS} PROPERTY LANGUAGE C) # set .S ASM file as C language +# SET_SOURCE_FILES_PROPERTIES(${ADD_ASM_SRCS} PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp -D BBBBB") +############################################### + +###### Add required/dependent components ###### +list(APPEND ADD_REQUIREMENTS maix) +############################################### + +###### Add link search path for requirements/libs ###### +# list(APPEND ADD_LINK_SEARCH_PATH "${CONFIG_TOOLCHAIN_PATH}/lib") +# list(APPEND ADD_REQUIREMENTS pthread m) # add system libs, pthread and math lib for example here +# set (OpenCV_DIR opencv/lib/cmake/opencv4) +# find_package(OpenCV REQUIRED) +############################################### + +############ Add static libs ################## +# list(APPEND ADD_STATIC_LIB "lib/libtest.a") +############################################### + +#### Add compile option for this component #### +#### Just for this component, won't affect other +#### modules, including component that depend +#### on this component +# list(APPEND ADD_DEFINITIONS_PRIVATE -DAAAAA=1) + +#### Add compile option for this component +#### and components depend on this component +# list(APPEND ADD_DEFINITIONS -DAAAAA222=1 +# -DAAAAA333=1) +############################################### + +############ Add static libs ################## +#### Update parent's variables like CMAKE_C_LINK_FLAGS +# set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--start-group libmaix/libtest.a -ltest2 -Wl,--end-group" PARENT_SCOPE) +############################################### + +######### Add files need to download ######### +# list(APPEND ADD_FILE_DOWNLOADS "{ +# 'url': 'https://*****/abcde.tar.xz', +# 'urls': [], +# 'sha256sum': '', +# 'filename': 'abcde.tar.xz', +# 'path': 'toolchains/xxxxx', +# 'check_files': [] +# }" +# ) +# +# then extracted file in ${DL_EXTRACTED_PATH}/toolchains/xxxxx, +# you can directly use then, for example use it in add_custom_command +############################################## + +register_component() + diff --git a/tools/maix_module2/main/Kconfig b/tools/maix_module2/main/Kconfig new file mode 100644 index 00000000..e69de29b diff --git a/tools/maix_module2/main/include/main.h b/tools/maix_module2/main/include/main.h new file mode 100644 index 00000000..45dcbb04 --- /dev/null +++ b/tools/maix_module2/main/include/main.h @@ -0,0 +1,3 @@ +#pragma once + + diff --git a/tools/maix_module2/main/src/main.cpp b/tools/maix_module2/main/src/main.cpp new file mode 100644 index 00000000..2b1dc61c --- /dev/null +++ b/tools/maix_module2/main/src/main.cpp @@ -0,0 +1,36 @@ + +#include "maix_basic.hpp" +#include "main.h" +#include // everything needed for embedding + + +namespace py = pybind11; + +using namespace maix; + +int _main(int argc, char* argv[]) +{ + py::scoped_interpreter guard{}; + // FIXME: run occurs core dump + py::exec(R"( + print("python start") + + from _maix import app + + print("Hello MaixPy") + print(maix.app.app_id()) + )"); + + return 0; +} + +int main(int argc, char* argv[]) +{ + // Catch signal and process + sys::register_default_signal_handle(); + + // Use CATCH_EXCEPTION_RUN_RETURN to catch exception, + // if we don't catch exception, when program throw exception, the objects will not be destructed. + // So we catch exception here to let resources be released(call objects' destructor) before exit. + CATCH_EXCEPTION_RUN_RETURN(_main, -1, argc, argv); +} diff --git a/tools/maix_module2/maix_kws/__init__.py b/tools/maix_module2/maix_kws/__init__.py new file mode 100644 index 00000000..d71583e2 --- /dev/null +++ b/tools/maix_module2/maix_kws/__init__.py @@ -0,0 +1,2 @@ +from .version import __version__ +from ._maix_kws import * diff --git a/tools/maix_module2/maix_kws/version.py b/tools/maix_module2/maix_kws/version.py new file mode 100644 index 00000000..2da130c4 --- /dev/null +++ b/tools/maix_module2/maix_kws/version.py @@ -0,0 +1,7 @@ +# Versions should comply with PEP440: https://peps.python.org/pep-0440/ + +version_major = 1 +version_minor = 0 +version_patch = 0 + +__version__ = "1.0.0" diff --git a/tools/maix_module2/module_name.txt b/tools/maix_module2/module_name.txt new file mode 100644 index 00000000..3ef547cb --- /dev/null +++ b/tools/maix_module2/module_name.txt @@ -0,0 +1 @@ +maix_kws \ No newline at end of file diff --git a/tools/maix_module2/project.py b/tools/maix_module2/project.py new file mode 100644 index 00000000..478c983e --- /dev/null +++ b/tools/maix_module2/project.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +#-*- coding = utf-8 -*- + +# +# @file from https://github.com/Neutree/c_cpp_project_framework +# @author neucrack +# @license Apache 2.0 +# + +import sys, os + +def is_project_valid(): + return os.path.exists("main") + +def get_sdk_path(): + sdk_path = None + sdk_env_name = "MAIXCDK_PATH" + + # 1. get SDK absolute path from MAIXCDK_PATH env + try: + if os.environ[sdk_env_name]: + sdk_path = os.environ[sdk_env_name] + except Exception: + pass + + # 2. check if in MaixCDK repo, higher priority + path = os.path.abspath("../../") + if os.path.exists(path+"/tools/cmake/project.py"): + sdk_path = path + + # 3. check if MaixCDK path valid + if not sdk_path: + print("") + print("Error: can not find MaixCDK, please set MAIXCDK_PATH env to MaixCDK directory by `export MAIXCDK_PATH=xxxxx`") + print("") + sys.exit(1) + if not os.path.exists(sdk_path): + print("") + print(f"Error: MaixCDK set to {sdk_path}, but not exists!") + print("") + sys.exit(1) + return os.path.abspath(sdk_path) + +def exec_project_py(): + # 1. check main component + if not is_project_valid(): + print("") + print("Error: can not find main component, please execute this command in your project root directory") + print("") + sys.exit(1) + + # 2. get MaixCDK path + sdk_path = get_sdk_path() + print("-- SDK_PATH:{}".format(sdk_path)) + project_path = os.path.abspath(".") + + # 3. execute project script from SDK + project_file_path = sdk_path+"/tools/cmake/project.py" + sys.path.insert(0, os.path.dirname(project_file_path)) + from project import main + main(sdk_path, project_path) + +def main(): + exec_project_py() + +if __name__ == "__main__": + main() diff --git a/tools/maix_module2/run.sh b/tools/maix_module2/run.sh new file mode 100644 index 00000000..c3421918 --- /dev/null +++ b/tools/maix_module2/run.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +maixpy_root="." +python -u -c "exec('import sys;sys.path.insert(0, \'$maixpy_root\');'+open('$1', 'r').read())" + + diff --git a/tools/maix_module2/setup.py b/tools/maix_module2/setup.py new file mode 100644 index 00000000..c62e9ba4 --- /dev/null +++ b/tools/maix_module2/setup.py @@ -0,0 +1,305 @@ +from setuptools import setup, find_packages, Distribution +import sys +import os +import platform +import shutil +import zipfile + +#################################################################### +# supported platforms +# 1. 新增 maixcam2 到支持的平台列表中 +board_names = ["linux", "maixcam", "maixcam2"] # 修改点:添加 maixcam2 +platform_names = { + # use correspond docker to compile https://github.com/pypa/manylinux + "linux": "manylinux2014_{}".format(platform.machine().replace("-", "_").replace(".", "_").lower()), + "m2dock": "linux_armv7l", + "maixcam": "linux_riscv64", + "maixcam2": "linux_arrch64", # 修改点:添加 maixcam2 对应的平台名称 +} +platform_toolchain_id = { + "maixcam": "musl_t-hread", + "maixcam2": "cross-gcc-11.3" # 修改点:添加 maixcam2 对应的工具链ID +} +#################################################################### + +with open("module_name.txt", "r") as f: + module_name = f.readline().strip() + +def get_build_python_version(): + version = [0, 0, 0] + mk_file = os.path.join("build", "config", "python_version.txt") + with open(mk_file, "r", encoding="utf-8") as f: + version_str = f.read().split(".") + for i in range(0, 3): + version[i] = int(version_str[i]) + if version[0] == 0 or version[1] == 0 or version[2] == 0: + print("-- Get build python version failed!") + sys.exit(1) + return version + +def get_python_version(): + return [sys.version_info.major, sys.version_info.minor, sys.version_info.micro] + +board_config_files = {} +for board in board_names: + board_config_files[board] = os.path.join("configs", "config_platform_{}.mk".format(board)) + if not os.path.exists(board_config_files[board]): + print("-- Platform config file not found: {}".format(board_config_files[board])) + sys.exit(1) + +board = None +for name in board_names: + if name in sys.argv: + board = name + sys.argv.remove(name) + break +if (not board) and not ("-h" in sys.argv or "--help" in sys.argv or "--help-commands" in sys.argv): + print("-- Please specify board name: {}, e.g. python setup.py bdist_wheel linux".format(board_names)) + sys.exit(1) + +# copy dist to dist_old +if os.path.exists("dist"): + os.makedirs("dist_old", exist_ok = True) + shutil.copytree("dist", "dist_old", dirs_exist_ok=True) + +# delete temp files +if "--not-clean" not in sys.argv and "--skip-build" not in sys.argv and os.path.exists(f"{module_name}/dl_lib"): + shutil.rmtree(f"{module_name}/dl_lib") + +def print_py_version_err(build_py_version): + print("-- Python version not match build python version!") + print(" You can use conda to create a virtual environment with python version:") + print(" Download miniconda from https://docs.conda.io/en/latest/miniconda.html") + print(" conda create -n python{}.{} python={}.{}".format(build_py_version[0], build_py_version[1], build_py_version[0], build_py_version[1])) + print(" conda activate python{}.{}".format(build_py_version[0], build_py_version[1])) + +# specially check for maixcam and maixcam2 +py_version = get_python_version() +if board == "maixcam" and f"{py_version[0]}.{py_version[1]}" != "3.11": + print_py_version_err([3, 11]) + sys.exit(1) +# 修改点:新增 maixcam2 的 Python 版本强制检测(要求 3.13) +if board == "maixcam2" and f"{py_version[0]}.{py_version[1]}" != "3.13": + print_py_version_err([3, 13]) + sys.exit(1) + +if board: + # build CPP modules, and copy to build/lib/ + if "--skip-build" not in sys.argv: + if "debug" in sys.argv: + release_str = "" + sys.argv.remove("debug") + else: + release_str = "--release" + cmd = "python project.py build -p {} {} --config-file {}".format(board, release_str, board_config_files[board]) + if "--not-clean" not in sys.argv: + cmd = "python project.py distclean && " + cmd + else: + sys.argv.remove("--not-clean") + cmd += f" --toolchain-id {platform_toolchain_id[board]}" if board in platform_toolchain_id else "" + ret = os.system(cmd) + if ret != 0: + print("-- Build cpp modules failed!") + sys.exit(1) + +# check python version +build_py_version = get_build_python_version() +print("-- Build Python version: {}.{}.{}".format(build_py_version[0], build_py_version[1], build_py_version[2])) +print("-- Python version: {}.{}.{}".format(py_version[0], py_version[1], py_version[2])) +if (py_version[0] != build_py_version[0]) or (py_version[1] != build_py_version[1]): + print_py_version_err(build_py_version) + sys.exit(1) + +if board: + # specific platform name for wheel package + sys.argv += ["--python-tag", "cp{}{}".format(build_py_version[0], build_py_version[1])] + # if board in ["linux"]: + sys.argv += ["--plat-name", platform_names[board]] + +# generate pyi stub files +if board == "linux": + try: + from pybind11_stubgen import main as pybind11_stubgen_main + except: + print("-- Please install pybind11-stubgen first: pip install pybind11-stubgen") + sys.exit(1) + old_sys_argv = sys.argv + sys.path.insert(0, ".") # to ensure use this folder's maix module but not system's + sys.argv = ["pybind11-stubgen", module_name, "-o", "stub"] + pybind11_stubgen_main() + sys.path.pop(0) + sys.argv = old_sys_argv + # copy stub/maix/* to maix/ recursively + for root, dirs, files in os.walk(f"stub/{module_name}"): + for name in files: + if name.endswith(".pyi"): + dst = os.path.join(root[5:], name) + os.makedirs(os.path.dirname(dst), exist_ok=True) + shutil.copy(os.path.join(root, name), dst) + +# generate api documentation + # COMMAND ${python} -u ${CMAKE_CURRENT_SOURCE_DIR}/gen_api.py -o ${maixpy_wrapper_src} --doc ${PROJECT_PATH}/docs/api --sdk_path ${SDK_PATH} +maixcdk_path = os.path.abspath(os.environ.get("MAIXCDK_PATH", None)) +maixpy_path = os.path.abspath(os.getcwd()) +if maixcdk_path.startswith(maixpy_path): + raise Exception("DO NOT put MaixCDK in MaixPy folder, please put MaixCDK in other place and set MAIXCDK_PATH environmenet variable by `export MAIXCDK_PATH=xxxxx`") +if not maixcdk_path: + raise Exception("No environment variable MAIXCDK_PATH, please set first by `export MAIXCDK_PATH=xxxxx`") +ret = os.system(f"python -u components/maix/gen_api.py --doc docs/api --sdk_path {maixcdk_path}") +if ret != 0: + raise Exception("Generate doc file failed") + +# requirement packages +requirements = [] + +# Read long description from README.md +with open("README.md", "r", encoding="utf-8") as fh: + long_description = fh.read() + +# Get version info from maix/version.py file +with open(f"{module_name}/version.py", "r", encoding="utf-8") as f: + vars = {} + exec(f.read(), vars) + __version__ = vars["__version__"] + +class BinaryDistribution(Distribution): + """Distribution which always forces a binary package with platform name""" + def has_ext_modules(foo): + return True + +pkgs = find_packages() +print("-- found packages: {}".format(pkgs)) + +setup( + # all keywords see https://setuptools.pypa.io/en/latest/references/keywords.html + + # Package name + name=module_name, + + # Versions should comply with PEP440: https://peps.python.org/pep-0440/ + version=__version__, + + author='Sipeed', + author_email='support@sipeed.com', + + description='Sipeed Maix Vision Python SDK', + long_description=long_description, + long_description_content_type="text/markdown", + + # The project's main homepage. + url='https://github.com/Sipeed/MaixPy', + + # All License should comply with https://spdx.org/licenses/ + license='Apache 2.0', + + # See https://pypi.python.org/pypi?%3Aaction=list_classifiers + classifiers=[ + # How mature is this project? Common values are + # 3 - Alpha + # 4 - Beta + # 5 - Production/Stable + 'Development Status :: 5 - Production/Stable', + + # Indicate who your project is intended for + 'Intended Audience :: Developers', + 'Intended Audience :: Education', + 'Intended Audience :: Science/Research', + 'Topic :: Software Development :: Embedded Systems', + + # Pick your license as you wish (should match "license" above) + 'License :: OSI Approved :: Apache Software License', + + # Specify the Python versions you support here. In particular, ensure + # that you indicate whether you support Python 2, Python 3 or both. + 'Programming Language :: Python :: 3' + ], + + # What does your project relate to? + keywords='Machine vision, AI vision, IOT, AIOT, Edge computing', + + # You can just specify the packages manually here if your project is + # simple. Or you can use find_packages(). + packages=pkgs, + + # Alternatively, if you want to distribute just a my_module.py, uncomment + # this: + # py_modules=["my_module"], + + # List run-time dependencies here. These will be installed by pip when + # your project is installed. For an analysis of "install_requires" vs pip's + # requirements files see: + # https://packaging.python.org/en/latest/requirements.html + install_requires=requirements, + + # List additional groups of dependencies here (e.g. development + # dependencies). You can install these using the following syntax, + # for example: + # $ pip install -e .[dev,test] + extras_require={ + # 'dev': ['check-manifest'], + # 'test': ['coverage'], + }, + + # If there are data files included in your packages that need to be + # installed, specify them here. If using Python 2.6 or less, then these + # have to be included in MANIFEST.in as well. + package_data={ + module_name: ['*.so', "dl_lib/*.so*", "*.pyi", "**/*.pyi", "**/**/*.pyi"] + }, + + # Although 'package_data' is the preferred approach, in some case you may + # need to place data files outside of your packages. See: + # http://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files # noqa + # In this case, 'data_file' will be installed into '/my_data' + data_files=[ + # ("",["LICENSE","README.md"]) + ], + + # To provide executable scripts, use entry points in preference to the + # "scripts" keyword. Entry points provide cross-platform support and allow + # pip to create the appropriate form of executable for the target platform. + entry_points={ + 'console_scripts': [ + # 'maix-resize=maix.maix_resize:main_cli', + # 'maix_test_hardware=maix.test_hardware:main', + ], + # 'gui_scripts': [ + # ], + }, + + distclass=BinaryDistribution +) + +if board: + py_tag = "cp{}{}".format(build_py_version[0], build_py_version[1]) + files = os.listdir("dist") + # 根据文件编辑时间排序,取最新的文件 + files.sort(key=lambda x: os.path.getmtime(os.path.join("dist", x)), reverse=True) + name = files[0] + # if name.endswith(".whl"): + # os.rename(os.path.join("dist", name), os.path.join("dist", + # "MaixPy-{}-{}-{}-{}.whl".format(__version__, py_tag, py_tag, platform_names[board])) + # ) + if True:# 所有平台都打包whl文件 + # pypi not support riscv64 yet, so we have to change to py3-none-any pkg + # unzip to dist/temp, change dist-info/WHEEL file + # zip back and rename + import zipfile + with zipfile.ZipFile(os.path.join("dist", name), "r") as zip_ref: + zip_ref.extractall("dist/temp") + with open("dist/temp/{}-{}.dist-info/WHEEL".format(module_name, __version__), "r", encoding="utf-8") as f: + lines = f.readlines() + with open("dist/temp/{}-{}.dist-info/WHEEL".format(module_name, __version__), "w", encoding="utf-8") as f: + for line in lines: + if line.startswith("Tag:"): + f.write("Tag: py3-none-any\n") + else: + f.write(line) + with zipfile.ZipFile(os.path.join("dist", name), "w") as zip_ref: + for root, dirs, files in os.walk("dist/temp"): + for file in files: + zip_ref.write(os.path.join(root, file), os.path.join(root[9:], file)) + shutil.rmtree("dist/temp") + os.rename(os.path.join("dist", name), os.path.join("dist", + "{}-{}-py3-none-any.whl".format(module_name, __version__)) + ) \ No newline at end of file diff --git a/tools/maix_module2/test/test_import.py b/tools/maix_module2/test/test_import.py new file mode 100644 index 00000000..39feff64 --- /dev/null +++ b/tools/maix_module2/test/test_import.py @@ -0,0 +1,2 @@ +import maix_kws +print(f'Successfully imported module: maix_kws') \ No newline at end of file